// 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.
///|
/// Kern pair record (format 0).
pub struct KernPair {
left : UInt
right : UInt
value : Int
} derive(Eq, Show, ToJson)
///|
/// Parsed kern subtable (format 0/2).
pub struct KernSubtable {
format : Int
horizontal : Bool
minimum : Bool
cross_stream : Bool
override_flag : Bool
pairs : Array[KernPair]
format2 : KernSubtableFormat2?
format3 : KernSubtableFormat3?
} derive(Show, ToJson)
///|
/// Kern class table (format 2).
pub struct KernClassTable {
first_glyph : Int
classes : Array[Int]
class_count : Int
} derive(Show, ToJson)
///|
/// Kern subtable format 2 (class-based).
pub struct KernSubtableFormat2 {
row_width : Int
left : KernClassTable
right : KernClassTable
right_class_count : Int
values : Array[Int]
} derive(Show, ToJson)
///|
/// Kern subtable format 3 (class-based, byte indices).
pub struct KernSubtableFormat3 {
glyph_count : Int
kern_value_count : Int
left_class_count : Int
right_class_count : Int
values : Array[Int]
left_class : Array[Int]
right_class : Array[Int]
kern_index : Array[Int]
} derive(Show, ToJson)
///|
/// Parsed kern table (subset).
pub struct KernTable {
subtables : Array[KernSubtable]
} derive(Show, ToJson)
///|
fn parse_kern_subtable_format0(
data : BytesView,
offset : Int,
length : Int,
format : Int,
horizontal : Bool,
minimum : Bool,
cross_stream : Bool,
override_flag : Bool,
) -> Result[KernSubtable, SfntError] {
let count = read_u16_int(data, offset + 6)
match count {
Err(err) => Err(err)
Ok(pair_count) => {
if pair_count < 0 {
return Err(InvalidFormat)
}
let pair_base = offset + 14
let required = pair_base + pair_count * 6
if required > offset + length {
return Err(InvalidFormat)
}
let pairs : Array[KernPair] = []
for i in 0.. return Err(err)
(_, Err(err), _) => return Err(err)
(_, _, Err(err)) => return Err(err)
(Ok(left), Ok(right), Ok(value)) =>
pairs.push(KernPair::{ left, right, value })
}
}
Ok(KernSubtable::{
format,
horizontal,
minimum,
cross_stream,
override_flag,
pairs,
format2: None,
format3: None,
})
}
}
}
///|
fn read_u8_int(data : BytesView, offset : Int) -> Result[Int, SfntError] {
if offset < 0 || offset + 1 > data.length() {
return Err(UnexpectedEof)
}
Ok(u16_to_int(data[offset].to_uint()))
}
///|
fn parse_kern_class_table(
data : BytesView,
offset : Int,
limit : Int,
) -> Result[KernClassTable, SfntError] {
if offset < 0 || offset + 4 > limit {
return Err(UnexpectedEof)
}
let first_glyph = read_u16_int(data, offset)
let count = read_u16_int(data, offset + 2)
match (first_glyph, count) {
(Err(err), _) => Err(err)
(_, Err(err)) => Err(err)
(Ok(first_glyph), Ok(count)) => {
if first_glyph < 0 || count < 0 {
return Err(InvalidFormat)
}
let classes : Array[Int] = []
let base = offset + 4
if base + count * 2 > limit {
return Err(UnexpectedEof)
}
let mut max_class = 0
for i in 0.. return Err(err)
Ok(class_value) => {
classes.push(class_value)
if class_value > max_class {
max_class = class_value
}
}
}
}
Ok(KernClassTable::{ first_glyph, classes, class_count: max_class + 1 })
}
}
}
///|
fn parse_kern_subtable_format2(
data : BytesView,
offset : Int,
length : Int,
format : Int,
horizontal : Bool,
minimum : Bool,
cross_stream : Bool,
override_flag : Bool,
) -> Result[KernSubtable, SfntError] {
let row_width = read_u16_int(data, offset + 6)
let left_offset = read_u16_int(data, offset + 8)
let right_offset = read_u16_int(data, offset + 10)
let array_offset = read_u16_int(data, offset + 12)
match (row_width, left_offset, right_offset, array_offset) {
(Err(err), _, _, _) => Err(err)
(_, Err(err), _, _) => Err(err)
(_, _, Err(err), _) => Err(err)
(_, _, _, Err(err)) => Err(err)
(Ok(row_width), Ok(left_offset), Ok(right_offset), Ok(array_offset)) => {
if row_width <= 0 || (row_width & 1) != 0 {
return Err(InvalidFormat)
}
if left_offset < 0 || right_offset < 0 || array_offset < 0 {
return Err(InvalidFormat)
}
let limit = offset + length
if offset + 14 > limit {
return Err(UnexpectedEof)
}
let left = match parse_kern_class_table(data, offset + left_offset, limit) {
Err(err) => return Err(err)
Ok(value) => value
}
let right = match parse_kern_class_table(data, offset + right_offset, limit) {
Err(err) => return Err(err)
Ok(value) => value
}
let right_class_count = row_width / 2
if right_class_count <= 0 {
return Err(InvalidFormat)
}
let array_base = offset + array_offset
let required = left.class_count * row_width
if array_base < 0 || array_base + required > limit {
return Err(UnexpectedEof)
}
let total_values = left.class_count * right_class_count
let values : Array[Int] = []
let mut cursor = array_base
for _ in 0.. return Err(err)
Ok(value) => values.push(value)
}
cursor = cursor + 2
}
Ok(KernSubtable::{
format,
horizontal,
minimum,
cross_stream,
override_flag,
pairs: [],
format2: Some(KernSubtableFormat2::{
row_width,
left,
right,
right_class_count,
values,
}),
format3: None,
})
}
}
}
///|
fn parse_kern_subtable_format3(
data : BytesView,
offset : Int,
length : Int,
format : Int,
horizontal : Bool,
minimum : Bool,
cross_stream : Bool,
override_flag : Bool,
) -> Result[KernSubtable, SfntError] {
let glyph_count = read_u16_int(data, offset + 6)
let kern_value_count = read_u8_int(data, offset + 8)
let left_class_count = read_u8_int(data, offset + 9)
let right_class_count = read_u8_int(data, offset + 10)
let flags = read_u8_int(data, offset + 11)
match (
glyph_count,
kern_value_count,
left_class_count,
right_class_count,
flags,
) {
(Err(err), _, _, _, _) => Err(err)
(_, Err(err), _, _, _) => Err(err)
(_, _, Err(err), _, _) => Err(err)
(_, _, _, Err(err), _) => Err(err)
(_, _, _, _, Err(err)) => Err(err)
(
Ok(glyph_count),
Ok(kern_value_count),
Ok(left_class_count),
Ok(right_class_count),
Ok(_),
) => {
if glyph_count < 0 || kern_value_count < 0 || left_class_count < 0 || right_class_count < 0 {
return Err(InvalidFormat)
}
if left_class_count == 0 || right_class_count == 0 {
return Err(InvalidFormat)
}
let limit = offset + length
let values_base = offset + 12
let values_bytes = kern_value_count * 2
if values_base < 0 || values_base + values_bytes > limit {
return Err(UnexpectedEof)
}
let values : Array[Int] = []
let mut cursor = values_base
for _ in 0.. return Err(err)
Ok(value) => values.push(value)
}
cursor = cursor + 2
}
let left_base = values_base + values_bytes
let left_len = glyph_count
if left_base < 0 || left_base + left_len > limit {
return Err(UnexpectedEof)
}
let left_class : Array[Int] = []
for i in 0.. return Err(err)
Ok(value) => left_class.push(value)
}
}
let right_base = left_base + left_len
let right_len = glyph_count
if right_base < 0 || right_base + right_len > limit {
return Err(UnexpectedEof)
}
let right_class : Array[Int] = []
for i in 0.. return Err(err)
Ok(value) => right_class.push(value)
}
}
let index_base = right_base + right_len
let index_len = left_class_count * right_class_count
if index_base < 0 || index_base + index_len > limit {
return Err(UnexpectedEof)
}
let kern_index : Array[Int] = []
for i in 0.. return Err(err)
Ok(value) => kern_index.push(value)
}
}
Ok(KernSubtable::{
format,
horizontal,
minimum,
cross_stream,
override_flag,
pairs: [],
format2: None,
format3: Some(KernSubtableFormat3::{
glyph_count,
kern_value_count,
left_class_count,
right_class_count,
values,
left_class,
right_class,
kern_index,
}),
})
}
}
}
///|
/// Parse kern table bytes (version 0, format 0/2/3 subtables).
pub fn KernTable::parse(data : BytesView) -> Result[KernTable, SfntError] {
let version = read_u16_int(data, 0)
let num_tables = read_u16_int(data, 2)
match (version, num_tables) {
(Err(err), _) => Err(err)
(_, Err(err)) => Err(err)
(Ok(version), Ok(count)) => {
if version != 0 || count < 0 {
return Err(InvalidFormat)
}
let subtables : Array[KernSubtable] = []
let mut offset = 4
for _ in 0.. data.length() {
return Err(UnexpectedEof)
}
let sub_version = read_u16_int(data, offset)
let length = read_u16_int(data, offset + 2)
let coverage = read_u16_int(data, offset + 4)
match (sub_version, length, coverage) {
(Err(err), _, _) => return Err(err)
(_, Err(err), _) => return Err(err)
(_, _, Err(err)) => return Err(err)
(Ok(sub_version), Ok(length), Ok(coverage)) => {
if sub_version != 0 || length < 6 {
return Err(InvalidFormat)
}
if offset + length > data.length() {
return Err(UnexpectedEof)
}
let format = (coverage >> 8) & 0xff
let flags = coverage & 0xff
let horizontal = (flags & 0x01) != 0
let minimum = (flags & 0x02) != 0
let cross_stream = (flags & 0x04) != 0
let override_flag = (flags & 0x08) != 0
if format == 0 {
let subtable = match parse_kern_subtable_format0(
data,
offset,
length,
format,
horizontal,
minimum,
cross_stream,
override_flag,
) {
Err(err) => return Err(err)
Ok(value) => value
}
subtables.push(subtable)
} else if format == 2 {
let subtable = match parse_kern_subtable_format2(
data,
offset,
length,
format,
horizontal,
minimum,
cross_stream,
override_flag,
) {
Err(err) => return Err(err)
Ok(value) => value
}
subtables.push(subtable)
} else if format == 3 {
let subtable = match parse_kern_subtable_format3(
data,
offset,
length,
format,
horizontal,
minimum,
cross_stream,
override_flag,
) {
Err(err) => return Err(err)
Ok(value) => value
}
subtables.push(subtable)
}
offset = offset + length
}
}
}
Ok(KernTable::{ subtables })
}
}
}
///|
/// Lookup kern value for a glyph pair (horizontal format 0/2/3 only).
pub fn KernTable::kern_value(
self : KernTable,
left : UInt,
right : UInt,
) -> Int? {
let mut found = false
let mut value = 0
for subtable in self.subtables {
if !subtable.horizontal || subtable.cross_stream {
continue
}
if subtable.format == 0 {
let mut pair_value : Int? = None
for pair in subtable.pairs {
if pair.left == left && pair.right == right {
pair_value = Some(pair.value)
break
}
}
match pair_value {
None => ()
Some(kern) => {
if !found || subtable.override_flag {
value = kern
} else {
value = value + kern
}
found = true
}
}
} else if subtable.format == 2 {
let format2 = match subtable.format2 {
None => continue
Some(value) => value
}
let left_class = kern_class_for(format2.left, left)
let right_class = kern_class_for(format2.right, right)
let index = left_class * format2.right_class_count + right_class
if index < 0 || index >= format2.values.length() {
continue
}
let kern = format2.values[index]
if !found || subtable.override_flag {
value = kern
} else {
value = value + kern
}
found = true
} else if subtable.format == 3 {
let format3 = match subtable.format3 {
None => continue
Some(value) => value
}
let left_class = kern_class_for_u8(format3.left_class, format3.glyph_count, left)
let right_class = kern_class_for_u8(format3.right_class, format3.glyph_count, right)
if left_class < 0 || right_class < 0 ||
left_class >= format3.left_class_count ||
right_class >= format3.right_class_count {
continue
}
let index = left_class * format3.right_class_count + right_class
if index < 0 || index >= format3.kern_index.length() {
continue
}
let value_index = format3.kern_index[index]
if value_index < 0 || value_index >= format3.values.length() {
continue
}
let kern = format3.values[value_index]
if !found || subtable.override_flag {
value = kern
} else {
value = value + kern
}
found = true
}
}
if found { Some(value) } else { None }
}
///|
fn kern_class_for(table : KernClassTable, glyph : UInt) -> Int {
if glyph > 0xffffU {
return 0
}
let glyph_int = u16_to_int(glyph)
let start = table.first_glyph
let count = table.classes.length()
if glyph_int < start || glyph_int >= start + count {
return 0
}
table.classes[glyph_int - start]
}
///|
fn kern_class_for_u8(classes : Array[Int], glyph_count : Int, glyph : UInt) -> Int {
if glyph > 0xffffU {
return 0
}
let glyph_int = u16_to_int(glyph)
if glyph_int < 0 || glyph_int >= glyph_count || glyph_int >= classes.length() {
return 0
}
classes[glyph_int]
}