字体文件的精简与压缩(使用 fontTools)

看见一个帖子:《教育部重編國語辭典修訂本》mdx版 - FreeMdict Forum,里面提到了字体精简与压缩的问题,因此我觉得可以把自己使用到的工具及经验分享给大家。

使用的系统为 Windows10,工具为 fontTools


【前期准备】

1. 首先,点开此电脑(或文件资源管理器),开启显示文件扩展名的选项。如下图:

image

开启该选项后以便于区分文件后缀、文件类型。

2. 第二步,下载并安装 Python 3.6 之后的版本。

如下图,进入 Python 官网下载适用于 Windows系统的安装包,目前最新的是 Python 3.10:

image

直接进入 Microsoft Store (微软应用商店)下载 Python 按理也可行,但我没有试验。

下载好安装包后,运行,出现下图安装界面(注意最下面的 Add Python to PATH 选项应当勾选,否则后续会多出很多步骤):

image

然后点击 Install Now (立即安装)即可。

3. 第三步,打开命令提示符。

如下图,左下角开始菜单旁有搜索按钮,点击,输入 cmd ,然后打开即可:

image

若搜索框搜不到,也可以打开文件资源管理器,按照下图的路径找:

image

打开后应当显示的是如下页面,其中 C:\Users\ 后面是你登录Windows系统时的用户名,每个人各不同,不需要在意:

image

接着,直接键入 python ,然后按键盘上的 Enter 建(下文统称“回车”)。若 Python 安装成功,且第二步提到的 Add Python to PATH 勾选了,则如下图:

image

后续操作 fontTools 不需要进入 Python,因此可以退出该界面,键入 exit() 后回车,或 按 Ctrl 和 Z 并回车即可。实在关不掉的话,右上角叉掉cmd的窗口,重新打开一遍就行。

4. 第四步,安装 fontTools。

如下图,键入 pip install fonttools 后回车即可:

image

若出现一行黄色字,说 pip version 等等之类的,不用担心,是 pip 需要更新,不更新暂时也没事。

键入 pip list,回车。如下图,可以看到 fontTools 已经安装好了:

image


【正文开始】

5. 第五步,准备好字体文件和要保留的字库文本。

如下图,需要先准备好一个文本文档(内含需要保留的字符,空格、换行等不影响效果),此处该文档被重命名为 characters.txt

image

接着,字体也准备好。此处用于示例的字体文件是 FSung-1.ttf (全宋體-1)。

我把字体文件与上一步准备好的文本文档都放在了 C:\Users\Medic\font\ 目录下(即 C盘\用户\自己的用户名\fonts 这个文件夹中)。如下图:

image

6. 第六步,使用 fontTools 的子集化功能 pyftsubset 来精简字体。

如下图。回到 cmd,键入

pyftsubset font/FSung-1.ttf --text-file=font/characters.txt

image

图中框起来的部分含义如下:

  • pyftsubset 是 fontTools 内置的一款工具,用于字体子集化。

  • font/FSung-1.ttf 是字体文件的位置。如第五步所述,FSung-1.ttf 这个字体文件在 C:\Users\Medic\font\ 文件夹中,而当前 cmd 已经处于 C:\Users\Medic\ 下,因此此处只需要标明 字体文件FSung-1.ttf 位于 font文件夹即可。
    如果搞不清楚,那此处也可直接填入字体文件的完整位置,例如:

pyftsubset C:\Users\Medic\font\FSung-1.ttf --text-file=C:\Users\Medic\font\characters.txt
  • --text-file= 表示后面所跟的文件内含字体中需要保留的字符。

  • font/characters.txt 参见上面两条说明。


【正文结束,下面是可选步骤(非必选)】

7. 第七步,添加 --flavor=woff2 参数以生成 woff2 格式的压缩字体。

如下图,如果直接键入,则会报错,提示没有 Brotli 模块:

image

不要慌,按照第四步的方式,用 pip install brotli 安装一下即可。如下图第一个红框:

image

然后重新这样键入就没有问题了,可以成功生成。如上图第二个红框:

pyftsubset font/FSung-1.ttf --text-file=font/characters.txt --flavor=woff2

第六步(精简)与第七步(精简并压缩)的生成结果:

image

注: 源字体文件在哪,精简和压缩后的文件就在哪,且输出的字体文件名默认是 源字体文件名.subset。如果有需要,可以使用 --output-file= 参数来指定输出位置及输出文件名。

8. 其他:不精简,只压缩。

使用 fonttools ttLib.woff2 compress 即可。例如:

fonttools ttLib.woff2 compress font\FSung-1.ttf

参考:fontTools 官方文档

9 Likes

感謝教學,這樣就能本機完成字型打包了

1 Like

這個帖子寫得很詳細,不會 Python 也不影響實際操作。 :100:

1 Like

请问能不能批量转换字体格式?英文盲,看不懂。先谢!

otf或ttf格式转成woff2格式只要用到上面的第8步即可,把最后的文件名换一下就可以。

批量的话,我刚刚看了一下文档,没找到方法,估计用脚本可以做到,可惜我不太会。

多谢!在你的提示下硬着头皮翻看文档,找到python脚本的用法。再次感谢!


fontTools.ttLib.woff2.compress(input_file, output_file, transform_tables=None)[source]

    Compress OpenType font to WOFF2.

    Parameters

            input_file – a file path, file or file-like object (open in binary mode) containing an OpenType font (either CFF- or TrueType-flavored).

            output_file – a file path, file or file-like object where to save the compressed WOFF2 font.

            transform_tables – Optional[Iterable[str]]: a set of table tags for which to enable preprocessing transformations. By default, only ‘glyf’ and ‘loca’ tables are transformed. An empty set means disable all transformations.


补充一下,制作字符表可以用emeditor,把源文件弄成一个字一行(正则替换一下,应该是把(.)换成\1\n)然后删去重复行保存就行了,直接上源文件浪费很多不必要的时间

浏览器对外挂字体的大小有限制,但是不知道是多少,所以全宋体有一部分不能用@font-face加载,就算压成woff2也不行

1 Like

字体大小限制我没有留意过,于是刚刚去查了一下,好像没有找到这方面的说明

应该是解压后不能超过30MB

https://groups.google.com/a/chromium.org/g/blink-reviews/c/eadz2s_Z0Tw?pli=1

看了一下后续进展,现在新版的那个什么OTS对字体文件的大小限制好像提升到了150兆还是300兆来着,正常使用应该足矣

1 Like

等到兼容性限制放开都不知道要多长时间,至少最近的版本的chrome是不行的。

感谢分享。
这个挺有用的

有个问题:
如果提取的子集分布在多个字体文件,如
FSung-p, FSung-1, FSung-2, FSung-3, FSung-F, FSung-X
应该如何使用、以提取子集到一个子文件中呢?

pyftmerge

1 Like

谢谢,光顾着看pyftsubset 的说明了

merge: Merge multiple fonts into one — fontTools Documentation

再请教A兄:
制作mdx或epub碰到UTF-8编码是6位的生僻字,我通常用&#x02538A编码的方式,这样文本编辑过程中复制粘贴删除等不容易出错。

为了提取字体子集,用脚本把文本中所有的&#x02538A这样的编码都提取了出来,每个一行,删除重复。得到的清单类似:
characters.zip (258 字节)

但这个清单还无法直接用pyftsubset处理。不知道有啥好办法?


我把txt文件改为html后缀在浏览器中打开、然后再复制到VScode等文本编辑器中。但总有一些字符出问题。

比如清单中有两个字符的UTF编码分别为2EBC、2EA9,用&#x2EBC &#x2EA9 在html文件中显示并复制后,在某些文本编辑器中无法粘贴,很奇怪。

你想把汉字都放到一个文件里吧?
这是不可能的。
一个字体文件,存在字符限制65535。
汉字太多了,一个文件放不下去的,至少得有两个文件。(这些没算私字。全宋体的私字,都有两个文件。)

必要性确实不大,也就是少加载几个资源而已。

fonttools的命令行工具好像只支持utf8,要合并也是先subset再merge,我在教育部那个词典里合并的原因是我只需要用它来显示系统不能显示的字体,把常用字体符号都去掉了,剩下的文件都很小,3区、F区和X区甚至只剩下四五个字,就干脆合并了。合并后woff2也就200k左右,ttf也就400多k

是的。一本书的生僻字可能在不同的字体文件,做子集的目的也是为了确保能跨设备跨平台正确显示,而不必要把所有的100M字体都附上。先 pyftsubset 再 pyftmerge 也不麻烦。
不过,这也是一些APP内含字体的epub用来加密的原理。

现在我的问题是:已经知道生僻字的Unicode二进制代码,为了 subset 先得将这些代码批量转为 UTF-8 的存储编码。