From 852ef22f290173f892fa76a1d256e349c0a1a9c4 Mon Sep 17 00:00:00 2001 From: "Libor M." Date: Tue, 30 Jul 2024 08:30:57 +0200 Subject: [PATCH] backport PR #2759 --- CHANGELOG.md | 4 ++ src/helpers.js | 12 ++++ src/tableProcessor.js | 25 ++++++-- tests/tableProcessor.js | 130 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 167 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c38a0490f..9621113c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## Unreleased + +- Fixes and validates input values headerRows and keepWithHeaderRows + ## 0.2.10 - 2024-03-07 - Removed unused brfs dependency diff --git a/src/helpers.js b/src/helpers.js index d66df0de1..b3f5af13f 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -32,6 +32,17 @@ function isUndefined(variable) { return variable === undefined; } +/** + * @param {any} variable + * @returns {boolean} + */ +function isPositiveInteger(variable) { + if (!isNumber(variable) || !Number.isInteger(variable) || variable <= 0) { + return false; + } + return true; +} + function pack() { var result = {}; @@ -117,6 +128,7 @@ module.exports = { isObject: isObject, isNull: isNull, isUndefined: isUndefined, + isPositiveInteger: isPositiveInteger, pack: pack, fontStringify: fontStringify, offsetVector: offsetVector, diff --git a/src/tableProcessor.js b/src/tableProcessor.js index 1a4daf57c..4d74e1e6f 100644 --- a/src/tableProcessor.js +++ b/src/tableProcessor.js @@ -3,6 +3,7 @@ var ColumnCalculator = require('./columnCalculator'); var isFunction = require('./helpers').isFunction; var isNumber = require('./helpers').isNumber; +var isPositiveInteger = require('./helpers').isPositiveInteger; function TableProcessor(tableNode) { this.tableNode = tableNode; @@ -24,12 +25,28 @@ TableProcessor.prototype.beginTable = function (writer) { this.rowSpanData = prepareRowSpanData(); this.cleanUpRepeatables = false; - this.headerRows = tableNode.table.headerRows || 0; - if (this.headerRows > tableNode.table.body.length) { - throw new Error(`Too few rows in the table. Property headerRows requires at least ${this.headerRows}, contains only ${tableNode.table.body.length}`); + // headersRows and rowsWithoutPageBreak (headerRows + keepWithHeaderRows) + this.headerRows = 0; + this.rowsWithoutPageBreak = 0; + + var headerRows = tableNode.table.headerRows; + + if (isPositiveInteger(headerRows)) { + this.headerRows = headerRows; + + if (this.headerRows > tableNode.table.body.length) { + throw new Error(`Too few rows in the table. Property headerRows requires at least ${this.headerRows}, contains only ${tableNode.table.body.length}`); + } + + this.rowsWithoutPageBreak = this.headerRows; + + const keepWithHeaderRows = tableNode.table.keepWithHeaderRows; + + if (isPositiveInteger(keepWithHeaderRows)) { + this.rowsWithoutPageBreak += keepWithHeaderRows; + } } - this.rowsWithoutPageBreak = this.headerRows + (tableNode.table.keepWithHeaderRows || 0); this.dontBreakRows = tableNode.table.dontBreakRows || false; if (this.rowsWithoutPageBreak) { diff --git a/tests/tableProcessor.js b/tests/tableProcessor.js index 81a64fca0..a550b6984 100644 --- a/tests/tableProcessor.js +++ b/tests/tableProcessor.js @@ -213,4 +213,134 @@ describe('TableProcessor', function () { assert.equal(fakeWriter.popFromRepeatables.callCount, 1); }); }); + + describe('headerRows and keepWithHeaderRows (issue #2754)', function () { + var fakeWriter = { + context: function () { + return { + availableWidth: 56473, + }; + }, + beginUnbreakableBlock: function () { }, + }; + + const inputTable = { + table: { + widths: [{ width: 20 }, { width: 20 }], + body: [['a', 'b'], ['c', 'd'], ['e', 'f']] + }, + _offsets: { + total: 9 + }, + _layout: { + paddingLeft: function () { }, + paddingRight: function () { }, + paddingBottom: function () { }, + paddingTop: function () { }, + vLineWidth: function () { }, + hLineWidth: function () { }, + fillColor: function () { }, + fillOpacity: function () { } + } + }; + + it('should ignore wrong values for headerRows - 1', function () { + inputTable.table.headerRows = '2'; + inputTable.table.keepWithHeaderRows = 2; + + var tableProcessor = new TableProcessor(inputTable); + tableProcessor.drawHorizontalLine = function() { }; + tableProcessor.beginTable(fakeWriter); + assert.equal(tableProcessor.headerRows, 0); + assert.equal(tableProcessor.rowsWithoutPageBreak, 0); + }); + + it('should ignore wrong values for headerRows - 2', function () { + inputTable.table.headerRows = -5; + inputTable.table.keepWithHeaderRows = 2; + + var tableProcessor = new TableProcessor(inputTable); + tableProcessor.drawHorizontalLine = function() { }; + tableProcessor.beginTable(fakeWriter); + assert.equal(tableProcessor.headerRows, 0); + assert.equal(tableProcessor.rowsWithoutPageBreak, 0); + }); + + + it('should ignore wrong values for headerRows - 3', function () { + inputTable.table.headerRows = 2.5; + inputTable.table.keepWithHeaderRows = 2; + + var tableProcessor = new TableProcessor(inputTable); + tableProcessor.drawHorizontalLine = function() { }; + tableProcessor.beginTable(fakeWriter); + assert.equal(tableProcessor.headerRows, 0); + assert.equal(tableProcessor.rowsWithoutPageBreak, 0); + }); + + it('should ignore keepWithHeaderRows if headerRows is not bigger than 0', function () { + inputTable.table.headerRows = 0; + inputTable.table.keepWithHeaderRows = 2; + + var tableProcessor = new TableProcessor(inputTable); + tableProcessor.drawHorizontalLine = function() { }; + tableProcessor.beginTable(fakeWriter); + assert.equal(tableProcessor.headerRows, 0); + assert.equal(tableProcessor.rowsWithoutPageBreak, 0); + }); + + it('should ignore wrong values for keepWithHeaderRows - 1', function () { + inputTable.table.headerRows = 1; + inputTable.table.keepWithHeaderRows = 1.5; + + var tableProcessor = new TableProcessor(inputTable); + tableProcessor.drawHorizontalLine = function() { }; + tableProcessor.beginTable(fakeWriter); + assert.equal(tableProcessor.headerRows, 1); + assert.equal(tableProcessor.rowsWithoutPageBreak, 1); + }); + + it('should ignore wrong values for keepWithHeaderRows - 2', function () { + inputTable.table.headerRows = 2; + inputTable.table.keepWithHeaderRows = '1.5'; + + var tableProcessor = new TableProcessor(inputTable); + tableProcessor.drawHorizontalLine = function() { }; + tableProcessor.beginTable(fakeWriter); + assert.equal(tableProcessor.headerRows, 2); + assert.equal(tableProcessor.rowsWithoutPageBreak, 2); + }); + + it('should sum up headerRows and keepWithHeaderRows - 1', function () { + inputTable.table.headerRows = 1; + inputTable.table.keepWithHeaderRows = 2; + + var tableProcessor = new TableProcessor(inputTable); + tableProcessor.drawHorizontalLine = function() { }; + tableProcessor.beginTable(fakeWriter); + assert.equal(tableProcessor.rowsWithoutPageBreak, 3); + assert.equal(tableProcessor.headerRows, 1); + }); + + it('should sum up headerRows and keepWithHeaderRows - 2', function () { + inputTable.table.headerRows = 1; + inputTable.table.keepWithHeaderRows = 0; + + var tableProcessor = new TableProcessor(inputTable); + tableProcessor.drawHorizontalLine = function() { }; + tableProcessor.beginTable(fakeWriter); + assert.equal(tableProcessor.rowsWithoutPageBreak, 1); + assert.equal(tableProcessor.headerRows, 1); + }); + + it('should throw exception when headerRows > table rows', function () { + inputTable.table.headerRows = 5; + inputTable.table.keepWithHeaderRows = 0; + + var tableProcessor = new TableProcessor(inputTable); + tableProcessor.drawHorizontalLine = function() { }; + assert.throws(() => tableProcessor.beginTable(fakeWriter), /Too few rows in the table/); + }); + }); + });