跳过正文

Rust 驱动 LCD - 显示中英文

Rust Esp32
目录
rust-esp32 - 这篇文章属于一个选集。
§ 13: 本文

embedded-graphics 支持 BDF 和 MonoFont 字体:

  • 优先选择 BDF 字体, 特别是是中英文混合显示场景, MonoFont 由于中英文等宽, 会导致英文字体显示间距过大.
  • 优先选择 embedded-graphics/bdf 方案.

The draw method for text drawables returns the position of the next character. This can be used to combine text with different character styles on a single line of text.

// https://docs.rs/embedded-graphics/latest/embedded_graphics/text/index.html#examples
// Create a small and a large character style.
let small_style = MonoTextStyle::new(&FONT_6X10, Rgb565::WHITE);
let large_style = MonoTextStyle::new(&FONT_10X20, Rgb565::WHITE);

// Draw the first text at (20, 30) using the small character style.
let next = Text::new("small ", Point::new(20, 30), small_style).draw(&mut display)?;

// Draw the second text after the first text using the large character style.
let next = Text::new("large", next, large_style).draw(&mut display)?;

字体参考:https://github.com/olikraus/u8g2

embedded-graphics/bdf
#

https://github.com/embedded-graphics/bdf/tree/master

该库可以从 bdf 字体文件中生成 embedded-graphics 能使用的:

  1. BDF Font:需要配合使用embedded-graphics/bdf/eg-bdf 库中提供的 BdfFont/BdfGlyph/BdfTextStyle 类型;
  2. MonoFont:embedded-graphics 可以直接导入使用;

先使用 otf2bdf 工具将 TTF(truetype font) 字体转换为 BDF(Bitmap Distribution Format) 类型:

  • 对于 X11 使用的 pcf 字体格式,使用 pcf2bdf 工具将它转换为 bdf 格式;
  • 转换时 -p 参数指定生成的 BDF 字体大小;
brew install otf2bdf
otf2bdf  -p 12 -r 75 -o LXGWWenKai_Regular.bdf  ~/Library/Fonts/LXGWWenKai-Regular.ttf

然后使用 eg-font-converter lib 来生成 BDF 或 MonoFont 字体:

  • 两者都至少包含 2 个文件: bitmap data 文件(.data) 和需要 include!() 到引用的 rs 文件;
  • MonoFont 的 bitmap 文件是从 png 图片生成的, 故 MonoFont 还会生成 png 文件;

生成 BDF 字体示例:

// /Users/zhangjun/codes/rust/bdf/eg-font-converter/src/bin/my-bdf-font.rs
zj@a:~/codes/rust/bdf/eg-font-converter$ mkdir output
zj@a:~/codes/rust/bdf/eg-font-converter$ ls
Cargo.lock  Cargo.toml  output/  src/  tests/
zj@a:~/codes/rust/bdf/eg-font-converter$ cat src/bin/my-bdf-font.rs
use eg_font_converter::{FontConverter};

// 中文 unicode 字体范围: https://en.wikipedia.org/wiki/CJK_Unified_Ideographs_(Unicode_block)
// 使用 .glyphs() 来指定要生成的字符范围
fn main() {
    let font = FontConverter::new("/Users/zhangjun/docs/LXGWWenKai_Regular.bdf", "BDF_LXGWWenKai_Regular_FONT")
        //.glyphs('a'..='z')
        .glyphs('\u{0000}'..='\u{007F}') // ASCII
        .glyphs('\u{4E00}'..='\u{9FFF}') // 常用中文字体范围
        .glyphs('\u{2E80}'..='\u{2EF3}') // 常见繁体字范围
        .missing_glyph_substitute('?') // 替代字符
        .convert_eg_bdf()
        .unwrap();
    font.save("output/").unwrap();  // 结果写入 output 目录下, 存入 new(bdf_file, name) 的 小写 name.rs 文件中.
}

// 运行该程序
zj@a:~/codes/rust/bdf/eg-font-converter$ cargo run --bin my-bdf-font
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.32s
     Running `/Users/zhangjun/codes/rust/bdf/target/debug/my-bdf-font`

zj@a:~/codes/rust/bdf/eg-font-converter$ ls output/
bdf_lxgwwenkai_regular_font.data  bdf_lxgwwenkai_regular_font.rs
zj@a:~/codes/rust/bdf/eg-font-converter$ ls -l output/
total 4.7M
-rw-r--r-- 1 zhangjun 310K  2 17 15:34 bdf_lxgwwenkai_regular_font.data
-rw-r--r-- 1 zhangjun 4.4M  2 17 15:34 bdf_lxgwwenkai_regular_font.rs
zj@a:~/codes/rust/bdf/eg-font-converter$ head -20 output/bdf_lxgwwenkai_regular_font.rs
const BDF_LXGWWenKai_Regular_FONT: ::eg_bdf::BdfFont = {
    const fn rect(
        x: i32,
        y: i32,
        width: u32,
        height: u32,
    ) -> ::embedded_graphics::primitives::Rectangle {
        ::embedded_graphics::primitives::Rectangle::new(
            ::embedded_graphics::geometry::Point::new(x, y),
            ::embedded_graphics::geometry::Size::new(width, height),
        )
    }
    ::eg_bdf::BdfFont {
        data: include_bytes!("bdf_lxgwwenkai_regular_font.data"),
        replacement_character: 63usize,
        ascent: 12u32,
        descent: 3u32,
        glyphs: &[
            BdfGlyph {
                character: '\0',
zj@a:~/codes/rust/bdf/eg-font-converter$

使用:

zj@a:~/codes/rust/bdf/eg-font-converter$ cp output/bdf_lxgwwenkai_regular_font.* ~/codes/esp32/st7735-lcd-examples/esp32c3-examples/src/

# 添加本地依赖
gzj@a:~/codes/esp32/st7735-lcd-examples/esp32c3-examples$ grep eg Cargo.toml
eg-bdf = { path = "../../../rust/bdf/eg-bdf/" } // clone https://github.com/embedded-graphics/bdf 的本地目录

代码举例:


use eg_bdf::{BdfGlyph, BdfTextStyle};

include!("./bdf_lxgwwenkai_regular_font.rs");

fn main() {
    let text = "Happy\u{AD}Loong\u{AD}Year!\
      Happy Hacking!\
      Happy Loong Year!\u{A0}新年快乐!\u{A0}-from Rust ESP32";
    let bdf_style = BdfTextStyle::new(&BDF_LXGWWenKai_Regular_FONT, Rgb565::WHITE);
    let textbox_style = TextBoxStyleBuilder::new()
      .line_height(LineHeight::Pixels(12)) // 字体高度与生成 otf2bdf  -p 12 一致
      .alignment(HorizontalAlignment::Justified)
      .paragraph_spacing(0)
      .build();
    let mut bounds = Rectangle::new(Point::new(0, 0), Size::new(128, 160));
    let mut text_box = TextBox::with_textbox_style(text, bounds, bdf_style, textbox_style);
    let next = text_box.draw(&mut display).unwrap();
}

embedded-text
#

缺点:

  1. 不支持 MonoFont,只支持 BdfFont;
  2. 自定义实现的 BdfTextStyle 不完整,[[https://github.com/flyingyizi/embedded-fonts/blob/main/src/style.rs][不支持 baseline/calculate bounding box 等特性]],在于 BoxText 结 合使用时,可能会导致排版不准确。

提供了一个 convert-bdf 工具命令,从指定的 bdf 文件中生成指定 range 的 BdfFont:

  • 该 crate 的 src/sytle.rs 提供了[[https://github.com/flyingyizi/embedded-fonts/blob/main/src/style.rs][自定义的 BdfFont, BdfGlyph 和 BdfTextStyle 实现]],使用时需要导入;
zj@a:~/docs$ convert-bdf  --bdffile LXGWWenKai_Regular.bdf
zj@a:~/codes/rust/bdf$ convert-bdf --range 'Happy Loong Year! Happy Hacking!新年快乐,龙年大吉!-from Rust ESP32'  --bdffile LXGWWenKai_Regular.bdf

MonoFont
#

embedded-graphics 缺省支持 Mono 和 BDF 字体。

构建 font-build-tool 二进制命令:

  • 可以设置 const UNICODE_CODE_BLOCKS: &[UnicodeCodeBlock] 来指定要生成的 MonoFont 字体范围,同时也 需要同步修改 [[https://github.com/ferristseng/rust-embedded-graphics-cjk/blob/b84db4ef0a540fb2a585161839d5cebb21ac4184/font-build-tool/src/builder.rs#L220][pub fn save_rust_source]] 中的字符串模板;
zj@a:~/codes/rust/rust-embedded-graphics-cjk/font-build-tool$ cargo build --bin ttf2bits --features=clap

# --intensity-threshold 表示一个字体在 bitmap 中输出时最小像素数(width*height)
# 常见 LCD 字体大小 8*16,12*11 均满足。
# -s 指定字体 width,字体 height 会被自动计算。
zj@a:~/codes/rust/rust-embedded-graphics-cjk/font-build-tool$ ../target/debug/ttf2bits --help
ttf2bits 0.1.0
Ferris Tseng <[email protected]>

USAGE:
    ttf2bits [OPTIONS] <FONT_PATH> <OUTPUT_PREFIX> <OUTPUT_FORMAT>

ARGS:
    <FONT_PATH>
    <OUTPUT_PREFIX>
    <OUTPUT_FORMAT>    [possible values: rs, png]

OPTIONS:
    -h, --help                                         Print help information
        --intensity-threshold <INTENSITY_THRESHOLD>    [default: 128]
    -o, --output-dir <OUTPUT_DIRECTORY>                [default: .]
    -s, --size <FONT_SIZES>
    -V, --version                                      Print version information
zj@a:~/codes/rust/rust-embedded-graphics-cjk/font-build-tool$

使用上面生成的 ttf2bits 命令将 =otf 字体文件= 转换成 embedded-graphics 能使用的 =monofont= :

  • embedded-graphics 能使用两类字体:bdf 和 MonoFont;
  • -s 指定生成的字体宽度,不需要指定字体高度,font-build-tool 在遍历字体时自动计算高度;在 stdout 输 出 max_glyph_height=20 max_glyph_width=12 等计算后的字体高度和宽度;
zj@a:~/codes/rust/rust-embedded-graphics-cjk/font-fusion-pixel$ rm -rf png src
zj@a:~/codes/rust/rust-embedded-graphics-cjk/font-fusion-pixel$ ls target/
zj@a:~/codes/rust/rust-embedded-graphics-cjk/font-fusion-pixel$ mkdir target/font
zj@a:~/codes/rust/rust-embedded-graphics-cjk/font-fusion-pixel$ cp ~/fusion-pixel-12px-monospaced-zh_hans.otf target/font/
zj@a:~/codes/rust/rust-embedded-graphics-cjk/font-fusion-pixel$ touch target/font/FUSION_PIXEL_DOWNLOAD
zj@a:~/codes/rust/rust-embedded-graphics-cjk/font-fusion-pixel$
zj@a:~/codes/rust/rust-embedded-graphics-cjk/font-fusion-pixel$ make
mkdir -p target/font/fusion-pixel-2023.11.26
../target/debug/ttf2bits \
                target/font/fusion-pixel-12px-monospaced-zh_hans.otf \
                fusion_pixel \
                png \
                -o png \
                -s 12
font.family_name=Some("Fusion Pixel 12px Monospaced zh_hans") font.style_name=Some("Regular")
max_glyph_height=14 max_glyph_width=12
../target/debug/ttf2bits \
                target/font/fusion-pixel-12px-monospaced-zh_hans.otf \
                fusion_pixel \
                png \
                -o png \
                -s 24
font.family_name=Some("Fusion Pixel 12px Monospaced zh_hans") font.style_name=Some("Regular")
max_glyph_height=30 max_glyph_width=24
../target/debug/ttf2bits \
                target/font/fusion-pixel-12px-monospaced-zh_hans.otf \
                fusion_pixel \
                rs \
                -o src \
                -s 12
font.family_name=Some("Fusion Pixel 12px Monospaced zh_hans") font.style_name=Some("Regular")
max_glyph_height=14 max_glyph_width=12
Error: IoError(Os { code: 2, kind: NotFound, message: "No such file or directory" })
make: *** [src/fusion_pixel_12.rs] Error 1
zj@a:~/codes/rust/rust-embedded-graphics-cjk/font-fusion-pixel$ # 解决办法:创建 src/data 目录
zj@a:~/codes/rust/rust-embedded-graphics-cjk/font-fusion-pixel$ mkdir -p src/data
zj@a:~/codes/rust/rust-embedded-graphics-cjk/font-fusion-pixel$ make


zj@a:~/codes/rust/rust-embedded-graphics-cjk/font-fusion-pixel$ ls
Cargo.toml  FUSION_PIXEL_VERSION  Makefile  png/  src/  target/
# 生成的包含所有字体的 png 图片
zj@a:~/codes/rust/rust-embedded-graphics-cjk/font-fusion-pixel$ ls png/
fusion_pixel-12.png  fusion_pixel-24.png
# 将 png 转换为 raw image 字节,在 fusion_pixel_12.rs  fusion_pixel_24.rs 里引用;
zj@a:~/codes/rust/rust-embedded-graphics-cjk/font-fusion-pixel$ ls src/data/ #
fusion_pixel-12.bin  fusion_pixel-24.bin
# fusion_pixel_12.rs 是 embedded_graphics::mono_font::MonoFont 类型的字体
# image:::embedded_graphics::image::ImageRaw 类型,内容来源于 fusion_pixel-12.bin;
# glyph_mapping:字体文件中包含的 glyph_mapping, 即 unicode 代码点列表;
# character_size:字体的宽度和高度(最大)
zj@a:~/codes/rust/rust-embedded-graphics-cjk/font-fusion-pixel$ ls src/
data/  fusion_pixel_12.rs  fusion_pixel_24.rs  lib.rs
# lib.rs 将字体导出到外部使用。
zj@a:~/codes/rust/rust-embedded-graphics-cjk/font-fusion-pixel$ cat src/lib.rs
mod fusion_pixel_12;
mod fusion_pixel_24;

pub use fusion_pixel_12::FONT as FUSION_PIXEL_12;
pub use fusion_pixel_24::FONT as FUSION_PIXEL_24;


# 后续每次重新构建需要删除文件
zj@a:~/codes/rust/rust-embedded-graphics-cjk/font-fusion-pixel$ rm -rf png src/fusion_pixel*.rs
zj@a:~/codes/rust/rust-embedded-graphics-cjk/font-fusion-pixel$ make

自动生成的文件:

// 查看生成的 png 图片
zj@a:~/codes/rust/rust-embedded-graphics-cjk/font-fusion-pixel$ open png/fusion_pixel-12.png
zj@a:~/codes/rust/rust-embedded-graphics-cjk/font-fusion-pixel$ cat src/fusion_pixel_12.rs
// This is generated code. Any modifications to this file will
// be overwritten.
use embedded_graphics::{
    geometry::Size,
    image::ImageRaw,
    mono_font::{DecorationDimensions, MonoFont},
};
use embedded_graphics_cjk_glyph_mapping::RangeGlyphMapping;

#[rustfmt::skip]
pub const FONT: MonoFont = MonoFont {
    image: ImageRaw::new(
        include_bytes!("data/fusion_pixel-12.bin"),
        32 * 12,
    ),
    glyph_mapping: &RangeGlyphMapping::new_unchecked(
        [
            '\u{0000}'..='\u{007F}',    // BASIC_LATIN
            '\u{2E80}'..='\u{2EF3}',    // CJK Radicals Supplement
            '\u{4E00}'..='\u{9FFF}',    // CJK Unified Ideographs
        ],
        0
    ),
    character_size: Size::new(12, 14),  // bin 文件中最大字体 width 12 和 height 14。
    character_spacing: 0,
    baseline: 0,
    underline: DecorationDimensions::new(15, 1),
    strikethrough: DecorationDimensions::new(7, 1),
};

flyingyizi/embedded-fonts
#

https://github.com/flyingyizi/embedded-fonts

With this crate, you can only import the required glyphs into your project. For example, only 35 characters “中国欢迎China welcomes日本へようこそWelcome to Japan북한 환영Welcome North Korea” are introduced, which is very meaningful in MCUs with limited space, such as avr.

$ convert-bdf   --range "中国欢迎China welcomes日本へようこそWelcome to Japan북한 환영Welcome North Korea"   wenquanyi_12pt.bdf
rust-esp32 - 这篇文章属于一个选集。
§ 13: 本文

相关文章

Rust 驱动 Audio - 播放和录音
Rust Esp32
Rust 驱动 Camera - 采集和播放
Rust Esp32
Rust 驱动 LCD - 显示图片
Rust Esp32
Rust 驱动 Touch - 触摸板
Rust Esp32