Merge PR #6661: fix(print): preserve WinAnsi characters in PDF labels

contributors/people.toml: both sides' new rows kept.
crates/engine/src/print_cmds/tests.rs: both sides' appended tests kept whole.
This commit is contained in:
aiongg committed 2026-10-22 02:22:51 +00:00
commit e752bb2ec8
3 files changed
+70 -5

No files matched your search

+3
View File
@@ -191,3 +191,6 @@ sync_github_name = true
[people.ha1des]
sync_github_name = true
[people.aqibmohd271]
sync_github_name = true
+40 -5
View File
@@ -289,16 +289,51 @@ pub struct PrintPage {
pub emulsion_down: bool,
}
// ISO 32000-1, Annex D.2: the standard Helvetica font below uses WinAnsiEncoding,
// not UTF-8. Escape non-ASCII bytes as three octal digits so the content stays ASCII
// and a following digit cannot become part of the escape. Other scripts still need
// an embedded font; retain the existing '?' fallback for unsupported characters.
fn pdf_string(s: &str) -> String {
let mut o = String::from("(");
for c in s.chars() {
match c {
'(' | ')' | '\\' => {
let byte = match c {
'\u{20}'..='\u{7e}' | '\u{a0}'..='\u{ff}' => c as u8,
'€' => 0x80,
'‚' => 0x82,
'ƒ' => 0x83,
'„' => 0x84,
'…' => 0x85,
'†' => 0x86,
'‡' => 0x87,
'ˆ' => 0x88,
'‰' => 0x89,
'Š' => 0x8a,
'‹' => 0x8b,
'Œ' => 0x8c,
'Ž' => 0x8e,
'‘' => 0x91,
'’' => 0x92,
'“' => 0x93,
'”' => 0x94,
'•' => 0x95,
'–' => 0x96,
'—' => 0x97,
'˜' => 0x98,
'™' => 0x99,
'š' => 0x9a,
'›' => 0x9b,
'œ' => 0x9c,
'ž' => 0x9e,
'Ÿ' => 0x9f,
_ => b'?',
};
match byte {
b'(' | b')' | b'\\' => {
o.push('\\');
o.push(c);
o.push(char::from(byte));
}
c if c.is_ascii() && !c.is_ascii_control() => o.push(c),
_ => o.push('?'),
0x80..=0xff => o.push_str(&format!("\\{byte:03o}")),
_ => o.push(char::from(byte)),
}
}
o.push(')');
+27
View File
@@ -940,3 +940,30 @@ fn bad_bleed_is_an_error_not_a_panic() {
assert!(matches!(r, Err(EngineError::BadParams { .. })), "bleed {bleed}: {r:?}");
}
}
#[test]
fn print_strings_keep_winansi_accents_and_punctuation() {
assert_eq!(pdf_string("Café – résumé"), r"(Caf\351 \226 r\351sum\351)");
assert_eq!(pdf_string("€ ‘quote’ “double” • Œœ Šš Žž Ÿ"), r"(\200 \221quote\222 \223double\224 \225 \214\234 \212\232 \216\236 \237)");
assert_eq!(pdf_string("é7"), r"(\3517)", "octal escapes must have three digits before a digit");
}
#[test]
fn print_strings_keep_pdf_delimiters_safe_and_fallback_bounded() {
assert_eq!(pdf_string(r"a(b)\c"), r"(a\(b\)\\c)");
assert_eq!(pdf_string("\n\r\t\0\u{7f}\u{81}中😀"), "(????????)");
assert_eq!(pdf_string(""), "()");
}
#[test]
fn printed_pdf_uses_winansi_for_labels_and_descriptions() {
let mut page = page_of(3, vec![255; 6], false);
page.label = Some("Café – résumé.psd".into());
page.description = Some("Price: €20 (proof)".into());
let pdf = print_pdf(&page);
assert!(String::from_utf8_lossy(&pdf).contains("/Encoding /WinAnsiEncoding"));
let content = pdf_content(&pdf);
assert!(content.contains(r"(Caf\351 \226 r\351sum\351.psd) Tj"), "{content}");
assert!(content.contains(r"(Price: \20020 \(proof\)) Tj"), "{content}");
assert!(content.is_ascii(), "encoded bytes are escaped, never UTF-8 text operands");
}