From 626169fa20b16d579fb5fc4973ac04c5bde67a4a Mon Sep 17 00:00:00 2001 From: dryheatwindbag Date: Sat, 4 Jul 2026 16:07:50 -0700 Subject: [PATCH] fix: preserve unknown empty elements inside runs (w:commentReference) CT_R::from_xml's Event::Empty branch handled tab/br/footnoteReference/ endnoteReference and silently dropped every other empty element inside . This destroys w:commentReference anchors on round-trip (the comments part survives but every comment is orphaned), and loses any other empty run-level markup. Add the same unknown-element fallback the function already has for Event::Start (and CT_P has for both): capture the element into extra_xml via capture_empty_element. Includes a regression test: a run containing round-trips verbatim. --- crates/rdocx-oxml/src/text.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/crates/rdocx-oxml/src/text.rs b/crates/rdocx-oxml/src/text.rs index c2f7b66..4156db8 100644 --- a/crates/rdocx-oxml/src/text.rs +++ b/crates/rdocx-oxml/src/text.rs @@ -174,6 +174,11 @@ impl CT_R { .and_then(|a| std::str::from_utf8(&a.value).ok()?.parse::().ok()) .unwrap_or(0); content.push(RunContent::EndnoteRef { id }); + } else { + // Capture unknown empty child elements (e.g. + // w:commentReference) as raw XML, mirroring the + // Event::Start fallback above. + extra_xml.push(capture_empty_element(e)?); } } Ok(Event::End(ref e)) if matches_local_name(e.name().as_ref(), b"r") => { @@ -652,6 +657,27 @@ mod tests { assert_eq!(parsed.hyperlinks[0].run_end, 2); } + #[test] + fn unknown_empty_run_children_roundtrip() { + // Unknown empty elements inside a run (e.g. w:commentReference) + // must be captured and re-emitted, not silently dropped. + let p = parse_paragraph( + r#"flagged"#, + ); + assert_eq!(p.runs.len(), 2); + assert_eq!(p.runs[1].extra_xml.len(), 1); + + let mut output = Vec::new(); + let mut writer = Writer::new(&mut output); + p.to_xml(&mut writer).unwrap(); + let xml = String::from_utf8(output).unwrap(); + assert!( + xml.contains(r#""#), + "comment reference must survive round-trip: {xml}" + ); + assert!(xml.contains("flagged")); + } + #[test] fn parse_fld_simple_page() { let p = parse_paragraph(