push b59ba84f7e04af9ef068bd4c6e96701941f0256e
[wine/hacks.git] / dlls / gdi32 / tests / font.c
blob7e560d28a9cf0bbe3734d8b4cf9c401c8008e008
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);
42 static HMODULE hgdi32 = 0;
44 static void init(void)
46 hgdi32 = GetModuleHandleA("gdi32.dll");
48 pGdiGetCharDimensions = (void *)GetProcAddress(hgdi32, "GdiGetCharDimensions");
49 pGetCharABCWidthsI = (void *)GetProcAddress(hgdi32, "GetCharABCWidthsI");
50 pGetCharABCWidthsW = (void *)GetProcAddress(hgdi32, "GetCharABCWidthsW");
51 pGetFontUnicodeRanges = (void *)GetProcAddress(hgdi32, "GetFontUnicodeRanges");
52 pGetGlyphIndicesA = (void *)GetProcAddress(hgdi32, "GetGlyphIndicesA");
53 pGetGlyphIndicesW = (void *)GetProcAddress(hgdi32, "GetGlyphIndicesW");
56 static INT CALLBACK is_truetype_font_installed_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
58 if (type != TRUETYPE_FONTTYPE) return 1;
60 return 0;
63 static BOOL is_truetype_font_installed(const char *name)
65 HDC hdc = GetDC(0);
66 BOOL ret = FALSE;
68 if (!EnumFontFamiliesA(hdc, name, is_truetype_font_installed_proc, 0))
69 ret = TRUE;
71 ReleaseDC(0, hdc);
72 return ret;
75 static INT CALLBACK is_font_installed_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
77 return 0;
80 static BOOL is_font_installed(const char *name)
82 HDC hdc = GetDC(0);
83 BOOL ret = FALSE;
85 if(!EnumFontFamiliesA(hdc, name, is_font_installed_proc, 0))
86 ret = TRUE;
88 ReleaseDC(0, hdc);
89 return ret;
92 static void check_font(const char* test, const LOGFONTA* lf, HFONT hfont)
94 LOGFONTA getobj_lf;
95 int ret, minlen = 0;
97 if (!hfont)
98 return;
100 ret = GetObject(hfont, sizeof(getobj_lf), &getobj_lf);
101 /* NT4 tries to be clever and only returns the minimum length */
102 while (lf->lfFaceName[minlen] && minlen < LF_FACESIZE-1)
103 minlen++;
104 minlen += FIELD_OFFSET(LOGFONTA, lfFaceName) + 1;
105 ok(ret == sizeof(LOGFONTA) || ret == minlen, "%s: GetObject returned %d\n", test, ret);
106 ok(!memcmp(&lf, &lf, FIELD_OFFSET(LOGFONTA, lfFaceName)), "%s: fonts don't match\n", test);
107 ok(!lstrcmpA(lf->lfFaceName, getobj_lf.lfFaceName),
108 "%s: font names don't match: %s != %s\n", test, lf->lfFaceName, getobj_lf.lfFaceName);
111 static HFONT create_font(const char* test, const LOGFONTA* lf)
113 HFONT hfont = CreateFontIndirectA(lf);
114 ok(hfont != 0, "%s: CreateFontIndirect failed\n", test);
115 if (hfont)
116 check_font(test, lf, hfont);
117 return hfont;
120 static void test_logfont(void)
122 LOGFONTA lf;
123 HFONT hfont;
125 memset(&lf, 0, sizeof lf);
127 lf.lfCharSet = ANSI_CHARSET;
128 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
129 lf.lfWeight = FW_DONTCARE;
130 lf.lfHeight = 16;
131 lf.lfWidth = 16;
132 lf.lfQuality = DEFAULT_QUALITY;
134 lstrcpyA(lf.lfFaceName, "Arial");
135 hfont = create_font("Arial", &lf);
136 DeleteObject(hfont);
138 memset(&lf, 'A', sizeof(lf));
139 hfont = CreateFontIndirectA(&lf);
140 ok(hfont != 0, "CreateFontIndirectA with strange LOGFONT failed\n");
142 lf.lfFaceName[LF_FACESIZE - 1] = 0;
143 check_font("AAA...", &lf, hfont);
144 DeleteObject(hfont);
147 static INT CALLBACK font_enum_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
149 if (type & RASTER_FONTTYPE)
151 LOGFONT *lf = (LOGFONT *)lParam;
152 *lf = *elf;
153 return 0; /* stop enumeration */
156 return 1; /* continue enumeration */
159 static void test_font_metrics(HDC hdc, HFONT hfont, LONG lfHeight, const char *test_str,
160 INT test_str_len, const TEXTMETRICA *tm_orig,
161 const SIZE *size_orig, INT width_of_A_orig,
162 INT scale_x, INT scale_y)
164 HFONT old_hfont;
165 LOGFONTA lf;
166 TEXTMETRICA tm;
167 SIZE size;
168 INT width_of_A, cx, cy;
170 if (!hfont)
171 return;
173 GetObjectA(hfont, sizeof(lf), &lf);
175 old_hfont = SelectObject(hdc, hfont);
177 GetTextMetricsA(hdc, &tm);
179 cx = tm.tmAveCharWidth / tm_orig->tmAveCharWidth;
180 cy = tm.tmHeight / tm_orig->tmHeight;
181 ok(cx == scale_x && cy == scale_y, "expected scale_x %d, scale_y %d, got cx %d, cy %d\n",
182 scale_x, scale_y, cx, cy);
183 ok(tm.tmHeight == tm_orig->tmHeight * scale_y, "%d != %d\n", tm.tmHeight, tm_orig->tmHeight * scale_y);
184 ok(tm.tmAscent == tm_orig->tmAscent * scale_y, "%d != %d\n", tm.tmAscent, tm_orig->tmAscent * scale_y);
185 ok(tm.tmDescent == tm_orig->tmDescent * scale_y, "%d != %d\n", tm.tmDescent, tm_orig->tmDescent * scale_y);
186 ok(tm.tmAveCharWidth == tm_orig->tmAveCharWidth * scale_x, "%d != %d\n", tm.tmAveCharWidth, tm_orig->tmAveCharWidth * scale_x);
187 ok(tm.tmMaxCharWidth == tm_orig->tmMaxCharWidth * scale_x, "%d != %d\n", tm.tmAveCharWidth, tm_orig->tmMaxCharWidth * scale_x);
189 ok(lf.lfHeight == lfHeight, "lf %d != %d\n", lf.lfHeight, lfHeight);
190 if (lf.lfWidth)
191 ok(lf.lfWidth == tm.tmAveCharWidth, "lf %d != tm %d\n", lf.lfWidth, tm.tmAveCharWidth);
193 GetTextExtentPoint32A(hdc, test_str, test_str_len, &size);
195 ok(size.cx == size_orig->cx * scale_x, "%d != %d\n", size.cx, size_orig->cx * scale_x);
196 ok(size.cy == size_orig->cy * scale_y, "%d != %d\n", size.cy, size_orig->cy * scale_y);
198 GetCharWidthA(hdc, 'A', 'A', &width_of_A);
200 ok(width_of_A == width_of_A_orig * scale_x, "%d != %d\n", width_of_A, width_of_A_orig * scale_x);
202 SelectObject(hdc, old_hfont);
205 /* Test how GDI scales bitmap font metrics */
206 static void test_bitmap_font(void)
208 static const char test_str[11] = "Test String";
209 HDC hdc;
210 LOGFONTA bitmap_lf;
211 HFONT hfont, old_hfont;
212 TEXTMETRICA tm_orig;
213 SIZE size_orig;
214 INT ret, i, width_orig, height_orig, scale;
216 hdc = GetDC(0);
218 /* "System" has only 1 pixel size defined, otherwise the test breaks */
219 ret = EnumFontFamiliesA(hdc, "System", font_enum_proc, (LPARAM)&bitmap_lf);
220 if (ret)
222 ReleaseDC(0, hdc);
223 trace("no bitmap fonts were found, skipping the test\n");
224 return;
227 trace("found bitmap font %s, height %d\n", bitmap_lf.lfFaceName, bitmap_lf.lfHeight);
229 height_orig = bitmap_lf.lfHeight;
230 hfont = create_font("bitmap", &bitmap_lf);
232 old_hfont = SelectObject(hdc, hfont);
233 ok(GetTextMetricsA(hdc, &tm_orig), "GetTextMetricsA failed\n");
234 ok(GetTextExtentPoint32A(hdc, test_str, sizeof(test_str), &size_orig), "GetTextExtentPoint32A failed\n");
235 ok(GetCharWidthA(hdc, 'A', 'A', &width_orig), "GetCharWidthA failed\n");
236 SelectObject(hdc, old_hfont);
237 DeleteObject(hfont);
239 /* test fractional scaling */
240 for (i = 1; i <= height_orig * 3; i++)
242 INT nearest_height;
244 bitmap_lf.lfHeight = i;
245 hfont = create_font("fractional", &bitmap_lf);
246 scale = (i + height_orig - 1) / height_orig;
247 nearest_height = scale * height_orig;
248 /* XP allows not more than 10% deviation */
249 if (scale > 1 && nearest_height - i > nearest_height / 10) scale--;
250 test_font_metrics(hdc, hfont, bitmap_lf.lfHeight, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 1, scale);
251 DeleteObject(hfont);
254 /* test integer scaling 3x2 */
255 bitmap_lf.lfHeight = height_orig * 2;
256 bitmap_lf.lfWidth *= 3;
257 hfont = create_font("3x2", &bitmap_lf);
258 test_font_metrics(hdc, hfont, bitmap_lf.lfHeight, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 3, 2);
259 DeleteObject(hfont);
261 /* test integer scaling 3x3 */
262 bitmap_lf.lfHeight = height_orig * 3;
263 bitmap_lf.lfWidth = 0;
264 hfont = create_font("3x3", &bitmap_lf);
265 test_font_metrics(hdc, hfont, bitmap_lf.lfHeight, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 3, 3);
266 DeleteObject(hfont);
268 ReleaseDC(0, hdc);
271 static INT CALLBACK find_font_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
273 LOGFONT *lf = (LOGFONT *)lParam;
275 if (elf->lfHeight == lf->lfHeight && !strcmp(elf->lfFaceName, lf->lfFaceName))
277 *lf = *elf;
278 return 0; /* stop enumeration */
280 return 1; /* continue enumeration */
283 static void test_bitmap_font_metrics(void)
285 static const struct font_data
287 const char face_name[LF_FACESIZE];
288 int weight, height, ascent, descent, int_leading, ext_leading;
289 int ave_char_width, max_char_width;
290 DWORD ansi_bitfield;
291 } fd[] =
293 { "MS Sans Serif", FW_NORMAL, 13, 11, 2, 2, 0, 5, 11, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
294 { "MS Sans Serif", FW_NORMAL, 16, 13, 3, 3, 0, 7, 14, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
295 { "MS Sans Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 16, FS_LATIN1 | FS_CYRILLIC },
296 { "MS Sans Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 18, FS_LATIN2 },
297 { "MS Sans Serif", FW_NORMAL, 24, 19, 5, 6, 0, 9, 19, FS_LATIN1 },
298 { "MS Sans Serif", FW_NORMAL, 24, 19, 5, 6, 0, 9, 24, FS_LATIN2 },
299 { "MS Sans Serif", FW_NORMAL, 24, 19, 5, 6, 0, 9, 20, FS_CYRILLIC },
300 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 5, 0, 12, 24, FS_LATIN1 },
301 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 6, 0, 12, 24, FS_LATIN2 },
302 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 5, 0, 12, 25, FS_CYRILLIC },
303 { "MS Sans Serif", FW_NORMAL, 37, 29, 8, 5, 0, 16, 32, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
304 { "MS Serif", FW_NORMAL, 10, 8, 2, 2, 0, 4, 8, FS_LATIN1 | FS_LATIN2 },
305 { "MS Serif", FW_NORMAL, 10, 8, 2, 2, 0, 5, 8, FS_CYRILLIC },
306 { "MS Serif", FW_NORMAL, 11, 9, 2, 2, 0, 5, 9, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
307 { "MS Serif", FW_NORMAL, 13, 11, 2, 2, 0, 5, 11, FS_LATIN1 },
308 { "MS Serif", FW_NORMAL, 13, 11, 2, 2, 0, 5, 12, FS_LATIN2 | FS_CYRILLIC },
309 { "MS Serif", FW_NORMAL, 16, 13, 3, 3, 0, 6, 14, FS_LATIN1 | FS_LATIN2 },
310 { "MS Serif", FW_NORMAL, 16, 13, 3, 3, 0, 6, 16, FS_CYRILLIC },
311 { "MS Serif", FW_NORMAL, 19, 15, 4, 3, 0, 8, 18, FS_LATIN1 | FS_LATIN2 },
312 { "MS Serif", FW_NORMAL, 19, 15, 4, 3, 0, 8, 19, FS_CYRILLIC },
313 { "MS Serif", FW_NORMAL, 21, 16, 5, 3, 0, 9, 17, FS_LATIN1 },
314 { "MS Serif", FW_NORMAL, 21, 16, 5, 3, 0, 9, 22, FS_LATIN2 },
315 { "MS Serif", FW_NORMAL, 21, 16, 5, 3, 0, 9, 23, FS_CYRILLIC },
316 { "MS Serif", FW_NORMAL, 27, 21, 6, 3, 0, 12, 23, FS_LATIN1 },
317 { "MS Serif", FW_NORMAL, 27, 21, 6, 3, 0, 12, 26, FS_LATIN2 },
318 { "MS Serif", FW_NORMAL, 27, 21, 6, 3, 0, 12, 27, FS_CYRILLIC },
319 { "MS Serif", FW_NORMAL, 35, 27, 8, 3, 0, 16, 33, FS_LATIN1 | FS_LATIN2 },
320 { "MS Serif", FW_NORMAL, 35, 27, 8, 3, 0, 16, 34, FS_CYRILLIC },
321 { "Courier", FW_NORMAL, 13, 11, 2, 0, 0, 8, 8, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
322 { "Courier", FW_NORMAL, 16, 13, 3, 0, 0, 9, 9, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
323 { "Courier", FW_NORMAL, 20, 16, 4, 0, 0, 12, 12, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
324 { "System", FW_BOLD, 16, 13, 3, 3, 0, 7, 14, FS_LATIN1 },
325 { "System", FW_BOLD, 16, 13, 3, 3, 0, 7, 15, FS_LATIN2 | FS_CYRILLIC },
327 * TODO: the system for CP932 should be NORMAL, not BOLD. However that would
328 * require a new system.sfd for that font
330 { "System", FW_BOLD, 18, 16, 2, 0, 2, 8, 16, FS_JISJAPAN },
331 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 1, 2, FS_LATIN1 },
332 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 1, 8, FS_LATIN2 | FS_CYRILLIC },
333 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 2, 4, FS_JISJAPAN },
334 { "Small Fonts", FW_NORMAL, 5, 4, 1, 1, 0, 3, 4, FS_LATIN1 },
335 { "Small Fonts", FW_NORMAL, 5, 4, 1, 1, 0, 2, 8, FS_LATIN2 | FS_CYRILLIC },
336 { "Small Fonts", FW_NORMAL, 5, 4, 1, 0, 0, 3, 6, FS_JISJAPAN },
337 { "Small Fonts", FW_NORMAL, 6, 5, 1, 1, 0, 3, 13, FS_LATIN1 },
338 { "Small Fonts", FW_NORMAL, 6, 5, 1, 1, 0, 3, 8, FS_LATIN2 | FS_CYRILLIC },
339 { "Small Fonts", FW_NORMAL, 6, 5, 1, 0, 0, 4, 8, FS_JISJAPAN },
340 { "Small Fonts", FW_NORMAL, 8, 7, 1, 1, 0, 4, 7, FS_LATIN1 },
341 { "Small Fonts", FW_NORMAL, 8, 7, 1, 1, 0, 4, 8, FS_LATIN2 | FS_CYRILLIC },
342 { "Small Fonts", FW_NORMAL, 8, 7, 1, 0, 0, 5, 10, FS_JISJAPAN },
343 { "Small Fonts", FW_NORMAL, 10, 8, 2, 2, 0, 4, 8, FS_LATIN1 | FS_LATIN2 },
344 { "Small Fonts", FW_NORMAL, 10, 8, 2, 2, 0, 5, 8, FS_CYRILLIC },
345 { "Small Fonts", FW_NORMAL, 10, 8, 2, 0, 0, 6, 12, FS_JISJAPAN },
346 { "Small Fonts", FW_NORMAL, 11, 9, 2, 2, 0, 5, 9, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
347 { "Small Fonts", FW_NORMAL, 11, 9, 2, 0, 0, 7, 14, FS_JISJAPAN },
348 { "Fixedsys", FW_NORMAL, 15, 12, 3, 3, 0, 8, 8, FS_LATIN1 | FS_LATIN2 },
349 { "Fixedsys", FW_NORMAL, 16, 12, 4, 3, 0, 8, 8, FS_CYRILLIC },
350 { "FixedSys", FW_NORMAL, 18, 16, 2, 0, 0, 8, 16, FS_JISJAPAN }
352 /* FIXME: add "Terminal" */
354 HDC hdc;
355 LOGFONT lf;
356 HFONT hfont, old_hfont;
357 TEXTMETRIC tm;
358 INT ret, i;
360 hdc = CreateCompatibleDC(0);
361 assert(hdc);
363 for (i = 0; i < sizeof(fd)/sizeof(fd[0]); i++)
365 int bit;
367 memset(&lf, 0, sizeof(lf));
369 lf.lfHeight = fd[i].height;
370 strcpy(lf.lfFaceName, fd[i].face_name);
372 for(bit = 0; bit < 32; bit++)
374 DWORD fs[2];
375 CHARSETINFO csi;
377 fs[0] = 1L << bit;
378 fs[1] = 0;
379 if((fd[i].ansi_bitfield & fs[0]) == 0) continue;
380 if(!TranslateCharsetInfo( fs, &csi, TCI_SRCFONTSIG )) continue;
382 lf.lfCharSet = csi.ciCharset;
383 ret = EnumFontFamiliesEx(hdc, &lf, find_font_proc, (LPARAM)&lf, 0);
384 if (ret) continue;
386 trace("found font %s, height %d charset %x\n", lf.lfFaceName, lf.lfHeight, lf.lfCharSet);
388 hfont = create_font(lf.lfFaceName, &lf);
389 old_hfont = SelectObject(hdc, hfont);
390 ok(GetTextMetrics(hdc, &tm), "GetTextMetrics error %d\n", GetLastError());
392 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);
393 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);
394 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);
395 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);
396 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);
397 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);
398 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);
400 /* Don't run the max char width test on System/ANSI_CHARSET. We have extra characters in our font
401 that make the max width bigger */
402 if(strcmp(lf.lfFaceName, "System") || lf.lfCharSet != ANSI_CHARSET)
403 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);
405 SelectObject(hdc, old_hfont);
406 DeleteObject(hfont);
410 DeleteDC(hdc);
413 static void test_GdiGetCharDimensions(void)
415 HDC hdc;
416 TEXTMETRICW tm;
417 LONG ret;
418 SIZE size;
419 LONG avgwidth, height;
420 static const char szAlphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
422 if (!pGdiGetCharDimensions)
424 skip("GdiGetCharDimensions not available on this platform\n");
425 return;
428 hdc = CreateCompatibleDC(NULL);
430 GetTextExtentPoint(hdc, szAlphabet, strlen(szAlphabet), &size);
431 avgwidth = ((size.cx / 26) + 1) / 2;
433 ret = pGdiGetCharDimensions(hdc, &tm, &height);
434 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
435 ok(height == tm.tmHeight, "GdiGetCharDimensions should have set height to %d instead of %d\n", tm.tmHeight, height);
437 ret = pGdiGetCharDimensions(hdc, &tm, NULL);
438 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
440 ret = pGdiGetCharDimensions(hdc, NULL, NULL);
441 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
443 height = 0;
444 ret = pGdiGetCharDimensions(hdc, NULL, &height);
445 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
446 ok(height == size.cy, "GdiGetCharDimensions should have set height to %d instead of %d\n", size.cy, height);
448 DeleteDC(hdc);
451 static void test_GetCharABCWidths(void)
453 static const WCHAR str[] = {'a',0};
454 BOOL ret;
455 HDC hdc;
456 LOGFONTA lf;
457 HFONT hfont;
458 ABC abc[1];
459 WORD glyphs[1];
460 DWORD nb;
462 if (!pGetCharABCWidthsW || !pGetCharABCWidthsI)
464 skip("GetCharABCWidthsW/I not available on this platform\n");
465 return;
468 memset(&lf, 0, sizeof(lf));
469 strcpy(lf.lfFaceName, "System");
470 lf.lfHeight = 20;
472 hfont = CreateFontIndirectA(&lf);
473 hdc = GetDC(0);
474 hfont = SelectObject(hdc, hfont);
476 nb = pGetGlyphIndicesW(hdc, str, 1, glyphs, 0);
477 ok(nb == 1, "pGetGlyphIndicesW should have returned 1\n");
479 ret = pGetCharABCWidthsI(NULL, 0, 1, glyphs, abc);
480 ok(!ret, "GetCharABCWidthsI should have failed\n");
482 ret = pGetCharABCWidthsI(hdc, 0, 1, glyphs, NULL);
483 ok(!ret, "GetCharABCWidthsI should have failed\n");
485 ret = pGetCharABCWidthsI(hdc, 0, 1, glyphs, abc);
486 ok(ret, "GetCharABCWidthsI should have succeeded\n");
488 ret = pGetCharABCWidthsW(NULL, 'a', 'a', abc);
489 ok(!ret, "GetCharABCWidthsW should have failed\n");
491 ret = pGetCharABCWidthsW(hdc, 'a', 'a', NULL);
492 ok(!ret, "GetCharABCWidthsW should have failed\n");
494 ret = pGetCharABCWidthsW(hdc, 'a', 'a', abc);
495 ok(!ret, "GetCharABCWidthsW should have failed\n");
497 hfont = SelectObject(hdc, hfont);
498 DeleteObject(hfont);
499 ReleaseDC(NULL, hdc);
502 static void test_text_extents(void)
504 static const WCHAR wt[] = {'O','n','e','\n','t','w','o',' ','3',0};
505 LPINT extents;
506 INT i, len, fit1, fit2;
507 LOGFONTA lf;
508 TEXTMETRICA tm;
509 HDC hdc;
510 HFONT hfont;
511 SIZE sz;
512 SIZE sz1, sz2;
514 memset(&lf, 0, sizeof(lf));
515 strcpy(lf.lfFaceName, "Arial");
516 lf.lfHeight = 20;
518 hfont = CreateFontIndirectA(&lf);
519 hdc = GetDC(0);
520 hfont = SelectObject(hdc, hfont);
521 GetTextMetricsA(hdc, &tm);
522 GetTextExtentPointA(hdc, "o", 1, &sz);
523 ok(sz.cy == tm.tmHeight, "cy %d tmHeight %d\n", sz.cy, tm.tmHeight);
525 SetLastError(0xdeadbeef);
526 GetTextExtentExPointW(hdc, wt, 1, 1, &fit1, &fit2, &sz1);
527 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
529 skip("Skipping remainder of text extents test on a Win9x platform\n");
530 hfont = SelectObject(hdc, hfont);
531 DeleteObject(hfont);
532 ReleaseDC(0, hdc);
533 return;
536 len = lstrlenW(wt);
537 extents = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof extents[0]);
538 extents[0] = 1; /* So that the increasing sequence test will fail
539 if the extents array is untouched. */
540 GetTextExtentExPointW(hdc, wt, len, 32767, &fit1, extents, &sz1);
541 GetTextExtentPointW(hdc, wt, len, &sz2);
542 ok(sz1.cy == sz2.cy,
543 "cy from GetTextExtentExPointW (%d) and GetTextExtentPointW (%d) differ\n", sz1.cy, sz2.cy);
544 /* Because of the '\n' in the string GetTextExtentExPoint and
545 GetTextExtentPoint return different widths under Win2k, but
546 under WinXP they return the same width. So we don't test that
547 here. */
549 for (i = 1; i < len; ++i)
550 ok(extents[i-1] <= extents[i],
551 "GetTextExtentExPointW generated a non-increasing sequence of partial extents (at position %d)\n",
553 ok(extents[len-1] == sz1.cx, "GetTextExtentExPointW extents and size don't match\n");
554 ok(0 <= fit1 && fit1 <= len, "GetTextExtentExPointW generated illegal value %d for fit\n", fit1);
555 ok(0 < fit1, "GetTextExtentExPointW says we can't even fit one letter in 32767 logical units\n");
556 GetTextExtentExPointW(hdc, wt, len, extents[2], &fit2, NULL, &sz2);
557 ok(sz1.cx == sz2.cx && sz1.cy == sz2.cy, "GetTextExtentExPointW returned different sizes for the same string\n");
558 ok(fit2 == 3, "GetTextExtentExPointW extents isn't consistent with fit\n");
559 GetTextExtentExPointW(hdc, wt, len, extents[2]-1, &fit2, NULL, &sz2);
560 ok(fit2 == 2, "GetTextExtentExPointW extents isn't consistent with fit\n");
561 GetTextExtentExPointW(hdc, wt, 2, 0, NULL, extents + 2, &sz2);
562 ok(extents[0] == extents[2] && extents[1] == extents[3],
563 "GetTextExtentExPointW with lpnFit == NULL returns incorrect results\n");
564 GetTextExtentExPointW(hdc, wt, 2, 0, NULL, NULL, &sz1);
565 ok(sz1.cx == sz2.cx && sz1.cy == sz2.cy,
566 "GetTextExtentExPointW with lpnFit and alpDx both NULL returns incorrect results\n");
567 HeapFree(GetProcessHeap(), 0, extents);
569 hfont = SelectObject(hdc, hfont);
570 DeleteObject(hfont);
571 ReleaseDC(NULL, hdc);
574 static void test_GetGlyphIndices(void)
576 HDC hdc;
577 HFONT hfont;
578 DWORD charcount;
579 LOGFONTA lf;
580 DWORD flags = 0;
581 WCHAR testtext[] = {'T','e','s','t',0xffff,0};
582 WORD glyphs[(sizeof(testtext)/2)-1];
583 TEXTMETRIC textm;
585 if (!pGetGlyphIndicesW) {
586 skip("GetGlyphIndices not available on platform\n");
587 return;
590 if(!is_font_installed("Symbol"))
592 skip("Symbol is not installed so skipping this test\n");
593 return;
596 memset(&lf, 0, sizeof(lf));
597 strcpy(lf.lfFaceName, "Symbol");
598 lf.lfHeight = 20;
600 hfont = CreateFontIndirectA(&lf);
601 hdc = GetDC(0);
603 ok(GetTextMetrics(hdc, &textm), "GetTextMetric failed\n");
604 flags |= GGI_MARK_NONEXISTING_GLYPHS;
605 charcount = pGetGlyphIndicesW(hdc, testtext, (sizeof(testtext)/2)-1, glyphs, flags);
606 ok(charcount == 5, "GetGlyphIndices count of glyphs should = 5 not %d\n", charcount);
607 ok((glyphs[4] == 0x001f || glyphs[4] == UNICODE_NOCHAR /* Vista */), "GetGlyphIndices should have returned a nonexistent char not %04x\n", glyphs[4]);
608 flags = 0;
609 charcount = pGetGlyphIndicesW(hdc, testtext, (sizeof(testtext)/2)-1, glyphs, flags);
610 ok(charcount == 5, "GetGlyphIndices count of glyphs should = 5 not %d\n", charcount);
611 ok(glyphs[4] == textm.tmDefaultChar, "GetGlyphIndices should have returned a %04x not %04x\n",
612 textm.tmDefaultChar, glyphs[4]);
615 static void test_GetKerningPairs(void)
617 static const struct kerning_data
619 const char face_name[LF_FACESIZE];
620 LONG height;
621 /* some interesting fields from OUTLINETEXTMETRIC */
622 LONG tmHeight, tmAscent, tmDescent;
623 UINT otmEMSquare;
624 INT otmAscent;
625 INT otmDescent;
626 UINT otmLineGap;
627 UINT otmsCapEmHeight;
628 UINT otmsXHeight;
629 INT otmMacAscent;
630 INT otmMacDescent;
631 UINT otmMacLineGap;
632 UINT otmusMinimumPPEM;
633 /* small subset of kerning pairs to test */
634 DWORD total_kern_pairs;
635 const KERNINGPAIR kern_pair[26];
636 } kd[] =
638 {"Arial", 12, 12, 9, 3,
639 2048, 7, -2, 1, 5, 2, 8, -2, 0, 9,
642 {' ','A',-1},{' ','T',0},{' ','Y',0},{'1','1',-1},
643 {'A',' ',-1},{'A','T',-1},{'A','V',-1},{'A','W',0},
644 {'A','Y',-1},{'A','v',0},{'A','w',0},{'A','y',0},
645 {'F',',',-1},{'F','.',-1},{'F','A',-1},{'L',' ',0},
646 {'L','T',-1},{'L','V',-1},{'L','W',-1},{'L','Y',-1},
647 {915,912,+1},{915,913,-1},{910,912,+1},{910,913,-1},
648 {933,970,+1},{933,972,-1}
651 {"Arial", -34, 39, 32, 7,
652 2048, 25, -7, 5, 17, 9, 31, -7, 1, 9,
655 {' ','A',-2},{' ','T',-1},{' ','Y',-1},{'1','1',-3},
656 {'A',' ',-2},{'A','T',-3},{'A','V',-3},{'A','W',-1},
657 {'A','Y',-3},{'A','v',-1},{'A','w',-1},{'A','y',-1},
658 {'F',',',-4},{'F','.',-4},{'F','A',-2},{'L',' ',-1},
659 {'L','T',-3},{'L','V',-3},{'L','W',-3},{'L','Y',-3},
660 {915,912,+3},{915,913,-3},{910,912,+3},{910,913,-3},
661 {933,970,+2},{933,972,-3}
664 { "Arial", 120, 120, 97, 23,
665 2048, 79, -23, 16, 54, 27, 98, -23, 4, 9,
668 {' ','A',-6},{' ','T',-2},{' ','Y',-2},{'1','1',-8},
669 {'A',' ',-6},{'A','T',-8},{'A','V',-8},{'A','W',-4},
670 {'A','Y',-8},{'A','v',-2},{'A','w',-2},{'A','y',-2},
671 {'F',',',-12},{'F','.',-12},{'F','A',-6},{'L',' ',-4},
672 {'L','T',-8},{'L','V',-8},{'L','W',-8},{'L','Y',-8},
673 {915,912,+9},{915,913,-10},{910,912,+9},{910,913,-8},
674 {933,970,+6},{933,972,-10}
677 #if 0 /* this set fails due to +1/-1 errors (rounding bug?), needs investigation. */
678 { "Arial", 1024 /* usually 1/2 of EM Square */, 1024, 830, 194,
679 2048, 668, -193, 137, 459, 229, 830, -194, 30, 9,
682 {' ','A',-51},{' ','T',-17},{' ','Y',-17},{'1','1',-68},
683 {'A',' ',-51},{'A','T',-68},{'A','V',-68},{'A','W',-34},
684 {'A','Y',-68},{'A','v',-17},{'A','w',-17},{'A','y',-17},
685 {'F',',',-102},{'F','.',-102},{'F','A',-51},{'L',' ',-34},
686 {'L','T',-68},{'L','V',-68},{'L','W',-68},{'L','Y',-68},
687 {915,912,+73},{915,913,-84},{910,912,+76},{910,913,-68},
688 {933,970,+54},{933,972,-83}
691 #endif
693 LOGFONT lf;
694 HFONT hfont, hfont_old;
695 KERNINGPAIR *kern_pair;
696 HDC hdc;
697 DWORD total_kern_pairs, ret, i, n, matches;
699 hdc = GetDC(0);
701 /* GetKerningPairsA maps unicode set of kerning pairs to current code page
702 * which may render this test unusable, so we're trying to avoid that.
704 SetLastError(0xdeadbeef);
705 GetKerningPairsW(hdc, 0, NULL);
706 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
708 skip("Skipping the GetKerningPairs test on a Win9x platform\n");
709 ReleaseDC(0, hdc);
710 return;
713 for (i = 0; i < sizeof(kd)/sizeof(kd[0]); i++)
715 OUTLINETEXTMETRICW otm;
717 if (!is_font_installed(kd[i].face_name))
719 trace("%s is not installed so skipping this test\n", kd[i].face_name);
720 continue;
723 trace("testing font %s, height %d\n", kd[i].face_name, kd[i].height);
725 memset(&lf, 0, sizeof(lf));
726 strcpy(lf.lfFaceName, kd[i].face_name);
727 lf.lfHeight = kd[i].height;
728 hfont = CreateFontIndirect(&lf);
729 assert(hfont != 0);
731 hfont_old = SelectObject(hdc, hfont);
733 SetLastError(0xdeadbeef);
734 otm.otmSize = sizeof(otm); /* just in case for Win9x compatibility */
735 ok(GetOutlineTextMetricsW(hdc, sizeof(otm), &otm) == sizeof(otm), "GetOutlineTextMetricsW error %d\n", GetLastError());
737 ok(kd[i].tmHeight == otm.otmTextMetrics.tmHeight, "expected %d, got %d\n",
738 kd[i].tmHeight, otm.otmTextMetrics.tmHeight);
739 ok(kd[i].tmAscent == otm.otmTextMetrics.tmAscent, "expected %d, got %d\n",
740 kd[i].tmAscent, otm.otmTextMetrics.tmAscent);
741 ok(kd[i].tmDescent == otm.otmTextMetrics.tmDescent, "expected %d, got %d\n",
742 kd[i].tmDescent, otm.otmTextMetrics.tmDescent);
744 ok(kd[i].otmEMSquare == otm.otmEMSquare, "expected %u, got %u\n",
745 kd[i].otmEMSquare, otm.otmEMSquare);
746 ok(kd[i].otmAscent == otm.otmAscent, "expected %d, got %d\n",
747 kd[i].otmAscent, otm.otmAscent);
748 ok(kd[i].otmDescent == otm.otmDescent, "expected %d, got %d\n",
749 kd[i].otmDescent, otm.otmDescent);
750 ok(kd[i].otmLineGap == otm.otmLineGap, "expected %u, got %u\n",
751 kd[i].otmLineGap, otm.otmLineGap);
752 todo_wine {
753 ok(kd[i].otmsCapEmHeight == otm.otmsCapEmHeight, "expected %u, got %u\n",
754 kd[i].otmsCapEmHeight, otm.otmsCapEmHeight);
755 ok(kd[i].otmsXHeight == otm.otmsXHeight, "expected %u, got %u\n",
756 kd[i].otmsXHeight, otm.otmsXHeight);
757 ok(kd[i].otmMacAscent == otm.otmMacAscent, "expected %d, got %d\n",
758 kd[i].otmMacAscent, otm.otmMacAscent);
759 ok(kd[i].otmMacDescent == otm.otmMacDescent, "expected %d, got %d\n",
760 kd[i].otmMacDescent, otm.otmMacDescent);
761 /* FIXME: this one sometimes succeeds due to expected 0, enable it when removing todo */
762 if (0) ok(kd[i].otmMacLineGap == otm.otmMacLineGap, "expected %u, got %u\n",
763 kd[i].otmMacLineGap, otm.otmMacLineGap);
764 ok(kd[i].otmusMinimumPPEM == otm.otmusMinimumPPEM, "expected %u, got %u\n",
765 kd[i].otmusMinimumPPEM, otm.otmusMinimumPPEM);
768 total_kern_pairs = GetKerningPairsW(hdc, 0, NULL);
769 trace("total_kern_pairs %u\n", total_kern_pairs);
770 kern_pair = HeapAlloc(GetProcessHeap(), 0, total_kern_pairs * sizeof(*kern_pair));
772 #if 0 /* Win98 (GetKerningPairsA) and XP behave differently here, the test passes on XP */
773 SetLastError(0xdeadbeef);
774 ret = GetKerningPairsW(hdc, 0, kern_pair);
775 ok(GetLastError() == ERROR_INVALID_PARAMETER,
776 "got error %ld, expected ERROR_INVALID_PARAMETER\n", GetLastError());
777 ok(ret == 0, "got %lu, expected 0\n", ret);
778 #endif
780 ret = GetKerningPairsW(hdc, 100, NULL);
781 ok(ret == total_kern_pairs, "got %u, expected %u\n", ret, total_kern_pairs);
783 ret = GetKerningPairsW(hdc, total_kern_pairs/2, kern_pair);
784 ok(ret == total_kern_pairs/2, "got %u, expected %u\n", ret, total_kern_pairs/2);
786 ret = GetKerningPairsW(hdc, total_kern_pairs, kern_pair);
787 ok(ret == total_kern_pairs, "got %u, expected %u\n", ret, total_kern_pairs);
789 matches = 0;
791 for (n = 0; n < ret; n++)
793 DWORD j;
794 #if 0
795 if (kern_pair[n].wFirst < 127 && kern_pair[n].wSecond < 127)
796 trace("{'%c','%c',%d},\n",
797 kern_pair[n].wFirst, kern_pair[n].wSecond, kern_pair[n].iKernAmount);
798 #endif
799 for (j = 0; j < kd[i].total_kern_pairs; j++)
801 if (kern_pair[n].wFirst == kd[i].kern_pair[j].wFirst &&
802 kern_pair[n].wSecond == kd[i].kern_pair[j].wSecond)
804 ok(kern_pair[n].iKernAmount == kd[i].kern_pair[j].iKernAmount,
805 "pair %d:%d got %d, expected %d\n",
806 kern_pair[n].wFirst, kern_pair[n].wSecond,
807 kern_pair[n].iKernAmount, kd[i].kern_pair[j].iKernAmount);
808 matches++;
813 ok(matches == kd[i].total_kern_pairs, "got matches %u, expected %u\n",
814 matches, kd[i].total_kern_pairs);
816 HeapFree(GetProcessHeap(), 0, kern_pair);
818 SelectObject(hdc, hfont_old);
819 DeleteObject(hfont);
822 ReleaseDC(0, hdc);
825 static void test_GetOutlineTextMetrics(void)
827 OUTLINETEXTMETRIC *otm;
828 LOGFONT lf;
829 HFONT hfont, hfont_old;
830 HDC hdc;
831 DWORD ret, otm_size;
833 if (!is_font_installed("Arial"))
835 skip("Arial is not installed\n");
836 return;
839 hdc = GetDC(0);
841 memset(&lf, 0, sizeof(lf));
842 strcpy(lf.lfFaceName, "Arial");
843 lf.lfHeight = -13;
844 lf.lfWeight = FW_NORMAL;
845 lf.lfPitchAndFamily = DEFAULT_PITCH;
846 lf.lfQuality = PROOF_QUALITY;
847 hfont = CreateFontIndirect(&lf);
848 assert(hfont != 0);
850 hfont_old = SelectObject(hdc, hfont);
851 otm_size = GetOutlineTextMetrics(hdc, 0, NULL);
852 trace("otm buffer size %u (0x%x)\n", otm_size, otm_size);
854 otm = HeapAlloc(GetProcessHeap(), 0, otm_size);
856 memset(otm, 0xAA, otm_size);
857 SetLastError(0xdeadbeef);
858 otm->otmSize = sizeof(*otm); /* just in case for Win9x compatibility */
859 ret = GetOutlineTextMetrics(hdc, otm->otmSize, otm);
860 ok(ret == 1 /* Win9x */ ||
861 ret == otm->otmSize /* XP*/,
862 "expected %u, got %u, error %d\n", otm->otmSize, ret, GetLastError());
863 if (ret != 1) /* Win9x doesn't care about pointing beyond of the buffer */
865 ok(otm->otmpFamilyName == NULL, "expected NULL got %p\n", otm->otmpFamilyName);
866 ok(otm->otmpFaceName == NULL, "expected NULL got %p\n", otm->otmpFaceName);
867 ok(otm->otmpStyleName == NULL, "expected NULL got %p\n", otm->otmpStyleName);
868 ok(otm->otmpFullName == NULL, "expected NULL got %p\n", otm->otmpFullName);
871 memset(otm, 0xAA, otm_size);
872 SetLastError(0xdeadbeef);
873 otm->otmSize = otm_size; /* just in case for Win9x compatibility */
874 ret = GetOutlineTextMetrics(hdc, otm->otmSize, otm);
875 ok(ret == 1 /* Win9x */ ||
876 ret == otm->otmSize /* XP*/,
877 "expected %u, got %u, error %d\n", otm->otmSize, ret, GetLastError());
878 if (ret != 1) /* Win9x doesn't care about pointing beyond of the buffer */
880 ok(otm->otmpFamilyName != NULL, "expected not NULL got %p\n", otm->otmpFamilyName);
881 ok(otm->otmpFaceName != NULL, "expected not NULL got %p\n", otm->otmpFaceName);
882 ok(otm->otmpStyleName != NULL, "expected not NULL got %p\n", otm->otmpStyleName);
883 ok(otm->otmpFullName != NULL, "expected not NULL got %p\n", otm->otmpFullName);
886 /* ask about truncated data */
887 memset(otm, 0xAA, otm_size);
888 SetLastError(0xdeadbeef);
889 otm->otmSize = sizeof(*otm) - sizeof(LPSTR); /* just in case for Win9x compatibility */
890 ret = GetOutlineTextMetrics(hdc, otm->otmSize, otm);
891 ok(ret == 1 /* Win9x */ ||
892 ret == otm->otmSize /* XP*/,
893 "expected %u, got %u, error %d\n", otm->otmSize, ret, GetLastError());
894 if (ret != 1) /* Win9x doesn't care about pointing beyond of the buffer */
896 ok(otm->otmpFamilyName == NULL, "expected NULL got %p\n", otm->otmpFamilyName);
897 ok(otm->otmpFaceName == NULL, "expected NULL got %p\n", otm->otmpFaceName);
898 ok(otm->otmpStyleName == NULL, "expected NULL got %p\n", otm->otmpStyleName);
900 ok(otm->otmpFullName == (LPSTR)0xAAAAAAAA, "expected 0xAAAAAAAA got %p\n", otm->otmpFullName);
902 HeapFree(GetProcessHeap(), 0, otm);
904 SelectObject(hdc, hfont_old);
905 DeleteObject(hfont);
907 ReleaseDC(0, hdc);
910 static void testJustification(HDC hdc, PSTR str, RECT *clientArea)
912 INT x, y,
913 breakCount,
914 outputWidth = 0, /* to test TabbedTextOut() */
915 justifiedWidth = 0, /* to test GetTextExtentExPointW() */
916 areaWidth = clientArea->right - clientArea->left,
917 nErrors = 0, e;
918 BOOL lastExtent = FALSE;
919 PSTR pFirstChar, pLastChar;
920 SIZE size;
921 TEXTMETRICA tm;
922 struct err
924 char extent[100];
925 int GetTextExtentExPointWWidth;
926 int TabbedTextOutWidth;
927 } error[10];
929 GetTextMetricsA(hdc, &tm);
930 y = clientArea->top;
931 do {
932 breakCount = 0;
933 while (*str == tm.tmBreakChar) str++; /* skip leading break chars */
934 pFirstChar = str;
936 do {
937 pLastChar = str;
939 /* if not at the end of the string, ... */
940 if (*str == '\0') break;
941 /* ... add the next word to the current extent */
942 while (*str != '\0' && *str++ != tm.tmBreakChar);
943 breakCount++;
944 SetTextJustification(hdc, 0, 0);
945 GetTextExtentPoint32(hdc, pFirstChar, str - pFirstChar - 1, &size);
946 } while ((int) size.cx < areaWidth);
948 /* ignore trailing break chars */
949 breakCount--;
950 while (*(pLastChar - 1) == tm.tmBreakChar)
952 pLastChar--;
953 breakCount--;
956 if (*str == '\0' || breakCount <= 0) pLastChar = str;
958 SetTextJustification(hdc, 0, 0);
959 GetTextExtentPoint32(hdc, pFirstChar, pLastChar - pFirstChar, &size);
961 /* do not justify the last extent */
962 if (*str != '\0' && breakCount > 0)
964 SetTextJustification(hdc, areaWidth - size.cx, breakCount);
965 GetTextExtentPoint32(hdc, pFirstChar, pLastChar - pFirstChar, &size);
966 justifiedWidth = size.cx;
968 else lastExtent = TRUE;
970 x = clientArea->left;
972 outputWidth = LOWORD(TabbedTextOut(
973 hdc, x, y, pFirstChar, pLastChar - pFirstChar,
974 0, NULL, 0));
975 /* catch errors and report them */
976 if (!lastExtent && ((outputWidth != areaWidth) || (justifiedWidth != areaWidth)))
978 memset(error[nErrors].extent, 0, 100);
979 memcpy(error[nErrors].extent, pFirstChar, pLastChar - pFirstChar);
980 error[nErrors].TabbedTextOutWidth = outputWidth;
981 error[nErrors].GetTextExtentExPointWWidth = justifiedWidth;
982 nErrors++;
985 y += size.cy;
986 str = pLastChar;
987 } while (*str && y < clientArea->bottom);
989 for (e = 0; e < nErrors; e++)
991 ok(error[e].TabbedTextOutWidth == areaWidth,
992 "The output text (\"%s\") width should be %d, not %d.\n",
993 error[e].extent, areaWidth, error[e].TabbedTextOutWidth);
994 /* The width returned by GetTextExtentPoint32() is exactly the same
995 returned by GetTextExtentExPointW() - see dlls/gdi32/font.c */
996 ok(error[e].GetTextExtentExPointWWidth == areaWidth,
997 "GetTextExtentPointW() for \"%s\" should have returned a width of %d, not %d.\n",
998 error[e].extent, areaWidth, error[e].GetTextExtentExPointWWidth);
1002 static void test_SetTextJustification(void)
1004 HDC hdc;
1005 RECT clientArea;
1006 LOGFONTA lf;
1007 HFONT hfont;
1008 HWND hwnd;
1009 static char testText[] =
1010 "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do "
1011 "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut "
1012 "enim ad minim veniam, quis nostrud exercitation ullamco laboris "
1013 "nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in "
1014 "reprehenderit in voluptate velit esse cillum dolore eu fugiat "
1015 "nulla pariatur. Excepteur sint occaecat cupidatat non proident, "
1016 "sunt in culpa qui officia deserunt mollit anim id est laborum.";
1018 hwnd = CreateWindowExA(0, "static", "", WS_POPUP, 0,0, 400,400, 0, 0, 0, NULL);
1019 GetClientRect( hwnd, &clientArea );
1020 hdc = GetDC( hwnd );
1022 memset(&lf, 0, sizeof lf);
1023 lf.lfCharSet = ANSI_CHARSET;
1024 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
1025 lf.lfWeight = FW_DONTCARE;
1026 lf.lfHeight = 20;
1027 lf.lfQuality = DEFAULT_QUALITY;
1028 lstrcpyA(lf.lfFaceName, "Times New Roman");
1029 hfont = create_font("Times New Roman", &lf);
1030 SelectObject(hdc, hfont);
1032 testJustification(hdc, testText, &clientArea);
1034 DeleteObject(hfont);
1035 ReleaseDC(hwnd, hdc);
1036 DestroyWindow(hwnd);
1039 static BOOL get_glyph_indices(INT charset, UINT code_page, WORD *idx, UINT count, BOOL unicode)
1041 HDC hdc;
1042 LOGFONTA lf;
1043 HFONT hfont, hfont_old;
1044 CHARSETINFO csi;
1045 FONTSIGNATURE fs;
1046 INT cs;
1047 DWORD i, ret;
1048 char name[64];
1050 assert(count <= 128);
1052 memset(&lf, 0, sizeof(lf));
1054 lf.lfCharSet = charset;
1055 lf.lfHeight = 10;
1056 lstrcpyA(lf.lfFaceName, "Arial");
1057 SetLastError(0xdeadbeef);
1058 hfont = CreateFontIndirectA(&lf);
1059 ok(hfont != 0, "CreateFontIndirectA error %u\n", GetLastError());
1061 hdc = GetDC(0);
1062 hfont_old = SelectObject(hdc, hfont);
1064 cs = GetTextCharsetInfo(hdc, &fs, 0);
1065 ok(cs == charset, "expected %d, got %d\n", charset, cs);
1067 SetLastError(0xdeadbeef);
1068 ret = GetTextFace(hdc, sizeof(name), name);
1069 ok(ret, "GetTextFace error %u\n", GetLastError());
1071 if (charset == SYMBOL_CHARSET)
1073 ok(strcmp("Arial", name), "face name should NOT be Arial\n");
1074 ok(fs.fsCsb[0] & (1 << 31), "symbol encoding should be available\n");
1076 else
1078 ok(!strcmp("Arial", name), "face name should be Arial, not %s\n", name);
1079 ok(!(fs.fsCsb[0] & (1 << 31)), "symbol encoding should NOT be available\n");
1082 if (!TranslateCharsetInfo((DWORD *)cs, &csi, TCI_SRCCHARSET))
1084 trace("Can't find codepage for charset %d\n", cs);
1085 ReleaseDC(0, hdc);
1086 return FALSE;
1088 ok(csi.ciACP == code_page, "expected %d, got %d\n", code_page, csi.ciACP);
1090 if (unicode)
1092 char ansi_buf[128];
1093 WCHAR unicode_buf[128];
1095 for (i = 0; i < count; i++) ansi_buf[i] = (BYTE)(i + 128);
1097 MultiByteToWideChar(code_page, 0, ansi_buf, count, unicode_buf, count);
1099 SetLastError(0xdeadbeef);
1100 ret = pGetGlyphIndicesW(hdc, unicode_buf, count, idx, 0);
1101 ok(ret == count, "GetGlyphIndicesA error %u\n", GetLastError());
1103 else
1105 char ansi_buf[128];
1107 for (i = 0; i < count; i++) ansi_buf[i] = (BYTE)(i + 128);
1109 SetLastError(0xdeadbeef);
1110 ret = pGetGlyphIndicesA(hdc, ansi_buf, count, idx, 0);
1111 ok(ret == count, "GetGlyphIndicesA error %u\n", GetLastError());
1114 SelectObject(hdc, hfont_old);
1115 DeleteObject(hfont);
1117 ReleaseDC(0, hdc);
1119 return TRUE;
1122 static void test_font_charset(void)
1124 static struct charset_data
1126 INT charset;
1127 UINT code_page;
1128 WORD font_idxA[128], font_idxW[128];
1129 } cd[] =
1131 { ANSI_CHARSET, 1252 },
1132 { RUSSIAN_CHARSET, 1251 },
1133 { SYMBOL_CHARSET, CP_SYMBOL } /* keep it as the last one */
1135 int i;
1137 if (!pGetGlyphIndicesA || !pGetGlyphIndicesW)
1139 skip("Skipping the font charset test on a Win9x platform\n");
1140 return;
1143 if (!is_font_installed("Arial"))
1145 skip("Arial is not installed\n");
1146 return;
1149 for (i = 0; i < sizeof(cd)/sizeof(cd[0]); i++)
1151 if (cd[i].charset == SYMBOL_CHARSET)
1153 if (!is_font_installed("Symbol") && !is_font_installed("Wingdings"))
1155 skip("Symbol or Wingdings is not installed\n");
1156 break;
1159 get_glyph_indices(cd[i].charset, cd[i].code_page, cd[i].font_idxA, 128, FALSE);
1160 get_glyph_indices(cd[i].charset, cd[i].code_page, cd[i].font_idxW, 128, TRUE);
1161 ok(!memcmp(cd[i].font_idxA, cd[i].font_idxW, 128*sizeof(WORD)), "%d: indices don't match\n", i);
1164 ok(memcmp(cd[0].font_idxW, cd[1].font_idxW, 128*sizeof(WORD)), "0 vs 1: indices shouldn't match\n");
1165 if (i > 2)
1167 ok(memcmp(cd[0].font_idxW, cd[2].font_idxW, 128*sizeof(WORD)), "0 vs 2: indices shouldn't match\n");
1168 ok(memcmp(cd[1].font_idxW, cd[2].font_idxW, 128*sizeof(WORD)), "1 vs 2: indices shouldn't match\n");
1170 else
1171 skip("Symbol or Wingdings is not installed\n");
1174 static void test_GetFontUnicodeRanges(void)
1176 LOGFONTA lf;
1177 HDC hdc;
1178 HFONT hfont, hfont_old;
1179 DWORD size;
1180 GLYPHSET *gs;
1182 if (!pGetFontUnicodeRanges)
1184 skip("GetFontUnicodeRanges not available before W2K\n");
1185 return;
1188 memset(&lf, 0, sizeof(lf));
1189 lstrcpyA(lf.lfFaceName, "Arial");
1190 hfont = create_font("Arial", &lf);
1192 hdc = GetDC(0);
1193 hfont_old = SelectObject(hdc, hfont);
1195 size = pGetFontUnicodeRanges(NULL, NULL);
1196 ok(!size, "GetFontUnicodeRanges succeeded unexpectedly\n");
1198 size = pGetFontUnicodeRanges(hdc, NULL);
1199 ok(size, "GetFontUnicodeRanges failed unexpectedly\n");
1201 gs = HeapAlloc(GetProcessHeap(), 0, size);
1203 size = pGetFontUnicodeRanges(hdc, gs);
1204 ok(size, "GetFontUnicodeRanges failed\n");
1205 #if 0
1206 for (i = 0; i < gs->cRanges; i++)
1207 trace("%03d wcLow %04x cGlyphs %u\n", i, gs->ranges[i].wcLow, gs->ranges[i].cGlyphs);
1208 #endif
1209 trace("found %u ranges\n", gs->cRanges);
1211 HeapFree(GetProcessHeap(), 0, gs);
1213 SelectObject(hdc, hfont_old);
1214 DeleteObject(hfont);
1215 ReleaseDC(NULL, hdc);
1218 #define MAX_ENUM_FONTS 256
1220 struct enum_font_data
1222 int total;
1223 LOGFONT lf[MAX_ENUM_FONTS];
1226 static INT CALLBACK arial_enum_proc(const LOGFONT *lf, const TEXTMETRIC *tm, DWORD type, LPARAM lParam)
1228 struct enum_font_data *efd = (struct enum_font_data *)lParam;
1230 if (type != TRUETYPE_FONTTYPE) return 1;
1231 #if 0
1232 trace("enumed font \"%s\", charset %d, weight %d, italic %d\n",
1233 lf->lfFaceName, lf->lfCharSet, lf->lfWeight, lf->lfItalic);
1234 #endif
1235 if (efd->total < MAX_ENUM_FONTS)
1236 efd->lf[efd->total++] = *lf;
1238 return 1;
1241 static void get_charset_stats(struct enum_font_data *efd,
1242 int *ansi_charset, int *symbol_charset,
1243 int *russian_charset)
1245 int i;
1247 *ansi_charset = 0;
1248 *symbol_charset = 0;
1249 *russian_charset = 0;
1251 for (i = 0; i < efd->total; i++)
1253 switch (efd->lf[i].lfCharSet)
1255 case ANSI_CHARSET:
1256 (*ansi_charset)++;
1257 break;
1258 case SYMBOL_CHARSET:
1259 (*symbol_charset)++;
1260 break;
1261 case RUSSIAN_CHARSET:
1262 (*russian_charset)++;
1263 break;
1268 static void test_EnumFontFamilies(const char *font_name, INT font_charset)
1270 struct enum_font_data efd;
1271 LOGFONT lf;
1272 HDC hdc;
1273 int i, ret, ansi_charset, symbol_charset, russian_charset;
1275 trace("Testing font %s, charset %d\n", *font_name ? font_name : "<empty>", font_charset);
1277 if (*font_name && !is_truetype_font_installed(font_name))
1279 skip("%s is not installed\n", font_name);
1280 return;
1283 hdc = GetDC(0);
1285 /* Observed behaviour: EnumFontFamilies enumerates aliases like "Arial Cyr"
1286 * while EnumFontFamiliesEx doesn't.
1288 if (!*font_name && font_charset == DEFAULT_CHARSET) /* do it only once */
1290 efd.total = 0;
1291 SetLastError(0xdeadbeef);
1292 ret = EnumFontFamilies(hdc, NULL, arial_enum_proc, (LPARAM)&efd);
1293 ok(ret, "EnumFontFamilies error %u\n", GetLastError());
1294 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
1295 trace("enumerated ansi %d, symbol %d, russian %d fonts for NULL\n",
1296 ansi_charset, symbol_charset, russian_charset);
1297 ok(efd.total > 0, "no fonts enumerated: NULL\n");
1298 ok(ansi_charset > 0, "NULL family should enumerate ANSI_CHARSET\n");
1299 ok(symbol_charset > 0, "NULL family should enumerate SYMBOL_CHARSET\n");
1300 ok(russian_charset > 0, "NULL family should enumerate RUSSIAN_CHARSET\n");
1302 efd.total = 0;
1303 SetLastError(0xdeadbeef);
1304 ret = EnumFontFamiliesEx(hdc, NULL, arial_enum_proc, (LPARAM)&efd, 0);
1305 ok(ret, "EnumFontFamiliesEx error %u\n", GetLastError());
1306 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
1307 trace("enumerated ansi %d, symbol %d, russian %d fonts for NULL\n",
1308 ansi_charset, symbol_charset, russian_charset);
1309 ok(efd.total > 0, "no fonts enumerated: NULL\n");
1310 ok(ansi_charset > 0, "NULL family should enumerate ANSI_CHARSET\n");
1311 ok(symbol_charset > 0, "NULL family should enumerate SYMBOL_CHARSET\n");
1312 ok(russian_charset > 0, "NULL family should enumerate RUSSIAN_CHARSET\n");
1315 efd.total = 0;
1316 SetLastError(0xdeadbeef);
1317 ret = EnumFontFamilies(hdc, font_name, arial_enum_proc, (LPARAM)&efd);
1318 ok(ret, "EnumFontFamilies error %u\n", GetLastError());
1319 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
1320 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s\n",
1321 ansi_charset, symbol_charset, russian_charset,
1322 *font_name ? font_name : "<empty>");
1323 if (*font_name)
1324 ok(efd.total > 0, "no fonts enumerated: %s\n", font_name);
1325 else
1326 ok(!efd.total, "no fonts should be enumerated for empty font_name\n");
1327 for (i = 0; i < efd.total; i++)
1329 /* FIXME: remove completely once Wine is fixed */
1330 if (efd.lf[i].lfCharSet != font_charset)
1332 todo_wine
1333 ok(efd.lf[i].lfCharSet == font_charset, "%d: got charset %d\n", i, efd.lf[i].lfCharSet);
1335 else
1336 ok(efd.lf[i].lfCharSet == font_charset, "%d: got charset %d\n", i, efd.lf[i].lfCharSet);
1337 ok(!lstrcmp(efd.lf[i].lfFaceName, font_name), "expected %s, got %s\n",
1338 font_name, efd.lf[i].lfFaceName);
1341 memset(&lf, 0, sizeof(lf));
1342 lf.lfCharSet = ANSI_CHARSET;
1343 lstrcpy(lf.lfFaceName, font_name);
1344 efd.total = 0;
1345 SetLastError(0xdeadbeef);
1346 ret = EnumFontFamiliesEx(hdc, &lf, arial_enum_proc, (LPARAM)&efd, 0);
1347 ok(ret, "EnumFontFamiliesEx error %u\n", GetLastError());
1348 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
1349 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s ANSI_CHARSET\n",
1350 ansi_charset, symbol_charset, russian_charset,
1351 *font_name ? font_name : "<empty>");
1352 if (font_charset == SYMBOL_CHARSET)
1354 if (*font_name)
1355 ok(efd.total == 0, "no fonts should be enumerated: %s ANSI_CHARSET\n", font_name);
1356 else
1357 ok(efd.total > 0, "no fonts enumerated: %s\n", font_name);
1359 else
1361 ok(efd.total > 0, "no fonts enumerated: %s ANSI_CHARSET\n", font_name);
1362 for (i = 0; i < efd.total; i++)
1364 ok(efd.lf[i].lfCharSet == ANSI_CHARSET, "%d: got charset %d\n", i, efd.lf[i].lfCharSet);
1365 if (*font_name)
1366 ok(!lstrcmp(efd.lf[i].lfFaceName, font_name), "expected %s, got %s\n",
1367 font_name, efd.lf[i].lfFaceName);
1371 /* DEFAULT_CHARSET should enumerate all available charsets */
1372 memset(&lf, 0, sizeof(lf));
1373 lf.lfCharSet = DEFAULT_CHARSET;
1374 lstrcpy(lf.lfFaceName, font_name);
1375 efd.total = 0;
1376 SetLastError(0xdeadbeef);
1377 EnumFontFamiliesEx(hdc, &lf, arial_enum_proc, (LPARAM)&efd, 0);
1378 ok(ret, "EnumFontFamiliesEx error %u\n", GetLastError());
1379 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
1380 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s DEFAULT_CHARSET\n",
1381 ansi_charset, symbol_charset, russian_charset,
1382 *font_name ? font_name : "<empty>");
1383 ok(efd.total > 0, "no fonts enumerated: %s DEFAULT_CHARSET\n", font_name);
1384 for (i = 0; i < efd.total; i++)
1386 if (*font_name)
1387 ok(!lstrcmp(efd.lf[i].lfFaceName, font_name), "expected %s, got %s\n",
1388 font_name, efd.lf[i].lfFaceName);
1390 if (*font_name)
1392 switch (font_charset)
1394 case ANSI_CHARSET:
1395 ok(ansi_charset > 0,
1396 "ANSI_CHARSET should enumerate ANSI_CHARSET for %s\n", font_name);
1397 ok(!symbol_charset,
1398 "ANSI_CHARSET should NOT enumerate SYMBOL_CHARSET for %s\n", font_name);
1399 ok(russian_charset > 0,
1400 "ANSI_CHARSET should enumerate RUSSIAN_CHARSET for %s\n", font_name);
1401 break;
1402 case SYMBOL_CHARSET:
1403 ok(!ansi_charset,
1404 "SYMBOL_CHARSET should NOT enumerate ANSI_CHARSET for %s\n", font_name);
1405 ok(symbol_charset,
1406 "SYMBOL_CHARSET should enumerate SYMBOL_CHARSET for %s\n", font_name);
1407 ok(!russian_charset,
1408 "SYMBOL_CHARSET should NOT enumerate RUSSIAN_CHARSET for %s\n", font_name);
1409 break;
1410 case DEFAULT_CHARSET:
1411 ok(ansi_charset > 0,
1412 "DEFAULT_CHARSET should enumerate ANSI_CHARSET for %s\n", font_name);
1413 ok(symbol_charset > 0,
1414 "DEFAULT_CHARSET should enumerate SYMBOL_CHARSET for %s\n", font_name);
1415 ok(russian_charset > 0,
1416 "DEFAULT_CHARSET should enumerate RUSSIAN_CHARSET for %s\n", font_name);
1417 break;
1420 else
1422 ok(ansi_charset > 0,
1423 "DEFAULT_CHARSET should enumerate ANSI_CHARSET for %s\n", *font_name ? font_name : "<empty>");
1424 ok(symbol_charset > 0,
1425 "DEFAULT_CHARSET should enumerate SYMBOL_CHARSET for %s\n", *font_name ? font_name : "<empty>");
1426 ok(russian_charset > 0,
1427 "DEFAULT_CHARSET should enumerate RUSSIAN_CHARSET for %s\n", *font_name ? font_name : "<empty>");
1430 memset(&lf, 0, sizeof(lf));
1431 lf.lfCharSet = SYMBOL_CHARSET;
1432 lstrcpy(lf.lfFaceName, font_name);
1433 efd.total = 0;
1434 SetLastError(0xdeadbeef);
1435 EnumFontFamiliesEx(hdc, &lf, arial_enum_proc, (LPARAM)&efd, 0);
1436 ok(ret, "EnumFontFamiliesEx error %u\n", GetLastError());
1437 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
1438 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s SYMBOL_CHARSET\n",
1439 ansi_charset, symbol_charset, russian_charset,
1440 *font_name ? font_name : "<empty>");
1441 if (*font_name && font_charset == ANSI_CHARSET)
1442 ok(efd.total == 0, "no fonts should be enumerated: %s SYMBOL_CHARSET\n", font_name);
1443 else
1445 ok(efd.total > 0, "no fonts enumerated: %s SYMBOL_CHARSET\n", font_name);
1446 for (i = 0; i < efd.total; i++)
1448 ok(efd.lf[i].lfCharSet == SYMBOL_CHARSET, "%d: got charset %d\n", i, efd.lf[i].lfCharSet);
1449 if (*font_name)
1450 ok(!lstrcmp(efd.lf[i].lfFaceName, font_name), "expected %s, got %s\n",
1451 font_name, efd.lf[i].lfFaceName);
1454 ok(!ansi_charset,
1455 "SYMBOL_CHARSET should NOT enumerate ANSI_CHARSET for %s\n", *font_name ? font_name : "<empty>");
1456 ok(symbol_charset > 0,
1457 "SYMBOL_CHARSET should enumerate SYMBOL_CHARSET for %s\n", *font_name ? font_name : "<empty>");
1458 ok(!russian_charset,
1459 "SYMBOL_CHARSET should NOT enumerate RUSSIAN_CHARSET for %s\n", *font_name ? font_name : "<empty>");
1462 ReleaseDC(0, hdc);
1465 /* PANOSE is 10 bytes in size, need to pack the structure properly */
1466 #include "pshpack2.h"
1467 typedef struct
1469 USHORT version;
1470 SHORT xAvgCharWidth;
1471 USHORT usWeightClass;
1472 USHORT usWidthClass;
1473 SHORT fsType;
1474 SHORT ySubscriptXSize;
1475 SHORT ySubscriptYSize;
1476 SHORT ySubscriptXOffset;
1477 SHORT ySubscriptYOffset;
1478 SHORT ySuperscriptXSize;
1479 SHORT ySuperscriptYSize;
1480 SHORT ySuperscriptXOffset;
1481 SHORT ySuperscriptYOffset;
1482 SHORT yStrikeoutSize;
1483 SHORT yStrikeoutPosition;
1484 SHORT sFamilyClass;
1485 PANOSE panose;
1486 ULONG ulUnicodeRange1;
1487 ULONG ulUnicodeRange2;
1488 ULONG ulUnicodeRange3;
1489 ULONG ulUnicodeRange4;
1490 CHAR achVendID[4];
1491 USHORT fsSelection;
1492 USHORT usFirstCharIndex;
1493 USHORT usLastCharIndex;
1494 /* According to the Apple spec, original version didn't have the below fields,
1495 * version numbers were taked from the OpenType spec.
1497 /* version 0 (TrueType 1.5) */
1498 USHORT sTypoAscender;
1499 USHORT sTypoDescender;
1500 USHORT sTypoLineGap;
1501 USHORT usWinAscent;
1502 USHORT usWinDescent;
1503 /* version 1 (TrueType 1.66) */
1504 ULONG ulCodePageRange1;
1505 ULONG ulCodePageRange2;
1506 /* version 2 (OpenType 1.2) */
1507 SHORT sxHeight;
1508 SHORT sCapHeight;
1509 USHORT usDefaultChar;
1510 USHORT usBreakChar;
1511 USHORT usMaxContext;
1512 } TT_OS2_V2;
1513 #include "poppack.h"
1515 #ifdef WORDS_BIGENDIAN
1516 #define GET_BE_WORD(x) (x)
1517 #else
1518 #define GET_BE_WORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
1519 #endif
1521 #define MS_MAKE_TAG(ch0, ch1, ch2, ch3) \
1522 ((DWORD)(BYTE)(ch0) | ((DWORD)(BYTE)(ch1) << 8) | \
1523 ((DWORD)(BYTE)(ch2) << 16) | ((DWORD)(BYTE)(ch3) << 24))
1524 #define MS_OS2_TAG MS_MAKE_TAG('O','S','/','2')
1526 static void test_text_metrics(const LOGFONTA *lf)
1528 HDC hdc;
1529 HFONT hfont, hfont_old;
1530 TEXTMETRICA tmA;
1531 TEXTMETRICW tmW;
1532 UINT first_unicode_char, last_unicode_char, default_char, break_char;
1533 INT test_char;
1534 TT_OS2_V2 tt_os2;
1535 USHORT version;
1536 LONG size, ret;
1537 const char *font_name = lf->lfFaceName;
1539 trace("Testing font metrics for %s, charset %d\n", font_name, lf->lfCharSet);
1541 hdc = GetDC(0);
1543 SetLastError(0xdeadbeef);
1544 hfont = CreateFontIndirectA(lf);
1545 ok(hfont != 0, "CreateFontIndirect error %u\n", GetLastError());
1547 hfont_old = SelectObject(hdc, hfont);
1549 if(lf->lfWidth > 0) {
1550 HFONT hfont2, hfont_prev;
1551 GLYPHMETRICS gm1, gm2;
1552 LOGFONTA lf2 = *lf;
1553 MAT2 mat2 = { {0,1}, {0,0}, {0,0}, {0,1} };
1555 /* negative widths are handled just as positive ones */
1556 lf2.lfWidth *= -1;
1558 SetLastError(0xdeadbeef);
1559 hfont2 = CreateFontIndirectA(&lf2);
1560 ok(hfont2 != 0, "CreateFontIndirect error %u\n", GetLastError());
1561 hfont_prev = SelectObject(hdc, hfont2);
1563 /* filling with 0xaa causes false pass under WINEDEBUG=warn+heap */
1564 memset(&gm1, 0xab, sizeof(gm1));
1565 SetLastError(0xdeadbeef);
1566 ret = GetGlyphOutlineA(hdc, 'x', GGO_METRICS, &gm1, 0, NULL, &mat2);
1567 ok(ret != GDI_ERROR, "GetGlyphOutline error 0x%x\n", GetLastError());
1569 SelectObject(hdc, hfont_prev);
1570 DeleteObject(hfont2);
1572 memset(&gm2, 0xbb, sizeof(gm2));
1573 SetLastError(0xdeadbeef);
1574 ret = GetGlyphOutlineA(hdc, 'x', GGO_METRICS, &gm2, 0, NULL, &mat2);
1575 ok(ret != GDI_ERROR, "GetGlyphOutline error 0x%x\n", GetLastError());
1577 ok(gm1.gmBlackBoxX == gm2.gmBlackBoxX &&
1578 gm1.gmBlackBoxY == gm2.gmBlackBoxY &&
1579 gm1.gmptGlyphOrigin.x == gm2.gmptGlyphOrigin.x &&
1580 gm1.gmptGlyphOrigin.y == gm2.gmptGlyphOrigin.y &&
1581 gm1.gmCellIncX == gm2.gmCellIncX &&
1582 gm1.gmCellIncY == gm2.gmCellIncY,
1583 "gm1=%d,%d,%d,%d,%d,%d gm2=%d,%d,%d,%d,%d,%d\n",
1584 gm1.gmBlackBoxX, gm1.gmBlackBoxY, gm1.gmptGlyphOrigin.x,
1585 gm1.gmptGlyphOrigin.y, gm1.gmCellIncX, gm1.gmCellIncY,
1586 gm2.gmBlackBoxX, gm2.gmBlackBoxY, gm2.gmptGlyphOrigin.x,
1587 gm2.gmptGlyphOrigin.y, gm2.gmCellIncX, gm2.gmCellIncY);
1590 size = GetFontData(hdc, MS_OS2_TAG, 0, NULL, 0);
1591 if (size == GDI_ERROR)
1593 trace("OS/2 chunk was not found\n");
1594 goto end_of_test;
1596 if (size > sizeof(tt_os2))
1598 trace("got too large OS/2 chunk of size %u\n", size);
1599 size = sizeof(tt_os2);
1602 memset(&tt_os2, 0, sizeof(tt_os2));
1603 ret = GetFontData(hdc, MS_OS2_TAG, 0, &tt_os2, size);
1604 ok(ret == size, "GetFontData should return %u not %u\n", size, ret);
1606 version = GET_BE_WORD(tt_os2.version);
1607 trace("OS/2 chunk version %u, vendor %4.4s\n", version, (LPCSTR)&tt_os2.achVendID);
1609 first_unicode_char = GET_BE_WORD(tt_os2.usFirstCharIndex);
1610 last_unicode_char = GET_BE_WORD(tt_os2.usLastCharIndex);
1611 default_char = GET_BE_WORD(tt_os2.usDefaultChar);
1612 break_char = GET_BE_WORD(tt_os2.usBreakChar);
1614 trace("for %s first %x, last %x, default %x, break %x\n", font_name,
1615 first_unicode_char, last_unicode_char, default_char, break_char);
1617 SetLastError(0xdeadbeef);
1618 ret = GetTextMetricsA(hdc, &tmA);
1619 ok(ret, "GetTextMetricsA error %u\n", GetLastError());
1620 trace("A: first %x, last %x, default %x, break %x\n",
1621 tmA.tmFirstChar, tmA.tmLastChar, tmA.tmDefaultChar, tmA.tmBreakChar);
1623 #if 0 /* FIXME: This doesn't appear to be what Windows does */
1624 test_char = min(first_unicode_char - 1, 255);
1625 ok(tmA.tmFirstChar == test_char, "A: tmFirstChar for %s %02x != %02x\n",
1626 font_name, tmA.tmFirstChar, test_char);
1627 #endif
1628 if (lf->lfCharSet == SYMBOL_CHARSET)
1630 test_char = min(last_unicode_char - 0xf000, 255);
1631 ok(tmA.tmLastChar == test_char, "A: tmLastChar for %s %02x != %02x\n",
1632 font_name, tmA.tmLastChar, test_char);
1634 else
1636 test_char = min(last_unicode_char, 255);
1637 ok(tmA.tmLastChar == test_char, "A: tmLastChar for %s %02x != %02x\n",
1638 font_name, tmA.tmLastChar, test_char);
1641 SetLastError(0xdeadbeef);
1642 ret = GetTextMetricsW(hdc, &tmW);
1643 ok(ret || GetLastError() == ERROR_CALL_NOT_IMPLEMENTED,
1644 "GetTextMetricsW error %u\n", GetLastError());
1645 if (ret)
1647 trace("W: first %x, last %x, default %x, break %x\n",
1648 tmW.tmFirstChar, tmW.tmLastChar, tmW.tmDefaultChar,
1649 tmW.tmBreakChar);
1651 if (lf->lfCharSet == SYMBOL_CHARSET)
1653 /* It appears that for fonts with SYMBOL_CHARSET Windows always
1654 * sets symbol range to 0 - f0ff
1656 ok(tmW.tmFirstChar == 0, "W: tmFirstChar for %s %02x != 0\n",
1657 font_name, tmW.tmFirstChar);
1658 /* FIXME: Windows returns f0ff here, while Wine f0xx */
1659 ok(tmW.tmLastChar >= 0xf000, "W: tmLastChar for %s %02x < 0xf000\n",
1660 font_name, tmW.tmLastChar);
1662 ok(tmW.tmDefaultChar == 0x1f, "W: tmDefaultChar for %s %02x != 0x1f\n",
1663 font_name, tmW.tmDefaultChar);
1664 ok(tmW.tmBreakChar == 0x20, "W: tmBreakChar for %s %02x != 0x20\n",
1665 font_name, tmW.tmBreakChar);
1667 else
1669 ok(tmW.tmFirstChar == first_unicode_char, "W: tmFirstChar for %s %02x != %02x\n",
1670 font_name, tmW.tmFirstChar, first_unicode_char);
1671 ok(tmW.tmLastChar == last_unicode_char, "W: tmLastChar for %s %02x != %02x\n",
1672 font_name, tmW.tmLastChar, last_unicode_char);
1674 ret = GetDeviceCaps(hdc, LOGPIXELSX);
1675 ok(tmW.tmDigitizedAspectX == ret, "W: tmDigitizedAspectX %u != %u\n",
1676 tmW.tmDigitizedAspectX, ret);
1677 ret = GetDeviceCaps(hdc, LOGPIXELSY);
1678 ok(tmW.tmDigitizedAspectX == ret, "W: tmDigitizedAspectY %u != %u\n",
1679 tmW.tmDigitizedAspectX, ret);
1682 end_of_test:
1683 SelectObject(hdc, hfont_old);
1684 DeleteObject(hfont);
1686 ReleaseDC(0, hdc);
1689 static INT CALLBACK enum_truetype_font_proc(const LOGFONT *lf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
1691 INT *enumed = (INT *)lParam;
1693 if (type == TRUETYPE_FONTTYPE)
1695 (*enumed)++;
1696 test_text_metrics(lf);
1698 return 1;
1701 static void test_GetTextMetrics(void)
1703 LOGFONTA lf;
1704 HDC hdc;
1705 INT enumed;
1707 hdc = GetDC(0);
1709 memset(&lf, 0, sizeof(lf));
1710 lf.lfCharSet = DEFAULT_CHARSET;
1711 enumed = 0;
1712 EnumFontFamiliesExA(hdc, &lf, enum_truetype_font_proc, (LPARAM)&enumed, 0);
1713 trace("Tested metrics of %d truetype fonts\n", enumed);
1715 ReleaseDC(0, hdc);
1718 static void test_nonexistent_font(void)
1720 LOGFONTA lf;
1721 HDC hdc;
1722 HFONT hfont;
1723 char buf[LF_FACESIZE];
1725 if (!is_truetype_font_installed("Arial Black"))
1727 skip("Arial not installed\n");
1728 return;
1731 hdc = GetDC(0);
1733 memset(&lf, 0, sizeof(lf));
1734 lf.lfHeight = 100;
1735 lf.lfWeight = FW_REGULAR;
1736 lf.lfCharSet = ANSI_CHARSET;
1737 lf.lfPitchAndFamily = FF_SWISS;
1738 strcpy(lf.lfFaceName, "Nonexistent font");
1740 hfont = CreateFontIndirectA(&lf);
1741 hfont = SelectObject(hdc, hfont);
1742 GetTextFaceA(hdc, sizeof(buf), buf);
1743 ok(!lstrcmpiA(buf, "Arial"), "Got %s\n", buf);
1744 DeleteObject(SelectObject(hdc, hfont));
1745 ReleaseDC(0, hdc);
1748 START_TEST(font)
1750 init();
1752 test_logfont();
1753 test_bitmap_font();
1754 test_bitmap_font_metrics();
1755 test_GdiGetCharDimensions();
1756 test_GetCharABCWidths();
1757 test_text_extents();
1758 test_GetGlyphIndices();
1759 test_GetKerningPairs();
1760 test_GetOutlineTextMetrics();
1761 test_SetTextJustification();
1762 test_font_charset();
1763 test_GetFontUnicodeRanges();
1764 test_nonexistent_font();
1766 /* On Windows Arial has a lot of default charset aliases such as Arial Cyr,
1767 * I'd like to avoid them in this test.
1769 test_EnumFontFamilies("Arial Black", ANSI_CHARSET);
1770 test_EnumFontFamilies("Symbol", SYMBOL_CHARSET);
1771 if (is_truetype_font_installed("Arial Black") &&
1772 (is_truetype_font_installed("Symbol") || is_truetype_font_installed("Wingdings")))
1774 test_EnumFontFamilies("", ANSI_CHARSET);
1775 test_EnumFontFamilies("", SYMBOL_CHARSET);
1776 test_EnumFontFamilies("", DEFAULT_CHARSET);
1778 else
1779 skip("Arial Black or Symbol/Wingdings is not installed\n");
1780 test_GetTextMetrics();