Firefox error rendering an SVG image to HTML5 canvas with drawImage

前端 未结 3 1194
失恋的感觉
失恋的感觉 2020-11-28 16:27

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\".

相关标签:
3条回答
  • 2020-11-28 16:59

    Firefox does not support drawing SVG images to canvas unless the svg file has width/height attributes on the root <svg> element and those width/height attributes are not percentages. This is a longstanding bug.

    You will need to edit the icon.svg file so it meets the above criteria.

    0 讨论(0)
  • 2020-11-28 17:07

    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;
       }
    }
    
    0 讨论(0)
  • 2020-11-28 17:12

    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;
    	
    	// 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(svg));
    	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.

    0 讨论(0)
提交回复
热议问题