diff --git a/src/export/packer/next-compiler.ts b/src/export/packer/next-compiler.ts index d311cf310c..cfec78ff23 100644 --- a/src/export/packer/next-compiler.ts +++ b/src/export/packer/next-compiler.ts @@ -36,6 +36,10 @@ export class Compiler { public async compile(file: File): Promise { const zip = new JSZip(); + // Run precompile steps + file.onCompile(); + file.Headers.forEach((header) => header.onCompile()); + const xmlifiedFileMapping = this.xmlifyFile(file); for (const key in xmlifiedFileMapping) { diff --git a/src/file/file.ts b/src/file/file.ts index 8958c4ccd3..12a1849835 100644 --- a/src/file/file.ts +++ b/src/file/file.ts @@ -13,10 +13,12 @@ import { IFileProperties } from "./file-properties"; import { FooterWrapper, IDocumentFooter } from "./footer-wrapper"; import { FootNotes } from "./footnotes"; import { HeaderWrapper, IDocumentHeader } from "./header-wrapper"; +import { IOnCompile } from "./life-cycles"; import { Image, Media } from "./media"; import { Numbering } from "./numbering"; import { Bookmark, Hyperlink, Paragraph } from "./paragraph"; import { Relationships } from "./relationships"; +import { TargetModeType } from "./relationships/relationship/relationship"; import { Settings } from "./settings"; import { Styles } from "./styles"; import { ExternalStylesFactory } from "./styles/external-styles-factory"; @@ -24,7 +26,7 @@ import { DefaultStylesFactory } from "./styles/factory"; import { Table } from "./table"; import { TableOfContents } from "./table-of-contents"; -export class File { +export class File implements IOnCompile { // tslint:disable-next-line:readonly-keyword private currentRelationshipId: number = 1; @@ -147,7 +149,7 @@ export class File { hyperlink.linkId, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink", link, - "External", + TargetModeType.EXTERNAL, ); return hyperlink; } @@ -221,6 +223,11 @@ export class File { } } + public onCompile(): void { + // this.media.Array.forEach((media) => { + // }); + } + private addHeaderToDocument(header: HeaderWrapper, type: HeaderReferenceType = HeaderReferenceType.DEFAULT): void { this.headers.push({ header, type }); this.docRelationships.createRelationship( diff --git a/src/file/footer-wrapper.ts b/src/file/footer-wrapper.ts index f9fecf6a50..d56eecac92 100644 --- a/src/file/footer-wrapper.ts +++ b/src/file/footer-wrapper.ts @@ -2,7 +2,8 @@ import { XmlComponent } from "file/xml-components"; import { FooterReferenceType } from "./document"; import { Footer } from "./footer/footer"; -import { Image, IMediaData, Media } from "./media"; +import { IOnCompile } from "./life-cycles"; +import { Image, Media } from "./media"; import { ImageParagraph, Paragraph } from "./paragraph"; import { Relationships } from "./relationships"; import { Table } from "./table"; @@ -12,7 +13,7 @@ export interface IDocumentFooter { readonly type: FooterReferenceType; } -export class FooterWrapper { +export class FooterWrapper implements IOnCompile { private readonly footer: Footer; private readonly relationships: Relationships; @@ -43,29 +44,8 @@ export class FooterWrapper { this.footer.addChildElement(childElement); } - public addImageRelationship(image: Buffer, refId: number, width?: number, height?: number): IMediaData { - const mediaData = this.media.addMedia(image, refId, width, height); - this.relationships.createRelationship( - mediaData.referenceId, - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", - `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); + const mediaData = this.media.addMedia(image, this.relationships.RelationshipCount, width, height); this.addImage(new Image(new ImageParagraph(mediaData))); } @@ -74,6 +54,16 @@ export class FooterWrapper { return this; } + public onCompile(): void { + this.media.Array.forEach((mediaData) => { + this.relationships.createRelationship( + mediaData.referenceId, + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", + `media/${mediaData.fileName}`, + ); + }); + } + public get Footer(): Footer { return this.footer; } diff --git a/src/file/header-wrapper.ts b/src/file/header-wrapper.ts index 5730b5eaa6..001245a924 100644 --- a/src/file/header-wrapper.ts +++ b/src/file/header-wrapper.ts @@ -2,7 +2,8 @@ import { XmlComponent } from "file/xml-components"; import { HeaderReferenceType } from "./document"; import { Header } from "./header/header"; -import { Image, IMediaData, Media } from "./media"; +import { IOnCompile } from "./life-cycles"; +import { Image, Media } from "./media"; import { ImageParagraph, Paragraph } from "./paragraph"; import { Relationships } from "./relationships"; import { Table } from "./table"; @@ -12,7 +13,7 @@ export interface IDocumentHeader { readonly type: HeaderReferenceType; } -export class HeaderWrapper { +export class HeaderWrapper implements IOnCompile { private readonly header: Header; private readonly relationships: Relationships; @@ -43,29 +44,8 @@ export class HeaderWrapper { this.header.addChildElement(childElement); } - public addImageRelationship(image: Buffer, refId: number, width?: number, height?: number): IMediaData { - const mediaData = this.media.addMedia(image, refId, width, height); - this.relationships.createRelationship( - mediaData.referenceId, - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", - `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); + const mediaData = this.media.addMedia(image, this.relationships.RelationshipCount, width, height); this.addImage(new Image(new ImageParagraph(mediaData))); } @@ -74,6 +54,16 @@ export class HeaderWrapper { return this; } + public onCompile(): void { + this.media.Array.forEach((mediaData) => { + this.relationships.createRelationship( + mediaData.referenceId, + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", + `media/${mediaData.fileName}`, + ); + }); + } + public get Header(): Header { return this.header; } diff --git a/src/file/life-cycles.ts b/src/file/life-cycles.ts new file mode 100644 index 0000000000..ac38750a50 --- /dev/null +++ b/src/file/life-cycles.ts @@ -0,0 +1,9 @@ +export interface IOnCompile { + readonly onCompile: () => void; +} + +// Needed because of: https://github.com/s-panferov/awesome-typescript-loader/issues/432 +/** + * @ignore + */ +export const WORKAROUND4 = ""; diff --git a/src/file/relationships/relationship/relationship.ts b/src/file/relationships/relationship/relationship.ts index 92553a38f3..82672644c9 100644 --- a/src/file/relationships/relationship/relationship.ts +++ b/src/file/relationships/relationship/relationship.ts @@ -17,7 +17,9 @@ export type RelationshipType = | "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink" | "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes"; -export type TargetModeType = "External"; +export enum TargetModeType { + EXTERNAL = "External", +} export class Relationship extends XmlComponent { constructor(id: string, type: RelationshipType, target: string, targetMode?: TargetModeType) { diff --git a/src/import-dotx/import-dotx.ts b/src/import-dotx/import-dotx.ts index 9ece4db3f1..e6cc0331c2 100644 --- a/src/import-dotx/import-dotx.ts +++ b/src/import-dotx/import-dotx.ts @@ -5,11 +5,11 @@ import { FooterReferenceType } from "file/document/body/section-properties/foote 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 { convertToXmlComponent, ImportedXmlComponent } from "file/xml-components"; - 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 = { "http://schemas.openxmlformats.org/officeDocument/2006/relationships/header": "header", @@ -23,10 +23,17 @@ interface IDocumentRefs { readonly footers: Array<{ readonly id: number; readonly type: FooterReferenceType }>; } +enum RelationshipType { + HEADER = "header", + FOOTER = "footer", + IMAGE = "image", + HYPERLINK = "hyperlink", +} + interface IRelationshipFileInfo { readonly id: number; readonly target: string; - readonly type: "header" | "footer" | "image" | "hyperlink"; + readonly type: RelationshipType; } // Document Template @@ -83,7 +90,7 @@ export class ImportDotx { } const importedComp = convertToXmlComponent(headerXmlElement) as ImportedXmlComponent; const header = new HeaderWrapper(media, this.currentRelationshipId++, importedComp); - await this.addRelationToWrapper(relationFileInfo, zipContent, header); + await this.addRelationshipToWrapper(relationFileInfo, zipContent, header, media); headers.push({ type: headerRef.type, header }); } @@ -106,7 +113,7 @@ export class ImportDotx { } const importedComp = convertToXmlComponent(footerXmlElement) as ImportedXmlComponent; const footer = new FooterWrapper(media, this.currentRelationshipId++, importedComp); - await this.addRelationToWrapper(relationFileInfo, zipContent, footer); + await this.addRelationshipToWrapper(relationFileInfo, zipContent, footer, media); footers.push({ type: footerRef.type, footer }); } @@ -120,29 +127,43 @@ export class ImportDotx { return templateDocument; } - public async addRelationToWrapper( + private async addRelationshipToWrapper( relationhipFile: IRelationshipFileInfo, zipContent: JSZip, wrapper: HeaderWrapper | FooterWrapper, + media: Media, ): Promise { - 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"); + + if (!refFile) { + return; } + + const xmlRef = await refFile.async("text"); + const wrapperImagesReferences = this.findReferenceFiles(xmlRef).filter((r) => r.type === RelationshipType.IMAGE); + const hyperLinkReferences = this.findReferenceFiles(xmlRef).filter((r) => r.type === RelationshipType.HYPERLINK); + for (const r of wrapperImagesReferences) { const buffer = await zipContent.files[`word/${r.target}`].async("nodebuffer"); - wrapper.addImageRelationship(buffer, r.id); + const mediaData = media.addMedia(buffer, r.id); + wrapper.Relationships.createRelationship( + mediaData.referenceId, + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", + `media/${mediaData.fileName}`, + ); } + for (const r of hyperLinkReferences) { - wrapper.addHyperlinkRelationship(r.target, r.id, "External"); + wrapper.Relationships.createRelationship( + r.id, + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink", + r.target, + TargetModeType.EXTERNAL, + ); } } - public findReferenceFiles(xmlData: string): IRelationshipFileInfo[] { + private findReferenceFiles(xmlData: string): IRelationshipFileInfo[] { const xmlObj = xml2js(xmlData, { compact: true }) as XMLElementCompact; const relationXmlArray = Array.isArray(xmlObj.Relationships.Relationship) ? xmlObj.Relationships.Relationship @@ -162,7 +183,7 @@ export class ImportDotx { return relationships; } - public extractDocumentRefs(xmlData: string): IDocumentRefs { + private extractDocumentRefs(xmlData: string): IDocumentRefs { const xmlObj = xml2js(xmlData, { compact: true }) as XMLElementCompact; const sectionProp = xmlObj["w:document"]["w:body"]["w:sectPr"]; @@ -208,13 +229,13 @@ export class ImportDotx { return { headers, footers }; } - public titlePageIsDefined(xmlData: string): boolean { + private titlePageIsDefined(xmlData: string): boolean { const xmlObj = xml2js(xmlData, { compact: true }) as XMLElementCompact; const sectionProp = xmlObj["w:document"]["w:body"]["w:sectPr"]; return sectionProp["w:titlePg"] !== undefined; } - public parseRefId(str: string): number { + private parseRefId(str: string): number { const match = /^rId(\d+)$/.exec(str); if (match === null) { throw new Error("Invalid ref id");