Add lifecycles to add image

This commit is contained in:
Dolan
2018-12-05 00:05:11 +00:00
parent 8fd99052fb
commit 39066fb5f2
7 changed files with 92 additions and 69 deletions

View File

@ -36,6 +36,10 @@ export class Compiler {
public async compile(file: File): Promise<JSZip> { public async compile(file: File): Promise<JSZip> {
const zip = new JSZip(); const zip = new JSZip();
// Run precompile steps
file.onCompile();
file.Headers.forEach((header) => header.onCompile());
const xmlifiedFileMapping = this.xmlifyFile(file); const xmlifiedFileMapping = this.xmlifyFile(file);
for (const key in xmlifiedFileMapping) { for (const key in xmlifiedFileMapping) {

View File

@ -13,10 +13,12 @@ import { IFileProperties } from "./file-properties";
import { FooterWrapper, IDocumentFooter } from "./footer-wrapper"; import { FooterWrapper, IDocumentFooter } from "./footer-wrapper";
import { FootNotes } from "./footnotes"; import { FootNotes } from "./footnotes";
import { HeaderWrapper, IDocumentHeader } from "./header-wrapper"; import { HeaderWrapper, IDocumentHeader } from "./header-wrapper";
import { IOnCompile } from "./life-cycles";
import { Image, Media } from "./media"; import { Image, Media } from "./media";
import { Numbering } from "./numbering"; import { Numbering } from "./numbering";
import { Bookmark, Hyperlink, Paragraph } from "./paragraph"; import { Bookmark, Hyperlink, Paragraph } from "./paragraph";
import { Relationships } from "./relationships"; import { Relationships } from "./relationships";
import { TargetModeType } from "./relationships/relationship/relationship";
import { Settings } from "./settings"; import { Settings } from "./settings";
import { Styles } from "./styles"; import { Styles } from "./styles";
import { ExternalStylesFactory } from "./styles/external-styles-factory"; import { ExternalStylesFactory } from "./styles/external-styles-factory";
@ -24,7 +26,7 @@ import { DefaultStylesFactory } from "./styles/factory";
import { Table } from "./table"; import { Table } from "./table";
import { TableOfContents } from "./table-of-contents"; import { TableOfContents } from "./table-of-contents";
export class File { export class File implements IOnCompile {
// tslint:disable-next-line:readonly-keyword // tslint:disable-next-line:readonly-keyword
private currentRelationshipId: number = 1; private currentRelationshipId: number = 1;
@ -147,7 +149,7 @@ export class File {
hyperlink.linkId, hyperlink.linkId,
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink",
link, link,
"External", TargetModeType.EXTERNAL,
); );
return hyperlink; 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 { private addHeaderToDocument(header: HeaderWrapper, type: HeaderReferenceType = HeaderReferenceType.DEFAULT): void {
this.headers.push({ header, type }); this.headers.push({ header, type });
this.docRelationships.createRelationship( this.docRelationships.createRelationship(

View File

@ -2,7 +2,8 @@ import { XmlComponent } from "file/xml-components";
import { FooterReferenceType } from "./document"; import { FooterReferenceType } from "./document";
import { Footer } from "./footer/footer"; 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 { ImageParagraph, Paragraph } from "./paragraph";
import { Relationships } from "./relationships"; import { Relationships } from "./relationships";
import { Table } from "./table"; import { Table } from "./table";
@ -12,7 +13,7 @@ export interface IDocumentFooter {
readonly type: FooterReferenceType; readonly type: FooterReferenceType;
} }
export class FooterWrapper { export class FooterWrapper implements IOnCompile {
private readonly footer: Footer; private readonly footer: Footer;
private readonly relationships: Relationships; private readonly relationships: Relationships;
@ -43,29 +44,8 @@ export class FooterWrapper {
this.footer.addChildElement(childElement); 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 { public createImage(image: Buffer | string | Uint8Array | ArrayBuffer, width?: number, height?: number): void {
// TODO const mediaData = this.media.addMedia(image, this.relationships.RelationshipCount, width, height);
// tslint:disable-next-line:no-any
const mediaData = this.addImageRelationship(image as any, this.relationships.RelationshipCount, width, height);
this.addImage(new Image(new ImageParagraph(mediaData))); this.addImage(new Image(new ImageParagraph(mediaData)));
} }
@ -74,6 +54,16 @@ export class FooterWrapper {
return this; 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 { public get Footer(): Footer {
return this.footer; return this.footer;
} }

View File

@ -2,7 +2,8 @@ import { XmlComponent } from "file/xml-components";
import { HeaderReferenceType } from "./document"; import { HeaderReferenceType } from "./document";
import { Header } from "./header/header"; 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 { ImageParagraph, Paragraph } from "./paragraph";
import { Relationships } from "./relationships"; import { Relationships } from "./relationships";
import { Table } from "./table"; import { Table } from "./table";
@ -12,7 +13,7 @@ export interface IDocumentHeader {
readonly type: HeaderReferenceType; readonly type: HeaderReferenceType;
} }
export class HeaderWrapper { export class HeaderWrapper implements IOnCompile {
private readonly header: Header; private readonly header: Header;
private readonly relationships: Relationships; private readonly relationships: Relationships;
@ -43,29 +44,8 @@ export class HeaderWrapper {
this.header.addChildElement(childElement); 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 { public createImage(image: Buffer | string | Uint8Array | ArrayBuffer, width?: number, height?: number): void {
// TODO const mediaData = this.media.addMedia(image, this.relationships.RelationshipCount, width, height);
// tslint:disable-next-line:no-any
const mediaData = this.addImageRelationship(image as any, this.relationships.RelationshipCount, width, height);
this.addImage(new Image(new ImageParagraph(mediaData))); this.addImage(new Image(new ImageParagraph(mediaData)));
} }
@ -74,6 +54,16 @@ export class HeaderWrapper {
return this; 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 { public get Header(): Header {
return this.header; return this.header;
} }

9
src/file/life-cycles.ts Normal file
View File

@ -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 = "";

View File

@ -17,7 +17,9 @@ export type RelationshipType =
| "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink" | "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink"
| "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes"; | "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes";
export type TargetModeType = "External"; export enum TargetModeType {
EXTERNAL = "External",
}
export class Relationship extends XmlComponent { export class Relationship extends XmlComponent {
constructor(id: string, type: RelationshipType, target: string, targetMode?: TargetModeType) { constructor(id: string, type: RelationshipType, target: string, targetMode?: TargetModeType) {

View File

@ -5,11 +5,11 @@ import { FooterReferenceType } from "file/document/body/section-properties/foote
import { HeaderReferenceType } from "file/document/body/section-properties/header-reference"; import { HeaderReferenceType } from "file/document/body/section-properties/header-reference";
import { FooterWrapper, IDocumentFooter } from "file/footer-wrapper"; import { FooterWrapper, IDocumentFooter } from "file/footer-wrapper";
import { HeaderWrapper, IDocumentHeader } from "file/header-wrapper"; import { HeaderWrapper, IDocumentHeader } from "file/header-wrapper";
import { convertToXmlComponent, ImportedXmlComponent } from "file/xml-components";
import { Media } from "file/media"; import { Media } from "file/media";
import { TargetModeType } from "file/relationships/relationship/relationship";
import { Styles } from "file/styles"; import { Styles } from "file/styles";
import { ExternalStylesFactory } from "file/styles/external-styles-factory"; import { ExternalStylesFactory } from "file/styles/external-styles-factory";
import { convertToXmlComponent, ImportedXmlComponent } from "file/xml-components";
const schemeToType = { const schemeToType = {
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/header": "header", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/header": "header",
@ -23,10 +23,17 @@ interface IDocumentRefs {
readonly footers: Array<{ readonly id: number; readonly type: FooterReferenceType }>; readonly footers: Array<{ readonly id: number; readonly type: FooterReferenceType }>;
} }
enum RelationshipType {
HEADER = "header",
FOOTER = "footer",
IMAGE = "image",
HYPERLINK = "hyperlink",
}
interface IRelationshipFileInfo { interface IRelationshipFileInfo {
readonly id: number; readonly id: number;
readonly target: string; readonly target: string;
readonly type: "header" | "footer" | "image" | "hyperlink"; readonly type: RelationshipType;
} }
// Document Template // Document Template
@ -83,7 +90,7 @@ export class ImportDotx {
} }
const importedComp = convertToXmlComponent(headerXmlElement) as ImportedXmlComponent; const importedComp = convertToXmlComponent(headerXmlElement) as ImportedXmlComponent;
const header = new HeaderWrapper(media, this.currentRelationshipId++, importedComp); 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 }); headers.push({ type: headerRef.type, header });
} }
@ -106,7 +113,7 @@ export class ImportDotx {
} }
const importedComp = convertToXmlComponent(footerXmlElement) as ImportedXmlComponent; const importedComp = convertToXmlComponent(footerXmlElement) as ImportedXmlComponent;
const footer = new FooterWrapper(media, this.currentRelationshipId++, importedComp); 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 }); footers.push({ type: footerRef.type, footer });
} }
@ -120,29 +127,43 @@ export class ImportDotx {
return templateDocument; return templateDocument;
} }
public async addRelationToWrapper( private async addRelationshipToWrapper(
relationhipFile: IRelationshipFileInfo, relationhipFile: IRelationshipFileInfo,
zipContent: JSZip, zipContent: JSZip,
wrapper: HeaderWrapper | FooterWrapper, wrapper: HeaderWrapper | FooterWrapper,
media: Media,
): Promise<void> { ): Promise<void> {
let wrapperImagesReferences: IRelationshipFileInfo[] = [];
let hyperLinkReferences: IRelationshipFileInfo[] = [];
const refFile = zipContent.files[`word/_rels/${relationhipFile.target}.rels`]; const refFile = zipContent.files[`word/_rels/${relationhipFile.target}.rels`];
if (refFile) {
const xmlRef = await refFile.async("text"); if (!refFile) {
wrapperImagesReferences = this.findReferenceFiles(xmlRef).filter((r) => r.type === "image"); return;
hyperLinkReferences = this.findReferenceFiles(xmlRef).filter((r) => r.type === "hyperlink");
} }
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) { for (const r of wrapperImagesReferences) {
const buffer = await zipContent.files[`word/${r.target}`].async("nodebuffer"); 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) { 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 xmlObj = xml2js(xmlData, { compact: true }) as XMLElementCompact;
const relationXmlArray = Array.isArray(xmlObj.Relationships.Relationship) const relationXmlArray = Array.isArray(xmlObj.Relationships.Relationship)
? xmlObj.Relationships.Relationship ? xmlObj.Relationships.Relationship
@ -162,7 +183,7 @@ export class ImportDotx {
return relationships; return relationships;
} }
public extractDocumentRefs(xmlData: string): IDocumentRefs { private extractDocumentRefs(xmlData: string): IDocumentRefs {
const xmlObj = xml2js(xmlData, { compact: true }) as XMLElementCompact; const xmlObj = xml2js(xmlData, { compact: true }) as XMLElementCompact;
const sectionProp = xmlObj["w:document"]["w:body"]["w:sectPr"]; const sectionProp = xmlObj["w:document"]["w:body"]["w:sectPr"];
@ -208,13 +229,13 @@ export class ImportDotx {
return { headers, footers }; return { headers, footers };
} }
public titlePageIsDefined(xmlData: string): boolean { private titlePageIsDefined(xmlData: string): boolean {
const xmlObj = xml2js(xmlData, { compact: true }) as XMLElementCompact; const xmlObj = xml2js(xmlData, { compact: true }) as XMLElementCompact;
const sectionProp = xmlObj["w:document"]["w:body"]["w:sectPr"]; const sectionProp = xmlObj["w:document"]["w:body"]["w:sectPr"];
return sectionProp["w:titlePg"] !== undefined; return sectionProp["w:titlePg"] !== undefined;
} }
public parseRefId(str: string): number { private parseRefId(str: string): number {
const match = /^rId(\d+)$/.exec(str); const match = /^rId(\d+)$/.exec(str);
if (match === null) { if (match === null) {
throw new Error("Invalid ref id"); throw new Error("Invalid ref id");