[ROSTESTS]
[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 /* Do not allow more than 1 deviation here */
34 #define match_off_by_1(a, b) (abs((a) - (b)) <= 1)
35
36 #define near_match(a, b) (abs((a) - (b)) <= 6)
37 #define expect(expected, got) ok(got == expected, "Expected %.8x, got %.8x\n", expected, got)
38
39 LONG (WINAPI *pGdiGetCharDimensions)(HDC hdc, LPTEXTMETRICW lptm, LONG *height);
40 BOOL (WINAPI *pGetCharABCWidthsI)(HDC hdc, UINT first, UINT count, LPWORD glyphs, LPABC abc);
41 BOOL (WINAPI *pGetCharABCWidthsW)(HDC hdc, UINT first, UINT last, LPABC abc);
42 DWORD (WINAPI *pGetFontUnicodeRanges)(HDC hdc, LPGLYPHSET lpgs);
43 DWORD (WINAPI *pGetGlyphIndicesA)(HDC hdc, LPCSTR lpstr, INT count, LPWORD pgi, DWORD flags);
44 DWORD (WINAPI *pGetGlyphIndicesW)(HDC hdc, LPCWSTR lpstr, INT count, LPWORD pgi, DWORD flags);
45 BOOL (WINAPI *pGdiRealizationInfo)(HDC hdc, DWORD *);
46 HFONT (WINAPI *pCreateFontIndirectExA)(const ENUMLOGFONTEXDV *);
47
48 static HMODULE hgdi32 = 0;
49
50 static void init(void)
51 {
52 hgdi32 = GetModuleHandleA("gdi32.dll");
53
54 pGdiGetCharDimensions = (void *)GetProcAddress(hgdi32, "GdiGetCharDimensions");
55 pGetCharABCWidthsI = (void *)GetProcAddress(hgdi32, "GetCharABCWidthsI");
56 pGetCharABCWidthsW = (void *)GetProcAddress(hgdi32, "GetCharABCWidthsW");
57 pGetFontUnicodeRanges = (void *)GetProcAddress(hgdi32, "GetFontUnicodeRanges");
58 pGetGlyphIndicesA = (void *)GetProcAddress(hgdi32, "GetGlyphIndicesA");
59 pGetGlyphIndicesW = (void *)GetProcAddress(hgdi32, "GetGlyphIndicesW");
60 pGdiRealizationInfo = (void *)GetProcAddress(hgdi32, "GdiRealizationInfo");
61 pCreateFontIndirectExA = (void *)GetProcAddress(hgdi32, "CreateFontIndirectExA");
62 }
63
64 static INT CALLBACK is_truetype_font_installed_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
65 {
66 if (type != TRUETYPE_FONTTYPE) return 1;
67
68 return 0;
69 }
70
71 static BOOL is_truetype_font_installed(const char *name)
72 {
73 HDC hdc = GetDC(0);
74 BOOL ret = FALSE;
75
76 if (!EnumFontFamiliesA(hdc, name, is_truetype_font_installed_proc, 0))
77 ret = TRUE;
78
79 ReleaseDC(0, hdc);
80 return ret;
81 }
82
83 static INT CALLBACK is_font_installed_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
84 {
85 return 0;
86 }
87
88 static BOOL is_font_installed(const char *name)
89 {
90 HDC hdc = GetDC(0);
91 BOOL ret = FALSE;
92
93 if(!EnumFontFamiliesA(hdc, name, is_font_installed_proc, 0))
94 ret = TRUE;
95
96 ReleaseDC(0, hdc);
97 return ret;
98 }
99
100 static void check_font(const char* test, const LOGFONTA* lf, HFONT hfont)
101 {
102 LOGFONTA getobj_lf;
103 int ret, minlen = 0;
104
105 if (!hfont)
106 return;
107
108 ret = GetObject(hfont, sizeof(getobj_lf), &getobj_lf);
109 /* NT4 tries to be clever and only returns the minimum length */
110 while (lf->lfFaceName[minlen] && minlen < LF_FACESIZE-1)
111 minlen++;
112 minlen += FIELD_OFFSET(LOGFONTA, lfFaceName) + 1;
113 ok(ret == sizeof(LOGFONTA) || ret == minlen, "%s: GetObject returned %d\n", test, ret);
114 ok(lf->lfHeight == getobj_lf.lfHeight ||
115 broken((SHORT)lf->lfHeight == getobj_lf.lfHeight), /* win9x */
116 "lfHeight: expect %08x got %08x\n", lf->lfHeight, getobj_lf.lfHeight);
117 ok(lf->lfWidth == getobj_lf.lfWidth ||
118 broken((SHORT)lf->lfWidth == getobj_lf.lfWidth), /* win9x */
119 "lfWidth: expect %08x got %08x\n", lf->lfWidth, getobj_lf.lfWidth);
120 ok(lf->lfEscapement == getobj_lf.lfEscapement ||
121 broken((SHORT)lf->lfEscapement == getobj_lf.lfEscapement), /* win9x */
122 "lfEscapement: expect %08x got %08x\n", lf->lfEscapement, getobj_lf.lfEscapement);
123 ok(lf->lfOrientation == getobj_lf.lfOrientation ||
124 broken((SHORT)lf->lfOrientation == getobj_lf.lfOrientation), /* win9x */
125 "lfOrientation: expect %08x got %08x\n", lf->lfOrientation, getobj_lf.lfOrientation);
126 ok(lf->lfWeight == getobj_lf.lfWeight ||
127 broken((SHORT)lf->lfWeight == getobj_lf.lfWeight), /* win9x */
128 "lfWeight: expect %08x got %08x\n", lf->lfWeight, getobj_lf.lfWeight);
129 ok(lf->lfItalic == getobj_lf.lfItalic, "lfItalic: expect %02x got %02x\n", lf->lfItalic, getobj_lf.lfItalic);
130 ok(lf->lfUnderline == getobj_lf.lfUnderline, "lfUnderline: expect %02x got %02x\n", lf->lfUnderline, getobj_lf.lfUnderline);
131 ok(lf->lfStrikeOut == getobj_lf.lfStrikeOut, "lfStrikeOut: expect %02x got %02x\n", lf->lfStrikeOut, getobj_lf.lfStrikeOut);
132 ok(lf->lfCharSet == getobj_lf.lfCharSet, "lfCharSet: expect %02x got %02x\n", lf->lfCharSet, getobj_lf.lfCharSet);
133 ok(lf->lfOutPrecision == getobj_lf.lfOutPrecision, "lfOutPrecision: expect %02x got %02x\n", lf->lfOutPrecision, getobj_lf.lfOutPrecision);
134 ok(lf->lfClipPrecision == getobj_lf.lfClipPrecision, "lfClipPrecision: expect %02x got %02x\n", lf->lfClipPrecision, getobj_lf.lfClipPrecision);
135 ok(lf->lfQuality == getobj_lf.lfQuality, "lfQuality: expect %02x got %02x\n", lf->lfQuality, getobj_lf.lfQuality);
136 ok(lf->lfPitchAndFamily == getobj_lf.lfPitchAndFamily, "lfPitchAndFamily: expect %02x got %02x\n", lf->lfPitchAndFamily, getobj_lf.lfPitchAndFamily);
137 ok(!lstrcmpA(lf->lfFaceName, getobj_lf.lfFaceName) ||
138 broken(!memcmp(lf->lfFaceName, getobj_lf.lfFaceName, LF_FACESIZE-1)), /* win9x doesn't ensure '\0' termination */
139 "%s: font names don't match: %s != %s\n", test, lf->lfFaceName, getobj_lf.lfFaceName);
140 }
141
142 static HFONT create_font(const char* test, const LOGFONTA* lf)
143 {
144 HFONT hfont = CreateFontIndirectA(lf);
145 ok(hfont != 0, "%s: CreateFontIndirect failed\n", test);
146 if (hfont)
147 check_font(test, lf, hfont);
148 return hfont;
149 }
150
151 static void test_logfont(void)
152 {
153 LOGFONTA lf;
154 HFONT hfont;
155
156 memset(&lf, 0, sizeof lf);
157
158 lf.lfCharSet = ANSI_CHARSET;
159 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
160 lf.lfWeight = FW_DONTCARE;
161 lf.lfHeight = 16;
162 lf.lfWidth = 16;
163 lf.lfQuality = DEFAULT_QUALITY;
164
165 lstrcpyA(lf.lfFaceName, "Arial");
166 hfont = create_font("Arial", &lf);
167 DeleteObject(hfont);
168
169 memset(&lf, 'A', sizeof(lf));
170 hfont = CreateFontIndirectA(&lf);
171 ok(hfont != 0, "CreateFontIndirectA with strange LOGFONT failed\n");
172
173 lf.lfFaceName[LF_FACESIZE - 1] = 0;
174 check_font("AAA...", &lf, hfont);
175 DeleteObject(hfont);
176 }
177
178 static INT CALLBACK font_enum_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
179 {
180 if (type & RASTER_FONTTYPE)
181 {
182 LOGFONT *lf = (LOGFONT *)lParam;
183 *lf = *elf;
184 return 0; /* stop enumeration */
185 }
186
187 return 1; /* continue enumeration */
188 }
189
190 static void compare_tm(const TEXTMETRICA *tm, const TEXTMETRICA *otm)
191 {
192 ok(tm->tmHeight == otm->tmHeight, "tmHeight %d != %d\n", tm->tmHeight, otm->tmHeight);
193 ok(tm->tmAscent == otm->tmAscent, "tmAscent %d != %d\n", tm->tmAscent, otm->tmAscent);
194 ok(tm->tmDescent == otm->tmDescent, "tmDescent %d != %d\n", tm->tmDescent, otm->tmDescent);
195 ok(tm->tmInternalLeading == otm->tmInternalLeading, "tmInternalLeading %d != %d\n", tm->tmInternalLeading, otm->tmInternalLeading);
196 ok(tm->tmExternalLeading == otm->tmExternalLeading, "tmExternalLeading %d != %d\n", tm->tmExternalLeading, otm->tmExternalLeading);
197 ok(tm->tmAveCharWidth == otm->tmAveCharWidth, "tmAveCharWidth %d != %d\n", tm->tmAveCharWidth, otm->tmAveCharWidth);
198 ok(tm->tmMaxCharWidth == otm->tmMaxCharWidth, "tmMaxCharWidth %d != %d\n", tm->tmMaxCharWidth, otm->tmMaxCharWidth);
199 ok(tm->tmWeight == otm->tmWeight, "tmWeight %d != %d\n", tm->tmWeight, otm->tmWeight);
200 ok(tm->tmOverhang == otm->tmOverhang, "tmOverhang %d != %d\n", tm->tmOverhang, otm->tmOverhang);
201 ok(tm->tmDigitizedAspectX == otm->tmDigitizedAspectX, "tmDigitizedAspectX %d != %d\n", tm->tmDigitizedAspectX, otm->tmDigitizedAspectX);
202 ok(tm->tmDigitizedAspectY == otm->tmDigitizedAspectY, "tmDigitizedAspectY %d != %d\n", tm->tmDigitizedAspectY, otm->tmDigitizedAspectY);
203 ok(tm->tmFirstChar == otm->tmFirstChar, "tmFirstChar %d != %d\n", tm->tmFirstChar, otm->tmFirstChar);
204 ok(tm->tmLastChar == otm->tmLastChar, "tmLastChar %d != %d\n", tm->tmLastChar, otm->tmLastChar);
205 ok(tm->tmDefaultChar == otm->tmDefaultChar, "tmDefaultChar %d != %d\n", tm->tmDefaultChar, otm->tmDefaultChar);
206 ok(tm->tmBreakChar == otm->tmBreakChar, "tmBreakChar %d != %d\n", tm->tmBreakChar, otm->tmBreakChar);
207 ok(tm->tmItalic == otm->tmItalic, "tmItalic %d != %d\n", tm->tmItalic, otm->tmItalic);
208 ok(tm->tmUnderlined == otm->tmUnderlined, "tmUnderlined %d != %d\n", tm->tmUnderlined, otm->tmUnderlined);
209 ok(tm->tmStruckOut == otm->tmStruckOut, "tmStruckOut %d != %d\n", tm->tmStruckOut, otm->tmStruckOut);
210 ok(tm->tmPitchAndFamily == otm->tmPitchAndFamily, "tmPitchAndFamily %d != %d\n", tm->tmPitchAndFamily, otm->tmPitchAndFamily);
211 ok(tm->tmCharSet == otm->tmCharSet, "tmCharSet %d != %d\n", tm->tmCharSet, otm->tmCharSet);
212 }
213
214 static void test_font_metrics(HDC hdc, HFONT hfont, LONG lfHeight,
215 LONG lfWidth, const char *test_str,
216 INT test_str_len, const TEXTMETRICA *tm_orig,
217 const SIZE *size_orig, INT width_of_A_orig,
218 INT scale_x, INT scale_y)
219 {
220 LOGFONTA lf;
221 OUTLINETEXTMETRIC otm;
222 TEXTMETRICA tm;
223 SIZE size;
224 INT width_of_A, cx, cy;
225 UINT ret;
226
227 if (!hfont)
228 return;
229
230 ok(GetCurrentObject(hdc, OBJ_FONT) == hfont, "hfont should be selected\n");
231
232 GetObjectA(hfont, sizeof(lf), &lf);
233
234 if (GetOutlineTextMetricsA(hdc, 0, NULL))
235 {
236 otm.otmSize = sizeof(otm) / 2;
237 ret = GetOutlineTextMetricsA(hdc, otm.otmSize, &otm);
238 ok(ret == sizeof(otm)/2 /* XP */ ||
239 ret == 1 /* Win9x */, "expected sizeof(otm)/2, got %u\n", ret);
240
241 memset(&otm, 0x1, sizeof(otm));
242 otm.otmSize = sizeof(otm);
243 ret = GetOutlineTextMetricsA(hdc, otm.otmSize, &otm);
244 ok(ret == sizeof(otm) /* XP */ ||
245 ret == 1 /* Win9x */, "expected sizeof(otm), got %u\n", ret);
246
247 memset(&tm, 0x2, sizeof(tm));
248 ret = GetTextMetricsA(hdc, &tm);
249 ok(ret, "GetTextMetricsA failed\n");
250 /* the structure size is aligned */
251 if (memcmp(&tm, &otm.otmTextMetrics, FIELD_OFFSET(TEXTMETRICA, tmCharSet) + 1))
252 {
253 ok(0, "tm != otm\n");
254 compare_tm(&tm, &otm.otmTextMetrics);
255 }
256
257 tm = otm.otmTextMetrics;
258 if (0) /* these metrics are scaled too, but with rounding errors */
259 {
260 ok(otm.otmAscent == tm.tmAscent, "ascent %d != %d\n", otm.otmAscent, tm.tmAscent);
261 ok(otm.otmDescent == -tm.tmDescent, "descent %d != %d\n", otm.otmDescent, -tm.tmDescent);
262 }
263 ok(otm.otmMacAscent == tm.tmAscent, "ascent %d != %d\n", otm.otmMacAscent, tm.tmAscent);
264 ok(otm.otmDescent < 0, "otm.otmDescent should be < 0\n");
265 ok(otm.otmMacDescent < 0, "otm.otmMacDescent should be < 0\n");
266 ok(tm.tmDescent > 0, "tm.tmDescent should be > 0\n");
267 ok(otm.otmMacDescent == -tm.tmDescent, "descent %d != %d\n", otm.otmMacDescent, -tm.tmDescent);
268 ok(otm.otmEMSquare == 2048, "expected 2048, got %d\n", otm.otmEMSquare);
269 }
270 else
271 {
272 ret = GetTextMetricsA(hdc, &tm);
273 ok(ret, "GetTextMetricsA failed\n");
274 }
275
276 cx = tm.tmAveCharWidth / tm_orig->tmAveCharWidth;
277 cy = tm.tmHeight / tm_orig->tmHeight;
278 ok(cx == scale_x && cy == scale_y, "height %d: expected scale_x %d, scale_y %d, got cx %d, cy %d\n",
279 lfHeight, scale_x, scale_y, cx, cy);
280 ok(tm.tmHeight == tm_orig->tmHeight * scale_y, "height %d != %d\n", tm.tmHeight, tm_orig->tmHeight * scale_y);
281 ok(tm.tmAscent == tm_orig->tmAscent * scale_y, "ascent %d != %d\n", tm.tmAscent, tm_orig->tmAscent * scale_y);
282 ok(tm.tmDescent == tm_orig->tmDescent * scale_y, "descent %d != %d\n", tm.tmDescent, tm_orig->tmDescent * scale_y);
283 ok(near_match(tm.tmAveCharWidth, tm_orig->tmAveCharWidth * scale_x), "ave width %d != %d\n", tm.tmAveCharWidth, tm_orig->tmAveCharWidth * scale_x);
284 ok(near_match(tm.tmMaxCharWidth, tm_orig->tmMaxCharWidth * scale_x), "max width %d != %d\n", tm.tmMaxCharWidth, tm_orig->tmMaxCharWidth * scale_x);
285
286 ok(lf.lfHeight == lfHeight, "lfHeight %d != %d\n", lf.lfHeight, lfHeight);
287 if (lf.lfHeight)
288 {
289 if (lf.lfWidth)
290 ok(lf.lfWidth == tm.tmAveCharWidth, "lfWidth %d != tm %d\n", lf.lfWidth, tm.tmAveCharWidth);
291 }
292 else
293 ok(lf.lfWidth == lfWidth, "lfWidth %d != %d\n", lf.lfWidth, lfWidth);
294
295 GetTextExtentPoint32A(hdc, test_str, test_str_len, &size);
296
297 ok(near_match(size.cx, size_orig->cx * scale_x), "cx %d != %d\n", size.cx, size_orig->cx * scale_x);
298 ok(size.cy == size_orig->cy * scale_y, "cy %d != %d\n", size.cy, size_orig->cy * scale_y);
299
300 GetCharWidthA(hdc, 'A', 'A', &width_of_A);
301
302 ok(near_match(width_of_A, width_of_A_orig * scale_x), "width A %d != %d\n", width_of_A, width_of_A_orig * scale_x);
303 }
304
305 /* Test how GDI scales bitmap font metrics */
306 static void test_bitmap_font(void)
307 {
308 static const char test_str[11] = "Test String";
309 HDC hdc;
310 LOGFONTA bitmap_lf;
311 HFONT hfont, old_hfont;
312 TEXTMETRICA tm_orig;
313 SIZE size_orig;
314 INT ret, i, width_orig, height_orig, scale, lfWidth;
315
316 if(!winetest_interactive)
317 {
318 skip("reactos bug 5401: Skipping bitmap font tests!\n");
319 return;
320 }
321
322 hdc = GetDC(0);
323
324 /* "System" has only 1 pixel size defined, otherwise the test breaks */
325 ret = EnumFontFamiliesA(hdc, "System", font_enum_proc, (LPARAM)&bitmap_lf);
326 if (ret)
327 {
328 ReleaseDC(0, hdc);
329 trace("no bitmap fonts were found, skipping the test\n");
330 return;
331 }
332
333 trace("found bitmap font %s, height %d\n", bitmap_lf.lfFaceName, bitmap_lf.lfHeight);
334
335 height_orig = bitmap_lf.lfHeight;
336 lfWidth = bitmap_lf.lfWidth;
337
338 hfont = create_font("bitmap", &bitmap_lf);
339 old_hfont = SelectObject(hdc, hfont);
340 ok(GetTextMetricsA(hdc, &tm_orig), "GetTextMetricsA failed\n");
341 ok(GetTextExtentPoint32A(hdc, test_str, sizeof(test_str), &size_orig), "GetTextExtentPoint32A failed\n");
342 ok(GetCharWidthA(hdc, 'A', 'A', &width_orig), "GetCharWidthA failed\n");
343 SelectObject(hdc, old_hfont);
344 DeleteObject(hfont);
345
346 bitmap_lf.lfHeight = 0;
347 bitmap_lf.lfWidth = 4;
348 hfont = create_font("bitmap", &bitmap_lf);
349 old_hfont = SelectObject(hdc, hfont);
350 test_font_metrics(hdc, hfont, 0, 4, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 1, 1);
351 SelectObject(hdc, old_hfont);
352 DeleteObject(hfont);
353
354 bitmap_lf.lfHeight = height_orig;
355 bitmap_lf.lfWidth = lfWidth;
356
357 /* test fractional scaling */
358 for (i = 1; i <= height_orig * 6; i++)
359 {
360 INT nearest_height;
361
362 bitmap_lf.lfHeight = i;
363 hfont = create_font("fractional", &bitmap_lf);
364 scale = (i + height_orig - 1) / height_orig;
365 nearest_height = scale * height_orig;
366 /* Only jump to the next height if the difference <= 25% original height */
367 if (scale > 2 && nearest_height - i > height_orig / 4) scale--;
368 /* The jump between unscaled and doubled is delayed by 1 in winnt+ but not in win9x,
369 so we'll not test this particular height. */
370 else if(scale == 2 && nearest_height - i == (height_orig / 4)) continue;
371 else if(scale == 2 && nearest_height - i > (height_orig / 4 - 1)) scale--;
372 old_hfont = SelectObject(hdc, hfont);
373 test_font_metrics(hdc, hfont, bitmap_lf.lfHeight, 0, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 1, scale);
374 SelectObject(hdc, old_hfont);
375 DeleteObject(hfont);
376 }
377
378 /* test integer scaling 3x2 */
379 bitmap_lf.lfHeight = height_orig * 2;
380 bitmap_lf.lfWidth *= 3;
381 hfont = create_font("3x2", &bitmap_lf);
382 old_hfont = SelectObject(hdc, hfont);
383 test_font_metrics(hdc, hfont, bitmap_lf.lfHeight, 0, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 3, 2);
384 SelectObject(hdc, old_hfont);
385 DeleteObject(hfont);
386
387 /* test integer scaling 3x3 */
388 bitmap_lf.lfHeight = height_orig * 3;
389 bitmap_lf.lfWidth = 0;
390 hfont = create_font("3x3", &bitmap_lf);
391 old_hfont = SelectObject(hdc, hfont);
392 test_font_metrics(hdc, hfont, bitmap_lf.lfHeight, 0, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 3, 3);
393 SelectObject(hdc, old_hfont);
394 DeleteObject(hfont);
395
396 ReleaseDC(0, hdc);
397 }
398
399 /* Test how GDI scales outline font metrics */
400 static void test_outline_font(void)
401 {
402 static const char test_str[11] = "Test String";
403 HDC hdc, hdc_2;
404 LOGFONTA lf;
405 HFONT hfont, old_hfont, old_hfont_2;
406 OUTLINETEXTMETRICA otm;
407 SIZE size_orig;
408 INT width_orig, height_orig, lfWidth;
409 XFORM xform;
410 GLYPHMETRICS gm;
411 MAT2 mat = { {0,1}, {0,0}, {0,0}, {0,1} };
412 MAT2 mat2 = { {0x8000,0}, {0,0}, {0,0}, {0x8000,0} };
413 POINT pt;
414 INT ret;
415
416 if(!winetest_interactive)
417 {
418 skip("reactos bug 5401: Skipping outline font tests!\n");
419 return;
420 }
421
422 if (!is_truetype_font_installed("Arial"))
423 {
424 skip("Arial is not installed\n");
425 return;
426 }
427
428 hdc = CreateCompatibleDC(0);
429
430 memset(&lf, 0, sizeof(lf));
431 strcpy(lf.lfFaceName, "Arial");
432 lf.lfHeight = 72;
433 hfont = create_font("outline", &lf);
434 old_hfont = SelectObject(hdc, hfont);
435 otm.otmSize = sizeof(otm);
436 ok(GetOutlineTextMetricsA(hdc, sizeof(otm), &otm), "GetTextMetricsA failed\n");
437 ok(GetTextExtentPoint32A(hdc, test_str, sizeof(test_str), &size_orig), "GetTextExtentPoint32A failed\n");
438 ok(GetCharWidthA(hdc, 'A', 'A', &width_orig), "GetCharWidthA failed\n");
439
440 test_font_metrics(hdc, hfont, lf.lfHeight, otm.otmTextMetrics.tmAveCharWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
441 SelectObject(hdc, old_hfont);
442 DeleteObject(hfont);
443
444 /* font of otmEMSquare height helps to avoid a lot of rounding errors */
445 lf.lfHeight = otm.otmEMSquare;
446 lf.lfHeight = -lf.lfHeight;
447 hfont = create_font("outline", &lf);
448 old_hfont = SelectObject(hdc, hfont);
449 otm.otmSize = sizeof(otm);
450 ok(GetOutlineTextMetricsA(hdc, sizeof(otm), &otm), "GetTextMetricsA failed\n");
451 ok(GetTextExtentPoint32A(hdc, test_str, sizeof(test_str), &size_orig), "GetTextExtentPoint32A failed\n");
452 ok(GetCharWidthA(hdc, 'A', 'A', &width_orig), "GetCharWidthA failed\n");
453 SelectObject(hdc, old_hfont);
454 DeleteObject(hfont);
455
456 height_orig = otm.otmTextMetrics.tmHeight;
457 lfWidth = otm.otmTextMetrics.tmAveCharWidth;
458
459 /* test integer scaling 3x2 */
460 lf.lfHeight = height_orig * 2;
461 lf.lfWidth = lfWidth * 3;
462 hfont = create_font("3x2", &lf);
463 old_hfont = SelectObject(hdc, hfont);
464 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 3, 2);
465 SelectObject(hdc, old_hfont);
466 DeleteObject(hfont);
467
468 /* test integer scaling 3x3 */
469 lf.lfHeight = height_orig * 3;
470 lf.lfWidth = lfWidth * 3;
471 hfont = create_font("3x3", &lf);
472 old_hfont = SelectObject(hdc, hfont);
473 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 3, 3);
474 SelectObject(hdc, old_hfont);
475 DeleteObject(hfont);
476
477 /* test integer scaling 1x1 */
478 lf.lfHeight = height_orig * 1;
479 lf.lfWidth = lfWidth * 1;
480 hfont = create_font("1x1", &lf);
481 old_hfont = SelectObject(hdc, hfont);
482 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
483 SelectObject(hdc, old_hfont);
484 DeleteObject(hfont);
485
486 /* test integer scaling 1x1 */
487 lf.lfHeight = height_orig;
488 lf.lfWidth = 0;
489 hfont = create_font("1x1", &lf);
490 old_hfont = SelectObject(hdc, hfont);
491 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
492
493 /* with an identity matrix */
494 memset(&gm, 0, sizeof(gm));
495 SetLastError(0xdeadbeef);
496 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
497 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
498 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
499 ok(gm.gmCellIncX == width_orig, "incX %d != %d\n", gm.gmCellIncX, width_orig);
500 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
501 /* with a custom matrix */
502 memset(&gm, 0, sizeof(gm));
503 SetLastError(0xdeadbeef);
504 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat2);
505 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
506 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
507 ok(gm.gmCellIncX == width_orig/2, "incX %d != %d\n", gm.gmCellIncX, width_orig/2);
508 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
509
510 /* Test that changing the DC transformation affects only the font
511 * selected on this DC and doesn't affect the same font selected on
512 * another DC.
513 */
514 hdc_2 = CreateCompatibleDC(0);
515 old_hfont_2 = SelectObject(hdc_2, hfont);
516 test_font_metrics(hdc_2, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
517
518 SetMapMode(hdc, MM_ANISOTROPIC);
519
520 /* font metrics on another DC should be unchanged */
521 test_font_metrics(hdc_2, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
522
523 /* test restrictions of compatibility mode GM_COMPATIBLE */
524 /* part 1: rescaling only X should not change font scaling on screen.
525 So compressing the X axis by 2 is not done, and this
526 appears as X scaling of 2 that no one requested. */
527 SetWindowExtEx(hdc, 100, 100, NULL);
528 SetViewportExtEx(hdc, 50, 100, NULL);
529 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 2, 1);
530 /* font metrics on another DC should be unchanged */
531 test_font_metrics(hdc_2, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
532
533 /* part 2: rescaling only Y should change font scaling.
534 As also X is scaled by a factor of 2, but this is not
535 requested by the DC transformation, we get a scaling factor
536 of 2 in the X coordinate. */
537 SetViewportExtEx(hdc, 100, 200, NULL);
538 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 2, 1);
539 /* font metrics on another DC should be unchanged */
540 test_font_metrics(hdc_2, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
541
542 /* restore scaling */
543 SetMapMode(hdc, MM_TEXT);
544
545 /* font metrics on another DC should be unchanged */
546 test_font_metrics(hdc_2, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
547
548 SelectObject(hdc_2, old_hfont_2);
549 DeleteDC(hdc_2);
550
551 if (!SetGraphicsMode(hdc, GM_ADVANCED))
552 {
553 SelectObject(hdc, old_hfont);
554 DeleteObject(hfont);
555 DeleteDC(hdc);
556 skip("GM_ADVANCED is not supported on this platform\n");
557 return;
558 }
559
560 xform.eM11 = 20.0f;
561 xform.eM12 = 0.0f;
562 xform.eM21 = 0.0f;
563 xform.eM22 = 20.0f;
564 xform.eDx = 0.0f;
565 xform.eDy = 0.0f;
566
567 SetLastError(0xdeadbeef);
568 ret = SetWorldTransform(hdc, &xform);
569 ok(ret, "SetWorldTransform error %u\n", GetLastError());
570
571 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
572
573 /* with an identity matrix */
574 memset(&gm, 0, sizeof(gm));
575 SetLastError(0xdeadbeef);
576 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
577 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
578 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
579 pt.x = width_orig; pt.y = 0;
580 LPtoDP(hdc, &pt, 1);
581 ok(gm.gmCellIncX == pt.x, "incX %d != %d\n", gm.gmCellIncX, pt.x);
582 ok(gm.gmCellIncX == 20 * width_orig, "incX %d != %d\n", gm.gmCellIncX, 20 * width_orig);
583 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
584 /* with a custom matrix */
585 memset(&gm, 0, sizeof(gm));
586 SetLastError(0xdeadbeef);
587 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat2);
588 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
589 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
590 pt.x = width_orig; pt.y = 0;
591 LPtoDP(hdc, &pt, 1);
592 ok(gm.gmCellIncX == pt.x/2, "incX %d != %d\n", gm.gmCellIncX, pt.x/2);
593 ok(near_match(gm.gmCellIncX, 10 * width_orig), "incX %d != %d\n", gm.gmCellIncX, 10 * width_orig);
594 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
595
596 SetLastError(0xdeadbeef);
597 ret = SetMapMode(hdc, MM_LOMETRIC);
598 ok(ret == MM_TEXT, "expected MM_TEXT, got %d, error %u\n", ret, GetLastError());
599
600 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
601
602 /* with an identity matrix */
603 memset(&gm, 0, sizeof(gm));
604 SetLastError(0xdeadbeef);
605 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
606 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
607 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
608 pt.x = width_orig; pt.y = 0;
609 LPtoDP(hdc, &pt, 1);
610 ok(near_match(gm.gmCellIncX, pt.x), "incX %d != %d\n", gm.gmCellIncX, pt.x);
611 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
612 /* with a custom matrix */
613 memset(&gm, 0, sizeof(gm));
614 SetLastError(0xdeadbeef);
615 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat2);
616 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
617 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
618 pt.x = width_orig; pt.y = 0;
619 LPtoDP(hdc, &pt, 1);
620 ok(near_match(gm.gmCellIncX, (pt.x + 1)/2), "incX %d != %d\n", gm.gmCellIncX, (pt.x + 1)/2);
621 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
622
623 SetLastError(0xdeadbeef);
624 ret = SetMapMode(hdc, MM_TEXT);
625 ok(ret == MM_LOMETRIC, "expected MM_LOMETRIC, got %d, error %u\n", ret, GetLastError());
626
627 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
628
629 /* with an identity matrix */
630 memset(&gm, 0, sizeof(gm));
631 SetLastError(0xdeadbeef);
632 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
633 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
634 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
635 pt.x = width_orig; pt.y = 0;
636 LPtoDP(hdc, &pt, 1);
637 ok(gm.gmCellIncX == pt.x, "incX %d != %d\n", gm.gmCellIncX, pt.x);
638 ok(gm.gmCellIncX == 20 * width_orig, "incX %d != %d\n", gm.gmCellIncX, 20 * width_orig);
639 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
640 /* with a custom matrix */
641 memset(&gm, 0, sizeof(gm));
642 SetLastError(0xdeadbeef);
643 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat2);
644 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
645 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
646 pt.x = width_orig; pt.y = 0;
647 LPtoDP(hdc, &pt, 1);
648 ok(gm.gmCellIncX == pt.x/2, "incX %d != %d\n", gm.gmCellIncX, pt.x/2);
649 ok(gm.gmCellIncX == 10 * width_orig, "incX %d != %d\n", gm.gmCellIncX, 10 * width_orig);
650 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
651
652 SelectObject(hdc, old_hfont);
653 DeleteObject(hfont);
654 DeleteDC(hdc);
655 }
656
657 static INT CALLBACK find_font_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
658 {
659 LOGFONT *lf = (LOGFONT *)lParam;
660
661 if (elf->lfHeight == lf->lfHeight && !strcmp(elf->lfFaceName, lf->lfFaceName))
662 {
663 *lf = *elf;
664 return 0; /* stop enumeration */
665 }
666 return 1; /* continue enumeration */
667 }
668
669 static void test_bitmap_font_metrics(void)
670 {
671 static const struct font_data
672 {
673 const char face_name[LF_FACESIZE];
674 int weight, height, ascent, descent, int_leading, ext_leading;
675 int ave_char_width, max_char_width, dpi;
676 DWORD ansi_bitfield;
677 } fd[] =
678 {
679 { "MS Sans Serif", FW_NORMAL, 13, 11, 2, 2, 0, 5, 11, 96, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
680 { "MS Sans Serif", FW_NORMAL, 16, 13, 3, 3, 0, 7, 14, 96, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
681 { "MS Sans Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 16, 96, FS_LATIN1 | FS_CYRILLIC },
682 { "MS Sans Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 18, 96, FS_LATIN2 },
683 { "MS Sans Serif", FW_NORMAL, 24, 19, 5, 6, 0, 9, 19, 96, FS_LATIN1 },
684 { "MS Sans Serif", FW_NORMAL, 24, 19, 5, 6, 0, 9, 24, 96, FS_LATIN2 },
685 { "MS Sans Serif", FW_NORMAL, 24, 19, 5, 6, 0, 9, 20, 96, FS_CYRILLIC },
686 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 5, 0, 12, 24, 96, FS_LATIN1 },
687 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 6, 0, 12, 24, 96, FS_LATIN2 },
688 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 5, 0, 12, 25, 96, FS_CYRILLIC },
689 { "MS Sans Serif", FW_NORMAL, 37, 29, 8, 5, 0, 16, 32, 96, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
690
691 { "MS Sans Serif", FW_NORMAL, 16, 13, 3, 3, 0, 7, 14, 120, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
692 { "MS Sans Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 18, 120, FS_LATIN1 | FS_LATIN2 },
693 { "MS Sans Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 17, 120, FS_CYRILLIC },
694 { "MS Sans Serif", FW_NORMAL, 25, 20, 5, 5, 0, 10, 21, 120, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
695 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 6, 0, 12, 24, 120, FS_LATIN1 | FS_LATIN2 },
696 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 5, 0, 12, 24, 120, FS_CYRILLIC },
697 { "MS Sans Serif", FW_NORMAL, 36, 29, 7, 6, 0, 15, 30, 120, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
698 { "MS Sans Serif", FW_NORMAL, 46, 37, 9, 6, 0, 20, 40, 120, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
699
700 { "MS Serif", FW_NORMAL, 10, 8, 2, 2, 0, 4, 8, 96, FS_LATIN1 | FS_LATIN2 },
701 { "MS Serif", FW_NORMAL, 10, 8, 2, 2, 0, 5, 8, 96, FS_CYRILLIC },
702 { "MS Serif", FW_NORMAL, 11, 9, 2, 2, 0, 5, 9, 96, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
703 { "MS Serif", FW_NORMAL, 13, 11, 2, 2, 0, 5, 11, 96, FS_LATIN1 },
704 { "MS Serif", FW_NORMAL, 13, 11, 2, 2, 0, 5, 12, 96, FS_LATIN2 | FS_CYRILLIC },
705 { "MS Serif", FW_NORMAL, 16, 13, 3, 3, 0, 6, 14, 96, FS_LATIN1 | FS_LATIN2 },
706 { "MS Serif", FW_NORMAL, 16, 13, 3, 3, 0, 6, 16, 96, FS_CYRILLIC },
707 { "MS Serif", FW_NORMAL, 19, 15, 4, 3, 0, 8, 18, 96, FS_LATIN1 | FS_LATIN2 },
708 { "MS Serif", FW_NORMAL, 19, 15, 4, 3, 0, 8, 19, 96, FS_CYRILLIC },
709 { "MS Serif", FW_NORMAL, 21, 16, 5, 3, 0, 9, 17, 96, FS_LATIN1 },
710 { "MS Serif", FW_NORMAL, 21, 16, 5, 3, 0, 9, 22, 96, FS_LATIN2 },
711 { "MS Serif", FW_NORMAL, 21, 16, 5, 3, 0, 9, 23, 96, FS_CYRILLIC },
712 { "MS Serif", FW_NORMAL, 27, 21, 6, 3, 0, 12, 23, 96, FS_LATIN1 },
713 { "MS Serif", FW_NORMAL, 27, 21, 6, 3, 0, 12, 26, 96, FS_LATIN2 },
714 { "MS Serif", FW_NORMAL, 27, 21, 6, 3, 0, 12, 27, 96, FS_CYRILLIC },
715 { "MS Serif", FW_NORMAL, 35, 27, 8, 3, 0, 16, 33, 96, FS_LATIN1 | FS_LATIN2 },
716 { "MS Serif", FW_NORMAL, 35, 27, 8, 3, 0, 16, 34, 96, FS_CYRILLIC },
717
718 { "MS Serif", FW_NORMAL, 16, 13, 3, 3, 0, 6, 14, 120, FS_LATIN1 | FS_CYRILLIC },
719 { "MS Serif", FW_NORMAL, 16, 13, 3, 3, 0, 6, 13, 120, FS_LATIN2 },
720 { "MS Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 18, 120, FS_LATIN1 | FS_CYRILLIC },
721 { "MS Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 15, 120, FS_LATIN2 },
722 { "MS Serif", FW_NORMAL, 23, 18, 5, 3, 0, 10, 21, 120, FS_LATIN1 | FS_CYRILLIC },
723 { "MS Serif", FW_NORMAL, 23, 18, 5, 3, 0, 10, 19, 120, FS_LATIN2 },
724 { "MS Serif", FW_NORMAL, 27, 21, 6, 4, 0, 12, 23, 120, FS_LATIN1 | FS_LATIN2 },
725 { "MS Serif", FW_MEDIUM, 27, 22, 5, 2, 0, 12, 30, 120, FS_CYRILLIC },
726 { "MS Serif", FW_NORMAL, 33, 26, 7, 3, 0, 14, 30, 120, FS_LATIN1 | FS_LATIN2 },
727 { "MS Serif", FW_MEDIUM, 32, 25, 7, 2, 0, 14, 32, 120, FS_CYRILLIC },
728 { "MS Serif", FW_NORMAL, 43, 34, 9, 3, 0, 19, 39, 120, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
729
730 { "Courier", FW_NORMAL, 13, 11, 2, 0, 0, 8, 8, 96, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
731 { "Courier", FW_NORMAL, 16, 13, 3, 0, 0, 9, 9, 96, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
732 { "Courier", FW_NORMAL, 20, 16, 4, 0, 0, 12, 12, 96, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
733
734 { "Courier", FW_NORMAL, 16, 13, 3, 0, 0, 9, 9, 120, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
735 { "Courier", FW_NORMAL, 20, 16, 4, 0, 0, 12, 12, 120, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
736 { "Courier", FW_NORMAL, 25, 20, 5, 0, 0, 15, 15, 120, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
737
738 { "System", FW_BOLD, 16, 13, 3, 3, 0, 7, 14, 96, FS_LATIN1 },
739 { "System", FW_BOLD, 16, 13, 3, 3, 0, 7, 15, 96, FS_LATIN2 | FS_CYRILLIC },
740 /*
741 * TODO: the system for CP932 should be NORMAL, not BOLD. However that would
742 * require a new system.sfd for that font
743 */
744 { "System", FW_BOLD, 18, 16, 2, 0, 2, 8, 16, 96, FS_JISJAPAN },
745
746 { "System", FW_BOLD, 20, 16, 4, 4, 0, 9, 14, 120, FS_LATIN1 },
747 { "System", FW_BOLD, 20, 16, 4, 4, 0, 9, 17, 120, FS_LATIN2 | FS_CYRILLIC },
748
749 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 1, 2, 96, FS_LATIN1 },
750 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 1, 8, 96, FS_LATIN2 | FS_CYRILLIC },
751 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 2, 4, 96, FS_JISJAPAN },
752 { "Small Fonts", FW_NORMAL, 5, 4, 1, 1, 0, 3, 4, 96, FS_LATIN1 },
753 { "Small Fonts", FW_NORMAL, 5, 4, 1, 1, 0, 2, 8, 96, FS_LATIN2 | FS_CYRILLIC },
754 { "Small Fonts", FW_NORMAL, 5, 4, 1, 0, 0, 3, 6, 96, FS_JISJAPAN },
755 { "Small Fonts", FW_NORMAL, 6, 5, 1, 1, 0, 3, 13, 96, FS_LATIN1 },
756 { "Small Fonts", FW_NORMAL, 6, 5, 1, 1, 0, 3, 8, 96, FS_LATIN2 | FS_CYRILLIC },
757 { "Small Fonts", FW_NORMAL, 6, 5, 1, 0, 0, 4, 8, 96, FS_JISJAPAN },
758 { "Small Fonts", FW_NORMAL, 8, 7, 1, 1, 0, 4, 7, 96, FS_LATIN1 },
759 { "Small Fonts", FW_NORMAL, 8, 7, 1, 1, 0, 4, 8, 96, FS_LATIN2 | FS_CYRILLIC },
760 { "Small Fonts", FW_NORMAL, 8, 7, 1, 0, 0, 5, 10, 96, FS_JISJAPAN },
761 { "Small Fonts", FW_NORMAL, 10, 8, 2, 2, 0, 4, 8, 96, FS_LATIN1 | FS_LATIN2 },
762 { "Small Fonts", FW_NORMAL, 10, 8, 2, 2, 0, 5, 8, 96, FS_CYRILLIC },
763 { "Small Fonts", FW_NORMAL, 10, 8, 2, 0, 0, 6, 12, 96, FS_JISJAPAN },
764 { "Small Fonts", FW_NORMAL, 11, 9, 2, 2, 0, 5, 9, 96, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
765 { "Small Fonts", FW_NORMAL, 11, 9, 2, 0, 0, 7, 14, 96, FS_JISJAPAN },
766
767 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 1, 2, 120, FS_LATIN1 | FS_JISJAPAN },
768 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 1, 8, 120, FS_LATIN2 | FS_CYRILLIC },
769 { "Small Fonts", FW_NORMAL, 6, 5, 1, 1, 0, 3, 5, 120, FS_LATIN1 | FS_JISJAPAN },
770 { "Small Fonts", FW_NORMAL, 6, 5, 1, 1, 0, 3, 8, 120, FS_LATIN2 | FS_CYRILLIC },
771 { "Small Fonts", FW_NORMAL, 8, 7, 1, 1, 0, 4, 7, 120, FS_LATIN1 | FS_JISJAPAN },
772 { "Small Fonts", FW_NORMAL, 8, 7, 1, 1, 0, 4, 8, 120, FS_LATIN2 | FS_CYRILLIC },
773 { "Small Fonts", FW_NORMAL, 10, 8, 2, 2, 0, 5, 9, 120, FS_LATIN1 | FS_LATIN2 | FS_JISJAPAN },
774 { "Small Fonts", FW_NORMAL, 10, 8, 2, 2, 0, 5, 8, 120, FS_CYRILLIC },
775 { "Small Fonts", FW_NORMAL, 12, 10, 2, 2, 0, 5, 10, 120, FS_LATIN1 | FS_LATIN2 | FS_JISJAPAN },
776 { "Small Fonts", FW_NORMAL, 12, 10, 2, 2, 0, 6, 10, 120, FS_CYRILLIC },
777 { "Small Fonts", FW_NORMAL, 13, 11, 2, 2, 0, 6, 12, 120, FS_LATIN1 | FS_LATIN2 | FS_JISJAPAN },
778 { "Small Fonts", FW_NORMAL, 13, 11, 2, 2, 0, 6, 11, 120, FS_CYRILLIC },
779
780 { "Fixedsys", FW_NORMAL, 15, 12, 3, 3, 0, 8, 8, 96, FS_LATIN1 | FS_LATIN2 },
781 { "Fixedsys", FW_NORMAL, 16, 12, 4, 3, 0, 8, 8, 96, FS_CYRILLIC },
782 { "FixedSys", FW_NORMAL, 18, 16, 2, 0, 0, 8, 16, 96, FS_JISJAPAN },
783
784 /* The 120dpi version still has its dpi marked as 96 */
785 { "Fixedsys", FW_NORMAL, 20, 16, 4, 2, 0, 10, 10, 96, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC }
786
787 /* FIXME: add "Terminal" */
788 };
789 HDC hdc;
790 LOGFONT lf;
791 HFONT hfont, old_hfont;
792 TEXTMETRIC tm;
793 INT ret, i;
794
795 hdc = CreateCompatibleDC(0);
796 assert(hdc);
797
798 for (i = 0; i < sizeof(fd)/sizeof(fd[0]); i++)
799 {
800 int bit;
801
802 memset(&lf, 0, sizeof(lf));
803
804 lf.lfHeight = fd[i].height;
805 strcpy(lf.lfFaceName, fd[i].face_name);
806
807 for(bit = 0; bit < 32; bit++)
808 {
809 DWORD fs[2];
810 CHARSETINFO csi;
811
812 fs[0] = 1L << bit;
813 fs[1] = 0;
814 if((fd[i].ansi_bitfield & fs[0]) == 0) continue;
815 if(!TranslateCharsetInfo( fs, &csi, TCI_SRCFONTSIG )) continue;
816
817 lf.lfCharSet = csi.ciCharset;
818 ret = EnumFontFamiliesEx(hdc, &lf, find_font_proc, (LPARAM)&lf, 0);
819 if (ret) continue;
820
821 hfont = create_font(lf.lfFaceName, &lf);
822 old_hfont = SelectObject(hdc, hfont);
823 ok(GetTextMetrics(hdc, &tm), "GetTextMetrics error %d\n", GetLastError());
824 if(fd[i].dpi == tm.tmDigitizedAspectX)
825 {
826 trace("found font %s, height %d charset %x dpi %d\n", lf.lfFaceName, lf.lfHeight, lf.lfCharSet, fd[i].dpi);
827 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);
828 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);
829 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);
830 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);
831 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);
832 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);
833 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);
834
835 /* Don't run the max char width test on System/ANSI_CHARSET. We have extra characters in our font
836 that make the max width bigger */
837 if(strcmp(lf.lfFaceName, "System") || lf.lfCharSet != ANSI_CHARSET)
838 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);
839 }
840 SelectObject(hdc, old_hfont);
841 DeleteObject(hfont);
842 }
843 }
844
845 DeleteDC(hdc);
846 }
847
848 static void test_GdiGetCharDimensions(void)
849 {
850 HDC hdc;
851 TEXTMETRICW tm;
852 LONG ret;
853 SIZE size;
854 LONG avgwidth, height;
855 static const char szAlphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
856
857 if (!pGdiGetCharDimensions)
858 {
859 win_skip("GdiGetCharDimensions not available on this platform\n");
860 return;
861 }
862
863 hdc = CreateCompatibleDC(NULL);
864
865 GetTextExtentPoint(hdc, szAlphabet, strlen(szAlphabet), &size);
866 avgwidth = ((size.cx / 26) + 1) / 2;
867
868 ret = pGdiGetCharDimensions(hdc, &tm, &height);
869 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
870 ok(height == tm.tmHeight, "GdiGetCharDimensions should have set height to %d instead of %d\n", tm.tmHeight, height);
871
872 ret = pGdiGetCharDimensions(hdc, &tm, NULL);
873 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
874
875 ret = pGdiGetCharDimensions(hdc, NULL, NULL);
876 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
877
878 height = 0;
879 ret = pGdiGetCharDimensions(hdc, NULL, &height);
880 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
881 ok(height == size.cy, "GdiGetCharDimensions should have set height to %d instead of %d\n", size.cy, height);
882
883 DeleteDC(hdc);
884 }
885
886 static void test_GetCharABCWidths(void)
887 {
888 static const WCHAR str[] = {'a',0};
889 BOOL ret;
890 HDC hdc;
891 LOGFONTA lf;
892 HFONT hfont;
893 ABC abc[1];
894 WORD glyphs[1];
895 DWORD nb;
896
897 if (!pGetCharABCWidthsW || !pGetCharABCWidthsI)
898 {
899 win_skip("GetCharABCWidthsW/I not available on this platform\n");
900 return;
901 }
902
903 memset(&lf, 0, sizeof(lf));
904 strcpy(lf.lfFaceName, "System");
905 lf.lfHeight = 20;
906
907 hfont = CreateFontIndirectA(&lf);
908 hdc = GetDC(0);
909 hfont = SelectObject(hdc, hfont);
910
911 nb = pGetGlyphIndicesW(hdc, str, 1, glyphs, 0);
912 ok(nb == 1, "GetGlyphIndicesW should have returned 1\n");
913
914 ret = pGetCharABCWidthsI(NULL, 0, 1, glyphs, abc);
915 ok(!ret, "GetCharABCWidthsI should have failed\n");
916
917 ret = pGetCharABCWidthsI(hdc, 0, 1, glyphs, NULL);
918 ok(!ret, "GetCharABCWidthsI should have failed\n");
919
920 ret = pGetCharABCWidthsI(hdc, 0, 1, glyphs, abc);
921 ok(ret, "GetCharABCWidthsI should have succeeded\n");
922
923 ret = pGetCharABCWidthsW(NULL, 'a', 'a', abc);
924 ok(!ret, "GetCharABCWidthsW should have failed\n");
925
926 ret = pGetCharABCWidthsW(hdc, 'a', 'a', NULL);
927 ok(!ret, "GetCharABCWidthsW should have failed\n");
928
929 ret = pGetCharABCWidthsW(hdc, 'a', 'a', abc);
930 ok(!ret, "GetCharABCWidthsW should have failed\n");
931
932 hfont = SelectObject(hdc, hfont);
933 DeleteObject(hfont);
934 ReleaseDC(NULL, hdc);
935 }
936
937 static void test_text_extents(void)
938 {
939 static const WCHAR wt[] = {'O','n','e','\n','t','w','o',' ','3',0};
940 LPINT extents;
941 INT i, len, fit1, fit2;
942 LOGFONTA lf;
943 TEXTMETRICA tm;
944 HDC hdc;
945 HFONT hfont;
946 SIZE sz;
947 SIZE sz1, sz2;
948
949 memset(&lf, 0, sizeof(lf));
950 strcpy(lf.lfFaceName, "Arial");
951 lf.lfHeight = 20;
952
953 hfont = CreateFontIndirectA(&lf);
954 hdc = GetDC(0);
955 hfont = SelectObject(hdc, hfont);
956 GetTextMetricsA(hdc, &tm);
957 GetTextExtentPointA(hdc, "o", 1, &sz);
958 ok(sz.cy == tm.tmHeight, "cy %d tmHeight %d\n", sz.cy, tm.tmHeight);
959
960 SetLastError(0xdeadbeef);
961 GetTextExtentExPointW(hdc, wt, 1, 1, &fit1, &fit2, &sz1);
962 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
963 {
964 win_skip("Skipping remainder of text extents test on a Win9x platform\n");
965 hfont = SelectObject(hdc, hfont);
966 DeleteObject(hfont);
967 ReleaseDC(0, hdc);
968 return;
969 }
970
971 len = lstrlenW(wt);
972 extents = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof extents[0]);
973 extents[0] = 1; /* So that the increasing sequence test will fail
974 if the extents array is untouched. */
975 GetTextExtentExPointW(hdc, wt, len, 32767, &fit1, extents, &sz1);
976 GetTextExtentPointW(hdc, wt, len, &sz2);
977 ok(sz1.cy == sz2.cy,
978 "cy from GetTextExtentExPointW (%d) and GetTextExtentPointW (%d) differ\n", sz1.cy, sz2.cy);
979 /* Because of the '\n' in the string GetTextExtentExPoint and
980 GetTextExtentPoint return different widths under Win2k, but
981 under WinXP they return the same width. So we don't test that
982 here. */
983
984 for (i = 1; i < len; ++i)
985 ok(extents[i-1] <= extents[i],
986 "GetTextExtentExPointW generated a non-increasing sequence of partial extents (at position %d)\n",
987 i);
988 ok(extents[len-1] == sz1.cx, "GetTextExtentExPointW extents and size don't match\n");
989 ok(0 <= fit1 && fit1 <= len, "GetTextExtentExPointW generated illegal value %d for fit\n", fit1);
990 ok(0 < fit1, "GetTextExtentExPointW says we can't even fit one letter in 32767 logical units\n");
991 GetTextExtentExPointW(hdc, wt, len, extents[2], &fit2, NULL, &sz2);
992 ok(sz1.cx == sz2.cx && sz1.cy == sz2.cy, "GetTextExtentExPointW returned different sizes for the same string\n");
993 ok(fit2 == 3, "GetTextExtentExPointW extents isn't consistent with fit\n");
994 GetTextExtentExPointW(hdc, wt, len, extents[2]-1, &fit2, NULL, &sz2);
995 ok(fit2 == 2, "GetTextExtentExPointW extents isn't consistent with fit\n");
996 GetTextExtentExPointW(hdc, wt, 2, 0, NULL, extents + 2, &sz2);
997 ok(extents[0] == extents[2] && extents[1] == extents[3],
998 "GetTextExtentExPointW with lpnFit == NULL returns incorrect results\n");
999 GetTextExtentExPointW(hdc, wt, 2, 0, NULL, NULL, &sz1);
1000 ok(sz1.cx == sz2.cx && sz1.cy == sz2.cy,
1001 "GetTextExtentExPointW with lpnFit and alpDx both NULL returns incorrect results\n");
1002 HeapFree(GetProcessHeap(), 0, extents);
1003
1004 hfont = SelectObject(hdc, hfont);
1005 DeleteObject(hfont);
1006 ReleaseDC(NULL, hdc);
1007 }
1008
1009 static void test_GetGlyphIndices(void)
1010 {
1011 HDC hdc;
1012 HFONT hfont;
1013 DWORD charcount;
1014 LOGFONTA lf;
1015 DWORD flags = 0;
1016 WCHAR testtext[] = {'T','e','s','t',0xffff,0};
1017 WORD glyphs[(sizeof(testtext)/2)-1];
1018 TEXTMETRIC textm;
1019 HFONT hOldFont;
1020
1021 if (!pGetGlyphIndicesW) {
1022 win_skip("GetGlyphIndicesW not available on platform\n");
1023 return;
1024 }
1025
1026 hdc = GetDC(0);
1027
1028 ok(GetTextMetrics(hdc, &textm), "GetTextMetric failed\n");
1029 flags |= GGI_MARK_NONEXISTING_GLYPHS;
1030 charcount = pGetGlyphIndicesW(hdc, testtext, (sizeof(testtext)/2)-1, glyphs, flags);
1031 ok(charcount == 5, "GetGlyphIndicesW count of glyphs should = 5 not %d\n", charcount);
1032 ok((glyphs[4] == 0x001f || glyphs[4] == 0xffff /* Vista */), "GetGlyphIndicesW should have returned a nonexistent char not %04x\n", glyphs[4]);
1033 flags = 0;
1034 charcount = pGetGlyphIndicesW(hdc, testtext, (sizeof(testtext)/2)-1, glyphs, flags);
1035 ok(charcount == 5, "GetGlyphIndicesW count of glyphs should = 5 not %d\n", charcount);
1036 ok(glyphs[4] == textm.tmDefaultChar, "GetGlyphIndicesW should have returned a %04x not %04x\n",
1037 textm.tmDefaultChar, glyphs[4]);
1038
1039 if(!is_font_installed("Tahoma"))
1040 {
1041 skip("Tahoma is not installed so skipping this test\n");
1042 return;
1043 }
1044 memset(&lf, 0, sizeof(lf));
1045 strcpy(lf.lfFaceName, "Tahoma");
1046 lf.lfHeight = 20;
1047
1048 hfont = CreateFontIndirectA(&lf);
1049 hOldFont = SelectObject(hdc, hfont);
1050 ok(GetTextMetrics(hdc, &textm), "GetTextMetric failed\n");
1051 flags |= GGI_MARK_NONEXISTING_GLYPHS;
1052 charcount = pGetGlyphIndicesW(hdc, testtext, (sizeof(testtext)/2)-1, glyphs, flags);
1053 ok(charcount == 5, "GetGlyphIndicesW count of glyphs should = 5 not %d\n", charcount);
1054 ok(glyphs[4] == 0xffff, "GetGlyphIndicesW should have returned 0xffff char not %04x\n", glyphs[4]);
1055 flags = 0;
1056 testtext[0] = textm.tmDefaultChar;
1057 charcount = pGetGlyphIndicesW(hdc, testtext, (sizeof(testtext)/2)-1, glyphs, flags);
1058 ok(charcount == 5, "GetGlyphIndicesW count of glyphs should = 5 not %d\n", charcount);
1059 ok(glyphs[0] == 0, "GetGlyphIndicesW for tmDefaultChar should be 0 not %04x\n", glyphs[0]);
1060 ok(glyphs[4] == 0, "GetGlyphIndicesW should have returned 0 not %04x\n", glyphs[4]);
1061 DeleteObject(SelectObject(hdc, hOldFont));
1062 }
1063
1064 static void test_GetKerningPairs(void)
1065 {
1066 static const struct kerning_data
1067 {
1068 const char face_name[LF_FACESIZE];
1069 LONG height;
1070 /* some interesting fields from OUTLINETEXTMETRIC */
1071 LONG tmHeight, tmAscent, tmDescent;
1072 UINT otmEMSquare;
1073 INT otmAscent;
1074 INT otmDescent;
1075 UINT otmLineGap;
1076 UINT otmsCapEmHeight;
1077 UINT otmsXHeight;
1078 INT otmMacAscent;
1079 INT otmMacDescent;
1080 UINT otmMacLineGap;
1081 UINT otmusMinimumPPEM;
1082 /* small subset of kerning pairs to test */
1083 DWORD total_kern_pairs;
1084 const KERNINGPAIR kern_pair[26];
1085 } kd[] =
1086 {
1087 {"Arial", 12, 12, 9, 3,
1088 2048, 7, -2, 1, 5, 2, 8, -2, 0, 9,
1089 26,
1090 {
1091 {' ','A',-1},{' ','T',0},{' ','Y',0},{'1','1',-1},
1092 {'A',' ',-1},{'A','T',-1},{'A','V',-1},{'A','W',0},
1093 {'A','Y',-1},{'A','v',0},{'A','w',0},{'A','y',0},
1094 {'F',',',-1},{'F','.',-1},{'F','A',-1},{'L',' ',0},
1095 {'L','T',-1},{'L','V',-1},{'L','W',-1},{'L','Y',-1},
1096 {915,912,+1},{915,913,-1},{910,912,+1},{910,913,-1},
1097 {933,970,+1},{933,972,-1}
1098 }
1099 },
1100 {"Arial", -34, 39, 32, 7,
1101 2048, 25, -7, 5, 17, 9, 31, -7, 1, 9,
1102 26,
1103 {
1104 {' ','A',-2},{' ','T',-1},{' ','Y',-1},{'1','1',-3},
1105 {'A',' ',-2},{'A','T',-3},{'A','V',-3},{'A','W',-1},
1106 {'A','Y',-3},{'A','v',-1},{'A','w',-1},{'A','y',-1},
1107 {'F',',',-4},{'F','.',-4},{'F','A',-2},{'L',' ',-1},
1108 {'L','T',-3},{'L','V',-3},{'L','W',-3},{'L','Y',-3},
1109 {915,912,+3},{915,913,-3},{910,912,+3},{910,913,-3},
1110 {933,970,+2},{933,972,-3}
1111 }
1112 },
1113 { "Arial", 120, 120, 97, 23,
1114 2048, 79, -23, 16, 54, 27, 98, -23, 4, 9,
1115 26,
1116 {
1117 {' ','A',-6},{' ','T',-2},{' ','Y',-2},{'1','1',-8},
1118 {'A',' ',-6},{'A','T',-8},{'A','V',-8},{'A','W',-4},
1119 {'A','Y',-8},{'A','v',-2},{'A','w',-2},{'A','y',-2},
1120 {'F',',',-12},{'F','.',-12},{'F','A',-6},{'L',' ',-4},
1121 {'L','T',-8},{'L','V',-8},{'L','W',-8},{'L','Y',-8},
1122 {915,912,+9},{915,913,-10},{910,912,+9},{910,913,-8},
1123 {933,970,+6},{933,972,-10}
1124 }
1125 },
1126 #if 0 /* this set fails due to +1/-1 errors (rounding bug?), needs investigation. */
1127 { "Arial", 1024 /* usually 1/2 of EM Square */, 1024, 830, 194,
1128 2048, 668, -193, 137, 459, 229, 830, -194, 30, 9,
1129 26,
1130 {
1131 {' ','A',-51},{' ','T',-17},{' ','Y',-17},{'1','1',-68},
1132 {'A',' ',-51},{'A','T',-68},{'A','V',-68},{'A','W',-34},
1133 {'A','Y',-68},{'A','v',-17},{'A','w',-17},{'A','y',-17},
1134 {'F',',',-102},{'F','.',-102},{'F','A',-51},{'L',' ',-34},
1135 {'L','T',-68},{'L','V',-68},{'L','W',-68},{'L','Y',-68},
1136 {915,912,+73},{915,913,-84},{910,912,+76},{910,913,-68},
1137 {933,970,+54},{933,972,-83}
1138 }
1139 }
1140 #endif
1141 };
1142 LOGFONT lf;
1143 HFONT hfont, hfont_old;
1144 KERNINGPAIR *kern_pair;
1145 HDC hdc;
1146 DWORD total_kern_pairs, ret, i, n, matches;
1147
1148 hdc = GetDC(0);
1149
1150 /* GetKerningPairsA maps unicode set of kerning pairs to current code page
1151 * which may render this test unusable, so we're trying to avoid that.
1152 */
1153 SetLastError(0xdeadbeef);
1154 GetKerningPairsW(hdc, 0, NULL);
1155 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
1156 {
1157 win_skip("Skipping the GetKerningPairs test on a Win9x platform\n");
1158 ReleaseDC(0, hdc);
1159 return;
1160 }
1161
1162 for (i = 0; i < sizeof(kd)/sizeof(kd[0]); i++)
1163 {
1164 OUTLINETEXTMETRICW otm;
1165
1166 if (!is_font_installed(kd[i].face_name))
1167 {
1168 trace("%s is not installed so skipping this test\n", kd[i].face_name);
1169 continue;
1170 }
1171
1172 trace("testing font %s, height %d\n", kd[i].face_name, kd[i].height);
1173
1174 memset(&lf, 0, sizeof(lf));
1175 strcpy(lf.lfFaceName, kd[i].face_name);
1176 lf.lfHeight = kd[i].height;
1177 hfont = CreateFontIndirect(&lf);
1178 assert(hfont != 0);
1179
1180 hfont_old = SelectObject(hdc, hfont);
1181
1182 SetLastError(0xdeadbeef);
1183 otm.otmSize = sizeof(otm); /* just in case for Win9x compatibility */
1184 ok(GetOutlineTextMetricsW(hdc, sizeof(otm), &otm) == sizeof(otm), "GetOutlineTextMetricsW error %d\n", GetLastError());
1185
1186 ok(match_off_by_1(kd[i].tmHeight, otm.otmTextMetrics.tmHeight), "expected %d, got %d\n",
1187 kd[i].tmHeight, otm.otmTextMetrics.tmHeight);
1188 ok(match_off_by_1(kd[i].tmAscent, otm.otmTextMetrics.tmAscent), "expected %d, got %d\n",
1189 kd[i].tmAscent, otm.otmTextMetrics.tmAscent);
1190 ok(kd[i].tmDescent == otm.otmTextMetrics.tmDescent, "expected %d, got %d\n",
1191 kd[i].tmDescent, otm.otmTextMetrics.tmDescent);
1192
1193 ok(kd[i].otmEMSquare == otm.otmEMSquare, "expected %u, got %u\n",
1194 kd[i].otmEMSquare, otm.otmEMSquare);
1195 ok(kd[i].otmAscent == otm.otmAscent, "expected %d, got %d\n",
1196 kd[i].otmAscent, otm.otmAscent);
1197 ok(kd[i].otmDescent == otm.otmDescent, "expected %d, got %d\n",
1198 kd[i].otmDescent, otm.otmDescent);
1199 ok(kd[i].otmLineGap == otm.otmLineGap, "expected %u, got %u\n",
1200 kd[i].otmLineGap, otm.otmLineGap);
1201 ok(near_match(kd[i].otmMacDescent, otm.otmMacDescent), "expected %d, got %d\n",
1202 kd[i].otmMacDescent, otm.otmMacDescent);
1203 ok(near_match(kd[i].otmMacAscent, otm.otmMacAscent), "expected %d, got %d\n",
1204 kd[i].otmMacAscent, otm.otmMacAscent);
1205 todo_wine {
1206 ok(kd[i].otmsCapEmHeight == otm.otmsCapEmHeight, "expected %u, got %u\n",
1207 kd[i].otmsCapEmHeight, otm.otmsCapEmHeight);
1208 ok(kd[i].otmsXHeight == otm.otmsXHeight, "expected %u, got %u\n",
1209 kd[i].otmsXHeight, otm.otmsXHeight);
1210 /* FIXME: this one sometimes succeeds due to expected 0, enable it when removing todo */
1211 if (0) ok(kd[i].otmMacLineGap == otm.otmMacLineGap, "expected %u, got %u\n",
1212 kd[i].otmMacLineGap, otm.otmMacLineGap);
1213 ok(kd[i].otmusMinimumPPEM == otm.otmusMinimumPPEM, "expected %u, got %u\n",
1214 kd[i].otmusMinimumPPEM, otm.otmusMinimumPPEM);
1215 }
1216
1217 total_kern_pairs = GetKerningPairsW(hdc, 0, NULL);
1218 trace("total_kern_pairs %u\n", total_kern_pairs);
1219 kern_pair = HeapAlloc(GetProcessHeap(), 0, total_kern_pairs * sizeof(*kern_pair));
1220
1221 #if 0 /* Win98 (GetKerningPairsA) and XP behave differently here, the test passes on XP */
1222 SetLastError(0xdeadbeef);
1223 ret = GetKerningPairsW(hdc, 0, kern_pair);
1224 ok(GetLastError() == ERROR_INVALID_PARAMETER,
1225 "got error %ld, expected ERROR_INVALID_PARAMETER\n", GetLastError());
1226 ok(ret == 0, "got %lu, expected 0\n", ret);
1227 #endif
1228
1229 ret = GetKerningPairsW(hdc, 100, NULL);
1230 ok(ret == total_kern_pairs, "got %u, expected %u\n", ret, total_kern_pairs);
1231
1232 ret = GetKerningPairsW(hdc, total_kern_pairs/2, kern_pair);
1233 ok(ret == total_kern_pairs/2, "got %u, expected %u\n", ret, total_kern_pairs/2);
1234
1235 ret = GetKerningPairsW(hdc, total_kern_pairs, kern_pair);
1236 ok(ret == total_kern_pairs, "got %u, expected %u\n", ret, total_kern_pairs);
1237
1238 matches = 0;
1239
1240 for (n = 0; n < ret; n++)
1241 {
1242 DWORD j;
1243 #if 0
1244 if (kern_pair[n].wFirst < 127 && kern_pair[n].wSecond < 127)
1245 trace("{'%c','%c',%d},\n",
1246 kern_pair[n].wFirst, kern_pair[n].wSecond, kern_pair[n].iKernAmount);
1247 #endif
1248 for (j = 0; j < kd[i].total_kern_pairs; j++)
1249 {
1250 if (kern_pair[n].wFirst == kd[i].kern_pair[j].wFirst &&
1251 kern_pair[n].wSecond == kd[i].kern_pair[j].wSecond)
1252 {
1253 ok(kern_pair[n].iKernAmount == kd[i].kern_pair[j].iKernAmount,
1254 "pair %d:%d got %d, expected %d\n",
1255 kern_pair[n].wFirst, kern_pair[n].wSecond,
1256 kern_pair[n].iKernAmount, kd[i].kern_pair[j].iKernAmount);
1257 matches++;
1258 }
1259 }
1260 }
1261
1262 ok(matches == kd[i].total_kern_pairs, "got matches %u, expected %u\n",
1263 matches, kd[i].total_kern_pairs);
1264
1265 HeapFree(GetProcessHeap(), 0, kern_pair);
1266
1267 SelectObject(hdc, hfont_old);
1268 DeleteObject(hfont);
1269 }
1270
1271 ReleaseDC(0, hdc);
1272 }
1273
1274 static void test_height_selection(void)
1275 {
1276 static const struct font_data
1277 {
1278 const char face_name[LF_FACESIZE];
1279 int requested_height;
1280 int weight, height, ascent, descent, int_leading, ext_leading, dpi;
1281 } fd[] =
1282 {
1283 {"Tahoma", -12, FW_NORMAL, 14, 12, 2, 2, 0, 96 },
1284 {"Tahoma", -24, FW_NORMAL, 29, 24, 5, 5, 0, 96 },
1285 {"Tahoma", -48, FW_NORMAL, 58, 48, 10, 10, 0, 96 },
1286 {"Tahoma", -96, FW_NORMAL, 116, 96, 20, 20, 0, 96 },
1287 {"Tahoma", -192, FW_NORMAL, 232, 192, 40, 40, 0, 96 },
1288 {"Tahoma", 12, FW_NORMAL, 12, 10, 2, 2, 0, 96 },
1289 {"Tahoma", 24, FW_NORMAL, 24, 20, 4, 4, 0, 96 },
1290 {"Tahoma", 48, FW_NORMAL, 48, 40, 8, 8, 0, 96 },
1291 {"Tahoma", 96, FW_NORMAL, 96, 80, 16, 17, 0, 96 },
1292 {"Tahoma", 192, FW_NORMAL, 192, 159, 33, 33, 0, 96 }
1293 };
1294 HDC hdc;
1295 LOGFONT lf;
1296 HFONT hfont, old_hfont;
1297 TEXTMETRIC tm;
1298 INT ret, i;
1299
1300 hdc = CreateCompatibleDC(0);
1301 assert(hdc);
1302
1303 for (i = 0; i < sizeof(fd)/sizeof(fd[0]); i++)
1304 {
1305 if (!is_truetype_font_installed(fd[i].face_name))
1306 {
1307 skip("%s is not installed\n", fd[i].face_name);
1308 continue;
1309 }
1310
1311 memset(&lf, 0, sizeof(lf));
1312 lf.lfHeight = fd[i].requested_height;
1313 lf.lfWeight = fd[i].weight;
1314 strcpy(lf.lfFaceName, fd[i].face_name);
1315
1316 hfont = CreateFontIndirect(&lf);
1317 assert(hfont);
1318
1319 old_hfont = SelectObject(hdc, hfont);
1320 ret = GetTextMetrics(hdc, &tm);
1321 ok(ret, "GetTextMetrics error %d\n", GetLastError());
1322 if(fd[i].dpi == tm.tmDigitizedAspectX)
1323 {
1324 trace("found font %s, height %d charset %x dpi %d\n", lf.lfFaceName, lf.lfHeight, lf.lfCharSet, fd[i].dpi);
1325 ok(tm.tmWeight == fd[i].weight, "%s(%d): tm.tmWeight %d != %d\n", fd[i].face_name, fd[i].requested_height, tm.tmWeight, fd[i].weight);
1326 ok(match_off_by_1(tm.tmHeight, fd[i].height), "%s(%d): tm.tmHeight %d != %d\n", fd[i].face_name, fd[i].requested_height, tm.tmHeight, fd[i].height);
1327 ok(match_off_by_1(tm.tmAscent, fd[i].ascent), "%s(%d): tm.tmAscent %d != %d\n", fd[i].face_name, fd[i].requested_height, tm.tmAscent, fd[i].ascent);
1328 ok(match_off_by_1(tm.tmDescent, fd[i].descent), "%s(%d): tm.tmDescent %d != %d\n", fd[i].face_name, fd[i].requested_height, tm.tmDescent, fd[i].descent);
1329 #if 0 /* FIXME: calculation of tmInternalLeading in Wine doesn't match what Windows does */
1330 ok(tm.tmInternalLeading == fd[i].int_leading, "%s(%d): tm.tmInternalLeading %d != %d\n", fd[i].face_name, fd[i].requested_height, tm.tmInternalLeading, fd[i].int_leading);
1331 #endif
1332 ok(tm.tmExternalLeading == fd[i].ext_leading, "%s(%d): tm.tmExternalLeading %d != %d\n", fd[i].face_name, fd[i].requested_height, tm.tmExternalLeading, fd[i].ext_leading);
1333 }
1334
1335 SelectObject(hdc, old_hfont);
1336 DeleteObject(hfont);
1337 }
1338
1339 DeleteDC(hdc);
1340 }
1341
1342 static void test_GetOutlineTextMetrics(void)
1343 {
1344 OUTLINETEXTMETRIC *otm;
1345 LOGFONT lf;
1346 HFONT hfont, hfont_old;
1347 HDC hdc;
1348 DWORD ret, otm_size;
1349 LPSTR unset_ptr;
1350
1351 if (!is_font_installed("Arial"))
1352 {
1353 skip("Arial is not installed\n");
1354 return;
1355 }
1356
1357 hdc = GetDC(0);
1358
1359 memset(&lf, 0, sizeof(lf));
1360 strcpy(lf.lfFaceName, "Arial");
1361 lf.lfHeight = -13;
1362 lf.lfWeight = FW_NORMAL;
1363 lf.lfPitchAndFamily = DEFAULT_PITCH;
1364 lf.lfQuality = PROOF_QUALITY;
1365 hfont = CreateFontIndirect(&lf);
1366 assert(hfont != 0);
1367
1368 hfont_old = SelectObject(hdc, hfont);
1369 otm_size = GetOutlineTextMetrics(hdc, 0, NULL);
1370 trace("otm buffer size %u (0x%x)\n", otm_size, otm_size);
1371
1372 otm = HeapAlloc(GetProcessHeap(), 0, otm_size);
1373
1374 memset(otm, 0xAA, otm_size);
1375 SetLastError(0xdeadbeef);
1376 otm->otmSize = sizeof(*otm); /* just in case for Win9x compatibility */
1377 ret = GetOutlineTextMetrics(hdc, otm->otmSize, otm);
1378 ok(ret == 1 /* Win9x */ ||
1379 ret == otm->otmSize /* XP*/,
1380 "expected %u, got %u, error %d\n", otm->otmSize, ret, GetLastError());
1381 if (ret != 1) /* Win9x doesn't care about pointing beyond of the buffer */
1382 {
1383 ok(otm->otmpFamilyName == NULL, "expected NULL got %p\n", otm->otmpFamilyName);
1384 ok(otm->otmpFaceName == NULL, "expected NULL got %p\n", otm->otmpFaceName);
1385 ok(otm->otmpStyleName == NULL, "expected NULL got %p\n", otm->otmpStyleName);
1386 ok(otm->otmpFullName == NULL, "expected NULL got %p\n", otm->otmpFullName);
1387 }
1388
1389 memset(otm, 0xAA, otm_size);
1390 SetLastError(0xdeadbeef);
1391 otm->otmSize = otm_size; /* just in case for Win9x compatibility */
1392 ret = GetOutlineTextMetrics(hdc, otm->otmSize, otm);
1393 ok(ret == 1 /* Win9x */ ||
1394 ret == otm->otmSize /* XP*/,
1395 "expected %u, got %u, error %d\n", otm->otmSize, ret, GetLastError());
1396 if (ret != 1) /* Win9x doesn't care about pointing beyond of the buffer */
1397 {
1398 ok(otm->otmpFamilyName != NULL, "expected not NULL got %p\n", otm->otmpFamilyName);
1399 ok(otm->otmpFaceName != NULL, "expected not NULL got %p\n", otm->otmpFaceName);
1400 ok(otm->otmpStyleName != NULL, "expected not NULL got %p\n", otm->otmpStyleName);
1401 ok(otm->otmpFullName != NULL, "expected not NULL got %p\n", otm->otmpFullName);
1402 }
1403
1404 /* ask about truncated data */
1405 memset(otm, 0xAA, otm_size);
1406 memset(&unset_ptr, 0xAA, sizeof(unset_ptr));
1407 SetLastError(0xdeadbeef);
1408 otm->otmSize = sizeof(*otm) - sizeof(LPSTR); /* just in case for Win9x compatibility */
1409 ret = GetOutlineTextMetrics(hdc, otm->otmSize, otm);
1410 ok(ret == 1 /* Win9x */ ||
1411 ret == otm->otmSize /* XP*/,
1412 "expected %u, got %u, error %d\n", otm->otmSize, ret, GetLastError());
1413 if (ret != 1) /* Win9x doesn't care about pointing beyond of the buffer */
1414 {
1415 ok(otm->otmpFamilyName == NULL, "expected NULL got %p\n", otm->otmpFamilyName);
1416 ok(otm->otmpFaceName == NULL, "expected NULL got %p\n", otm->otmpFaceName);
1417 ok(otm->otmpStyleName == NULL, "expected NULL got %p\n", otm->otmpStyleName);
1418 }
1419 ok(otm->otmpFullName == unset_ptr, "expected %p got %p\n", unset_ptr, otm->otmpFullName);
1420
1421 HeapFree(GetProcessHeap(), 0, otm);
1422
1423 SelectObject(hdc, hfont_old);
1424 DeleteObject(hfont);
1425
1426 ReleaseDC(0, hdc);
1427 }
1428
1429 static void testJustification(HDC hdc, PSTR str, RECT *clientArea)
1430 {
1431 INT y,
1432 breakCount,
1433 justifiedWidth = 0, /* to test GetTextExtentExPointW() */
1434 areaWidth = clientArea->right - clientArea->left,
1435 nErrors = 0, e;
1436 BOOL lastExtent = FALSE;
1437 PSTR pFirstChar, pLastChar;
1438 SIZE size;
1439 TEXTMETRICA tm;
1440 struct err
1441 {
1442 char extent[100];
1443 int GetTextExtentExPointWWidth;
1444 } error[10];
1445
1446 GetTextMetricsA(hdc, &tm);
1447 y = clientArea->top;
1448 do {
1449 breakCount = 0;
1450 while (*str == tm.tmBreakChar) str++; /* skip leading break chars */
1451 pFirstChar = str;
1452
1453 do {
1454 pLastChar = str;
1455
1456 /* if not at the end of the string, ... */
1457 if (*str == '\0') break;
1458 /* ... add the next word to the current extent */
1459 while (*str != '\0' && *str++ != tm.tmBreakChar);
1460 breakCount++;
1461 SetTextJustification(hdc, 0, 0);
1462 GetTextExtentPoint32(hdc, pFirstChar, str - pFirstChar - 1, &size);
1463 } while ((int) size.cx < areaWidth);
1464
1465 /* ignore trailing break chars */
1466 breakCount--;
1467 while (*(pLastChar - 1) == tm.tmBreakChar)
1468 {
1469 pLastChar--;
1470 breakCount--;
1471 }
1472
1473 if (*str == '\0' || breakCount <= 0) pLastChar = str;
1474
1475 SetTextJustification(hdc, 0, 0);
1476 GetTextExtentPoint32(hdc, pFirstChar, pLastChar - pFirstChar, &size);
1477
1478 /* do not justify the last extent */
1479 if (*str != '\0' && breakCount > 0)
1480 {
1481 SetTextJustification(hdc, areaWidth - size.cx, breakCount);
1482 GetTextExtentPoint32(hdc, pFirstChar, pLastChar - pFirstChar, &size);
1483 justifiedWidth = size.cx;
1484 }
1485 else lastExtent = TRUE;
1486
1487 /* catch errors and report them */
1488 if (!lastExtent && (justifiedWidth != areaWidth))
1489 {
1490 memset(error[nErrors].extent, 0, 100);
1491 memcpy(error[nErrors].extent, pFirstChar, pLastChar - pFirstChar);
1492 error[nErrors].GetTextExtentExPointWWidth = justifiedWidth;
1493 nErrors++;
1494 }
1495
1496 y += size.cy;
1497 str = pLastChar;
1498 } while (*str && y < clientArea->bottom);
1499
1500 for (e = 0; e < nErrors; e++)
1501 {
1502 /* The width returned by GetTextExtentPoint32() is exactly the same
1503 returned by GetTextExtentExPointW() - see dlls/gdi32/font.c */
1504 ok(error[e].GetTextExtentExPointWWidth == areaWidth,
1505 "GetTextExtentPointW() for \"%s\" should have returned a width of %d, not %d.\n",
1506 error[e].extent, areaWidth, error[e].GetTextExtentExPointWWidth);
1507 }
1508 }
1509
1510 static void test_SetTextJustification(void)
1511 {
1512 HDC hdc;
1513 RECT clientArea;
1514 LOGFONTA lf;
1515 HFONT hfont;
1516 HWND hwnd;
1517 static char testText[] =
1518 "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do "
1519 "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut "
1520 "enim ad minim veniam, quis nostrud exercitation ullamco laboris "
1521 "nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in "
1522 "reprehenderit in voluptate velit esse cillum dolore eu fugiat "
1523 "nulla pariatur. Excepteur sint occaecat cupidatat non proident, "
1524 "sunt in culpa qui officia deserunt mollit anim id est laborum.";
1525
1526 hwnd = CreateWindowExA(0, "static", "", WS_POPUP, 0,0, 400,400, 0, 0, 0, NULL);
1527 GetClientRect( hwnd, &clientArea );
1528 hdc = GetDC( hwnd );
1529
1530 memset(&lf, 0, sizeof lf);
1531 lf.lfCharSet = ANSI_CHARSET;
1532 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
1533 lf.lfWeight = FW_DONTCARE;
1534 lf.lfHeight = 20;
1535 lf.lfQuality = DEFAULT_QUALITY;
1536 lstrcpyA(lf.lfFaceName, "Times New Roman");
1537 hfont = create_font("Times New Roman", &lf);
1538 SelectObject(hdc, hfont);
1539
1540 testJustification(hdc, testText, &clientArea);
1541
1542 DeleteObject(hfont);
1543 ReleaseDC(hwnd, hdc);
1544 DestroyWindow(hwnd);
1545 }
1546
1547 static BOOL get_glyph_indices(INT charset, UINT code_page, WORD *idx, UINT count, BOOL unicode)
1548 {
1549 HDC hdc;
1550 LOGFONTA lf;
1551 HFONT hfont, hfont_old;
1552 CHARSETINFO csi;
1553 FONTSIGNATURE fs;
1554 INT cs;
1555 DWORD i, ret;
1556 char name[64];
1557
1558 assert(count <= 128);
1559
1560 memset(&lf, 0, sizeof(lf));
1561
1562 lf.lfCharSet = charset;
1563 lf.lfHeight = 10;
1564 lstrcpyA(lf.lfFaceName, "Arial");
1565 SetLastError(0xdeadbeef);
1566 hfont = CreateFontIndirectA(&lf);
1567 ok(hfont != 0, "CreateFontIndirectA error %u\n", GetLastError());
1568
1569 hdc = GetDC(0);
1570 hfont_old = SelectObject(hdc, hfont);
1571
1572 cs = GetTextCharsetInfo(hdc, &fs, 0);
1573 ok(cs == charset, "expected %d, got %d\n", charset, cs);
1574
1575 SetLastError(0xdeadbeef);
1576 ret = GetTextFaceA(hdc, sizeof(name), name);
1577 ok(ret, "GetTextFaceA error %u\n", GetLastError());
1578
1579 if (charset == SYMBOL_CHARSET)
1580 {
1581 ok(strcmp("Arial", name), "face name should NOT be Arial\n");
1582 ok(fs.fsCsb[0] & (1 << 31), "symbol encoding should be available\n");
1583 }
1584 else
1585 {
1586 ok(!strcmp("Arial", name), "face name should be Arial, not %s\n", name);
1587 ok(!(fs.fsCsb[0] & (1 << 31)), "symbol encoding should NOT be available\n");
1588 }
1589
1590 if (!TranslateCharsetInfo((DWORD *)(INT_PTR)cs, &csi, TCI_SRCCHARSET))
1591 {
1592 trace("Can't find codepage for charset %d\n", cs);
1593 ReleaseDC(0, hdc);
1594 return FALSE;
1595 }
1596 ok(csi.ciACP == code_page, "expected %d, got %d\n", code_page, csi.ciACP);
1597
1598 if (unicode)
1599 {
1600 char ansi_buf[128];
1601 WCHAR unicode_buf[128];
1602
1603 for (i = 0; i < count; i++) ansi_buf[i] = (BYTE)(i + 128);
1604
1605 MultiByteToWideChar(code_page, 0, ansi_buf, count, unicode_buf, count);
1606
1607 SetLastError(0xdeadbeef);
1608 ret = pGetGlyphIndicesW(hdc, unicode_buf, count, idx, 0);
1609 ok(ret == count, "GetGlyphIndicesW error %u\n", GetLastError());
1610 }
1611 else
1612 {
1613 char ansi_buf[128];
1614
1615 for (i = 0; i < count; i++) ansi_buf[i] = (BYTE)(i + 128);
1616
1617 SetLastError(0xdeadbeef);
1618 ret = pGetGlyphIndicesA(hdc, ansi_buf, count, idx, 0);
1619 ok(ret == count, "GetGlyphIndicesA error %u\n", GetLastError());
1620 }
1621
1622 SelectObject(hdc, hfont_old);
1623 DeleteObject(hfont);
1624
1625 ReleaseDC(0, hdc);
1626
1627 return TRUE;
1628 }
1629
1630 static void test_font_charset(void)
1631 {
1632 static struct charset_data
1633 {
1634 INT charset;
1635 UINT code_page;
1636 WORD font_idxA[128], font_idxW[128];
1637 } cd[] =
1638 {
1639 { ANSI_CHARSET, 1252 },
1640 { RUSSIAN_CHARSET, 1251 },
1641 { SYMBOL_CHARSET, CP_SYMBOL } /* keep it as the last one */
1642 };
1643 int i;
1644
1645 if (!pGetGlyphIndicesA || !pGetGlyphIndicesW)
1646 {
1647 win_skip("Skipping the font charset test on a Win9x platform\n");
1648 return;
1649 }
1650
1651 if (!is_font_installed("Arial"))
1652 {
1653 skip("Arial is not installed\n");
1654 return;
1655 }
1656
1657 for (i = 0; i < sizeof(cd)/sizeof(cd[0]); i++)
1658 {
1659 if (cd[i].charset == SYMBOL_CHARSET)
1660 {
1661 if (!is_font_installed("Symbol") && !is_font_installed("Wingdings"))
1662 {
1663 skip("Symbol or Wingdings is not installed\n");
1664 break;
1665 }
1666 }
1667 get_glyph_indices(cd[i].charset, cd[i].code_page, cd[i].font_idxA, 128, FALSE);
1668 get_glyph_indices(cd[i].charset, cd[i].code_page, cd[i].font_idxW, 128, TRUE);
1669 ok(!memcmp(cd[i].font_idxA, cd[i].font_idxW, 128*sizeof(WORD)), "%d: indices don't match\n", i);
1670 }
1671
1672 ok(memcmp(cd[0].font_idxW, cd[1].font_idxW, 128*sizeof(WORD)), "0 vs 1: indices shouldn't match\n");
1673 if (i > 2)
1674 {
1675 ok(memcmp(cd[0].font_idxW, cd[2].font_idxW, 128*sizeof(WORD)), "0 vs 2: indices shouldn't match\n");
1676 ok(memcmp(cd[1].font_idxW, cd[2].font_idxW, 128*sizeof(WORD)), "1 vs 2: indices shouldn't match\n");
1677 }
1678 else
1679 skip("Symbol or Wingdings is not installed\n");
1680 }
1681
1682 static void test_GetFontUnicodeRanges(void)
1683 {
1684 LOGFONTA lf;
1685 HDC hdc;
1686 HFONT hfont, hfont_old;
1687 DWORD size;
1688 GLYPHSET *gs;
1689
1690 if (!pGetFontUnicodeRanges)
1691 {
1692 win_skip("GetFontUnicodeRanges not available before W2K\n");
1693 return;
1694 }
1695
1696 memset(&lf, 0, sizeof(lf));
1697 lstrcpyA(lf.lfFaceName, "Arial");
1698 hfont = create_font("Arial", &lf);
1699
1700 hdc = GetDC(0);
1701 hfont_old = SelectObject(hdc, hfont);
1702
1703 size = pGetFontUnicodeRanges(NULL, NULL);
1704 ok(!size, "GetFontUnicodeRanges succeeded unexpectedly\n");
1705
1706 size = pGetFontUnicodeRanges(hdc, NULL);
1707 ok(size, "GetFontUnicodeRanges failed unexpectedly\n");
1708
1709 gs = HeapAlloc(GetProcessHeap(), 0, size);
1710
1711 size = pGetFontUnicodeRanges(hdc, gs);
1712 ok(size, "GetFontUnicodeRanges failed\n");
1713 #if 0
1714 for (i = 0; i < gs->cRanges; i++)
1715 trace("%03d wcLow %04x cGlyphs %u\n", i, gs->ranges[i].wcLow, gs->ranges[i].cGlyphs);
1716 #endif
1717 trace("found %u ranges\n", gs->cRanges);
1718
1719 HeapFree(GetProcessHeap(), 0, gs);
1720
1721 SelectObject(hdc, hfont_old);
1722 DeleteObject(hfont);
1723 ReleaseDC(NULL, hdc);
1724 }
1725
1726 #define MAX_ENUM_FONTS 4096
1727
1728 struct enum_font_data
1729 {
1730 int total;
1731 LOGFONT lf[MAX_ENUM_FONTS];
1732 };
1733
1734 struct enum_font_dataW
1735 {
1736 int total;
1737 LOGFONTW lf[MAX_ENUM_FONTS];
1738 };
1739
1740 static INT CALLBACK arial_enum_proc(const LOGFONT *lf, const TEXTMETRIC *tm, DWORD type, LPARAM lParam)
1741 {
1742 struct enum_font_data *efd = (struct enum_font_data *)lParam;
1743
1744 ok(lf->lfHeight == tm->tmHeight, "lfHeight %d != tmHeight %d\n", lf->lfHeight, tm->tmHeight);
1745
1746 if (type != TRUETYPE_FONTTYPE) return 1;
1747 #if 0
1748 trace("enumed font \"%s\", charset %d, height %d, weight %d, italic %d\n",
1749 lf->lfFaceName, lf->lfCharSet, lf->lfHeight, lf->lfWeight, lf->lfItalic);
1750 #endif
1751 if (efd->total < MAX_ENUM_FONTS)
1752 efd->lf[efd->total++] = *lf;
1753 else
1754 trace("enum tests invalid; you have more than %d fonts\n", MAX_ENUM_FONTS);
1755
1756 return 1;
1757 }
1758
1759 static INT CALLBACK arial_enum_procw(const LOGFONTW *lf, const TEXTMETRICW *tm, DWORD type, LPARAM lParam)
1760 {
1761 struct enum_font_dataW *efd = (struct enum_font_dataW *)lParam;
1762
1763 ok(lf->lfHeight == tm->tmHeight, "lfHeight %d != tmHeight %d\n", lf->lfHeight, tm->tmHeight);
1764
1765 if (type != TRUETYPE_FONTTYPE) return 1;
1766 #if 0
1767 trace("enumed font \"%s\", charset %d, height %d, weight %d, italic %d\n",
1768 lf->lfFaceName, lf->lfCharSet, lf->lfHeight, lf->lfWeight, lf->lfItalic);
1769 #endif
1770 if (efd->total < MAX_ENUM_FONTS)
1771 efd->lf[efd->total++] = *lf;
1772 else
1773 trace("enum tests invalid; you have more than %d fonts\n", MAX_ENUM_FONTS);
1774
1775 return 1;
1776 }
1777
1778 static void get_charset_stats(struct enum_font_data *efd,
1779 int *ansi_charset, int *symbol_charset,
1780 int *russian_charset)
1781 {
1782 int i;
1783
1784 *ansi_charset = 0;
1785 *symbol_charset = 0;
1786 *russian_charset = 0;
1787
1788 for (i = 0; i < efd->total; i++)
1789 {
1790 switch (efd->lf[i].lfCharSet)
1791 {
1792 case ANSI_CHARSET:
1793 (*ansi_charset)++;
1794 break;
1795 case SYMBOL_CHARSET:
1796 (*symbol_charset)++;
1797 break;
1798 case RUSSIAN_CHARSET:
1799 (*russian_charset)++;
1800 break;
1801 }
1802 }
1803 }
1804
1805 static void get_charset_statsW(struct enum_font_dataW *efd,
1806 int *ansi_charset, int *symbol_charset,
1807 int *russian_charset)
1808 {
1809 int i;
1810
1811 *ansi_charset = 0;
1812 *symbol_charset = 0;
1813 *russian_charset = 0;
1814
1815 for (i = 0; i < efd->total; i++)
1816 {
1817 switch (efd->lf[i].lfCharSet)
1818 {
1819 case ANSI_CHARSET:
1820 (*ansi_charset)++;
1821 break;
1822 case SYMBOL_CHARSET:
1823 (*symbol_charset)++;
1824 break;
1825 case RUSSIAN_CHARSET:
1826 (*russian_charset)++;
1827 break;
1828 }
1829 }
1830 }
1831
1832 static void test_EnumFontFamilies(const char *font_name, INT font_charset)
1833 {
1834 struct enum_font_data efd;
1835 struct enum_font_dataW efdw;
1836 LOGFONT lf;
1837 HDC hdc;
1838 int i, ret, ansi_charset, symbol_charset, russian_charset;
1839
1840 trace("Testing font %s, charset %d\n", *font_name ? font_name : "<empty>", font_charset);
1841
1842 if (*font_name && !is_truetype_font_installed(font_name))
1843 {
1844 skip("%s is not installed\n", font_name);
1845 return;
1846 }
1847
1848 hdc = GetDC(0);
1849
1850 /* Observed behaviour: EnumFontFamilies enumerates aliases like "Arial Cyr"
1851 * while EnumFontFamiliesEx doesn't.
1852 */
1853 if (!*font_name && font_charset == DEFAULT_CHARSET) /* do it only once */
1854 {
1855 /*
1856 * Use EnumFontFamiliesW since win98 crashes when the
1857 * second parameter is NULL using EnumFontFamilies
1858 */
1859 efdw.total = 0;
1860 SetLastError(0xdeadbeef);
1861 ret = EnumFontFamiliesW(hdc, NULL, arial_enum_procw, (LPARAM)&efdw);
1862 ok(ret || GetLastError() == ERROR_CALL_NOT_IMPLEMENTED, "EnumFontFamiliesW error %u\n", GetLastError());
1863 if(ret)
1864 {
1865 get_charset_statsW(&efdw, &ansi_charset, &symbol_charset, &russian_charset);
1866 trace("enumerated ansi %d, symbol %d, russian %d fonts for NULL\n",
1867 ansi_charset, symbol_charset, russian_charset);
1868 ok(efdw.total > 0, "fonts enumerated: NULL\n");
1869 ok(ansi_charset > 0, "NULL family should enumerate ANSI_CHARSET\n");
1870 ok(symbol_charset > 0, "NULL family should enumerate SYMBOL_CHARSET\n");
1871 ok(russian_charset > 0 ||
1872 broken(russian_charset == 0), /* NT4 */
1873 "NULL family should enumerate RUSSIAN_CHARSET\n");
1874 }
1875
1876 efdw.total = 0;
1877 SetLastError(0xdeadbeef);
1878 ret = EnumFontFamiliesExW(hdc, NULL, arial_enum_procw, (LPARAM)&efdw, 0);
1879 ok(ret || GetLastError() == ERROR_CALL_NOT_IMPLEMENTED, "EnumFontFamiliesExW error %u\n", GetLastError());
1880 if(ret)
1881 {
1882 get_charset_statsW(&efdw, &ansi_charset, &symbol_charset, &russian_charset);
1883 trace("enumerated ansi %d, symbol %d, russian %d fonts for NULL\n",
1884 ansi_charset, symbol_charset, russian_charset);
1885 ok(efdw.total > 0, "fonts enumerated: NULL\n");
1886 ok(ansi_charset > 0, "NULL family should enumerate ANSI_CHARSET\n");
1887 ok(symbol_charset > 0, "NULL family should enumerate SYMBOL_CHARSET\n");
1888 ok(russian_charset > 0, "NULL family should enumerate RUSSIAN_CHARSET\n");
1889 }
1890 }
1891
1892 efd.total = 0;
1893 SetLastError(0xdeadbeef);
1894 ret = EnumFontFamilies(hdc, font_name, arial_enum_proc, (LPARAM)&efd);
1895 ok(ret, "EnumFontFamilies error %u\n", GetLastError());
1896 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
1897 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s\n",
1898 ansi_charset, symbol_charset, russian_charset,
1899 *font_name ? font_name : "<empty>");
1900 if (*font_name)
1901 ok(efd.total > 0, "no fonts enumerated: %s\n", font_name);
1902 else
1903 ok(!efd.total, "no fonts should be enumerated for empty font_name\n");
1904 for (i = 0; i < efd.total; i++)
1905 {
1906 /* FIXME: remove completely once Wine is fixed */
1907 if (efd.lf[i].lfCharSet != font_charset)
1908 {
1909 todo_wine
1910 ok(efd.lf[i].lfCharSet == font_charset, "%d: got charset %d\n", i, efd.lf[i].lfCharSet);
1911 }
1912 else
1913 ok(efd.lf[i].lfCharSet == font_charset, "%d: got charset %d\n", i, efd.lf[i].lfCharSet);
1914 ok(!lstrcmp(efd.lf[i].lfFaceName, font_name), "expected %s, got %s\n",
1915 font_name, efd.lf[i].lfFaceName);
1916 }
1917
1918 memset(&lf, 0, sizeof(lf));
1919 lf.lfCharSet = ANSI_CHARSET;
1920 lstrcpy(lf.lfFaceName, font_name);
1921 efd.total = 0;
1922 SetLastError(0xdeadbeef);
1923 ret = EnumFontFamiliesEx(hdc, &lf, arial_enum_proc, (LPARAM)&efd, 0);
1924 ok(ret, "EnumFontFamiliesEx error %u\n", GetLastError());
1925 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
1926 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s ANSI_CHARSET\n",
1927 ansi_charset, symbol_charset, russian_charset,
1928 *font_name ? font_name : "<empty>");
1929 if (font_charset == SYMBOL_CHARSET)
1930 {
1931 if (*font_name)
1932 ok(efd.total == 0, "no fonts should be enumerated: %s ANSI_CHARSET\n", font_name);
1933 else
1934 ok(efd.total > 0, "no fonts enumerated: %s\n", font_name);
1935 }
1936 else
1937 {
1938 ok(efd.total > 0, "no fonts enumerated: %s ANSI_CHARSET\n", font_name);
1939 for (i = 0; i < efd.total; i++)
1940 {
1941 ok(efd.lf[i].lfCharSet == ANSI_CHARSET, "%d: got charset %d\n", i, efd.lf[i].lfCharSet);
1942 if (*font_name)
1943 ok(!lstrcmp(efd.lf[i].lfFaceName, font_name), "expected %s, got %s\n",
1944 font_name, efd.lf[i].lfFaceName);
1945 }
1946 }
1947
1948 /* DEFAULT_CHARSET should enumerate all available charsets */
1949 memset(&lf, 0, sizeof(lf));
1950 lf.lfCharSet = DEFAULT_CHARSET;
1951 lstrcpy(lf.lfFaceName, font_name);
1952 efd.total = 0;
1953 SetLastError(0xdeadbeef);
1954 EnumFontFamiliesEx(hdc, &lf, arial_enum_proc, (LPARAM)&efd, 0);
1955 ok(ret, "EnumFontFamiliesEx error %u\n", GetLastError());
1956 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
1957 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s DEFAULT_CHARSET\n",
1958 ansi_charset, symbol_charset, russian_charset,
1959 *font_name ? font_name : "<empty>");
1960 ok(efd.total > 0, "no fonts enumerated: %s DEFAULT_CHARSET\n", font_name);
1961 for (i = 0; i < efd.total; i++)
1962 {
1963 if (*font_name)
1964 ok(!lstrcmp(efd.lf[i].lfFaceName, font_name), "expected %s, got %s\n",
1965 font_name, efd.lf[i].lfFaceName);
1966 }
1967 if (*font_name)
1968 {
1969 switch (font_charset)
1970 {
1971 case ANSI_CHARSET:
1972 ok(ansi_charset > 0,
1973 "ANSI_CHARSET should enumerate ANSI_CHARSET for %s\n", font_name);
1974 ok(!symbol_charset,
1975 "ANSI_CHARSET should NOT enumerate SYMBOL_CHARSET for %s\n", font_name);
1976 ok(russian_charset > 0,
1977 "ANSI_CHARSET should enumerate RUSSIAN_CHARSET for %s\n", font_name);
1978 break;
1979 case SYMBOL_CHARSET:
1980 ok(!ansi_charset,
1981 "SYMBOL_CHARSET should NOT enumerate ANSI_CHARSET for %s\n", font_name);
1982 ok(symbol_charset,
1983 "SYMBOL_CHARSET should enumerate SYMBOL_CHARSET for %s\n", font_name);
1984 ok(!russian_charset,
1985 "SYMBOL_CHARSET should NOT enumerate RUSSIAN_CHARSET for %s\n", font_name);
1986 break;
1987 case DEFAULT_CHARSET:
1988 ok(ansi_charset > 0,
1989 "DEFAULT_CHARSET should enumerate ANSI_CHARSET for %s\n", font_name);
1990 ok(symbol_charset > 0,
1991 "DEFAULT_CHARSET should enumerate SYMBOL_CHARSET for %s\n", font_name);
1992 ok(russian_charset > 0,
1993 "DEFAULT_CHARSET should enumerate RUSSIAN_CHARSET for %s\n", font_name);
1994 break;
1995 }
1996 }
1997 else
1998 {
1999 ok(ansi_charset > 0,
2000 "DEFAULT_CHARSET should enumerate ANSI_CHARSET for %s\n", *font_name ? font_name : "<empty>");
2001 ok(symbol_charset > 0,
2002 "DEFAULT_CHARSET should enumerate SYMBOL_CHARSET for %s\n", *font_name ? font_name : "<empty>");
2003 ok(russian_charset > 0,
2004 "DEFAULT_CHARSET should enumerate RUSSIAN_CHARSET for %s\n", *font_name ? font_name : "<empty>");
2005 }
2006
2007 memset(&lf, 0, sizeof(lf));
2008 lf.lfCharSet = SYMBOL_CHARSET;
2009 lstrcpy(lf.lfFaceName, font_name);
2010 efd.total = 0;
2011 SetLastError(0xdeadbeef);
2012 EnumFontFamiliesEx(hdc, &lf, arial_enum_proc, (LPARAM)&efd, 0);
2013 ok(ret, "EnumFontFamiliesEx error %u\n", GetLastError());
2014 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
2015 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s SYMBOL_CHARSET\n",
2016 ansi_charset, symbol_charset, russian_charset,
2017 *font_name ? font_name : "<empty>");
2018 if (*font_name && font_charset == ANSI_CHARSET)
2019 ok(efd.total == 0, "no fonts should be enumerated: %s SYMBOL_CHARSET\n", font_name);
2020 else
2021 {
2022 ok(efd.total > 0, "no fonts enumerated: %s SYMBOL_CHARSET\n", font_name);
2023 for (i = 0; i < efd.total; i++)
2024 {
2025 ok(efd.lf[i].lfCharSet == SYMBOL_CHARSET, "%d: got charset %d\n", i, efd.lf[i].lfCharSet);
2026 if (*font_name)
2027 ok(!lstrcmp(efd.lf[i].lfFaceName, font_name), "expected %s, got %s\n",
2028 font_name, efd.lf[i].lfFaceName);
2029 }
2030
2031 ok(!ansi_charset,
2032 "SYMBOL_CHARSET should NOT enumerate ANSI_CHARSET for %s\n", *font_name ? font_name : "<empty>");
2033 ok(symbol_charset > 0,
2034 "SYMBOL_CHARSET should enumerate SYMBOL_CHARSET for %s\n", *font_name ? font_name : "<empty>");
2035 ok(!russian_charset,
2036 "SYMBOL_CHARSET should NOT enumerate RUSSIAN_CHARSET for %s\n", *font_name ? font_name : "<empty>");
2037 }
2038
2039 ReleaseDC(0, hdc);
2040 }
2041
2042 static void test_negative_width(HDC hdc, const LOGFONTA *lf)
2043 {
2044 HFONT hfont, hfont_prev;
2045 DWORD ret;
2046 GLYPHMETRICS gm1, gm2;
2047 LOGFONTA lf2 = *lf;
2048 WORD idx;
2049 MAT2 mat = { {0,1}, {0,0}, {0,0}, {0,1} };
2050
2051 if(!pGetGlyphIndicesA)
2052 return;
2053
2054 /* negative widths are handled just as positive ones */
2055 lf2.lfWidth = -lf->lfWidth;
2056
2057 SetLastError(0xdeadbeef);
2058 hfont = CreateFontIndirectA(lf);
2059 ok(hfont != 0, "CreateFontIndirect error %u\n", GetLastError());
2060 check_font("original", lf, hfont);
2061
2062 hfont_prev = SelectObject(hdc, hfont);
2063
2064 ret = pGetGlyphIndicesA(hdc, "x", 1, &idx, GGI_MARK_NONEXISTING_GLYPHS);
2065 if (ret == GDI_ERROR || idx == 0xffff)
2066 {
2067 SelectObject(hdc, hfont_prev);
2068 DeleteObject(hfont);
2069 skip("Font %s doesn't contain 'x', skipping the test\n", lf->lfFaceName);
2070 return;
2071 }
2072
2073 /* filling with 0xaa causes false pass under WINEDEBUG=warn+heap */
2074 memset(&gm1, 0xab, sizeof(gm1));
2075 SetLastError(0xdeadbeef);
2076 ret = GetGlyphOutlineA(hdc, 'x', GGO_METRICS, &gm1, 0, NULL, &mat);
2077 ok(ret != GDI_ERROR, "GetGlyphOutline error 0x%x\n", GetLastError());
2078
2079 SelectObject(hdc, hfont_prev);
2080 DeleteObject(hfont);
2081
2082 SetLastError(0xdeadbeef);
2083 hfont = CreateFontIndirectA(&lf2);
2084 ok(hfont != 0, "CreateFontIndirect error %u\n", GetLastError());
2085 check_font("negative width", &lf2, hfont);
2086
2087 hfont_prev = SelectObject(hdc, hfont);
2088
2089 memset(&gm2, 0xbb, sizeof(gm2));
2090 SetLastError(0xdeadbeef);
2091 ret = GetGlyphOutlineA(hdc, 'x', GGO_METRICS, &gm2, 0, NULL, &mat);
2092 ok(ret != GDI_ERROR, "GetGlyphOutline error 0x%x\n", GetLastError());
2093
2094 SelectObject(hdc, hfont_prev);
2095 DeleteObject(hfont);
2096
2097 ok(gm1.gmBlackBoxX == gm2.gmBlackBoxX &&
2098 gm1.gmBlackBoxY == gm2.gmBlackBoxY &&
2099 gm1.gmptGlyphOrigin.x == gm2.gmptGlyphOrigin.x &&
2100 gm1.gmptGlyphOrigin.y == gm2.gmptGlyphOrigin.y &&
2101 gm1.gmCellIncX == gm2.gmCellIncX &&
2102 gm1.gmCellIncY == gm2.gmCellIncY,
2103 "gm1=%d,%d,%d,%d,%d,%d gm2=%d,%d,%d,%d,%d,%d\n",
2104 gm1.gmBlackBoxX, gm1.gmBlackBoxY, gm1.gmptGlyphOrigin.x,
2105 gm1.gmptGlyphOrigin.y, gm1.gmCellIncX, gm1.gmCellIncY,
2106 gm2.gmBlackBoxX, gm2.gmBlackBoxY, gm2.gmptGlyphOrigin.x,
2107 gm2.gmptGlyphOrigin.y, gm2.gmCellIncX, gm2.gmCellIncY);
2108 }
2109
2110 /* PANOSE is 10 bytes in size, need to pack the structure properly */
2111 #include "pshpack2.h"
2112 typedef struct
2113 {
2114 USHORT version;
2115 SHORT xAvgCharWidth;
2116 USHORT usWeightClass;
2117 USHORT usWidthClass;
2118 SHORT fsType;
2119 SHORT ySubscriptXSize;
2120 SHORT ySubscriptYSize;
2121 SHORT ySubscriptXOffset;
2122 SHORT ySubscriptYOffset;
2123 SHORT ySuperscriptXSize;
2124 SHORT ySuperscriptYSize;
2125 SHORT ySuperscriptXOffset;
2126 SHORT ySuperscriptYOffset;
2127 SHORT yStrikeoutSize;
2128 SHORT yStrikeoutPosition;
2129 SHORT sFamilyClass;
2130 PANOSE panose;
2131 ULONG ulUnicodeRange1;
2132 ULONG ulUnicodeRange2;
2133 ULONG ulUnicodeRange3;
2134 ULONG ulUnicodeRange4;
2135 CHAR achVendID[4];
2136 USHORT fsSelection;
2137 USHORT usFirstCharIndex;
2138 USHORT usLastCharIndex;
2139 /* According to the Apple spec, original version didn't have the below fields,
2140 * version numbers were taked from the OpenType spec.
2141 */
2142 /* version 0 (TrueType 1.5) */
2143 USHORT sTypoAscender;
2144 USHORT sTypoDescender;
2145 USHORT sTypoLineGap;
2146 USHORT usWinAscent;
2147 USHORT usWinDescent;
2148 /* version 1 (TrueType 1.66) */
2149 ULONG ulCodePageRange1;
2150 ULONG ulCodePageRange2;
2151 /* version 2 (OpenType 1.2) */
2152 SHORT sxHeight;
2153 SHORT sCapHeight;
2154 USHORT usDefaultChar;
2155 USHORT usBreakChar;
2156 USHORT usMaxContext;
2157 } TT_OS2_V2;
2158 #include "poppack.h"
2159
2160 #ifdef WORDS_BIGENDIAN
2161 #define GET_BE_WORD(x) (x)
2162 #define GET_BE_DWORD(x) (x)
2163 #else
2164 #define GET_BE_WORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
2165 #define GET_BE_DWORD(x) MAKELONG(GET_BE_WORD(HIWORD(x)), GET_BE_WORD(LOWORD(x)));
2166 #endif
2167
2168 #define MS_MAKE_TAG(ch0, ch1, ch2, ch3) \
2169 ((DWORD)(BYTE)(ch0) | ((DWORD)(BYTE)(ch1) << 8) | \
2170 ((DWORD)(BYTE)(ch2) << 16) | ((DWORD)(BYTE)(ch3) << 24))
2171 #define MS_OS2_TAG MS_MAKE_TAG('O','S','/','2')
2172 #define MS_CMAP_TAG MS_MAKE_TAG('c','m','a','p')
2173
2174 typedef struct
2175 {
2176 USHORT version;
2177 USHORT num_tables;
2178 } cmap_header;
2179
2180 typedef struct
2181 {
2182 USHORT plat_id;
2183 USHORT enc_id;
2184 ULONG offset;
2185 } cmap_encoding_record;
2186
2187 typedef struct
2188 {
2189 USHORT format;
2190 USHORT length;
2191 USHORT language;
2192
2193 BYTE glyph_ids[256];
2194 } cmap_format_0;
2195
2196 typedef struct
2197 {
2198 USHORT format;
2199 USHORT length;
2200 USHORT language;
2201
2202 USHORT seg_countx2;
2203 USHORT search_range;
2204 USHORT entry_selector;
2205 USHORT range_shift;
2206
2207 USHORT end_count[1]; /* this is a variable-sized array of length seg_countx2 / 2 */
2208 /* Then follows:
2209 USHORT pad;
2210 USHORT start_count[seg_countx2 / 2];
2211 USHORT id_delta[seg_countx2 / 2];
2212 USHORT id_range_offset[seg_countx2 / 2];
2213 USHORT glyph_ids[];
2214 */
2215 } cmap_format_4;
2216
2217 typedef struct
2218 {
2219 USHORT end_count;
2220 USHORT start_count;
2221 USHORT id_delta;
2222 USHORT id_range_offset;
2223 } cmap_format_4_seg;
2224
2225 static void expect_ff(const TEXTMETRICA *tmA, const TT_OS2_V2 *os2, WORD family, const char *name)
2226 {
2227 ok((tmA->tmPitchAndFamily & 0xf0) == family, "%s: expected family %02x got %02x. panose %d-%d-%d-%d-...\n",
2228 name, family, tmA->tmPitchAndFamily, os2->panose.bFamilyType, os2->panose.bSerifStyle,
2229 os2->panose.bWeight, os2->panose.bProportion);
2230 }
2231
2232 static BOOL get_first_last_from_cmap0(void *ptr, DWORD *first, DWORD *last)
2233 {
2234 int i;
2235 cmap_format_0 *cmap = (cmap_format_0*)ptr;
2236
2237 *first = 256;
2238
2239 for(i = 0; i < 256; i++)
2240 {
2241 if(cmap->glyph_ids[i] == 0) continue;
2242 *last = i;
2243 if(*first == 256) *first = i;
2244 }
2245 if(*first == 256) return FALSE;
2246 return TRUE;
2247 }
2248
2249 static void get_seg4(cmap_format_4 *cmap, USHORT seg_num, cmap_format_4_seg *seg)
2250 {
2251 USHORT segs = GET_BE_WORD(cmap->seg_countx2) / 2;
2252 seg->end_count = GET_BE_WORD(cmap->end_count[seg_num]);
2253 seg->start_count = GET_BE_WORD(cmap->end_count[segs + 1 + seg_num]);
2254 seg->id_delta = GET_BE_WORD(cmap->end_count[2 * segs + 1 + seg_num]);
2255 seg->id_range_offset = GET_BE_WORD(cmap->end_count[3 * segs + 1 + seg_num]);
2256 }
2257
2258 static BOOL get_first_last_from_cmap4(void *ptr, DWORD *first, DWORD *last, DWORD limit)
2259 {
2260 int i;
2261 cmap_format_4 *cmap = (cmap_format_4*)ptr;
2262 USHORT seg_count = GET_BE_WORD(cmap->seg_countx2) / 2;
2263 USHORT const *glyph_ids = cmap->end_count + 4 * seg_count + 1;
2264
2265 *first = 0x10000;
2266
2267 for(i = 0; i < seg_count; i++)
2268 {
2269 DWORD code, index;
2270 cmap_format_4_seg seg;
2271
2272 get_seg4(cmap, i, &seg);
2273 for(code = seg.start_count; code <= seg.end_count; code++)
2274 {
2275 if(seg.id_range_offset == 0)
2276 index = (seg.id_delta + code) & 0xffff;
2277 else
2278 {
2279 index = seg.id_range_offset / 2
2280 + code - seg.start_count
2281 + i - seg_count;
2282
2283 /* some fonts have broken last segment */
2284 if ((char *)(glyph_ids + index + sizeof(*glyph_ids)) < (char *)ptr + limit)
2285 index = GET_BE_WORD(glyph_ids[index]);
2286 else
2287 {
2288 trace("segment %04x/%04x index %04x points to nowhere\n",
2289 seg.start_count, seg.end_count, index);
2290 index = 0;
2291 }
2292 if(index) index += seg.id_delta;
2293 }
2294 if(*first == 0x10000)
2295 *last = *first = code;
2296 else if(index)
2297 *last = code;
2298 }
2299 }
2300
2301 if(*first == 0x10000) return FALSE;
2302 return TRUE;
2303 }
2304
2305 static void *get_cmap(cmap_header *header, USHORT plat_id, USHORT enc_id)
2306 {
2307 USHORT i;
2308 cmap_encoding_record *record = (cmap_encoding_record *)(header + 1);
2309
2310 for(i = 0; i < GET_BE_WORD(header->num_tables); i++)
2311 {
2312 if(GET_BE_WORD(record->plat_id) == plat_id && GET_BE_WORD(record->enc_id) == enc_id)
2313 return (BYTE *)header + GET_BE_DWORD(record->offset);
2314 record++;
2315 }
2316 return NULL;
2317 }
2318
2319 typedef enum
2320 {
2321 cmap_none,
2322 cmap_ms_unicode,
2323 cmap_ms_symbol
2324 } cmap_type;
2325
2326 static BOOL get_first_last_from_cmap(HDC hdc, DWORD *first, DWORD *last, cmap_type *cmap_type)
2327 {
2328 LONG size, ret;
2329 cmap_header *header;
2330 void *cmap;
2331 BOOL r = FALSE;
2332 WORD format;
2333
2334 size = GetFontData(hdc, MS_CMAP_TAG, 0, NULL, 0);
2335 ok(size != GDI_ERROR, "no cmap table found\n");
2336 if(size == GDI_ERROR) return FALSE;
2337
2338 header = HeapAlloc(GetProcessHeap(), 0, size);
2339 ret = GetFontData(hdc, MS_CMAP_TAG, 0, header, size);
2340 ok(ret == size, "GetFontData should return %u not %u\n", size, ret);
2341 ok(GET_BE_WORD(header->version) == 0, "got cmap version %d\n", GET_BE_WORD(header->version));
2342
2343 cmap = get_cmap(header, 3, 1);
2344 if(cmap)
2345 *cmap_type = cmap_ms_unicode;
2346 else
2347 {
2348 cmap = get_cmap(header, 3, 0);
2349 if(cmap) *cmap_type = cmap_ms_symbol;
2350 }
2351 if(!cmap)
2352 {
2353 *cmap_type = cmap_none;
2354 goto end;
2355 }
2356
2357 format = GET_BE_WORD(*(WORD *)cmap);
2358 switch(format)
2359 {
2360 case 0:
2361 r = get_first_last_from_cmap0(cmap, first, last);
2362 break;
2363 case 4:
2364 r = get_first_last_from_cmap4(cmap, first, last, size);
2365 break;
2366 default:
2367 trace("unhandled cmap format %d\n", format);
2368 break;
2369 }
2370
2371 end:
2372 HeapFree(GetProcessHeap(), 0, header);
2373 return r;
2374 }
2375
2376 static void test_text_metrics(const LOGFONTA *lf)
2377 {
2378 HDC hdc;
2379 HFONT hfont, hfont_old;
2380 TEXTMETRICA tmA;
2381 TT_OS2_V2 tt_os2;
2382 LONG size, ret;
2383 const char *font_name = lf->lfFaceName;
2384 DWORD cmap_first = 0, cmap_last = 0;
2385 cmap_type cmap_type;
2386
2387 hdc = GetDC(0);
2388
2389 SetLastError(0xdeadbeef);
2390 hfont = CreateFontIndirectA(lf);
2391 ok(hfont != 0, "CreateFontIndirect error %u\n", GetLastError());
2392
2393 hfont_old = SelectObject(hdc, hfont);
2394
2395 size = GetFontData(hdc, MS_OS2_TAG, 0, NULL, 0);
2396 if (size == GDI_ERROR)
2397 {
2398 trace("OS/2 chunk was not found\n");
2399 goto end_of_test;
2400 }
2401 if (size > sizeof(tt_os2))
2402 {
2403 trace("got too large OS/2 chunk of size %u\n", size);
2404 size = sizeof(tt_os2);
2405 }
2406
2407 memset(&tt_os2, 0, sizeof(tt_os2));
2408 ret = GetFontData(hdc, MS_OS2_TAG, 0, &tt_os2, size);
2409 ok(ret == size, "GetFontData should return %u not %u\n", size, ret);
2410
2411 SetLastError(0xdeadbeef);
2412 ret = GetTextMetricsA(hdc, &tmA);
2413 ok(ret, "GetTextMetricsA error %u\n", GetLastError());
2414
2415 if(!get_first_last_from_cmap(hdc, &cmap_first, &cmap_last, &cmap_type))
2416 {
2417 skip("Unable to retrieve first and last glyphs from cmap\n");
2418 }
2419 else
2420 {
2421 USHORT expect_first_A, expect_last_A, expect_break_A, expect_default_A;
2422 USHORT expect_first_W, expect_last_W, expect_break_W, expect_default_W;
2423 UINT os2_first_char, os2_last_char, default_char, break_char;
2424 USHORT version;
2425 TEXTMETRICW tmW;
2426
2427 version = GET_BE_WORD(tt_os2.version);
2428
2429 os2_first_char = GET_BE_WORD(tt_os2.usFirstCharIndex);
2430 os2_last_char = GET_BE_WORD(tt_os2.usLastCharIndex);
2431 default_char = GET_BE_WORD(tt_os2.usDefaultChar);
2432 break_char = GET_BE_WORD(tt_os2.usBreakChar);
2433
2434 trace("font %s charset %u: %x-%x (%x-%x) default %x break %x OS/2 version %u vendor %4.4s\n",
2435 font_name, lf->lfCharSet, os2_first_char, os2_last_char, cmap_first, cmap_last,
2436 default_char, break_char, version, (LPCSTR)&tt_os2.achVendID);
2437
2438 if (cmap_type == cmap_ms_symbol || (cmap_first >= 0xf000 && cmap_first < 0xf100))
2439 {
2440 expect_first_W = 0;
2441 switch(GetACP())
2442 {
2443 case 1257: /* Baltic */
2444 expect_last_W = 0xf8fd;
2445 break;
2446 default:
2447 expect_last_W = 0xf0ff;
2448 }
2449 expect_break_W = 0x20;
2450 expect_default_W = expect_break_W - 1;
2451 expect_first_A = 0x1e;
2452 expect_last_A = min(os2_last_char - os2_first_char + 0x20, 0xff);
2453 }
2454 else
2455 {
2456 expect_first_W = cmap_first;
2457 expect_last_W = min(cmap_last, os2_last_char);
2458 if(os2_first_char <= 1)
2459 expect_break_W = os2_first_char + 2;
2460 else if(os2_first_char > 0xff)
2461 expect_break_W = 0x20;
2462 else
2463 expect_break_W = os2_first_char;
2464 expect_default_W = expect_break_W - 1;
2465 expect_first_A = expect_default_W - 1;
2466 expect_last_A = min(expect_last_W, 0xff);
2467 }
2468 expect_break_A = expect_break_W;
2469 expect_default_A = expect_default_W;
2470
2471 /* Wine currently uses SYMBOL_CHARSET to identify whether the ANSI metrics need special handling */
2472 if(cmap_type != cmap_ms_symbol && tmA.tmCharSet == SYMBOL_CHARSET && expect_first_A != 0x1e)
2473 todo_wine ok(tmA.tmFirstChar == expect_first_A ||
2474 tmA.tmFirstChar == expect_first_A + 1 /* win9x */,
2475 "A: tmFirstChar for %s got %02x expected %02x\n", font_name, tmA.tmFirstChar, expect_first_A);
2476 else
2477 ok(tmA.tmFirstChar == expect_first_A ||
2478 tmA.tmFirstChar == expect_first_A + 1 /* win9x */,
2479 "A: tmFirstChar for %s got %02x expected %02x\n", font_name, tmA.tmFirstChar, expect_first_A);
2480 ok(tmA.tmLastChar == expect_last_A ||
2481 tmA.tmLastChar == 0xff /* win9x */,
2482 "A: tmLastChar for %s got %02x expected %02x\n", font_name, tmA.tmLastChar, expect_last_A);
2483 ok(tmA.tmBreakChar == expect_break_A, "A: tmBreakChar for %s got %02x expected %02x\n",
2484 font_name, tmA.tmBreakChar, expect_break_A);
2485 ok(tmA.tmDefaultChar == expect_default_A, "A: tmDefaultChar for %s got %02x expected %02x\n",
2486 font_name, tmA.tmDefaultChar, expect_default_A);
2487
2488
2489 SetLastError(0xdeadbeef);
2490 ret = GetTextMetricsW(hdc, &tmW);
2491 ok(ret || GetLastError() == ERROR_CALL_NOT_IMPLEMENTED,
2492 "GetTextMetricsW error %u\n", GetLastError());
2493 if (ret)
2494 {
2495 /* Wine uses the os2 first char */
2496 if(cmap_first != os2_first_char && cmap_type != cmap_ms_symbol)
2497 todo_wine ok(tmW.tmFirstChar == expect_first_W, "W: tmFirstChar for %s got %02x expected %02x\n",
2498 font_name, tmW.tmFirstChar, expect_first_W);
2499 else
2500 ok(tmW.tmFirstChar == expect_first_W, "W: tmFirstChar for %s got %02x expected %02x\n",
2501 font_name, tmW.tmFirstChar, expect_first_W);
2502
2503 /* Wine uses the os2 last char */
2504 if(expect_last_W != os2_last_char && cmap_type != cmap_ms_symbol)
2505 todo_wine ok(tmW.tmLastChar == expect_last_W, "W: tmLastChar for %s got %02x expected %02x\n",
2506 font_name, tmW.tmLastChar, expect_last_W);
2507 else
2508 ok(tmW.tmLastChar == expect_last_W, "W: tmLastChar for %s got %02x expected %02x\n",
2509 font_name, tmW.tmLastChar, expect_last_W);
2510 ok(tmW.tmBreakChar == expect_break_W, "W: tmBreakChar for %s got %02x expected %02x\n",
2511 font_name, tmW.tmBreakChar, expect_break_W);
2512 ok(tmW.tmDefaultChar == expect_default_W, "W: tmDefaultChar for %s got %02x expected %02x\n",
2513 font_name, tmW.tmDefaultChar, expect_default_W);
2514
2515 /* Test the aspect ratio while we have tmW */
2516 ret = GetDeviceCaps(hdc, LOGPIXELSX);
2517 ok(tmW.tmDigitizedAspectX == ret, "W: tmDigitizedAspectX %u != %u\n",
2518 tmW.tmDigitizedAspectX, ret);
2519 ret = GetDeviceCaps(hdc, LOGPIXELSY);
2520 ok(tmW.tmDigitizedAspectX == ret, "W: tmDigitizedAspectY %u != %u\n",
2521 tmW.tmDigitizedAspectX, ret);
2522 }
2523 }
2524
2525 /* test FF_ values */
2526 switch(tt_os2.panose.bFamilyType)
2527 {
2528 case PAN_ANY:
2529 case PAN_NO_FIT:
2530 case PAN_FAMILY_TEXT_DISPLAY:
2531 case PAN_FAMILY_PICTORIAL:
2532 default:
2533 if((tmA.tmPitchAndFamily & 1) == 0 || /* fixed */
2534 tt_os2.panose.bProportion == PAN_PROP_MONOSPACED)
2535 {
2536 expect_ff(&tmA, &tt_os2, FF_MODERN, font_name);
2537 break;
2538 }
2539 switch(tt_os2.panose.bSerifStyle)
2540 {
2541 case PAN_ANY:
2542 case PAN_NO_FIT:
2543 default:
2544 expect_ff(&tmA, &tt_os2, FF_DONTCARE, font_name);
2545 break;
2546
2547 case PAN_SERIF_COVE:
2548 case PAN_SERIF_OBTUSE_COVE:
2549 case PAN_SERIF_SQUARE_COVE:
2550 case PAN_SERIF_OBTUSE_SQUARE_COVE:
2551 case PAN_SERIF_SQUARE:
2552 case PAN_SERIF_THIN:
2553 case PAN_SERIF_BONE:
2554 case PAN_SERIF_EXAGGERATED:
2555 case PAN_SERIF_TRIANGLE:
2556 expect_ff(&tmA, &tt_os2, FF_ROMAN, font_name);
2557 break;
2558
2559 case PAN_SERIF_NORMAL_SANS:
2560 case PAN_SERIF_OBTUSE_SANS:
2561 case PAN_SERIF_PERP_SANS:
2562 case PAN_SERIF_FLARED:
2563 case PAN_SERIF_ROUNDED: