1 /*
2  * PROJECT:         ReactOS api tests
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * PURPOSE:         Test for AddFontMemResourceEx
5  * PROGRAMMERS:     Mark Jansen
6  *
7  * PanosePitchTest + TTCTestV by Katayama Hirofumi MZ, licensed under CC BY
8  * Shadows_Into_Light by Kimberly Geswein, licensed under OFL
9  *                    Captured from firefox, embedded on reactos.org
10  */
11 
12 #include "precomp.h"
13 
14 typedef struct _fnt_res
15 {
16     const char* FontName;
17     TEXTMETRICA tm;
18 } fnt_res;
19 
20 typedef struct _fnt_test
21 {
22     const char* ResourceName;
23     int NumFaces;
24     fnt_res res[4];
25 } fnt_test;
26 
27 
28 
29 static fnt_test test_data[] =
30 {
31     {
32         /* .ResourceName = */ "PanosePitchTest.ttf",
33         /* .NumFaces = */ 2,
34         /* .res = */
35         {
36             {
37                 /* .FontName = */ "PanosePitchTest",
38                 {
39                 /* .tm.tmHeight = */ 11,
40                 /* .tm.tmAscent = */ 11,
41                 /* .tm.tmDescent = */ 0,
42                 /* .tm.tmInternalLeading = */ -5,
43                 /* .tm.tmExternalLeading = */ 1,
44                 /* .tm.tmAveCharWidth = */ 8,
45                 /* .tm.tmMaxCharWidth = */ 11,
46                 /* .tm.tmWeight = */ FW_NORMAL,
47                 /* .tm.tmOverhang = */ 0,
48                 /* .tm.tmDigitizedAspectX = */ 96,
49                 /* .tm.tmDigitizedAspectY = */ 96,
50                 /* .tm.tmFirstChar = */ 63,
51                 /* .tm.tmLastChar = */ 65,
52                 /* .tm.tmDefaultChar = */ 165,
53                 /* .tm.tmBreakChar = */ 65,
54                 /* .tm.tmItalic = */ 0,
55                 /* .tm.tmUnderlined = */ 0,
56                 /* .tm.tmStruckOut = */ 0,
57                 /* .tm.tmPitchAndFamily = */ TMPF_TRUETYPE | TMPF_VECTOR,
58                 /* .tm.tmCharSet = */ SHIFTJIS_CHARSET,
59                 }
60             },
61             {
62                 /* .FontName = */ "@PanosePitchTest",
63                 {
64                 /* .tm.tmHeight = */ 11,
65                 /* .tm.tmAscent = */ 11,
66                 /* .tm.tmDescent = */ 0,
67                 /* .tm.tmInternalLeading = */ -5,
68                 /* .tm.tmExternalLeading = */ 1,
69                 /* .tm.tmAveCharWidth = */ 8,
70                 /* .tm.tmMaxCharWidth = */ 11,
71                 /* .tm.tmWeight = */ FW_NORMAL,
72                 /* .tm.tmOverhang = */ 0,
73                 /* .tm.tmDigitizedAspectX = */ 96,
74                 /* .tm.tmDigitizedAspectY = */ 96,
75                 /* .tm.tmFirstChar = */ 63,
76                 /* .tm.tmLastChar = */ 65,
77                 /* .tm.tmDefaultChar = */ 165,
78                 /* .tm.tmBreakChar = */ 65,
79                 /* .tm.tmItalic = */ 0,
80                 /* .tm.tmUnderlined = */ 0,
81                 /* .tm.tmStruckOut = */ 0,
82                 /* .tm.tmPitchAndFamily = */ TMPF_TRUETYPE | TMPF_VECTOR,
83                 /* .tm.tmCharSet = */ SHIFTJIS_CHARSET,
84                 }
85             },
86         },
87     },
88     {
89         /* .ResourceName = */ "TTCTestV.ttc",
90         /* .NumFaces = */ 3,
91         /* .res = */
92         {
93             {
94                 /* .FontName = */ "No1Of3in1",
95                 {
96                 /* .tm.tmHeight = */ 12,
97                 /* .tm.tmAscent = */ 12,
98                 /* .tm.tmDescent = */ 0,
99                 /* .tm.tmInternalLeading = */ -4,
100                 /* .tm.tmExternalLeading = */ 1,
101                 /* .tm.tmAveCharWidth = */ -525,
102                 /* .tm.tmMaxCharWidth = */ 6,
103                 /* .tm.tmWeight = */ FW_NORMAL,
104                 /* .tm.tmOverhang = */ 0,
105                 /* .tm.tmDigitizedAspectX = */ 96,
106                 /* .tm.tmDigitizedAspectY = */ 96,
107                 /* .tm.tmFirstChar = */ 63,
108                 /* .tm.tmLastChar = */ 65,
109                 /* .tm.tmDefaultChar = */ 64,
110                 /* .tm.tmBreakChar = */ 65,
111                 /* .tm.tmItalic = */ 0,
112                 /* .tm.tmUnderlined = */ 0,
113                 /* .tm.tmStruckOut = */ 0,
114                 /* .tm.tmPitchAndFamily = */ TMPF_TRUETYPE | TMPF_VECTOR | TMPF_FIXED_PITCH,
115                 /* .tm.tmCharSet = */ ANSI_CHARSET,
116                 }
117             },
118             {
119                 /* .FontName = */ "No2Of3in1",
120                 {
121                 /* .tm.tmHeight = */ 12,
122                 /* .tm.tmAscent = */ 12,
123                 /* .tm.tmDescent = */ 0,
124                 /* .tm.tmInternalLeading = */ -4,
125                 /* .tm.tmExternalLeading = */ 1,
126                 /* .tm.tmAveCharWidth = */ 8,
127                 /* .tm.tmMaxCharWidth = */ 7,
128                 /* .tm.tmWeight = */ FW_NORMAL,
129                 /* .tm.tmOverhang = */ 0,
130                 /* .tm.tmDigitizedAspectX = */ 96,
131                 /* .tm.tmDigitizedAspectY = */ 96,
132                 /* .tm.tmFirstChar = */ 63,
133                 /* .tm.tmLastChar = */ 65,
134                 /* .tm.tmDefaultChar = */ 64,
135                 /* .tm.tmBreakChar = */ 65,
136                 /* .tm.tmItalic = */ 0,
137                 /* .tm.tmUnderlined = */ 0,
138                 /* .tm.tmStruckOut = */ 0,
139                 /* .tm.tmPitchAndFamily = */ TMPF_TRUETYPE | TMPF_VECTOR | TMPF_FIXED_PITCH,
140                 /* .tm.tmCharSet = */ ANSI_CHARSET,
141                 }
142             },
143             {
144                 /* .FontName = */ "No3Of3in1V",
145                 {
146                 /* .tm.tmHeight = */ 12,
147                 /* .tm.tmAscent = */ 12,
148                 /* .tm.tmDescent = */ 0,
149                 /* .tm.tmInternalLeading = */ -4,
150                 /* .tm.tmExternalLeading = */ 1,
151                 /* .tm.tmAveCharWidth = */ 8,
152                 /* .tm.tmMaxCharWidth = */ 13,
153                 /* .tm.tmWeight = */ FW_NORMAL,
154                 /* .tm.tmOverhang = */ 0,
155                 /* .tm.tmDigitizedAspectX = */ 96,
156                 /* .tm.tmDigitizedAspectY = */ 96,
157                 /* .tm.tmFirstChar = */ 63,
158                 /* .tm.tmLastChar = */ 65,
159                 /* .tm.tmDefaultChar = */ 64,
160                 /* .tm.tmBreakChar = */ 65,
161                 /* .tm.tmItalic = */ 0,
162                 /* .tm.tmUnderlined = */ 0,
163                 /* .tm.tmStruckOut = */ 0,
164                 /* .tm.tmPitchAndFamily = */ FF_MODERN | TMPF_TRUETYPE | TMPF_VECTOR,
165                 /* .tm.tmCharSet = */ ANSI_CHARSET,
166                 }
167             },
168         },
169     },
170     {
171         /* .ResourceName = */ "Shadows_Into_Light.ttf",
172         /* .NumFaces = */ 1,
173         /* .res = */
174         {
175             {
176                 /* .FontName = */ "ufaXaAlLOxCUGYJ7KN51UP2Q==",
177                 {
178                 /* .tm.tmHeight = */ 26,
179                 /* .tm.tmAscent = */ 19,
180                 /* .tm.tmDescent = */ 7,
181                 /* .tm.tmInternalLeading = */ 10,
182                 /* .tm.tmExternalLeading = */ 0,
183                 /* .tm.tmAveCharWidth = */ 7,
184                 /* .tm.tmMaxCharWidth = */ 23,
185                 /* .tm.tmWeight = */ FW_NORMAL,
186                 /* .tm.tmOverhang = */ 0,
187                 /* .tm.tmDigitizedAspectX = */ 96,
188                 /* .tm.tmDigitizedAspectY = */ 96,
189                 /* .tm.tmFirstChar = */ 30,
190                 /* .tm.tmLastChar = */ 255,
191                 /* .tm.tmDefaultChar = */ 31,
192                 /* .tm.tmBreakChar = */ 32,
193                 /* .tm.tmItalic = */ 0,
194                 /* .tm.tmUnderlined = */ 0,
195                 /* .tm.tmStruckOut = */ 0,
196                 /* .tm.tmPitchAndFamily = */ TMPF_TRUETYPE | TMPF_VECTOR | TMPF_FIXED_PITCH,
197                 /* .tm.tmCharSet = */ ANSI_CHARSET,
198                 }
199             },
200         },
201     },
202 };
203 
204 
205 #define ok_int2(expression) \
206     do { \
207         int _value = (expression); \
208         ok(_value == (res->expression), "Wrong value for '%s', expected: %d, got: %d for %s/%s\n", \
209            #expression, (int)(res->expression), _value, test_name, res->FontName); \
210     } while (0)
211 
212 #define ok_hex2(expression) \
213     do { \
214         int _value = (expression); \
215         ok(_value == (res->expression), "Wrong value for '%s', expected: 0x%x, got: 0x%x for %s/%s\n", \
216            #expression, (int)(res->expression), _value, test_name, res->FontName); \
217     } while (0)
218 
219 
220 static void test_font_caps(HDC hdc, int test_index)
221 {
222     HGDIOBJ old;
223     TEXTMETRICA tm = { 0 };
224     char name[64];
225     BOOL ret;
226     HFONT font;
227     int n;
228     const char* test_name = test_data[test_index].ResourceName;
229 
230     for (n = 0; test_data[test_index].res[n].FontName; ++n)
231     {
232         fnt_res* res = test_data[test_index].res + n;
233         font = CreateFontA(0, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, DEFAULT_CHARSET,
234                      OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, res->FontName);
235 
236         if (font)
237         {
238             old = SelectObject(hdc, font);
239 
240             memset(&tm, 0xaa, sizeof(tm));
241             ret = GetTextMetricsA(hdc, &tm);
242             ok(ret, "GetTextMetricsA() for %s/%s\n", test_name, res->FontName);
243 
244             SetLastError(0xdeadbeef);
245             ret = GetTextFaceA(hdc, sizeof(name), name);
246             ok(ret, "GetTextFaceA error %lu for %s/%s\n", GetLastError(), test_name, res->FontName);
247             if (ret)
248             {
249                 ok(!strcmp(name, res->FontName), "FontName was %s, expected %s for %s/%s", name, res->FontName, test_name, res->FontName);
250             }
251 
252             ok_int2(tm.tmHeight);
253             ok_int2(tm.tmAscent);
254             ok_int2(tm.tmDescent);
255             ok_int2(tm.tmInternalLeading);
256             ok_int2(tm.tmExternalLeading);
257             ok_int2(tm.tmAveCharWidth);
258             ok_int2(tm.tmMaxCharWidth);
259             ok_int2(tm.tmWeight);
260             ok_int2(tm.tmOverhang);
261             ok_int2(tm.tmDigitizedAspectX);
262             ok_int2(tm.tmDigitizedAspectY);
263             ok_int2(tm.tmFirstChar);
264             ok_int2(tm.tmLastChar);
265             ok_int2(tm.tmDefaultChar);
266             ok_int2(tm.tmBreakChar);
267             ok_int2(tm.tmItalic);
268             ok_int2(tm.tmUnderlined);
269             ok_int2(tm.tmStruckOut);
270             ok_hex2(tm.tmPitchAndFamily);
271             ok_int2(tm.tmCharSet);
272 
273             SelectObject(hdc, old);
274             DeleteObject(font);
275         }
276     }
277 }
278 
279 
280 /* Not working as of 2017-04-08 on ReactOS */
281 static BOOL is_font_available(HDC hdc, const char* fontName)
282 {
283     char name[64];
284     BOOL ret;
285 
286     HFONT font = CreateFontA(0, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, DEFAULT_CHARSET,
287                             OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, fontName);
288     HGDIOBJ old = SelectObject(hdc, font);
289 
290 
291     SetLastError(0xdeadbeef);
292 
293     ret = GetTextFaceA(hdc, sizeof(name), name);
294     ok(ret, "GetTextFaceA error %lu for %s\n", GetLastError(), fontName);
295     SelectObject(hdc, old);
296     DeleteObject(font);
297 
298     if (ret)
299     {
300         return !_strcmpi(name, fontName);
301     }
302     return FALSE;
303 }
304 
305 
306 
307 START_TEST(AddFontMemResourceEx)
308 {
309     HMODULE mod;
310     HRSRC hRsrc;
311 
312     HGLOBAL hTemplate;
313     DWORD dwSize, dwNumFonts;
314     LPVOID pFont;
315 
316     HANDLE hFont;
317     fnt_test* data;
318     int n;
319 
320     HDC hdc = CreateCompatibleDC(NULL);
321     BOOL is_font_available_broken = is_font_available(hdc, "Nonexisting font name here");
322 
323     ok(!is_font_available_broken, "Validating font is broken! (CORE-13053)!\n");
324 
325     for (n = 0; n < _countof(test_data); ++n)
326     {
327         data = test_data + n;
328 
329         mod = GetModuleHandle(NULL);
330         hRsrc = FindResourceA(mod, data->ResourceName, MAKEINTRESOURCE(RT_RCDATA));
331 
332         hTemplate = LoadResource(mod, hRsrc);
333         dwSize = SizeofResource(mod, hRsrc);
334         pFont = LockResource(hTemplate);
335 
336         dwNumFonts = 0;
337         hFont = AddFontMemResourceEx(pFont, dwSize, NULL, &dwNumFonts);
338         ok(dwNumFonts == data->NumFaces, "dwNumFonts was %lu, expected %d for %s\n", dwNumFonts, data->NumFaces, data->ResourceName);
339         ok(hFont != NULL, "Expected valid handle for %s\n", data->ResourceName);
340 
341         if (hFont)
342         {
343             test_font_caps(hdc, n);
344             RemoveFontMemResourceEx(hFont);
345             if (!is_font_available_broken)
346             {
347                 ok (!is_font_available(hdc, data->ResourceName), "Expected font to be unregistered again for %s\n", data->ResourceName);
348             }
349             else
350             {
351                 skip("Font unregister test for %s\n", data->ResourceName);
352             }
353         }
354 
355         UnlockResource(hTemplate);
356         FreeResource(hTemplate);
357     }
358 
359     DeleteDC(hdc);
360 }
361 
362