412

I want to convert SVG into bitmap images (like JPEG, PNG, etc.) through JavaScript.

7

16 Answers 16

288

Here is how you can do it through JavaScript:

  1. Use the canvg JavaScript library to render the SVG image using Canvas: https://github.com/gabelerner/canvg
  2. 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?
Sign up to request clarification or add additional context in comments.

9 Comments

This isn't strictly Javascript, but HTML5 as well. This will not work on IE8, or any other browser that does not support HTML5 Canvas.
Thanks for not supporting IE8. People should understand that it's time to move on.
You can now use the JavaScript SVG library Pablo to achieve this (I made it). See the toImage() and also download() for an auto-downloaded image.
canvg is incomplete. e.g. it doesn't support masks.
Note that in late 2022 (and beyond) step 1 is entirely unnecessary. Just draw the SVG onto a canvas, no third party libraries required, then use toDataURL.
|
57

This is an old question, in 2022 we have ES6 and we don't need 3rd party libraries.

Here is a very basic way to convert svg images into other formats.

The trick is to load the svg element as an img element, then use a canvas element to convert the image into the desired format. So, four steps are needed:

  1. Extract svg as xml data string.
  2. Load the xml data string into a img element
  3. Convert the img element to a dataURL using a canvas element
  4. Load the converted dataURL into a new img element

Step 1

Extracting a svg as xml data string is simple, we don't need to convert it as a base64 string. We just serialize it as XML then we encode the string as a URI:

// Data header for a svg image: const dataHeader = 'data:image/svg+xml;charset=utf-8' // Serialize it as xml string: const serializeAsXML = $e => (new XMLSerializer()).serializeToString($e) // Encode URI data as UTF8 data: const encodeAsUTF8 = s => `${dataHeader},${encodeURIComponent(s)}` // Select the element: const $svg = document.getElementById('svg-container').querySelector('svg') // Encode it as a data string: const svgData = encodeAsUTF8(serializeAsXML($svg)) 

Note:

If you need a base64 data you can use this option:

... // Encode URI data as base64 data: const encodeAsB64 = s => `${dataHeader};base64,${btoa(s)}` ... // Encode it as a data string: const svgData = encodeAsB64(serializeAsXML($svg)) 

Step 2

Loading the xml data string into a img element:

// This function returns a Promise whenever the $img is loaded const loadImage = async url => { const $img = document.createElement('img') $img.src = url return new Promise((resolve, reject) => { $img.onload = () => resolve($img) $img.onerror = reject $img.src = url }) } 

Step 3

Converting the img element to a dataURL using a canvas element:

const $canvas = document.createElement('canvas') $canvas.width = img.naturalWidth $canvas.height = img.naturalHeight $canvas.getContext('2d').drawImage(img, 0, 0, img.naturalWidth, img.naturalHeight) return $canvas.toDataURL(`image/${format}`, 1.0) 

Step 4

Loading the converted dataURL into a new img element:

const $img = document.createElement('img') $img.src = dataURL $holder.appendChild($img) 

Here you have a working snippet:

const dataHeader = 'data:image/svg+xml;charset=utf-8' const $svg = document.getElementById('svg-container').querySelector('svg') const $holder = document.getElementById('img-container') const $label = document.getElementById('img-format') const loadImage = async url => { const $img = document.createElement('img') $img.src = url return new Promise((resolve, reject) => { $img.onload = () => resolve($img) $img.onerror = reject }) } const serializeAsXML = $e => (new XMLSerializer()).serializeToString($e) const encodeAsUTF8 = s => `${dataHeader},${encodeURIComponent(s)}` const encodeAsB64 = s => `${dataHeader};base64,${btoa(s)}` const getImageURL = async (svgURL, { format, quality }) => { const img = await loadImage(svgURL) const $canvas = document.createElement('canvas') $canvas.width = img.naturalWidth $canvas.height = img.naturalHeight $canvas.getContext('2d').drawImage(img, 0, 0, img.naturalWidth, img.naturalHeight) return $canvas.toDataURL(`image/${format}`, quality) } const convertSVGtoImg = async e => { const $btn = e.target const format = $btn.dataset.format ?? 'png' $label.textContent = format const svgURL = encodeAsUTF8(serializeAsXML($svg)) // 0 = min; 1 = max; undefined = use browser default const quality = 1 const dataURL = await getImageURL(svgURL, { format, quality }) const $img = document.createElement('img') $img.src = dataURL // remove any existing image $holder.textContent = '' $holder.append($img) } const buttons = [...document.querySelectorAll('[data-format]')] for (const $btn of buttons) { $btn.onclick = convertSVGtoImg }
.wrapper { display: flex; flex-flow: row nowrap; width: 100vw; } .images { display: flex; flex-flow: row nowrap; width: 70%; } .image { width: 50%; display: flex; flex-flow: row wrap; justify-content: center; } .label { width: 100%; text-align: center; }
<div class="wrapper"> <div class="item images"> <div class="image left"> <div class="label">svg</div> <div id="svg-container"> <svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="200" height="200" viewBox="0 0 248 204"> <path fill="#1d9bf0" d="M221.95 51.29c.15 2.17.15 4.34.15 6.53 0 66.73-50.8 143.69-143.69 143.69v-.04c-27.44.04-54.31-7.82-77.41-22.64 3.99.48 8 .72 12.02.73 22.74.02 44.83-7.61 62.72-21.66-21.61-.41-40.56-14.5-47.18-35.07 7.57 1.46 15.37 1.16 22.8-.87-23.56-4.76-40.51-25.46-40.51-49.5v-.64c7.02 3.91 14.88 6.08 22.92 6.32C11.58 63.31 4.74 33.79 18.14 10.71c25.64 31.55 63.47 50.73 104.08 52.76-4.07-17.54 1.49-35.92 14.61-48.25 20.34-19.12 52.33-18.14 71.45 2.19 11.31-2.23 22.15-6.38 32.07-12.26-3.77 11.69-11.66 21.62-22.2 27.93 10.01-1.18 19.79-3.86 29-7.95-6.78 10.16-15.32 19.01-25.2 26.16z"/> </svg> </div> </div> <div class="image right"> <div id="img-format" class="label"></div> <div id="img-container"></div> </div> </div> <div class="item buttons"> <button id="btn-png" data-format="png">PNG</button> <button id="btn-jpg" data-format="jpeg">JPG</button> <button id="btn-webp" data-format="webp">WEBP</button> </div> </div>

4 Comments

in step 2 you are setting the img.src twice, I believe the second one is sufficient
the image in the step 2 still contains a data:image/svg+xml image we use the canvas to convert it into another format (i.e. png, jpeg, ...) If in your case the step 2 is enough great. I presented a complete solution for everybody.
This works great, and the working snippit is extremely helpful. A question: I notice that either const svgData = encodeAsUTF8(serializeAsXML($svg)) or const svgData = encodeAsB64(serializeAsXML($svg)) seem to work equally well. Any reason to prefer one over the other?
I don't understand your answer to @kartsims 's question. I don't think they are asking why you create a second img element in Step 4 (that part is clear). Rather, they are asking why Step 2 has two identical statements $img.src = url when one would be sufficient.
47

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... }); 

3 Comments

canvg need the second parameter to be <svg>...</svg but jquery html() function don't add svg tag, so this code works for me but I needed to edit the canvg live to canvg('canvas', '<svg>'+$("#editor").html()+'</svg>');
@Luckyn if you call $(selector).html() on the parent of your svg element, it will work
@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...
36

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; } 

5 Comments

This is not working in IE11, because of the security issue with .msToBlob()
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
Could you provide a usage example?
This copyStylesInline helped me a lot!! Without adding that, my exported png was looking very weird. Thank you!
I have an issue with using SVGAnimateElement (since animations do not affect the style of the SVG), did someone else encounter it?
33

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; }

4 Comments

This solution does not work for complex SVG (e.g. patterns with images)
Here is your code but optimized, but thank you for that... codepen.io/arcaelas-the-looper/pen/wvrqzvm
You are welcome, u can followme in github: arcaelas
For some reason, when I used URL.createObjectURL(new Blob([svg], { type: 'image/svg+xml' })), some SVGs got tainted (I suspect it has todo with that they have \<style\>s defined in them), but then I tried creating data urls from scratch like so: 'data:image/svg+xml;utf-8,${svg.replace(/#/g, '%23').replace(/\n/g, '%0A')}';, which worked flawlessly! You could also use base64 like this: 'data:image/svg+xml;base64,${btoa(drawing)}';. I am using electron
12

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.

Comments

12

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.onerror = function() { 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.

6 Comments

Didn't work for me in Safari, just hung, no error
Thanks for testing, I didn't test with Safari before. So there is also no error in the Javascript console, which would be strange?
Yes, very odd in my experience, but I didn't have more time to waste on it unfortunately
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.
@FredrikBlomqvist: (1) correct, I've added it. (2) Yes, I do have to attach it to body, in order to get the correct image ratio, see example with rectangular image and see the difference if you remove adding it to body: jsfiddle.net/3kba4gh9 (tested in current FF)
|
11

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() 

8 Comments

it'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 try to Switch the DOMParser interface developer.mozilla.org/en-US/docs/Web/API/DOMParser
@RyanArqueza what how?
@MTZ you should consider using utf8 part for utf8 characters for example your SVG contains non ASCI characters
This generates a SVG base64 string? How is this a png or jpeg?
|
8

A modern-day async function to convert svg -> png:

/** * Converts an svg string into a data:image/png;base64 string. * @param {string} svg * @param {number | undefined} width * @param {number | undefined} height */ async function svg2png(svg, width = 1000, height = 1000) { const img = new Image(); img.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`; await new Promise((resolve, reject) => { img.onload = resolve; img.onerror = reject; }); const canvas = document.createElement("canvas"); [canvas.width, canvas.height] = [width, height]; const ctx = canvas.getContext("2d"); ctx.drawImage(img, 0, 0, width, height); return canvas.toDataURL("image/png"); } 

4 Comments

Theoretically, the img.onload or img.onerror events could be raised before the Promise is constructed so the promise would not resolve, couldn't it?
looking back, it would probably be wiser to assign img.src inside the promise constructor callback, after assigning img.onerror
Or moving the img.src assignment after the promise construction and awaiting it just after, instead of when constructed :)
I'm pretty sure the event loop won't tick until the function starts awaiting, which happens when the promise constructor's callback returns
4
  1. get data URIs from SVG:

    data:image/svg+xml;base64,${btoa(new XMLSerializer().serializeToString(svgElem))}

  2. prepare an Image
  3. create a canvas and use toDataURL to export.

Example

<!-- test data--> <svg width="400" height="400"><g transform="translate(23.915343915343925,-80.03971756398937)" class="glyph" stroke="#000000" fill="#a0a0a0"><path d="M74.97 108.70L74.97 108.70L100.08 110.77Q93.89 147.91 87.35 179.89L87.35 179.89L148.23 179.89L148.23 194.34Q143.76 277.91 113.84 339.81L113.84 339.81Q144.44 363.54 163.70 382.46L163.70 382.46L146.51 402.75Q128.62 384.18 101.80 361.83L101.80 361.83Q75.32 405.85 34.39 436.80L34.39 436.80L17.20 415.82Q57.43 386.93 82.20 345.66L82.20 345.66Q57.78 326.40 27.86 304.39L27.86 304.39Q44.37 257.96 56.75 203.97L56.75 203.97L19.26 203.97L19.26 179.89L61.90 179.89Q69.47 145.16 74.97 108.70ZM93.20 323.99L93.20 323.99Q118.65 272.06 123.12 203.97L123.12 203.97L82.20 203.97Q69.47 260.03 55.71 297.17L55.71 297.17Q76.01 311.61 93.20 323.99ZM160.26 285.13L160.26 260.37L239.71 260.37L239.71 216.01Q268.25 191.24 294.05 155.48L294.05 155.48L170.58 155.48L170.58 130.71L322.94 130.71L322.94 155.48Q297.49 191.93 265.50 223.92L265.50 223.92L265.50 260.37L337.38 260.37L337.38 285.13L265.50 285.13L265.50 397.59Q265.50 431.64 237.65 431.64L237.65 431.64L187.09 431.64L180.21 407.57Q202.22 407.91 227.67 407.91L227.67 407.91Q239.71 407.91 239.71 390.03L239.71 390.03L239.71 285.13L160.26 285.13Z"></path></g></svg> <button title="download">svg2png</button> <script> const output = {"name": "result.png", "width": 64, "height": 64} document.querySelector("button").onclick = () => { const svgElem = document.querySelector("svg") // const uriData = `data:image/svg+xml;base64,${btoa(svgElem.outerHTML)}` // it may fail. const uriData = `data:image/svg+xml;base64,${btoa(new XMLSerializer().serializeToString(svgElem))}` const img = new Image() img.src = uriData img.onload = () => { const canvas = document.createElement("canvas"); [canvas.width, canvas.height] = [output.width, output.height] const ctx = canvas.getContext("2d") ctx.drawImage(img, 0, 0, output.width, output.height) // 👇 download const a = document.createElement("a") const quality = 1.0 // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/imageSmoothingQuality a.href = canvas.toDataURL("image/png", quality) a.download = output.name a.append(canvas) a.click() a.remove() } } </script>

Comments

2

Svg to png can be converted depending on conditions:

  1. If svg is in format SVG (string) paths:
  • create canvas
  • create new Path2D() and set svg as parameter
  • draw path on canvas
  • create image and use canvas.toDataURL() as src.

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

  1. Create svg blob 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.

  1. Use canvg JavaScript library. It is separate library but has useful functions.

Like:

ctx.drawSvg(rawSvg); var dataURL = canvas.toDataURL(); 

3 Comments

3rd link is broken
yes, 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
fixed linkrot. the wayback machine is your friend for fixing linkrot.
1

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

Comments

1

Convert SVG to image (JPEG, PNG, etc.) Most simple approach

<svg width="500" height="400"> <g transform="translate(23.915343915343925,-80.03971756398937)" class="glyph" stroke="#000000" fill="#a0a0a0"> <path d="M74.97 108.70L74.97 108.70L100.08 110.77Q93.89 147.91 87.35 179.89L87.35 179.89L148.23 179.89L148.23 194.34Q143.76 277.91 113.84 339.81L113.84 339.81Q144.44 363.54 163.70 382.46L163.70 382.46L146.51 402.75Q128.62 384.18 101.80 361.83L101.80 361.83Q75.32 405.85 34.39 436.80L34.39 436.80L17.20 415.82Q57.43 386.93 82.20 345.66L82.20 345.66Q57.78 326.40 27.86 304.39L27.86 304.39Q44.37 257.96 56.75 203.97L56.75 203.97L19.26 203.97L19.26 179.89L61.90 179.89Q69.47 145.16 74.97 108.70ZM93.20 323.99L93.20 323.99Q118.65 272.06 123.12 203.97L123.12 203.97L82.20 203.97Q69.47 260.03 55.71 297.17L55.71 297.17Q76.01 311.61 93.20 323.99ZM160.26 285.13L160.26 260.37L239.71 260.37L239.71 216.01Q268.25 191.24 294.05 155.48L294.05 155.48L170.58 155.48L170.58 130.71L322.94 130.71L322.94 155.48Q297.49 191.93 265.50 223.92L265.50 223.92L265.50 260.37L337.38 260.37L337.38 285.13L265.50 285.13L265.50 397.59Q265.50 431.64 237.65 431.64L237.65 431.64L187.09 431.64L180.21 407.57Q202.22 407.91 227.67 407.91L227.67 407.91Q239.71 407.91 239.71 390.03L239.71 390.03L239.71 285.13L160.26 285.13Z"></path> </g> </svg> <button title="download" class="btn btn-primary">Download</button> <script> document.querySelector("button").onclick = () => { var svg = document.querySelector("svg"); var Data = `data:image/svg+xml;base64,${btoa(new XMLSerializer().serializeToString(svg))}`; var img = new Image(); img.src = Data; img.onload = () => { var canvas = document.createElement("canvas"); var bbox = svg.getBBox(); canvas.width = bbox.width; canvas.height = bbox.height; var ctx = canvas.getContext("2d"); ctx.imageSmoothingQuality = "high"; ctx.drawImage(img, 0, 0, canvas.width, canvas.height); var a = document.createElement("a"); a.href = canvas.toDataURL("image/png"); a.download = "Tooth_Details.png"; a.click(); } } </script> 

1 Comment

Please add some explanations to your answers to clarify how this approach differs from the previous answers or has advantages compared to other solutions.
0

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/#

Comments

0

Here are my 2 cents. Somehow Download anchor tag is not working as expected in code snippet, however it was working fine in Chrome.

Here is working jsFiddle

const waitForImage = imgElem => new Promise(resolve => imgElem.complete ? resolve() : imgElem.onload = imgElem.onerror = resolve); const svgToImgDownload = ext => { if (!['png', 'jpg', 'webp'].includes(ext)) return; const _svg = document.querySelector("#svg_container").querySelector('svg'); const xmlSerializer = new XMLSerializer(); let _svgStr = xmlSerializer.serializeToString(_svg); const img = document.createElement('img'); img.src = 'data:image/svg+xml;base64,' + window.btoa(_svgStr); waitForImage(img) .then(_ => { const canvas = document.createElement('canvas'); canvas.width = _svg.clientWidth; canvas.height = _svg.clientHeight; canvas.getContext('2d').drawImage(img, 0, 0, _svg.clientWidth, _svg.clientHeight); return canvas.toDataURL('image/' + (ext == 'jpg' ? 'jpeg' : ext), 1.0); }) .then(dataURL => { console.log(dataURL); document.querySelector("#img_download_btn").innerHTML = `<a href="${dataURL}" download="img.${ext}">Download</a>`; }) .catch(console.error); }; document.querySelector('#map2Png').addEventListener('click', _ => svgToImgDownload('png')); document.querySelector('#map2Jpg').addEventListener('click', _ => svgToImgDownload('jpg')); document.querySelector('#map2Webp').addEventListener('click', _ => svgToImgDownload('webp'));
<div id="svg_container" style="float: left; width: 50%"> <svg width="200" height="200" viewBox="-100 -100 200 200"> <circle cx="0" cy="20" r="70" fill="#D1495B" /> <circle cx="0" cy="-75" r="12" fill="none" stroke="#F79257" stroke-width="2" /> <rect x="-17.5" y="-65" width="35" height="20" fill="#F79257" /> </svg> </div> <div> <button id="map2Png">PNG</button> <button id="map2Jpg">JPG</button> <button id="map2Webp">WEBP</button> </div> <div id="img_download_btn"></div>

1 Comment

if (!['jpg'].includes(ext)) return and ext == 'jpg' ? 'jpeg' will never reach if someone uses jpeg, guessing you meant ['png','jpg','jpeg','webp']
0

Typescript mix of the @klues and @Nitsan BenHanoch anwswers to converts an svg string into a data:image/png;base64 string keeping the aspect ratio

/** * Converts an svg string into a data:image/png;base64 string keeping the aspect ratio */ function svgStringToBase64Png( svg: string, width: number = 1500, ): Promise<string | null> { const svgUrl = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}` return new Promise<string | null>((resolve) => { const img = document.createElement('img') img.onload = function () { document.body.appendChild(img) const canvas = document.createElement('canvas') const ratio = img.clientWidth / img.clientHeight || 1 document.body.removeChild(img) canvas.width = width canvas.height = width / ratio const ctx = canvas.getContext('2d') ctx?.drawImage(img, 0, 0, canvas.width, canvas.height) try { const data = canvas.toDataURL('image/png') resolve(data) } catch (e) { resolve(null) } } img.onerror = function () { resolve(null) } img.src = svgUrl }) } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.