2 * Unit test suite for fonts
4 * Copyright 2002 Mike McCormack
5 * Copyright 2004 Dmitry Timoshkov
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.
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.
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
31 #include "wine/test.h"
33 #define expect(expected, got) ok(got == expected, "Expected %.8x, got %.8x\n", expected, got)
35 LONG (WINAPI *pGdiGetCharDimensions)(HDC hdc, LPTEXTMETRICW lptm, LONG *height);
36 BOOL (WINAPI *pGetCharABCWidthsW)(HDC hdc, UINT first, UINT last, LPABC abc);
37 DWORD (WINAPI *pGetFontUnicodeRanges)(HDC hdc, LPGLYPHSET lpgs);
38 DWORD (WINAPI *pGetGlyphIndicesA)(HDC hdc, LPCSTR lpstr, INT count, LPWORD pgi, DWORD flags);
39 DWORD (WINAPI *pGetGlyphIndicesW)(HDC hdc, LPCWSTR lpstr, INT count, LPWORD pgi, DWORD flags);
41 static HMODULE hgdi32 = 0;
43 static void init(void)
45 hgdi32 = GetModuleHandleA("gdi32.dll");
47 pGdiGetCharDimensions = (void *)GetProcAddress(hgdi32, "GdiGetCharDimensions");
48 pGetCharABCWidthsW = (void *)GetProcAddress(hgdi32, "GetCharABCWidthsW");
49 pGetFontUnicodeRanges = (void *)GetProcAddress(hgdi32, "GetFontUnicodeRanges");
50 pGetGlyphIndicesA = (void *)GetProcAddress(hgdi32, "GetGlyphIndicesA");
51 pGetGlyphIndicesW = (void *)GetProcAddress(hgdi32, "GetGlyphIndicesW");
54 static INT CALLBACK is_truetype_font_installed_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
56 if (type != TRUETYPE_FONTTYPE) return 1;
61 static BOOL is_truetype_font_installed(const char *name)
66 if (!EnumFontFamiliesA(hdc, name, is_truetype_font_installed_proc, 0))
73 static INT CALLBACK is_font_installed_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
78 static BOOL is_font_installed(const char *name)
83 if(!EnumFontFamiliesA(hdc, name, is_font_installed_proc, 0))
90 static void check_font(const char* test, const LOGFONTA* lf, HFONT hfont)
98 ret = GetObject(hfont, sizeof(getobj_lf), &getobj_lf);
99 /* NT4 tries to be clever and only returns the minimum length */
100 while (lf->lfFaceName[minlen] && minlen < LF_FACESIZE-1)
102 minlen += FIELD_OFFSET(LOGFONTA, lfFaceName) + 1;
103 ok(ret == sizeof(LOGFONTA) || ret == minlen, "%s: GetObject returned %d\n", test, ret);
104 ok(!memcmp(&lf, &lf, FIELD_OFFSET(LOGFONTA, lfFaceName)), "%s: fonts don't match\n", test);
105 ok(!lstrcmpA(lf->lfFaceName, getobj_lf.lfFaceName),
106 "%s: font names don't match: %s != %s\n", test, lf->lfFaceName, getobj_lf.lfFaceName);
109 static HFONT create_font(const char* test, const LOGFONTA* lf)
111 HFONT hfont = CreateFontIndirectA(lf);
112 ok(hfont != 0, "%s: CreateFontIndirect failed\n", test);
114 check_font(test, lf, hfont);
118 static void test_logfont(void)
123 memset(&lf, 0, sizeof lf);
125 lf.lfCharSet = ANSI_CHARSET;
126 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
127 lf.lfWeight = FW_DONTCARE;
130 lf.lfQuality = DEFAULT_QUALITY;
132 lstrcpyA(lf.lfFaceName, "Arial");
133 hfont = create_font("Arial", &lf);
136 memset(&lf, 'A', sizeof(lf));
137 hfont = CreateFontIndirectA(&lf);
138 ok(hfont != 0, "CreateFontIndirectA with strange LOGFONT failed\n");
140 lf.lfFaceName[LF_FACESIZE - 1] = 0;
141 check_font("AAA...", &lf, hfont);
145 static INT CALLBACK font_enum_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
147 if (type & RASTER_FONTTYPE)
149 LOGFONT *lf = (LOGFONT *)lParam;
151 return 0; /* stop enumeration */
154 return 1; /* continue enumeration */
157 static void test_font_metrics(HDC hdc, HFONT hfont, const char *test_str,
158 INT test_str_len, const TEXTMETRICA *tm_orig,
159 const SIZE *size_orig, INT width_orig,
160 INT scale_x, INT scale_y)
170 old_hfont = SelectObject(hdc, hfont);
172 GetTextMetricsA(hdc, &tm);
174 ok(tm.tmHeight == tm_orig->tmHeight * scale_y, "%d != %d\n", tm.tmHeight, tm_orig->tmHeight * scale_y);
175 ok(tm.tmAscent == tm_orig->tmAscent * scale_y, "%d != %d\n", tm.tmAscent, tm_orig->tmAscent * scale_y);
176 ok(tm.tmDescent == tm_orig->tmDescent * scale_y, "%d != %d\n", tm.tmDescent, tm_orig->tmDescent * scale_y);
177 ok(tm.tmAveCharWidth == tm_orig->tmAveCharWidth * scale_x, "%d != %d\n", tm.tmAveCharWidth, tm_orig->tmAveCharWidth * scale_x);
179 GetTextExtentPoint32A(hdc, test_str, test_str_len, &size);
181 ok(size.cx == size_orig->cx * scale_x, "%d != %d\n", size.cx, size_orig->cx * scale_x);
182 ok(size.cy == size_orig->cy * scale_y, "%d != %d\n", size.cy, size_orig->cy * scale_y);
184 GetCharWidthA(hdc, 'A', 'A', &width);
186 ok(width == width_orig * scale_x, "%d != %d\n", width, width_orig * scale_x);
188 SelectObject(hdc, old_hfont);
191 /* see whether GDI scales bitmap font metrics */
192 static void test_bitmap_font(void)
194 static const char test_str[11] = "Test String";
197 HFONT hfont, old_hfont;
200 INT ret, i, width_orig, height_orig;
204 /* "System" has only 1 pixel size defined, otherwise the test breaks */
205 ret = EnumFontFamiliesA(hdc, "System", font_enum_proc, (LPARAM)&bitmap_lf);
209 trace("no bitmap fonts were found, skipping the test\n");
213 trace("found bitmap font %s, height %d\n", bitmap_lf.lfFaceName, bitmap_lf.lfHeight);
215 height_orig = bitmap_lf.lfHeight;
216 hfont = create_font("bitmap", &bitmap_lf);
218 old_hfont = SelectObject(hdc, hfont);
219 ok(GetTextMetricsA(hdc, &tm_orig), "GetTextMetricsA failed\n");
220 ok(GetTextExtentPoint32A(hdc, test_str, sizeof(test_str), &size_orig), "GetTextExtentPoint32A failed\n");
221 ok(GetCharWidthA(hdc, 'A', 'A', &width_orig), "GetCharWidthA failed\n");
222 SelectObject(hdc, old_hfont);
225 /* test fractional scaling */
226 for (i = 1; i < height_orig; i++)
228 hfont = create_font("fractional", &bitmap_lf);
229 test_font_metrics(hdc, hfont, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 1, 1);
233 /* test integer scaling 3x2 */
234 bitmap_lf.lfHeight = height_orig * 2;
235 bitmap_lf.lfWidth *= 3;
236 hfont = create_font("3x2", &bitmap_lf);
239 test_font_metrics(hdc, hfont, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 3, 2);
243 /* test integer scaling 3x3 */
244 bitmap_lf.lfHeight = height_orig * 3;
245 bitmap_lf.lfWidth = 0;
246 hfont = create_font("3x3", &bitmap_lf);
250 test_font_metrics(hdc, hfont, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 3, 3);
257 static INT CALLBACK find_font_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
259 LOGFONT *lf = (LOGFONT *)lParam;
261 if (elf->lfHeight == lf->lfHeight && !strcmp(elf->lfFaceName, lf->lfFaceName))
264 return 0; /* stop enumeration */
266 return 1; /* continue enumeration */
269 #define CP1252_BIT 0x00000001
270 #define CP1250_BIT 0x00000002
271 #define CP1251_BIT 0x00000004
272 #define CP1253_BIT 0x00000008
273 #define CP1254_BIT 0x00000010
274 #define CP1255_BIT 0x00000020
275 #define CP1256_BIT 0x00000040
276 #define CP1257_BIT 0x00000080
277 #define CP1258_BIT 0x00000100
278 #define CP874_BIT 0x00010000
279 #define CP932_BIT 0x00020000
280 #define CP936_BIT 0x00040000
281 #define CP949_BIT 0x00080000
282 #define CP950_BIT 0x00100000
284 static void test_bitmap_font_metrics(void)
286 static const struct font_data
288 const char face_name[LF_FACESIZE];
289 int weight, height, ascent, descent, int_leading, ext_leading;
290 int ave_char_width, max_char_width;
294 { "MS Sans Serif", FW_NORMAL, 13, 11, 2, 2, 0, 5, 11, CP1252_BIT | CP1250_BIT | CP1251_BIT },
295 { "MS Sans Serif", FW_NORMAL, 16, 13, 3, 3, 0, 7, 14, CP1252_BIT | CP1250_BIT | CP1251_BIT },
296 { "MS Sans Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 16, CP1252_BIT | CP1251_BIT },
297 { "MS Sans Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 18, CP1250_BIT },
298 { "MS Sans Serif", FW_NORMAL, 24, 19, 5, 6, 0, 9, 19, CP1252_BIT },
299 { "MS Sans Serif", FW_NORMAL, 24, 19, 5, 6, 0, 9, 24, CP1250_BIT },
300 { "MS Sans Serif", FW_NORMAL, 24, 19, 5, 6, 0, 9, 20, CP1251_BIT },
301 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 5, 0, 12, 24, CP1252_BIT },
302 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 6, 0, 12, 24, CP1250_BIT },
303 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 5, 0, 12, 25, CP1251_BIT },
304 { "MS Sans Serif", FW_NORMAL, 37, 29, 8, 5, 0, 16, 32, CP1252_BIT | CP1250_BIT | CP1251_BIT },
305 { "MS Serif", FW_NORMAL, 10, 8, 2, 2, 0, 4, 8, CP1252_BIT | CP1250_BIT },
306 { "MS Serif", FW_NORMAL, 10, 8, 2, 2, 0, 5, 8, CP1251_BIT },
307 { "MS Serif", FW_NORMAL, 11, 9, 2, 2, 0, 5, 9, CP1252_BIT | CP1250_BIT | CP1251_BIT },
308 { "MS Serif", FW_NORMAL, 13, 11, 2, 2, 0, 5, 11, CP1252_BIT },
309 { "MS Serif", FW_NORMAL, 13, 11, 2, 2, 0, 5, 12, CP1250_BIT | CP1251_BIT },
310 { "MS Serif", FW_NORMAL, 16, 13, 3, 3, 0, 6, 14, CP1252_BIT | CP1250_BIT },
311 { "MS Serif", FW_NORMAL, 16, 13, 3, 3, 0, 6, 16, CP1251_BIT },
312 { "MS Serif", FW_NORMAL, 19, 15, 4, 3, 0, 8, 18, CP1252_BIT | CP1250_BIT },
313 { "MS Serif", FW_NORMAL, 19, 15, 4, 3, 0, 8, 19, CP1251_BIT },
314 { "MS Serif", FW_NORMAL, 21, 16, 5, 3, 0, 9, 17, CP1252_BIT },
315 { "MS Serif", FW_NORMAL, 21, 16, 5, 3, 0, 9, 22, CP1250_BIT },
316 { "MS Serif", FW_NORMAL, 21, 16, 5, 3, 0, 9, 23, CP1251_BIT },
317 { "MS Serif", FW_NORMAL, 27, 21, 6, 3, 0, 12, 23, CP1252_BIT },
318 { "MS Serif", FW_NORMAL, 27, 21, 6, 3, 0, 12, 26, CP1250_BIT },
319 { "MS Serif", FW_NORMAL, 27, 21, 6, 3, 0, 12, 27, CP1251_BIT },
320 { "MS Serif", FW_NORMAL, 35, 27, 8, 3, 0, 16, 33, CP1252_BIT | CP1250_BIT },
321 { "MS Serif", FW_NORMAL, 35, 27, 8, 3, 0, 16, 34, CP1251_BIT },
322 { "Courier", FW_NORMAL, 13, 11, 2, 0, 0, 8, 8, CP1252_BIT | CP1250_BIT | CP1251_BIT },
323 { "Courier", FW_NORMAL, 16, 13, 3, 0, 0, 9, 9, CP1252_BIT | CP1250_BIT | CP1251_BIT },
324 { "Courier", FW_NORMAL, 20, 16, 4, 0, 0, 12, 12, CP1252_BIT | CP1250_BIT | CP1251_BIT },
325 { "System", FW_BOLD, 16, 13, 3, 3, 0, 7, 14, CP1252_BIT },
326 { "System", FW_BOLD, 16, 13, 3, 3, 0, 7, 15, CP1250_BIT | CP1251_BIT },
327 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 1, 2, CP1252_BIT},
328 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 1, 8, CP1250_BIT | CP1251_BIT },
329 { "Small Fonts", FW_NORMAL, 5, 4, 1, 1, 0, 3, 4, CP1252_BIT },
330 { "Small Fonts", FW_NORMAL, 5, 4, 1, 1, 0, 2, 8, CP1250_BIT | CP1251_BIT },
331 { "Small Fonts", FW_NORMAL, 6, 5, 1, 1, 0, 3, 13, CP1252_BIT },
332 { "Small Fonts", FW_NORMAL, 6, 5, 1, 1, 0, 3, 8, CP1250_BIT | CP1251_BIT },
333 { "Small Fonts", FW_NORMAL, 8, 7, 1, 1, 0, 4, 7, CP1252_BIT },
334 { "Small Fonts", FW_NORMAL, 8, 7, 1, 1, 0, 4, 8, CP1250_BIT | CP1251_BIT },
335 { "Small Fonts", FW_NORMAL, 10, 8, 2, 2, 0, 4, 8, CP1252_BIT | CP1250_BIT },
336 { "Small Fonts", FW_NORMAL, 10, 8, 2, 2, 0, 5, 8, CP1251_BIT },
337 { "Small Fonts", FW_NORMAL, 11, 9, 2, 2, 0, 5, 9, CP1252_BIT | CP1250_BIT | CP1251_BIT },
338 { "Fixedsys", FW_NORMAL, 15, 12, 3, 3, 0, 8, 8, CP1252_BIT | CP1250_BIT },
339 { "Fixedsys", FW_NORMAL, 16, 12, 4, 3, 0, 8, 8, CP1251_BIT }
341 /* FIXME: add "Terminal" */
345 HFONT hfont, old_hfont;
349 hdc = CreateCompatibleDC(0);
352 for (i = 0; i < sizeof(fd)/sizeof(fd[0]); i++)
356 memset(&lf, 0, sizeof(lf));
358 lf.lfHeight = fd[i].height;
359 strcpy(lf.lfFaceName, fd[i].face_name);
361 for(bit = 0; bit < 32; bit++)
368 if((fd[i].ansi_bitfield & fs[0]) == 0) continue;
369 if(!TranslateCharsetInfo( fs, &csi, TCI_SRCFONTSIG )) continue;
371 lf.lfCharSet = csi.ciCharset;
372 ret = EnumFontFamiliesEx(hdc, &lf, find_font_proc, (LPARAM)&lf, 0);
375 trace("found font %s, height %d charset %x\n", lf.lfFaceName, lf.lfHeight, lf.lfCharSet);
377 hfont = create_font(lf.lfFaceName, &lf);
378 old_hfont = SelectObject(hdc, hfont);
379 ok(GetTextMetrics(hdc, &tm), "GetTextMetrics error %d\n", GetLastError());
381 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);
382 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);
383 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);
384 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);
385 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);
386 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);
387 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);
389 /* Don't run the max char width test on System/ANSI_CHARSET. We have extra characters in our font
390 that make the max width bigger */
391 if(strcmp(lf.lfFaceName, "System") || lf.lfCharSet != ANSI_CHARSET)
392 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);
394 SelectObject(hdc, old_hfont);
402 static void test_GdiGetCharDimensions(void)
408 LONG avgwidth, height;
409 static const char szAlphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
411 if (!pGdiGetCharDimensions)
413 skip("GdiGetCharDimensions not available on this platform\n");
417 hdc = CreateCompatibleDC(NULL);
419 GetTextExtentPoint(hdc, szAlphabet, strlen(szAlphabet), &size);
420 avgwidth = ((size.cx / 26) + 1) / 2;
422 ret = pGdiGetCharDimensions(hdc, &tm, &height);
423 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
424 ok(height == tm.tmHeight, "GdiGetCharDimensions should have set height to %d instead of %d\n", tm.tmHeight, height);
426 ret = pGdiGetCharDimensions(hdc, &tm, NULL);
427 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
429 ret = pGdiGetCharDimensions(hdc, NULL, NULL);
430 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
433 ret = pGdiGetCharDimensions(hdc, NULL, &height);
434 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
435 ok(height == size.cy, "GdiGetCharDimensions should have set height to %d instead of %d\n", size.cy, height);
440 static void test_GetCharABCWidthsW(void)
444 if (!pGetCharABCWidthsW) return;
446 ret = pGetCharABCWidthsW(NULL, 'a', 'a', abc);
447 ok(!ret, "GetCharABCWidthsW should have returned FALSE\n");
450 static void test_text_extents(void)
452 static const WCHAR wt[] = {'O','n','e','\n','t','w','o',' ','3',0};
454 INT i, len, fit1, fit2;
462 memset(&lf, 0, sizeof(lf));
463 strcpy(lf.lfFaceName, "Arial");
466 hfont = CreateFontIndirectA(&lf);
468 hfont = SelectObject(hdc, hfont);
469 GetTextMetricsA(hdc, &tm);
470 GetTextExtentPointA(hdc, "o", 1, &sz);
471 ok(sz.cy == tm.tmHeight, "cy %d tmHeight %d\n", sz.cy, tm.tmHeight);
473 SetLastError(0xdeadbeef);
474 GetTextExtentExPointW(hdc, wt, 1, 1, &fit1, &fit2, &sz1);
475 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
477 skip("Skipping remainder of text extents test on a Win9x platform\n");
478 hfont = SelectObject(hdc, hfont);
485 extents = HeapAlloc(GetProcessHeap(), 0, len * sizeof extents[0]);
486 memset(extents, 0, len * sizeof extents[0]);
487 extents[0] = 1; /* So that the increasing sequence test will fail
488 if the extents array is untouched. */
489 GetTextExtentExPointW(hdc, wt, len, 32767, &fit1, extents, &sz1);
490 GetTextExtentPointW(hdc, wt, len, &sz2);
492 "cy from GetTextExtentExPointW (%d) and GetTextExtentPointW (%d) differ\n", sz1.cy, sz2.cy);
493 /* Because of the '\n' in the string GetTextExtentExPoint and
494 GetTextExtentPoint return different widths under Win2k, but
495 under WinXP they return the same width. So we don't test that
498 for (i = 1; i < len; ++i)
499 ok(extents[i-1] <= extents[i],
500 "GetTextExtentExPointW generated a non-increasing sequence of partial extents (at position %d)\n",
502 ok(extents[len-1] == sz1.cx, "GetTextExtentExPointW extents and size don't match\n");
503 ok(0 <= fit1 && fit1 <= len, "GetTextExtentExPointW generated illegal value %d for fit\n", fit1);
504 ok(0 < fit1, "GetTextExtentExPointW says we can't even fit one letter in 32767 logical units\n");
505 GetTextExtentExPointW(hdc, wt, len, extents[2], &fit2, NULL, &sz2);
506 ok(sz1.cx == sz2.cx && sz1.cy == sz2.cy, "GetTextExtentExPointW returned different sizes for the same string\n");
507 ok(fit2 == 3, "GetTextExtentExPointW extents isn't consistent with fit\n");
508 GetTextExtentExPointW(hdc, wt, len, extents[2]-1, &fit2, NULL, &sz2);
509 ok(fit2 == 2, "GetTextExtentExPointW extents isn't consistent with fit\n");
510 GetTextExtentExPointW(hdc, wt, 2, 0, NULL, extents + 2, &sz2);
511 ok(extents[0] == extents[2] && extents[1] == extents[3],
512 "GetTextExtentExPointW with lpnFit == NULL returns incorrect results\n");
513 GetTextExtentExPointW(hdc, wt, 2, 0, NULL, NULL, &sz1);
514 ok(sz1.cx == sz2.cx && sz1.cy == sz2.cy,
515 "GetTextExtentExPointW with lpnFit and alpDx both NULL returns incorrect results\n");
516 HeapFree(GetProcessHeap(), 0, extents);
518 hfont = SelectObject(hdc, hfont);
520 ReleaseDC(NULL, hdc);
523 static void test_GetGlyphIndices(void)
530 WCHAR testtext[] = {'T','e','s','t',0xffff,0};
531 WORD glyphs[(sizeof(testtext)/2)-1];
534 if (!pGetGlyphIndicesW) {
535 skip("GetGlyphIndices not available on platform\n");
539 if(!is_font_installed("Symbol"))
541 skip("Symbol is not installed so skipping this test\n");
545 memset(&lf, 0, sizeof(lf));
546 strcpy(lf.lfFaceName, "Symbol");
549 hfont = CreateFontIndirectA(&lf);
552 ok(GetTextMetrics(hdc, &textm), "GetTextMetric failed\n");
553 flags |= GGI_MARK_NONEXISTING_GLYPHS;
554 charcount = pGetGlyphIndicesW(hdc, testtext, (sizeof(testtext)/2)-1, glyphs, flags);
555 ok(charcount == 5, "GetGlyphIndices count of glyphs should = 5 not %d\n", charcount);
556 ok(glyphs[4] == 0x001f, "GetGlyphIndices should have returned a nonexistent char not %04x\n", glyphs[4]);
558 charcount = pGetGlyphIndicesW(hdc, testtext, (sizeof(testtext)/2)-1, glyphs, flags);
559 ok(charcount == 5, "GetGlyphIndices count of glyphs should = 5 not %d\n", charcount);
560 ok(glyphs[4] == textm.tmDefaultChar, "GetGlyphIndices should have returned a %04x not %04x\n",
561 textm.tmDefaultChar, glyphs[4]);
564 static void test_GetKerningPairs(void)
566 static const struct kerning_data
568 const char face_name[LF_FACESIZE];
570 /* some interesting fields from OUTLINETEXTMETRIC */
571 LONG tmHeight, tmAscent, tmDescent;
576 UINT otmsCapEmHeight;
581 UINT otmusMinimumPPEM;
582 /* small subset of kerning pairs to test */
583 DWORD total_kern_pairs;
584 const KERNINGPAIR kern_pair[26];
587 {"Arial", 12, 12, 9, 3,
588 2048, 7, -2, 1, 5, 2, 8, -2, 0, 9,
591 {' ','A',-1},{' ','T',0},{' ','Y',0},{'1','1',-1},
592 {'A',' ',-1},{'A','T',-1},{'A','V',-1},{'A','W',0},
593 {'A','Y',-1},{'A','v',0},{'A','w',0},{'A','y',0},
594 {'F',',',-1},{'F','.',-1},{'F','A',-1},{'L',' ',0},
595 {'L','T',-1},{'L','V',-1},{'L','W',-1},{'L','Y',-1},
596 {915,912,+1},{915,913,-1},{910,912,+1},{910,913,-1},
597 {933,970,+1},{933,972,-1}
600 {"Arial", -34, 39, 32, 7,
601 2048, 25, -7, 5, 17, 9, 31, -7, 1, 9,
604 {' ','A',-2},{' ','T',-1},{' ','Y',-1},{'1','1',-3},
605 {'A',' ',-2},{'A','T',-3},{'A','V',-3},{'A','W',-1},
606 {'A','Y',-3},{'A','v',-1},{'A','w',-1},{'A','y',-1},
607 {'F',',',-4},{'F','.',-4},{'F','A',-2},{'L',' ',-1},
608 {'L','T',-3},{'L','V',-3},{'L','W',-3},{'L','Y',-3},
609 {915,912,+3},{915,913,-3},{910,912,+3},{910,913,-3},
610 {933,970,+2},{933,972,-3}
613 { "Arial", 120, 120, 97, 23,
614 2048, 79, -23, 16, 54, 27, 98, -23, 4, 9,
617 {' ','A',-6},{' ','T',-2},{' ','Y',-2},{'1','1',-8},
618 {'A',' ',-6},{'A','T',-8},{'A','V',-8},{'A','W',-4},
619 {'A','Y',-8},{'A','v',-2},{'A','w',-2},{'A','y',-2},
620 {'F',',',-12},{'F','.',-12},{'F','A',-6},{'L',' ',-4},
621 {'L','T',-8},{'L','V',-8},{'L','W',-8},{'L','Y',-8},
622 {915,912,+9},{915,913,-10},{910,912,+9},{910,913,-8},
623 {933,970,+6},{933,972,-10}
626 #if 0 /* this set fails due to +1/-1 errors (rounding bug?), needs investigation. */
627 { "Arial", 1024 /* usually 1/2 of EM Square */, 1024, 830, 194,
628 2048, 668, -193, 137, 459, 229, 830, -194, 30, 9,
631 {' ','A',-51},{' ','T',-17},{' ','Y',-17},{'1','1',-68},
632 {'A',' ',-51},{'A','T',-68},{'A','V',-68},{'A','W',-34},
633 {'A','Y',-68},{'A','v',-17},{'A','w',-17},{'A','y',-17},
634 {'F',',',-102},{'F','.',-102},{'F','A',-51},{'L',' ',-34},
635 {'L','T',-68},{'L','V',-68},{'L','W',-68},{'L','Y',-68},
636 {915,912,+73},{915,913,-84},{910,912,+76},{910,913,-68},
637 {933,970,+54},{933,972,-83}
643 HFONT hfont, hfont_old;
644 KERNINGPAIR *kern_pair;
646 DWORD total_kern_pairs, ret, i, n, matches;
650 /* GetKerningPairsA maps unicode set of kerning pairs to current code page
651 * which may render this test unusable, so we're trying to avoid that.
653 SetLastError(0xdeadbeef);
654 GetKerningPairsW(hdc, 0, NULL);
655 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
657 skip("Skipping the GetKerningPairs test on a Win9x platform\n");
662 for (i = 0; i < sizeof(kd)/sizeof(kd[0]); i++)
664 OUTLINETEXTMETRICW otm;
666 if (!is_font_installed(kd[i].face_name))
668 trace("%s is not installed so skipping this test\n", kd[i].face_name);
672 trace("testing font %s, height %d\n", kd[i].face_name, kd[i].height);
674 memset(&lf, 0, sizeof(lf));
675 strcpy(lf.lfFaceName, kd[i].face_name);
676 lf.lfHeight = kd[i].height;
677 hfont = CreateFontIndirect(&lf);
680 hfont_old = SelectObject(hdc, hfont);
682 SetLastError(0xdeadbeef);
683 otm.otmSize = sizeof(otm); /* just in case for Win9x compatibility */
684 ok(GetOutlineTextMetricsW(hdc, sizeof(otm), &otm) == sizeof(otm), "GetOutlineTextMetricsW error %d\n", GetLastError());
686 ok(kd[i].tmHeight == otm.otmTextMetrics.tmHeight, "expected %d, got %d\n",
687 kd[i].tmHeight, otm.otmTextMetrics.tmHeight);
688 ok(kd[i].tmAscent == otm.otmTextMetrics.tmAscent, "expected %d, got %d\n",
689 kd[i].tmAscent, otm.otmTextMetrics.tmAscent);
690 ok(kd[i].tmDescent == otm.otmTextMetrics.tmDescent, "expected %d, got %d\n",
691 kd[i].tmDescent, otm.otmTextMetrics.tmDescent);
693 ok(kd[i].otmEMSquare == otm.otmEMSquare, "expected %u, got %u\n",
694 kd[i].otmEMSquare, otm.otmEMSquare);
695 ok(kd[i].otmAscent == otm.otmAscent, "expected %d, got %d\n",
696 kd[i].otmAscent, otm.otmAscent);
697 ok(kd[i].otmDescent == otm.otmDescent, "expected %d, got %d\n",
698 kd[i].otmDescent, otm.otmDescent);
699 ok(kd[i].otmLineGap == otm.otmLineGap, "expected %u, got %u\n",
700 kd[i].otmLineGap, otm.otmLineGap);
702 ok(kd[i].otmsCapEmHeight == otm.otmsCapEmHeight, "expected %u, got %u\n",
703 kd[i].otmsCapEmHeight, otm.otmsCapEmHeight);
704 ok(kd[i].otmsXHeight == otm.otmsXHeight, "expected %u, got %u\n",
705 kd[i].otmsXHeight, otm.otmsXHeight);
706 ok(kd[i].otmMacAscent == otm.otmMacAscent, "expected %d, got %d\n",
707 kd[i].otmMacAscent, otm.otmMacAscent);
708 ok(kd[i].otmMacDescent == otm.otmMacDescent, "expected %d, got %d\n",
709 kd[i].otmMacDescent, otm.otmMacDescent);
710 /* FIXME: this one sometimes succeeds due to expected 0, enable it when removing todo */
711 if (0) ok(kd[i].otmMacLineGap == otm.otmMacLineGap, "expected %u, got %u\n",
712 kd[i].otmMacLineGap, otm.otmMacLineGap);
713 ok(kd[i].otmusMinimumPPEM == otm.otmusMinimumPPEM, "expected %u, got %u\n",
714 kd[i].otmusMinimumPPEM, otm.otmusMinimumPPEM);
717 total_kern_pairs = GetKerningPairsW(hdc, 0, NULL);
718 trace("total_kern_pairs %u\n", total_kern_pairs);
719 kern_pair = HeapAlloc(GetProcessHeap(), 0, total_kern_pairs * sizeof(*kern_pair));
721 #if 0 /* Win98 (GetKerningPairsA) and XP behave differently here, the test passes on XP */
722 SetLastError(0xdeadbeef);
723 ret = GetKerningPairsW(hdc, 0, kern_pair);
724 ok(GetLastError() == ERROR_INVALID_PARAMETER,
725 "got error %ld, expected ERROR_INVALID_PARAMETER\n", GetLastError());
726 ok(ret == 0, "got %lu, expected 0\n", ret);
729 ret = GetKerningPairsW(hdc, 100, NULL);
730 ok(ret == total_kern_pairs, "got %u, expected %u\n", ret, total_kern_pairs);
732 ret = GetKerningPairsW(hdc, total_kern_pairs/2, kern_pair);
733 ok(ret == total_kern_pairs/2, "got %u, expected %u\n", ret, total_kern_pairs/2);
735 ret = GetKerningPairsW(hdc, total_kern_pairs, kern_pair);
736 ok(ret == total_kern_pairs, "got %u, expected %u\n", ret, total_kern_pairs);
740 for (n = 0; n < ret; n++)
744 if (kern_pair[n].wFirst < 127 && kern_pair[n].wSecond < 127)
745 trace("{'%c','%c',%d},\n",
746 kern_pair[n].wFirst, kern_pair[n].wSecond, kern_pair[n].iKernAmount);
748 for (j = 0; j < kd[i].total_kern_pairs; j++)
750 if (kern_pair[n].wFirst == kd[i].kern_pair[j].wFirst &&
751 kern_pair[n].wSecond == kd[i].kern_pair[j].wSecond)
753 ok(kern_pair[n].iKernAmount == kd[i].kern_pair[j].iKernAmount,
754 "pair %d:%d got %d, expected %d\n",
755 kern_pair[n].wFirst, kern_pair[n].wSecond,
756 kern_pair[n].iKernAmount, kd[i].kern_pair[j].iKernAmount);
762 ok(matches == kd[i].total_kern_pairs, "got matches %u, expected %u\n",
763 matches, kd[i].total_kern_pairs);
765 HeapFree(GetProcessHeap(), 0, kern_pair);
767 SelectObject(hdc, hfont_old);
774 static void test_GetOutlineTextMetrics(void)
776 OUTLINETEXTMETRIC *otm;
778 HFONT hfont, hfont_old;
782 if (!is_font_installed("Arial"))
784 skip("Arial is not installed\n");
790 memset(&lf, 0, sizeof(lf));
791 strcpy(lf.lfFaceName, "Arial");
793 lf.lfWeight = FW_NORMAL;
794 lf.lfPitchAndFamily = DEFAULT_PITCH;
795 lf.lfQuality = PROOF_QUALITY;
796 hfont = CreateFontIndirect(&lf);
799 hfont_old = SelectObject(hdc, hfont);
800 otm_size = GetOutlineTextMetrics(hdc, 0, NULL);
801 trace("otm buffer size %u (0x%x)\n", otm_size, otm_size);
803 otm = HeapAlloc(GetProcessHeap(), 0, otm_size);
805 memset(otm, 0xAA, otm_size);
806 SetLastError(0xdeadbeef);
807 otm->otmSize = sizeof(*otm); /* just in case for Win9x compatibility */
808 ret = GetOutlineTextMetrics(hdc, otm->otmSize, otm);
809 ok(ret == 1 /* Win9x */ ||
810 ret == otm->otmSize /* XP*/,
811 "expected %u, got %u, error %d\n", otm->otmSize, ret, GetLastError());
812 if (ret != 1) /* Win9x doesn't care about pointing beyond of the buffer */
814 ok(otm->otmpFamilyName == NULL, "expected NULL got %p\n", otm->otmpFamilyName);
815 ok(otm->otmpFaceName == NULL, "expected NULL got %p\n", otm->otmpFaceName);
816 ok(otm->otmpStyleName == NULL, "expected NULL got %p\n", otm->otmpStyleName);
817 ok(otm->otmpFullName == NULL, "expected NULL got %p\n", otm->otmpFullName);
820 memset(otm, 0xAA, otm_size);
821 SetLastError(0xdeadbeef);
822 otm->otmSize = otm_size; /* just in case for Win9x compatibility */
823 ret = GetOutlineTextMetrics(hdc, otm->otmSize, otm);
824 ok(ret == 1 /* Win9x */ ||
825 ret == otm->otmSize /* XP*/,
826 "expected %u, got %u, error %d\n", otm->otmSize, ret, GetLastError());
827 if (ret != 1) /* Win9x doesn't care about pointing beyond of the buffer */
829 ok(otm->otmpFamilyName != NULL, "expected not NULL got %p\n", otm->otmpFamilyName);
830 ok(otm->otmpFaceName != NULL, "expected not NULL got %p\n", otm->otmpFaceName);
831 ok(otm->otmpStyleName != NULL, "expected not NULL got %p\n", otm->otmpStyleName);
832 ok(otm->otmpFullName != NULL, "expected not NULL got %p\n", otm->otmpFullName);
835 /* ask about truncated data */
836 memset(otm, 0xAA, otm_size);
837 SetLastError(0xdeadbeef);
838 otm->otmSize = sizeof(*otm) - sizeof(LPSTR); /* just in case for Win9x compatibility */
839 ret = GetOutlineTextMetrics(hdc, otm->otmSize, otm);
840 ok(ret == 1 /* Win9x */ ||
841 ret == otm->otmSize /* XP*/,
842 "expected %u, got %u, error %d\n", otm->otmSize, ret, GetLastError());
843 if (ret != 1) /* Win9x doesn't care about pointing beyond of the buffer */
845 ok(otm->otmpFamilyName == NULL, "expected NULL got %p\n", otm->otmpFamilyName);
846 ok(otm->otmpFaceName == NULL, "expected NULL got %p\n", otm->otmpFaceName);
847 ok(otm->otmpStyleName == NULL, "expected NULL got %p\n", otm->otmpStyleName);
849 ok(otm->otmpFullName == (LPSTR)0xAAAAAAAA, "expected 0xAAAAAAAA got %p\n", otm->otmpFullName);
851 HeapFree(GetProcessHeap(), 0, otm);
853 SelectObject(hdc, hfont_old);
859 static void testJustification(HDC hdc, PSTR str, RECT *clientArea)
863 outputWidth = 0, /* to test TabbedTextOut() */
864 justifiedWidth = 0, /* to test GetTextExtentExPointW() */
865 areaWidth = clientArea->right - clientArea->left,
867 BOOL lastExtent = FALSE;
868 PSTR pFirstChar, pLastChar;
874 int GetTextExtentExPointWWidth;
875 int TabbedTextOutWidth;
878 GetTextMetricsA(hdc, &tm);
882 while (*str == tm.tmBreakChar) str++; /* skip leading break chars */
888 /* if not at the end of the string, ... */
889 if (*str == '\0') break;
890 /* ... add the next word to the current extent */
891 while (*str != '\0' && *str++ != tm.tmBreakChar);
893 SetTextJustification(hdc, 0, 0);
894 GetTextExtentPoint32(hdc, pFirstChar, str - pFirstChar - 1, &size);
895 } while ((int) size.cx < areaWidth);
897 /* ignore trailing break chars */
899 while (*(pLastChar - 1) == tm.tmBreakChar)
905 if (*str == '\0' || breakCount <= 0) pLastChar = str;
907 SetTextJustification(hdc, 0, 0);
908 GetTextExtentPoint32(hdc, pFirstChar, pLastChar - pFirstChar, &size);
910 /* do not justify the last extent */
911 if (*str != '\0' && breakCount > 0)
913 SetTextJustification(hdc, areaWidth - size.cx, breakCount);
914 GetTextExtentPoint32(hdc, pFirstChar, pLastChar - pFirstChar, &size);
915 justifiedWidth = size.cx;
917 else lastExtent = TRUE;
919 x = clientArea->left;
921 outputWidth = LOWORD(TabbedTextOut(
922 hdc, x, y, pFirstChar, pLastChar - pFirstChar,
924 /* catch errors and report them */
925 if (!lastExtent && ((outputWidth != areaWidth) || (justifiedWidth != areaWidth)))
927 memset(error[nErrors].extent, 0, 100);
928 memcpy(error[nErrors].extent, pFirstChar, pLastChar - pFirstChar);
929 error[nErrors].TabbedTextOutWidth = outputWidth;
930 error[nErrors].GetTextExtentExPointWWidth = justifiedWidth;
936 } while (*str && y < clientArea->bottom);
938 for (e = 0; e < nErrors; e++)
940 ok(error[e].TabbedTextOutWidth == areaWidth,
941 "The output text (\"%s\") width should be %d, not %d.\n",
942 error[e].extent, areaWidth, error[e].TabbedTextOutWidth);
943 /* The width returned by GetTextExtentPoint32() is exactly the same
944 returned by GetTextExtentExPointW() - see dlls/gdi32/font.c */
945 ok(error[e].GetTextExtentExPointWWidth == areaWidth,
946 "GetTextExtentPointW() for \"%s\" should have returned a width of %d, not %d.\n",
947 error[e].extent, areaWidth, error[e].GetTextExtentExPointWWidth);
951 static void test_SetTextJustification(void)
958 static char testText[] =
959 "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do "
960 "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut "
961 "enim ad minim veniam, quis nostrud exercitation ullamco laboris "
962 "nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in "
963 "reprehenderit in voluptate velit esse cillum dolore eu fugiat "
964 "nulla pariatur. Excepteur sint occaecat cupidatat non proident, "
965 "sunt in culpa qui officia deserunt mollit anim id est laborum.";
967 hwnd = CreateWindowExA(0, "static", "", WS_POPUP, 0,0, 400,400, 0, 0, 0, NULL);
968 GetClientRect( hwnd, &clientArea );
971 memset(&lf, 0, sizeof lf);
972 lf.lfCharSet = ANSI_CHARSET;
973 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
974 lf.lfWeight = FW_DONTCARE;
976 lf.lfQuality = DEFAULT_QUALITY;
977 lstrcpyA(lf.lfFaceName, "Times New Roman");
978 hfont = create_font("Times New Roman", &lf);
979 SelectObject(hdc, hfont);
981 testJustification(hdc, testText, &clientArea);
984 ReleaseDC(hwnd, hdc);
988 static BOOL get_glyph_indices(INT charset, UINT code_page, WORD *idx, UINT count, BOOL unicode)
992 HFONT hfont, hfont_old;
999 assert(count <= 128);
1001 memset(&lf, 0, sizeof(lf));
1003 lf.lfCharSet = charset;
1005 lstrcpyA(lf.lfFaceName, "Arial");
1006 SetLastError(0xdeadbeef);
1007 hfont = CreateFontIndirectA(&lf);
1008 ok(hfont != 0, "CreateFontIndirectA error %u\n", GetLastError());
1011 hfont_old = SelectObject(hdc, hfont);
1013 cs = GetTextCharsetInfo(hdc, &fs, 0);
1014 ok(cs == charset, "expected %d, got %d\n", charset, cs);
1016 SetLastError(0xdeadbeef);
1017 ret = GetTextFace(hdc, sizeof(name), name);
1018 ok(ret, "GetTextFace error %u\n", GetLastError());
1020 if (charset == SYMBOL_CHARSET)
1022 ok(strcmp("Arial", name), "face name should NOT be Arial\n");
1023 ok(fs.fsCsb[0] & (1 << 31), "symbol encoding should be available\n");
1027 ok(!strcmp("Arial", name), "face name should be Arial, not %s\n", name);
1028 ok(!(fs.fsCsb[0] & (1 << 31)), "symbol encoding should NOT be available\n");
1031 if (!TranslateCharsetInfo((DWORD *)cs, &csi, TCI_SRCCHARSET))
1033 trace("Can't find codepage for charset %d\n", cs);
1037 ok(csi.ciACP == code_page, "expected %d, got %d\n", code_page, csi.ciACP);
1042 WCHAR unicode_buf[128];
1044 for (i = 0; i < count; i++) ansi_buf[i] = (BYTE)(i + 128);
1046 MultiByteToWideChar(code_page, 0, ansi_buf, count, unicode_buf, count);
1048 SetLastError(0xdeadbeef);
1049 ret = pGetGlyphIndicesW(hdc, unicode_buf, count, idx, 0);
1050 ok(ret == count, "GetGlyphIndicesA error %u\n", GetLastError());
1056 for (i = 0; i < count; i++) ansi_buf[i] = (BYTE)(i + 128);
1058 SetLastError(0xdeadbeef);
1059 ret = pGetGlyphIndicesA(hdc, ansi_buf, count, idx, 0);
1060 ok(ret == count, "GetGlyphIndicesA error %u\n", GetLastError());
1063 SelectObject(hdc, hfont_old);
1064 DeleteObject(hfont);
1071 static void test_font_charset(void)
1073 static struct charset_data
1077 WORD font_idxA[128], font_idxW[128];
1080 { ANSI_CHARSET, 1252 },
1081 { RUSSIAN_CHARSET, 1251 },
1082 { SYMBOL_CHARSET, CP_SYMBOL } /* keep it as the last one */
1086 if (!pGetGlyphIndicesA || !pGetGlyphIndicesW)
1088 skip("Skipping the font charset test on a Win9x platform\n");
1092 if (!is_font_installed("Arial"))
1094 skip("Arial is not installed\n");
1098 for (i = 0; i < sizeof(cd)/sizeof(cd[0]); i++)
1100 if (cd[i].charset == SYMBOL_CHARSET)
1102 if (!is_font_installed("Symbol") && !is_font_installed("Wingdings"))
1104 skip("Symbol or Wingdings is not installed\n");
1108 get_glyph_indices(cd[i].charset, cd[i].code_page, cd[i].font_idxA, 128, FALSE);
1109 get_glyph_indices(cd[i].charset, cd[i].code_page, cd[i].font_idxW, 128, TRUE);
1110 ok(!memcmp(cd[i].font_idxA, cd[i].font_idxW, 128*sizeof(WORD)), "%d: indices don't match\n", i);
1113 ok(memcmp(cd[0].font_idxW, cd[1].font_idxW, 128*sizeof(WORD)), "0 vs 1: indices shouldn't match\n");
1116 ok(memcmp(cd[0].font_idxW, cd[2].font_idxW, 128*sizeof(WORD)), "0 vs 2: indices shouldn't match\n");
1117 ok(memcmp(cd[1].font_idxW, cd[2].font_idxW, 128*sizeof(WORD)), "1 vs 2: indices shouldn't match\n");
1120 skip("Symbol or Wingdings is not installed\n");
1123 static void test_GetFontUnicodeRanges(void)
1127 HFONT hfont, hfont_old;
1131 if (!pGetFontUnicodeRanges)
1133 skip("GetFontUnicodeRanges not available before W2K\n");
1137 memset(&lf, 0, sizeof(lf));
1138 lstrcpyA(lf.lfFaceName, "Arial");
1139 hfont = create_font("Arial", &lf);
1142 hfont_old = SelectObject(hdc, hfont);
1144 size = pGetFontUnicodeRanges(NULL, NULL);
1145 ok(!size, "GetFontUnicodeRanges succeeded unexpectedly\n");
1147 size = pGetFontUnicodeRanges(hdc, NULL);
1148 ok(size, "GetFontUnicodeRanges failed unexpectedly\n");
1150 gs = HeapAlloc(GetProcessHeap(), 0, size);
1152 size = pGetFontUnicodeRanges(hdc, gs);
1153 ok(size, "GetFontUnicodeRanges failed\n");
1155 for (i = 0; i < gs->cRanges; i++)
1156 trace("%03d wcLow %04x cGlyphs %u\n", i, gs->ranges[i].wcLow, gs->ranges[i].cGlyphs);
1158 trace("found %u ranges\n", gs->cRanges);
1160 HeapFree(GetProcessHeap(), 0, gs);
1162 SelectObject(hdc, hfont_old);
1163 DeleteObject(hfont);
1164 ReleaseDC(NULL, hdc);
1167 #define MAX_ENUM_FONTS 256
1169 struct enum_font_data
1172 LOGFONT lf[MAX_ENUM_FONTS];
1175 static INT CALLBACK arial_enum_proc(const LOGFONT *lf, const TEXTMETRIC *tm, DWORD type, LPARAM lParam)
1177 struct enum_font_data *efd = (struct enum_font_data *)lParam;
1179 if (type != TRUETYPE_FONTTYPE) return 1;
1181 trace("enumed font \"%s\", charset %d, weight %d, italic %d\n",
1182 lf->lfFaceName, lf->lfCharSet, lf->lfWeight, lf->lfItalic);
1184 if (efd->total < MAX_ENUM_FONTS)
1185 efd->lf[efd->total++] = *lf;
1190 static void get_charset_stats(struct enum_font_data *efd,
1191 int *ansi_charset, int *symbol_charset,
1192 int *russian_charset)
1197 *symbol_charset = 0;
1198 *russian_charset = 0;
1200 for (i = 0; i < efd->total; i++)
1202 switch (efd->lf[i].lfCharSet)
1207 case SYMBOL_CHARSET:
1208 (*symbol_charset)++;
1210 case RUSSIAN_CHARSET:
1211 (*russian_charset)++;
1217 static void test_EnumFontFamilies(const char *font_name, INT font_charset)
1219 struct enum_font_data efd;
1222 int i, ret, ansi_charset, symbol_charset, russian_charset;
1224 trace("Testing font %s, charset %d\n", *font_name ? font_name : "<empty>", font_charset);
1226 if (*font_name && !is_truetype_font_installed(font_name))
1228 skip("%s is not installed\n", font_name);
1234 /* Observed behaviour: EnumFontFamilies enumerates aliases like "Arial Cyr"
1235 * while EnumFontFamiliesEx doesn't.
1237 if (!*font_name && font_charset == DEFAULT_CHARSET) /* do it only once */
1240 SetLastError(0xdeadbeef);
1241 ret = EnumFontFamilies(hdc, NULL, arial_enum_proc, (LPARAM)&efd);
1242 ok(ret, "EnumFontFamilies error %u\n", GetLastError());
1243 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
1244 trace("enumerated ansi %d, symbol %d, russian %d fonts for NULL\n",
1245 ansi_charset, symbol_charset, russian_charset);
1246 ok(efd.total > 0, "no fonts enumerated: NULL\n");
1247 ok(ansi_charset > 0, "NULL family should enumerate ANSI_CHARSET\n");
1248 ok(symbol_charset > 0, "NULL family should enumerate SYMBOL_CHARSET\n");
1249 ok(russian_charset > 0, "NULL family should enumerate RUSSIAN_CHARSET\n");
1253 SetLastError(0xdeadbeef);
1254 ret = EnumFontFamilies(hdc, font_name, arial_enum_proc, (LPARAM)&efd);
1255 ok(ret, "EnumFontFamilies error %u\n", GetLastError());
1256 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
1257 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s\n",
1258 ansi_charset, symbol_charset, russian_charset,
1259 *font_name ? font_name : "<empty>");
1261 ok(efd.total > 0, "no fonts enumerated: %s\n", font_name);
1263 ok(!efd.total, "no fonts should be enumerated for empty font_name\n");
1264 for (i = 0; i < efd.total; i++)
1266 /* FIXME: remove completely once Wine is fixed */
1267 if (efd.lf[i].lfCharSet != font_charset)
1270 ok(efd.lf[i].lfCharSet == font_charset, "%d: got charset %d\n", i, efd.lf[i].lfCharSet);
1273 ok(efd.lf[i].lfCharSet == font_charset, "%d: got charset %d\n", i, efd.lf[i].lfCharSet);
1274 ok(!lstrcmp(efd.lf[i].lfFaceName, font_name), "expected %s, got %s\n",
1275 font_name, efd.lf[i].lfFaceName);
1278 memset(&lf, 0, sizeof(lf));
1279 lf.lfCharSet = ANSI_CHARSET;
1280 lstrcpy(lf.lfFaceName, font_name);
1282 SetLastError(0xdeadbeef);
1283 ret = EnumFontFamiliesEx(hdc, &lf, arial_enum_proc, (LPARAM)&efd, 0);
1284 ok(ret, "EnumFontFamiliesEx error %u\n", GetLastError());
1285 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
1286 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s ANSI_CHARSET\n",
1287 ansi_charset, symbol_charset, russian_charset,
1288 *font_name ? font_name : "<empty>");
1289 if (font_charset == SYMBOL_CHARSET)
1292 ok(efd.total == 0, "no fonts should be enumerated: %s ANSI_CHARSET\n", font_name);
1294 ok(efd.total > 0, "no fonts enumerated: %s\n", font_name);
1298 ok(efd.total > 0, "no fonts enumerated: %s ANSI_CHARSET\n", font_name);
1299 for (i = 0; i < efd.total; i++)
1301 ok(efd.lf[i].lfCharSet == ANSI_CHARSET, "%d: got charset %d\n", i, efd.lf[i].lfCharSet);
1303 ok(!lstrcmp(efd.lf[i].lfFaceName, font_name), "expected %s, got %s\n",
1304 font_name, efd.lf[i].lfFaceName);
1308 /* DEFAULT_CHARSET should enumerate all available charsets */
1309 memset(&lf, 0, sizeof(lf));
1310 lf.lfCharSet = DEFAULT_CHARSET;
1311 lstrcpy(lf.lfFaceName, font_name);
1313 SetLastError(0xdeadbeef);
1314 EnumFontFamiliesEx(hdc, &lf, arial_enum_proc, (LPARAM)&efd, 0);
1315 ok(ret, "EnumFontFamiliesEx error %u\n", GetLastError());
1316 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
1317 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s DEFAULT_CHARSET\n",
1318 ansi_charset, symbol_charset, russian_charset,
1319 *font_name ? font_name : "<empty>");
1320 ok(efd.total > 0, "no fonts enumerated: %s DEFAULT_CHARSET\n", font_name);
1321 for (i = 0; i < efd.total; i++)
1324 ok(!lstrcmp(efd.lf[i].lfFaceName, font_name), "expected %s, got %s\n",
1325 font_name, efd.lf[i].lfFaceName);
1329 switch (font_charset)
1332 ok(ansi_charset > 0,
1333 "ANSI_CHARSET should enumerate ANSI_CHARSET for %s\n", font_name);
1335 "ANSI_CHARSET should NOT enumerate SYMBOL_CHARSET for %s\n", font_name);
1336 ok(russian_charset > 0,
1337 "ANSI_CHARSET should enumerate RUSSIAN_CHARSET for %s\n", font_name);
1339 case SYMBOL_CHARSET:
1341 "SYMBOL_CHARSET should NOT enumerate ANSI_CHARSET for %s\n", font_name);
1343 "SYMBOL_CHARSET should enumerate SYMBOL_CHARSET for %s\n", font_name);
1344 ok(!russian_charset,
1345 "SYMBOL_CHARSET should NOT enumerate RUSSIAN_CHARSET for %s\n", font_name);
1347 case DEFAULT_CHARSET:
1348 ok(ansi_charset > 0,
1349 "DEFAULT_CHARSET should enumerate ANSI_CHARSET for %s\n", font_name);
1350 ok(symbol_charset > 0,
1351 "DEFAULT_CHARSET should enumerate SYMBOL_CHARSET for %s\n", font_name);
1352 ok(russian_charset > 0,
1353 "DEFAULT_CHARSET should enumerate RUSSIAN_CHARSET for %s\n", font_name);
1359 ok(ansi_charset > 0,
1360 "DEFAULT_CHARSET should enumerate ANSI_CHARSET for %s\n", *font_name ? font_name : "<empty>");
1361 ok(symbol_charset > 0,
1362 "DEFAULT_CHARSET should enumerate SYMBOL_CHARSET for %s\n", *font_name ? font_name : "<empty>");
1363 ok(russian_charset > 0,
1364 "DEFAULT_CHARSET should enumerate RUSSIAN_CHARSET for %s\n", *font_name ? font_name : "<empty>");
1367 memset(&lf, 0, sizeof(lf));
1368 lf.lfCharSet = SYMBOL_CHARSET;
1369 lstrcpy(lf.lfFaceName, font_name);
1371 SetLastError(0xdeadbeef);
1372 EnumFontFamiliesEx(hdc, &lf, arial_enum_proc, (LPARAM)&efd, 0);
1373 ok(ret, "EnumFontFamiliesEx error %u\n", GetLastError());
1374 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
1375 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s SYMBOL_CHARSET\n",
1376 ansi_charset, symbol_charset, russian_charset,
1377 *font_name ? font_name : "<empty>");
1378 if (*font_name && font_charset == ANSI_CHARSET)
1379 ok(efd.total == 0, "no fonts should be enumerated: %s SYMBOL_CHARSET\n", font_name);
1382 ok(efd.total > 0, "no fonts enumerated: %s SYMBOL_CHARSET\n", font_name);
1383 for (i = 0; i < efd.total; i++)
1385 ok(efd.lf[i].lfCharSet == SYMBOL_CHARSET, "%d: got charset %d\n", i, efd.lf[i].lfCharSet);
1387 ok(!lstrcmp(efd.lf[i].lfFaceName, font_name), "expected %s, got %s\n",
1388 font_name, efd.lf[i].lfFaceName);
1392 "SYMBOL_CHARSET should NOT enumerate ANSI_CHARSET for %s\n", *font_name ? font_name : "<empty>");
1393 ok(symbol_charset > 0,
1394 "SYMBOL_CHARSET should enumerate SYMBOL_CHARSET for %s\n", *font_name ? font_name : "<empty>");
1395 ok(!russian_charset,
1396 "SYMBOL_CHARSET should NOT enumerate RUSSIAN_CHARSET for %s\n", *font_name ? font_name : "<empty>");
1402 /* PANOSE is 10 bytes in size, need to pack the structure properly */
1403 #include "pshpack2.h"
1407 SHORT xAvgCharWidth;
1408 USHORT usWeightClass;
1409 USHORT usWidthClass;
1411 SHORT ySubscriptXSize;
1412 SHORT ySubscriptYSize;
1413 SHORT ySubscriptXOffset;
1414 SHORT ySubscriptYOffset;
1415 SHORT ySuperscriptXSize;
1416 SHORT ySuperscriptYSize;
1417 SHORT ySuperscriptXOffset;
1418 SHORT ySuperscriptYOffset;
1419 SHORT yStrikeoutSize;
1420 SHORT yStrikeoutPosition;
1423 ULONG ulUnicodeRange1;
1424 ULONG ulUnicodeRange2;
1425 ULONG ulUnicodeRange3;
1426 ULONG ulUnicodeRange4;
1429 USHORT usFirstCharIndex;
1430 USHORT usLastCharIndex;
1431 /* According to the Apple spec, original version didn't have the below fields,
1432 * version numbers were taked from the OpenType spec.
1434 /* version 0 (TrueType 1.5) */
1435 USHORT sTypoAscender;
1436 USHORT sTypoDescender;
1437 USHORT sTypoLineGap;
1439 USHORT usWinDescent;
1440 /* version 1 (TrueType 1.66) */
1441 ULONG ulCodePageRange1;
1442 ULONG ulCodePageRange2;
1443 /* version 2 (OpenType 1.2) */
1446 USHORT usDefaultChar;
1448 USHORT usMaxContext;
1450 #include "poppack.h"
1452 #ifdef WORDS_BIGENDIAN
1453 #define GET_BE_WORD(x) (x)
1455 #define GET_BE_WORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
1458 #define MS_MAKE_TAG(ch0, ch1, ch2, ch3) \
1459 ((DWORD)(BYTE)(ch0) | ((DWORD)(BYTE)(ch1) << 8) | \
1460 ((DWORD)(BYTE)(ch2) << 16) | ((DWORD)(BYTE)(ch3) << 24))
1461 #define MS_OS2_TAG MS_MAKE_TAG('O','S','/','2')
1463 static void test_text_metrics(const LOGFONTA *lf)
1466 HFONT hfont, hfont_old;
1469 UINT first_unicode_char, last_unicode_char, default_char, break_char;
1474 const char *font_name = lf->lfFaceName;
1476 trace("Testing font metrics for %s, charset %d\n", font_name, lf->lfCharSet);
1480 SetLastError(0xdeadbeef);
1481 hfont = CreateFontIndirectA(lf);
1482 ok(hfont != 0, "CreateFontIndirect error %u\n", GetLastError());
1484 hfont_old = SelectObject(hdc, hfont);
1486 if(lf->lfWidth > 0) {
1488 GLYPHMETRICS gm1, gm2;
1490 MAT2 mat2 = { {0,1}, {0,0}, {0,0}, {0,1} };
1492 /* negative widths are handled just as positive ones */
1495 SetLastError(0xdeadbeef);
1496 hfont2 = CreateFontIndirectA(&lf2);
1497 ok(hfont != 0, "CreateFontIndirect error %u\n", GetLastError());
1498 SelectObject(hdc, hfont2);
1500 memset(&gm1, 0xaa, sizeof(gm1));
1501 SetLastError(0xdeadbeef);
1502 ret = GetGlyphOutlineA(hdc, 'x', GGO_METRICS, &gm1, 0, NULL, &mat2);
1503 ok(ret != GDI_ERROR, "GetGlyphOutline error 0x%x\n", GetLastError());
1505 SelectObject(hdc, hfont);
1506 DeleteObject(hfont2);
1508 memset(&gm2, 0xbb, sizeof(gm2));
1509 SetLastError(0xdeadbeef);
1510 ret = GetGlyphOutlineA(hdc, 'x', GGO_METRICS, &gm2, 0, NULL, &mat2);
1511 ok(ret != GDI_ERROR, "GetGlyphOutline error 0x%x\n", GetLastError());
1513 ok(gm1.gmBlackBoxX == gm2.gmBlackBoxX &&
1514 gm1.gmBlackBoxY == gm2.gmBlackBoxY &&
1515 gm1.gmptGlyphOrigin.x == gm2.gmptGlyphOrigin.x &&
1516 gm1.gmptGlyphOrigin.y == gm2.gmptGlyphOrigin.y &&
1517 gm1.gmCellIncX == gm2.gmCellIncX &&
1518 gm1.gmCellIncY == gm2.gmCellIncY,
1519 "gm1=%d,%d,%d,%d,%d,%d gm2=%d,%d,%d,%d,%d,%d\n",
1520 gm1.gmBlackBoxX, gm1.gmBlackBoxY, gm1.gmptGlyphOrigin.x,
1521 gm1.gmptGlyphOrigin.y, gm1.gmCellIncX, gm1.gmCellIncY,
1522 gm2.gmBlackBoxX, gm2.gmBlackBoxY, gm2.gmptGlyphOrigin.x,
1523 gm2.gmptGlyphOrigin.y, gm2.gmCellIncX, gm2.gmCellIncY);
1526 size = GetFontData(hdc, MS_OS2_TAG, 0, NULL, 0);
1527 if (size == GDI_ERROR)
1529 trace("OS/2 chunk was not found\n");
1532 if (size > sizeof(tt_os2))
1534 trace("got too large OS/2 chunk of size %u\n", size);
1535 size = sizeof(tt_os2);
1538 memset(&tt_os2, 0, sizeof(tt_os2));
1539 ret = GetFontData(hdc, MS_OS2_TAG, 0, &tt_os2, size);
1540 ok(ret == size, "GetFontData should return %u not %u\n", size, ret);
1542 version = GET_BE_WORD(tt_os2.version);
1543 trace("OS/2 chunk version %u, vendor %4.4s\n", version, (LPCSTR)&tt_os2.achVendID);
1545 first_unicode_char = GET_BE_WORD(tt_os2.usFirstCharIndex);
1546 last_unicode_char = GET_BE_WORD(tt_os2.usLastCharIndex);
1547 default_char = GET_BE_WORD(tt_os2.usDefaultChar);
1548 break_char = GET_BE_WORD(tt_os2.usBreakChar);
1550 trace("for %s first %x, last %x, default %x, break %x\n", font_name,
1551 first_unicode_char, last_unicode_char, default_char, break_char);
1553 SetLastError(0xdeadbeef);
1554 ret = GetTextMetricsA(hdc, &tmA);
1555 ok(ret, "GetTextMetricsA error %u\n", GetLastError());
1557 trace("A: first %x, last %x, default %x, break %x\n",
1558 tmA.tmFirstChar, tmA.tmLastChar, tmA.tmDefaultChar, tmA.tmBreakChar);
1560 SetLastError(0xdeadbeef);
1561 ret = GetTextMetricsW(hdc, &tmW);
1562 ok(ret, "GetTextMetricsA error %u\n", GetLastError());
1564 trace("W: first %x, last %x, default %x, break %x\n",
1565 tmW.tmFirstChar, tmW.tmLastChar, tmW.tmDefaultChar, tmW.tmBreakChar);
1567 if (lf->lfCharSet == SYMBOL_CHARSET)
1569 test_char = min(last_unicode_char - 0xf000, 255);
1570 ok(tmA.tmLastChar == test_char, "A: tmLastChar for %s %02x != %02x\n",
1571 font_name, tmA.tmLastChar, test_char);
1573 /* It appears that for fonts with SYMBOL_CHARSET Windows always sets
1574 * symbol range to 0 - f0ff
1576 ok(tmW.tmFirstChar == 0, "W: tmFirstChar for %s %02x != 0\n",
1577 font_name, tmW.tmFirstChar);
1578 /* FIXME: Windows returns f0ff here, while Wine f0xx */
1579 ok(tmW.tmLastChar >= 0xf000, "W: tmLastChar for %s %02x != 0xf0ff\n",
1580 font_name, tmW.tmLastChar);
1582 ok(tmA.tmDefaultChar == 0x1f, "A: tmDefaultChar for %s %02x != 0\n",
1583 font_name, tmW.tmDefaultChar);
1584 ok(tmA.tmBreakChar == 0x20, "A: tmBreakChar for %s %02x != 0xf0ff\n",
1585 font_name, tmW.tmBreakChar);
1586 ok(tmW.tmDefaultChar == 0x1f, "W: tmDefaultChar for %s %02x != 0\n",
1587 font_name, tmW.tmDefaultChar);
1588 ok(tmW.tmBreakChar == 0x20, "W: tmBreakChar for %s %02x != 0xf0ff\n",
1589 font_name, tmW.tmBreakChar);
1593 test_char = min(tmW.tmLastChar, 255);
1594 ok(tmA.tmLastChar == test_char, "A: tmLastChar for %s %02x != %02x\n",
1595 font_name, tmA.tmLastChar, test_char);
1597 ok(tmW.tmFirstChar == first_unicode_char, "W: tmFirstChar for %s %02x != %02x\n",
1598 font_name, tmW.tmFirstChar, first_unicode_char);
1599 ok(tmW.tmLastChar == last_unicode_char, "W: tmLastChar for %s %02x != %02x\n",
1600 font_name, tmW.tmLastChar, last_unicode_char);
1602 #if 0 /* FIXME: This doesn't appear to be what Windows does */
1603 test_char = min(tmW.tmFirstChar - 1, 255);
1604 ok(tmA.tmFirstChar == test_char, "A: tmFirstChar for %s %02x != %02x\n",
1605 font_name, tmA.tmFirstChar, test_char);
1607 ret = GetDeviceCaps(hdc, LOGPIXELSX);
1608 ok(tmW.tmDigitizedAspectX == ret, "tmDigitizedAspectX %u != %u\n",
1609 tmW.tmDigitizedAspectX, ret);
1610 ret = GetDeviceCaps(hdc, LOGPIXELSY);
1611 ok(tmW.tmDigitizedAspectX == ret, "tmDigitizedAspectY %u != %u\n",
1612 tmW.tmDigitizedAspectX, ret);
1615 SelectObject(hdc, hfont_old);
1616 DeleteObject(hfont);
1621 static INT CALLBACK enum_truetype_font_proc(const LOGFONT *lf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
1623 INT *enumed = (INT *)lParam;
1625 if (type == TRUETYPE_FONTTYPE)
1628 test_text_metrics(lf);
1633 static void test_GetTextMetrics(void)
1639 SetLastError(0xdeadbeef);
1640 GetTextMetricsW(0, NULL);
1641 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
1643 skip("Skipping GetTextMetrics test on a Win9x platform\n");
1649 memset(&lf, 0, sizeof(lf));
1650 lf.lfCharSet = DEFAULT_CHARSET;
1652 EnumFontFamiliesExA(hdc, &lf, enum_truetype_font_proc, (LPARAM)&enumed, 0);
1653 trace("Tested metrics of %d truetype fonts\n", enumed);
1664 test_bitmap_font_metrics();
1665 test_GdiGetCharDimensions();
1666 test_GetCharABCWidthsW();
1667 test_text_extents();
1668 test_GetGlyphIndices();
1669 test_GetKerningPairs();
1670 test_GetOutlineTextMetrics();
1671 test_SetTextJustification();
1672 test_font_charset();
1673 test_GetFontUnicodeRanges();
1674 /* On Windows Arial has a lot of default charset aliases such as Arial Cyr,
1675 * I'd like to avoid them in this test.
1677 test_EnumFontFamilies("Arial Black", ANSI_CHARSET);
1678 test_EnumFontFamilies("Symbol", SYMBOL_CHARSET);
1679 if (is_truetype_font_installed("Arial Black") &&
1680 (is_truetype_font_installed("Symbol") || is_truetype_font_installed("Wingdings")))
1682 test_EnumFontFamilies("", ANSI_CHARSET);
1683 test_EnumFontFamilies("", SYMBOL_CHARSET);
1684 test_EnumFontFamilies("", DEFAULT_CHARSET);
1687 skip("Arial Black or Symbol/Wingdings is not installed\n");
1688 test_GetTextMetrics();