3.2 FontMetricsThe abstract FontMetrics class provides the tools for calculating the actual width and height of text when displayed on the screen. You can use the results to position objects around text or to provide special effects like shadows and underlining. Like the Graphics class, FontMetrics is abstract. The run-time Java platform provides a concrete implementation of FontMetrics. You don't have to worry about the actual class; it is guaranteed to implement all the methods of FontMetrics. In case you're curious, on a Windows 95 platform, either the class sun.awt.win32.Win32FontMetrics ( JDK1.0) or the class sun.awt.windows.WFontMetrics ( JDK1.1) extends FontMetrics. On a UNIX/Motif platform, the class is sun.awt.motif.X11FontMetrics. With the Macintosh, the class is sun.awt.macos.MacFontMetrics. If you're not using the JDK, the class names may be different, but the principle still applies: you don't have to worry about the concrete class. The FontMetrics ClassVariables
Four variables describe the height of a font: leading (pronounced like the metal), ascent, descent, and height. Leading is the amount of space required between lines of the same font. Ascent is the space above the baseline required by the tallest character in the font. Descent is the space required below the baseline by the lowest descender (the "tail" of a character like "y"). Height is the total of the three: ascent, baseline, and descent. Figure 3.1 shows these values graphically. If that were the entire story, it would be simple. Unfortunately, it isn't. Some special characters (for example, capitals with umlauts or accents) are taller than the "tallest" character in the font; so Java defines a value called maxAscent to account for these. Similarly, some characters descend below the "greatest" descent, so Java defines a maxDescent to handle these cases.
It seems that on Windows and Macintosh platforms there is no difference between the return values of getMaxAscent() and getAscent(), or between getMaxDescent() and getDescent(). On UNIX platforms, they sometimes differ. For developing truly portable applications, the max methods should be used where necessary.
In the horizontal dimension, positioning characters is relatively simple: you don't have to worry about ascenders and descenders, you only have to worry about how far ahead to draw the next character after you have drawn the current one. The "how far" is called the advance width of a character. For most cases, the advance width is the actual width plus the intercharacter space. However, it's not a good idea to think in these terms; in many cases, the intercharacter space is actually negative (i.e., the bounding boxes for two adjacent characters overlap). For example, consider an italic font. The top right corner of one character probably extends beyond the character's advance width, overlapping the next character's bounding box. (To see this, look back at Figure 3.1; in particular, look at the ll in O'Reilly.) If you think purely in terms of the advance width (the amount to move horizontally after drawing a character), you won't run into trouble. Obviously, the advance width depends on the character, unless you're using a fixed width font.
Example 3.1: Centering Text in a Frame
import java.awt.*; public class Center extends Frame { static String text[]; private Dimension dim; static public void main (String args[]) { if (args.length == 0) { System.err.println ("Usage: java Center <some text>"); return; } text = args; Center f = new Center(); f.show(); } public void addNotify() { super.addNotify(); int maxWidth = 0; FontMetrics fm = getToolkit().getFontMetrics(getFont()); for (int i=0;i<text.length;i++) { maxWidth = Math.max (maxWidth, fm.stringWidth(text[i])); } Insets inset = insets(); dim = new Dimension (maxWidth + inset.left + inset.right, text.length*fm.getHeight() + inset.top + inset.bottom); resize (dim); } public void paint (Graphics g) { g.translate(insets().left, insets().top); FontMetrics fm = g.getFontMetrics(); for (int i=0;i<text.length;i++) { int x,y; x = (size().width - fm.stringWidth(text[i]))/2; y = (i+1)*fm.getHeight()-1; g.drawString (text[i], x, y); } } } This application extends the Frame class. It stores its command-line arguments in the String array text[]. The addNotify() method sizes the frame appropriately. It computes the size needed to display the arguments and resizes the Frame accordingly. To compute the width, it takes the longest stringWidth() and adds the left and right insets. To compute the height, it takes the current font's height, multiplies it by the number of lines to display, and adds insets. Then it is up to the paint() method to use stringWidth() and getHeight() to figure out where to put each string.
Font Display ExampleExample 3.2 displays all the available fonts in the different styles at 12 points. The code uses the FontMetrics methods to ensure that there is enough space for each line. Figure 3.3 shows the results, using the Java 1.0 font names, on several platforms. Example 3.2: Font Display
import java.awt.*; public class Display extends Frame { static String[] fonts; private Dimension dim; Display () { super ("Font Display"); fonts = Toolkit.getDefaultToolkit().getFontList(); } public void addNotify() { Font f; super.addNotify(); int height = 0; int maxWidth = 0; final int vMargin = 5, hMargin = 5; for (int i=0;i<fonts.length;i++) { f = new Font (fonts[i], Font.PLAIN, 12); height += getHeight (f); f = new Font (fonts[i], Font.BOLD, 12); height += getHeight (f); f = new Font (fonts[i], Font.ITALIC, 12); height += getHeight (f); f = new Font (fonts[i], Font.BOLD | Font.ITALIC, 12); height += getHeight (f); maxWidth = Math.max (maxWidth, getWidth (f, fonts[i] + " BOLDITALIC")); } Insets inset = insets(); dim = new Dimension (maxWidth + inset.left + inset.right + hMargin, height + inset.top + inset.bottom + vMargin); resize (dim); } static public void main (String args[]) { Display f = new Display(); f.show(); } private int getHeight (Font f) { FontMetrics fm = Toolkit.getDefaultToolkit().getFontMetrics(f); return fm.getHeight(); } private int getWidth (Font f, String s) { FontMetrics fm = Toolkit.getDefaultToolkit().getFontMetrics(f); return fm.stringWidth(s); } public void paint (Graphics g) { int x = 0; int y = 0; g.translate(insets().left, insets().top); for (int i=0;i<fonts.length;i++) { Font plain = new Font (fonts[i], Font.PLAIN, 12); Font bold = new Font (fonts[i], Font.BOLD, 12); Font italic = new Font (fonts[i], Font.ITALIC, 12); Font bolditalic = new Font (fonts[i], Font.BOLD | Font.ITALIC, 12); g.setFont (plain); y += getHeight (plain); g.drawString (fonts[i] + " PLAIN", x, y); g.setFont (bold); y += getHeight (bold); g.drawString (fonts[i] + " BOLD", x, y); g.setFont (italic); y += getHeight (italic); g.drawString (fonts[i] + " ITALIC", x, y); g.setFont (bolditalic); y += getHeight (bolditalic); g.drawString (fonts[i] + " BOLDITALIC", x, y); } resize (dim); } } |
|