# MicroPython New FontLib **Repository Path**: walkline/micropython-new-fontlib ## Basic Information - **Project Name**: MicroPython New FontLib - **Description**: MicroPython 用户自定义中文字库 - **Primary Language**: Python - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 26 - **Forks**: 15 - **Created**: 2021-08-12 - **Last Updated**: 2025-04-01 ## Categories & Tags **Categories**: utils **Tags**: MicroPython, 中文字库 ## README

MicroPython New FontLib

### 项目介绍 使用`MicroPython 开发板`读取自定义字库并显示 ### 获取完整项目 因为项目中使用了子模块 [FontMaker Client](https://gitee.com/walkline/fontmaker-client.git) 的`binary`分支 和 [OLED Research](https://gitee.com/walkline/oled-research.git),所以要获取完整项目代码需要如下操作 #### 克隆方式 ```bash $ git clone --recursive https://gitee.com/walkline/micropython-new-fontlib.git ``` #### 下载压缩文件方式 或者克隆时未使用`--recursive`参数的,使用如下代码更新子模块 ```bash $ cd micropython-new-fontlib $ git submodule update --init --recursive ``` ### 如何使用和测试 #### 生成字库文件 > 生成字库文件详细说明参考 [FontMaker Client](https://gitee.com/walkline/fontmaker-client) 项目文档 直接运行 ```bash $ client/youyuan_16.cmd ``` 会生成一个`combined.bin`文件,字库文件信息如下 | 参数 | 数值 | 说明 | | :-: | :-: | :-: | | 字体 | 幼圆 | | | 字号 | 16 | 像素 | | 字重 | 普通 | 不加粗、不斜体、无下划线 | | 字符宽度 | 固定 | | | 宽度 | 16 | 像素 | | 高度 | 16 | 像素 | | 水平偏移 | 0 | 像素 | | 垂直偏移 | 0 | 像素 | | 扫描方式 | 垂直扫描 | | | 字节顺序 | 低位在前 | | #### 使用电脑测试 直接运行 ```bash $ python fontlib.py ``` 会显示相关信息,包括: * 字库信息 * 获取的字模数据 * 字模打印预览 完整输出内容如下 ```docs HZK Info: .//client/combined.bin file size : 261972 font height : 16 data size : 32 scan mode : Vertical byte order : LSB characters : 7710 !: b'\x00\x00\x00\x00\x00\x00\x08\x00\x0c\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x00\x00\x08\x00\x08\x00\x00\x00' ☆: b'\x00\x00\x00\x00\x00\x80\x01\x80\x01\x80\x01@\x02@~? \x04\x10\x08\x0c\x10\x08\x10\x08\x90\x0bH\x148\x18\x08' ⒉: b'\x00\x00\x00\x00\x00\x00\x07\xc0\x08@\x00@\x00@\x00@\x00\x80\x01\x00\x01\x00\x02\x00\x04\x00\x0c\x08\x0f\xcc\x00\x00' ,: b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00p\x000\x00 \x00' 我: b'\x00\x00\x00@\x07PxH\x08D\x08D\x7f\xfe\x08D\x08D\t(\x0e0x0\x080\x08R\t\x8ax\x06' ㄘ: b'\x00\x00\x00\x00\x00\x00\x01\x00\x01\x80\x00\x80\x01\x00\x03\xf0\x1d\x00\x01\x00\x02\xe0\x03`\x00@\x00\x80\x00\x00\x00\x00' ■: b'\x00\x00\x00\x00\x7f\xff\x7f\xff\x7f\xff\x7f\xff\x7f\xff\x7f\xff\x7f\xff\x7f\xff\x7f\xff\x7f\xff\x7f\xff\x7f\xff\x7f\xff\x7f\xff' B: b'\x00\x00\x00\x00\x00\x00\x00\x00\x07\xf0\x060\x068\x060\x06p\x07\xf0\x06\x18\x06\x18\x06\x18\x07\xf0\x00\x00\x00\x00' 中: b'\x00\x00\x01\x00\x01\x00\x01\x00?\xfc!\x04!\x04!\x04!\x04!\x04!\x04?\xfc\x01\x00\x01\x00\x01\x00\x01\x00' 爱: b'\x00\x00\x00\x00?\xf8\x11\x10\t\x10?\xfcD\x02B\x02\x1f\xf8\x04\x00\x07\xf8\x0e\x08\x13\x10 \xe0\x01\xe0\x1e\x1e' β: b'\x00\x00\x00\x00\x00\x00\x01\xe0\x01\x10\x030\x02 \x02\xc0\x02`\x06 \x04 \x04`\x04`\x07\xc0\x0c\x00\x08\x00' あ: b"\x00\x00\x00\x00\x03\x00\x03\x00\x02\xc0\x1f\x80\x02\x00\x06\x80\x07\xf0\x0c\x98\x15\x0c'\x0c&\x0c/\x088\x10\x01\xe0" H: b'\x00\x00\x00\x00\x00\x00\xe7\x00B\x00B\x00B\x00B\x00~\x00B\x00B\x00B\x00B\x00\xe7\x00\x00\x00\x00\x00' 华: b'\x00\x00\x00\x00\x08@\x08H\x18p(\xc0+BHB\x08~\x01\x00\x01\x00\x7f\xfe\x01\x00\x01\x00\x01\x00\x01\x00' ǚ: b"\x00\x00\x00\x00\x00\x00\x00\x00\x14\x00\x08\x004\x00C\x00C\x00C\x00C\x00C\x00C\x00'\x00\x18\x00\x00\x00" e: b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00<\x00B\x00~\x00@\x00@\x00B\x00<\x00\x00\x00\x00\x00' l: b'\x00\x00\x00\x00\x00\x00p\x00\x10\x00\x10\x00\x10\x00\x10\x00\x10\x00\x10\x00\x10\x00\x10\x00\x10\x00|\x00\x00\x00\x00\x00' o: b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00<\x00B\x00B\x00B\x00B\x00B\x00<\x00\x00\x00\x00\x00' ⑴: b'\x00\x00\x00\x00\x08\x00\x10\x08!\x84 \x82@\x82@\x83@\x81@\x81@\x81@\x82 \x82 \x84\x13\xc4\x10\x08' ................ ................ ................ ................ ................ ................ ................ ................ ................ ................ .........@...... ................ ................ ........@....... ................ ................ .....@@@.@.@.... ................ ....@........... .......@@....... .....@@@@@...... ................ .@@@@....@..@... .......@........ ....@@.......... .......@@....... ....@....@...... ................ ....@....@...@.. .......@@....... ....@........... .......@.@...... .........@...... ................ ....@....@...@.. ........@....... ....@........... ......@..@...... .........@...... ................ .@@@@@@@@@@@@@@. .......@........ ....@........... .@@@@@@...@@@@@@ .........@...... ................ ....@....@...@.. ......@@@@@@.... ....@........... ..@..........@.. ........@....... ................ ....@....@...@.. ...@@@.@........ ....@........... ...@........@... .......@........ ................ ....@..@..@.@... .......@........ ....@........... ....@@.....@.... .......@........ ................ ....@@@...@@.... ......@.@@@..... ....@........... ....@......@.... ......@......... ................ .@@@@.....@@.... ......@@.@@..... ................ ....@...@..@.... .....@.......... ................ ....@.....@@.... .........@...... ....@........... ....@.@@.@..@... ....@@......@... .@@@............ ....@....@.@..@. ........@....... ....@........... ...@.@....@@@... ....@@@@@@..@@.. ..@@............ ....@..@@...@.@. ................ ................ ...@@.......@... ................ ..@............. .@@@@........@@. ................ ................ ................ ................ ................ ................ ................ ................ ................ .......@........ ................ ................ ................ .@@@@@@@@@@@@@@@ ................ .......@........ ..@@@@@@@@@@@... ................ ......@@........ .@@@@@@@@@@@@@@@ ................ .......@........ ...@...@...@.... .......@@@@..... ......@@........ .@@@@@@@@@@@@@@@ .....@@@@@@@.... ..@@@@@@@@@@@@.. ....@..@...@.... .......@...@.... ......@.@@...... .@@@@@@@@@@@@@@@ .....@@...@@.... ..@....@.....@.. ..@@@@@@@@@@@@.. ......@@..@@.... ...@@@@@@....... .@@@@@@@@@@@@@@@ .....@@...@@@... ..@....@.....@.. .@...@........@. ......@...@..... ......@......... .@@@@@@@@@@@@@@@ .....@@...@@.... ..@....@.....@.. .@....@.......@. ......@.@@...... .....@@.@....... .@@@@@@@@@@@@@@@ .....@@..@@@.... ..@....@.....@.. ...@@@@@@@@@@... ......@..@@..... .....@@@@@@@.... .@@@@@@@@@@@@@@@ .....@@@@@@@.... ..@....@.....@.. .....@.......... .....@@...@..... ....@@..@..@@... .@@@@@@@@@@@@@@@ .....@@....@@... ..@....@.....@.. .....@@@@@@@@... .....@....@..... ...@.@.@....@@.. .@@@@@@@@@@@@@@@ .....@@....@@... ..@@@@@@@@@@@@.. ....@@@.....@... .....@...@@..... ..@..@@@....@@.. .@@@@@@@@@@@@@@@ .....@@....@@... .......@........ ...@..@@...@.... .....@...@@..... ..@..@@.....@@.. .@@@@@@@@@@@@@@@ .....@@@@@@@.... .......@........ ..@.....@@@..... .....@@@@@...... ..@.@@@@....@... .@@@@@@@@@@@@@@@ ................ .......@........ .......@@@@..... ....@@.......... ..@@@......@.... .@@@@@@@@@@@@@@@ ................ .......@........ ...@@@@....@@@@. ....@........... .......@@@@..... ................ ................ ................ ................ ................ ................ ................ ................ ................ ................ ................ ................ ................ ....@....@...... ................ ................ ................ ................ @@@..@@@........ ....@....@..@... ................ ................ .@@@............ ................ .@....@......... ...@@....@@@.... ...@.@.......... ................ ...@............ ................ .@....@......... ..@.@...@@...... ....@........... ................ ...@............ ................ .@....@......... ..@.@.@@.@....@. ..@@.@.......... ................ ...@............ ................ .@....@......... .@..@....@....@. .@....@@........ ..@@@@.......... ...@............ ..@@@@.......... .@@@@@@......... ....@....@@@@@@. .@....@@........ .@....@......... ...@............ .@....@......... .@....@......... .......@........ .@....@@........ .@@@@@@......... ...@............ .@....@......... .@....@......... .......@........ .@....@@........ .@.............. ...@............ .@....@......... .@....@......... .@@@@@@@@@@@@@@. .@....@@........ .@.............. ...@............ .@....@......... .@....@......... .......@........ .@....@@........ .@....@......... ...@............ .@....@......... @@@..@@@........ .......@........ ..@..@@@........ ..@@@@.......... .@@@@@.......... ..@@@@.......... ................ .......@........ ...@@........... ................ ................ ................ ................ .......@........ ................ ................ ................ ................ ................ ................ ....@........... ...@........@... ..@....@@....@.. ..@.....@.....@. .@......@.....@. .@......@.....@@ .@......@......@ .@......@......@ .@......@......@ .@......@.....@. ..@.....@.....@. ..@.....@....@.. ...@..@@@@...@.. ...@........@... ``` #### 使用开发板测试 1 推荐使用 [AMPY Batch Tool](https://gitee.com/walkline/a-batch-tool) (`ab`工具) 进行文件上传和调试操作 ```bahs # 上传文件 $ ab # 进入 repl 模式 $ ab --repl # 运行开发板上的文件 # 使用快捷键 Ctrl-T >>> Run onboard file [1] /boot.py [2] /drivers/ssd1306.py [3] /fontlib.py Choose a file: 3 >>> # 在开发板上运行本地文件 # 使用快捷键 Ctrl-R >>> Run local file [1] fontlib.py [2] drivers\ssd1306.py Choose a file: 1 >>> ``` #### 使用开发板测试 2 这是把`fontlib.py`文件中非`MicroPython`代码精简掉,并编译为字节码再运行测试的方法 ```bash # 上传文件 $ ab abconfig-mpy ``` 因为上传文件中已经包含了`main.py`,所以直接复位根据提示进行选择,就可以看到效果了 ### 使用图标字体生成字库 方法参见 [使用图标字体生成字库](./ICONFONT.md) ### 选取其它语种字符的方法 `GB2312`收录了包括拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母在内的 682 个全角字符 可以用键盘输入它们,但是并不一定简单,好在我们还可以从 [GB2312简体中文编码表](http://tools.jb51.net/table/gb2312) 页面中直接查看复制它们 ### 关于速度 > 以当前字库文件举例: > * 文件名:`combined.bin` > * 文件大小:`261,972`字节 > * `GB2312`索引表大小:`17,672`字节 > * 点阵大小:`16x16`像素 > * 字符数据大小:`32`字节 > * 字符总数:`7710`个(包含`ASCII`和`GB2312`) * 实例化`FontLib`时打开字库文件,读取文件头信息,大约 12 ms * 同时读取`?`的字符数据作为占位符,大约 5 ms * 打印字库信息,大约 39 ms(简直无语,建议实际使用时不要打印) * 检索字符数据前再次打开字库文件,大约 12 ms * 检索字符数据,每字符大约 12 ms * 字符数据使用`Dict`存储,字符`Unicode`值作为关键字,取值耗时可以忽略 * 显示字符耗时未统计 ### 关于提速 决定显示速度的因素有两方面: * 从字库中读取指定字符的字模数据 * 在 oled 中显示 显示部分,`MicroPython`已经提供了`SSD1306`驱动,使用`FrameBuffer`进行数据管理,我相信官方的实力,所以显示部分的速度假设已经没有提升空间了 数据部分,最开始的思路就是“完成功能”,所以在读取的时候是这个流程: 1. 打开字库文件 2. 取一个字符 3. `分段`查找字符在`索引表`中的偏移量 4. `定位`到偏移量读取字模数据 5. 从第`2`步开始循环,直到读取所有字符数据 > `分段`的意思是:在`Python`中打开文件,它不提供查找功能,需要先读取文件内容再进行查找,对于开发板来说,一次性读取全部文件肯定会导致`内存溢出`,所以需要分段读取一段合理长度的数据再进行查找,在查找到之前就需要不停的分段读取 这个方法的问题在于,每个字符都要从`索引表`的开始位置进行查找,比较浪费 提速的思路就是,在每次`分段`读取数据的时候查找所有字符,减少`分段`次数 继续查找资料,有人说使用`read`代替`seek`可以提高`定位`的效率,在电脑上进行了简单测试,300M 的文件使用`read`比`seek`快大约`0.2 ms`,但是这个方法有个致命问题,虽然`read`读取到的数据并不使用,但也会分配内存空间,在开发板上运行的结果就是,更慢了 对比一下三种方式的读取速度(毫秒) 字库文件信息: * 文件大小:500024 字节 * 字符总数:8932 个 * 数据大小:54 字节 | | 原方法 | 新方法(seek) | 新方法(read) | | :-: | :-: | :-: | :-: | | 打开字库 | 27.51 | 26.22 | 61.21 | | 字符数 | 原方法 | 新方法(seek) | 新方法(read) | | :-: | :-: | :-: | :-: | | 240 | 13.09 | 6.50 | 159.28 | | 227 | 16.05 | 8.22 | / | | 767 | 内存溢出 | 内存溢出 | / | > 注:统计结果为**平均时间** 这里的字符数是包含了重复字符的总数,实际获取数据时是要去重的 读取速度还与字符在`索引表`中的相对位置有关,相对位置靠前的字符当然能更快被找到,反之则更慢 一次性读取太多的数据也会导致`内存溢出`,所以在实际使用时不建议一次性读取太多 手动调用垃圾回收(gc.collect())的时机一定要掌握好,并不是任何时候手动调用都能起到预期的作用,有时还会起到反作用 对于能够自动回收的,如函数内部的变量,可以省去手动调用的操作 > 统计运行时间使用的装饰器 ```python from utime import ticks_us, ticks_diff def cal_time(fn): def wrapper(*args,**kwargs): starTime = ticks_us() f = fn(*args,**kwargs) print('%s() runtime: %s ms' % (fn.__name__, ticks_diff(ticks_us(), starTime) / 1000)) return f return wrapper ``` ### 关于测速 又增加了一个单独测速的文件`fontlib_test.py`,发现一个现象,虽然知道和缓存有关,但是具体怎么实现的并不了解 这里测速使用了两个方法 * 一次获取所有字符的字模数据 * 分段获取所有字符的字模数据 单独测试这两种方法,得到的结果如下 ```docs ### method: load all ### 240 chars, get 111 chars: 1145.765 ms, avg: 10.32221 ms ``` ```docs ### method separated ### 0 get 18 chars: 201.403 ms, avg: 11.18906 ms ... ### 9 get 22 chars: 169.531 ms, avg: 7.705955 ms ### 240 chars, get 197 chars: 1760.472 ms, avg: 9.978033 ms ``` 但是同时测试两种方法的结果却是这样的 ```docs ### method: load all ### 240 chars, get 111 chars: 1145.765 ms, avg: 10.32221 ms ### method separated ### 0 get 18 chars: 110.468 ms, avg: 6.137111 ms ... ### 9 get 22 chars: 125.306 ms, avg: 5.695727 ms ### 240 chars, get 197 chars: 1170.494 ms, avg: 6.635651 ms ``` 调换顺序的结果也是类似 ```docs ### method separated ### 0 get 18 chars: 201.403 ms, avg: 11.18906 ms ... ### 9 get 22 chars: 169.531 ms, avg: 7.705955 ms ### 240 chars, get 197 chars: 1760.472 ms, avg: 9.978033 ms ### method: load all ### 240 chars, get 111 chars: 565.726 ms, avg: 5.096631 ms ``` 结论就是,不管是一次性还是分段,只要提前获取了所有字符的字模数据,下次再获取的时候速度就会快很多,不过显然这种提速并没有实际意义 ### 合作交流 * 联系邮箱: * QQ 交流群: * 走线物联:163271910 * 扇贝物联:31324057

走线物联扇贝物联