OSX font corruption

I am making a plugin, which loads a TTF font from a JUCE BinaryResource.
I am loading this font inside the “PluginEditor” class, the following way:

defaultTypefacePtr = Typeface::createSystemTypefaceFor(BinaryData::Roboto_Light_ttf, BinaryData::Roboto_Light_ttfSize);

In other words: This line is run every time I open the plugin window.

My problem: The font gets corrupted, after opening the plugin window several times inside a DAW. By corrupted I mean that letters are missing and that I even get crashes inside the font routines. For example, it can crash inside “TTrueTypeFontDataHandler::GetGlyphData”

I found a solution to my problem. But I am not sure yet, why it works:
I solved my problem, by first clearing the font cache with a call to clearTypefaceCache():

Typeface::clearTypefaceCache();
defaultTypefacePtr = Typeface::createSystemTypefaceFor(BinaryData::Roboto_Light_ttf, BinaryData::Roboto_Light_ttfSize);

So apparently, the font cache is kept, even if the plugin GUI window is destructed. When you open the plugin GUI window again, it is going to use the same font cache. But for some reason, this font cache gets corrupted over time. Clearing the cache every time, solves this.

Is this cache corruption a known problem? Or am I doing something wrong?
I only seem to get this on OSX. On Windows it works fine without clearing the typeface cache first.

I am on OSX 10.14.4 and JUCE 5.4.3 Build Date 22 Feb 2019.

4 Likes

Known issue, it happens in our app quite regularly. The exact cause or solution isn’t known. I’ve been trying to reproduce the problem in a minimal program, but so far have been failing.

Hi G-Mon,

Thanks for your answer. Good to know, I am not the only one experiencing this.

We’ve tried for years to nail this one down, but it’s very hard to reproduce. I assume it’s a bug in the OSX font system but exactly what triggers it, and how we’d work around it is very hard to guess. We’ll get it one day!

1 Like

Trying to reproduce this I discovered that creating 32766 Typeface objects will hang inside CTFontManagerRegisterGraphicsFont. Not sure if related.

1 Like

Thanks Jules.
Fingers crossed!

Bringing this up again. I have exactly the same issue. It crashed on my AAX Plugin on macOS and iOS app but not on my macOS app (all from the same codebase but quite random where it crashed and when).

As the issue seems to be OS related and not easy to resolve (if at all), I would highly recommend to put a warning in the docs.

Based on that thread https://forum.juce.com/t/guide-juce-font-embedding-2019/35041/9
I solved the issue doing:

enum FontStyle {
        regular = 0,
        thin,
        light,
        medium,
        bold,
        black,
        italic,
        thinItalic,
        lightItalic,
        mediumItalic,
        boldItalic,
        blackItalic
    };
    
    static const juce::Font getFontRobotoFor(FontStyle f)
    {
        Typeface::clearTypefaceCache();
        
        static auto tfRobotoRegular      = juce::Font(juce::Typeface::createSystemTypefaceFor(FontRoboto::RobotoRegular_ttf, FontRoboto::RobotoRegular_ttfSize));
        static auto tfRobotoThin         = juce::Font(juce::Typeface::createSystemTypefaceFor(FontRoboto::RobotoThin_ttf, FontRoboto::RobotoThin_ttfSize));
        static auto tfRobotoLight        = juce::Font(juce::Typeface::createSystemTypefaceFor(FontRoboto::RobotoLight_ttf, FontRoboto::RobotoLight_ttfSize));
        static auto tfRobotoMedium       = juce::Font(juce::Typeface::createSystemTypefaceFor(FontRoboto::RobotoMedium_ttf, FontRoboto::RobotoMedium_ttfSize));
        static auto tfRobotoBold         = juce::Font(juce::Typeface::createSystemTypefaceFor(FontRoboto::RobotoBold_ttf, FontRoboto::RobotoBold_ttfSize));
        static auto tfRobotoBlack        = juce::Font(juce::Typeface::createSystemTypefaceFor(FontRoboto::RobotoBlack_ttf, FontRoboto::RobotoBlack_ttfSize));
        static auto tfRobotoItalic       = juce::Font(juce::Typeface::createSystemTypefaceFor(FontRoboto::RobotoItalic_ttf, FontRoboto::RobotoItalic_ttfSize));
        static auto tfRobotoThinItalic   = juce::Font(juce::Typeface::createSystemTypefaceFor(FontRoboto::RobotoThinItalic_ttf, FontRoboto::RobotoThinItalic_ttfSize));
        static auto tfRobotoLightItalic  = juce::Font(juce::Typeface::createSystemTypefaceFor(FontRoboto::RobotoLightItalic_ttf, FontRoboto::RobotoLightItalic_ttfSize));
        static auto tfRobotoMediumItalic = juce::Font(juce::Typeface::createSystemTypefaceFor(FontRoboto::RobotoMediumItalic_ttf, FontRoboto::RobotoMediumItalic_ttfSize));
        static auto tfRobotoBoldItalic   = juce::Font(juce::Typeface::createSystemTypefaceFor(FontRoboto::RobotoBoldItalic_ttf, FontRoboto::RobotoBoldItalic_ttfSize));
        static auto tfRobotoBlackItalic  = juce::Font(juce::Typeface::createSystemTypefaceFor(FontRoboto::RobotoBlackItalic_ttf, FontRoboto::RobotoBlackItalic_ttfSize));
        
        switch(f) {
            case regular:
                return Font(tfRobotoRegular);
                break;
            case thin:
                return Font(tfRobotoThin);
                break;
            case light:
                return Font(tfRobotoLight);
                break;
            case medium:
                return Font(tfRobotoMedium);
                break;
            case bold:
                return Font(tfRobotoBold);
                break;
            case black:
                return Font(tfRobotoBlack);
                break;
            case italic:
                return Font(tfRobotoItalic);
                break;
            case thinItalic:
                return Font(tfRobotoThinItalic);
                break;
            case lightItalic:
                return Font(tfRobotoLightItalic);
                break;
            case mediumItalic:
                return Font(tfRobotoMediumItalic);
                break;
            case boldItalic:
                return Font(tfRobotoBoldItalic);
                break;
            case blackItalic:
                return Font(tfRobotoBlackItalic);
                break;
            default:
                return Font(tfRobotoRegular);
        }
    }

Xcode’s Address Sanitizer didn’t show any leaks.

cheers
Stefan

1 Like

Same issue here.
I’m on macOS 13.3, JUCE v7.0.5 [68beb7482] and in my case Typeface::clearTypefaceCache() doesn’t solve the problem.
I solved removing CTFontManagerRegisterGraphicsFont from OSXTypeface.

I did some digging in to this a while ago and summed up some information here:

Pretty much I found that removing the static keyword or doing as you suggest here resolves the issue, but never found out why. I suspect MacOS is trying to do something clever and share font resources between processes and marking the typeface data as static will prevent it from accessing the data outside of the declaring process. Or something like that. Because Apple.

1 Like