xref: /reactos/dll/cpl/console/font.c (revision 321bcc05)
1 /*
2  * PROJECT:         ReactOS Console Configuration DLL
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            dll/cpl/console/font.c
5  * PURPOSE:         Font dialog
6  * PROGRAMMERS:     Johannes Anderwald (johannes.anderwald@reactos.org)
7  *                  Hermes Belusca-Maito (hermes.belusca@sfr.fr)
8  */
9 
10 #include "console.h"
11 
12 #define NDEBUG
13 #include <debug.h>
14 
15 
16 //
17 // Some temporary code for future reference...
18 //
19 #if 0
20 /*
21  * This code comes from PuTTY
22  */
23 {
24     CHOOSEFONT cf;
25     LOGFONT lf;
26     HDC hdc;
27     FontSpec *fs = (FontSpec *)c->data;
28 
29     hdc = GetDC(0);
30     lf.lfHeight = -MulDiv(fs->height,
31               GetDeviceCaps(hdc, LOGPIXELSY), 72);
32     ReleaseDC(0, hdc);
33     lf.lfWidth = lf.lfEscapement = lf.lfOrientation = 0;
34     lf.lfItalic = lf.lfUnderline = lf.lfStrikeOut = 0;
35     lf.lfWeight = (fs->isbold ? FW_BOLD : 0);
36     lf.lfCharSet = fs->charset;
37     lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
38     lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
39     lf.lfQuality = DEFAULT_QUALITY;
40     lf.lfPitchAndFamily = FIXED_PITCH | FF_DONTCARE;
41     strncpy(lf.lfFaceName, fs->name,
42         sizeof(lf.lfFaceName) - 1);
43     lf.lfFaceName[sizeof(lf.lfFaceName) - 1] = '\0';
44 
45     cf.lStructSize = sizeof(cf);
46     cf.hwndOwner = dp->hwnd;
47     cf.lpLogFont = &lf;
48     cf.Flags = (dp->fixed_pitch_fonts ? CF_FIXEDPITCHONLY : 0) |
49             CF_FORCEFONTEXIST | CF_INITTOLOGFONTSTRUCT | CF_SCREENFONTS;
50 
51     if (ChooseFont(&cf)) {
52             fs = fontspec_new(lf.lfFaceName, (lf.lfWeight == FW_BOLD),
53                               cf.iPointSize / 10, lf.lfCharSet);
54     dlg_fontsel_set(ctrl, dp, fs);
55             fontspec_free(fs);
56 
57     ctrl->generic.handler(ctrl, dp, dp->data, EVENT_VALCHANGE);
58     }
59 }
60 
61 /*
62  * This code is from consrv.
63  */
64 {
65     if (!GetTextMetricsW(drawItem->hDC, &Metrics))
66     {
67         DPRINT1("PaintText: GetTextMetrics failed\n");
68         SelectObject(drawItem->hDC, OldFont);
69         DeleteObject(Font);
70         return;
71     }
72     GuiData->CharWidth  = Metrics.tmMaxCharWidth;
73     GuiData->CharHeight = Metrics.tmHeight + Metrics.tmExternalLeading;
74 
75     /* Measure real char width more precisely if possible */
76     if (GetTextExtentPoint32W(drawItem->hDC, L"R", 1, &CharSize))
77         GuiData->CharWidth = CharSize.cx;
78 }
79 
80 /*
81  * See also: Display_SetTypeFace in applications/fontview/display.c
82  */
83 #endif
84 
85 
86 /*
87  * Font pixel heights for TrueType fonts
88  */
89 static SHORT TrueTypePoints[] =
90 {
91     // 8, 9, 10, 11, 12, 14, 16, 18, 20,
92     // 22, 24, 26, 28, 36, 48, 72
93     5, 6, 7, 8, 10, 12, 14, 16, 18, 20, 24, 28, 36, 72
94 };
95 
96 static BOOL CALLBACK
97 EnumFontNamesProc(PLOGFONTW lplf,
98                   PNEWTEXTMETRICW lpntm,
99                   DWORD  FontType,
100                   LPARAM lParam)
101 {
102     HWND hwndCombo = (HWND)lParam;
103     LPWSTR pszName = lplf->lfFaceName;
104 
105     /* Record the font's attributes (Fixedwidth and Truetype) */
106     // BOOL fFixed    = ((lplf->lfPitchAndFamily & 0x03) == FIXED_PITCH);
107     // BOOL fTrueType = (lplf->lfOutPrecision == OUT_STROKE_PRECIS);
108 
109     /*
110      * According to: http://support.microsoft.com/kb/247815
111      * the criteria for console-eligible fonts are:
112      * - The font must be a fixed-pitch font.
113      * - The font cannot be an italic font.
114      * - The font cannot have a negative A or C space.
115      * - If it is a TrueType font, it must be FF_MODERN.
116      * - If it is not a TrueType font, it must be OEM_CHARSET.
117      *
118      * Non documented: vertical fonts are forbidden (their name start with a '@').
119      *
120      * Additional criteria for Asian installations:
121      * - If it is not a TrueType font, the face name must be "Terminal".
122      * - If it is an Asian TrueType font, it must also be an Asian character set.
123      *
124      * To install additional TrueType fonts to be available for the console,
125      * add entries of type REG_SZ named "0", "00" etc... in:
126      * HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Console\TrueTypeFont
127      * The names of the fonts listed there should match those in:
128      * HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Fonts
129      */
130 
131      /*
132       * In ReactOS, we relax some criteria:
133       * - We allow fixed-pitch FF_MODERN (Monospace) TrueType fonts
134       *   that can be italic and have negative A or C space.
135       * - If it is not a TrueType font, it can be from another character set
136       *   than OEM_CHARSET.
137       * - We do not support Asian criteria at the moment.
138       * - We do not look into the magic registry key mentioned above.
139       */
140 
141     /* Reject variable width fonts */
142     if (((lplf->lfPitchAndFamily & 0x03) != FIXED_PITCH)
143 #if 0 /* Reject italic and TrueType fonts with negative A or C space */
144         || (lplf->lfItalic)
145         || !(lpntm->ntmFlags & NTM_NONNEGATIVE_AC)
146 #endif
147         )
148     {
149         DPRINT1("Font '%S' rejected because it%s (lfPitchAndFamily = %d).\n",
150                 pszName, !(lplf->lfPitchAndFamily & FIXED_PITCH) ? "'s not FIXED_PITCH" : (!(lpntm->ntmFlags & NTM_NONNEGATIVE_AC) ? " has negative A or C space" : " is broken"),
151                 lplf->lfPitchAndFamily);
152         return TRUE;
153     }
154 
155     /* Reject TrueType fonts that are not FF_MODERN */
156     if ((FontType == TRUETYPE_FONTTYPE) && ((lplf->lfPitchAndFamily & 0xF0) != FF_MODERN))
157     {
158         DPRINT1("TrueType font '%S' rejected because it's not FF_MODERN (lfPitchAndFamily = %d)\n",
159                 pszName, lplf->lfPitchAndFamily);
160         return TRUE;
161     }
162 
163     /* Reject non-TrueType fonts that are not OEM */
164 #if 0
165     if ((FontType != TRUETYPE_FONTTYPE) && (lplf->lfCharSet != OEM_CHARSET))
166     {
167         DPRINT1("Non-TrueType font '%S' rejected because it's not OEM_CHARSET %d\n",
168                 pszName, lplf->lfCharSet);
169         return TRUE;
170     }
171 #else // Improved criterium
172     if ((FontType != TRUETYPE_FONTTYPE) &&
173         ((lplf->lfCharSet != ANSI_CHARSET) && (lplf->lfCharSet != DEFAULT_CHARSET) && (lplf->lfCharSet != OEM_CHARSET)))
174     {
175         DPRINT1("Non-TrueType font '%S' rejected because it's not ANSI_CHARSET or DEFAULT_CHARSET or OEM_CHARSET (lfCharSet = %d)\n",
176                 pszName, lplf->lfCharSet);
177         return TRUE;
178     }
179 #endif
180 
181     /* Reject fonts that are vertical (tategaki) */
182     if (pszName[0] == L'@')
183     {
184         DPRINT1("Font '%S' rejected because it's vertical\n", pszName);
185         return TRUE;
186     }
187 
188 #if 0 // For Asian installations only
189     /* Reject non-TrueType fonts that are not Terminal */
190     if ((FontType != TRUETYPE_FONTTYPE) && (wcscmp(pszName, L"Terminal") != 0))
191     {
192         DPRINT1("Non-TrueType font '%S' rejected because it's not Terminal\n", pszName);
193         return TRUE;
194     }
195 
196     // TODO: Asian TrueType font must also be an Asian character set.
197 #endif
198 
199     /* Make sure the font doesn't already exist in the list */
200     if (SendMessageW(hwndCombo, LB_FINDSTRINGEXACT, 0, (LPARAM)pszName) == LB_ERR)
201     {
202         /* Add the font */
203         INT idx = (INT)SendMessageW(hwndCombo, LB_ADDSTRING, 0, (LPARAM)pszName);
204 
205         DPRINT1("Add font '%S' (lfPitchAndFamily = %d)\n", pszName, lplf->lfPitchAndFamily);
206 
207         /* Store this information in the list-item's userdata area */
208         // SendMessageW(hwndCombo, LB_SETITEMDATA, idx, MAKEWPARAM(fFixed, fTrueType));
209         SendMessageW(hwndCombo, LB_SETITEMDATA, idx, (WPARAM)FontType);
210     }
211 
212     return TRUE;
213 }
214 
215 static BOOL CALLBACK
216 EnumFontSizesProc(PLOGFONTW lplf,
217                   PNEWTEXTMETRICW lpntm,
218                   DWORD  FontType,
219                   LPARAM lParam)
220 {
221     HWND hwndCombo = (HWND)lParam;
222     WCHAR FontSize[100];
223 
224     if (FontType != TRUETYPE_FONTTYPE)
225     {
226         // int logsize     = lpntm->tmHeight - lpntm->tmInternalLeading;
227         // LONG pointsize  = MulDiv(logsize, 72, GetDeviceCaps(hdc, LOGPIXELSY));
228 
229         // swprintf(FontSize, L"%2d (%d x %d)", pointsize, lplf->lfWidth, lplf->lfHeight);
230         swprintf(FontSize, L"%d x %d", lplf->lfWidth, lplf->lfHeight);
231 
232         /* Make sure the size doesn't already exist in the list */
233         if (SendMessageW(hwndCombo, LB_FINDSTRINGEXACT, 0, (LPARAM)FontSize) == LB_ERR)
234         {
235             /* Add the size */
236             INT idx = (INT)SendMessageW(hwndCombo, LB_ADDSTRING, 0, (LPARAM)FontSize);
237 
238             /*
239              * Store this information in the list-item's userdata area.
240              * Format:
241              * Width  = FontSize.X = LOWORD(FontSize);
242              * Height = FontSize.Y = HIWORD(FontSize);
243              */
244             SendMessageW(hwndCombo, LB_SETITEMDATA, idx, MAKEWPARAM(lplf->lfWidth, lplf->lfHeight));
245         }
246 
247         return TRUE;
248     }
249     else
250     {
251         ULONG i;
252         for (i = 0; i < ARRAYSIZE(TrueTypePoints); ++i)
253         {
254             swprintf(FontSize, L"%2d", TrueTypePoints[i]);
255 
256             /* Make sure the size doesn't already exist in the list */
257             if (SendMessageW(hwndCombo, LB_FINDSTRINGEXACT, 0, (LPARAM)FontSize) == LB_ERR)
258             {
259                 /* Add the size */
260                 INT idx = (INT)SendMessageW(hwndCombo, LB_ADDSTRING, 0, (LPARAM)FontSize);
261 
262                 /*
263                  * Store this information in the list-item's userdata area.
264                  * Format:
265                  * Width  = FontSize.X = LOWORD(FontSize);
266                  * Height = FontSize.Y = HIWORD(FontSize);
267                  */
268                 SendMessageW(hwndCombo, LB_SETITEMDATA, idx, MAKEWPARAM(0, TrueTypePoints[i]));
269             }
270         }
271 
272         return FALSE;
273     }
274 }
275 
276 
277 static VOID
278 FontSizeChange(HWND hwndDlg,
279                PCONSOLE_STATE_INFO pConInfo);
280 
281 static VOID
282 FontTypeChange(HWND hwndDlg,
283                PCONSOLE_STATE_INFO pConInfo)
284 {
285     INT Length, nSel;
286     LPWSTR FaceName;
287 
288     HDC hDC;
289     LOGFONTW lf;
290 
291     nSel = (INT)SendDlgItemMessageW(hwndDlg, IDC_LBOX_FONTTYPE,
292                                     LB_GETCURSEL, 0, 0);
293     if (nSel == LB_ERR) return;
294 
295     Length = (INT)SendDlgItemMessageW(hwndDlg, IDC_LBOX_FONTTYPE,
296                                       LB_GETTEXTLEN, nSel, 0);
297     if (Length == LB_ERR) return;
298 
299     FaceName = HeapAlloc(GetProcessHeap(),
300                          HEAP_ZERO_MEMORY,
301                          (Length + 1) * sizeof(WCHAR));
302     if (FaceName == NULL) return;
303 
304     Length = (INT)SendDlgItemMessageW(hwndDlg, IDC_LBOX_FONTTYPE,
305                                       LB_GETTEXT, nSel, (LPARAM)FaceName);
306     FaceName[Length] = '\0';
307 
308     Length = min(Length/*wcslen(FaceName) + 1*/, LF_FACESIZE - 1); // wcsnlen
309     wcsncpy(pConInfo->FaceName, FaceName, LF_FACESIZE);
310     pConInfo->FaceName[Length] = L'\0';
311     DPRINT1("pConInfo->FaceName = '%S'\n", pConInfo->FaceName);
312 
313     /* Enumerate the available sizes for the selected font */
314     ZeroMemory(&lf, sizeof(lf));
315     lf.lfCharSet  = DEFAULT_CHARSET; // OEM_CHARSET;
316     // lf.lfPitchAndFamily = FIXED_PITCH | FF_DONTCARE;
317     wcsncpy(lf.lfFaceName, FaceName, LF_FACESIZE);
318     lf.lfFaceName[Length] = L'\0';
319 
320     hDC = GetDC(NULL);
321     EnumFontFamiliesExW(hDC, &lf, (FONTENUMPROCW)EnumFontSizesProc,
322                         (LPARAM)GetDlgItem(hwndDlg, IDC_LBOX_FONTSIZE), 0);
323     ReleaseDC(NULL, hDC);
324 
325     HeapFree(GetProcessHeap(), 0, FaceName);
326 
327     // TODO: Select a default font size????
328     FontSizeChange(hwndDlg, pConInfo);
329 
330     // InvalidateRect(GetDlgItem(hwndDlg, IDC_STATIC_FONT_WINDOW_PREVIEW), NULL, TRUE);
331     // InvalidateRect(GetDlgItem(hwndDlg, IDC_STATIC_SELECT_FONT_PREVIEW), NULL, TRUE);
332 }
333 
334 static VOID
335 FontSizeChange(HWND hwndDlg,
336                PCONSOLE_STATE_INFO pConInfo)
337 {
338     INT nSel;
339     ULONG FontSize;
340     WCHAR FontSizeStr[20];
341 
342     nSel = (INT)SendDlgItemMessageW(hwndDlg, IDC_LBOX_FONTSIZE,
343                                     LB_GETCURSEL, 0, 0);
344     if (nSel == LB_ERR) return;
345 
346     /*
347      * Format:
348      * Width  = FontSize.X = LOWORD(FontSize);
349      * Height = FontSize.Y = HIWORD(FontSize);
350      */
351     FontSize = (ULONG)SendDlgItemMessageW(hwndDlg, IDC_LBOX_FONTSIZE,
352                                           LB_GETITEMDATA, nSel, 0);
353     if (FontSize == LB_ERR) return;
354 
355     pConInfo->FontSize.X = LOWORD(FontSize);
356     pConInfo->FontSize.Y = HIWORD(FontSize);
357     DPRINT1("pConInfo->FontSize = (%d x %d)\n", pConInfo->FontSize.X, pConInfo->FontSize.Y);
358 
359     InvalidateRect(GetDlgItem(hwndDlg, IDC_STATIC_FONT_WINDOW_PREVIEW), NULL, TRUE);
360     InvalidateRect(GetDlgItem(hwndDlg, IDC_STATIC_SELECT_FONT_PREVIEW), NULL, TRUE);
361 
362     swprintf(FontSizeStr, L"%2d", pConInfo->FontSize.X);
363     SetWindowText(GetDlgItem(hwndDlg, IDC_FONT_SIZE_X), FontSizeStr);
364     swprintf(FontSizeStr, L"%2d", pConInfo->FontSize.Y);
365     SetWindowText(GetDlgItem(hwndDlg, IDC_FONT_SIZE_Y), FontSizeStr);
366 }
367 
368 
369 INT_PTR
370 CALLBACK
371 FontProc(HWND hwndDlg,
372          UINT uMsg,
373          WPARAM wParam,
374          LPARAM lParam)
375 {
376     UNREFERENCED_PARAMETER(wParam);
377 
378     switch (uMsg)
379     {
380         case WM_INITDIALOG:
381         {
382             HDC  hDC;
383             LOGFONTW lf;
384             INT idx;
385 
386             ZeroMemory(&lf, sizeof(lf));
387             lf.lfCharSet  = DEFAULT_CHARSET; // OEM_CHARSET;
388             // lf.lfPitchAndFamily = FIXED_PITCH | FF_DONTCARE;
389 
390             hDC = GetDC(NULL);
391             EnumFontFamiliesExW(hDC, &lf, (FONTENUMPROCW)EnumFontNamesProc,
392                                 (LPARAM)GetDlgItem(hwndDlg, IDC_LBOX_FONTTYPE), 0);
393             ReleaseDC(NULL, hDC);
394 
395             DPRINT1("ConInfo->FaceName = '%S'\n", ConInfo->FaceName);
396             idx = (INT)SendDlgItemMessageW(hwndDlg, IDC_LBOX_FONTTYPE,
397                                            LB_FINDSTRINGEXACT, 0, (LPARAM)ConInfo->FaceName);
398             if (idx != LB_ERR) SendDlgItemMessageW(hwndDlg, IDC_LBOX_FONTTYPE,
399                                                    LB_SETCURSEL, (WPARAM)idx, 0);
400 
401             FontTypeChange(hwndDlg, ConInfo);
402 
403             return TRUE;
404         }
405 
406         case WM_DRAWITEM:
407         {
408             LPDRAWITEMSTRUCT drawItem = (LPDRAWITEMSTRUCT)lParam;
409 
410             if (drawItem->CtlID == IDC_STATIC_FONT_WINDOW_PREVIEW)
411                 PaintConsole(drawItem, ConInfo);
412             else if (drawItem->CtlID == IDC_STATIC_SELECT_FONT_PREVIEW)
413                 PaintText(drawItem, ConInfo, Screen);
414 
415             return TRUE;
416         }
417 
418         case WM_NOTIFY:
419         {
420             switch (((LPNMHDR)lParam)->code)
421             {
422                 case PSN_APPLY:
423                 {
424                     ApplyConsoleInfo(hwndDlg);
425                     return TRUE;
426                 }
427             }
428 
429             break;
430         }
431 
432         case WM_COMMAND:
433         {
434             switch (HIWORD(wParam))
435             {
436                 case LBN_SELCHANGE:
437                 {
438                     switch (LOWORD(wParam))
439                     {
440                         case IDC_LBOX_FONTTYPE:
441                         {
442                             FontTypeChange(hwndDlg, ConInfo);
443                             PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
444                             break;
445                         }
446 
447                         case IDC_LBOX_FONTSIZE:
448                         {
449                             FontSizeChange(hwndDlg, ConInfo);
450                             PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
451                             break;
452                         }
453                     }
454 
455                     break;
456                 }
457             }
458 
459             break;
460         }
461 
462         default:
463             break;
464     }
465 
466     return FALSE;
467 }
468