In this step you'll add font support to the Marquee applet. First, add the fields in blue:
public class Marquee extends Applet implements Runnable { // // Fields // Thread thread = new Thread(this); private Dimension size; private Color backColor, textColor, linkColor, activeColor; private Font textFont, linkFont; private FontMetrics textFontMetrics, linkFontMetrics; private String linkBase;
We declare two Font
objects and two FontMetrics
objects. A Font
object represents a font, and FontMetrics
holds information about a font such as size information. Here, we have Font
and FontMetrics
objects for normal text and link text.
These fields are initialized when the applet starts:
public void init() { // Save the applet size size = size(); // Get applet parameter settings backColor = initColor("bgcolor", Color.black); setBackground(backColor); textColor = initColor("text", Color.lightGray); linkColor = initColor("link", Color.yellow); activeColor = initColor("alink", Color.red); textFont = initFont("textfont", getFont()); textFontMetrics = getFontMetrics(textFont); linkFont = initFont("linkfont", textFont); linkFontMetrics = getFontMetrics(linkFont); initMessageSettings(); thread.start(); }
The new initFont()
method (discussed below) has two arguments: the parameter name and the default font to use. This method will read the parameter information and return the requested font. We use the applet's default font (returned by calling getFont()
) as the default for textFont
, and textFont
as the default for linkFont
. We also call getFontMetrics()
to get the font information for both fonts.
Add the new initFont()
method right after initColor()
:
// Initialize the font for drawing private Font initFont(String param, Font defFont) { String s = getParameter(param); // If no font is specified, use default if(s == null) return defFont; // Get delimiter positions int delim1 = s.indexOf(','), delim2 = s.indexOf(',', delim1 + 1); if(delim1 == -1 || delim2 == -1) { errorMsg = "Invalid " + param + ": face,style,size (style=B,I,BI)"; return defFont; } // Parse values String fontFace = defFont.getName(), fontStyles; int fontStyle = Font.PLAIN, fontSize = defFont.getSize(); if(delim1 > 0) // Get the font typeface fontFace = s.substring(0, delim1); if(delim2 > delim1 + 1) { // Get the font style fontStyles = s.substring(delim1 + 1, delim2); if(fontStyles.indexOf('B') != -1) fontStyle |= Font.BOLD; if(fontStyles.indexOf('I') != -1) fontStyle |= Font.ITALIC; } if(delim2 < s.length() - 1) { try { // Get the font size fontSize = Integer.parseInt(s.substring(delim2 + 1)); } catch(NumberFormatException e) { errorMsg = "Invalid " + param + " size"; return defFont; } } // Create the font return new Font(fontFace, fontStyle, fontSize); }
Before I go into the details of this complex method, let me explain the format of font parameters that Marquee expects. Basically, the format is this:
typeface,BI,size
The two commas are required, but all the other parts are optional. The typeface
part (everything before the first comma) names the font to use. The Web page author can also specify B
for bold and/or I
for italic in the middle partboth letters are optional, and can be specified in either order. If neither is given, the font will be plain. Finally, the size
part (everything after the second comma) is an integer that specifies the font size.
For example, the value "TimesRoman,B,"
means to use Times Roman, bold, in the default size. The typeface can be any of the following (depending on the user's system): Dialog
, Helvetica
, TimesRoman
, Courier
, or Symbol
.
I'll also quickly explain some terms for fonts. The baseline is the imaginary line that text rests on. Descent is how far below the baseline a particular character extends, and ascent is how far above. Leading is the amount of space between the descent of one line and the ascent of the next line. A font's standard height is the distance between the baselines of two adjacent lines of text, and is the sum of the ascent, descent, and leading. You can use the getAscent()
, getDescent()
, getHeight()
, and getLeading()
methods of a FontMetrics
object to get information about a font.
Another handy method of FontMetrics
that we'll be using is stringWidth()
, which returns the width of a string.
Now let's dissect initFont()
, line by line.
initFont
in DetailWe first call getParameter()
to get the parameter value, storing it in s
, and then return the default font if no value is specified:
String s = getParameter(param); // If no font is specified, use default if(s == null) return defFont;
Then we call the indexOf()
method of s
(class String
) to get the character positions of the two commas, delim1
and delim2
:
int delim1 = s.indexOf(','), delim2 = s.indexOf(',', delim1 + 1);
This method searches a string for the position of the first instance of a character. Note that we specify a single character (not a string) by enclosing it in single quotation marks ('
). Also note that there are two forms of indexOf()
; we can optionally specify a position to start searching from. For delim2
, we search the string after the first comma. Indices start at 0, so the first character is at position 0.
If the search fails, indexOf()
returns 1. If either delim1
or delim2
is 1, we set the error message (describing the correct syntax) and return the default font:
if(delim1 == -1 || delim2 == -1) { errorMsg = "Invalid " + param + ": face,style,size (style=B,I,BI)"; return defFont; }
Remember that the logical OR operator (||
) return true
if either of its operands are true
. Also note the use of the concatenation operator (+
) operator to join two or more strings.
If two commas have been found, we parse the three values. We declare three variables, fontFace
, fontStyle
, and fontSize
, to hold information about the default font:
String fontFace = defFont.getName(), fontStyles; int fontStyle = Font.PLAIN, fontSize = defFont.getSize();
As shown, we call getName()
and getSize()
to return the name and size of the font, and use the plain style constant Font.PLAIN
. (As you can probably guess, use getStyle()
to return the font's style).
We first check if delim1
is greater than 0. If so, meaning that a typeface has been specified, we get the value by calling the substring()
method. This method returns the characters starting at the first index up to, but not including, the character at the last index:
if(delim1 > 0) // Get the font typeface fontFace = s.substring(0, delim1);
Next, we check if a font style has been specified by seeing if delim2
is greater than (delim1 + 1
). If so, we extract the style using substring()
and then check to see if B
or I
is present. If they are, then we use the OR assignment operator (|=
) to combine the corresponding font style constants (Font.BOLD
and Font.ITALIC
):
if(fontStyles.indexOf('B') != -1) fontStyle |= Font.BOLD; if(fontStyles.indexOf('I') != -1) fontStyle |= Font.ITALIC;
Then, we check if a font size has been specified; if delim2
is less than the last character position (one less than the length of the string, returned by length()
), we parse the integer, handling NumberFormatException
appropriately:
fontSize = Integer.parseInt(s.substring(delim2 + 1));
Finally, if no errors occurred, we create a new Font
object with all the styles and return
it:
return new Font(fontFace, fontStyle, fontSize);
In the next step, we'll use the information we gathered here to display the error message.