如何使用freetype获取字形unicode



我正在尝试使用freetype来枚举字体文件中的字形(名称和unicode(。

为了获得名称,我使用FT_Get_Glyph_name。但是如何获取字形unicode值?

我是字形和字体的新手。

从技术上讲,Unicode代码点不会与TrueType/OpenType字体中的字形一起存储。必须迭代字体中的字体cmap表才能获得映射,该映射也可能是非Unicode映射,并且可能存在指向同一字形的多个映射。好消息是FreeType在API中提供了迭代当前所选字符映射中的字形代码点的功能,这些功能都有很好的文档记录。因此,使用代码:

// Ensure an unicode characater map is loaded
FT_Select_Charmap(face, FT_ENCODING_UNICODE);
FT_ULong charcode;
FT_UInt gid;
charcode = FT_Get_First_Char(face, &gid);
while (gid != 0)
{
std::cout << std::format("Codepoint: {:x}, gid: {}", charcode, gid) << std::endl;
charcode = FT_Get_Next_Char(face, charcode, &gid);
}

有了这些信息,您可以创建一个从字形到Unicode代码点的尽力而为的映射。

人们会期望FT_CharMap保存以下信息:

[…]当前活动的charmap可用作face->charmap

但不幸的是,它只定义了类型的编码(Unicode、MacRoman、Shift JIS等(。显然,查找代码的行为是在其他地方完成的——当该字符不可用时,.notdef只会返回。

在我自己的一个基于FreeType的OpenType渲染器中,如果可能的话,它会"按名称"报告,我在初始化序列中发现了一些代码,这些代码存储了字形的名称(如果有字形的话(,即Unicode其他。但该代码是基于字形名称的存在。

进一步思考:您可以测试每一个可能的Unicode代码点,看看它是否返回0(.notdef(或有效的字形索引。因此,为所有可能的字形初始化一个空表,如果下面的例程找到了,只填写每个字形的Unicode

对于一种适度现代的字体,您只需要检查Unicode U+FFFF;对于重中文字体(Heiti SC最高可达U+2F9F4(或表情符号(Segoe UI表情符号最高可达U+1FA95(,您需要更大的数组。(唉,从字体中获得最大数字完全是另一回事。决定做什么取决于你想用它做什么。(

printf ("num glyphs: %un", face->num_glyphs);
for (code=1; code<=0xFFFF; code++)
{
glyph_index = FT_Get_Char_Index(face, code);
/* 0 = .notdef */
if (glyph_index)
{
printf ("%d -> %04Xn", glyph_index, code);
}
}

这个简短的C代码片段打印出从字体字形索引到相应Unicode的转换表。注意(1(并非字体中的所有字形都需要与之关联的Unicode。有些字体有大量的"额外"字形,可用于OpenType替换(如替代设计和自定义连字号(或其他用途(如前面提到的Segoe UI表情符号;它包含所有表情符号的彩色掩码(。(2(一些字形可能与多个Unicode字符相关联。例如,A的字形设计既可以用作拉丁文大写字母a,也可以用作希腊文大写字母Alpha。

并非字体中的所有字形都必须具有Unicode代码点。在OpenType文本显示中,Unicode字符序列和字形序列之间存在m:n映射。如果您对Unicode代码点和字形之间的关系感兴趣,那么最有意义的是使用从Unicode代码点到字体的"cmap"表中包含的默认字形的映射。

有关更多背景信息,请参阅OpenType规范:高级排版扩展-OpenType布局。

至于字形名称,每个字形都可以有一个名称,无论它是否从"cmap"表中的代码点映射。Glyph名称包含在"post"表中。但并非所有字体都必须包含字形名称。例如,CJK字体不太可能包含字形名称。

最新更新