diff options
| author | Michael Williamson <mike@zwobble.org> | 2023-05-20 18:17:53 +0100 |
|---|---|---|
| committer | Michael Williamson <mike@zwobble.org> | 2023-05-20 18:17:53 +0100 |
| commit | 712e328c04ee6e23590ebd753723c2cb7e40f750 (patch) | |
| tree | 749f7d62a04ee7bfc788268532f7fb694cf95e8b | |
| parent | 0894561a06782812a4e73725ede6642da4501db0 (diff) | |
Support merged paragraphs when revisions are tracked
| -rw-r--r-- | NEWS | 4 | ||||
| -rw-r--r-- | lib/documents.js | 2 | ||||
| -rw-r--r-- | lib/docx/body-reader.js | 43 | ||||
| -rw-r--r-- | test/docx/body-reader.tests.js | 59 |
4 files changed, 96 insertions, 12 deletions
@@ -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", {}, [ |
