diff --git a/demo/demo43.ts b/demo/demo43.ts new file mode 100644 index 0000000000..41c0747164 --- /dev/null +++ b/demo/demo43.ts @@ -0,0 +1,17 @@ +// 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 } from "../build"; + +const doc = new Document(); + +const table = doc.createTable(4, 4); +table.getCell(2, 2).addParagraph(new Paragraph("Hello")); +table.getColumn(3).mergeCells(1, 2); +// table.getCell(3, 2).addParagraph(new Paragraph("Hello")); + +const packer = new Packer(); + +packer.toBuffer(doc).then((buffer) => { + fs.writeFileSync("My Document.docx", buffer); +}); diff --git a/docs/_sidebar.md b/docs/_sidebar.md index a36b611623..558029dbf6 100644 --- a/docs/_sidebar.md +++ b/docs/_sidebar.md @@ -15,6 +15,7 @@ * [Headers & Footers](usage/headers-and-footers.md) * [Bullet Points](usage/bullet-points.md) * [Numbering](usage/numbering.md) + * [Tables](usage/tables.md) * [Tab Stops](usage/tab-stops.md) * [Table of Contents](usage/table-of-contents.md) * [Page Numbers](usage/page-numbers.md) diff --git a/docs/usage/tables.md b/docs/usage/tables.md index a129023217..3629757341 100644 --- a/docs/usage/tables.md +++ b/docs/usage/tables.md @@ -28,23 +28,69 @@ const table = new Table(2, 4); doc.addTable(table); ``` +## Rows and Columns + +You can get a row or a column from a table like so, where `index` is a number: + +### Get Row + +```ts +const row = doc.getRow(index); +``` + +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: + +```ts +row.mergeCells(startIndex, endIndex); +``` + +You can get a cell from a `row` by using the `getCell()` method, where `index` is the row index: + +```ts +row.getCell(index); +``` + +### Get Column + +```ts +const column = doc.getColumn(index); +``` + +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); +``` + ## Cells -The above section created a table with cells. To access the cell, use the `getCell` method. +The `createTable()` method created a table with cells. To access the cell, use the `getCell()` method. ```ts const cell = table.getCell([ROW INDEX], [COLUMN INDEX]); ``` +You can also get a cell from a `column` or a `row` with `getCell()`, mentioned previously. + For example: ```ts const cell = table.getCell(0, 2); + +const cell = row.getCell(0); + +const cell = column.getCell(2); ``` ### Add paragraph to a cell -Once you have got the cell, you can add data to it with the `addParagraph` method. +Once you have got the cell, you can add data to it with the `addParagraph()` method. ```ts cell.addParagraph(new Paragraph("Hello")); @@ -152,3 +198,58 @@ cell.addTable(new Table(1, 1)); [Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo4.ts ":include") _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo4.ts_ + +### Custom borders + +Example showing how to add colourful borders to tables + +[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo20.ts ":include") + +_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo20.ts_ + +### Adding images + +Example showing how to add images to tables + +[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo24.ts ":include") + +_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo24.ts_ + +[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/36.ts ":include") + +_Source: https://github.com/dolanmiu/docx/blob/master/demo/36.ts_ + +### Alignment of text in a cell + +Example showing how align text in a table cell + +[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo31.ts ":include") + +_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo31.ts_ + +### Merging rows + +Example showing merging of `rows` + +[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo32.ts ":include") + +_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo32.ts_ + +[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo41.ts ":include") + +_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo41.ts_ + +### Merging columns + +Example showing merging of `columns` + +[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo43.ts ":include") + +_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo43.ts_ + +### Floating tables + +[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo34.ts ":include") + +_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo34.ts_ + diff --git a/src/file/table/table-cell/table-cell.ts b/src/file/table/table-cell/table-cell.ts index 39fcbb83cc..8e78eaac23 100644 --- a/src/file/table/table-cell/table-cell.ts +++ b/src/file/table/table-cell/table-cell.ts @@ -3,7 +3,7 @@ import { Paragraph } from "file/paragraph"; import { IXmlableObject, XmlComponent } from "file/xml-components"; import { Table } from "../table"; -import { TableCellBorders, VerticalAlign } from "./table-cell-components"; +import { TableCellBorders, VerticalAlign, VMergeType } from "./table-cell-components"; import { TableCellProperties } from "./table-cell-properties"; export class TableCell extends XmlComponent { @@ -58,6 +58,12 @@ export class TableCell extends XmlComponent { return this; } + public addVerticalMerge(type: VMergeType): TableCell { + this.properties.addVerticalMerge(type); + + return this; + } + public get Borders(): TableCellBorders { return this.properties.Borders; } diff --git a/src/file/table/table-column.spec.ts b/src/file/table/table-column.spec.ts new file mode 100644 index 0000000000..a536bcd01b --- /dev/null +++ b/src/file/table/table-column.spec.ts @@ -0,0 +1,52 @@ +import { expect } from "chai"; + +import { Formatter } from "export/formatter"; + +import { TableCell } from "./table-cell"; +import { TableColumn } from "./table-column"; + +describe("TableColumn", () => { + let cells: TableCell[]; + beforeEach(() => { + cells = [new TableCell(), new TableCell(), new TableCell()]; + }); + + 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": [{ "w:pPr": [] }] }], + }); + + const tree2 = new Formatter().format(cells[1]); + expect(tree2).to.deep.equal({ "w:tc": [{ "w:tcPr": [] }, { "w:p": [{ "w:pPr": [] }] }] }); + + const tree3 = new Formatter().format(cells[2]); + expect(tree3).to.deep.equal({ + "w:tc": [{ "w:tcPr": [{ "w:vMerge": [{ _attr: { "w:val": "continue" } }] }] }, { "w:p": [{ "w:pPr": [] }] }], + }); + }); + }); +}); diff --git a/src/file/table/table-column.ts b/src/file/table/table-column.ts new file mode 100644 index 0000000000..b06a9f29a4 --- /dev/null +++ b/src/file/table/table-column.ts @@ -0,0 +1,22 @@ +import { TableCell, VMergeType } 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); + this.cells[endIndex].addVerticalMerge(VMergeType.CONTINUE); + + return this.cells[startIndex]; + } +} diff --git a/src/file/table/table-properties/table-properties.ts b/src/file/table/table-properties/table-properties.ts index c6184d70c1..f577cd56c9 100644 --- a/src/file/table/table-properties/table-properties.ts +++ b/src/file/table/table-properties/table-properties.ts @@ -17,7 +17,7 @@ export class TableProperties extends XmlComponent { this.root.push(this.cellMargin); } - public setWidth(width: number | string, type: WidthType = WidthType.AUTO): TableProperties { + public setWidth(width: number, type: WidthType = WidthType.AUTO): TableProperties { this.root.push(new PreferredTableWidth(type, width)); return this; } diff --git a/src/file/table/table-properties/table-width.ts b/src/file/table/table-properties/table-width.ts index 1c76f82c6b..957fe49094 100644 --- a/src/file/table/table-properties/table-width.ts +++ b/src/file/table/table-properties/table-width.ts @@ -1,3 +1,4 @@ +// http://officeopenxml.com/WPtableWidth.php import { XmlAttributeComponent, XmlComponent } from "file/xml-components"; import { WidthType } from "../table-cell"; @@ -12,7 +13,7 @@ class TableWidthAttributes extends XmlAttributeComponent { } export class PreferredTableWidth extends XmlComponent { - constructor(type: WidthType, w: number | string) { + constructor(type: WidthType, w: number) { super("w:tblW"); this.root.push(new TableWidthAttributes({ type, w })); } diff --git a/src/file/table/table.spec.ts b/src/file/table/table.spec.ts index 05a2c2e364..ae470abc4c 100644 --- a/src/file/table/table.spec.ts +++ b/src/file/table/table.spec.ts @@ -106,8 +106,9 @@ describe("Table", () => { }); describe("#getRow and Row#getCell", () => { - it("returns the correct row", () => { - const table = new Table(2, 2); + const table = new Table(2, 2); + + it("should return the correct row", () => { table .getRow(0) .getCell(0) @@ -144,10 +145,25 @@ describe("Table", () => { ], }); }); + + it("throws an exception if index is out of bounds", () => { + expect(() => table.getCell(9, 9)).to.throw(); + }); + }); + + describe("#getColumn", () => { + const table = new Table(2, 2); + + it("should get correct row", () => { + 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("returns the correct cell", () => { + it("should returns the correct cell", () => { const table = new Table(2, 2); table.getCell(0, 0).addParagraph(new Paragraph("A1")); table.getCell(0, 1).addParagraph(new Paragraph("B1")); diff --git a/src/file/table/table.ts b/src/file/table/table.ts index a4f8f5ac28..8721358cb8 100644 --- a/src/file/table/table.ts +++ b/src/file/table/table.ts @@ -3,6 +3,7 @@ import { XmlComponent } from "file/xml-components"; import { TableGrid } from "./grid"; import { TableCell, WidthType } from "./table-cell"; +import { TableColumn } from "./table-column"; import { ITableFloatOptions, TableProperties } from "./table-properties"; import { TableRow } from "./table-row"; @@ -51,8 +52,8 @@ export class Table extends XmlComponent { } } - public getRow(ix: number): TableRow { - const row = this.rows[ix]; + 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"); @@ -61,11 +62,17 @@ export class Table extends XmlComponent { 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); } - public setWidth(width: number | string, type: WidthType = WidthType.AUTO): Table { + public setWidth(width: number, type: WidthType = WidthType.AUTO): Table { this.properties.setWidth(width, type); return this; }