From c07b5cf709d43058b8f27f3174ddb9af3a1e4ac3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20Mendon=C3=A7a?= Date: Thu, 20 Sep 2018 10:11:59 -0300 Subject: [PATCH 1/4] added settings.xml back --- src/export/packer/next-compiler.spec.ts | 5 +- src/export/packer/next-compiler.ts | 5 ++ src/file/file.ts | 13 ++++- src/file/settings/index.ts | 2 + src/file/settings/settings.spec.ts | 60 ++++++++++++++++++++ src/file/settings/settings.ts | 73 +++++++++++++++++++++++++ src/file/settings/update-fields.spec.ts | 40 ++++++++++++++ src/file/settings/update-fields.ts | 19 +++++++ 8 files changed, 214 insertions(+), 3 deletions(-) create mode 100644 src/file/settings/index.ts create mode 100644 src/file/settings/settings.spec.ts create mode 100644 src/file/settings/settings.ts create mode 100644 src/file/settings/update-fields.spec.ts create mode 100644 src/file/settings/update-fields.ts 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, + }), + ); + } +} From 12e2ae9e91afbb5f1e4368abe6c52431278a852d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20Mendon=C3=A7a?= Date: Thu, 20 Sep 2018 10:30:16 -0300 Subject: [PATCH 2/4] making leader a option field and test improvments --- .../paragraph/formatting/tab-stop.spec.ts | 25 +++++++++++++++++-- src/file/paragraph/formatting/tab-stop.ts | 4 +-- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/file/paragraph/formatting/tab-stop.spec.ts b/src/file/paragraph/formatting/tab-stop.spec.ts index bb9fa80d96..465b4deb36 100644 --- a/src/file/paragraph/formatting/tab-stop.spec.ts +++ b/src/file/paragraph/formatting/tab-stop.spec.ts @@ -1,7 +1,7 @@ import { assert } from "chai"; import { Utility } from "../../../tests/utility"; -import { LeftTabStop, MaxRightTabStop } from "./tab-stop"; +import { LeftTabStop, RightTabStop, MaxRightTabStop } from "./tab-stop"; describe("LeftTabStop", () => { let tabStop: LeftTabStop; @@ -28,7 +28,28 @@ describe("LeftTabStop", () => { }); describe("RightTabStop", () => { - // TODO + let tabStop: RightTabStop; + + beforeEach(() => { + tabStop = new RightTabStop(100, "dot"); + }); + + describe("#constructor()", () => { + it("should create a Tab Stop with correct attributes", () => { + const newJson = Utility.jsonify(tabStop); + const attributes = { + val: "right", + pos: 100, + leader: "dot", + }; + assert.equal(JSON.stringify(newJson.root[0].root[0].root), JSON.stringify(attributes)); + }); + + it("should create a Tab Stop with w:tab", () => { + const newJson = Utility.jsonify(tabStop); + assert.equal(newJson.root[0].rootKey, "w:tab"); + }); + }); }); describe("MaxRightTabStop", () => { diff --git a/src/file/paragraph/formatting/tab-stop.ts b/src/file/paragraph/formatting/tab-stop.ts index 3159a12177..bcaeee8371 100644 --- a/src/file/paragraph/formatting/tab-stop.ts +++ b/src/file/paragraph/formatting/tab-stop.ts @@ -11,7 +11,7 @@ export class TabStop extends XmlComponent { export type TabValue = "left" | "right" | "center" | "bar" | "clear" | "decimal" | "end" | "num" | "start"; export type LeaderType = "dot" | "hyphen" | "middleDot" | "none" | "underscore"; -export class TabAttributes extends XmlAttributeComponent<{ val: TabValue; pos: string | number; leader: LeaderType }> { +export class TabAttributes extends XmlAttributeComponent<{ val: TabValue; pos: string | number; leader?: LeaderType }> { protected xmlKeys = { val: "w:val", pos: "w:pos", leader: "w:leader" }; } @@ -22,7 +22,7 @@ export class TabStopItem extends XmlComponent { new TabAttributes({ val: value, pos: position, - leader: leader || "none", + leader, }), ); } From 4805efad2ef190345e8ea54c016b96101e6c1f3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20Mendon=C3=A7a?= Date: Thu, 20 Sep 2018 10:31:49 -0300 Subject: [PATCH 3/4] organized imports --- src/file/paragraph/formatting/tab-stop.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/file/paragraph/formatting/tab-stop.spec.ts b/src/file/paragraph/formatting/tab-stop.spec.ts index 465b4deb36..5b324623f5 100644 --- a/src/file/paragraph/formatting/tab-stop.spec.ts +++ b/src/file/paragraph/formatting/tab-stop.spec.ts @@ -1,7 +1,7 @@ import { assert } from "chai"; import { Utility } from "../../../tests/utility"; -import { LeftTabStop, RightTabStop, MaxRightTabStop } from "./tab-stop"; +import { LeftTabStop, MaxRightTabStop, RightTabStop } from "./tab-stop"; describe("LeftTabStop", () => { let tabStop: LeftTabStop; From 1cff104baed952b67c91384484dc51528d593b98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20Mendon=C3=A7a?= Date: Thu, 20 Sep 2018 10:47:10 -0300 Subject: [PATCH 4/4] correct sdt objects of table of contents --- .../{std-content.ts => sdt-content.ts} | 4 +- .../{std-properties.ts => sdt-properties.ts} | 4 +- .../table-of-contents.spec.ts | 98 ++++++++++++------- .../table-of-contents/table-of-contents.ts | 12 +-- 4 files changed, 73 insertions(+), 45 deletions(-) rename src/file/table-of-contents/{std-content.ts => sdt-content.ts} (76%) rename src/file/table-of-contents/{std-properties.ts => sdt-properties.ts} (68%) diff --git a/src/file/table-of-contents/std-content.ts b/src/file/table-of-contents/sdt-content.ts similarity index 76% rename from src/file/table-of-contents/std-content.ts rename to src/file/table-of-contents/sdt-content.ts index cbab4c8440..a059886b42 100644 --- a/src/file/table-of-contents/std-content.ts +++ b/src/file/table-of-contents/sdt-content.ts @@ -1,9 +1,9 @@ import { Paragraph } from "file/paragraph"; import { XmlComponent } from "file/xml-components"; -export class StdContent extends XmlComponent { +export class SdtContent extends XmlComponent { constructor() { - super("w:stdContent"); + super("w:sdtContent"); } public addGeneratedContent(paragraph: Paragraph): void { diff --git a/src/file/table-of-contents/std-properties.ts b/src/file/table-of-contents/sdt-properties.ts similarity index 68% rename from src/file/table-of-contents/std-properties.ts rename to src/file/table-of-contents/sdt-properties.ts index 628e92840c..bb8ef77a6b 100644 --- a/src/file/table-of-contents/std-properties.ts +++ b/src/file/table-of-contents/sdt-properties.ts @@ -1,9 +1,9 @@ import { XmlComponent } from "file/xml-components"; import { Alias } from "./alias"; -export class StdProperties extends XmlComponent { +export class SdtProperties extends XmlComponent { constructor(alias: string) { - super("w:stdPr"); + super("w:sdtPr"); this.root.push(new Alias(alias)); } } diff --git a/src/file/table-of-contents/table-of-contents.spec.ts b/src/file/table-of-contents/table-of-contents.spec.ts index 98c6a36936..d29b3ffc75 100644 --- a/src/file/table-of-contents/table-of-contents.spec.ts +++ b/src/file/table-of-contents/table-of-contents.spec.ts @@ -4,39 +4,14 @@ import { Formatter } from "../../export/formatter"; import { TableOfContents } from "./"; const DEFAULT_TOC = { - "w:p": [ + "w:sdt": [ { - "w:pPr": [], - }, - { - "w:r": [ + "w:sdtPr": [ { - "w:rPr": [], - }, - { - "w:fldChar": [ + "w:alias": [ { _attr: { - "w:fldCharType": "begin", - }, - }, - ], - }, - { - "w:instrText": [ - { - _attr: { - "xml:space": "preserve", - }, - }, - 'TOC \\o "1-6"', - ], - }, - { - "w:fldChar": [ - { - _attr: { - "w:fldCharType": "separate", + "w:val": "Table of Contents", }, }, ], @@ -44,16 +19,69 @@ const DEFAULT_TOC = { ], }, { - "w:r": [ + "w:sdtContent": [ { - "w:rPr": [], + "w:p": [ + { + "w:pPr": [], + }, + { + "w:r": [ + { + "w:rPr": [], + }, + { + "w:fldChar": [ + { + _attr: { + "w:fldCharType": "begin", + }, + }, + ], + }, + { + "w:instrText": [ + { + _attr: { + "xml:space": "preserve", + }, + }, + 'TOC \\o "1-6"', + ], + }, + { + "w:fldChar": [ + { + _attr: { + "w:fldCharType": "separate", + }, + }, + ], + }, + ], + }, + ], }, { - "w:fldChar": [ + "w:p": [ { - _attr: { - "w:fldCharType": "end", - }, + "w:pPr": [], + }, + { + "w:r": [ + { + "w:rPr": [], + }, + { + "w:fldChar": [ + { + _attr: { + "w:fldCharType": "end", + }, + }, + ], + }, + ], }, ], }, diff --git a/src/file/table-of-contents/table-of-contents.ts b/src/file/table-of-contents/table-of-contents.ts index 7a31373143..f6f70558f9 100644 --- a/src/file/table-of-contents/table-of-contents.ts +++ b/src/file/table-of-contents/table-of-contents.ts @@ -3,22 +3,22 @@ import { Paragraph } from "file/paragraph"; import { Run } from "file/paragraph/run"; import { Begin, End, Separate } from "file/paragraph/run/field"; import { XmlComponent } from "file/xml-components"; -import { StdContent } from "./std-content"; -import { StdProperties } from "./std-properties"; +import { SdtContent } from "./sdt-content"; +import { SdtProperties } from "./sdt-properties"; import { TableOfContentsInstruction } from "./table-of-contents-instruction"; export class TableOfContents extends XmlComponent { // private readonly tocProperties: TableOfContentsProperties; - private readonly properties: StdProperties; + private readonly properties: SdtProperties; - private readonly content: StdContent; + private readonly content: SdtContent; private readonly instruction: TableOfContentsInstruction; constructor(/*tocProperties?: TableOfContentsProperties*/) { super("w:sdt"); - this.properties = new StdProperties("Table of Contents"); - this.content = new StdContent(); + this.properties = new SdtProperties("Table of Contents"); + this.content = new SdtContent(); this.instruction = new TableOfContentsInstruction(); this.root.push(this.properties); this.root.push(this.content);