字体unicode字形映射到实际字符

时间:2012-02-15 09:49:17

标签: c# windows winapi fonts

我正在尝试以字体显示所有字形。我正在使用GetFontUnicodeRanges获取可用字符,然后创建一个位图,其中包含所有可用字符及其索引旁边的每个字符。

我使用字体"Wingdings 2"作为测试用例,并将其与我在Windows charmap.exe中看到的进行比较。我看到,当所有字符出现时,一些字符出现不止一次(非unicode字体总共480个字形),并且位置与charmap中的位置不同(例如,中等大小的圆形字形) ,在位于0x97的charmap中,字体为0xF097,我也认为它是0x2014中的那个。

我想将字体用作“常规”方式,这意味着,我希望看到与charmap.exe中相同的数据(并且在旁注中我还想知道字体是否是unicode字体或ascii字体,如charmap所示。基本上,你可以说我试图从头开始编写自己的charmap

如何填写缺失的数据?我正在查看Windows的字体和文本API,但找不到任何可以帮助我的东西,所以我必须缺少一些相关的API。它们是什么?

3 个答案:

答案 0 :(得分:6)

经过与GetFontData的挣扎以及缺乏文档(好吧,不完全缺乏,但实际上组织得不好,有些数据确实缺失),我找到了编写自己的CharMap的方法。这是我在开发过程中发现的:

  1. 文档将告诉您使用“技巧”,因为字形位置数据紧跟在cmap表中的数组之后。这并不意味着{strong> IN cmap表。实际上,它们位于loca表中。

  2. 您还需要阅读位置格式标志的head表(偏移量34),以及号码的maxp表字形字段(偏移4)。

  3. 似乎在符号字体中(如果cmap标题编码id为0,您可以判断字体是否为符号字体,至少以TTF格式4(Microsoft格式))添加字符{ {1}}到它们的实际索引,因此在Unicode表的远端获得Unicode值而不是常规ASCII码。我从每个字符代码中减去0xF000并在Wingdings [2,3]和Webdings字体上进行测试,它运行得很好。

  4. 我经常使用官方文档:www.microsoft.com/typography/tt/ttf_spec/ttch02.doc和参考代码:http://support.microsoft.com/kb/241020

    参考代码是用C语言编写的,所以为了在C#中写入,我将所有数据读到0xF000个缓冲区,然后“手动”从中读取每个元素。

答案 1 :(得分:2)

我几年前也经历过这个噩梦,现在我对这些事情了解很多。我想我应该投入并提供一些答案。

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

2)你不能假设“cmap header encoding id为0,至少以TTF格式4”是指符号字体。我知道某些旧阿拉伯字体也使用该编码。到目前为止,我仍然不知道如何区分它们。 Windows做到了,但我不知道如何。我不知道如何确定字体是符号字体。在许多情况下,即使检查OS / 2表中的代码页第32位也是不够的。

3)你不能简单地使用魔法0xF000号码并将其添加到你的小0-255号码中,以获得能够为你提供字形映射的角色。这是因为那些小的0到255“ASCII”代码将根据您的系统区域设置而有所不同。

符号字体是Windows处理它们的特殊方式。

与字形和字符之间的映射是静态的普通字体不同,符号字体映射根据非unicode应用程序(即CP_ACP)的系统默认代码页而有所不同。

例如,假装您的符号字体具有以下字形:'%'。如果您的系统默认使用CP 1252,那么为了渲染此字形,您必须渲染字符值'0xC2'。

如果您的系统默认使用CP 1251,那么为了渲染此字形,您必须渲染字符值'0x416',这是完全不同的。

另外说,字体的unicode范围根据默认的非unicode代码页而有所不同!

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

这是什么意思?这意味着您希望将MultiByteToWideChar与CP_ACP一起使用,以根据您的系统区域设置(CP_ACP)将值0到255的映射到其本地化的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。

当然,MS没有证明这种无意义......或者我永远找不到它。

答案 2 :(得分:1)

我从来没有详细看过“WingDings 2”,但是字形重复用于不同的角色是很常见的。例如,大写的Roman A和大写的Greek alpha通常是相同的字形。

但是,我认为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正在做什么,你可以使用它。