Class: Fontisan::Tables::Mvar

Inherits:
Binary::BaseRecord show all
Defined in:
lib/fontisan/tables/mvar.rb

Overview

Parser for the ‘MVAR’ (Metrics Variations) table

The MVAR table provides variation data for global font metrics such as:

  • Ascender and descender

  • Line gap

  • Caret offsets

  • Strikeout and underline positions/sizes

  • Subscript and superscript sizes

Each metric is identified by a tag and references delta sets in the ItemVariationStore.

Reference: OpenType specification, MVAR table

Examples:

Reading an MVAR table

data = font.table_data("MVAR")
mvar = Fontisan::Tables::Mvar.read(data)
hasc_deltas = mvar.metric_deltas("hasc")

Defined Under Namespace

Classes: ValueRecord

Constant Summary collapse

METRIC_TAGS =

Value tags for standard metrics

{
  "hasc" => :horizontal_ascender,
  "hdsc" => :horizontal_descender,
  "hlgp" => :horizontal_line_gap,
  "hcla" => :horizontal_caret_ascender,
  "hcld" => :horizontal_caret_descender,
  "hcof" => :horizontal_caret_offset,
  "vasc" => :vertical_ascender,
  "vdsc" => :vertical_descender,
  "vlgp" => :vertical_line_gap,
  "vcof" => :vertical_caret_offset,
  "xhgt" => :x_height,
  "cpht" => :cap_height,
  "sbxs" => :subscript_em_x_size,
  "sbys" => :subscript_em_y_size,
  "sbxo" => :subscript_em_x_offset,
  "sbyo" => :subscript_em_y_offset,
  "spxs" => :superscript_em_x_size,
  "spys" => :superscript_em_y_size,
  "spxo" => :superscript_em_x_offset,
  "spyo" => :superscript_em_y_offset,
  "strs" => :strikeout_size,
  "stro" => :strikeout_offset,
  "unds" => :underline_size,
  "undo" => :underline_offset,
}.freeze

Instance Method Summary collapse

Methods inherited from Binary::BaseRecord

#raw_data, read

Instance Method Details

#has_metric?(tag) ⇒ Boolean

Check if a specific metric is present

Parameters:

  • tag (String)

    Value tag

Returns:

  • (Boolean)

    True if metric is present



173
174
175
# File 'lib/fontisan/tables/mvar.rb', line 173

def has_metric?(tag)
  !value_record(tag).nil?
end

#item_variation_storeVariationCommon::ItemVariationStore?

Parse the item variation store

Returns:



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/fontisan/tables/mvar.rb', line 86

def item_variation_store
  return @item_variation_store if defined?(@item_variation_store)
  return @item_variation_store = nil if item_variation_store_offset.zero?

  data = raw_data
  offset = item_variation_store_offset

  return @item_variation_store = nil if offset >= data.bytesize

  store_data = data.byteslice(offset..-1)
  @item_variation_store = VariationCommon::ItemVariationStore.read(store_data)
rescue StandardError => e
  warn "Failed to parse MVAR item variation store: #{e.message}"
  @item_variation_store = nil
end

#metric_delta_set(tag) ⇒ Array<Integer>?

Get delta set for a specific metric tag

Parameters:

  • tag (String)

    Value tag (e.g., “hasc”, “hdsc”)

Returns:

  • (Array<Integer>, nil)

    Delta values or nil



135
136
137
138
139
140
141
142
143
144
145
# File 'lib/fontisan/tables/mvar.rb', line 135

def metric_delta_set(tag)
  return nil unless item_variation_store

  record = value_record(tag)
  return nil if record.nil?

  item_variation_store.delta_set(
    record.delta_set_outer_index,
    record.delta_set_inner_index,
  )
end

#metric_tagsArray<String>

Get all metric tags present in this table

Returns:

  • (Array<String>)

    Array of metric tags



150
151
152
# File 'lib/fontisan/tables/mvar.rb', line 150

def metric_tags
  value_records.map { |record| record.value_tag.to_s }
end

#metricsHash<String, Hash>

Get all metrics as a hash

Returns:

  • (Hash<String, Hash>)

    Hash of metric tag to record info



157
158
159
160
161
162
163
164
165
166
167
# File 'lib/fontisan/tables/mvar.rb', line 157

def metrics
  value_records.each_with_object({}) do |record, hash|
    # Strip trailing nulls from value_tag
    tag = record.value_tag.delete("\x00")
    hash[tag] = {
      name: record.metric_name,
      outer_index: record.delta_set_outer_index,
      inner_index: record.delta_set_inner_index,
    }
  end
end

#valid?Boolean

Check if table is valid

Returns:

  • (Boolean)

    True if valid



180
181
182
# File 'lib/fontisan/tables/mvar.rb', line 180

def valid?
  major_version == 1 && minor_version.zero?
end

#value_record(tag) ⇒ ValueRecord?

Get value record by tag

Parameters:

  • tag (String)

    Value tag (e.g., “hasc”, “hdsc”)

Returns:



127
128
129
# File 'lib/fontisan/tables/mvar.rb', line 127

def value_record(tag)
  value_records.find { |record| record.value_tag.to_s == tag }
end

#value_recordsArray<ValueRecord>

Parse value records

Returns:



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/fontisan/tables/mvar.rb', line 105

def value_records
  return @value_records if @value_records
  return @value_records = [] if value_record_count.zero?

  data = raw_data
  # Value records start after the header (14 bytes: 2+2+2+2+2+4)
  offset = 14

  @value_records = Array.new(value_record_count) do |i|
    record_offset = offset + (i * value_record_size)

    next nil if record_offset + value_record_size > data.bytesize

    record_data = data.byteslice(record_offset, value_record_size)
    ValueRecord.read(record_data)
  end.compact
end

#versionFloat

Get version as a float

Returns:

  • (Float)

    Version number (e.g., 1.0)



79
80
81
# File 'lib/fontisan/tables/mvar.rb', line 79

def version
  major_version + (minor_version / 10.0)
end