// 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.
///|
/// Parsed cmap table.
pub struct CmapTable {
data : BytesView
subtable_offset : Int
format : Int
variation_offset : Int?
} derive(Show, ToJson)
///|
/// Parse a cmap table from its blob.
pub fn CmapTable::parse(blob : @blob.Blob) -> Result[CmapTable, SfntError] {
let data = blob.as_view()
let num_tables = read_u16_int(data, 2)
match num_tables {
Err(err) => Err(err)
Ok(count) => {
let mut chosen_offset : Int? = None
let mut chosen_format : Int? = None
let mut chosen_rank = 0
let mut variation_offset : Int? = None
let mut offset = 4
for _ in 0.. return Err(err)
Ok(subtable_offset) => {
let format = read_u16_int(data, subtable_offset)
match format {
Err(err) => return Err(err)
Ok(format) => {
if format == 14 {
if variation_offset is None {
variation_offset = Some(subtable_offset)
}
} else {
let rank = cmap_format_rank(format)
if rank > chosen_rank {
chosen_rank = rank
chosen_offset = Some(subtable_offset)
chosen_format = Some(format)
}
}
}
}
}
}
offset = offset + 8
}
match (chosen_offset, chosen_format) {
(Some(subtable_offset), Some(format)) =>
Ok(CmapTable::{ data, subtable_offset, format, variation_offset })
_ => Err(InvalidFormat)
}
}
}
}
fn cmap_format_rank(format : Int) -> Int {
if format == 12 {
5
} else if format == 10 {
4
} else if format == 4 {
3
} else if format == 6 {
2
} else if format == 0 {
1
} else {
0
}
}
///|
/// Map a codepoint to a glyph id.
pub fn CmapTable::glyph_for(
self : CmapTable,
codepoint : UInt,
) -> Result[UInt?, SfntError] {
if self.format == 12 {
glyph_for_format12(self.data, self.subtable_offset, codepoint)
} else if self.format == 10 {
glyph_for_format10(self.data, self.subtable_offset, codepoint)
} else if self.format == 4 {
glyph_for_format4(self.data, self.subtable_offset, codepoint)
} else if self.format == 6 {
glyph_for_format6(self.data, self.subtable_offset, codepoint)
} else if self.format == 0 {
glyph_for_format0(self.data, self.subtable_offset, codepoint)
} else {
Err(InvalidFormat)
}
}
///|
/// Collect nominal glyph mappings from the cmap table.
pub fn CmapTable::collect_nominal_mapping(
self : CmapTable,
) -> Result[@common.CodepointMap, SfntError] {
let map = @common.CodepointMap::new()
if self.format == 12 {
match collect_format12(self.data, self.subtable_offset, map) {
Err(err) => Err(err)
Ok(_) => Ok(map)
}
} else if self.format == 10 {
match collect_format10(self.data, self.subtable_offset, map) {
Err(err) => Err(err)
Ok(_) => Ok(map)
}
} else if self.format == 4 {
match collect_format4(self.data, self.subtable_offset, map) {
Err(err) => Err(err)
Ok(_) => Ok(map)
}
} else if self.format == 6 {
match collect_format6(self.data, self.subtable_offset, map) {
Err(err) => Err(err)
Ok(_) => Ok(map)
}
} else if self.format == 0 {
match collect_format0(self.data, self.subtable_offset, map) {
Err(err) => Err(err)
Ok(_) => Ok(map)
}
} else {
Err(InvalidFormat)
}
}
///|
/// Map a codepoint + variation selector to a glyph id, if supported.
pub fn CmapTable::variation_mapping(
self : CmapTable,
codepoint : UInt,
selector : UInt,
) -> Result[(Bool, UInt?), SfntError] {
match self.variation_offset {
None => Ok((false, None))
Some(offset) => variation_mapping_format14(self.data, offset, codepoint, selector)
}
}
fn variation_mapping_format14(
data : BytesView,
offset : Int,
codepoint : UInt,
selector : UInt,
) -> Result[(Bool, UInt?), SfntError] {
let num_records = read_u32_int(data, offset + 6)
match num_records {
Err(err) => Err(err)
Ok(count) => {
if count < 0 {
return Err(InvalidFormat)
}
let mut base = offset + 10
for _ in 0.. return Err(err)
(_, Err(err), _) => return Err(err)
(_, _, Err(err)) => return Err(err)
(Ok(vs), Ok(default_offset), Ok(non_default_offset)) => {
if vs == selector {
if non_default_offset != 0 {
let mapping = non_default_variation_mapping(
data,
offset + non_default_offset,
codepoint,
)
match mapping {
Err(err) => return Err(err)
Ok(Some(glyph)) => return Ok((true, Some(glyph)))
Ok(None) => ()
}
}
if default_offset != 0 {
let found = default_variation_mapping(
data,
offset + default_offset,
codepoint,
)
match found {
Err(err) => return Err(err)
Ok(true) => return Ok((true, None))
Ok(false) => ()
}
}
return Ok((false, None))
}
}
}
base = base + 11
}
Ok((false, None))
}
}
}
fn non_default_variation_mapping(
data : BytesView,
offset : Int,
codepoint : UInt,
) -> Result[UInt?, SfntError] {
let count = read_u32_int(data, offset)
match count {
Err(err) => Err(err)
Ok(count) => {
if count < 0 {
return Err(InvalidFormat)
}
let mut base = offset + 4
for _ in 0.. return Err(err)
(_, Err(err)) => return Err(err)
(Ok(value), Ok(glyph_id)) => {
if value == codepoint {
return Ok(Some(glyph_id.reinterpret_as_uint()))
}
}
}
base = base + 5
}
Ok(None)
}
}
}
fn default_variation_mapping(
data : BytesView,
offset : Int,
codepoint : UInt,
) -> Result[Bool, SfntError] {
let count = read_u32_int(data, offset)
match count {
Err(err) => Err(err)
Ok(count) => {
if count < 0 {
return Err(InvalidFormat)
}
let mut base = offset + 4
for _ in 0.. return Err(err)
(_, Err(err)) => return Err(err)
(Ok(start), Ok(count_byte)) => {
let add = count_byte.reinterpret_as_uint()
let end = start + add
if codepoint >= start && codepoint <= end {
return Ok(true)
}
}
}
base = base + 4
}
Ok(false)
}
}
}
///|
fn glyph_for_format0(
data : BytesView,
offset : Int,
codepoint : UInt,
) -> Result[UInt?, SfntError] {
if codepoint > 0xffU {
return Ok(None)
}
let base = offset + 6
if base < 0 || base + 256 > data.length() {
return Err(UnexpectedEof)
}
let idx = codepoint.reinterpret_as_int()
match read_u8_int(data, base + idx) {
Err(err) => Err(err)
Ok(glyph_id) => Ok(Some(glyph_id.reinterpret_as_uint()))
}
}
fn collect_format0(
data : BytesView,
offset : Int,
map : @common.CodepointMap,
) -> Result[Unit, SfntError] {
let base = offset + 6
if base < 0 || base + 256 > data.length() {
return Err(UnexpectedEof)
}
for i in 0..<256 {
let gid = match read_u8_int(data, base + i) {
Err(err) => return Err(err)
Ok(value) => value
}
if gid != 0 {
map.set(i.reinterpret_as_uint(), gid.reinterpret_as_uint())
}
}
Ok(())
}
///|
fn glyph_for_format6(
data : BytesView,
offset : Int,
codepoint : UInt,
) -> Result[UInt?, SfntError] {
if codepoint > 0xffffU {
return Ok(None)
}
let first_code = read_u16_int(data, offset + 6)
let entry_count = read_u16_int(data, offset + 8)
match (first_code, entry_count) {
(Err(err), _) => Err(err)
(_, Err(err)) => Err(err)
(Ok(first_code), Ok(entry_count)) => {
if entry_count < 0 {
return Err(InvalidFormat)
}
let cp = u16_to_int(codepoint)
if cp < first_code || cp >= first_code + entry_count {
return Ok(None)
}
let index = cp - first_code
let base = offset + 10
let value = read_u16_int(data, base + index * 2)
match value {
Err(err) => Err(err)
Ok(glyph_id) => Ok(Some(glyph_id.reinterpret_as_uint()))
}
}
}
}
fn collect_format6(
data : BytesView,
offset : Int,
map : @common.CodepointMap,
) -> Result[Unit, SfntError] {
let first_code = read_u16_int(data, offset + 6)
let entry_count = read_u16_int(data, offset + 8)
match (first_code, entry_count) {
(Err(err), _) => Err(err)
(_, Err(err)) => Err(err)
(Ok(first_code), Ok(entry_count)) => {
if entry_count < 0 {
return Err(InvalidFormat)
}
let base = offset + 10
for i in 0.. return Err(err)
Ok(glyph_id) =>
if glyph_id != 0 {
let cp = (first_code + i).reinterpret_as_uint()
map.set(cp, glyph_id.reinterpret_as_uint())
}
}
}
Ok(())
}
}
}
///|
fn glyph_for_format10(
data : BytesView,
offset : Int,
codepoint : UInt,
) -> Result[UInt?, SfntError] {
let start_code = read_u32_int(data, offset + 12)
let entry_count = read_u32_int(data, offset + 16)
match (start_code, entry_count) {
(Err(err), _) => Err(err)
(_, Err(err)) => Err(err)
(Ok(start_code), Ok(entry_count)) => {
if entry_count < 0 {
return Err(InvalidFormat)
}
let start = start_code.reinterpret_as_uint()
let count = entry_count.reinterpret_as_uint()
let end = start + count
if codepoint < start || codepoint >= end {
return Ok(None)
}
let index = (codepoint - start).reinterpret_as_int()
let base = offset + 20
let value = read_u16_int(data, base + index * 2)
match value {
Err(err) => Err(err)
Ok(glyph_id) => Ok(Some(glyph_id.reinterpret_as_uint()))
}
}
}
}
fn collect_format10(
data : BytesView,
offset : Int,
map : @common.CodepointMap,
) -> Result[Unit, SfntError] {
let start_code = read_u32_int(data, offset + 12)
let entry_count = read_u32_int(data, offset + 16)
match (start_code, entry_count) {
(Err(err), _) => Err(err)
(_, Err(err)) => Err(err)
(Ok(start_code), Ok(entry_count)) => {
if entry_count < 0 {
return Err(InvalidFormat)
}
let start = start_code.reinterpret_as_uint()
let base = offset + 20
for i in 0.. return Err(err)
Ok(glyph_id) =>
if glyph_id != 0 {
let cp = start + i.reinterpret_as_uint()
map.set(cp, glyph_id.reinterpret_as_uint())
}
}
}
Ok(())
}
}
}
///|
fn glyph_for_format12(
data : BytesView,
offset : Int,
codepoint : UInt,
) -> Result[UInt?, SfntError] {
let groups = read_u32_int(data, offset + 12)
match groups {
Err(err) => Err(err)
Ok(num_groups) => {
let mut base = offset + 16
for _ in 0.. return Err(err)
(_, Err(err), _) => return Err(err)
(_, _, Err(err)) => return Err(err)
(Ok(start), Ok(end), Ok(start_gid)) =>
if codepoint >= start && codepoint <= end {
return Ok(Some(start_gid + (codepoint - start)))
}
}
base = base + 12
}
Ok(None)
}
}
}
fn collect_format12(
data : BytesView,
offset : Int,
map : @common.CodepointMap,
) -> Result[Unit, SfntError] {
let groups = read_u32_int(data, offset + 12)
match groups {
Err(err) => Err(err)
Ok(num_groups) => {
let mut base = offset + 16
for _ in 0.. return Err(err)
(_, Err(err), _) => return Err(err)
(_, _, Err(err)) => return Err(err)
(Ok(start), Ok(end), Ok(start_gid)) => {
let mut cp = start
while cp <= end {
let gid = start_gid + (cp - start)
if gid != 0U {
map.set(cp, gid)
}
cp = cp + 1U
}
}
}
base = base + 12
}
Ok(())
}
}
}
///|
fn glyph_for_format4(
data : BytesView,
offset : Int,
codepoint : UInt,
) -> Result[UInt?, SfntError] {
if codepoint > 0xffffU {
return Ok(None)
}
let seg_count_x2 = read_u16_int(data, offset + 6)
match seg_count_x2 {
Err(err) => Err(err)
Ok(seg_count_x2) => {
let seg_count = seg_count_x2 / 2
let end_code_offset = offset + 14
let start_code_offset = end_code_offset + seg_count * 2 + 2
let id_delta_offset = start_code_offset + seg_count * 2
let id_range_offset_offset = id_delta_offset + seg_count * 2
for i in 0.. return Err(err)
(_, Err(err)) => return Err(err)
(Ok(end_code), Ok(start_code)) => {
let cp = u16_to_int(codepoint)
if cp >= start_code && cp <= end_code {
let id_delta = read_i16(data, id_delta_offset + i * 2)
let id_range_offset = read_u16_int(
data,
id_range_offset_offset + i * 2,
)
match (id_delta, id_range_offset) {
(Err(err), _) => return Err(err)
(_, Err(err)) => return Err(err)
(Ok(id_delta), Ok(id_range_offset)) => {
if id_range_offset == 0 {
let gid = (cp + id_delta) & 0xffff
return Ok(Some(gid.reinterpret_as_uint()))
}
let glyph_index_offset = id_range_offset_offset +
i * 2 +
id_range_offset +
(cp - start_code) * 2
let glyph_id = read_u16_int(data, glyph_index_offset)
match glyph_id {
Err(err) => return Err(err)
Ok(glyph_id) => {
if glyph_id == 0 {
return Ok(Some(0U))
}
let gid = (glyph_id + id_delta) & 0xffff
return Ok(Some(gid.reinterpret_as_uint()))
}
}
}
}
}
}
}
}
Ok(None)
}
}
}
fn collect_format4(
data : BytesView,
offset : Int,
map : @common.CodepointMap,
) -> Result[Unit, SfntError] {
let seg_count_x2 = read_u16_int(data, offset + 6)
match seg_count_x2 {
Err(err) => Err(err)
Ok(seg_count_x2) => {
let seg_count = seg_count_x2 / 2
let end_code_offset = offset + 14
let start_code_offset = end_code_offset + seg_count * 2 + 2
let id_delta_offset = start_code_offset + seg_count * 2
let id_range_offset_offset = id_delta_offset + seg_count * 2
for i in 0.. return Err(err)
(_, Err(err)) => return Err(err)
(Ok(end_code), Ok(start_code)) => {
let id_delta = read_i16(data, id_delta_offset + i * 2)
let id_range_offset = read_u16_int(
data,
id_range_offset_offset + i * 2,
)
match (id_delta, id_range_offset) {
(Err(err), _) => return Err(err)
(_, Err(err)) => return Err(err)
(Ok(id_delta), Ok(id_range_offset)) => {
let mut cp = start_code
while cp <= end_code {
let gid =
if id_range_offset == 0 {
(cp + id_delta) & 0xffff
} else {
let glyph_index_offset = id_range_offset_offset +
i * 2 +
id_range_offset +
(cp - start_code) * 2
match read_u16_int(data, glyph_index_offset) {
Err(err) => return Err(err)
Ok(glyph_id) => {
if glyph_id == 0 { 0 } else { (glyph_id + id_delta) & 0xffff }
}
}
}
if gid != 0 {
map.set(cp.reinterpret_as_uint(), gid.reinterpret_as_uint())
}
cp = cp + 1
}
}
}
}
}
}
Ok(())
}
}
}