Different TypeFaces render text at different size

We use the default Juce TypeFace for our UI (‘Lucida Grande’ on OSX).
However we have switched to using ‘Noto’ (https://www.google.com/get/noto/) recently because we offer translations for our UI in Japanese, Chines etc.
The thing is, using a Font size of let’s say 12.0f will show the text at different sizes for the different TypeFaces. In this case the ‘Noto’ TypeFace will render it quite a bit smaller.

I can get around most issues by using a custom LookAndFeel and overriding getComboBoxFont, getLabelFont etc and applying a scale factor to the Font returned depending on the TypeFace being used.

I use TypeFace::getHeightToPointsFactor for this, it shows something like 0.8 for ‘Lucida Grande’ and 0.6 for ‘Noto Sans JP’. So with a translation of these values i get a long way. However i have quite a few custom Component::paint routines that do not use a LookAndFeel but still use Fonts to draw text. All these routines won’t have this magic scaling applied.

So my question is in the end, why do i have to go this length, why does Font(12.0f) not render the same text for different TypeFaces at the same size? If i can do this scaling manually why doesn’t Juce do this out of the box?

And of course if someone else has some tips and tricks regarding this issue, i would love to know.

Not an expert, but this seems to be a characteristic of fonts. The way they are individually designed, they do not have to be the same “apparent size” at the same font. There is no regulation concerning actual size. If you’ve ever done font replacement in text documents, or Photoshop, just scrolling through different fonts without changing the size shows that the apparent visual size changes radically between fonts. Anybody can design a font, and anyone can design the font to be whatever apparent size it is. I don’t think JUCE could do anything about it.

You can adjust font size according to the font X height. Render the string "x" to a path and check the bounding box height of that path. (or is there a function in JUCE go get those metrics by now?)

The height of a font is not that straightforward, see for instance the various metrics here: How to: Obtain Font Metrics (MSDN).

Or open the fonts panel in DemoRunner, go through the list and observe the differences in line advance.

1 Like

You could also try setting the font height in points.


Somehow overlooked the Font:: withPointHeight all this time. Damn, should have used this from the beginning.
I see it uses getHeightToPointsFactor internally.

@stephenk, The Juce class says that when you construct a Font, you pass it a fontHeight, this fontHeight supposed to be in Pixels. So wouldn’t you expect it to render the font in the amount of pixels specified? The whole UI of Juce works in pixels, for paths, bounds etc. Why wouldn’t this work for Fonts.
I tried PhotoShop of course, and PhotoShop does not change the size (you only see very small marginal changes) when changing Fonts, in Juce it can be up to 30% difference in size.

    /** Creates a sans-serif font in a given size.

        @param fontHeight   the height in pixels (can be fractional)
        @param styleFlags   the style to use - this can be a combination of the
                            Font::bold, Font::italic and Font::underlined, or
                            just Font::plain for the normal style.
        @see FontStyleFlags, getDefaultSansSerifFontName
    Font (float fontHeight, int styleFlags = plain);

Below a screenshot of Juce rendering the same text using Font(100.0f) for ‘Lucida Grande’ top and ‘Noto Sans’ bottom.

Wait a minute i’m confused now. Font::withPointHeight gives me uniform results across fonts but Font::withPointHeight(100.0f) renders larger (because it expects points i guess) than when i specify Font(100.0f).
PixelMator and PhotoShop seem to be able to give uniform results across fonts by allowing me to specify the Font size in Pixels. What method should i use in Juce to accomplish this?

AFAIK point height is simply the pixel height multiplied with some constant (commonly one pixel == 3/4 points).

That is, unless it uses a different definition of font height.

The point ii’m trying to make is this, if Font::withPointHeight using points can give me unified results across TypeFaces using points as unit, why can’t the Font constructor that takes pixels as argument not do the same but then using pixels as unit.

If you ask for a font with a given pixel height, you’ll get one where the ascenders + descenders fit into that number of pixels. The bodies of the font may be any size, because obviously different fonts have different ratios between their ascenders/descenders + body sizes.

If you ask for a number of points, then that’s the body size, but you don’t have control over the total height of the resulting font, so it could have ascenders + descenders that are too big for the space you’re putting it in.

It’s not that either of these ways of asking for a font is more correct than the other, they’re just different. Use the one that’s appropriate for your app.

1 Like

I do agree there are differences between TypeFaces and how heights will differ. But i don’t understand why the difference is so big in Juce compared to other tools/techniques.
Look at this example using an online CSS editor:

And compare it with how it renders in Juce using 200px:

Can you explain me why Juce renders these TypeFace with such a big difference in size?

Probably because CSS specifies the height of the body, not the overall height like juce does. But like people already said here, just use withPointHeight if that’s what you need. We can’t change the juce default because that’d silently break almost everybody’s apps.

Alright, i’ll go with the withPointHeight and other workarounds for now.
Would have been great to be able to easily switch between Fonts more easily though as our code base is code is full of g.setFont(Font(11.0f), label->setFont(12.0f), etc…

The docs describe both height and point height as returning the same metric though:

This is the maximum height, from the top of the ascent to the bottom of the descenders.