mac13k
mac13k

Reputation: 2663

D3: SVG downloadable, but attributes gone

I need to make my D3-created SVG charts downloadable. I found one answer to this question that suggested encoding SVG's XML to base64, but then I found this post that said plain text should be OK too, so I came up with something like this:

<!DOCTYPE html>
<html>
<head>
  <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
</head>
<body>
  <script>
    var data = [22, 33, 11, 55, 44];
    var svg = d3.select('body').append('svg')
      .attr('id', 'chart')
      .attr('version', '1.1')
      .attr('xmlns', 'http://www.w3.org/2000/svg')
      .attr('width', 600)
      .attr('height', 400)
      .style('background-color', 'powderblue')
      .selectAll('circle')
        .data(data)
        .enter().append('circle')
          .attr('cx', function(d) { return 8*d; })
          .attr('cy', function(d) { return 4*d; })
          .attr('r', function(d) { return d; })
          .style('fill', function(d) { return d3.rgb(2*d, 4*d, 3*d); })
    ;
    d3.select('body').append('a')
      .attr('href-lang', 'image/svg+xml')
      .attr('href', 'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg">' + unescape(svg.node().parentNode.innerHTML) + '</svg>')
      .text('Download')
    ;
  </script>
</body>
</html>

The above code works partially, because the download link can be opened in the browser or downloaded to a file, but in each case only the circles are visible and all else (ie. width, height, bg color) is lost, so the SVG is only properly displayed in the original HTML page. What is the right way to get the full code of SVG created with D3? I tried svg.html() and d3.select(svg).html(), but it returns null.

Upvotes: 1

Views: 969

Answers (1)

Kaiido
Kaiido

Reputation: 137044

You could try saving the parent's outerHTML.
.attr('href', 'data:image/svg+xml;utf8,' + unescape(svg.node().parentNode.outerHTML))

<!DOCTYPE html>
<html>
<head>
  <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
</head>
<body>
  <script>
    var data = [22, 33, 11, 55, 44];
    var svg = d3.select('body').append('svg')
      .attr('id', 'chart')
      .attr('version', '1.1')
      .attr('xmlns', 'http://www.w3.org/2000/svg')
      .attr('width', 600)
      .attr('height', 400)
      .style('background-color', 'powderblue')
      .selectAll('circle')
        .data(data)
        .enter().append('circle')
          .attr('cx', function(d) { return 8*d; })
          .attr('cy', function(d) { return 4*d; })
          .attr('r', function(d) { return d; })
          .style('fill', function(d) { return d3.rgb(2*d, 4*d, 3*d); })
    ;
    d3.select('body').append('a')
      .attr('href-lang', 'image/svg+xml')
      .attr('href', 'data:image/svg+xml; charset=utf8, ' + unescape(svg.node().parentNode.outerHTML))
      .text('Download')
    ;
  </script>
</body>
</html>

Ps: You may also want to add the download attribute on your link, for recent browsers users can directly download it as a file.

PPs: To force the width height of the new file, you should set the viewbox attribute of your svg

PPPs: To have a fully valid svg file (i.e with DOCTYPE set etc.) I think you will have to add those four more lines :

var svgDocType = document.implementation.createDocumentType('svg',  "-//W3C//DTD SVG 1.1//EN", "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd");
var svgDoc= document.implementation.createDocument ('http://www.w3.org/2000/svg', 'svg', svgDocType);
svgDoc.replaceChild(svg.node().parentNode.cloneNode(true), svgDoc.documentElement);
svgData = (new XMLSerializer()).serializeToString(svgDoc);

var data = [22, 33, 11, 55, 44];
    var svg = d3.select('body').append('svg')
      .attr('id', 'chart')
      .attr('version', '1.1')
      .attr('xmlns', 'http://www.w3.org/2000/svg')
      .attr('width', 600)
      .attr('height', 400)
      .style('background-color', 'powderblue')
      .selectAll('circle')
        .data(data)
        .enter().append('circle')
          .attr('cx', function(d) { return 8*d; })
          .attr('cy', function(d) { return 4*d; })
          .attr('r', function(d) { return d; })
          .style('fill', function(d) { return d3.rgb(2*d, 4*d, 3*d); })
    ;
    var svgDocType = document.implementation.createDocumentType('svg',  "-//W3C//DTD SVG 1.1//EN", "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd");
    var svgDoc= document.implementation.createDocument ('http://www.w3.org/2000/svg', 'svg', svgDocType);
    svgDoc.replaceChild(svg.node().parentNode.cloneNode(true), svgDoc.documentElement);
    svgData = (new XMLSerializer()).serializeToString(svgDoc);
    d3.select('body').append('a')
      .attr('href-lang', 'image/svg+xml')
      .attr('href', 'data:image/svg+xml; charset=utf8, ' + encodeURIComponent(svgData))
      .text('Download')
    ;
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

Upvotes: 4

Related Questions