Clean up
This commit is contained in:
@ -16,31 +16,31 @@ import {
|
|||||||
|
|
||||||
patchDocument(fs.readFileSync("demo/assets/simple-template.docx"), {
|
patchDocument(fs.readFileSync("demo/assets/simple-template.docx"), {
|
||||||
patches: {
|
patches: {
|
||||||
"name":{
|
name: {
|
||||||
type: PatchType.PARAGRAPH,
|
type: PatchType.PARAGRAPH,
|
||||||
children: [new TextRun("Sir. "), new TextRun("John Doe"), new TextRun("(The Conqueror)")],
|
children: [new TextRun("Sir. "), new TextRun("John Doe"), new TextRun("(The Conqueror)")],
|
||||||
},
|
},
|
||||||
"table_heading_1": {
|
table_heading_1: {
|
||||||
type: PatchType.PARAGRAPH,
|
type: PatchType.PARAGRAPH,
|
||||||
children: [new TextRun("Heading wow!")],
|
children: [new TextRun("Heading wow!")],
|
||||||
},
|
},
|
||||||
"item_1": {
|
item_1: {
|
||||||
type: PatchType.PARAGRAPH,
|
type: PatchType.PARAGRAPH,
|
||||||
children: [new TextRun("#657")],
|
children: [new TextRun("#657")],
|
||||||
},
|
},
|
||||||
"paragraph_replace": {
|
paragraph_replace: {
|
||||||
type: PatchType.DOCUMENT,
|
type: PatchType.DOCUMENT,
|
||||||
children: [new Paragraph("Lorem ipsum paragraph"), new Paragraph("Another paragraph")],
|
children: [new Paragraph("Lorem ipsum paragraph"), new Paragraph("Another paragraph")],
|
||||||
},
|
},
|
||||||
"header_adjective": {
|
header_adjective: {
|
||||||
type: PatchType.PARAGRAPH,
|
type: PatchType.PARAGRAPH,
|
||||||
children: [new TextRun("Delightful Header")],
|
children: [new TextRun("Delightful Header")],
|
||||||
},
|
},
|
||||||
"footer_text": {
|
footer_text: {
|
||||||
type: PatchType.PARAGRAPH,
|
type: PatchType.PARAGRAPH,
|
||||||
children: [new TextRun("replaced just as well")],
|
children: [new TextRun("replaced just as well")],
|
||||||
},
|
},
|
||||||
"table": {
|
table: {
|
||||||
type: PatchType.DOCUMENT,
|
type: PatchType.DOCUMENT,
|
||||||
children: [
|
children: [
|
||||||
new Table({
|
new Table({
|
||||||
@ -102,7 +102,6 @@ patchDocument(fs.readFileSync("demo/assets/simple-template.docx"), {
|
|||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -8,7 +8,7 @@ const doc = new Document({
|
|||||||
{
|
{
|
||||||
children: [
|
children: [
|
||||||
new Paragraph({
|
new Paragraph({
|
||||||
children: [new TextRun("{{ template }}")],
|
children: [new TextRun("{{template}}")],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
BIN
demo/assets/generated-template.docx
Normal file
BIN
demo/assets/generated-template.docx
Normal file
Binary file not shown.
@ -43,6 +43,7 @@ export const patchDocument = async (data: InputDataType, options: PatchDocumentO
|
|||||||
for (const [patchKey, patchValue] of Object.entries(options.patches)) {
|
for (const [patchKey, patchValue] of Object.entries(options.patches)) {
|
||||||
const patchText = `{{${patchKey}}}`;
|
const patchText = `{{${patchKey}}}`;
|
||||||
const renderedParagraphs = findLocationOfText(json, patchText);
|
const renderedParagraphs = findLocationOfText(json, patchText);
|
||||||
|
// TODO: mutates json. Make it immutable
|
||||||
replacer(json, patchValue, patchText, renderedParagraphs);
|
replacer(json, patchValue, patchText, renderedParagraphs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Element } from "xml-js";
|
import { Element } from "xml-js";
|
||||||
import { createTextElementContents } from "./util";
|
import { createTextElementContents, patchSpaceAttribute } from "./util";
|
||||||
|
|
||||||
export const findRunElementIndexWithToken = (paragraphElement: Element, token: string): number => {
|
export const findRunElementIndexWithToken = (paragraphElement: Element, token: string): number => {
|
||||||
for (let i = 0; i < (paragraphElement.elements ?? []).length; i++) {
|
for (let i = 0; i < (paragraphElement.elements ?? []).length; i++) {
|
||||||
@ -29,9 +29,7 @@ export const splitRunElement = (runElement: Element, token: string): { readonly
|
|||||||
const splitText = text.split(token);
|
const splitText = text.split(token);
|
||||||
const newElements = splitText.map((t) => ({
|
const newElements = splitText.map((t) => ({
|
||||||
...e,
|
...e,
|
||||||
attributes: {
|
...patchSpaceAttribute(e),
|
||||||
"xml:space": "preserve",
|
|
||||||
},
|
|
||||||
elements: createTextElementContents(t),
|
elements: createTextElementContents(t),
|
||||||
}));
|
}));
|
||||||
splitIndex = i;
|
splitIndex = i;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Element } from "xml-js";
|
import { Element } from "xml-js";
|
||||||
|
|
||||||
import { createTextElementContents } from "./util";
|
import { createTextElementContents, patchSpaceAttribute } from "./util";
|
||||||
import { IRenderedParagraphNode } from "./run-renderer";
|
import { IRenderedParagraphNode } from "./run-renderer";
|
||||||
|
|
||||||
enum ReplaceMode {
|
enum ReplaceMode {
|
||||||
@ -43,6 +43,11 @@ export const replaceTokenInParagraphElement = ({
|
|||||||
if (endIndex <= end) {
|
if (endIndex <= end) {
|
||||||
const lastPart = text.substring(endIndex - start + 1);
|
const lastPart = text.substring(endIndex - start + 1);
|
||||||
patchTextElement(paragraphElement.elements![run.index].elements![index], lastPart);
|
patchTextElement(paragraphElement.elements![run.index].elements![index], lastPart);
|
||||||
|
const currentElement = paragraphElement.elements![run.index].elements![index];
|
||||||
|
// We need to add xml:space="preserve" to the last element to preserve the whitespace
|
||||||
|
// Otherwise, the text will be merged with the next element
|
||||||
|
// eslint-disable-next-line functional/immutable-data
|
||||||
|
paragraphElement.elements![run.index].elements![index] = patchSpaceAttribute(currentElement);
|
||||||
replaceMode = ReplaceMode.END;
|
replaceMode = ReplaceMode.END;
|
||||||
} else {
|
} else {
|
||||||
patchTextElement(paragraphElement.elements![run.index].elements![index], "");
|
patchTextElement(paragraphElement.elements![run.index].elements![index], "");
|
||||||
|
@ -14,7 +14,12 @@ const formatter = new Formatter();
|
|||||||
|
|
||||||
const SPLIT_TOKEN = "ɵ";
|
const SPLIT_TOKEN = "ɵ";
|
||||||
|
|
||||||
export const replacer = (json: Element, patch: IPatch, patchText: string, renderedParagraphs: readonly IRenderedParagraphNode[]): Element => {
|
export const replacer = (
|
||||||
|
json: Element,
|
||||||
|
patch: IPatch,
|
||||||
|
patchText: string,
|
||||||
|
renderedParagraphs: readonly IRenderedParagraphNode[],
|
||||||
|
): Element => {
|
||||||
for (const renderedParagraph of renderedParagraphs) {
|
for (const renderedParagraph of renderedParagraphs) {
|
||||||
const textJson = patch.children.map((c) => toJson(xml(formatter.format(c as XmlComponent)))).map((c) => c.elements![0]);
|
const textJson = patch.children.map((c) => toJson(xml(formatter.format(c as XmlComponent)))).map((c) => c.elements![0]);
|
||||||
|
|
||||||
@ -24,7 +29,6 @@ export const replacer = (json: Element, patch: IPatch, patchText: string, render
|
|||||||
// eslint-disable-next-line functional/immutable-data, prefer-destructuring
|
// eslint-disable-next-line functional/immutable-data, prefer-destructuring
|
||||||
parentElement.elements?.splice(elementIndex, 1, ...textJson);
|
parentElement.elements?.splice(elementIndex, 1, ...textJson);
|
||||||
} else if (patch.type === PatchType.PARAGRAPH) {
|
} else if (patch.type === PatchType.PARAGRAPH) {
|
||||||
// Hard case where the text is only part of the paragraph
|
|
||||||
const paragraphElement = goToElementFromPath(json, renderedParagraph.path);
|
const paragraphElement = goToElementFromPath(json, renderedParagraph.path);
|
||||||
|
|
||||||
replaceTokenInParagraphElement({
|
replaceTokenInParagraphElement({
|
||||||
|
@ -8,17 +8,6 @@ export interface ElementWrapper {
|
|||||||
readonly parent: ElementWrapper | undefined;
|
readonly parent: ElementWrapper | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ILocationOfText {
|
|
||||||
readonly parent: Element;
|
|
||||||
readonly startIndex: number;
|
|
||||||
readonly endIndex: number;
|
|
||||||
readonly currentText: string;
|
|
||||||
// This is optional because the text could start in the middle of a tag
|
|
||||||
readonly startElement?: Element;
|
|
||||||
// This is optional because the text could end in the middle of a tag
|
|
||||||
readonly endElement?: Element;
|
|
||||||
}
|
|
||||||
|
|
||||||
const elementsToWrapper = (wrapper: ElementWrapper): readonly ElementWrapper[] =>
|
const elementsToWrapper = (wrapper: ElementWrapper): readonly ElementWrapper[] =>
|
||||||
wrapper.element.elements?.map((e, i) => ({
|
wrapper.element.elements?.map((e, i) => ({
|
||||||
element: e,
|
element: e,
|
||||||
@ -56,7 +45,5 @@ export const findLocationOfText = (node: Element, text: string): readonly IRende
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const filteredParagraphs = renderedParagraphs.filter((p) => p.text.includes(text));
|
return renderedParagraphs.filter((p) => p.text.includes(text));
|
||||||
|
|
||||||
return filteredParagraphs;
|
|
||||||
};
|
};
|
||||||
|
@ -17,3 +17,10 @@ export const createTextElementContents = (text: string): Element[] => {
|
|||||||
|
|
||||||
return textJson.elements![0].elements ?? [];
|
return textJson.elements![0].elements ?? [];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const patchSpaceAttribute = (element: Element): Element => ({
|
||||||
|
...element,
|
||||||
|
attributes: {
|
||||||
|
"xml:space": "preserve",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
Reference in New Issue
Block a user