如何减少字体文件尺寸
本 blog 最初选定英文字体的时候选择了Great Vibes这个字体,相对之下中日文的粗体字就会显得很不搭,一直在考虑解决。如果只是指定serif的话在 mac 端上能够显示想要的效果但是其他客户端都不行。自然应该是系统自带字体库的问题,最简单方法就是再加载一个外部字体。中日文都支持的字体里最有名的应该是noto-serif-sc,但是因为字符数很多,文件将近 7.7Mb。就算初次加载之后有 cache,初次加载的 cost 也太大了。
一顿搜索之后找到了fonttools这个开源项目,其中提供的pyftsubset这个工具可以对字体文件进行裁剪取出需要的字符。可惜并没有找到 node 对应的项目。
$ pyftsubset --help
...
--unicodes=<XXXX>[,<XXXX>...]
Specify comma/whitespace-separated list of Unicode codepoints or
ranges as hex numbers, optionally prefixed with 'U+', 'u', etc.
For example, --unicodes=41-5a,61-7a adds ASCII letters, so does
the more verbose --unicodes=U+0041-005A,U+0061-007A.
The special strings '*' will choose all Unicode characters mapped
by the font.
...
接下来使用gatsby的 query 获取所有的标题进行处理,在gatsby-node.js中加入prebuild的 hook:
query 如下:
{
allMdx {
edges {
node {
frontmatter {
title
}
}
}
}
}
整理 unicode 字符串并外部执行 py 命令:
const unicodes = [...new Set(titles.join("").replace(/[a-zA-Z0-9\s]/g, ""))]
.map(
char =>
"U+" +
char
.charCodeAt(0)
.toString(16)
.padStart(4, "0")
)
.join(",")
合并两者:
exports.onPreBuild = ({ graphql }) => {
console.log('preBuild fonts!')
return graphql(`
{
allMdx {
edges {
node {
frontmatter {
title
}
}
}
}
}
`).then(result => {
return new Promise(ok => {
const titles = result.data.allMdx.edges.map(
({ node }) => node.frontmatter.title
)
const unicodes = [...new Set(titles.join("").replace(/[a-zA-Z0-9\s]/g, ""))]
.map(
char =>
"U+" +
char
.charCodeAt(0)
.toString(16)
.padStart(4, "0")
)
.join(",")
exec(
`pyftsubset static/fonts/NotoSerifSC-Regular.woff2 --unicodes="${unicodes}" --flavor="woff2" `,
(err) => {
if (err) {
throw err
};
ok();
)
}
)
})
}
现在再确认一眼生成的NotoSerifSC-Regular.subset.woff2只有 72kB,差距还是非常巨大的。