gdi32: Font tests with Japanese font data. Thanks to Dmitry Timoshkov for the data...
[wine/multimedia.git] / dlls / gdi32 / tests / font.c
blob66d97b7acea35e1fcb80693207f25be90ea72f8e
1 /*
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
22 #include <stdarg.h>
23 #include <assert.h>
25 #include "windef.h"
26 #include "winbase.h"
27 #include "wingdi.h"
28 #include "winuser.h"
29 #include "winnls.h"
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;
58 return 0;
61 static BOOL is_truetype_font_installed(const char *name)
63 HDC hdc = GetDC(0);
64 BOOL ret = FALSE;
66 if (!EnumFontFamiliesA(hdc, name, is_truetype_font_installed_proc, 0))
67 ret = TRUE;
69 ReleaseDC(0, hdc);
70 return ret;
73 static INT CALLBACK is_font_installed_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
75 return 0;
78 static BOOL is_font_installed(const char *name)
80 HDC hdc = GetDC(0);
81 BOOL ret = FALSE;
83 if(!EnumFontFamiliesA(hdc, name, is_font_installed_proc, 0))
84 ret = TRUE;
86 ReleaseDC(0, hdc);
87 return ret;
90 static void check_font(const char* test, const LOGFONTA* lf, HFONT hfont)
92 LOGFONTA getobj_lf;
93 int ret, minlen = 0;
95 if (!hfont)
96 return;
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)
101 minlen++;
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);
113 if (hfont)
114 check_font(test, lf, hfont);
115 return hfont;
118 static void test_logfont(void)
120 LOGFONTA lf;
121 HFONT hfont;
123 memset(&lf, 0, sizeof lf);
125 lf.lfCharSet = ANSI_CHARSET;
126 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
127 lf.lfWeight = FW_DONTCARE;
128 lf.lfHeight = 16;
129 lf.lfWidth = 16;
130 lf.lfQuality = DEFAULT_QUALITY;
132 lstrcpyA(lf.lfFaceName, "Arial");
133 hfont = create_font("Arial", &lf);
134 DeleteObject(hfont);
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);
142 DeleteObject(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;
150 *lf = *elf;
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)
162 HFONT old_hfont;
163 TEXTMETRICA tm;
164 SIZE size;
165 INT width;
167 if (!hfont)
168 return;
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";
195 HDC hdc;
196 LOGFONTA bitmap_lf;
197 HFONT hfont, old_hfont;
198 TEXTMETRICA tm_orig;
199 SIZE size_orig;
200 INT ret, i, width_orig, height_orig;
202 hdc = GetDC(0);
204 /* "System" has only 1 pixel size defined, otherwise the test breaks */
205 ret = EnumFontFamiliesA(hdc, "System", font_enum_proc, (LPARAM)&bitmap_lf);
206 if (ret)
208 ReleaseDC(0, hdc);
209 trace("no bitmap fonts were found, skipping the test\n");
210 return;
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);
223 DeleteObject(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);
230 DeleteObject(hfont);
233 /* test integer scaling 3x2 */
234 bitmap_lf.lfHeight = height_orig * 2;
235 bitmap_lf.lfWidth *= 3;
236 hfont = create_font("3x2", &bitmap_lf);
237 todo_wine
239 test_font_metrics(hdc, hfont, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 3, 2);
241 DeleteObject(hfont);
243 /* test integer scaling 3x3 */
244 bitmap_lf.lfHeight = height_orig * 3;
245 bitmap_lf.lfWidth = 0;
246 hfont = create_font("3x3", &bitmap_lf);
248 todo_wine
250 test_font_metrics(hdc, hfont, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 3, 3);
252 DeleteObject(hfont);
254 ReleaseDC(0, hdc);
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))
263 *lf = *elf;
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;
291 DWORD ansi_bitfield;
292 } fd[] =
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 },
328 * TODO: the system for CP932 should be NORMAL, not BOLD. However that would
329 * require a new system.sfd for that font
331 { "System", FW_BOLD, 18, 16, 2, 0, 2, 8, 16, CP932_BIT },
332 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 1, 2, CP1252_BIT },
333 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 1, 8, CP1250_BIT | CP1251_BIT },
334 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 2, 4, CP932_BIT },
335 { "Small Fonts", FW_NORMAL, 5, 4, 1, 1, 0, 3, 4, CP1252_BIT },
336 { "Small Fonts", FW_NORMAL, 5, 4, 1, 1, 0, 2, 8, CP1250_BIT | CP1251_BIT },
337 { "Small Fonts", FW_NORMAL, 5, 4, 1, 0, 0, 3, 6, CP932_BIT },
338 { "Small Fonts", FW_NORMAL, 6, 5, 1, 1, 0, 3, 13, CP1252_BIT },
339 { "Small Fonts", FW_NORMAL, 6, 5, 1, 1, 0, 3, 8, CP1250_BIT | CP1251_BIT },
340 { "Small Fonts", FW_NORMAL, 6, 5, 1, 0, 0, 4, 8, CP932_BIT },
341 { "Small Fonts", FW_NORMAL, 8, 7, 1, 1, 0, 4, 7, CP1252_BIT },
342 { "Small Fonts", FW_NORMAL, 8, 7, 1, 1, 0, 4, 8, CP1250_BIT | CP1251_BIT },
343 { "Small Fonts", FW_NORMAL, 8, 7, 1, 0, 0, 5, 10, CP932_BIT },
344 { "Small Fonts", FW_NORMAL, 10, 8, 2, 2, 0, 4, 8, CP1252_BIT | CP1250_BIT },
345 { "Small Fonts", FW_NORMAL, 10, 8, 2, 2, 0, 5, 8, CP1251_BIT },
346 { "Small Fonts", FW_NORMAL, 10, 8, 2, 0, 0, 6, 12, CP932_BIT },
347 { "Small Fonts", FW_NORMAL, 11, 9, 2, 2, 0, 5, 9, CP1252_BIT | CP1250_BIT | CP1251_BIT },
348 /* No proper small font for Japanese yet */
349 /* { "Small Fonts", FW_NORMAL, 11, 9, 2, 0, 0, 7, 14, CP932_BIT }, */
350 { "Fixedsys", FW_NORMAL, 15, 12, 3, 3, 0, 8, 8, CP1252_BIT | CP1250_BIT },
351 { "Fixedsys", FW_NORMAL, 16, 12, 4, 3, 0, 8, 8, CP1251_BIT },
352 { "FixedSys", FW_NORMAL, 18, 16, 2, 0, 0, 8, 16, CP932_BIT }
354 /* FIXME: add "Terminal" */
356 HDC hdc;
357 LOGFONT lf;
358 HFONT hfont, old_hfont;
359 TEXTMETRIC tm;
360 INT ret, i;
362 hdc = CreateCompatibleDC(0);
363 assert(hdc);
365 for (i = 0; i < sizeof(fd)/sizeof(fd[0]); i++)
367 int bit;
369 memset(&lf, 0, sizeof(lf));
371 lf.lfHeight = fd[i].height;
372 strcpy(lf.lfFaceName, fd[i].face_name);
374 for(bit = 0; bit < 32; bit++)
376 DWORD fs[2];
377 CHARSETINFO csi;
379 fs[0] = 1L << bit;
380 fs[1] = 0;
381 if((fd[i].ansi_bitfield & fs[0]) == 0) continue;
382 if(!TranslateCharsetInfo( fs, &csi, TCI_SRCFONTSIG )) continue;
384 lf.lfCharSet = csi.ciCharset;
385 ret = EnumFontFamiliesEx(hdc, &lf, find_font_proc, (LPARAM)&lf, 0);
386 if (ret) continue;
388 trace("found font %s, height %d charset %x\n", lf.lfFaceName, lf.lfHeight, lf.lfCharSet);
390 hfont = create_font(lf.lfFaceName, &lf);
391 old_hfont = SelectObject(hdc, hfont);
392 ok(GetTextMetrics(hdc, &tm), "GetTextMetrics error %d\n", GetLastError());
394 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);
395 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);
396 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);
397 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);
398 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);
399 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);
400 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);
402 /* Don't run the max char width test on System/ANSI_CHARSET. We have extra characters in our font
403 that make the max width bigger */
404 if(strcmp(lf.lfFaceName, "System") || lf.lfCharSet != ANSI_CHARSET)
405 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);
407 SelectObject(hdc, old_hfont);
408 DeleteObject(hfont);
412 DeleteDC(hdc);
415 static void test_GdiGetCharDimensions(void)
417 HDC hdc;
418 TEXTMETRICW tm;
419 LONG ret;
420 SIZE size;
421 LONG avgwidth, height;
422 static const char szAlphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
424 if (!pGdiGetCharDimensions)
426 skip("GdiGetCharDimensions not available on this platform\n");
427 return;
430 hdc = CreateCompatibleDC(NULL);
432 GetTextExtentPoint(hdc, szAlphabet, strlen(szAlphabet), &size);
433 avgwidth = ((size.cx / 26) + 1) / 2;
435 ret = pGdiGetCharDimensions(hdc, &tm, &height);
436 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
437 ok(height == tm.tmHeight, "GdiGetCharDimensions should have set height to %d instead of %d\n", tm.tmHeight, height);
439 ret = pGdiGetCharDimensions(hdc, &tm, NULL);
440 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
442 ret = pGdiGetCharDimensions(hdc, NULL, NULL);
443 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
445 height = 0;
446 ret = pGdiGetCharDimensions(hdc, NULL, &height);
447 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
448 ok(height == size.cy, "GdiGetCharDimensions should have set height to %d instead of %d\n", size.cy, height);
450 DeleteDC(hdc);
453 static void test_GetCharABCWidthsW(void)
455 BOOL ret;
456 ABC abc[1];
457 if (!pGetCharABCWidthsW) return;
459 ret = pGetCharABCWidthsW(NULL, 'a', 'a', abc);
460 ok(!ret, "GetCharABCWidthsW should have returned FALSE\n");
463 static void test_text_extents(void)
465 static const WCHAR wt[] = {'O','n','e','\n','t','w','o',' ','3',0};
466 LPINT extents;
467 INT i, len, fit1, fit2;
468 LOGFONTA lf;
469 TEXTMETRICA tm;
470 HDC hdc;
471 HFONT hfont;
472 SIZE sz;
473 SIZE sz1, sz2;
475 memset(&lf, 0, sizeof(lf));
476 strcpy(lf.lfFaceName, "Arial");
477 lf.lfHeight = 20;
479 hfont = CreateFontIndirectA(&lf);
480 hdc = GetDC(0);
481 hfont = SelectObject(hdc, hfont);
482 GetTextMetricsA(hdc, &tm);
483 GetTextExtentPointA(hdc, "o", 1, &sz);
484 ok(sz.cy == tm.tmHeight, "cy %d tmHeight %d\n", sz.cy, tm.tmHeight);
486 SetLastError(0xdeadbeef);
487 GetTextExtentExPointW(hdc, wt, 1, 1, &fit1, &fit2, &sz1);
488 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
490 skip("Skipping remainder of text extents test on a Win9x platform\n");
491 hfont = SelectObject(hdc, hfont);
492 DeleteObject(hfont);
493 ReleaseDC(0, hdc);
494 return;
497 len = lstrlenW(wt);
498 extents = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof extents[0]);
499 extents[0] = 1; /* So that the increasing sequence test will fail
500 if the extents array is untouched. */
501 GetTextExtentExPointW(hdc, wt, len, 32767, &fit1, extents, &sz1);
502 GetTextExtentPointW(hdc, wt, len, &sz2);
503 ok(sz1.cy == sz2.cy,
504 "cy from GetTextExtentExPointW (%d) and GetTextExtentPointW (%d) differ\n", sz1.cy, sz2.cy);
505 /* Because of the '\n' in the string GetTextExtentExPoint and
506 GetTextExtentPoint return different widths under Win2k, but
507 under WinXP they return the same width. So we don't test that
508 here. */
510 for (i = 1; i < len; ++i)
511 ok(extents[i-1] <= extents[i],
512 "GetTextExtentExPointW generated a non-increasing sequence of partial extents (at position %d)\n",
514 ok(extents[len-1] == sz1.cx, "GetTextExtentExPointW extents and size don't match\n");
515 ok(0 <= fit1 && fit1 <= len, "GetTextExtentExPointW generated illegal value %d for fit\n", fit1);
516 ok(0 < fit1, "GetTextExtentExPointW says we can't even fit one letter in 32767 logical units\n");
517 GetTextExtentExPointW(hdc, wt, len, extents[2], &fit2, NULL, &sz2);
518 ok(sz1.cx == sz2.cx && sz1.cy == sz2.cy, "GetTextExtentExPointW returned different sizes for the same string\n");
519 ok(fit2 == 3, "GetTextExtentExPointW extents isn't consistent with fit\n");
520 GetTextExtentExPointW(hdc, wt, len, extents[2]-1, &fit2, NULL, &sz2);
521 ok(fit2 == 2, "GetTextExtentExPointW extents isn't consistent with fit\n");
522 GetTextExtentExPointW(hdc, wt, 2, 0, NULL, extents + 2, &sz2);
523 ok(extents[0] == extents[2] && extents[1] == extents[3],
524 "GetTextExtentExPointW with lpnFit == NULL returns incorrect results\n");
525 GetTextExtentExPointW(hdc, wt, 2, 0, NULL, NULL, &sz1);
526 ok(sz1.cx == sz2.cx && sz1.cy == sz2.cy,
527 "GetTextExtentExPointW with lpnFit and alpDx both NULL returns incorrect results\n");
528 HeapFree(GetProcessHeap(), 0, extents);
530 hfont = SelectObject(hdc, hfont);
531 DeleteObject(hfont);
532 ReleaseDC(NULL, hdc);
535 static void test_GetGlyphIndices(void)
537 HDC hdc;
538 HFONT hfont;
539 DWORD charcount;
540 LOGFONTA lf;
541 DWORD flags = 0;
542 WCHAR testtext[] = {'T','e','s','t',0xffff,0};
543 WORD glyphs[(sizeof(testtext)/2)-1];
544 TEXTMETRIC textm;
546 if (!pGetGlyphIndicesW) {
547 skip("GetGlyphIndices not available on platform\n");
548 return;
551 if(!is_font_installed("Symbol"))
553 skip("Symbol is not installed so skipping this test\n");
554 return;
557 memset(&lf, 0, sizeof(lf));
558 strcpy(lf.lfFaceName, "Symbol");
559 lf.lfHeight = 20;
561 hfont = CreateFontIndirectA(&lf);
562 hdc = GetDC(0);
564 ok(GetTextMetrics(hdc, &textm), "GetTextMetric failed\n");
565 flags |= GGI_MARK_NONEXISTING_GLYPHS;
566 charcount = pGetGlyphIndicesW(hdc, testtext, (sizeof(testtext)/2)-1, glyphs, flags);
567 ok(charcount == 5, "GetGlyphIndices count of glyphs should = 5 not %d\n", charcount);
568 ok(glyphs[4] == 0x001f, "GetGlyphIndices should have returned a nonexistent char not %04x\n", glyphs[4]);
569 flags = 0;
570 charcount = pGetGlyphIndicesW(hdc, testtext, (sizeof(testtext)/2)-1, glyphs, flags);
571 ok(charcount == 5, "GetGlyphIndices count of glyphs should = 5 not %d\n", charcount);
572 ok(glyphs[4] == textm.tmDefaultChar, "GetGlyphIndices should have returned a %04x not %04x\n",
573 textm.tmDefaultChar, glyphs[4]);
576 static void test_GetKerningPairs(void)
578 static const struct kerning_data
580 const char face_name[LF_FACESIZE];
581 LONG height;
582 /* some interesting fields from OUTLINETEXTMETRIC */
583 LONG tmHeight, tmAscent, tmDescent;
584 UINT otmEMSquare;
585 INT otmAscent;
586 INT otmDescent;
587 UINT otmLineGap;
588 UINT otmsCapEmHeight;
589 UINT otmsXHeight;
590 INT otmMacAscent;
591 INT otmMacDescent;
592 UINT otmMacLineGap;
593 UINT otmusMinimumPPEM;
594 /* small subset of kerning pairs to test */
595 DWORD total_kern_pairs;
596 const KERNINGPAIR kern_pair[26];
597 } kd[] =
599 {"Arial", 12, 12, 9, 3,
600 2048, 7, -2, 1, 5, 2, 8, -2, 0, 9,
603 {' ','A',-1},{' ','T',0},{' ','Y',0},{'1','1',-1},
604 {'A',' ',-1},{'A','T',-1},{'A','V',-1},{'A','W',0},
605 {'A','Y',-1},{'A','v',0},{'A','w',0},{'A','y',0},
606 {'F',',',-1},{'F','.',-1},{'F','A',-1},{'L',' ',0},
607 {'L','T',-1},{'L','V',-1},{'L','W',-1},{'L','Y',-1},
608 {915,912,+1},{915,913,-1},{910,912,+1},{910,913,-1},
609 {933,970,+1},{933,972,-1}
612 {"Arial", -34, 39, 32, 7,
613 2048, 25, -7, 5, 17, 9, 31, -7, 1, 9,
616 {' ','A',-2},{' ','T',-1},{' ','Y',-1},{'1','1',-3},
617 {'A',' ',-2},{'A','T',-3},{'A','V',-3},{'A','W',-1},
618 {'A','Y',-3},{'A','v',-1},{'A','w',-1},{'A','y',-1},
619 {'F',',',-4},{'F','.',-4},{'F','A',-2},{'L',' ',-1},
620 {'L','T',-3},{'L','V',-3},{'L','W',-3},{'L','Y',-3},
621 {915,912,+3},{915,913,-3},{910,912,+3},{910,913,-3},
622 {933,970,+2},{933,972,-3}
625 { "Arial", 120, 120, 97, 23,
626 2048, 79, -23, 16, 54, 27, 98, -23, 4, 9,
629 {' ','A',-6},{' ','T',-2},{' ','Y',-2},{'1','1',-8},
630 {'A',' ',-6},{'A','T',-8},{'A','V',-8},{'A','W',-4},
631 {'A','Y',-8},{'A','v',-2},{'A','w',-2},{'A','y',-2},
632 {'F',',',-12},{'F','.',-12},{'F','A',-6},{'L',' ',-4},
633 {'L','T',-8},{'L','V',-8},{'L','W',-8},{'L','Y',-8},
634 {915,912,+9},{915,913,-10},{910,912,+9},{910,913,-8},
635 {933,970,+6},{933,972,-10}
638 #if 0 /* this set fails due to +1/-1 errors (rounding bug?), needs investigation. */
639 { "Arial", 1024 /* usually 1/2 of EM Square */, 1024, 830, 194,
640 2048, 668, -193, 137, 459, 229, 830, -194, 30, 9,
643 {' ','A',-51},{' ','T',-17},{' ','Y',-17},{'1','1',-68},
644 {'A',' ',-51},{'A','T',-68},{'A','V',-68},{'A','W',-34},
645 {'A','Y',-68},{'A','v',-17},{'A','w',-17},{'A','y',-17},
646 {'F',',',-102},{'F','.',-102},{'F','A',-51},{'L',' ',-34},
647 {'L','T',-68},{'L','V',-68},{'L','W',-68},{'L','Y',-68},
648 {915,912,+73},{915,913,-84},{910,912,+76},{910,913,-68},
649 {933,970,+54},{933,972,-83}
652 #endif
654 LOGFONT lf;
655 HFONT hfont, hfont_old;
656 KERNINGPAIR *kern_pair;
657 HDC hdc;
658 DWORD total_kern_pairs, ret, i, n, matches;
660 hdc = GetDC(0);
662 /* GetKerningPairsA maps unicode set of kerning pairs to current code page
663 * which may render this test unusable, so we're trying to avoid that.
665 SetLastError(0xdeadbeef);
666 GetKerningPairsW(hdc, 0, NULL);
667 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
669 skip("Skipping the GetKerningPairs test on a Win9x platform\n");
670 ReleaseDC(0, hdc);
671 return;
674 for (i = 0; i < sizeof(kd)/sizeof(kd[0]); i++)
676 OUTLINETEXTMETRICW otm;
678 if (!is_font_installed(kd[i].face_name))
680 trace("%s is not installed so skipping this test\n", kd[i].face_name);
681 continue;
684 trace("testing font %s, height %d\n", kd[i].face_name, kd[i].height);
686 memset(&lf, 0, sizeof(lf));
687 strcpy(lf.lfFaceName, kd[i].face_name);
688 lf.lfHeight = kd[i].height;
689 hfont = CreateFontIndirect(&lf);
690 assert(hfont != 0);
692 hfont_old = SelectObject(hdc, hfont);
694 SetLastError(0xdeadbeef);
695 otm.otmSize = sizeof(otm); /* just in case for Win9x compatibility */
696 ok(GetOutlineTextMetricsW(hdc, sizeof(otm), &otm) == sizeof(otm), "GetOutlineTextMetricsW error %d\n", GetLastError());
698 ok(kd[i].tmHeight == otm.otmTextMetrics.tmHeight, "expected %d, got %d\n",
699 kd[i].tmHeight, otm.otmTextMetrics.tmHeight);
700 ok(kd[i].tmAscent == otm.otmTextMetrics.tmAscent, "expected %d, got %d\n",
701 kd[i].tmAscent, otm.otmTextMetrics.tmAscent);
702 ok(kd[i].tmDescent == otm.otmTextMetrics.tmDescent, "expected %d, got %d\n",
703 kd[i].tmDescent, otm.otmTextMetrics.tmDescent);
705 ok(kd[i].otmEMSquare == otm.otmEMSquare, "expected %u, got %u\n",
706 kd[i].otmEMSquare, otm.otmEMSquare);
707 ok(kd[i].otmAscent == otm.otmAscent, "expected %d, got %d\n",
708 kd[i].otmAscent, otm.otmAscent);
709 ok(kd[i].otmDescent == otm.otmDescent, "expected %d, got %d\n",
710 kd[i].otmDescent, otm.otmDescent);
711 ok(kd[i].otmLineGap == otm.otmLineGap, "expected %u, got %u\n",
712 kd[i].otmLineGap, otm.otmLineGap);
713 todo_wine {
714 ok(kd[i].otmsCapEmHeight == otm.otmsCapEmHeight, "expected %u, got %u\n",
715 kd[i].otmsCapEmHeight, otm.otmsCapEmHeight);
716 ok(kd[i].otmsXHeight == otm.otmsXHeight, "expected %u, got %u\n",
717 kd[i].otmsXHeight, otm.otmsXHeight);
718 ok(kd[i].otmMacAscent == otm.otmMacAscent, "expected %d, got %d\n",
719 kd[i].otmMacAscent, otm.otmMacAscent);
720 ok(kd[i].otmMacDescent == otm.otmMacDescent, "expected %d, got %d\n",
721 kd[i].otmMacDescent, otm.otmMacDescent);
722 /* FIXME: this one sometimes succeeds due to expected 0, enable it when removing todo */
723 if (0) ok(kd[i].otmMacLineGap == otm.otmMacLineGap, "expected %u, got %u\n",
724 kd[i].otmMacLineGap, otm.otmMacLineGap);
725 ok(kd[i].otmusMinimumPPEM == otm.otmusMinimumPPEM, "expected %u, got %u\n",
726 kd[i].otmusMinimumPPEM, otm.otmusMinimumPPEM);
729 total_kern_pairs = GetKerningPairsW(hdc, 0, NULL);
730 trace("total_kern_pairs %u\n", total_kern_pairs);
731 kern_pair = HeapAlloc(GetProcessHeap(), 0, total_kern_pairs * sizeof(*kern_pair));
733 #if 0 /* Win98 (GetKerningPairsA) and XP behave differently here, the test passes on XP */
734 SetLastError(0xdeadbeef);
735 ret = GetKerningPairsW(hdc, 0, kern_pair);
736 ok(GetLastError() == ERROR_INVALID_PARAMETER,
737 "got error %ld, expected ERROR_INVALID_PARAMETER\n", GetLastError());
738 ok(ret == 0, "got %lu, expected 0\n", ret);
739 #endif
741 ret = GetKerningPairsW(hdc, 100, NULL);
742 ok(ret == total_kern_pairs, "got %u, expected %u\n", ret, total_kern_pairs);
744 ret = GetKerningPairsW(hdc, total_kern_pairs/2, kern_pair);
745 ok(ret == total_kern_pairs/2, "got %u, expected %u\n", ret, total_kern_pairs/2);
747 ret = GetKerningPairsW(hdc, total_kern_pairs, kern_pair);
748 ok(ret == total_kern_pairs, "got %u, expected %u\n", ret, total_kern_pairs);
750 matches = 0;
752 for (n = 0; n < ret; n++)
754 DWORD j;
755 #if 0
756 if (kern_pair[n].wFirst < 127 && kern_pair[n].wSecond < 127)
757 trace("{'%c','%c',%d},\n",
758 kern_pair[n].wFirst, kern_pair[n].wSecond, kern_pair[n].iKernAmount);
759 #endif
760 for (j = 0; j < kd[i].total_kern_pairs; j++)
762 if (kern_pair[n].wFirst == kd[i].kern_pair[j].wFirst &&
763 kern_pair[n].wSecond == kd[i].kern_pair[j].wSecond)
765 ok(kern_pair[n].iKernAmount == kd[i].kern_pair[j].iKernAmount,
766 "pair %d:%d got %d, expected %d\n",
767 kern_pair[n].wFirst, kern_pair[n].wSecond,
768 kern_pair[n].iKernAmount, kd[i].kern_pair[j].iKernAmount);
769 matches++;
774 ok(matches == kd[i].total_kern_pairs, "got matches %u, expected %u\n",
775 matches, kd[i].total_kern_pairs);
777 HeapFree(GetProcessHeap(), 0, kern_pair);
779 SelectObject(hdc, hfont_old);
780 DeleteObject(hfont);
783 ReleaseDC(0, hdc);
786 static void test_GetOutlineTextMetrics(void)
788 OUTLINETEXTMETRIC *otm;
789 LOGFONT lf;
790 HFONT hfont, hfont_old;
791 HDC hdc;
792 DWORD ret, otm_size;
794 if (!is_font_installed("Arial"))
796 skip("Arial is not installed\n");
797 return;
800 hdc = GetDC(0);
802 memset(&lf, 0, sizeof(lf));
803 strcpy(lf.lfFaceName, "Arial");
804 lf.lfHeight = -13;
805 lf.lfWeight = FW_NORMAL;
806 lf.lfPitchAndFamily = DEFAULT_PITCH;
807 lf.lfQuality = PROOF_QUALITY;
808 hfont = CreateFontIndirect(&lf);
809 assert(hfont != 0);
811 hfont_old = SelectObject(hdc, hfont);
812 otm_size = GetOutlineTextMetrics(hdc, 0, NULL);
813 trace("otm buffer size %u (0x%x)\n", otm_size, otm_size);
815 otm = HeapAlloc(GetProcessHeap(), 0, otm_size);
817 memset(otm, 0xAA, otm_size);
818 SetLastError(0xdeadbeef);
819 otm->otmSize = sizeof(*otm); /* just in case for Win9x compatibility */
820 ret = GetOutlineTextMetrics(hdc, otm->otmSize, otm);
821 ok(ret == 1 /* Win9x */ ||
822 ret == otm->otmSize /* XP*/,
823 "expected %u, got %u, error %d\n", otm->otmSize, ret, GetLastError());
824 if (ret != 1) /* Win9x doesn't care about pointing beyond of the buffer */
826 ok(otm->otmpFamilyName == NULL, "expected NULL got %p\n", otm->otmpFamilyName);
827 ok(otm->otmpFaceName == NULL, "expected NULL got %p\n", otm->otmpFaceName);
828 ok(otm->otmpStyleName == NULL, "expected NULL got %p\n", otm->otmpStyleName);
829 ok(otm->otmpFullName == NULL, "expected NULL got %p\n", otm->otmpFullName);
832 memset(otm, 0xAA, otm_size);
833 SetLastError(0xdeadbeef);
834 otm->otmSize = otm_size; /* just in case for Win9x compatibility */
835 ret = GetOutlineTextMetrics(hdc, otm->otmSize, otm);
836 ok(ret == 1 /* Win9x */ ||
837 ret == otm->otmSize /* XP*/,
838 "expected %u, got %u, error %d\n", otm->otmSize, ret, GetLastError());
839 if (ret != 1) /* Win9x doesn't care about pointing beyond of the buffer */
841 ok(otm->otmpFamilyName != NULL, "expected not NULL got %p\n", otm->otmpFamilyName);
842 ok(otm->otmpFaceName != NULL, "expected not NULL got %p\n", otm->otmpFaceName);
843 ok(otm->otmpStyleName != NULL, "expected not NULL got %p\n", otm->otmpStyleName);
844 ok(otm->otmpFullName != NULL, "expected not NULL got %p\n", otm->otmpFullName);
847 /* ask about truncated data */
848 memset(otm, 0xAA, otm_size);
849 SetLastError(0xdeadbeef);
850 otm->otmSize = sizeof(*otm) - sizeof(LPSTR); /* just in case for Win9x compatibility */
851 ret = GetOutlineTextMetrics(hdc, otm->otmSize, otm);
852 ok(ret == 1 /* Win9x */ ||
853 ret == otm->otmSize /* XP*/,
854 "expected %u, got %u, error %d\n", otm->otmSize, ret, GetLastError());
855 if (ret != 1) /* Win9x doesn't care about pointing beyond of the buffer */
857 ok(otm->otmpFamilyName == NULL, "expected NULL got %p\n", otm->otmpFamilyName);
858 ok(otm->otmpFaceName == NULL, "expected NULL got %p\n", otm->otmpFaceName);
859 ok(otm->otmpStyleName == NULL, "expected NULL got %p\n", otm->otmpStyleName);
861 ok(otm->otmpFullName == (LPSTR)0xAAAAAAAA, "expected 0xAAAAAAAA got %p\n", otm->otmpFullName);
863 HeapFree(GetProcessHeap(), 0, otm);
865 SelectObject(hdc, hfont_old);
866 DeleteObject(hfont);
868 ReleaseDC(0, hdc);
871 static void testJustification(HDC hdc, PSTR str, RECT *clientArea)
873 INT x, y,
874 breakCount,
875 outputWidth = 0, /* to test TabbedTextOut() */
876 justifiedWidth = 0, /* to test GetTextExtentExPointW() */
877 areaWidth = clientArea->right - clientArea->left,
878 nErrors = 0, e;
879 BOOL lastExtent = FALSE;
880 PSTR pFirstChar, pLastChar;
881 SIZE size;
882 TEXTMETRICA tm;
883 struct err
885 char extent[100];
886 int GetTextExtentExPointWWidth;
887 int TabbedTextOutWidth;
888 } error[10];
890 GetTextMetricsA(hdc, &tm);
891 y = clientArea->top;
892 do {
893 breakCount = 0;
894 while (*str == tm.tmBreakChar) str++; /* skip leading break chars */
895 pFirstChar = str;
897 do {
898 pLastChar = str;
900 /* if not at the end of the string, ... */
901 if (*str == '\0') break;
902 /* ... add the next word to the current extent */
903 while (*str != '\0' && *str++ != tm.tmBreakChar);
904 breakCount++;
905 SetTextJustification(hdc, 0, 0);
906 GetTextExtentPoint32(hdc, pFirstChar, str - pFirstChar - 1, &size);
907 } while ((int) size.cx < areaWidth);
909 /* ignore trailing break chars */
910 breakCount--;
911 while (*(pLastChar - 1) == tm.tmBreakChar)
913 pLastChar--;
914 breakCount--;
917 if (*str == '\0' || breakCount <= 0) pLastChar = str;
919 SetTextJustification(hdc, 0, 0);
920 GetTextExtentPoint32(hdc, pFirstChar, pLastChar - pFirstChar, &size);
922 /* do not justify the last extent */
923 if (*str != '\0' && breakCount > 0)
925 SetTextJustification(hdc, areaWidth - size.cx, breakCount);
926 GetTextExtentPoint32(hdc, pFirstChar, pLastChar - pFirstChar, &size);
927 justifiedWidth = size.cx;
929 else lastExtent = TRUE;
931 x = clientArea->left;
933 outputWidth = LOWORD(TabbedTextOut(
934 hdc, x, y, pFirstChar, pLastChar - pFirstChar,
935 0, NULL, 0));
936 /* catch errors and report them */
937 if (!lastExtent && ((outputWidth != areaWidth) || (justifiedWidth != areaWidth)))
939 memset(error[nErrors].extent, 0, 100);
940 memcpy(error[nErrors].extent, pFirstChar, pLastChar - pFirstChar);
941 error[nErrors].TabbedTextOutWidth = outputWidth;
942 error[nErrors].GetTextExtentExPointWWidth = justifiedWidth;
943 nErrors++;
946 y += size.cy;
947 str = pLastChar;
948 } while (*str && y < clientArea->bottom);
950 for (e = 0; e < nErrors; e++)
952 ok(error[e].TabbedTextOutWidth == areaWidth,
953 "The output text (\"%s\") width should be %d, not %d.\n",
954 error[e].extent, areaWidth, error[e].TabbedTextOutWidth);
955 /* The width returned by GetTextExtentPoint32() is exactly the same
956 returned by GetTextExtentExPointW() - see dlls/gdi32/font.c */
957 ok(error[e].GetTextExtentExPointWWidth == areaWidth,
958 "GetTextExtentPointW() for \"%s\" should have returned a width of %d, not %d.\n",
959 error[e].extent, areaWidth, error[e].GetTextExtentExPointWWidth);
963 static void test_SetTextJustification(void)
965 HDC hdc;
966 RECT clientArea;
967 LOGFONTA lf;
968 HFONT hfont;
969 HWND hwnd;
970 static char testText[] =
971 "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do "
972 "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut "
973 "enim ad minim veniam, quis nostrud exercitation ullamco laboris "
974 "nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in "
975 "reprehenderit in voluptate velit esse cillum dolore eu fugiat "
976 "nulla pariatur. Excepteur sint occaecat cupidatat non proident, "
977 "sunt in culpa qui officia deserunt mollit anim id est laborum.";
979 hwnd = CreateWindowExA(0, "static", "", WS_POPUP, 0,0, 400,400, 0, 0, 0, NULL);
980 GetClientRect( hwnd, &clientArea );
981 hdc = GetDC( hwnd );
983 memset(&lf, 0, sizeof lf);
984 lf.lfCharSet = ANSI_CHARSET;
985 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
986 lf.lfWeight = FW_DONTCARE;
987 lf.lfHeight = 20;
988 lf.lfQuality = DEFAULT_QUALITY;
989 lstrcpyA(lf.lfFaceName, "Times New Roman");
990 hfont = create_font("Times New Roman", &lf);
991 SelectObject(hdc, hfont);
993 testJustification(hdc, testText, &clientArea);
995 DeleteObject(hfont);
996 ReleaseDC(hwnd, hdc);
997 DestroyWindow(hwnd);
1000 static BOOL get_glyph_indices(INT charset, UINT code_page, WORD *idx, UINT count, BOOL unicode)
1002 HDC hdc;
1003 LOGFONTA lf;
1004 HFONT hfont, hfont_old;
1005 CHARSETINFO csi;
1006 FONTSIGNATURE fs;
1007 INT cs;
1008 DWORD i, ret;
1009 char name[64];
1011 assert(count <= 128);
1013 memset(&lf, 0, sizeof(lf));
1015 lf.lfCharSet = charset;
1016 lf.lfHeight = 10;
1017 lstrcpyA(lf.lfFaceName, "Arial");
1018 SetLastError(0xdeadbeef);
1019 hfont = CreateFontIndirectA(&lf);
1020 ok(hfont != 0, "CreateFontIndirectA error %u\n", GetLastError());
1022 hdc = GetDC(0);
1023 hfont_old = SelectObject(hdc, hfont);
1025 cs = GetTextCharsetInfo(hdc, &fs, 0);
1026 ok(cs == charset, "expected %d, got %d\n", charset, cs);
1028 SetLastError(0xdeadbeef);
1029 ret = GetTextFace(hdc, sizeof(name), name);
1030 ok(ret, "GetTextFace error %u\n", GetLastError());
1032 if (charset == SYMBOL_CHARSET)
1034 ok(strcmp("Arial", name), "face name should NOT be Arial\n");
1035 ok(fs.fsCsb[0] & (1 << 31), "symbol encoding should be available\n");
1037 else
1039 ok(!strcmp("Arial", name), "face name should be Arial, not %s\n", name);
1040 ok(!(fs.fsCsb[0] & (1 << 31)), "symbol encoding should NOT be available\n");
1043 if (!TranslateCharsetInfo((DWORD *)cs, &csi, TCI_SRCCHARSET))
1045 trace("Can't find codepage for charset %d\n", cs);
1046 ReleaseDC(0, hdc);
1047 return FALSE;
1049 ok(csi.ciACP == code_page, "expected %d, got %d\n", code_page, csi.ciACP);
1051 if (unicode)
1053 char ansi_buf[128];
1054 WCHAR unicode_buf[128];
1056 for (i = 0; i < count; i++) ansi_buf[i] = (BYTE)(i + 128);
1058 MultiByteToWideChar(code_page, 0, ansi_buf, count, unicode_buf, count);
1060 SetLastError(0xdeadbeef);
1061 ret = pGetGlyphIndicesW(hdc, unicode_buf, count, idx, 0);
1062 ok(ret == count, "GetGlyphIndicesA error %u\n", GetLastError());
1064 else
1066 char ansi_buf[128];
1068 for (i = 0; i < count; i++) ansi_buf[i] = (BYTE)(i + 128);
1070 SetLastError(0xdeadbeef);
1071 ret = pGetGlyphIndicesA(hdc, ansi_buf, count, idx, 0);
1072 ok(ret == count, "GetGlyphIndicesA error %u\n", GetLastError());
1075 SelectObject(hdc, hfont_old);
1076 DeleteObject(hfont);
1078 ReleaseDC(0, hdc);
1080 return TRUE;
1083 static void test_font_charset(void)
1085 static struct charset_data
1087 INT charset;
1088 UINT code_page;
1089 WORD font_idxA[128], font_idxW[128];
1090 } cd[] =
1092 { ANSI_CHARSET, 1252 },
1093 { RUSSIAN_CHARSET, 1251 },
1094 { SYMBOL_CHARSET, CP_SYMBOL } /* keep it as the last one */
1096 int i;
1098 if (!pGetGlyphIndicesA || !pGetGlyphIndicesW)
1100 skip("Skipping the font charset test on a Win9x platform\n");
1101 return;
1104 if (!is_font_installed("Arial"))
1106 skip("Arial is not installed\n");
1107 return;
1110 for (i = 0; i < sizeof(cd)/sizeof(cd[0]); i++)
1112 if (cd[i].charset == SYMBOL_CHARSET)
1114 if (!is_font_installed("Symbol") && !is_font_installed("Wingdings"))
1116 skip("Symbol or Wingdings is not installed\n");
1117 break;
1120 get_glyph_indices(cd[i].charset, cd[i].code_page, cd[i].font_idxA, 128, FALSE);
1121 get_glyph_indices(cd[i].charset, cd[i].code_page, cd[i].font_idxW, 128, TRUE);
1122 ok(!memcmp(cd[i].font_idxA, cd[i].font_idxW, 128*sizeof(WORD)), "%d: indices don't match\n", i);
1125 ok(memcmp(cd[0].font_idxW, cd[1].font_idxW, 128*sizeof(WORD)), "0 vs 1: indices shouldn't match\n");
1126 if (i > 2)
1128 ok(memcmp(cd[0].font_idxW, cd[2].font_idxW, 128*sizeof(WORD)), "0 vs 2: indices shouldn't match\n");
1129 ok(memcmp(cd[1].font_idxW, cd[2].font_idxW, 128*sizeof(WORD)), "1 vs 2: indices shouldn't match\n");
1131 else
1132 skip("Symbol or Wingdings is not installed\n");
1135 static void test_GetFontUnicodeRanges(void)
1137 LOGFONTA lf;
1138 HDC hdc;
1139 HFONT hfont, hfont_old;
1140 DWORD size;
1141 GLYPHSET *gs;
1143 if (!pGetFontUnicodeRanges)
1145 skip("GetFontUnicodeRanges not available before W2K\n");
1146 return;
1149 memset(&lf, 0, sizeof(lf));
1150 lstrcpyA(lf.lfFaceName, "Arial");
1151 hfont = create_font("Arial", &lf);
1153 hdc = GetDC(0);
1154 hfont_old = SelectObject(hdc, hfont);
1156 size = pGetFontUnicodeRanges(NULL, NULL);
1157 ok(!size, "GetFontUnicodeRanges succeeded unexpectedly\n");
1159 size = pGetFontUnicodeRanges(hdc, NULL);
1160 ok(size, "GetFontUnicodeRanges failed unexpectedly\n");
1162 gs = HeapAlloc(GetProcessHeap(), 0, size);
1164 size = pGetFontUnicodeRanges(hdc, gs);
1165 ok(size, "GetFontUnicodeRanges failed\n");
1166 #if 0
1167 for (i = 0; i < gs->cRanges; i++)
1168 trace("%03d wcLow %04x cGlyphs %u\n", i, gs->ranges[i].wcLow, gs->ranges[i].cGlyphs);
1169 #endif
1170 trace("found %u ranges\n", gs->cRanges);
1172 HeapFree(GetProcessHeap(), 0, gs);
1174 SelectObject(hdc, hfont_old);
1175 DeleteObject(hfont);
1176 ReleaseDC(NULL, hdc);
1179 #define MAX_ENUM_FONTS 256
1181 struct enum_font_data
1183 int total;
1184 LOGFONT lf[MAX_ENUM_FONTS];
1187 static INT CALLBACK arial_enum_proc(const LOGFONT *lf, const TEXTMETRIC *tm, DWORD type, LPARAM lParam)
1189 struct enum_font_data *efd = (struct enum_font_data *)lParam;
1191 if (type != TRUETYPE_FONTTYPE) return 1;
1192 #if 0
1193 trace("enumed font \"%s\", charset %d, weight %d, italic %d\n",
1194 lf->lfFaceName, lf->lfCharSet, lf->lfWeight, lf->lfItalic);
1195 #endif
1196 if (efd->total < MAX_ENUM_FONTS)
1197 efd->lf[efd->total++] = *lf;
1199 return 1;
1202 static void get_charset_stats(struct enum_font_data *efd,
1203 int *ansi_charset, int *symbol_charset,
1204 int *russian_charset)
1206 int i;
1208 *ansi_charset = 0;
1209 *symbol_charset = 0;
1210 *russian_charset = 0;
1212 for (i = 0; i < efd->total; i++)
1214 switch (efd->lf[i].lfCharSet)
1216 case ANSI_CHARSET:
1217 (*ansi_charset)++;
1218 break;
1219 case SYMBOL_CHARSET:
1220 (*symbol_charset)++;
1221 break;
1222 case RUSSIAN_CHARSET:
1223 (*russian_charset)++;
1224 break;
1229 static void test_EnumFontFamilies(const char *font_name, INT font_charset)
1231 struct enum_font_data efd;
1232 LOGFONT lf;
1233 HDC hdc;
1234 int i, ret, ansi_charset, symbol_charset, russian_charset;
1236 trace("Testing font %s, charset %d\n", *font_name ? font_name : "<empty>", font_charset);
1238 if (*font_name && !is_truetype_font_installed(font_name))
1240 skip("%s is not installed\n", font_name);
1241 return;
1244 hdc = GetDC(0);
1246 /* Observed behaviour: EnumFontFamilies enumerates aliases like "Arial Cyr"
1247 * while EnumFontFamiliesEx doesn't.
1249 if (!*font_name && font_charset == DEFAULT_CHARSET) /* do it only once */
1251 efd.total = 0;
1252 SetLastError(0xdeadbeef);
1253 ret = EnumFontFamilies(hdc, NULL, arial_enum_proc, (LPARAM)&efd);
1254 ok(ret, "EnumFontFamilies error %u\n", GetLastError());
1255 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
1256 trace("enumerated ansi %d, symbol %d, russian %d fonts for NULL\n",
1257 ansi_charset, symbol_charset, russian_charset);
1258 ok(efd.total > 0, "no fonts enumerated: NULL\n");
1259 ok(ansi_charset > 0, "NULL family should enumerate ANSI_CHARSET\n");
1260 ok(symbol_charset > 0, "NULL family should enumerate SYMBOL_CHARSET\n");
1261 ok(russian_charset > 0, "NULL family should enumerate RUSSIAN_CHARSET\n");
1264 efd.total = 0;
1265 SetLastError(0xdeadbeef);
1266 ret = EnumFontFamilies(hdc, font_name, arial_enum_proc, (LPARAM)&efd);
1267 ok(ret, "EnumFontFamilies error %u\n", GetLastError());
1268 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
1269 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s\n",
1270 ansi_charset, symbol_charset, russian_charset,
1271 *font_name ? font_name : "<empty>");
1272 if (*font_name)
1273 ok(efd.total > 0, "no fonts enumerated: %s\n", font_name);
1274 else
1275 ok(!efd.total, "no fonts should be enumerated for empty font_name\n");
1276 for (i = 0; i < efd.total; i++)
1278 /* FIXME: remove completely once Wine is fixed */
1279 if (efd.lf[i].lfCharSet != font_charset)
1281 todo_wine
1282 ok(efd.lf[i].lfCharSet == font_charset, "%d: got charset %d\n", i, efd.lf[i].lfCharSet);
1284 else
1285 ok(efd.lf[i].lfCharSet == font_charset, "%d: got charset %d\n", i, efd.lf[i].lfCharSet);
1286 ok(!lstrcmp(efd.lf[i].lfFaceName, font_name), "expected %s, got %s\n",
1287 font_name, efd.lf[i].lfFaceName);
1290 memset(&lf, 0, sizeof(lf));
1291 lf.lfCharSet = ANSI_CHARSET;
1292 lstrcpy(lf.lfFaceName, font_name);
1293 efd.total = 0;
1294 SetLastError(0xdeadbeef);
1295 ret = EnumFontFamiliesEx(hdc, &lf, arial_enum_proc, (LPARAM)&efd, 0);
1296 ok(ret, "EnumFontFamiliesEx error %u\n", GetLastError());
1297 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
1298 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s ANSI_CHARSET\n",
1299 ansi_charset, symbol_charset, russian_charset,
1300 *font_name ? font_name : "<empty>");
1301 if (font_charset == SYMBOL_CHARSET)
1303 if (*font_name)
1304 ok(efd.total == 0, "no fonts should be enumerated: %s ANSI_CHARSET\n", font_name);
1305 else
1306 ok(efd.total > 0, "no fonts enumerated: %s\n", font_name);
1308 else
1310 ok(efd.total > 0, "no fonts enumerated: %s ANSI_CHARSET\n", font_name);
1311 for (i = 0; i < efd.total; i++)
1313 ok(efd.lf[i].lfCharSet == ANSI_CHARSET, "%d: got charset %d\n", i, efd.lf[i].lfCharSet);
1314 if (*font_name)
1315 ok(!lstrcmp(efd.lf[i].lfFaceName, font_name), "expected %s, got %s\n",
1316 font_name, efd.lf[i].lfFaceName);
1320 /* DEFAULT_CHARSET should enumerate all available charsets */
1321 memset(&lf, 0, sizeof(lf));
1322 lf.lfCharSet = DEFAULT_CHARSET;
1323 lstrcpy(lf.lfFaceName, font_name);
1324 efd.total = 0;
1325 SetLastError(0xdeadbeef);
1326 EnumFontFamiliesEx(hdc, &lf, arial_enum_proc, (LPARAM)&efd, 0);
1327 ok(ret, "EnumFontFamiliesEx error %u\n", GetLastError());
1328 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
1329 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s DEFAULT_CHARSET\n",
1330 ansi_charset, symbol_charset, russian_charset,
1331 *font_name ? font_name : "<empty>");
1332 ok(efd.total > 0, "no fonts enumerated: %s DEFAULT_CHARSET\n", font_name);
1333 for (i = 0; i < efd.total; i++)
1335 if (*font_name)
1336 ok(!lstrcmp(efd.lf[i].lfFaceName, font_name), "expected %s, got %s\n",
1337 font_name, efd.lf[i].lfFaceName);
1339 if (*font_name)
1341 switch (font_charset)
1343 case ANSI_CHARSET:
1344 ok(ansi_charset > 0,
1345 "ANSI_CHARSET should enumerate ANSI_CHARSET for %s\n", font_name);
1346 ok(!symbol_charset,
1347 "ANSI_CHARSET should NOT enumerate SYMBOL_CHARSET for %s\n", font_name);
1348 ok(russian_charset > 0,
1349 "ANSI_CHARSET should enumerate RUSSIAN_CHARSET for %s\n", font_name);
1350 break;
1351 case SYMBOL_CHARSET:
1352 ok(!ansi_charset,
1353 "SYMBOL_CHARSET should NOT enumerate ANSI_CHARSET for %s\n", font_name);
1354 ok(symbol_charset,
1355 "SYMBOL_CHARSET should enumerate SYMBOL_CHARSET for %s\n", font_name);
1356 ok(!russian_charset,
1357 "SYMBOL_CHARSET should NOT enumerate RUSSIAN_CHARSET for %s\n", font_name);
1358 break;
1359 case DEFAULT_CHARSET:
1360 ok(ansi_charset > 0,
1361 "DEFAULT_CHARSET should enumerate ANSI_CHARSET for %s\n", font_name);
1362 ok(symbol_charset > 0,
1363 "DEFAULT_CHARSET should enumerate SYMBOL_CHARSET for %s\n", font_name);
1364 ok(russian_charset > 0,
1365 "DEFAULT_CHARSET should enumerate RUSSIAN_CHARSET for %s\n", font_name);
1366 break;
1369 else
1371 ok(ansi_charset > 0,
1372 "DEFAULT_CHARSET should enumerate ANSI_CHARSET for %s\n", *font_name ? font_name : "<empty>");
1373 ok(symbol_charset > 0,
1374 "DEFAULT_CHARSET should enumerate SYMBOL_CHARSET for %s\n", *font_name ? font_name : "<empty>");
1375 ok(russian_charset > 0,
1376 "DEFAULT_CHARSET should enumerate RUSSIAN_CHARSET for %s\n", *font_name ? font_name : "<empty>");
1379 memset(&lf, 0, sizeof(lf));
1380 lf.lfCharSet = SYMBOL_CHARSET;
1381 lstrcpy(lf.lfFaceName, font_name);
1382 efd.total = 0;
1383 SetLastError(0xdeadbeef);
1384 EnumFontFamiliesEx(hdc, &lf, arial_enum_proc, (LPARAM)&efd, 0);
1385 ok(ret, "EnumFontFamiliesEx error %u\n", GetLastError());
1386 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
1387 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s SYMBOL_CHARSET\n",
1388 ansi_charset, symbol_charset, russian_charset,
1389 *font_name ? font_name : "<empty>");
1390 if (*font_name && font_charset == ANSI_CHARSET)
1391 ok(efd.total == 0, "no fonts should be enumerated: %s SYMBOL_CHARSET\n", font_name);
1392 else
1394 ok(efd.total > 0, "no fonts enumerated: %s SYMBOL_CHARSET\n", font_name);
1395 for (i = 0; i < efd.total; i++)
1397 ok(efd.lf[i].lfCharSet == SYMBOL_CHARSET, "%d: got charset %d\n", i, efd.lf[i].lfCharSet);
1398 if (*font_name)
1399 ok(!lstrcmp(efd.lf[i].lfFaceName, font_name), "expected %s, got %s\n",
1400 font_name, efd.lf[i].lfFaceName);
1403 ok(!ansi_charset,
1404 "SYMBOL_CHARSET should NOT enumerate ANSI_CHARSET for %s\n", *font_name ? font_name : "<empty>");
1405 ok(symbol_charset > 0,
1406 "SYMBOL_CHARSET should enumerate SYMBOL_CHARSET for %s\n", *font_name ? font_name : "<empty>");
1407 ok(!russian_charset,
1408 "SYMBOL_CHARSET should NOT enumerate RUSSIAN_CHARSET for %s\n", *font_name ? font_name : "<empty>");
1411 ReleaseDC(0, hdc);
1414 /* PANOSE is 10 bytes in size, need to pack the structure properly */
1415 #include "pshpack2.h"
1416 typedef struct
1418 USHORT version;
1419 SHORT xAvgCharWidth;
1420 USHORT usWeightClass;
1421 USHORT usWidthClass;
1422 SHORT fsType;
1423 SHORT ySubscriptXSize;
1424 SHORT ySubscriptYSize;
1425 SHORT ySubscriptXOffset;
1426 SHORT ySubscriptYOffset;
1427 SHORT ySuperscriptXSize;
1428 SHORT ySuperscriptYSize;
1429 SHORT ySuperscriptXOffset;
1430 SHORT ySuperscriptYOffset;
1431 SHORT yStrikeoutSize;
1432 SHORT yStrikeoutPosition;
1433 SHORT sFamilyClass;
1434 PANOSE panose;
1435 ULONG ulUnicodeRange1;
1436 ULONG ulUnicodeRange2;
1437 ULONG ulUnicodeRange3;
1438 ULONG ulUnicodeRange4;
1439 CHAR achVendID[4];
1440 USHORT fsSelection;
1441 USHORT usFirstCharIndex;
1442 USHORT usLastCharIndex;
1443 /* According to the Apple spec, original version didn't have the below fields,
1444 * version numbers were taked from the OpenType spec.
1446 /* version 0 (TrueType 1.5) */
1447 USHORT sTypoAscender;
1448 USHORT sTypoDescender;
1449 USHORT sTypoLineGap;
1450 USHORT usWinAscent;
1451 USHORT usWinDescent;
1452 /* version 1 (TrueType 1.66) */
1453 ULONG ulCodePageRange1;
1454 ULONG ulCodePageRange2;
1455 /* version 2 (OpenType 1.2) */
1456 SHORT sxHeight;
1457 SHORT sCapHeight;
1458 USHORT usDefaultChar;
1459 USHORT usBreakChar;
1460 USHORT usMaxContext;
1461 } TT_OS2_V2;
1462 #include "poppack.h"
1464 #ifdef WORDS_BIGENDIAN
1465 #define GET_BE_WORD(x) (x)
1466 #else
1467 #define GET_BE_WORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
1468 #endif
1470 #define MS_MAKE_TAG(ch0, ch1, ch2, ch3) \
1471 ((DWORD)(BYTE)(ch0) | ((DWORD)(BYTE)(ch1) << 8) | \
1472 ((DWORD)(BYTE)(ch2) << 16) | ((DWORD)(BYTE)(ch3) << 24))
1473 #define MS_OS2_TAG MS_MAKE_TAG('O','S','/','2')
1475 static void test_text_metrics(const LOGFONTA *lf)
1477 HDC hdc;
1478 HFONT hfont, hfont_old;
1479 TEXTMETRICA tmA;
1480 TEXTMETRICW tmW;
1481 UINT first_unicode_char, last_unicode_char, default_char, break_char;
1482 INT test_char;
1483 TT_OS2_V2 tt_os2;
1484 USHORT version;
1485 LONG size, ret;
1486 const char *font_name = lf->lfFaceName;
1488 trace("Testing font metrics for %s, charset %d\n", font_name, lf->lfCharSet);
1490 hdc = GetDC(0);
1492 SetLastError(0xdeadbeef);
1493 hfont = CreateFontIndirectA(lf);
1494 ok(hfont != 0, "CreateFontIndirect error %u\n", GetLastError());
1496 hfont_old = SelectObject(hdc, hfont);
1498 if(lf->lfWidth > 0) {
1499 HFONT hfont2, hfont_prev;
1500 GLYPHMETRICS gm1, gm2;
1501 LOGFONTA lf2 = *lf;
1502 MAT2 mat2 = { {0,1}, {0,0}, {0,0}, {0,1} };
1504 /* negative widths are handled just as positive ones */
1505 lf2.lfWidth *= -1;
1507 SetLastError(0xdeadbeef);
1508 hfont2 = CreateFontIndirectA(&lf2);
1509 ok(hfont2 != 0, "CreateFontIndirect error %u\n", GetLastError());
1510 hfont_prev = SelectObject(hdc, hfont2);
1512 /* filling with 0xaa causes false pass under WINEDEBUG=warn+heap */
1513 memset(&gm1, 0xab, sizeof(gm1));
1514 SetLastError(0xdeadbeef);
1515 ret = GetGlyphOutlineA(hdc, 'x', GGO_METRICS, &gm1, 0, NULL, &mat2);
1516 ok(ret != GDI_ERROR, "GetGlyphOutline error 0x%x\n", GetLastError());
1518 SelectObject(hdc, hfont_prev);
1519 DeleteObject(hfont2);
1521 memset(&gm2, 0xbb, sizeof(gm2));
1522 SetLastError(0xdeadbeef);
1523 ret = GetGlyphOutlineA(hdc, 'x', GGO_METRICS, &gm2, 0, NULL, &mat2);
1524 ok(ret != GDI_ERROR, "GetGlyphOutline error 0x%x\n", GetLastError());
1526 ok(gm1.gmBlackBoxX == gm2.gmBlackBoxX &&
1527 gm1.gmBlackBoxY == gm2.gmBlackBoxY &&
1528 gm1.gmptGlyphOrigin.x == gm2.gmptGlyphOrigin.x &&
1529 gm1.gmptGlyphOrigin.y == gm2.gmptGlyphOrigin.y &&
1530 gm1.gmCellIncX == gm2.gmCellIncX &&
1531 gm1.gmCellIncY == gm2.gmCellIncY,
1532 "gm1=%d,%d,%d,%d,%d,%d gm2=%d,%d,%d,%d,%d,%d\n",
1533 gm1.gmBlackBoxX, gm1.gmBlackBoxY, gm1.gmptGlyphOrigin.x,
1534 gm1.gmptGlyphOrigin.y, gm1.gmCellIncX, gm1.gmCellIncY,
1535 gm2.gmBlackBoxX, gm2.gmBlackBoxY, gm2.gmptGlyphOrigin.x,
1536 gm2.gmptGlyphOrigin.y, gm2.gmCellIncX, gm2.gmCellIncY);
1539 size = GetFontData(hdc, MS_OS2_TAG, 0, NULL, 0);
1540 if (size == GDI_ERROR)
1542 trace("OS/2 chunk was not found\n");
1543 goto end_of_test;
1545 if (size > sizeof(tt_os2))
1547 trace("got too large OS/2 chunk of size %u\n", size);
1548 size = sizeof(tt_os2);
1551 memset(&tt_os2, 0, sizeof(tt_os2));
1552 ret = GetFontData(hdc, MS_OS2_TAG, 0, &tt_os2, size);
1553 ok(ret == size, "GetFontData should return %u not %u\n", size, ret);
1555 version = GET_BE_WORD(tt_os2.version);
1556 trace("OS/2 chunk version %u, vendor %4.4s\n", version, (LPCSTR)&tt_os2.achVendID);
1558 first_unicode_char = GET_BE_WORD(tt_os2.usFirstCharIndex);
1559 last_unicode_char = GET_BE_WORD(tt_os2.usLastCharIndex);
1560 default_char = GET_BE_WORD(tt_os2.usDefaultChar);
1561 break_char = GET_BE_WORD(tt_os2.usBreakChar);
1563 trace("for %s first %x, last %x, default %x, break %x\n", font_name,
1564 first_unicode_char, last_unicode_char, default_char, break_char);
1566 SetLastError(0xdeadbeef);
1567 ret = GetTextMetricsA(hdc, &tmA);
1568 ok(ret, "GetTextMetricsA error %u\n", GetLastError());
1569 trace("A: first %x, last %x, default %x, break %x\n",
1570 tmA.tmFirstChar, tmA.tmLastChar, tmA.tmDefaultChar, tmA.tmBreakChar);
1572 #if 0 /* FIXME: This doesn't appear to be what Windows does */
1573 test_char = min(first_unicode_char - 1, 255);
1574 ok(tmA.tmFirstChar == test_char, "A: tmFirstChar for %s %02x != %02x\n",
1575 font_name, tmA.tmFirstChar, test_char);
1576 #endif
1577 if (lf->lfCharSet == SYMBOL_CHARSET)
1579 test_char = min(last_unicode_char - 0xf000, 255);
1580 ok(tmA.tmLastChar == test_char, "A: tmLastChar for %s %02x != %02x\n",
1581 font_name, tmA.tmLastChar, test_char);
1583 else
1585 test_char = min(last_unicode_char, 255);
1586 ok(tmA.tmLastChar == test_char, "A: tmLastChar for %s %02x != %02x\n",
1587 font_name, tmA.tmLastChar, test_char);
1590 SetLastError(0xdeadbeef);
1591 ret = GetTextMetricsW(hdc, &tmW);
1592 ok(ret || GetLastError() == ERROR_CALL_NOT_IMPLEMENTED,
1593 "GetTextMetricsW error %u\n", GetLastError());
1594 if (ret)
1596 trace("W: first %x, last %x, default %x, break %x\n",
1597 tmW.tmFirstChar, tmW.tmLastChar, tmW.tmDefaultChar,
1598 tmW.tmBreakChar);
1600 if (lf->lfCharSet == SYMBOL_CHARSET)
1602 /* It appears that for fonts with SYMBOL_CHARSET Windows always
1603 * sets symbol range to 0 - f0ff
1605 ok(tmW.tmFirstChar == 0, "W: tmFirstChar for %s %02x != 0\n",
1606 font_name, tmW.tmFirstChar);
1607 /* FIXME: Windows returns f0ff here, while Wine f0xx */
1608 ok(tmW.tmLastChar >= 0xf000, "W: tmLastChar for %s %02x < 0xf000\n",
1609 font_name, tmW.tmLastChar);
1611 ok(tmW.tmDefaultChar == 0x1f, "W: tmDefaultChar for %s %02x != 0x1f\n",
1612 font_name, tmW.tmDefaultChar);
1613 ok(tmW.tmBreakChar == 0x20, "W: tmBreakChar for %s %02x != 0x20\n",
1614 font_name, tmW.tmBreakChar);
1616 else
1618 ok(tmW.tmFirstChar == first_unicode_char, "W: tmFirstChar for %s %02x != %02x\n",
1619 font_name, tmW.tmFirstChar, first_unicode_char);
1620 ok(tmW.tmLastChar == last_unicode_char, "W: tmLastChar for %s %02x != %02x\n",
1621 font_name, tmW.tmLastChar, last_unicode_char);
1623 ret = GetDeviceCaps(hdc, LOGPIXELSX);
1624 ok(tmW.tmDigitizedAspectX == ret, "W: tmDigitizedAspectX %u != %u\n",
1625 tmW.tmDigitizedAspectX, ret);
1626 ret = GetDeviceCaps(hdc, LOGPIXELSY);
1627 ok(tmW.tmDigitizedAspectX == ret, "W: tmDigitizedAspectY %u != %u\n",
1628 tmW.tmDigitizedAspectX, ret);
1631 end_of_test:
1632 SelectObject(hdc, hfont_old);
1633 DeleteObject(hfont);
1635 ReleaseDC(0, hdc);
1638 static INT CALLBACK enum_truetype_font_proc(const LOGFONT *lf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
1640 INT *enumed = (INT *)lParam;
1642 if (type == TRUETYPE_FONTTYPE)
1644 (*enumed)++;
1645 test_text_metrics(lf);
1647 return 1;
1650 static void test_GetTextMetrics(void)
1652 LOGFONTA lf;
1653 HDC hdc;
1654 INT enumed;
1656 hdc = GetDC(0);
1658 memset(&lf, 0, sizeof(lf));
1659 lf.lfCharSet = DEFAULT_CHARSET;
1660 enumed = 0;
1661 EnumFontFamiliesExA(hdc, &lf, enum_truetype_font_proc, (LPARAM)&enumed, 0);
1662 trace("Tested metrics of %d truetype fonts\n", enumed);
1664 ReleaseDC(0, hdc);
1667 static void test_nonexistent_font(void)
1669 LOGFONTA lf;
1670 HDC hdc;
1671 HFONT hfont;
1672 char buf[LF_FACESIZE];
1674 if (!is_truetype_font_installed("Arial Black"))
1676 skip("Arial not installed\n");
1677 return;
1680 hdc = GetDC(0);
1682 memset(&lf, 0, sizeof(lf));
1683 lf.lfHeight = 100;
1684 lf.lfWeight = FW_REGULAR;
1685 lf.lfCharSet = ANSI_CHARSET;
1686 lf.lfPitchAndFamily = FF_SWISS;
1687 strcpy(lf.lfFaceName, "Nonexistent font");
1689 hfont = CreateFontIndirectA(&lf);
1690 hfont = SelectObject(hdc, hfont);
1691 GetTextFaceA(hdc, sizeof(buf), buf);
1692 ok(!lstrcmpiA(buf, "Arial"), "Got %s\n", buf);
1693 DeleteObject(SelectObject(hdc, hfont));
1694 ReleaseDC(0, hdc);
1697 START_TEST(font)
1699 init();
1701 test_logfont();
1702 test_bitmap_font();
1703 test_bitmap_font_metrics();
1704 test_GdiGetCharDimensions();
1705 test_GetCharABCWidthsW();
1706 test_text_extents();
1707 test_GetGlyphIndices();
1708 test_GetKerningPairs();
1709 test_GetOutlineTextMetrics();
1710 test_SetTextJustification();
1711 test_font_charset();
1712 test_GetFontUnicodeRanges();
1713 test_nonexistent_font();
1715 /* On Windows Arial has a lot of default charset aliases such as Arial Cyr,
1716 * I'd like to avoid them in this test.
1718 test_EnumFontFamilies("Arial Black", ANSI_CHARSET);
1719 test_EnumFontFamilies("Symbol", SYMBOL_CHARSET);
1720 if (is_truetype_font_installed("Arial Black") &&
1721 (is_truetype_font_installed("Symbol") || is_truetype_font_installed("Wingdings")))
1723 test_EnumFontFamilies("", ANSI_CHARSET);
1724 test_EnumFontFamilies("", SYMBOL_CHARSET);
1725 test_EnumFontFamilies("", DEFAULT_CHARSET);
1727 else
1728 skip("Arial Black or Symbol/Wingdings is not installed\n");
1729 test_GetTextMetrics();