update file/document and section-properties

This commit is contained in:
Tom Hunkapiller
2021-05-25 03:41:12 +03:00
parent e198f0752a
commit 63cea76eac
54 changed files with 861 additions and 659 deletions

View File

@ -0,0 +1,41 @@
import { decimalNumber, twipsMeasureValue } from "file/values";
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
// <xsd:complexType name="CT_Columns">
// <xsd:sequence minOccurs="0">
// <xsd:element name="col" type="CT_Column" maxOccurs="45"/>
// </xsd:sequence>
// <xsd:attribute name="equalWidth" type="s:ST_OnOff" use="optional"/>
// <xsd:attribute name="space" type="s:ST_TwipsMeasure" use="optional" default="720"/>
// <xsd:attribute name="num" type="ST_DecimalNumber" use="optional" default="1"/>
// <xsd:attribute name="sep" type="s:ST_OnOff" use="optional"/>
// </xsd:complexType>
export interface IColumnsAttributes {
readonly space?: number | string;
readonly count?: number;
readonly separate?: boolean;
readonly equalWidth?: boolean;
}
export class ColumnsAttributes extends XmlAttributeComponent<IColumnsAttributes> {
protected readonly xmlKeys = {
space: "w:space",
count: "w:num",
separate: "w:sep",
equalWidth: "w:equalWidth",
};
}
export class Columns extends XmlComponent {
constructor({ space, count, separate, equalWidth }: IColumnsAttributes) {
super("w:cols");
this.root.push(
new ColumnsAttributes({
space: space === undefined ? undefined : twipsMeasureValue(space),
count: count === undefined ? undefined : decimalNumber(count),
separate,
equalWidth,
}),
);
}
}

View File

@ -0,0 +1,38 @@
import { decimalNumber } from "file/values";
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
// not implemented
// <xsd:simpleType name="ST_DocGrid">
// <xsd:restriction base="xsd:string">
// <xsd:enumeration value="default"/>
// <xsd:enumeration value="lines"/>
// <xsd:enumeration value="linesAndChars"/>
// <xsd:enumeration value="snapToChars"/>
// </xsd:restriction>
// </xsd:simpleType>
// <xsd:complexType name="CT_DocGrid">
// <xsd:attribute name="type" type="ST_DocGrid"/>
// <xsd:attribute name="linePitch" type="ST_DecimalNumber"/>
// <xsd:attribute name="charSpace" type="ST_DecimalNumber"/>
// </xsd:complexType>
export interface IDocGridAttributesProperties {
readonly linePitch?: number;
}
export class DocGridAttributes extends XmlAttributeComponent<IDocGridAttributesProperties> {
protected readonly xmlKeys = {
linePitch: "w:linePitch",
};
}
export class DocumentGrid extends XmlComponent {
constructor(linePitch: number) {
super("w:docGrid");
this.root.push(
new DocGridAttributes({
linePitch: decimalNumber(linePitch),
}),
);
}
}

View File

@ -0,0 +1,56 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { HeaderFooterReference, HeaderFooterReferenceType, HeaderFooterType } from "./header-footer-reference";
describe("HeaderFooterReference", () => {
it("#constructor (footer)", () => {
const footer = new HeaderFooterReference(HeaderFooterType.FOOTER, {
type: HeaderFooterReferenceType.DEFAULT,
id: 1,
});
const tree = new Formatter().format(footer);
expect(tree).to.deep.equal({
"w:footerReference": {
_attr: {
"r:id": "rId1",
"w:type": "default",
},
},
});
});
it("#constructor (header)", () => {
const header = new HeaderFooterReference(HeaderFooterType.HEADER, {
type: HeaderFooterReferenceType.DEFAULT,
id: 1,
});
const tree = new Formatter().format(header);
expect(tree).to.deep.equal({
"w:headerReference": {
_attr: {
"r:id": "rId1",
"w:type": "default",
},
},
});
});
it("should create without a type", () => {
const footer = new HeaderFooterReference(HeaderFooterType.FOOTER, {
id: 1,
});
const tree = new Formatter().format(footer);
expect(tree).to.deep.equal({
"w:footerReference": {
_attr: {
"r:id": "rId1",
"w:type": "default",
},
},
});
});
});

View File

@ -0,0 +1,65 @@
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
// <xsd:simpleType name="ST_HdrFtr">
// <xsd:restriction base="xsd:string">
// <xsd:enumeration value="even"/>
// <xsd:enumeration value="default"/>
// <xsd:enumeration value="first"/>
// </xsd:restriction>
// </xsd:simpleType>
export enum HeaderFooterReferenceType {
DEFAULT = "default",
FIRST = "first",
EVEN = "even",
}
// </xsd:complexType>
// <xsd:group name="EG_HdrFtrReferences">
// <xsd:choice>
// <xsd:element name="headerReference" type="CT_HdrFtrRef" minOccurs="0"/>
// <xsd:element name="footerReference" type="CT_HdrFtrRef" minOccurs="0"/>
// </xsd:choice>
// </xsd:group>
// <xsd:complexType name="CT_HdrFtrRef">
// <xsd:complexContent>
// <xsd:extension base="CT_Rel">
// <xsd:attribute name="type" type="ST_HdrFtr" use="required"/>
// </xsd:extension>
// </xsd:complexContent>
// <xsd:complexType name="CT_Rel">
// <xsd:attribute ref="r:id" use="required"/>
// </xsd:complexType>
export interface IHeaderFooterOptions {
readonly type?: HeaderFooterReferenceType;
readonly id?: number;
}
class FooterReferenceAttributes extends XmlAttributeComponent<{
readonly type: HeaderFooterReferenceType;
readonly id: string;
}> {
protected readonly xmlKeys = {
type: "w:type",
id: "r:id",
};
}
export enum HeaderFooterType {
HEADER = "w:headerReference",
FOOTER = "w:footerReference",
}
export class HeaderFooterReference extends XmlComponent {
constructor(type: HeaderFooterType, options: IHeaderFooterOptions) {
super(type);
this.root.push(
new FooterReferenceAttributes({
type: options.type || HeaderFooterReferenceType.DEFAULT,
id: `rId${options.id}`,
}),
);
}
}

View File

@ -0,0 +1,11 @@
export * from "./columns";
export * from "./doc-grid";
// export * from "./header-reference";
export * from "./page-size";
export * from "./page-number";
export * from "./page-borders";
export * from "./page-margin";
export * from "./page-borders";
export * from "./line-number";
export * from "./section-type";
export * from "./header-footer-reference";

View File

@ -0,0 +1,53 @@
// http://officeopenxml.com/WPsectionLineNumbering.php
import { decimalNumber, twipsMeasureValue } from "file/values";
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
// <xsd:simpleType name="ST_LineNumberRestart">
// <xsd:restriction base="xsd:string">
// <xsd:enumeration value="newPage"/>
// <xsd:enumeration value="newSection"/>
// <xsd:enumeration value="continuous"/>
// </xsd:restriction>
// </xsd:simpleType>
export enum LineNumberRestartFormat {
NEW_PAGE = "newPage",
NEW_SECTION = "newSection",
CONTINUOUS = "continuous",
}
// <xsd:complexType name="CT_LineNumber">
// <xsd:attribute name="countBy" type="ST_DecimalNumber" use="optional"/>
// <xsd:attribute name="start" type="ST_DecimalNumber" use="optional" default="1"/>
// <xsd:attribute name="distance" type="s:ST_TwipsMeasure" use="optional"/>
// <xsd:attribute name="restart" type="ST_LineNumberRestart" use="optional" default="newPage"/>
// </xsd:complexType>
export interface ILineNumberAttributes {
readonly countBy?: number;
readonly start?: number;
readonly restart?: LineNumberRestartFormat;
readonly distance?: number | string;
}
export class LineNumberAttributes extends XmlAttributeComponent<ILineNumberAttributes> {
protected readonly xmlKeys = {
countBy: "w:countBy",
start: "w:start",
restart: "w:restart",
distance: "w:distance",
};
}
export class LineNumberType extends XmlComponent {
constructor({ countBy, start, restart, distance }: ILineNumberAttributes) {
super("w:lnNumType");
this.root.push(
new LineNumberAttributes({
countBy: countBy === undefined ? undefined : decimalNumber(countBy),
start: start === undefined ? undefined : decimalNumber(start),
restart,
distance: distance === undefined ? undefined : twipsMeasureValue(distance),
}),
);
}
}

View File

@ -0,0 +1,93 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { BorderStyle } from "file/border";
import { PageBorderDisplay, PageBorders, PageBorderZOrder } from "./page-borders";
describe("PageBorders", () => {
describe("#constructor()", () => {
it("should create empty element when no options are passed", () => {
const properties = new PageBorders();
expect(() => new Formatter().format(properties)).to.throw();
});
it("should create page borders with some configuration", () => {
const properties = new PageBorders({
pageBorders: {
display: PageBorderDisplay.FIRST_PAGE,
},
});
const tree = new Formatter().format(properties);
expect(Object.keys(tree)).to.deep.equal(["w:pgBorders"]);
expect(tree["w:pgBorders"]).to.deep.equal({ _attr: { "w:display": "firstPage" } });
});
it("should create page borders with default configuration", () => {
const properties = new PageBorders({});
const tree = new Formatter().format(properties);
expect(Object.keys(tree)).to.deep.equal(["w:pgBorders"]);
expect(tree).to.deep.equal({
"w:pgBorders": {
_attr: {},
},
});
});
it("should create page borders with full configuration", () => {
const properties = new PageBorders({
pageBorders: {
display: PageBorderDisplay.FIRST_PAGE,
zOrder: PageBorderZOrder.BACK,
},
pageBorderTop: {
style: BorderStyle.DOUBLE_WAVE,
size: 10,
color: "001122",
},
pageBorderRight: {
style: BorderStyle.DOUBLE,
size: 20,
color: "223344",
},
pageBorderBottom: {
style: BorderStyle.SINGLE,
size: 30,
color: "556677",
},
pageBorderLeft: {
style: BorderStyle.DOTTED,
size: 40,
color: "889900",
},
});
const tree = new Formatter().format(properties);
expect(Object.keys(tree)).to.deep.equal(["w:pgBorders"]);
expect(tree["w:pgBorders"]).to.be.an.instanceof(Array);
expect(tree["w:pgBorders"][0]).to.deep.equal({ _attr: { "w:display": "firstPage", "w:zOrder": "back" } });
expect(tree["w:pgBorders"][1]).to.deep.equal({
"w:top": {
_attr: { "w:color": "001122", "w:sz": 10, "w:val": "doubleWave" },
},
});
expect(tree["w:pgBorders"][2]).to.deep.equal({
"w:left": {
_attr: { "w:color": "889900", "w:sz": 40, "w:val": "dotted" },
},
});
expect(tree["w:pgBorders"][3]).to.deep.equal({
"w:bottom": {
_attr: { "w:color": "556677", "w:sz": 30, "w:val": "single" },
},
});
expect(tree["w:pgBorders"][4]).to.deep.equal({
"w:right": {
_attr: { "w:color": "223344", "w:sz": 20, "w:val": "double" },
},
});
});
});
});

View File

@ -0,0 +1,106 @@
// http://officeopenxml.com/WPsectionBorders.php
import { BorderElement, IBorderOptions } from "file/border";
import { IgnoreIfEmptyXmlComponent, XmlAttributeComponent } from "file/xml-components";
// <xsd:simpleType name="ST_PageBorderDisplay">
// <xsd:restriction base="xsd:string">
// <xsd:enumeration value="allPages"/>
// <xsd:enumeration value="firstPage"/>
// <xsd:enumeration value="notFirstPage"/>
// </xsd:restriction>
// </xsd:simpleType>
export enum PageBorderDisplay {
ALL_PAGES = "allPages",
FIRST_PAGE = "firstPage",
NOT_FIRST_PAGE = "notFirstPage",
}
// <xsd:simpleType name="ST_PageBorderOffset">
// <xsd:restriction base="xsd:string">
// <xsd:enumeration value="page"/>
// <xsd:enumeration value="text"/>
// </xsd:restriction>
// </xsd:simpleType>
export enum PageBorderOffsetFrom {
PAGE = "page",
TEXT = "text",
}
// <xsd:simpleType name="ST_PageBorderZOrder">
// <xsd:restriction base="xsd:string">
// <xsd:enumeration value="front"/>
// <xsd:enumeration value="back"/>
// </xsd:restriction>
// </xsd:simpleType>
export enum PageBorderZOrder {
BACK = "back",
FRONT = "front",
}
export interface IPageBorderAttributes {
readonly display?: PageBorderDisplay;
readonly offsetFrom?: PageBorderOffsetFrom;
readonly zOrder?: PageBorderZOrder;
}
export interface IPageBordersOptions {
readonly pageBorders?: IPageBorderAttributes;
readonly pageBorderTop?: IBorderOptions;
readonly pageBorderRight?: IBorderOptions;
readonly pageBorderBottom?: IBorderOptions;
readonly pageBorderLeft?: IBorderOptions;
}
class PageBordersAttributes extends XmlAttributeComponent<IPageBorderAttributes> {
protected readonly xmlKeys = {
display: "w:display",
offsetFrom: "w:offsetFrom",
zOrder: "w:zOrder",
};
}
// <xsd:complexType name="CT_PageBorders">
// <xsd:sequence>
// <xsd:element name="top" type="CT_TopPageBorder" minOccurs="0"/>
// <xsd:element name="left" type="CT_PageBorder" minOccurs="0"/>
// <xsd:element name="bottom" type="CT_BottomPageBorder" minOccurs="0"/>
// <xsd:element name="right" type="CT_PageBorder" minOccurs="0"/>
// </xsd:sequence>
// <xsd:attribute name="zOrder" type="ST_PageBorderZOrder" use="optional" default="front"/>
// <xsd:attribute name="display" type="ST_PageBorderDisplay" use="optional"/>
// <xsd:attribute name="offsetFrom" type="ST_PageBorderOffset" use="optional" default="text"/>
// </xsd:complexType>
export class PageBorders extends IgnoreIfEmptyXmlComponent {
constructor(options?: IPageBordersOptions) {
super("w:pgBorders");
if (!options) {
return;
}
if (options.pageBorders) {
this.root.push(
new PageBordersAttributes({
display: options.pageBorders.display,
offsetFrom: options.pageBorders.offsetFrom,
zOrder: options.pageBorders.zOrder,
}),
);
} else {
this.root.push(new PageBordersAttributes({}));
}
if (options.pageBorderTop) {
this.root.push(new BorderElement("w:top", options.pageBorderTop));
}
if (options.pageBorderLeft) {
this.root.push(new BorderElement("w:left", options.pageBorderLeft));
}
if (options.pageBorderBottom) {
this.root.push(new BorderElement("w:bottom", options.pageBorderBottom));
}
if (options.pageBorderRight) {
this.root.push(new BorderElement("w:right", options.pageBorderRight));
}
}
}

View File

@ -0,0 +1,58 @@
import { signedTwipsMeasureValue, twipsMeasureValue } from "file/values";
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
// <xsd:complexType name="CT_PageMar">
// <xsd:attribute name="top" type="ST_SignedTwipsMeasure" use="required"/>
// <xsd:attribute name="right" type="s:ST_TwipsMeasure" use="required"/>
// <xsd:attribute name="bottom" type="ST_SignedTwipsMeasure" use="required"/>
// <xsd:attribute name="left" type="s:ST_TwipsMeasure" use="required"/>
// <xsd:attribute name="header" type="s:ST_TwipsMeasure" use="required"/>
// <xsd:attribute name="footer" type="s:ST_TwipsMeasure" use="required"/>
// <xsd:attribute name="gutter" type="s:ST_TwipsMeasure" use="required"/>
// </xsd:complexType>
export interface IPageMarginAttributes {
readonly top?: number | string;
readonly right?: number | string;
readonly bottom?: number | string;
readonly left?: number | string;
readonly header?: number | string;
readonly footer?: number | string;
readonly gutter?: number | string;
}
export class PageMarginAttributes extends XmlAttributeComponent<IPageMarginAttributes> {
protected readonly xmlKeys = {
top: "w:top",
right: "w:right",
bottom: "w:bottom",
left: "w:left",
header: "w:header",
footer: "w:footer",
gutter: "w:gutter",
};
}
export class PageMargin extends XmlComponent {
constructor(
top: number | string,
right: number | string,
bottom: number | string,
left: number | string,
header: number | string,
footer: number | string,
gutter: number | string,
) {
super("w:pgMar");
this.root.push(
new PageMarginAttributes({
top: signedTwipsMeasureValue(top),
right: twipsMeasureValue(right),
bottom: signedTwipsMeasureValue(bottom),
left: twipsMeasureValue(left),
header: twipsMeasureValue(header),
footer: twipsMeasureValue(footer),
gutter: twipsMeasureValue(gutter),
}),
);
}
}

View File

@ -0,0 +1,54 @@
// http://officeopenxml.com/WPSectionPgNumType.php
import { NumberFormat } from "file/shared/number-format";
import { decimalNumber } from "file/values";
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
// <xsd:simpleType name="ST_ChapterSep">
// <xsd:restriction base="xsd:string">
// <xsd:enumeration value="hyphen"/>
// <xsd:enumeration value="period"/>
// <xsd:enumeration value="colon"/>
// <xsd:enumeration value="emDash"/>
// <xsd:enumeration value="enDash"/>
// </xsd:restriction>
// </xsd:simpleType>
export enum PageNumberSeparator {
HYPHEN = "hyphen",
PERIOD = "period",
COLON = "colon",
EM_DASH = "emDash",
EN_DASH = "endash",
}
export interface IPageNumberTypeAttributes {
readonly start?: number;
readonly formatType?: NumberFormat;
readonly separator?: PageNumberSeparator;
}
// <xsd:complexType name="CT_PageNumber">
// <xsd:attribute name="fmt" type="ST_NumberFormat" use="optional" default="decimal"/>
// <xsd:attribute name="start" type="ST_DecimalNumber" use="optional"/>
// <xsd:attribute name="chapStyle" type="ST_DecimalNumber" use="optional"/>
// <xsd:attribute name="chapSep" type="ST_ChapterSep" use="optional" default="hyphen"/>
// </xsd:complexType>
export class PageNumberTypeAttributes extends XmlAttributeComponent<IPageNumberTypeAttributes> {
protected readonly xmlKeys = {
start: "w:start",
formatType: "w:fmt",
separator: "w:chapSep",
};
}
export class PageNumberType extends XmlComponent {
constructor({ start, formatType, separator }: IPageNumberTypeAttributes) {
super("w:pgNumType");
this.root.push(
new PageNumberTypeAttributes({
start: start === undefined ? undefined : decimalNumber(start),
formatType,
separator,
}),
);
}
}

View File

@ -0,0 +1,25 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { PageOrientation, PageSize } from "./page-size";
describe("PageSize", () => {
describe("#constructor()", () => {
it("should create page size with portrait", () => {
const properties = new PageSize(100, 200, PageOrientation.PORTRAIT);
const tree = new Formatter().format(properties);
expect(Object.keys(tree)).to.deep.equal(["w:pgSz"]);
expect(tree["w:pgSz"]).to.deep.equal({ _attr: { "w:h": 200, "w:w": 100, "w:orient": "portrait" } });
});
it("should create page size with horizontal and invert the lengths", () => {
const properties = new PageSize(100, 200, PageOrientation.LANDSCAPE);
const tree = new Formatter().format(properties);
expect(Object.keys(tree)).to.deep.equal(["w:pgSz"]);
expect(tree["w:pgSz"]).to.deep.equal({ _attr: { "w:h": 100, "w:w": 200, "w:orient": "landscape" } });
});
});
});

View File

@ -0,0 +1,52 @@
import { twipsMeasureValue } from "file/values";
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
// <xsd:simpleType name="ST_PageOrientation">
// <xsd:restriction base="xsd:string">
// <xsd:enumeration value="portrait"/>
// <xsd:enumeration value="landscape"/>
// </xsd:restriction>
// </xsd:simpleType>
export enum PageOrientation {
PORTRAIT = "portrait",
LANDSCAPE = "landscape",
}
// <xsd:complexType name="CT_PageSz">
// <xsd:attribute name="w" type="s:ST_TwipsMeasure"/>
// <xsd:attribute name="h" type="s:ST_TwipsMeasure"/>
// <xsd:attribute name="orient" type="ST_PageOrientation" use="optional"/>
// <xsd:attribute name="code" type="ST_DecimalNumber" use="optional"/>
// </xsd:complexType>
export interface IPageSizeAttributes {
readonly width?: number | string;
readonly height?: number | string;
readonly orientation?: PageOrientation;
}
export class PageSizeAttributes extends XmlAttributeComponent<IPageSizeAttributes> {
protected readonly xmlKeys = {
width: "w:w",
height: "w:h",
orientation: "w:orient",
};
}
export class PageSize extends XmlComponent {
constructor(width: number | string, height: number | string, orientation: PageOrientation) {
super("w:pgSz");
const flip = orientation === PageOrientation.LANDSCAPE;
const widthTwips = twipsMeasureValue(width);
const heightTwips = twipsMeasureValue(height);
this.root.push(
new PageSizeAttributes({
width: flip ? heightTwips : widthTwips,
height: flip ? widthTwips : heightTwips,
orientation: orientation,
}),
);
}
}

View File

@ -0,0 +1,34 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { SectionType, Type } from "./section-type";
describe("Type", () => {
it("should create with even page section type", () => {
const sectionType = new Type(SectionType.EVEN_PAGE);
const tree = new Formatter().format(sectionType);
expect(tree).to.deep.equal({
"w:type": {
_attr: {
"w:val": "evenPage",
},
},
});
});
it("should create with continuous section type", () => {
const sectionType = new Type(SectionType.CONTINUOUS);
const tree = new Formatter().format(sectionType);
expect(tree).to.deep.equal({
"w:type": {
_attr: {
"w:val": "continuous",
},
},
});
});
});

View File

@ -0,0 +1,37 @@
// http://officeopenxml.com/WPsection.php
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
// <xsd:simpleType name="ST_SectionMark">
// <xsd:restriction base="xsd:string">
// <xsd:enumeration value="nextPage"/>
// <xsd:enumeration value="nextColumn"/>
// <xsd:enumeration value="continuous"/>
// <xsd:enumeration value="evenPage"/>
// <xsd:enumeration value="oddPage"/>
// </xsd:restriction>
// </xsd:simpleType>
export enum SectionType {
NEXT_PAGE = "nextPage",
NEXT_COLUMN = "nextColumn",
CONTINUOUS = "continuous",
EVEN_PAGE = "evenPage",
ODD_PAGE = "oddPage",
}
// <xsd:complexType name="CT_SectType">
// <xsd:attribute name="val" type="ST_SectionMark"/>
// </xsd:complexType>
export class SectionTypeAttributes extends XmlAttributeComponent<{
readonly val: SectionType;
}> {
protected readonly xmlKeys = {
val: "w:val",
};
}
export class Type extends XmlComponent {
constructor(value: SectionType) {
super("w:type");
this.root.push(new SectionTypeAttributes({ val: value }));
}
}