[Solved] Font rendering omitting certain character combinations, like 'ti'

Hey Embers,

I’ve found that when using the ‘Calibri’ font Cinder straight-up simply doesn’t render certain combinations of letters using the ci::gl::TextureFont object:

image

Notice how the connected lettering ‘ti’ is omitted in the first render. In the second render, I’ve inserted a space between the two letters and then they render as intended.

I’ve confirmed this to occur in a completely blank Cinder application apart from loading Calibri into a ci::gl::TextureFont object.

Obviously, a simple work around is to simply use a different font, but has anyone else tried anything similar?

Cheers,
Gazoo

These are called ligatures, and you can disable them in your DrawOptions with gl::TextureFont::DrawOptions{}.ligate ( false );

They can look much better when enabled though, so if you want them in there, they’ll need to be added to the supportedChars list when you create the TextureFont.

auto chars = gl::TextureFont::defaultChars();
chars.append ( ... the ligature codepoints here ... )
auto font = gl::TextureFont::create ( chars );

Edit: Been doing some digging, it seems that toggling ligatures isn’t supported on windows at all, but it will correctly apply ligatures as long as the codepoints are in the supplied glyphset.

The problem with your case is the ligature for ti (named glyph00415) is actually composed of a few smaller glyphs, glyph00424 and glyph00549), which, in turn, are composed of further smaller bits, so there’s not a direct codepoint you can include for this to work with the current API, unless somehow supplying all the parts allows it to resolve correctly. Text is the worst.

Edit 2: You’ve sent me down a real Sunday arvo rabbit hole here. I found article after article of people having trouble with calibri and ligatures on windows, although I was able to replicate the issue on mac too, so a lot of that turned out to be a red herring.

As far a I can tell, the issue lies with the font atlas in gl::TextureFont. The glyph measurement functions appear to do the right thing, and when passing the results into the OS level text rendering functions called by TextBox, you’ll get the correct output, but the ligated glyph isn’t being rendered correctly in the font atlas (or the codepoint is getting corrupted) so when it comes time to draw the ligated glyph from the atlas, it’s blank. That’s a lot of words with roughly zero useful information for you, but there you have it :wink:

In short, bake your textures with TextBox in advance if you can, or as you knew from the jump, use a different font.

2 Likes

As usual @lithium - You’ve gone above and beyond in your explanation and exploration. :slight_smile:

Just out of curiosity, I tried turning off the ligatures:

ci::gl::TextureFont::DrawOptions noLigate = ci::gl::TextureFont::DrawOptions{}.ligate( false );
mTextureFont->drawString( "Definition", ci::vec2(20,20), noLigate );

… but unfortunately, that didn’t seem to do any difference.

As noted in a previous post, I’d worry about performing for having to pre-bake my texts as textures, so I’m inclined to just going with an alternative font, e.g. Roboto.

Cheers,
Gazoo

I recommend looking at something like MSDF fonts once your text rendering gets to a certain critical mass. I bit the bullet one day and sat down to tackle it and now i have a semi-decent scale-invariant text renderer that’s fast and flexible, and as a bonus i added per character / word / line animation support that runs entirely on the GPU. I haven’t looked back since.

1 Like

That looks really cool @lithium - kudos to you on this work.

Did you find it tricky to integrate the code you reference on Github with Cinder?

Not really, no, but it’s not super fully featured layout wise and that’s where things get hairy. I’ve attached a standalone implementation (minus the animation stuff) for you to have a fiddle with if you’re so inclined.

Just unzip these into the assets folder and you should be good to go. These are just generated with msdf-atlas-gen using the following command, if you want to try your own fonts.

msdf-atlas-gen -font calibri.ttf -yorigin top -size 24 -imageout Calibri.png -json Calibri.json

It’s very rough, just copy and pasted together for the sake of demonstration, so it doesn’t cache anything or really do much performance wise at all, but still hopefully helpful.

A

3 Likes

It looks like @lithium covered pretty much everything here, I just wanted to add that I’ve been using Cinder-SDFText, which uses msdf as suggested.

I created a fork of the older block and have made sure it’s up and running with the latest Cinder/Windows; feel free to use it here:

I used it for a project last year and plan to use it again this coming year for a few works, so if you find any bugs I’m pretty invested in fixing them right away :wink:

2 Likes