Commit 671dbb6 for mammothjs

commit 671dbb6ae5738d6bf8f97d13dd08bfc0a23e0b04
Author: Michael Williamson <mike@zwobble.org>
Date:   Sat Sep 26 21:57:10 2026 +0100

    Handle paragraphs and runs that have been moved and tracked as a revision

diff --git a/NEWS b/NEWS
index 3a5ce10..3d9324a 100644
--- a/NEWS
+++ b/NEWS
@@ -15,6 +15,8 @@

 * Read the children of w:customXml elements.

+* Handle paragraphs and runs that have been moved and tracked as a revision.
+
 # 1.12.3

 * Avoid excessive backtracking when parsing an unterminated string with many
diff --git a/lib/docx/body-reader.js b/lib/docx/body-reader.js
index f9ab168..ddc4fce 100644
--- a/lib/docx/body-reader.js
+++ b/lib/docx/body-reader.js
@@ -445,6 +445,11 @@ function BodyReader(options) {

         "w:customXml": readChildElements,
         "w:ins": readChildElements,
+        "w:moveFromRangeEnd": readChildElements,
+        "w:moveFromRangeStart": readChildElements,
+        "w:moveTo": readChildElements,
+        "w:moveToRangeEnd": readChildElements,
+        "w:moveToRangeStart": readChildElements,
         "w:object": readChildElements,
         "w:smartTag": readChildElements,
         "w:drawing": readChildElements,
@@ -724,6 +729,7 @@ var ignoreElements = {
     "w:del": true,
     "w:footnoteRef": true,
     "w:endnoteRef": true,
+    "w:moveFrom": true,
     "w:pPr": true,
     "w:rPr": true,
     "w:tblPr": true,
diff --git a/test/docx/body-reader.tests.js b/test/docx/body-reader.tests.js
index 197f19d..a8b9c7c 100644
--- a/test/docx/body-reader.tests.js
+++ b/test/docx/body-reader.tests.js
@@ -1758,6 +1758,26 @@ test("children of w:ins are converted normally", function() {
     assertChildrenAreConvertedNormally("w:ins");
 });

+test("children of w:moveFromRangeEnd are converted normally", function() {
+    assertChildrenAreConvertedNormally("w:moveFromRangeEnd");
+});
+
+test("children of w:moveFromRangeStart are converted normally", function() {
+    assertChildrenAreConvertedNormally("w:moveFromRangeStart");
+});
+
+test("children of w:moveTo are converted normally", function() {
+    assertChildrenAreConvertedNormally("w:moveTo");
+});
+
+test("children of w:moveToRangeEnd are converted normally", function() {
+    assertChildrenAreConvertedNormally("w:moveToRangeEnd");
+});
+
+test("children of w:moveToRangeStart are converted normally", function() {
+    assertChildrenAreConvertedNormally("w:moveToRangeStart");
+});
+
 test("children of w:object are converted normally", function() {
     assertChildrenAreConvertedNormally("w:object");
 });
@@ -1978,6 +1998,19 @@ test("text nodes are ignored when reading children", function() {
     assert.deepEqual(run, new documents.Run([]));
 });

+test("w:moveFrom elements are ignored", function() {
+    assertElementIsIgnored("w:moveFrom");
+});
+
+function assertElementIsIgnored(tagName) {
+    var runXml = new XmlElement("w:r", {}, [
+        xml.element("w:t", {}, [xml.text("Blackdown")])
+    ]);
+    var result = readXmlElement(new XmlElement(tagName, {}, [runXml]));
+    assert.deepEqual(result.messages, []);
+    assert.deepEqual(result.value, []);
+}
+
 function paragraphWithStyleId(styleId) {
     return new XmlElement("w:p", {}, [
         new XmlElement("w:pPr", {}, [