-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Expand file tree
/
Copy pathpagebreaks.js
More file actions
145 lines (122 loc) · 5.06 KB
/
pagebreaks.js
File metadata and controls
145 lines (122 loc) · 5.06 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
import Worker from '../worker.js';
import { createElement, loadImages } from '../utils.js';
/* Pagebreak plugin:
Adds page-break functionality to the html2pdf library. Page-breaks can be
enabled by CSS styles, set on individual elements using selectors, or
avoided from breaking inside all elements.
Options on the `opt.pagebreak` object:
mode: String or array of strings: 'avoid-all', 'css', and/or 'legacy'
Default: ['css', 'legacy']
before: String or array of CSS selectors for which to add page-breaks
before each element. Can be a specific element with an ID
('#myID'), all elements of a type (e.g. 'img'), all of a class
('.myClass'), or even '*' to match every element.
after: Like 'before', but adds a page-break immediately after the element.
avoid: Like 'before', but avoids page-breaks on these elements. You can
enable this feature on every element using the 'avoid-all' mode.
*/
// Refs to original functions.
var orig = {
toContainer: Worker.prototype.toContainer
};
// Add pagebreak default options to the Worker template.
Worker.template.opt.pagebreak = {
mode: ['css', 'legacy'],
before: [],
after: [],
avoid: []
};
Worker.prototype.toContainer = function toContainer() {
var prereqs = [
function waitLoadImages() {
var root = this.prop.container;
var images = root.querySelectorAll("img");
return loadImages(images);
}
];
return orig.toContainer.call(this)
.thenList(prereqs)
.then(function toContainer_pagebreak() {
// Setup root element and inner page height.
var root = this.prop.container;
var pxPageHeight = this.prop.pageSize.inner.px.height;
// Check all requested modes.
var modeSrc = [].concat(this.opt.pagebreak.mode);
var mode = {
avoidAll: modeSrc.indexOf('avoid-all') !== -1,
css: modeSrc.indexOf('css') !== -1,
legacy: modeSrc.indexOf('legacy') !== -1
};
// Get arrays of all explicitly requested elements.
var select = {};
var self = this;
['before', 'after', 'avoid'].forEach(function(key) {
var all = mode.avoidAll && key === 'avoid';
select[key] = all ? [] : [].concat(self.opt.pagebreak[key] || []);
if (select[key].length > 0) {
select[key] = Array.prototype.slice.call(
root.querySelectorAll(select[key].join(', ')));
}
});
// Get all legacy page-break elements.
var legacyEls = root.querySelectorAll('.html2pdf__page-break');
legacyEls = Array.prototype.slice.call(legacyEls);
// Loop through all elements.
var els = root.querySelectorAll('*');
Array.prototype.forEach.call(els, function pagebreak_loop(el) {
// Setup pagebreak rules based on legacy and avoidAll modes.
var rules = {
before: false,
after: mode.legacy && legacyEls.indexOf(el) !== -1,
avoid: mode.avoidAll
};
// Add rules for css mode.
if (mode.css) {
// TODO: Check if this is valid with iFrames.
var style = window.getComputedStyle(el);
// TODO: Handle 'left' and 'right' correctly.
// TODO: Add support for 'avoid' on breakBefore/After.
var breakOpt = ['always', 'page', 'left', 'right'];
var avoidOpt = ['avoid', 'avoid-page'];
rules = {
before: rules.before || breakOpt.indexOf(style.breakBefore || style.pageBreakBefore) !== -1,
after: rules.after || breakOpt.indexOf(style.breakAfter || style.pageBreakAfter) !== -1,
avoid: rules.avoid || avoidOpt.indexOf(style.breakInside || style.pageBreakInside) !== -1
};
}
// Add rules for explicit requests.
Object.keys(rules).forEach(function(key) {
rules[key] = rules[key] || select[key].indexOf(el) !== -1;
});
// Get element position on the screen.
// TODO: Subtract the top of the container from clientRect.top/bottom?
var clientRect = el.getBoundingClientRect();
// Avoid: Check if a break happens mid-element.
if (rules.avoid && !rules.before) {
var startPage = Math.floor(clientRect.top / pxPageHeight);
var endPage = Math.floor(clientRect.bottom / pxPageHeight);
var nPages = Math.abs(clientRect.bottom - clientRect.top) / pxPageHeight;
// Turn on rules.before if the el is broken and is at most one page long.
if (endPage !== startPage && nPages <= 1) {
rules.before = true;
}
}
// Before: Create a padding div to push the element to the next page.
if (rules.before) {
var pad = createElement('div', {style: {
display: 'block',
height: pxPageHeight - (clientRect.top % pxPageHeight) + 'px'
}});
el.parentNode.insertBefore(pad, el);
}
// After: Create a padding div to fill the remaining page.
if (rules.after) {
var pad = createElement('div', {style: {
display: 'block',
height: pxPageHeight - (clientRect.bottom % pxPageHeight) + 'px'
}});
el.parentNode.insertBefore(pad, el.nextSibling);
}
});
});
};