diff --git a/CHANGELOG.md b/CHANGELOG.md index d0c64641..323242e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ### Unreleased +- Add a flag to ignore the trailing line break in text height calculations (including tables) + ### [v0.17.1] - 2025-05-02 - Fix null values in table cells rendering as `[object Object]` diff --git a/lib/line_wrapper.js b/lib/line_wrapper.js index 7702dac3..cde4e143 100644 --- a/lib/line_wrapper.js +++ b/lib/line_wrapper.js @@ -236,11 +236,19 @@ class LineWrapper extends EventEmitter { if (bk.required || !this.canFit(word, w)) { // if the user specified a max height and an ellipsis, and is about to pass the // max height and max columns after the next line, append the ellipsis - const lh = this.document.currentLineHeight(true); + let lh = this.document.currentLineHeight(true); + + if (options.trailingLineBreak === false) { + if (bk.required) lh = this.document.currentLineHeight(); + } + if ( this.height != null && this.ellipsis && - PDFNumber(this.document.y + lh * 2) > this.maxY && + // If the current line and its following one cant fit, then render the ellipsis + PDFNumber( + this.document.y + lh + this.document.currentLineHeight(true), + ) > this.maxY && this.column >= this.columns ) { if (this.ellipsis === true) { diff --git a/lib/mixins/text.js b/lib/mixins/text.js index 168d390f..33ae6ffc 100644 --- a/lib/mixins/text.js +++ b/lib/mixins/text.js @@ -174,6 +174,10 @@ export default { } let contentHeight = this.y - y; + if (options.trailingLineBreak === false) { + const fontGap = this.currentLineHeight(true) - this.currentLineHeight(); + contentHeight -= fontGap; + } // Clamp height to max height if (options.height) contentHeight = Math.min(contentHeight, options.height); @@ -245,7 +249,12 @@ export default { this.y += this.currentLineHeight(true) + lineGap; }); - const height = this.y - y; + let height = this.y - y; + if (options.trailingLineBreak === false) { + const fontGap = this.currentLineHeight(true) - this.currentLineHeight(); + height -= fontGap; + } + this.x = x; this.y = y; diff --git a/tests/visual/__image_snapshots__/table-spec-js-table-ignore-trailing-line-break-issue-1620-1-snap.png b/tests/visual/__image_snapshots__/table-spec-js-table-ignore-trailing-line-break-issue-1620-1-snap.png new file mode 100644 index 00000000..ed03dc96 Binary files /dev/null and b/tests/visual/__image_snapshots__/table-spec-js-table-ignore-trailing-line-break-issue-1620-1-snap.png differ diff --git a/tests/visual/__image_snapshots__/text-spec-js-text-ignore-trailing-line-break-issue-1620-1-snap.png b/tests/visual/__image_snapshots__/text-spec-js-text-ignore-trailing-line-break-issue-1620-1-snap.png new file mode 100644 index 00000000..96deb08a Binary files /dev/null and b/tests/visual/__image_snapshots__/text-spec-js-text-ignore-trailing-line-break-issue-1620-1-snap.png differ diff --git a/tests/visual/table.spec.js b/tests/visual/table.spec.js index 90d6fa2c..c7c1c5a3 100644 --- a/tests/visual/table.spec.js +++ b/tests/visual/table.spec.js @@ -425,4 +425,25 @@ describe('table', function () { }); }); }); + + test('ignore trailing line break - issue #1620', function () { + return runDocTest( + { + systemFonts: true, + failureThreshold: 0.002, + failureThresholdType: 'percent', + }, + function (doc) { + doc.table({ + debug: true, + data: [['trailingLineBreak\ndefault (true)']], + }); + doc.table({ + debug: true, + defaultStyle: { textOptions: { trailingLineBreak: false } }, + data: [['trailingLineBreak\nfalse']], + }); + }, + ); + }); }); diff --git a/tests/visual/text.spec.js b/tests/visual/text.spec.js index 8fb3150e..8b4a49ce 100644 --- a/tests/visual/text.spec.js +++ b/tests/visual/text.spec.js @@ -182,4 +182,34 @@ describe('text', function () { .fill('blue'); }); }); + + test('ignore trailing line break - issue #1620', function () { + return runDocTest( + { + systemFonts: true, + failureThreshold: 0.002, + failureThresholdType: 'percent', + }, + function (doc) { + const text = 'test\ntest'; + let heightWithout = doc.heightOfString(text, { + trailingLineBreak: false, + }); + doc + .save() + .rect(doc.x, doc.y, doc.page.contentWidth, heightWithout) + .strokeColor('red', 0.3) + .stroke() + .restore(); + let height = doc.heightOfString(text); + doc + .save() + .rect(doc.x, doc.y, doc.page.contentWidth, height) + .strokeColor('blue', 0.3) + .stroke() + .restore(); + doc.text(text, { height }); + }, + ); + }); });