33

I'm working on a client-side/javascript function to save or convert an existing D3-SVG graph into a file. I've searched a lot and found some recommendations, mainly using canvas.toDataURL().

I have no <canvas> in my page, and instead using:d3.select("body").append("svg").... I've also tried to append the SVG to the <canvas> but nothing happens.

Could you please help me to resolve this exception:

Uncaught TypeError: Object #<SVGSVGElement> has no method 'toDataURL' 

Thank you

Lars Kotthoff
  • 104,352
  • 13
  • 194
  • 196
Moein
  • 709
  • 2
  • 8
  • 16
  • For in-browser conversion to png, check out http://stackoverflow.com/questions/3975499/convert-svg-to-image-jpeg-png-etc-in-the-browser – widged Apr 17 '13 at 02:14
  • If it doesn't need to be at runtime, tools like casperjs let you take a screenshot of any element in the page http://casperjs.org/api.html#casper.captureSelector – widged Apr 17 '13 at 02:18
  • For pdf export, see http://stackoverflow.com/questions/3360641/how-to-insert-a-svg-file-in-a-pdf-document. – widged Apr 17 '13 at 02:31

4 Answers4

20

To display your svg within a canvas, you first have to convert it using a parser/renderer utility such as http://code.google.com/p/canvg/

(code adapted from: Convert SVG to image (JPEG, PNG, etc.) in the browser, not tested)

// the canvg call that takes the svg xml and converts it to a canvas
canvg('canvas', $("#my-svg").html());

// the canvas calls to output a png
var canvas = document.getElementById("canvas");
var img = canvas.toDataURL("image/png");
Community
  • 1
  • 1
widged
  • 2,640
  • 19
  • 25
  • 1
    The first line will not work because 1. canvg awaits for the URL, not the actual XML data and 2. $('svg').html() would not work either - you'll need innerSVG JS library to get access to inner SVG XML. – WASD42 Aug 13 '13 at 11:10
  • `canvg(document.getElementById('drawingArea'), '...')` is given as usage https://github.com/gabelerner/canvg. Changed `$('svg').html() ` to `$("#my-svg").html()`. – widged Sep 08 '15 at 00:51
  • 1
    Once you have img, how do you trigger a download of it? – rjurney Jul 05 '16 at 14:56
  • $("#my-svg").html() won't return the opening and closing `` tags; to get this to work, I had to call `canvg('canvas', "" + $("my-svg").html() + "")`. – Randoms May 01 '17 at 18:30
  • this does not seem to work in the browser when I use the npm build. – vijayst Jun 23 '18 at 12:59
6

Just a heads up that I turned this concept into a small JavaScript library: https://github.com/krunkosaurus/simg

It simply converts any SVG to an image to swap out or trigger a download. Idea taken from here: http://techslides.com/save-svg-as-an-image/

Mauvis Ledford
  • 38,054
  • 15
  • 78
  • 86
  • im trying you js library but is not working. The code from techslides does work, although not in Firefox 33.0 One thing i notice is that your code references the a.href to the img src, instead of the canvas data URL. Any ideas? – Sebastian Oct 28 '14 at 17:04
  • Also, im using a lineChart from DC.js and the png file looks like css styles were not applied, even though i pasted all dc.css in the html. – Sebastian Oct 28 '14 at 17:12
  • @sebastian: Just a heads up that after receiving a few pull request I went ahead and updated the codebase with documentation and more features. – Mauvis Ledford Sep 18 '15 at 05:56
  • This throws an error in Chrome as it refuses to deal with MIME – SumNeuron May 06 '17 at 09:52
  • Working good for me except that `d3.js` produced chart is black and white, colors got lost. Any ideas? – Nikita Vlasenko Mar 16 '18 at 23:52
5

As pointed out by @Premasagar in this comment on this question Convert SVG to image (JPEG, PNG, etc.) in the browser

If the borwser supports both SVG and canvas you can use this technique https://svgopen.org/2010/papers/62-From_SVG_to_Canvas_and_Back/index.html

function importSVG(sourceSVG, targetCanvas) {
    // https://developer.mozilla.org/en/XMLSerializer
    svg_xml = (new XMLSerializer()).serializeToString(sourceSVG);
    var ctx = targetCanvas.getContext('2d');

    // this is just a JavaScript (HTML) image
    var img = new Image();
    // http://en.wikipedia.org/wiki/SVG#Native_support
    // https://developer.mozilla.org/en/DOM/window.btoa
    img.src = "data:image/svg+xml;base64," + btoa(svg_xml);

    img.onload = function() {
        // after this, Canvas’ origin-clean is DIRTY
        ctx.drawImage(img, 0, 0);
    }
}
Community
  • 1
  • 1
Pavel Nikolov
  • 9,023
  • 5
  • 42
  • 55
3

The Simg library created and suggest by Mauvis Ledford above worked great for allowing my svg charts created with Dimple to be downloaded.

I did however need to change one aspect of the code to make it work. Inside of the toString() prototype, inside the forEach loop (line 37), if you change "svg.setAttribute(..)" to "svg[0].setAttribute" it will alleviate the "setAttribute(..) is not a function" error. Similarly the same needs to be done right below in the return statement, appending "[0]" after svg (line 39).

I also had to manually edit the "canvas.width" and "canvas.height" (lines 48 & 49) assignments in the toCanvas() prototype, in order to make the downloaded image a more correct size (it was previously just downloading a static 300x150 square in the top left corner of the chart).

Psilocybic
  • 41
  • 3