字体unicode图元映射到实际字符

5
我想展示字体中的所有字形,我使用GetFontUnicodeRanges获取可用字符,然后创建一个位图,其中包含所有可用字符和它们的索引。 我使用"Wingdings 2"字体作为测试案例,并与Windows的charmap.exe进行了比较。 我发现虽然所有字符都出现了,但某些字符出现了多次(在该非unicode字体中总共有480个字形),而且位置与charmap中的位置不同(例如,charmap中的中等大小圆形字形位于0x97,而在字体中,它是字形0xF097,我认为它也是0x2014中的字形)。 我想像“常规”方式一样使用字体,这意味着我想看到与charmap.exe相同的数据(顺便提一下,我还想知道字体是unicode字体还是ascii字体,就像charmap显示的那样)。 基本上,你可以说我正在尝试从头编写自己的charmap。 我该如何填充缺失的数据? 我查看了Windows的字体和文本API,但找不到任何帮助我的内容,所以我必须缺失一些相关的API。 它们是什么?
3个回答

6
经过对GetFontData的艰辛尝试以及文档缺少(并不是完全没有,但是组织得很不好,确实有一些数据缺失),我发现了自己编写CharMap的方法。以下是我在开发过程中发现的内容:
  1. 文档会告诉你使用一个“技巧”,因为字形位置数据紧跟在表中的数组之后。这并不意味着它在表中。实际上,它们在表中。
  2. 您还需要阅读表以获取位置格式标志(偏移量34)和表中的字符数字段(偏移量4)。
  3. 在符号字体中似乎添加了0xF000到其实际索引的字符(如果cmap头部编码ID为0,则可以确定字体是符号字体,至少在TTF格式4中,这是Microsoft格式),因此您会得到Unicode值在Unicode表的远端而不是常规ASCII代码。我从每个字符代码减去了0xF000并在Wingdings[2,3]和Webdings字体上进行了测试,结果很好。
我经常使用官方文档:www.microsoft.com/typography/tt/ttf_spec/ttch02.doc,以及参考代码:http://support.microsoft.com/kb/241020
参考代码是用C写的,因此为了在C#中编写它,我将所有数据读入缓冲区,并从中“手动”读取每个元素。

2
我几年前也经历了这场噩梦,现在我对这些东西非常了解。我想提供一些帮助。

1) 你不能假设“loca”跟随“cmap”。顺序可能因字体而异。每个块的位置由OffsetTable定义,该表通常从字体文件的第0字节开始。(http://www.microsoft.com/typography/otspec/otff.htm

2) 不能假设“cmap header encoding id is 0, at least in TTF format 4”表示符号字体。我确信某些旧的阿拉伯字体也使用该编码。到目前为止,我仍然不知道如何区分它们。Windows可以做到这一点,但我不知道如何做到。我不知道如何确定一个字体是否是符号字体。即使检查代码页位32的OS/2表,在许多情况下也不足够。

3) 你不能简单地使用神奇的0xF000数字并将其加到你的小型0-255数字上,以获得你要进行的字形映射的字符。那是因为这些小的0到255的“ASCII”代码将根据你的系统区域设置而变化。

符号字体在Windows处理中是特殊的。

与普通字体不同,符号字体的映射在于系统默认代码页(非Unicode应用程序,即CP_ACP)。

例如,假设你的符号字体有这个字形:'%'。如果你的系统默认使用CP 1252,则要呈现此字形,例如,你必须呈现字符值'0xC2'。

如果你的系统默认使用CP 1251,则要呈现此字形,例如,你必须呈现字符值“0x416”,这是完全不同的。

换句话说,字体的Unicode范围因默认的非Unicode代码页而异!

经过调查,我们发现字体的有效字符值是通过将0到255转换为它们的CP_ACP值而获得的unicode值。

这是什么意思?这意味着你需要使用MultiByteToWideChar和CP_ACP来获取值0到255的映射,以根据你的系统区域设置(CP_ACP)获得它们的本地化Unicode值。

所以,这样做会给你一个像这样的映射:

ASCII -> localized non-static UNICODE
0x00 -> 0x00
0x01 -> 0x01
0x02 -> 0x02
...
0xC2 -> 0x416 <----- This is correct : the value will be different in some cases.
...
0xE3 -> 0xE3

0xF000到0xF0FF是静态的UNICODE值:它们永远不会改变。

因此,要获取“本地化的非静态UNICODE”字形ID,您首先需要使用上面的映射来查找相应的ASCII值,然后将其加上0xF000,然后获取该值的字形ID。

当然,微软没有记录这些荒谬的内容...或者我找不到它。


1

我从未详细查看过"WingDings 2",但是字符的重用对于不同的字符来说很常见。例如,大写罗马字母A和大写希腊字母α经常是相同的字形。

然而,我猜0x97、0xF097和0x2014的相等性是某种处理windows-1252的黑客方式。在windows-1252代码页中,0x97是一个em-dash,它在Unicode中是0x2014。0xF097处于专用区域;我想它提供了一种兼容Unicode(且可逆)的方法来编码windows-1252的0x97。

根据我的经验,获取字体支持的unicode字符列表的最可靠方法是解析ttf文件中的cmap表。这有点烦人(cmap支持大约六种不同的编码),但可以在线上找到文档。您可以使用GetFontData函数获取原始数据,或直接解析ttf文件。

charmap使用GetFontData函数,代码中包含字符串“cmap”,这表明charmap也在执行此操作。

Windows SDK调试工具包括logger.exe,记录应用程序使用的所有API。如果您想确切知道charmap正在做什么,可以使用它。


谢谢。我现在正在尝试使用GetFontData,遵循http://support.microsoft.com/kb/241020上的文章,但我发现很难获得任何有意义的数字。是否有更好的解释和/或C#代码(或任何其他代码)可以作为参考? - Itai Bar-Haim
我意识到为什么我得到了错误的数字-我没有查看正确的索引。然而,这些索引实际上指向了我使用GetFontData读取的数组之外。当我读取字体“Wingdings 2”时,我得到了一个322字节的'cmap'表,其中实际上包含了除字形索引表之外的所有索引(根据www.microsoft.com/typography/tt/ttf_spec/ttch02.doc)。那么我该如何读取额外的数据呢? - Itai Bar-Haim
使用十六进制编辑器,我发现下一个块实际上是“loca”表。微软在文档中写这个有那么难吗?;-) - Itai Bar-Haim

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接