跳转至

27 hzfont-合宙矢量字库

作者:江访 | 最后修改:2026-06-11

一、概述

hzfont 是 LuatOS 专为嵌入式 UI 开发设计的矢量字库,能够高效、清晰地支持界面中的各类字符显示。它不仅内置便捷,还可灵活加载外部字体,显著降低开发成本与复杂度,并可搭配 lcd、AirUI、exeasyui 等核心库,大幅拓展 UI 表现力。

核心优势:

  • 全字号无级缩放:完整支持 14-255 字号,可随意指定任意大小,满足精细化的界面排版需求。
  • 智能抗锯齿优化:支持可调节的抗锯齿等级,有效平滑字体边缘,提升显示细腻度与视觉效果。
  • 字体使用高度自由:既可使用固件内置字库快速上手,也能轻松加载外部 .ttf 字体文件,便于对定制字体与多国语言的支持。

注意事项

  1. 当前 Air8000/Air780EXX 系列仅 V2020 及以上版本的 14 号和 114 号 LuatOS 固件版本支持 hzfont 且内置了.ttf 字体文件;

  2. Air8000 系列的模组可以参考:https://docs.openluat.com/air8000/luatos/firmware/

  3. Air7XX 系列模组可以参考:https://docs.openluat.com/air780epm/luatos/firmware/780ehm_version/
  4. Air8101 仅 V2004 及以上版本的 102 号和 104 号 LuatOS 固件版本支持 hzfont,固件内没有内置.ttf 字体文件,需要在下载固件时手动将.ttf 文件字体文件在烧录固件和脚本时一并烧录到模组的文件系统中

  5. Air8101 可以参考:https://docs.openluat.com/air8101/luatos/firmware/

  6. 各核心库使用 hzfont 的初始化差异

  7. lcd 核心库 — 需要先调用 hzfont.init() 初始化 hzfont 引擎,再使用 lcd.drawHzfontUtf8() 渲染
  8. airui 核心库 — 使用 airui.font_load({type="hzfont"})不需要调用 hzfont.init()
  9. eink 核心库 — 直接调用 eink.drawHzfont()不需要先执行 hzfont.init();仅支持固件内置 ttf
  10. u8g2 核心库 — 调用 u8g2.SetHzFont() 配置,之后使用 u8g2.DrawUTF8() 渲染,不需要先执行 hzfont.init()

二、核心示例

1、核心示例是指:使用本库文件提供的核心 API,开发的基础业务逻辑的演示代码;

2、核心示例的作用是:帮助开发者快速理解如何使用本库,所以核心示例的逻辑都比较简单;

3、更加完整和详细的 demo,请参考 LuatOS 仓库 中各个产品目录下的 demo/ui/hzfont

2.1 核心代码

-- hzfont搭配lcd核心库使用的演示代码

-- 加载exlcd扩展库
local exlcd = require("exlcd")

local function hzfont_test()
    -- 初始化LCD
    local result = exlcd.init({ lcd_model = "AirLCD_1000", direction = 3, w = 480, h = 320 })

    log.info("lcd.init", result)

    if result then
        -- 显示设置
        lcd.setupBuff(nil, true)
        lcd.autoFlush(false)

        -- 设置颜色,黑底白字,背景色:黑色(0x0000), 前景色:白色(0xFFFF)
        lcd.setColor(0x0000, 0xFFFF)

        -- 初始化方式三选一
        -- 1.使用14号固件内置hzfont字体初始化
        hzfont.init()

--[[         
        -- 2.使用文件系统中的字体初始化
        -- hzfont.init("/luadb/icomoon.ttf") 
]]

--[[         
        -- 3.使用SD卡中的字体初始化
        -- 初始化SPI1和TF卡
        -- 在Air780EHM/EHV/EGH核心板上TF卡的的pin_cs为gpio8,spi_id为0.请根据实际硬件修改
        local spi_id, pin_cs = 0, 8
        --初始化后拉高pin_cs,准备开始挂载TF卡
        gpio.setup(pin_cs, 1)

        -- 挂载TF卡;
        local max_retries = 3
        local retry_count = 0
        local result, err
        local mount_success = false -- TF卡挂载状态

        -- 以SPI对象的方式初始化SPI接口
        spi_sd_device = spi.deviceSetup(spi_id, pin_cs, 0, 0, 8, 24000000)

        -- 尝试多次挂载文件系统
        while retry_count < max_retries and not mount_success do
            -- 尝试挂载
            result, err = fatfs.mount(fatfs.SPI, "/sd", spi_sd_device)
            if result then
                mount_success = true
                log.info("TF Browser", "挂载成功! (尝试次数: " .. (retry_count + 1) .. ")")
            else
                log.warn("TF Browser", "挂载失败 (尝试 " .. (retry_count + 1) .. "):", err)
                retry_count = retry_count + 1
                sys.wait(500) -- 等待500ms后重试

                -- 尝试重置TF卡
                gpio.setup(pin_cs, 0)
                sys.wait(1000)
                gpio.setup(pin_cs, 1)
                sys.wait(1000)
            end
        end

        -- 获取SD卡的可用空间信息并打印。
        local data, err = fatfs.getfree("/sd")
        if not data then return end

        --打印SD卡的可用空间信息
        log.info("fatfs", "getfree", json.encode(data))

        if not io.exists("/sd/MiSans-Light.ttf") then return end

        -- 从SD卡加载,使用默认缓存 256
        hzfont.init("/sd/MiSans-Light.ttf")
 ]]
        -- hzfont调试信息开关
        -- hzfont.debug(true)

        -- 主显示循环
        while true do
            -- 清屏(黑色背景)
            lcd.clear(0x0000)

            -- 接口格式lcd.drawHzfontUtf8(x, y, str, fontSize, [color], [antialias])
            lcd.drawHzfontUtf8(10, 10, "合宙LuatOS字体演示", 10, 0xF800, 1)
            lcd.drawHzfontUtf8(10, 30, "合宙LuatOS字体演示", 20, 0x07E0, 1)
            lcd.drawHzfontUtf8(10, 70, "合宙LuatOS字体演示", 40, 0x001F, 1)
            lcd.drawHzfontUtf8(10, 130, "合宙LuatOS字体演示", 60, 0xFD20, 1)
            lcd.drawHzfontUtf8(10, 210, "合宙LuatOS字体演示", 80, 0x9E66, 1)
            lcd.drawHzfontUtf8(10, 310, "合宙LuatOS字体演示", 100, 0xFFFF, 1)

            -- 刷新显示
            lcd.flush()

            -- 延时2秒
            sys.wait(2000)
        end
    else
        log.error("LCD初始化失败")
    end
end

-- 启动显示协程
sys.taskInit(hzfont_test)

2.2 内置 hzfont 显示效果

三、常量详解

核心库常量,顾名思义是由合宙 LuatOS 内核固件中定义的、不可重新赋值或修改的固定值,在脚本代码中不需要声明,可直接调用,包含以下常量;

3.1 HZFONT_CACHE_128

参数含义:缓存最近显示文字数据的内存空间可容纳文字的个数最大值为128个;
数据类型:number
是否必选:可选;
注意事项:用于hzfont.init(ttf_path, cache_size, load_to_psram)接口的cache_size参数
参数示例:hzfont.init("/sd/font.ttf", hzfont.HZFONT_CACHE_128)

3.2 HZFONT_CACHE_256

参数含义:缓存最近显示文字数据的内存空间可容纳文字的个数最大值为256个;
数据类型:number
是否必选:可选;
注意事项:用于hzfont.init(ttf_path, cache_size, load_to_psram)接口的cache_size参数
参数示例:hzfont.init("/sd/font.ttf", hzfont.HZFONT_CACHE_256)

3.3 HZFONT_CACHE_512

参数含义:缓存最近显示文字数据的内存空间可容纳文字的个数最大值为512个;
数据类型:number
是否必选:可选;
注意事项:用于hzfont.init(ttf_path, cache_size, load_to_psram)接口的cache_size参数
参数示例:hzfont.init("/sd/font.ttf", hzfont.HZFONT_CACHE_512)

3.4 HZFONT_CACHE_1024

参数含义:缓存最近显示文字数据的内存空间可容纳文字的个数最大值为1024个;
数据类型:number
是否必选:可选;
注意事项:用于hzfont.init(ttf_path, cache_size, load_to_psram)接口的cache_size参数
参数示例:hzfont.init("/sd/font.ttf", hzfont.HZFONT_CACHE_1024)

3.5 HZFONT_CACHE_2048

参数含义:缓存最近显示文字数据的内存空间可容纳文字的个数最大值为2048个;
数据类型:number
是否必选:可选;
注意事项:用于hzfont.init(ttf_path, cache_size, load_to_psram)接口的cache_size参数
参数示例:hzfont.init("/sd/font.ttf", hzfont.HZFONT_CACHE_2048)

四、函数详解

4.1 hzfont.init(ttf_path, cache_size, load_to_psram)

功能

初始化 hzfont 字体库,加载 .ttf 字库,支持使用固件内置字库或加载外部 .ttf 文件。

参数

ttf_path

参数含义:TTF字体文件路径。传入 nil 或省略此参数时,使用固件内置的 ttf 字库;
         传入具体路径时,加载对应路径下的 .ttf 字体文件。
数据类型:string  nil
取值范围:
         传入 nil          使用固件内置 ttf 字库(仅部分固件支持,如 Air8000 14/114 号固件)
         "/MyFont.ttf"            烧录到模块文件系统中的字体
         "/luadb/MyFont.ttf"      随脚本一起烧录到脚本分区的字体
         "/sd/MyFont.ttf"         SD/TF 卡中的字体(需先挂载 fatfs
         "/little_flash/MyFont.ttf"   外置 NOR/NAND Flash 中的字体
是否必选:可选;
注意事项:固件内置字库包含字符范围见产品手册,不包含的字符会以 "*" 号显示;
         加载外部字体时,路径必须指向有效的 .ttf 文件,文件大小不能超过文件系统可用空间;
参数示例:hzfont.init()                  -- 使用固件内置字库
          hzfont.init("/luadb/MyFont.ttf")  -- 从脚本分区加载外部字体
cache_size

参数含义:缓存最近显示文字数据的内存空间可容纳文字个数的最大值;
数据类型:number
取值范围:章节3.1-3.5中的常量;
         1.直接从.ttf文件中加载字体并在屏幕上进行显示每个字所需时间约100ms
         2.从内存空间加载字体数据到屏幕显示每个字所需时间约1ms
         3.此参数为设置默认内存空间可存储文字数据的个数,每个字占用内存不超过100b空间
           笔画越复杂占用内存越多;
是否必选:可选;
注意事项:缺省默认为HZFONT_CACHE_256
参数示例:hzfont.init("/sd/font.ttf", hzfont.HZFONT_CACHE_1024)

load_to_psram

参数含义:是否将字库整包拷贝到PSRAM
数据类型:boolean
取值范围:true或false
是否必选:可选;
注意事项:1.将字库整包拷贝至PSRAM,每个字占用内存最大空间为100b,PSRAM空间不够会初始化失败
         2.使用rtos.meminfo("psram")接口可查询内存使用情况,
           系统运行后可以根据剩余内存空间大小选择是否加载;
         3.字库整包拷贝至PSRAM,每个字显示至屏幕所需时间约为1ms
         4.缺省.ttf字库文件不加载到psram,显示时先从文字缓存空间搜索,
           搜索不到则从.ttf文件中搜索
参数示例:hzfont.init("/sd/font.ttf", nil, true)

返回值

local result = hzfont.init(ttf_path, cache_size, load_to_psram)

参数含义:hzfont是否初始化成功
数据类型:boolean
取值范围:true或false
注意事项:成功返回true,失败返回false
参数示例:local result = hzfont.init()
         if result then
            lcd.drawHzfontUtf8(10, 10, "合宙LuatOS字体演示", 10, 0xF800, 1)
         end

示例 1: 使用 LCD 核心库加载 hzfont

-- 使用固件内置字库
hzfont.init()

-- 从文件加载,使用默认缓存 256
hzfont.init("/sd/font.ttf")

-- 从文件加载,指定缓1024
hzfont.init("/sd/font.ttf", hzfont.HZFONT_CACHE_1024)

-- 从luadb文件系统加载
hzfont.init("/luadb/font.ttf")

-- lcd库显示utf-8字符方式
lcd.drawHzfontUtf8(10, 10, "合宙LuatOS字体演示", 10, 0xF800, 1)
lcd.drawHzfontUtf8(10, 30, "合宙LuatOS字体演示", 20, 0x07E0, 1)
lcd.drawHzfontUtf8(10, 70, "合宙LuatOS字体演示", 40, 0x001F, 1)
lcd.drawHzfontUtf8(10, 130, "合宙LuatOS字体演示", 60, 0xFD20, 1)
lcd.drawHzfontUtf8(10, 210, "合宙LuatOS字体演示", 80, 0x9E66, 1)
lcd.drawHzfontUtf8(10, 310, "合宙LuatOS字体演示", 100, 0xFFFF, 1)

示例 2: 使用 AirUI 核心库加载 hzfont

-- PC端/Air8000/780EHM 从14号固件/114号固件中加载hzfont字库,从而支持12-255号中文显示
airui.font_load({
    type = "hzfont", -- 字体类型,可选 "hzfont" 或 "bin"
    path = nil,    -- 字体路径,对于 "hzfont",传 nil 则使用内置字库
    size = 20,     -- 字体大小,默认 16
    cache_size = 1048, -- 缓存字数大小,默认 2048
    antialias = 1, -- 抗锯齿等级,默认 4
})

-- Air8101使用104号固件将字体文件烧录到文件系统,从文件系统中加载hzfont字库,从而支持12-255号中文显示
airui.font_load({
    type = "hzfont",             -- 字体类型,可选 "hzfont" 或 "bin"
    path = "/MiSans_gb2312.ttf", -- 字体路径,对于 "hzfont",传 nil 则使用内置字库
    size = 20,                   -- 字体大小,默认 16
    cache_size = 1048,           -- 缓存字数大小,默认 2048
    antialias = 2,               -- 抗锯齿等级,默认 4
})

示例 3: 使用 exeasyui 扩展库加载 hzfont

-- 必须加载才能启用exeasyui的功能
local ui = require("exeasyui")

-- 启用14号固件内置HzFont矢量字体方式驱动
ui.hw_init({
    font_config = { type = "hzfont", size = 24, antialias = -1 }, -- 默认-1,表示自动抗锯齿

    -- lcd_config参数填写可以参考合宙exlcd显示扩展库exlcd.init(param)接口说明:https://docs.openluat.com/osapi/ext/exlcd/#31-exlcdinitparam
    lcd_config = {
        lcd_model = "AirLCD_1010", -- LCD型号
        -- pin_vcc = 24,           -- 供电引脚,使用GPIO控制屏幕供电可配置
        pin_rst = 36,              -- 复位引脚
        pin_pwr = 1,               -- 背光控制引脚GPIO ID号
        pin_pwm = 0,               -- 背光控制引脚PWM ID号
        port = lcd.HWID_0,         -- 驱动端口
        -- pin_dc = 0xFF,          -- lcd数据/命令选择引脚GPIO ID号,使用lcd 专用 SPI 接口 lcd.HWID_0不需要填此参数,使用通用SPI接口需要赋值
        direction = 0,             -- lcd屏幕方向 0:0° 1:90° 2:180° 3:270°,屏幕方向和分辨率保存一致
        w = 320,                   -- lcd 水平分辨率
        h = 480,                   -- lcd 竖直分辨率
        xoffset = 0,               -- x偏移(不同屏幕ic 不同屏幕方向会有差异)
        yoffset = 0,               -- y偏移(不同屏幕ic 不同屏幕方向会有差异)
        sleepcmd = 0X10,           -- 睡眠命令,默认0X10
        wakecmd = 0X11,            -- 唤醒命令,默认0X11
    }
})

示例 4: 使用 eink 核心库加载 hzfont

eink 核心库通过 eink.drawHzfont() 接口直接渲染 hzfont 矢量字体,不需要先调用 hzfont.init()。注意该接口仅支持固件内置的 ttf 数据,不支持加载外部 ttf 文件。

-- eink初始化(需先初始化SPI)
local spi_id = 0
local pin_cs = 31
local pin_dc = 30
local pin_busy = 32
local pin_reset = 29

spi_eink = spi.deviceSetup(spi_id, pin_cs, 0, 0, 8, 20 * 1000 * 1000, spi.MSB, 1, 1)

eink.init(eink.MODEL_1in54_V2,
    { port = "device", pin_dc = pin_dc, pin_busy = pin_busy, pin_rst = pin_reset },
    spi_eink)

eink.setWin(200, 200, 0)
eink.clear(1, true)

-- 直接使用 eink.drawHzfont 显示矢量字体(无需 hzfont.init())
eink.drawHzfont(20, 40, "合宙LuatOS 16号", 16, 1)
eink.drawHzfont(20, 80, "合宙LuatOS 24号", 24, 1)

eink.show(0, 0, true)

示例 5: 使用 u8g2 核心库加载 hzfont

u8g2 核心库通过 u8g2.SetHzFont() 接口加载 hzfont 矢量字体,不需要先调用 hzfont.init()。该接口支持通过 path 参数加载外部 ttf 文件。

-- u8g2初始化
local result = u8g2.begin(
    {
        ic = "ssd1306",
        direction = 0,
        mode = "spi_hw_4pin",
        spi_id = 0,
        spi_res = 20,
        spi_dc = 21,
        spi_cs = 24,
    },
    {
        width = 128,
        height = 64,
    }
)

-- 加载固件内置的 hzfont 矢量字体(无需 hzfont.init())
-- u8g2.SetHzFont()                          -- 使用固件内置字库
-- u8g2.SetHzFont("/luadb/MyFont.ttf")       -- 从文件系统加载 ttf
u8g2.SetHzFont()

-- 使用 DrawUTF8 显示矢量字体文字(以左下角为坐标)
u8g2.DrawUTF8("合宙LuatOS 矢量字体", 0, 30)

-- 刷新到屏幕
u8g2.SendBuffer()

4.2 hzfont.debug(enable)

功能

开启 hzfont 调试日志并进行打印开关

参数

enable

参数含义:是否开启hzfont调试日志
数据类型:boolean
取值范围:true或false
是否必选:必选;
注意事项:1.默认不打开debug日志
         2.打开时会打印大量加载字体具体耗时的日志;
参数示例:hzfont.debug(true) -- 开启debug
         hzfont.debug(false) -- 关闭debug

返回值

local result = hzfont.debug(enable)

result

参数含义:无;
数据类型:boolean
取值范围:总是返回true
注意事项:无法做为初始化成功判断信息;
参数示例:无

例子

-- hzfont调试信息开关
hzfont.debug(true)

五、产品支持说明

不同产品的LuatOS固件,对hzfont核心库的支持情况如下:

1、Air780EX2/Air700ECP/Air780EPM/Air780EGP 不支持;

2、Air700ECH/Air780EHM/EHV/EGH/EGG/EHU/EHN/Air8000系列 14/114号、15/115号、16/116号固件支持;

3、Air8101系列 102号、104号、105号、106号固件支持;

4、Air1601 / Air1602 都支持;

各产品固件支持核心库列表详细说明参考下面链接:

Air780EX2/Air700ECP/Air780EPM/Air780EGP固件支持列表

Air700ECH/Air780EHM/EHV/EGH/EGG/EHU/EHN固件支持列表

Air8000系列所有型号固件支持列表

Air8101系列所有型号固件支持列表

Air1601/Air1602固件支持列表

六、从外部从外部加载.ttf 操作说明

  1. Air8101 支持 hzfont 的固件中未内置.ttf 文件,可以选择从 SD 卡或者模组文件系统中加载。如果选择从模组文件系统中加载,那么在烧录固件时需要手动将.ttf 文件字体文件一并烧录到模组的文件系统中,且初始化 hzfont 时选择所烧录的字体文件。
  2. 字体文件大小不能超过固件所支持的文件系统空间。
  3. 烧录步骤如下:

  4. 将.ttf 文件准备好,或者下载 MiSans_gb2312.ttf,单独放入一个空文件夹中

  5. 烧录固件时选择对应的文件夹,然后选择下载底层和脚本
  6. 使用方式:搭配 lcd 核心库、AirUI 核心库、exeasyui 扩展库使用方式,可以参考见 4.1 章节 初始化示例,在此不重复赘述