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 https://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
46 47 48 |
# File 'lib/fontisan/tables/glyf.rb', line 46 def glyphs_cache @glyphs_cache end |
#raw_data ⇒ Object
Store the raw data for deferred parsing
42 43 44 |
# File 'lib/fontisan/tables/glyf.rb', line 42 def raw_data @raw_data end |
Class Method Details
.read(io) ⇒ Glyf
Override read to capture raw data
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/fontisan/tables/glyf.rb', line 52 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
260 261 262 263 264 265 266 267 268 269 |
# File 'lib/fontisan/tables/glyf.rb', line 260 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
136 137 138 |
# File 'lib/fontisan/tables/glyf.rb', line 136 def cache_size glyphs_cache.size end |
#cached?(glyph_id) ⇒ Boolean
Check if a glyph is cached
144 145 146 |
# File 'lib/fontisan/tables/glyf.rb', line 144 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.
129 130 131 |
# File 'lib/fontisan/tables/glyf.rb', line 129 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.
91 92 93 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 |
# File 'lib/fontisan/tables/glyf.rb', line 91 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
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 |
# File 'lib/fontisan/tables/glyf.rb', line 212 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
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 |
# File 'lib/fontisan/tables/glyf.rb', line 183 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
163 164 165 166 167 168 169 170 171 172 173 |
# File 'lib/fontisan/tables/glyf.rb', line 163 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
237 238 239 240 241 242 243 244 245 246 247 248 249 250 |
# File 'lib/fontisan/tables/glyf.rb', line 237 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 |