Topic : Using Fonts in MFC
Author : Unknown
Page : << Previous 2  Next >>
Go to page :


raster fonts can not be drawn in a scaled size, they are device dependent (the size of the output depends on the resolution of the device). The vector fonts and true Type Fonts are device independent because they are scalable, however, drawing True Type fonts is faster than drawing vector fonts. This is because the glyph of True Type fonts contains commands and hints.

Enumerating Font Family

We can find out all the font families installed in a system by calling API function ::EnumFontFamilies(...), which has the following format:

int EnumFontFamilies

(

HDC hdc, LPCTSTR lpszFamily, FONTENUMPROC lpEnumFontFamProc, LPARAM lParam

);


The first parameter hdc is the handle to a device context, which can be obtained from any window.

The second parameter lpszFamily specifies the font family name that will be enumerated, it could be any of "Decorative", "Dontcare", "Modern", "Roman", "Script" and "Swiss". To enumerate all fonts in the system, we just need to pass NULL to it.

The third parameter is a pointer to callback function, which will be used to implement the actual enumeration. This function must be provided by the programmer.

The final parameter lParam is a user-defined parameter that allows us to send information to the callback function.

The callback function must have the following format:

int CALLBACK EnumFontFamProc
(

ENUMLOGFONT FAR *lpelf, NEWTEXTMETRIC FAR *lpntm, int FontType, LPARAM lParam

);


Each time a new font family is enumerated, this function is called by the system. So the function will be called for all the available font types (e.g. if there are three types of fonts in the system, this funciton will be called three times by the system). The font's information is stored in an ENUMLOGFONT type object that is pointed by lpelf, and the font type is specified by FontType parameter. We can check RASTER_FONTTYPE or TRUETYPE_FONTTYPE bit of FontType to judge if the font is a raster font or a true type font. The final parameter lParam will be used to store the information that is passed through the user defined parameter (lParam in the function ::EnumFontFamilies(...)).

Sample 2\GDI demonstrates how to enumerate all the valid fonts in the system. It is a standard SDI application generated by Application Wizard. The application will display all the available fonts in the client window after it is executed. Because there are many types of fonts, the view class of this application is derived from CScrollView (This can be set in the final step of Application Wizard).

First, an int type array is declared in class CGDIView:

class CGDIView : public CScrollView
{

protected:

int m_nFontCount[3];

......

}


The first element of this array will be used to record the number of raster fonts in the system, the second and the third elements will be used to store the number of vector and true type fonts respectively. The array is initialized in the constructor as follows:

CGDIView::CGDIView()
{

m_nFontCount[0]=m_nFontCount[1]=m_nFontCount[2]=0;

}


We need to create the callback function in order to implement the enumeration. In the sample, a global function ::EnumFontFamProc(...) is declared as follows (This function can also be declared as a static member function):

int CALLBACK EnumFontFamProc(LPLOGFONT, LPNEWTEXTMETRIC, DWORD, LPVOID);

This function is implemented as follows:

(Code omitted)

We will use user-defined parameter lParam in the function ::EnumFontFamilies(...) to pass the address of CGDIView::m_nFontCount into the callback function, so that we can fill the font's information into this array when the enumeration is undergoing. In the callback function, the address of CGDIView:: m_nFontCount is received by parameter pFontCount, which is then cast to an integer type pointer. The font type is retrieved by examining parameter FontType, if the font is a raster font, the first element of CGDIView:: m_nFontCount will be incremented; if the font is a true type font, the third element will be incremented; in the rest case, the font must be a vector font, and the second element will be incremented.

The best place to implement the font enumeration is in function CView::OnInitialUpdate(), when the view is first created. In the sample, a client DC is created and function ::EnumFontFamilies(...) is called. When doing this, we pass the address of CGDIView::m_nFontCount as a user-defined parameter:

(Code omitted)

Still, we need to display the result in function CView::OnDraw(...). In the sample, this function is implemented as follows:

(Code omitted)

We just display three lines of text indicating how many fonts are contained in the system for each different font family.

Enumerating Font

Apart from the above information (how many fonts there are for each font family), we may further want to know the exact properties of every font type (i.e., face name). To implement this, we need to allocate enough memory to store the information of all fonts. Here, the size of this buffer depends on the number of fonts whose properties are to be retrieved. Since each font need a LOGFONT structure to store all its information, we can use the following formulae to calculate the required buffer size:

(sizeof structure LOGFONT) * (number of fonts)

For this purpose, in the sample, another two variables are declared in class CGDIView as follows:

class CGDIView : public CScrollView
{

protected:

......

LPLOGFONT m_lpLf[3];

CFont *m_ptrFont;

......

}


Array m_lpLf will be used to store LOGFONT information, and m_ptrFont will be used to store CFont type variables. The variables are initialized in the constructor as follows:

CGDIView::CGDIView()

{

......

m_lpLf[0]=m_lpLf[1]=m_lpLf[2]=NULL;

m_ptrFont=NULL;

}


We need to provide another callback function to retrieve the actual information for each font type. In the sample, this callback function is declared and implemented as follows:

int CALLBACK EnumFontProc(LPLOGFONT, LPNEWTEXTMETRIC, DWORD, LPVOID);

(Code omitted)

Three static variables are declared here to act as the counters for each type of fonts. When this function is called, the information of the font is copied from lplf to the buffers allocated in CGDIView:: OnInitialUpdate(), whose address is passed through user-defined parameter.

In function CGDIView::OnInitialUpdate(), after the font families are enumerated, we need to allocate enough memory, implement the enumeration again for every single type of font:

(Code omitted)

After obtaining the information for each type of font, we create a font using this information by calling function CFont::CreateFontIndirect(...). The addresses of these font objects are stored in array CGDIView::m_ptrFont.

In function CGDIView::OnDraw(...), the face names of all fonts are output to the client window:

(Code omitted)

For each font family, all the font face names are listed. Three loops are used for this purpose. Within each loop, one of the enumerated font is selected into the target DC, and function CDC::TextOut(...) is called to output the font's face name to the window. To avoid text from overlapping one another, a local variable nYPos is used as the vertical orgin of the output text, which will increment each time after a line of text is output to the window.

Because the memory is allocated at the initialization stage, we need to free it when the application exits. In the sample, WM_DESTROY message handler is added to class CGDIView through using Class Wizard, and the corresponding member function is implemented as follows:

(Code omitted)

The application is now ready to enumerate all the available fonts in the system.

3 Output Text Using CDC::ExtTextOut(...)

Function CDC::ExtTextOut(...)

Usually we use function CDC::TextOut(...) to output text. There is another powerful function CDC:: ExtTextOut(...), which allows us to output the text to a specified rectange. We can use either transparent or opaque drawing mode. In the latter case, we can also specify a background color. Besides this, we can set the distances between neighboring characters of the text. One version of function CDC::ExtTextOut(...) has the following format:

BOOL CDC::ExtTextOut
(

int x, int y,

UINT nOptions, LPCRECT lpRect, const CString &str, LPINT lpDxWidths

);


Like CDC::TextOut(...), the first two parameters x and y specify the position of the output text. The third parameter nOptions indicates the drawing mode, it could be any type of combination between ETO_CLIPPED and ETO_OPAQUE flags (Either flag bit can be set or not set, altogether there are four possibilities). Style ETO_CLIPPED allows us to output text within a specified rectangle, and restrict the drawing within the rectangle even if the size of the text is bigger than the rectangle. In this case, all interior part of the rectangle not occupied by the text is treated as background. The third parameter is a CString type value that specifies the actual text we want to output. The last parameter is a pointer to an array of integers, which specify the distances between origins of two adjacent characters. This gives us the control of placing each character within a text string to a specified place. If we pass NULL to this parameter, the default spacing method will be applied.

A very typical use of this function is to implement a progress bar with percentage displayed in it (Figure 9-2). The progress bar is divided into two parts. For one part the text color is white and the background color is blue, for the other part the text color is blue and the background color is white.

New Class

Sample 3\GDI demonstrates how to implement this percentage bar. It is a standard SDI application generated from Application Wizard.

First a new class CPercent is added to the application through using Class Wizard, this class will be used to implement the percentage bar. Here, the base class is selected as CStatic.

The purpose of choosing

Page : << Previous 2  Next >>