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 as 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.
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.
In this chapter, I'll use the first and third approaches.
Here is the process for creating, selecting, and deleting logical 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.
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 a 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 (Traditional 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 "Character 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.
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.
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 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:
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.
Value | Placement of Characters |
0 | Run from left to right (default) |
900 | Go up |
1800 | Run from right to left |
2700 | Go 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.
Value | Character Appearance |
0 | Normal (default) |
900 | Tipped 90 degrees to the right |
1800 | Upside down |
2700 | Tipped 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:
Value | Identifier |
0 | FW_DONTCARE |
100 | FW_THIN |
200 | FW_EXTRALIGHT or FW_ULTRALIGHT |
300 | FW_LIGHT |
400 | FW_NORMAL or FW_REGULAR |
500 | FW_MEDIUM |
600 | FW_SEMIBOLD or FW_DEMIBOLD |
700 | FW_BOLD |
800 | FW_EXTRABOLD or FW_ULTRABOLD |
900 | FW_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.
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.
Value | Identifier |
0 | DEFAULT_PITCH |
1 | FIXED_PITCH |
2 | VARIABLE_PITCH |
The upper half of this byte specifies the font family:
Value | Identifier |
0x00 | FW_DONTCARE |
0x10 | FF_ROMAN (variable widths, serifs) |
0x20 | FF_SWISS (variable widths, no serifs) |
0x30 | FF_MODERN (fixed pitch) |
0x40 | FF_SCRIPT (mimics handwriting) |
0x50 | FF_DECORATIVE |
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 considered 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:
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:
Value | Identifier |
0x01 | TMPF_FIXED_PITCH |
0x02 | TMPF_VECTOR |
0x04 | TMPF_TRUETYPE |
0x08 | TMPF_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 scaleable 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 indicate the font family and are the same values used in the LOGFONT lfPitchAndFamily field.
I discussed the concept of the Windows character set in Chapter 6, where we had to deal with international issues involving the keyboard. In the LOGFONT and TEXTMETRIC structures, the character set of the desired font (or the actual font) is indicated by a one-byte number between 0 and 255. The character set identifiers are defined in WINGDI.H like so:
#define ANSI_CHARSET 0 #define DEFAULT_CHARSET 1 #define SYMBOL_CHARSET 2 #define MAC_CHARSET 77 #define SHIFTJIS_CHARSET 128 #define HANGEUL_CHARSET 129 #define HANGUL_CHARSET 129 #define JOHAB_CHARSET 130 #define GB2312_CHARSET 134 #define CHINESEBIG5_CHARSET 136 #define GREEK_CHARSET 161 #define TURKISH_CHARSET 162 #define VIETNAMESE_CHARSET 163 #define HEBREW_CHARSET 177 #define ARABIC_CHARSET 178 #define BALTIC_CHARSET 186 #define RUSSIAN_CHARSET 204 #define THAI_CHARSET 222 #define EASTEUROPE_CHARSET 238 #define OEM_CHARSET 255
The character set is similar in concept to the code page, but the character set is specific to Windows and is always less than or equal to 255.
As with all of the programs in this book, you can compile PICKFONT both with and without the UNICODE identifier defined. As usual, on the companion disc, the two versions of the program are located in the DEBUG and RELEASE directories, respectively.
Notice that the character string that PICKFONT displays towards the bottom of its window is longer in the Unicode version of the program. In both versions, the character string begins with the character codes 0x40 through 0x45 and 0x60 through 0x65. Regardless of the character set you choose (except for SYMBOL_CHARSET), these character codes will display as the first five uppercase and lowercase letters of the Latin alphabet (that is, A through E and a through e).
When running the non-Unicode version of the PICKFONT program, the next 12 characters—the character codes 0xC0 through 0xC5 and 0xE0 through 0xE5—will be dependent upon the character set you choose. For ANSI_CHARSET, these character codes correspond to accented versions of the uppercase and lowercase letter A. For GREEK_CHARSET, these codes will correspond to letters of the Greek alphabet. For RUSSIAN_CHARSET, they will be letters of the Cyrillic alphabet. Notice that the font might change when you select one of these character sets. This is because a raster font might not have these characters, but a TrueType font probably will. You'll recall that most TrueType fonts are "Big Fonts" and include characters for several different character sets. If you're running a Far Eastern version of Windows, these characters will be interpreted as double-byte characters and will display as ideographs rather than letters.
When running the Unicode version of PICKFONT under Windows NT, the codes 0xC0 through 0xC5 and 0xE0 through 0xE5 will always (except for SYMBOL_CHARSET) be accented versions of the uppercase and lowercase letter A because that's how these codes are defined in Unicode. The program also displays character codes 0x0390 through 0x0395 and 0x03B0 through 0x03B5. Because of their definition in Unicode, these codes will always correspond to letters of the Greek alphabet. Similarly the program displays character codes 0x0410 through 0x0415 and 0x0430 through 0x0435, which always correspond to letters in the Cyrillic alphabet. However, note that these characters might not be present in a default font. You may have to select the GREEK_CHARSET or RUSSIAN_CHARSET to get them. In this case, the character set ID in the LOGFONT structure doesn't change the actual character set; the character set is always Unicode. The character set ID instead indicates that characters from this character set are desired.
Now select HEBREW_CHARSET (code 177). The Hebrew alphabet is not included in Windows' usual Big Fonts, so the operating system picks Lucida Sans Unicode, as you can verify at the bottom right corner of the modeless dialog box.
PICKFONT also displays character codes 0x5000 through 0x5004, which correspond to a few of the many Chinese, Japanese, and Korean ideographs. You'll see these if you're running a Far Eastern version of Windows, or you can download a free Unicode font that is more extensive than Lucida Sans Unicode. This is the Bitstream CyberBit font, available at http://www.bitstream.com/products/world/cyberbits. (Just to give you an idea of the difference, Lucida Sans Unicode is roughly 300K while Bitstream CyberBit is about 13 megabytes.) If you have this font installed, Windows will select it if you want a character set not supported by Lucida Sans Unicode, such as SHIFTJIS_CHARSET (Japanese), HANGUL_CHARSET (Korean), JOHAB_CHARSET (Korean), GB2312_CHARSET (Simplified Chinese), or CHINESEBIG5_CHARSET (Traditional Chinese).
I'll present a program that lets you view all the characters of a Unicode font later in this chapter.
The introduction of TrueType—and its basis in traditional typography—has provided Windows with a solid foundation for displaying text in its many varieties. However, some of the Windows font-selection functions are based on older technology, in which raster fonts on the screen had to approximate printer device fonts. In the next section, I'll describe font enumeration, which lets a program obtain a list of all the fonts available on the video display or printer. However, the ChooseFont dialog box (to be discussed shortly) largely eliminates the necessity for font enumeration by a program.
Because the standard TrueType fonts are available on every system, and because these fonts can be used for both the screen and the printer, it's not necessary for a program to enumerate fonts in order to select one, or to blindly request a certain font type that might need to be approximated. A program could simply and precisely select TrueType fonts that it knows to exist on the system (unless, of course, the user has deliberately deleted them). It really should be almost as simple as specifying the name of the font (probably one of the 13 names listed in this book) and its point size. I call this approach EZFONT ("easy font"), and the two files you need are shown in Figure 17-3.
Figure 17-3 The EZFONT files
EZFONT.H/*---------------------- EZFONT.H header file ----------------------*/ HFONT EzCreateFont (HDC hdc, TCHAR * szFaceName, int iDeciPtHeight, int iDeciPtWidth, int iAttributes, BOOL fLogRes) ; #define EZ_ATTR_BOLD 1 #define EZ_ATTR_ITALIC 2 #define EZ_ATTR_UNDERLINE 4 #define EZ_ATTR_STRIKEOUT 8 |
EZFONT.C/*--------------------------------------- EZFONT.C -- Easy Font Creation (c) Charles Petzold, 1998 ---------------------------------------*/ #include <windows.h> #include <math.h> #include "ezfont.h" HFONT EzCreateFont (HDC hdc, TCHAR * szFaceName, int iDeciPtHeight, int iDeciPtWidth, int iAttributes, BOOL fLogRes) { FLOAT cxDpi, cyDpi ; HFONT hFont ; LOGFONT lf ; POINT pt ; TEXTMETRIC tm ; SaveDC (hdc) ; SetGraphicsMode (hdc, GM_ADVANCED) ; ModifyWorldTransform (hdc, NULL, MWT_IDENTITY) ; SetViewportOrgEx (hdc, 0, 0, NULL) ; SetWindowOrgEx (hdc, 0, 0, NULL) ; if (fLogRes) { cxDpi = (FLOAT) GetDeviceCaps (hdc, LOGPIXELSX) ; cyDpi = (FLOAT) GetDeviceCaps (hdc, LOGPIXELSY) ; } else { cxDpi = (FLOAT) (25.4 * GetDeviceCaps (hdc, HORZRES) / GetDeviceCaps (hdc, HORZSIZE)) ; cyDpi = (FLOAT) (25.4 * GetDeviceCaps (hdc, VERTRES) / GetDeviceCaps (hdc, VERTSIZE)) ; } pt.x = (int) (iDeciPtWidth * cxDpi / 72) ; pt.y = (int) (iDeciPtHeight * cyDpi / 72) ; DPtoLP (hdc, &pt, 1) ; lf.lfHeight = - (int) (fabs (pt.y) / 10.0 + 0.5) ; lf.lfWidth = 0 ; lf.lfEscapement = 0 ; lf.lfOrientation = 0 ; lf.lfWeight = iAttributes & EZ_ATTR_BOLD ? 700 : 0 ; lf.lfItalic = iAttributes & EZ_ATTR_ITALIC ? 1 : 0 ; lf.lfUnderline = iAttributes & EZ_ATTR_UNDERLINE ? 1 : 0 ; lf.lfStrikeOut = iAttributes & EZ_ATTR_STRIKEOUT ? 1 : 0 ; lf.lfCharSet = DEFAULT_CHARSET ; lf.lfOutPrecision = 0 ; lf.lfClipPrecision = 0 ; lf.lfQuality = 0 ; lf.lfPitchAndFamily = 0 ; lstrcpy (lf.lfFaceName, szFaceName) ; hFont = CreateFontIndirect (&lf) ; if (iDeciPtWidth != 0) { hFont = (HFONT) SelectObject (hdc, hFont) ; GetTextMetrics (hdc, &tm) ; DeleteObject (SelectObject (hdc, hFont)) ; lf.lfWidth = (int) (tm.tmAveCharWidth * fabs (pt.x) / fabs (pt.y) + 0.5) ; hFont = CreateFontIndirect (&lf) ; } RestoreDC (hdc, -1) ; return hFont ; } |
EZFONT.C has only one function, called EzCreateFont, which you can use like so:
hFont = EzCreateFont (hdc, szFaceName, iDeciPtHeight, iDeciPtWidth, iAttributes, fLogRes) ;
The function returns a handle to a font. The font can be selected in the device context by calling SelectObject. You should then call GetTextMetrics or GetOutlineTextMetrics to determine the actual size of the font dimensions in logical coordinates. Before your program terminates, you should delete any created fonts by calling DeleteObject.
The szFaceName argument is any TrueType typeface name. The closer you stick to the standard fonts, the less chance there is that the font won't exist on the system.
The third argument indicates the desired point size, but it's specified in "decipoints," which are 1/10ths of a point. Thus, if you want a point size of 121/2, use a value of 125.
Normally, the fourth argument should be set to zero or made identical to the third argument. However, you can create a TrueType font with a wider or narrower size by setting this argument to something different. This is sometimes called the "em-width" of the font, and it describes the width of the font in points. Don't confuse this with the average width of the font characters or anything like that. Back in the early days of typography, a capital M was as wide as it was high. So, the concept of an "em-square" came into being, and that's the origin of the em-width measurement. When the em-width equals the em-height (the point size of the font), the character widths are as the font designer intended. A smaller or wider em-width lets you create slimmer or wider characters.
You can set the iAttributes argument to one or more of the following values defined in EZFONT.H:
EZ_ATTR_BOLD EZ_ATTR_ITALIC EZ_ATTR_UNDERLINE EZ_ATTR_STRIKEOUT
You could use EZ_ATTR_BOLD or EZ_ATTR_ITALIC or include the style as part of the complete TrueType typeface name.
Finally, you set the last argument to TRUE to base the font size on the "logical resolution" returned by the GetDeviceCaps function using the LOGPIXELSX and LOGPIXELSY arguments. Otherwise, the font size is based on the resolution as calculated from the HORZRES, HORZSIZE, VERTRES, and VERTSIZE values. This makes a difference only for the video display under Windows NT.
The EzCreateFont function begins by making some adjustments that are recognized by Windows NT only. These are the calls to the SetGraphicsMode and ModifyWorldTransform functions, which have no effect in Windows 98. The Windows NT world transform should have the effect of modifying the visible size of the font, so the world transform is set to the default—no transform—before the font size is calculated.
EzCreateFont basically sets the fields of a LOGFONT structure and calls CreateFontIndirect, which returns a handle to the font. The big chore of the EzCreateFont function is to convert a point size to logical units for the lfHeight field of the LOGFONT structure. It turns out that the point size must be converted to device units (pixels) first and then to logical units. To perform the first step, the function uses GetDeviceCaps. Getting from pixels to logical units would seem to involve a fairly simple call to the DPtoLP ("device point to logical point") function. But in order for the DPtoLP conversion to work correctly, the same mapping mode must be in effect when you later display text using the created font. This means that you should set your mapping mode before calling the EzCreateFont function. In most cases, you use only one mapping mode for drawing on a particular area of the window, so this requirement should not be a problem.
The EZTEST program in Figure 17-4 tests out the EZFONT files but not too rigorously. This program uses the EZTEST files shown above and also includes FONTDEMO files that are used in some later programs in this book.
Figure 17-4 The EZTEST program
EZTEST.C/*--------------------------------------- EZTEST.C -- Test of EZFONT (c) Charles Petzold, 1998 ---------------------------------------*/ #include <windows.h> #include "ezfont.h" TCHAR szAppName [] = TEXT ("EZTest") ; TCHAR szTitle [] = TEXT ("EZTest: Test of EZFONT") ; void PaintRoutine (HWND hwnd, HDC hdc, int cxArea, int cyArea) { HFONT hFont ; int y, iPointSize ; LOGFONT lf ; TCHAR szBuffer [100] ; TEXTMETRIC tm ; // Set Logical Twips mapping mode SetMapMode (hdc, MM_ANISOTROPIC) ; SetWindowExtEx (hdc, 1440, 1440, NULL) ; SetViewportExtEx (hdc, GetDeviceCaps (hdc, LOGPIXELSX), GetDeviceCaps (hdc, LOGPIXELSY), NULL) ; // Try some fonts y = 0 ; for (iPointSize = 80 ; iPointSize <= 120 ; iPointSize++) { hFont = EzCreateFont (hdc, TEXT ("Times New Roman"), iPointSize, 0, 0, TRUE) ; GetObject (hFont, sizeof (LOGFONT), &lf) ; SelectObject (hdc, hFont) ; GetTextMetrics (hdc, &tm) ; TextOut (hdc, 0, y, szBuffer, wsprintf (szBuffer, TEXT ("Times New Roman font of %i.%i points, ") TEXT ("lf.lfHeight = %i, tm.tmHeight = %i"), iPointSize / 10, iPointSize % 10, lf.lfHeight, tm.tmHeight)) ; DeleteObject (SelectObject (hdc, GetStockObject (SYSTEM_FONT))) ; y += tm.tmHeight ; } } |
FONTDEMO.C/*------------------------------------------------ FONTDEMO.C -- Font Demonstration Shell Program (c) Charles Petzold, 1998 ------------------------------------------------*/ #include <windows.h> #include "..\\EZTest\\EzFont.h" #include "..\\EZTest\\resource.h" extern void PaintRoutine (HWND, HDC, int, int) ; LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; HINSTANCE hInst ; extern TCHAR szAppName [] ; extern TCHAR szTitle [] ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { TCHAR szResource [] = TEXT ("FontDemo") ; HWND hwnd ; MSG msg ; WNDCLASS wndclass ; hInst = hInstance ; 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 = szResource ; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("This program requires Windows NT!"), szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL) ; ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static DOCINFO di = { sizeof (DOCINFO), TEXT ("Font Demo: Printing") } ; static int cxClient, cyClient ; static PRINTDLG pd = { sizeof (PRINTDLG) } ; BOOL fSuccess ; HDC hdc, hdcPrn ; int cxPage, cyPage ; PAINTSTRUCT ps ; switch (message) { case WM_COMMAND: switch (wParam) { case IDM_PRINT: // Get printer DC pd.hwndOwner = hwnd ; pd.Flags = PD_RETURNDC | PD_NOPAGENUMS | PD_NOSELECTION ; if (!PrintDlg (&pd)) return 0 ; if (NULL == (hdcPrn = pd.hDC)) { MessageBox (hwnd, TEXT ("Cannot obtain Printer DC"), szAppName, MB_ICONEXCLAMATION | MB_OK) ; return 0 ; } // Get size of printable area of page cxPage = GetDeviceCaps (hdcPrn, HORZRES) ; cyPage = GetDeviceCaps (hdcPrn, VERTRES) ; fSuccess = FALSE ; // Do the printer page SetCursor (LoadCursor (NULL, IDC_WAIT)) ; ShowCursor (TRUE) ; if ((StartDoc (hdcPrn, &di) > 0) && (StartPage (hdcPrn) > 0)) { PaintRoutine (hwnd, hdcPrn, cxPage, cyPage) ; if (EndPage (hdcPrn) > 0) { fSuccess = TRUE ; EndDoc (hdcPrn) ; } } DeleteDC (hdcPrn) ; ShowCursor (FALSE) ; SetCursor (LoadCursor (NULL, IDC_ARROW)) ; if (!fSuccess) MessageBox (hwnd, TEXT ("Error encountered during printing"), szAppName, MB_ICONEXCLAMATION | MB_OK) ; return 0 ; case IDM_ABOUT: MessageBox (hwnd, TEXT ("Font Demonstration Program\n") TEXT ("(c) Charles Petzold, 1998"), szAppName, MB_ICONINFORMATION | MB_OK) ; return 0 ; } break ; case WM_SIZE: cxClient = LOWORD (lParam) ; cyClient = HIWORD (lParam) ; return 0 ; case WM_PAINT: hdc = BeginPaint (hwnd, &ps) ; PaintRoutine (hwnd, hdc, cxClient, cyClient) ; EndPaint (hwnd, &ps) ; return 0 ; case WM_DESTROY : PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; } |
FONTDEMO.RC//Microsoft Developer Studio generated resource script. #include "resource.h" #include "afxres.h" ///////////////////////////////////////////////////////////////////////////// // Menu FONTDEMO MENU DISCARDABLE BEGIN POPUP "&File" BEGIN MENUITEM "&Print...", IDM_PRINT END POPUP "&Help" BEGIN MENUITEM "&About...", IDM_ABOUT END END |
RESOURCE.H// Microsoft Developer Studio generated include file. // Used by FontDemo.rc #define IDM_PRINT 40001 #define IDM_ABOUT 40002 |
The PaintRoutine function in EZTEST.C sets its mapping mode to Logical Twips and then creates Times New Roman fonts with sizes ranging from 8 points to 12 points in 0.1 point intervals. The program output may be a little disturbing when you first run it. Many of the lines of text use a font that is obviously the same size, and indeed the tmHeight font on the TEXTMETRIC function reports these fonts as having the same height. What's happening here is a result of the rasterization process. The discrete pixels of the display can't allow for every possible size. However, the FONTDEMO shell program allows printing the output as well. Here you'll find that the font sizes are more accurately differentiated.
As you may have discovered by experimenting with PICKFONT, the lfOrientation and lfEscapement fields of the LOGFONT structure allow you to rotate TrueType text. If you think about it, this shouldn't be much of a stretch for GDI. Formulas to rotate coordinate points around an origin are well known.
Although EzCreateFont does not allow you to specify a rotation angle for the font, it's fairly easy to make an adjustment after calling the function, as the FONTROT ("Font Rotate") program demonstrates. Figure 17-5 shows the FONTROT.C file; the program also requires the EZFONT files and the FONTDEMO files shown earlier.
Figure 17-5. The FONTROT program.
FONTROT.C/*---------------------------------------- FONTROT.C -- Rotated Fonts (c) Charles Petzold, 1998 ----------------------------------------*/ #include <windows.h> #include "..\\eztest\\ezfont.h" TCHAR szAppName [] = TEXT ("FontRot") ; TCHAR szTitle [] = TEXT ("FontRot: Rotated Fonts") ; void PaintRoutine (HWND hwnd, HDC hdc, int cxArea, int cyArea) { static TCHAR szString [] = TEXT (" Rotation") ; HFONT hFont ; int i ; LOGFONT lf ; hFont = EzCreateFont (hdc, TEXT ("Times New Roman"), 540, 0, 0, TRUE) ; GetObject (hFont, sizeof (LOGFONT), &lf) ; DeleteObject (hFont) ; SetBkMode (hdc, TRANSPARENT) ; SetTextAlign (hdc, TA_BASELINE) ; SetViewportOrgEx (hdc, cxArea / 2, cyArea / 2, NULL) ; for (i = 0 ; i < 12 ; i ++) { lf.lfEscapement = lf.lfOrientation = i * 300 ; SelectObject (hdc, CreateFontIndirect (&lf)) ; TextOut (hdc, 0, 0, szString, lstrlen (szString)) ; DeleteObject (SelectObject (hdc, GetStockObject (SYSTEM_FONT))) ; } } |
FONTROT calls EzCreateFont just to obtain the LOGFONT structure associated with a 54-point Times New Roman font. The program then deletes that font. In the for loop, for each angle in 30-degree increments, a new font is created and the text is displayed. The results are shown in Figure 17-6.
Figure 17-6. The FONTROT display.
If you're interested in a more generalized approach to graphics rotation and other linear transformation and you know that your programs will be restricted to running under Windows NT, you can use the XFORM matrix and the world transform functions.