Displaying Japanese text glyph by glyph: Automatic script font selection

284 views
Skip to first unread message

Michael Tyson

unread,
Jun 17, 2012, 8:48:18 AM6/17/12
to cocoa-...@googlegroups.com
Hello!

I have a UI element that displays text radially - it looks like this: http://cl.ly/1j3F2h2x0Z1h1Q1Q2k2W

It's rendered using Core Text/Quartz, by creating a CTLine, then iterating over the CTRuns within the line, and each glyph within the run, offsetting and rotating each glyph to render it along the circular path.

I've recently had my UI translated to Japanese, and rather than displaying the Japanese text, suddenly the UI element is blank, with some obscure "FT_Load_Glyph failed: error 6" errors showing up in console.

After a good deal of mucking around (including selecting the default system font, which displayed random romanesque junk), I experimented by setting a Japanese font (Hiragino Kaku Gothic ProN, for example), and suddenly it was fine: http://cl.ly/3h3Q2B3S2o2L2H0J2X0A

I anticipate adding a bunch more languages soon, and I'd really rather not have to find and select a font for every language I add (plus that just sounds dumb), so I'm looking for a sensible general way to support non-western languages.

CTFontCreateUIFontForLanguage looked like a very promising contender, but it produces this fabulous result: http://cl.ly/0u0N3e292o072O332h3W

Has anyone solved this problem? What on earth does UILabel/etc use? It seems very capable of displaying the right glyphs, even with my custom font selected.

If all else fails - is there a table somewhere displaying the available iOS fonts alongside the languages they support?

Cheers!
Michael

Michael Ash

unread,
Jun 17, 2012, 1:13:57 PM6/17/12
to cocoa-...@googlegroups.com
On Jun 17, 2012, at 8:48 AM, Michael Tyson wrote:

I've recently had my UI translated to Japanese, and rather than displaying the Japanese text, suddenly the UI element is blank, with some obscure "FT_Load_Glyph failed: error 6" errors showing up in console.

How are you rendering the glyphs you extract from the CTRun, exactly?

Fonts don't include every glyph (they'd have to have something like a million of them to cover everything), so the OS will automatically switch fonts when it needs a glyph that the one you're providing doesn't contain. It sounds like you may be bypassing that mechanism somehow.

If you use the CTRunDraw function, telling it to draw a range of length 1 each time, I'd expect all of that to be handled for you.

If you want to extract and draw the glyphs yourself, I think you'll need to call CTRunGetAttributes and use the font that's set there, which will be  your original font whenever possible, but will contain the substituted font if not.

Glyph IDs only make sense within the context of a particular font, so using a different font which contains what seem like the right glyphs still may not work, as it may not have the same IDs as the one chosen by the system. That's presumably why fetching a font with CTFontCreateUIFontForLanguage and using that produced garbage.

I hope that nudges you in the right direction, even though I'm not entirely sure of what's going on.

Mike

Michael Tyson

unread,
Jun 17, 2012, 2:30:42 PM6/17/12
to cocoa-...@googlegroups.com
Hey Mike,

> How are you rendering the glyphs you extract from the CTRun, exactly?
>
> Fonts don't include every glyph (they'd have to have something like a million of them to cover everything), so the OS will automatically switch fonts when it needs a glyph that the one you're providing doesn't contain. It sounds like you may be bypassing that mechanism somehow.

Ah yes, it sounds like that might be the case!

I'm currently using CTFontDrawGlyphs, one glyph at a time, to render. Previously I was using CGContextShowGlyphsAtPoint - there's no change in behaviour, though.


> If you use the CTRunDraw function, telling it to draw a range of length 1 each time, I'd expect all of that to be handled for you.
>
> If you want to extract and draw the glyphs yourself, I think you'll need to call CTRunGetAttributes and use the font that's set there, which will be your original font whenever possible, but will contain the substituted font if not.
>
> Glyph IDs only make sense within the context of a particular font, so using a different font which contains what seem like the right glyphs still may not work, as it may not have the same IDs as the one chosen by the system. That's presumably why fetching a font with CTFontCreateUIFontForLanguage and using that produced garbage.
>
> I hope that nudges you in the right direction, even though I'm not entirely sure of what's going on.
>
> Mike

Ah-ha! Thank you so much, that was what I needed: Pulling the font name from the CTRun's attributes dictionary and setting that via CGContextSelectFont did the trick.

Many thanks!


Michael Buckley

unread,
Jun 17, 2012, 2:31:08 PM6/17/12
to cocoa-...@googlegroups.com
Mike's answer is correct, but I'd like to bring up a few more topics for discussion.

You asked how UILabel works, and it uses Core Text's font cascading, which is the font substitution that Mike alluded to. It's documented in the Core Text Programming Guide. In particular, it calls out CTFontCreateForString as using font cascading, which may help you out if you're still having problems. If not, you may want use the kCTFontCascadeListAttribute to make sure there's a Japanese font in your cascade list.


"Core Text font references provide a sophisticated, automatic font-substitution mechanism called font cascading. This mechanism takes font traits into account, so it does a better job than previous schemes of picking an appropriate font to substitute for a missing font. Font cascading is based on cascade lists, which are arrays of ordered font descriptors. There is a system default cascade list (which is polymorphic, based on the user's language setting and current font) and a font cascade list that is specified at font creation time. Using the information in the font descriptors, the cascading mechanism can match fonts according to style as well as matching characters. The CTFontCreateForString function uses cascade lists to pick an appropriate font to encode a given string. You specify and retrieve font cascade lists using the kCTFontCascadeListAttribute property."

Second, I wanted to point out that I had a bit of a problem reading the Japanese text in the top-right corner. I'm a native English speaker, and while I wouldn't call myself fluent in Japanese, I have taken a few years of Japanese courses. It's possible that a native Japanese speaker would not have the same problem that I'm having, so you might want to consult a native speaker on this.

I think the root of my problem is, that while Japanese text can be written left-to-right, top-to bottom, it can also be written top-to-bottom, right-to-left, and given that the text runs vertically, I want to read it that way. I think it might also be easier to read that way, with every character's baseline being perfectly horizontal, since the last character is rendered at almost a 90 degree angle difference from the first character. In English, I have no problem compensating for this, but in Japanese, it makes the last character look almost like "te" (but missing a stroke) instead of "to".

Again, a native speaker might not have this problem, but it's worth keeping in mind that other languages might have trouble with this kind of character rotation as you localize your interface into more languages.

Michael Tyson

unread,
Jun 17, 2012, 2:51:27 PM6/17/12
to cocoa-...@googlegroups.com
Thanks for the extra info, Michael! That's kinda how I hoped it would all work, so it's nice to see =)

Good point about the readability - I might tweak the font size so that it spans less of the radius. Is it any better on the iPad version (http://yfrog.com/hsmnzp), or is it simply an overall angle issue?

Jim Dovey

unread,
Jun 17, 2012, 3:00:04 PM6/17/12
to cocoa-...@googlegroups.com
The letter forms are all recognizable as far as I'm concerned, but if you'd like I can ask some of the Japanese guys doing localization and text-rendering QA at work for their opinion tomorrow. The only thing I might think is whether it's more appropriate to always have the baseline for the text closer to the centre of the circle, rather than on the lower Y coordinate (i.e. flip the bottom two items' text the other way up). 

But overall I'd say it looks pretty readable— the layout implies that the text is flowing in a circle, and the glyph orientation makes it immediately apparent that the text is being laid out horizontally rather than vertically (plus there are actually different letter forms for some glyphs in vertical layout, which act as visual cues).

-Jim

Michael Buckley

unread,
Jun 17, 2012, 3:09:58 PM6/17/12
to cocoa-...@googlegroups.com
On Sun, Jun 17, 2012 at 12:00 PM, Jim Dovey <jimd...@gmail.com> wrote:
But overall I'd say it looks pretty readable— the layout implies that the text is flowing in a circle, and the glyph orientation makes it immediately apparent that the text is being laid out horizontally rather than vertically (plus there are actually different letter forms for some glyphs in vertical layout, which act as visual cues).

Yeah, I noticed after I sent the e-mail that if I tilt my head to the right, I don't have the same problem reading it, so the problem might just be me. It's still probably a good idea to keep this in mind when localizing into other alphabets though.

Jim Dovey

unread,
Jun 17, 2012, 3:18:41 PM6/17/12
to cocoa-...@googlegroups.com
Agreed— always have a native speaker look at your localizations before they go live.

Michael Tyson

unread,
Jun 17, 2012, 4:24:31 PM6/17/12
to cocoa-...@googlegroups.com
Thanks, guys!  That's probably a good idea - I'll see if I can find a Japanese user willing to take it for a prerelease spin.

Reply all
Reply to author
Forward
0 new messages