From a1e20f4c9a597ce6aac4a3adc2c6b6088bced703 Mon Sep 17 00:00:00 2001 From: amitm02 Date: Tue, 2 Oct 2018 16:02:28 +0300 Subject: [PATCH 1/3] handle templates with no headers or foolters --- src/import-dotx/import-dotx.ts | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/import-dotx/import-dotx.ts b/src/import-dotx/import-dotx.ts index 940ecd2f10..93c9ab97ff 100644 --- a/src/import-dotx/import-dotx.ts +++ b/src/import-dotx/import-dotx.ts @@ -135,12 +135,21 @@ export class ImportDotx { } 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 headersXmlArray = Array.isArray(sectionProp["w:headerReference"]) - ? sectionProp["w:headerReference"] - : [sectionProp["w:headerReference"]]; + 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"], @@ -148,9 +157,16 @@ export class ImportDotx { }; }); - const footersXmlArray = Array.isArray(sectionProp["w:footerReference"]) - ? sectionProp["w:footerReference"] - : [sectionProp["w:footerReference"]]; + 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"], From ffdcc7bacadc1d4bdcb44c67649fe95ce7d54f80 Mon Sep 17 00:00:00 2001 From: amitm02 Date: Tue, 2 Oct 2018 16:17:26 +0300 Subject: [PATCH 2/3] hande hyplerlink references in header or footers --- src/file/footer-wrapper.ts | 9 +++++++++ src/file/header-wrapper.ts | 9 +++++++++ src/import-dotx/import-dotx.ts | 20 +++++++++++++------- 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/file/footer-wrapper.ts b/src/file/footer-wrapper.ts index 99cff08f20..785f2724e4 100644 --- a/src/file/footer-wrapper.ts +++ b/src/file/footer-wrapper.ts @@ -55,6 +55,15 @@ export class FooterWrapper { 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, width?: number, height?: number): void { const mediaData = this.addImageRelationship(image, this.relationships.RelationshipCount, width, height); this.addImage(new Image(new ImageParagraph(mediaData))); diff --git a/src/file/header-wrapper.ts b/src/file/header-wrapper.ts index c2ed4841f1..beb7903cf5 100644 --- a/src/file/header-wrapper.ts +++ b/src/file/header-wrapper.ts @@ -55,6 +55,15 @@ export class HeaderWrapper { 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, width?: number, height?: number): void { const mediaData = this.addImageRelationship(image, this.relationships.RelationshipCount, width, height); this.addImage(new Image(new ImageParagraph(mediaData))); diff --git a/src/import-dotx/import-dotx.ts b/src/import-dotx/import-dotx.ts index 93c9ab97ff..9d541f5c07 100644 --- a/src/import-dotx/import-dotx.ts +++ b/src/import-dotx/import-dotx.ts @@ -20,6 +20,7 @@ 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 { @@ -29,8 +30,8 @@ interface IDocumentRefs { interface IRelationshipFileInfo { id: number; - targetFile: string; - type: "header" | "footer" | "image"; + target: string; + type: "header" | "footer" | "image" | "hyperlink"; } // Document Template @@ -70,7 +71,7 @@ export class ImportDotx { throw new Error(`Can not find target file for id ${headerRef.id}`); } - const xmlData = await zipContent.files[`word/${relationFileInfo.targetFile}`].async("text"); + const xmlData = await zipContent.files[`word/${relationFileInfo.target}`].async("text"); const xmlObj = fastXmlParser.parse(xmlData, importParseOptions); const importedComp = convertToXmlComponent(headerKey, xmlObj[headerKey]) as ImportedXmlComponent; @@ -87,7 +88,7 @@ export class ImportDotx { if (relationFileInfo === null || !relationFileInfo) { throw new Error(`Can not find target file for id ${footerRef.id}`); } - const xmlData = await zipContent.files[`word/${relationFileInfo.targetFile}`].async("text"); + const xmlData = await zipContent.files[`word/${relationFileInfo.target}`].async("text"); const xmlObj = fastXmlParser.parse(xmlData, importParseOptions); const importedComp = convertToXmlComponent(footerKey, xmlObj[footerKey]) as ImportedXmlComponent; @@ -106,15 +107,20 @@ export class ImportDotx { wrapper: HeaderWrapper | FooterWrapper, ): Promise { let wrapperImagesReferences: IRelationshipFileInfo[] = []; - const refFile = zipContent.files[`word/_rels/${relationFile.targetFile}.rels`]; + let hyperLinkReferences: IRelationshipFileInfo[] = []; + const refFile = zipContent.files[`word/_rels/${relationFile.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.targetFile}`].async("nodebuffer"); + 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[] { @@ -127,7 +133,7 @@ export class ImportDotx { return { id: this.parseRefId(item._attr.Id), type: schemeToType[item._attr.Type], - targetFile: item._attr.Target, + target: item._attr.Target, }; }) .filter((item) => item.type !== null); From f3aa6a92036e2d38c0800cbe547b80ccbd54a664 Mon Sep 17 00:00:00 2001 From: amitm02 Date: Tue, 2 Oct 2018 17:52:55 +0300 Subject: [PATCH 3/3] detect if page title is defined in tempalte --- demo/demo29.ts | 2 +- src/import-dotx/import-dotx.ts | 22 ++++++++++++++++++---- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/demo/demo29.ts b/demo/demo29.ts index 8949e17af5..94c417ceef 100644 --- a/demo/demo29.ts +++ b/demo/demo29.ts @@ -12,7 +12,7 @@ fs.readFile(filePath, (err, data) => { importDotx.extract(data).then((templateDocument) => { // This any needs fixing const sectionProps = { - titlePage: true, + titlePage: templateDocument.titlePageIsDefined, } as any; const doc = new Document(undefined, sectionProps, { diff --git a/src/import-dotx/import-dotx.ts b/src/import-dotx/import-dotx.ts index 9d541f5c07..4dbe01be56 100644 --- a/src/import-dotx/import-dotx.ts +++ b/src/import-dotx/import-dotx.ts @@ -41,6 +41,7 @@ export interface IDocumentTemplate { headers: IDocumentHeader[]; footers: IDocumentFooter[]; styles: Styles; + titlePageIsDefined: boolean; } export class ImportDotx { @@ -59,6 +60,7 @@ export class ImportDotx { 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")); @@ -77,7 +79,7 @@ export class ImportDotx { const importedComp = convertToXmlComponent(headerKey, xmlObj[headerKey]) as ImportedXmlComponent; const header = new HeaderWrapper(this.currentRelationshipId++, importedComp); - await this.addImagesToWrapper(relationFileInfo, zipContent, header); + await this.addRelationToWrapper(relationFileInfo, zipContent, header); headers.push({ type: headerRef.type, header }); } @@ -93,15 +95,21 @@ export class ImportDotx { const importedComp = convertToXmlComponent(footerKey, xmlObj[footerKey]) as ImportedXmlComponent; const footer = new FooterWrapper(this.currentRelationshipId++, importedComp); - await this.addImagesToWrapper(relationFileInfo, zipContent, footer); + await this.addRelationToWrapper(relationFileInfo, zipContent, footer); footers.push({ type: footerRef.type, footer }); } - const templateDocument: IDocumentTemplate = { headers, footers, currentRelationshipId: this.currentRelationshipId, styles }; + const templateDocument: IDocumentTemplate = { + headers, + footers, + currentRelationshipId: this.currentRelationshipId, + styles, + titlePageIsDefined, + }; return templateDocument; } - public async addImagesToWrapper( + public async addRelationToWrapper( relationFile: IRelationshipFileInfo, zipContent: JSZip, wrapper: HeaderWrapper | FooterWrapper, @@ -183,6 +191,12 @@ export class ImportDotx { 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) {