Skip to content

text: Use font metrics to determine line heights#31291

Merged
QuLogic merged 3 commits intomatplotlib:text-overhaulfrom
QuLogic:font-heights
Mar 20, 2026
Merged

text: Use font metrics to determine line heights#31291
QuLogic merged 3 commits intomatplotlib:text-overhaulfrom
QuLogic:font-heights

Conversation

@QuLogic
Copy link
Copy Markdown
Member

@QuLogic QuLogic commented Mar 12, 2026

PR summary

As outlined in #31220, we follow the process from the CSS Inline Layout module, specifically:

  1. The default ascent and descent come from the OS/2 font table, or failing that, the hhea table, with final fallback to the measurement we used to do.
  2. If linespacing (cf line height in CSS) is normal, then we do as before and size each line based on the maximum ascent/descent of its contents. Additionally, apply the line gap from the font metrics as half-leading around each line.
  3. If linespacing is a float, then scale it by font size of the first available font, and keep it fixed for each line.

Additionally, the first commit changes mathtext to use the x-height from the default fonts. We can read that from font metrics, but I thought I'd leave the general case to something like #31048.

Note, this is based on the text-overhaul-figures branch, so more test images than necessary are changed here. Please look at the last commit for the test images. Most test image changes are minor, maybe half a pixel or so with the text. The main ones you probably want to look at are those with multiple lines, such as 'basictext_wrap.png`.

One thing to decide is whether to apply the line gap for a single line. Previously, I don't believe we ended up doing that, because the line spacing was added before the second and subsequent lines. This PR currently always adds it, which means the text bounding box is taller in all cases. This causes such changes as the fancy bbox patches in boxarrow_test_image.png to all be larger, or e.g., every multi-artist legend to be larger. To minimize such changes, we can decide to drop the line gap for single-line text entries, if we want?

AI Disclosure

None

PR checklist

@QuLogic QuLogic added this to the v3.11.0 milestone Mar 12, 2026
@github-project-automation github-project-automation bot moved this to Waiting for other PR in Font and text overhaul Mar 12, 2026
@QuLogic QuLogic moved this from Waiting for other PR to Ready for Review in Font and text overhaul Mar 12, 2026
@QuLogic
Copy link
Copy Markdown
Member Author

QuLogic commented Mar 12, 2026

One thing to decide is whether to apply the line gap for a single line. Previously, I don't believe we ended up doing that, because the line spacing was added before the second and subsequent lines. This PR currently always adds it, which means the text bounding box is taller in all cases. This causes such changes as the fancy bbox patches in boxarrow_test_image.png to all be larger, or e.g., every multi-artist legend to be larger. To minimize such changes, we can decide to drop the line gap for single-line text entries, if we want?

Discussed on the call today and decided not to add line gap for a single-line text. This should be closer to what we used to do, and will minimize the number of changes that occur due to this.

@QuLogic QuLogic force-pushed the font-heights branch 2 times, most recently from 0264fde to 05c7199 Compare March 14, 2026 04:13
@QuLogic
Copy link
Copy Markdown
Member Author

QuLogic commented Mar 14, 2026

I've now pushed fixes to the text_placeholders fixture, which should reduce the number of test image changes. Also fixed some tests that explicitly check values of text locations/sizes/etc.

@QuLogic
Copy link
Copy Markdown
Member Author

QuLogic commented Mar 16, 2026

There is one (set of) regression for which I have not pushed the image updates (and so why tests are failing). All the vector multi-font tests now have a reduced line height:
multi_font_type42_pdf
vs before:
multi_font_type42-expected_pdf

This is because the test uses Computer Modern as its primary font, with fallback to DejaVu Sans for the other characters (which is on purpose to test fallbacks). While the ascent/descent metrics for Computer Modern are larger than the "lp" measurement, that font does not have a nonzero line gap. Additionally, since we don't do the fully-correct thing of looking at all font metrics for a used line, while we do see the ascent/descent of DejaVu Sans, we don't know that DejaVu Sans has a larger line gap. Since we aren't applying any other line spacing, all the lines now look a bit cramped. This last thing cannot be fixed until we move to processing all text in the backend where we can inspect all glyphs at a time.

I see three options for handling these:

  1. The easiest fix here would be to set a fixed line spacing in the tests themselves.
  2. If we think this might be something that users might complain about, then we can hard code a line gap for Computer Modern only to match the !~120% line spacing that we used to do. (But keep in mind that Computer Modern is the old default font, not the current one, and this would only affect multi-line text.)
  3. Just ignore it entirely.

Separately, I did mention that I would run the constrained/tight layout tests without placeholders so that we could compare the results. I did that, and posted the results to a gist here: https://gist.github.com/QuLogic/254ee85ecaa778dd44635924d706acea

@tacaswell
Copy link
Copy Markdown
Member

Consensus on call is to ignore for now and let it be cramped for now. If we fix this later a few tests would be acceptable.

QuLogic added 3 commits March 20, 2026 15:03
This is minimally different from the `x` measurement, but technically
more correct. We still do the measurement for fonts we don't ship, but
that may change with Unicode Math fonts in the future.
This follows from CSS' default for line height. At the moment, the
behaviour has not been changed, and still just falls back to 1.2 for
'normal'.
We follow the process from [the CSS Inline Layout
module](https://www.w3.org/TR/css-inline-3/), specifically:

1. The default ascent and descent come from the `OS/2` font table, or
   failing that, the `hhea` table, with final fallback to the
   measurement we used to do.
2. If `linespacing` (cf line height in CSS) is normal, then we do as
   before and size each line based on the maximum ascent/descent of its
   contents. Additionally, apply the line gap from the font metrics as
   half-leading around each line.
3. If `linespacing` is a float, then scale it by font size of the first
   available font, and keep it fixed for each line.

However, if we are drawing a single line, then we do not add the line
gap around the line, to keep them a similar height as before.
@QuLogic
Copy link
Copy Markdown
Member Author

QuLogic commented Mar 20, 2026

OK, pushed the images to the text-overhaul-figures branch, and dropped them from here. Will merge after CI is okay.

@QuLogic
Copy link
Copy Markdown
Member Author

QuLogic commented Mar 20, 2026

So it looks like some of these tests are failing on platform/arch-specific ways. What I'll do for now is merge this one since it's holding up everything, and put in a smaller PR upping the tolerances on the ones that are broken.

@QuLogic QuLogic merged commit 1f064dc into matplotlib:text-overhaul Mar 20, 2026
23 of 33 checks passed
@github-project-automation github-project-automation bot moved this from Ready for Review to Done in Font and text overhaul Mar 20, 2026
@QuLogic QuLogic deleted the font-heights branch March 20, 2026 19:45
@QuLogic QuLogic linked an issue Mar 27, 2026 that may be closed by this pull request
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Should we use font metrics for line height instead of "lp"?

3 participants