Javascript: Как экспортировать SVG со встроенными изображениями в PNG? - PullRequest
0 голосов
/ 07 июня 2018

Я написал некоторый код Javascript, который позволяет моему веб-приложению экспортировать SVG в PNG, но проблема в том, что встроенные изображения не включены в экспортированный файл PNG.

Вот мой код:

var svgString;

// Main download function
function download() {
    var exportResolution = 2; // TODO Changeble resolution
    var exportSVG = document.getElementById("canvas").cloneNode(true); // avoids having to reset everything afterward
    // grab its inner content BoundingBox
    var bb = document.getElementById("canvas").getBBox();
    // update its viewBox so it displays all its inner content
    exportSVG.setAttribute('viewBox', bb.x + ' ' + bb.y + ' ' + bb.width + ' ' + bb.height);
    exportSVG.setAttribute('width', bb.width);
    exportSVG.setAttribute('height', bb.height);
    exportSVG.style.fontFamily = "lato";
    exportSVG.style.background = "none"; // TODO without and with background
    svgString = getSVGString(exportSVG);
    console.log(exportSVG.getBBox().width + " / " + exportSVG.getBBox().height);
    svgStringToImage(svgString, bb.width * exportResolution, bb.height * exportResolution, 'png', save); // passes Blob and filesize String to the callback
    console.log("svg export code loaded");
}

function save(dataBlob, filesize) {
    saveAs(dataBlob, 'Example File Name'); // FileSaver.js function, TODO change to document name
}

// Below are the functions that handle actual exporting:
function getSVGString(svgNode) {
    svgNode.setAttribute('xlink', 'http://www.w3.org/1999/xlink');
    var cssStyleText = getCSSStyles(svgNode);
    appendCSS(cssStyleText, svgNode);
    var serializer = new XMLSerializer();
    var svgString = serializer.serializeToString(svgNode);
    svgString = svgString.replace(/(\w+)?:?xlink=/g, 'xmlns:xlink='); // Fix root xlink without namespace
    svgString = svgString.replace(/NS\d+:href/g, 'xlink:href'); // Safari NS namespace fix
    return svgString;

    function getCSSStyles(parentElement) {
        var selectorTextArr = [];
        // Add Parent element Id and Classes to the list
        selectorTextArr.push('#' + parentElement.id);
        for (var c = 0; c < parentElement.classList.length; c++)
            if (!contains('.' + parentElement.classList[c], selectorTextArr))
                selectorTextArr.push('.' + parentElement.classList[c]);
        // Add Children element Ids and Classes to the list
        var nodes = parentElement.getElementsByTagName("*");
        for (var i = 0; i < nodes.length; i++) {
            var id = nodes[i].id;
            if (!contains('#' + id, selectorTextArr))
                selectorTextArr.push('#' + id);
            var classes = nodes[i].classList;
            for (var c = 0; c < classes.length; c++)
                if (!contains('.' + classes[c], selectorTextArr))
                    selectorTextArr.push('.' + classes[c]);
        }
        // Extract CSS Rules
        var extractedCSSText = "";
        for (var i = 0; i < document.styleSheets.length; i++) {
            var s = document.styleSheets[i];

            try {
                if (!s.cssRules) continue;
            } catch (e) {
                if (e.name !== 'SecurityError') throw e; // for Firefox
                continue;
            }
            var cssRules = s.cssRules;
            for (var r = 0; r < cssRules.length; r++) {
                if (contains(cssRules[r].selectorText, selectorTextArr))
                    extractedCSSText += cssRules[r].cssText;
            }
        }
        return extractedCSSText;

        function contains(str, arr) {
            return arr.indexOf(str) === -1 ? false : true;
        }
    }

    function appendCSS(cssText, element) {
        var styleElement = document.createElement("style");
        styleElement.setAttribute("type", "text/css");
        styleElement.innerHTML = cssText;
        var refNode = element.hasChildNodes() ? element.children[0] : null;
        element.insertBefore(styleElement, refNode);
    }
}

function svgStringToImage(svgString, width, height, format, callback) {
    var format = format ? format : 'png';
    var imgsrc = 'data:image/svg+xml;base64,' + btoa(unescape(encodeURIComponent(svgString))); // Convert SVG string to data URL
    var canvas = document.createElement("canvas");
    var context = canvas.getContext("2d");
    canvas.width = width;
    canvas.height = height;
    var image = new Image();
    image.onload = function() {
        context.clearRect(0, 0, width, height);
        context.drawImage(image, 0, 0, width, height);
        canvas.toBlob(function(blob) {
            var filesize = Math.round(blob.length / 1024) + ' KB';
            if (callback) callback(blob, filesize);
        });
    };
    image.src = imgsrc;
}

Вот чертеж SVG в моем приложении: enter image description here

И вы можете увидеть, как выглядит экспортированный файл PNG: enter image description here

Как видите, флажки (которые являются встроенными файлами SVG) не включены в окончательный файл PNG.

Какие наиболее распространенные методы используются для включения встроенных изображенийв этом случае?

...