diff --git a/src/file/footer-wrapper.ts b/src/file/footer-wrapper.ts index bb6efbe25a..47d0f73928 100644 --- a/src/file/footer-wrapper.ts +++ b/src/file/footer-wrapper.ts @@ -3,6 +3,7 @@ import { IMediaData, Media } from "./media"; import { Paragraph } from "./paragraph"; import { Relationships } from "./relationships"; import { Table } from "./table"; +import { XmlComponent } from "."; export class FooterWrapper { private readonly footer: Footer; @@ -35,6 +36,10 @@ export class FooterWrapper { this.footer.addDrawing(imageData); } + public addChildElement(childElement: XmlComponent | string) { + this.footer.addChildElement(childElement); + } + public createImage(image: string): void { const mediaData = this.media.addMedia(image, this.relationships.RelationshipCount); this.relationships.createRelationship( diff --git a/src/file/header-wrapper.ts b/src/file/header-wrapper.ts index df15871bef..250919b489 100644 --- a/src/file/header-wrapper.ts +++ b/src/file/header-wrapper.ts @@ -3,6 +3,7 @@ import { IMediaData, Media } from "./media"; import { Paragraph } from "./paragraph"; import { Relationships } from "./relationships"; import { Table } from "./table"; +import { XmlComponent } from "."; export class HeaderWrapper { private readonly header: Header; @@ -35,6 +36,10 @@ export class HeaderWrapper { this.header.addDrawing(imageData); } + public addChildElement(childElement: XmlComponent | string) { + this.header.addChildElement(childElement); + } + public createImage(image: string): void { const mediaData = this.media.addMedia(image, this.relationships.RelationshipCount); this.relationships.createRelationship( diff --git a/src/file/styles/external-styles-factory.ts b/src/file/styles/external-styles-factory.ts index 315eb7452c..9757faf8d9 100644 --- a/src/file/styles/external-styles-factory.ts +++ b/src/file/styles/external-styles-factory.ts @@ -1,12 +1,6 @@ import { Styles } from "./"; import * as fastXmlParser from "fast-xml-parser"; -import { ImportedXmlComponent, ImportedRootElementAttributes } from "./../../file/xml-components"; - -const parseOptions = { - ignoreAttributes: false, - attributeNamePrefix: "", - attrNodeName: "_attr", -}; +import { ImportedXmlComponent, ImportedRootElementAttributes, parseOptions, convertToXmlComponent } from "./../../file/xml-components"; export class ExternalStylesFactory { /** @@ -44,19 +38,7 @@ export class ExternalStylesFactory { }); // convert the styles one by one - xmlStyles["w:style"].map((style) => this.convertElement("w:style", style)).forEach(importedStyle.push.bind(importedStyle)); - + xmlStyles["w:style"].map((style) => convertToXmlComponent("w:style", style)).forEach(importedStyle.push.bind(importedStyle)); return importedStyle; } - - convertElement(elementName: string, element: any): ImportedXmlComponent { - const xmlElement = new ImportedXmlComponent(elementName, element._attr); - if (typeof element === "object") { - Object.keys(element) - .filter((key) => key !== "_attr") - .map((item) => this.convertElement(item, element[item])) - .forEach(xmlElement.push.bind(xmlElement)); - } - return xmlElement; - } } diff --git a/src/file/xml-components/imported-xml-component.spec.ts b/src/file/xml-components/imported-xml-component.spec.ts index d7b638ba9f..e941d3f0ec 100644 --- a/src/file/xml-components/imported-xml-component.spec.ts +++ b/src/file/xml-components/imported-xml-component.spec.ts @@ -1,5 +1,52 @@ import { expect } from "chai"; -import { ImportedXmlComponent } from "./"; +import { ImportedXmlComponent, convertToXmlComponent } from "./"; + +const xmlString = ` + + + some value + + + Text 1 + + + Text 2 + + + `; + +const importedXmlElement = { + "w:p": { + _attr: { "w:one": "value 1", "w:two": "value 2" }, + "w:rPr": { "w:noProof": "some value" }, + "w:r": [{ _attr: { active: "true" }, "w:t": "Text 1" }, { _attr: { active: "true" }, "w:t": "Text 2" }], + }, +}; + +const convertedXmlElement = { + deleted: false, + rootKey: "w:p", + root: [ + { + deleted: false, + rootKey: "w:rPr", + root: [{ deleted: false, rootKey: "w:noProof", root: ["some value"] }], + }, + { + deleted: false, + rootKey: "w:r", + root: [{ deleted: false, rootKey: "w:t", root: ["Text 1"] }], + _attr: { active: "true" }, + }, + { + deleted: false, + rootKey: "w:r", + root: [{ deleted: false, rootKey: "w:t", root: ["Text 2"] }], + _attr: { active: "true" }, + }, + ], + _attr: { "w:one": "value 1", "w:two": "value 2" }, +}; describe("ImportedXmlComponent", () => { let importedXmlComponent: ImportedXmlComponent; @@ -31,4 +78,16 @@ describe("ImportedXmlComponent", () => { }); }); }); + + it("should create XmlComponent from xml string", () => { + const converted = ImportedXmlComponent.fromXmlString(xmlString); + expect(converted).to.eql(convertedXmlElement); + }); + + describe("convertToXmlComponent", () => { + it("should convert to xml component", () => { + const converted = convertToXmlComponent("w:p", importedXmlElement["w:p"]); + expect(converted).to.eql(convertedXmlElement); + }); + }); }); diff --git a/src/file/xml-components/imported-xml-component.ts b/src/file/xml-components/imported-xml-component.ts index 51af733c2e..41ad025ab5 100644 --- a/src/file/xml-components/imported-xml-component.ts +++ b/src/file/xml-components/imported-xml-component.ts @@ -1,4 +1,53 @@ import { XmlComponent, IXmlableObject } from "."; +import * as fastXmlParser from "fast-xml-parser"; +import { flatMap } from "lodash"; + +export const parseOptions = { + ignoreAttributes: false, + attributeNamePrefix: "", + attrNodeName: "_attr", +}; + +/** + * Converts the given xml element (in json format) into XmlComponent. + * Note: If element is array, them it will return ImportedXmlComponent[]. Example for given: + * element = [ + * { w:t: "val 1"}, + * { w:t: "val 2"} + * ] + * will return + * [ + * ImportedXmlComponent { rootKey: "w:t", root: [ "val 1" ]}, + * ImportedXmlComponent { rootKey: "w:t", root: [ "val 2" ]} + * ] + * + * @param elementName name (rootKey) of the XmlComponent + * @param element the xml element in json presentation + */ +export function convertToXmlComponent(elementName: string, element: any): ImportedXmlComponent | ImportedXmlComponent[] { + const xmlElement = new ImportedXmlComponent(elementName, element._attr); + if (Array.isArray(element)) { + const out: any[] = []; + element.forEach((itemInArray) => { + out.push(convertToXmlComponent(elementName, itemInArray)); + }); + return flatMap(out); + } else if (typeof element === "object") { + Object.keys(element) + .filter((key) => key !== "_attr") + .map((item) => convertToXmlComponent(item, element[item])) + .forEach((converted) => { + if (Array.isArray(converted)) { + converted.forEach(xmlElement.push.bind(xmlElement)); + } else { + xmlElement.push(converted); + } + }); + } else if (element !== "") { + xmlElement.push(element); + } + return xmlElement; +} /** * Represents imported xml component from xml file. @@ -53,6 +102,23 @@ export class ImportedXmlComponent extends XmlComponent { push(xmlComponent: XmlComponent) { this.root.push(xmlComponent); } + + /** + * Converts the xml string to a XmlComponent tree. + * + * @param importedContent xml content of the imported component + */ + static fromXmlString(importedContent: string): ImportedXmlComponent { + const imported = fastXmlParser.parse(importedContent, parseOptions); + const elementName = Object.keys(imported)[0]; + + const converted = convertToXmlComponent(elementName, imported[elementName]); + + if (Array.isArray(converted) && converted.length > 1) { + throw new Error("Invalid conversion, input must be one element."); + } + return Array.isArray(converted) ? converted[0] : converted; + } } /**