PuTTY wish typographical-metrics

This is a mirror. Follow this link to find the primary PuTTY web site.

Home | FAQ | Feedback | Licence | Updates | Mirrors | Keys | Links | Team
Download: Stable · Snapshot | Docs | Changes | Wishlist

summary: Make PuTTY use the typographical metrics in TrueType fonts
class: wish: This is a request for an enhancement.
difficulty: tricky: Needs many tuits.
priority: low: We aren't sure whether to fix this or not.

A user reported that the 'Inconsolata' font by Raph Levien works badly in Windows PuTTY, because the Windows terminal front end sets the terminal's character-cell height based on the obvious font metrics provided by the Windows text rendering API (usWinAscent and usWinDescent). Those metrics are based on the ink extent of the font rather than on the typographically ideal line spacing. And Inconsolata recently gained support for Vietnamese text, with multiple diacritics which increase the font's ink extent, so the lines are now more widely spaced than is desirable.

Apparently there's a separate set of metrics called the 'typographical metrics', and a flag in the font header to request they be used.

Our correspondent forwarded on some useful implementation notes from Raph Levien:

The first step is to use GetFontData to read the relevant tables. One is the OS/2 table, which has tag 0x322F534F, and the other is head (tag 0x64616568). A good reference for bit-bashing these tables btw is sfntly; here's the code for OS/2 and head. Both are quite small, OS/2 is typically 96 bytes and head is 54 bytes, so the buffers can be stack allocated. I recommend first calling with out a buffer to get a size, then calling a second time with min(sizeof(stack buffer), size reported). If the size reported from head is below 54 or the size of OS/2 is below 74, then bail (the sizes can vary somewhat based on table version).

From the head table you'll need unitsPerEm (big-endian uint16 at offset 18), and from OS/2 you'll need fsSelection (uint16 at 62), sTypoAscender, sTypoDescender, and sTypoLineGap (int16 at 68, 70, and 72). The logic goes like this. If bit 7 is set (fsSelection & 0x80 != 0), then compute ascent and descent from the typo metrics instead. I'd recommend combining line gap and ascender, as this will minimize clipping of accented characters. The math goes like this:

round_up(units, font_height, unitsPerEm) = floor((units * font_height + units - 1) / unitsPerEm)

new_ascent = round_up(sTypoAscender + sTypoLineGap, font_height, unitsPerEm)
new_descent = round_up(-sTypoDescender, font_height, unitsPerEm)

The sum of the two is your new height, to be used in place of tm.tmHeight. You will also need to subtract tm.tmAscent - new_ascent from your y coordinate when drawing text (ExtTextOut) to avoid the baseline being too low, as these drawing commands use the "top" as a reference point, and the GDI understanding of that point is the point above the Vietnamese accented characters, which is quite high.

Some thoughts from our own side:

Existing worked example: commit 34d7602954d4483b3bc9db700e7df2c15348947a in LibreOffice seems to be doing something relevant to this.

If you want to comment on this web site, see the Feedback page.
Audit trail for this wish.
(last revision of this bug record was at 2020-12-20 12:10:49 +0000)