Class: Fontisan::Tables::Glyf
- Inherits:
-
Binary::BaseRecord
- Object
- BinData::Record
- Binary::BaseRecord
- Fontisan::Tables::Glyf
- Extended by:
- Registered
- 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 https://docs.microsoft.com/en-us/typography/opentype/spec/glyf
Instance Attribute Summary collapse
-
#glyphs_cache ⇒ Hash
Lazy initialization of glyphs cache.
-
#raw_data ⇒ Object
Store the raw data for deferred parsing.
Class Method Summary collapse
-
.read(io) ⇒ Glyf
Override read to capture raw data.
Instance Method Summary collapse
-
#all_glyphs_accessible?(loca, head, num_glyphs) ⇒ Boolean
Validation helper: Check if all glyphs are accessible.
-
#cache_size ⇒ Integer
Get the number of cached glyphs.
-
#cached?(glyph_id) ⇒ Boolean
Check if a glyph is cached.
-
#clear_cache ⇒ void
Clear the glyph cache to free memory.
-
#glyph_for(glyph_id, loca, head) ⇒ SimpleGlyph, ...
Get glyph data for a specific glyph ID.
-
#instructions_sound?(loca, head, num_glyphs) ⇒ Boolean
Validation helper: Check if TrueType instructions are sound.
-
#no_clipped_glyphs?(loca, head, num_glyphs) ⇒ Boolean
Validation helper: Check if any glyphs are clipped (exceed bounds).
-
#no_empty_glyphs_except_special?(loca, _head, num_glyphs) ⇒ Boolean
Validation helper: Check if all non-special glyphs have contours.
-
#valid_contour_count?(glyph_id, loca, head) ⇒ Boolean
Validation helper: Check if glyph has valid number of contours.
Methods included from Registered
Methods inherited from Binary::BaseRecord
Instance Attribute Details
#glyphs_cache ⇒ Hash
Lazy initialization of glyphs cache
49 50 51 |
# File 'lib/fontisan/tables/glyf.rb', line 49 def glyphs_cache @glyphs_cache end |
#raw_data ⇒ Object
Store the raw data for deferred parsing
45 46 47 |
# File 'lib/fontisan/tables/glyf.rb', line 45 def raw_data @raw_data end |
Class Method Details
.read(io) ⇒ Glyf
Override read to capture raw data
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
# File 'lib/fontisan/tables/glyf.rb', line 55 def self.read(io) instance = new # Initialize cache instance.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
#all_glyphs_accessible?(loca, head, num_glyphs) ⇒ Boolean
Validation helper: Check if all glyphs are accessible
Attempts to access each glyph to ensure no corruption
264 265 266 267 268 269 270 271 272 273 |
# File 'lib/fontisan/tables/glyf.rb', line 264 def all_glyphs_accessible?(loca, head, num_glyphs) (0...num_glyphs).all? do |glyph_id| glyph_for(glyph_id, loca, head) true rescue Fontisan::CorruptedTableError false end rescue StandardError false end |
#cache_size ⇒ Integer
Get the number of cached glyphs
139 140 141 |
# File 'lib/fontisan/tables/glyf.rb', line 139 def cache_size glyphs_cache.size end |
#cached?(glyph_id) ⇒ Boolean
Check if a glyph is cached
147 148 149 |
# File 'lib/fontisan/tables/glyf.rb', line 147 def cached?(glyph_id) glyphs_cache.key?(glyph_id) end |
#clear_cache ⇒ void
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.
132 133 134 |
# File 'lib/fontisan/tables/glyf.rb', line 132 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.
94 95 96 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 |
# File 'lib/fontisan/tables/glyf.rb', line 94 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 |
#instructions_sound?(loca, head, num_glyphs) ⇒ Boolean
Validation helper: Check if TrueType instructions are sound
Validates that glyph instructions (if present) are parseable This is a basic check that ensures instructions exist and have valid length
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 |
# File 'lib/fontisan/tables/glyf.rb', line 215 def instructions_sound?(loca, head, num_glyphs) (0...num_glyphs).all? do |glyph_id| glyph = glyph_for(glyph_id, loca, head) next true if glyph.nil? # Empty glyphs are OK # Simple glyphs have instructions if glyph.nil? # Compound glyphs may have instructions too true else inst_len = glyph.instruction_length # If instructions present, length should be reasonable inst_len.nil? || inst_len >= 0 end end rescue StandardError false end |
#no_clipped_glyphs?(loca, head, num_glyphs) ⇒ Boolean
Validation helper: Check if any glyphs are clipped (exceed bounds)
Validates that glyph coordinates don't exceed head table's bounding box
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 |
# File 'lib/fontisan/tables/glyf.rb', line 186 def no_clipped_glyphs?(loca, head, num_glyphs) font_x_min = head.x_min font_y_min = head.y_min font_x_max = head.x_max font_y_max = head.y_max (0...num_glyphs).all? do |glyph_id| glyph = glyph_for(glyph_id, loca, head) next true if glyph.nil? # Empty glyphs are OK # Check if glyph bounds are within font bounds glyph.x_min >= font_x_min && glyph.y_min >= font_y_min && glyph.x_max <= font_x_max && glyph.y_max <= font_y_max end rescue StandardError false end |
#no_empty_glyphs_except_special?(loca, _head, num_glyphs) ⇒ Boolean
Validation helper: Check if all non-special glyphs have contours
The .notdef glyph (ID 0) can be empty, but other glyphs should have geometry
166 167 168 169 170 171 172 173 174 175 176 |
# File 'lib/fontisan/tables/glyf.rb', line 166 def no_empty_glyphs_except_special?(loca, _head, num_glyphs) # Check glyphs 1 through num_glyphs-1 (.notdef at 0 can be empty) (1...num_glyphs).all? do |glyph_id| size = loca.size_of(glyph_id) # Empty glyphs (like space) are allowed, but check if they should be empty # This is a basic check - we just ensure non-control glyphs have data size.nil? || size.positive? end rescue StandardError false end |
#valid_contour_count?(glyph_id, loca, head) ⇒ Boolean
Validation helper: Check if glyph has valid number of contours
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 |
# File 'lib/fontisan/tables/glyf.rb', line 240 def valid_contour_count?(glyph_id, loca, head) glyph = glyph_for(glyph_id, loca, head) return true if glyph.nil? # Empty glyphs are OK # Simple glyphs: contours should be >= 0 # Compound glyphs: numberOfContours = -1 case glyph when SimpleGlyph, CompoundGlyph glyph.num_contours >= -1 else true end rescue StandardError false end |