Text Dimensions Are Hard

This blog post natters on about SVG/Emacs font issues, but ends with a question to an Emacs-knowledgeable audience. Feel free to skip to the end if you know Emacs stuff!

Anyway, eplot, the Emacs charting library, needs to know the actual text dimensions for certain things.

Not for the labels on the x axis here — eplot generates SVGs, and SVG can anchor a text to the middle. It even works well with exotic fonts like Jolie Romantique, which is my go-to font for testing weird font rendering artefacts:

Even in the presence of strange things like ligatures that represent swooshes, SVG gets it right with some renderers:

But this fails when using things like rsvg-convert. Hey, Rust Evangelical Strikeforce! Get it together!

Anyway! I wasn’t really going to whine about font rendering, but whine about Emacs. In eplot, I need to see how much space a piece of text will really end up taking, and I need that to calculate how much space I need for things like the Y axes there. And there’s really no other way to determine that than to render the text and see what you get (due to fonts having weird/useless general data, not to mention ligatures and stuff).

For instance, to have these labels centred under each bar, I need to know how tall a typical non-descending character (e.g., “x” and not “g”), because when you specify where to put a text in SVG, the position it renders from is that baseline.

That approach even works with exotic fonts.

So… determining the actual room a text will take is necessary — not only to place the texts at the right place, but to determine how many of the texts we have room for on the axes. We can “eyeball” these things sometimes — we can take the font size and make “guesses” based on that… but in the chart above, the font size used is 60.

And in this chart, where the characters are approximately the same size visually, the font size used is 30!

And that’s the problem I’m whining about here!

OK, if you’re using very normal, boring fonts at lower point sizes, these problems aren’t massive, but once you start stepping up the point sizes, the charts soon look pretty ugly, what with off-centred tick marks and the like.

For instance, did you notice that the vertically rendered x-axis tick marks in the chart above were wrong? Me neither, until I started writing this.

There. Fixed. It was using the height of an “x” to determine the baseline, because that’s generally correct with text (“Thing” both ascends and descends, and you have to be consistent), but if the labels are all numerical, they just ascend from the baseline, making them off-centre.

*phew*

Anyway, an obvious way to see how much space a text takes is to create an SVG, convert it to PNG, strip away the background and then see what’s left. So let’s see how much time it takes.

(benchmark-run
  (cl-loop for size from 0 upto 99
           collect (eplot--text-size-1 "Testing" "futura" 'bold size)))
=> 22.8

Yeah, 22 seconds to do 100 texts using ImageMagick convert to determine the sizes. Because ImageMagick is dog slow on SVGs — it calls out to other programs to do the rendering.

(In practice, this isn’t quite as horrible as it sounds — for a typical chart, eplot only has to measure a couple of strings. And it caches the values.)

(benchmark-run
 (cl-loop for size from 0 upto 99
          collect (eplot--text-size-1 "Testing" "futura" 'bold size)))
=> 4.694829387

But if I do the rendering with rsvg-convert, it takes a fifth of the time. But now we need two external programs, one of which isn’t installed on that many systems…

Emacs does have built-in things to determine the actual width of a text (because I built it into Emacs 😉), so let’s try that:

(benchmark-run
  (cl-loop for size from 0 upto 99
           collect (eplot--text-width-2 "Testing" "futura" 'bold size)))
=> 0.19

Hey, nice! 0.2s is a lot faster than 4.7s (or 22s).

So what am I whining about? Here’s what I’m whining about: Emacs doesn’t have a way to see how tall a text is!

What am I talking about! Of course it does! But no.

Emacs has line-pixel-height, and that’s the room Emacs reserves for a line. (The red box there shows it.)

So that’s the size of the “F”, right?

No, it’s just using the font metrics, and there’s no way to get Emacs to tell me how tall that “o” really is.

I think! Does anybody know how to make Emacs cough out some data on that? It sucks to have to rely on external programs for stuff like this.

C’mon! Emacs power!

Leave a Reply