Skip to content

Commit 02232e9

Browse files
Deep DaveDeep Dave
authored andcommitted
Merge branch 'feature/pagebreaks' of https://github.com/dave-deep/html2pdf
2 parents f1b0449 + a1eee56 commit 02232e9

File tree

3 files changed

+121
-21
lines changed

3 files changed

+121
-21
lines changed

src/plugin/pagebreaks.js

Lines changed: 101 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,113 @@
11
import Worker from '../worker.js';
2+
import { objType, createElement } from './utils.js';
23

4+
// Add page-break functionality.
5+
6+
// Refs to original functions.
37
var orig = {
48
toContainer: Worker.prototype.toContainer
59
};
610

11+
// Add pageBreak default options to the Worker template.
12+
Worker.template.opt.pageBreak = {
13+
mode: ['css', 'legacy'], // 'avoid-all', 'css', 'legacy', 'whiteline'
14+
before: [],
15+
after: [],
16+
avoid: []
17+
};
18+
719
Worker.prototype.toContainer = function toContainer() {
820
return orig.toContainer.call(this).then(function toContainer_pagebreak() {
9-
// Enable page-breaks.
10-
var pageBreaks = this.prop.container.querySelectorAll('.html2pdf__page-break');
21+
// Setup root element and inner page height.
22+
var root = this.prop.container;
1123
var pxPageHeight = this.prop.pageSize.inner.px.height;
12-
Array.prototype.forEach.call(pageBreaks, function pageBreak_loop(el) {
13-
el.style.display = 'block';
24+
25+
// Check all requested modes.
26+
var modeSrc = [].concat(this.opt.pageBreak.mode);
27+
var mode = {
28+
avoidAll: modeSrc.indexOf('avoid-all') !== -1,
29+
css: modeSrc.indexOf('css') !== -1,
30+
legacy: modeSrc.indexOf('legacy') !== -1,
31+
whiteline: modeSrc.indexOf('whiteline') !== -1
32+
};
33+
34+
// Get arrays of all explicitly requested elements.
35+
var select = {};
36+
['before', 'after', 'avoid'].forEach(function(key) {
37+
var all = mode.avoidAll && key === 'avoid';
38+
select[key] = all ? [] : [].concat(this.opt.pageBreak[key]);
39+
if (select[key].length > 0) {
40+
select[key] = Array.prototype.slice.call(
41+
root.querySelectorAll(select[key].join(', ')));
42+
}
43+
});
44+
45+
// Get all legacy page-break elements.
46+
var legacyEls = root.querySelectorAll('.html2pdf__page-break');
47+
legacyEls = Array.prototype.slice.call(legacyEls);
48+
49+
// Loop through all elements.
50+
var els = root.querySelectorAll('*');
51+
Array.prototype.forEach.call(els, function pageBreak_loop(el) {
52+
// Setup pagebreak rules based on legacy and avoidAll modes.
53+
var rules = {
54+
before: false,
55+
after: mode.legacy && legacyEls.indexOf(el) !== -1,
56+
avoid: mode.avoidAll
57+
};
58+
59+
// Add rules for css mode.
60+
if (mode.css) {
61+
// TODO: Check if this is valid with iFrames.
62+
var style = window.getComputedStyle(el);
63+
// TODO: Handle 'left' and 'right' correctly.
64+
// TODO: Add support for 'avoid' on breakBefore/After.
65+
var cssOpt = ['always', 'left', 'right'];
66+
rules = {
67+
before: rules.before || cssOpt.indexOf(style.breakBefore || style.pageBreakBefore) !== -1,
68+
after: rules.after || cssOpt.indexOf(style.breakAfter || style.pageBreakAfter) !== -1,
69+
avoid: rules.avoid || (style.breakInside || style.pageBreakInside) === 'avoid'
70+
};
71+
}
72+
73+
// Add rules for explicit requests.
74+
Object.keys(rules).forEach(function(key) {
75+
rules[key] = rules[key] || select[key].indexOf(el) !== -1;
76+
});
77+
78+
// Get element position on the screen.
79+
// TODO: Subtract the top of the container from clientRect.top/bottom?
1480
var clientRect = el.getBoundingClientRect();
15-
el.style.height = pxPageHeight - (clientRect.top % pxPageHeight) + 'px';
16-
}, this);
81+
82+
// Avoid: Check if a break happens mid-element.
83+
if (rules.avoid && !rules.before) {
84+
var startPage = Math.floor(clientRect.top / pxPageHeight);
85+
var endPage = Math.floor(clientRect.bottom / pxPageHeight);
86+
var nPages = Math.abs(clientRect.bottom - clientRect.top) / pxPageHeight;
87+
88+
// Turn on rules.before if the el is broken and is less than a page long.
89+
if (endPage !== startPage && nPages < 1) {
90+
rules.before = true;
91+
}
92+
}
93+
94+
// Before: Create a padding div to push the element to the next page.
95+
if (rules.before) {
96+
var pad = createElement('div', {style: {
97+
display: 'block',
98+
height: pxPageHeight - (clientRect.top % pxPageHeight) + 'px'
99+
}});
100+
el.parentNode.insertBefore(pad, el);
101+
}
102+
103+
// After: Create a padding div to fill the remaining page.
104+
if (rules.after) {
105+
var pad = createElement('div', {style: {
106+
display: 'block',
107+
height: pxPageHeight - (clientRect.bottom % pxPageHeight) + 'px'
108+
}});
109+
el.parentNode.insertAfter(pad, el);
110+
}
111+
});
17112
});
18113
};

src/utils.js

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// Determine the type of a variable/object.
2-
export const objType = function(obj) {
2+
export const objType = function objType(obj) {
33
var type = typeof obj;
44
if (type === 'undefined') return 'undefined';
55
else if (type === 'string' || obj instanceof String) return 'string';
@@ -12,7 +12,7 @@ export const objType = function(obj) {
1212
};
1313

1414
// Create an HTML element with optional className, innerHTML, and style.
15-
export const createElement = function(tagName, opt) {
15+
export const createElement = function createElement(tagName, opt) {
1616
var el = document.createElement(tagName);
1717
if (opt.className) el.className = opt.className;
1818
if (opt.innerHTML) {
@@ -29,7 +29,7 @@ export const createElement = function(tagName, opt) {
2929
};
3030

3131
// Deep-clone a node and preserve contents/properties.
32-
export const cloneNode = function(node, javascriptEnabled) {
32+
export const cloneNode = function cloneNode(node, javascriptEnabled) {
3333
// Recursively clone the node.
3434
var clone = node.nodeType === 3 ? document.createTextNode(node.nodeValue) : node.cloneNode(false);
3535
for (var child = node.firstChild; child; child = child.nextSibling) {
@@ -59,11 +59,20 @@ export const cloneNode = function(node, javascriptEnabled) {
5959
return clone;
6060
}
6161

62-
// Convert units using the conversion value 'k' from jsPDF.
63-
export const unitConvert = function(obj, k) {
64-
var newObj = {};
65-
for (var key in obj) {
66-
newObj[key] = obj[key] * 72 / 96 / k;
62+
// Convert units from px using the conversion value 'k' from jsPDF.
63+
export const unitConvert = function unitConvert(obj, k) {
64+
if (objType(obj) === 'number') {
65+
return obj * 72 / 96 / k;
66+
} else {
67+
var newObj = {};
68+
for (var key in obj) {
69+
newObj[key] = obj[key] * 72 / 96 / k;
70+
}
71+
return newObj;
6772
}
68-
return newObj;
6973
};
74+
75+
// Convert units to px using the conversion value 'k' from jsPDF.
76+
export const toPx = function toPx(val, k) {
77+
return Math.floor(val * k / 72 * 96);
78+
}

src/worker.js

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import jsPDF from 'jspdf';
22
import html2canvas from 'html2canvas';
3-
import { objType, createElement, cloneNode, unitConvert } from './utils.js';
3+
import { objType, createElement, cloneNode, toPx } from './utils.js';
44

55
/* ----- CONSTRUCTOR ----- */
66

@@ -330,7 +330,7 @@ Worker.prototype.get = function get(key, cbk) {
330330

331331
Worker.prototype.setMargin = function setMargin(margin) {
332332
return this.then(function setMargin_main() {
333-
// Parse the margin property.
333+
// Parse the margin property: [top, left, bottom, right].
334334
switch (objType(margin)) {
335335
case 'number':
336336
margin = [margin, margin, margin, margin];
@@ -351,10 +351,6 @@ Worker.prototype.setMargin = function setMargin(margin) {
351351
}
352352

353353
Worker.prototype.setPageSize = function setPageSize(pageSize) {
354-
function toPx(val, k) {
355-
return Math.floor(val * k / 72 * 96);
356-
}
357-
358354
return this.then(function setPageSize_main() {
359355
// Retrieve page-size based on jsPDF settings, if not explicitly provided.
360356
pageSize = pageSize || jsPDF.getPageSize(this.opt.jsPDF);

0 commit comments

Comments
 (0)