SSR 字体修复

背景

现在很流行的服务端渲染(SSR),或者 JAMStack,都免不了要在服务器上做一些静态编译。如果我们编译的对象都只是纯粹的 JavaScript 和 CSS,那都不会有什么大问题。

但是有时候我们会希望渲染一些更丰富的内容,比如使用 puppeteer 将页面在服务端渲染成图片,又或者静态生成流程图。这时可能会遇到问题:

  • 如果渲染成 SVG,那结果可能是框框的大小和字的大小不匹配,字会超出框框的范围,或者干脆被截断了;
  • 如果渲染成位图,如 PNG,那结果很有可能是 CJK 的文字全部变成了框框,像这样:□□

根本原因就是服务器一般是 Linux 系统,系统没有内置对应的字体,所以降级成了其他(丑陋的)字体。

所以,我们需要向服务端安装一个支持 CJK 的字体,然后再做构建。

安装字体

Google 一下,再看看 StackOverflow,就能找到答案了:

mkdir -p ~/.fonts
cp my-font.ttc ~/.fonts
fc-cache ~/.fonts
npm run build

这里的重点是把字体复制到 ~/.fonts 后,需要强制运行一下 fc-cache ~/.fonts 来刷新字体缓存。

选择字体

那么安装的字体如何选择呢?

最完美的方案当然是服务端和客户端使用同一种字体来做渲染,这就要求我们的客户端也要内置一份字体。

然而,CJK 的字符集太大,字体文件通常也特别大,所以一般网页都会优先使用系统内置的字体,CSS 如下:

body {
  font-family: -apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",...;
}

所以,从实践来看,服务端和客户端使用同一份字体并不现实。

那就退而求其次,找个开源的字体安装到服务器上。这里我选择了文泉驿,下载 wqy-zenhei.ttc 之后,内置到项目中,构建之前安装一下。

整合到 Gatsby

集成到一个 Gatsby 项目中,只需修改一下 package.json 中的 build 命令如下:

{
  // ...
  "scripts": {
    // ...
    "build": "mkdir -p ~/.fonts && cp assets/wqy-zenhei.ttc ~/.fonts && fc-cache ~/.fonts && gatsby build",
  }
}

© 2020