Text can be surprisingly complex. To make the visual display of the text as readable as possible requires the creation and use of fonts, then decisions about mapping modes, kerning, and more.
Fonts control the display characteristics of text. An X Windows client application can use the XLoadQueryFont() and XSetFont() functions to apply a font to a given graphics context (GC), as shown in the following code:
#define FONT1 "-*-lucida-medium-r-*-*-12-*-*-*-*-*-*-*" Font font1; XFontStruct *font1Info; main() { Display *pDisplay; int iScreen; GC gc; pDisplay = XOpenDisplay("myDisplay"); iScreen = DefaultScreen(pDisplay); //+ // get the Graphics Context //- gc = DefaultGC(pDisplay,iScreen); //+ // attempt to load the font //- font1Info = XLoadQueryFont(pDisplay,FONT1); font1 = font1Info->fid; //+ // Set the font in the GC //- XSetFont(pDisplay, gc, font1);
A Win32-based application follows much the same logic. That is, it creates or selects a font, retrieves a device context (DC), and then selects the font object to that DC. For example:
#define FONT1 TEXT("Lucida Console"); HFONT hFont1; void fontDemo(HWND hWnd) { HDC hDC; HFONT hOldFont; //+ // get the Device Context //- hDC = GetDC(hWnd); //+ // attempt to load the system font //- hFont1 = (HFONT)GetStockObject (SYSTEM_FONT); //+ // Set the font in the GC //- hOldFont = (HFONT)SelectObject(hDC, hFont1);
Table 11.6 shows how to reference fixed font types.
Font Reference | Font Type |
---|---|
ANSI_FIXED_FONT | Windows fixed-pitch (monospace) system font. |
ANSI_VAR_FONT | Windows variable-pitch (proportional space) system font. |
DEVICE_DEFAULT_FONT | Microsoft Windows NT , Windows 2000, or Windows XP operating system: Device-dependent font. |
DEFAULT_GUI_FONT | Default font for user interface objects such as menus and dialog boxes. This is Microsoft Sans Serif. Compare this with SYSTEM_FONT. |
OEM_FIXED_FONT | Original equipment manufacturer (OEM)-dependent fixed-pitch (monospace) font. |
SYSTEM_FONT | System font. By default, the system uses the system font to draw menus, dialog box controls, and text. Windows 95/98 or Windows NT: The system font is Microsoft Sans Serif. Windows 2000 or Windows XP: The system font is Tahoma. |
SYSTEM_FIXED_FONT | Fixed-pitch (monospace) system font. This stock object is provided only for compatibility with 16-bit Windows earlier than version 3.0. |
Note | Win32 provides several utilities for adding and editing fonts. The Eudcedit.exe utility, which comes with the operating system, allows the user to create unique characters such as logos and special characters. Eudcedit Help describes how to create, store, and use these characters in the font library. The Charmap.exe utility, which comes with the operating systems, allows the user to view, find, and copy characters from the Windows, MS-DOS, and Unicode character sets. Charmap Help describes how to do so. The Fontedit.exe utility, which comes with Visual Studio, allows the user to create and edit raster fonts. |
Developers might want to create fonts to use in an application. The short example in this section uses the font specified by SYSTEM_FONT, which duplicates the default, although developers are likely to use something more creative. The Win32 CreateFont() , CreateFontIndirect() , and CreateFontIndirectEx() functions provide the ability to create logical fonts based on the fonts loaded on the system.
#define MY_FONT_FACE TEXT("Lucida Console") //+ // fontAttribute Option Bits //- #define fontAttribute_BOLD 0x01 #define fontAttribute_CROSSED_OUT 0x02 #define fontAttribute_UNDERLINED 0x04 #define fontAttribute_ITALIC 0x08 typedef struct { unsigned char fontSize; unsigned char fontStyle; TCHAR *fontFace; } tyFONT_ATTRIBUTE; HFONT createFont(tyFONT_ATTRIBUTE *fontAttributeObject) { HFONT hFont; LOGFONT lf; //+ // these are completely arbitrary values for this example code. // they simply associate a width and height with a // font size number found in the tyFONT_ATTRIBUTE struct. // // For example fontSize == 2 (used to index these two arrays) // will produce a 12x8 font //- int fontHeight[] = {8,8,12,16,16,24,32, 32,48,64,64,96,128,128,192}; int fontWidth [] = {6,8, 8,12,16,16,24, 32,32,48,64,64, 96,128,128}; //+ // pick a font face //- lstrcpy(lf.lfFaceName, fontAttributeObject->fontFace); //+ // protect against running out of the arrays above // and pick a default behavior of "2" //- if (fontAttributeObject->fontSize > 14) fontAttributeObject->fontSize = 2; if (fontAttributeObject->fontStyle & fontAttribute_BOLD) lf.lfWeight = FW_MEDIUM; else lf.lfWeight = FW_LIGHT; lf.lfItalic = (unsigned char)(fontAttributeObject->fontStyle & fontAttribute_ITALIC); lf.lfUnderline = (unsigned char)(fontAttributeObject->fontStyle & fontAttribute_UNDERLINED); lf.lfStrikeOut = (unsigned char)(fontAttributeObject->fontStyle & fontAttribute_CROSSED_OUT); lf.lfEscapement = 0; lf.lfOrientation = 0; lf.lfCharSet = ANSI_CHARSET; lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; lf.lfQuality = DRAFT_QUALITY; lf.lfPitchAndFamily = FF_MODERN FIXED_PITCH; lf.lfHeight = fontHeight [ fontAttributeObject->fontSize ]; lf.lfWidth = fontWidth [ fontAttributeObject->fontSize ]; hFont = CreateFontIndirect(&lf); return(hFont); } //+ // example using createFont() //- void fontDemo(HWND hWnd) { HDC hDC; HFONT hOldFont; HFONT hFont1; tyFONT_ATTRIBUTE fontAttribute; //+ // get the Device Context //- hDC = GetDC(hWnd); //+ // attempt to create a font //- fontAttribute.fontSize = 2; fontAttribute.fontStyle = (fontAttribute_BOLD fontAttribute_ITALIC); lstrcpy(fontAttribute.fontFace, MY_FONT_FACE); hFont1 = createFont(&fontAttribute); //+ // Set the font in the GC //- hOldFont = (HFONT)SelectObject(hDC, hFont1);
For more information about creating and using logical fonts in a Win32-based application, search the MSDN Web site ( http://msdn.microsoft.com/ ) for the article The Logical Font.
An application can retrieve font metrics for a physical font only after the font has been selected in a device context. When a user selects a font in a device context, the system scales the font for the device. The font metrics specific to the device are known as device units .
Portable metrics in fonts are known as design units . To apply them to a specified device, convert design units to device units by using the following formula:
DeviceUnits = ( DesignUnits / unitsPerEm ) * ( PointSize /72) * DeviceResolution
For a full explanation of device units, design units, and pixels, see the operating system Help or search the MSDN Web site ( http://msdn.microsoft.com/ ).
Table 11.7 lists the Windows character types. Most of the pointer-type names begin with a prefix of P or LP. For more information about character sets used by fonts, see the operating system Help or search the MSDN Web site ( http://msdn.microsoft.com/ ).
Reference | Character Type |
---|---|
CHAR | An 8-bit Windows (ANSI) character. |
LPCSTR | Pointer to a constant null- terminated string of 8-bit Windows (ANSI) characters. |
LPCTSTR | LPCWSTR if UNICODE is defined, LPCSTR otherwise . |
LPCWSTR | Pointer to a constant null-terminated string of 16-bit Unicode characters. |
LPSTR | Pointer to a null-terminated string of 8-bit Windows (ANSI) characters. |
LPTSTR | LPWSTR if UNICODE is defined, LPSTR otherwise. |
PCHAR | Pointer to CHAR. |
PCSTR | Pointer to a constant null-terminated string of 8-bit Windows (ANSI) characters. |
PCTSTR | PCWSTR if UNICODE is defined, PCSTR otherwise. |
PCWCH | Pointer to a constant WCHAR. |
PCWSTR | Pointer to a constant null-terminated string of 16-bit Unicode characters. |
PSTR | Pointer to a null-terminated string of 8-bit Windows (ANSI) characters. |
PTCHAR | Pointer to TCHAR. |
PTSTR | PWSTR if UNICODE is defined, PSTR otherwise. |
PWSTR | Pointer to a null-terminated string of 16-bit Unicode characters. |
TBYTE | WCHAR if UNICODE is defined, CHAR otherwise. |
TCHAR | WCHAR if UNICODE is defined, CHAR otherwise. |
WCHAR | A 16-bit Unicode character. |
A best practice with characters is to declare all characters and strings as TCHAR and use the TEXT() macro to declare static strings. For example:
TCHAR myString[255]; wsprintf(myString, TEXT("This is a good example %d is a %s \n"), 1950, TEXT("Year"));
For more information about wsprintf() and the rest of the string functions, see the operating system Help or search the MSDN Web site ( http://msdn.microsoft.com/ ).
Drawing text can be simple or complicated. Because simple is often better, this discussion starts with the X Windows XDrawString() function and the Win32 TextOut() function. Both functions require a context to draw on, the x and y coordinates, the string, and the string length in characters. The examples in this section draw the string Hello World in the current font and colors at the specified coordinates.
It is often desirable to set a particular font or color before writing the text. These examples show how the two systems perform these tasks .
A programmer can code font and text display in Win32 as follows:
#define rgbBlack (COLORREF)RGB(0x00,0x00,0x00) #define rgbWhite (COLORREF)RGB(0xFF,0xFF,0xFF) font = (HFONT)GetStockObject(OEM_FIXED_FONT); oldFont = (HFONT)SelectObject(hdc, font); // save old font SetTextColor (hdc, rgbBlack); SetBkColor (hdc, rgbWhite); TextOut(hdc, x, y, "Hello World", 11);
The preceding Win32 code example uses the COLORREF type, the RGB() macro, and the GetStockObject() function as follows:
The COLORREF value specifies an RGB color and is defined as shown here:
typedef DWORD COLORREF; typedef DWORD *LPCOLORREF;
The RGB macro selects a red, green, blue (RGB) color based on the arguments supplied and the color capabilities of the output device, as shown here:
COLORREF RGB(BYTE byRed , // red component of color BYTE byGreen , // green component of color BYTE byBlue // blue component of color);
The GetStockObject(int objectType) function retrieves a handle to one of the stock pens, brushes, fonts, or palettes. The return value must be cast to the expected type, as shown here:
void foo() { HFONT hFont; HBRUSH hBrush; hfont = (HFONT)GetStockObject(DEFAULT_GUI_FONT); hBrush = (HBRUSH)GetStockObject(BLACK_BRUSH); }
A programmer can code font and text display in X Windows as follows:
font = XLoadQueryFont (display, "fixed"); XSetFont (display, gc, font->fid); XSetBackground(display, gc, WhitePixel(display, screen)); XSetForeground(display, gc, BlackPixel(display, screen)); XDrawString(display, d, gc, x, y, "Hello World", 11);
X Windows provides explicit definitions of 8-bit and 16-bit character functions with XDrawString() and XDrawString16() . Likewise, Win32 provides TextOutA() for A SCII (8-bit characters) and TextOutW() for Wide Char (16-bit UNICODE characters). The TextOut() function is actually a macro that resolves correctly to TextOutA() or TextOutW() based on the status of the UNICODE definition, as follows:
#define UNICODE #define _UNICODE TextOut() // this will result in TextOutW() #undef UNICODE #undef _UNICODE TextOut() // this will result in TextOutA()
One drawback of using XDrawString() and TextOut() is that nothing is done about erasing the background. Continually outputting strings to the same x and y coordinates results in a jumble of unreadable text strings, one upon the other. The X Windows library provides the DrawImageString() function, which calculates a rectangle containing the string and fills it with the background pixel color before drawing the text in the foreground pixel color. Win32 supports the ExtTextOut() function to provide this capability. Using the Win32 ExtTextOut() function requires the bounding rectangle to be calculated and passed into the function. This requires knowledge about the current font and logical display units.
The X Windows programmer can rely on XTextWidth() to get the length of a character string in pixels. The Win32 programmer must work a little harder to get this number.
First, it is necessary to understand mapping mode. The mapping mode defines the unit of measure used to transform page-space units into device-space units. It also defines the orientation of the device s x and y axes.
A mapping mode is a scaling transformation that specifies the size of the units used for drawing operations. The mapping mode can also perform translation. In some cases, the mapping mode alters the orientation of the x and y axes in device space.
The default mapping mode is MM_TEXT. One logical unit equals one pixel. Positive x is to the right, and positive y is down. This mode maps directly to the device s coordinate system.
The Win32 SetMapMode function sets the mapping mode of the specified device context, as shown in the following code:
int SetMapMode(HDC hdc, // handle to device context int fnMapMode // new mapping mode);
Ultimately, to calculate the size of a string in pixels it is necessary for the current mapping mode to be MM_TEXT. The Win32 programmer can either assume the current mapping mode is the default MM_TEXT, set it to MM_TEXT by calling SetMapMode() , or make sure it is MM_TEXT by using GetMapMode() to retrieve it. (For more information, search for MM_TEXT on the MSDN Web site, http://msdn.microsoft.com/ .)
The Win32 GetTextExtentPoint32() function returns the width and height of a string of text in logical units, as shown in the follwoing code. (Recall that setting the mapping mode to MM_TEXT returns logical units as pixels.)
BOOL GetTextExtentPoint32(HDC hdc, // handle to DC LPCTSTR lpString, // text string int cbString, // characters in string LPSIZE lpSize // string size);
The size structure looks like the following and is defined in Windef.h:
typedef struct tagSIZE { LONG cx; LONG cy; } SIZE, *PSIZE, *LPSIZE;
The Win32 GetTextMetrics() function fills a TEXTMETRIC structure with all the information about the device context s currently selected font, as shown in the following code. The programmer can use this information to perform any number of scaling or text size calculations.
BOOL GetTextMetrics(HDC hdc, // handle to DC LPTEXTMETRIC lptm // text metrics);
The TEXTMETRIC structure contains basic information about a physical font, as shown in the following example. All sizes are specified in logical units; that is, they depend on the current mapping mode of the display context.
typedef struct tagTEXTMETRIC { LONG tmHeight; LONG tmAscent; LONG tmDescent; LONG tmInternalLeading; LONG tmExternalLeading; LONG tmAveCharWidth; LONG tmMaxCharWidth; LONG tmWeight; LONG tmOverhang; LONG tmDigitizedAspectX; LONG tmDigitizedAspectY; TCHAR tmFirstChar; TCHAR tmLastChar; TCHAR tmDefaultChar; TCHAR tmBreakChar; BYTE tmItalic; BYTE tmUnderlined; BYTE tmStruckOut; BYTE tmPitchAndFamily; BYTE tmCharSet; } TEXTMETRIC, *PTEXTMETRIC;
The following Win32 functions are also useful for working with text:
DrawText()
CreateSolidBrush()
GetSysColor()
SetTextColor()
GrayString()
This section discusses these functions and shows examples of their use.
The DrawText() function draws formatted text in the specified rectangle, as shown in the following example. It formats the text according to the specified method, expanding tabs, justifying characters, breaking lines, and so forth.
int DrawText(HDC hDC, // handle to DC LPCTSTR lpString, // text to draw int nCount, // text length LPRECT lpRect, // formatting dimensions UINT uFormat // text-drawing options);
The CreateSolidBrush() function creates a logical brush that has the specified solid color, as shown in the following example:
HBRUSH CreateSolidBrush(COLORREF crColor // brush color value);
The GetSysColor() function retrieves the current color of the specified display element, as shown in the following example. Display elements are the parts of a window and the Windows display that appear on the system display screen.
DWORD GetSysColor(int nIndex);
The SetTextColor() function sets the text color for the specified device context to the specified color, as shown in the following example:
COLORREF SetTextColor(HDC hdc, // handle to DC COLORREF crColor // text color);
The following example incorporates the use of DrawText() , CreateSolidBrush() , GetSysColor() , and SetTextColor() :
RECT myRectangle; //+ // create a brush //- HBRUSH myBackgroundBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND) // color of system background); //+ // set the text color to the systems button text color //- SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT) //color of text on buttons); // calculate myRectangle //+ // fill in (erase) the area inside the rectangle with the // systems background color //- FillRect(hdc, &myRectangle, myBackgroundBrush); //+ // The DrawText function uses the device contexts selected font, text // color, and background color to draw the text. Unless the DT_NOCLIP // format is used, DrawText clips the text so that it does not appear // outside the specified rectangle. //- DrawText(hdc, myString, _tcsclen(myString), // use _tcsclen() vs. strlen() &myRectangle, (DT_CENTER DT_SINGLELINE));
The GrayString() function draws gray text at the specified location, as shown in the following example. The function draws the text by copying it into a memory bitmap, graying the bitmap, and then copying the bitmap to the screen. The function grays the text regardless of the selected brush and background. GrayString() uses the font currently selected for the specified device context.
BOOL GrayString(HDC hDC, // handle to DC HBRUSH hBrush, // handle to the brush GRAYSTRINGPROC lpOutputFunc, // callback function LPARAM lpData, // application-defined data int nCount, // number of characters int X, // horizontal position int Y, // vertical position int nWidth, // width int nHeight // height);
A text widget or control is used to display, enter, and edit text. The exact functionality of a text widget or control depends upon how its resources are set.
In X Windows, the widget functionality is set as shown in the following example:
text = XtVaCreateManagedWidget ("myTextWidget", asciiTextWidgetClass, parentWidget, XtNfromHoriz, quit, XtNresize, XawtextResizeBoth, XtNresizable, True, NULL);
In Motif, the widget functionality is set as shown in the following example:
main (int argc, char *argv[]) { Widget mainWidget; Widget textWidget; XtAppContext appContext; mainWidget = XtVaOpenApplication (&appContext, "TextExample", NULL, 0, &argc, argv, NULL, sessionShellWidgetClass, NULL); ( ) textWidget = XmCreateText (mainWidget,"textWidget",NULL,0); () XtAppMainLoop(appContext); }
In Win32 and GDI, the control functionality is set as shown in the following example:
//+ // Create an edit Control. //- HWND handleToThisEditControl; handleToThisEditControl = CreateWindow(TEXT("EDIT"), // the type of control TEXT("Some Text"), // edit control text (WS_CHILD WS_VISIBLE ES_READONLY ES_LEFT ES_UPPERCASE), // the control style XpositionInParent, yPositionInnParent, CONTROL_WIDTH_IN_DEVICE_UNITS, CONTROL_HEIGHT_IN_DEVICE_UNITS, handleOfParentWindow, // parent window (HMENU)NUMBER_USED_TO_ID_THIS_EDIT_CONTROL, appContext, NULL); //+ // Turn off Read Only //- SendMessage(handleToThisEditControl , EM_SETREADOINLY, (WPARAM)FALSE, // set read only false (LPARAM)NULL); //+ // set the edit controls text //- SetWindowText(handleToThisEditControl, TEXT("Some New Text")); //+ // retrieve the edit controls text as text //- GetWindowText(handleToThisEditControl, myStringBuffer, myStringBufferMaxSize); //+ // retrieve the edit controls text as an integer //- myIntegerValue = GetDlgItemInt(handleOfParentWindow, NUMBER_USED_TO_ID_THIS_EDIT_CONTROL, &resultFlag, // did the translation succeed ? FALSE); // no this is an unsigned number