Training
Certifications
Books
Special Offers
Community




 
Programming Windows®, Fifth Edition
Author Charles Petzold
Pages 1520
Disk 1 Companion CD(s)
Level All Levels
Published 11/11/1998
ISBN 9781572319950
Price $59.99
To see this book's discounted price, select a reseller below.
 

More Information

About the Book
Table of Contents
Sample Chapter
Related Series
Related Books
About the Author

Support: Book & CD

Rate this book
Barnes Noble Amazon Quantum Books

 


Programming Windows, Fifth Ed.

Charles Petzold

ISBN: 1-57231-995-x


Chapter 17: Text and Fonts (continued)


The Logical Font

Now that we've nailed down the concept of logical inches and logical twips, it's time to talk about logical fonts.

A logical font is a GDI object whose handle is stored in a variable of type HFONT. A logical font is a description of a font. Like the logical pen and logical brush, it is an abstract object that becomes real only when it is a selected into a device context when an application calls SelectObject. For logical pens, for instance, you can specify any color you want for the pen, but Windows converts that to a pure color available on the device when you select the pen into the device context. Only then does Windows know about the color capabilities of the device.

Logical Font Creation and Selection

You create a logical font by calling CreateFont or CreateFontIndirect. The CreateFontIndirect function takes a pointer to a LOGFONT structure, which has 14 fields. The CreateFont function takes 14 arguments, which are identical to the 14 fields of the LOGFONT structure. These are the only two functions that create a logical font. (I mention this because there are multiple functions in Windows for some other font jobs.) Because the 14 fields are difficult to remember, CreateFont is rarely used, so I'll focus on CreateFontIndirect.

There are three basic ways to define the fields of a LOGFONT structure in preparation for calling CreateFontIndirect:

  • You can simply set the fields of the LOGFONT structure to the characteristics of the font that you want. In this case, when you call SelectObject, Windows uses a "font mapping" algorithm to attempt to give you the font available on the device that best matches these characteristics. Depending on the fonts available on the video display or printer, the result might differ considerably from what you request.

  • You can enumerate all the fonts on the device and choose from those or even present them to the user with a dialog box. I'll discuss the font enumeration functions later in this chapter. These are not used much these days because the third method does the enumeration for you.

  • You can take the simple approach and call the ChooseFont function, which I discussed a little in Chapter 11. You get back a LOGFONT structure that you can use directly for creating the font.

In this chapter, I'll use the first and third approaches.

Here is the process for creating, selecting, and deleting logical fonts:

  1. Create a logical font by calling CreateFont or CreateFontIndirect. These functions return a handle to a logical font of type HFONT.
  2. Select the logical font into the device context using SelectObject. Windows chooses a real font that most closely matches the logical font.
  3. Determine the size and characteristics of the real font with GetTextMetrics (and possibly some other functions). You can use this information to properly space the text that you write when this font is selected into the device context.
  4. After you've finished using the font, delete the logical font by calling DeleteObject. Don't delete the font while it is selected in a valid device context, and don't delete stock fonts.

The GetTextFace function lets a program determine the face name of the font currently selected in the device context:

GetTextFace (hdc, sizeof (szFaceName) / sizeof (TCHAR), szFaceName) ;

The detailed font information is available from GetTextMetrics:

GetTextMetrics (hdc, &textmetric) ;

where textmetric is a variable of type TEXTMETRIC, a structure with 20 fields.

I'll discuss the fields of the LOGFONT and TEXTMETRIC structures in detail shortly. The structures have some similar fields, so they can be confusing. For now, just keep in mind that LOGFONT is for defining a logical font and TEXTMETRIC is for obtaining information about the font currently selected in the device context.

The PICKFONT Program

With the PICKFONT program shown in Figure 17-1, you can define many of the fields of a LOGFONT structure. The program creates a logical font and displays the characteristics of the real font after the logical font has been selected in the screen device context. This is a handy program for understanding how logical fonts are mapped to real fonts.

Figure 17-1
The PICKFONT program.

PICKFONT.C

  /*-----------------------------------------
   PICKFONT.C -- Create Logical Font
                 (c) Charles Petzold, 1998
  -----------------------------------------*/

#include <windows.h>
#include "resource.h"

     // Structure shared between main window and dialog box

typedef struct
{
     int        iDevice, iMapMode ;
     BOOL       fMatchAspect ;
     BOOL       fAdvGraphics ;
     LOGFONT    lf ;
     TEXTMETRIC tm ;
     TCHAR      szFaceName [LF_FULLFACESIZE] ;
}
DLGPARAMS ;

     // Formatting for BCHAR fields of TEXTMETRIC structure

#ifdef UNICODE
#define BCHARFORM TEXT ("0x%04X")
#else
#define BCHARFORM TEXT ("0x%02X")
#endif

     // Global variables

HWND  hdlg ;
TCHAR szAppName[] = TEXT ("PickFont") ;

     // Forward declarations of functions

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
BOOL    CALLBACK DlgProc (HWND, UINT, WPARAM, LPARAM) ;
void SetLogFontFromFields    (HWND hdlg, DLGPARAMS * pdp) ;
void SetFieldsFromTextMetric (HWND hdlg, DLGPARAMS * pdp) ;
void MySetMapMode (HDC hdc, int iMapMode) ;

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PSTR szCmdLine, int iCmdShow)
{
     HWND     hwnd ;
     MSG      msg ;
     WNDCLASS wndclass ;
     
     wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
     wndclass.lpfnWndProc   = WndProc ;
     wndclass.cbClsExtra    = 0 ;
     wndclass.cbWndExtra    = 0 ;
     wndclass.hInstance     = hInstance ;
     wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
     wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
     wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
     wndclass.lpszMenuName  = szAppName ; 
     wndclass.lpszClassName = szAppName ;
     
     if (!RegisterClass (&wndclass))
     {
          MessageBox (NULL, TEXT ("This program requires Windows NT!"),
               szAppName, MB_ICONERROR) ;
          return 0 ;
     }
     
     hwnd = CreateWindow (szAppName, TEXT ("PickFont: Create Logical Font"),
                          WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          NULL, NULL, hInstance, NULL) ;
     
     ShowWindow (hwnd, iCmdShow) ;
     UpdateWindow (hwnd) ;
     
     while (GetMessage (&msg, NULL, 0, 0))
     {
          if (hdlg == 0 || !IsDialogMessage (hdlg, &msg))
          {
               TranslateMessage (&msg) ;
               DispatchMessage (&msg) ;
          }
     }
     return msg.wParam ;
}

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
     static DLGPARAMS dp ;
     static TCHAR     szText[] = TEXT ("\x41\x42\x43\x44\x45 ")
                                 TEXT ("\x61\x62\x63\x64\x65 ")

                                 TEXT ("\xC0\xC1\xC2\xC3\xC4\xC5 ")
                                 TEXT ("\xE0\xE1\xE2\xE3\xE4\xE5 ") 
#ifdef UNICODE
                                 TEXT ("\x0390\x0391\x0392\x0393\x0394\x0395 ")
                                 TEXT ("\x03B0\x03B1\x03B2\x03B3\x03B4\x03B5 ")

                                 TEXT ("\x0410\x0411\x0412\x0413\x0414\x0415 ")
                                 TEXT ("\x0430\x0431\x0432\x0433\x0434\x0435 ")

                                 TEXT ("\x5000\x5001\x5002\x5003\x5004") 
#endif
                                 ;
     HDC              hdc ;
     PAINTSTRUCT      ps ;
     RECT             rect ;
     
     switch (message)
     {
     case WM_CREATE:
          dp.iDevice = IDM_DEVICE_SCREEN ;

          hdlg = CreateDialogParam (((LPCREATESTRUCT) lParam)->hInstance, 
                                    szAppName, hwnd, DlgProc, (LPARAM) &dp) ;
          return 0 ;

     case WM_SETFOCUS:
          SetFocus (hdlg) ;
          return 0 ;

     case WM_COMMAND:
          switch (LOWORD (wParam))
          {
          case IDM_DEVICE_SCREEN:
          case IDM_DEVICE_PRINTER:
               CheckMenuItem (GetMenu (hwnd), dp.iDevice, MF_UNCHECKED) ;
               dp.iDevice = LOWORD (wParam) ;
               CheckMenuItem (GetMenu (hwnd), dp.iDevice, MF_CHECKED) ;

                 SendMessage (hwnd, WM_COMMAND, IDOK, 0) ;
               return 0 ;
          }
          break ;

     case WM_PAINT:
          hdc = BeginPaint (hwnd, &ps) ;

               // Set graphics mode so escapement works in Windows NT

          SetGraphicsMode (hdc, dp.fAdvGraphics ? GM_ADVANCED : GM_COMPATIBLE) ;

               // Set the mapping mode and the mapper flag

          MySetMapMode (hdc, dp.iMapMode) ;
          SetMapperFlags (hdc, dp.fMatchAspect) ;

               // Find the point to begin drawing text

          GetClientRect (hdlg, &rect) ;
          rect.bottom += 1 ;
          DPtoLP (hdc, (PPOINT) &rect, 2) ;

               // Create and select the font; display the text

          SelectObject (hdc, CreateFontIndirect (&dp.lf)) ;
          TextOut (hdc, rect.left, rect.bottom, szText, lstrlen (szText)) ;

          DeleteObject (SelectObject (hdc, GetStockObject (SYSTEM_FONT))) ;
          EndPaint (hwnd, &ps) ;
          return 0 ;
          
     case WM_DESTROY:
          PostQuitMessage (0) ;
          return 0 ;
     }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
}

BOOL CALLBACK DlgProc (HWND hdlg, UINT message, WPARAM wParam, LPARAM lParam)
{
     static DLGPARAMS * pdp ;
     static PRINTDLG    pd = { sizeof (PRINTDLG) } ;
     HDC                hdcDevice ;
     HFONT              hFont ;
     
     switch (message)
     {
     case WM_INITDIALOG:
               // Save pointer to dialog-parameters structure in WndProc

          pdp = (DLGPARAMS *) lParam ;

          SendDlgItemMessage (hdlg, IDC_LF_FACENAME, EM_LIMITTEXT, 
                                    LF_FACESIZE - 1, 0) ;

          CheckRadioButton (hdlg, IDC_OUT_DEFAULT, IDC_OUT_OUTLINE,
                                  IDC_OUT_DEFAULT) ;

          CheckRadioButton (hdlg, IDC_DEFAULT_QUALITY, IDC_PROOF_QUALITY,
                                  IDC_DEFAULT_QUALITY) ;

          CheckRadioButton (hdlg, IDC_DEFAULT_PITCH, IDC_VARIABLE_PITCH,
                                  IDC_DEFAULT_PITCH) ;

          CheckRadioButton (hdlg, IDC_FF_DONTCARE, IDC_FF_DECORATIVE,
                                  IDC_FF_DONTCARE) ;

          CheckRadioButton (hdlg, IDC_MM_TEXT, IDC_MM_LOGTWIPS,
                                  IDC_MM_TEXT) ;

          SendMessage (hdlg, WM_COMMAND, IDOK, 0) ;

                                   // fall through
     case WM_SETFOCUS:
          SetFocus (GetDlgItem (hdlg, IDC_LF_HEIGHT)) ;
          return FALSE ;

     case WM_COMMAND:
          switch (LOWORD (wParam))
          {
          case IDC_CHARSET_HELP:
               MessageBox (hdlg, 
                           TEXT ("0 = Ansi\n")
                           TEXT ("1 = Default\n")
                           TEXT ("2 = Symbol\n")
                           TEXT ("128 = Shift JIS (Japanese)\n")
                           TEXT ("129 = Hangul (Korean)\n")
                           TEXT ("130 = Johab (Korean)\n")
                           TEXT ("134 = GB 2312 (Simplified Chinese)\n")
                           TEXT ("136 = Chinese Big 5 (Tradtional Chinese)\n")
                           TEXT ("177 = Hebrew\n")
                           TEXT ("178 = Arabic\n")

                           TEXT ("161 = Greek\n")
                           TEXT ("162 = Turkish\n")
                           TEXT ("163 = Vietnamese\n")
                           TEXT ("204 = Russian\n")
                           TEXT ("222 = Thai\n")
                           TEXT ("238 = East European\n")
                           TEXT ("255 = OEM"),
                           szAppName, MB_OK | MB_ICONINFORMATION) ;
               return TRUE ;

               // These radio buttons set the lfOutPrecision field

          case IDC_OUT_DEFAULT:   
               pdp->lf.lfOutPrecision = OUT_DEFAULT_PRECIS ;  
               return TRUE ;

          case IDC_OUT_STRING:
               pdp->lf.lfOutPrecision = OUT_STRING_PRECIS ;  
               return TRUE ;

          case IDC_OUT_CHARACTER:
               pdp->lf.lfOutPrecision = OUT_CHARACTER_PRECIS ;  
               return TRUE ;

          case IDC_OUT_STROKE:
               pdp->lf.lfOutPrecision = OUT_STROKE_PRECIS ;  
               return TRUE ;

          case IDC_OUT_TT:
               pdp->lf.lfOutPrecision = OUT_TT_PRECIS ;  
               return TRUE ;

          case IDC_OUT_DEVICE:
               pdp->lf.lfOutPrecision = OUT_DEVICE_PRECIS ;  
               return TRUE ;

          case IDC_OUT_RASTER:
               pdp->lf.lfOutPrecision = OUT_RASTER_PRECIS ;  
               return TRUE ;

          case IDC_OUT_TT_ONLY:
               pdp->lf.lfOutPrecision = OUT_TT_ONLY_PRECIS ;  
               return TRUE ;

          case IDC_OUT_OUTLINE:
               pdp->lf.lfOutPrecision = OUT_OUTLINE_PRECIS ;  
               return TRUE ;

               // These three radio buttons set the lfQuality field

          case IDC_DEFAULT_QUALITY:
               pdp->lf.lfQuality = DEFAULT_QUALITY ;   
               return TRUE ;

          case IDC_DRAFT_QUALITY:
               pdp->lf.lfQuality = DRAFT_QUALITY ;  
               return TRUE ;

          case IDC_PROOF_QUALITY:
               pdp->lf.lfQuality = PROOF_QUALITY ;  
               return TRUE ;

               // These three radio buttons set the lower nibble
               //   of the lfPitchAndFamily field

          case IDC_DEFAULT_PITCH:
               pdp->lf.lfPitchAndFamily = 
                    (0xF0 & pdp->lf.lfPitchAndFamily) | DEFAULT_PITCH ; 
               return TRUE ;

          case IDC_FIXED_PITCH:
               pdp->lf.lfPitchAndFamily = 
                    (0xF0 & pdp->lf.lfPitchAndFamily) | FIXED_PITCH ; 
               return TRUE ;

          case IDC_VARIABLE_PITCH:
               pdp->lf.lfPitchAndFamily = 
                    (0xF0 & pdp->lf.lfPitchAndFamily) | VARIABLE_PITCH ;  
               return TRUE ;

               // These six radio buttons set the upper nibble
               //   of the lfPitchAndFamily field

          case IDC_FF_DONTCARE:
               pdp->lf.lfPitchAndFamily =
                    (0x0F & pdp->lf.lfPitchAndFamily) | FF_DONTCARE ;  
               return TRUE ;

          case IDC_FF_ROMAN:
               pdp->lf.lfPitchAndFamily =
                    (0x0F & pdp->lf.lfPitchAndFamily) | FF_ROMAN;  
               return TRUE ;

          case IDC_FF_SWISS:
               pdp->lf.lfPitchAndFamily =
                    (0x0F & pdp->lf.lfPitchAndFamily) | FF_SWISS ;  
               return TRUE ;

          case IDC_FF_MODERN:
               pdp->lf.lfPitchAndFamily =
                    (0x0F & pdp->lf.lfPitchAndFamily) | FF_MODERN ;  
               return TRUE ;

          case IDC_FF_SCRIPT:
               pdp->lf.lfPitchAndFamily =
                    (0x0F & pdp->lf.lfPitchAndFamily) | FF_SCRIPT ;  
               return TRUE ;

          case IDC_FF_DECORATIVE:
               pdp->lf.lfPitchAndFamily =
                    (0x0F & pdp->lf.lfPitchAndFamily) | FF_DECORATIVE ;  
               return TRUE ;

               // Mapping mode:

          case IDC_MM_TEXT:
          case IDC_MM_LOMETRIC:
          case IDC_MM_HIMETRIC:
          case IDC_MM_LOENGLISH:
          case IDC_MM_HIENGLISH:
          case IDC_MM_TWIPS:
          case IDC_MM_LOGTWIPS:
               pdp->iMapMode = LOWORD (wParam) ;
               return TRUE ;

               // OK button pressed
               // -----------------

          case IDOK:
                    // Get LOGFONT structure

               SetLogFontFromFields (hdlg, pdp) ;

                    // Set Match-Aspect and Advanced Graphics flags

               pdp->fMatchAspect = IsDlgButtonChecked (hdlg, IDC_MATCH_ASPECT) ;
               pdp->fAdvGraphics = IsDlgButtonChecked (hdlg, IDC_ADV_GRAPHICS) ;

                    // Get Information Context

               if (pdp->iDevice == IDM_DEVICE_SCREEN)
               {
                    hdcDevice = CreateIC (TEXT ("DISPLAY"), NULL, NULL, NULL) ;
               }
               else
               {
                    pd.hwndOwner = hdlg ;
                    pd.Flags = PD_RETURNDEFAULT | PD_RETURNIC ;
                    pd.hDevNames = NULL ;
                    pd.hDevMode = NULL ;

                    PrintDlg (&pd) ;

                    hdcDevice = pd.hDC ;
               }
                    // Set the mapping mode and the mapper flag

               MySetMapMode (hdcDevice, pdp->iMapMode) ;
               SetMapperFlags (hdcDevice, pdp->fMatchAspect) ;

                    // Create font and select it into IC

               hFont = CreateFontIndirect (&pdp->lf) ;
               SelectObject (hdcDevice, hFont) ;

                    // Get the text metrics and face name

               GetTextMetrics (hdcDevice, &pdp->tm) ;
               GetTextFace (hdcDevice, LF_FULLFACESIZE, pdp->szFaceName) ;
               DeleteDC (hdcDevice) ;
               DeleteObject (hFont) ;

                    // Update dialog fields and invalidate main window

               SetFieldsFromTextMetric (hdlg, pdp) ;
               InvalidateRect (GetParent (hdlg), NULL, TRUE) ;
               return TRUE ;
          }
          break ;
     }
     return FALSE ;
}
void SetLogFontFromFields (HWND hdlg, DLGPARAMS * pdp)
{
     pdp->lf.lfHeight      = GetDlgItemInt (hdlg, IDC_LF_HEIGHT,  NULL, TRUE) ;
     pdp->lf.lfWidth       = GetDlgItemInt (hdlg, IDC_LF_WIDTH,   NULL, TRUE) ;
     pdp->lf.lfEscapement  = GetDlgItemInt (hdlg, IDC_LF_ESCAPE,  NULL, TRUE) ;
     pdp->lf.lfOrientation = GetDlgItemInt (hdlg, IDC_LF_ORIENT,  NULL, TRUE) ;
     pdp->lf.lfWeight      = GetDlgItemInt (hdlg, IDC_LF_WEIGHT,  NULL, TRUE) ;
     pdp->lf.lfCharSet     = GetDlgItemInt (hdlg, IDC_LF_CHARSET, NULL, FALSE) ;

     pdp->lf.lfItalic = 
                    IsDlgButtonChecked (hdlg, IDC_LF_ITALIC) == BST_CHECKED ;
     pdp->lf.lfUnderline = 
                    IsDlgButtonChecked (hdlg, IDC_LF_UNDER)  == BST_CHECKED ;
     pdp->lf.lfStrikeOut = 
                    IsDlgButtonChecked (hdlg, IDC_LF_STRIKE) == BST_CHECKED ;

     GetDlgItemText (hdlg, IDC_LF_FACENAME, pdp->lf.lfFaceName, LF_FACESIZE) ;
}

void SetFieldsFromTextMetric (HWND hdlg, DLGPARAMS * pdp) 
{
     TCHAR   szBuffer [10] ;
     TCHAR * szYes = TEXT ("Yes") ; 
     TCHAR * szNo  = TEXT ("No") ;
     TCHAR * szFamily [] = { TEXT ("Don't Know"), TEXT ("Roman"),
                             TEXT ("Swiss"),      TEXT ("Modern"),
                             TEXT ("Script"),     TEXT ("Decorative"), 
                             TEXT ("Undefined") } ;

     SetDlgItemInt (hdlg, IDC_TM_HEIGHT,   pdp->tm.tmHeight,           TRUE) ;
     SetDlgItemInt (hdlg, IDC_TM_ASCENT,   pdp->tm.tmAscent,           TRUE) ;
     SetDlgItemInt (hdlg, IDC_TM_DESCENT,  pdp->tm.tmDescent,          TRUE) ;
     SetDlgItemInt (hdlg, IDC_TM_INTLEAD,  pdp->tm.tmInternalLeading,  TRUE) ;
     SetDlgItemInt (hdlg, IDC_TM_EXTLEAD,  pdp->tm.tmExternalLeading,  TRUE) ;
     SetDlgItemInt (hdlg, IDC_TM_AVECHAR,  pdp->tm.tmAveCharWidth,     TRUE) ;
     SetDlgItemInt (hdlg, IDC_TM_MAXCHAR,  pdp->tm.tmMaxCharWidth,     TRUE) ;
     SetDlgItemInt (hdlg, IDC_TM_WEIGHT,   pdp->tm.tmWeight,           TRUE) ;
     SetDlgItemInt (hdlg, IDC_TM_OVERHANG, pdp->tm.tmOverhang,         TRUE) ;
     SetDlgItemInt (hdlg, IDC_TM_DIGASPX,  pdp->tm.tmDigitizedAspectX, TRUE) ;
     SetDlgItemInt (hdlg, IDC_TM_DIGASPY,  pdp->tm.tmDigitizedAspectY, TRUE) ;

     wsprintf (szBuffer, BCHARFORM, pdp->tm.tmFirstChar) ;
     SetDlgItemText (hdlg, IDC_TM_FIRSTCHAR, szBuffer) ;

     wsprintf (szBuffer, BCHARFORM, pdp->tm.tmLastChar) ;
     SetDlgItemText (hdlg, IDC_TM_LASTCHAR, szBuffer) ;

     wsprintf (szBuffer, BCHARFORM, pdp->tm.tmDefaultChar) ;
     SetDlgItemText (hdlg, IDC_TM_DEFCHAR, szBuffer) ;

     wsprintf (szBuffer, BCHARFORM, pdp->tm.tmBreakChar) ;
     SetDlgItemText (hdlg, IDC_TM_BREAKCHAR, szBuffer) ;

     SetDlgItemText (hdlg, IDC_TM_ITALIC, pdp->tm.tmItalic     ? szYes : szNo) ;
     SetDlgItemText (hdlg, IDC_TM_UNDER,  pdp->tm.tmUnderlined ? szYes : szNo) ;
     SetDlgItemText (hdlg, IDC_TM_STRUCK, pdp->tm.tmStruckOut  ? szYes : szNo) ;

     SetDlgItemText (hdlg, IDC_TM_VARIABLE, 
               TMPF_FIXED_PITCH & pdp->tm.tmPitchAndFamily ? szYes : szNo) ;

     SetDlgItemText (hdlg, IDC_TM_VECTOR, 
               TMPF_VECTOR & pdp->tm.tmPitchAndFamily ? szYes : szNo) ;

     SetDlgItemText (hdlg, IDC_TM_TRUETYPE, 
               TMPF_TRUETYPE & pdp->tm.tmPitchAndFamily ? szYes : szNo) ;

     SetDlgItemText (hdlg, IDC_TM_DEVICE, 
               TMPF_DEVICE & pdp->tm.tmPitchAndFamily ? szYes : szNo) ;

     SetDlgItemText (hdlg, IDC_TM_FAMILY, 
               szFamily [min (6, pdp->tm.tmPitchAndFamily >> 4)]) ;

     SetDlgItemInt  (hdlg, IDC_TM_CHARSET,   pdp->tm.tmCharSet, FALSE) ;
     SetDlgItemText (hdlg, IDC_TM_FACENAME, pdp->szFaceName) ;
}

void MySetMapMode (HDC hdc, int iMapMode)
{
     switch (iMapMode)
     {
     case IDC_MM_TEXT:       SetMapMode (hdc, MM_TEXT) ;       break ;
     case IDC_MM_LOMETRIC:   SetMapMode (hdc, MM_LOMETRIC) ;   break ;
     case IDC_MM_HIMETRIC:   SetMapMode (hdc, MM_HIMETRIC) ;   break ;
     case IDC_MM_LOENGLISH:  SetMapMode (hdc, MM_LOENGLISH) ;  break ;
     case IDC_MM_HIENGLISH:  SetMapMode (hdc, MM_HIENGLISH) ;  break ;
     case IDC_MM_TWIPS:      SetMapMode (hdc, MM_TWIPS) ;      break ;
     case IDC_MM_LOGTWIPS:
          SetMapMode (hdc, MM_ANISOTROPIC) ;
          SetWindowExtEx (hdc, 1440, 1440, NULL) ;
          SetViewportExtEx (hdc, GetDeviceCaps (hdc, LOGPIXELSX),
                                 GetDeviceCaps (hdc, LOGPIXELSY), NULL) ;
          break ;
     }
}

PICKFONT.RC

//Microsoft Developer Studio generated resource script.

#include "resource.h"
#include "afxres.h"

/////////////////////////////////////////////////////////////////////////////
// Dialog

PICKFONT DIALOG DISCARDABLE  0, 0, 348, 308
STYLE WS_CHILD | WS_VISIBLE | WS_BORDER
FONT 8, "MS Sans Serif"
BEGIN
    LTEXT           "&Height:",IDC_STATIC,8,10,44,8
    EDITTEXT        IDC_LF_HEIGHT,64,8,24,12,ES_AUTOHSCROLL
    LTEXT           "&Width",IDC_STATIC,8,26,44,8
    EDITTEXT        IDC_LF_WIDTH,64,24,24,12,ES_AUTOHSCROLL
    LTEXT           "Escapement:",IDC_STATIC,8,42,44,8
    EDITTEXT        IDC_LF_ESCAPE,64,40,24,12,ES_AUTOHSCROLL
    LTEXT           "Orientation:",IDC_STATIC,8,58,44,8
    EDITTEXT        IDC_LF_ORIENT,64,56,24,12,ES_AUTOHSCROLL
    LTEXT           "Weight:",IDC_STATIC,8,74,44,8
    EDITTEXT        IDC_LF_WEIGHT,64,74,24,12,ES_AUTOHSCROLL
    GROUPBOX        "Mapping Mode",IDC_STATIC,97,3,96,90,WS_GROUP
    CONTROL         "Text",IDC_MM_TEXT,"Button",BS_AUTORADIOBUTTON,104,13,56,
                    8
    CONTROL         "Low Metric",IDC_MM_LOMETRIC,"Button",BS_AUTORADIOBUTTON,
                    104,24,56,8
    CONTROL         "High Metric",IDC_MM_HIMETRIC,"Button",
                    BS_AUTORADIOBUTTON,104,35,56,8
    CONTROL         "Low English",IDC_MM_LOENGLISH,"Button",
                    BS_AUTORADIOBUTTON,104,46,56,8
    CONTROL         "High English",IDC_MM_HIENGLISH,"Button",
                    BS_AUTORADIOBUTTON,104,57,56,8
    CONTROL         "Twips",IDC_MM_TWIPS,"Button",BS_AUTORADIOBUTTON,104,68,
                    56,8
    CONTROL         "Logical Twips",IDC_MM_LOGTWIPS,"Button",
                    BS_AUTORADIOBUTTON,104,79,64,8
    CONTROL         "Italic",IDC_LF_ITALIC,"Button",BS_AUTOCHECKBOX | 
                    WS_TABSTOP,8,90,48,12
    CONTROL         "Underline",IDC_LF_UNDER,"Button",BS_AUTOCHECKBOX | 
                    WS_TABSTOP,8,104,48,12
    CONTROL         "Strike Out",IDC_LF_STRIKE,"Button",BS_AUTOCHECKBOX | 
                    WS_TABSTOP,8,118,48,12
    CONTROL         "Match Aspect",IDC_MATCH_ASPECT,"Button",BS_AUTOCHECKBOX | 
                    WS_TABSTOP,60,104,62,8
    CONTROL         "Adv Grfx Mode",IDC_ADV_GRAPHICS,"Button",
                    BS_AUTOCHECKBOX | WS_TABSTOP,60,118,62,8
    LTEXT           "Character Set:",IDC_STATIC,8,137,46,8
    EDITTEXT        IDC_LF_CHARSET,58,135,24,12,ES_AUTOHSCROLL
    PUSHBUTTON      "?",IDC_CHARSET_HELP,90,135,14,14
    GROUPBOX        "Quality",IDC_STATIC,132,98,62,48,WS_GROUP
    CONTROL         "Default",IDC_DEFAULT_QUALITY,"Button",
                    BS_AUTORADIOBUTTON,136,110,40,8
    CONTROL         "Draft",IDC_DRAFT_QUALITY,"Button",BS_AUTORADIOBUTTON,
                    136,122,40,8
    CONTROL         "Proof",IDC_PROOF_QUALITY,"Button",BS_AUTORADIOBUTTON,
                    136,134,40,8
    LTEXT           "Face Name:",IDC_STATIC,8,154,44,8
    EDITTEXT        IDC_LF_FACENAME,58,152,136,12,ES_AUTOHSCROLL
    GROUPBOX        "Output Precision",IDC_STATIC,8,166,118,133,WS_GROUP
    CONTROL         "OUT_DEFAULT_PRECIS",IDC_OUT_DEFAULT,"Button",
                    BS_AUTORADIOBUTTON,12,178,112,8
    CONTROL         "OUT_STRING_PRECIS",IDC_OUT_STRING,"Button",
                    BS_AUTORADIOBUTTON,12,191,112,8
    CONTROL         "OUT_CHARACTER_PRECIS",IDC_OUT_CHARACTER,"Button",
                    BS_AUTORADIOBUTTON,12,204,112,8
    CONTROL         "OUT_STROKE_PRECIS",IDC_OUT_STROKE,"Button",
                    BS_AUTORADIOBUTTON,12,217,112,8
    CONTROL         "OUT_TT_PRECIS",IDC_OUT_TT,"Button",BS_AUTORADIOBUTTON,
                    12,230,112,8
    CONTROL         "OUT_DEVICE_PRECIS",IDC_OUT_DEVICE,"Button",
                    BS_AUTORADIOBUTTON,12,243,112,8
    CONTROL         "OUT_RASTER_PRECIS",IDC_OUT_RASTER,"Button",
                    BS_AUTORADIOBUTTON,12,256,112,8
    CONTROL         "OUT_TT_ONLY_PRECIS",IDC_OUT_TT_ONLY,"Button",
                    BS_AUTORADIOBUTTON,12,269,112,8
    CONTROL         "OUT_OUTLINE_PRECIS",IDC_OUT_OUTLINE,"Button",
                    BS_AUTORADIOBUTTON,12,282,112,8
    GROUPBOX        "Pitch",IDC_STATIC,132,166,62,50,WS_GROUP
    CONTROL         "Default",IDC_DEFAULT_PITCH,"Button",BS_AUTORADIOBUTTON,
                    137,176,52,8
    CONTROL         "Fixed",IDC_FIXED_PITCH,"Button",BS_AUTORADIOBUTTON,137,
                    189,52,8
    CONTROL         "Variable",IDC_VARIABLE_PITCH,"Button",
                    BS_AUTORADIOBUTTON,137,203,52,8
    GROUPBOX        "Family",IDC_STATIC,132,218,62,82,WS_GROUP
    CONTROL         "Don't Care",IDC_FF_DONTCARE,"Button",BS_AUTORADIOBUTTON,
                    137,229,52,8
    CONTROL         "Roman",IDC_FF_ROMAN,"Button",BS_AUTORADIOBUTTON,137,241,
                    52,8
    CONTROL         "Swiss",IDC_FF_SWISS,"Button",BS_AUTORADIOBUTTON,137,253,
                    52,8
    CONTROL         "Modern",IDC_FF_MODERN,"Button",BS_AUTORADIOBUTTON,137,
                    265,52,8
    CONTROL         "Script",IDC_FF_SCRIPT,"Button",BS_AUTORADIOBUTTON,137,
                    277,52,8
    CONTROL         "Decorative",IDC_FF_DECORATIVE,"Button",
                    BS_AUTORADIOBUTTON,137,289,52,8
    DEFPUSHBUTTON   "OK",IDOK,247,286,50,14
    GROUPBOX        "Text Metrics",IDC_STATIC,201,2,140,272,WS_GROUP
    LTEXT           "Height:",IDC_STATIC,207,12,64,8
    LTEXT           "0",IDC_TM_HEIGHT,281,12,44,8
    LTEXT           "Ascent:",IDC_STATIC,207,22,64,8
    LTEXT           "0",IDC_TM_ASCENT,281,22,44,8
    LTEXT           "Descent:",IDC_STATIC,207,32,64,8
    LTEXT           "0",IDC_TM_DESCENT,281,32,44,8
    LTEXT           "Internal Leading:",IDC_STATIC,207,42,64,8
    LTEXT           "0",IDC_TM_INTLEAD,281,42,44,8
    LTEXT           "External Leading:",IDC_STATIC,207,52,64,8
    LTEXT           "0",IDC_TM_EXTLEAD,281,52,44,8
    LTEXT           "Ave Char Width:",IDC_STATIC,207,62,64,8
    LTEXT           "0",IDC_TM_AVECHAR,281,62,44,8
    LTEXT           "Max Char Width:",IDC_STATIC,207,72,64,8
    LTEXT           "0",IDC_TM_MAXCHAR,281,72,44,8
    LTEXT           "Weight:",IDC_STATIC,207,82,64,8
    LTEXT           "0",IDC_TM_WEIGHT,281,82,44,8
    LTEXT           "Overhang:",IDC_STATIC,207,92,64,8
    LTEXT           "0",IDC_TM_OVERHANG,281,92,44,8
    LTEXT           "Digitized Aspect X:",IDC_STATIC,207,102,64,8
    LTEXT           "0",IDC_TM_DIGASPX,281,102,44,8
    LTEXT           "Digitized Aspect Y:",IDC_STATIC,207,112,64,8
    LTEXT           "0",IDC_TM_DIGASPY,281,112,44,8
    LTEXT           "First Char:",IDC_STATIC,207,122,64,8
    LTEXT           "0",IDC_TM_FIRSTCHAR,281,122,44,8
    LTEXT           "Last Char:",IDC_STATIC,207,132,64,8
    LTEXT           "0",IDC_TM_LASTCHAR,281,132,44,8
    LTEXT           "Default Char:",IDC_STATIC,207,142,64,8
    LTEXT           "0",IDC_TM_DEFCHAR,281,142,44,8
    LTEXT           "Break Char:",IDC_STATIC,207,152,64,8
    LTEXT           "0",IDC_TM_BREAKCHAR,281,152,44,8
    LTEXT           "Italic?",IDC_STATIC,207,162,64,8
    LTEXT           "0",IDC_TM_ITALIC,281,162,44,8
    LTEXT           "Underlined?",IDC_STATIC,207,172,64,8
    LTEXT           "0",IDC_TM_UNDER,281,172,44,8
    LTEXT           "Struck Out?",IDC_STATIC,207,182,64,8
    LTEXT           "0",IDC_TM_STRUCK,281,182,44,8
    LTEXT           "Variable Pitch?",IDC_STATIC,207,192,64,8
    LTEXT           "0",IDC_TM_VARIABLE,281,192,44,8
    LTEXT           "Vector Font?",IDC_STATIC,207,202,64,8
    LTEXT           "0",IDC_TM_VECTOR,281,202,44,8
    LTEXT           "TrueType Font?",IDC_STATIC,207,212,64,8
    LTEXT           "0",IDC_TM_TRUETYPE,281,212,44,8
    LTEXT           "Device Font?",IDC_STATIC,207,222,64,8
    LTEXT           "0",IDC_TM_DEVICE,281,222,44,8
    LTEXT           "Family:",IDC_STATIC,207,232,64,8
    LTEXT           "0",IDC_TM_FAMILY,281,232,44,8
    LTEXT           "Charecter Set:",IDC_STATIC,207,242,64,8
    LTEXT           "0",IDC_TM_CHARSET,281,242,44,8
    LTEXT           "0",IDC_TM_FACENAME,207,262,128,8
END

/////////////////////////////////////////////////////////////////////////////
// Menu

PICKFONT MENU DISCARDABLE 
BEGIN
    POPUP "&Device"
    BEGIN
        MENUITEM "&Screen",                     IDM_DEVICE_SCREEN, CHECKED
        MENUITEM "&Printer",                    IDM_DEVICE_PRINTER
    END
END

RESOURCE.H

// Microsoft Developer Studio generated include file.
// Used by PickFont.rc

#define IDC_LF_HEIGHT                   1000
#define IDC_LF_WIDTH                    1001
#define IDC_LF_ESCAPE                   1002
#define IDC_LF_ORIENT                   1003
#define IDC_LF_WEIGHT                   1004
#define IDC_MM_TEXT                     1005
#define IDC_MM_LOMETRIC                 1006
#define IDC_MM_HIMETRIC                 1007
#define IDC_MM_LOENGLISH                1008
#define IDC_MM_HIENGLISH                1009
#define IDC_MM_TWIPS                    1010
#define IDC_MM_LOGTWIPS                 1011
#define IDC_LF_ITALIC                   1012
#define IDC_LF_UNDER                    1013
#define IDC_LF_STRIKE                   1014
#define IDC_MATCH_ASPECT                1015
#define IDC_ADV_GRAPHICS                1016
#define IDC_LF_CHARSET                  1017
#define IDC_CHARSET_HELP                1018
#define IDC_DEFAULT_QUALITY             1019
#define IDC_DRAFT_QUALITY               1020
#define IDC_PROOF_QUALITY               1021
#define IDC_LF_FACENAME                 1022
#define IDC_OUT_DEFAULT                 1023
#define IDC_OUT_STRING                  1024
#define IDC_OUT_CHARACTER               1025
#define IDC_OUT_STROKE                  1026
#define IDC_OUT_TT                      1027
#define IDC_OUT_DEVICE                  1028
#define IDC_OUT_RASTER                  1029
#define IDC_OUT_TT_ONLY                 1030
#define IDC_OUT_OUTLINE                 1031
#define IDC_DEFAULT_PITCH               1032
#define IDC_FIXED_PITCH                 1033
#define IDC_VARIABLE_PITCH              1034
#define IDC_FF_DONTCARE                 1035
#define IDC_FF_ROMAN                    1036
#define IDC_FF_SWISS                    1037
#define IDC_FF_MODERN                   1038
#define IDC_FF_SCRIPT                   1039
#define IDC_FF_DECORATIVE               1040
#define IDC_TM_HEIGHT                   1041
#define IDC_TM_ASCENT                   1042
#define IDC_TM_DESCENT                  1043
#define IDC_TM_INTLEAD                  1044
#define IDC_TM_EXTLEAD                  1045
#define IDC_TM_AVECHAR                  1046
#define IDC_TM_MAXCHAR                  1047
#define IDC_TM_WEIGHT                   1048
#define IDC_TM_OVERHANG                 1049
#define IDC_TM_DIGASPX                  1050
#define IDC_TM_DIGASPY                  1051
#define IDC_TM_FIRSTCHAR                1052
#define IDC_TM_LASTCHAR                 1053
#define IDC_TM_DEFCHAR                  1054
#define IDC_TM_BREAKCHAR                1055
#define IDC_TM_ITALIC                   1056
#define IDC_TM_UNDER                    1057
#define IDC_TM_STRUCK                   1058
#define IDC_TM_VARIABLE                 1059
#define IDC_TM_VECTOR                   1060
#define IDC_TM_TRUETYPE                 1061
#define IDC_TM_DEVICE                   1062
#define IDC_TM_FAMILY                   1063
#define IDC_TM_CHARSET                  1064
#define IDC_TM_FACENAME                 1065
#define IDM_DEVICE_SCREEN               40001
#define IDM_DEVICE_PRINTER              40002

Figure 17-2 shows a typical PICKFONT screen. The left side of the PICKFONT display is a modeless dialog box that allows you to select most of the fields of the logical font structure. The right side of the dialog box shows the results of GetTextMetrics after the font is selected in the device context. Below the dialog box, the program displays a string of characters using this font. Because the modeless dialog box is so big, you're best off running this program on a display size of 1024 by 768 or larger.

Click to view graphic
Click to view graphic (22 KB)

Figure 17-2.
A typical PICKFONT display (Unicode version under Windows NT).

The modeless dialog box also contains some options that are not part of the logical font structure. These are the mapping mode, including my Logical Twips mode; the Match Aspect option, which changes the way Windows matches a logical font to a real font; and "Adv Grfx Mode," which sets the advanced graphics mode in Windows NT. I'll discuss these in more detail shortly.

From the Device menu you can select the default printer rather than the video display. In this case, PICKFONT selects the logical font into the printer device context and displays the TEXTMETRIC structure from the printer. The program then selects the logical font into the window device context for displaying the sample string. Thus, the text displayed by the program might use a different font (a screen font) than the font described by the list of the TEXTMETRIC fields (which is a printer font).

Much of the PICKFONT program contains the logic necessary to maintain the dialog box, so I won't go into detail on the workings of the program. Instead, I'll explain what you're doing when you create and select a logical font.

The Logical Font Structure

To create a logical font, you can call CreateFont, a function that has 14 arguments. Generally, it's easier to define a structure of type LOGFONT,

LOGFONT lf ;

and then define the fields of this structure. When finish, you call CreateFontIndirect with a pointer to the structure:

hFont = CreatFontIndirect (&lf) ;

You don't need to set each and every field of the LOGFONT structure. If your logical font structure is defined as a static variable, all the fields will be initialized to 0. The 0 values are generally defaults. You can then use that structure directly without any changes, and CreateFontIndirect will return a handle to a font. When you select that font into the device context, you'll get a reasonable default font. You can be as specific or as vague as you want in the LOGFONT structure, and the Windows will attempt to match your requests with a real font.

As I discuss each field of the LOGFONT structure, you may want to test them out using the PICKFONT program. Be sure to press Enter or the OK button when you want the program to use any fields you've entered.

The first two fields of the LOGFONT structure are in logical units, so they depend on the current setting of the mapping mode:

  • lfHeight This is the desired height of the characters (including internal leading but not external leading) in logical units. You can set lfHeight to 0 for a default size, or you can set it to a positive or negative value depending on what you want the field to represent. If you set lfHeightW to a positive value, you're implying that you want this value to be a height that includes internal leading. In effect, you're really requesting a font that is appropriate for a line spacing of lfHeight. If you set lfHeight to a negative value, Windows treats the absolute value of that number as a desired font height compatible with the point size. This is an important distinction: If you want a font of a particular point size, convert that point size to logical units and set the lfHeight field to the negative of that value. If lfHeight is positive, the tmHeight field of the resultant TEXTMETRIC structure will be roughly that value. (It's sometimes a little off, probably because of rounding.) If lfHeight is negative, it will roughly match the tmHeight field of the TEXTMETRIC structure less the tmInternalLeading field.

  • lfWidth This is the desired width of the characters in logical units. In most cases, you'll want to set this value to 0 and let Windows choose a font based solely on the height. Using a nonzero value does not work well with raster fonts, but with TrueType fonts you can easily use this to get a font that has wider or slimmer characters than normal. This field corresponds to the tmAveCharWidth field of the TEXTMETRIC structure. To use the lfWidth field intelligently, first set up the LOGFONT structure with a lfWidth field set to zero, create the logical font, select it into a device context, and then call GetTextMetrics. Get the tmAveCharWidth field, adjust it up or down, probably by a percentage, and then create a second font using that adjusted tmAveCharWidth value for lfWidth.

The next two fields specify the "escapement" and "orientation" of the text. In theory, lfEscapement allows character strings to be written at an angle (but with the baseline of each character still parallel to the horizontal axis) and lfOrientation allows individual characters to be tilted. These fields have never quite worked as advertised, and even today they don't work as they should except in one case: you're using a TrueType font, you're running Windows NT, and you call SetGraphicsMode with the CM_ADVANCED flag set. You can accomplish the final requirement in PICKFONT by checking the "Adv Grfx Mode" check box.

To experiment with these fields in PICKFONT, be aware that the units are in tenths of a degree and indicate a counterclockwise rotation. It's easy to enter values that cause the sample text string to disappear! For this reason, use values between 0 and -600 (or so) or values between 3000 and 3600.

  • lfEscapement This is an angle in tenths of a degree, measured from the horizontal in a counterclockwise direction. It specifies how the successive characters of a string are placed when you write text. Here are some examples:

    Value Placement of Characters
    0Run from left to right (default)
    900Go up
    1800Run from right to left
    2700Go down

    In Windows 98, this value sets both the escapement and orientation of TrueType text. In Windows NT, this value also normally sets both the escapement and orientation of TrueType text, except when you call SetGraphicsMode with the GM_ADVANCED argument, in which case it works as documented.

  • lfOrientation This is an angle in tenths of a degree, measured from the horizontal in a counterclockwise direction. It affects the appearance of each individual character. Here are some examples:

    Value Character Appearance
    0Normal (default)
    900Tipped 90 degrees to the right
    1800Upside down
    2700Tipped 90 degrees to the left

    This field has no effect except with a TrueType font under Windows NT with the graphics mode set to GM_ADVANCED, in which case it works as documented.

    The remaining 10 fields follow:

  • lfWeight This field allows you to specify boldface. The WINGDI.H header files defines a bunch of values to use with this field:

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

    In reality, this table is much more ambitious than anything that was ever implemented. You can use 0 or 400 for normal and 700 for bold.

  • lfItalic When nonzero, this specifies italics. Windows can synthesize italics on GDI raster fonts. That is, Windows simply shifts some rows of the character bitmap to mimic italic. With TrueType fonts, Windows uses the actual italic or oblique versions of the fonts.

  • lfUnderline When nonzero, this specifies underlining, which is always synthesized on GDI fonts. That is, the Windows GDI simply draws a line under each character, including spaces.

  • lfStrikeOut When nonzero, this specifies that the font should have a line drawn through the characters. This is also synthesized on GDI fonts.

  • lfCharSet This is a byte value that specifies the character set of the font. I'll have more to say about this field in the upcoming section, "Character Sets and Unicode",. In PICKFONT, you can press the button with the question mark to obtain a list of the character set codes you can use.

    Notice that the lfCharSet field is the only field where a zero does not indicate a default value. A zero value is equivalent to ANSI_CHARSET, the ANSI character set used in the United States and Western Europe. The DEFAULT_CHARSET code, which equals 1, indicates the default character set for the machine on which the program is running.

  • lfOutPrecision This specifies how Windows should attempt to match the desired font sizes and characteristics with actual fonts. It's a rather complex field that you probably won't use much. Check the documentation of the LOGFONT structure for more detail. Note that you can use the OUT_TT_ONLY_PRECIS flag to ensure that you always get a TrueType font.

  • lfClipPrecision This field specifies how characters are to be clipped when they lie partially outside the clipping region. This field is not used much and is not implemented in the PICKFONT program.

  • lfQuality This is an instruction to Windows regarding the matching of a desired font with a real font. It really has meaning with raster fonts only and should not affect TrueType fonts. The DRAFT_QUALITY flag indicates that you want GDI to scale raster fonts to achieve the size you want; the PROOF_QUALITY flag indicates no scaling should be done. The PROOF_QUALITY fonts are the most attractive, but they might be smaller than what you request. You'll probably use DEFAULT_QUALITY (or 0) in this field.

  • lfPitchAndFamily This byte is composed of two parts. You can use the C bitwise OR operator to combine two identifiers for this field. The lowest two bits specify whether the font has a fixed pitch (that is, all characters are the same width) or a variable pitch:

    ValueIdentifier
    0DEFAULT_PITCH
    1FIXED_PITCH
    2VARIABLE_PITCH

    The upper half of this byte specifies the font family:

    ValueIdentifier
    0x00FW_DONTCARE
    0x10FF_ROMAN (variable widths, serifs)
    0x20FF_SWISS (variable widths, no serifs)
    0x30FF_MODERN (fixed pitch)
    0x40FF_SCRIPT (mimics handwriting)
    0x50FF_DECORATIVE

  • lfFaceName This is the actual text name of a typeface (such as Courier, Arial, or Times New Roman). This field is a byte array that is LF_FACESIZE (or 32 characters) wide. If you want a TrueType italic or boldface font, you can get it in one of two ways. You can use the complete typeface name (such as Times New Roman Italic) in the lfFaceName field, or you can use the base name (Times New Roman) and set the lfItalic field.

The Font-Mapping Algorithm

After you set up the logical font structure, you call CreateFontIndirect to get a handle to the logical font. When you call SelectObject to select that logical font into a device context, Windows finds the real font that most closely matches the request. In doing so, it uses a "font-mapping algorithm." Certain fields of the structure are more important than other fields.

The best way to get a feel for font mapping is to spend some time experimenting with PICKFONT. Here are some general guidelines:

  • The lfCharSet (character set) field is very important. It used to be that if you specified OEM_CHARSET (255), you'd get either one of the stroke fonts or the Terminal font because these are the only fonts that used the OEM character sets. However, with the advent of TrueType "Big Fonts", a single TrueType font can be mapped to different character sets, including the OEM character set. You'll need to use SYMBOL_CHARSET (2) to get the Symbol font or the Wingdings font.

  • A pitch value of FIXED_PITCH in the lfPitchAndFamily field is important, because you are in effect telling Windows that you don't want to deal with a variable-width font.

  • The lfFaceName field is important, because you're being specific about the typeface of the font that you want. If you leave lfFaceName set to NULL and set the family value in the lfPitchAndFamily field to a value other than FF_DONTCARE, that field becomes important because you're being specific about the font family.

  • For raster fonts, Windows will attempt to match the lfHeight value even if it needs to increase the size of a smaller font. The height of the actual font will always be less than or equal to that of the requested font unless there is no font small enough to satisfy your request. For stroke or TrueType fonts, Windows will simply scale the font to the desired height.

  • You can prevent Windows from scaling a raster font by setting lfQuality to PROOF_QUALITY. By doing so, you're telling Windows that the requested height of the font is less important than the appearance of the font.

  • If you specify lfHeight and lfWeight values that are out of line for the particular aspect ratio of the display, Windows can map to a raster font that is designed for a display or other device of a different aspect ratio. This used to be a trick to get to get a thin or thick font. (This is not really necessary with TrueType, of course.) In general, you'll probably want to avoid matching with a font for another device, which you can do in PICKFONT by clicking the check box marked Match Aspect. If this box is checked, PICKFONT makes a call to SetMapperFlags with a TRUE argument.

Finding Out About the Font

At the right side of the modeless dialog box in PICKFONT is the information obtained from the GetTextMetrics function after the font has been selected in a device context. (Notice that you can use PICKFONT's device menu to indicate whether you want this device context to be the screen or the default printer. The results might be different because different fonts might be available on the printer.) At the bottom of the list in PICKFONT is the typeface name available from GetTextFace.

All the size values that Windows copies into the TEXTMETRIC structure are in logical units except for the digitized aspect ratios. The fields of the TEXTMETRIC structure are as follows:

  • tmHeight The height of the character in logical units. This is the value that should approximate the lfHeight field specified in the LOGFONT structure, if that value was positive, in which case it represents the line spacing of the font rather than the point size. If the lfHeight field of the LOGFONT structure was negative, the tmHeight field minus the tmInternalLeading field should approximate the absolute value of the lfHeight field.

  • tmAscent The vertical size of the character above the baseline in logical units.

  • tmDescent The vertical size of the character below the baseline in logical units.

  • tmInternalLeading A vertical size included in the tmHeight value that is usually occupied by diacritics on some capital letters. Once again, you can calculate the point size of the font by subtracting the tmInternalLeading value from the tmHeight value.

  • tmExternalLeading An additional amount of line spacing beyond tmHeight recommended by the designer of the font for spacing successive lines of text.

  • tmAveCharWidth The average width of lowercase letters in the font.

  • tmMaxCharWidth The width of the widest character in logical units. For a fixed-pitch font, this value is the same as tmAveCharWidth.

  • tmWeight The weight of the font ranging from 0 through 999. In reality, the field will be 400 for a normal font and 700 for a boldface font.

  • tmOverhang The amount of extra width (in logical units) that Windows adds to a raster font character when synthesizing italic or boldface. When a raster font is italicized, the tmAveCharWidth value remains unchanged, because a string of italicized characters has the same overall width as the same string of normal characters. For boldfacing, Windows must slightly expand the width of each character. For a boldface font, the tmAveCharWidth value less the tmOverhang value equals the tmAveCharWidth value for the same font without boldfacing.

  • tmDigitizedAspectX and tmDigitizedAspectY The aspect ratio for which the font is appropriate. These are equivalent to values obtained from GetDeviceCaps with the LOGPIXELSX and LOGPIXELSY identifiers.

  • tmFirstChar The character code of the first character in the font.

  • tmLastChar The character code of the last character in the font. If the TEXTMETRIC structure is obtained by a call to GetTextMetricsW (the wide character version of the function), then this value might be greater than 255.

  • tmDefaultChar The character code that Windows uses to display characters that are not in the font.

  • tmBreakChar The character that Windows, and your programs, should use to determine word breaks when justifying text. Unless you're using something bizarre (such as an EBCDIC font), this will be 32—the space character.

  • tmItalic Nonzero for an italic font.

  • tmUnderlined Nonzero for an underlined font.

  • tmStruckOut Nonzero for a strikethrough font.

  • tmPitchAndFamily The four low-order bits are flags that indicate some characteristics about the font, indicated by the following identifiers defined in WINGDI.H:

    ValueIdentifier
    0x01TMPF_FIXED_PITCH
    0x02TMPF_VECTOR
    0x04TMPF_TRUETYPE
    0x08TMPF_DEVICE

    Despite the name of the TMPF_FIXED_PITCH flag, the lowest bit is 1 if the font characters have a variable pitch. The second lowest bit (TMPF_VECTOR) will be 1 for TrueType fonts and fonts that use other scalable outline technologies, such as PostScript. The TMPF_DEVICE flag indicates a device font (that is, a font built into a printer) rather than a GDI-based font.

    The top four bits of this field indicates the font family, which are the same values used in the LOGFONT lfPitchAndFamily field.

  • tmCharSet The character set identifier.


TOP  |   PREVIOUS PAGE   |   NEXT PAGE

Visit Microsoft Press for more information on Programming Windows, Fifth Ed.

Top of Page


Last Updated: Friday, July 6, 2001