ttf 字体的一点问题
我们的游戏引擎使用的是 stb 的 truetype 库 来处理 ttf 字体的。最近发现在使用公司提供的 阿里巴巴普惠体 时出了一点问题。
引擎渲染出来的汉字比标称的像素高度矮了不少,想渲染 100 像素高的汉字,结果只有 70+ 像素左右。我们之前测试使用的中文字体( Windows 自带默认字体)没有这个问题。
在网上翻了一下,找到这么一个帖子:https://github.com/nothings/stb/issues/689 以及 imgui 也遇到过类似问题 。
原来,stb truetype 库提供了两个计算字模大小的 api ,stbtt_ScaleForPixelHeight()
和 stbtt_ScaleForMappingEmToPixels()
分别用两种方式计算字模高度比例。作者比较提倡用前一个 api ,但也保留了后一个 api 的使用场景。
computes a scale factor to produce a font whose EM size is mapped to 'pixels' tall. This is probably what traditional APIs compute, but I'm not positive.
另外在获取字体的 ascent 以及 decent 时,也有两个不同的 api : stbtt_GetFontVMetrics()
和 stbtt_GetFontVMetricsOS2()
。
我很好奇的翻阅了这些 api 的实现,并查阅了能找到的关于 truetype 的文档,得出了下面的结论:
在字体文件中,有两处地方描述了字体的 ascent 和 descent 值。这个值应该是字体设计者创作时使用的像素大小。原本它们应该记录在字体文件头的 hhea 表中,它最初是由 apple 设计的。我们可以在 apple 的网站查到文档。
后来,微软在设计字体时,可能是发现这些信息不够用,也或者是不赞成 apple 的某些设计决定。他们又设计了一张新的表叫 OS/2 ,里面也有类似的字段。文档在微软网站上可以找到 。
为什么两张表有很多重复的字段,在字体文件里放了两份(带来了冗余信息)?我猜恐怕和微软的代码实现风格有关:微软非常喜欢直接把内存结构映射为文件结构。所以他们选择加一整张表而不是只增加必要的字段在新的表中。
对于大部分字体来说,两张表的 ascent 和 descent 值是一样的。但正如 apple 文档中注释的,没有任何要求它们必须一致。而微软文档的一篇注解 则提到:
Some legacy platforms or applications may not use OS/2 fields at all. Thus, CJK fonts generally should have the same descender value recorded in hhea.descender, OS/2.sTypoDescender, and HorizAxis.ideo (if present) fields, and the same ascender value recorded in hhea.ascender, OS/2.sTypoAscender, and HorizAxis.idtp (if present) fields.
显然,阿里巴巴普惠体没有遵守这一点。
它在 hhea 表中,ascent = 1050 descent =-322 ,在 os/2 中,ascent = 850 descent = -150 。
用 stbtt_ScaleForPixelHeight()
计算缩放比时,使用的 pixel / (hhea.ascent - hhea.descent) 得到的值小于预期。
为何 stbtt_ScaleForMappingEmToPixels()
可以得到预期值?因为它使用的是 head 表中的 unitsPerEm 字段去计算的,这个值通常等于 ascent - descent (上面的文档中也提到了这一点)。在阿里巴巴普惠体文件中,它也等于 os2.sTypoAscender - os2.sTypoAscender 。
我不清楚为何这个字体会填入这样不同的数据。我猜测可能和 apple / ms 系统采用的默认屏幕 dpi 有关?它间接导致字体设计人员在打包字体时错误填写了某种参数?不过无论如何,我觉得是字体本身的 bug 。虽然,我通过修改我们引擎的代码绕开了这个问题。
Comments
Posted by: 分切机厂家 | (1) July 7, 2023 01:57 PM