Class: Fontisan::Tables::HmtxTable

Inherits:
SfntTable
  • Object
show all
Defined in:
lib/fontisan/tables/hmtx_table.rb

Overview

OOP representation of the 'hmtx' (Horizontal Metrics) table

The hmtx table contains horizontal metrics for each glyph in the font, providing advance width and left sidebearing values needed for proper glyph positioning and text layout.

This class extends SfntTable to provide hmtx-specific convenience methods for accessing glyph metrics. The hmtx table requires context from the hhea and maxp tables to function properly.

Examples:

Accessing horizontal metrics

hmtx = font.sfnt_table("hmtx")
hhea = font.table("hhea")
maxp = font.table("maxp")

hmtx.parse_with_context(hhea.number_of_h_metrics, maxp.num_glyphs)
metric = hmtx.metric_for(42)  # Get glyph metrics
metric[:advance_width] # => 1000
metric[:lsb]         # => 50

Instance Attribute Summary collapse

Attributes inherited from SfntTable

#data, #entry, #font, #parsed

Instance Method Summary collapse

Methods inherited from SfntTable

#available?, #calculate_checksum, #checksum, #data_loaded?, #human_name, #initialize, #inspect, #length, #load_data!, #offset, #parse, #parsed?, #required?, #tag, #to_s, #validate!

Constructor Details

This class inherits a constructor from Fontisan::SfntTable

Instance Attribute Details

#hhea_tableObject (readonly)

Cache for context tables



26
27
28
# File 'lib/fontisan/tables/hmtx_table.rb', line 26

def hhea_table
  @hhea_table
end

#maxp_tableObject (readonly)

Cache for context tables



26
27
28
# File 'lib/fontisan/tables/hmtx_table.rb', line 26

def maxp_table
  @maxp_table
end

Instance Method Details

#advance_width_for(glyph_id) ⇒ Integer?

Get advance width for a glyph

Parameters:

  • glyph_id (Integer)

    Glyph ID

Returns:

  • (Integer, nil)

    Advance width in FUnits, or nil



77
78
79
80
# File 'lib/fontisan/tables/hmtx_table.rb', line 77

def advance_width_for(glyph_id)
  metric = metric_for(glyph_id)
  metric&.dig(:advance_width)
end

#all_advance_widthsArray<Integer>

Get all advance widths

Returns:

  • (Array<Integer>)

    Array of advance widths



127
128
129
130
131
# File 'lib/fontisan/tables/hmtx_table.rb', line 127

def all_advance_widths
  return [] unless has_context?

  (0...num_glyphs).map { |gid| advance_width_for(gid) || 0 }
end

#all_lsbsArray<Integer>

Get all left sidebearings

Returns:

  • (Array<Integer>)

    Array of LSB values



136
137
138
139
140
# File 'lib/fontisan/tables/hmtx_table.rb', line 136

def all_lsbs
  return [] unless has_context?

  (0...num_glyphs).map { |gid| lsb_for(gid) || 0 }
end

#has_context?Boolean

Check if table has been parsed with context

Returns:

  • (Boolean)

    true if context is available



57
58
59
# File 'lib/fontisan/tables/hmtx_table.rb', line 57

def has_context?
  !@number_of_h_metrics.nil? && !@num_glyphs.nil?
end

#has_unique_metrics?(glyph_id) ⇒ Boolean

Check if a glyph has its own metrics

Glyphs with ID < numberOfHMetrics have their own advance width

Parameters:

  • glyph_id (Integer)

    Glyph ID

Returns:

  • (Boolean)

    true if glyph has unique metrics



117
118
119
120
121
122
# File 'lib/fontisan/tables/hmtx_table.rb', line 117

def has_unique_metrics?(glyph_id)
  num = number_of_h_metrics
  return false if num.nil?

  glyph_id < num
end

#lsb_for(glyph_id) ⇒ Integer?

Get left sidebearing for a glyph

Parameters:

  • glyph_id (integer)

    Glyph ID

Returns:

  • (Integer, nil)

    Left sidebearing in FUnits, or nil



86
87
88
89
# File 'lib/fontisan/tables/hmtx_table.rb', line 86

def lsb_for(glyph_id)
  metric = metric_for(glyph_id)
  metric&.dig(:lsb)
end

#metric_for(glyph_id) ⇒ Hash?

Get horizontal metrics for a glyph

Parameters:

  • glyph_id (Integer)

    Glyph ID (0-based)

Returns:

  • (Hash, nil)

    Hash with :advance_width and :lsb keys, or nil

Raises:

  • (ArgumentError)

    if context not set



66
67
68
69
70
71
# File 'lib/fontisan/tables/hmtx_table.rb', line 66

def metric_for(glyph_id)
  ensure_context!
  return nil unless parsed

  parsed.metric_for(glyph_id)
end

#num_glyphsInteger?

Get total number of glyphs

Returns:

  • (Integer, nil)

    Total glyph count, or nil if not parsed



104
105
106
107
108
109
# File 'lib/fontisan/tables/hmtx_table.rb', line 104

def num_glyphs
  return @num_glyphs if @num_glyphs
  return nil unless parsed

  parsed.num_glyphs
end

#number_of_h_metricsInteger?

Get number of horizontal metrics

Returns:

  • (Integer, nil)

    Number of hMetrics, or nil if not parsed



94
95
96
97
98
99
# File 'lib/fontisan/tables/hmtx_table.rb', line 94

def number_of_h_metrics
  return @number_of_h_metrics if @number_of_h_metrics
  return nil unless parsed

  parsed.number_of_h_metrics
end

#parse_with_context(number_of_h_metrics, num_glyphs) ⇒ self

Parse the hmtx table with required context

The hmtx table cannot be used without context from hhea and maxp tables.

Parameters:

  • number_of_h_metrics (Integer)

    Number of LongHorMetric records (from hhea)

  • num_glyphs (Integer)

    Total number of glyphs (from maxp)

Returns:

  • (self)

    Returns self for chaining

Raises:

  • (ArgumentError)

    if context is invalid



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/fontisan/tables/hmtx_table.rb', line 36

def parse_with_context(number_of_h_metrics, num_glyphs)
  unless number_of_h_metrics && num_glyphs
    raise ArgumentError,
          "hmtx table requires number_of_h_metrics and num_glyphs"
  end

  @number_of_h_metrics = number_of_h_metrics
  @num_glyphs = num_glyphs

  # Ensure parsed data is loaded
  parse

  # Parse with context
  parsed.parse_with_context(number_of_h_metrics, num_glyphs)

  self
end

#statisticsHash

Get metrics statistics

Returns:

  • (Hash)

    Statistics about horizontal metrics



145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/fontisan/tables/hmtx_table.rb', line 145

def statistics
  return {} unless has_context?

  widths = all_advance_widths
  lsbs = all_lsbs

  {
    num_glyphs: num_glyphs,
    number_of_h_metrics: number_of_h_metrics,
    min_advance_width: widths.min,
    max_advance_width: widths.max,
    avg_advance_width: widths.sum.fdiv(widths.size).round(2),
    min_lsb: lsbs.min,
    max_lsb: lsbs.max,
  }
end