What Java Programmers Should Know About Fonts

Understanding Fonts

The term font is vague and different people use it in different ways.  For now assume a font an array of tiny graphics, created by an artist so they share a particular look, and which map to letters, digits, and other characters.  (For the real story search the Internet and visit Unicode.org and see the javadoc API comments for java.awt.Font.)  These graphics are called glyphs.  Glyphs are to characters what numerals are to numbers: a visual representation of an abstract concept (e.g. the letter A).  Many different glyphs can represent the same character; they just look different.

Type designers knew some things about how humans read text, and devised serif fonts which are letter shapes composed of lines (or strokes) of varying thickness and small extra bits on the ends of the strokes.  Text of such fonts is much easier to read and pretty much all books and magazines use serif fonts for body text.  (As should you!)

Text without the extra bits, and often drawn with lines of constant thickness, are called sans-serif (sans is French for without) and are used for attention-grabbing such as for headings and captions.

In the early computer era usually a single screen font was built into terminals.  Printers were based on daisy-wheel or line-printer technology, again that supported a single font.  These early computer screens and printers were limited to drawing each character in the same sized rectangular block.  Such fonts are called mono-spaced since all characters take up the same amount of horizontal space.  This leads to an un-even appearance as fat letters such as 'm' take the same space as skinny letters such as 'i'.  As the technology grew more sophisticated computers and printers became capable of displaying traditional fonts called proportional.  In these fonts the space between the characters is the same, giving the text an even appearance.  (Are you reading this in a mono-spaced or proportional font?  Look at this to decide:  MMMMllll.)

So a font can be either proportional or mono-spaced.  It can have serifs or be sans-serif.  That's four possibilities, but fonts can have other attributes such as heaviness of the strokes (e.g., bold) or if the letters are straight (roman) or slanted (italics).

Early Unix systems used X Window fonts that were named for each of 14 possible font attributes.  You could find a bold, 12 point (one point is roughly 1/72 of an inch) font by doing a directory listing for:
-*-*-bold-*-*-*-12-*-*-*-*-*-*-*
which might find the font file:
-adobe-utopia-bold-r-normal--12-120-75-75-p-70-iso8859-1
Nowadays fonts have names such as Helvetica or Bookman DemiBold which is much less helpful.

When Java programmers set a font to use, they typically don't know what fonts are available on the user's system.  So if you guess to use a font Arial it may or may not be available.  However all home computers ship with a set of fonts standard for that platform.

So for every platform Sun supports, they picked 3 available fonts (these are the actual fonts installed and are called physical) and gave them names you can use in your program.  The names are called logical font names since the names reflect the type of the font:

So in your program you can chose the system-specific mono-spaced font for code listings, sans-serif for headings and captions, and serif for body text.  And you don't need to know the real or physical name for that font.

Most home computer systems also have a distinct look and feel to the pop-up dialog boxes and other system elements (e.g., window titles).  You can use these as well as Sun has kindly defined the system standard fonts for dialog boxes as Dialog and DialogInput logical font names, so you can make your dialogs appear native.

Modern AWT does include classes and methods to list all available fonts installed so you can look for specific (physical) fonts.  However in AWT you are limited to only using the fonts chosen for the logical font names!   With swing you can use any font available.  Consider the following code:

import javax.swing.*;
public class Fonts2 extends JFrame {
  public static void main ( String [] args ) {
    JFrame f = new Fonts();
    f.setSize(200,300);
    f.setVisible(true);
    JLabel lbl = new JLabel("hello world");
    lbl.setFont( 
      new Font("Broadway", Font.PLAIN, 24) );
    f.add( lbl );
  }
}
import java.awt.*;
public class Fonts1 extends Frame {
  public static void main ( String [] args ) {
    Frame f = new Fonts();
    f.setSize(200,300);
    f.setVisible(true);
    Label lbl = new Label("hello world");
    lbl.setFont( 
      new Font("Broadway", Font.PLAIN, 24) );
    f.add( lbl );
  }
}

Only the swing version will display the label in the selected font.  (If you don't have Broadway font available on your system, choose a different font, but make sure it is weird enough so you will see the difference between swing and AWT.)

Here's some code to list all installed fonts, and also a list of all the fonts available for use by Java:

import java.awt.*;

public class ShowFonts {
public static void main ( String [] args ) {
   Font[] fonts = GraphicsEnvironment.getLocalGraphicsEnvironment()
                  .getAllFonts();
   for ( int i = 0; i < fonts.length; ++i ) {
      System.out.print( fonts[i].getFontName() + " : " );
      System.out.print( fonts[i].getFamily() + " : " );
      System.out.println( fonts[i].getName() );
   }
   System.out.println( "\n\n\tAvailable Fonts:\n" );
   String[] names = GraphicsEnvironment.getLocalGraphicsEnvironment()
                  .getAvailableFontFamilyNames();
    for ( int i = 0; i < names.length; ++i )
       System.out.println( names[i] );
}  // end of main
}

Still it may be a problem to not know the actual font used by some logical font name, as different proportional fonts can do line breaks in different places and mess up the carefully planned appearance of your application or applet.  For this reason the JRE ships with a set of related fonts called Lucida.  These physical fonts are available in all Sun JREs and include mono-spaced, Sans-Serif, and Serif versions.  (Look in .../jre/lib/fonts on your system.)

In short, Sun as identified three of the fonts on each platform and given them logical names.  You can use one of these three or the platform-specific dialog fonts (making five logical font names in all), or pick some actual font name (a physical font) and hope it is available.  It will be if you pick Lucida and you have a Sun JRE.

Remember each font is a collection (an array or vector) of graphics known as glyphs.  (It's more than that really.)  Each glyph is identified by a number.  For example a capital A glyph in any font that has such a glyph is identified by the number 65.  Of course anyone can make their own collection of glyphs as a font, and identify any glyph with any number.  For Java and most software today the mapping of numbers to glyphs is the one defined by the Unicode standard.  The numbers are called code points.

Unicode had defined numbers for many thousands of glyphs and it is unlikely you have a single font file that has every glyph defined by Unicode.  This can be a problem since you don't always know if the font you're using has a glyph for all the symbols, arrows, smiley faces, Greek letters, math and engineering symbols, etc., that you might want to use.  If that is a problem you can find a font that can display the characters you need with code such as this:

 for (Iterator<Font> i = fontList.iterator(); i.hasNext(); ) {
    Font f = i.next();
    if ( ! f.canDisplay( '\u25B6' ) )
       i.remove();
 }

(See UnicodeSymbols.java for a sample applet with source that does this.)  Fonts will claim they can display a character if it falls in the covered range of that font, even if there is no glyph for it!

There are a couple of other issues you should know about.  One is that Java defines the size of fonts in points, which should be 1/72 of an inch.  This unit worked well with early font technology since dot-matrix printers and computer monitors had 72 pixels to the inch.  (Horizontally anyway; monitors often use rectangular pixels that are taller than they are wide.)  A shortcut was taken for fonts where the font designers assumed 1 point = 1 pixel.  Todays monitors can use much smaller pixels and they are spaced closer together.  This is called the monitor's DPI (dots per inch).  This is why when you increase a monitor's resolution, most fonts come out looking tiny.  Some software is smart enough to correct for that; AWT toolkit provides a method to find the DPI value.

When using logical font names, Java may not be able to pick a single physical font that contains all the Unicode characters, or even a significant number of them.  This is because many physical fonts only define 256 glyphs.  In this case the logical font name may actually refer to several physical fonts that are stitched together to define lots more glyphs than any one standard font available on your system.  On my Java6 JRE on Windows XP, the fontconfig.properties file defines the logical font serif to use the physical font Times New Roman for the standard 256 alphabetic glyphs, and the font MS Mincho for any Japanese glyphs.  By using the logical font name Serif you can use specify any Latin, Chinese, Hebrew, Japanese, or Korean glyph and it will display correctly, even though there isn't a single physical font that contains all those glyphs in the Windows XP standard set of fonts.  If you used the physical font Times New Roman and your text contained the Unicode number for some Japanese glyph, it won't display correctly.  (Usually the system displays a square in these cases.)

Another issue is that different font files store the glyph data in different formats.  Your software must be able to read the format or it can't use the font.  Currently Java 1.6 for Windows platform can read TrueType and OpenType font formats, but probably not other formats such as PostScript Type 1 fonts.  On the other hand with older Unix/Linux systems, Java 6 seems to only recognize PostScript fonts and not TrueType or OpenType.  (There are other standards for font files such as FreeType; Web pages will get support in HTML 5 for a new font system called WOFF (Web Open Font Format).)

In addition not all font files use Unicode to translate characters to glyphs; Java uses Unicode code points to identify the glyphs.  For these reasons you may have installed some font and find that Java can't use it, even if other software on your system can.

The final issue is one of encoding text.  Text is composed of a series of numbers (the code points) that identify characters.  This text doesn't change if you change the font you use to render (i.e., draw) the glyphs for those characters.  The problem is the numbers can take up 4 or more bytes in Unicode.  So how should the text string ABC be stored?  It might be 4 bytes per number, or two, or even one, with special rules to handle large numbers.  This is called the text encoding.  (For more information about Unicode and encoding, see the resource Encoding and Character Sets.)

Internally Java uses two byte numbers, with a special convention to represent characters defined in Unicode with numbers larger than 65,536 (the biggest value you can store in two bytes).  This is why the Java data type char is a two byte value.  However most operating systems historically used one byte numbers.  This is because before Unicode, most Western fonts contained fewer than 256 glyphs, more than enough for the Latin alphabet commonly used in the US and the UK.  When reading or writing text a program must pick the proper encoding.

Unfortunately there is no easy way to tell what encoding to use, or what encoding is used.  If you use a web browser the web page (which is just text) contains a header stating what encoding is used.  Try changing that and see the results, especially for curly quotes and bullets.

There is a whole lot more to the story including ligatures, kerning, leading, and other fascinating (to me anyway) facts and history.  (Did you know that originally printers (human ones) traveled with cases containing little wooden (or soft metal) font blocks?  The capital letters were used much less often than the others and were stored in the top or upper part of the case while the rest were kept in the more convenient lower part of the case, and that's how we got the terms lowercase and uppercase letters.  Is that interesting or what?)

Using Fonts in Java

Actually using fonts in Java is easy:

  Font titleFont = new Font( name, style, size );

where name is a logical font name or the name of a physical (real) font installed on your system, stye is one of:

and size is the size specified in points, which is probably really pixels on most monitors.  Note the size is the average height of the alphabetic glyphs.

The styles are limiting, you can't specify a "demi" weight or "slanted" instead of italic.  However most physical font files are named for the actual style: Regular, DemiBold, or Bold.  So you can pick a physical font name incuding the whole style part of the name.  (Keep in mind that you can't use physical fonts with AWT.)