SVG 转 PNG 图片

2022-05-07 ⏳1.9分钟(0.8千字) 🕸️

我最近着手把WebFeed插件移植到 Chrome 平台。但 Chrome 非常贱,居然不支持使用 svg 作图标。所以我需要将 svg 图片转换成 png 图片。我也查到了好多在线的转换工具,但效果都不理想。我寻思着浏览器既然可以展示 svg,那一定有办法用 js 转成 png 等格式的图片。经过一翻调研,终于找到了理想的技术方案。今天就把命令行和浏览器两种场景下 svg 转 png 图片的方案分享给大家。

先说命令行场景。

我在移植 Chrome 版插件的时候需要需要自动生成 png 图标。所以需要找一个命令行工具。一番谷歌之后发现了rsvg-convert。这是由librsvg库提供的。而 librsvg 又是 GNOME 项目使用的 svg 渲染库,其主要功能是用来解析 GNOME 环境下的 svg 图标。

mac 平台可以直接使用 homebrew 安装

brew install librsvg

rsvg-convert 的用法非常简单,只需要一个-h 参数指定输出的 png 高度即可:

for icon in $(ls icons/|grep svg); do
  for size in 32 64 128; do
    rsvg-convert -h $size icons/$icon > icons/${icon%.svg}-$size.png
  done
done

我在上述示例中使用两遍循环,批量生成了不同尺寸的 png 图标。

命令行场景比较简单。接下来介绍浏览器场景。

主要思路是使用 canvas 的 drawImage 接口绘制 svg 图片。然后利用 toDataURL 导出对应不同格式的位图,目前支持 png/jpg/webp 三种格式。

首先,我们需要构造一个 Image 对象。

let img = new Image();

然后我们可以设置它的src属性。浏览器就开始加载图片。等图片加载成功后会触发load事件。我们需要在注册一个回调函数,并在该函数内操作 canvas。

canvas 只能以<canvas>标签的形式写到 html 中,而不能直接使用new创建。

img.onload = (e) => {
  let canvas = document.querySelector('canvas');
  // 设置尺寸
  canvas.width = img.width;
  canvas.height = img.height;
  // 获取绘图上下文
  let ctx = canvas.getContext('2d');
  // 绘制图片
  ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
};

到这里,浏览器就能显示对应的 svg 图片了。接下来我们要把它转换成 png 格式的图片。

在调试的时候还碰到了 Firefox 的一个陈年BUG。历时十一年还没修好。如果 svg 中没有设置 height 和 width 参数,Firefox 调用 drawImage 接口会失败,而且没有任何报错。折腾了半天才发现……

// 创建下载连接
let a = document.createElement("a");
// 转换成 png 格式的连接
a.href = canvas.toDataURL('image/png');
// 设置下载文件名
a.download = "svg.png";

最后,我们需要支持用户选择本地文件。所以得设置一个file类型的input表单

<input type="file" id="picker" accept="image/svg+xml">

当用户选择 svg 后,我们得构造对应的链接赋给最开始的img对象。

var file = document.querySelector('input[type="file"]');
file.onchange = (e) => {
  let f = e.target.files[0];
  // 构造一个特殊的 URL 指向本地文件
  img.src = URL.createObjectURL(f);
  // ...
};

完整的代码我就不重复贴了。因为是纯浏览器功能,我把完整功能集成到文章里,大家可以体验一下。选择 svg 后会展示对应的图片。图片下面有不同类型的下载链接,点击可以保存到本地。

PNG JPG

以上就是本文的全部内容。欢迎大家留言讨论😄