Giter Site home page Giter Site logo

bubkoo / html-to-image Goto Github PK

View Code? Open in Web Editor NEW
5.2K 30.0 488.0 65.17 MB

✂️ Generates an image from a DOM node using HTML5 canvas and SVG.

License: MIT License

TypeScript 80.89% JavaScript 3.90% HTML 7.05% CSS 7.95% Shell 0.21%
screenshot canvas svg dataurl image dom picture javascript

html-to-image's Introduction

html-to-image

✂️ Generates an image from a DOM node using HTML5 canvas and SVG.

Fork from dom-to-image with more maintainable code and some new features.

build coverage NPM Package NPM Downloads

MIT License Language PRs Welcome

Install

npm install --save html-to-image

Usage

/* ES6 */
import * as htmlToImage from 'html-to-image';
import { toPng, toJpeg, toBlob, toPixelData, toSvg } from 'html-to-image';

/* ES5 */
var htmlToImage = require('html-to-image');

All the top level functions accept DOM node and rendering options, and return a promise fulfilled with corresponding dataURL:

Go with the following examples.

toPng

Get a PNG image base64-encoded data URL and display it right away:

var node = document.getElementById('my-node');

htmlToImage.toPng(node)
  .then(function (dataUrl) {
    var img = new Image();
    img.src = dataUrl;
    document.body.appendChild(img);
  })
  .catch(function (error) {
    console.error('oops, something went wrong!', error);
  });

Get a PNG image base64-encoded data URL and download it (using download):

htmlToImage.toPng(document.getElementById('my-node'))
  .then(function (dataUrl) {
    download(dataUrl, 'my-node.png');
  });

toSvg

Get an SVG data URL, but filter out all the <i> elements:

function filter (node) {
  return (node.tagName !== 'i');
}

htmlToImage.toSvg(document.getElementById('my-node'), { filter: filter })
  .then(function (dataUrl) {
    /* do something */
  });

toJpeg

Save and download a compressed JPEG image:

htmlToImage.toJpeg(document.getElementById('my-node'), { quality: 0.95 })
  .then(function (dataUrl) {
    var link = document.createElement('a');
    link.download = 'my-image-name.jpeg';
    link.href = dataUrl;
    link.click();
  });

toBlob

Get a PNG image blob and download it (using FileSaver):

htmlToImage.toBlob(document.getElementById('my-node'))
  .then(function (blob) {
    if (window.saveAs) {
      window.saveAs(blob, 'my-node.png');
    } else {
     FileSaver.saveAs(blob, 'my-node.png');
   }
  });

toCanvas

Get a HTMLCanvasElement, and display it right away:

htmlToImage.toCanvas(document.getElementById('my-node'))
  .then(function (canvas) {
    document.body.appendChild(canvas);
  });

toPixelData

Get the raw pixel data as a Uint8Array with every 4 array elements representing the RGBA data of a pixel:

var node = document.getElementById('my-node');

htmlToImage.toPixelData(node)
  .then(function (pixels) {
    for (var y = 0; y < node.scrollHeight; ++y) {
      for (var x = 0; x < node.scrollWidth; ++x) {
        pixelAtXYOffset = (4 * y * node.scrollHeight) + (4 * x);
        /* pixelAtXY is a Uint8Array[4] containing RGBA values of the pixel at (x, y) in the range 0..255 */
        pixelAtXY = pixels.slice(pixelAtXYOffset, pixelAtXYOffset + 4);
      }
    }
  });

React

import React, { useCallback, useRef } from 'react';
import { toPng } from 'html-to-image';

const App: React.FC = () => {
  const ref = useRef<HTMLDivElement>(null)

  const onButtonClick = useCallback(() => {
    if (ref.current === null) {
      return
    }

    toPng(ref.current, { cacheBust: true, })
      .then((dataUrl) => {
        const link = document.createElement('a')
        link.download = 'my-image-name.png'
        link.href = dataUrl
        link.click()
      })
      .catch((err) => {
        console.log(err)
      })
  }, [ref])

  return (
    <>
      <div ref={ref}>
      {/* DOM nodes you want to convert to PNG */}
      </div>
      <button onClick={onButtonClick}>Click me</button>
    </>
  )
}

Options

filter

(domNode: HTMLElement) => boolean

A function taking DOM node as argument. Should return true if passed node should be included in the output. Excluding node means excluding it's children as well.

You can add filter to every image function. For example,

const filter = (node: HTMLElement) => {
  const exclusionClasses = ['remove-me', 'secret-div'];
  return !exclusionClasses.some((classname) => node.classList?.contains(classname));
}

htmlToImage.toJpeg(node, { quality: 0.95, filter: filter});

or

htmlToImage.toPng(node, {filter:filter})

Not called on the root node.

backgroundColor

A string value for the background color, any valid CSS color value.

width, height

Width and height in pixels to be applied to node before rendering.

canvasWidth, canvasHeight

Allows to scale the canva's size including the elements inside to a given width and height (in pixels).

style

An object whose properties to be copied to node's style before rendering. You might want to check this reference for JavaScript names of CSS properties.

quality

A number between 0 and 1 indicating image quality (e.g. 0.92 => 92%) of the JPEG image.

Defaults to 1.0 (100%)

cacheBust

Set to true to append the current time as a query string to URL requests to enable cache busting.

Defaults to false

includeQueryParams

Set false to use all URL as cache key. If the value has falsy value, it will exclude query params from the provided URL.

Defaults to false

imagePlaceholder

A data URL for a placeholder image that will be used when fetching an image fails.

Defaults to an empty string and will render empty areas for failed images.

pixelRatio

The pixel ratio of the captured image. Default use the actual pixel ratio of the device. Set 1 to use as initial-scale 1 for the image.

preferredFontFormat

The format required for font embedding. This is a useful optimisation when a webfont provider specifies several different formats for fonts in the CSS, for example:

@font-face {
  name: 'proxima-nova';
  src: url("...") format("woff2"), url("...") format("woff"), url("...") format("opentype");
}

Instead of embedding each format, all formats other than the one specified will be discarded. If this option is not specified then all formats will be downloaded and embedded.

fontEmbedCSS

When supplied, the library will skip the process of parsing and embedding webfont URLs in CSS, instead using this value. This is useful when combined with getFontEmbedCSS() to only perform the embedding process a single time across multiple calls to library functions.

const fontEmbedCss = await htmlToImage.getFontEmbedCSS(element1);
html2Image.toSVG(element1, { fontEmbedCss });
html2Image.toSVG(element2, { fontEmbedCss });

skipAutoScale

When supplied, the library will skip the process of scaling extra large doms into the canvas object. You may experience loss of parts of the image if set to true and you are exporting a very large image.

Defaults to false

type

A string indicating the image format. The default type is image/png; that type is also used if the given type isn't supported. When supplied, the toCanvas function will return a blob matching the given image type and quality.

Defaults to image/png

Browsers

Only standard lib is currently used, but make sure your browser supports:

It's tested on latest Chrome, Firefox and Safari (49, 45 and 16 respectively at the time of writing), with Chrome performing significantly better on big DOM trees, possibly due to it's more performant SVG support, and the fact that it supports CSSStyleDeclaration.cssText property.

Internet Explorer is not (and will not be) supported, as it does not support SVG <foreignObject> tag.

How it works

There might some day exist (or maybe already exists?) a simple and standard way of exporting parts of the HTML to image (and then this script can only serve as an evidence of all the hoops I had to jump through in order to get such obvious thing done) but I haven't found one so far.

This library uses a feature of SVG that allows having arbitrary HTML content inside of the <foreignObject> tag. So, in order to render that DOM node for you, following steps are taken:

  1. Clone the original DOM node recursively
  2. Compute the style for the node and each sub-node and copy it to corresponding clone
    • and don't forget to recreate pseudo-elements, as they are not cloned in any way, of course
  3. Embed web fonts
    • find all the @font-face declarations that might represent web fonts
    • parse file URLs, download corresponding files
    • base64-encode and inline content as dataURLs
    • concatenate all the processed CSS rules and put them into one <style> element, then attach it to the clone
  4. Embed images
    • embed image URLs in <img> elements
    • inline images used in background CSS property, in a fashion similar to fonts
  5. Serialize the cloned node to XML
  6. Wrap XML into the <foreignObject> tag, then into the SVG, then make it a data URL
  7. Optionally, to get PNG content or raw pixel data as a Uint8Array, create an Image element with the SVG as a source, and render it on an off-screen canvas, that you have also created, then read the content from the canvas
  8. Done!

Things to watch out for

  • If the DOM node you want to render includes a <canvas> element with something drawn on it, it should be handled fine, unless the canvas is tainted - in this case rendering will rather not succeed.
  • Rendering will failed on huge DOM due to the dataURI limit varies.

Contributing

Please let us know how can we help. Do check out issues for bug reports or suggestions first.

To become a contributor, please follow our contributing guide.

Contributors

License

The scripts and documentation in this project are released under the MIT License

html-to-image's People

Contributors

andyearnshaw avatar anguedev avatar bubkoo avatar dependabot[bot] avatar dimitrisraptis96 avatar dustinbrett avatar gustavochavarria avatar happy-func avatar isergey87 avatar jeanfrancois8512 avatar june07 avatar lekoaf avatar lgtm-com[bot] avatar linjie997 avatar lopermo avatar lovenick avatar marcelvoss95 avatar marcusdelang avatar merapi avatar mjr9804 avatar perryhuan9 avatar prashoon123 avatar ramadis avatar sasithahtl avatar seahindeniz avatar semantic-release-bot avatar shaman123 avatar sureshtr1998 avatar vivcat[bot] avatar yuhao1118 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

html-to-image's Issues

@font-face not included in SVGs

I'm currently attempting to render out some SVG graphs to JPEG. It works fine after inlining all styles, as there were some "insecure operation" warnings when trying to read from CSS, but one thing I just cannot get working is using web fonts in SVG.

I've tried both importing the fonts using @import from Google Fonts, and defining the font with @font-face.

I tried putting fontFamily: 'Roboto' in the style object of options, as well as inlining style={{fontFamily: 'Roboto'}} directly on a <text> SVG element, neither worked.

Probably worth mentioning I'm using React for this.
I'm kinda running out of ideas. Any help?

Running using es5 configuration gives error on command line with browserify

Steps to Reproduce:

  1. Add the following to the source file being run:
var htmlToImage = require('html-to-image');
var svgNode = document.getElementById('someIdOfSvgElement');
htmlToImage.toPng(svgNode)
    .then(function (dataUrl) {
      console.log(dataUrl);
    })
    .catch(function (error) {
      console.error('Unable to capture SVG node with id: "' + aSvgId + '"');
    });
  1. Run using browserify (command in package.json):
"scripts": {
    "captureImage": "browserify captureImage.js -o bundle.js && open run.html"
}

Note that run.html in this case includes the bundle.js script.

Expected Results:

  • ✅ Browserify completes successfully, launches the run.html page.

Actual Results:

  • ❌ The following error is given on the command line:
import cloneNode from './cloneNode';
^
ParseError: 'import' and 'export' may appear only with 'sourceType: module'

My thinking is that this has something to do with how browserify isn't using es2015 or es2016. I did try using babelify, as in the following example:

"scripts": {
    "captureImage": "browserify captureImage.js -o bundle.js -t [ babelify --presets [ env ] ] && open run.html"
}

(After installing babelify, babel-core, and babel-preset-env, of course)

This seems to result in the same error.

Font family and font size changed in created canvas

Hello,

I am facing issue with fonts while converting dom into canvas using htmltoimage.toCanvas function. Generated canvas has different font family and font size which is creating in my final output. I also tried to set style object but that didn't work.

Can you help me out with this? Really Urgent.

Transparent image

Hi,
Thanks for your work on this lib.

Maybe that's a dumb question but is there a way to generate a PNG image from the DOM with transparency ? No background-color.

Thanks a lot

Slow Download and Error File when Download From Server

I have code:
htmlToImage.toPng(document.querySelector('.orgchart')) .then((dataUrl) => { download(dataUrl, ${fileNameExportHierarchy}.png, 'image/png'); });

When i try in local, no problem. but when code deployed to server, popup download too long and file error like this photo.
How i solving this problem?
image

Use in Browser

Hello
How can this library be used in a browser?
How to link it to a script?

Local asset icons missing from image

I cannot get images from the local assets available in the sources to show on the generated image.

They are SVG icon images that are available and working on the page on image elements. You can see the files available in the assets folder in the Sources tab of the chrome developer tools.

Cuts off bottom on high resolution screens

When running .toBlob(node) on a 3840x2160 resolution monitor, the bottom of the node element is cropped off the picture. I can drag the application (browser tab) onto one of my 1920x1080 monitors and it works as expected.

SVG + Azure Private blob service

I am facing an issue with SVGs which is rendered from Azure blob service. If an image is made publicly available it's working perfectly fine. But if we make blob container private than Package is unable to generate an image from it.

Wait for loading

I encountered a problem when creating a screenshot of Google maps in street view mode.
The fact is that the screenshot turns out to be a black image with Google controls, due to the fact that the 360-view image itself takes a long time to load.

Is there some way to wait for the download or wait some time before saving the screenshot?

Not all images are loading when saving image to jpeg on iOS

I have a pretty simple section I am saving as an image.
On web and Android it works fine, however on iOS it seems to include 1 of 3 images on every other download.

I have 3 images, nothing over 70kb. All assets are loaded before the save to jpeg event happens. But one of the images only shows in the saved image every other time, but the other 2 images show fine.
1 image is loaded from an external source and the other 2 are references locally.

I have tried CSS background, img tags and base64, all have the same issue.

Any ideas why this would be happening?

different width and height of the generated image due to screen resolution

I generated an image with the 335px of width and 102px of height. I tested it on a laptop with 1366x768 screen resolution but when I test this on a mobile phone with the 1080x1920 screen resolution, the size of the generated image becomes 670px by 204px automatically even I have set a fixed width and height. I found the reason for this particular issue which is the pixel ratio. I dive to the deep in the code and found where it multiply the width and the height by the pixel ratio and that's the final width and height for the output image.

Fonts not transferred to image

The fonts don't get transferred to the png image when I use htmlToImage.toPng(node).

I use the latest version of Chrome and your package

image

Example Implementation

This is not so much an issue as a request.

I would prefer to just reference a JS file and do things without node, as I'm trying to use this with a legacy project. In any case, I'm trying to use this but can't seem to get it to work (it may be related to this issue). I've tried with and without node.

Could you provide an example, perhaps on JS fiddle, that shows this code working?

A working example is always helpful, no matter how you choose to implement it.

ERROR Error: Uncaught (in promise): SecurityError

I have used this module to export html to png base64 dataurl and pass it to server
it works fine in Chrome and mozilla but not working in IE 11 it gives error like

ERROR Error: Uncaught (in promise): SecurityError

please help

Style "visibility" doesn't work with toCanvas

When I try to set visibility by providing it in style with toCanvas method, it doesn't work
htmlToImage
.toCanvas(data, {
style: { visibility: 'visible'},
})

where my data has style visibility='hidden'

Using document.body to take screenshot in React app causes a wired bug

I am using this package in React app & when I take a screenshot for <body /> tag I see a weird message You need to enable javascript to run this app. But when I use any other tag or the Reacts root tag the message disappears. Don't know what is going on.

This only appears in Chrome & not firefox.

Here's a screenshot.
Screenshot 2020-07-16 at 10 06 54 AM

It's not working with Iframes

Is there any workaround to make it work with child iframes?
I've been using this package for a few months and it was working like a charm. But now I had to insert iframe elements in my page and they appear blank in the final image.

Here is the piece of code which I'm using in my Angular application:

htmlToImage
   .toPng(this.contentAreaRef.nativeElement)
   .then((image: string) =>  {
      this.dashboard.thumbnail_base64 = image;
   });

Thanks in advance

Issue with Android

Hi,

I am working on a React app with Apache Cordova. My objective is build the app on Windows (with Electron) and Android.
I applied this library on my React app and works well on Windows (only a issue with scrollbars below tables) but on Android, nothing downloads. I am using the toJpeg method.

Is this expected?

If you have any questions, please tell me.

Snapshot of my code:

const exportTo = ( format: string ) => { const htmlReceipt = document.getElementById("orderreceipt"); if ( htmlReceipt == null ){ throw new Error("Error finding elementid"); } if ( format === "JPG") { htmltoimage.toJpeg(htmlReceipt, { backgroundColor: "white", style: { overflow: "hidden" } }).then(dataUrl => { const link = document.createElement('a'); link.download = 'orderreceipt.jpeg'; link.href = dataUrl; link.click(); }); } };

Note: style: { overflow: "hidden" } is here for avoid the scrollbars, but nothing happens.

Thanks,
Fernando.

import but undefined

import html2Image from 'html-to-image'
console.log(html2Image)

I got undefined.
I was trying to use it with Vuejs
Please help

Can n't generate a screenshots with active <svg/> elements(result is empty).

Summary

Can n't generate a screenshot of page with active elements. For example: html contains svg charts (highcharts/other). But it works good with external svg-files(added as background image for div elements or in img element)

Expected result: screenshots with SVG elements.

Really: screenshots with empty spaces for svg elements.

Does it possible to generate a screenshots with active elements/charts?

Svg elements are misaligned on printed Map

Hi, first thank you for this library.
I'm facing the following problem:

i'm actually using leaflet.js to generate a map, and when i try to print it with the .toPng function, svg elements are misaligned compared to the background tilelayer.
This problem disappears if i enable canvas rendering (rather than svg) in leaflet engine.

Here is a stackblitz relating the problem:
https://stackblitz.com/edit/leaflet-prefercanvasenabled

If you set option "preferCanvas" to true in Map object of the init() function (app.component.ts file), printing is really nice.
Unfortunately i can't use canvas rendering due to additionnal limitations.

It's seems there is a problem during svg conversion...any help would be really appreciated.
Thanks.

Define DPI

Thanks for this plugin. Is it possible to define the DPI so we can get better resolution images? I assume the max currently is 96dpi?

Full build of html-to-image

For a first start or for trials with html-to-image it would be great to have a full build of html-to-image at your homepage github.com/bubkoo/html-to-image, . e.g. html-to-image(.min).js . Similar to github.com/tsayen/dom-to-image, there are a dom-to-image.min.js and a dom-to-image.js available.

TypeError: Failed to execute 'getComputedStyle' on 'Window': parameter 1 is not of type 'Element'.

I have a project with few charts, and I want to create a pdf (using react-pdf) with those charts as images.
I've set the id for one of the charts and tried the example code as shown in the usage section:

 var node = document.getElementById("bp-graph");

    htmlToImage
      .toPng(node)
      .then(function (dataUrl) {
        console.log("inside");
        var bpImage = new Image();
        bpImage.src = dataUrl;
        document.body.appendChild(bpImage);
      })
      .catch(function (error) {
        console.error("oops, something went wrong!", error);
      });

but I'm getting the error in the title, any idea why, or what I need to change?

Height / width options appear not to work as might be expected

When I set the width option, I had hoped that the contents (which are 100% width) would scale to fit the given dimensions. Instead I get an image that is the size specified, but the contents remain at the size they are on the page.

I'd be happy to provide more details and an example if you're willing to look into this.

Thanks for the great library!

image CORS problem

image

This is a problem.

image

So I added this option, but it didn't work.
How can I fix it?

Please comment me.
Thank you.

Can we have a flag for skipping the CORS and 404 CSS URL ?

This is an issue. If we get the CORS url for css and svg files in the code, the promise just throws the error, same with (404) file not found too. If we have a flag Debugger false/true we can skip the errors and promise wont break

CSSStyleSheet Error

I get an error when I trying to convert google maps to image:

Error inlining remote css file SecurityError: Failed to read the 'cssRules' property from 'CSSStyleSheet': Cannot access rules
embedWebFonts.js:118 Error loading remote stylesheet SecurityError: Failed to read the 'cssRules' property from 'CSSStyleSheet': Cannot access rules
embedWebFonts.js:137 Error while reading CSS rules from https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Google+Sans:400,500,700 SecurityError: Failed to read the 'cssRules' property from 'CSSStyleSheet': Cannot access rules

In result, I got an image, but with bad size (bigger than my screen)

require generated by tsc, breaks inside browser

I have tried to use your lib inside a TypeScript project.

import * as htmlToImage from 'html-to-image';

htmlToImage
    .toPng(node)
    .then((dataUrl: string) => {
        const img = new Image();
        img.src = dataUrl;
        this.$graphContainer.appendChild(img);
    })
    .catch((error: string) => {
        console.error('oops, something went wrong!', error);
    });

inside node_modules/html-to-image/lib/index.js there is a :

var utils_1 = require("./utils");

i got this error in my app :

Uncaught (in promise) TypeError: utils.getNodeWidth is not a function
    at getImageSize (index.js:14)
    at toSvgDataURL (index.js:20)
    at toCanvas (index.js:30)
    at toPng (index.js:61)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.