1 /*
2  * PROJECT:     ReactOS Font Viewer
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     Display class
5  * COPYRIGHT:   Copyright 2007 Timo Kreuzer <timo.kreuzer@reactos.org>
6  */
7 
8 #include "precomp.h"
9 
10 #include <stdio.h>
11 #include <malloc.h>
12 
13 #define SPACING1 8
14 #define SPACING2 5
15 
16 extern INT g_NumFonts;
17 extern WCHAR g_FontTitle[];
18 
19 const WCHAR g_szFontDisplayClassName[] = L"FontDisplayClass";
20 LRESULT CALLBACK DisplayProc(HWND, UINT, WPARAM, LPARAM);
21 
22 /* Internal data storage type */
23 typedef struct
24 {
25     int nPageHeight;
26     WCHAR szTypeFaceName[LF_FULLFACESIZE];
27     WCHAR szFormat[MAX_FORMAT];
28     WCHAR szString[MAX_STRING];
29 
30     HFONT hCaptionFont;
31     HFONT hCharSetFont;
32     HFONT hSizeFont;
33     HFONT hFonts[MAX_SIZES];
34     int nSizes[MAX_SIZES];
35     int nHeights[MAX_SIZES];
36 } DISPLAYDATA;
37 
38 /* This is the only public function, it registers the class */
39 BOOL
Display_InitClass(HINSTANCE hInstance)40 Display_InitClass(HINSTANCE hInstance)
41 {
42     WNDCLASSEXW wincl;
43 
44     /* Set the fontdisplay window class structure */
45     wincl.cbSize = sizeof(WNDCLASSEX);
46     wincl.style = CS_DBLCLKS;
47     wincl.lpfnWndProc = DisplayProc;
48     wincl.cbClsExtra = 0;
49     wincl.cbWndExtra = 0;
50     wincl.hInstance = hInstance;
51     wincl.hIcon = NULL;
52     wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
53     wincl.hbrBackground = GetStockObject(WHITE_BRUSH);
54     wincl.lpszMenuName = NULL;
55     wincl.lpszClassName = g_szFontDisplayClassName;
56     wincl.hIconSm = NULL;
57 
58     /* Register the window class, and if it fails return FALSE */
59     if (!RegisterClassExW (&wincl))
60     {
61         return FALSE;
62     }
63     return TRUE;
64 }
65 
66 static int
Display_DrawText(HDC hDC,DISPLAYDATA * pData,int nYPos)67 Display_DrawText(HDC hDC, DISPLAYDATA* pData, int nYPos)
68 {
69     HFONT hOldFont;
70     TEXTMETRIC tm;
71     int i, y;
72     WCHAR szSize[5];
73     WCHAR szCaption[LF_FULLFACESIZE + 20];
74 
75     /* This is the location on the DC where we draw */
76     y = -nYPos;
77 
78     hOldFont = SelectObject(hDC, pData->hCaptionFont);
79     GetTextMetrics(hDC, &tm);
80 
81     swprintf(szCaption, L"%s%s", pData->szTypeFaceName, pData->szFormat);
82     TextOutW(hDC, 0, y, szCaption, (INT)wcslen(szCaption));
83     y += tm.tmHeight + SPACING1;
84 
85     /* Draw a separation Line */
86     SelectObject(hDC, GetStockObject(BLACK_PEN));
87     MoveToEx(hDC, 0, y, NULL);
88     LineTo(hDC, 10000, y);
89     y += SPACING2;
90 
91     /* TODO: Output font info */
92 
93     /* Output Character set */
94     SelectObject(hDC, pData->hCharSetFont);
95     GetTextMetrics(hDC, &tm);
96     swprintf(szCaption, L"abcdefghijklmnopqrstuvwxyz");
97     TextOutW(hDC, 0, y, szCaption, (INT)wcslen(szCaption));
98     y += tm.tmHeight + 1;
99 
100     swprintf(szCaption, L"ABCDEFGHIJKLMNOPQRSTUVWXYZ");
101     TextOutW(hDC, 0, y, szCaption, (INT)wcslen(szCaption));
102     y += tm.tmHeight + 1;
103 
104     swprintf(szCaption, L"0123456789.:,;(\"~!@#$%%^&*')");
105     TextOutW(hDC, 0, y, szCaption, (INT)wcslen(szCaption));
106     y += tm.tmHeight + 1;
107 
108     /* Draw a separation Line */
109     SelectObject(hDC, GetStockObject(BLACK_PEN));
110     MoveToEx(hDC, 0, y, NULL);
111     LineTo(hDC, 10000, y);
112     y += SPACING2;
113 
114     /* Output the strings for different sizes */
115     for (i = 0; i < MAX_SIZES; i++)
116     {
117         SelectObject(hDC, pData->hFonts[i]);
118         TextOutW(hDC, 20, y, pData->szString, (INT)wcslen(pData->szString));
119         GetTextMetrics(hDC, &tm);
120         y += tm.tmHeight + 1;
121         SelectObject(hDC, pData->hSizeFont);
122         swprintf(szSize, L"%d", pData->nSizes[i]);
123         TextOutW(hDC, 0, y - 13 - tm.tmDescent, szSize, (INT)wcslen(szSize));
124     }
125     SelectObject(hDC, hOldFont);
126 
127     return y;
128 }
129 
130 static int
131 CALLBACK
EnumFontFamProcW(const LOGFONTW * lpelfe,const TEXTMETRICW * lptm,DWORD FontType,LPARAM lParam)132 EnumFontFamProcW(
133     const LOGFONTW *lpelfe,
134     const TEXTMETRICW *lptm,
135     DWORD FontType,
136     LPARAM lParam)
137 {
138     PNEWTEXTMETRICW pntmw = (PNEWTEXTMETRICW)lptm;
139     PBOOL pfOpenType = (PBOOL)lParam;
140 
141     if (FontType & TRUETYPE_FONTTYPE)
142     {
143         if (pntmw->ntmFlags & (NTM_TT_OPENTYPE | NTM_PS_OPENTYPE))
144         {
145             *pfOpenType = TRUE;
146             return FALSE;
147         }
148     }
149     return TRUE;
150 }
151 
152 static LRESULT
Display_SetTypeFace(HWND hwnd,PLOGFONTW pLogFont)153 Display_SetTypeFace(HWND hwnd, PLOGFONTW pLogFont)
154 {
155     DISPLAYDATA* pData;
156     TEXTMETRIC tm;
157     HDC hDC;
158     RECT rect;
159     SCROLLINFO si;
160     int i;
161     LOGFONTW logfont;
162     BOOL fOpenType;
163     BYTE Buffer[512];
164     LPOUTLINETEXTMETRICW pOTM = (LPOUTLINETEXTMETRICW)Buffer;
165     LPWSTR pch;
166 
167     /* Set the new type face name */
168     pData = (DISPLAYDATA*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
169     lstrcpynW(pData->szTypeFaceName, pLogFont->lfFaceName,
170               ARRAYSIZE(pData->szTypeFaceName));
171 
172     /* Create the new fonts */
173     hDC = GetDC(hwnd);
174     DeleteObject(pData->hCharSetFont);
175 
176     logfont = *pLogFont;
177     logfont.lfHeight = -MulDiv(16, GetDeviceCaps(GetDC(NULL), LOGPIXELSY), 72);
178     pData->hCharSetFont = CreateFontIndirectW(&logfont);
179 
180     /* Get font format */
181     SelectObject(hDC, pData->hCharSetFont);
182     GetTextMetrics(hDC, &tm);
183     if (tm.tmPitchAndFamily & TMPF_TRUETYPE)
184     {
185         if (GetOutlineTextMetricsW(hDC, sizeof(Buffer), pOTM))
186         {
187             LPBYTE pb = Buffer;
188             pb += (WORD)(DWORD_PTR)pOTM->otmpStyleName;
189             pch = (LPWSTR)pb;
190             if (*pch)
191             {
192                 lstrcatW(pData->szTypeFaceName, L" ");
193                 lstrcatW(pData->szTypeFaceName, pch);
194             }
195         }
196 
197         fOpenType = FALSE;
198         EnumFontFamiliesExW(hDC, &logfont,
199             EnumFontFamProcW, (LPARAM)&fOpenType, 0);
200 
201         if (fOpenType)
202             swprintf(pData->szFormat, L" (OpenType)");
203         else
204             swprintf(pData->szFormat, L" (TrueType)");
205     }
206     else if (tm.tmPitchAndFamily & TMPF_VECTOR)
207     {
208         swprintf(pData->szFormat, L" (Vector)");
209     }
210     else
211     {
212         swprintf(pData->szFormat, L" (Raster)");
213     }
214 
215     for (i = 0; i < MAX_SIZES; i++)
216     {
217         DeleteObject(pData->hFonts[i]);
218         logfont.lfHeight = -MulDiv(pData->nSizes[i], GetDeviceCaps(hDC, LOGPIXELSY), 72);
219         pData->hFonts[i] = CreateFontIndirectW(&logfont);
220     }
221 
222     /* Calculate new page dimensions */
223     pData->nPageHeight = Display_DrawText(hDC, pData, 0);
224     ReleaseDC(hwnd, hDC);
225 
226     /* Set the vertical scrolling range and page size */
227     GetClientRect(hwnd, &rect);
228     si.cbSize = sizeof(si);
229     si.fMask  = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS;
230     si.nMin   = 0;
231     si.nMax   = pData->nPageHeight;
232     si.nPage  = rect.bottom;
233     si.nPos   = 0;
234     si.nTrackPos = 0;
235     SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
236 
237     return 0;
238 }
239 
240 static LRESULT
Display_SetString(HWND hwnd,LPCWSTR pszString)241 Display_SetString(HWND hwnd, LPCWSTR pszString)
242 {
243     DISPLAYDATA* pData;
244 
245     pData = (DISPLAYDATA*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
246     lstrcpynW(pData->szString, pszString, ARRAYSIZE(pData->szString));
247 
248     InvalidateRect(hwnd, NULL, TRUE);
249 
250     return 0;
251 }
252 
253 static LRESULT
Display_OnCreate(HWND hwnd)254 Display_OnCreate(HWND hwnd)
255 {
256     DISPLAYDATA* pData;
257     const int nSizes[MAX_SIZES] = {8, 12, 18, 24, 36, 48, 60, 72};
258     int i;
259     LOGFONTW LogFont = {50, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE,
260                         ANSI_CHARSET, OUT_DEFAULT_PRECIS,
261                         CLIP_DEFAULT_PRECIS, PROOF_QUALITY,
262                         DEFAULT_PITCH , L"MS Shell Dlg"};
263 
264     /* Create data structure */
265     pData = malloc(sizeof(DISPLAYDATA));
266     ZeroMemory(pData, sizeof(DISPLAYDATA));
267 
268     /* Set the window's GWLP_USERDATA to our data structure */
269     SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pData);
270 
271     for (i = 0; i < MAX_SIZES; i++)
272     {
273         pData->nSizes[i] = nSizes[i];
274     }
275 
276     pData->hCaptionFont = CreateFontIndirectW(&LogFont);
277     LogFont.lfHeight = 12;
278     pData->hSizeFont = CreateFontIndirectW(&LogFont);
279 
280     Display_SetString(hwnd,
281         L"Jackdaws love my big sphinx of quartz. 1234567890");
282 
283     Display_SetTypeFace(hwnd, &LogFont);
284 
285     return 0;
286 }
287 
288 static LRESULT
Display_OnPaint(HWND hwnd)289 Display_OnPaint(HWND hwnd)
290 {
291     DISPLAYDATA* pData;
292     PAINTSTRUCT ps;
293     SCROLLINFO si;
294 
295     pData = (DISPLAYDATA*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
296 
297     /* Get the Scroll position */
298     si.cbSize = sizeof(si);
299     si.fMask = SIF_POS;
300     GetScrollInfo(hwnd, SB_VERT, &si);
301 
302     BeginPaint(hwnd, &ps);
303 
304     /* Erase background */
305     FillRect(ps.hdc, &ps.rcPaint, GetStockObject(WHITE_BRUSH));
306 
307     /* Draw the text */
308     Display_DrawText(ps.hdc, pData, si.nPos);
309 
310     EndPaint(hwnd, &ps);
311 
312     return 0;
313 }
314 
315 static LRESULT
Display_OnSize(HWND hwnd)316 Display_OnSize(HWND hwnd)
317 {
318     RECT rect;
319     SCROLLINFO si;
320     int nOldPos;
321 
322     GetClientRect(hwnd, &rect);
323 
324     /* Get the old scroll pos */
325     si.cbSize = sizeof(si);
326     si.fMask  = SIF_POS;
327     GetScrollInfo(hwnd, SB_VERT, &si);
328     nOldPos = si.nPos;
329 
330     /* Set the new page size */
331     si.fMask  = SIF_PAGE;
332     si.nPage  = rect.bottom;
333     SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
334 
335     /* Get the new scroll pos */
336     si.fMask  = SIF_POS;
337     GetScrollInfo(hwnd, SB_VERT, &si);
338 
339     /* If they don't match ... */
340     if (nOldPos != si.nPos)
341     {
342         /* ... scroll the window */
343         ScrollWindowEx(hwnd, 0, nOldPos - si.nPos, NULL, NULL, NULL, NULL, SW_INVALIDATE);
344         UpdateWindow(hwnd);
345     }
346 
347     return 0;
348 }
349 
350 static LRESULT
Display_OnVScroll(HWND hwnd,WPARAM wParam)351 Display_OnVScroll(HWND hwnd, WPARAM wParam)
352 {
353     SCROLLINFO si;
354     int nPos;
355 
356     si.cbSize = sizeof(si);
357     si.fMask  = SIF_POS | SIF_RANGE | SIF_TRACKPOS;
358     GetScrollInfo(hwnd, SB_VERT, &si);
359 
360     switch(LOWORD(wParam))
361     {
362         case SB_PAGEUP:
363             nPos = si.nPos - 50;
364             break;
365         case SB_PAGEDOWN:
366             nPos = si.nPos + 50;
367             break;
368         case SB_LINEUP:
369             nPos = si.nPos - 10;
370             break;
371         case SB_LINEDOWN:
372             nPos = si.nPos + 10;
373             break;
374         case SB_THUMBTRACK:
375         case SB_THUMBPOSITION:
376             nPos = si.nTrackPos;
377             break;
378         default:
379             nPos = si.nPos;
380     }
381 
382     nPos = max(nPos, si.nMin);
383     nPos = min(nPos, si.nMax);
384     if (nPos != si.nPos)
385     {
386         ScrollWindowEx(hwnd, 0, si.nPos - nPos, NULL, NULL, NULL, NULL, SW_INVALIDATE);
387         si.cbSize = sizeof(si);
388         si.nPos = nPos;
389         si.fMask  = SIF_POS;
390         SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
391         UpdateWindow(hwnd);
392     }
393 
394     return 0;
395 }
396 
397 static LRESULT
Display_OnDestroy(HWND hwnd)398 Display_OnDestroy(HWND hwnd)
399 {
400     DISPLAYDATA* pData;
401     int i;
402 
403     pData = (DISPLAYDATA*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
404 
405     /* Delete the fonts */
406     DeleteObject(pData->hCaptionFont);
407     DeleteObject(pData->hCharSetFont);
408     DeleteObject(pData->hSizeFont);
409 
410     for (i = 0; i < MAX_SIZES; i++)
411     {
412         DeleteObject(pData->hFonts[i]);
413     }
414 
415     /* Free the data structure */
416     free(pData);
417 
418     return 0;
419 }
420 
421 LRESULT
Display_OnPrint(HWND hwnd)422 Display_OnPrint(HWND hwnd)
423 {
424     PRINTDLG pfont;
425     TEXTMETRIC tm;
426     int copies, yPos;
427 
428     /* Clears the memory before using it */
429     ZeroMemory(&pfont, sizeof(pfont));
430 
431     pfont.lStructSize = sizeof(pfont);
432     pfont.hwndOwner = hwnd;
433     pfont.hDevMode = NULL;
434     pfont.hDevNames = NULL;
435     pfont.Flags = PD_USEDEVMODECOPIESANDCOLLATE | PD_RETURNDC;
436     pfont.nCopies = 1;
437     pfont.nFromPage = 0xFFFF;
438     pfont.nToPage = 0xFFFF;
439     pfont.nMinPage = 1;
440     pfont.nMaxPage = 0xFFFF;
441 
442     /* Opens up the print dialog box */
443     if (PrintDlg(&pfont))
444     {
445         DOCINFO docinfo;
446 #if 0
447         DISPLAYDATA* pData;
448 
449         pData = malloc(sizeof(DISPLAYDATA));
450         ZeroMemory(pData, sizeof(DISPLAYDATA));
451 
452         /* Sets up the font layout */
453         pData = (DISPLAYDATA*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
454 #endif
455         docinfo.cbSize = sizeof(DOCINFO);
456         docinfo.lpszDocName = L"Printing Font";
457         docinfo.lpszOutput = NULL;
458         docinfo.lpszDatatype = NULL;
459         docinfo.fwType = 0;
460 
461         /* We start printing */
462         StartDoc(pfont.hDC, &docinfo);
463 
464         /* Grabs the text metrics for the printer */
465         GetTextMetrics(pfont.hDC, &tm);
466 
467         /* Start out with 0 for the y position for the page */
468         yPos = 0;
469 
470         /* Starts out with the current page */
471         StartPage(pfont.hDC);
472 
473         /* Used when printing for more than one copy */
474         for (copies = 0; copies < pfont.nCopies; copies++)
475         {
476             /* Test output */
477             TextOutW(pfont.hDC, 10, yPos, L"Testing...1...2...3", 19);
478 
479             /* TODO: Determine if using Display_DrawText() will work for both rendering out to the
480             window and to the printer output */
481 #if 0
482             Display_DrawText(pfont.hDC, pData, yPos);
483 #endif
484 
485             /* Ends the current page */
486             EndPage(pfont.hDC);
487 
488             /* If we are making more than one copy, start a new page */
489             if (copies != pfont.nCopies)
490             {
491                 yPos = 0;
492                 StartPage(pfont.hDC);
493             }
494         }
495 
496         /* The printing is now over */
497         EndDoc(pfont.hDC);
498 
499         DeleteDC(pfont.hDC);
500 #if 0
501         /* Frees the memory since we no longer need it for now */
502         free(pData);
503 #endif
504     }
505 
506     return 0;
507 }
508 
509 LRESULT
Display_GetFullName(HWND hwnd,INT length,PWSTR ptr)510 Display_GetFullName(HWND hwnd, INT length, PWSTR ptr)
511 {
512     DISPLAYDATA *pData;
513     INT len;
514 
515     pData = (DISPLAYDATA*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
516 
517     len = wcslen(pData->szTypeFaceName) + wcslen(pData->szFormat) + 2;
518 
519     if (ptr != NULL && length >= len)
520     {
521         swprintf(ptr, L"%s%s", pData->szTypeFaceName, pData->szFormat);
522     }
523 
524     return (LRESULT)len;
525 }
526 
527 LRESULT CALLBACK
DisplayProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)528 DisplayProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
529 {
530     switch (message)
531     {
532         case WM_CREATE:
533             return Display_OnCreate(hwnd);
534 
535         case WM_PAINT:
536             return Display_OnPaint(hwnd);
537 
538         case WM_SIZE:
539             return Display_OnSize(hwnd);
540 
541         case WM_VSCROLL:
542             return Display_OnVScroll(hwnd, wParam);
543 
544         case FVM_SETTYPEFACE:
545             return Display_SetTypeFace(hwnd, (PLOGFONTW)lParam);
546 
547         case FVM_SETSTRING:
548             return Display_SetString(hwnd, (WCHAR *)lParam);
549 
550         case FVM_GETFULLNAME:
551             return Display_GetFullName(hwnd, (INT)wParam, (PWSTR)lParam);
552 
553         case WM_DESTROY:
554             return Display_OnDestroy(hwnd);
555 
556         default:
557             return DefWindowProcW(hwnd, message, wParam, lParam);
558     }
559 
560     return 0;
561 }
562