From bb277a4a767ebef6364db40010cc9bfbed82a2ed Mon Sep 17 00:00:00 2001 From: Dolan Date: Thu, 3 Jan 2019 20:47:00 +0000 Subject: [PATCH 1/5] Format demo --- demo/demo35.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/demo/demo35.ts b/demo/demo35.ts index a91ad2347d..029c3fbfff 100644 --- a/demo/demo35.ts +++ b/demo/demo35.ts @@ -1,11 +1,12 @@ // Simple example to add text to a document // Import from 'docx' rather than '../build' if you install from npm import * as fs from "fs"; -import { Document, Packer, Paragraph, TextRun } from "../build"; +import { Document, Packer, Paragraph } from "../build"; + +const doc = new Document(); +const paragraph = new Paragraph(); +const link = doc.createHyperlink("http://www.example.com", "Hyperlink"); -var doc = new Document(); -var paragraph = new Paragraph(); -var link = doc.createHyperlink('http://www.example.com', 'Hyperlink'); link.bold(); paragraph.addHyperLink(link); doc.addParagraph(paragraph); From e3bcad6d3c67f0577f2d330f594a72a6e54d4820 Mon Sep 17 00:00:00 2001 From: Dolan Date: Fri, 4 Jan 2019 00:11:50 +0000 Subject: [PATCH 2/5] Fix missing media in Document Template --- demo/demo29.ts | 22 +++++++++++++++++----- src/file/document/document.ts | 3 ++- src/file/file.ts | 11 +++++++---- src/import-dotx/import-dotx.ts | 2 ++ 4 files changed, 28 insertions(+), 10 deletions(-) diff --git a/demo/demo29.ts b/demo/demo29.ts index 0650aac09c..08fc139de6 100644 --- a/demo/demo29.ts +++ b/demo/demo29.ts @@ -15,10 +15,22 @@ const item2 = new Paragraph("line with contextual spacing"); const item3 = new Paragraph("line without contextual spacing"); const item4 = new Paragraph("line without contextual spacing"); -item1.setNumbering(concrete, 0).spacing({before: 200}).contextualSpacing(true); -item2.setNumbering(concrete, 0).spacing({before: 200}).contextualSpacing(true); -item3.setNumbering(concrete, 0).spacing({before: 200}).contextualSpacing(false); -item4.setNumbering(concrete, 0).spacing({before: 200}).contextualSpacing(false); +item1 + .setNumbering(concrete, 0) + .spacing({ before: 200 }) + .contextualSpacing(true); +item2 + .setNumbering(concrete, 0) + .spacing({ before: 200 }) + .contextualSpacing(true); +item3 + .setNumbering(concrete, 0) + .spacing({ before: 200 }) + .contextualSpacing(false); +item4 + .setNumbering(concrete, 0) + .spacing({ before: 200 }) + .contextualSpacing(false); doc.addParagraph(item1); doc.addParagraph(item2); @@ -29,4 +41,4 @@ const packer = new Packer(); packer.toBuffer(doc).then((buffer) => { fs.writeFileSync("My Document.docx", buffer); -}); \ No newline at end of file +}); diff --git a/src/file/document/document.ts b/src/file/document/document.ts index d9dae1020f..5ca48ccb50 100644 --- a/src/file/document/document.ts +++ b/src/file/document/document.ts @@ -53,8 +53,9 @@ export class Document extends XmlComponent { return para; } - public addTable(table: Table): void { + public addTable(table: Table): Document { this.body.push(table); + return this; } public createTable(rows: number, cols: number): Table { diff --git a/src/file/file.ts b/src/file/file.ts index 5f01d71dad..ec953b5a63 100644 --- a/src/file/file.ts +++ b/src/file/file.ts @@ -56,12 +56,13 @@ export class File { 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(); + this.media = fileProperties.template && fileProperties.template.media ? fileProperties.template.media : new Media(); + if (fileProperties.template) { this.currentRelationshipId = fileProperties.template.currentRelationshipId + 1; } @@ -108,8 +109,9 @@ export class File { this.settings = new Settings(); } - public addTableOfContents(toc: TableOfContents): void { + public addTableOfContents(toc: TableOfContents): File { this.document.addTableOfContents(toc); + return this; } public addParagraph(paragraph: Paragraph): File { @@ -121,8 +123,9 @@ export class File { return this.document.createParagraph(text); } - public addTable(table: Table): void { - return this.document.addTable(table); + public addTable(table: Table): File { + this.document.addTable(table); + return this; } public createTable(rows: number, cols: number): Table { diff --git a/src/import-dotx/import-dotx.ts b/src/import-dotx/import-dotx.ts index fdfedc7304..5cdf7b8920 100644 --- a/src/import-dotx/import-dotx.ts +++ b/src/import-dotx/import-dotx.ts @@ -44,6 +44,7 @@ export interface IDocumentTemplate { readonly footers: IDocumentFooter[]; readonly styles: Styles; readonly titlePageIsDefined: boolean; + readonly media: Media; } export class ImportDotx { @@ -73,6 +74,7 @@ export class ImportDotx { currentRelationshipId: this.currentRelationshipId, styles: stylesFactory.newInstance(stylesContent), titlePageIsDefined: this.checkIfTitlePageIsDefined(documentContent), + media: media, }; return templateDocument; From 12ad545fe80d9b8f7e0d09be256ed95fb3bbe08c Mon Sep 17 00:00:00 2001 From: Dolan Date: Mon, 7 Jan 2019 21:41:39 +0000 Subject: [PATCH 3/5] Write ImportDotx more functionally --- src/import-dotx/import-dotx.ts | 117 +++++++++++++++------------------ 1 file changed, 53 insertions(+), 64 deletions(-) diff --git a/src/import-dotx/import-dotx.ts b/src/import-dotx/import-dotx.ts index 5cdf7b8920..2b1261efe7 100644 --- a/src/import-dotx/import-dotx.ts +++ b/src/import-dotx/import-dotx.ts @@ -7,8 +7,6 @@ import { FooterWrapper, IDocumentFooter } from "file/footer-wrapper"; import { HeaderWrapper, IDocumentHeader } from "file/header-wrapper"; import { Media } from "file/media"; import { TargetModeType } from "file/relationships/relationship/relationship"; -import { Styles } from "file/styles"; -import { ExternalStylesFactory } from "file/styles/external-styles-factory"; import { convertToXmlComponent, ImportedXmlComponent } from "file/xml-components"; const schemeToType = { @@ -42,37 +40,28 @@ export interface IDocumentTemplate { readonly currentRelationshipId: number; readonly headers: IDocumentHeader[]; readonly footers: IDocumentFooter[]; - readonly styles: Styles; + readonly styles: string; readonly titlePageIsDefined: boolean; readonly media: Media; } export class ImportDotx { - // tslint:disable-next-line:readonly-keyword - private currentRelationshipId: number; - - constructor() { - this.currentRelationshipId = 1; - } - public async extract(data: Buffer): Promise { const zipContent = await JSZip.loadAsync(data); - const stylesContent = await zipContent.files["word/styles.xml"].async("text"); const documentContent = await zipContent.files["word/document.xml"].async("text"); const relationshipContent = await zipContent.files["word/_rels/document.xml.rels"].async("text"); - const stylesFactory = new ExternalStylesFactory(); const documentRefs = this.extractDocumentRefs(documentContent); const documentRelationships = this.findReferenceFiles(relationshipContent); const media = new Media(); const templateDocument: IDocumentTemplate = { - headers: await this.createHeaders(zipContent, documentRefs, documentRelationships, media), - footers: await this.createFooters(zipContent, documentRefs, documentRelationships, media), - currentRelationshipId: this.currentRelationshipId, - styles: stylesFactory.newInstance(stylesContent), + headers: await this.createHeaders(zipContent, documentRefs, documentRelationships, media, 0), + footers: await this.createFooters(zipContent, documentRefs, documentRelationships, media, documentRefs.headers.length), + currentRelationshipId: documentRefs.footers.length + documentRefs.headers.length, + styles: await zipContent.files["word/styles.xml"].async("text"), titlePageIsDefined: this.checkIfTitlePageIsDefined(documentContent), media: media, }; @@ -85,34 +74,34 @@ export class ImportDotx { documentRefs: IDocumentRefs, documentRelationships: IRelationshipFileInfo[], media: Media, + startingRelationshipId: number, ): Promise { - const footers: IDocumentFooter[] = []; + const result = documentRefs.footers + .map(async (reference, i) => { + const relationshipFileInfo = documentRelationships.find((rel) => rel.id === reference.id); - for (const footerRef of documentRefs.footers) { - const relationFileInfo = documentRelationships.find((rel) => rel.id === footerRef.id); - - if (relationFileInfo === null || !relationFileInfo) { - throw new Error(`Can not find target file for id ${footerRef.id}`); - } - - const xmlData = await zipContent.files[`word/${relationFileInfo.target}`].async("text"); - const xmlObj = xml2js(xmlData, { compact: false, captureSpacesBetweenElements: true }) as XMLElement; - let footerXmlElement: XMLElement | undefined; - for (const xmlElm of xmlObj.elements || []) { - if (xmlElm.name === "w:ftr") { - footerXmlElement = xmlElm; + if (relationshipFileInfo === null || !relationshipFileInfo) { + throw new Error(`Can not find target file for id ${reference.id}`); } - } - if (footerXmlElement === undefined) { - continue; - } - const importedComp = convertToXmlComponent(footerXmlElement) as ImportedXmlComponent; - const footer = new FooterWrapper(media, this.currentRelationshipId++, importedComp); - await this.addRelationshipToWrapper(relationFileInfo, zipContent, footer, media); - footers.push({ type: footerRef.type, footer }); - } - return footers; + const xmlData = await zipContent.files[`word/${relationshipFileInfo.target}`].async("text"); + const xmlObj = xml2js(xmlData, { compact: false, captureSpacesBetweenElements: true }) as XMLElement; + + if (!xmlObj.elements) { + return undefined; + } + + const xmlElement = xmlObj.elements.reduce((acc, current) => (current.name === "w:ftr" ? current : acc)); + + const importedComp = convertToXmlComponent(xmlElement) as ImportedXmlComponent; + const wrapper = new FooterWrapper(media, startingRelationshipId + i, importedComp); + await this.addRelationshipToWrapper(relationshipFileInfo, zipContent, wrapper, media); + + return { type: reference.type, footer: wrapper }; + }) + .filter((x) => !!x) as Array>; + + return Promise.all(result); } private async createHeaders( @@ -120,34 +109,34 @@ export class ImportDotx { documentRefs: IDocumentRefs, documentRelationships: IRelationshipFileInfo[], media: Media, + startingRelationshipId: number, ): Promise { - const headers: IDocumentHeader[] = []; + const result = documentRefs.headers + .map(async (reference, i) => { + const relationshipFileInfo = documentRelationships.find((rel) => rel.id === reference.id); - for (const headerRef of documentRefs.headers) { - const relationFileInfo = documentRelationships.find((rel) => rel.id === headerRef.id); - if (relationFileInfo === null || !relationFileInfo) { - throw new Error(`Can not find target file for id ${headerRef.id}`); - } - - const xmlData = await zipContent.files[`word/${relationFileInfo.target}`].async("text"); - const xmlObj = xml2js(xmlData, { compact: false, captureSpacesBetweenElements: true }) as XMLElement; - let headerXmlElement: XMLElement | undefined; - for (const xmlElm of xmlObj.elements || []) { - if (xmlElm.name === "w:hdr") { - headerXmlElement = xmlElm; + if (relationshipFileInfo === null || !relationshipFileInfo) { + throw new Error(`Can not find target file for id ${reference.id}`); } - } - if (headerXmlElement === undefined) { - continue; - } - const importedComp = convertToXmlComponent(headerXmlElement) as ImportedXmlComponent; - const header = new HeaderWrapper(media, this.currentRelationshipId++, importedComp); - // await this.addMedia(zipContent, media, documentRefs, documentRelationships); - await this.addRelationshipToWrapper(relationFileInfo, zipContent, header, media); - headers.push({ type: headerRef.type, header }); - } - return headers; + const xmlData = await zipContent.files[`word/${relationshipFileInfo.target}`].async("text"); + const xmlObj = xml2js(xmlData, { compact: false, captureSpacesBetweenElements: true }) as XMLElement; + + if (!xmlObj.elements) { + return undefined; + } + + const xmlElement = xmlObj.elements.reduce((acc, current) => (current.name === "w:hdr" ? current : acc)); + + const importedComp = convertToXmlComponent(xmlElement) as ImportedXmlComponent; + const wrapper = new HeaderWrapper(media, startingRelationshipId + i, importedComp); + await this.addRelationshipToWrapper(relationshipFileInfo, zipContent, wrapper, media); + + return { type: reference.type, header: wrapper }; + }) + .filter((x) => !!x) as Array>; + + return Promise.all(result); } private async addRelationshipToWrapper( From 612c024b1feec46a2654c05c4b5011b31fb3c68a Mon Sep 17 00:00:00 2001 From: Dolan Date: Mon, 7 Jan 2019 21:43:04 +0000 Subject: [PATCH 4/5] Use string style factory --- src/file/file.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/file/file.ts b/src/file/file.ts index ec953b5a63..d5780b6230 100644 --- a/src/file/file.ts +++ b/src/file/file.ts @@ -72,7 +72,8 @@ export class File { throw Error("can not use both template and external styles"); } if (fileProperties.template) { - this.styles = fileProperties.template.styles; + const stylesFactory = new ExternalStylesFactory(); + this.styles = stylesFactory.newInstance(fileProperties.template.styles); } else if (options.externalStyles) { const stylesFactory = new ExternalStylesFactory(); this.styles = stylesFactory.newInstance(options.externalStyles); From d67452313913b0cf34d49c902e4c05237c3685fb Mon Sep 17 00:00:00 2001 From: Dolan Date: Fri, 11 Jan 2019 00:26:16 +0000 Subject: [PATCH 5/5] Fix tests - No longer uses dummy variable --- src/import-dotx/import-dotx.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/import-dotx/import-dotx.spec.ts b/src/import-dotx/import-dotx.spec.ts index f89131abf5..c0101174cc 100644 --- a/src/import-dotx/import-dotx.spec.ts +++ b/src/import-dotx/import-dotx.spec.ts @@ -7,7 +7,7 @@ describe("ImportDotx", () => { it("should create", () => { const file = new ImportDotx(); - expect(file).to.deep.equal({ currentRelationshipId: 1 }); + expect(file).to.deep.equal({}); }); });