Example code for creating & using Custom Typeface?

Hi all:

I want to use a Custom Typeface for my cross-platform audio plugin (I cheated on my last plugin, and used Arial). However, I find the whole Custom Typeface issue confusing. For starters, what is the difference between a Typeface and a Font? The documentation is pretty fuzzy on this.

Anyway, if anyone can provide some example code on how to take an existing font, turn it into a Custom Typeface, and use it within a Juce component, it would help me immeasurably. Explanations of the process are useful as well, but example code would be very handy.

Thanks,

Sean Costello

Basically, a Typeface is just a generic font name, e.g. “Times”, and a Font is a typeface plus a size and style, e.g. “Times Bold, 15 point”

Just in case you don’t want to rely on the custom font being installed, but want to embed the font in your binary you should

  1. have a look at the example code in this thread to convert a ttf-file into juce’s binary font format: http://www.rawmaterialsoftware.com/viewtopic.php?f=2&t=2119
  2. use the “binary builder” tool from the juce distribution to convert the binary output file of the FontBuilder into a .cpp/.h pair, named MyResources for example
  3. finally you create an instance of your embededed typeface with something like

MemoryInputStream mis(MyResources::my_font_bin, MyResources::my_font_binSize, false); Typeface* typeface = new CustomTypeface(mis);

Not knowing about the binary builder tool, It took me a while to figure this workflow out with the help of the available documentation and existing forum posts, so this synopsis might help anyone else struggling with the same issue.

1 Like

some time ago i updated the font serializer and am still using with the latest juce, http://rawmaterialsoftware.com/viewtopic.php?f=6&t=54&hilit=fserialize&start=30

[quote=“steffen”]Just in case you don’t want to rely on the custom font being installed, but want to embed the font in your binary you should

  1. have a look at the example code in this thread to convert a ttf-file into juce’s binary font format: http://www.rawmaterialsoftware.com/viewtopic.php?f=2&t=2119
  2. use the “binary builder” tool from the juce distribution to convert the binary output file of the FontBuilder into a .cpp/.h pair, named MyResources for example
  3. finally you create an instance of your embededed typeface with something like

MemoryInputStream mis(MyResources::my_font_bin, MyResources::my_font_binSize, false); Typeface* typeface = new CustomTypeface(mis);

Not knowing about the binary builder tool, It took me a while to figure this workflow out with the help of the available documentation and existing forum posts, so this synopsis might help anyone else struggling with the same issue.[/quote]

OK, I have all of that working, but how do I turn the Typeface into a Font I can use? Ideally I could set this in the LookAndFeel. Example code always helpful.

Thanks,

Sean Costello

Typeface t;
Font f (t);
f.setHeight (etc…)

Or use LookAndFeel::getTypefaceForFont to apply your typeface to any fonts that you want to.

[quote=“jules”]

Or use LookAndFeel::getTypefaceForFont to apply your typeface to any fonts that you want to.[/quote]

OK, THAT’S the ticket. I now have platform independent fonts running in my plugin. Which means that I can start sending the Windows beta out to my beta testers. Huzzah!

For those that are as C++ impaired as I am, here’s how I did it, in excruciating detail (EDIT: now updated to use the correct reference counted pointer for the Typeface in the Look & Feel):

  • In the new Jucer, create a new Console app, and call it Font Serializer
  • Paste the following code into your Main.cpp file, overwriting everything else in there:
#include "../JuceLibraryCode/JuceHeader.h"
#include <iostream>

int main (int argc, char* argv[])
{
    // This object makes sure that Juce is initialised and shut down correctly
    // for the scope of this function call. Make sure this declaration is the
    // first statement of this function.
    const ScopedJuceInitialiser_NonGUI juceSystemInitialiser;
    
    printf ("\n\n--------------------------------\n Font Serialiser by Niall Moody\n--------------------------------\n\n");
    
    if (argc != 3)
    {
        printf (" Usage: FontSerialiser <filename> <fontname>\n\n");
        printf (" FontSerialiser will turn a font into a compressed binary file.\n\n\n");
        
        return 1;
    }
    
    // because we're not using the proper application startup procedure, we need to call
    // this explicitly here to initialise some of the time-related stuff..
    initialiseJuce_GUI();
    
    // get file and font name from command line arguments
    const File destFile (File::getCurrentWorkingDirectory().getChildFile (argv[1]));
    String fontName(argv[2]);
    
    // make sure the destination file can be written to
    OutputStream *destStream = destFile.createOutputStream();
    if (destStream == 0)
    {
        String error;
        error << "\nError : Couldn't open " << destFile.getFullPathName() << " for writing.\n\n";
        std::cout << error;
        return 2;
    }
    
    // make sure the font is installed on the current system
    StringArray fontNames = Font::findAllTypefaceNames();
    if(!fontNames.contains(fontName))
    {
        String error ("\nError: The font " + fontName + " does not exist in the system\n");
        std::cout << error;
        return 3;
    }
    
    // load the font as a system-Typeface
    Font font(fontName, 10, 0);
    if(!Typeface::createSystemTypefaceFor  (font))
    {
        String error ("\nError : Where's the font?\n\n");
        std::cout << error;
        return 4;
    }
    
    
    // copy the font-properties to a CustomTypeface
    CustomTypeface customTypeface;
    customTypeface.setCharacteristics(font.getTypefaceName(), font.getAscent(),
                                      font.isBold(), font.isItalic(), ' ');
    // Here's the important part: copy all glyphs to a new instance of CustomTypeface
    customTypeface.addGlyphsFromOtherTypeface( *font.getTypeface(), 0, 256);
    
    
    // finally write the typeface into the destination file
    customTypeface.writeToStream(*destStream);
    
    String op;
    op << "\nWrote font " << fontName << " to file " << destFile.getFullPathName() << " successfully.\n\n";
    std::cout << op;
    
    delete destStream;
    
    std::cout << "\n(You might want to use Binary Builder to turn this file into a c++ file now)\n\n ";
    
    // this avoids leaking the LookAndFeel instance
        shutdownJuce_GUI();

        return 0;
}
  • Figure out whatever type of shell magic is needed to run this program on your system. In OS 10.6, I had to run the following code in Terminal, after having an old friend of mine who is skilled in Unix hold my hand (he also gave me a Korg Monotron last week - now that’s a good friend):

I had previously copied the FontSerializer executable to the ~/SDKs/utilities folder.

  • Run FontSerializer from the command window, using the proper arguments. Let’s say that you wanted to serialize a font called Helvetico. You would type something like this at the command prompt:

FontSerializer ~/SDKs/utilities/sourcefolder/HelveticoSerialized Helvetico

In this case, ~/SDKs/utilities/sourcefolder is the path to a folder called “sourcefolder” that we will be using in the next step. HelveticoSerialized is the name of the serialized font that we want to work with, and Helvetico is the source font located on our system.

  • Next, use BinaryBuilder to turn the serialized font binary into a bunch of scrambled ASCII stored inside a .cpp/h pair. In order to do this, build the BinaryBuilder from the Juce distribution (look in the extras folder). You may need to do some shell magic to be able to run this program from the command prompt. Once you have this all figured out, at the command prompt type

BinaryBuilder ~/SDKs/utilities/sourcefolder ~/SDKs/utilities/destinationfolder HelveticoResource

where the first argument is the folder that contains the binary file(s) you want to put into code, the destination folder is where the code will be written, and the third argument is the name of your code.

  • In your project, edit the .h file of your Look and Feel class to include the .h file of the files you just generated. You should probably copy the files to some place you know that they will be safe.

  • In the LookAndFeel .h file, add the following line in the class declaration (I put it in Public):

  • In the LookAndFeel .c file, add the following lines to the constuctor:
MemoryInputStream mis(HelveticoResource::helveticoserialized, HelveticoResource::helveticoserializedSize, false);
HelveticoTf = new CustomTypeface (mis);
  • In the destructor, be sure to delete HelveticoTf.

  • You will want to override the default getTypefaceForFont() function in your look in feel. The function can be as simple as

And that’s it. Custom typeface in your Juce project, platform independent.

Thanks for the help on this one.

Sean Costello

Alright, looks like I spoke too soon. My AU is crashing now. It looks like the crash is caused by the following code:

const Typeface::Ptr ValhallaShimmerLookAndFeel::getTypefaceForFont (const Font &font) { return FuturaTf; }

Everything else is as listed above.

When I open a new Logic 9.1.1 project in OS 10.6, and try to open a new instance of my plugin, Logic crashes. Like, instacrash. Here’s some crash report info:

[code]Exception Type: EXC_BAD_ACCESS (SIGABRT)
Exception Codes: KERN_PROTECTION_FAILURE at 0x0000000000000028
Crashed Thread: 0 Dispatch queue: com.apple.main-thread

Application Specific Information:
abort() called

Thread 0 Crashed: Dispatch queue: com.apple.main-thread
0 libSystem.B.dylib 0x9297cef6 __kill + 10
1 libSystem.B.dylib 0x9297cee8 kill$UNIX2003 + 32
2 libSystem.B.dylib 0x92a0f62d raise + 26
3 libSystem.B.dylib 0x92a25679 __abort + 124
4 libSystem.B.dylib 0x92a256f5 abort_report_np + 0
5 com.apple.logic.pro 0x00405649 std::ostream& TraceOutContainer(std::ostream&, CEvs, char const*, int) + 3974297
6 libSystem.B.dylib 0x929821fb _sigtramp + 43
7 libstdc++.6.dylib 0x98d6903f __dynamic_cast + 114
8 …ValhallaDSP.ValhallaShimmer 0x46a299c5 juce::NSViewComponentPeer::drawRect(_NSRect) + 2437
9 …ValhallaDSP.ValhallaShimmer 0x46a1f797 -[JuceNSView_1_52_51_ljlu drawRect:] + 55
10 com.apple.AppKit 0x9386fa36 -[NSView _drawRect:clip:] + 3510
11 com.apple.AppKit 0x9386e6d4 -[NSView _recursiveDisplayAllDirtyWithLockFocus:visRect:] + 1600
12 com.apple.AppKit 0x9386ea09 -[NSView _recursiveDisplayAllDirtyWithLockFocus:visRect:] + 2421
13 com.apple.AppKit 0x9386ea09 -[NSView _recursiveDisplayAllDirtyWithLockFocus:visRect:] + 2421
14 com.apple.AppKit 0x9386ea09 -[NSView _recursiveDisplayAllDirtyWithLockFocus:visRect:] + 2421
15 com.apple.AppKit 0x9386ea09 -[NSView _recursiveDisplayAllDirtyWithLockFocus:visRect:] + 2421
16 com.apple.AppKit 0x9386ea09 -[NSView _recursiveDisplayAllDirtyWithLockFocus:visRect:] + 2421
17 com.apple.AppKit 0x9386cbf3 -[NSView _recursiveDisplayRectIfNeededIgnoringOpacity:isVisibleRect:rectIsVisibleRectForView:topView:] + 711
18 com.apple.AppKit 0x9386db68 -[NSView _recursiveDisplayRectIfNeededIgnoringOpacity:isVisibleRect:rectIsVisibleRectForView:topView:] + 4668
19 com.apple.AppKit 0x9386db68 -[NSView _recursiveDisplayRectIfNeededIgnoringOpacity:isVisibleRect:rectIsVisibleRectForView:topView:] + 4668
20 com.apple.AppKit 0x9386c767 -[NSThemeFrame _recursiveDisplayRectIfNeededIgnoringOpacity:isVisibleRect:rectIsVisibleRectForView:topView:] + 265
21 com.apple.prokit 0x00feea50 -[NSProWindowFrame _recursiveDisplayRectIfNeededIgnoringOpacity:isVisibleRect:rectIsVisibleRectForView:topView:] + 105
22 com.apple.AppKit 0x938690ae -[NSView _displayRectIgnoringOpacity:isVisibleRect:rectIsVisibleRectForView:] + 3309
23 com.apple.AppKit 0x937c9d3f -[NSView displayIfNeeded] + 818
24 com.apple.AppKit 0x93793050 -[NSWindow displayIfNeeded] + 204
25 com.apple.AppKit 0x93791aeb -[NSWindow _reallyDoOrderWindow:relativeTo:findKey:forCounter:force:isModal:] + 1085
26 com.apple.prokit 0x00fcb195 -[NSProPanel(_ProSplash) _reallyDoOrderWindow:relativeTo:findKey:forCounter:force:isModal:] + 181
27 com.apple.AppKit 0x9379164d -[NSWindow orderWindow:relativeTo:] + 105
28 com.apple.prokit 0x00fe87fd -[NSProPanel orderWindow:relativeTo:] + 74
29 com.apple.logic.pro 0x0092baeb void UnitTest::CheckEqual<ScTypeSetter::tVerticalAlignment, ScTypeSetter::tVerticalAlignment>(UnitTest::TestResults&, ScTypeSetter::tVerticalAlignment, ScTypeSetter::tVerticalAlignment, UnitTest::TestDetails const&) + 20059
30 com.apple.AppKit 0x9390baad -[NSWindow orderFront:] + 50
31 com.apple.logic.pro 0x0092c0e5 void UnitTest::CheckEqual<ScTypeSetter::tVerticalAlignment, ScTypeSetter::tVerticalAlignment>(UnitTest::TestResults&, ScTypeSetter::tVerticalAlignment, ScTypeSetter::tVerticalAlignment, UnitTest::TestDetails const&) + 21589
32 com.apple.logic.pro 0x00753c79 std::ostream& TraceOutContainer(std::ostream&, CEvs, char const*, int) + 7441097
33 com.apple.logic.pro 0x00753a51 std::ostream& TraceOutContainer(std::ostream&, CEvs, char const*, int) + 7440545
34 com.apple.logic.pro 0x001ab54f std::ostream& TraceOutContainer(std::ostream&, CEvs, char const*, int) + 1508255
35 com.apple.logic.pro 0x000cccb7 std::ostream& TraceOutContainer(std::ostream&, CEvs, char const*, int) + 596743
36 com.apple.logic.pro 0x001eed04 std::ostream& TraceOutContainer(std::ostream&, CEvs, char const*, int) + 1784660
37 com.apple.logic.pro 0x0062e207 std::ostream& TraceOutContainer(std::ostream&, CEvs, char const*, int) + 6238295
38 com.apple.Foundation 0x991a0968 __NSFireTimer + 141
39 com.apple.CoreFoundation 0x9648570b __CFRunLoopRun + 8059
40 com.apple.CoreFoundation 0x96483094 CFRunLoopRunSpecific + 452
41 com.apple.CoreFoundation 0x96482ec1 CFRunLoopRunInMode + 97
42 com.apple.HIToolbox 0x93217f9c RunCurrentEventLoopInMode + 392
43 com.apple.HIToolbox 0x93217d51 ReceiveNextEventCommon + 354
44 com.apple.HIToolbox 0x93217bd6 BlockUntilNextEventMatchingListInMode + 81
45 com.apple.AppKit 0x9379aa89 _DPSNextEvent + 847
46 com.apple.AppKit 0x9379a2ca -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] + 156
47 com.apple.AppKit 0x9375c55b -[NSApplication run] + 821
48 com.apple.prokit 0x00fcaab1 NSProApplicationMain + 326
49 com.apple.logic.pro 0x0002aa55 DummyConnection::DummyConnection() + 193

[/code]

Any idea what is going on? If I comment out the getTypefaceForFont() method, no crashes happen, but my fonts aren’t being set when I follow the above code. If I am making some stupid mistake, now is the time to correct it.

VST works fine with the above code, BTW. Of course. It is just the Audio Units that are crashing. I haven’t tried RTAS.

Thanks in advance for help on this,

Sean

Well, what is “FuturaTf”? Is it correctly initialised when you use it? It’s not a static variable, is it?

“FuturaTF” is my clever code name for my Futura typeface. In my L&F header, I have

Typeface *FuturaTf;

In the L&F constructor, there is

MemoryInputStream mis(ShimmerFont::valhallageometric_jfont, ShimmerFont::valhallageometric_jfontSize, false); FuturaTf = new CustomTypeface (mis);

I delete FuturaTf in the destructor.

The code that seems to trigger the crash is

const Typeface::Ptr ValhallaShimmerLookAndFeel::getTypefaceForFont (const Font &font) { return FuturaTf; }

Thanks,

Sean

Doh! Think about what happens when that method takes your normal pointer and returns it as a ref-counted pointer! You need to store it as a Typeface::Ptr. (And obviously don’t delete it!)

OK, I have no idea what a ref-counted pointer is. I’m a DSP guy. Could you explain exactly what you mean here? Source code always helps.

Thanks,

Sean

Well, have a look at ReferenceCountedObject and ReferenceCountedObjectPtr.

Really, all you’d need to do is use Typeface::Ptr instead of Typeface*. And don’t delete it, of course.

OK, I replaced the line in my L&F header with

Things work now. Thanks for the help. Time to sleep (2:54 am here on the West Coast of the US).

Sean

Sleep well! Ref counted pointers are a very useful tool to know about.

You should never be using the delete operator to delete objects in your code anyway - always use a ScopedPointer or ReferenceCountedPointer or stack object instead.

Hey, I just followed all the directions in this thread and it worked perfectly. Thanks!

I'm about to embark on this journey, to get cross platform fonts working (Android initially). 

Am I right in thinking I can use the BinaryBuilder to do the font serializing part of the process? 

Also - I think this deserves a proper tutorial! 

2 Likes

Hey Sean, is this still the method you are using? Just curious since this post was almost 7 years ago, just wanted to see if this was still the least painful way of doing this.

I’ve used those threads a few weeks ago.
Here is my code:

Constructor:

    Font f = Font(Font::getDefaultSansSerifFontName(), 14, 0);
    getTypefaceForFont(f);
    setDefaultSansSerifTypefaceName("<font name goes here>");
    lineupDefaultFont.setTypefaceName("<font name goes here>");

The main method needed to be override:

Typeface::Ptr MyLandF::getTypefaceForFont (const Font &font)
{
    Typeface::Ptr tf;
    String faceName (font.getTypefaceName());
    // Make requests for the default sans serif font use our
    // FreeType hinted font instead.
    if (faceName == Font::getDefaultSansSerifFontName())
    {
        // Create a new Font identical to the old one, then
        // switch the name to our hinted font.
        Font f (font);
        // You'll need to know the exact name embedded in the font. There
        // are a variety of free programs for retrieving this information.
        f.setTypefaceName ("font name goes here");
        // Now get the hinted typeface.
        tf = Typeface::createSystemTypefaceFor(BinaryData::FontFilename_ttf, BinaryData::FontFilename_ttfSize);
    }
    // If we got here without creating a new typeface
    // then just use the default LookAndFeel behavior.
    if (!tf)
    {
        tf = LookAndFeel::getTypefaceForFont (font);
    }
    return tf;
}

For global LookAndFeel I’ve noticed it’s advisable mostly to try and override those:

Font getTextButtonFont (TextButton&, int buttonHeight) override;
Font getAlertWindowTitleFont() override;
Font getAlertWindowMessageFont() override;
Font getAlertWindowFont() override;

Font getPopupMenuFont() override;
Font getMenuBarFont (MenuBarComponent&, int itemIndex, const String& itemText) override;
Font getComboBoxFont (ComboBox&) override;
Font getLabelFont (Label&) override;
Font getSliderPopupFont (Slider&) override;
1 Like

The line of code in the L&F header here seems to have vanished, anyone know what it should be? I gather it’s something to do with the Typeface::Ptr instantiation?

I’m getting an IntelliSense error when trying to create a MemoryInputStream using the serialized font binary which I’ve sent through the BinaryBuilder as per Sean’s instructions. I’m hoping this missing line is the magic sauce I need.