Drawing text is easy with three Java classes

Find out how to create text that's visually appealing through this explanation of what classes to use and how they work together

In addition to methods for drawing primitive geometric types like lines and circles, the Graphics class provides methods for drawing text. When combined with the Font and FontMetrics classes, the result is a set of tools that makes the job of drawing appealing text much easier than it otherwise might be. This column will cover each of these classes in turn and will show you how to use them together. Before I begin, however, a short review of the role of the Graphics class is in order.

A review

In order to use the text methods of the Graphics class, an understanding of the role of the Graphics class itself is required. This section presents a brief overview of the function and operation of the Graphics class. Readers looking for thorough coverage should read my October column, available here.

The Graphics class plays two different but related roles within the abstract windowing toolkit (AWT). First, it maintains the graphics context, which consists of all of the information that will affect the outcome of a graphics operation. This includes the drawing color, the font, and the location and dimensions of the clipping rectangle (the region in which graphics can be drawn). More importantly, the graphics context defines the destination for the graphics operations about to be discussed (destinations include components and images).

In addition to its role as the graphics context, the Graphics class provides methods for drawing simple geometric shapes, text, and images to the graphics destination. All of the graphics-related operations on a component or image occur via one of these methods.

In order to draw, a program requires a valid graphics context (represented by an instance of the Graphics class). Because the Graphics class is an abstract base class, it cannot be instantiated directly. An instance typically is created by a component, and then handed to the program as an argument to a component's update() and paint() methods. These two methods are called as part of the normal drawing cycle initiated within the AWT.

The Graphics class works together with the Font and FontMetrics classes to provide the tools necessary to draw text within an image or component. Let's begin by examining the Graphics class's methods for drawing text.

Class Graphics

The Graphics class provides three methods that draw text on a component or an image.

void drawString(String str, int x, int y)

The drawString() method, shown below, takes as parameters an instance of the String class containing the text to be drawn, and two integer values specifying the coordinates where the text should start.

public void paint(Graphics g)
{
   g.drawString("abc", 25, 25);
}

The code in the listing above shows the drawString() method in use within a component's paint() method. The code in this example draws the word "abc" on the component containing this paint() method. The x and y coordinates specify the location of the lower-left corner of the enclosing text box. Figure 1 shows what the result would look like if this code were part of a suitable AWT component object.

Figure 1: A drawString() demonstration

void drawChars(char [] data, int offset, int length, int x, int y)

The drawChars() method below takes as parameters a character array containing the text to be drawn, an integer value indicating the offset into the array at which to begin, an integer value indicating the number of characters to draw, and two integer values specifying the coordinates where the text should start.

public void paint(Graphics g) { char [] rgc = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j' };

g.drawChars(rgc, 0, 5, 25, 25); g.drawChars(rgc, 5, 5, 25, 50); }

The code above shows the drawChars() method in use within a component's paint() method. The character array is drawn in two parts. In the first of the two calls to drawChars(), the offset parameter indicates that the drawing should begin with the first character in the array, and the length parameter indicates that a total of five characters should be drawn on the first line. The second of the two calls works in a similar fashion but draws the last five characters in the character array beginning at a position 25 pixels below the first. Figure 2 shows what the result would look like if this code were part of a suitable AWT component object.

Figure 2: A drawChars() demonstration

void drawBytes(byte [] data, int offset, int length, int x, int y)

As shown below, the drawBytes() method takes as parameters a byte array containing the text to be drawn, an integer value indicating the offset into the array at which to begin, an integer value indicating the number of bytes to draw, and two integer values specifying the coordinates where the text should start.

public void paint(Graphics g) { byte [] rgb = { 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't' };

g.drawBytes(rgb, 0, 5, 25, 25); g.drawBytes(rgb, 5, 5, 25, 50); }

The above code shows the drawBytes() method in use within a component's paint() method. Figure 3 shows what the result would look like if this code where part of a suitable AWT component object.

Figure 3: A drawBytes() demonstration

Unicode support

One of Java's most touted features is its support for international scripts via Unicode. It is unfortunate that the Java class library supplied with version 1.0 of the Java programming language did not fully support this facet of the language. However, it seems that good news is just around the corner. The preliminary Internationalization API (see Resources), available from SunSoft, has this to say:

JDK 1.0 was limited to displaying only the characters in the Latin-1 subset of Unicode. This restriction is removed in JDK 1.1. Java programs will now be able to display any Unicode character which can be rendered with a host font. Java provides a small number of predefined "virtual" font names and maps them to real fonts available on the host. In JDK 1.0, each Java font name mapped to exactly one host font. In JDK 1.1, a Java font name can map to a series of host fonts. The series of host fonts can be chosen to cover as much of the Unicode character set as is desired.

Text placement

Because text is just another kind of figure to the AWT, a line of text can be placed anywhere -- even on top of another line of text. The effect of haphazard placement, however, will not necessarily be pleasing to the eye. In order to assist the programmer in producing aesthetically pleasing text, a font definition includes guidelines for line and character placement. These guidelines, if followed, will help produce pleasing output.

Figure 4 contains a line of text that has been marked up to indicate the characteristics that we are about to discuss.

Figure 4: A line of text

The y coordinate parameter in the methods in the previous section specifies the location of the baseline of a line of text. The baseline is the line upon which most of the characters in a line of text rest (the exception being those characters with descenders such as "g" and "y"). The baseline isn't really a characteristic of a font but is the reference point to which all of the other characteristics refer.

The ascent is the distance from the baseline to the top of most of the characters in a font. This typically is the height of the capital letters in the font and of characters like "f" and "h". This figure is only a guideline, however. Some characters in the font may actually extend above this distance.

The descent is the distance from the baseline to the bottom of the characters in a font that have descenders -- characters like "p", "g", and "y". As with ascent, this figure is only a guideline. Some characters in the font may actually extend below this distance.

The leading (pronounced "ledding") is the amount of space between the descent of one line of text and the ascent of the line below it. The height of a line of text (the distance from the baseline of one line of text to the baseline of a line of text above or below it) includes this extra space.

In addition to the characteristics governing a font as a whole, each character in a font has an advance. The advance specifies how many pixels separate the beginning of the character from the beginning of a character to its right; in short, it is a character's width. Once again, some characters in a font may actually extend beyond this distance.

By adding up the widths of all of the characters in a line of text, the length of the entire line of text can be calculated. The FontMetrics class below provides a method that does just this, and more.

Class FontMetrics

The FontMetrics class provides a simple way to get at the characteristics discussed above. Here is the getFontMetrics method in action:

public void paint(Graphics g)
{
   FontMetrics fm = g.getFontMetrics();
      .
      .
      .
}

The code above demonstrates how font metrics information describing the current font can be obtained. The getFontMetrics() method returns an instance of the FontMetrics class. The FontMetrics class provides the following methods:

int getAscent()

  • Returns the ascent of the font.

int getDescent()

  • Returns the descent of the font.

int getLeading()

  • Returns the leading of the font.

int getHeight()

  • Returns the height of the font. The height is the sum of the font's ascent, descent, and leading.

int charWidth(int ch)

  • Returns the width of the specified character.

int charWidth(char ch)

  • Returns the width of the specified character.

int [] getWidths()

  • Returns an integer array containing the widths of the font's first 256 characters.

As mentioned above, the characters that make up a font may sometimes extend beyond the ascent, descent, and widths reported by the methods above. In cases where exact values are required, the following methods are provided.

int getMaxAscent()

  • Returns the maximum ascent of the font.

int getMaxDescent()

  • Returns the maximum descent of the font.

int getMaxAdvance()

  • Returns the width of the widest character in the font.

The following methods provide information about the width taken up by a sequence of characters.

int stringWidth(String str)

  • Returns the width of the sequence of characters.

int bytesWidth(byte [] rgb, int offset, int length)

  • Returns the width of the length long sequence of bytes beginning at offset.

int charsWidth(char [] rgc, int offset, int length)

  • Returns the width of the length long sequence of characters beginning at offset.

Class Font

The Font class encapsulates information about a font. A new font is produced by creating an instance of the Font class with a name, style, and point size.

Font f = new Font("Dialog", Font.PLAIN, 12);

Once created, a font can be assigned to an instance of the Graphics object.

g.setFont(f);

The Graphics object will then use the font for all subsequent text related graphics operations.

The Font class provides methods for getting at information about a font once it has been created.

String getName()

  • Returns the name of the font.

String getFamily()

  • Returns the platform specific name of the font.

int getSize()

  • Returns the point size of the font.

int getStyle()

  • Returns the style of the font.

boolean isBold()

  • Returns true if the font is bold.

boolean isItalic()

  • Returns true if the font is italic.

boolean isPlain()

  • Returns true if the font is plain.

String getName()

  • Returns the name of the font.

A demonstration

The applet in Figure 5 displays a line of text with markup sufficient to indicate the values of the associated metrics from the section above. A thick black line sits at the baseline. Two additional lines indicate the ascent and descent of the font in question. Smaller vertical lines indicate the widths of the characters. The three pull-down menus allow you to select a font, its style, and its point size.

You need a Java-enabled browser to view this applet.
Figure 5: An interactive font metric browser

The applet uses the Graphics, Font, and FontMetrics classes extensively. Its source is available here.

Conclusion

It seems the Graphics class has turned out to be very fertile ground for exploration. And the expedition is not yet finished. Next month I will end my excursion into the Graphics class with a column on its image support methods, and that column will begin a small series on other topics related to images and the AWT, including image producers and image consumers.

I'd like to thank all of you who have taken time to write me with your comments, ideas, and suggestions. Keep up the good work.

Todd Sundsted has been writing programs since computers became available in desktop models. Though originally interested in building distributed object applications in C++, Todd moved to the Java programming language when Java became the obvious choice for that sort of thing. Todd is coauthor of the Java Language API SuperBible, now in bookstores everywhere. In addition to writing, Todd provides Internet and Web consulting services to companies in the southeastern United States.

Learn more about this topic