Class: Fontisan::SfntTable Abstract

Inherits:
Object
  • Object
show all
Defined in:
lib/fontisan/sfnt_table.rb

Overview

This class is abstract.

Subclasses should override ‘parser_class` and `validate_parsed_table?`

Base class for SFNT font tables

Represents a single table in an SFNT font file, encapsulating:

  • Table metadata (tag, checksum, offset, length)

  • Lazy loading of table data

  • Parsing of table data into structured objects

  • Table-specific validation

This class provides an OOP representation of font tables, replacing the previous separation of TableDirectory (metadata), @table_data (raw bytes), and @parsed_tables (parsed objects) with a single cohesive domain object.

Examples:

Accessing table metadata

table = SfntTable.new(font, entry)
puts table.tag        # => "head"
puts table.checksum   # => 0x12345678
puts table.offset     # => 0x0000012C
puts table.length     # => 54

Lazy loading table data

table.load_data!  # Loads raw bytes from IO
puts table.data.bytesize

Parsing table data

head_table = table.parse
puts head_table.units_per_em

Validating table

table.validate!  # Raises InvalidFontError if invalid

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(font, entry) ⇒ SfntTable

Initialize a new SfntTable

Parameters:



91
92
93
94
95
96
# File 'lib/fontisan/sfnt_table.rb', line 91

def initialize(font, entry)
  @font = font
  @entry = entry
  @data = nil
  @parsed = nil
end

Instance Attribute Details

#dataString? (readonly)

Raw table data (loaded lazily)

Returns:

  • (String, nil)

    Raw binary table data, or nil if not loaded



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

def data
  @data
end

#entryTableDirectory (readonly)

Table metadata entry (from TableDirectory)

Returns:



42
43
44
# File 'lib/fontisan/sfnt_table.rb', line 42

def entry
  @entry
end

#fontSfntFont (readonly)

Parent font containing this table

Returns:

  • (SfntFont)

    The font that contains this table



47
48
49
# File 'lib/fontisan/sfnt_table.rb', line 47

def font
  @font
end

#parsedObject? (readonly)

Parsed table object (cached)

Returns:

  • (Object, nil)

    Parsed table object, or nil if not parsed



57
58
59
# File 'lib/fontisan/sfnt_table.rb', line 57

def parsed
  @parsed
end

Instance Method Details

#available?Boolean

Check if table is available in current loading mode

Returns:

  • (Boolean)

    true if table is available



208
209
210
# File 'lib/fontisan/sfnt_table.rb', line 208

def available?
  @font.table_available?(tag)
end

#calculate_checksumInteger

Calculate table checksum

Returns:

  • (Integer)

    The checksum of the table data



198
199
200
201
202
203
# File 'lib/fontisan/sfnt_table.rb', line 198

def calculate_checksum
  load_data! unless data_loaded?

  require_relative "utilities/checksum_calculator"
  Utilities::ChecksumCalculator.calculate_table_checksum(@data)
end

#checksumInteger

Table checksum

Returns:

  • (Integer)

    The table checksum



69
70
71
# File 'lib/fontisan/sfnt_table.rb', line 69

def checksum
  @entry.checksum
end

#data_loaded?Boolean

Check if table data is loaded

Returns:

  • (Boolean)

    true if table data has been loaded



128
129
130
# File 'lib/fontisan/sfnt_table.rb', line 128

def data_loaded?
  !@data.nil?
end

#human_nameString

Get human-readable table name

Returns:

  • (String)

    Human-readable name



222
223
224
# File 'lib/fontisan/sfnt_table.rb', line 222

def human_name
  Constants::TABLE_NAMES[tag] || tag
end

#inspectString

String representation

Returns:

  • (String)

    Human-readable representation



229
230
231
# File 'lib/fontisan/sfnt_table.rb', line 229

def inspect
  "#<#{self.class.name} tag=#{tag.inspect} offset=0x#{offset.to_s(16).upcase} length=#{length}>"
end

#lengthInteger

Table length in bytes

Returns:

  • (Integer)

    Table data length in bytes



83
84
85
# File 'lib/fontisan/sfnt_table.rb', line 83

def length
  @entry.table_length
end

#load_data!self

Load raw table data from font file

Reads the table data from the font’s IO source or from cached table data. This method supports lazy loading.

Returns:

  • (self)

    Returns self for chaining

Raises:

  • (RuntimeError)

    if table data cannot be loaded



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/fontisan/sfnt_table.rb', line 105

def load_data!
  # Check if already loaded
  return self if @data

  # Try to get from font's table_data cache
  if @font.table_data && @font.table_data[tag]
    @data = @font.table_data[tag]
    return self
  end

  # Load from IO source if available
  if @font.io_source
    @font.io_source.seek(offset)
    @data = @font.io_source.read(length)
    return self
  end

  raise "Cannot load table '#{tag}': no IO source or cached data"
end

#offsetInteger

Table offset in font file

Returns:

  • (Integer)

    Byte offset of table data



76
77
78
# File 'lib/fontisan/sfnt_table.rb', line 76

def offset
  @entry.offset
end

#parseObject?

Parse table data into structured object

Loads data if needed, then parses using the table-specific parser class. Results are cached for subsequent calls.

Returns:

  • (Object, nil)

    Parsed table object, or nil if no parser available

Raises:

  • (RuntimeError)

    if table data cannot be loaded for parsing



146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/fontisan/sfnt_table.rb', line 146

def parse
  return @parsed if parsed?

  # Load data if not already loaded
  load_data! unless data_loaded?

  # Get parser class for this table type
  parser = parser_class
  return nil unless parser

  # Parse and cache
  @parsed = parser.read(@data)
  @parsed
end

#parsed?Boolean

Check if table has been parsed

Returns:

  • (Boolean)

    true if table has been parsed



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

def parsed?
  !@parsed.nil?
end

#required?Boolean

Check if table is required for the font

Returns:

  • (Boolean)

    true if table is required



215
216
217
# File 'lib/fontisan/sfnt_table.rb', line 215

def required?
  Constants::REQUIRED_TABLES.include?(tag)
end

#tagString

Table tag (4-character string)

Returns:

  • (String)

    The table tag (e.g., “head”, “name”, “cmap”)



62
63
64
# File 'lib/fontisan/sfnt_table.rb', line 62

def tag
  @entry.tag
end

#to_sString

String representation for display

Returns:

  • (String)

    Human-readable representation



236
237
238
# File 'lib/fontisan/sfnt_table.rb', line 236

def to_s
  "#{tag}: #{human_name} (#{length} bytes @ 0x#{offset.to_s(16).upcase})"
end

#validate!Boolean

Validate the table

Performs table-specific validation. Subclasses should override ‘validate_parsed_table?` to provide custom validation logic.

Returns:

  • (Boolean)

    true if table is valid

Raises:



168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/fontisan/sfnt_table.rb', line 168

def validate!
  # Ensure data is loaded
  load_data! unless data_loaded?

  # Basic validation: data size matches expected size
  if @data.bytesize != length
    raise InvalidFontError,
          "Table '#{tag}' data size mismatch: expected #{length} bytes, got #{@data.bytesize}"
  end

  # Validate checksum if not head table (head table checksum is special)
  if tag != Constants::HEAD_TAG
    expected_checksum = calculate_checksum
    if checksum != expected_checksum
      # Checksum mismatch might be OK for some tables, log a warning
      # But don't fail validation for it
    end
  end

  # Table-specific validation (if parsed)
  if parsed?
    validate_parsed_table?
  end

  true
end