Html 将嵌入的 SVG 就地转换为 PNG

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5433806/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-29 07:35:21  来源:igfitidea点击:

Convert embedded SVG to PNG in-place

htmlsvg

提问by jjkparker

I'm generating HTML from a Docbook source while using SVG for images (converted from MathML). This works fine for some browsers that can interpret SVG, but fails for others.

我正在从 Docbook 源生成 HTML,同时将 SVG 用于图像(从 MathML 转换)。这对于一些可以解释 SVG 的浏览器来说很好,但对于其他浏览器来说却失败了。

What I would really like is to add a post-processing step that will convert SVG to PNG "in-place" (within the HTML).

我真正想要的是添加一个后处理步骤,该步骤将“就地”(在 HTML 中)将 SVG 转换为 PNG。

So something like this:

所以像这样:

<body>
    <svg xmlns="http://www.w3.org/2000/svg" version="1.1">
        <circle cx="50" cy="50" r="30" />
    </svg>
</body>

Would get seamlessly converted to this:

将无缝转换为:

<body>
    <img src="img0001.png" />
</body>

And I would get a converted PNG alongside.

我会在旁边得到一个转换后的 PNG。

Is there something that will do this?

有什么可以做到这一点吗?

回答by Phrogz

Demo: http://phrogz.net/SVG/svg_to_png.xhtml

演示:http: //phrogz.net/SVG/svg_to_png.xhtml

  1. Create an imgand set its src to your SVG.
  2. Create an HTML5 canvas and use drawImage()to draw that image to your canvas.
  3. Use toDataURL()on the canvas to get a base64 encoded PNG.
  4. Create an img and set it's src to that data URL.
  1. 创建一个img并将其 src 设置为您的 SVG。
  2. 创建一个 HTML5 画布并用于drawImage()将该图像绘制到您的画布上。
  3. toDataURL()在画布上使用以获取 base64 编码的 PNG。
  4. 创建一个 img 并将其 src 设置为该数据 URL。
var mySVG    = document.querySelector('…'),      // Inline SVG element
    tgtImage = document.querySelector('…'),      // Where to draw the result
    can      = document.createElement('canvas'), // Not shown on page
    ctx      = can.getContext('2d'),
    loader   = new Image;                        // Not shown on page

loader.width  = can.width  = tgtImage.width;
loader.height = can.height = tgtImage.height;
loader.onload = function(){
  ctx.drawImage( loader, 0, 0, loader.width, loader.height );
  tgtImage.src = can.toDataURL();
};
var svgAsXML = (new XMLSerializer).serializeToString( mySVG );
loader.src = 'data:image/svg+xml,' + encodeURIComponent( svgAsXML );

However, this answer (and all client-side only solutions) require the browser to support SVG, which may make it useless for your specific needs.

但是,此答案(以及所有仅限客户端的解决方案)要求浏览器支持 SVG,这可能使其无法满足您的特定需求。

Edit: This answer assumes that the SVG is available as a separate URL. Due to the problems described in this questionI cannot get the above to work with an SVG document embedded in the same document performing the work.

编辑:此答案假定 SVG 可作为单独的 URL 使用。由于此问题中描述的问题,我无法使上述内容与嵌入在执行工作的同一文档中的 SVG 文档一起使用。

Edit 2: The problems described in that other question have been overcome by improvements to Chrome and Firefox. There is still the limitation that the <svg>element musthave width="…" height="…"attributes for Firefox to allow it to be drawn to a canvas. And Safari currently taints the entire canvas whenever you draw any SVG to it (regardless of source) but that should change soon.

编辑 2:Chrome 和 Firefox 的改进已经克服了在另一个问题中描述的问题。仍然存在一个限制,即<svg>元素必须具有width="…" height="…"Firefox 的属性才能将其绘制到画布上。每当您向其绘制任何 SVG(无论来源如何)时,Safari 目前都会污染整个画布,但这应该很快就会改变

回答by user3229542

I ran into this problem over the past weekend, and ended up writing a simple library to do more or less what Phrogz describes. It also hard codes the target SVG's style in order to avoid rendering issues. Hopefully the next person looking for a way to do this can simply use my code and move on to the more interesting challenges!

我在上周末遇到了这个问题,最终编写了一个简单的库来完成 Phrogz 所描述的或多或少的工作。它还硬编码目标 SVG 的样式以避免渲染问题。希望下一个寻找方法的人可以简单地使用我的代码并继续进行更有趣的挑战!

P.S. I only tested this in Chrome.

PS 我只在 Chrome 中测试过这个。

// Takes an SVG element as target
function svg_to_png_data(target) {
  var ctx, mycanvas, svg_data, img, child;

  // Flatten CSS styles into the SVG
  for (i = 0; i < target.childNodes.length; i++) {
    child = target.childNodes[i];
    var cssStyle = window.getComputedStyle(child);
    if(cssStyle){
       child.style.cssText = cssStyle.cssText;
    }
  }

  // Construct an SVG image
  svg_data = '<svg xmlns="http://www.w3.org/2000/svg" width="' + target.offsetWidth +
             '" height="' + target.offsetHeight + '">' + target.innerHTML + '</svg>';
  img = new Image();
  img.src = "data:image/svg+xml," + encodeURIComponent(svg_data);

  // Draw the SVG image to a canvas
  mycanvas = document.createElement('canvas');
  mycanvas.width = target.offsetWidth;
  mycanvas.height = target.offsetHeight;
  ctx = mycanvas.getContext("2d");
  ctx.drawImage(img, 0, 0);

  // Return the canvas's data
  return mycanvas.toDataURL("image/png");
}

// Takes an SVG element as target
function svg_to_png_replace(target) {
  var data, img;
  data = svg_to_png_data(target);
  img = new Image();
  img.src = data;
  target.parentNode.replaceChild(img, target);
}

回答by Josh Pearce

FOP and Batik

FOP 和蜡染

http://xmlgraphics.apache.org/fop/

http://xmlgraphics.apache.org/fop/

http://xmlgraphics.apache.org/batik/

http://xmlgraphics.apache.org/batik/

FOP, from Apache, incorporates Batik, also from Apache. Batik has an SVG rendering tool which will generate your PNGs. FOP also is a document generating tool.

来自 Apache 的 FOP 结合了同样来自 Apache 的 Batik。Batik 有一个 SVG 渲染工具,可以生成您的 PNG。FOP 也是一个文档生成工具。

回答by anatoly techtonik

I took @Phrogz code above and made a working snippet. Not sure mySVG.clientWidthworks in FF though. It also available here - https://jsfiddle.net/LLjLpo05/

我采用了上面的@Phrogz 代码并制作了一个工作片段。mySVG.clientWidth虽然不确定在 FF 中是否有效。它也可以在这里找到 - https://jsfiddle.net/LLjLpo05/

var mySVG = document.querySelector('#svblock'),        // Inline SVG element
    tgtImage = document.querySelector('#diagram_png'), // Where to draw the result
    can = document.createElement('canvas'), // Not shown on page
    ctx = can.getContext('2d'),
    loader = new Image; // Not shown on page

//loader.width  = can.width  = tgtImage.width = mySVG.getBBox().width;
//loader.height = can.height = tgtImage.height = mySVG.getBBox().height;

loader.width = can.width = tgtImage.width = mySVG.clientWidth;
loader.height = can.height = tgtImage.height = mySVG.clientHeight;

loader.onload = function() {
  ctx.drawImage(loader, 0, 0, loader.width, loader.height);
  tgtImage.src = can.toDataURL();
};
var svgAsXML = (new XMLSerializer).serializeToString(mySVG);
loader.src = 'data:image/svg+xml,' + encodeURIComponent(svgAsXML);
<div id="diagram_image">
  <svg id="svblock" xmlns="http://www.w3.org/2000/svg" xmlns:inkspace="http://www.inkscape.org/namespaces/inkscape" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 640 120">
    <defs id="defs_block">
      <filter height="1.504" id="filter_blur" inkspace:collect="always" width="1.1575" x="-0.07875" y="-0.252">
        <feGaussianBlur id="feGaussianBlur3780" inkspace:collect="always" stdDeviation="4.2" />
      </filter>
    </defs>
    <title>blockdiag</title>
    <desc/>
    <rect fill="rgb(0,0,0)" height="40" stroke="rgb(0,0,0)" style="filter:url(#filter_blur);opacity:0.7;fill-opacity:1" width="128" x="67" y="46" />
    <rect fill="rgb(0,0,0)" height="40" stroke="rgb(0,0,0)" style="filter:url(#filter_blur);opacity:0.7;fill-opacity:1" width="128" x="259" y="46" />
    <rect fill="rgb(0,0,0)" height="40" stroke="rgb(0,0,0)" style="filter:url(#filter_blur);opacity:0.7;fill-opacity:1" width="128" x="451" y="46" />
    <rect fill="rgb(255,255,255)" height="40" stroke="rgb(0,0,0)" width="128" x="64" y="40" />
    <text fill="rgb(0,0,0)" font-family="sans-serif" font-size="11" font-style="normal" font-weight="normal" text-anchor="middle" textLength="55" x="128" y="66">discovery</text>
    <rect fill="rgb(255,255,255)" height="40" stroke="rgb(0,0,0)" width="128" x="256" y="40" />
    <text fill="rgb(0,0,0)" font-family="sans-serif" font-size="11" font-style="normal" font-weight="normal" text-anchor="middle" textLength="55" x="320" y="66">execution</text>
    <rect fill="rgb(255,255,255)" height="40" stroke="rgb(0,0,0)" width="128" x="448" y="40" />
    <text fill="rgb(0,0,0)" font-family="sans-serif" font-size="11" font-style="normal" font-weight="normal" text-anchor="middle" textLength="55" x="512" y="66">reporting</text>
    <path d="M 192 60 L 248 60" fill="none" stroke="rgb(0,0,0)" />
    <polygon fill="rgb(0,0,0)" points="255,60 248,56 248,64 255,60" stroke="rgb(0,0,0)" />
    <path d="M 384 60 L 440 60" fill="none" stroke="rgb(0,0,0)" />
    <polygon fill="rgb(0,0,0)" points="447,60 440,56 440,64 447,60" stroke="rgb(0,0,0)" />
  </svg>
</div>

<img id="diagram_png" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4
//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" />

UPDATE: Refactored a bit - https://jsfiddle.net/e4r8sk18/1/

更新:重构了一下 - https://jsfiddle.net/e4r8sk18/1/

UPDATE2: Refactored into converter class - https://jsfiddle.net/07a93Lt6/5/

UPDATE2:重构为转换器类 - https://jsfiddle.net/07a93Lt6/5/

回答by wildcard

if you want to do it purely on the client-side, you would need two steps:

如果您只想在客户端执行此操作,则需要两个步骤:

  1. convert SVG to Canvas (http://code.google.com/p/canvas-svg/ or some other tools)
  2. convert Canvas to PNG (http://www.nihilogic.dk/labs/canvas2image/ or some other tools)
  1. 将 SVG 转换为 Canvas(http://code.google.com/p/canvas-svg/ 或其他一些工具)
  2. 将画布转换为 PNG(http://www.nihilogic.dk/labs/canvas2image/ 或其他一些工具)

this obviously will work only in HTML5-capable browsers.

这显然只适用于支持 HTML5 的浏览器。

回答by Ramiro Araujo

this is rather old, but I found a simpler snippet that does the job correctly in modern browsers: https://gist.github.com/Caged/4649511

这是相当古老的,但我发现了一个更简单的片段,可以在现代浏览器中正确完成工作:https: //gist.github.com/Caged/4649511