From 36041a97bc188172df61f3d8585bc2d9e2fe4c4c Mon Sep 17 00:00:00 2001 From: Erik Koopmans Date: Tue, 11 Sep 2018 22:30:50 +1000 Subject: [PATCH 01/20] Improve page-break commenting --- src/plugin/pagebreaks.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/plugin/pagebreaks.js b/src/plugin/pagebreaks.js index b6206887..35322531 100644 --- a/src/plugin/pagebreaks.js +++ b/src/plugin/pagebreaks.js @@ -1,14 +1,19 @@ import Worker from '../worker.js'; +// Add page-break functionality. + +// Refs to original functions. var orig = { toContainer: Worker.prototype.toContainer }; Worker.prototype.toContainer = function toContainer() { return orig.toContainer.call(this).then(function toContainer_pagebreak() { - // Enable page-breaks. + // Find all page-break elements and setup page height. var pageBreaks = this.prop.container.querySelectorAll('.html2pdf__page-break'); var pxPageHeight = this.prop.pageSize.inner.px.height; + + // Set each page-break element to a block with the appropriate height. Array.prototype.forEach.call(pageBreaks, function pageBreak_loop(el) { el.style.display = 'block'; var clientRect = el.getBoundingClientRect(); From bb5f0f98286453b41cb0ca4b5c69a5f5aed02ef3 Mon Sep 17 00:00:00 2001 From: Erik Koopmans Date: Tue, 11 Sep 2018 23:07:42 +1000 Subject: [PATCH 02/20] Add margin comment --- src/worker.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/worker.js b/src/worker.js index 657fd77f..a667389d 100644 --- a/src/worker.js +++ b/src/worker.js @@ -330,7 +330,7 @@ Worker.prototype.get = function get(key, cbk) { Worker.prototype.setMargin = function setMargin(margin) { return this.then(function setMargin_main() { - // Parse the margin property. + // Parse the margin property: [top, left, bottom, right]. switch (objType(margin)) { case 'number': margin = [margin, margin, margin, margin]; From 8ab06047ffd9493575cbbc82ca45b7cd11e0bf75 Mon Sep 17 00:00:00 2001 From: Erik Koopmans Date: Tue, 11 Sep 2018 23:21:05 +1000 Subject: [PATCH 03/20] Move toPx into utils and update unitConvert --- src/utils.js | 19 ++++++++++++++----- src/worker.js | 6 +----- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/utils.js b/src/utils.js index a0e94866..cdec3a43 100644 --- a/src/utils.js +++ b/src/utils.js @@ -59,11 +59,20 @@ export const cloneNode = function(node, javascriptEnabled) { return clone; } -// Convert units using the conversion value 'k' from jsPDF. +// Convert units from px using the conversion value 'k' from jsPDF. export const unitConvert = function(obj, k) { - var newObj = {}; - for (var key in obj) { - newObj[key] = obj[key] * 72 / 96 / k; + if (objType(obj) === 'number') { + return obj * 72 / 96 / k; + } else { + var newObj = {}; + for (var key in obj) { + newObj[key] = obj[key] * 72 / 96 / k; + } + return newObj; } - return newObj; }; + +// Convert units to px using the conversion value 'k' from jsPDF. +export const toPx = function toPx(val, k) { + return Math.floor(val * k / 72 * 96); +} diff --git a/src/worker.js b/src/worker.js index a667389d..cbcb6fb2 100644 --- a/src/worker.js +++ b/src/worker.js @@ -1,6 +1,6 @@ import jsPDF from 'jspdf'; import html2canvas from 'html2canvas'; -import { objType, createElement, cloneNode, unitConvert } from './utils.js'; +import { objType, createElement, cloneNode, toPx } from './utils.js'; /* ----- CONSTRUCTOR ----- */ @@ -351,10 +351,6 @@ Worker.prototype.setMargin = function setMargin(margin) { } Worker.prototype.setPageSize = function setPageSize(pageSize) { - function toPx(val, k) { - return Math.floor(val * k / 72 * 96); - } - return this.then(function setPageSize_main() { // Retrieve page-size based on jsPDF settings, if not explicitly provided. pageSize = pageSize || jsPDF.getPageSize(this.opt.jsPDF); From 0f1e320990520b2822ceadf6af9a4837f9ea211f Mon Sep 17 00:00:00 2001 From: Erik Koopmans Date: Tue, 11 Sep 2018 23:23:15 +1000 Subject: [PATCH 04/20] Add proper function names to util functions --- src/utils.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/utils.js b/src/utils.js index cdec3a43..ce0c4137 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,5 +1,5 @@ // Determine the type of a variable/object. -export const objType = function(obj) { +export const objType = function objType(obj) { var type = typeof obj; if (type === 'undefined') return 'undefined'; else if (type === 'string' || obj instanceof String) return 'string'; @@ -12,7 +12,7 @@ export const objType = function(obj) { }; // Create an HTML element with optional className, innerHTML, and style. -export const createElement = function(tagName, opt) { +export const createElement = function createElement(tagName, opt) { var el = document.createElement(tagName); if (opt.className) el.className = opt.className; if (opt.innerHTML) { @@ -29,7 +29,7 @@ export const createElement = function(tagName, opt) { }; // Deep-clone a node and preserve contents/properties. -export const cloneNode = function(node, javascriptEnabled) { +export const cloneNode = function cloneNode(node, javascriptEnabled) { // Recursively clone the node. var clone = node.nodeType === 3 ? document.createTextNode(node.nodeValue) : node.cloneNode(false); for (var child = node.firstChild; child; child = child.nextSibling) { @@ -60,7 +60,7 @@ export const cloneNode = function(node, javascriptEnabled) { } // Convert units from px using the conversion value 'k' from jsPDF. -export const unitConvert = function(obj, k) { +export const unitConvert = function unitConvert(obj, k) { if (objType(obj) === 'number') { return obj * 72 / 96 / k; } else { From 6fe97553dba23a5bc9ce217d2c0ed0ec9e697ffc Mon Sep 17 00:00:00 2001 From: Erik Koopmans Date: Sat, 15 Sep 2018 18:01:15 +1000 Subject: [PATCH 05/20] Add pageBreak features: css and avoidAll --- src/plugin/pagebreaks.js | 105 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 98 insertions(+), 7 deletions(-) diff --git a/src/plugin/pagebreaks.js b/src/plugin/pagebreaks.js index 35322531..d06113c0 100644 --- a/src/plugin/pagebreaks.js +++ b/src/plugin/pagebreaks.js @@ -1,4 +1,5 @@ import Worker from '../worker.js'; +import { objType, createElement } from './utils.js'; // Add page-break functionality. @@ -7,17 +8,107 @@ var orig = { toContainer: Worker.prototype.toContainer }; +// Add pageBreak default options to the Worker template. +Worker.template.opt.pageBreak = { + mode: ['css', 'legacy'], // 'avoid-all', 'css', 'legacy', 'whiteline' + before: [], + after: [], + avoid: [] +}; + Worker.prototype.toContainer = function toContainer() { return orig.toContainer.call(this).then(function toContainer_pagebreak() { - // Find all page-break elements and setup page height. - var pageBreaks = this.prop.container.querySelectorAll('.html2pdf__page-break'); + // Setup root element and inner page height. + var root = this.prop.container; var pxPageHeight = this.prop.pageSize.inner.px.height; - // Set each page-break element to a block with the appropriate height. - Array.prototype.forEach.call(pageBreaks, function pageBreak_loop(el) { - el.style.display = 'block'; + // 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, + whiteline: modeSrc.indexOf('whiteline') !== -1 + }; + + // Get arrays of all explicitly requested elements. + var select = {}; + ['before', 'after', 'avoid'].forEach(function(key) { + var all = mode.avoidAll && key === 'avoid'; + select[key] = all ? [] : [].concat(this.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. + // TODO: Only loop through all if css option is chosen? + 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 cssOpt = ['always', 'left', 'right']; + rules = { + before: rules.before || cssOpt.indexOf(style.breakBefore || style.pageBreakBefore) !== -1, + after: rules.after || cssOpt.indexOf(style.breakAfter || style.pageBreakAfter) !== -1, + avoid: rules.avoid || (style.breakInside || style.pageBreakInside) === 'avoid' + }; + } + + // 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(); - el.style.height = pxPageHeight - (clientRect.top % pxPageHeight) + 'px'; - }, this); + + // 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 less than a 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.insertAfter(pad, el); + } + }); }); }; From a1eee56c654775c5ce96328ee9e98af2f0a877e0 Mon Sep 17 00:00:00 2001 From: Erik Koopmans Date: Sun, 16 Sep 2018 00:41:30 +1000 Subject: [PATCH 06/20] Remove goal of avoiding the loop through all els --- src/plugin/pagebreaks.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/plugin/pagebreaks.js b/src/plugin/pagebreaks.js index d06113c0..05de8c54 100644 --- a/src/plugin/pagebreaks.js +++ b/src/plugin/pagebreaks.js @@ -47,7 +47,6 @@ Worker.prototype.toContainer = function toContainer() { legacyEls = Array.prototype.slice.call(legacyEls); // Loop through all elements. - // TODO: Only loop through all if css option is chosen? var els = root.querySelectorAll('*'); Array.prototype.forEach.call(els, function pageBreak_loop(el) { // Setup pagebreak rules based on legacy and avoidAll modes. From b1f5bf40dfaab6c1fd2f36ecccc94f981420de4e Mon Sep 17 00:00:00 2001 From: Deep Dave Date: Sun, 30 Sep 2018 11:33:53 +0530 Subject: [PATCH 07/20] Fixed issues with references and checks --- src/plugin/pagebreaks.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/plugin/pagebreaks.js b/src/plugin/pagebreaks.js index 05de8c54..5a2c8ecc 100644 --- a/src/plugin/pagebreaks.js +++ b/src/plugin/pagebreaks.js @@ -1,5 +1,5 @@ import Worker from '../worker.js'; -import { objType, createElement } from './utils.js'; +import { objType, createElement } from '../utils.js'; // Add page-break functionality. @@ -33,9 +33,10 @@ Worker.prototype.toContainer = function toContainer() { // 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(this.opt.pageBreak[key]); + select[key] = all ? [] : (undefined === self.opt.pageBreak[key] ? [] : [].concat(self.opt.pageBreak[key])); if (select[key].length > 0) { select[key] = Array.prototype.slice.call( root.querySelectorAll(select[key].join(', '))); From d4764072baecca035c36cb30166372b89d96fc9b Mon Sep 17 00:00:00 2001 From: Erik Koopmans Date: Thu, 4 Oct 2018 23:34:49 +1000 Subject: [PATCH 08/20] Minor refactor when handling undefined keys --- src/plugin/pagebreaks.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugin/pagebreaks.js b/src/plugin/pagebreaks.js index 5a2c8ecc..8f3913f8 100644 --- a/src/plugin/pagebreaks.js +++ b/src/plugin/pagebreaks.js @@ -36,7 +36,7 @@ Worker.prototype.toContainer = function toContainer() { var self = this; ['before', 'after', 'avoid'].forEach(function(key) { var all = mode.avoidAll && key === 'avoid'; - select[key] = all ? [] : (undefined === self.opt.pageBreak[key] ? [] : [].concat(self.opt.pageBreak[key])); + select[key] = all ? [] : [].concat(self.opt.pageBreak[key] || []); if (select[key].length > 0) { select[key] = Array.prototype.slice.call( root.querySelectorAll(select[key].join(', '))); From e26fdf4abcddf50c1572d9cc389d83fcb3822d85 Mon Sep 17 00:00:00 2001 From: Erik Koopmans Date: Thu, 4 Oct 2018 23:37:09 +1000 Subject: [PATCH 09/20] Fix insertAfter usage --- src/plugin/pagebreaks.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugin/pagebreaks.js b/src/plugin/pagebreaks.js index 8f3913f8..f604f7fe 100644 --- a/src/plugin/pagebreaks.js +++ b/src/plugin/pagebreaks.js @@ -107,7 +107,7 @@ Worker.prototype.toContainer = function toContainer() { display: 'block', height: pxPageHeight - (clientRect.bottom % pxPageHeight) + 'px' }}); - el.parentNode.insertAfter(pad, el); + el.parentNode.insertBefore(pad, el.nextSibling); } }); }); From a8d1354ad587725a596aa455cc5f945671607da0 Mon Sep 17 00:00:00 2001 From: Erik Koopmans Date: Fri, 5 Oct 2018 00:02:15 +1000 Subject: [PATCH 10/20] Avoid breaks on elements that are exactly 1 page --- src/plugin/pagebreaks.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugin/pagebreaks.js b/src/plugin/pagebreaks.js index f604f7fe..4ce9e0c4 100644 --- a/src/plugin/pagebreaks.js +++ b/src/plugin/pagebreaks.js @@ -86,8 +86,8 @@ Worker.prototype.toContainer = function toContainer() { 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 less than a page long. - if (endPage !== startPage && nPages < 1) { + // Turn on rules.before if the el is broken and is at most one page long. + if (endPage !== startPage && nPages <= 1) { rules.before = true; } } From 81b75a0e906a7a47b5e0edb1a2e4408e16f67d89 Mon Sep 17 00:00:00 2001 From: Erik Koopmans Date: Sun, 7 Oct 2018 16:18:04 +1100 Subject: [PATCH 11/20] Fix to support modern CSS options --- src/plugin/pagebreaks.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugin/pagebreaks.js b/src/plugin/pagebreaks.js index 4ce9e0c4..ff8d0df2 100644 --- a/src/plugin/pagebreaks.js +++ b/src/plugin/pagebreaks.js @@ -63,7 +63,7 @@ Worker.prototype.toContainer = function toContainer() { var style = window.getComputedStyle(el); // TODO: Handle 'left' and 'right' correctly. // TODO: Add support for 'avoid' on breakBefore/After. - var cssOpt = ['always', 'left', 'right']; + var cssOpt = ['always', 'page', 'left', 'right']; rules = { before: rules.before || cssOpt.indexOf(style.breakBefore || style.pageBreakBefore) !== -1, after: rules.after || cssOpt.indexOf(style.breakAfter || style.pageBreakAfter) !== -1, From 804becc1327598defa8c52083eb43467f8785ac0 Mon Sep 17 00:00:00 2001 From: Erik Koopmans Date: Sun, 7 Oct 2018 16:20:35 +1100 Subject: [PATCH 12/20] Support multiple CSS break-inside options --- src/plugin/pagebreaks.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/plugin/pagebreaks.js b/src/plugin/pagebreaks.js index ff8d0df2..7d960403 100644 --- a/src/plugin/pagebreaks.js +++ b/src/plugin/pagebreaks.js @@ -63,11 +63,12 @@ Worker.prototype.toContainer = function toContainer() { var style = window.getComputedStyle(el); // TODO: Handle 'left' and 'right' correctly. // TODO: Add support for 'avoid' on breakBefore/After. - var cssOpt = ['always', 'page', 'left', 'right']; + var breakOpt = ['always', 'page', 'left', 'right']; + var avoidOpt = ['avoid', 'avoid-page']; rules = { - before: rules.before || cssOpt.indexOf(style.breakBefore || style.pageBreakBefore) !== -1, - after: rules.after || cssOpt.indexOf(style.breakAfter || style.pageBreakAfter) !== -1, - avoid: rules.avoid || (style.breakInside || style.pageBreakInside) === 'avoid' + 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 }; } From 7886ac6409f51a2827a45b450a0fc41e0a1af74d Mon Sep 17 00:00:00 2001 From: Erik Koopmans Date: Sun, 7 Oct 2018 16:40:53 +1100 Subject: [PATCH 13/20] Remove whiteline pagebreak option --- src/plugin/pagebreaks.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/plugin/pagebreaks.js b/src/plugin/pagebreaks.js index 7d960403..394cc4c4 100644 --- a/src/plugin/pagebreaks.js +++ b/src/plugin/pagebreaks.js @@ -10,7 +10,7 @@ var orig = { // Add pageBreak default options to the Worker template. Worker.template.opt.pageBreak = { - mode: ['css', 'legacy'], // 'avoid-all', 'css', 'legacy', 'whiteline' + mode: ['css', 'legacy'], // 'avoid-all', 'css', 'legacy' before: [], after: [], avoid: [] @@ -27,8 +27,7 @@ Worker.prototype.toContainer = function toContainer() { var mode = { avoidAll: modeSrc.indexOf('avoid-all') !== -1, css: modeSrc.indexOf('css') !== -1, - legacy: modeSrc.indexOf('legacy') !== -1, - whiteline: modeSrc.indexOf('whiteline') !== -1 + legacy: modeSrc.indexOf('legacy') !== -1 }; // Get arrays of all explicitly requested elements. From 9ee2f69e288f6595b5eaf17ac8a422c3e87c74ca Mon Sep 17 00:00:00 2001 From: Erik Koopmans Date: Sun, 7 Oct 2018 16:41:40 +1100 Subject: [PATCH 14/20] Change case of pagebreak option (no camel-case) --- src/plugin/pagebreaks.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/plugin/pagebreaks.js b/src/plugin/pagebreaks.js index 394cc4c4..c365c3f3 100644 --- a/src/plugin/pagebreaks.js +++ b/src/plugin/pagebreaks.js @@ -8,8 +8,8 @@ var orig = { toContainer: Worker.prototype.toContainer }; -// Add pageBreak default options to the Worker template. -Worker.template.opt.pageBreak = { +// Add pagebreak default options to the Worker template. +Worker.template.opt.pagebreak = { mode: ['css', 'legacy'], // 'avoid-all', 'css', 'legacy' before: [], after: [], @@ -23,7 +23,7 @@ Worker.prototype.toContainer = function toContainer() { var pxPageHeight = this.prop.pageSize.inner.px.height; // Check all requested modes. - var modeSrc = [].concat(this.opt.pageBreak.mode); + var modeSrc = [].concat(this.opt.pagebreak.mode); var mode = { avoidAll: modeSrc.indexOf('avoid-all') !== -1, css: modeSrc.indexOf('css') !== -1, @@ -35,7 +35,7 @@ Worker.prototype.toContainer = function toContainer() { var self = this; ['before', 'after', 'avoid'].forEach(function(key) { var all = mode.avoidAll && key === 'avoid'; - select[key] = all ? [] : [].concat(self.opt.pageBreak[key] || []); + select[key] = all ? [] : [].concat(self.opt.pagebreak[key] || []); if (select[key].length > 0) { select[key] = Array.prototype.slice.call( root.querySelectorAll(select[key].join(', '))); @@ -48,7 +48,7 @@ Worker.prototype.toContainer = function toContainer() { // Loop through all elements. var els = root.querySelectorAll('*'); - Array.prototype.forEach.call(els, function pageBreak_loop(el) { + Array.prototype.forEach.call(els, function pagebreak_loop(el) { // Setup pagebreak rules based on legacy and avoidAll modes. var rules = { before: false, From 950c355aebb13e81338ca03bbda277c07e4d7dd3 Mon Sep 17 00:00:00 2001 From: Erik Koopmans Date: Sun, 7 Oct 2018 16:42:10 +1100 Subject: [PATCH 15/20] Add manual tests (template and pagebreaks) --- test/manual/pagebreaks.html | 99 +++++++++++++++++++++++++++++++++++++ test/manual/template.html | 44 +++++++++++++++++ 2 files changed, 143 insertions(+) create mode 100644 test/manual/pagebreaks.html create mode 100644 test/manual/template.html diff --git a/test/manual/pagebreaks.html b/test/manual/pagebreaks.html new file mode 100644 index 00000000..2257fed6 --- /dev/null +++ b/test/manual/pagebreaks.html @@ -0,0 +1,99 @@ + + + + html2pdf Test - Pagebreaks + + + + + + + + + + + + + +
+

First line

+

Break before

+

Break after

+

No effect (should be top of 3rd page, using css or specify).

+

Legacy (should create a break after).

+

No effect (should be top of 2nd page, using legacy).

+

Big element (should start on new page, using avoid-all/css/specify).

+

No effect (should start on next page *only* using avoid-all).

+

No effect (for spacing).

+

Full-page element (should start on new page using avoid-all/css/specify).

+

No effect (for spacing).

+

Even bigger element (should continue normally, because it's more than a page).

+
+ + + + + + + diff --git a/test/manual/template.html b/test/manual/template.html new file mode 100644 index 00000000..0f447b24 --- /dev/null +++ b/test/manual/template.html @@ -0,0 +1,44 @@ + + + + html2pdf Test - Template + + + + + + + + + + +
+ This is a test +
+ + + + + + + From 25f4d2f0c6895657ca3037087215fd0a42cbbf40 Mon Sep 17 00:00:00 2001 From: Erik Koopmans Date: Sun, 7 Oct 2018 18:05:18 +1100 Subject: [PATCH 16/20] Add advanced tests of avoid-all --- test/manual/pagebreaks.html | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/test/manual/pagebreaks.html b/test/manual/pagebreaks.html index 2257fed6..6db20cb9 100644 --- a/test/manual/pagebreaks.html +++ b/test/manual/pagebreaks.html @@ -39,6 +39,14 @@ background-color: aqua; border: 1px solid black; } + + /* Table styling */ + table { + border-collapse: collapse; + } + td { + border: 1px solid black; + } @@ -68,6 +76,22 @@

Full-page element (should start on new page using avoid-all/css/specify).

No effect (for spacing).

Even bigger element (should continue normally, because it's more than a page).

+ + +
+

No effect inside parent div (testing avoid-all - no break yet because parent is more than a page).

+

Big element inside parent div (testing avoid-all - should have break before this).

+
+ + + + + + + + + +
Cell 1-1 - start of new pageCell 1-2 - start of new page
Cell 2-1 - start of another new pageCell 2-2 - start of another new page
From 79b4549093eb59ea1c120e6664af4e9fcac48b1e Mon Sep 17 00:00:00 2001 From: Erik Koopmans Date: Sun, 7 Oct 2018 18:05:43 +1100 Subject: [PATCH 17/20] Add avoid-all to default pagebreak behaviour --- src/plugin/pagebreaks.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugin/pagebreaks.js b/src/plugin/pagebreaks.js index c365c3f3..4ee10be0 100644 --- a/src/plugin/pagebreaks.js +++ b/src/plugin/pagebreaks.js @@ -10,7 +10,7 @@ var orig = { // Add pagebreak default options to the Worker template. Worker.template.opt.pagebreak = { - mode: ['css', 'legacy'], // 'avoid-all', 'css', 'legacy' + mode: ['avoid-all', 'css', 'legacy'], // All options: 'avoid-all', 'css', 'legacy' before: [], after: [], avoid: [] From 13f10114d24e1bed15502518d396b5e7d1632998 Mon Sep 17 00:00:00 2001 From: Erik Koopmans Date: Sun, 21 Oct 2018 01:32:31 +1100 Subject: [PATCH 18/20] Add doc and remove avoid-all from default --- src/plugin/pagebreaks.js | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/plugin/pagebreaks.js b/src/plugin/pagebreaks.js index 4ee10be0..5a4dc2b1 100644 --- a/src/plugin/pagebreaks.js +++ b/src/plugin/pagebreaks.js @@ -1,7 +1,27 @@ import Worker from '../worker.js'; import { objType, createElement } from '../utils.js'; -// Add page-break functionality. +/* 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 = { @@ -10,7 +30,7 @@ var orig = { // Add pagebreak default options to the Worker template. Worker.template.opt.pagebreak = { - mode: ['avoid-all', 'css', 'legacy'], // All options: 'avoid-all', 'css', 'legacy' + mode: ['css', 'legacy'], before: [], after: [], avoid: [] From 2de99854f2d8697c982fa099c5974a3a5fc2f91a Mon Sep 17 00:00:00 2001 From: Erik Koopmans Date: Sun, 21 Oct 2018 01:41:33 +1100 Subject: [PATCH 19/20] Change order of set/from in example --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e8268993..a438ad1a 100644 --- a/README.md +++ b/README.md @@ -98,7 +98,7 @@ var opt = { }; // New Promise-based usage: -html2pdf().from(element).set(opt).save(); +html2pdf().set(opt).from(element).save(); // Old monolithic-style usage: html2pdf(element, opt); From 60283226d96ae9f272604c6c536b329fcd688fd9 Mon Sep 17 00:00:00 2001 From: Erik Koopmans Date: Sun, 21 Oct 2018 02:47:35 +1100 Subject: [PATCH 20/20] Add pagebreak updates to the readme --- README.md | 61 +++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 46 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index a438ad1a..138226ae 100644 --- a/README.md +++ b/README.md @@ -106,25 +106,56 @@ html2pdf(element, opt); The `opt` parameter has the following optional fields: -|Name |Type |Default |Description | -|------------|----------------|------------------------------|------------------------------------------------------------------------------------------------------------| -|margin |number or array |0 |PDF margin (in jsPDF units). Can be a single number, `[vMargin, hMargin]`, or `[top, left, bottom, right]`. | -|filename |string |'file.pdf' |The default filename of the exported PDF. | -|image |object |{type: 'jpeg', quality: 0.95} |The image type and quality used to generate the PDF. See the Extra Features section below. | -|enableLinks |boolean |true |If enabled, PDF hyperlinks are automatically added ontop of all anchor tags. | -|html2canvas |object |{ } |Configuration options sent directly to `html2canvas` ([see here](https://html2canvas.hertzen.com/configuration) for usage).| -|jsPDF |object |{ } |Configuration options sent directly to `jsPDF` ([see here](http://rawgit.com/MrRio/jsPDF/master/docs/jsPDF.html) for usage).| +|Name |Type |Default |Description | +|------------|----------------|--------------------------------|------------------------------------------------------------------------------------------------------------| +|margin |number or array |`0` |PDF margin (in jsPDF units). Can be a single number, `[vMargin, hMargin]`, or `[top, left, bottom, right]`. | +|filename |string |`'file.pdf'` |The default filename of the exported PDF. | +|pagebreak |object |`{mode: ['css', 'legacy']}` |Controls the pagebreak behaviour on the page. See [Page-breaks](#page-breaks) below. | +|image |object |`{type: 'jpeg', quality: 0.95}` |The image type and quality used to generate the PDF. See [Image type and quality](#image-type-and-quality) below.| +|enableLinks |boolean |`true` |If enabled, PDF hyperlinks are automatically added ontop of all anchor tags. | +|html2canvas |object |`{ }` |Configuration options sent directly to `html2canvas` ([see here](https://html2canvas.hertzen.com/configuration) for usage).| +|jsPDF |object |`{ }` |Configuration options sent directly to `jsPDF` ([see here](http://rawgit.com/MrRio/jsPDF/master/docs/jsPDF.html) for usage).| ### Page-breaks -You may add `html2pdf`-specific page-breaks to your document by adding the CSS class `html2pdf__page-break` to any element (normally an empty `div`). For React elements, use `className=html2pdf__page-break`. During PDF creation, these elements will be given a height calculated to fill the remainder of the PDF page that they are on. Example usage: +html2pdf has the ability to automatically add page-breaks to clean up your document. Page-breaks can be added by CSS styles, set on individual elements using selectors, or avoided from breaking inside all elements (`avoid-all` mode). -```html -
- I'm on page 1! -
- I'm on page 2! -
+By default, html2pdf will respect most CSS [`break-before`](https://developer.mozilla.org/en-US/docs/Web/CSS/break-before), [`break-after`](https://developer.mozilla.org/en-US/docs/Web/CSS/break-after), and [`break-inside`](https://developer.mozilla.org/en-US/docs/Web/CSS/break-inside) rules, and also add page-breaks after any element with class `html2pdf__page-break` (for legacy purposes). + +#### Page-break settings + +|Setting |Type |Default |Description | +|----------|----------------|--------------------|------------| +|mode |string or array |`['css', 'legacy']` |The mode(s) on which to automatically add page-breaks. One or more of `'avoid-all'`, `'css'`, and `'legacy'`. | +|before |string or array |`[]` |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 |string or array |`[]` |Like 'before', but adds a page-break immediately after the element. | +|avoid |string or array |`[]` |Like 'before', but avoids page-breaks on these elements. You can enable this feature on every element using the 'avoid-all' mode. | + +#### Page-break modes + +| Mode | Description | +|-----------|-------------| +| avoid-all | Automatically adds page-breaks to avoid splitting any elements across pages. | +| css | Adds page-breaks according to the CSS `break-before`, `break-after`, and `break-inside` properties. Only recognizes `always/left/right` for before/after, and `avoid` for inside. | +| legacy | Adds page-breaks after elements with class `html2pdf__page-break`. This feature may be removed in the future. | + +#### Example usage + +```js +// Avoid page-breaks on all elements, and add one before #page2el. +html2pdf().set({ + pagebreak: { mode: 'avoid-all', before: '#page2el' } +}); + +// Enable all 'modes', with no explicit elements. +html2pdf().set({ + pagebreak: { mode: ['avoid-all', 'css', 'legacy'] } +}); + +// No modes, only explicit elements. +html2pdf().set({ + pagebreak: { before: '.beforeClass', after: ['#after1', '#after2'], avoid: 'img' } +}); ``` ### Image type and quality