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