Merge branch 'importDotx' of https://github.com/amitm02/docx into importDotx

This commit is contained in:
Dolan
2018-10-05 01:21:29 +01:00
4 changed files with 72 additions and 18 deletions

View File

@ -12,7 +12,7 @@ fs.readFile(filePath, (err, data) => {
importDotx.extract(data).then((templateDocument) => { importDotx.extract(data).then((templateDocument) => {
// This any needs fixing // This any needs fixing
const sectionProps = { const sectionProps = {
titlePage: true, titlePage: templateDocument.titlePageIsDefined,
} as any; } as any;
const doc = new Document(undefined, sectionProps, { const doc = new Document(undefined, sectionProps, {

View File

@ -55,6 +55,15 @@ export class FooterWrapper {
return mediaData; 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 { public createImage(image: Buffer, width?: number, height?: number): void {
const mediaData = this.addImageRelationship(image, this.relationships.RelationshipCount, width, height); const mediaData = this.addImageRelationship(image, this.relationships.RelationshipCount, width, height);
this.addImage(new Image(new ImageParagraph(mediaData))); this.addImage(new Image(new ImageParagraph(mediaData)));

View File

@ -55,6 +55,15 @@ export class HeaderWrapper {
return mediaData; 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 { public createImage(image: Buffer, width?: number, height?: number): void {
const mediaData = this.addImageRelationship(image, this.relationships.RelationshipCount, width, height); const mediaData = this.addImageRelationship(image, this.relationships.RelationshipCount, width, height);
this.addImage(new Image(new ImageParagraph(mediaData))); this.addImage(new Image(new ImageParagraph(mediaData)));

View File

@ -20,6 +20,7 @@ const schemeToType = {
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/header": "header", "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/footer": "footer",
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image": "image", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image": "image",
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink": "hyperlink",
}; };
interface IDocumentRefs { interface IDocumentRefs {
@ -29,8 +30,8 @@ interface IDocumentRefs {
interface IRelationshipFileInfo { interface IRelationshipFileInfo {
id: number; id: number;
targetFile: string; target: string;
type: "header" | "footer" | "image"; type: "header" | "footer" | "image" | "hyperlink";
} }
// Document Template // Document Template
@ -40,6 +41,7 @@ export interface IDocumentTemplate {
headers: IDocumentHeader[]; headers: IDocumentHeader[];
footers: IDocumentFooter[]; footers: IDocumentFooter[];
styles: Styles; styles: Styles;
titlePageIsDefined: boolean;
} }
export class ImportDotx { export class ImportDotx {
@ -58,6 +60,7 @@ export class ImportDotx {
const documentContent = zipContent.files["word/document.xml"]; const documentContent = zipContent.files["word/document.xml"];
const documentRefs: IDocumentRefs = this.extractDocumentRefs(await documentContent.async("text")); 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 relationshipContent = zipContent.files["word/_rels/document.xml.rels"];
const documentRelationships: IRelationshipFileInfo[] = this.findReferenceFiles(await relationshipContent.async("text")); const documentRelationships: IRelationshipFileInfo[] = this.findReferenceFiles(await relationshipContent.async("text"));
@ -70,13 +73,13 @@ export class ImportDotx {
throw new Error(`Can not find target file for id ${headerRef.id}`); 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 xmlObj = fastXmlParser.parse(xmlData, importParseOptions);
const importedComp = convertToXmlComponent(headerKey, xmlObj[headerKey]) as ImportedXmlComponent; const importedComp = convertToXmlComponent(headerKey, xmlObj[headerKey]) as ImportedXmlComponent;
const header = new HeaderWrapper(this.currentRelationshipId++, importedComp); 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 }); headers.push({ type: headerRef.type, header });
} }
@ -87,34 +90,45 @@ export class ImportDotx {
if (relationFileInfo === null || !relationFileInfo) { if (relationFileInfo === null || !relationFileInfo) {
throw new Error(`Can not find target file for id ${footerRef.id}`); 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 xmlObj = fastXmlParser.parse(xmlData, importParseOptions);
const importedComp = convertToXmlComponent(footerKey, xmlObj[footerKey]) as ImportedXmlComponent; const importedComp = convertToXmlComponent(footerKey, xmlObj[footerKey]) as ImportedXmlComponent;
const footer = new FooterWrapper(this.currentRelationshipId++, importedComp); 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 }); 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; return templateDocument;
} }
public async addImagesToWrapper( public async addRelationToWrapper(
relationFile: IRelationshipFileInfo, relationFile: IRelationshipFileInfo,
zipContent: JSZip, zipContent: JSZip,
wrapper: HeaderWrapper | FooterWrapper, wrapper: HeaderWrapper | FooterWrapper,
): Promise<void> { ): Promise<void> {
let wrapperImagesReferences: IRelationshipFileInfo[] = []; 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) { if (refFile) {
const xmlRef = await refFile.async("text"); const xmlRef = await refFile.async("text");
wrapperImagesReferences = this.findReferenceFiles(xmlRef).filter((r) => r.type === "image"); wrapperImagesReferences = this.findReferenceFiles(xmlRef).filter((r) => r.type === "image");
hyperLinkReferences = this.findReferenceFiles(xmlRef).filter((r) => r.type === "hyperlink");
} }
for (const r of wrapperImagesReferences) { 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); wrapper.addImageRelationship(buffer, r.id);
} }
for (const r of hyperLinkReferences) {
wrapper.addHyperlinkRelationship(r.target, r.id, "External");
}
} }
public findReferenceFiles(xmlData: string): IRelationshipFileInfo[] { public findReferenceFiles(xmlData: string): IRelationshipFileInfo[] {
@ -127,7 +141,7 @@ export class ImportDotx {
return { return {
id: this.parseRefId(item._attr.Id), id: this.parseRefId(item._attr.Id),
type: schemeToType[item._attr.Type], type: schemeToType[item._attr.Type],
targetFile: item._attr.Target, target: item._attr.Target,
}; };
}) })
.filter((item) => item.type !== null); .filter((item) => item.type !== null);
@ -135,12 +149,21 @@ export class ImportDotx {
} }
public extractDocumentRefs(xmlData: string): IDocumentRefs { public extractDocumentRefs(xmlData: string): IDocumentRefs {
interface IAttributedXML {
_attr: object;
}
const xmlObj = fastXmlParser.parse(xmlData, importParseOptions); const xmlObj = fastXmlParser.parse(xmlData, importParseOptions);
const sectionProp = xmlObj["w:document"]["w:body"]["w:sectPr"]; const sectionProp = xmlObj["w:document"]["w:body"]["w:sectPr"];
const headersXmlArray = Array.isArray(sectionProp["w:headerReference"]) const headerProps: undefined | IAttributedXML | IAttributedXML[] = sectionProp["w:headerReference"];
? sectionProp["w:headerReference"] let headersXmlArray: IAttributedXML[];
: [sectionProp["w:headerReference"]]; if (headerProps === undefined) {
headersXmlArray = [];
} else if (Array.isArray(headerProps)) {
headersXmlArray = headerProps;
} else {
headersXmlArray = [headerProps];
}
const headers = headersXmlArray.map((item) => { const headers = headersXmlArray.map((item) => {
return { return {
type: item._attr["w:type"], type: item._attr["w:type"],
@ -148,9 +171,16 @@ export class ImportDotx {
}; };
}); });
const footersXmlArray = Array.isArray(sectionProp["w:footerReference"]) const footerProps: undefined | IAttributedXML | IAttributedXML[] = sectionProp["w:footerReference"];
? sectionProp["w:footerReference"] let footersXmlArray: IAttributedXML[];
: [sectionProp["w:footerReference"]]; if (footerProps === undefined) {
footersXmlArray = [];
} else if (Array.isArray(footerProps)) {
footersXmlArray = footerProps;
} else {
footersXmlArray = [footerProps];
}
const footers = footersXmlArray.map((item) => { const footers = footersXmlArray.map((item) => {
return { return {
type: item._attr["w:type"], type: item._attr["w:type"],
@ -161,6 +191,12 @@ export class ImportDotx {
return { headers, footers }; 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 { public parseRefId(str: string): number {
const match = /^rId(\d+)$/.exec(str); const match = /^rId(\d+)$/.exec(str);
if (match === null) { if (match === null) {