fix(fonts): a CMap's codespace ranges are at most four bytes and listed once

This commit is contained in:
Dylan M. Taylor committed 2026-11-04 02:39:43 +00:00
1 parent 9ce6d89f46
commit 217effb1b2
2 files changed
+15 -1

No files matched your search

+5 -1
View File
@@ -564,6 +564,7 @@ fn number(t: &Tok) -> Option<u32> {
#[allow(clippy::type_complexity)]
fn parse_cmap(data: &[u8]) -> (Vec<(usize, Vec<u8>, Vec<u8>)>, Vec<(u32, u32, u32)>) {
let (mut spaces, mut cids) = (Vec::new(), Vec::new());
let mut seen = HashSet::new();
let mut toks = Lexer::new(data);
while let Some(t) = toks.next() {
match t {
@@ -571,7 +572,10 @@ fn parse_cmap(data: &[u8]) -> (Vec<(usize, Vec<u8>, Vec<u8>)>, Vec<(u32, u32, u3
while let Some([lo, hi]) = entry(&mut toks, b"endcodespacerange") {
if let (Tok::Hex(Some(lo)), Tok::Hex(Some(hi))) = (lo, hi)
&& lo.len() == hi.len()
// A code is at most four bytes; a repeated range can never match first, so it only costs every glyph a comparison.
&& (1..=4).contains(&lo.len())
&& spaces.len() < MAX_CMAP_RANGES
&& seen.insert((lo.clone(), hi.clone()))
{
spaces.push((lo.len(), lo, hi));
}
@@ -654,7 +658,7 @@ impl Metrics {
m.code_len = match &m.codes {
Codes::One => 1,
Codes::Two => 2,
Codes::Ranges(r) => r.last().map_or(2, |x| x.0),
Codes::Ranges(r) => r.last().map_or(2, |x| x.0.clamp(1, 4)),
};
m.unicode = unicode_map(doc, font, m.composite);
if m.std14.is_some() {
+10
View File
@@ -795,3 +795,13 @@ fn low_codes_resolve_to_the_covering_range() {
assert_eq!(covered, [5, 6, 7, 100, 101]);
assert_eq!((u.covering(0xFFFF).is_some(), u.covering(0x1_0001).is_some(), u.covering(0x1_0002).is_none()), (true, true, true));
}
#[test]
fn codespaces_are_deduplicated_and_at_most_four_bytes() {
let cmap = "3 begincodespacerange <00> <7F> <00> <7F> <0000000000> <FFFFFFFFFF> endcodespacerange";
let (spaces, _) = parse_cmap(cmap.as_bytes());
assert_eq!(spaces, vec![(1, vec![0x00], vec![0x7F])]);
let many: String = (0..2000).map(|_| "<0000> <FFFF>\n").collect();
let (spaces, _) = parse_cmap(format!("2000 begincodespacerange\n{many}endcodespacerange").as_bytes());
assert_eq!(spaces.len(), 1);
}