The "Not a Hello, World" program at the beginning of this chapter displayed a string in the default font. Often, you want to show text in a different font. You specify a font by its font face name. A font face name is composed of a font family name, such as "Helvetica," and an optional suffix such as "Bold." For example, the font faces "Helvetica" and "Helvetica Bold" are both considered to be part of the family named "Helvetica." To find out which fonts are available on a particular computer, call the getAvailableFontFamilyNames method of the GraphicsEnvironment class. The method returns an array of strings that contains the names of all available fonts. To obtain an instance of the GraphicsEnvironment class that describes the graphics environment of the user's system, use the static getLocalGraphicsEnvironment method. Thus, the following program prints the names of all fonts on your system: import java.awt.*; public class ListFonts { public static void main(String[] args) { String[] fontNames = GraphicsEnvironment .getLocalGraphicsEnvironment() .getAvailableFontFamilyNames(); for (String fontName : fontNames) System.out.println(fontName); } } On one system, the list starts out like this: Abadi MT Condensed Light Arial Arial Black Arial Narrow Arioso Baskerville Binner Gothic . . . and goes on for another 70 fonts or so. NOTE
Unfortunately, there is no absolute way of knowing whether a user has a font with a particular "look" installed. Font face names can be trademarked, and font designs can be copyrighted in some jurisdictions. Thus, the distribution of fonts often involves royalty payments to a font foundry. Of course, just as there are inexpensive imitations of famous perfumes, there are lookalikes for name-brand fonts. For example, the Helvetica imitation that is shipped with Windows is called Arial. To establish a common baseline, the AWT defines five logical font names: SansSerif Serif Monospaced Dialog DialogInput These names are always mapped to fonts that actually exist on the client machine. For example, on a Windows system, SansSerif is mapped to Arial. NOTE
To draw characters in a font, you must first create an object of the class Font. You specify the font face name, the font style, and the point size. Here is an example of how you construct a Font object: Font helvb14 = new Font("Helvetica", Font.BOLD, 14); The third argument is the point size. Points are commonly used in typography to indicate the size of a font. There are 72 points per inch. This sentence uses a 9-point font. You can use a logical font name in the place of a font face name in the Font constructor. You specify the style (plain, bold, italic, or bold italic) by setting the second Font constructor argument to one of the following values: Font.PLAIN Font.BOLD Font.ITALIC Font.BOLD + Font.ITALIC Here is an example: Font sansbold14 = new Font("SansSerif", Font.BOLD, 14) NOTE
TIP
CAUTION
The Java fonts contain the usual ASCII characters as well as symbols. For example, if you print the character '\u2297' in the Dialog font, then you get a character. Only those symbols that are defined in the Unicode character set are available. Here's the code that displays the string "Hello, World!" in the standard sans serif font on your system, using 14-point bold type: Font sansbold14 = new Font("SansSerif", Font.BOLD, 14); g2.setFont(sansbold14); String message = "Hello, World!"; g2.drawString(message, 75, 100); Next, let's center the string in its panel rather than drawing it at an arbitrary position. We need to know the width and height of the string in pixels. These dimensions depend on three factors:
To obtain an object that represents the font characteristics of the screen device, you call the getFontRenderContext method of the Graphics2D class. It returns an object of the FontRenderContext class. You simply pass that object to the getStringBounds method of the Font class: FontRenderContext context = g2.getFontRenderContext(); Rectangle2D bounds = f.getStringBounds(message, context); The getStringBounds method returns a rectangle that encloses the string. To interpret the dimensions of that rectangle, you should know some basic typesetting terms (see Figure 7-13). The baseline is the imaginary line where, for example, the bottom of a character like "e" rests. The ascent is the distance from the baseline to the top of an ascender, which is the upper part of a letter like "b" or "k," or an uppercase character. The descent is the distance from the baseline to a descender, which is the lower portion of a letter like "p" or "g." Figure 7-13. Typesetting terms illustratedLeading is the space between the descent of one line and the ascent of the next line. (The term has its origin from the strips of lead that typesetters used to separate lines.) The height of a font is the distance between successive baselines, which is the same as descent + leading + ascent. The width of the rectangle that the getStringBounds method returns is the horizontal extent of the string. The height of the rectangle is the sum of ascent, descent, and leading. The rectangle has its origin at the baseline of the string. The top y-coordinate of the rectangle is negative. Thus, you can obtain string width, height, and ascent as follows: double stringWidth = bounds.getWidth(); double stringHeight = bounds.getHeight(); double ascent = -bounds.getY(); If you need to know the descent or leading, you need to use the getLineMetrics method of the Font class. That method returns an object of the LineMetrics class, which has methods to obtain the descent and leading: LineMetrics metrics = f.getLineMetrics(message, context); float descent = metrics.getDescent(); float leading = metrics.getLeading(); The following code uses all this information to center a string in its surrounding panel: FontRenderContext context = g2.getFontRenderContext(); Rectangle2D bounds = f.getStringBounds(message, context); // (x,y) = top left corner of text double x = (getWidth() - bounds.getWidth()) / 2; double y = (getHeight() - bounds.getHeight()) / 2; // add ascent to y to reach the baseline double ascent = -bounds.getY(); double baseY = y + ascent; g2.drawString(message, (int) x, (int) baseY); To understand the centering, consider that getWidth() returns the width of the panel. A portion of that width, namely, bounds.getWidth(), is occupied by the message string. The remainder should be equally distributed on both sides. Therefore, the blank space on each side is half the difference. The same reasoning applies to the height. Finally, the program draws the baseline and the bounding rectangle. Figure 7-14 shows the screen display; Example 7-6 is the program listing. Example 7-6. FontTest.java1. import java.awt.*; 2. import java.awt.font.*; 3. import java.awt.geom.*; 4. import javax.swing.*; 5. 6. public class FontTest 7. { 8. public static void main(String[] args) 9. { 10. FontFrame frame = new FontFrame(); 11. frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 12. frame.setVisible(true); 13. } 14. } 15. 16. /** 17. A frame with a text message panel 18. */ 19. class FontFrame extends JFrame 20. { 21. public FontFrame() 22. { 23. setTitle("FontTest"); 24. setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); 25. 26. // add panel to frame 27. 28. FontPanel panel = new FontPanel(); 29. add(panel); 30. } 31. 32. public static final int DEFAULT_WIDTH = 300; 33. public static final int DEFAULT_HEIGHT = 200; 34. } 35. 36. /** 37. A panel that shows a centered message in a box. 38. */ 39. class FontPanel extends JPanel 40. { 41. public void paintComponent(Graphics g) 42. { 43. super.paintComponent(g); 44. Graphics2D g2 = (Graphics2D) g; 45. 46. String message = "Hello, World!"; 47. 48. Font f = new Font("Serif", Font.BOLD, 36); 49. g2.setFont(f); 50. 51. // measure the size of the message 52. 53. FontRenderContext context = g2.getFontRenderContext(); 54. Rectangle2D bounds = f.getStringBounds(message, context); 55. 56. // set (x,y) = top left corner of text 57. 58. double x = (getWidth() - bounds.getWidth()) / 2; 59. double y = (getHeight() - bounds.getHeight()) / 2; 60. 61. // add ascent to y to reach the baseline 62. 63. double ascent = -bounds.getY(); 64. double baseY = y + ascent; 65. 66. // draw the message 67. 68. g2.drawString(message, (int) x, (int) baseY); 69. 70. g2.setPaint(Color.GRAY); 71. 72. // draw the baseline 73. 74. g2.draw(new Line2D.Double(x, baseY, x + bounds.getWidth(), baseY)); 75. 76. // draw the enclosing rectangle 77. 78. Rectangle2D rect = new Rectangle2D.Double(x, y, bounds.getWidth(), bounds .getHeight()); 79. g2.draw(rect); 80. } 81. } Figure 7-14. Drawing the baseline and string bounds java.awt.Font 1.0
java.awt.font.LineMetrics 1.2
java.awt.Graphics 1.0
java.awt.Graphics2D 1.2
|