@chantouchsek/html2pdf-js
Advanced tools
@@ -5,2 +5,9 @@ # Changelog | ||
### [0.0.3](https://github.com/chantouchsek/html2pdf-js/compare/v0.0.2...v0.0.3) (2021-12-11) | ||
### Bug Fixes | ||
* :fire: fix ssr of self ([5cbe71e](https://github.com/chantouchsek/html2pdf-js/commit/5cbe71eda8b275846e0239393abef34fb5d53629)) | ||
### [0.0.2](https://github.com/chantouchsek/html2pdf-js/compare/v0.0.1...v0.0.2) (2021-12-02) | ||
@@ -7,0 +14,0 @@ |
/*! | ||
* @chantouchsek/html2pdf-js v0.0.1 | ||
* Copyright (c) 2021 Erik Koopmans | ||
* @chantouchsek/html2pdf-js v0.0.2 | ||
* Copyright (c) 2021 Chantouch Sek | ||
* Released under the MIT License. | ||
@@ -5,0 +5,0 @@ */ |
/*! | ||
* @chantouchsek/html2pdf-js v0.0.1 | ||
* Copyright (c) 2021 Erik Koopmans | ||
* @chantouchsek/html2pdf-js v0.0.2 | ||
* Copyright (c) 2021 Chantouch Sek | ||
* Released under the MIT License. | ||
@@ -5,0 +5,0 @@ */ |
{ | ||
"name": "@chantouchsek/html2pdf-js", | ||
"version": "0.0.2", | ||
"version": "0.0.3", | ||
"description": "Client-side HTML-to-PDF rendering using pure JS", | ||
@@ -5,0 +5,0 @@ "main": "dist/html2pdf.js", |
@@ -15,12 +15,12 @@ import Worker from './worker.js'; | ||
var html2pdf = function html2pdf(src, opt) { | ||
// Create a new worker with the given options. | ||
var worker = new html2pdf.Worker(opt); | ||
// Create a new worker with the given options. | ||
var worker = new html2pdf.Worker(opt); | ||
if (src) { | ||
// If src is specified, perform the traditional 'simple' operation. | ||
return worker.from(src).save(); | ||
} else { | ||
// Otherwise, return the worker for new Promise-based operation. | ||
return worker; | ||
} | ||
if (src) { | ||
// If src is specified, perform the traditional 'simple' operation. | ||
return worker.from(src).save(); | ||
} else { | ||
// Otherwise, return the worker for new Promise-based operation. | ||
return worker; | ||
} | ||
} | ||
@@ -27,0 +27,0 @@ html2pdf.Worker = Worker; |
import Worker from '../worker.js'; | ||
import { unitConvert } from '../utils.js'; | ||
import {unitConvert} from '../utils.js'; | ||
@@ -9,52 +9,52 @@ // Add hyperlink functionality to the PDF creation. | ||
var orig = { | ||
toContainer: Worker.prototype.toContainer, | ||
toPdf: Worker.prototype.toPdf, | ||
toContainer: Worker.prototype.toContainer, | ||
toPdf: Worker.prototype.toPdf, | ||
}; | ||
Worker.prototype.toContainer = function toContainer() { | ||
return orig.toContainer.call(this).then(function toContainer_hyperlink() { | ||
// Retrieve hyperlink info if the option is enabled. | ||
if (this.opt.enableLinks) { | ||
// Find all anchor tags and get the container's bounds for reference. | ||
var container = this.prop.container; | ||
var links = container.querySelectorAll('a'); | ||
var containerRect = unitConvert(container.getBoundingClientRect(), this.prop.pageSize.k); | ||
linkInfo = []; | ||
return orig.toContainer.call(this).then(function toContainer_hyperlink() { | ||
// Retrieve hyperlink info if the option is enabled. | ||
if (this.opt.enableLinks) { | ||
// Find all anchor tags and get the container's bounds for reference. | ||
var container = this.prop.container; | ||
var links = container.querySelectorAll('a'); | ||
var containerRect = unitConvert(container.getBoundingClientRect(), this.prop.pageSize.k); | ||
linkInfo = []; | ||
// Loop through each anchor tag. | ||
Array.prototype.forEach.call(links, function(link) { | ||
// Treat each client rect as a separate link (for text-wrapping). | ||
var clientRects = link.getClientRects(); | ||
for (var i=0; i<clientRects.length; i++) { | ||
var clientRect = unitConvert(clientRects[i], this.prop.pageSize.k); | ||
clientRect.left -= containerRect.left; | ||
clientRect.top -= containerRect.top; | ||
// Loop through each anchor tag. | ||
Array.prototype.forEach.call(links, function (link) { | ||
// Treat each client rect as a separate link (for text-wrapping). | ||
var clientRects = link.getClientRects(); | ||
for (var i = 0; i < clientRects.length; i++) { | ||
var clientRect = unitConvert(clientRects[i], this.prop.pageSize.k); | ||
clientRect.left -= containerRect.left; | ||
clientRect.top -= containerRect.top; | ||
var page = Math.floor(clientRect.top / this.prop.pageSize.inner.height) + 1; | ||
var top = this.opt.margin[0] + clientRect.top % this.prop.pageSize.inner.height; | ||
var left = this.opt.margin[1] + clientRect.left; | ||
var page = Math.floor(clientRect.top / this.prop.pageSize.inner.height) + 1; | ||
var top = this.opt.margin[0] + clientRect.top % this.prop.pageSize.inner.height; | ||
var left = this.opt.margin[1] + clientRect.left; | ||
linkInfo.push({ page, top, left, clientRect, link }); | ||
linkInfo.push({page, top, left, clientRect, link}); | ||
} | ||
}, this); | ||
} | ||
}, this); | ||
} | ||
}); | ||
}); | ||
}; | ||
Worker.prototype.toPdf = function toPdf() { | ||
return orig.toPdf.call(this).then(function toPdf_hyperlink() { | ||
// Add hyperlinks if the option is enabled. | ||
if (this.opt.enableLinks) { | ||
// Attach each anchor tag based on info from toContainer(). | ||
linkInfo.forEach(function(l) { | ||
this.prop.pdf.setPage(l.page); | ||
this.prop.pdf.link(l.left, l.top, l.clientRect.width, l.clientRect.height, | ||
{ url: l.link.href }); | ||
}, this); | ||
return orig.toPdf.call(this).then(function toPdf_hyperlink() { | ||
// Add hyperlinks if the option is enabled. | ||
if (this.opt.enableLinks) { | ||
// Attach each anchor tag based on info from toContainer(). | ||
linkInfo.forEach(function (l) { | ||
this.prop.pdf.setPage(l.page); | ||
this.prop.pdf.link(l.left, l.top, l.clientRect.width, l.clientRect.height, | ||
{url: l.link.href}); | ||
}, this); | ||
// Reset the active page of the PDF to the final page. | ||
var nPages = this.prop.pdf.internal.getNumberOfPages(); | ||
this.prop.pdf.setPage(nPages); | ||
} | ||
}); | ||
// Reset the active page of the PDF to the final page. | ||
var nPages = this.prop.pdf.internal.getNumberOfPages(); | ||
this.prop.pdf.setPage(nPages); | ||
} | ||
}); | ||
}; |
// Import dependencies. | ||
import { jsPDF } from 'jspdf'; | ||
import {jsPDF} from 'jspdf'; | ||
// 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; | ||
} | ||
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(); | ||
// 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 | ||
var 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] | ||
}; | ||
// Size in pt of various paper formats | ||
var 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': var k = 1; break; | ||
case 'mm': var k = 72 / 25.4; break; | ||
case 'cm': var k = 72 / 2.54; break; | ||
case 'in': var k = 72; break; | ||
case 'px': var k = 72 / 96; break; | ||
case 'pc': var k = 12; break; | ||
case 'em': var k = 12; break; | ||
case 'ex': var k = 6; break; | ||
default: | ||
throw ('Invalid unit: ' + unit); | ||
} | ||
// Unit conversion | ||
switch (unit) { | ||
case 'pt': | ||
var k = 1; | ||
break; | ||
case 'mm': | ||
var k = 72 / 25.4; | ||
break; | ||
case 'cm': | ||
var k = 72 / 2.54; | ||
break; | ||
case 'in': | ||
var k = 72; | ||
break; | ||
case 'px': | ||
var k = 72 / 96; | ||
break; | ||
case 'pc': | ||
var k = 12; | ||
break; | ||
case 'em': | ||
var k = 12; | ||
break; | ||
case 'ex': | ||
var 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)) { | ||
var pageHeight = pageFormats[format_as_string][1] / k; | ||
var pageWidth = pageFormats[format_as_string][0] / k; | ||
} else { | ||
try { | ||
var pageHeight = format[1]; | ||
var pageWidth = format[0]; | ||
} catch (err) { | ||
throw new Error('Invalid format: ' + format); | ||
// Dimensions are stored as user units and converted to points on output | ||
if (pageFormats.hasOwnProperty(format_as_string)) { | ||
var pageHeight = pageFormats[format_as_string][1] / k; | ||
var pageWidth = pageFormats[format_as_string][0] / k; | ||
} else { | ||
try { | ||
var pageHeight = format[1]; | ||
var pageWidth = format[0]; | ||
} catch (err) { | ||
throw new Error('Invalid format: ' + format); | ||
} | ||
} | ||
} | ||
// Handle page orientation | ||
if (orientation === 'p' || orientation === 'portrait') { | ||
orientation = 'p'; | ||
if (pageWidth > pageHeight) { | ||
var tmp = pageWidth; | ||
pageWidth = pageHeight; | ||
pageHeight = tmp; | ||
// Handle page orientation | ||
if (orientation === 'p' || orientation === 'portrait') { | ||
orientation = 'p'; | ||
if (pageWidth > pageHeight) { | ||
var tmp = pageWidth; | ||
pageWidth = pageHeight; | ||
pageHeight = tmp; | ||
} | ||
} else if (orientation === 'l' || orientation === 'landscape') { | ||
orientation = 'l'; | ||
if (pageHeight > pageWidth) { | ||
var tmp = pageWidth; | ||
pageWidth = pageHeight; | ||
pageHeight = tmp; | ||
} | ||
} else { | ||
throw('Invalid orientation: ' + orientation); | ||
} | ||
} else if (orientation === 'l' || orientation === 'landscape') { | ||
orientation = 'l'; | ||
if (pageHeight > pageWidth) { | ||
var 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; | ||
// Return information (k is the unit conversion ratio from pts) | ||
var info = {'width': pageWidth, 'height': pageHeight, 'unit': unit, 'k': k}; | ||
return info; | ||
}; | ||
export default jsPDF; |
import Worker from '../worker.js'; | ||
import { objType, createElement } from '../utils.js'; | ||
import {objType, createElement} from '../utils.js'; | ||
@@ -28,3 +28,3 @@ /* Pagebreak plugin: | ||
var orig = { | ||
toContainer: Worker.prototype.toContainer | ||
toContainer: Worker.prototype.toContainer | ||
}; | ||
@@ -34,103 +34,107 @@ | ||
Worker.template.opt.pagebreak = { | ||
mode: ['css', 'legacy'], | ||
before: [], | ||
after: [], | ||
avoid: [] | ||
mode: ['css', 'legacy'], | ||
before: [], | ||
after: [], | ||
avoid: [] | ||
}; | ||
Worker.prototype.toContainer = function toContainer() { | ||
return orig.toContainer.call(this).then(function toContainer_pagebreak() { | ||
// Setup root element and inner page height. | ||
var root = this.prop.container; | ||
var pxPageHeight = this.prop.pageSize.inner.px.height; | ||
return orig.toContainer.call(this).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 | ||
}; | ||
// 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 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); | ||
// 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 | ||
}; | ||
// 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 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; | ||
}); | ||
// 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(); | ||
// 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; | ||
// 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; | ||
} | ||
} | ||
// 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); | ||
} | ||
// 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); | ||
} | ||
// 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); | ||
} | ||
}); | ||
}); | ||
}); | ||
}; |
106
src/utils.js
// Determine the type of a variable/object. | ||
export const objType = function objType(obj) { | ||
var type = typeof obj; | ||
if (type === 'undefined') return 'undefined'; | ||
else if (type === 'string' || obj instanceof String) return 'string'; | ||
else if (type === 'number' || obj instanceof Number) return 'number'; | ||
else if (type === 'function' || obj instanceof Function) return 'function'; | ||
else if (!!obj && obj.constructor === Array) return 'array'; | ||
else if (obj && obj.nodeType === 1) return 'element'; | ||
else if (type === 'object') return 'object'; | ||
else return 'unknown'; | ||
var type = typeof obj; | ||
if (type === 'undefined') return 'undefined'; | ||
else if (type === 'string' || obj instanceof String) return 'string'; | ||
else if (type === 'number' || obj instanceof Number) return 'number'; | ||
else if (type === 'function' || obj instanceof Function) return 'function'; | ||
else if (!!obj && obj.constructor === Array) return 'array'; | ||
else if (obj && obj.nodeType === 1) return 'element'; | ||
else if (type === 'object') return 'object'; | ||
else return 'unknown'; | ||
}; | ||
@@ -16,15 +16,15 @@ | ||
export const createElement = function createElement(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]); | ||
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; | ||
for (var key in opt.style) { | ||
el.style[key] = opt.style[key]; | ||
} | ||
return el; | ||
}; | ||
@@ -34,29 +34,29 @@ | ||
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) { | ||
if (javascriptEnabled === true || child.nodeType !== 1 || child.nodeName !== 'SCRIPT') { | ||
clone.appendChild(cloneNode(child, 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; | ||
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); | ||
} | ||
// 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; | ||
// Return the cloned node. | ||
return clone; | ||
} | ||
@@ -66,11 +66,11 @@ | ||
export const unitConvert = function unitConvert(obj, 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; | ||
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; | ||
} | ||
}; | ||
@@ -80,3 +80,3 @@ | ||
export const toPx = function toPx(val, k) { | ||
return Math.floor(val * k / 72 * 96); | ||
return Math.floor(val * k / 72 * 96); | ||
} |
@@ -1,5 +0,6 @@ | ||
import { jsPDF } from 'jspdf'; | ||
import {jsPDF} from 'jspdf'; | ||
import * as html2canvas from 'html2canvas'; | ||
import { objType, createElement, cloneNode, toPx } from './utils.js'; | ||
import {objType, createElement, cloneNode, toPx} from './utils.js'; | ||
import es6promise from 'es6-promise'; | ||
var Promise = es6promise.Promise; | ||
@@ -10,11 +11,11 @@ | ||
var Worker = function Worker(opt) { | ||
// Create the root parent for the proto chain, and the starting Worker. | ||
var root = Object.assign(Worker.convert(Promise.resolve()), | ||
JSON.parse(JSON.stringify(Worker.template))); | ||
var self = Worker.convert(Promise.resolve(), root); | ||
// Create the root parent for the proto chain, and the starting Worker. | ||
var root = Object.assign(Worker.convert(Promise.resolve()), | ||
JSON.parse(JSON.stringify(Worker.template))); | ||
var self = Worker.convert(Promise.resolve(), root); | ||
// Set progress, optional settings, and return. | ||
self = self.setProgress(1, Worker, 1, [Worker]); | ||
self = self.set(opt); | ||
return self; | ||
// Set progress, optional settings, and return. | ||
self = self.setProgress(1, Worker, 1, [Worker]); | ||
self = self.set(opt); | ||
return self; | ||
}; | ||
@@ -28,31 +29,31 @@ | ||
Worker.convert = function convert(promise, inherit) { | ||
// Uses prototypal inheritance to receive changes made to ancestors' properties. | ||
promise.__proto__ = inherit || Worker.prototype; | ||
return promise; | ||
// Uses prototypal inheritance to receive changes made to ancestors' properties. | ||
promise.__proto__ = inherit || Worker.prototype; | ||
return promise; | ||
}; | ||
Worker.template = { | ||
prop: { | ||
src: null, | ||
container: null, | ||
overlay: null, | ||
canvas: null, | ||
img: null, | ||
pdf: null, | ||
pageSize: null | ||
}, | ||
progress: { | ||
val: 0, | ||
state: null, | ||
n: 0, | ||
stack: [] | ||
}, | ||
opt: { | ||
filename: 'file.pdf', | ||
margin: [0,0,0,0], | ||
image: { type: 'jpeg', quality: 0.95 }, | ||
enableLinks: true, | ||
html2canvas: {}, | ||
jsPDF: {} | ||
} | ||
prop: { | ||
src: null, | ||
container: null, | ||
overlay: null, | ||
canvas: null, | ||
img: null, | ||
pdf: null, | ||
pageSize: null | ||
}, | ||
progress: { | ||
val: 0, | ||
state: null, | ||
n: 0, | ||
stack: [] | ||
}, | ||
opt: { | ||
filename: 'file.pdf', | ||
margin: [0, 0, 0, 0], | ||
image: {type: 'jpeg', quality: 0.95}, | ||
enableLinks: true, | ||
html2canvas: {}, | ||
jsPDF: {} | ||
} | ||
}; | ||
@@ -63,159 +64,178 @@ | ||
Worker.prototype.from = function from(src, type) { | ||
function getType(src) { | ||
switch (objType(src)) { | ||
case 'string': return 'string'; | ||
case 'element': return src.nodeName.toLowerCase === 'canvas' ? 'canvas' : 'element'; | ||
default: return 'unknown'; | ||
function getType(src) { | ||
switch (objType(src)) { | ||
case 'string': | ||
return 'string'; | ||
case 'element': | ||
return src.nodeName.toLowerCase === 'canvas' ? 'canvas' : 'element'; | ||
default: | ||
return 'unknown'; | ||
} | ||
} | ||
} | ||
return this.then(function from_main() { | ||
type = type || getType(src); | ||
switch (type) { | ||
case 'string': return this.set({ src: createElement('div', {innerHTML: src}) }); | ||
case 'element': return this.set({ src: src }); | ||
case 'canvas': return this.set({ canvas: src }); | ||
case 'img': return this.set({ img: src }); | ||
default: return this.error('Unknown source type.'); | ||
} | ||
}); | ||
return this.then(function from_main() { | ||
type = type || getType(src); | ||
switch (type) { | ||
case 'string': | ||
return this.set({src: createElement('div', {innerHTML: src})}); | ||
case 'element': | ||
return this.set({src: src}); | ||
case 'canvas': | ||
return this.set({canvas: src}); | ||
case 'img': | ||
return this.set({img: src}); | ||
default: | ||
return this.error('Unknown source type.'); | ||
} | ||
}); | ||
}; | ||
Worker.prototype.to = function to(target) { | ||
// Route the 'to' request to the appropriate method. | ||
switch (target) { | ||
case 'container': | ||
return this.toContainer(); | ||
case 'canvas': | ||
return this.toCanvas(); | ||
case 'img': | ||
return this.toImg(); | ||
case 'pdf': | ||
return this.toPdf(); | ||
default: | ||
return this.error('Invalid target.'); | ||
} | ||
// Route the 'to' request to the appropriate method. | ||
switch (target) { | ||
case 'container': | ||
return this.toContainer(); | ||
case 'canvas': | ||
return this.toCanvas(); | ||
case 'img': | ||
return this.toImg(); | ||
case 'pdf': | ||
return this.toPdf(); | ||
default: | ||
return this.error('Invalid target.'); | ||
} | ||
}; | ||
Worker.prototype.toContainer = function toContainer() { | ||
// Set up function prerequisites. | ||
var prereqs = [ | ||
function checkSrc() { return this.prop.src || this.error('Cannot duplicate - no source HTML.'); }, | ||
function checkPageSize() { return this.prop.pageSize || this.setPageSize(); } | ||
]; | ||
// Set up function prerequisites. | ||
var prereqs = [ | ||
function checkSrc() { | ||
return this.prop.src || this.error('Cannot duplicate - no source HTML.'); | ||
}, | ||
function checkPageSize() { | ||
return this.prop.pageSize || this.setPageSize(); | ||
} | ||
]; | ||
return this.thenList(prereqs).then(function toContainer_main() { | ||
// 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: this.prop.pageSize.inner.width + this.prop.pageSize.unit, | ||
left: 0, right: 0, top: 0, height: 'auto', margin: 'auto', | ||
backgroundColor: 'white' | ||
}; | ||
return this.thenList(prereqs).then(function toContainer_main() { | ||
// 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: this.prop.pageSize.inner.width + this.prop.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; | ||
// 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 source = cloneNode(this.prop.src, this.opt.html2canvas.javascriptEnabled); | ||
this.prop.overlay = createElement('div', { className: 'v-application html2pdf__overlay', style: overlayCSS }); | ||
this.prop.container = createElement('div', { className: 'html2pdf__container', style: containerCSS }); | ||
this.prop.container.appendChild(source); | ||
this.prop.overlay.appendChild(this.prop.container); | ||
document.body.appendChild(this.prop.overlay); | ||
}); | ||
// Create and attach the elements. | ||
var source = cloneNode(this.prop.src, this.opt.html2canvas.javascriptEnabled); | ||
this.prop.overlay = createElement('div', {className: 'v-application html2pdf__overlay', style: overlayCSS}); | ||
this.prop.container = createElement('div', {className: 'html2pdf__container', style: containerCSS}); | ||
this.prop.container.appendChild(source); | ||
this.prop.overlay.appendChild(this.prop.container); | ||
document.body.appendChild(this.prop.overlay); | ||
}); | ||
}; | ||
Worker.prototype.toCanvas = function toCanvas() { | ||
// Set up function prerequisites. | ||
var prereqs = [ | ||
function checkContainer() { return document.body.contains(this.prop.container) | ||
|| this.toContainer(); } | ||
]; | ||
// Set up function prerequisites. | ||
var prereqs = [ | ||
function checkContainer() { | ||
return document.body.contains(this.prop.container) | ||
|| this.toContainer(); | ||
} | ||
]; | ||
// Fulfill prereqs then create the canvas. | ||
return this.thenList(prereqs).then(function toCanvas_main() { | ||
// Handle old-fashioned 'onrendered' argument. | ||
var options = Object.assign({}, this.opt.html2canvas); | ||
delete options.onrendered; | ||
// Fulfill prereqs then create the canvas. | ||
return this.thenList(prereqs).then(function toCanvas_main() { | ||
// Handle old-fashioned 'onrendered' argument. | ||
var options = Object.assign({}, this.opt.html2canvas); | ||
delete options.onrendered; | ||
return html2canvas(this.prop.container, options); | ||
}).then(function toCanvas_post(canvas) { | ||
// Handle old-fashioned 'onrendered' argument. | ||
var onRendered = this.opt.html2canvas.onrendered || function () {}; | ||
onRendered(canvas); | ||
return html2canvas(this.prop.container, options); | ||
}).then(function toCanvas_post(canvas) { | ||
// Handle old-fashioned 'onrendered' argument. | ||
var onRendered = this.opt.html2canvas.onrendered || function () { | ||
}; | ||
onRendered(canvas); | ||
this.prop.canvas = canvas; | ||
document.body.removeChild(this.prop.overlay); | ||
}); | ||
this.prop.canvas = canvas; | ||
document.body.removeChild(this.prop.overlay); | ||
}); | ||
}; | ||
Worker.prototype.toImg = function toImg() { | ||
// Set up function prerequisites. | ||
var prereqs = [ | ||
function checkCanvas() { return this.prop.canvas || this.toCanvas(); } | ||
]; | ||
// Set up function prerequisites. | ||
var prereqs = [ | ||
function checkCanvas() { | ||
return this.prop.canvas || this.toCanvas(); | ||
} | ||
]; | ||
// Fulfill prereqs then create the image. | ||
return this.thenList(prereqs).then(function toImg_main() { | ||
var imgData = this.prop.canvas.toDataURL('image/' + this.opt.image.type, this.opt.image.quality); | ||
this.prop.img = document.createElement('img'); | ||
this.prop.img.src = imgData; | ||
}); | ||
// Fulfill prereqs then create the image. | ||
return this.thenList(prereqs).then(function toImg_main() { | ||
var imgData = this.prop.canvas.toDataURL('image/' + this.opt.image.type, this.opt.image.quality); | ||
this.prop.img = document.createElement('img'); | ||
this.prop.img.src = imgData; | ||
}); | ||
}; | ||
Worker.prototype.toPdf = function toPdf() { | ||
// Set up function prerequisites. | ||
var prereqs = [ | ||
function checkCanvas() { return this.prop.canvas || this.toCanvas(); } | ||
]; | ||
// Set up function prerequisites. | ||
var prereqs = [ | ||
function checkCanvas() { | ||
return this.prop.canvas || this.toCanvas(); | ||
} | ||
]; | ||
// Fulfill prereqs then create the image. | ||
return this.thenList(prereqs).then(function toPdf_main() { | ||
// Create local copies of frequently used properties. | ||
var canvas = this.prop.canvas; | ||
var opt = this.opt; | ||
// Fulfill prereqs then create the image. | ||
return this.thenList(prereqs).then(function toPdf_main() { | ||
// Create local copies of frequently used properties. | ||
var canvas = this.prop.canvas; | ||
var opt = this.opt; | ||
// Calculate the number of pages. | ||
var pxFullHeight = canvas.height; | ||
var pxPageHeight = Math.floor(canvas.width * this.prop.pageSize.inner.ratio); | ||
var nPages = Math.ceil(pxFullHeight / pxPageHeight); | ||
// Calculate the number of pages. | ||
var pxFullHeight = canvas.height; | ||
var pxPageHeight = Math.floor(canvas.width * this.prop.pageSize.inner.ratio); | ||
var nPages = Math.ceil(pxFullHeight / pxPageHeight); | ||
// Define pageHeight separately so it can be trimmed on the final page. | ||
var pageHeight = this.prop.pageSize.inner.height; | ||
// Define pageHeight separately so it can be trimmed on the final page. | ||
var pageHeight = this.prop.pageSize.inner.height; | ||
// Create a one-page canvas to split up the full image. | ||
var pageCanvas = document.createElement('canvas'); | ||
var pageCtx = pageCanvas.getContext('2d'); | ||
pageCanvas.width = canvas.width; | ||
pageCanvas.height = pxPageHeight; | ||
// Create a one-page canvas to split up the full image. | ||
var pageCanvas = document.createElement('canvas'); | ||
var pageCtx = pageCanvas.getContext('2d'); | ||
pageCanvas.width = canvas.width; | ||
pageCanvas.height = pxPageHeight; | ||
// Initialize the PDF. | ||
this.prop.pdf = this.prop.pdf || new jsPDF(opt.jsPDF); | ||
// Initialize the PDF. | ||
this.prop.pdf = this.prop.pdf || new jsPDF(opt.jsPDF); | ||
for (var page=0; page<nPages; page++) { | ||
// Trim the final page to reduce file size. | ||
if (page === nPages-1 && pxFullHeight % pxPageHeight !== 0) { | ||
pageCanvas.height = pxFullHeight % pxPageHeight; | ||
pageHeight = pageCanvas.height * this.prop.pageSize.inner.width / pageCanvas.width; | ||
} | ||
for (var page = 0; page < nPages; page++) { | ||
// Trim the final page to reduce file size. | ||
if (page === nPages - 1 && pxFullHeight % pxPageHeight !== 0) { | ||
pageCanvas.height = pxFullHeight % pxPageHeight; | ||
pageHeight = pageCanvas.height * this.prop.pageSize.inner.width / pageCanvas.width; | ||
} | ||
// Display the page. | ||
var w = pageCanvas.width; | ||
var h = pageCanvas.height; | ||
pageCtx.fillStyle = 'white'; | ||
pageCtx.fillRect(0, 0, w, h); | ||
pageCtx.drawImage(canvas, 0, page*pxPageHeight, w, h, 0, 0, w, h); | ||
// Display the page. | ||
var w = pageCanvas.width; | ||
var h = pageCanvas.height; | ||
pageCtx.fillStyle = 'white'; | ||
pageCtx.fillRect(0, 0, w, h); | ||
pageCtx.drawImage(canvas, 0, page * pxPageHeight, w, h, 0, 0, w, h); | ||
// Add the page to the PDF. | ||
if (page) this.prop.pdf.addPage(); | ||
var imgData = pageCanvas.toDataURL('image/' + opt.image.type, opt.image.quality); | ||
this.prop.pdf.addImage(imgData, opt.image.type, opt.margin[1], opt.margin[0], | ||
this.prop.pageSize.inner.width, pageHeight); | ||
} | ||
}); | ||
// Add the page to the PDF. | ||
if (page) this.prop.pdf.addPage(); | ||
var imgData = pageCanvas.toDataURL('image/' + opt.image.type, opt.image.quality); | ||
this.prop.pdf.addImage(imgData, opt.image.type, opt.margin[1], opt.margin[0], | ||
this.prop.pageSize.inner.width, pageHeight); | ||
} | ||
}); | ||
}; | ||
@@ -227,64 +247,70 @@ | ||
Worker.prototype.output = function output(type, options, src) { | ||
// Redirect requests to the correct function (outputPdf / outputImg). | ||
src = src || 'pdf'; | ||
if (src.toLowerCase() === 'img' || src.toLowerCase() === 'image') { | ||
return this.outputImg(type, options); | ||
} else { | ||
return this.outputPdf(type, options); | ||
} | ||
// Redirect requests to the correct function (outputPdf / outputImg). | ||
src = src || 'pdf'; | ||
if (src.toLowerCase() === 'img' || src.toLowerCase() === 'image') { | ||
return this.outputImg(type, options); | ||
} else { | ||
return this.outputPdf(type, options); | ||
} | ||
}; | ||
Worker.prototype.outputPdf = function outputPdf(type, options) { | ||
// Set up function prerequisites. | ||
var prereqs = [ | ||
function checkPdf() { return this.prop.pdf || this.toPdf(); } | ||
]; | ||
// Set up function prerequisites. | ||
var prereqs = [ | ||
function checkPdf() { | ||
return this.prop.pdf || this.toPdf(); | ||
} | ||
]; | ||
// Fulfill prereqs then perform the appropriate output. | ||
return this.thenList(prereqs).then(function outputPdf_main() { | ||
/* Currently implemented output types: | ||
* https://rawgit.com/MrRio/jsPDF/master/docs/jspdf.js.html#line992 | ||
* save(options), arraybuffer, blob, bloburi/bloburl, | ||
* datauristring/dataurlstring, dataurlnewwindow, datauri/dataurl | ||
*/ | ||
return this.prop.pdf.output(type, options); | ||
}); | ||
// Fulfill prereqs then perform the appropriate output. | ||
return this.thenList(prereqs).then(function outputPdf_main() { | ||
/* Currently implemented output types: | ||
* https://rawgit.com/MrRio/jsPDF/master/docs/jspdf.js.html#line992 | ||
* save(options), arraybuffer, blob, bloburi/bloburl, | ||
* datauristring/dataurlstring, dataurlnewwindow, datauri/dataurl | ||
*/ | ||
return this.prop.pdf.output(type, options); | ||
}); | ||
}; | ||
Worker.prototype.outputImg = function outputImg(type, options) { | ||
// Set up function prerequisites. | ||
var prereqs = [ | ||
function checkImg() { return this.prop.img || this.toImg(); } | ||
]; | ||
// Set up function prerequisites. | ||
var prereqs = [ | ||
function checkImg() { | ||
return this.prop.img || this.toImg(); | ||
} | ||
]; | ||
// Fulfill prereqs then perform the appropriate output. | ||
return this.thenList(prereqs).then(function outputImg_main() { | ||
switch (type) { | ||
case undefined: | ||
case 'img': | ||
return this.prop.img; | ||
case 'datauristring': | ||
case 'dataurlstring': | ||
return this.prop.img.src; | ||
case 'datauri': | ||
case 'dataurl': | ||
return document.location.href = this.prop.img.src; | ||
default: | ||
throw 'Image output type "' + type + '" is not supported.'; | ||
} | ||
}); | ||
// Fulfill prereqs then perform the appropriate output. | ||
return this.thenList(prereqs).then(function outputImg_main() { | ||
switch (type) { | ||
case undefined: | ||
case 'img': | ||
return this.prop.img; | ||
case 'datauristring': | ||
case 'dataurlstring': | ||
return this.prop.img.src; | ||
case 'datauri': | ||
case 'dataurl': | ||
return document.location.href = this.prop.img.src; | ||
default: | ||
throw 'Image output type "' + type + '" is not supported.'; | ||
} | ||
}); | ||
}; | ||
Worker.prototype.save = function save(filename) { | ||
// Set up function prerequisites. | ||
var prereqs = [ | ||
function checkPdf() { return this.prop.pdf || this.toPdf(); } | ||
]; | ||
// Set up function prerequisites. | ||
var prereqs = [ | ||
function checkPdf() { | ||
return this.prop.pdf || this.toPdf(); | ||
} | ||
]; | ||
// Fulfill prereqs, update the filename (if provided), and save the PDF. | ||
return this.thenList(prereqs).set( | ||
filename ? { filename: filename } : null | ||
).then(function save_main() { | ||
this.prop.pdf.save(this.opt.filename); | ||
}); | ||
// Fulfill prereqs, update the filename (if provided), and save the PDF. | ||
return this.thenList(prereqs).set( | ||
filename ? {filename: filename} : null | ||
).then(function save_main() { | ||
this.prop.pdf.save(this.opt.filename); | ||
}); | ||
}; | ||
@@ -295,108 +321,115 @@ | ||
Worker.prototype.set = function set(opt) { | ||
// TODO: Implement ordered pairs? | ||
// TODO: Implement ordered pairs? | ||
// Silently ignore invalid or empty input. | ||
if (objType(opt) !== 'object') { | ||
return this; | ||
} | ||
// Silently ignore invalid or empty input. | ||
if (objType(opt) !== 'object') { | ||
return this; | ||
} | ||
// Build an array of setter functions to queue. | ||
var fns = Object.keys(opt || {}).map(function (key) { | ||
switch (key) { | ||
case 'margin': | ||
return this.setMargin.bind(this, opt.margin); | ||
case 'jsPDF': | ||
return function set_jsPDF() { this.opt.jsPDF = opt.jsPDF; return this.setPageSize(); } | ||
case 'pageSize': | ||
return this.setPageSize.bind(this, opt.pageSize); | ||
default: | ||
if (key in Worker.template.prop) { | ||
// Set pre-defined properties in prop. | ||
return function set_prop() { this.prop[key] = opt[key]; } | ||
} else { | ||
// Set any other properties in opt. | ||
return function set_opt() { this.opt[key] = opt[key] }; | ||
// Build an array of setter functions to queue. | ||
var fns = Object.keys(opt || {}).map(function (key) { | ||
switch (key) { | ||
case 'margin': | ||
return this.setMargin.bind(this, opt.margin); | ||
case 'jsPDF': | ||
return function set_jsPDF() { | ||
this.opt.jsPDF = opt.jsPDF; | ||
return this.setPageSize(); | ||
} | ||
case 'pageSize': | ||
return this.setPageSize.bind(this, opt.pageSize); | ||
default: | ||
if (key in Worker.template.prop) { | ||
// Set pre-defined properties in prop. | ||
return function set_prop() { | ||
this.prop[key] = opt[key]; | ||
} | ||
} else { | ||
// Set any other properties in opt. | ||
return function set_opt() { | ||
this.opt[key] = opt[key] | ||
}; | ||
} | ||
} | ||
} | ||
}, this); | ||
}, this); | ||
// Set properties within the promise chain. | ||
return this.then(function set_main() { | ||
return this.thenList(fns); | ||
}); | ||
// Set properties within the promise chain. | ||
return this.then(function set_main() { | ||
return this.thenList(fns); | ||
}); | ||
}; | ||
Worker.prototype.get = function get(key, cbk) { | ||
return this.then(function get_main() { | ||
// Fetch the requested property, either as a predefined prop or in opt. | ||
var val = (key in Worker.template.prop) ? this.prop[key] : this.opt[key]; | ||
return cbk ? cbk(val) : val; | ||
}); | ||
return this.then(function get_main() { | ||
// Fetch the requested property, either as a predefined prop or in opt. | ||
var val = (key in Worker.template.prop) ? this.prop[key] : this.opt[key]; | ||
return cbk ? cbk(val) : val; | ||
}); | ||
}; | ||
Worker.prototype.setMargin = function setMargin(margin) { | ||
return this.then(function setMargin_main() { | ||
// Parse the margin property: [top, left, bottom, right]. | ||
switch (objType(margin)) { | ||
case 'number': | ||
margin = [margin, margin, margin, margin]; | ||
case 'array': | ||
if (margin.length === 2) { | ||
margin = [margin[0], margin[1], margin[0], margin[1]]; | ||
return this.then(function setMargin_main() { | ||
// Parse the margin property: [top, left, bottom, right]. | ||
switch (objType(margin)) { | ||
case 'number': | ||
margin = [margin, margin, margin, margin]; | ||
case 'array': | ||
if (margin.length === 2) { | ||
margin = [margin[0], margin[1], margin[0], margin[1]]; | ||
} | ||
if (margin.length === 4) { | ||
break; | ||
} | ||
default: | ||
return this.error('Invalid margin array.'); | ||
} | ||
if (margin.length === 4) { | ||
break; | ||
} | ||
default: | ||
return this.error('Invalid margin array.'); | ||
} | ||
// Set the margin property, then update pageSize. | ||
this.opt.margin = margin; | ||
}).then(this.setPageSize); | ||
// Set the margin property, then update pageSize. | ||
this.opt.margin = margin; | ||
}).then(this.setPageSize); | ||
} | ||
Worker.prototype.setPageSize = function setPageSize(pageSize) { | ||
return this.then(function setPageSize_main() { | ||
// Retrieve page-size based on jsPDF settings, if not explicitly provided. | ||
pageSize = pageSize || jsPDF.getPageSize(this.opt.jsPDF); | ||
return this.then(function setPageSize_main() { | ||
// Retrieve page-size based on jsPDF settings, if not explicitly provided. | ||
pageSize = pageSize || jsPDF.getPageSize(this.opt.jsPDF); | ||
// Add 'inner' field if not present. | ||
if (!pageSize.hasOwnProperty('inner')) { | ||
pageSize.inner = { | ||
width: pageSize.width - this.opt.margin[1] - this.opt.margin[3], | ||
height: pageSize.height - this.opt.margin[0] - this.opt.margin[2] | ||
}; | ||
pageSize.inner.px = { | ||
width: toPx(pageSize.inner.width, pageSize.k), | ||
height: toPx(pageSize.inner.height, pageSize.k) | ||
}; | ||
pageSize.inner.ratio = pageSize.inner.height / pageSize.inner.width; | ||
} | ||
// Add 'inner' field if not present. | ||
if (!pageSize.hasOwnProperty('inner')) { | ||
pageSize.inner = { | ||
width: pageSize.width - this.opt.margin[1] - this.opt.margin[3], | ||
height: pageSize.height - this.opt.margin[0] - this.opt.margin[2] | ||
}; | ||
pageSize.inner.px = { | ||
width: toPx(pageSize.inner.width, pageSize.k), | ||
height: toPx(pageSize.inner.height, pageSize.k) | ||
}; | ||
pageSize.inner.ratio = pageSize.inner.height / pageSize.inner.width; | ||
} | ||
// Attach pageSize to this. | ||
this.prop.pageSize = pageSize; | ||
}); | ||
// Attach pageSize to this. | ||
this.prop.pageSize = pageSize; | ||
}); | ||
} | ||
Worker.prototype.setProgress = function setProgress(val, state, n, stack) { | ||
// Immediately update all progress values. | ||
if (val != null) this.progress.val = val; | ||
if (state != null) this.progress.state = state; | ||
if (n != null) this.progress.n = n; | ||
if (stack != null) this.progress.stack = stack; | ||
this.progress.ratio = this.progress.val / this.progress.state; | ||
// Immediately update all progress values. | ||
if (val != null) this.progress.val = val; | ||
if (state != null) this.progress.state = state; | ||
if (n != null) this.progress.n = n; | ||
if (stack != null) this.progress.stack = stack; | ||
this.progress.ratio = this.progress.val / this.progress.state; | ||
// Return this for command chaining. | ||
return this; | ||
// Return this for command chaining. | ||
return this; | ||
}; | ||
Worker.prototype.updateProgress = function updateProgress(val, state, n, stack) { | ||
// Immediately update all progress values, using setProgress. | ||
return this.setProgress( | ||
val ? this.progress.val + val : null, | ||
state ? state : null, | ||
n ? this.progress.n + n : null, | ||
stack ? this.progress.stack.concat(stack) : null | ||
); | ||
// Immediately update all progress values, using setProgress. | ||
return this.setProgress( | ||
val ? this.progress.val + val : null, | ||
state ? state : null, | ||
n ? this.progress.n + n : null, | ||
stack ? this.progress.stack.concat(stack) : null | ||
); | ||
}; | ||
@@ -407,67 +440,73 @@ | ||
Worker.prototype.then = function then(onFulfilled, onRejected) { | ||
// Wrap `this` for encapsulation. | ||
var self = this; | ||
// Wrap `this` for encapsulation. | ||
var self = this; | ||
return this.thenCore(onFulfilled, onRejected, function then_main(onFulfilled, onRejected) { | ||
// Update progress while queuing, calling, and resolving `then`. | ||
self.updateProgress(null, null, 1, [onFulfilled]); | ||
return Promise.prototype.then.call(this, function then_pre(val) { | ||
self.updateProgress(null, onFulfilled); | ||
return val; | ||
}).then(onFulfilled, onRejected).then(function then_post(val) { | ||
self.updateProgress(1); | ||
return val; | ||
return this.thenCore(onFulfilled, onRejected, function then_main(onFulfilled, onRejected) { | ||
// Update progress while queuing, calling, and resolving `then`. | ||
self.updateProgress(null, null, 1, [onFulfilled]); | ||
return Promise.prototype.then.call(this, function then_pre(val) { | ||
self.updateProgress(null, onFulfilled); | ||
return val; | ||
}).then(onFulfilled, onRejected).then(function then_post(val) { | ||
self.updateProgress(1); | ||
return val; | ||
}); | ||
}); | ||
}); | ||
}; | ||
Worker.prototype.thenCore = function thenCore(onFulfilled, onRejected, thenBase) { | ||
// Handle optional thenBase parameter. | ||
thenBase = thenBase || Promise.prototype.then; | ||
// Handle optional thenBase parameter. | ||
thenBase = thenBase || Promise.prototype.then; | ||
// Wrap `this` for encapsulation and bind it to the promise handlers. | ||
var self = this; | ||
if (onFulfilled) { onFulfilled = onFulfilled.bind(self); } | ||
if (onRejected) { onRejected = onRejected.bind(self); } | ||
// Wrap `this` for encapsulation and bind it to the promise handlers. | ||
var self = this; | ||
if (onFulfilled) { | ||
onFulfilled = onFulfilled.bind(self); | ||
} | ||
if (onRejected) { | ||
onRejected = onRejected.bind(self); | ||
} | ||
// Cast self into a Promise to avoid polyfills recursively defining `then`. | ||
var isNative = Promise.toString().indexOf('[native code]') !== -1 && Promise.name === 'Promise'; | ||
var selfPromise = isNative ? self : Worker.convert(Object.assign({}, self), Promise.prototype); | ||
// Cast self into a Promise to avoid polyfills recursively defining `then`. | ||
var isNative = Promise.toString().indexOf('[native code]') !== -1 && Promise.name === 'Promise'; | ||
var selfPromise = isNative ? self : Worker.convert(Object.assign({}, self), Promise.prototype); | ||
// Return the promise, after casting it into a Worker and preserving props. | ||
var returnVal = thenBase.call(selfPromise, onFulfilled, onRejected); | ||
return Worker.convert(returnVal, self.__proto__); | ||
// Return the promise, after casting it into a Worker and preserving props. | ||
var returnVal = thenBase.call(selfPromise, onFulfilled, onRejected); | ||
return Worker.convert(returnVal, self.__proto__); | ||
}; | ||
Worker.prototype.thenExternal = function thenExternal(onFulfilled, onRejected) { | ||
// Call `then` and return a standard promise (exits the Worker chain). | ||
return Promise.prototype.then.call(this, onFulfilled, onRejected); | ||
// Call `then` and return a standard promise (exits the Worker chain). | ||
return Promise.prototype.then.call(this, onFulfilled, onRejected); | ||
}; | ||
Worker.prototype.thenList = function thenList(fns) { | ||
// Queue a series of promise 'factories' into the promise chain. | ||
var self = this; | ||
fns.forEach(function thenList_forEach(fn) { | ||
self = self.thenCore(fn); | ||
}); | ||
return self; | ||
// Queue a series of promise 'factories' into the promise chain. | ||
var self = this; | ||
fns.forEach(function thenList_forEach(fn) { | ||
self = self.thenCore(fn); | ||
}); | ||
return self; | ||
}; | ||
Worker.prototype['catch'] = function (onRejected) { | ||
// Bind `this` to the promise handler, call `catch`, and return a Worker. | ||
if (onRejected) { onRejected = onRejected.bind(this); } | ||
var returnVal = Promise.prototype['catch'].call(this, onRejected); | ||
return Worker.convert(returnVal, this); | ||
// Bind `this` to the promise handler, call `catch`, and return a Worker. | ||
if (onRejected) { | ||
onRejected = onRejected.bind(this); | ||
} | ||
var returnVal = Promise.prototype['catch'].call(this, onRejected); | ||
return Worker.convert(returnVal, this); | ||
}; | ||
Worker.prototype.catchExternal = function catchExternal(onRejected) { | ||
// Call `catch` and return a standard promise (exits the Worker chain). | ||
return Promise.prototype['catch'].call(this, onRejected); | ||
// Call `catch` and return a standard promise (exits the Worker chain). | ||
return Promise.prototype['catch'].call(this, onRejected); | ||
}; | ||
Worker.prototype.error = function error(msg) { | ||
// Throw the error in the Promise chain. | ||
return this.then(function error_main() { | ||
throw new Error(msg); | ||
}); | ||
// Throw the error in the Promise chain. | ||
return this.then(function error_main() { | ||
throw new Error(msg); | ||
}); | ||
}; | ||
@@ -474,0 +513,0 @@ |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Unidentified License
License(Experimental) Something that seems like a license was found, but its contents could not be matched with a known license.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Unidentified License
License(Experimental) Something that seems like a license was found, but its contents could not be matched with a known license.
Found 2 instances in 1 package
5671328
0.16%3
-25%36844
0.16%9
-10%