OpenType 字体的 Firefox 呈现与字体规范不匹配

Firefox rendering of OpenType font does not match the font specification

我正在通过 Google 字体 API / CSS.

加载 OpenType 网络字体 Open Sans

在 Chrome43 (Linux+Windows) 和 Internet Explorer 11 (Windows) 中,浏览器完全按照字体中指定的方式呈现文本。但是,在 Firefox 38.0.5 中,某些字符的文本宽度 and/or 间距呈现不同。所有字体变体均为默认值 ("normal")。

例如,我们可以使用字符 1abi。 Open Sans "unitsPerEm"是2048,所以在18.0px的字号下,根据1/u * p * c * w where u = 2048,上面每个字符的30个字符的宽度应该是这样的, p = 18.0, c = 30, w为每个字符的前进宽度(Wolfram Alpha equation).

+----------------------------------------+-----------+
| char | font(px) | numChars |  advance  | width(px) |
+------+----------+----------+-----------+-----------+
|  1   |   18.0   |    30    |   1171    |  308.76   |   
|  a   |   18.0   |    30    |   1139    |  300.322  |
|  b   |   18.0   |    30    |   1255    |  330.908  |
|  i   |   18.0   |    30    |   518     |  136.582  |
+----------------------------------------+-----------+

本(JSFiddle)使用canvas方法measureText输出1a各30个字符的像素宽度, b,和 i

Chrome 文本长度与预期值完全匹配:

Firefox Linux 文本长度与除 a 以外的任何字符都不匹配,即使在考虑到 Firefox 不提供亚像素精度这一事实后也是如此:

我已经确认 canvas 报告的宽度确实是 Chrome 和 Firefox 输出的宽度 -- 下图显示红色背景,带有 Chrome 的黑色文本,Firefox 的白色文本——根据 Gimp "Measure" 工具,宽度与上面的输出相匹配。 Firefox 的 bi 太宽,1 太窄:

附带说明一下,Firefox Windows 文本长度甚至与 Firefox Linux 不一致——ab 宽度现在符合预期,但 1i 仍然不正确:

这是一个干净的 Firefox 配置文件,具有默认设置且未安装任何扩展。

谁能解释一下这是怎么回事,以及如何强制 Firefox 根据字体规范呈现字体?

UPDATE:在 Windows 上,将首选项 gfx.font_rendering.directwrite.enabled 设置为 true 可以解决问题(我认为这是 Firefox 默认设置硬件加速可用,即使硬件加速不可用,此设置也会强制启用它,例如在我的测试 VMWare 系统上)。 DirectWrite 已成为 Windows 上 Chrome 中的默认设置 since version 37. The Linux behavior is still unexplained. This blog post 在 Windows 上解释了有关 Firefox 中 DirectWrite 渲染的更多信息。

(这个答案总结了问题评论中提出的要点,以及问题发布后所做的一系列额外研究。)

由于 various and complex 原因,并非所有 browser/OS/font 大小组合都以相同的方式呈现到屏幕上,它们也不总是遵循字体规范。因此,一般来说,应以避免需要对文本进行像素完美定位的方式创建应用程序。

亚像素文本渲染配置

关于配置特定 browser/OS 组合以支持子像素文本渲染的一些评论:

Windows

  • 支持和启用 DirectWrite(相对于旧的 GDI 方法)的浏览器确实倾向于支持线性、无提示、亚像素文本定位,因此能够(并且通常会)遵循字体前进宽度规范.这包括 Chrome 37+ and Firefox 4+
    • 当硬件加速不可用时,Firefox 默认禁用 DirectWrite,但可以通过将配置 属性 gfx.font_rendering.directwrite.enabled 设置为 true.
    • 来启用它
    • Chrome 37+ DirectWrite 默认开启,但可以通过设置标志 Disable DirectWrite.
    • 禁用
    • Internet Explorer (9+?) DirectWrite 默认开启,但可以通过设置兼容模式禁用。

Linux

Configure your display for anti-aliasing and sub-pixel text rendering by setting up fontconfig for subpixel rendering (usually via Gnome or KDE display manager settings, but can be done manually via fontconfig config files), installing freetype-freeworld (freetype with non-free subpixel rendering support), and adding Xft.lcdfilter: lcddefault into ~/.Xresources for applications without fontconfig support. Set the correct type of subpixel rendering based on your LCD display type.

  • 即使底层显示器支持并配置为子像素渲染,浏览器行为似乎也不一致。
    • Chrome 的最新版本(44 个测试)似乎支持线性、无提示、子像素文本定位,因此通常遵循字体规范。在启用 RGB 子像素文本渲染的 KDE 4.14 上测试。
    • Firefox(38.0.5 测试)似乎进行非线性提示定位,因此不遵循字体规范,即使显示器配置为子像素渲染。我还没有找到强制 Firefox 使用亚像素文本渲染的方法。

Mac OS/X

暂无该平台的信息。

像素级完美定位

如果尽管困难重重,但要构建一个需要像素完美文本定位和检查的应用程序,那么通常有两种方法可以解决:

1) 基于 Canvas 或 DOM 的文本 width/height 测量:参见 Mike Kamermans (Pomax) 的 Calculate text width with JavaScript. See also Font.js

2) 使用OpenType.js从源字体确定文本尺寸,这比上面的方法有效时更快,但并非在所有情况下都有效。