How do I save/export an SVG file after creating an SVG with D3.js (IE, safari and chrome)?

后端 未结 6 1012
佛祖请我去吃肉
佛祖请我去吃肉 2020-11-28 19:50

I currently have a website using D3 and I\'d like the user to have the option to save the SVG as an SVG file. I\'m using crowbar.js to do this, but it only works on chrome.

相关标签:
6条回答
  • 2020-11-28 20:05

    There are 5 steps. I often use this method to output inline svg.

    1. get inline svg element to output.
    2. get svg source by XMLSerializer.
    3. add name spaces of svg and xlink.
    4. construct url data scheme of svg by encodeURIComponent method.
    5. set this url to href attribute of some "a" element, and right click this link to download svg file.

    //get svg element.
    var svg = document.getElementById("svg");
    
    //get svg source.
    var serializer = new XMLSerializer();
    var source = serializer.serializeToString(svg);
    
    //add name spaces.
    if(!source.match(/^<svg[^>]+xmlns="http\:\/\/www\.w3\.org\/2000\/svg"/)){
        source = source.replace(/^<svg/, '<svg xmlns="http://www.w3.org/2000/svg"');
    }
    if(!source.match(/^<svg[^>]+"http\:\/\/www\.w3\.org\/1999\/xlink"/)){
        source = source.replace(/^<svg/, '<svg xmlns:xlink="http://www.w3.org/1999/xlink"');
    }
    
    //add xml declaration
    source = '<?xml version="1.0" standalone="no"?>\r\n' + source;
    
    //convert svg source to URI data scheme.
    var url = "data:image/svg+xml;charset=utf-8,"+encodeURIComponent(source);
    
    //set url value to a element's href attribute.
    document.getElementById("link").href = url;
    //you can download svg file by right click menu.
    
    0 讨论(0)
  • 2020-11-28 20:05

    I tryed every solution here and nothing worked for me. My picture was always smaller than my d3.js canvas.

    I had to set the canvas width, height then do a clearRect on the context to make it works. Here is my working version

    Export function:

    var svgHtml = document.getElementById("d3-canvas"),
        svgData = new XMLSerializer().serializeToString(svgHtml),
        svgBlob = new Blob([svgData], {type:"image/svg+xml;charset=utf-8"}),
        bounding = svgHtml.getBoundingClientRect(),
        width = bounding.width * 2,
        height = bounding.height * 2,
        canvas = document.createElement("canvas"),
        context = canvas.getContext("2d"),
        exportFileName = 'd3-graph-image.png';
    
    //Set the canvas width and height before loading the new Image
    canvas.width = width;
    canvas.height = height;
    
    var image = new Image();
    image.onload = function() {
        //Clear the context
        context.clearRect(0, 0, width, height);
        context.drawImage(image, 0, 0, width, height);
    
        //Create blob and save if with FileSaver.js
        canvas.toBlob(function(blob) {
            saveAs(blob, exportFileName);
        });     
    };
    var svgUrl = URL.createObjectURL(svgBlob);
    image.src = svgUrl;
    

    It use FileSaver.js to save the file.

    This is my canvas creation, note that i solve the namespace issue here

    d3.js canvas creation:

    var canvas = d3.select("body")
        .insert("svg")
        .attr('id', 'd3-canvas')
        //Solve the namespace issue (xmlns and xlink)
        .attr("xmlns", "http://www.w3.org/2000/svg")
        .attr("xmlns:xlink", "http://www.w3.org/1999/xlink")
        .attr("width", width)
        .attr("height", height);
    
    0 讨论(0)
  • 2020-11-28 20:11

    I know this has already been answered, and that answer works well most of the time. However I found that it failed on Chrome (but not Firefox) if the svg image was large-ish (about 1MB). It works if you go back to using a Blob construct, as described here and here. The only difference is the type argument. In my code I wanted a single button press to download the svg for the user, which I accomplished with:

    var svgData = $("#figureSvg")[0].outerHTML;
    var svgBlob = new Blob([svgData], {type:"image/svg+xml;charset=utf-8"});
    var svgUrl = URL.createObjectURL(svgBlob);
    var downloadLink = document.createElement("a");
    downloadLink.href = svgUrl;
    downloadLink.download = "newesttree.svg";
    document.body.appendChild(downloadLink);
    downloadLink.click();
    document.body.removeChild(downloadLink);
    

    October 2019 edit: Comments have indicated that this code will work even without appending downloadLink to document.body and subsequently removing it after click(). I believe that used to work on Firefox, but as of now it no longer does (Firefox requires that you append and then remove downloadLink). The code works on Chrome either way.

    0 讨论(0)
  • 2020-11-28 20:13

    While this question has been answered, I created a small library called SaveSVG which can help save D3.js generated SVG which use external stylesheets or external definition files (using <use> and def) tags.

    0 讨论(0)
  • 2020-11-28 20:14

    For this snippet to work you need FileSaver.js.

    function save_as_svg(){
    
    
            var svg_data = document.getElementById("svg").innerHTML //put id of your svg element here
    
            var head = '<svg title="graph" version="1.1" xmlns="http://www.w3.org/2000/svg">'
    
            //if you have some additional styling like graph edges put them inside <style> tag
    
            var style = '<style>circle {cursor: pointer;stroke-width: 1.5px;}text {font: 10px arial;}path {stroke: DimGrey;stroke-width: 1.5px;}</style>'
    
            var full_svg = head +  style + svg_data + "</svg>"
            var blob = new Blob([full_svg], {type: "image/svg+xml"});  
            saveAs(blob, "graph.svg");
    
    
    };
    
    0 讨论(0)
  • 2020-11-28 20:20

    Combining Dave's and defghi1977 answers. Here is a reusable function:

    function saveSvg(svgEl, name) {
        svgEl.setAttribute("xmlns", "http://www.w3.org/2000/svg");
        var svgData = svgEl.outerHTML;
        var preface = '<?xml version="1.0" standalone="no"?>\r\n';
        var svgBlob = new Blob([preface, svgData], {type:"image/svg+xml;charset=utf-8"});
        var svgUrl = URL.createObjectURL(svgBlob);
        var downloadLink = document.createElement("a");
        downloadLink.href = svgUrl;
        downloadLink.download = name;
        document.body.appendChild(downloadLink);
        downloadLink.click();
        document.body.removeChild(downloadLink);
    }
    

    Invocation example:

    saveSvg(svg, 'test.svg')
    
    0 讨论(0)
提交回复
热议问题