书写此文的背景
2021年9月使用cairo实现DirectUI时,发现有些特殊文字无法正常显示。
分析cairo及pango的代码后,发现pango虽然已通过fontmap方式解决了大部分文字渲染问题,但在处理Unicode非0平面(Plane)的字符时,仍存在不合理之处。
文字渲染的问题
1、严重错误:某些字符被渲染成“其它字符”比如将”𩽾29F7E“渲染成“齾9f7e“。这是cairo的一个BUG,不具普遍性。
2、无法显示:某些字符被渲染成一个“方块”或“数字码”,这是普遍的文字渲染问题,存在于很多软件中。Windows系统已经处理的很好,而且有完善的编程接口。可以参考:关于Windows TextOut输出文字时的一些难看的“方块”
3、显示不好:已设置为字体A,但部分字符被渲染为其它字体。比如经常看到某些软件或网页中的“少部分”文字的显示效果与其它文字不一样。这也是一个普通性问题。
cairo及pango的现状
一、cairo(v1.16)获取文字glyph索引的方式是完全错误的
没有考虑4字节(2个wchar_t)文字的情况,代码片段如下:
wchar_t unicode[2]; ... unicode[0] = ucs4; unicode[1] = 0; if (GetGlyphIndicesW (hdc, unicode, 1, &glyph_index, 0) == GDI_ERROR) { ... glyph_index = 0; }
- cairo官方不建议使用其自身的cairo_show_text渲染文字,而是使用pango。
二、pango(v1.45.3)已通过fontmap解决了“无法显示”的问题
指定字体“无法显示”某个字符的根本原因是:单个字体文件的可包含字符数(2^16)远小于Unicode字符总数(16*2&^16)。当待渲染字符没有被当前字体包含时,将产生“无法显示”问题。
所以在pango中使用fontmap将多个字体关联在一起, 只要这些关联字体的任意一个包含待渲染文字的glyph定义,即可完成渲染。
例如:SimSun(宋体)可以关联MINGLIU(细明)、MSYH(微软雅黑)等字体,渲染字符时,如果宋体里找不到该字符,就再找细明体、微软雅黑体。使用此方式可以解决绝大部分的“无法显示”问题
pango的默认代码中包含了两种fontmap:
1、依据Windows的fontlink信息,fontlink的数据存储在如下注册表位置
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontLink\SystemLink
2、写在代码内的几个默认定义。开发者可以扩展此方式创建自己的fontmap,还算灵活
static const char * const builtin_aliases[] = { "courier = \"courier new\"", /* It sucks to use the same GulimChe, MS Gothic, Sylfaen, Kartika, * Latha, Mangal and Raavi fonts for all three of sans, serif and * mono, but it isn't like there would be much choice. For most * non-Latin scripts that Windows includes any font at all for, it * has ony one. One solution is to install the free DejaVu fonts * that are popular on Linux. They are listed here first. */ "sans = \"dejavu sans,tahoma,arial unicode ms,lucida sans unicode,browallia new,mingliu,simhei,gulimche,ms gothic,sylfaen,kartika,latha,mangal,raavi\"", "sans-serif = \"dejavu sans,tahoma,arial unicode ms,lucida sans unicode,browallia new,mingliu,simhei,gulimche,ms gothic,sylfaen,kartika,latha,mangal,raavi\"", "serif = \"dejavu serif,georgia,angsana new,mingliu,simsun,gulimche,ms gothic,sylfaen,kartika,latha,mangal,raavi\"", "mono = \"dejavu sans mono,courier new,lucida console,courier monothai,mingliu,simsun,SimSun-ExtB,gulimche,ms gothic,sylfaen,kartika,latha,mangal,raavi\"", "monospace = \"dejavu sans mono,courier new,lucida console,courier monothai,mingliu,simsun,gulimche,ms gothic,sylfaen,kartika,latha,mangal,raavi\"", "emoji = \"segoe ui emoji,segoe ui symbol,segoe ui\"", "cursive = \"commic sans ms\"", "fantasy = \"gabriola,impact\"", "system-ui = \"yu gothic ui,segoe ui,meiryo\"", };
三、pango(v1.45.3)未考虑Windows的SurrogateFallback机制
Windows的SurrogateFallback机制可以指定Unicode每个平面(Plane)的字符渲染特定字体。这样的直接定位方式,可以避免过大的fontmap产生的效率问题。
例如,Win32系统的如下定义里,第一个”Plane2″=”SimSun-ExtB”指定所有无定义的字体在渲染Plane2字符时,均使用SimSun-ExtB渲染。第二个”Plane2″=”SimSun-ExtB”,特指SimSun字体在渲染Plane2字符时使用SimSun-ExtB渲染。
Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows NT\CurrentVersion\LanguagePack\SurrogateFallback] "Plane2"="SimSun-ExtB" [HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows NT\CurrentVersion\LanguagePack\SurrogateFallback\SimSun] "Plane2"="SimSun-ExtB"
四、pango(v1.45.3)未考虑Windows的Font Substitution
Windows的Font Substitution支持在渲染时将字体A替换为字体B,并且可以精确到具体字符集。也就是说,它支持将字体A中的字符集0替换为字体B中的字符集121进行渲染。这些设置保存在如下注册表中:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes