summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Williamson <mike@zwobble.org>2023-05-20 18:17:53 +0100
committerMichael Williamson <mike@zwobble.org>2023-05-20 18:17:53 +0100
commit712e328c04ee6e23590ebd753723c2cb7e40f750 (patch)
tree749f7d62a04ee7bfc788268532f7fb694cf95e8b
parent0894561a06782812a4e73725ede6642da4501db0 (diff)
Support merged paragraphs when revisions are tracked
-rw-r--r--NEWS4
-rw-r--r--lib/documents.js2
-rw-r--r--lib/docx/body-reader.js43
-rw-r--r--test/docx/body-reader.tests.js59
4 files changed, 96 insertions, 12 deletions
diff --git a/NEWS b/NEWS
index 5fb2f53..c4309ef 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,7 @@
+# 1.6.0
+
+* Support merged paragraphs when revisions are tracked.
+
# 1.5.2
* Add transformDocument to the TypeScript declarations.
diff --git a/lib/documents.js b/lib/documents.js
index edc9b7c..d29ad1b 100644
--- a/lib/documents.js
+++ b/lib/documents.js
@@ -203,7 +203,7 @@ function BookmarkStart(options) {
exports.document = exports.Document = Document;
exports.paragraph = exports.Paragraph = Paragraph;
exports.run = exports.Run = Run;
-exports.Text = Text;
+exports.text = exports.Text = Text;
exports.tab = exports.Tab = Tab;
exports.Hyperlink = Hyperlink;
exports.noteReference = exports.NoteReference = NoteReference;
diff --git a/lib/docx/body-reader.js b/lib/docx/body-reader.js
index 61b198b..ca55bb7 100644
--- a/lib/docx/body-reader.js
+++ b/lib/docx/body-reader.js
@@ -23,6 +23,12 @@ function createBodyReader(options) {
function BodyReader(options) {
var complexFieldStack = [];
var currentInstrText = [];
+
+ // When a paragraph is marked as deleted, its contents should be combined
+ // with the following paragraph. See 17.13.5.15 del (Deleted Paragraph) of
+ // ECMA-376 4th edition Part 1.
+ var deletedParagraphContents = [];
+
var relationships = options.relationships;
var contentTypes = options.contentTypes;
var docxFile = options.docxFile;
@@ -213,15 +219,34 @@ function BodyReader(options) {
var xmlElementReaders = {
"w:p": function(element) {
- return readXmlElements(element.children)
- .map(function(children) {
- var properties = _.find(children, isParagraphProperties);
- return new documents.Paragraph(
- children.filter(negate(isParagraphProperties)),
- properties
- );
- })
- .insertExtra();
+ var isDeleted = !!element
+ .firstOrEmpty("w:pPr")
+ .firstOrEmpty("w:rPr")
+ .first("w:del");
+
+ if (isDeleted) {
+ element.children.filter(function(child) {
+ return child.name !== "w:pPr";
+ }).forEach(function(child) {
+ deletedParagraphContents.push(child);
+ });
+ return emptyResult();
+ } else {
+ var childrenXml = element.children;
+ if (deletedParagraphContents.length > 0) {
+ childrenXml = deletedParagraphContents.concat(childrenXml);
+ deletedParagraphContents = [];
+ }
+ return readXmlElements(childrenXml)
+ .map(function(children) {
+ var properties = _.find(children, isParagraphProperties);
+ return new documents.Paragraph(
+ children.filter(negate(isParagraphProperties)),
+ properties
+ );
+ })
+ .insertExtra();
+ }
},
"w:pPr": function(element) {
return readParagraphStyle(element).map(function(style) {
diff --git a/test/docx/body-reader.tests.js b/test/docx/body-reader.tests.js
index f5c8b87..bbc3499 100644
--- a/test/docx/body-reader.tests.js
+++ b/test/docx/body-reader.tests.js
@@ -33,11 +33,19 @@ var testing = require("../testing");
var test = require("../test")(module);
var createFakeDocxFile = testing.createFakeDocxFile;
-function readXmlElement(element, options) {
+function createBodyReaderForTests(options) {
options = Object.create(options || {});
options.styles = options.styles || new Styles({}, {});
options.numbering = options.numbering || defaultNumbering;
- return createBodyReader(options).readXmlElement(element);
+ return createBodyReader(options);
+}
+
+function readXmlElement(element, options) {
+ return createBodyReaderForTests(options).readXmlElement(element);
+}
+
+function readXmlElements(element, options) {
+ return createBodyReaderForTests(options).readXmlElements(element);
}
function readXmlElementValue(element, options) {
@@ -46,6 +54,12 @@ function readXmlElementValue(element, options) {
return result.value;
}
+function readXmlElementsValue(elements, options) {
+ var result = readXmlElements(elements, options);
+ assert.deepEqual(result.messages, []);
+ return result.value;
+}
+
var fakeContentTypes = {
findContentType: function(filePath) {
var extensionTypes = {
@@ -225,6 +239,47 @@ test("numbering properties are ignored if w:numId is missing", function() {
assert.equal(numberingLevel, null);
});
+test("content of deleted paragraph is prepended to next paragraph", function() {
+ var styles = new Styles(
+ {
+ "Heading1": {name: "Heading 1"},
+ "Heading2": {name: "Heading 2"}
+ },
+ {}
+ );
+ var bodyXml = [
+ new XmlElement("w:p", {}, [
+ new XmlElement("w:pPr", {}, [
+ new XmlElement("w:pStyle", {"w:val": "Heading1"}, []),
+ new XmlElement("w:rPr", {}, [
+ new XmlElement("w:del")
+ ])
+ ]),
+ runOfText("One")
+ ]),
+ new XmlElement("w:p", {}, [
+ new XmlElement("w:pPr", {}, [
+ new XmlElement("w:pStyle", {"w:val": "Heading2"}, [])
+ ]),
+ runOfText("Two")
+ ])
+ ];
+
+ var result = readXmlElementsValue(bodyXml, {styles: styles});
+
+ assertThat(result, contains(
+ hasProperties({
+ type: documents.types.paragraph,
+ styleId: "Heading2",
+ children: contains(
+ documents.run([documents.text("One")]),
+ documents.run([documents.text("Two")])
+ )
+ })
+ ));
+
+});
+
test("complex fields", (function() {
var uri = "http://example.com";
var beginXml = new XmlElement("w:r", {}, [