From 418adca9f376bb49e2824239cb70da1c669f4ca6 Mon Sep 17 00:00:00 2001 From: Dolan Date: Fri, 13 Sep 2019 00:51:20 +0100 Subject: [PATCH 01/14] Declarative tables --- demo/11-declaritive-styles-2.ts | 38 ++- demo/20-table-cell-borders.ts | 99 ++++++- demo/24-images-to-table-cell.ts | 77 +++++- demo/31-tables.ts | 52 ++-- demo/32-merge-table-cells.ts | 182 +++++++----- demo/34-floating-tables.ts | 27 +- demo/36-image-to-table-cell.ts | 55 +++- demo/4-basic-table.ts | 28 +- demo/41-merge-table-cells-2.ts | 148 +++++++--- demo/43-images-to-table-cell-2.ts | 71 ++++- src/file/file.spec.ts | 13 +- src/file/footer-wrapper.spec.ts | 13 +- src/file/header-wrapper.spec.ts | 13 +- src/file/table/grid.ts | 6 +- src/file/table/table-cell/table-cell.ts | 122 +++++---- src/file/table/table-column.spec.ts | 53 ++-- src/file/table/table-column.ts | 16 +- src/file/table/table-row/table-row.spec.ts | 75 +++-- src/file/table/table-row/table-row.ts | 73 ++--- src/file/table/table.spec.ts | 304 +++++++++------------ src/file/table/table.ts | 49 +--- 21 files changed, 978 insertions(+), 536 deletions(-) diff --git a/demo/11-declaritive-styles-2.ts b/demo/11-declaritive-styles-2.ts index bb6428d518..c6780c440f 100644 --- a/demo/11-declaritive-styles-2.ts +++ b/demo/11-declaritive-styles-2.ts @@ -1,7 +1,7 @@ // Setting styles with JavaScript configuration // Import from 'docx' rather than '../build' if you install from npm import * as fs from "fs"; -import { AlignmentType, Document, Footer, HeadingLevel, Media, Packer, Paragraph, Table } from "../build"; +import { AlignmentType, Document, Footer, HeadingLevel, Media, Packer, Paragraph, Table, TableCell, TableRow } from "../build"; const doc = new Document(); @@ -81,13 +81,37 @@ doc.Styles.createParagraphStyle("ListParagraph", "List Paragraph") const image = Media.addImage(doc, fs.readFileSync("./demo/images/pizza.gif")); const table = new Table({ - rows: 4, - columns: 4, + rows: [ + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("Test cell 1.")], + }), + ], + }), + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("Test cell 2.")], + }), + ], + }), + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("Test cell 3.")], + }), + ], + }), + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("Test cell 4.")], + }), + ], + }), + ], }); -table - .getRow(0) - .getCell(0) - .add(new Paragraph("Pole No.")); const image1 = Media.addImage(doc, fs.readFileSync("./demo/images/pizza.gif")); const image2 = Media.addImage(doc, fs.readFileSync("./demo/images/pizza.gif")); diff --git a/demo/20-table-cell-borders.ts b/demo/20-table-cell-borders.ts index 13809a6164..028d9fdb04 100644 --- a/demo/20-table-cell-borders.ts +++ b/demo/20-table-cell-borders.ts @@ -1,23 +1,102 @@ // Add custom borders to table cell // Import from 'docx' rather than '../build' if you install from npm import * as fs from "fs"; -import { BorderStyle, Document, Packer, Paragraph, Table } from "../build"; +import { BorderStyle, Document, Packer, Paragraph, Table, TableCell, TableRow } from "../build"; const doc = new Document(); const table = new Table({ - rows: 4, - columns: 4, + rows: [ + new TableRow({ + children: [ + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + ], + }), + new TableRow({ + children: [ + new TableCell({ + children: [], + }), + new TableCell({ + children: [new Paragraph("Hello")], + borders: { + top: { + style: BorderStyle.DASH_DOT_STROKED, + size: 3, + color: "red", + }, + bottom: { + style: BorderStyle.DOUBLE, + size: 3, + color: "blue", + }, + left: { + style: BorderStyle.DASH_DOT_STROKED, + size: 3, + color: "green", + }, + right: { + style: BorderStyle.DASH_DOT_STROKED, + size: 3, + color: "#ff8000", + }, + }, + }), + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + ], + }), + new TableRow({ + children: [ + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + ], + }), + new TableRow({ + children: [ + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + ], + }), + ], }); doc.addSection({ children: [table] }); -table - .getCell(2, 2) - .add(new Paragraph("Hello")) - .Borders.addTopBorder(BorderStyle.DASH_DOT_STROKED, 3, "red") - .addBottomBorder(BorderStyle.DOUBLE, 3, "blue") - .addStartBorder(BorderStyle.DOT_DOT_DASH, 3, "green") - .addEndBorder(BorderStyle.DOT_DOT_DASH, 3, "#ff8000"); Packer.toBuffer(doc).then((buffer) => { fs.writeFileSync("My Document.docx", buffer); diff --git a/demo/24-images-to-table-cell.ts b/demo/24-images-to-table-cell.ts index 114be5491e..e37d1aa812 100644 --- a/demo/24-images-to-table-cell.ts +++ b/demo/24-images-to-table-cell.ts @@ -1,24 +1,85 @@ // Add image to table cell // Import from 'docx' rather than '../build' if you install from npm import * as fs from "fs"; -import { Document, Media, Packer, Paragraph, Table } from "../build"; +import { Document, Media, Packer, Paragraph, Table, TableCell, TableRow } from "../build"; const doc = new Document(); +const image = Media.addImage(doc, fs.readFileSync("./demo/images/image1.jpeg")); + const table = new Table({ - rows: 4, - columns: 4, + rows: [ + new TableRow({ + children: [ + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + ], + }), + new TableRow({ + children: [ + new TableCell({ + children: [], + }), + new TableCell({ + children: [new Paragraph(image)], + }), + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + ], + }), + new TableRow({ + children: [ + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + new TableCell({ + children: [new Paragraph("Hello")], + }), + new TableCell({ + children: [], + }), + ], + }), + new TableRow({ + children: [ + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + ], + }), + ], }); doc.addSection({ children: [table], }); -table.getCell(2, 2).add(new Paragraph("Hello")); - -const image = Media.addImage(doc, fs.readFileSync("./demo/images/image1.jpeg")); -table.getCell(1, 1).add(new Paragraph(image)); - Packer.toBuffer(doc).then((buffer) => { fs.writeFileSync("My Document.docx", buffer); }); diff --git a/demo/31-tables.ts b/demo/31-tables.ts index 050403bb99..7daec1506d 100644 --- a/demo/31-tables.ts +++ b/demo/31-tables.ts @@ -1,28 +1,48 @@ // Example of how you would create a table and add data to it // Import from 'docx' rather than '../build' if you install from npm import * as fs from "fs"; -import { Document, HeadingLevel, Packer, Paragraph, Table, VerticalAlign } from "../build"; +import { Document, HeadingLevel, Packer, Paragraph, Table, TableCell, TableRow, VerticalAlign } from "../build"; const doc = new Document(); const table = new Table({ - rows: 2, - columns: 2, + rows: [ + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph({}), new Paragraph({})], + verticalAlign: VerticalAlign.CENTER, + }), + new TableCell({ + children: [new Paragraph({}), new Paragraph({})], + verticalAlign: VerticalAlign.CENTER, + }), + ], + }), + new TableRow({ + children: [ + new TableCell({ + children: [ + new Paragraph({ + text: + "Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah", + heading: HeadingLevel.HEADING_1, + }), + ], + }), + new TableCell({ + children: [ + new Paragraph({ + text: "This text should be in the middle of the cell", + }), + ], + verticalAlign: VerticalAlign.CENTER, + }), + ], + }), + ], }); -table - .getCell(1, 1) - .add(new Paragraph("This text should be in the middle of the cell")) - .setVerticalAlign(VerticalAlign.CENTER); - -table.getCell(1, 0).add( - new Paragraph({ - text: - "Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah", - heading: HeadingLevel.HEADING_1, - }), -); - doc.addSection({ children: [table], }); diff --git a/demo/32-merge-table-cells.ts b/demo/32-merge-table-cells.ts index cd850067e5..55a9181281 100644 --- a/demo/32-merge-table-cells.ts +++ b/demo/32-merge-table-cells.ts @@ -1,40 +1,118 @@ // Example of how you would merge cells together // Import from 'docx' rather than '../build' if you install from npm import * as fs from "fs"; -import { Document, HeadingLevel, Packer, Paragraph, ShadingType, Table, WidthType } from "../build"; +import { Document, HeadingLevel, Packer, Paragraph, ShadingType, Table, TableCell, TableRow, WidthType } from "../build"; const doc = new Document(); const table = new Table({ - rows: 2, - columns: 2, + rows: [ + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("Hello")], + columnSpan: 2, + }), + ], + }), + new TableRow({ + children: [ + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + ], + }), + ], }); -table.getCell(0, 0).add(new Paragraph("Hello")); -table.getRow(0).mergeCells(0, 1); - const table2 = new Table({ - rows: 2, - columns: 3, + rows: [ + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("World")], + margins: { + top: 1000, + bottom: 1000, + left: 1000, + right: 1000, + }, + columnSpan: 3, + }), + ], + }), + new TableRow({ + children: [ + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + ], + }), + ], width: 100, widthUnitType: WidthType.AUTO, columnWidths: [1000, 1000, 1000], }); -table2 - .getCell(0, 0) - .add(new Paragraph("World")) - .setMargins({ - top: 1000, - bottom: 1000, - left: 1000, - right: 1000, - }); -table.getRow(0).mergeCells(0, 2); - const table3 = new Table({ - rows: 2, - columns: 4, + rows: [ + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("Foo")], + }), + new TableCell({ + children: [new Paragraph("v")], + columnSpan: 3, + }), + ], + }), + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("Bar1")], + shading: { + fill: "b79c2f", + val: ShadingType.REVERSE_DIAGONAL_STRIPE, + color: "auto", + }, + }), + new TableCell({ + children: [new Paragraph("Bar2")], + shading: { + fill: "42c5f4", + val: ShadingType.PERCENT_95, + color: "auto", + }, + }), + new TableCell({ + children: [new Paragraph("Bar3")], + shading: { + fill: "880aa8", + val: ShadingType.PERCENT_10, + color: "e2df0b", + }, + }), + new TableCell({ + children: [new Paragraph("Bar4")], + shading: { + fill: "FF0000", + val: ShadingType.CLEAR, + color: "auto", + }, + }), + ], + }), + ], width: 7000, widthUnitType: WidthType.DXA, margins: { @@ -45,47 +123,29 @@ const table3 = new Table({ }, }); -table3.getCell(0, 0).add(new Paragraph("Foo")); -table3.getCell(0, 1).add(new Paragraph("v")); - -table3 - .getCell(1, 0) - .add(new Paragraph("Bar1")) - .setShading({ - fill: "b79c2f", - val: ShadingType.REVERSE_DIAGONAL_STRIPE, - color: "auto", - }); -table3 - .getCell(1, 1) - .add(new Paragraph("Bar2")) - .setShading({ - fill: "42c5f4", - val: ShadingType.PERCENT_95, - color: "auto", - }); -table3 - .getCell(1, 2) - .add(new Paragraph("Bar3")) - .setShading({ - fill: "880aa8", - val: ShadingType.PERCENT_10, - color: "e2df0b", - }); -table3 - .getCell(1, 3) - .add(new Paragraph("Bar4")) - .setShading({ - fill: "FF0000", - val: ShadingType.CLEAR, - color: "auto", - }); - -table3.getRow(0).mergeCells(0, 3); - const table4 = new Table({ - rows: 2, - columns: 2, + rows: [ + new TableRow({ + children: [ + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + ], + }), + new TableRow({ + children: [ + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + ], + }), + ], width: 100, widthUnitType: WidthType.PERCENTAGE, }); diff --git a/demo/34-floating-tables.ts b/demo/34-floating-tables.ts index c5b8a18cf8..4a20ae2eee 100644 --- a/demo/34-floating-tables.ts +++ b/demo/34-floating-tables.ts @@ -9,15 +9,35 @@ import { RelativeVerticalPosition, Table, TableAnchorType, + TableCell, TableLayoutType, + TableRow, WidthType, } from "../build"; const doc = new Document(); const table = new Table({ - rows: 2, - columns: 2, + rows: [ + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("Hello")], + columnSpan: 2, + }), + ], + }), + new TableRow({ + children: [ + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + ], + }), + ], float: { horizontalAnchor: TableAnchorType.MARGIN, verticalAnchor: TableAnchorType.MARGIN, @@ -29,9 +49,6 @@ const table = new Table({ layout: TableLayoutType.FIXED, }); -table.getCell(0, 0).add(new Paragraph("Hello")); -table.getRow(0).mergeCells(0, 1); - doc.addSection({ children: [table], }); diff --git a/demo/36-image-to-table-cell.ts b/demo/36-image-to-table-cell.ts index 1c7897c558..a0d87230a2 100644 --- a/demo/36-image-to-table-cell.ts +++ b/demo/36-image-to-table-cell.ts @@ -1,16 +1,61 @@ -// Add image to table cell +// Add image to table cell in a header and body // Import from 'docx' rather than '../build' if you install from npm import * as fs from "fs"; -import { Document, Header, Media, Packer, Paragraph, Table } from "../build"; +import { Document, Header, Media, Packer, Paragraph, Table, TableCell, TableRow } from "../build"; const doc = new Document(); const image = Media.addImage(doc, fs.readFileSync("./demo/images/image1.jpeg")); const table = new Table({ - rows: 2, - columns: 2, + rows: [ + new TableRow({ + children: [ + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + ], + }), + new TableRow({ + children: [ + new TableCell({ + children: [], + }), + new TableCell({ + children: [new Paragraph(image)], + }), + ], + }), + new TableRow({ + children: [ + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + ], + }), + new TableRow({ + children: [ + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + ], + }), + ], }); -table.getCell(1, 1).add(new Paragraph(image)); // Adding same table in the body and in the header doc.addSection({ diff --git a/demo/4-basic-table.ts b/demo/4-basic-table.ts index 78367caab9..593fe821a5 100644 --- a/demo/4-basic-table.ts +++ b/demo/4-basic-table.ts @@ -1,17 +1,35 @@ // Example of how you would create a table and add data to it // Import from 'docx' rather than '../build' if you install from npm import * as fs from "fs"; -import { Document, Packer, Paragraph, Table } from "../build"; +import { Document, Packer, Paragraph, Table, TableCell, TableRow } from "../build"; const doc = new Document(); const table = new Table({ - rows: 4, - columns: 4, + rows: [ + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("Hello")], + }), + new TableCell({ + children: [], + }), + ], + }), + new TableRow({ + children: [ + new TableCell({ + children: [], + }), + new TableCell({ + children: [new Paragraph("World")], + }), + ], + }), + ], }); -table.getCell(2, 2).add(new Paragraph("Hello")); - doc.addSection({ children: [table], }); diff --git a/demo/41-merge-table-cells-2.ts b/demo/41-merge-table-cells-2.ts index 1801eb4a6e..50141a1a7a 100644 --- a/demo/41-merge-table-cells-2.ts +++ b/demo/41-merge-table-cells-2.ts @@ -1,50 +1,122 @@ // Multiple cells merging in the same table // Import from 'docx' rather than '../build' if you install from npm import * as fs from "fs"; -import { Document, Packer, Paragraph, Table } from "../build"; +import { Document, Packer, Paragraph, Table, TableCell, TableRow } from "../build"; const doc = new Document(); const table = new Table({ - rows: 13, - columns: 6, + rows: [ + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("0,0")], + }), + new TableCell({ + children: [new Paragraph("0,1")], + columnSpan: 2, + }), + new TableCell({ + children: [new Paragraph("0,3")], + }), + new TableCell({ + children: [new Paragraph("0,4")], + columnSpan: 2, + }), + ], + }), + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("1,0")], + columnSpan: 2, + }), + new TableCell({ + children: [new Paragraph("1,2")], + columnSpan: 2, + }), + new TableCell({ + children: [new Paragraph("1,4")], + columnSpan: 2, + }), + ], + }), + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("2,0")], + }), + new TableCell({ + children: [new Paragraph("2,1")], + columnSpan: 2, + }), + new TableCell({ + children: [new Paragraph("2,3")], + }), + new TableCell({ + children: [new Paragraph("2,4")], + columnSpan: 2, + }), + ], + }), + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("3,0")], + }), + new TableCell({ + children: [new Paragraph("3,1")], + }), + new TableCell({ + children: [new Paragraph("3,2")], + }), + new TableCell({ + children: [new Paragraph("3,3")], + }), + new TableCell({ + children: [new Paragraph("3,4")], + }), + new TableCell({ + children: [new Paragraph("3,5")], + }), + ], + }), + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("4,0")], + columnSpan: 5, + }), + new TableCell({ + children: [new Paragraph("4,5")], + }), + ], + }), + new TableRow({ + children: [ + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + ], + }), + ], }); -let row = 0; -table.getCell(row, 0).add(new Paragraph("0,0")); -table.getCell(row, 1).add(new Paragraph("0,1")); -table.getCell(row, 3).add(new Paragraph("0,3")); -table.getCell(row, 4).add(new Paragraph("0,4")); -table.getRow(row).mergeCells(4, 5); -table.getRow(row).mergeCells(1, 2); -row = 1; -table.getCell(row, 0).add(new Paragraph("1,0")); -table.getCell(row, 2).add(new Paragraph("1,2")); -table.getCell(row, 4).add(new Paragraph("1,4")); -table.getRow(row).mergeCells(4, 5); -table.getRow(row).mergeCells(2, 3); -table.getRow(row).mergeCells(0, 1); - -row = 2; -table.getCell(row, 0).add(new Paragraph("2,0")); -table.getCell(row, 1).add(new Paragraph("2,1")); -table.getCell(row, 2).add(new Paragraph("2,2")); -table.getCell(row, 3).add(new Paragraph("2,3")); -table.getCell(row, 4).add(new Paragraph("2,4")); -table.getRow(row).mergeCells(4, 5); -table.getRow(row).mergeCells(1, 2); -row = 3; -table.getCell(row, 0).add(new Paragraph("3,0")); -table.getCell(row, 1).add(new Paragraph("3,1")); -table.getCell(row, 2).add(new Paragraph("3,2")); -table.getCell(row, 3).add(new Paragraph("3,3")); -table.getCell(row, 4).add(new Paragraph("3,4")); -table.getCell(row, 5).add(new Paragraph("3,5")); -row = 4; -table.getCell(row, 0).add(new Paragraph("4,0")); -table.getCell(row, 5).add(new Paragraph("4,5")); -table.getRow(row).mergeCells(0, 4); - doc.addSection({ children: [table], }); diff --git a/demo/43-images-to-table-cell-2.ts b/demo/43-images-to-table-cell-2.ts index 90d6322bb7..1840c9382c 100644 --- a/demo/43-images-to-table-cell-2.ts +++ b/demo/43-images-to-table-cell-2.ts @@ -1,16 +1,79 @@ // Add image to table cell // Import from 'docx' rather than '../build' if you install from npm import * as fs from "fs"; -import { Document, Packer, Paragraph, Table } from "../build"; +import { Document, Packer, Paragraph, Table, TableCell, TableRow } from "../build"; const doc = new Document(); const table = new Table({ - rows: 4, - columns: 4, + rows: [ + new TableRow({ + children: [ + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + ], + }), + new TableRow({ + children: [ + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + ], + }), + new TableRow({ + children: [ + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + new TableCell({ + children: [new Paragraph("Hello")], + }), + new TableCell({ + children: [], + }), + ], + }), + new TableRow({ + children: [ + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + ], + }), + ], }); -table.getCell(2, 2).add(new Paragraph("Hello")); table.getColumn(3).mergeCells(1, 2); doc.addSection({ diff --git a/src/file/file.spec.ts b/src/file/file.spec.ts index e8c081d1ec..59b42e009c 100644 --- a/src/file/file.spec.ts +++ b/src/file/file.spec.ts @@ -6,7 +6,7 @@ import { Formatter } from "export/formatter"; import { File } from "./file"; import { Footer, Header } from "./header"; import { Paragraph } from "./paragraph"; -import { Table } from "./table"; +import { Table, TableCell, TableRow } from "./table"; import { TableOfContents } from "./table-of-contents"; describe("File", () => { @@ -108,8 +108,15 @@ describe("File", () => { file.addSection({ children: [ new Table({ - rows: 1, - columns: 1, + rows: [ + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("hello")], + }), + ], + }), + ], }), ], }); diff --git a/src/file/footer-wrapper.spec.ts b/src/file/footer-wrapper.spec.ts index 3f6fb01b82..899bd22e70 100644 --- a/src/file/footer-wrapper.spec.ts +++ b/src/file/footer-wrapper.spec.ts @@ -4,7 +4,7 @@ import * as sinon from "sinon"; import { FooterWrapper } from "./footer-wrapper"; import { Media } from "./media"; import { Paragraph } from "./paragraph"; -import { Table } from "./table"; +import { Table, TableCell, TableRow } from "./table"; describe("FooterWrapper", () => { describe("#add", () => { @@ -21,8 +21,15 @@ describe("FooterWrapper", () => { const spy = sinon.spy(file.Footer, "add"); file.add( new Table({ - rows: 1, - columns: 1, + rows: [ + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("hello")], + }), + ], + }), + ], }), ); diff --git a/src/file/header-wrapper.spec.ts b/src/file/header-wrapper.spec.ts index d07473f03a..7c3073a094 100644 --- a/src/file/header-wrapper.spec.ts +++ b/src/file/header-wrapper.spec.ts @@ -4,7 +4,7 @@ import * as sinon from "sinon"; import { HeaderWrapper } from "./header-wrapper"; import { Media } from "./media"; import { Paragraph } from "./paragraph"; -import { Table } from "./table"; +import { Table, TableCell, TableRow } from "./table"; describe("HeaderWrapper", () => { describe("#add", () => { @@ -21,8 +21,15 @@ describe("HeaderWrapper", () => { const spy = sinon.spy(wrapper.Header, "add"); wrapper.add( new Table({ - rows: 1, - columns: 1, + rows: [ + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("hello")], + }), + ], + }), + ], }), ); diff --git a/src/file/table/grid.ts b/src/file/table/grid.ts index b91f6ac9d4..5ce6486fcc 100644 --- a/src/file/table/grid.ts +++ b/src/file/table/grid.ts @@ -2,9 +2,11 @@ import { XmlAttributeComponent, XmlComponent } from "file/xml-components"; export class TableGrid extends XmlComponent { - constructor(cols: number[]) { + constructor(widths: number[]) { super("w:tblGrid"); - cols.forEach((col) => this.root.push(new GridCol(col))); + for (const width of widths) { + this.root.push(new GridCol(width)); + } } } diff --git a/src/file/table/table-cell/table-cell.ts b/src/file/table/table-cell/table-cell.ts index 1c546ab934..05416b34d5 100644 --- a/src/file/table/table-cell/table-cell.ts +++ b/src/file/table/table-cell/table-cell.ts @@ -1,77 +1,107 @@ // http://officeopenxml.com/WPtableGrid.php import { Paragraph } from "file/paragraph"; +import { BorderStyle } from "file/styles"; import { IXmlableObject, XmlComponent } from "file/xml-components"; import { ITableShadingAttributesProperties } from "../shading"; import { Table } from "../table"; import { ITableCellMarginOptions } from "./cell-margin/table-cell-margins"; -import { TableCellBorders, VerticalAlign, VMergeType } from "./table-cell-components"; +import { VerticalAlign, VMergeType } from "./table-cell-components"; import { TableCellProperties } from "./table-cell-properties"; export interface ITableCellOptions { readonly shading?: ITableShadingAttributesProperties; + readonly margins?: ITableCellMarginOptions; + readonly verticalAlign?: VerticalAlign; + readonly verticalMerge?: VMergeType; + readonly columnSpan?: number; + readonly borders?: { + readonly top?: { + readonly style: BorderStyle; + readonly size: number; + readonly color: string; + }; + readonly bottom?: { + readonly style: BorderStyle; + readonly size: number; + readonly color: string; + }; + readonly left?: { + readonly style: BorderStyle; + readonly size: number; + readonly color: string; + }; + readonly right?: { + readonly style: BorderStyle; + readonly size: number; + readonly color: string; + }; + }; + readonly children: Array; } export class TableCell extends XmlComponent { private readonly properties: TableCellProperties; - constructor() { + constructor(readonly options: ITableCellOptions) { super("w:tc"); this.properties = new TableCellProperties(); this.root.push(this.properties); - } - public add(item: Paragraph | Table): TableCell { - this.root.push(item); + for (const child of options.children) { + this.root.push(child); + } - return this; + if (options.verticalAlign) { + this.properties.setVerticalAlign(options.verticalAlign); + } + + if (options.verticalMerge) { + this.properties.addVerticalMerge(options.verticalMerge); + } + + if (options.margins) { + this.properties.addMargins(options.margins); + } + + if (options.shading) { + this.properties.setShading(options.shading); + } + + if (options.columnSpan) { + this.properties.addGridSpan(options.columnSpan); + } + + if (options.borders) { + if (options.borders.top) { + this.properties.Borders.addTopBorder(options.borders.top.style, options.borders.top.size, options.borders.top.color); + } + if (options.borders.bottom) { + this.properties.Borders.addBottomBorder( + options.borders.bottom.style, + options.borders.bottom.size, + options.borders.bottom.color, + ); + } + if (options.borders.left) { + this.properties.Borders.addLeftBorder(options.borders.left.style, options.borders.left.size, options.borders.left.color); + } + if (options.borders.right) { + this.properties.Borders.addRightBorder( + options.borders.right.style, + options.borders.right.size, + options.borders.right.color, + ); + } + } } public prepForXml(): IXmlableObject | undefined { // Cells must end with a paragraph if (!(this.root[this.root.length - 1] instanceof Paragraph)) { - const para = new Paragraph({}); - this.add(para); + this.root.push(new Paragraph({})); } return super.prepForXml(); } - - public setVerticalAlign(type: VerticalAlign): TableCell { - this.properties.setVerticalAlign(type); - - return this; - } - - public addGridSpan(cellSpan: number): TableCell { - this.properties.addGridSpan(cellSpan); - - return this; - } - - public addVerticalMerge(type: VMergeType): TableCell { - this.properties.addVerticalMerge(type); - - return this; - } - - public setMargins(margins: ITableCellMarginOptions): TableCell { - this.properties.addMargins(margins); - - return this; - } - - public setShading(attrs: ITableShadingAttributesProperties): TableCell { - this.properties.setShading(attrs); - - return this; - } - - public get Borders(): TableCellBorders { - return this.properties.Borders; - } - - public get Properties(): TableCellProperties { - return this.properties; - } } diff --git a/src/file/table/table-column.spec.ts b/src/file/table/table-column.spec.ts index aa031423a3..50e9cb8aa4 100644 --- a/src/file/table/table-column.spec.ts +++ b/src/file/table/table-column.spec.ts @@ -1,16 +1,25 @@ import { expect } from "chai"; -import { Formatter } from "export/formatter"; +// import { Formatter } from "export/formatter"; +// import { EMPTY_OBJECT } from "file/xml-components"; import { TableCell } from "./table-cell"; import { TableColumn } from "./table-column"; -import { EMPTY_OBJECT } from "file/xml-components"; - describe("TableColumn", () => { let cells: TableCell[]; beforeEach(() => { - cells = [new TableCell(), new TableCell(), new TableCell()]; + cells = [ + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + ]; }); describe("#getCell", () => { @@ -32,25 +41,25 @@ describe("TableColumn", () => { }); }); - describe("#mergeCells", () => { - it("should add vMerge to correct cells", () => { - const tableColumn = new TableColumn(cells); - tableColumn.mergeCells(0, 2); + // describe("#mergeCells", () => { + // it("should add vMerge to correct cells", () => { + // const tableColumn = new TableColumn(cells); + // tableColumn.mergeCells(0, 2); - const tree = new Formatter().format(cells[0]); - expect(tree).to.deep.equal({ - "w:tc": [{ "w:tcPr": [{ "w:vMerge": { _attr: { "w:val": "restart" } } }] }, { "w:p": EMPTY_OBJECT }], - }); + // const tree = new Formatter().format(cells[0]); + // expect(tree).to.deep.equal({ + // "w:tc": [{ "w:tcPr": [{ "w:vMerge": { _attr: { "w:val": "restart" } } }] }, { "w:p": EMPTY_OBJECT }], + // }); - const tree2 = new Formatter().format(cells[1]); - expect(tree2).to.deep.equal({ - "w:tc": [{ "w:tcPr": [{ "w:vMerge": { _attr: { "w:val": "continue" } } }] }, { "w:p": EMPTY_OBJECT }], - }); + // const tree2 = new Formatter().format(cells[1]); + // expect(tree2).to.deep.equal({ + // "w:tc": [{ "w:tcPr": [{ "w:vMerge": { _attr: { "w:val": "continue" } } }] }, { "w:p": EMPTY_OBJECT }], + // }); - const tree3 = new Formatter().format(cells[2]); - expect(tree3).to.deep.equal({ - "w:tc": [{ "w:tcPr": [{ "w:vMerge": { _attr: { "w:val": "continue" } } }] }, { "w:p": EMPTY_OBJECT }], - }); - }); - }); + // const tree3 = new Formatter().format(cells[2]); + // expect(tree3).to.deep.equal({ + // "w:tc": [{ "w:tcPr": [{ "w:vMerge": { _attr: { "w:val": "continue" } } }] }, { "w:p": EMPTY_OBJECT }], + // }); + // }); + // }); }); diff --git a/src/file/table/table-column.ts b/src/file/table/table-column.ts index 5752bde472..fc8c2cc240 100644 --- a/src/file/table/table-column.ts +++ b/src/file/table/table-column.ts @@ -1,4 +1,4 @@ -import { TableCell, VMergeType } from "./table-cell"; +import { TableCell } from "./table-cell"; export class TableColumn { constructor(private readonly cells: TableCell[]) {} @@ -13,13 +13,13 @@ export class TableColumn { return cell; } - public mergeCells(startIndex: number, endIndex: number): TableCell { - this.cells[startIndex].addVerticalMerge(VMergeType.RESTART); + // public mergeCells(startIndex: number, endIndex: number): TableCell { + // this.cells[startIndex].addVerticalMerge(VMergeType.RESTART); - for (let i = startIndex + 1; i <= endIndex; i++) { - this.cells[i].addVerticalMerge(VMergeType.CONTINUE); - } + // for (let i = startIndex + 1; i <= endIndex; i++) { + // this.cells[i].addVerticalMerge(VMergeType.CONTINUE); + // } - return this.cells[startIndex]; - } + // return this.cells[startIndex]; + // } } diff --git a/src/file/table/table-row/table-row.spec.ts b/src/file/table/table-row/table-row.spec.ts index 3e9a479949..3c732c6217 100644 --- a/src/file/table/table-row/table-row.spec.ts +++ b/src/file/table/table-row/table-row.spec.ts @@ -10,7 +10,9 @@ import { TableRow } from "./table-row"; describe("TableRow", () => { describe("#constructor", () => { it("should create with no cells", () => { - const tableRow = new TableRow([]); + const tableRow = new TableRow({ + children: [], + }); const tree = new Formatter().format(tableRow); expect(tree).to.deep.equal({ "w:tr": EMPTY_OBJECT, @@ -18,7 +20,13 @@ describe("TableRow", () => { }); it("should create with one cell", () => { - const tableRow = new TableRow([new TableCell()]); + const tableRow = new TableRow({ + children: [ + new TableCell({ + children: [], + }), + ], + }); const tree = new Formatter().format(tableRow); expect(tree).to.deep.equal({ "w:tr": [ @@ -32,46 +40,15 @@ describe("TableRow", () => { ], }); }); - }); - describe("#getCell", () => { - it("should get the cell", () => { - const cell = new TableCell(); - const tableRow = new TableRow([cell]); - - expect(tableRow.getCell(0)).to.equal(cell); - }); - - it("should throw an error if index is out of bounds", () => { - const cell = new TableCell(); - const tableRow = new TableRow([cell]); - - expect(() => tableRow.getCell(1)).to.throw(); - }); - }); - - describe("#addGridSpan", () => { - it("should merge the cell", () => { - const tableRow = new TableRow([new TableCell(), new TableCell()]); - - tableRow.addGridSpan(0, 2); - expect(() => tableRow.getCell(1)).to.throw(); - }); - }); - - describe("#mergeCells", () => { - it("should merge the cell", () => { - const tableRow = new TableRow([new TableCell(), new TableCell()]); - - tableRow.mergeCells(0, 1); - expect(() => tableRow.getCell(1)).to.throw(); - }); - }); - - describe("#setHeight", () => { it("should set row height", () => { - const tableRow = new TableRow([]); - tableRow.setHeight(100, HeightRule.EXACT); + const tableRow = new TableRow({ + children: [], + height: { + height: 100, + rule: HeightRule.EXACT, + }, + }); const tree = new Formatter().format(tableRow); expect(tree).to.deep.equal({ "w:tr": [ @@ -91,4 +68,22 @@ describe("TableRow", () => { }); }); }); + + // describe("#mergeCells", () => { + // it("should merge the cell", () => { + // const tableRow = new TableRow({ + // children: [ + // new TableCell({ + // children: [], + // }), + // new TableCell({ + // children: [], + // }), + // ], + // }); + + // tableRow.mergeCells(0, 1); + // expect(() => tableRow.getCell(1)).to.throw(); + // }); + // }); }); diff --git a/src/file/table/table-row/table-row.ts b/src/file/table/table-row/table-row.ts index f811392348..df47bf70bf 100644 --- a/src/file/table/table-row/table-row.ts +++ b/src/file/table/table-row/table-row.ts @@ -3,56 +3,57 @@ import { XmlComponent } from "file/xml-components"; import { TableCell } from "../table-cell"; import { TableRowProperties } from "./table-row-properties"; +export interface ITableRowOptions { + readonly cantSplit?: boolean; + readonly tableHeader?: boolean; + readonly height?: { + readonly height: number; + readonly rule: HeightRule; + }; + readonly children: TableCell[]; +} + export class TableRow extends XmlComponent { private readonly properties: TableRowProperties; - constructor(private readonly cells: TableCell[]) { + constructor(private readonly options: ITableRowOptions) { super("w:tr"); this.properties = new TableRowProperties(); this.root.push(this.properties); - cells.forEach((c) => this.root.push(c)); - } - public getCell(index: number): TableCell { - const cell = this.cells[index]; - - if (!cell) { - throw Error("Index out of bounds when trying to get cell on row"); + for (const child of options.children) { + this.root.push(child); } - return cell; + if (options.cantSplit) { + this.properties.setCantSplit(); + } + + if (options.tableHeader) { + this.properties.setTableHeader(); + } + + if (options.height) { + this.properties.setHeight(options.height.height, options.height.rule); + } } - public addGridSpan(index: number, cellSpan: number): TableCell { - const remainCell = this.cells[index]; - remainCell.addGridSpan(cellSpan); - this.cells.splice(index + 1, cellSpan - 1); - this.root.splice(index + 2, cellSpan - 1); - - return remainCell; + public get CellCount(): number { + return this.options.children.length; } - public mergeCells(startIndex: number, endIndex: number): TableCell { - const cellSpan = endIndex - startIndex + 1; + // public mergeCells(startIndex: number, endIndex: number): TableCell { + // const cellSpan = endIndex - startIndex + 1; - return this.addGridSpan(startIndex, cellSpan); - } + // return this.addGridSpan(startIndex, cellSpan); + // } - public setCantSplit(): TableRow { - this.properties.setCantSplit(); + // private addGridSpan(index: number, cellSpan: number): TableCell { + // const remainCell = this.options.children[index]; + // remainCell.addGridSpan(cellSpan); + // this.options.children.splice(index + 1, cellSpan - 1); + // this.root.splice(index + 2, cellSpan - 1); - return this; - } - - public setTableHeader(): TableRow { - this.properties.setTableHeader(); - - return this; - } - - public setHeight(height: number, rule: HeightRule): TableRow { - this.properties.setHeight(height, rule); - - return this; - } + // return remainCell; + // } } diff --git a/src/file/table/table.spec.ts b/src/file/table/table.spec.ts index 7c93c5fc8e..efbf2ebb24 100644 --- a/src/file/table/table.spec.ts +++ b/src/file/table/table.spec.ts @@ -9,7 +9,9 @@ import { Table } from "./table"; import { RelativeHorizontalPosition, RelativeVerticalPosition, TableAnchorType } from "./table-properties"; import { EMPTY_OBJECT } from "file/xml-components"; +import { TableCell } from "./table-cell"; import { TableLayoutType } from "./table-properties/table-layout"; +import { TableRow } from "./table-row"; const DEFAULT_TABLE_PROPERTIES = { "w:tblCellMar": [ @@ -118,8 +120,38 @@ describe("Table", () => { describe("#constructor", () => { it("creates a table with the correct number of rows and columns", () => { const table = new Table({ - rows: 3, - columns: 2, + rows: [ + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("hello")], + }), + new TableCell({ + children: [new Paragraph("hello")], + }), + ], + }), + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("hello")], + }), + new TableCell({ + children: [new Paragraph("hello")], + }), + ], + }), + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("hello")], + }), + new TableCell({ + children: [new Paragraph("hello")], + }), + ], + }), + ], }); const tree = new Formatter().format(table); const cell = { "w:tc": [{ "w:p": EMPTY_OBJECT }] }; @@ -138,8 +170,15 @@ describe("Table", () => { it("sets the table to fixed width layout", () => { const table = new Table({ - rows: 1, - columns: 1, + rows: [ + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("hello")], + }), + ], + }), + ], layout: TableLayoutType.FIXED, }); const tree = new Formatter().format(table); @@ -153,128 +192,19 @@ describe("Table", () => { }); }); - describe("#getRow and Row#getCell", () => { - const table = new Table({ - rows: 2, - columns: 2, - }); - - it("should return the correct row", () => { - table - .getRow(0) - .getCell(0) - .add(new Paragraph("A1")); - table - .getRow(0) - .getCell(1) - .add(new Paragraph("B1")); - table - .getRow(1) - .getCell(0) - .add(new Paragraph("A2")); - table - .getRow(1) - .getCell(1) - .add(new Paragraph("B2")); - const tree = new Formatter().format(table); - const cell = (c) => ({ - "w:tc": [ - { - "w:p": [{ "w:r": [{ "w:t": [{ _attr: { "xml:space": "preserve" } }, c] }] }], - }, - ], - }); - expect(tree).to.deep.equal({ - "w:tbl": [ - { "w:tblPr": [DEFAULT_TABLE_PROPERTIES, BORDERS, WIDTHS] }, - { - "w:tblGrid": [{ "w:gridCol": { _attr: { "w:w": 100 } } }, { "w:gridCol": { _attr: { "w:w": 100 } } }], - }, - { "w:tr": [cell("A1"), cell("B1")] }, - { "w:tr": [cell("A2"), cell("B2")] }, - ], - }); - }); - - it("throws an exception if index is out of bounds", () => { - expect(() => table.getCell(9, 9)).to.throw(); - }); - }); - - describe("#getColumn", () => { - const table = new Table({ - rows: 2, - columns: 2, - }); - - it("should get correct cell", () => { - const column = table.getColumn(0); - - expect(column.getCell(0)).to.equal(table.getCell(0, 0)); - expect(column.getCell(1)).to.equal(table.getCell(1, 0)); - }); - }); - - describe("#getCell", () => { - it("should returns the correct cell", () => { - const table = new Table({ - rows: 2, - columns: 2, - }); - table.getCell(0, 0).add(new Paragraph("A1")); - table.getCell(0, 1).add(new Paragraph("B1")); - table.getCell(1, 0).add(new Paragraph("A2")); - table.getCell(1, 1).add(new Paragraph("B2")); - const tree = new Formatter().format(table); - const cell = (c) => ({ - "w:tc": [ - { - "w:p": [{ "w:r": [{ "w:t": [{ _attr: { "xml:space": "preserve" } }, c] }] }], - }, - ], - }); - expect(tree).to.deep.equal({ - "w:tbl": [ - { "w:tblPr": [DEFAULT_TABLE_PROPERTIES, BORDERS, WIDTHS] }, - { - "w:tblGrid": [{ "w:gridCol": { _attr: { "w:w": 100 } } }, { "w:gridCol": { _attr: { "w:w": 100 } } }], - }, - { "w:tr": [cell("A1"), cell("B1")] }, - { "w:tr": [cell("A2"), cell("B2")] }, - ], - }); - }); - }); - - // describe("#setWidth", () => { - // it("should set the preferred width on the table", () => { - // const table = new Table({rows: 1,columns: 1,}).setWidth(1000, WidthType.PERCENTAGE); - // 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": [DEFAULT_TABLE_PROPERTIES, { "w:tblW": { _attr: { "w:type": "pct", "w:w": "1000%" } } }], - // }); - // }); - - // it("sets the preferred width on the table with a default of AUTO", () => { - // const table = new Table({rows: 1,columns: 1,}).setWidth(1000); - // const tree = new Formatter().format(table); - - // expect(tree["w:tbl"][0]).to.deep.equal({ - // "w:tblPr": [DEFAULT_TABLE_PROPERTIES, { "w:tblW": { _attr: { "w:type": "auto", "w:w": 1000 } } }], - // }); - // }); - // }); - describe("Cell", () => { describe("#prepForXml", () => { it("inserts a paragraph at the end of the cell if it is empty", () => { const table = new Table({ - rows: 1, - columns: 1, + rows: [ + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("hello")], + }), + ], + }), + ], }); const tree = new Formatter().format(table); expect(tree) @@ -290,64 +220,92 @@ describe("Table", () => { }); }); - it("inserts a paragraph at the end of the cell even if it has a child table", () => { - const parentTable = new Table({ - rows: 1, - columns: 1, - }); - parentTable.getCell(0, 0).add( - new Table({ - rows: 1, - columns: 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": EMPTY_OBJECT, - }); - }); + // it("inserts a paragraph at the end of the cell even if it has a child table", () => { + // const table = new Table({ + // rows: [ + // new TableRow({ + // children: [ + // new TableCell({ + // children: [new Paragraph("hello")], + // }), + // ], + // }), + // ], + // }); + // table.getCell(0, 0).add( + // new Table({ + // rows: [ + // new TableRow({ + // children: [ + // new TableCell({ + // children: [new Paragraph("hello")], + // }), + // ], + // }), + // ], + // }), + // ); + // 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); + // 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": EMPTY_OBJECT, + // }); + // }); - it("does not insert a paragraph if it already ends with one", () => { - const parentTable = new Table({ - rows: 1, - columns: 1, - }); - parentTable.getCell(0, 0).add(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:p": [{ "w:r": [{ "w:t": [{ _attr: { "xml:space": "preserve" } }, "Hello"] }] }], - }, - ], - }); - }); + // it("does not insert a paragraph if it already ends with one", () => { + // const table = new Table({ + // rows: [ + // new TableRow({ + // children: [ + // new TableCell({ + // children: [new Paragraph("hello")], + // }), + // ], + // }), + // ], + // }); + // table.getCell(0, 0).add(new Paragraph("Hello")); + // 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:p": [{ "w:r": [{ "w:t": [{ _attr: { "xml:space": "preserve" } }, "Hello"] }] }], + // }, + // ], + // }); + // }); }); }); describe("#float", () => { it("sets the table float properties", () => { const table = new Table({ - rows: 1, - columns: 1, + rows: [ + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("hello")], + }), + ], + }), + ], float: { horizontalAnchor: TableAnchorType.MARGIN, verticalAnchor: TableAnchorType.PAGE, diff --git a/src/file/table/table.ts b/src/file/table/table.ts index c56bc4d62c..ea8e64d72c 100644 --- a/src/file/table/table.ts +++ b/src/file/table/table.ts @@ -1,12 +1,11 @@ // http://officeopenxml.com/WPtableGrid.php import { XmlComponent } from "file/xml-components"; - import { TableGrid } from "./grid"; -import { TableCell, WidthType } from "./table-cell"; -import { TableColumn } from "./table-column"; +import { WidthType } from "./table-cell"; import { ITableFloatOptions, TableProperties } from "./table-properties"; import { TableLayoutType } from "./table-properties/table-layout"; import { TableRow } from "./table-row"; + /* 0-width columns don't get rendered correctly, so we need to give them some value. A reasonable default would be @@ -18,8 +17,7 @@ import { TableRow } from "./table-row"; algorithm will expand columns to fit its content */ export interface ITableOptions { - readonly rows: number; - readonly columns: number; + readonly rows: TableRow[]; readonly width?: number; readonly widthUnitType?: WidthType; readonly columnWidths?: number[]; @@ -36,14 +34,12 @@ export interface ITableOptions { export class Table extends XmlComponent { private readonly properties: TableProperties; - private readonly rows: TableRow[]; constructor({ rows, - columns, width = 100, widthUnitType = WidthType.AUTO, - columnWidths = Array(columns).fill(100), + columnWidths = Array(Math.max(...rows.map((row) => row.CellCount))).fill(100), margins: { marginUnitType, top, bottom, right, left } = { marginUnitType: WidthType.AUTO, top: 0, bottom: 0, right: 0, left: 0 }, float, layout, @@ -57,21 +53,12 @@ export class Table extends XmlComponent { this.properties.CellMargin.addTopMargin(top || 0, marginUnitType); this.properties.CellMargin.addLeftMargin(left || 0, marginUnitType); this.properties.CellMargin.addRightMargin(right || 0, marginUnitType); - const grid = new TableGrid(columnWidths); - this.root.push(grid); + this.root.push(new TableGrid(columnWidths)); - this.rows = Array(rows) - .fill(0) - .map(() => { - const cells = Array(columns) - .fill(0) - .map(() => new TableCell()); - const row = new TableRow(cells); - return row; - }); - - this.rows.forEach((x) => this.root.push(x)); + for (const row of rows) { + this.root.push(row); + } if (float) { this.properties.setTableFloatProperties(float); @@ -81,24 +68,4 @@ export class Table extends XmlComponent { this.properties.setLayout(layout); } } - - public getRow(index: number): TableRow { - const row = this.rows[index]; - - if (!row) { - throw Error("Index out of bounds when trying to get row on table"); - } - - return row; - } - - public getColumn(index: number): TableColumn { - // This is a convinence method for people who like to work with columns - const cells = this.rows.map((row) => row.getCell(index)); - return new TableColumn(cells); - } - - public getCell(row: number, col: number): TableCell { - return this.getRow(row).getCell(col); - } } From d2f82052b4e4af147ed70bfe6bb2b94085dddc7f Mon Sep 17 00:00:00 2001 From: Dolan Miu Date: Thu, 19 Sep 2019 22:49:09 +0100 Subject: [PATCH 02/14] Improve documentation --- docs/usage/tables.md | 147 ++++++++++++++++++++++++++----------------- 1 file changed, 90 insertions(+), 57 deletions(-) diff --git a/docs/usage/tables.md b/docs/usage/tables.md index bf6c16d7ea..81dbc7e818 100644 --- a/docs/usage/tables.md +++ b/docs/usage/tables.md @@ -1,95 +1,128 @@ # Tables -You can create tables with `docx`. More information can be found [here](http://officeopenxml.com/WPtable.php). +!> Paragraphs requires an understanding of [Sections](usage/sections.md). -## Create Table +## Intro -To create a table, simply create one with `new Table()`, then add it to the document: `doc.add()`. - -```ts -const table = doc.add(new Table({ - rows: [NUMBER OF ROWS], - columns: [NUMBER OF COLUMNS] -}); -``` - -Alternatively, you can create a table object directly, and then add it in the `document` - -```ts -const table = new Table(4, 4); -doc.add(table); -``` - -The snippet below creates a table of 2 rows and 4 columns. +Create a simple table like so: ```ts const table = new Table({ - rows: 2, - columns: 4, + rows: [Array of `TableRow`s] }); -doc.add(table); ``` -## Rows and Columns - -You can get a row or a column from a table like so, where `index` is a number: - -### Get Row +Then add the table in the `section` ```ts -const row = doc.getRow(index); +doc.addSection({ + children: [table], +}); ``` -With this, you can merge a row by using the `mergeCells()` method, where `startIndex` is the row number you want to merge from, and `endIndex` is where you want it to merge to: +## 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: ```ts -row.mergeCells(startIndex, endIndex); +const tableRow = new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("hello")], + }), + ], +}); ``` -You can get a cell from a `row` by using the `getCell()` method, where `index` is the row index: +Or preferably, add the tableRow directly into the `table` without declaring a variable: ```ts -row.getCell(index); +const table = new Table({ + rows: [ + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("hello")], + }), + ], + }), + ], +}); ``` -### Get Column +### Options -```ts -const column = doc.getColumn(index); -``` +Here is a list of options you can add to the `table row`: -Again, you can merge a row by using the `mergeCells()` method, where `startIndex` is the row number you want to merge from, and `endIndex` is where you want it to merge to: - -```ts -column.mergeCells(startIndex, endIndex); -``` - -You can get a cell from a `column` by using the `getCell()` method, where `index` is the column index: - -```ts -column.getCell(index); -``` +| Property | Type | Notes | +| ----------- | ------------------------------------- | -------- | +| children | `Array` | Required | +| cantSplit | `boolean` | Optional | +| tableHeader | `boolean` | Optional | +| height | `{ value: number, rule: HeightRule }` | Optional | ## Cells -To access the cell, use the `getCell()` method. +Cells need to be added in the `table row`, you can create a table cell like: ```ts -const cell = table.getCell([ROW INDEX], [COLUMN INDEX]); +const tableCell = new TableCell({ + children: [new Paragraph("hello")], +}); ``` -You can also get a cell from a `column` or a `row` with `getCell()`, mentioned previously. - -For example: +Or preferably, add the tableRow directly into the `table row` without declaring a variable: ```ts -const cell = table.getCell(0, 2); - -const cell = row.getCell(0); - -const cell = column.getCell(2); +const tableRow = new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("hello")], + }), + ], +}); ``` +| Property | Type | Notes | +| ----------- | ------------------------------------- | -------- | +| children | `Array` | Required | +| cantSplit | `boolean` | Optional | +| tableHeader | `boolean` | Optional | +| height | `{ value: number, rule: HeightRule }` | Optional | + +### Options + + readonly shading?: ITableShadingAttributesProperties; + readonly margins?: ITableCellMarginOptions; + readonly verticalAlign?: VerticalAlign; + readonly verticalMerge?: VMergeType; + readonly columnSpan?: number; + readonly borders?: { + readonly top?: { + readonly style: BorderStyle; + readonly size: number; + readonly color: string; + }; + readonly bottom?: { + readonly style: BorderStyle; + readonly size: number; + readonly color: string; + }; + readonly left?: { + readonly style: BorderStyle; + readonly size: number; + readonly color: string; + }; + readonly right?: { + readonly style: BorderStyle; + readonly size: number; + readonly color: string; + }; + }; + readonly children: Array; + + + ### Add paragraph to a cell Once you have got the cell, you can add data to it with the `add()` method. From a9d4ebc89873e706c7448929b1b5882652db10ac Mon Sep 17 00:00:00 2001 From: Dolan Miu Date: Sun, 22 Sep 2019 02:39:38 +0100 Subject: [PATCH 03/14] Add declarative column merge --- demo/32-merge-table-cells.ts | 2 +- demo/41-merge-table-cells-2.ts | 137 +++++++++++++++++++++++- demo/43-images-to-table-cell-2.ts | 3 +- src/file/table/table-cell/table-cell.ts | 31 ++++++ src/file/table/table-column.spec.ts | 65 ----------- src/file/table/table-column.ts | 25 ----- src/file/table/table-row/table-row.ts | 17 +-- src/file/table/table.ts | 7 ++ 8 files changed, 179 insertions(+), 108 deletions(-) delete mode 100644 src/file/table/table-column.spec.ts delete mode 100644 src/file/table/table-column.ts diff --git a/demo/32-merge-table-cells.ts b/demo/32-merge-table-cells.ts index 55a9181281..873f3b50db 100644 --- a/demo/32-merge-table-cells.ts +++ b/demo/32-merge-table-cells.ts @@ -1,4 +1,4 @@ -// Example of how you would merge cells together +// Example of how you would merge cells together - Rows and Columns // Import from 'docx' rather than '../build' if you install from npm import * as fs from "fs"; import { Document, HeadingLevel, Packer, Paragraph, ShadingType, Table, TableCell, TableRow, WidthType } from "../build"; diff --git a/demo/41-merge-table-cells-2.ts b/demo/41-merge-table-cells-2.ts index 50141a1a7a..3ef8615590 100644 --- a/demo/41-merge-table-cells-2.ts +++ b/demo/41-merge-table-cells-2.ts @@ -117,8 +117,143 @@ const table = new Table({ ], }); +const table2 = new Table({ + rows: [ + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("0,0")], + }), + new TableCell({ + children: [new Paragraph("0,1")], + rowSpan: 2, + }), + new TableCell({ + children: [new Paragraph("0,2")], + }), + new TableCell({ + children: [new Paragraph("0,3")], + }), + new TableCell({ + children: [new Paragraph("0,4")], + }), + new TableCell({ + children: [new Paragraph("0,5")], + }), + ], + }), + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("1,0")], + }), + new TableCell({ + children: [new Paragraph("1,2")], + }), + new TableCell({ + children: [new Paragraph("1,3")], + }), + new TableCell({ + children: [new Paragraph("1,4")], + }), + new TableCell({ + children: [new Paragraph("1,5")], + }), + ], + }), + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("2,0")], + }), + new TableCell({ + children: [new Paragraph("2,1")], + }), + new TableCell({ + children: [new Paragraph("2,2")], + }), + new TableCell({ + children: [new Paragraph("2,3")], + }), + new TableCell({ + children: [new Paragraph("2,4")], + }), + new TableCell({ + children: [new Paragraph("2,5")], + }), + ], + }), + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("3,0")], + }), + new TableCell({ + children: [new Paragraph("3,1")], + }), + new TableCell({ + children: [new Paragraph("3,2")], + }), + new TableCell({ + children: [new Paragraph("3,3")], + }), + new TableCell({ + children: [new Paragraph("3,4")], + }), + new TableCell({ + children: [new Paragraph("3,5")], + }), + ], + }), + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("4,0")], + }), + new TableCell({ + children: [new Paragraph("4,1")], + }), + new TableCell({ + children: [new Paragraph("4,2")], + }), + new TableCell({ + children: [new Paragraph("4,3")], + }), + new TableCell({ + children: [new Paragraph("4,4")], + }), + new TableCell({ + children: [new Paragraph("4,5")], + }), + ], + }), + new TableRow({ + children: [ + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + ], + }), + ], +}); + doc.addSection({ - children: [table], + children: [table, new Paragraph(""), table2], }); Packer.toBuffer(doc).then((buffer) => { diff --git a/demo/43-images-to-table-cell-2.ts b/demo/43-images-to-table-cell-2.ts index 1840c9382c..ab950d284c 100644 --- a/demo/43-images-to-table-cell-2.ts +++ b/demo/43-images-to-table-cell-2.ts @@ -36,6 +36,7 @@ const table = new Table({ }), new TableCell({ children: [], + rowSpan: 2, }), ], }), @@ -74,8 +75,6 @@ const table = new Table({ ], }); -table.getColumn(3).mergeCells(1, 2); - doc.addSection({ children: [table], }); diff --git a/src/file/table/table-cell/table-cell.ts b/src/file/table/table-cell/table-cell.ts index 05416b34d5..a93652e5d6 100644 --- a/src/file/table/table-cell/table-cell.ts +++ b/src/file/table/table-cell/table-cell.ts @@ -5,6 +5,7 @@ import { IXmlableObject, XmlComponent } from "file/xml-components"; import { ITableShadingAttributesProperties } from "../shading"; import { Table } from "../table"; +import { TableRow } from "../table-row"; import { ITableCellMarginOptions } from "./cell-margin/table-cell-margins"; import { VerticalAlign, VMergeType } from "./table-cell-components"; import { TableCellProperties } from "./table-cell-properties"; @@ -15,6 +16,7 @@ export interface ITableCellOptions { readonly verticalAlign?: VerticalAlign; readonly verticalMerge?: VMergeType; readonly columnSpan?: number; + readonly rowSpan?: number; readonly borders?: { readonly top?: { readonly style: BorderStyle; @@ -40,8 +42,15 @@ export interface ITableCellOptions { readonly children: Array; } +interface ITableCellMetaData { + readonly column: TableCell[]; + readonly row: TableRow; +} + export class TableCell extends XmlComponent { private readonly properties: TableCellProperties; + // tslint:disable-next-line: readonly-keyword + private metaData: ITableCellMetaData; constructor(readonly options: ITableCellOptions) { super("w:tc"); @@ -98,10 +107,32 @@ export class TableCell extends XmlComponent { } public prepForXml(): IXmlableObject | undefined { + // Row Span has to be added in this method and not the constructor because it needs to know information about the column which happens after Table Cell construction + // Row Span of 1 will crash word as it will add RESTART and not a corresponding CONTINUE + if (this.options.rowSpan && this.options.rowSpan > 1) { + this.properties.addVerticalMerge(VMergeType.RESTART); + + const currentIndex = this.metaData.column.indexOf(this); + for (let i = currentIndex + 1; i <= currentIndex + this.options.rowSpan - 1; i++) { + this.metaData.column[i].metaData.row.Children.splice( + i, + 0, + new TableCell({ + children: [], + }), + ); + this.metaData.column[i].properties.addVerticalMerge(VMergeType.CONTINUE); + } + } + // Cells must end with a paragraph if (!(this.root[this.root.length - 1] instanceof Paragraph)) { this.root.push(new Paragraph({})); } return super.prepForXml(); } + + public set MetaData(metaData: ITableCellMetaData) { + this.metaData = metaData; + } } diff --git a/src/file/table/table-column.spec.ts b/src/file/table/table-column.spec.ts deleted file mode 100644 index 50e9cb8aa4..0000000000 --- a/src/file/table/table-column.spec.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { expect } from "chai"; - -// import { Formatter } from "export/formatter"; -// import { EMPTY_OBJECT } from "file/xml-components"; - -import { TableCell } from "./table-cell"; -import { TableColumn } from "./table-column"; - -describe("TableColumn", () => { - let cells: TableCell[]; - beforeEach(() => { - cells = [ - new TableCell({ - children: [], - }), - new TableCell({ - children: [], - }), - new TableCell({ - children: [], - }), - ]; - }); - - describe("#getCell", () => { - it("should get the correct cell", () => { - const tableColumn = new TableColumn(cells); - const cell = tableColumn.getCell(0); - - expect(cell).to.deep.equal(cells[0]); - - const cell2 = tableColumn.getCell(1); - - expect(cell2).to.deep.equal(cells[1]); - }); - - it("should throw an error if index is out of bounds", () => { - const tableColumn = new TableColumn(cells); - - expect(() => tableColumn.getCell(9)).to.throw(); - }); - }); - - // describe("#mergeCells", () => { - // it("should add vMerge to correct cells", () => { - // const tableColumn = new TableColumn(cells); - // tableColumn.mergeCells(0, 2); - - // const tree = new Formatter().format(cells[0]); - // expect(tree).to.deep.equal({ - // "w:tc": [{ "w:tcPr": [{ "w:vMerge": { _attr: { "w:val": "restart" } } }] }, { "w:p": EMPTY_OBJECT }], - // }); - - // const tree2 = new Formatter().format(cells[1]); - // expect(tree2).to.deep.equal({ - // "w:tc": [{ "w:tcPr": [{ "w:vMerge": { _attr: { "w:val": "continue" } } }] }, { "w:p": EMPTY_OBJECT }], - // }); - - // const tree3 = new Formatter().format(cells[2]); - // expect(tree3).to.deep.equal({ - // "w:tc": [{ "w:tcPr": [{ "w:vMerge": { _attr: { "w:val": "continue" } } }] }, { "w:p": EMPTY_OBJECT }], - // }); - // }); - // }); -}); diff --git a/src/file/table/table-column.ts b/src/file/table/table-column.ts deleted file mode 100644 index fc8c2cc240..0000000000 --- a/src/file/table/table-column.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { TableCell } from "./table-cell"; - -export class TableColumn { - constructor(private readonly cells: TableCell[]) {} - - public getCell(index: number): TableCell { - const cell = this.cells[index]; - - if (!cell) { - throw Error("Index out of bounds when trying to get cell on column"); - } - - return cell; - } - - // public mergeCells(startIndex: number, endIndex: number): TableCell { - // this.cells[startIndex].addVerticalMerge(VMergeType.RESTART); - - // for (let i = startIndex + 1; i <= endIndex; i++) { - // this.cells[i].addVerticalMerge(VMergeType.CONTINUE); - // } - - // return this.cells[startIndex]; - // } -} diff --git a/src/file/table/table-row/table-row.ts b/src/file/table/table-row/table-row.ts index df47bf70bf..07cd811fb9 100644 --- a/src/file/table/table-row/table-row.ts +++ b/src/file/table/table-row/table-row.ts @@ -42,18 +42,7 @@ export class TableRow extends XmlComponent { return this.options.children.length; } - // public mergeCells(startIndex: number, endIndex: number): TableCell { - // const cellSpan = endIndex - startIndex + 1; - - // return this.addGridSpan(startIndex, cellSpan); - // } - - // private addGridSpan(index: number, cellSpan: number): TableCell { - // const remainCell = this.options.children[index]; - // remainCell.addGridSpan(cellSpan); - // this.options.children.splice(index + 1, cellSpan - 1); - // this.root.splice(index + 2, cellSpan - 1); - - // return remainCell; - // } + public get Children(): TableCell[] { + return this.options.children; + } } diff --git a/src/file/table/table.ts b/src/file/table/table.ts index ea8e64d72c..5459400aeb 100644 --- a/src/file/table/table.ts +++ b/src/file/table/table.ts @@ -57,6 +57,13 @@ export class Table extends XmlComponent { this.root.push(new TableGrid(columnWidths)); for (const row of rows) { + row.Children.forEach((cell, i) => { + cell.MetaData = { + column: rows.map((r) => r.Children[i]), + row: row, + }; + }); + this.root.push(row); } From c11af71ed7f5b401c812b462bdbae5179155c869 Mon Sep 17 00:00:00 2001 From: Dolan Miu Date: Sun, 22 Sep 2019 19:09:34 +0100 Subject: [PATCH 04/14] Add test such that it only should call prep once --- src/export/formatter.spec.ts | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/export/formatter.spec.ts b/src/export/formatter.spec.ts index bd6c9b7160..7ee05fa5d1 100644 --- a/src/export/formatter.spec.ts +++ b/src/export/formatter.spec.ts @@ -1,7 +1,8 @@ import { assert, expect } from "chai"; +import * as sinon from "sinon"; import { Formatter } from "export/formatter"; -import * as file from "file"; +import { Paragraph, TextRun } from "file"; import { CoreProperties } from "file/core-properties"; import { Attributes } from "file/xml-components"; @@ -14,22 +15,22 @@ describe("Formatter", () => { describe("#format()", () => { it("should format simple paragraph", () => { - const paragraph = new file.Paragraph(""); + const paragraph = new Paragraph(""); const newJson = formatter.format(paragraph); assert.isDefined(newJson["w:p"]); }); it("should remove xmlKeys", () => { - const paragraph = new file.Paragraph(""); + const paragraph = new Paragraph(""); const newJson = formatter.format(paragraph); const stringifiedJson = JSON.stringify(newJson); assert(stringifiedJson.indexOf("xmlKeys") < 0); }); it("should format simple paragraph with bold text", () => { - const paragraph = new file.Paragraph(""); + const paragraph = new Paragraph(""); paragraph.addRun( - new file.TextRun({ + new TextRun({ text: "test", bold: true, }), @@ -63,7 +64,7 @@ describe("Formatter", () => { }); it("should should change 'p' tag into 'w:p' tag", () => { - const paragraph = new file.Paragraph(""); + const paragraph = new Paragraph(""); const newJson = formatter.format(paragraph); assert.isDefined(newJson["w:p"]); }); @@ -76,5 +77,13 @@ describe("Formatter", () => { const newJson = formatter.format(properties); assert.isDefined(newJson["cp:coreProperties"]); }); + + it("should call the prep method only once", () => { + const paragraph = new Paragraph(""); + const spy = sinon.spy(paragraph, "prepForXml"); + + formatter.format(paragraph); + expect(spy.calledOnce).to.equal(true); + }); }); }); From cc36ea75429c9cb8083f74b1c9baee2f7ddf0a74 Mon Sep 17 00:00:00 2001 From: Dolan Miu Date: Sun, 22 Sep 2019 20:45:24 +0100 Subject: [PATCH 05/14] Optimise formatting to not over-format the same files --- src/export/packer/next-compiler.spec.ts | 20 +++++++++++++++++++- src/export/packer/next-compiler.ts | 12 +++++------- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/export/packer/next-compiler.spec.ts b/src/export/packer/next-compiler.spec.ts index ac271c02d9..7c48569793 100644 --- a/src/export/packer/next-compiler.spec.ts +++ b/src/export/packer/next-compiler.spec.ts @@ -1,7 +1,8 @@ /* tslint:disable:typedef space-before-function-paren */ import { expect } from "chai"; +import * as sinon from "sinon"; -import { File, Footer, Header } from "file"; +import { File, Footer, Header, Paragraph } from "file"; import { Compiler } from "./next-compiler"; @@ -72,5 +73,22 @@ describe("Compiler", () => { expect(fileNames).to.include("word/footer2.xml"); expect(fileNames).to.include("word/_rels/footer2.xml.rels"); }); + + it("should call the format method X times equalling X files to be formatted", () => { + // This test is required because before, there was a case where Document was formatted twice, which was inefficient + // This also caused issues such as running prepForXml multiple times as format() was ran multiple times. + const paragraph = new Paragraph(""); + const doc = new File(); + + doc.addSection({ + properties: {}, + children: [paragraph], + }); + // tslint:disable-next-line: no-string-literal + const spy = sinon.spy(compiler["formatter"], "format"); + + compiler.compile(file); + expect(spy.callCount).to.equal(10); + }); }); }); diff --git a/src/export/packer/next-compiler.ts b/src/export/packer/next-compiler.ts index 426d32174f..6a3e752098 100644 --- a/src/export/packer/next-compiler.ts +++ b/src/export/packer/next-compiler.ts @@ -68,13 +68,13 @@ export class Compiler { file.verifyUpdateFields(); const documentRelationshipCount = file.DocumentRelationships.RelationshipCount + 1; + const documentXmlData = xml(this.formatter.format(file.Document), prettify); + const documentMediaDatas = this.imageReplacer.getMediaData(documentXmlData, file.Media); + return { Relationships: { data: (() => { - const xmlData = xml(this.formatter.format(file.Document), prettify); - const mediaDatas = this.imageReplacer.getMediaData(xmlData, file.Media); - - mediaDatas.forEach((mediaData, i) => { + documentMediaDatas.forEach((mediaData, i) => { file.DocumentRelationships.createRelationship( documentRelationshipCount + i, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", @@ -88,9 +88,7 @@ export class Compiler { }, Document: { data: (() => { - const tempXmlData = xml(this.formatter.format(file.Document), prettify); - const mediaDatas = this.imageReplacer.getMediaData(tempXmlData, file.Media); - const xmlData = this.imageReplacer.replace(tempXmlData, mediaDatas, documentRelationshipCount); + const xmlData = this.imageReplacer.replace(documentXmlData, documentMediaDatas, documentRelationshipCount); return xmlData; })(), From 7aa4134e2b698d39271319799b5c39b633f06000 Mon Sep 17 00:00:00 2001 From: Dolan Miu Date: Wed, 25 Sep 2019 00:57:24 +0100 Subject: [PATCH 06/14] Refactor row merging to table level --- ...s.ts => 32-merge-and-shade-table-cells.ts} | 56 ++++++++++++++++++- demo/41-merge-table-cells-2.ts | 2 +- src/file/table/table-cell/table-cell.ts | 34 ++--------- src/file/table/table-row/table-row.ts | 5 ++ src/file/table/table.ts | 31 +++++++--- 5 files changed, 86 insertions(+), 42 deletions(-) rename demo/{32-merge-table-cells.ts => 32-merge-and-shade-table-cells.ts} (74%) diff --git a/demo/32-merge-table-cells.ts b/demo/32-merge-and-shade-table-cells.ts similarity index 74% rename from demo/32-merge-table-cells.ts rename to demo/32-merge-and-shade-table-cells.ts index 873f3b50db..1112931079 100644 --- a/demo/32-merge-table-cells.ts +++ b/demo/32-merge-and-shade-table-cells.ts @@ -1,4 +1,4 @@ -// Example of how you would merge cells together - Rows and Columns +// Example of how you would merge cells together (Rows and Columns) and apply shading // Import from 'docx' rather than '../build' if you install from npm import * as fs from "fs"; import { Document, HeadingLevel, Packer, Paragraph, ShadingType, Table, TableCell, TableRow, WidthType } from "../build"; @@ -125,13 +125,61 @@ const table3 = new Table({ const table4 = new Table({ rows: [ + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("0,0")], + columnSpan: 2, + }), + ], + }), + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("1,0")], + }), + new TableCell({ + children: [new Paragraph("1,1")], + }), + ], + }), + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("2,0")], + columnSpan: 2, + }), + ], + }), + ], + width: 100, + widthUnitType: WidthType.PERCENTAGE, +}); + +const table5 = new Table({ + rows: [ + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("0,0")], + }), + new TableCell({ + children: [new Paragraph("0,1")], + rowSpan: 2, + }), + new TableCell({ + children: [new Paragraph("0,2")], + }), + ], + }), new TableRow({ children: [ new TableCell({ children: [], }), new TableCell({ - children: [], + children: [new Paragraph("1,2")], + rowSpan: 2, }), ], }), @@ -163,8 +211,10 @@ doc.addSection({ heading: HeadingLevel.HEADING_2, }), table3, - new Paragraph("hi"), + new Paragraph("Merging columns"), table4, + new Paragraph("More Merging columns"), + table5, ], }); diff --git a/demo/41-merge-table-cells-2.ts b/demo/41-merge-table-cells-2.ts index 3ef8615590..94e7bffe1d 100644 --- a/demo/41-merge-table-cells-2.ts +++ b/demo/41-merge-table-cells-2.ts @@ -1,4 +1,4 @@ -// Multiple cells merging in the same table +// Multiple cells merging in the same table - Rows and Columns // Import from 'docx' rather than '../build' if you install from npm import * as fs from "fs"; import { Document, Packer, Paragraph, Table, TableCell, TableRow } from "../build"; diff --git a/src/file/table/table-cell/table-cell.ts b/src/file/table/table-cell/table-cell.ts index a93652e5d6..616065a0c1 100644 --- a/src/file/table/table-cell/table-cell.ts +++ b/src/file/table/table-cell/table-cell.ts @@ -5,7 +5,6 @@ import { IXmlableObject, XmlComponent } from "file/xml-components"; import { ITableShadingAttributesProperties } from "../shading"; import { Table } from "../table"; -import { TableRow } from "../table-row"; import { ITableCellMarginOptions } from "./cell-margin/table-cell-margins"; import { VerticalAlign, VMergeType } from "./table-cell-components"; import { TableCellProperties } from "./table-cell-properties"; @@ -42,15 +41,8 @@ export interface ITableCellOptions { readonly children: Array; } -interface ITableCellMetaData { - readonly column: TableCell[]; - readonly row: TableRow; -} - export class TableCell extends XmlComponent { private readonly properties: TableCellProperties; - // tslint:disable-next-line: readonly-keyword - private metaData: ITableCellMetaData; constructor(readonly options: ITableCellOptions) { super("w:tc"); @@ -82,6 +74,10 @@ export class TableCell extends XmlComponent { this.properties.addGridSpan(options.columnSpan); } + if (options.rowSpan && options.rowSpan > 1) { + this.properties.addVerticalMerge(VMergeType.RESTART); + } + if (options.borders) { if (options.borders.top) { this.properties.Borders.addTopBorder(options.borders.top.style, options.borders.top.size, options.borders.top.color); @@ -107,32 +103,10 @@ export class TableCell extends XmlComponent { } public prepForXml(): IXmlableObject | undefined { - // Row Span has to be added in this method and not the constructor because it needs to know information about the column which happens after Table Cell construction - // Row Span of 1 will crash word as it will add RESTART and not a corresponding CONTINUE - if (this.options.rowSpan && this.options.rowSpan > 1) { - this.properties.addVerticalMerge(VMergeType.RESTART); - - const currentIndex = this.metaData.column.indexOf(this); - for (let i = currentIndex + 1; i <= currentIndex + this.options.rowSpan - 1; i++) { - this.metaData.column[i].metaData.row.Children.splice( - i, - 0, - new TableCell({ - children: [], - }), - ); - this.metaData.column[i].properties.addVerticalMerge(VMergeType.CONTINUE); - } - } - // Cells must end with a paragraph if (!(this.root[this.root.length - 1] instanceof Paragraph)) { this.root.push(new Paragraph({})); } return super.prepForXml(); } - - public set MetaData(metaData: ITableCellMetaData) { - this.metaData = metaData; - } } diff --git a/src/file/table/table-row/table-row.ts b/src/file/table/table-row/table-row.ts index 07cd811fb9..466b7eb320 100644 --- a/src/file/table/table-row/table-row.ts +++ b/src/file/table/table-row/table-row.ts @@ -45,4 +45,9 @@ export class TableRow extends XmlComponent { public get Children(): TableCell[] { return this.options.children; } + + public addCellToIndex(cell: TableCell, index: number): void { + // Offset because properties is also in root. + this.root.splice(index + 1, 0, cell); + } } diff --git a/src/file/table/table.ts b/src/file/table/table.ts index 5459400aeb..64e276f73d 100644 --- a/src/file/table/table.ts +++ b/src/file/table/table.ts @@ -1,7 +1,7 @@ // http://officeopenxml.com/WPtableGrid.php import { XmlComponent } from "file/xml-components"; import { TableGrid } from "./grid"; -import { WidthType } from "./table-cell"; +import { TableCell, VMergeType, WidthType } from "./table-cell"; import { ITableFloatOptions, TableProperties } from "./table-properties"; import { TableLayoutType } from "./table-properties/table-layout"; import { TableRow } from "./table-row"; @@ -57,16 +57,31 @@ export class Table extends XmlComponent { this.root.push(new TableGrid(columnWidths)); for (const row of rows) { - row.Children.forEach((cell, i) => { - cell.MetaData = { - column: rows.map((r) => r.Children[i]), - row: row, - }; - }); - this.root.push(row); } + for (const row of rows) { + row.Children.forEach((cell, cellIndex) => { + const column = rows.map((r) => r.Children[cellIndex]); + // Row Span has to be added in this method and not the constructor because it needs to know information about the column which happens after Table Cell construction + // Row Span of 1 will crash word as it will add RESTART and not a corresponding CONTINUE + if (cell.options.rowSpan && cell.options.rowSpan > 1) { + const thisCellsColumnIndex = column.indexOf(cell); + const endColumnIndex = thisCellsColumnIndex + (cell.options.rowSpan - 1); + + for (let i = thisCellsColumnIndex + 1; i <= endColumnIndex; i++) { + rows[i].addCellToIndex( + new TableCell({ + children: [], + verticalMerge: VMergeType.CONTINUE, + }), + i, + ); + } + } + }); + } + if (float) { this.properties.setTableFloatProperties(float); } From b2de74a0e6c1392b842368d4cff88c8b5112ec41 Mon Sep 17 00:00:00 2001 From: Dolan Miu Date: Wed, 25 Sep 2019 01:09:53 +0100 Subject: [PATCH 07/14] Fix tests --- src/file/table/table.spec.ts | 45 +++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/src/file/table/table.spec.ts b/src/file/table/table.spec.ts index efbf2ebb24..b88bda964a 100644 --- a/src/file/table/table.spec.ts +++ b/src/file/table/table.spec.ts @@ -8,7 +8,6 @@ import { Table } from "./table"; // import { WidthType } from "./table-cell"; import { RelativeHorizontalPosition, RelativeVerticalPosition, TableAnchorType } from "./table-properties"; -import { EMPTY_OBJECT } from "file/xml-components"; import { TableCell } from "./table-cell"; import { TableLayoutType } from "./table-properties/table-layout"; import { TableRow } from "./table-row"; @@ -154,7 +153,28 @@ describe("Table", () => { ], }); const tree = new Formatter().format(table); - const cell = { "w:tc": [{ "w:p": EMPTY_OBJECT }] }; + const cell = { + "w:tc": [ + { + "w:p": [ + { + "w:r": [ + { + "w:t": [ + { + _attr: { + "xml:space": "preserve", + }, + }, + "hello", + ], + }, + ], + }, + ], + }, + ], + }; expect(tree).to.deep.equal({ "w:tbl": [ { "w:tblPr": [DEFAULT_TABLE_PROPERTIES, BORDERS, WIDTHS] }, @@ -216,7 +236,26 @@ describe("Table", () => { .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:p": EMPTY_OBJECT }], + "w:tc": [ + { + "w:p": [ + { + "w:r": [ + { + "w:t": [ + { + _attr: { + "xml:space": "preserve", + }, + }, + "hello", + ], + }, + ], + }, + ], + }, + ], }); }); From 2842619196fc8811f01ddf8a276c12f9de17e409 Mon Sep 17 00:00:00 2001 From: Dolan Miu Date: Wed, 25 Sep 2019 01:59:30 +0100 Subject: [PATCH 08/14] Update table documentation --- docs/usage/tables.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/usage/tables.md b/docs/usage/tables.md index 81dbc7e818..e3230c7293 100644 --- a/docs/usage/tables.md +++ b/docs/usage/tables.md @@ -128,7 +128,9 @@ const tableRow = new TableRow({ Once you have got the cell, you can add data to it with the `add()` method. ```ts -cell.add(new Paragraph("Hello")); +new TableCell({ + children: [new Paragraph("Hello")], +}), ``` ### Set width of a cell @@ -312,9 +314,9 @@ Example showing how align text in a table cell _Source: https://github.com/dolanmiu/docx/blob/master/demo/31-tables.ts_ -### Merging rows +### Shading -Example showing merging of `rows` +Example showing merging of columns and rows and shading [Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/32-merge-table-cells.ts ':include') @@ -326,7 +328,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/41-merge-table-cells- ### Merging columns -Example showing merging of `columns` +Example showing merging of columns and rows [Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/43-images-to-table-cell-2.ts ':include') From bd888219fcaf25e532e44c158dcdf5a5fa1e16eb Mon Sep 17 00:00:00 2001 From: Dolan Date: Thu, 26 Sep 2019 02:03:17 +0100 Subject: [PATCH 09/14] Amend table documentation --- docs/usage/tables.md | 340 ++++++++++-------- .../table/table-cell/table-cell-components.ts | 10 +- .../table-cell/table-cell-properties.spec.ts | 4 +- .../table/table-cell/table-cell-properties.ts | 15 +- src/file/table/table-cell/table-cell.ts | 6 +- src/file/table/table.ts | 21 +- 6 files changed, 229 insertions(+), 167 deletions(-) diff --git a/docs/usage/tables.md b/docs/usage/tables.md index e3230c7293..ece0f6930d 100644 --- a/docs/usage/tables.md +++ b/docs/usage/tables.md @@ -4,6 +4,10 @@ ## Intro +* `Tables` contain a list of `Rows` +* `Rows` contain a list of `TableCells` +* `TableCells` contain a list of `Parahraphs` and/or `Tables`. You can add `Tables` as tables can be nested inside each other + Create a simple table like so: ```ts @@ -20,6 +24,46 @@ doc.addSection({ }); ``` +## Table + +### Set Width + +```ts +const table = new Table({ + ..., + width: { + size: [TABLE_WIDTH], + type: WidthType, + } +}); +``` + +For example: + +```ts + +const table = new Table({ + ..., + width: { + size: 4535, + type: WidthType.DXA, + } +}); +``` + +### Pagination + +#### Prevent row pagination + +To prevent breaking contents of a row across multiple pages, call `cantSplit`: + +```ts +const table = new Table({ + rows: [], + cantSplit: true, +}); +``` + ## 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: @@ -61,7 +105,18 @@ Here is a list of options you can add to the `table row`: | tableHeader | `boolean` | Optional | | height | `{ value: number, rule: HeightRule }` | Optional | -## Cells +### Repeat row + +If a table is paginated on multiple pages, it is possible to repeat a row at the top of each new page by setting `tableHeader` to `true`: + +```ts +const row = new TableRow({ + ..., + tableHeader: true, +}); +``` + +## Table Cells Cells need to be added in the `table row`, you can create a table cell like: @@ -83,206 +138,197 @@ const tableRow = new TableRow({ }); ``` -| Property | Type | Notes | -| ----------- | ------------------------------------- | -------- | -| children | `Array` | Required | -| cantSplit | `boolean` | Optional | -| tableHeader | `boolean` | Optional | -| height | `{ value: number, rule: HeightRule }` | Optional | - ### Options - readonly shading?: ITableShadingAttributesProperties; - readonly margins?: ITableCellMarginOptions; - readonly verticalAlign?: VerticalAlign; - readonly verticalMerge?: VMergeType; - readonly columnSpan?: number; - readonly borders?: { - readonly top?: { - readonly style: BorderStyle; - readonly size: number; - readonly color: string; - }; - readonly bottom?: { - readonly style: BorderStyle; - readonly size: number; - readonly color: string; - }; - readonly left?: { - readonly style: BorderStyle; - readonly size: number; - readonly color: string; - }; - readonly right?: { - readonly style: BorderStyle; - readonly size: number; - readonly color: string; - }; - }; - readonly children: Array; +| Property | Type | Notes | +| ------------- | ----------------------------------- | ----------------------------------------------------------- | +| children | `Array` | Required. You can nest tables by adding a table into a cell | +| shading | `ITableShadingAttributesProperties` | Optional | +| margins | `ITableCellMarginOptions` | Optional | +| verticalAlign | `VerticalAlign` | Optional | +| columnSpan | `number` | Optional | +| rowSpan | `number` | Optional | +| borders | `BorderOptions` | Optional | +| width | `{ size: number type: WidthType }` | Optional | +#### Border Options +| Property | Type | Notes | +| -------- | ----------------------------------------------------- | -------- | +| top | `{ style: BorderStyle, size: number, color: string }` | Optional | +| bottom | `{ style: BorderStyle, size: number, color: string }` | Optional | +| left | `{ style: BorderStyle, size: number, color: string }` | Optional | +| right | `{ style: BorderStyle, size: number, color: string }` | Optional | + +##### Example + +```ts +const cell = new TableCell({ + ..., + borders: { + top: { + style: BorderStyle.DASH_DOT_STROKED, + size: 1, + color: "red", + }, + bottom: { + style: BorderStyle.THICK_THIN_MEDIUM_GAP, + size: 5, + color: "889900", + }, + }, +}); +``` + +##### Google DOCS + +Google DOCS does not support start and end borders, instead they use left and right borders. So to set left and right borders for Google DOCS you should use: + +```ts +const cell = new TableCell({ + ..., + borders: { + top: { + style: BorderStyle.DOT_DOT_DASH, + size: 3, + color: "green", + }, + bottom: { + style: BorderStyle.DOT_DOT_DASH, + size: 3, + color: "ff8000", + }, + }, +}); +``` ### Add paragraph to a cell -Once you have got the cell, you can add data to it with the `add()` method. +Once you have got the cell, you can add data to it: ```ts -new TableCell({ +const cell = new TableCell({ children: [new Paragraph("Hello")], -}), +}); ``` ### Set width of a cell You can specify the width of a cell using: -`cell.Properties.setWidth(width, format)` +```ts +const cell = new TableCell({ + ..., + width: { + size: number, + type: WidthType, + }, +}); +``` -format can be: +`WidthType` values can be: -- WidthType.AUTO -- WidthType.DXA: value is in twentieths of a point -- WidthType.NIL: is considered as zero -- WidthType.PCT: percent of table width +| Property | Notes | +| -------- | --------------------------------- | +| AUTO | | +| DXA | value is in twentieths of a point | +| NIL | is considered as zero | +| PCT | percent of table width | -### Example +#### Example ```ts cell.Properties.setWidth(100, WidthType.DXA); ``` -```ts -cell.Properties.setWidth("50%", WidthType.PCT); -``` +### Nested Tables -## Borders - -BorderStyle can be imported from `docx`. Size determines the thickness. HTML color can be a hex code or alias such as `red`. +To have a table within a table, simply add it in the `children` block of a `table cell`: ```ts -cell.Borders.addTopBorder([BorderStyle], [SIZE], [HTML COLOR]); +const cell = new TableCell({ + children: [new Table(...)], +}); ``` -```ts -cell.Borders.addBottomBorder([BorderStyle], [SIZE], [HTML COLOR]); -``` - -```ts -cell.Borders.addStartBorder([[BorderStyle]], [SIZE], [HTML COLOR]); -``` - -```ts -cell.Borders.addEndBorder([BorderStyle], [SIZE], [HTML COLOR]); -``` - -### Example - -```ts -import { BorderStyle } from "docx"; - -cell.Borders.addStartBorder(BorderStyle.DOT_DOT_DASH, 3, "green"); -cell.Borders.addEndBorder(BorderStyle.DOT_DOT_DASH, 3, "#ff8000"); -``` - -### Google DOCS - -Google DOCS does not support start and end borders, instead they use left and right borders. So to set left and right borders for Google DOCS you should use: - -```ts -import { BorderStyle } from "docx"; - -cell.Borders.addLeftBorder(BorderStyle.DOT_DOT_DASH, 3, "green"); -cell.Borders.addRightBorder(BorderStyle.DOT_DOT_DASH, 3, "#ff8000"); -``` - -## Set Width - -```ts -import { WidthType } from "docx"; - -table.setWidth([WIDTH], [OPTIONAL WidthType. Defaults to DXA]); -``` - -For example: - -```ts -table.setWidth(4535, WidthType.DXA); -``` - -## Vertical Align +### Vertical Align Sets the vertical alignment of the contents of the cell ```ts -import { VerticalAlign } from "docx"; - -cell.setVerticalAlign([VerticalAlign TYPE]); +const cell = new TableCell({ + ..., + verticalAlign: VerticalAlign, +}); ``` +`VerticalAlign` values can be: + +| Property | Notes | +| -------- | ------------------------------------------ | +| BOTTOM | Align the contents on the bottom | +| CENTER | Align the contents on the center | +| TOP | Align the contents on the top. The default | + For example, to center align a cell: ```ts -cell.setVerticalAlign(VerticalAlign.CENTER); +const cell = new TableCell({ + verticalAlign: VerticalAlign.CENTER, +}); ``` -## Rows +## Merging cells together -To get a row, use the `getRow` method on a `table`. There are a handful of methods which you can apply to a row which will be explained below. +### Row Merge + +When cell rows are merged, it counts as multiple rows, so be sure to remove excess cells. It is similar to how HTML's `rowspan` works. +https://www.w3schools.com/tags/att_td_rowspan.asp ```ts -table.getRow([ROW INDEX]); -``` - -## Merge cells together - -### Merging on a row - -First obtain the row, and call `mergeCells()`. The first argument is where the merge should start. The second argument is where the merge should end. - -```ts -table.getRow(0).mergeCells([FROM INDEX], [TO INDEX]); +const cell = new TableCell({ + ..., + rowSpan: [NUMBER_OF_CELLS_TO_MERGE], +}); ``` #### Example -This will merge 3 cells together starting from index `0`: +The example will merge three rows together. ```ts -table.getRow(0).mergeCells(0, 2); +const cell = new TableCell({ + ..., + rowSpan: 3, +}); ``` -### Merging on a column +### Column Merge -It has not been implemented yet, but it will follow a similar structure as merging a row. - -## Nested Tables - -To have a table within a table +When cell columns are merged, it counts as multiple columns, so be sure to remove excess cells. It is similar to how HTML's `colspan` works. +https://www.w3schools.com/tags/att_td_colspan.asp ```ts -cell.add(new Table(1, 1)); +const cell = new TableCell({ + ..., + columnSpan: [NUMBER_OF_CELLS_TO_MERGE], +}); ``` -## Pagination +#### Example -###Prevent row pagination -To prevent breaking contents of a row across multiple pages, call `cantSplit()`: +The example will merge three columns together. ```ts -table.getRow(0).setCantSplit(); -``` - -###Repeat row -If a table is paginated on multiple pages, it is possible to repeat a row at the top of each new page calling `setTableHeader()`: - -```ts -table.getRow(0).setTableHeader(); +const cell = new TableCell({ + ..., + columnSpan: 3, +}); ``` ## Examples -[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/4-basic-table.ts ':include') +[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/4-basic-table.ts ":include") _Source: https://github.com/dolanmiu/docx/blob/master/demo/4-basic-table.ts_ @@ -290,7 +336,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/4-basic-table.ts_ Example showing how to add colourful borders to tables -[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/20-table-cell-borders.ts ':include') +[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/20-table-cell-borders.ts ":include") _Source: https://github.com/dolanmiu/docx/blob/master/demo/20-table-cell-borders.ts_ @@ -298,11 +344,11 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/20-table-cell-borders Example showing how to add images to tables -[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/24-images-to-table-cell.ts ':include') +[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/24-images-to-table-cell.ts ":include") _Source: https://github.com/dolanmiu/docx/blob/master/demo/24-images-to-table-cell.ts_ -[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/36-image-to-table-cell.ts ':include') +[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/36-image-to-table-cell.ts ":include") _Source: https://github.com/dolanmiu/docx/blob/master/demo/36-image-to-table-cell.ts_ @@ -310,19 +356,19 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/36-image-to-table-cel Example showing how align text in a table cell -[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/31-tables.ts ':include') +[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/31-tables.ts ":include") _Source: https://github.com/dolanmiu/docx/blob/master/demo/31-tables.ts_ -### Shading +### Shading Example showing merging of columns and rows and shading -[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/32-merge-table-cells.ts ':include') +[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/32-merge-table-cells.ts ":include") _Source: https://github.com/dolanmiu/docx/blob/master/demo/32-merge-table-cells.ts_ -[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/41-merge-table-cells-2.ts ':include') +[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/41-merge-table-cells-2.ts ":include") _Source: https://github.com/dolanmiu/docx/blob/master/demo/41-merge-table-cells-2.ts_ @@ -330,12 +376,12 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/41-merge-table-cells- Example showing merging of columns and rows -[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/43-images-to-table-cell-2.ts ':include') +[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/43-images-to-table-cell-2.ts ":include") _Source: https://github.com/dolanmiu/docx/blob/master/demo/43-images-to-table-cell-2.ts_ ### Floating tables -[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/34-floating-tables.ts ':include') +[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/34-floating-tables.ts ":include") _Source: https://github.com/dolanmiu/docx/blob/master/demo/34-floating-tables.ts_ diff --git a/src/file/table/table-cell/table-cell-components.ts b/src/file/table/table-cell/table-cell-components.ts index 518b8a67b2..e16c2f6885 100644 --- a/src/file/table/table-cell/table-cell-components.ts +++ b/src/file/table/table-cell/table-cell-components.ts @@ -103,7 +103,7 @@ export class GridSpan extends XmlComponent { /** * Vertical merge types. */ -export enum VMergeType { +export enum VerticalMergeType { /** * Cell that is merged with upper one. */ @@ -114,19 +114,19 @@ export enum VMergeType { RESTART = "restart", } -class VMergeAttributes extends XmlAttributeComponent<{ readonly val: VMergeType }> { +class VerticalMergeAttributes extends XmlAttributeComponent<{ readonly val: VerticalMergeType }> { protected readonly xmlKeys = { val: "w:val" }; } /** * Vertical merge element. Should be used in a table cell. */ -export class VMerge extends XmlComponent { - constructor(value: VMergeType) { +export class VerticalMerge extends XmlComponent { + constructor(value: VerticalMergeType) { super("w:vMerge"); this.root.push( - new VMergeAttributes({ + new VerticalMergeAttributes({ val: value, }), ); diff --git a/src/file/table/table-cell/table-cell-properties.spec.ts b/src/file/table/table-cell/table-cell-properties.spec.ts index 65bfd92fe6..9835aa9623 100644 --- a/src/file/table/table-cell/table-cell-properties.spec.ts +++ b/src/file/table/table-cell/table-cell-properties.spec.ts @@ -3,7 +3,7 @@ import { expect } from "chai"; import { Formatter } from "export/formatter"; import { BorderStyle } from "file/styles"; -import { VerticalAlign, VMergeType, WidthType } from "./table-cell-components"; +import { VerticalAlign, VerticalMergeType, WidthType } from "./table-cell-components"; import { TableCellProperties } from "./table-cell-properties"; describe("TableCellProperties", () => { @@ -30,7 +30,7 @@ describe("TableCellProperties", () => { describe("#addVerticalMerge", () => { it("adds vertical merge", () => { const properties = new TableCellProperties(); - properties.addVerticalMerge(VMergeType.CONTINUE); + properties.addVerticalMerge(VerticalMergeType.CONTINUE); const tree = new Formatter().format(properties); expect(tree).to.deep.equal({ "w:tcPr": [{ "w:vMerge": { _attr: { "w:val": "continue" } } }] }); }); diff --git a/src/file/table/table-cell/table-cell-properties.ts b/src/file/table/table-cell/table-cell-properties.ts index 5a2544b9cd..aed56aaeda 100644 --- a/src/file/table/table-cell/table-cell-properties.ts +++ b/src/file/table/table-cell/table-cell-properties.ts @@ -2,7 +2,16 @@ import { IgnoreIfEmptyXmlComponent } from "file/xml-components"; import { ITableShadingAttributesProperties, TableShading } from "../shading"; import { ITableCellMarginOptions, TableCellMargin } from "./cell-margin/table-cell-margins"; -import { GridSpan, TableCellBorders, TableCellWidth, VAlign, VerticalAlign, VMerge, VMergeType, WidthType } from "./table-cell-components"; +import { + GridSpan, + TableCellBorders, + TableCellWidth, + VAlign, + VerticalAlign, + VerticalMerge, + VerticalMergeType, + WidthType, +} from "./table-cell-components"; export class TableCellProperties extends IgnoreIfEmptyXmlComponent { private readonly cellBorder: TableCellBorders; @@ -23,8 +32,8 @@ export class TableCellProperties extends IgnoreIfEmptyXmlComponent { return this; } - public addVerticalMerge(type: VMergeType): TableCellProperties { - this.root.push(new VMerge(type)); + public addVerticalMerge(type: VerticalMergeType): TableCellProperties { + this.root.push(new VerticalMerge(type)); return this; } diff --git a/src/file/table/table-cell/table-cell.ts b/src/file/table/table-cell/table-cell.ts index 616065a0c1..5c34d17903 100644 --- a/src/file/table/table-cell/table-cell.ts +++ b/src/file/table/table-cell/table-cell.ts @@ -6,14 +6,14 @@ import { IXmlableObject, XmlComponent } from "file/xml-components"; import { ITableShadingAttributesProperties } from "../shading"; import { Table } from "../table"; import { ITableCellMarginOptions } from "./cell-margin/table-cell-margins"; -import { VerticalAlign, VMergeType } from "./table-cell-components"; +import { VerticalAlign, VerticalMergeType } from "./table-cell-components"; import { TableCellProperties } from "./table-cell-properties"; export interface ITableCellOptions { readonly shading?: ITableShadingAttributesProperties; readonly margins?: ITableCellMarginOptions; readonly verticalAlign?: VerticalAlign; - readonly verticalMerge?: VMergeType; + readonly verticalMerge?: VerticalMergeType; readonly columnSpan?: number; readonly rowSpan?: number; readonly borders?: { @@ -75,7 +75,7 @@ export class TableCell extends XmlComponent { } if (options.rowSpan && options.rowSpan > 1) { - this.properties.addVerticalMerge(VMergeType.RESTART); + this.properties.addVerticalMerge(VerticalMergeType.RESTART); } if (options.borders) { diff --git a/src/file/table/table.ts b/src/file/table/table.ts index 64e276f73d..08bd47eda2 100644 --- a/src/file/table/table.ts +++ b/src/file/table/table.ts @@ -1,7 +1,7 @@ // http://officeopenxml.com/WPtableGrid.php import { XmlComponent } from "file/xml-components"; import { TableGrid } from "./grid"; -import { TableCell, VMergeType, WidthType } from "./table-cell"; +import { TableCell, VerticalMergeType, WidthType } from "./table-cell"; import { ITableFloatOptions, TableProperties } from "./table-properties"; import { TableLayoutType } from "./table-properties/table-layout"; import { TableRow } from "./table-row"; @@ -18,8 +18,10 @@ import { TableRow } from "./table-row"; */ export interface ITableOptions { readonly rows: TableRow[]; - readonly width?: number; - readonly widthUnitType?: WidthType; + readonly width?: { + readonly size: number; + readonly type?: WidthType; + }; readonly columnWidths?: number[]; readonly margins?: { readonly marginUnitType?: WidthType; @@ -37,8 +39,7 @@ export class Table extends XmlComponent { constructor({ rows, - width = 100, - widthUnitType = WidthType.AUTO, + width, columnWidths = Array(Math.max(...rows.map((row) => row.CellCount))).fill(100), margins: { marginUnitType, top, bottom, right, left } = { marginUnitType: WidthType.AUTO, top: 0, bottom: 0, right: 0, left: 0 }, float, @@ -48,7 +49,13 @@ export class Table extends XmlComponent { this.properties = new TableProperties(); this.root.push(this.properties); this.properties.setBorder(); - this.properties.setWidth(width, widthUnitType); + + if (width) { + this.properties.setWidth(width.size, width.type); + } else { + this.properties.setWidth(100); + } + this.properties.CellMargin.addBottomMargin(bottom || 0, marginUnitType); this.properties.CellMargin.addTopMargin(top || 0, marginUnitType); this.properties.CellMargin.addLeftMargin(left || 0, marginUnitType); @@ -73,7 +80,7 @@ export class Table extends XmlComponent { rows[i].addCellToIndex( new TableCell({ children: [], - verticalMerge: VMergeType.CONTINUE, + verticalMerge: VerticalMergeType.CONTINUE, }), i, ); From 44b95f2f1567fd320860c1ef0d70c116cc0b737a Mon Sep 17 00:00:00 2001 From: Dolan Date: Thu, 26 Sep 2019 02:14:52 +0100 Subject: [PATCH 10/14] Add shading test --- .../table-properties/table-properties.spec.ts | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/file/table/table-properties/table-properties.spec.ts b/src/file/table/table-properties/table-properties.spec.ts index dbd1c6b371..960d117b7c 100644 --- a/src/file/table/table-properties/table-properties.spec.ts +++ b/src/file/table/table-properties/table-properties.spec.ts @@ -2,6 +2,7 @@ import { expect } from "chai"; import { Formatter } from "export/formatter"; +import { ShadingType } from "../shading"; import { WidthType } from "../table-cell"; import { TableLayoutType } from "./table-layout"; import { TableProperties } from "./table-properties"; @@ -66,4 +67,29 @@ describe("TableProperties", () => { }); }); }); + + describe("#setShading", () => { + it("sets the shading of the table", () => { + const tp = new TableProperties(); + tp.setShading({ + fill: "b79c2f", + val: ShadingType.REVERSE_DIAGONAL_STRIPE, + color: "auto", + }); + const tree = new Formatter().format(tp); + expect(tree).to.deep.equal({ + "w:tblPr": [ + { + "w:shd": { + _attr: { + "w:color": "auto", + "w:fill": "b79c2f", + "w:val": "reverseDiagStripe", + }, + }, + }, + ], + }); + }); + }); }); From c5eb3d567083b1571452615c057e17145a611c5f Mon Sep 17 00:00:00 2001 From: Dolan Date: Thu, 26 Sep 2019 02:24:43 +0100 Subject: [PATCH 11/14] Add addMargin test --- .../table-cell/table-cell-properties.spec.ts | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/file/table/table-cell/table-cell-properties.spec.ts b/src/file/table/table-cell/table-cell-properties.spec.ts index 9835aa9623..c3452e757c 100644 --- a/src/file/table/table-cell/table-cell-properties.spec.ts +++ b/src/file/table/table-cell/table-cell-properties.spec.ts @@ -73,6 +73,54 @@ describe("TableCellProperties", () => { }); }); + describe("#addMargins", () => { + it("sets shading", () => { + const properties = new TableCellProperties(); + properties.addMargins({}); + const tree = new Formatter().format(properties); + expect(tree).to.deep.equal({ + "w:tcPr": [ + { + "w:tcMar": [ + { + "w:top": { + _attr: { + "w:type": "dxa", + "w:w": 0, + }, + }, + }, + { + "w:bottom": { + _attr: { + "w:type": "dxa", + "w:w": 0, + }, + }, + }, + { + "w:end": { + _attr: { + "w:type": "dxa", + "w:w": 0, + }, + }, + }, + { + "w:start": { + _attr: { + "w:type": "dxa", + "w:w": 0, + }, + }, + }, + ], + }, + ], + }); + }); + }); + describe("#Borders", () => { it("should return the TableCellBorders if Border has borders", () => { const properties = new TableCellProperties(); From 172c333357072449cc6cc3a0d75a21dde3158c50 Mon Sep 17 00:00:00 2001 From: Dolan Date: Sun, 29 Sep 2019 04:17:21 +0100 Subject: [PATCH 12/14] Add tests and clean up code --- .../footer-reference/footer-reference.spec.ts | 42 +++ .../header-reference/header-reference.spec.ts | 42 +++ .../section-properties.spec.ts | 1 + src/file/footer-wrapper.spec.ts | 9 - src/file/footer-wrapper.ts | 7 +- src/file/footer/footer.spec.ts | 47 +++ src/file/header-wrapper.spec.ts | 11 - src/file/header-wrapper.ts | 7 +- src/file/header/header.spec.ts | 58 +++ src/file/media/image.ts | 13 - src/file/media/index.ts | 1 - src/file/media/media.spec.ts | 10 + src/file/paragraph/formatting/border.spec.ts | 93 ++++- src/file/paragraph/image.spec.ts | 39 -- src/file/paragraph/image.ts | 18 - src/file/paragraph/index.ts | 1 - src/file/paragraph/paragraph.ts | 8 - src/file/table/table-cell/table-cell.spec.ts | 333 +++++++++++++++++- .../table-cell-margin.spec.ts | 36 +- src/file/table/table-row/table-row.spec.ts | 128 ++++++- src/file/table/table.spec.ts | 41 ++- 21 files changed, 797 insertions(+), 148 deletions(-) create mode 100644 src/file/document/body/section-properties/footer-reference/footer-reference.spec.ts create mode 100644 src/file/document/body/section-properties/header-reference/header-reference.spec.ts create mode 100644 src/file/footer/footer.spec.ts create mode 100644 src/file/header/header.spec.ts delete mode 100644 src/file/media/image.ts delete mode 100644 src/file/paragraph/image.spec.ts delete mode 100644 src/file/paragraph/image.ts diff --git a/src/file/document/body/section-properties/footer-reference/footer-reference.spec.ts b/src/file/document/body/section-properties/footer-reference/footer-reference.spec.ts new file mode 100644 index 0000000000..50570b4b77 --- /dev/null +++ b/src/file/document/body/section-properties/footer-reference/footer-reference.spec.ts @@ -0,0 +1,42 @@ +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", + }, + }, + }); + }); +}); diff --git a/src/file/document/body/section-properties/header-reference/header-reference.spec.ts b/src/file/document/body/section-properties/header-reference/header-reference.spec.ts new file mode 100644 index 0000000000..2d6b39e553 --- /dev/null +++ b/src/file/document/body/section-properties/header-reference/header-reference.spec.ts @@ -0,0 +1,42 @@ +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", + }, + }, + }); + }); +}); diff --git a/src/file/document/body/section-properties/section-properties.spec.ts b/src/file/document/body/section-properties/section-properties.spec.ts index 31828ff5f4..abdbf9e1ed 100644 --- a/src/file/document/body/section-properties/section-properties.spec.ts +++ b/src/file/document/body/section-properties/section-properties.spec.ts @@ -38,6 +38,7 @@ describe("SectionProperties", () => { }, pageNumberStart: 10, pageNumberFormatType: PageNumberFormat.CARDINAL_TEXT, + titlePage: true, }); const tree = new Formatter().format(properties); expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]); diff --git a/src/file/footer-wrapper.spec.ts b/src/file/footer-wrapper.spec.ts index 899bd22e70..4f3e95acdb 100644 --- a/src/file/footer-wrapper.spec.ts +++ b/src/file/footer-wrapper.spec.ts @@ -35,15 +35,6 @@ describe("FooterWrapper", () => { expect(spy.called).to.equal(true); }); - - it("should call the underlying footer's addImage", () => { - const file = new FooterWrapper(new Media(), 1); - const spy = sinon.spy(file.Footer, "add"); - // tslint:disable-next-line:no-any - file.addImage({} as any); - - expect(spy.called).to.equal(true); - }); }); describe("#addChildElement", () => { diff --git a/src/file/footer-wrapper.ts b/src/file/footer-wrapper.ts index 01087e9861..8390887ba4 100644 --- a/src/file/footer-wrapper.ts +++ b/src/file/footer-wrapper.ts @@ -2,7 +2,7 @@ import { XmlComponent } from "file/xml-components"; import { FooterReferenceType } from "./document"; import { Footer } from "./footer/footer"; -import { Image, Media } from "./media"; +import { Media } from "./media"; import { Paragraph } from "./paragraph"; import { Relationships } from "./relationships"; import { Table } from "./table"; @@ -25,11 +25,6 @@ export class FooterWrapper { this.footer.add(item); } - public addImage(image: Image): FooterWrapper { - this.footer.add(image.Paragraph); - return this; - } - public addChildElement(childElement: XmlComponent): void { this.footer.addChildElement(childElement); } diff --git a/src/file/footer/footer.spec.ts b/src/file/footer/footer.spec.ts new file mode 100644 index 0000000000..4f970a119a --- /dev/null +++ b/src/file/footer/footer.spec.ts @@ -0,0 +1,47 @@ +import { expect } from "chai"; + +import { Formatter } from "export/formatter"; + +import { Paragraph } from "../paragraph"; +import { Footer } from "./footer"; + +describe("Footer", () => { + it("should create", () => { + const footer = new Footer(1); + + const tree = new Formatter().format(footer); + + expect(tree).to.deep.equal({ + "w:ftr": { + _attr: { + "xmlns:m": "http://schemas.openxmlformats.org/officeDocument/2006/math", + "xmlns:mc": "http://schemas.openxmlformats.org/markup-compatibility/2006", + "xmlns:o": "urn:schemas-microsoft-com:office:office", + "xmlns:r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships", + "xmlns:v": "urn:schemas-microsoft-com:vml", + "xmlns:w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main", + "xmlns:w10": "urn:schemas-microsoft-com:office:word", + "xmlns:w14": "http://schemas.microsoft.com/office/word/2010/wordml", + "xmlns:w15": "http://schemas.microsoft.com/office/word/2012/wordml", + "xmlns:wne": "http://schemas.microsoft.com/office/word/2006/wordml", + "xmlns:wp": "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing", + "xmlns:wp14": "http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing", + "xmlns:wpc": "http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas", + "xmlns:wpg": "http://schemas.microsoft.com/office/word/2010/wordprocessingGroup", + "xmlns:wpi": "http://schemas.microsoft.com/office/word/2010/wordprocessingInk", + "xmlns:wps": "http://schemas.microsoft.com/office/word/2010/wordprocessingShape", + }, + }, + }); + }); + + it("should create with initContent", () => { + const header = new Footer(1, new Paragraph({})); + + const tree = new Formatter().format(header); + + expect(tree).to.deep.equal({ + "w:ftr": {}, + }); + }); +}); diff --git a/src/file/header-wrapper.spec.ts b/src/file/header-wrapper.spec.ts index 7c3073a094..00ee776a95 100644 --- a/src/file/header-wrapper.spec.ts +++ b/src/file/header-wrapper.spec.ts @@ -37,17 +37,6 @@ describe("HeaderWrapper", () => { }); }); - describe("#addImage", () => { - it("should call the underlying header's addImage", () => { - const file = new HeaderWrapper(new Media(), 1); - const spy = sinon.spy(file.Header, "add"); - // tslint:disable-next-line:no-any - file.addImage({} as any); - - expect(spy.called).to.equal(true); - }); - }); - describe("#addChildElement", () => { it("should call the underlying header's addChildElement", () => { const file = new HeaderWrapper(new Media(), 1); diff --git a/src/file/header-wrapper.ts b/src/file/header-wrapper.ts index d698505e4a..407399a8fe 100644 --- a/src/file/header-wrapper.ts +++ b/src/file/header-wrapper.ts @@ -2,7 +2,7 @@ import { XmlComponent } from "file/xml-components"; import { HeaderReferenceType } from "./document"; import { Header } from "./header/header"; -import { Image, Media } from "./media"; +import { Media } from "./media"; import { Paragraph } from "./paragraph"; import { Relationships } from "./relationships"; import { Table } from "./table"; @@ -27,11 +27,6 @@ export class HeaderWrapper { return this; } - public addImage(image: Image): HeaderWrapper { - this.header.add(image.Paragraph); - return this; - } - public addChildElement(childElement: XmlComponent | string): void { this.header.addChildElement(childElement); } diff --git a/src/file/header/header.spec.ts b/src/file/header/header.spec.ts new file mode 100644 index 0000000000..651c21b545 --- /dev/null +++ b/src/file/header/header.spec.ts @@ -0,0 +1,58 @@ +import { expect } from "chai"; + +import { Formatter } from "export/formatter"; + +import { Paragraph } from "../paragraph"; +import { Header } from "./header"; + +describe("Header", () => { + it("should create", () => { + const header = new Header(1); + + const tree = new Formatter().format(header); + + expect(tree).to.deep.equal({ + "w:hdr": { + _attr: { + "xmlns:cx": "http://schemas.microsoft.com/office/drawing/2014/chartex", + "xmlns:cx1": "http://schemas.microsoft.com/office/drawing/2015/9/8/chartex", + "xmlns:cx2": "http://schemas.microsoft.com/office/drawing/2015/10/21/chartex", + "xmlns:cx3": "http://schemas.microsoft.com/office/drawing/2016/5/9/chartex", + "xmlns:cx4": "http://schemas.microsoft.com/office/drawing/2016/5/10/chartex", + "xmlns:cx5": "http://schemas.microsoft.com/office/drawing/2016/5/11/chartex", + "xmlns:cx6": "http://schemas.microsoft.com/office/drawing/2016/5/12/chartex", + "xmlns:cx7": "http://schemas.microsoft.com/office/drawing/2016/5/13/chartex", + "xmlns:cx8": "http://schemas.microsoft.com/office/drawing/2016/5/14/chartex", + "xmlns:m": "http://schemas.openxmlformats.org/officeDocument/2006/math", + "xmlns:mc": "http://schemas.openxmlformats.org/markup-compatibility/2006", + "xmlns:o": "urn:schemas-microsoft-com:office:office", + "xmlns:r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships", + "xmlns:v": "urn:schemas-microsoft-com:vml", + "xmlns:w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main", + "xmlns:w10": "urn:schemas-microsoft-com:office:word", + "xmlns:w14": "http://schemas.microsoft.com/office/word/2010/wordml", + "xmlns:w15": "http://schemas.microsoft.com/office/word/2012/wordml", + "xmlns:w16cid": "http://schemas.microsoft.com/office/word/2016/wordml/cid", + "xmlns:w16se": "http://schemas.microsoft.com/office/word/2015/wordml/symex", + "xmlns:wne": "http://schemas.microsoft.com/office/word/2006/wordml", + "xmlns:wp": "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing", + "xmlns:wp14": "http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing", + "xmlns:wpc": "http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas", + "xmlns:wpg": "http://schemas.microsoft.com/office/word/2010/wordprocessingGroup", + "xmlns:wpi": "http://schemas.microsoft.com/office/word/2010/wordprocessingInk", + "xmlns:wps": "http://schemas.microsoft.com/office/word/2010/wordprocessingShape", + }, + }, + }); + }); + + it("should create with initContent", () => { + const header = new Header(1, new Paragraph({})); + + const tree = new Formatter().format(header); + + expect(tree).to.deep.equal({ + "w:hdr": {}, + }); + }); +}); diff --git a/src/file/media/image.ts b/src/file/media/image.ts deleted file mode 100644 index 8255fe7398..0000000000 --- a/src/file/media/image.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { ImageParagraph, PictureRun } from "../paragraph"; - -export class Image { - constructor(private readonly paragraph: ImageParagraph) {} - - public get Paragraph(): ImageParagraph { - return this.paragraph; - } - - public get Run(): PictureRun { - return this.paragraph.Run; - } -} diff --git a/src/file/media/index.ts b/src/file/media/index.ts index 2ccc436e68..3575274e26 100644 --- a/src/file/media/index.ts +++ b/src/file/media/index.ts @@ -1,3 +1,2 @@ export * from "./media"; export * from "./data"; -export * from "./image"; diff --git a/src/file/media/media.spec.ts b/src/file/media/media.spec.ts index b7ce0c106c..e566213ee5 100644 --- a/src/file/media/media.spec.ts +++ b/src/file/media/media.spec.ts @@ -80,6 +80,16 @@ describe("Media", () => { const image = new Media().addMedia(""); expect(image.stream).to.be.an.instanceof(Uint8Array); }); + + it("should use data as is if its not a string", () => { + // tslint:disable-next-line + ((process as any).atob as any) = () => "atob result"; + // tslint:disable-next-line:no-any + (Media as any).generateId = () => "test"; + + const image = new Media().addMedia(new Buffer("")); + expect(image.stream).to.be.an.instanceof(Uint8Array); + }); }); describe("#getMedia", () => { diff --git a/src/file/paragraph/formatting/border.spec.ts b/src/file/paragraph/formatting/border.spec.ts index f07a297479..6e89ffcfc8 100644 --- a/src/file/paragraph/formatting/border.spec.ts +++ b/src/file/paragraph/formatting/border.spec.ts @@ -1,11 +1,87 @@ -import { assert, expect } from "chai"; +import { expect } from "chai"; import { Formatter } from "export/formatter"; -import { ThematicBreak } from "./border"; +import { Border, ThematicBreak } from "./border"; describe("Border", () => { - // TODO: Need tests here + describe("#constructor", () => { + it("should create", () => { + const border = new Border({ + top: { + color: "red", + space: 1, + value: "test", + size: 2, + }, + bottom: { + color: "red", + space: 3, + value: "test", + size: 4, + }, + left: { + color: "red", + space: 5, + value: "test", + size: 6, + }, + right: { + color: "red", + space: 7, + value: "test", + size: 8, + }, + }); + + const tree = new Formatter().format(border); + + expect(tree).to.deep.equal({ + "w:pBdr": [ + { + "w:top": { + _attr: { + "w:color": "red", + "w:space": 1, + "w:sz": 2, + "w:val": "test", + }, + }, + }, + { + "w:bottom": { + _attr: { + "w:color": "red", + "w:space": 3, + "w:sz": 4, + "w:val": "test", + }, + }, + }, + { + "w:left": { + _attr: { + "w:color": "red", + "w:space": 5, + "w:sz": 6, + "w:val": "test", + }, + }, + }, + { + "w:right": { + _attr: { + "w:color": "red", + "w:space": 7, + "w:sz": 8, + "w:val": "test", + }, + }, + }, + ], + }); + }); + }); }); describe("ThematicBreak", () => { @@ -16,17 +92,6 @@ describe("ThematicBreak", () => { }); describe("#constructor()", () => { - it("should create valid JSON", () => { - const stringifiedJson = JSON.stringify(thematicBreak); - - try { - JSON.parse(stringifiedJson); - } catch (e) { - assert.isTrue(false); - } - assert.isTrue(true); - }); - it("should create a Thematic Break with correct border properties", () => { const tree = new Formatter().format(thematicBreak); expect(tree).to.deep.equal({ diff --git a/src/file/paragraph/image.spec.ts b/src/file/paragraph/image.spec.ts deleted file mode 100644 index 715c1c93ab..0000000000 --- a/src/file/paragraph/image.spec.ts +++ /dev/null @@ -1,39 +0,0 @@ -// tslint:disable:object-literal-key-quotes -import { assert } from "chai"; - -import { ImageParagraph } from "./image"; - -describe("Image", () => { - let image: ImageParagraph; - - beforeEach(() => { - image = new ImageParagraph({ - stream: new Buffer(""), - path: "", - fileName: "test.png", - dimensions: { - pixels: { - x: 10, - y: 10, - }, - emus: { - x: 10, - y: 10, - }, - }, - }); - }); - - describe("#constructor()", () => { - it("should create valid JSON", () => { - const stringifiedJson = JSON.stringify(image); - - try { - JSON.parse(stringifiedJson); - } catch (e) { - assert.isTrue(false); - } - assert.isTrue(true); - }); - }); -}); diff --git a/src/file/paragraph/image.ts b/src/file/paragraph/image.ts deleted file mode 100644 index 4fa3d97b9f..0000000000 --- a/src/file/paragraph/image.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { IDrawingOptions } from "../drawing"; -import { IMediaData } from "../media"; -import { Paragraph } from "./paragraph"; -import { PictureRun } from "./run"; - -export class ImageParagraph extends Paragraph { - private readonly pictureRun: PictureRun; - - constructor(imageData: IMediaData, drawingOptions?: IDrawingOptions) { - super({}); - this.pictureRun = new PictureRun(imageData, drawingOptions); - this.root.push(this.pictureRun); - } - - public get Run(): PictureRun { - return this.pictureRun; - } -} diff --git a/src/file/paragraph/index.ts b/src/file/paragraph/index.ts index 222cb1bf4f..68a1b0b800 100644 --- a/src/file/paragraph/index.ts +++ b/src/file/paragraph/index.ts @@ -3,4 +3,3 @@ export * from "./paragraph"; export * from "./properties"; export * from "./run"; export * from "./links"; -export * from "./image"; diff --git a/src/file/paragraph/paragraph.ts b/src/file/paragraph/paragraph.ts index 3f39ba68bc..24c3e1717d 100644 --- a/src/file/paragraph/paragraph.ts +++ b/src/file/paragraph/paragraph.ts @@ -1,6 +1,5 @@ // http://officeopenxml.com/WPparagraph.php import { FootnoteReferenceRun } from "file/footnotes/footnote/run/reference-run"; -import { Image } from "file/media"; import { Num } from "file/numbering/num"; import { XmlComponent } from "file/xml-components"; @@ -190,13 +189,6 @@ export class Paragraph extends XmlComponent { return this; } - public addImage(image: Image): PictureRun { - const run = image.Run; - this.addRun(run); - - return run; - } - public pageBreak(): Paragraph { this.root.push(new PageBreak()); return this; diff --git a/src/file/table/table-cell/table-cell.spec.ts b/src/file/table/table-cell/table-cell.spec.ts index 8ac54ed85a..4dd86ffc9c 100644 --- a/src/file/table/table-cell/table-cell.spec.ts +++ b/src/file/table/table-cell/table-cell.spec.ts @@ -3,7 +3,9 @@ import { expect } from "chai"; import { Formatter } from "export/formatter"; import { BorderStyle } from "file/styles"; -import { TableCellBorders, TableCellWidth, WidthType } from "./table-cell-components"; +import { ShadingType } from "../shading"; +import { TableCell } from "./table-cell"; +import { TableCellBorders, TableCellWidth, VerticalAlign, VerticalMergeType, WidthType } from "./table-cell-components"; describe("TableCellBorders", () => { describe("#prepForXml", () => { @@ -222,3 +224,332 @@ describe("TableCellWidth", () => { }); }); }); + +describe("TableCell", () => { + describe("#constructor", () => { + it("should create", () => { + const cell = new TableCell({ + children: [], + }); + + const tree = new Formatter().format(cell); + + expect(tree).to.deep.equal({ + "w:tc": [ + { + "w:p": {}, + }, + ], + }); + }); + + it("should create with vertical align", () => { + const cell = new TableCell({ + children: [], + verticalAlign: VerticalAlign.CENTER, + }); + + const tree = new Formatter().format(cell); + + expect(tree).to.deep.equal({ + "w:tc": [ + { + "w:tcPr": [ + { + "w:vAlign": { + _attr: { + "w:val": "center", + }, + }, + }, + ], + }, + { + "w:p": {}, + }, + ], + }); + }); + + it("should create with vertical merge", () => { + const cell = new TableCell({ + children: [], + verticalMerge: VerticalMergeType.RESTART, + }); + + const tree = new Formatter().format(cell); + + expect(tree).to.deep.equal({ + "w:tc": [ + { + "w:tcPr": [ + { + "w:vMerge": { + _attr: { + "w:val": "restart", + }, + }, + }, + ], + }, + { + "w:p": {}, + }, + ], + }); + }); + + it("should create with margins", () => { + const cell = new TableCell({ + children: [], + margins: { + top: 1, + left: 1, + bottom: 1, + right: 1, + }, + }); + + const tree = new Formatter().format(cell); + + expect(tree).to.deep.equal({ + "w:tc": [ + { + "w:tcPr": [ + { + "w:tcMar": [ + { + "w:top": { + _attr: { + "w:type": "dxa", + "w:w": 1, + }, + }, + }, + { + "w:bottom": { + _attr: { + "w:type": "dxa", + "w:w": 1, + }, + }, + }, + { + "w:end": { + _attr: { + "w:type": "dxa", + "w:w": 1, + }, + }, + }, + { + "w:start": { + _attr: { + "w:type": "dxa", + "w:w": 1, + }, + }, + }, + ], + }, + ], + }, + { + "w:p": {}, + }, + ], + }); + }); + + it("should create with shading", () => { + const cell = new TableCell({ + children: [], + shading: { + fill: "red", + color: "blue", + val: ShadingType.PERCENT_10, + }, + }); + + const tree = new Formatter().format(cell); + + expect(tree).to.deep.equal({ + "w:tc": [ + { + "w:tcPr": [ + { + "w:shd": { + _attr: { + "w:color": "blue", + "w:fill": "red", + "w:val": "pct10", + }, + }, + }, + ], + }, + { + "w:p": {}, + }, + ], + }); + }); + + it("should create with column span", () => { + const cell = new TableCell({ + children: [], + columnSpan: 2, + }); + + const tree = new Formatter().format(cell); + + expect(tree).to.deep.equal({ + "w:tc": [ + { + "w:tcPr": [ + { + "w:gridSpan": { + _attr: { + "w:val": 2, + }, + }, + }, + ], + }, + { + "w:p": {}, + }, + ], + }); + }); + + describe("rowSpan", () => { + it("should not create with row span if its less than 1", () => { + const cell = new TableCell({ + children: [], + rowSpan: 0, + }); + + const tree = new Formatter().format(cell); + + expect(tree).to.deep.equal({ + "w:tc": [ + { + "w:p": {}, + }, + ], + }); + }); + + it("should create with row span if its greater than 1", () => { + const cell = new TableCell({ + children: [], + rowSpan: 2, + }); + + const tree = new Formatter().format(cell); + + expect(tree).to.deep.equal({ + "w:tc": [ + { + "w:tcPr": [ + { + "w:vMerge": { + _attr: { + "w:val": "restart", + }, + }, + }, + ], + }, + { + "w:p": {}, + }, + ], + }); + }); + + it("should create with borders", () => { + const cell = new TableCell({ + children: [], + borders: { + top: { + style: BorderStyle.DASH_DOT_STROKED, + size: 3, + color: "red", + }, + bottom: { + style: BorderStyle.DOUBLE, + size: 3, + color: "blue", + }, + left: { + style: BorderStyle.DASH_DOT_STROKED, + size: 3, + color: "green", + }, + right: { + style: BorderStyle.DASH_DOT_STROKED, + size: 3, + color: "#ff8000", + }, + }, + }); + + const tree = new Formatter().format(cell); + + expect(tree).to.deep.equal({ + "w:tc": [ + { + "w:tcPr": [ + { + "w:tcBorders": [ + { + "w:top": { + _attr: { + "w:color": "red", + "w:sz": 3, + "w:val": "dashDotStroked", + }, + }, + }, + { + "w:bottom": { + _attr: { + "w:color": "blue", + "w:sz": 3, + "w:val": "double", + }, + }, + }, + { + "w:left": { + _attr: { + "w:color": "green", + "w:sz": 3, + "w:val": "dashDotStroked", + }, + }, + }, + { + "w:right": { + _attr: { + "w:color": "#ff8000", + "w:sz": 3, + "w:val": "dashDotStroked", + }, + }, + }, + ], + }, + ], + }, + { + "w:p": {}, + }, + ], + }); + }); + }); + }); +}); diff --git a/src/file/table/table-properties/table-cell-margin.spec.ts b/src/file/table/table-properties/table-cell-margin.spec.ts index 7f0d13d218..2a89db7a4a 100644 --- a/src/file/table/table-properties/table-cell-margin.spec.ts +++ b/src/file/table/table-properties/table-cell-margin.spec.ts @@ -14,38 +14,66 @@ describe("TableCellMargin", () => { }); describe("#addTopMargin", () => { - it("adds a table cell top margin", () => { + it("should add a table cell top margin", () => { const cellMargin = new TableCellMargin(); cellMargin.addTopMargin(1234, WidthType.DXA); const tree = new Formatter().format(cellMargin); expect(tree).to.deep.equal({ "w:tblCellMar": [{ "w:top": { _attr: { "w:type": "dxa", "w:w": 1234 } } }] }); }); + + it("should add a table cell top margin using default width type", () => { + const cellMargin = new TableCellMargin(); + cellMargin.addTopMargin(1234); + const tree = new Formatter().format(cellMargin); + expect(tree).to.deep.equal({ "w:tblCellMar": [{ "w:top": { _attr: { "w:type": "dxa", "w:w": 1234 } } }] }); + }); }); describe("#addLeftMargin", () => { - it("adds a table cell left margin", () => { + it("should add a table cell left margin", () => { const cellMargin = new TableCellMargin(); cellMargin.addLeftMargin(1234, WidthType.DXA); const tree = new Formatter().format(cellMargin); expect(tree).to.deep.equal({ "w:tblCellMar": [{ "w:left": { _attr: { "w:type": "dxa", "w:w": 1234 } } }] }); }); + + it("should add a table cell left margin using default width type", () => { + const cellMargin = new TableCellMargin(); + cellMargin.addLeftMargin(1234); + const tree = new Formatter().format(cellMargin); + expect(tree).to.deep.equal({ "w:tblCellMar": [{ "w:left": { _attr: { "w:type": "dxa", "w:w": 1234 } } }] }); + }); }); describe("#addBottomMargin", () => { - it("adds a table cell bottom margin", () => { + it("should add a table cell bottom margin", () => { const cellMargin = new TableCellMargin(); cellMargin.addBottomMargin(1234, WidthType.DXA); const tree = new Formatter().format(cellMargin); expect(tree).to.deep.equal({ "w:tblCellMar": [{ "w:bottom": { _attr: { "w:type": "dxa", "w:w": 1234 } } }] }); }); + + it("should add a table cell bottom margin using default width type", () => { + const cellMargin = new TableCellMargin(); + cellMargin.addBottomMargin(1234); + const tree = new Formatter().format(cellMargin); + expect(tree).to.deep.equal({ "w:tblCellMar": [{ "w:bottom": { _attr: { "w:type": "dxa", "w:w": 1234 } } }] }); + }); }); describe("#addRightMargin", () => { - it("adds a table cell right margin", () => { + it("should add a table cell right margin", () => { const cellMargin = new TableCellMargin(); cellMargin.addRightMargin(1234, WidthType.DXA); const tree = new Formatter().format(cellMargin); expect(tree).to.deep.equal({ "w:tblCellMar": [{ "w:right": { _attr: { "w:type": "dxa", "w:w": 1234 } } }] }); }); + + it("should add a table cell right margin using default width type", () => { + const cellMargin = new TableCellMargin(); + cellMargin.addRightMargin(1234); + const tree = new Formatter().format(cellMargin); + expect(tree).to.deep.equal({ "w:tblCellMar": [{ "w:right": { _attr: { "w:type": "dxa", "w:w": 1234 } } }] }); + }); }); }); diff --git a/src/file/table/table-row/table-row.spec.ts b/src/file/table/table-row/table-row.spec.ts index 3c732c6217..e013153cd8 100644 --- a/src/file/table/table-row/table-row.spec.ts +++ b/src/file/table/table-row/table-row.spec.ts @@ -2,6 +2,7 @@ import { expect } from "chai"; import { Formatter } from "export/formatter"; +import { Paragraph } from "file/paragraph"; import { HeightRule } from "file/table/table-row/table-row-height"; import { EMPTY_OBJECT } from "file/xml-components"; import { TableCell } from "../table-cell"; @@ -41,6 +42,52 @@ describe("TableRow", () => { }); }); + it("should create with cant split", () => { + const tableRow = new TableRow({ + children: [], + cantSplit: true, + }); + const tree = new Formatter().format(tableRow); + expect(tree).to.deep.equal({ + "w:tr": [ + { + "w:trPr": [ + { + "w:cantSplit": { + _attr: { + "w:val": true, + }, + }, + }, + ], + }, + ], + }); + }); + + it("should create with table header", () => { + const tableRow = new TableRow({ + children: [], + tableHeader: true, + }); + const tree = new Formatter().format(tableRow); + expect(tree).to.deep.equal({ + "w:tr": [ + { + "w:trPr": [ + { + "w:tblHeader": { + _attr: { + "w:val": true, + }, + }, + }, + ], + }, + ], + }); + }); + it("should set row height", () => { const tableRow = new TableRow({ children: [], @@ -69,21 +116,70 @@ describe("TableRow", () => { }); }); - // describe("#mergeCells", () => { - // it("should merge the cell", () => { - // const tableRow = new TableRow({ - // children: [ - // new TableCell({ - // children: [], - // }), - // new TableCell({ - // children: [], - // }), - // ], - // }); + describe("#addCellToIndex", () => { + it("should add cell to correct index with no initial properties", () => { + const tableRow = new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("test")], + }), + ], + tableHeader: true, + }); - // tableRow.mergeCells(0, 1); - // expect(() => tableRow.getCell(1)).to.throw(); - // }); - // }); + tableRow.addCellToIndex( + new TableCell({ + children: [], + }), + 0, + ); + + const tree = new Formatter().format(tableRow); + + expect(tree).to.deep.equal({ + "w:tr": [ + { + "w:trPr": [ + { + "w:tblHeader": { + _attr: { + "w:val": true, + }, + }, + }, + ], + }, + { + "w:tc": [ + { + "w:p": {}, + }, + ], + }, + { + "w:tc": [ + { + "w:p": [ + { + "w:r": [ + { + "w:t": [ + { + _attr: { + "xml:space": "preserve", + }, + }, + "test", + ], + }, + ], + }, + ], + }, + ], + }, + ], + }); + }); + }); }); diff --git a/src/file/table/table.spec.ts b/src/file/table/table.spec.ts index b88bda964a..d2aec26051 100644 --- a/src/file/table/table.spec.ts +++ b/src/file/table/table.spec.ts @@ -8,7 +8,7 @@ import { Table } from "./table"; // import { WidthType } from "./table-cell"; import { RelativeHorizontalPosition, RelativeVerticalPosition, TableAnchorType } from "./table-properties"; -import { TableCell } from "./table-cell"; +import { TableCell, WidthType } from "./table-cell"; import { TableLayoutType } from "./table-properties/table-layout"; import { TableRow } from "./table-row"; @@ -210,6 +210,45 @@ describe("Table", () => { "w:tblPr": [DEFAULT_TABLE_PROPERTIES, BORDERS, WIDTHS, { "w:tblLayout": { _attr: { "w:type": "fixed" } } }], }); }); + + it("should set the table to provided width", () => { + const table = new Table({ + rows: [ + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("hello")], + }), + ], + }), + ], + width: { + size: 100, + type: WidthType.PERCENTAGE, + }, + layout: TableLayoutType.FIXED, + }); + 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": [ + DEFAULT_TABLE_PROPERTIES, + BORDERS, + { + "w:tblW": { + _attr: { + "w:type": "pct", + "w:w": "100%", + }, + }, + }, + { "w:tblLayout": { _attr: { "w:type": "fixed" } } }, + ], + }); + }); }); describe("Cell", () => { From 59be3812137ff8cd6d855e8deab7cdca19208bae Mon Sep 17 00:00:00 2001 From: Dolan Date: Sun, 29 Sep 2019 04:25:40 +0100 Subject: [PATCH 13/14] Update travis config --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 3c015ad296..3bf1b47690 100644 --- a/.travis.yml +++ b/.travis.yml @@ -42,7 +42,7 @@ script: - npm run ts-node -- ./demo/29-numbered-lists.ts - npm run ts-node -- ./demo/30-template-document.ts - npm run ts-node -- ./demo/31-tables.ts - - npm run ts-node -- ./demo/32-merge-table-cells.ts + - npm run ts-node -- ./demo/32-merge-and-shade-table-cells.ts # - npm run e2e "My Document.docx" // Need to fix - npm run ts-node -- ./demo/33-sequential-captions.ts - npm run ts-node -- ./demo/34-floating-tables.ts From b43ed12c8476e34cc56e33f097d85515405332ae Mon Sep 17 00:00:00 2001 From: Dolan Date: Sun, 29 Sep 2019 04:38:07 +0100 Subject: [PATCH 14/14] Fix tests --- demo/32-merge-and-shade-table-cells.ts | 24 ++++++++++++++++-------- demo/34-floating-tables.ts | 6 ++++-- demo/44-multiple-columns.ts | 4 ++-- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/demo/32-merge-and-shade-table-cells.ts b/demo/32-merge-and-shade-table-cells.ts index 1112931079..77d6815d1e 100644 --- a/demo/32-merge-and-shade-table-cells.ts +++ b/demo/32-merge-and-shade-table-cells.ts @@ -58,8 +58,10 @@ const table2 = new Table({ ], }), ], - width: 100, - widthUnitType: WidthType.AUTO, + width: { + size: 100, + type: WidthType.AUTO, + }, columnWidths: [1000, 1000, 1000], }); @@ -113,8 +115,10 @@ const table3 = new Table({ ], }), ], - width: 7000, - widthUnitType: WidthType.DXA, + width: { + size: 7000, + type: WidthType.DXA, + }, margins: { top: 400, bottom: 400, @@ -152,8 +156,10 @@ const table4 = new Table({ ], }), ], - width: 100, - widthUnitType: WidthType.PERCENTAGE, + width: { + size: 100, + type: WidthType.PERCENTAGE, + }, }); const table5 = new Table({ @@ -194,8 +200,10 @@ const table5 = new Table({ ], }), ], - width: 100, - widthUnitType: WidthType.PERCENTAGE, + width: { + size: 100, + type: WidthType.PERCENTAGE, + }, }); doc.addSection({ diff --git a/demo/34-floating-tables.ts b/demo/34-floating-tables.ts index 4a20ae2eee..dadf80f9da 100644 --- a/demo/34-floating-tables.ts +++ b/demo/34-floating-tables.ts @@ -44,8 +44,10 @@ const table = new Table({ relativeHorizontalPosition: RelativeHorizontalPosition.RIGHT, relativeVerticalPosition: RelativeVerticalPosition.BOTTOM, }, - width: 4535, - widthUnitType: WidthType.DXA, + width: { + size: 4535, + type: WidthType.DXA, + }, layout: TableLayoutType.FIXED, }); diff --git a/demo/44-multiple-columns.ts b/demo/44-multiple-columns.ts index 3fdf328461..3985486620 100644 --- a/demo/44-multiple-columns.ts +++ b/demo/44-multiple-columns.ts @@ -8,7 +8,7 @@ const doc = new Document(); doc.addSection({ properties: { column: { - width: 708, + space: 708, count: 2, }, }, @@ -23,7 +23,7 @@ doc.addSection({ doc.addSection({ properties: { column: { - width: 708, + space: 708, count: 3, }, },