FX Experience Has Gone Read-Only

I've been maintaining FX Experience for a really long time now, and I love hearing from people who enjoy my weekly links roundup. One thing I've noticed recently is that maintaining two sites (FX Experience and JonathanGiles.net) takes more time than ideal, and splits the audience up. Therefore, FX Experience will become read-only for new blog posts, but weekly posts will continue to be published on JonathanGiles.net. If you follow @FXExperience on Twitter, I suggest you also follow @JonathanGiles. This is not the end - just a consolidation of my online presence to make my life a little easier!

tl;dr: Follow me on Twitter and check for the latest news on JonathanGiles.net.

I was going through my bug list today and noticed this bug, Document how to embed fonts in FX apps. A quick google search turned up a single forum posting on the subject which just linked back to the root bug (lucky for me I put the solution in the bug report so people weren’t completely stuck). Surprisingly, I couldn’t find any documentation on the subject here at FXExperience.com, so I thought to rectify that with a short post explaining the subject.

I want to first say that the technique I’m about to describe is supported on all three platforms. This technique requires bundling the font files with your jars. So be sure that you have the rights to redistribute your chosen font files in this manner. We are looking at the CSS 3 specification for web fonts and hope to be able to support it fully when it goes final.

Within your application JAR file is a META-INF directory. It is not uncommon for frameworks to put special properties files or other files into this directory. At runtime, they read these files to bootstrap themselves. This is essentially what we are doing in JavaFX. To embed a TrueType font with a JavaFX application, you need to:

  1. Add the ttf file to some directory in your source tree
  2. Create a fonts.mf file
  3. Add lines to the fonts.mf file mapping a “logical name” to the path of the embedded ttf file
  4. Modify your build process to copy the fonts.mf file into the META-INF of your final JAR file
  5. In your JavaFX Script file, simply create a font by name referring to the “logical name” you entered in your fonts.mf file.

The fonts.mf file is nothing more than a properties file, mapping logical names to actual font files. The reason we took this approach as opposed to simply allowing you to load a font from an input stream, is that such dynamic font loading was not supported on the MIDP based JavaFX mobile implementation.

For this tip, I’ve chosen to use one of the fonts which are free for non-commercial use at 1001 Free Fonts. It is called Birth of a Hero. You can just download that file, and extract the ttf file into a directory of your choosing. I created a project called “Hero” which consists of a single package also called “hero” (note the lower case). I extracted the “BIRTH OF A HERO.ttf” file into that package.

I then created the following fonts.mf file and put it in my projects root directory (using NetBeans, I did this by using the “Files” tab).

Hero=/hero/BIRTH\ OF\ A\ HERO.ttf

I could have renamed the TTF file to be something without spaces, but decided for the sake of example to show also how to deal with spaces in file names in the fonts.mf file. Note the leading slash on the path. This is very important.

I next had to modify my build script to make sure fonts.mf gets placed in the application JAR file. In my NetBeans project, I did this by using the following:

<target name="-pre-compile" depends="init">
    <mkdir dir="${build.dir}/compiled/META-INF"/>
    <copy todir="${build.dir}/compiled/META-INF" file="fonts.mf" />
</target>

Now that I have the ttf file and fonts.mf in my jar, I simply have to use the font from JavaFX:

Label {
    text: "Birth of a Hero"
    font: Font { name:"Hero" size: 128 }
}

Now when I run this application, I get a warning message printed which says “Failed to get style for font ‘Hero'”. You can safely ignore this, but it is annoying and actually it is telling us something useful. If you inspect the style of the Font, you would find that there is not one. If you would like a style, you need to build it into the name. For example, lets change the name of our font from “Hero” to “Hero Regular”. We will then find that the style of the font is “Regular” and the warning message goes away.

Hero\ Regular=/hero/BIRTH\ OF\ A\ HERO.ttf

import javafx.scene.text.Font;
import javafx.scene.control.Label;

Label {
    text: "Birth of a Hero"
    font: Font { name: "Hero Regular" size: 128 }
}

And here is our finished product:

Embedded "Birth of a Hero" font in JavaFX