I am loading the OpenType webfont Open Sans
via the Google Fonts API / CSS.
In both Chrome 43 (Linux+Windows) and Internet Explorer 11 (Windows) the browser renders the text exactly as specified in the font. However, in Firefox 38.0.5, the text width and/or spacing is rendered differently for some characters. All font variants are at the default value ("normal").
As an example, we can use the characters 1
, a
, b
, and i
. Open Sans "unitsPerEm" is 2048. Therefore, at a font size of 18.0px, the width of 30 characters of each of the above characters should be as follows based on 1/u * p * c * w where u = 2048, p = 18.0, c = 30, and w is the advance width of each char (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 | +----------------------------------------+-----------+
This (JSFiddle) uses the canvas method measureText
to output the width in pixels of 30 characters of each of 1
, a
, b
, and i
.
Chrome text lengths match the expected values exactly:
Firefox Linux text lengths do not match for any of the characters except a
, even after accounting for the fact that Firefox does not provide subpixel accuracy:
I have confirmed that the width reported by canvas is indeed what is output by both Chrome and Firefox -- the following image shows a red background, with Chrome's text in black, and Firefox's text in white -- the widths match the outputs above according to the Gimp "Measure" tool. Firefox's b
and i
is too wide, and 1
is too narrow:
And as a side note, Firefox Windows text lengths are not even consistent with Firefox Linux -- the a
and b
widths are now as expected, but 1
and i
are still incorrect:
This is a clean Firefox profile with default settings and no extensions installed.
Can someone explain what is going on, and how to force Firefox to render the font according to the font specification?
UPDATE: On Windows, setting the preference gfx.font_rendering.directwrite.enabled
to true
fixes the problem (which I believe is the Firefox default when hardware acceleration is available, this setting just forces it on even if hardware acceleration is unavailable, such as on my test VMWare system). DirectWrite has been the default in Chrome on Windows since version 37. The Linux behavior is still unexplained. This blog post explains more about DirectWrite rendering in Firefox on Windows.
(This answer summarizes the main points raised in the question comments, as well as a bunch of additional research done after the question was posted.)
For various and complex reasons, not all browser/OS/font size combinations render to screen in the same way, nor do they always follow the specifications of the font. Therefore, in general, applications should be created in a way that avoids needing to do pixel-perfect positioning of text.
Subpixel Text Rendering Configuration
Some comments on configuring specific browser/OS combinations to support subpixel text rendering:
Windows
gfx.font_rendering.directwrite.enabled
totrue
.Disable 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.Mac OS/X
No information for this platform yet.
Pixel-perfect Positioning
If, despite the difficulty, one is building an application that requires pixel-perfect text positioning and inspection, then there are generally two ways to go about it:
1) Canvas or DOM-based text width/height measurement: See Calculate text width with JavaScript. See also Font.js by Mike Kamermans (Pomax).
2) Use of OpenType.js to determine text dimensions from the source font, which is faster than the method above when it works, but does not work in all cases.