diff --git a/src/DocumentContext.js b/src/DocumentContext.js index 8329d002e..39ff2fb79 100644 --- a/src/DocumentContext.js +++ b/src/DocumentContext.js @@ -18,7 +18,6 @@ class DocumentContext extends EventEmitter { this.page = -1; this.snapshots = []; - this.endingCell = null; this.backgroundLength = []; this.addPage(pageSize); @@ -38,7 +37,6 @@ class DocumentContext extends EventEmitter { availableWidth: this.availableWidth, page: this.page }, - endingCell: this.endingCell, lastColumnWidth: this.lastColumnWidth }); @@ -48,9 +46,8 @@ class DocumentContext extends EventEmitter { beginColumn(width, offset, endingCell) { let saved = this.snapshots[this.snapshots.length - 1]; - this.calculateBottomMost(saved); + this.calculateBottomMost(saved, endingCell); - this.endingCell = endingCell; this.page = saved.page; this.x = this.x + this.lastColumnWidth + (offset || 0); this.y = saved.y; @@ -60,10 +57,9 @@ class DocumentContext extends EventEmitter { this.lastColumnWidth = width; } - calculateBottomMost(destContext) { - if (this.endingCell) { - this.saveContextInEndingCell(this.endingCell); - this.endingCell = null; + calculateBottomMost(destContext, endingCell) { + if (endingCell) { + this.saveContextInEndingCell(endingCell); } else { destContext.bottomMost = bottomMostContext(this, destContext.bottomMost); } @@ -89,12 +85,11 @@ class DocumentContext extends EventEmitter { }; } - completeColumnGroup(height) { + completeColumnGroup(height, endingCell) { let saved = this.snapshots.pop(); - this.calculateBottomMost(saved); + this.calculateBottomMost(saved, endingCell); - this.endingCell = null; this.x = saved.x; let y = saved.bottomMost.y; @@ -171,7 +166,6 @@ class DocumentContext extends EventEmitter { availableHeight: this.availableHeight, availableWidth: this.availableWidth, page: this.page, - endingCell: this.endingCell, lastColumnWidth: this.lastColumnWidth }); } @@ -184,7 +178,6 @@ class DocumentContext extends EventEmitter { this.availableWidth = saved.availableWidth; this.availableHeight = saved.availableHeight; this.page = saved.page; - this.endingCell = saved.endingCell; this.lastColumnWidth = saved.lastColumnWidth; } diff --git a/src/LayoutBuilder.js b/src/LayoutBuilder.js index 38560f8d3..b08d7f986 100644 --- a/src/LayoutBuilder.js +++ b/src/LayoutBuilder.js @@ -495,6 +495,21 @@ class LayoutBuilder { } } + findStartingSpanCell(arr, i) { + let requiredColspan = 1; + for (let index = i - 1; index >= 0; index--) { + if (!arr[index]._span) { + if (arr[index].rowSpan > 1 && (arr[index].colSpan || 1) === requiredColspan) { + return arr[index]; + } else { + return null; + } + } + requiredColspan++; + } + return null; + } + processRow(columns, widths, gaps, tableBody, tableRow, height) { const storePageBreakData = data => { let pageDesc; @@ -535,17 +550,52 @@ class LayoutBuilder { } } - this.writer.context().beginColumn(width, leftOffset, getEndingCell(column, i)); + // if rowspan starts in this cell, we retrieve the last cell affected by the rowspan + const endingCell = getEndingCell(column, i); + if (endingCell) { + // We store a reference of the ending cell in the first cell of the rowspan + column._endingCell = endingCell; + } + + // Check if exists and retrieve the cell that started the rowspan in case we are in the cell just after + let startingSpanCell = this.findStartingSpanCell(columns, i); + let endingSpanCell = null; + if (startingSpanCell && startingSpanCell._endingCell) { + // Reference to the last cell of the rowspan + endingSpanCell = startingSpanCell._endingCell; + } + + // We pass the endingSpanCell reference to store the context just after processing rowspan cell + this.writer.context().beginColumn(width, leftOffset, endingSpanCell); if (!column._span) { this.processNode(column); addAll(positions, column.positions); } else if (column._columnEndingContext) { // row-span ending + // Recover the context after processing the rowspanned cell this.writer.context().markEnding(column); } } - this.writer.context().completeColumnGroup(height); + // Check if last cell is part of a span + let endingSpanCell = null; + const lastColumn = columns.length > 0 ? columns[columns.length - 1] : null; + if (lastColumn) { + // Previous column cell has a rowspan + if (lastColumn._endingCell) { + endingSpanCell = lastColumn._endingCell; + // Previous column cell is part of a span + } else if (lastColumn._span === true) { + // We get the cell that started the span where we set a reference to the ending cell + const startingSpanCell = this.findStartingSpanCell(columns, columns.length); + if (startingSpanCell) { + // Context will be stored here (ending cell) + endingSpanCell = startingSpanCell._endingCell; + } + } + } + + this.writer.context().completeColumnGroup(height, endingSpanCell); this.writer.removeListener('pageChanged', storePageBreakData); diff --git a/tests/unit/DocumentContext.spec.js b/tests/unit/DocumentContext.spec.js index 7c035a8b3..7c01fbee3 100644 --- a/tests/unit/DocumentContext.spec.js +++ b/tests/unit/DocumentContext.spec.js @@ -71,11 +71,10 @@ describe('DocumentContext', function () { it('should save context in endingCell if provided', function () { var endingCell = {}; pc.beginColumnGroup(); - pc.beginColumn(30, 0, endingCell); pc.y = 150; pc.page = 3; pc.availableHeight = 123; - pc.beginColumn(30, 0); + pc.beginColumn(30, 0, endingCell); assert.equal(endingCell._columnEndingContext.y, 150); assert.equal(endingCell._columnEndingContext.page, 3); @@ -109,10 +108,10 @@ describe('DocumentContext', function () { var endingCell = {}; pc.beginColumnGroup(); - pc.beginColumn(30, 0, endingCell); pc.y = 150; pc.page = 3; pc.availableHeight = 123; + pc.beginColumn(30, 0, endingCell); pc.beginColumn(30, 0); pc.y = 100; pc.page = 3; @@ -131,6 +130,7 @@ describe('DocumentContext', function () { // col1 spans over 2 rows pc.beginColumn(30, 0, endingCell); pc.y = 350; + // col 2 pc.beginColumn(40); pc.y = 100; // column3 contains a nested table @@ -147,7 +147,9 @@ describe('DocumentContext', function () { pc.completeColumnGroup(); //// bottom of all non-spanned columns - assert.equal(pc.y, 120); + assert.equal(pc.y, 180); + // Check context has been stored in ending cell + assert.equal(endingCell2._columnEndingContext.y, 120); // second row (of nested table) pc.beginColumnGroup(); @@ -165,7 +167,7 @@ describe('DocumentContext', function () { pc.completeColumnGroup(); //// bottom of all non-spanned columns - assert.equal(pc.y, 180); + assert.equal(pc.y, 350); // second row pc.beginColumnGroup();