Keep custom footnote/endnote marks as text inside the note on DOCX save (#2647)

Found while testing crates/docx/src/write/story.rs:512-530 (InlineObject::NoteRef, the `current_note == Some(..)` branch)

Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
NetterSilver
2026-10-18 03:43:04 -04:00
committed by GitHub
co-authored by Claude Haiku 5.5
parent 4950690479
commit 33f00e8dfe
2 changed files with 32 additions and 1 deletions
+6 -1
View File
@@ -520,7 +520,12 @@ impl Writer<'_> {
self.rev_open(w, props);
w.open("w:r", &[]);
self.rpr(w, &p);
w.empty(mark, &[]);
// A custom mark is plain text; the placeholder would turn it into a number.
if custom.is_empty() {
w.empty(mark, &[]);
} else {
w.leaf("w:t", &[("xml:space", "preserve")], custom);
}
w.close("w:r");
self.rev_close(w, props);
}
+26
View File
@@ -707,6 +707,32 @@ fn note_never_references_itself() {
assert_eq!(part_text(&r, Some(*id)).trim(), "The note.");
}
/// A note with a custom mark keeps it in the note's own text: a plain run with the mark, not the
/// `w:footnoteRef` / `w:endnoteRef` placeholder that Word replaces by the automatic number.
#[test]
fn custom_note_mark_stays_in_the_note() {
let mut d = Document::new();
let f = d.add_part(PartKind::Footnote, Vec::new());
let e = d.add_part(PartKind::Endnote, Vec::new());
for (id, kind) in [(f, NoteKind::Footnote), (e, NoteKind::Endnote)] {
let mut note = Paragraph::with_text(" The note.", CharProps::default());
note.insert_object(0, InlineObject::NoteRef { kind, id, custom: "*".into() }, &CharProps::default()).unwrap();
d.parts.get_mut(&id).unwrap().blocks = vec![para_block(note)];
}
let mut p = Paragraph::with_text("Text", CharProps::default());
p.insert_object(4, InlineObject::NoteRef { kind: NoteKind::Footnote, id: f, custom: "*".into() }, &CharProps::default()).unwrap();
let end = p.len();
p.insert_object(end, InlineObject::NoteRef { kind: NoteKind::Endnote, id: e, custom: "*".into() }, &CharProps::default()).unwrap();
d.body = vec![para_block(p)];
let bytes = wordcraft_docx::write(&d).expect("write");
for (file, mark) in [("word/footnotes.xml", "<w:footnoteRef/>"), ("word/endnotes.xml", "<w:endnoteRef/>")] {
let xml = part_xml(&bytes, file);
assert!(!xml.contains(mark), "{file}: the custom mark became an automatic number: {xml}");
assert!(xml.contains(r#"<w:t xml:space="preserve">*</w:t>"#), "{file}: {xml}");
}
}
/// A note read from a Word file starts with its own number (a reference to itself, from the
/// `w:footnoteRef` mark). Saving writes that mark back exactly once, with its character style, and
/// never as a `w:footnoteReference`; reading the saved file gives the same note again.