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