2013-03-03 5 views
17

Mam aplikację internetową, która generuje grafikę SVG w kliencie w locie na podstawie interakcji użytkownika. Grafika jest częściowo definiowana przez atrybuty elementów, a częściowo przez klasy i identyfikatory CSS.jak zapisać/eksportować SVG w stylu z css z przeglądarki do pliku obrazu

Chciałbym umożliwić klientowi opcję zapisania kopii wbudowanego pliku SVG jako pliku bitmapowego lub pliku obrazu .svg. Ważne jest, aby wszystkie style były stosowane z zewnętrznych plików stylów CSS. Jak mogę zapewnić tę funkcjonalność, aby zapisać jako .svg lub bitmap (.gif) najlepiej w przeglądarce przy użyciu javascript lub na serwerze z node.js?

Odpowiedz

7

Konieczne będzie jawne ustawienie obliczonych stylów css jako właściwości stylu SVG dla każdego elementu SVG przed jego zapisaniem. Oto przykład:

<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

Ta funkcja trawersu zdaje się iść tylko na jedną warstwę, co nie było dla mnie przydatne. W jQuery następujący wiersz pobiera wszystkie elementy gotowe do uruchomienia przez 'jawnieSetStyle'' $ ('# svg'). Find ("*"); ' – Craig

0

myślę, co jest ogólnie brakuje tych wyjaśnień na ten temat, jest fakt, że „.svg” plik jest faktycznie tylko znaczników w pliku tekstowym.

Więc pobierz zawartość svg z domu, a następnie zapisz plik tekstowy z nazwą pliku ".svg".

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

Jeśli na przykład program Illustrator podaje błąd "Nieprawidłowy SVG, sprawdź poprawność svg przed kontynuowaniem". Następnie sprawdź zawartość pobranego pliku i upewnij się, że nie ma żadnych zbędnych elementów ani niczego, i że text.slice(text.indexOf("<svg"),indexOf("/svg>")+4); nie uciął niczego ważnego.

+3

Pobrane SVG, które to daje, utraciły wszystkie style CSS, które były stosowane w oryginalnym dokumencie, który jest dokładnie problemem, który osoba przesłuchująca próbowała rozwiązać. – nedned

9

Dlaczego nie kopiować węzła/drzewa SVG, a następnie przyjmować style, jako defiend na znacznik (Będzie potrzebne oryginalne drzewo, ponieważ kopia może być bez stylów na wypadek, gdyby element był częścią dłuższego drzewa). To gwarantuje, że kopiujesz tylko te style, które są odpowiednie, jak ustawione w pliku CSS. typu eksport może być łatwo ustawione przed wysłaniem pakietu do 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); 
} 
2

Jeśli CSS nie są zbyt skomplikowane, można wykonać następujące czynności:

  1. przeczytać .css plik, który zawiera wszystkie reguły css. W razie potrzeby możesz użyć innego pliku css i umieścić go na serwerze, który możesz użyć tylko w tym celu.

    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. Teraz zastosować styl do wszystkich elementów svg

    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. Zastosowanie canvg aby przekształcić go

    $('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 Obraz z płótna

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

Ten kod działa bardzo dobrze! Właśnie tego chciałem.Wielkie dzięki! – toshi

Powiązane problemy