15

I am trying to load an svg image into canvas for pixel manipulation I need a method like toDataURL or getImageData for svg

on Chrome/Safari I can try doing it through and image and canvas

var img = new Image()
img.onload = function(){
  ctx.drawImage(img,0,0) //this correctly draws the svg image to the canvas! however...
  var dataURL = canvas.toDataURL(); //SECURITY_ERR: DOM Exception 18
  var data = ctx.getImageData(0,0,img.width, img.height).data //also SECURITY_ERR: DOM Exception 18
  }
  img.src = "image.svg" //that is an svg file. (same domain as html file :))

But I get security errors. Any other way?

Here is a live demo of the problem http://clstff.appspot.com/gist/462846 (you can view source)

Drew LeSueur
  • 19,185
  • 29
  • 89
  • 106
  • Is the SVG image coming from a different domain? If it is, you can't call `toDataURL` or `getImageData` because the canvas isn't "origin-clean" anymore. – Matthew Crumley Jul 03 '10 at 22:43
  • It is coming from the *same* domain. I even tried doing `img.src = "data:image/svg+xml;base64,..."` but that also didn't work – Drew LeSueur Jul 03 '10 at 22:57
  • This seems to be a bug in Webkit: https://bugs.webkit.org/show_bug.cgi?id=39059 – stefanw Jul 03 '10 at 23:10
  • 1
    I looked at that bug. It says the problem is with `drawImage`. For me, `drawImage` works. It's the `toDataURL` or `getImageData` that doesn't work after I draw the image. – Drew LeSueur Jul 03 '10 at 23:26
  • 2
    Have you tried using fabric.js to load SVG onto canvas, then get dataurl image? – kangax Sep 16 '11 at 18:12

5 Answers5

11
var dataUrl = 'data:image/svg+xml,'+encodeURIComponent(svgString);
Hubert OG
  • 19,314
  • 7
  • 45
  • 73
7

From: http://www.svgopen.org/2009/papers/12-Using_Canvas_in_SVG/#d4e105

The reason why you cannot use an SVG image element as source for the drawImage method is simple, but painful: the current Canvas specification does not (yet) allow to reference SVGImageElement as source for drawImage and can only cope with HTMLImageElement, HTMLCanvasElement and HTMLVideoelement. This short-coming will hopefully be addressed during the process of defining "SVG in HTML5" behavior and could be extended to allow SVGSVGElement as well. The xhtml:img element in listing 3 uses visibility:hidden as we do not want it to interfere with its visible copy on the Canvas.

manalang
  • 805
  • 1
  • 6
  • 10
0

Yes, you can't do that directly, but you can use canvg for that, I mean, to draw SVG to Canvas, and get data url from Canvas. It's not without bugs, especially for complex gradients processing, but I had some positive experience with it.

vladich
  • 568
  • 6
  • 9
0

Illustrating Hubert OG 's answer with code; 2 remarks first.

First (thank you, Robert Longson), it might work without encodeURIComponent, but you better include it (for example, if you have something like fill="url(#pattern)" the encodeURIComponent is necessary, because of the # character)

Second, the SVG namespace is not optional, but it might not be included in svgElt.outerHTML. Especially, if you have created your svg node via createElementNS, like so

svgElt = document.createElementNS("http://www.w3.org/2000/svg", 'svg')

then a subsequent call to svgElt.outerHTML will not contain the namespace attribute, and this would result in not working code, unless you include the namespace attribute later in some fashion

var imgEnc    = new Image();
var imgEncNS  = new Image();
var prefix    = 'data:image/svg+xml,';
var svgOpen   = '<svg width="200" height="30">';
var svgClose  = '</svg>';
var svgOpenNS = '<svg xmlns="http://www.w3.org/2000/svg" width="200" height="30">';
var rect      = '<rect width="200" height="30" fill="black"/>';
var html      = svgOpen + rect + svgClose;
var htmlNS    = svgOpenNS + rect + svgClose;
var htmlEnc   = window.encodeURIComponent(html);
var htmlEncNS = window.encodeURIComponent(htmlNS);
imgEnc.src   = prefix + htmlEnc;
imgEncNS.src = prefix + htmlEncNS;
function T(txt){
  document.body.appendChild(document.createTextNode(txt));
}
function HR(){ document.body.appendChild(document.createElement('hr')); }
function BR(){ document.body.appendChild(document.createElement('br')); }
function A(arg){ document.body.appendChild(arg); }
T('without namespace'); BR(); A(imgEnc); HR();
T('with namespace'); BR(); A(imgEncNS);

you can fix the missing namespace issue by using function outerXHTML from my answer to this question

or like this

function imgFromSVGnode(node){
    function setNS(elt, parentNS){
        var namespace = elt.namespaceURI;
        if (namespace!==parentNS){
            elt.setAttribute('xmlns', namespace);
        }
        for (var i=0; i<elt.childNodes.length; i++){
            setNS(elt.childNodes[i], namespace);
        }
    }
    setNS(node, null);
    var html = node.outerHTML;
    var dataURL = 'data:image/svg+xml,' + window.encodeURIComponent(html);
    var img = new Image();
    img.src = dataURL;
    return img;
};
mathheadinclouds
  • 3,507
  • 2
  • 27
  • 37
  • https://stackoverflow.com/questions/62109331/namespace-attributes-not-present-in-outerhtml – mathheadinclouds May 31 '20 at 09:42
  • https://stackoverflow.com/questions/8227612/how-to-create-document-objects-with-javascript – mathheadinclouds Jun 03 '20 at 15:00
  • The question you answered **was** caused by very early implementations of the canvas API, when svg images were not yet accepted as source-image. There is no real reasons anyone would face this issue today. You were probably looking for https://stackoverflow.com/q/27230293/3702797 instead. – Kaiido Jul 11 '20 at 04:16
-1

first download a js library svg_todataurl.js and include it

then just paste this code

var SVGtopngDataURL = document.getElementById("svg_element_id").toDataURL("image/png");
yellowcode
  • 37
  • 3