From 666648349bb262cc8bf25c56c48aa81d405dca31 Mon Sep 17 00:00:00 2001 From: NetterSilver <133535268+Silvertree2010@users.noreply.github.com> Date: Sun, 18 Oct 2026 23:06:07 +0200 Subject: [PATCH] Drop U+FFFE and U+FFFF from XFDF/XML form-data export (#3330) Found while testing crates/xfdf/src/lib.rs:192-209 (esc), used by export_xfdf (:445) and export_data XML (:594-612) Co-authored-by: Claude Haiku 5.5 --- crates/xfdf/src/lib.rs | 2 ++ crates/xfdf/src/tests.rs | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/crates/xfdf/src/lib.rs b/crates/xfdf/src/lib.rs index 44004796e..ef5a30a90 100644 --- a/crates/xfdf/src/lib.rs +++ b/crates/xfdf/src/lib.rs @@ -202,6 +202,8 @@ fn esc(s: &str) -> String { '\n' => o.push_str(" "), '\r' => o.push_str(" "), c if (c as u32) < 0x20 && c != '\n' && c != '\t' => {} + // XML 1.0 noncharacters are not allowed even as raw text. + '\u{FFFE}' | '\u{FFFF}' => {} c => o.push(c), } } diff --git a/crates/xfdf/src/tests.rs b/crates/xfdf/src/tests.rs index b2f4e4406..a4f6abb2d 100644 --- a/crates/xfdf/src/tests.rs +++ b/crates/xfdf/src/tests.rs @@ -168,6 +168,22 @@ fn a_replace_text_pair_survives_an_xfdf_round_trip() { assert_eq!(comment_view(&dst), comment_view(&src), "the pair and its thread survive"); } +#[test] +fn xml_exports_drop_xml_noncharacters() { + let mut src = filled(); + set_value(&mut src, "Name", &FieldValue::Text("a\u{FFFE}b\u{FFFF}c".into())).unwrap(); + let xfdf = export_xfdf(&src, false, true, "f.pdf"); + let doc = roxmltree::Document::parse(&xfdf).unwrap_or_else(|e| panic!("XFDF is not well-formed: {e}: {xfdf:?}")); + let value = doc + .descendants() + .find(|n| n.has_tag_name("field") && n.attribute("name") == Some("Name")) + .and_then(|n| n.children().find(|c| c.has_tag_name("value"))) + .and_then(|n| n.text()); + assert_eq!(value, Some("abc")); + let data = export_data(&src, Format::Xml); + roxmltree::Document::parse(&data).unwrap_or_else(|e| panic!("XML is not well-formed: {e}: {data:?}")); +} + #[test] fn xml_exports_preserve_whitespace_in_field_names() { let names = ["Line\nName", "Line\tName", "Line\rName", "Line\r\nName", "\t Leading\nTrailing \t", "Line Name"];