From b617c08ba66c04668f9e3fca31cd904be9d49b15 Mon Sep 17 00:00:00 2001 From: Rahul Date: Tue, 27 Oct 2026 14:21:17 +0530 Subject: [PATCH 1/4] fix(docx): read the comments part and render comments as notes (#191) Word's review layer was dropped whole: the comments relationship was unknown so word/comments.xml was never located, and commentReference fell through the run walker's catch-all so even the anchor position vanished. The comments part now loads through the same table-driven path as footnotes and endnotes (NoteKind::Comment), and a commentReference pushes the note reference that renders the comment as a Markdown footnote. Co-authored-by: rahul05ranjan <11793455+rahul05ranjan@users.noreply.github.com> --- src/model/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/model/mod.rs b/src/model/mod.rs index e5e835a7..b7078efd 100644 --- a/src/model/mod.rs +++ b/src/model/mod.rs @@ -55,4 +55,6 @@ pub enum NoteKind { Footnote, /// Collected at the end of the document or section. Endnote, + /// A reviewer comment anchored in the body by its comment reference. + Comment, } From b332cf48d5862c8bef0477b3d039b26065dc50ff Mon Sep 17 00:00:00 2001 From: Rahul Date: Tue, 27 Oct 2026 14:21:19 +0530 Subject: [PATCH 2/4] fix(docx): read the comments part and render comments as notes (#191) Word's review layer was dropped whole: the comments relationship was unknown so word/comments.xml was never located, and commentReference fell through the run walker's catch-all so even the anchor position vanished. The comments part now loads through the same table-driven path as footnotes and endnotes (NoteKind::Comment), and a commentReference pushes the note reference that renders the comment as a Markdown footnote. Co-authored-by: rahul05ranjan <11793455+rahul05ranjan@users.noreply.github.com> --- src/package/relationships.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/package/relationships.rs b/src/package/relationships.rs index ecb74ff8..aa9c8d4a 100644 --- a/src/package/relationships.rs +++ b/src/package/relationships.rs @@ -20,6 +20,8 @@ pub mod rel_type { "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes"; pub const ENDNOTES: &str = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/endnotes"; + pub const COMMENTS: &str = + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments"; } #[derive(Debug, Clone, Copy, PartialEq, Eq)] From f3506aa1ede913e2b7abf560dcce130b5c724b1f Mon Sep 17 00:00:00 2001 From: Rahul Date: Tue, 27 Oct 2026 14:21:21 +0530 Subject: [PATCH 3/4] fix(docx): read the comments part and render comments as notes (#191) Word's review layer was dropped whole: the comments relationship was unknown so word/comments.xml was never located, and commentReference fell through the run walker's catch-all so even the anchor position vanished. The comments part now loads through the same table-driven path as footnotes and endnotes (NoteKind::Comment), and a commentReference pushes the note reference that renders the comment as a Markdown footnote. Co-authored-by: rahul05ranjan <11793455+rahul05ranjan@users.noreply.github.com> --- src/formats/docx/mod.rs | 46 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/formats/docx/mod.rs b/src/formats/docx/mod.rs index eee5317e..53fbcb46 100644 --- a/src/formats/docx/mod.rs +++ b/src/formats/docx/mod.rs @@ -61,6 +61,7 @@ pub fn parse(bytes: &[u8]) -> Result { let footnotes_part = typed_part_path(&doc_rels, &main_part, rel_type::FOOTNOTES, "footnotes.xml"); let endnotes_part = typed_part_path(&doc_rels, &main_part, rel_type::ENDNOTES, "endnotes.xml"); + let comments_part = typed_part_path(&doc_rels, &main_part, rel_type::COMMENTS, "comments.xml"); let ctx = Ctx { pkg: &pkg, @@ -77,6 +78,7 @@ pub fn parse(bytes: &[u8]) -> Result { for (part, root_name, elem_name, prefix, kind) in [ (footnotes_part, "footnotes", "footnote", "fn", NoteKind::Footnote), (endnotes_part, "endnotes", "endnote", "en", NoteKind::Endnote), + (comments_part, "comments", "comment", "c", NoteKind::Comment), ] { let Some(tree) = pkg.borrow_mut().optional_xml_part(&part)? else { continue; @@ -150,6 +152,50 @@ mod tests { }) } + /// #191: the review layer — commentRangeStart/End anchors plus the comments part — must + /// survive conversion: the comment loads as a note and its reference renders as one. + #[test] + fn comments_load_as_notes_and_render() { + let document = format!( + r#" + Priceless + + artefact + + + "# + ); + let comments = format!( + r#" + + This section needs a citation. + + "# + ); + let rels = r#" + + "#; + let bytes = docx_parts(&[ + ("word/document.xml", &document), + ("word/comments.xml", &comments), + ("word/_rels/document.xml.rels", rels), + ]); + let doc = parse(&bytes).unwrap(); + let Block::Paragraph(inlines) = &doc.blocks[0] else { unreachable!() }; + assert!( + inlines.iter().any(|i| matches!(i, Inline::NoteRef(id) if id == "c1")), + "the comment reference becomes a note reference: {inlines:?}" + ); + assert_eq!(doc.notes.len(), 1); + assert_eq!((doc.notes[0].id.as_str(), doc.notes[0].kind), ("c1", NoteKind::Comment)); + let markdown = crate::to_markdown_bytes(&bytes, crate::Format::Docx).unwrap(); + assert!(markdown.contains("Priceless artefact"), "the anchored text stays: {markdown}"); + assert!(markdown.contains("[^1]: This section needs a citation."), "the comment renders as a note: {markdown}"); + } + #[test] fn linked_image_relationship_becomes_an_external_source() { // M9: `r:link` with an external-mode relationship must carry the URL From ba1130c1cb5ae18657d574f31c0ca6eb7048da67 Mon Sep 17 00:00:00 2001 From: Rahul Date: Tue, 27 Oct 2026 14:21:24 +0530 Subject: [PATCH 4/4] fix(docx): read the comments part and render comments as notes (#191) Word's review layer was dropped whole: the comments relationship was unknown so word/comments.xml was never located, and commentReference fell through the run walker's catch-all so even the anchor position vanished. The comments part now loads through the same table-driven path as footnotes and endnotes (NoteKind::Comment), and a commentReference pushes the note reference that renders the comment as a Markdown footnote. Co-authored-by: rahul05ranjan <11793455+rahul05ranjan@users.noreply.github.com> --- src/formats/docx/content.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/formats/docx/content.rs b/src/formats/docx/content.rs index c5a7c18b..1d47c473 100644 --- a/src/formats/docx/content.rs +++ b/src/formats/docx/content.rs @@ -538,6 +538,14 @@ impl<'a, 'b, 'e> InlineWalker<'a, 'b, 'e> { f.instr.push_str(&child.text()); } } + "commentReference" => { + // The comment's body is loaded from the comments part and + // renders as a note, so the anchor becomes its reference + // (#191: before, the whole comment layer vanished). + if let Some(id) = child.attr(ns::W, "id") { + self.push(Inline::NoteRef(format!("c{id}"))); + } + } _ => {} } }