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('