搜索
bottom↓
回复: 10

写了个自动制作小字库的程序

[复制链接]

出0入362汤圆

发表于 2018-2-16 00:45:22 | 显示全部楼层 |阅读模式
本帖最后由 tomzbj 于 2018-2-16 15:38 编辑

需要在点阵LCD之类场合使用少量汉字时一般是做成小字库.
以前用纯c做过, 这次用python3重新实现了一遍, gb2312和utf-8编码的源文件都可以识别.

  1. import sys
  2. import chardet

  3. target = "AVR"

  4. def GetContent(path): # 读取文件内容
  5.     with open(path, mode="rb") as f:
  6.         return f.read()
  7. # main
  8. if __name__ == "__main__":

  9.     if target == "AVR":
  10.         suffix = "PROGMEM"   # 如果是AVR, 需要用PROGMEM关键字来把字库放进flash. stm32因为是统一寻址, 只要加上const就足够了.
  11.     else:
  12.         suffix = ""

  13.     if len(sys.argv) < 4:
  14.         exit()
  15.    
  16.     ziku = GetContent(sys.argv[1])  # 把全部字库文件读到内存
  17.     source = GetContent(sys.argv[2])  # 含有汉字字符串的源文件
  18.     enc = chardet.detect(source)["encoding"]   # 判断源文件编码
  19.     if enc == "UTF-8-SIG":            # 有些utf-8编码文件前面有3字节的签名, 要去掉
  20.         source = source[3:]
  21.     enc = chardet.detect(source)["encoding"].upper()
  22.     source = source.decode(encoding=enc)    # 至此获得了unicode的输入文件

  23.     cstr = ""
  24.     for c in source:
  25.         if ord(c) > 127:    # 把非ASCII的都挑出来, 假定都是需要的汉字
  26.             cstr += c
  27.     cstr = "".join(sorted(set(cstr)))       # 去掉重复的汉字, 再排个序(否则顺序随机)

  28.     cstr_gb2312 = cstr.encode(encoding="gb2312")
  29.     cstr_utf8 = cstr.encode(encoding="utf-8")        # 得到了gb2312和utf-8编码的源文件
  30.     ofss = []
  31.     icodes_gb2312 = []
  32.     icodes_utf8 = []
  33.     while len(cstr_gb2312) > 0:
  34.         icodes_gb2312.append(int(cstr_gb2312[0]) * 256 + int(cstr_gb2312[1]))
  35.         icodes_utf8.append(int(cstr_utf8[0]) * 65536 + int(cstr_utf8[1]) * 256 + int(cstr_utf8[2]))
  36.         ofss.append(((int(cstr_gb2312[0]) - 0xa1) * 94 + (int(cstr_gb2312[1]) - 0xa1)) * 32)  # 根据gb2312编码计算出在HZK16V文件中的偏移量, 读出字模
  37.         cstr_gb2312 = cstr_gb2312[2:]
  38.         cstr_utf8 = cstr_utf8[3:]

  39.     if target == "AVR":
  40.         output = "#include <avr/pgmspace.h>\n\n"           # 如果是AVR需要加上这个头文件, 否则不认得PROGMEM关键字
  41.     else:
  42.         output = ""

  43.     output += "const int cfont_num = %d;\n" % len(cstr)
  44.     output += "const char cfont_source_encoding[] = "" + enc + "";\n"
  45.     output += "const unsigned short cfont_icodes_gb2312[] %s = {\n        " % suffix
  46.     n = 0
  47.     for icode in icodes_gb2312:
  48.         output += "0x%04x, " % icode
  49.         n += 8
  50.         if n > 66:
  51.             n = 0
  52.             output += "\n        "  # 处理折行
  53.     if n != 0:
  54.         output = output[:-2]
  55.     else:
  56.         output = output[:-11]

  57.     output += "\n};\n"

  58.     output += "const unsigned long cfont_icodes_utf8[] %s = {\n        " % suffix
  59.     n = 0
  60.     for icode in icodes_utf8:
  61.         output += "0x%06x, " % icode
  62.         n += 10
  63.         if n > 62:
  64.             n = 0
  65.             output += "\n        "
  66.     if n != 0:
  67.         output = output[:-2]
  68.     else:
  69.         output = output[:-11]
  70.     output += "\n};\n"

  71.     output += "const unsigned char cfont_mask[] %s = {\n        " % suffix
  72.     n = 0
  73.     for ofs in ofss:
  74.         mask = ziku[ofs:ofs + 32]
  75.         for i in mask:
  76.             output += "0x%02x, " % i
  77.             n += 6
  78.             if n > 66:
  79.                 n = 0
  80.                 output += "\n        "
  81.     if n != 0:
  82.         output = output[:-2]
  83.     else:
  84.         output = output[:-11]
  85.     output += "\n};"

  86.     try:
  87.         f = open(sys.argv[3], "w")  # 写入目标文件
  88.     except:
  89.         print("err 3")
  90.         exit()
  91.     f.write(output)
  92.     f.close()
复制代码


使用方法: font_gen.py HZK16V cstring.c cfont.c
其中HZK16V是16点阵/纵向取模的字库文件, 见附件:
把所有需要用到的汉字字符串常量放进cstring.c, 比如:
  1. unsigned char STRING_1[] = "浔阳江头夜送客";
  2. unsigned char STRING_2[] = "枫叶荻花秋瑟瑟";
复制代码

cfont.c是输出文件名, 上面例子的效果:
  1. #include <avr/pgmspace.h>

  2. const int cfont_num = 13;
  3. const char cfont_source_encoding[] = "UTF-8";
  4. const unsigned short cfont_icodes_gb2312[] PROGMEM = {
  5.         0xd2b6, 0xd2b9, 0xcdb7, 0xbfcd, 0xb7e3, 0xbdad, 0xe4b1, 0xc9aa, 0xc7ef,
  6.         0xbba8, 0xddb6, 0xcbcd, 0xd1f4
  7. };
  8. const unsigned long cfont_icodes_utf8[] PROGMEM = {
  9.         0xe58fb6, 0xe5a49c, 0xe5a4b4, 0xe5aea2, 0xe69eab, 0xe6b19f, 0xe6b594,
  10.         0xe7919f, 0xe7a78b, 0xe88ab1, 0xe88dbb, 0xe98081, 0xe998b3
  11. };
  12. const unsigned char cfont_mask[] PROGMEM = {
  13.         0x00, 0x00, 0xfc, 0x1f, 0x04, 0x08, 0x04, 0x08, 0xfe, 0x1f, 0x44, 0x00,
  14.         0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0xff, 0xff, 0x40, 0x00, 0x40, 0x00,
  15.         0x40, 0x00, 0x60, 0x00, 0x40, 0x00, 0x00, 0x00, 0x04, 0x02, 0x04, 0x01,
  16.         0x84, 0x00, 0xe4, 0xff, 0x1c, 0x82, 0x04, 0x81, 0x85, 0x41, 0x46, 0x22,
  17.         0xbc, 0x14, 0x24, 0x09, 0x24, 0x14, 0x24, 0x23, 0xe4, 0x40, 0x06, 0xc0,
  18.         0x04, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x81, 0x10, 0x81, 0x20, 0x41,
  19.         0x64, 0x41, 0x08, 0x21, 0x18, 0x11, 0x00, 0x0d, 0xff, 0x03, 0x00, 0x09,
  20.         0x00, 0x09, 0x00, 0x11, 0x00, 0x61, 0x80, 0xc1, 0x00, 0x01, 0x00, 0x00,
  21.         0x10, 0x04, 0x0c, 0x04, 0x84, 0x02, 0x44, 0x02, 0x3c, 0xfd, 0x54, 0x45,
  22.         0x95, 0x44, 0x96, 0x44, 0x94, 0x44, 0x54, 0x45, 0x34, 0xfd, 0x14, 0x02,
  23.         0x04, 0x02, 0x14, 0x06, 0x0c, 0x02, 0x00, 0x00, 0x10, 0x04, 0x10, 0x03,
  24.         0xd0, 0x00, 0xff, 0xff, 0x50, 0x80, 0x90, 0x60, 0xfc, 0x1f, 0x44, 0x08,
  25.         0x84, 0x04, 0x04, 0x03, 0x84, 0x04, 0x44, 0x08, 0xfe, 0x7f, 0x04, 0x80,
  26.         0x00, 0xe0, 0x00, 0x00, 0x10, 0x04, 0x21, 0x04, 0x62, 0xfe, 0x06, 0x01,
  27.         0x80, 0x20, 0x04, 0x20, 0x04, 0x20, 0x04, 0x20, 0x04, 0x20, 0xfc, 0x3f,
  28.         0x04, 0x20, 0x04, 0x20, 0x06, 0x20, 0x04, 0x30, 0x00, 0x20, 0x00, 0x00,
  29.         0x10, 0x04, 0x62, 0x04, 0x04, 0xfe, 0x8c, 0x01, 0x60, 0x02, 0x02, 0x02,
  30.         0x92, 0x06, 0x92, 0x1a, 0x92, 0x02, 0x92, 0x42, 0x92, 0x82, 0x92, 0x7f,
  31.         0xff, 0x02, 0x02, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x82, 0x98,
  32.         0x92, 0x80, 0x92, 0x7c, 0xfe, 0xc0, 0x93, 0xa0, 0x92, 0xa1, 0x00, 0x96,
  33.         0x92, 0x88, 0x92, 0x84, 0xfe, 0x82, 0x92, 0x80, 0xd3, 0xe0, 0x82, 0x08,
  34.         0x00, 0x30, 0x00, 0x00, 0x24, 0x08, 0x24, 0x06, 0xa4, 0x01, 0xfe, 0xff,
  35.         0x23, 0x81, 0x22, 0x42, 0x80, 0x20, 0x70, 0x18, 0x00, 0x06, 0xff, 0x01,
  36.         0x00, 0x06, 0x40, 0x08, 0x20, 0x30, 0x30, 0x60, 0x00, 0x20, 0x00, 0x00,
  37.         0x04, 0x04, 0x04, 0x02, 0x04, 0x01, 0xc4, 0xff, 0x3f, 0x00, 0x04, 0x20,
  38.         0x04, 0x10, 0x04, 0x08, 0xe4, 0x3f, 0x04, 0x42, 0x1f, 0x41, 0x84, 0x40,
  39.         0xc4, 0x40, 0x06, 0x40, 0x04, 0x70, 0x00, 0x00, 0x04, 0x10, 0x04, 0x11,
  40.         0x14, 0x49, 0xa4, 0x84, 0x44, 0x42, 0xaf, 0x3f, 0x04, 0x80, 0x04, 0x42,
  41.         0xc4, 0x31, 0x0f, 0x0c, 0xf4, 0x03, 0x04, 0x1c, 0x04, 0x61, 0xc6, 0xc0,
  42.         0x04, 0x40, 0x00, 0x00, 0x40, 0x00, 0x42, 0x40, 0x44, 0x20, 0xcc, 0x1f,
  43.         0x80, 0x20, 0x88, 0x40, 0x89, 0xa0, 0x8a, 0x90, 0x8c, 0x8c, 0xf8, 0x83,
  44.         0x8c, 0x88, 0x8a, 0x90, 0x89, 0xb0, 0xc8, 0xc0, 0x80, 0x40, 0x00, 0x00,
  45.         0x00, 0x00, 0xfe, 0xff, 0x02, 0x08, 0x22, 0x10, 0xda, 0x08, 0x06, 0x07,
  46.         0x00, 0x00, 0xfe, 0xff, 0x82, 0x40, 0x82, 0x40, 0x82, 0x40, 0x82, 0x40,
  47.         0x82, 0x40, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00
  48. };
复制代码


在程序中调用时先判断cfont_source_encoding是"GB2312"还是"UTF-8". 如果是前者, 则依次在cfont_icodes_gb2312[]查找需要显示的汉字的内码, 找到序号号从cfont_mask[]里读取相应的字模, 之后源字符串指针+=2. 如果是"UTF-8"则在cfont_icodes_utf8[]里查找, 之后源字符串指针要+=3.

把这个py文件加入到工程中, 编译前先执行, 这样就实现了修改cstring.c里的汉字字符串常量之后自动更新cfont.c里的字库.
每个汉字需要32+2+4=38字节的存储空间. 如果不需要自动判断源程序的编码, 还可以去掉cfont_icodes_gb2312和cfont_icodes_utf8其中之一, 可以再节约几个字节.

这里没有把内码和字模按内码排序,  查找时需要逐个比较, 效率比较低. 如果需要显示的汉字稍微多一些, 可以把内码和字模按其中一种内码排序, 读取时可以用二分查找, 速度就快多了. 不过不知道GB2312和UTF-8的汉字排列顺序是不是一致, 如果不一致的话就不容易实现自动兼容两者了.

如果需要显示的汉字很多... 还是外挂一片spi flash把整个字库存进去比较好. ps. GT23/GT30系列的字库IC, 其实就是一片GD25Q16.

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

阿莫论坛20周年了!感谢大家的支持与爱护!!

月入3000的是反美的。收入3万是亲美的。收入30万是移民美国的。收入300万是取得绿卡后回国,教唆那些3000来反美的!

出0入0汤圆

发表于 2018-2-16 01:08:43 | 显示全部楼层
新年第一个技术帖

出0入0汤圆

发表于 2018-2-16 01:25:35 | 显示全部楼层
向你致敬新年快乐,祝大家新春快乐!财运旺旺!

出0入42汤圆

发表于 2018-2-16 09:18:45 来自手机 | 显示全部楼层
新年快乐,祝大家万事如意,财运连连!

出0入0汤圆

发表于 2018-2-16 10:38:21 来自手机 | 显示全部楼层
。。。凌晨写帖

出0入0汤圆

发表于 2018-2-16 11:22:28 | 显示全部楼层
新年快乐,我也干过类似的事情

出0入0汤圆

发表于 2018-2-16 11:38:44 | 显示全部楼层
为新年都在做技术工作的点个赞,我今天闲时也在看PDF

出0入0汤圆

发表于 2018-2-16 11:56:17 来自手机 | 显示全部楼层
佩服楼主

出0入0汤圆

发表于 2018-2-16 18:44:36 来自手机 | 显示全部楼层
新年快乐!新帖子。

出0入0汤圆

发表于 2018-2-16 18:55:05 | 显示全部楼层
新年快乐!取模软件类的吧

出0入0汤圆

发表于 2018-2-18 18:10:48 来自手机 | 显示全部楼层
感谢楼主开源
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-26 06:50

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表