how to save/ export inline SVG styled with css from browser to image file

前端 未结 4 1647
爱一瞬间的悲伤
爱一瞬间的悲伤 2021-02-01 17:43

I have a web app that is generating inline SVG graphics in the client on the fly based on user interaction. The graphic is defined partly by element attributes and partially by

相关标签:
4条回答
  • 2021-02-01 18:00

    You will need to explicitly set the calculated css styles as SVG dom style properties for each SVG element before saving it. Here is an example:

    <html>
        <body>
        <!-- in this example the inline svg has black backgroud-->
        <svg id="svg" xmlns="http://www.w3.org/2000/svg" version="1.1" height="190">
            <polygon id="polygon" points="100,10 40,180 190,60 10,60 160,180" style="stroke:purple;stroke-width:5;">
        </svg>
        <style>
            /* the external svg style makes svg shape background red */
            polygon 
            {
                fill:red;
            }
        </style>
    <svg id="emptysvg" xmlns="http://www.w3.org/2000/svg" version="1.1" height="2"/>
    <br/>
    image original:
    <canvas id="canvasOriginal" height="190" width="190" ></canvas>
    <br/>
    image computed:
    <canvas id="canvasComputed" height="190" width="190" ></canvas>
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
    <script type="text/javascript" src="http://canvg.googlecode.com/svn/trunk/rgbcolor.js"></script> 
    <script type="text/javascript" src="http://canvg.googlecode.com/svn/trunk/StackBlur.js"></script>
    <script type="text/javascript" src="http://canvg.googlecode.com/svn/trunk/canvg.js"></script> 
    <script src="http://www.nihilogic.dk/labs/canvas2image/canvas2image.js"></script>
    <script type="text/javascript">
    var svg = $('#svg')[0];
    var canvasOriginal = $('#canvasOriginal')[0];
    var ctxOriginal = canvasOriginal.getContext('2d');
    var canvasComputed=$('#canvasComputed')[0];
    var ctxConverted=canvasComputed.getContext("2d");
    // this saves the inline svg to canvas without external css
    canvg('canvasOriginal', new XMLSerializer().serializeToString(svg));
    // we need to calculate the difference between the empty svg and ours
    var emptySvgDeclarationComputed = getComputedStyle($('#emptysvg')[0]);
    function explicitlySetStyle (element) {
        var cSSStyleDeclarationComputed = getComputedStyle(element);
        var i, len, key, value;
        var computedStyleStr = "";
        for (i=0, len=cSSStyleDeclarationComputed.length; i<len; i++) {
            key=cSSStyleDeclarationComputed[i];
            value=cSSStyleDeclarationComputed.getPropertyValue(key);
            if (value!==emptySvgDeclarationComputed.getPropertyValue(key)) {
                computedStyleStr+=key+":"+value+";";
            }
        }
        element.setAttribute('style', computedStyleStr);
    }
    function traverse(obj){
        var tree = [];
        tree.push(obj);
        if (obj.hasChildNodes()) {
            var child = obj.firstChild;
            while (child) {
                if (child.nodeType === 1 && child.nodeName != 'SCRIPT'){
                    tree.push(child);
                }
                child = child.nextSibling;
            }
        }
        return tree;
    }
    // hardcode computed css styles inside svg
    var allElements = traverse(svg);
    var i = allElements.length;
    while (i--){
        explicitlySetStyle(allElements[i]);
    }
    // this saves the inline svg to canvas with computed styles
    canvg('canvasComputed', new XMLSerializer().serializeToString(svg));
    $("canvas").click(function (event) {
        Canvas2Image.saveAsPNG(event.target);
    });
    </script>
        </body>
    </html>
    
    0 讨论(0)
  • 2021-02-01 18:01

    If your css rules are not too much complicated, you can do the following steps:

    1. Read the .css file, which contains all the css rule. If required, you can use a different css file and put it on the server, which you can only use for this purpose.

      function readTextFile(file) {
          var rawFile = new XMLHttpRequest();
          var allText = '';
          rawFile.open("GET", file, false);
          rawFile.onreadystatechange = function () {
              if(rawFile.readyState === 4) {
                  if(rawFile.status === 200 || rawFile.status == 0) {
                      allText = rawFile.responseText;
                  }
              }
          };
          rawFile.send(null);
          return allText;
      }
      
      var svg_style = readTextFile(base_url + '/css/svg_sm_dashboard.css');
      
    2. Now apply the style on all the svg elements

      var all_style = svg_style.replace(/\r?\n|\r/g,'').split('}');
      all_style.forEach(function(el) {
          if (el.trim() != '') {
              var full_rule_string = el.split('{');
              var selector = full_rule_string[0].trim();
              var all_rule = full_rule_string[1].split(';');
              all_rule.forEach(function (elem) {
                  if (elem.trim() != '') {
                      var attr_value = elem.split(':');
                      //d3.selectAll(selector).style(attr_value[0].trim() + '', attr_value[1].trim() + '');
                      var prop = attr_value[0].trim();
                      var value = attr_value[1].trim();
      
                      d3.selectAll(selector).each(function(d, i){
                          if(!this.getAttribute(prop) && !this.style[prop]){
                              d3.select(this).style(prop + '', value + '');
                          }
                      });
                  }
             });
         }
      });
      
    3. Use canvg to convert it

      $('body').after('<canvas id="sm_canvas" style="display=none;"></canvas>');
      var canvas = document.getElementById('sm_canvas');
      canvg(canvas, $("<div>").append($('svg').clone()).html());
      
    4. Get Image from the canvas

      var imgData = canvas.toDataURL('image/jpeg');
      
    0 讨论(0)
  • 2021-02-01 18:02

    Why not copying the SVG node/tree and then take the styles, as defined per tag (You will need the original tree, as the copy may be without styles in case the element is part of a longer tree). This ensures that you are only copying those styles relevant as set in the CSS file. The export type could easily be set before sending the package to the blob

    var ContainerElements = ["svg","g"];
    var RelevantStyles = {"rect":["fill","stroke","stroke-width"],"path":["fill","stroke","stroke-width"],"circle":["fill","stroke","stroke-width"],"line":["stroke","stroke-width"],"text":["fill","font-size","text-anchor"],"polygon":["stroke","fill"]};
    
    
    function read_Element(ParentNode, OrigData){
        var Children = ParentNode.childNodes;
        var OrigChildDat = OrigData.childNodes;     
    
        for (var cd = 0; cd < Children.length; cd++){
            var Child = Children[cd];
    
            var TagName = Child.tagName;
            if (ContainerElements.indexOf(TagName) != -1){
                read_Element(Child, OrigChildDat[cd])
            } else if (TagName in RelevantStyles){
                var StyleDef = window.getComputedStyle(OrigChildDat[cd]);
    
                var StyleString = "";
                for (var st = 0; st < RelevantStyles[TagName].length; st++){
                    StyleString += RelevantStyles[TagName][st] + ":" + StyleDef.getPropertyValue(RelevantStyles[TagName][st]) + "; ";
                }
    
                Child.setAttribute("style",StyleString);
            }
        }
    
    }
    
    function export_StyledSVG(SVGElem){
    
    
        var oDOM = SVGElem.cloneNode(true)
        read_Element(oDOM, SVGElem)
    
        var data = new XMLSerializer().serializeToString(oDOM);
        var svg = new Blob([data], { type: "image/svg+xml;charset=utf-8" });
        var url = URL.createObjectURL(svg);
    
        var link = document.createElement("a");
        link.setAttribute("target","_blank");
        var Text = document.createTextNode("Export");
        link.appendChild(Text);
        link.href=url;
    
        document.body.appendChild(link);
    }
    
    0 讨论(0)
  • 2021-02-01 18:02

    I think what is generally missing from these explanations on this topic, is the fact that a ".svg" file is actually just the markup in a text file.

    So get the svg contents from the dom, then save a text file with ".svg" filename.

    var text = $('#svg-container').html();
    text = text.slice(text.indexOf("<svg"),indexOf("/svg>")+4);
    var pom = document.createElement('a');
    pom.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
    pom.setAttribute('download', "image.svg");
    pom.style.display = 'none';
    document.body.appendChild(pom);
    pom.click();
    document.body.removeChild(pom);
    

    If for example illustrator is giving you an error like "SVG invalid, validate svg before continuing". Then double check the contents of the downloaded file, and make sure there isn't any unnecessary s or anything, and that text.slice(text.indexOf("<svg"),indexOf("/svg>")+4); didn't slice off anything important.

    0 讨论(0)
提交回复
热议问题