字体

1. 支持的字体

Solution 当前支持以下字体能力:

  1. 提供两种字体实现方式:点阵字体(bitmap)和 FreeType 字体。

  2. 点阵字体支持 LVGL 生成的 bitmap 格式。

  3. FreeType 字体当前仅支持 .ttf 格式。

  4. 支持同时使用多个 bitmap 字体和多个 FreeType 字体。

  5. 支持标准 Emoji。

  6. 支持为指定对象(obj)固定字体。

  7. 提供可免费使用的 tiny 压缩字体,字重为 55,支持 27000+ 简繁体汉字,占用空间约 1.06 MB。

  8. 提供可免费使用的印地语变形(shape)支持,包含以下语种:

语言

Language

印地语

Hindi

马拉地语

Marathi

梵语

Sanskrit

尼泊尔语

Nepali

迈蒂利语

Maithili

孔卡尼语

Konkani

多格拉语

Dogri

博多语

Bodo

  1. Solution V2.6.0 及以后版本新增支持以下语种的变形(shape):

语言

Language

孟加拉语

Bengali

阿拉伯语

Arabic

波斯语

Persian/Farsi

说明: 使用 Solution V2.6.0 及以后版本新增的语种变形能力时,Code Size 需要增加 850KB。如果需要支持其他需要 shape 的语言,需由客户自行提供支持变形算法及对应 .ttf 的方案。

2. 字体放置目录

在 Solution 方案中,资源统一存放在 resource 目录下,字体文件需要放在 resource/fonts 目录中。

3. .ttf 字体的增删

  1. 新增字体:将新的 .ttf 文件放到 solution/examples/xxx/resource/fonts/freetype 目录下。

  2. 调整顺序:编辑 ttf_order.txt,将新增字体加入文件并设置顺序。UI 显示文字时,会按照 ttf_order.txt 中的顺序遍历字体并查找字形。

  3. 删除字体:删除对应的 .ttf 文件即可。

4. .ttf 字体选择

可通过 Butterfli 工具的 UI 选择需要参与编译的字体。

⚠️ 注意

solution/examples/xxx/resource/fonts/freetype 目录下的 .ttf 文件中,除 _tiny55_full_tiny55_litehindi_ttf 外,其余字体仅用于功能展示。若客户需要商用,请务必确认字体版权。

5. .ttf 字号设置

5.1 默认字号

lvsf_font.h 中定义了默认字号,可通过修改对应宏进行调整。

5.2 自定义字号

如果项目需要使用不同字号,可按以下步骤配置:

  1. solution/framework/__template__/project 目录中复制子目录 __applicaiton_private__ 到对应的 HCPU 和 Simulator 目录。

  2. menuconfig 中使能 FT_SIZE_SELF_DEFINED

  3. 修改 __applicaiton_private__/ft_size_custom_reg.h 中的字号定义。

ft_size_custom_reg.h 示例:

/**
 * @brief  Register fonts through this interface.
 *         The font size must be arranged from small to large.
 *         Because font is searched from small to large.
 */
#ifdef FT_SIZE_SELF_DEFINED

typedef enum
{
    FONT_SMALL      = 16,
    FONT_NORMAL     = 20,
    FONT_SUBTITLE   = 24,
    FONT_TITLE      = 28,
    FONT_BIGL       = 36,
    FONT_HUGE       = 56,
    FONT_SUPER      = 72,
} FONT_SIZES;

#define LVSF_FREETYPE_FONT_REGISTER(freetype_font)                          \
    extern lv_font_freetype_lib_dsc_t CONCAT_2(freetype_font, _lib);        \
    SECTION_ITEM_REGISTER(FONT_SECTION_NAME, static const font_desc_t CONCAT_2(freetype_font, _reg_list)[]) =     \
    {                                                                       \
        LVSF_FONT_REGISTER(freetype_font, 10),                              \
        LVSF_FONT_REGISTER(freetype_font, 11),                              \
        LVSF_FONT_REGISTER(freetype_font, 12),                              \
        LVSF_FONT_REGISTER(freetype_font, 13),                              \
        LVSF_FONT_REGISTER(freetype_font, 14),                              \
        LVSF_FONT_REGISTER(freetype_font, 15),                              \
        LVSF_FONT_REGISTER(freetype_font, 16),                              \
        LVSF_FONT_REGISTER(freetype_font, 17),                              \
        LVSF_FONT_REGISTER(freetype_font, 18),                              \
        LVSF_FONT_REGISTER(freetype_font, 19),                              \
        LVSF_FONT_REGISTER(freetype_font, 20),                              \
        LVSF_FONT_REGISTER(freetype_font, 21),                              \
        LVSF_FONT_REGISTER(freetype_font, 22),                              \
        LVSF_FONT_REGISTER(freetype_font, 23),                              \
        LVSF_FONT_REGISTER(freetype_font, 24),                              \
        LVSF_FONT_REGISTER(freetype_font, 25),                              \
        LVSF_FONT_REGISTER(freetype_font, 26),                              \
        LVSF_FONT_REGISTER(freetype_font, 27),                              \
        LVSF_FONT_REGISTER(freetype_font, 28),                              \
        LVSF_FONT_REGISTER(freetype_font, 29),                              \
        LVSF_FONT_REGISTER(freetype_font, 30),                              \
        LVSF_FONT_REGISTER(freetype_font, 31),                              \
        LVSF_FONT_REGISTER(freetype_font, 32),                              \
        LVSF_FONT_REGISTER(freetype_font, 33),                              \
        LVSF_FONT_REGISTER(freetype_font, 34),                              \
        LVSF_FONT_REGISTER(freetype_font, 35),                              \
        LVSF_FONT_REGISTER(freetype_font, 36),                              \
        LVSF_FONT_REGISTER(freetype_font, 37),                              \
        LVSF_FONT_REGISTER(freetype_font, 38),                              \
        LVSF_FONT_REGISTER(freetype_font, 39),                              \
        LVSF_FONT_REGISTER(freetype_font, 40),                              \
        LVSF_FONT_REGISTER(freetype_font, 41),                              \
        LVSF_FONT_REGISTER(freetype_font, 42),                              \
        LVSF_FONT_REGISTER(freetype_font, 43),                              \
        LVSF_FONT_REGISTER(freetype_font, 44),                              \
        LVSF_FONT_REGISTER(freetype_font, 45),                              \
        LVSF_FONT_REGISTER(freetype_font, 46),                              \
        LVSF_FONT_REGISTER(freetype_font, 47),                              \
        LVSF_FONT_REGISTER(freetype_font, 48),                              \
        LVSF_FONT_REGISTER(freetype_font, 49),                              \
        LVSF_FONT_REGISTER(freetype_font, 50),                              \
        LVSF_FONT_REGISTER(freetype_font, 51),                              \
        LVSF_FONT_REGISTER(freetype_font, 52),                              \
        LVSF_FONT_REGISTER(freetype_font, 53),                              \
        LVSF_FONT_REGISTER(freetype_font, 54),                              \
        LVSF_FONT_REGISTER(freetype_font, 55),                              \
        LVSF_FONT_REGISTER(freetype_font, 56),                              \
        LVSF_FONT_REGISTER(freetype_font, 57),                              \
        LVSF_FONT_REGISTER(freetype_font, 58),                              \
        LVSF_FONT_REGISTER(freetype_font, 59),                              \
        LVSF_FONT_REGISTER(freetype_font, 60),                              \
    };

#endif

6. Emoji 字体的增删

  1. 新增 Emoji:将新增的 Emoji 图片放在 solution/examples/xxx/resource/images_emoji/common/ezip 目录下。

    • 图片命名格式必须为 emoji_xxx.png

    • 其中 xxx 为 Emoji 的 Unicode 编码。

    • 例如,Unicode 为 1f46d 的 Emoji,应命名为 emoji_1f46d.png

  2. 删除 Emoji:删除对应图片即可。

7. 字体显示流程

为支持多语言,通常需要多个 .ttf 组合使用,才能覆盖全部语言字符。字体显示流程如下:

  1. 设置字体(.ttf)顺序,并注册字体链表 font_list

  2. 创建空的缓存链表 cache_list

  3. 按照文本中的每个 Unicode 逐个获取 bitmap。

    • 如果在 cache_list 中找到对应 Unicode 的 bitmap,则直接调用 lv_draw_letter 显示。

    • 如果未找到,则先存入 cache_list,再调用 lv_draw_letter 显示。

    • 如果存入时发现超出缓存容量,则会清除部分缓存后再继续写入。

  4. 显示速度取决于:

    • 该 Unicode 位于 font_list 中靠前还是靠后;

    • 该字形是否已经命中 cache_list

也就是说,如果 Unicode 对应字形位于前面的字体中,或者能够在缓存中直接命中,则显示速度更快;否则需要进入 FreeType 查找并渲染字形,速度会略慢。

8. 代码中使用 .ttf 字体

8.1 自动选择字体

接口:lv_ext_set_local_font(lv_obj_t *obj, uint16_t size, lv_color_t color)

lv_obj_t *calorie = lv_label_create(bg_img);
lv_ext_set_local_font(calorie, FONT_SUBTITLE, lv_color_make(255, 255, 255));
lv_label_set_text(calorie, app_get_str(key_calorie, "calorie"));

8.2 指定字体名称

接口:lv_ext_label_set_indicated_font(lv_obj_t *obj, uint16_t size, lv_color_t color, const char *font_name)

lv_obj_t *calorie_lab = lv_label_create(bg_img);
lv_ext_set_local_font(calorie_lab, FONT_SUBTITLE, lv_color_make(255, 255, 255));
lv_ext_label_set_indicated_font(calorie_lab, FONT_SUBTITLE, lv_color_make(255, 255, 255), "HarmonyOS_Sans_SC_Bold");

9. 代码中重新设置字体顺序

以下接口只能在 GUI 线程中使用,不能跨线程调用。

9.1 lvsf_font_set_order(char **font_name, uint16_t font_num)

  • 接口说明:调整字体顺序,将 font_name 指定的字体移动到最前面。

  • font_name:字符串数组,表示需要前置的字体。

  • font_num:需要调整顺序的字体个数。

示例:

假设当前字体列表为 tiny5_fullhindiarabHarmonyOS_Sans_SC_Bold,需要将 arabhindi 调整到最前面,适用于语言切换时优化字体访问顺序。

char *font_name[32] = {"arab", "hindi"};
uint16_t font_num = 2;
lvsf_font_set_order(font_name, font_num);

调整后的结果为:arabhinditiny5_fullHarmonyOS_Sans_SC_Bold

9.2 lvsf_font_set_order_reverse(char **font_name, uint16_t font_num)

  • 接口说明:调整字体顺序,将 font_name 指定的字体移动到最后面。

示例:

假设当前字体列表为 tiny5_fullhindiarabHarmonyOS_Sans_SC_Bold,需要将 arab 调整到最后面。

char *font_name[32] = {"arab"};
uint16_t font_num = 1;
lvsf_font_set_order_reverse(font_name, font_num);

调整后的结果为:tiny5_fullhindiHarmonyOS_Sans_SC_Boldarab

9.3 lvsf_font_reset_order(void)

  • 接口说明:将字体顺序恢复为开机时的默认顺序。

10. 重新设置某个字体支持的字号

以下接口只能在 GUI 线程中使用,不能跨线程调用。

10.1 lvsf_set_font_size_by_name(char *font_name, int *size)

  • 接口说明:调整指定字体支持的字号。

  • 注意:字号必须按从小到大的顺序排列。

示例:

假设字体当前支持的字号为 162024283656,分别对应 FONT_SMALLFONT_NORMALFONT_SUBTITLEFONT_TITLEFONT_BIGLFONT_HUGE

char *font_name = "arab";
int size[] = {20, 22, 0}; /* 以 0 结尾 */
lvsf_set_font_size_by_name(font_name, size);

调整后的结果为:202224283656。即该字体的 FONT_SMALL 变为 20FONT_NORMAL 变为 22

10.2 lvsf_reset_font_size_by_name(char *font_name)

  • 接口说明:将指定字体的字号恢复为开机时的默认值。

11. 设置支持 bitmap(点阵字体)

  1. Solution 通过如下 menuconfig 配置使能 bitmap。

  1. 以 watch 产品为例,点阵字体生成的 .c 文件通常放在如下目录(其他产品目录类似)。

  1. 代码中调用 bitmap 的示例:lv_ext_set_local_bitmap_font

lv_obj_t *keybord = lv_keyboard_create(parent);
lv_obj_set_width(keybord, LV_HOR_RES_MAX);
lv_keyboard_set_mode(keybord, LV_KEYBOARD_MODE_TEXT_LOWER);
lv_keyboard_set_textarea(keybord, ta);
lv_obj_align_to(keybord, ta, LV_ALIGN_OUT_BOTTOM_MID, 0, 0);
#ifdef USING_BITMAP_FONT
    lv_ext_set_local_bitmap_font(keybord, LV_COLOR_BLACK, lv_font_montserrat_16);
#else
    lv_ext_set_local_font(keybord, FONT_NORMAL, lv_color_make(0xFF, 0x00, 0x00));
#endif
lv_obj_refr_size(keybord);

lv_obj_add_event_cb(ta, setting_bt_name_ta_event_cb, LV_EVENT_ALL, keybord);

注意:

  1. 在 Solution 中通过上述配置即可使用点阵字体,无需在 SDK 中额外配置 bitmap。

  2. 使用 lv_ext_set_local_bitmap_font 调用点阵字体(例如示例中的 lv_font_montserrat_16)时,需要确保 resource/fonts/bitmap 中存在对应字体定义。

12. 引用外置 .ttf 字体

除内置字体外,Solution 还支持使用外置字体,例如:

  • 通过蓝牙 / Wi-Fi 推送到文件系统中的字体;

  • 直接访问 U 盘 / TF 卡中的字体。

相关接口如下:

  1. int lvsf_font_load_ex(char *font_path, uint16_t *size)
    从指定路径 font_path 加载字体。可加载该目录下全部字体,也可以通过完整文件路径(含 .ttf 后缀)加载单个字体。加载后字体会插入字体链表,但默认处于未启用状态。加载完成后,可通过 lvsf_font_trav_ex 查询已加载的外置字体。

  2. int lvsf_font_set_enable(char *font_name, int enable)
    启用或禁用指定名称 font_name 的字体。启用后会占用内存,尤其在 NAND / eMMC 方案中更明显,因此需控制加载数量,特别是大字体文件。

  3. void lvsf_font_unload_ex(char *font_path)
    从字体链表中卸载指定路径 font_path 下的全部字体,或通过完整文件路径卸载单个字体(含 .ttf 后缀)。

  4. char *lvsf_font_trav_ex(rt_list_t **list, int ex)
    遍历字体链表,获取所有已加载的外置字体信息。

13. UNKNOWN 字的显示

参照字体显示流程,当某个字符无法在当前 .ttf 中找到时,系统会显示 UNKNOWN 字。通常情况下,UNKNOWN 字显示为方块

在某些场景下,客户可能需要自定义 UNKNOWN 字的显示方式。例如:

  • 中文环境下显示为中文问号

  • 也可以显示为英文问号 ?

  • 或替换为其他字符

为此,Solution 提供了以下接口:lv_freetype_set_unknown_font_mode

/**
 * @brief Configure how missing glyphs are handled by the FreeType-backed font renderer.
 *
 * @param mode
 *        - 0: render a replacement glyph using @p unknown_font_unicode
 *        - 1: ignore missing glyphs (nothing will be drawn)
 * @param unknown_font_unicode Replacement Unicode code point used only when @p mode is 0.
 *
 * @note This API name contains a historical typo ("unkown"). Prefer
 *       `lv_freetype_set_unknown_font_mode()` for new code.
 */
void lv_freetype_set_unkown_font_mode(int mode, uint32_t unknown_font_unicode);

代码调用位置:sdk/middleware/lvgl/lvsf/lvsf_font.c 中的 ft_callback_reg

示例:

  1. 将 UNKNOWN 字显示为 (中文问号)

lv_freetype_set_unkown_font_mode(0, 0xFF1F); /* "?" U+FF1F (0xFF1F) */
  1. 将 UNKNOWN 字显示为 ?(英文问号)

lv_freetype_set_unkown_font_mode(0, 0x3F); /* '?' U+003F (0x3F) */

说明: 该功能仅在 Solution V2.5 及以上版本支持。如需在旧版本中支持,请联系 FAE 升级 PATCH。

14. 提升字体显示速度

使用 FreeType 进行字体显示时,通常需要先查找 glyph 再进行渲染。一个字符首次显示时,耗时通常为毫秒级。为提升显示速度,Solution 提供以下两种方法。

注意:

  1. Font_gen 工具不能从 tiny55_full.ttfhindi.ttf 中抽取静态字体。

  2. 如果需要使用 hindi.ttf(印地语),印地语显示仍需通过 hindi.ttf 实现,因此抽取出的字体风格(粗细)应与 hindi.ttf 保持一致。

14.1 使用 Font_genmulti_language_table.xlsx 抽取静态字体到一个 .ttf 文件

  • 工具位置:solution/tools/Font_gen/font_gen.exe

  • 使用示例:font_gen.exe -i multi_language_table.xlsx -i unicode 0x02-0xff HarmonyOS_Sans_SC_Bold.ttf arab.ttf

生成 static_font.ttf 后,可按以下方式使用:

  1. 放入 solution/examples/xxx/resource/builtin/freetype/ 目录

    • 编译时会将 static_font.ttf 转换为 .c 文件并编译进代码。

    • 这种方式会增加一些内存占用,但字体显示速度更快。

  2. 放入 solution/examples/xxx/resource/fonts/freetype 目录

    • 将其作为普通 .ttf 字体参与方案。

    • 这种方式不会额外占用代码空间,但显示速度相比 builtin 字体略慢。

注意: 如果方案中仍需使用 tiny55_full.ttf,则该方法通常不适用。原因包括:

  • 抽取字体的风格通常与 tiny55_full.ttf 不一致;

  • 当代码中的静态文本发生变化时,作为文件存在的 .ttf 也需要同步升级。

14.2 使用 Font_genmulti_language_table.xlsx 抽取静态字体到 bitmap 文件

相比 static_font.ttf,bitmap 文件能更有效地解决 FreeType 字体渲染耗时较长的问题。

  • 使用前提:multi_language_table.xlsx 中需准确填写 Bitmap_fontsize 列,明确每行文字对应的显示字号。

  • 工具位置:solution/tools/Font_gen/font_gen.exe

  • 使用示例:font_gen.exe -i multi_language_table.xlsx -i unicode 0x02-0xff HarmonyOS_Sans_SC_Bold.ttf arab.ttf -ttf2bitmap 2

  • solution/framework/__template__/project 下的 __applicaiton_private__ 复制到工程 project 目录下(与 hcpu 目录同级)。

  • 将生成的静态字体 C 文件(文件名通常形如 static_font_xx.c,由工具按字号生成)复制到 project/__applicaiton_private__/ 目录下。

  • 所有静态字体显示统一使用 lv_ext_set_local_bitmap_font 接口。

说明: 使用该方法时,方案仍可保留 tiny55_full.ttf 作为动态字库,用于消息、电子书、实时生成信息等场景,从而实现静态字库与 tiny55_full.ttf 的混合使用,并允许静态文本与动态文本采用不同字体风格。

说明: 该功能仅在 Solution V2.5 及以上版本支持。