1 /*
2 * Unit test suite for fonts
3 *
4 * Copyright 2002 Mike McCormack
5 * Copyright 2004 Dmitry Timoshkov
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 */
21
22 #include <stdarg.h>
23 #include <assert.h>
24
25 #include "windef.h"
26 #include "winbase.h"
27 #include "wingdi.h"
28 #include "winuser.h"
29 #include "winnls.h"
30
31 #include "wine/test.h"
32
33 #define near_match(a, b) (abs((a) - (b)) <= 6)
34 #define expect(expected, got) ok(got == expected, "Expected %.8x, got %.8x\n", expected, got)
35
36 LONG (WINAPI *pGdiGetCharDimensions)(HDC hdc, LPTEXTMETRICW lptm, LONG *height);
37 BOOL (WINAPI *pGetCharABCWidthsI)(HDC hdc, UINT first, UINT count, LPWORD glyphs, LPABC abc);
38 BOOL (WINAPI *pGetCharABCWidthsW)(HDC hdc, UINT first, UINT last, LPABC abc);
39 DWORD (WINAPI *pGetFontUnicodeRanges)(HDC hdc, LPGLYPHSET lpgs);
40 DWORD (WINAPI *pGetGlyphIndicesA)(HDC hdc, LPCSTR lpstr, INT count, LPWORD pgi, DWORD flags);
41 DWORD (WINAPI *pGetGlyphIndicesW)(HDC hdc, LPCWSTR lpstr, INT count, LPWORD pgi, DWORD flags);
42 BOOL (WINAPI *pGdiRealizationInfo)(HDC hdc, DWORD *);
43
44 static HMODULE hgdi32 = 0;
45
46 static void init(void)
47 {
48 hgdi32 = GetModuleHandleA("gdi32.dll");
49
50 pGdiGetCharDimensions = (void *)GetProcAddress(hgdi32, "GdiGetCharDimensions");
51 pGetCharABCWidthsI = (void *)GetProcAddress(hgdi32, "GetCharABCWidthsI");
52 pGetCharABCWidthsW = (void *)GetProcAddress(hgdi32, "GetCharABCWidthsW");
53 pGetFontUnicodeRanges = (void *)GetProcAddress(hgdi32, "GetFontUnicodeRanges");
54 pGetGlyphIndicesA = (void *)GetProcAddress(hgdi32, "GetGlyphIndicesA");
55 pGetGlyphIndicesW = (void *)GetProcAddress(hgdi32, "GetGlyphIndicesW");
56 pGdiRealizationInfo = (void *)GetProcAddress(hgdi32, "GdiRealizationInfo");
57 }
58
59 static INT CALLBACK is_truetype_font_installed_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
60 {
61 if (type != TRUETYPE_FONTTYPE) return 1;
62
63 return 0;
64 }
65
66 static BOOL is_truetype_font_installed(const char *name)
67 {
68 HDC hdc = GetDC(0);
69 BOOL ret = FALSE;
70
71 if (!EnumFontFamiliesA(hdc, name, is_truetype_font_installed_proc, 0))
72 ret = TRUE;
73
74 ReleaseDC(0, hdc);
75 return ret;
76 }
77
78 static INT CALLBACK is_font_installed_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
79 {
80 return 0;
81 }
82
83 static BOOL is_font_installed(const char *name)
84 {
85 HDC hdc = GetDC(0);
86 BOOL ret = FALSE;
87
88 if(!EnumFontFamiliesA(hdc, name, is_font_installed_proc, 0))
89 ret = TRUE;
90
91 ReleaseDC(0, hdc);
92 return ret;
93 }
94
95 static void check_font(const char* test, const LOGFONTA* lf, HFONT hfont)
96 {
97 LOGFONTA getobj_lf;
98 int ret, minlen = 0;
99
100 if (!hfont)
101 return;
102
103 ret = GetObject(hfont, sizeof(getobj_lf), &getobj_lf);
104 /* NT4 tries to be clever and only returns the minimum length */
105 while (lf->lfFaceName[minlen] && minlen < LF_FACESIZE-1)
106 minlen++;
107 minlen += FIELD_OFFSET(LOGFONTA, lfFaceName) + 1;
108 ok(ret == sizeof(LOGFONTA) || ret == minlen, "%s: GetObject returned %d\n", test, ret);
109 ok(!memcmp(&lf, &lf, FIELD_OFFSET(LOGFONTA, lfFaceName)), "%s: fonts don't match\n", test);
110 ok(!lstrcmpA(lf->lfFaceName, getobj_lf.lfFaceName),
111 "%s: font names don't match: %s != %s\n", test, lf->lfFaceName, getobj_lf.lfFaceName);
112 }
113
114 static HFONT create_font(const char* test, const LOGFONTA* lf)
115 {
116 HFONT hfont = CreateFontIndirectA(lf);
117 ok(hfont != 0, "%s: CreateFontIndirect failed\n", test);
118 if (hfont)
119 check_font(test, lf, hfont);
120 return hfont;
121 }
122
123 static void test_logfont(void)
124 {
125 LOGFONTA lf;
126 HFONT hfont;
127
128 memset(&lf, 0, sizeof lf);
129
130 lf.lfCharSet = ANSI_CHARSET;
131 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
132 lf.lfWeight = FW_DONTCARE;
133 lf.lfHeight = 16;
134 lf.lfWidth = 16;
135 lf.lfQuality = DEFAULT_QUALITY;
136
137 lstrcpyA(lf.lfFaceName, "Arial");
138 hfont = create_font("Arial", &lf);
139 DeleteObject(hfont);
140
141 memset(&lf, 'A', sizeof(lf));
142 hfont = CreateFontIndirectA(&lf);
143 ok(hfont != 0, "CreateFontIndirectA with strange LOGFONT failed\n");
144
145 lf.lfFaceName[LF_FACESIZE - 1] = 0;
146 check_font("AAA...", &lf, hfont);
147 DeleteObject(hfont);
148 }
149
150 static INT CALLBACK font_enum_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
151 {
152 if (type & RASTER_FONTTYPE)
153 {
154 LOGFONT *lf = (LOGFONT *)lParam;
155 *lf = *elf;
156 return 0; /* stop enumeration */
157 }
158
159 return 1; /* continue enumeration */
160 }
161
162 static void compare_tm(const TEXTMETRICA *tm, const TEXTMETRICA *otm)
163 {
164 ok(tm->tmHeight == otm->tmHeight, "tmHeight %d != %d\n", tm->tmHeight, otm->tmHeight);
165 ok(tm->tmAscent == otm->tmAscent, "tmAscent %d != %d\n", tm->tmAscent, otm->tmAscent);
166 ok(tm->tmDescent == otm->tmDescent, "tmDescent %d != %d\n", tm->tmDescent, otm->tmDescent);
167 ok(tm->tmInternalLeading == otm->tmInternalLeading, "tmInternalLeading %d != %d\n", tm->tmInternalLeading, otm->tmInternalLeading);
168 ok(tm->tmExternalLeading == otm->tmExternalLeading, "tmExternalLeading %d != %d\n", tm->tmExternalLeading, otm->tmExternalLeading);
169 ok(tm->tmAveCharWidth == otm->tmAveCharWidth, "tmAveCharWidth %d != %d\n", tm->tmAveCharWidth, otm->tmAveCharWidth);
170 ok(tm->tmMaxCharWidth == otm->tmMaxCharWidth, "tmMaxCharWidth %d != %d\n", tm->tmMaxCharWidth, otm->tmMaxCharWidth);
171 ok(tm->tmWeight == otm->tmWeight, "tmWeight %d != %d\n", tm->tmWeight, otm->tmWeight);
172 ok(tm->tmOverhang == otm->tmOverhang, "tmOverhang %d != %d\n", tm->tmOverhang, otm->tmOverhang);
173 ok(tm->tmDigitizedAspectX == otm->tmDigitizedAspectX, "tmDigitizedAspectX %d != %d\n", tm->tmDigitizedAspectX, otm->tmDigitizedAspectX);
174 ok(tm->tmDigitizedAspectY == otm->tmDigitizedAspectY, "tmDigitizedAspectY %d != %d\n", tm->tmDigitizedAspectY, otm->tmDigitizedAspectY);
175 ok(tm->tmFirstChar == otm->tmFirstChar, "tmFirstChar %d != %d\n", tm->tmFirstChar, otm->tmFirstChar);
176 ok(tm->tmLastChar == otm->tmLastChar, "tmLastChar %d != %d\n", tm->tmLastChar, otm->tmLastChar);
177 ok(tm->tmDefaultChar == otm->tmDefaultChar, "tmDefaultChar %d != %d\n", tm->tmDefaultChar, otm->tmDefaultChar);
178 ok(tm->tmBreakChar == otm->tmBreakChar, "tmBreakChar %d != %d\n", tm->tmBreakChar, otm->tmBreakChar);
179 ok(tm->tmItalic == otm->tmItalic, "tmItalic %d != %d\n", tm->tmItalic, otm->tmItalic);
180 ok(tm->tmUnderlined == otm->tmUnderlined, "tmUnderlined %d != %d\n", tm->tmUnderlined, otm->tmUnderlined);
181 ok(tm->tmStruckOut == otm->tmStruckOut, "tmStruckOut %d != %d\n", tm->tmStruckOut, otm->tmStruckOut);
182 ok(tm->tmPitchAndFamily == otm->tmPitchAndFamily, "tmPitchAndFamily %d != %d\n", tm->tmPitchAndFamily, otm->tmPitchAndFamily);
183 ok(tm->tmCharSet == otm->tmCharSet, "tmCharSet %d != %d\n", tm->tmCharSet, otm->tmCharSet);
184 }
185
186 static void test_font_metrics(HDC hdc, HFONT hfont, LONG lfHeight,
187 LONG lfWidth, const char *test_str,
188 INT test_str_len, const TEXTMETRICA *tm_orig,
189 const SIZE *size_orig, INT width_of_A_orig,
190 INT scale_x, INT scale_y)
191 {
192 HFONT old_hfont;
193 LOGFONTA lf;
194 OUTLINETEXTMETRIC otm;
195 TEXTMETRICA tm;
196 SIZE size;
197 INT width_of_A, cx, cy;
198 UINT ret;
199
200 if (!hfont)
201 return;
202
203 GetObjectA(hfont, sizeof(lf), &lf);
204
205 old_hfont = SelectObject(hdc, hfont);
206
207 if (GetOutlineTextMetricsA(hdc, 0, NULL))
208 {
209 otm.otmSize = sizeof(otm) / 2;
210 ret = GetOutlineTextMetricsA(hdc, otm.otmSize, &otm);
211 ok(ret == sizeof(otm)/2 /* XP */ ||
212 ret == 1 /* Win9x */, "expected sizeof(otm)/2, got %u\n", ret);
213
214 memset(&otm, 0x1, sizeof(otm));
215 otm.otmSize = sizeof(otm);
216 ret = GetOutlineTextMetricsA(hdc, otm.otmSize, &otm);
217 ok(ret == sizeof(otm) /* XP */ ||
218 ret == 1 /* Win9x */, "expected sizeof(otm), got %u\n", ret);
219
220 memset(&tm, 0x2, sizeof(tm));
221 ret = GetTextMetricsA(hdc, &tm);
222 ok(ret, "GetTextMetricsA failed\n");
223 /* the structure size is aligned */
224 if (memcmp(&tm, &otm.otmTextMetrics, FIELD_OFFSET(TEXTMETRICA, tmCharSet) + 1))
225 {
226 ok(0, "tm != otm\n");
227 compare_tm(&tm, &otm.otmTextMetrics);
228 }
229
230 tm = otm.otmTextMetrics;
231 if (0) /* these metrics are scaled too, but with rounding errors */
232 {
233 ok(otm.otmAscent == tm.tmAscent, "ascent %d != %d\n", otm.otmAscent, tm.tmAscent);
234 ok(otm.otmDescent == -tm.tmDescent, "descent %d != %d\n", otm.otmDescent, -tm.tmDescent);
235 }
236 ok(otm.otmMacAscent == tm.tmAscent, "ascent %d != %d\n", otm.otmMacAscent, tm.tmAscent);
237 ok(otm.otmDescent < 0, "otm.otmDescent should be < 0\n");
238 ok(otm.otmMacDescent < 0, "otm.otmMacDescent should be < 0\n");
239 ok(tm.tmDescent > 0, "tm.tmDescent should be > 0\n");
240 ok(otm.otmMacDescent == -tm.tmDescent, "descent %d != %d\n", otm.otmMacDescent, -tm.tmDescent);
241 ok(otm.otmEMSquare == 2048, "expected 2048, got %d\n", otm.otmEMSquare);
242 }
243 else
244 {
245 ret = GetTextMetricsA(hdc, &tm);
246 ok(ret, "GetTextMetricsA failed\n");
247 }
248
249 cx = tm.tmAveCharWidth / tm_orig->tmAveCharWidth;
250 cy = tm.tmHeight / tm_orig->tmHeight;
251 ok(cx == scale_x && cy == scale_y, "expected scale_x %d, scale_y %d, got cx %d, cy %d\n",
252 scale_x, scale_y, cx, cy);
253 ok(tm.tmHeight == tm_orig->tmHeight * scale_y, "height %d != %d\n", tm.tmHeight, tm_orig->tmHeight * scale_y);
254 ok(tm.tmAscent == tm_orig->tmAscent * scale_y, "ascent %d != %d\n", tm.tmAscent, tm_orig->tmAscent * scale_y);
255 ok(tm.tmDescent == tm_orig->tmDescent * scale_y, "descent %d != %d\n", tm.tmDescent, tm_orig->tmDescent * scale_y);
256 ok(near_match(tm.tmAveCharWidth, tm_orig->tmAveCharWidth * scale_x), "ave width %d != %d\n", tm.tmAveCharWidth, tm_orig->tmAveCharWidth * scale_x);
257 ok(near_match(tm.tmMaxCharWidth, tm_orig->tmMaxCharWidth * scale_x), "max width %d != %d\n", tm.tmMaxCharWidth, tm_orig->tmMaxCharWidth * scale_x);
258
259 ok(lf.lfHeight == lfHeight, "lfHeight %d != %d\n", lf.lfHeight, lfHeight);
260 if (lf.lfHeight)
261 {
262 if (lf.lfWidth)
263 ok(lf.lfWidth == tm.tmAveCharWidth, "lfWidth %d != tm %d\n", lf.lfWidth, tm.tmAveCharWidth);
264 }
265 else
266 ok(lf.lfWidth == lfWidth, "lfWidth %d != %d\n", lf.lfWidth, lfWidth);
267
268 GetTextExtentPoint32A(hdc, test_str, test_str_len, &size);
269
270 ok(near_match(size.cx, size_orig->cx * scale_x), "cx %d != %d\n", size.cx, size_orig->cx * scale_x);
271 ok(size.cy == size_orig->cy * scale_y, "cy %d != %d\n", size.cy, size_orig->cy * scale_y);
272
273 GetCharWidthA(hdc, 'A', 'A', &width_of_A);
274
275 ok(near_match(width_of_A, width_of_A_orig * scale_x), "width A %d != %d\n", width_of_A, width_of_A_orig * scale_x);
276
277 SelectObject(hdc, old_hfont);
278 }
279
280 /* Test how GDI scales bitmap font metrics */
281 static void test_bitmap_font(void)
282 {
283 static const char test_str[11] = "Test String";
284 HDC hdc;
285 LOGFONTA bitmap_lf;
286 HFONT hfont, old_hfont;
287 TEXTMETRICA tm_orig;
288 SIZE size_orig;
289 INT ret, i, width_orig, height_orig, scale, lfWidth;
290
291 hdc = GetDC(0);
292
293 /* "System" has only 1 pixel size defined, otherwise the test breaks */
294 ret = EnumFontFamiliesA(hdc, "System", font_enum_proc, (LPARAM)&bitmap_lf);
295 if (ret)
296 {
297 ReleaseDC(0, hdc);
298 trace("no bitmap fonts were found, skipping the test\n");
299 return;
300 }
301
302 trace("found bitmap font %s, height %d\n", bitmap_lf.lfFaceName, bitmap_lf.lfHeight);
303
304 height_orig = bitmap_lf.lfHeight;
305 lfWidth = bitmap_lf.lfWidth;
306
307 hfont = create_font("bitmap", &bitmap_lf);
308 old_hfont = SelectObject(hdc, hfont);
309 ok(GetTextMetricsA(hdc, &tm_orig), "GetTextMetricsA failed\n");
310 ok(GetTextExtentPoint32A(hdc, test_str, sizeof(test_str), &size_orig), "GetTextExtentPoint32A failed\n");
311 ok(GetCharWidthA(hdc, 'A', 'A', &width_orig), "GetCharWidthA failed\n");
312 SelectObject(hdc, old_hfont);
313 DeleteObject(hfont);
314
315 bitmap_lf.lfHeight = 0;
316 bitmap_lf.lfWidth = 4;
317 hfont = create_font("bitmap", &bitmap_lf);
318 test_font_metrics(hdc, hfont, 0, 4, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 1, 1);
319 DeleteObject(hfont);
320
321 bitmap_lf.lfHeight = height_orig;
322 bitmap_lf.lfWidth = lfWidth;
323
324 /* test fractional scaling */
325 for (i = 1; i <= height_orig * 3; i++)
326 {
327 INT nearest_height;
328
329 bitmap_lf.lfHeight = i;
330 hfont = create_font("fractional", &bitmap_lf);
331 scale = (i + height_orig - 1) / height_orig;
332 nearest_height = scale * height_orig;
333 /* XP allows not more than 10% deviation */
334 if (scale > 1 && nearest_height - i > nearest_height / 10) scale--;
335 test_font_metrics(hdc, hfont, bitmap_lf.lfHeight, 0, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 1, scale);
336 DeleteObject(hfont);
337 }
338
339 /* test integer scaling 3x2 */
340 bitmap_lf.lfHeight = height_orig * 2;
341 bitmap_lf.lfWidth *= 3;
342 hfont = create_font("3x2", &bitmap_lf);
343 test_font_metrics(hdc, hfont, bitmap_lf.lfHeight, 0, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 3, 2);
344 DeleteObject(hfont);
345
346 /* test integer scaling 3x3 */
347 bitmap_lf.lfHeight = height_orig * 3;
348 bitmap_lf.lfWidth = 0;
349 hfont = create_font("3x3", &bitmap_lf);
350 test_font_metrics(hdc, hfont, bitmap_lf.lfHeight, 0, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 3, 3);
351 DeleteObject(hfont);
352
353 ReleaseDC(0, hdc);
354 }
355
356 /* Test how GDI scales outline font metrics */
357 static void test_outline_font(void)
358 {
359 static const char test_str[11] = "Test String";
360 HDC hdc;
361 LOGFONTA lf;
362 HFONT hfont, old_hfont;
363 OUTLINETEXTMETRICA otm;
364 SIZE size_orig;
365 INT width_orig, height_orig, lfWidth;
366 XFORM xform;
367 GLYPHMETRICS gm;
368 MAT2 mat = { {0,1}, {0,0}, {0,0}, {0,1} };
369 MAT2 mat2 = { {0x8000,0}, {0,0}, {0,0}, {0x8000,0} };
370 POINT pt;
371 INT ret;
372
373 if (!is_truetype_font_installed("Arial"))
374 {
375 skip("Arial is not installed\n");
376 return;
377 }
378
379 hdc = CreateCompatibleDC(0);
380
381 memset(&lf, 0, sizeof(lf));
382 strcpy(lf.lfFaceName, "Arial");
383 lf.lfHeight = 72;
384 hfont = create_font("outline", &lf);
385 old_hfont = SelectObject(hdc, hfont);
386 otm.otmSize = sizeof(otm);
387 ok(GetOutlineTextMetricsA(hdc, sizeof(otm), &otm), "GetTextMetricsA failed\n");
388 ok(GetTextExtentPoint32A(hdc, test_str, sizeof(test_str), &size_orig), "GetTextExtentPoint32A failed\n");
389 ok(GetCharWidthA(hdc, 'A', 'A', &width_orig), "GetCharWidthA failed\n");
390 SelectObject(hdc, old_hfont);
391
392 test_font_metrics(hdc, hfont, lf.lfHeight, otm.otmTextMetrics.tmAveCharWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
393 DeleteObject(hfont);
394
395 /* font of otmEMSquare height helps to avoid a lot of rounding errors */
396 lf.lfHeight = otm.otmEMSquare;
397 lf.lfHeight = -lf.lfHeight;
398 hfont = create_font("outline", &lf);
399 old_hfont = SelectObject(hdc, hfont);
400 otm.otmSize = sizeof(otm);
401 ok(GetOutlineTextMetricsA(hdc, sizeof(otm), &otm), "GetTextMetricsA failed\n");
402 ok(GetTextExtentPoint32A(hdc, test_str, sizeof(test_str), &size_orig), "GetTextExtentPoint32A failed\n");
403 ok(GetCharWidthA(hdc, 'A', 'A', &width_orig), "GetCharWidthA failed\n");
404 SelectObject(hdc, old_hfont);
405 DeleteObject(hfont);
406
407 height_orig = otm.otmTextMetrics.tmHeight;
408 lfWidth = otm.otmTextMetrics.tmAveCharWidth;
409
410 /* test integer scaling 3x2 */
411 lf.lfHeight = height_orig * 2;
412 lf.lfWidth = lfWidth * 3;
413 hfont = create_font("3x2", &lf);
414 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 3, 2);
415 DeleteObject(hfont);
416
417 /* test integer scaling 3x3 */
418 lf.lfHeight = height_orig * 3;
419 lf.lfWidth = lfWidth * 3;
420 hfont = create_font("3x3", &lf);
421 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 3, 3);
422 DeleteObject(hfont);
423
424 /* test integer scaling 1x1 */
425 lf.lfHeight = height_orig * 1;
426 lf.lfWidth = lfWidth * 1;
427 hfont = create_font("1x1", &lf);
428 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
429 DeleteObject(hfont);
430
431 /* test integer scaling 1x1 */
432 lf.lfHeight = height_orig;
433 lf.lfWidth = 0;
434 hfont = create_font("1x1", &lf);
435 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
436
437 old_hfont = SelectObject(hdc, hfont);
438 /* with an identity matrix */
439 memset(&gm, 0, sizeof(gm));
440 SetLastError(0xdeadbeef);
441 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
442 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
443 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
444 ok(gm.gmCellIncX == width_orig, "incX %d != %d\n", gm.gmCellIncX, width_orig);
445 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
446 /* with a custom matrix */
447 memset(&gm, 0, sizeof(gm));
448 SetLastError(0xdeadbeef);
449 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat2);
450 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
451 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
452 ok(gm.gmCellIncX == width_orig/2, "incX %d != %d\n", gm.gmCellIncX, width_orig/2);
453 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
454 SelectObject(hdc, old_hfont);
455
456 SetMapMode(hdc, MM_ANISOTROPIC);
457 /* test restrictions of compatibility mode GM_COMPATIBLE */
458 /* part 1: rescaling only X should not change font scaling on screen.
459 So compressing the X axis by 2 is not done, and this
460 appears as X scaling of 2 that no one requested. */
461 SetWindowExtEx(hdc, 100, 100, NULL);
462 SetViewportExtEx(hdc, 50, 100, NULL);
463 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 2, 1);
464
465 /* part 2: rescaling only Y should change font scaling.
466 As also X is scaled by a factor of 2, but this is not
467 requested by the DC transformation, we get a scaling factor
468 of 2 in the X coordinate. */
469 SetViewportExtEx(hdc, 100, 200, NULL);
470 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 2, 1);
471
472 /* restore scaling */
473 SetMapMode(hdc, MM_TEXT);
474
475 if (!SetGraphicsMode(hdc, GM_ADVANCED))
476 {
477 DeleteObject(hfont);
478 DeleteDC(hdc);
479 skip("GM_ADVANCED is not supported on this platform\n");
480 return;
481 }
482
483 xform.eM11 = 20.0f;
484 xform.eM12 = 0.0f;
485 xform.eM21 = 0.0f;
486 xform.eM22 = 20.0f;
487 xform.eDx = 0.0f;
488 xform.eDy = 0.0f;
489
490 SetLastError(0xdeadbeef);
491 ret = SetWorldTransform(hdc, &xform);
492 ok(ret, "SetWorldTransform error %u\n", GetLastError());
493
494 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
495
496 old_hfont = SelectObject(hdc, hfont);
497 /* with an identity matrix */
498 memset(&gm, 0, sizeof(gm));
499 SetLastError(0xdeadbeef);
500 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
501 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
502 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
503 pt.x = width_orig; pt.y = 0;
504 LPtoDP(hdc, &pt, 1);
505 ok(gm.gmCellIncX == pt.x, "incX %d != %d\n", gm.gmCellIncX, pt.x);
506 ok(gm.gmCellIncX == 20 * width_orig, "incX %d != %d\n", gm.gmCellIncX, 20 * width_orig);
507 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
508 /* with a custom matrix */
509 memset(&gm, 0, sizeof(gm));
510 SetLastError(0xdeadbeef);
511 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat2);
512 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
513 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
514 pt.x = width_orig; pt.y = 0;
515 LPtoDP(hdc, &pt, 1);
516 ok(gm.gmCellIncX == pt.x/2, "incX %d != %d\n", gm.gmCellIncX, pt.x/2);
517 ok(near_match(gm.gmCellIncX, 10 * width_orig), "incX %d != %d\n", gm.gmCellIncX, 10 * width_orig);
518 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
519 SelectObject(hdc, old_hfont);
520
521 SetLastError(0xdeadbeef);
522 ret = SetMapMode(hdc, MM_LOMETRIC);
523 ok(ret == MM_TEXT, "expected MM_TEXT, got %d, error %u\n", ret, GetLastError());
524
525 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
526
527 old_hfont = SelectObject(hdc, hfont);
528 /* with an identity matrix */
529 memset(&gm, 0, sizeof(gm));
530 SetLastError(0xdeadbeef);
531 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
532 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
533 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
534 pt.x = width_orig; pt.y = 0;
535 LPtoDP(hdc, &pt, 1);
536 ok(near_match(gm.gmCellIncX, pt.x), "incX %d != %d\n", gm.gmCellIncX, pt.x);
537 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
538 /* with a custom matrix */
539 memset(&gm, 0, sizeof(gm));
540 SetLastError(0xdeadbeef);
541 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat2);
542 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
543 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
544 pt.x = width_orig; pt.y = 0;
545 LPtoDP(hdc, &pt, 1);
546 ok(near_match(gm.gmCellIncX, (pt.x + 1)/2), "incX %d != %d\n", gm.gmCellIncX, (pt.x + 1)/2);
547 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
548 SelectObject(hdc, old_hfont);
549
550 SetLastError(0xdeadbeef);
551 ret = SetMapMode(hdc, MM_TEXT);
552 ok(ret == MM_LOMETRIC, "expected MM_LOMETRIC, got %d, error %u\n", ret, GetLastError());
553
554 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
555
556 old_hfont = SelectObject(hdc, hfont);
557 /* with an identity matrix */
558 memset(&gm, 0, sizeof(gm));
559 SetLastError(0xdeadbeef);
560 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
561 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
562 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
563 pt.x = width_orig; pt.y = 0;
564 LPtoDP(hdc, &pt, 1);
565 ok(gm.gmCellIncX == pt.x, "incX %d != %d\n", gm.gmCellIncX, pt.x);
566 ok(gm.gmCellIncX == 20 * width_orig, "incX %d != %d\n", gm.gmCellIncX, 20 * width_orig);
567 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
568 /* with a custom matrix */
569 memset(&gm, 0, sizeof(gm));
570 SetLastError(0xdeadbeef);
571 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat2);
572 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
573 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
574 pt.x = width_orig; pt.y = 0;
575 LPtoDP(hdc, &pt, 1);
576 ok(gm.gmCellIncX == pt.x/2, "incX %d != %d\n", gm.gmCellIncX, pt.x/2);
577 ok(gm.gmCellIncX == 10 * width_orig, "incX %d != %d\n", gm.gmCellIncX, 10 * width_orig);
578 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
579 SelectObject(hdc, old_hfont);
580
581 DeleteObject(hfont);
582 DeleteDC(hdc);
583 }
584
585 static INT CALLBACK find_font_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
586 {
587 LOGFONT *lf = (LOGFONT *)lParam;
588
589 if (elf->lfHeight == lf->lfHeight && !strcmp(elf->lfFaceName, lf->lfFaceName))
590 {
591 *lf = *elf;
592 return 0; /* stop enumeration */
593 }
594 return 1; /* continue enumeration */
595 }
596
597 static void test_bitmap_font_metrics(void)
598 {
599 static const struct font_data
600 {
601 const char face_name[LF_FACESIZE];
602 int weight, height, ascent, descent, int_leading, ext_leading;
603 int ave_char_width, max_char_width;
604 DWORD ansi_bitfield;
605 } fd[] =
606 {
607 { "MS Sans Serif", FW_NORMAL, 13, 11, 2, 2, 0, 5, 11, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
608 { "MS Sans Serif", FW_NORMAL, 16, 13, 3, 3, 0, 7, 14, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
609 { "MS Sans Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 16, FS_LATIN1 | FS_CYRILLIC },
610 { "MS Sans Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 18, FS_LATIN2 },
611 { "MS Sans Serif", FW_NORMAL, 24, 19, 5, 6, 0, 9, 19, FS_LATIN1 },
612 { "MS Sans Serif", FW_NORMAL, 24, 19, 5, 6, 0, 9, 24, FS_LATIN2 },
613 { "MS Sans Serif", FW_NORMAL, 24, 19, 5, 6, 0, 9, 20, FS_CYRILLIC },
614 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 5, 0, 12, 24, FS_LATIN1 },
615 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 6, 0, 12, 24, FS_LATIN2 },
616 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 5, 0, 12, 25, FS_CYRILLIC },
617 { "MS Sans Serif", FW_NORMAL, 37, 29, 8, 5, 0, 16, 32, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
618 { "MS Serif", FW_NORMAL, 10, 8, 2, 2, 0, 4, 8, FS_LATIN1 | FS_LATIN2 },
619 { "MS Serif", FW_NORMAL, 10, 8, 2, 2, 0, 5, 8, FS_CYRILLIC },
620 { "MS Serif", FW_NORMAL, 11, 9, 2, 2, 0, 5, 9, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
621 { "MS Serif", FW_NORMAL, 13, 11, 2, 2, 0, 5, 11, FS_LATIN1 },
622 { "MS Serif", FW_NORMAL, 13, 11, 2, 2, 0, 5, 12, FS_LATIN2 | FS_CYRILLIC },
623 { "MS Serif", FW_NORMAL, 16, 13, 3, 3, 0, 6, 14, FS_LATIN1 | FS_LATIN2 },
624 { "MS Serif", FW_NORMAL, 16, 13, 3, 3, 0, 6, 16, FS_CYRILLIC },
625 { "MS Serif", FW_NORMAL, 19, 15, 4, 3, 0, 8, 18, FS_LATIN1 | FS_LATIN2 },
626 { "MS Serif", FW_NORMAL, 19, 15, 4, 3, 0, 8, 19, FS_CYRILLIC },
627 { "MS Serif", FW_NORMAL, 21, 16, 5, 3, 0, 9, 17, FS_LATIN1 },
628 { "MS Serif", FW_NORMAL, 21, 16, 5, 3, 0, 9, 22, FS_LATIN2 },
629 { "MS Serif", FW_NORMAL, 21, 16, 5, 3, 0, 9, 23, FS_CYRILLIC },
630 { "MS Serif", FW_NORMAL, 27, 21, 6, 3, 0, 12, 23, FS_LATIN1 },
631 { "MS Serif", FW_NORMAL, 27, 21, 6, 3, 0, 12, 26, FS_LATIN2 },
632 { "MS Serif", FW_NORMAL, 27, 21, 6, 3, 0, 12, 27, FS_CYRILLIC },
633 { "MS Serif", FW_NORMAL, 35, 27, 8, 3, 0, 16, 33, FS_LATIN1 | FS_LATIN2 },
634 { "MS Serif", FW_NORMAL, 35, 27, 8, 3, 0, 16, 34, FS_CYRILLIC },
635 { "Courier", FW_NORMAL, 13, 11, 2, 0, 0, 8, 8, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
636 { "Courier", FW_NORMAL, 16, 13, 3, 0, 0, 9, 9, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
637 { "Courier", FW_NORMAL, 20, 16, 4, 0, 0, 12, 12, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
638 { "System", FW_BOLD, 16, 13, 3, 3, 0, 7, 14, FS_LATIN1 },
639 { "System", FW_BOLD, 16, 13, 3, 3, 0, 7, 15, FS_LATIN2 | FS_CYRILLIC },
640 /*
641 * TODO: the system for CP932 should be NORMAL, not BOLD. However that would
642 * require a new system.sfd for that font
643 */
644 { "System", FW_BOLD, 18, 16, 2, 0, 2, 8, 16, FS_JISJAPAN },
645 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 1, 2, FS_LATIN1 },
646 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 1, 8, FS_LATIN2 | FS_CYRILLIC },
647 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 2, 4, FS_JISJAPAN },
648 { "Small Fonts", FW_NORMAL, 5, 4, 1, 1, 0, 3, 4, FS_LATIN1 },
649 { "Small Fonts", FW_NORMAL, 5, 4, 1, 1, 0, 2, 8, FS_LATIN2 | FS_CYRILLIC },
650 { "Small Fonts", FW_NORMAL, 5, 4, 1, 0, 0, 3, 6, FS_JISJAPAN },
651 { "Small Fonts", FW_NORMAL, 6, 5, 1, 1, 0, 3, 13, FS_LATIN1 },
652 { "Small Fonts", FW_NORMAL, 6, 5, 1, 1, 0, 3, 8, FS_LATIN2 | FS_CYRILLIC },
653 { "Small Fonts", FW_NORMAL, 6, 5, 1, 0, 0, 4, 8, FS_JISJAPAN },
654 { "Small Fonts", FW_NORMAL, 8, 7, 1, 1, 0, 4, 7, FS_LATIN1 },
655 { "Small Fonts", FW_NORMAL, 8, 7, 1, 1, 0, 4, 8, FS_LATIN2 | FS_CYRILLIC },
656 { "Small Fonts", FW_NORMAL, 8, 7, 1, 0, 0, 5, 10, FS_JISJAPAN },
657 { "Small Fonts", FW_NORMAL, 10, 8, 2, 2, 0, 4, 8, FS_LATIN1 | FS_LATIN2 },
658 { "Small Fonts", FW_NORMAL, 10, 8, 2, 2, 0, 5, 8, FS_CYRILLIC },
659 { "Small Fonts", FW_NORMAL, 10, 8, 2, 0, 0, 6, 12, FS_JISJAPAN },
660 { "Small Fonts", FW_NORMAL, 11, 9, 2, 2, 0, 5, 9, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
661 { "Small Fonts", FW_NORMAL, 11, 9, 2, 0, 0, 7, 14, FS_JISJAPAN },
662 { "Fixedsys", FW_NORMAL, 15, 12, 3, 3, 0, 8, 8, FS_LATIN1 | FS_LATIN2 },
663 { "Fixedsys", FW_NORMAL, 16, 12, 4, 3, 0, 8, 8, FS_CYRILLIC },
664 { "FixedSys", FW_NORMAL, 18, 16, 2, 0, 0, 8, 16, FS_JISJAPAN }
665
666 /* FIXME: add "Terminal" */
667 };
668 HDC hdc;
669 LOGFONT lf;
670 HFONT hfont, old_hfont;
671 TEXTMETRIC tm;
672 INT ret, i;
673
674 hdc = CreateCompatibleDC(0);
675 assert(hdc);
676
677 for (i = 0; i < sizeof(fd)/sizeof(fd[0]); i++)
678 {
679 int bit;
680
681 memset(&lf, 0, sizeof(lf));
682
683 lf.lfHeight = fd[i].height;
684 strcpy(lf.lfFaceName, fd[i].face_name);
685
686 for(bit = 0; bit < 32; bit++)
687 {
688 DWORD fs[2];
689 CHARSETINFO csi;
690
691 fs[0] = 1L << bit;
692 fs[1] = 0;
693 if((fd[i].ansi_bitfield & fs[0]) == 0) continue;
694 if(!TranslateCharsetInfo( fs, &csi, TCI_SRCFONTSIG )) continue;
695
696 lf.lfCharSet = csi.ciCharset;
697 ret = EnumFontFamiliesEx(hdc, &lf, find_font_proc, (LPARAM)&lf, 0);
698 if (ret) continue;
699
700 trace("found font %s, height %d charset %x\n", lf.lfFaceName, lf.lfHeight, lf.lfCharSet);
701
702 hfont = create_font(lf.lfFaceName, &lf);
703 old_hfont = SelectObject(hdc, hfont);
704 ok(GetTextMetrics(hdc, &tm), "GetTextMetrics error %d\n", GetLastError());
705
706 ok(tm.tmWeight == fd[i].weight, "%s(%d): tm.tmWeight %d != %d\n", fd[i].face_name, fd[i].height, tm.tmWeight, fd[i].weight);
707 ok(tm.tmHeight == fd[i].height, "%s(%d): tm.tmHeight %d != %d\n", fd[i].face_name, fd[i].height, tm.tmHeight, fd[i].height);
708 ok(tm.tmAscent == fd[i].ascent, "%s(%d): tm.tmAscent %d != %d\n", fd[i].face_name, fd[i].height, tm.tmAscent, fd[i].ascent);
709 ok(tm.tmDescent == fd[i].descent, "%s(%d): tm.tmDescent %d != %d\n", fd[i].face_name, fd[i].height, tm.tmDescent, fd[i].descent);
710 ok(tm.tmInternalLeading == fd[i].int_leading, "%s(%d): tm.tmInternalLeading %d != %d\n", fd[i].face_name, fd[i].height, tm.tmInternalLeading, fd[i].int_leading);
711 ok(tm.tmExternalLeading == fd[i].ext_leading, "%s(%d): tm.tmExternalLeading %d != %d\n", fd[i].face_name, fd[i].height, tm.tmExternalLeading, fd[i].ext_leading);
712 ok(tm.tmAveCharWidth == fd[i].ave_char_width, "%s(%d): tm.tmAveCharWidth %d != %d\n", fd[i].face_name, fd[i].height, tm.tmAveCharWidth, fd[i].ave_char_width);
713
714 /* Don't run the max char width test on System/ANSI_CHARSET. We have extra characters in our font
715 that make the max width bigger */
716 if(strcmp(lf.lfFaceName, "System") || lf.lfCharSet != ANSI_CHARSET)
717 ok(tm.tmMaxCharWidth == fd[i].max_char_width, "%s(%d): tm.tmMaxCharWidth %d != %d\n", fd[i].face_name, fd[i].height, tm.tmMaxCharWidth, fd[i].max_char_width);
718
719 SelectObject(hdc, old_hfont);
720 DeleteObject(hfont);
721 }
722 }
723
724 DeleteDC(hdc);
725 }
726
727 static void test_GdiGetCharDimensions(void)
728 {
729 HDC hdc;
730 TEXTMETRICW tm;
731 LONG ret;
732 SIZE size;
733 LONG avgwidth, height;
734 static const char szAlphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
735
736 if (!pGdiGetCharDimensions)
737 {
738 skip("GdiGetCharDimensions not available on this platform\n");
739 return;
740 }
741
742 hdc = CreateCompatibleDC(NULL);
743
744 GetTextExtentPoint(hdc, szAlphabet, strlen(szAlphabet), &size);
745 avgwidth = ((size.cx / 26) + 1) / 2;
746
747 ret = pGdiGetCharDimensions(hdc, &tm, &height);
748 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
749 ok(height == tm.tmHeight, "GdiGetCharDimensions should have set height to %d instead of %d\n", tm.tmHeight, height);
750
751 ret = pGdiGetCharDimensions(hdc, &tm, NULL);
752 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
753
754 ret = pGdiGetCharDimensions(hdc, NULL, NULL);
755 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
756
757 height = 0;
758 ret = pGdiGetCharDimensions(hdc, NULL, &height);
759 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
760 ok(height == size.cy, "GdiGetCharDimensions should have set height to %d instead of %d\n", size.cy, height);
761
762 DeleteDC(hdc);
763 }
764
765 static void test_GetCharABCWidths(void)
766 {
767 static const WCHAR str[] = {'a',0};
768 BOOL ret;
769 HDC hdc;
770 LOGFONTA lf;
771 HFONT hfont;
772 ABC abc[