gdi32: Ignore the lfHeight value when it exceeds the limit.
[wine/wine-gecko.git] / dlls / gdi32 / tests / font.c
blob876b312192edfa6c0f9c3948b43367dc736e8892
1 /*
2 * Unit test suite for fonts
4 * Copyright 2002 Mike McCormack
5 * Copyright 2004 Dmitry Timoshkov
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.
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.
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
22 #include <stdarg.h>
23 #include <assert.h>
25 #include "windef.h"
26 #include "winbase.h"
27 #include "wingdi.h"
28 #include "winuser.h"
29 #include "winnls.h"
31 #include "wine/test.h"
33 /* Do not allow more than 1 deviation here */
34 #define match_off_by_1(a, b) (abs((a) - (b)) <= 1)
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)
39 static LONG (WINAPI *pGdiGetCharDimensions)(HDC hdc, LPTEXTMETRICW lptm, LONG *height);
40 static DWORD (WINAPI *pGdiGetCodePage)(HDC hdc);
41 static BOOL (WINAPI *pGetCharABCWidthsI)(HDC hdc, UINT first, UINT count, LPWORD glyphs, LPABC abc);
42 static BOOL (WINAPI *pGetCharABCWidthsA)(HDC hdc, UINT first, UINT last, LPABC abc);
43 static BOOL (WINAPI *pGetCharABCWidthsW)(HDC hdc, UINT first, UINT last, LPABC abc);
44 static BOOL (WINAPI *pGetCharABCWidthsFloatW)(HDC hdc, UINT first, UINT last, LPABCFLOAT abc);
45 static DWORD (WINAPI *pGetFontUnicodeRanges)(HDC hdc, LPGLYPHSET lpgs);
46 static DWORD (WINAPI *pGetGlyphIndicesA)(HDC hdc, LPCSTR lpstr, INT count, LPWORD pgi, DWORD flags);
47 static DWORD (WINAPI *pGetGlyphIndicesW)(HDC hdc, LPCWSTR lpstr, INT count, LPWORD pgi, DWORD flags);
48 static BOOL (WINAPI *pGetTextExtentExPointI)(HDC hdc, const WORD *indices, INT count, INT max_ext,
49 LPINT nfit, LPINT dxs, LPSIZE size );
50 static BOOL (WINAPI *pGdiRealizationInfo)(HDC hdc, DWORD *);
51 static HFONT (WINAPI *pCreateFontIndirectExA)(const ENUMLOGFONTEXDV *);
52 static HANDLE (WINAPI *pAddFontMemResourceEx)(PVOID, DWORD, PVOID, DWORD *);
53 static BOOL (WINAPI *pRemoveFontMemResourceEx)(HANDLE);
54 static INT (WINAPI *pAddFontResourceExA)(LPCSTR, DWORD, PVOID);
55 static BOOL (WINAPI *pRemoveFontResourceExA)(LPCSTR, DWORD, PVOID);
57 static HMODULE hgdi32 = 0;
58 static const MAT2 mat = { {0,1}, {0,0}, {0,0}, {0,1} };
59 static WORD system_lang_id;
61 static void init(void)
63 hgdi32 = GetModuleHandleA("gdi32.dll");
65 pGdiGetCharDimensions = (void *)GetProcAddress(hgdi32, "GdiGetCharDimensions");
66 pGdiGetCodePage = (void *) GetProcAddress(hgdi32,"GdiGetCodePage");
67 pGetCharABCWidthsI = (void *)GetProcAddress(hgdi32, "GetCharABCWidthsI");
68 pGetCharABCWidthsA = (void *)GetProcAddress(hgdi32, "GetCharABCWidthsA");
69 pGetCharABCWidthsW = (void *)GetProcAddress(hgdi32, "GetCharABCWidthsW");
70 pGetCharABCWidthsFloatW = (void *)GetProcAddress(hgdi32, "GetCharABCWidthsFloatW");
71 pGetFontUnicodeRanges = (void *)GetProcAddress(hgdi32, "GetFontUnicodeRanges");
72 pGetGlyphIndicesA = (void *)GetProcAddress(hgdi32, "GetGlyphIndicesA");
73 pGetGlyphIndicesW = (void *)GetProcAddress(hgdi32, "GetGlyphIndicesW");
74 pGetTextExtentExPointI = (void *)GetProcAddress(hgdi32, "GetTextExtentExPointI");
75 pGdiRealizationInfo = (void *)GetProcAddress(hgdi32, "GdiRealizationInfo");
76 pCreateFontIndirectExA = (void *)GetProcAddress(hgdi32, "CreateFontIndirectExA");
77 pAddFontMemResourceEx = (void *)GetProcAddress(hgdi32, "AddFontMemResourceEx");
78 pRemoveFontMemResourceEx = (void *)GetProcAddress(hgdi32, "RemoveFontMemResourceEx");
79 pAddFontResourceExA = (void *)GetProcAddress(hgdi32, "AddFontResourceExA");
80 pRemoveFontResourceExA = (void *)GetProcAddress(hgdi32, "RemoveFontResourceExA");
82 system_lang_id = PRIMARYLANGID(GetSystemDefaultLangID());
85 static INT CALLBACK is_truetype_font_installed_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
87 if (type != TRUETYPE_FONTTYPE) return 1;
89 return 0;
92 static BOOL is_truetype_font_installed(const char *name)
94 HDC hdc = GetDC(0);
95 BOOL ret = FALSE;
97 if (!EnumFontFamiliesA(hdc, name, is_truetype_font_installed_proc, 0))
98 ret = TRUE;
100 ReleaseDC(0, hdc);
101 return ret;
104 static INT CALLBACK is_font_installed_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
106 return 0;
109 static BOOL is_font_installed(const char *name)
111 HDC hdc = GetDC(0);
112 BOOL ret = FALSE;
114 if(!EnumFontFamiliesA(hdc, name, is_font_installed_proc, 0))
115 ret = TRUE;
117 ReleaseDC(0, hdc);
118 return ret;
121 static void check_font(const char* test, const LOGFONTA* lf, HFONT hfont)
123 LOGFONTA getobj_lf;
124 int ret, minlen = 0;
126 if (!hfont)
127 return;
129 ret = GetObject(hfont, sizeof(getobj_lf), &getobj_lf);
130 /* NT4 tries to be clever and only returns the minimum length */
131 while (lf->lfFaceName[minlen] && minlen < LF_FACESIZE-1)
132 minlen++;
133 minlen += FIELD_OFFSET(LOGFONTA, lfFaceName) + 1;
134 ok(ret == sizeof(LOGFONTA) || ret == minlen, "%s: GetObject returned %d\n", test, ret);
135 ok(lf->lfHeight == getobj_lf.lfHeight ||
136 broken((SHORT)lf->lfHeight == getobj_lf.lfHeight), /* win9x */
137 "lfHeight: expect %08x got %08x\n", lf->lfHeight, getobj_lf.lfHeight);
138 ok(lf->lfWidth == getobj_lf.lfWidth ||
139 broken((SHORT)lf->lfWidth == getobj_lf.lfWidth), /* win9x */
140 "lfWidth: expect %08x got %08x\n", lf->lfWidth, getobj_lf.lfWidth);
141 ok(lf->lfEscapement == getobj_lf.lfEscapement ||
142 broken((SHORT)lf->lfEscapement == getobj_lf.lfEscapement), /* win9x */
143 "lfEscapement: expect %08x got %08x\n", lf->lfEscapement, getobj_lf.lfEscapement);
144 ok(lf->lfOrientation == getobj_lf.lfOrientation ||
145 broken((SHORT)lf->lfOrientation == getobj_lf.lfOrientation), /* win9x */
146 "lfOrientation: expect %08x got %08x\n", lf->lfOrientation, getobj_lf.lfOrientation);
147 ok(lf->lfWeight == getobj_lf.lfWeight ||
148 broken((SHORT)lf->lfWeight == getobj_lf.lfWeight), /* win9x */
149 "lfWeight: expect %08x got %08x\n", lf->lfWeight, getobj_lf.lfWeight);
150 ok(lf->lfItalic == getobj_lf.lfItalic, "lfItalic: expect %02x got %02x\n", lf->lfItalic, getobj_lf.lfItalic);
151 ok(lf->lfUnderline == getobj_lf.lfUnderline, "lfUnderline: expect %02x got %02x\n", lf->lfUnderline, getobj_lf.lfUnderline);
152 ok(lf->lfStrikeOut == getobj_lf.lfStrikeOut, "lfStrikeOut: expect %02x got %02x\n", lf->lfStrikeOut, getobj_lf.lfStrikeOut);
153 ok(lf->lfCharSet == getobj_lf.lfCharSet, "lfCharSet: expect %02x got %02x\n", lf->lfCharSet, getobj_lf.lfCharSet);
154 ok(lf->lfOutPrecision == getobj_lf.lfOutPrecision, "lfOutPrecision: expect %02x got %02x\n", lf->lfOutPrecision, getobj_lf.lfOutPrecision);
155 ok(lf->lfClipPrecision == getobj_lf.lfClipPrecision, "lfClipPrecision: expect %02x got %02x\n", lf->lfClipPrecision, getobj_lf.lfClipPrecision);
156 ok(lf->lfQuality == getobj_lf.lfQuality, "lfQuality: expect %02x got %02x\n", lf->lfQuality, getobj_lf.lfQuality);
157 ok(lf->lfPitchAndFamily == getobj_lf.lfPitchAndFamily, "lfPitchAndFamily: expect %02x got %02x\n", lf->lfPitchAndFamily, getobj_lf.lfPitchAndFamily);
158 ok(!lstrcmpA(lf->lfFaceName, getobj_lf.lfFaceName) ||
159 broken(!memcmp(lf->lfFaceName, getobj_lf.lfFaceName, LF_FACESIZE-1)), /* win9x doesn't ensure '\0' termination */
160 "%s: font names don't match: %s != %s\n", test, lf->lfFaceName, getobj_lf.lfFaceName);
163 static HFONT create_font(const char* test, const LOGFONTA* lf)
165 HFONT hfont = CreateFontIndirectA(lf);
166 ok(hfont != 0, "%s: CreateFontIndirect failed\n", test);
167 if (hfont)
168 check_font(test, lf, hfont);
169 return hfont;
172 static void test_logfont(void)
174 LOGFONTA lf;
175 HFONT hfont;
177 memset(&lf, 0, sizeof lf);
179 lf.lfCharSet = ANSI_CHARSET;
180 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
181 lf.lfWeight = FW_DONTCARE;
182 lf.lfHeight = 16;
183 lf.lfWidth = 16;
184 lf.lfQuality = DEFAULT_QUALITY;
186 lstrcpyA(lf.lfFaceName, "Arial");
187 hfont = create_font("Arial", &lf);
188 DeleteObject(hfont);
190 memset(&lf, 'A', sizeof(lf));
191 hfont = CreateFontIndirectA(&lf);
192 ok(hfont != 0, "CreateFontIndirectA with strange LOGFONT failed\n");
194 lf.lfFaceName[LF_FACESIZE - 1] = 0;
195 check_font("AAA...", &lf, hfont);
196 DeleteObject(hfont);
199 static INT CALLBACK font_enum_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
201 if (type & RASTER_FONTTYPE)
203 LOGFONT *lf = (LOGFONT *)lParam;
204 *lf = *elf;
205 return 0; /* stop enumeration */
208 return 1; /* continue enumeration */
211 static void compare_tm(const TEXTMETRICA *tm, const TEXTMETRICA *otm)
213 ok(tm->tmHeight == otm->tmHeight, "tmHeight %d != %d\n", tm->tmHeight, otm->tmHeight);
214 ok(tm->tmAscent == otm->tmAscent, "tmAscent %d != %d\n", tm->tmAscent, otm->tmAscent);
215 ok(tm->tmDescent == otm->tmDescent, "tmDescent %d != %d\n", tm->tmDescent, otm->tmDescent);
216 ok(tm->tmInternalLeading == otm->tmInternalLeading, "tmInternalLeading %d != %d\n", tm->tmInternalLeading, otm->tmInternalLeading);
217 ok(tm->tmExternalLeading == otm->tmExternalLeading, "tmExternalLeading %d != %d\n", tm->tmExternalLeading, otm->tmExternalLeading);
218 ok(tm->tmAveCharWidth == otm->tmAveCharWidth, "tmAveCharWidth %d != %d\n", tm->tmAveCharWidth, otm->tmAveCharWidth);
219 ok(tm->tmMaxCharWidth == otm->tmMaxCharWidth, "tmMaxCharWidth %d != %d\n", tm->tmMaxCharWidth, otm->tmMaxCharWidth);
220 ok(tm->tmWeight == otm->tmWeight, "tmWeight %d != %d\n", tm->tmWeight, otm->tmWeight);
221 ok(tm->tmOverhang == otm->tmOverhang, "tmOverhang %d != %d\n", tm->tmOverhang, otm->tmOverhang);
222 ok(tm->tmDigitizedAspectX == otm->tmDigitizedAspectX, "tmDigitizedAspectX %d != %d\n", tm->tmDigitizedAspectX, otm->tmDigitizedAspectX);
223 ok(tm->tmDigitizedAspectY == otm->tmDigitizedAspectY, "tmDigitizedAspectY %d != %d\n", tm->tmDigitizedAspectY, otm->tmDigitizedAspectY);
224 ok(tm->tmFirstChar == otm->tmFirstChar, "tmFirstChar %d != %d\n", tm->tmFirstChar, otm->tmFirstChar);
225 ok(tm->tmLastChar == otm->tmLastChar, "tmLastChar %d != %d\n", tm->tmLastChar, otm->tmLastChar);
226 ok(tm->tmDefaultChar == otm->tmDefaultChar, "tmDefaultChar %d != %d\n", tm->tmDefaultChar, otm->tmDefaultChar);
227 ok(tm->tmBreakChar == otm->tmBreakChar, "tmBreakChar %d != %d\n", tm->tmBreakChar, otm->tmBreakChar);
228 ok(tm->tmItalic == otm->tmItalic, "tmItalic %d != %d\n", tm->tmItalic, otm->tmItalic);
229 ok(tm->tmUnderlined == otm->tmUnderlined, "tmUnderlined %d != %d\n", tm->tmUnderlined, otm->tmUnderlined);
230 ok(tm->tmStruckOut == otm->tmStruckOut, "tmStruckOut %d != %d\n", tm->tmStruckOut, otm->tmStruckOut);
231 ok(tm->tmPitchAndFamily == otm->tmPitchAndFamily, "tmPitchAndFamily %d != %d\n", tm->tmPitchAndFamily, otm->tmPitchAndFamily);
232 ok(tm->tmCharSet == otm->tmCharSet, "tmCharSet %d != %d\n", tm->tmCharSet, otm->tmCharSet);
235 static void test_font_metrics(HDC hdc, HFONT hfont, LONG lfHeight,
236 LONG lfWidth, const char *test_str,
237 INT test_str_len, const TEXTMETRICA *tm_orig,
238 const SIZE *size_orig, INT width_of_A_orig,
239 INT scale_x, INT scale_y)
241 LOGFONTA lf;
242 OUTLINETEXTMETRIC otm;
243 TEXTMETRICA tm;
244 SIZE size;
245 INT width_of_A, cx, cy;
246 UINT ret;
248 if (!hfont)
249 return;
251 ok(GetCurrentObject(hdc, OBJ_FONT) == hfont, "hfont should be selected\n");
253 GetObjectA(hfont, sizeof(lf), &lf);
255 if (GetOutlineTextMetricsA(hdc, 0, NULL))
257 otm.otmSize = sizeof(otm) / 2;
258 ret = GetOutlineTextMetricsA(hdc, otm.otmSize, &otm);
259 ok(ret == sizeof(otm)/2 /* XP */ ||
260 ret == 1 /* Win9x */, "expected sizeof(otm)/2, got %u\n", ret);
262 memset(&otm, 0x1, sizeof(otm));
263 otm.otmSize = sizeof(otm);
264 ret = GetOutlineTextMetricsA(hdc, otm.otmSize, &otm);
265 ok(ret == sizeof(otm) /* XP */ ||
266 ret == 1 /* Win9x */, "expected sizeof(otm), got %u\n", ret);
268 memset(&tm, 0x2, sizeof(tm));
269 ret = GetTextMetricsA(hdc, &tm);
270 ok(ret, "GetTextMetricsA failed\n");
271 /* the structure size is aligned */
272 if (memcmp(&tm, &otm.otmTextMetrics, FIELD_OFFSET(TEXTMETRICA, tmCharSet) + 1))
274 ok(0, "tm != otm\n");
275 compare_tm(&tm, &otm.otmTextMetrics);
278 tm = otm.otmTextMetrics;
279 if (0) /* these metrics are scaled too, but with rounding errors */
281 ok(otm.otmAscent == tm.tmAscent, "ascent %d != %d\n", otm.otmAscent, tm.tmAscent);
282 ok(otm.otmDescent == -tm.tmDescent, "descent %d != %d\n", otm.otmDescent, -tm.tmDescent);
284 ok(otm.otmMacAscent == tm.tmAscent, "ascent %d != %d\n", otm.otmMacAscent, tm.tmAscent);
285 ok(otm.otmDescent < 0, "otm.otmDescent should be < 0\n");
286 ok(otm.otmMacDescent < 0, "otm.otmMacDescent should be < 0\n");
287 ok(tm.tmDescent > 0, "tm.tmDescent should be > 0\n");
288 ok(otm.otmMacDescent == -tm.tmDescent, "descent %d != %d\n", otm.otmMacDescent, -tm.tmDescent);
289 ok(otm.otmEMSquare == 2048, "expected 2048, got %d\n", otm.otmEMSquare);
291 else
293 ret = GetTextMetricsA(hdc, &tm);
294 ok(ret, "GetTextMetricsA failed\n");
297 cx = tm.tmAveCharWidth / tm_orig->tmAveCharWidth;
298 cy = tm.tmHeight / tm_orig->tmHeight;
299 ok(cx == scale_x && cy == scale_y, "height %d: expected scale_x %d, scale_y %d, got cx %d, cy %d\n",
300 lfHeight, scale_x, scale_y, cx, cy);
301 ok(tm.tmHeight == tm_orig->tmHeight * scale_y, "height %d != %d\n", tm.tmHeight, tm_orig->tmHeight * scale_y);
302 ok(tm.tmAscent == tm_orig->tmAscent * scale_y, "ascent %d != %d\n", tm.tmAscent, tm_orig->tmAscent * scale_y);
303 ok(tm.tmDescent == tm_orig->tmDescent * scale_y, "descent %d != %d\n", tm.tmDescent, tm_orig->tmDescent * scale_y);
304 ok(near_match(tm.tmAveCharWidth, tm_orig->tmAveCharWidth * scale_x), "ave width %d != %d\n", tm.tmAveCharWidth, tm_orig->tmAveCharWidth * scale_x);
305 ok(near_match(tm.tmMaxCharWidth, tm_orig->tmMaxCharWidth * scale_x), "max width %d != %d\n", tm.tmMaxCharWidth, tm_orig->tmMaxCharWidth * scale_x);
307 ok(lf.lfHeight == lfHeight, "lfHeight %d != %d\n", lf.lfHeight, lfHeight);
308 if (lf.lfHeight)
310 if (lf.lfWidth)
311 ok(lf.lfWidth == tm.tmAveCharWidth, "lfWidth %d != tm %d\n", lf.lfWidth, tm.tmAveCharWidth);
313 else
314 ok(lf.lfWidth == lfWidth, "lfWidth %d != %d\n", lf.lfWidth, lfWidth);
316 GetTextExtentPoint32A(hdc, test_str, test_str_len, &size);
318 ok(near_match(size.cx, size_orig->cx * scale_x), "cx %d != %d\n", size.cx, size_orig->cx * scale_x);
319 ok(size.cy == size_orig->cy * scale_y, "cy %d != %d\n", size.cy, size_orig->cy * scale_y);
321 GetCharWidthA(hdc, 'A', 'A', &width_of_A);
323 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);
326 /* Test how GDI scales bitmap font metrics */
327 static void test_bitmap_font(void)
329 static const char test_str[11] = "Test String";
330 HDC hdc;
331 LOGFONTA bitmap_lf;
332 HFONT hfont, old_hfont;
333 TEXTMETRICA tm_orig;
334 SIZE size_orig;
335 INT ret, i, width_orig, height_orig, scale, lfWidth;
337 hdc = CreateCompatibleDC(0);
339 /* "System" has only 1 pixel size defined, otherwise the test breaks */
340 ret = EnumFontFamiliesA(hdc, "System", font_enum_proc, (LPARAM)&bitmap_lf);
341 if (ret)
343 ReleaseDC(0, hdc);
344 trace("no bitmap fonts were found, skipping the test\n");
345 return;
348 trace("found bitmap font %s, height %d\n", bitmap_lf.lfFaceName, bitmap_lf.lfHeight);
350 height_orig = bitmap_lf.lfHeight;
351 lfWidth = bitmap_lf.lfWidth;
353 hfont = create_font("bitmap", &bitmap_lf);
354 old_hfont = SelectObject(hdc, hfont);
355 ok(GetTextMetricsA(hdc, &tm_orig), "GetTextMetricsA failed\n");
356 ok(GetTextExtentPoint32A(hdc, test_str, sizeof(test_str), &size_orig), "GetTextExtentPoint32A failed\n");
357 ok(GetCharWidthA(hdc, 'A', 'A', &width_orig), "GetCharWidthA failed\n");
358 SelectObject(hdc, old_hfont);
359 DeleteObject(hfont);
361 bitmap_lf.lfHeight = 0;
362 bitmap_lf.lfWidth = 4;
363 hfont = create_font("bitmap", &bitmap_lf);
364 old_hfont = SelectObject(hdc, hfont);
365 test_font_metrics(hdc, hfont, 0, 4, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 1, 1);
366 SelectObject(hdc, old_hfont);
367 DeleteObject(hfont);
369 bitmap_lf.lfHeight = height_orig;
370 bitmap_lf.lfWidth = lfWidth;
372 /* test fractional scaling */
373 for (i = 1; i <= height_orig * 6; i++)
375 INT nearest_height;
377 bitmap_lf.lfHeight = i;
378 hfont = create_font("fractional", &bitmap_lf);
379 scale = (i + height_orig - 1) / height_orig;
380 nearest_height = scale * height_orig;
381 /* Only jump to the next height if the difference <= 25% original height */
382 if (scale > 2 && nearest_height - i > height_orig / 4) scale--;
383 /* The jump between unscaled and doubled is delayed by 1 in winnt+ but not in win9x,
384 so we'll not test this particular height. */
385 else if(scale == 2 && nearest_height - i == (height_orig / 4)) continue;
386 else if(scale == 2 && nearest_height - i > (height_orig / 4 - 1)) scale--;
387 old_hfont = SelectObject(hdc, hfont);
388 test_font_metrics(hdc, hfont, bitmap_lf.lfHeight, 0, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 1, scale);
389 SelectObject(hdc, old_hfont);
390 DeleteObject(hfont);
393 /* test integer scaling 3x2 */
394 bitmap_lf.lfHeight = height_orig * 2;
395 bitmap_lf.lfWidth *= 3;
396 hfont = create_font("3x2", &bitmap_lf);
397 old_hfont = SelectObject(hdc, hfont);
398 test_font_metrics(hdc, hfont, bitmap_lf.lfHeight, 0, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 3, 2);
399 SelectObject(hdc, old_hfont);
400 DeleteObject(hfont);
402 /* test integer scaling 3x3 */
403 bitmap_lf.lfHeight = height_orig * 3;
404 bitmap_lf.lfWidth = 0;
405 hfont = create_font("3x3", &bitmap_lf);
406 old_hfont = SelectObject(hdc, hfont);
407 test_font_metrics(hdc, hfont, bitmap_lf.lfHeight, 0, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 3, 3);
408 SelectObject(hdc, old_hfont);
409 DeleteObject(hfont);
411 DeleteDC(hdc);
414 /* Test how GDI scales outline font metrics */
415 static void test_outline_font(void)
417 static const char test_str[11] = "Test String";
418 HDC hdc, hdc_2;
419 LOGFONTA lf;
420 HFONT hfont, old_hfont, old_hfont_2;
421 OUTLINETEXTMETRICA otm;
422 SIZE size_orig;
423 INT width_orig, height_orig, lfWidth;
424 XFORM xform;
425 GLYPHMETRICS gm;
426 MAT2 mat2 = { {0x8000,0}, {0,0}, {0,0}, {0x8000,0} };
427 POINT pt;
428 INT ret;
430 if (!is_truetype_font_installed("Arial"))
432 skip("Arial is not installed\n");
433 return;
436 hdc = CreateCompatibleDC(0);
438 memset(&lf, 0, sizeof(lf));
439 strcpy(lf.lfFaceName, "Arial");
440 lf.lfHeight = 72;
441 hfont = create_font("outline", &lf);
442 old_hfont = SelectObject(hdc, hfont);
443 otm.otmSize = sizeof(otm);
444 ok(GetOutlineTextMetricsA(hdc, sizeof(otm), &otm), "GetTextMetricsA failed\n");
445 ok(GetTextExtentPoint32A(hdc, test_str, sizeof(test_str), &size_orig), "GetTextExtentPoint32A failed\n");
446 ok(GetCharWidthA(hdc, 'A', 'A', &width_orig), "GetCharWidthA failed\n");
448 test_font_metrics(hdc, hfont, lf.lfHeight, otm.otmTextMetrics.tmAveCharWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
449 SelectObject(hdc, old_hfont);
450 DeleteObject(hfont);
452 /* font of otmEMSquare height helps to avoid a lot of rounding errors */
453 lf.lfHeight = otm.otmEMSquare;
454 lf.lfHeight = -lf.lfHeight;
455 hfont = create_font("outline", &lf);
456 old_hfont = SelectObject(hdc, hfont);
457 otm.otmSize = sizeof(otm);
458 ok(GetOutlineTextMetricsA(hdc, sizeof(otm), &otm), "GetTextMetricsA failed\n");
459 ok(GetTextExtentPoint32A(hdc, test_str, sizeof(test_str), &size_orig), "GetTextExtentPoint32A failed\n");
460 ok(GetCharWidthA(hdc, 'A', 'A', &width_orig), "GetCharWidthA failed\n");
461 SelectObject(hdc, old_hfont);
462 DeleteObject(hfont);
464 height_orig = otm.otmTextMetrics.tmHeight;
465 lfWidth = otm.otmTextMetrics.tmAveCharWidth;
467 /* test integer scaling 3x2 */
468 lf.lfHeight = height_orig * 2;
469 lf.lfWidth = lfWidth * 3;
470 hfont = create_font("3x2", &lf);
471 old_hfont = SelectObject(hdc, hfont);
472 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 3, 2);
473 SelectObject(hdc, old_hfont);
474 DeleteObject(hfont);
476 /* test integer scaling 3x3 */
477 lf.lfHeight = height_orig * 3;
478 lf.lfWidth = lfWidth * 3;
479 hfont = create_font("3x3", &lf);
480 old_hfont = SelectObject(hdc, hfont);
481 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 3, 3);
482 SelectObject(hdc, old_hfont);
483 DeleteObject(hfont);
485 /* test integer scaling 1x1 */
486 lf.lfHeight = height_orig * 1;
487 lf.lfWidth = lfWidth * 1;
488 hfont = create_font("1x1", &lf);
489 old_hfont = SelectObject(hdc, hfont);
490 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
491 SelectObject(hdc, old_hfont);
492 DeleteObject(hfont);
494 /* test integer scaling 1x1 */
495 lf.lfHeight = height_orig;
496 lf.lfWidth = 0;
497 hfont = create_font("1x1", &lf);
498 old_hfont = SelectObject(hdc, hfont);
499 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
501 /* with an identity matrix */
502 memset(&gm, 0, sizeof(gm));
503 SetLastError(0xdeadbeef);
504 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
505 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
506 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
507 ok(gm.gmCellIncX == width_orig, "incX %d != %d\n", gm.gmCellIncX, width_orig);
508 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
509 /* with a custom matrix */
510 memset(&gm, 0, sizeof(gm));
511 SetLastError(0xdeadbeef);
512 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat2);
513 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
514 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
515 ok(gm.gmCellIncX == width_orig/2, "incX %d != %d\n", gm.gmCellIncX, width_orig/2);
516 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
518 /* Test that changing the DC transformation affects only the font
519 * selected on this DC and doesn't affect the same font selected on
520 * another DC.
522 hdc_2 = CreateCompatibleDC(0);
523 old_hfont_2 = SelectObject(hdc_2, hfont);
524 test_font_metrics(hdc_2, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
526 SetMapMode(hdc, MM_ANISOTROPIC);
528 /* font metrics on another DC should be unchanged */
529 test_font_metrics(hdc_2, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
531 /* test restrictions of compatibility mode GM_COMPATIBLE */
532 /* part 1: rescaling only X should not change font scaling on screen.
533 So compressing the X axis by 2 is not done, and this
534 appears as X scaling of 2 that no one requested. */
535 SetWindowExtEx(hdc, 100, 100, NULL);
536 SetViewportExtEx(hdc, 50, 100, NULL);
537 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 2, 1);
538 /* font metrics on another DC should be unchanged */
539 test_font_metrics(hdc_2, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
541 /* part 2: rescaling only Y should change font scaling.
542 As also X is scaled by a factor of 2, but this is not
543 requested by the DC transformation, we get a scaling factor
544 of 2 in the X coordinate. */
545 SetViewportExtEx(hdc, 100, 200, NULL);
546 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 2, 1);
547 /* font metrics on another DC should be unchanged */
548 test_font_metrics(hdc_2, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
550 /* restore scaling */
551 SetMapMode(hdc, MM_TEXT);
553 /* font metrics on another DC should be unchanged */
554 test_font_metrics(hdc_2, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
556 SelectObject(hdc_2, old_hfont_2);
557 DeleteDC(hdc_2);
559 if (!SetGraphicsMode(hdc, GM_ADVANCED))
561 SelectObject(hdc, old_hfont);
562 DeleteObject(hfont);
563 DeleteDC(hdc);
564 skip("GM_ADVANCED is not supported on this platform\n");
565 return;
568 xform.eM11 = 20.0f;
569 xform.eM12 = 0.0f;
570 xform.eM21 = 0.0f;
571 xform.eM22 = 20.0f;
572 xform.eDx = 0.0f;
573 xform.eDy = 0.0f;
575 SetLastError(0xdeadbeef);
576 ret = SetWorldTransform(hdc, &xform);
577 ok(ret, "SetWorldTransform error %u\n", GetLastError());
579 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
581 /* with an identity matrix */
582 memset(&gm, 0, sizeof(gm));
583 SetLastError(0xdeadbeef);
584 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
585 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
586 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
587 pt.x = width_orig; pt.y = 0;
588 LPtoDP(hdc, &pt, 1);
589 ok(gm.gmCellIncX == pt.x, "incX %d != %d\n", gm.gmCellIncX, pt.x);
590 ok(gm.gmCellIncX == 20 * width_orig, "incX %d != %d\n", gm.gmCellIncX, 20 * width_orig);
591 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
592 /* with a custom matrix */
593 memset(&gm, 0, sizeof(gm));
594 SetLastError(0xdeadbeef);
595 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat2);
596 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
597 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
598 pt.x = width_orig; pt.y = 0;
599 LPtoDP(hdc, &pt, 1);
600 ok(gm.gmCellIncX == pt.x/2, "incX %d != %d\n", gm.gmCellIncX, pt.x/2);
601 ok(near_match(gm.gmCellIncX, 10 * width_orig), "incX %d != %d\n", gm.gmCellIncX, 10 * width_orig);
602 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
604 SetLastError(0xdeadbeef);
605 ret = SetMapMode(hdc, MM_LOMETRIC);
606 ok(ret == MM_TEXT, "expected MM_TEXT, got %d, error %u\n", ret, GetLastError());
608 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
610 /* with an identity matrix */
611 memset(&gm, 0, sizeof(gm));
612 SetLastError(0xdeadbeef);
613 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
614 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
615 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
616 pt.x = width_orig; pt.y = 0;
617 LPtoDP(hdc, &pt, 1);
618 ok(near_match(gm.gmCellIncX, pt.x), "incX %d != %d\n", gm.gmCellIncX, pt.x);
619 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
620 /* with a custom matrix */
621 memset(&gm, 0, sizeof(gm));
622 SetLastError(0xdeadbeef);
623 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat2);
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(near_match(gm.gmCellIncX, (pt.x + 1)/2), "incX %d != %d\n", gm.gmCellIncX, (pt.x + 1)/2);
629 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
631 SetLastError(0xdeadbeef);
632 ret = SetMapMode(hdc, MM_TEXT);
633 ok(ret == MM_LOMETRIC, "expected MM_LOMETRIC, got %d, error %u\n", ret, GetLastError());
635 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
637 /* with an identity matrix */
638 memset(&gm, 0, sizeof(gm));
639 SetLastError(0xdeadbeef);
640 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
641 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
642 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
643 pt.x = width_orig; pt.y = 0;
644 LPtoDP(hdc, &pt, 1);
645 ok(gm.gmCellIncX == pt.x, "incX %d != %d\n", gm.gmCellIncX, pt.x);
646 ok(gm.gmCellIncX == 20 * width_orig, "incX %d != %d\n", gm.gmCellIncX, 20 * width_orig);
647 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
648 /* with a custom matrix */
649 memset(&gm, 0, sizeof(gm));
650 SetLastError(0xdeadbeef);
651 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat2);
652 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
653 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
654 pt.x = width_orig; pt.y = 0;
655 LPtoDP(hdc, &pt, 1);
656 ok(gm.gmCellIncX == pt.x/2, "incX %d != %d\n", gm.gmCellIncX, pt.x/2);
657 ok(gm.gmCellIncX == 10 * width_orig, "incX %d != %d\n", gm.gmCellIncX, 10 * width_orig);
658 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
660 SelectObject(hdc, old_hfont);
661 DeleteObject(hfont);
662 DeleteDC(hdc);
665 static INT CALLBACK find_font_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
667 LOGFONT *lf = (LOGFONT *)lParam;
669 if (elf->lfHeight == lf->lfHeight && !strcmp(elf->lfFaceName, lf->lfFaceName))
671 *lf = *elf;
672 return 0; /* stop enumeration */
674 return 1; /* continue enumeration */
677 static BOOL is_CJK(void)
679 return (system_lang_id == LANG_CHINESE || system_lang_id == LANG_JAPANESE || system_lang_id == LANG_KOREAN);
682 #define FH_SCALE 0x80000000
683 static void test_bitmap_font_metrics(void)
685 static const struct font_data
687 const char face_name[LF_FACESIZE];
688 int weight, height, ascent, descent, int_leading, ext_leading;
689 int ave_char_width, max_char_width, dpi;
690 BYTE first_char, last_char, def_char, break_char;
691 DWORD ansi_bitfield;
692 WORD skip_lang_id;
693 int scaled_height;
694 } fd[] =
696 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 6, 11, 2, 2, 0, 5, 11, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2, LANG_ARABIC, 13 },
697 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 6, 11, 2, 2, 0, 5, 11, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC, 0, 13 },
698 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 8, 11, 2, 2, 0, 5, 11, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2, LANG_ARABIC, 13 },
699 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 8, 11, 2, 2, 0, 5, 11, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC, 0, 13 },
700 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 10, 11, 2, 2, 0, 5, 11, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2, LANG_ARABIC, 13 },
701 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 10, 11, 2, 2, 0, 5, 11, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC, 0, 13 },
702 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 14, 11, 2, 2, 0, 5, 11, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2, LANG_ARABIC, 13 },
703 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 14, 11, 2, 2, 0, 5, 11, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC, 0, 13 },
704 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 18, 13, 3, 3, 0, 7, 14, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2, LANG_ARABIC, 16 },
705 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 18, 13, 3, 3, 0, 7, 14, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC, 0, 16 },
707 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 6, 13, 3, 3, 0, 7, 14, 120, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2, 0, 16 },
708 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 6, 13, 3, 3, 0, 7, 14, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC, 0, 16 },
709 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 8, 13, 3, 3, 0, 7, 14, 120, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2, 0, 16 },
710 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 8, 13, 3, 3, 0, 7, 14, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC, 0, 16 },
711 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 10, 13, 3, 3, 0, 7, 14, 120, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2, 0, 16 },
712 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 10, 13, 3, 3, 0, 7, 14, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC, 0, 16 },
713 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 14, 13, 3, 3, 0, 7, 14, 120, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2, 0, 16 },
714 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 14, 13, 3, 3, 0, 7, 14, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC, 0, 16 },
715 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 18, 13, 3, 3, 0, 7, 14, 120, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2, 0, 16 },
716 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 18, 13, 3, 3, 0, 7, 14, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC, 0, 16 },
718 { "MS Sans Serif", FW_NORMAL, 13, 11, 2, 2, 0, 5, 11, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2 },
719 { "MS Sans Serif", FW_NORMAL, 13, 11, 2, 2, 0, 5, 11, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
720 { "MS Sans Serif", FW_NORMAL, 16, 13, 3, 3, 0, 7, 14, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2 },
721 { "MS Sans Serif", FW_NORMAL, 16, 13, 3, 3, 0, 7, 14, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
722 { "MS Sans Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 16, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 },
723 { "MS Sans Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 18, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN2 },
724 { "MS Sans Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 16, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
725 { "MS Sans Serif", FW_NORMAL, 24, 19, 5, 6, 0, 9, 19, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 },
726 { "MS Sans Serif", FW_NORMAL, 24, 19, 5, 6, 0, 9, 24, 96, 0x20, 0xff, 0x81, 0x40, FS_LATIN2 },
727 { "MS Sans Serif", FW_NORMAL, 24, 19, 5, 6, 0, 9, 20, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
728 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 5, 0, 12, 24, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 },
729 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 6, 0, 12, 24, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN2 },
730 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 5, 0, 12, 25, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
731 { "MS Sans Serif", FW_NORMAL, 37, 29, 8, 5, 0, 16, 32, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 },
732 { "MS Sans Serif", FW_NORMAL, 37, 29, 8, 5, 0, 16, 32, 96, 0x20, 0xff, 0x81, 0x40, FS_LATIN2 },
733 { "MS Sans Serif", FW_NORMAL, 37, 29, 8, 5, 0, 16, 32, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
735 { "MS Sans Serif", FW_NORMAL, 16, 13, 3, 3, 0, 7, 14, 120, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2 },
736 { "MS Sans Serif", FW_NORMAL, 16, 13, 3, 3, 0, 7, 14, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
737 { "MS Sans Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 18, 120, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2 },
738 { "MS Sans Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 17, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
739 { "MS Sans Serif", FW_NORMAL, 25, 20, 5, 5, 0, 10, 21, 120, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2 },
740 { "MS Sans Serif", FW_NORMAL, 25, 20, 5, 5, 0, 10, 21, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
741 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 6, 0, 12, 24, 120, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2 },
742 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 5, 0, 12, 24, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
743 { "MS Sans Serif", FW_NORMAL, 36, 29, 7, 6, 0, 15, 30, 120, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2 },
744 { "MS Sans Serif", FW_NORMAL, 36, 29, 7, 6, 0, 15, 30, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
745 { "MS Sans Serif", FW_NORMAL, 46, 37, 9, 6, 0, 20, 40, 120, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2 },
746 { "MS Sans Serif", FW_NORMAL, 46, 37, 9, 6, 0, 20, 40, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
748 { "MS Serif", FW_NORMAL, 10, 8, 2, 2, 0, 4, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 },
749 { "MS Serif", FW_NORMAL, 10, 8, 2, 2, 0, 5, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
750 { "MS Serif", FW_NORMAL, 11, 9, 2, 2, 0, 5, 9, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
751 { "MS Serif", FW_NORMAL, 13, 11, 2, 2, 0, 5, 11, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 },
752 { "MS Serif", FW_NORMAL, 13, 11, 2, 2, 0, 5, 12, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
753 { "MS Serif", FW_NORMAL, 16, 13, 3, 3, 0, 6, 14, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 },
754 { "MS Serif", FW_NORMAL, 16, 13, 3, 3, 0, 6, 16, 96, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
755 { "MS Serif", FW_NORMAL, 19, 15, 4, 3, 0, 8, 18, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 },
756 { "MS Serif", FW_NORMAL, 19, 15, 4, 3, 0, 8, 19, 96, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
757 { "MS Serif", FW_NORMAL, 21, 16, 5, 3, 0, 9, 17, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 },
758 { "MS Serif", FW_NORMAL, 21, 16, 5, 3, 0, 9, 22, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 },
759 { "MS Serif", FW_NORMAL, 21, 16, 5, 3, 0, 9, 23, 96, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
760 { "MS Serif", FW_NORMAL, 27, 21, 6, 3, 0, 12, 23, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 },
761 { "MS Serif", FW_NORMAL, 27, 21, 6, 3, 0, 12, 26, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 },
762 { "MS Serif", FW_NORMAL, 27, 21, 6, 3, 0, 12, 27, 96, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
763 { "MS Serif", FW_NORMAL, 35, 27, 8, 3, 0, 16, 33, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 },
764 { "MS Serif", FW_NORMAL, 35, 27, 8, 3, 0, 16, 34, 96, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
766 { "MS Serif", FW_NORMAL, 16, 13, 3, 3, 0, 6, 14, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_CYRILLIC },
767 { "MS Serif", FW_NORMAL, 16, 13, 3, 3, 0, 6, 13, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 },
768 { "MS Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 18, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_CYRILLIC },
769 { "MS Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 15, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 },
770 { "MS Serif", FW_NORMAL, 23, 18, 5, 3, 0, 10, 21, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_CYRILLIC },
771 { "MS Serif", FW_NORMAL, 23, 18, 5, 3, 0, 10, 19, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 },
772 { "MS Serif", FW_NORMAL, 27, 21, 6, 4, 0, 12, 23, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 },
773 { "MS Serif", FW_MEDIUM, 27, 22, 5, 2, 0, 12, 30, 120, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
774 { "MS Serif", FW_NORMAL, 33, 26, 7, 3, 0, 14, 30, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 },
775 { "MS Serif", FW_MEDIUM, 32, 25, 7, 2, 0, 14, 32, 120, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
776 { "MS Serif", FW_NORMAL, 43, 34, 9, 3, 0, 19, 39, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
778 { "Courier", FW_NORMAL, 13, 11, 2, 0, 0, 8, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
779 { "Courier", FW_NORMAL, 16, 13, 3, 0, 0, 9, 9, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
780 { "Courier", FW_NORMAL, 20, 16, 4, 0, 0, 12, 12, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
782 { "Courier", FW_NORMAL, 16, 13, 3, 0, 0, 9, 9, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
783 { "Courier", FW_NORMAL, 20, 16, 4, 0, 0, 12, 12, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
784 { "Courier", FW_NORMAL, 25, 20, 5, 0, 0, 15, 15, 120, 0x20, 0xff, 0x40, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
786 { "System", FW_BOLD, 16, 13, 3, 3, 0, 7, 14, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 },
787 { "System", FW_BOLD, 16, 13, 3, 3, 0, 7, 15, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
788 { "System", FW_NORMAL, 18, 16, 2, 0, 2, 8, 16, 96, 0x20, 0xff, 0x80, 0x20, FS_JISJAPAN },
790 { "System", FW_BOLD, 20, 16, 4, 4, 0, 9, 14, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 },
791 { "System", FW_BOLD, 20, 16, 4, 4, 0, 9, 17, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
793 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 1, 2, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 },
794 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 1, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
795 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 2, 4, 96, 0x20, 0xff, 0x80, 0x20, FS_JISJAPAN },
796 { "Small Fonts", FW_NORMAL, 5, 4, 1, 1, 0, 3, 4, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1, LANG_ARABIC },
797 { "Small Fonts", FW_NORMAL, 5, 4, 1, 1, 0, 2, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
798 { "Small Fonts", FW_NORMAL, 5, 4, 1, 0, 0, 3, 6, 96, 0x20, 0xff, 0x80, 0x20, FS_JISJAPAN },
799 { "Small Fonts", FW_NORMAL, 6, 5, 1, 1, 0, 3, 13, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1, LANG_ARABIC },
800 { "Small Fonts", FW_NORMAL, 6, 5, 1, 1, 0, 3, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
801 { "Small Fonts", FW_NORMAL, 6, 5, 1, 1, 0, 3, 8, 96, 0x00, 0xff, 0x60, 0x00, FS_ARABIC },
802 { "Small Fonts", FW_NORMAL, 6, 5, 1, 0, 0, 4, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_JISJAPAN },
803 { "Small Fonts", FW_NORMAL, 8, 7, 1, 1, 0, 4, 7, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1, LANG_ARABIC },
804 { "Small Fonts", FW_NORMAL, 8, 7, 1, 1, 0, 4, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
805 { "Small Fonts", FW_NORMAL, 8, 7, 1, 1, 0, 4, 8, 96, 0x00, 0xff, 0x60, 0x00, FS_ARABIC },
806 { "Small Fonts", FW_NORMAL, 8, 7, 1, 0, 0, 5, 10, 96, 0x20, 0xff, 0x80, 0x20, FS_JISJAPAN },
807 { "Small Fonts", FW_NORMAL, 10, 8, 2, 2, 0, 4, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2, LANG_ARABIC },
808 { "Small Fonts", FW_NORMAL, 10, 8, 2, 2, 0, 5, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
809 { "Small Fonts", FW_NORMAL, 10, 8, 2, 2, 0, 4, 9, 96, 0x00, 0xff, 0x60, 0x00, FS_ARABIC },
810 { "Small Fonts", FW_NORMAL, 10, 8, 2, 0, 0, 6, 12, 96, 0x20, 0xff, 0x80, 0x20, FS_JISJAPAN },
811 { "Small Fonts", FW_NORMAL, 11, 9, 2, 2, 0, 5, 9, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC, LANG_ARABIC },
812 { "Small Fonts", FW_NORMAL, 11, 9, 2, 2, 0, 4, 10, 96, 0x00, 0xff, 0x60, 0x00, FS_ARABIC },
813 { "Small Fonts", FW_NORMAL, 11, 9, 2, 0, 0, 7, 14, 96, 0x20, 0xff, 0x80, 0x20, FS_JISJAPAN },
815 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 1, 2, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_JISJAPAN },
816 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 1, 8, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
817 { "Small Fonts", FW_NORMAL, 6, 5, 1, 1, 0, 3, 5, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_JISJAPAN },
818 { "Small Fonts", FW_NORMAL, 6, 5, 1, 1, 0, 3, 8, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
819 { "Small Fonts", FW_NORMAL, 8, 7, 1, 1, 0, 4, 7, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_JISJAPAN },
820 { "Small Fonts", FW_NORMAL, 8, 7, 1, 1, 0, 4, 8, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
821 { "Small Fonts", FW_NORMAL, 10, 8, 2, 2, 0, 5, 9, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_JISJAPAN },
822 { "Small Fonts", FW_NORMAL, 10, 8, 2, 2, 0, 5, 8, 120, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
823 { "Small Fonts", FW_NORMAL, 12, 10, 2, 2, 0, 5, 10, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_JISJAPAN },
824 { "Small Fonts", FW_NORMAL, 12, 10, 2, 2, 0, 6, 10, 120, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
825 { "Small Fonts", FW_NORMAL, 13, 11, 2, 2, 0, 6, 12, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_JISJAPAN },
826 { "Small Fonts", FW_NORMAL, 13, 11, 2, 2, 0, 6, 11, 120, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
828 { "Fixedsys", FW_NORMAL, 15, 12, 3, 3, 0, 8, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 },
829 { "Fixedsys", FW_NORMAL, 16, 12, 4, 3, 0, 8, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
830 { "FixedSys", FW_NORMAL, 18, 16, 2, 0, 0, 8, 16, 96, 0x20, 0xff, 0xa0, 0x20, FS_JISJAPAN },
832 { "Fixedsys", FW_NORMAL, 20, 16, 4, 2, 0, 10, 10, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC }
834 /* FIXME: add "Terminal" */
836 static const int font_log_pixels[] = { 96, 120 };
837 HDC hdc;
838 LOGFONT lf;
839 HFONT hfont, old_hfont;
840 TEXTMETRIC tm;
841 INT ret, i, expected_cs, screen_log_pixels, diff, font_res;
842 char face_name[LF_FACESIZE];
843 CHARSETINFO csi;
845 trace("system language id %04x\n", system_lang_id);
847 expected_cs = GetACP();
848 if (!TranslateCharsetInfo(ULongToPtr(expected_cs), &csi, TCI_SRCCODEPAGE))
850 skip("TranslateCharsetInfo failed for code page %d\n", expected_cs);
851 return;
853 expected_cs = csi.ciCharset;
854 trace("ACP %d -> charset %d\n", GetACP(), expected_cs);
856 hdc = CreateCompatibleDC(0);
857 assert(hdc);
859 trace("logpixelsX %d, logpixelsY %d\n", GetDeviceCaps(hdc, LOGPIXELSX),
860 GetDeviceCaps(hdc, LOGPIXELSY));
862 screen_log_pixels = GetDeviceCaps(hdc, LOGPIXELSY);
863 diff = 32768;
864 font_res = 0;
865 for (i = 0; i < sizeof(font_log_pixels)/sizeof(font_log_pixels[0]); i++)
867 int new_diff = abs(font_log_pixels[i] - screen_log_pixels);
868 if (new_diff < diff)
870 diff = new_diff;
871 font_res = font_log_pixels[i];
874 trace("best font resolution is %d\n", font_res);
876 for (i = 0; i < sizeof(fd)/sizeof(fd[0]); i++)
878 int bit, height;
880 memset(&lf, 0, sizeof(lf));
882 height = fd[i].height & ~FH_SCALE;
883 lf.lfHeight = height;
884 strcpy(lf.lfFaceName, fd[i].face_name);
886 for(bit = 0; bit < 32; bit++)
888 GLYPHMETRICS gm;
889 DWORD fs[2];
890 BOOL bRet;
892 fs[0] = 1L << bit;
893 fs[1] = 0;
894 if((fd[i].ansi_bitfield & fs[0]) == 0) continue;
895 if(!TranslateCharsetInfo( fs, &csi, TCI_SRCFONTSIG )) continue;
897 lf.lfCharSet = csi.ciCharset;
898 trace("looking for %s height %d charset %d\n", lf.lfFaceName, lf.lfHeight, lf.lfCharSet);
899 ret = EnumFontFamiliesEx(hdc, &lf, find_font_proc, (LPARAM)&lf, 0);
900 if (fd[i].height & FH_SCALE)
901 ok(ret, "scaled font height %d should not be enumerated\n", height);
902 else
904 if (font_res == fd[i].dpi && lf.lfCharSet == expected_cs)
906 if (ret) /* FIXME: Remove once Wine is fixed */
907 todo_wine ok(!ret, "%s height %d charset %d dpi %d should be enumerated\n", lf.lfFaceName, lf.lfHeight, lf.lfCharSet, fd[i].dpi);
908 else
909 ok(!ret, "%s height %d charset %d dpi %d should be enumerated\n", lf.lfFaceName, lf.lfHeight, lf.lfCharSet, fd[i].dpi);
912 if (ret && !(fd[i].height & FH_SCALE))
913 continue;
915 hfont = create_font(lf.lfFaceName, &lf);
916 old_hfont = SelectObject(hdc, hfont);
918 SetLastError(0xdeadbeef);
919 ret = GetTextFace(hdc, sizeof(face_name), face_name);
920 ok(ret, "GetTextFace error %u\n", GetLastError());
922 if (lstrcmp(face_name, fd[i].face_name) != 0)
924 ok(ret != ANSI_CHARSET, "font charset should not be ANSI_CHARSET\n");
925 ok(ret != expected_cs, "font charset %d should not be %d\n", ret, expected_cs);
926 trace("Skipping replacement %s height %d charset %d\n", face_name, tm.tmHeight, tm.tmCharSet);
927 SelectObject(hdc, old_hfont);
928 DeleteObject(hfont);
929 continue;
932 memset(&gm, 0, sizeof(gm));
933 SetLastError(0xdeadbeef);
934 ret = GetGlyphOutline(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
935 todo_wine {
936 ok(ret == GDI_ERROR, "GetGlyphOutline should fail for a bitmap font\n");
937 ok(GetLastError() == ERROR_CAN_NOT_COMPLETE, "expected ERROR_CAN_NOT_COMPLETE, got %u\n", GetLastError());
940 bRet = GetTextMetrics(hdc, &tm);
941 ok(bRet, "GetTextMetrics error %d\n", GetLastError());
943 SetLastError(0xdeadbeef);
944 ret = GetTextCharset(hdc);
945 if (is_CJK() && lf.lfCharSet == ANSI_CHARSET)
946 ok(ret == ANSI_CHARSET, "got charset %d, expected ANSI_CHARSETd\n", ret);
947 else
948 ok(ret == expected_cs, "got charset %d, expected %d\n", ret, expected_cs);
950 trace("created %s, height %d charset %x dpi %d\n", face_name, tm.tmHeight, tm.tmCharSet, tm.tmDigitizedAspectX);
951 trace("expected %s, height %d scaled_hight %d, dpi %d\n", fd[i].face_name, height, fd[i].scaled_height, fd[i].dpi);
953 if(fd[i].dpi == tm.tmDigitizedAspectX)
955 trace("matched %s, height %d charset %x dpi %d\n", lf.lfFaceName, lf.lfHeight, lf.lfCharSet, fd[i].dpi);
956 if (fd[i].skip_lang_id == 0 || system_lang_id != fd[i].skip_lang_id)
958 ok(tm.tmWeight == fd[i].weight, "%s(%d): tm.tmWeight %d != %d\n", fd[i].face_name, height, tm.tmWeight, fd[i].weight);
959 if (fd[i].height & FH_SCALE)
960 ok(tm.tmHeight == fd[i].scaled_height, "%s(%d): tm.tmHeight %d != %d\n", fd[i].face_name, height, tm.tmHeight, fd[i].scaled_height);
961 else
962 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);
963 ok(tm.tmAscent == fd[i].ascent, "%s(%d): tm.tmAscent %d != %d\n", fd[i].face_name, height, tm.tmAscent, fd[i].ascent);
964 ok(tm.tmDescent == fd[i].descent, "%s(%d): tm.tmDescent %d != %d\n", fd[i].face_name, height, tm.tmDescent, fd[i].descent);
965 ok(tm.tmInternalLeading == fd[i].int_leading, "%s(%d): tm.tmInternalLeading %d != %d\n", fd[i].face_name, height, tm.tmInternalLeading, fd[i].int_leading);
966 ok(tm.tmExternalLeading == fd[i].ext_leading, "%s(%d): tm.tmExternalLeading %d != %d\n", fd[i].face_name, height, tm.tmExternalLeading, fd[i].ext_leading);
967 ok(tm.tmAveCharWidth == fd[i].ave_char_width, "%s(%d): tm.tmAveCharWidth %d != %d\n", fd[i].face_name, height, tm.tmAveCharWidth, fd[i].ave_char_width);
968 ok(tm.tmFirstChar == fd[i].first_char, "%s(%d): tm.tmFirstChar = %02x\n", fd[i].face_name, height, tm.tmFirstChar);
969 ok(tm.tmLastChar == fd[i].last_char, "%s(%d): tm.tmLastChar = %02x\n", fd[i].face_name, height, tm.tmLastChar);
970 /* Substitutions like MS Sans Serif,0=MS Sans Serif,204
971 make default char test fail */
972 if (tm.tmCharSet == lf.lfCharSet)
973 ok(tm.tmDefaultChar == fd[i].def_char, "%s(%d): tm.tmDefaultChar = %02x\n", fd[i].face_name, height, tm.tmDefaultChar);
974 ok(tm.tmBreakChar == fd[i].break_char, "%s(%d): tm.tmBreakChar = %02x\n", fd[i].face_name, height, tm.tmBreakChar);
975 ok(tm.tmCharSet == expected_cs || tm.tmCharSet == ANSI_CHARSET, "%s(%d): tm.tmCharSet %d != %d\n", fd[i].face_name, height, tm.tmCharSet, expected_cs);
977 /* Don't run the max char width test on System/ANSI_CHARSET. We have extra characters in our font
978 that make the max width bigger */
979 if(strcmp(lf.lfFaceName, "System") || lf.lfCharSet != ANSI_CHARSET)
980 ok(tm.tmMaxCharWidth == fd[i].max_char_width, "%s(%d): tm.tmMaxCharWidth %d != %d\n", fd[i].face_name, height, tm.tmMaxCharWidth, fd[i].max_char_width);
982 else
983 skip("Skipping font metrics test for system langid 0x%x\n",
984 system_lang_id);
986 SelectObject(hdc, old_hfont);
987 DeleteObject(hfont);
991 DeleteDC(hdc);
994 static void test_GdiGetCharDimensions(void)
996 HDC hdc;
997 TEXTMETRICW tm;
998 LONG ret;
999 SIZE size;
1000 LONG avgwidth, height;
1001 static const char szAlphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
1003 if (!pGdiGetCharDimensions)
1005 win_skip("GdiGetCharDimensions not available on this platform\n");
1006 return;
1009 hdc = CreateCompatibleDC(NULL);
1011 GetTextExtentPoint(hdc, szAlphabet, strlen(szAlphabet), &size);
1012 avgwidth = ((size.cx / 26) + 1) / 2;
1014 ret = pGdiGetCharDimensions(hdc, &tm, &height);
1015 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
1016 ok(height == tm.tmHeight, "GdiGetCharDimensions should have set height to %d instead of %d\n", tm.tmHeight, height);
1018 ret = pGdiGetCharDimensions(hdc, &tm, NULL);
1019 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
1021 ret = pGdiGetCharDimensions(hdc, NULL, NULL);
1022 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
1024 height = 0;
1025 ret = pGdiGetCharDimensions(hdc, NULL, &height);
1026 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
1027 ok(height == size.cy, "GdiGetCharDimensions should have set height to %d instead of %d\n", size.cy, height);
1029 DeleteDC(hdc);
1032 static int CALLBACK create_font_proc(const LOGFONT *lpelfe,
1033 const TEXTMETRIC *lpntme,
1034 DWORD FontType, LPARAM lParam)
1036 if (FontType & TRUETYPE_FONTTYPE)
1038 HFONT hfont;
1040 hfont = CreateFontIndirect(lpelfe);
1041 if (hfont)
1043 *(HFONT *)lParam = hfont;
1044 return 0;
1048 return 1;
1051 static void test_GetCharABCWidths(void)
1053 static const WCHAR str[] = {'a',0};
1054 BOOL ret;
1055 HDC hdc;
1056 LOGFONTA lf;
1057 HFONT hfont;
1058 ABC abc[1];
1059 ABCFLOAT abcf[1];
1060 WORD glyphs[1];
1061 DWORD nb;
1062 static const struct
1064 UINT first;
1065 UINT last;
1066 } range[] =
1068 {0xff, 0xff},
1069 {0x100, 0x100},
1070 {0xff, 0x100},
1071 {0x1ff, 0xff00},
1072 {0xffff, 0xffff},
1073 {0x10000, 0x10000},
1074 {0xffff, 0x10000},
1075 {0xffffff, 0xffffff},
1076 {0x1000000, 0x1000000},
1077 {0xffffff, 0x1000000},
1078 {0xffffffff, 0xffffffff},
1079 {0x00, 0xff}
1081 static const struct
1083 UINT cs;
1084 UINT a;
1085 UINT w;
1086 BOOL r[sizeof range / sizeof range[0]];
1087 } c[] =
1089 {ANSI_CHARSET, 0x30, 0x30,
1090 {TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE}},
1091 {SHIFTJIS_CHARSET, 0x82a0, 0x3042,
1092 {TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE}},
1093 {HANGEUL_CHARSET, 0x8141, 0xac02,
1094 {TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE}},
1095 {JOHAB_CHARSET, 0x8446, 0x3135,
1096 {TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE}},
1097 {GB2312_CHARSET, 0x8141, 0x4e04,
1098 {TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE}},
1099 {CHINESEBIG5_CHARSET, 0xa142, 0x3001,
1100 {TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE}}
1102 UINT i;
1104 if (!pGetCharABCWidthsA || !pGetCharABCWidthsW || !pGetCharABCWidthsFloatW || !pGetCharABCWidthsI)
1106 win_skip("GetCharABCWidthsA/W/I not available on this platform\n");
1107 return;
1110 memset(&lf, 0, sizeof(lf));
1111 strcpy(lf.lfFaceName, "System");
1112 lf.lfHeight = 20;
1114 hfont = CreateFontIndirectA(&lf);
1115 hdc = GetDC(0);
1116 hfont = SelectObject(hdc, hfont);
1118 nb = pGetGlyphIndicesW(hdc, str, 1, glyphs, 0);
1119 ok(nb == 1, "GetGlyphIndicesW should have returned 1\n");
1121 ret = pGetCharABCWidthsI(NULL, 0, 1, glyphs, abc);
1122 ok(!ret, "GetCharABCWidthsI should have failed\n");
1124 ret = pGetCharABCWidthsI(hdc, 0, 1, glyphs, NULL);
1125 ok(!ret, "GetCharABCWidthsI should have failed\n");
1127 ret = pGetCharABCWidthsI(hdc, 0, 1, glyphs, abc);
1128 ok(ret, "GetCharABCWidthsI should have succeeded\n");
1130 ret = pGetCharABCWidthsW(NULL, 'a', 'a', abc);
1131 ok(!ret, "GetCharABCWidthsW should have failed\n");
1133 ret = pGetCharABCWidthsW(hdc, 'a', 'a', NULL);
1134 ok(!ret, "GetCharABCWidthsW should have failed\n");
1136 ret = pGetCharABCWidthsW(hdc, 'a', 'a', abc);
1137 ok(!ret, "GetCharABCWidthsW should have failed\n");
1139 ret = pGetCharABCWidthsFloatW(NULL, 'a', 'a', abcf);
1140 ok(!ret, "GetCharABCWidthsFloatW should have failed\n");
1142 ret = pGetCharABCWidthsFloatW(hdc, 'a', 'a', NULL);
1143 ok(!ret, "GetCharABCWidthsFloatW should have failed\n");
1145 ret = pGetCharABCWidthsFloatW(hdc, 'a', 'a', abcf);
1146 ok(ret, "GetCharABCWidthsFloatW should have succeeded\n");
1148 hfont = SelectObject(hdc, hfont);
1149 DeleteObject(hfont);
1151 for (i = 0; i < sizeof c / sizeof c[0]; ++i)
1153 ABC a[2], w[2];
1154 ABC full[256];
1155 UINT code = 0x41, j;
1157 lf.lfFaceName[0] = '\0';
1158 lf.lfCharSet = c[i].cs;
1159 lf.lfPitchAndFamily = 0;
1160 if (EnumFontFamiliesEx(hdc, &lf, create_font_proc, (LPARAM)&hfont, 0))
1162 skip("TrueType font for charset %u is not installed\n", c[i].cs);
1163 continue;
1166 memset(a, 0, sizeof a);
1167 memset(w, 0, sizeof w);
1168 hfont = SelectObject(hdc, hfont);
1169 ok(pGetCharABCWidthsA(hdc, c[i].a, c[i].a + 1, a) &&
1170 pGetCharABCWidthsW(hdc, c[i].w, c[i].w + 1, w) &&
1171 memcmp(a, w, sizeof a) == 0,
1172 "GetCharABCWidthsA and GetCharABCWidthsW should return same widths. charset = %u\n", c[i].cs);
1174 memset(a, 0xbb, sizeof a);
1175 ret = pGetCharABCWidthsA(hdc, code, code, a);
1176 ok(ret, "GetCharABCWidthsA should have succeeded\n");
1177 memset(full, 0xcc, sizeof full);
1178 ret = pGetCharABCWidthsA(hdc, 0x00, code, full);
1179 ok(ret, "GetCharABCWidthsA should have succeeded\n");
1180 ok(memcmp(&a[0], &full[code], sizeof(ABC)) == 0,
1181 "GetCharABCWidthsA info should match. codepage = %u\n", c[i].cs);
1183 for (j = 0; j < sizeof range / sizeof range[0]; ++j)
1185 memset(full, 0xdd, sizeof full);
1186 ret = pGetCharABCWidthsA(hdc, range[j].first, range[j].last, full);
1187 ok(ret == c[i].r[j], "GetCharABCWidthsA %x - %x should have %s\n",
1188 range[j].first, range[j].last, c[i].r[j] ? "succeeded" : "failed");
1189 if (ret)
1191 UINT last = range[j].last - range[j].first;
1192 ret = pGetCharABCWidthsA(hdc, range[j].last, range[j].last, a);
1193 ok(ret && memcmp(&full[last], &a[0], sizeof(ABC)) == 0,
1194 "GetCharABCWidthsA %x should match. codepage = %u\n",
1195 range[j].last, c[i].cs);
1199 hfont = SelectObject(hdc, hfont);
1200 DeleteObject(hfont);
1203 ReleaseDC(NULL, hdc);
1206 static void test_text_extents(void)
1208 static const WCHAR wt[] = {'O','n','e','\n','t','w','o',' ','3',0};
1209 LPINT extents;
1210 INT i, len, fit1, fit2;
1211 LOGFONTA lf;
1212 TEXTMETRICA tm;
1213 HDC hdc;
1214 HFONT hfont;
1215 SIZE sz;
1216 SIZE sz1, sz2;
1217 BOOL ret;
1219 memset(&lf, 0, sizeof(lf));
1220 strcpy(lf.lfFaceName, "Arial");
1221 lf.lfHeight = 20;
1223 hfont = CreateFontIndirectA(&lf);
1224 hdc = GetDC(0);
1225 hfont = SelectObject(hdc, hfont);
1226 GetTextMetricsA(hdc, &tm);
1227 GetTextExtentPointA(hdc, "o", 1, &sz);
1228 ok(sz.cy == tm.tmHeight, "cy %d tmHeight %d\n", sz.cy, tm.tmHeight);
1230 SetLastError(0xdeadbeef);
1231 GetTextExtentExPointW(hdc, wt, 1, 1, &fit1, &fit2, &sz1);
1232 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
1234 win_skip("Skipping remainder of text extents test on a Win9x platform\n");
1235 hfont = SelectObject(hdc, hfont);
1236 DeleteObject(hfont);
1237 ReleaseDC(0, hdc);
1238 return;
1241 len = lstrlenW(wt);
1242 extents = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof extents[0]);
1243 extents[0] = 1; /* So that the increasing sequence test will fail
1244 if the extents array is untouched. */
1245 GetTextExtentExPointW(hdc, wt, len, 32767, &fit1, extents, &sz1);
1246 GetTextExtentPointW(hdc, wt, len, &sz2);
1247 ok(sz1.cy == sz2.cy,
1248 "cy from GetTextExtentExPointW (%d) and GetTextExtentPointW (%d) differ\n", sz1.cy, sz2.cy);
1249 /* Because of the '\n' in the string GetTextExtentExPoint and
1250 GetTextExtentPoint return different widths under Win2k, but
1251 under WinXP they return the same width. So we don't test that
1252 here. */
1254 for (i = 1; i < len; ++i)
1255 ok(extents[i-1] <= extents[i],
1256 "GetTextExtentExPointW generated a non-increasing sequence of partial extents (at position %d)\n",
1258 ok(extents[len-1] == sz1.cx, "GetTextExtentExPointW extents and size don't match\n");
1259 ok(0 <= fit1 && fit1 <= len, "GetTextExtentExPointW generated illegal value %d for fit\n", fit1);
1260 ok(0 < fit1, "GetTextExtentExPointW says we can't even fit one letter in 32767 logical units\n");
1261 GetTextExtentExPointW(hdc, wt, len, extents[2], &fit2, NULL, &sz2);
1262 ok(sz1.cx == sz2.cx && sz1.cy == sz2.cy, "GetTextExtentExPointW returned different sizes for the same string\n");
1263 ok(fit2 == 3, "GetTextExtentExPointW extents isn't consistent with fit\n");
1264 GetTextExtentExPointW(hdc, wt, len, extents[2]-1, &fit2, NULL, &sz2);
1265 ok(fit2 == 2, "GetTextExtentExPointW extents isn't consistent with fit\n");
1266 GetTextExtentExPointW(hdc, wt, 2, 0, NULL, extents + 2, &sz2);
1267 ok(extents[0] == extents[2] && extents[1] == extents[3],
1268 "GetTextExtentExPointW with lpnFit == NULL returns incorrect results\n");
1269 GetTextExtentExPointW(hdc, wt, 2, 0, NULL, NULL, &sz1);
1270 ok(sz1.cx == sz2.cx && sz1.cy == sz2.cy,
1271 "GetTextExtentExPointW with lpnFit and alpDx both NULL returns incorrect results\n");
1272 HeapFree(GetProcessHeap(), 0, extents);
1274 /* extents functions fail with -ve counts (the interesting case being -1) */
1275 ret = GetTextExtentPointA(hdc, "o", -1, &sz);
1276 ok(ret == FALSE, "got %d\n", ret);
1277 ret = GetTextExtentExPointA(hdc, "o", -1, 0, NULL, NULL, &sz);
1278 ok(ret == FALSE, "got %d\n", ret);
1279 ret = GetTextExtentExPointW(hdc, wt, -1, 0, NULL, NULL, &sz1);
1280 ok(ret == FALSE, "got %d\n", ret);
1282 /* max_extent = 0 succeeds and returns zero */
1283 fit1 = fit2 = -215;
1284 ret = GetTextExtentExPointA(hdc, NULL, 0, 0, &fit1, NULL, &sz);
1285 ok(ret == TRUE ||
1286 broken(ret == FALSE), /* NT4, 2k */
1287 "got %d\n", ret);
1288 ok(fit1 == 0 ||
1289 broken(fit1 == -215), /* NT4, 2k */
1290 "fit = %d\n", fit1);
1291 ret = GetTextExtentExPointW(hdc, NULL, 0, 0, &fit2, NULL, &sz1);
1292 ok(ret == TRUE, "got %d\n", ret);
1293 ok(fit2 == 0, "fit = %d\n", fit2);
1295 /* max_extent = -1 is interpreted as a very large width that will
1296 * definitely fit our three characters */
1297 fit1 = fit2 = -215;
1298 ret = GetTextExtentExPointA(hdc, "One", 3, -1, &fit1, NULL, &sz);
1299 ok(ret == TRUE, "got %d\n", ret);
1300 ok(fit1 == 3, "fit = %d\n", fit1);
1301 ret = GetTextExtentExPointW(hdc, wt, 3, -1, &fit2, NULL, &sz);
1302 ok(ret == TRUE, "got %d\n", ret);
1303 ok(fit2 == 3, "fit = %d\n", fit2);
1305 /* max_extent = -2 is interpreted similarly, but the Ansi version
1306 * rejects it while the Unicode one accepts it */
1307 fit1 = fit2 = -215;
1308 ret = GetTextExtentExPointA(hdc, "One", 3, -2, &fit1, NULL, &sz);
1309 ok(ret == FALSE, "got %d\n", ret);
1310 ok(fit1 == -215, "fit = %d\n", fit1);
1311 ret = GetTextExtentExPointW(hdc, wt, 3, -2, &fit2, NULL, &sz);
1312 ok(ret == TRUE, "got %d\n", ret);
1313 ok(fit2 == 3, "fit = %d\n", fit2);
1315 hfont = SelectObject(hdc, hfont);
1316 DeleteObject(hfont);
1317 ReleaseDC(NULL, hdc);
1320 static void test_GetGlyphIndices(void)
1322 HDC hdc;
1323 HFONT hfont;
1324 DWORD charcount;
1325 LOGFONTA lf;
1326 DWORD flags = 0;
1327 WCHAR testtext[] = {'T','e','s','t',0xffff,0};
1328 WORD glyphs[(sizeof(testtext)/2)-1];
1329 TEXTMETRIC textm;
1330 HFONT hOldFont;
1332 if (!pGetGlyphIndicesW) {
1333 win_skip("GetGlyphIndicesW not available on platform\n");
1334 return;
1337 hdc = GetDC(0);
1339 memset(&lf, 0, sizeof(lf));
1340 strcpy(lf.lfFaceName, "System");
1341 lf.lfHeight = 16;
1342 lf.lfCharSet = ANSI_CHARSET;
1344 hfont = CreateFontIndirectA(&lf);
1345 ok(hfont != 0, "CreateFontIndirectEx failed\n");
1346 ok(GetTextMetrics(hdc, &textm), "GetTextMetric failed\n");
1347 if (textm.tmCharSet == ANSI_CHARSET)
1349 flags |= GGI_MARK_NONEXISTING_GLYPHS;
1350 charcount = pGetGlyphIndicesW(hdc, testtext, (sizeof(testtext)/2)-1, glyphs, flags);
1351 ok(charcount == 5, "GetGlyphIndicesW count of glyphs should = 5 not %d\n", charcount);
1352 ok((glyphs[4] == 0x001f || glyphs[4] == 0xffff /* Vista */), "GetGlyphIndicesW should have returned a nonexistent char not %04x\n", glyphs[4]);
1353 flags = 0;
1354 charcount = pGetGlyphIndicesW(hdc, testtext, (sizeof(testtext)/2)-1, glyphs, flags);
1355 ok(charcount == 5, "GetGlyphIndicesW count of glyphs should = 5 not %d\n", charcount);
1356 ok(glyphs[4] == textm.tmDefaultChar, "GetGlyphIndicesW should have returned a %04x not %04x\n",
1357 textm.tmDefaultChar, glyphs[4]);
1359 else
1360 /* FIXME: Write tests for non-ANSI charsets. */
1361 skip("GetGlyphIndices System font tests only for ANSI_CHARSET\n");
1363 if(!is_font_installed("Tahoma"))
1365 skip("Tahoma is not installed so skipping this test\n");
1366 return;
1368 memset(&lf, 0, sizeof(lf));
1369 strcpy(lf.lfFaceName, "Tahoma");
1370 lf.lfHeight = 20;
1372 hfont = CreateFontIndirectA(&lf);
1373 hOldFont = SelectObject(hdc, hfont);
1374 ok(GetTextMetrics(hdc, &textm), "GetTextMetric failed\n");
1375 flags |= GGI_MARK_NONEXISTING_GLYPHS;
1376 charcount = pGetGlyphIndicesW(hdc, testtext, (sizeof(testtext)/2)-1, glyphs, flags);
1377 ok(charcount == 5, "GetGlyphIndicesW count of glyphs should = 5 not %d\n", charcount);
1378 ok(glyphs[4] == 0xffff, "GetGlyphIndicesW should have returned 0xffff char not %04x\n", glyphs[4]);
1379 flags = 0;
1380 testtext[0] = textm.tmDefaultChar;
1381 charcount = pGetGlyphIndicesW(hdc, testtext, (sizeof(testtext)/2)-1, glyphs, flags);
1382 ok(charcount == 5, "GetGlyphIndicesW count of glyphs should = 5 not %d\n", charcount);
1383 ok(glyphs[0] == 0, "GetGlyphIndicesW for tmDefaultChar should be 0 not %04x\n", glyphs[0]);
1384 ok(glyphs[4] == 0, "GetGlyphIndicesW should have returned 0 not %04x\n", glyphs[4]);
1385 DeleteObject(SelectObject(hdc, hOldFont));
1388 static void test_GetKerningPairs(void)
1390 static const struct kerning_data
1392 const char face_name[LF_FACESIZE];
1393 LONG height;
1394 /* some interesting fields from OUTLINETEXTMETRIC */
1395 LONG tmHeight, tmAscent, tmDescent;
1396 UINT otmEMSquare;
1397 INT otmAscent;
1398 INT otmDescent;
1399 UINT otmLineGap;
1400 UINT otmsCapEmHeight;
1401 UINT otmsXHeight;
1402 INT otmMacAscent;
1403 INT otmMacDescent;
1404 UINT otmMacLineGap;
1405 UINT otmusMinimumPPEM;
1406 /* small subset of kerning pairs to test */
1407 DWORD total_kern_pairs;
1408 const KERNINGPAIR kern_pair[26];
1409 } kd[] =
1411 {"Arial", 12, 12, 9, 3,
1412 2048, 7, -2, 1, 5, 2, 8, -2, 0, 9,
1415 {' ','A',-1},{' ','T',0},{' ','Y',0},{'1','1',-1},
1416 {'A',' ',-1},{'A','T',-1},{'A','V',-1},{'A','W',0},
1417 {'A','Y',-1},{'A','v',0},{'A','w',0},{'A','y',0},
1418 {'F',',',-1},{'F','.',-1},{'F','A',-1},{'L',' ',0},
1419 {'L','T',-1},{'L','V',-1},{'L','W',-1},{'L','Y',-1},
1420 {915,912,+1},{915,913,-1},{910,912,+1},{910,913,-1},
1421 {933,970,+1},{933,972,-1}
1424 {"Arial", -34, 39, 32, 7,
1425 2048, 25, -7, 5, 17, 9, 31, -7, 1, 9,
1428 {' ','A',-2},{' ','T',-1},{' ','Y',-1},{'1','1',-3},
1429 {'A',' ',-2},{'A','T',-3},{'A','V',-3},{'A','W',-1},
1430 {'A','Y',-3},{'A','v',-1},{'A','w',-1},{'A','y',-1},
1431 {'F',',',-4},{'F','.',-4},{'F','A',-2},{'L',' ',-1},
1432 {'L','T',-3},{'L','V',-3},{'L','W',-3},{'L','Y',-3},
1433 {915,912,+3},{915,913,-3},{910,912,+3},{910,913,-3},
1434 {933,970,+2},{933,972,-3}
1437 { "Arial", 120, 120, 97, 23,
1438 2048, 79, -23, 16, 54, 27, 98, -23, 4, 9,
1441 {' ','A',-6},{' ','T',-2},{' ','Y',-2},{'1','1',-8},
1442 {'A',' ',-6},{'A','T',-8},{'A','V',-8},{'A','W',-4},
1443 {'A','Y',-8},{'A','v',-2},{'A','w',-2},{'A','y',-2},
1444 {'F',',',-12},{'F','.',-12},{'F','A',-6},{'L',' ',-4},
1445 {'L','T',-8},{'L','V',-8},{'L','W',-8},{'L','Y',-8},
1446 {915,912,+9},{915,913,-10},{910,912,+9},{910,913,-8},
1447 {933,970,+6},{933,972,-10}
1450 #if 0 /* this set fails due to +1/-1 errors (rounding bug?), needs investigation. */
1451 { "Arial", 1024 /* usually 1/2 of EM Square */, 1024, 830, 194,
1452 2048, 668, -193, 137, 459, 229, 830, -194, 30, 9,
1455 {' ','A',-51},{' ','T',-17},{' ','Y',-17},{'1','1',-68},
1456 {'A',' ',-51},{'A','T',-68},{'A','V',-68},{'A','W',-34},
1457 {'A','Y',-68},{'A','v',-17},{'A','w',-17},{'A','y',-17},
1458 {'F',',',-102},{'F','.',-102},{'F','A',-51},{'L',' ',-34},
1459 {'L','T',-68},{'L','V',-68},{'L','W',-68},{'L','Y',-68},
1460 {915,912,+73},{915,913,-84},{910,912,+76},{910,913,-68},
1461 {933,970,+54},{933,972,-83}
1464 #endif
1466 LOGFONT lf;
1467 HFONT hfont, hfont_old;
1468 KERNINGPAIR *kern_pair;
1469 HDC hdc;
1470 DWORD total_kern_pairs, ret, i, n, matches;
1472 hdc = GetDC(0);
1474 /* GetKerningPairsA maps unicode set of kerning pairs to current code page
1475 * which may render this test unusable, so we're trying to avoid that.
1477 SetLastError(0xdeadbeef);
1478 GetKerningPairsW(hdc, 0, NULL);
1479 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
1481 win_skip("Skipping the GetKerningPairs test on a Win9x platform\n");
1482 ReleaseDC(0, hdc);
1483 return;
1486 for (i = 0; i < sizeof(kd)/sizeof(kd[0]); i++)
1488 OUTLINETEXTMETRICW otm;
1489 UINT uiRet;
1491 if (!is_font_installed(kd[i].face_name))
1493 trace("%s is not installed so skipping this test\n", kd[i].face_name);
1494 continue;
1497 trace("testing font %s, height %d\n", kd[i].face_name, kd[i].height);
1499 memset(&lf, 0, sizeof(lf));
1500 strcpy(lf.lfFaceName, kd[i].face_name);
1501 lf.lfHeight = kd[i].height;
1502 hfont = CreateFontIndirect(&lf);
1503 assert(hfont != 0);
1505 hfont_old = SelectObject(hdc, hfont);
1507 SetLastError(0xdeadbeef);
1508 otm.otmSize = sizeof(otm); /* just in case for Win9x compatibility */
1509 uiRet = GetOutlineTextMetricsW(hdc, sizeof(otm), &otm);
1510 ok(uiRet == sizeof(otm), "GetOutlineTextMetricsW error %d\n", GetLastError());
1512 ok(match_off_by_1(kd[i].tmHeight, otm.otmTextMetrics.tmHeight), "expected %d, got %d\n",
1513 kd[i].tmHeight, otm.otmTextMetrics.tmHeight);
1514 ok(match_off_by_1(kd[i].tmAscent, otm.otmTextMetrics.tmAscent), "expected %d, got %d\n",
1515 kd[i].tmAscent, otm.otmTextMetrics.tmAscent);
1516 ok(kd[i].tmDescent == otm.otmTextMetrics.tmDescent, "expected %d, got %d\n",
1517 kd[i].tmDescent, otm.otmTextMetrics.tmDescent);
1519 ok(kd[i].otmEMSquare == otm.otmEMSquare, "expected %u, got %u\n",
1520 kd[i].otmEMSquare, otm.otmEMSquare);
1521 ok(kd[i].otmAscent == otm.otmAscent, "expected %d, got %d\n",
1522 kd[i].otmAscent, otm.otmAscent);
1523 ok(kd[i].otmDescent == otm.otmDescent, "expected %d, got %d\n",
1524 kd[i].otmDescent, otm.otmDescent);
1525 ok(kd[i].otmLineGap == otm.otmLineGap, "expected %u, got %u\n",
1526 kd[i].otmLineGap, otm.otmLineGap);
1527 ok(near_match(kd[i].otmMacDescent, otm.otmMacDescent), "expected %d, got %d\n",
1528 kd[i].otmMacDescent, otm.otmMacDescent);
1529 ok(near_match(kd[i].otmMacAscent, otm.otmMacAscent), "expected %d, got %d\n",
1530 kd[i].otmMacAscent, otm.otmMacAscent);
1531 todo_wine {
1532 ok(kd[i].otmsCapEmHeight == otm.otmsCapEmHeight, "expected %u, got %u\n",
1533 kd[i].otmsCapEmHeight, otm.otmsCapEmHeight);
1534 ok(kd[i].otmsXHeight == otm.otmsXHeight, "expected %u, got %u\n",
1535 kd[i].otmsXHeight, otm.otmsXHeight);
1536 /* FIXME: this one sometimes succeeds due to expected 0, enable it when removing todo */
1537 if (0) ok(kd[i].otmMacLineGap == otm.otmMacLineGap, "expected %u, got %u\n",
1538 kd[i].otmMacLineGap, otm.otmMacLineGap);
1539 ok(kd[i].otmusMinimumPPEM == otm.otmusMinimumPPEM, "expected %u, got %u\n",
1540 kd[i].otmusMinimumPPEM, otm.otmusMinimumPPEM);
1543 total_kern_pairs = GetKerningPairsW(hdc, 0, NULL);
1544 trace("total_kern_pairs %u\n", total_kern_pairs);
1545 kern_pair = HeapAlloc(GetProcessHeap(), 0, total_kern_pairs * sizeof(*kern_pair));
1547 /* Win98 (GetKerningPairsA) and XP behave differently here, the test
1548 * passes on XP.
1550 SetLastError(0xdeadbeef);
1551 ret = GetKerningPairsW(hdc, 0, kern_pair);
1552 ok(GetLastError() == ERROR_INVALID_PARAMETER,
1553 "got error %u, expected ERROR_INVALID_PARAMETER\n", GetLastError());
1554 ok(ret == 0, "got %u, expected 0\n", ret);
1556 ret = GetKerningPairsW(hdc, 100, NULL);
1557 ok(ret == total_kern_pairs, "got %u, expected %u\n", ret, total_kern_pairs);
1559 ret = GetKerningPairsW(hdc, total_kern_pairs/2, kern_pair);
1560 ok(ret == total_kern_pairs/2, "got %u, expected %u\n", ret, total_kern_pairs/2);
1562 ret = GetKerningPairsW(hdc, total_kern_pairs, kern_pair);
1563 ok(ret == total_kern_pairs, "got %u, expected %u\n", ret, total_kern_pairs);
1565 matches = 0;
1567 for (n = 0; n < ret; n++)
1569 DWORD j;
1570 /* Disabled to limit console spam */
1571 if (0 && kern_pair[n].wFirst < 127 && kern_pair[n].wSecond < 127)
1572 trace("{'%c','%c',%d},\n",
1573 kern_pair[n].wFirst, kern_pair[n].wSecond, kern_pair[n].iKernAmount);
1574 for (j = 0; j < kd[i].total_kern_pairs; j++)
1576 if (kern_pair[n].wFirst == kd[i].kern_pair[j].wFirst &&
1577 kern_pair[n].wSecond == kd[i].kern_pair[j].wSecond)
1579 ok(kern_pair[n].iKernAmount == kd[i].kern_pair[j].iKernAmount,
1580 "pair %d:%d got %d, expected %d\n",
1581 kern_pair[n].wFirst, kern_pair[n].wSecond,
1582 kern_pair[n].iKernAmount, kd[i].kern_pair[j].iKernAmount);
1583 matches++;
1588 ok(matches == kd[i].total_kern_pairs, "got matches %u, expected %u\n",
1589 matches, kd[i].total_kern_pairs);
1591 HeapFree(GetProcessHeap(), 0, kern_pair);
1593 SelectObject(hdc, hfont_old);
1594 DeleteObject(hfont);
1597 ReleaseDC(0, hdc);
1600 static void test_height_selection(void)
1602 static const struct font_data
1604 const char face_name[LF_FACESIZE];
1605 int requested_height;
1606 int weight, height, ascent, descent, int_leading, ext_leading, dpi;
1607 } fd[] =
1609 {"Tahoma", -12, FW_NORMAL, 14, 12, 2, 2, 0, 96 },
1610 {"Tahoma", -24, FW_NORMAL, 29, 24, 5, 5, 0, 96 },
1611 {"Tahoma", -48, FW_NORMAL, 58, 48, 10, 10, 0, 96 },
1612 {"Tahoma", -96, FW_NORMAL, 116, 96, 20, 20, 0, 96 },
1613 {"Tahoma", -192, FW_NORMAL, 232, 192, 40, 40, 0, 96 },
1614 {"Tahoma", 12, FW_NORMAL, 12, 10, 2, 2, 0, 96 },
1615 {"Tahoma", 24, FW_NORMAL, 24, 20, 4, 4, 0, 96 },
1616 {"Tahoma", 48, FW_NORMAL, 48, 40, 8, 8, 0, 96 },
1617 {"Tahoma", 96, FW_NORMAL, 96, 80, 16, 17, 0, 96 },
1618 {"Tahoma", 192, FW_NORMAL, 192, 159, 33, 33, 0, 96 }
1620 HDC hdc;
1621 LOGFONT lf;
1622 HFONT hfont, old_hfont;
1623 TEXTMETRIC tm;
1624 INT ret, i;
1626 hdc = CreateCompatibleDC(0);
1627 assert(hdc);
1629 for (i = 0; i < sizeof(fd)/sizeof(fd[0]); i++)
1631 if (!is_truetype_font_installed(fd[i].face_name))
1633 skip("%s is not installed\n", fd[i].face_name);
1634 continue;
1637 memset(&lf, 0, sizeof(lf));
1638 lf.lfHeight = fd[i].requested_height;
1639 lf.lfWeight = fd[i].weight;
1640 strcpy(lf.lfFaceName, fd[i].face_name);
1642 hfont = CreateFontIndirect(&lf);
1643 assert(hfont);
1645 old_hfont = SelectObject(hdc, hfont);
1646 ret = GetTextMetrics(hdc, &tm);
1647 ok(ret, "GetTextMetrics error %d\n", GetLastError());
1648 if(fd[i].dpi == tm.tmDigitizedAspectX)
1650 trace("found font %s, height %d charset %x dpi %d\n", lf.lfFaceName, lf.lfHeight, lf.lfCharSet, fd[i].dpi);
1651 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);
1652 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);
1653 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);
1654 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);
1655 #if 0 /* FIXME: calculation of tmInternalLeading in Wine doesn't match what Windows does */
1656 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);
1657 #endif
1658 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);
1661 SelectObject(hdc, old_hfont);
1662 DeleteObject(hfont);
1665 DeleteDC(hdc);
1668 static void test_GetOutlineTextMetrics(void)
1670 OUTLINETEXTMETRIC *otm;
1671 LOGFONT lf;
1672 HFONT hfont, hfont_old;
1673 HDC hdc;
1674 DWORD ret, otm_size;
1675 LPSTR unset_ptr;
1677 if (!is_font_installed("Arial"))
1679 skip("Arial is not installed\n");
1680 return;
1683 hdc = GetDC(0);
1685 memset(&lf, 0, sizeof(lf));
1686 strcpy(lf.lfFaceName, "Arial");
1687 lf.lfHeight = -13;
1688 lf.lfWeight = FW_NORMAL;
1689 lf.lfPitchAndFamily = DEFAULT_PITCH;
1690 lf.lfQuality = PROOF_QUALITY;
1691 hfont = CreateFontIndirect(&lf);
1692 assert(hfont != 0);
1694 hfont_old = SelectObject(hdc, hfont);
1695 otm_size = GetOutlineTextMetrics(hdc, 0, NULL);
1696 trace("otm buffer size %u (0x%x)\n", otm_size, otm_size);
1698 otm = HeapAlloc(GetProcessHeap(), 0, otm_size);
1700 memset(otm, 0xAA, otm_size);
1701 SetLastError(0xdeadbeef);
1702 otm->otmSize = sizeof(*otm); /* just in case for Win9x compatibility */
1703 ret = GetOutlineTextMetrics(hdc, otm->otmSize, otm);
1704 ok(ret == 1 /* Win9x */ ||
1705 ret == otm->otmSize /* XP*/,
1706 "expected %u, got %u, error %d\n", otm->otmSize, ret, GetLastError());
1707 if (ret != 1) /* Win9x doesn't care about pointing beyond of the buffer */
1709 ok(otm->otmpFamilyName == NULL, "expected NULL got %p\n", otm->otmpFamilyName);
1710 ok(otm->otmpFaceName == NULL, "expected NULL got %p\n", otm->otmpFaceName);
1711 ok(otm->otmpStyleName == NULL, "expected NULL got %p\n", otm->otmpStyleName);
1712 ok(otm->otmpFullName == NULL, "expected NULL got %p\n", otm->otmpFullName);
1715 memset(otm, 0xAA, otm_size);
1716 SetLastError(0xdeadbeef);
1717 otm->otmSize = otm_size; /* just in case for Win9x compatibility */
1718 ret = GetOutlineTextMetrics(hdc, otm->otmSize, otm);
1719 ok(ret == 1 /* Win9x */ ||
1720 ret == otm->otmSize /* XP*/,
1721 "expected %u, got %u, error %d\n", otm->otmSize, ret, GetLastError());
1722 if (ret != 1) /* Win9x doesn't care about pointing beyond of the buffer */
1724 ok(otm->otmpFamilyName != NULL, "expected not NULL got %p\n", otm->otmpFamilyName);
1725 ok(otm->otmpFaceName != NULL, "expected not NULL got %p\n", otm->otmpFaceName);
1726 ok(otm->otmpStyleName != NULL, "expected not NULL got %p\n", otm->otmpStyleName);
1727 ok(otm->otmpFullName != NULL, "expected not NULL got %p\n", otm->otmpFullName);
1730 /* ask about truncated data */
1731 memset(otm, 0xAA, otm_size);
1732 memset(&unset_ptr, 0xAA, sizeof(unset_ptr));
1733 SetLastError(0xdeadbeef);
1734 otm->otmSize = sizeof(*otm) - sizeof(LPSTR); /* just in case for Win9x compatibility */
1735 ret = GetOutlineTextMetrics(hdc, otm->otmSize, otm);
1736 ok(ret == 1 /* Win9x */ ||
1737 ret == otm->otmSize /* XP*/,
1738 "expected %u, got %u, error %d\n", otm->otmSize, ret, GetLastError());
1739 if (ret != 1) /* Win9x doesn't care about pointing beyond of the buffer */
1741 ok(otm->otmpFamilyName == NULL, "expected NULL got %p\n", otm->otmpFamilyName);
1742 ok(otm->otmpFaceName == NULL, "expected NULL got %p\n", otm->otmpFaceName);
1743 ok(otm->otmpStyleName == NULL, "expected NULL got %p\n", otm->otmpStyleName);
1745 ok(otm->otmpFullName == unset_ptr, "expected %p got %p\n", unset_ptr, otm->otmpFullName);
1747 HeapFree(GetProcessHeap(), 0, otm);
1749 SelectObject(hdc, hfont_old);
1750 DeleteObject(hfont);
1752 ReleaseDC(0, hdc);
1755 static void testJustification(HDC hdc, PSTR str, RECT *clientArea)
1757 INT y,
1758 breakCount,
1759 areaWidth = clientArea->right - clientArea->left,
1760 nErrors = 0, e;
1761 PSTR pFirstChar, pLastChar;
1762 SIZE size;
1763 TEXTMETRICA tm;
1764 struct err
1766 char *start;
1767 int len;
1768 int GetTextExtentExPointWWidth;
1769 } error[20];
1771 GetTextMetricsA(hdc, &tm);
1772 y = clientArea->top;
1773 do {
1774 breakCount = 0;
1775 while (*str == tm.tmBreakChar) str++; /* skip leading break chars */
1776 pFirstChar = str;
1778 do {
1779 pLastChar = str;
1781 /* if not at the end of the string, ... */
1782 if (*str == '\0') break;
1783 /* ... add the next word to the current extent */
1784 while (*str != '\0' && *str++ != tm.tmBreakChar);
1785 breakCount++;
1786 SetTextJustification(hdc, 0, 0);
1787 GetTextExtentPoint32(hdc, pFirstChar, str - pFirstChar - 1, &size);
1788 } while ((int) size.cx < areaWidth);
1790 /* ignore trailing break chars */
1791 breakCount--;
1792 while (*(pLastChar - 1) == tm.tmBreakChar)
1794 pLastChar--;
1795 breakCount--;
1798 if (*str == '\0' || breakCount <= 0) pLastChar = str;
1800 SetTextJustification(hdc, 0, 0);
1801 GetTextExtentPoint32(hdc, pFirstChar, pLastChar - pFirstChar, &size);
1803 /* do not justify the last extent */
1804 if (*str != '\0' && breakCount > 0)
1806 SetTextJustification(hdc, areaWidth - size.cx, breakCount);
1807 GetTextExtentPoint32(hdc, pFirstChar, pLastChar - pFirstChar, &size);
1808 if (size.cx != areaWidth && nErrors < sizeof(error)/sizeof(error[0]) - 1)
1810 error[nErrors].start = pFirstChar;
1811 error[nErrors].len = pLastChar - pFirstChar;
1812 error[nErrors].GetTextExtentExPointWWidth = size.cx;
1813 nErrors++;
1817 trace( "%u %.*s\n", size.cx, (int)(pLastChar - pFirstChar), pFirstChar);
1819 y += size.cy;
1820 str = pLastChar;
1821 } while (*str && y < clientArea->bottom);
1823 for (e = 0; e < nErrors; e++)
1825 /* The width returned by GetTextExtentPoint32() is exactly the same
1826 returned by GetTextExtentExPointW() - see dlls/gdi32/font.c */
1827 ok(error[e].GetTextExtentExPointWWidth == areaWidth,
1828 "GetTextExtentPointW() for \"%.*s\" should have returned a width of %d, not %d.\n",
1829 error[e].len, error[e].start, areaWidth, error[e].GetTextExtentExPointWWidth);
1833 static void test_SetTextJustification(void)
1835 HDC hdc;
1836 RECT clientArea;
1837 LOGFONTA lf;
1838 HFONT hfont;
1839 HWND hwnd;
1840 SIZE size, expect;
1841 int i;
1842 WORD indices[2];
1843 static char testText[] =
1844 "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do "
1845 "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut "
1846 "enim ad minim veniam, quis nostrud exercitation ullamco laboris "
1847 "nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in "
1848 "reprehenderit in voluptate velit esse cillum dolore eu fugiat "
1849 "nulla pariatur. Excepteur sint occaecat cupidatat non proident, "
1850 "sunt in culpa qui officia deserunt mollit anim id est laborum.";
1852 hwnd = CreateWindowExA(0, "static", "", WS_POPUP, 0,0, 400,400, 0, 0, 0, NULL);
1853 GetClientRect( hwnd, &clientArea );
1854 hdc = GetDC( hwnd );
1856 memset(&lf, 0, sizeof lf);
1857 lf.lfCharSet = ANSI_CHARSET;
1858 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
1859 lf.lfWeight = FW_DONTCARE;
1860 lf.lfHeight = 20;
1861 lf.lfQuality = DEFAULT_QUALITY;
1862 lstrcpyA(lf.lfFaceName, "Times New Roman");
1863 hfont = create_font("Times New Roman", &lf);
1864 SelectObject(hdc, hfont);
1866 testJustification(hdc, testText, &clientArea);
1868 if (!pGetGlyphIndicesA || !pGetTextExtentExPointI) goto done;
1869 pGetGlyphIndicesA( hdc, "A ", 2, indices, 0 );
1871 SetTextJustification(hdc, 0, 0);
1872 GetTextExtentPoint32(hdc, " ", 1, &expect);
1873 GetTextExtentPoint32(hdc, " ", 3, &size);
1874 ok( size.cx == 3 * expect.cx, "wrong size %d/%d\n", size.cx, expect.cx );
1875 SetTextJustification(hdc, 4, 1);
1876 GetTextExtentPoint32(hdc, " ", 1, &size);
1877 ok( size.cx == expect.cx + 4, "wrong size %d/%d\n", size.cx, expect.cx );
1878 SetTextJustification(hdc, 9, 2);
1879 GetTextExtentPoint32(hdc, " ", 2, &size);
1880 ok( size.cx == 2 * expect.cx + 9, "wrong size %d/%d\n", size.cx, expect.cx );
1881 SetTextJustification(hdc, 7, 3);
1882 GetTextExtentPoint32(hdc, " ", 3, &size);
1883 ok( size.cx == 3 * expect.cx + 7, "wrong size %d/%d\n", size.cx, expect.cx );
1884 SetTextJustification(hdc, 7, 3);
1885 SetTextCharacterExtra(hdc, 2 );
1886 GetTextExtentPoint32(hdc, " ", 3, &size);
1887 ok( size.cx == 3 * (expect.cx + 2) + 7, "wrong size %d/%d\n", size.cx, expect.cx );
1888 SetTextJustification(hdc, 0, 0);
1889 SetTextCharacterExtra(hdc, 0);
1890 size.cx = size.cy = 1234;
1891 GetTextExtentPoint32(hdc, " ", 0, &size);
1892 ok( size.cx == 0 && size.cy == 0, "wrong size %d,%d\n", size.cx, size.cy );
1893 pGetTextExtentExPointI(hdc, indices, 2, -1, NULL, NULL, &expect);
1894 SetTextJustification(hdc, 5, 1);
1895 pGetTextExtentExPointI(hdc, indices, 2, -1, NULL, NULL, &size);
1896 ok( size.cx == expect.cx + 5, "wrong size %d/%d\n", size.cx, expect.cx );
1897 SetTextJustification(hdc, 0, 0);
1899 SetMapMode( hdc, MM_ANISOTROPIC );
1900 SetWindowExtEx( hdc, 2, 2, NULL );
1901 GetClientRect( hwnd, &clientArea );
1902 DPtoLP( hdc, (POINT *)&clientArea, 2 );
1903 testJustification(hdc, testText, &clientArea);
1905 GetTextExtentPoint32(hdc, "A", 1, &expect);
1906 for (i = 0; i < 10; i++)
1908 SetTextCharacterExtra(hdc, i);
1909 GetTextExtentPoint32(hdc, "A", 1, &size);
1910 ok( size.cx == expect.cx + i, "wrong size %d/%d+%d\n", size.cx, expect.cx, i );
1912 SetTextCharacterExtra(hdc, 0);
1913 pGetTextExtentExPointI(hdc, indices, 1, -1, NULL, NULL, &expect);
1914 for (i = 0; i < 10; i++)
1916 SetTextCharacterExtra(hdc, i);
1917 pGetTextExtentExPointI(hdc, indices, 1, -1, NULL, NULL, &size);
1918 ok( size.cx == expect.cx + i, "wrong size %d/%d+%d\n", size.cx, expect.cx, i );
1920 SetTextCharacterExtra(hdc, 0);
1922 SetViewportExtEx( hdc, 3, 3, NULL );
1923 GetClientRect( hwnd, &clientArea );
1924 DPtoLP( hdc, (POINT *)&clientArea, 2 );
1925 testJustification(hdc, testText, &clientArea);
1927 GetTextExtentPoint32(hdc, "A", 1, &expect);
1928 for (i = 0; i < 10; i++)
1930 SetTextCharacterExtra(hdc, i);
1931 GetTextExtentPoint32(hdc, "A", 1, &size);
1932 ok( size.cx == expect.cx + i, "wrong size %d/%d+%d\n", size.cx, expect.cx, i );
1935 done:
1936 DeleteObject(hfont);
1937 ReleaseDC(hwnd, hdc);
1938 DestroyWindow(hwnd);
1941 static BOOL get_glyph_indices(INT charset, UINT code_page, WORD *idx, UINT count, BOOL unicode)
1943 HDC hdc;
1944 LOGFONTA lf;
1945 HFONT hfont, hfont_old;
1946 CHARSETINFO csi;
1947 FONTSIGNATURE fs;
1948 INT cs;
1949 DWORD i, ret;
1950 char name[64];
1952 assert(count <= 128);
1954 memset(&lf, 0, sizeof(lf));
1956 lf.lfCharSet = charset;
1957 lf.lfHeight = 10;
1958 lstrcpyA(lf.lfFaceName, "Arial");
1959 SetLastError(0xdeadbeef);
1960 hfont = CreateFontIndirectA(&lf);
1961 ok(hfont != 0, "CreateFontIndirectA error %u\n", GetLastError());
1963 hdc = GetDC(0);
1964 hfont_old = SelectObject(hdc, hfont);
1966 cs = GetTextCharsetInfo(hdc, &fs, 0);
1967 ok(cs == charset, "expected %d, got %d\n", charset, cs);
1969 SetLastError(0xdeadbeef);
1970 ret = GetTextFaceA(hdc, sizeof(name), name);
1971 ok(ret, "GetTextFaceA error %u\n", GetLastError());
1973 if (charset == SYMBOL_CHARSET)
1975 ok(strcmp("Arial", name), "face name should NOT be Arial\n");
1976 ok(fs.fsCsb[0] & (1 << 31), "symbol encoding should be available\n");
1978 else
1980 ok(!strcmp("Arial", name), "face name should be Arial, not %s\n", name);
1981 ok(!(fs.fsCsb[0] & (1 << 31)), "symbol encoding should NOT be available\n");
1984 if (!TranslateCharsetInfo((DWORD *)(INT_PTR)cs, &csi, TCI_SRCCHARSET))
1986 trace("Can't find codepage for charset %d\n", cs);
1987 ReleaseDC(0, hdc);
1988 return FALSE;
1990 ok(csi.ciACP == code_page, "expected %d, got %d\n", code_page, csi.ciACP);
1992 if (pGdiGetCodePage != NULL && pGdiGetCodePage(hdc) != code_page)
1994 skip("Font code page %d, looking for code page %d\n",
1995 pGdiGetCodePage(hdc), code_page);
1996 ReleaseDC(0, hdc);
1997 return FALSE;
2000 if (unicode)
2002 char ansi_buf[128];
2003 WCHAR unicode_buf[128];
2005 for (i = 0; i < count; i++) ansi_buf[i] = (BYTE)(i + 128);
2007 MultiByteToWideChar(code_page, 0, ansi_buf, count, unicode_buf, count);
2009 SetLastError(0xdeadbeef);
2010 ret = pGetGlyphIndicesW(hdc, unicode_buf, count, idx, 0);
2011 ok(ret == count, "GetGlyphIndicesW expected %d got %d, error %u\n",
2012 count, ret, GetLastError());
2014 else
2016 char ansi_buf[128];
2018 for (i = 0; i < count; i++) ansi_buf[i] = (BYTE)(i + 128);
2020 SetLastError(0xdeadbeef);
2021 ret = pGetGlyphIndicesA(hdc, ansi_buf, count, idx, 0);
2022 ok(ret == count, "GetGlyphIndicesA expected %d got %d, error %u\n",
2023 count, ret, GetLastError());
2026 SelectObject(hdc, hfont_old);
2027 DeleteObject(hfont);
2029 ReleaseDC(0, hdc);
2031 return TRUE;
2034 static void test_font_charset(void)
2036 static struct charset_data
2038 INT charset;
2039 UINT code_page;
2040 WORD font_idxA[128], font_idxW[128];
2041 } cd[] =
2043 { ANSI_CHARSET, 1252 },
2044 { RUSSIAN_CHARSET, 1251 },
2045 { SYMBOL_CHARSET, CP_SYMBOL } /* keep it as the last one */
2047 int i;
2049 if (!pGetGlyphIndicesA || !pGetGlyphIndicesW)
2051 win_skip("Skipping the font charset test on a Win9x platform\n");
2052 return;
2055 if (!is_font_installed("Arial"))
2057 skip("Arial is not installed\n");
2058 return;
2061 for (i = 0; i < sizeof(cd)/sizeof(cd[0]); i++)
2063 if (cd[i].charset == SYMBOL_CHARSET)
2065 if (!is_font_installed("Symbol") && !is_font_installed("Wingdings"))
2067 skip("Symbol or Wingdings is not installed\n");
2068 break;
2071 if (get_glyph_indices(cd[i].charset, cd[i].code_page, cd[i].font_idxA, 128, FALSE) &&
2072 get_glyph_indices(cd[i].charset, cd[i].code_page, cd[i].font_idxW, 128, TRUE))
2073 ok(!memcmp(cd[i].font_idxA, cd[i].font_idxW, 128*sizeof(WORD)), "%d: indices don't match\n", i);
2076 ok(memcmp(cd[0].font_idxW, cd[1].font_idxW, 128*sizeof(WORD)), "0 vs 1: indices shouldn't match\n");
2077 if (i > 2)
2079 ok(memcmp(cd[0].font_idxW, cd[2].font_idxW, 128*sizeof(WORD)), "0 vs 2: indices shouldn't match\n");
2080 ok(memcmp(cd[1].font_idxW, cd[2].font_idxW, 128*sizeof(WORD)), "1 vs 2: indices shouldn't match\n");
2082 else
2083 skip("Symbol or Wingdings is not installed\n");
2086 static void test_GdiGetCodePage(void)
2088 static const struct _matching_data
2090 UINT current_codepage;
2091 LPCSTR lfFaceName;
2092 UCHAR lfCharSet;
2093 UINT expected_codepage;
2094 } matching_data[] = {
2095 {1251, "Arial", ANSI_CHARSET, 1252},
2096 {1251, "Tahoma", ANSI_CHARSET, 1252},
2098 {1252, "Arial", ANSI_CHARSET, 1252},
2099 {1252, "Tahoma", ANSI_CHARSET, 1252},
2101 {1253, "Arial", ANSI_CHARSET, 1252},
2102 {1253, "Tahoma", ANSI_CHARSET, 1252},
2104 { 932, "Arial", ANSI_CHARSET, 1252}, /* Japanese Windows returns 1252, not 932 */
2105 { 932, "Tahoma", ANSI_CHARSET, 1252},
2106 { 932, "MS UI Gothic", ANSI_CHARSET, 1252},
2108 { 936, "Arial", ANSI_CHARSET, 936},
2109 { 936, "Tahoma", ANSI_CHARSET, 936},
2110 { 936, "Simsun", ANSI_CHARSET, 936},
2112 { 949, "Arial", ANSI_CHARSET, 949},
2113 { 949, "Tahoma", ANSI_CHARSET, 949},
2114 { 949, "Gulim", ANSI_CHARSET, 949},
2116 { 950, "Arial", ANSI_CHARSET, 950},
2117 { 950, "Tahoma", ANSI_CHARSET, 950},
2118 { 950, "PMingLiU", ANSI_CHARSET, 950},
2120 HDC hdc;
2121 LOGFONT lf;
2122 HFONT hfont;
2123 UINT charset, acp;
2124 DWORD codepage;
2125 int i;
2127 if (!pGdiGetCodePage)
2129 skip("GdiGetCodePage not available on this platform\n");
2130 return;
2133 acp = GetACP();
2135 for (i = 0; i < sizeof(matching_data) / sizeof(struct _matching_data); i++)
2137 /* only test data matched current locale codepage */
2138 if (matching_data[i].current_codepage != acp)
2139 continue;
2141 if (!is_font_installed(matching_data[i].lfFaceName))
2143 skip("%s is not installed\n", matching_data[i].lfFaceName);
2144 continue;
2147 hdc = GetDC(0);
2149 memset(&lf, 0, sizeof(lf));
2150 lf.lfHeight = -16;
2151 lf.lfCharSet = matching_data[i].lfCharSet;
2152 lstrcpyA(lf.lfFaceName, matching_data[i].lfFaceName);
2153 hfont = CreateFontIndirectA(&lf);
2154 ok(hfont != 0, "CreateFontIndirectA error %u\n", GetLastError());
2156 hfont = SelectObject(hdc, hfont);
2157 charset = GetTextCharset(hdc);
2158 codepage = pGdiGetCodePage(hdc);
2159 trace("acp=%d, lfFaceName=%s, lfCharSet=%d, GetTextCharset=%d, GdiGetCodePage=%d, expected codepage=%d\n",
2160 acp, lf.lfFaceName, lf.lfCharSet, charset, codepage, matching_data[i].expected_codepage);
2161 ok(codepage == matching_data[i].expected_codepage,
2162 "GdiGetCodePage should have returned %d, got %d\n", matching_data[i].expected_codepage, codepage);
2164 hfont = SelectObject(hdc, hfont);
2165 DeleteObject(hfont);
2166 ReleaseDC(NULL, hdc);
2170 static void test_GetFontUnicodeRanges(void)
2172 LOGFONTA lf;
2173 HDC hdc;
2174 HFONT hfont, hfont_old;
2175 DWORD size;
2176 GLYPHSET *gs;
2177 DWORD i;
2179 if (!pGetFontUnicodeRanges)
2181 win_skip("GetFontUnicodeRanges not available before W2K\n");
2182 return;
2185 memset(&lf, 0, sizeof(lf));
2186 lstrcpyA(lf.lfFaceName, "Arial");
2187 hfont = create_font("Arial", &lf);
2189 hdc = GetDC(0);
2190 hfont_old = SelectObject(hdc, hfont);
2192 size = pGetFontUnicodeRanges(NULL, NULL);
2193 ok(!size, "GetFontUnicodeRanges succeeded unexpectedly\n");
2195 size = pGetFontUnicodeRanges(hdc, NULL);
2196 ok(size, "GetFontUnicodeRanges failed unexpectedly\n");
2198 gs = HeapAlloc(GetProcessHeap(), 0, size);
2200 size = pGetFontUnicodeRanges(hdc, gs);
2201 ok(size, "GetFontUnicodeRanges failed\n");
2203 if (0) /* Disabled to limit console spam */
2204 for (i = 0; i < gs->cRanges; i++)
2205 trace("%03d wcLow %04x cGlyphs %u\n", i, gs->ranges[i].wcLow, gs->ranges[i].cGlyphs);
2206 trace("found %u ranges\n", gs->cRanges);
2208 HeapFree(GetProcessHeap(), 0, gs);
2210 SelectObject(hdc, hfont_old);
2211 DeleteObject(hfont);
2212 ReleaseDC(NULL, hdc);
2215 #define MAX_ENUM_FONTS 4096
2217 struct enum_font_data
2219 int total;
2220 LOGFONT lf[MAX_ENUM_FONTS];
2223 struct enum_fullname_data
2225 int total;
2226 ENUMLOGFONT elf[MAX_ENUM_FONTS];
2229 struct enum_font_dataW
2231 int total;
2232 LOGFONTW lf[MAX_ENUM_FONTS];
2235 static INT CALLBACK arial_enum_proc(const LOGFONT *lf, const TEXTMETRIC *tm, DWORD type, LPARAM lParam)
2237 struct enum_font_data *efd = (struct enum_font_data *)lParam;
2238 const NEWTEXTMETRIC *ntm = (const NEWTEXTMETRIC *)tm;
2240 ok(lf->lfHeight == tm->tmHeight, "lfHeight %d != tmHeight %d\n", lf->lfHeight, tm->tmHeight);
2241 ok(lf->lfHeight > 0 && lf->lfHeight < 200, "enumerated font height %d\n", lf->lfHeight);
2243 if (type != TRUETYPE_FONTTYPE) return 1;
2245 ok(ntm->ntmCellHeight + ntm->ntmCellHeight/5 >= ntm->ntmSizeEM, "ntmCellHeight %d should be close to ntmSizeEM %d\n", ntm->ntmCellHeight, ntm->ntmSizeEM);
2247 if (0) /* Disabled to limit console spam */
2248 trace("enumed font \"%s\", charset %d, height %d, weight %d, italic %d\n",
2249 lf->lfFaceName, lf->lfCharSet, lf->lfHeight, lf->lfWeight, lf->lfItalic);
2250 if (efd->total < MAX_ENUM_FONTS)
2251 efd->lf[efd->total++] = *lf;
2252 else
2253 trace("enum tests invalid; you have more than %d fonts\n", MAX_ENUM_FONTS);
2255 return 1;
2258 static INT CALLBACK arial_enum_procw(const LOGFONTW *lf, const TEXTMETRICW *tm, DWORD type, LPARAM lParam)
2260 struct enum_font_dataW *efd = (struct enum_font_dataW *)lParam;
2261 const NEWTEXTMETRICW *ntm = (const NEWTEXTMETRICW *)tm;
2263 ok(lf->lfHeight == tm->tmHeight, "lfHeight %d != tmHeight %d\n", lf->lfHeight, tm->tmHeight);
2264 ok(lf->lfHeight > 0 && lf->lfHeight < 200, "enumerated font height %d\n", lf->lfHeight);
2266 if (type != TRUETYPE_FONTTYPE) return 1;
2268 ok(ntm->ntmCellHeight + ntm->ntmCellHeight/5 >= ntm->ntmSizeEM, "ntmCellHeight %d should be close to ntmSizeEM %d\n", ntm->ntmCellHeight, ntm->ntmSizeEM);
2270 if (0) /* Disabled to limit console spam */
2271 trace("enumed font %s, charset %d, height %d, weight %d, italic %d\n",
2272 wine_dbgstr_w(lf->lfFaceName), lf->lfCharSet, lf->lfHeight, lf->lfWeight, lf->lfItalic);
2273 if (efd->total < MAX_ENUM_FONTS)
2274 efd->lf[efd->total++] = *lf;
2275 else
2276 trace("enum tests invalid; you have more than %d fonts\n", MAX_ENUM_FONTS);
2278 return 1;
2281 static void get_charset_stats(struct enum_font_data *efd,
2282 int *ansi_charset, int *symbol_charset,
2283 int *russian_charset)
2285 int i;
2287 *ansi_charset = 0;
2288 *symbol_charset = 0;
2289 *russian_charset = 0;
2291 for (i = 0; i < efd->total; i++)
2293 switch (efd->lf[i].lfCharSet)
2295 case ANSI_CHARSET:
2296 (*ansi_charset)++;
2297 break;
2298 case SYMBOL_CHARSET:
2299 (*symbol_charset)++;
2300 break;
2301 case RUSSIAN_CHARSET:
2302 (*russian_charset)++;
2303 break;
2308 static void get_charset_statsW(struct enum_font_dataW *efd,
2309 int *ansi_charset, int *symbol_charset,
2310 int *russian_charset)
2312 int i;
2314 *ansi_charset = 0;
2315 *symbol_charset = 0;
2316 *russian_charset = 0;
2318 for (i = 0; i < efd->total; i++)
2320 switch (efd->lf[i].lfCharSet)
2322 case ANSI_CHARSET:
2323 (*ansi_charset)++;
2324 break;
2325 case SYMBOL_CHARSET:
2326 (*symbol_charset)++;
2327 break;
2328 case RUSSIAN_CHARSET:
2329 (*russian_charset)++;
2330 break;
2335 static void test_EnumFontFamilies(const char *font_name, INT font_charset)
2337 struct enum_font_data efd;
2338 struct enum_font_dataW efdw;
2339 LOGFONT lf;
2340 HDC hdc;
2341 int i, ret, ansi_charset, symbol_charset, russian_charset;
2343 trace("Testing font %s, charset %d\n", *font_name ? font_name : "<empty>", font_charset);
2345 if (*font_name && !is_truetype_font_installed(font_name))
2347 skip("%s is not installed\n", font_name);
2348 return;
2351 hdc = GetDC(0);
2353 /* Observed behaviour: EnumFontFamilies enumerates aliases like "Arial Cyr"
2354 * while EnumFontFamiliesEx doesn't.
2356 if (!*font_name && font_charset == DEFAULT_CHARSET) /* do it only once */
2359 * Use EnumFontFamiliesW since win98 crashes when the
2360 * second parameter is NULL using EnumFontFamilies
2362 efdw.total = 0;
2363 SetLastError(0xdeadbeef);
2364 ret = EnumFontFamiliesW(hdc, NULL, arial_enum_procw, (LPARAM)&efdw);
2365 ok(ret || GetLastError() == ERROR_CALL_NOT_IMPLEMENTED, "EnumFontFamiliesW error %u\n", GetLastError());
2366 if(ret)
2368 get_charset_statsW(&efdw, &ansi_charset, &symbol_charset, &russian_charset);
2369 trace("enumerated ansi %d, symbol %d, russian %d fonts for NULL\n",
2370 ansi_charset, symbol_charset, russian_charset);
2371 ok(efdw.total > 0, "fonts enumerated: NULL\n");
2372 ok(ansi_charset > 0, "NULL family should enumerate ANSI_CHARSET\n");
2373 ok(symbol_charset > 0, "NULL family should enumerate SYMBOL_CHARSET\n");
2374 ok(russian_charset > 0 ||
2375 broken(russian_charset == 0), /* NT4 */
2376 "NULL family should enumerate RUSSIAN_CHARSET\n");
2379 efdw.total = 0;
2380 SetLastError(0xdeadbeef);
2381 ret = EnumFontFamiliesExW(hdc, NULL, arial_enum_procw, (LPARAM)&efdw, 0);
2382 ok(ret || GetLastError() == ERROR_CALL_NOT_IMPLEMENTED, "EnumFontFamiliesExW error %u\n", GetLastError());
2383 if(ret)
2385 get_charset_statsW(&efdw, &ansi_charset, &symbol_charset, &russian_charset);
2386 trace("enumerated ansi %d, symbol %d, russian %d fonts for NULL\n",
2387 ansi_charset, symbol_charset, russian_charset);
2388 ok(efdw.total > 0, "fonts enumerated: NULL\n");
2389 ok(ansi_charset > 0, "NULL family should enumerate ANSI_CHARSET\n");
2390 ok(symbol_charset > 0, "NULL family should enumerate SYMBOL_CHARSET\n");
2391 ok(russian_charset > 0, "NULL family should enumerate RUSSIAN_CHARSET\n");
2395 efd.total = 0;
2396 SetLastError(0xdeadbeef);
2397 ret = EnumFontFamilies(hdc, font_name, arial_enum_proc, (LPARAM)&efd);
2398 ok(ret, "EnumFontFamilies error %u\n", GetLastError());
2399 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
2400 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s\n",
2401 ansi_charset, symbol_charset, russian_charset,
2402 *font_name ? font_name : "<empty>");
2403 if (*font_name)
2404 ok(efd.total > 0, "no fonts enumerated: %s\n", font_name);
2405 else
2406 ok(!efd.total, "no fonts should be enumerated for empty font_name\n");
2407 for (i = 0; i < efd.total; i++)
2409 /* FIXME: remove completely once Wine is fixed */
2410 if (efd.lf[i].lfCharSet != font_charset)
2412 todo_wine
2413 ok(efd.lf[i].lfCharSet == font_charset, "%d: got charset %d\n", i, efd.lf[i].lfCharSet);
2415 else
2416 ok(efd.lf[i].lfCharSet == font_charset, "%d: got charset %d\n", i, efd.lf[i].lfCharSet);
2417 ok(!lstrcmp(efd.lf[i].lfFaceName, font_name), "expected %s, got %s\n",
2418 font_name, efd.lf[i].lfFaceName);
2421 memset(&lf, 0, sizeof(lf));
2422 lf.lfCharSet = ANSI_CHARSET;
2423 lstrcpy(lf.lfFaceName, font_name);
2424 efd.total = 0;
2425 SetLastError(0xdeadbeef);
2426 ret = EnumFontFamiliesEx(hdc, &lf, arial_enum_proc, (LPARAM)&efd, 0);
2427 ok(ret, "EnumFontFamiliesEx error %u\n", GetLastError());
2428 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
2429 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s ANSI_CHARSET\n",
2430 ansi_charset, symbol_charset, russian_charset,
2431 *font_name ? font_name : "<empty>");
2432 if (font_charset == SYMBOL_CHARSET)
2434 if (*font_name)
2435 ok(efd.total == 0, "no fonts should be enumerated: %s ANSI_CHARSET\n", font_name);
2436 else
2437 ok(efd.total > 0, "no fonts enumerated: %s\n", font_name);
2439 else
2441 ok(efd.total > 0, "no fonts enumerated: %s ANSI_CHARSET\n", font_name);
2442 for (i = 0; i < efd.total; i++)
2444 ok(efd.lf[i].lfCharSet == ANSI_CHARSET, "%d: got charset %d\n", i, efd.lf[i].lfCharSet);
2445 if (*font_name)
2446 ok(!lstrcmp(efd.lf[i].lfFaceName, font_name), "expected %s, got %s\n",
2447 font_name, efd.lf[i].lfFaceName);
2451 /* DEFAULT_CHARSET should enumerate all available charsets */
2452 memset(&lf, 0, sizeof(lf));
2453 lf.lfCharSet = DEFAULT_CHARSET;
2454 lstrcpy(lf.lfFaceName, font_name);
2455 efd.total = 0;
2456 SetLastError(0xdeadbeef);
2457 EnumFontFamiliesEx(hdc, &lf, arial_enum_proc, (LPARAM)&efd, 0);
2458 ok(ret, "EnumFontFamiliesEx error %u\n", GetLastError());
2459 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
2460 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s DEFAULT_CHARSET\n",
2461 ansi_charset, symbol_charset, russian_charset,
2462 *font_name ? font_name : "<empty>");
2463 ok(efd.total > 0, "no fonts enumerated: %s DEFAULT_CHARSET\n", font_name);
2464 for (i = 0; i < efd.total; i++)
2466 if (*font_name)
2467 ok(!lstrcmp(efd.lf[i].lfFaceName, font_name), "expected %s, got %s\n",
2468 font_name, efd.lf[i].lfFaceName);
2470 if (*font_name)
2472 switch (font_charset)
2474 case ANSI_CHARSET:
2475 ok(ansi_charset > 0,
2476 "ANSI_CHARSET should enumerate ANSI_CHARSET for %s\n", font_name);
2477 ok(!symbol_charset,
2478 "ANSI_CHARSET should NOT enumerate SYMBOL_CHARSET for %s\n", font_name);
2479 ok(russian_charset > 0,
2480 "ANSI_CHARSET should enumerate RUSSIAN_CHARSET for %s\n", font_name);
2481 break;
2482 case SYMBOL_CHARSET:
2483 ok(!ansi_charset,
2484 "SYMBOL_CHARSET should NOT enumerate ANSI_CHARSET for %s\n", font_name);
2485 ok(symbol_charset,
2486 "SYMBOL_CHARSET should enumerate SYMBOL_CHARSET for %s\n", font_name);
2487 ok(!russian_charset,
2488 "SYMBOL_CHARSET should NOT enumerate RUSSIAN_CHARSET for %s\n", font_name);
2489 break;
2490 case DEFAULT_CHARSET:
2491 ok(ansi_charset > 0,
2492 "DEFAULT_CHARSET should enumerate ANSI_CHARSET for %s\n", font_name);
2493 ok(symbol_charset > 0,
2494 "DEFAULT_CHARSET should enumerate SYMBOL_CHARSET for %s\n", font_name);
2495 ok(russian_charset > 0,
2496 "DEFAULT_CHARSET should enumerate RUSSIAN_CHARSET for %s\n", font_name);
2497 break;
2500 else
2502 ok(ansi_charset > 0,
2503 "DEFAULT_CHARSET should enumerate ANSI_CHARSET for %s\n", *font_name ? font_name : "<empty>");
2504 ok(symbol_charset > 0,
2505 "DEFAULT_CHARSET should enumerate SYMBOL_CHARSET for %s\n", *font_name ? font_name : "<empty>");
2506 ok(russian_charset > 0,
2507 "DEFAULT_CHARSET should enumerate RUSSIAN_CHARSET for %s\n", *font_name ? font_name : "<empty>");
2510 memset(&lf, 0, sizeof(lf));
2511 lf.lfCharSet = SYMBOL_CHARSET;
2512 lstrcpy(lf.lfFaceName, font_name);
2513 efd.total = 0;
2514 SetLastError(0xdeadbeef);
2515 EnumFontFamiliesEx(hdc, &lf, arial_enum_proc, (LPARAM)&efd, 0);
2516 ok(ret, "EnumFontFamiliesEx error %u\n", GetLastError());
2517 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
2518 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s SYMBOL_CHARSET\n",
2519 ansi_charset, symbol_charset, russian_charset,
2520 *font_name ? font_name : "<empty>");
2521 if (*font_name && font_charset == ANSI_CHARSET)
2522 ok(efd.total == 0, "no fonts should be enumerated: %s SYMBOL_CHARSET\n", font_name);
2523 else
2525 ok(efd.total > 0, "no fonts enumerated: %s SYMBOL_CHARSET\n", font_name);
2526 for (i = 0; i < efd.total; i++)
2528 ok(efd.lf[i].lfCharSet == SYMBOL_CHARSET, "%d: got charset %d\n", i, efd.lf[i].lfCharSet);
2529 if (*font_name)
2530 ok(!lstrcmp(efd.lf[i].lfFaceName, font_name), "expected %s, got %s\n",
2531 font_name, efd.lf[i].lfFaceName);
2534 ok(!ansi_charset,
2535 "SYMBOL_CHARSET should NOT enumerate ANSI_CHARSET for %s\n", *font_name ? font_name : "<empty>");
2536 ok(symbol_charset > 0,
2537 "SYMBOL_CHARSET should enumerate SYMBOL_CHARSET for %s\n", *font_name ? font_name : "<empty>");
2538 ok(!russian_charset,
2539 "SYMBOL_CHARSET should NOT enumerate RUSSIAN_CHARSET for %s\n", *font_name ? font_name : "<empty>");
2542 ReleaseDC(0, hdc);
2545 static INT CALLBACK enum_font_data_proc(const LOGFONT *lf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
2547 struct enum_font_data *efd = (struct enum_font_data *)lParam;
2549 if (type != TRUETYPE_FONTTYPE) return 1;
2551 if (efd->total < MAX_ENUM_FONTS)
2552 efd->lf[efd->total++] = *lf;
2553 else
2554 trace("enum tests invalid; you have more than %d fonts\n", MAX_ENUM_FONTS);
2556 return 1;
2559 static INT CALLBACK enum_fullname_data_proc(const LOGFONT *lf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
2561 struct enum_fullname_data *efnd = (struct enum_fullname_data *)lParam;
2563 if (type != TRUETYPE_FONTTYPE) return 1;
2565 if (efnd->total < MAX_ENUM_FONTS)
2566 efnd->elf[efnd->total++] = *(ENUMLOGFONT*)lf;
2567 else
2568 trace("enum tests invalid; you have more than %d fonts\n", MAX_ENUM_FONTS);
2570 return 1;
2573 static void test_EnumFontFamiliesEx_default_charset(void)
2575 struct enum_font_data efd;
2576 LOGFONT gui_font, enum_font;
2577 DWORD ret;
2578 HDC hdc;
2580 ret = GetObject(GetStockObject(DEFAULT_GUI_FONT), sizeof(gui_font), &gui_font);
2581 ok(ret, "GetObject failed.\n");
2582 if (!ret)
2583 return;
2585 efd.total = 0;
2587 hdc = GetDC(0);
2588 memset(&enum_font, 0, sizeof(enum_font));
2589 lstrcpy(enum_font.lfFaceName, gui_font.lfFaceName);
2590 enum_font.lfCharSet = DEFAULT_CHARSET;
2591 EnumFontFamiliesEx(hdc, &enum_font, enum_font_data_proc, (LPARAM)&efd, 0);
2592 ReleaseDC(0, hdc);
2594 if (efd.total == 0) {
2595 skip("'%s' is not found or not a TrueType font.\n", gui_font.lfFaceName);
2596 return;
2598 trace("'%s' has %d charsets.\n", gui_font.lfFaceName, efd.total);
2600 ok(efd.lf[0].lfCharSet == gui_font.lfCharSet || broken(system_lang_id == LANG_ARABIC),
2601 "(%s) got charset %d expected %d\n",
2602 efd.lf[0].lfFaceName, efd.lf[0].lfCharSet, gui_font.lfCharSet);
2604 return;
2607 static void test_negative_width(HDC hdc, const LOGFONTA *lf)
2609 HFONT hfont, hfont_prev;
2610 DWORD ret;
2611 GLYPHMETRICS gm1, gm2;
2612 LOGFONTA lf2 = *lf;
2613 WORD idx;
2615 if(!pGetGlyphIndicesA)
2616 return;
2618 /* negative widths are handled just as positive ones */
2619 lf2.lfWidth = -lf->lfWidth;
2621 SetLastError(0xdeadbeef);
2622 hfont = CreateFontIndirectA(lf);
2623 ok(hfont != 0, "CreateFontIndirect error %u\n", GetLastError());
2624 check_font("original", lf, hfont);
2626 hfont_prev = SelectObject(hdc, hfont);
2628 ret = pGetGlyphIndicesA(hdc, "x", 1, &idx, GGI_MARK_NONEXISTING_GLYPHS);
2629 if (ret == GDI_ERROR || idx == 0xffff)
2631 SelectObject(hdc, hfont_prev);
2632 DeleteObject(hfont);
2633 skip("Font %s doesn't contain 'x', skipping the test\n", lf->lfFaceName);
2634 return;
2637 /* filling with 0xaa causes false pass under WINEDEBUG=warn+heap */
2638 memset(&gm1, 0xab, sizeof(gm1));
2639 SetLastError(0xdeadbeef);
2640 ret = GetGlyphOutlineA(hdc, 'x', GGO_METRICS, &gm1, 0, NULL, &mat);
2641 ok(ret != GDI_ERROR, "GetGlyphOutline error 0x%x\n", GetLastError());
2643 SelectObject(hdc, hfont_prev);
2644 DeleteObject(hfont);
2646 SetLastError(0xdeadbeef);
2647 hfont = CreateFontIndirectA(&lf2);
2648 ok(hfont != 0, "CreateFontIndirect error %u\n", GetLastError());
2649 check_font("negative width", &lf2, hfont);
2651 hfont_prev = SelectObject(hdc, hfont);
2653 memset(&gm2, 0xbb, sizeof(gm2));
2654 SetLastError(0xdeadbeef);
2655 ret = GetGlyphOutlineA(hdc, 'x', GGO_METRICS, &gm2, 0, NULL, &mat);
2656 ok(ret != GDI_ERROR, "GetGlyphOutline error 0x%x\n", GetLastError());
2658 SelectObject(hdc, hfont_prev);
2659 DeleteObject(hfont);
2661 ok(gm1.gmBlackBoxX == gm2.gmBlackBoxX &&
2662 gm1.gmBlackBoxY == gm2.gmBlackBoxY &&
2663 gm1.gmptGlyphOrigin.x == gm2.gmptGlyphOrigin.x &&
2664 gm1.gmptGlyphOrigin.y == gm2.gmptGlyphOrigin.y &&
2665 gm1.gmCellIncX == gm2.gmCellIncX &&
2666 gm1.gmCellIncY == gm2.gmCellIncY,
2667 "gm1=%d,%d,%d,%d,%d,%d gm2=%d,%d,%d,%d,%d,%d\n",
2668 gm1.gmBlackBoxX, gm1.gmBlackBoxY, gm1.gmptGlyphOrigin.x,
2669 gm1.gmptGlyphOrigin.y, gm1.gmCellIncX, gm1.gmCellIncY,
2670 gm2.gmBlackBoxX, gm2.gmBlackBoxY, gm2.gmptGlyphOrigin.x,
2671 gm2.gmptGlyphOrigin.y, gm2.gmCellIncX, gm2.gmCellIncY);
2674 /* PANOSE is 10 bytes in size, need to pack the structure properly */
2675 #include "pshpack2.h"
2676 typedef struct
2678 USHORT version;
2679 SHORT xAvgCharWidth;
2680 USHORT usWeightClass;
2681 USHORT usWidthClass;
2682 SHORT fsType;
2683 SHORT ySubscriptXSize;
2684 SHORT ySubscriptYSize;
2685 SHORT ySubscriptXOffset;
2686 SHORT ySubscriptYOffset;
2687 SHORT ySuperscriptXSize;
2688 SHORT ySuperscriptYSize;
2689 SHORT ySuperscriptXOffset;
2690 SHORT ySuperscriptYOffset;
2691 SHORT yStrikeoutSize;
2692 SHORT yStrikeoutPosition;
2693 SHORT sFamilyClass;
2694 PANOSE panose;
2695 ULONG ulUnicodeRange1;
2696 ULONG ulUnicodeRange2;
2697 ULONG ulUnicodeRange3;
2698 ULONG ulUnicodeRange4;
2699 CHAR achVendID[4];
2700 USHORT fsSelection;
2701 USHORT usFirstCharIndex;
2702 USHORT usLastCharIndex;
2703 /* According to the Apple spec, original version didn't have the below fields,
2704 * version numbers were taken from the OpenType spec.
2706 /* version 0 (TrueType 1.5) */
2707 USHORT sTypoAscender;
2708 USHORT sTypoDescender;
2709 USHORT sTypoLineGap;
2710 USHORT usWinAscent;
2711 USHORT usWinDescent;
2712 /* version 1 (TrueType 1.66) */
2713 ULONG ulCodePageRange1;
2714 ULONG ulCodePageRange2;
2715 /* version 2 (OpenType 1.2) */
2716 SHORT sxHeight;
2717 SHORT sCapHeight;
2718 USHORT usDefaultChar;
2719 USHORT usBreakChar;
2720 USHORT usMaxContext;
2721 } TT_OS2_V2;
2722 #include "poppack.h"
2724 #ifdef WORDS_BIGENDIAN
2725 #define GET_BE_WORD(x) (x)
2726 #define GET_BE_DWORD(x) (x)
2727 #else
2728 #define GET_BE_WORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
2729 #define GET_BE_DWORD(x) MAKELONG(GET_BE_WORD(HIWORD(x)), GET_BE_WORD(LOWORD(x)));
2730 #endif
2732 #define MS_MAKE_TAG(ch0, ch1, ch2, ch3) \
2733 ((DWORD)(BYTE)(ch0) | ((DWORD)(BYTE)(ch1) << 8) | \
2734 ((DWORD)(BYTE)(ch2) << 16) | ((DWORD)(BYTE)(ch3) << 24))
2735 #define MS_OS2_TAG MS_MAKE_TAG('O','S','/','2')
2736 #define MS_CMAP_TAG MS_MAKE_TAG('c','m','a','p')
2737 #define MS_NAME_TAG MS_MAKE_TAG('n','a','m','e')
2739 typedef struct
2741 USHORT version;
2742 USHORT num_tables;
2743 } cmap_header;
2745 typedef struct
2747 USHORT plat_id;
2748 USHORT enc_id;
2749 ULONG offset;
2750 } cmap_encoding_record;
2752 typedef struct
2754 USHORT format;
2755 USHORT length;
2756 USHORT language;
2758 BYTE glyph_ids[256];
2759 } cmap_format_0;
2761 typedef struct
2763 USHORT format;
2764 USHORT length;
2765 USHORT language;
2767 USHORT seg_countx2;
2768 USHORT search_range;
2769 USHORT entry_selector;
2770 USHORT range_shift;
2772 USHORT end_count[1]; /* this is a variable-sized array of length seg_countx2 / 2 */
2773 /* Then follows:
2774 USHORT pad;
2775 USHORT start_count[seg_countx2 / 2];
2776 USHORT id_delta[seg_countx2 / 2];
2777 USHORT id_range_offset[seg_countx2 / 2];
2778 USHORT glyph_ids[];
2780 } cmap_format_4;
2782 typedef struct
2784 USHORT end_count;
2785 USHORT start_count;
2786 USHORT id_delta;
2787 USHORT id_range_offset;
2788 } cmap_format_4_seg;
2790 static void expect_ff(const TEXTMETRICA *tmA, const TT_OS2_V2 *os2, WORD family, const char *name)
2792 ok((tmA->tmPitchAndFamily & 0xf0) == family ||
2793 broken(PRIMARYLANGID(GetSystemDefaultLangID()) != LANG_ENGLISH),
2794 "%s: expected family %02x got %02x. panose %d-%d-%d-%d-...\n",
2795 name, family, tmA->tmPitchAndFamily, os2->panose.bFamilyType, os2->panose.bSerifStyle,
2796 os2->panose.bWeight, os2->panose.bProportion);
2799 static BOOL get_first_last_from_cmap0(void *ptr, DWORD *first, DWORD *last)
2801 int i;
2802 cmap_format_0 *cmap = (cmap_format_0*)ptr;
2804 *first = 256;
2806 for(i = 0; i < 256; i++)
2808 if(cmap->glyph_ids[i] == 0) continue;
2809 *last = i;
2810 if(*first == 256) *first = i;
2812 if(*first == 256) return FALSE;
2813 return TRUE;
2816 static void get_seg4(cmap_format_4 *cmap, USHORT seg_num, cmap_format_4_seg *seg)
2818 USHORT segs = GET_BE_WORD(cmap->seg_countx2) / 2;
2819 seg->end_count = GET_BE_WORD(cmap->end_count[seg_num]);
2820 seg->start_count = GET_BE_WORD(cmap->end_count[segs + 1 + seg_num]);
2821 seg->id_delta = GET_BE_WORD(cmap->end_count[2 * segs + 1 + seg_num]);
2822 seg->id_range_offset = GET_BE_WORD(cmap->end_count[3 * segs + 1 + seg_num]);
2825 static BOOL get_first_last_from_cmap4(void *ptr, DWORD *first, DWORD *last, DWORD limit)
2827 int i;
2828 cmap_format_4 *cmap = (cmap_format_4*)ptr;
2829 USHORT seg_count = GET_BE_WORD(cmap->seg_countx2) / 2;
2830 USHORT const *glyph_ids = cmap->end_count + 4 * seg_count + 1;
2832 *first = 0x10000;
2834 for(i = 0; i < seg_count; i++)
2836 DWORD code, index;
2837 cmap_format_4_seg seg;
2839 get_seg4(cmap, i, &seg);
2840 for(code = seg.start_count; code <= seg.end_count; code++)
2842 if(seg.id_range_offset == 0)
2843 index = (seg.id_delta + code) & 0xffff;
2844 else
2846 index = seg.id_range_offset / 2
2847 + code - seg.start_count
2848 + i - seg_count;
2850 /* some fonts have broken last segment */
2851 if ((char *)(glyph_ids + index + 1) < (char *)ptr + limit)
2852 index = GET_BE_WORD(glyph_ids[index]);
2853 else
2855 trace("segment %04x/%04x index %04x points to nowhere\n",
2856 seg.start_count, seg.end_count, index);
2857 index = 0;
2859 if(index) index += seg.id_delta;
2861 if(*first == 0x10000)
2862 *last = *first = code;
2863 else if(index)
2864 *last = code;
2868 if(*first == 0x10000) return FALSE;
2869 return TRUE;
2872 static void *get_cmap(cmap_header *header, USHORT plat_id, USHORT enc_id)
2874 USHORT i;
2875 cmap_encoding_record *record = (cmap_encoding_record *)(header + 1);
2877 for(i = 0; i < GET_BE_WORD(header->num_tables); i++)
2879 if(GET_BE_WORD(record->plat_id) == plat_id && GET_BE_WORD(record->enc_id) == enc_id)
2880 return (BYTE *)header + GET_BE_DWORD(record->offset);
2881 record++;
2883 return NULL;
2886 typedef enum
2888 cmap_none,
2889 cmap_ms_unicode,
2890 cmap_ms_symbol
2891 } cmap_type;
2893 static BOOL get_first_last_from_cmap(HDC hdc, DWORD *first, DWORD *last, cmap_type *cmap_type)
2895 LONG size, ret;
2896 cmap_header *header;
2897 void *cmap;
2898 BOOL r = FALSE;
2899 WORD format;
2901 size = GetFontData(hdc, MS_CMAP_TAG, 0, NULL, 0);
2902 ok(size != GDI_ERROR, "no cmap table found\n");
2903 if(size == GDI_ERROR) return FALSE;
2905 header = HeapAlloc(GetProcessHeap(), 0, size);
2906 ret = GetFontData(hdc, MS_CMAP_TAG, 0, header, size);
2907 ok(ret == size, "GetFontData should return %u not %u\n", size, ret);
2908 ok(GET_BE_WORD(header->version) == 0, "got cmap version %d\n", GET_BE_WORD(header->version));
2910 cmap = get_cmap(header, 3, 1);
2911 if(cmap)
2912 *cmap_type = cmap_ms_unicode;
2913 else
2915 cmap = get_cmap(header, 3, 0);
2916 if(cmap) *cmap_type = cmap_ms_symbol;
2918 if(!cmap)
2920 *cmap_type = cmap_none;
2921 goto end;
2924 format = GET_BE_WORD(*(WORD *)cmap);
2925 switch(format)
2927 case 0:
2928 r = get_first_last_from_cmap0(cmap, first, last);
2929 break;
2930 case 4:
2931 r = get_first_last_from_cmap4(cmap, first, last, size);
2932 break;
2933 default:
2934 trace("unhandled cmap format %d\n", format);
2935 break;
2938 end:
2939 HeapFree(GetProcessHeap(), 0, header);
2940 return r;
2943 #define TT_PLATFORM_MICROSOFT 3
2944 #define TT_MS_ID_SYMBOL_CS 0
2945 #define TT_MS_ID_UNICODE_CS 1
2946 #define TT_MS_LANGID_ENGLISH_UNITED_STATES 0x0409
2947 #define TT_NAME_ID_FONT_FAMILY 1
2948 #define TT_NAME_ID_FONT_SUBFAMILY 2
2949 #define TT_NAME_ID_UNIQUE_ID 3
2950 #define TT_NAME_ID_FULL_NAME 4
2952 static BOOL get_ttf_nametable_entry(HDC hdc, WORD name_id, WCHAR *out_buf, SIZE_T out_size, LCID language_id)
2954 struct sfnt_name_header
2956 USHORT format;
2957 USHORT number_of_record;
2958 USHORT storage_offset;
2959 } *header;
2960 struct sfnt_name
2962 USHORT platform_id;
2963 USHORT encoding_id;
2964 USHORT language_id;
2965 USHORT name_id;
2966 USHORT length;
2967 USHORT offset;
2968 } *entry;
2969 BOOL r = FALSE;
2970 LONG size, offset, length;
2971 LONG c, ret;
2972 WCHAR *name;
2973 BYTE *data;
2974 USHORT i;
2976 size = GetFontData(hdc, MS_NAME_TAG, 0, NULL, 0);
2977 ok(size != GDI_ERROR, "no name table found\n");
2978 if(size == GDI_ERROR) return FALSE;
2980 data = HeapAlloc(GetProcessHeap(), 0, size);
2981 ret = GetFontData(hdc, MS_NAME_TAG, 0, data, size);
2982 ok(ret == size, "GetFontData should return %u not %u\n", size, ret);
2984 header = (void *)data;
2985 header->format = GET_BE_WORD(header->format);
2986 header->number_of_record = GET_BE_WORD(header->number_of_record);
2987 header->storage_offset = GET_BE_WORD(header->storage_offset);
2988 if (header->format != 0)
2990 trace("got format %u\n", header->format);
2991 goto out;
2993 if (header->number_of_record == 0 || sizeof(*header) + header->number_of_record * sizeof(*entry) > size)
2995 trace("number records out of range: %d\n", header->number_of_record);
2996 goto out;
2998 if (header->storage_offset >= size)
3000 trace("storage_offset %u > size %u\n", header->storage_offset, size);
3001 goto out;
3004 entry = (void *)&header[1];
3005 for (i = 0; i < header->number_of_record; i++)
3007 if (GET_BE_WORD(entry[i].platform_id) != TT_PLATFORM_MICROSOFT ||
3008 (GET_BE_WORD(entry[i].encoding_id) != TT_MS_ID_UNICODE_CS && GET_BE_WORD(entry[i].encoding_id) != TT_MS_ID_SYMBOL_CS) ||
3009 GET_BE_WORD(entry[i].language_id) != language_id ||
3010 GET_BE_WORD(entry[i].name_id) != name_id)
3012 continue;
3015 offset = header->storage_offset + GET_BE_WORD(entry[i].offset);
3016 length = GET_BE_WORD(entry[i].length);
3017 if (offset + length > size)
3019 trace("entry %d is out of range\n", i);
3020 break;
3022 if (length >= out_size)
3024 trace("buffer too small for entry %d\n", i);
3025 break;
3028 name = (WCHAR *)(data + offset);
3029 for (c = 0; c < length / 2; c++)
3030 out_buf[c] = GET_BE_WORD(name[c]);
3031 out_buf[c] = 0;
3033 r = TRUE;
3034 break;
3037 out:
3038 HeapFree(GetProcessHeap(), 0, data);
3039 return r;
3042 static void test_text_metrics(const LOGFONT *lf, const NEWTEXTMETRIC *ntm)
3044 HDC hdc;
3045 HFONT hfont, hfont_old;
3046 TEXTMETRICA tmA;
3047 TT_OS2_V2 tt_os2;
3048 LONG size, ret;
3049 const char *font_name = lf->lfFaceName;
3050 DWORD cmap_first = 0, cmap_last = 0;
3051 UINT ascent, descent, cell_height;
3052 cmap_type cmap_type;
3053 BOOL sys_lang_non_english;
3055 sys_lang_non_english = PRIMARYLANGID(GetSystemDefaultLangID()) != LANG_ENGLISH;
3056 hdc = GetDC(0);
3058 SetLastError(0xdeadbeef);
3059 hfont = CreateFontIndirectA(lf);
3060 ok(hfont != 0, "CreateFontIndirect error %u\n", GetLastError());
3062 hfont_old = SelectObject(hdc, hfont);
3064 size = GetFontData(hdc, MS_OS2_TAG, 0, NULL, 0);
3065 if (size == GDI_ERROR)
3067 trace("OS/2 chunk was not found\n");
3068 goto end_of_test;
3070 if (size > sizeof(tt_os2))
3072 trace("got too large OS/2 chunk of size %u\n", size);
3073 size = sizeof(tt_os2);
3076 memset(&tt_os2, 0, sizeof(tt_os2));
3077 ret = GetFontData(hdc, MS_OS2_TAG, 0, &tt_os2, size);
3078 ok(ret == size, "GetFontData should return %u not %u\n", size, ret);
3080 ascent = GET_BE_WORD(tt_os2.usWinAscent);
3081 descent = GET_BE_WORD(tt_os2.usWinDescent);
3082 cell_height = ascent + descent;
3083 ok(ntm->ntmCellHeight == cell_height, "%s: ntmCellHeight %u != %u, os2.usWinAscent/os2.usWinDescent %u/%u\n",
3084 font_name, ntm->ntmCellHeight, cell_height, ascent, descent);
3086 SetLastError(0xdeadbeef);
3087 ret = GetTextMetricsA(hdc, &tmA);
3088 ok(ret, "GetTextMetricsA error %u\n", GetLastError());
3090 if(!get_first_last_from_cmap(hdc, &cmap_first, &cmap_last, &cmap_type))
3092 skip("Unable to retrieve first and last glyphs from cmap\n");
3094 else
3096 USHORT expect_first_A, expect_last_A, expect_break_A, expect_default_A;
3097 USHORT expect_first_W, expect_last_W, expect_break_W, expect_default_W;
3098 UINT os2_first_char, os2_last_char, default_char, break_char;
3099 USHORT version;
3100 TEXTMETRICW tmW;
3102 version = GET_BE_WORD(tt_os2.version);
3104 os2_first_char = GET_BE_WORD(tt_os2.usFirstCharIndex);
3105 os2_last_char = GET_BE_WORD(tt_os2.usLastCharIndex);
3106 default_char = GET_BE_WORD(tt_os2.usDefaultChar);
3107 break_char = GET_BE_WORD(tt_os2.usBreakChar);
3109 trace("font %s charset %u: %x-%x (%x-%x) default %x break %x OS/2 version %u vendor %4.4s\n",
3110 font_name, lf->lfCharSet, os2_first_char, os2_last_char, cmap_first, cmap_last,
3111 default_char, break_char, version, (LPCSTR)&tt_os2.achVendID);
3113 if (cmap_type == cmap_ms_symbol || (cmap_first >= 0xf000 && cmap_first < 0xf100))
3115 expect_first_W = 0;
3116 switch(GetACP())
3118 case 1257: /* Baltic */
3119 expect_last_W = 0xf8fd;
3120 break;
3121 default:
3122 expect_last_W = 0xf0ff;
3124 expect_break_W = 0x20;
3125 expect_default_W = expect_break_W - 1;
3126 expect_first_A = 0x1e;
3127 expect_last_A = min(os2_last_char - os2_first_char + 0x20, 0xff);
3129 else
3131 expect_first_W = cmap_first;
3132 expect_last_W = min(cmap_last, os2_last_char);
3133 if(os2_first_char <= 1)
3134 expect_break_W = os2_first_char + 2;
3135 else if(os2_first_char > 0xff)
3136 expect_break_W = 0x20;
3137 else
3138 expect_break_W = os2_first_char;
3139 expect_default_W = expect_break_W - 1;
3140 expect_first_A = expect_default_W - 1;
3141 expect_last_A = min(expect_last_W, 0xff);
3143 expect_break_A = expect_break_W;
3144 expect_default_A = expect_default_W;
3146 /* Wine currently uses SYMBOL_CHARSET to identify whether the ANSI metrics need special handling */
3147 if(cmap_type != cmap_ms_symbol && tmA.tmCharSet == SYMBOL_CHARSET && expect_first_A != 0x1e)
3148 todo_wine ok(tmA.tmFirstChar == expect_first_A ||
3149 tmA.tmFirstChar == expect_first_A + 1 /* win9x */,
3150 "A: tmFirstChar for %s got %02x expected %02x\n", font_name, tmA.tmFirstChar, expect_first_A);
3151 else
3152 ok(tmA.tmFirstChar == expect_first_A ||
3153 tmA.tmFirstChar == expect_first_A + 1 /* win9x */,
3154 "A: tmFirstChar for %s got %02x expected %02x\n", font_name, tmA.tmFirstChar, expect_first_A);
3155 if (pGdiGetCodePage == NULL || ! IsDBCSLeadByteEx(pGdiGetCodePage(hdc), tmA.tmLastChar))
3156 ok(tmA.tmLastChar == expect_last_A ||
3157 tmA.tmLastChar == 0xff /* win9x */,
3158 "A: tmLastChar for %s got %02x expected %02x\n", font_name, tmA.tmLastChar, expect_last_A);
3159 else
3160 skip("tmLastChar is DBCS lead byte\n");
3161 ok(tmA.tmBreakChar == expect_break_A, "A: tmBreakChar for %s got %02x expected %02x\n",
3162 font_name, tmA.tmBreakChar, expect_break_A);
3163 ok(tmA.tmDefaultChar == expect_default_A || broken(sys_lang_non_english),
3164 "A: tmDefaultChar for %s got %02x expected %02x\n",
3165 font_name, tmA.tmDefaultChar, expect_default_A);
3168 SetLastError(0xdeadbeef);
3169 ret = GetTextMetricsW(hdc, &tmW);
3170 ok(ret || GetLastError() == ERROR_CALL_NOT_IMPLEMENTED,
3171 "GetTextMetricsW error %u\n", GetLastError());
3172 if (ret)
3174 /* Wine uses the os2 first char */
3175 if(cmap_first != os2_first_char && cmap_type != cmap_ms_symbol)
3176 todo_wine ok(tmW.tmFirstChar == expect_first_W, "W: tmFirstChar for %s got %02x expected %02x\n",
3177 font_name, tmW.tmFirstChar, expect_first_W);
3178 else
3179 ok(tmW.tmFirstChar == expect_first_W, "W: tmFirstChar for %s got %02x expected %02x\n",
3180 font_name, tmW.tmFirstChar, expect_first_W);
3182 /* Wine uses the os2 last char */
3183 if(expect_last_W != os2_last_char && cmap_type != cmap_ms_symbol)
3184 todo_wine ok(tmW.tmLastChar == expect_last_W, "W: tmLastChar for %s got %02x expected %02x\n",
3185 font_name, tmW.tmLastChar, expect_last_W);
3186 else
3187 ok(tmW.tmLastChar == expect_last_W, "W: tmLastChar for %s got %02x expected %02x\n",
3188 font_name, tmW.tmLastChar, expect_last_W);
3189 ok(tmW.tmBreakChar == expect_break_W, "W: tmBreakChar for %s got %02x expected %02x\n",
3190 font_name, tmW.tmBreakChar, expect_break_W);
3191 ok(tmW.tmDefaultChar == expect_default_W || broken(sys_lang_non_english),
3192 "W: tmDefaultChar for %s got %02x expected %02x\n",
3193 font_name, tmW.tmDefaultChar, expect_default_W);
3195 /* Test the aspect ratio while we have tmW */
3196 ret = GetDeviceCaps(hdc, LOGPIXELSX);
3197 ok(tmW.tmDigitizedAspectX == ret, "W: tmDigitizedAspectX %u != %u\n",
3198 tmW.tmDigitizedAspectX, ret);
3199 ret = GetDeviceCaps(hdc, LOGPIXELSY);
3200 ok(tmW.tmDigitizedAspectX == ret, "W: tmDigitizedAspectY %u != %u\n",
3201 tmW.tmDigitizedAspectX, ret);
3205 /* test FF_ values */
3206 switch(tt_os2.panose.bFamilyType)
3208 case PAN_ANY:
3209 case PAN_NO_FIT:
3210 case PAN_FAMILY_TEXT_DISPLAY:
3211 case PAN_FAMILY_PICTORIAL:
3212 default:
3213 if((tmA.tmPitchAndFamily & 1) == 0 || /* fixed */
3214 tt_os2.panose.bProportion == PAN_PROP_MONOSPACED)
3216 expect_ff(&tmA, &tt_os2, FF_MODERN, font_name);
3217 break;
3219 switch(tt_os2.panose.bSerifStyle)
3221 case PAN_ANY:
3222 case PAN_NO_FIT:
3223 default:
3224 expect_ff(&tmA, &tt_os2, FF_DONTCARE, font_name);
3225 break;
3227 case PAN_SERIF_COVE:
3228 case PAN_SERIF_OBTUSE_COVE:
3229 case PAN_SERIF_SQUARE_COVE:
3230 case PAN_SERIF_OBTUSE_SQUARE_COVE:
3231 case PAN_SERIF_SQUARE:
3232 case PAN_SERIF_THIN:
3233 case PAN_SERIF_BONE:
3234 case PAN_SERIF_EXAGGERATED:
3235 case PAN_SERIF_TRIANGLE:
3236 expect_ff(&tmA, &tt_os2, FF_ROMAN, font_name);
3237 break;
3239 case PAN_SERIF_NORMAL_SANS:
3240 case PAN_SERIF_OBTUSE_SANS:
3241 case PAN_SERIF_PERP_SANS:
3242 case PAN_SERIF_FLARED:
3243 case PAN_SERIF_ROUNDED:
3244 expect_ff(&tmA, &tt_os2, FF_SWISS, font_name);
3245 break;
3247 break;
3249 case PAN_FAMILY_SCRIPT:
3250 expect_ff(&tmA, &tt_os2, FF_SCRIPT, font_name);
3251 break;
3253 case PAN_FAMILY_DECORATIVE:
3254 expect_ff(&tmA, &tt_os2, FF_DECORATIVE, font_name);
3255 break;
3258 test_negative_width(hdc, lf);
3260 end_of_test:
3261 SelectObject(hdc, hfont_old);
3262 DeleteObject(hfont);
3264 ReleaseDC(0, hdc);
3267 static INT CALLBACK enum_truetype_font_proc(const LOGFONT *lf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
3269 INT *enumed = (INT *)lParam;
3271 if (type == TRUETYPE_FONTTYPE)
3273 (*enumed)++;
3274 test_text_metrics(lf, (const NEWTEXTMETRIC *)ntm);
3276 return 1;
3279 static void test_GetTextMetrics(void)
3281 LOGFONTA lf;
3282 HDC hdc;
3283 INT enumed;
3285 /* Report only once */
3286 if(!pGetGlyphIndicesA)
3287 win_skip("GetGlyphIndicesA is unavailable, negative width will not be checked\n");
3289 hdc = GetDC(0);
3291 memset(&lf, 0, sizeof(lf));
3292 lf.lfCharSet = DEFAULT_CHARSET;
3293 enumed = 0;
3294 EnumFontFamiliesExA(hdc, &lf, enum_truetype_font_proc, (LPARAM)&enumed, 0);
3295 trace("Tested metrics of %d truetype fonts\n", enumed);
3297 ReleaseDC(0, hdc);
3300 static void test_nonexistent_font(void)
3302 static const struct
3304 const char *name;
3305 int charset;
3306 } font_subst[] =
3308 { "Times New Roman Baltic", 186 },
3309 { "Times New Roman CE", 238 },
3310 { "Times New Roman CYR", 204 },
3311 { "Times New Roman Greek", 161 },
3312 { "Times New Roman TUR", 162 }
3314 LOGFONTA lf;
3315 HDC hdc;
3316 HFONT hfont;
3317 CHARSETINFO csi;
3318 INT cs, expected_cs, i;
3319 char buf[LF_FACESIZE];
3321 if (!is_truetype_font_installed("Arial") ||
3322 !is_truetype_font_installed("Times New Roman"))
3324 skip("Arial or Times New Roman not installed\n");
3325 return;
3328 expected_cs = GetACP();
3329 if (!TranslateCharsetInfo(ULongToPtr(expected_cs), &csi, TCI_SRCCODEPAGE))
3331 skip("TranslateCharsetInfo failed for code page %d\n", expected_cs);
3332 return;
3334 expected_cs = csi.ciCharset;
3335 trace("ACP %d -> charset %d\n", GetACP(), expected_cs);
3337 hdc = GetDC(0);
3339 memset(&lf, 0, sizeof(lf));
3340 lf.lfHeight = 100;
3341 lf.lfWeight = FW_REGULAR;
3342 lf.lfCharSet = ANSI_CHARSET;
3343 lf.lfPitchAndFamily = FF_SWISS;
3344 strcpy(lf.lfFaceName, "Nonexistent font");
3345 hfont = CreateFontIndirectA(&lf);
3346 hfont = SelectObject(hdc, hfont);
3347 GetTextFaceA(hdc, sizeof(buf), buf);
3348 ok(!lstrcmpiA(buf, "Arial"), "Got %s\n", buf);
3349 cs = GetTextCharset(hdc);
3350 ok(cs == ANSI_CHARSET, "expected ANSI_CHARSET, got %d\n", cs);
3351 DeleteObject(SelectObject(hdc, hfont));
3353 memset(&lf, 0, sizeof(lf));
3354 lf.lfHeight = -13;
3355 lf.lfWeight = FW_DONTCARE;
3356 strcpy(lf.lfFaceName, "Nonexistent font");
3357 hfont = CreateFontIndirectA(&lf);
3358 hfont = SelectObject(hdc, hfont);
3359 GetTextFaceA(hdc, sizeof(buf), buf);
3360 todo_wine /* Wine uses Arial for all substitutions */
3361 ok(!lstrcmpiA(buf, "Nonexistent font") /* XP, Vista */ ||
3362 !lstrcmpiA(buf, "MS Serif") || /* Win9x */
3363 !lstrcmpiA(buf, "MS Sans Serif"), /* win2k3 */
3364 "Got %s\n", buf);
3365 cs = GetTextCharset(hdc);
3366 ok(cs == expected_cs || cs == ANSI_CHARSET, "expected %d, got %d\n", expected_cs, cs);
3367 DeleteObject(SelectObject(hdc, hfont));
3369 memset(&lf, 0, sizeof(lf));
3370 lf.lfHeight = -13;
3371 lf.lfWeight = FW_REGULAR;
3372 strcpy(lf.lfFaceName, "Nonexistent font");
3373 hfont = CreateFontIndirectA(&lf);
3374 hfont = SelectObject(hdc, hfont);
3375 GetTextFaceA(hdc, sizeof(buf), buf);
3376 ok(!lstrcmpiA(buf, "Arial") /* XP, Vista */ ||
3377 !lstrcmpiA(buf, "Times New Roman") /* Win9x */, "Got %s\n", buf);
3378 cs = GetTextCharset(hdc);
3379 ok(cs == ANSI_CHARSET, "expected ANSI_CHARSET, got %d\n", cs);
3380 DeleteObject(SelectObject(hdc, hfont));
3382 memset(&lf, 0, sizeof(lf));
3383 lf.lfHeight = -13;
3384 lf.lfWeight = FW_DONTCARE;
3385 strcpy(lf.lfFaceName, "Times New Roman");
3386 hfont = CreateFontIndirectA(&lf);
3387 hfont = SelectObject(hdc, hfont);
3388 GetTextFaceA(hdc, sizeof(buf), buf);
3389 ok(!lstrcmpiA(buf, "Times New Roman"), "Got %s\n", buf);
3390 cs = GetTextCharset(hdc);
3391 ok(cs == ANSI_CHARSET, "expected ANSI_CHARSET, got %d\n", cs);
3392 DeleteObject(SelectObject(hdc, hfont));
3394 for (i = 0; i < sizeof(font_subst)/sizeof(font_subst[0]); i++)
3396 memset(&lf, 0, sizeof(lf));
3397 lf.lfHeight = -13;
3398 lf.lfWeight = FW_REGULAR;
3399 strcpy(lf.lfFaceName, font_subst[i].name);
3400 hfont = CreateFontIndirectA(&lf);
3401 hfont = SelectObject(hdc, hfont);
3402 cs = GetTextCharset(hdc);
3403 if (font_subst[i].charset == expected_cs)
3405 ok(cs == expected_cs, "expected %d, got %d for font %s\n", expected_cs, cs, font_subst[i].name);
3406 GetTextFaceA(hdc, sizeof(buf), buf);
3407 ok(!lstrcmpiA(buf, font_subst[i].name), "expected %s, got %s\n", font_subst[i].name, buf);
3409 else
3411 ok(cs == ANSI_CHARSET, "expected ANSI_CHARSET, got %d for font %s\n", cs, font_subst[i].name);
3412 GetTextFaceA(hdc, sizeof(buf), buf);
3413 ok(!lstrcmpiA(buf, "Arial") /* XP, Vista */ ||
3414 !lstrcmpiA(buf, "Times New Roman") /* Win9x */, "got %s for font %s\n", buf, font_subst[i].name);
3416 DeleteObject(SelectObject(hdc, hfont));
3418 memset(&lf, 0, sizeof(lf));
3419 lf.lfHeight = -13;
3420 lf.lfWeight = FW_DONTCARE;
3421 strcpy(lf.lfFaceName, font_subst[i].name);
3422 hfont = CreateFontIndirectA(&lf);
3423 hfont = SelectObject(hdc, hfont);
3424 GetTextFaceA(hdc, sizeof(buf), buf);
3425 ok(!lstrcmpiA(buf, "Arial") /* Wine */ ||
3426 !lstrcmpiA(buf, font_subst[i].name) /* XP, Vista */ ||
3427 !lstrcmpiA(buf, "MS Serif") /* Win9x */ ||
3428 !lstrcmpiA(buf, "MS Sans Serif"), /* win2k3 */
3429 "got %s for font %s\n", buf, font_subst[i].name);
3430 cs = GetTextCharset(hdc);
3431 ok(cs == expected_cs || cs == ANSI_CHARSET, "expected %d, got %d for font %s\n", expected_cs, cs, font_subst[i].name);
3432 DeleteObject(SelectObject(hdc, hfont));
3435 ReleaseDC(0, hdc);
3438 static void test_GdiRealizationInfo(void)
3440 HDC hdc;
3441 DWORD info[4];
3442 BOOL r;
3443 HFONT hfont, hfont_old;
3444 LOGFONTA lf;
3446 if(!pGdiRealizationInfo)
3448 win_skip("GdiRealizationInfo not available\n");
3449 return;
3452 hdc = GetDC(0);
3454 memset(info, 0xcc, sizeof(info));
3455 r = pGdiRealizationInfo(hdc, info);
3456 ok(r != 0, "ret 0\n");
3457 ok((info[0] & 0xf) == 1, "info[0] = %x for the system font\n", info[0]);
3458 ok(info[3] == 0xcccccccc, "structure longer than 3 dwords\n");
3460 if (!is_truetype_font_installed("Arial"))
3462 skip("skipping GdiRealizationInfo with truetype font\n");
3463 goto end;
3466 memset(&lf, 0, sizeof(lf));
3467 strcpy(lf.lfFaceName, "Arial");
3468 lf.lfHeight = 20;
3469 lf.lfWeight = FW_NORMAL;
3470 hfont = CreateFontIndirectA(&lf);
3471 hfont_old = SelectObject(hdc, hfont);
3473 memset(info, 0xcc, sizeof(info));
3474 r = pGdiRealizationInfo(hdc, info);
3475 ok(r != 0, "ret 0\n");
3476 ok((info[0] & 0xf) == 3, "info[0] = %x for arial\n", info[0]);
3477 ok(info[3] == 0xcccccccc, "structure longer than 3 dwords\n");
3479 DeleteObject(SelectObject(hdc, hfont_old));
3481 end:
3482 ReleaseDC(0, hdc);
3485 /* Tests on XP SP2 show that the ANSI version of GetTextFace does NOT include
3486 the nul in the count of characters copied when the face name buffer is not
3487 NULL, whereas it does if the buffer is NULL. Further, the Unicode version
3488 always includes it. */
3489 static void test_GetTextFace(void)
3491 static const char faceA[] = "Tahoma";
3492 static const WCHAR faceW[] = {'T','a','h','o','m','a', 0};
3493 LOGFONTA fA = {0};
3494 LOGFONTW fW = {0};
3495 char bufA[LF_FACESIZE];
3496 WCHAR bufW[LF_FACESIZE];
3497 HFONT f, g;
3498 HDC dc;
3499 int n;
3501 if(!is_font_installed("Tahoma"))
3503 skip("Tahoma is not installed so skipping this test\n");
3504 return;
3507 /* 'A' case. */
3508 memcpy(fA.lfFaceName, faceA, sizeof faceA);
3509 f = CreateFontIndirectA(&fA);
3510 ok(f != NULL, "CreateFontIndirectA failed\n");
3512 dc = GetDC(NULL);
3513 g = SelectObject(dc, f);
3514 n = GetTextFaceA(dc, sizeof bufA, bufA);
3515 ok(n == sizeof faceA - 1, "GetTextFaceA returned %d\n", n);
3516 ok(lstrcmpA(faceA, bufA) == 0, "GetTextFaceA\n");
3518 /* Play with the count arg. */
3519 bufA[0] = 'x';
3520 n = GetTextFaceA(dc, 0, bufA);
3521 ok(n == 0, "GetTextFaceA returned %d\n", n);
3522 ok(bufA[0] == 'x', "GetTextFaceA buf[0] == %d\n", bufA[0]);
3524 bufA[0] = 'x';
3525 n = GetTextFaceA(dc, 1, bufA);
3526 ok(n == 0, "GetTextFaceA returned %d\n", n);
3527 ok(bufA[0] == '\0', "GetTextFaceA buf[0] == %d\n", bufA[0]);
3529 bufA[0] = 'x'; bufA[1] = 'y';
3530 n = GetTextFaceA(dc, 2, bufA);
3531 ok(n == 1, "GetTextFaceA returned %d\n", n);
3532 ok(bufA[0] == faceA[0] && bufA[1] == '\0', "GetTextFaceA didn't copy\n");
3534 n = GetTextFaceA(dc, 0, NULL);
3535 ok(n == sizeof faceA ||
3536 broken(n == 0), /* win98, winMe */
3537 "GetTextFaceA returned %d\n", n);
3539 DeleteObject(SelectObject(dc, g));
3540 ReleaseDC(NULL, dc);
3542 /* 'W' case. */
3543 memcpy(fW.lfFaceName, faceW, sizeof faceW);
3544 SetLastError(0xdeadbeef);
3545 f = CreateFontIndirectW(&fW);
3546 if (!f && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
3548 win_skip("CreateFontIndirectW is not implemented\n");
3549 return;
3551 ok(f != NULL, "CreateFontIndirectW failed\n");
3553 dc = GetDC(NULL);
3554 g = SelectObject(dc, f);
3555 n = GetTextFaceW(dc, sizeof bufW / sizeof bufW[0], bufW);
3556 ok(n == sizeof faceW / sizeof faceW[0], "GetTextFaceW returned %d\n", n);
3557 ok(lstrcmpW(faceW, bufW) == 0, "GetTextFaceW\n");
3559 /* Play with the count arg. */
3560 bufW[0] = 'x';
3561 n = GetTextFaceW(dc, 0, bufW);
3562 ok(n == 0, "GetTextFaceW returned %d\n", n);
3563 ok(bufW[0] == 'x', "GetTextFaceW buf[0] == %d\n", bufW[0]);
3565 bufW[0] = 'x';
3566 n = GetTextFaceW(dc, 1, bufW);
3567 ok(n == 1, "GetTextFaceW returned %d\n", n);
3568 ok(bufW[0] == '\0', "GetTextFaceW buf[0] == %d\n", bufW[0]);
3570 bufW[0] = 'x'; bufW[1] = 'y';
3571 n = GetTextFaceW(dc, 2, bufW);
3572 ok(n == 2, "GetTextFaceW returned %d\n", n);
3573 ok(bufW[0] == faceW[0] && bufW[1] == '\0', "GetTextFaceW didn't copy\n");
3575 n = GetTextFaceW(dc, 0, NULL);
3576 ok(n == sizeof faceW / sizeof faceW[0], "GetTextFaceW returned %d\n", n);
3578 DeleteObject(SelectObject(dc, g));
3579 ReleaseDC(NULL, dc);
3582 static void test_orientation(void)
3584 static const char test_str[11] = "Test String";
3585 HDC hdc;
3586 LOGFONTA lf;
3587 HFONT hfont, old_hfont;
3588 SIZE size;
3590 if (!is_truetype_font_installed("Arial"))
3592 skip("Arial is not installed\n");
3593 return;
3596 hdc = CreateCompatibleDC(0);
3597 memset(&lf, 0, sizeof(lf));
3598 lstrcpyA(lf.lfFaceName, "Arial");
3599 lf.lfHeight = 72;
3600 lf.lfOrientation = lf.lfEscapement = 900;
3601 hfont = create_font("orientation", &lf);
3602 old_hfont = SelectObject(hdc, hfont);
3603 ok(GetTextExtentExPointA(hdc, test_str, sizeof(test_str), 32767, NULL, NULL, &size), "GetTextExtentExPointA failed\n");
3604 ok(near_match(311, size.cx), "cx should be about 311, got %d\n", size.cx);
3605 ok(near_match(75, size.cy), "cy should be about 75, got %d\n", size.cy);
3606 SelectObject(hdc, old_hfont);
3607 DeleteObject(hfont);
3608 DeleteDC(hdc);
3611 static void test_oemcharset(void)
3613 HDC hdc;
3614 LOGFONTA lf, clf;
3615 HFONT hfont, old_hfont;
3616 int charset;
3618 hdc = CreateCompatibleDC(0);
3619 ZeroMemory(&lf, sizeof(lf));
3620 lf.lfHeight = 12;
3621 lf.lfCharSet = OEM_CHARSET;
3622 lf.lfPitchAndFamily = FIXED_PITCH | FF_MODERN;
3623 lstrcpyA(lf.lfFaceName, "Terminal");
3624 hfont = CreateFontIndirectA(&lf);
3625 old_hfont = SelectObject(hdc, hfont);
3626 charset = GetTextCharset(hdc);
3627 todo_wine
3628 ok(charset == OEM_CHARSET, "expected %d charset, got %d\n", OEM_CHARSET, charset);
3629 hfont = SelectObject(hdc, old_hfont);
3630 GetObjectA(hfont, sizeof(clf), &clf);
3631 ok(!lstrcmpA(clf.lfFaceName, lf.lfFaceName), "expected %s face name, got %s\n", lf.lfFaceName, clf.lfFaceName);
3632 ok(clf.lfPitchAndFamily == lf.lfPitchAndFamily, "expected %x family, got %x\n", lf.lfPitchAndFamily, clf.lfPitchAndFamily);
3633 ok(clf.lfCharSet == lf.lfCharSet, "expected %d charset, got %d\n", lf.lfCharSet, clf.lfCharSet);
3634 ok(clf.lfHeight == lf.lfHeight, "expected %d height, got %d\n", lf.lfHeight, clf.lfHeight);
3635 DeleteObject(hfont);
3636 DeleteDC(hdc);
3639 static int CALLBACK create_fixed_pitch_font_proc(const LOGFONT *lpelfe,
3640 const TEXTMETRIC *lpntme,
3641 DWORD FontType, LPARAM lParam)
3643 const NEWTEXTMETRICEX *lpntmex = (const NEWTEXTMETRICEX *)lpntme;
3644 CHARSETINFO csi;
3645 LOGFONT lf = *lpelfe;
3646 HFONT hfont;
3648 /* skip bitmap, proportional or vertical font */
3649 if ((FontType & TRUETYPE_FONTTYPE) == 0 ||
3650 (lf.lfPitchAndFamily & 0xf) != FIXED_PITCH ||
3651 lf.lfFaceName[0] == '@')
3652 return 1;
3654 /* skip linked font */
3655 if (!TranslateCharsetInfo((DWORD*)(INT_PTR)lpelfe->lfCharSet, &csi, TCI_SRCCHARSET) ||
3656 (lpntmex->ntmFontSig.fsCsb[0] & csi.fs.fsCsb[0]) == 0)
3657 return 1;
3659 /* test with an odd height */
3660 lf.lfHeight = -19;
3661 lf.lfWidth = 0;
3662 hfont = CreateFontIndirect(&lf);
3663 if (hfont)
3665 *(HFONT *)lParam = hfont;
3666 return 0;
3668 return 1;
3671 static void test_GetGlyphOutline(void)
3673 HDC hdc;
3674 GLYPHMETRICS gm, gm2;
3675 LOGFONTA lf;
3676 HFONT hfont, old_hfont;
3677 INT ret, ret2;
3678 static const struct
3680 UINT cs;
3681 UINT a;
3682 UINT w;
3683 } c[] =
3685 {ANSI_CHARSET, 0x30, 0x30},
3686 {SHIFTJIS_CHARSET, 0x82a0, 0x3042},
3687 {HANGEUL_CHARSET, 0x8141, 0xac02},
3688 {JOHAB_CHARSET, 0x8446, 0x3135},
3689 {GB2312_CHARSET, 0x8141, 0x4e04},
3690 {CHINESEBIG5_CHARSET, 0xa142, 0x3001}
3692 UINT i;
3694 if (!is_truetype_font_installed("Tahoma"))
3696 skip("Tahoma is not installed\n");
3697 return;
3700 hdc = CreateCompatibleDC(0);
3701 memset(&lf, 0, sizeof(lf));
3702 lf.lfHeight = 72;
3703 lstrcpyA(lf.lfFaceName, "Tahoma");
3704 SetLastError(0xdeadbeef);
3705 hfont = CreateFontIndirectA(&lf);
3706 ok(hfont != 0, "CreateFontIndirectA error %u\n", GetLastError());
3707 old_hfont = SelectObject(hdc, hfont);
3709 memset(&gm, 0, sizeof(gm));
3710 SetLastError(0xdeadbeef);
3711 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
3712 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %u\n", GetLastError());
3714 memset(&gm, 0, sizeof(gm));
3715 SetLastError(0xdeadbeef);
3716 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, NULL);
3717 ok(ret == GDI_ERROR, "GetGlyphOutlineA should fail\n");
3718 ok(GetLastError() == 0xdeadbeef ||
3719 GetLastError() == ERROR_INVALID_PARAMETER, /* win98, winMe */
3720 "expected 0xdeadbeef, got %u\n", GetLastError());
3722 memset(&gm, 0, sizeof(gm));
3723 SetLastError(0xdeadbeef);
3724 ret = GetGlyphOutlineW(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
3725 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
3726 ok(ret != GDI_ERROR, "GetGlyphOutlineW error %u\n", GetLastError());
3728 memset(&gm, 0, sizeof(gm));
3729 SetLastError(0xdeadbeef);
3730 ret = GetGlyphOutlineW(hdc, 'A', GGO_METRICS, &gm, 0, NULL, NULL);
3731 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
3733 ok(ret == GDI_ERROR, "GetGlyphOutlineW should fail\n");
3734 ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %u\n", GetLastError());
3737 /* test for needed buffer size request on space char */
3738 memset(&gm, 0, sizeof(gm));
3739 SetLastError(0xdeadbeef);
3740 ret = GetGlyphOutlineW(hdc, ' ', GGO_NATIVE, &gm, 0, NULL, &mat);
3741 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
3742 ok(ret == 0, "GetGlyphOutlineW should return 0 buffer size for space char\n");
3744 /* requesting buffer size for space char + error */
3745 memset(&gm, 0, sizeof(gm));
3746 SetLastError(0xdeadbeef);
3747 ret = GetGlyphOutlineW(0, ' ', GGO_NATIVE, &gm, 0, NULL, NULL);
3748 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
3750 ok(ret == GDI_ERROR, "GetGlyphOutlineW should return GDI_ERROR\n");
3751 ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %u\n", GetLastError());
3754 SelectObject(hdc, old_hfont);
3755 DeleteObject(hfont);
3757 for (i = 0; i < sizeof c / sizeof c[0]; ++i)
3759 static const MAT2 rotate_mat = {{0, 0}, {0, -1}, {0, 1}, {0, 0}};
3760 TEXTMETRIC tm;
3762 lf.lfFaceName[0] = '\0';
3763 lf.lfCharSet = c[i].cs;
3764 lf.lfPitchAndFamily = 0;
3765 if (EnumFontFamiliesEx(hdc, &lf, create_font_proc, (LPARAM)&hfont, 0))
3767 skip("TrueType font for charset %u is not installed\n", c[i].cs);
3768 continue;
3771 old_hfont = SelectObject(hdc, hfont);
3773 /* expected to ignore superfluous bytes (sigle-byte character) */
3774 ret = GetGlyphOutlineA(hdc, 0x8041, GGO_BITMAP, &gm, 0, NULL, &mat);
3775 ret2 = GetGlyphOutlineA(hdc, 0x41, GGO_BITMAP, &gm2, 0, NULL, &mat);
3776 ok(ret == ret2 && memcmp(&gm, &gm2, sizeof gm) == 0, "%d %d\n", ret, ret2);
3778 ret = GetGlyphOutlineA(hdc, 0xcc8041, GGO_BITMAP, &gm, 0, NULL, &mat);
3779 ok(ret == ret2 && memcmp(&gm, &gm2, sizeof gm) == 0,
3780 "Expected to ignore superfluous bytes, got %d %d\n", ret, ret2);
3782 /* expected to ignore superfluous bytes (double-byte character) */
3783 ret = GetGlyphOutlineA(hdc, c[i].a, GGO_BITMAP, &gm, 0, NULL, &mat);
3784 ret2 = GetGlyphOutlineA(hdc, c[i].a | 0xdead0000, GGO_BITMAP, &gm2, 0, NULL, &mat);
3785 ok(ret == ret2 && memcmp(&gm, &gm2, sizeof gm) == 0,
3786 "Expected to ignore superfluous bytes, got %d %d\n", ret, ret2);
3788 /* expected to match wide-char version results */
3789 ret2 = GetGlyphOutlineW(hdc, c[i].w, GGO_BITMAP, &gm2, 0, NULL, &mat);
3790 ok(ret == ret2 && memcmp(&gm, &gm2, sizeof gm) == 0, "%d %d\n", ret, ret2);
3792 if (EnumFontFamiliesEx(hdc, &lf, create_fixed_pitch_font_proc, (LPARAM)&hfont, 0))
3794 skip("Fixed-pitch TrueType font for charset %u is not available\n", c[i].cs);
3795 continue;
3797 DeleteObject(SelectObject(hdc, hfont));
3798 if (c[i].a <= 0xff)
3800 DeleteObject(SelectObject(hdc, old_hfont));
3801 continue;
3804 ret = GetObject(hfont, sizeof lf, &lf);
3805 ok(ret > 0, "GetObject error %u\n", GetLastError());
3807 ret = GetTextMetrics(hdc, &tm);
3808 ok(ret, "GetTextMetrics error %u\n", GetLastError());
3809 ret = GetGlyphOutlineA(hdc, c[i].a, GGO_METRICS, &gm2, 0, NULL, &mat);
3810 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %u\n", GetLastError());
3811 trace("Tests with height=%d,avg=%d,full=%d,face=%s,charset=%d\n",
3812 -lf.lfHeight, tm.tmAveCharWidth, gm2.gmCellIncX, lf.lfFaceName, lf.lfCharSet);
3813 ok(gm2.gmCellIncX == tm.tmAveCharWidth * 2 || broken(gm2.gmCellIncX == -lf.lfHeight),
3814 "expected %d, got %d (%s:%d)\n",
3815 tm.tmAveCharWidth * 2, gm2.gmCellIncX, lf.lfFaceName, lf.lfCharSet);
3817 ret = GetGlyphOutlineA(hdc, c[i].a, GGO_METRICS, &gm2, 0, NULL, &rotate_mat);
3818 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %u\n", GetLastError());
3819 ok(gm2.gmCellIncY == -lf.lfHeight,
3820 "expected %d, got %d (%s:%d)\n",
3821 -lf.lfHeight, gm2.gmCellIncY, lf.lfFaceName, lf.lfCharSet);
3823 lf.lfItalic = TRUE;
3824 hfont = CreateFontIndirect(&lf);
3825 ok(hfont != NULL, "CreateFontIndirect error %u\n", GetLastError());
3826 DeleteObject(SelectObject(hdc, hfont));
3827 ret = GetTextMetrics(hdc, &tm);
3828 ok(ret, "GetTextMetrics error %u\n", GetLastError());
3829 ret = GetGlyphOutlineA(hdc, c[i].a, GGO_METRICS, &gm2, 0, NULL, &mat);
3830 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %u\n", GetLastError());
3831 ok(gm2.gmCellIncX == tm.tmAveCharWidth * 2 || broken(gm2.gmCellIncX == -lf.lfHeight),
3832 "expected %d, got %d (%s:%d)\n",
3833 tm.tmAveCharWidth * 2, gm2.gmCellIncX, lf.lfFaceName, lf.lfCharSet);
3835 lf.lfItalic = FALSE;
3836 lf.lfEscapement = lf.lfOrientation = 2700;
3837 hfont = CreateFontIndirect(&lf);
3838 ok(hfont != NULL, "CreateFontIndirect error %u\n", GetLastError());
3839 DeleteObject(SelectObject(hdc, hfont));
3840 ret = GetGlyphOutlineA(hdc, c[i].a, GGO_METRICS, &gm2, 0, NULL, &mat);
3841 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %u\n", GetLastError());
3842 ok(gm2.gmCellIncY == -lf.lfHeight,
3843 "expected %d, got %d (%s:%d)\n",
3844 -lf.lfHeight, gm2.gmCellIncY, lf.lfFaceName, lf.lfCharSet);
3846 hfont = SelectObject(hdc, old_hfont);
3847 DeleteObject(hfont);
3850 DeleteDC(hdc);
3853 /* bug #9995: there is a limit to the character width that can be specified */
3854 static void test_GetTextMetrics2(const char *fontname, int font_height)
3856 HFONT of, hf;
3857 HDC hdc;
3858 TEXTMETRICA tm;
3859 BOOL ret;
3860 int ave_width, height, width, ratio, scale;
3862 if (!is_truetype_font_installed( fontname)) {
3863 skip("%s is not installed\n", fontname);
3864 return;
3866 hdc = CreateCompatibleDC(0);
3867 ok( hdc != NULL, "CreateCompatibleDC failed\n");
3868 /* select width = 0 */
3869 hf = CreateFontA(font_height, 0, 0, 0, FW_REGULAR, FALSE, FALSE, FALSE,
3870 DEFAULT_CHARSET, OUT_TT_PRECIS, CLIP_LH_ANGLES,
3871 DEFAULT_QUALITY, VARIABLE_PITCH,
3872 fontname);
3873 ok( hf != NULL, "CreateFontA(%s, %d) failed\n", fontname, font_height);
3874 of = SelectObject( hdc, hf);
3875 ret = GetTextMetricsA( hdc, &tm);
3876 ok(ret, "GetTextMetricsA error %u\n", GetLastError());
3877 height = tm.tmHeight;
3878 ave_width = tm.tmAveCharWidth;
3879 SelectObject( hdc, of);
3880 DeleteObject( hf);
3882 trace("height %d, ave width %d\n", height, ave_width);
3884 for (width = ave_width * 2; /* nothing*/; width += ave_width)
3886 hf = CreateFont(height, width, 0, 0, FW_REGULAR, FALSE, FALSE, FALSE,
3887 DEFAULT_CHARSET, OUT_TT_PRECIS, CLIP_LH_ANGLES,
3888 DEFAULT_QUALITY, VARIABLE_PITCH, fontname);
3889 ok(hf != 0, "CreateFont failed\n");
3890 of = SelectObject(hdc, hf);
3891 ret = GetTextMetrics(hdc, &tm);
3892 ok(ret, "GetTextMetrics error %u\n", GetLastError());
3893 SelectObject(hdc, of);
3894 DeleteObject(hf);
3896 if (match_off_by_1(tm.tmAveCharWidth, ave_width) || width / height > 200)
3897 break;
3900 DeleteDC(hdc);
3902 ratio = width / height;
3903 scale = width / ave_width;
3905 trace("max width/height ratio (%d / %d) %d, max width scale (%d / %d) %d\n",
3906 width, height, ratio, width, ave_width, scale);
3908 ok(ratio >= 90 && ratio <= 110, "expected width/height ratio 90-110, got %d\n", ratio);
3911 static void test_CreateFontIndirect(void)
3913 LOGFONTA lf, getobj_lf;
3914 int ret, i;
3915 HFONT hfont;
3916 char TestName[][16] = {"Arial", "Arial Bold", "Arial Italic", "Arial Baltic"};
3918 memset(&lf, 0, sizeof(lf));
3919 lf.lfCharSet = ANSI_CHARSET;
3920 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
3921 lf.lfHeight = 16;
3922 lf.lfWidth = 16;
3923 lf.lfQuality = DEFAULT_QUALITY;
3924 lf.lfItalic = FALSE;
3925 lf.lfWeight = FW_DONTCARE;
3927 for (i = 0; i < sizeof(TestName)/sizeof(TestName[0]); i++)
3929 lstrcpyA(lf.lfFaceName, TestName[i]);
3930 hfont = CreateFontIndirectA(&lf);
3931 ok(hfont != 0, "CreateFontIndirectA failed\n");
3932 SetLastError(0xdeadbeef);
3933 ret = GetObject(hfont, sizeof(getobj_lf), &getobj_lf);
3934 ok(ret, "GetObject failed: %d\n", GetLastError());
3935 ok(lf.lfItalic == getobj_lf.lfItalic, "lfItalic: expect %02x got %02x\n", lf.lfItalic, getobj_lf.lfItalic);
3936 ok(lf.lfWeight == getobj_lf.lfWeight ||
3937 broken((SHORT)lf.lfWeight == getobj_lf.lfWeight), /* win9x */
3938 "lfWeight: expect %08x got %08x\n", lf.lfWeight, getobj_lf.lfWeight);
3939 ok(!lstrcmpA(lf.lfFaceName, getobj_lf.lfFaceName) ||
3940 broken(!memcmp(lf.lfFaceName, getobj_lf.lfFaceName, LF_FACESIZE-1)), /* win9x doesn't ensure '\0' termination */
3941 "font names don't match: %s != %s\n", lf.lfFaceName, getobj_lf.lfFaceName);
3942 DeleteObject(hfont);
3946 static void test_CreateFontIndirectEx(void)
3948 ENUMLOGFONTEXDVA lfex;
3949 HFONT hfont;
3951 if (!pCreateFontIndirectExA)
3953 win_skip("CreateFontIndirectExA is not available\n");
3954 return;
3957 if (!is_truetype_font_installed("Arial"))
3959 skip("Arial is not installed\n");
3960 return;
3963 SetLastError(0xdeadbeef);
3964 hfont = pCreateFontIndirectExA(NULL);
3965 ok(hfont == NULL, "got %p\n", hfont);
3966 ok(GetLastError() == 0xdeadbeef, "got error %d\n", GetLastError());
3968 memset(&lfex, 0, sizeof(lfex));
3969 lstrcpyA(lfex.elfEnumLogfontEx.elfLogFont.lfFaceName, "Arial");
3970 hfont = pCreateFontIndirectExA(&lfex);
3971 ok(hfont != 0, "CreateFontIndirectEx failed\n");
3972 if (hfont)
3973 check_font("Arial", &lfex.elfEnumLogfontEx.elfLogFont, hfont);
3974 DeleteObject(hfont);
3977 static void free_font(void *font)
3979 UnmapViewOfFile(font);
3982 static void *load_font(const char *font_name, DWORD *font_size)
3984 char file_name[MAX_PATH];
3985 HANDLE file, mapping;
3986 void *font;
3988 if (!GetWindowsDirectory(file_name, sizeof(file_name))) return NULL;
3989 strcat(file_name, "\\fonts\\");
3990 strcat(file_name, font_name);
3992 file = CreateFile(file_name, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, 0);
3993 if (file == INVALID_HANDLE_VALUE) return NULL;
3995 *font_size = GetFileSize(file, NULL);
3997 mapping = CreateFileMapping(file, NULL, PAGE_READONLY, 0, 0, NULL);
3998 if (!mapping)
4000 CloseHandle(file);
4001 return NULL;
4004 font = MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0);
4006 CloseHandle(file);
4007 CloseHandle(mapping);
4008 return font;
4011 static void test_AddFontMemResource(void)
4013 void *font;
4014 DWORD font_size, num_fonts;
4015 HANDLE ret;
4016 BOOL bRet;
4018 if (!pAddFontMemResourceEx || !pRemoveFontMemResourceEx)
4020 win_skip("AddFontMemResourceEx is not available on this platform\n");
4021 return;
4024 font = load_font("sserife.fon", &font_size);
4025 if (!font)
4027 skip("Unable to locate and load font sserife.fon\n");
4028 return;
4031 SetLastError(0xdeadbeef);
4032 ret = pAddFontMemResourceEx(NULL, 0, NULL, NULL);
4033 ok(!ret, "AddFontMemResourceEx should fail\n");
4034 ok(GetLastError() == ERROR_INVALID_PARAMETER,
4035 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
4036 GetLastError());
4038 SetLastError(0xdeadbeef);
4039 ret = pAddFontMemResourceEx(NULL, 10, NULL, NULL);
4040 ok(!ret, "AddFontMemResourceEx should fail\n");
4041 ok(GetLastError() == ERROR_INVALID_PARAMETER,
4042 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
4043 GetLastError());
4045 SetLastError(0xdeadbeef);
4046 ret = pAddFontMemResourceEx(NULL, 0, NULL, &num_fonts);
4047 ok(!ret, "AddFontMemResourceEx should fail\n");
4048 ok(GetLastError() == ERROR_INVALID_PARAMETER,
4049 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
4050 GetLastError());
4052 SetLastError(0xdeadbeef);
4053 ret = pAddFontMemResourceEx(NULL, 10, NULL, &num_fonts);
4054 ok(!ret, "AddFontMemResourceEx should fail\n");
4055 ok(GetLastError() == ERROR_INVALID_PARAMETER,
4056 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
4057 GetLastError());
4059 SetLastError(0xdeadbeef);
4060 ret = pAddFontMemResourceEx(font, 0, NULL, NULL);
4061 ok(!ret, "AddFontMemResourceEx should fail\n");
4062 ok(GetLastError() == ERROR_INVALID_PARAMETER,
4063 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
4064 GetLastError());
4066 SetLastError(0xdeadbeef);
4067 ret = pAddFontMemResourceEx(font, 10, NULL, NULL);
4068 ok(!ret, "AddFontMemResourceEx should fail\n");
4069 ok(GetLastError() == ERROR_INVALID_PARAMETER,
4070 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
4071 GetLastError());
4073 num_fonts = 0xdeadbeef;
4074 SetLastError(0xdeadbeef);
4075 ret = pAddFontMemResourceEx(font, 0, NULL, &num_fonts);
4076 ok(!ret, "AddFontMemResourceEx should fail\n");
4077 ok(GetLastError() == ERROR_INVALID_PARAMETER,
4078 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
4079 GetLastError());
4080 ok(num_fonts == 0xdeadbeef, "number of loaded fonts should be 0xdeadbeef\n");
4082 if (0) /* hangs under windows 2000 */
4084 num_fonts = 0xdeadbeef;
4085 SetLastError(0xdeadbeef);
4086 ret = pAddFontMemResourceEx(font, 10, NULL, &num_fonts);
4087 ok(!ret, "AddFontMemResourceEx should fail\n");
4088 ok(GetLastError() == 0xdeadbeef,
4089 "Expected GetLastError() to return 0xdeadbeef, got %u\n",
4090 GetLastError());
4091 ok(num_fonts == 0xdeadbeef, "number of loaded fonts should be 0xdeadbeef\n");
4094 num_fonts = 0xdeadbeef;
4095 SetLastError(0xdeadbeef);
4096 ret = pAddFontMemResourceEx(font, font_size, NULL, &num_fonts);
4097 ok(ret != 0, "AddFontMemResourceEx error %d\n", GetLastError());
4098 ok(num_fonts != 0xdeadbeef, "number of loaded fonts should not be 0xdeadbeef\n");
4099 ok(num_fonts != 0, "number of loaded fonts should not be 0\n");
4101 free_font(font);
4103 SetLastError(0xdeadbeef);
4104 bRet = pRemoveFontMemResourceEx(ret);
4105 ok(bRet, "RemoveFontMemResourceEx error %d\n", GetLastError());
4107 /* test invalid pointer to number of loaded fonts */
4108 font = load_font("sserife.fon", &font_size);
4109 ok(font != NULL, "Unable to locate and load font sserife.fon\n");
4111 SetLastError(0xdeadbeef);
4112 ret = pAddFontMemResourceEx(font, font_size, NULL, (void *)0xdeadbeef);
4113 ok(!ret, "AddFontMemResourceEx should fail\n");
4114 ok(GetLastError() == 0xdeadbeef,
4115 "Expected GetLastError() to return 0xdeadbeef, got %u\n",
4116 GetLastError());
4118 SetLastError(0xdeadbeef);
4119 ret = pAddFontMemResourceEx(font, font_size, NULL, NULL);
4120 ok(!ret, "AddFontMemResourceEx should fail\n");
4121 ok(GetLastError() == ERROR_INVALID_PARAMETER,
4122 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
4123 GetLastError());
4125 free_font(font);
4128 static INT CALLBACK enum_fonts_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lparam)
4130 LOGFONT *lf;
4132 if (type != TRUETYPE_FONTTYPE) return 1;
4134 ok(ntm->tmWeight == elf->lfWeight, "expected %d got %d\n", ntm->tmWeight, elf->lfWeight);
4136 lf = (LOGFONT *)lparam;
4137 *lf = *elf;
4138 return 0;
4141 static INT CALLBACK enum_all_fonts_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lparam)
4143 int ret;
4144 LOGFONT *lf;
4146 if (type != TRUETYPE_FONTTYPE) return 1;
4148 lf = (LOGFONT *)lparam;
4149 ret = strcmp(lf->lfFaceName, elf->lfFaceName);
4150 if(ret == 0)
4152 ok(ntm->tmWeight == elf->lfWeight, "expected %d got %d\n", ntm->tmWeight, elf->lfWeight);
4153 *lf = *elf;
4154 return 0;
4156 return 1;
4159 static INT CALLBACK enum_with_magic_retval_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lparam)
4161 return lparam;
4164 static void test_EnumFonts(void)
4166 int ret;
4167 LOGFONT lf;
4168 HDC hdc;
4170 if (!is_truetype_font_installed("Arial"))
4172 skip("Arial is not installed\n");
4173 return;
4176 /* Windows uses localized font face names, so Arial Bold won't be found */
4177 if (PRIMARYLANGID(GetUserDefaultLangID()) != LANG_ENGLISH)
4179 skip("User locale is not English, skipping the test\n");
4180 return;
4183 hdc = CreateCompatibleDC(0);
4185 /* check that the enumproc's retval is returned */
4186 ret = EnumFontFamilies(hdc, NULL, enum_with_magic_retval_proc, 0xcafe);
4187 ok(ret == 0xcafe, "got %08x\n", ret);
4189 ret = EnumFontFamilies(hdc, "Arial", enum_fonts_proc, (LPARAM)&lf);
4190 ok(!ret, "font Arial is not enumerated\n");
4191 ret = strcmp(lf.lfFaceName, "Arial");
4192 ok(!ret, "expected Arial got %s\n", lf.lfFaceName);
4193 ok(lf.lfWeight == FW_NORMAL, "expected FW_NORMAL got %d\n", lf.lfWeight);
4195 lstrcpy(lf.lfFaceName, "Arial");
4196 ret = EnumFontFamilies(hdc, NULL, enum_all_fonts_proc, (LPARAM)&lf);
4197 ok(!ret, "font Arial is not enumerated\n");
4198 ret = strcmp(lf.lfFaceName, "Arial");
4199 ok(!ret, "expected Arial got %s\n", lf.lfFaceName);
4200 ok(lf.lfWeight == FW_NORMAL, "expected FW_NORMAL got %d\n", lf.lfWeight);
4202 ret = EnumFontFamilies(hdc, "Arial Bold", enum_fonts_proc, (LPARAM)&lf);
4203 ok(!ret, "font Arial Bold is not enumerated\n");
4204 ret = strcmp(lf.lfFaceName, "Arial");
4205 ok(!ret, "expected Arial got %s\n", lf.lfFaceName);
4206 ok(lf.lfWeight == FW_BOLD, "expected FW_BOLD got %d\n", lf.lfWeight);
4208 lstrcpy(lf.lfFaceName, "Arial Bold");
4209 ret = EnumFontFamilies(hdc, NULL, enum_all_fonts_proc, (LPARAM)&lf);
4210 ok(ret, "font Arial Bold should not be enumerated\n");
4212 ret = EnumFontFamilies(hdc, "Arial Bold Italic", enum_fonts_proc, (LPARAM)&lf);
4213 ok(!ret, "font Arial Bold Italic is not enumerated\n");
4214 ret = strcmp(lf.lfFaceName, "Arial");
4215 ok(!ret, "expected Arial got %s\n", lf.lfFaceName);
4216 ok(lf.lfWeight == FW_BOLD, "expected FW_BOLD got %d\n", lf.lfWeight);
4218 lstrcpy(lf.lfFaceName, "Arial Bold Italic");
4219 ret = EnumFontFamilies(hdc, NULL, enum_all_fonts_proc, (LPARAM)&lf);
4220 ok(ret, "font Arial Bold Italic should not be enumerated\n");
4222 ret = EnumFontFamilies(hdc, "Arial Italic Bold", enum_fonts_proc, (LPARAM)&lf);
4223 ok(ret, "font Arial Italic Bold should not be enumerated\n");
4225 lstrcpy(lf.lfFaceName, "Arial Italic Bold");
4226 ret = EnumFontFamilies(hdc, NULL, enum_all_fonts_proc, (LPARAM)&lf);
4227 ok(ret, "font Arial Italic Bold should not be enumerated\n");
4229 DeleteDC(hdc);
4232 static INT CALLBACK is_font_installed_fullname_proc(const LOGFONT *lf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
4234 const ENUMLOGFONT *elf = (const ENUMLOGFONT *)lf;
4235 const char *fullname = (const char *)lParam;
4237 if (!strcmp((const char *)elf->elfFullName, fullname)) return 0;
4239 return 1;
4242 static BOOL is_font_installed_fullname(const char *family, const char *fullname)
4244 HDC hdc = GetDC(0);
4245 BOOL ret = FALSE;
4247 if(!EnumFontFamiliesA(hdc, family, is_font_installed_fullname_proc, (LPARAM)fullname))
4248 ret = TRUE;
4250 ReleaseDC(0, hdc);
4251 return ret;
4254 static void test_fullname(void)
4256 static const char *TestName[] = {"Lucida Sans Demibold Roman", "Lucida Sans Italic", "Lucida Sans Regular"};
4257 WCHAR bufW[LF_FULLFACESIZE];
4258 char bufA[LF_FULLFACESIZE];
4259 HFONT hfont, of;
4260 LOGFONTA lf;
4261 HDC hdc;
4262 int i;
4263 DWORD ret;
4265 hdc = CreateCompatibleDC(0);
4266 ok(hdc != NULL, "CreateCompatibleDC failed\n");
4268 memset(&lf, 0, sizeof(lf));
4269 lf.lfCharSet = ANSI_CHARSET;
4270 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
4271 lf.lfHeight = 16;
4272 lf.lfWidth = 16;
4273 lf.lfQuality = DEFAULT_QUALITY;
4274 lf.lfItalic = FALSE;
4275 lf.lfWeight = FW_DONTCARE;
4277 for (i = 0; i < sizeof(TestName) / sizeof(TestName[0]); i++)
4279 if (!is_font_installed_fullname("Lucida Sans", TestName[i]))
4281 skip("%s is not installed\n", TestName[i]);
4282 continue;
4285 lstrcpyA(lf.lfFaceName, TestName[i]);
4286 hfont = CreateFontIndirectA(&lf);
4287 ok(hfont != 0, "CreateFontIndirectA failed\n");
4289 of = SelectObject(hdc, hfont);
4290 bufW[0] = 0;
4291 bufA[0] = 0;
4292 ret = get_ttf_nametable_entry(hdc, TT_NAME_ID_FULL_NAME, bufW, sizeof(bufW), TT_MS_LANGID_ENGLISH_UNITED_STATES);
4293 ok(ret, "face full name could not be read\n");
4294 WideCharToMultiByte(CP_ACP, 0, bufW, -1, bufA, sizeof(bufA), NULL, FALSE);
4295 ok(!lstrcmpA(bufA, TestName[i]), "font full names don't match: %s != %s\n", TestName[i], bufA);
4296 SelectObject(hdc, of);
4297 DeleteObject(hfont);
4299 DeleteDC(hdc);
4302 static WCHAR *prepend_at(WCHAR *family)
4304 if (!family)
4305 return NULL;
4307 memmove(family + 1, family, (lstrlenW(family) + 1) * sizeof(WCHAR));
4308 family[0] = '@';
4309 return family;
4312 static void test_fullname2_helper(const char *Family)
4314 char *FamilyName, *FaceName, *StyleName, *otmStr;
4315 struct enum_fullname_data efnd;
4316 WCHAR *bufW;
4317 char *bufA;
4318 HFONT hfont, of;
4319 LOGFONTA lf;
4320 HDC hdc;
4321 int i;
4322 DWORD otm_size, ret, buf_size;
4323 OUTLINETEXTMETRICA *otm;
4324 BOOL want_vertical, get_vertical;
4325 want_vertical = ( Family[0] == '@' );
4327 hdc = CreateCompatibleDC(0);
4328 ok(hdc != NULL, "CreateCompatibleDC failed\n");
4330 memset(&lf, 0, sizeof(lf));
4331 lf.lfCharSet = DEFAULT_CHARSET;
4332 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
4333 lf.lfHeight = 16;
4334 lf.lfWidth = 16;
4335 lf.lfQuality = DEFAULT_QUALITY;
4336 lf.lfItalic = FALSE;
4337 lf.lfWeight = FW_DONTCARE;
4338 lstrcpy(lf.lfFaceName, Family);
4339 efnd.total = 0;
4340 EnumFontFamiliesExA(hdc, &lf, enum_fullname_data_proc, (LPARAM)&efnd, 0);
4341 if (efnd.total == 0)
4342 skip("%s is not installed\n", lf.lfFaceName);
4344 for (i = 0; i < efnd.total; i++)
4346 FamilyName = (char *)efnd.elf[i].elfLogFont.lfFaceName;
4347 FaceName = (char *)efnd.elf[i].elfFullName;
4348 StyleName = (char *)efnd.elf[i].elfStyle;
4350 trace("Checking font %s:\nFamilyName: %s; FaceName: %s; StyleName: %s\n", Family, FamilyName, FaceName, StyleName);
4352 get_vertical = ( FamilyName[0] == '@' );
4353 ok(get_vertical == want_vertical, "Vertical flags don't match: %s %s\n", Family, FamilyName);
4355 lstrcpyA(lf.lfFaceName, FaceName);
4356 hfont = CreateFontIndirectA(&lf);
4357 ok(hfont != 0, "CreateFontIndirectA failed\n");
4359 of = SelectObject(hdc, hfont);
4360 buf_size = GetFontData(hdc, MS_NAME_TAG, 0, NULL, 0);
4361 ok(buf_size != GDI_ERROR, "no name table found\n");
4362 if (buf_size == GDI_ERROR) continue;
4364 bufW = HeapAlloc(GetProcessHeap(), 0, buf_size);
4365 bufA = HeapAlloc(GetProcessHeap(), 0, buf_size);
4367 otm_size = GetOutlineTextMetricsA(hdc, 0, NULL);
4368 otm = HeapAlloc(GetProcessHeap(), 0, otm_size);
4369 memset(otm, 0, otm_size);
4370 ret = GetOutlineTextMetrics(hdc, otm_size, otm);
4371 ok(ret != 0, "GetOutlineTextMetrics fails!\n");
4372 if (ret == 0) continue;
4374 bufW[0] = 0;
4375 bufA[0] = 0;
4376 ret = get_ttf_nametable_entry(hdc, TT_NAME_ID_FONT_FAMILY, bufW, buf_size, GetSystemDefaultLangID());
4377 if (!ret)
4379 trace("no localized FONT_FAMILY found.\n");
4380 ret = get_ttf_nametable_entry(hdc, TT_NAME_ID_FONT_FAMILY, bufW, buf_size, TT_MS_LANGID_ENGLISH_UNITED_STATES);
4382 ok(ret, "FAMILY (family name) could not be read\n");
4383 if (want_vertical) bufW = prepend_at(bufW);
4384 WideCharToMultiByte(CP_ACP, 0, bufW, -1, bufA, buf_size, NULL, FALSE);
4385 ok(!lstrcmpA(FamilyName, bufA), "font family names don't match: returned %s, expect %s\n", FamilyName, bufA);
4386 otmStr = (LPSTR)otm + (UINT_PTR)otm->otmpFamilyName;
4387 ok(!lstrcmpA(FamilyName, otmStr), "FamilyName %s doesn't match otmpFamilyName %s\n", FamilyName, otmStr);
4389 bufW[0] = 0;
4390 bufA[0] = 0;
4391 ret = get_ttf_nametable_entry(hdc, TT_NAME_ID_FULL_NAME, bufW, buf_size, GetSystemDefaultLangID());
4392 if (!ret)
4394 trace("no localized FULL_NAME found.\n");
4395 ret = get_ttf_nametable_entry(hdc, TT_NAME_ID_FULL_NAME, bufW, buf_size, TT_MS_LANGID_ENGLISH_UNITED_STATES);
4397 ok(ret, "FULL_NAME (face name) could not be read\n");
4398 if (want_vertical) bufW = prepend_at(bufW);
4399 WideCharToMultiByte(CP_ACP, 0, bufW, -1, bufA, buf_size, NULL, FALSE);
4400 ok(!lstrcmpA(FaceName, bufA), "font face names don't match: returned %s, expect %s\n", FaceName, bufA);
4401 otmStr = (LPSTR)otm + (UINT_PTR)otm->otmpFaceName;
4402 ok(!lstrcmpA(FaceName, otmStr), "FaceName %s doesn't match otmpFaceName %s\n", FaceName, otmStr);
4404 bufW[0] = 0;
4405 bufA[0] = 0;
4406 ret = get_ttf_nametable_entry(hdc, TT_NAME_ID_FONT_SUBFAMILY, bufW, buf_size, GetSystemDefaultLangID());
4407 if (!ret)
4409 trace("no localized FONT_SUBFAMILY found.\n");
4410 ret = get_ttf_nametable_entry(hdc, TT_NAME_ID_FONT_SUBFAMILY, bufW, buf_size, TT_MS_LANGID_ENGLISH_UNITED_STATES);
4412 ok(ret, "SUBFAMILY (style name) could not be read\n");
4413 WideCharToMultiByte(CP_ACP, 0, bufW, -1, bufA, buf_size, NULL, FALSE);
4414 ok(!lstrcmpA(StyleName, bufA), "style names don't match: returned %s, expect %s\n", StyleName, bufA);
4415 otmStr = (LPSTR)otm + (UINT_PTR)otm->otmpStyleName;
4416 ok(!lstrcmpA(StyleName, otmStr), "StyleName %s doesn't match otmpStyleName %s\n", StyleName, otmStr);
4418 bufW[0] = 0;
4419 bufA[0] = 0;
4420 ret = get_ttf_nametable_entry(hdc, TT_NAME_ID_UNIQUE_ID, bufW, buf_size, GetSystemDefaultLangID());
4421 if (!ret)
4423 trace("no localized UNIQUE_ID found.\n");
4424 ret = get_ttf_nametable_entry(hdc, TT_NAME_ID_UNIQUE_ID, bufW, buf_size, TT_MS_LANGID_ENGLISH_UNITED_STATES);
4426 ok(ret, "UNIQUE_ID (full name) could not be read\n");
4427 WideCharToMultiByte(CP_ACP, 0, bufW, -1, bufA, buf_size, NULL, FALSE);
4428 otmStr = (LPSTR)otm + (UINT_PTR)otm->otmpFullName;
4429 ok(!lstrcmpA(otmStr, bufA), "UNIQUE ID (full name) doesn't match: returned %s, expect %s\n", otmStr, bufA);
4431 SelectObject(hdc, of);
4432 DeleteObject(hfont);
4434 HeapFree(GetProcessHeap(), 0, otm);
4435 HeapFree(GetProcessHeap(), 0, bufW);
4436 HeapFree(GetProcessHeap(), 0, bufA);
4438 DeleteDC(hdc);
4441 static void test_fullname2(void)
4443 test_fullname2_helper("Arial");
4444 test_fullname2_helper("DejaVu Sans");
4445 test_fullname2_helper("Lucida Sans");
4446 test_fullname2_helper("Tahoma");
4447 test_fullname2_helper("Webdings");
4448 test_fullname2_helper("Wingdings");
4449 test_fullname2_helper("SimSun");
4450 test_fullname2_helper("NSimSun");
4451 test_fullname2_helper("MingLiu");
4452 test_fullname2_helper("PMingLiu");
4453 test_fullname2_helper("WenQuanYi Micro Hei");
4454 test_fullname2_helper("MS UI Gothic");
4455 test_fullname2_helper("Ume UI Gothic");
4456 test_fullname2_helper("MS Gothic");
4457 test_fullname2_helper("Ume Gothic");
4458 test_fullname2_helper("MS PGothic");
4459 test_fullname2_helper("Ume P Gothic");
4460 test_fullname2_helper("Gulim");
4461 test_fullname2_helper("Batang");
4462 test_fullname2_helper("UnBatang");
4463 test_fullname2_helper("UnDotum");
4464 test_fullname2_helper("@SimSun");
4465 test_fullname2_helper("@NSimSun");
4466 test_fullname2_helper("@MingLiu");
4467 test_fullname2_helper("@PMingLiu");
4468 test_fullname2_helper("@WenQuanYi Micro Hei");
4469 test_fullname2_helper("@MS UI Gothic");
4470 test_fullname2_helper("@Ume UI Gothic");
4471 test_fullname2_helper("@MS Gothic");
4472 test_fullname2_helper("@Ume Gothic");
4473 test_fullname2_helper("@MS PGothic");
4474 test_fullname2_helper("@Ume P Gothic");
4475 test_fullname2_helper("@Gulim");
4476 test_fullname2_helper("@Batang");
4477 test_fullname2_helper("@UnBatang");
4478 test_fullname2_helper("@UnDotum");
4482 static BOOL write_ttf_file(const char *fontname, char *tmp_name)
4484 char tmp_path[MAX_PATH];
4485 HRSRC rsrc;
4486 void *rsrc_data;
4487 DWORD rsrc_size;
4488 HANDLE hfile;
4489 BOOL ret;
4491 SetLastError(0xdeadbeef);
4492 rsrc = FindResource(GetModuleHandle(0), fontname, RT_RCDATA);
4493 ok(rsrc != 0, "FindResource error %d\n", GetLastError());
4494 if (!rsrc) return FALSE;
4495 SetLastError(0xdeadbeef);
4496 rsrc_data = LockResource(LoadResource(GetModuleHandle(0), rsrc));
4497 ok(rsrc_data != 0, "LockResource error %d\n", GetLastError());
4498 if (!rsrc_data) return FALSE;
4499 SetLastError(0xdeadbeef);
4500 rsrc_size = SizeofResource(GetModuleHandle(0), rsrc);
4501 ok(rsrc_size != 0, "SizeofResource error %d\n", GetLastError());
4502 if (!rsrc_size) return FALSE;
4504 SetLastError(0xdeadbeef);
4505 ret = GetTempPath(MAX_PATH, tmp_path);
4506 ok(ret, "GetTempPath() error %d\n", GetLastError());
4507 SetLastError(0xdeadbeef);
4508 ret = GetTempFileName(tmp_path, "ttf", 0, tmp_name);
4509 ok(ret, "GetTempFileName() error %d\n", GetLastError());
4511 SetLastError(0xdeadbeef);
4512 hfile = CreateFile(tmp_name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
4513 ok(hfile != INVALID_HANDLE_VALUE, "CreateFile() error %d\n", GetLastError());
4514 if (hfile == INVALID_HANDLE_VALUE) return FALSE;
4516 SetLastError(0xdeadbeef);
4517 ret = WriteFile(hfile, rsrc_data, rsrc_size, &rsrc_size, NULL);
4518 ok(ret, "WriteFile() error %d\n", GetLastError());
4520 CloseHandle(hfile);
4521 return ret;
4524 static void test_GetGlyphOutline_empty_contour(void)
4526 HDC hdc;
4527 LOGFONTA lf;
4528 HFONT hfont, hfont_prev;
4529 TTPOLYGONHEADER *header;
4530 GLYPHMETRICS gm;
4531 char buf[1024];
4532 DWORD ret;
4534 memset(&lf, 0, sizeof(lf));
4535 lf.lfHeight = 72;
4536 lstrcpyA(lf.lfFaceName, "wine_test");
4538 hfont = CreateFontIndirectA(&lf);
4539 ok(hfont != 0, "CreateFontIndirectA error %u\n", GetLastError());
4541 hdc = GetDC(NULL);
4543 hfont_prev = SelectObject(hdc, hfont);
4544 ok(hfont_prev != NULL, "SelectObject failed\n");
4546 ret = GetGlyphOutlineW(hdc, 0xa8, GGO_NATIVE, &gm, 0, NULL, &mat);
4547 ok(ret == 228, "GetGlyphOutline returned %d, expected 228\n", ret);
4549 header = (TTPOLYGONHEADER*)buf;
4550 ret = GetGlyphOutlineW(hdc, 0xa8, GGO_NATIVE, &gm, sizeof(buf), buf, &mat);
4551 ok(ret == 228, "GetGlyphOutline returned %d, expected 228\n", ret);
4552 ok(header->cb == 36, "header->cb = %d, expected 36\n", header->cb);
4553 ok(header->dwType == TT_POLYGON_TYPE, "header->dwType = %d, expected TT_POLYGON_TYPE\n", header->dwType);
4554 header = (TTPOLYGONHEADER*)((char*)header+header->cb);
4555 ok(header->cb == 96, "header->cb = %d, expected 96\n", header->cb);
4556 header = (TTPOLYGONHEADER*)((char*)header+header->cb);
4557 ok(header->cb == 96, "header->cb = %d, expected 96\n", header->cb);
4559 SelectObject(hdc, hfont_prev);
4560 DeleteObject(hfont);
4561 ReleaseDC(NULL, hdc);
4564 static void test_GetGlyphOutline_metric_clipping(void)
4566 HDC hdc;
4567 LOGFONTA lf;
4568 HFONT hfont, hfont_prev;
4569 GLYPHMETRICS gm;
4570 TEXTMETRICA tm;
4571 DWORD ret;
4573 memset(&lf, 0, sizeof(lf));
4574 lf.lfHeight = 72;
4575 lstrcpyA(lf.lfFaceName, "wine_test");
4577 SetLastError(0xdeadbeef);
4578 hfont = CreateFontIndirectA(&lf);
4579 ok(hfont != 0, "CreateFontIndirectA error %u\n", GetLastError());
4581 hdc = GetDC(NULL);
4583 hfont_prev = SelectObject(hdc, hfont);
4584 ok(hfont_prev != NULL, "SelectObject failed\n");
4586 SetLastError(0xdeadbeef);
4587 ret = GetTextMetrics(hdc, &tm);
4588 ok(ret, "GetTextMetrics error %u\n", GetLastError());
4590 GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
4591 ok(gm.gmptGlyphOrigin.y <= tm.tmAscent,
4592 "Glyph top(%d) exceeds ascent(%d)\n",
4593 gm.gmptGlyphOrigin.y, tm.tmAscent);
4594 GetGlyphOutlineA(hdc, 'D', GGO_METRICS, &gm, 0, NULL, &mat);
4595 ok(gm.gmptGlyphOrigin.y - gm.gmBlackBoxY >= -tm.tmDescent,
4596 "Glyph bottom(%d) exceeds descent(%d)\n",
4597 gm.gmptGlyphOrigin.y - gm.gmBlackBoxY, -tm.tmDescent);
4599 SelectObject(hdc, hfont_prev);
4600 DeleteObject(hfont);
4601 ReleaseDC(NULL, hdc);
4604 static void test_CreateScalableFontResource(void)
4606 char ttf_name[MAX_PATH];
4607 char tmp_path[MAX_PATH];
4608 char fot_name[MAX_PATH];
4609 char *file_part;
4610 DWORD ret;
4611 int i;
4613 if (!pAddFontResourceExA || !pRemoveFontResourceExA)
4615 win_skip("AddFontResourceExA is not available on this platform\n");
4616 return;
4619 if (!write_ttf_file("wine_test.ttf", ttf_name))
4621 skip("Failed to create ttf file for testing\n");
4622 return;
4625 trace("created %s\n", ttf_name);
4627 ret = is_truetype_font_installed("wine_test");
4628 ok(!ret, "font wine_test should not be enumerated\n");
4630 ret = GetTempPath(MAX_PATH, tmp_path);
4631 ok(ret, "GetTempPath() error %d\n", GetLastError());
4632 ret = GetTempFileName(tmp_path, "fot", 0, fot_name);
4633 ok(ret, "GetTempFileName() error %d\n", GetLastError());
4635 ret = GetFileAttributes(fot_name);
4636 ok(ret != INVALID_FILE_ATTRIBUTES, "file %s does not exist\n", fot_name);
4638 SetLastError(0xdeadbeef);
4639 ret = CreateScalableFontResource(0, fot_name, ttf_name, NULL);
4640 ok(!ret, "CreateScalableFontResource() should fail\n");
4641 ok(GetLastError() == ERROR_FILE_EXISTS, "not expected error %d\n", GetLastError());
4643 SetLastError(0xdeadbeef);
4644 ret = CreateScalableFontResource(0, fot_name, ttf_name, "");
4645 ok(!ret, "CreateScalableFontResource() should fail\n");
4646 ok(GetLastError() == ERROR_FILE_EXISTS, "not expected error %d\n", GetLastError());
4648 file_part = strrchr(ttf_name, '\\');
4649 SetLastError(0xdeadbeef);
4650 ret = CreateScalableFontResource(0, fot_name, file_part, tmp_path);
4651 ok(!ret, "CreateScalableFontResource() should fail\n");
4652 ok(GetLastError() == ERROR_FILE_EXISTS, "not expected error %d\n", GetLastError());
4654 SetLastError(0xdeadbeef);
4655 ret = CreateScalableFontResource(0, fot_name, "random file name", tmp_path);
4656 ok(!ret, "CreateScalableFontResource() should fail\n");
4657 ok(GetLastError() == ERROR_INVALID_PARAMETER, "not expected error %d\n", GetLastError());
4659 SetLastError(0xdeadbeef);
4660 ret = CreateScalableFontResource(0, fot_name, NULL, ttf_name);
4661 ok(!ret, "CreateScalableFontResource() should fail\n");
4662 ok(GetLastError() == ERROR_INVALID_PARAMETER, "not expected error %d\n", GetLastError());
4664 ret = DeleteFile(fot_name);
4665 ok(ret, "DeleteFile() error %d\n", GetLastError());
4667 ret = pRemoveFontResourceExA(fot_name, 0, 0);
4668 ok(!ret, "RemoveFontResourceEx() should fail\n");
4670 /* test public font resource */
4671 SetLastError(0xdeadbeef);
4672 ret = CreateScalableFontResource(0, fot_name, ttf_name, NULL);
4673 ok(ret, "CreateScalableFontResource() error %d\n", GetLastError());
4675 ret = is_truetype_font_installed("wine_test");
4676 ok(!ret, "font wine_test should not be enumerated\n");
4678 SetLastError(0xdeadbeef);
4679 ret = pAddFontResourceExA(fot_name, 0, 0);
4680 ok(ret, "AddFontResourceEx() error %d\n", GetLastError());
4682 ret = is_truetype_font_installed("wine_test");
4683 ok(ret, "font wine_test should be enumerated\n");
4685 test_GetGlyphOutline_empty_contour();
4686 test_GetGlyphOutline_metric_clipping();
4688 ret = pRemoveFontResourceExA(fot_name, FR_PRIVATE, 0);
4689 ok(!ret, "RemoveFontResourceEx() with not matching flags should fail\n");
4691 SetLastError(0xdeadbeef);
4692 ret = pRemoveFontResourceExA(fot_name, 0, 0);
4693 ok(ret, "RemoveFontResourceEx() error %d\n", GetLastError());
4695 ret = is_truetype_font_installed("wine_test");
4696 ok(!ret, "font wine_test should not be enumerated\n");
4698 ret = pRemoveFontResourceExA(fot_name, 0, 0);
4699 ok(!ret, "RemoveFontResourceEx() should fail\n");
4701 /* test refcounting */
4702 for (i = 0; i < 5; i++)
4704 SetLastError(0xdeadbeef);
4705 ret = pAddFontResourceExA(fot_name, 0, 0);
4706 ok(ret, "AddFontResourceEx() error %d\n", GetLastError());
4708 for (i = 0; i < 5; i++)
4710 SetLastError(0xdeadbeef);
4711 ret = pRemoveFontResourceExA(fot_name, 0, 0);
4712 ok(ret, "RemoveFontResourceEx() error %d\n", GetLastError());
4714 ret = pRemoveFontResourceExA(fot_name, 0, 0);
4715 ok(!ret, "RemoveFontResourceEx() should fail\n");
4717 DeleteFile(fot_name);
4719 /* test hidden font resource */
4720 SetLastError(0xdeadbeef);
4721 ret = CreateScalableFontResource(1, fot_name, ttf_name, NULL);
4722 ok(ret, "CreateScalableFontResource() error %d\n", GetLastError());
4724 ret = is_truetype_font_installed("wine_test");
4725 ok(!ret, "font wine_test should not be enumerated\n");
4727 SetLastError(0xdeadbeef);
4728 ret = pAddFontResourceExA(fot_name, 0, 0);
4729 ok(ret, "AddFontResourceEx() error %d\n", GetLastError());
4731 ret = is_truetype_font_installed("wine_test");
4732 todo_wine
4733 ok(!ret, "font wine_test should not be enumerated\n");
4735 /* XP allows removing a private font added with 0 flags */
4736 SetLastError(0xdeadbeef);
4737 ret = pRemoveFontResourceExA(fot_name, FR_PRIVATE, 0);
4738 ok(ret, "RemoveFontResourceEx() error %d\n", GetLastError());
4740 ret = is_truetype_font_installed("wine_test");
4741 ok(!ret, "font wine_test should not be enumerated\n");
4743 ret = pRemoveFontResourceExA(fot_name, 0, 0);
4744 ok(!ret, "RemoveFontResourceEx() should fail\n");
4746 DeleteFile(fot_name);
4747 DeleteFile(ttf_name);
4750 static void check_vertical_font(const char *name, BOOL *installed, BOOL *selected, GLYPHMETRICS *gm, WORD *gi)
4752 LOGFONTA lf;
4753 HFONT hfont, hfont_prev;
4754 HDC hdc;
4755 char facename[100];
4756 DWORD ret;
4757 static const WCHAR str[] = { 0x2025 };
4759 *installed = is_truetype_font_installed(name);
4761 lf.lfHeight = -18;
4762 lf.lfWidth = 0;
4763 lf.lfEscapement = 0;
4764 lf.lfOrientation = 0;
4765 lf.lfWeight = FW_DONTCARE;
4766 lf.lfItalic = 0;
4767 lf.lfUnderline = 0;
4768 lf.lfStrikeOut = 0;
4769 lf.lfCharSet = DEFAULT_CHARSET;
4770 lf.lfOutPrecision = OUT_TT_ONLY_PRECIS;
4771 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
4772 lf.lfQuality = DEFAULT_QUALITY;
4773 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
4774 strcpy(lf.lfFaceName, name);
4776 hfont = CreateFontIndirectA(&lf);
4777 ok(hfont != NULL, "CreateFontIndirectA failed\n");
4779 hdc = GetDC(NULL);
4781 hfont_prev = SelectObject(hdc, hfont);
4782 ok(hfont_prev != NULL, "SelectObject failed\n");
4784 ret = GetTextFaceA(hdc, sizeof facename, facename);
4785 ok(ret, "GetTextFaceA failed\n");
4786 *selected = !strcmp(facename, name);
4788 ret = GetGlyphOutlineW(hdc, 0x2025, GGO_METRICS, gm, 0, NULL, &mat);
4789 ok(ret != GDI_ERROR, "GetGlyphOutlineW failed\n");
4790 if (!*selected)
4791 memset(gm, 0, sizeof *gm);
4793 ret = pGetGlyphIndicesW(hdc, str, 1, gi, 0);
4794 ok(ret != GDI_ERROR, "GetGlyphIndicesW failed\n");
4796 SelectObject(hdc, hfont_prev);
4797 DeleteObject(hfont);
4798 ReleaseDC(NULL, hdc);
4801 static void test_vertical_font(void)
4803 char ttf_name[MAX_PATH];
4804 int num;
4805 BOOL ret, installed, selected;
4806 GLYPHMETRICS gm;
4807 WORD hgi, vgi;
4809 if (!pAddFontResourceExA || !pRemoveFontResourceExA || !pGetGlyphIndicesW)
4811 win_skip("AddFontResourceExA or GetGlyphIndicesW is not available on this platform\n");
4812 return;
4815 if (!write_ttf_file("vertical.ttf", ttf_name))
4817 skip("Failed to create ttf file for testing\n");
4818 return;
4821 num = pAddFontResourceExA(ttf_name, FR_PRIVATE, 0);
4822 ok(num == 2, "AddFontResourceExA should add 2 fonts from vertical.ttf\n");
4824 check_vertical_font("@WineTestVertical", &installed, &selected, &gm, &hgi);
4825 ok(installed, "@WineTestVertical is not installed\n");
4826 ok(selected, "@WineTestVertical is not selected\n");
4827 ok(gm.gmBlackBoxX > gm.gmBlackBoxY,
4828 "gmBlackBoxX(%u) should be greater than gmBlackBoxY(%u) if horizontal\n",
4829 gm.gmBlackBoxX, gm.gmBlackBoxY);
4831 check_vertical_font("@@WineTestVertical", &installed, &selected, &gm, &vgi);
4832 ok(installed, "@@WineTestVertical is not installed\n");
4833 ok(selected, "@@WineTestVertical is not selected\n");
4834 ok(gm.gmBlackBoxX < gm.gmBlackBoxY,
4835 "gmBlackBoxX(%u) should be less than gmBlackBoxY(%u) if vertical\n",
4836 gm.gmBlackBoxX, gm.gmBlackBoxY);
4838 ok(hgi == vgi, "different glyph h:%u v:%u\n", hgi, vgi);
4840 ret = pRemoveFontResourceExA(ttf_name, FR_PRIVATE, 0);
4841 ok(ret, "RemoveFontResourceEx() error %d\n", GetLastError());
4843 DeleteFile(ttf_name);
4846 static INT CALLBACK has_vertical_font_proc(const LOGFONT *lf, const TEXTMETRIC *ntm,
4847 DWORD type, LPARAM lParam)
4849 if (lf->lfFaceName[0] == '@') {
4850 return 0;
4852 return 1;
4855 static void test_east_asian_font_selection(void)
4857 HDC hdc;
4858 UINT charset[] = { SHIFTJIS_CHARSET, HANGEUL_CHARSET, JOHAB_CHARSET,
4859 GB2312_CHARSET, CHINESEBIG5_CHARSET };
4860 size_t i;
4862 hdc = GetDC(NULL);
4864 for (i = 0; i < sizeof(charset)/sizeof(charset[0]); i++)
4866 LOGFONTA lf;
4867 HFONT hfont;
4868 char face_name[LF_FACESIZE];
4869 int ret;
4871 memset(&lf, 0, sizeof lf);
4872 lf.lfFaceName[0] = '\0';
4873 lf.lfCharSet = charset[i];
4875 if (EnumFontFamiliesEx(hdc, &lf, has_vertical_font_proc, 0, 0))
4877 skip("Vertical font for charset %u is not installed\n", charset[i]);
4878 continue;
4881 hfont = CreateFontIndirectA(&lf);
4882 hfont = SelectObject(hdc, hfont);
4883 memset(face_name, 0, sizeof face_name);
4884 ret = GetTextFaceA(hdc, sizeof face_name, face_name);
4885 ok(ret && face_name[0] != '@',
4886 "expected non-vertical face for charset %u, got %s\n", charset[i], face_name);
4887 DeleteObject(SelectObject(hdc, hfont));
4889 memset(&lf, 0, sizeof lf);
4890 strcpy(lf.lfFaceName, "@");
4891 lf.lfCharSet = charset[i];
4892 hfont = CreateFontIndirectA(&lf);
4893 hfont = SelectObject(hdc, hfont);
4894 memset(face_name, 0, sizeof face_name);
4895 ret = GetTextFaceA(hdc, sizeof face_name, face_name);
4896 ok(ret && face_name[0] == '@',
4897 "expected vertical face for charset %u, got %s\n", charset[i], face_name);
4898 DeleteObject(SelectObject(hdc, hfont));
4900 ReleaseDC(NULL, hdc);
4903 static int get_font_dpi(const LOGFONT *lf)
4905 HDC hdc = CreateCompatibleDC(0);
4906 HFONT hfont;
4907 TEXTMETRIC tm;
4908 int ret;
4910 hfont = CreateFontIndirect(lf);
4911 ok(hfont != 0, "CreateFontIndirect failed\n");
4913 SelectObject(hdc, hfont);
4914 ret = GetTextMetrics(hdc, &tm);
4915 ok(ret, "GetTextMetrics failed\n");
4916 ret = tm.tmDigitizedAspectX;
4918 DeleteDC(hdc);
4919 DeleteObject(hfont);
4921 return ret;
4924 static void test_stock_fonts(void)
4926 static const int font[] =
4928 ANSI_FIXED_FONT, ANSI_VAR_FONT, SYSTEM_FONT, DEVICE_DEFAULT_FONT, DEFAULT_GUI_FONT
4929 /* SYSTEM_FIXED_FONT, OEM_FIXED_FONT */
4931 static const struct test_data
4933 int charset, weight, height, dpi;
4934 const char face_name[LF_FACESIZE];
4935 } td[][11] =
4937 { /* ANSI_FIXED_FONT */
4938 { DEFAULT_CHARSET, FW_NORMAL, 12, 96, "Courier" },
4939 { DEFAULT_CHARSET, FW_NORMAL, 12, 120, "Courier" },
4940 { 0 }
4942 { /* ANSI_VAR_FONT */
4943 { DEFAULT_CHARSET, FW_NORMAL, 12, 96, "MS Sans Serif" },
4944 { DEFAULT_CHARSET, FW_NORMAL, 12, 120, "MS Sans Serif" },
4945 { 0 }
4947 { /* SYSTEM_FONT */
4948 { SHIFTJIS_CHARSET, FW_NORMAL, 18, 96, "System" },
4949 { SHIFTJIS_CHARSET, FW_NORMAL, 22, 120, "System" },
4950 { HANGEUL_CHARSET, FW_NORMAL, 16, 96, "System" },
4951 { HANGEUL_CHARSET, FW_NORMAL, 20, 120, "System" },
4952 { DEFAULT_CHARSET, FW_BOLD, 16, 96, "System" },
4953 { DEFAULT_CHARSET, FW_BOLD, 20, 120, "System" },
4954 { 0 }
4956 { /* DEVICE_DEFAULT_FONT */
4957 { SHIFTJIS_CHARSET, FW_NORMAL, 18, 96, "System" },
4958 { SHIFTJIS_CHARSET, FW_NORMAL, 22, 120, "System" },
4959 { HANGEUL_CHARSET, FW_NORMAL, 16, 96, "System" },
4960 { HANGEUL_CHARSET, FW_NORMAL, 20, 120, "System" },
4961 { DEFAULT_CHARSET, FW_BOLD, 16, 96, "System" },
4962 { DEFAULT_CHARSET, FW_BOLD, 20, 120, "System" },
4963 { 0 }
4965 { /* DEFAULT_GUI_FONT */
4966 { SHIFTJIS_CHARSET, FW_NORMAL, -12, 96, "?MS UI Gothic" },
4967 { SHIFTJIS_CHARSET, FW_NORMAL, -15, 120, "?MS UI Gothic" },
4968 { HANGEUL_CHARSET, FW_NORMAL, -12, 96, "?Gulim" },
4969 { HANGEUL_CHARSET, FW_NORMAL, -15, 120, "?Gulim" },
4970 { GB2312_CHARSET, FW_NORMAL, -12, 96, "?SimHei" },
4971 { GB2312_CHARSET, FW_NORMAL, -15, 120, "?SimHei" },
4972 { CHINESEBIG5_CHARSET, FW_NORMAL, -12, 96, "?MingLiU" },
4973 { CHINESEBIG5_CHARSET, FW_NORMAL, -15, 120, "?MingLiU" },
4974 { DEFAULT_CHARSET, FW_NORMAL, -11, 96, "MS Shell Dlg" },
4975 { DEFAULT_CHARSET, FW_NORMAL, -13, 120, "MS Shell Dlg" },
4976 { 0 }
4979 int i, j;
4981 for (i = 0; i < sizeof(font)/sizeof(font[0]); i++)
4983 HFONT hfont;
4984 LOGFONT lf;
4985 int ret;
4987 hfont = GetStockObject(font[i]);
4988 ok(hfont != 0, "%d: GetStockObject(%d) failed\n", i, font[i]);
4990 ret = GetObject(hfont, sizeof(lf), &lf);
4991 if (ret != sizeof(lf))
4993 /* NT4 */
4994 win_skip("%d: GetObject returned %d instead of sizeof(LOGFONT)\n", i, ret);
4995 continue;
4998 for (j = 0; td[i][j].face_name[0] != 0; j++)
5000 if (lf.lfCharSet != td[i][j].charset && td[i][j].charset != DEFAULT_CHARSET)
5002 continue;
5005 ret = get_font_dpi(&lf);
5006 if (ret != td[i][j].dpi)
5008 trace("%d(%d): font %s %d dpi doesn't match test data %d\n",
5009 i, j, lf.lfFaceName, ret, td[i][j].dpi);
5010 continue;
5013 ok(td[i][j].weight == lf.lfWeight, "%d(%d): expected lfWeight %d, got %d\n", i, j, td[i][j].weight, lf.lfWeight);
5014 ok(td[i][j].height == lf.lfHeight, "%d(%d): expected lfHeight %d, got %d\n", i, j, td[i][j].height, lf.lfHeight);
5015 if (td[i][j].face_name[0] == '?')
5017 /* Wine doesn't have this font, skip this case for now.
5018 Actually, the face name is localized on Windows and varies
5019 dpending on Windows versions (e.g. Japanese NT4 vs win2k). */
5020 trace("%d(%d): default gui font is %s\n", i, j, lf.lfFaceName);
5022 else
5024 ok(!lstrcmp(td[i][j].face_name, lf.lfFaceName), "%d(%d): expected lfFaceName %s, got %s\n", i, j, td[i][j].face_name, lf.lfFaceName);
5026 break;
5031 static void test_max_height(void)
5033 HDC hdc;
5034 LOGFONT lf;
5035 HFONT hfont, hfont_old;
5036 TEXTMETRICA tm1, tm;
5037 BOOL r;
5038 LONG invalid_height[] = { -65536, -123456, 123456 };
5039 size_t i;
5041 memset(&tm1, 0, sizeof(tm1));
5042 memset(&lf, 0, sizeof(lf));
5043 strcpy(lf.lfFaceName, "Tahoma");
5044 lf.lfHeight = -1;
5046 hdc = GetDC(NULL);
5048 /* get 1 ppem value */
5049 hfont = CreateFontIndirect(&lf);
5050 hfont_old = SelectObject(hdc, hfont);
5051 r = GetTextMetrics(hdc, &tm1);
5052 ok(r, "GetTextMetrics failed\n");
5053 ok(tm1.tmHeight > 0, "expected a positive value, got %d\n", tm1.tmHeight);
5054 ok(tm1.tmAveCharWidth > 0, "expected a positive value, got %d\n", tm1.tmHeight);
5055 DeleteObject(SelectObject(hdc, hfont_old));
5057 /* test the largest value */
5058 lf.lfHeight = -((1 << 16) - 1);
5059 hfont = CreateFontIndirect(&lf);
5060 hfont_old = SelectObject(hdc, hfont);
5061 memset(&tm, 0, sizeof(tm));
5062 r = GetTextMetrics(hdc, &tm);
5063 ok(r, "GetTextMetrics failed\n");
5064 ok(tm.tmHeight > tm1.tmHeight,
5065 "expected greater than 1 ppem value (%d), got %d\n", tm1.tmHeight, tm.tmHeight);
5066 ok(tm.tmAveCharWidth > tm1.tmAveCharWidth,
5067 "expected greater than 1 ppem value (%d), got %d\n", tm1.tmAveCharWidth, tm.tmAveCharWidth);
5068 DeleteObject(SelectObject(hdc, hfont_old));
5070 /* test an invalid value */
5071 for (i = 0; i < sizeof(invalid_height)/sizeof(invalid_height[0]); i++) {
5072 lf.lfHeight = invalid_height[i];
5073 hfont = CreateFontIndirect(&lf);
5074 hfont_old = SelectObject(hdc, hfont);
5075 memset(&tm, 0, sizeof(tm));
5076 r = GetTextMetrics(hdc, &tm);
5077 ok(r, "GetTextMetrics failed\n");
5078 ok(tm.tmHeight == tm1.tmHeight,
5079 "expected 1 ppem value (%d), got %d\n", tm1.tmHeight, tm.tmHeight);
5080 ok(tm.tmAveCharWidth == tm1.tmAveCharWidth,
5081 "expected 1 ppem value (%d), got %d\n", tm1.tmAveCharWidth, tm.tmAveCharWidth);
5082 DeleteObject(SelectObject(hdc, hfont_old));
5085 ReleaseDC(NULL, hdc);
5086 return;
5089 START_TEST(font)
5091 init();
5093 test_stock_fonts();
5094 test_logfont();
5095 test_bitmap_font();
5096 test_outline_font();
5097 test_bitmap_font_metrics();
5098 test_GdiGetCharDimensions();
5099 test_GetCharABCWidths();
5100 test_text_extents();
5101 test_GetGlyphIndices();
5102 test_GetKerningPairs();
5103 test_GetOutlineTextMetrics();
5104 test_SetTextJustification();
5105 test_font_charset();
5106 test_GdiGetCodePage();
5107 test_GetFontUnicodeRanges();
5108 test_nonexistent_font();
5109 test_orientation();
5110 test_height_selection();
5111 test_AddFontMemResource();
5112 test_EnumFonts();
5114 /* On Windows Arial has a lot of default charset aliases such as Arial Cyr,
5115 * I'd like to avoid them in this test.
5117 test_EnumFontFamilies("Arial Black", ANSI_CHARSET);
5118 test_EnumFontFamilies("Symbol", SYMBOL_CHARSET);
5119 if (is_truetype_font_installed("Arial Black") &&
5120 (is_truetype_font_installed("Symbol") || is_truetype_font_installed("Wingdings")))
5122 test_EnumFontFamilies("", ANSI_CHARSET);
5123 test_EnumFontFamilies("", SYMBOL_CHARSET);
5124 test_EnumFontFamilies("", DEFAULT_CHARSET);
5126 else
5127 skip("Arial Black or Symbol/Wingdings is not installed\n");
5128 test_EnumFontFamiliesEx_default_charset();
5129 test_GetTextMetrics();
5130 test_GdiRealizationInfo();
5131 test_GetTextFace();
5132 test_GetGlyphOutline();
5133 test_GetTextMetrics2("Tahoma", -11);
5134 test_GetTextMetrics2("Tahoma", -55);
5135 test_GetTextMetrics2("Tahoma", -110);
5136 test_GetTextMetrics2("Arial", -11);
5137 test_GetTextMetrics2("Arial", -55);
5138 test_GetTextMetrics2("Arial", -110);
5139 test_CreateFontIndirect();
5140 test_CreateFontIndirectEx();
5141 test_oemcharset();
5142 test_fullname();
5143 test_fullname2();
5144 test_east_asian_font_selection();
5145 test_max_height();
5147 /* These tests should be last test until RemoveFontResource
5148 * is properly implemented.
5150 test_vertical_font();
5151 test_CreateScalableFontResource();