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:



88
89
90
91
92
93
# File 'lib/fontisan/sfnt_table.rb', line 88

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



49
50
51
# File 'lib/fontisan/sfnt_table.rb', line 49

def data
  @data
end

#entryTableDirectory (readonly)

Table metadata entry (from TableDirectory)

Returns:



39
40
41
# File 'lib/fontisan/sfnt_table.rb', line 39

def entry
  @entry
end

#fontSfntFont (readonly)

Parent font containing this table

Returns:

  • (SfntFont)

    The font that contains this table



44
45
46
# File 'lib/fontisan/sfnt_table.rb', line 44

def font
  @font
end

#parsedObject? (readonly)

Parsed table object (cached)

Returns:

  • (Object, nil)

    Parsed table object, or nil if not parsed



54
55
56
# File 'lib/fontisan/sfnt_table.rb', line 54

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



204
205
206
# File 'lib/fontisan/sfnt_table.rb', line 204

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

#calculate_checksumInteger

Calculate table checksum

Returns:

  • (Integer)

    The checksum of the table data



195
196
197
198
199
# File 'lib/fontisan/sfnt_table.rb', line 195

def calculate_checksum
  load_data! unless data_loaded?

  Utilities::ChecksumCalculator.calculate_table_checksum(@data)
end

#checksumInteger

Table checksum

Returns:

  • (Integer)

    The table checksum



66
67
68
# File 'lib/fontisan/sfnt_table.rb', line 66

def checksum
  @entry.checksum
end

#data_loaded?Boolean

Check if table data is loaded

Returns:

  • (Boolean)

    true if table data has been loaded



125
126
127
# File 'lib/fontisan/sfnt_table.rb', line 125

def data_loaded?
  !@data.nil?
end

#human_nameString

Get human-readable table name

Returns:

  • (String)

    Human-readable name



218
219
220
# File 'lib/fontisan/sfnt_table.rb', line 218

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

#inspectString

String representation

Returns:

  • (String)

    Human-readable representation



225
226
227
# File 'lib/fontisan/sfnt_table.rb', line 225

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



80
81
82
# File 'lib/fontisan/sfnt_table.rb', line 80

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



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

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



73
74
75
# File 'lib/fontisan/sfnt_table.rb', line 73

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



143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/fontisan/sfnt_table.rb', line 143

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



132
133
134
# File 'lib/fontisan/sfnt_table.rb', line 132

def parsed?
  !@parsed.nil?
end

#required?Boolean

Check if table is required for the font

Returns:

  • (Boolean)

    true if table is required



211
212
213
# File 'lib/fontisan/sfnt_table.rb', line 211

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

#tagString

Table tag (4-character string)

Returns:

  • (String)

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



59
60
61
# File 'lib/fontisan/sfnt_table.rb', line 59

def tag
  @entry.tag
end

#to_sString

String representation for display

Returns:

  • (String)

    Human-readable representation



232
233
234
# File 'lib/fontisan/sfnt_table.rb', line 232

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:



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

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