在浏览器中将 SVG 转换为图像(JPEG、PNG 等)

Posted

技术标签:

【中文标题】在浏览器中将 SVG 转换为图像(JPEG、PNG 等)【英文标题】:Convert SVG to image (JPEG, PNG, etc.) in the browser 【发布时间】:2011-04-27 21:24:13 【问题描述】:

我想通过 javascript 将 SVG 转换为位图图像(如 JPEG、PNG 等)。

【问题讨论】:

您真正想要完成的任务是什么?尽管 echo-flows 的回答告诉我们,(在某些浏览器中)可能有更好、更简单的转换方法适用于几乎所有实际情况。 这里是一个使用 d3 的例子:***.com/a/23667012/439699 svgopen.org/2010/papers/62-From_SVG_to_Canvas_and_Back - 完美运行! [在链接页面上,sourceSVG = $("#your_svg_elem_name").get(0) ] 相关:***.com/questions/3173048/… mybyways.com/blog/convert-svg-to-png-using-your-browser 【参考方案1】:

以下是通过 JavaScript 实现的方法:

    使用 canvg JavaScript 库通过 Canvas 呈现 SVG 图像:https://github.com/gabelerner/canvg 根据以下说明从画布中捕获编码为 JPG(或 PNG)的数据 URI:Capture html Canvas as gif/jpg/png/pdf?

【讨论】:

这不是严格意义上的 Javascript,而是 HTML5。这不适用于 IE8 或任何其他不支持 HTML5 Canvas 的浏览器。 如果浏览器支持 SVG 和画布,那么将有更简单的方法将 SVG 加载到内存中,然后将其绘制到画布中,而不需要 Canvg,这是一个非常大的库因为它处理支持 SVG 的浏览器已经免费提供的所有 SVG 解析。我不确定这是否满足原始用例,但如果满足,那么 see this resource for details. 感谢您不支持 IE8。人们应该明白,是时候继续前进了。 您现在可以使用 JavaScript SVG 库 Pablo 来实现这一点(我做到了)。请参阅 toImage()download() 以获取自动下载的图像。 svgopen.org/2010/papers/62-From_SVG_to_Canvas_and_Back - 完美运行! [在链接页面上,sourceSVG = $("#your_svg_elem_name").get(0) ]【参考方案2】:

jbeard4 解决方案效果很好。

我正在使用Raphael SketchPad 创建一个 SVG。链接到步骤 1 中的文件。

对于保存按钮(svg 的 id 是“editor”,canvas 的 id 是“canvas”):

$("#editor_save").click(function() 

// the canvg call that takes the svg xml and converts it to a canvas
canvg('canvas', $("#editor").html());

// the canvas calls to output a png
var canvas = document.getElementById("canvas");
var img = canvas.toDataURL("image/png");
// do what you want with the base64, write to screen, post to server, etc...
);

【讨论】:

canvg 需要第二个参数是 <svg>...</svg 但 jquery html() 函数不添加 svg 标签,所以这段代码对我有用,但我需要将 canvg 实时编辑为 canvg('canvas', '<svg>'+$("#editor").html()+'</svg>'); @Luckyn 如果您在 svg 元素的父级上调用 $(selector).html(),它将起作用 @Luckyn 和@jonathanGB,你不应该在包装器上使用html(),或者手动构造父svg 标签——它甚至可能有你在这个hack 中遗漏的属性。只需使用 $(svg_elem)[0].outerHTML 即可为您提供 svg 及其内容的完整来源。只是说...【参考方案3】:

这似乎适用于大多数浏览器:

function copyStylesInline(destinationNode, sourceNode) 
   var containerElements = ["svg","g"];
   for (var cd = 0; cd < destinationNode.childNodes.length; cd++) 
       var child = destinationNode.childNodes[cd];
       if (containerElements.indexOf(child.tagName) != -1) 
            copyStylesInline(child, sourceNode.childNodes[cd]);
            continue;
       
       var style = sourceNode.childNodes[cd].currentStyle || window.getComputedStyle(sourceNode.childNodes[cd]);
       if (style == "undefined" || style == null) continue;
       for (var st = 0; st < style.length; st++)
            child.style.setProperty(style[st], style.getPropertyValue(style[st]));
       
   


function triggerDownload (imgURI, fileName) 
  var evt = new MouseEvent("click", 
    view: window,
    bubbles: false,
    cancelable: true
  );
  var a = document.createElement("a");
  a.setAttribute("download", fileName);
  a.setAttribute("href", imgURI);
  a.setAttribute("target", '_blank');
  a.dispatchEvent(evt);


function downloadSvg(svg, fileName) 
  var copy = svg.cloneNode(true);
  copyStylesInline(copy, svg);
  var canvas = document.createElement("canvas");
  var bbox = svg.getBBox();
  canvas.width = bbox.width;
  canvas.height = bbox.height;
  var ctx = canvas.getContext("2d");
  ctx.clearRect(0, 0, bbox.width, bbox.height);
  var data = (new XMLSerializer()).serializeToString(copy);
  var DOMURL = window.URL || window.webkitURL || window;
  var img = new Image();
  var svgBlob = new Blob([data], type: "image/svg+xml;charset=utf-8");
  var url = DOMURL.createObjectURL(svgBlob);
  img.onload = function () 
    ctx.drawImage(img, 0, 0);
    DOMURL.revokeObjectURL(url);
    if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob)
    
        var blob = canvas.msToBlob();         
        navigator.msSaveOrOpenBlob(blob, fileName);
     
    else 
        var imgURI = canvas
            .toDataURL("image/png")
            .replace("image/png", "image/octet-stream");
        triggerDownload(imgURI, fileName);
    
    document.removeChild(canvas);
  ;
  img.src = url;

【讨论】:

这在 IE11 中不起作用,因为 .msToBlob() 的安全问题 谢谢!!我喜欢它对“本地”SVG HTML 节点和远程 SVG URL 的工作方式。此外,它不需要完整的外部库 你能提供一个使用例子吗?【参考方案4】:

SVG转blob URL和blob URL转png图片的解决方案

const svg=`<svg version="1.1" baseProfile="full"  
xmlns="http://www.w3.org/2000/svg">
   <rect   fill="red" />
   <circle cx="150" cy="100" r="80" fill="green" />
   <text x="150" y="125" font-size="60" text-anchor="middle" fill="white">SVG</text></svg>`
svgToPng(svg,(imgData)=>
    const pngImage = document.createElement('img');
    document.body.appendChild(pngImage);
    pngImage.src=imgData;
);
 function svgToPng(svg, callback) 
    const url = getSvgUrl(svg);
    svgUrlToPng(url, (imgData) => 
        callback(imgData);
        URL.revokeObjectURL(url);
    );

function getSvgUrl(svg) 
    return  URL.createObjectURL(new Blob([svg],  type: 'image/svg+xml' ));

function svgUrlToPng(svgUrl, callback) 
    const svgImage = document.createElement('img');
    // imgPreview.style.position = 'absolute';
    // imgPreview.style.top = '-9999px';
    document.body.appendChild(svgImage);
    svgImage.onload = function () 
        const canvas = document.createElement('canvas');
        canvas.width = svgImage.clientWidth;
        canvas.height = svgImage.clientHeight;
        const canvasCtx = canvas.getContext('2d');
        canvasCtx.drawImage(svgImage, 0, 0);
        const imgData = canvas.toDataURL('image/png');
        callback(imgData);
        // document.body.removeChild(imgPreview);
    ;
    svgImage.src = svgUrl;
 

【讨论】:

此解决方案不适用于复杂的 SVG(例如带有图像的图案) 这是您的代码,但已优化,但谢谢您...codepen.io/arcaelas-the-looper/pen/wvrqzvm 谢谢@AlejandroReyes 不客气,你可以在github上关注我:arcaelas【参考方案5】:

我的用例是从网络加载 svg 数据,这个 ES6 类完成了这项工作。

class SvgToPngConverter 
  constructor() 
    this._init = this._init.bind(this);
    this._cleanUp = this._cleanUp.bind(this);
    this.convertFromInput = this.convertFromInput.bind(this);
  

  _init() 
    this.canvas = document.createElement("canvas");
    this.imgPreview = document.createElement("img");
    this.imgPreview.style = "position: absolute; top: -9999px";

    document.body.appendChild(this.imgPreview);
    this.canvasCtx = this.canvas.getContext("2d");
  

  _cleanUp() 
    document.body.removeChild(this.imgPreview);
  

  convertFromInput(input, callback) 
    this._init();
    let _this = this;
    this.imgPreview.onload = function() 
      const img = new Image();
      _this.canvas.width = _this.imgPreview.clientWidth;
      _this.canvas.height = _this.imgPreview.clientHeight;
      img.crossOrigin = "anonymous";
      img.src = _this.imgPreview.src;
      img.onload = function() 
        _this.canvasCtx.drawImage(img, 0, 0);
        let imgData = _this.canvas.toDataURL("image/png");
        if(typeof callback == "function")
            callback(imgData)
        
        _this._cleanUp();
      ;
    ;

    this.imgPreview.src = input;
  

这是你的使用方法

let input = "https://restcountries.eu/data/afg.svg"
new SvgToPngConverter().convertFromInput(input, function(imgData)
    // You now have your png data in base64 (imgData). 
    // Do what ever you wish with it here.
);

如果你想要一个普通的 JavaScript 版本,你可以head over to Babel website 并在那里转译代码。

【讨论】:

【参考方案6】:

更改 svg 以匹配您的元素

function svg2img()
    var svg = document.querySelector('svg');
    var xml = new XMLSerializer().serializeToString(svg);
    var svg64 = btoa(xml); //for utf8: btoa(unescape(encodeURIComponent(xml)))
    var b64start = 'data:image/svg+xml;base64,';
    var image64 = b64start + svg64;
    return image64;
;svg2img()

【讨论】:

它对我不起作用,我收到此错误:Uncaught TypeError: Failed to execute 'serializeToString' on 'XMLSerializer': parameter 1 is not of type 'Node'. @Xsmael 尝试切换DOMParser接口developer.mozilla.org/en-US/docs/Web/API/DOMParser 谢谢,这对我有用。无需使用外部库。 @RyanArqueza 怎么样? @MTZ 您应该考虑将 utf8 部分用于 utf8 字符,例如您的 SVG 包含非 ASCI 字符【参考方案7】:

这是一个无需库即可工作并返回Promise的函数:

/**
 * converts a base64 encoded data url SVG image to a PNG image
 * @param originalBase64 data url of svg image
 * @param width target width in pixel of PNG image
 * @return Promise<String> resolves to png data url of the image
 */
function base64SvgToBase64Png (originalBase64, width) 
    return new Promise(resolve => 
        let img = document.createElement('img');
        img.onload = function () 
            document.body.appendChild(img);
            let canvas = document.createElement("canvas");
            let ratio = (img.clientWidth / img.clientHeight) || 1;
            document.body.removeChild(img);
            canvas.width = width;
            canvas.height = width / ratio;
            let ctx = canvas.getContext("2d");
            ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
            try 
                let data = canvas.toDataURL('image/png');
                resolve(data);
             catch (e) 
                resolve(null);
            
        ;
        img.src = originalBase64;
    );

在 Firefox 上有一个issue for SVGs without set width / height。

请参阅此working example,其中包含 Firefox 问题的修复。

【讨论】:

在 Safari 中对我不起作用,只是挂起,没有错误 感谢测试,我之前没有用 Safari 测试过。所以Javascript控制台也没有报错,会不会很奇怪? 是的,在我的经验中很奇怪,但不幸的是我没有更多的时间可以浪费在它上面 感谢您提供这个绝妙的例子!【参考方案8】:

Svgpng可以根据条件转换:

    如果svg 的格式为SVG (string) paths:
创建画布 创建new Path2D()并将svg设置为参数 在画布上绘制路径 创建图像并将canvas.toDataURL() 用作src

示例:

const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
let svgText = 'M10 10 h 80 v 80 h -80 Z';
let p = new Path2D('M10 10 h 80 v 80 h -80 Z');
ctx.stroke(p);
let url = canvas.toDataURL();
const img = new Image();
img.src = url;

请注意 Path2Die 中不受支持,而在 edge 中部分支持。 Polyfill 解决了这个问题: https://github.com/nilzona/path2d-polyfill

    创建svg blob 并使用.drawImage() 在画布上绘制:
制作画布元素 从 svg xml 创建一个 svgBlob 对象 从 domUrl.createObjectURL(svgBlob) 创建一个 url 对象; 创建一个 Image 对象并将 url 分配给 image src 将图像绘制到画布中 从画布中获取png数据字符串:canvas.toDataURL();

很好的描述: https://web.archive.org/web/20200125162931/http://ramblings.mcpher.com:80/Home/excelquirks/gassnips/svgtopng

请注意,在 ie 中,canvas.toDataURL(); 的阶段会出现异常;这是因为 IE 的安全限制太高,在画布后将画布视为只读。所有其他浏览器仅在图像是跨源的情况下进行限制。

    使用canvg JavaScript 库。它是独立的库,但具有有用的功能。

喜欢:

ctx.drawSvg(rawSvg);
var dataURL = canvas.toDataURL();

【讨论】:

第三个链接坏了 是的,确实如此。我现在不知道如何到达那里。但是上面的描述对于一些理解来说已经足够了。参考后的未来复制一些上下文的好主意 固定链接旋转。 Wayback 机器是您修复 linkrot 的朋友。【参考方案9】:

我最近发现了几个用于 JavaScript 的图像跟踪库,它们确实能够为位图构建一个可接受的近似值,无论是大小还是质量。我正在开发这个 JavaScript 库和 CLI:

https://www.npmjs.com/package/svg-png-converter

它为所有这些提供统一的API,支持浏览器和节点,不依赖于DOM,以及一个命令行工具。

对于转换徽标/卡通/类似图像,它做得很好。对于照片/真实感,需要进行一些调整,因为输出大小会增长很多。

它有一个游乐场,尽管现在我正在开发一个更好、更易于使用的游乐场,因为添加了更多功能:

https://cancerberosgx.github.io/demos/svg-png-converter/playground/#

【讨论】:

【参考方案10】:

有多种方法可以使用 Canvg 库将 SVG 转换为 PNG。

就我而言,我需要从内联 SVG 获取 PNG blob

库文档提供了example(参见 OffscreenCanvas 示例)。

但是这个方法目前Firefox不起作用。是的,您可以在设置中启用 gfx.offscreencanvas.enabled 选项。但是网站上的每个用户都会这样做吗? :)

不过,还有另一种方法也适用于 Firefox。

const el = document.getElementById("some-svg"); //this is our inline SVG

var canvas = document.createElement('canvas'); //create a canvas for the SVG render
canvas.width = el.clientWidth; //set canvas sizes
canvas.height = el.clientHeight;

const svg = new XMLSerializer().serializeToString(el); //convert SVG to string

//render SVG inside canvas
const ctx = canvas.getContext('2d');
const v = await Canvg.fromString(ctx, svg);
await v.render();

let canvasBlob = await new Promise(resolve => canvas.toBlob(resolve));

感谢this的最后一行回答

【讨论】:

以上是关于在浏览器中将 SVG 转换为图像(JPEG、PNG 等)的主要内容,如果未能解决你的问题,请参考以下文章

在Nodejs中将base64 png转换为jpeg图像

在颤动中将应用程序/八位字节流转换为图像/jpeg/png?

如何将 SVG 文件转换为 JPEG?

在 PHP 中将 JPG/GIF 图像转换为 PNG?

使用 C# 将 SVG 转换为 PNG [关闭]

将 SVG 转换为 PNG/JPEG,而不在无服务器函数中使用画布