Class: Fontisan::Tables::Glyf

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

Overview

Parser for the ‘glyf’ (Glyph Data) table

The glyf table contains TrueType glyph outline data. Each glyph is described by either a simple glyph (with contours and points) or a compound glyph (composed of other glyphs with transformations).

The glyf table is accessed via offsets from the loca table, which provides the byte offset and size for each glyph. Empty glyphs (e.g., space characters) have zero size in loca.

Glyph types are determined by numberOfContours:

  • numberOfContours >= 0: Simple glyph with that many contours

  • numberOfContours == -1: Compound glyph composed of other glyphs

The glyf table is context-dependent and requires:

  • loca table (for glyph offsets and sizes)

  • head table (for coordinate interpretation and flags)

Reference: OpenType specification, glyf table docs.microsoft.com/en-us/typography/opentype/spec/glyf

Examples:

Accessing a glyph

# Get required tables first
head = font.table('head')
loca = font.table('loca')
loca.parse_with_context(head.index_to_loc_format, maxp.num_glyphs)

# Parse glyf table
data = font.read_table_data('glyf')
glyf = Fontisan::Tables::Glyf.read(data)

# Get a specific glyph
glyph = glyf.glyph_for(42, loca, head)
puts glyph.simple? ? "Simple glyph" : "Compound glyph"
puts glyph.bounding_box  # => [xMin, yMin, xMax, yMax]

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Binary::BaseRecord

#valid?

Instance Attribute Details

#glyphs_cacheHash (readonly)

Lazy initialization of glyphs cache

Returns:

  • (Hash)

    The glyphs cache



52
53
54
# File 'lib/fontisan/tables/glyf.rb', line 52

def glyphs_cache
  @glyphs_cache
end

#raw_dataObject

Store the raw data for deferred parsing



48
49
50
# File 'lib/fontisan/tables/glyf.rb', line 48

def raw_data
  @raw_data
end

Class Method Details

.read(io) ⇒ Glyf

Override read to capture raw data

Parameters:

  • io (IO, String)

    Input data

Returns:

  • (Glyf)

    Parsed table instance



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/fontisan/tables/glyf.rb', line 58

def self.read(io)
  instance = new

  # Initialize cache
  instance.instance_variable_set(:@glyphs_cache, {})

  # Handle nil or empty data gracefully
  instance.raw_data = if io.nil?
                        "".b
                      elsif io.is_a?(String)
                        io
                      else
                        io.read || "".b
                      end

  instance
end

Instance Method Details

#cache_sizeInteger

Get the number of cached glyphs

Returns:

  • (Integer)

    Number of glyphs in cache



142
143
144
# File 'lib/fontisan/tables/glyf.rb', line 142

def cache_size
  glyphs_cache.size
end

#cached?(glyph_id) ⇒ Boolean

Check if a glyph is cached

Parameters:

  • glyph_id (Integer)

    Glyph ID to check

Returns:

  • (Boolean)

    True if glyph is cached



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

def cached?(glyph_id)
  glyphs_cache.key?(glyph_id)
end

#clear_cachevoid

This method returns an undefined value.

Clear the glyph cache to free memory

This is useful for long-running processes that parse many glyphs but don’t need to keep them all in memory.



135
136
137
# File 'lib/fontisan/tables/glyf.rb', line 135

def clear_cache
  glyphs_cache.clear
end

#glyph_for(glyph_id, loca, head) ⇒ SimpleGlyph, ...

Get glyph data for a specific glyph ID

This method retrieves and parses the glyph at the specified ID. It uses the loca table to determine the offset and size, then parses the glyph data to create either a SimpleGlyph or CompoundGlyph.

Results are cached to avoid re-parsing the same glyph multiple times.

Examples:

Getting a simple glyph

glyph = glyf.glyph_for(65, loca, head)  # 'A' character
if glyph.simple?
  puts "Contours: #{glyph.num_contours}"
  puts "Points: #{glyph.x_coordinates.length}"
end

Parameters:

  • glyph_id (Integer)

    Glyph ID (0-based, 0 is .notdef)

  • loca (Loca)

    Parsed loca table with offsets

  • head (Head)

    Parsed head table for coordinate interpretation

Returns:

Raises:



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/fontisan/tables/glyf.rb', line 97

def glyph_for(glyph_id, loca, head)
  # Return cached glyph if available
  return glyphs_cache[glyph_id] if glyphs_cache.key?(glyph_id)

  # Validate inputs
  validate_context!(loca, head, glyph_id)

  # Get offset and size from loca table
  offset = loca.offset_for(glyph_id)
  size = loca.size_of(glyph_id)

  # Empty glyph (e.g., space character)
  if size.nil? || size.zero?
    glyphs_cache[glyph_id] = nil
    return nil
  end

  # Validate offset and size
  if offset + size > raw_data.length
    raise Fontisan::CorruptedTableError,
          "Glyph #{glyph_id} extends beyond glyf table: " \
          "offset=#{offset}, size=#{size}, table_size=#{raw_data.length}"
  end

  # Extract glyph data
  glyph_data = raw_data[offset, size]

  # Parse glyph
  glyph = parse_glyph_data(glyph_data, glyph_id)
  glyphs_cache[glyph_id] = glyph
end