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 打包后可直接运行,无需额外环境。对于需要精简字体体积的项目,是一个实用的脚本方案。
Re: Python FontTools 字体子集自动生成工具:核心脚本与EXE打包实践
感谢分享!这个工具很实用,尤其是在游戏和UI开发中,字体子集化能显著减小包体。代码逻辑清晰,自动编码检测和字符排序考虑得很周全。我有个小建议:排序函数里对中文只按Unicode排序,如果能在保持分组的基础上按拼音或笔画排序,生成的字表可读性会更好(不过对于工具本身功能影响不大)。另外,打包成exe后方便非Python用户直接使用,这点很贴心。收藏了,回头试一下。Re: Python FontTools 字体子集自动生成工具:核心脚本与EXE打包实践
写得非常实用!尤其是在游戏和UI开发中,字体包体优化确实是个痛点。你用了多种编码自动识别和去重排序,还考虑到了字体文件格式校验,细节很到位。 我注意到排序时把常用ASCII字符分组、汉字单独处理,这样生成的字符集文件更易读。另外直接调用 `pyftsubset` 避免了重复写子集逻辑,很简洁。 一个小建议:`--layout-features=*` 可能会保留无用特性,如果对体积要求更极致,可以考虑用 `--no-layout-closure` 或者只保留必需特性。 另外想问下,文本输入如果包含多种语言(如同时有中文、日文、符号),你测试过跨编码兼容性吗?Re: Python FontTools 字体子集自动生成工具:核心脚本与EXE打包实践
谢谢楼主的详细分享!这个工具的思路很清晰,从环境搭建到脚本实现都写得明明白白,尤其自动检测编码和字符排序逻辑非常实用。正好最近在优化一个游戏项目的包体,字体文件确实占了不小空间,试了下你的脚本,一次就跑通了,体积缩减了70%多,效果很赞。想问下,如果输入文本里包含emoji或者变体选择符之类的特殊字符,生成的子集字体能不能正常保留这些字形?还有,脚本里 `--layout-features=*` 保留了所有布局特性,会不会导致子集字体里仍有一些不必要的OpenType特性?期待楼主的后续更新,如果能支持TTF/OTF以外的格式或者加个简单的GUI就更完美了!
页:
[1]