diff --git a/demo/63-odd-even-header-footer.ts b/demo/63-odd-even-header-footer.ts new file mode 100644 index 0000000000..4582e14455 --- /dev/null +++ b/demo/63-odd-even-header-footer.ts @@ -0,0 +1,70 @@ +// Move + offset header and footer +// Import from 'docx' rather than '../build' if you install from npm +import * as fs from "fs"; +import { Document, Footer, Header, Packer, PageBreak, Paragraph, TextRun } from "../build"; + +const doc = new Document({ + evenAndOddHeaderAndFooters: true, +}); + +doc.addSection({ + headers: { + default: new Header({ + children: [ + new Paragraph({ + text: "Odd Header text", + }), + new Paragraph({ + text: "Odd - Some more header text", + }), + ], + }), + even: new Header({ + children: [ + new Paragraph({ + text: "Even header text", + }), + new Paragraph({ + text: "Even - Some more header text", + }), + ], + }), + }, + footers: { + default: new Footer({ + children: [ + new Paragraph({ + text: "Odd Footer text", + }), + ], + }), + even: new Footer({ + children: [ + new Paragraph({ + text: "Even Cool Footer text", + }), + ], + }), + }, + children: [ + new Paragraph({ + children: [new TextRun("Hello World 1"), new PageBreak()], + }), + new Paragraph({ + children: [new TextRun("Hello World 2"), new PageBreak()], + }), + new Paragraph({ + children: [new TextRun("Hello World 3"), new PageBreak()], + }), + new Paragraph({ + children: [new TextRun("Hello World 4"), new PageBreak()], + }), + new Paragraph({ + children: [new TextRun("Hello World 5"), new PageBreak()], + }), + ], +}); + +Packer.toBuffer(doc).then((buffer) => { + fs.writeFileSync("My Document.docx", buffer); +}); diff --git a/src/file/core-properties/properties.ts b/src/file/core-properties/properties.ts index c12b65ee6d..91ced5bb53 100644 --- a/src/file/core-properties/properties.ts +++ b/src/file/core-properties/properties.ts @@ -30,6 +30,7 @@ export interface IPropertiesOptions { }; readonly compatabilityModeVersion?: number; readonly customProperties?: ICustomPropertyOptions[]; + readonly evenAndOddHeaderAndFooters?: boolean; } export class CoreProperties extends XmlComponent { diff --git a/src/file/file.spec.ts b/src/file/file.spec.ts index b2195f439f..59f3d8fe8b 100644 --- a/src/file/file.spec.ts +++ b/src/file/file.spec.ts @@ -451,4 +451,14 @@ describe("File", () => { ], }); }); + + it("should create with even and odd headers and footers", () => { + const doc = new File({ + evenAndOddHeaderAndFooters: true, + }); + + const tree = new Formatter().format(doc.Settings); + + expect(tree["w:settings"][2]).to.deep.equal({ "w:evenAndOddHeaders": {} }); + }); }); diff --git a/src/file/file.ts b/src/file/file.ts index 55464d04e9..62e53be230 100644 --- a/src/file/file.ts +++ b/src/file/file.ts @@ -86,6 +86,7 @@ export class File { this.documentWrapper = new DocumentWrapper({ background: options.background || {} }); this.settings = new Settings({ compatabilityModeVersion: options.compatabilityModeVersion, + evenAndOddHeaders: options.evenAndOddHeaderAndFooters ? true : false, }); this.media = fileProperties.template && fileProperties.template.media ? fileProperties.template.media : new Media(); diff --git a/src/file/settings/even-odd-headers.ts b/src/file/settings/even-odd-headers.ts new file mode 100644 index 0000000000..c6f85a7aca --- /dev/null +++ b/src/file/settings/even-odd-headers.ts @@ -0,0 +1,9 @@ +// 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"); + } +} diff --git a/src/file/settings/settings.spec.ts b/src/file/settings/settings.spec.ts index 381b24d372..03a3612a0b 100644 --- a/src/file/settings/settings.spec.ts +++ b/src/file/settings/settings.spec.ts @@ -7,7 +7,9 @@ import { Settings } from "./settings"; describe("Settings", () => { describe("#constructor", () => { 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); let keys = Object.keys(tree); expect(keys).is.an.instanceof(Array); @@ -45,12 +47,16 @@ describe("Settings", () => { 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, + }); settings.addUpdateFields(); assertSettingsWithUpdateFields(settings); }); it("should add a UpdateFields with value true only once", () => { - const settings = new Settings({}); + const settings = new Settings({ + evenAndOddHeaders: false, + }); settings.addUpdateFields(); assertSettingsWithUpdateFields(settings); settings.addUpdateFields(); @@ -59,7 +65,9 @@ describe("Settings", () => { }); describe("#addCompatibility", () => { it("should add an empty Compatibility by default", () => { - const settings = new Settings({}); + const settings = new Settings({ + evenAndOddHeaders: false, + }); const tree = new Formatter().format(settings); let keys: string[] = Object.keys(tree); @@ -88,7 +96,9 @@ describe("Settings", () => { }); describe("#addTrackRevisions", () => { it("should add an empty Track Revisions", () => { - const settings = new Settings({}); + const settings = new Settings({ + evenAndOddHeaders: false, + }); settings.addTrackRevisions(); const tree = new Formatter().format(settings); @@ -109,7 +119,9 @@ describe("Settings", () => { }); describe("#addTrackRevisionsTwice", () => { it("should add an empty Track Revisions if called twice", () => { - const settings = new Settings({}); + const settings = new Settings({ + evenAndOddHeaders: false, + }); settings.addTrackRevisions(); settings.addTrackRevisions(); @@ -129,4 +141,33 @@ describe("Settings", () => { 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"); + }); + }); }); diff --git a/src/file/settings/settings.ts b/src/file/settings/settings.ts index 352c86b23f..30b26d8f04 100644 --- a/src/file/settings/settings.ts +++ b/src/file/settings/settings.ts @@ -1,6 +1,8 @@ import { XmlAttributeComponent, XmlComponent } from "file/xml-components"; + 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"; @@ -46,10 +48,10 @@ export class SettingsAttributes extends XmlAttributeComponent<{ export interface ISettingsOptions { readonly compatabilityModeVersion?: number; + readonly evenAndOddHeaders: boolean; } export class Settings extends XmlComponent { - private readonly compatibility: Compatibility; private readonly trackRevisions: TrackRevisions; constructor(options: ISettingsOptions) { @@ -76,11 +78,16 @@ export class Settings extends XmlComponent { }), ); - this.compatibility = new Compatibility({ - version: options.compatabilityModeVersion || 15, - }); + this.root.push( + new Compatibility({ + version: options.compatabilityModeVersion || 15, + }), + ); + + if (options.evenAndOddHeaders) { + this.root.push(new EvenAndOddHeadersAndFooters()); + } - this.root.push(this.compatibility); this.trackRevisions = new TrackRevisions(); this.root.push(new DisplayBackgroundShape());