diff --git a/crates/docx/src/write/story.rs b/crates/docx/src/write/story.rs
index 07f0795db..d41f0e418 100644
--- a/crates/docx/src/write/story.rs
+++ b/crates/docx/src/write/story.rs
@@ -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);
}
diff --git a/crates/docx/tests/roundtrip.rs b/crates/docx/tests/roundtrip.rs
index 6d3a9f872..b8b8e330f 100644
--- a/crates/docx/tests/roundtrip.rs
+++ b/crates/docx/tests/roundtrip.rs
@@ -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", ""), ("word/endnotes.xml", "")] {
+ let xml = part_xml(&bytes, file);
+ assert!(!xml.contains(mark), "{file}: the custom mark became an automatic number: {xml}");
+ assert!(xml.contains(r#"*"#), "{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.