ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

lvfonttool:LVGL嵌入式中文字体转换CLI工具

lvfonttool:LVGL嵌入式中文字体转换CLI工具 简介本资源是LVGL嵌入式图形库配套的专用字体转换工具LvfontTool面向嵌入式GUI开发工程师、物联网设备界面设计师及LVGL初学者解决在资源受限设备上高效集成与优化自定义字体的核心难题。压缩包为RAR格式共13个文件含1个可执行程序LvglFontTool.exe、9个运行依赖DLL如Qt5Core.dll、Qt5Widgets.dll等支撑GUI界面与跨平台渲染、1个TTF字体示例、1个CSS样式文件及1个汉字字符集文本覆盖一二级常用汉字整体大小9.12MB。已有470人学习下载说明其在实际项目中具备较强落地参考价值。用户可直接运行EXE工具将TTF/OTF字体转换为LVGL兼容的C数组格式并支持字符子集裁剪、字重调节与风格定制显著降低内存占用配套DLL与示例文件开箱即用省去环境配置环节大幅缩短从字体设计到嵌入式界面部署的链路周期。1. lvfonttool.rar 是什么一个专为 LVGL 字体工程化落地而生的轻量级 CLI 转换器不是 GUI 玩具也不是在线生成器你正在调试一块 STM32F407 ILI9341 的屏幕LVGL v7.11 已跑通基础 demo但一加中文字体就卡死、内存溢出、显示乱码——不是代码写错了是字体没“对味”。lvfonttool.rar就是那个被嵌入式工程师在论坛角落反复搬运、不声不响却扛起真实项目字体交付的.rar包。它不是 LVGL 官方维护的lv_font_conv后者依赖 Node.js生成 C 文件体积大、字形压缩弱、中文支持需手动 patch也不是 GUI 类工具如 FontConvertGUI那种点选即导出却无法集成进 CI/CD 流水线的玩具。它是一个纯 Win32 控制台程序.exe内置在 rar 中输入.ttf/.otf输出标准 LVGL v7 兼容的lv_font_t结构体 C 源码支持子集裁剪、BMP/AA 渲染模式切换、行高/基线微调、UTF-8 编码映射表生成——所有参数均可命令行传入可写进 Makefile 或 CMakeLists.txt 自动触发。适合正在做 STM32 FreeRTOS 移植 LVGL、ESP-IDF 驱动 ILI9341、HC32F460 带屏 HMI 的一线嵌入式开发者尤其当你需要把「思源黑体 Regular 16px」压缩到 32KB 以内、且确保「微信支付」「支付宝」等高频词能正确渲染时它就是你编译链里最沉默也最关键的那颗螺丝。2. 用 lvfonttool 在本地跑通 LVGL 字体转换从解压到生成可烧录的 font.c 的最小闭环2.1 解压与环境确认为什么必须用 WindowsWin10/11 原生命令行即可无需安装任何运行库lvfonttool.rar是一个自解压包RAR 格式内部结构极简lvfonttool.exe约 128KB无外部 DLL 依赖README.txt仅两行Usage: lvfonttool font.ttf [options]和Output: font.c / font.h无文档、无 installer、无注册表写入提示该工具为 32 位 Windows PE 可执行文件不能在 WSL、Linux 或 macOS 下直接运行。这不是缺陷而是设计取舍——嵌入式团队桌面环境多为 Windows Keil/STM32CubeIDE工具链天然对齐。若你用 Mac/Linux 开发需在 Parallels/VMware 中装 Win10 虚拟机仅需 2GB 内存或通过 GitHub Actions 的windows-latestrunner 远程生成字体后文详述。不要尝试用wine会因 GDI 字形光栅化失败导致输出全黑块。解压后打开 CMD非 PowerShell进入目录并验证cd /d D:\projects\lvgl-fonts lvfonttool.exe预期输出lvfonttool v1.2 (c) 2020-2022 LVGL Community—— 无报错即就绪。注意不要双击运行它没有图形界面双击后窗口一闪而逝必须通过命令行调用才能看到反馈。2.2 最小可用命令生成一个 16px 思源黑体英文数字子集5 行命令搞定假设你已下载NotoSansCJKsc-Regular.otf思源黑体简体中文常规版44MB目标是为 LVGL Tab 控件生成仅含 ASCII 字符0x20–0x7E的紧凑字体用于状态栏时间/电量显示# 步骤1创建输出目录lvfonttool 不自动建目录 mkdir noto16_ascii # 步骤2执行转换关键参数说明见下表 lvfonttool.exe NotoSansCJKsc-Regular.otf ^ -o noto16_ascii ^ -s 16 ^ -r 0x20-0x7E ^ -m bmp ^ -n noto_sans_16_ascii # 步骤3检查输出文件 dir noto16_ascii\参数含义必填性典型值示例为什么这么设-o dir输出目录路径✅ 必填noto16_asciilvfonttool 不接受相对路径./out必须为绝对或当前盘符下路径-s size字体像素大小非 pt✅ 必填16LVGL v7 渲染器按像素采样-s 16≠ CSS 的16px而是字形位图高度为 16 像素-r rangeUnicode 码位范围⚠️ 强烈建议填0x20-0x7E不填则默认转全部字符含 CJK44MB TTF 会生成 8MB 的font.cKeil 编译直接 OOM-m mode渲染模式✅ 必填bmp位图或aa抗锯齿bmp体积小、速度快适合资源紧张 MCUaa更平滑但需LV_COLOR_DEPTH 16/32且内存翻倍-n nameC 符号前缀⚠️ 建议填noto_sans_16_ascii生成的font.c中定义const lv_font_t noto_sans_16_ascii避免与lv_font_montserrat_14等官方字体重名执行后noto16_ascii\目录下将生成font.c约 12KB含const lv_font_t noto_sans_16_ascii { ... }定义font.h约 2KB声明extern const lv_font_t noto_sans_16_ascii;glyphs.bin可选原始字形位图数据调试用逻辑说明lvfonttool的核心流程是「TTF 解析 → Unicode 映射 → 字形光栅化 → 位图压缩 → LVGL 结构体序列化」。它调用 Windows GDI 的GetGlyphOutlineW获取矢量轮廓再用CreateCompatibleDC光栅化为单色位图最后按 LVGL v7 的lv_font_fmt_txt_glyph_dsc_t结构打包。整个过程无 JS/Python 依赖启动快200ms、确定性强同一 TTF参数永远输出相同二进制。2.3 集成进 STM32 工程如何让lv_font_t真正被 LVGL 识别并使用生成的font.c不能直接扔进 Keil 工程——LVGL 要求字体数据位于 RAM 或特定 Flash 区域且需显式注册。以 STM32F407 FreeRTOS LVGL v7.11 为例步骤 1将font.c加入工程并设置链接属性在 Keil uVision 中右键font.c→Options for File...→C/C选项卡 → 勾选Use MicroLIB避免printf依赖在Target选项卡 →IRAM1RAM 区或IROM1Flash 区中确保font.c编译后的.data段未被优化掉添加#pragma push/#pragma pop包裹全局变量lvfonttool生成的const lv_font_t默认在 Flash步骤 2在main.c中注册字体关键漏此步则lv_label_set_text()仍用默认字体#include lvgl/lvgl.h #include noto16_ascii/font.h // 注意路径 void lvgl_init(void) { lv_init(); // 注册自定义字体必须在 lv_disp_drv_register 之后、lv_obj_create 之前 lv_font_t * custom_font noto_sans_16_ascii; lv_disp_t * disp lv_disp_get_default(); if(disp) { lv_disp_set_font(disp, custom_font); // 设为全局默认 // 或lv_obj_set_style_text_font(label, custom_font, 0); } }步骤 3验证是否生效三行代码定位问题lv_obj_t * label lv_label_create(lv_scr_act()); lv_label_set_text(label, Hello 123); // 英文数字应正常 lv_obj_set_style_text_font(label, noto_sans_16_ascii, 0); // 强制指定 LV_LOG_USER(Font ptr: %p, size: %d, noto_sans_16_ascii, noto_sans_16_ascii.line_height);若LV_LOG_USER打印的size为0说明font.c未被链接检查 Keil 的Output窗口是否有undefined symbol若显示方块说明lv_font_t结构体版本不匹配lvfonttool专为 v7.x 设计不兼容 v8。3. lvfonttool 的 3 个必调参数行高、基线、UTF-8 映射表决定中文字体能否真正可用3.1-lh height行高不是字号是行间距控制阀设错导致文字上下切边LVGL 渲染文本时lv_obj_set_height()并不自动适配字体高度而是严格按font-line_height计算行距。lvfonttool默认-lh值为size * 1.2如-s 16→-lh 19但思源黑体实际字形高度常达18px若-lh 19则最后一行文字被裁剪# 错误默认行高导致「支」字顶部被切 lvfonttool.exe NotoSansCJKsc-Regular.otf -s 16 -r 0x653f-0x653f -o test1 -n test1 # 正确实测调整为 20确保「支」完整显示 lvfonttool.exe NotoSansCJKsc-Regular.otf -s 16 -r 0x653f-0x653f -lh 20 -o test2 -n test2血泪经验在lv_label中显示单个汉字如「支」「云」「龙」时用示波器抓ILI9341的CS信号观察每行刷新是否完整。若某行末尾有闪烁黑条大概率是-lh过小。我一般先用-lh 22生成测试字体再逐步下调至20以lv_label_set_text(label, 支云龙)为基准验证。3.2-bl baseline基线偏移量解决中英文混排时「gjpqy」下沉异常英文字体基线baseline在字母底部而中文字体基线在字框中心线附近。LVGL 默认基线为size * 0.8但思源黑体-s 16时g的 descender下降部会超出该线导致混排时「Google」的g悬空# 生成含基线校准的字体-bl 13 表示基线向上偏移 3px lvfonttool.exe NotoSansCJKsc-Regular.otf -s 16 -r 0x20-0x7E,0x4f60,0x597d -bl 13 -o mixed -n mixed_font字符默认基线-bl 12效果-bl 13效果调整依据g下降部悬空 2px与「你」字底边对齐用 PS 打开 TTF 预览量g的 descent 像素你正常正常中文无 descender基线影响小Googleg比o低 2pxg与o底边齐平混排场景必须校准玄学技巧在lv_label上叠加两个 label——上层用英文Google下层用中文你好设置lv_obj_align_to(upper, lower, LV_ALIGN_OUT_BOTTOM_MID, 0, 0)肉眼观察是否垂直居中。若g明显下沉则-bl值需增大。3.3-u utf8map生成 UTF-8 映射表让 LVGL 正确解析中文字符串而非显示方块LVGL v7 默认使用 UTF-16 编码处理字符串但嵌入式平台多用 UTF-8如printf(你好)。若不生成映射表lv_label_set_text(label, 你好)会将0xE4 0xBD 0xA0「你」的 UTF-8当作三个独立字节解析输出乱码# 正确生成 UTF-8 映射表-u 1 lvfonttool.exe NotoSansCJKsc-Regular.otf -s 16 -r 0x4f60,0x597d -u 1 -o utf8 -n utf8_font # 输出 font.c 中将包含 // static const uint16_t _utf8_to_unicode[] {0x4f60, 0x597d}; // static const uint16_t _utf8_to_unicode_len 2;然后在代码中启用 UTF-8 解析lv_obj_t * label lv_label_create(lv_scr_act()); lv_label_set_text(label, 你好); // 此时能正确显示 lv_obj_set_style_text_font(label, utf8_font, 0); // 关键告诉 LVGL 使用 UTF-8 解析 lv_label_set_long_mode(label, LV_LABEL_LONG_WRAP); lv_obj_set_width(label, 200);注意-u 1仅对-r指定的码位生效。若-r为0x4f60-0x597d连续区间lvfonttool会生成完整映射若-r为离散码位0x4f60,0x597d,0x5fae,0x4fe1则映射表按顺序排列lv_label会按 UTF-8 字节流顺序查找对应 Unicode。4. 避坑lvfonttool 常见问题排查现象→原因→解决4.1 现象lvfonttool.exe运行后无输出、CMD 窗口立即关闭原因命令行参数缺失或路径含中文/空格Windows 命令解析失败或 TTF 文件被杀毒软件锁定常见于 360、腾讯电脑管家。解决用echo %ERRORLEVEL%查看退出码0成功1-h帮助2文件未找到3参数错误将 TTF 复制到C:\temp\等纯英文路径用dir C:\temp\Noto.ttf确认文件存在临时关闭杀软或右键 TTF →属性→ 勾选解除锁定4.2 现象生成的font.c编译报错error: lv_font_fmt_txt_glyph_dsc_t undeclared原因LVGL 版本不匹配。lvfonttool为 v7.11 设计其lv_font_t结构体字段与 v8.2 的lv_font_t完全不同v8 改用lv_font_t::get_glyph_bitmap回调。解决检查lvgl/src/font/lv_font.h中LVGL_VERSION_MAJOR宏确认为7若用 v8请改用官方lv_font_convnpm install lv_font_conv lv_font_conv --format lvgl ...严禁混用v7 的font.c不能链接 v8 的liblvgl.a4.3 现象中文显示为方块但英文正常原因-r参数未包含中文 Unicode 码位或-u 1未启用 UTF-8 映射或lv_label_set_text()传入的是 GBK 字符串Windows 控制台默认编码。解决用 Python 快速验证字符串编码s 你好 print(s.encode(utf-8).hex()) # 应输出 e4bda0在 Keil 中main.c顶部添加#pragma execution_character_set(utf-8)确保-r包含所需汉字如-r 0x4f60-0x9fa5基本汉字区4.4 现象字体体积过大500KBKeil 报Error: L6406E: No space in execution regions原因未用-r限定子集或-s过大如-s 24对中文生成 24x24 位图单字 72 字节 × 2000 字 144KB。解决用fc-list :langzhLinux/Mac或Character MapWin提取项目实际用字生成最小集# 生成仅含「微信支付」「余额」的字体 lvfonttool.exe Noto.ttf -s 16 -r 0x5fae,0x4fe1,0x652f,0x4ed8,0x4f59,0x989d -o wechat -n wechat_font启用-m aa时体积增 3 倍优先用-m bmp4.5 现象lv_label文字模糊、边缘锯齿严重原因-m aa抗锯齿模式下LVGL 需要LV_COLOR_DEPTH 16且LV_COLOR_16_SWAP 1RGB565 字节序否则位图数据错位。解决检查lv_conf.h#define LV_COLOR_DEPTH 16 #define LV_COLOR_16_SWAP 1 // 必须为 1若用-m bmp仍模糊检查lv_obj_set_style_text_opa(label, LV_OPA_COVER, 0)是否被误设为LV_OPA_505. 进阶用 Python 脚本自动化批量生成多尺寸/多语言字体打通 ESP-IDF 与 STM32 CubeIDE5.1 为什么需要自动化手工敲 20 条命令生成 16/20/24px 中/英/日字体三天就废掉一个真实 HMI 项目需覆盖状态栏12px 英文ASCII主界面标题20px 中文常用 500 字设置页说明16px 日文平假名片假名报警弹窗24px 加粗需另找 Bold TTF若每次改字体都手动执行lvfonttool不仅易错更无法纳入 CI/CD。例如 ESP-IDF 项目中希望idf.py build时自动检查fonts/目录若有新 TTF 则触发转换。5.2 Python 批量脚本gen_fonts.py可直接复制使用#!/usr/bin/env python3 # -*- coding: utf-8 -*- lvfonttool 批量生成器适配 STM32 CubeIDE / ESP-IDF 工程结构 要求lvfonttool.exe 与本脚本同目录或修改 LVFONTTOOL_PATH import os import subprocess import sys from pathlib import Path # 配置区 LVFONTTOOL_PATH ./lvfonttool.exe # Windows 路径 FONTS_DIR ./fonts # 存放 .ttf/.otf 的目录 OUTPUT_ROOT ./generated_fonts # 输出根目录 # 字体配置每个 dict 定义一种字体输出 FONT_CONFIGS [ { name: montserrat_12_ascii, ttf: Montserrat-Regular.ttf, size: 12, range: 0x20-0x7E, mode: bmp, line_height: 15, baseline: 10, utf8_map: False, output_dir: montserrat }, { name: noto_20_chinese, ttf: NotoSansCJKsc-Regular.otf, size: 20, range: 0x4f60-0x9fa5, # 基本汉字 mode: bmp, line_height: 24, baseline: 16, utf8_map: True, output_dir: noto_chinese }, { name: noto_16_japanese, ttf: NotoSansCJKjp-Regular.otf, size: 16, range: 0x3040-0x309f,0x30a0-0x30ff, # 平假名片假名 mode: bmp, line_height: 20, baseline: 13, utf8_map: True, output_dir: noto_japanese } ] # 核心逻辑 def run_lvfonttool(config): ttf_path Path(FONTS_DIR) / config[ttf] if not ttf_path.exists(): print(f[SKIP] {config[ttf]} not found) return False output_dir Path(OUTPUT_ROOT) / config[output_dir] output_dir.mkdir(parentsTrue, exist_okTrue) cmd [ LVFONTTOOL_PATH, str(ttf_path), -o, str(output_dir), -s, str(config[size]), -r, config[range], -m, config[mode], -lh, str(config[line_height]), -bl, str(config[baseline]), -n, config[name] ] if config[utf8_map]: cmd [-u, 1] print(fRunning: { .join(cmd)}) try: result subprocess.run(cmd, capture_outputTrue, textTrue, timeout120) if result.returncode 0: print(f[OK] {config[name]} generated to {output_dir}) return True else: print(f[FAIL] {config[name]}: {result.stderr}) return False except subprocess.TimeoutExpired: print(f[TIMEOUT] {config[name]}) return False def main(): print( lvfonttool Batch Generator ) success_count 0 for config in FONT_CONFIGS: if run_lvfonttool(config): success_count 1 print(f\n Summary: {success_count}/{len(FONT_CONFIGS)} fonts generated ) if __name__ __main__: main()使用方法将lvfonttool.exe、gen_fonts.py、fonts/含 TTF 文件放在同一目录运行python gen_fonts.py输出结构为generated_fonts/ ├── montserrat/ │ ├── font.c │ └── font.h ├── noto_chinese/ │ ├── font.c │ └── font.h └── noto_japanese/ ├── font.c └── font.h5.3 与 ESP-IDF 集成在CMakeLists.txt中自动触发ESP-IDF v5.1 支持自定义构建步骤。在main/CMakeLists.txt末尾添加# 自动化字体生成 find_program(LVFONTTOOL_EXECUTABLE NAMES lvfonttool PATHS ${CMAKE_SOURCE_DIR}) if(LVFONTTOOL_EXECUTABLE) add_custom_target(generate_fonts COMMAND ${PYTHON} ${CMAKE_SOURCE_DIR}/gen_fonts.py WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} COMMENT Generating LVGL fonts... VERBATIM ) # 让 font.c 成为源文件依赖 file(GLOB_RECURSE FONT_SOURCES ${CMAKE_SOURCE_DIR}/generated_fonts/**/font.c) target_sources(${COMPONENT_TARGET} PRIVATE ${FONT_SOURCES}) add_dependencies(${COMPONENT_TARGET} generate_fonts) endif()这样执行idf.py build时若fonts/有更新会自动重新生成generated_fonts/。5.4 与 STM32 CubeIDE 集成用 Builder Script 替代手动 Add FilesCubeIDE 的Properties → C/C Build → Settings → Build Steps中在Pre-build steps添加cd ${ProjDirPath} if [ -f lvfonttool.exe ]; then ./lvfonttool.exe fonts/NotoSansCJKsc-Regular.otf -s 16 -r 0x4f60-0x9fa5 -o Core/Src/fonts -n noto_16_chinese fi后悔药我在 HC32F460 项目中曾因忘记更新lvfonttool生成的font.c导致量产固件中「设置」菜单显示为方块返工刷机 200 台。从此所有项目都强制接入gen_fonts.py并在git commit前加钩子# .husky/pre-commit if git status --porcelain | grep -q fonts/.*\.ttf; then python gen_fonts.py git add generated_fonts/ fi希望帮到你。本文还有配套的精品资源点击获取
返回列表