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 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:



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

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



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

def base_glyph_records_offset
  @base_glyph_records_offset
end

#layer_recordsArray<LayerRecord> (readonly)

Returns Parsed layer records.

Returns:



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

def layer_records
  @layer_records
end

#layer_records_offsetInteger (readonly)

Returns Offset to layer records array.

Returns:

  • (Integer)

    Offset to layer records array



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

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



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

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



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

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



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

def raw_data
  @raw_data
end

#versionInteger (readonly)

Returns COLR version (0 for version 0).

Returns:

  • (Integer)

    COLR version (0 for version 0)



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

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



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

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



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

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



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

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



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

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



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

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:



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

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



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

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