// Copyright 2025 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.
///|
/// Font references.
///
/// Ported from `swash/src/font.rs` (swash is dual-licensed Apache-2.0 OR MIT).
///|
/// Reference to the content of a font file.
pub struct FontDataRef {
data : Bytes
len : Int
}
///|
pub fn FontDataRef::from_data(data : Bytes) -> FontDataRef? {
if !@internal.is_font(data, 0) && !@internal.is_collection(data) {
None
} else {
Some(FontDataRef::{ data, len: @internal.count(data) })
}
}
///|
pub fn FontDataRef::is_collection(self : FontDataRef) -> Bool {
@internal.is_collection(self.data)
}
///|
pub fn FontDataRef::data(self : FontDataRef) -> Bytes {
self.data
}
///|
pub fn FontDataRef::len(self : FontDataRef) -> Int {
self.len
}
///|
pub fn FontDataRef::is_empty(self : FontDataRef) -> Bool {
self.len == 0
}
///|
pub fn FontDataRef::get(self : FontDataRef, index : Int) -> FontRef? {
match @internal.offset(self.data, index) {
None => None
Some(off) => FontRef::from_offset(self.data, off)
}
}
///|
pub fn FontDataRef::fonts(self : FontDataRef) -> Iter[FontRef] {
let mut pos = 0
Iter::new(fn() {
guard pos < self.len else { None }
let i = pos
pos = pos + 1
self.get(i)
})
}
///|
/// Reference to a font.
pub struct FontRef {
data : Bytes
offset : Int
key : CacheKey
}
///|
/// Source that can provide table data by tag.
pub(open) trait TableProvider {
/// Returns the table for the specified tag.
fn table_by_tag(Self, tag : Tag) -> BytesView?
}
///|
pub impl TableProvider for FontRef with fn table_by_tag(self, tag) {
self.table_data(tag)
}
///|
pub fn FontRef::data(self : FontRef) -> Bytes {
self.data
}
///|
pub fn FontRef::offset(self : FontRef) -> Int {
self.offset
}
///|
pub fn FontRef::key(self : FontRef) -> CacheKey {
self.key
}
///|
pub fn FontRef::from_index(data : Bytes, index : Int) -> FontRef? {
match FontDataRef::from_data(data) {
None => None
Some(d) => d.get(index)
}
}
///|
pub fn FontRef::from_offset(data : Bytes, offset : Int) -> FontRef? {
if !@internal.is_font(data, offset) {
None
} else {
Some(FontRef::{ data, offset, key: CacheKey::CacheKey() })
}
}
///|
pub impl @internal.RawFont for FontRef with fn data(self) {
self.data
}
///|
pub impl @internal.RawFont for FontRef with fn offset(self) {
self.offset
}
///|
pub fn FontRef::table_data(self : FontRef, tag : Tag) -> BytesView? {
@internal.table_data(self, tag)
}
///|
/// Returns the byte offset of the table with the specified tag.
pub fn FontRef::table_offset(self : FontRef, tag : Tag) -> UInt {
@internal.table_offset(self, tag)
}
///|
pub fn FontRef::head(self : FontRef) -> @internal.Head? {
@internal.Head::from_font(self)
}
///|
pub fn FontRef::os2(self : FontRef) -> @internal.Os2? {
@internal.Os2::from_font(self)
}
///|
pub fn FontRef::post(self : FontRef) -> @internal.Post? {
@internal.Post::from_font(self)
}
///|
/// Returns the name for the specified glyph identifier.
///
/// Note: upstream marks this API as unstable and uses it for testing only.
pub fn FontRef::glyph_name(self : FontRef, glyph_id : GlyphId) -> String? {
match self.post() {
None => None
Some(p) => p.name(glyph_id)
}
}
///|
pub fn FontRef::maxp(self : FontRef) -> @internal.Maxp? {
@internal.Maxp::from_font(self)
}
///|
pub fn FontRef::hhea(self : FontRef) -> @internal.Hhea? {
@internal.Hhea::from_font(self)
}
///|
pub fn FontRef::vhea(self : FontRef) -> @internal.Vhea? {
@internal.Vhea::from_font(self)
}
///|
test "FontDataRef/FontRef basic collection support" {
// Collection with one font at offset 0x20.
let data = Bytes::makei(36, i => {
if i == 0 {
b't'
} else if i == 1 {
b't'
} else if i == 2 {
b'c'
} else if i == 3 {
b'f'
} else if i == 8 {
b'\x00'
} else if i == 9 {
b'\x00'
} else if i == 10 {
b'\x00'
} else if i == 11 {
b'\x01'
} else if i == 12 {
b'\x00'
} else if i == 13 {
b'\x00'
} else if i == 14 {
b'\x00'
} else if i == 15 {
b'\x20'
} else if i == 32 {
b'\x00'
} else if i == 33 {
b'\x01'
} else if i == 34 {
b'\x00'
} else if i == 35 {
b'\x00'
} else {
b'\x00'
}
})
let fdr_opt = FontDataRef::from_data(data)
if fdr_opt is Some(fdr) {
inspect(fdr.is_collection(), content="true")
inspect(fdr.len(), content="1")
inspect(fdr.get(0) is Some(_), content="true")
} else {
fail("expected Some(FontDataRef)")
}
}