Class: Fontisan::Variation::MetricsAdjuster
- Inherits:
-
Object
- Object
- Fontisan::Variation::MetricsAdjuster
- Includes:
- TableAccessor
- Defined in:
- lib/fontisan/variation/metrics_adjuster.rb
Overview
Applies metrics variation deltas to font metrics tables
This class handles applying variation deltas from HVAR, VVAR, and MVAR tables to the corresponding metrics tables (hmtx, vmtx, head, hhea, etc.).
Process:
- Parse ItemVariationStore from HVAR/VVAR/MVAR
- Calculate scalars for current coordinates using regions
- Apply deltas to base metrics
- Update metrics tables with adjusted values
Constant Summary collapse
- METRIC_SOURCES =
Maps MVAR metric tags to [table_tag, method] for base value lookup.
{ "hasc" => ["hhea", :ascender], "hdsc" => ["hhea", :descender], "hlgp" => ["hhea", :line_gap], "xhgt" => ["OS/2", :sx_height], "cpht" => ["OS/2", :s_cap_height], }.freeze
Instance Attribute Summary collapse
-
#font ⇒ TrueTypeFont, OpenTypeFont
readonly
Font instance.
-
#interpolator ⇒ Interpolator
readonly
Coordinate interpolator.
Instance Method Summary collapse
-
#apply_font_wide_metrics_deltas(mvar, scalars) ⇒ Object
Apply font-wide metrics deltas.
-
#apply_horizontal_metrics_deltas(hvar, hmtx, glyph_count, scalars) ⇒ Array<Hash>
Apply horizontal metrics deltas.
-
#apply_hvar_deltas(coordinates) ⇒ Boolean
Apply HVAR deltas to horizontal metrics.
-
#apply_mvar_deltas(coordinates) ⇒ Boolean
Apply MVAR deltas to font-wide metrics.
-
#apply_vvar_deltas(coordinates) ⇒ Boolean
Apply VVAR deltas to vertical metrics.
-
#build_hmtx_data(metrics) ⇒ String?
Build hmtx binary data from metrics.
-
#extract_regions_from_store(store) ⇒ Array<Hash>
Extract regions from ItemVariationStore.
- #get_base_metric_value(tag) ⇒ Object
-
#initialize(font, interpolator) ⇒ MetricsAdjuster
constructor
Initialize metrics adjuster.
-
#rebuild_hmtx_table(metrics) ⇒ Object
Rebuild hmtx table with adjusted metrics.
-
#update_font_metric(tag, value) ⇒ Object
Update font metric in appropriate table.
-
#update_hhea_number_of_h_metrics(count) ⇒ Object
Update hhea table's numberOfHMetrics field.
Methods included from TableAccessor
#clear_variation_cache, #clear_variation_table, #has_variation_table?, #require_variation_table, #variation_table
Constructor Details
#initialize(font, interpolator) ⇒ MetricsAdjuster
Initialize metrics adjuster
32 33 34 35 36 |
# File 'lib/fontisan/variation/metrics_adjuster.rb', line 32 def initialize(font, interpolator) @font = font @interpolator = interpolator @variation_tables = {} end |
Instance Attribute Details
#font ⇒ TrueTypeFont, OpenTypeFont (readonly)
Returns Font instance.
23 24 25 |
# File 'lib/fontisan/variation/metrics_adjuster.rb', line 23 def font @font end |
#interpolator ⇒ Interpolator (readonly)
Returns Coordinate interpolator.
26 27 28 |
# File 'lib/fontisan/variation/metrics_adjuster.rb', line 26 def interpolator @interpolator end |
Instance Method Details
#apply_font_wide_metrics_deltas(mvar, scalars) ⇒ Object
Apply font-wide metrics deltas
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 |
# File 'lib/fontisan/variation/metrics_adjuster.rb', line 194 def apply_font_wide_metrics_deltas(mvar, scalars) # Get all metric tags mvar..each do |tag| delta_set = mvar.metric_delta_set(tag) next unless delta_set # Get base value for this metric base_value = get_base_metric_value(tag) next unless base_value # Apply deltas new_value = @interpolator.interpolate_value( base_value, delta_set, scalars, ).round # Update metric in appropriate table update_font_metric(tag, new_value) end end |
#apply_horizontal_metrics_deltas(hvar, hmtx, glyph_count, scalars) ⇒ Array<Hash>
Apply horizontal metrics deltas
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 |
# File 'lib/fontisan/variation/metrics_adjuster.rb', line 157 def apply_horizontal_metrics_deltas(hvar, hmtx, glyph_count, scalars) adjusted_metrics = [] glyph_count.times do |glyph_id| base_metric = hmtx.metric_for(glyph_id) next unless base_metric # Get advance width deltas advance_deltas = hvar.advance_width_delta_set(glyph_id) || [] lsb_deltas = hvar.lsb_delta_set(glyph_id) || [] # Apply deltas using scalars new_advance = @interpolator.interpolate_value( base_metric[:advance_width], advance_deltas, scalars, ).round new_lsb = @interpolator.interpolate_value( base_metric[:lsb], lsb_deltas, scalars, ).round adjusted_metrics << { advance_width: new_advance, lsb: new_lsb, } end adjusted_metrics end |
#apply_hvar_deltas(coordinates) ⇒ Boolean
Apply HVAR deltas to horizontal metrics
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
# File 'lib/fontisan/variation/metrics_adjuster.rb', line 42 def apply_hvar_deltas(coordinates) return false unless has_variation_table?("HVAR") return false unless has_variation_table?("hmtx") hvar = variation_table("HVAR") return false unless hvar&.item_variation_store # Parse region list and calculate scalars regions = extract_regions_from_store(hvar.item_variation_store) return false if regions.empty? scalars = @interpolator.calculate_scalars(coordinates, regions) # Get hmtx table hmtx = variation_table("hmtx") return false unless hmtx&.parsed? # Get glyph count maxp = variation_table("maxp") glyph_count = maxp ? maxp.num_glyphs : 0 return false if glyph_count.zero? # Apply deltas to each glyph adjusted_metrics = apply_horizontal_metrics_deltas( hvar, hmtx, glyph_count, scalars ) # Rebuild hmtx table with adjusted metrics rebuild_hmtx_table(adjusted_metrics) true end |
#apply_mvar_deltas(coordinates) ⇒ Boolean
Apply MVAR deltas to font-wide metrics
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/fontisan/variation/metrics_adjuster.rb', line 103 def apply_mvar_deltas(coordinates) return false unless has_variation_table?("MVAR") mvar = variation_table("MVAR") return false unless mvar&.item_variation_store # Parse region list and calculate scalars regions = extract_regions_from_store(mvar.item_variation_store) return false if regions.empty? scalars = @interpolator.calculate_scalars(coordinates, regions) # Apply deltas to each metric tag apply_font_wide_metrics_deltas(mvar, scalars) true end |
#apply_vvar_deltas(coordinates) ⇒ Boolean
Apply VVAR deltas to vertical metrics
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/fontisan/variation/metrics_adjuster.rb', line 79 def apply_vvar_deltas(coordinates) return false unless has_variation_table?("VVAR") return false unless has_variation_table?("vmtx") vvar = variation_table("VVAR") return false unless vvar&.item_variation_store # Parse region list and calculate scalars regions = extract_regions_from_store(vvar.item_variation_store) return false if regions.empty? @interpolator.calculate_scalars(coordinates, regions) # Apply deltas to vmtx # Similar to HVAR but for vertical metrics # Placeholder for full implementation true end |
#build_hmtx_data(metrics) ⇒ String?
Build hmtx binary data from metrics
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 |
# File 'lib/fontisan/variation/metrics_adjuster.rb', line 260 def build_hmtx_data(metrics) return nil if metrics.empty? # Find last unique advance width last_advance = metrics.last[:advance_width] number_of_h_metrics = metrics.length # Optimize: count from end while advance width is same (metrics.length - 1).downto(1) do |i| break if metrics[i][:advance_width] != last_advance number_of_h_metrics = i end # Build binary data data = String.new("", encoding: Encoding::BINARY) # Write hMetrics array number_of_h_metrics.times do |i| metric = metrics[i] data << [metric[:advance_width]].pack("n") # uint16 data << [metric[:lsb]].pack("n") # int16 (as uint16, will be interpreted as signed) end # Write remaining LSBs (number_of_h_metrics...metrics.length).each do |i| data << [metrics[i][:lsb]].pack("n") # int16 end # Update hhea's numberOfHMetrics update_hhea_number_of_h_metrics(number_of_h_metrics) data end |
#extract_regions_from_store(store) ⇒ Array<Hash>
Extract regions from ItemVariationStore
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
# File 'lib/fontisan/variation/metrics_adjuster.rb', line 127 def extract_regions_from_store(store) region_list = store.variation_region_list return [] unless region_list regions = [] region_list.regions.each do |region_coords| region = {} region_coords.each_with_index do |axis_coords, axis_index| next if axis_index >= @interpolator.axes.length axis = @interpolator.axes[axis_index] region[axis.axis_tag] = { start: axis_coords.start, peak: axis_coords.peak, end: axis_coords.end_value, } end regions << region end regions end |
#get_base_metric_value(tag) ⇒ Object
225 226 227 228 229 230 231 |
# File 'lib/fontisan/variation/metrics_adjuster.rb', line 225 def get_base_metric_value(tag) source = METRIC_SOURCES[tag] return nil unless source table_tag, method = source variation_table(table_tag)&.public_send(method) end |
#rebuild_hmtx_table(metrics) ⇒ Object
Rebuild hmtx table with adjusted metrics
248 249 250 251 252 253 254 |
# File 'lib/fontisan/variation/metrics_adjuster.rb', line 248 def rebuild_hmtx_table(metrics) # Build new hmtx binary data data = build_hmtx_data(metrics) # Update font's table data @font.table_data["hmtx"] = data if data end |
#update_font_metric(tag, value) ⇒ Object
Update font metric in appropriate table
237 238 239 240 241 242 243 |
# File 'lib/fontisan/variation/metrics_adjuster.rb', line 237 def update_font_metric(tag, value) # This is a placeholder - full implementation would: # 1. Modify the appropriate table's binary data # 2. Update the font's table data # For now, we just log the update # In production, this would rebuild the affected tables end |
#update_hhea_number_of_h_metrics(count) ⇒ Object
Update hhea table's numberOfHMetrics field
298 299 300 301 302 303 304 305 306 |
# File 'lib/fontisan/variation/metrics_adjuster.rb', line 298 def update_hhea_number_of_h_metrics(count) return unless has_variation_table?("hhea") hhea = variation_table("hhea") return unless hhea # Update the field if hhea supports it hhea.number_of_h_metrics = count end |