From 8c9b61b37aa3d4acaa43d1e1a03741a88fa6bd4a Mon Sep 17 00:00:00 2001 From: wangfengming Date: Mon, 22 Jun 2020 12:25:51 +0800 Subject: [PATCH] :fix: handle `rowSpan` by convert between the virtual column index and the root index --- demo/32-merge-and-shade-table-cells.ts | 87 +++++++++++++++++++--- src/file/table/table-row/table-row.spec.ts | 87 ++++++++++++++++++++++ src/file/table/table-row/table-row.ts | 40 ++++++++++ src/file/table/table.ts | 8 +- 4 files changed, 209 insertions(+), 13 deletions(-) diff --git a/demo/32-merge-and-shade-table-cells.ts b/demo/32-merge-and-shade-table-cells.ts index 615f97c46b..6fc7f867ec 100644 --- a/demo/32-merge-and-shade-table-cells.ts +++ b/demo/32-merge-and-shade-table-cells.ts @@ -184,7 +184,7 @@ const table5 = new Table({ new TableRow({ children: [ new TableCell({ - children: [], + children: [new Paragraph("1,0")], }), new TableCell({ children: [new Paragraph("1,2")], @@ -195,10 +195,10 @@ const table5 = new Table({ new TableRow({ children: [ new TableCell({ - children: [], + children: [new Paragraph("2,0")], }), new TableCell({ - children: [], + children: [new Paragraph("2,1")], }), ], }), @@ -238,12 +238,12 @@ const table6 = new Table({ children: [ new TableCell({ borders, - children: [new Paragraph("11"), new Paragraph("21")], + children: [new Paragraph("0,0")], rowSpan: 2, }), new TableCell({ borders, - children: [new Paragraph("12")], + children: [new Paragraph("0,1")], }), ], }), @@ -251,7 +251,7 @@ const table6 = new Table({ children: [ new TableCell({ borders, - children: [new Paragraph("22"), new Paragraph("32")], + children: [new Paragraph("1,1")], rowSpan: 2, }), ], @@ -260,7 +260,72 @@ const table6 = new Table({ children: [ new TableCell({ borders, - children: [new Paragraph("31")], + children: [new Paragraph("2,0")], + }), + ], + }), + ], + width: { + size: 100, + type: WidthType.PERCENTAGE, + }, +}); + +const table7 = new Table({ + rows: [ + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("0,0")], + }), + new TableCell({ + children: [new Paragraph("0,1")], + }), + new TableCell({ + children: [new Paragraph("0,2")], + rowSpan: 2, + }), + new TableCell({ + children: [new Paragraph("0,3")], + }), + ], + }), + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("1,0")], + columnSpan: 2, + }), + new TableCell({ + children: [new Paragraph("1,3")], + }), + ], + }), + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("2,0")], + columnSpan: 2, + }), + new TableCell({ + children: [new Paragraph("2,2")], + rowSpan: 2, + }), + new TableCell({ + children: [new Paragraph("2,3")], + }), + ], + }), + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("3,0")], + }), + new TableCell({ + children: [new Paragraph("3,1")], + }), + new TableCell({ + children: [new Paragraph("3,3")], }), ], }), @@ -284,12 +349,14 @@ doc.addSection({ heading: HeadingLevel.HEADING_2, }), table3, - new Paragraph("Merging columns"), + new Paragraph("Merging columns 1"), table4, - new Paragraph("More Merging columns"), + new Paragraph("Merging columns 2"), table5, - new Paragraph("Another Merging columns"), + new Paragraph("Merging columns 3"), table6, + new Paragraph("Merging columns 4"), + table7, ], }); diff --git a/src/file/table/table-row/table-row.spec.ts b/src/file/table/table-row/table-row.spec.ts index e013153cd8..0cdb0196b8 100644 --- a/src/file/table/table-row/table-row.spec.ts +++ b/src/file/table/table-row/table-row.spec.ts @@ -182,4 +182,91 @@ describe("TableRow", () => { }); }); }); + + describe("#rootIndexToColumnIndex", () => { + it("should get the correct virtual column index by root index", () => { + const tableRow = new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("test")], + columnSpan: 3, + }), + new TableCell({ + children: [new Paragraph("test")], + }), + new TableCell({ + children: [new Paragraph("test")], + }), + new TableCell({ + children: [new Paragraph("test")], + columnSpan: 3, + }), + ], + }); + + expect(tableRow.rootIndexToColumnIndex(1)).to.equal(0); + expect(tableRow.rootIndexToColumnIndex(2)).to.equal(3); + expect(tableRow.rootIndexToColumnIndex(3)).to.equal(4); + expect(tableRow.rootIndexToColumnIndex(4)).to.equal(5); + }); + }); + + describe("#columnIndexToRootIndex", () => { + it("should get the correct root index by virtual column index", () => { + const tableRow = new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("test")], + columnSpan: 3, + }), + new TableCell({ + children: [new Paragraph("test")], + }), + new TableCell({ + children: [new Paragraph("test")], + }), + new TableCell({ + children: [new Paragraph("test")], + columnSpan: 3, + }), + ], + }); + + expect(tableRow.columnIndexToRootIndex(0)).to.equal(1); + expect(tableRow.columnIndexToRootIndex(1)).to.equal(1); + expect(tableRow.columnIndexToRootIndex(2)).to.equal(1); + + expect(tableRow.columnIndexToRootIndex(3)).to.equal(2); + expect(tableRow.columnIndexToRootIndex(4)).to.equal(3); + + expect(tableRow.columnIndexToRootIndex(5)).to.equal(4); + expect(tableRow.columnIndexToRootIndex(6)).to.equal(4); + expect(tableRow.columnIndexToRootIndex(7)).to.equal(4); + }); + + it("should allow end new cell index", () => { + const tableRow = new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("test")], + columnSpan: 3, + }), + new TableCell({ + children: [new Paragraph("test")], + }), + new TableCell({ + children: [new Paragraph("test")], + }), + new TableCell({ + children: [new Paragraph("test")], + columnSpan: 3, + }), + ], + }); + + expect(() => tableRow.columnIndexToRootIndex(8)).to.throw(`cell 'columnIndex' should not great than 7`); + expect(tableRow.columnIndexToRootIndex(8, true)).to.equal(5); + expect(() => tableRow.columnIndexToRootIndex(9, true)).to.throw(`cell 'columnIndex' should not great than 8`); + }); + }); }); diff --git a/src/file/table/table-row/table-row.ts b/src/file/table/table-row/table-row.ts index 2a655a535d..320616e809 100644 --- a/src/file/table/table-row/table-row.ts +++ b/src/file/table/table-row/table-row.ts @@ -54,4 +54,44 @@ export class TableRow extends XmlComponent { // Offset because properties is also in root. this.root.splice(index + 1, 0, cell); } + + public addCellToColumnIndex(cell: TableCell, columnIndex: number): void { + const rootIndex = this.columnIndexToRootIndex(columnIndex, true); + this.addCellToIndex(cell, rootIndex - 1); + } + + public rootIndexToColumnIndex(rootIndex: number): number { + // convert the root index to the virtual column index + if (rootIndex < 1 || rootIndex >= this.root.length) { + throw new Error(`cell 'rootIndex' should between 1 to ${this.root.length - 1}`); + } + let colIdx = 0; + // Offset because properties is also in root. + for (let rootIdx = 1; rootIdx < rootIndex; rootIdx++) { + const cell = this.root[rootIdx] as TableCell; + colIdx += cell.options.columnSpan || 1; + } + return colIdx; + } + + public columnIndexToRootIndex(columnIndex: number, allowEndNewCell: boolean = false): number { + // convert the virtual column index to the root index + // `allowEndNewCell` for get index to inert new cell + if (columnIndex < 0) { + throw new Error(`cell 'columnIndex' should not less than zero`); + } + 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}`); + } + const cell = this.root[rootIdx] as TableCell; + rootIdx += 1; + colIdx += (cell && cell.options.columnSpan) || 1; + } + return rootIdx - 1; + } } diff --git a/src/file/table/table.ts b/src/file/table/table.ts index 888f91dca1..f99b6fc9b1 100644 --- a/src/file/table/table.ts +++ b/src/file/table/table.ts @@ -83,16 +83,18 @@ export class Table extends XmlComponent { // 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 = rowIndex + 1; i <= endRowIndex; i++) { - rows[i].addCellToIndex( + for (let i = startRowIndex; i <= endRowIndex; i++) { + rows[i].addCellToColumnIndex( new TableCell({ columnSpan: cell.options.columnSpan, borders: cell.options.borders, children: [], verticalMerge: VerticalMergeType.CONTINUE, }), - cellIndex, + columnIndex, ); } }