18.4.7 Drawing Text

An application can use the following functions to draw text:

Function Description

DrawText Draws formatted text in a rectangle. DrawText formats text by expanding tabs into appropriate spaces, aligning text to the left, right, or center of the given rectangle, and breaking text into lines that fit within the given rectangle. This is not a GDI function; it is in USER.EXE.
ExtTextOut Writes a character string within a rectangular region. The rectangular region can be opaque (filled with the current background color), and it can be a clipping region.
GrayString Draws gray text by writing the text in a memory bitmap, graying the bitmap, and then copying the bitmap to the device. GrayString grays the text regardless of the selected brush and background. This is not a GDI function; it is in USER.EXE.
TabbedTextOut Writes a character string, expanding tabs to the values specified in an array of tab-stop positions.
TextOut Writes a character string at a specified location.

The ExtTextOut function is the fastest Windows text-output function. The DrawText function is the slowest (although it offers the richest formatting options).

Instead of using the GrayString function, an application could simply set the text color to gray, as follows:

dwColorPrevious = SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));

18.4.7.1 Setting the Text Alignment

An application can query and set the text alignment for a device context by using the GetTextAlign and SetTextAlign functions. The text-alignment settings determine how text is positioned relative to a given location. Text can be aligned to the right or left of the position or centered over it; it can also be aligned above or below the point. In addition, an application can use the SetTextAlign function to update the current position when a text-output function is called.

For example, the following example uses the SetTextAlign function to update the current position when the TextOut function is called. In this example, cArial is an integer that specifies the number of Arial fonts:

UINT uAlignPrev;
char szCount[8];

uAlignPrev = SetTextAlign(hdc, TA_UPDATECP);
MoveTo(hdc, 10, 50);
TextOut(hdc, 0, 0, "Number of Arial fonts: ", 23);
itoa(cArial, szCount, 10);
TextOut(hdc, 0, 0, (LPSTR) szCount, strlen(szCount));
SetTextAlign(hdc, uAlignPrev);

18.4.7.2 Using Color

When an application first creates a device context, the text color is black and the background color is white. An application can add color to text by setting the text and background colors of the device context. The text color determines the color of the character to be written; the background color determines the color of everything in the character cell except the character.

An application can set the text and background colors by using the SetTextColor and SetBkColor functions. The following example sets the text color to red and the background color to green:

SetTextColor(hdc, RGB(255,0,0));
SetBkColor(hdc, RGB(0,255,0));

The background color applies only when the background mode is opaque. The background mode determines whether the background color in the character cell has any effect on what is already on the screen. If the mode is opaque, the background color overwrites anything already on the screen; if the mode is transparent, anything on the screen that would otherwise be overwritten by the background is preserved. The background color for an italic string that GDI has synthesized is sheared along with the characters; this can lead to unexpected results when the text background color is different from the window background color. An application can set and retrieve the background mode by using the SetBkMode function and GetBkMode functions. Similarly, an application can retrieve the current text and background color by using the GetTextColor and GetBkColor functions.

18.4.7.3 Using Multiple Fonts in a Line

Different type styles within a font family can have different widths. For example, bold and italic styles of a family are always wider than the roman style for a given point size. An application that can display or print several type styles on a single line must keep track of the width of the line to avoid having characters print on top of one another.

An application can use the following functions to retrieve the width (or extent) of text in the current font:

Function Description

GetTabbedTextExtent Computes the width and height of a character string. If the string contains one or more tab characters, the width of the string is based upon a specified array of tab-stop positions.
GetTextExtent Computes the width and height of a line of text.

When necessary, GDI synthesizes a font by changing the character bitmaps. To synthesize a character in a bold font, GDI draws the character twice: once at the starting point, and again one pixel to the right of the starting point. To synthesize a character in an italic font, GDI draws the two rows of pixels at the bottom of the character cell, moves the starting point one pixel to the right, draws the next two rows, and continues until the character has been drawn. The base line of a synthesized italic character is shifted to the right by an amount determined by the height of the character cell. To determine the amount a base line is shifted to the right, an application can perform the following calculation, using values retrieved by a call to the GetTextMetrics function:

units base line shifted right = (tmDescent * tmOverhang) / tmAscent

One way to write a line of text that contains multiple fonts is to use the GetTextExtent function after each call to TextOut and add the length to a current position. The following example writes the line “This is a sample string.”, using bold characters for the words “This is a”, italic characters for the word “sample”, and system default characters for “string.”:

int XIncrement;
TEXTMETRIC tm;
HFONT hfntDefault, hfntItalic, hfntBold;

XIncrement = 10;
hfntDefault = SelectObject(hdc, hfntBold);
TextOut(hdc, XIncrement, 50, "This is a ", 10);

XIncrement += LOWORD(GetTextExtent(hdc, "This is a ", 10));
GetTextMetrics(hdc, &tm);
XIncrement -= tm.tmOverhang;
SelectObject(hdc, hfntItalic);
GetTextMetrics(hdc, &tm);
XIncrement -= tm.tmOverhang;
TextOut(hdc, XIncrement, 50, "sample ", 7);

XIncrement += LOWORD(GetTextExtent(hdc, "sample ", 7));
SelectObject(hdc, hfntDefault);
TextOut(hdc, XIncrement - tm.tmOverhang, 50, "string.", 7);

In this example, the GetTextExtent function returns a 32-bit value (of type DWORD) containing both the length and height of the specified string. The LOWORD macro then retrieves the length of the string, which is added to the current position. The GetTextMetrics function retrieves the overhang for the current font. Because the overhang is zero if the font is a TrueType font, the overhang value does not change the string placement in that case. For raster fonts, however, it is important to use the overhang value. (For more information about overhangs, see Section 18.1.3, “Measuring Line and Intercharacter Spacing.”) The overhang is subtracted from the bold string once, to bring subsequent characters closer to the end of the string if the font is a raster font. Because overhang affects both the beginning and end of the italic string in a raster font, the glyphs begin to the right of the specified location and end to the left of the endpoint of the last character cell. (The GetTextExtent function retrieves the extent of the character cells, not the extent of the glyphs.) To account for the overhang for the raster italic string, this example subtracts the overhang before placing the string and subtracts it again before placing subsequent characters.

An application that must place characters with greater precision can use the GetCharWidth or GetCharABCWidths function to retrieve the widths of individual characters in a font. The GetCharABCWidths function is more accurate than the GetCharWidth function, but only when it is used with TrueType fonts; when GetCharABCWidths is used with non-TrueType fonts, it retrieves the same information as GetCharWidth.

The SetTextJustification function adds extra space to the break characters in a line of text. An application can use the GetTextExtent function to determine the extent of a string, subtract the extent from the total amount of space the line should occupy, and use the SetTextJustification function to distribute the extra space among the break characters in the string. The SetTextCharacterExtra function adds extra space to every character cell in the selected font, including the break character. (An application can use the GetTextCharacterExtra function to determine the current amount of extra space being added to the character cells; the default setting is zero.)

ABC spacing also allows an application to perform very accurate text alignment. For example, when an application right aligns a raster roman font without using ABC spacing, the advance width is calculated as the character width. This means the white space to the right of the glyph in the bitmap is aligned, not the glyph itself. By using ABC widths, applications have more flexibility in the placement and removal of white space when aligning text, because they have information that allows them to finely control intercharacter spacing.

18.4.7.4 Rotating Text

Applications can rotate TrueType fonts at any angle. This is useful for labeling charts and other illustrations. The following example rotates a string in 10-degree increments around the center of the client area by changing the value of the lfEscapement member of the LOGFONT structure used to create the font:

RECT rc;
int angle;
HFONT hfnt, hfntPrev;
LPSTR lpszRotate = "String to be rotated.";
PLOGFONT plf = (PLOGFONT) LocalAlloc(LPTR, sizeof(LOGFONT));

lstrcpy(plf->lfFaceName, "Arial");
plf->lfWeight = 700;

GetClientRect(hwnd, &rc);
SetBkMode(hdc, TRANSPARENT);

for (angle = 0; angle < 3600; angle += 100) {
    plf->lfEscapement = angle;
    hfnt = CreateFontIndirect(plf);
    hfntPrev = SelectObject(hdc, hfnt);
    TextOut(hdc, rc.right / 2, rc.bottom / 2,
        lpszRotate, lstrlen(lpszRotate));
    SelectObject(hdc, hfntPrev);
    DeleteObject(hfnt);
}

SetBkMode(hdc, OPAQUE);
LocalFree((LOCALHANDLE) plf);

This example produces the following pattern:

The lfOrientation member of the LOGFONT structure is ignored by GDI, which currently, assumes that the values for lfEscapement and lfOrientation are identical.