javascript:将<svg>元素保存到磁盘上的文件

时间:2016-07-20 09:54:07

标签: javascript html css svg save

在我的HTML上我有一个SVG元素。 它使用d3js渲染,并在CSS中应用了样式。

当我在浏览器中右键单击时,我可以选择&#34;保存图像&#34;。此操作将图像保存为应用了所有css样式的渲染图像。

我一直在寻找保存文件的好方法

  • 转到画布并导出画布
  • 保存为SVG的文件管理器
  • 对这些
  • 的变化

然而,当我将文件送到磁盘时,我的css中的额外样式不会应用于保存的图像。

问题:如何在应用了css的浏览器中保存我的SVG。

2 个答案:

答案 0 :(得分:5)

CSS解析不是一件容易的事,CSS规则很复杂......

我尝试write something获取我的SVG2Bitmap小脚本,但它仍然远非完美......

基本上,它解析文档中的所有样式表,并检查svg的任何节点是否与规则匹配(感谢querySelectorElement.matches()方法)。

问题是,一旦附加到svg doc中,规则可能不再匹配(例如body>svg>rect将失败)。我仍然没有找到一种优雅的方式来处理它,如果有人有,请告诉我。

我遇到的另一个问题是无效规则会使之前提到的方法抛出错误。这不应该是一个太大的问题,但一些浏览器(Chrome不告诉它的名字)接受一些像[xlink\\:href]这样的hacky规则,但将其保存在cssRules[xlink\:href]会失败,从而引发错误......

然而,由于XMLSerializer对象,“保存为文件”部分变得更加容易,这将使浏览器创建所解析内容的独立版本,并提供所需的一切。

要制作100%有效的svg文件,您还需要在自己的顶部设置Doctype 文档。

所以让我们跳进代码:

var exportSVG = function(svg) {
  // first create a clone of our svg node so we don't mess the original one
  var clone = svg.cloneNode(true);
  // parse the styles
  parseStyles(clone);

  // create a doctype
  var svgDocType = document.implementation.createDocumentType('svg', "-//W3C//DTD SVG 1.1//EN", "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd");
  // a fresh svg document
  var svgDoc = document.implementation.createDocument('http://www.w3.org/2000/svg', 'svg', svgDocType);
  // replace the documentElement with our clone 
  svgDoc.replaceChild(clone, svgDoc.documentElement);
  // get the data
  var svgData = (new XMLSerializer()).serializeToString(svgDoc);

  // now you've got your svg data, the following will depend on how you want to download it
  // e.g yo could make a Blob of it for FileSaver.js
  /*
  var blob = new Blob([svgData.replace(/></g, '>\n\r<')]);
  saveAs(blob, 'myAwesomeSVG.svg');
  */
  // here I'll just make a simple a with download attribute

  var a = document.createElement('a');
  a.href = 'data:image/svg+xml; charset=utf8, ' + encodeURIComponent(svgData.replace(/></g, '>\n\r<'));
  a.download = 'myAwesomeSVG.svg';
  a.innerHTML = 'download the svg file';
  document.body.appendChild(a);

};

var parseStyles = function(svg) {
  var styleSheets = [];
  var i;
  // get the stylesheets of the document (ownerDocument in case svg is in <iframe> or <object>)
  var docStyles = svg.ownerDocument.styleSheets;

  // transform the live StyleSheetList to an array to avoid endless loop
  for (i = 0; i < docStyles.length; i++) {
    styleSheets.push(docStyles[i]);
  }

  if (!styleSheets.length) {
    return;
  }

  var defs = svg.querySelector('defs') || document.createElementNS('http://www.w3.org/2000/svg', 'defs');
  if (!defs.parentNode) {
    svg.insertBefore(defs, svg.firstElementChild);
  }
  svg.matches = svg.matches || svg.webkitMatchesSelector || svg.mozMatchesSelector || svg.msMatchesSelector || svg.oMatchesSelector;


  // iterate through all document's stylesheets
  for (i = 0; i < styleSheets.length; i++) {
    var currentStyle = styleSheets[i]

    var rules;
    try {
      rules = currentStyle.cssRules;
    } catch (e) {
      continue;
    }
    // create a new style element
    var style = document.createElement('style');
    // some stylesheets can't be accessed and will throw a security error
    var l = rules && rules.length;
    // iterate through each cssRules of this stylesheet
    for (var j = 0; j < l; j++) {
      // get the selector of this cssRules
      var selector = rules[j].selectorText;
      // probably an external stylesheet we can't access
      if (!selector) {
        continue;
      }

      // is it our svg node or one of its children ?
      if ((svg.matches && svg.matches(selector)) || svg.querySelector(selector)) {

        var cssText = rules[j].cssText;
        // append it to our <style> node
        style.innerHTML += cssText + '\n';
      }
    }
    // if we got some rules
    if (style.innerHTML) {
      // append the style node to the clone's defs
      defs.appendChild(style);
    }
  }

};

exportSVG(document.getElementById('mySVG'));
svg >rect {
  fill: yellow
}
/* this will fail, it could work with a check for document.querySelector instead of svg.querySelector, but that would just be a kill for performances with a lot of cssRules..., and would need to set the elements' style attribute instead of using a <style> tag */

body > svg >rect {
  stroke: red
}
<svg width="120" height="120" viewBox="0 0 120 120" id="mySVG">
  <rect x="10" y="10" width="100" height="100" />
</svg>

Ps:此代码段的下载部分在FF中不起作用,您可以在this fiddle中尝试。

答案 1 :(得分:0)

我认为这应该适合你:

https://stackoverflow.com/a/8694938/2308019

  

我知道您正在为SVG标记使用外部样式表。所以我认为你需要一个三步解决方案:

     
      
  1. 制作适用于SVG标记内联的样式表声明。这是最好的客户端。我现在没有源代码的解决方案,但应该可以使用W3C DOM Level 2 Style接口实现来找出适用于元素的选择器,以及已在相应块中使用的声明(文档)仅.defaultView.getComputedStyle()可能会导致SVG片段内联声明太多。)
  2.   
  3. 将带有内联样式表的SVG标记转换为PNG。这最好在服务器端完成(例如,使用ImageMagick),因此您需要将SVG标记发送到服务器。
  4.   
  5. 将PNG资源提供给用户。
  6.         

    可以在表单提交上执行这两个步骤,在onsubmit属性中执行步骤#1,然后调用表单的submit()方法。