I want to convert SVG into bitmap images (like JPEG, PNG, etc.) through JavaScript.
-
What task is it that you actually want to accomplish? Even though echo-flows answer tell us that it is (in some browsers) possible there are better and easier conversion methods for almost all practical cases. – aaaaaaaaaaaa Oct 20 '10 at 10:54
-
2Here's an example using d3: http://stackoverflow.com/a/23667012/439699 – ace May 14 '14 at 23:50
-
http://svgopen.org/2010/papers/62-From_SVG_to_Canvas_and_Back/ - Works perfectly! [On the link page, sourceSVG = $("#your_svg_elem_name").get(0) ] – Vijay Singh Apr 24 '16 at 17:41
-
related: https://stackoverflow.com/questions/3173048/is-there-an-equivalent-of-canvass-todataurl-method-for-svg – mathheadinclouds May 30 '20 at 19:08
-
https://mybyways.com/blog/convert-svg-to-png-using-your-browser – natenho Apr 17 '21 at 04:51
-
Does this answer your question? [Capture HTML Canvas as gif/jpg/png/pdf?](https://stackoverflow.com/questions/923885/capture-html-canvas-as-gif-jpg-png-pdf) – Carson Jul 18 '21 at 13:02
10 Answers
Here is how you can do it through JavaScript:
- Use the canvg JavaScript library to render the SVG image using Canvas: https://github.com/gabelerner/canvg
- Capture a data URI encoded as a JPG (or PNG) from the Canvas, according to these instructions: Capture HTML Canvas as gif/jpg/png/pdf?
-
29This isn't strictly Javascript, but HTML5 as well. This will not work on IE8, or any other browser that does not support HTML5 Canvas. – James Jun 25 '12 at 18:31
-
16If the browser supports SVG and canvas, then there would be a much simpler way to load the SVG into memory and then paint it into a canvas, without the need for Canvg, which is a pretty large library because it handles all the SVG parsing that an SVG-supporting browser already provides for free. I'm not sure if this satisfies the original use-case, but if so, then [see this resource for details](https://svgopen.org/2010/papers/62-From_SVG_to_Canvas_and_Back/index.html). – Prem Oct 03 '13 at 08:19
-
161Thanks for not supporting IE8. People should understand that it's time to move on. – Sanket Sahu Jan 31 '14 at 07:51
-
10You can now use the JavaScript SVG library [Pablo](http://pablojs.com) to achieve this (I made it). See the [`toImage()`](http://pablojs.com/api/toImage/) and also [`download()`](http://pablojs.com/api/download/) for an auto-downloaded image. – Prem Sep 15 '14 at 14:04
-
-
2http://svgopen.org/2010/papers/62-From_SVG_to_Canvas_and_Back/ - Works perfectly! [On the link page, sourceSVG = $("#your_svg_elem_name").get(0) ] – Vijay Singh Apr 24 '16 at 17:40
-
-
1@Premasagar Unfortunately the link doesn't work, but your comment sounds very interesting. – Andru May 04 '18 at 15:59
jbeard4 solution worked beautifully.
I'm using Raphael SketchPad to create an SVG. Link to the files in step 1.
For a Save button (id of svg is "editor", id of canvas is "canvas"):
$("#editor_save").click(function() {
// the canvg call that takes the svg xml and converts it to a canvas
canvg('canvas', $("#editor").html());
// the canvas calls to output a png
var canvas = document.getElementById("canvas");
var img = canvas.toDataURL("image/png");
// do what you want with the base64, write to screen, post to server, etc...
});
-
1
-
1@Luckyn if you call `$(selector).html()` on the parent of your *svg* element, it will work – jonathanGB Aug 12 '16 at 18:31
-
@Luckyn and @jonathanGB, you shouldn't have to use `html()` on wrappers, or manually construct the parent `svg` tag -- which might even have attributes you leave out with this hack. Just use `$(svg_elem)[0].outerHTML` gives you the full source of the svg and its contents. Just saying... – JWL Oct 19 '16 at 12:53
This seems to work in most browsers:
function copyStylesInline(destinationNode, sourceNode) {
var containerElements = ["svg","g"];
for (var cd = 0; cd < destinationNode.childNodes.length; cd++) {
var child = destinationNode.childNodes[cd];
if (containerElements.indexOf(child.tagName) != -1) {
copyStylesInline(child, sourceNode.childNodes[cd]);
continue;
}
var style = sourceNode.childNodes[cd].currentStyle || window.getComputedStyle(sourceNode.childNodes[cd]);
if (style == "undefined" || style == null) continue;
for (var st = 0; st < style.length; st++){
child.style.setProperty(style[st], style.getPropertyValue(style[st]));
}
}
}
function triggerDownload (imgURI, fileName) {
var evt = new MouseEvent("click", {
view: window,
bubbles: false,
cancelable: true
});
var a = document.createElement("a");
a.setAttribute("download", fileName);
a.setAttribute("href", imgURI);
a.setAttribute("target", '_blank');
a.dispatchEvent(evt);
}
function downloadSvg(svg, fileName) {
var copy = svg.cloneNode(true);
copyStylesInline(copy, svg);
var canvas = document.createElement("canvas");
var bbox = svg.getBBox();
canvas.width = bbox.width;
canvas.height = bbox.height;
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, bbox.width, bbox.height);
var data = (new XMLSerializer()).serializeToString(copy);
var DOMURL = window.URL || window.webkitURL || window;
var img = new Image();
var svgBlob = new Blob([data], {type: "image/svg+xml;charset=utf-8"});
var url = DOMURL.createObjectURL(svgBlob);
img.onload = function () {
ctx.drawImage(img, 0, 0);
DOMURL.revokeObjectURL(url);
if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob)
{
var blob = canvas.msToBlob();
navigator.msSaveOrOpenBlob(blob, fileName);
}
else {
var imgURI = canvas
.toDataURL("image/png")
.replace("image/png", "image/octet-stream");
triggerDownload(imgURI, fileName);
}
document.removeChild(canvas);
};
img.src = url;
}
- 325
- 3
- 3
-
3This is not working in IE11, because of the security issue with `.msToBlob()` – Florian Leitgeb Jun 05 '18 at 11:27
-
Thanks!! I love how this works for both a "local" SVG HTML node and a remote SVG URL. Plus it doesn't require a full external library – Rudolf Real Nov 14 '19 at 22:24
-
4
-
1This copyStylesInline helped me a lot!! Without adding that, my exported png was looking very weird. Thank you! – Augusto Peres Mar 11 '22 at 20:01
The solution to convert SVG to blob URL and blob URL to png image
const svg=`<svg version="1.1" baseProfile="full" width="300" height="200"
xmlns="http://www.w3.org/2000/svg">
<rect width="100%" height="100%" fill="red" />
<circle cx="150" cy="100" r="80" fill="green" />
<text x="150" y="125" font-size="60" text-anchor="middle" fill="white">SVG</text></svg>`
svgToPng(svg,(imgData)=>{
const pngImage = document.createElement('img');
document.body.appendChild(pngImage);
pngImage.src=imgData;
});
function svgToPng(svg, callback) {
const url = getSvgUrl(svg);
svgUrlToPng(url, (imgData) => {
callback(imgData);
URL.revokeObjectURL(url);
});
}
function getSvgUrl(svg) {
return URL.createObjectURL(new Blob([svg], { type: 'image/svg+xml' }));
}
function svgUrlToPng(svgUrl, callback) {
const svgImage = document.createElement('img');
// imgPreview.style.position = 'absolute';
// imgPreview.style.top = '-9999px';
document.body.appendChild(svgImage);
svgImage.onload = function () {
const canvas = document.createElement('canvas');
canvas.width = svgImage.clientWidth;
canvas.height = svgImage.clientHeight;
const canvasCtx = canvas.getContext('2d');
canvasCtx.drawImage(svgImage, 0, 0);
const imgData = canvas.toDataURL('image/png');
callback(imgData);
// document.body.removeChild(imgPreview);
};
svgImage.src = svgUrl;
}
- 2,131
- 2
- 17
- 37
- 5,403
- 3
- 22
- 37
-
-
1Here is your code but optimized, but thank you for that... https://codepen.io/arcaelas-the-looper/pen/wvrqzvm – Alejandro Reyes Dec 21 '21 at 04:18
-
1
-
My use case was to have the svg data loaded from a network and this ES6 Class did the Job.
class SvgToPngConverter {
constructor() {
this._init = this._init.bind(this);
this._cleanUp = this._cleanUp.bind(this);
this.convertFromInput = this.convertFromInput.bind(this);
}
_init() {
this.canvas = document.createElement("canvas");
this.imgPreview = document.createElement("img");
this.imgPreview.style = "position: absolute; top: -9999px";
document.body.appendChild(this.imgPreview);
this.canvasCtx = this.canvas.getContext("2d");
}
_cleanUp() {
document.body.removeChild(this.imgPreview);
}
convertFromInput(input, callback) {
this._init();
let _this = this;
this.imgPreview.onload = function() {
const img = new Image();
_this.canvas.width = _this.imgPreview.clientWidth;
_this.canvas.height = _this.imgPreview.clientHeight;
img.crossOrigin = "anonymous";
img.src = _this.imgPreview.src;
img.onload = function() {
_this.canvasCtx.drawImage(img, 0, 0);
let imgData = _this.canvas.toDataURL("image/png");
if(typeof callback == "function"){
callback(imgData)
}
_this._cleanUp();
};
};
this.imgPreview.src = input;
}
}
Here is how you use it
let input = "https://restcountries.eu/data/afg.svg"
new SvgToPngConverter().convertFromInput(input, function(imgData){
// You now have your png data in base64 (imgData).
// Do what ever you wish with it here.
});
If you want a vanilla JavaScript version, you could head over to Babel website and transpile the code there.
- 845
- 12
- 21
change svg to match your element
function svg2img(){
var svg = document.querySelector('svg');
var xml = new XMLSerializer().serializeToString(svg);
var svg64 = btoa(xml); //for utf8: btoa(unescape(encodeURIComponent(xml)))
var b64start = 'data:image/svg+xml;base64,';
var image64 = b64start + svg64;
return image64;
};svg2img()
- 923
- 12
- 27
-
2it's not working for me, i get this error: `Uncaught TypeError: Failed to execute 'serializeToString' on 'XMLSerializer': parameter 1 is not of type 'Node'.` – Xsmael Dec 14 '18 at 19:45
-
1@Xsmael try to Switch the DOMParser interface http://developer.mozilla.org/en-US/docs/Web/API/DOMParser – Mahdi Khalili Dec 15 '18 at 05:19
-
-
-
1
-
-
1@MTZ you should consider using utf8 part for utf8 characters for example your SVG contains non ASCI characters – Mahdi Khalili May 18 '21 at 04:31
Here a function that works without libraries and returns a Promise:
/**
* converts a base64 encoded data url SVG image to a PNG image
* @param originalBase64 data url of svg image
* @param width target width in pixel of PNG image
* @return {Promise<String>} resolves to png data url of the image
*/
function base64SvgToBase64Png (originalBase64, width) {
return new Promise(resolve => {
let img = document.createElement('img');
img.onload = function () {
document.body.appendChild(img);
let canvas = document.createElement("canvas");
let ratio = (img.clientWidth / img.clientHeight) || 1;
document.body.removeChild(img);
canvas.width = width;
canvas.height = width / ratio;
let ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
try {
let data = canvas.toDataURL('image/png');
resolve(data);
} catch (e) {
resolve(null);
}
};
img.src = originalBase64;
});
}
On Firefox there is an issue for SVGs without set width / height.
See this working example including a fix for the Firefox issue.
- 677
- 8
- 16
-
-
Thanks for testing, I didn't test with Safari before. So there is also no error in the Javascript console, which would be strange? – klues Feb 05 '21 at 08:46
-
Yes, very odd in my experience, but I didn't have more time to waste on it unfortunately – chrisheseltine Feb 06 '21 at 14:53
-
-
1) I suggest attaching to img.onerror also to not risk stalling the Promise in that case. 2) you don't have to attach the image to the document.body. – Fredrik Blomqvist Jun 03 '22 at 10:35
Svg to png can be converted depending on conditions:
- If
svgis in format SVG (string) paths:
- create canvas
- create
new Path2D()and setsvgas parameter - draw path on canvas
- create image and use
canvas.toDataURL()assrc.
example:
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
let svgText = 'M10 10 h 80 v 80 h -80 Z';
let p = new Path2D('M10 10 h 80 v 80 h -80 Z');
ctx.stroke(p);
let url = canvas.toDataURL();
const img = new Image();
img.src = url;
Note that Path2D not supported in ie and partially supported in edge. Polyfill solves that:
https://github.com/nilzona/path2d-polyfill
- Create
svgblob and draw on canvas using.drawImage():
- make canvas element
- make a svgBlob object from the svg xml
- make a url object from domUrl.createObjectURL(svgBlob);
- create an Image object and assign url to image src
- draw image into canvas
- get png data string from canvas: canvas.toDataURL();
Nice description: https://web.archive.org/web/20200125162931/http://ramblings.mcpher.com:80/Home/excelquirks/gassnips/svgtopng
Note that in ie you will get exception on stage of canvas.toDataURL(); It is because IE has too high security restriction and treats canvas as readonly after drawing image there. All other browsers restrict only if image is cross origin.
- Use
canvgJavaScript library. It is separate library but has useful functions.
Like:
ctx.drawSvg(rawSvg);
var dataURL = canvas.toDataURL();
- 8,048
- 3
- 45
- 62
- 2,588
- 3
- 16
- 37
-
1
-
1yes, indeed. I don't know how to reach there now. But description above can be enough for some understanding. Good idea for future co copy some context after the reference – Alex Vovchuk Jun 10 '20 at 16:33
-
I recently discovered a couple of image tracing libraries for JavaScript that indeed are able to build an acceptable approximation to the bitmap, both size and quality. I'm developing this JavaScript library and CLI :
https://www.npmjs.com/package/svg-png-converter
Which provides unified API for all of them, supporting browser and node, non depending on DOM, and a Command line tool.
For converting logos/cartoon/like images it does excellent job. For photos / realism some tweaking is needed since the output size can grow a lot.
It has a playground although right now I'm working on a better one, easier to use, since more features has been added:
https://cancerberosgx.github.io/demos/svg-png-converter/playground/#
- 6,276
- 1
- 30
- 21
There are several ways to convert SVG to PNG using the Canvg library.
In my case, I needed to get the PNG blob from inline SVG.
The library documentation provides an example (see OffscreenCanvas example).
But this method does not work at the moment in Firefox. Yes, you can enable the gfx.offscreencanvas.enabled option in the settings. But will every user on the site do this? :)
However, there is another way that will work in Firefox too.
const el = document.getElementById("some-svg"); //this is our inline SVG
var canvas = document.createElement('canvas'); //create a canvas for the SVG render
canvas.width = el.clientWidth; //set canvas sizes
canvas.height = el.clientHeight;
const svg = new XMLSerializer().serializeToString(el); //convert SVG to string
//render SVG inside canvas
const ctx = canvas.getContext('2d');
const v = await Canvg.fromString(ctx, svg);
await v.render();
let canvasBlob = await new Promise(resolve => canvas.toBlob(resolve));
For the last line thanks to this answer
- 66
- 2
- 8