// 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.
///|
/// Mapping characters to nominal glyph identifiers.
///
/// Ported from `swash/src/charmap.rs` (swash is dual-licensed Apache-2.0 OR MIT).
///|
/// Proxy for rematerializing a character map.
pub struct CharmapProxy {
offset : UInt
format : UInt
symbol : Bool
}
///|
pub fn CharmapProxy::from_font(font : FontRef) -> CharmapProxy {
match @internal.subtable(font) {
None => CharmapProxy::{ offset: 0, format: 0, symbol: false }
Some((offset, format, symbol)) => CharmapProxy::{ offset, format, symbol }
}
}
///|
/// Materializes a character map from the specified font. This proxy must have
/// been created from the same font.
pub fn CharmapProxy::materialize(
self : CharmapProxy,
font : FontRef,
) -> Charmap {
Charmap::{ data: font.data(), proxy: self }
}
///|
/// Maps characters to nominal glyph identifiers.
pub struct Charmap {
data : Bytes
proxy : CharmapProxy
}
///|
pub fn Charmap::from_font(font : FontRef) -> Charmap {
let proxy = CharmapProxy::from_font(font)
Charmap::{ data: font.data(), proxy }
}
///|
pub fn Charmap::proxy(self : Charmap) -> CharmapProxy {
self.proxy
}
///|
pub fn Charmap::map(self : Charmap, codepoint : UInt) -> GlyphId {
let mut glyph_id_u = @internal.map(
self.data,
self.proxy.offset,
self.proxy.format,
codepoint,
).unwrap_or(0)
// Remap U+0000..=U+00FF to U+F000..=U+F0FF for symbol encodings.
if glyph_id_u == 0 && self.proxy.symbol && codepoint <= 0x00FF {
glyph_id_u = @internal.map(
self.data,
self.proxy.offset,
self.proxy.format,
codepoint + 0xF000,
).unwrap_or(0)
}
glyph_id_u.to_uint16()
}
///|
pub fn Charmap::enumerate(self : Charmap, f : (UInt, GlyphId) -> Unit) -> Unit {
@internal.enumerate(self.data, self.proxy.offset, (cp, gid_u) => {
f(cp, gid_u.to_uint16())
})
}
///|
test "Charmap maps U+0041 to glyph id via FontRef" {
// Same synthetic font as internal/cmap_test.
let data = Bytes::makei(76, i => {
if i == 0 {
b'\x00'
} else if i == 1 {
b'\x01'
} else if i == 2 {
b'\x00'
} else if i == 3 {
b'\x00'
} else if i == 4 {
b'\x00'
} else if i == 5 {
b'\x01'
} else if i == 12 {
b'c'
} else if i == 13 {
b'm'
} else if i == 14 {
b'a'
} else if i == 15 {
b'p'
} else if i == 20 {
b'\x00'
} else if i == 21 {
b'\x00'
} else if i == 22 {
b'\x00'
} else if i == 23 {
b'\x20'
} else if i == 24 {
b'\x00'
} else if i == 25 {
b'\x00'
} else if i == 26 {
b'\x00'
} else if i == 27 {
b'\x2C'
} else if i == 32 {
b'\x00'
} else if i == 33 {
b'\x00'
} else if i == 34 {
b'\x00'
} else if i == 35 {
b'\x01'
} else if i == 36 {
b'\x00'
} else if i == 37 {
b'\x03'
} else if i == 38 {
b'\x00'
} else if i == 39 {
b'\x01'
} else if i == 40 {
b'\x00'
} else if i == 41 {
b'\x00'
} else if i == 42 {
b'\x00'
} else if i == 43 {
b'\x0C'
} else if i == 44 {
b'\x00'
} else if i == 45 {
b'\x04'
} else if i == 46 {
b'\x00'
} else if i == 47 {
b'\x20'
} else if i == 48 {
b'\x00'
} else if i == 49 {
b'\x00'
} else if i == 50 {
b'\x00'
} else if i == 51 {
b'\x04'
} else if i == 58 {
b'\x00'
} else if i == 59 {
b'\x41'
} else if i == 60 {
b'\xFF'
} else if i == 61 {
b'\xFF'
} else if i == 62 {
b'\x00'
} else if i == 63 {
b'\x00'
} else if i == 64 {
b'\x00'
} else if i == 65 {
b'\x41'
} else if i == 66 {
b'\xFF'
} else if i == 67 {
b'\xFF'
} else if i == 68 {
b'\xFF'
} else if i == 69 {
b'\xC2'
} else if i == 70 {
b'\x00'
} else if i == 71 {
b'\x01'
} else if i == 72 {
b'\x00'
} else if i == 73 {
b'\x00'
} else if i == 74 {
b'\x00'
} else if i == 75 {
b'\x00'
} else {
b'\x00'
}
})
let font_opt = FontRef::from_offset(data, 0)
if font_opt is Some(font) {
let cm = Charmap::from_font(font)
inspect(cm.map(0x41), content="3")
inspect(cm.map(0x42), content="0")
} else {
fail("expected Some(FontRef)")
}
}