New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

html3pdf

Package Overview
Dependencies
Maintainers
1
Versions
3
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

html3pdf - npm Package Compare versions

Comparing version 0.11.1 to 0.12.1

3

package.json
{
"name": "html3pdf",
"version": "0.11.1",
"version": "0.12.1",
"description": "html3pdf, based on html2pdf by ekoopmans",

@@ -32,2 +32,3 @@ "main": "dist/html3pdf.js",

"commander": "^11.0.0",
"eslint": "^8.46.0",
"jsdoc": "^4.0.2",

@@ -34,0 +35,0 @@ "karma": "^6.4.2",

@@ -8,4 +8,9 @@ # html3pdf.js

Breaking changes:
- Dropped support for environments that don't have native Promises.
- Progress-tracking API implemented.
- Previously if you called the `.toPdf()` method of the html2pdf.js object, without explicitly calling `.toCanvas()` or `.toImg()`, the default worker object would have the properties `.prop.canvas` and `.prop.img`, which would be an HTMLCanvasElement and an HTMLImageElement respectively. Now the default types are Array\<HTMLCanvasElement> and Array\<HTMLImageElement>. If you do not want them to be arrays, you can explicitly call `.toCanvas()` and `.toImg()`. See [Workflow](#workflow) below for more details. This is a niche use case and if you don't know what any of this means, you probably don't need to worry about it.
Features/Bug fixes:
- [Progress tracking](#progress-tracking) API implemented.
- `.toCanvases()` and `.toImgs()` added to the worker API as alternatives to the original `.toCanvas()` and `.toImg()` methods. These are now the default if you simply call `.toPdf()` without being explicit about the rest of the chain. These functions create one canvas per page, and the corresponding images for each canvas. This resolves the issues with canvas size limits (unless each page is enormous).
- PRs from the original library merged: 170, 260, 261, 340, 447, 503, 516, 531, 569, 635, 641. These include: some fixes to the pagebreak calculations, the option to change what elements are used for pagebreaks and assign a class to those elements, the addition of the jsPDF documentProperties to the api, and a fix for the bug where long documents are blank - by rendering one canvas per page.

@@ -52,3 +57,3 @@

*Note: [Read about dependences](#dependencies) for more information about using the unbundled version `dist/html2canvas.min.js`.*
*Note: [Read about dependencies](#dependencies) for more information about using the unbundled version `dist/html2canvas.min.js`.*

@@ -78,3 +83,3 @@ #### Raw JS

function addScript(url) {
var script = document.createElement('script');
const script = document.createElement('script');
script.type = 'application/javascript';

@@ -93,3 +98,3 @@ script.src = url;

```js
var element = document.getElementById('element-to-print');
const element = document.getElementById('element-to-print');
html2pdf(element);

@@ -103,3 +108,3 @@ ```

```js
var worker = html2pdf(); // Or: var worker = new html2pdf.Worker;
const worker = html2pdf(); // Or: const worker = new html2pdf.Worker;
```

@@ -110,4 +115,4 @@

```js
// This will implicitly create the canvas and PDF objects before saving.
var worker = html2pdf().from(element).save();
// This will implicitly create the canvases and PDF objects before saving.
const worker = html2pdf().from(element).save();
```

@@ -120,5 +125,15 @@

```
// The default in html3pdf.js
.from() -> .toContainer() -> .toCanvases() -> .toImgs() -> .toPdf() -> .save()
// The default in html2pdf.js
.from() -> .toContainer() -> .toCanvas() -> .toImg() -> .toPdf() -> .save()
// You can also call .toImgs() after calling toCanvas(), it will just have an array with one image.
.from() -> .toContainer() -> .toCanvas() -> .toImgs() -> .toPdf() -> .save()
// But you can't call `.toImg` if you previously called `.toCanvases` on the same worker, `.toImg` does not want to assemble a single image out of multiple canvases. If you need this for some reason, feel free to submit a PR!
```
#### Worker API

@@ -129,3 +144,3 @@

| from | src, type | Sets the source (HTML string or element) for the PDF. Optional `type` specifies other sources: `'string'`, `'element'`, `'canvas'`, or `'img'`. |
| to | target | Converts the source to the specified target (`'container'`, `'canvas'`, `'img'`, or `'pdf'`). Each target also has its own `toX` method that can be called directly: `toContainer()`, `toCanvas()`, `toImg()`, and `toPdf()`. |
| to | target | Converts the source to the specified target (`'container'`, `'canvas'`, `'canvases'`, `'img'`, `'imgs'`, or `'pdf'`). Each target also has its own `toX` method that can be called directly: `toContainer()`, `toCanvas()`, `toCanvases()`, `toImg()`, `toImgs()`, and `toPdf()`. |
| output | type, options, src | Routes to the appropriate `outputPdf` or `outputImg` method based on specified `src` (`'pdf'` (default) or `'img'`). |

@@ -160,6 +175,6 @@ | outputPdf | type, options | Sends `type` and `options` to the jsPDF object's `output` method, and returns the result as a Promise (use `.then` to access). See the [jsPDF documentation](https://rawgit.com/MrRio/jsPDF/master/docs/jsPDF.html#output) for more info. |

```js
var element = document.getElementById('element-to-print');
var opt = {
const element = document.getElementById('element-to-print');
const opt = {
margin: 1,
filename: 'myfile.pdf',
filename: 'myFile.pdf',
image: { type: 'jpeg', quality: 0.98 },

@@ -186,3 +201,3 @@ html2canvas: { scale: 2 },

|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. |
|enableLinks |boolean |`true` |If enabled, PDF hyperlinks are automatically added on top of all anchor tags. |
|html2canvas |object |`{ }` |Configuration options sent directly to `html2canvas` ([see here](https://html2canvas.hertzen.com/configuration) for usage).|

@@ -189,0 +204,0 @@ |jsPDF |object |`{ }` |Configuration options sent directly to `jsPDF` ([see here](http://rawgit.com/MrRio/jsPDF/master/docs/jsPDF.html) for usage).|

import Worker from './worker.js';
import './plugin/jspdf-plugin.js';
import './plugin/pagebreaks.js';
import './plugin/ios-pdf-fix.js';
import './plugin/hyperlinks.js';

@@ -16,5 +15,5 @@

*/
var html2pdf = function html2pdf(src, opt) {
const html2pdf = function html2pdf(src, opt) {
// Create a new worker with the given options.
var worker = new html2pdf.Worker(opt);
const worker = new html2pdf.Worker(opt);

@@ -21,0 +20,0 @@ if (src) {

@@ -7,4 +7,4 @@ import Worker from '../worker.js';

// Main link array, and refs to original functions.
var linkInfo = [];
var orig = {
let linkInfo = [];
const orig = {
toContainer: Worker.prototype.toContainer,

@@ -19,5 +19,5 @@ toPdf: Worker.prototype.toPdf,

// 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);
const container = this.prop.container;
const links = container.querySelectorAll('a');
const containerRect = unitConvert(container.getBoundingClientRect(), this.prop.pageSize.k);
linkInfo = [];

@@ -28,11 +28,11 @@

// 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);
const clientRects = link.getClientRects();
for (let i=0; i<clientRects.length; i++) {
const 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;
const page = Math.floor(clientRect.top / this.prop.pageSize.inner.height) + 1;
const top = this.opt.margin[0] + clientRect.top % this.prop.pageSize.inner.height;
const left = this.opt.margin[1] + clientRect.left;

@@ -53,8 +53,7 @@ linkInfo.push({ page, top, left, clientRect, link });

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.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();
const nPages = this.prop.pdf.internal.getNumberOfPages();
this.prop.pdf.setPage(nPages);

@@ -61,0 +60,0 @@ }

// Import dependencies.
import { jsPDF } from 'jspdf';
// Get dimensions of a PDF page, as determined by jsPDF.
/**
* Get dimensions of a PDF page, as determined by jsPDF.
* @param {string} orientation - Page orientation, either 'p' or 'l'.
* @param {string} unit - Measurement unit to use, either 'pt', 'mm', 'cm', 'in'.
* @param {string|array} format - Page format, either 'a0'-'a10', 'b0'-'b10', 'c0'-'c10', 'dl', 'letter', 'government-letter', 'legal', 'junior-legal', 'ledger', 'tabloid', 'credit-card' or an array containing the width and height in points (1/72nd of an inch).
* @returns {{width: number, height: number, unit: string, k: number}} Page dimensions.
*/
jsPDF.getPageSize = function(orientation, unit, format) {
// Decode options object
if (typeof orientation === 'object') {
var options = orientation;
const options = orientation;
orientation = options.orientation;

@@ -18,6 +24,6 @@ unit = options.unit || unit;

orientation = ('' + (orientation || 'P')).toLowerCase();
var format_as_string = ('' + format).toLowerCase();
const format_as_string = ('' + format).toLowerCase();
// Size in pt of various paper formats
var pageFormats = {
const pageFormats = {
'a0' : [2383.94, 3370.39], 'a1' : [1683.78, 2383.94],

@@ -50,11 +56,12 @@ 'a2' : [1190.55, 1683.78], 'a3' : [ 841.89, 1190.55],

// Unit conversion
let k;
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;
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:

@@ -65,9 +72,11 @@ 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;
let pageHeight;
let pageWidth
if (Object.prototype.hasOwnProperty.call(pageFormats, format_as_string)) {
pageHeight = pageFormats[format_as_string][1] / k;
pageWidth = pageFormats[format_as_string][0] / k;
} else {
try {
var pageHeight = format[1];
var pageWidth = format[0];
pageHeight = format[1];
pageWidth = format[0];
} catch (err) {

@@ -82,3 +91,3 @@ throw new Error('Invalid format: ' + format);

if (pageWidth > pageHeight) {
var tmp = pageWidth;
const tmp = pageWidth;
pageWidth = pageHeight;

@@ -90,3 +99,3 @@ pageHeight = tmp;

if (pageHeight > pageWidth) {
var tmp = pageWidth;
const tmp = pageWidth;
pageWidth = pageHeight;

@@ -100,3 +109,3 @@ pageHeight = tmp;

// Return information (k is the unit conversion ratio from pts)
var info = { 'width': pageWidth, 'height': pageHeight, 'unit': unit, 'k': k };
const info = { 'width': pageWidth, 'height': pageHeight, 'unit': unit, 'k': k };
return info;

@@ -103,0 +112,0 @@ };

@@ -27,3 +27,3 @@ import Worker from '../worker.js';

// Refs to original functions.
var orig = {
const orig = {
toContainer: Worker.prototype.toContainer

@@ -43,98 +43,98 @@ };

Worker.prototype.toContainer = function toContainer() {
var prereqs = [
const prereqs = [
function waitLoadImages() {
var root = this.prop.container;
var images = root.querySelectorAll("img");
const root = this.prop.container;
const images = root.querySelectorAll('img');
return loadImages(images);
}
];
];
return orig.toContainer.call(this)
.thenList(prereqs)
.then(function toContainer_pagebreak() {
.thenList(prereqs)
.then(function toContainer_pagebreak() {
// Setup root element and inner page height.
var root = this.prop.container;
var pxPageHeight = this.opt['html2canvas'] && this.opt['html2canvas']['width'] ?
this.opt['html2canvas']['width'] * this.prop.pageSize.inner.ratio :
this.prop.pageSize.inner.px.heightExact; // avoid rounding errors by using the exact height in px
const root = this.prop.container;
const pxPageHeight = this.opt['html2canvas'] && this.opt['html2canvas']['width'] ?
this.opt['html2canvas']['width'] * this.prop.pageSize.inner.ratio :
this.prop.pageSize.inner.px.heightExact; // avoid rounding errors by using the exact height in px
// 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.
const modeSrc = [].concat(this.opt.pagebreak.mode);
const 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.
const select = {};
const self = this;
['before', 'after', 'avoid'].forEach(function(key) {
const 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.
let 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) {
// Loop through all elements.
const 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
};
let rules = {
before: false,
after: mode.legacy && legacyEls.indexOf(el) !== -1,
avoid: mode.avoidAll
};
// Add rules for css mode.
if (mode.css) {
// 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
};
}
const style = window.getComputedStyle(el);
// TODO: Handle 'left' and 'right' correctly.
// TODO: Add support for 'avoid' on breakBefore/After.
const breakOpt = ['always', 'page', 'left', 'right'];
const 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?
const 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) {
const startPage = Math.floor(clientRect.top / pxPageHeight);
const endPage = Math.floor(clientRect.bottom / pxPageHeight);
const 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 height = clientRect.top >= 0
? Math.floor(pxPageHeight - (clientRect.top % pxPageHeight))
: Math.abs(clientRect.top);
// We allow for creating any type of element in place of the default div
// e.g. 'tr' is useful for tables, and there are other cases where a 'div' will ruin the styling
var pad = createElement(self.opt.pagebreak.elementType, {style:
// Before: Create a padding div to push the element to the next page.
if (rules.before) {
const height = clientRect.top >= 0
? Math.floor(pxPageHeight - (clientRect.top % pxPageHeight))
: Math.abs(clientRect.top);
// We allow for creating any type of element in place of the default div
// e.g. 'tr' is useful for tables, and there are other cases where a 'div' will ruin the styling
const pad = createElement(self.opt.pagebreak.elementType, {style:
{

@@ -146,17 +146,17 @@ display: 'block',

className: self.opt.pagebreak.className // allow control of styling of added sections
});
el.parentNode.insertBefore(pad, el);
}
});
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',
width: '100%',
height: Math.floor(pxPageHeight - (clientRect.bottom % pxPageHeight)) + 'px'
}});
el.parentNode.insertBefore(pad, el.nextSibling);
}
// After: Create a padding div to fill the remaining page.
if (rules.after) {
const pad = createElement('div', {style: {
display: 'block',
width: '100%',
height: Math.floor(pxPageHeight - (clientRect.bottom % pxPageHeight)) + 'px'
}});
el.parentNode.insertBefore(pad, el.nextSibling);
}
});
});
});
};

@@ -7,3 +7,3 @@ /**

export const objType = function objType(obj) {
var type = typeof obj;
const type = typeof obj;
if (type === 'undefined') return 'undefined';

@@ -30,12 +30,12 @@ else if (type === 'string' || obj instanceof String) return 'string';

export const createElement = function createElement(tagName, opt) {
var el = document.createElement(tagName);
const 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) {
const scripts = el.getElementsByTagName('script');
for (let i = scripts.length; i-- > 0; null) {
scripts[i].parentNode.removeChild(scripts[i]);
}
}
for (var key in opt.style) {
for (const key in opt.style) {
el.style[key] = opt.style[key];

@@ -55,4 +55,4 @@ }

// 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) {
const clone = node.nodeType === 3 ? document.createTextNode(node.nodeValue) : node.cloneNode(false);
for (let child = node.firstChild; child; child = child.nextSibling) {
if (javascriptEnabled === true || child.nodeType !== 1 || child.nodeName !== 'SCRIPT') {

@@ -95,4 +95,4 @@ clone.appendChild(cloneNode(child, javascriptEnabled));

} else {
var newObj = {};
for (var key in obj) {
const newObj = {};
for (const key in obj) {
newObj[key] = obj[key] * 72 / 96 / k;

@@ -116,31 +116,31 @@ }

// make sure all images finish loading (even though some of them failed to load) then fire the callback
/**
* make sure all images finish loading (even if some of them failed to load) then fire the callback
* @param {Array<HTMLImageElement>} images
* @returns {Promise<void>} resolves when the images are loaded
*/
export function loadImages(images) {
if (images.length > 0) {
var loadedImages = 0;
var promiseResolve;
if (!images.length)
return
var promise = new Promise(function (resolve) {
promiseResolve = resolve;
});
images.forEach(function wait_images_loading(img) {
var newImg = new Image();
return new Promise(resolve => {
let loadedImages = 0;
images.forEach(function wait_image_loading(img) {
const newImg = new Image();
const onFinishLoading = function () {
loadedImages++;
if (loadedImages === images.length) {
promiseResolve();
resolve();
}
};
newImg.onload = onFinishLoading;
newImg.onerror = onFinishLoading;
var src = img.getAttribute("src");
const src = img.getAttribute('src');
newImg.src = src;
});
return promise;
}
});
}

@@ -22,4 +22,4 @@ import { jsPDF } from 'jspdf';

* @property {HTMLElement} overlay - The overlay element.
* @property {HTMLCanvasElement} canvas - The canvas element.
* @property {HTMLImageElement} img - The image element.
* @property {Array<HTMLCanvasElement>|HTMLCanvasElement} canvas - The canvas element(s).
* @property {Array<HTMLImageElement>|HTMLImageElement} img - The image element(s).
* @property {jsPDF} pdf - The jsPDF object.

@@ -55,7 +55,7 @@ * @property {Array} pageSize - The page size.

*/
var Worker = function Worker(opt) {
const Worker = function Worker(opt) {
// Create the root parent for the proto chain, and the starting Worker.
var root = Object.assign(convert(Promise.resolve()),
JSON.parse(JSON.stringify(Worker.template)));
var self = convert(Promise.resolve(), root);
const root = Object.assign(convert(Promise.resolve()),
JSON.parse(JSON.stringify(Worker.template)));
let self = convert(Promise.resolve(), root);

@@ -74,3 +74,3 @@ // Set progress, optional settings, and return.

// Boilerplate for subclassing Promise.
// Boilerplate to subclass Promise.
Worker.prototype = Object.create(Promise.prototype);

@@ -151,4 +151,4 @@ Worker.prototype.constructor = Worker;

* How the 'to' system works:
* To create the pdf, we create a container element, convert it to a canvas, convert the canvas to an image, and then convert the image to a pdf.
* Therefore, .toContainer, .toCanvas, .toImg, and .toPdf must all be called, in that order.
* To create the pdf, we create a container element, convert it to canvases/a canvas, convert the canvas(es) to image(s), and then convert the image(s) to a pdf.
* Therefore, .toContainer, .toCanvases/.toCanvas, .toImgs/.toImg, and .toPdf must all be called, in that order.
* To create a user-friendly API, we do not require the user to call all four functions.

@@ -159,6 +159,5 @@ * Instead, there is a "prereq" system:

*
* @param {"container"|"canvas"|"img"|"pdf"} target
* @param {"container"|"canvases"|"imgs"|"canvas"|"img"|"pdf"} target
* @returns {worker} returns itself for chaining.
*/
Worker.prototype.to = function to(target) {
*/Worker.prototype.to = function to(target) {
// Route the 'to' request to the appropriate method.

@@ -168,2 +167,6 @@ switch (target) {

return this.toContainer();
case 'canvases':
return this.toCanvases();
case 'imgs':
return this.toImgs();
case 'canvas':

@@ -191,3 +194,3 @@ return this.toCanvas();

// Set up function prerequisites.
var prereqs = [
const prereqs = [
function checkSrc() { return this.prop.src || this.error('Cannot duplicate - no source HTML.'); },

@@ -199,3 +202,3 @@ function checkPageSize() { return this.prop.pageSize || this.setPageSize(); }

// Define the CSS styles for the container and its overlay parent.
var overlayCSS = {
const overlayCSS = {
position: 'fixed', overflow: 'hidden', zIndex: 1000,

@@ -205,3 +208,3 @@ left: 0, right: 0, bottom: 0, top: 0,

};
var containerCSS = {
const containerCSS = {
position: 'absolute', width: this.prop.pageSize.inner.width + this.prop.pageSize.unit,

@@ -216,3 +219,3 @@ left: 0, right: 0, top: 0, height: 'auto', margin: 'auto',

// Create and attach the elements.
var source = cloneNode(this.prop.src, this.opt.html2canvas.javascriptEnabled);
const source = cloneNode(this.prop.src, this.opt.html2canvas.javascriptEnabled);
this.prop.overlay = createElement('div', { className: 'html2pdf__overlay', style: overlayCSS });

@@ -228,3 +231,3 @@ this.prop.container = createElement('div', { className: 'html2pdf__container', style: containerCSS });

/**
* Creates a canvas element from the container element, by calling html2canvas.
* Creates a canvas element from the container element by calling html2canvas.
* removes the overlay div from the body when done.

@@ -235,5 +238,7 @@ * @returns {worker} returns itself for chaining.

// Set up function prerequisites.
var prereqs = [
function checkContainer() { return document.body.contains(this.prop.container) ||
this.toContainer(); }
const prereqs = [
function checkContainer() {
return document.body.contains(this.prop.container) ||
this.toContainer();
}
];

@@ -244,3 +249,3 @@

// Handle old-fashioned 'onrendered' argument.
var options = Object.assign({}, this.opt.html2canvas);
const options = Object.assign({}, this.opt.html2canvas);
delete options.onrendered;

@@ -251,3 +256,3 @@

// Handle old-fashioned 'onrendered' argument.
var onRendered = this.opt.html2canvas.onrendered || function () {};
const onRendered = this.opt.html2canvas.onrendered || function () {};
onRendered(canvas);

@@ -267,3 +272,3 @@

// Set up function prerequisites.
var prereqs = [
const prereqs = [
function checkCanvas() { return this.prop.canvas || this.toCanvas(); }

@@ -274,3 +279,3 @@ ];

return this.thenList(prereqs).then(function toImg_main() {
var imgData = this.prop.canvas.toDataURL('image/' + this.opt.image.type, this.opt.image.quality);
const imgData = this.prop.canvas.toDataURL('image/' + this.opt.image.type, this.opt.image.quality);
this.prop.img = document.createElement('img');

@@ -283,54 +288,103 @@ this.prop.img.src = imgData;

/**
* Creates the pdf by setting this.prop.pdf to a new jsPDF object, splitting the canvas into pages, and adding each page to the pdf using the jspdf addImage function.
* Creates an array of canvas elements (one per page) from the container element, by calling html2canvas.
* removes the overlay div from the body when done.
* @returns {worker} returns itself for chaining.
*/
Worker.prototype.toPdf = function toPdf() {
Worker.prototype.toCanvases = function toCanvases() {
// Set up function prerequisites.
var prereqs = [
function checkCanvas() { return this.prop.canvas || this.toCanvas(); }
const prereqs = [
function checkContainer() { return document.body.contains(this.prop.container) || this.toContainer(); }
];
// Fulfill prereqs then create the canvases.
return this.thenList(prereqs).then(async function toCanvases_main() {
const opt = this.opt;
const root = this.prop.container;
const pxPageWidth = this.prop.pageSize.inner.px.width;
const pxPageHeight = this.prop.pageSize.inner.px.height;
const clientBoundingRect = root.getBoundingClientRect();
const pxFullHeight = clientBoundingRect.height;
const nPages = Math.ceil(pxFullHeight / pxPageHeight);
opt.html2canvas.width = pxPageWidth;
opt.html2canvas.height = pxPageHeight;
opt.html2canvas.windowWidth = pxPageWidth;
opt.html2canvas.windowHeight = pxPageHeight;
if (!this.prop.canvas) {
this.prop.canvas = [];
}
for (let page = 0; page < nPages; page++) {
const options = Object.assign({}, opt.html2canvas);
delete options.onrendered;
options.x = 0;
// Increase the y value to capture only the 'current' page
// -1 to be exclusive to the current page's content
options.y = page * (pxPageHeight - 1);
const canvas = await html2canvas(this.prop.container, options);
this.prop.canvas.push(canvas);
}
document.body.removeChild(this.prop.overlay);
});
};
/**
* Converts the canvases to images by setting the data URLs as the src of new image elements.
* @returns {worker} returns itself for chaining.
*/
Worker.prototype.toImgs = function toImgs() {
// Set up function prerequisites.
const prereqs = [
function checkCanvases() { return this.prop.canvas || this.toCanvases(); }
];
// 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;
return this.thenList(prereqs).then(function toImgs_main() {
if (!this.prop.imgs) {
this.prop.imgs = [];
}
// 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);
// This function should still work even if .toCanvas was run previously instead of .toCanvases
const canvases = this.prop.canvas instanceof Array ? this.prop.canvas : [this.prop.canvas];
// Define pageHeight separately so it can be trimmed on the final page.
var pageHeight = this.prop.pageSize.inner.height;
for (const canvas of canvases) {
const img = document.createElement('img');
const imgData = canvas.toDataURL('image/' + this.opt.image.type, this.opt.image.quality);
img.src = imgData;
this.prop.imgs.push(img);
}
});
};
// 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);
/**
* Creates the pdf by setting this.prop.pdf to a new jsPDF object, and adding each page to the pdf using the jspdf addImage function.
* @returns {worker} returns itself for chaining.
*/
Worker.prototype.toPdf = function toPdf() {
// Set up function prerequisites.
const prereqs = [
function checkImg() { return this.prop.img || this.toImgs(); }
];
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;
}
// Fulfill prereqs then create the image.
return this.thenList(prereqs).then(function toPdf_main() {
// Should work with either a single image or an array of images
const imgs = this.prop.imgs instanceof Array ? this.prop.imgs : [this.prop.img];
// 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);
// Initialize the PDF.
this.prop.pdf = this.prop.pdf || new jsPDF(this.opt.jsPDF);
// 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);
for (const img of imgs) {
this.prop.pdf.addPage();
this.prop.pdf.addImage(img.src, this.opt.image.type, this.opt.margin[1], this.opt.margin[0], this.prop.pageSize.inner.width, this.prop.pageSize.inner.height);
}
});

@@ -368,3 +422,3 @@ };

// Set up function prerequisites.
var prereqs = [
const prereqs = [
function checkPdf() { return this.prop.pdf || this.toPdf(); }

@@ -393,3 +447,3 @@ ];

// Set up function prerequisites.
var prereqs = [
const prereqs = [
function checkImg() { return this.prop.img || this.toImg(); }

@@ -425,7 +479,7 @@ ];

// Set up function prerequisites.
var prereqs = [
const prereqs = [
function checkPdf() { return this.prop.pdf || this.toPdf(); }
];
var setProps = {};
const setProps = {};
if (filename) {

@@ -466,3 +520,3 @@ setProps.filename = filename;

// Build an array of setter functions to queue.
var fns = Object.keys(opt || {}).map(function (key) {
const fns = Object.keys(opt || {}).map(function (key) {
switch (key) {

@@ -502,3 +556,3 @@ case 'margin':

// 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];
const val = (key in Worker.template.prop) ? this.prop[key] : this.opt[key];
return cbk ? cbk(val) : val;

@@ -521,8 +575,11 @@ });

margin = [margin, margin, margin, margin];
break;
case 'array':
if (margin.length === 2) {
margin = [margin[0], margin[1], margin[0], margin[1]];
}
if (margin.length === 4) {
break;
} else if (margin.length === 4) {
break;
} else {
return this.error('Invalid margin array.');
}

@@ -550,3 +607,3 @@ default:

// Add 'inner' field if not present.
if (!pageSize.hasOwnProperty('inner')) {
if (!Object.prototype.hasOwnProperty.call(pageSize, 'inner')) {
pageSize.inner = {

@@ -572,3 +629,3 @@ width: pageSize.width - this.opt.margin[1] - this.opt.margin[3],

/**
* Update the progress properties of the worker - I believe the goal here is to take the entire promise chain, and after each one resolves, we update the progress properties with how far along the chain we are.
* Update the progress properties of the worker - we take the entire promise chain and after each one resolves we update the progress properties with how far along the chain we are.
* @param {number} val current step number

@@ -610,3 +667,3 @@ * @param {*} state ??

// Wrap `this` for encapsulation.
var self = this;
const self = this;

@@ -642,3 +699,3 @@ return this.thenCore(onFulfilled, onRejected, function then_main(onFulfilled, onRejected) {

// Wrap `this` for encapsulation and bind it to the promise handlers.
var self = this;
const self = this;
if (onFulfilled) {

@@ -653,3 +710,3 @@ this.progress.stack.push(onFulfilled.name)

// Return the promise, after casting it into a Worker and preserving props.
var returnVal = thenBase.call(self, onFulfilled, onRejected);
const returnVal = thenBase.call(self, onFulfilled, onRejected);
return convert(returnVal, self.__proto__);

@@ -672,7 +729,7 @@ };

* Queue a series of promise 'factories' into the promise chain.
* @param {Function[]} fns array of functions that may return promises, to add to the promise chain.
* @param {Array<Function>} fns array of functions that may return promises, to add to the promise chain.
* @returns {worker} returns itself for chaining.
*/
Worker.prototype.thenList = function thenList(fns) {
var self = this;
let self = this;
fns.forEach(function thenList_forEach(fn) {

@@ -692,3 +749,3 @@ self = self.thenCore(fn); // Don't need to pass onRejected because errors will be caught by any .catch() in the chain.

if (onRejected) { onRejected = onRejected.bind(this); }
var returnVal = Promise.prototype['catch'].call(this, onRejected);
const returnVal = Promise.prototype['catch'].call(this, onRejected);
return convert(returnVal, this);

@@ -695,0 +752,0 @@ };

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc