From d1f864b5cb4f4cde63befb9dc30ab1f167027685 Mon Sep 17 00:00:00 2001 From: Jeff Sieu Date: Sun, 25 Oct 2020 02:20:46 +0800 Subject: [PATCH 01/16] Fix margin values not working for html --- src/jspdf.js | 7 +++- src/modules/context2d.js | 89 ++++++++++++++++++++++++++++++---------- src/modules/html.js | 1 + 3 files changed, 74 insertions(+), 23 deletions(-) diff --git a/src/jspdf.js b/src/jspdf.js index a111c5995..ffec36ec9 100644 --- a/src/jspdf.js +++ b/src/jspdf.js @@ -3327,7 +3327,7 @@ function jsPDF(options) { */ options = options || {}; var scope = options.scope || this; - var payload, da, angle, align, charSpace, maxWidth, flags; + var payload, da, angle, align, charSpace, maxWidth, renderMaxWidthOverflow, flags; // Pre-August-2012 the order of arguments was function(x, y, text, flags) // in effort to make all calls have similar signature like @@ -3526,6 +3526,7 @@ function jsPDF(options) { //multiline maxWidth = options.maxWidth || 0; + renderMaxWidthOverflow = options.renderMaxWidthOverflow !== undefined ? options.renderMaxWidthOverflow : true; if (maxWidth > 0) { if (typeof text === "string") { @@ -3537,6 +3538,10 @@ function jsPDF(options) { } } + if (!renderMaxWidthOverflow) { + text = [text[0]]; + } + //creating Payload-Object to make text byRef payload = { text: text, diff --git a/src/modules/context2d.js b/src/modules/context2d.js index 6bd5094e9..b79a5e597 100644 --- a/src/modules/context2d.js +++ b/src/modules/context2d.js @@ -46,6 +46,8 @@ import { console } from "../libs/console.js"; this.currentPoint = ctx.currentPoint || new Point(); this.miterLimit = ctx.miterLimit || 10.0; this.lastPoint = ctx.lastPoint || new Point(); + this.margin = ctx.margin || [0, 0, 0, 0]; + this.prevPageLastElemOffset = ctx.prevPageLastElemOffset || 0; this.ignoreClearRect = typeof ctx.ignoreClearRect === "boolean" ? ctx.ignoreClearRect : true; @@ -157,6 +159,20 @@ import { console } from "../libs/console.js"; } }); + /** + * @name margin + * @type {array} + * @default [0, 0, 0, 0] + */ + Object.defineProperty(this, "margin", { + get: function() { + return _ctx.margin; + }, + set: function(value) { + _ctx.margin = value; + } + }); + var _autoPaging = false; /** * @name autoPaging @@ -1457,13 +1473,19 @@ import { console } from "../libs/console.js"; for (var i = min; i < max + 1; i++) { this.pdf.setPage(i); + var topMargin = (i === min ? Math.max(this.posY, this.margin[0]) : this.margin[0]); + var firstPageHeight = this.pdf.internal.pageSize.height - Math.max(this.posY, this.margin[0]) - this.margin[2]; + var pageHeightMinusMargin = this.pdf.internal.pageSize.height - this.margin[2]; + var pageWidthMinusMargin = this.pdf.internal.pageSize.width - this.margin[1]; + var previousPageHeightSum = i === 1 ? 0 : firstPageHeight + (i - 2) * pageHeightMinusMargin; + if (this.ctx.clip_path.length !== 0) { var tmpPaths = this.path; clipPath = JSON.parse(JSON.stringify(this.ctx.clip_path)); this.path = pathPositionRedo( clipPath, - this.posX, - -1 * this.pdf.internal.pageSize.height * (i - 1) + this.posY + Math.max(this.posX, this.margin[3]), + -1 * previousPageHeightSum + topMargin + this.ctx.prevPageLastElemOffset ); drawPaths.call(this, "fill", true); this.path = tmpPaths; @@ -1471,15 +1493,15 @@ import { console } from "../libs/console.js"; var tmpRect = JSON.parse(JSON.stringify(xRect)); tmpRect = pathPositionRedo( [tmpRect], - this.posX, - -1 * this.pdf.internal.pageSize.height * (i - 1) + this.posY + Math.max(this.posX, this.margin[3]), + -1 * previousPageHeightSum + topMargin + this.ctx.prevPageLastElemOffset )[0]; this.pdf.addImage( img, "JPEG", tmpRect.x, tmpRect.y, - tmpRect.w, + Math.min(tmpRect.w, this.pdf.internal.pageSize.width - this.margin[1] - tmpRect.x), tmpRect.h, null, null, @@ -1504,7 +1526,7 @@ import { console } from "../libs/console.js"; var getPagesByPath = function(path, pageWrapX, pageWrapY) { var result = []; pageWrapX = pageWrapX || this.pdf.internal.pageSize.width; - pageWrapY = pageWrapY || this.pdf.internal.pageSize.height; + pageWrapY = pageWrapY || this.pdf.internal.pageSize.height - this.margin[0] - this.margin[2]; switch (path.type) { default: @@ -1653,13 +1675,18 @@ import { console } from "../libs/console.js"; this.lineWidth = lineWidth; this.lineJoin = lineJoin; + var topMargin = (k === min ? Math.max(this.posY, this.margin[0]) : this.margin[0]); + var firstPageHeight = this.pdf.internal.pageSize.height - Math.max(this.posY, this.margin[0]) - this.margin[2]; + var pageHeightMinusMargin = this.pdf.internal.pageSize.height - this.margin[2]; + var previousPageHeightSum = k === 1 ? 0 : firstPageHeight + (k - 2) * pageHeightMinusMargin; + if (this.ctx.clip_path.length !== 0) { var tmpPaths = this.path; clipPath = JSON.parse(JSON.stringify(this.ctx.clip_path)); this.path = pathPositionRedo( clipPath, - this.posX, - -1 * this.pdf.internal.pageSize.height * (k - 1) + this.posY + Math.max(this.posX, this.margin[3]), + -1 * previousPageHeightSum + topMargin + this.ctx.prevPageLastElemOffset ); drawPaths.call(this, rule, true); this.path = tmpPaths; @@ -1667,8 +1694,8 @@ import { console } from "../libs/console.js"; tmpPath = JSON.parse(JSON.stringify(origPath)); this.path = pathPositionRedo( tmpPath, - this.posX, - -1 * this.pdf.internal.pageSize.height * (k - 1) + this.posY + Math.max(this.posX, this.margin[3]), + -1 * previousPageHeightSum + topMargin + this.ctx.prevPageLastElemOffset ); if (isClip === false || k === 0) { drawPaths.call(this, rule, isClip); @@ -2007,19 +2034,25 @@ import { console } from "../libs/console.js"; sortPages(pages); var clipPath, oldSize, oldLineWidth; - if (this.autoPaging === true) { + if (this.autoPaging) { var min = pages[0]; var max = pages[pages.length - 1]; for (var i = min; i < max + 1; i++) { this.pdf.setPage(i); + var topMargin = (i === min ? Math.max(this.posY, this.margin[0]) : this.margin[0]); + var firstPageHeight = this.pdf.internal.pageSize.height - Math.max(this.posY, this.margin[0]) - this.margin[2]; + var pageHeightMinusMargin = this.pdf.internal.pageSize.height - this.margin[2]; + var pageWidthMinusMargin = this.pdf.internal.pageSize.width - this.margin[1]; + var previousPageHeightSum = i === 1 ? 0 : firstPageHeight + (i - 2) * pageHeightMinusMargin; + if (this.ctx.clip_path.length !== 0) { var tmpPaths = this.path; clipPath = JSON.parse(JSON.stringify(this.ctx.clip_path)); this.path = pathPositionRedo( clipPath, - this.posX, - -1 * this.pdf.internal.pageSize.height * (i - 1) + this.posY + Math.max(this.posX, this.margin[3]), + -1 * previousPageHeightSum + topMargin + this.ctx.prevPageLastElemOffset ); drawPaths.call(this, "fill", true); this.path = tmpPaths; @@ -2027,8 +2060,8 @@ import { console } from "../libs/console.js"; var tmpRect = JSON.parse(JSON.stringify(textRect)); tmpRect = pathPositionRedo( [tmpRect], - this.posX, - -1 * this.pdf.internal.pageSize.height * (i - 1) + this.posY + Math.max(this.posX, this.margin[3]), + -1 * previousPageHeightSum + topMargin + this.ctx.prevPageLastElemOffset )[0]; if (options.scale >= 0.01) { @@ -2037,12 +2070,24 @@ import { console } from "../libs/console.js"; oldLineWidth = this.lineWidth; this.lineWidth = oldLineWidth * options.scale; } - this.pdf.text(options.text, tmpRect.x, tmpRect.y, { - angle: options.angle, - align: textAlign, - renderingMode: options.renderingMode, - maxWidth: options.maxWidth - }); + + if (tmpRect.y <= pageHeightMinusMargin) { + if (tmpRect.y - tmpRect.h >= 0 && tmpRect.x <= pageWidthMinusMargin) { + this.pdf.text(options.text, tmpRect.x, tmpRect.y, { + angle: options.angle, + align: textAlign, + renderingMode: options.renderingMode, + maxWidth: options.maxWidth || pageWidthMinusMargin - tmpRect.x, + renderMaxWidthOverflow: false + }); + } + } else { + // This text is the last element of the page, but it got cut off due to the margin + // so we render it in the next page + + // As a result, all other elements have their y offset increased + this.ctx.prevPageLastElemOffset += pageHeightMinusMargin - tmpRect.y + tmpRect.h; + } if (options.scale >= 0.01) { this.pdf.setFontSize(oldSize); @@ -2056,7 +2101,7 @@ import { console } from "../libs/console.js"; oldLineWidth = this.lineWidth; this.lineWidth = oldLineWidth * options.scale; } - this.pdf.text(options.text, pt.x + this.posX, pt.y + this.posY, { + this.pdf.text(options.text, pt.x + Math.max(this.posX, this.margin[3]), pt.y + this.posY, { angle: options.angle, align: textAlign, renderingMode: options.renderingMode, diff --git a/src/modules/html.js b/src/modules/html.js index 5fff1aaf4..543a9c93f 100644 --- a/src/modules/html.js +++ b/src/modules/html.js @@ -449,6 +449,7 @@ import { globalObject } from "../libs/globalObject.js"; pdf.context2d.autoPaging = true; pdf.context2d.posX = this.opt.x; pdf.context2d.posY = this.opt.y; + pdf.context2d.margin = this.opt.margin; options.windowHeight = options.windowHeight || 0; options.windowHeight = From 93aab8537a3a23a7bc2193433fce5ef54d315530 Mon Sep 17 00:00:00 2001 From: Jeff Sieu Date: Mon, 26 Oct 2020 19:55:02 +0800 Subject: [PATCH 02/16] Remove renderMaxWidthOverflow flag --- src/jspdf.js | 7 +------ src/modules/context2d.js | 4 ++-- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/jspdf.js b/src/jspdf.js index ffec36ec9..a111c5995 100644 --- a/src/jspdf.js +++ b/src/jspdf.js @@ -3327,7 +3327,7 @@ function jsPDF(options) { */ options = options || {}; var scope = options.scope || this; - var payload, da, angle, align, charSpace, maxWidth, renderMaxWidthOverflow, flags; + var payload, da, angle, align, charSpace, maxWidth, flags; // Pre-August-2012 the order of arguments was function(x, y, text, flags) // in effort to make all calls have similar signature like @@ -3526,7 +3526,6 @@ function jsPDF(options) { //multiline maxWidth = options.maxWidth || 0; - renderMaxWidthOverflow = options.renderMaxWidthOverflow !== undefined ? options.renderMaxWidthOverflow : true; if (maxWidth > 0) { if (typeof text === "string") { @@ -3538,10 +3537,6 @@ function jsPDF(options) { } } - if (!renderMaxWidthOverflow) { - text = [text[0]]; - } - //creating Payload-Object to make text byRef payload = { text: text, diff --git a/src/modules/context2d.js b/src/modules/context2d.js index b79a5e597..c06476b29 100644 --- a/src/modules/context2d.js +++ b/src/modules/context2d.js @@ -2073,11 +2073,11 @@ import { console } from "../libs/console.js"; if (tmpRect.y <= pageHeightMinusMargin) { if (tmpRect.y - tmpRect.h >= 0 && tmpRect.x <= pageWidthMinusMargin) { - this.pdf.text(options.text, tmpRect.x, tmpRect.y, { + var croppedText = this.pdf.splitTextToSize(options.text, options.maxWidth || pageWidthMinusMargin - tmpRect.x)[0]; + this.pdf.text(croppedText, tmpRect.x, tmpRect.y, { angle: options.angle, align: textAlign, renderingMode: options.renderingMode, - maxWidth: options.maxWidth || pageWidthMinusMargin - tmpRect.x, renderMaxWidthOverflow: false }); } From 184fcfa9f306c68d3211b18261e4254a6684d10f Mon Sep 17 00:00:00 2001 From: Jeff Sieu Date: Tue, 27 Oct 2020 21:44:32 +0800 Subject: [PATCH 03/16] Make margin parameter independent from x, y --- src/modules/context2d.js | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/modules/context2d.js b/src/modules/context2d.js index c06476b29..627111c70 100644 --- a/src/modules/context2d.js +++ b/src/modules/context2d.js @@ -1473,8 +1473,8 @@ import { console } from "../libs/console.js"; for (var i = min; i < max + 1; i++) { this.pdf.setPage(i); - var topMargin = (i === min ? Math.max(this.posY, this.margin[0]) : this.margin[0]); - var firstPageHeight = this.pdf.internal.pageSize.height - Math.max(this.posY, this.margin[0]) - this.margin[2]; + var topMargin = (i === min ? this.posY + this.margin[0] : this.margin[0]); + var firstPageHeight = this.pdf.internal.pageSize.height - this.posY - this.margin[0] - this.margin[2]; var pageHeightMinusMargin = this.pdf.internal.pageSize.height - this.margin[2]; var pageWidthMinusMargin = this.pdf.internal.pageSize.width - this.margin[1]; var previousPageHeightSum = i === 1 ? 0 : firstPageHeight + (i - 2) * pageHeightMinusMargin; @@ -1484,7 +1484,7 @@ import { console } from "../libs/console.js"; clipPath = JSON.parse(JSON.stringify(this.ctx.clip_path)); this.path = pathPositionRedo( clipPath, - Math.max(this.posX, this.margin[3]), + this.posX + this.margin[3], -1 * previousPageHeightSum + topMargin + this.ctx.prevPageLastElemOffset ); drawPaths.call(this, "fill", true); @@ -1493,7 +1493,7 @@ import { console } from "../libs/console.js"; var tmpRect = JSON.parse(JSON.stringify(xRect)); tmpRect = pathPositionRedo( [tmpRect], - Math.max(this.posX, this.margin[3]), + this.posX + this.margin[3], -1 * previousPageHeightSum + topMargin + this.ctx.prevPageLastElemOffset )[0]; this.pdf.addImage( @@ -1675,8 +1675,8 @@ import { console } from "../libs/console.js"; this.lineWidth = lineWidth; this.lineJoin = lineJoin; - var topMargin = (k === min ? Math.max(this.posY, this.margin[0]) : this.margin[0]); - var firstPageHeight = this.pdf.internal.pageSize.height - Math.max(this.posY, this.margin[0]) - this.margin[2]; + var topMargin = (k === min ? this.posY + this.margin[0] : this.margin[0]); + var firstPageHeight = this.pdf.internal.pageSize.height - this.posY - this.margin[0] - this.margin[2]; var pageHeightMinusMargin = this.pdf.internal.pageSize.height - this.margin[2]; var previousPageHeightSum = k === 1 ? 0 : firstPageHeight + (k - 2) * pageHeightMinusMargin; @@ -1685,7 +1685,7 @@ import { console } from "../libs/console.js"; clipPath = JSON.parse(JSON.stringify(this.ctx.clip_path)); this.path = pathPositionRedo( clipPath, - Math.max(this.posX, this.margin[3]), + this.posX + this.margin[3], -1 * previousPageHeightSum + topMargin + this.ctx.prevPageLastElemOffset ); drawPaths.call(this, rule, true); @@ -1694,7 +1694,7 @@ import { console } from "../libs/console.js"; tmpPath = JSON.parse(JSON.stringify(origPath)); this.path = pathPositionRedo( tmpPath, - Math.max(this.posX, this.margin[3]), + this.posX + this.margin[3], -1 * previousPageHeightSum + topMargin + this.ctx.prevPageLastElemOffset ); if (isClip === false || k === 0) { @@ -2040,8 +2040,8 @@ import { console } from "../libs/console.js"; for (var i = min; i < max + 1; i++) { this.pdf.setPage(i); - var topMargin = (i === min ? Math.max(this.posY, this.margin[0]) : this.margin[0]); - var firstPageHeight = this.pdf.internal.pageSize.height - Math.max(this.posY, this.margin[0]) - this.margin[2]; + var topMargin = (i === min ? this.posY + this.margin[0] : this.margin[0]); + var firstPageHeight = this.pdf.internal.pageSize.height - this.posY - this.margin[0] - this.margin[2]; var pageHeightMinusMargin = this.pdf.internal.pageSize.height - this.margin[2]; var pageWidthMinusMargin = this.pdf.internal.pageSize.width - this.margin[1]; var previousPageHeightSum = i === 1 ? 0 : firstPageHeight + (i - 2) * pageHeightMinusMargin; @@ -2051,7 +2051,7 @@ import { console } from "../libs/console.js"; clipPath = JSON.parse(JSON.stringify(this.ctx.clip_path)); this.path = pathPositionRedo( clipPath, - Math.max(this.posX, this.margin[3]), + this.posX + this.margin[3], -1 * previousPageHeightSum + topMargin + this.ctx.prevPageLastElemOffset ); drawPaths.call(this, "fill", true); @@ -2060,7 +2060,7 @@ import { console } from "../libs/console.js"; var tmpRect = JSON.parse(JSON.stringify(textRect)); tmpRect = pathPositionRedo( [tmpRect], - Math.max(this.posX, this.margin[3]), + this.posX + this.margin[3], -1 * previousPageHeightSum + topMargin + this.ctx.prevPageLastElemOffset )[0]; @@ -2101,7 +2101,7 @@ import { console } from "../libs/console.js"; oldLineWidth = this.lineWidth; this.lineWidth = oldLineWidth * options.scale; } - this.pdf.text(options.text, pt.x + Math.max(this.posX, this.margin[3]), pt.y + this.posY, { + this.pdf.text(options.text, pt.x + this.posX + this.margin[3], pt.y + this.posY, { angle: options.angle, align: textAlign, renderingMode: options.renderingMode, From 15264b06dbdc9a00a685af63d176138800db2ae0 Mon Sep 17 00:00:00 2001 From: Jeff Sieu Date: Thu, 29 Oct 2020 17:08:59 +0800 Subject: [PATCH 04/16] Add test cases for html margin, x and y --- test/reference/html-margin.pdf | Bin 0 -> 4134 bytes test/reference/html-x-y.pdf | Bin 0 -> 4135 bytes test/specs/html.spec.js | 16 ++++++++++++++++ 3 files changed, 16 insertions(+) create mode 100644 test/reference/html-margin.pdf create mode 100644 test/reference/html-x-y.pdf diff --git a/test/reference/html-margin.pdf b/test/reference/html-margin.pdf new file mode 100644 index 0000000000000000000000000000000000000000..dca2023dbbea2f1ed0e991f2b56c334280654e38 GIT binary patch literal 4134 zcmcInU2fwx5Ps(=<|Sx?A`*W@iYkl%YddbzX5qjQ(4xUU$TsbjAxo(wZC+KC` zqjV_CwfvLX+r+XUQ==hg_;L7g=Ip-PyJU_aXMg_n$KON%g={z@t(F_UJQ;BBqp2bI z%uQMX!>FHaqmnzEk6UJfhMR?l(~uKggR3Bv~e-`jZpZWFb9@*KHX?j z2R~ByfpA9)PkBgVvy#aHJ>j7SyL&N&Lb5c-3UDDp$f$ob#0< zS|LVnR^99C{!!nqI(Y?02M}ny5&gYDw?pFg7=?gBI3bJ*$7_T|prwZJ4p+A^%${XY ziaV4sDFQFh+V{DX5RS=tJ1XK4TtGM@*WrE#nnO3+C-hcI>99ch9v6WQ6!q|>a`z_u zc=!I!Dui%Ce2q36m)7bSWIe_mjy`HG71LaB>EUn)?Tv}XaXLK#xNq`T2#g>pF`y~+ zCH1r?g34chG_c4yHL29>V~K8u1`Fc;7>^5hv?MuzioF(=6)h>%9p%3rNllzUiUq?JOC;zX$$Tc+ejv@vjR!T;3Z7{S{7F7*6I$aL|zso};nJ-yZ|y*Hr|+ z7KpX&6GZ}BZ=pjDoD6?v)00&au}o*ScViQT)A_N^s$Rn)jOV7f9Axt-JwY%uld1hU zEbX6$6xd->T$;Zvqa+@kVE%`RaXtT&IdPa9e~F$ZT|8ljQHf{GHZ2}?1hB*8_wX_w zX375?zqzCC^KA5BN_cd!h5$D-&v?Au&aq%<*Z-aR{}#{6>hx>1Z0-j(Ah3?;aj+|6 zTLLURw&j7Hf^A8#1ZrCfbN{;OL&Ypss-fE8wiy7c4rOejCR-L0*k(`#pH)F!wz7uG zY-JCXE6@h^Aa#H44`*gn0y>JX>vNqoxf~}xUW`l*=d;32ojUltz+wjLk}}THZd4jL z?|#Pu?K95m66IS;zrp*WF)H6>=}v^3Ae-8fYl;O{U2*lFO!{GD$6jS0m=TmWkS>x0 zv9-E)(Mz_#1?Z=ylO>CJ+OYGSn+aAvoRM|81WHOFVFI-b(Uon7~P{{Y`CGU5OL literal 0 HcmV?d00001 diff --git a/test/reference/html-x-y.pdf b/test/reference/html-x-y.pdf new file mode 100644 index 0000000000000000000000000000000000000000..701e33584066b1cac2c4038c2a42f023513b6f98 GIT binary patch literal 4135 zcmcInU2fwx5Ps(=<|Sx?A`<^e6+wWFlQe0wNZ=S~(O@5BoAxTOrBssJq-W|0dYSep z9m?`rww&7A#Ij*g^lWc^2SauW7lo*aaC@z@dk z&0Sgo!+*OZ?7AYG||+) zo(qtbr_)p7(xC_7pXWeGc;nJ>`573EB7zo0nY(yOXrQ#Ajf=tWgu?fP1u*~SXWM7VzLzvI~`b&AcpnRjLQ7F9S)fpqD6d zB*d_Ez6IfxoBZa=WDt|^e%3E5mGxT7NO+MBvWnsGrzHK57RjOmcTAEOW#@C8gW#x$ zas9xRYD}(opZxeNn+)F{f%ysqxBW@->kLnq_ol&q1qe^W-hf0~fpqX7Oy0eK;e_dI_{rLcs{?J|-wq=r2eYeS&#} zt9=xc*H#}>7#OQ|Oz75FCT(jh(`|puNDP8Cf3`iAQ`)lQSPiU?=$Yyn(f{#tx{JvtA2UT6=VcW!LMclQ4lZsl{{=JXG93T_ literal 0 HcmV?d00001 diff --git a/test/specs/html.spec.js b/test/specs/html.spec.js index 579ddf6cf..c60dd0ab3 100644 --- a/test/specs/html.spec.js +++ b/test/specs/html.spec.js @@ -14,4 +14,20 @@ describe("Module: html", function() { ); comparePdf(doc.output(), "html-basic.pdf", "html"); }); + + it("html margin insets properly", async () => { + const doc = jsPDF(); + await new Promise(resolve => + doc.html("

Basic HTML

Heading 2

", { callback: resolve, margin: [10, 10] }) + ); + comparePdf(doc.output(), "html-margin.pdf", "html"); + }); + + it("html x, y offsets properly", async () => { + const doc = jsPDF(); + await new Promise(resolve => + doc.html("

Basic HTML

Heading 2

", { callback: resolve, x: 30, y: 40 }) + ); + comparePdf(doc.output(), "html-x-y.pdf", "html"); + }); }); From d2068505969fed1579980a8321b60c145f6c0eb7 Mon Sep 17 00:00:00 2001 From: Jeff Sieu Date: Fri, 30 Oct 2020 01:40:19 +0800 Subject: [PATCH 05/16] Fix html tests --- src/modules/context2d.js | 6 +++--- test/reference/html-basic.pdf | Bin 3354 -> 3354 bytes test/reference/html-margin.pdf | Bin 4134 -> 3582 bytes test/reference/html-x-y.pdf | Bin 4135 -> 3579 bytes test/specs/html.spec.js | 4 ++-- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/modules/context2d.js b/src/modules/context2d.js index 627111c70..3330bf945 100644 --- a/src/modules/context2d.js +++ b/src/modules/context2d.js @@ -1475,7 +1475,7 @@ import { console } from "../libs/console.js"; var topMargin = (i === min ? this.posY + this.margin[0] : this.margin[0]); var firstPageHeight = this.pdf.internal.pageSize.height - this.posY - this.margin[0] - this.margin[2]; - var pageHeightMinusMargin = this.pdf.internal.pageSize.height - this.margin[2]; + var pageHeightMinusMargin = this.pdf.internal.pageSize.height - this.margin[0] - this.margin[2]; var pageWidthMinusMargin = this.pdf.internal.pageSize.width - this.margin[1]; var previousPageHeightSum = i === 1 ? 0 : firstPageHeight + (i - 2) * pageHeightMinusMargin; @@ -1677,7 +1677,7 @@ import { console } from "../libs/console.js"; var topMargin = (k === min ? this.posY + this.margin[0] : this.margin[0]); var firstPageHeight = this.pdf.internal.pageSize.height - this.posY - this.margin[0] - this.margin[2]; - var pageHeightMinusMargin = this.pdf.internal.pageSize.height - this.margin[2]; + var pageHeightMinusMargin = this.pdf.internal.pageSize.height - this.margin[0] - this.margin[2]; var previousPageHeightSum = k === 1 ? 0 : firstPageHeight + (k - 2) * pageHeightMinusMargin; if (this.ctx.clip_path.length !== 0) { @@ -2042,7 +2042,7 @@ import { console } from "../libs/console.js"; var topMargin = (i === min ? this.posY + this.margin[0] : this.margin[0]); var firstPageHeight = this.pdf.internal.pageSize.height - this.posY - this.margin[0] - this.margin[2]; - var pageHeightMinusMargin = this.pdf.internal.pageSize.height - this.margin[2]; + var pageHeightMinusMargin = this.pdf.internal.pageSize.height - this.margin[0] - this.margin[2]; var pageWidthMinusMargin = this.pdf.internal.pageSize.width - this.margin[1]; var previousPageHeightSum = i === 1 ? 0 : firstPageHeight + (i - 2) * pageHeightMinusMargin; diff --git a/test/reference/html-basic.pdf b/test/reference/html-basic.pdf index 89971084184cd31c501681b7287ec01bc69af480..8bc4ef4cab66531f4219038f1d15e565827a1db5 100644 GIT binary patch delta 47 zcmbOwHA`y32SEc9JyUaq94-Sr1yeA4G9zO)i-Df8+2jJoL>6N`Q6;BGt13a8C|&nENu)F diff --git a/test/reference/html-margin.pdf b/test/reference/html-margin.pdf index dca2023dbbea2f1ed0e991f2b56c334280654e38..c83e32d32169a4e8c41813cdb1600984b8345b63 100644 GIT binary patch delta 817 zcmb7CJxfC|6a{gx859&jaPT@*6eLY!4L@d+F}D*`7KCk_3@@Gc9{C~go|gc zbxHX$8(Jb)Go@Ce*&TEOA7}qiQ%Z4dQaPM=_5QWKQ`i%h1c+q8Aikz_AsIV8+&mhi z&ZmSEHoziauZ`DC+KC` zqjV_CwfvLX+r+XUQ==hg_;L7g=Ip-PyJU_aXMg_n$KON%g={z@t(F_UJQ;BBqp2bI z%uQMX!>FHaqmnzEk6UJfhMR?l(~uKggR3Bv~e-`jZpZWFb9@*KHX?j z2R~ByfpA9)PkBgVvy#aHJ>j7SyL&N&Lb5c-3UDDp$f$ob#0< zS|LVnR^99C{!!nqI(Y?02M}ny5&gYDw?pFg7=?gBI3bJ*$7_T|prwZJ4p+A^%${XY ziaV4sDFQFh+V{DX5RS=tJ1XK4TtGM@*WrE#nnO3+C-hcI>99ch9v6WQ6!q|>a`z_u zc=!I!Dui%Ce2q36m)7bSWIe_mjy`HG71LaB>EUn)?Tv}XaXLK#xNq`T2#g>pF`y~+ zCH1r?g34chG_c4yHL29>V~K8u1`Fc;7>^5hv?MuzioF(=6)h>%9p%3rNllzUiUq?JOC;zX$$Tc+ejv@vjR!T;3Z7{S{7F7*6I$aL|zso};nJ-yZ|y*Hr|+ z7KpX&6GZ}BZ=pjDoD6?v)00&au}o*ScViQT)A_N^s$Rn)jOV7f9Axt-JwY%uld1hU zEbX6$6xd->T$;Zvqa+@kVE%`RaXtT&IdPa9e~F$ZT|8ljQHf{GHZ2}?1hB*8_wX_w zX375?zqzCC^KA5BN_cd!h5$D-&v?Au&aq%<*Z-aR{}#{6>hx>1Z0-j(Ah3?;aj+|6 zTLLURw&j7Hf^A8#1ZrCfbN{;OL&Ypss-fE8wiy7c4rOejCR-L0*k(`#pH)F!wz7uG zY-JCXE6@h^Aa#H44`*gn0y>JX>vNqoxf~}xUW`l*=d;32ojUltz+wjLk}}THZd4jL z?|#Pu?K95m66IS;zrp*WF)H6>=}v^3Ae-8fYl;O{U2*lFO!{GD$6jS0m=TmWkS>x0 zv9-E)(Mz_#1?Z=ylO>CJ+OYGSn+aAvoRM|81WHOFVFI-b(Uon7~P{{Y`CGU5OL diff --git a/test/reference/html-x-y.pdf b/test/reference/html-x-y.pdf index 701e33584066b1cac2c4038c2a42f023513b6f98..cd8cab14ed6c21c59682d2ae61785ecf050443b9 100644 GIT binary patch delta 814 zcmb7BJxfC|6osN-Gbkv6;NV55C`g*zm%Q989jYxN6_*UI#eUFYE3|@((7ltT{s(`6 zf5SiHQ9{Tey@U4VEH{13?JEtgwpg0; zY7=v7z%|%uxjyXN!x^|PtGE>Sl{-qnxCX3@;4+?puiQ;73%&MY%8nJQQb0`LTFjfo z)jA?H9Ot8&^+u=P4wPK{V;m~t%2Rn;xG3x6dt)zuAdE5~k^_BUvxh((tLgFf$p}@p zB*-r>(=II9Y)%Rt=A^}-FIs_BI_(=RXp)*_05&oTa->3HYeJ7QG=dl{&AUvDVid<& c5>$SNnO{JXfjcBi)A7z*-j890Lglvl3$Tf)t^fc4 literal 4135 zcmcInU2fwx5Ps(=<|Sx?A`<^e6+wWFlQe0wNZ=S~(O@5BoAxTOrBssJq-W|0dYSep z9m?`rww&7A#Ij*g^lWc^2SauW7lo*aaC@z@dk z&0Sgo!+*OZ?7AYG||+) zo(qtbr_)p7(xC_7pXWeGc;nJ>`573EB7zo0nY(yOXrQ#Ajf=tWgu?fP1u*~SXWM7VzLzvI~`b&AcpnRjLQ7F9S)fpqD6d zB*d_Ez6IfxoBZa=WDt|^e%3E5mGxT7NO+MBvWnsGrzHK57RjOmcTAEOW#@C8gW#x$ zas9xRYD}(opZxeNn+)F{f%ysqxBW@->kLnq_ol&q1qe^W-hf0~fpqX7Oy0eK;e_dI_{rLcs{?J|-wq=r2eYeS&#} zt9=xc*H#}>7#OQ|Oz75FCT(jh(`|puNDP8Cf3`iAQ`)lQSPiU?=$Yyn(f{#tx{JvtA2UT6=VcW!LMclQ4lZsl{{=JXG93T_ diff --git a/test/specs/html.spec.js b/test/specs/html.spec.js index c60dd0ab3..3961451ea 100644 --- a/test/specs/html.spec.js +++ b/test/specs/html.spec.js @@ -16,7 +16,7 @@ describe("Module: html", function() { }); it("html margin insets properly", async () => { - const doc = jsPDF(); + const doc = jsPDF({ floatPrecision: 2 }); await new Promise(resolve => doc.html("

Basic HTML

Heading 2

", { callback: resolve, margin: [10, 10] }) ); @@ -24,7 +24,7 @@ describe("Module: html", function() { }); it("html x, y offsets properly", async () => { - const doc = jsPDF(); + const doc = jsPDF({ floatPrecision: 2 }); await new Promise(resolve => doc.html("

Basic HTML

Heading 2

", { callback: resolve, x: 30, y: 40 }) ); From 56f27473b48669df8088cec772db05ed9fdaa081 Mon Sep 17 00:00:00 2001 From: Jeff Sieu Date: Sat, 31 Oct 2020 15:54:09 +0800 Subject: [PATCH 06/16] Changes --- src/jspdf.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jspdf.js b/src/jspdf.js index de3a7bf3c..d1dbc9ae6 100644 --- a/src/jspdf.js +++ b/src/jspdf.js @@ -2633,7 +2633,7 @@ function jsPDF(options) { newtext.push(bch); newtext.push(ch - (bch << 8)); } - return String.fromCharCode.apply(undefined, newtext); + return String.fromCharCodeapply(undefined, newtext); }; var pdfEscape = (API.__private__.pdfEscape = API.pdfEscape = function( From ccd252eae685fa46a67c1397a74bdca0d4ffaa38 Mon Sep 17 00:00:00 2001 From: Jeff Sieu Date: Sat, 31 Oct 2020 15:54:24 +0800 Subject: [PATCH 07/16] Changes --- src/jspdf.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jspdf.js b/src/jspdf.js index d1dbc9ae6..de3a7bf3c 100644 --- a/src/jspdf.js +++ b/src/jspdf.js @@ -2633,7 +2633,7 @@ function jsPDF(options) { newtext.push(bch); newtext.push(ch - (bch << 8)); } - return String.fromCharCodeapply(undefined, newtext); + return String.fromCharCode.apply(undefined, newtext); }; var pdfEscape = (API.__private__.pdfEscape = API.pdfEscape = function( From 824be42de26a1c5e2cd6107b04c9c50631442819 Mon Sep 17 00:00:00 2001 From: Jeff Sieu Date: Sat, 31 Oct 2020 16:20:26 +0800 Subject: [PATCH 08/16] Fix html test cases --- test/reference/html-basic.pdf | Bin 3354 -> 3358 bytes test/reference/html-margin.pdf | Bin 3582 -> 3588 bytes test/reference/html-x-y.pdf | Bin 3579 -> 3582 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/test/reference/html-basic.pdf b/test/reference/html-basic.pdf index 8bc4ef4cab66531f4219038f1d15e565827a1db5..d39a7c8d9e40ca645aca1b4be8e4f94de2a8f5ff 100644 GIT binary patch delta 67 zcmbOwHBU-NHNeG9*HF)xOZEN!z4zAuiHUrV1Pu-Jj7$`AxD50ZOu_8QjEvbVMtY_e TlM5IVSTS E0F^gcH~;_u delta 440 zcmZpX`6tb%8sOrlYp7?;rMm0>ngXtGuAURQpn*lvd}X&RWJf`Ckrx` zvl!_a7*6hCOkp&=F`R7#dodVu%@I(Q9Igsm|2G1fsVd z;Uq(ILo*BmEle>jwzRChiMb()AV|!@5X03*mcSrS P=bgrG%B8C6>hA^sAaGq6 diff --git a/test/reference/html-x-y.pdf b/test/reference/html-x-y.pdf index cd8cab14ed6c21c59682d2ae61785ecf050443b9..034e4959e175ddf2dbfe27168e34c447aeb5c0b6 100644 GIT binary patch delta 449 zcmew@{ZCp*HNeG9*HF)xOZEN!z4zAuiHUs8jD`~@rHWaY>ls)oKT|Q7(%%d zABs*kVC0zifelFc^IPf}S}KI3aA`Ot7H1|aOy18VJ((YfV;B|LOw3L6j0`8gW=vo) zHr6wr9Ke*qYHF@$Xuf$TlMN@M@#JsZ*7e2;1|Xo2r@#ef7#LU@S)hv<8kk~=8Cn`* zs58c**TfW4ovDQZL~lL9VncI7BMbvAOfW6BG&e(6XJlxA;SnPvV+`|*jLl3@^y&jW hZDe9*jv@#WvoOSPwULFT`Q$j>Y3znvs;aL3ZUCTNWETJc delta 386 zcmew-{acz(HNeG9*HF)xOLf=%H4i2VH8UDboR%tLZlPytp^(F6VX9|fqF@N-PUdGU zVKLLQG@sngn8IS9XJ|J0J!68Pk-46cnL=9Bf96xfVREc8r_ zHt%7w;bb(K{FB?-)jYbRdw}u0|3hZRUZHV From 0d4f672ebafc5e77cc7e8960686c0c0aa8d0ca57 Mon Sep 17 00:00:00 2001 From: Jeff Sieu Date: Sat, 31 Oct 2020 16:29:08 +0800 Subject: [PATCH 09/16] Fix test cases --- test/reference/encrypted_standard.pdf | Bin 3314 -> 3423 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/test/reference/encrypted_standard.pdf b/test/reference/encrypted_standard.pdf index cdc5d3188880c1b9a70871b918d2754b247e82b8..41dc2b2972226988f434815cb7845b1f535475dc 100644 GIT binary patch delta 278 zcmew)d0$FMHNeG9*HF)xOZEN!z4zAuiHUsu^*{=O?BOh5AWQt_Y6#yp6-YC4WvBxw zR!KjIU=)y&2VwzvO+!y04bocy#1Isr24?~pAdL`|6Aotr8Ig6uV(<40L*;@XQn^w< zdgD_@&Hx~Nr0Md0AY}oODgsiPT>8#Msfi_-`FSphC8-J;5Gf#edWSWHoeLL=Ee0|L dftXbkNNg78(q&?_nC!~4jNOP!Rn^ts4FHtDr-A?g delta 169 zcmV;a09OCs8uA$qB~V00Eio=L3MIPVtl*Ink4U`#P8sD=s z0Ragyr^JHOzo5zz$| X<+FqeEdl{Ild=q^2QdmIB}Gq0RF+KD From 365b9bb93ef1f0bb39f1429d941ad8525600a12a Mon Sep 17 00:00:00 2001 From: Jeff Sieu Date: Sat, 31 Oct 2020 16:48:41 +0800 Subject: [PATCH 10/16] Fix tests --- test/reference/encrypted_standard.pdf | Bin 3423 -> 3386 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/test/reference/encrypted_standard.pdf b/test/reference/encrypted_standard.pdf index 41dc2b2972226988f434815cb7845b1f535475dc..45122e2ebf5071ec0fbd85ab7c7f6d952af454ea 100644 GIT binary patch delta 159 zcmV;Q0AT;$8oC-1B~V00Eio=L3MIn2!=J*e!=RB8kAK3v!@t9&Kf+@e z!;8b8C&IA8n!}E4SHhyghr)@&gIRtP7Q%?byC71-qQi@A8pEGlcM4^0WOH Date: Sat, 31 Oct 2020 16:56:21 +0800 Subject: [PATCH 11/16] Fix tests --- test/reference/encrypted_standard.pdf | Bin 3386 -> 3337 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/test/reference/encrypted_standard.pdf b/test/reference/encrypted_standard.pdf index 45122e2ebf5071ec0fbd85ab7c7f6d952af454ea..bef05083aa18e4e03a109a3daab409ed3be4d411 100644 GIT binary patch delta 106 zcmV-w0G0o`8i^VWB~V00Eio=L3MJZ~%#jh0LEOLPzogolO~aSjtJ|49W#@yaCj}W! z>$jT6pVp8cDKSa&zq7d9i{Gcvn$E0O!iUL&S$-21$h#m?;)|^s-<-1#0euSrH0piC&GXQ8BW8i!?#+)habYW5FaTqNyD>0!?Sq9yu+HqzE~#1uEU(e zi^HEM!mz@c!;WlM!lJ^5!imF!S$-21!idAWAX3Ak!;5Vi!=GGt3T19&b98cLVQmU! ZZe(v_vF~gP0XUOf45kM%3MC~)Pe$lVQpf-R From 4f05e8c37066df1f694c9aef8260e2dc693a74c5 Mon Sep 17 00:00:00 2001 From: Lukas Hollaender Date: Thu, 5 Nov 2020 16:27:49 +0100 Subject: [PATCH 12/16] add a couple of test cases (results are still wrong) --- test/reference/encrypted_standard.pdf | Bin 3337 -> 3314 bytes test/reference/html-basic.pdf | Bin 3358 -> 3354 bytes .../reference/html-margin-page-break-text.pdf | Bin 0 -> 10414 bytes test/reference/html-margin-page-break.pdf | Bin 0 -> 4298 bytes test/reference/html-margin-x-y.pdf | Bin 0 -> 3573 bytes test/reference/html-margin.pdf | Bin 3588 -> 3573 bytes test/reference/html-x-y.pdf | Bin 3582 -> 3573 bytes test/specs/html.spec.js | 95 +++++++++++++++++- 8 files changed, 91 insertions(+), 4 deletions(-) create mode 100644 test/reference/html-margin-page-break-text.pdf create mode 100644 test/reference/html-margin-page-break.pdf create mode 100644 test/reference/html-margin-x-y.pdf diff --git a/test/reference/encrypted_standard.pdf b/test/reference/encrypted_standard.pdf index bef05083aa18e4e03a109a3daab409ed3be4d411..cdc5d3188880c1b9a70871b918d2754b247e82b8 100644 GIT binary patch delta 154 zcmV;L0A>G)8uA$qB~V00Eio=L3MIPVtl*Ijk4e1$P8sA)@ z-U%_M#Ddems5r;Ua+E0wFGF%=VRUJ4ZbV^pWgsYtjo_!txI5X&ZIqt%U2}vH(FGOd Ivn~qT0tazQG5`Po delta 177 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 JoxK|m{|Cj~P{;rP literal 0 HcmV?d00001 diff --git a/test/reference/html-margin-x-y.pdf b/test/reference/html-margin-x-y.pdf new file mode 100644 index 0000000000000000000000000000000000000000..6a8a16ab27420a057579b9ec6bf622048e82b8dc GIT binary patch literal 3573 zcmcIm?@rq=5dZF{xG!j+i6(ZMHkm3UXeqEUm9(`j=5S^ms(w5O)d;MMQ0=QUoHCfz z43kvXuYv>|f!Gi7fpFb9X;TonY?`rIOWx8s>~AeG7xjy8$tq~U7^Qr!qkC{#h+8Pd43Q!=f|u_J{F7fa4gf^ zc03N$2EZ_1H&=rpv%i_7z}F|m%>0QCqOiY#{jX1qv;A+(iS@bhTW~XMVE*p~8f(pR zT6|Us;OmoL=YHIaqgU#mzfosN-0w&YT?<=6u+HTjmba5J_J0TYx1N7buvVJtSF0hx zfdGMRVN(E|3{ehHc!(+kZGk8kD1j1H0h7;&tb)lbM0#K@IFUYZQB|JRuT$E(UBdqk_N~~1 z>M&M~Kug$eeBi!UJ?%cQ zBhbg7CQ*d3CE3&9KAzwKw3MvJ(PXUV?A#@Ch%Kw&EcXRJP;p(yg&|CN*aL%^1~uhj zdx3{RzZuWADWpq0E->UvysBSZ>kB-lMLBM}MIQI?sFviM6>H1oqMTbT_{Xa@9`}+y zuUxe2SBo*Qi~A5l6!ZzFf~UD?LzF9cVU~EFUGQ0W{+ygnf-u6SfG$fX{3+?&taco0 sFnB>QpQvFB29h*rKo@Gp$`h}>=ZIVqW>Os_+8{>weuZsq9k!bP0Dw`qtN;K2 literal 0 HcmV?d00001 diff --git a/test/reference/html-margin.pdf b/test/reference/html-margin.pdf index 04473b22f868327b3265b3245265a7bf5fb90033..6a8a16ab27420a057579b9ec6bf622048e82b8dc 100644 GIT binary patch delta 913 zcmcIhy-EZz5C$7@{3#S{v?gvHe%8{O`qA z(J?KhiWssyi)uOMQBBKO$>ebY^v=(NSVR#a359JDl8U05#>g~jL*;0-1TscJlJtjz EADAMza{vGU delta 944 zcmbV~ze)o^5XQkqT+zZp5iJJcz!a`Kv%9yuO^RTmMMR4$Y=ikjf=Q7GmIvYsguI5G zwfGP|KpsIGd+#nex!idm9?P-w?ach2SxpK5LvHm(2Tkd!0W=zv=elo-wRi zWt01Hr!{1PYXc8V!1;836cV>Gi3W@;ml`Zet`!Vd!@FfjhB!(n3756$X<681DV2vH zc)Z5FRI3H-w1EPgfS}7zMAJafjHb?HCWHFE;v^wxv*TK8(!YfY1OrwN(ni^e?0(5! zaKb-;w07~u#Gs4dviTp30N=)TjODcACX9_Ib`)A})PmA!wfm#qzYLXWa3$1YZGvTDlYbgAphRzi7miY+Crj@o^ig|*`gsV8X=N}ek2WM`eD+Vl#v;6p;;_UO-{(!&DM-bAq?}p`c>l_ DVsyqD diff --git a/test/reference/html-x-y.pdf b/test/reference/html-x-y.pdf index 034e4959e175ddf2dbfe27168e34c447aeb5c0b6..6a8a16ab27420a057579b9ec6bf622048e82b8dc 100644 GIT binary patch delta 883 zcmcIhJxjzu5Ctn8JwXqvYZf7;VRn-2M{#GPC)k`KVts`Qa(8g2U>Wcq9Q!l0v$OMm z2>K^1Y{bnbVm6!Bu#kQ8X5V|W``!EOZAK>t!#y4+5WT!VeJr;hMnb?~0JnFG%kdO2 zCUy_Vjv&F9sf5Q$vng==LlZdj$)Z!HuG4bM$K9OAPUI%+s}v_CL)9VY<$=<;YHI~o z;9Ta#uUw038{a0!oE?`WvPOioK86FHs|S8vy)zduE2XWa)w0XF*7rq%++N_nCh*?I z%jdwkH-`sWhpMjfwB;&mb3DJTtgh{FhQ-Hb4BQ4O%ar+A*ZWedCWo4mW{qPO*0>U8 zzR~T6Oqz;7A(L59lPJw=tAdtcW(>0uku+PKZXcW4lnM%J3WHpvG?{V70BJ2CiVjD^ EAN`iIcmMzZ delta 937 zcmbV~y-EW?5XZqvSF}*XBF!KiXyH1udwaXvSOgO-B3f)=8_Wj@CPgAx9{2_!uVH5` zK7Zimd3qIf9W-rNSvip3fiJhOb z@#Co795Ccc!xKZeoXn15!HvvRpBcq51T73#F!-Y$=8BBwXyG53XBL;sRfiQo4w$?# zhf%kPRUJF46M{u>9Y!dVMqDFlIJ0KXyho)iCkw8{j;qaa?;cLT^;yjog~}P_^PFAc z#H&G}4!NKVbmgua>tQ03j-w41{xosAaw#R(Ii%fe^@iOw=UD|xIYMbpIY`V$* { - const doc = jsPDF({ floatPrecision: 2 }); + const doc = jsPDF({ floatPrecision: 2, unit: "pt" }); + doc.line(30, 10, 100, 10); + doc.line(30, 10, 30, 100); await new Promise(resolve => - doc.html("

Basic HTML

Heading 2

", { callback: resolve, margin: [10, 10] }) + doc.html( + "
", + { + callback: resolve, + margin: [10, 30] + } + ) ); comparePdf(doc.output(), "html-margin.pdf", "html"); }); + it("html margin on page break", async () => { + const doc = jsPDF({ floatPrecision: 2, unit: "pt" }); + doc.rect( + 30, + 10, + doc.internal.pageSize.getWidth() - 60, + doc.internal.pageSize.getHeight() - 20 + ); + await new Promise(resolve => + doc.html( + "
", + { + callback: resolve, + margin: [10, 30, 10, 30] + } + ) + ); + doc.rect( + 30, + 10, + doc.internal.pageSize.getWidth() - 60, + doc.internal.pageSize.getHeight() - 20 + ); + comparePdf(doc.output(), "html-margin-page-break.pdf", "html"); + }); + it("html x, y offsets properly", async () => { - const doc = jsPDF({ floatPrecision: 2 }); + const doc = jsPDF({ floatPrecision: 2, unit: "pt" }); + doc.line(30, 10, 100, 10); + doc.line(30, 10, 30, 100); await new Promise(resolve => - doc.html("

Basic HTML

Heading 2

", { callback: resolve, x: 30, y: 40 }) + doc.html( + "
", + { + callback: resolve, + x: 30, + y: 10 + } + ) ); comparePdf(doc.output(), "html-x-y.pdf", "html"); }); + + it("html x, y + margin offsets properly", async () => { + const doc = jsPDF({ floatPrecision: 2, unit: "pt" }); + doc.line(30, 10, 100, 10); + doc.line(30, 10, 30, 100); + await new Promise(resolve => + doc.html( + "
", + { + callback: resolve, + x: 10, + y: 3, + margin: [7, 20] + } + ) + ); + comparePdf(doc.output(), "html-margin-x-y.pdf", "html"); + }); + + it("page break with text", async () => { + const doc = jsPDF({ floatPrecision: 2, unit: "pt" }); + doc.rect( + 30, + 10, + doc.internal.pageSize.getWidth() - 60, + doc.internal.pageSize.getHeight() - 20 + ); + await new Promise(resolve => + doc.html( + "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.", + { + callback: resolve, + margin: [10, 30, 10, 30] + } + ) + ); + doc.rect( + 30, + 10, + doc.internal.pageSize.getWidth() - 60, + doc.internal.pageSize.getHeight() - 20 + ); + comparePdf(doc.output(), "html-margin-page-break-text.pdf", "html"); + }); }); From 14bacf1201fed1d8b5b23ee6f9022b74ca0cde18 Mon Sep 17 00:00:00 2001 From: Jeff Sieu Date: Wed, 18 Nov 2020 19:19:33 +0800 Subject: [PATCH 13/16] Apply suggestions from code review Co-authored-by: Rui-Jesus <32493599+Rui-Jesus@users.noreply.github.com> --- src/modules/context2d.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/context2d.js b/src/modules/context2d.js index 3330bf945..6d36f444a 100644 --- a/src/modules/context2d.js +++ b/src/modules/context2d.js @@ -2040,7 +2040,7 @@ import { console } from "../libs/console.js"; for (var i = min; i < max + 1; i++) { this.pdf.setPage(i); - var topMargin = (i === min ? this.posY + this.margin[0] : this.margin[0]); + var topMargin = (i === 1 ? this.posY + this.margin[0] : this.margin[0]); var firstPageHeight = this.pdf.internal.pageSize.height - this.posY - this.margin[0] - this.margin[2]; var pageHeightMinusMargin = this.pdf.internal.pageSize.height - this.margin[0] - this.margin[2]; var pageWidthMinusMargin = this.pdf.internal.pageSize.width - this.margin[1]; @@ -2052,7 +2052,7 @@ import { console } from "../libs/console.js"; this.path = pathPositionRedo( clipPath, this.posX + this.margin[3], - -1 * previousPageHeightSum + topMargin + this.ctx.prevPageLastElemOffset + -1 * previousPageHeightSum + topMargin ); drawPaths.call(this, "fill", true); this.path = tmpPaths; From 1643669601687925311586b7f69ba25afbf29485 Mon Sep 17 00:00:00 2001 From: Lukas Hollaender Date: Wed, 14 Jul 2021 16:27:38 +0200 Subject: [PATCH 14/16] - fix issues with text baseline & use the correct bounding boxes at the right place - fix getPagesByPath (needs to respect prevPageLastElemOffset, too) - add clip path at margins for images and paths - improve doc/typings --- src/modules/context2d.js | 239 +++++++++++++----- src/modules/html.js | 2 +- .../html-margin-page-break-image.pdf | Bin 0 -> 5261 bytes .../reference/html-margin-page-break-text.pdf | Bin 10414 -> 24356 bytes test/reference/html-margin-page-break.pdf | Bin 4298 -> 4984 bytes test/reference/html-margin-x-y-text.pdf | Bin 0 -> 3404 bytes test/specs/context2d.spec.js | 16 ++ test/specs/html.spec.js | 96 ++++--- types/index.d.ts | 1 + 9 files changed, 258 insertions(+), 96 deletions(-) create mode 100644 test/reference/html-margin-page-break-image.pdf create mode 100644 test/reference/html-margin-x-y-text.pdf diff --git a/src/modules/context2d.js b/src/modules/context2d.js index 6d36f444a..eebff2141 100644 --- a/src/modules/context2d.js +++ b/src/modules/context2d.js @@ -160,8 +160,9 @@ import { console } from "../libs/console.js"; }); /** + * Gets or sets the page margin when using auto paging. Has no effect when {@link autoPaging} is off. * @name margin - * @type {array} + * @type {number|number[]} * @default [0, 0, 0, 0] */ Object.defineProperty(this, "margin", { @@ -169,7 +170,17 @@ import { console } from "../libs/console.js"; return _ctx.margin; }, set: function(value) { - _ctx.margin = value; + var margin; + if (typeof value === "number") { + margin = [value, value, value, value]; + } else { + margin = new Array(4); + margin[0] = value[0]; + margin[1] = value.length >= 2 ? value[1] : margin[0]; + margin[2] = value.length >= 3 ? value[2] : margin[0]; + margin[3] = value.length >= 4 ? value[3] : margin[1]; + } + _ctx.margin = margin; } }); @@ -1143,7 +1154,6 @@ import { console } from "../libs/console.js"; return; } - y = getBaseline.call(this, y); var degs = rad2deg(this.ctx.transform.rotation); // We only use X axis as scale hint @@ -1181,7 +1191,6 @@ import { console } from "../libs/console.js"; } maxWidth = isNaN(maxWidth) ? undefined : maxWidth; - y = getBaseline.call(this, y); var degs = rad2deg(this.ctx.transform.rotation); var scale = this.ctx.transform.scaleX; @@ -1384,6 +1393,15 @@ import { console } from "../libs/console.js"; } }; + var hasMargins = function() { + return ( + this.margin[0] > 0 || + this.margin[1] > 0 || + this.margin[2] > 0 || + this.margin[3] > 0 + ); + }; + /** * Draws an image, canvas, or video onto the canvas * @@ -1473,11 +1491,18 @@ import { console } from "../libs/console.js"; for (var i = min; i < max + 1; i++) { this.pdf.setPage(i); - var topMargin = (i === min ? this.posY + this.margin[0] : this.margin[0]); - var firstPageHeight = this.pdf.internal.pageSize.height - this.posY - this.margin[0] - this.margin[2]; - var pageHeightMinusMargin = this.pdf.internal.pageSize.height - this.margin[0] - this.margin[2]; - var pageWidthMinusMargin = this.pdf.internal.pageSize.width - this.margin[1]; - var previousPageHeightSum = i === 1 ? 0 : firstPageHeight + (i - 2) * pageHeightMinusMargin; + var pageWidthMinusMargins = + this.pdf.internal.pageSize.width - this.margin[3] - this.margin[1]; + var topMargin = i === 1 ? this.posY + this.margin[0] : this.margin[0]; + var firstPageHeight = + this.pdf.internal.pageSize.height - + this.posY - + this.margin[0] - + this.margin[2]; + var pageHeightMinusMargins = + this.pdf.internal.pageSize.height - this.margin[0] - this.margin[2]; + var previousPageHeightSum = + i === 1 ? 0 : firstPageHeight + (i - 2) * pageHeightMinusMargins; if (this.ctx.clip_path.length !== 0) { var tmpPaths = this.path; @@ -1485,7 +1510,7 @@ import { console } from "../libs/console.js"; this.path = pathPositionRedo( clipPath, this.posX + this.margin[3], - -1 * previousPageHeightSum + topMargin + this.ctx.prevPageLastElemOffset + -previousPageHeightSum + topMargin + this.ctx.prevPageLastElemOffset ); drawPaths.call(this, "fill", true); this.path = tmpPaths; @@ -1494,19 +1519,38 @@ import { console } from "../libs/console.js"; tmpRect = pathPositionRedo( [tmpRect], this.posX + this.margin[3], - -1 * previousPageHeightSum + topMargin + this.ctx.prevPageLastElemOffset + -previousPageHeightSum + topMargin + this.ctx.prevPageLastElemOffset )[0]; + + const needsClipping = (i > min || i < max) && hasMargins.call(this); + + if (needsClipping) { + this.pdf.saveGraphicsState(); + this.pdf + .rect( + this.margin[3], + this.margin[0], + pageWidthMinusMargins, + pageHeightMinusMargins, + null + ) + .clip() + .discardPath(); + } this.pdf.addImage( img, "JPEG", tmpRect.x, tmpRect.y, - Math.min(tmpRect.w, this.pdf.internal.pageSize.width - this.margin[1] - tmpRect.x), + tmpRect.w, tmpRect.h, null, null, angle ); + if (needsClipping) { + this.pdf.restoreGraphicsState(); + } } } else { this.pdf.addImage( @@ -1526,20 +1570,23 @@ import { console } from "../libs/console.js"; var getPagesByPath = function(path, pageWrapX, pageWrapY) { var result = []; pageWrapX = pageWrapX || this.pdf.internal.pageSize.width; - pageWrapY = pageWrapY || this.pdf.internal.pageSize.height - this.margin[0] - this.margin[2]; + pageWrapY = + pageWrapY || + this.pdf.internal.pageSize.height - this.margin[0] - this.margin[2]; + var yOffset = this.posY + this.ctx.prevPageLastElemOffset; switch (path.type) { default: case "mt": case "lt": - result.push(Math.floor((path.y + this.posY) / pageWrapY) + 1); + result.push(Math.floor((path.y + yOffset) / pageWrapY) + 1); break; case "arc": result.push( - Math.floor((path.y + this.posY - path.radius) / pageWrapY) + 1 + Math.floor((path.y + yOffset - path.radius) / pageWrapY) + 1 ); result.push( - Math.floor((path.y + this.posY + path.radius) / pageWrapY) + 1 + Math.floor((path.y + yOffset + path.radius) / pageWrapY) + 1 ); break; case "qct": @@ -1551,10 +1598,13 @@ import { console } from "../libs/console.js"; path.x, path.y ); - result.push(Math.floor(rectOfQuadraticCurve.y / pageWrapY) + 1); + result.push( + Math.floor((rectOfQuadraticCurve.y + yOffset) / pageWrapY) + 1 + ); result.push( Math.floor( - (rectOfQuadraticCurve.y + rectOfQuadraticCurve.h) / pageWrapY + (rectOfQuadraticCurve.y + rectOfQuadraticCurve.h + yOffset) / + pageWrapY ) + 1 ); break; @@ -1569,15 +1619,18 @@ import { console } from "../libs/console.js"; path.x, path.y ); - result.push(Math.floor(rectOfBezierCurve.y / pageWrapY) + 1); result.push( - Math.floor((rectOfBezierCurve.y + rectOfBezierCurve.h) / pageWrapY) + - 1 + Math.floor((rectOfBezierCurve.y + yOffset) / pageWrapY) + 1 + ); + result.push( + Math.floor( + (rectOfBezierCurve.y + rectOfBezierCurve.h + yOffset) / pageWrapY + ) + 1 ); break; case "rect": - result.push(Math.floor((path.y + this.posY) / pageWrapY) + 1); - result.push(Math.floor((path.y + path.h + this.posY) / pageWrapY) + 1); + result.push(Math.floor((path.y + yOffset) / pageWrapY) + 1); + result.push(Math.floor((path.y + path.h + yOffset) / pageWrapY) + 1); } for (var i = 0; i < result.length; i += 1) { @@ -1675,10 +1728,18 @@ import { console } from "../libs/console.js"; this.lineWidth = lineWidth; this.lineJoin = lineJoin; - var topMargin = (k === min ? this.posY + this.margin[0] : this.margin[0]); - var firstPageHeight = this.pdf.internal.pageSize.height - this.posY - this.margin[0] - this.margin[2]; - var pageHeightMinusMargin = this.pdf.internal.pageSize.height - this.margin[0] - this.margin[2]; - var previousPageHeightSum = k === 1 ? 0 : firstPageHeight + (k - 2) * pageHeightMinusMargin; + var pageWidthMinusMargins = + this.pdf.internal.pageSize.width - this.margin[3] - this.margin[1]; + var topMargin = k === 1 ? this.posY + this.margin[0] : this.margin[0]; + var firstPageHeight = + this.pdf.internal.pageSize.height - + this.posY - + this.margin[0] - + this.margin[2]; + var pageHeightMinusMargins = + this.pdf.internal.pageSize.height - this.margin[0] - this.margin[2]; + var previousPageHeightSum = + k === 1 ? 0 : firstPageHeight + (k - 2) * pageHeightMinusMargins; if (this.ctx.clip_path.length !== 0) { var tmpPaths = this.path; @@ -1686,7 +1747,7 @@ import { console } from "../libs/console.js"; this.path = pathPositionRedo( clipPath, this.posX + this.margin[3], - -1 * previousPageHeightSum + topMargin + this.ctx.prevPageLastElemOffset + -previousPageHeightSum + topMargin + this.ctx.prevPageLastElemOffset ); drawPaths.call(this, rule, true); this.path = tmpPaths; @@ -1695,10 +1756,27 @@ import { console } from "../libs/console.js"; this.path = pathPositionRedo( tmpPath, this.posX + this.margin[3], - -1 * previousPageHeightSum + topMargin + this.ctx.prevPageLastElemOffset + -previousPageHeightSum + topMargin + this.ctx.prevPageLastElemOffset ); if (isClip === false || k === 0) { + const needsClipping = (k > min || k < max) && hasMargins.call(this); + if (needsClipping) { + this.pdf.saveGraphicsState(); + this.pdf + .rect( + this.margin[3], + this.margin[0], + pageWidthMinusMargins, + pageHeightMinusMargins, + null + ) + .clip() + .discardPath(); + } drawPaths.call(this, rule, isClip); + if (needsClipping) { + this.pdf.restoreGraphicsState(); + } } this.lineWidth = oldLineWidth; } @@ -1895,6 +1973,13 @@ import { console } from "../libs/console.js"; } }; + var getTextBottom = function(yBaseLine) { + var height = + this.pdf.internal.getFontSize() / this.pdf.internal.scaleFactor; + var descent = height * (this.pdf.internal.getLineHeightFactor() - 1); + return yBaseLine + descent; + }; + Context2D.prototype.createLinearGradient = function createLinearGradient() { var canvasGradient = function canvasGradient() {}; @@ -2004,26 +2089,25 @@ import { console } from "../libs/console.js"; break; } - var pt = this.ctx.transform.applyToPoint(new Point(options.x, options.y)); + var textDimensions = this.pdf.getTextDimensions(options.text); + var yBaseLine = getBaseline.call(this, options.y); + var yBottom = getTextBottom.call(this, yBaseLine); + var yTop = yBottom - textDimensions.h; + + var pt = this.ctx.transform.applyToPoint(new Point(options.x, yBaseLine)); var decomposedTransformationMatrix = this.ctx.transform.decompose(); var matrix = new Matrix(); matrix = matrix.multiply(decomposedTransformationMatrix.translate); matrix = matrix.multiply(decomposedTransformationMatrix.skew); matrix = matrix.multiply(decomposedTransformationMatrix.scale); - var textDimensions = this.pdf.getTextDimensions(options.text); - var textRect = this.ctx.transform.applyToRectangle( - new Rectangle(options.x, options.y, textDimensions.w, textDimensions.h) + var baselineRect = this.ctx.transform.applyToRectangle( + new Rectangle(options.x, yBaseLine, textDimensions.w, textDimensions.h) ); - var textXRect = matrix.applyToRectangle( - new Rectangle( - options.x, - options.y - textDimensions.h, - textDimensions.w, - textDimensions.h - ) + var textBounds = matrix.applyToRectangle( + new Rectangle(options.x, yTop, textDimensions.w, textDimensions.h) ); - var pageArray = getPagesByPath.call(this, textXRect); + var pageArray = getPagesByPath.call(this, textBounds); var pages = []; for (var ii = 0; ii < pageArray.length; ii += 1) { if (pages.indexOf(pageArray[ii]) === -1) { @@ -2040,11 +2124,20 @@ import { console } from "../libs/console.js"; for (var i = min; i < max + 1; i++) { this.pdf.setPage(i); - var topMargin = (i === 1 ? this.posY + this.margin[0] : this.margin[0]); - var firstPageHeight = this.pdf.internal.pageSize.height - this.posY - this.margin[0] - this.margin[2]; - var pageHeightMinusMargin = this.pdf.internal.pageSize.height - this.margin[0] - this.margin[2]; - var pageWidthMinusMargin = this.pdf.internal.pageSize.width - this.margin[1]; - var previousPageHeightSum = i === 1 ? 0 : firstPageHeight + (i - 2) * pageHeightMinusMargin; + var topMargin = i === 1 ? this.posY + this.margin[0] : this.margin[0]; + var firstPageHeight = + this.pdf.internal.pageSize.height - + this.posY - + this.margin[0] - + this.margin[2]; + var pageHeightMinusBottomMargin = + this.pdf.internal.pageSize.height - this.margin[2]; + var pageHeightMinusMargins = + pageHeightMinusBottomMargin - this.margin[0]; + var pageWidthMinusRightMargin = + this.pdf.internal.pageSize.width - this.margin[1]; + var previousPageHeightSum = + i === 1 ? 0 : firstPageHeight + (i - 2) * pageHeightMinusMargins; if (this.ctx.clip_path.length !== 0) { var tmpPaths = this.path; @@ -2057,11 +2150,10 @@ import { console } from "../libs/console.js"; drawPaths.call(this, "fill", true); this.path = tmpPaths; } - var tmpRect = JSON.parse(JSON.stringify(textRect)); - tmpRect = pathPositionRedo( - [tmpRect], + var textBoundsOnPage = pathPositionRedo( + [JSON.parse(JSON.stringify(textBounds))], this.posX + this.margin[3], - -1 * previousPageHeightSum + topMargin + this.ctx.prevPageLastElemOffset + -previousPageHeightSum + topMargin + this.ctx.prevPageLastElemOffset )[0]; if (options.scale >= 0.01) { @@ -2071,22 +2163,45 @@ import { console } from "../libs/console.js"; this.lineWidth = oldLineWidth * options.scale; } - if (tmpRect.y <= pageHeightMinusMargin) { - if (tmpRect.y - tmpRect.h >= 0 && tmpRect.x <= pageWidthMinusMargin) { - var croppedText = this.pdf.splitTextToSize(options.text, options.maxWidth || pageWidthMinusMargin - tmpRect.x)[0]; - this.pdf.text(croppedText, tmpRect.x, tmpRect.y, { - angle: options.angle, - align: textAlign, - renderingMode: options.renderingMode, - renderMaxWidthOverflow: false - }); + if ( + textBoundsOnPage.y + textBoundsOnPage.h <= + pageHeightMinusBottomMargin + ) { + if ( + textBoundsOnPage.y >= topMargin && + textBoundsOnPage.x <= pageWidthMinusRightMargin + ) { + var croppedText = this.pdf.splitTextToSize( + options.text, + options.maxWidth || pageWidthMinusRightMargin - textBoundsOnPage.x + )[0]; + var baseLineRectOnPage = pathPositionRedo( + [JSON.parse(JSON.stringify(baselineRect))], + this.posX + this.margin[3], + -previousPageHeightSum + + topMargin + + this.ctx.prevPageLastElemOffset + )[0]; + this.pdf.text( + croppedText, + baseLineRectOnPage.x, + baseLineRectOnPage.y, + { + angle: options.angle, + align: textAlign, + renderingMode: options.renderingMode + } + ); } } else { // This text is the last element of the page, but it got cut off due to the margin // so we render it in the next page - // As a result, all other elements have their y offset increased - this.ctx.prevPageLastElemOffset += pageHeightMinusMargin - tmpRect.y + tmpRect.h; + if (textBoundsOnPage.y < pageHeightMinusBottomMargin) { + // As a result, all other elements have their y offset increased + this.ctx.prevPageLastElemOffset += + pageHeightMinusBottomMargin - textBoundsOnPage.y; + } } if (options.scale >= 0.01) { @@ -2101,7 +2216,7 @@ import { console } from "../libs/console.js"; oldLineWidth = this.lineWidth; this.lineWidth = oldLineWidth * options.scale; } - this.pdf.text(options.text, pt.x + this.posX + this.margin[3], pt.y + this.posY, { + this.pdf.text(options.text, pt.x + this.posX, pt.y + this.posY, { angle: options.angle, align: textAlign, renderingMode: options.renderingMode, diff --git a/src/modules/html.js b/src/modules/html.js index 793f1d528..1d8ccce4e 100644 --- a/src/modules/html.js +++ b/src/modules/html.js @@ -970,7 +970,7 @@ import { globalObject } from "../libs/globalObject.js"; * @param {HTMLElement|string} source The source HTMLElement or a string containing HTML. * @param {Object} [options] Collection of settings * @param {function} [options.callback] The mandatory callback-function gets as first parameter the current jsPDF instance - * @param {number|array} [options.margin] Array of margins [left, bottom, right, top] + * @param {number|number[]} [options.margin] Page margins [top, right, bottom, left]. * @param {string} [options.filename] name of the file * @param {HTMLOptionImage} [options.image] image settings when converting HTML to image * @param {Html2CanvasOptions} [options.html2canvas] html2canvas options diff --git a/test/reference/html-margin-page-break-image.pdf b/test/reference/html-margin-page-break-image.pdf new file mode 100644 index 0000000000000000000000000000000000000000..4239fc56dd573d79c0d3fd6d32ffa314875e7ceb GIT binary patch literal 5261 zcmcIo?{eBk5YOj(iv8kgG94`4NeJk8JQG8(X_K)Fo^hw1d=TP{Ph~>1}zdzmXYVWMqZ}5GG?S1|B?|;kz8Sau1g_E^yo4SBKBc>LziHS7zWU&cj0{;|VFC z|5_2VKFWBSUxONSrt9jybs4!LN(o zMjH>QWEU)6tVauKrlWE((4bPJL6Wls)sieR)X8VT%4nrb7)dHAWKdzi zf-Er@as^l~7qvM+>!3w8{vfhii-f;2vf347(f=y4I&1bOLek^ldHn3=$WfF;6Av0% ziqx5Py&}g;s&u+eJ_}ezC!xtmB9aWz+Lu)6v>{hu^-@8bL$d}{WK|VYk4K*3OS`PP zZA_UMVaZXhMz|vya>mx|a8v~~JW4psnDtvY$>5SI=n!ZZ90DPB2&xRuv3xqE-K(<@ z!iEV~Aopis@cRCX?R(?W|D74JeRurRzrE^V4H)}5)}HFd z_@pu*w(owUhiIJ4epG&aPy6F^d?N#_a|?q9k(w|s!U>LmSldoyIK9SElV-B}Z8JQL1ySOZ8fAc^tGY4+2i7nHot;N<1{`Mz zYxK#_{sN)L(_#`;h1!fY-`~8c4>^i|wQju3v*Xl%c#>;pDbCtKo}|EqawSR8lP5T8 zhHPFMkiM^RtW*!r(j*wk94;+`7p$S~|OUUa1H%aKXfUEuqSKGk#+-LPWIO~|pP%76=J8I*=3wN>*4`JD*!(<-F6yDxs6bXAD-|rBg_?#jS zlep*S65jTH;hlDi3+x@$>u1~k2|w@3QtKtA>*F?-Oz8>B6rZOUgHLZ|{5~MrML&ss zU48=bd^W>S6Yod=A(>-7PGhO@T+(x&$}4;xB3jJYf`d8a0iJ7`6fuR|bNy;j%`BA~Lyy_yc;*!62& zx28ihJ%n6Eo>%unvu%f16@~xvDdtbm1cfE;xpAg20>pOQlQxA@+rTLroFB4H`^d1R97%4IK2x8d zFVj9s_mHwQdu+#-vxK7%4K*HMVr)!+=QL z+gvWV|J(hI^Xjkt=5*V%b6gGgqav@4&npT8tBybNj;a2KccAAy=2+&UHSQm*CcQ zR_F`&uG2zaxOp83ec|r4QRoY|uWf|BaQ|AD>#O1ZwIAsBB3wbzcH8B z!tLAS>RPyaeJ-wro7d*rTD*62Y3*~Y+hK?AGM1x6c$Nrf$$H$1^=tqv`2sAZ_8qX~ z zYp5CBt*-nQURj%<^X0=UYvXOce0XJTlIZK>1Fx)&&h>Tp(n`6$4qsbiuCId^SKs9N zI(T*UF4xyRr@TIi7keZ0h5OfQp)cIOz83nz{p*6z7w%u@g}!kAI-Be3;r?}e{a)7} z5BIO5Twf3OuZ_9B9`0Y;AstA)OB|JqvU3-_-HLSMLl4L?C# z`>TQb*SK8Y2JT;Da(x@Pe+}jOHgNyynCsiX{i_?HFWkS{3w`1K)mG>W_pjDMU$}pD zE%b%^mqM;@fcux4T;BlqFS%Ub0QWDMT;BlqFHx>+qNfNitr9&&cxjdBDZ)#uL{AZ3S|xgl@Y>o4^c3;MRiLK` zudWh3MR<9Y=qWy@zPV5Ih^9t=^4v^5uJ)FV6*``#dmb+3w!Ht+;tsy_!ODnj87TO89?WkeCoEf z1X6}W0-Q{{uO;VvsP(Jq6d8Dtm3WE_yvRyEMFw7EC7>b$FS3$Qk%1RkiKu7;$y33o zXam_(0jUU|K9!V;@cC1Tspwe=)R%B7e8#chRQQl%!Kp0V$pxpfa3>d>%EFyoa4LMz zvEWqrq+`LU@KMKtQ{l6Y1*gJ?9Sct7;Me4WQ}O)>_^!U-R1WUsf>SxTlM7De;7%?$ z6+ZGU|8 zlM7CT4?h;13ZH%~I2AtrSa2$Q{;}Xx_yDBjR1Mt7l2bKsA4^Wvz-=r!6@U2)T_yyl zYMx`_7jY_nc6Xa>+61WT2a?J_Rq7}}m8MYtW}wRQQ=0*QhU~w?ciZaGl;6bIf}M1+u~iBnxDP zPe>NX3LlXykQF{7Ss<$zA2jlF$2qw7HfdfB|2#gMtIuh8%|n&OLO4_@mwu?y^S?vY z-CZ53Zm`T79%!=*j4ww{;LOqbR;VA8bDu}ltcE+Id7Gxx@Q3#5AMIj%I;w_8Hz$j3 z;LbEBRm1m_)9KlGa-oJljwkOX^YLv54@x;EQKG4d{Bv}9EiBk1BbMp zw*Jl31&(k%ZS@}(qpR`h2KN8)+SniewEr91h{vnr-$!50_xKucGFtGJs4R?6x(sj@ z?6G5g^zHg&diB!fXN&(do1PxE3-x)(V^gc)ar>1oaUZUEAnKjv@7?9^4}8Jwu7KOy zJVC0vRPq4T`&x03mM^S4X%t6Z`$?MyVeTiL;uvW^=@kb)`^iReG_#)!s&jc-LR6j6 z(j@0+x|6<}Ce@vR-LzJm_v_!u8^O`M<+N4Zk?S%z!@nJzT(+kR#aqjyV+}hv2e&@B zy!U4}r|nF=zMQB1uLu0^4UeMD17a`6)5+dw(W=*bf1nr|&SBR5>-Tm2JN|i-FZFJz z-W{k>iaYY!FX%ZOHFS33^6s7ecVc7rPpdc?n;MN z_F3NbJAFLHci9&$CnJjYM#m3vWgq7!IKE%@nRH&H*J@iRME+!7^u?NQ;bouZ#0Os^ z(rfizO+2*uoj%VG5X$)if8(X>iyVR}`k3Q$0Zgy;IyCVouCmYEIez?%fuFCH^Ev*+ zT=q40^w2tRR->FRGC?i+OwClX?6dcr2QGOvz0!UxW~1>H-?=15AC3Rjrt@leIGrw3 xdS`O z7Z!ZyA}tApyCcgUi6UF)Q&A9BW&9*&Nqn9?kvj)6{K)UKkc1xXhtU_y9%N|=PZz{f zrO(bRk*;gW+mtTqJ1(5|MiZU1R(sKHY{BAkqljS;n61vhN@m*!V7c!O1 zg>`GuKB+$WeCryX`UKDaATy<*hqj?*JF!rS9yrjalH21A1_7uopJln*Xrm`v$PPUDA(pgL^Z@kJOh-cNjf*( zk|5GJi$zlA#zPf^4bWm{-2FfU4Rek0Q4n|<=QK;#%V*>MGmWuG@?~Z|K#s<@6w8(Q zf$T>b-z0rZW@}gw49`VA*C4OW8}~KDi+Gad!npTt1F?DUsfJfg)8@T*G`!1rp2nu5 z4ig%`w{RO4_V$~C??T!{J_Uo0*})$6l*?=#PZDAN$*a3Sh6 z5F6)0p+*;n3I!Tm6ei^Bg&2znxf8_Es+U_8nz@R6mHm(n6SuUOJ{m7JA4yiX$egTGh9nz!kFP&3X>)*FN)j;_9Qzr zT5g|qHdhPF{xO*q@cCbyOBpOf@MS|pnc*n&**q8xmUbK)}Ch>hz6^Zi!k z;oB?koo9=gc@Ls@cyK+D5;JUEn+^k&8{C_>lWK?jMG#aR@H}aH(@Wi90K4>w$PuC- z_x)dwLR~jfU!cu7g#(T6;Q`P-zOcV5w1a@>NAq_6QY^;FQWU*Wwv1DR&d`2g-yesn zew=B69-bDf>Mu)JQKty)KRh)y?MHLs@a*_&yt==D6<`vVu;%PejBi>5*x}jl_IWwU z7XQ_LYg)O)%2BdkL$hTK`T@;ery`A>fqKkP(TD zymE;QkG%4Tn!)9zPvD$Sl^YORpJW^sD&AF;dwZ)Q#}U;RQHpm z!2;dX2PWvIMzEp6Hj;~}{mb>_K}<^mu0pa|+gW=yho;ZgQ<0P7he9^q>BIjC9HfFX zQDu^)7jY@b@x}Kv3OQxess6rooNwTB(ze>2rw1_%gKRF3bj{%aR~>=tl}JBKrR@z5 z0aj3cCh2;yfauDySMhTOuN(GtDh9BBxJ+C1JmumZ4p%YPs(pbU68LaXi`lNZ5;+isrYPzd#QIfpVpVmr@+H|b6u_dDe{97o$N$GL=L+a3@7ZeA3^LbF{i zgot5!H2J1YBN_J94U8_sZAVTZ(*20yph*}wmN|G_LUA)4H>PRF=@ypv#Dc?-#K zjAfVt$6rUx7|A4B#Z#Gp$QU2w%=a#%2e>hj!A7Z*&1GcrK; znIUGpm*FD)26n5-P#{TT=`ERgF5quw!W+g>o4Vq~7L8Wkpujs~lH@JBXBINs__6WR zXyFh`_KAf#f0E@U@GB{f$aMFDloUT~~Cv6A&@wfli{oxf(j!s2pkR)cAD*Rj}^Ee-ZO>L9% zs5KF}$7VrtCo(Yrr~t2>%abtwt0oT$V!_SBCzMA{)toGqPfnv;_r_E$vJ9${oW*d* zmP=(l16u*r2{ZomX9?U;OE3c3mPbJNAAzdtE9?i3w{i(-{@oJK)_JW!W&WzXLG@AM00*p_7_$o~NHG>&_KTgu_H!lWDV5{Ki8{s&# z8nkgveaCveWR1&{)*F{mFgt+@n<&->pfQ{T{{0FYtD^^kIjV2K+n@wj}czq|T-fb~*Uz)lAV4g?51MfU|Bk~L`o z*Jta#4P2k9`wnnbd{MRuJUc-feHXYY#>i~}&rUT)zm1)|R)$)6)=#1}4t#A7^(rio zZB#7Q7E!Yb+=vRQ@p(A8mD3c^S-jnpSW(>1aq_d(RL1b)HlgX8=lFh!$9+7yr+yUn zyi~%A-XFZ(a=F0nL%n`C&EN2PS=3tHskV%F7+;co?>$B|iF7euC#b?X3Fmu~-7M}JhJkdrdmgH-RejA?WgeE>s`zYkt0HF$ z!t2^@8hd_#jRq~ivHy=G92lc0Ncy!- Oplu14ot?cN_J0D{pWN;M literal 4298 zcmcIn?{3;i5dXeUF<+unm+5%?GJ9b6m8u)DSbuS zjJreu_(M0~^U&670-mq75|_2I{=qTNlUaE14}wSmf&wh|16W!>rn7@gqIZ$$+Ix{% zgp_mjlFCt5yxK$5=RHO}0gt7KJc=ciBk~k8DBRmsdl7jYouz=6 zGotR{x{*XZeQOU0Js#Qv39A8bO0A8=n=<{kV3EQ#rR53G5tx9=s2j}2g78?J4) zD~`tyvo6yw{8Z8i^ba!pOJ>2`XZmzA%T#iw6wjIdE}F-SAXwohTFa;)QzaWII4nv{bJ6(C_A`TZXZ$bA77K~?M@O^_t?WNtosrZ#yBd?y120{6u zJxjJ|dJGPItl)u6TJQqh2@ZRq(A^Ri`1THSWb>W&0#Lo$?1w_t3GLajt(uSd;wo6n zv@?mIFE#~S+nI8u87WRKUHE#Fcni0pdhwy?}lt@36p)*4V0C6#xC=e}vUi)xTa3 z2OJ0x*ue}1P-JLi0=b7q7N`tPLmRLO$hi*iqRSb&pmGaM@POytj6UoFSA`k5xSr=% zNgQ)f@T1u%ftUJF1TQtB4CS^7RdW5ycy=S_8K9|1Zfm=!W^>&7Vl$Tsyt+wg^M;1s z*VtrX!;}SaH1IPCuLf^;*K@hxM*jKLFn+<`>#Eddp6>g24B{2Fdn;`23J;8LW%PDV zvdeo2-U_l05N*N`y-RaH`S+`tfVkmZaj?s|EU9-va90XH+tuTy+ zeS(XI4k2uW!_Hc>X`iq~!!N?I8@33yxnI#GG}m>QhK0p(hsUjk&8F3D_|LT6%6_xN z4??^?(6O2Z|453jK8a(zl@_OE6fI*=(8Uul8o)L5l;#7E^HZjLC2XacpJWPICWe}w JoxK|m{|Cj~P{;rP diff --git a/test/reference/html-margin-x-y-text.pdf b/test/reference/html-margin-x-y-text.pdf new file mode 100644 index 0000000000000000000000000000000000000000..5164f48d273cd85fdf621334ba97ee7db3a3761e GIT binary patch literal 3404 zcmcIm?{4Bo5dXeUF<+u4s$zTB7~2(!bU?VHmlF|KYSkzo3@n_H|D^W1m)tYm6Wq&O zAEh&EQ~o$9qzZuW?09x&cILM;b9UPwTyn!^XWxH(`gB=moKavFmvDoZ$p40 zp3#Ef1`oZF(#PhmA$$nt?EEInWdauvtk`wX)VaM=AL}UnuJ5n)4iHKlR0s?HvSPlEc#ggr=h}LvtJgEY61Lk@tZs)(=7g<`nPY?ZJy00Qo)^xlNOkP ze8l!POmM8~*3Vx39AdB3-LKO@f&&2pr>%hi$_!C9PuKgO5Z|7N?O)9}4reJrveK8&e}Se;-X(a;5+s#pR~2 z^X*!W2cMTSnZx<2prcy`elKtw#L-SgS=tYkg!BF{-1l5AaHi9rpDpVr{9SBm70oK$ z3-JF7bG?`EWXJ$>Lrpgura#xcg0WN*T!ti%M2QrXMmGD)}WJmzwN zlc35G7ER*P~q`aBqEK{KIf?yt|iwra*Y0-x}=x8hd@ppBM$UR}EP+_h# OVuUALc6N3>8vFxBkDAB; literal 0 HcmV?d00001 diff --git a/test/specs/context2d.spec.js b/test/specs/context2d.spec.js index 8d7472553..6f71740fd 100644 --- a/test/specs/context2d.spec.js +++ b/test/specs/context2d.spec.js @@ -555,4 +555,20 @@ describe("Context2D: standard tests", () => { comparePdf(doc.output(), "autoPaging10Pages.pdf", "context2d"); }); + + it("margin property shorthands", () => { + const doc = new jsPDF(); + const ctx = doc.context2d; + expect(ctx.margin).toEqual([0, 0, 0, 0]); + ctx.margin = 1; + expect(ctx.margin).toEqual([1, 1, 1, 1]); + ctx.margin = [1]; + expect(ctx.margin).toEqual([1, 1, 1, 1]); + ctx.margin = [1, 2]; + expect(ctx.margin).toEqual([1, 2, 1, 2]); + ctx.margin = [1, 2, 3]; + expect(ctx.margin).toEqual([1, 2, 3, 2]); + ctx.margin = [1, 2, 3, 4]; + expect(ctx.margin).toEqual([1, 2, 3, 4]); + }); }); diff --git a/test/specs/html.spec.js b/test/specs/html.spec.js index 72c6b3081..5a38da81a 100644 --- a/test/specs/html.spec.js +++ b/test/specs/html.spec.js @@ -32,31 +32,49 @@ describe("Module: html", function() { }); it("html margin on page break", async () => { - const doc = jsPDF({ floatPrecision: 2, unit: "pt" }); - doc.rect( - 30, - 10, - doc.internal.pageSize.getWidth() - 60, - doc.internal.pageSize.getHeight() - 20 - ); + const doc = jsPDF({ floatPrecision: 2, unit: "pt", format: [100, 100] }); await new Promise(resolve => doc.html( - "
", + "
", { callback: resolve, margin: [10, 30, 10, 30] } ) ); - doc.rect( - 30, - 10, - doc.internal.pageSize.getWidth() - 60, - doc.internal.pageSize.getHeight() - 20 - ); + const numberOfPages = doc.getNumberOfPages(); + const pageWidth = doc.internal.pageSize.getWidth(); + const pageHeight = doc.internal.pageSize.getHeight(); + for (let i = 1; i <= numberOfPages; i++) { + doc.setPage(i); + doc.rect(30, 10, pageWidth - 60, pageHeight - 20); + } + doc.line(0, 50, 100, 50); comparePdf(doc.output(), "html-margin-page-break.pdf", "html"); }); + it("page break with image", async () => { + const doc = jsPDF({ floatPrecision: 2, unit: "pt", format: [100, 100] }); + await new Promise(resolve => + doc.html( + '', + { + callback: resolve, + margin: [10, 30, 10, 30] + } + ) + ); + const numberOfPages = doc.getNumberOfPages(); + const pageWidth = doc.internal.pageSize.getWidth(); + const pageHeight = doc.internal.pageSize.getHeight(); + for (let i = 1; i <= numberOfPages; i++) { + doc.setPage(i); + doc.rect(30, 10, pageWidth - 60, pageHeight - 20); + } + doc.line(0, 50, 100, 50); + comparePdf(doc.output(), "html-margin-page-break-image.pdf", "html"); + }); + it("html x, y offsets properly", async () => { const doc = jsPDF({ floatPrecision: 2, unit: "pt" }); doc.line(30, 10, 100, 10); @@ -92,29 +110,41 @@ describe("Module: html", function() { comparePdf(doc.output(), "html-margin-x-y.pdf", "html"); }); - it("page break with text", async () => { + it("html x, y + margin offsets properly", async () => { const doc = jsPDF({ floatPrecision: 2, unit: "pt" }); - doc.rect( - 30, - 10, - doc.internal.pageSize.getWidth() - 60, - doc.internal.pageSize.getHeight() - 20 - ); + doc.line(30, 10, 100, 10); + doc.line(30, 10, 30, 100); await new Promise(resolve => - doc.html( - "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.", - { - callback: resolve, - margin: [10, 30, 10, 30] - } - ) + doc.html("Lorem Ipsum", { + callback: resolve, + x: 10, + y: 3, + margin: [7, 20] + }) ); - doc.rect( - 30, - 10, - doc.internal.pageSize.getWidth() - 60, - doc.internal.pageSize.getHeight() - 20 + comparePdf(doc.output(), "html-margin-x-y-text.pdf", "html"); + }); + + it("page break with text", async () => { + const text = Array.from({ length: 200 }) + .map((_, i) => `ABC${i}`) + .join(" "); + + const doc = jsPDF({ floatPrecision: 2, unit: "pt" }); + await new Promise(resolve => + doc.html(`${text}`, { + callback: resolve, + margin: [10, 30, 10, 30] + }) ); + + const numberOfPages = doc.getNumberOfPages(); + const pageWidth = doc.internal.pageSize.getWidth(); + const pageHeight = doc.internal.pageSize.getHeight(); + for (let i = 1; i <= numberOfPages; i++) { + doc.setPage(i); + doc.rect(30, 10, pageWidth - 60, pageHeight - 20); + } comparePdf(doc.output(), "html-margin-page-break-text.pdf", "html"); }); }); diff --git a/types/index.d.ts b/types/index.d.ts index 69f5525ea..3fe596067 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -331,6 +331,7 @@ declare module "jspdf" { export interface Context2d { autoPaging: boolean; + margin: number[]; fillStyle: string | Gradient; filter: string; font: string; From 64d095914ff3b124c2ff9402b12105ec2eb02647 Mon Sep 17 00:00:00 2001 From: Lukas Hollaender Date: Mon, 19 Jul 2021 15:54:09 +0200 Subject: [PATCH 15/16] implement 'slice' and 'text' auto paging modes --- src/modules/context2d.js | 71 +++++++++++++++++++++++++++++++++------- src/modules/html.js | 22 +++++++++++-- test/specs/html.spec.js | 29 ++++++++++++++-- types/index.d.ts | 1 + 4 files changed, 107 insertions(+), 16 deletions(-) diff --git a/src/modules/context2d.js b/src/modules/context2d.js index eebff2141..f4560d55c 100644 --- a/src/modules/context2d.js +++ b/src/modules/context2d.js @@ -186,16 +186,34 @@ import { console } from "../libs/console.js"; var _autoPaging = false; /** - * @name autoPaging - * @type {boolean} - * @default true + * Gets or sets the auto paging mode. When auto paging is enabled, the context2d will automatically draw on the + * next page if a shape or text chunk doesn't fit entirely on the current page. The context2d will create new + * pages if required. + * + * Context2d supports different modes: + *
    + *
  • + * false: Auto paging is disabled. + *
  • + *
  • + * true or 'slice': Will cut shapes or text chunks across page breaks. Will possibly + * slice text in half, making it difficult to read. + *
  • + *
  • + * 'text': Trys not to cut text in half across page breaks. Works best for documents consisting + * mostly of a single column of text. + *
  • + *
+ * @name Context2D#autoPaging + * @type {boolean|"slice"|"text"} + * @default false */ Object.defineProperty(this, "autoPaging", { get: function() { return _autoPaging; }, set: function(value) { - _autoPaging = Boolean(value); + _autoPaging = value; } }); @@ -2136,6 +2154,7 @@ import { console } from "../libs/console.js"; pageHeightMinusBottomMargin - this.margin[0]; var pageWidthMinusRightMargin = this.pdf.internal.pageSize.width - this.margin[1]; + var pageWidthMinusMargins = pageWidthMinusRightMargin - this.margin[3]; var previousPageHeightSum = i === 1 ? 0 : firstPageHeight + (i - 2) * pageHeightMinusMargins; @@ -2163,18 +2182,24 @@ import { console } from "../libs/console.js"; this.lineWidth = oldLineWidth * options.scale; } + var doSlice = this.autoPaging !== "text"; + if ( - textBoundsOnPage.y + textBoundsOnPage.h <= - pageHeightMinusBottomMargin + doSlice || + textBoundsOnPage.y + textBoundsOnPage.h <= pageHeightMinusBottomMargin ) { if ( - textBoundsOnPage.y >= topMargin && - textBoundsOnPage.x <= pageWidthMinusRightMargin + doSlice || + (textBoundsOnPage.y >= topMargin && + textBoundsOnPage.x <= pageWidthMinusRightMargin) ) { - var croppedText = this.pdf.splitTextToSize( - options.text, - options.maxWidth || pageWidthMinusRightMargin - textBoundsOnPage.x - )[0]; + var croppedText = doSlice + ? options.text + : this.pdf.splitTextToSize( + options.text, + options.maxWidth || + pageWidthMinusRightMargin - textBoundsOnPage.x + )[0]; var baseLineRectOnPage = pathPositionRedo( [JSON.parse(JSON.stringify(baselineRect))], this.posX + this.margin[3], @@ -2182,6 +2207,24 @@ import { console } from "../libs/console.js"; topMargin + this.ctx.prevPageLastElemOffset )[0]; + + const needsClipping = + doSlice && (i > min || i < max) && hasMargins.call(this); + + if (needsClipping) { + this.pdf.saveGraphicsState(); + this.pdf + .rect( + this.margin[3], + this.margin[0], + pageWidthMinusMargins, + pageHeightMinusMargins, + null + ) + .clip() + .discardPath(); + } + this.pdf.text( croppedText, baseLineRectOnPage.x, @@ -2192,6 +2235,10 @@ import { console } from "../libs/console.js"; renderingMode: options.renderingMode } ); + + if (needsClipping) { + this.pdf.restoreGraphicsState(); + } } } else { // This text is the last element of the page, but it got cut off due to the margin diff --git a/src/modules/html.js b/src/modules/html.js index 1d8ccce4e..f1a62ed48 100644 --- a/src/modules/html.js +++ b/src/modules/html.js @@ -446,7 +446,10 @@ import { globalObject } from "../libs/globalObject.js"; ); delete options.onrendered; - pdf.context2d.autoPaging = true; + pdf.context2d.autoPaging = + typeof this.opt.autoPaging === "undefined" + ? true + : this.opt.autoPaging; pdf.context2d.posX = this.opt.x; pdf.context2d.posY = this.opt.y; pdf.context2d.margin = this.opt.margin; @@ -970,7 +973,22 @@ import { globalObject } from "../libs/globalObject.js"; * @param {HTMLElement|string} source The source HTMLElement or a string containing HTML. * @param {Object} [options] Collection of settings * @param {function} [options.callback] The mandatory callback-function gets as first parameter the current jsPDF instance - * @param {number|number[]} [options.margin] Page margins [top, right, bottom, left]. + * @param {(number|number[])=} [options.margin] Page margins [top, right, bottom, left]. Default is 0. + * @param {(boolean|'slice'|'text')=} [options.autoPaging] The auto paging mode. + *
    + *
  • + * false: Auto paging is disabled. + *
  • + *
  • + * true or 'slice': Will cut shapes or text chunks across page breaks. Will possibly + * slice text in half, making it difficult to read. + *
  • + *
  • + * 'text': Trys not to cut text in half across page breaks. Works best for documents consisting + * mostly of a single column of text. + *
  • + *
+ * Default is true. * @param {string} [options.filename] name of the file * @param {HTMLOptionImage} [options.image] image settings when converting HTML to image * @param {Html2CanvasOptions} [options.html2canvas] html2canvas options diff --git a/test/specs/html.spec.js b/test/specs/html.spec.js index 5a38da81a..32b88fa36 100644 --- a/test/specs/html.spec.js +++ b/test/specs/html.spec.js @@ -125,7 +125,7 @@ describe("Module: html", function() { comparePdf(doc.output(), "html-margin-x-y-text.pdf", "html"); }); - it("page break with text", async () => { + it("page break with autoPaging: 'text'", async () => { const text = Array.from({ length: 200 }) .map((_, i) => `ABC${i}`) .join(" "); @@ -134,7 +134,8 @@ describe("Module: html", function() { await new Promise(resolve => doc.html(`${text}`, { callback: resolve, - margin: [10, 30, 10, 30] + margin: [10, 30, 10, 30], + autoPaging: "text" }) ); @@ -147,4 +148,28 @@ describe("Module: html", function() { } comparePdf(doc.output(), "html-margin-page-break-text.pdf", "html"); }); + + it("page break with autoPaging: 'slice'", async () => { + const text = Array.from({ length: 200 }) + .map((_, i) => `ABC${i}`) + .join(" "); + + const doc = jsPDF({ floatPrecision: 2, unit: "pt" }); + await new Promise(resolve => + doc.html(`${text}`, { + callback: resolve, + margin: [10, 30, 10, 30], + autoPaging: "slice" + }) + ); + + const numberOfPages = doc.getNumberOfPages(); + const pageWidth = doc.internal.pageSize.getWidth(); + const pageHeight = doc.internal.pageSize.getHeight(); + for (let i = 1; i <= numberOfPages; i++) { + doc.setPage(i); + doc.rect(30, 10, pageWidth - 60, pageHeight - 20); + } + comparePdf(doc.output(), "html-margin-page-break-text-slice.pdf", "html"); + }); }); diff --git a/types/index.d.ts b/types/index.d.ts index 3fe596067..11c6c80e7 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -185,6 +185,7 @@ declare module "jspdf" { export interface HTMLOptions { callback?: (doc: jsPDF) => void; margin?: number | number[]; + autoPaging?: boolean | "slice" | "text"; filename?: string; image?: HTMLOptionImage; html2canvas?: Html2CanvasOptions; From f75ae8c6d9133b53ba00613e505c92ca115db903 Mon Sep 17 00:00:00 2001 From: Lukas Hollaender Date: Thu, 9 Sep 2021 12:29:27 +0200 Subject: [PATCH 16/16] update reference files - update reference files from width/windowWidth PR (#3203): there are no longer duplicate words - check in previously forgotten file --- .../html-margin-page-break-slice.pdf | Bin 0 -> 24749 bytes .../html-width-100-windowWidth-500.pdf | Bin 7050 -> 6262 bytes .../html-width-210-windowWidth-1000.pdf | Bin 7957 -> 6353 bytes .../html-width-210-windowWidth-250.pdf | Bin 6766 -> 6364 bytes .../html-width-210-windowWidth-500.pdf | Bin 7201 -> 6378 bytes ...html-width-300-windowWidth-500-scale-2.pdf | Bin 7163 -> 6343 bytes .../html-width-300-windowWidth-500.pdf | Bin 7085 -> 6282 bytes ...html-width-default-windowWidth-default.pdf | Bin 6742 -> 6666 bytes test/specs/html.spec.js | 2 +- 9 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 test/reference/html-margin-page-break-slice.pdf diff --git a/test/reference/html-margin-page-break-slice.pdf b/test/reference/html-margin-page-break-slice.pdf new file mode 100644 index 0000000000000000000000000000000000000000..8303e85dd1af2e1c3d95aa6c47c1d5be1ae846bc GIT binary patch literal 24749 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 Tvqf`9W3~p1+g`kQd-CqTF;9=R literal 0 HcmV?d00001 diff --git a/test/reference/html-width-100-windowWidth-500.pdf b/test/reference/html-width-100-windowWidth-500.pdf index 4fb42d34dcde9b8f867ee81a360834ea9b53d6d5..7d7b946906598c9a1ced149c79f425a345a3b736 100644 GIT binary patch delta 406 zcmZXQJqp5L42IFRD&5@FsUY3IpXB>FfDSGmLO&1`!4uTQ#YH725f9)Qyn?%{uKrLc zHkrbcH_4mq?Nb~Nu~rBty3@50XwwWdjOKKUX0(k1`D972WJTvzpwM1ihMh^br$LKM zd?QvMKm^Z zvHA;2m>3yaC>Vf%LY@K_m|Y{i{7`8Rj&jm&X68ndE953~nVOrMajB}h`nv%D_ity@ delta 788 zcmca;IMr@KKZ}W_iRHw}k6Cmr^voyMFv?7x%%U;5m@!)xB%lzI!ljW}P+Xd;ps5g& z#pN2pWu#|etWZ9A0*lV%n~b>-Z9YsglMi!fO?G0+;f8A~&YY~sEDSbo1yeRep(L|3 zSb&cis3NA*{HZ-TgRKyv?LM0#*tM6~N+ANi>~0JWEXQD|;0m4L1nuMq900)`@OUg1jbvI(r-{nE z{3Jj5&i NXu&C`qAYiZ{SUucQZ)bo delta 520 zcmca(_|9a)1ZHzni;0t;vgsO|=vf#}u49y%yopV3as^|SJVZz#B!x>Ov!J*%S3y%D zB#X;6gv&_J!dRhv@1sIP*$jaZ6(p1p^RJ$W!0~GYm}4O${es7qhIlv@}GQGchzY!w@qv!4xw#x5Q9q zibb!PF{V0mGYgR3dSpkLSOR%q_aQmdz!<|~pdFYVGQ#wTsfiJ$d8VeuNOl;4tur+< hHANDHh*?@L>k7-(jKVX=w1Ifjo+EHOP|YG90Eo~fakDZ&mT lXmA*rnIirSZhcPg!*h&Gk$s*D*>>e#ojlxs)+S4lJS&lES5tSx{V>tDvb6 zlEvj3!eyjqVXRO-`2nlWq315REQOg)#_@#hE48w5?;xlSL>>%uU6rN{l%l zYIrxZ0@zh`%;iv#-(V4)$?uuVpd#5UAP4A9PGHGbK$z`XmYSD|Y5WBa-N`3eilAB@ zS(PR;^6G9jW1TK2VPb4%sbByC3V8}#V1|L2p@rGxr(zcMCMHH^=#nO;riK_|W)_%Y z7KSDm>MSw!ni`m6s53P(Fb3+aM{$*@v9T$-fu<(r7#5qFT4MOf)Ev_zrWU4{=2==; oAnY)L28V&A0YVTeW@3!tYBQj-C;LiF<}x!gGT~BHb@g`x08r|aj{pDw diff --git a/test/reference/html-width-300-windowWidth-500-scale-2.pdf b/test/reference/html-width-300-windowWidth-500-scale-2.pdf index 54058571e6493acafb6fc081e7bf411e880b10c3..d32682fd1e81c5407a469679b0876b0deb3df534 100644 GIT binary patch delta 403 zcmexue%x?EKeMs1@x)0_Cm&|am|V@2G1-_oWAaPpw8`^WGA75e=1*2;%b)z1EpzgG z_Uz5c9Myv2#-@gb3I-sckf*=}W*C?n8%_QyX<2VMiD90pp|JtN4nt^Y h7#SNO1fgQ4<`}LvH8YzWD?Nn^C}qZ_s_N?R1^{1XSwsK; delta 647 zcmX?Z_}hF!KeLIU>BLD-*>o+8^eikU*D^{?Uc{|Axr8xW9wMX=lES5tSx{V>tDvb6 zlEvj3!Ua@mtWZ9AA-C4#8;todt-eeUtqx52vPfEsGfS{(T)~tJ(3AU4uIbRh? zV{(38acXi&YDsAkrUBZM%b3fMRVSuo7GxG@CTHfQV^jT(j47DQ=scH#7xaFJY;Hy=@C;4 tLrn85O$`v{8A3zDz|;sK2o*E7#BjBliN$11i78xWrWR&gs;aL3ZUDEKmxKTS diff --git a/test/reference/html-width-300-windowWidth-500.pdf b/test/reference/html-width-300-windowWidth-500.pdf index 3cdfc67a5d60ac23610ae62e410bf18d3c7e4414..249776e0bd9938b2ed36090cbbaf438b6a1d017f 100644 GIT binary patch delta 406 zcmZ2$-eowUpT*e7++^b9hm-d+rcEwpN}sIBoHqG3bL!+imbA&9thtl9*>WczXG@=4 z&z`y2o+D6D!q~*rK*0b66!H|fzzhRZBQx{KDA(It(|jEykF%*`;xEG$jY z)tMMz>NPPm#Z+fxVFJ`!kK!s56GJl$15HgaEH*K-!0?fY1*S(#ER8YDGc_=?MA%^j l4Gu$dLxdny%+wIW)uv_!hLZ)PCv%ya8<=vbs=E5S0RXsZSE2v_ delta 614 zcmeA&Tx&j|pT)$$+1gb6)>3SD8$m4~QO2ub16 zNXgI1FH+D{2+87d4dF7)1$zR6sS?1t2w%o06prn$1@!L;sVk(q47p*eXiOFm3UiB$btWNL=#5mR$xO!F+vjSzZ` mAi-g3X>NiLga(nZA%?5XObkpWzZIX%Wd?LVm#V6(zZ(GlhKMx) diff --git a/test/reference/html-width-default-windowWidth-default.pdf b/test/reference/html-width-default-windowWidth-default.pdf index 227caa0e04a158dfcc1bd3d63253a546ba10d8c4..575828fdebde0d10793eb54c8edc04f72d4d904f 100644 GIT binary patch delta 368 zcmca+(q*zCn}gZZ(tL6r$A`_UI5YUf%`Hq63_w63Pk{@}FfcVUu$-(ZkyvkGVva6n zY-w(UE@onAfGK8VY>J`I*xVRH%)}DI4ihsYGmyG^WJj5pn_6IKwy?x>ynzvhV@wTA yEYbCv8ku95XKG?-h@{RC92};mh8V6kwJ^mr(9*Ay0t|%rG!DvoM`}S3I%a(#RNH&cwjP0z=Hu98=8L&=5nNiHRkKn5j9Y z9p(l`Aa(V~jxw<@Ho?$rX^!c514|6Ym>L { doc.setPage(i); doc.rect(30, 10, pageWidth - 60, pageHeight - 20); } - comparePdf(doc.output(), "html-margin-page-break-text-slice.pdf", "html"); + comparePdf(doc.output(), "html-margin-page-break-slice.pdf", "html"); }); });