clean up settings; BREAKING: TableOfContents requires Document features.updateFields = true setting

This commit is contained in:
Tom Hunkapiller
2021-05-26 08:36:05 +03:00
parent da61a30eb8
commit 60b7ce4785
20 changed files with 389 additions and 350 deletions

View File

@ -9,6 +9,9 @@ import { File, HeadingLevel, Packer, Paragraph, StyleLevel, TableOfContents } fr
// Let's define the properties for generate a TOC for heading 1-5 and MySpectacularStyle, // Let's define the properties for generate a TOC for heading 1-5 and MySpectacularStyle,
// making the entries be hyperlinks for the paragraph // making the entries be hyperlinks for the paragraph
const doc = new File({ const doc = new File({
features: {
updateFields: true,
},
styles: { styles: {
paragraphStyles: [ paragraphStyles: [
{ {

View File

@ -20,10 +20,12 @@ import {
- https://docs.microsoft.com/en-us/dotnet/api/documentformat.openxml.wordprocessing.insertedrun - https://docs.microsoft.com/en-us/dotnet/api/documentformat.openxml.wordprocessing.insertedrun
- https://docs.microsoft.com/en-us/dotnet/api/documentformat.openxml.wordprocessing.deletedrun - https://docs.microsoft.com/en-us/dotnet/api/documentformat.openxml.wordprocessing.deletedrun
The method `addTrackRevisions()` adds an element `<w:trackRevisions />` to the `settings.xml` file. This specifies that the application shall track *new* revisions made to the existing document. The setting `features: { trackRevisions: true }` adds an element `<w:trackRevisions />` to the `settings.xml` file.
This specifies that the application shall track *new* revisions made to the existing document.
See also https://docs.microsoft.com/en-us/dotnet/api/documentformat.openxml.wordprocessing.trackrevisions See also https://docs.microsoft.com/en-us/dotnet/api/documentformat.openxml.wordprocessing.trackrevisions
Note that this setting enables to track *new changes* after teh file is generated, so this example will still show inserted and deleted text runs when you remove it. Note that this setting enables to track *new changes* after teh file is generated, so this example will still
show inserted and deleted text runs when you remove it.
*/ */
const paragraph = new Paragraph({ const paragraph = new Paragraph({

View File

@ -12,14 +12,29 @@ The complete documentation can be found [here](https://www.ecma-international.or
All you need to do is create a `TableOfContents` object and assign it to the document. All you need to do is create a `TableOfContents` object and assign it to the document.
```ts **Note**: updateFields feature must be enabled for TableOfContents to update correctly.
const toc = new TableOfContents("Summary", {
hyperlink: true,
headingStyleRange: "1-5",
stylesWithLevels: [new StyleLevel("MySpectacularStyle", 1)],
});
doc.addTableOfContents(toc); ```ts
const doc = new Document({
features: {
updateFields: true,
},
sections: [
{
children: [
new TableOfContents("Summary", {
hyperlink: true,
headingStyleRange: "1-5",
}),
new Paragraph({
text: "Header #1",
heading: HeadingLevel.HEADING_1,
pageBreakBefore: true,
}),
]
}
]
});
``` ```
## Table of Contents Options ## Table of Contents Options

View File

@ -70,7 +70,6 @@ export class Compiler {
} }
private xmlifyFile(file: File, prettify?: boolean): IXmlifyedFileMapping { private xmlifyFile(file: File, prettify?: boolean): IXmlifyedFileMapping {
file.verifyUpdateFields();
const documentRelationshipCount = file.Document.Relationships.RelationshipCount + 1; const documentRelationshipCount = file.Document.Relationships.RelationshipCount + 1;
const documentXmlData = xml( const documentXmlData = xml(
@ -78,7 +77,13 @@ export class Compiler {
viewWrapper: file.Document, viewWrapper: file.Document,
file, file,
}), }),
prettify, {
indent: prettify,
declaration: {
standalone: "yes",
encoding: "UTF-8",
},
},
); );
const documentMediaDatas = this.imageReplacer.getMediaData(documentXmlData, file.Media); const documentMediaDatas = this.imageReplacer.getMediaData(documentXmlData, file.Media);
@ -98,7 +103,12 @@ export class Compiler {
viewWrapper: file.Document, viewWrapper: file.Document,
file, file,
}), }),
prettify, {
indent: prettify,
declaration: {
encoding: "UTF-8",
},
},
); );
})(), })(),
path: "word/_rels/document.xml.rels", path: "word/_rels/document.xml.rels",
@ -118,7 +128,13 @@ export class Compiler {
viewWrapper: file.Document, viewWrapper: file.Document,
file, file,
}), }),
prettify, {
indent: prettify,
declaration: {
standalone: "yes",
encoding: "UTF-8",
},
},
), ),
path: "word/styles.xml", path: "word/styles.xml",
}, },
@ -129,6 +145,7 @@ export class Compiler {
file, file,
}), }),
{ {
indent: prettify,
declaration: { declaration: {
standalone: "yes", standalone: "yes",
encoding: "UTF-8", encoding: "UTF-8",
@ -143,7 +160,13 @@ export class Compiler {
viewWrapper: file.Document, viewWrapper: file.Document,
file, file,
}), }),
prettify, {
indent: prettify,
declaration: {
standalone: "yes",
encoding: "UTF-8",
},
},
), ),
path: "word/numbering.xml", path: "word/numbering.xml",
}, },
@ -153,7 +176,12 @@ export class Compiler {
viewWrapper: file.Document, viewWrapper: file.Document,
file, file,
}), }),
prettify, {
indent: prettify,
declaration: {
encoding: "UTF-8",
},
},
), ),
path: "_rels/.rels", path: "_rels/.rels",
}, },
@ -163,7 +191,12 @@ export class Compiler {
viewWrapper: headerWrapper, viewWrapper: headerWrapper,
file, file,
}), }),
prettify, {
indent: prettify,
declaration: {
encoding: "UTF-8",
},
},
); );
const mediaDatas = this.imageReplacer.getMediaData(xmlData, file.Media); const mediaDatas = this.imageReplacer.getMediaData(xmlData, file.Media);
@ -181,7 +214,12 @@ export class Compiler {
viewWrapper: headerWrapper, viewWrapper: headerWrapper,
file, file,
}), }),
prettify, {
indent: prettify,
declaration: {
encoding: "UTF-8",
},
},
), ),
path: `word/_rels/header${index + 1}.xml.rels`, path: `word/_rels/header${index + 1}.xml.rels`,
}; };
@ -192,7 +230,12 @@ export class Compiler {
viewWrapper: footerWrapper, viewWrapper: footerWrapper,
file, file,
}), }),
prettify, {
indent: prettify,
declaration: {
encoding: "UTF-8",
},
},
); );
const mediaDatas = this.imageReplacer.getMediaData(xmlData, file.Media); const mediaDatas = this.imageReplacer.getMediaData(xmlData, file.Media);
@ -210,7 +253,12 @@ export class Compiler {
viewWrapper: footerWrapper, viewWrapper: footerWrapper,
file, file,
}), }),
prettify, {
indent: prettify,
declaration: {
encoding: "UTF-8",
},
},
), ),
path: `word/_rels/footer${index + 1}.xml.rels`, path: `word/_rels/footer${index + 1}.xml.rels`,
}; };
@ -221,7 +269,12 @@ export class Compiler {
viewWrapper: headerWrapper, viewWrapper: headerWrapper,
file, file,
}), }),
prettify, {
indent: prettify,
declaration: {
encoding: "UTF-8",
},
},
); );
const mediaDatas = this.imageReplacer.getMediaData(tempXmlData, file.Media); const mediaDatas = this.imageReplacer.getMediaData(tempXmlData, file.Media);
// TODO: 0 needs to be changed when headers get relationships of their own // TODO: 0 needs to be changed when headers get relationships of their own
@ -238,7 +291,12 @@ export class Compiler {
viewWrapper: footerWrapper, viewWrapper: footerWrapper,
file, file,
}), }),
prettify, {
indent: prettify,
declaration: {
encoding: "UTF-8",
},
},
); );
const mediaDatas = this.imageReplacer.getMediaData(tempXmlData, file.Media); const mediaDatas = this.imageReplacer.getMediaData(tempXmlData, file.Media);
// TODO: 0 needs to be changed when headers get relationships of their own // TODO: 0 needs to be changed when headers get relationships of their own
@ -255,7 +313,12 @@ export class Compiler {
viewWrapper: file.Document, viewWrapper: file.Document,
file, file,
}), }),
prettify, {
indent: prettify,
declaration: {
encoding: "UTF-8",
},
},
), ),
path: "[Content_Types].xml", path: "[Content_Types].xml",
}, },
@ -265,7 +328,13 @@ export class Compiler {
viewWrapper: file.Document, viewWrapper: file.Document,
file, file,
}), }),
prettify, {
indent: prettify,
declaration: {
standalone: "yes",
encoding: "UTF-8",
},
},
), ),
path: "docProps/custom.xml", path: "docProps/custom.xml",
}, },
@ -275,7 +344,13 @@ export class Compiler {
viewWrapper: file.Document, viewWrapper: file.Document,
file, file,
}), }),
prettify, {
indent: prettify,
declaration: {
standalone: "yes",
encoding: "UTF-8",
},
},
), ),
path: "docProps/app.xml", path: "docProps/app.xml",
}, },
@ -285,7 +360,12 @@ export class Compiler {
viewWrapper: file.FootNotes, viewWrapper: file.FootNotes,
file: file, file: file,
}), }),
prettify, {
indent: prettify,
declaration: {
encoding: "UTF-8",
},
},
), ),
path: "word/footnotes.xml", path: "word/footnotes.xml",
}, },
@ -295,7 +375,12 @@ export class Compiler {
viewWrapper: file.FootNotes, viewWrapper: file.FootNotes,
file: file, file: file,
}), }),
prettify, {
indent: prettify,
declaration: {
encoding: "UTF-8",
},
},
), ),
path: "word/_rels/footnotes.xml.rels", path: "word/_rels/footnotes.xml.rels",
}, },
@ -305,7 +390,13 @@ export class Compiler {
viewWrapper: file.Document, viewWrapper: file.Document,
file, file,
}), }),
prettify, {
indent: prettify,
declaration: {
standalone: "yes",
encoding: "UTF-8",
},
},
), ),
path: "word/settings.xml", path: "word/settings.xml",
}, },

View File

@ -29,6 +29,7 @@ export interface IPropertiesOptions {
readonly background?: IDocumentBackgroundOptions; readonly background?: IDocumentBackgroundOptions;
readonly features?: { readonly features?: {
readonly trackRevisions?: boolean; readonly trackRevisions?: boolean;
readonly updateFields?: boolean;
}; };
readonly compatabilityModeVersion?: number; readonly compatabilityModeVersion?: number;
readonly customProperties?: ICustomPropertyOptions[]; readonly customProperties?: ICustomPropertyOptions[];

View File

@ -165,20 +165,6 @@ describe("File", () => {
}); });
}); });
describe("#addTrackRevisionsFeature", () => {
it("should call the underlying document's add", () => {
const file = new File({
features: {
trackRevisions: true,
},
sections: [],
});
// tslint:disable-next-line: no-unused-expression no-string-literal
expect(file.Settings["trackRevisions"]).to.exist;
});
});
describe("#createFootnote", () => { describe("#createFootnote", () => {
it("should create footnote", () => { it("should create footnote", () => {
const wrapper = new File({ const wrapper = new File({

View File

@ -78,6 +78,8 @@ export class File {
this.settings = new Settings({ this.settings = new Settings({
compatabilityModeVersion: options.compatabilityModeVersion, compatabilityModeVersion: options.compatabilityModeVersion,
evenAndOddHeaders: options.evenAndOddHeaderAndFooters ? true : false, evenAndOddHeaders: options.evenAndOddHeaderAndFooters ? true : false,
trackRevisions: options.features?.trackRevisions,
updateFields: options.features?.updateFields,
}); });
this.media = fileProperties.template && fileProperties.template.media ? fileProperties.template.media : new Media(); this.media = fileProperties.template && fileProperties.template.media ? fileProperties.template.media : new Media();
@ -132,18 +134,6 @@ export class File {
this.footnotesWrapper.View.createFootNote(parseFloat(key), options.footnotes[key].children); this.footnotesWrapper.View.createFootNote(parseFloat(key), options.footnotes[key].children);
} }
} }
if (options.features) {
if (options.features.trackRevisions) {
this.settings.addTrackRevisions();
}
}
}
public verifyUpdateFields(): void {
if (this.documentWrapper.View.getTablesOfContents().length) {
this.settings.addUpdateFields();
}
} }
private addSection({ headers = {}, footers = {}, children, properties }: ISectionOptions): void { private addSection({ headers = {}, footers = {}, children, properties }: ISectionOptions): void {

View File

@ -1,5 +1,15 @@
import { XmlAttributeComponent, XmlComponent } from "file/xml-components"; import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
// Currently, this is hard-coded for Microsoft word compatSettings.
// Theoretically, we could add compatSettings for other programs, but
// currently there isn't a need.
// <xsd:complexType name="CT_CompatSetting">
// <xsd:attribute name="name" type="s:ST_String"/>
// <xsd:attribute name="uri" type="s:ST_String"/>
// <xsd:attribute name="val" type="s:ST_String"/>
// </xsd:complexType>
export class CompatibilitySettingAttributes extends XmlAttributeComponent<{ export class CompatibilitySettingAttributes extends XmlAttributeComponent<{
readonly version: number; readonly version: number;
readonly name: string; readonly name: string;
@ -12,6 +22,8 @@ export class CompatibilitySettingAttributes extends XmlAttributeComponent<{
}; };
} }
// https://docs.microsoft.com/en-us/openspecs/office_standards/ms-docx/90138c4d-eb18-4edc-aa6c-dfb799cb1d0d
export class CompatibilitySetting extends XmlComponent { export class CompatibilitySetting extends XmlComponent {
constructor(version: number) { constructor(version: number) {
super("w:compatSetting"); super("w:compatSetting");

View File

@ -1,11 +1,77 @@
import { XmlComponent } from "file/xml-components"; import { OnOffElement, XmlComponent } from "file/xml-components";
import { CompatibilitySetting } from "./compatibility-setting/compatibility-setting"; import { CompatibilitySetting } from "./compatibility-setting/compatibility-setting";
class DoNotExpandShiftReturn extends XmlComponent { // <xsd:complexType name="CT_Compat">
constructor() { // <xsd:sequence>
super("w:doNotExpandShiftReturn"); // <xsd:element name="useSingleBorderforContiguousCells" type="CT_OnOff" minOccurs="0"/>
} // <xsd:element name="wpJustification" type="CT_OnOff" minOccurs="0"/>
} // <xsd:element name="noTabHangInd" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="noLeading" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="spaceForUL" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="noColumnBalance" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="balanceSingleByteDoubleByteWidth" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="noExtraLineSpacing" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="doNotLeaveBackslashAlone" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="ulTrailSpace" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="doNotExpandShiftReturn" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="spacingInWholePoints" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="lineWrapLikeWord6" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="printBodyTextBeforeHeader" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="printColBlack" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="wpSpaceWidth" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="showBreaksInFrames" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="subFontBySize" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="suppressBottomSpacing" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="suppressTopSpacing" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="suppressSpacingAtTopOfPage" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="suppressTopSpacingWP" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="suppressSpBfAfterPgBrk" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="swapBordersFacingPages" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="convMailMergeEsc" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="truncateFontHeightsLikeWP6" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="mwSmallCaps" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="usePrinterMetrics" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="doNotSuppressParagraphBorders" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="wrapTrailSpaces" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="footnoteLayoutLikeWW8" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="shapeLayoutLikeWW8" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="alignTablesRowByRow" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="forgetLastTabAlignment" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="adjustLineHeightInTable" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="autoSpaceLikeWord95" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="noSpaceRaiseLower" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="doNotUseHTMLParagraphAutoSpacing" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="layoutRawTableWidth" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="layoutTableRowsApart" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="useWord97LineBreakRules" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="doNotBreakWrappedTables" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="doNotSnapToGridInCell" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="selectFldWithFirstOrLastChar" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="applyBreakingRules" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="doNotWrapTextWithPunct" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="doNotUseEastAsianBreakRules" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="useWord2002TableStyleRules" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="growAutofit" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="useFELayout" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="useNormalStyleForList" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="doNotUseIndentAsNumberingTabStop" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="useAltKinsokuLineBreakRules" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="allowSpaceOfSameStyleInTable" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="doNotSuppressIndentation" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="doNotAutofitConstrainedTables" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="autofitToFirstFixedWidthCell" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="underlineTabInNumList" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="displayHangulFixedWidth" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="splitPgBreakAndParaMark" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="doNotVertAlignCellWithSp" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="doNotBreakConstrainedForcedTable" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="doNotVertAlignInTxbx" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="useAnsiKerningPairs" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="cachedColBalance" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="compatSetting" type="CT_CompatSetting" minOccurs="0" maxOccurs="unbounded"
// />
// </xsd:sequence>
// </xsd:complexType>
export interface ICompatibilityOptions { export interface ICompatibilityOptions {
readonly doNotExpandShiftReturn?: boolean; readonly doNotExpandShiftReturn?: boolean;
@ -16,8 +82,9 @@ export class Compatibility extends XmlComponent {
constructor(options: ICompatibilityOptions) { constructor(options: ICompatibilityOptions) {
super("w:compat"); super("w:compat");
if (options.doNotExpandShiftReturn) { // Don't justify lines ending in soft line break setting
this.root.push(new DoNotExpandShiftReturn()); if (options.doNotExpandShiftReturn !== undefined) {
this.root.push(new OnOffElement("w:doNotExpandShiftReturn", options.doNotExpandShiftReturn));
} }
if (options.version) { if (options.version) {

View File

@ -1,17 +0,0 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { DisplayBackgroundShape } from "./display-background-shape";
describe("DisplayBackgroundShape", () => {
describe("#constructor()", () => {
it("should create", () => {
const displayBackgroundShape = new DisplayBackgroundShape();
const tree = new Formatter().format(displayBackgroundShape);
expect(tree).to.deep.equal({
"w:displayBackgroundShape": {},
});
});
});
});

View File

@ -1,9 +0,0 @@
// http://officeopenxml.com/WPdocument.php
// http://www.datypic.com/sc/ooxml/e-w_background-1.html
import { XmlComponent } from "file/xml-components";
export class DisplayBackgroundShape extends XmlComponent {
constructor() {
super("w:displayBackgroundShape");
}
}

View File

@ -1,9 +0,0 @@
// http://officeopenxml.com/WPSectionFooterReference.php
// https://c-rex.net/projects/samples/ooxml/e1/Part4/OOXML_P4_DOCX_evenAndOddHeaders_topic_ID0ET1WU.html
import { XmlComponent } from "file/xml-components";
export class EvenAndOddHeadersAndFooters extends XmlComponent {
constructor() {
super("w:evenAndOddHeaders");
}
}

View File

@ -1,2 +1 @@
export * from "./settings"; export * from "./settings";
export * from "./update-fields";

View File

@ -7,83 +7,37 @@ import { Settings } from "./settings";
describe("Settings", () => { describe("Settings", () => {
describe("#constructor", () => { describe("#constructor", () => {
it("should create a empty Settings with correct rootKey", () => { it("should create a empty Settings with correct rootKey", () => {
const settings = new Settings({ const settings = new Settings({});
evenAndOddHeaders: false,
});
const tree = new Formatter().format(settings); const tree = new Formatter().format(settings);
let keys = Object.keys(tree);
expect(keys).is.an.instanceof(Array); expect(Object.keys(tree)).has.length(1);
expect(keys).has.length(1); expect(tree["w:settings"]).to.be.an("array");
expect(keys[0]).to.be.equal("w:settings");
keys = Object.keys(tree["w:settings"]);
expect(keys).is.an.instanceof(Array);
expect(keys).has.length(3);
}); });
});
describe("#addUpdateFields", () => { it("should add updateFields setting", () => {
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(4);
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[3]);
expect(keys).is.an.instanceof(Array);
expect(keys).has.length(1);
expect(keys[0]).to.be.equal("w:updateFields");
const updateFields = rootArray[3]["w:updateFields"];
keys = Object.keys(updateFields);
expect(keys).is.an.instanceof(Array);
expect(keys).has.length(1);
expect(keys[0]).to.be.equal("_attr");
const updateFieldsAttr = updateFields._attr;
expect(updateFieldsAttr["w:val"]).to.be.equal(true);
};
it("should add a UpdateFields with value true", () => {
const settings = new Settings({ const settings = new Settings({
evenAndOddHeaders: false, updateFields: true,
});
settings.addUpdateFields();
assertSettingsWithUpdateFields(settings);
});
it("should add a UpdateFields with value true only once", () => {
const settings = new Settings({
evenAndOddHeaders: false,
});
settings.addUpdateFields();
assertSettingsWithUpdateFields(settings);
settings.addUpdateFields();
assertSettingsWithUpdateFields(settings);
});
});
describe("#addCompatibility", () => {
it("should add an empty Compatibility by default", () => {
const settings = new Settings({
evenAndOddHeaders: false,
}); });
const tree = new Formatter().format(settings); const tree = new Formatter().format(settings);
let keys: string[] = Object.keys(tree); expect(Object.keys(tree)).has.length(1);
expect(keys[0]).to.be.equal("w:settings"); expect(tree["w:settings"]).to.be.an("array");
const rootArray = tree["w:settings"];
expect(rootArray).is.an.instanceof(Array); expect(tree["w:settings"]).to.deep.include({
expect(rootArray).has.length(3); "w:updateFields": {},
keys = Object.keys(rootArray[0]); });
expect(keys).is.an.instanceof(Array); });
expect(keys).has.length(1);
expect(keys[0]).to.be.equal("_attr"); it("should indicate modern word compatibility by default", () => {
keys = Object.keys(rootArray[1]); const settings = new Settings({});
expect(keys).is.an.instanceof(Array);
expect(keys).has.length(1); const tree = new Formatter().format(settings);
expect(keys[0]).to.be.equal("w:compat"); expect(Object.keys(tree)).has.length(1);
expect(rootArray[1]["w:compat"][0]).to.deep.equal({ expect(tree["w:settings"]).to.be.an("array");
const compat = tree["w:settings"][2];
expect(compat).to.be.an("object").with.keys("w:compat");
expect(compat["w:compat"]).to.deep.include({
"w:compatSetting": { "w:compatSetting": {
_attr: { _attr: {
"w:val": 15, "w:val": 15,
@ -93,81 +47,19 @@ describe("Settings", () => {
}, },
}); });
}); });
});
describe("#addTrackRevisions", () => { it("should add trackRevisions setting", () => {
it("should add an empty Track Revisions", () => {
const settings = new Settings({ const settings = new Settings({
evenAndOddHeaders: false, trackRevisions: true,
}); });
settings.addTrackRevisions();
const tree = new Formatter().format(settings); const tree = new Formatter().format(settings);
let keys: string[] = Object.keys(tree); expect(Object.keys(tree)).has.length(1);
expect(keys[0]).to.be.equal("w:settings"); expect(tree["w:settings"]).to.be.an("array");
const rootArray = tree["w:settings"];
expect(rootArray).is.an.instanceof(Array); expect(tree["w:settings"]).to.deep.include({
expect(rootArray).has.length(4); "w:trackRevisions": {},
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[3]);
expect(keys).is.an.instanceof(Array);
expect(keys).has.length(1);
expect(keys[0]).to.be.equal("w:trackRevisions");
});
});
describe("#addTrackRevisionsTwice", () => {
it("should add an empty Track Revisions if called twice", () => {
const settings = new Settings({
evenAndOddHeaders: false,
}); });
settings.addTrackRevisions();
settings.addTrackRevisions();
const tree = new Formatter().format(settings);
let keys: string[] = Object.keys(tree);
expect(keys[0]).to.be.equal("w:settings");
const rootArray = tree["w:settings"];
expect(rootArray).is.an.instanceof(Array);
expect(rootArray).has.length(4);
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[3]);
expect(keys).is.an.instanceof(Array);
expect(keys).has.length(1);
expect(keys[0]).to.be.equal("w:trackRevisions");
});
});
describe("#addTrackRevisionsTwice", () => {
it("should add an empty Track Revisions if called twice", () => {
const settings = new Settings({
evenAndOddHeaders: true,
});
settings.addTrackRevisions();
settings.addTrackRevisions();
const tree = new Formatter().format(settings);
let keys: string[] = Object.keys(tree);
expect(keys[0]).to.be.equal("w:settings");
const rootArray = tree["w:settings"];
expect(rootArray).is.an.instanceof(Array);
expect(rootArray).has.length(5);
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[4]);
expect(keys).is.an.instanceof(Array);
expect(keys).has.length(1);
expect(keys[0]).to.be.equal("w:trackRevisions");
keys = Object.keys(rootArray[2]);
expect(keys).is.an.instanceof(Array);
expect(keys).has.length(1);
expect(keys[0]).to.be.equal("w:evenAndOddHeaders");
}); });
}); });
}); });

View File

@ -1,10 +1,6 @@
import { XmlAttributeComponent, XmlComponent } from "file/xml-components"; import { OnOffElement, XmlAttributeComponent, XmlComponent } from "file/xml-components";
import { Compatibility } from "./compatibility"; import { Compatibility } from "./compatibility";
import { DisplayBackgroundShape } from "./display-background-shape";
import { EvenAndOddHeadersAndFooters } from "./even-odd-headers";
import { TrackRevisions } from "./track-revisions";
import { UpdateFields } from "./update-fields";
export class SettingsAttributes extends XmlAttributeComponent<{ export class SettingsAttributes extends XmlAttributeComponent<{
readonly wpc?: string; readonly wpc?: string;
@ -46,14 +42,118 @@ export class SettingsAttributes extends XmlAttributeComponent<{
}; };
} }
// <xsd:complexType name="CT_Settings">
// <xsd:sequence>
// <xsd:element name="writeProtection" type="CT_WriteProtection" minOccurs="0"/>
// <xsd:element name="view" type="CT_View" minOccurs="0"/>
// <xsd:element name="zoom" type="CT_Zoom" minOccurs="0"/>
// <xsd:element name="removePersonalInformation" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="removeDateAndTime" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="doNotDisplayPageBoundaries" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="displayBackgroundShape" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="printPostScriptOverText" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="printFractionalCharacterWidth" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="printFormsData" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="embedTrueTypeFonts" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="embedSystemFonts" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="saveSubsetFonts" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="saveFormsData" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="mirrorMargins" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="alignBordersAndEdges" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="bordersDoNotSurroundHeader" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="bordersDoNotSurroundFooter" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="gutterAtTop" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="hideSpellingErrors" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="hideGrammaticalErrors" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="activeWritingStyle" type="CT_WritingStyle" minOccurs="0"
// maxOccurs="unbounded"/>
// <xsd:element name="proofState" type="CT_Proof" minOccurs="0"/>
// <xsd:element name="formsDesign" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="attachedTemplate" type="CT_Rel" minOccurs="0"/>
// <xsd:element name="linkStyles" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="stylePaneFormatFilter" type="CT_StylePaneFilter" minOccurs="0"/>
// <xsd:element name="stylePaneSortMethod" type="CT_StyleSort" minOccurs="0"/>
// <xsd:element name="documentType" type="CT_DocType" minOccurs="0"/>
// <xsd:element name="mailMerge" type="CT_MailMerge" minOccurs="0"/>
// <xsd:element name="revisionView" type="CT_TrackChangesView" minOccurs="0"/>
// <xsd:element name="trackRevisions" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="doNotTrackMoves" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="doNotTrackFormatting" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="documentProtection" type="CT_DocProtect" minOccurs="0"/>
// <xsd:element name="autoFormatOverride" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="styleLockTheme" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="styleLockQFSet" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="defaultTabStop" type="CT_TwipsMeasure" minOccurs="0"/>
// <xsd:element name="autoHyphenation" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="consecutiveHyphenLimit" type="CT_DecimalNumber" minOccurs="0"/>
// <xsd:element name="hyphenationZone" type="CT_TwipsMeasure" minOccurs="0"/>
// <xsd:element name="doNotHyphenateCaps" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="showEnvelope" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="summaryLength" type="CT_DecimalNumberOrPrecent" minOccurs="0"/>
// <xsd:element name="clickAndTypeStyle" type="CT_String" minOccurs="0"/>
// <xsd:element name="defaultTableStyle" type="CT_String" minOccurs="0"/>
// <xsd:element name="evenAndOddHeaders" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="bookFoldRevPrinting" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="bookFoldPrinting" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="bookFoldPrintingSheets" type="CT_DecimalNumber" minOccurs="0"/>
// <xsd:element name="drawingGridHorizontalSpacing" type="CT_TwipsMeasure" minOccurs="0"/>
// <xsd:element name="drawingGridVerticalSpacing" type="CT_TwipsMeasure" minOccurs="0"/>
// <xsd:element name="displayHorizontalDrawingGridEvery" type="CT_DecimalNumber" minOccurs="0"/>
// <xsd:element name="displayVerticalDrawingGridEvery" type="CT_DecimalNumber" minOccurs="0"/>
// <xsd:element name="doNotUseMarginsForDrawingGridOrigin" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="drawingGridHorizontalOrigin" type="CT_TwipsMeasure" minOccurs="0"/>
// <xsd:element name="drawingGridVerticalOrigin" type="CT_TwipsMeasure" minOccurs="0"/>
// <xsd:element name="doNotShadeFormData" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="noPunctuationKerning" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="characterSpacingControl" type="CT_CharacterSpacing" minOccurs="0"/>
// <xsd:element name="printTwoOnOne" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="strictFirstAndLastChars" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="noLineBreaksAfter" type="CT_Kinsoku" minOccurs="0"/>
// <xsd:element name="noLineBreaksBefore" type="CT_Kinsoku" minOccurs="0"/>
// <xsd:element name="savePreviewPicture" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="doNotValidateAgainstSchema" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="saveInvalidXml" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="ignoreMixedContent" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="alwaysShowPlaceholderText" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="doNotDemarcateInvalidXml" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="saveXmlDataOnly" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="useXSLTWhenSaving" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="saveThroughXslt" type="CT_SaveThroughXslt" minOccurs="0"/>
// <xsd:element name="showXMLTags" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="alwaysMergeEmptyNamespace" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="updateFields" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="hdrShapeDefaults" type="CT_ShapeDefaults" minOccurs="0"/>
// <xsd:element name="footnotePr" type="CT_FtnDocProps" minOccurs="0"/>
// <xsd:element name="endnotePr" type="CT_EdnDocProps" minOccurs="0"/>
// <xsd:element name="compat" type="CT_Compat" minOccurs="0"/>
// <xsd:element name="docVars" type="CT_DocVars" minOccurs="0"/>
// <xsd:element name="rsids" type="CT_DocRsids" minOccurs="0"/>
// <xsd:element ref="m:mathPr" minOccurs="0" maxOccurs="1"/>
// <xsd:element name="attachedSchema" type="CT_String" minOccurs="0" maxOccurs="unbounded"/>
// <xsd:element name="themeFontLang" type="CT_Language" minOccurs="0" maxOccurs="1"/>
// <xsd:element name="clrSchemeMapping" type="CT_ColorSchemeMapping" minOccurs="0"/>
// <xsd:element name="doNotIncludeSubdocsInStats" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="doNotAutoCompressPictures" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="forceUpgrade" type="CT_Empty" minOccurs="0" maxOccurs="1"/>
// <xsd:element name="captions" type="CT_Captions" minOccurs="0" maxOccurs="1"/>
// <xsd:element name="readModeInkLockDown" type="CT_ReadingModeInkLockDown" minOccurs="0"/>
// <xsd:element name="smartTagType" type="CT_SmartTagType" minOccurs="0" maxOccurs="unbounded"/>
// <xsd:element ref="sl:schemaLibrary" minOccurs="0" maxOccurs="1"/>
// <xsd:element name="shapeDefaults" type="CT_ShapeDefaults" minOccurs="0"/>
// <xsd:element name="doNotEmbedSmartTags" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="decimalSymbol" type="CT_String" minOccurs="0" maxOccurs="1"/>
// <xsd:element name="listSeparator" type="CT_String" minOccurs="0" maxOccurs="1"/>
// </xsd:sequence>
// </xsd:complexType>
export interface ISettingsOptions { export interface ISettingsOptions {
readonly compatabilityModeVersion?: number; readonly compatabilityModeVersion?: number;
readonly evenAndOddHeaders: boolean; readonly evenAndOddHeaders?: boolean;
readonly trackRevisions?: boolean;
readonly updateFields?: boolean;
} }
export class Settings extends XmlComponent { export class Settings extends XmlComponent {
private readonly trackRevisions: TrackRevisions;
constructor(options: ISettingsOptions) { constructor(options: ISettingsOptions) {
super("w:settings"); super("w:settings");
this.root.push( this.root.push(
@ -78,32 +178,29 @@ export class Settings extends XmlComponent {
}), }),
); );
// http://officeopenxml.com/WPdocument.php
// https://c-rex.net/projects/samples/ooxml/e1/Part4/OOXML_P4_DOCX_displayBackgroundSha_topic_ID0ET4SX.html
this.root.push(new OnOffElement("w:displayBackgroundShape", true));
// https://c-rex.net/projects/samples/ooxml/e1/Part4/OOXML_P4_DOCX_trackRevisions_topic_ID0EKXKY.html
if (options.trackRevisions !== undefined) {
this.root.push(new OnOffElement("w:trackRevisions", options.trackRevisions));
}
// http://officeopenxml.com/WPSectionFooterReference.php
// https://c-rex.net/projects/samples/ooxml/e1/Part4/OOXML_P4_DOCX_evenAndOddHeaders_topic_ID0ET1WU.html
if (options.evenAndOddHeaders !== undefined) {
this.root.push(new OnOffElement("w:evenAndOddHeaders", options.evenAndOddHeaders));
}
if (options.updateFields !== undefined) {
this.root.push(new OnOffElement("w:updateFields", options.updateFields));
}
this.root.push( this.root.push(
new Compatibility({ new Compatibility({
version: options.compatabilityModeVersion || 15, version: options.compatabilityModeVersion || 15,
}), }),
); );
if (options.evenAndOddHeaders) {
this.root.push(new EvenAndOddHeadersAndFooters());
}
this.trackRevisions = new TrackRevisions();
this.root.push(new DisplayBackgroundShape());
}
public addUpdateFields(): void {
if (!this.root.find((child) => child instanceof UpdateFields)) {
this.addChildElement(new UpdateFields());
}
}
public addTrackRevisions(): TrackRevisions {
if (!this.root.find((child) => child instanceof TrackRevisions)) {
this.addChildElement(this.trackRevisions);
}
return this.trackRevisions;
} }
} }

View File

@ -1,16 +0,0 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { TrackRevisions } from "file/settings/track-revisions";
import { EMPTY_OBJECT } from "file/xml-components";
describe("TrackRevisions", () => {
describe("#constructor", () => {
it("creates an initially empty property object", () => {
const trackRevisions = new TrackRevisions();
const tree = new Formatter().format(trackRevisions);
expect(tree).to.deep.equal({ "w:trackRevisions": EMPTY_OBJECT });
});
});
});

View File

@ -1,7 +0,0 @@
import { XmlComponent } from "file/xml-components";
export class TrackRevisions extends XmlComponent {
constructor() {
super("w:trackRevisions");
}
}

View File

@ -1,39 +0,0 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { UpdateFields } from "./update-fields";
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);
});
});
});

View File

@ -1,19 +0,0 @@
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
export class UpdateFieldsAttributes extends XmlAttributeComponent<{
readonly enabled: boolean;
}> {
protected readonly xmlKeys = {
enabled: "w:val",
};
}
export class UpdateFields extends XmlComponent {
constructor(enabled: boolean = true) {
super("w:updateFields");
this.root.push(
new UpdateFieldsAttributes({
enabled,
}),
);
}
}

View File

@ -186,7 +186,7 @@ export function measurementOrPercentValue(val: number | string): number | string
if (typeof val === "number") { if (typeof val === "number") {
return decimalNumber(val); return decimalNumber(val);
} }
if (val.slice(-1) === '%') { if (val.slice(-1) === "%") {
return percentageValue(val); return percentageValue(val);
} }
return universalMeasureValue(val); return universalMeasureValue(val);