Work on replacer logic
This commit is contained in:
@ -9,6 +9,18 @@ patchDocument(fs.readFileSync("demo/assets/simple-template.docx"), {
|
|||||||
children: [new TextRun("John Doe")],
|
children: [new TextRun("John Doe")],
|
||||||
text: "{{ name }}",
|
text: "{{ name }}",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
children: [new TextRun("Heading wow!")],
|
||||||
|
text: "{{ table_heading_1 }}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
children: [new TextRun("#657")],
|
||||||
|
text: "{{ item_1 }}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
children: [new TextRun("Lorem ipsum paragraph")],
|
||||||
|
text: "{{ paragraph_replace }}",
|
||||||
|
},
|
||||||
],
|
],
|
||||||
}).then((doc) => {
|
}).then((doc) => {
|
||||||
fs.writeFileSync("My Document.docx", doc);
|
fs.writeFileSync("My Document.docx", doc);
|
||||||
|
56
src/templater/paragraph-split-inject.ts
Normal file
56
src/templater/paragraph-split-inject.ts
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import { Element } from "xml-js";
|
||||||
|
import { createTextElementContents } from "./util";
|
||||||
|
|
||||||
|
export const findRunElementIndexWithToken = (paragraphElement: Element, token: string): number => {
|
||||||
|
for (let i = 0; i < (paragraphElement.elements ?? []).length; i++) {
|
||||||
|
const element = paragraphElement.elements![i];
|
||||||
|
if (element.type === "element" && element.name === "w:r") {
|
||||||
|
const textElement = (element.elements ?? []).filter((e) => e.type === "element" && e.name === "w:t");
|
||||||
|
|
||||||
|
for (const text of textElement) {
|
||||||
|
if ((text.elements?.[0].text as string)?.includes(token)) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// return -1;
|
||||||
|
throw new Error("Token not found");
|
||||||
|
};
|
||||||
|
|
||||||
|
export const splitRunElement = (runElement: Element, token: string): { readonly left: Element; readonly right: Element } => {
|
||||||
|
let splitIndex = 0;
|
||||||
|
|
||||||
|
const splitElements =
|
||||||
|
runElement.elements
|
||||||
|
?.map((e, i) => {
|
||||||
|
if (e.type === "element" && e.name === "w:t") {
|
||||||
|
const text = e.elements?.[0].text as string;
|
||||||
|
const splitText = text.split(token);
|
||||||
|
const newElements = splitText.map((t) => ({
|
||||||
|
...e,
|
||||||
|
attributes: {
|
||||||
|
"xml:space": "preserve",
|
||||||
|
},
|
||||||
|
elements: createTextElementContents(t),
|
||||||
|
}));
|
||||||
|
splitIndex = i;
|
||||||
|
return newElements;
|
||||||
|
} else {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.flat() ?? [];
|
||||||
|
|
||||||
|
const leftRunElement: Element = {
|
||||||
|
...JSON.parse(JSON.stringify(runElement)),
|
||||||
|
elements: splitElements.slice(0, splitIndex + 1),
|
||||||
|
};
|
||||||
|
|
||||||
|
const rightRunElement: Element = {
|
||||||
|
...JSON.parse(JSON.stringify(runElement)),
|
||||||
|
elements: splitElements.slice(splitIndex + 1),
|
||||||
|
};
|
||||||
|
|
||||||
|
return { left: leftRunElement, right: rightRunElement };
|
||||||
|
};
|
@ -1,10 +1,6 @@
|
|||||||
import { Element } from "xml-js";
|
import { Element } from "xml-js";
|
||||||
import * as xml from "xml";
|
|
||||||
|
|
||||||
import { Formatter } from "@export/formatter";
|
import { createTextElementContents } from "./util";
|
||||||
import { Text } from "@file/paragraph/run/run-components/text";
|
|
||||||
|
|
||||||
import { toJson } from "./util";
|
|
||||||
import { IRenderedParagraphNode } from "./run-renderer";
|
import { IRenderedParagraphNode } from "./run-renderer";
|
||||||
|
|
||||||
enum ReplaceMode {
|
enum ReplaceMode {
|
||||||
@ -13,8 +9,6 @@ enum ReplaceMode {
|
|||||||
END,
|
END,
|
||||||
}
|
}
|
||||||
|
|
||||||
const formatter = new Formatter();
|
|
||||||
|
|
||||||
export const replaceTokenInParagraphElement = ({
|
export const replaceTokenInParagraphElement = ({
|
||||||
paragraphElement,
|
paragraphElement,
|
||||||
renderedParagraph,
|
renderedParagraph,
|
||||||
@ -63,10 +57,8 @@ export const replaceTokenInParagraphElement = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const patchTextElement = (element: Element, text: string): Element => {
|
const patchTextElement = (element: Element, text: string): Element => {
|
||||||
const textJson = toJson(xml(formatter.format(new Text({ text }))));
|
|
||||||
|
|
||||||
// eslint-disable-next-line functional/immutable-data
|
// eslint-disable-next-line functional/immutable-data
|
||||||
element.elements = textJson.elements;
|
element.elements = createTextElementContents(text);
|
||||||
|
|
||||||
return element;
|
return element;
|
||||||
};
|
};
|
||||||
|
@ -8,6 +8,7 @@ import { IPatch } from "./from-docx";
|
|||||||
import { toJson } from "./util";
|
import { toJson } from "./util";
|
||||||
import { IRenderedParagraphNode } from "./run-renderer";
|
import { IRenderedParagraphNode } from "./run-renderer";
|
||||||
import { replaceTokenInParagraphElement } from "./paragraph-token-replacer";
|
import { replaceTokenInParagraphElement } from "./paragraph-token-replacer";
|
||||||
|
import { findRunElementIndexWithToken, splitRunElement } from "./paragraph-split-inject";
|
||||||
|
|
||||||
const formatter = new Formatter();
|
const formatter = new Formatter();
|
||||||
|
|
||||||
@ -18,7 +19,6 @@ export const replacer = (json: Element, options: IPatch, renderedParagraphs: rea
|
|||||||
if (child instanceof Paragraph) {
|
if (child instanceof Paragraph) {
|
||||||
console.log("is para");
|
console.log("is para");
|
||||||
} else if (child instanceof TextRun) {
|
} else if (child instanceof TextRun) {
|
||||||
console.log("paragrapghs", JSON.stringify(renderedParagraphs, null, 2));
|
|
||||||
for (const renderedParagraph of renderedParagraphs) {
|
for (const renderedParagraph of renderedParagraphs) {
|
||||||
const textJson = toJson(xml(formatter.format(child)));
|
const textJson = toJson(xml(formatter.format(child)));
|
||||||
const paragraphElement = goToElementFromPath(json, renderedParagraph.path);
|
const paragraphElement = goToElementFromPath(json, renderedParagraph.path);
|
||||||
@ -33,9 +33,6 @@ export const replacer = (json: Element, options: IPatch, renderedParagraphs: rea
|
|||||||
} else {
|
} else {
|
||||||
// Hard case where the text is only part of the paragraph
|
// Hard case where the text is only part of the paragraph
|
||||||
|
|
||||||
console.log("hard case");
|
|
||||||
console.log("paragraphElement", JSON.stringify(paragraphElement, null, 2));
|
|
||||||
|
|
||||||
replaceTokenInParagraphElement({
|
replaceTokenInParagraphElement({
|
||||||
paragraphElement,
|
paragraphElement,
|
||||||
renderedParagraph,
|
renderedParagraph,
|
||||||
@ -43,6 +40,12 @@ export const replacer = (json: Element, options: IPatch, renderedParagraphs: rea
|
|||||||
replacementText: SPLIT_TOKEN,
|
replacementText: SPLIT_TOKEN,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const index = findRunElementIndexWithToken(paragraphElement, SPLIT_TOKEN);
|
||||||
|
|
||||||
|
const { left, right } = splitRunElement(paragraphElement.elements![index], SPLIT_TOKEN);
|
||||||
|
// eslint-disable-next-line functional/immutable-data
|
||||||
|
paragraphElement.elements!.splice(index, 1, left, ...textJson.elements!, right);
|
||||||
|
console.log(index, JSON.stringify(paragraphElement.elements![index], null, 2));
|
||||||
console.log("paragraphElement after", JSON.stringify(paragraphElement, null, 2));
|
console.log("paragraphElement after", JSON.stringify(paragraphElement, null, 2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,7 +82,7 @@ const renderRunNode = (node: Element, index: number, currentRunStringIndex: numb
|
|||||||
|
|
||||||
const parts = node.elements
|
const parts = node.elements
|
||||||
.map((element, i: number) =>
|
.map((element, i: number) =>
|
||||||
element.name === "w:t" && element.elements
|
element.name === "w:t" && element.elements && element.elements.length > 0
|
||||||
? {
|
? {
|
||||||
text: element.elements[0].text?.toString() ?? "",
|
text: element.elements[0].text?.toString() ?? "",
|
||||||
index: i,
|
index: i,
|
||||||
|
@ -1,6 +1,19 @@
|
|||||||
import { xml2js, Element } from "xml-js";
|
import { xml2js, Element } from "xml-js";
|
||||||
|
import * as xml from "xml";
|
||||||
|
|
||||||
|
import { Formatter } from "@export/formatter";
|
||||||
|
import { Text } from "@file/paragraph/run/run-components/text";
|
||||||
|
|
||||||
|
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 }) as Element;
|
||||||
return xmlObj;
|
return xmlObj;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// eslint-disable-next-line functional/prefer-readonly-type
|
||||||
|
export const createTextElementContents = (text: string): Element[] => {
|
||||||
|
const textJson = toJson(xml(formatter.format(new Text({ text }))));
|
||||||
|
|
||||||
|
return textJson.elements![0].elements ?? [];
|
||||||
|
};
|
||||||
|
Reference in New Issue
Block a user