Merge pull request #956 from devoidfury/experiment

v7 prep - includes redesigning parts of core, and unifying different types
This commit is contained in:
Dolan
2021-06-01 01:12:32 +01:00
committed by GitHub
208 changed files with 3800 additions and 4196 deletions

View File

@ -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, Footer, Header, Packer, PageNumber, PageNumberFormat, PageOrientation, Paragraph, TextRun } from "../build";
import { Document, Footer, Header, Packer, PageNumber, NumberFormat, PageOrientation, Paragraph, TextRun } from "../build";
const doc = new Document({
sections: [
@ -13,7 +13,7 @@ const doc = new Document({
page: {
pageNumbers: {
start: 1,
formatType: PageNumberFormat.DECIMAL,
formatType: NumberFormat.DECIMAL,
},
},
},
@ -38,7 +38,7 @@ const doc = new Document({
},
pageNumbers: {
start: 1,
formatType: PageNumberFormat.DECIMAL,
formatType: NumberFormat.DECIMAL,
},
},
},
@ -85,7 +85,7 @@ const doc = new Document({
orientation: PageOrientation.PORTRAIT,
},
pageNumbers: {
formatType: PageNumberFormat.UPPER_ROMAN,
formatType: NumberFormat.UPPER_ROMAN,
},
},
},
@ -116,7 +116,7 @@ const doc = new Document({
},
pageNumbers: {
start: 25,
formatType: PageNumberFormat.DECIMAL,
formatType: NumberFormat.DECIMAL,
},
},
},

View File

@ -24,7 +24,7 @@ const doc = new Document({
size: 28,
bold: true,
italics: true,
color: "red",
color: "FF0000",
},
paragraph: {
spacing: {

View File

@ -36,17 +36,17 @@ const doc = new Document({
top: {
style: BorderStyle.DASH_DOT_STROKED,
size: 3,
color: "red",
color: "FF0000",
},
bottom: {
style: BorderStyle.DOUBLE,
size: 3,
color: "blue",
color: "0000FF",
},
left: {
style: BorderStyle.DASH_DOT_STROKED,
size: 3,
color: "green",
color: "00FF00",
},
right: {
style: BorderStyle.DASH_DOT_STROKED,

View File

@ -1,7 +1,7 @@
// Creates two paragraphs, one with a border and one without
// Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs";
import { Document, Packer, Paragraph } from "../build";
import { BorderStyle, Document, Packer, Paragraph } from "../build";
const doc = new Document({
sections: [
@ -14,13 +14,13 @@ const doc = new Document({
top: {
color: "auto",
space: 1,
value: "single",
style: BorderStyle.SINGLE,
size: 6,
},
bottom: {
color: "auto",
space: 1,
value: "single",
style: BorderStyle.SINGLE,
size: 6,
},
},

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,
// making the entries be hyperlinks for the paragraph
const doc = new File({
features: {
updateFields: true,
},
styles: {
paragraphStyles: [
{

View File

@ -98,7 +98,7 @@ const table3 = new Table({
children: [new Paragraph("Bar1")],
shading: {
fill: "b79c2f",
val: ShadingType.REVERSE_DIAGONAL_STRIPE,
type: ShadingType.REVERSE_DIAGONAL_STRIPE,
color: "auto",
},
}),
@ -106,7 +106,7 @@ const table3 = new Table({
children: [new Paragraph("Bar2")],
shading: {
fill: "42c5f4",
val: ShadingType.PERCENT_95,
type: ShadingType.PERCENT_95,
color: "auto",
},
}),
@ -114,7 +114,7 @@ const table3 = new Table({
children: [new Paragraph("Bar3")],
shading: {
fill: "880aa8",
val: ShadingType.PERCENT_10,
type: ShadingType.PERCENT_10,
color: "e2df0b",
},
}),
@ -122,7 +122,7 @@ const table3 = new Table({
children: [new Paragraph("Bar4")],
shading: {
fill: "FF0000",
val: ShadingType.CLEAR,
type: ShadingType.CLEAR,
color: "auto",
},
}),
@ -224,22 +224,22 @@ const borders = {
top: {
style: BorderStyle.DASH_SMALL_GAP,
size: 1,
color: "red",
color: "FF0000",
},
bottom: {
style: BorderStyle.DASH_SMALL_GAP,
size: 1,
color: "red",
color: "FF0000",
},
left: {
style: BorderStyle.DASH_SMALL_GAP,
size: 1,
color: "red",
color: "FF0000",
},
right: {
style: BorderStyle.DASH_SMALL_GAP,
size: 1,
color: "red",
color: "FF0000",
},
};

View File

@ -1,7 +1,7 @@
// Example how to display page numbers
// Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs";
import { AlignmentType, Document, Footer, Header, Packer, PageBreak, PageNumber, PageNumberFormat, Paragraph, TextRun } from "../build";
import { AlignmentType, Document, Footer, Header, Packer, PageBreak, PageNumber, NumberFormat, Paragraph, TextRun } from "../build";
const doc = new Document({
sections: [
@ -10,7 +10,7 @@ const doc = new Document({
page: {
pageNumbers: {
start: 1,
formatType: PageNumberFormat.DECIMAL,
formatType: NumberFormat.DECIMAL,
},
},
},

View File

@ -14,7 +14,7 @@ const doc = new Document({
children: [
new TextRun({
text: "Hello World",
color: "red",
color: "FF0000",
bold: true,
size: 24,
font: {

View File

@ -14,7 +14,7 @@ const doc = new Document({
children: [
new TextRun({
text: "Hello World",
color: "red",
color: "FF0000",
bold: true,
size: 24,
font: {

View File

@ -1,7 +1,7 @@
// Multiple sections with total number of pages in each section
// Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs";
import { AlignmentType, Document, Footer, Header, Packer, PageBreak, PageNumber, PageNumberFormat, Paragraph, TextRun } from "../build";
import { AlignmentType, Document, Footer, Header, Packer, PageBreak, PageNumber, NumberFormat, Paragraph, TextRun } from "../build";
const header = new Header({
children: [
@ -31,7 +31,7 @@ const doc = new Document({
page: {
pageNumbers: {
start: 1,
formatType: PageNumberFormat.DECIMAL,
formatType: NumberFormat.DECIMAL,
},
},
},
@ -52,7 +52,7 @@ const doc = new Document({
page: {
pageNumbers: {
start: 1,
formatType: PageNumberFormat.DECIMAL,
formatType: NumberFormat.DECIMAL,
},
},
},

View File

@ -1,13 +1,13 @@
// Example of making content of section vertically aligned
// Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs";
import { Document, Packer, Paragraph, SectionVerticalAlignValue, TextRun } from "../build";
import { Document, Packer, Paragraph, VerticalAlign, TextRun } from "../build";
const doc = new Document({
sections: [
{
properties: {
verticalAlign: SectionVerticalAlignValue.CENTER,
verticalAlign: VerticalAlign.CENTER,
},
children: [
new Paragraph({

View File

@ -24,22 +24,22 @@ const table = new Table({
top: {
style: BorderStyle.DASH_SMALL_GAP,
size: 1,
color: "red",
color: "ff0000",
},
bottom: {
style: BorderStyle.DASH_SMALL_GAP,
size: 1,
color: "red",
color: "ff0000",
},
left: {
style: BorderStyle.DASH_SMALL_GAP,
size: 1,
color: "red",
color: "ff0000",
},
right: {
style: BorderStyle.DASH_SMALL_GAP,
size: 1,
color: "red",
color: "ff0000",
},
},
children: [new Paragraph("Hello")],

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.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
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({
@ -85,7 +87,7 @@ const doc = new Document({
new DeletedTextRun({
break: 1,
text: "in order",
color: "red",
color: "ff0000",
bold: true,
size: 24,
font: {

View File

@ -1,7 +1,16 @@
// Text Frame (Text Box) example
// Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs";
import { Document, FrameAnchorType, HorizontalPositionAlign, Packer, Paragraph, TextRun, VerticalPositionAlign } from "../build";
import {
BorderStyle,
Document,
FrameAnchorType,
HorizontalPositionAlign,
Packer,
Paragraph,
TextRun,
VerticalPositionAlign,
} from "../build";
const doc = new Document({
sections: [
@ -29,25 +38,25 @@ const doc = new Document({
top: {
color: "auto",
space: 1,
value: "single",
style: BorderStyle.SINGLE,
size: 6,
},
bottom: {
color: "auto",
space: 1,
value: "single",
style: BorderStyle.SINGLE,
size: 6,
},
left: {
color: "auto",
space: 1,
value: "single",
style: BorderStyle.SINGLE,
size: 6,
},
right: {
color: "auto",
space: 1,
value: "single",
style: BorderStyle.SINGLE,
size: 6,
},
},

View File

@ -37,7 +37,7 @@ const paragraph = new Paragraph({
new TextRun("This is a simple demo"),
new DeletedTextRun({
text: "with a deletion.",
color: "red",
color: "ff0000",
bold: true,
size: 24,
id: 0,

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.
```ts
const toc = new TableOfContents("Summary", {
hyperlink: true,
headingStyleRange: "1-5",
stylesWithLevels: [new StyleLevel("MySpectacularStyle", 1)],
});
**Note**: updateFields feature must be enabled for TableOfContents to update correctly.
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

View File

@ -53,6 +53,18 @@ const table = new Table({
});
```
### Set Indent
```ts
const table = new Table({
...,
indent: {
size: 600,
type: WidthType.DXA,
}
});
```
## Table Row
A table consists of multiple `table rows`. Table rows have a list of `children` which accepts a list of `table cells` explained below. You can create a simple `table row` like so:
@ -145,7 +157,7 @@ const tableRow = new TableRow({
| Property | Type | Notes |
| ------------- | ----------------------------------- | ----------------------------------------------------------- |
| children | `Array<Paragraph or Table>` | Required. You can nest tables by adding a table into a cell |
| shading | `ITableShadingAttributesProperties` | Optional |
| shading | `IShadingAttributesProperties` | Optional |
| margins | `ITableCellMarginOptions` | Optional |
| verticalAlign | `VerticalAlign` | Optional |
| columnSpan | `number` | Optional |
@ -171,7 +183,7 @@ const cell = new TableCell({
top: {
style: BorderStyle.DASH_DOT_STROKED,
size: 1,
color: "red",
color: "ff0000",
},
bottom: {
style: BorderStyle.THICK_THIN_MEDIUM_GAP,
@ -190,12 +202,12 @@ Google DOCS does not support start and end borders, instead they use left and ri
const cell = new TableCell({
...,
borders: {
top: {
left: {
style: BorderStyle.DOT_DOT_DASH,
size: 3,
color: "green",
color: "00FF00",
},
bottom: {
right: {
style: BorderStyle.DOT_DOT_DASH,
size: 3,
color: "ff8000",

View File

@ -18,14 +18,14 @@ describe("Utility", () => {
});
describe("#uniqueNumericId", () => {
it("should generate a unique ID", () => {
it("should generate a unique incrementing ID", () => {
// tslint:disable-next-line: no-unused-expression
expect(uniqueNumericId()).to.not.be.undefined;
});
});
describe("#uniqueId", () => {
it("should call the underlying header's addChildElement", () => {
it("should generate a unique pseudorandom ID", () => {
// tslint:disable-next-line: no-unused-expression
expect(uniqueId()).to.not.be.empty;
});

View File

@ -12,7 +12,7 @@ export const convertInchesToTwip = (inches: number): number => {
};
export const uniqueNumericId = (): number => {
return currentCount++;
return ++currentCount;
};
export const uniqueId = (): string => {

View File

@ -45,18 +45,10 @@ describe("Formatter", () => {
{
"w:rPr": [
{
"w:b": {
_attr: {
"w:val": true,
},
},
"w:b": {},
},
{
"w:bCs": {
_attr: {
"w:val": true,
},
},
"w:bCs": {},
},
],
},

View File

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

View File

@ -0,0 +1,54 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { BorderStyle } from "file/border";
import { BorderElement } from "./border";
describe("BorderElement", () => {
describe("#constructor", () => {
it("should create a simple border element", () => {
const border = new BorderElement("w:top", {
style: BorderStyle.SINGLE,
});
const tree = new Formatter().format(border);
expect(tree).to.deep.equal({
"w:top": {
_attr: {
"w:val": "single",
},
},
});
});
it("should create a simple border element with a size", () => {
const border = new BorderElement("w:top", {
style: BorderStyle.SINGLE,
size: 22,
});
const tree = new Formatter().format(border);
expect(tree).to.deep.equal({
"w:top": {
_attr: {
"w:val": "single",
"w:sz": 22,
},
},
});
});
it("should create a simple border element with space", () => {
const border = new BorderElement("w:top", {
style: BorderStyle.SINGLE,
space: 22,
});
const tree = new Formatter().format(border);
expect(tree).to.deep.equal({
"w:top": {
_attr: {
"w:val": "single",
"w:space": 22,
},
},
});
});
});
});

86
src/file/border/border.ts Normal file
View File

@ -0,0 +1,86 @@
// Note that the border type is identical in all places,
// regardless of where it's used like paragraph/table/etc.
// PageBorders are a superset, but we're not using any of those extras.
//
// http://officeopenxml.com/WPborders.php
// http://officeopenxml.com/WPtableBorders.php
// http://officeopenxml.com/WPtableCellProperties-Borders.php
// http://officeopenxml.com/WPsectionBorders.php
//
// This describes the CT_Border type.
// <xsd:complexType name="CT_Border">
// <xsd:attribute name="val" type="ST_Border" use="required"/>
// <xsd:attribute name="color" type="ST_HexColor" use="optional" default="auto"/>
// <xsd:attribute name="themeColor" type="ST_ThemeColor" use="optional"/>
// <xsd:attribute name="themeTint" type="ST_UcharHexNumber" use="optional"/>
// <xsd:attribute name="themeShade" type="ST_UcharHexNumber" use="optional"/>
// <xsd:attribute name="sz" type="ST_EighthPointMeasure" use="optional"/>
// <xsd:attribute name="space" type="ST_PointMeasure" use="optional" default="0"/>
// <xsd:attribute name="shadow" type="s:ST_OnOff" use="optional"/>
// <xsd:attribute name="frame" type="s:ST_OnOff" use="optional"/>
// </xsd:complexType>
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
import { eighthPointMeasureValue, hexColorValue, pointMeasureValue } from "../values";
export interface IBorderOptions {
readonly style: BorderStyle;
/** Border color, in hex (eg 'FF00AA') */
readonly color?: string;
/** Size of the border in 1/8 pt */
readonly size?: number;
/** Spacing offset. Values are specified in pt */
readonly space?: number;
}
export class BorderElement extends XmlComponent {
constructor(elementName: string, { color, size, space, style }: IBorderOptions) {
super(elementName);
this.root.push(
new BordersAttributes({
style,
color: color === undefined ? undefined : hexColorValue(color),
size: size === undefined ? undefined : eighthPointMeasureValue(size),
space: space === undefined ? undefined : pointMeasureValue(space),
}),
);
}
}
class BordersAttributes extends XmlAttributeComponent<IBorderOptions> {
protected readonly xmlKeys = {
style: "w:val",
color: "w:color",
size: "w:sz",
space: "w:space",
};
}
export enum BorderStyle {
SINGLE = "single",
DASH_DOT_STROKED = "dashDotStroked",
DASHED = "dashed",
DASH_SMALL_GAP = "dashSmallGap",
DOT_DASH = "dotDash",
DOT_DOT_DASH = "dotDotDash",
DOTTED = "dotted",
DOUBLE = "double",
DOUBLE_WAVE = "doubleWave",
INSET = "inset",
NIL = "nil",
NONE = "none",
OUTSET = "outset",
THICK = "thick",
THICK_THIN_LARGE_GAP = "thickThinLargeGap",
THICK_THIN_MEDIUM_GAP = "thickThinMediumGap",
THICK_THIN_SMALL_GAP = "thickThinSmallGap",
THIN_THICK_LARGE_GAP = "thinThickLargeGap",
THIN_THICK_MEDIUM_GAP = "thinThickMediumGap",
THIN_THICK_SMALL_GAP = "thinThickSmallGap",
THIN_THICK_THIN_LARGE_GAP = "thinThickThinLargeGap",
THIN_THICK_THIN_MEDIUM_GAP = "thinThickThinMediumGap",
THIN_THICK_THIN_SMALL_GAP = "thinThickThinSmallGap",
THREE_D_EMBOSS = "threeDEmboss",
THREE_D_ENGRAVE = "threeDEngrave",
TRIPLE = "triple",
WAVE = "wave",
}

1
src/file/border/index.ts Normal file
View File

@ -0,0 +1 @@
export * from "./border";

View File

@ -1,89 +0,0 @@
import { XmlComponent } from "file/xml-components";
import { DocumentAttributes } from "../document/document-attributes";
export class Title extends XmlComponent {
constructor(value: string) {
super("dc:title");
this.root.push(value);
}
}
export class Subject extends XmlComponent {
constructor(value: string) {
super("dc:subject");
this.root.push(value);
}
}
export class Creator extends XmlComponent {
constructor(value: string) {
super("dc:creator");
this.root.push(value);
}
}
export class Keywords extends XmlComponent {
constructor(value: string) {
super("cp:keywords");
this.root.push(value);
}
}
export class Description extends XmlComponent {
constructor(value: string) {
super("dc:description");
this.root.push(value);
}
}
export class LastModifiedBy extends XmlComponent {
constructor(value: string) {
super("cp:lastModifiedBy");
this.root.push(value);
}
}
export class Revision extends XmlComponent {
constructor(value: string) {
super("cp:revision");
this.root.push(value);
}
}
export abstract class DateComponent extends XmlComponent {
protected getCurrentDate(): string {
const date = new Date();
const year = date.getFullYear();
const month = ("0" + (date.getMonth() + 1)).slice(-2);
const day = ("0" + date.getDate()).slice(-2);
const hours = ("0" + date.getHours()).slice(-2);
const minutes = ("0" + date.getMinutes()).slice(-2);
const seconds = ("0" + date.getSeconds()).slice(-2);
return year + "-" + month + "-" + day + "T" + hours + ":" + minutes + ":" + seconds + "Z";
}
}
export class Created extends DateComponent {
constructor() {
super("dcterms:created");
this.root.push(
new DocumentAttributes({
type: "dcterms:W3CDTF",
}),
);
this.root.push(this.getCurrentDate());
}
}
export class Modified extends DateComponent {
constructor() {
super("dcterms:modified");
this.root.push(
new DocumentAttributes({
type: "dcterms:W3CDTF",
}),
);
this.root.push(this.getCurrentDate());
}
}

View File

@ -27,8 +27,7 @@ describe("Properties", () => {
const tree = new Formatter().format(properties);
expect(Object.keys(tree)).to.deep.equal(["cp:coreProperties"]);
expect(tree["cp:coreProperties"]).to.be.an.instanceof(Array);
expect(Object.keys(tree["cp:coreProperties"][0])).to.deep.equal(["_attr"]);
expect(tree["cp:coreProperties"][1]).to.deep.equal({ "dc:title": ["test document"] });
expect(tree["cp:coreProperties"]).to.deep.include({ "dc:title": ["test document"] });
});
it("should create properties with all the attributes given", () => {
@ -44,9 +43,9 @@ describe("Properties", () => {
const tree = new Formatter().format(properties);
expect(Object.keys(tree)).to.deep.equal(["cp:coreProperties"]);
expect(tree["cp:coreProperties"]).to.be.an.instanceof(Array);
const key = (obj) => Object.keys(obj)[0];
const props = tree["cp:coreProperties"].map(key).sort();
expect(props).to.deep.equal([
expect(tree["cp:coreProperties"].map(key)).to.include.members([
"_attr",
"cp:keywords",
"cp:lastModifiedBy",
@ -58,7 +57,7 @@ describe("Properties", () => {
"dcterms:created",
"dcterms:modified",
]);
expect(tree["cp:coreProperties"].slice(1, -2).sort((a, b) => (key(a) < key(b) ? -1 : 1))).to.deep.equal([
expect(tree["cp:coreProperties"]).to.deep.include.members([
{ "cp:keywords": ["test docx"] },
{ "cp:lastModifiedBy": ["the author"] },
{ "cp:revision": ["123"] },

View File

@ -1,4 +1,4 @@
import { XmlComponent } from "file/xml-components";
import { StringContainer, XmlComponent } from "file/xml-components";
import { ICustomPropertyOptions } from "../custom-properties";
import { IDocumentBackgroundOptions } from "../document";
@ -7,7 +7,7 @@ import { ISectionOptions } from "../file";
import { INumberingOptions } from "../numbering";
import { Paragraph } from "../paragraph";
import { IStylesOptions } from "../styles";
import { Created, Creator, Description, Keywords, LastModifiedBy, Modified, Revision, Subject, Title } from "./components";
import { dateTimeValue } from "../values";
export interface IPropertiesOptions {
readonly sections: ISectionOptions[];
@ -29,12 +29,35 @@ export interface IPropertiesOptions {
readonly background?: IDocumentBackgroundOptions;
readonly features?: {
readonly trackRevisions?: boolean;
readonly updateFields?: boolean;
};
readonly compatabilityModeVersion?: number;
readonly customProperties?: ICustomPropertyOptions[];
readonly evenAndOddHeaderAndFooters?: boolean;
}
// <xs:element name="coreProperties" type="CT_CoreProperties"/>
// <xs:complexType name="CT_CoreProperties">
// <xs:all>
// <xs:element name="category" minOccurs="0" maxOccurs="1" type="xs:string"/>
// <xs:element name="contentStatus" minOccurs="0" maxOccurs="1" type="xs:string"/>
// <xs:element ref="dcterms:created" minOccurs="0" maxOccurs="1"/>
// <xs:element ref="dc:creator" minOccurs="0" maxOccurs="1"/>
// <xs:element ref="dc:description" minOccurs="0" maxOccurs="1"/>
// <xs:element ref="dc:identifier" minOccurs="0" maxOccurs="1"/>
// <xs:element name="keywords" minOccurs="0" maxOccurs="1" type="CT_Keywords"/>
// <xs:element ref="dc:language" minOccurs="0" maxOccurs="1"/>
// <xs:element name="lastModifiedBy" minOccurs="0" maxOccurs="1" type="xs:string"/>
// <xs:element name="lastPrinted" minOccurs="0" maxOccurs="1" type="xs:dateTime"/>
// <xs:element ref="dcterms:modified" minOccurs="0" maxOccurs="1"/>
// <xs:element name="revision" minOccurs="0" maxOccurs="1" type="xs:string"/>
// <xs:element ref="dc:subject" minOccurs="0" maxOccurs="1"/>
// <xs:element ref="dc:title" minOccurs="0" maxOccurs="1"/>
// <xs:element name="version" minOccurs="0" maxOccurs="1" type="xs:string"/>
// </xs:all>
// </xs:complexType>
export class CoreProperties extends XmlComponent {
constructor(options: Omit<IPropertiesOptions, "sections">) {
super("cp:coreProperties");
@ -48,27 +71,39 @@ export class CoreProperties extends XmlComponent {
}),
);
if (options.title) {
this.root.push(new Title(options.title));
this.root.push(new StringContainer("dc:title", options.title));
}
if (options.subject) {
this.root.push(new Subject(options.subject));
this.root.push(new StringContainer("dc:subject", options.subject));
}
if (options.creator) {
this.root.push(new Creator(options.creator));
this.root.push(new StringContainer("dc:creator", options.creator));
}
if (options.keywords) {
this.root.push(new Keywords(options.keywords));
this.root.push(new StringContainer("cp:keywords", options.keywords));
}
if (options.description) {
this.root.push(new Description(options.description));
this.root.push(new StringContainer("dc:description", options.description));
}
if (options.lastModifiedBy) {
this.root.push(new LastModifiedBy(options.lastModifiedBy));
this.root.push(new StringContainer("cp:lastModifiedBy", options.lastModifiedBy));
}
if (options.revision) {
this.root.push(new Revision(options.revision));
this.root.push(new StringContainer("cp:revision", options.revision));
}
this.root.push(new Created());
this.root.push(new Modified());
this.root.push(new TimestampElement("dcterms:created"));
this.root.push(new TimestampElement("dcterms:modified"));
}
}
class TimestampElement extends XmlComponent {
constructor(name: string) {
super(name);
this.root.push(
new DocumentAttributes({
type: "dcterms:W3CDTF",
}),
);
this.root.push(dateTimeValue(new Date()));
}
}

View File

@ -3,6 +3,7 @@ import { expect } from "chai";
import { Formatter } from "export/formatter";
import { Body } from "./body";
import { sectionMarginDefaults } from "./section-properties";
describe("Body", () => {
let body: Body;
@ -32,13 +33,13 @@ describe("Body", () => {
{
"w:pgMar": {
_attr: {
"w:top": 1440,
"w:right": 1440,
"w:bottom": 1440,
"w:left": 1440,
"w:header": 708,
"w:footer": 708,
"w:gutter": 0,
"w:top": sectionMarginDefaults.TOP,
"w:right": sectionMarginDefaults.RIGHT,
"w:bottom": sectionMarginDefaults.BOTTOM,
"w:left": sectionMarginDefaults.LEFT,
"w:header": sectionMarginDefaults.HEADER,
"w:footer": sectionMarginDefaults.FOOTER,
"w:gutter": sectionMarginDefaults.GUTTER,
},
},
},
@ -47,7 +48,7 @@ describe("Body", () => {
_attr: {},
},
},
{ "w:cols": { _attr: { "w:space": 708, "w:sep": false, "w:num": 1 } } },
// { "w:cols": { _attr: { "w:space": 708, "w:sep": false, "w:num": 1 } } },
{ "w:docGrid": { _attr: { "w:linePitch": 360 } } },
],
},

View File

@ -1,6 +1,6 @@
import { IContext, IXmlableObject, XmlComponent } from "file/xml-components";
import { Paragraph, ParagraphProperties, TableOfContents } from "../..";
import { Paragraph, ParagraphProperties } from "../..";
import { ISectionPropertiesOptions, SectionProperties } from "./section-properties/section-properties";
export class Body extends XmlComponent {
@ -38,10 +38,6 @@ export class Body extends XmlComponent {
this.root.push(component);
}
public getTablesOfContents(): TableOfContents[] {
return this.root.filter((child) => child instanceof TableOfContents) as TableOfContents[];
}
private createSectionParagraph(section: SectionProperties): Paragraph {
const paragraph = new Paragraph({});
const properties = new ParagraphProperties({});

View File

@ -1,13 +0,0 @@
import { XmlAttributeComponent } from "file/xml-components";
export class ColumnsAttributes extends XmlAttributeComponent<{
readonly space?: number;
readonly num?: number;
readonly separate?: boolean;
}> {
protected readonly xmlKeys = {
space: "w:space",
num: "w:num",
separate: "w:sep",
};
}

View File

@ -1,15 +0,0 @@
import { XmlComponent } from "file/xml-components";
import { ColumnsAttributes } from "./columns-attributes";
export class Columns extends XmlComponent {
constructor(space: number, num: number, separate: boolean) {
super("w:cols");
this.root.push(
new ColumnsAttributes({
space: space,
num: num,
separate: separate,
}),
);
}
}

View File

@ -1,11 +0,0 @@
import { XmlAttributeComponent } from "file/xml-components";
export interface IDocGridAttributesProperties {
readonly linePitch?: number;
}
export class DocGridAttributes extends XmlAttributeComponent<IDocGridAttributesProperties> {
protected readonly xmlKeys = {
linePitch: "w:linePitch",
};
}

View File

@ -1,13 +0,0 @@
import { XmlComponent } from "file/xml-components";
import { DocGridAttributes } from "./doc-grid-attributes";
export class DocumentGrid extends XmlComponent {
constructor(linePitch: number) {
super("w:docGrid");
this.root.push(
new DocGridAttributes({
linePitch: linePitch,
}),
);
}
}

View File

@ -1,17 +0,0 @@
import { XmlAttributeComponent } from "file/xml-components";
export enum FooterReferenceType {
DEFAULT = "default",
FIRST = "first",
EVEN = "even",
}
export class FooterReferenceAttributes extends XmlAttributeComponent<{
readonly type: string;
readonly id: string;
}> {
protected readonly xmlKeys = {
type: "w:type",
id: "r:id",
};
}

View File

@ -1,42 +0,0 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { FooterReference } from "./footer-reference";
import { FooterReferenceType } from "./footer-reference-attributes";
describe("footerReference", () => {
it("should create", () => {
const footer = new FooterReference({
footerType: FooterReferenceType.DEFAULT,
footerId: 1,
});
const tree = new Formatter().format(footer);
expect(tree).to.deep.equal({
"w:footerReference": {
_attr: {
"r:id": "rId1",
"w:type": "default",
},
},
});
});
it("should create without a footer type", () => {
const footer = new FooterReference({
footerId: 1,
});
const tree = new Formatter().format(footer);
expect(tree).to.deep.equal({
"w:footerReference": {
_attr: {
"r:id": "rId1",
"w:type": "default",
},
},
});
});
});

View File

@ -1,20 +0,0 @@
import { XmlComponent } from "file/xml-components";
import { FooterReferenceAttributes, FooterReferenceType } from "./footer-reference-attributes";
export interface IFooterOptions {
readonly footerType?: FooterReferenceType;
readonly footerId?: number;
}
export class FooterReference extends XmlComponent {
constructor(options: IFooterOptions) {
super("w:footerReference");
this.root.push(
new FooterReferenceAttributes({
type: options.footerType || FooterReferenceType.DEFAULT,
id: `rId${options.footerId}`,
}),
);
}
}

View File

@ -1,2 +0,0 @@
export * from "./footer-reference";
export * from "./footer-reference-attributes";

View File

@ -1,17 +0,0 @@
import { XmlAttributeComponent } from "file/xml-components";
export enum HeaderReferenceType {
DEFAULT = "default",
FIRST = "first",
EVEN = "even",
}
export class HeaderReferenceAttributes extends XmlAttributeComponent<{
readonly type: string;
readonly id: string;
}> {
protected readonly xmlKeys = {
type: "w:type",
id: "r:id",
};
}

View File

@ -1,42 +0,0 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { HeaderReference } from "./header-reference";
import { HeaderReferenceType } from "./header-reference-attributes";
describe("HeaderReference", () => {
it("should create", () => {
const footer = new HeaderReference({
headerType: HeaderReferenceType.DEFAULT,
headerId: 1,
});
const tree = new Formatter().format(footer);
expect(tree).to.deep.equal({
"w:headerReference": {
_attr: {
"r:id": "rId1",
"w:type": "default",
},
},
});
});
it("should create without a header type", () => {
const footer = new HeaderReference({
headerId: 1,
});
const tree = new Formatter().format(footer);
expect(tree).to.deep.equal({
"w:headerReference": {
_attr: {
"r:id": "rId1",
"w:type": "default",
},
},
});
});
});

View File

@ -1,19 +0,0 @@
import { XmlComponent } from "file/xml-components";
import { HeaderReferenceAttributes, HeaderReferenceType } from "./header-reference-attributes";
export interface IHeaderReferenceOptions {
readonly headerType?: HeaderReferenceType;
readonly headerId?: number;
}
export class HeaderReference extends XmlComponent {
constructor(options: IHeaderReferenceOptions) {
super("w:headerReference");
this.root.push(
new HeaderReferenceAttributes({
type: options.headerType || HeaderReferenceType.DEFAULT,
id: `rId${options.headerId}`,
}),
);
}
}

View File

@ -1,2 +0,0 @@
export * from "./header-reference";
export * from "./header-reference-attributes";

View File

@ -1,9 +1,2 @@
export * from "./section-properties";
export * from "./footer-reference";
export * from "./header-reference";
export * from "./page-size";
export * from "./page-number";
export * from "./page-border";
export * from "./line-number";
export * from "./vertical-align";
export * from "./type";
export * from "./properties";

View File

@ -1 +0,0 @@
export * from "./line-number";

View File

@ -1,38 +0,0 @@
// http://officeopenxml.com/WPsectionLineNumbering.php
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
export enum LineNumberRestartFormat {
CONTINUOUS = "continuous",
NEW_SECTION = "newSection",
NEW_PAGE = "newPage",
}
export interface ILineNumberAttributes {
readonly countBy?: number;
readonly start?: number;
readonly restart?: LineNumberRestartFormat;
readonly distance?: number;
}
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?: number, start?: number, restart?: LineNumberRestartFormat, dist?: number) {
super("w:lnNumType");
this.root.push(
new LineNumberAttributes({
countBy: countBy,
start: start,
restart: restart,
distance: dist,
}),
);
}
}

View File

@ -1 +0,0 @@
export * from "./page-borders";

View File

@ -1,100 +0,0 @@
// http://officeopenxml.com/WPsectionBorders.php
import { BorderStyle } from "file/styles";
import { IgnoreIfEmptyXmlComponent, XmlAttributeComponent, XmlComponent } from "file/xml-components";
export enum PageBorderDisplay {
ALL_PAGES = "allPages",
FIRST_PAGE = "firstPage",
NOT_FIRST_PAGE = "notFirstPage",
}
export enum PageBorderOffsetFrom {
PAGE = "page",
TEXT = "text",
}
export enum PageBorderZOrder {
BACK = "back",
FRONT = "front",
}
export interface IPageBorderAttributes {
readonly display?: PageBorderDisplay;
readonly offsetFrom?: PageBorderOffsetFrom;
readonly zOrder?: PageBorderZOrder;
}
export interface IPageBorderConfiguration {
readonly style?: BorderStyle;
readonly size?: number;
readonly color?: string;
readonly space?: number;
}
export interface IPageBordersOptions {
readonly pageBorders?: IPageBorderAttributes;
readonly pageBorderTop?: IPageBorderConfiguration;
readonly pageBorderRight?: IPageBorderConfiguration;
readonly pageBorderBottom?: IPageBorderConfiguration;
readonly pageBorderLeft?: IPageBorderConfiguration;
}
class PageBordeAttributes extends XmlAttributeComponent<IPageBorderConfiguration> {
protected readonly xmlKeys = {
style: "w:val",
size: "w:size",
color: "w:color",
space: "w:space",
};
}
class PageBorder extends XmlComponent {
constructor(key: string, options: IPageBorderConfiguration) {
super(key);
this.root.push(new PageBordeAttributes(options));
}
}
class PageBordersAttributes extends XmlAttributeComponent<IPageBorderAttributes> {
protected readonly xmlKeys = {
display: "w:display",
offsetFrom: "w:offsetFrom",
zOrder: "w:zOrder",
};
}
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 PageBorder("w:top", options.pageBorderTop));
}
if (options.pageBorderRight) {
this.root.push(new PageBorder("w:right", options.pageBorderRight));
}
if (options.pageBorderBottom) {
this.root.push(new PageBorder("w:bottom", options.pageBorderBottom));
}
if (options.pageBorderLeft) {
this.root.push(new PageBorder("w:left", options.pageBorderLeft));
}
}
}

View File

@ -1,23 +0,0 @@
import { XmlAttributeComponent } from "file/xml-components";
export interface IPageMarginAttributes {
readonly top?: number;
readonly right?: number;
readonly bottom?: number;
readonly left?: number;
readonly header?: number;
readonly footer?: number;
readonly gutter?: number;
}
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",
};
}

View File

@ -1,19 +0,0 @@
import { XmlComponent } from "file/xml-components";
import { PageMarginAttributes } from "./page-margin-attributes";
export class PageMargin extends XmlComponent {
constructor(top: number, right: number, bottom: number, left: number, header: number, footer: number, gutter: number) {
super("w:pgMar");
this.root.push(
new PageMarginAttributes({
top: top,
right: right,
bottom: bottom,
left: left,
header: header,
footer: footer,
gutter: gutter,
}),
);
}
}

View File

@ -1 +0,0 @@
export * from "./page-number";

View File

@ -1,53 +0,0 @@
// http://officeopenxml.com/WPSectionPgNumType.php
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
export enum PageNumberFormat {
CARDINAL_TEXT = "cardinalText",
DECIMAL = "decimal",
DECIMAL_ENCLOSED_CIRCLE = "decimalEnclosedCircle",
DECIMAL_ENCLOSED_FULL_STOP = "decimalEnclosedFullstop",
DECIMAL_ENCLOSED_PAREN = "decimalEnclosedParen",
DECIMAL_ZERO = "decimalZero",
LOWER_LETTER = "lowerLetter",
LOWER_ROMAN = "lowerRoman",
NONE = "none",
ORDINAL_TEXT = "ordinalText",
UPPER_LETTER = "upperLetter",
UPPER_ROMAN = "upperRoman",
DECIMAL_FULL_WIDTH = "decimalFullWidth",
}
export enum PageNumberSeparator {
COLON = "colon",
EM_DASH = "emDash",
EN_DASH = "endash",
HYPHEN = "hyphen",
PERIOD = "period",
}
export interface IPageNumberTypeAttributes {
readonly start?: number;
readonly formatType?: PageNumberFormat;
readonly separator?: PageNumberSeparator;
}
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?: number, numberFormat?: PageNumberFormat, separator?: PageNumberSeparator) {
super("w:pgNumType");
this.root.push(
new PageNumberTypeAttributes({
start: start,
formatType: numberFormat,
separator: separator,
}),
);
}
}

View File

@ -1,2 +0,0 @@
export * from "./page-size";
export * from "./page-size-attributes";

View File

@ -1,20 +0,0 @@
import { XmlAttributeComponent } from "file/xml-components";
export enum PageOrientation {
PORTRAIT = "portrait",
LANDSCAPE = "landscape",
}
export interface IPageSizeAttributes {
readonly width?: number;
readonly height?: number;
readonly orientation?: PageOrientation;
}
export class PageSizeAttributes extends XmlAttributeComponent<IPageSizeAttributes> {
protected readonly xmlKeys = {
width: "w:w",
height: "w:h",
orientation: "w:orient",
};
}

View File

@ -1,18 +0,0 @@
import { XmlComponent } from "file/xml-components";
import { PageOrientation, PageSizeAttributes } from "./page-size-attributes";
export class PageSize extends XmlComponent {
constructor(width: number, height: number, orientation: PageOrientation) {
super("w:pgSz");
const flip = orientation === PageOrientation.LANDSCAPE;
this.root.push(
new PageSizeAttributes({
width: flip ? height : width,
height: flip ? width : height,
orientation: orientation,
}),
);
}
}

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

@ -1,7 +1,7 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { BorderStyle } from "file/styles";
import { BorderStyle } from "file/border";
import { PageBorderDisplay, PageBorders, PageBorderZOrder } from "./page-borders";
@ -70,22 +70,22 @@ describe("PageBorders", () => {
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:size": 10, "w:val": "doubleWave" },
_attr: { "w:color": "001122", "w:sz": 10, "w:val": "doubleWave" },
},
});
expect(tree["w:pgBorders"][2]).to.deep.equal({
"w:right": {
_attr: { "w:color": "223344", "w:size": 20, "w:val": "double" },
"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:size": 30, "w:val": "single" },
_attr: { "w:color": "556677", "w:sz": 30, "w:val": "single" },
},
});
expect(tree["w:pgBorders"][4]).to.deep.equal({
"w:left": {
_attr: { "w:color": "889900", "w:size": 40, "w:val": "dotted" },
"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

@ -2,8 +2,7 @@ import { expect } from "chai";
import { Formatter } from "export/formatter";
import { PageSize } from "./page-size";
import { PageOrientation } from "./page-size-attributes";
import { PageOrientation, PageSize } from "./page-size";
describe("PageSize", () => {
describe("#constructor()", () => {

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

@ -1,8 +1,7 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { Type } from "./section-type";
import { SectionType } from "./section-type-attributes";
import { SectionType, Type } from "./section-type";
describe("Type", () => {
it("should create with even page section type", () => {

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 }));
}
}

View File

@ -5,13 +5,30 @@ import { Formatter } from "export/formatter";
import { FooterWrapper } from "file/footer-wrapper";
import { HeaderWrapper } from "file/header-wrapper";
import { Media } from "file/media";
import { LineNumberRestartFormat } from "./line-number";
import { NumberFormat } from "file/shared/number-format";
import { VerticalAlign } from "file/vertical-align";
import { PageBorderOffsetFrom } from "./page-border";
import { PageNumberFormat } from "./page-number";
import { SectionProperties } from "./section-properties";
import { SectionType } from "./type/section-type-attributes";
import { SectionVerticalAlignValue } from "./vertical-align";
import { PageOrientation } from "./properties";
import { LineNumberRestartFormat } from "./properties/line-number";
import { PageBorderOffsetFrom } from "./properties/page-borders";
import { SectionType } from "./properties/section-type";
import { sectionMarginDefaults, sectionPageSizeDefaults, SectionProperties } from "./section-properties";
const DEFAULT_MARGINS = {
"w:bottom": sectionMarginDefaults.BOTTOM,
"w:footer": sectionMarginDefaults.FOOTER,
"w:top": sectionMarginDefaults.TOP,
"w:right": sectionMarginDefaults.RIGHT,
"w:left": sectionMarginDefaults.LEFT,
"w:header": sectionMarginDefaults.HEADER,
"w:gutter": sectionMarginDefaults.GUTTER,
};
const PAGE_SIZE_DEFAULTS = {
"w:h": sectionPageSizeDefaults.HEIGHT,
"w:orient": sectionPageSizeDefaults.ORIENTATION,
"w:w": sectionPageSizeDefaults.WIDTH,
};
describe("SectionProperties", () => {
describe("#constructor()", () => {
@ -21,26 +38,27 @@ describe("SectionProperties", () => {
const properties = new SectionProperties({
page: {
size: {
width: 11906,
height: 16838,
width: 1190,
height: 1680,
orientation: PageOrientation.PORTRAIT,
},
margin: {
top: convertInchesToTwip(1),
right: convertInchesToTwip(1),
bottom: convertInchesToTwip(1),
left: convertInchesToTwip(1),
header: 708,
footer: 708,
gutter: 0,
top: "2in",
right: "2in",
bottom: "2in",
left: "2in",
header: 808,
footer: 808,
gutter: 10,
},
pageNumbers: {
start: 10,
formatType: PageNumberFormat.CARDINAL_TEXT,
formatType: NumberFormat.CARDINAL_TEXT,
},
},
column: {
space: 708,
count: 1,
space: 208,
count: 2,
separate: true,
},
grid: {
@ -53,7 +71,7 @@ describe("SectionProperties", () => {
even: new FooterWrapper(media, 200),
},
titlePage: true,
verticalAlign: SectionVerticalAlignValue.TOP,
verticalAlign: VerticalAlign.TOP,
});
const tree = new Formatter().format(properties);
@ -62,25 +80,25 @@ describe("SectionProperties", () => {
expect(tree["w:sectPr"]).to.be.an.instanceof(Array);
expect(tree["w:sectPr"][0]).to.deep.equal({ "w:headerReference": { _attr: { "r:id": "rId100", "w:type": "default" } } });
expect(tree["w:sectPr"][1]).to.deep.equal({ "w:footerReference": { _attr: { "r:id": "rId200", "w:type": "even" } } });
expect(tree["w:sectPr"][2]).to.deep.equal({ "w:pgSz": { _attr: { "w:h": 16838, "w:w": 11906, "w:orient": "portrait" } } });
expect(tree["w:sectPr"][2]).to.deep.equal({ "w:pgSz": { _attr: { "w:h": 1680, "w:w": 1190, "w:orient": "portrait" } } });
expect(tree["w:sectPr"][3]).to.deep.equal({
"w:pgMar": {
_attr: {
"w:bottom": 1440,
"w:footer": 708,
"w:top": 1440,
"w:right": 1440,
"w:left": 1440,
"w:header": 708,
"w:gutter": 0,
"w:bottom": "2in",
"w:footer": 808,
"w:top": "2in",
"w:right": "2in",
"w:left": "2in",
"w:header": 808,
"w:gutter": 10,
},
},
});
expect(tree["w:sectPr"][4]).to.deep.equal({ "w:pgNumType": { _attr: { "w:fmt": "cardinalText", "w:start": 10 } } });
expect(tree["w:sectPr"][5]).to.deep.equal({ "w:cols": { _attr: { "w:space": 708, "w:sep": true, "w:num": 1 } } });
expect(tree["w:sectPr"][5]).to.deep.equal({ "w:cols": { _attr: { "w:space": 208, "w:sep": true, "w:num": 2 } } });
expect(tree["w:sectPr"][6]).to.deep.equal({ "w:vAlign": { _attr: { "w:val": "top" } } });
expect(tree["w:sectPr"][7]).to.deep.equal({ "w:titlePg": { _attr: { "w:val": "1" } } });
expect(tree["w:sectPr"][7]).to.deep.equal({ "w:titlePg": {} });
expect(tree["w:sectPr"][8]).to.deep.equal({ "w:docGrid": { _attr: { "w:linePitch": 360 } } });
});
@ -89,22 +107,12 @@ describe("SectionProperties", () => {
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" } } });
expect(tree["w:sectPr"][0]).to.deep.equal({ "w:pgSz": { _attr: PAGE_SIZE_DEFAULTS } });
expect(tree["w:sectPr"][1]).to.deep.equal({
"w:pgMar": {
_attr: {
"w:bottom": 1440,
"w:footer": 708,
"w:top": 1440,
"w:right": 1440,
"w:left": 1440,
"w:header": 708,
"w:gutter": 0,
},
},
"w:pgMar": { _attr: DEFAULT_MARGINS },
});
expect(tree["w:sectPr"][3]).to.deep.equal({ "w:cols": { _attr: { "w:space": 708, "w:sep": false, "w:num": 1 } } });
expect(tree["w:sectPr"][4]).to.deep.equal({ "w:docGrid": { _attr: { "w:linePitch": 360 } } });
// expect(tree["w:sectPr"][3]).to.deep.equal({ "w:cols": { _attr: { "w:space": 708, "w:sep": false, "w:num": 1 } } });
expect(tree["w:sectPr"][3]).to.deep.equal({ "w:docGrid": { _attr: { "w:linePitch": 360 } } });
});
it("should create section properties with changed options", () => {
@ -118,17 +126,12 @@ describe("SectionProperties", () => {
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" } } });
expect(tree["w:sectPr"][0]).to.deep.equal({ "w:pgSz": { _attr: PAGE_SIZE_DEFAULTS } });
expect(tree["w:sectPr"][1]).to.deep.equal({
"w:pgMar": {
_attr: {
"w:bottom": 1440,
"w:footer": 708,
...DEFAULT_MARGINS,
"w:top": 0,
"w:right": 1440,
"w:left": 1440,
"w:header": 708,
"w:gutter": 0,
},
},
});
@ -145,17 +148,12 @@ describe("SectionProperties", () => {
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" } } });
expect(tree["w:sectPr"][0]).to.deep.equal({ "w:pgSz": { _attr: PAGE_SIZE_DEFAULTS } });
expect(tree["w:sectPr"][1]).to.deep.equal({
"w:pgMar": {
_attr: {
...DEFAULT_MARGINS,
"w:bottom": 0,
"w:footer": 708,
"w:top": 1440,
"w:right": 1440,
"w:left": 1440,
"w:header": 708,
"w:gutter": 0,
},
},
});
@ -167,24 +165,25 @@ describe("SectionProperties", () => {
size: {
width: 0,
height: 0,
orientation: PageOrientation.LANDSCAPE,
},
},
});
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": 0, "w:w": 0, "w:orient": "portrait" } } });
expect(tree["w:sectPr"][0]).to.deep.equal({
"w:pgSz": {
_attr: {
"w:h": 0,
"w:orient": PageOrientation.LANDSCAPE,
"w:w": 0,
},
},
});
expect(tree["w:sectPr"][1]).to.deep.equal({
"w:pgMar": {
_attr: {
"w:bottom": 1440,
"w:footer": 708,
"w:top": 1440,
"w:right": 1440,
"w:left": 1440,
"w:header": 708,
"w:gutter": 0,
},
_attr: DEFAULT_MARGINS,
},
});
});
@ -211,7 +210,7 @@ describe("SectionProperties", () => {
const properties = new SectionProperties({
page: {
pageNumbers: {
formatType: PageNumberFormat.UPPER_ROMAN,
formatType: NumberFormat.UPPER_ROMAN,
},
},
});

View File

@ -1,29 +1,21 @@
// 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";
import { XmlComponent } from "file/xml-components";
import { VerticalAlign, VerticalAlignElement } from "file/vertical-align";
import { OnOffElement, XmlComponent } from "file/xml-components";
import { Columns } from "./columns/columns";
import { DocumentGrid } from "./doc-grid/doc-grid";
import { IDocGridAttributesProperties } from "./doc-grid/doc-grid-attributes";
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 { ILineNumberAttributes, LineNumberType } from "./line-number";
import { IPageBordersOptions, PageBorders } from "./page-border";
import { PageMargin } from "./page-margin/page-margin";
import { IPageMarginAttributes } from "./page-margin/page-margin-attributes";
import { IPageNumberTypeAttributes, PageNumberType } from "./page-number";
import { PageSize } from "./page-size/page-size";
import { IPageSizeAttributes, PageOrientation } from "./page-size/page-size-attributes";
import { TitlePage } from "./title-page/title-page";
import { Type } from "./type/section-type";
import { SectionType } from "./type/section-type-attributes";
import { SectionVerticalAlign, SectionVerticalAlignValue } from "./vertical-align";
import { HeaderFooterReference, HeaderFooterReferenceType, HeaderFooterType } from "./properties/header-footer-reference";
import { Columns, IColumnsAttributes } from "./properties/columns";
import { DocumentGrid, IDocGridAttributesProperties } from "./properties/doc-grid";
import { ILineNumberAttributes, LineNumberType } from "./properties/line-number";
import { IPageBordersOptions, PageBorders } from "./properties/page-borders";
import { IPageMarginAttributes, PageMargin } from "./properties/page-margin";
import { IPageNumberTypeAttributes, PageNumberType } from "./properties/page-number";
import { IPageSizeAttributes, PageOrientation, PageSize } from "./properties/page-size";
import { SectionType, Type } from "./properties/section-type";
export interface IHeaderFooterGroup<T> {
readonly default?: T;
@ -43,53 +35,93 @@ export interface ISectionPropertiesOptions {
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 verticalAlign?: VerticalAlign;
readonly column?: IColumnsAttributes;
readonly type?: SectionType;
}
// <xsd:complexType name="CT_SectPr">
// <xsd:sequence>
// <xsd:group ref="EG_HdrFtrReferences" minOccurs="0" maxOccurs="6"/>
// <xsd:group ref="EG_SectPrContents" minOccurs="0"/>
// <xsd:element name="sectPrChange" type="CT_SectPrChange" minOccurs="0"/>
// </xsd:sequence>
// <xsd:attributeGroup ref="AG_SectPrAttributes"/>
// </xsd:complexType>
// <xsd:group name="EG_SectPrContents">
// <xsd:sequence>
// <xsd:element name="footnotePr" type="CT_FtnProps" minOccurs="0"/>
// <xsd:element name="endnotePr" type="CT_EdnProps" minOccurs="0"/>
// <xsd:element name="type" type="CT_SectType" minOccurs="0"/>
// <xsd:element name="pgSz" type="CT_PageSz" minOccurs="0"/>
// <xsd:element name="pgMar" type="CT_PageMar" minOccurs="0"/>
// <xsd:element name="paperSrc" type="CT_PaperSource" minOccurs="0"/>
// <xsd:element name="pgBorders" type="CT_PageBorders" minOccurs="0"/>
// <xsd:element name="lnNumType" type="CT_LineNumber" minOccurs="0"/>
// <xsd:element name="pgNumType" type="CT_PageNumber" minOccurs="0"/>
// <xsd:element name="cols" type="CT_Columns" minOccurs="0"/>
// <xsd:element name="formProt" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="vAlign" type="CT_VerticalJc" minOccurs="0"/>
// <xsd:element name="noEndnote" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="titlePg" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="textDirection" type="CT_TextDirection" minOccurs="0"/>
// <xsd:element name="bidi" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="rtlGutter" type="CT_OnOff" minOccurs="0"/>
// <xsd:element name="docGrid" type="CT_DocGrid" minOccurs="0"/>
// <xsd:element name="printerSettings" type="CT_Rel" minOccurs="0"/>
// </xsd:sequence>
// </xsd:group>
export const sectionMarginDefaults = {
TOP: "1in",
RIGHT: "1in",
BOTTOM: "1in",
LEFT: "1in",
HEADER: 708,
FOOTER: 708,
GUTTER: 0,
};
export const sectionPageSizeDefaults = {
WIDTH: 11906,
HEIGHT: 16838,
ORIENTATION: PageOrientation.PORTRAIT,
};
export class SectionProperties extends XmlComponent {
constructor({
page: {
size: { width = 11906, height = 16838, orientation = PageOrientation.PORTRAIT } = {},
size: {
width = sectionPageSizeDefaults.WIDTH,
height = sectionPageSizeDefaults.HEIGHT,
orientation = sectionPageSizeDefaults.ORIENTATION,
} = {},
margin: {
top = convertInchesToTwip(1),
right = convertInchesToTwip(1),
bottom = convertInchesToTwip(1),
left = convertInchesToTwip(1),
header = 708,
footer = 708,
gutter = 0,
} = {},
pageNumbers: {
start: pageNumberStart = undefined,
formatType: pageNumberFormatType = undefined,
separator: pageNumberSeparator = undefined,
} = {},
borders: {
pageBorders = undefined,
pageBorderTop = undefined,
pageBorderRight = undefined,
pageBorderBottom = undefined,
pageBorderLeft = undefined,
top = sectionMarginDefaults.TOP,
right = sectionMarginDefaults.RIGHT,
bottom = sectionMarginDefaults.BOTTOM,
left = sectionMarginDefaults.LEFT,
header = sectionMarginDefaults.HEADER,
footer = sectionMarginDefaults.FOOTER,
gutter = sectionMarginDefaults.GUTTER,
} = {},
pageNumbers = {},
borders,
} = {},
grid: { linePitch = 360 } = {},
headerWrapperGroup = {},
footerWrapperGroup = {},
lineNumbers: { countBy: lineNumberCountBy, start: lineNumberStart, restart: lineNumberRestart, distance: lineNumberDistance } = {},
titlePage = false,
lineNumbers,
titlePage,
verticalAlign,
column: { space = 708, count = 1, separate = false } = {},
column,
type,
}: ISectionPropertiesOptions = {}) {
super("w:sectPr");
this.addHeaders(headerWrapperGroup);
this.addFooters(footerWrapperGroup);
this.addHeaderFooterGroup(HeaderFooterType.HEADER, headerWrapperGroup);
this.addHeaderFooterGroup(HeaderFooterType.FOOTER, footerWrapperGroup);
if (type) {
this.root.push(new Type(type));
@ -98,90 +130,58 @@ export class SectionProperties extends XmlComponent {
this.root.push(new PageSize(width, height, orientation));
this.root.push(new PageMargin(top, right, bottom, left, header, footer, gutter));
if (pageBorders || pageBorderTop || pageBorderRight || pageBorderBottom || pageBorderLeft) {
this.root.push(
new PageBorders({
pageBorders: pageBorders,
pageBorderTop: pageBorderTop,
pageBorderRight: pageBorderRight,
pageBorderBottom: pageBorderBottom,
pageBorderLeft: pageBorderLeft,
}),
);
if (borders) {
this.root.push(new PageBorders(borders));
}
if (lineNumberCountBy || lineNumberStart || lineNumberRestart || lineNumberDistance) {
this.root.push(new LineNumberType(lineNumberCountBy, lineNumberStart, lineNumberRestart, lineNumberDistance));
if (lineNumbers) {
this.root.push(new LineNumberType(lineNumbers));
}
this.root.push(new PageNumberType(pageNumberStart, pageNumberFormatType, pageNumberSeparator));
this.root.push(new PageNumberType(pageNumbers));
this.root.push(new Columns(space, count, separate));
if (column) {
this.root.push(new Columns(column));
}
if (verticalAlign) {
this.root.push(new SectionVerticalAlign(verticalAlign));
this.root.push(new VerticalAlignElement(verticalAlign));
}
if (titlePage) {
this.root.push(new TitlePage());
if (titlePage !== undefined) {
this.root.push(new OnOffElement("w:titlePg", titlePage));
}
this.root.push(new DocumentGrid(linePitch));
}
private addHeaders(headers: IHeaderFooterGroup<HeaderWrapper>): void {
if (headers.default) {
private addHeaderFooterGroup(
type: HeaderFooterType,
group: IHeaderFooterGroup<HeaderWrapper> | IHeaderFooterGroup<FooterWrapper>,
): void {
if (group.default) {
this.root.push(
new HeaderReference({
headerType: HeaderReferenceType.DEFAULT,
headerId: headers.default.View.ReferenceId,
new HeaderFooterReference(type, {
type: HeaderFooterReferenceType.DEFAULT,
id: group.default.View.ReferenceId,
}),
);
}
if (headers.first) {
if (group.first) {
this.root.push(
new HeaderReference({
headerType: HeaderReferenceType.FIRST,
headerId: headers.first.View.ReferenceId,
new HeaderFooterReference(type, {
type: HeaderFooterReferenceType.FIRST,
id: group.first.View.ReferenceId,
}),
);
}
if (headers.even) {
if (group.even) {
this.root.push(
new HeaderReference({
headerType: HeaderReferenceType.EVEN,
headerId: headers.even.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.even) {
this.root.push(
new FooterReference({
footerType: FooterReferenceType.EVEN,
footerId: footers.even.View.ReferenceId,
new HeaderFooterReference(type, {
type: HeaderFooterReferenceType.EVEN,
id: group.even.View.ReferenceId,
}),
);
}

View File

@ -1,9 +0,0 @@
import { XmlAttributeComponent } from "file/xml-components";
export class TitlePageAttributes extends XmlAttributeComponent<{
readonly value: string;
}> {
protected readonly xmlKeys = {
value: "w:val",
};
}

View File

@ -1,17 +0,0 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { TitlePage } from "./title-page";
describe("PageSize", () => {
describe("#constructor()", () => {
it("should create title page property for different first page header", () => {
const properties = new TitlePage();
const tree = new Formatter().format(properties);
expect(Object.keys(tree)).to.deep.equal(["w:titlePg"]);
expect(tree["w:titlePg"]).to.deep.equal({ _attr: { "w:val": "1" } });
});
});
});

View File

@ -1,13 +0,0 @@
import { XmlComponent } from "file/xml-components";
import { TitlePageAttributes } from "./title-page-attributes";
export class TitlePage extends XmlComponent {
constructor() {
super("w:titlePg");
this.root.push(
new TitlePageAttributes({
value: "1",
}),
);
}
}

View File

@ -1,2 +0,0 @@
export * from "./section-type";
export * from "./section-type-attributes";

View File

@ -1,17 +0,0 @@
import { XmlAttributeComponent } from "file/xml-components";
export enum SectionType {
CONTINUOUS = "continuous",
EVEN_PAGE = "evenPage",
NEXT_COLUMN = "nextColumn",
NEXT_PAGE = "nextPage",
ODD_PAGE = "oddPage",
}
export class SectionTypeAttributes extends XmlAttributeComponent<{
readonly val: SectionType;
}> {
protected readonly xmlKeys = {
val: "w:val",
};
}

View File

@ -1,10 +0,0 @@
// http://officeopenxml.com/WPsection.php
import { XmlComponent } from "file/xml-components";
import { SectionType, SectionTypeAttributes } from "./section-type-attributes";
export class Type extends XmlComponent {
constructor(value: SectionType) {
super("w:type");
this.root.push(new SectionTypeAttributes({ val: value }));
}
}

View File

@ -1,2 +0,0 @@
export * from "./vertical-align";
export * from "./vertical-align-attributes";

View File

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

View File

@ -1,18 +0,0 @@
// http://officeopenxml.com/WPsection.php
import { XmlComponent } from "file/xml-components";
import { SectionVerticalAlignAttributes } from "./vertical-align-attributes";
export enum SectionVerticalAlignValue {
BOTH = "both",
BOTTOM = "bottom",
CENTER = "center",
TOP = "top",
}
export class SectionVerticalAlign extends XmlComponent {
constructor(value: SectionVerticalAlignValue) {
super("w:vAlign");
this.root.push(new SectionVerticalAlignAttributes({ verticalAlign: value }));
}
}

View File

@ -34,8 +34,8 @@ describe("DocumentBackground", () => {
const documentBackground = new DocumentBackground({
color: "ffff00",
themeColor: "test",
themeShade: "test",
themeTint: "test",
themeShade: "0A",
themeTint: "0B",
});
const tree = new Formatter().format(documentBackground);
expect(tree).to.deep.equal({
@ -43,8 +43,8 @@ describe("DocumentBackground", () => {
_attr: {
"w:color": "ffff00",
"w:themeColor": "test",
"w:themeShade": "test",
"w:themeTint": "test",
"w:themeShade": "0A",
"w:themeTint": "0B",
},
},
});

View File

@ -1,7 +1,30 @@
// http://officeopenxml.com/WPdocument.php
// http://www.datypic.com/sc/ooxml/e-w_background-1.html
import { hexColorValue, uCharHexNumber } from "file/values";
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
// <xsd:simpleType name="ST_ThemeColor">
// <xsd:restriction base="xsd:string">
// <xsd:enumeration value="dark1"/>
// <xsd:enumeration value="light1"/>
// <xsd:enumeration value="dark2"/>
// <xsd:enumeration value="light2"/>
// <xsd:enumeration value="accent1"/>
// <xsd:enumeration value="accent2"/>
// <xsd:enumeration value="accent3"/>
// <xsd:enumeration value="accent4"/>
// <xsd:enumeration value="accent5"/>
// <xsd:enumeration value="accent6"/>
// <xsd:enumeration value="hyperlink"/>
// <xsd:enumeration value="followedHyperlink"/>
// <xsd:enumeration value="none"/>
// <xsd:enumeration value="background1"/>
// <xsd:enumeration value="text1"/>
// <xsd:enumeration value="background2"/>
// <xsd:enumeration value="text2"/>
// </xsd:restriction>
// </xsd:simpleType>
export class DocumentBackgroundAttributes extends XmlAttributeComponent<{
readonly color: string;
readonly themeColor?: string;
@ -23,16 +46,32 @@ export interface IDocumentBackgroundOptions {
readonly themeTint?: string;
}
// <xsd:complexType name="CT_Background">
// <xsd:sequence>
// <xsd:sequence maxOccurs="unbounded">
// <xsd:any processContents="lax" namespace="urn:schemas-microsoft-com:vml" minOccurs="0"
// maxOccurs="unbounded"/>
// <xsd:any processContents="lax" namespace="urn:schemas-microsoft-com:office:office"
// minOccurs="0" maxOccurs="unbounded"/>
// </xsd:sequence>
// <xsd:element name="drawing" type="CT_Drawing" minOccurs="0"/>
// </xsd:sequence>
// <xsd:attribute name="color" type="ST_HexColor" use="optional" default="auto"/>
// <xsd:attribute name="themeColor" type="ST_ThemeColor" use="optional"/>
// <xsd:attribute name="themeTint" type="ST_UcharHexNumber" use="optional"/>
// <xsd:attribute name="themeShade" type="ST_UcharHexNumber" use="optional"/>
// </xsd:complexType>
export class DocumentBackground extends XmlComponent {
constructor(options: IDocumentBackgroundOptions) {
super("w:background");
this.root.push(
new DocumentBackgroundAttributes({
color: options.color ? options.color : "FFFFFF",
color: hexColorValue(options.color ? options.color : "FFFFFF"),
themeColor: options.themeColor,
themeShade: options.themeShade,
themeTint: options.themeTint,
themeShade: options.themeShade === undefined ? undefined : uCharHexNumber(options.themeShade),
themeTint: options.themeTint === undefined ? undefined : uCharHexNumber(options.themeTint),
}),
);
}

View File

@ -11,6 +11,25 @@ export interface IDocumentOptions {
readonly background: IDocumentBackgroundOptions;
}
// <xsd:element name="document" type="CT_Document"/>
//
// <xsd:complexType name="CT_Document">
// <xsd:complexContent>
// <xsd:extension base="CT_DocumentBase">
// <xsd:sequence>
// <xsd:element name="body" type="CT_Body" minOccurs="0" maxOccurs="1"/>
// </xsd:sequence>
// <xsd:attribute name="conformance" type="s:ST_ConformanceClass"/>
// <xsd:attribute ref="mc:Ignorable" use="optional" />
// </xsd:extension>
// </xsd:complexContent>
// </xsd:complexType>
//
// <xsd:complexType name="CT_DocumentBase">
// <xsd:sequence>
// <xsd:element name="background" type="CT_Background" minOccurs="0"/>
// </xsd:sequence>
// </xsd:complexType>
export class Document extends XmlComponent {
private readonly body: Body;
@ -50,8 +69,4 @@ export class Document extends XmlComponent {
public get Body(): Body {
return this.body;
}
public getTablesOfContents(): TableOfContents[] {
return this.body.getTablesOfContents();
}
}

View File

@ -11,6 +11,31 @@ import { Extent } from "./../extent/extent";
import { GraphicFrameProperties } from "./../graphic-frame/graphic-frame-properties";
import { AnchorAttributes } from "./anchor-attributes";
// <xsd:complexType name="CT_Anchor">
// <xsd:sequence>
// <xsd:element name="simplePos" type="a:CT_Point2D"/>
// <xsd:element name="positionH" type="CT_PosH"/>
// <xsd:element name="positionV" type="CT_PosV"/>
// <xsd:element name="extent" type="a:CT_PositiveSize2D"/>
// <xsd:element name="effectExtent" type="CT_EffectExtent" minOccurs="0"/>
// <xsd:group ref="EG_WrapType"/>
// <xsd:element name="docPr" type="a:CT_NonVisualDrawingProps" minOccurs="1" maxOccurs="1"/>
// <xsd:element name="cNvGraphicFramePr" type="a:CT_NonVisualGraphicFrameProperties"
// minOccurs="0" maxOccurs="1"/>
// <xsd:element ref="a:graphic" minOccurs="1" maxOccurs="1"/>
// </xsd:sequence>
// <xsd:attribute name="distT" type="ST_WrapDistance" use="optional"/>
// <xsd:attribute name="distB" type="ST_WrapDistance" use="optional"/>
// <xsd:attribute name="distL" type="ST_WrapDistance" use="optional"/>
// <xsd:attribute name="distR" type="ST_WrapDistance" use="optional"/>
// <xsd:attribute name="simplePos" type="xsd:boolean"/>
// <xsd:attribute name="relativeHeight" type="xsd:unsignedInt" use="required"/>
// <xsd:attribute name="behindDoc" type="xsd:boolean" use="required"/>
// <xsd:attribute name="locked" type="xsd:boolean" use="required"/>
// <xsd:attribute name="layoutInCell" type="xsd:boolean" use="required"/>
// <xsd:attribute name="hidden" type="xsd:boolean" use="optional"/>
// <xsd:attribute name="allowOverlap" type="xsd:boolean" use="required"/>
// </xsd:complexType>
export class Anchor extends XmlComponent {
constructor(mediaData: IMediaData, transform: IMediaDataTransformation, drawingOptions: IDrawingOptions) {
super("wp:anchor");

View File

@ -15,6 +15,13 @@ export interface IDrawingOptions {
readonly floating?: IFloating;
}
// <xsd:complexType name="CT_Drawing">
// <xsd:choice minOccurs="1" maxOccurs="unbounded">
// <xsd:element ref="wp:anchor" minOccurs="0"/>
// <xsd:element ref="wp:inline" minOccurs="0"/>
// </xsd:choice>
// </xsd:complexType>
export class Drawing extends XmlComponent {
private readonly inline: Inline;
@ -28,8 +35,4 @@ export class Drawing extends XmlComponent {
this.root.push(new Anchor(imageData, imageData.transformation, drawingOptions));
}
}
public scale(factorX: number, factorY: number): void {
this.inline.scale(factorX, factorY);
}
}

View File

@ -15,11 +15,4 @@ export class Extent extends XmlComponent {
this.root.push(this.attributes);
}
public setXY(x: number, y: number): void {
this.attributes.set({
cx: x,
cy: y,
});
}
}

View File

@ -49,5 +49,9 @@ describe("HorizontalPosition", () => {
],
});
});
it("should require one of align or offset", () => {
expect(() => new HorizontalPosition({})).to.throw();
});
});
});

View File

@ -49,5 +49,9 @@ describe("VerticalPosition", () => {
],
});
});
it("should require one of align or offset", () => {
expect(() => new VerticalPosition({})).to.throw();
});
});
});

View File

@ -20,8 +20,4 @@ export class GraphicData extends XmlComponent {
this.root.push(this.pic);
}
public setXY(x: number, y: number): void {
this.pic.setXY(x, y);
}
}

View File

@ -8,8 +8,6 @@ import { PicAttributes } from "./pic-attributes";
import { ShapeProperties } from "./shape-properties/shape-properties";
export class Pic extends XmlComponent {
private readonly shapeProperties: ShapeProperties;
constructor(mediaData: IMediaData, transform: IMediaDataTransformation) {
super("pic:pic");
@ -19,14 +17,8 @@ export class Pic extends XmlComponent {
}),
);
this.shapeProperties = new ShapeProperties(transform);
this.root.push(new NonVisualPicProperties());
this.root.push(new BlipFill(mediaData));
this.root.push(new ShapeProperties(transform));
}
public setXY(x: number, y: number): void {
this.shapeProperties.setXY(x, y);
}
}

View File

@ -15,11 +15,4 @@ export class Extents extends XmlComponent {
this.root.push(this.attributes);
}
public setXY(x: number, y: number): void {
this.attributes.set({
cx: x,
cy: y,
});
}
}

View File

@ -37,8 +37,4 @@ export class Form extends XmlComponent {
this.root.push(new Offset());
this.root.push(this.extents);
}
public setXY(x: number, y: number): void {
this.extents.setXY(x, y);
}
}

View File

@ -26,8 +26,4 @@ export class ShapeProperties extends XmlComponent {
// this.root.push(new NoFill());
// this.root.push(new Outline());
}
public setXY(x: number, y: number): void {
this.form.setXY(x, y);
}
}

View File

@ -26,8 +26,4 @@ export class Graphic extends XmlComponent {
this.root.push(this.data);
}
public setXY(x: number, y: number): void {
this.data.setXY(x, y);
}
}

View File

@ -8,11 +8,25 @@ import { GraphicFrameProperties } from "./../graphic-frame/graphic-frame-propert
import { Graphic } from "./../inline/graphic";
import { InlineAttributes } from "./inline-attributes";
// <xsd:complexType name="CT_Inline">
// <xsd:sequence>
// <xsd:element name="extent" type="a:CT_PositiveSize2D"/>
// <xsd:element name="effectExtent" type="CT_EffectExtent" minOccurs="0"/>
// <xsd:element name="docPr" type="a:CT_NonVisualDrawingProps" minOccurs="1" maxOccurs="1"/>
// <xsd:element name="cNvGraphicFramePr" type="a:CT_NonVisualGraphicFrameProperties"
// minOccurs="0" maxOccurs="1"/>
// <xsd:element ref="a:graphic" minOccurs="1" maxOccurs="1"/>
// </xsd:sequence>
// <xsd:attribute name="distT" type="ST_WrapDistance" use="optional"/>
// <xsd:attribute name="distB" type="ST_WrapDistance" use="optional"/>
// <xsd:attribute name="distL" type="ST_WrapDistance" use="optional"/>
// <xsd:attribute name="distR" type="ST_WrapDistance" use="optional"/>
// </xsd:complexType>
export class Inline extends XmlComponent {
private readonly extent: Extent;
private readonly graphic: Graphic;
constructor(mediaData: IMediaData, private readonly transform: IMediaDataTransformation) {
constructor(mediaData: IMediaData, transform: IMediaDataTransformation) {
super("wp:inline");
this.root.push(
@ -33,12 +47,4 @@ export class Inline extends XmlComponent {
this.root.push(new GraphicFrameProperties());
this.root.push(this.graphic);
}
public scale(factorX: number, factorY: number): void {
const newX = Math.round(this.transform.emus.x * factorX);
const newY = Math.round(this.transform.emus.y * factorY);
this.extent.setXY(newX, newY);
this.graphic.setXY(newX, newY);
}
}

View File

@ -1,11 +1,18 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { sectionMarginDefaults, sectionPageSizeDefaults } from "./document";
import { File } from "./file";
import { Footer, Header } from "./header";
import { Paragraph } from "./paragraph";
const PAGE_SIZE_DEFAULTS = {
"w:h": sectionPageSizeDefaults.HEIGHT,
"w:orient": sectionPageSizeDefaults.ORIENTATION,
"w:w": sectionPageSizeDefaults.WIDTH,
};
describe("File", () => {
describe("#constructor", () => {
it("should create with correct headers and footers", () => {
@ -114,23 +121,19 @@ describe("File", () => {
"w:sectPr": [
{
"w:pgSz": {
_attr: {
"w:h": 16838,
"w:orient": "portrait",
"w:w": 11906,
},
_attr: PAGE_SIZE_DEFAULTS,
},
},
{
"w:pgMar": {
_attr: {
"w:bottom": 1440,
"w:footer": 708,
"w:gutter": 0,
"w:header": 708,
"w:left": 1440,
"w:right": 1440,
"w:top": 1440,
"w:bottom": sectionMarginDefaults.BOTTOM,
"w:footer": sectionMarginDefaults.FOOTER,
"w:gutter": sectionMarginDefaults.GUTTER,
"w:header": sectionMarginDefaults.HEADER,
"w:left": sectionMarginDefaults.LEFT,
"w:right": sectionMarginDefaults.RIGHT,
"w:top": sectionMarginDefaults.TOP,
},
},
},
@ -139,15 +142,15 @@ describe("File", () => {
_attr: {},
},
},
{
"w:cols": {
_attr: {
"w:num": 1,
"w:sep": false,
"w:space": 708,
},
},
},
// {
// "w:cols": {
// _attr: {
// "w:num": 1,
// "w:sep": false,
// "w:space": 708,
// },
// },
// },
{
"w:docGrid": {
_attr: {
@ -162,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", () => {
it("should create footnote", () => {
const wrapper = new File({

View File

@ -3,7 +3,7 @@ import { ContentTypes } from "./content-types/content-types";
import { CoreProperties, IPropertiesOptions } from "./core-properties";
import { CustomProperties } from "./custom-properties";
import { DocumentWrapper } from "./document-wrapper";
import { FooterReferenceType, HeaderReferenceType, ISectionPropertiesOptions } from "./document/body/section-properties";
import { HeaderFooterReferenceType, ISectionPropertiesOptions } from "./document/body/section-properties";
import { IFileProperties } from "./file-properties";
import { FooterWrapper, IDocumentFooter } from "./footer-wrapper";
import { FootnotesWrapper } from "./footnotes-wrapper";
@ -78,6 +78,8 @@ export class File {
this.settings = new Settings({
compatabilityModeVersion: options.compatabilityModeVersion,
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();
@ -132,18 +134,6 @@ export class File {
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 {
@ -188,7 +178,7 @@ export class File {
return wrapper;
}
private addHeaderToDocument(header: HeaderWrapper, type: HeaderReferenceType = HeaderReferenceType.DEFAULT): void {
private addHeaderToDocument(header: HeaderWrapper, type: HeaderFooterReferenceType = HeaderFooterReferenceType.DEFAULT): void {
this.headers.push({ header, type });
this.documentWrapper.Relationships.createRelationship(
header.View.ReferenceId,
@ -198,7 +188,7 @@ export class File {
this.contentTypes.addHeader(this.headers.length);
}
private addFooterToDocument(footer: FooterWrapper, type: FooterReferenceType = FooterReferenceType.DEFAULT): void {
private addFooterToDocument(footer: FooterWrapper, type: HeaderFooterReferenceType = HeaderFooterReferenceType.DEFAULT): void {
this.footers.push({ footer, type });
this.documentWrapper.Relationships.createRelationship(
footer.View.ReferenceId,

View File

@ -1,6 +1,6 @@
import { XmlComponent } from "file/xml-components";
import { FooterReferenceType } from "./document";
import { HeaderFooterReferenceType } from "./document";
import { IViewWrapper } from "./document-wrapper";
import { Footer } from "./footer/footer";
import { Media } from "./media";
@ -10,7 +10,7 @@ import { Table } from "./table";
export interface IDocumentFooter {
readonly footer: FooterWrapper;
readonly type: FooterReferenceType;
readonly type: HeaderFooterReferenceType;
}
export class FooterWrapper implements IViewWrapper {

View File

@ -1,5 +1,4 @@
import { Run } from "file/paragraph/run";
import { Style } from "file/paragraph/run/style";
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
export class FootNoteReferenceRunAttributes extends XmlAttributeComponent<{
@ -24,9 +23,7 @@ export class FootnoteReference extends XmlComponent {
export class FootnoteReferenceRun extends Run {
constructor(id: number) {
super({});
this.properties.push(new Style("FootnoteReference"));
super({ style: "FootnoteReference" });
this.root.push(new FootnoteReference(id));
}

View File

@ -1,6 +1,6 @@
import { XmlComponent } from "file/xml-components";
import { HeaderReferenceType } from "./document";
import { HeaderFooterReferenceType } from "./document";
import { IViewWrapper } from "./document-wrapper";
import { Header } from "./header/header";
import { Media } from "./media";
@ -10,7 +10,7 @@ import { Table } from "./table";
export interface IDocumentHeader {
readonly header: HeaderWrapper;
readonly type: HeaderReferenceType;
readonly type: HeaderFooterReferenceType;
}
export class HeaderWrapper implements IViewWrapper {

View File

@ -6,6 +6,7 @@ export * from "./numbering";
export * from "./media";
export * from "./drawing";
export * from "./document";
export * from "./shading";
export * from "./styles";
export * from "./table-of-contents";
export * from "./xml-components";
@ -15,3 +16,6 @@ export * from "./header";
export * from "./footnotes";
export * from "./track-revision";
export * from "./shared";
export * from "./border";
export * from "./values";
export * from "./vertical-align";

View File

@ -5,7 +5,7 @@ import { EMPTY_OBJECT } from "file/xml-components";
import { AlignmentType, EmphasisMarkType, TabStopPosition } from "../paragraph";
import { UnderlineType } from "../paragraph/run/underline";
import { ShadingType } from "../table";
import { ShadingType } from "../shading";
import { AbstractNumbering } from "./abstract-numbering";
import { LevelFormat, LevelSuffix } from "./level";
@ -351,7 +351,7 @@ describe("AbstractNumbering", () => {
]);
const tree = new Formatter().format(abstractNumbering);
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({
"w:rPr": [{ "w:smallCaps": { _attr: { "w:val": true } } }],
"w:rPr": [{ "w:smallCaps": {} }],
});
});
@ -370,7 +370,7 @@ describe("AbstractNumbering", () => {
]);
const tree = new Formatter().format(abstractNumbering);
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({
"w:rPr": [{ "w:caps": { _attr: { "w:val": true } } }],
"w:rPr": [{ "w:caps": {} }],
});
});
@ -390,7 +390,7 @@ describe("AbstractNumbering", () => {
const tree = new Formatter().format(abstractNumbering);
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({
"w:rPr": [{ "w:strike": { _attr: { "w:val": true } } }],
"w:rPr": [{ "w:strike": {} }],
});
});
@ -409,7 +409,7 @@ describe("AbstractNumbering", () => {
]);
const tree = new Formatter().format(abstractNumbering);
expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({
"w:rPr": [{ "w:dstrike": { _attr: { "w:val": true } } }],
"w:rPr": [{ "w:dstrike": {} }],
});
});
@ -515,17 +515,17 @@ describe("AbstractNumbering", () => {
const boldTests = [
{
bold: true,
expected: [{ "w:b": { _attr: { "w:val": true } } }, { "w:bCs": { _attr: { "w:val": true } } }],
expected: [{ "w:b": {} }, { "w:bCs": {} }],
},
{
bold: true,
boldComplexScript: true,
expected: [{ "w:b": { _attr: { "w:val": true } } }, { "w:bCs": { _attr: { "w:val": true } } }],
expected: [{ "w:b": {} }, { "w:bCs": {} }],
},
{
bold: true,
boldComplexScript: false,
expected: [{ "w:b": { _attr: { "w:val": true } } }],
expected: [{ "w:b": {} }],
},
];
boldTests.forEach(({ bold, boldComplexScript, expected }) => {
@ -548,17 +548,17 @@ describe("AbstractNumbering", () => {
const italicsTests = [
{
italics: true,
expected: [{ "w:i": { _attr: { "w:val": true } } }, { "w:iCs": { _attr: { "w:val": true } } }],
expected: [{ "w:i": {} }, { "w:iCs": {} }],
},
{
italics: true,
italicsComplexScript: true,
expected: [{ "w:i": { _attr: { "w:val": true } } }, { "w:iCs": { _attr: { "w:val": true } } }],
expected: [{ "w:i": {} }, { "w:iCs": {} }],
},
{
italics: true,
italicsComplexScript: false,
expected: [{ "w:i": { _attr: { "w:val": true } } }],
expected: [{ "w:i": {} }],
},
];
italicsTests.forEach(({ italics, italicsComplexScript, expected }) => {
@ -617,37 +617,13 @@ describe("AbstractNumbering", () => {
});
const shadingTests = [
{
shadow: {
type: ShadingType.PERCENT_10,
fill: "00FFFF",
color: "FF0000",
},
expected: [{ "w:shd": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } }],
},
{
shading: {
type: ShadingType.PERCENT_10,
fill: "00FFFF",
color: "FF0000",
type: ShadingType.DIAGONAL_STRIPE,
fill: "006622",
color: "0000FF",
},
expected: [{ "w:shd": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } }],
},
{
shading: {
type: ShadingType.PERCENT_10,
fill: "00FFFF",
color: "FF0000",
},
expected: [{ "w:shd": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } }],
},
{
shading: {
type: ShadingType.PERCENT_10,
fill: "00FFFF",
color: "FF0000",
},
expected: [{ "w:shd": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } }],
expected: [{ "w:shd": { _attr: { "w:val": "diagStripe", "w:fill": "006622", "w:color": "0000FF" } } }],
},
{
shading: {
@ -658,15 +634,15 @@ describe("AbstractNumbering", () => {
expected: [{ "w:shd": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } }],
},
];
shadingTests.forEach(({ shadow, shading, expected }) => {
it("#shadow correctly", () => {
shadingTests.forEach(({ shading, expected }) => {
it("#shade correctly", () => {
const abstractNumbering = new AbstractNumbering(1, [
{
level: 0,
format: LevelFormat.LOWER_ROMAN,
text: "%0.",
style: {
run: { shadow, shading },
run: { shading },
},
},
]);

Some files were not shown because too many files have changed in this diff Show More