Skip to content

Commit

Permalink
Merge pull request #2781 from fe-cj/fix/issues-1006-2748
Browse files Browse the repository at this point in the history
Fixes issues #1006 and #2748
  • Loading branch information
liborm85 committed Sep 7, 2024
2 parents 9d98fbb + 4867cf6 commit 95d81f6
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 32 deletions.
88 changes: 64 additions & 24 deletions src/LayoutBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -370,15 +370,30 @@ class LayoutBuilder {
}

if (margin) {
this.writer.context().moveDown(margin[1]);
this.writer.context().addMargin(margin[0], margin[2]);
const availableHeight = this.writer.context().availableHeight;
// If top margin is bigger than available space, move to next page
// Necessary for nodes inside tables
if (availableHeight - margin[1] < 0) {
this.writer.context().moveDown(availableHeight);
this.writer.moveToNextPage(node.pageOrientation);
} else {
this.writer.context().moveDown(margin[1]);
this.writer.context().addMargin(margin[0], margin[2]);
}
}

callback();

if (margin) {
this.writer.context().addMargin(-margin[0], -margin[2]);
this.writer.context().moveDown(margin[3]);
const availableHeight = this.writer.context().availableHeight;
// If bottom margin is bigger than available space, move to next page
// Necessary for nodes inside tables
if (availableHeight - margin[3] < 0) {
this.writer.context().moveDown(availableHeight);
this.writer.moveToNextPage(node.pageOrientation);
} else {
this.writer.context().addMargin(-margin[0], -margin[2]);
this.writer.context().moveDown(margin[3]);
}
}

if (node.pageBreak === 'after') {
Expand Down Expand Up @@ -476,7 +491,11 @@ class LayoutBuilder {
}

ColumnCalculator.buildColumnWidths(columns, availableWidth);
let result = this.processRow(false, columns, columns, gaps);
let result = this.processRow({
cells: columns,
widths: columns,
gaps
});
addAll(columnNode.positions, result.positions);

function gapArray(gap) {
Expand Down Expand Up @@ -510,17 +529,17 @@ class LayoutBuilder {
return null;
}

processRow(dontBreakRows, columns, widths, gaps, tableBody, tableRow, height) {
processRow({dontBreakRows = false, rowsWithoutPageBreak = 0, cells, widths, gaps, tableBody, rowIndex, height}) {
const updatePageBreakData = (page, prevY) => {
let pageDesc;
// Find page break data for this row and page
for (let i = 0, l = pageBreaks.length; i < l; i++) {
let desc = pageBreaks[i];
if (desc.prevPage === page) {
pageDesc = desc;
break;
}
}
for (let i = 0, l = pageBreaks.length; i < l; i++) {
let desc = pageBreaks[i];
if (desc.prevPage === page) {
pageDesc = desc;
break;
}
}
// If row has page break in this page, update prevY
if (pageDesc) {
pageDesc.prevY = Math.max(pageDesc.prevY, prevY);
Expand All @@ -546,17 +565,23 @@ class LayoutBuilder {
pageDesc.y = Math.min(pageDesc.y, data.y);
};

const isUnbreakableRow = dontBreakRows || rowIndex <= rowsWithoutPageBreak - 1;
let pageBreaks = [];
let positions = [];

let willBreakByHeight = false;
this.writer.addListener('pageChanged', storePageBreakData);

widths = widths || columns;
// Check if row should break by height
if (!isUnbreakableRow && height > this.writer.context().availableHeight) {
willBreakByHeight = true;
}

widths = widths || cells;

this.writer.context().beginColumnGroup();

for (let i = 0, l = columns.length; i < l; i++) {
let column = columns[i];
for (let i = 0, l = cells.length; i < l; i++) {
let column = cells[i];
let width = widths[i]._calcWidth;
let leftOffset = colLeftOffset(i);

Expand All @@ -575,7 +600,7 @@ class LayoutBuilder {
}

// 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 startingSpanCell = this.findStartingSpanCell(cells, i);
let endingSpanCell = null;
if (startingSpanCell && startingSpanCell._endingCell) {
// Reference to the last cell of the rowspan
Expand Down Expand Up @@ -614,15 +639,15 @@ class LayoutBuilder {

// Check if last cell is part of a span
let endingSpanCell = null;
const lastColumn = columns.length > 0 ? columns[columns.length - 1] : null;
const lastColumn = cells.length > 0 ? cells[cells.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);
const startingSpanCell = this.findStartingSpanCell(cells, cells.length);
if (startingSpanCell) {
// Context will be stored here (ending cell)
endingSpanCell = startingSpanCell._endingCell;
Expand All @@ -638,8 +663,13 @@ class LayoutBuilder {
// If there are page breaks in this row, update data with prevY of last cell
updatePageBreakData(this.writer.context().page, this.writer.context().y);

this.writer.context().completeColumnGroup(height, endingSpanCell);
// If content did not break page, check if we should break by height
if (!isUnbreakableRow && pageBreaks.length === 0 && willBreakByHeight) {
this.writer.context().moveDown(this.writer.context().availableHeight);
this.writer.moveToNextPage();
}

this.writer.context().completeColumnGroup(height, endingSpanCell);
this.writer.removeListener('pageChanged', storePageBreakData);

return { pageBreaks: pageBreaks, positions: positions };
Expand All @@ -653,7 +683,7 @@ class LayoutBuilder {

function getEndingCell(column, columnIndex) {
if (column.rowSpan && column.rowSpan > 1) {
let endingRow = tableRow + column.rowSpan - 1;
let endingRow = rowIndex + column.rowSpan - 1;
if (endingRow >= tableBody.length) {
throw new Error(`Row span for column ${columnIndex} (with indexes starting from 0) exceeded row count`);
}
Expand Down Expand Up @@ -741,7 +771,17 @@ class LayoutBuilder {
height = undefined;
}

let result = this.processRow(processor.dontBreakRows, tableNode.table.body[i], tableNode.table.widths, tableNode._offsets.offsets, tableNode.table.body, i, height);
let result = this.processRow({
dontBreakRows: processor.dontBreakRows,
rowsWithoutPageBreak: processor.rowsWithoutPageBreak,
cells: tableNode.table.body[i],
widths: tableNode.table.widths,
gaps: tableNode._offsets.offsets,
tableBody: tableNode.table.body,
rowIndex: i,
height
});

addAll(tableNode.positions, result.positions);

processor.endRow(i, this.writer, result.pageBreaks);
Expand Down
57 changes: 49 additions & 8 deletions tests/unit/LayoutBuilder.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1679,16 +1679,27 @@ describe('LayoutBuilder', function () {
it('should return an empty array if no page breaks occur', function () {
var doc = createTable(1, 0);

var result = builder2.processRow(false, doc.table.body[0], doc.table.widths, doc._offsets.offsets, doc.table.body, 0);
var result = builder2.processRow({
cells: doc.table.body[0],
widths: doc.table.widths,
gaps: doc._offsets.offsets,
tableBody: doc.table.body,
rowIndex: 0
});

assert(result.pageBreaks instanceof Array);
assert.equal(result.pageBreaks.length, 0);
});

it('on page break should return an entry with ending/starting positions', function () {
var doc = createTable(0, 1, 10, 5, 5);
var result = builder2.processRow(false, doc.table.body[0], doc.table.widths, doc._offsets.offsets, doc.table.body, 0);

var result = builder2.processRow({
cells: doc.table.body[0],
widths: doc.table.widths,
gaps: doc._offsets.offsets,
tableBody: doc.table.body,
rowIndex: 0
});
assert(result.pageBreaks instanceof Array);
assert.equal(result.pageBreaks.length, 1);
assert.equal(result.pageBreaks[0].prevPage, 0);
Expand All @@ -1697,7 +1708,13 @@ describe('LayoutBuilder', function () {

it('on page break should return an entry with ending/starting positions 2', function () {
var doc = createTable(0, 1, 10, 5);
var result = builder2.processRow(false, doc.table.body[0], doc.table.widths, doc._offsets.offsets, doc.table.body, 0);
var result = builder2.processRow({
cells: doc.table.body[0],
widths: doc.table.widths,
gaps: doc._offsets.offsets,
tableBody: doc.table.body,
rowIndex: 0
});

assert(result.pageBreaks instanceof Array);
assert.equal(result.pageBreaks.length, 1);
Expand All @@ -1708,14 +1725,26 @@ describe('LayoutBuilder', function () {

it('on multi-pass page break (columns or table columns) should treat bottom-most page-break as the ending position ', function () {
var doc = createTable(0, 1, 10, 5, 7);
var result = builder2.processRow(false, doc.table.body[0], doc.table.widths, doc._offsets.offsets, doc.table.body, 0);
var result = builder2.processRow({
cells: doc.table.body[0],
widths: doc.table.widths,
gaps: doc._offsets.offsets,
tableBody: doc.table.body,
rowIndex: 0
});

assert.equal(result.pageBreaks[0].prevY, 40 + 12 * 7);
});

it('on multiple page breaks (more than 2 pages), should return all entries with ending/starting positions', function () {
var doc = createTable(0, 1, 100, 90, 90);
var result = builder2.processRow(false, doc.table.body[0], doc.table.widths, doc._offsets.offsets, doc.table.body, 0);
var result = builder2.processRow({
cells: doc.table.body[0],
widths: doc.table.widths,
gaps: doc._offsets.offsets,
tableBody: doc.table.body,
rowIndex: 0
});

assert(result.pageBreaks instanceof Array);
assert.equal(result.pageBreaks.length, 2);
Expand All @@ -1727,7 +1756,13 @@ describe('LayoutBuilder', function () {

it('on multiple page breaks (more than 2 pages), should return all entries with ending/starting positions 2', function () {
var doc = createTable(0, 1, 100, 90);
var result = builder2.processRow(false, doc.table.body[0], doc.table.widths, doc._offsets.offsets, doc.table.body, 0);
var result = builder2.processRow({
cells: doc.table.body[0],
widths: doc.table.widths,
gaps: doc._offsets.offsets,
tableBody: doc.table.body,
rowIndex: 0
});

assert(result.pageBreaks instanceof Array);
assert.equal(result.pageBreaks.length, 2);
Expand All @@ -1739,7 +1774,13 @@ describe('LayoutBuilder', function () {

it('on multiple and multi-pass page breaks should calculate bottom-most endings for every page', function () {
var doc = createTable(0, 1, 100, 90, 92);
var result = builder2.processRow(false, doc.table.body[0], doc.table.widths, doc._offsets.offsets, doc.table.body, 0);
var result = builder2.processRow({
cells: doc.table.body[0],
widths: doc.table.widths,
gaps: doc._offsets.offsets,
tableBody: doc.table.body,
rowIndex: 0
});

assert(result.pageBreaks instanceof Array);
assert.equal(result.pageBreaks.length, 2);
Expand Down

0 comments on commit 95d81f6

Please sign in to comment.