Hello!
In this article, I will try to briefly describe how Java Virtual Machine works with fonts. Once I needed to change the font used by the JVM and, surprisingly, found only pieces of legacy information about this. I spent a little time investigating the problem and now want to share this information with anybody who could find it useful. Feel free to leave any comments :)
First of all, let's agree on definitions:
TrueType - font technology, the way to describe fonts in vector format (
.ttf
files).FreeType - library to transform vector fonts to bitmaps.
Logical font - front-end representation of fonts in Java;
Font
class and its instances.Physical font - font file in the system (e.g.
.ttf
file).
How letters are drawn?
The logical font is set in the Java program (manually or automatically during the creation of instances of some AWT/Swing classes). The five Java logical fonts Dialog, DialogInput, Monospaced, SansSerif and Serif must be supported by any JRE [2].
The next step happens during the execution of compiled Java Bytecode. JVM maps logical font onto the most suitable physical font available on the machine (JRE supports TrueType and PostScript Type 1 fonts). Mapping is done through a special mapfile (font configuration file). Old documentation says that font configuration files are located in $JAVA_HOME/lib
. One file is chosen depending on OS and OS version. If there is no suitable file then default fontconfig.properties
is chosen. [3]
I found that these files exist only in Oracle JDK (in $JAVA_HOME/jre/lib/
) and have names like fontconfig.properties.src
. There were no such files in other OpenJDK builds I investigated...
Tracing the execution of a simple Java program that works with fonts showed the following (this is true for all JDKs, including Oracle):
$ javac FontView.java
$ strace -f java FontView &> out.txt
$ grep open out.txt | grep properties
...
[pid 24909] openat(AT_FDCWD, "/home/user/.java/fonts/1.8.0_222/fcinfo-1-pc-Ubuntu-16.04-en.properties", O_RDONLY) = 5
Here it is! The actual location of Java's font configuration file (mapfile) is $HOME/.java/fonts/[version]
. If there is no such file, JVM creates it automatically and fills it with the fonts matched with the help of libfontconfig
. This makes me believe that $JAVA_HOME/lib/fontconfig.properties
(a.k.a, $JAVA_HOME/jre/lib/fontconfig.properties.src
) is some kind of legacy...
To draw physical fonts as accurately as possible, fontconfig library (libfontconfig
) is used. Long story short, vector fonts are configured with several configuration values, e.g. antialias, embolden, dpi, or size. libfontconfig
is used to deal with all these dependencies.
Finally, libfontconfig
calls for FreeType library. Now the text is ready to be mapped from memory to our screen :)
How to change the font?
The simplest way is described. The whole system will use a directory with your desired fonts only. fonts.conf
is responsible for this (it tells which physical fonts are available for OS and programs). If you want a more advanced setup of fonts.conf
then read additional sources.
Install the desired physical font on the system (e.g. place
.ttf
files to/etc/fonts/my-custom-fonts
).Check that
/etc/fonts/font.conf
contains the line<dir>/etc/fonts/my-custom-fonts</dir>
inside of<fontconfig>
tag and there are no other<dir>
tags.Remove old configuration file(s):
$ rm $HOME/.java/fonts/[version]/*
Run any program with text and fonts to create a new mapfile:
$ java FontView
Check that Java uses new physical font now:
$ cat $HOME/.java/fonts/[version]/[fontconfig file].properties