Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ All the top level functions accept DOM node and rendering options, and return a
- [toSvg](#toSvg)
- [toJpeg](#toJpeg)
- [toBlob](#toBlob)
- [toImage](#toImage)
- [toCanvas](#toCanvas)
- [toCanvasList](#toCanvasList)
- [toPixelData](#toPixelData)

Go with the following examples.
Expand Down Expand Up @@ -114,6 +116,16 @@ htmlToImage.toBlob(document.getElementById('my-node'))
});
```

#### toImage
Get a HTMLImageElement, which is a svg image that you can scale it to a big size and it will not blurred.

```js
htmlToImage.toImage(document.getElementById('my-node'))
.then(function (img) {
document.body.appendChild(img);
});
```

#### toCanvas
Get a HTMLCanvasElement, and display it right away:

Expand All @@ -124,6 +136,17 @@ htmlToImage.toCanvas(document.getElementById('my-node'))
});
```

#### toCanvasList
Get a array of HTMLCanvasElement. Not like `toCanvas` which is limited by [canvas size](https://jhildenbiddle.github.io/canvas-size/#/?id=test-results),
`toCanvasList` can get rid of the limitation of canvas size, so this can export a very large html:

```js
htmlToImage.toCanvasList(document.getElementById('my-node'))
.then(function (canvasList) {
canvasList.map(canvas => document.body.appendChild(canvas));
});
```

#### toPixelData
Get the raw pixel data as a [Uint8Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) with every 4 array elements representing the RGBA data of a pixel:

Expand Down Expand Up @@ -292,6 +315,15 @@ A string indicating the image format. The default type is image/png; that type i
When supplied, the toCanvas function will return a blob matching the given image type and quality.

Defaults to `image/png`


### usePageCss

Use `true` to add a `<style>` tag in svg content which imports all styles of current html page, and do not add computed styles to every node any more(this make svg content so large that Firefox throw errors while load svg as image).
This will make the svg content much smaller, to resolve problems which caused by html that has large amount of sub nodes.


Defaults to `true`

## Browsers

Expand Down
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "html-to-image",
"version": "1.11.11",
"description": "Generates an image from a DOM node using HTML5 canvas and SVG.",
"name": "html-to-image-big",
"version": "1.11.15",
"description": "Generates an image from a DOM node using HTML5 canvas and SVG. Support big html page.",
"main": "lib/index.js",
"module": "es/index.js",
"unpkg": "dist/html-to-image.js",
Expand All @@ -23,7 +23,7 @@
"svg"
],
"scripts": {
"lint": "eslint 'src/**/*.{js,ts}?(x)' --fix",
"lint": "eslint src/**/*.{js,ts} --fix",
"clean": "rimraf dist es lib",
"build:esm": "tsc --module esnext --target es2017 --outDir ./es",
"build:cjs": "tsc --module commonjs --target es5 --outDir ./lib",
Expand Down
69 changes: 41 additions & 28 deletions src/clone-node.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import type { Options } from './types'
import { clonePseudoElements } from './clone-pseudos'
import { createImage, toArray, isInstanceOfElement } from './util'
import { createImage, toArray, isInstanceOfElement, getDoc } from './util'
import { getMimeType } from './mimes'
import { resourceToDataURL } from './dataurl'
import { toDataUrl } from './index'

async function cloneCanvasElement(canvas: HTMLCanvasElement) {
const dataURL = canvas.toDataURL()
Expand All @@ -14,7 +15,7 @@ async function cloneCanvasElement(canvas: HTMLCanvasElement) {

async function cloneVideoElement(video: HTMLVideoElement, options: Options) {
if (video.currentSrc) {
const canvas = document.createElement('canvas')
const canvas = getDoc().createElement('canvas')
const ctx = canvas.getContext('2d')
canvas.width = video.clientWidth
canvas.height = video.clientHeight
Expand All @@ -29,21 +30,21 @@ async function cloneVideoElement(video: HTMLVideoElement, options: Options) {
return createImage(dataURL)
}

async function cloneIFrameElement(iframe: HTMLIFrameElement) {
try {
if (iframe?.contentDocument?.body) {
return (await cloneNode(
iframe.contentDocument.body,
{},
true,
)) as HTMLBodyElement
}
} catch {
// Failed to clone iframe
}

return iframe.cloneNode(false) as HTMLIFrameElement
}
// async function cloneIFrameElement(iframe: HTMLIFrameElement) {
// try {
// if (iframe?.contentDocument?.body) {
// return (await cloneNode(
// iframe.contentDocument.body,
// {},
// true,
// )) as HTMLBodyElement
// }
// } catch {
// // Failed to clone iframe
// }
//
// return iframe.cloneNode(false) as HTMLIFrameElement
// }

async function cloneSingleNode<T extends HTMLElement>(
node: T,
Expand All @@ -58,7 +59,15 @@ async function cloneSingleNode<T extends HTMLElement>(
}

if (isInstanceOfElement(node, HTMLIFrameElement)) {
return cloneIFrameElement(node)
if (node.offsetWidth + node.offsetHeight > 0) {
const body = node?.contentDocument?.body
if(body) {
const img = getDoc().createElement('img')
img.src = await toDataUrl(body, options)
return img
}
}
return getDoc().createElement('div') //cloneIFrameElement(node)
}

return node.cloneNode(false) as T
Expand Down Expand Up @@ -112,7 +121,7 @@ function cloneCSSStyle<T extends HTMLElement>(nativeNode: T, clonedNode: T) {
if (!targetStyle) {
return
}

const window = nativeNode.ownerDocument.defaultView || self
const sourceStyle = window.getComputedStyle(nativeNode)
if (sourceStyle.cssText) {
targetStyle.cssText = sourceStyle.cssText
Expand All @@ -133,11 +142,11 @@ function cloneCSSStyle<T extends HTMLElement>(nativeNode: T, clonedNode: T) {
) {
value = 'block'
}

if (name === 'd' && clonedNode.getAttribute('d')) {
value = `path(${clonedNode.getAttribute('d')})`
}

targetStyle.setProperty(
name,
value,
Expand Down Expand Up @@ -170,10 +179,14 @@ function cloneSelectValue<T extends HTMLElement>(nativeNode: T, clonedNode: T) {
}
}

function decorate<T extends HTMLElement>(nativeNode: T, clonedNode: T): T {
function decorate<T extends HTMLElement>(
nativeNode: T,
clonedNode: T,
usePageCss?: boolean,
): T {
if (isInstanceOfElement(clonedNode, Element)) {
cloneCSSStyle(nativeNode, clonedNode)
clonePseudoElements(nativeNode, clonedNode)
if (!usePageCss) cloneCSSStyle(nativeNode, clonedNode)
if (!usePageCss) clonePseudoElements(nativeNode, clonedNode)
cloneInputValue(nativeNode, clonedNode)
cloneSelectValue(nativeNode, clonedNode)
}
Expand All @@ -196,7 +209,7 @@ async function ensureSVGSymbols<T extends HTMLElement>(
const id = use.getAttribute('xlink:href')
if (id) {
const exist = clone.querySelector(id)
const definition = document.querySelector(id) as HTMLElement
const definition = getDoc().querySelector(id) as HTMLElement
if (!exist && definition && !processedDefs[id]) {
// eslint-disable-next-line no-await-in-loop
processedDefs[id] = (await cloneNode(definition, options, true))!
Expand All @@ -207,15 +220,15 @@ async function ensureSVGSymbols<T extends HTMLElement>(
const nodes = Object.values(processedDefs)
if (nodes.length) {
const ns = 'http://www.w3.org/1999/xhtml'
const svg = document.createElementNS(ns, 'svg')
const svg = getDoc().createElementNS(ns, 'svg')
svg.setAttribute('xmlns', ns)
svg.style.position = 'absolute'
svg.style.width = '0'
svg.style.height = '0'
svg.style.overflow = 'hidden'
svg.style.display = 'none'

const defs = document.createElementNS(ns, 'defs')
const defs = getDoc().createElementNS(ns, 'defs')
svg.appendChild(defs)

for (let i = 0; i < nodes.length; i++) {
Expand All @@ -240,6 +253,6 @@ export async function cloneNode<T extends HTMLElement>(
return Promise.resolve(node)
.then((clonedNode) => cloneSingleNode(clonedNode, options) as Promise<T>)
.then((clonedNode) => cloneChildren(node, clonedNode, options))
.then((clonedNode) => decorate(node, clonedNode))
.then((clonedNode) => decorate(node, clonedNode, options.usePageCss))
.then((clonedNode) => ensureSVGSymbols(clonedNode, options))
}
3 changes: 2 additions & 1 deletion src/clone-pseudos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ function clonePseudoElement<T extends HTMLElement>(
clonedNode: T,
pseudo: Pseudo,
) {
const window = nativeNode.ownerDocument.defaultView || self
const style = window.getComputedStyle(nativeNode, pseudo)
const content = style.getPropertyValue('content')
if (content === '' || content === 'none') {
Expand All @@ -49,7 +50,7 @@ function clonePseudoElement<T extends HTMLElement>(
return
}

const styleElement = document.createElement('style')
const styleElement = nativeNode.ownerDocument.createElement('style')
styleElement.appendChild(getPseudoElementStyle(className, pseudo, style))
clonedNode.appendChild(styleElement)
}
Expand Down
96 changes: 0 additions & 96 deletions src/embed-images.ts

This file was deleted.

Loading