diff --git a/src/html2pdf.js b/src/html2pdf.js
index 45ae5b0..a6ebe53 100644
--- a/src/html2pdf.js
+++ b/src/html2pdf.js
@@ -32,356 +32,433 @@
* 'image' ('type' and 'quality'), and 'html2canvas' / 'jspdf', which are
* sent as settings to their corresponding functions.
*/
-var html2pdf = (function(html2canvas, jsPDF) {
-
- /* ---------- MAIN FUNCTION ---------- */
-
- var html2pdf = function(source, opt) {
- // Handle input.
- opt = objType(opt) === 'object' ? opt : {};
- var source = html2pdf.parseInput(source, opt);
-
- // Determine the PDF page size.
- var pageSize = jsPDF.getPageSize(opt.jsPDF);
- pageSize.inner = {
- width: pageSize.width - opt.margin[1] - opt.margin[3],
- height: pageSize.height - opt.margin[0] - opt.margin[2]
- };
- pageSize.inner.ratio = pageSize.inner.height / pageSize.inner.width;
-
- // Copy the source element into a PDF-styled container div.
- var container = html2pdf.makeContainer(source, pageSize);
- var overlay = container.parentElement;
-
- // Get the locations of all hyperlinks.
- if (opt.enableLinks) {
- // Find all anchor tags and get the container's bounds for reference.
- opt.links = [];
- var links = container.querySelectorAll('a');
- var containerRect = unitConvert(container.getBoundingClientRect(), pageSize.k);
-
- // Treat each client rect as a separate link (for text-wrapping).
- Array.prototype.forEach.call(links, function(link) {
- var clientRects = link.getClientRects();
- for (var i=0; i whiteColorLevel);
+ };
+
+ /**
+ * @desc (ilan 16/10/2017) - added support to cut the pages on white rows and now on text. added on page break check for the row that gonna be broken, and if the row isn't white - will check for the closest white row above.
+ * @param canvas
+ * @param pageSize
+ * @param opt
+ */
+ html2pdf.makePDF = function (canvas, pageSize, opt) {
+ // Calculate the number of pages.
+ var ctx = canvas.getContext('2d');
+ var pxFullHeight = canvas.height;
+ var pxPageHeight = Math.floor(canvas.width * pageSize.inner.ratio);
+ var nPages = Math.ceil(pxFullHeight / pxPageHeight);
+ var pageModulus = pxFullHeight % pxPageHeight;
+
+ // Create a one-page canvas to split up the full image.
+ var pageCanvas = document.createElement('canvas');
+ var pageCtx = pageCanvas.getContext('2d');
+ var pageHeight = pageSize.inner.height;
+ pageCanvas.width = canvas.width;
+ pageCanvas.height = pxPageHeight;
+
+ // Initialize the PDF.
+ var pdf = new jsPDF(opt.jsPDF);
+
+ var pageStartPos = 0;
+ var maxPixelToGoUp = 150;
+ for (var page = 0; page < nPages; page++) {
+ // Trim the final page to reduce file size.
+ if (page === nPages - 1) {
+ pageCanvas.height = pageModulus;
+ pageHeight = pageCanvas.height * pageSize.inner.width / pageCanvas.width;
+ }
+ // Display the page.
+ var w = pageCanvas.width;
+ var heightDrawBack = 0;
+ do {
+
+ var h = pageCanvas.height - heightDrawBack;
+ var pixelRow = ctx.getImageData(0, pageStartPos+h, w, 1).data;
+ var whiteRow = html2pdf.isCanvasRowWhite(pixelRow);
+ heightDrawBack++;
+ }
+ while (!whiteRow && heightDrawBack < maxPixelToGoUp) ;
+ if(heightDrawBack === maxPixelToGoUp) {
+ heightDrawBack = 0;
+ h = pageCanvas.height;
+ }
+ else {
+ heightDrawBack--;
+ }
+ pageModulus += heightDrawBack;
+ if (pageModulus >= pxPageHeight) {
+ nPages++;
+ pageModulus -= pxPageHeight;
+ }
+ pageCtx.fillStyle = 'white';
+ pageCtx.fillRect(0, 0, w, h);
+ pageCtx.drawImage(canvas, 0, pageStartPos, w, h, 0, 0, w, h);
+
+ if(heightDrawBack) pageCtx.fillRect(0, h, w, heightDrawBack);
+
+ // Add the page to the PDF.
+ if (page) pdf.addPage();
+ var imgData = pageCanvas.toDataURL('image/' + opt.image.type, opt.image.quality);
+ pdf.addImage(imgData, opt.image.type, opt.margin[1], opt.margin[0],
+ pageSize.inner.width, pageHeight);
+
+ pageStartPos += h;
+ // Add hyperlinks.
+ if (opt.enableLinks) {
+ var pageTop = page * pageSize.inner.height;
+ opt.links.forEach(function (link) {
+ if (link.clientRect.top > pageTop && link.clientRect.top < pageTop + pageSize.inner.height) {
+ var left = opt.margin[1] + link.clientRect.left;
+ var top = opt.margin[0] + link.clientRect.top - pageTop;
+ pdf.link(left, top, link.clientRect.width, link.clientRect.height, { url: link.el.href });
+ }
+ });
+ }
+ }
+
+ // Finish the PDF.
+ if (typeof opt.downloadPDF === 'undefined' || opt.downloadPDF) {
+ pdf.save(opt.filename);
+ }
+ if (typeof opt.callback === 'function') {
+ opt.callback(pdf);
+ }
}
- });
- }
-
- // Render the canvas and pass the result to makePDF.
- var onRendered = opt.html2canvas.onrendered || function() {};
- opt.html2canvas.onrendered = function(canvas) {
- onRendered(canvas);
- document.body.removeChild(overlay);
- html2pdf.makePDF(canvas, pageSize, opt);
- }
- html2canvas(container, opt.html2canvas);
- };
-
- html2pdf.parseInput = function(source, opt) {
- // Parse the opt object.
- opt.jsPDF = opt.jsPDF || {};
- opt.html2canvas = opt.html2canvas || {};
- opt.filename = opt.filename && objType(opt.filename) === 'string' ? opt.filename : 'file.pdf';
- opt.enableLinks = opt.hasOwnProperty('enableLinks') ? opt.enableLinks : true;
- opt.image = opt.image || {};
- opt.image.type = opt.image.type || 'jpeg';
- opt.image.quality = opt.image.quality || 0.95;
-
- // Parse the margin property of the opt object.
- switch (objType(opt.margin)) {
- case 'undefined':
- opt.margin = 0;
- case 'number':
- opt.margin = [opt.margin, opt.margin, opt.margin, opt.margin];
- break;
- case 'array':
- if (opt.margin.length === 2) {
- opt.margin = [opt.margin[0], opt.margin[1], opt.margin[0], opt.margin[1]];
- }
- if (opt.margin.length === 4) {
- break;
+
+
+ /* ---------- UTILS ---------- */
+
+ // Determine the type of a variable/object.
+ var objType = function (obj) {
+ if (typeof obj === 'undefined') return 'undefined';
+ else if (typeof obj === 'string' || obj instanceof String) return 'string';
+ else if (typeof obj === 'number' || obj instanceof Number) return 'number';
+ else if (!!obj && obj.constructor === Array) return 'array';
+ else if (obj && obj.nodeType === 1) return 'element';
+ else if (typeof obj === 'object') return 'object';
+ else return 'unknown';
+ };
+
+ // Create an HTML element with optional className, innerHTML, and style.
+ var createElement = function (tagName, opt) {
+ var el = document.createElement(tagName);
+ if (opt.className) el.className = opt.className;
+ if (opt.innerHTML) {
+ el.innerHTML = opt.innerHTML;
+ var scripts = el.getElementsByTagName('script');
+ for (var i = scripts.length; i-- > 0; null) {
+ scripts[i].parentNode.removeChild(scripts[i]);
+ }
+ }
+ for (var key in opt.style) {
+ el.style[key] = opt.style[key];
+ }
+ return el;
+ };
+
+ // Deep-clone a node and preserve contents/properties.
+ var cloneNode = function (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) {
+ if (javascriptEnabled === true || child.nodeType !== 1 || child.nodeName !== 'SCRIPT') {
+ clone.appendChild(cloneNode(child, javascriptEnabled));
+ }
+ }
+
+ if (node.nodeType === 1) {
+ // Preserve contents/properties of special nodes.
+ if (node.nodeName === 'CANVAS') {
+ clone.width = node.width;
+ clone.height = node.height;
+ clone.getContext('2d').drawImage(node, 0, 0);
+ } else if (node.nodeName === 'TEXTAREA' || node.nodeName === 'SELECT') {
+ clone.value = node.value;
+ }
+
+ // Preserve the node's scroll position when it loads.
+ clone.addEventListener('load', function () {
+ clone.scrollTop = node.scrollTop;
+ clone.scrollLeft = node.scrollLeft;
+ }, true);
+ }
+
+ // Return the cloned node.
+ return clone;
}
- default:
- throw 'Invalid margin array.';
- }
-
- // Parse the source element/string.
- if (!source) {
- throw 'Missing source element or string.';
- } else if (objType(source) === 'string') {
- source = createElement('div', { innerHTML: source });
- } else if (objType(source) === 'element') {
- source = cloneNode(source, opt.html2canvas.javascriptEnabled);
- } else {
- throw 'Invalid source - please specify an HTML Element or string.';
- }
-
- // Return the parsed input (opt is modified in-place, no need to return).
- return source;
- };
-
- html2pdf.makeContainer = function(source, pageSize) {
- // Define the CSS styles for the container and its overlay parent.
- var overlayCSS = {
- position: 'fixed', overflow: 'hidden', zIndex: 1000,
- left: 0, right: 0, bottom: 0, top: 0,
- backgroundColor: 'rgba(0,0,0,0.8)'
- };
- var containerCSS = {
- position: 'absolute', width: pageSize.inner.width + pageSize.unit,
- left: 0, right: 0, top: 0, height: 'auto', margin: 'auto',
- backgroundColor: 'white'
- };
-
- // Set the overlay to hidden (could be changed in the future to provide a print preview).
- overlayCSS.opacity = 0;
-
- // Create and attach the elements.
- var overlay = createElement('div', { className: 'html2pdf__overlay', style: overlayCSS });
- var container = createElement('div', { className: 'html2pdf__container', style: containerCSS });
- container.appendChild(source);
- overlay.appendChild(container);
- document.body.appendChild(overlay);
-
- // Enable page-breaks.
- var pageBreaks = source.querySelectorAll('.html2pdf__page-break');
- var pxPageHeight = pageSize.inner.height * pageSize.k / 72 * 96;
- Array.prototype.forEach.call(pageBreaks, function(el) {
- el.style.display = 'block';
- var clientRect = el.getBoundingClientRect();
- el.style.height = pxPageHeight - (clientRect.top % pxPageHeight) + 'px';
- }, this);
-
- // Return the container.
- return container;
- };
-
- html2pdf.makePDF = function(canvas, pageSize, opt) {
- // Calculate the number of pages.
- var ctx = canvas.getContext('2d');
- var pxFullHeight = canvas.height;
- var pxPageHeight = Math.floor(canvas.width * pageSize.inner.ratio);
- var nPages = Math.ceil(pxFullHeight / pxPageHeight);
-
- // Create a one-page canvas to split up the full image.
- var pageCanvas = document.createElement('canvas');
- var pageCtx = pageCanvas.getContext('2d');
- var pageHeight = pageSize.inner.height;
- pageCanvas.width = canvas.width;
- pageCanvas.height = pxPageHeight;
-
- // Initialize the PDF.
- var pdf = new jsPDF(opt.jsPDF);
-
- for (var page=0; page pageTop && link.clientRect.top < pageTop + pageSize.inner.height) {
- var left = opt.margin[1] + link.clientRect.left;
- var top = opt.margin[0] + link.clientRect.top - pageTop;
- pdf.link(left, top, link.clientRect.width, link.clientRect.height, { url: link.el.href });
- }
- });
- }
- }
-
- // Finish the PDF.
- pdf.save( opt.filename );
- }
-
-
- /* ---------- UTILS ---------- */
-
- // Determine the type of a variable/object.
- var objType = function(obj) {
- if (typeof obj === 'undefined') return 'undefined';
- else if (typeof obj === 'string' || obj instanceof String) return 'string';
- else if (typeof obj === 'number' || obj instanceof Number) return 'number';
- else if (!!obj && obj.constructor === Array) return 'array';
- else if (obj && obj.nodeType === 1) return 'element';
- else if (typeof obj === 'object') return 'object';
- else return 'unknown';
- };
-
- // Create an HTML element with optional className, innerHTML, and style.
- var createElement = function(tagName, opt) {
- var el = document.createElement(tagName);
- if (opt.className) el.className = opt.className;
- if (opt.innerHTML) {
- el.innerHTML = opt.innerHTML;
- var scripts = el.getElementsByTagName('script');
- for (var i = scripts.length; i-- > 0; null) {
- scripts[i].parentNode.removeChild(scripts[i]);
- }
- }
- for (var key in opt.style) {
- el.style[key] = opt.style[key];
- }
- return el;
- };
-
- // Deep-clone a node and preserve contents/properties.
- var cloneNode = function(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) {
- if (javascriptEnabled === true || child.nodeType !== 1 || child.nodeName !== 'SCRIPT') {
- clone.appendChild(cloneNode(child, javascriptEnabled));
- }
- }
-
- if (node.nodeType === 1) {
- // Preserve contents/properties of special nodes.
- if (node.nodeName === 'CANVAS') {
- clone.width = node.width;
- clone.height = node.height;
- clone.getContext('2d').drawImage(node, 0, 0);
- } else if (node.nodeName === 'TEXTAREA' || node.nodeName === 'SELECT') {
- clone.value = node.value;
- }
-
- // Preserve the node's scroll position when it loads.
- clone.addEventListener('load', function() {
- clone.scrollTop = node.scrollTop;
- clone.scrollLeft = node.scrollLeft;
- }, true);
- }
-
- // Return the cloned node.
- return clone;
- }
-
- // Convert units using the conversion value 'k' from jsPDF.
- var unitConvert = function(obj, k) {
- var newObj = {};
- for (var key in obj) {
- newObj[key] = obj[key] * 72 / 96 / k;
- }
- return newObj;
- };
-
- // Get dimensions of a PDF page, as determined by jsPDF.
- jsPDF.getPageSize = function(orientation, unit, format) {
- // Decode options object
- if (typeof orientation === 'object') {
- var options = orientation;
- orientation = options.orientation;
- unit = options.unit || unit;
- format = options.format || format;
- }
-
- // Default options
- unit = unit || 'mm';
- format = format || 'a4';
- orientation = ('' + (orientation || 'P')).toLowerCase();
- var format_as_string = ('' + format).toLowerCase();
-
- // Size in pt of various paper formats
- pageFormats = {
- 'a0' : [2383.94, 3370.39], 'a1' : [1683.78, 2383.94],
- 'a2' : [1190.55, 1683.78], 'a3' : [ 841.89, 1190.55],
- 'a4' : [ 595.28, 841.89], 'a5' : [ 419.53, 595.28],
- 'a6' : [ 297.64, 419.53], 'a7' : [ 209.76, 297.64],
- 'a8' : [ 147.40, 209.76], 'a9' : [ 104.88, 147.40],
- 'a10' : [ 73.70, 104.88], 'b0' : [2834.65, 4008.19],
- 'b1' : [2004.09, 2834.65], 'b2' : [1417.32, 2004.09],
- 'b3' : [1000.63, 1417.32], 'b4' : [ 708.66, 1000.63],
- 'b5' : [ 498.90, 708.66], 'b6' : [ 354.33, 498.90],
- 'b7' : [ 249.45, 354.33], 'b8' : [ 175.75, 249.45],
- 'b9' : [ 124.72, 175.75], 'b10' : [ 87.87, 124.72],
- 'c0' : [2599.37, 3676.54], 'c1' : [1836.85, 2599.37],
- 'c2' : [1298.27, 1836.85], 'c3' : [ 918.43, 1298.27],
- 'c4' : [ 649.13, 918.43], 'c5' : [ 459.21, 649.13],
- 'c6' : [ 323.15, 459.21], 'c7' : [ 229.61, 323.15],
- 'c8' : [ 161.57, 229.61], 'c9' : [ 113.39, 161.57],
- 'c10' : [ 79.37, 113.39], 'dl' : [ 311.81, 623.62],
- 'letter' : [612, 792],
- 'government-letter' : [576, 756],
- 'legal' : [612, 1008],
- 'junior-legal' : [576, 360],
- 'ledger' : [1224, 792],
- 'tabloid' : [792, 1224],
- 'credit-card' : [153, 243]
- };
-
- // Unit conversion
- switch (unit) {
- case 'pt': k = 1; break;
- case 'mm': k = 72 / 25.4; break;
- case 'cm': k = 72 / 2.54; break;
- case 'in': k = 72; break;
- case 'px': k = 72 / 96; break;
- case 'pc': k = 12; break;
- case 'em': k = 12; break;
- case 'ex': k = 6; break;
- default:
- throw ('Invalid unit: ' + unit);
- }
-
- // Dimensions are stored as user units and converted to points on output
- if (pageFormats.hasOwnProperty(format_as_string)) {
- pageHeight = pageFormats[format_as_string][1] / k;
- pageWidth = pageFormats[format_as_string][0] / k;
- } else {
- try {
- pageHeight = format[1];
- pageWidth = format[0];
- } catch (err) {
- throw new Error('Invalid format: ' + format);
- }
- }
-
- // Handle page orientation
- if (orientation === 'p' || orientation === 'portrait') {
- orientation = 'p';
- if (pageWidth > pageHeight) {
- tmp = pageWidth;
- pageWidth = pageHeight;
- pageHeight = tmp;
- }
- } else if (orientation === 'l' || orientation === 'landscape') {
- orientation = 'l';
- if (pageHeight > pageWidth) {
- tmp = pageWidth;
- pageWidth = pageHeight;
- pageHeight = tmp;
- }
- } else {
- throw('Invalid orientation: ' + orientation);
- }
-
- // Return information (k is the unit conversion ratio from pts)
- var info = { 'width': pageWidth, 'height': pageHeight, 'unit': unit, 'k': k };
- return info;
- };
-
-
- // Expose the html2pdf function.
- return html2pdf;
-}(html2canvas, jsPDF));
+
+ // Convert units using the conversion value 'k' from jsPDF.
+ var unitConvert = function (obj, k) {
+ var newObj = {};
+ for (var key in obj) {
+ newObj[key] = obj[key] * 72 / 96 / k;
+ }
+ return newObj;
+ };
+
+ // Get dimensions of a PDF page, as determined by jsPDF.
+ jsPDF.getPageSize = function (orientation, unit, format) {
+ // Decode options object
+ if (typeof orientation === 'object') {
+ var options = orientation;
+ orientation = options.orientation;
+ unit = options.unit || unit;
+ format = options.format || format;
+ }
+
+ // Default options
+ unit = unit || 'mm';
+ format = format || 'a4';
+ orientation = ('' + (orientation || 'P')).toLowerCase();
+ var format_as_string = ('' + format).toLowerCase();
+
+ // Size in pt of various paper formats
+ pageFormats = {
+ 'a0': [2383.94, 3370.39], 'a1': [1683.78, 2383.94],
+ 'a2': [1190.55, 1683.78], 'a3': [841.89, 1190.55],
+ 'a4': [595.28, 841.89], 'a5': [419.53, 595.28],
+ 'a6': [297.64, 419.53], 'a7': [209.76, 297.64],
+ 'a8': [147.40, 209.76], 'a9': [104.88, 147.40],
+ 'a10': [73.70, 104.88], 'b0': [2834.65, 4008.19],
+ 'b1': [2004.09, 2834.65], 'b2': [1417.32, 2004.09],
+ 'b3': [1000.63, 1417.32], 'b4': [708.66, 1000.63],
+ 'b5': [498.90, 708.66], 'b6': [354.33, 498.90],
+ 'b7': [249.45, 354.33], 'b8': [175.75, 249.45],
+ 'b9': [124.72, 175.75], 'b10': [87.87, 124.72],
+ 'c0': [2599.37, 3676.54], 'c1': [1836.85, 2599.37],
+ 'c2': [1298.27, 1836.85], 'c3': [918.43, 1298.27],
+ 'c4': [649.13, 918.43], 'c5': [459.21, 649.13],
+ 'c6': [323.15, 459.21], 'c7': [229.61, 323.15],
+ 'c8': [161.57, 229.61], 'c9': [113.39, 161.57],
+ 'c10': [79.37, 113.39], 'dl': [311.81, 623.62],
+ 'letter': [612, 792],
+ 'government-letter': [576, 756],
+ 'legal': [612, 1008],
+ 'junior-legal': [576, 360],
+ 'ledger': [1224, 792],
+ 'tabloid': [792, 1224],
+ 'credit-card': [153, 243]
+ };
+
+ // Unit conversion
+ switch (unit) {
+ case 'pt':
+ k = 1;
+ break;
+ case 'mm':
+ k = 72 / 25.4;
+ break;
+ case 'cm':
+ k = 72 / 2.54;
+ break;
+ case 'in':
+ k = 72;
+ break;
+ case 'px':
+ k = 72 / 96;
+ break;
+ case 'pc':
+ k = 12;
+ break;
+ case 'em':
+ k = 12;
+ break;
+ case 'ex':
+ k = 6;
+ break;
+ default:
+ throw ('Invalid unit: ' + unit);
+ }
+
+ // Dimensions are stored as user units and converted to points on output
+ if (pageFormats.hasOwnProperty(format_as_string)) {
+ pageHeight = pageFormats[format_as_string][1] / k;
+ pageWidth = pageFormats[format_as_string][0] / k;
+ } else {
+ try {
+ pageHeight = format[1];
+ pageWidth = format[0];
+ } catch (err) {
+ throw new Error('Invalid format: ' + format);
+ }
+ }
+
+ // Handle page orientation
+ if (orientation === 'p' || orientation === 'portrait') {
+ orientation = 'p';
+ if (pageWidth > pageHeight) {
+ tmp = pageWidth;
+ pageWidth = pageHeight;
+ pageHeight = tmp;
+ }
+ } else if (orientation === 'l' || orientation === 'landscape') {
+ orientation = 'l';
+ if (pageHeight > pageWidth) {
+ tmp = pageWidth;
+ pageWidth = pageHeight;
+ pageHeight = tmp;
+ }
+ } else {
+ throw('Invalid orientation: ' + orientation);
+ }
+
+ // Return information (k is the unit conversion ratio from pts)
+ var info = { 'width': pageWidth, 'height': pageHeight, 'unit': unit, 'k': k };
+ return info;
+ };
+
+
+ // Expose the html2pdf function.
+ return html2pdf;
+ }(html2canvas, jsPDF)
+);