Merge pull request #179 from dolanmiu/feat/import-dotx
Feat/import dotx
This commit is contained in:
24
.vscode/tasks.json
vendored
Normal file
24
.vscode/tasks.json
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||||
|
// for the documentation about the tasks.json format
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"type": "typescript",
|
||||||
|
"tsconfig": "tsconfig.json",
|
||||||
|
"option": "watch",
|
||||||
|
"problemMatcher": [
|
||||||
|
"$tsc-watch"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "npm",
|
||||||
|
"script": "ts-node",
|
||||||
|
"problemMatcher": [],
|
||||||
|
"group": {
|
||||||
|
"kind": "build",
|
||||||
|
"isDefault": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -15,8 +15,12 @@ const footer = doc.createFooter();
|
|||||||
footer.createParagraph("Footer on another page");
|
footer.createParagraph("Footer on another page");
|
||||||
|
|
||||||
doc.addSection({
|
doc.addSection({
|
||||||
headerId: header.Header.ReferenceId,
|
headers: {
|
||||||
footerId: footer.Footer.ReferenceId,
|
default: header,
|
||||||
|
},
|
||||||
|
footers: {
|
||||||
|
default: footer,
|
||||||
|
},
|
||||||
pageNumberStart: 1,
|
pageNumberStart: 1,
|
||||||
pageNumberFormatType: PageNumberFormat.DECIMAL,
|
pageNumberFormatType: PageNumberFormat.DECIMAL,
|
||||||
});
|
});
|
||||||
@ -24,8 +28,12 @@ doc.addSection({
|
|||||||
doc.createParagraph("hello");
|
doc.createParagraph("hello");
|
||||||
|
|
||||||
doc.addSection({
|
doc.addSection({
|
||||||
headerId: header.Header.ReferenceId,
|
headers: {
|
||||||
footerId: footer.Footer.ReferenceId,
|
default: header,
|
||||||
|
},
|
||||||
|
footers: {
|
||||||
|
default: footer,
|
||||||
|
},
|
||||||
pageNumberStart: 1,
|
pageNumberStart: 1,
|
||||||
pageNumberFormatType: PageNumberFormat.DECIMAL,
|
pageNumberFormatType: PageNumberFormat.DECIMAL,
|
||||||
orientation: PageOrientation.LANDSCAPE,
|
orientation: PageOrientation.LANDSCAPE,
|
||||||
|
@ -8,11 +8,11 @@ const doc = new Document();
|
|||||||
const paragraph = new Paragraph("Hello World");
|
const paragraph = new Paragraph("Hello World");
|
||||||
doc.addParagraph(paragraph);
|
doc.addParagraph(paragraph);
|
||||||
|
|
||||||
const image = Media.addImage(doc, "./demo/images/image1.jpeg");
|
const image = Media.addImage(doc, fs.readFileSync("./demo/images/image1.jpeg"));
|
||||||
const image2 = Media.addImage(doc, "./demo/images/dog.png");
|
const image2 = Media.addImage(doc, fs.readFileSync("./demo/images/dog.png"));
|
||||||
const image3 = Media.addImage(doc, "./demo/images/cat.jpg");
|
const image3 = Media.addImage(doc, fs.readFileSync("./demo/images/cat.jpg"));
|
||||||
const image4 = Media.addImage(doc, "./demo/images/parrots.bmp");
|
const image4 = Media.addImage(doc, fs.readFileSync("./demo/images/parrots.bmp"));
|
||||||
const image5 = Media.addImage(doc, "./demo/images/pizza.gif");
|
const image5 = Media.addImage(doc, fs.readFileSync("./demo/images/pizza.gif"));
|
||||||
|
|
||||||
const imageBase64Data = `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`;
|
const imageBase64Data = `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`;
|
||||||
const image6 = Media.addImage(doc, Buffer.from(imageBase64Data, "base64"), 100, 100);
|
const image6 = Media.addImage(doc, Buffer.from(imageBase64Data, "base64"), 100, 100);
|
||||||
|
@ -8,7 +8,7 @@ const doc = new Document();
|
|||||||
const table = doc.createTable(4, 4);
|
const table = doc.createTable(4, 4);
|
||||||
table.getCell(2, 2).addContent(new Paragraph("Hello"));
|
table.getCell(2, 2).addContent(new Paragraph("Hello"));
|
||||||
|
|
||||||
const image = Media.addImage(doc, "./demo/images/image1.jpeg");
|
const image = Media.addImage(doc, fs.readFileSync("./demo/images/image1.jpeg"));
|
||||||
table.getCell(1, 1).addContent(image.Paragraph);
|
table.getCell(1, 1).addContent(image.Paragraph);
|
||||||
|
|
||||||
const packer = new Packer();
|
const packer = new Packer();
|
||||||
|
@ -30,5 +30,4 @@ const packer = new Packer();
|
|||||||
|
|
||||||
packer.toBuffer(doc).then((buffer) => {
|
packer.toBuffer(doc).then((buffer) => {
|
||||||
fs.writeFileSync("My Document.docx", buffer);
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
console.log("Document created successfully at project root!");
|
|
||||||
});
|
});
|
||||||
|
29
demo/demo30.ts
Normal file
29
demo/demo30.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import * as fs from "fs";
|
||||||
|
import { Document, ImportDotx, Packer, Paragraph } from "../build";
|
||||||
|
|
||||||
|
const importDotx = new ImportDotx();
|
||||||
|
const filePath = "./demo/dotx/template.dotx";
|
||||||
|
|
||||||
|
fs.readFile(filePath, (err, data) => {
|
||||||
|
if (err) {
|
||||||
|
throw new Error(`Failed to read file ${filePath}.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
importDotx.extract(data).then((templateDocument) => {
|
||||||
|
// This any needs fixing
|
||||||
|
const sectionProps = {
|
||||||
|
titlePage: templateDocument.titlePageIsDefined,
|
||||||
|
} as any;
|
||||||
|
|
||||||
|
const doc = new Document(undefined, sectionProps, {
|
||||||
|
template: templateDocument,
|
||||||
|
});
|
||||||
|
const paragraph = new Paragraph("Hello World");
|
||||||
|
doc.addParagraph(paragraph);
|
||||||
|
|
||||||
|
const packer = new Packer();
|
||||||
|
packer.toBuffer(doc).then((buffer) => {
|
||||||
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
BIN
demo/dotx/template.dotx
Normal file
BIN
demo/dotx/template.dotx
Normal file
Binary file not shown.
@ -48,7 +48,7 @@
|
|||||||
"types": "./build/index.d.ts",
|
"types": "./build/index.d.ts",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/image-size": "0.0.29",
|
"@types/image-size": "0.0.29",
|
||||||
"@types/jszip": "^3.1.3",
|
"@types/jszip": "^3.1.4",
|
||||||
"fast-xml-parser": "^3.3.6",
|
"fast-xml-parser": "^3.3.6",
|
||||||
"image-size": "^0.6.2",
|
"image-size": "^0.6.2",
|
||||||
"jszip": "^3.1.5",
|
"jszip": "^3.1.5",
|
||||||
|
@ -2,6 +2,12 @@ import { BaseXmlComponent, IXmlableObject } from "file/xml-components";
|
|||||||
|
|
||||||
export class Formatter {
|
export class Formatter {
|
||||||
public format(input: BaseXmlComponent): IXmlableObject {
|
public format(input: BaseXmlComponent): IXmlableObject {
|
||||||
return input.prepForXml();
|
const output = input.prepForXml();
|
||||||
|
|
||||||
|
if (output) {
|
||||||
|
return output;
|
||||||
|
} else {
|
||||||
|
throw Error("XMLComponent did not format correctly");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,6 +59,18 @@ export class Compiler {
|
|||||||
zip.file(`word/media/${data.fileName}`, mediaData);
|
zip.file(`word/media/${data.fileName}`, mediaData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const header of file.Headers) {
|
||||||
|
for (const data of header.Media.Array) {
|
||||||
|
zip.file(`word/media/${data.fileName}`, data.stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const footer of file.Footers) {
|
||||||
|
for (const data of footer.Media.Array) {
|
||||||
|
zip.file(`word/media/${data.fileName}`, data.stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return zip;
|
return zip;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,4 +140,13 @@ export class Compiler {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* By default docx collapse empty tags. <a></a> -> <a/>. this function mimic it
|
||||||
|
so comparing (diff) original docx file and the library output is easier
|
||||||
|
Currently not used, so commenting out */
|
||||||
|
// private collapseEmptyTags(xmlData: string): string {
|
||||||
|
// const regEx = /<(([^ <>]+)[^<>]*)><\/\2>/g;
|
||||||
|
// const collapsed = xmlData.replace(regEx, "<$1/>");
|
||||||
|
// return collapsed;
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ describe("Body", () => {
|
|||||||
expect(formatted)
|
expect(formatted)
|
||||||
.to.have.property("w:sectPr")
|
.to.have.property("w:sectPr")
|
||||||
.and.to.be.an.instanceof(Array);
|
.and.to.be.an.instanceof(Array);
|
||||||
expect(formatted["w:sectPr"]).to.have.length(7);
|
expect(formatted["w:sectPr"]).to.have.length(5);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ export class Body extends XmlComponent {
|
|||||||
this.sections.push(new SectionProperties(params));
|
this.sections.push(new SectionProperties(params));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public prepForXml(): IXmlableObject {
|
public prepForXml(): IXmlableObject | undefined {
|
||||||
if (this.sections.length === 1) {
|
if (this.sections.length === 1) {
|
||||||
this.root.push(this.sections[0]);
|
this.root.push(this.sections[0]);
|
||||||
} else if (this.sections.length > 1) {
|
} else if (this.sections.length > 1) {
|
||||||
|
@ -8,9 +8,7 @@ describe("PageBorders", () => {
|
|||||||
describe("#constructor()", () => {
|
describe("#constructor()", () => {
|
||||||
it("should create empty element when no options are passed", () => {
|
it("should create empty element when no options are passed", () => {
|
||||||
const properties = new PageBorders();
|
const properties = new PageBorders();
|
||||||
const tree = new Formatter().format(properties);
|
expect(() => new Formatter().format(properties)).to.throw();
|
||||||
|
|
||||||
expect(tree).to.equal("");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should create page borders with some configuration", () => {
|
it("should create page borders with some configuration", () => {
|
||||||
|
@ -98,7 +98,9 @@ export class PageBorders extends XmlComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public prepForXml(): IXmlableObject {
|
public prepForXml(): IXmlableObject | undefined {
|
||||||
return this.root.length > 0 ? super.prepForXml() : "";
|
if (this.root.length > 0) {
|
||||||
|
return super.prepForXml();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,17 @@
|
|||||||
import { expect } from "chai";
|
import { expect } from "chai";
|
||||||
|
|
||||||
import { Formatter } from "../../../../export/formatter";
|
import { Formatter } from "../../../../export/formatter";
|
||||||
import { FooterReferenceType, PageBorderOffsetFrom, PageNumberFormat } from "./";
|
import { FooterWrapper } from "../../../footer-wrapper";
|
||||||
|
import { HeaderWrapper } from "../../../header-wrapper";
|
||||||
|
import { Media } from "../../../media";
|
||||||
|
import { PageBorderOffsetFrom, PageNumberFormat } from "./";
|
||||||
import { SectionProperties } from "./section-properties";
|
import { SectionProperties } from "./section-properties";
|
||||||
|
|
||||||
describe("SectionProperties", () => {
|
describe("SectionProperties", () => {
|
||||||
describe("#constructor()", () => {
|
describe("#constructor()", () => {
|
||||||
it("should create section properties with options", () => {
|
it("should create section properties with options", () => {
|
||||||
|
const media = new Media();
|
||||||
|
|
||||||
const properties = new SectionProperties({
|
const properties = new SectionProperties({
|
||||||
width: 11906,
|
width: 11906,
|
||||||
height: 16838,
|
height: 16838,
|
||||||
@ -20,9 +25,12 @@ describe("SectionProperties", () => {
|
|||||||
mirror: false,
|
mirror: false,
|
||||||
space: 708,
|
space: 708,
|
||||||
linePitch: 360,
|
linePitch: 360,
|
||||||
headerId: 100,
|
headers: {
|
||||||
footerId: 200,
|
default: new HeaderWrapper(media, 100),
|
||||||
footerType: FooterReferenceType.EVEN,
|
},
|
||||||
|
footers: {
|
||||||
|
even: new FooterWrapper(media, 200),
|
||||||
|
},
|
||||||
pageNumberStart: 10,
|
pageNumberStart: 10,
|
||||||
pageNumberFormatType: PageNumberFormat.CARDINAL_TEXT,
|
pageNumberFormatType: PageNumberFormat.CARDINAL_TEXT,
|
||||||
});
|
});
|
||||||
@ -78,9 +86,7 @@ describe("SectionProperties", () => {
|
|||||||
});
|
});
|
||||||
expect(tree["w:sectPr"][2]).to.deep.equal({ "w:cols": [{ _attr: { "w:space": 708 } }] });
|
expect(tree["w:sectPr"][2]).to.deep.equal({ "w:cols": [{ _attr: { "w:space": 708 } }] });
|
||||||
expect(tree["w:sectPr"][3]).to.deep.equal({ "w:docGrid": [{ _attr: { "w:linePitch": 360 } }] });
|
expect(tree["w:sectPr"][3]).to.deep.equal({ "w:docGrid": [{ _attr: { "w:linePitch": 360 } }] });
|
||||||
expect(tree["w:sectPr"][4]).to.deep.equal({ "w:headerReference": [{ _attr: { "r:id": "rId0", "w:type": "default" } }] });
|
expect(tree["w:sectPr"][4]).to.deep.equal({ "w:pgNumType": [{ _attr: { "w:fmt": "decimal" } }] });
|
||||||
expect(tree["w:sectPr"][5]).to.deep.equal({ "w:footerReference": [{ _attr: { "r:id": "rId0", "w:type": "default" } }] });
|
|
||||||
expect(tree["w:sectPr"][6]).to.deep.equal({ "w:pgNumType": [{ _attr: { "w:fmt": "decimal" } }] });
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should create section properties with changed options", () => {
|
it("should create section properties with changed options", () => {
|
||||||
@ -170,7 +176,8 @@ describe("SectionProperties", () => {
|
|||||||
});
|
});
|
||||||
const tree = new Formatter().format(properties);
|
const tree = new Formatter().format(properties);
|
||||||
expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]);
|
expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]);
|
||||||
expect(tree["w:sectPr"][7]).to.deep.equal({
|
const pgBorders = tree["w:sectPr"].find((item) => item["w:pgBorders"] !== undefined);
|
||||||
|
expect(pgBorders).to.deep.equal({
|
||||||
"w:pgBorders": [{ _attr: { "w:offsetFrom": "page" } }],
|
"w:pgBorders": [{ _attr: { "w:offsetFrom": "page" } }],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,115 +1,170 @@
|
|||||||
// http://officeopenxml.com/WPsection.php
|
// http://officeopenxml.com/WPsection.php
|
||||||
import { XmlComponent } from "file/xml-components";
|
import { XmlComponent } from "file/xml-components";
|
||||||
import { FooterReferenceType, IPageBordersOptions, IPageNumberTypeAttributes, PageBorders, PageNumberFormat, PageNumberType } from "./";
|
import { FooterWrapper } from "../../../footer-wrapper";
|
||||||
|
import { HeaderWrapper } from "../../../header-wrapper";
|
||||||
|
import { IPageBordersOptions, IPageNumberTypeAttributes, PageBorders, PageNumberFormat, PageNumberType } from "./";
|
||||||
import { Columns } from "./columns/columns";
|
import { Columns } from "./columns/columns";
|
||||||
import { IColumnsAttributes } from "./columns/columns-attributes";
|
import { IColumnsAttributes } from "./columns/columns-attributes";
|
||||||
import { DocumentGrid } from "./doc-grid/doc-grid";
|
import { DocumentGrid } from "./doc-grid/doc-grid";
|
||||||
import { IDocGridAttributesProperties } from "./doc-grid/doc-grid-attributes";
|
import { IDocGridAttributesProperties } from "./doc-grid/doc-grid-attributes";
|
||||||
import { FooterReference, IFooterOptions } from "./footer-reference/footer-reference";
|
import { FooterReferenceType } from "./footer-reference";
|
||||||
import { HeaderReference, IHeaderOptions } from "./header-reference/header-reference";
|
import { FooterReference } from "./footer-reference/footer-reference";
|
||||||
import { HeaderReferenceType } from "./header-reference/header-reference-attributes";
|
import { HeaderReferenceType } from "./header-reference";
|
||||||
|
import { HeaderReference } from "./header-reference/header-reference";
|
||||||
import { PageMargin } from "./page-margin/page-margin";
|
import { PageMargin } from "./page-margin/page-margin";
|
||||||
import { IPageMarginAttributes } from "./page-margin/page-margin-attributes";
|
import { IPageMarginAttributes } from "./page-margin/page-margin-attributes";
|
||||||
import { PageSize } from "./page-size/page-size";
|
import { PageSize } from "./page-size/page-size";
|
||||||
import { IPageSizeAttributes, PageOrientation } from "./page-size/page-size-attributes";
|
import { IPageSizeAttributes, PageOrientation } from "./page-size/page-size-attributes";
|
||||||
|
import { TitlePage } from "./title-page/title-page";
|
||||||
|
|
||||||
|
export interface IHeaderFooterGroup<T> {
|
||||||
|
default?: T;
|
||||||
|
first?: T;
|
||||||
|
even?: T;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IHeadersOptions {
|
||||||
|
headers?: IHeaderFooterGroup<HeaderWrapper>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IFootersOptions {
|
||||||
|
footers?: IHeaderFooterGroup<FooterWrapper>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ITitlePageOptions {
|
||||||
|
titlePage?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export type SectionPropertiesOptions = IPageSizeAttributes &
|
export type SectionPropertiesOptions = IPageSizeAttributes &
|
||||||
IPageMarginAttributes &
|
IPageMarginAttributes &
|
||||||
IColumnsAttributes &
|
IColumnsAttributes &
|
||||||
IDocGridAttributesProperties &
|
IDocGridAttributesProperties &
|
||||||
IHeaderOptions &
|
IHeadersOptions &
|
||||||
IFooterOptions &
|
IFootersOptions &
|
||||||
IPageNumberTypeAttributes &
|
IPageNumberTypeAttributes &
|
||||||
IPageBordersOptions;
|
IPageBordersOptions &
|
||||||
|
ITitlePageOptions;
|
||||||
|
|
||||||
export class SectionProperties extends XmlComponent {
|
export class SectionProperties extends XmlComponent {
|
||||||
private readonly options: SectionPropertiesOptions;
|
private readonly options: SectionPropertiesOptions;
|
||||||
|
|
||||||
constructor(options?: SectionPropertiesOptions) {
|
constructor(options: SectionPropertiesOptions = {}) {
|
||||||
super("w:sectPr");
|
super("w:sectPr");
|
||||||
|
|
||||||
const defaultOptions = {
|
const {
|
||||||
width: 11906,
|
width = 11906,
|
||||||
height: 16838,
|
height = 16838,
|
||||||
top: 1440,
|
top = 1440,
|
||||||
right: 1440,
|
right = 1440,
|
||||||
bottom: 1440,
|
bottom = 1440,
|
||||||
left: 1440,
|
left = 1440,
|
||||||
header: 708,
|
header = 708,
|
||||||
footer: 708,
|
footer = 708,
|
||||||
gutter: 0,
|
gutter = 0,
|
||||||
mirror: false,
|
mirror = false,
|
||||||
space: 708,
|
space = 708,
|
||||||
linePitch: 360,
|
linePitch = 360,
|
||||||
orientation: PageOrientation.PORTRAIT,
|
orientation = PageOrientation.PORTRAIT,
|
||||||
headerType: HeaderReferenceType.DEFAULT,
|
headers,
|
||||||
headerId: 0,
|
footers,
|
||||||
footerType: FooterReferenceType.DEFAULT,
|
pageNumberFormatType = PageNumberFormat.DECIMAL,
|
||||||
footerId: 0,
|
pageNumberStart,
|
||||||
pageNumberStart: undefined,
|
pageBorders,
|
||||||
pageNumberFormatType: PageNumberFormat.DECIMAL,
|
pageBorderTop,
|
||||||
pageBorders: undefined,
|
pageBorderRight,
|
||||||
pageBorderTop: undefined,
|
pageBorderBottom,
|
||||||
pageBorderRight: undefined,
|
pageBorderLeft,
|
||||||
pageBorderBottom: undefined,
|
titlePage = false,
|
||||||
pageBorderLeft: undefined,
|
} = options;
|
||||||
};
|
|
||||||
|
|
||||||
const mergedOptions = {
|
this.options = options;
|
||||||
...defaultOptions,
|
this.root.push(new PageSize(width, height, orientation));
|
||||||
...options,
|
this.root.push(new PageMargin(top, right, bottom, left, header, footer, gutter, mirror));
|
||||||
};
|
this.root.push(new Columns(space));
|
||||||
|
this.root.push(new DocumentGrid(linePitch));
|
||||||
|
|
||||||
this.root.push(new PageSize(mergedOptions.width, mergedOptions.height, mergedOptions.orientation));
|
this.addHeaders(headers);
|
||||||
this.root.push(
|
this.addFooters(footers);
|
||||||
new PageMargin(
|
|
||||||
mergedOptions.top,
|
|
||||||
mergedOptions.right,
|
|
||||||
mergedOptions.bottom,
|
|
||||||
mergedOptions.left,
|
|
||||||
mergedOptions.header,
|
|
||||||
mergedOptions.footer,
|
|
||||||
mergedOptions.gutter,
|
|
||||||
mergedOptions.mirror,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
this.root.push(new Columns(mergedOptions.space));
|
|
||||||
this.root.push(new DocumentGrid(mergedOptions.linePitch));
|
|
||||||
|
|
||||||
this.root.push(
|
this.root.push(new PageNumberType(pageNumberStart, pageNumberFormatType));
|
||||||
new HeaderReference({
|
|
||||||
headerType: mergedOptions.headerType,
|
|
||||||
headerId: mergedOptions.headerId,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
this.root.push(
|
|
||||||
new FooterReference({
|
|
||||||
footerType: mergedOptions.footerType,
|
|
||||||
footerId: mergedOptions.footerId,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
this.root.push(new PageNumberType(mergedOptions.pageNumberStart, mergedOptions.pageNumberFormatType));
|
if (pageBorders || pageBorderTop || pageBorderRight || pageBorderBottom || pageBorderLeft) {
|
||||||
|
|
||||||
if (
|
|
||||||
mergedOptions.pageBorders ||
|
|
||||||
mergedOptions.pageBorderTop ||
|
|
||||||
mergedOptions.pageBorderRight ||
|
|
||||||
mergedOptions.pageBorderBottom ||
|
|
||||||
mergedOptions.pageBorderLeft
|
|
||||||
) {
|
|
||||||
this.root.push(
|
this.root.push(
|
||||||
new PageBorders({
|
new PageBorders({
|
||||||
pageBorders: mergedOptions.pageBorders,
|
pageBorders: pageBorders,
|
||||||
pageBorderTop: mergedOptions.pageBorderTop,
|
pageBorderTop: pageBorderTop,
|
||||||
pageBorderRight: mergedOptions.pageBorderRight,
|
pageBorderRight: pageBorderRight,
|
||||||
pageBorderBottom: mergedOptions.pageBorderBottom,
|
pageBorderBottom: pageBorderBottom,
|
||||||
pageBorderLeft: mergedOptions.pageBorderLeft,
|
pageBorderLeft: pageBorderLeft,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.options = mergedOptions;
|
if (titlePage) {
|
||||||
|
this.root.push(new TitlePage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private addHeaders(headers?: IHeaderFooterGroup<HeaderWrapper>): void {
|
||||||
|
if (headers) {
|
||||||
|
if (headers.default) {
|
||||||
|
this.root.push(
|
||||||
|
new HeaderReference({
|
||||||
|
headerType: HeaderReferenceType.DEFAULT,
|
||||||
|
headerId: headers.default.Header.ReferenceId,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (headers.first) {
|
||||||
|
this.root.push(
|
||||||
|
new HeaderReference({
|
||||||
|
headerType: HeaderReferenceType.FIRST,
|
||||||
|
headerId: headers.first.Header.ReferenceId,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (headers.even) {
|
||||||
|
this.root.push(
|
||||||
|
new HeaderReference({
|
||||||
|
headerType: HeaderReferenceType.EVEN,
|
||||||
|
headerId: headers.even.Header.ReferenceId,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private addFooters(footers?: IHeaderFooterGroup<FooterWrapper>): void {
|
||||||
|
if (footers) {
|
||||||
|
if (footers.default) {
|
||||||
|
this.root.push(
|
||||||
|
new FooterReference({
|
||||||
|
footerType: FooterReferenceType.DEFAULT,
|
||||||
|
footerId: footers.default.Footer.ReferenceId,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (footers.first) {
|
||||||
|
this.root.push(
|
||||||
|
new FooterReference({
|
||||||
|
footerType: FooterReferenceType.FIRST,
|
||||||
|
footerId: footers.first.Footer.ReferenceId,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (footers.even) {
|
||||||
|
this.root.push(
|
||||||
|
new FooterReference({
|
||||||
|
footerType: FooterReferenceType.EVEN,
|
||||||
|
footerId: footers.even.Footer.ReferenceId,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public get Options(): SectionPropertiesOptions {
|
public get Options(): SectionPropertiesOptions {
|
||||||
|
11
src/file/file-properties.ts
Normal file
11
src/file/file-properties.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { IDocumentTemplate } from "../import-dotx";
|
||||||
|
|
||||||
|
export interface IFileProperties {
|
||||||
|
template?: IDocumentTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Needed because of: https://github.com/s-panferov/awesome-typescript-loader/issues/432
|
||||||
|
/**
|
||||||
|
* @ignore
|
||||||
|
*/
|
||||||
|
export const WORKAROUND = "";
|
325
src/file/file.ts
325
src/file/file.ts
@ -2,10 +2,17 @@ import { AppProperties } from "./app-properties/app-properties";
|
|||||||
import { ContentTypes } from "./content-types/content-types";
|
import { ContentTypes } from "./content-types/content-types";
|
||||||
import { CoreProperties, IPropertiesOptions } from "./core-properties";
|
import { CoreProperties, IPropertiesOptions } from "./core-properties";
|
||||||
import { Document } from "./document";
|
import { Document } from "./document";
|
||||||
import { FooterReferenceType, HeaderReference, HeaderReferenceType, SectionPropertiesOptions } from "./document/body/section-properties";
|
import {
|
||||||
import { FooterWrapper } from "./footer-wrapper";
|
FooterReferenceType,
|
||||||
|
HeaderReference,
|
||||||
|
HeaderReferenceType,
|
||||||
|
IHeaderFooterGroup,
|
||||||
|
SectionPropertiesOptions,
|
||||||
|
} from "./document/body/section-properties";
|
||||||
|
import { IFileProperties } from "./file-properties";
|
||||||
|
import { FooterWrapper, IDocumentFooter } from "./footer-wrapper";
|
||||||
import { FootNotes } from "./footnotes";
|
import { FootNotes } from "./footnotes";
|
||||||
import { HeaderWrapper } from "./header-wrapper";
|
import { HeaderWrapper, IDocumentHeader } from "./header-wrapper";
|
||||||
import { Image, Media } from "./media";
|
import { Image, Media } from "./media";
|
||||||
import { Numbering } from "./numbering";
|
import { Numbering } from "./numbering";
|
||||||
import { Bookmark, Hyperlink, Paragraph } from "./paragraph";
|
import { Bookmark, Hyperlink, Paragraph } from "./paragraph";
|
||||||
@ -18,33 +25,51 @@ import { Table } from "./table";
|
|||||||
import { TableOfContents } from "./table-of-contents";
|
import { TableOfContents } from "./table-of-contents";
|
||||||
|
|
||||||
export class File {
|
export class File {
|
||||||
|
private currentRelationshipId: number = 1;
|
||||||
|
|
||||||
private readonly document: Document;
|
private readonly document: Document;
|
||||||
private styles: Styles;
|
private readonly headers: IDocumentHeader[] = [];
|
||||||
|
private readonly footers: IDocumentFooter[] = [];
|
||||||
|
private readonly docRelationships: Relationships;
|
||||||
private readonly coreProperties: CoreProperties;
|
private readonly coreProperties: CoreProperties;
|
||||||
private readonly numbering: Numbering;
|
private readonly numbering: Numbering;
|
||||||
private readonly media: Media;
|
private readonly media: Media;
|
||||||
private readonly docRelationships: Relationships;
|
|
||||||
private readonly fileRelationships: Relationships;
|
private readonly fileRelationships: Relationships;
|
||||||
private readonly headerWrapper: HeaderWrapper[] = [];
|
|
||||||
private readonly footerWrapper: FooterWrapper[] = [];
|
|
||||||
private readonly footNotes: FootNotes;
|
private readonly footNotes: FootNotes;
|
||||||
private readonly settings: Settings;
|
private readonly settings: Settings;
|
||||||
|
|
||||||
private readonly contentTypes: ContentTypes;
|
private readonly contentTypes: ContentTypes;
|
||||||
private readonly appProperties: AppProperties;
|
private readonly appProperties: AppProperties;
|
||||||
|
private styles: Styles;
|
||||||
|
|
||||||
private currentRelationshipId: number = 1;
|
constructor(
|
||||||
|
options: IPropertiesOptions = {
|
||||||
constructor(options?: IPropertiesOptions, sectionPropertiesOptions?: SectionPropertiesOptions) {
|
|
||||||
if (!options) {
|
|
||||||
options = {
|
|
||||||
creator: "Un-named",
|
creator: "Un-named",
|
||||||
revision: "1",
|
revision: "1",
|
||||||
lastModifiedBy: "Un-named",
|
lastModifiedBy: "Un-named",
|
||||||
};
|
},
|
||||||
|
sectionPropertiesOptions: SectionPropertiesOptions = {},
|
||||||
|
fileProperties: IFileProperties = {},
|
||||||
|
) {
|
||||||
|
this.coreProperties = new CoreProperties(options);
|
||||||
|
this.numbering = new Numbering();
|
||||||
|
this.docRelationships = new Relationships();
|
||||||
|
this.media = new Media();
|
||||||
|
this.fileRelationships = new Relationships();
|
||||||
|
this.appProperties = new AppProperties();
|
||||||
|
this.footNotes = new FootNotes();
|
||||||
|
this.contentTypes = new ContentTypes();
|
||||||
|
|
||||||
|
if (fileProperties.template) {
|
||||||
|
this.currentRelationshipId = fileProperties.template.currentRelationshipId + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.externalStyles) {
|
// set up styles
|
||||||
|
if (fileProperties.template && options.externalStyles) {
|
||||||
|
throw Error("can not use both template and external styles");
|
||||||
|
}
|
||||||
|
if (fileProperties.template) {
|
||||||
|
this.styles = fileProperties.template.styles;
|
||||||
|
} else if (options.externalStyles) {
|
||||||
const stylesFactory = new ExternalStylesFactory();
|
const stylesFactory = new ExternalStylesFactory();
|
||||||
this.styles = stylesFactory.newInstance(options.externalStyles);
|
this.styles = stylesFactory.newInstance(options.externalStyles);
|
||||||
} else {
|
} else {
|
||||||
@ -52,61 +77,30 @@ export class File {
|
|||||||
this.styles = stylesFactory.newInstance();
|
this.styles = stylesFactory.newInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.coreProperties = new CoreProperties(options);
|
this.addDefaultRelationships();
|
||||||
this.numbering = new Numbering();
|
|
||||||
this.docRelationships = new Relationships();
|
|
||||||
this.docRelationships.createRelationship(
|
|
||||||
this.currentRelationshipId++,
|
|
||||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles",
|
|
||||||
"styles.xml",
|
|
||||||
);
|
|
||||||
this.docRelationships.createRelationship(
|
|
||||||
this.currentRelationshipId++,
|
|
||||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering",
|
|
||||||
"numbering.xml",
|
|
||||||
);
|
|
||||||
this.contentTypes = new ContentTypes();
|
|
||||||
|
|
||||||
this.docRelationships.createRelationship(
|
if (fileProperties.template && fileProperties.template.headers) {
|
||||||
this.currentRelationshipId++,
|
for (const templateHeader of fileProperties.template.headers) {
|
||||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes",
|
this.addHeaderToDocument(templateHeader.header, templateHeader.type);
|
||||||
"footnotes.xml",
|
|
||||||
);
|
|
||||||
this.media = new Media();
|
|
||||||
|
|
||||||
const header = this.createHeader();
|
|
||||||
const footer = this.createFooter();
|
|
||||||
|
|
||||||
this.fileRelationships = new Relationships();
|
|
||||||
this.fileRelationships.createRelationship(
|
|
||||||
1,
|
|
||||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument",
|
|
||||||
"word/document.xml",
|
|
||||||
);
|
|
||||||
this.fileRelationships.createRelationship(
|
|
||||||
2,
|
|
||||||
"http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties",
|
|
||||||
"docProps/core.xml",
|
|
||||||
);
|
|
||||||
this.fileRelationships.createRelationship(
|
|
||||||
3,
|
|
||||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties",
|
|
||||||
"docProps/app.xml",
|
|
||||||
);
|
|
||||||
this.appProperties = new AppProperties();
|
|
||||||
|
|
||||||
this.footNotes = new FootNotes();
|
|
||||||
if (!sectionPropertiesOptions) {
|
|
||||||
sectionPropertiesOptions = {
|
|
||||||
footerType: FooterReferenceType.DEFAULT,
|
|
||||||
headerType: HeaderReferenceType.DEFAULT,
|
|
||||||
headerId: header.Header.ReferenceId,
|
|
||||||
footerId: footer.Footer.ReferenceId,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
sectionPropertiesOptions.headerId = header.Header.ReferenceId;
|
|
||||||
sectionPropertiesOptions.footerId = footer.Footer.ReferenceId;
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
this.createHeader();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileProperties.template && fileProperties.template.footers) {
|
||||||
|
for (const templateFooter of fileProperties.template.footers) {
|
||||||
|
this.addFooterToDocument(templateFooter.footer, templateFooter.type);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.createFooter();
|
||||||
|
}
|
||||||
|
|
||||||
|
sectionPropertiesOptions = {
|
||||||
|
...sectionPropertiesOptions,
|
||||||
|
headers: this.groupHeaders(this.headers, sectionPropertiesOptions.headers),
|
||||||
|
footers: this.groupFooters(this.footers, sectionPropertiesOptions.footers),
|
||||||
|
};
|
||||||
|
|
||||||
this.document = new Document(sectionPropertiesOptions);
|
this.document = new Document(sectionPropertiesOptions);
|
||||||
this.settings = new Settings();
|
this.settings = new Settings();
|
||||||
}
|
}
|
||||||
@ -115,8 +109,9 @@ export class File {
|
|||||||
this.document.addTableOfContents(toc);
|
this.document.addTableOfContents(toc);
|
||||||
}
|
}
|
||||||
|
|
||||||
public addParagraph(paragraph: Paragraph): void {
|
public addParagraph(paragraph: Paragraph): File {
|
||||||
this.document.addParagraph(paragraph);
|
this.document.addParagraph(paragraph);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public createParagraph(text?: string): Paragraph {
|
public createParagraph(text?: string): Paragraph {
|
||||||
@ -179,25 +174,13 @@ export class File {
|
|||||||
|
|
||||||
public createHeader(): HeaderWrapper {
|
public createHeader(): HeaderWrapper {
|
||||||
const header = new HeaderWrapper(this.media, this.currentRelationshipId++);
|
const header = new HeaderWrapper(this.media, this.currentRelationshipId++);
|
||||||
this.headerWrapper.push(header);
|
this.addHeaderToDocument(header);
|
||||||
this.docRelationships.createRelationship(
|
|
||||||
header.Header.ReferenceId,
|
|
||||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/header",
|
|
||||||
`header${this.headerWrapper.length}.xml`,
|
|
||||||
);
|
|
||||||
this.contentTypes.addHeader(this.headerWrapper.length);
|
|
||||||
return header;
|
return header;
|
||||||
}
|
}
|
||||||
|
|
||||||
public createFooter(): FooterWrapper {
|
public createFooter(): FooterWrapper {
|
||||||
const footer = new FooterWrapper(this.media, this.currentRelationshipId++);
|
const footer = new FooterWrapper(this.media, this.currentRelationshipId++);
|
||||||
this.footerWrapper.push(footer);
|
this.addFooterToDocument(footer);
|
||||||
this.docRelationships.createRelationship(
|
|
||||||
footer.Footer.ReferenceId,
|
|
||||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer",
|
|
||||||
`footer${this.footerWrapper.length}.xml`,
|
|
||||||
);
|
|
||||||
this.contentTypes.addFooter(this.footerWrapper.length);
|
|
||||||
return footer;
|
return footer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,6 +197,152 @@ export class File {
|
|||||||
return headerWrapper;
|
return headerWrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getFooterByReferenceNumber(refId: number): FooterWrapper {
|
||||||
|
const entry = this.footers.map((item) => item.footer).find((h) => h.Footer.ReferenceId === refId);
|
||||||
|
if (entry) {
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
throw new Error(`There is no footer with given reference id ${refId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getHeaderByReferenceNumber(refId: number): HeaderWrapper {
|
||||||
|
const entry = this.headers.map((item) => item.header).find((h) => h.Header.ReferenceId === refId);
|
||||||
|
if (entry) {
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
throw new Error(`There is no header with given reference id ${refId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public verifyUpdateFields(): void {
|
||||||
|
if (this.document.getTablesOfContents().length) {
|
||||||
|
this.settings.addUpdateFields();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private addHeaderToDocument(header: HeaderWrapper, type: HeaderReferenceType = HeaderReferenceType.DEFAULT): void {
|
||||||
|
this.headers.push({ header, type });
|
||||||
|
this.docRelationships.createRelationship(
|
||||||
|
header.Header.ReferenceId,
|
||||||
|
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/header",
|
||||||
|
`header${this.headers.length}.xml`,
|
||||||
|
);
|
||||||
|
this.contentTypes.addHeader(this.headers.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
private addFooterToDocument(footer: FooterWrapper, type: FooterReferenceType = FooterReferenceType.DEFAULT): void {
|
||||||
|
this.footers.push({ footer, type });
|
||||||
|
this.docRelationships.createRelationship(
|
||||||
|
footer.Footer.ReferenceId,
|
||||||
|
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer",
|
||||||
|
`footer${this.footers.length}.xml`,
|
||||||
|
);
|
||||||
|
this.contentTypes.addFooter(this.footers.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
private addDefaultRelationships(): void {
|
||||||
|
this.fileRelationships.createRelationship(
|
||||||
|
1,
|
||||||
|
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument",
|
||||||
|
"word/document.xml",
|
||||||
|
);
|
||||||
|
this.fileRelationships.createRelationship(
|
||||||
|
2,
|
||||||
|
"http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties",
|
||||||
|
"docProps/core.xml",
|
||||||
|
);
|
||||||
|
this.fileRelationships.createRelationship(
|
||||||
|
3,
|
||||||
|
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties",
|
||||||
|
"docProps/app.xml",
|
||||||
|
);
|
||||||
|
|
||||||
|
this.docRelationships.createRelationship(
|
||||||
|
this.currentRelationshipId++,
|
||||||
|
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles",
|
||||||
|
"styles.xml",
|
||||||
|
);
|
||||||
|
this.docRelationships.createRelationship(
|
||||||
|
this.currentRelationshipId++,
|
||||||
|
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering",
|
||||||
|
"numbering.xml",
|
||||||
|
);
|
||||||
|
this.docRelationships.createRelationship(
|
||||||
|
this.currentRelationshipId++,
|
||||||
|
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes",
|
||||||
|
"footnotes.xml",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private groupHeaders(headers: IDocumentHeader[], group: IHeaderFooterGroup<HeaderWrapper> = {}): IHeaderFooterGroup<HeaderWrapper> {
|
||||||
|
let newGroup = group;
|
||||||
|
|
||||||
|
for (const header of headers) {
|
||||||
|
switch (header.type) {
|
||||||
|
case HeaderReferenceType.DEFAULT:
|
||||||
|
newGroup = {
|
||||||
|
...newGroup,
|
||||||
|
default: header.header,
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case HeaderReferenceType.FIRST:
|
||||||
|
newGroup = {
|
||||||
|
...newGroup,
|
||||||
|
first: header.header,
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case HeaderReferenceType.EVEN:
|
||||||
|
newGroup = {
|
||||||
|
...newGroup,
|
||||||
|
even: header.header,
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
newGroup = {
|
||||||
|
...newGroup,
|
||||||
|
default: header.header,
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
private groupFooters(footers: IDocumentFooter[], group: IHeaderFooterGroup<FooterWrapper> = {}): IHeaderFooterGroup<FooterWrapper> {
|
||||||
|
let newGroup = group;
|
||||||
|
|
||||||
|
for (const footer of footers) {
|
||||||
|
switch (footer.type) {
|
||||||
|
case FooterReferenceType.DEFAULT:
|
||||||
|
newGroup = {
|
||||||
|
...newGroup,
|
||||||
|
default: footer.footer,
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case FooterReferenceType.FIRST:
|
||||||
|
newGroup = {
|
||||||
|
...newGroup,
|
||||||
|
first: footer.footer,
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case FooterReferenceType.EVEN:
|
||||||
|
newGroup = {
|
||||||
|
...newGroup,
|
||||||
|
even: footer.footer,
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
newGroup = {
|
||||||
|
...newGroup,
|
||||||
|
default: footer.footer,
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newGroup;
|
||||||
|
}
|
||||||
|
|
||||||
public get Document(): Document {
|
public get Document(): Document {
|
||||||
return this.document;
|
return this.document;
|
||||||
}
|
}
|
||||||
@ -247,35 +376,19 @@ export class File {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public get Header(): HeaderWrapper {
|
public get Header(): HeaderWrapper {
|
||||||
return this.headerWrapper[0];
|
return this.headers[0].header;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get Headers(): HeaderWrapper[] {
|
public get Headers(): HeaderWrapper[] {
|
||||||
return this.headerWrapper;
|
return this.headers.map((item) => item.header);
|
||||||
}
|
|
||||||
|
|
||||||
public HeaderByRefNumber(refId: number): HeaderWrapper {
|
|
||||||
const entry = this.headerWrapper.find((h) => h.Header.ReferenceId === refId);
|
|
||||||
if (entry) {
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
throw new Error(`There is no header with given reference id ${refId}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public get Footer(): FooterWrapper {
|
public get Footer(): FooterWrapper {
|
||||||
return this.footerWrapper[0];
|
return this.footers[0].footer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get Footers(): FooterWrapper[] {
|
public get Footers(): FooterWrapper[] {
|
||||||
return this.footerWrapper;
|
return this.footers.map((item) => item.footer);
|
||||||
}
|
|
||||||
|
|
||||||
public FooterByRefNumber(refId: number): FooterWrapper {
|
|
||||||
const entry = this.footerWrapper.find((h) => h.Footer.ReferenceId === refId);
|
|
||||||
if (entry) {
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
throw new Error(`There is no footer with given reference id ${refId}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public get ContentTypes(): ContentTypes {
|
public get ContentTypes(): ContentTypes {
|
||||||
@ -293,10 +406,4 @@ export class File {
|
|||||||
public get Settings(): Settings {
|
public get Settings(): Settings {
|
||||||
return this.settings;
|
return this.settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
public verifyUpdateFields(): void {
|
|
||||||
if (this.document.getTablesOfContents().length) {
|
|
||||||
this.settings.addUpdateFields();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,23 @@
|
|||||||
import { XmlComponent } from "file/xml-components";
|
import { XmlComponent } from "file/xml-components";
|
||||||
|
|
||||||
|
import { FooterReferenceType } from "./document";
|
||||||
import { Footer } from "./footer/footer";
|
import { Footer } from "./footer/footer";
|
||||||
import { Image, Media } from "./media";
|
import { Image, IMediaData, Media } from "./media";
|
||||||
import { ImageParagraph, Paragraph } from "./paragraph";
|
import { ImageParagraph, Paragraph } from "./paragraph";
|
||||||
import { Relationships } from "./relationships";
|
import { Relationships } from "./relationships";
|
||||||
import { Table } from "./table";
|
import { Table } from "./table";
|
||||||
|
|
||||||
|
export interface IDocumentFooter {
|
||||||
|
footer: FooterWrapper;
|
||||||
|
type: FooterReferenceType;
|
||||||
|
}
|
||||||
|
|
||||||
export class FooterWrapper {
|
export class FooterWrapper {
|
||||||
private readonly footer: Footer;
|
private readonly footer: Footer;
|
||||||
private readonly relationships: Relationships;
|
private readonly relationships: Relationships;
|
||||||
|
|
||||||
constructor(private readonly media: Media, referenceId: number) {
|
constructor(private readonly media: Media, referenceId: number, initContent?: XmlComponent) {
|
||||||
this.footer = new Footer(referenceId);
|
this.footer = new Footer(referenceId, initContent);
|
||||||
this.relationships = new Relationships();
|
this.relationships = new Relationships();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,17 +39,33 @@ export class FooterWrapper {
|
|||||||
return this.footer.createTable(rows, cols);
|
return this.footer.createTable(rows, cols);
|
||||||
}
|
}
|
||||||
|
|
||||||
public addChildElement(childElement: XmlComponent | string): void {
|
public addChildElement(childElement: XmlComponent): void {
|
||||||
this.footer.addChildElement(childElement);
|
this.footer.addChildElement(childElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
public createImage(image: Buffer, width?: number, height?: number): void {
|
public addImageRelationship(image: Buffer, refId: number, width?: number, height?: number): IMediaData {
|
||||||
const mediaData = this.media.addMedia(image, this.relationships.RelationshipCount, width, height);
|
const mediaData = this.media.addMedia(image, refId, width, height);
|
||||||
this.relationships.createRelationship(
|
this.relationships.createRelationship(
|
||||||
mediaData.referenceId,
|
mediaData.referenceId,
|
||||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
|
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
|
||||||
`media/${mediaData.fileName}`,
|
`media/${mediaData.fileName}`,
|
||||||
);
|
);
|
||||||
|
return mediaData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public addHyperlinkRelationship(target: string, refId: number, targetMode?: "External" | undefined): void {
|
||||||
|
this.relationships.createRelationship(
|
||||||
|
refId,
|
||||||
|
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink",
|
||||||
|
target,
|
||||||
|
targetMode,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public createImage(image: Buffer | string | Uint8Array | ArrayBuffer, width?: number, height?: number): void {
|
||||||
|
// TODO
|
||||||
|
// tslint:disable-next-line:no-any
|
||||||
|
const mediaData = this.addImageRelationship(image as any, this.relationships.RelationshipCount, width, height);
|
||||||
this.addImage(new Image(new ImageParagraph(mediaData)));
|
this.addImage(new Image(new ImageParagraph(mediaData)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,4 +81,8 @@ export class FooterWrapper {
|
|||||||
public get Relationships(): Relationships {
|
public get Relationships(): Relationships {
|
||||||
return this.relationships;
|
return this.relationships;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get Media(): Media {
|
||||||
|
return this.media;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
// http://officeopenxml.com/WPfooters.php
|
// http://officeopenxml.com/WPfooters.php
|
||||||
import { XmlComponent } from "file/xml-components";
|
import { InitializableXmlComponent, XmlComponent } from "file/xml-components";
|
||||||
import { Paragraph } from "../paragraph";
|
import { Paragraph } from "../paragraph";
|
||||||
import { Table } from "../table";
|
import { Table } from "../table";
|
||||||
import { FooterAttributes } from "./footer-attributes";
|
import { FooterAttributes } from "./footer-attributes";
|
||||||
|
|
||||||
export class Footer extends XmlComponent {
|
export class Footer extends InitializableXmlComponent {
|
||||||
private readonly refId: number;
|
private readonly refId: number;
|
||||||
|
|
||||||
constructor(referenceNumber: number) {
|
constructor(referenceNumber: number, initContent?: XmlComponent) {
|
||||||
super("w:ftr");
|
super("w:ftr", initContent);
|
||||||
this.refId = referenceNumber;
|
this.refId = referenceNumber;
|
||||||
this.root.push(
|
this.root.push(
|
||||||
new FooterAttributes({
|
new FooterAttributes({
|
||||||
|
@ -1,16 +1,23 @@
|
|||||||
import { XmlComponent } from "file/xml-components";
|
import { XmlComponent } from "file/xml-components";
|
||||||
|
|
||||||
|
import { HeaderReferenceType } from "./document";
|
||||||
import { Header } from "./header/header";
|
import { Header } from "./header/header";
|
||||||
import { Image, Media } from "./media";
|
import { Image, IMediaData, Media } from "./media";
|
||||||
import { ImageParagraph, Paragraph } from "./paragraph";
|
import { ImageParagraph, Paragraph } from "./paragraph";
|
||||||
import { Relationships } from "./relationships";
|
import { Relationships } from "./relationships";
|
||||||
import { Table } from "./table";
|
import { Table } from "./table";
|
||||||
|
|
||||||
|
export interface IDocumentHeader {
|
||||||
|
header: HeaderWrapper;
|
||||||
|
type: HeaderReferenceType;
|
||||||
|
}
|
||||||
|
|
||||||
export class HeaderWrapper {
|
export class HeaderWrapper {
|
||||||
private readonly header: Header;
|
private readonly header: Header;
|
||||||
private readonly relationships: Relationships;
|
private readonly relationships: Relationships;
|
||||||
|
|
||||||
constructor(private readonly media: Media, referenceId: number) {
|
constructor(private readonly media: Media, referenceId: number, initContent?: XmlComponent) {
|
||||||
this.header = new Header(referenceId);
|
this.header = new Header(referenceId, initContent);
|
||||||
this.relationships = new Relationships();
|
this.relationships = new Relationships();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,13 +43,29 @@ export class HeaderWrapper {
|
|||||||
this.header.addChildElement(childElement);
|
this.header.addChildElement(childElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
public createImage(image: Buffer, width?: number, height?: number): void {
|
public addImageRelationship(image: Buffer, refId: number, width?: number, height?: number): IMediaData {
|
||||||
const mediaData = this.media.addMedia(image, this.relationships.RelationshipCount, width, height);
|
const mediaData = this.media.addMedia(image, refId, width, height);
|
||||||
this.relationships.createRelationship(
|
this.relationships.createRelationship(
|
||||||
mediaData.referenceId,
|
mediaData.referenceId,
|
||||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
|
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
|
||||||
`media/${mediaData.fileName}`,
|
`media/${mediaData.fileName}`,
|
||||||
);
|
);
|
||||||
|
return mediaData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public addHyperlinkRelationship(target: string, refId: number, targetMode?: "External" | undefined): void {
|
||||||
|
this.relationships.createRelationship(
|
||||||
|
refId,
|
||||||
|
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink",
|
||||||
|
target,
|
||||||
|
targetMode,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public createImage(image: Buffer | string | Uint8Array | ArrayBuffer, width?: number, height?: number): void {
|
||||||
|
// TODO
|
||||||
|
// tslint:disable-next-line:no-any
|
||||||
|
const mediaData = this.addImageRelationship(image as any, this.relationships.RelationshipCount, width, height);
|
||||||
this.addImage(new Image(new ImageParagraph(mediaData)));
|
this.addImage(new Image(new ImageParagraph(mediaData)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,4 +81,8 @@ export class HeaderWrapper {
|
|||||||
public get Relationships(): Relationships {
|
public get Relationships(): Relationships {
|
||||||
return this.relationships;
|
return this.relationships;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get Media(): Media {
|
||||||
|
return this.media;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,17 @@ export interface IHeaderAttributesProperties {
|
|||||||
dcmitype?: string;
|
dcmitype?: string;
|
||||||
xsi?: string;
|
xsi?: string;
|
||||||
type?: string;
|
type?: string;
|
||||||
|
cx?: string;
|
||||||
|
cx1?: string;
|
||||||
|
cx2?: string;
|
||||||
|
cx3?: string;
|
||||||
|
cx4?: string;
|
||||||
|
cx5?: string;
|
||||||
|
cx6?: string;
|
||||||
|
cx7?: string;
|
||||||
|
cx8?: string;
|
||||||
|
w16cid: string;
|
||||||
|
w16se: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class HeaderAttributes extends XmlAttributeComponent<IHeaderAttributesProperties> {
|
export class HeaderAttributes extends XmlAttributeComponent<IHeaderAttributesProperties> {
|
||||||
@ -49,5 +60,16 @@ export class HeaderAttributes extends XmlAttributeComponent<IHeaderAttributesPro
|
|||||||
dcmitype: "xmlns:dcmitype",
|
dcmitype: "xmlns:dcmitype",
|
||||||
xsi: "xmlns:xsi",
|
xsi: "xmlns:xsi",
|
||||||
type: "xsi:type",
|
type: "xsi:type",
|
||||||
|
cx: "xmlns:cx",
|
||||||
|
cx1: "xmlns:cx1",
|
||||||
|
cx2: "xmlns:cx2",
|
||||||
|
cx3: "xmlns:cx3",
|
||||||
|
cx4: "xmlns:cx4",
|
||||||
|
cx5: "xmlns:cx5",
|
||||||
|
cx6: "xmlns:cx6",
|
||||||
|
cx7: "xmlns:cx7",
|
||||||
|
cx8: "xmlns:cx8",
|
||||||
|
w16cid: "xmlns:w16cid",
|
||||||
|
w16se: "xmlns:w16se",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
// http://officeopenxml.com/WPheaders.php
|
// http://officeopenxml.com/WPheaders.php
|
||||||
import { XmlComponent } from "file/xml-components";
|
import { InitializableXmlComponent, XmlComponent } from "file/xml-components";
|
||||||
import { Paragraph } from "../paragraph";
|
import { Paragraph } from "../paragraph";
|
||||||
import { Table } from "../table";
|
import { Table } from "../table";
|
||||||
import { HeaderAttributes } from "./header-attributes";
|
import { HeaderAttributes } from "./header-attributes";
|
||||||
|
|
||||||
export class Header extends XmlComponent {
|
export class Header extends InitializableXmlComponent {
|
||||||
private readonly refId: number;
|
private readonly refId: number;
|
||||||
|
|
||||||
constructor(referenceNumber: number) {
|
constructor(referenceNumber: number, initContent?: XmlComponent) {
|
||||||
super("w:hdr");
|
super("w:hdr", initContent);
|
||||||
|
|
||||||
this.refId = referenceNumber;
|
this.refId = referenceNumber;
|
||||||
|
|
||||||
this.root.push(
|
this.root.push(
|
||||||
new HeaderAttributes({
|
new HeaderAttributes({
|
||||||
wpc: "http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas",
|
wpc: "http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas",
|
||||||
@ -29,6 +30,17 @@ export class Header extends XmlComponent {
|
|||||||
wpi: "http://schemas.microsoft.com/office/word/2010/wordprocessingInk",
|
wpi: "http://schemas.microsoft.com/office/word/2010/wordprocessingInk",
|
||||||
wne: "http://schemas.microsoft.com/office/word/2006/wordml",
|
wne: "http://schemas.microsoft.com/office/word/2006/wordml",
|
||||||
wps: "http://schemas.microsoft.com/office/word/2010/wordprocessingShape",
|
wps: "http://schemas.microsoft.com/office/word/2010/wordprocessingShape",
|
||||||
|
cx: "http://schemas.microsoft.com/office/drawing/2014/chartex",
|
||||||
|
cx1: "http://schemas.microsoft.com/office/drawing/2015/9/8/chartex",
|
||||||
|
cx2: "http://schemas.microsoft.com/office/drawing/2015/10/21/chartex",
|
||||||
|
cx3: "http://schemas.microsoft.com/office/drawing/2016/5/9/chartex",
|
||||||
|
cx4: "http://schemas.microsoft.com/office/drawing/2016/5/10/chartex",
|
||||||
|
cx5: "http://schemas.microsoft.com/office/drawing/2016/5/11/chartex",
|
||||||
|
cx6: "http://schemas.microsoft.com/office/drawing/2016/5/12/chartex",
|
||||||
|
cx7: "http://schemas.microsoft.com/office/drawing/2016/5/13/chartex",
|
||||||
|
cx8: "http://schemas.microsoft.com/office/drawing/2016/5/14/chartex",
|
||||||
|
w16cid: "http://schemas.microsoft.com/office/word/2016/wordml/cid",
|
||||||
|
w16se: "http://schemas.microsoft.com/office/word/2015/wordml/symex",
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
export * from "./paragraph";
|
export * from "./paragraph";
|
||||||
export * from "./table";
|
export * from "./table";
|
||||||
export * from "./file";
|
export * from "./file";
|
||||||
|
export * from "./file-properties";
|
||||||
export * from "./numbering";
|
export * from "./numbering";
|
||||||
export * from "./media";
|
export * from "./media";
|
||||||
export * from "./drawing";
|
export * from "./drawing";
|
||||||
|
@ -87,7 +87,7 @@ export class Media {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const imageData = {
|
const imageData = {
|
||||||
referenceId: this.map.size + relationshipsCount + 1,
|
referenceId: relationshipsCount,
|
||||||
stream: data,
|
stream: data,
|
||||||
path: filePath,
|
path: filePath,
|
||||||
fileName: key,
|
fileName: key,
|
||||||
|
@ -71,7 +71,7 @@ export class Numbering extends XmlComponent {
|
|||||||
return num;
|
return num;
|
||||||
}
|
}
|
||||||
|
|
||||||
public prepForXml(): IXmlableObject {
|
public prepForXml(): IXmlableObject | undefined {
|
||||||
this.abstractNumbering.forEach((x) => this.root.push(x));
|
this.abstractNumbering.forEach((x) => this.root.push(x));
|
||||||
this.concreteNumbering.forEach((x) => this.root.push(x));
|
this.concreteNumbering.forEach((x) => this.root.push(x));
|
||||||
return super.prepForXml();
|
return super.prepForXml();
|
||||||
|
@ -61,8 +61,6 @@ describe("External styles factory", () => {
|
|||||||
it("should parse other child elements of w:styles", () => {
|
it("should parse other child elements of w:styles", () => {
|
||||||
// tslint:disable-next-line:no-any
|
// tslint:disable-next-line:no-any
|
||||||
const importedStyle = new ExternalStylesFactory().newInstance(externalStyles) as any;
|
const importedStyle = new ExternalStylesFactory().newInstance(externalStyles) as any;
|
||||||
|
|
||||||
expect(importedStyle.root.length).to.equal(5);
|
|
||||||
expect(importedStyle.root[1]).to.eql({
|
expect(importedStyle.root[1]).to.eql({
|
||||||
deleted: false,
|
deleted: false,
|
||||||
root: [
|
root: [
|
||||||
|
@ -21,8 +21,10 @@ export class TableCellMargin extends XmlComponent {
|
|||||||
super("w:tblCellMar");
|
super("w:tblCellMar");
|
||||||
}
|
}
|
||||||
|
|
||||||
public prepForXml(): IXmlableObject {
|
public prepForXml(): IXmlableObject | undefined {
|
||||||
return this.root.length > 0 ? super.prepForXml() : "";
|
if (this.root.length > 0) {
|
||||||
|
return super.prepForXml();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public addTopMargin(value: number, type: WidthType = WidthType.DXA): void {
|
public addTopMargin(value: number, type: WidthType = WidthType.DXA): void {
|
||||||
|
@ -8,8 +8,7 @@ describe("TableCellBorders", () => {
|
|||||||
describe("#prepForXml", () => {
|
describe("#prepForXml", () => {
|
||||||
it("should not add empty borders element if there are no borders defined", () => {
|
it("should not add empty borders element if there are no borders defined", () => {
|
||||||
const tb = new TableCellBorders();
|
const tb = new TableCellBorders();
|
||||||
const tree = new Formatter().format(tb);
|
expect(() => new Formatter().format(tb)).to.throw();
|
||||||
expect(tree).to.deep.equal("");
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -29,8 +29,10 @@ export class TableCellBorders extends XmlComponent {
|
|||||||
super("w:tcBorders");
|
super("w:tcBorders");
|
||||||
}
|
}
|
||||||
|
|
||||||
public prepForXml(): IXmlableObject {
|
public prepForXml(): IXmlableObject | undefined {
|
||||||
return this.root.length > 0 ? super.prepForXml() : "";
|
if (this.root.length > 0) {
|
||||||
|
return super.prepForXml();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public addTopBorder(style: BorderStyle, size: number, color: string): TableCellBorders {
|
public addTopBorder(style: BorderStyle, size: number, color: string): TableCellBorders {
|
||||||
|
@ -145,9 +145,13 @@ export class TableCell extends XmlComponent {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public prepForXml(): IXmlableObject {
|
public prepForXml(): IXmlableObject | undefined {
|
||||||
// Cells must end with a paragraph
|
// Cells must end with a paragraph
|
||||||
const retval = super.prepForXml();
|
const retval = super.prepForXml();
|
||||||
|
if (!retval) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
const content = retval["w:tc"];
|
const content = retval["w:tc"];
|
||||||
if (!content[content.length - 1]["w:p"]) {
|
if (!content[content.length - 1]["w:p"]) {
|
||||||
content.push(new Paragraph().prepForXml());
|
content.push(new Paragraph().prepForXml());
|
||||||
|
@ -8,7 +8,7 @@ export abstract class BaseXmlComponent {
|
|||||||
this.rootKey = rootKey;
|
this.rootKey = rootKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract prepForXml(): IXmlableObject;
|
public abstract prepForXml(): IXmlableObject | undefined;
|
||||||
|
|
||||||
public get IsDeleted(): boolean {
|
public get IsDeleted(): boolean {
|
||||||
return this.deleted;
|
return this.deleted;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/* tslint:disable */
|
// tslint:disable:no-any
|
||||||
import { XmlComponent, IXmlableObject } from ".";
|
|
||||||
import * as fastXmlParser from "fast-xml-parser";
|
import * as fastXmlParser from "fast-xml-parser";
|
||||||
import { flatMap } from "lodash";
|
import { flatMap } from "lodash";
|
||||||
|
import { IXmlableObject, XmlComponent } from ".";
|
||||||
|
|
||||||
export const parseOptions = {
|
export const parseOptions = {
|
||||||
ignoreAttributes: false,
|
ignoreAttributes: false,
|
||||||
@ -54,8 +54,27 @@ export function convertToXmlComponent(elementName: string, element: any): Import
|
|||||||
* Represents imported xml component from xml file.
|
* Represents imported xml component from xml file.
|
||||||
*/
|
*/
|
||||||
export class ImportedXmlComponent extends XmlComponent {
|
export class ImportedXmlComponent extends XmlComponent {
|
||||||
private _attr: any;
|
/**
|
||||||
|
* Converts the xml string to a XmlComponent tree.
|
||||||
|
*
|
||||||
|
* @param importedContent xml content of the imported component
|
||||||
|
*/
|
||||||
|
public static fromXmlString(importedContent: string): ImportedXmlComponent {
|
||||||
|
const imported = fastXmlParser.parse(importedContent, parseOptions);
|
||||||
|
const elementName = Object.keys(imported)[0];
|
||||||
|
|
||||||
|
const converted = convertToXmlComponent(elementName, imported[elementName]);
|
||||||
|
|
||||||
|
if (Array.isArray(converted) && converted.length > 1) {
|
||||||
|
throw new Error("Invalid conversion, input must be one element.");
|
||||||
|
}
|
||||||
|
return Array.isArray(converted) ? converted[0] : converted;
|
||||||
|
}
|
||||||
|
|
||||||
|
// tslint:disable-next-line:variable-name
|
||||||
|
private readonly _attr: any;
|
||||||
|
|
||||||
|
// tslint:disable-next-line:variable-name
|
||||||
constructor(rootKey: string, _attr?: any) {
|
constructor(rootKey: string, _attr?: any) {
|
||||||
super(rootKey);
|
super(rootKey);
|
||||||
if (_attr) {
|
if (_attr) {
|
||||||
@ -89,8 +108,12 @@ export class ImportedXmlComponent extends XmlComponent {
|
|||||||
* ]
|
* ]
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
prepForXml(): IXmlableObject {
|
public prepForXml(): IXmlableObject | undefined {
|
||||||
const result = super.prepForXml();
|
const result = super.prepForXml();
|
||||||
|
if (!result) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
if (!!this._attr) {
|
if (!!this._attr) {
|
||||||
if (!Array.isArray(result[this.rootKey])) {
|
if (!Array.isArray(result[this.rootKey])) {
|
||||||
result[this.rootKey] = [result[this.rootKey]];
|
result[this.rootKey] = [result[this.rootKey]];
|
||||||
@ -100,33 +123,17 @@ export class ImportedXmlComponent extends XmlComponent {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
push(xmlComponent: XmlComponent) {
|
public push(xmlComponent: XmlComponent): void {
|
||||||
this.root.push(xmlComponent);
|
this.root.push(xmlComponent);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts the xml string to a XmlComponent tree.
|
|
||||||
*
|
|
||||||
* @param importedContent xml content of the imported component
|
|
||||||
*/
|
|
||||||
static fromXmlString(importedContent: string): ImportedXmlComponent {
|
|
||||||
const imported = fastXmlParser.parse(importedContent, parseOptions);
|
|
||||||
const elementName = Object.keys(imported)[0];
|
|
||||||
|
|
||||||
const converted = convertToXmlComponent(elementName, imported[elementName]);
|
|
||||||
|
|
||||||
if (Array.isArray(converted) && converted.length > 1) {
|
|
||||||
throw new Error("Invalid conversion, input must be one element.");
|
|
||||||
}
|
|
||||||
return Array.isArray(converted) ? converted[0] : converted;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used for the attributes of root element that is being imported.
|
* Used for the attributes of root element that is being imported.
|
||||||
*/
|
*/
|
||||||
export class ImportedRootElementAttributes extends XmlComponent {
|
export class ImportedRootElementAttributes extends XmlComponent {
|
||||||
constructor(private _attr: any) {
|
// tslint:disable-next-line:variable-name
|
||||||
|
constructor(private readonly _attr: any) {
|
||||||
super("");
|
super("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,3 +3,4 @@ export * from "./attributes";
|
|||||||
export * from "./default-attributes";
|
export * from "./default-attributes";
|
||||||
export * from "./imported-xml-component";
|
export * from "./imported-xml-component";
|
||||||
export * from "./xmlable-object";
|
export * from "./xmlable-object";
|
||||||
|
export * from "./initializable-xml-component";
|
||||||
|
11
src/file/xml-components/initializable-xml-component.ts
Normal file
11
src/file/xml-components/initializable-xml-component.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { XmlComponent } from "file/xml-components";
|
||||||
|
|
||||||
|
export abstract class InitializableXmlComponent extends XmlComponent {
|
||||||
|
constructor(rootKey: string, initComponent?: InitializableXmlComponent) {
|
||||||
|
super(rootKey);
|
||||||
|
|
||||||
|
if (initComponent) {
|
||||||
|
this.root = initComponent.root;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -26,6 +26,11 @@ describe("XmlComponent", () => {
|
|||||||
xmlComponent.addChildElement(child);
|
xmlComponent.addChildElement(child);
|
||||||
|
|
||||||
const xml = xmlComponent.prepForXml();
|
const xml = xmlComponent.prepForXml();
|
||||||
|
|
||||||
|
if (!xml) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
assert.equal(xml["w:test"].length, 0);
|
assert.equal(xml["w:test"].length, 0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -7,10 +7,10 @@ export abstract class XmlComponent extends BaseXmlComponent {
|
|||||||
|
|
||||||
constructor(rootKey: string) {
|
constructor(rootKey: string) {
|
||||||
super(rootKey);
|
super(rootKey);
|
||||||
this.root = new Array<BaseXmlComponent>();
|
this.root = new Array<BaseXmlComponent | string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public prepForXml(): IXmlableObject {
|
public prepForXml(): IXmlableObject | undefined {
|
||||||
const children = this.root
|
const children = this.root
|
||||||
.filter((c) => {
|
.filter((c) => {
|
||||||
if (c instanceof BaseXmlComponent) {
|
if (c instanceof BaseXmlComponent) {
|
||||||
@ -24,7 +24,7 @@ export abstract class XmlComponent extends BaseXmlComponent {
|
|||||||
}
|
}
|
||||||
return comp;
|
return comp;
|
||||||
})
|
})
|
||||||
.filter((comp) => comp); // Exclude null, undefined, and empty strings
|
.filter((comp) => comp !== undefined); // Exclude undefined
|
||||||
return {
|
return {
|
||||||
[this.rootKey]: children,
|
[this.rootKey]: children,
|
||||||
};
|
};
|
||||||
|
209
src/import-dotx/import-dotx.ts
Normal file
209
src/import-dotx/import-dotx.ts
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
import * as fastXmlParser from "fast-xml-parser";
|
||||||
|
import * as JSZip from "jszip";
|
||||||
|
|
||||||
|
import { FooterReferenceType } from "file/document/body/section-properties/footer-reference";
|
||||||
|
import { HeaderReferenceType } from "file/document/body/section-properties/header-reference";
|
||||||
|
import { FooterWrapper, IDocumentFooter } from "file/footer-wrapper";
|
||||||
|
import { HeaderWrapper, IDocumentHeader } from "file/header-wrapper";
|
||||||
|
import { Media } from "file/media";
|
||||||
|
import { Styles } from "file/styles";
|
||||||
|
import { ExternalStylesFactory } from "file/styles/external-styles-factory";
|
||||||
|
import { convertToXmlComponent, ImportedXmlComponent, parseOptions } from "file/xml-components";
|
||||||
|
|
||||||
|
const importParseOptions = {
|
||||||
|
...parseOptions,
|
||||||
|
textNodeName: "",
|
||||||
|
trimValues: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
const schemeToType = {
|
||||||
|
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/header": "header",
|
||||||
|
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer": "footer",
|
||||||
|
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image": "image",
|
||||||
|
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink": "hyperlink",
|
||||||
|
};
|
||||||
|
|
||||||
|
interface IDocumentRefs {
|
||||||
|
headers: Array<{ id: number; type: HeaderReferenceType }>;
|
||||||
|
footers: Array<{ id: number; type: FooterReferenceType }>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IRelationshipFileInfo {
|
||||||
|
id: number;
|
||||||
|
target: string;
|
||||||
|
type: "header" | "footer" | "image" | "hyperlink";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Document Template
|
||||||
|
// https://fileinfo.com/extension/dotx
|
||||||
|
export interface IDocumentTemplate {
|
||||||
|
currentRelationshipId: number;
|
||||||
|
headers: IDocumentHeader[];
|
||||||
|
footers: IDocumentFooter[];
|
||||||
|
styles: Styles;
|
||||||
|
titlePageIsDefined: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ImportDotx {
|
||||||
|
private currentRelationshipId: number;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.currentRelationshipId = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async extract(data: Buffer): Promise<IDocumentTemplate> {
|
||||||
|
const zipContent = await JSZip.loadAsync(data);
|
||||||
|
|
||||||
|
const stylesContent = await zipContent.files["word/styles.xml"].async("text");
|
||||||
|
const stylesFactory = new ExternalStylesFactory();
|
||||||
|
const styles = stylesFactory.newInstance(stylesContent);
|
||||||
|
|
||||||
|
const documentContent = zipContent.files["word/document.xml"];
|
||||||
|
const documentRefs: IDocumentRefs = this.extractDocumentRefs(await documentContent.async("text"));
|
||||||
|
const titlePageIsDefined = this.titlePageIsDefined(await documentContent.async("text"));
|
||||||
|
|
||||||
|
const relationshipContent = zipContent.files["word/_rels/document.xml.rels"];
|
||||||
|
const documentRelationships: IRelationshipFileInfo[] = this.findReferenceFiles(await relationshipContent.async("text"));
|
||||||
|
|
||||||
|
const media = new Media();
|
||||||
|
|
||||||
|
const headers: IDocumentHeader[] = [];
|
||||||
|
for (const headerRef of documentRefs.headers) {
|
||||||
|
const headerKey = "w:hdr";
|
||||||
|
const relationshipFileInfo = documentRelationships.find((rel) => rel.id === headerRef.id);
|
||||||
|
if (!relationshipFileInfo) {
|
||||||
|
throw new Error(`Can not find target file for id ${headerRef.id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const xmlData = await zipContent.files[`word/${relationshipFileInfo.target}`].async("text");
|
||||||
|
const xmlObj = fastXmlParser.parse(xmlData, importParseOptions);
|
||||||
|
|
||||||
|
const importedComp = convertToXmlComponent(headerKey, xmlObj[headerKey]) as ImportedXmlComponent;
|
||||||
|
|
||||||
|
const header = new HeaderWrapper(media, this.currentRelationshipId++, importedComp);
|
||||||
|
await this.addRelationToWrapper(relationshipFileInfo, zipContent, header);
|
||||||
|
headers.push({ type: headerRef.type, header });
|
||||||
|
}
|
||||||
|
|
||||||
|
const footers: IDocumentFooter[] = [];
|
||||||
|
for (const footerRef of documentRefs.footers) {
|
||||||
|
const footerKey = "w:ftr";
|
||||||
|
const relationshipFileInfo = documentRelationships.find((rel) => rel.id === footerRef.id);
|
||||||
|
if (!relationshipFileInfo) {
|
||||||
|
throw new Error(`Can not find target file for id ${footerRef.id}`);
|
||||||
|
}
|
||||||
|
const xmlData = await zipContent.files[`word/${relationshipFileInfo.target}`].async("text");
|
||||||
|
const xmlObj = fastXmlParser.parse(xmlData, importParseOptions);
|
||||||
|
const importedComp = convertToXmlComponent(footerKey, xmlObj[footerKey]) as ImportedXmlComponent;
|
||||||
|
|
||||||
|
const footer = new FooterWrapper(media, this.currentRelationshipId++, importedComp);
|
||||||
|
await this.addRelationToWrapper(relationshipFileInfo, zipContent, footer);
|
||||||
|
footers.push({ type: footerRef.type, footer });
|
||||||
|
}
|
||||||
|
|
||||||
|
const templateDocument: IDocumentTemplate = {
|
||||||
|
headers,
|
||||||
|
footers,
|
||||||
|
currentRelationshipId: this.currentRelationshipId,
|
||||||
|
styles,
|
||||||
|
titlePageIsDefined,
|
||||||
|
};
|
||||||
|
return templateDocument;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async addRelationToWrapper(
|
||||||
|
relationhipFile: IRelationshipFileInfo,
|
||||||
|
zipContent: JSZip,
|
||||||
|
wrapper: HeaderWrapper | FooterWrapper,
|
||||||
|
): Promise<void> {
|
||||||
|
let wrapperImagesReferences: IRelationshipFileInfo[] = [];
|
||||||
|
let hyperLinkReferences: IRelationshipFileInfo[] = [];
|
||||||
|
const refFile = zipContent.files[`word/_rels/${relationhipFile.target}.rels`];
|
||||||
|
if (refFile) {
|
||||||
|
const xmlRef = await refFile.async("text");
|
||||||
|
wrapperImagesReferences = this.findReferenceFiles(xmlRef).filter((r) => r.type === "image");
|
||||||
|
hyperLinkReferences = this.findReferenceFiles(xmlRef).filter((r) => r.type === "hyperlink");
|
||||||
|
}
|
||||||
|
for (const r of wrapperImagesReferences) {
|
||||||
|
const buffer = await zipContent.files[`word/${r.target}`].async("nodebuffer");
|
||||||
|
wrapper.addImageRelationship(buffer, r.id);
|
||||||
|
}
|
||||||
|
for (const r of hyperLinkReferences) {
|
||||||
|
wrapper.addHyperlinkRelationship(r.target, r.id, "External");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public findReferenceFiles(xmlData: string): IRelationshipFileInfo[] {
|
||||||
|
const xmlObj = fastXmlParser.parse(xmlData, importParseOptions);
|
||||||
|
const relationshipXmlArray = Array.isArray(xmlObj.Relationships.Relationship)
|
||||||
|
? xmlObj.Relationships.Relationship
|
||||||
|
: [xmlObj.Relationships.Relationship];
|
||||||
|
const relationships: IRelationshipFileInfo[] = relationshipXmlArray
|
||||||
|
.map((item) => {
|
||||||
|
return {
|
||||||
|
id: this.parseRefId(item._attr.Id),
|
||||||
|
type: schemeToType[item._attr.Type],
|
||||||
|
target: item._attr.Target,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.filter((item) => item.type !== null);
|
||||||
|
return relationships;
|
||||||
|
}
|
||||||
|
|
||||||
|
public extractDocumentRefs(xmlData: string): IDocumentRefs {
|
||||||
|
interface IAttributedXML {
|
||||||
|
_attr: object;
|
||||||
|
}
|
||||||
|
const xmlObj = fastXmlParser.parse(xmlData, importParseOptions);
|
||||||
|
const sectionProp = xmlObj["w:document"]["w:body"]["w:sectPr"];
|
||||||
|
|
||||||
|
const headerProps: undefined | IAttributedXML | IAttributedXML[] = sectionProp["w:headerReference"];
|
||||||
|
let headersXmlArray: IAttributedXML[];
|
||||||
|
if (headerProps === undefined) {
|
||||||
|
headersXmlArray = [];
|
||||||
|
} else if (Array.isArray(headerProps)) {
|
||||||
|
headersXmlArray = headerProps;
|
||||||
|
} else {
|
||||||
|
headersXmlArray = [headerProps];
|
||||||
|
}
|
||||||
|
const headers = headersXmlArray.map((item) => {
|
||||||
|
return {
|
||||||
|
type: item._attr["w:type"],
|
||||||
|
id: this.parseRefId(item._attr["r:id"]),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const footerProps: undefined | IAttributedXML | IAttributedXML[] = sectionProp["w:footerReference"];
|
||||||
|
let footersXmlArray: IAttributedXML[];
|
||||||
|
if (footerProps === undefined) {
|
||||||
|
footersXmlArray = [];
|
||||||
|
} else if (Array.isArray(footerProps)) {
|
||||||
|
footersXmlArray = footerProps;
|
||||||
|
} else {
|
||||||
|
footersXmlArray = [footerProps];
|
||||||
|
}
|
||||||
|
|
||||||
|
const footers = footersXmlArray.map((item) => {
|
||||||
|
return {
|
||||||
|
type: item._attr["w:type"],
|
||||||
|
id: this.parseRefId(item._attr["r:id"]),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return { headers, footers };
|
||||||
|
}
|
||||||
|
|
||||||
|
public titlePageIsDefined(xmlData: string): boolean {
|
||||||
|
const xmlObj = fastXmlParser.parse(xmlData, importParseOptions);
|
||||||
|
const sectionProp = xmlObj["w:document"]["w:body"]["w:sectPr"];
|
||||||
|
return sectionProp["w:titlePg"] !== undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
public parseRefId(str: string): number {
|
||||||
|
const match = /^rId(\d+)$/.exec(str);
|
||||||
|
if (match === null) {
|
||||||
|
throw new Error("Invalid ref id");
|
||||||
|
}
|
||||||
|
return parseInt(match[1], 10);
|
||||||
|
}
|
||||||
|
}
|
1
src/import-dotx/index.ts
Normal file
1
src/import-dotx/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from "./import-dotx";
|
@ -3,3 +3,4 @@
|
|||||||
export { File as Document } from "./file";
|
export { File as Document } from "./file";
|
||||||
export * from "./file";
|
export * from "./file";
|
||||||
export * from "./export";
|
export * from "./export";
|
||||||
|
export * from "./import-dotx";
|
||||||
|
Reference in New Issue
Block a user