Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit d735b39

Browse files
Improvement - PDF - Add orientation and overflowTolerance config options #243
1 parent 2f5a2ba commit d735b39

File tree

4 files changed

+77
-50
lines changed

4 files changed

+77
-50
lines changed

‎src/pdf.js

Lines changed: 70 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,27 @@
11
import { domToPng } from "./dom-to-png";
22

3-
export default async function pdf({ domElement, fileName, scale = 2, options = {} }) {
3+
export default async function pdf({
4+
domElement,
5+
fileName,
6+
scale = 2,
7+
orientation = "auto", // 'auto' | 'portrait' | 'landscape'
8+
overflowTolerance = 0.2, // up to +n% height overflow gets squeezed onto 1 page
9+
}) {
410
if (!domElement) return Promise.reject("No domElement provided");
511

6-
const isSafari = typeof navigator !== 'undefined' &&
12+
const isSafari =
13+
typeof navigator !== "undefined" &&
714
/^((?!chrome|android).)*safari/i.test(navigator.userAgent);
815

916
let JsPDF;
10-
1117
try {
12-
JsPDF = (await import('jspdf')).default;
13-
} catch (e) {
14-
return Promise.reject('jspdf is not installed. Run npm install jspdf')
18+
JsPDF = (await import("jspdf")).default;
19+
} catch (_) {
20+
return Promise.reject("jspdf is not installed. Run npm install jspdf");
1521
}
1622

17-
const a4 = {
18-
width: 595.28,
19-
height: 841.89,
20-
};
23+
const A4_PORTRAIT = { width: 595.28, height: 841.89 };
24+
const A4_LANDSCAPE = { width: 841.89, height: 595.28 };
2125

2226
if (isSafari) {
2327
// Warming up in Safari, because it never works on the first try
@@ -32,54 +36,82 @@ export default async function pdf({ domElement, fileName, scale = 2, options = {
3236
}
3337

3438
const imgData = await domToPng({ container: domElement, scale });
39+
3540
return await new Promise((resolve, reject) => {
3641
const img = new window.Image();
3742
img.onload = function () {
43+
const EPS = 0.5; // small epsilon to avoid off-by-one paging due to rounding
44+
3845
const contentWidth = img.naturalWidth;
3946
const contentHeight = img.naturalHeight;
4047

41-
let imgWidth = a4.width;
42-
let imgHeight = (a4.width / contentWidth) * contentHeight;
43-
44-
const pdf = new JsPDF("", "pt", "a4");
45-
let position = 0;
46-
let leftHeight = contentHeight;
47-
const pageHeight = (contentWidth / a4.width) * a4.height;
48-
49-
if (leftHeight < pageHeight) {
50-
pdf.addImage(
51-
imgData,
52-
"PNG",
53-
0,
54-
0,
55-
imgWidth,
56-
imgHeight,
57-
"",
58-
"FAST"
59-
);
48+
const chosenOrientation =
49+
orientation === "auto"
50+
? contentHeight >= contentWidth
51+
? "p"
52+
: "l"
53+
: orientation;
54+
55+
const a4 =
56+
chosenOrientation === "l" ? A4_LANDSCAPE : A4_PORTRAIT;
57+
58+
const ratioToWidth = a4.width / contentWidth;
59+
const ratioToHeight = a4.height / contentHeight;
60+
const scaledHeightAtWidth = contentHeight * ratioToWidth;
61+
62+
let mode = "single"; // 'single' | 'multi'
63+
let ratio;
64+
65+
if (scaledHeightAtWidth <= a4.height + EPS) {
66+
ratio = ratioToWidth;
67+
} else if (scaledHeightAtWidth <= a4.height * (1 + overflowTolerance)) {
68+
ratio = Math.min(ratioToWidth, ratioToHeight);
69+
} else {
70+
mode = "multi";
71+
ratio = ratioToWidth;
72+
}
73+
74+
const imgWidth = contentWidth * ratio;
75+
const imgHeight = contentHeight * ratio;
76+
77+
const x = (a4.width - imgWidth) / 2;
78+
79+
const pdf = new JsPDF({
80+
orientation: chosenOrientation,
81+
unit: "pt",
82+
format: "a4"
83+
});
84+
85+
if (mode === "single") {
86+
const y = (a4.height - imgHeight) / 2;
87+
pdf.addImage(imgData, "PNG", x, y, imgWidth, imgHeight, "", "FAST");
6088
} else {
61-
while (leftHeight > 0) {
89+
const pageHeightInImagePx = a4.height / ratio;
90+
91+
let leftHeight = contentHeight;
92+
let positionY = 0;
93+
94+
while (leftHeight > EPS) {
6295
pdf.addImage(
6396
imgData,
6497
"PNG",
65-
0,
66-
position,
98+
x,
99+
positionY,
67100
imgWidth,
68101
imgHeight,
69102
"",
70103
"FAST"
71104
);
72-
leftHeight -= pageHeight;
73-
position -= a4.height;
74-
if (leftHeight > 0) {
75-
pdf.addPage();
76-
}
105+
leftHeight -= pageHeightInImagePx;
106+
positionY -= a4.height;
107+
if (leftHeight > EPS) pdf.addPage();
77108
}
78109
}
110+
79111
pdf.save(`${fileName}.pdf`);
80112
resolve();
81113
};
82-
img.onerror = err => reject("Failed to load image for PDF: " + err);
114+
img.onerror = (err) => reject("Failed to load image for PDF: " + err);
83115
img.src = imgData;
84116
});
85117
}

‎src/useConfig.js

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -207,12 +207,9 @@ export function useConfig() {
207207
},
208208
buttonTitles,
209209
print: {
210-
allowTaint: false,
211-
backgroundColor: COLOR_WHITE,
212-
useCORS: false,
213-
onclone: null,
214210
scale: 2,
215-
logging: false
211+
orientation: 'auto', // 'auto' | 'l' | 'p'
212+
overflowTolerance: 0.2,
216213
}
217214
}
218215
}

‎src/usePrinter.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ export function usePrinter({
2121
await pdf({
2222
domElement: document.getElementById(elementId),
2323
fileName,
24-
options
24+
orientation: options.orientation,
25+
overflowTolerance: options.overflowTolerance,
26+
scale: options.scale
2527
});
2628
} catch (error) {
2729
console.error("Error generating PDF:", error);

‎types/vue-data-ui.d.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -267,14 +267,10 @@ declare module "vue-data-ui" {
267267
table?: null | (() => void);
268268
tooltip?: null | (() => void);
269269
};
270-
// old html2canvas options
271270
print?: {
272-
allowTaint?: boolean;
273-
backgroundColor?: string;
274-
useCORS?: boolean;
275-
onclone?: null | ((doc: Document) => void);
276271
scale?: number;
277-
logging?: boolean;
272+
orientation?: 'auto' | 'l' | 'p';
273+
overflowTolerance?: number;
278274
};
279275
};
280276

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /