字体字符集自动提取¶
背景¶
EmbeddedGUI 的字体资源在编译期由 ttf2c.py 将 TTF 转换为 C 数组,只编译实际用到的字符,以节省 ROM 空间。这意味着:每次在代码中新增中文文本或图标字符,都必须把对应的字符写入字体的字符集文件(text 字段指定的 .txt 文件),否则运行时会显示方框或空白。
手动维护字符集文件存在以下问题:
容易遗漏:代码中的字符串分散在多个文件、多个静态数组、双语宏等位置。
格式不统一:有些项目把所有字符合并成一行,维护时难以分辨哪些字符来自哪段代码。
多字体时更繁琐:一个应用可能同时用 NotoSansSC(中文)和 MaterialSymbolsOutlined(图标),两套字符集需要分开管理。
为解决上述问题,提供了自动提取脚本 scripts/tools/extract_font_text.py。
工作流程¶
C 源文件 (*.c / *.h)
│
▼
extract_font_text.py ← 扫描提取所有文本字符串
│
├─ cn_text.txt ← 中文字体字符集(每行一条字符串)
└─ icon_text.txt ← 图标字体字符集(每行一个 &#xNNNN; 实体)
│
▼
make resource_refresh ← 重新生成 C 字体数组
脚本会自动:
读取
example/{APP}/resource/src/app_resource_config.json,根据字体文件名推断字体类型(中文 / 图标 / 拉丁)。扫描应用目录下的所有
.c和.h文件,通过以下多种规则提取字符串:S("EN", "中文")双语宏——提取第二参数中文部分(最可靠)flag ? "EN" : "中文"或flag ? "中文" : "EN"三元表达式——两侧都提取,因为同一 widget 使用 CN 字体渲染两种状态xxx_cn[]静态数组——提取数组中的所有字符串代码中直接出现的 CJK 字符串(catch-all 保底)
\xNN\xNN等 hex 转义字节,解码后属于 PUA 范围的图标字符
每条字符串单独写一行,便于后续维护。
图标字符以
&#xNNNN;HTML 实体格式输出(ttf2c.py原生支持该格式)。默认使用追加模式:不删除已有行,只补充新字符串,不会破坏手动维护的内容。
app_resource_generate.py会在生成字体时对所有字符去重,因此文本文件中的重复字符串无害。
快速上手¶
基本用法¶
# 追加模式:提取并填充新字符(不覆盖已有内容)
python scripts/tools/extract_font_text.py --app HelloShowcase
# 预览模式:只打印提取结果,不写入任何文件
python scripts/tools/extract_font_text.py --app HelloShowcase --dry-run
# 覆盖模式:完全重建 text 文件(首次迁移旧项目时使用)
python scripts/tools/extract_font_text.py --app HelloShowcase --overwrite
完整开发循环¶
# 1. 在 C 代码中新增中文文本或图标字符后,重新提取
python scripts/tools/extract_font_text.py --app HelloShowcase
# 2. 重新生成字体资源
make resource_refresh APP=HelloShowcase
# 3. 编译验证
make all APP=HelloShowcase
字体类型判断规则¶
脚本根据字体文件名中的关键词自动判断字体类型,无需手动配置:
类型 |
触发关键词示例 |
提取内容 |
|---|---|---|
中文(CN) |
|
代码中的 CJK 字符串 |
图标(Icon) |
|
解码后为 PUA(U+E000~F8FF)的字符 |
拉丁(Latin) |
其余 |
跳过(ASCII 字符由默认字体提供) |
文本文件格式¶
中文字体(cn_text.txt)¶
每行写一条完整的字符串,即代码中实际出现的文本,包括标点和空格:
基础
切换
滑块/选择
动态 42
多行文本块
你好
请输入...
选项 1
选项 2
这种格式的优点:
可读性强:一眼可知哪些字符来自哪段界面文本。
方便 diff:每次新增字符串只会追加新行,不会修改已有行,Git 历史清晰。
允许重复:
app_resource_generate.py内部自动去重,同一汉字出现多次不影响生成结果。
图标字体(icon_text.txt)¶
图标字符位于 Unicode PUA 区域,无法直接输入,以 HTML 实体表示:


ttf2c.py 在读取文本文件时会自动将 &#xNNNN; 解析为对应的 Unicode 字符。
如何获取图标的 Unicode 码点?
在代码中,图标通常以\xEE\x94\x98这样的 UTF-8 hex 转义出现。
将其字节序列按 UTF-8 解码即得到码点,例如:
\xEE\x94\x98→ UTF-8 字节EE 94 98→ U+E518 →
多字体场景¶
若同一应用使用多个中文字体(如不同字号),app_resource_config.json 中会有多个 CN 类型的字体条目,各自指向不同的 text 文件。
{
"font": [
{
"file": "build_in/NotoSansSC-VF.ttf",
"name": "notosanssc_14",
"pixelsize": "14",
"text": "cn_text_14.txt"
},
{
"file": "build_in/NotoSansSC-VF.ttf",
"name": "notosanssc_20",
"pixelsize": "20",
"text": "cn_text_20.txt"
}
]
}
脚本会为每个字体的 text 文件都填充完整字符集(安全做法)。如需按字号精确拆分,只保留各自需要的字符串行即可。
命令行参数¶
参数 |
说明 |
默认值 |
|---|---|---|
|
应用名称(必填) |
— |
|
源代码目录 |
|
|
资源 src 目录 |
|
|
预览提取结果,不写入文件 |
关闭 |
|
覆盖模式,重建 text 文件 |
关闭(默认追加) |
常见问题¶
某个 ASCII 字母用 CN 字体渲染时显示方框¶
典型场景:btn_lang 始终使用 CN 字体,其文本在中英切换之间交替:
egui_view_label_set_text(..., is_chinese ? "EN" : "中文");
“EN” 中没有 CJK 字符,之前版本的提取脚本会忽略它。现在脚本通过三元表达式规则(? "latin" : "cjk" 两侧都提取)自动处理此类情况。如果使用旧版本脚本生成了字符集,应重新运行 extract_font_text.py --overwrite。
某个汉字仍然显示方框¶
原因:该字符未被脚本提取到(例如通过运行时拼接生成的字符串)。
修复:在对应的 text 文件中手动添加包含该字符的行,然后重新执行 make resource_refresh。
运行脚本后字符集文件没有变化¶
可能的原因:
追加模式下,所有字符已存在于 text 文件中——说明字符集是最新的,无需操作。
配置文件中该字体没有指定
text字段——需要在app_resource_config.json的字体条目中添加"text"字段。
图标字符显示为空白(不是方框)¶
图标字符进入了 text 文件并生成了字体资源,但渲染时没有设置颜色。alpha 格式的图标不带颜色信息,需要在代码中调用:
egui_view_image_set_image_color(view, EGUI_COLOR_MAKE(255, 255, 255), EGUI_ALPHA_100);