找回密码
 立即注册
查看: 770|回复: 7

汉字Unicode编码介绍与点阵字库的显示2

[复制链接]
  • TA的每日心情
    开心
    6 小时前
  • 签到天数: 130 天

    [LV.7]常住居民III

    13

    主题

    52

    回帖

    1358

    积分

    金牌会员

    积分
    1358
    发表于 2023-8-18 10:29:24 | 显示全部楼层 |阅读模式
    本帖最后由 飞哥 于 2023-8-25 09:01 编辑

        (接上一贴)

        [6. Unicode、BMP、UCS和UTF]
        Unicode学名是"Universal Multiple-Octet Coded Character Set"(全球通用多八位字符集),简称为UCS, 又称统一码、万国码。目标是容纳全世界所有语言文字。UCS分为:UCS-2和UCS-4。UCS-2就是用两个字节编码的字符集,UCS-4就是用4个字节(实际上只用了31位,最高位必须为0)编码的字符集。UCS-4根据最高位为0的最高字节可分成2^7=128个组(group),但目前只规划到了17个组,其余均保留未用。每组再根据次高字节分为256个平面(plane)。每个平面根据第3个字节分为256行(rows),每行包含256个单元(cells)。当然同一行的单元只是最后一个字节不同,其余都相同。
        第0组的第0平面被称作基本多文种平面Basic Multilingual Plane(BMP),或者说UCS-4高两个字节均为0的码位被称作BMP。其它平面为辅助平面(或称为增补平面)。将UCS-4的BMP去掉前面的两个零字节就得到了UCS-2。若在UCS-2的两个字节前加上两个零字节,就得到了UCS-4的BMP。UCS-2编码范围不包括保留区(UTF16代理区)0xD800~0xDFFF。在Unicode3.0还只支持UCS-2,之后版本就开始有少量生僻字符被分配在BMP之外,比如5.0版(见附件Unicode5.0编码表)。Unicode 5.0,已定义的码位已有238605个,而且Unicode规范还在不断更新增加中。Unicode 11版已包含少量全彩图片的绘文字表情(Emoji),同一个Emoji编码也能以黑白字符形式显示。2022年的Unicode 15.0已收录97046字,从基本区一直到扩展H区。

            表1 Unicode 平面编码范围
    平面        编码范围                        中文名称                                英文名称(简称)
    0号        U+0000~U+FFFF                基本多文种平面                        Basic Multilingual Plane(BMP)
    1号        U+10000~U+1FFFF                多文种补充平面                        Supplementary Multilingual Plane(SMP)
    2号        U+20000~U+2FFFF                表意文字补充平面                        Supplementary Ideographic Plane(SIP)
    3号        U+30000~U+3FFFF                表意文字第三平面(未正式使用)        Tertiary Ideographic Plane(TIP)
    4~13号        U+40000~U+DFFFF                (尚未使用)                        -
    14号        U+E0000~U+EFFFF                特别用途补充平面                        Supplementary Special-purpose Plane(SSP)
    15号        U+F0000~U+FFFFF                保留作为私人使用区(A区)                Private Use Area-A (PUA-A)
    16号        U+100000~U+10FFFF        保留作为私人使用区(B区)                Private Use Area-B (PUA-B)

        字符是文字和符号的总称,包括文字、数字、字母、标点符号、图形符号等。表意文字是指将意思置换成形状来表示的文字的统称。也就是说,表意文字只要看到一个文字的形状就能理解想要表达的意思。表意文字有很多,比如汉字、彝文、东巴文等。在Unicode标准中,码点与字符并不一定总是一一对应的,在Unicode标准中,一个字符有可能有多个码点,比如U+51C9与U+F977都是同一个字符“凉”,也有可能由多个码点来表示一个字符。常见的是组合字符,人们会认为是一个字符,由多个码点组合而成的组合字符在Unicode里称为“用户感知字符”。Unicode字符集包含抽象字符表中的字符、与字符对应的码点、以及没有与字符对应的码点。
        对于可以用ASCII表示的字符(0x80~0xFF)使用Unicode并不高效,因为Unicode比ASCII占用大一倍的空间,而对ASCII来说高字节的0对它没有用处。为了解决这个问题,就出现了一些中间格式的字符集,即UTF(Unicode Transformation Format)通用字符集转换格式。它们是Unicode编码的具体实现编码方案,以方便传输。UTF主要有:UTF-7(已淘汰)、UTF-7.5(已淘汰)、UTF-8(与ISO-8859-1完全兼容)、UTF-16、UTF-32(极少用)。

            表2 Unicode 编码的层次结构(Unicode编码模型)                        
    层次        层次名称                作用                                        最终结果               
    第1层        抽象字符表ACR        确定支持哪些字符                                -        
    第2层        编号字符集CSS        将字符编号                                Unicode字符集
    第3层        字符编码方式CEF        将字符逻辑编码(已是二进制)                码元序列
    第4层        字符编码方案CES        将码元序列物理编码(主要确定字节序)        字节序列
    第5层        传输编码语法TES        进一步编码以适于传输                        -

        详细的层次介绍可参考这里:
    https://blog.csdn.net/hyongilfmmm/article/details/112037596

        6.1 UTF-8
        UTF-8(8-bit Unicode Transformation Format)是一种针对Unicode的可变长度字符编码,又称万国码。于1992年创建,现在已经标准化为RFC 3629。UTF-8用1到6个字节编码Unicode字符。UTF-8主要用在网页上传输可以统一页面显示中文简体繁体及其它语言(如英文,日文,韩文)。
        如果Unicode字符只由小于0x7F的ASCII码字节表示,则编码成UTF-8只需要1个字节。如果Unicode字符由2个字节表示(UCS-2),则编码成UTF-8就需要2到3个字节。对于U+4E00到U+9FFF范围的2字节Unicode基本汉字,编码成UTF-8需要3个字节。而对于辅助平面的3个字节Unicode汉字,编码成UTF-8需要4个或者6个字节(见下面的标准和修正的UTF-8)。这就造成了UTF-8编码的中文网页、源代码文件体积比较臃肿,UTF-8存储中文比UTF-16要多出50%以上,故不推荐要大量显示中文的程序使用(而是推荐UTF16)。
        UTF-8编码规则:如果只有一个字节则其最高二进制位为0;如果是多字节,其第一个字节从最高位开始,连续的二进制位值为1的个数决定了其编码的字节数,其余各字节均以二进制10开头。实际表示ASCII字符的Unicode字符,将会编码成1个字节,并且UTF-8表示与ASCII字符表示是一样的。(不包括NULL字符)所有其它的Unicode字符转化成UTF-8将需要至少2个字节。每个字节由一个换码序列开始。第一个字节由唯一的换码序列,由n位连续的1加一位0组成, 首字节连续的1的个数表示字符编码所需的字节数。
        Unicode转换为UTF-8时,可以将Unicode二进制从低位往高位取出二进制数字,每次取6位,前面按格式填补,不足8位用0填补。注:Unicode转换为UTF-8需要的字节数可以根据这个规则计算:如果Unicode小于0x80(ASCII字符),则转换后为1个字节。否则转换后的字节数为Unicode二进制位数减1再除以5。
        UTF-8编码可以通过屏蔽位和移位操作快速读写。字符串比较时strcmp()和wcscmp()的返回结果相同,因此使排序变得更加容易。字节FF和FE在UTF-8编码中永远不会出现,因此他们可以用来表明UTF-16或UTF-32文本的BOM。UTF-8 是字节顺序无关的。它的字节顺序在所有系统中都是一样的,都是先从第1个字节开始传输与存储,因此它实际上并不需要BOM。
        UTF-8的缺点也显而易见,我们无法从Unicode字符数判断出UTF-8文本的字节数,因为UTF-8是一种变长编码。它需要用2个字节编码那些扩展ASCII字符集只需1个字节的字符。UTF-8在它的表示中使用值100xxxxx的几率超过50%,而早期的一些ISO 2022、4873、6429、8859系统,会把它错认为是C1控制码。因此产生了UTF-7.5编码。
        标准UTF-8和修正的UTF-8有两点不同:
        修正的UTF-8中,null字符编码成2个字节(0b1100000010000000)而不是标准的1个字节(0b00000000),这样可以保证编码后的字符串中不会嵌入null字符。因此如果在类C语言中处理字符串,文本不会在第一个null字符时截断(C字符串以'\0'结尾)。在标准UTF-8编码中,超出基本多语言范围(BMP-Basic Multilingual Plane)的字符被编码为4字节格式,但是在修正的UTF-8编码中,他们由UTF16的代理编码对(surrogatepairs)表示,然后这些代理编码对在序列中分别重新编码。结果标准UTF-8编码中需要4个字节的字符,在修正后的UTF-8编码中将需要6个字节。

        表3 Unicode与UTF-8转换表
       |  Unicode符号范围      |  标准UTF-8编码方式  
    n |  (十六进制)           | (二进制)  
    ---+-----------------------+------------------------------------------------------  
    1 | 0000 0000 - 0000 007F |                                              0xxxxxxx  
    2 | 0000 0080 - 0000 07FF |                                     110xxxxx 10xxxxxx  
    3 | 0000 0800 - 0000 FFFF |                            1110xxxx 10xxxxxx 10xxxxxx  
    4 | 0001 0000 - 0010 FFFF |                   11110xxx 10xxxxxx 10xxxxxx 10xxxxxx  
    5 | 0020 0000 - 03FF FFFF |          111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx  
    6 | 0400 0000 - 7FFF FFFF | 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx  
    ---+-----------------------+------------------------------------------------------

        6.2 UTF-16
        UTF-16是Unicode字符编码五层次模型的第三层:字符编码表(Character Encoding Form,也称为 "storage format")的一种实现方式。即把Unicode字符集的抽象码位映射为16位长的整数(即码元)的序列,用于数据存储或传递。Unicode字符的码位,需要1个或者2个16位长的码元来表示,因此这是一个变长表示。UTF-16比起UTF-8,好处在于大部分字符都以固定长度的字节 (2字节) 储存,但UTF-16却无法兼容于ASCII编码。
        在UTF-16文件的开头,都会放置一个U+FEFF字符作为Byte Order Mark(简称BOM。UTF-16LE以FF FE代表,UTF-16BE以FE FF代表),以显示这个文字档案是以UTF-16编码,其中U+FEFF字符在Unicode中代表的意义是ZERO WIDTH NO-BREAK SPACE,顾名思义,它是个没有宽度也没有断字的空白。
        UTF-16可看成是UCS-2的父集。现在若有软件声称自己支持UCS-2编码,那其实是暗指它不能支持在UTF-16中超过2bytes的子集。对于小于0x10000的UCS码,UTF-16编码就等于UCS-2码。
        (1) 从U+D800到U+DFFF的码位(代理区)
        在基本多语言平面内,U+D800~U+DFFF的值不对应于任何字符,为代理区。因此,UTF-16利用保留下来的0xD800~0xDFFF区段的码位来对辅助平面的字符的码位进行编码。但是在使用UCS-2的时代,U+D800~U+DFFF内的值曾被占用,用于某些字符的映射。但只要不构成代理对,许多UTF-16编码解码还是能把这些不符合Unicode标准的字符映射正确的辨识、转换成合规的码元。按照Unicode标准,这种码元串行本来应算作编码错误.
        (2) 从U+0000至U+D7FF以及从U+E000至U+FFFF的码位
        第一个Unicode平面(BMP),除去代理区,包含了最常用的字符。UTF-16与UCS-2编码在这个范围内的码位为单个16比特长的码元,数值等价于对应的码位。
        (3) 从U+10000到U+10FFFF的码位
        辅助平面(Supplementary Planes)中的码位,大于等于0x10000,在UTF-16中被编码为一对16比特长的码元(即32位,4字节),这种编码单元称作代理对(surrogate pair)。从U+D800到U+DFFF的码位(代理区),可将一个WORD(2字节)的UTF-16编码与两个WORD(4字节)的UTF-16编码区分开来。具体规则是:
    ● 码位减去0x10000, 得到的值的范围为20比特长的0~0xFFFFF(因为Unicode的最大码位是0x10ffff,减去0x10000后得到的最大值是0xfffff,肯定可以用20个二进制位表示),写成二进制形式:yyyy yyyy yyxx xxxx xxxx。
    ● 高位的10比特的值(值的范围为0~0x3FF)被加上0xD800得到第一个码元后称作前导代理(lead surrogates), 值的范围是0xD800~0xDBFF。
    ● 低位的10比特的值(值的范围也是0~0x3FF)被加上0xDC00得到第二个码元后称作后尾代理(trail surrogates)。值的范围是0xDC00~0xDFFF。
    ● 最终的UTF-16(4字节)的编码(二进制)就是:110110yyyyyyyyyy 110111xxxxxxxxxx。
        按照上述规则,Unicode编码0x10000~0x10FFFF的UTF-16编码有两个WORD,第一个WORD的高6位是(二进制)110110,第二个WORD的高6位是110111。可见,第一个WORD的取值范围是11011000 00000000到11011011 11111111,即0xD800~0xDBFF。第二个WORD的取值范围是11011100 00000000到11011111 11111111,即0xDC00~0xDFFF。
        由于前导代理、后尾代理、BMP中的有效字符的码位,三者互不重叠,这意味着UTF-16是自同步的(self-synchronizing)。搜索也简单了:可以通过仅检查一个码元就可以判定给定字符的下一个字符的起始码元。 UTF-8也有类似优点,但许多早期的编码模式就不是这样,必须从头开始分析文本才能确定不同字符的码元的边界。由于最常用的字符都在基本多文种平面中,许多软件忽视了代理对的处理和不充分的测试,这导致了一些长期的bug与潜在安全漏洞,甚至包括一些广为流行、最常用的应用软件。


        七、确定支持的字体与软件编码
        1. 关于汉字编码
        汉字编码从GB2312、GB13000-1(GBK1.0)、GBK2K(GB18030-2000/Unicode3.0)、GB18030-2005、Unicode15.0与GB18030-2022、……一路发展过来,字符数量不断增加,到了GB18030-2022已有87887个汉字。本次的实验以Unicode为主。
        Unicode字符集分为非常多段范围,每段范围规定了相应用途。比如U+FF00~U+FF5E的是对应半角字符的全角字符。并不是所有码位的字符都能显示,有些是相互组合的组件,字符的长宽也不是固定不变,还有些字符显示的方向也是相反的。如果不了解清楚就用,就会给实际应用带来非常大的问题。汉字的具体Unicode编码范围以及不同字符集的unicode编码范围,可参考我搜集和翻译的“Unicode世界各国语言范围 汉字部分.xls”表格附件。推荐用BablePad软件查看电脑内每个字体的Unicode覆盖范围(这软件还可代替WIN自带记事本,快速打开txt文件。),确定等下用哪个字体。首先Unicode的汉字,必备的就是基本平面U+4E00~U+9FFF范围的字了,不过这里面没有包括标点符号。我把WIN7电脑里的字体都找了个遍,发现内置汉字字体大部分就只做到基本平面,大部分字体是以U+9FBB结束。之后的汉字GBK里是没有的,也只有少数几个能显示,比如用文泉驿正黑、思源宋体CN、遍黑体(P1)字体,最后累计可凑到U+9FFF。点阵字库是上个月做的,就懒得重新补充U+9FBB之后的缺失字符了,不然得把缺失字符都从各个字体文件中找齐并分别生成字模数据,还得重新生成16、24、32点阵字库。

    BablePad字符映射表

    BablePad字符映射表

    (图1)BablePad字符映射表

        2. 确定支持的字体
        除了基本平面的汉字,有时还需要扩展平面的汉字。只有“MingLiU_HKSCS-ExtB”、“SmiSun-ExtB”包括了扩充B区。中文Win10原生宋体字库也只支持到扩展D区及以下全集+扩展E区部分字。要更多扩展区的字符,就要下载安装更多字体去支持最新的扩展区。这里再次强调一下:从电脑的矢量字体转换成点阵字体,仍然是同一种字体,版权是一样的。电脑中的字体大部分都是不开源不免费商用的。开源的字体有:宋体、黑体、仿宋、楷体、思源字体可免费商用;文泉驿字体,基于GPL授权的;阿里巴巴普惠体3.0(支持最新GB18030-2022,已于2023年8月正式生效),支持到扩充F区。文泉驿有点阵字体和矢量字体,以及等宽字体。点阵字体一般是小号字,如果小号字用矢量字体渲染效果会不好,所以很多电脑字体中会包含矢量字体和小号的点阵字体。但是文泉驿的字体基本没有扩充区,只有U+2F800~U+2FA1F的扩充。普惠体3.0从扩充B区到F区都有,只是都缺少几个字,略可惜。还有就是遍黑体,居然包揽了扩充G、H区的全部字,甚至连笔划超多的“biang”字也有(可以开心玩各种梗了),但是其它区就基本空白。那么根据免费优先和需要用到的汉字范围,选择的字体确定为:

    编码范围U+        区域                优先用字体        备注
    <10000                (BMP)                (大部分均可)        -
    20000-2A6DF        CJK扩充B                普惠体3.0        缺45个汉字
    2A700-2B73F        扩充C                普惠体3.0        缺49个汉字
    2B740-2B81F        扩充D                普惠体3.0        缺8个汉字,或遍黑体(P1)不缺
    2B820-2CEAF        扩充E                普惠体3.0        缺108个汉字
    2CEB0-2EBEF        扩充F                普惠体3.0        不缺了
    2F800-2FA1F        兼容表意文字补充        文泉驿点阵正黑        不缺
    30000-3134F        扩充G                遍黑体(P2)        不缺
    31350-323AF        扩充H                遍黑体(P2)        不缺

        3. 对应字体的下载
        (1)文泉驿字体: http://wenq.org
        (2)遍黑体的GitHub: https://github.com/Fitzgerald-Porthmouth-Koenigsegg/Plangothic
        (3)阿里巴巴普惠体3.0: www.alibabafonts.com

        4. 编码与编程
        现在的WINDOWS都是Unicode的内码,然后用代码页适应各种语言。同一种字体一般都支持Unicode和GB18030-2022,当然对于应用程序是否是Unicode(UTF16)的编码,以及是否支持Unicode码输入输出,得看编写它的程序员心情。至少自带的记事本是支持的,由记事本保存与另存转码的Unicode编码文件会自动加上BOM。KEIL编程开发软件刚安装完如果不设置的话默认编码为ANSI(老外的软件嘛),且KEIL C51只支持C语言的C89古董标准,这个标准不支持宽字符(<wchar.h>宽字符支持,<wctype.h>宽字符分类和映射支持,都只有KEIL ARM环境才支持)。所以安装完KEIL都要改为常用的GB2312编码(实测是GBK)或者没有BOM的UTF-8。另外,KEIL的编码设置是不能改变源文件的编码的,改变的只是显示的编码,如果打开源文件出现大量乱码,那就是编码选错了。要转码得用其它软件,有些软件转换过来时会在开头加上BOM,必须要把所有的.c、.h等源文件都删掉BOM才能通过编译。由于KEIL不支持BOM(signture),可用UltraCodingSwitch软件批量转换源代码文件的格式。
        开始编程前,我们先把整个工程根据主要使用的编码规划为GB系列、UN系列。对于GB系列,配置源代码编码为GB2312(GBK) (对应的代码页code.page=936),稍后制作的字库就以GBK编码为主,如果有少量Unicode就转换为GBK。对于UN系列,配置编码为没有BOM的UTF-8 (对应的代码页code.page=65001),此时可以支持Unicode扩展平面的字符。对于混合两种编码的工程,就优先考虑主流的Unicode。然后开始编写源代码或者把已有的源代码转码过来放入工程中。稍后制作的字库就以Unicode编码为主,外加GBK转换为Unicode。附件里的一个KEIL深色主题文件已设置成UTF8编码,替换掉UV4/global.prop文件即可。

    KEIL编码设置为GB2312

    KEIL编码设置为GB2312

    (图2)KEIL编码设置为GB2312

    KEIL编码设置为UTF8

    KEIL编码设置为UTF8

    (图3)KEIL编码设置为UTF8

        对于字符串结束判断标志,之前GBK编码的字符串是用1个NULL字节(0x00)用来判断字符串结束,包括ASCII码和UTF8也可以这样(见帖子前面的标准UTF-8),而对于Unicode的UTF-16字符串,这样行不通。UTF-16编码的字符串得由2个字节的Unicode NULL字符(0x0000)来表示。
        (1)GB系列。如果源代码编码为GB2312(GBK),那么程序里Unicode的UTF-16字符串得用比较复杂的方式实现。可以使用万能的\x把UTF16编码的汉字逐个编码手动输入进来,比如字符串“◇按住P3.3按键进入U盘模式。”编写如下:
    SPI_ShowUCS2Str(10,21,RED,WHITE,"\x25\xc7\x63\x09\x4f\x4f\x00\x50\x00\x33\x00\x2e\x00\x33\x63\x09\x95\x2e""\x00\x00",16,0);
        这样多写几句简直让人崩溃。于是改为数组的形式,其中数组的最后1个数0x0000表示字符串结束,只是少打\x了:
    code u16 main_UCSstr01[]= { //◇按住P3.3按键进入U盘模式。
            0x25c7,0x6309,0x4f4f,0x0050,0x0033,0x002e,0x0033,0x6309,0x952e, 0X8FDB,0X5165,0XFF35,0X76D8,0X6A21,0X5F0F,0X3002,      
            0x0000,};  
        然后在程序中使用:
    SPI_ShowUCS2Str(10,21,RED,WHITE, main_UCSstr01 ,16,0);
        这样稍微少打些字,但是字符串的编码转换还是得手工进行。仍繁琐。所以只适合很少Unicode字符串的场景。
        (2)UN系列。源代码编码采用UTF-8,只需键入需显示的字符串即可,省事。代码如下:
    SPI_ShowUnicodeStr(10,3+18,RED,WHITE, "◇按住P3.3按键进入U盘模式。" ,16,0);
        接下来就需要制作Unicode字库与编码转换。


        八、制作Unicode字库
        1. Unicode字库遇到的问题
        Unicode字库的制作,分为UCS2基本平面部分和UCS4辅助平面两大部分。UCS2这部分的制作步骤跟GBK相同,只不过用的是UCS2字符集,就不赘述。UCS4辅助平面部分就遇到大麻烦了。
        首先,GB类的汉字属于最基本的汉字,笔划总的来说不复杂,对应的取模步骤在上一个帖子讲了。但是最基本的汉字之外存在大量扩展汉字,在复杂的笔划下,16x16的字可能会糊的看不清。24x24字体勉强看清,32x32字体却比较清楚。比如笔划数最多的四个繁体龙的叠字(编码为U+2A6A5)有64划,24x24字体只能勉强辨认。从占用空间来说,16x16的字只占用32*N字节,32x32的字要占用128*N字节,大了4倍。转成字库W25Q64可能会装不下,TFT整屏可显示的字数也会随之减少。所以实际工程应用里,需权衡字体清晰度与占用空间等选择合适的字体大小。
        其次,字模的转换出问题了。由于网上常见的各种字模软件都很古老,无法识别超过Unicode BMP(U+FFFF)的编码,完全无法使用;而字模3虽然比较古老,但在超过BMP编码时还只能一次转几个字模。如果使用它的批量转换Unicode大于0xFFFF编码的字符就都是方框(识别不了UTF16编码的代理对,导致4个字节的编码被识别为2个字节),也极不方便。
        再就是字体裁剪问题。由于我们不能简单的把整套Unicode字体塞进容量有限的SPI flash芯片中,Unicode里也不是所有的字符均可显示,所以我们需要选择用到的字体编码范围,并选择固定一两套大小的字体,用于特定的项目。
        2. Unicode字库的制作
        以上几个问题导致UCS2、UCS4辅助平面字符的制作只能临时绕道走远路,改用以下步骤:

        以上几个问题导致UCS2、UCS4辅助平面字符的制作只能临时绕道走远路,改用以下步骤:
        (1) 用BablePad软件,把字体文件分批转成对应分辨率的(比如32*32)bmp图片。
        BabelPad旧版请百度搜索下载,最新版下载地址:
    https://www.babelstone.co.uk/Software/BabelPad.html
        在BabelPad里查看,复制字体覆盖率,粘贴在txt记事本,通过搜索“NO”来寻找缺少的字(对应编码)。把全部有“NO”的一行都复制到另一个文本里记录缺失文字。15.0.0.4版的批量输出bmp字形功能点了直接停止响应,改为BabelPad 10.0.0.6版输出,设置为:工具->输出字形->“Font Name:”选择需要的字体。“Code Points”编码范围根据需要输入,比如(十六进制):20000 to 2A6D6。然后选择好保存路径“Create at”,比如:“D:\我的文档\Fonts”。如图:

    BablePad批量导出BMP图片格式

    BablePad批量导出BMP图片格式

    (图4)BablePad批量导出BMP图片格式

    32x32字体的设置:“Font Size:” 24points(磅), Width(宽)32,Height(高)32。
    24x24字体的设置:“Font Size:” 19磅,...24,24。
    16x16字体的设置:“Font Size:” 13磅,...16,16。
    阿里巴巴普惠体的32x32字体可以设置到25磅。

        表5 常用字体大小与像素关系
    中文字号        英文字号        毫米        像素
    初号        42pt        14.82        56px
    小初        36pt        12.7        48px
    小一        24pt        8.47        32px
    小二        18pt        6.35        24px
    小四        12pt        4.23        16px

        软件里面的字体大小(Font Size)并不是我们生成点阵的大小,12磅字体是Windows的叫法,点阵字体的大小(px)由宽和高的像素点数来决定,为方便单片机程序处理与存储,一般为8的倍数。电脑的12磅字体约等于16*16点阵字大小,见表5。这里适当调大一点,为了让复杂笔划的字能显示稍微清晰一点。另外,待转换的文件名字尽量都用短文件名,每次转换不要超过65000个字,以免出错。转换过程异常缓慢,我这边WIN7 64位电脑有SSD加持都花了一两个小时。完成后转成大量bmp图片,还需核对数量,缺失的字需用备用方框的bmp粘贴代替,不然编码会乱套。代替的文件名按照编码规则命名,比如:“U_02ABCD.bmp”。

        (2) 用Image2Lcd v3.2把全部图片批量转成一堆BIN。
        设置输出数据类型为二进制(*.bin),扫描模式:水平扫描,输出灰度:单色,最大宽度和高度:比如16x16的汉字就分别输入16、16,高位在前(MSB First)。如图:

    设置

    设置

    (图5) 设置.png

        点击“打开”bmp所在文件夹里的任意一个图片,点击“批量转换”。Image2Lcd的转换虽然较快,但是转几百张图之后就假死了,任务管理器中“I/O写入字节”仍在增大,说明仍在转换中,只能等其完全转好。
        (3) 用批处理.bat自动合并成一个大的BIN文件。
        把合并后的BIN文件存放到另一个文件夹。删除原来文件夹的bmp和bin。之前生成的4万多张图片直接占用了170多MB,再生成同样数量的BIN又占用了同样大小。(为啥占用这么大?因为1个文件要占用至少1个簇,有4万多个文件呢。)双击.bat,合并成一个BIN后体积就很小了。这样就制作好点阵字库文件了。

        3. GBK与Unicode编码互转
        如果应用中同时用到两种不同系列的编码就需要转换编码,但是不要编码间来回转换,以免转出问题。Unicode UCS2与GBK编码相互转换,参考的是网上的cc939.c文件。在Unicode转GBK之前,先要进行Unicode的几种编码之间的转换,得到UCS2编码或者是UCS4辅助平面部分编码。对于UCS4辅助平面部分就直接转换为字模数据,存放在数组tmp[]里。再对UCS2编码的U+4E00到U+9FFF的基本汉字部分(CJK Unified Ideographs)和U+0080到U+33D5、U+FE30到U+FFE5的标点符号部分,分别采用公式法快速查表和二分查找法获取对应字模数据。
        因为如果完全通过二分查找法把Unicode与GBK互转,就需要16次查找,表格文件也会较大。同时由于Unicode编码的符号放置比较分散,对应多个范围,于是我把Unicode编码的4E00~9FFF做成单独查表的表格文件“UNI2GBK_.SYS”。每个表项按照Unicode顺序依次存放对应编码的GBK编码,这样每个表项只需要2字节,所以这个转换表格文件只占用45KB。UCS2的基本汉字部分相对位置转换公式为:[(高8位 -0x4e)*256 +(低8位 -0x00)]*2,再加上字库偏移和逻辑扇区偏移就可得到实际地址。
        接下来Unicode缺少的符号由UNI转GBK实现。GBK符号与Unicode映射表分为两段:Unicode范围U+0080~U+33D5、U+FE30~U+FFE5,单独放在一个文件“UNIGBK_symbol.BIN”。每个表项需要Unicode码和对应的GBK码共4个字节,仍按照Unicode升序排列,对应GBK的范围几乎都在A1xx到A9xx之间,只需4KB就可以存下,而且只有用到标点符号时才用二分查找,只需最多12次查找,速度快很多。如果不在查找范围之内,就把编码"\xA1\xF6"(符号"■"的编码)放入指针再返回指针;如果查找结束仍未能匹配就返回编码"\xA1\xF1"(符号"●"的编码)。如果是UCS4辅助平面部分超出范围,也分别返回"\xA1\xCA"(范围在U+10000~U+1FFFF)或者返回"\xA1\xD7"(范围>U+2A6DF),用于提示用户。编写与调试二分查找法费了不少时间,之前查找总是不能匹配,然后我用TFT把每次搜索的结果和对应地址、编码转换前后值等数值都显示出来,逐步排查错误发现起止地址弄错或者大于、小于判断弄反等BUG,调好才能正常实现功能。为此又照着之前的一个函数改成了十六进制显示函数用于显示flash实际地址,写程序像盖大楼一样缺什么代码就补什么。GBK转Unicode基本同样,使用表格文件“GBK2UNI_.SYS”,文件也只占用49KB。连符号也分散在非常多段范围,可能用到的Unicode符号见下表:

            表6 常用Unicode符号编码范围
    编码                字符数
    0000-02ff        768
    0370-04ff        400
    2000-20cf        208
    2100-23ff        768
    2460-27ff        928
    2e80-2fdf        352
    3000-312f        48
    31c0-33ff        576
    4dc0-4dff        64
    fe30-fe50        32
    ff00-ffef        240

        4. 滥用编码互转带来的乱码问题
        刚才说不要编码间来回转换,那么如果编码来回转换会怎样?冒出来的锟斤拷是啥?能吃不?看下这个帖子就知道了:还有谁喜欢烫屯锟斤拷。
    https://www.zhihu.com/question/52684968

    06.PNG
    (图6)
    ------------------
        本帖主要讲解了Unicode编码、字体及其点阵字库的制作。下一贴将会做一个实际应用,把FAT文件系统加进去,用GBK与Unicode混合编码实现文件系统的文件名显示。
        附件里包含一套Unicode基本汉字和一套扩展A区点阵字库(3种字号),在SYSTEM文件夹中,拷到SPI flash里即可读取,其它区可根据本教程自行制作。8月23日更新:1.把Unicode字符集重新整理一遍。 2.重新制作UCS2 U+4E00~U+9FFF的点阵字库,增加了一些之前字体文件没有的字符(参考自Unicode网站: https://www.unicode.org/charts/)。3.上传到附件(备注:代码经过过多次修改已不小心调乱了,检查了几天找BUG中,就先不上传代码了,不排除后续调好后再跟帖加上)。

        附件:
    附件1.rar (36.55 MB, 下载次数: 41)






    1 喜欢他/她就送朵鲜花吧,赠人玫瑰,手有余香!
    回复 送花

    使用道具 举报

  • TA的每日心情
    开心
    14 小时前
  • 签到天数: 156 天

    [LV.7]常住居民III

    10

    主题

    102

    回帖

    122

    积分

    注册会员

    积分
    122
    发表于 2023-9-2 14:04:06 | 显示全部楼层
    感谢飞哥,期待后续.
    回复 支持 反对 送花

    使用道具 举报

  • TA的每日心情
    开心
    昨天 08:40
  • 签到天数: 139 天

    [LV.7]常住居民III

    2

    主题

    89

    回帖

    586

    积分

    高级会员

    积分
    586
    发表于 2023-12-1 15:57:41 来自手机 | 显示全部楼层
    👍
    回复 送花

    使用道具 举报

  • TA的每日心情
    开心
    22 小时前
  • 签到天数: 148 天

    [LV.7]常住居民III

    13

    主题

    104

    回帖

    897

    积分

    高级会员

    积分
    897
    发表于 2024-1-9 08:58:12 | 显示全部楼层
    znfat中文长文件名记录的是长文件名unicode码,外部FLASH用的是GBK字库,我想把长名文件名显示到OLED12864上该如何操作:有没有简单点的unicode码到GBK码的转换,我的想法是把读出来的unicode码通过转换成GBK编码,再读取GBK字库显示到OLED,GBK字库显示到OLED已实现。
    回复 支持 反对 送花

    使用道具 举报

  • TA的每日心情
    开心
    6 小时前
  • 签到天数: 130 天

    [LV.7]常住居民III

    13

    主题

    52

    回帖

    1358

    积分

    金牌会员

    积分
    1358
     楼主| 发表于 2024-1-9 09:28:15 | 显示全部楼层
    yanhui 发表于 2024-1-9 08:58
    znfat中文长文件名记录的是长文件名unicode码,外部FLASH用的是GBK字库,我想把长名文件名显示到OLED12864 ...

    FAT文件系统里的短文件名为8.3格式(ANSI编码,即本机应为GBK),长文件名为UTF16,同一个系统要同时两种编码必然要用一种编码转成另一种。UNICODE与GBK的转换没有公式,只能是查表。简单应用可以照这个帖子查表转换即可:
    https://www.stcaimcu.com/forum.php?mod=viewthread&tid=2769

    复杂的(超出基本平面)UTF16文件名不知道ZNFAT是否支持,毕竟不是所有的汉字都是只有2个字节编码。(可以照此贴操作。)
    回复 支持 反对 送花

    使用道具 举报

  • TA的每日心情
    开心
    22 小时前
  • 签到天数: 148 天

    [LV.7]常住居民III

    13

    主题

    104

    回帖

    897

    积分

    高级会员

    积分
    897
    发表于 2024-1-9 13:19:04 | 显示全部楼层
    好的,我试试看,谢谢
    回复 支持 反对 送花

    使用道具 举报

  • TA的每日心情

    昨天 06:52
  • 签到天数: 155 天

    [LV.7]常住居民III

    142

    主题

    199

    回帖

    1686

    积分

    金牌会员

    积分
    1686
    发表于 2024-1-9 14:50:35 | 显示全部楼层
    4QPP5{~{G[66S)~]L6K1XAT.png


    用这软件生成不香吗
    回复 支持 反对 送花

    使用道具 举报

  • TA的每日心情
    开心
    6 小时前
  • 签到天数: 130 天

    [LV.7]常住居民III

    13

    主题

    52

    回帖

    1358

    积分

    金牌会员

    积分
    1358
     楼主| 发表于 2024-1-9 19:38:21 | 显示全部楼层
    QQ624353765 发表于 2024-1-9 14:50
    用这软件生成不香吗

    这个支持扩展字?或者自己写一个也不错。
    回复 支持 反对 送花

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

    GMT+8, 2024-5-8 23:18 , Processed in 0.073729 second(s), 60 queries .

    Powered by Discuz! X3.5

    © 2001-2024 Discuz! Team.

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