Make .addSection fully declarative

This commit is contained in:
Dolan
2021-03-19 20:53:56 +00:00
parent 4783812044
commit 3299c557a0
86 changed files with 3341 additions and 3278 deletions

View File

@ -14,8 +14,12 @@ describe("Body", () => {
describe("#addSection", () => {
it("should add section with default parameters", () => {
body.addSection({
width: 10000,
height: 10000,
page: {
size: {
width: 10000,
height: 10000,
},
},
});
const tree = new Formatter().format(body);

View File

@ -1,7 +1,7 @@
import { IContext, IXmlableObject, XmlComponent } from "file/xml-components";
import { Paragraph, ParagraphProperties, TableOfContents } from "../..";
import { SectionProperties, SectionPropertiesOptions } from "./section-properties/section-properties";
import { ISectionPropertiesOptions, SectionProperties } from "./section-properties/section-properties";
export class Body extends XmlComponent {
private readonly sections: SectionProperties[] = [];
@ -18,7 +18,7 @@ export class Body extends XmlComponent {
* - last section should be direct child of body
* @param options new section options
*/
public addSection(options: SectionPropertiesOptions): void {
public addSection(options: ISectionPropertiesOptions): void {
const currentSection = this.sections.pop() as SectionProperties;
this.root.push(this.createSectionParagraph(currentSection));

View File

@ -8,18 +8,18 @@ export enum LineNumberRestartFormat {
}
export interface ILineNumberAttributes {
readonly lineNumberCountBy?: number;
readonly lineNumberStart?: number;
readonly lineNumberRestart?: LineNumberRestartFormat;
readonly lineNumberDistance?: number;
readonly countBy?: number;
readonly start?: number;
readonly restart?: LineNumberRestartFormat;
readonly distance?: number;
}
export class LineNumberAttributes extends XmlAttributeComponent<ILineNumberAttributes> {
protected readonly xmlKeys = {
lineNumberCountBy: "w:countBy",
lineNumberStart: "w:start",
lineNumberRestart: "w:restart",
lineNumberDistance: "w:distance",
countBy: "w:countBy",
start: "w:start",
restart: "w:restart",
distance: "w:distance",
};
}
@ -28,10 +28,10 @@ export class LineNumberType extends XmlComponent {
super("w:lnNumType");
this.root.push(
new LineNumberAttributes({
lineNumberCountBy: countBy,
lineNumberStart: start,
lineNumberRestart: restart,
lineNumberDistance: dist,
countBy: countBy,
start: start,
restart: restart,
distance: dist,
}),
);
}

View File

@ -26,16 +26,16 @@ export enum PageNumberSeparator {
}
export interface IPageNumberTypeAttributes {
readonly pageNumberStart?: number;
readonly pageNumberFormatType?: PageNumberFormat;
readonly pageNumberSeparator?: PageNumberSeparator;
readonly start?: number;
readonly formatType?: PageNumberFormat;
readonly separator?: PageNumberSeparator;
}
export class PageNumberTypeAttributes extends XmlAttributeComponent<IPageNumberTypeAttributes> {
protected readonly xmlKeys = {
pageNumberStart: "w:start",
pageNumberFormatType: "w:fmt",
pageNumberSeparator: "w:chapSep",
start: "w:start",
formatType: "w:fmt",
separator: "w:chapSep",
};
}
@ -44,9 +44,9 @@ export class PageNumberType extends XmlComponent {
super("w:pgNumType");
this.root.push(
new PageNumberTypeAttributes({
pageNumberStart: start,
pageNumberFormatType: numberFormat,
pageNumberSeparator: separator,
start: start,
formatType: numberFormat,
separator: separator,
}),
);
}

View File

@ -19,34 +19,46 @@ describe("SectionProperties", () => {
const media = new Media();
const properties = new SectionProperties({
width: 11906,
height: 16838,
top: convertInchesToTwip(1),
right: convertInchesToTwip(1),
bottom: convertInchesToTwip(1),
left: convertInchesToTwip(1),
header: 708,
footer: 708,
gutter: 0,
mirror: false,
page: {
size: {
width: 11906,
height: 16838,
},
margin: {
top: convertInchesToTwip(1),
right: convertInchesToTwip(1),
bottom: convertInchesToTwip(1),
left: convertInchesToTwip(1),
header: 708,
footer: 708,
gutter: 0,
mirror: false,
},
pageNumbers: {
start: 10,
formatType: PageNumberFormat.CARDINAL_TEXT,
},
},
column: {
space: 708,
count: 1,
separate: true,
},
linePitch: convertInchesToTwip(0.25),
headers: {
grid: {
linePitch: convertInchesToTwip(0.25),
},
headerWrapperGroup: {
default: new HeaderWrapper(media, 100),
},
footers: {
footerWrapperGroup: {
even: new FooterWrapper(media, 200),
},
pageNumberStart: 10,
pageNumberFormatType: PageNumberFormat.CARDINAL_TEXT,
titlePage: true,
verticalAlign: SectionVerticalAlignValue.TOP,
});
const tree = new Formatter().format(properties);
expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]);
expect(tree["w:sectPr"]).to.be.an.instanceof(Array);
expect(tree["w:sectPr"][0]).to.deep.equal({ "w:pgSz": { _attr: { "w:h": 16838, "w:w": 11906, "w:orient": "portrait" } } });
@ -98,7 +110,11 @@ describe("SectionProperties", () => {
it("should create section properties with changed options", () => {
const properties = new SectionProperties({
top: 0,
page: {
margin: {
top: 0,
},
},
});
const tree = new Formatter().format(properties);
expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]);
@ -122,7 +138,11 @@ describe("SectionProperties", () => {
it("should create section properties with changed options", () => {
const properties = new SectionProperties({
bottom: 0,
page: {
margin: {
bottom: 0,
},
},
});
const tree = new Formatter().format(properties);
expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]);
@ -146,8 +166,12 @@ describe("SectionProperties", () => {
it("should create section properties with changed options", () => {
const properties = new SectionProperties({
width: 0,
height: 0,
page: {
size: {
width: 0,
height: 0,
},
},
});
const tree = new Formatter().format(properties);
expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]);
@ -171,8 +195,12 @@ describe("SectionProperties", () => {
it("should create section properties with page borders", () => {
const properties = new SectionProperties({
pageBorders: {
offsetFrom: PageBorderOffsetFrom.PAGE,
page: {
borders: {
pageBorders: {
offsetFrom: PageBorderOffsetFrom.PAGE,
},
},
},
});
const tree = new Formatter().format(properties);
@ -185,7 +213,11 @@ describe("SectionProperties", () => {
it("should create section properties with page number type, but without start attribute", () => {
const properties = new SectionProperties({
pageNumberFormatType: PageNumberFormat.UPPER_ROMAN,
page: {
pageNumbers: {
formatType: PageNumberFormat.UPPER_ROMAN,
},
},
});
const tree = new Formatter().format(properties);
expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]);
@ -217,10 +249,12 @@ describe("SectionProperties", () => {
it("should create section properties line number type", () => {
const properties = new SectionProperties({
lineNumberCountBy: 2,
lineNumberStart: 2,
lineNumberRestart: LineNumberRestartFormat.CONTINUOUS,
lineNumberDistance: 4,
lineNumbers: {
countBy: 2,
start: 2,
restart: LineNumberRestartFormat.CONTINUOUS,
distance: 4,
},
});
const tree = new Formatter().format(properties);
expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]);

View File

@ -1,4 +1,6 @@
// http://officeopenxml.com/WPsection.php
// tslint:disable: no-unnecessary-initializer
import { convertInchesToTwip } from "convenience-functions";
import { FooterWrapper } from "file/footer-wrapper";
import { HeaderWrapper } from "file/header-wrapper";
@ -21,7 +23,7 @@ import { IPageSizeAttributes, PageOrientation } from "./page-size/page-size-attr
import { TitlePage } from "./title-page/title-page";
import { Type } from "./type/section-type";
import { SectionType } from "./type/section-type-attributes";
import { ISectionVerticalAlignAttributes, SectionVerticalAlign } from "./vertical-align";
import { SectionVerticalAlign, SectionVerticalAlignValue } from "./vertical-align";
export interface IHeaderFooterGroup<T> {
readonly default?: T;
@ -29,89 +31,71 @@ export interface IHeaderFooterGroup<T> {
readonly even?: T;
}
interface IHeadersOptions {
readonly headers?: IHeaderFooterGroup<HeaderWrapper>;
}
interface IFootersOptions {
readonly footers?: IHeaderFooterGroup<FooterWrapper>;
}
interface ITitlePageOptions {
readonly titlePage?: boolean;
}
export type SectionPropertiesOptions = IPageSizeAttributes &
IPageMarginAttributes &
IDocGridAttributesProperties &
IHeadersOptions &
IFootersOptions &
IPageNumberTypeAttributes &
ILineNumberAttributes &
IPageBordersOptions &
ITitlePageOptions &
ISectionVerticalAlignAttributes & {
readonly column?: {
readonly space?: number;
readonly count?: number;
readonly separate?: boolean;
};
readonly type?: SectionType;
export interface ISectionPropertiesOptions {
readonly page?: {
readonly size?: IPageSizeAttributes;
readonly margin?: IPageMarginAttributes;
readonly pageNumbers?: IPageNumberTypeAttributes;
readonly borders?: IPageBordersOptions;
};
// Need to decouple this from the attributes
readonly grid?: IDocGridAttributesProperties;
readonly headerWrapperGroup?: IHeaderFooterGroup<HeaderWrapper>;
readonly footerWrapperGroup?: IHeaderFooterGroup<FooterWrapper>;
readonly lineNumbers?: ILineNumberAttributes;
readonly titlePage?: boolean;
readonly verticalAlign?: SectionVerticalAlignValue;
readonly column?: {
readonly space?: number;
readonly count?: number;
readonly separate?: boolean;
};
readonly type?: SectionType;
}
export class SectionProperties extends XmlComponent {
public readonly width: number;
public readonly rightMargin: number;
public readonly leftMargin: number;
constructor(
{
width = 11906,
height = 16838,
top = convertInchesToTwip(1),
right = convertInchesToTwip(1),
bottom = convertInchesToTwip(1),
left = convertInchesToTwip(1),
header = 708,
footer = 708,
gutter = 0,
mirror = false,
column = {},
linePitch = 360,
orientation = PageOrientation.PORTRAIT,
headers,
footers,
pageNumberFormatType,
pageNumberStart,
pageNumberSeparator,
lineNumberCountBy,
lineNumberStart,
lineNumberRestart,
lineNumberDistance,
pageBorders,
pageBorderTop,
pageBorderRight,
pageBorderBottom,
pageBorderLeft,
titlePage = false,
verticalAlign,
type,
}: SectionPropertiesOptions = { column: {} },
) {
constructor({
page: {
size: { width = 11906, height = 16838, orientation = PageOrientation.PORTRAIT } = {},
margin: {
top = convertInchesToTwip(1),
right = convertInchesToTwip(1),
bottom = convertInchesToTwip(1),
left = convertInchesToTwip(1),
header = 708,
footer = 708,
gutter = 0,
mirror = false,
} = {},
pageNumbers: {
start: pageNumberStart = undefined,
formatType: pageNumberFormatType = undefined,
separator: pageNumberSeparator = undefined,
} = {},
borders: {
pageBorders = undefined,
pageBorderTop = undefined,
pageBorderRight = undefined,
pageBorderBottom = undefined,
pageBorderLeft = undefined,
} = {},
} = {},
grid: { linePitch = 360 } = {},
headerWrapperGroup = {},
footerWrapperGroup = {},
lineNumbers: { countBy: lineNumberCountBy, start: lineNumberStart, restart: lineNumberRestart, distance: lineNumberDistance } = {},
titlePage = false,
verticalAlign,
column: { space = 708, count = 1, separate = false } = {},
type,
}: ISectionPropertiesOptions = {}) {
super("w:sectPr");
this.leftMargin = left;
this.rightMargin = right;
this.width = width;
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(column.space ? column.space : 708, column.count ? column.count : 1, column.separate ?? false));
this.root.push(new Columns(space, count, separate));
this.root.push(new DocumentGrid(linePitch));
this.addHeaders(headers);
this.addFooters(footers);
this.addHeaders(headerWrapperGroup);
this.addFooters(footerWrapperGroup);
this.root.push(new PageNumberType(pageNumberStart, pageNumberFormatType, pageNumberSeparator));
@ -144,65 +128,61 @@ export class SectionProperties extends XmlComponent {
}
}
private addHeaders(headers?: IHeaderFooterGroup<HeaderWrapper>): void {
if (headers) {
if (headers.default) {
this.root.push(
new HeaderReference({
headerType: HeaderReferenceType.DEFAULT,
headerId: headers.default.View.ReferenceId,
}),
);
}
private addHeaders(headers: IHeaderFooterGroup<HeaderWrapper>): void {
if (headers.default) {
this.root.push(
new HeaderReference({
headerType: HeaderReferenceType.DEFAULT,
headerId: headers.default.View.ReferenceId,
}),
);
}
if (headers.first) {
this.root.push(
new HeaderReference({
headerType: HeaderReferenceType.FIRST,
headerId: headers.first.View.ReferenceId,
}),
);
}
if (headers.first) {
this.root.push(
new HeaderReference({
headerType: HeaderReferenceType.FIRST,
headerId: headers.first.View.ReferenceId,
}),
);
}
if (headers.even) {
this.root.push(
new HeaderReference({
headerType: HeaderReferenceType.EVEN,
headerId: headers.even.View.ReferenceId,
}),
);
}
if (headers.even) {
this.root.push(
new HeaderReference({
headerType: HeaderReferenceType.EVEN,
headerId: headers.even.View.ReferenceId,
}),
);
}
}
private addFooters(footers?: IHeaderFooterGroup<FooterWrapper>): void {
if (footers) {
if (footers.default) {
this.root.push(
new FooterReference({
footerType: FooterReferenceType.DEFAULT,
footerId: footers.default.View.ReferenceId,
}),
);
}
private addFooters(footers: IHeaderFooterGroup<FooterWrapper>): void {
if (footers.default) {
this.root.push(
new FooterReference({
footerType: FooterReferenceType.DEFAULT,
footerId: footers.default.View.ReferenceId,
}),
);
}
if (footers.first) {
this.root.push(
new FooterReference({
footerType: FooterReferenceType.FIRST,
footerId: footers.first.View.ReferenceId,
}),
);
}
if (footers.first) {
this.root.push(
new FooterReference({
footerType: FooterReferenceType.FIRST,
footerId: footers.first.View.ReferenceId,
}),
);
}
if (footers.even) {
this.root.push(
new FooterReference({
footerType: FooterReferenceType.EVEN,
footerId: footers.even.View.ReferenceId,
}),
);
}
if (footers.even) {
this.root.push(
new FooterReference({
footerType: FooterReferenceType.EVEN,
footerId: footers.even.View.ReferenceId,
}),
);
}
}
}

View File

@ -1,11 +1,9 @@
import { XmlAttributeComponent } from "file/xml-components";
import { SectionVerticalAlignValue } from "./vertical-align";
export interface ISectionVerticalAlignAttributes {
export class SectionVerticalAlignAttributes extends XmlAttributeComponent<{
readonly verticalAlign?: SectionVerticalAlignValue;
}
export class SectionVerticalAlignAttributes extends XmlAttributeComponent<ISectionVerticalAlignAttributes> {
}> {
protected readonly xmlKeys = {
verticalAlign: "w:val",
};