Merge trunk (r43561)
[reactos.git] / rostests / winetests / gdi32 / font.c
1 /*
2 * Unit test suite for fonts
3 *
4 * Copyright 2002 Mike McCormack
5 * Copyright 2004 Dmitry Timoshkov
6 *
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.
11 *
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.
16 *
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
20 */
21
22 #include <stdarg.h>
23 #include <assert.h>
24
25 #include "windef.h"
26 #include "winbase.h"
27 #include "wingdi.h"
28 #include "winuser.h"
29 #include "winnls.h"
30
31 #include "wine/test.h"
32
33 #define expect(expected, got) ok(got == expected, "Expected %.8x, got %.8x\n", expected, got)
34
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 *);
42
43 static HMODULE hgdi32 = 0;
44
45 static void init(void)
46 {
47 hgdi32 = GetModuleHandleA("gdi32.dll");
48
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");
56 }
57
58 static INT CALLBACK is_truetype_font_installed_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
59 {
60 if (type != TRUETYPE_FONTTYPE) return 1;
61
62 return 0;
63 }
64
65 static BOOL is_truetype_font_installed(const char *name)
66 {
67 HDC hdc = GetDC(0);
68 BOOL ret = FALSE;
69
70 if (!EnumFontFamiliesA(hdc, name, is_truetype_font_installed_proc, 0))
71 ret = TRUE;
72
73 ReleaseDC(0, hdc);
74 return ret;
75 }
76
77 static INT CALLBACK is_font_installed_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
78 {
79 return 0;
80 }
81
82 static BOOL is_font_installed(const char *name)
83 {
84 HDC hdc = GetDC(0);
85 BOOL ret = FALSE;
86
87 if(!EnumFontFamiliesA(hdc, name, is_font_installed_proc, 0))
88 ret = TRUE;
89
90 ReleaseDC(0, hdc);
91 return ret;
92 }
93
94 static void check_font(const char* test, const LOGFONTA* lf, HFONT hfont)
95 {
96 LOGFONTA getobj_lf;
97 int ret, minlen = 0;
98
99 if (!hfont)
100 return;
101
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);
111 }
112
113 static HFONT create_font(const char* test, const LOGFONTA* lf)
114 {
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;
120 }
121
122 static void test_logfont(void)
123 {
124 LOGFONTA lf;
125 HFONT hfont;
126
127 memset(&lf, 0, sizeof lf);
128
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;
135
136 lstrcpyA(lf.lfFaceName, "Arial");
137 hfont = create_font("Arial", &lf);
138 DeleteObject(hfont);
139
140 memset(&lf, 'A', sizeof(lf));
141 hfont = CreateFontIndirectA(&lf);
142 ok(hfont != 0, "CreateFontIndirectA with strange LOGFONT failed\n");
143
144 lf.lfFaceName[LF_FACESIZE - 1] = 0;
145 check_font("AAA...", &lf, hfont);
146 DeleteObject(hfont);
147 }
148
149 static INT CALLBACK font_enum_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
150 {
151 if (type & RASTER_FONTTYPE)
152 {
153 LOGFONT *lf = (LOGFONT *)lParam;
154 *lf = *elf;
155 return 0; /* stop enumeration */
156 }
157
158 return 1; /* continue enumeration */
159 }
160
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)
166 {
167 HFONT old_hfont;
168 LOGFONTA lf;
169 TEXTMETRICA tm;
170 SIZE size;
171 INT width_of_A, cx, cy;
172
173 if (!hfont)
174 return;
175
176 GetObjectA(hfont, sizeof(lf), &lf);
177
178 old_hfont = SelectObject(hdc, hfont);
179
180 GetTextMetricsA(hdc, &tm);
181
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);
191
192 ok(lf.lfHeight == lfHeight, "lf %d != %d\n", lf.lfHeight, lfHeight);
193 if (lf.lfHeight)
194 {
195 if (lf.lfWidth)
196 ok(lf.lfWidth == tm.tmAveCharWidth, "lf %d != tm %d\n", lf.lfWidth, tm.tmAveCharWidth);
197 }
198 else
199 ok(lf.lfWidth == lfWidth, "lf %d != %d\n", lf.lfWidth, lfWidth);
200
201 GetTextExtentPoint32A(hdc, test_str, test_str_len, &size);
202
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);
205
206 GetCharWidthA(hdc, 'A', 'A', &width_of_A);
207
208 ok(width_of_A == width_of_A_orig * scale_x, "%d != %d\n", width_of_A, width_of_A_orig * scale_x);
209
210 SelectObject(hdc, old_hfont);
211 }
212
213 /* Test how GDI scales bitmap font metrics */
214 static void test_bitmap_font(void)
215 {
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;
223
224 hdc = GetDC(0);
225
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)
229 {
230 ReleaseDC(0, hdc);
231 trace("no bitmap fonts were found, skipping the test\n");
232 return;
233 }
234
235 trace("found bitmap font %s, height %d\n", bitmap_lf.lfFaceName, bitmap_lf.lfHeight);
236
237 height_orig = bitmap_lf.lfHeight;
238 lfWidth = bitmap_lf.lfWidth;
239
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);
247
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);
253
254 bitmap_lf.lfHeight = height_orig;
255 bitmap_lf.lfWidth = lfWidth;
256
257 /* test fractional scaling */
258 for (i = 1; i <= height_orig * 3; i++)
259 {
260 INT nearest_height;
261
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);
270 }
271
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);
278
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);
285
286 ReleaseDC(0, hdc);
287 }
288
289 static INT CALLBACK find_font_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
290 {
291 LOGFONT *lf = (LOGFONT *)lParam;
292
293 if (elf->lfHeight == lf->lfHeight && !strcmp(elf->lfFaceName, lf->lfFaceName))
294 {
295 *lf = *elf;
296 return 0; /* stop enumeration */
297 }
298 return 1; /* continue enumeration */
299 }
300
301 static void test_bitmap_font_metrics(void)
302 {
303 static const struct font_data
304 {
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[] =
310 {
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 },
344 /*
345 * TODO: the system for CP932 should be NORMAL, not BOLD. However that would
346 * require a new system.sfd for that font
347 */
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 }
369
370 /* FIXME: add "Terminal" */
371 };
372 HDC hdc;
373 LOGFONT lf;
374 HFONT hfont, old_hfont;
375 TEXTMETRIC tm;
376 INT ret, i;
377
378 hdc = CreateCompatibleDC(0);
379 assert(hdc);
380
381 for (i = 0; i < sizeof(fd)/sizeof(fd[0]); i++)
382 {
383 int bit;
384
385 memset(&lf, 0, sizeof(lf));
386
387 lf.lfHeight = fd[i].height;
388 strcpy(lf.lfFaceName, fd[i].face_name);
389
390 for(bit = 0; bit < 32; bit++)
391 {
392 DWORD fs[2];
393 CHARSETINFO csi;
394
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;
399
400 lf.lfCharSet = csi.ciCharset;
401 ret = EnumFontFamiliesEx(hdc, &lf, find_font_proc, (LPARAM)&lf, 0);
402 if (ret) continue;
403
404 trace("found font %s, height %d charset %x\n", lf.lfFaceName, lf.lfHeight, lf.lfCharSet);
405
406 hfont = create_font(lf.lfFaceName, &lf);
407 old_hfont = SelectObject(hdc, hfont);
408 ok(GetTextMetrics(hdc, &tm), "GetTextMetrics error %d\n", GetLastError());
409
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);
417
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);
422
423 SelectObject(hdc, old_hfont);
424 DeleteObject(hfont);
425 }
426 }
427
428 DeleteDC(hdc);
429 }
430
431 static void test_GdiGetCharDimensions(void)
432 {
433 HDC hdc;
434 TEXTMETRICW tm;
435 LONG ret;
436 SIZE size;
437 LONG avgwidth, height;
438 static const char szAlphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
439
440 if (!pGdiGetCharDimensions)
441 {
442 skip("GdiGetCharDimensions not available on this platform\n");
443 return;
444 }
445
446 hdc = CreateCompatibleDC(NULL);
447
448 GetTextExtentPoint(hdc, szAlphabet, strlen(szAlphabet), &size);
449 avgwidth = ((size.cx / 26) + 1) / 2;
450
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);
454
455 ret = pGdiGetCharDimensions(hdc, &tm, NULL);
456 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
457
458 ret = pGdiGetCharDimensions(hdc, NULL, NULL);
459 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
460
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);
465
466 DeleteDC(hdc);
467 }
468
469 static void test_GetCharABCWidths(void)
470 {
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;
479
480 if (!pGetCharABCWidthsW || !pGetCharABCWidthsI)
481 {
482 skip("GetCharABCWidthsW/I not available on this platform\n");
483 return;
484 }
485
486 memset(&lf, 0, sizeof(lf));
487 strcpy(lf.lfFaceName, "System");
488 lf.lfHeight = 20;
489
490 hfont = CreateFontIndirectA(&lf);
491 hdc = GetDC(0);
492 hfont = SelectObject(hdc, hfont);
493
494 nb = pGetGlyphIndicesW(hdc, str, 1, glyphs, 0);
495 ok(nb == 1, "GetGlyphIndicesW should have returned 1\n");
496
497 ret = pGetCharABCWidthsI(NULL, 0, 1, glyphs, abc);
498 ok(!ret, "GetCharABCWidthsI should have failed\n");
499
500 ret = pGetCharABCWidthsI(hdc, 0, 1, glyphs, NULL);
501 ok(!ret, "GetCharABCWidthsI should have failed\n");
502
503 ret = pGetCharABCWidthsI(hdc, 0, 1, glyphs, abc);
504 ok(ret, "GetCharABCWidthsI should have succeeded\n");
505
506 ret = pGetCharABCWidthsW(NULL, 'a', 'a', abc);
507 ok(!ret, "GetCharABCWidthsW should have failed\n");
508
509 ret = pGetCharABCWidthsW(hdc, 'a', 'a', NULL);
510 ok(!ret, "GetCharABCWidthsW should have failed\n");
511
512 ret = pGetCharABCWidthsW(hdc, 'a', 'a', abc);
513 ok(!ret, "GetCharABCWidthsW should have failed\n");
514
515 hfont = SelectObject(hdc, hfont);
516 DeleteObject(hfont);
517 ReleaseDC(NULL, hdc);
518 }
519
520 static void test_text_extents(void)
521 {
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;
531
532 memset(&lf, 0, sizeof(lf));
533 strcpy(lf.lfFaceName, "Arial");
534 lf.lfHeight = 20;
535
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);
542
543 SetLastError(0xdeadbeef);
544 GetTextExtentExPointW(hdc, wt, 1, 1, &fit1, &fit2, &sz1);
545 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
546 {
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;
552 }
553
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. */
566
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",
570 i);
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);
586
587 hfont = SelectObject(hdc, hfont);
588 DeleteObject(hfont);
589 ReleaseDC(NULL, hdc);
590 }
591
592 static void test_GetGlyphIndices(void)
593 {
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;
603
604 if (!pGetGlyphIndicesW) {
605 skip("GetGlyphIndicesW not available on platform\n");
606 return;
607 }
608
609 hdc = GetDC(0);
610
611 ok(GetTextMetrics(hdc, &textm), "GetTextMetric failed\n");
612 flags |= GGI_MARK_NONEXISTING_GLYPHS;
613 charcount = pGetGlyphIndicesW(hdc, testtext, (sizeof(testtext)/2)-1, glyphs, flags);
614 ok(charcount == 5, "GetGlyphIndicesW count of glyphs should = 5 not %d\n", charcount);
615 ok((glyphs[4] == 0x001f || glyphs[4] == 0xffff /* Vista */), "GetGlyphIndicesW should have returned a nonexistent char not %04x\n", glyphs[4]);
616 flags = 0;
617 charcount = pGetGlyphIndicesW(hdc, testtext, (sizeof(testtext)/2)-1, glyphs, flags);
618 ok(charcount == 5, "GetGlyphIndicesW count of glyphs should = 5 not %d\n", charcount);
619 ok(glyphs[4] == textm.tmDefaultChar, "GetGlyphIndicesW should have returned a %04x not %04x\n",
620 textm.tmDefaultChar, glyphs[4]);
621
622 if(!is_font_installed("Tahoma"))
623 {
624 skip("Tahoma is not installed so skipping this test\n");
625 return;
626 }
627 memset(&lf, 0, sizeof(lf));
628 strcpy(lf.lfFaceName, "Tahoma");
629 lf.lfHeight = 20;
630
631 hfont = CreateFontIndirectA(&lf);
632 hOldFont = SelectObject(hdc, hfont);
633 ok(GetTextMetrics(hdc, &textm), "GetTextMetric failed\n");
634 flags |= GGI_MARK_NONEXISTING_GLYPHS;
635 charcount = pGetGlyphIndicesW(hdc, testtext, (sizeof(testtext)/2)-1, glyphs, flags);
636 ok(charcount == 5, "GetGlyphIndicesW count of glyphs should = 5 not %d\n", charcount);
637 ok(glyphs[4] == 0xffff, "GetGlyphIndicesW should have returned 0xffff char not %04x\n", glyphs[4]);
638 flags = 0;
639 testtext[0] = textm.tmDefaultChar;
640 charcount = pGetGlyphIndicesW(hdc, testtext, (sizeof(testtext)/2)-1, glyphs, flags);
641 ok(charcount == 5, "GetGlyphIndicesW count of glyphs should = 5 not %d\n", charcount);
642 todo_wine ok(glyphs[0] == 0, "GetGlyphIndicesW for tmDefaultChar should be 0 not %04x\n", glyphs[0]);
643 ok(glyphs[4] == 0, "GetGlyphIndicesW should have returned 0 not %04x\n", glyphs[4]);
644 DeleteObject(SelectObject(hdc, hOldFont));
645 }
646
647 static void test_GetKerningPairs(void)
648 {
649 static const struct kerning_data
650 {
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[] =
669 {
670 {"Arial", 12, 12, 9, 3,
671 2048, 7, -2, 1, 5, 2, 8, -2, 0, 9,
672 26,
673 {
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}
681 }
682 },
683 {"Arial", -34, 39, 32, 7,
684 2048, 25, -7, 5, 17, 9, 31, -7, 1, 9,
685 26,
686 {
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}
694 }
695 },
696 { "Arial", 120, 120, 97, 23,
697 2048, 79, -23, 16, 54, 27, 98, -23, 4, 9,
698 26,
699 {
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}
707 }
708 },
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,
712 26,
713 {
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}
721 }
722 }
723 #endif
724 };
725 LOGFONT lf;
726 HFONT hfont, hfont_old;
727 KERNINGPAIR *kern_pair;
728 HDC hdc;
729 DWORD total_kern_pairs, ret, i, n, matches;
730
731 hdc = GetDC(0);
732
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.
735 */
736 SetLastError(0xdeadbeef);
737 GetKerningPairsW(hdc, 0, NULL);
738 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
739 {
740 skip("Skipping the GetKerningPairs test on a Win9x platform\n");
741 ReleaseDC(0, hdc);
742 return;
743 }
744
745 for (i = 0; i < sizeof(kd)/sizeof(kd[0]); i++)
746 {
747 OUTLINETEXTMETRICW otm;
748
749 if (!is_font_installed(kd[i].face_name))
750 {
751 trace("%s is not installed so skipping this test\n", kd[i].face_name);
752 continue;
753 }
754
755 trace("testing font %s, height %d\n", kd[i].face_name, kd[i].height);
756
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);
762
763 hfont_old = SelectObject(hdc, hfont);
764
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());
768
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);
775
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);
798 }
799
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));
803
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
811
812 ret = GetKerningPairsW(hdc, 100, NULL);
813 ok(ret == total_kern_pairs, "got %u, expected %u\n", ret, total_kern_pairs);
814
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);
817
818 ret = GetKerningPairsW(hdc, total_kern_pairs, kern_pair);
819 ok(ret == total_kern_pairs, "got %u, expected %u\n", ret, total_kern_pairs);
820
821 matches = 0;
822
823 for (n = 0; n < ret; n++)
824 {
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++)
832 {
833 if (kern_pair[n].wFirst == kd[i].kern_pair[j].wFirst &&
834 kern_pair[n].wSecond == kd[i].kern_pair[j].wSecond)
835 {
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++;
841 }
842 }
843 }
844
845 ok(matches == kd[i].total_kern_pairs, "got matches %u, expected %u\n",
846 matches, kd[i].total_kern_pairs);
847
848 HeapFree(GetProcessHeap(), 0, kern_pair);
849
850 SelectObject(hdc, hfont_old);
851 DeleteObject(hfont);
852 }
853
854 ReleaseDC(0, hdc);
855 }
856
857 static void test_GetOutlineTextMetrics(void)
858 {
859 OUTLINETEXTMETRIC *otm;
860 LOGFONT lf;
861 HFONT hfont, hfont_old;
862 HDC hdc;
863 DWORD ret, otm_size;
864
865 if (!is_font_installed("Arial"))
866 {
867 skip("Arial is not installed\n");
868 return;
869 }
870
871 hdc = GetDC(0);
872
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);
881
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);
885
886 otm = HeapAlloc(GetProcessHeap(), 0, otm_size);
887
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 */
896 {
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);
901 }
902
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 */
911 {
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);
916 }
917
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 */
927 {
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);
931 }
932 ok(otm->otmpFullName == (LPSTR)0xAAAAAAAA, "expected 0xAAAAAAAA got %p\n", otm->otmpFullName);
933
934 HeapFree(GetProcessHeap(), 0, otm);
935
936 SelectObject(hdc, hfont_old);
937 DeleteObject(hfont);
938
939 ReleaseDC(0, hdc);
940 }
941
942 static void testJustification(HDC hdc, PSTR str, RECT *clientArea)
943 {
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
955 {
956 char extent[100];
957 int GetTextExtentExPointWWidth;
958 int TabbedTextOutWidth;
959 } error[10];
960
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;
967
968 do {
969 pLastChar = str;
970
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);
979
980 /* ignore trailing break chars */
981 breakCount--;
982 while (*(pLastChar - 1) == tm.tmBreakChar)
983 {
984 pLastChar--;
985 breakCount--;
986 }
987
988 if (*str == '\0' || breakCount <= 0) pLastChar = str;
989
990 SetTextJustification(hdc, 0, 0);
991 GetTextExtentPoint32(hdc, pFirstChar, pLastChar - pFirstChar, &size);
992
993 /* do not justify the last extent */
994 if (*str != '\0' && breakCount > 0)
995 {
996 SetTextJustification(hdc, areaWidth - size.cx, breakCount);
997 GetTextExtentPoint32(hdc, pFirstChar, pLastChar - pFirstChar, &size);
998 justifiedWidth = size.cx;
999 }
1000 else lastExtent = TRUE;
1001
1002 x = clientArea->left;
1003
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)))
1009 {
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++;
1015 }
1016
1017 y += size.cy;
1018 str = pLastChar;
1019 } while (*str && y < clientArea->bottom);
1020
1021 for (e = 0; e < nErrors; e++)
1022 {
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);
1031 }
1032 }
1033
1034 static void test_SetTextJustification(void)
1035 {
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.";
1049
1050 hwnd = CreateWindowExA(0, "static", "", WS_POPUP, 0,0, 400,400, 0, 0, 0, NULL);
1051 GetClientRect( hwnd, &clientArea );
1052 hdc = GetDC( hwnd );
1053
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);
1063
1064 testJustification(hdc, testText, &clientArea);
1065
1066 DeleteObject(hfont);
1067 ReleaseDC(hwnd, hdc);
1068 DestroyWindow(hwnd);
1069 }
1070
1071 static BOOL get_glyph_indices(INT charset, UINT code_page, WORD *idx, UINT count, BOOL unicode)
1072 {
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];
1081
1082 assert(count <= 128);
1083
1084 memset(&lf, 0, sizeof(lf));
1085
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());
1092
1093 hdc = GetDC(0);
1094 hfont_old = SelectObject(hdc, hfont);
1095
1096 cs = GetTextCharsetInfo(hdc, &fs, 0);
1097 ok(cs == charset, "expected %d, got %d\n", charset, cs);
1098
1099 SetLastError(0xdeadbeef);
1100 ret = GetTextFace(hdc, sizeof(name), name);
1101 ok(ret, "GetTextFace error %u\n", GetLastError());
1102
1103 if (charset == SYMBOL_CHARSET)
1104 {
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");
1107 }
1108 else
1109 {
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");
1112 }
1113
1114 if (!TranslateCharsetInfo((DWORD *)cs, &csi, TCI_SRCCHARSET))
1115 {
1116 trace("Can't find codepage for charset %d\n", cs);
1117 ReleaseDC(0, hdc);
1118 return FALSE;
1119 }
1120 ok(csi.ciACP == code_page, "expected %d, got %d\n", code_page, csi.ciACP);
1121
1122 if (unicode)
1123 {
1124 char ansi_buf[128];
1125 WCHAR unicode_buf[128];
1126
1127 for (i = 0; i < count; i++) ansi_buf[i] = (BYTE)(i + 128);
1128
1129 MultiByteToWideChar(code_page, 0, ansi_buf, count, unicode_buf, count);
1130
1131 SetLastError(0xdeadbeef);
1132 ret = pGetGlyphIndicesW(hdc, unicode_buf, count, idx, 0);
1133 ok(ret == count, "GetGlyphIndicesW error %u\n", GetLastError());
1134 }
1135 else
1136 {
1137 char ansi_buf[128];
1138
1139 for (i = 0; i < count; i++) ansi_buf[i] = (BYTE)(i + 128);
1140
1141 SetLastError(0xdeadbeef);
1142 ret = pGetGlyphIndicesA(hdc, ansi_buf, count, idx, 0);
1143 ok(ret == count, "GetGlyphIndicesA error %u\n", GetLastError());
1144 }
1145
1146 SelectObject(hdc, hfont_old);
1147 DeleteObject(hfont);
1148
1149 ReleaseDC(0, hdc);
1150
1151 return TRUE;
1152 }
1153
1154 static void test_font_charset(void)
1155 {
1156 static struct charset_data
1157 {
1158 INT charset;
1159 UINT code_page;
1160 WORD font_idxA[128], font_idxW[128];
1161 } cd[] =
1162 {
1163 { ANSI_CHARSET, 1252 },
1164 { RUSSIAN_CHARSET, 1251 },
1165 { SYMBOL_CHARSET, CP_SYMBOL } /* keep it as the last one */
1166 };
1167 int i;
1168
1169 if (!pGetGlyphIndicesA || !pGetGlyphIndicesW)
1170 {
1171 skip("Skipping the font charset test on a Win9x platform\n");
1172 return;
1173 }
1174
1175 if (!is_font_installed("Arial"))
1176 {
1177 skip("Arial is not installed\n");
1178 return;
1179 }
1180
1181 for (i = 0; i < sizeof(cd)/sizeof(cd[0]); i++)
1182 {
1183 if (cd[i].charset == SYMBOL_CHARSET)
1184 {
1185 if (!is_font_installed("Symbol") && !is_font_installed("Wingdings"))
1186 {
1187 skip("Symbol or Wingdings is not installed\n");
1188 break;
1189 }
1190 }
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);
1194 }
1195
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)
1198 {
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");
1201 }
1202 else
1203 skip("Symbol or Wingdings is not installed\n");
1204 }
1205
1206 static void test_GetFontUnicodeRanges(void)
1207 {
1208 LOGFONTA lf;
1209 HDC hdc;
1210 HFONT hfont, hfont_old;
1211 DWORD size;
1212 GLYPHSET *gs;
1213
1214 if (!pGetFontUnicodeRanges)
1215 {
1216 skip("GetFontUnicodeRanges not available before W2K\n");
1217 return;
1218 }
1219
1220 memset(&lf, 0, sizeof(lf));
1221 lstrcpyA(lf.lfFaceName, "Arial");
1222 hfont = create_font("Arial", &lf);
1223
1224 hdc = GetDC(0);
1225 hfont_old = SelectObject(hdc, hfont);
1226
1227 size = pGetFontUnicodeRanges(NULL, NULL);
1228 ok(!size, "GetFontUnicodeRanges succeeded unexpectedly\n");
1229
1230 size = pGetFontUnicodeRanges(hdc, NULL);
1231 ok(size, "GetFontUnicodeRanges failed unexpectedly\n");
1232
1233 gs = HeapAlloc(GetProcessHeap(), 0, size);
1234
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);
1242
1243 HeapFree(GetProcessHeap(), 0, gs);
1244
1245 SelectObject(hdc, hfont_old);
1246 DeleteObject(hfont);
1247 ReleaseDC(NULL, hdc);
1248 }
1249
1250 #define MAX_ENUM_FONTS 256
1251
1252 struct enum_font_data
1253 {
1254 int total;
1255 LOGFONT lf[MAX_ENUM_FONTS];
1256 };
1257
1258 static INT CALLBACK arial_enum_proc(const LOGFONT *lf, const TEXTMETRIC *tm, DWORD type, LPARAM lParam)
1259 {
1260 struct enum_font_data *efd = (struct enum_font_data *)lParam;
1261
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;
1269
1270 return 1;
1271 }
1272
1273 static void get_charset_stats(struct enum_font_data *efd,
1274 int *ansi_charset, int *symbol_charset,
1275 int *russian_charset)
1276 {
1277 int i;
1278
1279 *ansi_charset = 0;
1280 *symbol_charset = 0;
1281 *russian_charset = 0;
1282
1283 for (i = 0; i < efd->total; i++)
1284 {
1285 switch (efd->lf[i].lfCharSet)
1286 {
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;
1296 }
1297 }
1298 }
1299
1300 static void test_EnumFontFamilies(const char *font_name, INT font_charset)
1301 {
1302 struct enum_font_data efd;
1303 LOGFONT lf;
1304 HDC hdc;
1305 int i, ret, ansi_charset, symbol_charset, russian_charset;
1306
1307 trace("Testing font %s, charset %d\n", *font_name ? font_name : "<empty>", font_charset);
1308
1309 if (*font_name && !is_truetype_font_installed(font_name))
1310 {
1311 skip("%s is not installed\n", font_name);
1312 return;
1313 }
1314
1315 hdc = GetDC(0);
1316
1317 /* Observed behaviour: EnumFontFamilies enumerates aliases like "Arial Cyr"
1318 * while EnumFontFamiliesEx doesn't.
1319 */
1320 if (!*font_name && font_charset == DEFAULT_CHARSET) /* do it only once */
1321 {
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");
1333
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");
1345 }
1346
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++)
1360 {
1361 /* FIXME: remove completely once Wine is fixed */
1362 if (efd.lf[i].lfCharSet != font_charset)
1363 {
1364 todo_wine
1365 ok(efd.lf[i].lfCharSet == font_charset, "%d: got charset %d\n", i, efd.lf[i].lfCharSet);
1366 }
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);
1371 }
1372
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)
1385 {
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);
1390 }
1391 else
1392 {
1393 ok(efd.total > 0, "no fonts enumerated: %s ANSI_CHARSET\n", font_name);
1394 for (i = 0; i < efd.total; i++)
1395 {
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);
1400 }
1401 }
1402
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++)
1417 {
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);
1421 }
1422 if (*font_name)
1423 {
1424 switch (font_charset)
1425 {
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;
1450 }
1451 }
1452 else
1453 {
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>");
1460 }
1461
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
1476 {
1477 ok(efd.total > 0, "no fonts enumerated: %s SYMBOL_CHARSET\n", font_name);
1478 for (i = 0; i < efd.total; i++)
1479 {
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);
1484 }
1485
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>");
1492 }
1493
1494 ReleaseDC(0, hdc);
1495 }
1496
1497 static void test_negative_width(HDC hdc, const LOGFONTA *lf)
1498 {
1499 HFONT hfont, hfont_prev;
1500 DWORD ret;
1501 GLYPHMETRICS gm1, gm2;
1502 LOGFONTA lf2 = *lf;
1503 WORD idx;
1504 MAT2 mat = { {0,1}, {0,0}, {0,0}, {0,1} };
1505
1506 /* negative widths are handled just as positive ones */
1507 lf2.lfWidth = -lf->lfWidth;
1508
1509 SetLastError(0xdeadbeef);
1510 hfont = CreateFontIndirectA(lf);
1511 ok(hfont != 0, "CreateFontIndirect error %u\n", GetLastError());
1512 check_font("original", lf, hfont);
1513
1514 hfont_prev = SelectObject(hdc, hfont);
1515
1516 ret = pGetGlyphIndicesA(hdc, "x", 1, &idx, GGI_MARK_NONEXISTING_GLYPHS);
1517 if (ret == GDI_ERROR || idx == 0xffff)
1518 {
1519 SelectObject(hdc, hfont_prev);
1520 DeleteObject(hfont);
1521 skip("Font %s doesn't contain 'x', skipping the test\n", lf->lfFaceName);
1522 return;
1523 }
1524
1525 /* filling with 0xaa causes false pass under WINEDEBUG=warn+heap */
1526 memset(&gm1, 0xab, sizeof(gm1));
1527 SetLastError(0xdeadbeef);
1528 ret = GetGlyphOutlineA(hdc, 'x', GGO_METRICS, &gm1, 0, NULL, &mat);
1529 ok(ret != GDI_ERROR, "GetGlyphOutline error 0x%x\n", GetLastError());
1530
1531 SelectObject(hdc, hfont_prev);
1532 DeleteObject(hfont);
1533
1534 SetLastError(0xdeadbeef);
1535 hfont = CreateFontIndirectA(&lf2);
1536 ok(hfont != 0, "CreateFontIndirect error %u\n", GetLastError());
1537 check_font("negative width", &lf2, hfont);
1538
1539 hfont_prev = SelectObject(hdc, hfont);
1540
1541 memset(&gm2, 0xbb, sizeof(gm2));
1542 SetLastError(0xdeadbeef);
1543 ret = GetGlyphOutlineA(hdc, 'x', GGO_METRICS, &gm2, 0, NULL, &mat);
1544 ok(ret != GDI_ERROR, "GetGlyphOutline error 0x%x\n", GetLastError());
1545
1546 SelectObject(hdc, hfont_prev);
1547 DeleteObject(hfont);
1548
1549 ok(gm1.gmBlackBoxX == gm2.gmBlackBoxX &&
1550 gm1.gmBlackBoxY == gm2.gmBlackBoxY &&
1551 gm1.gmptGlyphOrigin.x == gm2.gmptGlyphOrigin.x &&
1552 gm1.gmptGlyphOrigin.y == gm2.gmptGlyphOrigin.y &&
1553 gm1.gmCellIncX == gm2.gmCellIncX &&
1554 gm1.gmCellIncY == gm2.gmCellIncY,
1555 "gm1=%d,%d,%d,%d,%d,%d gm2=%d,%d,%d,%d,%d,%d\n",
1556 gm1.gmBlackBoxX, gm1.gmBlackBoxY, gm1.gmptGlyphOrigin.x,
1557 gm1.gmptGlyphOrigin.y, gm1.gmCellIncX, gm1.gmCellIncY,
1558 gm2.gmBlackBoxX, gm2.gmBlackBoxY, gm2.gmptGlyphOrigin.x,
1559 gm2.gmptGlyphOrigin.y, gm2.gmCellIncX, gm2.gmCellIncY);
1560 }
1561
1562 /* PANOSE is 10 bytes in size, need to pack the structure properly */
1563 #include "pshpack2.h"
1564 typedef struct
1565 {
1566 USHORT version;
1567 SHORT xAvgCharWidth;
1568 USHORT usWeightClass;
1569 USHORT usWidthClass;
1570 SHORT fsType;
1571 SHORT ySubscriptXSize;
1572 SHORT ySubscriptYSize;
1573 SHORT ySubscriptXOffset;
1574 SHORT ySubscriptYOffset;
1575 SHORT ySuperscriptXSize;
1576 SHORT ySuperscriptYSize;
1577 SHORT ySuperscriptXOffset;
1578 SHORT ySuperscriptYOffset;
1579 SHORT yStrikeoutSize;
1580 SHORT yStrikeoutPosition;
1581 SHORT sFamilyClass;
1582 PANOSE panose;
1583 ULONG ulUnicodeRange1;
1584 ULONG ulUnicodeRange2;
1585 ULONG ulUnicodeRange3;
1586 ULONG ulUnicodeRange4;
1587 CHAR achVendID[4];
1588 USHORT fsSelection;
1589 USHORT usFirstCharIndex;
1590 USHORT usLastCharIndex;
1591 /* According to the Apple spec, original version didn't have the below fields,
1592 * version numbers were taked from the OpenType spec.
1593 */
1594 /* version 0 (TrueType 1.5) */
1595 USHORT sTypoAscender;
1596 USHORT sTypoDescender;
1597 USHORT sTypoLineGap;
1598 USHORT usWinAscent;
1599 USHORT usWinDescent;
1600 /* version 1 (TrueType 1.66) */
1601 ULONG ulCodePageRange1;
1602 ULONG ulCodePageRange2;
1603 /* version 2 (OpenType 1.2) */
1604 SHORT sxHeight;
1605 SHORT sCapHeight;
1606 USHORT usDefaultChar;
1607 USHORT usBreakChar;
1608 USHORT usMaxContext;
1609 } TT_OS2_V2;
1610 #include "poppack.h"
1611
1612 #ifdef WORDS_BIGENDIAN
1613 #define GET_BE_WORD(x) (x)
1614 #else
1615 #define GET_BE_WORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
1616 #endif
1617
1618 #define MS_MAKE_TAG(ch0, ch1, ch2, ch3) \
1619 ((DWORD)(BYTE)(ch0) | ((DWORD)(BYTE)(ch1) << 8) | \
1620 ((DWORD)(BYTE)(ch2) << 16) | ((DWORD)(BYTE)(ch3) << 24))
1621 #define MS_OS2_TAG MS_MAKE_TAG('O','S','/','2')
1622
1623 static void test_text_metrics(const LOGFONTA *lf)
1624 {
1625 HDC hdc;
1626 HFONT hfont, hfont_old;
1627 TEXTMETRICA tmA;
1628 TEXTMETRICW tmW;
1629 UINT first_unicode_char, last_unicode_char, default_char, break_char;
1630 INT test_char;
1631 TT_OS2_V2 tt_os2;
1632 USHORT version;
1633 LONG size, ret;
1634 const char *font_name = lf->lfFaceName;
1635
1636 trace("Testing font metrics for %s, charset %d\n", font_name, lf->lfCharSet);
1637
1638 hdc = GetDC(0);
1639
1640 SetLastError(0xdeadbeef);
1641 hfont = CreateFontIndirectA(lf);
1642 ok(hfont != 0, "CreateFontIndirect error %u\n", GetLastError());
1643
1644 hfont_old = SelectObject(hdc, hfont);
1645
1646 size = GetFontData(hdc, MS_OS2_TAG, 0, NULL, 0);
1647 if (size == GDI_ERROR)
1648 {
1649 trace("OS/2 chunk was not found\n");
1650 goto end_of_test;
1651 }
1652 if (size > sizeof(tt_os2))
1653 {
1654 trace("got too large OS/2 chunk of size %u\n", size);
1655 size = sizeof(tt_os2);
1656 }
1657
1658 memset(&tt_os2, 0, sizeof(tt_os2));
1659 ret = GetFontData(hdc, MS_OS2_TAG, 0, &tt_os2, size);
1660 ok(ret == size, "GetFontData should return %u not %u\n", size, ret);
1661
1662 version = GET_BE_WORD(tt_os2.version);
1663 trace("OS/2 chunk version %u, vendor %4.4s\n", version, (LPCSTR)&tt_os2.achVendID);
1664
1665 first_unicode_char = GET_BE_WORD(tt_os2.usFirstCharIndex);
1666 last_unicode_char = GET_BE_WORD(tt_os2.usLastCharIndex);
1667 default_char = GET_BE_WORD(tt_os2.usDefaultChar);
1668 break_char = GET_BE_WORD(tt_os2.usBreakChar);
1669
1670 trace("for %s first %x, last %x, default %x, break %x\n", font_name,
1671 first_unicode_char, last_unicode_char, default_char, break_char);
1672
1673 SetLastError(0xdeadbeef);
1674 ret = GetTextMetricsA(hdc, &tmA);
1675 ok(ret, "GetTextMetricsA error %u\n", GetLastError());
1676 trace("A: first %x, last %x, default %x, break %x\n",
1677 tmA.tmFirstChar, tmA.tmLastChar, tmA.tmDefaultChar, tmA.tmBreakChar);
1678
1679 #if 0 /* FIXME: This doesn't appear to be what Windows does */
1680 test_char = min(first_unicode_char - 1, 255);
1681 ok(tmA.tmFirstChar == test_char, "A: tmFirstChar for %s %02x != %02x\n",
1682 font_name, tmA.tmFirstChar, test_char);
1683 #endif
1684 if (lf->lfCharSet == SYMBOL_CHARSET)
1685 {
1686 test_char = min(last_unicode_char - 0xf000, 255);
1687 ok(tmA.tmLastChar == test_char, "A: tmLastChar for %s %02x != %02x\n",
1688 font_name, tmA.tmLastChar, test_char);
1689 }
1690 else
1691 {
1692 test_char = min(last_unicode_char, 255);
1693 ok(tmA.tmLastChar == test_char, "A: tmLastChar for %s %02x != %02x\n",
1694 font_name, tmA.tmLastChar, test_char);
1695 }
1696
1697 SetLastError(0xdeadbeef);
1698 ret = GetTextMetricsW(hdc, &tmW);
1699 ok(ret || GetLastError() == ERROR_CALL_NOT_IMPLEMENTED,
1700 "GetTextMetricsW error %u\n", GetLastError());
1701 if (ret)
1702 {
1703 trace("W: first %x, last %x, default %x, break %x\n",
1704 tmW.tmFirstChar, tmW.tmLastChar, tmW.tmDefaultChar,
1705 tmW.tmBreakChar);
1706
1707 if (lf->lfCharSet == SYMBOL_CHARSET)
1708 {
1709 /* It appears that for fonts with SYMBOL_CHARSET Windows always
1710 * sets symbol range to 0 - f0ff
1711 */
1712 ok(tmW.tmFirstChar == 0, "W: tmFirstChar for %s %02x != 0\n",
1713 font_name, tmW.tmFirstChar);
1714 /* FIXME: Windows returns f0ff here, while Wine f0xx */
1715 ok(tmW.tmLastChar >= 0xf000, "W: tmLastChar for %s %02x < 0xf000\n",
1716 font_name, tmW.tmLastChar);
1717
1718 ok(tmW.tmDefaultChar == 0x1f, "W: tmDefaultChar for %s %02x != 0x1f\n",
1719 font_name, tmW.tmDefaultChar);
1720 ok(tmW.tmBreakChar == 0x20, "W: tmBreakChar for %s %02x != 0x20\n",
1721 font_name, tmW.tmBreakChar);
1722 }
1723 else
1724 {
1725 ok(tmW.tmFirstChar == first_unicode_char, "W: tmFirstChar for %s %02x != %02x\n",
1726 font_name, tmW.tmFirstChar, first_unicode_char);
1727 ok(tmW.tmLastChar == last_unicode_char, "W: tmLastChar for %s %02x != %02x\n",
1728 font_name, tmW.tmLastChar, last_unicode_char);
1729 }
1730 ret = GetDeviceCaps(hdc, LOGPIXELSX);
1731 ok(tmW.tmDigitizedAspectX == ret, "W: tmDigitizedAspectX %u != %u\n",
1732 tmW.tmDigitizedAspectX, ret);
1733 ret = GetDeviceCaps(hdc, LOGPIXELSY);
1734 ok(tmW.tmDigitizedAspectX == ret, "W: tmDigitizedAspectY %u != %u\n",
1735 tmW.tmDigitizedAspectX, ret);
1736 }
1737
1738 test_negative_width(hdc, lf);
1739
1740 end_of_test:
1741 SelectObject(hdc, hfont_old);
1742 DeleteObject(hfont);
1743
1744 ReleaseDC(0, hdc);
1745 }
1746
1747 static INT CALLBACK enum_truetype_font_proc(const LOGFONT *lf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
1748 {
1749 INT *enumed = (INT *)lParam;
1750
1751 if (type == TRUETYPE_FONTTYPE)
1752 {
1753 (*enumed)++;
1754 test_text_metrics(lf);
1755 }
1756 return 1;
1757 }
1758
1759 static void test_GetTextMetrics(void)
1760 {
1761 LOGFONTA lf;
1762 HDC hdc;
1763 INT enumed;
1764
1765 hdc = GetDC(0);
1766
1767 memset(&lf, 0, sizeof(lf));
1768 lf.lfCharSet = DEFAULT_CHARSET;
1769 enumed = 0;
1770 EnumFontFamiliesExA(hdc, &lf, enum_truetype_font_proc, (LPARAM)&enumed, 0);
1771 trace("Tested metrics of %d truetype fonts\n", enumed);
1772
1773 ReleaseDC(0, hdc);
1774 }
1775
1776 static void test_nonexistent_font(void)
1777 {
1778 LOGFONTA lf;
1779 HDC hdc;
1780 HFONT hfont;
1781 char buf[LF_FACESIZE];
1782
1783 if (!is_truetype_font_installed("Arial Black"))
1784 {
1785 skip("Arial not installed\n");
1786 return;
1787 }
1788
1789 hdc = GetDC(0);
1790
1791 memset(&lf, 0, sizeof(lf));
1792 lf.lfHeight = 100;
1793 lf.lfWeight = FW_REGULAR;
1794 lf.lfCharSet = ANSI_CHARSET;
1795 lf.lfPitchAndFamily = FF_SWISS;
1796 strcpy(lf.lfFaceName, "Nonexistent font");
1797
1798 hfont = CreateFontIndirectA(&lf);
1799 hfont = SelectObject(hdc, hfont);
1800 GetTextFaceA(hdc, sizeof(buf), buf);
1801 ok(!lstrcmpiA(buf, "Arial"), "Got %s\n", buf);
1802 DeleteObject(SelectObject(hdc, hfont));
1803 ReleaseDC(0, hdc);
1804 }
1805
1806 static void test_GdiRealizationInfo(void)
1807 {
1808 HDC hdc;
1809 DWORD info[4];
1810 BOOL r;
1811 HFONT hfont, hfont_old;
1812 LOGFONTA lf;
1813
1814 if(!pGdiRealizationInfo)
1815 {
1816 skip("GdiRealizationInfo not available\n");
1817 return;
1818 }
1819
1820 hdc = GetDC(0);
1821
1822 memset(info, 0xcc, sizeof(info));
1823 r = pGdiRealizationInfo(hdc, info);
1824 ok(r != 0, "ret 0\n");
1825 ok(info[0] == 1, "info[0] = %x for the system font\n", info[0]);
1826 ok(info[3] == 0xcccccccc, "structure longer than 3 dwords\n");
1827
1828 if (!is_truetype_font_installed("Arial"))
1829 {
1830 skip("skipping GdiRealizationInfo with truetype font\n");
1831 goto end;
1832 }
1833
1834 memset(&lf, 0, sizeof(lf));
1835 strcpy(lf.lfFaceName, "Arial");
1836 lf.lfHeight = 20;
1837 lf.lfWeight = FW_NORMAL;
1838 hfont = CreateFontIndirectA(&lf);
1839 hfont_old = SelectObject(hdc, hfont);
1840
1841 memset(info, 0xcc, sizeof(info));
1842 r = pGdiRealizationInfo(hdc, info);
1843 ok(r != 0, "ret 0\n");
1844 ok(info[0] == 3, "info[0] = %x for arial\n", info[0]);
1845 ok(info[3] == 0xcccccccc, "structure longer than 3 dwords\n");
1846
1847 DeleteObject(SelectObject(hdc, hfont_old));
1848
1849 end:
1850 ReleaseDC(0, hdc);
1851 }
1852
1853 START_TEST(font)
1854 {
1855 init();
1856
1857 test_logfont();
1858 test_bitmap_font();
1859 test_bitmap_font_metrics();
1860 test_GdiGetCharDimensions();
1861 test_GetCharABCWidths();
1862 test_text_extents();
1863 test_GetGlyphIndices();
1864 test_GetKerningPairs();
1865 test_GetOutlineTextMetrics();
1866 test_SetTextJustification();
1867 test_font_charset();
1868 test_GetFontUnicodeRanges();
1869 test_nonexistent_font();
1870
1871 /* On Windows Arial has a lot of default charset aliases such as Arial Cyr,
1872 * I'd like to avoid them in this test.
1873 */
1874 test_EnumFontFamilies("Arial Black", ANSI_CHARSET);
1875 test_EnumFontFamilies("Symbol", SYMBOL_CHARSET);
1876 if (is_truetype_font_installed("Arial Black") &&
1877 (is_truetype_font_installed("Symbol") || is_truetype_font_installed("Wingdings")))
1878 {
1879 test_EnumFontFamilies("", ANSI_CHARSET);
1880 test_EnumFontFamilies("", SYMBOL_CHARSET);
1881 test_EnumFontFamilies("", DEFAULT_CHARSET);
1882 }
1883 else
1884 skip("Arial Black or Symbol/Wingdings is not installed\n");
1885 test_GetTextMetrics();
1886 test_GdiRealizationInfo();
1887 }