From f8f5d43b0c26dc424711250a9a7231d77e799676 Mon Sep 17 00:00:00 2001 From: Dolan Miu Date: Sat, 25 Feb 2023 19:33:12 +0000 Subject: [PATCH] Allow for Paragraph and array patching --- demo/85-template-document.ts | 87 +++++++++++++++++++++++++++++-- demo/assets/simple-template.docx | Bin 12624 -> 13057 bytes src/templater/from-docx.ts | 25 +++++++-- src/templater/replacer.ts | 72 ++++++++++++++----------- 4 files changed, 148 insertions(+), 36 deletions(-) diff --git a/demo/85-template-document.ts b/demo/85-template-document.ts index 4e7608009f..0c95599e84 100644 --- a/demo/85-template-document.ts +++ b/demo/85-template-document.ts @@ -1,26 +1,107 @@ // Simple template example // Import from 'docx' rather than '../build' if you install from npm import * as fs from "fs"; -import { patchDocument, TextRun } from "../build"; +import { + HeadingLevel, + Paragraph, + patchDocument, + PatchType, + Table, + TableCell, + TableRow, + TextDirection, + TextRun, + VerticalAlign, +} from "../src"; patchDocument(fs.readFileSync("demo/assets/simple-template.docx"), { patches: [ { - children: [new TextRun("John Doe")], + type: PatchType.PARAGRAPH, + children: [new TextRun("Sir. "), new TextRun("John Doe"), new TextRun("(The Conqueror)")], text: "{{ name }}", }, { + type: PatchType.PARAGRAPH, children: [new TextRun("Heading wow!")], text: "{{ table_heading_1 }}", }, { + type: PatchType.PARAGRAPH, children: [new TextRun("#657")], text: "{{ item_1 }}", }, { - children: [new TextRun("Lorem ipsum paragraph")], + type: PatchType.DOCUMENT, + children: [new Paragraph("Lorem ipsum paragraph"), new Paragraph("Another paragraph")], text: "{{ paragraph_replace }}", }, + { + type: PatchType.DOCUMENT, + children: [ + new Table({ + rows: [ + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph({}), new Paragraph({})], + verticalAlign: VerticalAlign.CENTER, + }), + new TableCell({ + children: [new Paragraph({}), new Paragraph({})], + verticalAlign: VerticalAlign.CENTER, + }), + new TableCell({ + children: [new Paragraph({ text: "bottom to top" }), new Paragraph({})], + textDirection: TextDirection.BOTTOM_TO_TOP_LEFT_TO_RIGHT, + }), + new TableCell({ + children: [new Paragraph({ text: "top to bottom" }), new Paragraph({})], + textDirection: TextDirection.TOP_TO_BOTTOM_RIGHT_TO_LEFT, + }), + ], + }), + new TableRow({ + children: [ + new TableCell({ + children: [ + new Paragraph({ + text: "Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah", + heading: HeadingLevel.HEADING_1, + }), + ], + }), + new TableCell({ + children: [ + new Paragraph({ + text: "This text should be in the middle of the cell", + }), + ], + verticalAlign: VerticalAlign.CENTER, + }), + new TableCell({ + children: [ + new Paragraph({ + text: "Text above should be vertical from bottom to top", + }), + ], + verticalAlign: VerticalAlign.CENTER, + }), + new TableCell({ + children: [ + new Paragraph({ + text: "Text above should be vertical from top to bottom", + }), + ], + verticalAlign: VerticalAlign.CENTER, + }), + ], + }), + ], + }), + ], + text: "{{ table }}", + }, ], }).then((doc) => { fs.writeFileSync("My Document.docx", doc); diff --git a/demo/assets/simple-template.docx b/demo/assets/simple-template.docx index 1ff29bc52974b12438f3e01b325c0a84c6113f19..c926191881f5028f5e816be14089314c6924c787 100644 GIT binary patch delta 7990 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 B{<8o8 diff --git a/src/templater/from-docx.ts b/src/templater/from-docx.ts index 842e600560..4117e04917 100644 --- a/src/templater/from-docx.ts +++ b/src/templater/from-docx.ts @@ -1,5 +1,9 @@ import * as JSZip from "jszip"; import { Element, js2xml } from "xml-js"; + +import { ParagraphChild } from "@file/paragraph"; +import { FileChild } from "@file/file-child"; + import { replacer } from "./replacer"; import { findLocationOfText } from "./traverser"; import { toJson } from "./util"; @@ -7,10 +11,25 @@ import { toJson } from "./util"; // eslint-disable-next-line functional/prefer-readonly-type type InputDataType = Buffer | string | number[] | Uint8Array | ArrayBuffer | Blob | NodeJS.ReadableStream; -export interface IPatch { - readonly children: any[]; - readonly text: string; +export enum PatchType { + DOCUMENT = "file", + PARAGRAPH = "paragraph", } + +type ParagraphPatch = { + readonly type: PatchType.PARAGRAPH; + readonly children: readonly ParagraphChild[]; +}; + +type FilePatch = { + readonly type: PatchType.DOCUMENT; + readonly children: readonly FileChild[]; +}; + +export type IPatch = { + readonly text: string; +} & (ParagraphPatch | FilePatch); + export interface PatchDocumentOptions { readonly patches: readonly IPatch[]; } diff --git a/src/templater/replacer.ts b/src/templater/replacer.ts index e40d66ca92..95e607a777 100644 --- a/src/templater/replacer.ts +++ b/src/templater/replacer.ts @@ -2,9 +2,9 @@ import { Element } from "xml-js"; import * as xml from "xml"; import { Formatter } from "@export/formatter"; -import { Paragraph, TextRun } from "@file/paragraph"; +import { XmlComponent } from "@file/xml-components"; -import { IPatch } from "./from-docx"; +import { IPatch, PatchType } from "./from-docx"; import { toJson } from "./util"; import { IRenderedParagraphNode } from "./run-renderer"; import { replaceTokenInParagraphElement } from "./paragraph-token-replacer"; @@ -15,39 +15,46 @@ const formatter = new Formatter(); const SPLIT_TOKEN = "ɵ"; export const replacer = (json: Element, options: IPatch, renderedParagraphs: readonly IRenderedParagraphNode[]): Element => { - for (const child of options.children) { - if (child instanceof Paragraph) { - console.log("is para"); - } else if (child instanceof TextRun) { - for (const renderedParagraph of renderedParagraphs) { - const textJson = toJson(xml(formatter.format(child))); - const paragraphElement = goToElementFromPath(json, renderedParagraph.path); + for (const renderedParagraph of renderedParagraphs) { + const textJson = options.children.map((c) => toJson(xml(formatter.format(c as XmlComponent)))).map((c) => c.elements![0]); - const startIndex = renderedParagraph.text.indexOf(options.text); - const endIndex = startIndex + options.text.length - 1; + if (options.type === PatchType.DOCUMENT) { + const parentElement = goToParentElementFromPath(json, renderedParagraph.path); + const elementIndex = getLastElementIndexFromPath(renderedParagraph.path); + // Easy case where the text is the entire paragraph + // We can assume that the Paragraph/Table only has one element + // eslint-disable-next-line functional/immutable-data, prefer-destructuring + parentElement.elements?.splice(elementIndex, 1, ...textJson); + // console.log(JSON.stringify(renderedParagraphs, null, 2)); + // console.log(JSON.stringify(textJson, null, 2)); + // console.log("paragraphElement after", JSON.stringify(parentElement.elements![elementIndex], null, 2)); + } else if (options.type === PatchType.PARAGRAPH) { + const paragraphElement = goToElementFromPath(json, renderedParagraph.path); + const startIndex = renderedParagraph.text.indexOf(options.text); + const endIndex = startIndex + options.text.length - 1; - if (startIndex === 0 && endIndex === renderedParagraph.text.length - 1) { - // Easy case where the text is the entire paragraph - // eslint-disable-next-line functional/immutable-data - paragraphElement.elements = textJson.elements; - } else { - // Hard case where the text is only part of the paragraph + if (startIndex === 0 && endIndex === renderedParagraph.text.length - 1) { + // Easy case where the text is the entire paragraph + // eslint-disable-next-line functional/immutable-data + paragraphElement.elements = textJson; + console.log(JSON.stringify(paragraphElement, null, 2)); + } else { + // Hard case where the text is only part of the paragraph - replaceTokenInParagraphElement({ - paragraphElement, - renderedParagraph, - originalText: options.text, - replacementText: SPLIT_TOKEN, - }); + replaceTokenInParagraphElement({ + paragraphElement, + renderedParagraph, + originalText: options.text, + replacementText: SPLIT_TOKEN, + }); - const index = findRunElementIndexWithToken(paragraphElement, 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)); - } + const { left, right } = splitRunElement(paragraphElement.elements![index], SPLIT_TOKEN); + // eslint-disable-next-line functional/immutable-data + paragraphElement.elements!.splice(index, 1, left, ...textJson, right); + // console.log(index, JSON.stringify(paragraphElement.elements![index], null, 2)); + // console.log("paragraphElement after", JSON.stringify(paragraphElement, null, 2)); } } } @@ -73,3 +80,8 @@ const goToElementFromPath = (json: Element, path: readonly number[]): Element => return element; }; + +const goToParentElementFromPath = (json: Element, path: readonly number[]): Element => + goToElementFromPath(json, path.slice(0, path.length - 1)); + +const getLastElementIndexFromPath = (path: readonly number[]): number => path[path.length - 1];