diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..3ea8f49744d6e345d11fedcfb4258b2064889405
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,117 @@
+.vscode/
+
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+.hypothesis/
+.pytest_cache/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+.python-version
+
+# celery beat schedule file
+celerybeat-schedule
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000000000000000000000000000000000000..b5d74a7689ee7d2e515d0cb5a03f2413ea553652
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,7 @@
+[submodule "research"]
+ path = research
+ url = https://gitee.com/walkline/oled-research
+[submodule "client"]
+ path = client
+ url = https://gitee.com/walkline/fontmaker-client.git
+ branch = binary
diff --git a/.mpyproject.json b/.mpyproject.json
new file mode 100644
index 0000000000000000000000000000000000000000..7d78cb92f3b83e3fd561f080ed3f1e9f3178a8cb
--- /dev/null
+++ b/.mpyproject.json
@@ -0,0 +1,5 @@
+//This is an automatically generated configuration file.
+//Please do not modify or delete it!!!
+{
+ "projectName":"MicroPython-New-FontLib"
+}
\ No newline at end of file
diff --git a/README.en.md b/README.en.md
deleted file mode 100644
index 3464ee6a15d97e561c0143233c7848cbf6d2d1a3..0000000000000000000000000000000000000000
--- a/README.en.md
+++ /dev/null
@@ -1,36 +0,0 @@
-# MicroPython New FontLib
-
-#### Description
-{**When you're done, you can delete the content in this README and update the file with details for others getting started with your repository**}
-
-#### Software Architecture
-Software architecture description
-
-#### Installation
-
-1. xxxx
-2. xxxx
-3. xxxx
-
-#### Instructions
-
-1. xxxx
-2. xxxx
-3. xxxx
-
-#### Contribution
-
-1. Fork the repository
-2. Create Feat_xxx branch
-3. Commit your code
-4. Create Pull Request
-
-
-#### Gitee Feature
-
-1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md
-2. Gitee blog [blog.gitee.com](https://blog.gitee.com)
-3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore)
-4. The most valuable open source project [GVP](https://gitee.com/gvp)
-5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help)
-6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
diff --git a/README.md b/README.md
index 5494b720650d4498a071823f0c263ec9610a4a10..7991910c35a833525aca484f728647676cadd362 100644
--- a/README.md
+++ b/README.md
@@ -1,39 +1,185 @@
-# MicroPython New FontLib
+
MicroPython New FontLib
-#### 介绍
-{**以下是 Gitee 平台说明,您可以替换此简介**
-Gitee 是 OSCHINA 推出的基于 Git 的代码托管平台(同时支持 SVN)。专为开发者提供稳定、高效、安全的云端软件开发协作平台
-无论是个人、团队、或是企业,都能够用 Gitee 实现代码托管、项目管理、协作开发。企业项目请看 [https://gitee.com/enterprises](https://gitee.com/enterprises)}
+
-#### 软件架构
-软件架构说明
+### 项目介绍
+使用`MicroPython 开发板`读取自定义字库并显示
-#### 安装教程
+> 项目开发中
-1. xxxx
-2. xxxx
-3. xxxx
+### 获取完整项目
-#### 使用说明
+因为项目中使用了子模块 [FontMaker Client](https://gitee.com/walkline/fontmaker-client.git) 的`binary`分支 和 [OLED Research](https://gitee.com/walkline/oled-research.git),所以要获取完整项目代码需要如下操作
-1. xxxx
-2. xxxx
-3. xxxx
+#### 克隆方式
-#### 参与贡献
+```bash
+$ git clone --recursive https://gitee.com/walkline/micropython-new-fontlib.git
+```
-1. Fork 本仓库
-2. 新建 Feat_xxx 分支
-3. 提交代码
-4. 新建 Pull Request
+#### 下载压缩文件方式
+或者克隆时未使用`--recursive`参数的,使用如下代码更新子模块
-#### 特技
+```bash
+$ cd micropython-new-fontlib
+$ git submodule update --init --recursive
+```
-1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md
-2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com)
-3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目
-4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目
-5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
-6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
+### 如何使用和测试
+
+#### 生成字库文件
+
+直接运行
+
+```bash
+$ client/FontMaker_Cli.exe
+```
+
+会生成一个`combined.bin`文件
+
+> 目前这个命令行软件还不支持输入参数进行定制生成字库文件,运行后默认使用的参数如下:
+>
+> 字体:幼圆
+> 字号:16 像素
+> 字重:普通(不加粗、不斜体、无下划线)
+> 字符宽度:固定
+> 字符宽度:16 像素
+> 字符高度:16 像素
+> 水平偏移:0 像素
+> 垂直偏移:0 像素
+> 扫描方式:行扫描
+> 字节顺序:高位在前
+
+#### 使用电脑测试
+
+直接运行
+
+```bash
+$ python fontlib.py
+```
+
+会显示相关信息,包括:
+
+* 字库信息
+* 获取的字模数据
+* 字模打印预览
+
+完整输出内容如下
+
+```docs
+HZK Info: .//client/combined.bin
+ file size : 303520
+ font height : 16
+ data size : 32
+ characters : 8932
+
+'!' [65281, 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']
+
+'☆' [9734, 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']
+
+'⒉' [9353, 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']
+
+',' [65292, 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']
+
+'我' [25105, b'\x00\x00\x00@\x07PxH\x08D\x08D\x7f\xfe\x08D\x08D\t(\x0e0x0\x080\x08R\t\x8ax\x06']
+
+'ㄘ' [12568, 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']
+
+'■' [9632, 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' [65314, 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']
+
+'中' [20013, 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']
+
+'爱' [29233, 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']
+
+'β' [946, 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']
+
+'あ' [12354, 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' [72, b'\x00\x00\x00\x00\x00\x00\xe7\x00B\x00B\x00B\x00B\x00~\x00B\x00B\x00B\x00B\x00\xe7\x00\x00\x00\x00\x00']
+
+'华' [21326, 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']
+
+'ǚ' [474, 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' [101, 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' [108, 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' [111, 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']
+
+'⑴' [9332, b'\x00\x00\x00\x00\x08\x00\x10\x08!\x84 \x82@\x82@\x83@\x81@\x81@\x81@\x82 \x82 \x84\x13\xc4\x10\x08']
+
+................ ................ ................ ................ ................ ................ ................ ................
+................ ................ ................ ................ .........@...... ................ ................ ................
+................ ........@....... ................ ................ .....@@@.@.@.... ................ .@@@@@@@@@@@@@@@ ................
+....@........... .......@@....... .....@@@@@...... ................ .@@@@....@..@... .......@........ .@@@@@@@@@@@@@@@ ................
+....@@.......... .......@@....... ....@....@...... ................ ....@....@...@.. .......@@....... .@@@@@@@@@@@@@@@ .....@@@@@@@....
+....@........... .......@.@...... .........@...... ................ ....@....@...@.. ........@....... .@@@@@@@@@@@@@@@ .....@@...@@....
+....@........... ......@..@...... .........@...... ................ .@@@@@@@@@@@@@@. .......@........ .@@@@@@@@@@@@@@@ .....@@...@@@...
+....@........... .@@@@@@...@@@@@@ .........@...... ................ ....@....@...@.. ......@@@@@@.... .@@@@@@@@@@@@@@@ .....@@...@@....
+....@........... ..@..........@.. ........@....... ................ ....@....@...@.. ...@@@.@........ .@@@@@@@@@@@@@@@ .....@@..@@@....
+....@........... ...@........@... .......@........ ................ ....@..@..@.@... .......@........ .@@@@@@@@@@@@@@@ .....@@@@@@@....
+....@........... ....@@.....@.... .......@........ ................ ....@@@...@@.... ......@.@@@..... .@@@@@@@@@@@@@@@ .....@@....@@...
+....@........... ....@......@.... ......@......... ................ .@@@@.....@@.... ......@@.@@..... .@@@@@@@@@@@@@@@ .....@@....@@...
+................ ....@...@..@.... .....@.......... ................ ....@.....@@.... .........@...... .@@@@@@@@@@@@@@@ .....@@....@@...
+....@........... ....@.@@.@..@... ....@@......@... .@@@............ ....@....@.@..@. ........@....... .@@@@@@@@@@@@@@@ .....@@@@@@@....
+....@........... ...@.@....@@@... ....@@@@@@..@@.. ..@@............ ....@..@@...@.@. ................ .@@@@@@@@@@@@@@@ ................
+................ ...@@.......@... ................ ..@............. .@@@@........@@. ................ .@@@@@@@@@@@@@@@ ................
+
+................ ................ ................ ................ ................ ................ ................ ................
+.......@........ ................ ................ ................ ................ ................ ................ ................
+.......@........ ..@@@@@@@@@@@... ................ ......@@........ ................ ....@....@...... ................ ................
+.......@........ ...@...@...@.... .......@@@@..... ......@@........ @@@..@@@........ ....@....@..@... ................ ................
+..@@@@@@@@@@@@.. ....@..@...@.... .......@...@.... ......@.@@...... .@....@......... ...@@....@@@.... ...@.@.......... ................
+..@....@.....@.. ..@@@@@@@@@@@@.. ......@@..@@.... ...@@@@@@....... .@....@......... ..@.@...@@...... ....@........... ................
+..@....@.....@.. .@...@........@. ......@...@..... ......@......... .@....@......... ..@.@.@@.@....@. ..@@.@.......... ................
+..@....@.....@.. .@....@.......@. ......@.@@...... .....@@.@....... .@....@......... .@..@....@....@. .@....@@........ ..@@@@..........
+..@....@.....@.. ...@@@@@@@@@@... ......@..@@..... .....@@@@@@@.... .@@@@@@......... ....@....@@@@@@. .@....@@........ .@....@.........
+..@....@.....@.. .....@.......... .....@@...@..... ....@@..@..@@... .@....@......... .......@........ .@....@@........ .@@@@@@.........
+..@....@.....@.. .....@@@@@@@@... .....@....@..... ...@.@.@....@@.. .@....@......... .......@........ .@....@@........ .@..............
+..@@@@@@@@@@@@.. ....@@@.....@... .....@...@@..... ..@..@@@....@@.. .@....@......... .@@@@@@@@@@@@@@. .@....@@........ .@..............
+.......@........ ...@..@@...@.... .....@...@@..... ..@..@@.....@@.. .@....@......... .......@........ .@....@@........ .@....@.........
+.......@........ ..@.....@@@..... .....@@@@@...... ..@.@@@@....@... @@@..@@@........ .......@........ ..@..@@@........ ..@@@@..........
+.......@........ .......@@@@..... ....@@.......... ..@@@......@.... ................ .......@........ ...@@........... ................
+.......@........ ...@@@@....@@@@. ....@........... .......@@@@..... ................ .......@........ ................ ................
+
+................ ................ ................
+................ ................ ................
+................ ................ ....@...........
+.@@@............ ................ ...@........@...
+...@............ ................ ..@....@@....@..
+...@............ ................ ..@.....@.....@.
+...@............ ................ .@......@.....@.
+...@............ ..@@@@.......... .@......@.....@@
+...@............ .@....@......... .@......@......@
+...@............ .@....@......... .@......@......@
+...@............ .@....@......... .@......@......@
+...@............ .@....@......... .@......@.....@.
+...@............ .@....@......... ..@.....@.....@.
+.@@@@@.......... ..@@@@.......... ..@.....@....@..
+................ ................ ...@..@@@@...@..
+................ ................ ...@........@...
+```
+
+#### 使用开发板测试
+
+使用相关软件上传如下代码到开发板,运行`fontlib.py`即可
+
+```bash
+drivers/ssd1306.py
+client/combined.bin
+fontlib.py
+```
+
+### 合作交流
+
+* 联系邮箱:
+* QQ 交流群:
+ * 走线物联:163271910
+ * 扇贝物联:31324057
+
+

diff --git a/client b/client
new file mode 160000
index 0000000000000000000000000000000000000000..f41014c6c12440894152b05dac4c65843238d2ed
--- /dev/null
+++ b/client
@@ -0,0 +1 @@
+Subproject commit f41014c6c12440894152b05dac4c65843238d2ed
diff --git a/drivers/ssd1306.py b/drivers/ssd1306.py
new file mode 100644
index 0000000000000000000000000000000000000000..61aba408952a8d77678ef71f9819d76d3f13036c
--- /dev/null
+++ b/drivers/ssd1306.py
@@ -0,0 +1,163 @@
+# MicroPython SSD1306 OLED driver, I2C and SPI interfaces
+
+from micropython import const
+import framebuf
+
+
+# register definitions
+SET_CONTRAST = const(0x81)
+SET_ENTIRE_ON = const(0xA4)
+SET_NORM_INV = const(0xA6)
+SET_DISP = const(0xAE)
+SET_MEM_ADDR = const(0x20)
+SET_COL_ADDR = const(0x21)
+SET_PAGE_ADDR = const(0x22)
+SET_DISP_START_LINE = const(0x40)
+SET_SEG_REMAP = const(0xA0)
+SET_MUX_RATIO = const(0xA8)
+SET_IREF_SELECT = const(0xAD)
+SET_COM_OUT_DIR = const(0xC0)
+SET_DISP_OFFSET = const(0xD3)
+SET_COM_PIN_CFG = const(0xDA)
+SET_DISP_CLK_DIV = const(0xD5)
+SET_PRECHARGE = const(0xD9)
+SET_VCOM_DESEL = const(0xDB)
+SET_CHARGE_PUMP = const(0x8D)
+
+# Subclassing FrameBuffer provides support for graphics primitives
+# http://docs.micropython.org/en/latest/pyboard/library/framebuf.html
+class SSD1306(framebuf.FrameBuffer):
+ def __init__(self, width, height, external_vcc):
+ self.width = width
+ self.height = height
+ self.external_vcc = external_vcc
+ self.pages = self.height // 8
+ self.buffer = bytearray(self.pages * self.width)
+ super().__init__(self.buffer, self.width, self.height, framebuf.MONO_VLSB)
+ self.init_display()
+
+ def init_display(self):
+ for cmd in (
+ SET_DISP, # display off
+ # address setting
+ SET_MEM_ADDR,
+ 0x00, # horizontal
+ # resolution and layout
+ SET_DISP_START_LINE, # start at line 0
+ SET_SEG_REMAP | 0x01, # column addr 127 mapped to SEG0
+ SET_MUX_RATIO,
+ self.height - 1,
+ SET_COM_OUT_DIR | 0x08, # scan from COM[N] to COM0
+ SET_DISP_OFFSET,
+ 0x00,
+ SET_COM_PIN_CFG,
+ 0x02 if self.width > 2 * self.height else 0x12,
+ # timing and driving scheme
+ SET_DISP_CLK_DIV,
+ 0x80,
+ SET_PRECHARGE,
+ 0x22 if self.external_vcc else 0xF1,
+ SET_VCOM_DESEL,
+ 0x30, # 0.83*Vcc
+ # display
+ SET_CONTRAST,
+ 0xFF, # maximum
+ SET_ENTIRE_ON, # output follows RAM contents
+ SET_NORM_INV, # not inverted
+ SET_IREF_SELECT,
+ 0x30, # enable internal IREF during display on
+ # charge pump
+ SET_CHARGE_PUMP,
+ 0x10 if self.external_vcc else 0x14,
+ SET_DISP | 0x01, # display on
+ ): # on
+ self.write_cmd(cmd)
+ self.fill(0)
+ self.show()
+
+ def poweroff(self):
+ self.write_cmd(SET_DISP)
+
+ def poweron(self):
+ self.write_cmd(SET_DISP | 0x01)
+
+ def contrast(self, contrast):
+ self.write_cmd(SET_CONTRAST)
+ self.write_cmd(contrast)
+
+ def invert(self, invert):
+ self.write_cmd(SET_NORM_INV | (invert & 1))
+
+ def rotate(self, rotate):
+ self.write_cmd(SET_COM_OUT_DIR | ((rotate & 1) << 3))
+ self.write_cmd(SET_SEG_REMAP | (rotate & 1))
+
+ def show(self):
+ x0 = 0
+ x1 = self.width - 1
+ if self.width != 128:
+ # narrow displays use centred columns
+ col_offset = (128 - self.width) // 2
+ x0 += col_offset
+ x1 += col_offset
+ self.write_cmd(SET_COL_ADDR)
+ self.write_cmd(x0)
+ self.write_cmd(x1)
+ self.write_cmd(SET_PAGE_ADDR)
+ self.write_cmd(0)
+ self.write_cmd(self.pages - 1)
+ self.write_data(self.buffer)
+
+
+class SSD1306_I2C(SSD1306):
+ def __init__(self, width, height, i2c, addr=0x3C, external_vcc=False):
+ self.i2c = i2c
+ self.addr = addr
+ self.temp = bytearray(2)
+ self.write_list = [b"\x40", None] # Co=0, D/C#=1
+ super().__init__(width, height, external_vcc)
+
+ def write_cmd(self, cmd):
+ self.temp[0] = 0x80 # Co=1, D/C#=0
+ self.temp[1] = cmd
+ self.i2c.writeto(self.addr, self.temp)
+
+ def write_data(self, buf):
+ self.write_list[1] = buf
+ self.i2c.writevto(self.addr, self.write_list)
+
+
+class SSD1306_SPI(SSD1306):
+ def __init__(self, width, height, spi, dc, res, cs, external_vcc=False):
+ self.rate = 10 * 1024 * 1024
+ dc.init(dc.OUT, value=0)
+ res.init(res.OUT, value=0)
+ cs.init(cs.OUT, value=1)
+ self.spi = spi
+ self.dc = dc
+ self.res = res
+ self.cs = cs
+ import time
+
+ self.res(1)
+ time.sleep_ms(1)
+ self.res(0)
+ time.sleep_ms(10)
+ self.res(1)
+ super().__init__(width, height, external_vcc)
+
+ def write_cmd(self, cmd):
+ self.spi.init(baudrate=self.rate, polarity=0, phase=0)
+ self.cs(1)
+ self.dc(0)
+ self.cs(0)
+ self.spi.write(bytearray([cmd]))
+ self.cs(1)
+
+ def write_data(self, buf):
+ self.spi.init(baudrate=self.rate, polarity=0, phase=0)
+ self.cs(1)
+ self.dc(1)
+ self.cs(0)
+ self.spi.write(buf)
+ self.cs(1)
diff --git a/fontlib.py b/fontlib.py
new file mode 100644
index 0000000000000000000000000000000000000000..1620d561ccb1d42c83a031b16bcf3399f728a34c
--- /dev/null
+++ b/fontlib.py
@@ -0,0 +1,209 @@
+"""
+The MIT License (MIT)
+Copyright © 2021 Walkline Wang (https://walkline.wang)
+Gitee: https://gitee.com/walkline/micropython-new-fontlib
+"""
+import os
+import gc
+import struct
+
+
+try:
+ import framebuf
+ MICROPYTHON = True
+except ImportError:
+ MICROPYTHON = False
+
+CURRENT_DIR = os.getcwd() if MICROPYTHON else os.path.dirname(__file__) + '/'
+FONT_DIR = '/client/'
+HZK_FILE = 'combined.bin'
+
+
+class FontLibHeaderException(Exception):
+ pass
+
+class FontLibException(Exception):
+ pass
+
+
+'''
+Header Data Sample:
+ b'FMUX\xa4\xa1\x04\x00\x10\xe4"\x01$E\x00\x00$Q\x00\x00\\\x00A\x00' # little-endian
+
+ [4] b'FMUX' - identify
+ [4] b'\xa4\xa1\x04\x00' - file length
+ [1] b'\x10' - font height
+ [2] b'\xe4"' - character counts
+ [1] b'\x01' - has index table
+ [4] b'$E\x00\x00' - ascii start address
+ [4] b'$Q\x00\x00' - gb2312 start address
+ [4] b'\\\x00A\x00' - reserved
+'''
+class FontLibHeader(object):
+ LENGTH = 24
+
+ def __init__(self, header_data):
+ if len(header_data) != FontLibHeader.LENGTH:
+ raise FontLibHeaderException('Invalid header length')
+
+ self.identify,\
+ self.file_size,\
+ self.font_height,\
+ self.characters,\
+ self.has_index_table,\
+ self.ascii_start,\
+ self.gb2312_start,\
+ _ = struct.unpack('<4sIBHBII4s', header_data)
+
+ if self.identify not in (b'FMUX',):
+ raise FontLibHeaderException('Invalid font file')
+
+ self.data_size = ((self.font_height - 1) // 8 + 1) * self.font_height
+
+ if self.has_index_table:
+ self.index_table_address = FontLibHeader.LENGTH
+ else:
+ self.index_table_address = 0
+
+
+class FontLib(object):
+ def __init__(self, font_filename):
+ self.__font_filename = font_filename
+ self.__header = None
+
+ with open(self.__font_filename, 'rb') as font_file:
+ self.__header = FontLibHeader(font_file.read(FontLibHeader.LENGTH))
+ self.__placeholder_buffer = self.__get_character_unicode_buffer(font_file, {ord('?')})[0][1]
+
+ gc.collect()
+
+ def __is_ascii(self, char_code):
+ return 0x20 <= char_code <= 0x7f
+
+ def __is_gb2312(self, char_code):
+ return 0x80 <= char_code <= 0xffef
+
+ def __get_character_unicode_buffer(self, font_file, unicode_set):
+ buffer_list = []
+
+ for unicode in unicode_set:
+ if self.__is_ascii(unicode):
+ char_offset = self.__header.ascii_start + (unicode - 0x20) * self.__header.data_size
+ elif self.__is_gb2312(unicode):
+ gb2312_index = struct.pack('