From 16bfc78d8d9c9d08c95db8e3289c3b51040f4670 Mon Sep 17 00:00:00 2001 From: Dolan Miu Date: Thu, 23 Mar 2023 21:01:24 +0000 Subject: [PATCH] #2028 Fix patcher offset indexes --- demo/87-template-document.ts | 15 ++ demo/assets/simple-template-2.docx | Bin 0 -> 14561 bytes src/patcher/paragraph-token-replacer.spec.ts | 150 +++++++++++++++++++ src/patcher/paragraph-token-replacer.ts | 8 +- src/patcher/util.ts | 2 +- 5 files changed, 173 insertions(+), 2 deletions(-) create mode 100644 demo/87-template-document.ts create mode 100644 demo/assets/simple-template-2.docx diff --git a/demo/87-template-document.ts b/demo/87-template-document.ts new file mode 100644 index 0000000000..f1f595b2aa --- /dev/null +++ b/demo/87-template-document.ts @@ -0,0 +1,15 @@ +// Patch a document with patches +// Import from 'docx' rather than '../build' if you install from npm +import * as fs from "fs"; +import { patchDocument, PatchType, TextRun } from "../build"; + +patchDocument(fs.readFileSync("demo/assets/simple-template-2.docx"), { + patches: { + name: { + type: PatchType.PARAGRAPH, + children: [new TextRun("Max")], + }, + }, +}).then((doc) => { + fs.writeFileSync("My Document.docx", doc); +}); diff --git a/demo/assets/simple-template-2.docx b/demo/assets/simple-template-2.docx new file mode 100644 index 0000000000000000000000000000000000000000..92c46cd041e3746d6b4764ce32a69b96eca15213 GIT binary patch literal 14561 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 G)BghyQZ@_# literal 0 HcmV?d00001 diff --git a/src/patcher/paragraph-token-replacer.spec.ts b/src/patcher/paragraph-token-replacer.spec.ts index bcfc72e75c..fff9d954eb 100644 --- a/src/patcher/paragraph-token-replacer.spec.ts +++ b/src/patcher/paragraph-token-replacer.spec.ts @@ -71,6 +71,156 @@ describe("paragraph-token-replacer", () => { }); }); + it("should handle case where it cannot find any text to replace", () => { + const output = replaceTokenInParagraphElement({ + paragraphElement: { + name: "w:p", + attributes: { + "w14:paraId": "2499FE9F", + "w14:textId": "27B4FBC2", + "w:rsidR": "00B51233", + "w:rsidRDefault": "007B52ED", + "w:rsidP": "007B52ED", + }, + elements: [ + { + type: "element", + name: "w:pPr", + elements: [{ type: "element", name: "w:pStyle", attributes: { "w:val": "Title" } }], + }, + { + type: "element", + name: "w:r", + elements: [ + { + type: "element", + name: "w:t", + attributes: { "xml:space": "preserve" }, + elements: [{ type: "text", text: "Hello " }], + }, + ], + }, + { + type: "element", + name: "w:r", + attributes: { "w:rsidR": "007F116B" }, + elements: [ + { + type: "element", + name: "w:t", + attributes: { "xml:space": "preserve" }, + elements: [{ type: "text", text: "{{name}} " }], + }, + ], + }, + { + type: "element", + name: "w:r", + elements: [{ type: "element", name: "w:t", elements: [{ type: "text", text: "World" }] }], + }, + ], + }, + renderedParagraph: { + text: "Hello {{name}} World", + runs: [ + { text: "Hello ", parts: [{ text: "Hello ", index: 0, start: 0, end: 5 }], index: 1, start: 0, end: 5 }, + { text: "{{name}} ", parts: [{ text: "{{name}} ", index: 0, start: 6, end: 14 }], index: 2, start: 6, end: 14 }, + { text: "World", parts: [{ text: "World", index: 0, start: 15, end: 19 }], index: 3, start: 15, end: 19 }, + ], + index: 0, + path: [0, 1, 0, 0], + }, + originalText: "{{name}}", + replacementText: "John", + }); + + expect(output).to.deep.equal({ + attributes: { + "w14:paraId": "2499FE9F", + "w14:textId": "27B4FBC2", + "w:rsidP": "007B52ED", + "w:rsidR": "00B51233", + "w:rsidRDefault": "007B52ED", + }, + elements: [ + { + elements: [ + { + attributes: { + "w:val": "Title", + }, + name: "w:pStyle", + type: "element", + }, + ], + name: "w:pPr", + type: "element", + }, + { + elements: [ + { + attributes: { + "xml:space": "preserve", + }, + elements: [ + { + text: "Hello ", + type: "text", + }, + ], + name: "w:t", + type: "element", + }, + ], + name: "w:r", + type: "element", + }, + { + attributes: { + "w:rsidR": "007F116B", + }, + elements: [ + { + attributes: { + "xml:space": "preserve", + }, + elements: [ + { + text: "John ", + type: "text", + }, + ], + name: "w:t", + type: "element", + }, + ], + name: "w:r", + type: "element", + }, + { + elements: [ + { + attributes: { + "xml:space": "preserve", + }, + elements: [ + { + text: "World", + type: "text", + }, + ], + name: "w:t", + type: "element", + }, + ], + name: "w:r", + type: "element", + }, + ], + name: "w:p", + }); + }); + // Try to fill rest of test coverage // it("should replace token in paragraph", () => { // const output = replaceTokenInParagraphElement({ diff --git a/src/patcher/paragraph-token-replacer.ts b/src/patcher/paragraph-token-replacer.ts index 06593c625f..fe3583c0e8 100644 --- a/src/patcher/paragraph-token-replacer.ts +++ b/src/patcher/paragraph-token-replacer.ts @@ -30,9 +30,15 @@ export const replaceTokenInParagraphElement = ({ switch (replaceMode) { case ReplaceMode.START: if (startIndex >= start) { - const partToReplace = run.text.substring(Math.max(startIndex, start), Math.min(endIndex, end) + 1); + const offsetStartIndex = startIndex - start; + const offsetEndIndex = Math.min(endIndex, end) - start; + const partToReplace = run.text.substring(offsetStartIndex, offsetEndIndex + 1); // We use a token to split the text if the replacement is within the same run // If not, we just add text to the middle of the run later + if (partToReplace === "") { + continue; + } + const firstPart = text.replace(partToReplace, replacementText); patchTextElement(paragraphElement.elements![run.index].elements![index], firstPart); replaceMode = ReplaceMode.MIDDLE; diff --git a/src/patcher/util.ts b/src/patcher/util.ts index 6f9ed57dd0..6faef2fb97 100644 --- a/src/patcher/util.ts +++ b/src/patcher/util.ts @@ -7,7 +7,7 @@ import { Text } from "@file/paragraph/run/run-components/text"; const formatter = new Formatter(); export const toJson = (xmlData: string): Element => { - const xmlObj = xml2js(xmlData, { compact: false }) as Element; + const xmlObj = xml2js(xmlData, { compact: false, captureSpacesBetweenElements: true }) as Element; return xmlObj; };