Hint, hint, nudge, nudge, say no more!

At the time of writing, the task with the most votes in Qt's bug tracker is the somewhat vaguely named umbrella task "Allow better font rendering for projects like Koffice".

The background for the task is a lacking cross-platform support in Qt for text layouts done in the design metrics of the font. Depending on the font back-end in the operating system, text in Qt might be hinted, meaning that the glyphs are altered slightly before they are painted on screen, optimized to be painted in a specific size and resolution. For high-density devices, such as printers, hinting the glyphs is unnecessary, but for lower-density displays such as desktop monitors, it will make the text crisper and easier to read by aligning its outlines to the pixel grid.

Typical examples are Windows' GDI, which hints glyphs in vertical and horizontal directions (full hinting), and Mac OS X's Core Text, which does absolutely no hinting at all. With FreeType, the text rasterization technology used on Linux, this is customizable between full hinting, just vertical hinting and no hinting. Look at the following screenshot to see the difference between Windows and Mac text rasterization.

GDI/Core Text text rasterization comparison

Looking at e.g. the 'e' in "Lorem" in this screenshot, you'll see that the GDI version is sharper and more defined. This is due to the hinting altering the shapes slightly to align them to the pixel grid, to avoid having parts of the outline that are centered between two pixels, giving a slightly more smudgy appearance.

A second thing you will notice, if you zoom in on the two examples, is that every instance of a given character in the GDI version is rasterized exactly the same, while in Core Text its appearance will differ depending on the character's position in the string. This is due to subpixel positioning of the glyphs in the Core Text version, causing the glyphs to cover different fractions of pixels (and subpixels) based on its position (see my blog about subpixel positioning in the raster paint engine.) When a glyph is aligned to the pixel grid, so are its metrics, so in GDI, each glyph is positioned at whole pixels, giving the exact same rasterization for each instance.

The third thing to notice, is the one that originally triggered QTBUG-10615: The widths of the lines of text in the GDI version are different from in the Core Text version. In the example above, the difference is small, but very visible. In other examples, it might even affect properties of the layout, such as line breaks in the text. For text in user interfaces, this will be largely irrelevant. The UI can adapt to minor differences in the metrics of the text, and the main concern is that the application looks at home on the platform where it's running, so this is what Qt has focused on in the past. In more typographically sensitive use cases, however, it could be a disaster, as it prevents the typographer from predicting the size and layout of the text when moving to a device with a different density, such as a printer. This is such a problem that some typographical applications, such as Microsoft Word, will first do its layout in design metrics, then calculate the error in metrics between the rendered glyphs and the glyphs in the layout, and then distribute this error evenly out between words and between letters for more accuracy (but giving some odd effects in lines of text that do not contain any spaces). In Qt, we've ignored the problem so far.

Together with Jiang, I've spent the last few weeks looking at this issue, and in order to rectify the situation we'll add a new property to QFont in Qt 4.8: QFont::hintingPreference() (the naming refers to the fact that behavior is not guaranteed, but that we'll try to do our best given the capabilities of the font back-end. Please let me know if there are any views on what the API should be, as it is not set in stone yet.)

The platform support has some limitations: On Core Text, the text is already typographically correct, and text in native user interfaces is also rendered unhinted, so there is no support for any hinting style other than "None" on this platform. On GDI, there's no way of turning off hinting, so older Windows platforms will not benefit from this API either.

The two major platforms on which we've focused are:

  • FreeType, which can be used to rasterize text on Linux platforms and Symbian. We can support all three levels of hinting on top of this back-end: "None", "Vertical only (light)" and "Full".
  • DirectWrite, which is supported on Windows Vista after the Platform Update has been installed, and on Windows 7. We can support full hinting and vertical hinting on this back-end.

(note: In order to get support for the DirectWrite back-end, you need to build Qt with the configuration flag "-directwrite" which will make your applications depend on DirectWrite support from your target platforms.)

Lets look at the same screenshot as before, but with DirectWrite rasterization added for comparison:

GDI/Core Text/DirectWrite text rasterization comparison

Looking at this screenshot, you'll see that DirectWrite renders the text with the same metrics as Core Text (the design metrics in the font), thus giving equal line widths as in the Core Text version. If you zoom in, you will also see that the DirectWrite engine positions glyphs at subpixels, giving a range of slightly different rasterizations for each character based on its position in the string. Finally, you'll see that the DirectWrite rasterization is still crisper than the Core Text equivalent. This is because DirectWrite still hints the glyphs in the vertical direction (look at e.g. the horizontal bar in the 'e', which lies between two pixels in the Core Text rasterization). It doesn't affect a horizontal text layout, but gives a slightly crisper rasterization which some people consider more readable than the completely unhinted text.

To complete the comparison, lets add two examples rendered with the FreeType engine as well:

GDI/Core Text/DirectWrite/FreeType text rasterization comparison

As is visible here, the "no hinting" case corresponds to the Core Text rasterization, the "vertical hinting" (what is known as "light" in FreeType) corresponds to the DirectWrite rasterization, and the "full hinting" case corresponds to the GDI rasterization.

Please allow some time for these changes to reach mainline. The FreeType changes are still being polished, and the DirectWrite back-end needs to go through a few stages of testing and integration before it reaches the outside. Soon should be able to support typographically correct text rendering in your application for these three main back-ends.

update: Added "full hinting" example to final image as requested.

Blog Topics: