Merge branch 'master' into image-support

This commit is contained in:
Dolan Miu
2017-03-12 21:38:38 +00:00
27 changed files with 937 additions and 49 deletions

15
demo/demo.js Normal file
View File

@ -0,0 +1,15 @@
const docx = require('../build');
var doc = new docx.Document();
var paragraph = new docx.Paragraph("Hello World");
var institutionText = new docx.TextRun("University College London").bold();
var dateText = new docx.TextRun("5th Dec 2015").tab().bold();
paragraph.addText(institutionText);
paragraph.addText(dateText);
doc.addParagraph(paragraph);
var exporter = new docx.LocalPacker(doc);
exporter.pack('My Document');
console.log('Document created succesfully at project root!');

View File

@ -8,7 +8,8 @@
"test": "mocha ./build-tests --recursive",
"prepublishOnly": "npm run build",
"lint": "tslint --project ./ts",
"build": "rimraf ./build && tsc -p ts"
"build": "rimraf ./build && tsc -p ts",
"demo": "npm run build && node ./demo/demo.js"
},
"files": [
"ts",

View File

@ -1,7 +1,9 @@
import { Paragraph } from "../paragraph";
import { Table } from "../table";
import { XmlComponent } from "../xml-components";
import { Body } from "./body";
import { DocumentAttributes } from "./document-attributes";
export class Document extends XmlComponent {
private body: Body;
@ -39,4 +41,15 @@ export class Document extends XmlComponent {
this.addParagraph(para);
return para;
}
public addTable(table: Table): void {
this.body.push(table);
}
public createTable(rows: number, cols: number): Table {
const table = new Table(rows, cols);
this.addTable(table);
return table;
}
}

View File

@ -2,3 +2,4 @@ export { Document } from "./document";
export { Paragraph } from "./paragraph";
export { Run } from "./run";
export { TextRun } from "./run/text-run";
export { Table } from "./table";

View File

@ -0,0 +1,15 @@
import { XmlAttributeComponent, XmlComponent } from "../xml-components";
type alignmentOptions = "left" | "center" | "right" | "both";
class AlignmentAttributes extends XmlAttributeComponent<{val: alignmentOptions}> {
protected xmlKeys = {val: "w:val"};
}
export class Alignment extends XmlComponent {
constructor(type: alignmentOptions) {
super("w:jc");
this.root.push(new AlignmentAttributes({val: type}));
}
}

View File

@ -0,0 +1,9 @@
export { Alignment } from "./alignment";
export { ThematicBreak } from "./border";
export { Indent } from "./indent";
export { PageBreak } from "./page-break";
export { ParagraphProperties } from "./properties";
export { ISpacingProperties, Spacing } from "./spacing";
export { Style } from "./style";
export { LeftTabStop, MaxRightTabStop } from "./tab-stop";
export { NumberProperties } from "./unordered-list";

View File

@ -1,7 +1,8 @@
import { Num } from "../../numbering/num";
import { TextRun } from "../run/text-run";
import { Attributes, XmlComponent } from "../xml-components";
import { XmlComponent } from "../xml-components";
import { Alignment } from "./alignment";
import { ThematicBreak } from "./border";
import { Indent } from "./indent";
import { PageBreak } from "./page-break";
@ -11,16 +12,6 @@ import { Style } from "./style";
import { LeftTabStop, MaxRightTabStop } from "./tab-stop";
import { NumberProperties } from "./unordered-list";
class Alignment extends XmlComponent {
constructor(type: string) {
super("w:jc");
this.root.push(new Attributes({
val: type,
}));
}
}
export class Paragraph extends XmlComponent {
private properties: ParagraphProperties;

View File

@ -1,4 +1,4 @@
import { Attributes, XmlComponent } from "../xml-components";
import { XmlAttributeComponent, XmlComponent } from "../xml-components";
class TabStop extends XmlComponent {
@ -8,11 +8,17 @@ class TabStop extends XmlComponent {
}
}
export type tabOptions = "left" | "right";
class TabAttributes extends XmlAttributeComponent<{val: tabOptions, pos: string | number}> {
protected xmlKeys = {val: "w:val", pos: "w:pos"};
}
class Tab extends XmlComponent {
constructor(value: string, position: string | number) {
constructor(value: tabOptions, position: string | number) {
super("w:tab");
this.root.push(new Attributes({
this.root.push(new TabAttributes({
val: value,
pos: position,
}));

View File

@ -1,5 +1,7 @@
import { Attributes, XmlComponent } from "../xml-components";
export { Underline } from "./underline";
export { SubScript, SuperScript } from "./script";
export { RunFonts } from "./run-fonts";
export class Bold extends XmlComponent {

View File

@ -1,10 +1,10 @@
import { Break } from "./break";
import { Caps, SmallCaps } from "./caps";
import { Bold, Italics } from "./formatting";
import { Bold, Color, DoubleStrike, Italics, Size, Strike } from "./formatting";
import { RunProperties } from "./properties";
import { RunFonts } from "./run-fonts";
import { SubScript, SuperScript } from "./script";
import { DoubleStrike, Strike } from "./strike";
import { Style } from "./style";
import { Tab } from "./tab";
import { Underline } from "./underline";
@ -29,8 +29,18 @@ export class Run extends XmlComponent {
return this;
}
public underline(): Run {
this.properties.push(new Underline());
public underline(underlineType?: string, color?: string): Run {
this.properties.push(new Underline(underlineType, color));
return this;
}
public color(color: string): Run {
this.properties.push(new Color(color));
return this;
}
public size(size: number): Run {
this.properties.push(new Size(size));
return this;
}
@ -78,4 +88,9 @@ export class Run extends XmlComponent {
this.properties.push(new RunFonts(fontName));
return this;
}
public style(styleId: string): Run {
this.properties.push(new Style(styleId));
return this;
}
}

View File

@ -1,15 +0,0 @@
import { XmlComponent } from "../xml-components";
export class Strike extends XmlComponent {
constructor() {
super("w:strike");
}
}
export class DoubleStrike extends XmlComponent {
constructor() {
super("w:dstrike");
}
}

13
ts/docx/run/style.ts Normal file
View File

@ -0,0 +1,13 @@
import { XmlAttributeComponent, XmlComponent } from "../xml-components";
class StyleAttributes extends XmlAttributeComponent<{val: string}> {
protected xmlKeys = {val: "w:val"};
}
export class Style extends XmlComponent {
constructor(styleId: string) {
super("w:rStyle");
this.root.push(new StyleAttributes({val: styleId}));
}
}

View File

@ -1,3 +0,0 @@
export class Table {
}

21
ts/docx/table/grid.ts Normal file
View File

@ -0,0 +1,21 @@
import { XmlAttributeComponent, XmlComponent } from "../xml-components";
export class TableGrid extends XmlComponent {
constructor(cols: number[]) {
super("w:tblGrid");
cols.forEach((col) => this.root.push(new GridCol(col)));
}
}
class GridColAttributes extends XmlAttributeComponent<{w: number}> {
protected xmlKeys = {w: "w:w"};
}
export class GridCol extends XmlComponent {
constructor(width?: number) {
super("w:gridCol");
if (width !== undefined) {
this.root.push(new GridColAttributes({w: width}));
}
}
}

123
ts/docx/table/index.ts Normal file
View File

@ -0,0 +1,123 @@
import { Paragraph } from "../paragraph";
import { XmlComponent } from "../xml-components";
import { TableGrid } from "./grid";
import { TableProperties, widthTypes } from "./properties";
export class Table extends XmlComponent {
private properties: TableProperties;
private rows: TableRow[];
private grid: TableGrid;
constructor(rows: number, cols: number) {
super("w:tbl");
this.properties = new TableProperties();
this.root.push(this.properties);
const gridCols: number[] = [];
for (let i = 0; i < cols; i++) {
/*
0-width columns don't get rendered correctly, so we need
to give them some value. A reasonable default would be
~6in / numCols, but if we do that it becomes very hard
to resize the table using setWidth, unless the layout
algorithm is set to 'fixed'. Instead, the approach here
means even in 'auto' layout, setting a width on the
table will make it look reasonable, as the layout
algorithm will expand columns to fit its content
*/
gridCols.push(1);
}
this.grid = new TableGrid(gridCols);
this.root.push(this.grid);
this.rows = [];
for (let i = 0; i < rows; i++) {
const cells: TableCell[] = [];
for (let j = 0; j < cols; j++) {
cells.push(new TableCell());
}
const row = new TableRow(cells);
this.rows.push(row);
this.root.push(row);
}
}
public getRow(ix: number): TableRow {
return this.rows[ix];
}
public getCell(row: number, col: number): TableCell {
return this.getRow(row).getCell(col);
}
public setWidth(type: widthTypes, width: number | string): Table {
this.properties.setWidth(type, width);
return this;
}
public fixedWidthLayout(): Table {
this.properties.fixedWidthLayout();
return this;
}
}
class TableRow extends XmlComponent {
private properties: TableRowProperties;
private cells: TableCell[];
constructor(cells: TableCell[]) {
super("w:tr");
this.properties = new TableRowProperties();
this.root.push(this.properties);
this.cells = cells;
cells.forEach((c) => this.root.push(c));
}
public getCell(ix: number): TableCell {
return this.cells[ix];
}
}
class TableRowProperties extends XmlComponent {
constructor() {
super("w:trPr");
}
}
class TableCell extends XmlComponent {
private properties: TableCellProperties;
constructor() {
super("w:tc");
this.properties = new TableCellProperties();
this.root.push(this.properties);
}
public addContent(content: Paragraph | Table): TableCell {
this.root.push(content);
return this;
}
public prepForXml(): object {
// Cells must end with a paragraph
const retval = super.prepForXml();
const content = retval["w:tc"];
if (!content[content.length - 1]["w:p"]) {
content.push(new Paragraph().prepForXml());
}
return retval;
}
public createParagraph(text?: string): Paragraph {
const para = new Paragraph(text);
this.addContent(para);
return para;
}
}
class TableCellProperties extends XmlComponent {
constructor() {
super("w:tcPr");
}
}

View File

@ -0,0 +1,48 @@
import { XmlAttributeComponent, XmlComponent } from "../xml-components";
export type widthTypes = "dxa" | "pct" | "nil" | "auto";
export class TableProperties extends XmlComponent {
constructor() {
super("w:tblPr");
}
public setWidth(type: widthTypes, w: number | string): TableProperties {
this.root.push(new PreferredTableWidth(type, w));
return this;
}
public fixedWidthLayout(): TableProperties {
this.root.push(new TableLayout("fixed"));
return this;
}
}
interface ITableWidth {
type: widthTypes;
w: number | string;
}
class TableWidthAttributes extends XmlAttributeComponent<ITableWidth> {
protected xmlKeys = {type: "w:type", w: "w:w"};
}
class PreferredTableWidth extends XmlComponent {
constructor(type: widthTypes, w: number | string) {
super("w:tblW");
this.root.push(new TableWidthAttributes({type, w}));
}
}
type tableLayout = "autofit" | "fixed";
class TableLayoutAttributes extends XmlAttributeComponent<{type: tableLayout}> {
protected xmlKeys = {type: "w:type"};
}
class TableLayout extends XmlComponent {
constructor(type: tableLayout) {
super("w:tblLayout");
this.root.push(new TableLayoutAttributes({type}));
}
}

View File

@ -18,7 +18,7 @@ export class ExpressPacker extends Packer {
}
public pack(name: string): void {
this.res.attachment(name + ".docx");
this.res.attachment(`${name}.docx`);
super.pack(this.res);
}
}

View File

@ -13,7 +13,7 @@ export class LocalPacker extends Packer {
}
public pack(path: string): void {
this.stream = fs.createWriteStream(path);
this.stream = fs.createWriteStream(`${path}.docx`);
super.pack(this.stream);
}
}

View File

@ -1,6 +1,4 @@
import { Indent } from "../../docx/paragraph/indent";
import { ParagraphProperties } from "../../docx/paragraph/properties";
import { ISpacingProperties, Spacing } from "../../docx/paragraph/spacing";
import * as paragraph from "../../docx/paragraph/formatting";
import * as formatting from "../../docx/run/formatting";
import { RunProperties } from "../../docx/run/properties";
import { XmlAttributeComponent, XmlComponent } from "../../docx/xml-components";
@ -40,12 +38,12 @@ export class Style extends XmlComponent {
export class ParagraphStyle extends Style {
private paragraphProperties: ParagraphProperties;
private paragraphProperties: paragraph.ParagraphProperties;
private runProperties: RunProperties;
constructor(styleId: string, name?: string) {
super({type: "paragraph", styleId: styleId}, name);
this.paragraphProperties = new ParagraphProperties();
this.paragraphProperties = new paragraph.ParagraphProperties();
this.runProperties = new RunProperties();
this.root.push(this.paragraphProperties);
this.root.push(this.runProperties);
@ -74,6 +72,8 @@ export class ParagraphStyle extends Style {
return this;
}
// ---------- Run formatting ---------------------- //
public size(twips: number): ParagraphStyle {
this.addRunProperty(new formatting.Size(twips));
return this;
@ -89,6 +89,36 @@ export class ParagraphStyle extends Style {
return this;
}
public smallCaps(): ParagraphStyle {
this.addRunProperty(new formatting.SmallCaps());
return this;
}
public allCaps(): ParagraphStyle {
this.addRunProperty(new formatting.Caps());
return this;
}
public strike(): ParagraphStyle {
this.addRunProperty(new formatting.Strike());
return this;
}
public doubleStrike(): ParagraphStyle {
this.addRunProperty(new formatting.DoubleStrike());
return this;
}
public subScript(): ParagraphStyle {
this.addRunProperty(new formatting.SubScript());
return this;
}
public superScript(): ParagraphStyle {
this.addRunProperty(new formatting.SuperScript());
return this;
}
public underline(underlineType?: string, color?: string): ParagraphStyle {
this.addRunProperty(new formatting.Underline(underlineType, color));
return this;
@ -99,13 +129,55 @@ export class ParagraphStyle extends Style {
return this;
}
public indent(left: number, hanging?: number): ParagraphStyle {
this.addParagraphProperty(new Indent(left, hanging));
public font(fontName: string): ParagraphStyle {
this.addRunProperty(new formatting.RunFonts(fontName));
return this;
}
public spacing(params: ISpacingProperties): ParagraphStyle {
this.addParagraphProperty(new Spacing(params));
// --------------------- Paragraph formatting ------------------------ //
public center(): ParagraphStyle {
this.addParagraphProperty(new paragraph.Alignment("center"));
return this;
}
public left(): ParagraphStyle {
this.addParagraphProperty(new paragraph.Alignment("left"));
return this;
}
public right(): ParagraphStyle {
this.addParagraphProperty(new paragraph.Alignment("right"));
return this;
}
public justified(): ParagraphStyle {
this.addParagraphProperty(new paragraph.Alignment("both"));
return this;
}
public thematicBreak(): ParagraphStyle {
this.addParagraphProperty(new paragraph.ThematicBreak());
return this;
}
public maxRightTabStop(): ParagraphStyle {
this.addParagraphProperty(new paragraph.MaxRightTabStop());
return this;
}
public leftTabStop(position: number): ParagraphStyle {
this.addParagraphProperty(new paragraph.LeftTabStop(position));
return this;
}
public indent(left: number, hanging?: number): ParagraphStyle {
this.addParagraphProperty(new paragraph.Indent(left, hanging));
return this;
}
public spacing(params: paragraph.ISpacingProperties): ParagraphStyle {
this.addParagraphProperty(new paragraph.Spacing(params));
return this;
};
}

View File

@ -1,7 +1,12 @@
{
"compilerOptions": {
"target": "es6",
"strictNullChecks": true,
"sourceMap": true,
"removeComments": true,
"preserveConstEnums": true,
"outDir": "../build-tests",
"sourceRoot": "./",
"rootDir": "./",
"module": "commonjs"
}

View File

@ -46,4 +46,28 @@ describe("Document", () => {
});
});
});
describe("#createTable", () => {
it("should create a new table and append it to body", () => {
const table = document.createTable(2, 3);
expect(table).to.be.an.instanceof(docx.Table);
const body = new Formatter().format(document)["w:document"][1]["w:body"];
expect(body).to.be.an("array").which.has.length.at.least(1);
expect(body[0]).to.have.property("w:tbl");
});
it("should create a table with the correct dimensions", () => {
document.createTable(2, 3);
const body = new Formatter().format(document)["w:document"][1]["w:body"];
expect(body).to.be.an("array").which.has.length.at.least(1);
expect(body[0]).to.have.property("w:tbl").which.includes({
"w:tblGrid": [
{"w:gridCol": [{_attr: {"w:w": 1}}]},
{"w:gridCol": [{_attr: {"w:w": 1}}]},
{"w:gridCol": [{_attr: {"w:w": 1}}]},
],
});
expect(body[0]["w:tbl"].filter((x) => x["w:tr"])).to.have.length(2);
});
});
});

View File

@ -32,6 +32,26 @@ describe("Run", () => {
const newJson = Utility.jsonify(run);
assert.equal(newJson.root[0].root[0].rootKey, "w:u");
});
it("should default to 'single' and no color", () => {
run.underline();
const tree = new Formatter().format(run);
expect(tree).to.deep.equal({
"w:r": [
{"w:rPr": [{"w:u": [{_attr: {"w:val": "single"}}]}]},
],
});
});
it("should set the style type and color if given", () => {
run.underline("double", "990011");
const tree = new Formatter().format(run);
expect(tree).to.deep.equal({
"w:r": [
{"w:rPr": [{"w:u": [{_attr: {"w:val": "double", "w:color": "990011"}}]}]},
],
});
});
});
describe("#smallCaps()", () => {
@ -97,4 +117,40 @@ describe("Run", () => {
});
});
});
describe("#color", () => {
it("should set the run to the color given", () => {
run.color("001122");
const tree = new Formatter().format(run);
expect(tree).to.deep.equal({
"w:r": [
{"w:rPr": [{"w:color": [{_attr: {"w:val": "001122"}}]}]},
],
});
});
});
describe("#size", () => {
it("should set the run to the given size", () => {
run.size(24);
const tree = new Formatter().format(run);
expect(tree).to.deep.equal({
"w:r": [
{"w:rPr": [{"w:sz": [{_attr: {"w:val": 24}}]}]},
],
});
});
});
describe("#style", () => {
it("should set the style to the given styleId", () => {
run.style("myRunStyle");
const tree = new Formatter().format(run);
expect(tree).to.deep.equal({
"w:r": [
{"w:rPr": [{"w:rStyle": [{_attr: {"w:val": "myRunStyle"}}]}]},
],
});
});
});
});

View File

@ -1,5 +1,5 @@
import { assert } from "chai";
import { DoubleStrike, Strike } from "../../../docx/run/strike";
import { DoubleStrike, Strike } from "../../../docx/run/formatting";
import { Utility } from "../../utility";
describe("Strike", () => {

View File

@ -0,0 +1,37 @@
import { expect } from "chai";
import { GridCol, TableGrid } from "../../../docx/table/grid";
import { Formatter } from "../../../export/formatter";
describe("GridCol", () => {
describe("#constructor", () => {
it("sets the width attribute to the value given", () => {
const grid = new GridCol(1234);
const tree = new Formatter().format(grid);
expect(tree).to.deep.equal({
"w:gridCol": [{_attr: {"w:w": 1234}}],
});
});
it("does not set a width attribute if not given", () => {
const grid = new GridCol();
const tree = new Formatter().format(grid);
expect(tree).to.deep.equal({"w:gridCol": []});
});
});
});
describe("TableGrid", () => {
describe("#constructor", () => {
it("creates a column for each width given", () => {
const grid = new TableGrid([1234, 321, 123]);
const tree = new Formatter().format(grid);
expect(tree).to.deep.equal({
"w:tblGrid": [
{"w:gridCol": [{_attr: {"w:w": 1234}}]},
{"w:gridCol": [{_attr: {"w:w": 321}}]},
{"w:gridCol": [{_attr: {"w:w": 123}}]},
],
});
});
});
});

View File

@ -0,0 +1,37 @@
import { expect } from "chai";
import { TableProperties } from "../../../docx/table/properties";
import { Formatter } from "../../../export/formatter";
describe("TableProperties", () => {
describe("#constructor", () => {
it("creates an initially empty property object", () => {
const tp = new TableProperties();
const tree = new Formatter().format(tp);
expect(tree).to.deep.equal({"w:tblPr": []});
});
});
describe("#setWidth", () => {
it("adds a table width property", () => {
const tp = new TableProperties().setWidth("dxa", 1234);
const tree = new Formatter().format(tp);
expect(tree).to.deep.equal({
"w:tblPr": [
{"w:tblW": [{_attr: {"w:type": "dxa", "w:w": 1234}}]},
],
});
});
});
describe("#fixedWidthLayout", () => {
it("sets the table to fixed width layout", () => {
const tp = new TableProperties().fixedWidthLayout();
const tree = new Formatter().format(tp);
expect(tree).to.deep.equal({
"w:tblPr": [
{"w:tblLayout": [{_attr: {"w:type": "fixed"}}]},
],
});
});
});
});

View File

@ -0,0 +1,185 @@
import { expect } from "chai";
import { Paragraph } from "../../../docx/paragraph";
import { Table } from "../../../docx/table";
import { Formatter } from "../../../export/formatter";
describe("Table", () => {
describe("#constructor", () => {
it("creates a table with the correct number of rows and columns", () => {
const table = new Table(3, 2);
const tree = new Formatter().format(table);
const cell = {"w:tc": [{"w:tcPr": []}, {"w:p": [{"w:pPr": []}]}]};
expect(tree).to.deep.equal({
"w:tbl": [
{"w:tblPr": []},
{"w:tblGrid": [
{"w:gridCol": [{_attr: {"w:w": 1}}]},
{"w:gridCol": [{_attr: {"w:w": 1}}]},
]},
{"w:tr": [{"w:trPr": []}, cell, cell]},
{"w:tr": [{"w:trPr": []}, cell, cell]},
{"w:tr": [{"w:trPr": []}, cell, cell]},
],
});
});
});
describe("#getRow and Row#getCell", () => {
it("returns the correct row", () => {
const table = new Table(2, 2);
table.getRow(0).getCell(0).addContent(new Paragraph("A1"));
table.getRow(0).getCell(1).addContent(new Paragraph("B1"));
table.getRow(1).getCell(0).addContent(new Paragraph("A2"));
table.getRow(1).getCell(1).addContent(new Paragraph("B2"));
const tree = new Formatter().format(table);
const cell = (c) => ({"w:tc": [
{"w:tcPr": []},
{"w:p": [
{"w:pPr": []},
{"w:r": [{"w:rPr": []}, {"w:t": [c]}]},
]},
]});
expect(tree).to.deep.equal({
"w:tbl": [
{"w:tblPr": []},
{"w:tblGrid": [
{"w:gridCol": [{_attr: {"w:w": 1}}]},
{"w:gridCol": [{_attr: {"w:w": 1}}]},
]},
{"w:tr": [{"w:trPr": []}, cell("A1"), cell("B1")]},
{"w:tr": [{"w:trPr": []}, cell("A2"), cell("B2")]},
],
});
});
});
describe("#getCell", () => {
it("returns the correct cell", () => {
const table = new Table(2, 2);
table.getCell(0, 0).addContent(new Paragraph("A1"));
table.getCell(0, 1).addContent(new Paragraph("B1"));
table.getCell(1, 0).addContent(new Paragraph("A2"));
table.getCell(1, 1).addContent(new Paragraph("B2"));
const tree = new Formatter().format(table);
const cell = (c) => ({"w:tc": [
{"w:tcPr": []},
{"w:p": [
{"w:pPr": []},
{"w:r": [{"w:rPr": []}, {"w:t": [c]}]},
]},
]});
expect(tree).to.deep.equal({
"w:tbl": [
{"w:tblPr": []},
{"w:tblGrid": [
{"w:gridCol": [{_attr: {"w:w": 1}}]},
{"w:gridCol": [{_attr: {"w:w": 1}}]},
]},
{"w:tr": [{"w:trPr": []}, cell("A1"), cell("B1")]},
{"w:tr": [{"w:trPr": []}, cell("A2"), cell("B2")]},
],
});
});
});
describe("#setWidth", () => {
it("sets the preferred width on the table", () => {
const table = new Table(2, 2).setWidth("pct", 1000);
const tree = new Formatter().format(table);
expect(tree).to.have.property("w:tbl").which.is.an("array").with.has.length.at.least(1);
expect(tree["w:tbl"][0]).to.deep.equal({
"w:tblPr": [
{"w:tblW": [{_attr: {"w:type": "pct", "w:w": 1000}}]},
],
});
});
});
describe("#fixedWidthLayout", () => {
it("sets the table to fixed width layout", () => {
const table = new Table(2, 2).fixedWidthLayout();
const tree = new Formatter().format(table);
expect(tree).to.have.property("w:tbl").which.is.an("array").with.has.length.at.least(1);
expect(tree["w:tbl"][0]).to.deep.equal({
"w:tblPr": [
{"w:tblLayout": [{_attr: {"w:type": "fixed"}}]},
],
});
});
});
describe("Cell", () => {
describe("#prepForXml", () => {
it("inserts a paragraph at the end of the cell if it is empty", () => {
const table = new Table(1, 1);
const tree = new Formatter().format(table);
expect(tree).to.have.property("w:tbl").which.is.an("array");
const row = tree["w:tbl"].find((x) => x["w:tr"]);
expect(row).not.to.be.undefined;
expect(row["w:tr"]).to.be.an("array").which.has.length.at.least(1);
expect(row["w:tr"].find((x) => x["w:tc"])).to.deep.equal({
"w:tc": [
{"w:tcPr": []},
{"w:p": [{"w:pPr": []}]},
],
});
});
it("inserts a paragraph at the end of the cell even if it has a child table", () => {
const parentTable = new Table(1, 1);
parentTable.getCell(0, 0).addContent(new Table(1, 1));
const tree = new Formatter().format(parentTable);
expect(tree).to.have.property("w:tbl").which.is.an("array");
const row = tree["w:tbl"].find((x) => x["w:tr"]);
expect(row).not.to.be.undefined;
expect(row["w:tr"]).to.be.an("array").which.has.length.at.least(1);
const cell = row["w:tr"].find((x) => x["w:tc"]);
expect(cell).not.to.be.undefined;
expect(cell["w:tc"][cell["w:tc"].length - 1]).to.deep.equal({
"w:p": [{"w:pPr": []}],
});
});
it("does not insert a paragraph if it already ends with one", () => {
const parentTable = new Table(1, 1);
parentTable.getCell(0, 0).addContent(new Paragraph("Hello"));
const tree = new Formatter().format(parentTable);
expect(tree).to.have.property("w:tbl").which.is.an("array");
const row = tree["w:tbl"].find((x) => x["w:tr"]);
expect(row).not.to.be.undefined;
expect(row["w:tr"]).to.be.an("array").which.has.length.at.least(1);
expect(row["w:tr"].find((x) => x["w:tc"])).to.deep.equal({
"w:tc": [
{"w:tcPr": []},
{"w:p": [
{"w:pPr": []},
{"w:r": [{"w:rPr": []}, {"w:t": ["Hello"]}]},
]},
],
});
});
});
describe("#createParagraph", () => {
it("inserts a new paragraph in the cell", () => {
const table = new Table(1, 1);
const para = table.getCell(0, 0).createParagraph("Test paragraph");
expect(para).to.be.an.instanceof(Paragraph);
const tree = new Formatter().format(table);
expect(tree).to.have.property("w:tbl").which.is.an("array");
const row = tree["w:tbl"].find((x) => x["w:tr"]);
expect(row).not.to.be.undefined;
expect(row["w:tr"]).to.be.an("array").which.has.length.at.least(1);
expect(row["w:tr"].find((x) => x["w:tc"])).to.deep.equal({
"w:tc": [
{"w:tcPr": []},
{"w:p": [
{"w:pPr": []},
{"w:r": [{"w:rPr": []}, {"w:t": ["Test paragraph"]}]},
]},
],
});
});
});
});
});

View File

@ -212,6 +212,120 @@ describe("ParagraphStyle", () => {
],
});
});
it("#center", () => {
const style = new ParagraphStyle("myStyleId")
.center();
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
{_attr: {"w:type": "paragraph", "w:styleId": "myStyleId"}},
{"w:pPr": [
{"w:jc": [{_attr: {"w:val": "center"}}]},
]},
{"w:rPr": []},
],
});
});
it("#left", () => {
const style = new ParagraphStyle("myStyleId")
.left();
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
{_attr: {"w:type": "paragraph", "w:styleId": "myStyleId"}},
{"w:pPr": [
{"w:jc": [{_attr: {"w:val": "left"}}]},
]},
{"w:rPr": []},
],
});
});
it("#right", () => {
const style = new ParagraphStyle("myStyleId")
.right();
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
{_attr: {"w:type": "paragraph", "w:styleId": "myStyleId"}},
{"w:pPr": [
{"w:jc": [{_attr: {"w:val": "right"}}]},
]},
{"w:rPr": []},
],
});
});
it("#justified", () => {
const style = new ParagraphStyle("myStyleId")
.justified();
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
{_attr: {"w:type": "paragraph", "w:styleId": "myStyleId"}},
{"w:pPr": [
{"w:jc": [{_attr: {"w:val": "both"}}]},
]},
{"w:rPr": []},
],
});
});
it("#thematicBreak", () => {
const style = new ParagraphStyle("myStyleId")
.thematicBreak();
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
{_attr: {"w:type": "paragraph", "w:styleId": "myStyleId"}},
{"w:pPr": [
{"w:pBdr": [{"w:bottom": [{_attr: {
"w:color": "auto",
"w:space": "1",
"w:val": "single",
"w:sz": "6",
}}]}]},
]},
{"w:rPr": []},
],
});
});
it("#leftTabStop", () => {
const style = new ParagraphStyle("myStyleId")
.leftTabStop(1200);
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
{_attr: {"w:type": "paragraph", "w:styleId": "myStyleId"}},
{"w:pPr": [
{"w:tabs": [
{"w:tab": [{_attr: {"w:val": "left", "w:pos": 1200}}]},
]},
]},
{"w:rPr": []},
],
});
});
it("#maxRightTabStop", () => {
const style = new ParagraphStyle("myStyleId")
.maxRightTabStop();
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
{_attr: {"w:type": "paragraph", "w:styleId": "myStyleId"}},
{"w:pPr": [
{"w:tabs": [
{"w:tab": [{_attr: {"w:val": "right", "w:pos": 9026}}]},
]},
]},
{"w:rPr": []},
],
});
});
});
describe("formatting methods: run properties", () => {
@ -230,6 +344,109 @@ describe("ParagraphStyle", () => {
});
});
it("#smallCaps", () => {
const style = new ParagraphStyle("myStyleId")
.smallCaps();
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
{_attr: {"w:type": "paragraph", "w:styleId": "myStyleId"}},
{"w:pPr": []},
{"w:rPr": [
{"w:smallCaps": [{_attr: {"w:val": true}}]},
]},
],
});
});
it("#allCaps", () => {
const style = new ParagraphStyle("myStyleId")
.allCaps();
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
{_attr: {"w:type": "paragraph", "w:styleId": "myStyleId"}},
{"w:pPr": []},
{"w:rPr": [
{"w:caps": [{_attr: {"w:val": true}}]},
]},
],
});
});
it("#strike", () => {
const style = new ParagraphStyle("myStyleId")
.strike();
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
{_attr: {"w:type": "paragraph", "w:styleId": "myStyleId"}},
{"w:pPr": []},
{"w:rPr": [
{"w:strike": [{_attr: {"w:val": true}}]},
]},
],
});
});
it("#doubleStrike", () => {
const style = new ParagraphStyle("myStyleId")
.doubleStrike();
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
{_attr: {"w:type": "paragraph", "w:styleId": "myStyleId"}},
{"w:pPr": []},
{"w:rPr": [
{"w:dstrike": [{_attr: {"w:val": true}}]},
]},
],
});
});
it("#subScript", () => {
const style = new ParagraphStyle("myStyleId")
.subScript();
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
{_attr: {"w:type": "paragraph", "w:styleId": "myStyleId"}},
{"w:pPr": []},
{"w:rPr": [
{"w:vertAlign": [{_attr: {"w:val": "subscript"}}]},
]},
],
});
});
it("#superScript", () => {
const style = new ParagraphStyle("myStyleId")
.superScript();
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
{_attr: {"w:type": "paragraph", "w:styleId": "myStyleId"}},
{"w:pPr": []},
{"w:rPr": [
{"w:vertAlign": [{_attr: {"w:val": "superscript"}}]},
]},
],
});
});
it("#font", () => {
const style = new ParagraphStyle("myStyleId")
.font("Times");
const tree = new Formatter().format(style);
expect(tree).to.deep.equal({
"w:style": [
{_attr: {"w:type": "paragraph", "w:styleId": "myStyleId"}},
{"w:pPr": []},
{"w:rPr": [{"w:rFonts": [{_attr: {"w:ascii": "Times", "w:hAnsi": "Times"}}]}]},
],
});
});
it("#bold", () => {
const style = new ParagraphStyle("myStyleId")
.bold();