Weird behaviour with in-memory fonts and OSX Layouts


#1

When I use embedded Fonts and TextLayout on mac it shows the wrong characters.

Here is a github I just pushed up, based on the IntroJucer Basic GUI project - https://github.com/hm1992/juceTextLayoutBugOSX

I am using an embedded Roboto ttf font (but I am not sure it is being used). The meat of the code is as follows.

// Draw Attributed String at the top
string.draw(g, bounds.removeFromTop(400).toFloat());
// Draw Text Layout at the bottom
layout.draw(g, bounds.toFloat());

And this is what I get: 

http://pasteboard.co/1B0OfjME.png

1. I don't think that is my font (though I could be wrong)

2. The Glyphs printed by layout.draw are all off by one ("@o`" is 64 111 96, whearas "Apa" is 65 112 97)

 

p.s. I am using TextLayouts/AttributedStrings after profiling use of TextEditor components to display these strings. They were very slow to perform TextEditor::setText() due to a lot of time spent in OSX getStringLength.


#2

​Just so you know, I am looking into this (we haven't forgotten you!). Thank you very much for providing a simple example that reproduces the problem on Github, it makes things much easier for us. Will let you know when anything happens regarding it.


#3

Thanks for getting in touch. You are welcome about the github project. I figured there was no point letting my diagnosis work go to waste! :)


#4

Hi jgerrard,

 

Is there a workaround for this issue? All of these elements seem to have drawbacks.

1. Attributed String - no get height function so cannot be placed inside scrolling viewports.

2. Layout - doesn't draw correct Glyphs.

3. Text Box - spends a long time in the mac string length functions when setting up with such a long string.


#5

Apologies, I did look into this! The JUCE summit has been sucking up my time recently ...

There's no fix yet, although I've gathered a fair bit of information about the problem. As for a temporary workaround, TextBox is probably your best bet for now, unless it's unresonably slow.


#6

Has there been any movement on this issue?


#7

I don't remember whether Joshua fixed this or not (he's not here right now to ask).. Does the latest version help?


#8

I just rebuilt that project form introjucer with 4.1.0 and got a different (wrong) set of characters.

 

http://pasteboard.co/cyxs7PU.png

 

Here follows the hex codes displayed

Correct       ("Apache"): 41 70 61 63 68 65

Old incorrect ("@o`bgd"): 40 6F 60 62 67 64 [-1]

New incorrect ("Crcejg"): 43 72 63 65 6A 67 [+2]

#9

Hi there

Sorry, didn't realise that Joshua never finished this. 

Just had a quick look myself, and although I'm a bit puzzled about why it's drawing broken glyphs, there's a very easy fix. You hadn't actually set a font for the AttributedString, and it all works when I just added a line to do that:


MainContentComponent::MainContentComponent()
{
    LookAndFeel::setDefaultLookAndFeel(new TestLookAndFeel());
    string = AttributedString(BinaryData::licence_apache_2_0_txt);

    string.setFont (Font (15.0f));   // <-- added this

    layout.createLayout(string, 600 /*400*/);
    setSize (600, 800);
}

I'll dig a bit deeper into why it was producing garbage without that, but this should get you running.


#10

With the latest tip and/or a call to setFont I have all the glyphs correct. Thank you.

 

I have had to change to a default system font though due to the bug below. I assume there is nothing to be done here?

   #if JUCE_CORETEXT_AVAILABLE

    // Seems to be an unfathomable bug in CoreText which prevents the layout working with

    // typefaces that were loaded from memory, so have to fallback if we hit any of those..

    if (canAllTypefacesBeUsedInLayout (text))

    {

        CoreTextTypeLayout::createLayout (*this, text);

        return true;

    }

   #endif

#11

Well, there's no fix that I know of for that one, but it should still work by falling back to the non-OSX layout routine, which won't handle extended unicode layouts, but should do the basics for a western font, right?


#12

The original performance problem was in the non-OSX layout function.

juce::OSXTypeface::getStringWidth and juce::OSXTypeface::getGlyphPositions were ~40% (~2s) each in the time profiler for the UI startup routine. We are a bit behind the tip so this may be better with JUCE tip. With the native layout function everything is much faster and I think it is an acceptable workaround. Startup times for similar situations have gone from ~4.5s -> ~0.25s.


#13

Surprised that it's faster with the native layout, I'd have expected it to be the other way round!

But it does sound like you're doing a lot of unnecessary work either way.. If you're repeatedly drawing text, you might want to consider caching it in a GlyphArrangement?


#14

https://github.com/hm1992/juceTextLayoutBugOSX/tree/master/Source

I have updated the example project to show the differences (just change which line is commented to see the difference).


#15

Harry - so sorry, I was just going through unanswered posts and realised I never got back to you about this!

Trying your code on my machine, even in debug mode it's saying 35ms, so I guess something is very different between what you're running and what I've got here. I'm building against the very latest tip so perhaps we fixed something relevent since the version you were using?


#16

Hi Jules,

35ms is what I am getting with "Font font ("Helvetica", "Regular", 10.0f)". With "Font font (10.0f)" which uses the included Roboto ttf font I am still getting around 5s with the JUCE tip on OSX 10.11. I include a screenshot of profiler.


#17

So it's just blocking for 5 seconds inside CTLineCreateWithAttributedString??

That's really bizarre, and I'm not sure what I could do to reproduce it here - when I tried last time, it all worked just fine..


#18

Hi Jules,

I have looked at this again and updated my test project on github. My output looks like this:

JUCE v4.1.0
Time taken to create Native font Layout: 35.4491
Time taken to create Bundled font Layout: 4634.54
Time taken to draw Native font Layout: 1149.28
Time taken to draw Bundled font Layout: 1132.54

Also is there a bug in layout.draw? Should it clip to the rectangle as follows? I am happy to make a new thread for this if needed.


void TextLayout::draw (Graphics& g, const Rectangle<float>& area) const
{
    const Point<float> origin (justification.appliedToRectangle (Rectangle<float> (width, getHeight()), area).getPosition());
    LowLevelGraphicsContext& context = g.getInternalContext();
    context.saveState();
    context.clipToRectangle(area.toNearestInt());
    for (int i = 0; i < lines.size(); ++i)
    {
        const Line& line = getLine (i);
        const Point<float> lineOrigin (origin + line.lineOrigin);
        for (int j = 0; j < line.runs.size(); ++j)
        {
            const Run& run = *line.runs.getUnchecked (j);
            context.setFont (run.font);
            context.setFill (run.colour);
            for (int k = 0; k < run.glyphs.size(); ++k)
            {
                const Glyph& glyph = run.glyphs.getReference (k);
                context.drawGlyph (glyph.glyphCode, AffineTransform::translation (lineOrigin.x + glyph.anchor.x,
                                                                                  lineOrigin.y + glyph.anchor.y));
            }
        }
    }
    context.restoreState();
}

Custom Typeface bug in JUCE pop-up dialogs
#19

Hi Harry

Yes, I'm seeing this too, but if you try profiling it, you'll see that it's just sitting inside some OS functions that seem to be initialising typeface attributes the first time it gets used.. I guess that for some reason it behaves differently for in-memory fonts than it does for file-based ones.

As far as clipping goes, I avoided doing that, as clipping can add some overhead when doing a lot of drawing, so would rather leave it up to the caller to clip if they really need to...