Compare commits

...

5 Commits

10 changed files with 142 additions and 66 deletions

View File

@ -2,6 +2,24 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
## [1.4.1](https://github.com/niklasvh/html2canvas/compare/v1.4.0...v1.4.1) (2022-01-22)
### deps
* fix source maps (#2812) ([67c5e8d](https://github.com/niklasvh/html2canvas/commit/67c5e8dec4b2af9260a2b5b75b3399495fd1fee9)), closes [#2812](https://github.com/niklasvh/html2canvas/issues/2812)
### feat
* add support for <video> elements (#2788) ([181d1b1](https://github.com/niklasvh/html2canvas/commit/181d1b1103910d6e1b5277d5c007fc5e3006c6bf)), closes [#2788](https://github.com/niklasvh/html2canvas/issues/2788)
### fix
* Properties x and y of BoundingRect is undefined in old browser (#2797) ([e587a82](https://github.com/niklasvh/html2canvas/commit/e587a82dca01d9ada78cae34fd1bdb934e547f9b)), closes [#2797](https://github.com/niklasvh/html2canvas/issues/2797)
* source maps (#2787) ([46db867](https://github.com/niklasvh/html2canvas/commit/46db86755f064828559a4b0b37310f3ae94f5494)), closes [#2787](https://github.com/niklasvh/html2canvas/issues/2787)
# [1.4.0](https://github.com/niklasvh/html2canvas/compare/v1.3.4...v1.4.0) (2022-01-01) # [1.4.0](https://github.com/niklasvh/html2canvas/compare/v1.3.4...v1.4.0) (2022-01-01)

90
package-lock.json generated
View File

@ -1,15 +1,15 @@
{ {
"name": "html2canvas", "name": "html2canvas",
"version": "1.4.0", "version": "1.4.1",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"version": "1.2.2", "version": "1.4.0",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"css-line-break": "2.0.1", "css-line-break": "^2.1.0",
"text-segmentation": "^1.0.2" "text-segmentation": "^1.0.3"
}, },
"devDependencies": { "devDependencies": {
"@babel/cli": "^7.4.3", "@babel/cli": "^7.4.3",
@ -5705,6 +5705,7 @@
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.1.tgz", "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.1.tgz",
"integrity": "sha512-vFIUq7FdLtjZMhATwDul5RZWv2jpXQ09Pd6jcVEOvIsqCWTRFD/ONHNfyOS8dA/Ippi5dsIgpyKWKZaAKZltbA==", "integrity": "sha512-vFIUq7FdLtjZMhATwDul5RZWv2jpXQ09Pd6jcVEOvIsqCWTRFD/ONHNfyOS8dA/Ippi5dsIgpyKWKZaAKZltbA==",
"dev": true,
"engines": { "engines": {
"node": ">= 0.6.0" "node": ">= 0.6.0"
} }
@ -8465,19 +8466,11 @@
} }
}, },
"node_modules/css-line-break": { "node_modules/css-line-break": {
"version": "2.0.1", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.0.1.tgz", "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz",
"integrity": "sha512-gwKYIMUn7xodIcb346wgUhE2Dt5O1Kmrc16PWi8sL4FTfyDj8P5095rzH7+O8CTZudJr+uw2GCI/hwEkDJFI2w==", "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==",
"dependencies": { "dependencies": {
"base64-arraybuffer": "^0.2.0" "utrie": "^1.0.2"
}
},
"node_modules/css-line-break/node_modules/base64-arraybuffer": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.2.0.tgz",
"integrity": "sha512-7emyCsu1/xiBXgQZrscw/8KPRT44I4Yq9Pe6EGs3aPRTsWuggML1/1DTuZUuIaJPIm1FTDUVXl4x/yW8s0kQDQ==",
"engines": {
"node": ">= 0.6.0"
} }
}, },
"node_modules/cssom": { "node_modules/cssom": {
@ -22757,11 +22750,11 @@
} }
}, },
"node_modules/text-segmentation": { "node_modules/text-segmentation": {
"version": "1.0.2", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.2.tgz", "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz",
"integrity": "sha512-uTqvLxdBrVnx/CFQOtnf8tfzSXFm+1Qxau7Xi54j4OPTZokuDOX8qncQzrg2G8ZicAMOM8TgzFAYTb+AqNO4Cw==", "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==",
"dependencies": { "dependencies": {
"utrie": "^1.0.1" "utrie": "^1.0.2"
} }
}, },
"node_modules/text-table": { "node_modules/text-table": {
@ -23805,11 +23798,19 @@
} }
}, },
"node_modules/utrie": { "node_modules/utrie": {
"version": "1.0.1", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.1.tgz", "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz",
"integrity": "sha512-JPaDXF3vzgZxfeEwutdGzlrNoVFL5UvZcbO6Qo9D4GoahrieUPoMU8GCpVpR7MQqcKhmShIh8VlbEN3PLM3EBg==", "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==",
"dependencies": { "dependencies": {
"base64-arraybuffer": "^1.0.1" "base64-arraybuffer": "^1.0.2"
}
},
"node_modules/utrie/node_modules/base64-arraybuffer": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
"integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==",
"engines": {
"node": ">= 0.6.0"
} }
}, },
"node_modules/uuid": { "node_modules/uuid": {
@ -29686,7 +29687,8 @@
"base64-arraybuffer": { "base64-arraybuffer": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.1.tgz", "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.1.tgz",
"integrity": "sha512-vFIUq7FdLtjZMhATwDul5RZWv2jpXQ09Pd6jcVEOvIsqCWTRFD/ONHNfyOS8dA/Ippi5dsIgpyKWKZaAKZltbA==" "integrity": "sha512-vFIUq7FdLtjZMhATwDul5RZWv2jpXQ09Pd6jcVEOvIsqCWTRFD/ONHNfyOS8dA/Ippi5dsIgpyKWKZaAKZltbA==",
"dev": true
}, },
"base64-js": { "base64-js": {
"version": "1.5.1", "version": "1.5.1",
@ -31950,18 +31952,11 @@
} }
}, },
"css-line-break": { "css-line-break": {
"version": "2.0.1", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.0.1.tgz", "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz",
"integrity": "sha512-gwKYIMUn7xodIcb346wgUhE2Dt5O1Kmrc16PWi8sL4FTfyDj8P5095rzH7+O8CTZudJr+uw2GCI/hwEkDJFI2w==", "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==",
"requires": { "requires": {
"base64-arraybuffer": "^0.2.0" "utrie": "^1.0.2"
},
"dependencies": {
"base64-arraybuffer": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.2.0.tgz",
"integrity": "sha512-7emyCsu1/xiBXgQZrscw/8KPRT44I4Yq9Pe6EGs3aPRTsWuggML1/1DTuZUuIaJPIm1FTDUVXl4x/yW8s0kQDQ=="
}
} }
}, },
"cssom": { "cssom": {
@ -43210,11 +43205,11 @@
"dev": true "dev": true
}, },
"text-segmentation": { "text-segmentation": {
"version": "1.0.2", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.2.tgz", "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz",
"integrity": "sha512-uTqvLxdBrVnx/CFQOtnf8tfzSXFm+1Qxau7Xi54j4OPTZokuDOX8qncQzrg2G8ZicAMOM8TgzFAYTb+AqNO4Cw==", "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==",
"requires": { "requires": {
"utrie": "^1.0.1" "utrie": "^1.0.2"
} }
}, },
"text-table": { "text-table": {
@ -44011,11 +44006,18 @@
"dev": true "dev": true
}, },
"utrie": { "utrie": {
"version": "1.0.1", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.1.tgz", "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz",
"integrity": "sha512-JPaDXF3vzgZxfeEwutdGzlrNoVFL5UvZcbO6Qo9D4GoahrieUPoMU8GCpVpR7MQqcKhmShIh8VlbEN3PLM3EBg==", "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==",
"requires": { "requires": {
"base64-arraybuffer": "^1.0.1" "base64-arraybuffer": "^1.0.2"
},
"dependencies": {
"base64-arraybuffer": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
"integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ=="
}
} }
}, },
"uuid": { "uuid": {

View File

@ -6,7 +6,7 @@
"module": "dist/html2canvas.esm.js", "module": "dist/html2canvas.esm.js",
"typings": "dist/types/index.d.ts", "typings": "dist/types/index.d.ts",
"browser": "dist/html2canvas.js", "browser": "dist/html2canvas.js",
"version": "1.4.0", "version": "1.4.1",
"author": { "author": {
"name": "Niklas von Hertzen", "name": "Niklas von Hertzen",
"email": "niklasvh@gmail.com", "email": "niklasvh@gmail.com",
@ -118,7 +118,7 @@
"homepage": "https://html2canvas.hertzen.com", "homepage": "https://html2canvas.hertzen.com",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"css-line-break": "2.0.1", "css-line-break": "^2.1.0",
"text-segmentation": "^1.0.2" "text-segmentation": "^1.0.3"
} }
} }

View File

@ -30,7 +30,7 @@ export default {
// Allow json resolution // Allow json resolution
json(), json(),
// Compile TypeScript files // Compile TypeScript files
typescript(), typescript({ sourceMap: true, inlineSources: true }),
// Allow bundling cjs modules (unlike webpack, rollup doesn't understand cjs) // Allow bundling cjs modules (unlike webpack, rollup doesn't understand cjs)
commonjs({ commonjs({
include: 'node_modules/**' include: 'node_modules/**'

View File

@ -20,8 +20,8 @@ export class Bounds {
const domRect = Array.from(domRectList).find((rect) => rect.width !== 0); const domRect = Array.from(domRectList).find((rect) => rect.width !== 0);
return domRect return domRect
? new Bounds( ? new Bounds(
domRect.x + context.windowBounds.left, domRect.left + context.windowBounds.left,
domRect.y + context.windowBounds.top, domRect.top + context.windowBounds.top,
domRect.width, domRect.width,
domRect.height domRect.height
) )

View File

@ -13,7 +13,8 @@ import {
isStyleElement, isStyleElement,
isSVGElementNode, isSVGElementNode,
isTextareaElement, isTextareaElement,
isTextNode isTextNode,
isVideoElement
} from './node-parser'; } from './node-parser';
import {isIdentToken, nonFunctionArgSeparator} from '../css/syntax/parser'; import {isIdentToken, nonFunctionArgSeparator} from '../css/syntax/parser';
import {TokenType} from '../css/syntax/tokenizer'; import {TokenType} from '../css/syntax/tokenizer';
@ -145,7 +146,9 @@ export class DocumentCloner {
if (isCanvasElement(node)) { if (isCanvasElement(node)) {
return this.createCanvasClone(node); return this.createCanvasClone(node);
} }
if (isVideoElement(node)) {
return this.createVideoClone(node);
}
if (isStyleElement(node)) { if (isStyleElement(node)) {
return this.createStyleClone(node); return this.createStyleClone(node);
} }
@ -244,6 +247,32 @@ export class DocumentCloner {
return clonedCanvas; return clonedCanvas;
} }
createVideoClone(video: HTMLVideoElement): HTMLCanvasElement {
const canvas = video.ownerDocument.createElement('canvas');
canvas.width = video.offsetWidth;
canvas.height = video.offsetHeight;
const ctx = canvas.getContext('2d');
try {
if (ctx) {
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
if (!this.options.allowTaint) {
ctx.getImageData(0, 0, canvas.width, canvas.height);
}
}
return canvas;
} catch (e) {
this.context.logger.info(`Unable to clone video as it is tainted`, video);
}
const blankCanvas = video.ownerDocument.createElement('canvas');
blankCanvas.width = video.offsetWidth;
blankCanvas.height = video.offsetHeight;
return blankCanvas;
}
appendChildNode(clone: HTMLElement | SVGElement, child: Node, copyStyles: boolean): void { appendChildNode(clone: HTMLElement | SVGElement, child: Node, copyStyles: boolean): void {
if ( if (
!isElementNode(child) || !isElementNode(child) ||
@ -257,6 +286,23 @@ export class DocumentCloner {
} }
} }
cloneChildNodes(node: Element, clone: HTMLElement | SVGElement, copyStyles: boolean): void {
for (
let child = node.shadowRoot ? node.shadowRoot.firstChild : node.firstChild;
child;
child = child.nextSibling
) {
if (isElementNode(child) && isSlotElement(child) && typeof child.assignedNodes === 'function') {
const assignedNodes = child.assignedNodes() as ChildNode[];
if (assignedNodes.length) {
assignedNodes.forEach((assignedNode) => this.appendChildNode(clone, assignedNode, copyStyles));
}
} else {
this.appendChildNode(clone, child, copyStyles);
}
}
}
cloneNode(node: Node, copyStyles: boolean): Node { cloneNode(node: Node, copyStyles: boolean): Node {
if (isTextNode(node)) { if (isTextNode(node)) {
return document.createTextNode(node.data); return document.createTextNode(node.data);
@ -290,19 +336,8 @@ export class DocumentCloner {
copyStyles = true; copyStyles = true;
} }
for ( if (!isVideoElement(node)) {
let child = node.shadowRoot ? node.shadowRoot.firstChild : node.firstChild; this.cloneChildNodes(node, clone, copyStyles);
child;
child = child.nextSibling
) {
if (isElementNode(child) && isSlotElement(child) && typeof child.assignedNodes === 'function') {
const assignedNodes = child.assignedNodes() as ChildNode[];
if (assignedNodes.length) {
assignedNodes.forEach((assignedNode) => this.appendChildNode(clone, assignedNode, copyStyles));
}
} else {
this.appendChildNode(clone, child, copyStyles);
}
} }
if (before) { if (before) {

View File

@ -124,6 +124,7 @@ export const isHTMLElement = (node: Element): node is HTMLHtmlElement => node.ta
export const isSVGElement = (node: Element): node is SVGSVGElement => node.tagName === 'svg'; export const isSVGElement = (node: Element): node is SVGSVGElement => node.tagName === 'svg';
export const isBodyElement = (node: Element): node is HTMLBodyElement => node.tagName === 'BODY'; export const isBodyElement = (node: Element): node is HTMLBodyElement => node.tagName === 'BODY';
export const isCanvasElement = (node: Element): node is HTMLCanvasElement => node.tagName === 'CANVAS'; export const isCanvasElement = (node: Element): node is HTMLCanvasElement => node.tagName === 'CANVAS';
export const isVideoElement = (node: Element): node is HTMLVideoElement => node.tagName === 'VIDEO';
export const isImageElement = (node: Element): node is HTMLImageElement => node.tagName === 'IMG'; export const isImageElement = (node: Element): node is HTMLImageElement => node.tagName === 'IMG';
export const isIFrameElement = (node: Element): node is HTMLIFrameElement => node.tagName === 'IFRAME'; export const isIFrameElement = (node: Element): node is HTMLIFrameElement => node.tagName === 'IFRAME';
export const isStyleElement = (node: Element): node is HTMLStyleElement => node.tagName === 'STYLE'; export const isStyleElement = (node: Element): node is HTMLStyleElement => node.tagName === 'STYLE';

BIN
tests/assets/cc0-video.mp4 Normal file

Binary file not shown.

View File

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Video tests</title>
<script type="text/javascript" src="../../test.js"></script>
</head>
<body>
<h2>Same origin</h2>
<video controls width="250">
<source src="../../assets/cc0-video.mp4" type="video/mp4">
Sorry, your browser doesn't support embedded videos.
</video>
<h2>Cross-origin (doesn't taint)</h2>
<video controls width="250">
<source src="http://localhost:8081/cors/tests/assets/cc0-video.mp4" type="video/mp4">
Sorry, your browser doesn't support embedded videos.
</video>
</body>
</html>

View File

@ -12,7 +12,7 @@ const mkdirp = require('mkdirp');
export const app = express(); export const app = express();
app.use('/', serveIndex(path.resolve(__dirname, '../'), {icons: true})); app.use('/', serveIndex(path.resolve(__dirname, '../'), {icons: true}));
app.use('/', express.static(path.resolve(__dirname, '../'))); app.use([/^\/src($|\/)/, '/'], express.static(path.resolve(__dirname, '../')));
export const corsApp = express(); export const corsApp = express();
corsApp.use('/proxy', proxy()); corsApp.use('/proxy', proxy());