24

I am trying to convert an external svg icon to a base64 png using a canvas. It is working in all browsers except Firefox, which throws an error "NS_ERROR_NOT_AVAILABLE".

var img = new Image(); img.src = "icon.svg"; img.onload = function() { var canvas = document.createElement("canvas"); canvas.width = this.width; canvas.height = this.height; var ctx = canvas.getContext("2d"); ctx.drawImage(this, 0, 0); var dataURL = canvas.toDataURL("image/png"); return dataURL; }; 

Can anyone help me on this please? Thanks in advance.

5
  • 1
    Does your svg icon have width and height attributes? If it does are they percentages? Commented Feb 24, 2015 at 8:02
  • Hi Robert, this is a svg file not a svg dom element and we can assign any width/height to it. I am using this svg in the page as <image src="icon.svg" width="32" height="32"> Commented Feb 24, 2015 at 9:33
  • That doesn't answer my question. Does icon.svg have width/height attributes on the root <svg> element. If it does are those attribute values percentages? Commented Feb 24, 2015 at 9:34
  • The svg icon is generated using Adobe Illustrator and I dont see any width/height in the svg file. <svg version="1.1" id="Layer_1" xmlns:x="&ns_extend;" xmlns:i="&ns_ai;" xmlns:graph="&ns_graphs;" xmlns="w3.org/2000/svg" xmlns:xlink="w3.org/1999/xlink" xmlns:a="ns.adobe.com/AdobeSVGViewerExtensions/3.0" x="0px" y="0px" viewBox="0 0 32 32" enable-background="new 0 0 32 32" xml:space="preserve"> <metadata><?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> Commented Feb 24, 2015 at 9:46
  • 4
    I had the same problem except that the call to drawImage() didn't even throw an error. Adding width/height in the <svg> did fix it! Commented Jul 7, 2016 at 22:10

3 Answers 3

61

Firefox should draw this SVG to canvas without problems these days.

Firefox used to only support drawing SVG images to canvas when the svg file had width/height attributes on the root <svg> element and those width/height attributes were not percentages. That bug was fixed some time ago though.

Sign up to request clarification or add additional context in comments.

3 Comments

care to post a link to that bug? It'd be interesting to track fix progress
@tivoni I've added a link, but there will be no progress on the bug till w3c defines what should happen.
This comment shows a possible workaround: bugzilla.mozilla.org/show_bug.cgi?id=700533#c39
6

As mentioned, this is an open bug caused by limitations on what Firefox accepts as specification for SVG sizes when drawing to a canvas. There is a workaround.

Firefox requires explicit width and height attributes in the SVG itself. We can add these by getting the SVG as XML and modifying it.

var img = new Image(); var src = "icon.svg"; // request the XML of your svg file var request = new XMLHttpRequest(); request.open('GET', src, true) request.onload = function() { // once the request returns, parse the response and get the SVG var parser = new DOMParser(); var result = parser.parseFromString(request.responseText, 'text/xml'); var inlineSVG = result.getElementsByTagName("svg")[0]; // add the attributes Firefox needs. These should be absolute values, not relative inlineSVG.setAttribute('width', '48px'); inlineSVG.setAttribute('height', '48px'); // convert the SVG to a data uri var svg64 = btoa(new XMLSerializer().serializeToString(inlineSVG)); var image64 = 'data:image/svg+xml;base64,' + svg64; // set that as your image source img.src = img64; // do your canvas work img.onload = function() { var canvas = document.createElement("canvas"); canvas.width = this.width; canvas.height = this.height; var ctx = canvas.getContext("2d"); ctx.drawImage(this, 0, 0); var dataURL = canvas.toDataURL("image/png"); return dataURL; }; } // send the request request.send();

This is the most basic version of this solution, and includes no handling for errors when retrieving the XML. Better error handling is demonstrated in this inline-svg handler (circa line 110) from which I derived part of this method.

2 Comments

This is a really good answer, and it worked for my implementation for most SVGs. To improve its compatibility, I replaced the btoa(new XMLSerializer().serializeToString(inlineSVG)); line with Buffer.Buffer.from(new XMLSerializer().serializeToString(inlineSVG), 'utf').toString('base64'); (Using the standalone NodeJS Buffer plugin, which can be downloaded here: https://bundle.run/[email protected]
phrogz.net/SVG/svg_to_png.xhtml is different in Firefox, Chrome, and Safari, so you really need to have control of the SVG file, or use the method in this answer.
0

This isn't the most robust solution, but this hack worked for our purposes. Extract viewBox data and use these dimensions for the width/height attributes.

This only works if the first viewBox encountered has a size that accurately can represent the size of the SVG document, which will not be true for all cases.

 // @svgDoc is some SVG document. let svgSize = getSvgViewBox(svgDoc); // No SVG size? if (!svgSize.width || !svgSize.height) { console.log('Image is missing width or height'); // Have size, resolve with new SVG image data. } else { // Rewrite SVG doc let unit = 'px'; $('svg', svgDoc).attr('width', svgSize.width + unit); $('svg', svgDoc).attr('height', svgSize.height + unit); // Get data URL for new SVG. let svgDataUrl = svgDocToDataURL(svgDoc); } function getSvgViewBox(svgDoc) { if (svgDoc) { // Get viewBox from SVG doc. let viewBox = $(svgDoc).find('svg').prop('viewBox').baseVal; // Have viewBox? if (viewBox) { return { width: viewBox.width, height: viewBox.height } } } // If here, no viewBox found so return null case. return { width: null, height: null } } function svgDocToDataURL(svgDoc, base64) { // Set SVG prefix. const svgPrefix = "data:image/svg+xml;"; // Serialize SVG doc. var svgData = new XMLSerializer().serializeToString(svgDoc); // Base64? Return Base64-encoding for data URL. if (base64) { var base64Data = btoa(svgData); return svgPrefix + "base64," + base64Data; // Nope, not Base64. Return URL-encoding for data URL. } else { var urlData = encodeURIComponent(svgData); return svgPrefix + "charset=utf8," + urlData; } } 

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.