Skip to content

Commit

Permalink
Add margins functionality and autoPaging modes to html function (#2977)
Browse files Browse the repository at this point in the history
* 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]>
  • Loading branch information
3 people authored Sep 14, 2021
1 parent 574c6f0 commit 1daef93
Show file tree
Hide file tree
Showing 20 changed files with 455 additions and 53 deletions.
309 changes: 258 additions & 51 deletions src/modules/context2d.js

Large diffs are not rendered by default.

23 changes: 21 additions & 2 deletions src/modules/html.js
Original file line number Diff line number Diff line change
Expand Up @@ -462,9 +462,13 @@ 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;
pdf.context2d.fontFaces = fontFaces;

if (fontFaces) {
Expand Down Expand Up @@ -1019,7 +1023,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|array} [options.margin] Array of margins [left, bottom, right, top]
* @param {(number|number[])=} [options.margin] Page margins [top, right, bottom, left]. Default is 0.
* @param {(boolean|'slice'|'text')=} [options.autoPaging] The auto paging mode.
* <ul>
* <li>
* <code>false</code>: Auto paging is disabled.
* </li>
* <li>
* <code>true</code> or <code>'slice'</code>: Will cut shapes or text chunks across page breaks. Will possibly
* slice text in half, making it difficult to read.
* </li>
* <li>
* <code>'text'</code>: Trys not to cut text in half across page breaks. Works best for documents consisting
* mostly of a single column of text.
* </li>
* </ul>
* Default is <code>true</code>.
* @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
Expand Down
Binary file added test/reference/html-margin-page-break-image.pdf
Binary file not shown.
Binary file added test/reference/html-margin-page-break-slice.pdf
Binary file not shown.
Binary file added test/reference/html-margin-page-break-text.pdf
Binary file not shown.
Binary file added test/reference/html-margin-page-break.pdf
Binary file not shown.
Binary file added test/reference/html-margin-x-y-text.pdf
Binary file not shown.
Binary file added test/reference/html-margin-x-y.pdf
Binary file not shown.
Binary file added test/reference/html-margin.pdf
Binary file not shown.
Binary file modified test/reference/html-width-100-windowWidth-500.pdf
Binary file not shown.
Binary file modified test/reference/html-width-210-windowWidth-1000.pdf
Binary file not shown.
Binary file modified test/reference/html-width-210-windowWidth-250.pdf
Binary file not shown.
Binary file modified test/reference/html-width-210-windowWidth-500.pdf
Binary file not shown.
Binary file modified test/reference/html-width-300-windowWidth-500-scale-2.pdf
Binary file not shown.
Binary file modified test/reference/html-width-300-windowWidth-500.pdf
Binary file not shown.
Binary file modified test/reference/html-width-default-windowWidth-default.pdf
Binary file not shown.
Binary file added test/reference/html-x-y.pdf
Binary file not shown.
16 changes: 16 additions & 0 deletions test/specs/context2d.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -676,4 +676,20 @@ describe("Context2D: standard tests", () => {
"1. w"
]);
});

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]);
});
});
158 changes: 158 additions & 0 deletions test/specs/html.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -237,4 +237,162 @@ describe("Module: html", () => {

comparePdf(doc.output(), "html-font-faces.pdf", "html");
});

it("html margin insets 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(
"<div style='background: red; width: 10px; height: 10px;'></div>",
{
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", format: [100, 100] });
await new Promise(resolve =>
doc.html(
"<div style='background: red; width: 10px; height: 200px;'></div>",
{
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.pdf", "html");
});

it("page break with image", async () => {
const doc = jsPDF({ floatPrecision: 2, unit: "pt", format: [100, 100] });
await new Promise(resolve =>
doc.html(
'<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8DwHwAFBQIAX8jx0gAAAABJRU5ErkJggg==" width="10" height="200">',
{
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);
doc.line(30, 10, 30, 100);
await new Promise(resolve =>
doc.html(
"<div style='background: red; width: 10px; height: 10px;'></div>",
{
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(
"<div style='background: red; width: 10px; height: 10px;'></div>",
{
callback: resolve,
x: 10,
y: 3,
margin: [7, 20]
}
)
);
comparePdf(doc.output(), "html-margin-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("<span>Lorem Ipsum</span>", {
callback: resolve,
x: 10,
y: 3,
margin: [7, 20]
})
);
comparePdf(doc.output(), "html-margin-x-y-text.pdf", "html");
});

it("page break with autoPaging: '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(`<span>${text}</span>`, {
callback: resolve,
margin: [10, 30, 10, 30],
autoPaging: "text"
})
);

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");
});

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(`<span>${text}</span>`, {
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-slice.pdf", "html");
});
});
2 changes: 2 additions & 0 deletions types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ declare module "jspdf" {
export interface HTMLOptions {
callback?: (doc: jsPDF) => void;
margin?: number | number[];
autoPaging?: boolean | "slice" | "text";
filename?: string;
image?: HTMLOptionImage;
html2canvas?: Html2CanvasOptions;
Expand Down Expand Up @@ -372,6 +373,7 @@ declare module "jspdf" {

export interface Context2d {
autoPaging: boolean;
margin: number[];
fillStyle: string | Gradient;
filter: string;
font: string;
Expand Down

0 comments on commit 1daef93

Please sign in to comment.