// 子集结果自检:不依赖外部工具,用同一套解析器回读并逐项比对。
//
// 这是项目的「终点」判据:子集字体必须能被重新解析、覆盖请求的字符、
// 度量与原字体一致、复合字形组件编号合法、目录校验和与整字体校验和自洽。
// 调用方还可以通过 `SubsetOptions::with_validator` 追加自己的验收规则。
///|
pub(all) struct VerifyReport {
ok : Bool
chars_checked : Int
glyphs : Int
original_size : Int
subset_size : Int
savings_percent : Double
failures : Array[String]
}
///|
/// 用默认策略子集化并自检。
pub fn verify_subset(
original : Font,
subset_bytes : Bytes,
chars : Array[Int],
) -> VerifyReport {
verify_subset_with(original, subset_bytes, chars, SubsetOptions::default())
}
///|
/// 子集化自检;`options.validators` 中的规则会在内建检查之后执行。
pub fn verify_subset_with(
original : Font,
subset_bytes : Bytes,
chars : Array[Int],
options : SubsetOptions,
) -> VerifyReport {
let failures : Array[String] = []
let subset_opt : Font? = Some(parse_font(subset_bytes)) catch { _ => None }
let mut glyphs = 0
let mut checked = 0
match subset_opt {
None => failures.push("子集字体无法被重新解析")
Some(subset) => {
glyphs = subset.num_glyphs
if subset.units_per_em != original.units_per_em {
failures.push("unitsPerEm 与原字体不一致")
}
let mut i = 0
let seen : Map[Int, Bool] = Map([])
while i < chars.length() {
let code = chars[i]
i = i + 1
if seen.contains(code) {
continue
}
seen.set(code, true)
match original.cmap.get(code) {
Some(glyph) =>
if glyph > 0 {
checked = checked + 1
match subset.cmap.get(code) {
None => failures.push("缺少字符 U+\{hex(code)}")
Some(new_glyph) =>
if new_glyph <= 0 || new_glyph >= subset.num_glyphs {
failures.push("字符 U+\{hex(code)} 的字形编号越界")
} else {
if new_glyph >= subset.advances.length() {
failures.push("字符 U+\{hex(code)} 缺少水平度量")
} else if subset.advances[new_glyph] !=
original.advances[glyph] {
failures.push(
"字符 U+\{hex(code)} 的前进宽度被改变",
)
}
let original_length = original.loca[glyph + 1] -
original.loca[glyph]
let new_length = subset.loca[new_glyph + 1] -
subset.loca[new_glyph]
if original_length > 0 && new_length == 0 {
failures.push(
"字符 U+\{hex(code)} 的字形数据为空",
)
}
}
}
}
None => ()
}
}
// 复合字形闭包:所有组件编号必须落在新字体范围内
let mut glyph = 0
while glyph < subset.num_glyphs {
let components = component_gids(subset, glyph) catch { _ => [] }
for component in components {
if component >= subset.num_glyphs {
failures.push(
"复合字形 \{glyph} 引用了越界组件 \{component}",
)
}
}
glyph = glyph + 1
}
verify_checksums(subset, failures) catch {
_ => failures.push("校验和复核失败")
}
for validator in options.validators {
for failure in validator(original, subset, chars) {
failures.push(failure)
}
}
}
}
let original_size = original.bytes.length()
let subset_size = subset_bytes.length()
let savings = if original_size == 0 {
0.0
} else {
(1.0 - subset_size.to_double() / original_size.to_double()) * 100.0
}
{
ok: failures.length() == 0,
chars_checked: checked,
glyphs,
original_size,
subset_size,
savings_percent: savings,
failures,
}
}
///|
/// 复核表目录校验和与整字体校验和。
///
/// `head.checkSumAdjustment` 在计算 head 表校验和时必须视为 0,
/// 而整字体校验和必须等于 0xB1B0AFBA——这两条是「文件结构自洽」的硬判据。
fn verify_checksums(
subset : Font,
failures : Array[String],
) -> Unit raise FontError {
for entry in subset.tables {
let tag = entry.0
let record = entry.1
let sum = if tag == "head" {
let copy : Array[Byte] = []
push_range(copy, subset.bytes, record.offset, record.length)
if copy.length() >= 12 {
copy[8] = Byte::default()
copy[9] = Byte::default()
copy[10] = Byte::default()
copy[11] = Byte::default()
}
checksum_range(bytes_of(copy), 0, copy.length())
} else {
checksum_range(subset.bytes, record.offset, record.length)
}
if sum != record.checksum {
failures.push("表 \{tag} 的校验和与目录不一致")
}
}
let total = checksum_range(subset.bytes, 0, subset.bytes.length())
let magic : UInt = 0xB1B0AFBA
if total != magic {
failures.push("整字体校验和不等于 0xB1B0AFBA")
}
}
///|
/// 码点的十六进制表示,用于稳定的错误信息。
pub fn hex(code : Int) -> String {
code.to_string(radix=16)
}