SVG to Canvas with d3.js

Has anyone tried using a svg to canvas library when creating d3.js visualizations? I've tried to use canvg.js and d3.js to convert svg to canvas from within an android 2.3 application webview, but when I call:

  svg.selectAll(".axis")
      .data(d3.range(angle.domain()[1]))
    .enter().append("g")
      .attr("class", "axis")
      .attr("transform", function(d) { return "rotate(" + angle(d) * 180 / Math.PI + ")"; })
    .call(d3.svg.axis()
      .scale(radius.copy().range([-5, -outerRadius]))
      .ticks(5)
      .orient("left"))
    .append("text")
      .attr("y", 
        function (d) {
          if (window.innerWidth < 455){
            console.log("innerWidth less than 455: ",window.innerWidth);
            return -(window.innerHeight * .33);
          }
          else{
            console.log("innerWidth greater than 455: ",window.innerWidth);
            return -(window.innerHeight * .33);
          }
        })
      .attr("dy", ".71em")
      .attr("text-anchor", "middle")
      .text(function(d, i) { return capitalMeta[i]; })
      .attr("style","font-size:12px;");

I get the error: Uncaught TypeError: Cannot call method 'setProperty' of null http://mbostock.github.com/d3/d3.js?2.5.0:1707

Would some sort of headless browser application, or a server side js parser work? Has anyone encountered this before?

Here's one way you could write your svg to canvas (and then save the result as a png or whatever):

// Create an export button
d3.select("body")
    .append("button")
    .html("Export")
    .on("click",svgToCanvas);

var w = 100, // or whatever your svg width is
    h = 100;

// Create the export function - this will just export 
// the first svg element it finds
function svgToCanvas(){
    // Select the first svg element
    var svg = d3.select("svg")[0][0],
        img = new Image(),
        serializer = new XMLSerializer(),
        svgStr = serializer.serializeToString(svg);

    img.src = 'data:image/svg+xml;base64,'+window.btoa(svgStr);

    // You could also use the actual string without base64 encoding it:
    //img.src = "data:image/svg+xml;utf8," + svgStr;

    var canvas = document.createElement("canvas");
    document.body.appendChild(canvas);

    canvas.width = w;
    canvas.height = h;
    canvas.getContext("2d").drawImage(img,0,0,w,h);
    // Now save as png or whatever
};

Have you tried the same code on a browser supporting SVG to see if it's a problem with webview? Then try this example using canvg or this one using DOM serialization. For server-side rendering, you could start with this example for how to render it to canvas server-side using Node.js.

I've not tried a library, but have rendered a d3-produced SVG to a canvas following this post on MDN.

This code is a quick mish-mash of the MDN and some jQuery, that'll you'll need to tidy up, and it has no error- or platfrom-checking, but it works, and I hope it helps.

$(document.body).append(
    '<canvas id="canvas" width="'+diameter+'" height="'+diameter+'"></canvas>'
);

// https://developer.mozilla.org/en/docs/HTML/Canvas/Drawing_DOM_objects_into_a_canvas
var el = $($('svg')[0]);
var svgMarkup = '<svg xmlns="http://www.w3.org/2000/svg"'
+ ' class="'  + el.attr('class') +'"'
+ ' width="'  + el.attr('width') +'"'
+ ' height="' + el.attr('height') +'"'
+ '>'
+ $('svg')[0].innerHTML.toString()+'</svg>';
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var DOMURL = this.URL || this.webkitURL || this;
var img = new Image();
var svg = new Blob([svgMarkup], {type: "image/svg+xml;charset=utf-8"});
var url = DOMURL.createObjectURL(svg);
img.onload = function() {
    ctx.drawImage(img, 0, 0);
    alert('ok');
    DOMURL.revokeObjectURL(url);
};
img.src = url;