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