HTML to Image Converter
Renders a fragment of HTML and CSS to PNG or SVG in your browser, and lists what will not survive the trip before you find out from a blank rectangle.
This tool needs JavaScript: the markup is laid out and drawn by your own browser, which is why nothing is uploaded.
A fragment, not a whole document. Scripts, frames and event handlers are removed before anything is drawn.
Paste the rules rather than linking a stylesheet: nothing external is fetched when the image is drawn.
2 gives a file twice the size in each direction, which is what a retina screen wants.
Rendering HTML to an image in a browser is one trick: put the markup inside an SVG foreignObject and
draw that SVG onto a canvas. It works, and it fails in three ways that produce the same symptom, a blank
white rectangle, with nothing in the console.
This does the trick, and spends most of its effort on the failures. It lists what in your fragment will not survive before it draws anything, and when the markup cannot be parsed it hands you the parser’s complaint instead of an empty picture.
How to use
- Paste a fragment of HTML. Not a whole document: no
<html>, no<head>. - Paste the CSS. Paste it, do not link it, because nothing external is fetched when the image is drawn.
- Set the width. The height is measured from the content.
- Look at the warnings, then download the SVG or the PNG.
Example
A pull quote at 720 pixels wide:
720 × 214 CSS px · PNG would be 1440 × 428 at 2×
The height was measured, not guessed: the fragment is laid out off screen at the content width, measured, and the SVG is sized to fit. That is the only honest way to do it, because a fragment has no height until something lays it out.
Pitfalls
Nothing external loads. No linked stylesheet, no web font, no image from another origin. The SVG is rasterised in isolation, and anything it would have to fetch is simply absent. A Google Font falls back to a system font, which changes the line breaks and often the height.
A cross-origin image also breaks the PNG. Drawing an SVG that references another origin taints the canvas, and asking a tainted canvas for a PNG throws. Convert the picture to a data URI first; the warning here says so before you find out.
The markup has to be valid XML. An SVG is XML, so <br> is a fatal error and <p>one<p>two is
another. Void elements are closed for you, but unbalanced tags are not something to guess at: the parse
error is reported with the position the parser gives.
Scripts never run. They are stripped before anything is drawn, and so are event handler attributes. That is a feature: an image that executed the markup it was made from would be a bad idea on a page that accepts pasted HTML.
Fonts render with the metrics of the machine that drew them. The same fragment made on a Mac and on Windows can differ in line breaks. If the text must break in a particular place, set an explicit width and check the picture rather than the markup.
position: fixed has nothing to be fixed to. Inside the image it behaves like absolute against the
box, which is rarely what the rule meant.
Very tall fragments are capped at 8000 pixels. Past that you want a screenshot of a page rather than a rendering of a fragment, and the canvas starts to struggle in any case.
Emoji and other colour glyphs usually work, and sometimes do not. They depend on the platform’s colour font support during SVG rasterisation, which differs between browsers.
Compatibility
Works in current Chrome, Firefox and Safari. Everything happens in your browser: the markup is never sent anywhere, and the files are made locally.
The pipeline is: strip what cannot render, close void elements so the fragment is valid XML, lay it out
off screen to measure the height, wrap it in an SVG with the stylesheet in a CDATA section, check the
whole thing parses, and hand you a blob. The CDATA matters more than it looks: an ampersand or a < in
a selector would otherwise be a parse error, and the failure mode of a parse error is a blank image.
Measuring happens in a real element with visibility: hidden and a large negative offset rather than
display: none, because an element with no display has no layout and measures zero.
The PNG is made when you ask for it rather than on every keystroke, and the preview is redrawn a quarter of a second after you stop typing. Both are about the same thing: a layout and a rasterisation are the expensive operations here, and doing them on every input is what makes a tool like this feel broken.
Density is applied at rasterisation, so the SVG stays one size and the PNG comes out at two or three times the pixels for a retina screen.