Merge branch 'master' into feat/deprecate-shelljs
This commit is contained in:
29
demo/89-template-document.ts
Normal file
29
demo/89-template-document.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// Patch a document with patches
|
||||||
|
// Import from 'docx' rather than '../build' if you install from npm
|
||||||
|
import * as fs from "fs";
|
||||||
|
import { IPatch, patchDocument, PatchType, TextRun } from "../build";
|
||||||
|
|
||||||
|
export const font = "Trebuchet MS";
|
||||||
|
export const getPatches = (fields: { [key: string]: string }) => {
|
||||||
|
const patches: { [key: string]: IPatch } = {};
|
||||||
|
|
||||||
|
for (const field in fields) {
|
||||||
|
patches[field] = {
|
||||||
|
type: PatchType.PARAGRAPH,
|
||||||
|
children: [new TextRun({ text: fields[field], font })],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return patches;
|
||||||
|
};
|
||||||
|
|
||||||
|
const patches = getPatches({
|
||||||
|
salutation: "Mr.",
|
||||||
|
"first-name": "John",
|
||||||
|
});
|
||||||
|
|
||||||
|
patchDocument(fs.readFileSync("demo/assets/simple-template-3.docx"), {
|
||||||
|
patches,
|
||||||
|
}).then((doc) => {
|
||||||
|
fs.writeFileSync("My Document.docx", doc);
|
||||||
|
});
|
BIN
demo/assets/simple-template-3.docx
Normal file
BIN
demo/assets/simple-template-3.docx
Normal file
Binary file not shown.
@ -38,6 +38,7 @@
|
|||||||
<script src="https://unpkg.com/docsify-copy-code@2"></script>
|
<script src="https://unpkg.com/docsify-copy-code@2"></script>
|
||||||
<script src="//unpkg.com/docsify/lib/plugins/search.min.js"></script>
|
<script src="//unpkg.com/docsify/lib/plugins/search.min.js"></script>
|
||||||
<script src="//unpkg.com/prismjs/components/prism-typescript.min.js"></script>
|
<script src="//unpkg.com/prismjs/components/prism-typescript.min.js"></script>
|
||||||
|
<script src="https://unpkg.com/docsify-sign-off-sheet@1.0.0/dist/index.iife.js"></script>
|
||||||
<script src="//cdn.jsdelivr.net/npm/docsify-darklight-theme@latest/dist/index.min.js" type="text/javascript"></script>
|
<script src="//cdn.jsdelivr.net/npm/docsify-darklight-theme@latest/dist/index.min.js" type="text/javascript"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
805
package-lock.json
generated
805
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "docx",
|
"name": "docx",
|
||||||
"version": "8.0.1",
|
"version": "8.0.3",
|
||||||
"description": "Easily generate .docx files with JS/TS with a nice declarative API. Works for Node and on the Browser.",
|
"description": "Easily generate .docx files with JS/TS with a nice declarative API. Works for Node and on the Browser.",
|
||||||
"main": "build/index.js",
|
"main": "build/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@ -105,7 +105,7 @@
|
|||||||
"tsconfig-paths": "^4.0.0",
|
"tsconfig-paths": "^4.0.0",
|
||||||
"tsconfig-paths-webpack-plugin": "^4.0.0",
|
"tsconfig-paths-webpack-plugin": "^4.0.0",
|
||||||
"typedoc": "^0.23.2",
|
"typedoc": "^0.23.2",
|
||||||
"typescript": "5.0.2",
|
"typescript": "5.0.3",
|
||||||
"unzipper": "^0.10.11",
|
"unzipper": "^0.10.11",
|
||||||
"webpack": "^5.28.0",
|
"webpack": "^5.28.0",
|
||||||
"webpack-cli": "^5.0.0"
|
"webpack-cli": "^5.0.0"
|
||||||
|
@ -41,11 +41,11 @@ const createAnchor = (drawingOptions: IDrawingOptions): Anchor =>
|
|||||||
|
|
||||||
describe("Anchor", () => {
|
describe("Anchor", () => {
|
||||||
before(() => {
|
before(() => {
|
||||||
stub(convenienceFunctions, "uniqueNumericId").callsFake(() => 0);
|
stub(convenienceFunctions, "docPropertiesUniqueNumericId").callsFake(() => 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
after(() => {
|
after(() => {
|
||||||
(convenienceFunctions.uniqueNumericId as SinonStub).restore();
|
(convenienceFunctions.docPropertiesUniqueNumericId as SinonStub).restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
let anchor: Anchor;
|
let anchor: Anchor;
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
import { IContext, IXmlableObject, NextAttributeComponent, XmlComponent } from "@file/xml-components";
|
import { IContext, IXmlableObject, NextAttributeComponent, XmlComponent } from "@file/xml-components";
|
||||||
import { ConcreteHyperlink } from "@file/paragraph";
|
import { ConcreteHyperlink } from "@file/paragraph";
|
||||||
|
|
||||||
import { uniqueNumericId } from "@util/convenience-functions";
|
import { docPropertiesUniqueNumericId } from "@util/convenience-functions";
|
||||||
|
|
||||||
import { createHyperlinkClick } from "./doc-properties-children";
|
import { createHyperlinkClick } from "./doc-properties-children";
|
||||||
|
|
||||||
@ -32,7 +32,7 @@ export class DocProperties extends XmlComponent {
|
|||||||
new NextAttributeComponent({
|
new NextAttributeComponent({
|
||||||
id: {
|
id: {
|
||||||
key: "id",
|
key: "id",
|
||||||
value: uniqueNumericId(),
|
value: docPropertiesUniqueNumericId(),
|
||||||
},
|
},
|
||||||
name: {
|
name: {
|
||||||
key: "name",
|
key: "name",
|
||||||
|
@ -31,11 +31,11 @@ const createDrawing = (drawingOptions?: IDrawingOptions): Drawing =>
|
|||||||
|
|
||||||
describe("Drawing", () => {
|
describe("Drawing", () => {
|
||||||
before(() => {
|
before(() => {
|
||||||
stub(convenienceFunctions, "uniqueNumericId").callsFake(() => 0);
|
stub(convenienceFunctions, "docPropertiesUniqueNumericId").callsFake(() => 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
after(() => {
|
after(() => {
|
||||||
(convenienceFunctions.uniqueNumericId as SinonStub).restore();
|
(convenienceFunctions.docPropertiesUniqueNumericId as SinonStub).restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
let currentBreak: Drawing;
|
let currentBreak: Drawing;
|
||||||
|
@ -8,11 +8,13 @@ import { Numbering } from "./numbering";
|
|||||||
|
|
||||||
describe("Numbering", () => {
|
describe("Numbering", () => {
|
||||||
before(() => {
|
before(() => {
|
||||||
stub(convenienceFunctions, "uniqueNumericId").callsFake(() => 0);
|
stub(convenienceFunctions, "abstractNumUniqueNumericId").callsFake(() => 0);
|
||||||
|
stub(convenienceFunctions, "concreteNumUniqueNumericId").callsFake(() => 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
after(() => {
|
after(() => {
|
||||||
(convenienceFunctions.uniqueNumericId as SinonStub).restore();
|
(convenienceFunctions.abstractNumUniqueNumericId as SinonStub).restore();
|
||||||
|
(convenienceFunctions.concreteNumUniqueNumericId as SinonStub).restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("#constructor", () => {
|
describe("#constructor", () => {
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
// https://stackoverflow.com/questions/58622437/purpose-of-abstractnum-and-numberinginstance
|
// https://stackoverflow.com/questions/58622437/purpose-of-abstractnum-and-numberinginstance
|
||||||
import { AlignmentType } from "@file/paragraph";
|
import { AlignmentType } from "@file/paragraph";
|
||||||
import { IContext, IXmlableObject, XmlComponent } from "@file/xml-components";
|
import { IContext, IXmlableObject, XmlComponent } from "@file/xml-components";
|
||||||
import { convertInchesToTwip, uniqueNumericId } from "@util/convenience-functions";
|
import { abstractNumUniqueNumericId, concreteNumUniqueNumericId, convertInchesToTwip } from "@util/convenience-functions";
|
||||||
|
|
||||||
import { DocumentAttributes } from "../document/document-attributes";
|
import { DocumentAttributes } from "../document/document-attributes";
|
||||||
import { AbstractNumbering } from "./abstract-numbering";
|
import { AbstractNumbering } from "./abstract-numbering";
|
||||||
@ -55,7 +55,7 @@ export class Numbering extends XmlComponent {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
const abstractNumbering = new AbstractNumbering(uniqueNumericId(), [
|
const abstractNumbering = new AbstractNumbering(abstractNumUniqueNumericId(), [
|
||||||
{
|
{
|
||||||
level: 0,
|
level: 0,
|
||||||
format: LevelFormat.BULLET,
|
format: LevelFormat.BULLET,
|
||||||
@ -176,7 +176,7 @@ export class Numbering extends XmlComponent {
|
|||||||
this.abstractNumberingMap.set("default-bullet-numbering", abstractNumbering);
|
this.abstractNumberingMap.set("default-bullet-numbering", abstractNumbering);
|
||||||
|
|
||||||
for (const con of options.config) {
|
for (const con of options.config) {
|
||||||
this.abstractNumberingMap.set(con.reference, new AbstractNumbering(uniqueNumericId(), con.levels));
|
this.abstractNumberingMap.set(con.reference, new AbstractNumbering(abstractNumUniqueNumericId(), con.levels));
|
||||||
this.referenceConfigMap.set(con.reference, con.levels);
|
this.referenceConfigMap.set(con.reference, con.levels);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -209,7 +209,7 @@ export class Numbering extends XmlComponent {
|
|||||||
const firstLevelStartNumber = referenceConfigLevels && referenceConfigLevels[0].start;
|
const firstLevelStartNumber = referenceConfigLevels && referenceConfigLevels[0].start;
|
||||||
|
|
||||||
const concreteNumberingSettings = {
|
const concreteNumberingSettings = {
|
||||||
numId: uniqueNumericId(),
|
numId: concreteNumUniqueNumericId(),
|
||||||
abstractNumId: abstractNumbering.id,
|
abstractNumId: abstractNumbering.id,
|
||||||
reference,
|
reference,
|
||||||
instance,
|
instance,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// http://officeopenxml.com/WPbookmark.php
|
// http://officeopenxml.com/WPbookmark.php
|
||||||
import { XmlComponent } from "@file/xml-components";
|
import { XmlComponent } from "@file/xml-components";
|
||||||
import { uniqueNumericId } from "@util/convenience-functions";
|
import { bookmarkUniqueNumericId } from "@util/convenience-functions";
|
||||||
|
|
||||||
import { ParagraphChild } from "../paragraph";
|
import { ParagraphChild } from "../paragraph";
|
||||||
import { BookmarkEndAttributes, BookmarkStartAttributes } from "./bookmark-attributes";
|
import { BookmarkEndAttributes, BookmarkStartAttributes } from "./bookmark-attributes";
|
||||||
@ -11,7 +11,7 @@ export class Bookmark {
|
|||||||
public readonly end: BookmarkEnd;
|
public readonly end: BookmarkEnd;
|
||||||
|
|
||||||
public constructor(options: { readonly id: string; readonly children: readonly ParagraphChild[] }) {
|
public constructor(options: { readonly id: string; readonly children: readonly ParagraphChild[] }) {
|
||||||
const linkId = uniqueNumericId();
|
const linkId = bookmarkUniqueNumericId();
|
||||||
|
|
||||||
this.start = new BookmarkStart(options.id, linkId);
|
this.start = new BookmarkStart(options.id, linkId);
|
||||||
this.children = options.children;
|
this.children = options.children;
|
||||||
|
@ -20,12 +20,12 @@ import { TextRun } from "./run";
|
|||||||
describe("Paragraph", () => {
|
describe("Paragraph", () => {
|
||||||
before(() => {
|
before(() => {
|
||||||
stub(convenienceFunctions, "uniqueId").callsFake(() => "test-unique-id");
|
stub(convenienceFunctions, "uniqueId").callsFake(() => "test-unique-id");
|
||||||
stub(convenienceFunctions, "uniqueNumericId").callsFake(() => -101);
|
stub(convenienceFunctions, "bookmarkUniqueNumericId").callsFake(() => -101);
|
||||||
});
|
});
|
||||||
|
|
||||||
after(() => {
|
after(() => {
|
||||||
(convenienceFunctions.uniqueId as SinonStub).restore();
|
(convenienceFunctions.uniqueId as SinonStub).restore();
|
||||||
(convenienceFunctions.uniqueNumericId as SinonStub).restore();
|
(convenienceFunctions.bookmarkUniqueNumericId as SinonStub).restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("#constructor()", () => {
|
describe("#constructor()", () => {
|
||||||
|
@ -11,12 +11,12 @@ import { ImageRun } from "./image-run";
|
|||||||
describe("ImageRun", () => {
|
describe("ImageRun", () => {
|
||||||
before(() => {
|
before(() => {
|
||||||
stub(convenienceFunctions, "uniqueId").callsFake(() => "test-unique-id");
|
stub(convenienceFunctions, "uniqueId").callsFake(() => "test-unique-id");
|
||||||
stub(convenienceFunctions, "uniqueNumericId").callsFake(() => 0);
|
stub(convenienceFunctions, "docPropertiesUniqueNumericId").callsFake(() => 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
after(() => {
|
after(() => {
|
||||||
(convenienceFunctions.uniqueId as SinonStub).restore();
|
(convenienceFunctions.uniqueId as SinonStub).restore();
|
||||||
(convenienceFunctions.uniqueNumericId as SinonStub).restore();
|
(convenienceFunctions.docPropertiesUniqueNumericId as SinonStub).restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("#constructor()", () => {
|
describe("#constructor()", () => {
|
||||||
|
@ -86,6 +86,59 @@ describe("paragraph-split-inject", () => {
|
|||||||
),
|
),
|
||||||
).to.throw();
|
).to.throw();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should continue if text run doesn't have text", () => {
|
||||||
|
expect(() =>
|
||||||
|
findRunElementIndexWithToken(
|
||||||
|
{
|
||||||
|
name: "w:p",
|
||||||
|
type: "element",
|
||||||
|
elements: [
|
||||||
|
{
|
||||||
|
name: "w:r",
|
||||||
|
type: "element",
|
||||||
|
elements: [
|
||||||
|
{
|
||||||
|
name: "w:t",
|
||||||
|
type: "element",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"hello",
|
||||||
|
),
|
||||||
|
).to.throw();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should continue if text run doesn't have text", () => {
|
||||||
|
expect(() =>
|
||||||
|
findRunElementIndexWithToken(
|
||||||
|
{
|
||||||
|
name: "w:p",
|
||||||
|
type: "element",
|
||||||
|
elements: [
|
||||||
|
{
|
||||||
|
name: "w:r",
|
||||||
|
type: "element",
|
||||||
|
elements: [
|
||||||
|
{
|
||||||
|
name: "w:t",
|
||||||
|
type: "element",
|
||||||
|
elements: [
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"hello",
|
||||||
|
),
|
||||||
|
).to.throw();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("splitRunElement", () => {
|
describe("splitRunElement", () => {
|
||||||
|
@ -8,7 +8,11 @@ export const findRunElementIndexWithToken = (paragraphElement: Element, token: s
|
|||||||
const textElement = (element.elements ?? []).filter((e) => e.type === "element" && e.name === "w:t");
|
const textElement = (element.elements ?? []).filter((e) => e.type === "element" && e.name === "w:t");
|
||||||
|
|
||||||
for (const text of textElement) {
|
for (const text of textElement) {
|
||||||
if ((text.elements?.[0].text as string)?.includes(token)) {
|
if (!text.elements?.[0]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((text.elements[0].text as string)?.includes(token)) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { expect } from "chai";
|
import { expect } from "chai";
|
||||||
|
|
||||||
import { convertInchesToTwip, convertMillimetersToTwip, uniqueId, uniqueNumericId } from "./convenience-functions";
|
import { convertInchesToTwip, convertMillimetersToTwip, uniqueId, uniqueNumericIdCreator } from "./convenience-functions";
|
||||||
|
|
||||||
describe("Utility", () => {
|
describe("Utility", () => {
|
||||||
describe("#convertMillimetersToTwip", () => {
|
describe("#convertMillimetersToTwip", () => {
|
||||||
@ -17,8 +17,9 @@ describe("Utility", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("#uniqueNumericId", () => {
|
describe("#uniqueNumericIdCreator", () => {
|
||||||
it("should generate a unique incrementing ID", () => {
|
it("should generate a unique incrementing ID", () => {
|
||||||
|
const uniqueNumericId = uniqueNumericIdCreator();
|
||||||
expect(uniqueNumericId()).to.not.be.undefined;
|
expect(uniqueNumericId()).to.not.be.undefined;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,12 +1,19 @@
|
|||||||
import { nanoid } from "nanoid/non-secure";
|
import { nanoid } from "nanoid/non-secure";
|
||||||
|
|
||||||
let currentCount = 0;
|
|
||||||
|
|
||||||
// Twip - twentieths of a point
|
// Twip - twentieths of a point
|
||||||
export const convertMillimetersToTwip = (millimeters: number): number => Math.floor((millimeters / 25.4) * 72 * 20);
|
export const convertMillimetersToTwip = (millimeters: number): number => Math.floor((millimeters / 25.4) * 72 * 20);
|
||||||
|
|
||||||
export const convertInchesToTwip = (inches: number): number => Math.floor(inches * 72 * 20);
|
export const convertInchesToTwip = (inches: number): number => Math.floor(inches * 72 * 20);
|
||||||
|
|
||||||
export const uniqueNumericId = (): number => ++currentCount;
|
export const uniqueNumericIdCreator = (initial = 0): (() => number) => {
|
||||||
|
let currentCount = initial;
|
||||||
|
|
||||||
|
return () => ++currentCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const abstractNumUniqueNumericId = uniqueNumericIdCreator();
|
||||||
|
export const concreteNumUniqueNumericId = uniqueNumericIdCreator(1); // Setting initial to 1 as we have numId = 1 for "default-bullet-numbering"
|
||||||
|
export const docPropertiesUniqueNumericId = uniqueNumericIdCreator();
|
||||||
|
export const bookmarkUniqueNumericId = uniqueNumericIdCreator();
|
||||||
|
|
||||||
export const uniqueId = (): string => nanoid().toLowerCase();
|
export const uniqueId = (): string => nanoid().toLowerCase();
|
||||||
|
Reference in New Issue
Block a user