Vertical Metrics How-To

Indices : "How To" Section : Setting Cross-Platform Vertical Metrics

Originally written by John Hudson in a thread on Typophile:

[Note that the descender values in the hhea table and the OS/2 sTypoDescender field are properly expressed as negative integer (presuming the descender goes below the baseline), while the OS/2 usWinDescent value is expressed as a positive integer. For the purpose of the discussion below, all values are treated as if they were positive integers, since what matters is the sum of the values.]

The Discrepancy

Apple originally spec'd the hhea table as part of the TrueType spec. This includes three values for vertical metrics (despite the fact that the full name of the table is 'Horizontal Header'):

- Ascender
- Descender
- LineGap

Together, these values determine the default linespacing in Mac applications that use system font metrics. [This is typical of word processing apps, text editors, and other apps that that do not , as a default, handle leading independently of font size. Page layout apps and other professional graphics software will typically not use the font metrics to determine linespacing, but will expect the user to set leading values or will calculate 'auto' leading as a percentage of the font size.]

When Microsoft licensed TrueType, and began the extensions of the sfnt table format that would lead eventually to OpenType, they decided to spec their own vertical metrics values in the grab-all OS/2 table. One of the reasons for this was the need to be able to create fonts that had backwards compatible metrics with existing Windows bitmap fonts. This led to the OS/2 table containing two different sets of vertical metrics data:

- sTypoAscender
- sTypoDescender
- sTypoLineGap


- usWinAscent
- usWinDescent

As originally intended by Microsoft, the 'Typo' values were intended to be used by apps to calculate default linespacing, in exactly the same way as the corresponding hhea table values on the Mac. The 'usWin' values were intended to define clipping zones for the font, and as such were expected to correspond to the actual vertical extremes of the tallest and deepest glyphs in a font, to ensure that these would not be clipped (i.e. have pixels chopped off on screen).

If this intent were followed, one could obtain reliable cross-platform default linespacing by making the hhea vertical metrics values equal to the corresponding OS/2 'Typo' values.

However, almost every Windows app developer -- including Microsoft's own app teams -- have incorrectly calculated default linespacing based on the OS/2 'usWin' values instead of the correct, 'Typo' values. It seems likely that this is the result of some application developers noting that using the 'usWin' values ensured that all glyphs in most fonts avoidec contact with any glyphs on lines above or below, while this was not always the case with the 'Typo' values.

So we're now in a complicated situation in which:

a) the specification clearly indicates that the correct way to produce cross-platform linespacing is to make the OS/2 'Typo' and hhea vertical metrics equivalent;

b) basically all Windows apps calculate linespacing incorrectly;

c) therefore, the only way to get reliable cross-platform linespacing is by deliberately ignoring the specification, and by making the hhea table values equivalent to the OS/2 'usWin' values.

One can sidestep the issue by making the sum of the OS/2 'Typo' and 'usWin' values equivalent, so that all three sets of values produce equivalent results. In my experience, this approach -- as outlined below -- will produce good results for most text faces and probably the majority of display faces. But display faces with abnormally long extenders can be a problem, because one must either clip the extenders or set all the values to provide clearance, which produces abnormally large linespacing. We hit this issue when we were making the AAT version of Zapfino that ships with Mac OS X.

Best Practices

Leaving aside the for-now unsolveable issue of such display faces, here are my recommendations for setting the OS/2 and hhea font metrics.

1. OS/2 usWin values:
These should be set to determine the default linespacing, since this is what almost all Windows apps will actually use. For most designs, this will also ensure that glyphs are not clipped, since default linespacing typically includes enough 'leading' to provide clearance. If non-clipping is vital, you simply have to ensure that these values serve double-duty. If you have no very tall or deep glyphs that you want to avoid clipping, a good basic setting for these values will equal the UPM value of the font multiplied by 1.20 or 1.25, as this is a reasonable default leading. The actual value of the two fields should reflect the design. In Latin faces this will typically mean than a larger value is assigned to the usWinAscent field, while in some Arabic fonts the values might favour the descender. For the purpose of this explanation, let's assume a Latin text face with typical TT UPM of 2048, which we'll multiply by 1.25 (=2560) and assign like this:

- usWinAscent = 1836
- usWinDescent = 724

2. Os/2 'Typo' values
We want to make the sum of these values equivalent to the usWin values, i.e. to add up to 2560. My recommendation here is to make the sum of the sTypoAscender and sTypoDescender equal to the font UPM value (2048), and to assign the different between this and 2560 to the sTypoLineGap field. Again this should correspond to the design, and you should begin by measuring your typical ascender height and descender depth, e.g. measuring the lowercase d and p. Then, if the combined height of these is shorter than 2048, add the difference in equal amounts to the sTypoAscender and sTypoDescender. So let's assume that the typical height of the ascender in our example font is 1430 and the typical descender depth is 550, i.e. a total height of 1980.

2048 - 1980 = 68 and 68 /2 = 34

So this gives us

- sTypoAscender = 1464
- sTypoDescender = 584 (actually -584)

and the difference between 2048 and the combined usWin values of 2560 gives us

- sTypoLineGap = 512

3. hhea values
Now setting the hhea values and obtaining cross-platform linespacing is easy, because you can simply make these values match the corresponding OS/2 'Typo' values:

- Ascender = 1464
- Descender = 584 (actually -584)
- LineGap = 512


Until recently, I was recommending

OS/2 TypoAscent == hhea Ascender
OS/2 TypoDescent == hhea Descender
OS/2 TypoLineGap == hhea LinaGap


(OS/2 TypoAscent + OS/2 TypoDescent + OS/2 TypoLineGap) ==
(OS/2 WinAscent + OS/2 WinDescent)


(hhea Ascender + hhea Descender + hhea LinaGap) =
(OS/2 WinAscent + OS/2 WinDescent)

But Read Roberts at Adobe has been conducting tests on different Mac apps, and has found out that different apps calculate the baseline-to-baseline distance using different combinations of the hhea metrics, e.g.:

Mac FontBook
baseline-to-baseline == hhea.Ascender + hhea.lineGap + hhea.Descender

Mac TextEdit, BBedit
baseline-to-baseline == hhea.Ascender + hhea.lineGap + hhea.Descender

Mac MS Word for OSX
baseline-to-baseline == hhea.Ascender + hhea.Descender

In addition, clipping is handled differently in different applications, and different methods are used to calculate the distance from the top of the text frame to the first baseline.

Based on this analysis, Read's recommendation, with which I reluctantly have to agree, is that cross-platform compatibility requires, the hhea metrics to be set as follows:

hhea.Ascender == OS/2.winAscender
hhea.Descender == OS/2.winDescender
hhea.lineGap = 0

Which is actually what I was recommending a few years ago, and what the MS Font Validator recommends, even though it is contrary to the OT spec.

Update to the update

The question of how best to set the hhea metrics is under discussion again. Microsoft have specified an fsSelection bit setting to force applications to use OS/2 Typo metrics instead of Win metrics, in which case it would seem best to leave the hhea metrics equivalent to the OS/2 Typo metrics as originally recommended.

Update to the update to the update (7 June 2003)

Microsoft have specified fsSelection bit 7 as an instruction to applications to use the OS/2 Typo metrics to calculate linespacing (in preference to the Win metrics). However, the implementation of this in Office 2007 (the first applications to respect the bit) is less than one would hope. The clean separation of linespacing from clipping is only implemented in the new math mode (used for typesetting math equations); elsewhere in e.g. Word, the bit is respected and the linespacing is calculated according to the Typo metrics, but an assumption is made that clipping is still constraine by line height. In effect, instead of determining the linespacing according to the clipping zone of the Win values, Office is now determining the clipping zone according to the linespacing of the Typo values. [Insert 'irony mark' here.]

So what is my current recommendation for setting metrics? There's no easy answer to that other than try to set all three sets of metrics to sum to the same total if at all possible, in which case it won't matter much, in terms of cross-platform compatibility, whether the hhea metrics mirror the OS/2 Typo metrics or the OS/2 Win metrics.

Line Height Info
Font Dimensions - Generate Problem
FL Vertical Spacing
WinAscent... LineGap... Font BBox?
Ascenders Cut Off in MS Word
Question about Vertical Metrics

Additional Articles:
Font Metrics (PDF) by Karsten Luecke

Syndicate content