diff --git a/src/export/packer/next-compiler.ts b/src/export/packer/next-compiler.ts index b1b80682c1..197edf2b49 100644 --- a/src/export/packer/next-compiler.ts +++ b/src/export/packer/next-compiler.ts @@ -53,17 +53,18 @@ export class Compiler { } } - for (const data of file.Media.Array) { const mediaData = data.stream; zip.file(`word/media/${data.fileName}`, mediaData); } - for (let header of file.Headers) { + + for (const header of file.Headers) { for (const data of header.media.Array) { zip.file(`word/media/${data.fileName}`, data.stream); } } - for (let footer of file.Footers) { + + for (const footer of file.Footers) { for (const data of footer.media.Array) { zip.file(`word/media/${data.fileName}`, data.stream); } @@ -134,12 +135,12 @@ export class Compiler { }; } - /* By default docx collapse empty tags. -> . this function mimic it - so comparing (diff) original docx file and the library output is easier */ - collapseEmptyTags(xmlData : string) : string { - const regEx = /<(([^ <>]+)[^<>]*)><\/\2>/g; - let collapsed = xmlData.replace(regEx, '<$1/>'); - return collapsed; - } + so comparing (diff) original docx file and the library output is easier + Currently not used, so commenting out */ + // private collapseEmptyTags(xmlData: string): string { + // const regEx = /<(([^ <>]+)[^<>]*)><\/\2>/g; + // const collapsed = xmlData.replace(regEx, "<$1/>"); + // return collapsed; + // } } diff --git a/src/file/core-properties/properties.ts b/src/file/core-properties/properties.ts index ce51fe2029..4b1cb21766 100644 --- a/src/file/core-properties/properties.ts +++ b/src/file/core-properties/properties.ts @@ -1,9 +1,8 @@ import { XmlComponent } from "file/xml-components"; +import { ITemplateDocument } from "importDocx/importDocx"; import { DocumentAttributes } from "../document/document-attributes"; import { Created, Creator, Description, Keywords, LastModifiedBy, Modified, Revision, Subject, Title } from "./components"; -import { TemplateDocument } from 'importDocx/importDocx' - export interface IPropertiesOptions { title?: string; subject?: string; @@ -14,7 +13,7 @@ export interface IPropertiesOptions { revision?: string; externalStyles?: string; - templateDocument? : TemplateDocument; + templateDocument?: ITemplateDocument; } export class CoreProperties extends XmlComponent { diff --git a/src/file/document/body/section-properties/section-properties.spec.ts b/src/file/document/body/section-properties/section-properties.spec.ts index 991926f14f..8da53c8e57 100644 --- a/src/file/document/body/section-properties/section-properties.spec.ts +++ b/src/file/document/body/section-properties/section-properties.spec.ts @@ -1,7 +1,7 @@ import { expect } from "chai"; import { Formatter } from "../../../../export/formatter"; -import { PageBorderOffsetFrom, PageNumberFormat, FooterReferenceType, HeaderReferenceType } from "./"; +import { FooterReferenceType, HeaderReferenceType, PageBorderOffsetFrom, PageNumberFormat } from "./"; import { SectionProperties } from "./section-properties"; describe("SectionProperties", () => { diff --git a/src/file/file.ts b/src/file/file.ts index da9b289cc9..4cda13f7e7 100644 --- a/src/file/file.ts +++ b/src/file/file.ts @@ -93,7 +93,7 @@ export class File { if (!templateHeaders) { this.createHeader(); } else { - for (let templateHeader of templateHeaders) { + for (const templateHeader of templateHeaders) { this.addHeaderToDocument(templateHeader.header, templateHeader.type); } } @@ -102,7 +102,7 @@ export class File { if (!templateFooters) { this.createFooter(); } else { - for (let templateFooter of templateFooters) { + for (const templateFooter of templateFooters) { this.addFooterToDocument(templateFooter.footer, templateFooter.type); } } @@ -127,16 +127,16 @@ export class File { this.footNotes = new FootNotes(); - let headersOptions: IHeaderOptions[] = []; - for (let documentHeader of this.documentHeaders) { + const headersOptions: IHeaderOptions[] = []; + for (const documentHeader of this.documentHeaders) { headersOptions.push({ headerId: documentHeader.header.Header.ReferenceId, headerType: documentHeader.type, }); } - let footersOptions: IFooterOptions[] = []; - for (let documentFooter of this.documentFooters) { + const footersOptions: IFooterOptions[] = []; + for (const documentFooter of this.documentFooters) { footersOptions.push({ footerId: documentFooter.footer.Footer.ReferenceId, footerType: documentFooter.type, @@ -148,8 +148,7 @@ export class File { headers: headersOptions, footers: footersOptions, }; - } - else { + } else { sectionPropertiesOptions.headers = headersOptions; sectionPropertiesOptions.footers = footersOptions; } @@ -225,7 +224,7 @@ export class File { return header; } - private addHeaderToDocument(header: HeaderWrapper, type: HeaderReferenceType = HeaderReferenceType.DEFAULT) { + private addHeaderToDocument(header: HeaderWrapper, type: HeaderReferenceType = HeaderReferenceType.DEFAULT): void { this.documentHeaders.push({ header, type }); this.docRelationships.createRelationship( header.Header.ReferenceId, @@ -241,7 +240,7 @@ export class File { return footer; } - private addFooterToDocument(footer: FooterWrapper, type: FooterReferenceType = FooterReferenceType.DEFAULT) { + private addFooterToDocument(footer: FooterWrapper, type: FooterReferenceType = FooterReferenceType.DEFAULT): void { this.documentFooters.push({ footer, type }); this.docRelationships.createRelationship( footer.Footer.ReferenceId, diff --git a/src/file/header-wrapper.ts b/src/file/header-wrapper.ts index 37b7981848..11e7587edb 100644 --- a/src/file/header-wrapper.ts +++ b/src/file/header-wrapper.ts @@ -1,11 +1,10 @@ +import { IMediaData } from "file/media"; import { XmlComponent } from "file/xml-components"; import { Header } from "./header/header"; import { Image, Media } from "./media"; import { ImageParagraph, Paragraph } from "./paragraph"; import { Relationships } from "./relationships"; import { Table } from "./table"; -import { IMediaData } from 'file/media'; - export class HeaderWrapper { private readonly header: Header; @@ -13,7 +12,7 @@ export class HeaderWrapper { public readonly media = new Media(); // constructor(private readonly media: Media, referenceId: number, initContent? : XmlComponent) { - constructor(referenceId: number, initContent? : XmlComponent) { + constructor(referenceId: number, initContent?: XmlComponent) { this.header = new Header(referenceId, initContent); this.relationships = new Relationships(); } @@ -40,7 +39,7 @@ export class HeaderWrapper { this.header.addChildElement(childElement); } - public addImageRelation(image: Buffer, refId : number, width?: number, height?: number) : IMediaData { + public addImageRelation(image: Buffer, refId: number, width?: number, height?: number): IMediaData { const mediaData = this.media.addMedia(image, refId, width, height); this.relationships.createRelationship( refId, @@ -49,9 +48,9 @@ export class HeaderWrapper { ); return mediaData; } - + public createImage(image: Buffer, width?: number, height?: number): void { - let mediaData = this.addImageRelation(image, this.relationships.RelationshipCount, width, height); + const mediaData = this.addImageRelation(image, this.relationships.RelationshipCount, width, height); this.addImage(new Image(new ImageParagraph(mediaData))); } diff --git a/src/file/header/header.ts b/src/file/header/header.ts index 14bfac2aba..d4d73b3912 100644 --- a/src/file/header/header.ts +++ b/src/file/header/header.ts @@ -39,8 +39,8 @@ export class Header extends XmlComponent { cx6: "http://schemas.microsoft.com/office/drawing/2016/5/12/chartex", cx7: "http://schemas.microsoft.com/office/drawing/2016/5/13/chartex", cx8: "http://schemas.microsoft.com/office/drawing/2016/5/14/chartex", - w16cid: "http://schemas.microsoft.com/office/word/2016/wordml/cid", - w16se: "http://schemas.microsoft.com/office/word/2015/wordml/symex" + w16cid: "http://schemas.microsoft.com/office/word/2016/wordml/cid", + w16se: "http://schemas.microsoft.com/office/word/2015/wordml/symex", }), ); } diff --git a/src/file/xml-components/xml-component.ts b/src/file/xml-components/xml-component.ts index a0f41500b5..a3d0a6186f 100644 --- a/src/file/xml-components/xml-component.ts +++ b/src/file/xml-components/xml-component.ts @@ -5,7 +5,7 @@ export { BaseXmlComponent }; export abstract class XmlComponent extends BaseXmlComponent { public root: Array; - constructor(rootKey: string, initContent? : XmlComponent) { + constructor(rootKey: string, initContent?: XmlComponent) { super(rootKey); this.root = initContent ? initContent.root : new Array(); } diff --git a/src/importDocx/importDocx.ts b/src/importDocx/importDocx.ts index 73a0114933..1207f7fda8 100644 --- a/src/importDocx/importDocx.ts +++ b/src/importDocx/importDocx.ts @@ -1,152 +1,154 @@ -import * as JSZip from "jszip"; import * as fastXmlParser from "fast-xml-parser"; -import { convertToXmlComponent, parseOptions, ImportedXmlComponent } from "file/xml-components"; -import { HeaderWrapper } from 'file/header-wrapper'; -import { FooterWrapper } from 'file/footer-wrapper'; -import { HeaderReferenceType } from 'file/document/body/section-properties/header-reference'; -import { FooterReferenceType } from 'file/document/body/section-properties/footer-reference'; -// import { RelationshipType } from 'file/relationships/relationship/relationship'; +import * as JSZip from "jszip"; + +import { FooterReferenceType } from "file/document/body/section-properties/footer-reference"; +import { HeaderReferenceType } from "file/document/body/section-properties/header-reference"; +import { FooterWrapper } from "file/footer-wrapper"; +import { HeaderWrapper } from "file/header-wrapper"; +import { convertToXmlComponent, ImportedXmlComponent, parseOptions } from "file/xml-components"; 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/header": "header", + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer": "footer", + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image": "image", +}; + +interface IDocumentRefs { + headers: Array<{ id: number; type: HeaderReferenceType }>; + footers: Array<{ id: number; type: FooterReferenceType }>; } -interface DocumentRefs { - headers : {id : number, type: HeaderReferenceType}[], - footers : {id : number, type: FooterReferenceType}[] +interface IRelationFileInfo { + id: number; + targetFile: string; + type: "header" | "footer" | "image"; } -type RelationFileInfo = {id : number, targetFile: string, type: 'header' | 'footer' | 'image'}; +type DocumentHeaders = Array<{ type: HeaderReferenceType; header: HeaderWrapper }>; +type DocumentFooters = Array<{ type: FooterReferenceType; footer: FooterWrapper }>; -type DocumentHeaders = {type : HeaderReferenceType, header : HeaderWrapper}[]; -type DocumentFooters = {type : FooterReferenceType, footer : FooterWrapper}[]; - -export interface TemplateDocument { - currentRelationshipId : number; - headers : DocumentHeaders, - footers : DocumentFooters, +export interface ITemplateDocument { + currentRelationshipId: number; + headers: DocumentHeaders; + footers: DocumentFooters; } export class ImportDocx { - - private currentRelationshipId: number = 1; + private currentRelationshipId: number; constructor() { + this.currentRelationshipId = 1; } + public async extract(data: Buffer): Promise { + const zipContent = await JSZip.loadAsync(data); + const documentContent = zipContent.files["word/document.xml"]; + const documentRefs: IDocumentRefs = this.extractDocumentRefs(await documentContent.async("text")); - async extract(data : Buffer) : Promise { - let zipContent = await JSZip.loadAsync(data); + const relationshipContent = zipContent.files["word/_rels/document.xml.rels"]; + const documentRelations: IRelationFileInfo[] = this.findReferenceFiles(await relationshipContent.async("text")); - let documentContent = zipContent['files']['word/document.xml']; - const documentRefs : DocumentRefs = this.extractDocumentRefs(await documentContent.async('text')) - - let relationshipContent = zipContent['files']['word/_rels/document.xml.rels']; - const documentRelations : RelationFileInfo[] = this.findReferenceFiles(await relationshipContent.async('text')); - - let headers : DocumentHeaders = []; - for(let headerRef of documentRefs.headers) { - const headerKey = 'w:hdr'; - const relationFileInfo = documentRelations.find(rel => rel.id === headerRef.id); - if (relationFileInfo == null) { - throw `can not find target file for id ${headerRef.id}`; + const headers: DocumentHeaders = []; + for (const headerRef of documentRefs.headers) { + const headerKey = "w:hdr"; + const relationFileInfo = documentRelations.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.targetFile}`].async('text'); + + const xmlData = await zipContent.files[`word/${relationFileInfo.targetFile}`].async("text"); const xmlObj = fastXmlParser.parse(xmlData, parseOptions); - let importedComp = convertToXmlComponent(headerKey, xmlObj[headerKey]) as ImportedXmlComponent; - - let header = new HeaderWrapper(this.currentRelationshipId++, importedComp); + const importedComp = convertToXmlComponent(headerKey, xmlObj[headerKey]) as ImportedXmlComponent; + + const header = new HeaderWrapper(this.currentRelationshipId++, importedComp); await this.addImagesToWrapper(relationFileInfo, zipContent, header); - headers.push({type : headerRef.type, header}) + headers.push({ type: headerRef.type, header }); } - let footers : DocumentFooters = []; - for(let footerRef of documentRefs.footers) { - const footerKey = 'w:ftr' - const relationFileInfo = documentRelations.find(rel => rel.id === footerRef.id); - if (relationFileInfo == null) { - throw `can not find target file for id ${footerRef.id}`; + const footers: DocumentFooters = []; + for (const footerRef of documentRefs.footers) { + const footerKey = "w:ftr"; + const relationFileInfo = documentRelations.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.targetFile}`].async('text'); + const xmlData = await zipContent.files[`word/${relationFileInfo.targetFile}`].async("text"); const xmlObj = fastXmlParser.parse(xmlData, parseOptions); - let importedComp = convertToXmlComponent(footerKey, xmlObj[footerKey]) as ImportedXmlComponent; - - let footer = new FooterWrapper(this.currentRelationshipId++, importedComp); + const importedComp = convertToXmlComponent(footerKey, xmlObj[footerKey]) as ImportedXmlComponent; + + const footer = new FooterWrapper(this.currentRelationshipId++, importedComp); await this.addImagesToWrapper(relationFileInfo, zipContent, footer); - footers.push({type : footerRef.type, footer}) + footers.push({ type: footerRef.type, footer }); } - let templateDocument : TemplateDocument = {headers, footers, currentRelationshipId : this.currentRelationshipId} + const templateDocument: ITemplateDocument = { headers, footers, currentRelationshipId: this.currentRelationshipId }; return templateDocument; } - async addImagesToWrapper(relationFile : RelationFileInfo, zipContent, wrapper : HeaderWrapper | FooterWrapper) { - let wrapperImagesReferences : RelationFileInfo[] = []; - const refFile = zipContent['files'][`word/_rels/${relationFile.targetFile}.rels`]; + public async addImagesToWrapper(relationFile: IRelationFileInfo, zipContent: JSZip, wrapper: HeaderWrapper | FooterWrapper): Promise { + let wrapperImagesReferences: IRelationFileInfo[] = []; + const refFile = zipContent.files[`word/_rels/${relationFile.targetFile}.rels`]; if (refFile) { - const xmlRef = await refFile.async('text'); - wrapperImagesReferences = this.findReferenceFiles(xmlRef).filter(r => r.type === 'image'); + const xmlRef = await refFile.async("text"); + wrapperImagesReferences = this.findReferenceFiles(xmlRef).filter((r) => r.type === "image"); } - for (let r of wrapperImagesReferences) { - const buffer = await zipContent['files'][`word/${r.targetFile}`].async('nodebuffer'); + for (const r of wrapperImagesReferences) { + const buffer = await zipContent.files[`word/${r.targetFile}`].async("nodebuffer"); wrapper.addImageRelation(buffer, r.id); } } - - findReferenceFiles(xmlData : string) : RelationFileInfo[] { + public findReferenceFiles(xmlData: string): IRelationFileInfo[] { const xmlObj = fastXmlParser.parse(xmlData, parseOptions); - const relationXmlArray = Array.isArray(xmlObj['Relationships']['Relationship']) ? xmlObj['Relationships']['Relationship'] : [xmlObj['Relationships']['Relationship']]; - const relations : RelationFileInfo[] = relationXmlArray - .map(item => { + const relationXmlArray = Array.isArray(xmlObj.Relationships.Relationship) + ? xmlObj.Relationships.Relationship + : [xmlObj.Relationships.Relationship]; + const relations: IRelationFileInfo[] = relationXmlArray + .map((item) => { return { - id : this.parseRefId(item['_attr']['Id']), - type : schemeToType[item['_attr']['Type']], - targetFile : item['_attr']['Target'] - } + id: this.parseRefId(item._attr.Id), + type: schemeToType[item._attr.Type], + targetFile: item._attr.Target, + }; }) - .filter(item => item.type != null) + .filter((item) => item.type !== null); return relations; } - extractDocumentRefs(xmlData : string) : DocumentRefs { - + public extractDocumentRefs(xmlData: string): IDocumentRefs { const xmlObj = fastXmlParser.parse(xmlData, parseOptions); - const sectionProp = xmlObj['w:document']['w:body']['w:sectPr']; - - const headersXmlArray = Array.isArray(sectionProp['w:headerReference']) ? sectionProp['w:headerReference'] : [sectionProp['w:headerReference']]; - const headers = headersXmlArray - .map(item => { - return { - type : item['_attr']['w:type'], - id : this.parseRefId(item['_attr']['r:id']) - } - }); + const sectionProp = xmlObj["w:document"]["w:body"]["w:sectPr"]; - const footersXmlArray = Array.isArray(sectionProp['w:footerReference']) ? sectionProp['w:footerReference'] : [sectionProp['w:footerReference']]; - const footers = footersXmlArray - .map(item => { - return { - type : item['_attr']['w:type'], - id : this.parseRefId(item['_attr']['r:id']) - } - }); - - return {headers, footers} + const headersXmlArray = Array.isArray(sectionProp["w:headerReference"]) + ? sectionProp["w:headerReference"] + : [sectionProp["w:headerReference"]]; + const headers = headersXmlArray.map((item) => { + return { + type: item._attr["w:type"], + id: this.parseRefId(item._attr["r:id"]), + }; + }); + + const footersXmlArray = Array.isArray(sectionProp["w:footerReference"]) + ? sectionProp["w:footerReference"] + : [sectionProp["w:footerReference"]]; + const footers = footersXmlArray.map((item) => { + return { + type: item._attr["w:type"], + id: this.parseRefId(item._attr["r:id"]), + }; + }); + + return { headers, footers }; } - parseRefId(str : string) : number { - let match = /^rId(\d+)$/.exec(str); - if (match == null) { - throw 'invalid ref id'; + public parseRefId(str: string): number { + const match = /^rId(\d+)$/.exec(str); + if (match === null) { + throw new Error("Invalid ref id"); } - return parseInt(match[1]); - } - + return parseInt(match[1], 10); + } } - diff --git a/src/importDocx/index.ts b/src/importDocx/index.ts index 3827939939..0b1a5a85f8 100644 --- a/src/importDocx/index.ts +++ b/src/importDocx/index.ts @@ -1 +1 @@ -export * from './importDocx'; \ No newline at end of file +export * from "./importDocx"; diff --git a/src/index.ts b/src/index.ts index 0116405d20..5a2e7d56cd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,4 +4,3 @@ export { File as Document } from "./file"; export * from "./file"; export * from "./export"; export * from "./importDocx"; -