Friday, 24 June 2011

Swing : Using Special Fonts for Text

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.”

Finding the list of all Available Fonts:

To find out the fonts that 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.

Try executing the below sample program:

import java.awt.*;

public class ListAllFontsInMyPC
{
public static void main(String[] args)
{
String[] fontNames = GraphicsEnvironment
  .getLocalGraphicsEnvironment()
  .getAvailableFontFamilyNames();
  for (String fontName : fontNames)
    System.out.println(fontName);
  }
}

On my PC, I get a list of fonts that starts like below:

Agency FB
Aharoni
Akzidenz Grotesk CE Light
Akzidenz Grotesk CE Roman
Akzidenz Grotesk Light
Akzidenz Grotesk Roman
AkzidenzGrotesk
AkzidenzGroteskCE
Algerian
Andalus
Angsana New
AngsanaUPC

Note: There are over 200 fonts in my PC and the whole list will be too big to print out here. So, I have just put in the first 12 fonts. You can run this in your PC to see what all fonts are installed/available in your system.

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.

In addition, the Sun JDK always includes three font families named “Lucida Sans,” “Lucida Bright,” and “Lucida Sans Typewriter.”

Creating a Font Object:

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.

Ex:
Font sansbold14 = new Font("SansSerif", 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. The bigger the number here, the larger the font size would be…

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

You can read font files in TrueType or PostScript Type 1 formats. You need an input stream for the font—typically from a file or URL. Then call the static Font.createFont method like below:

URL url = new URL("http://www.fonts.com/Wingbats.ttf");
InputStream in = url.openStream();
Font f1 = Font.createFont(Font.TRUETYPE_FONT, in);

The font is plain with a font size of 1 point. Use the deriveFont method to get a font of the desired size:

Font f = f1.deriveFont(14.0F);

Trivia:
There are two overloaded versions of the deriveFont method. One of them (with a float parameter) sets the font size, the other (with an int parameter) sets the font style. Thus, f1.deriveFont(14) sets the style and not the size! (The result is an italic font because it happens that the binary representation of 14 sets the ITALIC bit but not the BOLD bit.)

Using a Font:

Once you create a font object, you can use it to affect a string that will be displayed on screen.
For ex:

Font sansbold14 = new Font("SansSerif", Font.BOLD, 14);
g2.setFont(sansbold14);
String message = "Hey Buddy!!! !";
g2.drawString(message, 75, 100);

Next, let’s center the string in its component 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:

  • The font used (in our case, sans serif, bold, 14 point);
  • The string (in our case, “Hey Buddy!!!”); and
  • The device on which the font is drawn (in our case, the user’s screen).

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. Look at the image below:

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.”

Leading is the space between the descent of one line and the ascent of the next line. 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.

So, 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 example uses all this information to center a string in its surrounding component:

FontRenderContext context = g2.getFontRenderContext();
Rectangle2D bounds = f.getStringBounds(message, context);

// (x,y) for getting 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 component. 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.

A Fully Functional Code Example:

Let us now take a look at a fully functional code example that will print out some text and then center it.

import java.awt.*;
import java.awt.font.*;
import java.awt.geom.*;
import javax.swing.*;

public class TestFonts
{
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
MyFrame frame = new MyFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}

/**
 * A frame with a text message component
 */
class MyFrame extends JFrame
{
public MyFrame()
{
setTitle("Test Fonts");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);

// add component to frame
MyFontComponent component = new MyFontComponent();
add(component);
}

public static final int DEFAULT_WIDTH = 300;
public static final int DEFAULT_HEIGHT = 200;
}

/**
 * A component that shows a centered message in a box.
 */
class MyFontComponent extends JComponent
{
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;

String message = "Hey Buddy!!!";

Font f = new Font("Serif", Font.BOLD, 36);
g2.setFont(f);

// measure the size of the message
FontRenderContext context = g2.getFontRenderContext();
Rectangle2D bounds = f.getStringBounds(message, context);

// set (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;

// draw the message
g2.drawString(message, (int) x, (int) baseY);
g2.setPaint(Color.LIGHT_GRAY);

// draw the baseline
g2.draw(new Line2D.Double(x, baseY, x + bounds.getWidth(), baseY));

// draw the enclosing rectangle
Rectangle2D rect = new Rectangle2D.Double(x, y, bounds.getWidth(), bounds.getHeight());
g2.draw(rect);
}
}


No comments:

Post a Comment