// Copyright 2026 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
///|
/// COLR table (v0/v1) parsed data.
pub struct ColrTable {
version : Int
base_glyphs : Array[ColrBaseGlyph]
layers : Array[ColrLayer]
base_glyph_paints : Array[BaseGlyphPaintRecord]
layer_paint_offsets : Array[Int]
clip_list : ClipList?
var_idx_map : @ot_var.DeltaSetIndexMap?
var_store : @ot_var.ItemVariationStore?
data : BytesView
} derive(Show, ToJson)
///|
/// Parse COLR table data.
pub fn ColrTable::parse(data : BytesView) -> Result[ColrTable, ColorError] {
let version = read_u16_int(data, 0)
let num_base_glyphs = read_u16_int(data, 2)
let base_glyphs_offset = read_u32_int(data, 4)
let layers_offset = read_u32_int(data, 8)
let num_layers = read_u16_int(data, 12)
match (version, num_base_glyphs, base_glyphs_offset, layers_offset, num_layers) {
(Err(err), _, _, _, _) => Err(err)
(_, Err(err), _, _, _) => Err(err)
(_, _, Err(err), _, _) => Err(err)
(_, _, _, Err(err), _) => Err(err)
(_, _, _, _, Err(err)) => Err(err)
(Ok(version), Ok(num_base_glyphs), Ok(base_glyphs_offset), Ok(layers_offset), Ok(num_layers)) => {
if version < 0 || num_base_glyphs < 0 || num_layers < 0 {
return Err(InvalidFormat)
}
if base_glyphs_offset < 0 || layers_offset < 0 {
return Err(InvalidFormat)
}
let base_glyphs : Array[ColrBaseGlyph] = []
if num_base_glyphs > 0 {
let record_size = 6
if base_glyphs_offset + num_base_glyphs * record_size > data.length() {
return Err(UnexpectedEof)
}
for i in 0.. return Err(err)
(_, Err(err), _) => return Err(err)
(_, _, Err(err)) => return Err(err)
(Ok(glyph_id), Ok(first_layer_index), Ok(num_layers)) =>
base_glyphs.push(ColrBaseGlyph::{
glyph_id,
first_layer_index,
num_layers,
})
}
}
}
let layers : Array[ColrLayer] = []
if num_layers > 0 {
let record_size = 4
if layers_offset + num_layers * record_size > data.length() {
return Err(UnexpectedEof)
}
for i in 0.. return Err(err)
(_, Err(err)) => return Err(err)
(Ok(glyph_id), Ok(color_index)) =>
layers.push(ColrLayer::{ glyph_id, color_index })
}
}
}
let base_glyph_paints : Array[BaseGlyphPaintRecord] = []
let layer_paint_offsets : Array[Int] = []
let mut clip_list : ClipList? = None
let mut var_idx_map : @ot_var.DeltaSetIndexMap? = None
let mut var_store : @ot_var.ItemVariationStore? = None
if version >= 1 {
if data.length() < 34 {
return Err(UnexpectedEof)
}
let base_glyph_list_offset = read_u32_int(data, 14)
let layer_list_offset = read_u32_int(data, 18)
let clip_list_offset = read_u32_int(data, 22)
let var_idx_map_offset = read_u32_int(data, 26)
let var_store_offset = read_u32_int(data, 30)
match (
base_glyph_list_offset,
layer_list_offset,
clip_list_offset,
var_idx_map_offset,
var_store_offset,
) {
(Err(err), _, _, _, _) => return Err(err)
(_, Err(err), _, _, _) => return Err(err)
(_, _, Err(err), _, _) => return Err(err)
(_, _, _, Err(err), _) => return Err(err)
(_, _, _, _, Err(err)) => return Err(err)
(
Ok(base_glyph_list_offset),
Ok(layer_list_offset),
Ok(clip_list_offset),
Ok(var_idx_map_offset),
Ok(var_store_offset),
) => {
if base_glyph_list_offset != 0 {
if base_glyph_list_offset < 0 || base_glyph_list_offset + 4 > data.length() {
return Err(UnexpectedEof)
}
let count = read_u32_int(data, base_glyph_list_offset)
match count {
Err(err) => return Err(err)
Ok(count) => {
if count < 0 {
return Err(InvalidFormat)
}
let record_size = 6
let records_offset = base_glyph_list_offset + 4
if records_offset + count * record_size > data.length() {
return Err(UnexpectedEof)
}
for i in 0.. return Err(err)
(_, Err(err)) => return Err(err)
(Ok(glyph_id), Ok(paint_offset)) => {
if paint_offset < 0 {
return Err(InvalidFormat)
}
let abs_offset = base_glyph_list_offset + paint_offset
if abs_offset < 0 || abs_offset >= data.length() {
return Err(UnexpectedEof)
}
base_glyph_paints.push(BaseGlyphPaintRecord::{
glyph_id,
paint_offset: abs_offset,
})
}
}
}
}
}
}
if layer_list_offset != 0 {
if layer_list_offset < 0 || layer_list_offset + 4 > data.length() {
return Err(UnexpectedEof)
}
let count = read_u32_int(data, layer_list_offset)
match count {
Err(err) => return Err(err)
Ok(count) => {
if count < 0 {
return Err(InvalidFormat)
}
let entries_offset = layer_list_offset + 4
if entries_offset + count * 4 > data.length() {
return Err(UnexpectedEof)
}
for i in 0.. return Err(err)
Ok(paint_offset) => {
if paint_offset < 0 {
return Err(InvalidFormat)
}
let abs_offset = layer_list_offset + paint_offset
if abs_offset < 0 || abs_offset >= data.length() {
return Err(UnexpectedEof)
}
layer_paint_offsets.push(abs_offset)
}
}
}
}
}
}
if clip_list_offset != 0 {
if clip_list_offset < 0 {
return Err(InvalidFormat)
}
let parsed = parse_clip_list(data, clip_list_offset)
match parsed {
Err(err) => return Err(err)
Ok(value) => clip_list = Some(value)
}
}
if var_idx_map_offset != 0 {
if var_idx_map_offset < 0 {
return Err(InvalidFormat)
}
let parsed = @ot_var.DeltaSetIndexMap::parse(data, var_idx_map_offset)
match parsed {
Err(err) => return Err(ColorError::Var(err))
Ok(value) => var_idx_map = Some(value)
}
}
if var_store_offset != 0 {
if var_store_offset < 0 || var_store_offset >= data.length() {
return Err(UnexpectedEof)
}
let slice = data[var_store_offset:]
let parsed = @ot_var.ItemVariationStore::parse(slice)
match parsed {
Err(err) => return Err(ColorError::Var(err))
Ok(value) => var_store = Some(value)
}
}
}
}
}
Ok(ColrTable::{
version,
base_glyphs,
layers,
base_glyph_paints,
layer_paint_offsets,
clip_list,
var_idx_map,
var_store,
data,
})
}
}
}
///|
/// True if COLRv0 data exists.
pub fn ColrTable::has_v0(self : ColrTable) -> Bool {
self.base_glyphs.length() > 0
}
///|
/// True if COLRv1 data exists.
pub fn ColrTable::has_v1(self : ColrTable) -> Bool {
self.version >= 1 && self.base_glyph_paints.length() > 0
}
///|
/// Get COLRv0 layers for a glyph.
pub fn ColrTable::glyph_layers(
self : ColrTable,
glyph_id : Int,
) -> Array[ColrLayer]? {
for record in self.base_glyphs {
if record.glyph_id == glyph_id {
let start = record.first_layer_index
let end = start + record.num_layers
if start < 0 || end > self.layers.length() {
return None
}
let out : Array[ColrLayer] = []
for i in start.. Result[@paint.Paint, ColorError] {
if offset < 0 || offset >= self.data.length() {
return Err(UnexpectedEof)
}
parse_paint_root(self.data, offset)
}
///|
/// Get COLRv1 paint for a glyph, if present.
pub fn ColrTable::paint_for_glyph(
self : ColrTable,
glyph_id : Int,
) -> Result[@paint.Paint?, ColorError] {
for record in self.base_glyph_paints {
if record.glyph_id == glyph_id {
let paint = self.paint_at(record.paint_offset)
match paint {
Err(err) => return Err(err)
Ok(value) => return Ok(Some(value))
}
}
}
Ok(None)
}
///|
/// Get COLRv1 layer paint by index.
pub fn ColrTable::layer_paint(
self : ColrTable,
index : Int,
) -> Result[@paint.Paint?, ColorError] {
if index < 0 || index >= self.layer_paint_offsets.length() {
return Ok(None)
}
let offset = self.layer_paint_offsets[index]
let paint = self.paint_at(offset)
match paint {
Err(err) => Err(err)
Ok(value) => Ok(Some(value))
}
}
///|
/// Get COLRv1 clip box for a glyph, if present.
pub fn ColrTable::clip_box_for_glyph(
self : ColrTable,
glyph_id : Int,
) -> ClipBox? {
match self.clip_list {
None => None
Some(list) => {
for record in list.records {
if glyph_id >= record.start_glyph_id && glyph_id <= record.end_glyph_id {
return Some(record.clip_box)
}
}
None
}
}
}
///|
/// Get COLRv1 clip extents for a glyph, applying variation if available.
pub fn ColrTable::clip_extents_for_glyph(
self : ColrTable,
glyph_id : Int,
coords? : Array[Int] = [],
) -> ClipExtents? {
let clip_box = self.clip_box_for_glyph(glyph_id)
match clip_box {
None => None
Some(box) => {
let mut x_min = box.x_min
let mut y_min = box.y_min
let mut x_max = box.x_max
let mut y_max = box.y_max
if coords.length() > 0 {
let instancer = self.var_instancer(coords)
x_min = x_min + instancer.delta(box.var_idx_base, 0).round().to_int()
y_min = y_min + instancer.delta(box.var_idx_base, 1).round().to_int()
x_max = x_max + instancer.delta(box.var_idx_base, 2).round().to_int()
y_max = y_max + instancer.delta(box.var_idx_base, 3).round().to_int()
}
Some(ClipExtents::{
x_bearing: x_min,
y_bearing: y_max,
width: x_max - x_min,
height: y_min - y_max,
})
}
}
}
fn parse_clip_box(data : BytesView, offset : Int) -> Result[ClipBox, ColorError] {
let format = read_u8_int(data, offset)
match format {
Err(err) => Err(err)
Ok(format) =>
match format {
1 => {
let x_min = read_i16(data, offset + 1)
let y_min = read_i16(data, offset + 3)
let x_max = read_i16(data, offset + 5)
let y_max = read_i16(data, offset + 7)
match (x_min, y_min, x_max, y_max) {
(Err(err), _, _, _) => Err(err)
(_, Err(err), _, _) => Err(err)
(_, _, Err(err), _) => Err(err)
(_, _, _, Err(err)) => Err(err)
(Ok(x_min), Ok(y_min), Ok(x_max), Ok(y_max)) =>
Ok(ClipBox::{
format,
x_min,
y_min,
x_max,
y_max,
var_idx_base: None,
})
}
}
2 => {
let x_min = read_i16(data, offset + 1)
let y_min = read_i16(data, offset + 3)
let x_max = read_i16(data, offset + 5)
let y_max = read_i16(data, offset + 7)
let var_idx = read_u32(data, offset + 9)
match (x_min, y_min, x_max, y_max, var_idx) {
(Err(err), _, _, _, _) => Err(err)
(_, Err(err), _, _, _) => Err(err)
(_, _, Err(err), _, _) => Err(err)
(_, _, _, Err(err), _) => Err(err)
(_, _, _, _, Err(err)) => Err(err)
(Ok(x_min), Ok(y_min), Ok(x_max), Ok(y_max), Ok(var_idx)) =>
Ok(ClipBox::{
format,
x_min,
y_min,
x_max,
y_max,
var_idx_base: Some(var_idx),
})
}
}
_ => Err(InvalidFormat)
}
}
}
fn parse_clip_list(data : BytesView, offset : Int) -> Result[ClipList, ColorError] {
let format = read_u8_int(data, offset)
let count = read_u32_int(data, offset + 1)
match (format, count) {
(Err(err), _) => Err(err)
(_, Err(err)) => Err(err)
(Ok(format), Ok(count)) => {
if count < 0 {
return Err(InvalidFormat)
}
let record_size = 7
let records_offset = offset + 5
if records_offset + count * record_size > data.length() {
return Err(UnexpectedEof)
}
let records : Array[ClipRecord] = []
for i in 0.. return Err(err)
(_, Err(err), _) => return Err(err)
(_, _, Err(err)) => return Err(err)
(Ok(start_gid), Ok(end_gid), Ok(clip_offset)) => {
let clip_abs = match resolve_offset(offset, clip_offset, data) {
Err(err) => return Err(err)
Ok(value) => value
}
let clip_box = parse_clip_box(data, clip_abs)
match clip_box {
Err(err) => return Err(err)
Ok(clip_box) =>
records.push(ClipRecord::{
start_glyph_id: start_gid,
end_glyph_id: end_gid,
clip_box,
})
}
}
}
}
Ok(ClipList::{ format, records })
}
}
}