diff --git a/MyDocument.docx b/MyDocument.docx
index 3bc32bccfa..1cfaaf5a6a 100644
Binary files a/MyDocument.docx and b/MyDocument.docx differ
diff --git a/demo/importDemo.ts b/demo/demo27.ts
similarity index 52%
rename from demo/importDemo.ts
rename to demo/demo27.ts
index 42fcf2a1ea..1d91734203 100644
--- a/demo/importDemo.ts
+++ b/demo/demo27.ts
@@ -1,26 +1,25 @@
import { Document, Packer, Paragraph, ImportDocx } from "../build";
import * as fs from "fs";
-console.log(process.cwd());
-
let importDocx = new ImportDocx();
-fs.readFile("./src/importDocx/simple.dotx", (err, data) => {
+const filePath = "./demo/dotx/template.dotx";
+fs.readFile(filePath, (err, data) => {
if (err) {
- console.log(err);
+ console.error(`failed to read file ${filePath}.`);
}
else {
- importDocx.read(data).then(xmlComp => {
- console.log(xmlComp);
- const doc = new Document({templateHeader : xmlComp});
- // const doc = new Document();
+ importDocx.extract(data).then(templateDocument => {
+ let options = {};
+ options['templateDocument'] = templateDocument;
+
+ const doc = new Document(options);
const paragraph = new Paragraph("Hello World");
doc.addParagraph(paragraph);
- // console.log(JSON.stringify(xmlComp, null, 2));
const packer = new Packer();
-
packer.toBuffer(doc).then((buffer) => {
fs.writeFileSync("MyDocument.docx", buffer);
+ console.log('done. open MyDocument.docx');
});
});
diff --git a/demo/dotx/simple.dotx b/demo/dotx/simple.dotx
new file mode 100644
index 0000000000..8ad9309eb2
Binary files /dev/null and b/demo/dotx/simple.dotx differ
diff --git a/demo/dotx/simple2.dotx b/demo/dotx/simple2.dotx
new file mode 100644
index 0000000000..13ab583d96
Binary files /dev/null and b/demo/dotx/simple2.dotx differ
diff --git a/src/importDocx/template.dotx b/demo/dotx/template.dotx
similarity index 100%
rename from src/importDocx/template.dotx
rename to demo/dotx/template.dotx
diff --git a/demo/importDemo.js b/demo/importDemo.js
deleted file mode 100644
index c40eb0a6ae..0000000000
--- a/demo/importDemo.js
+++ /dev/null
@@ -1,13 +0,0 @@
-"use strict";
-exports.__esModule = true;
-var build_1 = require("../build");
-var fs = require("fs");
-var importDocx = new build_1.ImportDocx();
-fs.readFile("./src/import/template.dotx", function (err, data) {
- if (err) {
- console.log(err);
- }
- else {
- importDocx.read(data);
- }
-});
diff --git a/src/export/packer/next-compiler.ts b/src/export/packer/next-compiler.ts
index f06c09e196..b1b80682c1 100644
--- a/src/export/packer/next-compiler.ts
+++ b/src/export/packer/next-compiler.ts
@@ -53,10 +53,21 @@ export class Compiler {
}
}
+
for (const data of file.Media.Array) {
const mediaData = data.stream;
zip.file(`word/media/${data.fileName}`, mediaData);
}
+ for (let header of file.Headers) {
+ for (const data of header.media.Array) {
+ zip.file(`word/media/${data.fileName}`, data.stream);
+ }
+ }
+ for (let footer of file.Footers) {
+ for (const data of footer.media.Array) {
+ zip.file(`word/media/${data.fileName}`, data.stream);
+ }
+ }
return zip;
}
@@ -122,4 +133,13 @@ export class Compiler {
},
};
}
+
+
+ /* By default docx collapse empty tags. -> . this function mimic it
+ so comparing (diff) original docx file and the library output is easier */
+ collapseEmptyTags(xmlData : string) : string {
+ const regEx = /<(([^ <>]+)[^<>]*)><\/\2>/g;
+ let collapsed = xmlData.replace(regEx, '<$1/>');
+ return collapsed;
+ }
}
diff --git a/src/file/core-properties/properties.ts b/src/file/core-properties/properties.ts
index a5a239635d..ce51fe2029 100644
--- a/src/file/core-properties/properties.ts
+++ b/src/file/core-properties/properties.ts
@@ -2,6 +2,8 @@ import { XmlComponent } from "file/xml-components";
import { DocumentAttributes } from "../document/document-attributes";
import { Created, Creator, Description, Keywords, LastModifiedBy, Modified, Revision, Subject, Title } from "./components";
+import { TemplateDocument } from 'importDocx/importDocx'
+
export interface IPropertiesOptions {
title?: string;
subject?: string;
@@ -12,7 +14,7 @@ export interface IPropertiesOptions {
revision?: string;
externalStyles?: string;
- templateHeader? : XmlComponent;
+ templateDocument? : TemplateDocument;
}
export class CoreProperties extends XmlComponent {
diff --git a/src/file/document/body/section-properties/section-properties.spec.ts b/src/file/document/body/section-properties/section-properties.spec.ts
index e80facb635..0bc2ac24c7 100644
--- a/src/file/document/body/section-properties/section-properties.spec.ts
+++ b/src/file/document/body/section-properties/section-properties.spec.ts
@@ -1,7 +1,7 @@
import { expect } from "chai";
import { Formatter } from "../../../../export/formatter";
-import { FooterReferenceType, PageBorderOffsetFrom, PageNumberFormat } from "./";
+import { PageBorderOffsetFrom, PageNumberFormat, FooterReferenceType, HeaderReferenceType } from "./";
import { SectionProperties } from "./section-properties";
describe("SectionProperties", () => {
@@ -19,9 +19,8 @@ describe("SectionProperties", () => {
gutter: 0,
space: 708,
linePitch: 360,
- headerId: 100,
- footerId: 200,
- footerType: FooterReferenceType.EVEN,
+ headers: [{headerId: 100, headerType: HeaderReferenceType.DEFAULT}],
+ footers: [{footerId: 200, footerType: FooterReferenceType.EVEN}],
pageNumberStart: 10,
pageNumberFormatType: PageNumberFormat.CARDINAL_TEXT,
});
diff --git a/src/file/document/body/section-properties/section-properties.ts b/src/file/document/body/section-properties/section-properties.ts
index c23964cd14..aa2cc9a664 100644
--- a/src/file/document/body/section-properties/section-properties.ts
+++ b/src/file/document/body/section-properties/section-properties.ts
@@ -1,24 +1,27 @@
// http://officeopenxml.com/WPsection.php
import { XmlComponent } from "file/xml-components";
-import { FooterReferenceType, IPageBordersOptions, IPageNumberTypeAttributes, PageBorders, PageNumberFormat, PageNumberType } from "./";
+import { IPageBordersOptions, IPageNumberTypeAttributes, PageBorders, PageNumberFormat, PageNumberType } from "./";
import { Columns } from "./columns/columns";
import { IColumnsAttributes } from "./columns/columns-attributes";
import { DocumentGrid } from "./doc-grid/doc-grid";
import { IDocGridAttributesProperties } from "./doc-grid/doc-grid-attributes";
import { FooterReference, IFooterOptions } from "./footer-reference/footer-reference";
import { HeaderReference, IHeaderOptions } from "./header-reference/header-reference";
-import { HeaderReferenceType } from "./header-reference/header-reference-attributes";
import { PageMargin } from "./page-margin/page-margin";
import { IPageMarginAttributes } from "./page-margin/page-margin-attributes";
import { PageSize } from "./page-size/page-size";
import { IPageSizeAttributes, PageOrientation } from "./page-size/page-size-attributes";
+import { TitlePage } from "./title-page/title-page";
+
+type IHeadersOptions = {headers? : IHeaderOptions[]}
+type IFootersOptions = {footers? : IFooterOptions[]}
export type SectionPropertiesOptions = IPageSizeAttributes &
IPageMarginAttributes &
IColumnsAttributes &
IDocGridAttributesProperties &
- IHeaderOptions &
- IFooterOptions &
+ IHeadersOptions &
+ IFootersOptions &
IPageNumberTypeAttributes &
IPageBordersOptions;
@@ -41,10 +44,8 @@ export class SectionProperties extends XmlComponent {
space: 708,
linePitch: 360,
orientation: PageOrientation.PORTRAIT,
- headerType: HeaderReferenceType.DEFAULT,
- headerId: 0,
- footerType: FooterReferenceType.DEFAULT,
- footerId: 0,
+ headers: [],
+ footers: [],
pageNumberStart: undefined,
pageNumberFormatType: PageNumberFormat.DECIMAL,
pageBorders: undefined,
@@ -52,6 +53,7 @@ export class SectionProperties extends XmlComponent {
pageBorderRight: undefined,
pageBorderBottom: undefined,
pageBorderLeft: undefined,
+ titlePage : true
};
const mergedOptions = {
@@ -59,6 +61,7 @@ export class SectionProperties extends XmlComponent {
...options,
};
+
this.root.push(new PageSize(mergedOptions.width, mergedOptions.height, mergedOptions.orientation));
this.root.push(
new PageMargin(
@@ -74,19 +77,24 @@ export class SectionProperties extends XmlComponent {
this.root.push(new Columns(mergedOptions.space));
this.root.push(new DocumentGrid(mergedOptions.linePitch));
- this.root.push(
- new HeaderReference({
- headerType: mergedOptions.headerType,
- headerId: mergedOptions.headerId,
- }),
- );
- this.root.push(
- new FooterReference({
- footerType: mergedOptions.footerType,
- footerId: mergedOptions.footerId,
- }),
- );
+ for (let header of mergedOptions.headers) {
+ this.root.push(
+ new HeaderReference({
+ headerType: header.headerType,
+ headerId: header.headerId,
+ }),
+ );
+ }
+ for (let footer of mergedOptions.footers) {
+ this.root.push(
+ new FooterReference({
+ footerType: footer.footerType,
+ footerId: footer.footerId,
+ }),
+ );
+ }
+
this.root.push(new PageNumberType(mergedOptions.pageNumberStart, mergedOptions.pageNumberFormatType));
if (
@@ -107,6 +115,10 @@ export class SectionProperties extends XmlComponent {
);
}
+ if (mergedOptions.titlePage) {
+ this.root.push(new TitlePage());
+ }
+
this.options = mergedOptions;
}
diff --git a/src/file/file.ts b/src/file/file.ts
index 2700c226b2..da9b289cc9 100644
--- a/src/file/file.ts
+++ b/src/file/file.ts
@@ -2,7 +2,14 @@ import { AppProperties } from "./app-properties/app-properties";
import { ContentTypes } from "./content-types/content-types";
import { CoreProperties, IPropertiesOptions } from "./core-properties";
import { Document } from "./document";
-import { FooterReferenceType, HeaderReference, HeaderReferenceType, SectionPropertiesOptions } from "./document/body/section-properties";
+import {
+ FooterReferenceType,
+ HeaderReference,
+ HeaderReferenceType,
+ SectionPropertiesOptions,
+ IHeaderOptions,
+ IFooterOptions,
+} from "./document/body/section-properties";
import { FooterWrapper } from "./footer-wrapper";
import { FootNotes } from "./footnotes";
import { HeaderWrapper } from "./header-wrapper";
@@ -14,7 +21,9 @@ import { Styles } from "./styles";
import { ExternalStylesFactory } from "./styles/external-styles-factory";
import { DefaultStylesFactory } from "./styles/factory";
import { Table } from "./table";
-import { XmlComponent } from "./xml-components";
+
+type DocumentHeaders = { header: HeaderWrapper; type: HeaderReferenceType }[];
+type DocumentFooters = { footer: FooterWrapper; type: FooterReferenceType }[];
export class File {
private readonly document: Document;
@@ -24,8 +33,9 @@ export class File {
private readonly media: Media;
private readonly docRelationships: Relationships;
private readonly fileRelationships: Relationships;
- private readonly headerWrapper: HeaderWrapper[] = [];
- private readonly footerWrapper: FooterWrapper[] = [];
+ private readonly documentHeaders: DocumentHeaders = [];
+ private readonly documentFooters: DocumentFooters = [];
+
private readonly footNotes: FootNotes;
private readonly contentTypes: ContentTypes;
@@ -42,6 +52,11 @@ export class File {
};
}
+ const templateDocument = options.templateDocument;
+ if (templateDocument) {
+ this.currentRelationshipId = templateDocument.currentRelationshipId + 1;
+ }
+
if (options.externalStyles) {
const stylesFactory = new ExternalStylesFactory();
this.styles = stylesFactory.newInstance(options.externalStyles);
@@ -52,6 +67,7 @@ export class File {
this.coreProperties = new CoreProperties(options);
this.numbering = new Numbering();
+
this.docRelationships = new Relationships();
this.docRelationships.createRelationship(
this.currentRelationshipId++,
@@ -70,10 +86,26 @@ export class File {
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes",
"footnotes.xml",
);
+
this.media = new Media();
- const header = this.createHeader(options.templateHeader);
- const footer = this.createFooter();
+ const templateHeaders = templateDocument && templateDocument.headers;
+ if (!templateHeaders) {
+ this.createHeader();
+ } else {
+ for (let templateHeader of templateHeaders) {
+ this.addHeaderToDocument(templateHeader.header, templateHeader.type);
+ }
+ }
+
+ const templateFooters = templateDocument && templateDocument.footers;
+ if (!templateFooters) {
+ this.createFooter();
+ } else {
+ for (let templateFooter of templateFooters) {
+ this.addFooterToDocument(templateFooter.footer, templateFooter.type);
+ }
+ }
this.fileRelationships = new Relationships();
this.fileRelationships.createRelationship(
@@ -94,17 +126,34 @@ export class File {
this.appProperties = new AppProperties();
this.footNotes = new FootNotes();
+
+ let headersOptions: IHeaderOptions[] = [];
+ for (let documentHeader of this.documentHeaders) {
+ headersOptions.push({
+ headerId: documentHeader.header.Header.ReferenceId,
+ headerType: documentHeader.type,
+ });
+ }
+
+ let footersOptions: IFooterOptions[] = [];
+ for (let documentFooter of this.documentFooters) {
+ footersOptions.push({
+ footerId: documentFooter.footer.Footer.ReferenceId,
+ footerType: documentFooter.type,
+ });
+ }
+
if (!sectionPropertiesOptions) {
sectionPropertiesOptions = {
- footerType: FooterReferenceType.DEFAULT,
- headerType: HeaderReferenceType.DEFAULT,
- headerId: header.Header.ReferenceId,
- footerId: footer.Footer.ReferenceId,
+ headers: headersOptions,
+ footers: footersOptions,
};
- } else {
- sectionPropertiesOptions.headerId = header.Header.ReferenceId;
- sectionPropertiesOptions.footerId = footer.Footer.ReferenceId;
}
+ else {
+ sectionPropertiesOptions.headers = headersOptions;
+ sectionPropertiesOptions.footers = footersOptions;
+ }
+
this.document = new Document(sectionPropertiesOptions);
}
@@ -170,30 +219,36 @@ export class File {
this.footNotes.createFootNote(paragraph);
}
- public createHeader(templateHeader? : XmlComponent): HeaderWrapper {
- const header = new HeaderWrapper(this.media, this.currentRelationshipId++, templateHeader);
- console.log('\n\n-------\n\n');
- console.log('header', JSON.stringify(header.Header, null, 2));
- this.headerWrapper.push(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);
+ public createHeader(): HeaderWrapper {
+ const header = new HeaderWrapper(this.currentRelationshipId++);
+ this.addHeaderToDocument(header);
return header;
}
+ private addHeaderToDocument(header: HeaderWrapper, type: HeaderReferenceType = HeaderReferenceType.DEFAULT) {
+ this.documentHeaders.push({ header, type });
+ this.docRelationships.createRelationship(
+ header.Header.ReferenceId,
+ "http://schemas.openxmlformats.org/officeDocument/2006/relationships/header",
+ `header${this.documentHeaders.length}.xml`,
+ );
+ this.contentTypes.addHeader(this.documentHeaders.length);
+ }
+
public createFooter(): FooterWrapper {
- const footer = new FooterWrapper(this.media, this.currentRelationshipId++);
- this.footerWrapper.push(footer);
+ const footer = new FooterWrapper(this.currentRelationshipId++);
+ this.addFooterToDocument(footer);
+ return footer;
+ }
+
+ private addFooterToDocument(footer: FooterWrapper, type: FooterReferenceType = FooterReferenceType.DEFAULT) {
+ this.documentFooters.push({ footer, type });
this.docRelationships.createRelationship(
footer.Footer.ReferenceId,
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer",
- `footer${this.footerWrapper.length}.xml`,
+ `footer${this.documentFooters.length}.xml`,
);
- this.contentTypes.addFooter(this.footerWrapper.length);
- return footer;
+ this.contentTypes.addFooter(this.documentFooters.length);
}
public createFirstPageHeader(): HeaderWrapper {
@@ -238,15 +293,15 @@ export class File {
}
public get Header(): HeaderWrapper {
- return this.headerWrapper[0];
+ return this.documentHeaders[0].header;
}
public get Headers(): HeaderWrapper[] {
- return this.headerWrapper;
+ return this.documentHeaders.map((item) => item.header);
}
public HeaderByRefNumber(refId: number): HeaderWrapper {
- const entry = this.headerWrapper.find((h) => h.Header.ReferenceId === refId);
+ const entry = this.documentHeaders.map((item) => item.header).find((h) => h.Header.ReferenceId === refId);
if (entry) {
return entry;
}
@@ -254,15 +309,15 @@ export class File {
}
public get Footer(): FooterWrapper {
- return this.footerWrapper[0];
+ return this.documentFooters[0].footer;
}
public get Footers(): FooterWrapper[] {
- return this.footerWrapper;
+ return this.documentFooters.map((item) => item.footer);
}
public FooterByRefNumber(refId: number): FooterWrapper {
- const entry = this.footerWrapper.find((h) => h.Footer.ReferenceId === refId);
+ const entry = this.documentFooters.map((item) => item.footer).find((h) => h.Footer.ReferenceId === refId);
if (entry) {
return entry;
}
diff --git a/src/file/footer-wrapper.ts b/src/file/footer-wrapper.ts
index afd673c2b2..f738d92a27 100644
--- a/src/file/footer-wrapper.ts
+++ b/src/file/footer-wrapper.ts
@@ -4,14 +4,17 @@ import { Image, Media } from "./media";
import { ImageParagraph, Paragraph } from "./paragraph";
import { Relationships } from "./relationships";
import { Table } from "./table";
+import { IMediaData } from 'file/media';
export class FooterWrapper {
private readonly footer: Footer;
private readonly relationships: Relationships;
+ public readonly media = new Media();
- constructor(private readonly media: Media, referenceId: number) {
- this.footer = new Footer(referenceId);
+ constructor(referenceId: number, initContent? : XmlComponent) {
+ this.footer = new Footer(referenceId, initContent);
this.relationships = new Relationships();
+
}
public addParagraph(paragraph: Paragraph): void {
@@ -36,16 +39,22 @@ export class FooterWrapper {
this.footer.addChildElement(childElement);
}
- public createImage(image: Buffer, width?: number, height?: number): void {
- const mediaData = this.media.addMedia(image, this.relationships.RelationshipCount, width, height);
+ public addImageRelation(image: Buffer, refId : number, width?: number, height?: number) : IMediaData {
+ const mediaData = this.media.addMedia(image, refId, width, height);
this.relationships.createRelationship(
- mediaData.referenceId,
+ refId,
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
`media/${mediaData.fileName}`,
);
+ return mediaData;
+ }
+
+ public createImage(image: Buffer, width?: number, height?: number): void {
+ let mediaData = this.addImageRelation(image, this.relationships.RelationshipCount, width, height);
this.addImage(new Image(new ImageParagraph(mediaData)));
}
+
public addImage(image: Image): FooterWrapper {
this.footer.addParagraph(image.Paragraph);
return this;
diff --git a/src/file/footer/footer.ts b/src/file/footer/footer.ts
index 532e662c25..959540d083 100644
--- a/src/file/footer/footer.ts
+++ b/src/file/footer/footer.ts
@@ -7,8 +7,8 @@ import { FooterAttributes } from "./footer-attributes";
export class Footer extends XmlComponent {
private readonly refId: number;
- constructor(referenceNumber: number) {
- super("w:ftr");
+ constructor(referenceNumber: number, initContent? : XmlComponent) {
+ super("w:ftr", initContent);
this.refId = referenceNumber;
this.root.push(
new FooterAttributes({
diff --git a/src/file/header-wrapper.ts b/src/file/header-wrapper.ts
index 1ba399aeca..37b7981848 100644
--- a/src/file/header-wrapper.ts
+++ b/src/file/header-wrapper.ts
@@ -4,13 +4,16 @@ import { Image, Media } from "./media";
import { ImageParagraph, Paragraph } from "./paragraph";
import { Relationships } from "./relationships";
import { Table } from "./table";
+import { IMediaData } from 'file/media';
export class HeaderWrapper {
private readonly header: Header;
private readonly relationships: Relationships;
+ public readonly media = new Media();
- constructor(private readonly media: Media, referenceId: number, initContent? : XmlComponent) {
+ // constructor(private readonly media: Media, referenceId: number, initContent? : XmlComponent) {
+ constructor(referenceId: number, initContent? : XmlComponent) {
this.header = new Header(referenceId, initContent);
this.relationships = new Relationships();
}
@@ -37,13 +40,18 @@ export class HeaderWrapper {
this.header.addChildElement(childElement);
}
- public createImage(image: Buffer, width?: number, height?: number): void {
- const mediaData = this.media.addMedia(image, this.relationships.RelationshipCount, width, height);
+ public addImageRelation(image: Buffer, refId : number, width?: number, height?: number) : IMediaData {
+ const mediaData = this.media.addMedia(image, refId, width, height);
this.relationships.createRelationship(
- mediaData.referenceId,
+ refId,
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
`media/${mediaData.fileName}`,
);
+ return mediaData;
+ }
+
+ public createImage(image: Buffer, width?: number, height?: number): void {
+ let mediaData = this.addImageRelation(image, this.relationships.RelationshipCount, width, height);
this.addImage(new Image(new ImageParagraph(mediaData)));
}
diff --git a/src/file/header/header-attributes.ts b/src/file/header/header-attributes.ts
index e47271841c..9d7f8a7801 100644
--- a/src/file/header/header-attributes.ts
+++ b/src/file/header/header-attributes.ts
@@ -23,6 +23,17 @@ export interface IHeaderAttributesProperties {
dcmitype?: string;
xsi?: 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 {
@@ -49,5 +60,16 @@ export class HeaderAttributes extends XmlAttributeComponent();
- if (initContent) {
- console.log('\n\n-------\n\n');
- console.log('new root', JSON.stringify(initContent, null,2));
- console.log('\n\n-------\n\n');
- }
-
}
public prepForXml(): IXmlableObject {
@@ -30,7 +24,7 @@ export abstract class XmlComponent extends BaseXmlComponent {
}
return comp;
})
- .filter((comp) => comp); // Exclude null, undefined, and empty strings
+ .filter((comp) => comp !== null); // Exclude null, undefined, and empty strings
return {
[this.rootKey]: children,
};
diff --git a/src/importDocx/importDocx.ts b/src/importDocx/importDocx.ts
index 9370771a68..73a0114933 100644
--- a/src/importDocx/importDocx.ts
+++ b/src/importDocx/importDocx.ts
@@ -1,27 +1,152 @@
import * as JSZip from "jszip";
import * as fastXmlParser from "fast-xml-parser";
import { convertToXmlComponent, parseOptions, ImportedXmlComponent } from "file/xml-components";
+import { HeaderWrapper } from 'file/header-wrapper';
+import { FooterWrapper } from 'file/footer-wrapper';
+import { HeaderReferenceType } from 'file/document/body/section-properties/header-reference';
+import { FooterReferenceType } from 'file/document/body/section-properties/footer-reference';
+// import { RelationshipType } from 'file/relationships/relationship/relationship';
+
+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',
+}
+
+interface DocumentRefs {
+ headers : {id : number, type: HeaderReferenceType}[],
+ footers : {id : number, type: FooterReferenceType}[]
+}
+
+type RelationFileInfo = {id : number, targetFile: string, type: 'header' | 'footer' | 'image'};
+
+type DocumentHeaders = {type : HeaderReferenceType, header : HeaderWrapper}[];
+type DocumentFooters = {type : FooterReferenceType, footer : FooterWrapper}[];
+
+export interface TemplateDocument {
+ currentRelationshipId : number;
+ headers : DocumentHeaders,
+ footers : DocumentFooters,
+}
export class ImportDocx {
+ private currentRelationshipId: number = 1;
+
constructor() {
}
- read(data) : Promise {
- return new Promise((resolve) => {
- JSZip.loadAsync(data).then((zipContent) => {
- let headerContent = zipContent['files']['word/header2.xml'];
-
- headerContent.async('text').then((xmlData : string) => {
- console.log('\n\n-------\n\n');
- console.log('headerContent', JSON.stringify(xmlData, null, 2));
- console.log('\n\n-------\n\n');
- const jsonObj = fastXmlParser.parse(xmlData, parseOptions);
- let xmlComp = convertToXmlComponent('w:hdr', jsonObj['w:hdr']) as ImportedXmlComponent;
- resolve(xmlComp);
- })
- });
- })
+
+
+ async extract(data : Buffer) : Promise {
+ let zipContent = await JSZip.loadAsync(data);
+
+ let documentContent = zipContent['files']['word/document.xml'];
+ const documentRefs : DocumentRefs = this.extractDocumentRefs(await documentContent.async('text'))
+
+ let relationshipContent = zipContent['files']['word/_rels/document.xml.rels'];
+ const documentRelations : RelationFileInfo[] = this.findReferenceFiles(await relationshipContent.async('text'));
+
+ let headers : DocumentHeaders = [];
+ for(let headerRef of documentRefs.headers) {
+ const headerKey = 'w:hdr';
+ const relationFileInfo = documentRelations.find(rel => rel.id === headerRef.id);
+ if (relationFileInfo == null) {
+ throw `can not find target file for id ${headerRef.id}`;
+ }
+
+ const xmlData = await zipContent['files'][`word/${relationFileInfo.targetFile}`].async('text');
+ const xmlObj = fastXmlParser.parse(xmlData, parseOptions);
+
+ let importedComp = convertToXmlComponent(headerKey, xmlObj[headerKey]) as ImportedXmlComponent;
+
+ let header = new HeaderWrapper(this.currentRelationshipId++, importedComp);
+ await this.addImagesToWrapper(relationFileInfo, zipContent, header);
+ headers.push({type : headerRef.type, header})
+ }
+
+ let footers : DocumentFooters = [];
+ for(let footerRef of documentRefs.footers) {
+ const footerKey = 'w:ftr'
+ const relationFileInfo = documentRelations.find(rel => rel.id === footerRef.id);
+ if (relationFileInfo == null) {
+ throw `can not find target file for id ${footerRef.id}`;
+ }
+ const xmlData = await zipContent['files'][`word/${relationFileInfo.targetFile}`].async('text');
+ const xmlObj = fastXmlParser.parse(xmlData, parseOptions);
+ let importedComp = convertToXmlComponent(footerKey, xmlObj[footerKey]) as ImportedXmlComponent;
+
+ let footer = new FooterWrapper(this.currentRelationshipId++, importedComp);
+ await this.addImagesToWrapper(relationFileInfo, zipContent, footer);
+ footers.push({type : footerRef.type, footer})
+ }
+
+ let templateDocument : TemplateDocument = {headers, footers, currentRelationshipId : this.currentRelationshipId}
+ return templateDocument;
}
+
+ async addImagesToWrapper(relationFile : RelationFileInfo, zipContent, wrapper : HeaderWrapper | FooterWrapper) {
+ let wrapperImagesReferences : RelationFileInfo[] = [];
+ const refFile = zipContent['files'][`word/_rels/${relationFile.targetFile}.rels`];
+ if (refFile) {
+ const xmlRef = await refFile.async('text');
+ wrapperImagesReferences = this.findReferenceFiles(xmlRef).filter(r => r.type === 'image');
+ }
+ for (let r of wrapperImagesReferences) {
+ const buffer = await zipContent['files'][`word/${r.targetFile}`].async('nodebuffer');
+ wrapper.addImageRelation(buffer, r.id);
+ }
+ }
+
+
+ findReferenceFiles(xmlData : string) : RelationFileInfo[] {
+ const xmlObj = fastXmlParser.parse(xmlData, parseOptions);
+ const relationXmlArray = Array.isArray(xmlObj['Relationships']['Relationship']) ? xmlObj['Relationships']['Relationship'] : [xmlObj['Relationships']['Relationship']];
+ const relations : RelationFileInfo[] = relationXmlArray
+ .map(item => {
+ return {
+ id : this.parseRefId(item['_attr']['Id']),
+ type : schemeToType[item['_attr']['Type']],
+ targetFile : item['_attr']['Target']
+ }
+ })
+ .filter(item => item.type != null)
+ return relations;
+ }
+
+ extractDocumentRefs(xmlData : string) : DocumentRefs {
+
+ const xmlObj = fastXmlParser.parse(xmlData, parseOptions);
+ const sectionProp = xmlObj['w:document']['w:body']['w:sectPr'];
+
+ const headersXmlArray = Array.isArray(sectionProp['w:headerReference']) ? sectionProp['w:headerReference'] : [sectionProp['w:headerReference']];
+ const headers = headersXmlArray
+ .map(item => {
+ return {
+ type : item['_attr']['w:type'],
+ id : this.parseRefId(item['_attr']['r:id'])
+ }
+ });
+
+ const footersXmlArray = Array.isArray(sectionProp['w:footerReference']) ? sectionProp['w:footerReference'] : [sectionProp['w:footerReference']];
+ const footers = footersXmlArray
+ .map(item => {
+ return {
+ type : item['_attr']['w:type'],
+ id : this.parseRefId(item['_attr']['r:id'])
+ }
+ });
+
+ return {headers, footers}
+ }
+
+ parseRefId(str : string) : number {
+ let match = /^rId(\d+)$/.exec(str);
+ if (match == null) {
+ throw 'invalid ref id';
+ }
+ return parseInt(match[1]);
+ }
+
}
diff --git a/src/importDocx/simple.dotx b/src/importDocx/simple.dotx
deleted file mode 100644
index cc25525149..0000000000
Binary files a/src/importDocx/simple.dotx and /dev/null differ