diff --git a/demo/32-merge-and-shade-table-cells.ts b/demo/32-merge-and-shade-table-cells.ts index 6fc7f867ec..f12350988e 100644 --- a/demo/32-merge-and-shade-table-cells.ts +++ b/demo/32-merge-and-shade-table-cells.ts @@ -287,6 +287,7 @@ const table7 = new Table({ }), new TableCell({ children: [new Paragraph("0,3")], + rowSpan: 3, }), ], }), @@ -296,9 +297,6 @@ const table7 = new Table({ children: [new Paragraph("1,0")], columnSpan: 2, }), - new TableCell({ - children: [new Paragraph("1,3")], - }), ], }), new TableRow({ @@ -311,9 +309,6 @@ const table7 = new Table({ children: [new Paragraph("2,2")], rowSpan: 2, }), - new TableCell({ - children: [new Paragraph("2,3")], - }), ], }), new TableRow({ @@ -336,6 +331,41 @@ const table7 = new Table({ }, }); +const table8 = new Table({ + rows: [ + new TableRow({ + children: [ + new TableCell({ children: [new Paragraph("1,1")] }), + new TableCell({ children: [new Paragraph("1,2")] }), + new TableCell({ children: [new Paragraph("1,3")] }), + new TableCell({ children: [new Paragraph("1,4")], rowSpan: 4, borders }), + ], + }), + new TableRow({ + children: [ + new TableCell({ children: [new Paragraph("2,1")] }), + new TableCell({ children: [new Paragraph("2,2")] }), + new TableCell({ children: [new Paragraph("2,3")], rowSpan: 3 }), + ], + }), + new TableRow({ + children: [ + new TableCell({ children: [new Paragraph("3,1")] }), + new TableCell({ children: [new Paragraph("3,2")], rowSpan: 2 }), + ], + }), + new TableRow({ + children: [ + new TableCell({ children: [new Paragraph("4,1")] }), + ], + }), + ], + width: { + size: 100, + type: WidthType.PERCENTAGE, + }, +}); + doc.addSection({ children: [ table, @@ -357,6 +387,8 @@ doc.addSection({ table6, new Paragraph("Merging columns 4"), table7, + new Paragraph("Merging columns 5"), + table8, ], }); diff --git a/src/file/table/table-cell/table-cell.ts b/src/file/table/table-cell/table-cell.ts index c809c157ad..babd0031ff 100644 --- a/src/file/table/table-cell/table-cell.ts +++ b/src/file/table/table-cell/table-cell.ts @@ -70,6 +70,9 @@ export class TableCell extends XmlComponent { if (options.verticalMerge) { this.properties.addVerticalMerge(options.verticalMerge); + } else if (options.rowSpan && options.rowSpan > 1) { + // if cell already have a `verticalMerge`, don't handle `rowSpan` + this.properties.addVerticalMerge(VerticalMergeType.RESTART); } if (options.margins) { @@ -84,10 +87,6 @@ export class TableCell extends XmlComponent { this.properties.addGridSpan(options.columnSpan); } - if (options.rowSpan && options.rowSpan > 1) { - this.properties.addVerticalMerge(VerticalMergeType.RESTART); - } - if (options.width) { this.properties.setWidth(options.width.size, options.width.type); } diff --git a/src/file/table/table-row/table-row.spec.ts b/src/file/table/table-row/table-row.spec.ts index 03b8b2862e..90f6ad4937 100644 --- a/src/file/table/table-row/table-row.spec.ts +++ b/src/file/table/table-row/table-row.spec.ts @@ -271,7 +271,8 @@ describe("TableRow", () => { }); expect(tableRow.columnIndexToRootIndex(8, true)).to.equal(5); - expect(() => tableRow.columnIndexToRootIndex(9, true)).to.throw(`cell 'columnIndex' should not great than 8`); + // for column 10, just place the new cell at the end of row + expect(tableRow.columnIndexToRootIndex(10, true)).to.equal(5); }); }); }); diff --git a/src/file/table/table-row/table-row.ts b/src/file/table/table-row/table-row.ts index 320616e809..11ea0bb2e2 100644 --- a/src/file/table/table-row/table-row.ts +++ b/src/file/table/table-row/table-row.ts @@ -83,10 +83,14 @@ export class TableRow extends XmlComponent { let colIdx = 0; // Offset because properties is also in root. let rootIdx = 1; - const endRootIndex = allowEndNewCell ? this.root.length : this.root.length - 1; while (colIdx <= columnIndex) { - if (rootIdx > endRootIndex) { - throw new Error(`cell 'columnIndex' should not great than ${colIdx - 1}`); + if (rootIdx >= this.root.length) { + if (allowEndNewCell) { + // for inserting verticalMerge CONTINUE cell at end of row + return this.root.length; + } else { + throw new Error(`cell 'columnIndex' should not great than ${colIdx - 1}`); + } } const cell = this.root[rootIdx] as TableCell; rootIdx += 1; diff --git a/src/file/table/table.ts b/src/file/table/table.ts index f99b6fc9b1..69b3c0f775 100644 --- a/src/file/table/table.ts +++ b/src/file/table/table.ts @@ -79,25 +79,26 @@ export class Table extends XmlComponent { } rows.forEach((row, rowIndex) => { - row.cells.forEach((cell, cellIndex) => { + if (rowIndex === rows.length - 1) { + // don't process the end row + return; + } + let columnIndex = 0; + row.cells.forEach((cell) => { // 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 columnIndex = row.rootIndexToColumnIndex(cellIndex + 1); - const startRowIndex = rowIndex + 1; - const endRowIndex = rowIndex + (cell.options.rowSpan - 1); - for (let i = startRowIndex; i <= endRowIndex; i++) { - rows[i].addCellToColumnIndex( - new TableCell({ - columnSpan: cell.options.columnSpan, - borders: cell.options.borders, - children: [], - verticalMerge: VerticalMergeType.CONTINUE, - }), - columnIndex, - ); - } + const continueCell = new TableCell({ + // the inserted CONTINUE cell has rowSpan, and will be handled when process the next row + rowSpan: cell.options.rowSpan - 1, + columnSpan: cell.options.columnSpan, + borders: cell.options.borders, + children: [], + verticalMerge: VerticalMergeType.CONTINUE, + }); + rows[rowIndex + 1].addCellToColumnIndex(continueCell, columnIndex); } + columnIndex += cell.options.columnSpan || 1; }); });