Summary
Drawing text on a component is just as easy as drawing lines and circles. The Graphics class provides the methods, and the Font and FontMetrics classes provide the support necessary to guarantee that the result is visually appealing. This column describes these three classes and demonstrates how they work together. (2,350 words)
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()
int getDescent()
int getLeading()
int getHeight()
int charWidth(int ch)
int charWidth(char ch)
int [] getWidths()
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()
int getMaxDescent()
int getMaxAdvance()
The following methods provide information about the width taken up by a sequence of characters.
int stringWidth(String str)
int bytesWidth(byte [] rgb, int offset, int length)
int charsWidth(char [] rgc, int offset, int length)
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()
String getFamily()
int getSize()
int getStyle()
boolean isBold()
boolean isItalic()
boolean isPlain()
String getName()
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.
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.
About the author
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.
Last updated: 15 December 1996