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/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); 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 4c7b6b9aa0..2a11737279 100644 --- a/src/file/file.ts +++ b/src/file/file.ts @@ -58,12 +58,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; } @@ -73,7 +74,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); @@ -110,8 +112,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 { @@ -123,8 +126,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.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({}); }); }); diff --git a/src/import-dotx/import-dotx.ts b/src/import-dotx/import-dotx.ts index fdfedc7304..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,30 @@ 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, }; return templateDocument; @@ -83,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( @@ -118,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(