DirectWrite (CreateTextFormat) 如何选择后备字体?

Posted

技术标签:

【中文标题】DirectWrite (CreateTextFormat) 如何选择后备字体?【英文标题】:How does DirectWrite (CreateTextFormat) pick the fallback font(s)? 【发布时间】:2014-10-30 20:47:08 【问题描述】:

CreateTextFormat 的文档没有说明字体回退选择,但如果选择了默认(NULL = 系统)集合,那么 DirectWrite 显然实现了字体回退。例如,如果我将在 Gabriola 字体中找不到的两个字形添加到 DirectWrite SDK 演示应用程序使用的测试字符串中,那么 DirectWrite 会从 Segoe UI Symbol 字体中挑选缺少的字形。这发生在基本的DrawText 调用以及自定义渲染器(对字体回退没有任何自定义),如下所示(唯一的修改是测试字符串):

复选标记和白星来自 Segoe UI Symbol,而不是来自 Gabriola,即使在演示应用中只指定了 Gabriola。那么,有谁知道 DirectWrite (CreateTextFormat) 是如何选择后备字体的?

更新。我看到有一个GetSystemFontFallback 可以列出备用字体,但它仅在 Windows 8.1 中可用(因为它在 IDWriteFactory2 中)。我猜他们已经注意到 API 在枚举后备字体方面的差距。所以我猜在 Windows 8.1 之前没有办法做到这一点,但如果有人知道黑客/解决方法......

更新 2。引用MSFT employee:

DirectWrite 具有不可从注册表读取或以任何方式可配置的后备数据。不过,在 Windows 8.1 中,引入了 API,允许应用指定自己的回退。 (它类似于用于创建复合字体定义的 WPF API。)

这仍然不能准确解释硬编码算法/替换方案实际上是什么。

【问题讨论】:

【参考方案1】:

IDWriteTextLayout 调用IDWriteFontFallback::MapCharacters 将每个 Unicode 字符映射到字体系列的有序列表,这些字体系列会一直尝试直到满足该字符。想象一个循环逐个读取每个字符,将代码点值和语言标记映射到 Unicode 范围,并在 cmap 表中支持该字符的第一个字体处停止。伪代码:

for each ch in text
    if ch is a combining mark or other similar extending character
        if the previously selected font supports the combining mark too
            use the previously selected font
            continue
        endif
    endif
    find first mapping for ch within the Unicode range, which matches
        the current language and base font family too (if pertinent)
    if mapping found
        for each font in mapping (starting with first listed)
            if ch in font cmap
                use current font
            endif
        endfor
    else
        use base font and undefined (.notdef) glyph
    endif
endfor

还有逻辑来确定回退语言环境(例如zh-Hans/Hant 或通用ja 匹配更具体的ja-jp)。它与 CSS 为浏览器 [http://www.w3.org/TR/css3-fonts/#font-matching-algorithm] 指定的字体回退算法有些相似(但不如?那么复杂),并且类似于回退算法由 WPF/XAML/Silverlight 使用。

请参阅IDWriteFontFallbackBuilder::AddMapping API (Win 8.1+),用于构建自定义后备列表,了解所使用的输入。

参见 C:\Windows\Fonts\GlobalUserInterface.CompositeFont 示例数据(注意这个文件实际上是用于 WPF,与 DWrite 使用的定义不太一样)。

<FontFamilyMap
    Unicode  = "3000-30FF, 31F0-31FF"
    Language = "ja"
    Target   = "Meiryo UI, Meiryo, Microsoft YaHei UI, Microsoft YaHei, MS Gothic, MingLiu, Arial Unicode MS"
    Scale    = "1.0" />

【讨论】:

以上是关于DirectWrite (CreateTextFormat) 如何选择后备字体?的主要内容,如果未能解决你的问题,请参考以下文章

DirectWrite

使用 Direct2D 和 DirectWrite(C++、DirectX)制作按钮

为啥 DirectX/DirectWrite/Direct2D 文本渲染不能像 GDI 一样清晰?

如何在 DirectWrite 中为倾斜字体获取正确的文本宽度?

360极速浏览器,关闭“开启DirectWrite高清字体渲染支持”导致内置打印崩溃

360极速浏览器,关闭“开启DirectWrite高清字体渲染支持”导致内置打印崩溃