gdi32: Add more font substitution tests, make them pass under Wine.
[wine/dcerpc.git] / dlls / gdi32 / tests / font.c
blobd4abc8cbcd18cdde8afbb4f9ff89ba8a48c82ded
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 *pGetCharABCWidthsI)(HDC hdc, UINT first, UINT count, LPWORD glyphs, LPABC abc);
37 BOOL (WINAPI *pGetCharABCWidthsW)(HDC hdc, UINT first, UINT last, LPABC abc);
38 DWORD (WINAPI *pGetFontUnicodeRanges)(HDC hdc, LPGLYPHSET lpgs);
39 DWORD (WINAPI *pGetGlyphIndicesA)(HDC hdc, LPCSTR lpstr, INT count, LPWORD pgi, DWORD flags);
40 DWORD (WINAPI *pGetGlyphIndicesW)(HDC hdc, LPCWSTR lpstr, INT count, LPWORD pgi, DWORD flags);
41 BOOL (WINAPI *pGdiRealizationInfo)(HDC hdc, DWORD *);
43 static HMODULE hgdi32 = 0;
45 static void init(void)
47 hgdi32 = GetModuleHandleA("gdi32.dll");
49 pGdiGetCharDimensions = (void *)GetProcAddress(hgdi32, "GdiGetCharDimensions");
50 pGetCharABCWidthsI = (void *)GetProcAddress(hgdi32, "GetCharABCWidthsI");
51 pGetCharABCWidthsW = (void *)GetProcAddress(hgdi32, "GetCharABCWidthsW");
52 pGetFontUnicodeRanges = (void *)GetProcAddress(hgdi32, "GetFontUnicodeRanges");
53 pGetGlyphIndicesA = (void *)GetProcAddress(hgdi32, "GetGlyphIndicesA");
54 pGetGlyphIndicesW = (void *)GetProcAddress(hgdi32, "GetGlyphIndicesW");
55 pGdiRealizationInfo = (void *)GetProcAddress(hgdi32, "GdiRealizationInfo");
58 static INT CALLBACK is_truetype_font_installed_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
60 if (type != TRUETYPE_FONTTYPE) return 1;
62 return 0;
65 static BOOL is_truetype_font_installed(const char *name)
67 HDC hdc = GetDC(0);
68 BOOL ret = FALSE;
70 if (!EnumFontFamiliesA(hdc, name, is_truetype_font_installed_proc, 0))
71 ret = TRUE;
73 ReleaseDC(0, hdc);
74 return ret;
77 static INT CALLBACK is_font_installed_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
79 return 0;
82 static BOOL is_font_installed(const char *name)
84 HDC hdc = GetDC(0);
85 BOOL ret = FALSE;
87 if(!EnumFontFamiliesA(hdc, name, is_font_installed_proc, 0))
88 ret = TRUE;
90 ReleaseDC(0, hdc);
91 return ret;
94 static void check_font(const char* test, const LOGFONTA* lf, HFONT hfont)
96 LOGFONTA getobj_lf;
97 int ret, minlen = 0;
99 if (!hfont)
100 return;
102 ret = GetObject(hfont, sizeof(getobj_lf), &getobj_lf);
103 /* NT4 tries to be clever and only returns the minimum length */
104 while (lf->lfFaceName[minlen] && minlen < LF_FACESIZE-1)
105 minlen++;
106 minlen += FIELD_OFFSET(LOGFONTA, lfFaceName) + 1;
107 ok(ret == sizeof(LOGFONTA) || ret == minlen, "%s: GetObject returned %d\n", test, ret);
108 ok(!memcmp(&lf, &lf, FIELD_OFFSET(LOGFONTA, lfFaceName)), "%s: fonts don't match\n", test);
109 ok(!lstrcmpA(lf->lfFaceName, getobj_lf.lfFaceName),
110 "%s: font names don't match: %s != %s\n", test, lf->lfFaceName, getobj_lf.lfFaceName);
113 static HFONT create_font(const char* test, const LOGFONTA* lf)
115 HFONT hfont = CreateFontIndirectA(lf);
116 ok(hfont != 0, "%s: CreateFontIndirect failed\n", test);
117 if (hfont)
118 check_font(test, lf, hfont);
119 return hfont;
122 static void test_logfont(void)
124 LOGFONTA lf;
125 HFONT hfont;
127 memset(&lf, 0, sizeof lf);
129 lf.lfCharSet = ANSI_CHARSET;
130 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
131 lf.lfWeight = FW_DONTCARE;
132 lf.lfHeight = 16;
133 lf.lfWidth = 16;
134 lf.lfQuality = DEFAULT_QUALITY;
136 lstrcpyA(lf.lfFaceName, "Arial");
137 hfont = create_font("Arial", &lf);
138 DeleteObject(hfont);
140 memset(&lf, 'A', sizeof(lf));
141 hfont = CreateFontIndirectA(&lf);
142 ok(hfont != 0, "CreateFontIndirectA with strange LOGFONT failed\n");
144 lf.lfFaceName[LF_FACESIZE - 1] = 0;
145 check_font("AAA...", &lf, hfont);
146 DeleteObject(hfont);
149 static INT CALLBACK font_enum_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
151 if (type & RASTER_FONTTYPE)
153 LOGFONT *lf = (LOGFONT *)lParam;
154 *lf = *elf;
155 return 0; /* stop enumeration */
158 return 1; /* continue enumeration */
161 static void test_font_metrics(HDC hdc, HFONT hfont, LONG lfHeight,
162 LONG lfWidth, const char *test_str,
163 INT test_str_len, const TEXTMETRICA *tm_orig,
164 const SIZE *size_orig, INT width_of_A_orig,
165 INT scale_x, INT scale_y)
167 HFONT old_hfont;
168 LOGFONTA lf;
169 TEXTMETRICA tm;
170 SIZE size;
171 INT width_of_A, cx, cy;
173 if (!hfont)
174 return;
176 GetObjectA(hfont, sizeof(lf), &lf);
178 old_hfont = SelectObject(hdc, hfont);
180 GetTextMetricsA(hdc, &tm);
182 cx = tm.tmAveCharWidth / tm_orig->tmAveCharWidth;
183 cy = tm.tmHeight / tm_orig->tmHeight;
184 ok(cx == scale_x && cy == scale_y, "expected scale_x %d, scale_y %d, got cx %d, cy %d\n",
185 scale_x, scale_y, cx, cy);
186 ok(tm.tmHeight == tm_orig->tmHeight * scale_y, "%d != %d\n", tm.tmHeight, tm_orig->tmHeight * scale_y);
187 ok(tm.tmAscent == tm_orig->tmAscent * scale_y, "%d != %d\n", tm.tmAscent, tm_orig->tmAscent * scale_y);
188 ok(tm.tmDescent == tm_orig->tmDescent * scale_y, "%d != %d\n", tm.tmDescent, tm_orig->tmDescent * scale_y);
189 ok(tm.tmAveCharWidth == tm_orig->tmAveCharWidth * scale_x, "%d != %d\n", tm.tmAveCharWidth, tm_orig->tmAveCharWidth * scale_x);
190 ok(tm.tmMaxCharWidth == tm_orig->tmMaxCharWidth * scale_x, "%d != %d\n", tm.tmAveCharWidth, tm_orig->tmMaxCharWidth * scale_x);
192 ok(lf.lfHeight == lfHeight, "lf %d != %d\n", lf.lfHeight, lfHeight);
193 if (lf.lfHeight)
195 if (lf.lfWidth)
196 ok(lf.lfWidth == tm.tmAveCharWidth, "lf %d != tm %d\n", lf.lfWidth, tm.tmAveCharWidth);
198 else
199 ok(lf.lfWidth == lfWidth, "lf %d != %d\n", lf.lfWidth, lfWidth);
201 GetTextExtentPoint32A(hdc, test_str, test_str_len, &size);
203 ok(size.cx == size_orig->cx * scale_x, "%d != %d\n", size.cx, size_orig->cx * scale_x);
204 ok(size.cy == size_orig->cy * scale_y, "%d != %d\n", size.cy, size_orig->cy * scale_y);
206 GetCharWidthA(hdc, 'A', 'A', &width_of_A);
208 ok(width_of_A == width_of_A_orig * scale_x, "%d != %d\n", width_of_A, width_of_A_orig * scale_x);
210 SelectObject(hdc, old_hfont);
213 /* Test how GDI scales bitmap font metrics */
214 static void test_bitmap_font(void)
216 static const char test_str[11] = "Test String";
217 HDC hdc;
218 LOGFONTA bitmap_lf;
219 HFONT hfont, old_hfont;
220 TEXTMETRICA tm_orig;
221 SIZE size_orig;
222 INT ret, i, width_orig, height_orig, scale, lfWidth;
224 hdc = GetDC(0);
226 /* "System" has only 1 pixel size defined, otherwise the test breaks */
227 ret = EnumFontFamiliesA(hdc, "System", font_enum_proc, (LPARAM)&bitmap_lf);
228 if (ret)
230 ReleaseDC(0, hdc);
231 trace("no bitmap fonts were found, skipping the test\n");
232 return;
235 trace("found bitmap font %s, height %d\n", bitmap_lf.lfFaceName, bitmap_lf.lfHeight);
237 height_orig = bitmap_lf.lfHeight;
238 lfWidth = bitmap_lf.lfWidth;
240 hfont = create_font("bitmap", &bitmap_lf);
241 old_hfont = SelectObject(hdc, hfont);
242 ok(GetTextMetricsA(hdc, &tm_orig), "GetTextMetricsA failed\n");
243 ok(GetTextExtentPoint32A(hdc, test_str, sizeof(test_str), &size_orig), "GetTextExtentPoint32A failed\n");
244 ok(GetCharWidthA(hdc, 'A', 'A', &width_orig), "GetCharWidthA failed\n");
245 SelectObject(hdc, old_hfont);
246 DeleteObject(hfont);
248 bitmap_lf.lfHeight = 0;
249 bitmap_lf.lfWidth = 4;
250 hfont = create_font("bitmap", &bitmap_lf);
251 test_font_metrics(hdc, hfont, 0, 4, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 1, 1);
252 DeleteObject(hfont);
254 bitmap_lf.lfHeight = height_orig;
255 bitmap_lf.lfWidth = lfWidth;
257 /* test fractional scaling */
258 for (i = 1; i <= height_orig * 3; i++)
260 INT nearest_height;
262 bitmap_lf.lfHeight = i;
263 hfont = create_font("fractional", &bitmap_lf);
264 scale = (i + height_orig - 1) / height_orig;
265 nearest_height = scale * height_orig;
266 /* XP allows not more than 10% deviation */
267 if (scale > 1 && nearest_height - i > nearest_height / 10) scale--;
268 test_font_metrics(hdc, hfont, bitmap_lf.lfHeight, 0, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 1, scale);
269 DeleteObject(hfont);
272 /* test integer scaling 3x2 */
273 bitmap_lf.lfHeight = height_orig * 2;
274 bitmap_lf.lfWidth *= 3;
275 hfont = create_font("3x2", &bitmap_lf);
276 test_font_metrics(hdc, hfont, bitmap_lf.lfHeight, 0, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 3, 2);
277 DeleteObject(hfont);
279 /* test integer scaling 3x3 */
280 bitmap_lf.lfHeight = height_orig * 3;
281 bitmap_lf.lfWidth = 0;
282 hfont = create_font("3x3", &bitmap_lf);
283 test_font_metrics(hdc, hfont, bitmap_lf.lfHeight, 0, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 3, 3);
284 DeleteObject(hfont);
286 ReleaseDC(0, hdc);
289 static INT CALLBACK find_font_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
291 LOGFONT *lf = (LOGFONT *)lParam;
293 if (elf->lfHeight == lf->lfHeight && !strcmp(elf->lfFaceName, lf->lfFaceName))
295 *lf = *elf;
296 return 0; /* stop enumeration */
298 return 1; /* continue enumeration */
301 static void test_bitmap_font_metrics(void)
303 static const struct font_data
305 const char face_name[LF_FACESIZE];
306 int weight, height, ascent, descent, int_leading, ext_leading;
307 int ave_char_width, max_char_width;
308 DWORD ansi_bitfield;
309 } fd[] =
311 { "MS Sans Serif", FW_NORMAL, 13, 11, 2, 2, 0, 5, 11, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
312 { "MS Sans Serif", FW_NORMAL, 16, 13, 3, 3, 0, 7, 14, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
313 { "MS Sans Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 16, FS_LATIN1 | FS_CYRILLIC },
314 { "MS Sans Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 18, FS_LATIN2 },
315 { "MS Sans Serif", FW_NORMAL, 24, 19, 5, 6, 0, 9, 19, FS_LATIN1 },
316 { "MS Sans Serif", FW_NORMAL, 24, 19, 5, 6, 0, 9, 24, FS_LATIN2 },
317 { "MS Sans Serif", FW_NORMAL, 24, 19, 5, 6, 0, 9, 20, FS_CYRILLIC },
318 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 5, 0, 12, 24, FS_LATIN1 },
319 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 6, 0, 12, 24, FS_LATIN2 },
320 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 5, 0, 12, 25, FS_CYRILLIC },
321 { "MS Sans Serif", FW_NORMAL, 37, 29, 8, 5, 0, 16, 32, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
322 { "MS Serif", FW_NORMAL, 10, 8, 2, 2, 0, 4, 8, FS_LATIN1 | FS_LATIN2 },
323 { "MS Serif", FW_NORMAL, 10, 8, 2, 2, 0, 5, 8, FS_CYRILLIC },
324 { "MS Serif", FW_NORMAL, 11, 9, 2, 2, 0, 5, 9, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
325 { "MS Serif", FW_NORMAL, 13, 11, 2, 2, 0, 5, 11, FS_LATIN1 },
326 { "MS Serif", FW_NORMAL, 13, 11, 2, 2, 0, 5, 12, FS_LATIN2 | FS_CYRILLIC },
327 { "MS Serif", FW_NORMAL, 16, 13, 3, 3, 0, 6, 14, FS_LATIN1 | FS_LATIN2 },
328 { "MS Serif", FW_NORMAL, 16, 13, 3, 3, 0, 6, 16, FS_CYRILLIC },
329 { "MS Serif", FW_NORMAL, 19, 15, 4, 3, 0, 8, 18, FS_LATIN1 | FS_LATIN2 },
330 { "MS Serif", FW_NORMAL, 19, 15, 4, 3, 0, 8, 19, FS_CYRILLIC },
331 { "MS Serif", FW_NORMAL, 21, 16, 5, 3, 0, 9, 17, FS_LATIN1 },
332 { "MS Serif", FW_NORMAL, 21, 16, 5, 3, 0, 9, 22, FS_LATIN2 },
333 { "MS Serif", FW_NORMAL, 21, 16, 5, 3, 0, 9, 23, FS_CYRILLIC },
334 { "MS Serif", FW_NORMAL, 27, 21, 6, 3, 0, 12, 23, FS_LATIN1 },
335 { "MS Serif", FW_NORMAL, 27, 21, 6, 3, 0, 12, 26, FS_LATIN2 },
336 { "MS Serif", FW_NORMAL, 27, 21, 6, 3, 0, 12, 27, FS_CYRILLIC },
337 { "MS Serif", FW_NORMAL, 35, 27, 8, 3, 0, 16, 33, FS_LATIN1 | FS_LATIN2 },
338 { "MS Serif", FW_NORMAL, 35, 27, 8, 3, 0, 16, 34, FS_CYRILLIC },
339 { "Courier", FW_NORMAL, 13, 11, 2, 0, 0, 8, 8, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
340 { "Courier", FW_NORMAL, 16, 13, 3, 0, 0, 9, 9, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
341 { "Courier", FW_NORMAL, 20, 16, 4, 0, 0, 12, 12, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
342 { "System", FW_BOLD, 16, 13, 3, 3, 0, 7, 14, FS_LATIN1 },
343 { "System", FW_BOLD, 16, 13, 3, 3, 0, 7, 15, FS_LATIN2 | FS_CYRILLIC },
345 * TODO: the system for CP932 should be NORMAL, not BOLD. However that would
346 * require a new system.sfd for that font
348 { "System", FW_BOLD, 18, 16, 2, 0, 2, 8, 16, FS_JISJAPAN },
349 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 1, 2, FS_LATIN1 },
350 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 1, 8, FS_LATIN2 | FS_CYRILLIC },
351 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 2, 4, FS_JISJAPAN },
352 { "Small Fonts", FW_NORMAL, 5, 4, 1, 1, 0, 3, 4, FS_LATIN1 },
353 { "Small Fonts", FW_NORMAL, 5, 4, 1, 1, 0, 2, 8, FS_LATIN2 | FS_CYRILLIC },
354 { "Small Fonts", FW_NORMAL, 5, 4, 1, 0, 0, 3, 6, FS_JISJAPAN },
355 { "Small Fonts", FW_NORMAL, 6, 5, 1, 1, 0, 3, 13, FS_LATIN1 },
356 { "Small Fonts", FW_NORMAL, 6, 5, 1, 1, 0, 3, 8, FS_LATIN2 | FS_CYRILLIC },
357 { "Small Fonts", FW_NORMAL, 6, 5, 1, 0, 0, 4, 8, FS_JISJAPAN },
358 { "Small Fonts", FW_NORMAL, 8, 7, 1, 1, 0, 4, 7, FS_LATIN1 },
359 { "Small Fonts", FW_NORMAL, 8, 7, 1, 1, 0, 4, 8, FS_LATIN2 | FS_CYRILLIC },
360 { "Small Fonts", FW_NORMAL, 8, 7, 1, 0, 0, 5, 10, FS_JISJAPAN },
361 { "Small Fonts", FW_NORMAL, 10, 8, 2, 2, 0, 4, 8, FS_LATIN1 | FS_LATIN2 },
362 { "Small Fonts", FW_NORMAL, 10, 8, 2, 2, 0, 5, 8, FS_CYRILLIC },
363 { "Small Fonts", FW_NORMAL, 10, 8, 2, 0, 0, 6, 12, FS_JISJAPAN },
364 { "Small Fonts", FW_NORMAL, 11, 9, 2, 2, 0, 5, 9, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
365 { "Small Fonts", FW_NORMAL, 11, 9, 2, 0, 0, 7, 14, FS_JISJAPAN },
366 { "Fixedsys", FW_NORMAL, 15, 12, 3, 3, 0, 8, 8, FS_LATIN1 | FS_LATIN2 },
367 { "Fixedsys", FW_NORMAL, 16, 12, 4, 3, 0, 8, 8, FS_CYRILLIC },
368 { "FixedSys", FW_NORMAL, 18, 16, 2, 0, 0, 8, 16, FS_JISJAPAN }
370 /* FIXME: add "Terminal" */
372 HDC hdc;
373 LOGFONT lf;
374 HFONT hfont, old_hfont;
375 TEXTMETRIC tm;
376 INT ret, i;
378 hdc = CreateCompatibleDC(0);
379 assert(hdc);
381 for (i = 0; i < sizeof(fd)/sizeof(fd[0]); i++)
383 int bit;
385 memset(&lf, 0, sizeof(lf));
387 lf.lfHeight = fd[i].height;
388 strcpy(lf.lfFaceName, fd[i].face_name);
390 for(bit = 0; bit < 32; bit++)
392 DWORD fs[2];
393 CHARSETINFO csi;
395 fs[0] = 1L << bit;
396 fs[1] = 0;
397 if((fd[i].ansi_bitfield & fs[0]) == 0) continue;
398 if(!TranslateCharsetInfo( fs, &csi, TCI_SRCFONTSIG )) continue;
400 lf.lfCharSet = csi.ciCharset;
401 ret = EnumFontFamiliesEx(hdc, &lf, find_font_proc, (LPARAM)&lf, 0);
402 if (ret) continue;
404 trace("found font %s, height %d charset %x\n", lf.lfFaceName, lf.lfHeight, lf.lfCharSet);
406 hfont = create_font(lf.lfFaceName, &lf);
407 old_hfont = SelectObject(hdc, hfont);
408 ok(GetTextMetrics(hdc, &tm), "GetTextMetrics error %d\n", GetLastError());
410 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);
411 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);
412 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);
413 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);
414 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);
415 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);
416 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);
418 /* Don't run the max char width test on System/ANSI_CHARSET. We have extra characters in our font
419 that make the max width bigger */
420 if(strcmp(lf.lfFaceName, "System") || lf.lfCharSet != ANSI_CHARSET)
421 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);
423 SelectObject(hdc, old_hfont);
424 DeleteObject(hfont);
428 DeleteDC(hdc);
431 static void test_GdiGetCharDimensions(void)
433 HDC hdc;
434 TEXTMETRICW tm;
435 LONG ret;
436 SIZE size;
437 LONG avgwidth, height;
438 static const char szAlphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
440 if (!pGdiGetCharDimensions)
442 skip("GdiGetCharDimensions not available on this platform\n");
443 return;
446 hdc = CreateCompatibleDC(NULL);
448 GetTextExtentPoint(hdc, szAlphabet, strlen(szAlphabet), &size);
449 avgwidth = ((size.cx / 26) + 1) / 2;
451 ret = pGdiGetCharDimensions(hdc, &tm, &height);
452 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
453 ok(height == tm.tmHeight, "GdiGetCharDimensions should have set height to %d instead of %d\n", tm.tmHeight, height);
455 ret = pGdiGetCharDimensions(hdc, &tm, NULL);
456 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
458 ret = pGdiGetCharDimensions(hdc, NULL, NULL);
459 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
461 height = 0;
462 ret = pGdiGetCharDimensions(hdc, NULL, &height);
463 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
464 ok(height == size.cy, "GdiGetCharDimensions should have set height to %d instead of %d\n", size.cy, height);
466 DeleteDC(hdc);
469 static void test_GetCharABCWidths(void)
471 static const WCHAR str[] = {'a',0};
472 BOOL ret;
473 HDC hdc;
474 LOGFONTA lf;
475 HFONT hfont;
476 ABC abc[1];
477 WORD glyphs[1];
478 DWORD nb;
480 if (!pGetCharABCWidthsW || !pGetCharABCWidthsI)
482 skip("GetCharABCWidthsW/I not available on this platform\n");
483 return;
486 memset(&lf, 0, sizeof(lf));
487 strcpy(lf.lfFaceName, "System");
488 lf.lfHeight = 20;
490 hfont = CreateFontIndirectA(&lf);
491 hdc = GetDC(0);
492 hfont = SelectObject(hdc, hfont);
494 nb = pGetGlyphIndicesW(hdc, str, 1, glyphs, 0);
495 ok(nb == 1, "GetGlyphIndicesW should have returned 1\n");
497 ret = pGetCharABCWidthsI(NULL, 0, 1, glyphs, abc);
498 ok(!ret, "GetCharABCWidthsI should have failed\n");
500 ret = pGetCharABCWidthsI(hdc, 0, 1, glyphs, NULL);
501 ok(!ret, "GetCharABCWidthsI should have failed\n");
503 ret = pGetCharABCWidthsI(hdc, 0, 1, glyphs, abc);
504 ok(ret, "GetCharABCWidthsI should have succeeded\n");
506 ret = pGetCharABCWidthsW(NULL, 'a', 'a', abc);
507 ok(!ret, "GetCharABCWidthsW should have failed\n");
509 ret = pGetCharABCWidthsW(hdc, 'a', 'a', NULL);
510 ok(!ret, "GetCharABCWidthsW should have failed\n");
512 ret = pGetCharABCWidthsW(hdc, 'a', 'a', abc);
513 ok(!ret, "GetCharABCWidthsW should have failed\n");
515 hfont = SelectObject(hdc, hfont);
516 DeleteObject(hfont);
517 ReleaseDC(NULL, hdc);
520 static void test_text_extents(void)
522 static const WCHAR wt[] = {'O','n','e','\n','t','w','o',' ','3',0};
523 LPINT extents;
524 INT i, len, fit1, fit2;
525 LOGFONTA lf;
526 TEXTMETRICA tm;
527 HDC hdc;
528 HFONT hfont;
529 SIZE sz;
530 SIZE sz1, sz2;
532 memset(&lf, 0, sizeof(lf));
533 strcpy(lf.lfFaceName, "Arial");
534 lf.lfHeight = 20;
536 hfont = CreateFontIndirectA(&lf);
537 hdc = GetDC(0);
538 hfont = SelectObject(hdc, hfont);
539 GetTextMetricsA(hdc, &tm);
540 GetTextExtentPointA(hdc, "o", 1, &sz);
541 ok(sz.cy == tm.tmHeight, "cy %d tmHeight %d\n", sz.cy, tm.tmHeight);
543 SetLastError(0xdeadbeef);
544 GetTextExtentExPointW(hdc, wt, 1, 1, &fit1, &fit2, &sz1);
545 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
547 skip("Skipping remainder of text extents test on a Win9x platform\n");
548 hfont = SelectObject(hdc, hfont);
549 DeleteObject(hfont);
550 ReleaseDC(0, hdc);
551 return;
554 len = lstrlenW(wt);
555 extents = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof extents[0]);
556 extents[0] = 1; /* So that the increasing sequence test will fail
557 if the extents array is untouched. */
558 GetTextExtentExPointW(hdc, wt, len, 32767, &fit1, extents, &sz1);
559 GetTextExtentPointW(hdc, wt, len, &sz2);
560 ok(sz1.cy == sz2.cy,
561 "cy from GetTextExtentExPointW (%d) and GetTextExtentPointW (%d) differ\n", sz1.cy, sz2.cy);
562 /* Because of the '\n' in the string GetTextExtentExPoint and
563 GetTextExtentPoint return different widths under Win2k, but
564 under WinXP they return the same width. So we don't test that
565 here. */
567 for (i = 1; i < len; ++i)
568 ok(extents[i-1] <= extents[i],
569 "GetTextExtentExPointW generated a non-increasing sequence of partial extents (at position %d)\n",
571 ok(extents[len-1] == sz1.cx, "GetTextExtentExPointW extents and size don't match\n");
572 ok(0 <= fit1 && fit1 <= len, "GetTextExtentExPointW generated illegal value %d for fit\n", fit1);
573 ok(0 < fit1, "GetTextExtentExPointW says we can't even fit one letter in 32767 logical units\n");
574 GetTextExtentExPointW(hdc, wt, len, extents[2], &fit2, NULL, &sz2);
575 ok(sz1.cx == sz2.cx && sz1.cy == sz2.cy, "GetTextExtentExPointW returned different sizes for the same string\n");
576 ok(fit2 == 3, "GetTextExtentExPointW extents isn't consistent with fit\n");
577 GetTextExtentExPointW(hdc, wt, len, extents[2]-1, &fit2, NULL, &sz2);
578 ok(fit2 == 2, "GetTextExtentExPointW extents isn't consistent with fit\n");
579 GetTextExtentExPointW(hdc, wt, 2, 0, NULL, extents + 2, &sz2);
580 ok(extents[0] == extents[2] && extents[1] == extents[3],
581 "GetTextExtentExPointW with lpnFit == NULL returns incorrect results\n");
582 GetTextExtentExPointW(hdc, wt, 2, 0, NULL, NULL, &sz1);
583 ok(sz1.cx == sz2.cx && sz1.cy == sz2.cy,
584 "GetTextExtentExPointW with lpnFit and alpDx both NULL returns incorrect results\n");
585 HeapFree(GetProcessHeap(), 0, extents);
587 hfont = SelectObject(hdc, hfont);
588 DeleteObject(hfont);
589 ReleaseDC(NULL, hdc);
592 static void test_GetGlyphIndices(void)
594 HDC hdc;
595 HFONT hfont;
596 DWORD charcount;
597 LOGFONTA lf;
598 DWORD flags = 0;
599 WCHAR testtext[] = {'T','e','s','t',0xffff,0};
600 WORD glyphs[(sizeof(testtext)/2)-1];
601 TEXTMETRIC textm;
602 HFONT hOldFont;
604 if (!pGetGlyphIndicesW) {
605 skip("GetGlyphIndicesW not available on platform\n");
606 return;
609 hdc = GetDC(0);
611 ok(GetTextMetrics(hdc, &textm), "GetTextMetric failed\n");
612 flags |= GGI_MARK_NONEXISTING_GLYPHS;
613 charcount = pGetGlyphIndicesW(hdc, testtext, (sizeof(testtext)/2)-1, glyphs, flags);
614 ok(charcount == 5, "GetGlyphIndicesW count of glyphs should = 5 not %d\n", charcount);
615 ok((glyphs[4] == 0x001f || glyphs[4] == 0xffff /* Vista */), "GetGlyphIndicesW should have returned a nonexistent char not %04x\n", glyphs[4]);
616 flags = 0;
617 charcount = pGetGlyphIndicesW(hdc, testtext, (sizeof(testtext)/2)-1, glyphs, flags);
618 ok(charcount == 5, "GetGlyphIndicesW count of glyphs should = 5 not %d\n", charcount);
619 ok(glyphs[4] == textm.tmDefaultChar, "GetGlyphIndicesW should have returned a %04x not %04x\n",
620 textm.tmDefaultChar, glyphs[4]);
622 if(!is_font_installed("Tahoma"))
624 skip("Tahoma is not installed so skipping this test\n");
625 return;
627 memset(&lf, 0, sizeof(lf));
628 strcpy(lf.lfFaceName, "Tahoma");
629 lf.lfHeight = 20;
631 hfont = CreateFontIndirectA(&lf);
632 hOldFont = SelectObject(hdc, hfont);
633 ok(GetTextMetrics(hdc, &textm), "GetTextMetric failed\n");
634 flags |= GGI_MARK_NONEXISTING_GLYPHS;
635 charcount = pGetGlyphIndicesW(hdc, testtext, (sizeof(testtext)/2)-1, glyphs, flags);
636 ok(charcount == 5, "GetGlyphIndicesW count of glyphs should = 5 not %d\n", charcount);
637 ok(glyphs[4] == 0xffff, "GetGlyphIndicesW should have returned 0xffff char not %04x\n", glyphs[4]);
638 flags = 0;
639 testtext[0] = textm.tmDefaultChar;
640 charcount = pGetGlyphIndicesW(hdc, testtext, (sizeof(testtext)/2)-1, glyphs, flags);
641 ok(charcount == 5, "GetGlyphIndicesW count of glyphs should = 5 not %d\n", charcount);
642 todo_wine ok(glyphs[0] == 0, "GetGlyphIndicesW for tmDefaultChar should be 0 not %04x\n", glyphs[0]);
643 ok(glyphs[4] == 0, "GetGlyphIndicesW should have returned 0 not %04x\n", glyphs[4]);
644 DeleteObject(SelectObject(hdc, hOldFont));
647 static void test_GetKerningPairs(void)
649 static const struct kerning_data
651 const char face_name[LF_FACESIZE];
652 LONG height;
653 /* some interesting fields from OUTLINETEXTMETRIC */
654 LONG tmHeight, tmAscent, tmDescent;
655 UINT otmEMSquare;
656 INT otmAscent;
657 INT otmDescent;
658 UINT otmLineGap;
659 UINT otmsCapEmHeight;
660 UINT otmsXHeight;
661 INT otmMacAscent;
662 INT otmMacDescent;
663 UINT otmMacLineGap;
664 UINT otmusMinimumPPEM;
665 /* small subset of kerning pairs to test */
666 DWORD total_kern_pairs;
667 const KERNINGPAIR kern_pair[26];
668 } kd[] =
670 {"Arial", 12, 12, 9, 3,
671 2048, 7, -2, 1, 5, 2, 8, -2, 0, 9,
674 {' ','A',-1},{' ','T',0},{' ','Y',0},{'1','1',-1},
675 {'A',' ',-1},{'A','T',-1},{'A','V',-1},{'A','W',0},
676 {'A','Y',-1},{'A','v',0},{'A','w',0},{'A','y',0},
677 {'F',',',-1},{'F','.',-1},{'F','A',-1},{'L',' ',0},
678 {'L','T',-1},{'L','V',-1},{'L','W',-1},{'L','Y',-1},
679 {915,912,+1},{915,913,-1},{910,912,+1},{910,913,-1},
680 {933,970,+1},{933,972,-1}
683 {"Arial", -34, 39, 32, 7,
684 2048, 25, -7, 5, 17, 9, 31, -7, 1, 9,
687 {' ','A',-2},{' ','T',-1},{' ','Y',-1},{'1','1',-3},
688 {'A',' ',-2},{'A','T',-3},{'A','V',-3},{'A','W',-1},
689 {'A','Y',-3},{'A','v',-1},{'A','w',-1},{'A','y',-1},
690 {'F',',',-4},{'F','.',-4},{'F','A',-2},{'L',' ',-1},
691 {'L','T',-3},{'L','V',-3},{'L','W',-3},{'L','Y',-3},
692 {915,912,+3},{915,913,-3},{910,912,+3},{910,913,-3},
693 {933,970,+2},{933,972,-3}
696 { "Arial", 120, 120, 97, 23,
697 2048, 79, -23, 16, 54, 27, 98, -23, 4, 9,
700 {' ','A',-6},{' ','T',-2},{' ','Y',-2},{'1','1',-8},
701 {'A',' ',-6},{'A','T',-8},{'A','V',-8},{'A','W',-4},
702 {'A','Y',-8},{'A','v',-2},{'A','w',-2},{'A','y',-2},
703 {'F',',',-12},{'F','.',-12},{'F','A',-6},{'L',' ',-4},
704 {'L','T',-8},{'L','V',-8},{'L','W',-8},{'L','Y',-8},
705 {915,912,+9},{915,913,-10},{910,912,+9},{910,913,-8},
706 {933,970,+6},{933,972,-10}
709 #if 0 /* this set fails due to +1/-1 errors (rounding bug?), needs investigation. */
710 { "Arial", 1024 /* usually 1/2 of EM Square */, 1024, 830, 194,
711 2048, 668, -193, 137, 459, 229, 830, -194, 30, 9,
714 {' ','A',-51},{' ','T',-17},{' ','Y',-17},{'1','1',-68},
715 {'A',' ',-51},{'A','T',-68},{'A','V',-68},{'A','W',-34},
716 {'A','Y',-68},{'A','v',-17},{'A','w',-17},{'A','y',-17},
717 {'F',',',-102},{'F','.',-102},{'F','A',-51},{'L',' ',-34},
718 {'L','T',-68},{'L','V',-68},{'L','W',-68},{'L','Y',-68},
719 {915,912,+73},{915,913,-84},{910,912,+76},{910,913,-68},
720 {933,970,+54},{933,972,-83}
723 #endif
725 LOGFONT lf;
726 HFONT hfont, hfont_old;
727 KERNINGPAIR *kern_pair;
728 HDC hdc;
729 DWORD total_kern_pairs, ret, i, n, matches;
731 hdc = GetDC(0);
733 /* GetKerningPairsA maps unicode set of kerning pairs to current code page
734 * which may render this test unusable, so we're trying to avoid that.
736 SetLastError(0xdeadbeef);
737 GetKerningPairsW(hdc, 0, NULL);
738 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
740 skip("Skipping the GetKerningPairs test on a Win9x platform\n");
741 ReleaseDC(0, hdc);
742 return;
745 for (i = 0; i < sizeof(kd)/sizeof(kd[0]); i++)
747 OUTLINETEXTMETRICW otm;
749 if (!is_font_installed(kd[i].face_name))
751 trace("%s is not installed so skipping this test\n", kd[i].face_name);
752 continue;
755 trace("testing font %s, height %d\n", kd[i].face_name, kd[i].height);
757 memset(&lf, 0, sizeof(lf));
758 strcpy(lf.lfFaceName, kd[i].face_name);
759 lf.lfHeight = kd[i].height;
760 hfont = CreateFontIndirect(&lf);
761 assert(hfont != 0);
763 hfont_old = SelectObject(hdc, hfont);
765 SetLastError(0xdeadbeef);
766 otm.otmSize = sizeof(otm); /* just in case for Win9x compatibility */
767 ok(GetOutlineTextMetricsW(hdc, sizeof(otm), &otm) == sizeof(otm), "GetOutlineTextMetricsW error %d\n", GetLastError());
769 ok(kd[i].tmHeight == otm.otmTextMetrics.tmHeight, "expected %d, got %d\n",
770 kd[i].tmHeight, otm.otmTextMetrics.tmHeight);
771 ok(kd[i].tmAscent == otm.otmTextMetrics.tmAscent, "expected %d, got %d\n",
772 kd[i].tmAscent, otm.otmTextMetrics.tmAscent);
773 ok(kd[i].tmDescent == otm.otmTextMetrics.tmDescent, "expected %d, got %d\n",
774 kd[i].tmDescent, otm.otmTextMetrics.tmDescent);
776 ok(kd[i].otmEMSquare == otm.otmEMSquare, "expected %u, got %u\n",
777 kd[i].otmEMSquare, otm.otmEMSquare);
778 ok(kd[i].otmAscent == otm.otmAscent, "expected %d, got %d\n",
779 kd[i].otmAscent, otm.otmAscent);
780 ok(kd[i].otmDescent == otm.otmDescent, "expected %d, got %d\n",
781 kd[i].otmDescent, otm.otmDescent);
782 ok(kd[i].otmLineGap == otm.otmLineGap, "expected %u, got %u\n",
783 kd[i].otmLineGap, otm.otmLineGap);
784 todo_wine {
785 ok(kd[i].otmsCapEmHeight == otm.otmsCapEmHeight, "expected %u, got %u\n",
786 kd[i].otmsCapEmHeight, otm.otmsCapEmHeight);
787 ok(kd[i].otmsXHeight == otm.otmsXHeight, "expected %u, got %u\n",
788 kd[i].otmsXHeight, otm.otmsXHeight);
789 ok(kd[i].otmMacAscent == otm.otmMacAscent, "expected %d, got %d\n",
790 kd[i].otmMacAscent, otm.otmMacAscent);
791 ok(kd[i].otmMacDescent == otm.otmMacDescent, "expected %d, got %d\n",
792 kd[i].otmMacDescent, otm.otmMacDescent);
793 /* FIXME: this one sometimes succeeds due to expected 0, enable it when removing todo */
794 if (0) ok(kd[i].otmMacLineGap == otm.otmMacLineGap, "expected %u, got %u\n",
795 kd[i].otmMacLineGap, otm.otmMacLineGap);
796 ok(kd[i].otmusMinimumPPEM == otm.otmusMinimumPPEM, "expected %u, got %u\n",
797 kd[i].otmusMinimumPPEM, otm.otmusMinimumPPEM);
800 total_kern_pairs = GetKerningPairsW(hdc, 0, NULL);
801 trace("total_kern_pairs %u\n", total_kern_pairs);
802 kern_pair = HeapAlloc(GetProcessHeap(), 0, total_kern_pairs * sizeof(*kern_pair));
804 #if 0 /* Win98 (GetKerningPairsA) and XP behave differently here, the test passes on XP */
805 SetLastError(0xdeadbeef);
806 ret = GetKerningPairsW(hdc, 0, kern_pair);
807 ok(GetLastError() == ERROR_INVALID_PARAMETER,
808 "got error %ld, expected ERROR_INVALID_PARAMETER\n", GetLastError());
809 ok(ret == 0, "got %lu, expected 0\n", ret);
810 #endif
812 ret = GetKerningPairsW(hdc, 100, NULL);
813 ok(ret == total_kern_pairs, "got %u, expected %u\n", ret, total_kern_pairs);
815 ret = GetKerningPairsW(hdc, total_kern_pairs/2, kern_pair);
816 ok(ret == total_kern_pairs/2, "got %u, expected %u\n", ret, total_kern_pairs/2);
818 ret = GetKerningPairsW(hdc, total_kern_pairs, kern_pair);
819 ok(ret == total_kern_pairs, "got %u, expected %u\n", ret, total_kern_pairs);
821 matches = 0;
823 for (n = 0; n < ret; n++)
825 DWORD j;
826 #if 0
827 if (kern_pair[n].wFirst < 127 && kern_pair[n].wSecond < 127)
828 trace("{'%c','%c',%d},\n",
829 kern_pair[n].wFirst, kern_pair[n].wSecond, kern_pair[n].iKernAmount);
830 #endif
831 for (j = 0; j < kd[i].total_kern_pairs; j++)
833 if (kern_pair[n].wFirst == kd[i].kern_pair[j].wFirst &&
834 kern_pair[n].wSecond == kd[i].kern_pair[j].wSecond)
836 ok(kern_pair[n].iKernAmount == kd[i].kern_pair[j].iKernAmount,
837 "pair %d:%d got %d, expected %d\n",
838 kern_pair[n].wFirst, kern_pair[n].wSecond,
839 kern_pair[n].iKernAmount, kd[i].kern_pair[j].iKernAmount);
840 matches++;
845 ok(matches == kd[i].total_kern_pairs, "got matches %u, expected %u\n",
846 matches, kd[i].total_kern_pairs);
848 HeapFree(GetProcessHeap(), 0, kern_pair);
850 SelectObject(hdc, hfont_old);
851 DeleteObject(hfont);
854 ReleaseDC(0, hdc);
857 static void test_GetOutlineTextMetrics(void)
859 OUTLINETEXTMETRIC *otm;
860 LOGFONT lf;
861 HFONT hfont, hfont_old;
862 HDC hdc;
863 DWORD ret, otm_size;
865 if (!is_font_installed("Arial"))
867 skip("Arial is not installed\n");
868 return;
871 hdc = GetDC(0);
873 memset(&lf, 0, sizeof(lf));
874 strcpy(lf.lfFaceName, "Arial");
875 lf.lfHeight = -13;
876 lf.lfWeight = FW_NORMAL;
877 lf.lfPitchAndFamily = DEFAULT_PITCH;
878 lf.lfQuality = PROOF_QUALITY;
879 hfont = CreateFontIndirect(&lf);
880 assert(hfont != 0);
882 hfont_old = SelectObject(hdc, hfont);
883 otm_size = GetOutlineTextMetrics(hdc, 0, NULL);
884 trace("otm buffer size %u (0x%x)\n", otm_size, otm_size);
886 otm = HeapAlloc(GetProcessHeap(), 0, otm_size);
888 memset(otm, 0xAA, otm_size);
889 SetLastError(0xdeadbeef);
890 otm->otmSize = sizeof(*otm); /* just in case for Win9x compatibility */
891 ret = GetOutlineTextMetrics(hdc, otm->otmSize, otm);
892 ok(ret == 1 /* Win9x */ ||
893 ret == otm->otmSize /* XP*/,
894 "expected %u, got %u, error %d\n", otm->otmSize, ret, GetLastError());
895 if (ret != 1) /* Win9x doesn't care about pointing beyond of the buffer */
897 ok(otm->otmpFamilyName == NULL, "expected NULL got %p\n", otm->otmpFamilyName);
898 ok(otm->otmpFaceName == NULL, "expected NULL got %p\n", otm->otmpFaceName);
899 ok(otm->otmpStyleName == NULL, "expected NULL got %p\n", otm->otmpStyleName);
900 ok(otm->otmpFullName == NULL, "expected NULL got %p\n", otm->otmpFullName);
903 memset(otm, 0xAA, otm_size);
904 SetLastError(0xdeadbeef);
905 otm->otmSize = otm_size; /* just in case for Win9x compatibility */
906 ret = GetOutlineTextMetrics(hdc, otm->otmSize, otm);
907 ok(ret == 1 /* Win9x */ ||
908 ret == otm->otmSize /* XP*/,
909 "expected %u, got %u, error %d\n", otm->otmSize, ret, GetLastError());
910 if (ret != 1) /* Win9x doesn't care about pointing beyond of the buffer */
912 ok(otm->otmpFamilyName != NULL, "expected not NULL got %p\n", otm->otmpFamilyName);
913 ok(otm->otmpFaceName != NULL, "expected not NULL got %p\n", otm->otmpFaceName);
914 ok(otm->otmpStyleName != NULL, "expected not NULL got %p\n", otm->otmpStyleName);
915 ok(otm->otmpFullName != NULL, "expected not NULL got %p\n", otm->otmpFullName);
918 /* ask about truncated data */
919 memset(otm, 0xAA, otm_size);
920 SetLastError(0xdeadbeef);
921 otm->otmSize = sizeof(*otm) - sizeof(LPSTR); /* just in case for Win9x compatibility */
922 ret = GetOutlineTextMetrics(hdc, otm->otmSize, otm);
923 ok(ret == 1 /* Win9x */ ||
924 ret == otm->otmSize /* XP*/,
925 "expected %u, got %u, error %d\n", otm->otmSize, ret, GetLastError());
926 if (ret != 1) /* Win9x doesn't care about pointing beyond of the buffer */
928 ok(otm->otmpFamilyName == NULL, "expected NULL got %p\n", otm->otmpFamilyName);
929 ok(otm->otmpFaceName == NULL, "expected NULL got %p\n", otm->otmpFaceName);
930 ok(otm->otmpStyleName == NULL, "expected NULL got %p\n", otm->otmpStyleName);
932 ok(otm->otmpFullName == (LPSTR)0xAAAAAAAA, "expected 0xAAAAAAAA got %p\n", otm->otmpFullName);
934 HeapFree(GetProcessHeap(), 0, otm);
936 SelectObject(hdc, hfont_old);
937 DeleteObject(hfont);
939 ReleaseDC(0, hdc);
942 static void testJustification(HDC hdc, PSTR str, RECT *clientArea)
944 INT x, y,
945 breakCount,
946 outputWidth = 0, /* to test TabbedTextOut() */
947 justifiedWidth = 0, /* to test GetTextExtentExPointW() */
948 areaWidth = clientArea->right - clientArea->left,
949 nErrors = 0, e;
950 BOOL lastExtent = FALSE;
951 PSTR pFirstChar, pLastChar;
952 SIZE size;
953 TEXTMETRICA tm;
954 struct err
956 char extent[100];
957 int GetTextExtentExPointWWidth;
958 int TabbedTextOutWidth;
959 } error[10];
961 GetTextMetricsA(hdc, &tm);
962 y = clientArea->top;
963 do {
964 breakCount = 0;
965 while (*str == tm.tmBreakChar) str++; /* skip leading break chars */
966 pFirstChar = str;
968 do {
969 pLastChar = str;
971 /* if not at the end of the string, ... */
972 if (*str == '\0') break;
973 /* ... add the next word to the current extent */
974 while (*str != '\0' && *str++ != tm.tmBreakChar);
975 breakCount++;
976 SetTextJustification(hdc, 0, 0);
977 GetTextExtentPoint32(hdc, pFirstChar, str - pFirstChar - 1, &size);
978 } while ((int) size.cx < areaWidth);
980 /* ignore trailing break chars */
981 breakCount--;
982 while (*(pLastChar - 1) == tm.tmBreakChar)
984 pLastChar--;
985 breakCount--;
988 if (*str == '\0' || breakCount <= 0) pLastChar = str;
990 SetTextJustification(hdc, 0, 0);
991 GetTextExtentPoint32(hdc, pFirstChar, pLastChar - pFirstChar, &size);
993 /* do not justify the last extent */
994 if (*str != '\0' && breakCount > 0)
996 SetTextJustification(hdc, areaWidth - size.cx, breakCount);
997 GetTextExtentPoint32(hdc, pFirstChar, pLastChar - pFirstChar, &size);
998 justifiedWidth = size.cx;
1000 else lastExtent = TRUE;
1002 x = clientArea->left;
1004 outputWidth = LOWORD(TabbedTextOut(
1005 hdc, x, y, pFirstChar, pLastChar - pFirstChar,
1006 0, NULL, 0));
1007 /* catch errors and report them */
1008 if (!lastExtent && ((outputWidth != areaWidth) || (justifiedWidth != areaWidth)))
1010 memset(error[nErrors].extent, 0, 100);
1011 memcpy(error[nErrors].extent, pFirstChar, pLastChar - pFirstChar);
1012 error[nErrors].TabbedTextOutWidth = outputWidth;
1013 error[nErrors].GetTextExtentExPointWWidth = justifiedWidth;
1014 nErrors++;
1017 y += size.cy;
1018 str = pLastChar;
1019 } while (*str && y < clientArea->bottom);
1021 for (e = 0; e < nErrors; e++)
1023 ok(error[e].TabbedTextOutWidth == areaWidth,
1024 "The output text (\"%s\") width should be %d, not %d.\n",
1025 error[e].extent, areaWidth, error[e].TabbedTextOutWidth);
1026 /* The width returned by GetTextExtentPoint32() is exactly the same
1027 returned by GetTextExtentExPointW() - see dlls/gdi32/font.c */
1028 ok(error[e].GetTextExtentExPointWWidth == areaWidth,
1029 "GetTextExtentPointW() for \"%s\" should have returned a width of %d, not %d.\n",
1030 error[e].extent, areaWidth, error[e].GetTextExtentExPointWWidth);
1034 static void test_SetTextJustification(void)
1036 HDC hdc;
1037 RECT clientArea;
1038 LOGFONTA lf;
1039 HFONT hfont;
1040 HWND hwnd;
1041 static char testText[] =
1042 "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do "
1043 "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut "
1044 "enim ad minim veniam, quis nostrud exercitation ullamco laboris "
1045 "nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in "
1046 "reprehenderit in voluptate velit esse cillum dolore eu fugiat "
1047 "nulla pariatur. Excepteur sint occaecat cupidatat non proident, "
1048 "sunt in culpa qui officia deserunt mollit anim id est laborum.";
1050 hwnd = CreateWindowExA(0, "static", "", WS_POPUP, 0,0, 400,400, 0, 0, 0, NULL);
1051 GetClientRect( hwnd, &clientArea );
1052 hdc = GetDC( hwnd );
1054 memset(&lf, 0, sizeof lf);
1055 lf.lfCharSet = ANSI_CHARSET;
1056 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
1057 lf.lfWeight = FW_DONTCARE;
1058 lf.lfHeight = 20;
1059 lf.lfQuality = DEFAULT_QUALITY;
1060 lstrcpyA(lf.lfFaceName, "Times New Roman");
1061 hfont = create_font("Times New Roman", &lf);
1062 SelectObject(hdc, hfont);
1064 testJustification(hdc, testText, &clientArea);
1066 DeleteObject(hfont);
1067 ReleaseDC(hwnd, hdc);
1068 DestroyWindow(hwnd);
1071 static BOOL get_glyph_indices(INT charset, UINT code_page, WORD *idx, UINT count, BOOL unicode)
1073 HDC hdc;
1074 LOGFONTA lf;
1075 HFONT hfont, hfont_old;
1076 CHARSETINFO csi;
1077 FONTSIGNATURE fs;
1078 INT cs;
1079 DWORD i, ret;
1080 char name[64];
1082 assert(count <= 128);
1084 memset(&lf, 0, sizeof(lf));
1086 lf.lfCharSet = charset;
1087 lf.lfHeight = 10;
1088 lstrcpyA(lf.lfFaceName, "Arial");
1089 SetLastError(0xdeadbeef);
1090 hfont = CreateFontIndirectA(&lf);
1091 ok(hfont != 0, "CreateFontIndirectA error %u\n", GetLastError());
1093 hdc = GetDC(0);
1094 hfont_old = SelectObject(hdc, hfont);
1096 cs = GetTextCharsetInfo(hdc, &fs, 0);
1097 ok(cs == charset, "expected %d, got %d\n", charset, cs);
1099 SetLastError(0xdeadbeef);
1100 ret = GetTextFace(hdc, sizeof(name), name);
1101 ok(ret, "GetTextFace error %u\n", GetLastError());
1103 if (charset == SYMBOL_CHARSET)
1105 ok(strcmp("Arial", name), "face name should NOT be Arial\n");
1106 ok(fs.fsCsb[0] & (1 << 31), "symbol encoding should be available\n");
1108 else
1110 ok(!strcmp("Arial", name), "face name should be Arial, not %s\n", name);
1111 ok(!(fs.fsCsb[0] & (1 << 31)), "symbol encoding should NOT be available\n");
1114 if (!TranslateCharsetInfo((DWORD *)cs, &csi, TCI_SRCCHARSET))
1116 trace("Can't find codepage for charset %d\n", cs);
1117 ReleaseDC(0, hdc);
1118 return FALSE;
1120 ok(csi.ciACP == code_page, "expected %d, got %d\n", code_page, csi.ciACP);
1122 if (unicode)
1124 char ansi_buf[128];
1125 WCHAR unicode_buf[128];
1127 for (i = 0; i < count; i++) ansi_buf[i] = (BYTE)(i + 128);
1129 MultiByteToWideChar(code_page, 0, ansi_buf, count, unicode_buf, count);
1131 SetLastError(0xdeadbeef);
1132 ret = pGetGlyphIndicesW(hdc, unicode_buf, count, idx, 0);
1133 ok(ret == count, "GetGlyphIndicesW error %u\n", GetLastError());
1135 else
1137 char ansi_buf[128];
1139 for (i = 0; i < count; i++) ansi_buf[i] = (BYTE)(i + 128);
1141 SetLastError(0xdeadbeef);
1142 ret = pGetGlyphIndicesA(hdc, ansi_buf, count, idx, 0);
1143 ok(ret == count, "GetGlyphIndicesA error %u\n", GetLastError());
1146 SelectObject(hdc, hfont_old);
1147 DeleteObject(hfont);
1149 ReleaseDC(0, hdc);
1151 return TRUE;
1154 static void test_font_charset(void)
1156 static struct charset_data
1158 INT charset;
1159 UINT code_page;
1160 WORD font_idxA[128], font_idxW[128];
1161 } cd[] =
1163 { ANSI_CHARSET, 1252 },
1164 { RUSSIAN_CHARSET, 1251 },
1165 { SYMBOL_CHARSET, CP_SYMBOL } /* keep it as the last one */
1167 int i;
1169 if (!pGetGlyphIndicesA || !pGetGlyphIndicesW)
1171 skip("Skipping the font charset test on a Win9x platform\n");
1172 return;
1175 if (!is_font_installed("Arial"))
1177 skip("Arial is not installed\n");
1178 return;
1181 for (i = 0; i < sizeof(cd)/sizeof(cd[0]); i++)
1183 if (cd[i].charset == SYMBOL_CHARSET)
1185 if (!is_font_installed("Symbol") && !is_font_installed("Wingdings"))
1187 skip("Symbol or Wingdings is not installed\n");
1188 break;
1191 get_glyph_indices(cd[i].charset, cd[i].code_page, cd[i].font_idxA, 128, FALSE);
1192 get_glyph_indices(cd[i].charset, cd[i].code_page, cd[i].font_idxW, 128, TRUE);
1193 ok(!memcmp(cd[i].font_idxA, cd[i].font_idxW, 128*sizeof(WORD)), "%d: indices don't match\n", i);
1196 ok(memcmp(cd[0].font_idxW, cd[1].font_idxW, 128*sizeof(WORD)), "0 vs 1: indices shouldn't match\n");
1197 if (i > 2)
1199 ok(memcmp(cd[0].font_idxW, cd[2].font_idxW, 128*sizeof(WORD)), "0 vs 2: indices shouldn't match\n");
1200 ok(memcmp(cd[1].font_idxW, cd[2].font_idxW, 128*sizeof(WORD)), "1 vs 2: indices shouldn't match\n");
1202 else
1203 skip("Symbol or Wingdings is not installed\n");
1206 static void test_GetFontUnicodeRanges(void)
1208 LOGFONTA lf;
1209 HDC hdc;
1210 HFONT hfont, hfont_old;
1211 DWORD size;
1212 GLYPHSET *gs;
1214 if (!pGetFontUnicodeRanges)
1216 skip("GetFontUnicodeRanges not available before W2K\n");
1217 return;
1220 memset(&lf, 0, sizeof(lf));
1221 lstrcpyA(lf.lfFaceName, "Arial");
1222 hfont = create_font("Arial", &lf);
1224 hdc = GetDC(0);
1225 hfont_old = SelectObject(hdc, hfont);
1227 size = pGetFontUnicodeRanges(NULL, NULL);
1228 ok(!size, "GetFontUnicodeRanges succeeded unexpectedly\n");
1230 size = pGetFontUnicodeRanges(hdc, NULL);
1231 ok(size, "GetFontUnicodeRanges failed unexpectedly\n");
1233 gs = HeapAlloc(GetProcessHeap(), 0, size);
1235 size = pGetFontUnicodeRanges(hdc, gs);
1236 ok(size, "GetFontUnicodeRanges failed\n");
1237 #if 0
1238 for (i = 0; i < gs->cRanges; i++)
1239 trace("%03d wcLow %04x cGlyphs %u\n", i, gs->ranges[i].wcLow, gs->ranges[i].cGlyphs);
1240 #endif
1241 trace("found %u ranges\n", gs->cRanges);
1243 HeapFree(GetProcessHeap(), 0, gs);
1245 SelectObject(hdc, hfont_old);
1246 DeleteObject(hfont);
1247 ReleaseDC(NULL, hdc);
1250 #define MAX_ENUM_FONTS 256
1252 struct enum_font_data
1254 int total;
1255 LOGFONT lf[MAX_ENUM_FONTS];
1258 static INT CALLBACK arial_enum_proc(const LOGFONT *lf, const TEXTMETRIC *tm, DWORD type, LPARAM lParam)
1260 struct enum_font_data *efd = (struct enum_font_data *)lParam;
1262 if (type != TRUETYPE_FONTTYPE) return 1;
1263 #if 0
1264 trace("enumed font \"%s\", charset %d, weight %d, italic %d\n",
1265 lf->lfFaceName, lf->lfCharSet, lf->lfWeight, lf->lfItalic);
1266 #endif
1267 if (efd->total < MAX_ENUM_FONTS)
1268 efd->lf[efd->total++] = *lf;
1270 return 1;
1273 static void get_charset_stats(struct enum_font_data *efd,
1274 int *ansi_charset, int *symbol_charset,
1275 int *russian_charset)
1277 int i;
1279 *ansi_charset = 0;
1280 *symbol_charset = 0;
1281 *russian_charset = 0;
1283 for (i = 0; i < efd->total; i++)
1285 switch (efd->lf[i].lfCharSet)
1287 case ANSI_CHARSET:
1288 (*ansi_charset)++;
1289 break;
1290 case SYMBOL_CHARSET:
1291 (*symbol_charset)++;
1292 break;
1293 case RUSSIAN_CHARSET:
1294 (*russian_charset)++;
1295 break;
1300 static void test_EnumFontFamilies(const char *font_name, INT font_charset)
1302 struct enum_font_data efd;
1303 LOGFONT lf;
1304 HDC hdc;
1305 int i, ret, ansi_charset, symbol_charset, russian_charset;
1307 trace("Testing font %s, charset %d\n", *font_name ? font_name : "<empty>", font_charset);
1309 if (*font_name && !is_truetype_font_installed(font_name))
1311 skip("%s is not installed\n", font_name);
1312 return;
1315 hdc = GetDC(0);
1317 /* Observed behaviour: EnumFontFamilies enumerates aliases like "Arial Cyr"
1318 * while EnumFontFamiliesEx doesn't.
1320 if (!*font_name && font_charset == DEFAULT_CHARSET) /* do it only once */
1322 efd.total = 0;
1323 SetLastError(0xdeadbeef);
1324 ret = EnumFontFamilies(hdc, NULL, arial_enum_proc, (LPARAM)&efd);
1325 ok(ret, "EnumFontFamilies error %u\n", GetLastError());
1326 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
1327 trace("enumerated ansi %d, symbol %d, russian %d fonts for NULL\n",
1328 ansi_charset, symbol_charset, russian_charset);
1329 ok(efd.total > 0, "no fonts enumerated: NULL\n");
1330 ok(ansi_charset > 0, "NULL family should enumerate ANSI_CHARSET\n");
1331 ok(symbol_charset > 0, "NULL family should enumerate SYMBOL_CHARSET\n");
1332 ok(russian_charset > 0, "NULL family should enumerate RUSSIAN_CHARSET\n");
1334 efd.total = 0;
1335 SetLastError(0xdeadbeef);
1336 ret = EnumFontFamiliesEx(hdc, NULL, arial_enum_proc, (LPARAM)&efd, 0);
1337 ok(ret, "EnumFontFamiliesEx error %u\n", GetLastError());
1338 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
1339 trace("enumerated ansi %d, symbol %d, russian %d fonts for NULL\n",
1340 ansi_charset, symbol_charset, russian_charset);
1341 ok(efd.total > 0, "no fonts enumerated: NULL\n");
1342 ok(ansi_charset > 0, "NULL family should enumerate ANSI_CHARSET\n");
1343 ok(symbol_charset > 0, "NULL family should enumerate SYMBOL_CHARSET\n");
1344 ok(russian_charset > 0, "NULL family should enumerate RUSSIAN_CHARSET\n");
1347 efd.total = 0;
1348 SetLastError(0xdeadbeef);
1349 ret = EnumFontFamilies(hdc, font_name, arial_enum_proc, (LPARAM)&efd);
1350 ok(ret, "EnumFontFamilies error %u\n", GetLastError());
1351 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
1352 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s\n",
1353 ansi_charset, symbol_charset, russian_charset,
1354 *font_name ? font_name : "<empty>");
1355 if (*font_name)
1356 ok(efd.total > 0, "no fonts enumerated: %s\n", font_name);
1357 else
1358 ok(!efd.total, "no fonts should be enumerated for empty font_name\n");
1359 for (i = 0; i < efd.total; i++)
1361 /* FIXME: remove completely once Wine is fixed */
1362 if (efd.lf[i].lfCharSet != font_charset)
1364 todo_wine
1365 ok(efd.lf[i].lfCharSet == font_charset, "%d: got charset %d\n", i, efd.lf[i].lfCharSet);
1367 else
1368 ok(efd.lf[i].lfCharSet == font_charset, "%d: got charset %d\n", i, efd.lf[i].lfCharSet);
1369 ok(!lstrcmp(efd.lf[i].lfFaceName, font_name), "expected %s, got %s\n",
1370 font_name, efd.lf[i].lfFaceName);
1373 memset(&lf, 0, sizeof(lf));
1374 lf.lfCharSet = ANSI_CHARSET;
1375 lstrcpy(lf.lfFaceName, font_name);
1376 efd.total = 0;
1377 SetLastError(0xdeadbeef);
1378 ret = EnumFontFamiliesEx(hdc, &lf, arial_enum_proc, (LPARAM)&efd, 0);
1379 ok(ret, "EnumFontFamiliesEx error %u\n", GetLastError());
1380 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
1381 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s ANSI_CHARSET\n",
1382 ansi_charset, symbol_charset, russian_charset,
1383 *font_name ? font_name : "<empty>");
1384 if (font_charset == SYMBOL_CHARSET)
1386 if (*font_name)
1387 ok(efd.total == 0, "no fonts should be enumerated: %s ANSI_CHARSET\n", font_name);
1388 else
1389 ok(efd.total > 0, "no fonts enumerated: %s\n", font_name);
1391 else
1393 ok(efd.total > 0, "no fonts enumerated: %s ANSI_CHARSET\n", font_name);
1394 for (i = 0; i < efd.total; i++)
1396 ok(efd.lf[i].lfCharSet == ANSI_CHARSET, "%d: got charset %d\n", i, efd.lf[i].lfCharSet);
1397 if (*font_name)
1398 ok(!lstrcmp(efd.lf[i].lfFaceName, font_name), "expected %s, got %s\n",
1399 font_name, efd.lf[i].lfFaceName);
1403 /* DEFAULT_CHARSET should enumerate all available charsets */
1404 memset(&lf, 0, sizeof(lf));
1405 lf.lfCharSet = DEFAULT_CHARSET;
1406 lstrcpy(lf.lfFaceName, font_name);
1407 efd.total = 0;
1408 SetLastError(0xdeadbeef);
1409 EnumFontFamiliesEx(hdc, &lf, arial_enum_proc, (LPARAM)&efd, 0);
1410 ok(ret, "EnumFontFamiliesEx error %u\n", GetLastError());
1411 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
1412 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s DEFAULT_CHARSET\n",
1413 ansi_charset, symbol_charset, russian_charset,
1414 *font_name ? font_name : "<empty>");
1415 ok(efd.total > 0, "no fonts enumerated: %s DEFAULT_CHARSET\n", font_name);
1416 for (i = 0; i < efd.total; i++)
1418 if (*font_name)
1419 ok(!lstrcmp(efd.lf[i].lfFaceName, font_name), "expected %s, got %s\n",
1420 font_name, efd.lf[i].lfFaceName);
1422 if (*font_name)
1424 switch (font_charset)
1426 case ANSI_CHARSET:
1427 ok(ansi_charset > 0,
1428 "ANSI_CHARSET should enumerate ANSI_CHARSET for %s\n", font_name);
1429 ok(!symbol_charset,
1430 "ANSI_CHARSET should NOT enumerate SYMBOL_CHARSET for %s\n", font_name);
1431 ok(russian_charset > 0,
1432 "ANSI_CHARSET should enumerate RUSSIAN_CHARSET for %s\n", font_name);
1433 break;
1434 case SYMBOL_CHARSET:
1435 ok(!ansi_charset,
1436 "SYMBOL_CHARSET should NOT enumerate ANSI_CHARSET for %s\n", font_name);
1437 ok(symbol_charset,
1438 "SYMBOL_CHARSET should enumerate SYMBOL_CHARSET for %s\n", font_name);
1439 ok(!russian_charset,
1440 "SYMBOL_CHARSET should NOT enumerate RUSSIAN_CHARSET for %s\n", font_name);
1441 break;
1442 case DEFAULT_CHARSET:
1443 ok(ansi_charset > 0,
1444 "DEFAULT_CHARSET should enumerate ANSI_CHARSET for %s\n", font_name);
1445 ok(symbol_charset > 0,
1446 "DEFAULT_CHARSET should enumerate SYMBOL_CHARSET for %s\n", font_name);
1447 ok(russian_charset > 0,
1448 "DEFAULT_CHARSET should enumerate RUSSIAN_CHARSET for %s\n", font_name);
1449 break;
1452 else
1454 ok(ansi_charset > 0,
1455 "DEFAULT_CHARSET should enumerate ANSI_CHARSET for %s\n", *font_name ? font_name : "<empty>");
1456 ok(symbol_charset > 0,
1457 "DEFAULT_CHARSET should enumerate SYMBOL_CHARSET for %s\n", *font_name ? font_name : "<empty>");
1458 ok(russian_charset > 0,
1459 "DEFAULT_CHARSET should enumerate RUSSIAN_CHARSET for %s\n", *font_name ? font_name : "<empty>");
1462 memset(&lf, 0, sizeof(lf));
1463 lf.lfCharSet = SYMBOL_CHARSET;
1464 lstrcpy(lf.lfFaceName, font_name);
1465 efd.total = 0;
1466 SetLastError(0xdeadbeef);
1467 EnumFontFamiliesEx(hdc, &lf, arial_enum_proc, (LPARAM)&efd, 0);
1468 ok(ret, "EnumFontFamiliesEx error %u\n", GetLastError());
1469 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
1470 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s SYMBOL_CHARSET\n",
1471 ansi_charset, symbol_charset, russian_charset,
1472 *font_name ? font_name : "<empty>");
1473 if (*font_name && font_charset == ANSI_CHARSET)
1474 ok(efd.total == 0, "no fonts should be enumerated: %s SYMBOL_CHARSET\n", font_name);
1475 else
1477 ok(efd.total > 0, "no fonts enumerated: %s SYMBOL_CHARSET\n", font_name);
1478 for (i = 0; i < efd.total; i++)
1480 ok(efd.lf[i].lfCharSet == SYMBOL_CHARSET, "%d: got charset %d\n", i, efd.lf[i].lfCharSet);
1481 if (*font_name)
1482 ok(!lstrcmp(efd.lf[i].lfFaceName, font_name), "expected %s, got %s\n",
1483 font_name, efd.lf[i].lfFaceName);
1486 ok(!ansi_charset,
1487 "SYMBOL_CHARSET should NOT enumerate ANSI_CHARSET for %s\n", *font_name ? font_name : "<empty>");
1488 ok(symbol_charset > 0,
1489 "SYMBOL_CHARSET should enumerate SYMBOL_CHARSET for %s\n", *font_name ? font_name : "<empty>");
1490 ok(!russian_charset,
1491 "SYMBOL_CHARSET should NOT enumerate RUSSIAN_CHARSET for %s\n", *font_name ? font_name : "<empty>");
1494 ReleaseDC(0, hdc);
1497 static void test_negative_width(HDC hdc, const LOGFONTA *lf)
1499 HFONT hfont, hfont_prev;
1500 DWORD ret;
1501 GLYPHMETRICS gm1, gm2;
1502 LOGFONTA lf2 = *lf;
1503 WORD idx;
1504 MAT2 mat = { {0,1}, {0,0}, {0,0}, {0,1} };
1506 /* negative widths are handled just as positive ones */
1507 lf2.lfWidth = -lf->lfWidth;
1509 SetLastError(0xdeadbeef);
1510 hfont = CreateFontIndirectA(lf);
1511 ok(hfont != 0, "CreateFontIndirect error %u\n", GetLastError());
1512 check_font("original", lf, hfont);
1514 hfont_prev = SelectObject(hdc, hfont);
1516 ret = pGetGlyphIndicesA(hdc, "x", 1, &idx, GGI_MARK_NONEXISTING_GLYPHS);
1517 if (ret == GDI_ERROR || idx == 0xffff)
1519 SelectObject(hdc, hfont_prev);
1520 DeleteObject(hfont);
1521 skip("Font %s doesn't contain 'x', skipping the test\n", lf->lfFaceName);
1522 return;
1525 /* filling with 0xaa causes false pass under WINEDEBUG=warn+heap */
1526 memset(&gm1, 0xab, sizeof(gm1));
1527 SetLastError(0xdeadbeef);
1528 ret = GetGlyphOutlineA(hdc, 'x', GGO_METRICS, &gm1, 0, NULL, &mat);
1529 ok(ret != GDI_ERROR, "GetGlyphOutline error 0x%x\n", GetLastError());
1531 SelectObject(hdc, hfont_prev);
1532 DeleteObject(hfont);
1534 SetLastError(0xdeadbeef);
1535 hfont = CreateFontIndirectA(&lf2);
1536 ok(hfont != 0, "CreateFontIndirect error %u\n", GetLastError());
1537 check_font("negative width", &lf2, hfont);
1539 hfont_prev = SelectObject(hdc, hfont);
1541 memset(&gm2, 0xbb, sizeof(gm2));
1542 SetLastError(0xdeadbeef);
1543 ret = GetGlyphOutlineA(hdc, 'x', GGO_METRICS, &gm2, 0, NULL, &mat);
1544 ok(ret != GDI_ERROR, "GetGlyphOutline error 0x%x\n", GetLastError());
1546 SelectObject(hdc, hfont_prev);
1547 DeleteObject(hfont);
1549 ok(gm1.gmBlackBoxX == gm2.gmBlackBoxX &&
1550 gm1.gmBlackBoxY == gm2.gmBlackBoxY &&
1551 gm1.gmptGlyphOrigin.x == gm2.gmptGlyphOrigin.x &&
1552 gm1.gmptGlyphOrigin.y == gm2.gmptGlyphOrigin.y &&
1553 gm1.gmCellIncX == gm2.gmCellIncX &&
1554 gm1.gmCellIncY == gm2.gmCellIncY,
1555 "gm1=%d,%d,%d,%d,%d,%d gm2=%d,%d,%d,%d,%d,%d\n",
1556 gm1.gmBlackBoxX, gm1.gmBlackBoxY, gm1.gmptGlyphOrigin.x,
1557 gm1.gmptGlyphOrigin.y, gm1.gmCellIncX, gm1.gmCellIncY,
1558 gm2.gmBlackBoxX, gm2.gmBlackBoxY, gm2.gmptGlyphOrigin.x,
1559 gm2.gmptGlyphOrigin.y, gm2.gmCellIncX, gm2.gmCellIncY);
1562 /* PANOSE is 10 bytes in size, need to pack the structure properly */
1563 #include "pshpack2.h"
1564 typedef struct
1566 USHORT version;
1567 SHORT xAvgCharWidth;
1568 USHORT usWeightClass;
1569 USHORT usWidthClass;
1570 SHORT fsType;
1571 SHORT ySubscriptXSize;
1572 SHORT ySubscriptYSize;
1573 SHORT ySubscriptXOffset;
1574 SHORT ySubscriptYOffset;
1575 SHORT ySuperscriptXSize;
1576 SHORT ySuperscriptYSize;
1577 SHORT ySuperscriptXOffset;
1578 SHORT ySuperscriptYOffset;
1579 SHORT yStrikeoutSize;
1580 SHORT yStrikeoutPosition;
1581 SHORT sFamilyClass;
1582 PANOSE panose;
1583 ULONG ulUnicodeRange1;
1584 ULONG ulUnicodeRange2;
1585 ULONG ulUnicodeRange3;
1586 ULONG ulUnicodeRange4;
1587 CHAR achVendID[4];
1588 USHORT fsSelection;
1589 USHORT usFirstCharIndex;
1590 USHORT usLastCharIndex;
1591 /* According to the Apple spec, original version didn't have the below fields,
1592 * version numbers were taked from the OpenType spec.
1594 /* version 0 (TrueType 1.5) */
1595 USHORT sTypoAscender;
1596 USHORT sTypoDescender;
1597 USHORT sTypoLineGap;
1598 USHORT usWinAscent;
1599 USHORT usWinDescent;
1600 /* version 1 (TrueType 1.66) */
1601 ULONG ulCodePageRange1;
1602 ULONG ulCodePageRange2;
1603 /* version 2 (OpenType 1.2) */
1604 SHORT sxHeight;
1605 SHORT sCapHeight;
1606 USHORT usDefaultChar;
1607 USHORT usBreakChar;
1608 USHORT usMaxContext;
1609 } TT_OS2_V2;
1610 #include "poppack.h"
1612 #ifdef WORDS_BIGENDIAN
1613 #define GET_BE_WORD(x) (x)
1614 #else
1615 #define GET_BE_WORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
1616 #endif
1618 #define MS_MAKE_TAG(ch0, ch1, ch2, ch3) \
1619 ((DWORD)(BYTE)(ch0) | ((DWORD)(BYTE)(ch1) << 8) | \
1620 ((DWORD)(BYTE)(ch2) << 16) | ((DWORD)(BYTE)(ch3) << 24))
1621 #define MS_OS2_TAG MS_MAKE_TAG('O','S','/','2')
1623 static void test_text_metrics(const LOGFONTA *lf)
1625 HDC hdc;
1626 HFONT hfont, hfont_old;
1627 TEXTMETRICA tmA;
1628 TEXTMETRICW tmW;
1629 UINT first_unicode_char, last_unicode_char, default_char, break_char;
1630 INT test_char;
1631 TT_OS2_V2 tt_os2;
1632 USHORT version;
1633 LONG size, ret;
1634 const char *font_name = lf->lfFaceName;
1636 trace("Testing font metrics for %s, charset %d\n", font_name, lf->lfCharSet);
1638 hdc = GetDC(0);
1640 SetLastError(0xdeadbeef);
1641 hfont = CreateFontIndirectA(lf);
1642 ok(hfont != 0, "CreateFontIndirect error %u\n", GetLastError());
1644 hfont_old = SelectObject(hdc, hfont);
1646 size = GetFontData(hdc, MS_OS2_TAG, 0, NULL, 0);
1647 if (size == GDI_ERROR)
1649 trace("OS/2 chunk was not found\n");
1650 goto end_of_test;
1652 if (size > sizeof(tt_os2))
1654 trace("got too large OS/2 chunk of size %u\n", size);
1655 size = sizeof(tt_os2);
1658 memset(&tt_os2, 0, sizeof(tt_os2));
1659 ret = GetFontData(hdc, MS_OS2_TAG, 0, &tt_os2, size);
1660 ok(ret == size, "GetFontData should return %u not %u\n", size, ret);
1662 version = GET_BE_WORD(tt_os2.version);
1663 trace("OS/2 chunk version %u, vendor %4.4s\n", version, (LPCSTR)&tt_os2.achVendID);
1665 first_unicode_char = GET_BE_WORD(tt_os2.usFirstCharIndex);
1666 last_unicode_char = GET_BE_WORD(tt_os2.usLastCharIndex);
1667 default_char = GET_BE_WORD(tt_os2.usDefaultChar);
1668 break_char = GET_BE_WORD(tt_os2.usBreakChar);
1670 trace("for %s first %x, last %x, default %x, break %x\n", font_name,
1671 first_unicode_char, last_unicode_char, default_char, break_char);
1673 SetLastError(0xdeadbeef);
1674 ret = GetTextMetricsA(hdc, &tmA);
1675 ok(ret, "GetTextMetricsA error %u\n", GetLastError());
1676 trace("A: first %x, last %x, default %x, break %x\n",
1677 tmA.tmFirstChar, tmA.tmLastChar, tmA.tmDefaultChar, tmA.tmBreakChar);
1679 #if 0 /* FIXME: This doesn't appear to be what Windows does */
1680 test_char = min(first_unicode_char - 1, 255);
1681 ok(tmA.tmFirstChar == test_char, "A: tmFirstChar for %s %02x != %02x\n",
1682 font_name, tmA.tmFirstChar, test_char);
1683 #endif
1684 if (lf->lfCharSet == SYMBOL_CHARSET)
1686 test_char = min(last_unicode_char - 0xf000, 255);
1687 ok(tmA.tmLastChar == test_char, "A: tmLastChar for %s %02x != %02x\n",
1688 font_name, tmA.tmLastChar, test_char);
1690 else
1692 test_char = min(last_unicode_char, 255);
1693 ok(tmA.tmLastChar == test_char, "A: tmLastChar for %s %02x != %02x\n",
1694 font_name, tmA.tmLastChar, test_char);
1697 SetLastError(0xdeadbeef);
1698 ret = GetTextMetricsW(hdc, &tmW);
1699 ok(ret || GetLastError() == ERROR_CALL_NOT_IMPLEMENTED,
1700 "GetTextMetricsW error %u\n", GetLastError());
1701 if (ret)
1703 trace("W: first %x, last %x, default %x, break %x\n",
1704 tmW.tmFirstChar, tmW.tmLastChar, tmW.tmDefaultChar,
1705 tmW.tmBreakChar);
1707 if (lf->lfCharSet == SYMBOL_CHARSET)
1709 /* It appears that for fonts with SYMBOL_CHARSET Windows always
1710 * sets symbol range to 0 - f0ff
1712 ok(tmW.tmFirstChar == 0, "W: tmFirstChar for %s %02x != 0\n",
1713 font_name, tmW.tmFirstChar);
1714 /* FIXME: Windows returns f0ff here, while Wine f0xx */
1715 ok(tmW.tmLastChar >= 0xf000, "W: tmLastChar for %s %02x < 0xf000\n",
1716 font_name, tmW.tmLastChar);
1718 ok(tmW.tmDefaultChar == 0x1f, "W: tmDefaultChar for %s %02x != 0x1f\n",
1719 font_name, tmW.tmDefaultChar);
1720 ok(tmW.tmBreakChar == 0x20, "W: tmBreakChar for %s %02x != 0x20\n",
1721 font_name, tmW.tmBreakChar);
1723 else
1725 ok(tmW.tmFirstChar == first_unicode_char, "W: tmFirstChar for %s %02x != %02x\n",
1726 font_name, tmW.tmFirstChar, first_unicode_char);
1727 ok(tmW.tmLastChar == last_unicode_char, "W: tmLastChar for %s %02x != %02x\n",
1728 font_name, tmW.tmLastChar, last_unicode_char);
1730 ret = GetDeviceCaps(hdc, LOGPIXELSX);
1731 ok(tmW.tmDigitizedAspectX == ret, "W: tmDigitizedAspectX %u != %u\n",
1732 tmW.tmDigitizedAspectX, ret);
1733 ret = GetDeviceCaps(hdc, LOGPIXELSY);
1734 ok(tmW.tmDigitizedAspectX == ret, "W: tmDigitizedAspectY %u != %u\n",
1735 tmW.tmDigitizedAspectX, ret);
1738 test_negative_width(hdc, lf);
1740 end_of_test:
1741 SelectObject(hdc, hfont_old);
1742 DeleteObject(hfont);
1744 ReleaseDC(0, hdc);
1747 static INT CALLBACK enum_truetype_font_proc(const LOGFONT *lf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
1749 INT *enumed = (INT *)lParam;
1751 if (type == TRUETYPE_FONTTYPE)
1753 (*enumed)++;
1754 test_text_metrics(lf);
1756 return 1;
1759 static void test_GetTextMetrics(void)
1761 LOGFONTA lf;
1762 HDC hdc;
1763 INT enumed;
1765 hdc = GetDC(0);
1767 memset(&lf, 0, sizeof(lf));
1768 lf.lfCharSet = DEFAULT_CHARSET;
1769 enumed = 0;
1770 EnumFontFamiliesExA(hdc, &lf, enum_truetype_font_proc, (LPARAM)&enumed, 0);
1771 trace("Tested metrics of %d truetype fonts\n", enumed);
1773 ReleaseDC(0, hdc);
1776 static void test_nonexistent_font(void)
1778 LOGFONTA lf;
1779 HDC hdc;
1780 HFONT hfont;
1781 CHARSETINFO csi;
1782 INT cs, expected_cs;
1783 char buf[LF_FACESIZE];
1785 if (!is_truetype_font_installed("Arial") ||
1786 !is_truetype_font_installed("Times New Roman"))
1788 skip("Arial or Times New Roman not installed\n");
1789 return;
1792 expected_cs = GetACP();
1793 if (!TranslateCharsetInfo(ULongToPtr(expected_cs), &csi, TCI_SRCCODEPAGE))
1795 skip("TranslateCharsetInfo failed for code page %d\n", expected_cs);
1796 return;
1798 expected_cs = csi.ciCharset;
1799 trace("ACP %d -> charset %d\n", GetACP(), expected_cs);
1801 hdc = GetDC(0);
1803 memset(&lf, 0, sizeof(lf));
1804 lf.lfHeight = 100;
1805 lf.lfWeight = FW_REGULAR;
1806 lf.lfCharSet = ANSI_CHARSET;
1807 lf.lfPitchAndFamily = FF_SWISS;
1808 strcpy(lf.lfFaceName, "Nonexistent font");
1809 hfont = CreateFontIndirectA(&lf);
1810 hfont = SelectObject(hdc, hfont);
1811 GetTextFaceA(hdc, sizeof(buf), buf);
1812 ok(!lstrcmpiA(buf, "Arial"), "Got %s\n", buf);
1813 cs = GetTextCharset(hdc);
1814 ok(cs == ANSI_CHARSET, "expected ANSI_CHARSET, got %d\n", cs);
1815 DeleteObject(SelectObject(hdc, hfont));
1817 memset(&lf, 0, sizeof(lf));
1818 lf.lfHeight = -13;
1819 lf.lfWeight = FW_DONTCARE;
1820 strcpy(lf.lfFaceName, "Nonexistent font");
1821 hfont = CreateFontIndirectA(&lf);
1822 hfont = SelectObject(hdc, hfont);
1823 GetTextFaceA(hdc, sizeof(buf), buf);
1824 todo_wine /* Wine uses Arial for all substitutions */
1825 ok(!lstrcmpiA(buf, "Nonexistent font") /* XP, Vista */ ||
1826 !lstrcmpiA(buf, "MS Serif") /* Win9x */, "Got %s\n", buf);
1827 cs = GetTextCharset(hdc);
1828 ok(cs == expected_cs, "expected %d, got %d\n", expected_cs, cs);
1829 DeleteObject(SelectObject(hdc, hfont));
1831 memset(&lf, 0, sizeof(lf));
1832 lf.lfHeight = -13;
1833 lf.lfWeight = FW_REGULAR;
1834 strcpy(lf.lfFaceName, "Nonexistent font");
1835 hfont = CreateFontIndirectA(&lf);
1836 hfont = SelectObject(hdc, hfont);
1837 GetTextFaceA(hdc, sizeof(buf), buf);
1838 ok(!lstrcmpiA(buf, "Arial") /* XP, Vista */ ||
1839 !lstrcmpiA(buf, "Times New Roman") /* Win9x */, "Got %s\n", buf);
1840 cs = GetTextCharset(hdc);
1841 ok(cs == ANSI_CHARSET, "expected ANSI_CHARSET, got %d\n", cs);
1842 DeleteObject(SelectObject(hdc, hfont));
1844 memset(&lf, 0, sizeof(lf));
1845 lf.lfHeight = -13;
1846 lf.lfWeight = FW_DONTCARE;
1847 strcpy(lf.lfFaceName, "Times New Roman CE");
1848 hfont = CreateFontIndirectA(&lf);
1849 hfont = SelectObject(hdc, hfont);
1850 GetTextFaceA(hdc, sizeof(buf), buf);
1851 todo_wine /* Wine uses Arial for all substitutions */
1852 ok(!lstrcmpiA(buf, "Times New Roman CE") /* XP, Vista */ ||
1853 !lstrcmpiA(buf, "MS Serif") /* Win9x */, "Got %s\n", buf);
1854 cs = GetTextCharset(hdc);
1855 ok(cs == expected_cs, "expected %d, got %d\n", expected_cs, cs);
1856 DeleteObject(SelectObject(hdc, hfont));
1858 memset(&lf, 0, sizeof(lf));
1859 lf.lfHeight = -13;
1860 lf.lfWeight = FW_DONTCARE;
1861 strcpy(lf.lfFaceName, "Times New Roman");
1862 hfont = CreateFontIndirectA(&lf);
1863 hfont = SelectObject(hdc, hfont);
1864 GetTextFaceA(hdc, sizeof(buf), buf);
1865 ok(!lstrcmpiA(buf, "Times New Roman"), "Got %s\n", buf);
1866 cs = GetTextCharset(hdc);
1867 ok(cs == ANSI_CHARSET, "expected ANSI_CHARSET, got %d\n", cs);
1868 DeleteObject(SelectObject(hdc, hfont));
1870 memset(&lf, 0, sizeof(lf));
1871 lf.lfHeight = -13;
1872 lf.lfWeight = FW_REGULAR;
1873 strcpy(lf.lfFaceName, "Times New Roman CE");
1874 hfont = CreateFontIndirectA(&lf);
1875 hfont = SelectObject(hdc, hfont);
1876 GetTextFaceA(hdc, sizeof(buf), buf);
1877 ok(!lstrcmpiA(buf, "Arial") /* XP, Vista */ ||
1878 !lstrcmpiA(buf, "Times New Roman") /* Win9x */, "Got %s\n", buf);
1879 cs = GetTextCharset(hdc);
1880 ok(cs == ANSI_CHARSET, "expected ANSI_CHARSET, got %d\n", cs);
1881 DeleteObject(SelectObject(hdc, hfont));
1883 ReleaseDC(0, hdc);
1886 static void test_GdiRealizationInfo(void)
1888 HDC hdc;
1889 DWORD info[4];
1890 BOOL r;
1891 HFONT hfont, hfont_old;
1892 LOGFONTA lf;
1894 if(!pGdiRealizationInfo)
1896 skip("GdiRealizationInfo not available\n");
1897 return;
1900 hdc = GetDC(0);
1902 memset(info, 0xcc, sizeof(info));
1903 r = pGdiRealizationInfo(hdc, info);
1904 ok(r != 0, "ret 0\n");
1905 ok(info[0] == 1, "info[0] = %x for the system font\n", info[0]);
1906 ok(info[3] == 0xcccccccc, "structure longer than 3 dwords\n");
1908 if (!is_truetype_font_installed("Arial"))
1910 skip("skipping GdiRealizationInfo with truetype font\n");
1911 goto end;
1914 memset(&lf, 0, sizeof(lf));
1915 strcpy(lf.lfFaceName, "Arial");
1916 lf.lfHeight = 20;
1917 lf.lfWeight = FW_NORMAL;
1918 hfont = CreateFontIndirectA(&lf);
1919 hfont_old = SelectObject(hdc, hfont);
1921 memset(info, 0xcc, sizeof(info));
1922 r = pGdiRealizationInfo(hdc, info);
1923 ok(r != 0, "ret 0\n");
1924 ok(info[0] == 3, "info[0] = %x for arial\n", info[0]);
1925 ok(info[3] == 0xcccccccc, "structure longer than 3 dwords\n");
1927 DeleteObject(SelectObject(hdc, hfont_old));
1929 end:
1930 ReleaseDC(0, hdc);
1933 START_TEST(font)
1935 init();
1937 test_logfont();
1938 test_bitmap_font();
1939 test_bitmap_font_metrics();
1940 test_GdiGetCharDimensions();
1941 test_GetCharABCWidths();
1942 test_text_extents();
1943 test_GetGlyphIndices();
1944 test_GetKerningPairs();
1945 test_GetOutlineTextMetrics();
1946 test_SetTextJustification();
1947 test_font_charset();
1948 test_GetFontUnicodeRanges();
1949 test_nonexistent_font();
1951 /* On Windows Arial has a lot of default charset aliases such as Arial Cyr,
1952 * I'd like to avoid them in this test.
1954 test_EnumFontFamilies("Arial Black", ANSI_CHARSET);
1955 test_EnumFontFamilies("Symbol", SYMBOL_CHARSET);
1956 if (is_truetype_font_installed("Arial Black") &&
1957 (is_truetype_font_installed("Symbol") || is_truetype_font_installed("Wingdings")))
1959 test_EnumFontFamilies("", ANSI_CHARSET);
1960 test_EnumFontFamilies("", SYMBOL_CHARSET);
1961 test_EnumFontFamilies("", DEFAULT_CHARSET);
1963 else
1964 skip("Arial Black or Symbol/Wingdings is not installed\n");
1965 test_GetTextMetrics();
1966 test_GdiRealizationInfo();