2013-03-03 2 views
17

Ich habe eine Webanwendung, die Inline-SVG-Grafiken im Client auf der Grundlage von Benutzerinteraktion generiert. Die Grafik wird teilweise durch Elementattribute und teilweise durch CSS-Klassen und IDs definiert.So speichern/exportieren Sie inline SVG im Stil von CSS von Browser zu Bilddatei

Ich möchte eine Option für den Client bereitstellen können, um eine Kopie der Inline-SVG als Bitmap oder eine .svg-Bilddatei zu speichern. Es ist wichtig, dass alle Stile aus den externen CSS-Styling-Dateien angewendet werden. Wie kann ich diese Funktionalität bereitstellen, um entweder als .svg oder Bitmap (.gif) vorzugsweise im Browser mit Javascript oder sonst auf dem Server mit node.js zu speichern?

Antwort

7

Sie müssen die berechneten CSS-Stile explizit als SVG-Dom-Style-Eigenschaften für jedes SVG-Element festlegen, bevor Sie sie speichern. Hier ist ein Beispiel:

<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> 
+2

Diese Traversierungsfunktion scheint nur eine Schicht tief zu gehen, was mir keinen Nutzen brachte. Mit jQuery werden in der folgenden Zeile alle Elemente durch 'explicitSetStyle' '$ (' # svg ') zur Ausführung gebracht. Find (" * ");' – Craig

0

Ich denke, was im Allgemeinen aus diesen Erklärungen zu diesem Thema fehlt, ist die Tatsache, dass eine „.svg“ Datei ist eigentlich nur das Markup in einer Textdatei.

So erhalten Sie den Svg-Inhalt aus dem Dom, speichern Sie dann eine Textdatei mit ". Svg" Dateiname.

Wenn zum Beispiel Illustrator gibt Ihnen einen Fehler wie "SVG ungültig, validiere Svg vor dem Fortfahren". Überprüfen Sie dann den Inhalt der heruntergeladenen Datei und stellen Sie sicher, dass keine unnötigen s oder irgendetwas vorhanden sind, und dass text.slice(text.indexOf("<svg"),indexOf("/svg>")+4); nichts Wichtiges abgeschnitten hat.

+3

Die heruntergeladene SVG, die dies ergibt, hat alle CSS-Stile verloren, die angewendet wurden dazu im Originaldokument, welches genau das Problem ist, das der Fragesteller zu lösen versuchte. – nedned

9

Warum kopieren Sie nicht den SVG-Knoten/Baum und nehmen Sie dann die Stile als defiend pro Tag (Sie benötigen den Originalbaum, da die Kopie ohne Stile sein kann, falls das Element Teil eines längeren Baumes ist). Dadurch wird sichergestellt, dass Sie nur die relevanten Stile kopieren, die in der CSS-Datei festgelegt wurden. Der Exporttyp Blob leicht einstellen, das Paket könnte

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); 
} 
2

vor dem Senden Wenn Sie Ihre CSS-Regeln sind nicht zu viel kompliziert, können Sie die folgenden Schritte ausführen:

  1. Lesen Sie die CSS- Datei, die alle CSS-Regel enthält. Bei Bedarf können Sie eine andere CSS-Datei verwenden und auf den Server legen, den Sie nur für diesen Zweck verwenden können.

    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. nun den Stil auf allen SVG-Elemente anwenden

    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. Verwenden canvg, um es zu konvertieren

    $('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 Bild von der Leinwand

    var imgData = canvas.toDataURL('image/jpeg'); 
    
+1

Dieser Code funktioniert sehr gut! Genau das wollte ich.Danke vielmals! – toshi