脚本专家 发表于 2026-6-12 09:00:00

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

在游戏开发、UI 设计或嵌入式项目中,原始字体文件往往包含大量未使用的字符,导致包体臃肿。通过 Python 结合 FontTools 库,可以自动提取文本中的可见字符并生成精简后的字体子集,大幅缩小体积。下面一步步搭建这个工具。

一、项目环境搭建

创建项目目录并进入:

cd /d F:\tools\fonttools


使用 Python 内置的 venv 创建虚拟环境:

python -m venv venv


激活虚拟环境(Windows):

venv\Scripts\activate

命令行出现 (venv) 前缀表示成功。

安装依赖:

pip install fonttools pyinstaller


验证 FontTools 可正常调用:

python -c "from fontTools.subset import main; print('OK')"

输出 OK 说明环境就绪。

二、核心脚本:font_subset_core.py

该脚本负责读取文本、去重排序、生成字符集文件,并调用 pyftsubset 生成子集字体。完整代码如下:


import sys
from pathlib import Path
from fontTools.subset import main as pyftsubset_main

FONT_EXTS = {".ttf", ".otf"}

def read_text_auto(path):
    """自动尝试多种编码读取文本文件"""
    data = Path(path).read_bytes()
    for enc in ("utf-8-sig", "utf-8", "gbk", "gb18030"):
      try:
            return data.decode(enc), enc
      except UnicodeDecodeError:
            pass
    return data.decode("gb18030", errors="ignore"), "gb18030-ignore"

def sort_key(c):
    code = ord(c)
    if '0' <= c <= '9':
      return (0, code)
    if 'A' <= c <= 'Z':
      return (1, code)
    if 'a' <= c <= 'z':
      return (2, code)
    if 0x4E00 <= code <= 0x9FFF:
      return (4, code)
    return (3, code)

def make_unique_chars(text):
    chars = {c for c in text if not c.isspace() and ord(c) >= 32}
    return "".join(sorted(chars, key=sort_key))

def build_subset(font_path, chars_path, out_font_path):
    pyftsubset_main([
      str(font_path),
      f"--text-file={chars_path}",
      f"--output-file={out_font_path}",
      "--layout-features=*",
      "--glyph-names",
      "--symbol-cmap",
      "--legacy-cmap",
      "--notdef-glyph",
      "--notdef-outline",
      "--recommended-glyphs",
    ])

def main():
    if len(sys.argv) < 3:
      print("用法: font_subset_core.exe 字体.ttf 文本.txt")
      return
    font_path = Path(sys.argv)
    text_path = Path(sys.argv)
   
    if not font_path.exists():
      print("字体文件不存在:", font_path)
      return
    if not text_path.exists():
      print("文本文件不存在:", text_path)
      return
    if font_path.suffix.lower() not in FONT_EXTS:
      print("第一个参数必须是字体文件:", font_path)
      return
   
    text, enc = read_text_auto(text_path)
    chars = make_unique_chars(text)
    chars_path = text_path.with_name(text_path.stem + "_chars.txt")
    chars_path.write_text(chars, encoding="utf-8")
    out_font_path = font_path.with_name(font_path.stem + "_subset" + font_path.suffix)
    build_subset(font_path, chars_path, out_font_path)
   
    print("处理完成")
    print("识别编码 :", enc)
    print("字符数量 :", len(chars))
    print("字符文件 :", chars_path)
    print("子集字体 :", out_font_path)

if __name__ == "__main__":
    main()


关键函数说明:


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


三、运行测试

在虚拟环境中执行:

python font_subset_core.py test.ttf test.txt


测试结果示例:

处理完成
识别编码 : utf-8-sig
字符数量 : 247
字符文件 : test_chars.txt
子集字体 : test_subset.ttf


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

四、打包为独立 EXE

方便在没有 Python 环境的目标机器上直接运行,使用 PyInstaller 打包:

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。使用方式:

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 打包后可直接运行,无需额外环境。对于需要精简字体体积的项目,是一个实用的脚本方案。

热心网友7 发表于 2026-6-12 09:05:00

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

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

热心网友2 发表于 2026-6-20 10:05:00

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

写得非常实用!尤其是在游戏和UI开发中,字体包体优化确实是个痛点。你用了多种编码自动识别和去重排序,还考虑到了字体文件格式校验,细节很到位。 我注意到排序时把常用ASCII字符分组、汉字单独处理,这样生成的字符集文件更易读。另外直接调用 `pyftsubset` 避免了重复写子集逻辑,很简洁。 一个小建议:`--layout-features=*` 可能会保留无用特性,如果对体积要求更极致,可以考虑用 `--no-layout-closure` 或者只保留必需特性。 另外想问下,文本输入如果包含多种语言(如同时有中文、日文、符号),你测试过跨编码兼容性吗?

热心网友1 发表于 2026-6-20 12:30:00

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

谢谢楼主的详细分享!这个工具的思路很清晰,从环境搭建到脚本实现都写得明明白白,尤其自动检测编码和字符排序逻辑非常实用。正好最近在优化一个游戏项目的包体,字体文件确实占了不小空间,试了下你的脚本,一次就跑通了,体积缩减了70%多,效果很赞。想问下,如果输入文本里包含emoji或者变体选择符之类的特殊字符,生成的子集字体能不能正常保留这些字形?还有,脚本里 `--layout-features=*` 保留了所有布局特性,会不会导致子集字体里仍有一些不必要的OpenType特性?期待楼主的后续更新,如果能支持TTF/OTF以外的格式或者加个简单的GUI就更完美了!
页: [1]
查看完整版本: Python FontTools 字体子集自动生成工具:核心脚本与EXE打包实践