FontPruner 字体精简工具

需求

FontPruner 是我们 16 年底开发的一个小工具,可以通过白名单机制来消除字体文件 (.ttf) 中大量的冗余符号,减小字体文件的尺寸,从而降低其包体占用和内存开销。

我们知道,中文字体普遍个头比较大,小的 3-5 MB,大一些的 10-20 MB,有的甚至还更大一些。而通过 FontForge 这样的工具可以看出,汉字仅占字体文件的一半不到(其他为日韩等语言和特殊符号),而其中常用 3000 字又仅占汉字总量的5%,所以理论上这个开销能缩减到原尺寸的 1/10 以内。

(下图是我机器上已安装的字体列表)

font-types.png


思路

要精简这些字体的尺寸,一个简单的做法是,找到中文,英文,标点符号它们各自的起止点,把在范围之外的其他语言字符去掉。这样做逻辑上很简单,但是有几个问题,一是游戏文本中可能用到特殊字符没有在我们框定的范围内,二是汉字中有 80% 是实际上使用频率极低的冷僻字,而偏偏是这些冷僻字笔画特别多,用于描述字形的 glyph data 的数据量,比常用的简化字要大得多。

那么有没有更好的方法呢?

一个改进的方案是,扫描所有游戏中可能出现的文本信息,生成一个活跃字表 (Active Character Table, ACT),然后把这个表之外的字符全部剔除。

使用这种白名单的方式,我们可以更精确地释放那些冷僻字占用的空间,但需要注意的事情有两点:

  1. 当游戏内容改变时,要及时更新我们的 ACT 表。
  2. 聊天框的用户输入可能出现任意字符,建议直接使用系统已有的默认字体

这个白名单方案有一个额外的好处,就是能顺便帮我我们找出所有游戏内的可显示文本,有助于统一规范化游戏内所有文本的引用,便于之后可能的本地化工作。


原理

基于上面说的白名单方案,我们制作了工具 FontPruner,它的主要工作原理如下:

p1.png

简单地解释一下,FontPruner 首先收集游戏内可能出现在屏幕上的各类文本,生成白名单(也就是曾经在游戏中出现过所有字符的集合),然后调用 sfntly 对字体文件做精简处理。


流程

具体的处理流程如下所示:

p2.png

我们可以定期在 Build 机器上执行此工具,这样游戏内容变更时,可以自动更新对应的字体文件。


效果

接下来是精简的具体效果:

p3.png

上图分别是 ttf 包含 1/4/100/200/500 个中文字体时,文件尺寸的变化情况。根据该测试数据可知,在包含 5000 常用字的情况下,对于一个 28MB 的字体文件,我们可以把字体的开销控制在 3-5MB 左右。


资源

关于此工具的更多信息,请访问下面的链接:

https://github.com/GameBuildingBlocks/FontPruner

如果基于此工具有好的想法,欢迎一起讨论~


Gu Lu

Comments
Write a Comment
  • Zs9024 reply

    你好,使用中碰到字体没有正确生成(大小只有9k)的问题,不知道是不是使用姿势不对?

    以下是命令行内容:

    D:\Project\Font\FontPruner-master>python FontPruner.py --inputPath=temp --inputF

    ont=MicrosoftYaHei-Bold.ttf --tempPath=temp

    {'--inputFont': ['MicrosoftYaHei-Bold.ttf'],

    '--inputPath': ['temp'],

    '--tempPath': 'temp'}

    path = temp

    temp\1.txt

    genFilePathList completed temp temp\input_filelist.txt

    提取完成

    extractFileString completed

    fontNameMicrosoftYaHei-Bold.ttf

    extracted file :tmp\intermediate\ChineseOutPut.txt

    extracted file :tmp\intermediate\unChineseOutPut.txt

    文本不存在或者无效tmp\intermediate\ChineseOutPut.txt

    文本不存在或者无效tmp\intermediate\unChineseOutPut.txt

    bulidNewFont completedMicrosoftYaHei-Bold.ttf

    字体文件和temp目录都在FontPruner.py同级,intermediate目录下的文件正常

    另:1).lua文件可以提取,但是.cs文件不行;2)可以支持excel的提取吗?实际的原始文本配置基本都在excel中。

    谢谢

  • 雨水 reply

    你好,下载以后用pycharm运行后,一直提示Usage:

    FontPruner.py --inputPath=<tmp>... --inputFont=<simkai.ttf>... [--tempPath=<tmp>],麻烦问一下,具体的使用流程呢,谢了

  • 2431713873 reply

    空格怎么添加呢,我使用了这个工具后,空格加不进去,不清楚,是不是哪边没有处理好,可以分享下方案吗

    • Funny reply

      @2431713873 最后调用sfntly -c的时候,空格被过滤掉了,可以使用java -jar bin\sfnttool.jar -c ./\ChineseOutPut.txt ./\unChineseOutPut.txt ./aa.ttf ./\output\./aa.ttf 此时ChineseOutPut.txt中包含空格

Tags

游戏开发   随笔   Programming   C/C++   优化   Unity   C++   知乎   游戏设计   比特币   Unity3D   区块链   软件开发   引擎设计   系统架构   Production   idtech   中国文化   加密货币   项目管理   Bitcoin   游戏评论   资源管理   资源流水线   效率   道德经   网络   方法论   模板编程   Lua   Oculus   GDC   渲染   VR   PerfAssist   Unity MemoryProfiler   BCH   经济学   信息过载   行业报告   字体   Productivity   图形   网络编程   Dice   协程   EMC   Premake   测试   中间件   Game Engine   新手引导   Methodology   CI   命令行解析   goroutine   ndk   Ethereum   nanomsg   自动化   Scripting   摘录   Debugging   同步技术   cppcon   C++模板   DOOM3   技术评估   Unity GC   C++11   学习方法   Surface Pro 3   Engine Evaluation   CRT   文化   Blockchain   笔记   golang   图形编程   多线程   ETH   Blockchain Computing   Bitcoin Cash   cppcon14   Bitcoin SV   Visual Studio   Unity Coroutine   跨语言可变参数列表   团队协作   货币   Deployment   Visual Assist   工程改进   Michael Abrash   exp   开放世界   域名   虚拟现实   系统重构   slua   遮挡剔除   完美转发   协作式调度   Modern C++   类型推导   Memory Debugging   个人成长   小故事   BTC   暴雪   产品   历史   错误处理   Unity Profiler   MOD  

知识共享许可协议
本作品由Gu Lu创作,采用知识共享Attribution-NonCommercial-NoDerivatives 4.0 国际许可协议进行许可。