Juce 8 applyFontToAllText appears to be text-size-dependent

Hi,

I am in the process of porting my project to JUCE 8, which has been mostly flawless except for some unexpected behaviour with label rendering: labels seem to not be inheriting the default LookAndFeel font (as if applyFontToAllText had no effect whatsoever). Assigning the font explicitly for each label works as expected).

I tried reverting back to the old software renderer but it seems to not be the culprit. Does anyone know the possible origin of such behaviour?

Thank you

Sorry, I don’t completely understand the problem.

You mention applyFontToAllText(), but this is a member of TextEditor, so it won’t have an effect on Label rendering.

Please could you provide a minimal example of some code that behaves differently in JUCE 7 to JUCE 8?

Thank you for your response, I will try to elaborate with the addition of some relevant code.

I have a LookAndFeel with a default bold font that is, let’s say, 10pt high.

getLookAndFeel().setDefaultSansSerifTypeface(tf);
[...]
Typeface::Ptr tf= Typeface::createSystemTypefaceFor (BinaryData::font, BinaryData::fontSize);

Consider three components: ā€œsimpleā€ non-editable Labels, editable Labels, and editable slider TextBox.

My understanding is that they are all instances of the same class with an ā€œeditableā€ flag that allows for the instance to become a TextEditor and be changed on user interaction.

All of them inherit the same LookAndFeel with the above mentioned font.

component.setLookAndFeel(&myLookAndFeel);

JUCE 7 → As expected, all of the components are rendered with the bold 10pt font.
Sometimes I override the font size in the resized() with

label.setFont(FontOptions(new_size));

And if the new_size is the same as the LookAndFeel one it obviously renders no difference.

JUCE 8 → All the three components above that do NOT get overridden in the resized() fall back to a completely different font (not bold and smaller, maybe 6pt). Normal labels can be perfectly fixed with the above mentioned setFont (even if it wouldn’t be necessary), but editable labels that get overridden increase to only probably 7pt. Slider TextBoxes do not have setFont method so it is not clear to me neither why they are not inheriting from the look and feel nor how to override their font.

What could cause this strange behaviour?

Sorry, I’m still not able to reproduce the problem. Please could you supply the following information?

  • What version of JUCE 7 were you using (commit hash or full version number, e.g. 7.0.12)?
  • What platform are you using for testing (e.g. Windows 11, macOS 14, Ubuntu 24.04)?
  • Are you using a custom renderer (e.g. OpenGL)?
  • Are you able to provide a full code snippet that behaves differently in JUCE 7 and JUCE 8? Perhaps the following code will be useful as a starting point:
class MainComponent final : public juce::Component
{
public:
    //==============================================================================
    MainComponent()
    {
        setSize (400, 300);

        getLookAndFeel().setDefaultSansSerifTypeface (juce::Typeface::createSystemTypefaceFor (BinaryData::KarlaRegular_ttf, BinaryData::KarlaRegular_ttfSize));

        addAndMakeVisible (labelA);
        addAndMakeVisible (labelB);
        addAndMakeVisible (slider);

        labelB.setEditable (true);
    }

    //==============================================================================
    void paint (juce::Graphics& g) override
    {
        g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId));
    }

    void resized() override
    {
        juce::FlexBox fb;
        fb.items = { juce::FlexItem { labelA }.withFlex (1.0f),
                     juce::FlexItem { labelB }.withFlex (1.0f),
                     juce::FlexItem { slider }.withFlex (1.0f) };
        fb.performLayout (getLocalBounds());
    }

    void parentHierarchyChanged() override
    {
        if (auto* p = getPeer())
            p->setCurrentRenderingEngine (1);
    }

private:
    juce::Label labelA { "", "label a" }, labelB { "", "label b" };
    juce::Slider slider;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainComponent)
};

Is the chosen font definitely a completely different font, as opposed to just the requested typeface at a smaller-than-expected size? If you’re unsure, perhaps you could try replacing the custom typeface with a very distinctive one (e.g. impact, wingdings).

Hi, thank you for your response!

  • I am transitioning from JUCE 7.0.12 to 8.0.0 on Windows 11
  • I am not modifying anything about the renderer so I assume it is the default one.

Meanwhile, thank you for suggesting using an exotic font to check font propagation, I was able to confirm it is actually the same font but smaller/slimmer (probably without antialiasing?), as you said. However this surfaced an even stranger problem: when the font is simply scaled to match the old size the scaling has different effects in different parts of the code. Here is a series of screenshots:

Editable Labels JUCE 7, custom look&feel (font, size), location A
image

Editable Labels JUCE 7, custom look&feel (font, size), location B
image

Editable Labels JUCE 8, custom look&feel (font, size), location A
image

Editable Labels JUCE 8, custom look&feel (font, size), location B
image

Editable Labels JUCE 8 (size simply scaled up by 10%), custom look&feel (font, size), location A
image

Editable Labels JUCE 8 (size simply scaled up by 10%), custom look&feel (font, size), location B
image

You can see as in location A scaling also ā€œthickensā€ the text while in location B (same component, font and dimension, theoretically) it just enlarges the text with no thinkening. In short JUCE 7 and 8 (with +10%size) are almost identical at location A and very different at location B.

For now I would ask if there is a quick way that simply reverts the font rendering to the JUCE 7.0.12 default, just to be able to continue production. setCurrentRenderingEngine had no visible effect. Since my codebase is fairly big and as it probably is clear I am quite confused about the origin of the problem I am writing this post before finishing extracting a minimal example.

There are three things I think you’ll need to do.

  1. From the breaking changes document

JUCE widgets now query the LookAndFeel to determine the TypefaceMetricsKind to use. By default, the LookAndFeel will specify the ā€œportableā€ metrics kind, which may change the size of text in JUCE widgets, depending on the font and platform.

Possible Issues

Using ā€œportableā€ metrics may cause text to render at a different scale when compared to the old ā€œlegacyā€ metrics.

Workaround

If you want to restore the old metrics, e.g. to maintain the same text scaling in an existing app, you can override LookAndFeel::getDefaultMetricsKind() on each LookAndFeel in your application, to return the ā€œlegacyā€ metrics kind.

Rationale

Using portable font metrics streamlines the development experience when working on applications that must run on multiple platforms. Using portable metrics by default means that new projects will benefit from this improved cross-platform behaviour from the outset.

This will force all the widgets to use the legacy style metrics which will mean they appear the same size as they did in JUCE 7.

  1. Anywhere where you manually specify a Font either
  • Use the old Font constructors that don’t take a FontOptions argument (note this will cause warnings in your builds)
  • Add .withMetricsKind (juce::TypefaceMetricsKind::legacy) to any of your FontOptions for example
label.setFont (FontOptions (new_size).withMetricsKind (TypefaceMetricsKind::legacy));

This will force all of your custom specified fonts to use the legacy style metrics which will mean they appear the same size as they did in JUCE 7.

  1. From your top level component switch to the software renderer on Windows only, probably best to do this in parentHierarchyChanged() (note the code below is untested)
void parentHierarchyChanged() override
{
   #if JUCE_WINDOWS
    if (auto* peer = getPeer())
        peer->setCurrentRenderingEngine (peer->getAvailableRenderingEngines().indexOf ("Software Renderer"));
   #endif
}

This will force all drawing using the software renderer which is the same one used in JUCE 7. This will most likely fix the issue with fonts looking bolder.

Hope that helps.

Hi, thank you for your response.

Unfortunately I have been unsuccessful in reverting back to JUCE 7 rendering. In particular, answering point by point:

  1. getDefaultMetricsKind does not seem to change anything visibly, which stuns me given how simple the implementation should be in my custom look and feel class:
TypefaceMetricsKind getDefaultMetricsKind() const override { return TypefaceMetricsKind::legacy; }

I have checked that components use the correct custom look and feel.

  1. Both old Font constructor and the new one with .withMetricsKind do not have any visible effect either, with or without point 1.

  2. Similarly this yields no visible effect at any point in the hierarchy (from pluginEditor to the particular component(s) that I am experiencing problems in), both as copied from your answer and from reuk’s, which is a slightly different form.

The results have been so consistently unchanging that I came to fear something in cmake build system had incorrectly cached, but even after a fresh rebuild nothing changed. Is there perhaps some build define I should be setting?

Hi

Having the same issue. Updated to 8.0.0 and massive change to text rendering that I am looking to revert so I do not need to go through code with a toothcomb to resolve. The ā€˜::getDefaultsMetricsKind’ fix above did not work.

Looking for more support on this issue - it remains unresolved.

Thanks in anticipation

The getDefaultsMetricsKind is only for cases where you’re not already overriding the font for some internal JUCE widget.

Anywhere you are specifying the font you can either leave your code exactly as it is using the deprecated Font constructors or to avoid the deprecation warning construct the font using a FontOptions and add .withMetricsKind (juce::TypefaceMetricsKind::legacy).

With those changes in place I would expect all the Fonts to be the same size on all platforms*, if that’s not the case could you please share the font you’re using (that goes to both @ssc and @dongoodeve).

*Although the fonts should be the same size, on Windows you may still see a change in rendering due to the new Direct2D renderer. Please see the posts above for how to change back to the software renderer.

Hi,

thank you for your responses. Unfortunately this issue still persists to me. I am using Ubuntu font.

If anyone finds a solution or a catch in the ones proposed above, thank you in advance.

There are relatively few cases in my code @anthony-nicholls where I am using explicit font setting. The default font has changed and sizing is throwing rendering of multiple elements off.

Is there a simple fix to render in Juce8.0.0 exactly as in JUCE7.0.12 so I can get a release out or do I need to revert whilst I figure out how to address this issue?

I have applied the parentHierachyChanged software renderer selection (no effect) and the setCurrentRenderingEngine fix as noted in your initial response - seems like I and @ssc have the same issue and similar concerns.

Thanks in anticipation

 Don

Has the font changed, or just the size of the font? What platforms are you using for development and testing? What typeface is being used as the default, and do you get different results on JUCE 8 to JUCE 7? You can use the following snippet to find the name of the default typeface.

DBG (juce::Font{}.getTypefacePtr()->getName()); 

No, there have been extensive internal changes to fonts and typefaces in JUCE 8, and a lot of the JUCE 7 code was completely replaced or removed. There’s no way to get JUCE 7 font selection in JUCE 8.

If you’re able to provide a code snippet that exhibits substantially different rendering in JUCE 7 and JUCE 8, along with instructions to reproduce the issue (installed fonts, test platform etc.), that will help us to determine what has caused the change in behaviour. It might be easiest to modify one of the JUCE examples so that it triggers the issue.

I ask for more information because it seems that this issue is only present in very specific scenarios. We on the JUCE team have not seen anything like the reported behaviour during development. This is the only forum thread about the issue; if the issue affected all projects, I’m sure we’d have received many more reports.

This is also a jump between major versions. I would expect changes. If you want JUCE7 behavior, you can use JUCE7.

Goes without saying. I had hoped it would be a straightforward upgrade path.

Now to figure out why I am getting massive memory bloat - however that is for another thread…

  Don

Hi guys,

I have the same problem as @ssc and @dongoodeve. I have modified a JUCE example to show the problem:

From left to right:

  • VSCode text rendering → perfect
  • JUCE 7.0.12 text rendering → bad, but JUCE has always been very bad at text rendering since day 1
  • JUCE 8.0.0 text rendering → too bad and different from JUCE 7, not suitable for production release. Letters seem to be smaller, thinner and out of focus

I’m using Windows 11, Visual Studio 2022 compiler. I’ll attach the code in the next message.

1 Like

Code example (expires in 7 days): WeTransfer - Send Large Files & Share Photos Online - Up to 2GB Free

1 Like

Thanks for the examples.

I’ve taken a closer look at this and it seems this is less about a size or renderer change from JUCE 7 to JUCE 8. I’m fairly sure the differences are because JUCE 7 on Windows either wasn’t applying hinting or was applying some automatic hinting not based on the hinting specified in the font, whereas JUCE 8 applies hinting as indicated by the font. This is most likely due to the use of HarfBuzz. I’ve looked to see if there is a way to disable this, unfortunately I haven’t found a way in the library yet but it is possible to edit a font to remove hinting entirely.

However, it’s worth noting that JUCE 8 now closer matches macOS (and probably Linux), as well as other applications on Windows such as Chrome.

Generally hinting should also only have these kind of visible effects on small fonts and lower resolution screens. I’ve included a gif below showing the same Ubuntu medium font with a height of 10pt rendered with JUCE 7, JUCE 8 (with and without hinting), and Chrome. The only big difference between JUCE 8 and Chrome is that Chrome is using sub-pixel anti-aliasing while JUCE 8 uses grey scale anti-aliasing. You can also clearly see how JUCE 8 follows hinting which at this scale has the effect of making the text look slightly smaller and bolder.

I haven’t tested but I wouldn’t be surprised if changing the display scale has some impact on this too.

I think if you’re using the Ubuntu font with relatively small font sizes and you want it to look exactly the same you can either…

  • Continue using JUCE 7
  • Generate a custom bitmap font

Alternatively if you just want to get it closer then you could try removing hinting from the font. I’ve included a version of Ubuntu medium with hinting removed below. If you decide you want to do this yourself

  • Open the font using FontForge
  • In the ā€œHintsā€ menu select ā€œRemove Insert Tablesā€
  • In the ā€œFileā€ menu select ā€œGenerate Fontsā€¦ā€
  • After selecting the location and name select ā€œGenerateā€

Note I also tried applying auto-hinting to the font in FontForge but unfortunately this appeared to make no difference.

animation1
An animation showing the differences for a Ubuntu medium font at 10pt on Windows between JUCE 7, JUCE 8 (with and without hinting), and Chrome

Ubuntu-Medium-nohint.ttf (247.7 KB)

1 Like

Something always occurs to you as soon as you post.

In my original test of this it seems I wasn’t actually switching to the Software Renderer in JUCE 8.

So to correct my original statement, Direct2D is the one applying hinting, but HarfBuzz does have some impact on glyph placement.

Using the example @AleBd supplied with a few minor tweaks I get this…

animation2

I think the changes seem pretty subtle? If you need something more precise than that I suggest either staying on JUCE 7 or creating a bitmap font.

The changes I made to the example code were…

  1. I switched to the software renderer (this code should work equally well on JUCE 7 and JUCE 8)
void parentHierarchyChanged() override
{
    if (auto* peer = getPeer())
        peer->setCurrentRenderingEngine (peer->getAvailableRenderingEngines().indexOf ("Software Renderer"));
}
  1. I set the Font like so
// this is compatible with JUCE 7 and JUCE 8 but produces a warning on JUCE 8
l.setFont (Font (15.0f));

If you want to avoid the warning on JUCE 8 but still make sure it’s the same as JUCE 7

l.setFont (FontOptions (15.0f).withMetricsKind (TypefaceMetricsKind::legacy));
  1. I moved the setting of the font into the constructor, but this isn’t strictly required

I have not yet upgraded to Juce 8, but I am dreading it, since I have been working on my project in JUCE for over 4 years and have spent a huge amount of time tweaking things to get my embedded fonts all perfect on both Windows and Mac. But I can clearly see that in the JUCE 8 version here, the space between the T and E in Text looks better (is that kerning?).

I done a test with just the ā€œTeā€ text and found that the gap between the two scales with size, so it may well be to do with kerning, as if JUCE 7 adds some additional kerning in this case. However without digging deeper I can’t be 100% certain. What I can say is that neither Chrome or Edge add any additional spacing so I’m fairly confident it’s an issue with JUCE 7.

1 Like