Подтвердить что ты не робот

Как сохранить/экспортировать встроенный SVG в стиле css из браузера в файл изображения

У меня есть веб-приложение, которое генерирует встроенную графику SVG на клиенте "на лету" на основе взаимодействия с пользователем. Графика определяется частично атрибутами элемента, а частично - классами CSS и идентификаторами.

Я хотел бы иметь возможность предоставить клиенту возможность сохранить копию встроенного SVG в виде растрового изображения или файла изображения .svg. Важно, чтобы все стили применялись из внешних файлов стилей CSS. Как я могу предоставить эту функциональность для сохранения в виде .svg или bitmap (.gif) предпочтительно в браузере с помощью javascript или на сервере с node.js?

4b9b3361

Ответ 1

Почему бы не скопировать узел/дерево SVG, а затем взять стили, как определено для тега (Вам понадобится оригинальное дерево, поскольку копия может быть без стилей, если элемент является частью более длинного дерева). Это гарантирует, что вы копируете только те стили, которые соответствуют настройкам в файле CSS. Тип экспорта может быть легко установлен перед отправкой пакета в 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

Вам нужно будет явно задать вычисленные CSS-стили как свойства стиля SVG для каждого элемента SVG перед сохранением. Вот пример:

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

Ответ 3

Если ваши правила css не слишком сложны, вы можете сделать следующие шаги:

  • Прочтите файл .css, содержащий все правило css. При необходимости вы можете использовать другой файл css и поместить его на сервер, который вы можете использовать только для этой цели.

    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');
    
  • Теперь примените стиль для всех элементов 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 + '');
                        }
                    });
                }
           });
       }
    });
    
  • Используйте canvg для его преобразования

    $('body').after('<canvas id="sm_canvas" style="display=none;"></canvas>');
    var canvas = document.getElementById('sm_canvas');
    canvg(canvas, $("<div>").append($('svg').clone()).html());
    
  • Получить изображение с холста

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

Ответ 4

Я думаю, что, как правило, отсутствует в этих объяснениях по этому вопросу, заключается в том, что файл ".svg" на самом деле является только разметкой в ​​текстовом файле.

Получите содержимое svg из dom, затем сохраните текстовый файл с именем файла .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);

Если, например, иллюстратор дает вам ошибку, например, "SVG недействителен, проверьте svg перед продолжением". Затем дважды проверьте содержимое загруженного файла и убедитесь, что нет лишних s или чего-то еще, и что text.slice(text.indexOf("<svg"),indexOf("/svg>")+4); не отменил ничего важного.