Updating the Juce Font System


#1

The way Juce is currently handling fonts and font styles is quite dated and issues are starting to occur due to the existing design.

Existing Design
The current Font class has a fixed set of styles (enum FontStyleFlags { plain = 0, bold = 1, italic = 2, underlined = 4 }).
On Windows, font enumeration is handled by Win32 and a list of font names (which seem to be otmFamilyName) are returned.
On Mac OS X, font enumeration is handled by Cocoa and a list of font families are returned.
On iOS, font enumeration is handled by Cocoa Touch and a list of font families are returned.
On Linux, font enumeration is handled by Freetype and a list of font families are returned.
On Android, font enumeration is done by getting a list of *.ttf files in /system/fonts.

Existing Issues

Mac OS X
All font styles beyond “Regular”, “Bold”, “Italic” and “Bold + Italic” are simply inaccessible in Juce.
You can see this yourself by downloading the Ubuntu fonts from: http://www.fontsquirrel.com/fonts/ubuntu
There are 9 font styles in that zip. They show up in Mac OS X as two font families: Ubuntu (8 font styles) and Ubuntu Condensed (1 font style).
As you can see, we have lost the ability to access 4 of the fonts.

Windows
When using DirectWrite for font rendering, many fonts don’t render properly.
You can see this by just running the Juce Demo on Windows and looking at Arial Black.
What is happening is Juce is searching for the font “Arial Black” as a DirectWrite font family and DirectWrite is failing to find it. This is because to DirectWrite, this font is known as Font Family: “Arial”, Font Face: “Black”.
This problem will also crop up when the otmFamilyName does not match. For example the font known as otmFaceName: Copperplate Gothic Bold Condensed BT has an otmFamilyName: CopprplGoth BdCn BT. Juce will try to match the font based on “CopprplGoth BdCn BT” and DirectWrite will fail to find it.

Recommendations
I think it is time to modernize the way Juce handles Fonts. Doing this will fix both of the above issues.
All modern font systems have converged on the following architecture.

I also think it is time we enumerate fonts based on the actual text rendering/layout API’s we are using. This way we know the font families and font styles will match exactly.
Windows XP, Windows Vista (Pre SP2 PU): Win32
Windows Vista (SP2 PU), Windows 7, Windows 8: DirectWrite
Mac OS X 10.4 (and older): Cocoa
Mac OS X 10.5 (and newer): Core Text
iOS 3.1 (and older): Cocoa Touch
iOS 3.2 (and newer): Core Text
Linux: FreeType
Android: Reading /system/fonts


#2

Updated Design
Move font styles out of Font and into Typeface

Typeface
Old: Typeface has a “name” string (name: “Arial Black”)
New: Typeface has a “family” string and a “style” string (name: “Arial”, style: “Black”)

Font
Old: Font has a “typefaceName” string and a “styleFlags” int, font style code (name: “Arial Black”, styleFlags: 0)
New: Font has a “typefaceFamily” string and a “typefaceStyle” string, remove all font style code (name: “Arial”, style: “Black”)

Additional classes/methods
StringArray GetFontFamlies()
Example: GetFontFamlies() returns “Arial”, “Courier”, “Times New Roman”

StringArray GetFontStyles(string typefaceFamily)
Example: GetFontStyles(“Arial”) returns “Narrow”, “Regular”, “Italic”, “Bold”, “Bold Italic”, “Black”

Font Enumeration
Win32: Use existing font list (otmFamilyName) as font families and add 4 generic styles (Regular, Italic, Bold, Bold Italic)
DirectWrite: No issues, full support for families and styles
Cocoa/Cocoa Touch/Core Text: No issues, full support for families and styles
FreeType: No issues, full support for families and styles (FT_String* family_name; FT_String* style_name;)
Android:Use existing font list (/system/fonts) as font families and add 4 generic styles (Regular, Italic, Bold, Bold Italic)

Note About DirectWrite Font Enumeration
When getting the number of fonts in a family, the number of faces returned will generally be a lot larger that actual number of fonts. For example, DirectWrite reports the number of font faces in the Ubuntu font family as 16 which differs from 9 which is the actual number of fonts when viewed through Windows Explorer. This is because DirectWrite automatically adds algorithmic fonts to its font faces list. Most often these are additional Oblique fonts. DirectWrite dynamically creates these fonts on the fly from the original set of fonts. It is possible to detect these fonts using the GetSimulations method on IDWriteFontFace while doing font enumeration to remove them from a font styles list if this is desired.


#3

Very extensive job. I too, were having the same problems with mismatched fonts.
I hope this gets done.

Again, thanks for the elaborated post :slight_smile:


#4

I have partially implemented the updated design. It has gone smoothly so far. By eliminating font styles from the Font class, we are essentially breaking every Juce GUI program out there. However, this is easy to fix by spending a couple minutes with Search & Replace. I think it is well worth the hassle.

So far I have finished updating all the Juce classes, updated the Win32 font enumeration and implemented the DirectWrite font enumeration. I will move onto the mac implementation next.

The results of my existing updates can be seen below.
You can see how Fonts are properly grouped by font family and font style when using DirectWrite rather than the old way of grouping fonts by regular-italic-bold-bold+italic.
You can also see that Arial Black is now rendering properly under DirectWrite unlike the image in my first post.
The top right image is a snippet of me browsing the font family combo box to show off how the Ubuntu fonts are displayed in Win32. Arial Black was still the selected font at the time which is why the sample text is still in that font.


#5

This all looks very impressive, and I think you’re probably right, but I’m trying to concentrate on too many other things right now to give it the attention it deserves… Let me get the modules stuff out the door as a release, and then remind me to look at this properly.


#6

As I finished the implementing the design on Windows, a localization issue became apparent. By replacing an integer based style (Font::bold) with a string based one (“Bold”) I was not sure the implications of this running on non English versions of Windows.

Windows
I don’t have access to localized versions of Windows 7 so testing that way was out of the question. Thankfully, Microsoft has implemented a system known as the Multilingual User Interface which allows Windows users to change their display language through the use of Language Packs. I installed French and Japanese and fired up the font dialog box in Notepad. As you can see the font styles are all localized to the current display language.

I then fired up a copy of JuceFont. Even thought my display language is clearly set to Japanese, JuceFont displays all the styles in English.

This is not that surprising, since the DirectWrite code always gets strings using the “en-us” locale (it tells FindLocaleName to use “en-us” when retrieving strings from IDWriteLocalizedStrings).

While this is great for English speakers, I’m sure non-English speakers would probably prefer localized font style names. Unfortunately, I have not found a way to obtain these strings. As far as I can see, there is no way to do this from DirectWrite. Although DirectWrite returns IDWriteLocalizedStrings for both font families and font styles, these localized strings are the ones embedded in the font themselves. It is up to the font author to embed these strings and only few authors embed more than an English name. Even the few authors, like Microsoft, that do embed multiple languages, don’t add a string for every language other there.

The font style names shown in the Windows 7 CHOOSEFONT dialog box definitely are not coming from the font itself. When viewing the font table of Segoe UI, you can see that all though there are two French localized strings, there is no Japanese localized string.

This is confirmed by enumerating the fonts in DirectWrite and looking at the IDWriteLocalizedStrings of the font faces. The results of this enumeration can be seen in the text below. The results also show the reason there are two French strings in the image above is because one is for French (Canada) and the other is for French (France).

Segoe UI
 face count: 10
 locale count: 1
    en-us
  Light
 locale count: 25
    ca-es
    cs-cz
    da-dk
    de-de
    el-gr
    en-us
    es-es
    es-es_tradnl
    es-mx
    eu-es
    fi-fi
    fr-ca
    fr-fr
    hu-hu
    it-it
    nb-no
    nl-nl
    pl-pl
    pt-br
    pt-pt
    ru-ru
    sk-sk
    sl-si
    sv-se
    tr-tr
  Regular
 locale count: 25
    ca-es
    cs-cz
    da-dk
    de-de
    el-gr
    en-us
    es-es
    es-es_tradnl
    es-mx
    eu-es
    fi-fi
    fr-ca
    fr-fr
    hu-hu
    it-it
    nb-no
    nl-nl
    pl-pl
    pt-br
    pt-pt
    ru-ru
    sk-sk
    sl-si
    sv-se
    tr-tr
  Italic
 locale count: 1
    en-us
  Semibold
 locale count: 25
    ca-es
    cs-cz
    da-dk
    de-de
    el-gr
    en-us
    es-es
    es-es_tradnl
    es-mx
    eu-es
    fi-fi
    fr-ca
    fr-fr
    hu-hu
    it-it
    nb-no
    nl-nl
    pl-pl
    pt-br
    pt-pt
    ru-ru
    sk-sk
    sl-si
    sv-se
    tr-tr
  Bold
 locale count: 25
    ca-es
    cs-cz
    da-dk
    de-de
    el-gr
    en-us
    es-es
    es-es_tradnl
    es-mx
    eu-es
    fi-fi
    fr-ca
    fr-fr
    hu-hu
    it-it
    nb-no
    nl-nl
    pl-pl
    pt-br
    pt-pt
    ru-ru
    sk-sk
    sl-si
    sv-se
    tr-tr
  Bold Italic
 locale count: 1
    en-us
  Light Oblique
 locale count: 1
    en-us
  Oblique
 locale count: 1
    en-us
  Semibold Oblique
 locale count: 1
    en-us
  Bold Oblique

So there is clearly no Japanese localized font style names in the font table or obtainable via DirectWrite. Then where are those string in the CHOOSEFONT dialog coming from? My best guess is from the language packs themselves. I certainly haven’t come across any Win32 APIs that will let me access these strings. I tried using Spy++ to see what was going on in Notepad but the Windows Messages were pretty useless. The last thing I can think of is to use a x86 assembly level debugger on it but I don’t have the time to do that.

Mac
Unlike Windows, I don’t think localized versions of Mac OS X exist. Mac OS X lets you change the display language without requiring you to download any additional language packs. I switched to French and Japanese and fired up the font dialog box in TextEdit. As you can see the font styles are all localized to the current display language.

I am quite puzzled by the results. I’m not sure why some fonts don’t have localized names in certain langauges like the Menlo example. I also find it really odd that Arial wouldn’t have localized font styles in Japanese considering how universal it is.

I then fired up a copy of JuceFont. Even thought my display language is clearly set to Japanese, JuceFont displays all the styles in English.

This confirms that when using CTFontDescriptorCopyAttribute and kFontStyleNameAttribute you will always get the font style in English regardless of the user language.
Changing the call to use CTFontDescriptorCopyLocalizedAttribute and kFontStyleNameAttribute instead gives you the font style in the user’s language.

The following font enumeration was obtained by through Core Text. The results are exactly like the results seen when using the Font dialog with TextEdit.

family: Arial
cfsLocalizedFontStyle: Regular
cfsLocalizedFontStyle: Italic
cfsLocalizedFontStyle: Bold
cfsLocalizedFontStyle: Bold Italic

family: Helvetica
cfsLocalizedFontStyle: レギュラー
cfsLocalizedFontStyle: ライト
cfsLocalizedFontStyle: イタリック
cfsLocalizedFontStyle: ライト・オブリーク
cfsLocalizedFontStyle: ボールド
cfsLocalizedFontStyle: ボールド・イタリック

family: Menlo
cfsLocalizedFontStyle: Regular
cfsLocalizedFontStyle: Italic
cfsLocalizedFontStyle: Bold
cfsLocalizedFontStyle: Bold Italic

So unlike Windows, on Mac OS X and iOS we are able to get the localized font style string via API. However, we are not able to obtain a font using its localized font style string. The only way to obtain a font with a specific font style is to use an English font style string when creating the CTFontDescriptor. According to Apple this is “expected behavior”. So the only way to make it work is create string pairs between localized font styles and English font styles, display the localized font style and finally when a user selects a style, get it’s English counterpart and use that to get the correct font face.

Summary
There is no way to get localized font style names on Windows via API.
We can get localized font style names on Mac OS X and iOS via API.
We cannot used a localized font style name to get a font face on Mac OS X and iOS, we must use an English font style name.
With the updated Juce Font System, all font styles will be displayed in English.
With the updated Juce Font System, font styles which were set in English at compile time will always work at runtime regardless of the user language.

Recommendations
The only way to get localized font styles working on both platforms is to create local files containing font style translation pairs and then use these pairs when displaying font styles (as localized strings) and creating fonts via API (with English strings).


#7

What you are trying to achieve is interesting, but I think you’re probably missing the TRANS and LocalisedString feature.
Ideally, you ©(sh)ould fill the font name & characteristic in the translation mapping.

The number of software displaying a user selectable font list with Juce is probably small.
So under Windows, if one wants to have “Bold” or “Gras”, it’s just a matter of providing the translation file for this. There is no more than few words to translate.

On both Mac and Linux, you can use the “LocalisedString” feature to pre-fill the translation table.
By the way, on Windows, you also figure out the localized font name by doing what is done here (see example on the bottom):
http://msdn.microsoft.com/en-us/library/windows/desktop/dd368214(v=vs.85).aspx


#8

I have finished updating the Core Text, Cocoa and Cocoa Touch font enumeration.

You can see how you are now able to access all 8 fonts styles in Ubuntu rather than just 4 as in the original post. The screenshot is from me using Core Text. I didn’t see a point including a screen shot of Cocoa since it looks identical.

You can see things are working well on iOS as well. Cocoa Touch returns font families only as Postscript names, not as typical font style names. This makes setting non regular font styles at compile time problematic. There is no problem getting a regular font style, the API seems to match usual font names in addition to Postscript names. I don’t think this is a big deal, any one with an iPad has iOS 3.2+ which supports Core Text. The number of iPhones running iOS 2 and 3, where Core Text is unsupported, is getting smaller each day.

Since I am unable to do Linux or Android builds, I won’t be implementing those myself. However both should be pretty straight forward since FreeType already has style strings (FT_String* family_name; FT_String* style_name;) and Android is just adding hard coded generic styles (Regular, Italic, Bold, Bold Italic) like in Win32.

I have passed my changes along to Jules, hopefully they’ll make it into the tip soon.


#9

I think it would have been preferable to get localized font styles via API as the OS has already translated everything. But since that is not possible on Windows, as I said in my recommendations, the only way to implement localized font styles is by utilizing translation pairs. In Juce this would be done using the TRANS and LocalisedString features which you mentioned.

Agreed.

I agree, there aren’t that many font styles to translate, but there are certainly many languages to translate them to.

I suppose we could do this. Though if we already have translation files for Windows we could probably just reuse those for Mac. I have no idea if Linux has these localized strings somewhere.

As I stated earlier:
Although DirectWrite returns IDWriteLocalizedStrings for both font families and font styles, these localized strings are the ones embedded in the font themselves. It is up to the font author to embed these strings and only few authors embed more than an English name. Even the few authors, like Microsoft, that do embed multiple languages, don’t add a string for every language other there.

The font style names shown in the Windows 7 CHOOSEFONT dialog box definitely are not coming from the font itself. When viewing the font table of Segoe UI, you can see that all though there are two French localized strings, there is no Japanese localized string.


#10

Oh, I see. CHOOSEFONT is using LOGFONT structure, and the number of a font variant is small:

FW_THIN	100
FW_EXTRALIGHT	200
FW_ULTRALIGHT	200
FW_LIGHT	300
FW_NORMAL	400
FW_REGULAR	400
FW_MEDIUM	500
FW_SEMIBOLD	600
FW_DEMIBOLD	600
FW_BOLD	700
FW_EXTRABOLD	800
FW_ULTRABOLD	800
FW_HEAVY	900
FW_BLACK	900

So I guess the Choosefont dialog simply does not query the font name themself, but simply have a string array for this type, per langage. That’s clearly one thing we can do on our own too.


#11

It would be REALLY COOL if the Juce Typeface/Font system let me bring in an entire font family, with its various styles and weights, and let me treat it as a single collection with appropriate members (setWeight(), setStyle(), etc)


#12

Nudge to fix fonts now that Release 2.0 is out.


#13

So these screenshots are making me insanely jealous :slight_smile:


#14

Ok, I just had a look at this…

In principle, extending the styles like this is great. I wish I’d done it like that in the first place. But in practice, the changes you’ve suggested to the Font class would break all existing code that has a GUI!

Could the same thing be achieved while still keeping the Font::plain, Font::bold, setStyleFlags, setBold, isBold, etc? Obviously I understand that there’s no foolproof mapping between a named style and the idea of “bold”, “italic”, etc, but there’s really no way I could consider removing functionality that’s used in every single app out there!


#15

Could we keep backward compatibility by introducing a new class (e.g. ExtendedFont, or Fontex) ?


#16

I don’t think there’d need to be a new class - we could just add some new methods to provide the more advanced style choices if people need them.

And TBH there’s also a basic usability problem with this new stuff - if you have a function that is passed some kind of font, and it then needs to temporarily use a bold version of it, how would you write that? All those methods like Font::boldened(), italicised() etc, are incredibly useful, and definitely not something that could be removed!


#17

Yes it would. Though you should be able to update nearly all of the code using Search & Replace.

Fair enough.

Maybe. Are you ok with altering Font and Typeface to at least always have a font family and font style?

Based on my experience (and as you can see in all those screenshots), if there are bold and italic versions of a font, they always have style names “Bold”, “Italic”.

That may require a lot of changes to existing Juce components. It would be nice to have a totally new set of components all based on TextLayout so they could all render complex text.

return Font (this.getHeight(),this.getTypefaceFamily(), “Bold”); ?

isBold(), isItalic(), getStyleFlags() should all be fine. boldended() and italicised() return copies so they should be fine too.
The problem is really setBold(), setItalic() and setStyleFlags() as every font and typeface has a set family name and style name.


#18

Oh yes, I’m totally happy to change the underlying dat structure, and the Typeface class isn’t a problem because very few people use it directly. I just need Font to stay backwards-compatible.

Well, if that’s the case, all the existing functionality should be easy enough to replicate… I suspect that with a few heuristics it shouldn’t be hard to replicate all the bold/italic functions reliably.


#19

Yes that should be the case. Even when the user’s language is set to something non-English, the font style names are always all in English so it should be fine (for more details see my previous posts).

The reason I removed all those functions in my code is they seemed redundant with the ability to set the font style directly by name.

Agreed.


#20

Well, just because something can be done a different way doesn’t mean it’s redundant. Even if the style could be set by a string, I’d always prefer to write “someFont.boldened()” than “someFont.withStyle (“Bold”)”, because it avoids magic string literals in your code, and saves me having to worry about exactly what the string should be and whether it’ll be correct for all fonts/OSes.