LVGL V9 加载中文字体教程

前言

之前在使用 LVGL v9 进行项目开发过程中,发现默认情况下是不支持显示中文字体的,而我们常常需要在实际项目中显示中文字符,在阅读官方文档并进行一段时间的实践摸索后,最终解决了该问题。

本篇文章将先讲解 LVGL 默认内置的几种字体,然后讲解如何更改内置字体引擎的字体,最后再讲解如何使用 freetype 字体引擎。废话不多说,马上进入正题。

内置字体

LVLG v9版本默认内置了几种字体,打开项目根目录的 lv_conf.h 配置文件可以看到如下内容:

/* Montserrat字体,包含ASCII范围和一些符号,使用bpp = 4 */
#define LV_FONT_MONTSERRAT_8  1
#define LV_FONT_MONTSERRAT_10 1
#define LV_FONT_MONTSERRAT_12 1
#define LV_FONT_MONTSERRAT_14 1
#define LV_FONT_MONTSERRAT_16 1
#define LV_FONT_MONTSERRAT_18 1
#define LV_FONT_MONTSERRAT_20 1
#define LV_FONT_MONTSERRAT_22 1
#define LV_FONT_MONTSERRAT_24 1
#define LV_FONT_MONTSERRAT_26 1
#define LV_FONT_MONTSERRAT_28 1
#define LV_FONT_MONTSERRAT_30 1
#define LV_FONT_MONTSERRAT_32 1
#define LV_FONT_MONTSERRAT_34 1
#define LV_FONT_MONTSERRAT_36 1
#define LV_FONT_MONTSERRAT_38 1
#define LV_FONT_MONTSERRAT_40 1
#define LV_FONT_MONTSERRAT_42 1
#define LV_FONT_MONTSERRAT_44 1
#define LV_FONT_MONTSERRAT_46 1
#define LV_FONT_MONTSERRAT_48 1

/* 演示特殊功能 */
#define LV_FONT_MONTSERRAT_28_COMPRESSED 1  /*bpp = 3*/
#define LV_FONT_DEJAVU_16_PERSIAN_HEBREW 1  /*希伯来语、阿拉伯语、波斯语字母及其所有形式*/
#define LV_FONT_SIMSUN_16_CJK            1  /*1000个最常用的中日韩部首*/

/* 像素完美的等宽字体 */
#define LV_FONT_UNSCII_8  1
#define LV_FONT_UNSCII_16 1

/* 设置默认字体 */
#define LV_FONT_DEFAULT &lv_font_montserrat_14

从上面的内容,可以得出LVGL中有如下几种默认内置字体:

  • Montserrat字体:包含ASCII范围和一些符号,使用 bpp = 4,每像素用 4 比特表示,即每个像素可以有 16 种不同的灰度级别或颜色,包含从 8px 到 48px 各种大小,LVGL 默认使用的 Montserrat 14px 的字体,这也是为什么显示不了中文的原因,因为它包含的是ASCII 码范围的字体;
  • MONTSERRAT_28_COMPRESSED字体:也是 Montserrat 字体,只不过是使用 3 比特进行压缩,28px 的大小;
  • DEJAVU_16_PERSIAN_HEBREW:特殊的希伯来语、阿拉伯语、波斯语字母字体,16px 大小;
  • SIMSUN_16_CJK:Simsum字体,16px 大小,有 1000 个常用的中日韩字体,这个字体基本无法满足我们的要求,因为包含的中文字体太少了;

修改字体的两种方法

接下来讲述的是如何修改默认字体引擎的字体方法,这种方法的优点是修改步骤非常简单,缺点是字体只能支持固定的大小,如果设置了16px,那么这个字体就是 16px,如果想要 20px 的字体,就需要再添加 20px 的同类字体。这种方法适用于不需要频繁修改字体大小的场景。

有两种修改方式,一种是生成 C 文件的形式,另外一种是生成 bin 文件的形式。

生成 C 文件

生成 C 文件的形式,字体会链接到最终的可执行文件中,也就是会增加可执行文件的文件大小,适合不用频繁更改字体包的场景。

修改的方式如下:

  1. 使用字体转换器转换字体包成C文件,字体包支持 ttf 和 woff 两种格式;
  2. 将 C 文件放入到项目中;
  3. 声明字体;
  4. 使用字体;

字体转换器转换字体

字体转换器分为两种:

  1. 在线字体转换器:https://lvgl.io/tools/fontconverter
  2. 离线字体转换器(需要使用 Node.js)
在线字体转换器

这里两种都进行演示。

首先,我们需要找到一款合适的字体,这里推荐一个字体网站:https://www.fonts.net.cn

这里以青鸟字体为例,下载后得到了 QingNiaoHuaGuangJianMeiHei-2.ttf 文件,然后打开在线字体转换器网站。

image.png

上面的都比较好理解,但是 Range 字段可能很多人都不清楚是什么,这里简单科普下。

LVGL 默认使用UTF8变长字符编码,UTF8 现在是世界上最流行的字符编码格式,它支持的字符能容纳世界上所有的语言字符,这也是它流行的原因。这个 Range 字段,就是选择你想要的字符编码的范围,每个字符都有对应的一个编码。

我们要显示的是中英文,就要输入中英文范围的 unicode 区间,要如何得到 unicode 区间呢?那就是查阅 unicode 表,这里推荐一个不错的查阅 unicode 表网站:https://symbl.cc/en/unicode-table

从网站我们可以得到我们想要的区间是:0x20-0x7F,0x4E00-0x9FFF,0x20-0x7F表示 ASCII 码的区间,也就是英文字母,而 0x4E00-0x9FFF 是中文的区间:

image.png

有了区间,我们就可以生成我们的c文件了。

image.png

点击 Sumbit 按钮,会下载一个 .c 文件。

这里再补充一点小知识:如果我们在使用字体时加载了一个不是在该范围内的字符,屏幕就会为这个字符显示一个方框,这也是我们常说的乱码。

离线字体转换器

转换字体的另外一种方式是使用离线字体转换器,好处是没网了也可以进行转换,它使用的是 node.js 脚本,转换原理和在线版是一样的。首先确保你的电脑安装了 node.js,版本最好是 16 以上,然后在终端输入如下命令:

npm i lv_font_conv -g

下面是它的命令选项:

常用选项:

--bpp - 每像素位数(抗锯齿)。
--size - 输出字体大小(像素)。
-o, --output - 输出路径(文件或目录,取决于格式)。
--format - 输出格式。
--format dump - 导出字形图像和字体信息,便于调试。
--format bin - 以二进制形式导出字体(如规范中所述)。
--format lvgl - 以LVGL格式导出字体。
--force-fast-kern-format - 始终使用更快的字距存储格式,但会增加一些大小。如果出现大小差异,将显示出来。
--lcd - 生成3倍水平分辨率的位图,用于子像素平滑。
--lcd-v - 生成3倍垂直分辨率的位图,用于子像素平滑。
--use-color-info - 尝试使用字体中的字形颜色信息来创建灰度图标。由于灰度通过透明度模拟,结果仅在高对比度背景上效果较好。
--lv-include - 仅与--format lvgl一起使用,设置lvgl.h的备用路径。

每种字体:

--font - 字体文件路径(ttf/woff/woff2/otf)。可以多次使用以合并字体。
-r, --range - 单个字形或范围+可选映射,属于之前声明的--font。可以多次使用。例如:
-r 0x1F450 - 单个值,十进制或十六进制格式。
-r 0x1F450-0x1F470 - 范围。
-r '0x1F450=>0xF005' - 带映射的单个字形。
-r '0x1F450-0x1F470=>0xF005' - 带映射的范围。
-r 0x1F450 -r 0x1F451-0x1F470 - 两个范围。
-r 0x1F450,0x1F451-0x1F470 - 与上述相同,但用单个-r定义。
--symbols - 要复制的字符列表(替代-r中的数字格式)。
--symbols 0123456789., - 提取用于显示数字的字符。
--autohint-off - 不强制自动提示(默认启用“light”)。
--autohint-strong - 使用更强的自动提示(会破坏字距)。

附加调试选项:

--no-compress - 禁用内置的RLE压缩。
--no-prefilter - 禁用位图行滤镜(XOR),用于提高压缩率。
--no-kerning - 删除字距信息以减少大小(不推荐)。
--full-info - 不缩短'font_info.json'(包括像素数据)。

还是以那个青鸟字体为例,输入如下命令:

lv_font_conv --font QingNiaoHuaGuangJianMeiHei-2.ttf --size 16 --format lvgl --bpp 1 -o ./qingniao_16.c --range 0x20-0x7F,0x4E00-0x9FFF

这条命令会在本地当前目录下生成一个 qingniao_16.c 的c文件。这里有个小建议,字体的命名尽量以字体名_像素大小的方式命名,和官方的命名规范一样,同时也能更好的辨别字体的像素大小。

添加和使用字体

接下来的步骤就比较简单了,将得到的 qingniao_16.c 文件,放入到你的项目下的 lvgl/src/font 的目录下,当然你也可以在根目录建一个 font 目录,再放进去,但这需要修改 CMake 的构建配置文件,如果不清楚 LVGL 的项目构建配置原理,可以参考我上一篇文章:LVGL V9 CMake构建源码刨析

放入之后,我们打开这个 c 文件,拉到最下面,可以看到如下的代码:

/*Initialize a public general font descriptor*/
#if LVGL_VERSION_MAJOR >= 8
const lv_font_t qingniao_16 = {
#else
lv_font_t qingniao_16 = {
#endif
    .get_glyph_dsc = lv_font_get_glyph_dsc_fmt_txt,    /*Function pointer to get glyph's data*/
    .get_glyph_bitmap = lv_font_get_bitmap_fmt_txt,    /*Function pointer to get glyph's bitmap*/
    .line_height = 18,          /*The maximum line height required by the font*/
    .base_line = 4,             /*Baseline measured from the bottom of the line*/
#if !(LVGL_VERSION_MAJOR == 6 && LVGL_VERSION_MINOR == 0)
    .subpx = LV_FONT_SUBPX_NONE,
#endif
#if LV_VERSION_CHECK(7, 4, 0) || LVGL_VERSION_MAJOR >= 8
    .underline_position = -2,
    .underline_thickness = 1,
#endif
    .dsc = &font_dsc,          /*The custom font data. Will be accessed by `get_glyph_bitmap/dsc` */
#if LV_VERSION_CHECK(8, 2, 0) || LVGL_VERSION_MAJOR >= 9
    .fallback = NULL,
#endif
    .user_data = NULL,
};

qingniao_16 就是我们定义的那个字体名,.fallback = NULL表示没有指定兜底字体,如果指定了兜底字体,当查找不到字体字符时,LVGL会去兜底字体中查找,如果还查找不到,就会显示一个方框。这里我们可以手动指定兜底字体,也可以在在使用字体转换器时指定,这里我指定为 &lv_font_montserrat_14,表示如果在 qingniao_16 字体中查找不到字符,就去 lv_font_montserrat_14 字体中查找。

接着打开根目录的 lv_conf.h 文件,找到 LV_FONT_CUSTOM_DECLARE 这个宏,进行如下修改:

#define LV_FONT_CUSTOM_DECLARE LV_FONT_DECLARE(qingniao_16)

这表示声明了我们的的自定义字体,名称是 qingniao_16 ,这个名称要和我们生成的 .c 文件中 lv_font_t 类型的变量名匹配。

接下来有两种使用方式,一种是指定默认字体,另一种是在创建 widget 时指定字体,这里我演示下指定默认字体。还是打开 lv_conf.h 文件,找到 LV_FONT_DEFAULT 宏,进行如下修改:

#define LV_FONT_DEFAULT &qingniao_16

接下来就可以正常使用中文字体了。尝试创建一个 label,设置中文文本,将看到中文字体可以正常地显示!

生成 bin 文件

要使用 bin 文件,需要开启 LVGL 的文件系统(文件系统默认是开启的)。使用 bin 文件的好处是我们可以随时修改字体,不需要重新编译可执行文件,可以把 bin 文件理解成动态链接库,而 c 文件是静态库。

bin 文件的生成和 c 文件的生成没什么区别,只有最后在指定输出格式上不同。

假设我们生成了 qingniao_16.bin 文件,将其放到文件系统的根目录(假设盘符是A),在代码中动态加载 bin 文件并使用,代码框架如下:

lv_font_t *my_font = lv_binfont_create("A:qingniao_16.bin");
if(my_font == NULL) return;
/*使用字体*/
/*如果不再需要字体,释放字体*/
lv_binfont_destroy(my_font);

后面如果想要更改字体,直接更改 bin 文件即可。

使用 freetype 字体引擎

经过前面的介绍,大家应该能发现一个问题,那就是如果我要动态指定字体大小,非常麻烦,因为需要为字体的每个大小生成一个 c 或 bin 文件。那有没有更好的方式呢?

有的,那就是更换字体引擎,这里以 freetype 为例,讲解如何在 LVGL v9 中使用 freetype,LVGL v9 对 freetype 做了一层封装,可以让我们专注于使用字体,而不是编写字体引擎再使用字体,大大降低了使用门槛。

要使用 freetype,需要确保你安装了 freetype 库,freetype 的交叉编译和安装都比较简单,这里不过多赘述。安装好之后,打开 lv_conf.h,找到 LV_USE_FREETYPE 宏,设置为 1 即可开启。下面是 freetype 的一些更详细的配置:

#if LV_USE_FREETYPE
    /*FreeType使用的内存以KB为单位缓存字符*/
    #define LV_FREETYPE_CACHE_SIZE 768

    /*让FreeType使用LVGL的内存和文件端口*/
    #define LV_FREETYPE_USE_LVGL_PORT 0

    /* 此缓存实例管理的最大打开FT_Face/FT_Size对象数量。*/
    /* (0:使用系统默认值) */
    #define LV_FREETYPE_CACHE_FT_FACES 8
    #define LV_FREETYPE_CACHE_FT_SIZES 8
    #define LV_FREETYPE_CACHE_FT_GLYPH_CNT 256
#endif

开启之后,还需要添加链接库,打开根目录的 CMakeLists.txt,为 main 目标添加链接库,修改成如下:

target_link_libraries(main lvgl lvgl::examples lvgl::demos lvgl::thorvg m pthread freetype)

之后就可以愉快的使用 freetype了。下面是一个以官方demo的案例展示如何使用 freetype:

void lv_example_freetype_1(void)
{
    /*Create a font*/
    lv_font_t * font = lv_freetype_font_create(PATH_PREFIX "lvgl/examples/libs/freetype/Lato-Regular.ttf",
                                               LV_FREETYPE_FONT_RENDER_MODE_BITMAP,
                                               24,
                                               LV_FREETYPE_FONT_STYLE_NORMAL);

    if(!font) {
        LV_LOG_ERROR("freetype font create failed.");
        return;
    }

    /*Create style with the new font*/
    static lv_style_t style;
    lv_style_init(&style);
    lv_style_set_text_font(&style, font);
    lv_style_set_text_align(&style, LV_TEXT_ALIGN_CENTER);

    /*Create a label with the new style*/
    lv_obj_t * label = lv_label_create(lv_screen_active());
    lv_obj_add_style(label, &style, 0);
    lv_label_set_text(label, "Hello world\nI'm a font created with FreeType");
    lv_obj_center(label);
}

可以看到,freetype支持直接加载ttf文件,并且可以指定字体大小,非常的方便。

结尾

本篇文章介绍了如何在LVGL v9中使用中文字体,但其实它也适合与其他语言字体,方式原理都是差不多的。希望本篇文章能对各位读者有所帮助~

如果觉得本文写得还不错,请多多点赞、收藏与转发,谢谢~

  • 14
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值