Class: Fontisan::Tables::Glyf
- Inherits:
-
Binary::BaseRecord
- Object
- BinData::Record
- Binary::BaseRecord
- Fontisan::Tables::Glyf
- 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
Instance Attribute Summary collapse
-
#glyphs_cache ⇒ Hash
readonly
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 inherited from Binary::BaseRecord
Instance Attribute Details
#glyphs_cache ⇒ Hash (readonly)
Lazy initialization of glyphs cache
52 53 54 |
# File 'lib/fontisan/tables/glyf.rb', line 52 def glyphs_cache @glyphs_cache end |
#raw_data ⇒ Object
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
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
#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
266 267 268 269 270 271 272 273 274 275 |
# File 'lib/fontisan/tables/glyf.rb', line 266 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
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
150 151 152 |
# File 'lib/fontisan/tables/glyf.rb', line 150 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.
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.
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 |
#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
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 |
# File 'lib/fontisan/tables/glyf.rb', line 218 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.respond_to?(:instruction_length) inst_len = glyph.instruction_length # If instructions present, length should be reasonable inst_len.nil? || inst_len >= 0 else # Compound glyphs may have instructions too true 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
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 |
# File 'lib/fontisan/tables/glyf.rb', line 189 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
169 170 171 172 173 174 175 176 177 178 179 |
# File 'lib/fontisan/tables/glyf.rb', line 169 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
243 244 245 246 247 248 249 250 251 252 253 254 255 256 |
# File 'lib/fontisan/tables/glyf.rb', line 243 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 if glyph.respond_to?(:num_contours) glyph.num_contours >= -1 else true end rescue StandardError false end |