diff --git a/src/export/packer/next-compiler.spec.ts b/src/export/packer/next-compiler.spec.ts index fc02d4a814..774b71c26f 100644 --- a/src/export/packer/next-compiler.spec.ts +++ b/src/export/packer/next-compiler.spec.ts @@ -19,7 +19,7 @@ describe("Compiler", () => { const fileNames = Object.keys(zipFile.files).map((f) => zipFile.files[f].name); expect(fileNames).is.an.instanceof(Array); - expect(fileNames).has.length(17); + expect(fileNames).has.length(18); expect(fileNames).to.include("word/document.xml"); expect(fileNames).to.include("word/styles.xml"); expect(fileNames).to.include("docProps/core.xml"); @@ -29,6 +29,7 @@ describe("Compiler", () => { expect(fileNames).to.include("word/_rels/header1.xml.rels"); expect(fileNames).to.include("word/footer1.xml"); expect(fileNames).to.include("word/footnotes.xml"); + expect(fileNames).to.include("word/settings.xml"); expect(fileNames).to.include("word/_rels/footer1.xml.rels"); expect(fileNames).to.include("word/_rels/document.xml.rels"); expect(fileNames).to.include("[Content_Types].xml"); @@ -47,7 +48,7 @@ describe("Compiler", () => { const fileNames = Object.keys(zipFile.files).map((f) => zipFile.files[f].name); expect(fileNames).is.an.instanceof(Array); - expect(fileNames).has.length(25); + expect(fileNames).has.length(26); expect(fileNames).to.include("word/header1.xml"); expect(fileNames).to.include("word/_rels/header1.xml.rels"); diff --git a/src/export/packer/next-compiler.ts b/src/export/packer/next-compiler.ts index e89786c78f..af00b2bc92 100644 --- a/src/export/packer/next-compiler.ts +++ b/src/export/packer/next-compiler.ts @@ -23,6 +23,7 @@ interface IXmlifyedFileMapping { ContentTypes: IXmlifyedFile; AppProperties: IXmlifyedFile; FootNotes: IXmlifyedFile; + Settings: IXmlifyedFile; } export class Compiler { @@ -122,6 +123,10 @@ export class Compiler { data: xml(this.formatter.format(file.FootNotes)), path: "word/footnotes.xml", }, + Settings: { + data: xml(this.formatter.format(file.Settings)), + path: "word/settings.xml", + }, }; } } diff --git a/src/file/file.ts b/src/file/file.ts index 18bae5ce71..45af9e385a 100644 --- a/src/file/file.ts +++ b/src/file/file.ts @@ -12,6 +12,7 @@ import { Bookmark, Hyperlink, Paragraph, Run, TextRun } from "./paragraph"; import { Begin, End, Separate } from "./paragraph/run/field"; import { Tab } from "./paragraph/run/tab"; import { Relationships } from "./relationships"; +import { Settings } from "./settings"; import { Styles } from "./styles"; import { ExternalStylesFactory } from "./styles/external-styles-factory"; import { DefaultStylesFactory } from "./styles/factory"; @@ -29,6 +30,7 @@ export class File { private readonly headerWrapper: HeaderWrapper[] = []; private readonly footerWrapper: FooterWrapper[] = []; private readonly footNotes: FootNotes; + private readonly settings: Settings; private readonly contentTypes: ContentTypes; private readonly appProperties: AppProperties; @@ -109,6 +111,7 @@ export class File { sectionPropertiesOptions.footerId = footer.Footer.ReferenceId; } this.document = new Document(sectionPropertiesOptions); + this.settings = new Settings(); } public addTableOfContents(toc: TableOfContents): void { @@ -286,9 +289,17 @@ export class File { return this.footNotes; } + public get Settings(): Settings { + return this.settings; + } + public generateTablesOfContents(): void { // console.log("generateTablesOfContents"); - this.document.getTablesOfContents().forEach((child) => this.generateContent(child)); + const TOCs = this.document.getTablesOfContents(); + if (TOCs && TOCs.length) { + this.settings.addUpdateFields(); + TOCs.forEach((child) => this.generateContent(child)); + } } private generateContent(toc: TableOfContents): void { diff --git a/src/file/settings/index.ts b/src/file/settings/index.ts new file mode 100644 index 0000000000..d750485a16 --- /dev/null +++ b/src/file/settings/index.ts @@ -0,0 +1,2 @@ +export * from "./settings"; +export * from "./update-fields"; diff --git a/src/file/settings/settings.spec.ts b/src/file/settings/settings.spec.ts new file mode 100644 index 0000000000..9ba0016319 --- /dev/null +++ b/src/file/settings/settings.spec.ts @@ -0,0 +1,60 @@ +import { expect } from "chai"; +import { Formatter } from "../../export/formatter"; +import { Settings } from "./"; +describe("Settings", () => { + describe("#constructor", () => { + it("should create a empty Settings with correct rootKey", () => { + const settings = new Settings(); + const tree = new Formatter().format(settings); + let keys = Object.keys(tree); + expect(keys).is.an.instanceof(Array); + expect(keys).has.length(1); + expect(keys[0]).to.be.equal("w:settings"); + expect(tree["w:settings"]).is.an.instanceof(Array); + expect(tree["w:settings"]).has.length(1); + keys = Object.keys(tree["w:settings"][0]); + expect(keys).is.an.instanceof(Array); + expect(keys).has.length(1); + expect(keys[0]).to.be.equal("_attr"); + }); + }); + describe("#addUpdateFields", () => { + const assertSettingsWithUpdateFields = (settings: Settings) => { + const tree = new Formatter().format(settings); + let keys = Object.keys(tree); + expect(keys).is.an.instanceof(Array); + expect(keys).has.length(1); + expect(keys[0]).to.be.equal("w:settings"); + const rootArray = tree["w:settings"]; + expect(rootArray).is.an.instanceof(Array); + expect(rootArray).has.length(2); + keys = Object.keys(rootArray[0]); + expect(keys).is.an.instanceof(Array); + expect(keys).has.length(1); + expect(keys[0]).to.be.equal("_attr"); + keys = Object.keys(rootArray[1]); + expect(keys).is.an.instanceof(Array); + expect(keys).has.length(1); + expect(keys[0]).to.be.equal("w:updateFields"); + const updateFieldsArray = rootArray[1]["w:updateFields"]; + keys = Object.keys(updateFieldsArray[0]); + expect(keys).is.an.instanceof(Array); + expect(keys).has.length(1); + expect(keys[0]).to.be.equal("_attr"); + const updateFieldsAttr = updateFieldsArray[0]._attr; + expect(updateFieldsAttr["w:val"]).to.be.equal(true); + }; + it("should add a UpdateFields with value true", () => { + const settings = new Settings(); + settings.addUpdateFields(); + assertSettingsWithUpdateFields(settings); + }); + it("should add a UpdateFields with value true only once", () => { + const settings = new Settings(); + settings.addUpdateFields(); + assertSettingsWithUpdateFields(settings); + settings.addUpdateFields(); + assertSettingsWithUpdateFields(settings); + }); + }); +}); diff --git a/src/file/settings/settings.ts b/src/file/settings/settings.ts new file mode 100644 index 0000000000..d955de0177 --- /dev/null +++ b/src/file/settings/settings.ts @@ -0,0 +1,73 @@ +import { XmlAttributeComponent, XmlComponent } from "file/xml-components"; +import { UpdateFields } from "./update-fields"; +export interface ISettingsAttributesProperties { + wpc?: string; + mc?: string; + o?: string; + r?: string; + m?: string; + v?: string; + wp14?: string; + wp?: string; + w10?: string; + w?: string; + w14?: string; + w15?: string; + wpg?: string; + wpi?: string; + wne?: string; + wps?: string; + Ignorable?: string; +} +export class SettingsAttributes extends XmlAttributeComponent { + protected xmlKeys = { + wpc: "xmlns:wpc", + mc: "xmlns:mc", + o: "xmlns:o", + r: "xmlns:r", + m: "xmlns:m", + v: "xmlns:v", + wp14: "xmlns:wp14", + wp: "xmlns:wp", + w10: "xmlns:w10", + w: "xmlns:w", + w14: "xmlns:w14", + w15: "xmlns:w15", + wpg: "xmlns:wpg", + wpi: "xmlns:wpi", + wne: "xmlns:wne", + wps: "xmlns:wps", + Ignorable: "mc:Ignorable", + }; +} +export class Settings extends XmlComponent { + constructor() { + super("w:settings"); + this.root.push( + new SettingsAttributes({ + wpc: "http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas", + mc: "http://schemas.openxmlformats.org/markup-compatibility/2006", + o: "urn:schemas-microsoft-com:office:office", + r: "http://schemas.openxmlformats.org/officeDocument/2006/relationships", + m: "http://schemas.openxmlformats.org/officeDocument/2006/math", + v: "urn:schemas-microsoft-com:vml", + wp14: "http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing", + wp: "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing", + w10: "urn:schemas-microsoft-com:office:word", + w: "http://schemas.openxmlformats.org/wordprocessingml/2006/main", + w14: "http://schemas.microsoft.com/office/word/2010/wordml", + w15: "http://schemas.microsoft.com/office/word/2012/wordml", + wpg: "http://schemas.microsoft.com/office/word/2010/wordprocessingGroup", + wpi: "http://schemas.microsoft.com/office/word/2010/wordprocessingInk", + wne: "http://schemas.microsoft.com/office/word/2006/wordml", + wps: "http://schemas.microsoft.com/office/word/2010/wordprocessingShape", + Ignorable: "w14 w15 wp14", + }), + ); + } + public addUpdateFields(): void { + if (!this.root.find((child) => child instanceof UpdateFields)) { + this.addChildElement(new UpdateFields()); + } + } +} diff --git a/src/file/settings/update-fields.spec.ts b/src/file/settings/update-fields.spec.ts new file mode 100644 index 0000000000..70fad8685c --- /dev/null +++ b/src/file/settings/update-fields.spec.ts @@ -0,0 +1,40 @@ +import { expect } from "chai"; +import { Formatter } from "../../export/formatter"; +import { UpdateFields } from "./"; +const UF_TRUE = { + "w:updateFields": [ + { + _attr: { + "w:val": true, + }, + }, + ], +}; +const UF_FALSE = { + "w:updateFields": [ + { + _attr: { + "w:val": false, + }, + }, + ], +}; +describe("Update Fields", () => { + describe("#constructor", () => { + it("should construct a Update Fields with TRUE value by default", () => { + const uf = new UpdateFields(); + const tree = new Formatter().format(uf); + expect(tree).to.be.deep.equal(UF_TRUE); + }); + it("should construct a Update Fields with TRUE value", () => { + const uf = new UpdateFields(true); + const tree = new Formatter().format(uf); + expect(tree).to.be.deep.equal(UF_TRUE); + }); + it("should construct a Update Fields with FALSE value", () => { + const uf = new UpdateFields(false); + const tree = new Formatter().format(uf); + expect(tree).to.be.deep.equal(UF_FALSE); + }); + }); +}); diff --git a/src/file/settings/update-fields.ts b/src/file/settings/update-fields.ts new file mode 100644 index 0000000000..782aa13264 --- /dev/null +++ b/src/file/settings/update-fields.ts @@ -0,0 +1,19 @@ +import { XmlAttributeComponent, XmlComponent } from "file/xml-components"; +export interface IUpdateFieldsAttributesProperties { + enabled: boolean; +} +export class UpdateFieldsAttributes extends XmlAttributeComponent { + protected xmlKeys = { + enabled: "w:val", + }; +} +export class UpdateFields extends XmlComponent { + constructor(enabled: boolean = true) { + super("w:updateFields"); + this.root.push( + new UpdateFieldsAttributes({ + enabled, + }), + ); + } +}