diff --git a/demo/demo16.ts b/demo/demo16.ts index f14674e3f2..6c75218e53 100644 --- a/demo/demo16.ts +++ b/demo/demo16.ts @@ -1,7 +1,7 @@ // Multiple sections and headers // Import from 'docx' rather than '../build' if you install from npm import * as fs from "fs"; -import { Document, FooterReferenceType, HeaderReferenceType, Packer, PageNumberFormat, PageOrientation, Paragraph } from "../build"; +import { Document, Packer, PageNumberFormat, PageOrientation, Paragraph } from "../build"; const doc = new Document(); @@ -15,8 +15,12 @@ const footer = doc.createFooter(); footer.createParagraph("Footer on another page"); doc.addSection({ - headers: [{headerId: header.Header.ReferenceId, headerType: HeaderReferenceType.FIRST}], - footers: [{footerId: footer.Footer.ReferenceId, footerType: FooterReferenceType.FIRST}], + headers: { + default: header, + }, + footers: { + default: footer, + }, pageNumberStart: 1, pageNumberFormatType: PageNumberFormat.DECIMAL, }); @@ -24,8 +28,12 @@ doc.addSection({ doc.createParagraph("hello"); doc.addSection({ - headers: [{headerId: header.Header.ReferenceId, headerType: HeaderReferenceType.FIRST}], - footers: [{footerId: footer.Footer.ReferenceId, footerType: FooterReferenceType.FIRST}], + headers: { + default: header, + }, + footers: { + default: footer, + }, pageNumberStart: 1, pageNumberFormatType: PageNumberFormat.DECIMAL, orientation: PageOrientation.LANDSCAPE, diff --git a/src/file/document/body/section-properties/section-properties.spec.ts b/src/file/document/body/section-properties/section-properties.spec.ts index 4419c84aca..9ab0621ad9 100644 --- a/src/file/document/body/section-properties/section-properties.spec.ts +++ b/src/file/document/body/section-properties/section-properties.spec.ts @@ -1,7 +1,9 @@ import { expect } from "chai"; import { Formatter } from "../../../../export/formatter"; -import { FooterReferenceType, HeaderReferenceType, PageBorderOffsetFrom, PageNumberFormat } from "./"; +import { FooterWrapper } from "../../../footer-wrapper"; +import { HeaderWrapper } from "../../../header-wrapper"; +import { PageBorderOffsetFrom, PageNumberFormat } from "./"; import { SectionProperties } from "./section-properties"; describe("SectionProperties", () => { @@ -20,8 +22,12 @@ describe("SectionProperties", () => { mirror: false, space: 708, linePitch: 360, - headers: [{ headerId: 100, headerType: HeaderReferenceType.DEFAULT }], - footers: [{ footerId: 200, footerType: FooterReferenceType.EVEN }], + headers: { + default: new HeaderWrapper(100), + }, + footers: { + even: new FooterWrapper(200), + }, pageNumberStart: 10, pageNumberFormatType: PageNumberFormat.CARDINAL_TEXT, }); diff --git a/src/file/document/body/section-properties/section-properties.ts b/src/file/document/body/section-properties/section-properties.ts index 8ff347c202..ec8a6b6746 100644 --- a/src/file/document/body/section-properties/section-properties.ts +++ b/src/file/document/body/section-properties/section-properties.ts @@ -1,24 +1,38 @@ // http://officeopenxml.com/WPsection.php import { XmlComponent } from "file/xml-components"; +import { FooterWrapper } from "../../../footer-wrapper"; +import { HeaderWrapper } from "../../../header-wrapper"; import { IPageBordersOptions, IPageNumberTypeAttributes, PageBorders, PageNumberFormat, PageNumberType } from "./"; import { Columns } from "./columns/columns"; import { IColumnsAttributes } from "./columns/columns-attributes"; import { DocumentGrid } from "./doc-grid/doc-grid"; import { IDocGridAttributesProperties } from "./doc-grid/doc-grid-attributes"; -import { FooterReference, IFooterOptions } from "./footer-reference/footer-reference"; -import { HeaderReference, IHeaderOptions } from "./header-reference/header-reference"; +import { FooterReferenceType } from "./footer-reference"; +import { FooterReference } from "./footer-reference/footer-reference"; +import { HeaderReferenceType } from "./header-reference"; +import { HeaderReference } from "./header-reference/header-reference"; import { PageMargin } from "./page-margin/page-margin"; import { IPageMarginAttributes } from "./page-margin/page-margin-attributes"; import { PageSize } from "./page-size/page-size"; import { IPageSizeAttributes, PageOrientation } from "./page-size/page-size-attributes"; import { TitlePage } from "./title-page/title-page"; +export interface IHeaderFooterGroup { + default?: T; + first?: T; + even?: T; +} + interface IHeadersOptions { - headers?: IHeaderOptions[]; + headers?: IHeaderFooterGroup; } interface IFootersOptions { - footers?: IFooterOptions[]; + footers?: IHeaderFooterGroup; +} + +interface ITitlePageOptions { + titlePage?: boolean; } export type SectionPropertiesOptions = IPageSizeAttributes & @@ -28,104 +42,129 @@ export type SectionPropertiesOptions = IPageSizeAttributes & IHeadersOptions & IFootersOptions & IPageNumberTypeAttributes & - IPageBordersOptions; + IPageBordersOptions & + ITitlePageOptions; export class SectionProperties extends XmlComponent { private readonly options: SectionPropertiesOptions; - constructor(options?: SectionPropertiesOptions) { + constructor(options: SectionPropertiesOptions = {}) { super("w:sectPr"); - const defaultOptions = { - width: 11906, - height: 16838, - top: 1440, - right: 1440, - bottom: 1440, - left: 1440, - header: 708, - footer: 708, - gutter: 0, - mirror: false, - space: 708, - linePitch: 360, - orientation: PageOrientation.PORTRAIT, - headers: [], - footers: [], - pageNumberStart: undefined, - pageNumberFormatType: PageNumberFormat.DECIMAL, - pageBorders: undefined, - pageBorderTop: undefined, - pageBorderRight: undefined, - pageBorderBottom: undefined, - pageBorderLeft: undefined, - titlePage: false, - }; + const { + width = 11906, + height = 16838, + top = 1440, + right = 1440, + bottom = 1440, + left = 1440, + header = 708, + footer = 708, + gutter = 0, + mirror = false, + space = 708, + linePitch = 360, + orientation = PageOrientation.PORTRAIT, + headers, + footers, + pageNumberFormatType = PageNumberFormat.DECIMAL, + pageNumberStart, + pageBorders, + pageBorderTop, + pageBorderRight, + pageBorderBottom, + pageBorderLeft, + titlePage = false, + } = options; - const mergedOptions = { - ...defaultOptions, - ...options, - }; + this.options = options; + this.root.push(new PageSize(width, height, orientation)); + this.root.push(new PageMargin(top, right, bottom, left, header, footer, gutter, mirror)); + this.root.push(new Columns(space)); + this.root.push(new DocumentGrid(linePitch)); - this.root.push(new PageSize(mergedOptions.width, mergedOptions.height, mergedOptions.orientation)); - this.root.push( - new PageMargin( - mergedOptions.top, - mergedOptions.right, - mergedOptions.bottom, - mergedOptions.left, - mergedOptions.header, - mergedOptions.footer, - mergedOptions.gutter, - mergedOptions.mirror, - ), - ); - this.root.push(new Columns(mergedOptions.space)); - this.root.push(new DocumentGrid(mergedOptions.linePitch)); + this.addHeaders(headers); + this.addFooters(footers); - for (const header of mergedOptions.headers) { - this.root.push( - new HeaderReference({ - headerType: header.headerType, - headerId: header.headerId, - }), - ); - } + this.root.push(new PageNumberType(pageNumberStart, pageNumberFormatType)); - for (const footer of mergedOptions.footers) { - this.root.push( - new FooterReference({ - footerType: footer.footerType, - footerId: footer.footerId, - }), - ); - } - - this.root.push(new PageNumberType(mergedOptions.pageNumberStart, mergedOptions.pageNumberFormatType)); - - if ( - mergedOptions.pageBorders || - mergedOptions.pageBorderTop || - mergedOptions.pageBorderRight || - mergedOptions.pageBorderBottom || - mergedOptions.pageBorderLeft - ) { + if (pageBorders || pageBorderTop || pageBorderRight || pageBorderBottom || pageBorderLeft) { this.root.push( new PageBorders({ - pageBorders: mergedOptions.pageBorders, - pageBorderTop: mergedOptions.pageBorderTop, - pageBorderRight: mergedOptions.pageBorderRight, - pageBorderBottom: mergedOptions.pageBorderBottom, - pageBorderLeft: mergedOptions.pageBorderLeft, + pageBorders: pageBorders, + pageBorderTop: pageBorderTop, + pageBorderRight: pageBorderRight, + pageBorderBottom: pageBorderBottom, + pageBorderLeft: pageBorderLeft, }), ); } - if (mergedOptions.titlePage) { + if (titlePage) { this.root.push(new TitlePage()); } + } - this.options = mergedOptions; + private addHeaders(headers?: IHeaderFooterGroup): void { + if (headers) { + if (headers.default) { + this.root.push( + new HeaderReference({ + headerType: HeaderReferenceType.DEFAULT, + headerId: headers.default.Header.ReferenceId, + }), + ); + } + + if (headers.first) { + this.root.push( + new HeaderReference({ + headerType: HeaderReferenceType.FIRST, + headerId: headers.first.Header.ReferenceId, + }), + ); + } + + if (headers.even) { + this.root.push( + new HeaderReference({ + headerType: HeaderReferenceType.EVEN, + headerId: headers.even.Header.ReferenceId, + }), + ); + } + } + } + + private addFooters(footers?: IHeaderFooterGroup): void { + if (footers) { + if (footers.default) { + this.root.push( + new FooterReference({ + footerType: FooterReferenceType.DEFAULT, + footerId: footers.default.Footer.ReferenceId, + }), + ); + } + + if (footers.first) { + this.root.push( + new FooterReference({ + footerType: FooterReferenceType.FIRST, + footerId: footers.first.Footer.ReferenceId, + }), + ); + } + + if (footers.even) { + this.root.push( + new FooterReference({ + footerType: FooterReferenceType.EVEN, + footerId: footers.even.Footer.ReferenceId, + }), + ); + } + } } public get Options(): SectionPropertiesOptions { diff --git a/src/file/file.ts b/src/file/file.ts index ce9d331013..addca15c8f 100644 --- a/src/file/file.ts +++ b/src/file/file.ts @@ -2,7 +2,13 @@ import { AppProperties } from "./app-properties/app-properties"; import { ContentTypes } from "./content-types/content-types"; import { CoreProperties, IPropertiesOptions } from "./core-properties"; import { Document } from "./document"; -import { FooterReferenceType, HeaderReference, HeaderReferenceType, SectionPropertiesOptions } from "./document/body/section-properties"; +import { + FooterReferenceType, + HeaderReference, + HeaderReferenceType, + IHeaderFooterGroup, + SectionPropertiesOptions, +} from "./document/body/section-properties"; import { IFileProperties } from "./file-properties"; import { FooterWrapper, IDocumentFooter } from "./footer-wrapper"; import { FootNotes } from "./footnotes"; @@ -91,14 +97,8 @@ export class File { sectionPropertiesOptions = { ...sectionPropertiesOptions, - headers: this.headers.map((header) => ({ - headerId: header.header.Header.ReferenceId, - headerType: header.type, - })), - footers: this.footers.map((footer) => ({ - footerId: footer.footer.Footer.ReferenceId, - footerType: footer.type, - })), + headers: this.groupHeaders(this.headers, sectionPropertiesOptions.headers), + footers: this.groupFooters(this.footers, sectionPropertiesOptions.footers), }; this.document = new Document(sectionPropertiesOptions); @@ -273,6 +273,68 @@ export class File { ); } + private groupHeaders(headers: IDocumentHeader[], group: IHeaderFooterGroup = {}): IHeaderFooterGroup { + let newGroup = group; + + for (const header of headers) { + switch (header.type) { + case HeaderReferenceType.DEFAULT: + newGroup = { + ...newGroup, + default: header.header, + }; + case HeaderReferenceType.FIRST: + newGroup = { + ...newGroup, + first: header.header, + }; + case HeaderReferenceType.EVEN: + newGroup = { + ...newGroup, + even: header.header, + }; + default: + newGroup = { + ...newGroup, + default: header.header, + }; + } + } + + return newGroup; + } + + private groupFooters(footers: IDocumentFooter[], group: IHeaderFooterGroup = {}): IHeaderFooterGroup { + let newGroup = group; + + for (const footer of footers) { + switch (footer.type) { + case FooterReferenceType.DEFAULT: + newGroup = { + ...newGroup, + default: footer.footer, + }; + case FooterReferenceType.FIRST: + newGroup = { + ...newGroup, + first: footer.footer, + }; + case FooterReferenceType.EVEN: + newGroup = { + ...newGroup, + even: footer.footer, + }; + default: + newGroup = { + ...newGroup, + default: footer.footer, + }; + } + } + + return newGroup; + } + public get Document(): Document { return this.document; }