gdi32: Add a font scaling test when lfHeight == 0 and lfWidth != 0, make it pass...
[wine.git] / dlls / gdi32 / tests / font.c
bloba91adcbfec161a3cae31490f93b1cf16a6aa59e3
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, "pGetGlyphIndicesW 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("GetGlyphIndices 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, "GetGlyphIndices count of glyphs should = 5 not %d\n", charcount);
615 ok((glyphs[4] == 0x001f || glyphs[4] == 0xffff /* Vista */), "GetGlyphIndices 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, "GetGlyphIndices count of glyphs should = 5 not %d\n", charcount);
619 ok(glyphs[4] == textm.tmDefaultChar, "GetGlyphIndices 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, "GetGlyphIndices count of glyphs should = 5 not %d\n", charcount);
637 ok(glyphs[4] == 0xffff, "GetGlyphIndices 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, "GetGlyphIndices count of glyphs should = 5 not %d\n", charcount);
642 todo_wine ok(glyphs[0] == 0, "GetGlyphIndices for tmDefaultChar should be 0 not %04x\n", glyphs[0]);
643 ok(glyphs[4] == 0, "GetGlyphIndices 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, "GetGlyphIndicesA 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 /* PANOSE is 10 bytes in size, need to pack the structure properly */
1498 #include "pshpack2.h"
1499 typedef struct
1501 USHORT version;
1502 SHORT xAvgCharWidth;
1503 USHORT usWeightClass;
1504 USHORT usWidthClass;
1505 SHORT fsType;
1506 SHORT ySubscriptXSize;
1507 SHORT ySubscriptYSize;
1508 SHORT ySubscriptXOffset;
1509 SHORT ySubscriptYOffset;
1510 SHORT ySuperscriptXSize;
1511 SHORT ySuperscriptYSize;
1512 SHORT ySuperscriptXOffset;
1513 SHORT ySuperscriptYOffset;
1514 SHORT yStrikeoutSize;
1515 SHORT yStrikeoutPosition;
1516 SHORT sFamilyClass;
1517 PANOSE panose;
1518 ULONG ulUnicodeRange1;
1519 ULONG ulUnicodeRange2;
1520 ULONG ulUnicodeRange3;
1521 ULONG ulUnicodeRange4;
1522 CHAR achVendID[4];
1523 USHORT fsSelection;
1524 USHORT usFirstCharIndex;
1525 USHORT usLastCharIndex;
1526 /* According to the Apple spec, original version didn't have the below fields,
1527 * version numbers were taked from the OpenType spec.
1529 /* version 0 (TrueType 1.5) */
1530 USHORT sTypoAscender;
1531 USHORT sTypoDescender;
1532 USHORT sTypoLineGap;
1533 USHORT usWinAscent;
1534 USHORT usWinDescent;
1535 /* version 1 (TrueType 1.66) */
1536 ULONG ulCodePageRange1;
1537 ULONG ulCodePageRange2;
1538 /* version 2 (OpenType 1.2) */
1539 SHORT sxHeight;
1540 SHORT sCapHeight;
1541 USHORT usDefaultChar;
1542 USHORT usBreakChar;
1543 USHORT usMaxContext;
1544 } TT_OS2_V2;
1545 #include "poppack.h"
1547 #ifdef WORDS_BIGENDIAN
1548 #define GET_BE_WORD(x) (x)
1549 #else
1550 #define GET_BE_WORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
1551 #endif
1553 #define MS_MAKE_TAG(ch0, ch1, ch2, ch3) \
1554 ((DWORD)(BYTE)(ch0) | ((DWORD)(BYTE)(ch1) << 8) | \
1555 ((DWORD)(BYTE)(ch2) << 16) | ((DWORD)(BYTE)(ch3) << 24))
1556 #define MS_OS2_TAG MS_MAKE_TAG('O','S','/','2')
1558 static void test_text_metrics(const LOGFONTA *lf)
1560 HDC hdc;
1561 HFONT hfont, hfont_old;
1562 TEXTMETRICA tmA;
1563 TEXTMETRICW tmW;
1564 UINT first_unicode_char, last_unicode_char, default_char, break_char;
1565 INT test_char;
1566 TT_OS2_V2 tt_os2;
1567 USHORT version;
1568 LONG size, ret;
1569 const char *font_name = lf->lfFaceName;
1571 trace("Testing font metrics for %s, charset %d\n", font_name, lf->lfCharSet);
1573 hdc = GetDC(0);
1575 SetLastError(0xdeadbeef);
1576 hfont = CreateFontIndirectA(lf);
1577 ok(hfont != 0, "CreateFontIndirect error %u\n", GetLastError());
1579 hfont_old = SelectObject(hdc, hfont);
1581 if(lf->lfWidth > 0) {
1582 HFONT hfont2, hfont_prev;
1583 GLYPHMETRICS gm1, gm2;
1584 LOGFONTA lf2 = *lf;
1585 MAT2 mat2 = { {0,1}, {0,0}, {0,0}, {0,1} };
1587 /* negative widths are handled just as positive ones */
1588 lf2.lfWidth *= -1;
1590 SetLastError(0xdeadbeef);
1591 hfont2 = CreateFontIndirectA(&lf2);
1592 ok(hfont2 != 0, "CreateFontIndirect error %u\n", GetLastError());
1593 hfont_prev = SelectObject(hdc, hfont2);
1595 /* filling with 0xaa causes false pass under WINEDEBUG=warn+heap */
1596 memset(&gm1, 0xab, sizeof(gm1));
1597 SetLastError(0xdeadbeef);
1598 ret = GetGlyphOutlineA(hdc, 'x', GGO_METRICS, &gm1, 0, NULL, &mat2);
1599 ok(ret != GDI_ERROR, "GetGlyphOutline error 0x%x\n", GetLastError());
1601 SelectObject(hdc, hfont_prev);
1602 DeleteObject(hfont2);
1604 memset(&gm2, 0xbb, sizeof(gm2));
1605 SetLastError(0xdeadbeef);
1606 ret = GetGlyphOutlineA(hdc, 'x', GGO_METRICS, &gm2, 0, NULL, &mat2);
1607 ok(ret != GDI_ERROR, "GetGlyphOutline error 0x%x\n", GetLastError());
1609 ok(gm1.gmBlackBoxX == gm2.gmBlackBoxX &&
1610 gm1.gmBlackBoxY == gm2.gmBlackBoxY &&
1611 gm1.gmptGlyphOrigin.x == gm2.gmptGlyphOrigin.x &&
1612 gm1.gmptGlyphOrigin.y == gm2.gmptGlyphOrigin.y &&
1613 gm1.gmCellIncX == gm2.gmCellIncX &&
1614 gm1.gmCellIncY == gm2.gmCellIncY,
1615 "gm1=%d,%d,%d,%d,%d,%d gm2=%d,%d,%d,%d,%d,%d\n",
1616 gm1.gmBlackBoxX, gm1.gmBlackBoxY, gm1.gmptGlyphOrigin.x,
1617 gm1.gmptGlyphOrigin.y, gm1.gmCellIncX, gm1.gmCellIncY,
1618 gm2.gmBlackBoxX, gm2.gmBlackBoxY, gm2.gmptGlyphOrigin.x,
1619 gm2.gmptGlyphOrigin.y, gm2.gmCellIncX, gm2.gmCellIncY);
1622 size = GetFontData(hdc, MS_OS2_TAG, 0, NULL, 0);
1623 if (size == GDI_ERROR)
1625 trace("OS/2 chunk was not found\n");
1626 goto end_of_test;
1628 if (size > sizeof(tt_os2))
1630 trace("got too large OS/2 chunk of size %u\n", size);
1631 size = sizeof(tt_os2);
1634 memset(&tt_os2, 0, sizeof(tt_os2));
1635 ret = GetFontData(hdc, MS_OS2_TAG, 0, &tt_os2, size);
1636 ok(ret == size, "GetFontData should return %u not %u\n", size, ret);
1638 version = GET_BE_WORD(tt_os2.version);
1639 trace("OS/2 chunk version %u, vendor %4.4s\n", version, (LPCSTR)&tt_os2.achVendID);
1641 first_unicode_char = GET_BE_WORD(tt_os2.usFirstCharIndex);
1642 last_unicode_char = GET_BE_WORD(tt_os2.usLastCharIndex);
1643 default_char = GET_BE_WORD(tt_os2.usDefaultChar);
1644 break_char = GET_BE_WORD(tt_os2.usBreakChar);
1646 trace("for %s first %x, last %x, default %x, break %x\n", font_name,
1647 first_unicode_char, last_unicode_char, default_char, break_char);
1649 SetLastError(0xdeadbeef);
1650 ret = GetTextMetricsA(hdc, &tmA);
1651 ok(ret, "GetTextMetricsA error %u\n", GetLastError());
1652 trace("A: first %x, last %x, default %x, break %x\n",
1653 tmA.tmFirstChar, tmA.tmLastChar, tmA.tmDefaultChar, tmA.tmBreakChar);
1655 #if 0 /* FIXME: This doesn't appear to be what Windows does */
1656 test_char = min(first_unicode_char - 1, 255);
1657 ok(tmA.tmFirstChar == test_char, "A: tmFirstChar for %s %02x != %02x\n",
1658 font_name, tmA.tmFirstChar, test_char);
1659 #endif
1660 if (lf->lfCharSet == SYMBOL_CHARSET)
1662 test_char = min(last_unicode_char - 0xf000, 255);
1663 ok(tmA.tmLastChar == test_char, "A: tmLastChar for %s %02x != %02x\n",
1664 font_name, tmA.tmLastChar, test_char);
1666 else
1668 test_char = min(last_unicode_char, 255);
1669 ok(tmA.tmLastChar == test_char, "A: tmLastChar for %s %02x != %02x\n",
1670 font_name, tmA.tmLastChar, test_char);
1673 SetLastError(0xdeadbeef);
1674 ret = GetTextMetricsW(hdc, &tmW);
1675 ok(ret || GetLastError() == ERROR_CALL_NOT_IMPLEMENTED,
1676 "GetTextMetricsW error %u\n", GetLastError());
1677 if (ret)
1679 trace("W: first %x, last %x, default %x, break %x\n",
1680 tmW.tmFirstChar, tmW.tmLastChar, tmW.tmDefaultChar,
1681 tmW.tmBreakChar);
1683 if (lf->lfCharSet == SYMBOL_CHARSET)
1685 /* It appears that for fonts with SYMBOL_CHARSET Windows always
1686 * sets symbol range to 0 - f0ff
1688 ok(tmW.tmFirstChar == 0, "W: tmFirstChar for %s %02x != 0\n",
1689 font_name, tmW.tmFirstChar);
1690 /* FIXME: Windows returns f0ff here, while Wine f0xx */
1691 ok(tmW.tmLastChar >= 0xf000, "W: tmLastChar for %s %02x < 0xf000\n",
1692 font_name, tmW.tmLastChar);
1694 ok(tmW.tmDefaultChar == 0x1f, "W: tmDefaultChar for %s %02x != 0x1f\n",
1695 font_name, tmW.tmDefaultChar);
1696 ok(tmW.tmBreakChar == 0x20, "W: tmBreakChar for %s %02x != 0x20\n",
1697 font_name, tmW.tmBreakChar);
1699 else
1701 ok(tmW.tmFirstChar == first_unicode_char, "W: tmFirstChar for %s %02x != %02x\n",
1702 font_name, tmW.tmFirstChar, first_unicode_char);
1703 ok(tmW.tmLastChar == last_unicode_char, "W: tmLastChar for %s %02x != %02x\n",
1704 font_name, tmW.tmLastChar, last_unicode_char);
1706 ret = GetDeviceCaps(hdc, LOGPIXELSX);
1707 ok(tmW.tmDigitizedAspectX == ret, "W: tmDigitizedAspectX %u != %u\n",
1708 tmW.tmDigitizedAspectX, ret);
1709 ret = GetDeviceCaps(hdc, LOGPIXELSY);
1710 ok(tmW.tmDigitizedAspectX == ret, "W: tmDigitizedAspectY %u != %u\n",
1711 tmW.tmDigitizedAspectX, ret);
1714 end_of_test:
1715 SelectObject(hdc, hfont_old);
1716 DeleteObject(hfont);
1718 ReleaseDC(0, hdc);
1721 static INT CALLBACK enum_truetype_font_proc(const LOGFONT *lf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
1723 INT *enumed = (INT *)lParam;
1725 if (type == TRUETYPE_FONTTYPE)
1727 (*enumed)++;
1728 test_text_metrics(lf);
1730 return 1;
1733 static void test_GetTextMetrics(void)
1735 LOGFONTA lf;
1736 HDC hdc;
1737 INT enumed;
1739 hdc = GetDC(0);
1741 memset(&lf, 0, sizeof(lf));
1742 lf.lfCharSet = DEFAULT_CHARSET;
1743 enumed = 0;
1744 EnumFontFamiliesExA(hdc, &lf, enum_truetype_font_proc, (LPARAM)&enumed, 0);
1745 trace("Tested metrics of %d truetype fonts\n", enumed);
1747 ReleaseDC(0, hdc);
1750 static void test_nonexistent_font(void)
1752 LOGFONTA lf;
1753 HDC hdc;
1754 HFONT hfont;
1755 char buf[LF_FACESIZE];
1757 if (!is_truetype_font_installed("Arial Black"))
1759 skip("Arial not installed\n");
1760 return;
1763 hdc = GetDC(0);
1765 memset(&lf, 0, sizeof(lf));
1766 lf.lfHeight = 100;
1767 lf.lfWeight = FW_REGULAR;
1768 lf.lfCharSet = ANSI_CHARSET;
1769 lf.lfPitchAndFamily = FF_SWISS;
1770 strcpy(lf.lfFaceName, "Nonexistent font");
1772 hfont = CreateFontIndirectA(&lf);
1773 hfont = SelectObject(hdc, hfont);
1774 GetTextFaceA(hdc, sizeof(buf), buf);
1775 ok(!lstrcmpiA(buf, "Arial"), "Got %s\n", buf);
1776 DeleteObject(SelectObject(hdc, hfont));
1777 ReleaseDC(0, hdc);
1780 static void test_GdiRealizationInfo(void)
1782 HDC hdc;
1783 DWORD info[4];
1784 BOOL r;
1785 HFONT hfont, hfont_old;
1786 LOGFONTA lf;
1788 if(!pGdiRealizationInfo)
1790 skip("GdiRealizationInfo not available\n");
1791 return;
1794 hdc = GetDC(0);
1796 memset(info, 0xcc, sizeof(info));
1797 r = pGdiRealizationInfo(hdc, info);
1798 ok(r != 0, "ret 0\n");
1799 ok(info[0] == 1, "info[0] = %x for the system font\n", info[0]);
1800 ok(info[3] == 0xcccccccc, "structure longer than 3 dwords\n");
1802 if (!is_truetype_font_installed("Arial"))
1804 skip("skipping GdiRealizationInfo with truetype font\n");
1805 goto end;
1808 memset(&lf, 0, sizeof(lf));
1809 strcpy(lf.lfFaceName, "Arial");
1810 lf.lfHeight = 20;
1811 lf.lfWeight = FW_NORMAL;
1812 hfont = CreateFontIndirectA(&lf);
1813 hfont_old = SelectObject(hdc, hfont);
1815 memset(info, 0xcc, sizeof(info));
1816 r = pGdiRealizationInfo(hdc, info);
1817 ok(r != 0, "ret 0\n");
1818 ok(info[0] == 3, "info[0] = %x for arial\n", info[0]);
1819 ok(info[3] == 0xcccccccc, "structure longer than 3 dwords\n");
1821 DeleteObject(SelectObject(hdc, hfont_old));
1823 end:
1824 ReleaseDC(0, hdc);
1827 START_TEST(font)
1829 init();
1831 test_logfont();
1832 test_bitmap_font();
1833 test_bitmap_font_metrics();
1834 test_GdiGetCharDimensions();
1835 test_GetCharABCWidths();
1836 test_text_extents();
1837 test_GetGlyphIndices();
1838 test_GetKerningPairs();
1839 test_GetOutlineTextMetrics();
1840 test_SetTextJustification();
1841 test_font_charset();
1842 test_GetFontUnicodeRanges();
1843 test_nonexistent_font();
1845 /* On Windows Arial has a lot of default charset aliases such as Arial Cyr,
1846 * I'd like to avoid them in this test.
1848 test_EnumFontFamilies("Arial Black", ANSI_CHARSET);
1849 test_EnumFontFamilies("Symbol", SYMBOL_CHARSET);
1850 if (is_truetype_font_installed("Arial Black") &&
1851 (is_truetype_font_installed("Symbol") || is_truetype_font_installed("Wingdings")))
1853 test_EnumFontFamilies("", ANSI_CHARSET);
1854 test_EnumFontFamilies("", SYMBOL_CHARSET);
1855 test_EnumFontFamilies("", DEFAULT_CHARSET);
1857 else
1858 skip("Arial Black or Symbol/Wingdings is not installed\n");
1859 test_GetTextMetrics();
1860 test_GdiRealizationInfo();