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



29
30
31
# File 'lib/fontisan/tables/hmtx_table.rb', line 29

def hhea_table
  @hhea_table
end

#maxp_tableObject (readonly)

Cache for context tables



29
30
31
# File 'lib/fontisan/tables/hmtx_table.rb', line 29

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



80
81
82
83
# File 'lib/fontisan/tables/hmtx_table.rb', line 80

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



130
131
132
133
134
# File 'lib/fontisan/tables/hmtx_table.rb', line 130

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



139
140
141
142
143
# File 'lib/fontisan/tables/hmtx_table.rb', line 139

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



60
61
62
# File 'lib/fontisan/tables/hmtx_table.rb', line 60

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



120
121
122
123
124
125
# File 'lib/fontisan/tables/hmtx_table.rb', line 120

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



89
90
91
92
# File 'lib/fontisan/tables/hmtx_table.rb', line 89

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



69
70
71
72
73
74
# File 'lib/fontisan/tables/hmtx_table.rb', line 69

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



107
108
109
110
111
112
# File 'lib/fontisan/tables/hmtx_table.rb', line 107

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



97
98
99
100
101
102
# File 'lib/fontisan/tables/hmtx_table.rb', line 97

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



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

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



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

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