4. 文本资源管理
做多语言界面,说白了就是让屏幕能正确显示各种文字。中文、日文、韩文、阿拉伯文……每种语言都有自己的字符集。我刚开始接触TouchGFX时,以为只要把字库文件扔进去就行,结果一运行,满屏的方框。嗯,那感觉,就像你写好了代码却忘了加分号。
这一章,我们就来彻底搞定字体和字符集。我会把我在项目中踩过的坑、总结的经验,都摊开来讲。
4.1 理解Unicode与字符集
先问一个问题:为什么你的屏幕显示不了中文?
因为你的字体文件里没有中文字符的“形状”。每个字符在计算机里都有一个唯一的编号,这个编号就是Unicode。而字体文件,就是一张“编号→形状”的映射表。你想想看,如果表里没有“中”这个编号,那屏幕当然只能画个方框了。
TouchGFX支持Unicode,这是好事。但Unicode本身不包含字形,它只是标准。真正干活的是字体文件。常见的字符集编码方式有几种:
| 编码方式 | 特点 | 典型应用 |
|---|---|---|
| UTF-8 | 变长编码,兼容ASCII,存储效率高 | Web、现代嵌入式系统 |
| UTF-16 | 固定2字节或4字节,Windows和Java常用 | Windows API、Java内部 |
| UTF-32 | 固定4字节,简单但浪费空间 | 内部处理,极少用于存储 |
我个人习惯在TouchGFX项目里用UTF-8编码的源文件。为什么呢?因为UTF-8对ASCII字符只占1字节,而中文占3字节。虽然比GB2312的2字节多了一点,但胜在通用——你不需要为每种语言单独切换编码。
4.2 为不同语言配置字体
配置字体,不是简单地把一个.ttf文件拖进去就完事了。你需要考虑三件事:字体格式、字符覆盖范围、以及字体渲染效果。
4.2.1 字体格式选择
TouchGFX支持两种字体格式:位图字体和矢量字体。
- 位图字体:每个字符预先渲染成像素点阵。优点是渲染速度快,缺点是放大后模糊,且文件体积随字符数线性增长。
- 矢量字体:存储字符的轮廓曲线。优点是缩放不失真,文件体积相对较小,但渲染时需要计算,稍微慢一点。
我在一个低端MCU项目上用过位图字体,因为那个芯片没有FPU,矢量字体的贝塞尔曲线计算太慢了。但如果你用的是Cortex-M4以上,放心用矢量字体,性能完全扛得住。
4.2.2 中文字体配置
中文字体是最大的坑。一个完整的中文字体文件,比如宋体或黑体,动辄十几MB。你的MCU Flash才几MB,根本放不下。
怎么办?只包含你需要的字符。
TouchGFX的字体配置工具允许你指定一个“字符范围”。比如,你只需要显示“设置”、“确定”、“取消”这几个词,那就只把这几个字加进去。我见过有人把整个GB2312字符集都加进去,结果字体文件占了8MB,Flash直接爆了。
配置步骤很简单:
- 在TouchGFX Designer中,打开“Texts”面板。
- 选择你需要配置的字体,点击“Edit”。
- 在“Character Set”选项卡中,选择“Custom”,然后手动输入你需要的字符。
- 或者,选择“Range”,指定Unicode范围。比如中文常用字在0x4E00-0x9FFF之间。
我个人更推荐“Custom”方式。因为“Range”方式会把整个区间的字符都包含进去,哪怕你只用了其中10个字。比如0x4E00-0x9FFF包含了2万多个汉字,而你的界面可能只需要200个。
4.2.3 日文字体配置
日文比中文更复杂。它包含三种字符集:平假名、片假名、以及汉字(日文汉字)。平假名和片假名加起来不到200个字符,但日文汉字和中文汉字有重叠,也有差异。
比如“画”这个字,中文和日文写法一样,但Unicode编码不同。中文的“画”是U+753B,日文的“画”是U+753B(同一个编码)。但有些字,比如“働”,中文里没有,日文里有。
所以,配置日文字体时,我建议:
- 平假名和片假名:用日文字体文件,比如Noto Sans JP。
- 日文汉字:如果和中文汉字重叠,可以用中文字体;如果不重叠,必须用日文字体。
我曾经犯过一个错误:用中文字体显示日文界面,结果“働”字显示不出来,用户反馈说“这个按钮没有文字”。后来我加了一个日文字体作为回退,问题才解决。
4.3 处理字体回退(Font Fallback)
字体回退,说白了就是“这个字体没有这个字符,那就换另一个字体试试”。
TouchGFX支持字体回退机制。你可以在一个文本区域里指定多个字体,按优先级排列。当第一个字体找不到某个字符时,自动尝试第二个字体,以此类推。
配置方法:
- 在TouchGFX Designer中,选中一个文本控件。
- 在属性面板中,找到“Font”设置。
- 点击“+”号,添加多个字体。排在前面的优先级高。
举个例子:
字体列表:
1. NotoSansSC_Regular(中文字体,包含常用汉字)
2. NotoSansJP_Regular(日文字体,包含平假名、片假名、日文汉字)
3. NotoSans_Regular(英文字体,包含ASCII字符)
当显示“Hello 世界 こんにちは”时:
- “Hello” → 字体1没有这些英文字符?不对,中文字体通常也包含ASCII。所以字体1能显示。
- “世界” → 字体1能显示。
- “こんにちは” → 字体1没有平假名,回退到字体2。字体2能显示。
4.4 优化字体文件大小
字体文件是嵌入式系统里的“体积大户”。优化字体文件,是每个嵌入式UI工程师的必修课。
4.4.1 只包含需要的字符
这是最有效的方法。前面已经说过,不再重复。但我要强调一点:不要用“All Characters”选项。那个选项会把字体文件里所有字符都包含进去,哪怕你只用了1%。
4.4.2 使用子集化工具
TouchGFX自带的字体工具已经做了子集化。但如果你需要更精细的控制,可以用第三方工具,比如FontForge或pyftsubset。
举个例子,用pyftsubset提取一个字体文件中你需要的字符:
pyftsubset NotoSansSC-Regular.ttf \
--text="设置确定取消返回保存删除编辑添加" \
--output-file=NotoSansSC-Subset.ttf
这个命令只保留“设置确定取消返回保存删除编辑添加”这12个字符。生成的字体文件可能只有几KB。
4.4.3 降低字体精度
矢量字体在渲染时,需要将轮廓曲线转换成像素点阵。这个转换的精度越高,渲染效果越好,但字体文件也越大。
TouchGFX允许你设置“Glyph Precision”。默认是“High”,你可以改成“Medium”或“Low”。对于小字号(比如12px以下),降低精度几乎看不出区别,但文件体积能减少30%以上。
| 精度等级 | 文件体积 | 渲染质量 | 适用场景 |
|---|---|---|---|
| High | 100% | 最佳 | 大标题、高DPI屏幕 |
| Medium | ~70% | 良好 | 正文、按钮文字 |
| Low | ~50% | 可接受 | 小字号、低分辨率屏幕 |
4.4.4 合并字体文件
如果你有多个字体文件,比如一个中文字体和一个英文字体,可以考虑合并成一个。但要注意:合并后的字体文件体积是两个文件之和,而且管理起来更复杂。我个人不推荐合并,除非你的Flash空间实在紧张到按字节算。
4.5 本章知识体系
下面这张图,是我自己总结的字体与字符集配置流程。你可以把它当作一个检查清单:
这张图从理解Unicode开始,到选择字体格式,再到配置字符集和设置回退,最后用优化技巧收尾。你每次做多语言界面时,都可以按这个流程走一遍,基本不会漏掉什么。
好了,字体和字符集这块,核心就是这些。记住:不要贪多,够用就好。你的Flash空间是有限的,用户的耐心也是有限的。把每个字节都用在刀刃上,这才是嵌入式UI工程师的本事。