0%

如何把svg渲染成png图片

简洁版:在小程序里无法把svg转为png,cloudflare 的 worker 上也不能,最终选择在自运维的服务器上转换。

背景

最近prompt大师开发了一套新的提示词很有意思,能把一个词语用鲁迅的语气,幽默、讽刺、批判性的进行解释。这个提示词要配合 Claude AI 使用,输出的内容是 SVG 。

例如,对程序员这个词:

问题

而我们有一个微信小程序,在小程序上, svg 的展示就有一些小问题了,主要是无法预览、无法下载。

那么如何能实现svg在小程序上的预览呢? 最简单的思路当然是,转换成PNG图片。

接下来的问题是,在哪转?

serverless

因为转换本身肯定是要消耗一些资源的,所以一开始是不愿意在服务器上转换的。而赛博佛祖 cloudflare 提供的 serverless 服务有非常大的免费额度,所以想着看能不能用 cloudflare 的 worker 实现这个需求。

结论是不行,安装 resvg-js 后编写逻辑,运行时提示无相关能力,发现 serverless 阉割了一些底层能力,不支持 native APIs。正常写网络业务逻辑没问题,一旦需要用一些底层支持的时候就挂了。

但还是不死心,搜了下相关资料 “svg to png cloudflare worker”,还真有一篇博文和一个 reddit 帖子。里面提到了一个叫 resvg-wasm 的包,通过 WebAssembly ,把 rust 编译成 wasm,以此来渲染 svg。

发现确实可以,但不支持字体,在我这个场景下尤为致命。reddit 的帖子里有老哥就分享了另一个方法:svg2png-wasm,这个包支持文字。但实测发现,仅支持英文,中文不知道是我姿势不对还是字体文件太大,反正出的结果就是一堆框框。

总之,折腾半天的结论就是,缺少 native APIs 的 serverless 环境不行……

小程序

那如果不能在 worker 上做,第二个思路就是,能不能在小程序里做?小程序本身是可以通过 image 标签把 svg 展示出来的,但无法预览也无法下载。

那么能否结合小程序的 canvas ,把 svg 绘制在 canvas 上,再从 canvas 保存为 png 呢?

答案是也不行,小程序的 canvas 不支持 svg,社区里相关问题最早在18年就出现了,但一直到24年依然是没有解决方案。不知道到底是微信的技术团队比较菜还是他们不认为这是一个高优的问题。因为 svg 的支持其实是比想象中难不少的,尤其是 svg 是可以通过引入资源等做很多复杂的事情的。

传统方案

所以花了很长的时间验证上面两条分布式的道路走不通后,最终还是妥协用传统方案来做。

而传统方案的容易程度真的是震惊到我了,实在是太简单了,有系统支持下的 node ,太快乐了。

安装一个 sharp 包用于解析渲染 svg ,核心代码就几行:

1
2
3
4
5
6
7
8
9
const svgBuffer = await this.downloadSvg(url);

// 使用 sharp 将 SVG 转换为 PNG
const pngBuffer = await sharp(svgBuffer)
.png()
.toBuffer();
res.setHeader('Content-Type', 'image/png');
res.setHeader('Content-Disposition', 'inline; filename="converted.png"');
res.send(pngBuffer);

请求调用出图,都不用调试一次就成功了。

没有压测过,但请求量一旦大了后,估计很容易崩。但没关系,这个量目测不会太大,大了再想办法解决。

字体

但仔细观察发现第一次出的图的效果,好像并不好看,至少和原作者分享的效果不一致,仔细观察了一下 svg 的内容,声明了 text 的 font-family ,标题是楷体,内容是汇文明朝体。

下载字体后好看很多(就是上面放的图里的效果)。

于是给服务器上也安装了对应的字体。记录一下相关过程。

  1. 查看Ubuntu上的字体:fc-list :lang=zh
  2. 从网上下载字体或者从Windows的C:\Windows\Fonts路径把字体复制出来,注意格式是ttf,发送到服务器上。
  3. 把文件复制到相应的目录sudo cp -r /home/upload-font /usr/share/fonts
  4. 执行:sudo mkfontscalesudo mkfontdir 以及 sudo fc-cache -fv 等待字体安装
  5. 再输入 fc-list :lang=zh 看看安装是否成功

其实第五步里我没看到有汇文明朝体,但从svg生成的png上看确实是成功了,也没探究细节,反正实现了,先这样吧。