Class: Fontisan::Tables::Colr

Inherits:
Binary::BaseRecord show all
Defined in:
lib/fontisan/tables/colr.rb

Overview

COLR (Color) table parser

The COLR table defines layered color glyphs where each layer references a glyph ID and a palette index from the CPAL table. This enables fonts to display multi-colored glyphs such as emoji or brand logos.

COLR Table Structure:

COLR Table = Header (14 bytes)
           + Base Glyph Records (6 bytes each)
           + Layer Records (4 bytes each)

Version 0 Structure:

  • version (uint16): Table version (0)
  • numBaseGlyphRecords (uint16): Number of base glyphs
  • baseGlyphRecordsOffset (uint32): Offset to base glyph records array
  • layerRecordsOffset (uint32): Offset to layer records array
  • numLayerRecords (uint16): Number of layer records

The COLR table must be used together with the CPAL (Color Palette) table which defines the actual RGB color values referenced by palette indices.

Reference: OpenType COLR specification https://docs.microsoft.com/en-us/typography/opentype/spec/colr

Examples:

Reading a COLR table

data = font.table_data['COLR']
colr = Fontisan::Tables::Colr.read(data)
layers = colr.layers_for_glyph(42)
puts "Glyph 42 has #{layers.length} color layers"

Defined Under Namespace

Classes: BaseGlyphRecord, LayerRecord

Constant Summary collapse

TAG =

OpenType table tag for COLR

"COLR"

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#base_glyph_recordsArray<BaseGlyphRecord> (readonly)

Returns Parsed base glyph records.

Returns:



96
97
98
# File 'lib/fontisan/tables/colr.rb', line 96

def base_glyph_records
  @base_glyph_records
end

#base_glyph_records_offsetInteger (readonly)

Returns Offset to base glyph records array.

Returns:

  • (Integer)

    Offset to base glyph records array



84
85
86
# File 'lib/fontisan/tables/colr.rb', line 84

def base_glyph_records_offset
  @base_glyph_records_offset
end

#layer_recordsArray<LayerRecord> (readonly)

Returns Parsed layer records.

Returns:



99
100
101
# File 'lib/fontisan/tables/colr.rb', line 99

def layer_records
  @layer_records
end

#layer_records_offsetInteger (readonly)

Returns Offset to layer records array.

Returns:

  • (Integer)

    Offset to layer records array



87
88
89
# File 'lib/fontisan/tables/colr.rb', line 87

def layer_records_offset
  @layer_records_offset
end

#num_base_glyph_recordsInteger (readonly)

Returns Number of base glyph records.

Returns:

  • (Integer)

    Number of base glyph records



81
82
83
# File 'lib/fontisan/tables/colr.rb', line 81

def num_base_glyph_records
  @num_base_glyph_records
end

#num_layer_recordsInteger (readonly)

Returns Number of layer records.

Returns:

  • (Integer)

    Number of layer records



90
91
92
# File 'lib/fontisan/tables/colr.rb', line 90

def num_layer_records
  @num_layer_records
end

#raw_dataString (readonly)

Returns Raw binary data for the entire COLR table.

Returns:

  • (String)

    Raw binary data for the entire COLR table



93
94
95
# File 'lib/fontisan/tables/colr.rb', line 93

def raw_data
  @raw_data
end

#versionInteger (readonly)

Returns COLR version (0 for version 0).

Returns:

  • (Integer)

    COLR version (0 for version 0)



78
79
80
# File 'lib/fontisan/tables/colr.rb', line 78

def version
  @version
end

Class Method Details

.read(io) ⇒ Colr

Override read to parse COLR structure

Parameters:

  • io (IO, String)

    Binary data to read

Returns:

  • (Colr)

    Parsed COLR table



105
106
107
108
109
110
111
112
# File 'lib/fontisan/tables/colr.rb', line 105

def self.read(io)
  colr = new
  return colr if io.nil?

  data = io.is_a?(String) ? io : io.read
  colr.parse!(data)
  colr
end

Instance Method Details

#color_glyph_idsArray<Integer>

Get all glyph IDs that have color data

Returns:

  • (Array<Integer>)

    Array of glyph IDs with color layers



168
169
170
# File 'lib/fontisan/tables/colr.rb', line 168

def color_glyph_ids
  base_glyph_records.map(&:glyph_id)
end

#has_color_glyph?(glyph_id) ⇒ Boolean

Check if COLR table has color data for a specific glyph

Parameters:

  • glyph_id (Integer)

    Glyph ID to check

Returns:

  • (Boolean)

    True if glyph has color layers



161
162
163
# File 'lib/fontisan/tables/colr.rb', line 161

def has_color_glyph?(glyph_id)
  !layers_for_glyph(glyph_id).empty?
end

#layers_for_glyph(glyph_id) ⇒ Array<LayerRecord>

Get color layers for a specific glyph ID

Returns an array of LayerRecord objects for the specified glyph. Returns empty array if glyph has no color layers.

Parameters:

  • glyph_id (Integer)

    Glyph ID to look up

Returns:

  • (Array<LayerRecord>)

    Array of layer records for this glyph



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

def layers_for_glyph(glyph_id)
  # Find base glyph record for this glyph ID
  base_record = find_base_glyph_record(glyph_id)
  return [] unless base_record

  # Extract layers for this glyph
  first_index = base_record.first_layer_index
  num_layers = base_record.num_layers

  return [] if num_layers.zero?

  # Return slice of layer records
  layer_records[first_index, num_layers] || []
end

#num_color_glyphsInteger

Get the number of color glyphs in this table

Returns:

  • (Integer)

    Number of base glyphs



175
176
177
# File 'lib/fontisan/tables/colr.rb', line 175

def num_color_glyphs
  num_base_glyph_records
end

#parse!(data) ⇒ Object

Parse the COLR table structure

Parameters:

  • data (String)

    Binary data for the COLR table

Raises:



118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/fontisan/tables/colr.rb', line 118

def parse!(data)
  @raw_data = data
  io = StringIO.new(data)

  # Parse COLR header (14 bytes)
  parse_header(io)
  validate_header!

  # Parse base glyph records
  parse_base_glyph_records(io)

  # Parse layer records
  parse_layer_records(io)
rescue StandardError => e
  raise CorruptedTableError, "Failed to parse COLR table: #{e.message}"
end

#valid?Boolean

Validate the COLR table structure

Returns:

  • (Boolean)

    True if valid



182
183
184
185
186
187
188
189
190
191
# File 'lib/fontisan/tables/colr.rb', line 182

def valid?
  return false if version.nil?
  return false if version != 0 # Only version 0 supported currently
  return false if num_base_glyph_records.nil? || num_base_glyph_records.negative?
  return false if num_layer_records.nil? || num_layer_records.negative?
  return false unless base_glyph_records
  return false unless layer_records

  true
end