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