How to fix borked ver­ti­cal met­rics in web fonts

Here’s a re­ally spe­cific prob­lem I’ve run into a hand­ful of times build­ing web­sites with cus­tom fonts: I set some type on my Windows ma­chine, and every­thing works as ex­pected. But when I pull up the same page on a Mac (regardless of the browser) the line height is to­tally dif­fer­ent.

My first thought was that some­thing was wrong with my CSS — maybe there’s a rogue line-height de­c­la­ra­tion that gets ap­plied in one place and not the other? But it turned out the prob­lem was ac­tu­ally the font it­self: It had dif­fer­ent ver­ti­cal met­rics for each plat­form.

How to fix the prob­lem

The best op­tion is to get who­ever pro­duced the font to re-ex­port it with the cor­rect met­rics. This is es­pe­cially true for com­mer­cial type­faces, which you’re usu­ally not al­lowed to mod­ify. Failing that, you can gen­er­ate new font files your­self in one of two ways:

1. FontSquirrel

Upload the file to the FontSquirrel Webfont Generator, switch to Expert mode, check Auto-Adjust Vertical Metrics”, and down­load the gen­er­ated fonts. If you’re lucky, this will re­pair the in­con­sis­tent met­rics and your type will ren­der cor­rectly.

2. Fonttools

If this does­n’t work, you can ad­just the met­rics man­u­ally us­ing the com­mand line and a text ed­i­tor.

Install font­tools and brotli with pip install fonttools brotli. Then cd your way to your pro­ject folder and run ttx borked-font.ttf. This will con­vert the bi­nary ttf into a hu­man-read­able XML file called borked-font.ttx.

Open the ttx file in your text ed­i­tor and look for prob­lems. Specifically, you want to en­sure that:

When you’re done, run ttx --flavor woff borked-font.ttx to con­vert it back into a woff file. Set --flavor woff2 to com­pile straight to woff2, or drop the flag al­to­gether to pro­duce an un­com­pressed ttf. Load up the new file on your web­site, and see if you solved the prob­lem.

Background

OpenType fonts are com­pli­cated pieces of soft­ware. In ad­di­tion to the ac­tual let­ter­forms (stored as Bézier curves), they con­tain ta­bles con­tain­ing the data needed to map these out­lines to uni­code points and en­able things like con­tex­tual al­ter­nates, kern­ing pairs, vari­able fonts, and what­ever else you might want to do.

One of the things that’s stored in these ta­bles is the font’s ver­ti­cal met­rics. This is a set of num­bers that de­fine the height of the as­cen­ders, the depth of the de­scen­ders, and the rec­om­mended line­spacing. Rendering en­gines use these num­bers to cal­cu­late where the first base­line of a text should fall, what the dis­tance be­tween sub­se­quent lines should be, and how much padding to ap­ply be­low the last line. They’re roughly equiv­a­lent to the space above and be­low the raised let­ter­form on a metal sort.

Drawing of relief letter used in letterpress printing

For his­tor­i­cal rea­sons, ver­ti­cal met­rics are stored in three dif­fer­ent places (called hhea, OS/2 typo and OS/2 win), and dif­fer­ent ren­der­ing en­gines get their in­for­ma­tion from dif­fer­ent ones. Apple de­vices gen­er­ally use hhea, Windows uses ei­ther OS/2 typo or OS/2 win, and old ver­sions of MS Office use OS/2 win ex­clu­sively. If the num­bers in these ta­bles aren’t the same, you can end up in a sit­u­a­tion where type ren­ders dif­fer­ently in dif­fer­ent browsers, de­sign tools, or op­er­at­ing sys­tems.

You can get out of that sit­u­a­tion as a user by synch­ing up the num­bers your­self, like we did above. First, we set bit 7 in fsSelect to 1 to ac­ti­vate a set­ting called USE_TYPO_METRICS. This tells browsers on Windows to use the val­ues in OS/2 typo rather than OS/2 win. Then we synched up the val­ues in hhea and OS/2 typo and set OS/2 Win to match the tallest as­cen­der and deppest de­scen­der in the font to avoid clip­ping. Finally we re­com­piled the font with the new met­rics, hope­fully solv­ing our is­sue. There are other ap­proaches to set­tings ver­ti­cal met­rics, but this is the one rec­om­mended by Glyphs and the Google Fonts Team.

If you’re a type de­signer, you can avoid the prob­lem al­to­gether by set­ting the met­rics cor­rectly as you de­sign the type­face, and us­ing au­to­mated test­ing to catch in­con­sis­ten­cies in your build process.

Notes