2

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:

Chrome widths

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:

Firefox Linux widths

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:

Chrome/Firefox Comparison

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:

Firefox Windows widths

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.

Raman
  • 17,606
  • 5
  • 95
  • 112
  • Remove all styles. Use an element that the only style it has is `display:block`. Such element can be ``, ``, `` and so on. Then you add the style and make the comparisson. – Ismael Miguel Jun 18 '15 at 18:16
  • Try adding a `letter-spacing` and check if it is still the same. Notice that different browsers may have different styles. Also, do you have somewhere where we can check the effect? – Ismael Miguel Jun 18 '15 at 18:24
  • Nevermind, found what I needed. – Ismael Miguel Jun 18 '15 at 18:30
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/80922/discussion-between-raman-and-ismael-miguel). – Raman Jun 18 '15 at 18:55
  • the difference between Linux and Windows is in no way surprising: difference operating systems use different shaping engines, so the real question here is which engine Firefox is using on windows, and whether that engine *outside of firefox* also gets it wrong, because this sounds like a shaping bug. – Mike 'Pomax' Kamermans Jun 19 '15 at 17:30
  • @Mike'Pomax'Kamermans I just added an update re. Windows and GDI vs DirectWrite. Forcing DirectWrite on my non-accelerated VM "fixed" it. I'm trying to figure out what FF is doing on Linux and how it differs from Chrome. – Raman Jun 19 '15 at 17:36
  • Also note that DirectWrite is automatically enabled for FF on Windows if hardware acceleration is enabled and available, on a real desktop you'll almost always see the DirectWrite result (VMs are notoriously unreliable there) – Mike 'Pomax' Kamermans Jun 19 '15 at 17:37
  • Note that the Harfbuzz engine used in Firefox (and some versions of Chrome) doesn't take advance widths into account due to incredibly hard font things, best described in https://docs.google.com/document/d/1wpzgGMqXgit6FBVaO76epnnFC_rQPdVKswrDQWyqO1M (text shaping is incredibly hard, you're virtually guaranteed to never see the same things between two different render engines because of all the little things that go into getting it right) – Mike 'Pomax' Kamermans Jun 19 '15 at 17:43
  • @Mike'Pomax'Kamermans Interesting. So if I'm building an application that needs to know the text dimensions, I'm better off using your Font.js library to get the actual rendered dimensions rather than inferring the dimensions from Opentype.js... – Raman Jun 19 '15 at 17:49
  • Font.js is really expensive, for small fonts, OpenType.js is much better by now, but the true trick is to develop you application in a way that does not rely on pixel perfect text, but allows for margins of error. – Mike 'Pomax' Kamermans Jun 19 '15 at 18:09
  • @Mike'Pomax'Kamermans For most applications that is fine, but I'm creating a Google-docs-like editor. So I need pixel-perfect positioning for cursor-related stuff. Perhaps the solution is to use OpenType.js by default, testing the length of various character strings against Font.js to ensure correct positioning, and falling back to Font.js if Font.js and OpenType.js disagree by more than a pixel or so. – Raman Jun 19 '15 at 18:16
  • hm, I'd argue the opposite - if you need a google docs type editor, pixel perfection is the opposite of a problem. Yes, you might get line wraps, but users of google docs and office and the like expect this already. If you were targeting TeX audiences, then it'd matter. I'd first try to compensate by ensuring CSS3 opentype features are all set to the same thing and then ignore pixel shifts. It's a very deep rabbit hole and your time's more valuable than trying to get that to work properly, computably. – Mike 'Pomax' Kamermans Jun 19 '15 at 18:17
  • @Mike'Pomax'Kamermans Hmm... I'm not worried about the line wraps -- I can live with different wrapping on different platforms. BUT, I think I need this to a) position the cursor correctly when navigating the doc (the cursor is just a div, see http://googledrive.blogspot.ca/2010/05/whats-different-about-new-google-docs.html), and b) to know the character position when the user clicks on the document (and again, position the cursor correctly). I don't see another way of going about it given the underlying approach, but I'm happy to be shown the errors of my ways :-) – Raman Jun 19 '15 at 18:28

1 Answers1

2

(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

  • Browsers that support and enable DirectWrite (as opposed to the older GDI method) do tend to support linear, non-hinted, subpixel text positioning, and therefore are able to (and usually do) follow the font advance width specifications. This includes Chrome 37+ and Firefox 4+.
    • Firefox disables DirectWrite by default when hardware acceleration is not available, but it can be enabled via setting the config property gfx.font_rendering.directwrite.enabled to true.
    • Chrome 37+ DirectWrite is on by default, but can be disabled by setting the flag Disable DirectWrite.
    • Internet Explorer (9+?) DirectWrite is on by default, but can be disabled by setting compatibility mode.

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.

  • Browser behavior seems inconsistent even when the underlying display supports and is configured for subpixel rendering.
    • Recent versions of Chrome (44 tested) appear to support linear, non-hinted, subpixel text positioning, and therefore generally follow the font specifications. Tested on KDE 4.14 with RGB subpixel text rendering enabled.
    • Firefox (38.0.5 tested) appears to do non-linear hinted positioning, therefore does not follow the font specifications, even if the display is configured for subpixel rendering. I have not identified a way to force Firefox to use subpixel text rendering.

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.

Community
  • 1
  • 1
Raman
  • 17,606
  • 5
  • 95
  • 112