全面披露:我正在开发我的 libui GUI 框架的文本 API。这包含了 Windows 上的 DirectWrite、OS X 上的 Core Text 和其他 Unix 上的 Pango(使用 HarfBuzz 进行 OpenType 整形)。我要指定的文本格式属性之一是要使用的 OpenType 特性的集合,这三个特性都提供;DirectWrite 是IDWriteTypography
.
现在,当您使用这些库绘制一些文本时,默认情况下您会启用一些有用的 OpenType 功能,例如标准连字 ( liga
),如 f+i 连字。我认为这是特定于字体的,但事实证明这是特定于正在成形的文本的脚本。Microsoft 为 OpenType 支持的所有脚本提供了指南(在“特定于脚本的开发”下),我可以看到在 HarfBuzz 本身中完成这一切以确认它的相当复杂的逻辑。
在 Core Text 和 Pango 上,如果我启用其他属性,它们将被添加到这些默认值之上。但特别是使用 DirectWrite,IDWriteTextLayout::SetTypography()
这样做会删除默认值:
可以在此处找到产生此输出的程序。
显然,我的第一个选择是询问如何获得 DirectWrite 的默认功能。不过,有人已经在这个网站上这样做了,答案似乎是“不”。
我猜 DirectWrite 允许我完全控制应用于某些文本的功能列表。这很好,但除非我以某种方式明确禁用默认功能,否则我无法使用其他 API 执行此操作!当然,我不知道这个列表是否会改变,所以硬编码可能不是最好的主意。
即使硬编码是一种选择,我也可以为每个脚本获取 HarfBuzz 的列表,但是 a)它相当复杂b)脚本有多种可能的整形器,这取决于(我认为)版本兼容性(例如,缅甸)。
那么为什么不使用 HarfBuzz 的列表来重新创建 DirectWrite 的默认功能列表呢?无论如何,它似乎想要对其他塑造者准确,所以这应该有效,对吧?好吧,我需要做两件事:弄清楚要使用什么脚本,并弄清楚要在脚本的哪些字符上使用哪些属性,其中字符在单词中的位置很重要。
DirectWrite 提供了一个接口,该接口IDWriteTextAnalyzer
提供了执行整形的工具。我可以使用它,但似乎脚本数据以DWRITE_SCRIPT_ANALYSIS
结构返回,并且脚本 ID 的描述说“编写系统脚本的从零开始的索引表示。”。
这没有帮助,所以我编写了一个程序来转储我输入的文本的脚本编号。在输入字符串上运行它
لللللللللللللاااااااااالا abcd محمد ابن بطوطة Отложения датского яруса
产生输出
0 - 26 script 3 shapes 0
26 - 5 script 49 shapes 0
31 - 14 script 3 shapes 0
45 - 2 script 1 shapes 1
47 - 25 script 22 shapes 0
我无法将这些脚本编号与任何 Windows 标头中的任何内容匹配:如果在任何 API 中定义了阿拉伯文、拉丁文或西里尔文编号,它们与这些不匹配。即使我确实得到了脚本和脚本编号之间的映射,这仍然没有给我提供应用词内特征的数据。
Uniscribe呢?好吧,等效SCRIPT_ANALYSIS
类型的文档说它的脚本 ID 是一个“[opaque] 值”,它的“这个成员的值是未定义的,应用程序不应该依赖于它的值从一个版本到下一个版本是相同的”。虽然我可以获得LANG_ENGLISH
一个语言代码来识别脚本,但除了“西方”(拉丁语?)脚本之外,仍然没有定义值。DirectWrite 值是否与 Uniscribe 相同?似乎我至少可以通过查看fLinkBefore
andfLinkAfter
字段来确定单词的初始和最终状态,但这足以正确应用每个脚本的属性吗?
HarfBuzz 确实有一个实验性的 DirectWrite 后端,它不打算被实际程序使用;我还不确定它是否具有我上面指定的相同的功能破坏。如果我发现了,我会在这里更新这部分。
最后,如果我以类似 kaxaml 的方式输入以下与上面第一个等效的测试用例:
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<FlowDocumentPageViewer>
<FlowDocument FontFamily="Constantia" FontSize="48">
<Paragraph>
afford afire aflight 1/4<LineBreak/>
<Run Typography.Fraction="1">afford afire aflight 1/4</Run>
</Paragraph>
</FlowDocument>
</FlowDocumentPageViewer>
</Grid>
</Page>
我看到连字被正确应用,即使在后一种情况下:
(最后的分数只是为了证明该属性正在被应用。)如果我假设 XAML 使用 DirectWrite,那么这证明我的第一个选项(简单地将我的自定义属性覆盖在默认值之上)应该是可能的......(我做出这个假设的基础是 XAML 提供了一个与 Direct2D 惊人相似的 API 来绘制 2D 图形,并且填补了很多漏洞,我必须手动编写大量胶水代码才能用 vanilla Direct2D 做同样的事情,所以我假设 XAML 中的任何可能都可以通过 Direct2D 实现,并且通过扩展 DirectWrite,因为它们在技术上是一起引入的......)
在这一点上,我完全迷失了。我希望至少可以跨平台进行预测,而且我不确定程序甚至应该如何,更不用说直接使用 OpenType 功能了。我是否对文本布局 API 抱有不好的期望?如果需要,我是否必须放弃 IDWriteTextLayout 并自己进行所有文本整形和布局?
还是我必须放弃普通 Windows 7 支持并升级到平台更新 DirectWrite 功能集?甚至完全是Windows 7?