Teach Yourself Visual C++® 5 in 24 Hours

Previous chapterNext chapterContents


- Hour 13 -
Fonts

Fonts define the symbols and characters used to display text in a Windows program. In this hour, you will learn

At the end of the hour is some sample code that extends the DCTest example to show how fonts are used in a Windows program.

What Are Fonts?

Fonts are GDI objects, much like the pens and brushes discussed in Hour 12, "Using Pens and Brushes," and are used to define the characters used for output in a Windows program. A collection of characters and other symbols that share the same attributes is a font.


Just a Minute: Strictly speaking, fonts are not necessary for most programs written for Windows. A default font is selected into every device context automatically, and it can work just fine for most applications. However, almost every program can benefit from using fonts that have been selected to suit its specific needs.

In this hour you see some terms that are unique to programming with fonts.

New Term: A glyph is an individual character.

New Term: Font pitch refers to the width of individual characters; fixed pitch means that each character has the same width; variable pitch means that some characters will be wider than others.

New Term: A serif is the small cross at the ends of some characters. A font with a serif has short crosses at the ends of lines making up the font; Times New Roman is such a serif font. A font without serifs is often called a sans-serif font. Figure 13.1 shows examples of a serif and a sans-serif font.

Figure 13.1.
Serif and sans-serif fonts.

Fonts are maintained by Windows. Information about each currently installed font is stored in a system table known as the font table.

There are three different types of fonts; each type has different capabilities:


Just a Minute: Scaleable fonts that can display italic, bold, or underlined text give a program an extra amount of usability. Most printers supported by Windows also allow TrueType fonts to be displayed on a printer exactly as they are on a video screen; this is an extra advantage because it greatly simplifies the work required for printing.

Fonts are also arranged into six families that define the general attributes of the font. Fonts in the same family share similar strokes, serifs, and pitch. The following are the six font families:

Specifying Font Attributes

Like other GDI objects, the easiest way to use a font is to use the MFC class library. Like other GDI objects, fonts must be used with a device context, and they are influenced by the current state of the device context, such as mapping mode and color definitions. When you're working with text output, the CFont class helps make using a font easy.

There are two basic ways to use a font in your program:

In addition to the font families discussed earlier in this hour, you can use other general attributes to specify a font. Many font attributes exist, mainly because there are so many different ways to display characters in a program written for Windows. Don't worry; after you've used fonts a few times, you'll be able to create fonts with no trouble at all. Later in the hour you will build some examples to learn how you can use these attributes.

The Font Height and Width

You can specify the height of the font using one of the following methods:

Logical units normally are used for screen display, and physical units are normally used for printing. In Hour 21, "Printing," you use MM_TWIPS to create fonts based on device units.


Just a Minute: The width of a font normally is set to zero, which tells Windows to select an appropriate default width. However, in some cases you might want to specify your own font width to display compressed or elongated text.

The Font Character Set

Every font is made up of a large number of characters and other symbols that can be displayed. The actual symbols that are contained in a font depend on the character set supported by that font. These three character sets are available:

Attributes that Affect Font Output

Three parameters specify output attributes of the selected font: output precision, clipping precision, and output quality.

Output precision is used to specify how closely the font returned by Windows must match the requested font. A range of options is available, from allowing Windows to select a reasonable match to requiring an exact match.

Clipping precision is used to specify how characters are treated when they lie on a clipping boundary. There are three options:

The output quality of the font refers to the degree to which GDI routines must match logical font attributes to the physical representation of the font. Here, again, there are three options:

Font Pitch and Family Attributes

All fonts have a certain pitch. When requesting a font from Windows, you have three different choices for the pitch:

As was discussed earlier, the font family describes general characteristics for a type of font and can be used when a specific font might not be available on all machines. Here are the values for font families:

The pitch attribute can be combined with a font family attribute using the bitwise OR operator, like this:

lfHeading.lfPitchAndFamily = DEFAULT_PITCH | FF_SWISS;


CAUTION: Combining the pitch and family attributes isn't necessary; often, the family name implies a pitch. In the preceding example, it's possible to specify just FF_SWISS.

FF_ROMAN and FF_SWISS always imply a variable pitch. FF_MODERN always implies a fixed pitch. Other family types contain fonts that have both fixed and variable pitch.


Font Weights

You can specify the relative weight of a font, based on a scale from 0 to 1,000. A weight of 400 describes a normal font, whereas 700 is used for a bold font. If you use 0, Windows uses a reasonable default weight for the font. Each of the weight options between 0 and 900 has a symbolic name, as shown in Table 13.1.

Table 13.1. Symbolic names for font weights.

Symbol Weight
FW_DONTCARE 0
FW_THIN 100
FW_EXTRALIGHT 200
FW_ULTRALIGHT 200
FW_LIGHT 300
FW_NORMAL 400
FW_REGULAR 400
FW_MEDIUM 500
FW_SEMIBOLD 600
FW_DEMIBOLD 600
FW_BOLD 700
FW_EXTRABOLD 800
FW_ULTRABOLD 800
FW_BLACK 900
FW_HEAVY 900

Although not every weight is available for every font, Windows tries to select a font weight close to the requested value.

Other Font Attributes

It's possible to define the escapement and orientation of a font. The escapement is the angle, in tenths of a degree, formed by a line of text in relation to the bottom of the page. Each degree in escapement adds 10 to the parameter value. For example, an escapement parameter value of 900 (90deg. x 10) describes a font where each line of text is rotated 90 degrees counterclockwise. The orientation of a font is similar to the escapement, but applies to each character rather than to an entire line of text.

Italic, underline, and strikethrough effects are assigned by specifying TRUE or FALSE for each of these attributes.

Finally, you can specify the typeface name. This is the name of a font that should be a good match for the parameters specified in other parts of the font description. If this parameter is set to NULL, Windows uses the other parameters when searching for a font. If you specify a name, that name is used to search for a font. If a font with that name is found, it is used.

Creating Fonts for Windows Programs

There are two ways to create fonts using MFC. If you are creating a small number of fonts, you can use the CFont class and its CreateFont member function. If you're creating several similar fonts or a large number of fonts, you can use the LOGFONT structure.

Creating a Font Using CFont


Time Saver: The first time you consider creating a CFont object, you might be intimidated by the large number of parameters it takes. Don't worry; most of the parameters can actually be set to default values or zero, and the Windows font mapper selects a font for you.

To illustrate this, Listing 13.1 creates two fonts. One font, fntArial, uses zero for all the parameters and specifies a font name. The other font, fntBoldSwiss, specifies many of the characteristics of a desired font. In both cases the font mapper determines a reasonable font. Add the source code from Listing 13.1 to the CDCTestView::OnDraw function in the DCTest project that was originally created in Hour 11, "Device Contexts."

TYPE: Listing 13.1. Two different ways to create a CFont object.

void CDCTestView::OnDraw(CDC* pDC)

{

    pDC->SetMapMode( m_nMapMode );

    CRect rcClient;

    GetClientRect( rcClient );

    pDC->DPtoLP( rcClient );

    COLORREF clrOld = pDC->SetTextColor( m_clrChoice );

    int nOldMode = pDC->SetBkMode( TRANSPARENT );

    CFont   fntArial, fntBoldSwiss;

    fntArial.CreateFont( 0, 0, 0, 0, 0, 0, 0, 0,

                         0, 0, 0, 0, 0, "Arial" );

    fntBoldSwiss.CreateFont( rcClient.Height()/20, 0, 0, 0,

                        FW_BOLD, TRUE, FALSE, 0, ANSI_CHARSET,

                        OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS,

                        DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS,

                        NULL );

    CString szMsg = "Hello! Change the color and mapping mode";

    CFont* pOldFont = pDC->SelectObject( &fntArial );

    int cy = rcClient.Height()/4;

    pDC->TextOut( 0, cy, szMsg );



    pDC->SelectObject( &fntBoldSwiss );

    TEXTMETRIC tm;

    pDC->GetTextMetrics(&tm);

    cy += tm.tmHeight + tm.tmExternalLeading;

    pDC->TextOut( 0, cy , szMsg );



    // Restore the old GDI objects

    pDC->SelectObject( pOldFont );

    pDC->SetTextColor( clrOld );

    pDC->SetBkMode( nOldMode );

}


CAUTION: As with all GDI objects, you must save the original font that is returned when a new font is selected into a device context. If you fail to select the original font into the device context when you're finished with the DC, you will create a resource leak.

Creating a Font Using a LOGFONT Structure

The LOGFONT structure is often used to describe a font. Just as the LOGBRUSH structure discussed in Hour 12 was used to describe a particular brush, the LOGFONT structure is used to describe a particular font. A LOGFONT isn't a font; it's just a description, so it contains members for all the attributes available for a font.

Using a LOGFONT simplifies creating fonts because many of the attributes for a series of fonts can be shared. Listing 13.2 is a version of CDCTestView::OnDraw that uses a LOGFONT structure to create several different fonts.

TYPE: Listing 13.2. Using a LOGFONT structure to create fonts.



void CDCTestView::OnDraw(CDC* pDC)

{

    CRect rcClient;

    GetClientRect( rcClient );

    pDC->DPtoLP( rcClient );

    COLORREF clrOld = pDC->SetTextColor( m_clrChoice );

    int nOldMode = pDC->SetBkMode( TRANSPARENT );

    CString szMsg = "Hello! I'm an Arial font";

    CFont   fntArial;

    LOGFONT lf;

    ZeroMemory( &lf, sizeof(LOGFONT) );

    lstrcpy( lf.lfFaceName, "Arial" );

    fntArial.CreateFontIndirect( &lf );

    CFont* pOldFont = pDC->SelectObject( &fntArial );

    pDC->TextOut( rcClient.Width()/2, rcClient.Height()/2, szMsg );

    pDC->SelectObject( pOldFont );

    pDC->SetTextColor( clrOld );

    pDC->SetBkMode( nOldMode );

} 

Most of the earlier version of OnDraw can remain in place; only the middle part of the function has changed between Listings 13.1 and 13.2. The first eight and the last five lines are the same in both versions.

Selecting and Configuring the Right Fonts

The remainder of this hour discusses two ways to simplify and improve your font-handling code: by using stock fonts provided by Windows and by using the Font Selection common dialog box. You will also make some changes to the DCTest example so that the user can select a font for the application.

Stock Font Objects

Just as with stock pens and brushes, discussed in Hour 12, Windows maintains a set of stock fonts. Windows provides six stock fonts:

As with other stock objects, these fonts are used through a CDC object by calling the SelectStockObject function, passing the stock object as a parameter, as follows:

CPen* pOldFont = pDC->SelectStockObject(SYSTEM_FONT);

Setting the Font for a Window

You can change the font used by a control or any other window by calling the CWnd::SetFont function. The SetFont function takes a pointer to a CFont object as a parameter:

pCtrl->SetFont(fntWingDings);

If you change the font for a window, you must be careful to keep the font that is passed as a parameter valid for as long as the window exists.

Using the Common Font Dialog Box

Like the other common dialog boxes, the Common Font dialog box enables you, as a programmer, to easily use a commonly used dialog box in your Windows programs. The Common Font dialog box is extremely flexible from a user's point of view; the user can change the color, style, typeface, and size of the font in a single dialog box. In this section, you use the Common Font dialog box to select a font to be used in the view window.

The font is represented by a LOGFONT variable that is a member of the CDCTestView class. After selecting a new font with the Common Font dialog box, the LOGFONT variable is updated and the view redrawn.

Five steps are involved in adding support for the Common Font dialog box:

1. Add a new LOGFONT variable to the CDCTestView class.

2. Modify the CDCTestView constructor.

3. Create a new menu item for changing the font.

4. Create a function in the CDCTestView class to handle the new menu item.

5. Modify the CDCTestView::OnDraw member function so that the new LOGFONT variable is used when creating a font.

Just to make things interesting, you modify the OnDraw function to display the text rotated around the center of the view.

Add a LOGFONT Variable to the CDCTestView Class

The first step is to add a LOGFONT variable to the CDCTestView class. Although the font is created and destroyed every time the OnDraw member function is called, the LOGFONT variable stores the current attributes for the font selected for the view. Add this line to the attributes section of the CDCTestView class declaration:

LOGFONT    m_logFont;

The CDCTestView class constructor must initialize this variable to a known value. Listing 13.3 contains the source code for the new version of the CDCTestView constructor. Only the last two lines of the source code have been added since the previous version.

TYPE: Listing 13.3. Source code to initialize the m_logFont variable.

CDCTestView::CDCTestView()

{

    m_nMapMode = MM_TEXT;

    m_map.SetAt( MM_ANISOTROPIC, "MM_ANISOTROPIC" );

    m_map.SetAt( MM_HIENGLISH, "MM_HIENGLISH" );

    m_map.SetAt( MM_HIMETRIC, "MM_HIMETRIC" );

    m_map.SetAt( MM_ISOTROPIC, "MM_ISOTROPIC" );

    m_map.SetAt( MM_LOENGLISH, "MM_LOENGLISH" );

    m_map.SetAt( MM_LOMETRIC, "MM_LOMETRIC" );

    m_map.SetAt( MM_TEXT, "MM_TEXT" );

    m_map.SetAt( MM_TWIPS, "MM_TWIPS" );

    m_cxEllipse = 100;

    m_cyEllipse = 200;

    m_clrChoice = RGB(0,0,0);

    ZeroMemory( &m_logFont, sizeof(LOGFONT) );

    lstrcpy( m_logFont.lfFaceName, "Arial" );

}

Add a New Menu Item

Using the Developer Studio resource editor, add a new menu item to the View menu, using the values from Table 13.2.

Table 13.2. Values used for the Font menu item.

Resource ID Caption Member Function
ID_VIEW_FONT &Font... CDCTestView::OnViewFont

Use ClassWizard to add a message-handling function to the CDCTestView class for the new menu item, using the default name of OnViewFont. The source code for OnViewFont is shown in Listing 13.4.

TYPE: Listing 13.4. The CDCTestView::OnViewFont member function.

void CDCTestView::OnViewFont()

{

    CFontDialog dlgFont( &m_logFont );

    dlgFont.DoModal();

    m_clrChoice = dlgFont.GetColor();

    InvalidateRect( NULL );

} The source code in Listing 13.4 is the heart of this example. The current LOGFONT is passed during construction to the Common Font dialog box, which uses it as a starting point when the dialog box is initially displayed. After the user dismisses the dialog box, the LOGFONT will contain any modifications made by the user. Because the LOGFONT structure doesn't store the text color, the GetColor function is called to update any color selections made in the Common Font dialog box.

Modify the OnDraw Member Function

The final step in this example is to use the selected font to draw a rotating text message in the view. The lfEscapement field from the LOGFONT structure is used to specify the angle of the text line. The source code in Listing 13.5 updates the font's escapement in a for loop, causing the text to rotate.

TYPE: Listing 13.5. Displaying a rotating text message using a LOGFONT.

void CDCTestView::OnDraw(CDC* pDC)

{

    CRect rcClient;

    GetClientRect( rcClient );

    pDC->DPtoLP( rcClient );

    COLORREF clrOld = pDC->SetTextColor( m_clrChoice );

    int nOldMode = pDC->SetBkMode( TRANSPARENT );

    CString szMsg = "  ...Help! I'm Spinning and I can't get up!";

    CFont   fntRotate;

    for( int nDegrees = 0; nDegrees < 3600; nDegrees += 200 )

    {

        m_logFont.lfEscapement = nDegrees;

        fntRotate.CreateFontIndirect( &m_logFont );

        CFont* pOldFont = pDC->SelectObject( &fntRotate );

        pDC->TextOut( rcClient.Width()/2,

                      rcClient.Height()/2,

                      szMsg );

        pDC->SelectObject( pOldFont );

        fntRotate.DeleteObject();

    }

    pDC->SetTextColor( clrOld );

    pDC->SetBkMode( nOldMode );

} The text will rotate around the center of the view, as shown in Figure 13.2. The font and color are updated when a new selection is made in the Common Font dialog box.

Figure 13.2.
Displaying rotating text using font escapement.

Summary

In this hour you learned about using fonts in Windows programs, as well as how to use the CFont and CFontDialog classes. Sample programs illustrated the use of the LOGFONT structure, the use of the Common Font dialog box, and rotating fonts.

Q&A

Q I have problems determining the correct text metrics for my device context. I call GetTextMetrics and then select my font into the device context. What am I doing wrong?

A The GetTextMetrics function returns information about the currently selected font--you must select the font into the device context before calling GetTextMetrics.

Q When I change the font for a pushbutton control nothing happens--the original font is still used to display the caption for the button. What am I doing wrong?

A This problem is usually due to the CFont object being destroyed before the control is destroyed. The control doesn't copy the font passed to it during the SetFont function call--it must be available as long as the control exists. If you change the font for a control, you should make the CFont object a member of the CDialog derived class that contains the control.

Workshop

The Workshop is designed to help you anticipate possible questions, review what you've learned, and begin thinking ahead to putting your knowledge into practice. The answers to the quiz are in Appendix B, "Quiz Answers."

Quiz

1. Give some examples of serif and sans-serif fonts.

2. What are the stock font objects maintained by Windows?

3. What is the font escapement attribute used for?

4. What is a glyph?

5. What MFC class is used to manage fonts?

6. What are the six font families?

7. What are the three pitch choices for a font?

8. What function is used to change the font used by a window?

Exercises

1. Modify the DCTest project so that a different font is used for the Color... pushbutton.

2. Modify the DCTest project to display text metric information for the currently selected font.


Previous chapterNext chapterContents


Macmillan Computer Publishing USA

© Copyright, Macmillan Computer Publishing. All rights reserved.