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中包含空格

  • jingfly reply

    你好,java -jar bin\sfnttool.jar -c ./\ChineseOutPut.txt ./\unChineseOutPut.txt ./aa.ttf ./\output\./aa.ttf 这个命令中 aa.ttf 需要怎么制作呢?然后还发现一个问题,

    这个换行符也会出现乱码,\我是已经加入进去的了,请问下可能是什么原因呢,感谢!

Tags

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

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