24 / 01 / 28

「折腾小记」AstroPaper OG Image

本文发布于 Donut-Zone Blogs

AstroPaper 为 Blog 提供了 OG Image 支持,且可以使用 Vercel Satori 生成动态 OG Image,但本博客使用语言大多为中文,默认配置会导致 OG Image 无法渲染中文字符。本文简述了对动态 OG Image 生成配置做出的修改。

添加中文支持

AstroPaper 的 OG Image Generator 基于 Vercel Satori 实现,默认调用 1001fonts 的 IBM Plex Mono 字体,这一字体仅支持英文字符,无法正常渲染中文字符。AstroPaper 在介绍 Dynamic OG image generation 的 文章 中指出了一种解决办法,即将 embedFonts 设置为 false,但这一方法会导致图片中的中文字符发生重叠,并不可用。

初次尝试

既然是因为字体问题无法正常渲染,最自然的解决方案便是改用中文字体。最初的想法是上传中文字体并在 generateOgImages.tsx 中调用,但中文字体文件较大,且笔者尚不清楚如何在 Astro 中加载字体文件,故此方案作罢。

无法将字体作为本地文件,那么不妨保持原作者的思路,通过 fetchFonts 获得在线的字体文件。Satori 支持 TTF、OTF 和 WOFF 作为自定义字体,不支持 WOFF2,但大多数字体的 CDN 服务提供的都是 CSS 格式,且其中包含的是切片的 WOFF2 文件,无法在 Satori 中直接调用;大多数开源字体提供的下载方式大多为体积庞大的原字体或压缩文件,也无法在 Satori 中直接调用。

最后经过不懈的寻找,找到了体积合适的 OPPOSans 文件链接,原以为在 generateOgImages.tsx 中调用此字体即可大功告成,可惜事与愿违。替换字体并启用后 embedFonts,中文字符可以正常渲染,但空格无法正常显示,会被渲染为✖,百思不得其解。

再次尝试

在虾虾提醒下,检查字体文件,发现所使用的 OPPO Sans 2 的切片中并不包含空格,遂继续寻找字体文件。十分感谢 OPPO,找到了 OPPO Sans 3.0 的字体文件,且各种格式一应俱全,体积也尽如人意。将 generateOgImages.tsx 中的字体文件链接替换为 OPPO Sans 3.0 后问题迎刃而解。

在完成 OG Image 的基础功能之后,与群友分享了卡片预览效果,鱼鱼提出可以在右下角 Donut-Zone Blogs 前增加甜甜圈元素。虽然 AstroPaper 作者在上述文档中明确指出了在 OG Image 中使用 Emoji 可能有一些棘手,但不妨碍我们另辟蹊径解决问题。

在为 Donut-Zone Blog 设置 favicon 时,上传了甜甜圈 Emoji 的 SVG 文件,若可以在 OG Image 中复用则再好不过。万幸的是 Satori 提供了 svg 文件支持。于是借助 OG Image Playground 开始测试效果。

方案一:直接嵌入 svg

Satori 语法与 HTML 基本一致,支持直接通过 <svg></svg> 的形式嵌入 svg 文件,通过这一方法嵌入的 svg 图片大小无法灵活调整,在 OG Image 中有些许喧宾夺主,故放弃。

方案二:使用 img 嵌入 svg

Satori 中使用 <obj></obj> 嵌入 svg 并不生效,于是使用 img 进行尝试,且 img 可以通过指定 width 完成缩放,最终调试选择使用 <img src = "https://donutblogs.com/donut.svg" width="30"/>,效果差强人意。

更好看一点

在添加 Donut Emoji 后,Donut Emoji 与后面的字符直接并没有何时的间距,出于习惯添加了 | 但并没有达到预期的效果,竖线后侧的空格被正常渲染但竖线前的空格则被忽略,且 Satori 中使用 &nbsp; 强制插入空格并不生效,只能另谋他法。

<span style={{ color: "transparent", }} > " </span>

观察生成的 OG Image 发现左下角 by 与作者 ID 直接存在空格,翻阅 post.tsx 发现原作者使用了上述代码实现空格效果,故抄之,渲染结果与预期一致,愉快下班。