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 DWORD (WINAPI *pGetGlyphIndicesA)(HDC hdc, LPCSTR lpstr, INT count, LPWORD pgi, DWORD flags);
34 DWORD (WINAPI *pGetGlyphIndicesW)(HDC hdc, LPCWSTR lpstr, INT count, LPWORD pgi, DWORD flags);
36 static INT CALLBACK is_font_installed_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
41 static BOOL is_font_installed(const char *name)
46 if(!EnumFontFamiliesA(hdc, name, is_font_installed_proc, 0))
53 static void check_font(const char* test, const LOGFONTA* lf, HFONT hfont)
61 ret = GetObject(hfont, sizeof(getobj_lf), &getobj_lf);
62 /* NT4 tries to be clever and only returns the minimum length */
63 while (lf->lfFaceName[minlen] && minlen < LF_FACESIZE-1)
65 minlen += FIELD_OFFSET(LOGFONTA, lfFaceName) + 1;
66 ok(ret == sizeof(LOGFONTA) || ret == minlen, "%s: GetObject returned %d\n", test, ret);
67 ok(!memcmp(&lf, &lf, FIELD_OFFSET(LOGFONTA, lfFaceName)), "%s: fonts don't match\n", test);
68 ok(!lstrcmpA(lf->lfFaceName, getobj_lf.lfFaceName),
69 "%s: font names don't match: %s != %s\n", test, lf->lfFaceName, getobj_lf.lfFaceName);
72 static HFONT create_font(const char* test, const LOGFONTA* lf)
74 HFONT hfont = CreateFontIndirectA(lf);
75 ok(hfont != 0, "%s: CreateFontIndirect failed\n", test);
77 check_font(test, lf, hfont);
81 static void test_logfont(void)
86 memset(&lf, 0, sizeof lf);
88 lf.lfCharSet = ANSI_CHARSET;
89 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
90 lf.lfWeight = FW_DONTCARE;
93 lf.lfQuality = DEFAULT_QUALITY;
95 lstrcpyA(lf.lfFaceName, "Arial");
96 hfont = create_font("Arial", &lf);
99 memset(&lf, 'A', sizeof(lf));
100 hfont = CreateFontIndirectA(&lf);
101 ok(hfont != 0, "CreateFontIndirectA with strange LOGFONT failed\n");
103 lf.lfFaceName[LF_FACESIZE - 1] = 0;
104 check_font("AAA...", &lf, hfont);
108 static INT CALLBACK font_enum_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
110 if (type & RASTER_FONTTYPE)
112 LOGFONT *lf = (LOGFONT *)lParam;
114 return 0; /* stop enumeration */
117 return 1; /* continue enumeration */
120 static void test_font_metrics(HDC hdc, HFONT hfont, const char *test_str,
121 INT test_str_len, const TEXTMETRICA *tm_orig,
122 const SIZE *size_orig, INT width_orig,
123 INT scale_x, INT scale_y)
133 old_hfont = SelectObject(hdc, hfont);
135 GetTextMetricsA(hdc, &tm);
137 ok(tm.tmHeight == tm_orig->tmHeight * scale_y, "%d != %d\n", tm.tmHeight, tm_orig->tmHeight * scale_y);
138 ok(tm.tmAscent == tm_orig->tmAscent * scale_y, "%d != %d\n", tm.tmAscent, tm_orig->tmAscent * scale_y);
139 ok(tm.tmDescent == tm_orig->tmDescent * scale_y, "%d != %d\n", tm.tmDescent, tm_orig->tmDescent * scale_y);
140 ok(tm.tmAveCharWidth == tm_orig->tmAveCharWidth * scale_x, "%d != %d\n", tm.tmAveCharWidth, tm_orig->tmAveCharWidth * scale_x);
142 GetTextExtentPoint32A(hdc, test_str, test_str_len, &size);
144 ok(size.cx == size_orig->cx * scale_x, "%d != %d\n", size.cx, size_orig->cx * scale_x);
145 ok(size.cy == size_orig->cy * scale_y, "%d != %d\n", size.cy, size_orig->cy * scale_y);
147 GetCharWidthA(hdc, 'A', 'A', &width);
149 ok(width == width_orig * scale_x, "%d != %d\n", width, width_orig * scale_x);
151 SelectObject(hdc, old_hfont);
154 /* see whether GDI scales bitmap font metrics */
155 static void test_bitmap_font(void)
157 static const char test_str[11] = "Test String";
160 HFONT hfont, old_hfont;
163 INT ret, i, width_orig, height_orig;
167 /* "System" has only 1 pixel size defined, otherwise the test breaks */
168 ret = EnumFontFamiliesA(hdc, "System", font_enum_proc, (LPARAM)&bitmap_lf);
172 trace("no bitmap fonts were found, skipping the test\n");
176 trace("found bitmap font %s, height %d\n", bitmap_lf.lfFaceName, bitmap_lf.lfHeight);
178 height_orig = bitmap_lf.lfHeight;
179 hfont = create_font("bitmap", &bitmap_lf);
181 old_hfont = SelectObject(hdc, hfont);
182 ok(GetTextMetricsA(hdc, &tm_orig), "GetTextMetricsA failed\n");
183 ok(GetTextExtentPoint32A(hdc, test_str, sizeof(test_str), &size_orig), "GetTextExtentPoint32A failed\n");
184 ok(GetCharWidthA(hdc, 'A', 'A', &width_orig), "GetCharWidthA failed\n");
185 SelectObject(hdc, old_hfont);
188 /* test fractional scaling */
189 for (i = 1; i < height_orig; i++)
191 hfont = create_font("fractional", &bitmap_lf);
192 test_font_metrics(hdc, hfont, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 1, 1);
196 /* test integer scaling 3x2 */
197 bitmap_lf.lfHeight = height_orig * 2;
198 bitmap_lf.lfWidth *= 3;
199 hfont = create_font("3x2", &bitmap_lf);
202 test_font_metrics(hdc, hfont, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 3, 2);
206 /* test integer scaling 3x3 */
207 bitmap_lf.lfHeight = height_orig * 3;
208 bitmap_lf.lfWidth = 0;
209 hfont = create_font("3x3", &bitmap_lf);
213 test_font_metrics(hdc, hfont, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 3, 3);
220 static INT CALLBACK find_font_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
222 LOGFONT *lf = (LOGFONT *)lParam;
224 if (elf->lfHeight == lf->lfHeight && !strcmp(elf->lfFaceName, lf->lfFaceName))
227 return 0; /* stop enumeration */
229 return 1; /* continue enumeration */
232 #define CP1252_BIT 0x00000001
233 #define CP1250_BIT 0x00000002
234 #define CP1251_BIT 0x00000004
235 #define CP1253_BIT 0x00000008
236 #define CP1254_BIT 0x00000010
237 #define CP1255_BIT 0x00000020
238 #define CP1256_BIT 0x00000040
239 #define CP1257_BIT 0x00000080
240 #define CP1258_BIT 0x00000100
241 #define CP874_BIT 0x00010000
242 #define CP932_BIT 0x00020000
243 #define CP936_BIT 0x00040000
244 #define CP949_BIT 0x00080000
245 #define CP950_BIT 0x00100000
247 static void test_bitmap_font_metrics(void)
249 static const struct font_data
251 const char face_name[LF_FACESIZE];
252 int weight, height, ascent, descent, int_leading, ext_leading;
253 int ave_char_width, max_char_width;
257 { "MS Sans Serif", FW_NORMAL, 13, 11, 2, 2, 0, 5, 11, CP1252_BIT | CP1250_BIT | CP1251_BIT },
258 { "MS Sans Serif", FW_NORMAL, 16, 13, 3, 3, 0, 7, 14, CP1252_BIT | CP1250_BIT | CP1251_BIT },
259 { "MS Sans Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 16, CP1252_BIT | CP1251_BIT },
260 { "MS Sans Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 18, CP1250_BIT },
261 { "MS Sans Serif", FW_NORMAL, 24, 19, 5, 6, 0, 9, 19, CP1252_BIT },
262 { "MS Sans Serif", FW_NORMAL, 24, 19, 5, 6, 0, 9, 24, CP1250_BIT },
263 { "MS Sans Serif", FW_NORMAL, 24, 19, 5, 6, 0, 9, 20, CP1251_BIT },
264 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 5, 0, 12, 24, CP1252_BIT },
265 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 6, 0, 12, 24, CP1250_BIT },
266 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 5, 0, 12, 25, CP1251_BIT },
267 { "MS Sans Serif", FW_NORMAL, 37, 29, 8, 5, 0, 16, 32, CP1252_BIT | CP1250_BIT | CP1251_BIT },
268 { "MS Serif", FW_NORMAL, 10, 8, 2, 2, 0, 4, 8, CP1252_BIT | CP1250_BIT },
269 { "MS Serif", FW_NORMAL, 10, 8, 2, 2, 0, 5, 8, CP1251_BIT },
270 { "MS Serif", FW_NORMAL, 11, 9, 2, 2, 0, 5, 9, CP1252_BIT | CP1250_BIT | CP1251_BIT },
271 { "MS Serif", FW_NORMAL, 13, 11, 2, 2, 0, 5, 11, CP1252_BIT },
272 { "MS Serif", FW_NORMAL, 13, 11, 2, 2, 0, 5, 12, CP1250_BIT | CP1251_BIT },
273 { "MS Serif", FW_NORMAL, 16, 13, 3, 3, 0, 6, 14, CP1252_BIT | CP1250_BIT },
274 { "MS Serif", FW_NORMAL, 16, 13, 3, 3, 0, 6, 16, CP1251_BIT },
275 { "MS Serif", FW_NORMAL, 19, 15, 4, 3, 0, 8, 18, CP1252_BIT | CP1250_BIT },
276 { "MS Serif", FW_NORMAL, 19, 15, 4, 3, 0, 8, 19, CP1251_BIT },
277 { "MS Serif", FW_NORMAL, 21, 16, 5, 3, 0, 9, 17, CP1252_BIT },
278 { "MS Serif", FW_NORMAL, 21, 16, 5, 3, 0, 9, 22, CP1250_BIT },
279 { "MS Serif", FW_NORMAL, 21, 16, 5, 3, 0, 9, 23, CP1251_BIT },
280 { "MS Serif", FW_NORMAL, 27, 21, 6, 3, 0, 12, 23, CP1252_BIT },
281 { "MS Serif", FW_NORMAL, 27, 21, 6, 3, 0, 12, 26, CP1250_BIT },
282 { "MS Serif", FW_NORMAL, 27, 21, 6, 3, 0, 12, 27, CP1251_BIT },
283 { "MS Serif", FW_NORMAL, 35, 27, 8, 3, 0, 16, 33, CP1252_BIT | CP1250_BIT },
284 { "MS Serif", FW_NORMAL, 35, 27, 8, 3, 0, 16, 34, CP1251_BIT },
285 { "Courier", FW_NORMAL, 13, 11, 2, 0, 0, 8, 8, CP1252_BIT | CP1250_BIT | CP1251_BIT },
286 { "Courier", FW_NORMAL, 16, 13, 3, 0, 0, 9, 9, CP1252_BIT | CP1250_BIT | CP1251_BIT },
287 { "Courier", FW_NORMAL, 20, 16, 4, 0, 0, 12, 12, CP1252_BIT | CP1250_BIT | CP1251_BIT },
288 { "System", FW_BOLD, 16, 13, 3, 3, 0, 7, 14, CP1252_BIT },
289 { "System", FW_BOLD, 16, 13, 3, 3, 0, 7, 15, CP1250_BIT | CP1251_BIT },
290 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 1, 2, CP1252_BIT},
291 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 1, 8, CP1250_BIT | CP1251_BIT },
292 { "Small Fonts", FW_NORMAL, 5, 4, 1, 1, 0, 3, 4, CP1252_BIT },
293 { "Small Fonts", FW_NORMAL, 5, 4, 1, 1, 0, 2, 8, CP1250_BIT | CP1251_BIT },
294 { "Small Fonts", FW_NORMAL, 6, 5, 1, 1, 0, 3, 13, CP1252_BIT },
295 { "Small Fonts", FW_NORMAL, 6, 5, 1, 1, 0, 3, 8, CP1250_BIT | CP1251_BIT },
296 { "Small Fonts", FW_NORMAL, 8, 7, 1, 1, 0, 4, 7, CP1252_BIT },
297 { "Small Fonts", FW_NORMAL, 8, 7, 1, 1, 0, 4, 8, CP1250_BIT | CP1251_BIT },
298 { "Small Fonts", FW_NORMAL, 10, 8, 2, 2, 0, 4, 8, CP1252_BIT | CP1250_BIT },
299 { "Small Fonts", FW_NORMAL, 10, 8, 2, 2, 0, 5, 8, CP1251_BIT },
300 { "Small Fonts", FW_NORMAL, 11, 9, 2, 2, 0, 5, 9, CP1252_BIT | CP1250_BIT | CP1251_BIT },
301 { "Fixedsys", FW_NORMAL, 15, 12, 3, 3, 0, 8, 8, CP1252_BIT | CP1250_BIT },
302 { "Fixedsys", FW_NORMAL, 16, 12, 4, 3, 0, 8, 8, CP1251_BIT }
304 /* FIXME: add "Terminal" */
308 HFONT hfont, old_hfont;
312 hdc = CreateCompatibleDC(0);
315 for (i = 0; i < sizeof(fd)/sizeof(fd[0]); i++)
319 memset(&lf, 0, sizeof(lf));
321 lf.lfHeight = fd[i].height;
322 strcpy(lf.lfFaceName, fd[i].face_name);
324 for(bit = 0; bit < 32; bit++)
331 if((fd[i].ansi_bitfield & fs[0]) == 0) continue;
332 if(!TranslateCharsetInfo( fs, &csi, TCI_SRCFONTSIG )) continue;
334 lf.lfCharSet = csi.ciCharset;
335 ret = EnumFontFamiliesEx(hdc, &lf, find_font_proc, (LPARAM)&lf, 0);
338 trace("found font %s, height %d charset %x\n", lf.lfFaceName, lf.lfHeight, lf.lfCharSet);
340 hfont = create_font(lf.lfFaceName, &lf);
341 old_hfont = SelectObject(hdc, hfont);
342 ok(GetTextMetrics(hdc, &tm), "GetTextMetrics error %d\n", GetLastError());
344 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);
345 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);
346 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);
347 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);
348 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);
349 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);
350 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);
352 /* Don't run the max char width test on System/ANSI_CHARSET. We have extra characters in our font
353 that make the max width bigger */
354 if(strcmp(lf.lfFaceName, "System") || lf.lfCharSet != ANSI_CHARSET)
355 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);
357 SelectObject(hdc, old_hfont);
365 static void test_GdiGetCharDimensions(void)
371 LONG avgwidth, height;
372 static const char szAlphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
373 typedef LONG (WINAPI *fnGdiGetCharDimensions)(HDC hdc, LPTEXTMETRICW lptm, LONG *height);
374 fnGdiGetCharDimensions GdiGetCharDimensions = (fnGdiGetCharDimensions)GetProcAddress(LoadLibrary("gdi32"), "GdiGetCharDimensions");
375 if (!GdiGetCharDimensions) return;
377 hdc = CreateCompatibleDC(NULL);
379 GetTextExtentPoint(hdc, szAlphabet, strlen(szAlphabet), &size);
380 avgwidth = ((size.cx / 26) + 1) / 2;
382 ret = GdiGetCharDimensions(hdc, &tm, &height);
383 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
384 ok(height == tm.tmHeight, "GdiGetCharDimensions should have set height to %d instead of %d\n", tm.tmHeight, height);
386 ret = GdiGetCharDimensions(hdc, &tm, NULL);
387 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
389 ret = GdiGetCharDimensions(hdc, NULL, NULL);
390 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
393 ret = GdiGetCharDimensions(hdc, NULL, &height);
394 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
395 ok(height == size.cy, "GdiGetCharDimensions should have set height to %d instead of %d\n", size.cy, height);
400 static void test_GetCharABCWidthsW(void)
404 typedef BOOL (WINAPI *fnGetCharABCWidthsW)(HDC hdc, UINT first, UINT last, LPABC abc);
405 fnGetCharABCWidthsW GetCharABCWidthsW = (fnGetCharABCWidthsW)GetProcAddress(LoadLibrary("gdi32"), "GetCharABCWidthsW");
406 if (!GetCharABCWidthsW) return;
408 ret = GetCharABCWidthsW(NULL, 'a', 'a', abc);
409 ok(!ret, "GetCharABCWidthsW should have returned FALSE\n");
412 static void test_text_extents(void)
414 static const WCHAR wt[] = {'O','n','e','\n','t','w','o',' ','3',0};
416 INT i, len, fit1, fit2;
424 memset(&lf, 0, sizeof(lf));
425 strcpy(lf.lfFaceName, "Arial");
428 hfont = CreateFontIndirectA(&lf);
430 hfont = SelectObject(hdc, hfont);
431 GetTextMetricsA(hdc, &tm);
432 GetTextExtentPointA(hdc, "o", 1, &sz);
433 ok(sz.cy == tm.tmHeight, "cy %d tmHeight %d\n", sz.cy, tm.tmHeight);
435 SetLastError(0xdeadbeef);
436 GetTextExtentExPointW(hdc, wt, 1, 1, &fit1, &fit2, &sz1);
437 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
439 skip("Skipping remainder of text extents test on a Win9x platform\n");
440 hfont = SelectObject(hdc, hfont);
447 extents = HeapAlloc(GetProcessHeap(), 0, len * sizeof extents[0]);
448 memset(extents, 0, len * sizeof extents[0]);
449 extents[0] = 1; /* So that the increasing sequence test will fail
450 if the extents array is untouched. */
451 GetTextExtentExPointW(hdc, wt, len, 32767, &fit1, extents, &sz1);
452 GetTextExtentPointW(hdc, wt, len, &sz2);
454 "cy from GetTextExtentExPointW (%d) and GetTextExtentPointW (%d) differ\n", sz1.cy, sz2.cy);
455 /* Because of the '\n' in the string GetTextExtentExPoint and
456 GetTextExtentPoint return different widths under Win2k, but
457 under WinXP they return the same width. So we don't test that
460 for (i = 1; i < len; ++i)
461 ok(extents[i-1] <= extents[i],
462 "GetTextExtentExPointW generated a non-increasing sequence of partial extents (at position %d)\n",
464 ok(extents[len-1] == sz1.cx, "GetTextExtentExPointW extents and size don't match\n");
465 ok(0 <= fit1 && fit1 <= len, "GetTextExtentExPointW generated illegal value %d for fit\n", fit1);
466 ok(0 < fit1, "GetTextExtentExPointW says we can't even fit one letter in 32767 logical units\n");
467 GetTextExtentExPointW(hdc, wt, len, extents[2], &fit2, NULL, &sz2);
468 ok(sz1.cx == sz2.cx && sz1.cy == sz2.cy, "GetTextExtentExPointW returned different sizes for the same string\n");
469 ok(fit2 == 3, "GetTextExtentExPointW extents isn't consistent with fit\n");
470 GetTextExtentExPointW(hdc, wt, len, extents[2]-1, &fit2, NULL, &sz2);
471 ok(fit2 == 2, "GetTextExtentExPointW extents isn't consistent with fit\n");
472 GetTextExtentExPointW(hdc, wt, 2, 0, NULL, extents + 2, &sz2);
473 ok(extents[0] == extents[2] && extents[1] == extents[3],
474 "GetTextExtentExPointW with lpnFit == NULL returns incorrect results\n");
475 GetTextExtentExPointW(hdc, wt, 2, 0, NULL, NULL, &sz1);
476 ok(sz1.cx == sz2.cx && sz1.cy == sz2.cy,
477 "GetTextExtentExPointW with lpnFit and alpDx both NULL returns incorrect results\n");
478 HeapFree(GetProcessHeap(), 0, extents);
480 hfont = SelectObject(hdc, hfont);
482 ReleaseDC(NULL, hdc);
485 static void test_GetGlyphIndices()
492 WCHAR testtext[] = {'T','e','s','t',0xffff,0};
493 WORD glyphs[(sizeof(testtext)/2)-1];
496 typedef BOOL (WINAPI *fnGetGlyphIndicesW)(HDC hdc, LPCWSTR lpstr, INT count, LPWORD pgi, DWORD flags);
497 fnGetGlyphIndicesW GetGlyphIndicesW = (fnGetGlyphIndicesW)GetProcAddress(LoadLibrary("gdi32"),
499 if (!GetGlyphIndicesW) {
500 trace("GetGlyphIndices not available on platform\n");
504 if(!is_font_installed("Symbol"))
506 trace("Symbol is not installed so skipping this test\n");
510 memset(&lf, 0, sizeof(lf));
511 strcpy(lf.lfFaceName, "Symbol");
514 hfont = CreateFontIndirectA(&lf);
517 ok(GetTextMetrics(hdc, &textm), "GetTextMetric failed\n");
518 flags |= GGI_MARK_NONEXISTING_GLYPHS;
519 charcount = GetGlyphIndicesW(hdc, testtext, (sizeof(testtext)/2)-1, glyphs, flags);
520 ok(charcount == 5, "GetGlyphIndices count of glyphs should = 5 not %d\n", charcount);
521 ok(glyphs[4] == 0x001f, "GetGlyphIndices should have returned a nonexistent char not %04x\n", glyphs[4]);
523 charcount = GetGlyphIndicesW(hdc, testtext, (sizeof(testtext)/2)-1, glyphs, flags);
524 ok(charcount == 5, "GetGlyphIndices count of glyphs should = 5 not %d\n", charcount);
525 ok(glyphs[4] == textm.tmDefaultChar, "GetGlyphIndices should have returned a %04x not %04x\n",
526 textm.tmDefaultChar, glyphs[4]);
529 static void test_GetKerningPairs(void)
531 static const struct kerning_data
533 const char face_name[LF_FACESIZE];
535 /* some interesting fields from OUTLINETEXTMETRIC */
536 LONG tmHeight, tmAscent, tmDescent;
541 UINT otmsCapEmHeight;
546 UINT otmusMinimumPPEM;
547 /* small subset of kerning pairs to test */
548 DWORD total_kern_pairs;
549 const KERNINGPAIR kern_pair[26];
552 {"Arial", 12, 12, 9, 3,
553 2048, 7, -2, 1, 5, 2, 8, -2, 0, 9,
556 {' ','A',-1},{' ','T',0},{' ','Y',0},{'1','1',-1},
557 {'A',' ',-1},{'A','T',-1},{'A','V',-1},{'A','W',0},
558 {'A','Y',-1},{'A','v',0},{'A','w',0},{'A','y',0},
559 {'F',',',-1},{'F','.',-1},{'F','A',-1},{'L',' ',0},
560 {'L','T',-1},{'L','V',-1},{'L','W',-1},{'L','Y',-1},
561 {915,912,+1},{915,913,-1},{910,912,+1},{910,913,-1},
562 {933,970,+1},{933,972,-1}
565 {"Arial", -34, 39, 32, 7,
566 2048, 25, -7, 5, 17, 9, 31, -7, 1, 9,
569 {' ','A',-2},{' ','T',-1},{' ','Y',-1},{'1','1',-3},
570 {'A',' ',-2},{'A','T',-3},{'A','V',-3},{'A','W',-1},
571 {'A','Y',-3},{'A','v',-1},{'A','w',-1},{'A','y',-1},
572 {'F',',',-4},{'F','.',-4},{'F','A',-2},{'L',' ',-1},
573 {'L','T',-3},{'L','V',-3},{'L','W',-3},{'L','Y',-3},
574 {915,912,+3},{915,913,-3},{910,912,+3},{910,913,-3},
575 {933,970,+2},{933,972,-3}
578 { "Arial", 120, 120, 97, 23,
579 2048, 79, -23, 16, 54, 27, 98, -23, 4, 9,
582 {' ','A',-6},{' ','T',-2},{' ','Y',-2},{'1','1',-8},
583 {'A',' ',-6},{'A','T',-8},{'A','V',-8},{'A','W',-4},
584 {'A','Y',-8},{'A','v',-2},{'A','w',-2},{'A','y',-2},
585 {'F',',',-12},{'F','.',-12},{'F','A',-6},{'L',' ',-4},
586 {'L','T',-8},{'L','V',-8},{'L','W',-8},{'L','Y',-8},
587 {915,912,+9},{915,913,-10},{910,912,+9},{910,913,-8},
588 {933,970,+6},{933,972,-10}
591 #if 0 /* this set fails due to +1/-1 errors (rounding bug?), needs investigation. */
592 { "Arial", 1024 /* usually 1/2 of EM Square */, 1024, 830, 194,
593 2048, 668, -193, 137, 459, 229, 830, -194, 30, 9,
596 {' ','A',-51},{' ','T',-17},{' ','Y',-17},{'1','1',-68},
597 {'A',' ',-51},{'A','T',-68},{'A','V',-68},{'A','W',-34},
598 {'A','Y',-68},{'A','v',-17},{'A','w',-17},{'A','y',-17},
599 {'F',',',-102},{'F','.',-102},{'F','A',-51},{'L',' ',-34},
600 {'L','T',-68},{'L','V',-68},{'L','W',-68},{'L','Y',-68},
601 {915,912,+73},{915,913,-84},{910,912,+76},{910,913,-68},
602 {933,970,+54},{933,972,-83}
608 HFONT hfont, hfont_old;
609 KERNINGPAIR *kern_pair;
611 DWORD total_kern_pairs, ret, i, n, matches;
615 /* GetKerningPairsA maps unicode set of kerning pairs to current code page
616 * which may render this test unusable, so we're trying to avoid that.
618 SetLastError(0xdeadbeef);
619 GetKerningPairsW(hdc, 0, NULL);
620 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
622 skip("Skipping the GetKerningPairs test on a Win9x platform\n");
627 for (i = 0; i < sizeof(kd)/sizeof(kd[0]); i++)
629 OUTLINETEXTMETRICW otm;
631 if (!is_font_installed(kd[i].face_name))
633 trace("%s is not installed so skipping this test\n", kd[i].face_name);
637 trace("testing font %s, height %d\n", kd[i].face_name, kd[i].height);
639 memset(&lf, 0, sizeof(lf));
640 strcpy(lf.lfFaceName, kd[i].face_name);
641 lf.lfHeight = kd[i].height;
642 hfont = CreateFontIndirect(&lf);
645 hfont_old = SelectObject(hdc, hfont);
647 SetLastError(0xdeadbeef);
648 otm.otmSize = sizeof(otm); /* just in case for Win9x compatibility */
649 ok(GetOutlineTextMetricsW(hdc, sizeof(otm), &otm) == sizeof(otm), "GetOutlineTextMetricsW error %d\n", GetLastError());
651 ok(kd[i].tmHeight == otm.otmTextMetrics.tmHeight, "expected %d, got %d\n",
652 kd[i].tmHeight, otm.otmTextMetrics.tmHeight);
653 ok(kd[i].tmAscent == otm.otmTextMetrics.tmAscent, "expected %d, got %d\n",
654 kd[i].tmAscent, otm.otmTextMetrics.tmAscent);
655 ok(kd[i].tmDescent == otm.otmTextMetrics.tmDescent, "expected %d, got %d\n",
656 kd[i].tmDescent, otm.otmTextMetrics.tmDescent);
658 ok(kd[i].otmEMSquare == otm.otmEMSquare, "expected %u, got %u\n",
659 kd[i].otmEMSquare, otm.otmEMSquare);
660 ok(kd[i].otmAscent == otm.otmAscent, "expected %d, got %d\n",
661 kd[i].otmAscent, otm.otmAscent);
662 ok(kd[i].otmDescent == otm.otmDescent, "expected %d, got %d\n",
663 kd[i].otmDescent, otm.otmDescent);
664 ok(kd[i].otmLineGap == otm.otmLineGap, "expected %u, got %u\n",
665 kd[i].otmLineGap, otm.otmLineGap);
667 ok(kd[i].otmsCapEmHeight == otm.otmsCapEmHeight, "expected %u, got %u\n",
668 kd[i].otmsCapEmHeight, otm.otmsCapEmHeight);
669 ok(kd[i].otmsXHeight == otm.otmsXHeight, "expected %u, got %u\n",
670 kd[i].otmsXHeight, otm.otmsXHeight);
671 ok(kd[i].otmMacAscent == otm.otmMacAscent, "expected %d, got %d\n",
672 kd[i].otmMacAscent, otm.otmMacAscent);
673 ok(kd[i].otmMacDescent == otm.otmMacDescent, "expected %d, got %d\n",
674 kd[i].otmMacDescent, otm.otmMacDescent);
675 /* FIXME: this one sometimes succeeds due to expected 0, enable it when removing todo */
676 if (0) ok(kd[i].otmMacLineGap == otm.otmMacLineGap, "expected %u, got %u\n",
677 kd[i].otmMacLineGap, otm.otmMacLineGap);
678 ok(kd[i].otmusMinimumPPEM == otm.otmusMinimumPPEM, "expected %u, got %u\n",
679 kd[i].otmusMinimumPPEM, otm.otmusMinimumPPEM);
682 total_kern_pairs = GetKerningPairsW(hdc, 0, NULL);
683 trace("total_kern_pairs %u\n", total_kern_pairs);
684 kern_pair = HeapAlloc(GetProcessHeap(), 0, total_kern_pairs * sizeof(*kern_pair));
686 #if 0 /* Win98 (GetKerningPairsA) and XP behave differently here, the test passes on XP */
687 SetLastError(0xdeadbeef);
688 ret = GetKerningPairsW(hdc, 0, kern_pair);
689 ok(GetLastError() == ERROR_INVALID_PARAMETER,
690 "got error %ld, expected ERROR_INVALID_PARAMETER\n", GetLastError());
691 ok(ret == 0, "got %lu, expected 0\n", ret);
694 ret = GetKerningPairsW(hdc, 100, NULL);
695 ok(ret == total_kern_pairs, "got %u, expected %u\n", ret, total_kern_pairs);
697 ret = GetKerningPairsW(hdc, total_kern_pairs/2, kern_pair);
698 ok(ret == total_kern_pairs/2, "got %u, expected %u\n", ret, total_kern_pairs/2);
700 ret = GetKerningPairsW(hdc, total_kern_pairs, kern_pair);
701 ok(ret == total_kern_pairs, "got %u, expected %u\n", ret, total_kern_pairs);
705 for (n = 0; n < ret; n++)
709 if (kern_pair[n].wFirst < 127 && kern_pair[n].wSecond < 127)
710 trace("{'%c','%c',%d},\n",
711 kern_pair[n].wFirst, kern_pair[n].wSecond, kern_pair[n].iKernAmount);
713 for (j = 0; j < kd[i].total_kern_pairs; j++)
715 if (kern_pair[n].wFirst == kd[i].kern_pair[j].wFirst &&
716 kern_pair[n].wSecond == kd[i].kern_pair[j].wSecond)
718 ok(kern_pair[n].iKernAmount == kd[i].kern_pair[j].iKernAmount,
719 "pair %d:%d got %d, expected %d\n",
720 kern_pair[n].wFirst, kern_pair[n].wSecond,
721 kern_pair[n].iKernAmount, kd[i].kern_pair[j].iKernAmount);
727 ok(matches == kd[i].total_kern_pairs, "got matches %u, expected %u\n",
728 matches, kd[i].total_kern_pairs);
730 HeapFree(GetProcessHeap(), 0, kern_pair);
732 SelectObject(hdc, hfont_old);
739 static void test_GetOutlineTextMetrics(void)
741 OUTLINETEXTMETRIC *otm;
743 HFONT hfont, hfont_old;
747 if (!is_font_installed("Arial"))
749 skip("Arial is not installed\n");
755 memset(&lf, 0, sizeof(lf));
756 strcpy(lf.lfFaceName, "Arial");
758 lf.lfWeight = FW_NORMAL;
759 lf.lfPitchAndFamily = DEFAULT_PITCH;
760 lf.lfQuality = PROOF_QUALITY;
761 hfont = CreateFontIndirect(&lf);
764 hfont_old = SelectObject(hdc, hfont);
765 otm_size = GetOutlineTextMetrics(hdc, 0, NULL);
766 trace("otm buffer size %u (0x%x)\n", otm_size, otm_size);
768 otm = HeapAlloc(GetProcessHeap(), 0, otm_size);
770 memset(otm, 0xAA, otm_size);
771 SetLastError(0xdeadbeef);
772 otm->otmSize = sizeof(*otm); /* just in case for Win9x compatibility */
773 ret = GetOutlineTextMetrics(hdc, otm->otmSize, otm);
774 ok(ret == 1 /* Win9x */ ||
775 ret == otm->otmSize /* XP*/,
776 "expected %u, got %u, error %d\n", otm->otmSize, ret, GetLastError());
777 if (ret != 1) /* Win9x doesn't care about pointing beyond of the buffer */
779 ok(otm->otmpFamilyName == NULL, "expected NULL got %p\n", otm->otmpFamilyName);
780 ok(otm->otmpFaceName == NULL, "expected NULL got %p\n", otm->otmpFaceName);
781 ok(otm->otmpStyleName == NULL, "expected NULL got %p\n", otm->otmpStyleName);
782 ok(otm->otmpFullName == NULL, "expected NULL got %p\n", otm->otmpFullName);
785 memset(otm, 0xAA, otm_size);
786 SetLastError(0xdeadbeef);
787 otm->otmSize = otm_size; /* just in case for Win9x compatibility */
788 ret = GetOutlineTextMetrics(hdc, otm->otmSize, otm);
789 ok(ret == 1 /* Win9x */ ||
790 ret == otm->otmSize /* XP*/,
791 "expected %u, got %u, error %d\n", otm->otmSize, ret, GetLastError());
792 if (ret != 1) /* Win9x doesn't care about pointing beyond of the buffer */
794 ok(otm->otmpFamilyName != NULL, "expected not NULL got %p\n", otm->otmpFamilyName);
795 ok(otm->otmpFaceName != NULL, "expected not NULL got %p\n", otm->otmpFaceName);
796 ok(otm->otmpStyleName != NULL, "expected not NULL got %p\n", otm->otmpStyleName);
797 ok(otm->otmpFullName != NULL, "expected not NULL got %p\n", otm->otmpFullName);
800 /* ask about truncated data */
801 memset(otm, 0xAA, otm_size);
802 SetLastError(0xdeadbeef);
803 otm->otmSize = sizeof(*otm) - sizeof(LPSTR); /* just in case for Win9x compatibility */
804 ret = GetOutlineTextMetrics(hdc, otm->otmSize, otm);
805 ok(ret == 1 /* Win9x */ ||
806 ret == otm->otmSize /* XP*/,
807 "expected %u, got %u, error %d\n", otm->otmSize, ret, GetLastError());
808 if (ret != 1) /* Win9x doesn't care about pointing beyond of the buffer */
810 ok(otm->otmpFamilyName == NULL, "expected NULL got %p\n", otm->otmpFamilyName);
811 ok(otm->otmpFaceName == NULL, "expected NULL got %p\n", otm->otmpFaceName);
812 ok(otm->otmpStyleName == NULL, "expected NULL got %p\n", otm->otmpStyleName);
814 ok(otm->otmpFullName == (LPSTR)0xAAAAAAAA, "expected 0xAAAAAAAA got %p\n", otm->otmpFullName);
816 HeapFree(GetProcessHeap(), 0, otm);
818 SelectObject(hdc, hfont_old);
824 static BOOL get_glyph_indices(INT charset, UINT code_page, WORD *idx, UINT count, BOOL unicode)
828 HFONT hfont, hfont_old;
833 assert(count <= 128);
835 memset(&lf, 0, sizeof(lf));
837 lf.lfCharSet = charset;
839 lstrcpyA(lf.lfFaceName, "Arial");
840 SetLastError(0xdeadbeef);
841 hfont = CreateFontIndirectA(&lf);
842 ok(hfont != 0, "CreateFontIndirectA error %u\n", GetLastError());
845 hfont_old = SelectObject(hdc, hfont);
847 cs = GetTextCharset(hdc);
848 ok(cs == charset, "expected %d, got %d\n", charset, cs);
850 if (!TranslateCharsetInfo((DWORD *)cs, &csi, TCI_SRCCHARSET))
852 trace("Can't find codepage for charset %d\n", cs);
856 ok(csi.ciACP == code_page, "expected %d, got %d\n", code_page, csi.ciACP);
861 WCHAR unicode_buf[128];
863 for (i = 0; i < count; i++) ansi_buf[i] = (BYTE)(i + 128);
865 MultiByteToWideChar(code_page, 0, ansi_buf, count, unicode_buf, count);
867 SetLastError(0xdeadbeef);
868 ret = pGetGlyphIndicesW(hdc, unicode_buf, count, idx, 0);
869 ok(ret == count, "GetGlyphIndicesA error %u\n", GetLastError());
875 for (i = 0; i < count; i++) ansi_buf[i] = (BYTE)(i + 128);
877 SetLastError(0xdeadbeef);
878 ret = pGetGlyphIndicesA(hdc, ansi_buf, count, idx, 0);
879 ok(ret == count, "GetGlyphIndicesA error %u\n", GetLastError());
882 SelectObject(hdc, hfont_old);
890 static void testJustification(HDC hdc, PSTR str, RECT *clientArea)
894 outputWidth = 0, /* to test TabbedTextOut() */
895 justifiedWidth = 0, /* to test GetTextExtentExPointW() */
896 areaWidth = clientArea->right - clientArea->left,
898 BOOL lastExtent = FALSE;
899 PSTR pFirstChar, pLastChar;
905 int GetTextExtentExPointWWidth;
906 int TabbedTextOutWidth;
909 GetTextMetricsA(hdc, &tm);
913 while (*str == tm.tmBreakChar) str++; /* skip leading break chars */
919 /* if not at the end of the string, ... */
920 if (*str == '\0') break;
921 /* ... add the next word to the current extent */
922 while (*str != '\0' && *str++ != tm.tmBreakChar);
924 SetTextJustification(hdc, 0, 0);
925 GetTextExtentPoint32(hdc, pFirstChar, str - pFirstChar - 1, &size);
926 } while ((int) size.cx < areaWidth);
928 /* ignore trailing break chars */
930 while (*(pLastChar - 1) == tm.tmBreakChar)
936 if (*str == '\0' || breakCount <= 0) pLastChar = str;
938 SetTextJustification(hdc, 0, 0);
939 GetTextExtentPoint32(hdc, pFirstChar, pLastChar - pFirstChar, &size);
941 /* do not justify the last extent */
942 if (*str != '\0' && breakCount > 0)
944 SetTextJustification(hdc, areaWidth - size.cx, breakCount);
945 GetTextExtentPoint32(hdc, pFirstChar, pLastChar - pFirstChar, &size);
946 justifiedWidth = size.cx;
948 else lastExtent = TRUE;
950 x = clientArea->left;
952 outputWidth = LOWORD(TabbedTextOut(
953 hdc, x, y, pFirstChar, pLastChar - pFirstChar,
955 /* catch errors and report them */
956 if (!lastExtent && ((outputWidth != areaWidth) || (justifiedWidth != areaWidth)))
958 memset(error[nErrors].extent, 0, 100);
959 memcpy(error[nErrors].extent, pFirstChar, pLastChar - pFirstChar);
960 error[nErrors].TabbedTextOutWidth = outputWidth;
961 error[nErrors].GetTextExtentExPointWWidth = justifiedWidth;
967 } while (*str && y < clientArea->bottom);
969 for (e = 0; e < nErrors; e++)
971 ok(error[e].TabbedTextOutWidth == areaWidth,
972 "The output text (\"%s\") width should be %d, not %d.\n",
973 error[e].extent, areaWidth, error[e].TabbedTextOutWidth);
974 /* The width returned by GetTextExtentPoint32() is exactly the same
975 returned by GetTextExtentExPointW() - see dlls/gdi32/font.c */
976 ok(error[e].GetTextExtentExPointWWidth == areaWidth,
977 "GetTextExtentPointW() for \"%s\" should have returned a width of %d, not %d.\n",
978 error[e].extent, areaWidth, error[e].GetTextExtentExPointWWidth);
982 static void test_SetTextJustification(void)
985 RECT clientArea = {0, 0, 400, 400};
988 static char testText[] =
989 "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do "
990 "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut "
991 "enim ad minim veniam, quis nostrud exercitation ullamco laboris "
992 "nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in "
993 "reprehenderit in voluptate velit esse cillum dolore eu fugiat "
994 "nulla pariatur. Excepteur sint occaecat cupidatat non proident, "
995 "sunt in culpa qui officia deserunt mollit anim id est laborum.";
997 memset(&lf, 0, sizeof lf);
998 lf.lfCharSet = ANSI_CHARSET;
999 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
1000 lf.lfWeight = FW_DONTCARE;
1002 lf.lfQuality = DEFAULT_QUALITY;
1003 lstrcpyA(lf.lfFaceName, "Times New Roman");
1004 hfont = create_font("Times New Roman", &lf);
1005 SelectObject(hdc, hfont);
1007 testJustification(hdc, testText, &clientArea);
1009 DeleteObject(hfont);
1013 static void test_font_charset(void)
1015 static struct charset_data
1019 WORD font_idxA[128], font_idxW[128];
1022 { ANSI_CHARSET, 1252 },
1023 { SYMBOL_CHARSET, CP_SYMBOL },
1024 { RUSSIAN_CHARSET, 1251 }
1028 pGetGlyphIndicesA = (void *)GetProcAddress(GetModuleHandle("gdi32.dll"), "GetGlyphIndicesA");
1029 pGetGlyphIndicesW = (void *)GetProcAddress(GetModuleHandle("gdi32.dll"), "GetGlyphIndicesW");
1031 if (!pGetGlyphIndicesA || !pGetGlyphIndicesW)
1033 skip("Skipping the font charset test on a Win9x platform\n");
1037 if (!is_font_installed("Arial"))
1039 skip("Arial is not installed\n");
1043 for (i = 0; i < sizeof(cd)/sizeof(cd[0]); i++)
1045 get_glyph_indices(cd[i].charset, cd[i].code_page, cd[i].font_idxA, 128, FALSE);
1046 get_glyph_indices(cd[i].charset, cd[i].code_page, cd[i].font_idxW, 128, TRUE);
1047 ok(!memcmp(cd[i].font_idxA, cd[i].font_idxW, 128*sizeof(WORD)), "%d: indices don't match\n", i);
1050 ok(memcmp(cd[0].font_idxW, cd[1].font_idxW, 128*sizeof(WORD)), "0 vs 1: indices shouldn't match\n");
1051 ok(memcmp(cd[0].font_idxW, cd[2].font_idxW, 128*sizeof(WORD)), "0 vs 2: indices shouldn't match\n");
1052 ok(memcmp(cd[1].font_idxW, cd[2].font_idxW, 128*sizeof(WORD)), "1 vs 2: indices shouldn't match\n");
1059 test_bitmap_font_metrics();
1060 test_GdiGetCharDimensions();
1061 test_GetCharABCWidthsW();
1062 test_text_extents();
1063 test_GetGlyphIndices();
1064 test_GetKerningPairs();
1065 test_GetOutlineTextMetrics();
1066 test_SetTextJustification();
1067 test_font_charset();