// 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.
///|
/// Face holds font data and basic metadata.
pub struct Face {
blob : @blob.Blob
mut index : Int
mut upem : Int
mut glyph_count : Int
mut tables : Map[@common.Tag, @blob.Blob]
} derive(Show, ToJson)
///|
/// Return the number of faces in a blob.
pub fn face_count(blob : @blob.Blob) -> Int {
let data = blob.as_view()
if data.length() < 4 {
return 0
}
let tag = @common.Tag::from_bytes(data[0], data[1], data[2], data[3])
let ttcf = @common.Tag::from_chars('t', 't', 'c', 'f')
if tag == ttcf {
if data.length() < 12 {
return 0
}
let b0 = data[8].to_uint()
let b1 = data[9].to_uint()
let b2 = data[10].to_uint()
let b3 = data[11].to_uint()
let value = ((b0 << 24) | (b1 << 16) | (b2 << 8) | b3).reinterpret_as_int()
if value < 0 { 0 } else { value }
} else {
let sfnt = @common.Tag::from_bytes(0x00, 0x01, 0x00, 0x00)
let otto = @common.Tag::from_chars('O', 'T', 'T', 'O')
let true_tag = @common.Tag::from_chars('t', 'r', 'u', 'e')
let typ1 = @common.Tag::from_chars('t', 'y', 'p', '1')
if tag == sfnt || tag == otto || tag == true_tag || tag == typ1 {
1
} else {
0
}
}
}
///|
/// Create a face from a blob.
pub fn Face::new(blob : @blob.Blob, index? : Int = 0) -> Face {
Face::{ blob, index, upem: 0, glyph_count: 0, tables: Map::new() }
}
///|
/// Create a face from raw bytes.
pub fn Face::from_bytes(bytes : Bytes, index? : Int = 0) -> Face {
Face::new(@blob.Blob::from_bytes(bytes), index~)
}
///|
/// Access the face blob.
pub fn Face::reference_blob(self : Face) -> @blob.Blob {
self.blob
}
///|
/// Update the face index.
pub fn Face::set_index(self : Face, index : Int) -> Unit {
if self.index != index {
self.index = index
self.tables = Map::new()
self.upem = 0
self.glyph_count = 0
}
}
///|
/// Read the face index.
pub fn Face::get_index(self : Face) -> Int {
self.index
}
///|
/// Update units-per-em.
pub fn Face::set_upem(self : Face, upem : Int) -> Unit {
self.upem = upem
}
///|
/// Read units-per-em.
pub fn Face::get_upem(self : Face) -> Int {
self.upem
}
///|
/// Update glyph count.
pub fn Face::set_glyph_count(self : Face, count : Int) -> Unit {
self.glyph_count = count
}
///|
/// Read glyph count.
pub fn Face::get_glyph_count(self : Face) -> Int {
self.glyph_count
}
///|
/// Attach a table blob.
pub fn Face::set_table(
self : Face,
tag : @common.Tag,
blob : @blob.Blob,
) -> Unit {
self.tables[tag] = blob
}