#2028 Fix patcher offset indexes
This commit is contained in:
15
demo/87-template-document.ts
Normal file
15
demo/87-template-document.ts
Normal file
@ -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);
|
||||||
|
});
|
BIN
demo/assets/simple-template-2.docx
Normal file
BIN
demo/assets/simple-template-2.docx
Normal file
Binary file not shown.
@ -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
|
// Try to fill rest of test coverage
|
||||||
// it("should replace token in paragraph", () => {
|
// it("should replace token in paragraph", () => {
|
||||||
// const output = replaceTokenInParagraphElement({
|
// const output = replaceTokenInParagraphElement({
|
||||||
|
@ -30,9 +30,15 @@ export const replaceTokenInParagraphElement = ({
|
|||||||
switch (replaceMode) {
|
switch (replaceMode) {
|
||||||
case ReplaceMode.START:
|
case ReplaceMode.START:
|
||||||
if (startIndex >= 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
|
// 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 not, we just add text to the middle of the run later
|
||||||
|
if (partToReplace === "") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const firstPart = text.replace(partToReplace, replacementText);
|
const firstPart = text.replace(partToReplace, replacementText);
|
||||||
patchTextElement(paragraphElement.elements![run.index].elements![index], firstPart);
|
patchTextElement(paragraphElement.elements![run.index].elements![index], firstPart);
|
||||||
replaceMode = ReplaceMode.MIDDLE;
|
replaceMode = ReplaceMode.MIDDLE;
|
||||||
|
@ -7,7 +7,7 @@ import { Text } from "@file/paragraph/run/run-components/text";
|
|||||||
const formatter = new Formatter();
|
const formatter = new Formatter();
|
||||||
|
|
||||||
export const toJson = (xmlData: string): Element => {
|
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;
|
return xmlObj;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user