查看: 231|回复: 1

Python FontTools 字体子集自动生成工具:核心脚本与EXE打包实践

[复制链接]
发表于 2 小时前 | 显示全部楼层 |阅读模式
在游戏开发、UI 设计或嵌入式项目中,原始字体文件往往包含大量未使用的字符,导致包体臃肿。通过 Python 结合 FontTools 库,可以自动提取文本中的可见字符并生成精简后的字体子集,大幅缩小体积。下面一步步搭建这个工具。

一、项目环境搭建

创建项目目录并进入:
  1. cd /d F:\tools\fonttools
复制代码

使用 Python 内置的 venv 创建虚拟环境:
  1. python -m venv venv
复制代码

激活虚拟环境(Windows):
  1. venv\Scripts\activate
复制代码
命令行出现 (venv) 前缀表示成功。

安装依赖:
  1. pip install fonttools pyinstaller
复制代码

验证 FontTools 可正常调用:
  1. python -c "from fontTools.subset import main; print('OK')"
复制代码
输出 OK 说明环境就绪。

二、核心脚本:font_subset_core.py

该脚本负责读取文本、去重排序、生成字符集文件,并调用 pyftsubset 生成子集字体。完整代码如下:
  1. import sys
  2. from pathlib import Path
  3. from fontTools.subset import main as pyftsubset_main
  4. FONT_EXTS = {".ttf", ".otf"}
  5. def read_text_auto(path):
  6.     """自动尝试多种编码读取文本文件"""
  7.     data = Path(path).read_bytes()
  8.     for enc in ("utf-8-sig", "utf-8", "gbk", "gb18030"):
  9.         try:
  10.             return data.decode(enc), enc
  11.         except UnicodeDecodeError:
  12.             pass
  13.     return data.decode("gb18030", errors="ignore"), "gb18030-ignore"
  14. def sort_key(c):
  15.     code = ord(c)
  16.     if '0' <= c <= '9':
  17.         return (0, code)
  18.     if 'A' <= c <= 'Z':
  19.         return (1, code)
  20.     if 'a' <= c <= 'z':
  21.         return (2, code)
  22.     if 0x4E00 <= code <= 0x9FFF:
  23.         return (4, code)
  24.     return (3, code)
  25. def make_unique_chars(text):
  26.     chars = {c for c in text if not c.isspace() and ord(c) >= 32}
  27.     return "".join(sorted(chars, key=sort_key))
  28. def build_subset(font_path, chars_path, out_font_path):
  29.     pyftsubset_main([
  30.         str(font_path),
  31.         f"--text-file={chars_path}",
  32.         f"--output-file={out_font_path}",
  33.         "--layout-features=*",
  34.         "--glyph-names",
  35.         "--symbol-cmap",
  36.         "--legacy-cmap",
  37.         "--notdef-glyph",
  38.         "--notdef-outline",
  39.         "--recommended-glyphs",
  40.     ])
  41. def main():
  42.     if len(sys.argv) < 3:
  43.         print("用法: font_subset_core.exe 字体.ttf 文本.txt")
  44.         return
  45.     font_path = Path(sys.argv[1])
  46.     text_path = Path(sys.argv[2])
  47.    
  48.     if not font_path.exists():
  49.         print("字体文件不存在:", font_path)
  50.         return
  51.     if not text_path.exists():
  52.         print("文本文件不存在:", text_path)
  53.         return
  54.     if font_path.suffix.lower() not in FONT_EXTS:
  55.         print("第一个参数必须是字体文件:", font_path)
  56.         return
  57.    
  58.     text, enc = read_text_auto(text_path)
  59.     chars = make_unique_chars(text)
  60.     chars_path = text_path.with_name(text_path.stem + "_chars.txt")
  61.     chars_path.write_text(chars, encoding="utf-8")
  62.     out_font_path = font_path.with_name(font_path.stem + "_subset" + font_path.suffix)
  63.     build_subset(font_path, chars_path, out_font_path)
  64.    
  65.     print("处理完成")
  66.     print("识别编码 :", enc)
  67.     print("字符数量 :", len(chars))
  68.     print("字符文件 :", chars_path)
  69.     print("子集字体 :", out_font_path)
  70. if __name__ == "__main__":
  71.     main()
复制代码

关键函数说明:


  • read_text_auto:依次尝试 UTF-8 with BOM、UTF-8、GBK、GB18030 解码,保证中文文本兼容。
  • sort_key:将字符排序为数字、大写字母、小写字母、其他字符、CJK 汉字(按 Unicode 码位),输出符合日常阅读习惯。
  • make_unique_chars:过滤空白和控制字符,返回去重排序后的字符串。
  • build_subset:封装 pyftsubset 命令,保留所有布局特性、字形名称、符号映射及推荐字形。


三、运行测试

在虚拟环境中执行:
  1. python font_subset_core.py test.ttf test.txt
复制代码

测试结果示例:
  1. 处理完成
  2. 识别编码 : utf-8-sig
  3. 字符数量 : 247
  4. 字符文件 : test_chars.txt
  5. 子集字体 : test_subset.ttf
复制代码

过程自动完成:读取文本 → 去重排序 → 生成 chars.txt → 调用 pyftsubset → 生成 subset.ttf。

四、打包为独立 EXE

方便在没有 Python 环境的目标机器上直接运行,使用 PyInstaller 打包:
  1. python -m PyInstaller -F --clean --collect-all fontTools --name FontSubset font_subset_core.py
复制代码

参数含义:

  • -F:生成单个 exe 文件。
  • --clean:清理上次打包缓存。
  • --collect-all fontTools:强制收集 FontTools 全部模块及依赖。
  • --name:指定输出 exe 名称。


打包后得到 dist/FontSubset.exe。使用方式:
  1. FontSubset.exe FZBWKS.ttf all_text.txt
复制代码

执行后会在同目录生成 all_text_chars.txt(字符集)和 FZBWKS_subset.ttf(子集字体)。适用于传奇游戏、手游项目、UI 字体优化等场景,可大幅缩小字体体积。

五、补充:字体加粗处理

生成的子集字体可能需要加粗效果。可以借助 FontForge 工具手动调整:

1. 打开生成的 subset.ttf。
2. 按 Ctrl+A 全选所有字形。
3. 选择菜单“元素 → 字体加粗”,设置加粗单位(如 6 em units),中文字体建议保留间距。
4. 导出为字体文件即可。

此步骤属于字体后期微调,与 Python 脚本配合即可完成完整字体优化流程。

六、总结

本工具通过 Python + FontTools 实现了从文本到字体子集的自动化生成,支持 .ttf / .otf 格式。借助 PyInstaller 打包后可直接运行,无需额外环境。对于需要精简字体体积的项目,是一个实用的脚本方案。
回复

使用道具 举报

发表于 2 小时前 | 显示全部楼层

Re: Python FontTools 字体子集自动生成工具:核心脚本与EXE打包实践

感谢分享!这个工具很实用,尤其是在游戏和UI开发中,字体子集化能显著减小包体。代码逻辑清晰,自动编码检测和字符排序考虑得很周全。我有个小建议:排序函数里对中文只按Unicode排序,如果能在保持分组的基础上按拼音或笔画排序,生成的字表可读性会更好(不过对于工具本身功能影响不大)。另外,打包成exe后方便非Python用户直接使用,这点很贴心。收藏了,回头试一下。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-6-12 11:09 , Processed in 0.034494 second(s), 18 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部