Skip to content

Commit 6fe9755

Browse files
committed
Add pageBreak features: css and avoidAll
1 parent 0f1e320 commit 6fe9755

File tree

1 file changed

+98
-7
lines changed

1 file changed

+98
-7
lines changed

src/plugin/pagebreaks.js

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

34
// Add page-break functionality.
45

@@ -7,17 +8,107 @@ var orig = {
78
toContainer: Worker.prototype.toContainer
89
};
910

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+
1019
Worker.prototype.toContainer = function toContainer() {
1120
return orig.toContainer.call(this).then(function toContainer_pagebreak() {
12-
// Find all page-break elements and setup page height.
13-
var pageBreaks = this.prop.container.querySelectorAll('.html2pdf__page-break');
21+
// Setup root element and inner page height.
22+
var root = this.prop.container;
1423
var pxPageHeight = this.prop.pageSize.inner.px.height;
1524

16-
// Set each page-break element to a block with the appropriate height.
17-
Array.prototype.forEach.call(pageBreaks, function pageBreak_loop(el) {
18-
el.style.display = 'block';
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+
// TODO: Only loop through all if css option is chosen?
51+
var els = root.querySelectorAll('*');
52+
Array.prototype.forEach.call(els, function pageBreak_loop(el) {
53+
// Setup pagebreak rules based on legacy and avoidAll modes.
54+
var rules = {
55+
before: false,
56+
after: mode.legacy && legacyEls.indexOf(el) !== -1,
57+
avoid: mode.avoidAll
58+
};
59+
60+
// Add rules for css mode.
61+
if (mode.css) {
62+
// TODO: Check if this is valid with iFrames.
63+
var style = window.getComputedStyle(el);
64+
// TODO: Handle 'left' and 'right' correctly.
65+
// TODO: Add support for 'avoid' on breakBefore/After.
66+
var cssOpt = ['always', 'left', 'right'];
67+
rules = {
68+
before: rules.before || cssOpt.indexOf(style.breakBefore || style.pageBreakBefore) !== -1,
69+
after: rules.after || cssOpt.indexOf(style.breakAfter || style.pageBreakAfter) !== -1,
70+
avoid: rules.avoid || (style.breakInside || style.pageBreakInside) === 'avoid'
71+
};
72+
}
73+
74+
// Add rules for explicit requests.
75+
Object.keys(rules).forEach(function(key) {
76+
rules[key] = rules[key] || select[key].indexOf(el) !== -1;
77+
});
78+
79+
// Get element position on the screen.
80+
// TODO: Subtract the top of the container from clientRect.top/bottom?
1981
var clientRect = el.getBoundingClientRect();
20-
el.style.height = pxPageHeight - (clientRect.top % pxPageHeight) + 'px';
21-
}, this);
82+
83+
// Avoid: Check if a break happens mid-element.
84+
if (rules.avoid && !rules.before) {
85+
var startPage = Math.floor(clientRect.top / pxPageHeight);
86+
var endPage = Math.floor(clientRect.bottom / pxPageHeight);
87+
var nPages = Math.abs(clientRect.bottom - clientRect.top) / pxPageHeight;
88+
89+
// Turn on rules.before if the el is broken and is less than a page long.
90+
if (endPage !== startPage && nPages < 1) {
91+
rules.before = true;
92+
}
93+
}
94+
95+
// Before: Create a padding div to push the element to the next page.
96+
if (rules.before) {
97+
var pad = createElement('div', {style: {
98+
display: 'block',
99+
height: pxPageHeight - (clientRect.top % pxPageHeight) + 'px'
100+
}});
101+
el.parentNode.insertBefore(pad, el);
102+
}
103+
104+
// After: Create a padding div to fill the remaining page.
105+
if (rules.after) {
106+
var pad = createElement('div', {style: {
107+
display: 'block',
108+
height: pxPageHeight - (clientRect.bottom % pxPageHeight) + 'px'
109+
}});
110+
el.parentNode.insertAfter(pad, el);
111+
}
112+
});
22113
});
23114
};

0 commit comments

Comments
 (0)