DEV Community

Cover image for How to turn HTML webpage into an Image?
Jasmin Virdi
Jasmin Virdi

Posted on • Updated on

How to turn HTML webpage into an Image?

While working in one of my project I had to implement a feature where I have turn an HTML webpage to an Image. The first thought that occurred to me was to use an inbuilt library but like dom-to-image or using Chrome Headless or a wrapper library like Puppeteer. While working I came across this technique using pure Javascript.

Let's try to achieve this without using any library.

Converting HTML webpage into an Image by using Canvas.

We cannot directly draw the HTML into Canvas due to the security reasons. We will follow another approach which will be safer.

Steps

  • Create SVG Image that will contain the rendering content.
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">
</svg>
Enter fullscreen mode Exit fullscreen mode
  • Insert a <foreignObject> element inside the SVG which will contain the HTML.
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">' +
    <foreignObject width="100%" height="100%">
    </foreignObject>
</svg>
Enter fullscreen mode Exit fullscreen mode
  • Add the XHTML content inside the <foreignObject> node.
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">' +
    <foreignObject width="100%" height="100%">
        <div xmlns="http://www.w3.org/1999/xhtml">. 
          <style>em{color:red;}</style>
             Hey there...
        </div>
    </foreignObject>
</svg>
Enter fullscreen mode Exit fullscreen mode
  • Create the image object and set the src of an image to the data url of the image.
const tempImg = document.createElement('img')
tempImg.src = 'data:image/svg+xml,' + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100"><foreignObject width="100%" height="100%"><div xmlns="http://www.w3.org/1999/xhtml">Hey there...</div></foreignObject></svg>')
Enter fullscreen mode Exit fullscreen mode
  • Draw this Image onto Canvas and set canvas data to target img.src.
const newImg = document.createElement('img')
newImg.src = 'data:image/svg+xml,' + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100"><foreignObject width="100%" height="100%"><div xmlns="http://www.w3.org/1999/xhtml">Hey there...</div></foreignObject></svg>')

// add event listener to image.
newImg.addEventListener('load', onNewImageLoad)

// method to draw image to canvas and set data to target img.src

function onNewImageLoad(e){
  ctx.drawImage(e.target, 0, 0)
  targetImg.src = canvas.toDataURL()
}
Enter fullscreen mode Exit fullscreen mode

You can find the complete code in the CodeSandox!

Reasons why using SVG and Canvas is safe?

Implementation of SVG image is very restrictive as we don't allow SVG image to load an external resource even one that appears on the same domain. Scripting in an SVG image is not allowed, there is no way to access the DOM of an SVG image from other scripts, and DOM elements in SVG images cannot receive input events. Thus there is no way to load privileged information into a form control (such as a full path in a <input type="file">) and render it.

The restriction that script can't directly touch DOM nodes that get rendered to the canvas is important from the security point of view.

I tried to cover this topic and steps in brief. Please feel free to add on related to this topic. 😅

Happy Learning!👩‍💻

Discussion (22)

Collapse
code913 profile image
code913

That's a pretty smart approach, however I think it's a bit unnecessary
If you need an image of your HTML page, you can use the built in chrome devtools screenshotter:

  • Press Ctrl Shift J
  • Press Ctrl Shift P
  • Type screenshot
  • Choose one of the screen shot methods

You can also use an <iframe> with locked down permissions through the sandbox property

Collapse
imthedeveloper profile image
ImTheDeveloper

I imagine the use case was for automation rather than manually doing so.

Collapse
peerreynders profile image
peerreynders • Edited on

Puppeteer: page.screenshot()
Playwright: page.screenshot()
Cypress: cy.screenshot()

Interesting use of Web APIs regardless

CanvasRenderingContext2D.drawImage()
HTMLCanvasElement.toDataURL()

Thread Thread
jasmin profile image
Jasmin Virdi Author

Thanks for mentioning 🙌

Collapse
jasmin profile image
Jasmin Virdi Author

Definitely, you can do this manually. My intention was to state an approach when you have to automate it or do it via code without using any library.👍

Collapse
timmotal profile image
Timmotal

As a programmer, I think think it's great to know how to use codes to achieve this.

Collapse
racheal profile image
Racheal Walker

hey,
i like your approach but you can also try doing this.

Upload html-file(s) Select files from Computer, Google Drive, Dropbox, URL or by dragging it on the page.
Choose "to jpg" Choose jpg or any other format you need as a result (more than 200 formats supported)
Download your jpg.

Collapse
peerreynders profile image
peerreynders

That's the Google featured snippet you get when you search "How to turn HTML webpage into an Image?" (i.e. the title of the article) - which isn''t that useful without the website where it came from.

Collapse
jasmin profile image
Jasmin Virdi Author

You can definitely try this way but I want to cover the case where you have to do it via Code.

Collapse
emekaorji profile image
EmekaOrji

This is so needed,
I was encountered with the problem of creating a card in 'img' format from information that a user inputs into a form

So this makes sense as I can dynamically add the user-input into the svg/canvas and print an image

ALL WITHOUT USING AN EXTERNAL LIBRARY😒!!!

Collapse
sefatanam profile image
Sefat Anam

from my point of view - OMGGGGG !

  • i'm backend dev :)
Collapse
jasmin profile image
Jasmin Virdi Author

🙌

Collapse
youpiwaza profile image
max

Or chrome extension "go full page".
You can print as PNG or PDF ;)

Nice article though, I liked the writing

Collapse
jasmin profile image
Jasmin Virdi Author

Definitely, there could be other ways but I wanted to try using plain JS

Collapse
youpiwaza profile image
max

No problem, if it was for discovery reasons ou just see if it waws possible :)

The security aspect is nice, it could be used as a deep sanitize ^^

Collapse
vulcanwm profile image
Medea

woah nice!

Collapse
jasmin profile image
Jasmin Virdi Author

Thanks 🙂

Collapse
skywarth profile image
skywarth • Edited on

Fascinating. Can you please explain why we have to use 'xmlns' attribute.

Collapse
randwulf_magnus profile image
Randell Knight

Wow, this is so cool. Thanks for sharing!

Collapse
brunoj profile image
Bruno

Thank you for writing this