fix(forms): finding the first character a simple font cannot hold encodes the whole string once, then each distinct character once (#7047)

This commit is contained in:
Dylan M. Taylor authored and GitHub committed 2026-11-13 12:23:58 +01:00
1 parent b167422b2e
commit 785e7e510c
1 file changed
+24 -1
+24 -1
View File
@@ -326,7 +326,15 @@ impl FontText {
match self {
Self::WinAnsi => pdfcraft_fonts::first_undrawable(s),
Self::Simple(metrics) => {
s.chars().map(|c| if c == '\t' { ' ' } else { c }).find(|c| metrics.encode(c.encode_utf8(&mut [0; 4])).is_none())
// A whole string that encodes has no such character, in one pass over the font's
// ranges. Otherwise each distinct character is asked once (a call scans every range).
if metrics.encode(&s.replace('\t', " ")).is_some() {
return None;
}
let mut held = std::collections::HashSet::new();
s.chars()
.map(|c| if c == '\t' { ' ' } else { c })
.find(|c| !held.contains(c) && !(metrics.encode(c.encode_utf8(&mut [0; 4])).is_some() && held.insert(*c)))
}
Self::Unicode(cmap) => cmap.first_unencodable(s),
}
@@ -1244,4 +1252,19 @@ mod tests {
// When empty selection:
assert_eq!(ListRows::top_showing(0, &[], 3, 5), 0);
}
#[test]
fn first_unencodable_finds_the_first_character_the_simple_font_cannot_hold() {
let doc = Document::new_empty();
let mut f = Dict::new();
f.set(b"Type".to_vec(), Object::name("Font"));
f.set(b"Subtype".to_vec(), Object::name("Type1"));
f.set(b"BaseFont".to_vec(), Object::name("Helvetica"));
f.set(b"Encoding".to_vec(), Object::name("WinAnsiEncoding"));
let font = FontText::Simple(Box::new(Metrics::from_dict(&doc, &f)));
assert_eq!(font.first_unencodable("h\u{e9}llo\tw\u{e9}rld"), None);
assert_eq!(font.first_unencodable(&"a\u{e9}".repeat(1000)), None);
assert_eq!(font.first_unencodable("ab\u{3a9}c\u{3a8}"), Some('\u{3a9}'));
assert_eq!(font.first_unencodable(""), None);
}
}