Skip to content

Commit 1daef93

Browse files
jeffsieuHackbrettXXXRui-Jesus
authored
Add margins functionality and autoPaging modes to html function (#2977)
* implement margins that were previously part of the API but had no effect * implement 'slice' and 'text' auto paging modes Co-authored-by: Lukas Hollaender <[email protected]> Co-authored-by: Rui-Jesus <[email protected]>
1 parent 574c6f0 commit 1daef93

20 files changed

+455
-53
lines changed

src/modules/context2d.js

+258-51
Large diffs are not rendered by default.

src/modules/html.js

+21-2
Original file line numberDiff line numberDiff line change
@@ -462,9 +462,13 @@ import { globalObject } from "../libs/globalObject.js";
462462
);
463463
delete options.onrendered;
464464

465-
pdf.context2d.autoPaging = true;
465+
pdf.context2d.autoPaging =
466+
typeof this.opt.autoPaging === "undefined"
467+
? true
468+
: this.opt.autoPaging;
466469
pdf.context2d.posX = this.opt.x;
467470
pdf.context2d.posY = this.opt.y;
471+
pdf.context2d.margin = this.opt.margin;
468472
pdf.context2d.fontFaces = fontFaces;
469473

470474
if (fontFaces) {
@@ -1019,7 +1023,22 @@ import { globalObject } from "../libs/globalObject.js";
10191023
* @param {HTMLElement|string} source The source HTMLElement or a string containing HTML.
10201024
* @param {Object} [options] Collection of settings
10211025
* @param {function} [options.callback] The mandatory callback-function gets as first parameter the current jsPDF instance
1022-
* @param {number|array} [options.margin] Array of margins [left, bottom, right, top]
1026+
* @param {(number|number[])=} [options.margin] Page margins [top, right, bottom, left]. Default is 0.
1027+
* @param {(boolean|'slice'|'text')=} [options.autoPaging] The auto paging mode.
1028+
* <ul>
1029+
* <li>
1030+
* <code>false</code>: Auto paging is disabled.
1031+
* </li>
1032+
* <li>
1033+
* <code>true</code> or <code>'slice'</code>: Will cut shapes or text chunks across page breaks. Will possibly
1034+
* slice text in half, making it difficult to read.
1035+
* </li>
1036+
* <li>
1037+
* <code>'text'</code>: Trys not to cut text in half across page breaks. Works best for documents consisting
1038+
* mostly of a single column of text.
1039+
* </li>
1040+
* </ul>
1041+
* Default is <code>true</code>.
10231042
* @param {string} [options.filename] name of the file
10241043
* @param {HTMLOptionImage} [options.image] image settings when converting HTML to image
10251044
* @param {Html2CanvasOptions} [options.html2canvas] html2canvas options
5.14 KB
Binary file not shown.
24.2 KB
Binary file not shown.
23.8 KB
Binary file not shown.
4.87 KB
Binary file not shown.
3.32 KB
Binary file not shown.

test/reference/html-margin-x-y.pdf

3.49 KB
Binary file not shown.

test/reference/html-margin.pdf

3.49 KB
Binary file not shown.
-788 Bytes
Binary file not shown.
Binary file not shown.
-402 Bytes
Binary file not shown.
-823 Bytes
Binary file not shown.
Binary file not shown.
-803 Bytes
Binary file not shown.
Binary file not shown.

test/reference/html-x-y.pdf

3.49 KB
Binary file not shown.

test/specs/context2d.spec.js

+16
Original file line numberDiff line numberDiff line change
@@ -676,4 +676,20 @@ describe("Context2D: standard tests", () => {
676676
"1. w"
677677
]);
678678
});
679+
680+
it("margin property shorthands", () => {
681+
const doc = new jsPDF();
682+
const ctx = doc.context2d;
683+
expect(ctx.margin).toEqual([0, 0, 0, 0]);
684+
ctx.margin = 1;
685+
expect(ctx.margin).toEqual([1, 1, 1, 1]);
686+
ctx.margin = [1];
687+
expect(ctx.margin).toEqual([1, 1, 1, 1]);
688+
ctx.margin = [1, 2];
689+
expect(ctx.margin).toEqual([1, 2, 1, 2]);
690+
ctx.margin = [1, 2, 3];
691+
expect(ctx.margin).toEqual([1, 2, 3, 2]);
692+
ctx.margin = [1, 2, 3, 4];
693+
expect(ctx.margin).toEqual([1, 2, 3, 4]);
694+
});
679695
});

test/specs/html.spec.js

+158
Original file line numberDiff line numberDiff line change
@@ -237,4 +237,162 @@ describe("Module: html", () => {
237237

238238
comparePdf(doc.output(), "html-font-faces.pdf", "html");
239239
});
240+
241+
it("html margin insets properly", async () => {
242+
const doc = jsPDF({ floatPrecision: 2, unit: "pt" });
243+
doc.line(30, 10, 100, 10);
244+
doc.line(30, 10, 30, 100);
245+
await new Promise(resolve =>
246+
doc.html(
247+
"<div style='background: red; width: 10px; height: 10px;'></div>",
248+
{
249+
callback: resolve,
250+
margin: [10, 30]
251+
}
252+
)
253+
);
254+
comparePdf(doc.output(), "html-margin.pdf", "html");
255+
});
256+
257+
it("html margin on page break", async () => {
258+
const doc = jsPDF({ floatPrecision: 2, unit: "pt", format: [100, 100] });
259+
await new Promise(resolve =>
260+
doc.html(
261+
"<div style='background: red; width: 10px; height: 200px;'></div>",
262+
{
263+
callback: resolve,
264+
margin: [10, 30, 10, 30]
265+
}
266+
)
267+
);
268+
const numberOfPages = doc.getNumberOfPages();
269+
const pageWidth = doc.internal.pageSize.getWidth();
270+
const pageHeight = doc.internal.pageSize.getHeight();
271+
for (let i = 1; i <= numberOfPages; i++) {
272+
doc.setPage(i);
273+
doc.rect(30, 10, pageWidth - 60, pageHeight - 20);
274+
}
275+
doc.line(0, 50, 100, 50);
276+
comparePdf(doc.output(), "html-margin-page-break.pdf", "html");
277+
});
278+
279+
it("page break with image", async () => {
280+
const doc = jsPDF({ floatPrecision: 2, unit: "pt", format: [100, 100] });
281+
await new Promise(resolve =>
282+
doc.html(
283+
'<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8DwHwAFBQIAX8jx0gAAAABJRU5ErkJggg==" width="10" height="200">',
284+
{
285+
callback: resolve,
286+
margin: [10, 30, 10, 30]
287+
}
288+
)
289+
);
290+
const numberOfPages = doc.getNumberOfPages();
291+
const pageWidth = doc.internal.pageSize.getWidth();
292+
const pageHeight = doc.internal.pageSize.getHeight();
293+
for (let i = 1; i <= numberOfPages; i++) {
294+
doc.setPage(i);
295+
doc.rect(30, 10, pageWidth - 60, pageHeight - 20);
296+
}
297+
doc.line(0, 50, 100, 50);
298+
comparePdf(doc.output(), "html-margin-page-break-image.pdf", "html");
299+
});
300+
301+
it("html x, y offsets properly", async () => {
302+
const doc = jsPDF({ floatPrecision: 2, unit: "pt" });
303+
doc.line(30, 10, 100, 10);
304+
doc.line(30, 10, 30, 100);
305+
await new Promise(resolve =>
306+
doc.html(
307+
"<div style='background: red; width: 10px; height: 10px;'></div>",
308+
{
309+
callback: resolve,
310+
x: 30,
311+
y: 10
312+
}
313+
)
314+
);
315+
comparePdf(doc.output(), "html-x-y.pdf", "html");
316+
});
317+
318+
it("html x, y + margin offsets properly", async () => {
319+
const doc = jsPDF({ floatPrecision: 2, unit: "pt" });
320+
doc.line(30, 10, 100, 10);
321+
doc.line(30, 10, 30, 100);
322+
await new Promise(resolve =>
323+
doc.html(
324+
"<div style='background: red; width: 10px; height: 10px;'></div>",
325+
{
326+
callback: resolve,
327+
x: 10,
328+
y: 3,
329+
margin: [7, 20]
330+
}
331+
)
332+
);
333+
comparePdf(doc.output(), "html-margin-x-y.pdf", "html");
334+
});
335+
336+
it("html x, y + margin offsets properly", async () => {
337+
const doc = jsPDF({ floatPrecision: 2, unit: "pt" });
338+
doc.line(30, 10, 100, 10);
339+
doc.line(30, 10, 30, 100);
340+
await new Promise(resolve =>
341+
doc.html("<span>Lorem Ipsum</span>", {
342+
callback: resolve,
343+
x: 10,
344+
y: 3,
345+
margin: [7, 20]
346+
})
347+
);
348+
comparePdf(doc.output(), "html-margin-x-y-text.pdf", "html");
349+
});
350+
351+
it("page break with autoPaging: 'text'", async () => {
352+
const text = Array.from({ length: 200 })
353+
.map((_, i) => `ABC${i}`)
354+
.join(" ");
355+
356+
const doc = jsPDF({ floatPrecision: 2, unit: "pt" });
357+
await new Promise(resolve =>
358+
doc.html(`<span>${text}</span>`, {
359+
callback: resolve,
360+
margin: [10, 30, 10, 30],
361+
autoPaging: "text"
362+
})
363+
);
364+
365+
const numberOfPages = doc.getNumberOfPages();
366+
const pageWidth = doc.internal.pageSize.getWidth();
367+
const pageHeight = doc.internal.pageSize.getHeight();
368+
for (let i = 1; i <= numberOfPages; i++) {
369+
doc.setPage(i);
370+
doc.rect(30, 10, pageWidth - 60, pageHeight - 20);
371+
}
372+
comparePdf(doc.output(), "html-margin-page-break-text.pdf", "html");
373+
});
374+
375+
it("page break with autoPaging: 'slice'", async () => {
376+
const text = Array.from({ length: 200 })
377+
.map((_, i) => `ABC${i}`)
378+
.join(" ");
379+
380+
const doc = jsPDF({ floatPrecision: 2, unit: "pt" });
381+
await new Promise(resolve =>
382+
doc.html(`<span>${text}</span>`, {
383+
callback: resolve,
384+
margin: [10, 30, 10, 30],
385+
autoPaging: "slice"
386+
})
387+
);
388+
389+
const numberOfPages = doc.getNumberOfPages();
390+
const pageWidth = doc.internal.pageSize.getWidth();
391+
const pageHeight = doc.internal.pageSize.getHeight();
392+
for (let i = 1; i <= numberOfPages; i++) {
393+
doc.setPage(i);
394+
doc.rect(30, 10, pageWidth - 60, pageHeight - 20);
395+
}
396+
comparePdf(doc.output(), "html-margin-page-break-slice.pdf", "html");
397+
});
240398
});

types/index.d.ts

+2
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ declare module "jspdf" {
223223
export interface HTMLOptions {
224224
callback?: (doc: jsPDF) => void;
225225
margin?: number | number[];
226+
autoPaging?: boolean | "slice" | "text";
226227
filename?: string;
227228
image?: HTMLOptionImage;
228229
html2canvas?: Html2CanvasOptions;
@@ -372,6 +373,7 @@ declare module "jspdf" {
372373

373374
export interface Context2d {
374375
autoPaging: boolean;
376+
margin: number[];
375377
fillStyle: string | Gradient;
376378
filter: string;
377379
font: string;

0 commit comments

Comments
 (0)