Re: [hackers] [dwm][PATCH] drw: use cap height for optical text centering

From: Alessio Artoni <aartoni_AT_artixlinux.org>
Date: Tue, 19 May 2026 21:37:42 +0200

On 5/19/26 6:18 PM, Storkman wrote:
> For dwm, the drawing area always has height (ascent + descent + 2).
> Is there a guarantee that descent + cap_height == ascent for every font?

No, and that's the whole point. For most fonts ascent - cap_height !=
descent; the space above the cap line (internal leading) differs from
the descent zone.
The old formula assumes they're equal while this patch stops assuming.

> In fact, for Fira Code, if we have ascent=1800, descent=600, and cap=1377,
> that would put the baseline at 1888.5, with descent up to 2488.5.
> That's 88.5 under the box.

Your math is correct in font units. In pixels at size=10 (asc=19,
desc=7, h=26, bh=28, cap=15 measured):

Old: ty = 1 + 19 = 20. Descent bottom: 27. bh: 28. 1px spare.
New: ty = (28 + 15) / 2 = 21. Descent bottom: 28. bh: 28. Exact fit.

> Should the bar height then be calculated as (ascent + descent + cap/2)?

Fair point, we can adjust bh to compensate.

With your version, symmetric fonts should get pixel-identical output.
Asymmetric fonts get the bar height needed to accommodate the recentered
baseline. I'll include this in v3.

> I just find it difficult to accept that correct code should have a step
> like "measure the outline of letter H in particular, and use it as a constant"

Cap height is defined as the height of flat-topped capitals (H, I, E).
Measuring H. However I fully agree, it seems an heuristic even if it is
the definition.

> You could potentially read sCapHeight from the 'OS/2' table through FreeType.
> (See: https://st.suckless.org/patches/fontmetrics/st-fontmetrics-0.9.3.diff)

I explored this first. The issue is that FT_Get_Sfnt_Table is a FreeType
function, not an Xft function. Xft links FreeType internally, but
calling FT_Get_Sfnt_Table directly requires adding -lfreetype to LDFLAGS.

Measuring H via XftTextExtents8 uses only Xft, which is already linked.
No new dependency. And it works for fonts without an OS/2 table (bitmap
fonts, some CJK fonts where sCapHeight is 0).

That said, the right long-term fix is for Xft to expose cap height
directly. I've opened an issue upstream for that:
https://gitlab.freedesktop.org/xorg/lib/libxft/-/work_items/26

If that lands, drw.c can drop the XftTextExtents8 measurement and
read the field directly.

>> + font->cap = MAX(xfont->ascent - xfont->descent, 0);
> I'd just get rid of this entirely.

Agreed. With the if/else at draw time the old formula is the explicit
fallback, so the MAX line serves no purpose. ecalloc zeros the struct,
so cap defaults to 0 if the measurement fails.

> if (usedfont->cap)
> ty = y + (h + usedfont->cap) / 2;
> else
> ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent;

Agreed, this is clearer.

I'll send v3 once we've settled the remaining points.
--
aartoni




Received on Tue May 19 2026 - 21:37:42 CEST

This archive was generated by hypermail 2.3.0 : Tue May 19 2026 - 21:48:37 CEST