gdi32: Ensure a fixed-pitch full-width character has double advance of a half-width...
[wine/multimedia.git] / dlls / gdi32 / tests / font.c
blob943f584271a1e703aea9ca2e3455691de5b226a1
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_GetFontUnicodeRanges(void)
2088 LOGFONTA lf;
2089 HDC hdc;
2090 HFONT hfont, hfont_old;
2091 DWORD size;
2092 GLYPHSET *gs;
2093 DWORD i;
2095 if (!pGetFontUnicodeRanges)
2097 win_skip("GetFontUnicodeRanges not available before W2K\n");
2098 return;
2101 memset(&lf, 0, sizeof(lf));
2102 lstrcpyA(lf.lfFaceName, "Arial");
2103 hfont = create_font("Arial", &lf);
2105 hdc = GetDC(0);
2106 hfont_old = SelectObject(hdc, hfont);
2108 size = pGetFontUnicodeRanges(NULL, NULL);
2109 ok(!size, "GetFontUnicodeRanges succeeded unexpectedly\n");
2111 size = pGetFontUnicodeRanges(hdc, NULL);
2112 ok(size, "GetFontUnicodeRanges failed unexpectedly\n");
2114 gs = HeapAlloc(GetProcessHeap(), 0, size);
2116 size = pGetFontUnicodeRanges(hdc, gs);
2117 ok(size, "GetFontUnicodeRanges failed\n");
2119 if (0) /* Disabled to limit console spam */
2120 for (i = 0; i < gs->cRanges; i++)
2121 trace("%03d wcLow %04x cGlyphs %u\n", i, gs->ranges[i].wcLow, gs->ranges[i].cGlyphs);
2122 trace("found %u ranges\n", gs->cRanges);
2124 HeapFree(GetProcessHeap(), 0, gs);
2126 SelectObject(hdc, hfont_old);
2127 DeleteObject(hfont);
2128 ReleaseDC(NULL, hdc);
2131 #define MAX_ENUM_FONTS 4096
2133 struct enum_font_data
2135 int total;
2136 LOGFONT lf[MAX_ENUM_FONTS];
2139 struct enum_fullname_data
2141 int total;
2142 ENUMLOGFONT elf[MAX_ENUM_FONTS];
2145 struct enum_font_dataW
2147 int total;
2148 LOGFONTW lf[MAX_ENUM_FONTS];
2151 static INT CALLBACK arial_enum_proc(const LOGFONT *lf, const TEXTMETRIC *tm, DWORD type, LPARAM lParam)
2153 struct enum_font_data *efd = (struct enum_font_data *)lParam;
2154 const NEWTEXTMETRIC *ntm = (const NEWTEXTMETRIC *)tm;
2156 ok(lf->lfHeight == tm->tmHeight, "lfHeight %d != tmHeight %d\n", lf->lfHeight, tm->tmHeight);
2157 ok(lf->lfHeight > 0 && lf->lfHeight < 200, "enumerated font height %d\n", lf->lfHeight);
2159 if (type != TRUETYPE_FONTTYPE) return 1;
2161 ok(ntm->ntmCellHeight + ntm->ntmCellHeight/5 >= ntm->ntmSizeEM, "ntmCellHeight %d should be close to ntmSizeEM %d\n", ntm->ntmCellHeight, ntm->ntmSizeEM);
2163 if (0) /* Disabled to limit console spam */
2164 trace("enumed font \"%s\", charset %d, height %d, weight %d, italic %d\n",
2165 lf->lfFaceName, lf->lfCharSet, lf->lfHeight, lf->lfWeight, lf->lfItalic);
2166 if (efd->total < MAX_ENUM_FONTS)
2167 efd->lf[efd->total++] = *lf;
2168 else
2169 trace("enum tests invalid; you have more than %d fonts\n", MAX_ENUM_FONTS);
2171 return 1;
2174 static INT CALLBACK arial_enum_procw(const LOGFONTW *lf, const TEXTMETRICW *tm, DWORD type, LPARAM lParam)
2176 struct enum_font_dataW *efd = (struct enum_font_dataW *)lParam;
2177 const NEWTEXTMETRICW *ntm = (const NEWTEXTMETRICW *)tm;
2179 ok(lf->lfHeight == tm->tmHeight, "lfHeight %d != tmHeight %d\n", lf->lfHeight, tm->tmHeight);
2180 ok(lf->lfHeight > 0 && lf->lfHeight < 200, "enumerated font height %d\n", lf->lfHeight);
2182 if (type != TRUETYPE_FONTTYPE) return 1;
2184 ok(ntm->ntmCellHeight + ntm->ntmCellHeight/5 >= ntm->ntmSizeEM, "ntmCellHeight %d should be close to ntmSizeEM %d\n", ntm->ntmCellHeight, ntm->ntmSizeEM);
2186 if (0) /* Disabled to limit console spam */
2187 trace("enumed font %s, charset %d, height %d, weight %d, italic %d\n",
2188 wine_dbgstr_w(lf->lfFaceName), lf->lfCharSet, lf->lfHeight, lf->lfWeight, lf->lfItalic);
2189 if (efd->total < MAX_ENUM_FONTS)
2190 efd->lf[efd->total++] = *lf;
2191 else
2192 trace("enum tests invalid; you have more than %d fonts\n", MAX_ENUM_FONTS);
2194 return 1;
2197 static void get_charset_stats(struct enum_font_data *efd,
2198 int *ansi_charset, int *symbol_charset,
2199 int *russian_charset)
2201 int i;
2203 *ansi_charset = 0;
2204 *symbol_charset = 0;
2205 *russian_charset = 0;
2207 for (i = 0; i < efd->total; i++)
2209 switch (efd->lf[i].lfCharSet)
2211 case ANSI_CHARSET:
2212 (*ansi_charset)++;
2213 break;
2214 case SYMBOL_CHARSET:
2215 (*symbol_charset)++;
2216 break;
2217 case RUSSIAN_CHARSET:
2218 (*russian_charset)++;
2219 break;
2224 static void get_charset_statsW(struct enum_font_dataW *efd,
2225 int *ansi_charset, int *symbol_charset,
2226 int *russian_charset)
2228 int i;
2230 *ansi_charset = 0;
2231 *symbol_charset = 0;
2232 *russian_charset = 0;
2234 for (i = 0; i < efd->total; i++)
2236 switch (efd->lf[i].lfCharSet)
2238 case ANSI_CHARSET:
2239 (*ansi_charset)++;
2240 break;
2241 case SYMBOL_CHARSET:
2242 (*symbol_charset)++;
2243 break;
2244 case RUSSIAN_CHARSET:
2245 (*russian_charset)++;
2246 break;
2251 static void test_EnumFontFamilies(const char *font_name, INT font_charset)
2253 struct enum_font_data efd;
2254 struct enum_font_dataW efdw;
2255 LOGFONT lf;
2256 HDC hdc;
2257 int i, ret, ansi_charset, symbol_charset, russian_charset;
2259 trace("Testing font %s, charset %d\n", *font_name ? font_name : "<empty>", font_charset);
2261 if (*font_name && !is_truetype_font_installed(font_name))
2263 skip("%s is not installed\n", font_name);
2264 return;
2267 hdc = GetDC(0);
2269 /* Observed behaviour: EnumFontFamilies enumerates aliases like "Arial Cyr"
2270 * while EnumFontFamiliesEx doesn't.
2272 if (!*font_name && font_charset == DEFAULT_CHARSET) /* do it only once */
2275 * Use EnumFontFamiliesW since win98 crashes when the
2276 * second parameter is NULL using EnumFontFamilies
2278 efdw.total = 0;
2279 SetLastError(0xdeadbeef);
2280 ret = EnumFontFamiliesW(hdc, NULL, arial_enum_procw, (LPARAM)&efdw);
2281 ok(ret || GetLastError() == ERROR_CALL_NOT_IMPLEMENTED, "EnumFontFamiliesW error %u\n", GetLastError());
2282 if(ret)
2284 get_charset_statsW(&efdw, &ansi_charset, &symbol_charset, &russian_charset);
2285 trace("enumerated ansi %d, symbol %d, russian %d fonts for NULL\n",
2286 ansi_charset, symbol_charset, russian_charset);
2287 ok(efdw.total > 0, "fonts enumerated: NULL\n");
2288 ok(ansi_charset > 0, "NULL family should enumerate ANSI_CHARSET\n");
2289 ok(symbol_charset > 0, "NULL family should enumerate SYMBOL_CHARSET\n");
2290 ok(russian_charset > 0 ||
2291 broken(russian_charset == 0), /* NT4 */
2292 "NULL family should enumerate RUSSIAN_CHARSET\n");
2295 efdw.total = 0;
2296 SetLastError(0xdeadbeef);
2297 ret = EnumFontFamiliesExW(hdc, NULL, arial_enum_procw, (LPARAM)&efdw, 0);
2298 ok(ret || GetLastError() == ERROR_CALL_NOT_IMPLEMENTED, "EnumFontFamiliesExW error %u\n", GetLastError());
2299 if(ret)
2301 get_charset_statsW(&efdw, &ansi_charset, &symbol_charset, &russian_charset);
2302 trace("enumerated ansi %d, symbol %d, russian %d fonts for NULL\n",
2303 ansi_charset, symbol_charset, russian_charset);
2304 ok(efdw.total > 0, "fonts enumerated: NULL\n");
2305 ok(ansi_charset > 0, "NULL family should enumerate ANSI_CHARSET\n");
2306 ok(symbol_charset > 0, "NULL family should enumerate SYMBOL_CHARSET\n");
2307 ok(russian_charset > 0, "NULL family should enumerate RUSSIAN_CHARSET\n");
2311 efd.total = 0;
2312 SetLastError(0xdeadbeef);
2313 ret = EnumFontFamilies(hdc, font_name, arial_enum_proc, (LPARAM)&efd);
2314 ok(ret, "EnumFontFamilies error %u\n", GetLastError());
2315 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
2316 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s\n",
2317 ansi_charset, symbol_charset, russian_charset,
2318 *font_name ? font_name : "<empty>");
2319 if (*font_name)
2320 ok(efd.total > 0, "no fonts enumerated: %s\n", font_name);
2321 else
2322 ok(!efd.total, "no fonts should be enumerated for empty font_name\n");
2323 for (i = 0; i < efd.total; i++)
2325 /* FIXME: remove completely once Wine is fixed */
2326 if (efd.lf[i].lfCharSet != font_charset)
2328 todo_wine
2329 ok(efd.lf[i].lfCharSet == font_charset, "%d: got charset %d\n", i, efd.lf[i].lfCharSet);
2331 else
2332 ok(efd.lf[i].lfCharSet == font_charset, "%d: got charset %d\n", i, efd.lf[i].lfCharSet);
2333 ok(!lstrcmp(efd.lf[i].lfFaceName, font_name), "expected %s, got %s\n",
2334 font_name, efd.lf[i].lfFaceName);
2337 memset(&lf, 0, sizeof(lf));
2338 lf.lfCharSet = ANSI_CHARSET;
2339 lstrcpy(lf.lfFaceName, font_name);
2340 efd.total = 0;
2341 SetLastError(0xdeadbeef);
2342 ret = EnumFontFamiliesEx(hdc, &lf, arial_enum_proc, (LPARAM)&efd, 0);
2343 ok(ret, "EnumFontFamiliesEx error %u\n", GetLastError());
2344 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
2345 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s ANSI_CHARSET\n",
2346 ansi_charset, symbol_charset, russian_charset,
2347 *font_name ? font_name : "<empty>");
2348 if (font_charset == SYMBOL_CHARSET)
2350 if (*font_name)
2351 ok(efd.total == 0, "no fonts should be enumerated: %s ANSI_CHARSET\n", font_name);
2352 else
2353 ok(efd.total > 0, "no fonts enumerated: %s\n", font_name);
2355 else
2357 ok(efd.total > 0, "no fonts enumerated: %s ANSI_CHARSET\n", font_name);
2358 for (i = 0; i < efd.total; i++)
2360 ok(efd.lf[i].lfCharSet == ANSI_CHARSET, "%d: got charset %d\n", i, efd.lf[i].lfCharSet);
2361 if (*font_name)
2362 ok(!lstrcmp(efd.lf[i].lfFaceName, font_name), "expected %s, got %s\n",
2363 font_name, efd.lf[i].lfFaceName);
2367 /* DEFAULT_CHARSET should enumerate all available charsets */
2368 memset(&lf, 0, sizeof(lf));
2369 lf.lfCharSet = DEFAULT_CHARSET;
2370 lstrcpy(lf.lfFaceName, font_name);
2371 efd.total = 0;
2372 SetLastError(0xdeadbeef);
2373 EnumFontFamiliesEx(hdc, &lf, arial_enum_proc, (LPARAM)&efd, 0);
2374 ok(ret, "EnumFontFamiliesEx error %u\n", GetLastError());
2375 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
2376 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s DEFAULT_CHARSET\n",
2377 ansi_charset, symbol_charset, russian_charset,
2378 *font_name ? font_name : "<empty>");
2379 ok(efd.total > 0, "no fonts enumerated: %s DEFAULT_CHARSET\n", font_name);
2380 for (i = 0; i < efd.total; i++)
2382 if (*font_name)
2383 ok(!lstrcmp(efd.lf[i].lfFaceName, font_name), "expected %s, got %s\n",
2384 font_name, efd.lf[i].lfFaceName);
2386 if (*font_name)
2388 switch (font_charset)
2390 case ANSI_CHARSET:
2391 ok(ansi_charset > 0,
2392 "ANSI_CHARSET should enumerate ANSI_CHARSET for %s\n", font_name);
2393 ok(!symbol_charset,
2394 "ANSI_CHARSET should NOT enumerate SYMBOL_CHARSET for %s\n", font_name);
2395 ok(russian_charset > 0,
2396 "ANSI_CHARSET should enumerate RUSSIAN_CHARSET for %s\n", font_name);
2397 break;
2398 case SYMBOL_CHARSET:
2399 ok(!ansi_charset,
2400 "SYMBOL_CHARSET should NOT enumerate ANSI_CHARSET for %s\n", font_name);
2401 ok(symbol_charset,
2402 "SYMBOL_CHARSET should enumerate SYMBOL_CHARSET for %s\n", font_name);
2403 ok(!russian_charset,
2404 "SYMBOL_CHARSET should NOT enumerate RUSSIAN_CHARSET for %s\n", font_name);
2405 break;
2406 case DEFAULT_CHARSET:
2407 ok(ansi_charset > 0,
2408 "DEFAULT_CHARSET should enumerate ANSI_CHARSET for %s\n", font_name);
2409 ok(symbol_charset > 0,
2410 "DEFAULT_CHARSET should enumerate SYMBOL_CHARSET for %s\n", font_name);
2411 ok(russian_charset > 0,
2412 "DEFAULT_CHARSET should enumerate RUSSIAN_CHARSET for %s\n", font_name);
2413 break;
2416 else
2418 ok(ansi_charset > 0,
2419 "DEFAULT_CHARSET should enumerate ANSI_CHARSET for %s\n", *font_name ? font_name : "<empty>");
2420 ok(symbol_charset > 0,
2421 "DEFAULT_CHARSET should enumerate SYMBOL_CHARSET for %s\n", *font_name ? font_name : "<empty>");
2422 ok(russian_charset > 0,
2423 "DEFAULT_CHARSET should enumerate RUSSIAN_CHARSET for %s\n", *font_name ? font_name : "<empty>");
2426 memset(&lf, 0, sizeof(lf));
2427 lf.lfCharSet = SYMBOL_CHARSET;
2428 lstrcpy(lf.lfFaceName, font_name);
2429 efd.total = 0;
2430 SetLastError(0xdeadbeef);
2431 EnumFontFamiliesEx(hdc, &lf, arial_enum_proc, (LPARAM)&efd, 0);
2432 ok(ret, "EnumFontFamiliesEx error %u\n", GetLastError());
2433 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
2434 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s SYMBOL_CHARSET\n",
2435 ansi_charset, symbol_charset, russian_charset,
2436 *font_name ? font_name : "<empty>");
2437 if (*font_name && font_charset == ANSI_CHARSET)
2438 ok(efd.total == 0, "no fonts should be enumerated: %s SYMBOL_CHARSET\n", font_name);
2439 else
2441 ok(efd.total > 0, "no fonts enumerated: %s SYMBOL_CHARSET\n", font_name);
2442 for (i = 0; i < efd.total; i++)
2444 ok(efd.lf[i].lfCharSet == SYMBOL_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);
2450 ok(!ansi_charset,
2451 "SYMBOL_CHARSET should NOT enumerate ANSI_CHARSET for %s\n", *font_name ? font_name : "<empty>");
2452 ok(symbol_charset > 0,
2453 "SYMBOL_CHARSET should enumerate SYMBOL_CHARSET for %s\n", *font_name ? font_name : "<empty>");
2454 ok(!russian_charset,
2455 "SYMBOL_CHARSET should NOT enumerate RUSSIAN_CHARSET for %s\n", *font_name ? font_name : "<empty>");
2458 ReleaseDC(0, hdc);
2461 static INT CALLBACK enum_font_data_proc(const LOGFONT *lf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
2463 struct enum_font_data *efd = (struct enum_font_data *)lParam;
2465 if (type != TRUETYPE_FONTTYPE) return 1;
2467 if (efd->total < MAX_ENUM_FONTS)
2468 efd->lf[efd->total++] = *lf;
2469 else
2470 trace("enum tests invalid; you have more than %d fonts\n", MAX_ENUM_FONTS);
2472 return 1;
2475 static INT CALLBACK enum_fullname_data_proc(const LOGFONT *lf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
2477 struct enum_fullname_data *efnd = (struct enum_fullname_data *)lParam;
2479 if (type != TRUETYPE_FONTTYPE) return 1;
2481 if (efnd->total < MAX_ENUM_FONTS)
2482 efnd->elf[efnd->total++] = *(ENUMLOGFONT*)lf;
2483 else
2484 trace("enum tests invalid; you have more than %d fonts\n", MAX_ENUM_FONTS);
2486 return 1;
2489 static void test_EnumFontFamiliesEx_default_charset(void)
2491 struct enum_font_data efd;
2492 LOGFONT gui_font, enum_font;
2493 DWORD ret;
2494 HDC hdc;
2496 ret = GetObject(GetStockObject(DEFAULT_GUI_FONT), sizeof(gui_font), &gui_font);
2497 ok(ret, "GetObject failed.\n");
2498 if (!ret)
2499 return;
2501 efd.total = 0;
2503 hdc = GetDC(0);
2504 memset(&enum_font, 0, sizeof(enum_font));
2505 lstrcpy(enum_font.lfFaceName, gui_font.lfFaceName);
2506 enum_font.lfCharSet = DEFAULT_CHARSET;
2507 EnumFontFamiliesEx(hdc, &enum_font, enum_font_data_proc, (LPARAM)&efd, 0);
2508 ReleaseDC(0, hdc);
2510 if (efd.total == 0) {
2511 skip("'%s' is not found or not a TrueType font.\n", gui_font.lfFaceName);
2512 return;
2514 trace("'%s' has %d charsets.\n", gui_font.lfFaceName, efd.total);
2516 ok(efd.lf[0].lfCharSet == gui_font.lfCharSet || broken(system_lang_id == LANG_ARABIC),
2517 "(%s) got charset %d expected %d\n",
2518 efd.lf[0].lfFaceName, efd.lf[0].lfCharSet, gui_font.lfCharSet);
2520 return;
2523 static void test_negative_width(HDC hdc, const LOGFONTA *lf)
2525 HFONT hfont, hfont_prev;
2526 DWORD ret;
2527 GLYPHMETRICS gm1, gm2;
2528 LOGFONTA lf2 = *lf;
2529 WORD idx;
2531 if(!pGetGlyphIndicesA)
2532 return;
2534 /* negative widths are handled just as positive ones */
2535 lf2.lfWidth = -lf->lfWidth;
2537 SetLastError(0xdeadbeef);
2538 hfont = CreateFontIndirectA(lf);
2539 ok(hfont != 0, "CreateFontIndirect error %u\n", GetLastError());
2540 check_font("original", lf, hfont);
2542 hfont_prev = SelectObject(hdc, hfont);
2544 ret = pGetGlyphIndicesA(hdc, "x", 1, &idx, GGI_MARK_NONEXISTING_GLYPHS);
2545 if (ret == GDI_ERROR || idx == 0xffff)
2547 SelectObject(hdc, hfont_prev);
2548 DeleteObject(hfont);
2549 skip("Font %s doesn't contain 'x', skipping the test\n", lf->lfFaceName);
2550 return;
2553 /* filling with 0xaa causes false pass under WINEDEBUG=warn+heap */
2554 memset(&gm1, 0xab, sizeof(gm1));
2555 SetLastError(0xdeadbeef);
2556 ret = GetGlyphOutlineA(hdc, 'x', GGO_METRICS, &gm1, 0, NULL, &mat);
2557 ok(ret != GDI_ERROR, "GetGlyphOutline error 0x%x\n", GetLastError());
2559 SelectObject(hdc, hfont_prev);
2560 DeleteObject(hfont);
2562 SetLastError(0xdeadbeef);
2563 hfont = CreateFontIndirectA(&lf2);
2564 ok(hfont != 0, "CreateFontIndirect error %u\n", GetLastError());
2565 check_font("negative width", &lf2, hfont);
2567 hfont_prev = SelectObject(hdc, hfont);
2569 memset(&gm2, 0xbb, sizeof(gm2));
2570 SetLastError(0xdeadbeef);
2571 ret = GetGlyphOutlineA(hdc, 'x', GGO_METRICS, &gm2, 0, NULL, &mat);
2572 ok(ret != GDI_ERROR, "GetGlyphOutline error 0x%x\n", GetLastError());
2574 SelectObject(hdc, hfont_prev);
2575 DeleteObject(hfont);
2577 ok(gm1.gmBlackBoxX == gm2.gmBlackBoxX &&
2578 gm1.gmBlackBoxY == gm2.gmBlackBoxY &&
2579 gm1.gmptGlyphOrigin.x == gm2.gmptGlyphOrigin.x &&
2580 gm1.gmptGlyphOrigin.y == gm2.gmptGlyphOrigin.y &&
2581 gm1.gmCellIncX == gm2.gmCellIncX &&
2582 gm1.gmCellIncY == gm2.gmCellIncY,
2583 "gm1=%d,%d,%d,%d,%d,%d gm2=%d,%d,%d,%d,%d,%d\n",
2584 gm1.gmBlackBoxX, gm1.gmBlackBoxY, gm1.gmptGlyphOrigin.x,
2585 gm1.gmptGlyphOrigin.y, gm1.gmCellIncX, gm1.gmCellIncY,
2586 gm2.gmBlackBoxX, gm2.gmBlackBoxY, gm2.gmptGlyphOrigin.x,
2587 gm2.gmptGlyphOrigin.y, gm2.gmCellIncX, gm2.gmCellIncY);
2590 /* PANOSE is 10 bytes in size, need to pack the structure properly */
2591 #include "pshpack2.h"
2592 typedef struct
2594 USHORT version;
2595 SHORT xAvgCharWidth;
2596 USHORT usWeightClass;
2597 USHORT usWidthClass;
2598 SHORT fsType;
2599 SHORT ySubscriptXSize;
2600 SHORT ySubscriptYSize;
2601 SHORT ySubscriptXOffset;
2602 SHORT ySubscriptYOffset;
2603 SHORT ySuperscriptXSize;
2604 SHORT ySuperscriptYSize;
2605 SHORT ySuperscriptXOffset;
2606 SHORT ySuperscriptYOffset;
2607 SHORT yStrikeoutSize;
2608 SHORT yStrikeoutPosition;
2609 SHORT sFamilyClass;
2610 PANOSE panose;
2611 ULONG ulUnicodeRange1;
2612 ULONG ulUnicodeRange2;
2613 ULONG ulUnicodeRange3;
2614 ULONG ulUnicodeRange4;
2615 CHAR achVendID[4];
2616 USHORT fsSelection;
2617 USHORT usFirstCharIndex;
2618 USHORT usLastCharIndex;
2619 /* According to the Apple spec, original version didn't have the below fields,
2620 * version numbers were taken from the OpenType spec.
2622 /* version 0 (TrueType 1.5) */
2623 USHORT sTypoAscender;
2624 USHORT sTypoDescender;
2625 USHORT sTypoLineGap;
2626 USHORT usWinAscent;
2627 USHORT usWinDescent;
2628 /* version 1 (TrueType 1.66) */
2629 ULONG ulCodePageRange1;
2630 ULONG ulCodePageRange2;
2631 /* version 2 (OpenType 1.2) */
2632 SHORT sxHeight;
2633 SHORT sCapHeight;
2634 USHORT usDefaultChar;
2635 USHORT usBreakChar;
2636 USHORT usMaxContext;
2637 } TT_OS2_V2;
2638 #include "poppack.h"
2640 #ifdef WORDS_BIGENDIAN
2641 #define GET_BE_WORD(x) (x)
2642 #define GET_BE_DWORD(x) (x)
2643 #else
2644 #define GET_BE_WORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
2645 #define GET_BE_DWORD(x) MAKELONG(GET_BE_WORD(HIWORD(x)), GET_BE_WORD(LOWORD(x)));
2646 #endif
2648 #define MS_MAKE_TAG(ch0, ch1, ch2, ch3) \
2649 ((DWORD)(BYTE)(ch0) | ((DWORD)(BYTE)(ch1) << 8) | \
2650 ((DWORD)(BYTE)(ch2) << 16) | ((DWORD)(BYTE)(ch3) << 24))
2651 #define MS_OS2_TAG MS_MAKE_TAG('O','S','/','2')
2652 #define MS_CMAP_TAG MS_MAKE_TAG('c','m','a','p')
2653 #define MS_NAME_TAG MS_MAKE_TAG('n','a','m','e')
2655 typedef struct
2657 USHORT version;
2658 USHORT num_tables;
2659 } cmap_header;
2661 typedef struct
2663 USHORT plat_id;
2664 USHORT enc_id;
2665 ULONG offset;
2666 } cmap_encoding_record;
2668 typedef struct
2670 USHORT format;
2671 USHORT length;
2672 USHORT language;
2674 BYTE glyph_ids[256];
2675 } cmap_format_0;
2677 typedef struct
2679 USHORT format;
2680 USHORT length;
2681 USHORT language;
2683 USHORT seg_countx2;
2684 USHORT search_range;
2685 USHORT entry_selector;
2686 USHORT range_shift;
2688 USHORT end_count[1]; /* this is a variable-sized array of length seg_countx2 / 2 */
2689 /* Then follows:
2690 USHORT pad;
2691 USHORT start_count[seg_countx2 / 2];
2692 USHORT id_delta[seg_countx2 / 2];
2693 USHORT id_range_offset[seg_countx2 / 2];
2694 USHORT glyph_ids[];
2696 } cmap_format_4;
2698 typedef struct
2700 USHORT end_count;
2701 USHORT start_count;
2702 USHORT id_delta;
2703 USHORT id_range_offset;
2704 } cmap_format_4_seg;
2706 static void expect_ff(const TEXTMETRICA *tmA, const TT_OS2_V2 *os2, WORD family, const char *name)
2708 ok((tmA->tmPitchAndFamily & 0xf0) == family ||
2709 broken(PRIMARYLANGID(GetSystemDefaultLangID()) != LANG_ENGLISH),
2710 "%s: expected family %02x got %02x. panose %d-%d-%d-%d-...\n",
2711 name, family, tmA->tmPitchAndFamily, os2->panose.bFamilyType, os2->panose.bSerifStyle,
2712 os2->panose.bWeight, os2->panose.bProportion);
2715 static BOOL get_first_last_from_cmap0(void *ptr, DWORD *first, DWORD *last)
2717 int i;
2718 cmap_format_0 *cmap = (cmap_format_0*)ptr;
2720 *first = 256;
2722 for(i = 0; i < 256; i++)
2724 if(cmap->glyph_ids[i] == 0) continue;
2725 *last = i;
2726 if(*first == 256) *first = i;
2728 if(*first == 256) return FALSE;
2729 return TRUE;
2732 static void get_seg4(cmap_format_4 *cmap, USHORT seg_num, cmap_format_4_seg *seg)
2734 USHORT segs = GET_BE_WORD(cmap->seg_countx2) / 2;
2735 seg->end_count = GET_BE_WORD(cmap->end_count[seg_num]);
2736 seg->start_count = GET_BE_WORD(cmap->end_count[segs + 1 + seg_num]);
2737 seg->id_delta = GET_BE_WORD(cmap->end_count[2 * segs + 1 + seg_num]);
2738 seg->id_range_offset = GET_BE_WORD(cmap->end_count[3 * segs + 1 + seg_num]);
2741 static BOOL get_first_last_from_cmap4(void *ptr, DWORD *first, DWORD *last, DWORD limit)
2743 int i;
2744 cmap_format_4 *cmap = (cmap_format_4*)ptr;
2745 USHORT seg_count = GET_BE_WORD(cmap->seg_countx2) / 2;
2746 USHORT const *glyph_ids = cmap->end_count + 4 * seg_count + 1;
2748 *first = 0x10000;
2750 for(i = 0; i < seg_count; i++)
2752 DWORD code, index;
2753 cmap_format_4_seg seg;
2755 get_seg4(cmap, i, &seg);
2756 for(code = seg.start_count; code <= seg.end_count; code++)
2758 if(seg.id_range_offset == 0)
2759 index = (seg.id_delta + code) & 0xffff;
2760 else
2762 index = seg.id_range_offset / 2
2763 + code - seg.start_count
2764 + i - seg_count;
2766 /* some fonts have broken last segment */
2767 if ((char *)(glyph_ids + index + 1) < (char *)ptr + limit)
2768 index = GET_BE_WORD(glyph_ids[index]);
2769 else
2771 trace("segment %04x/%04x index %04x points to nowhere\n",
2772 seg.start_count, seg.end_count, index);
2773 index = 0;
2775 if(index) index += seg.id_delta;
2777 if(*first == 0x10000)
2778 *last = *first = code;
2779 else if(index)
2780 *last = code;
2784 if(*first == 0x10000) return FALSE;
2785 return TRUE;
2788 static void *get_cmap(cmap_header *header, USHORT plat_id, USHORT enc_id)
2790 USHORT i;
2791 cmap_encoding_record *record = (cmap_encoding_record *)(header + 1);
2793 for(i = 0; i < GET_BE_WORD(header->num_tables); i++)
2795 if(GET_BE_WORD(record->plat_id) == plat_id && GET_BE_WORD(record->enc_id) == enc_id)
2796 return (BYTE *)header + GET_BE_DWORD(record->offset);
2797 record++;
2799 return NULL;
2802 typedef enum
2804 cmap_none,
2805 cmap_ms_unicode,
2806 cmap_ms_symbol
2807 } cmap_type;
2809 static BOOL get_first_last_from_cmap(HDC hdc, DWORD *first, DWORD *last, cmap_type *cmap_type)
2811 LONG size, ret;
2812 cmap_header *header;
2813 void *cmap;
2814 BOOL r = FALSE;
2815 WORD format;
2817 size = GetFontData(hdc, MS_CMAP_TAG, 0, NULL, 0);
2818 ok(size != GDI_ERROR, "no cmap table found\n");
2819 if(size == GDI_ERROR) return FALSE;
2821 header = HeapAlloc(GetProcessHeap(), 0, size);
2822 ret = GetFontData(hdc, MS_CMAP_TAG, 0, header, size);
2823 ok(ret == size, "GetFontData should return %u not %u\n", size, ret);
2824 ok(GET_BE_WORD(header->version) == 0, "got cmap version %d\n", GET_BE_WORD(header->version));
2826 cmap = get_cmap(header, 3, 1);
2827 if(cmap)
2828 *cmap_type = cmap_ms_unicode;
2829 else
2831 cmap = get_cmap(header, 3, 0);
2832 if(cmap) *cmap_type = cmap_ms_symbol;
2834 if(!cmap)
2836 *cmap_type = cmap_none;
2837 goto end;
2840 format = GET_BE_WORD(*(WORD *)cmap);
2841 switch(format)
2843 case 0:
2844 r = get_first_last_from_cmap0(cmap, first, last);
2845 break;
2846 case 4:
2847 r = get_first_last_from_cmap4(cmap, first, last, size);
2848 break;
2849 default:
2850 trace("unhandled cmap format %d\n", format);
2851 break;
2854 end:
2855 HeapFree(GetProcessHeap(), 0, header);
2856 return r;
2859 #define TT_PLATFORM_MICROSOFT 3
2860 #define TT_MS_ID_SYMBOL_CS 0
2861 #define TT_MS_ID_UNICODE_CS 1
2862 #define TT_MS_LANGID_ENGLISH_UNITED_STATES 0x0409
2863 #define TT_NAME_ID_FONT_FAMILY 1
2864 #define TT_NAME_ID_FONT_SUBFAMILY 2
2865 #define TT_NAME_ID_UNIQUE_ID 3
2866 #define TT_NAME_ID_FULL_NAME 4
2868 static BOOL get_ttf_nametable_entry(HDC hdc, WORD name_id, WCHAR *out_buf, SIZE_T out_size, LCID language_id)
2870 struct sfnt_name_header
2872 USHORT format;
2873 USHORT number_of_record;
2874 USHORT storage_offset;
2875 } *header;
2876 struct sfnt_name
2878 USHORT platform_id;
2879 USHORT encoding_id;
2880 USHORT language_id;
2881 USHORT name_id;
2882 USHORT length;
2883 USHORT offset;
2884 } *entry;
2885 BOOL r = FALSE;
2886 LONG size, offset, length;
2887 LONG c, ret;
2888 WCHAR *name;
2889 BYTE *data;
2890 USHORT i;
2892 size = GetFontData(hdc, MS_NAME_TAG, 0, NULL, 0);
2893 ok(size != GDI_ERROR, "no name table found\n");
2894 if(size == GDI_ERROR) return FALSE;
2896 data = HeapAlloc(GetProcessHeap(), 0, size);
2897 ret = GetFontData(hdc, MS_NAME_TAG, 0, data, size);
2898 ok(ret == size, "GetFontData should return %u not %u\n", size, ret);
2900 header = (void *)data;
2901 header->format = GET_BE_WORD(header->format);
2902 header->number_of_record = GET_BE_WORD(header->number_of_record);
2903 header->storage_offset = GET_BE_WORD(header->storage_offset);
2904 if (header->format != 0)
2906 trace("got format %u\n", header->format);
2907 goto out;
2909 if (header->number_of_record == 0 || sizeof(*header) + header->number_of_record * sizeof(*entry) > size)
2911 trace("number records out of range: %d\n", header->number_of_record);
2912 goto out;
2914 if (header->storage_offset >= size)
2916 trace("storage_offset %u > size %u\n", header->storage_offset, size);
2917 goto out;
2920 entry = (void *)&header[1];
2921 for (i = 0; i < header->number_of_record; i++)
2923 if (GET_BE_WORD(entry[i].platform_id) != TT_PLATFORM_MICROSOFT ||
2924 (GET_BE_WORD(entry[i].encoding_id) != TT_MS_ID_UNICODE_CS && GET_BE_WORD(entry[i].encoding_id) != TT_MS_ID_SYMBOL_CS) ||
2925 GET_BE_WORD(entry[i].language_id) != language_id ||
2926 GET_BE_WORD(entry[i].name_id) != name_id)
2928 continue;
2931 offset = header->storage_offset + GET_BE_WORD(entry[i].offset);
2932 length = GET_BE_WORD(entry[i].length);
2933 if (offset + length > size)
2935 trace("entry %d is out of range\n", i);
2936 break;
2938 if (length >= out_size)
2940 trace("buffer too small for entry %d\n", i);
2941 break;
2944 name = (WCHAR *)(data + offset);
2945 for (c = 0; c < length / 2; c++)
2946 out_buf[c] = GET_BE_WORD(name[c]);
2947 out_buf[c] = 0;
2949 r = TRUE;
2950 break;
2953 out:
2954 HeapFree(GetProcessHeap(), 0, data);
2955 return r;
2958 static void test_text_metrics(const LOGFONT *lf, const NEWTEXTMETRIC *ntm)
2960 HDC hdc;
2961 HFONT hfont, hfont_old;
2962 TEXTMETRICA tmA;
2963 TT_OS2_V2 tt_os2;
2964 LONG size, ret;
2965 const char *font_name = lf->lfFaceName;
2966 DWORD cmap_first = 0, cmap_last = 0;
2967 UINT ascent, descent, cell_height;
2968 cmap_type cmap_type;
2969 BOOL sys_lang_non_english;
2971 sys_lang_non_english = PRIMARYLANGID(GetSystemDefaultLangID()) != LANG_ENGLISH;
2972 hdc = GetDC(0);
2974 SetLastError(0xdeadbeef);
2975 hfont = CreateFontIndirectA(lf);
2976 ok(hfont != 0, "CreateFontIndirect error %u\n", GetLastError());
2978 hfont_old = SelectObject(hdc, hfont);
2980 size = GetFontData(hdc, MS_OS2_TAG, 0, NULL, 0);
2981 if (size == GDI_ERROR)
2983 trace("OS/2 chunk was not found\n");
2984 goto end_of_test;
2986 if (size > sizeof(tt_os2))
2988 trace("got too large OS/2 chunk of size %u\n", size);
2989 size = sizeof(tt_os2);
2992 memset(&tt_os2, 0, sizeof(tt_os2));
2993 ret = GetFontData(hdc, MS_OS2_TAG, 0, &tt_os2, size);
2994 ok(ret == size, "GetFontData should return %u not %u\n", size, ret);
2996 ascent = GET_BE_WORD(tt_os2.usWinAscent);
2997 descent = GET_BE_WORD(tt_os2.usWinDescent);
2998 cell_height = ascent + descent;
2999 ok(ntm->ntmCellHeight == cell_height, "%s: ntmCellHeight %u != %u, os2.usWinAscent/os2.usWinDescent %u/%u\n",
3000 font_name, ntm->ntmCellHeight, cell_height, ascent, descent);
3002 SetLastError(0xdeadbeef);
3003 ret = GetTextMetricsA(hdc, &tmA);
3004 ok(ret, "GetTextMetricsA error %u\n", GetLastError());
3006 if(!get_first_last_from_cmap(hdc, &cmap_first, &cmap_last, &cmap_type))
3008 skip("Unable to retrieve first and last glyphs from cmap\n");
3010 else
3012 USHORT expect_first_A, expect_last_A, expect_break_A, expect_default_A;
3013 USHORT expect_first_W, expect_last_W, expect_break_W, expect_default_W;
3014 UINT os2_first_char, os2_last_char, default_char, break_char;
3015 USHORT version;
3016 TEXTMETRICW tmW;
3018 version = GET_BE_WORD(tt_os2.version);
3020 os2_first_char = GET_BE_WORD(tt_os2.usFirstCharIndex);
3021 os2_last_char = GET_BE_WORD(tt_os2.usLastCharIndex);
3022 default_char = GET_BE_WORD(tt_os2.usDefaultChar);
3023 break_char = GET_BE_WORD(tt_os2.usBreakChar);
3025 trace("font %s charset %u: %x-%x (%x-%x) default %x break %x OS/2 version %u vendor %4.4s\n",
3026 font_name, lf->lfCharSet, os2_first_char, os2_last_char, cmap_first, cmap_last,
3027 default_char, break_char, version, (LPCSTR)&tt_os2.achVendID);
3029 if (cmap_type == cmap_ms_symbol || (cmap_first >= 0xf000 && cmap_first < 0xf100))
3031 expect_first_W = 0;
3032 switch(GetACP())
3034 case 1257: /* Baltic */
3035 expect_last_W = 0xf8fd;
3036 break;
3037 default:
3038 expect_last_W = 0xf0ff;
3040 expect_break_W = 0x20;
3041 expect_default_W = expect_break_W - 1;
3042 expect_first_A = 0x1e;
3043 expect_last_A = min(os2_last_char - os2_first_char + 0x20, 0xff);
3045 else
3047 expect_first_W = cmap_first;
3048 expect_last_W = min(cmap_last, os2_last_char);
3049 if(os2_first_char <= 1)
3050 expect_break_W = os2_first_char + 2;
3051 else if(os2_first_char > 0xff)
3052 expect_break_W = 0x20;
3053 else
3054 expect_break_W = os2_first_char;
3055 expect_default_W = expect_break_W - 1;
3056 expect_first_A = expect_default_W - 1;
3057 expect_last_A = min(expect_last_W, 0xff);
3059 expect_break_A = expect_break_W;
3060 expect_default_A = expect_default_W;
3062 /* Wine currently uses SYMBOL_CHARSET to identify whether the ANSI metrics need special handling */
3063 if(cmap_type != cmap_ms_symbol && tmA.tmCharSet == SYMBOL_CHARSET && expect_first_A != 0x1e)
3064 todo_wine ok(tmA.tmFirstChar == expect_first_A ||
3065 tmA.tmFirstChar == expect_first_A + 1 /* win9x */,
3066 "A: tmFirstChar for %s got %02x expected %02x\n", font_name, tmA.tmFirstChar, expect_first_A);
3067 else
3068 ok(tmA.tmFirstChar == expect_first_A ||
3069 tmA.tmFirstChar == expect_first_A + 1 /* win9x */,
3070 "A: tmFirstChar for %s got %02x expected %02x\n", font_name, tmA.tmFirstChar, expect_first_A);
3071 if (pGdiGetCodePage == NULL || ! IsDBCSLeadByteEx(pGdiGetCodePage(hdc), tmA.tmLastChar))
3072 ok(tmA.tmLastChar == expect_last_A ||
3073 tmA.tmLastChar == 0xff /* win9x */,
3074 "A: tmLastChar for %s got %02x expected %02x\n", font_name, tmA.tmLastChar, expect_last_A);
3075 else
3076 skip("tmLastChar is DBCS lead byte\n");
3077 ok(tmA.tmBreakChar == expect_break_A, "A: tmBreakChar for %s got %02x expected %02x\n",
3078 font_name, tmA.tmBreakChar, expect_break_A);
3079 ok(tmA.tmDefaultChar == expect_default_A || broken(sys_lang_non_english),
3080 "A: tmDefaultChar for %s got %02x expected %02x\n",
3081 font_name, tmA.tmDefaultChar, expect_default_A);
3084 SetLastError(0xdeadbeef);
3085 ret = GetTextMetricsW(hdc, &tmW);
3086 ok(ret || GetLastError() == ERROR_CALL_NOT_IMPLEMENTED,
3087 "GetTextMetricsW error %u\n", GetLastError());
3088 if (ret)
3090 /* Wine uses the os2 first char */
3091 if(cmap_first != os2_first_char && cmap_type != cmap_ms_symbol)
3092 todo_wine ok(tmW.tmFirstChar == expect_first_W, "W: tmFirstChar for %s got %02x expected %02x\n",
3093 font_name, tmW.tmFirstChar, expect_first_W);
3094 else
3095 ok(tmW.tmFirstChar == expect_first_W, "W: tmFirstChar for %s got %02x expected %02x\n",
3096 font_name, tmW.tmFirstChar, expect_first_W);
3098 /* Wine uses the os2 last char */
3099 if(expect_last_W != os2_last_char && cmap_type != cmap_ms_symbol)
3100 todo_wine ok(tmW.tmLastChar == expect_last_W, "W: tmLastChar for %s got %02x expected %02x\n",
3101 font_name, tmW.tmLastChar, expect_last_W);
3102 else
3103 ok(tmW.tmLastChar == expect_last_W, "W: tmLastChar for %s got %02x expected %02x\n",
3104 font_name, tmW.tmLastChar, expect_last_W);
3105 ok(tmW.tmBreakChar == expect_break_W, "W: tmBreakChar for %s got %02x expected %02x\n",
3106 font_name, tmW.tmBreakChar, expect_break_W);
3107 ok(tmW.tmDefaultChar == expect_default_W || broken(sys_lang_non_english),
3108 "W: tmDefaultChar for %s got %02x expected %02x\n",
3109 font_name, tmW.tmDefaultChar, expect_default_W);
3111 /* Test the aspect ratio while we have tmW */
3112 ret = GetDeviceCaps(hdc, LOGPIXELSX);
3113 ok(tmW.tmDigitizedAspectX == ret, "W: tmDigitizedAspectX %u != %u\n",
3114 tmW.tmDigitizedAspectX, ret);
3115 ret = GetDeviceCaps(hdc, LOGPIXELSY);
3116 ok(tmW.tmDigitizedAspectX == ret, "W: tmDigitizedAspectY %u != %u\n",
3117 tmW.tmDigitizedAspectX, ret);
3121 /* test FF_ values */
3122 switch(tt_os2.panose.bFamilyType)
3124 case PAN_ANY:
3125 case PAN_NO_FIT:
3126 case PAN_FAMILY_TEXT_DISPLAY:
3127 case PAN_FAMILY_PICTORIAL:
3128 default:
3129 if((tmA.tmPitchAndFamily & 1) == 0 || /* fixed */
3130 tt_os2.panose.bProportion == PAN_PROP_MONOSPACED)
3132 expect_ff(&tmA, &tt_os2, FF_MODERN, font_name);
3133 break;
3135 switch(tt_os2.panose.bSerifStyle)
3137 case PAN_ANY:
3138 case PAN_NO_FIT:
3139 default:
3140 expect_ff(&tmA, &tt_os2, FF_DONTCARE, font_name);
3141 break;
3143 case PAN_SERIF_COVE:
3144 case PAN_SERIF_OBTUSE_COVE:
3145 case PAN_SERIF_SQUARE_COVE:
3146 case PAN_SERIF_OBTUSE_SQUARE_COVE:
3147 case PAN_SERIF_SQUARE:
3148 case PAN_SERIF_THIN:
3149 case PAN_SERIF_BONE:
3150 case PAN_SERIF_EXAGGERATED:
3151 case PAN_SERIF_TRIANGLE:
3152 expect_ff(&tmA, &tt_os2, FF_ROMAN, font_name);
3153 break;
3155 case PAN_SERIF_NORMAL_SANS:
3156 case PAN_SERIF_OBTUSE_SANS:
3157 case PAN_SERIF_PERP_SANS:
3158 case PAN_SERIF_FLARED:
3159 case PAN_SERIF_ROUNDED:
3160 expect_ff(&tmA, &tt_os2, FF_SWISS, font_name);
3161 break;
3163 break;
3165 case PAN_FAMILY_SCRIPT:
3166 expect_ff(&tmA, &tt_os2, FF_SCRIPT, font_name);
3167 break;
3169 case PAN_FAMILY_DECORATIVE:
3170 expect_ff(&tmA, &tt_os2, FF_DECORATIVE, font_name);
3171 break;
3174 test_negative_width(hdc, lf);
3176 end_of_test:
3177 SelectObject(hdc, hfont_old);
3178 DeleteObject(hfont);
3180 ReleaseDC(0, hdc);
3183 static INT CALLBACK enum_truetype_font_proc(const LOGFONT *lf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
3185 INT *enumed = (INT *)lParam;
3187 if (type == TRUETYPE_FONTTYPE)
3189 (*enumed)++;
3190 test_text_metrics(lf, (const NEWTEXTMETRIC *)ntm);
3192 return 1;
3195 static void test_GetTextMetrics(void)
3197 LOGFONTA lf;
3198 HDC hdc;
3199 INT enumed;
3201 /* Report only once */
3202 if(!pGetGlyphIndicesA)
3203 win_skip("GetGlyphIndicesA is unavailable, negative width will not be checked\n");
3205 hdc = GetDC(0);
3207 memset(&lf, 0, sizeof(lf));
3208 lf.lfCharSet = DEFAULT_CHARSET;
3209 enumed = 0;
3210 EnumFontFamiliesExA(hdc, &lf, enum_truetype_font_proc, (LPARAM)&enumed, 0);
3211 trace("Tested metrics of %d truetype fonts\n", enumed);
3213 ReleaseDC(0, hdc);
3216 static void test_nonexistent_font(void)
3218 static const struct
3220 const char *name;
3221 int charset;
3222 } font_subst[] =
3224 { "Times New Roman Baltic", 186 },
3225 { "Times New Roman CE", 238 },
3226 { "Times New Roman CYR", 204 },
3227 { "Times New Roman Greek", 161 },
3228 { "Times New Roman TUR", 162 }
3230 LOGFONTA lf;
3231 HDC hdc;
3232 HFONT hfont;
3233 CHARSETINFO csi;
3234 INT cs, expected_cs, i;
3235 char buf[LF_FACESIZE];
3237 if (!is_truetype_font_installed("Arial") ||
3238 !is_truetype_font_installed("Times New Roman"))
3240 skip("Arial or Times New Roman not installed\n");
3241 return;
3244 expected_cs = GetACP();
3245 if (!TranslateCharsetInfo(ULongToPtr(expected_cs), &csi, TCI_SRCCODEPAGE))
3247 skip("TranslateCharsetInfo failed for code page %d\n", expected_cs);
3248 return;
3250 expected_cs = csi.ciCharset;
3251 trace("ACP %d -> charset %d\n", GetACP(), expected_cs);
3253 hdc = GetDC(0);
3255 memset(&lf, 0, sizeof(lf));
3256 lf.lfHeight = 100;
3257 lf.lfWeight = FW_REGULAR;
3258 lf.lfCharSet = ANSI_CHARSET;
3259 lf.lfPitchAndFamily = FF_SWISS;
3260 strcpy(lf.lfFaceName, "Nonexistent font");
3261 hfont = CreateFontIndirectA(&lf);
3262 hfont = SelectObject(hdc, hfont);
3263 GetTextFaceA(hdc, sizeof(buf), buf);
3264 ok(!lstrcmpiA(buf, "Arial"), "Got %s\n", buf);
3265 cs = GetTextCharset(hdc);
3266 ok(cs == ANSI_CHARSET, "expected ANSI_CHARSET, got %d\n", cs);
3267 DeleteObject(SelectObject(hdc, hfont));
3269 memset(&lf, 0, sizeof(lf));
3270 lf.lfHeight = -13;
3271 lf.lfWeight = FW_DONTCARE;
3272 strcpy(lf.lfFaceName, "Nonexistent font");
3273 hfont = CreateFontIndirectA(&lf);
3274 hfont = SelectObject(hdc, hfont);
3275 GetTextFaceA(hdc, sizeof(buf), buf);
3276 todo_wine /* Wine uses Arial for all substitutions */
3277 ok(!lstrcmpiA(buf, "Nonexistent font") /* XP, Vista */ ||
3278 !lstrcmpiA(buf, "MS Serif") || /* Win9x */
3279 !lstrcmpiA(buf, "MS Sans Serif"), /* win2k3 */
3280 "Got %s\n", buf);
3281 cs = GetTextCharset(hdc);
3282 ok(cs == expected_cs || cs == ANSI_CHARSET, "expected %d, got %d\n", expected_cs, cs);
3283 DeleteObject(SelectObject(hdc, hfont));
3285 memset(&lf, 0, sizeof(lf));
3286 lf.lfHeight = -13;
3287 lf.lfWeight = FW_REGULAR;
3288 strcpy(lf.lfFaceName, "Nonexistent font");
3289 hfont = CreateFontIndirectA(&lf);
3290 hfont = SelectObject(hdc, hfont);
3291 GetTextFaceA(hdc, sizeof(buf), buf);
3292 ok(!lstrcmpiA(buf, "Arial") /* XP, Vista */ ||
3293 !lstrcmpiA(buf, "Times New Roman") /* Win9x */, "Got %s\n", buf);
3294 cs = GetTextCharset(hdc);
3295 ok(cs == ANSI_CHARSET, "expected ANSI_CHARSET, got %d\n", cs);
3296 DeleteObject(SelectObject(hdc, hfont));
3298 memset(&lf, 0, sizeof(lf));
3299 lf.lfHeight = -13;
3300 lf.lfWeight = FW_DONTCARE;
3301 strcpy(lf.lfFaceName, "Times New Roman");
3302 hfont = CreateFontIndirectA(&lf);
3303 hfont = SelectObject(hdc, hfont);
3304 GetTextFaceA(hdc, sizeof(buf), buf);
3305 ok(!lstrcmpiA(buf, "Times New Roman"), "Got %s\n", buf);
3306 cs = GetTextCharset(hdc);
3307 ok(cs == ANSI_CHARSET, "expected ANSI_CHARSET, got %d\n", cs);
3308 DeleteObject(SelectObject(hdc, hfont));
3310 for (i = 0; i < sizeof(font_subst)/sizeof(font_subst[0]); i++)
3312 memset(&lf, 0, sizeof(lf));
3313 lf.lfHeight = -13;
3314 lf.lfWeight = FW_REGULAR;
3315 strcpy(lf.lfFaceName, font_subst[i].name);
3316 hfont = CreateFontIndirectA(&lf);
3317 hfont = SelectObject(hdc, hfont);
3318 cs = GetTextCharset(hdc);
3319 if (font_subst[i].charset == expected_cs)
3321 ok(cs == expected_cs, "expected %d, got %d for font %s\n", expected_cs, cs, font_subst[i].name);
3322 GetTextFaceA(hdc, sizeof(buf), buf);
3323 ok(!lstrcmpiA(buf, font_subst[i].name), "expected %s, got %s\n", font_subst[i].name, buf);
3325 else
3327 ok(cs == ANSI_CHARSET, "expected ANSI_CHARSET, got %d for font %s\n", cs, font_subst[i].name);
3328 GetTextFaceA(hdc, sizeof(buf), buf);
3329 ok(!lstrcmpiA(buf, "Arial") /* XP, Vista */ ||
3330 !lstrcmpiA(buf, "Times New Roman") /* Win9x */, "got %s for font %s\n", buf, font_subst[i].name);
3332 DeleteObject(SelectObject(hdc, hfont));
3334 memset(&lf, 0, sizeof(lf));
3335 lf.lfHeight = -13;
3336 lf.lfWeight = FW_DONTCARE;
3337 strcpy(lf.lfFaceName, font_subst[i].name);
3338 hfont = CreateFontIndirectA(&lf);
3339 hfont = SelectObject(hdc, hfont);
3340 GetTextFaceA(hdc, sizeof(buf), buf);
3341 ok(!lstrcmpiA(buf, "Arial") /* Wine */ ||
3342 !lstrcmpiA(buf, font_subst[i].name) /* XP, Vista */ ||
3343 !lstrcmpiA(buf, "MS Serif") /* Win9x */ ||
3344 !lstrcmpiA(buf, "MS Sans Serif"), /* win2k3 */
3345 "got %s for font %s\n", buf, font_subst[i].name);
3346 cs = GetTextCharset(hdc);
3347 ok(cs == expected_cs || cs == ANSI_CHARSET, "expected %d, got %d for font %s\n", expected_cs, cs, font_subst[i].name);
3348 DeleteObject(SelectObject(hdc, hfont));
3351 ReleaseDC(0, hdc);
3354 static void test_GdiRealizationInfo(void)
3356 HDC hdc;
3357 DWORD info[4];
3358 BOOL r;
3359 HFONT hfont, hfont_old;
3360 LOGFONTA lf;
3362 if(!pGdiRealizationInfo)
3364 win_skip("GdiRealizationInfo not available\n");
3365 return;
3368 hdc = GetDC(0);
3370 memset(info, 0xcc, sizeof(info));
3371 r = pGdiRealizationInfo(hdc, info);
3372 ok(r != 0, "ret 0\n");
3373 ok((info[0] & 0xf) == 1, "info[0] = %x for the system font\n", info[0]);
3374 ok(info[3] == 0xcccccccc, "structure longer than 3 dwords\n");
3376 if (!is_truetype_font_installed("Arial"))
3378 skip("skipping GdiRealizationInfo with truetype font\n");
3379 goto end;
3382 memset(&lf, 0, sizeof(lf));
3383 strcpy(lf.lfFaceName, "Arial");
3384 lf.lfHeight = 20;
3385 lf.lfWeight = FW_NORMAL;
3386 hfont = CreateFontIndirectA(&lf);
3387 hfont_old = SelectObject(hdc, hfont);
3389 memset(info, 0xcc, sizeof(info));
3390 r = pGdiRealizationInfo(hdc, info);
3391 ok(r != 0, "ret 0\n");
3392 ok((info[0] & 0xf) == 3, "info[0] = %x for arial\n", info[0]);
3393 ok(info[3] == 0xcccccccc, "structure longer than 3 dwords\n");
3395 DeleteObject(SelectObject(hdc, hfont_old));
3397 end:
3398 ReleaseDC(0, hdc);
3401 /* Tests on XP SP2 show that the ANSI version of GetTextFace does NOT include
3402 the nul in the count of characters copied when the face name buffer is not
3403 NULL, whereas it does if the buffer is NULL. Further, the Unicode version
3404 always includes it. */
3405 static void test_GetTextFace(void)
3407 static const char faceA[] = "Tahoma";
3408 static const WCHAR faceW[] = {'T','a','h','o','m','a', 0};
3409 LOGFONTA fA = {0};
3410 LOGFONTW fW = {0};
3411 char bufA[LF_FACESIZE];
3412 WCHAR bufW[LF_FACESIZE];
3413 HFONT f, g;
3414 HDC dc;
3415 int n;
3417 if(!is_font_installed("Tahoma"))
3419 skip("Tahoma is not installed so skipping this test\n");
3420 return;
3423 /* 'A' case. */
3424 memcpy(fA.lfFaceName, faceA, sizeof faceA);
3425 f = CreateFontIndirectA(&fA);
3426 ok(f != NULL, "CreateFontIndirectA failed\n");
3428 dc = GetDC(NULL);
3429 g = SelectObject(dc, f);
3430 n = GetTextFaceA(dc, sizeof bufA, bufA);
3431 ok(n == sizeof faceA - 1, "GetTextFaceA returned %d\n", n);
3432 ok(lstrcmpA(faceA, bufA) == 0, "GetTextFaceA\n");
3434 /* Play with the count arg. */
3435 bufA[0] = 'x';
3436 n = GetTextFaceA(dc, 0, bufA);
3437 ok(n == 0, "GetTextFaceA returned %d\n", n);
3438 ok(bufA[0] == 'x', "GetTextFaceA buf[0] == %d\n", bufA[0]);
3440 bufA[0] = 'x';
3441 n = GetTextFaceA(dc, 1, bufA);
3442 ok(n == 0, "GetTextFaceA returned %d\n", n);
3443 ok(bufA[0] == '\0', "GetTextFaceA buf[0] == %d\n", bufA[0]);
3445 bufA[0] = 'x'; bufA[1] = 'y';
3446 n = GetTextFaceA(dc, 2, bufA);
3447 ok(n == 1, "GetTextFaceA returned %d\n", n);
3448 ok(bufA[0] == faceA[0] && bufA[1] == '\0', "GetTextFaceA didn't copy\n");
3450 n = GetTextFaceA(dc, 0, NULL);
3451 ok(n == sizeof faceA ||
3452 broken(n == 0), /* win98, winMe */
3453 "GetTextFaceA returned %d\n", n);
3455 DeleteObject(SelectObject(dc, g));
3456 ReleaseDC(NULL, dc);
3458 /* 'W' case. */
3459 memcpy(fW.lfFaceName, faceW, sizeof faceW);
3460 SetLastError(0xdeadbeef);
3461 f = CreateFontIndirectW(&fW);
3462 if (!f && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
3464 win_skip("CreateFontIndirectW is not implemented\n");
3465 return;
3467 ok(f != NULL, "CreateFontIndirectW failed\n");
3469 dc = GetDC(NULL);
3470 g = SelectObject(dc, f);
3471 n = GetTextFaceW(dc, sizeof bufW / sizeof bufW[0], bufW);
3472 ok(n == sizeof faceW / sizeof faceW[0], "GetTextFaceW returned %d\n", n);
3473 ok(lstrcmpW(faceW, bufW) == 0, "GetTextFaceW\n");
3475 /* Play with the count arg. */
3476 bufW[0] = 'x';
3477 n = GetTextFaceW(dc, 0, bufW);
3478 ok(n == 0, "GetTextFaceW returned %d\n", n);
3479 ok(bufW[0] == 'x', "GetTextFaceW buf[0] == %d\n", bufW[0]);
3481 bufW[0] = 'x';
3482 n = GetTextFaceW(dc, 1, bufW);
3483 ok(n == 1, "GetTextFaceW returned %d\n", n);
3484 ok(bufW[0] == '\0', "GetTextFaceW buf[0] == %d\n", bufW[0]);
3486 bufW[0] = 'x'; bufW[1] = 'y';
3487 n = GetTextFaceW(dc, 2, bufW);
3488 ok(n == 2, "GetTextFaceW returned %d\n", n);
3489 ok(bufW[0] == faceW[0] && bufW[1] == '\0', "GetTextFaceW didn't copy\n");
3491 n = GetTextFaceW(dc, 0, NULL);
3492 ok(n == sizeof faceW / sizeof faceW[0], "GetTextFaceW returned %d\n", n);
3494 DeleteObject(SelectObject(dc, g));
3495 ReleaseDC(NULL, dc);
3498 static void test_orientation(void)
3500 static const char test_str[11] = "Test String";
3501 HDC hdc;
3502 LOGFONTA lf;
3503 HFONT hfont, old_hfont;
3504 SIZE size;
3506 if (!is_truetype_font_installed("Arial"))
3508 skip("Arial is not installed\n");
3509 return;
3512 hdc = CreateCompatibleDC(0);
3513 memset(&lf, 0, sizeof(lf));
3514 lstrcpyA(lf.lfFaceName, "Arial");
3515 lf.lfHeight = 72;
3516 lf.lfOrientation = lf.lfEscapement = 900;
3517 hfont = create_font("orientation", &lf);
3518 old_hfont = SelectObject(hdc, hfont);
3519 ok(GetTextExtentExPointA(hdc, test_str, sizeof(test_str), 32767, NULL, NULL, &size), "GetTextExtentExPointA failed\n");
3520 ok(near_match(311, size.cx), "cx should be about 311, got %d\n", size.cx);
3521 ok(near_match(75, size.cy), "cy should be about 75, got %d\n", size.cy);
3522 SelectObject(hdc, old_hfont);
3523 DeleteObject(hfont);
3524 DeleteDC(hdc);
3527 static void test_oemcharset(void)
3529 HDC hdc;
3530 LOGFONTA lf, clf;
3531 HFONT hfont, old_hfont;
3532 int charset;
3534 hdc = CreateCompatibleDC(0);
3535 ZeroMemory(&lf, sizeof(lf));
3536 lf.lfHeight = 12;
3537 lf.lfCharSet = OEM_CHARSET;
3538 lf.lfPitchAndFamily = FIXED_PITCH | FF_MODERN;
3539 lstrcpyA(lf.lfFaceName, "Terminal");
3540 hfont = CreateFontIndirectA(&lf);
3541 old_hfont = SelectObject(hdc, hfont);
3542 charset = GetTextCharset(hdc);
3543 todo_wine
3544 ok(charset == OEM_CHARSET, "expected %d charset, got %d\n", OEM_CHARSET, charset);
3545 hfont = SelectObject(hdc, old_hfont);
3546 GetObjectA(hfont, sizeof(clf), &clf);
3547 ok(!lstrcmpA(clf.lfFaceName, lf.lfFaceName), "expected %s face name, got %s\n", lf.lfFaceName, clf.lfFaceName);
3548 ok(clf.lfPitchAndFamily == lf.lfPitchAndFamily, "expected %x family, got %x\n", lf.lfPitchAndFamily, clf.lfPitchAndFamily);
3549 ok(clf.lfCharSet == lf.lfCharSet, "expected %d charset, got %d\n", lf.lfCharSet, clf.lfCharSet);
3550 ok(clf.lfHeight == lf.lfHeight, "expected %d height, got %d\n", lf.lfHeight, clf.lfHeight);
3551 DeleteObject(hfont);
3552 DeleteDC(hdc);
3555 static int CALLBACK create_fixed_pitch_font_proc(const LOGFONT *lpelfe,
3556 const TEXTMETRIC *lpntme,
3557 DWORD FontType, LPARAM lParam)
3559 const NEWTEXTMETRICEX *lpntmex = (const NEWTEXTMETRICEX *)lpntme;
3560 CHARSETINFO csi;
3561 LOGFONT lf = *lpelfe;
3562 HFONT hfont;
3564 /* skip bitmap, proportional or vertical font */
3565 if ((FontType & TRUETYPE_FONTTYPE) == 0 ||
3566 (lf.lfPitchAndFamily & 0xf) != FIXED_PITCH ||
3567 lf.lfFaceName[0] == '@')
3568 return 1;
3570 /* skip linked font */
3571 if (!TranslateCharsetInfo((DWORD*)(INT_PTR)lpelfe->lfCharSet, &csi, TCI_SRCCHARSET) ||
3572 (lpntmex->ntmFontSig.fsCsb[0] & csi.fs.fsCsb[0]) == 0)
3573 return 1;
3575 /* test with an odd height */
3576 lf.lfHeight = -19;
3577 lf.lfWidth = 0;
3578 hfont = CreateFontIndirect(&lf);
3579 if (hfont)
3581 *(HFONT *)lParam = hfont;
3582 return 0;
3584 return 1;
3587 static void test_GetGlyphOutline(void)
3589 HDC hdc;
3590 GLYPHMETRICS gm, gm2;
3591 LOGFONTA lf;
3592 HFONT hfont, old_hfont;
3593 INT ret, ret2;
3594 static const struct
3596 UINT cs;
3597 UINT a;
3598 UINT w;
3599 } c[] =
3601 {ANSI_CHARSET, 0x30, 0x30},
3602 {SHIFTJIS_CHARSET, 0x82a0, 0x3042},
3603 {HANGEUL_CHARSET, 0x8141, 0xac02},
3604 {JOHAB_CHARSET, 0x8446, 0x3135},
3605 {GB2312_CHARSET, 0x8141, 0x4e04},
3606 {CHINESEBIG5_CHARSET, 0xa142, 0x3001}
3608 UINT i;
3610 if (!is_truetype_font_installed("Tahoma"))
3612 skip("Tahoma is not installed\n");
3613 return;
3616 hdc = CreateCompatibleDC(0);
3617 memset(&lf, 0, sizeof(lf));
3618 lf.lfHeight = 72;
3619 lstrcpyA(lf.lfFaceName, "Tahoma");
3620 SetLastError(0xdeadbeef);
3621 hfont = CreateFontIndirectA(&lf);
3622 ok(hfont != 0, "CreateFontIndirectA error %u\n", GetLastError());
3623 old_hfont = SelectObject(hdc, hfont);
3625 memset(&gm, 0, sizeof(gm));
3626 SetLastError(0xdeadbeef);
3627 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
3628 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %u\n", GetLastError());
3630 memset(&gm, 0, sizeof(gm));
3631 SetLastError(0xdeadbeef);
3632 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, NULL);
3633 ok(ret == GDI_ERROR, "GetGlyphOutlineA should fail\n");
3634 ok(GetLastError() == 0xdeadbeef ||
3635 GetLastError() == ERROR_INVALID_PARAMETER, /* win98, winMe */
3636 "expected 0xdeadbeef, got %u\n", GetLastError());
3638 memset(&gm, 0, sizeof(gm));
3639 SetLastError(0xdeadbeef);
3640 ret = GetGlyphOutlineW(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
3641 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
3642 ok(ret != GDI_ERROR, "GetGlyphOutlineW error %u\n", GetLastError());
3644 memset(&gm, 0, sizeof(gm));
3645 SetLastError(0xdeadbeef);
3646 ret = GetGlyphOutlineW(hdc, 'A', GGO_METRICS, &gm, 0, NULL, NULL);
3647 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
3649 ok(ret == GDI_ERROR, "GetGlyphOutlineW should fail\n");
3650 ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %u\n", GetLastError());
3653 /* test for needed buffer size request on space char */
3654 memset(&gm, 0, sizeof(gm));
3655 SetLastError(0xdeadbeef);
3656 ret = GetGlyphOutlineW(hdc, ' ', GGO_NATIVE, &gm, 0, NULL, &mat);
3657 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
3658 ok(ret == 0, "GetGlyphOutlineW should return 0 buffer size for space char\n");
3660 /* requesting buffer size for space char + error */
3661 memset(&gm, 0, sizeof(gm));
3662 SetLastError(0xdeadbeef);
3663 ret = GetGlyphOutlineW(0, ' ', GGO_NATIVE, &gm, 0, NULL, NULL);
3664 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
3666 ok(ret == GDI_ERROR, "GetGlyphOutlineW should return GDI_ERROR\n");
3667 ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %u\n", GetLastError());
3670 SelectObject(hdc, old_hfont);
3671 DeleteObject(hfont);
3673 for (i = 0; i < sizeof c / sizeof c[0]; ++i)
3675 static const MAT2 rotate_mat = {{0, 0}, {0, -1}, {0, 1}, {0, 0}};
3677 lf.lfFaceName[0] = '\0';
3678 lf.lfCharSet = c[i].cs;
3679 lf.lfPitchAndFamily = 0;
3680 if (EnumFontFamiliesEx(hdc, &lf, create_font_proc, (LPARAM)&hfont, 0))
3682 skip("TrueType font for charset %u is not installed\n", c[i].cs);
3683 continue;
3686 old_hfont = SelectObject(hdc, hfont);
3688 /* expected to ignore superfluous bytes (sigle-byte character) */
3689 ret = GetGlyphOutlineA(hdc, 0x8041, GGO_BITMAP, &gm, 0, NULL, &mat);
3690 ret2 = GetGlyphOutlineA(hdc, 0x41, GGO_BITMAP, &gm2, 0, NULL, &mat);
3691 ok(ret == ret2 && memcmp(&gm, &gm2, sizeof gm) == 0, "%d %d\n", ret, ret2);
3693 ret = GetGlyphOutlineA(hdc, 0xcc8041, GGO_BITMAP, &gm, 0, NULL, &mat);
3694 ok(ret == ret2 && memcmp(&gm, &gm2, sizeof gm) == 0,
3695 "Expected to ignore superfluous bytes, got %d %d\n", ret, ret2);
3697 /* expected to ignore superfluous bytes (double-byte character) */
3698 ret = GetGlyphOutlineA(hdc, c[i].a, GGO_BITMAP, &gm, 0, NULL, &mat);
3699 ret2 = GetGlyphOutlineA(hdc, c[i].a | 0xdead0000, GGO_BITMAP, &gm2, 0, NULL, &mat);
3700 ok(ret == ret2 && memcmp(&gm, &gm2, sizeof gm) == 0,
3701 "Expected to ignore superfluous bytes, got %d %d\n", ret, ret2);
3703 /* expected to match wide-char version results */
3704 ret2 = GetGlyphOutlineW(hdc, c[i].w, GGO_BITMAP, &gm2, 0, NULL, &mat);
3705 ok(ret == ret2 && memcmp(&gm, &gm2, sizeof gm) == 0, "%d %d\n", ret, ret2);
3707 if (EnumFontFamiliesEx(hdc, &lf, create_fixed_pitch_font_proc, (LPARAM)&hfont, 0))
3709 skip("Fixed-pitch TrueType font for charset %u is not available\n", c[i].cs);
3710 continue;
3712 DeleteObject(SelectObject(hdc, hfont));
3713 if (c[i].a <= 0xff)
3715 DeleteObject(SelectObject(hdc, old_hfont));
3716 continue;
3719 ret = GetObject(hfont, sizeof lf, &lf);
3720 ok(ret > 0, "GetObject error %u\n", GetLastError());
3722 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
3723 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %u\n", GetLastError());
3724 ret = GetGlyphOutlineA(hdc, c[i].a, GGO_METRICS, &gm2, 0, NULL, &mat);
3725 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %u\n", GetLastError());
3726 trace("Tests with height=%d,half=%d,full=%d,face=%s,charset=%d\n",
3727 -lf.lfHeight, gm.gmCellIncX, gm2.gmCellIncX, lf.lfFaceName, lf.lfCharSet);
3728 ok(gm2.gmCellIncX == gm.gmCellIncX * 2 || broken(gm2.gmCellIncX == -lf.lfHeight),
3729 "expected %d, got %d (%s:%d)\n",
3730 gm.gmCellIncX * 2, gm2.gmCellIncX, lf.lfFaceName, lf.lfCharSet);
3732 ret = GetGlyphOutlineA(hdc, c[i].a, GGO_METRICS, &gm2, 0, NULL, &rotate_mat);
3733 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %u\n", GetLastError());
3734 ok(gm2.gmCellIncY == -lf.lfHeight,
3735 "expected %d, got %d (%s:%d)\n",
3736 -lf.lfHeight, gm2.gmCellIncY, lf.lfFaceName, lf.lfCharSet);
3738 lf.lfItalic = TRUE;
3739 hfont = CreateFontIndirect(&lf);
3740 ok(hfont != NULL, "CreateFontIndirect error %u\n", GetLastError());
3741 DeleteObject(SelectObject(hdc, hfont));
3742 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
3743 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %u\n", GetLastError());
3744 ret = GetGlyphOutlineA(hdc, c[i].a, GGO_METRICS, &gm2, 0, NULL, &mat);
3745 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %u\n", GetLastError());
3746 ok(gm2.gmCellIncX == gm.gmCellIncX * 2 || broken(gm2.gmCellIncX == -lf.lfHeight),
3747 "expected %d, got %d (%s:%d)\n",
3748 gm.gmCellIncX * 2, gm2.gmCellIncX, lf.lfFaceName, lf.lfCharSet);
3750 lf.lfItalic = FALSE;
3751 lf.lfEscapement = lf.lfOrientation = 2700;
3752 hfont = CreateFontIndirect(&lf);
3753 ok(hfont != NULL, "CreateFontIndirect error %u\n", GetLastError());
3754 DeleteObject(SelectObject(hdc, hfont));
3755 ret = GetGlyphOutlineA(hdc, c[i].a, GGO_METRICS, &gm2, 0, NULL, &mat);
3756 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %u\n", GetLastError());
3757 ok(gm2.gmCellIncY == -lf.lfHeight,
3758 "expected %d, got %d (%s:%d)\n",
3759 -lf.lfHeight, gm2.gmCellIncY, lf.lfFaceName, lf.lfCharSet);
3761 hfont = SelectObject(hdc, old_hfont);
3762 DeleteObject(hfont);
3765 DeleteDC(hdc);
3768 /* bug #9995: there is a limit to the character width that can be specified */
3769 static void test_GetTextMetrics2(const char *fontname, int font_height)
3771 HFONT of, hf;
3772 HDC hdc;
3773 TEXTMETRICA tm;
3774 BOOL ret;
3775 int ave_width, height, width, ratio, scale;
3777 if (!is_truetype_font_installed( fontname)) {
3778 skip("%s is not installed\n", fontname);
3779 return;
3781 hdc = CreateCompatibleDC(0);
3782 ok( hdc != NULL, "CreateCompatibleDC failed\n");
3783 /* select width = 0 */
3784 hf = CreateFontA(font_height, 0, 0, 0, FW_REGULAR, FALSE, FALSE, FALSE,
3785 DEFAULT_CHARSET, OUT_TT_PRECIS, CLIP_LH_ANGLES,
3786 DEFAULT_QUALITY, VARIABLE_PITCH,
3787 fontname);
3788 ok( hf != NULL, "CreateFontA(%s, %d) failed\n", fontname, font_height);
3789 of = SelectObject( hdc, hf);
3790 ret = GetTextMetricsA( hdc, &tm);
3791 ok(ret, "GetTextMetricsA error %u\n", GetLastError());
3792 height = tm.tmHeight;
3793 ave_width = tm.tmAveCharWidth;
3794 SelectObject( hdc, of);
3795 DeleteObject( hf);
3797 trace("height %d, ave width %d\n", height, ave_width);
3799 for (width = ave_width * 2; /* nothing*/; width += ave_width)
3801 hf = CreateFont(height, width, 0, 0, FW_REGULAR, FALSE, FALSE, FALSE,
3802 DEFAULT_CHARSET, OUT_TT_PRECIS, CLIP_LH_ANGLES,
3803 DEFAULT_QUALITY, VARIABLE_PITCH, fontname);
3804 ok(hf != 0, "CreateFont failed\n");
3805 of = SelectObject(hdc, hf);
3806 ret = GetTextMetrics(hdc, &tm);
3807 ok(ret, "GetTextMetrics error %u\n", GetLastError());
3808 SelectObject(hdc, of);
3809 DeleteObject(hf);
3811 if (match_off_by_1(tm.tmAveCharWidth, ave_width) || width / height > 200)
3812 break;
3815 DeleteDC(hdc);
3817 ratio = width / height;
3818 scale = width / ave_width;
3820 trace("max width/height ratio (%d / %d) %d, max width scale (%d / %d) %d\n",
3821 width, height, ratio, width, ave_width, scale);
3823 ok(ratio >= 90 && ratio <= 110, "expected width/height ratio 90-110, got %d\n", ratio);
3826 static void test_CreateFontIndirect(void)
3828 LOGFONTA lf, getobj_lf;
3829 int ret, i;
3830 HFONT hfont;
3831 char TestName[][16] = {"Arial", "Arial Bold", "Arial Italic", "Arial Baltic"};
3833 memset(&lf, 0, sizeof(lf));
3834 lf.lfCharSet = ANSI_CHARSET;
3835 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
3836 lf.lfHeight = 16;
3837 lf.lfWidth = 16;
3838 lf.lfQuality = DEFAULT_QUALITY;
3839 lf.lfItalic = FALSE;
3840 lf.lfWeight = FW_DONTCARE;
3842 for (i = 0; i < sizeof(TestName)/sizeof(TestName[0]); i++)
3844 lstrcpyA(lf.lfFaceName, TestName[i]);
3845 hfont = CreateFontIndirectA(&lf);
3846 ok(hfont != 0, "CreateFontIndirectA failed\n");
3847 SetLastError(0xdeadbeef);
3848 ret = GetObject(hfont, sizeof(getobj_lf), &getobj_lf);
3849 ok(ret, "GetObject failed: %d\n", GetLastError());
3850 ok(lf.lfItalic == getobj_lf.lfItalic, "lfItalic: expect %02x got %02x\n", lf.lfItalic, getobj_lf.lfItalic);
3851 ok(lf.lfWeight == getobj_lf.lfWeight ||
3852 broken((SHORT)lf.lfWeight == getobj_lf.lfWeight), /* win9x */
3853 "lfWeight: expect %08x got %08x\n", lf.lfWeight, getobj_lf.lfWeight);
3854 ok(!lstrcmpA(lf.lfFaceName, getobj_lf.lfFaceName) ||
3855 broken(!memcmp(lf.lfFaceName, getobj_lf.lfFaceName, LF_FACESIZE-1)), /* win9x doesn't ensure '\0' termination */
3856 "font names don't match: %s != %s\n", lf.lfFaceName, getobj_lf.lfFaceName);
3857 DeleteObject(hfont);
3861 static void test_CreateFontIndirectEx(void)
3863 ENUMLOGFONTEXDVA lfex;
3864 HFONT hfont;
3866 if (!pCreateFontIndirectExA)
3868 win_skip("CreateFontIndirectExA is not available\n");
3869 return;
3872 if (!is_truetype_font_installed("Arial"))
3874 skip("Arial is not installed\n");
3875 return;
3878 SetLastError(0xdeadbeef);
3879 hfont = pCreateFontIndirectExA(NULL);
3880 ok(hfont == NULL, "got %p\n", hfont);
3881 ok(GetLastError() == 0xdeadbeef, "got error %d\n", GetLastError());
3883 memset(&lfex, 0, sizeof(lfex));
3884 lstrcpyA(lfex.elfEnumLogfontEx.elfLogFont.lfFaceName, "Arial");
3885 hfont = pCreateFontIndirectExA(&lfex);
3886 ok(hfont != 0, "CreateFontIndirectEx failed\n");
3887 if (hfont)
3888 check_font("Arial", &lfex.elfEnumLogfontEx.elfLogFont, hfont);
3889 DeleteObject(hfont);
3892 static void free_font(void *font)
3894 UnmapViewOfFile(font);
3897 static void *load_font(const char *font_name, DWORD *font_size)
3899 char file_name[MAX_PATH];
3900 HANDLE file, mapping;
3901 void *font;
3903 if (!GetWindowsDirectory(file_name, sizeof(file_name))) return NULL;
3904 strcat(file_name, "\\fonts\\");
3905 strcat(file_name, font_name);
3907 file = CreateFile(file_name, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, 0);
3908 if (file == INVALID_HANDLE_VALUE) return NULL;
3910 *font_size = GetFileSize(file, NULL);
3912 mapping = CreateFileMapping(file, NULL, PAGE_READONLY, 0, 0, NULL);
3913 if (!mapping)
3915 CloseHandle(file);
3916 return NULL;
3919 font = MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0);
3921 CloseHandle(file);
3922 CloseHandle(mapping);
3923 return font;
3926 static void test_AddFontMemResource(void)
3928 void *font;
3929 DWORD font_size, num_fonts;
3930 HANDLE ret;
3931 BOOL bRet;
3933 if (!pAddFontMemResourceEx || !pRemoveFontMemResourceEx)
3935 win_skip("AddFontMemResourceEx is not available on this platform\n");
3936 return;
3939 font = load_font("sserife.fon", &font_size);
3940 if (!font)
3942 skip("Unable to locate and load font sserife.fon\n");
3943 return;
3946 SetLastError(0xdeadbeef);
3947 ret = pAddFontMemResourceEx(NULL, 0, NULL, NULL);
3948 ok(!ret, "AddFontMemResourceEx should fail\n");
3949 ok(GetLastError() == ERROR_INVALID_PARAMETER,
3950 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
3951 GetLastError());
3953 SetLastError(0xdeadbeef);
3954 ret = pAddFontMemResourceEx(NULL, 10, NULL, NULL);
3955 ok(!ret, "AddFontMemResourceEx should fail\n");
3956 ok(GetLastError() == ERROR_INVALID_PARAMETER,
3957 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
3958 GetLastError());
3960 SetLastError(0xdeadbeef);
3961 ret = pAddFontMemResourceEx(NULL, 0, NULL, &num_fonts);
3962 ok(!ret, "AddFontMemResourceEx should fail\n");
3963 ok(GetLastError() == ERROR_INVALID_PARAMETER,
3964 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
3965 GetLastError());
3967 SetLastError(0xdeadbeef);
3968 ret = pAddFontMemResourceEx(NULL, 10, NULL, &num_fonts);
3969 ok(!ret, "AddFontMemResourceEx should fail\n");
3970 ok(GetLastError() == ERROR_INVALID_PARAMETER,
3971 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
3972 GetLastError());
3974 SetLastError(0xdeadbeef);
3975 ret = pAddFontMemResourceEx(font, 0, NULL, NULL);
3976 ok(!ret, "AddFontMemResourceEx should fail\n");
3977 ok(GetLastError() == ERROR_INVALID_PARAMETER,
3978 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
3979 GetLastError());
3981 SetLastError(0xdeadbeef);
3982 ret = pAddFontMemResourceEx(font, 10, NULL, NULL);
3983 ok(!ret, "AddFontMemResourceEx should fail\n");
3984 ok(GetLastError() == ERROR_INVALID_PARAMETER,
3985 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
3986 GetLastError());
3988 num_fonts = 0xdeadbeef;
3989 SetLastError(0xdeadbeef);
3990 ret = pAddFontMemResourceEx(font, 0, NULL, &num_fonts);
3991 ok(!ret, "AddFontMemResourceEx should fail\n");
3992 ok(GetLastError() == ERROR_INVALID_PARAMETER,
3993 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
3994 GetLastError());
3995 ok(num_fonts == 0xdeadbeef, "number of loaded fonts should be 0xdeadbeef\n");
3997 if (0) /* hangs under windows 2000 */
3999 num_fonts = 0xdeadbeef;
4000 SetLastError(0xdeadbeef);
4001 ret = pAddFontMemResourceEx(font, 10, NULL, &num_fonts);
4002 ok(!ret, "AddFontMemResourceEx should fail\n");
4003 ok(GetLastError() == 0xdeadbeef,
4004 "Expected GetLastError() to return 0xdeadbeef, got %u\n",
4005 GetLastError());
4006 ok(num_fonts == 0xdeadbeef, "number of loaded fonts should be 0xdeadbeef\n");
4009 num_fonts = 0xdeadbeef;
4010 SetLastError(0xdeadbeef);
4011 ret = pAddFontMemResourceEx(font, font_size, NULL, &num_fonts);
4012 ok(ret != 0, "AddFontMemResourceEx error %d\n", GetLastError());
4013 ok(num_fonts != 0xdeadbeef, "number of loaded fonts should not be 0xdeadbeef\n");
4014 ok(num_fonts != 0, "number of loaded fonts should not be 0\n");
4016 free_font(font);
4018 SetLastError(0xdeadbeef);
4019 bRet = pRemoveFontMemResourceEx(ret);
4020 ok(bRet, "RemoveFontMemResourceEx error %d\n", GetLastError());
4022 /* test invalid pointer to number of loaded fonts */
4023 font = load_font("sserife.fon", &font_size);
4024 ok(font != NULL, "Unable to locate and load font sserife.fon\n");
4026 SetLastError(0xdeadbeef);
4027 ret = pAddFontMemResourceEx(font, font_size, NULL, (void *)0xdeadbeef);
4028 ok(!ret, "AddFontMemResourceEx should fail\n");
4029 ok(GetLastError() == 0xdeadbeef,
4030 "Expected GetLastError() to return 0xdeadbeef, got %u\n",
4031 GetLastError());
4033 SetLastError(0xdeadbeef);
4034 ret = pAddFontMemResourceEx(font, font_size, NULL, NULL);
4035 ok(!ret, "AddFontMemResourceEx should fail\n");
4036 ok(GetLastError() == ERROR_INVALID_PARAMETER,
4037 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
4038 GetLastError());
4040 free_font(font);
4043 static INT CALLBACK enum_fonts_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lparam)
4045 LOGFONT *lf;
4047 if (type != TRUETYPE_FONTTYPE) return 1;
4049 ok(ntm->tmWeight == elf->lfWeight, "expected %d got %d\n", ntm->tmWeight, elf->lfWeight);
4051 lf = (LOGFONT *)lparam;
4052 *lf = *elf;
4053 return 0;
4056 static INT CALLBACK enum_all_fonts_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lparam)
4058 int ret;
4059 LOGFONT *lf;
4061 if (type != TRUETYPE_FONTTYPE) return 1;
4063 lf = (LOGFONT *)lparam;
4064 ret = strcmp(lf->lfFaceName, elf->lfFaceName);
4065 if(ret == 0)
4067 ok(ntm->tmWeight == elf->lfWeight, "expected %d got %d\n", ntm->tmWeight, elf->lfWeight);
4068 *lf = *elf;
4069 return 0;
4071 return 1;
4074 static INT CALLBACK enum_with_magic_retval_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lparam)
4076 return lparam;
4079 static void test_EnumFonts(void)
4081 int ret;
4082 LOGFONT lf;
4083 HDC hdc;
4085 if (!is_truetype_font_installed("Arial"))
4087 skip("Arial is not installed\n");
4088 return;
4091 /* Windows uses localized font face names, so Arial Bold won't be found */
4092 if (PRIMARYLANGID(GetUserDefaultLangID()) != LANG_ENGLISH)
4094 skip("User locale is not English, skipping the test\n");
4095 return;
4098 hdc = CreateCompatibleDC(0);
4100 /* check that the enumproc's retval is returned */
4101 ret = EnumFontFamilies(hdc, NULL, enum_with_magic_retval_proc, 0xcafe);
4102 ok(ret == 0xcafe, "got %08x\n", ret);
4104 ret = EnumFontFamilies(hdc, "Arial", enum_fonts_proc, (LPARAM)&lf);
4105 ok(!ret, "font Arial is not enumerated\n");
4106 ret = strcmp(lf.lfFaceName, "Arial");
4107 ok(!ret, "expected Arial got %s\n", lf.lfFaceName);
4108 ok(lf.lfWeight == FW_NORMAL, "expected FW_NORMAL got %d\n", lf.lfWeight);
4110 lstrcpy(lf.lfFaceName, "Arial");
4111 ret = EnumFontFamilies(hdc, NULL, enum_all_fonts_proc, (LPARAM)&lf);
4112 ok(!ret, "font Arial is not enumerated\n");
4113 ret = strcmp(lf.lfFaceName, "Arial");
4114 ok(!ret, "expected Arial got %s\n", lf.lfFaceName);
4115 ok(lf.lfWeight == FW_NORMAL, "expected FW_NORMAL got %d\n", lf.lfWeight);
4117 ret = EnumFontFamilies(hdc, "Arial Bold", enum_fonts_proc, (LPARAM)&lf);
4118 ok(!ret, "font Arial Bold is not enumerated\n");
4119 ret = strcmp(lf.lfFaceName, "Arial");
4120 ok(!ret, "expected Arial got %s\n", lf.lfFaceName);
4121 ok(lf.lfWeight == FW_BOLD, "expected FW_BOLD got %d\n", lf.lfWeight);
4123 lstrcpy(lf.lfFaceName, "Arial Bold");
4124 ret = EnumFontFamilies(hdc, NULL, enum_all_fonts_proc, (LPARAM)&lf);
4125 ok(ret, "font Arial Bold should not be enumerated\n");
4127 ret = EnumFontFamilies(hdc, "Arial Bold Italic", enum_fonts_proc, (LPARAM)&lf);
4128 ok(!ret, "font Arial Bold Italic is not enumerated\n");
4129 ret = strcmp(lf.lfFaceName, "Arial");
4130 ok(!ret, "expected Arial got %s\n", lf.lfFaceName);
4131 ok(lf.lfWeight == FW_BOLD, "expected FW_BOLD got %d\n", lf.lfWeight);
4133 lstrcpy(lf.lfFaceName, "Arial Bold Italic");
4134 ret = EnumFontFamilies(hdc, NULL, enum_all_fonts_proc, (LPARAM)&lf);
4135 ok(ret, "font Arial Bold Italic should not be enumerated\n");
4137 ret = EnumFontFamilies(hdc, "Arial Italic Bold", enum_fonts_proc, (LPARAM)&lf);
4138 ok(ret, "font Arial Italic Bold should not be enumerated\n");
4140 lstrcpy(lf.lfFaceName, "Arial Italic Bold");
4141 ret = EnumFontFamilies(hdc, NULL, enum_all_fonts_proc, (LPARAM)&lf);
4142 ok(ret, "font Arial Italic Bold should not be enumerated\n");
4144 DeleteDC(hdc);
4147 static INT CALLBACK is_font_installed_fullname_proc(const LOGFONT *lf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
4149 const ENUMLOGFONT *elf = (const ENUMLOGFONT *)lf;
4150 const char *fullname = (const char *)lParam;
4152 if (!strcmp((const char *)elf->elfFullName, fullname)) return 0;
4154 return 1;
4157 static BOOL is_font_installed_fullname(const char *family, const char *fullname)
4159 HDC hdc = GetDC(0);
4160 BOOL ret = FALSE;
4162 if(!EnumFontFamiliesA(hdc, family, is_font_installed_fullname_proc, (LPARAM)fullname))
4163 ret = TRUE;
4165 ReleaseDC(0, hdc);
4166 return ret;
4169 static void test_fullname(void)
4171 static const char *TestName[] = {"Lucida Sans Demibold Roman", "Lucida Sans Italic", "Lucida Sans Regular"};
4172 WCHAR bufW[LF_FULLFACESIZE];
4173 char bufA[LF_FULLFACESIZE];
4174 HFONT hfont, of;
4175 LOGFONTA lf;
4176 HDC hdc;
4177 int i;
4178 DWORD ret;
4180 hdc = CreateCompatibleDC(0);
4181 ok(hdc != NULL, "CreateCompatibleDC failed\n");
4183 memset(&lf, 0, sizeof(lf));
4184 lf.lfCharSet = ANSI_CHARSET;
4185 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
4186 lf.lfHeight = 16;
4187 lf.lfWidth = 16;
4188 lf.lfQuality = DEFAULT_QUALITY;
4189 lf.lfItalic = FALSE;
4190 lf.lfWeight = FW_DONTCARE;
4192 for (i = 0; i < sizeof(TestName) / sizeof(TestName[0]); i++)
4194 if (!is_font_installed_fullname("Lucida Sans", TestName[i]))
4196 skip("%s is not installed\n", TestName[i]);
4197 continue;
4200 lstrcpyA(lf.lfFaceName, TestName[i]);
4201 hfont = CreateFontIndirectA(&lf);
4202 ok(hfont != 0, "CreateFontIndirectA failed\n");
4204 of = SelectObject(hdc, hfont);
4205 bufW[0] = 0;
4206 bufA[0] = 0;
4207 ret = get_ttf_nametable_entry(hdc, TT_NAME_ID_FULL_NAME, bufW, sizeof(bufW), TT_MS_LANGID_ENGLISH_UNITED_STATES);
4208 ok(ret, "face full name could not be read\n");
4209 WideCharToMultiByte(CP_ACP, 0, bufW, -1, bufA, sizeof(bufA), NULL, FALSE);
4210 ok(!lstrcmpA(bufA, TestName[i]), "font full names don't match: %s != %s\n", TestName[i], bufA);
4211 SelectObject(hdc, of);
4212 DeleteObject(hfont);
4214 DeleteDC(hdc);
4217 static WCHAR *prepend_at(WCHAR *family)
4219 if (!family)
4220 return NULL;
4222 memmove(family + 1, family, (lstrlenW(family) + 1) * sizeof(WCHAR));
4223 family[0] = '@';
4224 return family;
4227 static void test_fullname2_helper(const char *Family)
4229 char *FamilyName, *FaceName, *StyleName, *otmStr;
4230 struct enum_fullname_data efnd;
4231 WCHAR *bufW;
4232 char *bufA;
4233 HFONT hfont, of;
4234 LOGFONTA lf;
4235 HDC hdc;
4236 int i;
4237 DWORD otm_size, ret, buf_size;
4238 OUTLINETEXTMETRICA *otm;
4239 BOOL want_vertical, get_vertical;
4240 want_vertical = ( Family[0] == '@' );
4242 hdc = CreateCompatibleDC(0);
4243 ok(hdc != NULL, "CreateCompatibleDC failed\n");
4245 memset(&lf, 0, sizeof(lf));
4246 lf.lfCharSet = DEFAULT_CHARSET;
4247 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
4248 lf.lfHeight = 16;
4249 lf.lfWidth = 16;
4250 lf.lfQuality = DEFAULT_QUALITY;
4251 lf.lfItalic = FALSE;
4252 lf.lfWeight = FW_DONTCARE;
4253 lstrcpy(lf.lfFaceName, Family);
4254 efnd.total = 0;
4255 EnumFontFamiliesExA(hdc, &lf, enum_fullname_data_proc, (LPARAM)&efnd, 0);
4256 if (efnd.total == 0)
4257 skip("%s is not installed\n", lf.lfFaceName);
4259 for (i = 0; i < efnd.total; i++)
4261 FamilyName = (char *)efnd.elf[i].elfLogFont.lfFaceName;
4262 FaceName = (char *)efnd.elf[i].elfFullName;
4263 StyleName = (char *)efnd.elf[i].elfStyle;
4265 trace("Checking font %s:\nFamilyName: %s; FaceName: %s; StyleName: %s\n", Family, FamilyName, FaceName, StyleName);
4267 get_vertical = ( FamilyName[0] == '@' );
4268 ok(get_vertical == want_vertical, "Vertical flags don't match: %s %s\n", Family, FamilyName);
4270 lstrcpyA(lf.lfFaceName, FaceName);
4271 hfont = CreateFontIndirectA(&lf);
4272 ok(hfont != 0, "CreateFontIndirectA failed\n");
4274 of = SelectObject(hdc, hfont);
4275 buf_size = GetFontData(hdc, MS_NAME_TAG, 0, NULL, 0);
4276 ok(buf_size != GDI_ERROR, "no name table found\n");
4277 if (buf_size == GDI_ERROR) continue;
4279 bufW = HeapAlloc(GetProcessHeap(), 0, buf_size);
4280 bufA = HeapAlloc(GetProcessHeap(), 0, buf_size);
4282 otm_size = GetOutlineTextMetricsA(hdc, 0, NULL);
4283 otm = HeapAlloc(GetProcessHeap(), 0, otm_size);
4284 memset(otm, 0, otm_size);
4285 ret = GetOutlineTextMetrics(hdc, otm_size, otm);
4286 ok(ret != 0, "GetOutlineTextMetrics fails!\n");
4287 if (ret == 0) continue;
4289 bufW[0] = 0;
4290 bufA[0] = 0;
4291 ret = get_ttf_nametable_entry(hdc, TT_NAME_ID_FONT_FAMILY, bufW, buf_size, GetSystemDefaultLangID());
4292 if (!ret)
4294 trace("no localized FONT_FAMILY found.\n");
4295 ret = get_ttf_nametable_entry(hdc, TT_NAME_ID_FONT_FAMILY, bufW, buf_size, TT_MS_LANGID_ENGLISH_UNITED_STATES);
4297 ok(ret, "FAMILY (family name) could not be read\n");
4298 if (want_vertical) bufW = prepend_at(bufW);
4299 WideCharToMultiByte(CP_ACP, 0, bufW, -1, bufA, buf_size, NULL, FALSE);
4300 ok(!lstrcmpA(FamilyName, bufA), "font family names don't match: returned %s, expect %s\n", FamilyName, bufA);
4301 otmStr = (LPSTR)otm + (UINT_PTR)otm->otmpFamilyName;
4302 ok(!lstrcmpA(FamilyName, otmStr), "FamilyName %s doesn't match otmpFamilyName %s\n", FamilyName, otmStr);
4304 bufW[0] = 0;
4305 bufA[0] = 0;
4306 ret = get_ttf_nametable_entry(hdc, TT_NAME_ID_FULL_NAME, bufW, buf_size, GetSystemDefaultLangID());
4307 if (!ret)
4309 trace("no localized FULL_NAME found.\n");
4310 ret = get_ttf_nametable_entry(hdc, TT_NAME_ID_FULL_NAME, bufW, buf_size, TT_MS_LANGID_ENGLISH_UNITED_STATES);
4312 ok(ret, "FULL_NAME (face name) could not be read\n");
4313 if (want_vertical) bufW = prepend_at(bufW);
4314 WideCharToMultiByte(CP_ACP, 0, bufW, -1, bufA, buf_size, NULL, FALSE);
4315 ok(!lstrcmpA(FaceName, bufA), "font face names don't match: returned %s, expect %s\n", FaceName, bufA);
4316 otmStr = (LPSTR)otm + (UINT_PTR)otm->otmpFaceName;
4317 ok(!lstrcmpA(FaceName, otmStr), "FaceName %s doesn't match otmpFaceName %s\n", FaceName, otmStr);
4319 bufW[0] = 0;
4320 bufA[0] = 0;
4321 ret = get_ttf_nametable_entry(hdc, TT_NAME_ID_FONT_SUBFAMILY, bufW, buf_size, GetSystemDefaultLangID());
4322 if (!ret)
4324 trace("no localized FONT_SUBFAMILY found.\n");
4325 ret = get_ttf_nametable_entry(hdc, TT_NAME_ID_FONT_SUBFAMILY, bufW, buf_size, TT_MS_LANGID_ENGLISH_UNITED_STATES);
4327 ok(ret, "SUBFAMILY (style name) could not be read\n");
4328 WideCharToMultiByte(CP_ACP, 0, bufW, -1, bufA, buf_size, NULL, FALSE);
4329 ok(!lstrcmpA(StyleName, bufA), "style names don't match: returned %s, expect %s\n", StyleName, bufA);
4330 otmStr = (LPSTR)otm + (UINT_PTR)otm->otmpStyleName;
4331 ok(!lstrcmpA(StyleName, otmStr), "StyleName %s doesn't match otmpStyleName %s\n", StyleName, otmStr);
4333 bufW[0] = 0;
4334 bufA[0] = 0;
4335 ret = get_ttf_nametable_entry(hdc, TT_NAME_ID_UNIQUE_ID, bufW, buf_size, GetSystemDefaultLangID());
4336 if (!ret)
4338 trace("no localized UNIQUE_ID found.\n");
4339 ret = get_ttf_nametable_entry(hdc, TT_NAME_ID_UNIQUE_ID, bufW, buf_size, TT_MS_LANGID_ENGLISH_UNITED_STATES);
4341 ok(ret, "UNIQUE_ID (full name) could not be read\n");
4342 WideCharToMultiByte(CP_ACP, 0, bufW, -1, bufA, buf_size, NULL, FALSE);
4343 otmStr = (LPSTR)otm + (UINT_PTR)otm->otmpFullName;
4344 ok(!lstrcmpA(otmStr, bufA), "UNIQUE ID (full name) doesn't match: returned %s, expect %s\n", otmStr, bufA);
4346 SelectObject(hdc, of);
4347 DeleteObject(hfont);
4349 HeapFree(GetProcessHeap(), 0, otm);
4350 HeapFree(GetProcessHeap(), 0, bufW);
4351 HeapFree(GetProcessHeap(), 0, bufA);
4353 DeleteDC(hdc);
4356 static void test_fullname2(void)
4358 test_fullname2_helper("Arial");
4359 test_fullname2_helper("DejaVu Sans");
4360 test_fullname2_helper("Lucida Sans");
4361 test_fullname2_helper("Tahoma");
4362 test_fullname2_helper("Webdings");
4363 test_fullname2_helper("Wingdings");
4364 test_fullname2_helper("SimSun");
4365 test_fullname2_helper("NSimSun");
4366 test_fullname2_helper("MingLiu");
4367 test_fullname2_helper("PMingLiu");
4368 test_fullname2_helper("WenQuanYi Micro Hei");
4369 test_fullname2_helper("MS UI Gothic");
4370 test_fullname2_helper("Ume UI Gothic");
4371 test_fullname2_helper("MS Gothic");
4372 test_fullname2_helper("Ume Gothic");
4373 test_fullname2_helper("MS PGothic");
4374 test_fullname2_helper("Ume P Gothic");
4375 test_fullname2_helper("Gulim");
4376 test_fullname2_helper("Batang");
4377 test_fullname2_helper("UnBatang");
4378 test_fullname2_helper("UnDotum");
4379 test_fullname2_helper("@SimSun");
4380 test_fullname2_helper("@NSimSun");
4381 test_fullname2_helper("@MingLiu");
4382 test_fullname2_helper("@PMingLiu");
4383 test_fullname2_helper("@WenQuanYi Micro Hei");
4384 test_fullname2_helper("@MS UI Gothic");
4385 test_fullname2_helper("@Ume UI Gothic");
4386 test_fullname2_helper("@MS Gothic");
4387 test_fullname2_helper("@Ume Gothic");
4388 test_fullname2_helper("@MS PGothic");
4389 test_fullname2_helper("@Ume P Gothic");
4390 test_fullname2_helper("@Gulim");
4391 test_fullname2_helper("@Batang");
4392 test_fullname2_helper("@UnBatang");
4393 test_fullname2_helper("@UnDotum");
4397 static BOOL write_ttf_file(const char *fontname, char *tmp_name)
4399 char tmp_path[MAX_PATH];
4400 HRSRC rsrc;
4401 void *rsrc_data;
4402 DWORD rsrc_size;
4403 HANDLE hfile;
4404 BOOL ret;
4406 SetLastError(0xdeadbeef);
4407 rsrc = FindResource(GetModuleHandle(0), fontname, RT_RCDATA);
4408 ok(rsrc != 0, "FindResource error %d\n", GetLastError());
4409 if (!rsrc) return FALSE;
4410 SetLastError(0xdeadbeef);
4411 rsrc_data = LockResource(LoadResource(GetModuleHandle(0), rsrc));
4412 ok(rsrc_data != 0, "LockResource error %d\n", GetLastError());
4413 if (!rsrc_data) return FALSE;
4414 SetLastError(0xdeadbeef);
4415 rsrc_size = SizeofResource(GetModuleHandle(0), rsrc);
4416 ok(rsrc_size != 0, "SizeofResource error %d\n", GetLastError());
4417 if (!rsrc_size) return FALSE;
4419 SetLastError(0xdeadbeef);
4420 ret = GetTempPath(MAX_PATH, tmp_path);
4421 ok(ret, "GetTempPath() error %d\n", GetLastError());
4422 SetLastError(0xdeadbeef);
4423 ret = GetTempFileName(tmp_path, "ttf", 0, tmp_name);
4424 ok(ret, "GetTempFileName() error %d\n", GetLastError());
4426 SetLastError(0xdeadbeef);
4427 hfile = CreateFile(tmp_name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
4428 ok(hfile != INVALID_HANDLE_VALUE, "CreateFile() error %d\n", GetLastError());
4429 if (hfile == INVALID_HANDLE_VALUE) return FALSE;
4431 SetLastError(0xdeadbeef);
4432 ret = WriteFile(hfile, rsrc_data, rsrc_size, &rsrc_size, NULL);
4433 ok(ret, "WriteFile() error %d\n", GetLastError());
4435 CloseHandle(hfile);
4436 return ret;
4439 static void test_GetGlyphOutline_empty_contour(void)
4441 HDC hdc;
4442 LOGFONTA lf;
4443 HFONT hfont, hfont_prev;
4444 TTPOLYGONHEADER *header;
4445 GLYPHMETRICS gm;
4446 char buf[1024];
4447 DWORD ret;
4449 memset(&lf, 0, sizeof(lf));
4450 lf.lfHeight = 72;
4451 lstrcpyA(lf.lfFaceName, "wine_test");
4453 hfont = CreateFontIndirectA(&lf);
4454 ok(hfont != 0, "CreateFontIndirectA error %u\n", GetLastError());
4456 hdc = GetDC(NULL);
4458 hfont_prev = SelectObject(hdc, hfont);
4459 ok(hfont_prev != NULL, "SelectObject failed\n");
4461 ret = GetGlyphOutlineW(hdc, 0xa8, GGO_NATIVE, &gm, 0, NULL, &mat);
4462 ok(ret == 228, "GetGlyphOutline returned %d, expected 228\n", ret);
4464 header = (TTPOLYGONHEADER*)buf;
4465 ret = GetGlyphOutlineW(hdc, 0xa8, GGO_NATIVE, &gm, sizeof(buf), buf, &mat);
4466 ok(ret == 228, "GetGlyphOutline returned %d, expected 228\n", ret);
4467 ok(header->cb == 36, "header->cb = %d, expected 36\n", header->cb);
4468 ok(header->dwType == TT_POLYGON_TYPE, "header->dwType = %d, expected TT_POLYGON_TYPE\n", header->dwType);
4469 header = (TTPOLYGONHEADER*)((char*)header+header->cb);
4470 ok(header->cb == 96, "header->cb = %d, expected 96\n", header->cb);
4471 header = (TTPOLYGONHEADER*)((char*)header+header->cb);
4472 ok(header->cb == 96, "header->cb = %d, expected 96\n", header->cb);
4474 SelectObject(hdc, hfont_prev);
4475 DeleteObject(hfont);
4476 ReleaseDC(NULL, hdc);
4479 static void test_CreateScalableFontResource(void)
4481 char ttf_name[MAX_PATH];
4482 char tmp_path[MAX_PATH];
4483 char fot_name[MAX_PATH];
4484 char *file_part;
4485 DWORD ret;
4487 if (!pAddFontResourceExA || !pRemoveFontResourceExA)
4489 win_skip("AddFontResourceExA is not available on this platform\n");
4490 return;
4493 if (!write_ttf_file("wine_test.ttf", ttf_name))
4495 skip("Failed to create ttf file for testing\n");
4496 return;
4499 trace("created %s\n", ttf_name);
4501 ret = is_truetype_font_installed("wine_test");
4502 ok(!ret, "font wine_test should not be enumerated\n");
4504 ret = GetTempPath(MAX_PATH, tmp_path);
4505 ok(ret, "GetTempPath() error %d\n", GetLastError());
4506 ret = GetTempFileName(tmp_path, "fot", 0, fot_name);
4507 ok(ret, "GetTempFileName() error %d\n", GetLastError());
4509 ret = GetFileAttributes(fot_name);
4510 ok(ret != INVALID_FILE_ATTRIBUTES, "file %s does not exist\n", fot_name);
4512 SetLastError(0xdeadbeef);
4513 ret = CreateScalableFontResource(0, fot_name, ttf_name, NULL);
4514 ok(!ret, "CreateScalableFontResource() should fail\n");
4515 ok(GetLastError() == ERROR_FILE_EXISTS, "not expected error %d\n", GetLastError());
4517 SetLastError(0xdeadbeef);
4518 ret = CreateScalableFontResource(0, fot_name, ttf_name, "");
4519 ok(!ret, "CreateScalableFontResource() should fail\n");
4520 ok(GetLastError() == ERROR_FILE_EXISTS, "not expected error %d\n", GetLastError());
4522 file_part = strrchr(ttf_name, '\\');
4523 SetLastError(0xdeadbeef);
4524 ret = CreateScalableFontResource(0, fot_name, file_part, tmp_path);
4525 ok(!ret, "CreateScalableFontResource() should fail\n");
4526 ok(GetLastError() == ERROR_FILE_EXISTS, "not expected error %d\n", GetLastError());
4528 SetLastError(0xdeadbeef);
4529 ret = CreateScalableFontResource(0, fot_name, "random file name", tmp_path);
4530 ok(!ret, "CreateScalableFontResource() should fail\n");
4531 ok(GetLastError() == ERROR_INVALID_PARAMETER, "not expected error %d\n", GetLastError());
4533 SetLastError(0xdeadbeef);
4534 ret = CreateScalableFontResource(0, fot_name, NULL, ttf_name);
4535 ok(!ret, "CreateScalableFontResource() should fail\n");
4536 ok(GetLastError() == ERROR_INVALID_PARAMETER, "not expected error %d\n", GetLastError());
4538 ret = DeleteFile(fot_name);
4539 ok(ret, "DeleteFile() error %d\n", GetLastError());
4541 ret = pRemoveFontResourceExA(fot_name, 0, 0);
4542 ok(!ret, "RemoveFontResourceEx() should fail\n");
4544 /* test public font resource */
4545 SetLastError(0xdeadbeef);
4546 ret = CreateScalableFontResource(0, fot_name, ttf_name, NULL);
4547 ok(ret, "CreateScalableFontResource() error %d\n", GetLastError());
4549 ret = is_truetype_font_installed("wine_test");
4550 ok(!ret, "font wine_test should not be enumerated\n");
4552 SetLastError(0xdeadbeef);
4553 ret = pAddFontResourceExA(fot_name, 0, 0);
4554 ok(ret, "AddFontResourceEx() error %d\n", GetLastError());
4556 ret = is_truetype_font_installed("wine_test");
4557 ok(ret, "font wine_test should be enumerated\n");
4559 test_GetGlyphOutline_empty_contour();
4561 ret = pRemoveFontResourceExA(fot_name, FR_PRIVATE, 0);
4562 ok(!ret, "RemoveFontResourceEx() with not matching flags should fail\n");
4564 SetLastError(0xdeadbeef);
4565 ret = pRemoveFontResourceExA(fot_name, 0, 0);
4566 ok(ret, "RemoveFontResourceEx() error %d\n", GetLastError());
4568 ret = is_truetype_font_installed("wine_test");
4569 ok(!ret, "font wine_test should not be enumerated\n");
4571 ret = pRemoveFontResourceExA(fot_name, 0, 0);
4572 ok(!ret, "RemoveFontResourceEx() should fail\n");
4574 DeleteFile(fot_name);
4576 /* test hidden font resource */
4577 SetLastError(0xdeadbeef);
4578 ret = CreateScalableFontResource(1, fot_name, ttf_name, NULL);
4579 ok(ret, "CreateScalableFontResource() error %d\n", GetLastError());
4581 ret = is_truetype_font_installed("wine_test");
4582 ok(!ret, "font wine_test should not be enumerated\n");
4584 SetLastError(0xdeadbeef);
4585 ret = pAddFontResourceExA(fot_name, 0, 0);
4586 ok(ret, "AddFontResourceEx() error %d\n", GetLastError());
4588 ret = is_truetype_font_installed("wine_test");
4589 todo_wine
4590 ok(!ret, "font wine_test should not be enumerated\n");
4592 /* XP allows removing a private font added with 0 flags */
4593 SetLastError(0xdeadbeef);
4594 ret = pRemoveFontResourceExA(fot_name, FR_PRIVATE, 0);
4595 ok(ret, "RemoveFontResourceEx() error %d\n", GetLastError());
4597 ret = is_truetype_font_installed("wine_test");
4598 ok(!ret, "font wine_test should not be enumerated\n");
4600 ret = pRemoveFontResourceExA(fot_name, 0, 0);
4601 ok(!ret, "RemoveFontResourceEx() should fail\n");
4603 DeleteFile(fot_name);
4604 DeleteFile(ttf_name);
4607 static void check_vertical_font(const char *name, BOOL *installed, BOOL *selected, GLYPHMETRICS *gm, WORD *gi)
4609 LOGFONTA lf;
4610 HFONT hfont, hfont_prev;
4611 HDC hdc;
4612 char facename[100];
4613 DWORD ret;
4614 static const WCHAR str[] = { 0x2025 };
4616 *installed = is_truetype_font_installed(name);
4618 lf.lfHeight = -18;
4619 lf.lfWidth = 0;
4620 lf.lfEscapement = 0;
4621 lf.lfOrientation = 0;
4622 lf.lfWeight = FW_DONTCARE;
4623 lf.lfItalic = 0;
4624 lf.lfUnderline = 0;
4625 lf.lfStrikeOut = 0;
4626 lf.lfCharSet = DEFAULT_CHARSET;
4627 lf.lfOutPrecision = OUT_TT_ONLY_PRECIS;
4628 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
4629 lf.lfQuality = DEFAULT_QUALITY;
4630 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
4631 strcpy(lf.lfFaceName, name);
4633 hfont = CreateFontIndirectA(&lf);
4634 ok(hfont != NULL, "CreateFontIndirectA failed\n");
4636 hdc = GetDC(NULL);
4638 hfont_prev = SelectObject(hdc, hfont);
4639 ok(hfont_prev != NULL, "SelectObject failed\n");
4641 ret = GetTextFaceA(hdc, sizeof facename, facename);
4642 ok(ret, "GetTextFaceA failed\n");
4643 *selected = !strcmp(facename, name);
4645 ret = GetGlyphOutlineW(hdc, 0x2025, GGO_METRICS, gm, 0, NULL, &mat);
4646 ok(ret != GDI_ERROR, "GetGlyphOutlineW failed\n");
4647 if (!*selected)
4648 memset(gm, 0, sizeof *gm);
4650 ret = pGetGlyphIndicesW(hdc, str, 1, gi, 0);
4651 ok(ret != GDI_ERROR, "GetGlyphIndicesW failed\n");
4653 SelectObject(hdc, hfont_prev);
4654 DeleteObject(hfont);
4655 ReleaseDC(NULL, hdc);
4658 static void test_vertical_font(void)
4660 char ttf_name[MAX_PATH];
4661 int num;
4662 BOOL ret, installed, selected;
4663 GLYPHMETRICS gm;
4664 WORD hgi, vgi;
4666 if (!pAddFontResourceExA || !pRemoveFontResourceExA || !pGetGlyphIndicesW)
4668 win_skip("AddFontResourceExA or GetGlyphIndicesW is not available on this platform\n");
4669 return;
4672 if (!write_ttf_file("vertical.ttf", ttf_name))
4674 skip("Failed to create ttf file for testing\n");
4675 return;
4678 num = pAddFontResourceExA(ttf_name, FR_PRIVATE, 0);
4679 ok(num == 2, "AddFontResourceExA should add 2 fonts from vertical.ttf\n");
4681 check_vertical_font("@WineTestVertical", &installed, &selected, &gm, &hgi);
4682 ok(installed, "@WineTestVertical is not installed\n");
4683 ok(selected, "@WineTestVertical is not selected\n");
4684 ok(gm.gmBlackBoxX > gm.gmBlackBoxY,
4685 "gmBlackBoxX(%u) should be greater than gmBlackBoxY(%u) if horizontal\n",
4686 gm.gmBlackBoxX, gm.gmBlackBoxY);
4688 check_vertical_font("@@WineTestVertical", &installed, &selected, &gm, &vgi);
4689 ok(installed, "@@WineTestVertical is not installed\n");
4690 ok(selected, "@@WineTestVertical is not selected\n");
4691 ok(gm.gmBlackBoxX < gm.gmBlackBoxY,
4692 "gmBlackBoxX(%u) should be less than gmBlackBoxY(%u) if vertical\n",
4693 gm.gmBlackBoxX, gm.gmBlackBoxY);
4695 ok(hgi == vgi, "different glyph h:%u v:%u\n", hgi, vgi);
4697 ret = pRemoveFontResourceExA(ttf_name, FR_PRIVATE, 0);
4698 ok(ret, "RemoveFontResourceEx() error %d\n", GetLastError());
4700 DeleteFile(ttf_name);
4703 static INT CALLBACK has_vertical_font_proc(const LOGFONT *lf, const TEXTMETRIC *ntm,
4704 DWORD type, LPARAM lParam)
4706 if (lf->lfFaceName[0] == '@') {
4707 return 0;
4709 return 1;
4712 static void test_east_asian_font_selection(void)
4714 HDC hdc;
4715 UINT charset[] = { SHIFTJIS_CHARSET, HANGEUL_CHARSET, JOHAB_CHARSET,
4716 GB2312_CHARSET, CHINESEBIG5_CHARSET };
4717 size_t i;
4719 hdc = GetDC(NULL);
4721 for (i = 0; i < sizeof(charset)/sizeof(charset[0]); i++)
4723 LOGFONTA lf;
4724 HFONT hfont;
4725 char face_name[LF_FACESIZE];
4726 int ret;
4728 memset(&lf, 0, sizeof lf);
4729 lf.lfFaceName[0] = '\0';
4730 lf.lfCharSet = charset[i];
4732 if (EnumFontFamiliesEx(hdc, &lf, has_vertical_font_proc, 0, 0))
4734 skip("Vertical font for charset %u is not installed\n", charset[i]);
4735 continue;
4738 hfont = CreateFontIndirectA(&lf);
4739 hfont = SelectObject(hdc, hfont);
4740 memset(face_name, 0, sizeof face_name);
4741 ret = GetTextFaceA(hdc, sizeof face_name, face_name);
4742 ok(ret && face_name[0] != '@',
4743 "expected non-vertical face for charset %u, got %s\n", charset[i], face_name);
4744 DeleteObject(SelectObject(hdc, hfont));
4746 memset(&lf, 0, sizeof lf);
4747 strcpy(lf.lfFaceName, "@");
4748 lf.lfCharSet = charset[i];
4749 hfont = CreateFontIndirectA(&lf);
4750 hfont = SelectObject(hdc, hfont);
4751 memset(face_name, 0, sizeof face_name);
4752 ret = GetTextFaceA(hdc, sizeof face_name, face_name);
4753 ok(ret && face_name[0] == '@',
4754 "expected vertical face for charset %u, got %s\n", charset[i], face_name);
4755 DeleteObject(SelectObject(hdc, hfont));
4757 ReleaseDC(NULL, hdc);
4760 static int get_font_dpi(const LOGFONT *lf)
4762 HDC hdc = CreateCompatibleDC(0);
4763 HFONT hfont;
4764 TEXTMETRIC tm;
4765 int ret;
4767 hfont = CreateFontIndirect(lf);
4768 ok(hfont != 0, "CreateFontIndirect failed\n");
4770 SelectObject(hdc, hfont);
4771 ret = GetTextMetrics(hdc, &tm);
4772 ok(ret, "GetTextMetrics failed\n");
4773 ret = tm.tmDigitizedAspectX;
4775 DeleteDC(hdc);
4776 DeleteObject(hfont);
4778 return ret;
4781 static void test_stock_fonts(void)
4783 static const int font[] =
4785 ANSI_FIXED_FONT, ANSI_VAR_FONT, SYSTEM_FONT, DEVICE_DEFAULT_FONT, DEFAULT_GUI_FONT
4786 /* SYSTEM_FIXED_FONT, OEM_FIXED_FONT */
4788 static const struct test_data
4790 int charset, weight, height, dpi;
4791 const char face_name[LF_FACESIZE];
4792 } td[][11] =
4794 { /* ANSI_FIXED_FONT */
4795 { DEFAULT_CHARSET, FW_NORMAL, 12, 96, "Courier" },
4796 { DEFAULT_CHARSET, FW_NORMAL, 12, 120, "Courier" },
4797 { 0 }
4799 { /* ANSI_VAR_FONT */
4800 { DEFAULT_CHARSET, FW_NORMAL, 12, 96, "MS Sans Serif" },
4801 { DEFAULT_CHARSET, FW_NORMAL, 12, 120, "MS Sans Serif" },
4802 { 0 }
4804 { /* SYSTEM_FONT */
4805 { SHIFTJIS_CHARSET, FW_NORMAL, 18, 96, "System" },
4806 { SHIFTJIS_CHARSET, FW_NORMAL, 22, 120, "System" },
4807 { HANGEUL_CHARSET, FW_NORMAL, 16, 96, "System" },
4808 { HANGEUL_CHARSET, FW_NORMAL, 20, 120, "System" },
4809 { DEFAULT_CHARSET, FW_BOLD, 16, 96, "System" },
4810 { DEFAULT_CHARSET, FW_BOLD, 20, 120, "System" },
4811 { 0 }
4813 { /* DEVICE_DEFAULT_FONT */
4814 { SHIFTJIS_CHARSET, FW_NORMAL, 18, 96, "System" },
4815 { SHIFTJIS_CHARSET, FW_NORMAL, 22, 120, "System" },
4816 { HANGEUL_CHARSET, FW_NORMAL, 16, 96, "System" },
4817 { HANGEUL_CHARSET, FW_NORMAL, 20, 120, "System" },
4818 { DEFAULT_CHARSET, FW_BOLD, 16, 96, "System" },
4819 { DEFAULT_CHARSET, FW_BOLD, 20, 120, "System" },
4820 { 0 }
4822 { /* DEFAULT_GUI_FONT */
4823 { SHIFTJIS_CHARSET, FW_NORMAL, -12, 96, "?MS UI Gothic" },
4824 { SHIFTJIS_CHARSET, FW_NORMAL, -15, 120, "?MS UI Gothic" },
4825 { HANGEUL_CHARSET, FW_NORMAL, -12, 96, "?Gulim" },
4826 { HANGEUL_CHARSET, FW_NORMAL, -15, 120, "?Gulim" },
4827 { GB2312_CHARSET, FW_NORMAL, -12, 96, "?SimHei" },
4828 { GB2312_CHARSET, FW_NORMAL, -15, 120, "?SimHei" },
4829 { CHINESEBIG5_CHARSET, FW_NORMAL, -12, 96, "?MingLiU" },
4830 { CHINESEBIG5_CHARSET, FW_NORMAL, -15, 120, "?MingLiU" },
4831 { DEFAULT_CHARSET, FW_NORMAL, -11, 96, "MS Shell Dlg" },
4832 { DEFAULT_CHARSET, FW_NORMAL, -13, 120, "MS Shell Dlg" },
4833 { 0 }
4836 int i, j;
4838 for (i = 0; i < sizeof(font)/sizeof(font[0]); i++)
4840 HFONT hfont;
4841 LOGFONT lf;
4842 int ret;
4844 hfont = GetStockObject(font[i]);
4845 ok(hfont != 0, "%d: GetStockObject(%d) failed\n", i, font[i]);
4847 ret = GetObject(hfont, sizeof(lf), &lf);
4848 if (ret != sizeof(lf))
4850 /* NT4 */
4851 win_skip("%d: GetObject returned %d instead of sizeof(LOGFONT)\n", i, ret);
4852 continue;
4855 for (j = 0; td[i][j].face_name[0] != 0; j++)
4857 if (lf.lfCharSet != td[i][j].charset && td[i][j].charset != DEFAULT_CHARSET)
4859 continue;
4862 ret = get_font_dpi(&lf);
4863 if (ret != td[i][j].dpi)
4865 trace("%d(%d): font %s %d dpi doesn't match test data %d\n",
4866 i, j, lf.lfFaceName, ret, td[i][j].dpi);
4867 continue;
4870 ok(td[i][j].weight == lf.lfWeight, "%d(%d): expected lfWeight %d, got %d\n", i, j, td[i][j].weight, lf.lfWeight);
4871 ok(td[i][j].height == lf.lfHeight, "%d(%d): expected lfHeight %d, got %d\n", i, j, td[i][j].height, lf.lfHeight);
4872 if (td[i][j].face_name[0] == '?')
4874 /* Wine doesn't have this font, skip this case for now.
4875 Actually, the face name is localized on Windows and varies
4876 dpending on Windows versions (e.g. Japanese NT4 vs win2k). */
4877 trace("%d(%d): default gui font is %s\n", i, j, lf.lfFaceName);
4879 else
4881 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);
4883 break;
4888 START_TEST(font)
4890 init();
4892 test_stock_fonts();
4893 test_logfont();
4894 test_bitmap_font();
4895 test_outline_font();
4896 test_bitmap_font_metrics();
4897 test_GdiGetCharDimensions();
4898 test_GetCharABCWidths();
4899 test_text_extents();
4900 test_GetGlyphIndices();
4901 test_GetKerningPairs();
4902 test_GetOutlineTextMetrics();
4903 test_SetTextJustification();
4904 test_font_charset();
4905 test_GetFontUnicodeRanges();
4906 test_nonexistent_font();
4907 test_orientation();
4908 test_height_selection();
4909 test_AddFontMemResource();
4910 test_EnumFonts();
4912 /* On Windows Arial has a lot of default charset aliases such as Arial Cyr,
4913 * I'd like to avoid them in this test.
4915 test_EnumFontFamilies("Arial Black", ANSI_CHARSET);
4916 test_EnumFontFamilies("Symbol", SYMBOL_CHARSET);
4917 if (is_truetype_font_installed("Arial Black") &&
4918 (is_truetype_font_installed("Symbol") || is_truetype_font_installed("Wingdings")))
4920 test_EnumFontFamilies("", ANSI_CHARSET);
4921 test_EnumFontFamilies("", SYMBOL_CHARSET);
4922 test_EnumFontFamilies("", DEFAULT_CHARSET);
4924 else
4925 skip("Arial Black or Symbol/Wingdings is not installed\n");
4926 test_EnumFontFamiliesEx_default_charset();
4927 test_GetTextMetrics();
4928 test_GdiRealizationInfo();
4929 test_GetTextFace();
4930 test_GetGlyphOutline();
4931 test_GetTextMetrics2("Tahoma", -11);
4932 test_GetTextMetrics2("Tahoma", -55);
4933 test_GetTextMetrics2("Tahoma", -110);
4934 test_GetTextMetrics2("Arial", -11);
4935 test_GetTextMetrics2("Arial", -55);
4936 test_GetTextMetrics2("Arial", -110);
4937 test_CreateFontIndirect();
4938 test_CreateFontIndirectEx();
4939 test_oemcharset();
4940 test_fullname();
4941 test_fullname2();
4942 test_east_asian_font_selection();
4944 /* These tests should be last test until RemoveFontResource
4945 * is properly implemented.
4947 test_vertical_font();
4948 test_CreateScalableFontResource();