gdi32/tests: Add a test for GetGlyphOutline with an insufficient buffer.
[wine/multimedia.git] / dlls / gdi32 / tests / font.c
blobeff1793d6522b9a50a0bb152223682caf8e98391
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, exact) (abs((a) - (b)) <= ((exact) ? 0 : 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 BOOL (WINAPI *pGetCharWidth32A)(HDC hdc, UINT first, UINT last, LPINT buffer);
46 static BOOL (WINAPI *pGetCharWidth32W)(HDC hdc, UINT first, UINT last, LPINT buffer);
47 static DWORD (WINAPI *pGetFontUnicodeRanges)(HDC hdc, LPGLYPHSET lpgs);
48 static DWORD (WINAPI *pGetGlyphIndicesA)(HDC hdc, LPCSTR lpstr, INT count, LPWORD pgi, DWORD flags);
49 static DWORD (WINAPI *pGetGlyphIndicesW)(HDC hdc, LPCWSTR lpstr, INT count, LPWORD pgi, DWORD flags);
50 static BOOL (WINAPI *pGetTextExtentExPointI)(HDC hdc, const WORD *indices, INT count, INT max_ext,
51 LPINT nfit, LPINT dxs, LPSIZE size );
52 static BOOL (WINAPI *pGdiRealizationInfo)(HDC hdc, DWORD *);
53 static HFONT (WINAPI *pCreateFontIndirectExA)(const ENUMLOGFONTEXDVA *);
54 static HANDLE (WINAPI *pAddFontMemResourceEx)(PVOID, DWORD, PVOID, DWORD *);
55 static BOOL (WINAPI *pRemoveFontMemResourceEx)(HANDLE);
56 static INT (WINAPI *pAddFontResourceExA)(LPCSTR, DWORD, PVOID);
57 static BOOL (WINAPI *pRemoveFontResourceExA)(LPCSTR, DWORD, PVOID);
59 static HMODULE hgdi32 = 0;
60 static const MAT2 mat = { {0,1}, {0,0}, {0,0}, {0,1} };
61 static WORD system_lang_id;
63 #ifdef WORDS_BIGENDIAN
64 #define GET_BE_WORD(x) (x)
65 #define GET_BE_DWORD(x) (x)
66 #else
67 #define GET_BE_WORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
68 #define GET_BE_DWORD(x) MAKELONG(GET_BE_WORD(HIWORD(x)), GET_BE_WORD(LOWORD(x)));
69 #endif
71 #define MS_MAKE_TAG(ch0, ch1, ch2, ch3) \
72 ((DWORD)(BYTE)(ch0) | ((DWORD)(BYTE)(ch1) << 8) | \
73 ((DWORD)(BYTE)(ch2) << 16) | ((DWORD)(BYTE)(ch3) << 24))
74 #define MS_OS2_TAG MS_MAKE_TAG('O','S','/','2')
75 #define MS_CMAP_TAG MS_MAKE_TAG('c','m','a','p')
76 #define MS_NAME_TAG MS_MAKE_TAG('n','a','m','e')
78 static void init(void)
80 hgdi32 = GetModuleHandleA("gdi32.dll");
82 pGdiGetCharDimensions = (void *)GetProcAddress(hgdi32, "GdiGetCharDimensions");
83 pGdiGetCodePage = (void *) GetProcAddress(hgdi32,"GdiGetCodePage");
84 pGetCharABCWidthsI = (void *)GetProcAddress(hgdi32, "GetCharABCWidthsI");
85 pGetCharABCWidthsA = (void *)GetProcAddress(hgdi32, "GetCharABCWidthsA");
86 pGetCharABCWidthsW = (void *)GetProcAddress(hgdi32, "GetCharABCWidthsW");
87 pGetCharABCWidthsFloatW = (void *)GetProcAddress(hgdi32, "GetCharABCWidthsFloatW");
88 pGetCharWidth32A = (void *)GetProcAddress(hgdi32, "GetCharWidth32A");
89 pGetCharWidth32W = (void *)GetProcAddress(hgdi32, "GetCharWidth32W");
90 pGetFontUnicodeRanges = (void *)GetProcAddress(hgdi32, "GetFontUnicodeRanges");
91 pGetGlyphIndicesA = (void *)GetProcAddress(hgdi32, "GetGlyphIndicesA");
92 pGetGlyphIndicesW = (void *)GetProcAddress(hgdi32, "GetGlyphIndicesW");
93 pGetTextExtentExPointI = (void *)GetProcAddress(hgdi32, "GetTextExtentExPointI");
94 pGdiRealizationInfo = (void *)GetProcAddress(hgdi32, "GdiRealizationInfo");
95 pCreateFontIndirectExA = (void *)GetProcAddress(hgdi32, "CreateFontIndirectExA");
96 pAddFontMemResourceEx = (void *)GetProcAddress(hgdi32, "AddFontMemResourceEx");
97 pRemoveFontMemResourceEx = (void *)GetProcAddress(hgdi32, "RemoveFontMemResourceEx");
98 pAddFontResourceExA = (void *)GetProcAddress(hgdi32, "AddFontResourceExA");
99 pRemoveFontResourceExA = (void *)GetProcAddress(hgdi32, "RemoveFontResourceExA");
101 system_lang_id = PRIMARYLANGID(GetSystemDefaultLangID());
104 static INT CALLBACK is_truetype_font_installed_proc(const LOGFONTA *elf, const TEXTMETRICA *ntm, DWORD type, LPARAM lParam)
106 if (type != TRUETYPE_FONTTYPE) return 1;
108 return 0;
111 static BOOL is_truetype_font_installed(const char *name)
113 HDC hdc = GetDC(0);
114 BOOL ret = FALSE;
116 if (!EnumFontFamiliesA(hdc, name, is_truetype_font_installed_proc, 0))
117 ret = TRUE;
119 ReleaseDC(0, hdc);
120 return ret;
123 static INT CALLBACK is_font_installed_proc(const LOGFONTA *elf, const TEXTMETRICA *ntm, DWORD type, LPARAM lParam)
125 return 0;
128 static BOOL is_font_installed(const char *name)
130 HDC hdc = GetDC(0);
131 BOOL ret = FALSE;
133 if(!EnumFontFamiliesA(hdc, name, is_font_installed_proc, 0))
134 ret = TRUE;
136 ReleaseDC(0, hdc);
137 return ret;
140 static void *get_res_data(const char *fontname, DWORD *rsrc_size)
142 HRSRC rsrc;
143 void *rsrc_data;
145 rsrc = FindResourceA(GetModuleHandleA(NULL), fontname, (LPCSTR)RT_RCDATA);
146 if (!rsrc) return NULL;
148 rsrc_data = LockResource(LoadResource(GetModuleHandleA(NULL), rsrc));
149 if (!rsrc_data) return NULL;
151 *rsrc_size = SizeofResource(GetModuleHandleA(NULL), rsrc);
152 if (!*rsrc_size) return NULL;
154 return rsrc_data;
157 static BOOL write_tmp_file( const void *data, DWORD *size, char *tmp_name )
159 char tmp_path[MAX_PATH];
160 HANDLE hfile;
161 BOOL ret;
163 GetTempPathA(MAX_PATH, tmp_path);
164 GetTempFileNameA(tmp_path, "ttf", 0, tmp_name);
166 hfile = CreateFileA(tmp_name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
167 if (hfile == INVALID_HANDLE_VALUE) return FALSE;
169 ret = WriteFile(hfile, data, *size, size, NULL);
171 CloseHandle(hfile);
172 return ret;
175 static BOOL write_ttf_file(const char *fontname, char *tmp_name)
177 void *rsrc_data;
178 DWORD rsrc_size;
180 rsrc_data = get_res_data( fontname, &rsrc_size );
181 if (!rsrc_data) return FALSE;
183 return write_tmp_file( rsrc_data, &rsrc_size, tmp_name );
186 static void check_font(const char* test, const LOGFONTA* lf, HFONT hfont)
188 LOGFONTA getobj_lf;
189 int ret, minlen = 0;
191 if (!hfont)
192 return;
194 ret = GetObjectA(hfont, sizeof(getobj_lf), &getobj_lf);
195 /* NT4 tries to be clever and only returns the minimum length */
196 while (lf->lfFaceName[minlen] && minlen < LF_FACESIZE-1)
197 minlen++;
198 minlen += FIELD_OFFSET(LOGFONTA, lfFaceName) + 1;
199 ok(ret == sizeof(LOGFONTA) || ret == minlen, "%s: GetObject returned %d\n", test, ret);
200 ok(lf->lfHeight == getobj_lf.lfHeight ||
201 broken((SHORT)lf->lfHeight == getobj_lf.lfHeight), /* win9x */
202 "lfHeight: expect %08x got %08x\n", lf->lfHeight, getobj_lf.lfHeight);
203 ok(lf->lfWidth == getobj_lf.lfWidth ||
204 broken((SHORT)lf->lfWidth == getobj_lf.lfWidth), /* win9x */
205 "lfWidth: expect %08x got %08x\n", lf->lfWidth, getobj_lf.lfWidth);
206 ok(lf->lfEscapement == getobj_lf.lfEscapement ||
207 broken((SHORT)lf->lfEscapement == getobj_lf.lfEscapement), /* win9x */
208 "lfEscapement: expect %08x got %08x\n", lf->lfEscapement, getobj_lf.lfEscapement);
209 ok(lf->lfOrientation == getobj_lf.lfOrientation ||
210 broken((SHORT)lf->lfOrientation == getobj_lf.lfOrientation), /* win9x */
211 "lfOrientation: expect %08x got %08x\n", lf->lfOrientation, getobj_lf.lfOrientation);
212 ok(lf->lfWeight == getobj_lf.lfWeight ||
213 broken((SHORT)lf->lfWeight == getobj_lf.lfWeight), /* win9x */
214 "lfWeight: expect %08x got %08x\n", lf->lfWeight, getobj_lf.lfWeight);
215 ok(lf->lfItalic == getobj_lf.lfItalic, "lfItalic: expect %02x got %02x\n", lf->lfItalic, getobj_lf.lfItalic);
216 ok(lf->lfUnderline == getobj_lf.lfUnderline, "lfUnderline: expect %02x got %02x\n", lf->lfUnderline, getobj_lf.lfUnderline);
217 ok(lf->lfStrikeOut == getobj_lf.lfStrikeOut, "lfStrikeOut: expect %02x got %02x\n", lf->lfStrikeOut, getobj_lf.lfStrikeOut);
218 ok(lf->lfCharSet == getobj_lf.lfCharSet, "lfCharSet: expect %02x got %02x\n", lf->lfCharSet, getobj_lf.lfCharSet);
219 ok(lf->lfOutPrecision == getobj_lf.lfOutPrecision, "lfOutPrecision: expect %02x got %02x\n", lf->lfOutPrecision, getobj_lf.lfOutPrecision);
220 ok(lf->lfClipPrecision == getobj_lf.lfClipPrecision, "lfClipPrecision: expect %02x got %02x\n", lf->lfClipPrecision, getobj_lf.lfClipPrecision);
221 ok(lf->lfQuality == getobj_lf.lfQuality, "lfQuality: expect %02x got %02x\n", lf->lfQuality, getobj_lf.lfQuality);
222 ok(lf->lfPitchAndFamily == getobj_lf.lfPitchAndFamily, "lfPitchAndFamily: expect %02x got %02x\n", lf->lfPitchAndFamily, getobj_lf.lfPitchAndFamily);
223 ok(!lstrcmpA(lf->lfFaceName, getobj_lf.lfFaceName) ||
224 broken(!memcmp(lf->lfFaceName, getobj_lf.lfFaceName, LF_FACESIZE-1)), /* win9x doesn't ensure '\0' termination */
225 "%s: font names don't match: %s != %s\n", test, lf->lfFaceName, getobj_lf.lfFaceName);
228 static HFONT create_font(const char* test, const LOGFONTA* lf)
230 HFONT hfont = CreateFontIndirectA(lf);
231 ok(hfont != 0, "%s: CreateFontIndirect failed\n", test);
232 if (hfont)
233 check_font(test, lf, hfont);
234 return hfont;
237 static void test_logfont(void)
239 LOGFONTA lf;
240 HFONT hfont;
242 memset(&lf, 0, sizeof lf);
244 lf.lfCharSet = ANSI_CHARSET;
245 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
246 lf.lfWeight = FW_DONTCARE;
247 lf.lfHeight = 16;
248 lf.lfWidth = 16;
249 lf.lfQuality = DEFAULT_QUALITY;
251 lstrcpyA(lf.lfFaceName, "Arial");
252 hfont = create_font("Arial", &lf);
253 DeleteObject(hfont);
255 memset(&lf, 'A', sizeof(lf));
256 hfont = CreateFontIndirectA(&lf);
257 ok(hfont != 0, "CreateFontIndirectA with strange LOGFONT failed\n");
259 lf.lfFaceName[LF_FACESIZE - 1] = 0;
260 check_font("AAA...", &lf, hfont);
261 DeleteObject(hfont);
264 static INT CALLBACK font_enum_proc(const LOGFONTA *elf, const TEXTMETRICA *ntm, DWORD type, LPARAM lParam)
266 if (type & RASTER_FONTTYPE)
268 LOGFONTA *lf = (LOGFONTA *)lParam;
269 *lf = *elf;
270 return 0; /* stop enumeration */
273 return 1; /* continue enumeration */
276 static void compare_tm(const TEXTMETRICA *tm, const TEXTMETRICA *otm)
278 ok(tm->tmHeight == otm->tmHeight, "tmHeight %d != %d\n", tm->tmHeight, otm->tmHeight);
279 ok(tm->tmAscent == otm->tmAscent, "tmAscent %d != %d\n", tm->tmAscent, otm->tmAscent);
280 ok(tm->tmDescent == otm->tmDescent, "tmDescent %d != %d\n", tm->tmDescent, otm->tmDescent);
281 ok(tm->tmInternalLeading == otm->tmInternalLeading, "tmInternalLeading %d != %d\n", tm->tmInternalLeading, otm->tmInternalLeading);
282 ok(tm->tmExternalLeading == otm->tmExternalLeading, "tmExternalLeading %d != %d\n", tm->tmExternalLeading, otm->tmExternalLeading);
283 ok(tm->tmAveCharWidth == otm->tmAveCharWidth, "tmAveCharWidth %d != %d\n", tm->tmAveCharWidth, otm->tmAveCharWidth);
284 ok(tm->tmMaxCharWidth == otm->tmMaxCharWidth, "tmMaxCharWidth %d != %d\n", tm->tmMaxCharWidth, otm->tmMaxCharWidth);
285 ok(tm->tmWeight == otm->tmWeight, "tmWeight %d != %d\n", tm->tmWeight, otm->tmWeight);
286 ok(tm->tmOverhang == otm->tmOverhang, "tmOverhang %d != %d\n", tm->tmOverhang, otm->tmOverhang);
287 ok(tm->tmDigitizedAspectX == otm->tmDigitizedAspectX, "tmDigitizedAspectX %d != %d\n", tm->tmDigitizedAspectX, otm->tmDigitizedAspectX);
288 ok(tm->tmDigitizedAspectY == otm->tmDigitizedAspectY, "tmDigitizedAspectY %d != %d\n", tm->tmDigitizedAspectY, otm->tmDigitizedAspectY);
289 ok(tm->tmFirstChar == otm->tmFirstChar, "tmFirstChar %d != %d\n", tm->tmFirstChar, otm->tmFirstChar);
290 ok(tm->tmLastChar == otm->tmLastChar, "tmLastChar %d != %d\n", tm->tmLastChar, otm->tmLastChar);
291 ok(tm->tmDefaultChar == otm->tmDefaultChar, "tmDefaultChar %d != %d\n", tm->tmDefaultChar, otm->tmDefaultChar);
292 ok(tm->tmBreakChar == otm->tmBreakChar, "tmBreakChar %d != %d\n", tm->tmBreakChar, otm->tmBreakChar);
293 ok(tm->tmItalic == otm->tmItalic, "tmItalic %d != %d\n", tm->tmItalic, otm->tmItalic);
294 ok(tm->tmUnderlined == otm->tmUnderlined, "tmUnderlined %d != %d\n", tm->tmUnderlined, otm->tmUnderlined);
295 ok(tm->tmStruckOut == otm->tmStruckOut, "tmStruckOut %d != %d\n", tm->tmStruckOut, otm->tmStruckOut);
296 ok(tm->tmPitchAndFamily == otm->tmPitchAndFamily, "tmPitchAndFamily %d != %d\n", tm->tmPitchAndFamily, otm->tmPitchAndFamily);
297 ok(tm->tmCharSet == otm->tmCharSet, "tmCharSet %d != %d\n", tm->tmCharSet, otm->tmCharSet);
300 static void test_font_metrics(HDC hdc, HFONT hfont, LONG lfHeight,
301 LONG lfWidth, const char *test_str,
302 INT test_str_len, const TEXTMETRICA *tm_orig,
303 const SIZE *size_orig, INT width_of_A_orig,
304 INT scale_x, INT scale_y)
306 LOGFONTA lf;
307 OUTLINETEXTMETRICA otm;
308 TEXTMETRICA tm;
309 SIZE size;
310 INT width_of_A, cx, cy;
311 UINT ret;
313 if (!hfont)
314 return;
316 ok(GetCurrentObject(hdc, OBJ_FONT) == hfont, "hfont should be selected\n");
318 GetObjectA(hfont, sizeof(lf), &lf);
320 if (GetOutlineTextMetricsA(hdc, 0, NULL))
322 otm.otmSize = sizeof(otm) / 2;
323 ret = GetOutlineTextMetricsA(hdc, otm.otmSize, &otm);
324 ok(ret == sizeof(otm)/2 /* XP */ ||
325 ret == 1 /* Win9x */, "expected sizeof(otm)/2, got %u\n", ret);
327 memset(&otm, 0x1, sizeof(otm));
328 otm.otmSize = sizeof(otm);
329 ret = GetOutlineTextMetricsA(hdc, otm.otmSize, &otm);
330 ok(ret == sizeof(otm) /* XP */ ||
331 ret == 1 /* Win9x */, "expected sizeof(otm), got %u\n", ret);
333 memset(&tm, 0x2, sizeof(tm));
334 ret = GetTextMetricsA(hdc, &tm);
335 ok(ret, "GetTextMetricsA failed\n");
336 /* the structure size is aligned */
337 if (memcmp(&tm, &otm.otmTextMetrics, FIELD_OFFSET(TEXTMETRICA, tmCharSet) + 1))
339 ok(0, "tm != otm\n");
340 compare_tm(&tm, &otm.otmTextMetrics);
343 tm = otm.otmTextMetrics;
344 if (0) /* these metrics are scaled too, but with rounding errors */
346 ok(otm.otmAscent == tm.tmAscent, "ascent %d != %d\n", otm.otmAscent, tm.tmAscent);
347 ok(otm.otmDescent == -tm.tmDescent, "descent %d != %d\n", otm.otmDescent, -tm.tmDescent);
349 ok(otm.otmMacAscent == tm.tmAscent, "ascent %d != %d\n", otm.otmMacAscent, tm.tmAscent);
350 ok(otm.otmDescent < 0, "otm.otmDescent should be < 0\n");
351 ok(otm.otmMacDescent < 0, "otm.otmMacDescent should be < 0\n");
352 ok(tm.tmDescent > 0, "tm.tmDescent should be > 0\n");
353 ok(otm.otmMacDescent == -tm.tmDescent, "descent %d != %d\n", otm.otmMacDescent, -tm.tmDescent);
354 ok(otm.otmEMSquare == 2048, "expected 2048, got %d\n", otm.otmEMSquare);
356 else
358 ret = GetTextMetricsA(hdc, &tm);
359 ok(ret, "GetTextMetricsA failed\n");
362 cx = tm.tmAveCharWidth / tm_orig->tmAveCharWidth;
363 cy = tm.tmHeight / tm_orig->tmHeight;
364 ok(cx == scale_x && cy == scale_y, "height %d: expected scale_x %d, scale_y %d, got cx %d, cy %d\n",
365 lfHeight, scale_x, scale_y, cx, cy);
366 ok(tm.tmHeight == tm_orig->tmHeight * scale_y, "height %d != %d\n", tm.tmHeight, tm_orig->tmHeight * scale_y);
367 ok(tm.tmAscent == tm_orig->tmAscent * scale_y, "ascent %d != %d\n", tm.tmAscent, tm_orig->tmAscent * scale_y);
368 ok(tm.tmDescent == tm_orig->tmDescent * scale_y, "descent %d != %d\n", tm.tmDescent, tm_orig->tmDescent * scale_y);
369 ok(near_match(tm.tmAveCharWidth, tm_orig->tmAveCharWidth * scale_x), "ave width %d != %d\n", tm.tmAveCharWidth, tm_orig->tmAveCharWidth * scale_x);
370 ok(near_match(tm.tmMaxCharWidth, tm_orig->tmMaxCharWidth * scale_x), "max width %d != %d\n", tm.tmMaxCharWidth, tm_orig->tmMaxCharWidth * scale_x);
372 ok(lf.lfHeight == lfHeight, "lfHeight %d != %d\n", lf.lfHeight, lfHeight);
373 if (lf.lfHeight)
375 if (lf.lfWidth)
376 ok(lf.lfWidth == tm.tmAveCharWidth, "lfWidth %d != tm %d\n", lf.lfWidth, tm.tmAveCharWidth);
378 else
379 ok(lf.lfWidth == lfWidth, "lfWidth %d != %d\n", lf.lfWidth, lfWidth);
381 GetTextExtentPoint32A(hdc, test_str, test_str_len, &size);
383 ok(near_match(size.cx, size_orig->cx * scale_x), "cx %d != %d\n", size.cx, size_orig->cx * scale_x);
384 ok(size.cy == size_orig->cy * scale_y, "cy %d != %d\n", size.cy, size_orig->cy * scale_y);
386 GetCharWidthA(hdc, 'A', 'A', &width_of_A);
388 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);
391 /* Test how GDI scales bitmap font metrics */
392 static void test_bitmap_font(void)
394 static const char test_str[11] = "Test String";
395 HDC hdc;
396 LOGFONTA bitmap_lf;
397 HFONT hfont, old_hfont;
398 TEXTMETRICA tm_orig;
399 SIZE size_orig;
400 INT ret, i, width_orig, height_orig, scale, lfWidth;
402 hdc = CreateCompatibleDC(0);
404 /* "System" has only 1 pixel size defined, otherwise the test breaks */
405 ret = EnumFontFamiliesA(hdc, "System", font_enum_proc, (LPARAM)&bitmap_lf);
406 if (ret)
408 ReleaseDC(0, hdc);
409 trace("no bitmap fonts were found, skipping the test\n");
410 return;
413 trace("found bitmap font %s, height %d\n", bitmap_lf.lfFaceName, bitmap_lf.lfHeight);
415 height_orig = bitmap_lf.lfHeight;
416 lfWidth = bitmap_lf.lfWidth;
418 hfont = create_font("bitmap", &bitmap_lf);
419 old_hfont = SelectObject(hdc, hfont);
420 ok(GetTextMetricsA(hdc, &tm_orig), "GetTextMetricsA failed\n");
421 ok(GetTextExtentPoint32A(hdc, test_str, sizeof(test_str), &size_orig), "GetTextExtentPoint32A failed\n");
422 ok(GetCharWidthA(hdc, 'A', 'A', &width_orig), "GetCharWidthA failed\n");
423 SelectObject(hdc, old_hfont);
424 DeleteObject(hfont);
426 bitmap_lf.lfHeight = 0;
427 bitmap_lf.lfWidth = 4;
428 hfont = create_font("bitmap", &bitmap_lf);
429 old_hfont = SelectObject(hdc, hfont);
430 test_font_metrics(hdc, hfont, 0, 4, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 1, 1);
431 SelectObject(hdc, old_hfont);
432 DeleteObject(hfont);
434 bitmap_lf.lfHeight = height_orig;
435 bitmap_lf.lfWidth = lfWidth;
437 /* test fractional scaling */
438 for (i = 1; i <= height_orig * 6; i++)
440 INT nearest_height;
442 bitmap_lf.lfHeight = i;
443 hfont = create_font("fractional", &bitmap_lf);
444 scale = (i + height_orig - 1) / height_orig;
445 nearest_height = scale * height_orig;
446 /* Only jump to the next height if the difference <= 25% original height */
447 if (scale > 2 && nearest_height - i > height_orig / 4) scale--;
448 /* The jump between unscaled and doubled is delayed by 1 in winnt+ but not in win9x,
449 so we'll not test this particular height. */
450 else if(scale == 2 && nearest_height - i == (height_orig / 4)) continue;
451 else if(scale == 2 && nearest_height - i > (height_orig / 4 - 1)) scale--;
452 old_hfont = SelectObject(hdc, hfont);
453 test_font_metrics(hdc, hfont, bitmap_lf.lfHeight, 0, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 1, scale);
454 SelectObject(hdc, old_hfont);
455 DeleteObject(hfont);
458 /* test integer scaling 3x2 */
459 bitmap_lf.lfHeight = height_orig * 2;
460 bitmap_lf.lfWidth *= 3;
461 hfont = create_font("3x2", &bitmap_lf);
462 old_hfont = SelectObject(hdc, hfont);
463 test_font_metrics(hdc, hfont, bitmap_lf.lfHeight, 0, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 3, 2);
464 SelectObject(hdc, old_hfont);
465 DeleteObject(hfont);
467 /* test integer scaling 3x3 */
468 bitmap_lf.lfHeight = height_orig * 3;
469 bitmap_lf.lfWidth = 0;
470 hfont = create_font("3x3", &bitmap_lf);
471 old_hfont = SelectObject(hdc, hfont);
472 test_font_metrics(hdc, hfont, bitmap_lf.lfHeight, 0, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 3, 3);
473 SelectObject(hdc, old_hfont);
474 DeleteObject(hfont);
476 DeleteDC(hdc);
479 /* Test how GDI scales outline font metrics */
480 static void test_outline_font(void)
482 static const char test_str[11] = "Test String";
483 HDC hdc, hdc_2;
484 LOGFONTA lf;
485 HFONT hfont, old_hfont, old_hfont_2;
486 OUTLINETEXTMETRICA otm;
487 SIZE size_orig;
488 INT width_orig, height_orig, lfWidth;
489 XFORM xform;
490 GLYPHMETRICS gm;
491 MAT2 mat2 = { {0x8000,0}, {0,0}, {0,0}, {0x8000,0} };
492 POINT pt;
493 INT ret;
495 if (!is_truetype_font_installed("Arial"))
497 skip("Arial is not installed\n");
498 return;
501 hdc = CreateCompatibleDC(0);
503 memset(&lf, 0, sizeof(lf));
504 strcpy(lf.lfFaceName, "Arial");
505 lf.lfHeight = 72;
506 hfont = create_font("outline", &lf);
507 old_hfont = SelectObject(hdc, hfont);
508 otm.otmSize = sizeof(otm);
509 ok(GetOutlineTextMetricsA(hdc, sizeof(otm), &otm), "GetTextMetricsA failed\n");
510 ok(GetTextExtentPoint32A(hdc, test_str, sizeof(test_str), &size_orig), "GetTextExtentPoint32A failed\n");
511 ok(GetCharWidthA(hdc, 'A', 'A', &width_orig), "GetCharWidthA failed\n");
513 test_font_metrics(hdc, hfont, lf.lfHeight, otm.otmTextMetrics.tmAveCharWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
514 SelectObject(hdc, old_hfont);
515 DeleteObject(hfont);
517 /* font of otmEMSquare height helps to avoid a lot of rounding errors */
518 lf.lfHeight = otm.otmEMSquare;
519 lf.lfHeight = -lf.lfHeight;
520 hfont = create_font("outline", &lf);
521 old_hfont = SelectObject(hdc, hfont);
522 otm.otmSize = sizeof(otm);
523 ok(GetOutlineTextMetricsA(hdc, sizeof(otm), &otm), "GetTextMetricsA failed\n");
524 ok(GetTextExtentPoint32A(hdc, test_str, sizeof(test_str), &size_orig), "GetTextExtentPoint32A failed\n");
525 ok(GetCharWidthA(hdc, 'A', 'A', &width_orig), "GetCharWidthA failed\n");
526 SelectObject(hdc, old_hfont);
527 DeleteObject(hfont);
529 height_orig = otm.otmTextMetrics.tmHeight;
530 lfWidth = otm.otmTextMetrics.tmAveCharWidth;
532 /* test integer scaling 3x2 */
533 lf.lfHeight = height_orig * 2;
534 lf.lfWidth = lfWidth * 3;
535 hfont = create_font("3x2", &lf);
536 old_hfont = SelectObject(hdc, hfont);
537 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 3, 2);
538 SelectObject(hdc, old_hfont);
539 DeleteObject(hfont);
541 /* test integer scaling 3x3 */
542 lf.lfHeight = height_orig * 3;
543 lf.lfWidth = lfWidth * 3;
544 hfont = create_font("3x3", &lf);
545 old_hfont = SelectObject(hdc, hfont);
546 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 3, 3);
547 SelectObject(hdc, old_hfont);
548 DeleteObject(hfont);
550 /* test integer scaling 1x1 */
551 lf.lfHeight = height_orig * 1;
552 lf.lfWidth = lfWidth * 1;
553 hfont = create_font("1x1", &lf);
554 old_hfont = SelectObject(hdc, hfont);
555 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
556 SelectObject(hdc, old_hfont);
557 DeleteObject(hfont);
559 /* test integer scaling 1x1 */
560 lf.lfHeight = height_orig;
561 lf.lfWidth = 0;
562 hfont = create_font("1x1", &lf);
563 old_hfont = SelectObject(hdc, hfont);
564 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
566 /* with an identity matrix */
567 memset(&gm, 0, sizeof(gm));
568 SetLastError(0xdeadbeef);
569 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
570 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
571 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
572 ok(gm.gmCellIncX == width_orig, "incX %d != %d\n", gm.gmCellIncX, width_orig);
573 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
574 /* with a custom matrix */
575 memset(&gm, 0, sizeof(gm));
576 SetLastError(0xdeadbeef);
577 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat2);
578 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
579 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
580 ok(gm.gmCellIncX == width_orig/2, "incX %d != %d\n", gm.gmCellIncX, width_orig/2);
581 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
583 /* Test that changing the DC transformation affects only the font
584 * selected on this DC and doesn't affect the same font selected on
585 * another DC.
587 hdc_2 = CreateCompatibleDC(0);
588 old_hfont_2 = SelectObject(hdc_2, hfont);
589 test_font_metrics(hdc_2, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
591 SetMapMode(hdc, MM_ANISOTROPIC);
593 /* font metrics on another DC should be unchanged */
594 test_font_metrics(hdc_2, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
596 /* test restrictions of compatibility mode GM_COMPATIBLE */
597 /* part 1: rescaling only X should not change font scaling on screen.
598 So compressing the X axis by 2 is not done, and this
599 appears as X scaling of 2 that no one requested. */
600 SetWindowExtEx(hdc, 100, 100, NULL);
601 SetViewportExtEx(hdc, 50, 100, NULL);
602 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 2, 1);
603 /* font metrics on another DC should be unchanged */
604 test_font_metrics(hdc_2, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
606 /* part 2: rescaling only Y should change font scaling.
607 As also X is scaled by a factor of 2, but this is not
608 requested by the DC transformation, we get a scaling factor
609 of 2 in the X coordinate. */
610 SetViewportExtEx(hdc, 100, 200, NULL);
611 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 2, 1);
612 /* font metrics on another DC should be unchanged */
613 test_font_metrics(hdc_2, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
615 /* restore scaling */
616 SetMapMode(hdc, MM_TEXT);
618 /* font metrics on another DC should be unchanged */
619 test_font_metrics(hdc_2, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
621 SelectObject(hdc_2, old_hfont_2);
622 DeleteDC(hdc_2);
624 if (!SetGraphicsMode(hdc, GM_ADVANCED))
626 SelectObject(hdc, old_hfont);
627 DeleteObject(hfont);
628 DeleteDC(hdc);
629 skip("GM_ADVANCED is not supported on this platform\n");
630 return;
633 xform.eM11 = 20.0f;
634 xform.eM12 = 0.0f;
635 xform.eM21 = 0.0f;
636 xform.eM22 = 20.0f;
637 xform.eDx = 0.0f;
638 xform.eDy = 0.0f;
640 SetLastError(0xdeadbeef);
641 ret = SetWorldTransform(hdc, &xform);
642 ok(ret, "SetWorldTransform error %u\n", GetLastError());
644 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
646 /* with an identity matrix */
647 memset(&gm, 0, sizeof(gm));
648 SetLastError(0xdeadbeef);
649 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
650 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
651 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
652 pt.x = width_orig; pt.y = 0;
653 LPtoDP(hdc, &pt, 1);
654 ok(gm.gmCellIncX == pt.x, "incX %d != %d\n", gm.gmCellIncX, pt.x);
655 ok(gm.gmCellIncX == 20 * width_orig, "incX %d != %d\n", gm.gmCellIncX, 20 * width_orig);
656 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
657 /* with a custom matrix */
658 memset(&gm, 0, sizeof(gm));
659 SetLastError(0xdeadbeef);
660 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat2);
661 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
662 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
663 pt.x = width_orig; pt.y = 0;
664 LPtoDP(hdc, &pt, 1);
665 ok(gm.gmCellIncX == pt.x/2, "incX %d != %d\n", gm.gmCellIncX, pt.x/2);
666 ok(near_match(gm.gmCellIncX, 10 * width_orig), "incX %d != %d\n", gm.gmCellIncX, 10 * width_orig);
667 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
669 SetLastError(0xdeadbeef);
670 ret = SetMapMode(hdc, MM_LOMETRIC);
671 ok(ret == MM_TEXT, "expected MM_TEXT, got %d, error %u\n", ret, GetLastError());
673 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
675 /* with an identity matrix */
676 memset(&gm, 0, sizeof(gm));
677 SetLastError(0xdeadbeef);
678 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
679 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
680 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
681 pt.x = width_orig; pt.y = 0;
682 LPtoDP(hdc, &pt, 1);
683 ok(near_match(gm.gmCellIncX, pt.x), "incX %d != %d\n", gm.gmCellIncX, pt.x);
684 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
685 /* with a custom matrix */
686 memset(&gm, 0, sizeof(gm));
687 SetLastError(0xdeadbeef);
688 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat2);
689 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
690 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
691 pt.x = width_orig; pt.y = 0;
692 LPtoDP(hdc, &pt, 1);
693 ok(near_match(gm.gmCellIncX, (pt.x + 1)/2), "incX %d != %d\n", gm.gmCellIncX, (pt.x + 1)/2);
694 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
696 SetLastError(0xdeadbeef);
697 ret = SetMapMode(hdc, MM_TEXT);
698 ok(ret == MM_LOMETRIC, "expected MM_LOMETRIC, got %d, error %u\n", ret, GetLastError());
700 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
702 /* with an identity matrix */
703 memset(&gm, 0, sizeof(gm));
704 SetLastError(0xdeadbeef);
705 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
706 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
707 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
708 pt.x = width_orig; pt.y = 0;
709 LPtoDP(hdc, &pt, 1);
710 ok(gm.gmCellIncX == pt.x, "incX %d != %d\n", gm.gmCellIncX, pt.x);
711 ok(gm.gmCellIncX == 20 * width_orig, "incX %d != %d\n", gm.gmCellIncX, 20 * width_orig);
712 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
713 /* with a custom matrix */
714 memset(&gm, 0, sizeof(gm));
715 SetLastError(0xdeadbeef);
716 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat2);
717 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
718 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
719 pt.x = width_orig; pt.y = 0;
720 LPtoDP(hdc, &pt, 1);
721 ok(gm.gmCellIncX == pt.x/2, "incX %d != %d\n", gm.gmCellIncX, pt.x/2);
722 ok(gm.gmCellIncX == 10 * width_orig, "incX %d != %d\n", gm.gmCellIncX, 10 * width_orig);
723 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
725 SelectObject(hdc, old_hfont);
726 DeleteObject(hfont);
727 DeleteDC(hdc);
730 static INT CALLBACK find_font_proc(const LOGFONTA *elf, const TEXTMETRICA *ntm, DWORD type, LPARAM lParam)
732 LOGFONTA *lf = (LOGFONTA *)lParam;
734 if (elf->lfHeight == lf->lfHeight && !strcmp(elf->lfFaceName, lf->lfFaceName))
736 *lf = *elf;
737 return 0; /* stop enumeration */
739 return 1; /* continue enumeration */
742 static BOOL is_CJK(void)
744 return (system_lang_id == LANG_CHINESE || system_lang_id == LANG_JAPANESE || system_lang_id == LANG_KOREAN);
747 #define FH_SCALE 0x80000000
748 static void test_bitmap_font_metrics(void)
750 static const struct font_data
752 const char face_name[LF_FACESIZE];
753 int weight, height, ascent, descent, int_leading, ext_leading;
754 int ave_char_width, max_char_width, dpi;
755 BYTE first_char, last_char, def_char, break_char;
756 DWORD ansi_bitfield;
757 WORD skip_lang_id;
758 int scaled_height;
759 } fd[] =
761 { "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 },
762 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 6, 11, 2, 2, 0, 5, 11, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC, 0, 13 },
763 { "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 },
764 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 8, 11, 2, 2, 0, 5, 11, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC, 0, 13 },
765 { "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 },
766 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 10, 11, 2, 2, 0, 5, 11, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC, 0, 13 },
767 { "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 },
768 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 14, 11, 2, 2, 0, 5, 11, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC, 0, 13 },
769 { "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 },
770 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 18, 13, 3, 3, 0, 7, 14, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC, 0, 16 },
772 { "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 },
773 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 6, 13, 3, 3, 0, 7, 14, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC, 0, 16 },
774 { "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 },
775 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 8, 13, 3, 3, 0, 7, 14, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC, 0, 16 },
776 { "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 },
777 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 10, 13, 3, 3, 0, 7, 14, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC, 0, 16 },
778 { "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 },
779 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 14, 13, 3, 3, 0, 7, 14, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC, 0, 16 },
780 { "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 },
781 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 18, 13, 3, 3, 0, 7, 14, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC, 0, 16 },
783 { "MS Sans Serif", FW_NORMAL, 13, 11, 2, 2, 0, 5, 11, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2 },
784 { "MS Sans Serif", FW_NORMAL, 13, 11, 2, 2, 0, 5, 11, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
785 { "MS Sans Serif", FW_NORMAL, 16, 13, 3, 3, 0, 7, 14, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2 },
786 { "MS Sans Serif", FW_NORMAL, 16, 13, 3, 3, 0, 7, 14, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
787 { "MS Sans Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 16, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 },
788 { "MS Sans Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 18, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN2 },
789 { "MS Sans Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 16, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
790 { "MS Sans Serif", FW_NORMAL, 24, 19, 5, 6, 0, 9, 19, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 },
791 { "MS Sans Serif", FW_NORMAL, 24, 19, 5, 6, 0, 9, 24, 96, 0x20, 0xff, 0x81, 0x40, FS_LATIN2 },
792 { "MS Sans Serif", FW_NORMAL, 24, 19, 5, 6, 0, 9, 20, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
793 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 5, 0, 12, 24, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 },
794 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 6, 0, 12, 24, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN2 },
795 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 5, 0, 12, 25, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
796 { "MS Sans Serif", FW_NORMAL, 37, 29, 8, 5, 0, 16, 32, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 },
797 { "MS Sans Serif", FW_NORMAL, 37, 29, 8, 5, 0, 16, 32, 96, 0x20, 0xff, 0x81, 0x40, FS_LATIN2 },
798 { "MS Sans Serif", FW_NORMAL, 37, 29, 8, 5, 0, 16, 32, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
800 { "MS Sans Serif", FW_NORMAL, 16, 13, 3, 3, 0, 7, 14, 120, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2 },
801 { "MS Sans Serif", FW_NORMAL, 16, 13, 3, 3, 0, 7, 14, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
802 { "MS Sans Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 18, 120, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2 },
803 { "MS Sans Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 17, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
804 { "MS Sans Serif", FW_NORMAL, 25, 20, 5, 5, 0, 10, 21, 120, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2 },
805 { "MS Sans Serif", FW_NORMAL, 25, 20, 5, 5, 0, 10, 21, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
806 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 6, 0, 12, 24, 120, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2 },
807 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 5, 0, 12, 24, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
808 { "MS Sans Serif", FW_NORMAL, 36, 29, 7, 6, 0, 15, 30, 120, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2 },
809 { "MS Sans Serif", FW_NORMAL, 36, 29, 7, 6, 0, 15, 30, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
810 { "MS Sans Serif", FW_NORMAL, 46, 37, 9, 6, 0, 20, 40, 120, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2 },
811 { "MS Sans Serif", FW_NORMAL, 46, 37, 9, 6, 0, 20, 40, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
813 { "MS Serif", FW_NORMAL, 10, 8, 2, 2, 0, 4, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 },
814 { "MS Serif", FW_NORMAL, 10, 8, 2, 2, 0, 5, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
815 { "MS Serif", FW_NORMAL, 11, 9, 2, 2, 0, 5, 9, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
816 { "MS Serif", FW_NORMAL, 13, 11, 2, 2, 0, 5, 11, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 },
817 { "MS Serif", FW_NORMAL, 13, 11, 2, 2, 0, 5, 12, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
818 { "MS Serif", FW_NORMAL, 16, 13, 3, 3, 0, 6, 14, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 },
819 { "MS Serif", FW_NORMAL, 16, 13, 3, 3, 0, 6, 16, 96, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
820 { "MS Serif", FW_NORMAL, 19, 15, 4, 3, 0, 8, 18, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 },
821 { "MS Serif", FW_NORMAL, 19, 15, 4, 3, 0, 8, 19, 96, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
822 { "MS Serif", FW_NORMAL, 21, 16, 5, 3, 0, 9, 17, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 },
823 { "MS Serif", FW_NORMAL, 21, 16, 5, 3, 0, 9, 22, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 },
824 { "MS Serif", FW_NORMAL, 21, 16, 5, 3, 0, 9, 23, 96, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
825 { "MS Serif", FW_NORMAL, 27, 21, 6, 3, 0, 12, 23, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 },
826 { "MS Serif", FW_NORMAL, 27, 21, 6, 3, 0, 12, 26, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 },
827 { "MS Serif", FW_NORMAL, 27, 21, 6, 3, 0, 12, 27, 96, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
828 { "MS Serif", FW_NORMAL, 35, 27, 8, 3, 0, 16, 33, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 },
829 { "MS Serif", FW_NORMAL, 35, 27, 8, 3, 0, 16, 34, 96, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
831 { "MS Serif", FW_NORMAL, 16, 13, 3, 3, 0, 6, 14, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_CYRILLIC },
832 { "MS Serif", FW_NORMAL, 16, 13, 3, 3, 0, 6, 13, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 },
833 { "MS Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 18, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_CYRILLIC },
834 { "MS Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 15, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 },
835 { "MS Serif", FW_NORMAL, 23, 18, 5, 3, 0, 10, 21, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_CYRILLIC },
836 { "MS Serif", FW_NORMAL, 23, 18, 5, 3, 0, 10, 19, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 },
837 { "MS Serif", FW_NORMAL, 27, 21, 6, 4, 0, 12, 23, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 },
838 { "MS Serif", FW_MEDIUM, 27, 22, 5, 2, 0, 12, 30, 120, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
839 { "MS Serif", FW_NORMAL, 33, 26, 7, 3, 0, 14, 30, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 },
840 { "MS Serif", FW_MEDIUM, 32, 25, 7, 2, 0, 14, 32, 120, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
841 { "MS Serif", FW_NORMAL, 43, 34, 9, 3, 0, 19, 39, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
843 { "Courier", FW_NORMAL, 13, 11, 2, 0, 0, 8, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
844 { "Courier", FW_NORMAL, 16, 13, 3, 0, 0, 9, 9, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
845 { "Courier", FW_NORMAL, 20, 16, 4, 0, 0, 12, 12, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
847 { "Courier", FW_NORMAL, 16, 13, 3, 0, 0, 9, 9, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
848 { "Courier", FW_NORMAL, 20, 16, 4, 0, 0, 12, 12, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
849 { "Courier", FW_NORMAL, 25, 20, 5, 0, 0, 15, 15, 120, 0x20, 0xff, 0x40, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
851 { "System", FW_BOLD, 16, 13, 3, 3, 0, 7, 14, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 },
852 { "System", FW_BOLD, 16, 13, 3, 3, 0, 7, 15, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
853 { "System", FW_NORMAL, 18, 16, 2, 0, 2, 8, 16, 96, 0x20, 0xff, 0x80, 0x20, FS_JISJAPAN },
855 { "System", FW_BOLD, 20, 16, 4, 4, 0, 9, 14, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 },
856 { "System", FW_BOLD, 20, 16, 4, 4, 0, 9, 17, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
858 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 1, 2, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 },
859 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 1, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
860 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 2, 4, 96, 0x20, 0xff, 0x80, 0x20, FS_JISJAPAN },
861 { "Small Fonts", FW_NORMAL, 5, 4, 1, 1, 0, 3, 4, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1, LANG_ARABIC },
862 { "Small Fonts", FW_NORMAL, 5, 4, 1, 1, 0, 2, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
863 { "Small Fonts", FW_NORMAL, 5, 4, 1, 0, 0, 3, 6, 96, 0x20, 0xff, 0x80, 0x20, FS_JISJAPAN },
864 { "Small Fonts", FW_NORMAL, 6, 5, 1, 1, 0, 3, 13, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1, LANG_ARABIC },
865 { "Small Fonts", FW_NORMAL, 6, 5, 1, 1, 0, 3, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
866 { "Small Fonts", FW_NORMAL, 6, 5, 1, 1, 0, 3, 8, 96, 0x00, 0xff, 0x60, 0x00, FS_ARABIC },
867 { "Small Fonts", FW_NORMAL, 6, 5, 1, 0, 0, 4, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_JISJAPAN },
868 { "Small Fonts", FW_NORMAL, 8, 7, 1, 1, 0, 4, 7, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1, LANG_ARABIC },
869 { "Small Fonts", FW_NORMAL, 8, 7, 1, 1, 0, 4, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
870 { "Small Fonts", FW_NORMAL, 8, 7, 1, 1, 0, 4, 8, 96, 0x00, 0xff, 0x60, 0x00, FS_ARABIC },
871 { "Small Fonts", FW_NORMAL, 8, 7, 1, 0, 0, 5, 10, 96, 0x20, 0xff, 0x80, 0x20, FS_JISJAPAN },
872 { "Small Fonts", FW_NORMAL, 10, 8, 2, 2, 0, 4, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2, LANG_ARABIC },
873 { "Small Fonts", FW_NORMAL, 10, 8, 2, 2, 0, 5, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
874 { "Small Fonts", FW_NORMAL, 10, 8, 2, 2, 0, 4, 9, 96, 0x00, 0xff, 0x60, 0x00, FS_ARABIC },
875 { "Small Fonts", FW_NORMAL, 10, 8, 2, 0, 0, 6, 12, 96, 0x20, 0xff, 0x80, 0x20, FS_JISJAPAN },
876 { "Small Fonts", FW_NORMAL, 11, 9, 2, 2, 0, 5, 9, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC, LANG_ARABIC },
877 { "Small Fonts", FW_NORMAL, 11, 9, 2, 2, 0, 4, 10, 96, 0x00, 0xff, 0x60, 0x00, FS_ARABIC },
878 { "Small Fonts", FW_NORMAL, 11, 9, 2, 0, 0, 7, 14, 96, 0x20, 0xff, 0x80, 0x20, FS_JISJAPAN },
880 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 1, 2, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_JISJAPAN },
881 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 1, 8, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
882 { "Small Fonts", FW_NORMAL, 6, 5, 1, 1, 0, 3, 5, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_JISJAPAN },
883 { "Small Fonts", FW_NORMAL, 6, 5, 1, 1, 0, 3, 8, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
884 { "Small Fonts", FW_NORMAL, 8, 7, 1, 1, 0, 4, 7, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_JISJAPAN },
885 { "Small Fonts", FW_NORMAL, 8, 7, 1, 1, 0, 4, 8, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
886 { "Small Fonts", FW_NORMAL, 10, 8, 2, 2, 0, 5, 9, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_JISJAPAN },
887 { "Small Fonts", FW_NORMAL, 10, 8, 2, 2, 0, 5, 8, 120, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
888 { "Small Fonts", FW_NORMAL, 12, 10, 2, 2, 0, 5, 10, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_JISJAPAN },
889 { "Small Fonts", FW_NORMAL, 12, 10, 2, 2, 0, 6, 10, 120, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
890 { "Small Fonts", FW_NORMAL, 13, 11, 2, 2, 0, 6, 12, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_JISJAPAN },
891 { "Small Fonts", FW_NORMAL, 13, 11, 2, 2, 0, 6, 11, 120, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
893 { "Fixedsys", FW_NORMAL, 15, 12, 3, 3, 0, 8, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 },
894 { "Fixedsys", FW_NORMAL, 16, 12, 4, 3, 0, 8, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
895 { "FixedSys", FW_NORMAL, 18, 16, 2, 0, 0, 8, 16, 96, 0x20, 0xff, 0xa0, 0x20, FS_JISJAPAN },
897 { "Fixedsys", FW_NORMAL, 20, 16, 4, 2, 0, 10, 10, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC }
899 /* FIXME: add "Terminal" */
901 static const int font_log_pixels[] = { 96, 120 };
902 HDC hdc;
903 LOGFONTA lf;
904 HFONT hfont, old_hfont;
905 TEXTMETRICA tm;
906 INT ret, i, expected_cs, screen_log_pixels, diff, font_res;
907 char face_name[LF_FACESIZE];
908 CHARSETINFO csi;
910 trace("system language id %04x\n", system_lang_id);
912 expected_cs = GetACP();
913 if (!TranslateCharsetInfo(ULongToPtr(expected_cs), &csi, TCI_SRCCODEPAGE))
915 skip("TranslateCharsetInfo failed for code page %d\n", expected_cs);
916 return;
918 expected_cs = csi.ciCharset;
919 trace("ACP %d -> charset %d\n", GetACP(), expected_cs);
921 hdc = CreateCompatibleDC(0);
922 assert(hdc);
924 trace("logpixelsX %d, logpixelsY %d\n", GetDeviceCaps(hdc, LOGPIXELSX),
925 GetDeviceCaps(hdc, LOGPIXELSY));
927 screen_log_pixels = GetDeviceCaps(hdc, LOGPIXELSY);
928 diff = 32768;
929 font_res = 0;
930 for (i = 0; i < sizeof(font_log_pixels)/sizeof(font_log_pixels[0]); i++)
932 int new_diff = abs(font_log_pixels[i] - screen_log_pixels);
933 if (new_diff < diff)
935 diff = new_diff;
936 font_res = font_log_pixels[i];
939 trace("best font resolution is %d\n", font_res);
941 for (i = 0; i < sizeof(fd)/sizeof(fd[0]); i++)
943 int bit, height;
945 memset(&lf, 0, sizeof(lf));
947 height = fd[i].height & ~FH_SCALE;
948 lf.lfHeight = height;
949 strcpy(lf.lfFaceName, fd[i].face_name);
951 for(bit = 0; bit < 32; bit++)
953 GLYPHMETRICS gm;
954 DWORD fs[2];
955 BOOL bRet;
957 fs[0] = 1L << bit;
958 fs[1] = 0;
959 if((fd[i].ansi_bitfield & fs[0]) == 0) continue;
960 if(!TranslateCharsetInfo( fs, &csi, TCI_SRCFONTSIG )) continue;
962 lf.lfCharSet = csi.ciCharset;
963 ret = EnumFontFamiliesExA(hdc, &lf, find_font_proc, (LPARAM)&lf, 0);
964 if (fd[i].height & FH_SCALE)
965 ok(ret, "scaled font height %d should not be enumerated\n", height);
966 else
968 if (font_res == fd[i].dpi && lf.lfCharSet == expected_cs)
970 if (ret) /* FIXME: Remove once Wine is fixed */
971 todo_wine ok(!ret, "%s height %d charset %d dpi %d should be enumerated\n", lf.lfFaceName, lf.lfHeight, lf.lfCharSet, fd[i].dpi);
972 else
973 ok(!ret, "%s height %d charset %d dpi %d should be enumerated\n", lf.lfFaceName, lf.lfHeight, lf.lfCharSet, fd[i].dpi);
976 if (ret && !(fd[i].height & FH_SCALE))
977 continue;
979 hfont = create_font(lf.lfFaceName, &lf);
980 old_hfont = SelectObject(hdc, hfont);
982 SetLastError(0xdeadbeef);
983 ret = GetTextFaceA(hdc, sizeof(face_name), face_name);
984 ok(ret, "GetTextFace error %u\n", GetLastError());
986 if (strcmp(face_name, fd[i].face_name) != 0)
988 ok(ret != ANSI_CHARSET, "font charset should not be ANSI_CHARSET\n");
989 ok(ret != expected_cs, "font charset %d should not be %d\n", ret, expected_cs);
990 SelectObject(hdc, old_hfont);
991 DeleteObject(hfont);
992 continue;
995 memset(&gm, 0, sizeof(gm));
996 SetLastError(0xdeadbeef);
997 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
998 todo_wine {
999 ok(ret == GDI_ERROR, "GetGlyphOutline should fail for a bitmap font\n");
1000 ok(GetLastError() == ERROR_CAN_NOT_COMPLETE, "expected ERROR_CAN_NOT_COMPLETE, got %u\n", GetLastError());
1003 bRet = GetTextMetricsA(hdc, &tm);
1004 ok(bRet, "GetTextMetrics error %d\n", GetLastError());
1006 SetLastError(0xdeadbeef);
1007 ret = GetTextCharset(hdc);
1008 if (is_CJK() && lf.lfCharSet == ANSI_CHARSET)
1009 ok(ret == ANSI_CHARSET, "got charset %d, expected ANSI_CHARSETd\n", ret);
1010 else
1011 ok(ret == expected_cs, "got charset %d, expected %d\n", ret, expected_cs);
1013 trace("created %s, height %d charset %x dpi %d\n", face_name, tm.tmHeight, tm.tmCharSet, tm.tmDigitizedAspectX);
1014 trace("expected %s, height %d scaled_hight %d, dpi %d\n", fd[i].face_name, height, fd[i].scaled_height, fd[i].dpi);
1016 if(fd[i].dpi == tm.tmDigitizedAspectX)
1018 trace("matched %s, height %d charset %x dpi %d\n", lf.lfFaceName, lf.lfHeight, lf.lfCharSet, fd[i].dpi);
1019 if (fd[i].skip_lang_id == 0 || system_lang_id != fd[i].skip_lang_id)
1021 ok(tm.tmWeight == fd[i].weight, "%s(%d): tm.tmWeight %d != %d\n", fd[i].face_name, height, tm.tmWeight, fd[i].weight);
1022 if (fd[i].height & FH_SCALE)
1023 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);
1024 else
1025 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);
1026 ok(tm.tmAscent == fd[i].ascent, "%s(%d): tm.tmAscent %d != %d\n", fd[i].face_name, height, tm.tmAscent, fd[i].ascent);
1027 ok(tm.tmDescent == fd[i].descent, "%s(%d): tm.tmDescent %d != %d\n", fd[i].face_name, height, tm.tmDescent, fd[i].descent);
1028 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);
1029 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);
1030 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);
1031 ok(tm.tmFirstChar == fd[i].first_char, "%s(%d): tm.tmFirstChar = %02x\n", fd[i].face_name, height, tm.tmFirstChar);
1032 ok(tm.tmLastChar == fd[i].last_char, "%s(%d): tm.tmLastChar = %02x\n", fd[i].face_name, height, tm.tmLastChar);
1033 /* Substitutions like MS Sans Serif,0=MS Sans Serif,204
1034 make default char test fail */
1035 if (tm.tmCharSet == lf.lfCharSet)
1036 ok(tm.tmDefaultChar == fd[i].def_char, "%s(%d): tm.tmDefaultChar = %02x\n", fd[i].face_name, height, tm.tmDefaultChar);
1037 ok(tm.tmBreakChar == fd[i].break_char, "%s(%d): tm.tmBreakChar = %02x\n", fd[i].face_name, height, tm.tmBreakChar);
1038 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);
1040 /* Don't run the max char width test on System/ANSI_CHARSET. We have extra characters in our font
1041 that make the max width bigger */
1042 if ((strcmp(lf.lfFaceName, "System") || lf.lfCharSet != ANSI_CHARSET) && tm.tmDigitizedAspectX == 96)
1043 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);
1045 else
1046 skip("Skipping font metrics test for system langid 0x%x\n",
1047 system_lang_id);
1049 SelectObject(hdc, old_hfont);
1050 DeleteObject(hfont);
1054 DeleteDC(hdc);
1057 static void test_GdiGetCharDimensions(void)
1059 HDC hdc;
1060 TEXTMETRICW tm;
1061 LONG ret;
1062 SIZE size;
1063 LONG avgwidth, height;
1064 static const char szAlphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
1066 if (!pGdiGetCharDimensions)
1068 win_skip("GdiGetCharDimensions not available on this platform\n");
1069 return;
1072 hdc = CreateCompatibleDC(NULL);
1074 GetTextExtentPointA(hdc, szAlphabet, strlen(szAlphabet), &size);
1075 avgwidth = ((size.cx / 26) + 1) / 2;
1077 ret = pGdiGetCharDimensions(hdc, &tm, &height);
1078 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
1079 ok(height == tm.tmHeight, "GdiGetCharDimensions should have set height to %d instead of %d\n", tm.tmHeight, height);
1081 ret = pGdiGetCharDimensions(hdc, &tm, NULL);
1082 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
1084 ret = pGdiGetCharDimensions(hdc, NULL, NULL);
1085 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
1087 height = 0;
1088 ret = pGdiGetCharDimensions(hdc, NULL, &height);
1089 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
1090 ok(height == size.cy, "GdiGetCharDimensions should have set height to %d instead of %d\n", size.cy, height);
1092 DeleteDC(hdc);
1095 static int CALLBACK create_font_proc(const LOGFONTA *lpelfe,
1096 const TEXTMETRICA *lpntme,
1097 DWORD FontType, LPARAM lParam)
1099 if (FontType & TRUETYPE_FONTTYPE)
1101 HFONT hfont;
1103 hfont = CreateFontIndirectA(lpelfe);
1104 if (hfont)
1106 *(HFONT *)lParam = hfont;
1107 return 0;
1111 return 1;
1114 static void ABCWidths_helper(const char* description, HDC hdc, WORD *glyphs, ABC *base_abci, ABC *base_abcw, ABCFLOAT *base_abcf, INT todo)
1116 ABC abc[1];
1117 ABCFLOAT abcf[1];
1118 BOOL ret = FALSE;
1120 ret = pGetCharABCWidthsI(hdc, 0, 1, glyphs, abc);
1121 ok(ret, "%s: GetCharABCWidthsI should have succeeded\n", description);
1122 ok ((INT)abc->abcB > 0, "%s: abcB should be positive\n", description);
1123 if (todo) todo_wine ok(abc->abcA * base_abci->abcA >= 0, "%s: abcA's sign should be unchanged\n", description);
1124 else ok(abc->abcA * base_abci->abcA >= 0, "%s: abcA's sign should be unchanged\n", description);
1125 if (todo) todo_wine ok(abc->abcC * base_abci->abcC >= 0, "%s: abcC's sign should be unchanged\n", description);
1126 else ok(abc->abcC * base_abci->abcC >= 0, "%s: abcC's sign should be unchanged\n", description);
1128 ret = pGetCharABCWidthsW(hdc, 'i', 'i', abc);
1129 ok(ret, "%s: GetCharABCWidthsW should have succeeded\n", description);
1130 ok ((INT)abc->abcB > 0, "%s: abcB should be positive\n", description);
1131 if (todo) todo_wine ok(abc->abcA * base_abcw->abcA >= 0, "%s: abcA's sign should be unchanged\n", description);
1132 else ok(abc->abcA * base_abcw->abcA >= 0, "%s: abcA's sign should be unchanged\n", description);
1133 if (todo) todo_wine ok(abc->abcC * base_abcw->abcC >= 0, "%s: abcC's sign should be unchanged\n", description);
1134 else ok(abc->abcC * base_abcw->abcC >= 0, "%s: abcC's should be unchanged\n", description);
1136 ret = pGetCharABCWidthsFloatW(hdc, 'i', 'i', abcf);
1137 ok(ret, "%s: GetCharABCWidthsFloatW should have succeeded\n", description);
1138 ok (abcf->abcfB > 0.0, "%s: abcfB should be positive\n", description);
1139 if (todo) todo_wine ok(abcf->abcfA * base_abcf->abcfA >= 0.0, "%s: abcfA's sign should be unchanged\n", description);
1140 else ok(abcf->abcfA * base_abcf->abcfA >= 0.0, "%s: abcfA's should be unchanged\n", description);
1141 if (todo) todo_wine ok(abcf->abcfC * base_abcf->abcfC >= 0.0, "%s: abcfC's sign should be unchanged\n", description);
1142 else ok(abcf->abcfC * base_abcf->abcfC >= 0.0, "%s: abcfC's sign should be unchanged\n", description);
1145 static void test_GetCharABCWidths(void)
1147 static const WCHAR str[] = {'i',0};
1148 BOOL ret;
1149 HDC hdc;
1150 LOGFONTA lf;
1151 HFONT hfont;
1152 ABC abc[1];
1153 ABC abcw[1];
1154 ABCFLOAT abcf[1];
1155 WORD glyphs[1];
1156 DWORD nb;
1157 HWND hwnd;
1158 static const struct
1160 UINT first;
1161 UINT last;
1162 } range[] =
1164 {0xff, 0xff},
1165 {0x100, 0x100},
1166 {0xff, 0x100},
1167 {0x1ff, 0xff00},
1168 {0xffff, 0xffff},
1169 {0x10000, 0x10000},
1170 {0xffff, 0x10000},
1171 {0xffffff, 0xffffff},
1172 {0x1000000, 0x1000000},
1173 {0xffffff, 0x1000000},
1174 {0xffffffff, 0xffffffff},
1175 {0x00, 0xff}
1177 static const struct
1179 UINT cs;
1180 UINT a;
1181 UINT w;
1182 BOOL r[sizeof range / sizeof range[0]];
1183 } c[] =
1185 {ANSI_CHARSET, 0x30, 0x30,
1186 {TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE}},
1187 {SHIFTJIS_CHARSET, 0x82a0, 0x3042,
1188 {TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE}},
1189 {HANGEUL_CHARSET, 0x8141, 0xac02,
1190 {TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE}},
1191 {GB2312_CHARSET, 0x8141, 0x4e04,
1192 {TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE}},
1193 {CHINESEBIG5_CHARSET, 0xa142, 0x3001,
1194 {TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE}}
1196 UINT i;
1198 if (!pGetCharABCWidthsA || !pGetCharABCWidthsW || !pGetCharABCWidthsFloatW || !pGetCharABCWidthsI)
1200 win_skip("GetCharABCWidthsA/W/I not available on this platform\n");
1201 return;
1204 memset(&lf, 0, sizeof(lf));
1205 strcpy(lf.lfFaceName, "System");
1206 lf.lfHeight = 20;
1208 hfont = CreateFontIndirectA(&lf);
1209 hdc = GetDC(0);
1210 hfont = SelectObject(hdc, hfont);
1212 nb = pGetGlyphIndicesW(hdc, str, 1, glyphs, 0);
1213 ok(nb == 1, "GetGlyphIndicesW should have returned 1\n");
1215 ret = pGetCharABCWidthsI(NULL, 0, 1, glyphs, abc);
1216 ok(!ret, "GetCharABCWidthsI should have failed\n");
1218 ret = pGetCharABCWidthsI(hdc, 0, 1, glyphs, NULL);
1219 ok(!ret, "GetCharABCWidthsI should have failed\n");
1221 ret = pGetCharABCWidthsI(hdc, 0, 1, glyphs, abc);
1222 ok(ret, "GetCharABCWidthsI should have succeeded\n");
1224 ret = pGetCharABCWidthsW(NULL, 'a', 'a', abc);
1225 ok(!ret, "GetCharABCWidthsW should have failed\n");
1227 ret = pGetCharABCWidthsW(hdc, 'a', 'a', NULL);
1228 ok(!ret, "GetCharABCWidthsW should have failed\n");
1230 ret = pGetCharABCWidthsW(hdc, 'a', 'a', abc);
1231 ok(!ret, "GetCharABCWidthsW should have failed\n");
1233 ret = pGetCharABCWidthsFloatW(NULL, 'a', 'a', abcf);
1234 ok(!ret, "GetCharABCWidthsFloatW should have failed\n");
1236 ret = pGetCharABCWidthsFloatW(hdc, 'a', 'a', NULL);
1237 ok(!ret, "GetCharABCWidthsFloatW should have failed\n");
1239 ret = pGetCharABCWidthsFloatW(hdc, 'a', 'a', abcf);
1240 ok(ret, "GetCharABCWidthsFloatW should have succeeded\n");
1242 hfont = SelectObject(hdc, hfont);
1243 DeleteObject(hfont);
1245 for (i = 0; i < sizeof c / sizeof c[0]; ++i)
1247 ABC a[2], w[2];
1248 ABC full[256];
1249 UINT code = 0x41, j;
1251 lf.lfFaceName[0] = '\0';
1252 lf.lfCharSet = c[i].cs;
1253 lf.lfPitchAndFamily = 0;
1254 if (EnumFontFamiliesExA(hdc, &lf, create_font_proc, (LPARAM)&hfont, 0))
1256 skip("TrueType font for charset %u is not installed\n", c[i].cs);
1257 continue;
1260 memset(a, 0, sizeof a);
1261 memset(w, 0, sizeof w);
1262 hfont = SelectObject(hdc, hfont);
1263 ok(pGetCharABCWidthsA(hdc, c[i].a, c[i].a + 1, a) &&
1264 pGetCharABCWidthsW(hdc, c[i].w, c[i].w + 1, w) &&
1265 memcmp(a, w, sizeof a) == 0,
1266 "GetCharABCWidthsA and GetCharABCWidthsW should return same widths. charset = %u\n", c[i].cs);
1268 memset(a, 0xbb, sizeof a);
1269 ret = pGetCharABCWidthsA(hdc, code, code, a);
1270 ok(ret, "GetCharABCWidthsA should have succeeded\n");
1271 memset(full, 0xcc, sizeof full);
1272 ret = pGetCharABCWidthsA(hdc, 0x00, code, full);
1273 ok(ret, "GetCharABCWidthsA should have succeeded\n");
1274 ok(memcmp(&a[0], &full[code], sizeof(ABC)) == 0,
1275 "GetCharABCWidthsA info should match. codepage = %u\n", c[i].cs);
1277 for (j = 0; j < sizeof range / sizeof range[0]; ++j)
1279 memset(full, 0xdd, sizeof full);
1280 ret = pGetCharABCWidthsA(hdc, range[j].first, range[j].last, full);
1281 ok(ret == c[i].r[j], "GetCharABCWidthsA %x - %x should have %s\n",
1282 range[j].first, range[j].last, c[i].r[j] ? "succeeded" : "failed");
1283 if (ret)
1285 UINT last = range[j].last - range[j].first;
1286 ret = pGetCharABCWidthsA(hdc, range[j].last, range[j].last, a);
1287 ok(ret && memcmp(&full[last], &a[0], sizeof(ABC)) == 0,
1288 "GetCharABCWidthsA %x should match. codepage = %u\n",
1289 range[j].last, c[i].cs);
1293 hfont = SelectObject(hdc, hfont);
1294 DeleteObject(hfont);
1297 memset(&lf, 0, sizeof(lf));
1298 strcpy(lf.lfFaceName, "Tahoma");
1299 lf.lfHeight = 200;
1300 hfont = CreateFontIndirectA(&lf);
1302 /* test empty glyph's metrics */
1303 hfont = SelectObject(hdc, hfont);
1304 ret = pGetCharABCWidthsFloatW(hdc, ' ', ' ', abcf);
1305 ok(ret, "GetCharABCWidthsFloatW should have succeeded\n");
1306 ok(abcf[0].abcfB == 1.0, "got %f\n", abcf[0].abcfB);
1307 ret = pGetCharABCWidthsW(hdc, ' ', ' ', abcw);
1308 ok(ret, "GetCharABCWidthsW should have succeeded\n");
1309 ok(abcw[0].abcB == 1, "got %u\n", abcw[0].abcB);
1311 /* 1) prepare unrotated font metrics */
1312 ret = pGetCharABCWidthsW(hdc, 'a', 'a', abcw);
1313 ok(ret, "GetCharABCWidthsW should have succeeded\n");
1314 DeleteObject(SelectObject(hdc, hfont));
1316 /* 2) get rotated font metrics */
1317 lf.lfEscapement = lf.lfOrientation = 900;
1318 hfont = CreateFontIndirectA(&lf);
1319 hfont = SelectObject(hdc, hfont);
1320 ret = pGetCharABCWidthsW(hdc, 'a', 'a', abc);
1321 ok(ret, "GetCharABCWidthsW should have succeeded\n");
1323 /* 3) compare ABC results */
1324 ok(match_off_by_1(abcw[0].abcA, abc[0].abcA, FALSE),
1325 "got %d, expected %d (A)\n", abc[0].abcA, abcw[0].abcA);
1326 ok(match_off_by_1(abcw[0].abcB, abc[0].abcB, FALSE),
1327 "got %d, expected %d (B)\n", abc[0].abcB, abcw[0].abcB);
1328 ok(match_off_by_1(abcw[0].abcC, abc[0].abcC, FALSE),
1329 "got %d, expected %d (C)\n", abc[0].abcC, abcw[0].abcC);
1331 DeleteObject(SelectObject(hdc, hfont));
1332 ReleaseDC(NULL, hdc);
1334 trace("ABC sign test for a variety of transforms:\n");
1335 memset(&lf, 0, sizeof(lf));
1336 strcpy(lf.lfFaceName, "Tahoma");
1337 lf.lfHeight = 20;
1338 hfont = CreateFontIndirectA(&lf);
1339 hwnd = CreateWindowExA(0, "static", "", WS_POPUP, 0,0,100,100,
1340 0, 0, 0, NULL);
1341 hdc = GetDC(hwnd);
1342 SetMapMode(hdc, MM_ANISOTROPIC);
1343 SelectObject(hdc, hfont);
1345 nb = pGetGlyphIndicesW(hdc, str, 1, glyphs, 0);
1346 ok(nb == 1, "GetGlyphIndicesW should have returned 1\n");
1348 ret = pGetCharABCWidthsI(hdc, 0, 1, glyphs, abc);
1349 ok(ret, "GetCharABCWidthsI should have succeeded\n");
1350 ret = pGetCharABCWidthsW(hdc, 'i', 'i', abcw);
1351 ok(ret, "GetCharABCWidthsW should have succeeded\n");
1352 ret = pGetCharABCWidthsFloatW(hdc, 'i', 'i', abcf);
1353 ok(ret, "GetCharABCWidthsFloatW should have succeeded\n");
1355 ABCWidths_helper("LTR", hdc, glyphs, abc, abcw, abcf, 0);
1356 SetWindowExtEx(hdc, -1, -1, NULL);
1357 SetGraphicsMode(hdc, GM_COMPATIBLE);
1358 ABCWidths_helper("LTR -1 compatible", hdc, glyphs, abc, abcw, abcf, 0);
1359 SetGraphicsMode(hdc, GM_ADVANCED);
1360 ABCWidths_helper("LTR -1 advanced", hdc, glyphs, abc, abcw, abcf, 1);
1361 SetWindowExtEx(hdc, 1, 1, NULL);
1362 SetGraphicsMode(hdc, GM_COMPATIBLE);
1363 ABCWidths_helper("LTR 1 compatible", hdc, glyphs, abc, abcw, abcf, 0);
1364 SetGraphicsMode(hdc, GM_ADVANCED);
1365 ABCWidths_helper("LTR 1 advanced", hdc, glyphs, abc, abcw, abcf, 0);
1367 ReleaseDC(hwnd, hdc);
1368 DestroyWindow(hwnd);
1370 trace("RTL layout\n");
1371 hwnd = CreateWindowExA(WS_EX_LAYOUTRTL, "static", "", WS_POPUP, 0,0,100,100,
1372 0, 0, 0, NULL);
1373 hdc = GetDC(hwnd);
1374 SetMapMode(hdc, MM_ANISOTROPIC);
1375 SelectObject(hdc, hfont);
1377 ABCWidths_helper("RTL", hdc, glyphs, abc, abcw, abcf, 0);
1378 SetWindowExtEx(hdc, -1, -1, NULL);
1379 SetGraphicsMode(hdc, GM_COMPATIBLE);
1380 ABCWidths_helper("RTL -1 compatible", hdc, glyphs, abc, abcw, abcf, 0);
1381 SetGraphicsMode(hdc, GM_ADVANCED);
1382 ABCWidths_helper("RTL -1 advanced", hdc, glyphs, abc, abcw, abcf, 0);
1383 SetWindowExtEx(hdc, 1, 1, NULL);
1384 SetGraphicsMode(hdc, GM_COMPATIBLE);
1385 ABCWidths_helper("RTL 1 compatible", hdc, glyphs, abc, abcw, abcf, 0);
1386 SetGraphicsMode(hdc, GM_ADVANCED);
1387 ABCWidths_helper("RTL 1 advanced", hdc, glyphs, abc, abcw, abcf, 1);
1389 ReleaseDC(hwnd, hdc);
1390 DestroyWindow(hwnd);
1391 DeleteObject(hfont);
1394 static void test_text_extents(void)
1396 static const WCHAR wt[] = {'O','n','e','\n','t','w','o',' ','3',0};
1397 LPINT extents;
1398 INT i, len, fit1, fit2, extents2[3];
1399 LOGFONTA lf;
1400 TEXTMETRICA tm;
1401 HDC hdc;
1402 HFONT hfont;
1403 SIZE sz;
1404 SIZE sz1, sz2;
1405 BOOL ret;
1407 memset(&lf, 0, sizeof(lf));
1408 strcpy(lf.lfFaceName, "Arial");
1409 lf.lfHeight = 20;
1411 hfont = CreateFontIndirectA(&lf);
1412 hdc = GetDC(0);
1413 hfont = SelectObject(hdc, hfont);
1414 GetTextMetricsA(hdc, &tm);
1415 GetTextExtentPointA(hdc, "o", 1, &sz);
1416 ok(sz.cy == tm.tmHeight, "cy %d tmHeight %d\n", sz.cy, tm.tmHeight);
1418 SetLastError(0xdeadbeef);
1419 GetTextExtentExPointW(hdc, wt, 1, 1, &fit1, &fit2, &sz1);
1420 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
1422 win_skip("Skipping remainder of text extents test on a Win9x platform\n");
1423 hfont = SelectObject(hdc, hfont);
1424 DeleteObject(hfont);
1425 ReleaseDC(0, hdc);
1426 return;
1429 len = lstrlenW(wt);
1430 extents = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof extents[0]);
1431 extents[0] = 1; /* So that the increasing sequence test will fail
1432 if the extents array is untouched. */
1433 GetTextExtentExPointW(hdc, wt, len, 32767, &fit1, extents, &sz1);
1434 GetTextExtentPointW(hdc, wt, len, &sz2);
1435 ok(sz1.cy == sz2.cy,
1436 "cy from GetTextExtentExPointW (%d) and GetTextExtentPointW (%d) differ\n", sz1.cy, sz2.cy);
1437 /* Because of the '\n' in the string GetTextExtentExPoint and
1438 GetTextExtentPoint return different widths under Win2k, but
1439 under WinXP they return the same width. So we don't test that
1440 here. */
1442 for (i = 1; i < len; ++i)
1443 ok(extents[i-1] <= extents[i],
1444 "GetTextExtentExPointW generated a non-increasing sequence of partial extents (at position %d)\n",
1446 ok(extents[len-1] == sz1.cx, "GetTextExtentExPointW extents and size don't match\n");
1447 ok(0 <= fit1 && fit1 <= len, "GetTextExtentExPointW generated illegal value %d for fit\n", fit1);
1448 ok(0 < fit1, "GetTextExtentExPointW says we can't even fit one letter in 32767 logical units\n");
1449 GetTextExtentExPointW(hdc, wt, len, extents[2], &fit2, NULL, &sz2);
1450 ok(sz1.cx == sz2.cx && sz1.cy == sz2.cy, "GetTextExtentExPointW returned different sizes for the same string\n");
1451 ok(fit2 == 3, "GetTextExtentExPointW extents isn't consistent with fit\n");
1452 GetTextExtentExPointW(hdc, wt, len, extents[2]-1, &fit2, NULL, &sz2);
1453 ok(fit2 == 2, "GetTextExtentExPointW extents isn't consistent with fit\n");
1454 GetTextExtentExPointW(hdc, wt, 2, 0, NULL, extents + 2, &sz2);
1455 ok(extents[0] == extents[2] && extents[1] == extents[3],
1456 "GetTextExtentExPointW with lpnFit == NULL returns incorrect results\n");
1457 GetTextExtentExPointW(hdc, wt, 2, 0, NULL, NULL, &sz1);
1458 ok(sz1.cx == sz2.cx && sz1.cy == sz2.cy,
1459 "GetTextExtentExPointW with lpnFit and alpDx both NULL returns incorrect results\n");
1461 /* extents functions fail with -ve counts (the interesting case being -1) */
1462 ret = GetTextExtentPointA(hdc, "o", -1, &sz);
1463 ok(ret == FALSE, "got %d\n", ret);
1464 ret = GetTextExtentExPointA(hdc, "o", -1, 0, NULL, NULL, &sz);
1465 ok(ret == FALSE, "got %d\n", ret);
1466 ret = GetTextExtentExPointW(hdc, wt, -1, 0, NULL, NULL, &sz1);
1467 ok(ret == FALSE, "got %d\n", ret);
1469 /* max_extent = 0 succeeds and returns zero */
1470 fit1 = fit2 = -215;
1471 ret = GetTextExtentExPointA(hdc, NULL, 0, 0, &fit1, NULL, &sz);
1472 ok(ret == TRUE ||
1473 broken(ret == FALSE), /* NT4, 2k */
1474 "got %d\n", ret);
1475 ok(fit1 == 0 ||
1476 broken(fit1 == -215), /* NT4, 2k */
1477 "fit = %d\n", fit1);
1478 ret = GetTextExtentExPointW(hdc, NULL, 0, 0, &fit2, NULL, &sz1);
1479 ok(ret == TRUE, "got %d\n", ret);
1480 ok(fit2 == 0, "fit = %d\n", fit2);
1482 /* max_extent = -1 is interpreted as a very large width that will
1483 * definitely fit our three characters */
1484 fit1 = fit2 = -215;
1485 ret = GetTextExtentExPointA(hdc, "One", 3, -1, &fit1, NULL, &sz);
1486 ok(ret == TRUE, "got %d\n", ret);
1487 ok(fit1 == 3, "fit = %d\n", fit1);
1488 ret = GetTextExtentExPointW(hdc, wt, 3, -1, &fit2, NULL, &sz);
1489 ok(ret == TRUE, "got %d\n", ret);
1490 ok(fit2 == 3, "fit = %d\n", fit2);
1492 /* max_extent = -2 is interpreted similarly, but the Ansi version
1493 * rejects it while the Unicode one accepts it */
1494 fit1 = fit2 = -215;
1495 ret = GetTextExtentExPointA(hdc, "One", 3, -2, &fit1, NULL, &sz);
1496 ok(ret == FALSE, "got %d\n", ret);
1497 ok(fit1 == -215, "fit = %d\n", fit1);
1498 ret = GetTextExtentExPointW(hdc, wt, 3, -2, &fit2, NULL, &sz);
1499 ok(ret == TRUE, "got %d\n", ret);
1500 ok(fit2 == 3, "fit = %d\n", fit2);
1502 hfont = SelectObject(hdc, hfont);
1503 DeleteObject(hfont);
1505 /* non-MM_TEXT mapping mode */
1506 lf.lfHeight = 2000;
1507 hfont = CreateFontIndirectA(&lf);
1508 hfont = SelectObject(hdc, hfont);
1510 SetMapMode( hdc, MM_HIMETRIC );
1511 ret = GetTextExtentExPointW(hdc, wt, 3, 0, NULL, extents, &sz);
1512 ok(ret, "got %d\n", ret);
1513 ok(sz.cx == extents[2], "got %d vs %d\n", sz.cx, extents[2]);
1515 ret = GetTextExtentExPointW(hdc, wt, 3, extents[1], &fit1, extents2, &sz2);
1516 ok(ret, "got %d\n", ret);
1517 ok(fit1 == 2, "got %d\n", fit1);
1518 ok(sz2.cx == sz.cx, "got %d vs %d\n", sz2.cx, sz.cx);
1519 for(i = 0; i < 2; i++)
1520 ok(extents2[i] == extents[i], "%d: %d, %d\n", i, extents2[i], extents[i]);
1522 hfont = SelectObject(hdc, hfont);
1523 DeleteObject(hfont);
1524 HeapFree(GetProcessHeap(), 0, extents);
1525 ReleaseDC(NULL, hdc);
1528 static void test_GetGlyphIndices(void)
1530 HDC hdc;
1531 HFONT hfont;
1532 DWORD charcount;
1533 LOGFONTA lf;
1534 DWORD flags = 0;
1535 WCHAR testtext[] = {'T','e','s','t',0xffff,0};
1536 WORD glyphs[(sizeof(testtext)/2)-1];
1537 TEXTMETRICA textm;
1538 HFONT hOldFont;
1540 if (!pGetGlyphIndicesW) {
1541 win_skip("GetGlyphIndicesW not available on platform\n");
1542 return;
1545 hdc = GetDC(0);
1547 memset(&lf, 0, sizeof(lf));
1548 strcpy(lf.lfFaceName, "System");
1549 lf.lfHeight = 16;
1550 lf.lfCharSet = ANSI_CHARSET;
1552 hfont = CreateFontIndirectA(&lf);
1553 ok(hfont != 0, "CreateFontIndirectEx failed\n");
1554 ok(GetTextMetricsA(hdc, &textm), "GetTextMetric failed\n");
1555 if (textm.tmCharSet == ANSI_CHARSET)
1557 flags |= GGI_MARK_NONEXISTING_GLYPHS;
1558 charcount = pGetGlyphIndicesW(hdc, testtext, (sizeof(testtext)/2)-1, glyphs, flags);
1559 ok(charcount == 5, "GetGlyphIndicesW count of glyphs should = 5 not %d\n", charcount);
1560 ok((glyphs[4] == 0x001f || glyphs[4] == 0xffff /* Vista */), "GetGlyphIndicesW should have returned a nonexistent char not %04x\n", glyphs[4]);
1561 flags = 0;
1562 charcount = pGetGlyphIndicesW(hdc, testtext, (sizeof(testtext)/2)-1, glyphs, flags);
1563 ok(charcount == 5, "GetGlyphIndicesW count of glyphs should = 5 not %d\n", charcount);
1564 ok(glyphs[4] == textm.tmDefaultChar, "GetGlyphIndicesW should have returned a %04x not %04x\n",
1565 textm.tmDefaultChar, glyphs[4]);
1567 else
1568 /* FIXME: Write tests for non-ANSI charsets. */
1569 skip("GetGlyphIndices System font tests only for ANSI_CHARSET\n");
1571 if(!is_font_installed("Tahoma"))
1573 skip("Tahoma is not installed so skipping this test\n");
1574 return;
1576 memset(&lf, 0, sizeof(lf));
1577 strcpy(lf.lfFaceName, "Tahoma");
1578 lf.lfHeight = 20;
1580 hfont = CreateFontIndirectA(&lf);
1581 hOldFont = SelectObject(hdc, hfont);
1582 ok(GetTextMetricsA(hdc, &textm), "GetTextMetric failed\n");
1583 flags |= GGI_MARK_NONEXISTING_GLYPHS;
1584 charcount = pGetGlyphIndicesW(hdc, testtext, (sizeof(testtext)/2)-1, glyphs, flags);
1585 ok(charcount == 5, "GetGlyphIndicesW count of glyphs should = 5 not %d\n", charcount);
1586 ok(glyphs[4] == 0xffff, "GetGlyphIndicesW should have returned 0xffff char not %04x\n", glyphs[4]);
1587 flags = 0;
1588 testtext[0] = textm.tmDefaultChar;
1589 charcount = pGetGlyphIndicesW(hdc, testtext, (sizeof(testtext)/2)-1, glyphs, flags);
1590 ok(charcount == 5, "GetGlyphIndicesW count of glyphs should = 5 not %d\n", charcount);
1591 ok(glyphs[0] == 0, "GetGlyphIndicesW for tmDefaultChar should be 0 not %04x\n", glyphs[0]);
1592 ok(glyphs[4] == 0, "GetGlyphIndicesW should have returned 0 not %04x\n", glyphs[4]);
1593 DeleteObject(SelectObject(hdc, hOldFont));
1596 static void test_GetKerningPairs(void)
1598 static const struct kerning_data
1600 const char face_name[LF_FACESIZE];
1601 LONG height;
1602 /* some interesting fields from OUTLINETEXTMETRIC */
1603 LONG tmHeight, tmAscent, tmDescent;
1604 UINT otmEMSquare;
1605 INT otmAscent;
1606 INT otmDescent;
1607 UINT otmLineGap;
1608 UINT otmsCapEmHeight;
1609 UINT otmsXHeight;
1610 INT otmMacAscent;
1611 INT otmMacDescent;
1612 UINT otmMacLineGap;
1613 UINT otmusMinimumPPEM;
1614 /* small subset of kerning pairs to test */
1615 DWORD total_kern_pairs;
1616 const KERNINGPAIR kern_pair[26];
1617 } kd[] =
1619 {"Arial", 12, 12, 9, 3,
1620 2048, 7, -2, 1, 5, 2, 8, -2, 0, 9,
1623 {' ','A',-1},{' ','T',0},{' ','Y',0},{'1','1',-1},
1624 {'A',' ',-1},{'A','T',-1},{'A','V',-1},{'A','W',0},
1625 {'A','Y',-1},{'A','v',0},{'A','w',0},{'A','y',0},
1626 {'F',',',-1},{'F','.',-1},{'F','A',-1},{'L',' ',0},
1627 {'L','T',-1},{'L','V',-1},{'L','W',-1},{'L','Y',-1},
1628 {915,912,+1},{915,913,-1},{910,912,+1},{910,913,-1},
1629 {933,970,+1},{933,972,-1}
1632 {"Arial", -34, 39, 32, 7,
1633 2048, 25, -7, 5, 17, 9, 31, -7, 1, 9,
1636 {' ','A',-2},{' ','T',-1},{' ','Y',-1},{'1','1',-3},
1637 {'A',' ',-2},{'A','T',-3},{'A','V',-3},{'A','W',-1},
1638 {'A','Y',-3},{'A','v',-1},{'A','w',-1},{'A','y',-1},
1639 {'F',',',-4},{'F','.',-4},{'F','A',-2},{'L',' ',-1},
1640 {'L','T',-3},{'L','V',-3},{'L','W',-3},{'L','Y',-3},
1641 {915,912,+3},{915,913,-3},{910,912,+3},{910,913,-3},
1642 {933,970,+2},{933,972,-3}
1645 { "Arial", 120, 120, 97, 23,
1646 2048, 79, -23, 16, 54, 27, 98, -23, 4, 9,
1649 {' ','A',-6},{' ','T',-2},{' ','Y',-2},{'1','1',-8},
1650 {'A',' ',-6},{'A','T',-8},{'A','V',-8},{'A','W',-4},
1651 {'A','Y',-8},{'A','v',-2},{'A','w',-2},{'A','y',-2},
1652 {'F',',',-12},{'F','.',-12},{'F','A',-6},{'L',' ',-4},
1653 {'L','T',-8},{'L','V',-8},{'L','W',-8},{'L','Y',-8},
1654 {915,912,+9},{915,913,-10},{910,912,+9},{910,913,-8},
1655 {933,970,+6},{933,972,-10}
1658 #if 0 /* this set fails due to +1/-1 errors (rounding bug?), needs investigation. */
1659 { "Arial", 1024 /* usually 1/2 of EM Square */, 1024, 830, 194,
1660 2048, 668, -193, 137, 459, 229, 830, -194, 30, 9,
1663 {' ','A',-51},{' ','T',-17},{' ','Y',-17},{'1','1',-68},
1664 {'A',' ',-51},{'A','T',-68},{'A','V',-68},{'A','W',-34},
1665 {'A','Y',-68},{'A','v',-17},{'A','w',-17},{'A','y',-17},
1666 {'F',',',-102},{'F','.',-102},{'F','A',-51},{'L',' ',-34},
1667 {'L','T',-68},{'L','V',-68},{'L','W',-68},{'L','Y',-68},
1668 {915,912,+73},{915,913,-84},{910,912,+76},{910,913,-68},
1669 {933,970,+54},{933,972,-83}
1672 #endif
1674 LOGFONTA lf;
1675 HFONT hfont, hfont_old;
1676 KERNINGPAIR *kern_pair;
1677 HDC hdc;
1678 DWORD total_kern_pairs, ret, i, n, matches;
1680 hdc = GetDC(0);
1682 /* GetKerningPairsA maps unicode set of kerning pairs to current code page
1683 * which may render this test unusable, so we're trying to avoid that.
1685 SetLastError(0xdeadbeef);
1686 GetKerningPairsW(hdc, 0, NULL);
1687 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
1689 win_skip("Skipping the GetKerningPairs test on a Win9x platform\n");
1690 ReleaseDC(0, hdc);
1691 return;
1694 for (i = 0; i < sizeof(kd)/sizeof(kd[0]); i++)
1696 OUTLINETEXTMETRICW otm;
1697 UINT uiRet;
1699 if (!is_font_installed(kd[i].face_name))
1701 trace("%s is not installed so skipping this test\n", kd[i].face_name);
1702 continue;
1705 trace("testing font %s, height %d\n", kd[i].face_name, kd[i].height);
1707 memset(&lf, 0, sizeof(lf));
1708 strcpy(lf.lfFaceName, kd[i].face_name);
1709 lf.lfHeight = kd[i].height;
1710 hfont = CreateFontIndirectA(&lf);
1711 assert(hfont != 0);
1713 hfont_old = SelectObject(hdc, hfont);
1715 SetLastError(0xdeadbeef);
1716 otm.otmSize = sizeof(otm); /* just in case for Win9x compatibility */
1717 uiRet = GetOutlineTextMetricsW(hdc, sizeof(otm), &otm);
1718 ok(uiRet == sizeof(otm), "GetOutlineTextMetricsW error %d\n", GetLastError());
1720 ok(match_off_by_1(kd[i].tmHeight, otm.otmTextMetrics.tmHeight, FALSE), "expected %d, got %d\n",
1721 kd[i].tmHeight, otm.otmTextMetrics.tmHeight);
1722 ok(match_off_by_1(kd[i].tmAscent, otm.otmTextMetrics.tmAscent, FALSE), "expected %d, got %d\n",
1723 kd[i].tmAscent, otm.otmTextMetrics.tmAscent);
1724 ok(kd[i].tmDescent == otm.otmTextMetrics.tmDescent, "expected %d, got %d\n",
1725 kd[i].tmDescent, otm.otmTextMetrics.tmDescent);
1727 ok(kd[i].otmEMSquare == otm.otmEMSquare, "expected %u, got %u\n",
1728 kd[i].otmEMSquare, otm.otmEMSquare);
1729 ok(kd[i].otmAscent == otm.otmAscent, "expected %d, got %d\n",
1730 kd[i].otmAscent, otm.otmAscent);
1731 ok(kd[i].otmDescent == otm.otmDescent, "expected %d, got %d\n",
1732 kd[i].otmDescent, otm.otmDescent);
1733 ok(kd[i].otmLineGap == otm.otmLineGap, "expected %u, got %u\n",
1734 kd[i].otmLineGap, otm.otmLineGap);
1735 ok(near_match(kd[i].otmMacDescent, otm.otmMacDescent), "expected %d, got %d\n",
1736 kd[i].otmMacDescent, otm.otmMacDescent);
1737 ok(near_match(kd[i].otmMacAscent, otm.otmMacAscent), "expected %d, got %d\n",
1738 kd[i].otmMacAscent, otm.otmMacAscent);
1739 todo_wine {
1740 ok(kd[i].otmsCapEmHeight == otm.otmsCapEmHeight, "expected %u, got %u\n",
1741 kd[i].otmsCapEmHeight, otm.otmsCapEmHeight);
1742 ok(kd[i].otmsXHeight == otm.otmsXHeight, "expected %u, got %u\n",
1743 kd[i].otmsXHeight, otm.otmsXHeight);
1744 /* FIXME: this one sometimes succeeds due to expected 0, enable it when removing todo */
1745 if (0) ok(kd[i].otmMacLineGap == otm.otmMacLineGap, "expected %u, got %u\n",
1746 kd[i].otmMacLineGap, otm.otmMacLineGap);
1747 ok(kd[i].otmusMinimumPPEM == otm.otmusMinimumPPEM, "expected %u, got %u\n",
1748 kd[i].otmusMinimumPPEM, otm.otmusMinimumPPEM);
1751 total_kern_pairs = GetKerningPairsW(hdc, 0, NULL);
1752 trace("total_kern_pairs %u\n", total_kern_pairs);
1753 kern_pair = HeapAlloc(GetProcessHeap(), 0, total_kern_pairs * sizeof(*kern_pair));
1755 /* Win98 (GetKerningPairsA) and XP behave differently here, the test
1756 * passes on XP.
1758 SetLastError(0xdeadbeef);
1759 ret = GetKerningPairsW(hdc, 0, kern_pair);
1760 ok(GetLastError() == ERROR_INVALID_PARAMETER,
1761 "got error %u, expected ERROR_INVALID_PARAMETER\n", GetLastError());
1762 ok(ret == 0, "got %u, expected 0\n", ret);
1764 ret = GetKerningPairsW(hdc, 100, NULL);
1765 ok(ret == total_kern_pairs, "got %u, expected %u\n", ret, total_kern_pairs);
1767 ret = GetKerningPairsW(hdc, total_kern_pairs/2, kern_pair);
1768 ok(ret == total_kern_pairs/2, "got %u, expected %u\n", ret, total_kern_pairs/2);
1770 ret = GetKerningPairsW(hdc, total_kern_pairs, kern_pair);
1771 ok(ret == total_kern_pairs, "got %u, expected %u\n", ret, total_kern_pairs);
1773 matches = 0;
1775 for (n = 0; n < ret; n++)
1777 DWORD j;
1778 /* Disabled to limit console spam */
1779 if (0 && kern_pair[n].wFirst < 127 && kern_pair[n].wSecond < 127)
1780 trace("{'%c','%c',%d},\n",
1781 kern_pair[n].wFirst, kern_pair[n].wSecond, kern_pair[n].iKernAmount);
1782 for (j = 0; j < kd[i].total_kern_pairs; j++)
1784 if (kern_pair[n].wFirst == kd[i].kern_pair[j].wFirst &&
1785 kern_pair[n].wSecond == kd[i].kern_pair[j].wSecond)
1787 ok(kern_pair[n].iKernAmount == kd[i].kern_pair[j].iKernAmount,
1788 "pair %d:%d got %d, expected %d\n",
1789 kern_pair[n].wFirst, kern_pair[n].wSecond,
1790 kern_pair[n].iKernAmount, kd[i].kern_pair[j].iKernAmount);
1791 matches++;
1796 ok(matches == kd[i].total_kern_pairs, "got matches %u, expected %u\n",
1797 matches, kd[i].total_kern_pairs);
1799 HeapFree(GetProcessHeap(), 0, kern_pair);
1801 SelectObject(hdc, hfont_old);
1802 DeleteObject(hfont);
1805 ReleaseDC(0, hdc);
1808 struct font_data
1810 const char face_name[LF_FACESIZE];
1811 int requested_height;
1812 int weight, height, ascent, descent, int_leading, ext_leading, dpi;
1813 BOOL exact;
1816 static void test_height( HDC hdc, const struct font_data *fd )
1818 LOGFONTA lf;
1819 HFONT hfont, old_hfont;
1820 TEXTMETRICA tm;
1821 INT ret, i;
1823 for (i = 0; fd[i].face_name[0]; i++)
1825 if (!is_truetype_font_installed(fd[i].face_name))
1827 skip("%s is not installed\n", fd[i].face_name);
1828 continue;
1831 memset(&lf, 0, sizeof(lf));
1832 lf.lfHeight = fd[i].requested_height;
1833 lf.lfWeight = fd[i].weight;
1834 strcpy(lf.lfFaceName, fd[i].face_name);
1836 hfont = CreateFontIndirectA(&lf);
1837 assert(hfont);
1839 old_hfont = SelectObject(hdc, hfont);
1840 ret = GetTextMetricsA(hdc, &tm);
1841 ok(ret, "GetTextMetrics error %d\n", GetLastError());
1842 if(fd[i].dpi == tm.tmDigitizedAspectX)
1844 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);
1845 ok(match_off_by_1(tm.tmHeight, fd[i].height, fd[i].exact), "%s(%d): tm.tmHeight %d != %d\n", fd[i].face_name, fd[i].requested_height, tm.tmHeight, fd[i].height);
1846 ok(match_off_by_1(tm.tmAscent, fd[i].ascent, fd[i].exact), "%s(%d): tm.tmAscent %d != %d\n", fd[i].face_name, fd[i].requested_height, tm.tmAscent, fd[i].ascent);
1847 ok(match_off_by_1(tm.tmDescent, fd[i].descent, fd[i].exact), "%s(%d): tm.tmDescent %d != %d\n", fd[i].face_name, fd[i].requested_height, tm.tmDescent, fd[i].descent);
1848 ok(match_off_by_1(tm.tmInternalLeading, fd[i].int_leading, fd[i].exact), "%s(%d): tm.tmInternalLeading %d != %d\n", fd[i].face_name, fd[i].requested_height, tm.tmInternalLeading, fd[i].int_leading);
1849 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);
1852 SelectObject(hdc, old_hfont);
1853 DeleteObject(hfont);
1857 static void *find_ttf_table( void *ttf, DWORD size, DWORD tag )
1859 WORD i, num_tables = GET_BE_WORD(*((WORD *)ttf + 2));
1860 DWORD *table = (DWORD *)ttf + 3;
1862 for (i = 0; i < num_tables; i++)
1864 if (table[0] == tag)
1865 return (BYTE *)ttf + GET_BE_DWORD(table[2]);
1866 table += 4;
1868 return NULL;
1871 static void test_height_selection_vdmx( HDC hdc )
1873 static const struct font_data charset_0[] = /* doesn't use VDMX */
1875 { "wine_vdmx", 10, FW_NORMAL, 10, 8, 2, 2, 0, 96, TRUE },
1876 { "wine_vdmx", 11, FW_NORMAL, 11, 9, 2, 2, 0, 96, TRUE },
1877 { "wine_vdmx", 12, FW_NORMAL, 12, 10, 2, 2, 0, 96, TRUE },
1878 { "wine_vdmx", 13, FW_NORMAL, 13, 11, 2, 2, 0, 96, TRUE },
1879 { "wine_vdmx", 14, FW_NORMAL, 14, 12, 2, 2, 0, 96, TRUE },
1880 { "wine_vdmx", 15, FW_NORMAL, 15, 12, 3, 3, 0, 96, FALSE },
1881 { "wine_vdmx", 16, FW_NORMAL, 16, 13, 3, 3, 0, 96, TRUE },
1882 { "wine_vdmx", 17, FW_NORMAL, 17, 14, 3, 3, 0, 96, TRUE },
1883 { "wine_vdmx", 18, FW_NORMAL, 18, 15, 3, 3, 0, 96, TRUE },
1884 { "wine_vdmx", 19, FW_NORMAL, 19, 16, 3, 3, 0, 96, TRUE },
1885 { "wine_vdmx", 20, FW_NORMAL, 20, 17, 3, 4, 0, 96, FALSE },
1886 { "wine_vdmx", 21, FW_NORMAL, 21, 17, 4, 4, 0, 96, TRUE },
1887 { "wine_vdmx", 22, FW_NORMAL, 22, 18, 4, 4, 0, 96, TRUE },
1888 { "wine_vdmx", 23, FW_NORMAL, 23, 19, 4, 4, 0, 96, TRUE },
1889 { "wine_vdmx", 24, FW_NORMAL, 24, 20, 4, 4, 0, 96, TRUE },
1890 { "wine_vdmx", 25, FW_NORMAL, 25, 21, 4, 4, 0, 96, TRUE },
1891 { "wine_vdmx", 26, FW_NORMAL, 26, 22, 4, 5, 0, 96, FALSE },
1892 { "wine_vdmx", 27, FW_NORMAL, 27, 22, 5, 5, 0, 96, TRUE },
1893 { "wine_vdmx", 28, FW_NORMAL, 28, 23, 5, 5, 0, 96, TRUE },
1894 { "wine_vdmx", 29, FW_NORMAL, 29, 24, 5, 5, 0, 96, TRUE },
1895 { "wine_vdmx", 30, FW_NORMAL, 30, 25, 5, 5, 0, 96, TRUE },
1896 { "wine_vdmx", 31, FW_NORMAL, 31, 26, 5, 5, 0, 96, TRUE },
1897 { "wine_vdmx", 32, FW_NORMAL, 32, 27, 5, 6, 0, 96, FALSE },
1898 { "wine_vdmx", 48, FW_NORMAL, 48, 40, 8, 8, 0, 96, TRUE },
1899 { "wine_vdmx", 64, FW_NORMAL, 64, 53, 11, 11, 0, 96, TRUE },
1900 { "wine_vdmx", 96, FW_NORMAL, 96, 80, 16, 17, 0, 96, FALSE },
1901 { "wine_vdmx", -10, FW_NORMAL, 12, 10, 2, 2, 0, 96, TRUE },
1902 { "wine_vdmx", -11, FW_NORMAL, 13, 11, 2, 2, 0, 96, TRUE },
1903 { "wine_vdmx", -12, FW_NORMAL, 14, 12, 2, 2, 0, 96, TRUE },
1904 { "wine_vdmx", -13, FW_NORMAL, 16, 13, 3, 3, 0, 96, TRUE },
1905 { "wine_vdmx", -14, FW_NORMAL, 17, 14, 3, 3, 0, 96, TRUE },
1906 { "wine_vdmx", -15, FW_NORMAL, 18, 15, 3, 3, 0, 96, TRUE },
1907 { "wine_vdmx", -16, FW_NORMAL, 19, 16, 3, 3, 0, 96, TRUE },
1908 { "wine_vdmx", -17, FW_NORMAL, 21, 17, 4, 4, 0, 96, TRUE },
1909 { "wine_vdmx", -18, FW_NORMAL, 22, 18, 4, 4, 0, 96, TRUE },
1910 { "wine_vdmx", -19, FW_NORMAL, 23, 19, 4, 4, 0, 96, TRUE },
1911 { "wine_vdmx", -20, FW_NORMAL, 24, 20, 4, 4, 0, 96, TRUE },
1912 { "wine_vdmx", -21, FW_NORMAL, 25, 21, 4, 4, 0, 96, TRUE },
1913 { "wine_vdmx", -22, FW_NORMAL, 27, 22, 5, 5, 0, 96, TRUE },
1914 { "wine_vdmx", -23, FW_NORMAL, 28, 23, 5, 5, 0, 96, TRUE },
1915 { "wine_vdmx", -24, FW_NORMAL, 29, 24, 5, 5, 0, 96, TRUE },
1916 { "wine_vdmx", -25, FW_NORMAL, 30, 25, 5, 5, 0, 96, TRUE },
1917 { "wine_vdmx", -26, FW_NORMAL, 31, 26, 5, 5, 0, 96, TRUE },
1918 { "wine_vdmx", -27, FW_NORMAL, 33, 27, 6, 6, 0, 96, TRUE },
1919 { "wine_vdmx", -28, FW_NORMAL, 34, 28, 6, 6, 0, 96, TRUE },
1920 { "wine_vdmx", -29, FW_NORMAL, 35, 29, 6, 6, 0, 96, TRUE },
1921 { "wine_vdmx", -30, FW_NORMAL, 36, 30, 6, 6, 0, 96, TRUE },
1922 { "wine_vdmx", -31, FW_NORMAL, 37, 31, 6, 6, 0, 96, TRUE },
1923 { "wine_vdmx", -32, FW_NORMAL, 39, 32, 7, 7, 0, 96, TRUE },
1924 { "wine_vdmx", -48, FW_NORMAL, 58, 48, 10, 10, 0, 96, TRUE },
1925 { "wine_vdmx", -64, FW_NORMAL, 77, 64, 13, 13, 0, 96, TRUE },
1926 { "wine_vdmx", -96, FW_NORMAL, 116, 96, 20, 20, 0, 96, TRUE },
1927 { "", 0, 0, 0, 0, 0, 0, 0, 0, 0 }
1930 static const struct font_data charset_1[] = /* Uses VDMX */
1932 { "wine_vdmx", 10, FW_NORMAL, 10, 8, 2, 2, 0, 96, TRUE },
1933 { "wine_vdmx", 11, FW_NORMAL, 11, 9, 2, 2, 0, 96, TRUE },
1934 { "wine_vdmx", 12, FW_NORMAL, 12, 10, 2, 2, 0, 96, TRUE },
1935 { "wine_vdmx", 13, FW_NORMAL, 13, 11, 2, 2, 0, 96, TRUE },
1936 { "wine_vdmx", 14, FW_NORMAL, 13, 11, 2, 2, 0, 96, TRUE },
1937 { "wine_vdmx", 15, FW_NORMAL, 13, 11, 2, 2, 0, 96, TRUE },
1938 { "wine_vdmx", 16, FW_NORMAL, 16, 13, 3, 4, 0, 96, TRUE },
1939 { "wine_vdmx", 17, FW_NORMAL, 16, 13, 3, 3, 0, 96, TRUE },
1940 { "wine_vdmx", 18, FW_NORMAL, 16, 13, 3, 3, 0, 96, TRUE },
1941 { "wine_vdmx", 19, FW_NORMAL, 19, 15, 4, 5, 0, 96, TRUE },
1942 { "wine_vdmx", 20, FW_NORMAL, 20, 16, 4, 5, 0, 96, TRUE },
1943 { "wine_vdmx", 21, FW_NORMAL, 21, 17, 4, 5, 0, 96, TRUE },
1944 { "wine_vdmx", 22, FW_NORMAL, 22, 18, 4, 5, 0, 96, TRUE },
1945 { "wine_vdmx", 23, FW_NORMAL, 23, 19, 4, 5, 0, 96, TRUE },
1946 { "wine_vdmx", 24, FW_NORMAL, 23, 19, 4, 5, 0, 96, TRUE },
1947 { "wine_vdmx", 25, FW_NORMAL, 25, 21, 4, 6, 0, 96, TRUE },
1948 { "wine_vdmx", 26, FW_NORMAL, 26, 22, 4, 6, 0, 96, TRUE },
1949 { "wine_vdmx", 27, FW_NORMAL, 27, 23, 4, 6, 0, 96, TRUE },
1950 { "wine_vdmx", 28, FW_NORMAL, 27, 23, 4, 5, 0, 96, TRUE },
1951 { "wine_vdmx", 29, FW_NORMAL, 29, 24, 5, 6, 0, 96, TRUE },
1952 { "wine_vdmx", 30, FW_NORMAL, 29, 24, 5, 6, 0, 96, TRUE },
1953 { "wine_vdmx", 31, FW_NORMAL, 29, 24, 5, 6, 0, 96, TRUE },
1954 { "wine_vdmx", 32, FW_NORMAL, 32, 26, 6, 8, 0, 96, TRUE },
1955 { "wine_vdmx", 48, FW_NORMAL, 48, 40, 8, 10, 0, 96, TRUE },
1956 { "wine_vdmx", 64, FW_NORMAL, 64, 54, 10, 13, 0, 96, TRUE },
1957 { "wine_vdmx", 96, FW_NORMAL, 95, 79, 16, 18, 0, 96, TRUE },
1958 { "wine_vdmx", -10, FW_NORMAL, 12, 10, 2, 2, 0, 96, TRUE },
1959 { "wine_vdmx", -11, FW_NORMAL, 13, 11, 2, 2, 0, 96, TRUE },
1960 { "wine_vdmx", -12, FW_NORMAL, 16, 13, 3, 4, 0, 96, TRUE },
1961 { "wine_vdmx", -13, FW_NORMAL, 16, 13, 3, 3, 0, 96, TRUE },
1962 { "wine_vdmx", -14, FW_NORMAL, 19, 15, 4, 5, 0, 96, TRUE },
1963 { "wine_vdmx", -15, FW_NORMAL, 20, 16, 4, 5, 0, 96, TRUE },
1964 { "wine_vdmx", -16, FW_NORMAL, 21, 17, 4, 5, 0, 96, TRUE },
1965 { "wine_vdmx", -17, FW_NORMAL, 22, 18, 4, 5, 0, 96, TRUE },
1966 { "wine_vdmx", -18, FW_NORMAL, 23, 19, 4, 5, 0, 96, TRUE },
1967 { "wine_vdmx", -19, FW_NORMAL, 25, 21, 4, 6, 0, 96, TRUE },
1968 { "wine_vdmx", -20, FW_NORMAL, 26, 22, 4, 6, 0, 96, TRUE },
1969 { "wine_vdmx", -21, FW_NORMAL, 27, 23, 4, 6, 0, 96, TRUE },
1970 { "wine_vdmx", -22, FW_NORMAL, 27, 23, 4, 5, 0, 96, TRUE },
1971 { "wine_vdmx", -23, FW_NORMAL, 29, 24, 5, 6, 0, 96, TRUE },
1972 { "wine_vdmx", -24, FW_NORMAL, 32, 26, 6, 8, 0, 96, TRUE },
1973 { "wine_vdmx", -25, FW_NORMAL, 32, 26, 6, 7, 0, 96, TRUE },
1974 { "wine_vdmx", -26, FW_NORMAL, 33, 27, 6, 7, 0, 96, TRUE },
1975 { "wine_vdmx", -27, FW_NORMAL, 35, 29, 6, 8, 0, 96, TRUE },
1976 { "wine_vdmx", -28, FW_NORMAL, 36, 30, 6, 8, 0, 96, TRUE },
1977 { "wine_vdmx", -29, FW_NORMAL, 36, 30, 6, 7, 0, 96, TRUE },
1978 { "wine_vdmx", -30, FW_NORMAL, 38, 32, 6, 8, 0, 96, TRUE },
1979 { "wine_vdmx", -31, FW_NORMAL, 39, 33, 6, 8, 0, 96, TRUE },
1980 { "wine_vdmx", -32, FW_NORMAL, 40, 33, 7, 8, 0, 96, TRUE },
1981 { "wine_vdmx", -48, FW_NORMAL, 60, 50, 10, 12, 0, 96, TRUE },
1982 { "wine_vdmx", -64, FW_NORMAL, 81, 67, 14, 17, 0, 96, TRUE },
1983 { "wine_vdmx", -96, FW_NORMAL, 119, 99, 20, 23, 0, 96, TRUE },
1984 { "", 0, 0, 0, 0, 0, 0, 0, 0, 0 }
1987 static const struct vdmx_data
1989 WORD version;
1990 BYTE bCharSet;
1991 const struct font_data *fd;
1992 } data[] =
1994 { 0, 0, charset_0 },
1995 { 0, 1, charset_1 },
1996 { 1, 0, charset_0 },
1997 { 1, 1, charset_1 }
1999 int i;
2000 DWORD size, num;
2001 WORD *vdmx_header;
2002 BYTE *ratio_rec;
2003 char ttf_name[MAX_PATH];
2004 void *res, *copy;
2006 if (!pAddFontResourceExA)
2008 win_skip("AddFontResourceExA unavailable\n");
2009 return;
2012 for (i = 0; i < sizeof(data) / sizeof(data[0]); i++)
2014 res = get_res_data( "wine_vdmx.ttf", &size );
2016 copy = HeapAlloc( GetProcessHeap(), 0, size );
2017 memcpy( copy, res, size );
2018 vdmx_header = find_ttf_table( copy, size, MS_MAKE_TAG('V','D','M','X') );
2019 vdmx_header[0] = GET_BE_WORD( data[i].version );
2020 ok( GET_BE_WORD( vdmx_header[1] ) == 1, "got %04x\n", GET_BE_WORD( vdmx_header[1] ) );
2021 ok( GET_BE_WORD( vdmx_header[2] ) == 1, "got %04x\n", GET_BE_WORD( vdmx_header[2] ) );
2022 ratio_rec = (BYTE *)&vdmx_header[3];
2023 ratio_rec[0] = data[i].bCharSet;
2025 write_tmp_file( copy, &size, ttf_name );
2026 HeapFree( GetProcessHeap(), 0, copy );
2028 ok( !is_truetype_font_installed("wine_vdmx"), "Already installed\n" );
2029 num = pAddFontResourceExA( ttf_name, FR_PRIVATE, 0 );
2030 if (!num) win_skip("Unable to add ttf font resource\n");
2031 else
2033 ok( is_truetype_font_installed("wine_vdmx"), "Not installed\n" );
2034 test_height( hdc, data[i].fd );
2035 pRemoveFontResourceExA( ttf_name, FR_PRIVATE, 0 );
2037 DeleteFileA( ttf_name );
2041 static void test_height_selection(void)
2043 static const struct font_data tahoma[] =
2045 {"Tahoma", -12, FW_NORMAL, 14, 12, 2, 2, 0, 96, TRUE },
2046 {"Tahoma", -24, FW_NORMAL, 29, 24, 5, 5, 0, 96, TRUE },
2047 {"Tahoma", -48, FW_NORMAL, 58, 48, 10, 10, 0, 96, TRUE },
2048 {"Tahoma", -96, FW_NORMAL, 116, 96, 20, 20, 0, 96, TRUE },
2049 {"Tahoma", -192, FW_NORMAL, 232, 192, 40, 40, 0, 96, TRUE },
2050 {"Tahoma", 12, FW_NORMAL, 12, 10, 2, 2, 0, 96, TRUE },
2051 {"Tahoma", 24, FW_NORMAL, 24, 20, 4, 4, 0, 96, TRUE },
2052 {"Tahoma", 48, FW_NORMAL, 48, 40, 8, 8, 0, 96, TRUE },
2053 {"Tahoma", 96, FW_NORMAL, 96, 80, 16, 17, 0, 96, FALSE },
2054 {"Tahoma", 192, FW_NORMAL, 192, 159, 33, 33, 0, 96, TRUE },
2055 {"", 0, 0, 0, 0, 0, 0, 0, 0, 0 }
2057 HDC hdc = CreateCompatibleDC(0);
2058 assert(hdc);
2060 test_height( hdc, tahoma );
2061 test_height_selection_vdmx( hdc );
2063 DeleteDC(hdc);
2066 static void test_GetOutlineTextMetrics(void)
2068 OUTLINETEXTMETRICA *otm;
2069 LOGFONTA lf;
2070 HFONT hfont, hfont_old;
2071 HDC hdc;
2072 DWORD ret, otm_size;
2073 LPSTR unset_ptr;
2075 if (!is_font_installed("Arial"))
2077 skip("Arial is not installed\n");
2078 return;
2081 hdc = GetDC(0);
2083 memset(&lf, 0, sizeof(lf));
2084 strcpy(lf.lfFaceName, "Arial");
2085 lf.lfHeight = -13;
2086 lf.lfWeight = FW_NORMAL;
2087 lf.lfPitchAndFamily = DEFAULT_PITCH;
2088 lf.lfQuality = PROOF_QUALITY;
2089 hfont = CreateFontIndirectA(&lf);
2090 assert(hfont != 0);
2092 hfont_old = SelectObject(hdc, hfont);
2093 otm_size = GetOutlineTextMetricsA(hdc, 0, NULL);
2094 trace("otm buffer size %u (0x%x)\n", otm_size, otm_size);
2096 otm = HeapAlloc(GetProcessHeap(), 0, otm_size);
2098 memset(otm, 0xAA, otm_size);
2099 SetLastError(0xdeadbeef);
2100 otm->otmSize = sizeof(*otm); /* just in case for Win9x compatibility */
2101 ret = GetOutlineTextMetricsA(hdc, otm->otmSize, otm);
2102 ok(ret == 1 /* Win9x */ ||
2103 ret == otm->otmSize /* XP*/,
2104 "expected %u, got %u, error %d\n", otm->otmSize, ret, GetLastError());
2105 if (ret != 1) /* Win9x doesn't care about pointing beyond of the buffer */
2107 ok(otm->otmpFamilyName == NULL, "expected NULL got %p\n", otm->otmpFamilyName);
2108 ok(otm->otmpFaceName == NULL, "expected NULL got %p\n", otm->otmpFaceName);
2109 ok(otm->otmpStyleName == NULL, "expected NULL got %p\n", otm->otmpStyleName);
2110 ok(otm->otmpFullName == NULL, "expected NULL got %p\n", otm->otmpFullName);
2113 memset(otm, 0xAA, otm_size);
2114 SetLastError(0xdeadbeef);
2115 otm->otmSize = otm_size; /* just in case for Win9x compatibility */
2116 ret = GetOutlineTextMetricsA(hdc, otm->otmSize, otm);
2117 ok(ret == 1 /* Win9x */ ||
2118 ret == otm->otmSize /* XP*/,
2119 "expected %u, got %u, error %d\n", otm->otmSize, ret, GetLastError());
2120 if (ret != 1) /* Win9x doesn't care about pointing beyond of the buffer */
2122 ok(otm->otmpFamilyName != NULL, "expected not NULL got %p\n", otm->otmpFamilyName);
2123 ok(otm->otmpFaceName != NULL, "expected not NULL got %p\n", otm->otmpFaceName);
2124 ok(otm->otmpStyleName != NULL, "expected not NULL got %p\n", otm->otmpStyleName);
2125 ok(otm->otmpFullName != NULL, "expected not NULL got %p\n", otm->otmpFullName);
2128 /* ask about truncated data */
2129 memset(otm, 0xAA, otm_size);
2130 memset(&unset_ptr, 0xAA, sizeof(unset_ptr));
2131 SetLastError(0xdeadbeef);
2132 otm->otmSize = sizeof(*otm) - sizeof(LPSTR); /* just in case for Win9x compatibility */
2133 ret = GetOutlineTextMetricsA(hdc, otm->otmSize, otm);
2134 ok(ret == 1 /* Win9x */ ||
2135 ret == otm->otmSize /* XP*/,
2136 "expected %u, got %u, error %d\n", otm->otmSize, ret, GetLastError());
2137 if (ret != 1) /* Win9x doesn't care about pointing beyond of the buffer */
2139 ok(otm->otmpFamilyName == NULL, "expected NULL got %p\n", otm->otmpFamilyName);
2140 ok(otm->otmpFaceName == NULL, "expected NULL got %p\n", otm->otmpFaceName);
2141 ok(otm->otmpStyleName == NULL, "expected NULL got %p\n", otm->otmpStyleName);
2143 ok(otm->otmpFullName == unset_ptr, "expected %p got %p\n", unset_ptr, otm->otmpFullName);
2145 HeapFree(GetProcessHeap(), 0, otm);
2147 SelectObject(hdc, hfont_old);
2148 DeleteObject(hfont);
2150 ReleaseDC(0, hdc);
2153 static void testJustification(HDC hdc, PCSTR str, RECT *clientArea)
2155 INT y,
2156 breakCount,
2157 areaWidth = clientArea->right - clientArea->left,
2158 nErrors = 0, e;
2159 const char *pFirstChar, *pLastChar;
2160 SIZE size;
2161 TEXTMETRICA tm;
2162 struct err
2164 const char *start;
2165 int len;
2166 int GetTextExtentExPointWWidth;
2167 } error[20];
2169 GetTextMetricsA(hdc, &tm);
2170 y = clientArea->top;
2171 do {
2172 breakCount = 0;
2173 while (*str == tm.tmBreakChar) str++; /* skip leading break chars */
2174 pFirstChar = str;
2176 do {
2177 pLastChar = str;
2179 /* if not at the end of the string, ... */
2180 if (*str == '\0') break;
2181 /* ... add the next word to the current extent */
2182 while (*str != '\0' && *str++ != tm.tmBreakChar);
2183 breakCount++;
2184 SetTextJustification(hdc, 0, 0);
2185 GetTextExtentPoint32A(hdc, pFirstChar, str - pFirstChar - 1, &size);
2186 } while ((int) size.cx < areaWidth);
2188 /* ignore trailing break chars */
2189 breakCount--;
2190 while (*(pLastChar - 1) == tm.tmBreakChar)
2192 pLastChar--;
2193 breakCount--;
2196 if (*str == '\0' || breakCount <= 0) pLastChar = str;
2198 SetTextJustification(hdc, 0, 0);
2199 GetTextExtentPoint32A(hdc, pFirstChar, pLastChar - pFirstChar, &size);
2201 /* do not justify the last extent */
2202 if (*str != '\0' && breakCount > 0)
2204 SetTextJustification(hdc, areaWidth - size.cx, breakCount);
2205 GetTextExtentPoint32A(hdc, pFirstChar, pLastChar - pFirstChar, &size);
2206 if (size.cx != areaWidth && nErrors < sizeof(error)/sizeof(error[0]) - 1)
2208 error[nErrors].start = pFirstChar;
2209 error[nErrors].len = pLastChar - pFirstChar;
2210 error[nErrors].GetTextExtentExPointWWidth = size.cx;
2211 nErrors++;
2215 y += size.cy;
2216 str = pLastChar;
2217 } while (*str && y < clientArea->bottom);
2219 for (e = 0; e < nErrors; e++)
2221 /* The width returned by GetTextExtentPoint32() is exactly the same
2222 returned by GetTextExtentExPointW() - see dlls/gdi32/font.c */
2223 ok(error[e].GetTextExtentExPointWWidth == areaWidth,
2224 "GetTextExtentPointW() for \"%.*s\" should have returned a width of %d, not %d.\n",
2225 error[e].len, error[e].start, areaWidth, error[e].GetTextExtentExPointWWidth);
2229 static void test_SetTextJustification(void)
2231 HDC hdc;
2232 RECT clientArea;
2233 LOGFONTA lf;
2234 HFONT hfont;
2235 HWND hwnd;
2236 SIZE size, expect;
2237 int i;
2238 WORD indices[2];
2239 static const char testText[] =
2240 "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do "
2241 "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut "
2242 "enim ad minim veniam, quis nostrud exercitation ullamco laboris "
2243 "nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in "
2244 "reprehenderit in voluptate velit esse cillum dolore eu fugiat "
2245 "nulla pariatur. Excepteur sint occaecat cupidatat non proident, "
2246 "sunt in culpa qui officia deserunt mollit anim id est laborum.";
2248 hwnd = CreateWindowExA(0, "static", "", WS_POPUP, 0,0, 400,400, 0, 0, 0, NULL);
2249 GetClientRect( hwnd, &clientArea );
2250 hdc = GetDC( hwnd );
2252 if (!is_font_installed("Times New Roman"))
2254 skip("Times New Roman is not installed\n");
2255 return;
2258 memset(&lf, 0, sizeof lf);
2259 lf.lfCharSet = ANSI_CHARSET;
2260 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
2261 lf.lfWeight = FW_DONTCARE;
2262 lf.lfHeight = 20;
2263 lf.lfQuality = DEFAULT_QUALITY;
2264 lstrcpyA(lf.lfFaceName, "Times New Roman");
2265 hfont = create_font("Times New Roman", &lf);
2266 SelectObject(hdc, hfont);
2268 testJustification(hdc, testText, &clientArea);
2270 if (!pGetGlyphIndicesA || !pGetTextExtentExPointI) goto done;
2271 pGetGlyphIndicesA( hdc, "A ", 2, indices, 0 );
2273 SetTextJustification(hdc, 0, 0);
2274 GetTextExtentPoint32A(hdc, " ", 1, &expect);
2275 GetTextExtentPoint32A(hdc, " ", 3, &size);
2276 ok( size.cx == 3 * expect.cx, "wrong size %d/%d\n", size.cx, expect.cx );
2277 SetTextJustification(hdc, 4, 1);
2278 GetTextExtentPoint32A(hdc, " ", 1, &size);
2279 ok( size.cx == expect.cx + 4, "wrong size %d/%d\n", size.cx, expect.cx );
2280 SetTextJustification(hdc, 9, 2);
2281 GetTextExtentPoint32A(hdc, " ", 2, &size);
2282 ok( size.cx == 2 * expect.cx + 9, "wrong size %d/%d\n", size.cx, expect.cx );
2283 SetTextJustification(hdc, 7, 3);
2284 GetTextExtentPoint32A(hdc, " ", 3, &size);
2285 ok( size.cx == 3 * expect.cx + 7, "wrong size %d/%d\n", size.cx, expect.cx );
2286 SetTextJustification(hdc, 7, 3);
2287 SetTextCharacterExtra(hdc, 2 );
2288 GetTextExtentPoint32A(hdc, " ", 3, &size);
2289 ok( size.cx == 3 * (expect.cx + 2) + 7, "wrong size %d/%d\n", size.cx, expect.cx );
2290 SetTextJustification(hdc, 0, 0);
2291 SetTextCharacterExtra(hdc, 0);
2292 size.cx = size.cy = 1234;
2293 GetTextExtentPoint32A(hdc, " ", 0, &size);
2294 ok( size.cx == 0 && size.cy == 0, "wrong size %d,%d\n", size.cx, size.cy );
2295 pGetTextExtentExPointI(hdc, indices, 2, -1, NULL, NULL, &expect);
2296 SetTextJustification(hdc, 5, 1);
2297 pGetTextExtentExPointI(hdc, indices, 2, -1, NULL, NULL, &size);
2298 ok( size.cx == expect.cx + 5, "wrong size %d/%d\n", size.cx, expect.cx );
2299 SetTextJustification(hdc, 0, 0);
2301 SetMapMode( hdc, MM_ANISOTROPIC );
2302 SetWindowExtEx( hdc, 2, 2, NULL );
2303 GetClientRect( hwnd, &clientArea );
2304 DPtoLP( hdc, (POINT *)&clientArea, 2 );
2305 testJustification(hdc, testText, &clientArea);
2307 GetTextExtentPoint32A(hdc, "A", 1, &expect);
2308 for (i = 0; i < 10; i++)
2310 SetTextCharacterExtra(hdc, i);
2311 GetTextExtentPoint32A(hdc, "A", 1, &size);
2312 ok( size.cx == expect.cx + i, "wrong size %d/%d+%d\n", size.cx, expect.cx, i );
2314 SetTextCharacterExtra(hdc, 0);
2315 pGetTextExtentExPointI(hdc, indices, 1, -1, NULL, NULL, &expect);
2316 for (i = 0; i < 10; i++)
2318 SetTextCharacterExtra(hdc, i);
2319 pGetTextExtentExPointI(hdc, indices, 1, -1, NULL, NULL, &size);
2320 ok( size.cx == expect.cx + i, "wrong size %d/%d+%d\n", size.cx, expect.cx, i );
2322 SetTextCharacterExtra(hdc, 0);
2324 SetViewportExtEx( hdc, 3, 3, NULL );
2325 GetClientRect( hwnd, &clientArea );
2326 DPtoLP( hdc, (POINT *)&clientArea, 2 );
2327 testJustification(hdc, testText, &clientArea);
2329 GetTextExtentPoint32A(hdc, "A", 1, &expect);
2330 for (i = 0; i < 10; i++)
2332 SetTextCharacterExtra(hdc, i);
2333 GetTextExtentPoint32A(hdc, "A", 1, &size);
2334 ok( size.cx == expect.cx + i, "wrong size %d/%d+%d\n", size.cx, expect.cx, i );
2337 done:
2338 DeleteObject(hfont);
2339 ReleaseDC(hwnd, hdc);
2340 DestroyWindow(hwnd);
2343 static BOOL get_glyph_indices(INT charset, UINT code_page, WORD *idx, UINT count, BOOL unicode)
2345 HDC hdc;
2346 LOGFONTA lf;
2347 HFONT hfont, hfont_old;
2348 CHARSETINFO csi;
2349 FONTSIGNATURE fs;
2350 INT cs;
2351 DWORD i, ret;
2352 char name[64];
2354 assert(count <= 128);
2356 memset(&lf, 0, sizeof(lf));
2358 lf.lfCharSet = charset;
2359 lf.lfHeight = 10;
2360 lstrcpyA(lf.lfFaceName, "Arial");
2361 SetLastError(0xdeadbeef);
2362 hfont = CreateFontIndirectA(&lf);
2363 ok(hfont != 0, "CreateFontIndirectA error %u\n", GetLastError());
2365 hdc = GetDC(0);
2366 hfont_old = SelectObject(hdc, hfont);
2368 cs = GetTextCharsetInfo(hdc, &fs, 0);
2369 ok(cs == charset, "expected %d, got %d\n", charset, cs);
2371 SetLastError(0xdeadbeef);
2372 ret = GetTextFaceA(hdc, sizeof(name), name);
2373 ok(ret, "GetTextFaceA error %u\n", GetLastError());
2375 if (charset == SYMBOL_CHARSET)
2377 ok(strcmp("Arial", name), "face name should NOT be Arial\n");
2378 ok(fs.fsCsb[0] & (1 << 31), "symbol encoding should be available\n");
2380 else
2382 ok(!strcmp("Arial", name), "face name should be Arial, not %s\n", name);
2383 ok(!(fs.fsCsb[0] & (1 << 31)), "symbol encoding should NOT be available\n");
2386 if (!TranslateCharsetInfo((DWORD *)(INT_PTR)cs, &csi, TCI_SRCCHARSET))
2388 trace("Can't find codepage for charset %d\n", cs);
2389 ReleaseDC(0, hdc);
2390 return FALSE;
2392 ok(csi.ciACP == code_page, "expected %d, got %d\n", code_page, csi.ciACP);
2394 if (pGdiGetCodePage != NULL && pGdiGetCodePage(hdc) != code_page)
2396 skip("Font code page %d, looking for code page %d\n",
2397 pGdiGetCodePage(hdc), code_page);
2398 ReleaseDC(0, hdc);
2399 return FALSE;
2402 if (unicode)
2404 char ansi_buf[128];
2405 WCHAR unicode_buf[128];
2407 for (i = 0; i < count; i++) ansi_buf[i] = (BYTE)(i + 128);
2409 MultiByteToWideChar(code_page, 0, ansi_buf, count, unicode_buf, count);
2411 SetLastError(0xdeadbeef);
2412 ret = pGetGlyphIndicesW(hdc, unicode_buf, count, idx, 0);
2413 ok(ret == count, "GetGlyphIndicesW expected %d got %d, error %u\n",
2414 count, ret, GetLastError());
2416 else
2418 char ansi_buf[128];
2420 for (i = 0; i < count; i++) ansi_buf[i] = (BYTE)(i + 128);
2422 SetLastError(0xdeadbeef);
2423 ret = pGetGlyphIndicesA(hdc, ansi_buf, count, idx, 0);
2424 ok(ret == count, "GetGlyphIndicesA expected %d got %d, error %u\n",
2425 count, ret, GetLastError());
2428 SelectObject(hdc, hfont_old);
2429 DeleteObject(hfont);
2431 ReleaseDC(0, hdc);
2433 return TRUE;
2436 static void test_font_charset(void)
2438 static struct charset_data
2440 INT charset;
2441 UINT code_page;
2442 WORD font_idxA[128], font_idxW[128];
2443 } cd[] =
2445 { ANSI_CHARSET, 1252 },
2446 { RUSSIAN_CHARSET, 1251 },
2447 { SYMBOL_CHARSET, CP_SYMBOL } /* keep it as the last one */
2449 int i;
2451 if (!pGetGlyphIndicesA || !pGetGlyphIndicesW)
2453 win_skip("Skipping the font charset test on a Win9x platform\n");
2454 return;
2457 if (!is_font_installed("Arial"))
2459 skip("Arial is not installed\n");
2460 return;
2463 for (i = 0; i < sizeof(cd)/sizeof(cd[0]); i++)
2465 if (cd[i].charset == SYMBOL_CHARSET)
2467 if (!is_font_installed("Symbol") && !is_font_installed("Wingdings"))
2469 skip("Symbol or Wingdings is not installed\n");
2470 break;
2473 if (get_glyph_indices(cd[i].charset, cd[i].code_page, cd[i].font_idxA, 128, FALSE) &&
2474 get_glyph_indices(cd[i].charset, cd[i].code_page, cd[i].font_idxW, 128, TRUE))
2475 ok(!memcmp(cd[i].font_idxA, cd[i].font_idxW, 128*sizeof(WORD)), "%d: indices don't match\n", i);
2478 ok(memcmp(cd[0].font_idxW, cd[1].font_idxW, 128*sizeof(WORD)), "0 vs 1: indices shouldn't match\n");
2479 if (i > 2)
2481 ok(memcmp(cd[0].font_idxW, cd[2].font_idxW, 128*sizeof(WORD)), "0 vs 2: indices shouldn't match\n");
2482 ok(memcmp(cd[1].font_idxW, cd[2].font_idxW, 128*sizeof(WORD)), "1 vs 2: indices shouldn't match\n");
2484 else
2485 skip("Symbol or Wingdings is not installed\n");
2488 static void test_GdiGetCodePage(void)
2490 static const struct _matching_data
2492 UINT current_codepage;
2493 LPCSTR lfFaceName;
2494 UCHAR lfCharSet;
2495 UINT expected_codepage;
2496 } matching_data[] = {
2497 {1251, "Arial", ANSI_CHARSET, 1252},
2498 {1251, "Tahoma", ANSI_CHARSET, 1252},
2500 {1252, "Arial", ANSI_CHARSET, 1252},
2501 {1252, "Tahoma", ANSI_CHARSET, 1252},
2503 {1253, "Arial", ANSI_CHARSET, 1252},
2504 {1253, "Tahoma", ANSI_CHARSET, 1252},
2506 { 932, "Arial", ANSI_CHARSET, 1252}, /* Japanese Windows returns 1252, not 932 */
2507 { 932, "Tahoma", ANSI_CHARSET, 1252},
2508 { 932, "MS UI Gothic", ANSI_CHARSET, 1252},
2510 { 936, "Arial", ANSI_CHARSET, 936},
2511 { 936, "Tahoma", ANSI_CHARSET, 936},
2512 { 936, "Simsun", ANSI_CHARSET, 936},
2514 { 949, "Arial", ANSI_CHARSET, 949},
2515 { 949, "Tahoma", ANSI_CHARSET, 949},
2516 { 949, "Gulim", ANSI_CHARSET, 949},
2518 { 950, "Arial", ANSI_CHARSET, 950},
2519 { 950, "Tahoma", ANSI_CHARSET, 950},
2520 { 950, "PMingLiU", ANSI_CHARSET, 950},
2522 HDC hdc;
2523 LOGFONTA lf;
2524 HFONT hfont;
2525 UINT charset, acp;
2526 DWORD codepage;
2527 int i;
2529 if (!pGdiGetCodePage)
2531 skip("GdiGetCodePage not available on this platform\n");
2532 return;
2535 acp = GetACP();
2537 for (i = 0; i < sizeof(matching_data) / sizeof(struct _matching_data); i++)
2539 /* only test data matched current locale codepage */
2540 if (matching_data[i].current_codepage != acp)
2541 continue;
2543 if (!is_font_installed(matching_data[i].lfFaceName))
2545 skip("%s is not installed\n", matching_data[i].lfFaceName);
2546 continue;
2549 hdc = GetDC(0);
2551 memset(&lf, 0, sizeof(lf));
2552 lf.lfHeight = -16;
2553 lf.lfCharSet = matching_data[i].lfCharSet;
2554 lstrcpyA(lf.lfFaceName, matching_data[i].lfFaceName);
2555 hfont = CreateFontIndirectA(&lf);
2556 ok(hfont != 0, "CreateFontIndirectA error %u\n", GetLastError());
2558 hfont = SelectObject(hdc, hfont);
2559 charset = GetTextCharset(hdc);
2560 codepage = pGdiGetCodePage(hdc);
2561 trace("acp=%d, lfFaceName=%s, lfCharSet=%d, GetTextCharset=%d, GdiGetCodePage=%d, expected codepage=%d\n",
2562 acp, lf.lfFaceName, lf.lfCharSet, charset, codepage, matching_data[i].expected_codepage);
2563 ok(codepage == matching_data[i].expected_codepage,
2564 "GdiGetCodePage should have returned %d, got %d\n", matching_data[i].expected_codepage, codepage);
2566 hfont = SelectObject(hdc, hfont);
2567 DeleteObject(hfont);
2569 /* CLIP_DFA_DISABLE turns off the font association */
2570 lf.lfClipPrecision = CLIP_DFA_DISABLE;
2571 hfont = CreateFontIndirectA(&lf);
2572 ok(hfont != 0, "CreateFontIndirectA error %u\n", GetLastError());
2574 hfont = SelectObject(hdc, hfont);
2575 charset = GetTextCharset(hdc);
2576 codepage = pGdiGetCodePage(hdc);
2577 trace("acp=%d, lfFaceName=%s, lfCharSet=%d, GetTextCharset=%d, GdiGetCodePage=%d\n",
2578 acp, lf.lfFaceName, lf.lfCharSet, charset, codepage);
2579 ok(codepage == 1252, "GdiGetCodePage returned %d\n", codepage);
2581 hfont = SelectObject(hdc, hfont);
2582 DeleteObject(hfont);
2584 ReleaseDC(NULL, hdc);
2588 static void test_GetFontUnicodeRanges(void)
2590 LOGFONTA lf;
2591 HDC hdc;
2592 HFONT hfont, hfont_old;
2593 DWORD size;
2594 GLYPHSET *gs;
2595 DWORD i;
2597 if (!pGetFontUnicodeRanges)
2599 win_skip("GetFontUnicodeRanges not available before W2K\n");
2600 return;
2603 memset(&lf, 0, sizeof(lf));
2604 lstrcpyA(lf.lfFaceName, "Arial");
2605 hfont = create_font("Arial", &lf);
2607 hdc = GetDC(0);
2608 hfont_old = SelectObject(hdc, hfont);
2610 size = pGetFontUnicodeRanges(NULL, NULL);
2611 ok(!size, "GetFontUnicodeRanges succeeded unexpectedly\n");
2613 size = pGetFontUnicodeRanges(hdc, NULL);
2614 ok(size, "GetFontUnicodeRanges failed unexpectedly\n");
2616 gs = HeapAlloc(GetProcessHeap(), 0, size);
2618 size = pGetFontUnicodeRanges(hdc, gs);
2619 ok(size, "GetFontUnicodeRanges failed\n");
2621 if (0) /* Disabled to limit console spam */
2622 for (i = 0; i < gs->cRanges; i++)
2623 trace("%03d wcLow %04x cGlyphs %u\n", i, gs->ranges[i].wcLow, gs->ranges[i].cGlyphs);
2624 trace("found %u ranges\n", gs->cRanges);
2626 HeapFree(GetProcessHeap(), 0, gs);
2628 SelectObject(hdc, hfont_old);
2629 DeleteObject(hfont);
2630 ReleaseDC(NULL, hdc);
2633 #define MAX_ENUM_FONTS 4096
2635 struct enum_font_data
2637 int total;
2638 LOGFONTA lf[MAX_ENUM_FONTS];
2641 struct enum_fullname_data
2643 int total;
2644 ENUMLOGFONTA elf[MAX_ENUM_FONTS];
2647 struct enum_font_dataW
2649 int total;
2650 LOGFONTW lf[MAX_ENUM_FONTS];
2653 static INT CALLBACK arial_enum_proc(const LOGFONTA *lf, const TEXTMETRICA *tm, DWORD type, LPARAM lParam)
2655 struct enum_font_data *efd = (struct enum_font_data *)lParam;
2656 const NEWTEXTMETRICA *ntm = (const NEWTEXTMETRICA *)tm;
2658 ok(lf->lfHeight == tm->tmHeight, "lfHeight %d != tmHeight %d\n", lf->lfHeight, tm->tmHeight);
2659 ok(lf->lfHeight > 0 && lf->lfHeight < 200, "enumerated font height %d\n", lf->lfHeight);
2661 if (type != TRUETYPE_FONTTYPE) return 1;
2663 ok(ntm->ntmCellHeight + ntm->ntmCellHeight/5 >= ntm->ntmSizeEM, "ntmCellHeight %d should be close to ntmSizeEM %d\n", ntm->ntmCellHeight, ntm->ntmSizeEM);
2665 if (0) /* Disabled to limit console spam */
2666 trace("enumed font \"%s\", charset %d, height %d, weight %d, italic %d\n",
2667 lf->lfFaceName, lf->lfCharSet, lf->lfHeight, lf->lfWeight, lf->lfItalic);
2668 if (efd->total < MAX_ENUM_FONTS)
2669 efd->lf[efd->total++] = *lf;
2670 else
2671 trace("enum tests invalid; you have more than %d fonts\n", MAX_ENUM_FONTS);
2673 return 1;
2676 static INT CALLBACK arial_enum_procw(const LOGFONTW *lf, const TEXTMETRICW *tm, DWORD type, LPARAM lParam)
2678 struct enum_font_dataW *efd = (struct enum_font_dataW *)lParam;
2679 const NEWTEXTMETRICW *ntm = (const NEWTEXTMETRICW *)tm;
2681 ok(lf->lfHeight == tm->tmHeight, "lfHeight %d != tmHeight %d\n", lf->lfHeight, tm->tmHeight);
2682 ok(lf->lfHeight > 0 && lf->lfHeight < 200, "enumerated font height %d\n", lf->lfHeight);
2684 if (type != TRUETYPE_FONTTYPE) return 1;
2686 ok(ntm->ntmCellHeight + ntm->ntmCellHeight/5 >= ntm->ntmSizeEM, "ntmCellHeight %d should be close to ntmSizeEM %d\n", ntm->ntmCellHeight, ntm->ntmSizeEM);
2688 if (0) /* Disabled to limit console spam */
2689 trace("enumed font %s, charset %d, height %d, weight %d, italic %d\n",
2690 wine_dbgstr_w(lf->lfFaceName), lf->lfCharSet, lf->lfHeight, lf->lfWeight, lf->lfItalic);
2691 if (efd->total < MAX_ENUM_FONTS)
2692 efd->lf[efd->total++] = *lf;
2693 else
2694 trace("enum tests invalid; you have more than %d fonts\n", MAX_ENUM_FONTS);
2696 return 1;
2699 static void get_charset_stats(struct enum_font_data *efd,
2700 int *ansi_charset, int *symbol_charset,
2701 int *russian_charset)
2703 int i;
2705 *ansi_charset = 0;
2706 *symbol_charset = 0;
2707 *russian_charset = 0;
2709 for (i = 0; i < efd->total; i++)
2711 switch (efd->lf[i].lfCharSet)
2713 case ANSI_CHARSET:
2714 (*ansi_charset)++;
2715 break;
2716 case SYMBOL_CHARSET:
2717 (*symbol_charset)++;
2718 break;
2719 case RUSSIAN_CHARSET:
2720 (*russian_charset)++;
2721 break;
2726 static void get_charset_statsW(struct enum_font_dataW *efd,
2727 int *ansi_charset, int *symbol_charset,
2728 int *russian_charset)
2730 int i;
2732 *ansi_charset = 0;
2733 *symbol_charset = 0;
2734 *russian_charset = 0;
2736 for (i = 0; i < efd->total; i++)
2738 switch (efd->lf[i].lfCharSet)
2740 case ANSI_CHARSET:
2741 (*ansi_charset)++;
2742 break;
2743 case SYMBOL_CHARSET:
2744 (*symbol_charset)++;
2745 break;
2746 case RUSSIAN_CHARSET:
2747 (*russian_charset)++;
2748 break;
2753 static void test_EnumFontFamilies(const char *font_name, INT font_charset)
2755 struct enum_font_data efd;
2756 struct enum_font_dataW efdw;
2757 LOGFONTA lf;
2758 HDC hdc;
2759 int i, ret, ansi_charset, symbol_charset, russian_charset;
2761 trace("Testing font %s, charset %d\n", *font_name ? font_name : "<empty>", font_charset);
2763 if (*font_name && !is_truetype_font_installed(font_name))
2765 skip("%s is not installed\n", font_name);
2766 return;
2769 hdc = GetDC(0);
2771 /* Observed behaviour: EnumFontFamilies enumerates aliases like "Arial Cyr"
2772 * while EnumFontFamiliesEx doesn't.
2774 if (!*font_name && font_charset == DEFAULT_CHARSET) /* do it only once */
2777 * Use EnumFontFamiliesW since win98 crashes when the
2778 * second parameter is NULL using EnumFontFamilies
2780 efdw.total = 0;
2781 SetLastError(0xdeadbeef);
2782 ret = EnumFontFamiliesW(hdc, NULL, arial_enum_procw, (LPARAM)&efdw);
2783 ok(ret || GetLastError() == ERROR_CALL_NOT_IMPLEMENTED, "EnumFontFamiliesW error %u\n", GetLastError());
2784 if(ret)
2786 get_charset_statsW(&efdw, &ansi_charset, &symbol_charset, &russian_charset);
2787 trace("enumerated ansi %d, symbol %d, russian %d fonts for NULL\n",
2788 ansi_charset, symbol_charset, russian_charset);
2789 ok(efdw.total > 0, "fonts enumerated: NULL\n");
2790 ok(ansi_charset > 0, "NULL family should enumerate ANSI_CHARSET\n");
2791 ok(symbol_charset > 0, "NULL family should enumerate SYMBOL_CHARSET\n");
2792 ok(russian_charset > 0 ||
2793 broken(russian_charset == 0), /* NT4 */
2794 "NULL family should enumerate RUSSIAN_CHARSET\n");
2797 efdw.total = 0;
2798 SetLastError(0xdeadbeef);
2799 ret = EnumFontFamiliesExW(hdc, NULL, arial_enum_procw, (LPARAM)&efdw, 0);
2800 ok(ret || GetLastError() == ERROR_CALL_NOT_IMPLEMENTED, "EnumFontFamiliesExW error %u\n", GetLastError());
2801 if(ret)
2803 get_charset_statsW(&efdw, &ansi_charset, &symbol_charset, &russian_charset);
2804 trace("enumerated ansi %d, symbol %d, russian %d fonts for NULL\n",
2805 ansi_charset, symbol_charset, russian_charset);
2806 ok(efdw.total > 0, "fonts enumerated: NULL\n");
2807 ok(ansi_charset > 0, "NULL family should enumerate ANSI_CHARSET\n");
2808 ok(symbol_charset > 0, "NULL family should enumerate SYMBOL_CHARSET\n");
2809 ok(russian_charset > 0, "NULL family should enumerate RUSSIAN_CHARSET\n");
2813 efd.total = 0;
2814 SetLastError(0xdeadbeef);
2815 ret = EnumFontFamiliesA(hdc, font_name, arial_enum_proc, (LPARAM)&efd);
2816 ok(ret, "EnumFontFamilies error %u\n", GetLastError());
2817 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
2818 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s\n",
2819 ansi_charset, symbol_charset, russian_charset,
2820 *font_name ? font_name : "<empty>");
2821 if (*font_name)
2822 ok(efd.total > 0, "no fonts enumerated: %s\n", font_name);
2823 else
2824 ok(!efd.total, "no fonts should be enumerated for empty font_name\n");
2825 for (i = 0; i < efd.total; i++)
2827 /* FIXME: remove completely once Wine is fixed */
2828 if (efd.lf[i].lfCharSet != font_charset)
2830 todo_wine
2831 ok(efd.lf[i].lfCharSet == font_charset, "%d: got charset %d\n", i, efd.lf[i].lfCharSet);
2833 else
2834 ok(efd.lf[i].lfCharSet == font_charset, "%d: got charset %d\n", i, efd.lf[i].lfCharSet);
2835 ok(!strcmp(efd.lf[i].lfFaceName, font_name), "expected %s, got %s\n",
2836 font_name, efd.lf[i].lfFaceName);
2839 memset(&lf, 0, sizeof(lf));
2840 lf.lfCharSet = ANSI_CHARSET;
2841 strcpy(lf.lfFaceName, font_name);
2842 efd.total = 0;
2843 SetLastError(0xdeadbeef);
2844 ret = EnumFontFamiliesExA(hdc, &lf, arial_enum_proc, (LPARAM)&efd, 0);
2845 ok(ret, "EnumFontFamiliesEx error %u\n", GetLastError());
2846 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
2847 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s ANSI_CHARSET\n",
2848 ansi_charset, symbol_charset, russian_charset,
2849 *font_name ? font_name : "<empty>");
2850 if (font_charset == SYMBOL_CHARSET)
2852 if (*font_name)
2853 ok(efd.total == 0, "no fonts should be enumerated: %s ANSI_CHARSET\n", font_name);
2854 else
2855 ok(efd.total > 0, "no fonts enumerated: %s\n", font_name);
2857 else
2859 ok(efd.total > 0, "no fonts enumerated: %s ANSI_CHARSET\n", font_name);
2860 for (i = 0; i < efd.total; i++)
2862 ok(efd.lf[i].lfCharSet == ANSI_CHARSET, "%d: got charset %d\n", i, efd.lf[i].lfCharSet);
2863 if (*font_name)
2864 ok(!strcmp(efd.lf[i].lfFaceName, font_name), "expected %s, got %s\n",
2865 font_name, efd.lf[i].lfFaceName);
2869 /* DEFAULT_CHARSET should enumerate all available charsets */
2870 memset(&lf, 0, sizeof(lf));
2871 lf.lfCharSet = DEFAULT_CHARSET;
2872 strcpy(lf.lfFaceName, font_name);
2873 efd.total = 0;
2874 SetLastError(0xdeadbeef);
2875 EnumFontFamiliesExA(hdc, &lf, arial_enum_proc, (LPARAM)&efd, 0);
2876 ok(ret, "EnumFontFamiliesEx error %u\n", GetLastError());
2877 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
2878 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s DEFAULT_CHARSET\n",
2879 ansi_charset, symbol_charset, russian_charset,
2880 *font_name ? font_name : "<empty>");
2881 ok(efd.total > 0, "no fonts enumerated: %s DEFAULT_CHARSET\n", font_name);
2882 for (i = 0; i < efd.total; i++)
2884 if (*font_name)
2885 ok(!strcmp(efd.lf[i].lfFaceName, font_name), "expected %s, got %s\n",
2886 font_name, efd.lf[i].lfFaceName);
2888 if (*font_name)
2890 switch (font_charset)
2892 case ANSI_CHARSET:
2893 ok(ansi_charset > 0,
2894 "ANSI_CHARSET should enumerate ANSI_CHARSET for %s\n", font_name);
2895 ok(!symbol_charset,
2896 "ANSI_CHARSET should NOT enumerate SYMBOL_CHARSET for %s\n", font_name);
2897 ok(russian_charset > 0,
2898 "ANSI_CHARSET should enumerate RUSSIAN_CHARSET for %s\n", font_name);
2899 break;
2900 case SYMBOL_CHARSET:
2901 ok(!ansi_charset,
2902 "SYMBOL_CHARSET should NOT enumerate ANSI_CHARSET for %s\n", font_name);
2903 ok(symbol_charset,
2904 "SYMBOL_CHARSET should enumerate SYMBOL_CHARSET for %s\n", font_name);
2905 ok(!russian_charset,
2906 "SYMBOL_CHARSET should NOT enumerate RUSSIAN_CHARSET for %s\n", font_name);
2907 break;
2908 case DEFAULT_CHARSET:
2909 ok(ansi_charset > 0,
2910 "DEFAULT_CHARSET should enumerate ANSI_CHARSET for %s\n", font_name);
2911 ok(symbol_charset > 0,
2912 "DEFAULT_CHARSET should enumerate SYMBOL_CHARSET for %s\n", font_name);
2913 ok(russian_charset > 0,
2914 "DEFAULT_CHARSET should enumerate RUSSIAN_CHARSET for %s\n", font_name);
2915 break;
2918 else
2920 ok(ansi_charset > 0,
2921 "DEFAULT_CHARSET should enumerate ANSI_CHARSET for %s\n", *font_name ? font_name : "<empty>");
2922 ok(symbol_charset > 0,
2923 "DEFAULT_CHARSET should enumerate SYMBOL_CHARSET for %s\n", *font_name ? font_name : "<empty>");
2924 ok(russian_charset > 0,
2925 "DEFAULT_CHARSET should enumerate RUSSIAN_CHARSET for %s\n", *font_name ? font_name : "<empty>");
2928 memset(&lf, 0, sizeof(lf));
2929 lf.lfCharSet = SYMBOL_CHARSET;
2930 strcpy(lf.lfFaceName, font_name);
2931 efd.total = 0;
2932 SetLastError(0xdeadbeef);
2933 EnumFontFamiliesExA(hdc, &lf, arial_enum_proc, (LPARAM)&efd, 0);
2934 ok(ret, "EnumFontFamiliesEx error %u\n", GetLastError());
2935 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
2936 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s SYMBOL_CHARSET\n",
2937 ansi_charset, symbol_charset, russian_charset,
2938 *font_name ? font_name : "<empty>");
2939 if (*font_name && font_charset == ANSI_CHARSET)
2940 ok(efd.total == 0, "no fonts should be enumerated: %s SYMBOL_CHARSET\n", font_name);
2941 else
2943 ok(efd.total > 0, "no fonts enumerated: %s SYMBOL_CHARSET\n", font_name);
2944 for (i = 0; i < efd.total; i++)
2946 ok(efd.lf[i].lfCharSet == SYMBOL_CHARSET, "%d: got charset %d\n", i, efd.lf[i].lfCharSet);
2947 if (*font_name)
2948 ok(!strcmp(efd.lf[i].lfFaceName, font_name), "expected %s, got %s\n",
2949 font_name, efd.lf[i].lfFaceName);
2952 ok(!ansi_charset,
2953 "SYMBOL_CHARSET should NOT enumerate ANSI_CHARSET for %s\n", *font_name ? font_name : "<empty>");
2954 ok(symbol_charset > 0,
2955 "SYMBOL_CHARSET should enumerate SYMBOL_CHARSET for %s\n", *font_name ? font_name : "<empty>");
2956 ok(!russian_charset,
2957 "SYMBOL_CHARSET should NOT enumerate RUSSIAN_CHARSET for %s\n", *font_name ? font_name : "<empty>");
2960 ReleaseDC(0, hdc);
2963 static INT CALLBACK enum_multi_charset_font_proc(const LOGFONTA *lf, const TEXTMETRICA *tm, DWORD type, LPARAM lParam)
2965 const NEWTEXTMETRICEXA *ntm = (const NEWTEXTMETRICEXA *)tm;
2966 LOGFONTA *target = (LOGFONTA *)lParam;
2967 const DWORD valid_bits = 0x003f01ff;
2968 CHARSETINFO csi;
2969 DWORD fs;
2971 if (type != TRUETYPE_FONTTYPE) return TRUE;
2973 if (TranslateCharsetInfo(ULongToPtr(target->lfCharSet), &csi, TCI_SRCCHARSET)) {
2974 fs = ntm->ntmFontSig.fsCsb[0] & valid_bits;
2975 if ((fs & csi.fs.fsCsb[0]) && (fs & ~csi.fs.fsCsb[0]) && (fs & FS_LATIN1)) {
2976 *target = *lf;
2977 return FALSE;
2981 return TRUE;
2984 static INT CALLBACK enum_font_data_proc(const LOGFONTA *lf, const TEXTMETRICA *ntm, DWORD type, LPARAM lParam)
2986 struct enum_font_data *efd = (struct enum_font_data *)lParam;
2988 if (type != TRUETYPE_FONTTYPE) return 1;
2990 if (efd->total < MAX_ENUM_FONTS)
2991 efd->lf[efd->total++] = *lf;
2992 else
2993 trace("enum tests invalid; you have more than %d fonts\n", MAX_ENUM_FONTS);
2995 return 1;
2998 static INT CALLBACK enum_fullname_data_proc(const LOGFONTA *lf, const TEXTMETRICA *ntm, DWORD type, LPARAM lParam)
3000 struct enum_fullname_data *efnd = (struct enum_fullname_data *)lParam;
3002 if (type != TRUETYPE_FONTTYPE) return 1;
3004 if (efnd->total < MAX_ENUM_FONTS)
3005 efnd->elf[efnd->total++] = *(ENUMLOGFONTA *)lf;
3006 else
3007 trace("enum tests invalid; you have more than %d fonts\n", MAX_ENUM_FONTS);
3009 return 1;
3012 static void test_EnumFontFamiliesEx_default_charset(void)
3014 struct enum_font_data efd;
3015 LOGFONTA target, enum_font;
3016 UINT acp;
3017 HDC hdc;
3018 CHARSETINFO csi;
3020 acp = GetACP();
3021 if (!TranslateCharsetInfo(ULongToPtr(acp), &csi, TCI_SRCCODEPAGE)) {
3022 skip("TranslateCharsetInfo failed for code page %u.\n", acp);
3023 return;
3026 hdc = GetDC(0);
3027 memset(&enum_font, 0, sizeof(enum_font));
3028 enum_font.lfCharSet = csi.ciCharset;
3029 target.lfFaceName[0] = '\0';
3030 target.lfCharSet = csi.ciCharset;
3031 EnumFontFamiliesExA(hdc, &enum_font, enum_multi_charset_font_proc, (LPARAM)&target, 0);
3032 if (target.lfFaceName[0] == '\0') {
3033 skip("suitable font isn't found for charset %d.\n", enum_font.lfCharSet);
3034 return;
3036 if (acp == 874 || acp == 1255 || acp == 1256) {
3037 /* these codepage use complex script, expecting ANSI_CHARSET here. */
3038 target.lfCharSet = ANSI_CHARSET;
3041 efd.total = 0;
3042 memset(&enum_font, 0, sizeof(enum_font));
3043 strcpy(enum_font.lfFaceName, target.lfFaceName);
3044 enum_font.lfCharSet = DEFAULT_CHARSET;
3045 EnumFontFamiliesExA(hdc, &enum_font, enum_font_data_proc, (LPARAM)&efd, 0);
3046 ReleaseDC(0, hdc);
3048 trace("'%s' has %d charsets.\n", target.lfFaceName, efd.total);
3049 if (efd.total < 2) {
3050 ok(0, "EnumFontFamilies is broken. Expected >= 2, got %d.\n", efd.total);
3051 return;
3054 ok(efd.lf[0].lfCharSet == target.lfCharSet,
3055 "(%s) got charset %d expected %d\n",
3056 efd.lf[0].lfFaceName, efd.lf[0].lfCharSet, target.lfCharSet);
3058 return;
3061 static void test_negative_width(HDC hdc, const LOGFONTA *lf)
3063 HFONT hfont, hfont_prev;
3064 DWORD ret;
3065 GLYPHMETRICS gm1, gm2;
3066 LOGFONTA lf2 = *lf;
3067 WORD idx;
3069 if(!pGetGlyphIndicesA)
3070 return;
3072 /* negative widths are handled just as positive ones */
3073 lf2.lfWidth = -lf->lfWidth;
3075 SetLastError(0xdeadbeef);
3076 hfont = CreateFontIndirectA(lf);
3077 ok(hfont != 0, "CreateFontIndirect error %u\n", GetLastError());
3078 check_font("original", lf, hfont);
3080 hfont_prev = SelectObject(hdc, hfont);
3082 ret = pGetGlyphIndicesA(hdc, "x", 1, &idx, GGI_MARK_NONEXISTING_GLYPHS);
3083 if (ret == GDI_ERROR || idx == 0xffff)
3085 SelectObject(hdc, hfont_prev);
3086 DeleteObject(hfont);
3087 skip("Font %s doesn't contain 'x', skipping the test\n", lf->lfFaceName);
3088 return;
3091 /* filling with 0xaa causes false pass under WINEDEBUG=warn+heap */
3092 memset(&gm1, 0xab, sizeof(gm1));
3093 SetLastError(0xdeadbeef);
3094 ret = GetGlyphOutlineA(hdc, 'x', GGO_METRICS, &gm1, 0, NULL, &mat);
3095 ok(ret != GDI_ERROR, "GetGlyphOutline error 0x%x\n", GetLastError());
3097 SelectObject(hdc, hfont_prev);
3098 DeleteObject(hfont);
3100 SetLastError(0xdeadbeef);
3101 hfont = CreateFontIndirectA(&lf2);
3102 ok(hfont != 0, "CreateFontIndirect error %u\n", GetLastError());
3103 check_font("negative width", &lf2, hfont);
3105 hfont_prev = SelectObject(hdc, hfont);
3107 memset(&gm2, 0xbb, sizeof(gm2));
3108 SetLastError(0xdeadbeef);
3109 ret = GetGlyphOutlineA(hdc, 'x', GGO_METRICS, &gm2, 0, NULL, &mat);
3110 ok(ret != GDI_ERROR, "GetGlyphOutline error 0x%x\n", GetLastError());
3112 SelectObject(hdc, hfont_prev);
3113 DeleteObject(hfont);
3115 ok(gm1.gmBlackBoxX == gm2.gmBlackBoxX &&
3116 gm1.gmBlackBoxY == gm2.gmBlackBoxY &&
3117 gm1.gmptGlyphOrigin.x == gm2.gmptGlyphOrigin.x &&
3118 gm1.gmptGlyphOrigin.y == gm2.gmptGlyphOrigin.y &&
3119 gm1.gmCellIncX == gm2.gmCellIncX &&
3120 gm1.gmCellIncY == gm2.gmCellIncY,
3121 "gm1=%d,%d,%d,%d,%d,%d gm2=%d,%d,%d,%d,%d,%d\n",
3122 gm1.gmBlackBoxX, gm1.gmBlackBoxY, gm1.gmptGlyphOrigin.x,
3123 gm1.gmptGlyphOrigin.y, gm1.gmCellIncX, gm1.gmCellIncY,
3124 gm2.gmBlackBoxX, gm2.gmBlackBoxY, gm2.gmptGlyphOrigin.x,
3125 gm2.gmptGlyphOrigin.y, gm2.gmCellIncX, gm2.gmCellIncY);
3128 /* PANOSE is 10 bytes in size, need to pack the structure properly */
3129 #include "pshpack2.h"
3130 typedef struct
3132 USHORT version;
3133 SHORT xAvgCharWidth;
3134 USHORT usWeightClass;
3135 USHORT usWidthClass;
3136 SHORT fsType;
3137 SHORT ySubscriptXSize;
3138 SHORT ySubscriptYSize;
3139 SHORT ySubscriptXOffset;
3140 SHORT ySubscriptYOffset;
3141 SHORT ySuperscriptXSize;
3142 SHORT ySuperscriptYSize;
3143 SHORT ySuperscriptXOffset;
3144 SHORT ySuperscriptYOffset;
3145 SHORT yStrikeoutSize;
3146 SHORT yStrikeoutPosition;
3147 SHORT sFamilyClass;
3148 PANOSE panose;
3149 ULONG ulUnicodeRange1;
3150 ULONG ulUnicodeRange2;
3151 ULONG ulUnicodeRange3;
3152 ULONG ulUnicodeRange4;
3153 CHAR achVendID[4];
3154 USHORT fsSelection;
3155 USHORT usFirstCharIndex;
3156 USHORT usLastCharIndex;
3157 /* According to the Apple spec, original version didn't have the below fields,
3158 * version numbers were taken from the OpenType spec.
3160 /* version 0 (TrueType 1.5) */
3161 USHORT sTypoAscender;
3162 USHORT sTypoDescender;
3163 USHORT sTypoLineGap;
3164 USHORT usWinAscent;
3165 USHORT usWinDescent;
3166 /* version 1 (TrueType 1.66) */
3167 ULONG ulCodePageRange1;
3168 ULONG ulCodePageRange2;
3169 /* version 2 (OpenType 1.2) */
3170 SHORT sxHeight;
3171 SHORT sCapHeight;
3172 USHORT usDefaultChar;
3173 USHORT usBreakChar;
3174 USHORT usMaxContext;
3175 } TT_OS2_V2;
3176 #include "poppack.h"
3178 typedef struct
3180 USHORT version;
3181 USHORT num_tables;
3182 } cmap_header;
3184 typedef struct
3186 USHORT plat_id;
3187 USHORT enc_id;
3188 ULONG offset;
3189 } cmap_encoding_record;
3191 typedef struct
3193 USHORT format;
3194 USHORT length;
3195 USHORT language;
3197 BYTE glyph_ids[256];
3198 } cmap_format_0;
3200 typedef struct
3202 USHORT format;
3203 USHORT length;
3204 USHORT language;
3206 USHORT seg_countx2;
3207 USHORT search_range;
3208 USHORT entry_selector;
3209 USHORT range_shift;
3211 USHORT end_count[1]; /* this is a variable-sized array of length seg_countx2 / 2 */
3212 /* Then follows:
3213 USHORT pad;
3214 USHORT start_count[seg_countx2 / 2];
3215 USHORT id_delta[seg_countx2 / 2];
3216 USHORT id_range_offset[seg_countx2 / 2];
3217 USHORT glyph_ids[];
3219 } cmap_format_4;
3221 typedef struct
3223 USHORT end_count;
3224 USHORT start_count;
3225 USHORT id_delta;
3226 USHORT id_range_offset;
3227 } cmap_format_4_seg;
3229 static void expect_ff(const TEXTMETRICA *tmA, const TT_OS2_V2 *os2, WORD family, const char *name)
3231 ok((tmA->tmPitchAndFamily & 0xf0) == family ||
3232 broken(PRIMARYLANGID(GetSystemDefaultLangID()) != LANG_ENGLISH),
3233 "%s: expected family %02x got %02x. panose %d-%d-%d-%d-...\n",
3234 name, family, tmA->tmPitchAndFamily, os2->panose.bFamilyType, os2->panose.bSerifStyle,
3235 os2->panose.bWeight, os2->panose.bProportion);
3238 static BOOL get_first_last_from_cmap0(void *ptr, DWORD *first, DWORD *last)
3240 int i;
3241 cmap_format_0 *cmap = (cmap_format_0*)ptr;
3243 *first = 256;
3245 for(i = 0; i < 256; i++)
3247 if(cmap->glyph_ids[i] == 0) continue;
3248 *last = i;
3249 if(*first == 256) *first = i;
3251 if(*first == 256) return FALSE;
3252 return TRUE;
3255 static void get_seg4(cmap_format_4 *cmap, USHORT seg_num, cmap_format_4_seg *seg)
3257 USHORT segs = GET_BE_WORD(cmap->seg_countx2) / 2;
3258 seg->end_count = GET_BE_WORD(cmap->end_count[seg_num]);
3259 seg->start_count = GET_BE_WORD(cmap->end_count[segs + 1 + seg_num]);
3260 seg->id_delta = GET_BE_WORD(cmap->end_count[2 * segs + 1 + seg_num]);
3261 seg->id_range_offset = GET_BE_WORD(cmap->end_count[3 * segs + 1 + seg_num]);
3264 static BOOL get_first_last_from_cmap4(void *ptr, DWORD *first, DWORD *last, DWORD limit)
3266 int i;
3267 cmap_format_4 *cmap = (cmap_format_4*)ptr;
3268 USHORT seg_count = GET_BE_WORD(cmap->seg_countx2) / 2;
3270 *first = 0x10000;
3272 for(i = 0; i < seg_count; i++)
3274 cmap_format_4_seg seg;
3276 get_seg4(cmap, i, &seg);
3278 if(seg.start_count > 0xfffe) break;
3280 if(*first == 0x10000) *first = seg.start_count;
3282 *last = min(seg.end_count, 0xfffe);
3285 if(*first == 0x10000) return FALSE;
3286 return TRUE;
3289 static void *get_cmap(cmap_header *header, USHORT plat_id, USHORT enc_id)
3291 USHORT i;
3292 cmap_encoding_record *record = (cmap_encoding_record *)(header + 1);
3294 for(i = 0; i < GET_BE_WORD(header->num_tables); i++)
3296 if(GET_BE_WORD(record->plat_id) == plat_id && GET_BE_WORD(record->enc_id) == enc_id)
3297 return (BYTE *)header + GET_BE_DWORD(record->offset);
3298 record++;
3300 return NULL;
3303 typedef enum
3305 cmap_none,
3306 cmap_ms_unicode,
3307 cmap_ms_symbol
3308 } cmap_type;
3310 static BOOL get_first_last_from_cmap(HDC hdc, DWORD *first, DWORD *last, cmap_type *cmap_type)
3312 LONG size, ret;
3313 cmap_header *header;
3314 void *cmap;
3315 BOOL r = FALSE;
3316 WORD format;
3318 size = GetFontData(hdc, MS_CMAP_TAG, 0, NULL, 0);
3319 ok(size != GDI_ERROR, "no cmap table found\n");
3320 if(size == GDI_ERROR) return FALSE;
3322 header = HeapAlloc(GetProcessHeap(), 0, size);
3323 ret = GetFontData(hdc, MS_CMAP_TAG, 0, header, size);
3324 ok(ret == size, "GetFontData should return %u not %u\n", size, ret);
3325 ok(GET_BE_WORD(header->version) == 0, "got cmap version %d\n", GET_BE_WORD(header->version));
3327 cmap = get_cmap(header, 3, 1);
3328 if(cmap)
3329 *cmap_type = cmap_ms_unicode;
3330 else
3332 cmap = get_cmap(header, 3, 0);
3333 if(cmap) *cmap_type = cmap_ms_symbol;
3335 if(!cmap)
3337 *cmap_type = cmap_none;
3338 goto end;
3341 format = GET_BE_WORD(*(WORD *)cmap);
3342 switch(format)
3344 case 0:
3345 r = get_first_last_from_cmap0(cmap, first, last);
3346 break;
3347 case 4:
3348 r = get_first_last_from_cmap4(cmap, first, last, size);
3349 break;
3350 default:
3351 trace("unhandled cmap format %d\n", format);
3352 break;
3355 end:
3356 HeapFree(GetProcessHeap(), 0, header);
3357 return r;
3360 #define TT_PLATFORM_APPLE_UNICODE 0
3361 #define TT_PLATFORM_MACINTOSH 1
3362 #define TT_PLATFORM_MICROSOFT 3
3363 #define TT_APPLE_ID_DEFAULT 0
3364 #define TT_APPLE_ID_ISO_10646 2
3365 #define TT_APPLE_ID_UNICODE_2_0 3
3366 #define TT_MS_ID_SYMBOL_CS 0
3367 #define TT_MS_ID_UNICODE_CS 1
3368 #define TT_MS_LANGID_ENGLISH_UNITED_STATES 0x0409
3369 #define TT_NAME_ID_FONT_FAMILY 1
3370 #define TT_NAME_ID_FONT_SUBFAMILY 2
3371 #define TT_NAME_ID_UNIQUE_ID 3
3372 #define TT_NAME_ID_FULL_NAME 4
3373 #define TT_MAC_ID_SIMPLIFIED_CHINESE 25
3375 typedef struct sfnt_name
3377 USHORT platform_id;
3378 USHORT encoding_id;
3379 USHORT language_id;
3380 USHORT name_id;
3381 USHORT length;
3382 USHORT offset;
3383 } sfnt_name;
3385 static const LANGID mac_langid_table[] =
3387 MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ENGLISH */
3388 MAKELANGID(LANG_FRENCH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_FRENCH */
3389 MAKELANGID(LANG_GERMAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_GERMAN */
3390 MAKELANGID(LANG_ITALIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ITALIAN */
3391 MAKELANGID(LANG_DUTCH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_DUTCH */
3392 MAKELANGID(LANG_SWEDISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SWEDISH */
3393 MAKELANGID(LANG_SPANISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SPANISH */
3394 MAKELANGID(LANG_DANISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_DANISH */
3395 MAKELANGID(LANG_PORTUGUESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_PORTUGUESE */
3396 MAKELANGID(LANG_NORWEGIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_NORWEGIAN */
3397 MAKELANGID(LANG_HEBREW,SUBLANG_DEFAULT), /* TT_MAC_LANGID_HEBREW */
3398 MAKELANGID(LANG_JAPANESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_JAPANESE */
3399 MAKELANGID(LANG_ARABIC,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ARABIC */
3400 MAKELANGID(LANG_FINNISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_FINNISH */
3401 MAKELANGID(LANG_GREEK,SUBLANG_DEFAULT), /* TT_MAC_LANGID_GREEK */
3402 MAKELANGID(LANG_ICELANDIC,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ICELANDIC */
3403 MAKELANGID(LANG_MALTESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MALTESE */
3404 MAKELANGID(LANG_TURKISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TURKISH */
3405 MAKELANGID(LANG_CROATIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_CROATIAN */
3406 MAKELANGID(LANG_CHINESE_TRADITIONAL,SUBLANG_DEFAULT), /* TT_MAC_LANGID_CHINESE_TRADITIONAL */
3407 MAKELANGID(LANG_URDU,SUBLANG_DEFAULT), /* TT_MAC_LANGID_URDU */
3408 MAKELANGID(LANG_HINDI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_HINDI */
3409 MAKELANGID(LANG_THAI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_THAI */
3410 MAKELANGID(LANG_KOREAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_KOREAN */
3411 MAKELANGID(LANG_LITHUANIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_LITHUANIAN */
3412 MAKELANGID(LANG_POLISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_POLISH */
3413 MAKELANGID(LANG_HUNGARIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_HUNGARIAN */
3414 MAKELANGID(LANG_ESTONIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ESTONIAN */
3415 MAKELANGID(LANG_LATVIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_LETTISH */
3416 MAKELANGID(LANG_SAMI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SAAMISK */
3417 MAKELANGID(LANG_FAEROESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_FAEROESE */
3418 MAKELANGID(LANG_FARSI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_FARSI */
3419 MAKELANGID(LANG_RUSSIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_RUSSIAN */
3420 MAKELANGID(LANG_CHINESE_SIMPLIFIED,SUBLANG_DEFAULT), /* TT_MAC_LANGID_CHINESE_SIMPLIFIED */
3421 MAKELANGID(LANG_DUTCH,SUBLANG_DUTCH_BELGIAN), /* TT_MAC_LANGID_FLEMISH */
3422 MAKELANGID(LANG_IRISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_IRISH */
3423 MAKELANGID(LANG_ALBANIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ALBANIAN */
3424 MAKELANGID(LANG_ROMANIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ROMANIAN */
3425 MAKELANGID(LANG_CZECH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_CZECH */
3426 MAKELANGID(LANG_SLOVAK,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SLOVAK */
3427 MAKELANGID(LANG_SLOVENIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SLOVENIAN */
3428 0, /* TT_MAC_LANGID_YIDDISH */
3429 MAKELANGID(LANG_SERBIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SERBIAN */
3430 MAKELANGID(LANG_MACEDONIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MACEDONIAN */
3431 MAKELANGID(LANG_BULGARIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_BULGARIAN */
3432 MAKELANGID(LANG_UKRAINIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_UKRAINIAN */
3433 MAKELANGID(LANG_BELARUSIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_BYELORUSSIAN */
3434 MAKELANGID(LANG_UZBEK,SUBLANG_DEFAULT), /* TT_MAC_LANGID_UZBEK */
3435 MAKELANGID(LANG_KAZAK,SUBLANG_DEFAULT), /* TT_MAC_LANGID_KAZAKH */
3436 MAKELANGID(LANG_AZERI,SUBLANG_AZERI_CYRILLIC), /* TT_MAC_LANGID_AZERBAIJANI */
3437 0, /* TT_MAC_LANGID_AZERBAIJANI_ARABIC_SCRIPT */
3438 MAKELANGID(LANG_ARMENIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ARMENIAN */
3439 MAKELANGID(LANG_GEORGIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_GEORGIAN */
3440 0, /* TT_MAC_LANGID_MOLDAVIAN */
3441 MAKELANGID(LANG_KYRGYZ,SUBLANG_DEFAULT), /* TT_MAC_LANGID_KIRGHIZ */
3442 MAKELANGID(LANG_TAJIK,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TAJIKI */
3443 MAKELANGID(LANG_TURKMEN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TURKMEN */
3444 MAKELANGID(LANG_MONGOLIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MONGOLIAN */
3445 MAKELANGID(LANG_MONGOLIAN,SUBLANG_MONGOLIAN_CYRILLIC_MONGOLIA), /* TT_MAC_LANGID_MONGOLIAN_CYRILLIC_SCRIPT */
3446 MAKELANGID(LANG_PASHTO,SUBLANG_DEFAULT), /* TT_MAC_LANGID_PASHTO */
3447 0, /* TT_MAC_LANGID_KURDISH */
3448 MAKELANGID(LANG_KASHMIRI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_KASHMIRI */
3449 MAKELANGID(LANG_SINDHI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SINDHI */
3450 MAKELANGID(LANG_TIBETAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TIBETAN */
3451 MAKELANGID(LANG_NEPALI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_NEPALI */
3452 MAKELANGID(LANG_SANSKRIT,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SANSKRIT */
3453 MAKELANGID(LANG_MARATHI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MARATHI */
3454 MAKELANGID(LANG_BENGALI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_BENGALI */
3455 MAKELANGID(LANG_ASSAMESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ASSAMESE */
3456 MAKELANGID(LANG_GUJARATI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_GUJARATI */
3457 MAKELANGID(LANG_PUNJABI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_PUNJABI */
3458 MAKELANGID(LANG_ORIYA,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ORIYA */
3459 MAKELANGID(LANG_MALAYALAM,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MALAYALAM */
3460 MAKELANGID(LANG_KANNADA,SUBLANG_DEFAULT), /* TT_MAC_LANGID_KANNADA */
3461 MAKELANGID(LANG_TAMIL,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TAMIL */
3462 MAKELANGID(LANG_TELUGU,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TELUGU */
3463 MAKELANGID(LANG_SINHALESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SINHALESE */
3464 0, /* TT_MAC_LANGID_BURMESE */
3465 MAKELANGID(LANG_KHMER,SUBLANG_DEFAULT), /* TT_MAC_LANGID_KHMER */
3466 MAKELANGID(LANG_LAO,SUBLANG_DEFAULT), /* TT_MAC_LANGID_LAO */
3467 MAKELANGID(LANG_VIETNAMESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_VIETNAMESE */
3468 MAKELANGID(LANG_INDONESIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_INDONESIAN */
3469 0, /* TT_MAC_LANGID_TAGALOG */
3470 MAKELANGID(LANG_MALAY,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MALAY_ROMAN_SCRIPT */
3471 0, /* TT_MAC_LANGID_MALAY_ARABIC_SCRIPT */
3472 MAKELANGID(LANG_AMHARIC,SUBLANG_DEFAULT), /* TT_MAC_LANGID_AMHARIC */
3473 MAKELANGID(LANG_TIGRIGNA,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TIGRINYA */
3474 0, /* TT_MAC_LANGID_GALLA */
3475 0, /* TT_MAC_LANGID_SOMALI */
3476 MAKELANGID(LANG_SWAHILI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SWAHILI */
3477 0, /* TT_MAC_LANGID_RUANDA */
3478 0, /* TT_MAC_LANGID_RUNDI */
3479 0, /* TT_MAC_LANGID_CHEWA */
3480 MAKELANGID(LANG_MALAGASY,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MALAGASY */
3481 MAKELANGID(LANG_ESPERANTO,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ESPERANTO */
3482 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 95-111 */
3483 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 112-127 */
3484 MAKELANGID(LANG_WELSH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_WELSH */
3485 MAKELANGID(LANG_BASQUE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_BASQUE */
3486 MAKELANGID(LANG_CATALAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_CATALAN */
3487 0, /* TT_MAC_LANGID_LATIN */
3488 MAKELANGID(LANG_QUECHUA,SUBLANG_DEFAULT), /* TT_MAC_LANGID_QUECHUA */
3489 0, /* TT_MAC_LANGID_GUARANI */
3490 0, /* TT_MAC_LANGID_AYMARA */
3491 MAKELANGID(LANG_TATAR,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TATAR */
3492 MAKELANGID(LANG_UIGHUR,SUBLANG_DEFAULT), /* TT_MAC_LANGID_UIGHUR */
3493 0, /* TT_MAC_LANGID_DZONGKHA */
3494 0, /* TT_MAC_LANGID_JAVANESE */
3495 0, /* TT_MAC_LANGID_SUNDANESE */
3496 MAKELANGID(LANG_GALICIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_GALICIAN */
3497 MAKELANGID(LANG_AFRIKAANS,SUBLANG_DEFAULT), /* TT_MAC_LANGID_AFRIKAANS */
3498 MAKELANGID(LANG_BRETON,SUBLANG_DEFAULT), /* TT_MAC_LANGID_BRETON */
3499 MAKELANGID(LANG_INUKTITUT,SUBLANG_DEFAULT), /* TT_MAC_LANGID_INUKTITUT */
3500 MAKELANGID(LANG_SCOTTISH_GAELIC,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SCOTTISH_GAELIC */
3501 MAKELANGID(LANG_MANX_GAELIC,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MANX_GAELIC */
3502 MAKELANGID(LANG_IRISH,SUBLANG_IRISH_IRELAND), /* TT_MAC_LANGID_IRISH_GAELIC */
3503 0, /* TT_MAC_LANGID_TONGAN */
3504 0, /* TT_MAC_LANGID_GREEK_POLYTONIC */
3505 MAKELANGID(LANG_GREENLANDIC,SUBLANG_DEFAULT), /* TT_MAC_LANGID_GREELANDIC */
3506 MAKELANGID(LANG_AZERI,SUBLANG_AZERI_LATIN), /* TT_MAC_LANGID_AZERBAIJANI_ROMAN_SCRIPT */
3509 static inline WORD get_mac_code_page( const sfnt_name *name )
3511 if (GET_BE_WORD(name->encoding_id) == TT_MAC_ID_SIMPLIFIED_CHINESE) return 10008; /* special case */
3512 return 10000 + GET_BE_WORD(name->encoding_id);
3515 static int match_name_table_language( const sfnt_name *name, LANGID lang )
3517 LANGID name_lang;
3518 int res = 0;
3520 switch (GET_BE_WORD(name->platform_id))
3522 case TT_PLATFORM_MICROSOFT:
3523 res += 5; /* prefer the Microsoft name */
3524 switch (GET_BE_WORD(name->encoding_id))
3526 case TT_MS_ID_UNICODE_CS:
3527 case TT_MS_ID_SYMBOL_CS:
3528 name_lang = GET_BE_WORD(name->language_id);
3529 break;
3530 default:
3531 return 0;
3533 break;
3534 case TT_PLATFORM_MACINTOSH:
3535 if (!IsValidCodePage( get_mac_code_page( name ))) return 0;
3536 if (GET_BE_WORD(name->language_id) >= sizeof(mac_langid_table)/sizeof(mac_langid_table[0])) return 0;
3537 name_lang = mac_langid_table[GET_BE_WORD(name->language_id)];
3538 break;
3539 case TT_PLATFORM_APPLE_UNICODE:
3540 res += 2; /* prefer Unicode encodings */
3541 switch (GET_BE_WORD(name->encoding_id))
3543 case TT_APPLE_ID_DEFAULT:
3544 case TT_APPLE_ID_ISO_10646:
3545 case TT_APPLE_ID_UNICODE_2_0:
3546 if (GET_BE_WORD(name->language_id) >= sizeof(mac_langid_table)/sizeof(mac_langid_table[0])) return 0;
3547 name_lang = mac_langid_table[GET_BE_WORD(name->language_id)];
3548 break;
3549 default:
3550 return 0;
3552 break;
3553 default:
3554 return 0;
3556 if (name_lang == lang) res += 30;
3557 else if (PRIMARYLANGID( name_lang ) == PRIMARYLANGID( lang )) res += 20;
3558 else if (name_lang == MAKELANGID( LANG_ENGLISH, SUBLANG_DEFAULT )) res += 10;
3559 return res;
3562 static BOOL get_ttf_nametable_entry(HDC hdc, WORD name_id, WCHAR *out_buf, SIZE_T out_size, LCID language_id)
3564 struct sfnt_name_header
3566 USHORT format;
3567 USHORT number_of_record;
3568 USHORT storage_offset;
3569 } *header;
3570 sfnt_name *entry;
3571 BOOL r = FALSE;
3572 LONG size, offset, length;
3573 LONG c, ret;
3574 WCHAR *name;
3575 BYTE *data;
3576 USHORT i;
3577 int res, best_lang = 0, best_index = -1;
3579 size = GetFontData(hdc, MS_NAME_TAG, 0, NULL, 0);
3580 ok(size != GDI_ERROR, "no name table found\n");
3581 if(size == GDI_ERROR) return FALSE;
3583 data = HeapAlloc(GetProcessHeap(), 0, size);
3584 ret = GetFontData(hdc, MS_NAME_TAG, 0, data, size);
3585 ok(ret == size, "GetFontData should return %u not %u\n", size, ret);
3587 header = (void *)data;
3588 header->format = GET_BE_WORD(header->format);
3589 header->number_of_record = GET_BE_WORD(header->number_of_record);
3590 header->storage_offset = GET_BE_WORD(header->storage_offset);
3591 if (header->format != 0)
3593 trace("got format %u\n", header->format);
3594 goto out;
3596 if (header->number_of_record == 0 || sizeof(*header) + header->number_of_record * sizeof(*entry) > size)
3598 trace("number records out of range: %d\n", header->number_of_record);
3599 goto out;
3601 if (header->storage_offset >= size)
3603 trace("storage_offset %u > size %u\n", header->storage_offset, size);
3604 goto out;
3607 entry = (void *)&header[1];
3608 for (i = 0; i < header->number_of_record; i++)
3610 if (GET_BE_WORD(entry[i].name_id) != name_id) continue;
3611 res = match_name_table_language( &entry[i], language_id);
3612 if (res > best_lang)
3614 best_lang = res;
3615 best_index = i;
3619 offset = header->storage_offset + GET_BE_WORD(entry[best_index].offset);
3620 length = GET_BE_WORD(entry[best_index].length);
3621 if (offset + length > size)
3623 trace("entry %d is out of range\n", best_index);
3624 goto out;
3626 if (length >= out_size)
3628 trace("buffer too small for entry %d\n", best_index);
3629 goto out;
3632 name = (WCHAR *)(data + offset);
3633 for (c = 0; c < length / 2; c++)
3634 out_buf[c] = GET_BE_WORD(name[c]);
3635 out_buf[c] = 0;
3637 r = TRUE;
3639 out:
3640 HeapFree(GetProcessHeap(), 0, data);
3641 return r;
3644 static void test_text_metrics(const LOGFONTA *lf, const NEWTEXTMETRICA *ntm)
3646 HDC hdc;
3647 HFONT hfont, hfont_old;
3648 TEXTMETRICA tmA;
3649 TT_OS2_V2 tt_os2;
3650 LONG size, ret;
3651 const char *font_name = lf->lfFaceName;
3652 DWORD cmap_first = 0, cmap_last = 0;
3653 UINT ascent, descent, cell_height;
3654 cmap_type cmap_type;
3655 BOOL sys_lang_non_english;
3657 sys_lang_non_english = PRIMARYLANGID(GetSystemDefaultLangID()) != LANG_ENGLISH;
3658 hdc = GetDC(0);
3660 SetLastError(0xdeadbeef);
3661 hfont = CreateFontIndirectA(lf);
3662 ok(hfont != 0, "CreateFontIndirect error %u\n", GetLastError());
3664 hfont_old = SelectObject(hdc, hfont);
3666 size = GetFontData(hdc, MS_OS2_TAG, 0, NULL, 0);
3667 if (size == GDI_ERROR)
3669 trace("OS/2 chunk was not found\n");
3670 goto end_of_test;
3672 if (size > sizeof(tt_os2))
3674 trace("got too large OS/2 chunk of size %u\n", size);
3675 size = sizeof(tt_os2);
3678 memset(&tt_os2, 0, sizeof(tt_os2));
3679 ret = GetFontData(hdc, MS_OS2_TAG, 0, &tt_os2, size);
3680 ok(ret == size, "GetFontData should return %u not %u\n", size, ret);
3682 SetLastError(0xdeadbeef);
3683 ret = GetTextMetricsA(hdc, &tmA);
3684 ok(ret, "GetTextMetricsA error %u\n", GetLastError());
3686 if(!get_first_last_from_cmap(hdc, &cmap_first, &cmap_last, &cmap_type))
3688 skip("%s is not a Windows font, OS/2 metrics may be invalid.\n",font_name);
3690 else
3692 USHORT expect_first_A, expect_last_A, expect_break_A, expect_default_A;
3693 USHORT expect_first_W, expect_last_W, expect_break_W, expect_default_W;
3694 UINT os2_first_char, os2_last_char, default_char, break_char;
3695 USHORT version;
3696 TEXTMETRICW tmW;
3698 ascent = GET_BE_WORD(tt_os2.usWinAscent);
3699 descent = GET_BE_WORD(tt_os2.usWinDescent);
3700 cell_height = ascent + descent;
3701 ok(ntm->ntmCellHeight == cell_height, "%s: ntmCellHeight %u != %u, os2.usWinAscent/os2.usWinDescent %u/%u\n",
3702 font_name, ntm->ntmCellHeight, cell_height, ascent, descent);
3704 version = GET_BE_WORD(tt_os2.version);
3706 os2_first_char = GET_BE_WORD(tt_os2.usFirstCharIndex);
3707 os2_last_char = GET_BE_WORD(tt_os2.usLastCharIndex);
3708 default_char = GET_BE_WORD(tt_os2.usDefaultChar);
3709 break_char = GET_BE_WORD(tt_os2.usBreakChar);
3711 trace("font %s charset %u: %x-%x (%x-%x) default %x break %x OS/2 version %u vendor %4.4s\n",
3712 font_name, lf->lfCharSet, os2_first_char, os2_last_char, cmap_first, cmap_last,
3713 default_char, break_char, version, (LPCSTR)&tt_os2.achVendID);
3715 if (cmap_type == cmap_ms_symbol || (cmap_first >= 0xf000 && cmap_first < 0xf100))
3717 expect_first_W = 0;
3718 switch(GetACP())
3720 case 1255: /* Hebrew */
3721 expect_last_W = 0xf896;
3722 break;
3723 case 1257: /* Baltic */
3724 expect_last_W = 0xf8fd;
3725 break;
3726 default:
3727 expect_last_W = 0xf0ff;
3729 expect_break_W = 0x20;
3730 expect_default_W = expect_break_W - 1;
3731 expect_first_A = 0x1e;
3732 expect_last_A = min(os2_last_char - os2_first_char + 0x20, 0xff);
3734 else
3736 expect_first_W = cmap_first;
3737 expect_last_W = cmap_last;
3738 if(os2_first_char <= 1)
3739 expect_break_W = os2_first_char + 2;
3740 else if(os2_first_char > 0xff)
3741 expect_break_W = 0x20;
3742 else
3743 expect_break_W = os2_first_char;
3744 expect_default_W = expect_break_W - 1;
3745 expect_first_A = expect_default_W - 1;
3746 expect_last_A = min(expect_last_W, 0xff);
3748 expect_break_A = expect_break_W;
3749 expect_default_A = expect_default_W;
3751 /* Wine currently uses SYMBOL_CHARSET to identify whether the ANSI metrics need special handling */
3752 if(cmap_type != cmap_ms_symbol && tmA.tmCharSet == SYMBOL_CHARSET && expect_first_A != 0x1e)
3753 todo_wine ok(tmA.tmFirstChar == expect_first_A ||
3754 tmA.tmFirstChar == expect_first_A + 1 /* win9x */,
3755 "A: tmFirstChar for %s got %02x expected %02x\n", font_name, tmA.tmFirstChar, expect_first_A);
3756 else
3757 ok(tmA.tmFirstChar == expect_first_A ||
3758 tmA.tmFirstChar == expect_first_A + 1 /* win9x */,
3759 "A: tmFirstChar for %s got %02x expected %02x\n", font_name, tmA.tmFirstChar, expect_first_A);
3760 if (pGdiGetCodePage == NULL || ! IsDBCSLeadByteEx(pGdiGetCodePage(hdc), tmA.tmLastChar))
3761 ok(tmA.tmLastChar == expect_last_A ||
3762 tmA.tmLastChar == 0xff /* win9x */,
3763 "A: tmLastChar for %s got %02x expected %02x\n", font_name, tmA.tmLastChar, expect_last_A);
3764 else
3765 skip("tmLastChar is DBCS lead byte\n");
3766 ok(tmA.tmBreakChar == expect_break_A, "A: tmBreakChar for %s got %02x expected %02x\n",
3767 font_name, tmA.tmBreakChar, expect_break_A);
3768 ok(tmA.tmDefaultChar == expect_default_A || broken(sys_lang_non_english),
3769 "A: tmDefaultChar for %s got %02x expected %02x\n",
3770 font_name, tmA.tmDefaultChar, expect_default_A);
3773 SetLastError(0xdeadbeef);
3774 ret = GetTextMetricsW(hdc, &tmW);
3775 ok(ret || GetLastError() == ERROR_CALL_NOT_IMPLEMENTED,
3776 "GetTextMetricsW error %u\n", GetLastError());
3777 if (ret)
3779 /* Wine uses the os2 first char */
3780 if(cmap_first != os2_first_char && cmap_type != cmap_ms_symbol)
3781 todo_wine ok(tmW.tmFirstChar == expect_first_W, "W: tmFirstChar for %s got %02x expected %02x\n",
3782 font_name, tmW.tmFirstChar, expect_first_W);
3783 else
3784 ok(tmW.tmFirstChar == expect_first_W, "W: tmFirstChar for %s got %02x expected %02x\n",
3785 font_name, tmW.tmFirstChar, expect_first_W);
3787 /* Wine uses the os2 last char */
3788 if(expect_last_W != os2_last_char && cmap_type != cmap_ms_symbol)
3789 todo_wine ok(tmW.tmLastChar == expect_last_W, "W: tmLastChar for %s got %02x expected %02x\n",
3790 font_name, tmW.tmLastChar, expect_last_W);
3791 else
3792 ok(tmW.tmLastChar == expect_last_W, "W: tmLastChar for %s got %02x expected %02x\n",
3793 font_name, tmW.tmLastChar, expect_last_W);
3794 ok(tmW.tmBreakChar == expect_break_W, "W: tmBreakChar for %s got %02x expected %02x\n",
3795 font_name, tmW.tmBreakChar, expect_break_W);
3796 ok(tmW.tmDefaultChar == expect_default_W || broken(sys_lang_non_english),
3797 "W: tmDefaultChar for %s got %02x expected %02x\n",
3798 font_name, tmW.tmDefaultChar, expect_default_W);
3800 /* Test the aspect ratio while we have tmW */
3801 ret = GetDeviceCaps(hdc, LOGPIXELSX);
3802 ok(tmW.tmDigitizedAspectX == ret, "W: tmDigitizedAspectX %u != %u\n",
3803 tmW.tmDigitizedAspectX, ret);
3804 ret = GetDeviceCaps(hdc, LOGPIXELSY);
3805 ok(tmW.tmDigitizedAspectX == ret, "W: tmDigitizedAspectY %u != %u\n",
3806 tmW.tmDigitizedAspectX, ret);
3810 /* test FF_ values */
3811 switch(tt_os2.panose.bFamilyType)
3813 case PAN_ANY:
3814 case PAN_NO_FIT:
3815 case PAN_FAMILY_TEXT_DISPLAY:
3816 case PAN_FAMILY_PICTORIAL:
3817 default:
3818 if((tmA.tmPitchAndFamily & 1) == 0 || /* fixed */
3819 tt_os2.panose.bProportion == PAN_PROP_MONOSPACED)
3821 expect_ff(&tmA, &tt_os2, FF_MODERN, font_name);
3822 break;
3824 switch(tt_os2.panose.bSerifStyle)
3826 case PAN_ANY:
3827 case PAN_NO_FIT:
3828 default:
3829 expect_ff(&tmA, &tt_os2, FF_DONTCARE, font_name);
3830 break;
3832 case PAN_SERIF_COVE:
3833 case PAN_SERIF_OBTUSE_COVE:
3834 case PAN_SERIF_SQUARE_COVE:
3835 case PAN_SERIF_OBTUSE_SQUARE_COVE:
3836 case PAN_SERIF_SQUARE:
3837 case PAN_SERIF_THIN:
3838 case PAN_SERIF_BONE:
3839 case PAN_SERIF_EXAGGERATED:
3840 case PAN_SERIF_TRIANGLE:
3841 expect_ff(&tmA, &tt_os2, FF_ROMAN, font_name);
3842 break;
3844 case PAN_SERIF_NORMAL_SANS:
3845 case PAN_SERIF_OBTUSE_SANS:
3846 case PAN_SERIF_PERP_SANS:
3847 case PAN_SERIF_FLARED:
3848 case PAN_SERIF_ROUNDED:
3849 expect_ff(&tmA, &tt_os2, FF_SWISS, font_name);
3850 break;
3852 break;
3854 case PAN_FAMILY_SCRIPT:
3855 expect_ff(&tmA, &tt_os2, FF_SCRIPT, font_name);
3856 break;
3858 case PAN_FAMILY_DECORATIVE:
3859 expect_ff(&tmA, &tt_os2, FF_DECORATIVE, font_name);
3860 break;
3863 test_negative_width(hdc, lf);
3865 end_of_test:
3866 SelectObject(hdc, hfont_old);
3867 DeleteObject(hfont);
3869 ReleaseDC(0, hdc);
3872 static INT CALLBACK enum_truetype_font_proc(const LOGFONTA *lf, const TEXTMETRICA *ntm, DWORD type, LPARAM lParam)
3874 INT *enumed = (INT *)lParam;
3876 if (type == TRUETYPE_FONTTYPE)
3878 (*enumed)++;
3879 test_text_metrics(lf, (const NEWTEXTMETRICA *)ntm);
3881 return 1;
3884 static void test_GetTextMetrics(void)
3886 LOGFONTA lf;
3887 HDC hdc;
3888 INT enumed;
3890 /* Report only once */
3891 if(!pGetGlyphIndicesA)
3892 win_skip("GetGlyphIndicesA is unavailable, negative width will not be checked\n");
3894 hdc = GetDC(0);
3896 memset(&lf, 0, sizeof(lf));
3897 lf.lfCharSet = DEFAULT_CHARSET;
3898 enumed = 0;
3899 EnumFontFamiliesExA(hdc, &lf, enum_truetype_font_proc, (LPARAM)&enumed, 0);
3900 trace("Tested metrics of %d truetype fonts\n", enumed);
3902 ReleaseDC(0, hdc);
3905 static void test_nonexistent_font(void)
3907 static const struct
3909 const char *name;
3910 int charset;
3911 } font_subst[] =
3913 { "Times New Roman Baltic", 186 },
3914 { "Times New Roman CE", 238 },
3915 { "Times New Roman CYR", 204 },
3916 { "Times New Roman Greek", 161 },
3917 { "Times New Roman TUR", 162 }
3919 LOGFONTA lf;
3920 HDC hdc;
3921 HFONT hfont;
3922 CHARSETINFO csi;
3923 INT cs, expected_cs, i;
3924 char buf[LF_FACESIZE];
3926 if (!is_truetype_font_installed("Arial") ||
3927 !is_truetype_font_installed("Times New Roman"))
3929 skip("Arial or Times New Roman not installed\n");
3930 return;
3933 expected_cs = GetACP();
3934 if (!TranslateCharsetInfo(ULongToPtr(expected_cs), &csi, TCI_SRCCODEPAGE))
3936 skip("TranslateCharsetInfo failed for code page %d\n", expected_cs);
3937 return;
3939 expected_cs = csi.ciCharset;
3940 trace("ACP %d -> charset %d\n", GetACP(), expected_cs);
3942 hdc = GetDC(0);
3944 memset(&lf, 0, sizeof(lf));
3945 lf.lfHeight = 100;
3946 lf.lfWeight = FW_REGULAR;
3947 lf.lfCharSet = ANSI_CHARSET;
3948 lf.lfPitchAndFamily = FF_SWISS;
3949 strcpy(lf.lfFaceName, "Nonexistent font");
3950 hfont = CreateFontIndirectA(&lf);
3951 hfont = SelectObject(hdc, hfont);
3952 GetTextFaceA(hdc, sizeof(buf), buf);
3953 ok(!lstrcmpiA(buf, "Arial"), "Got %s\n", buf);
3954 cs = GetTextCharset(hdc);
3955 ok(cs == ANSI_CHARSET, "expected ANSI_CHARSET, got %d\n", cs);
3956 DeleteObject(SelectObject(hdc, hfont));
3958 memset(&lf, 0, sizeof(lf));
3959 lf.lfHeight = -13;
3960 lf.lfWeight = FW_DONTCARE;
3961 strcpy(lf.lfFaceName, "Nonexistent font");
3962 hfont = CreateFontIndirectA(&lf);
3963 hfont = SelectObject(hdc, hfont);
3964 GetTextFaceA(hdc, sizeof(buf), buf);
3965 todo_wine /* Wine uses Arial for all substitutions */
3966 ok(!lstrcmpiA(buf, "Nonexistent font") /* XP, Vista */ ||
3967 !lstrcmpiA(buf, "MS Serif") || /* Win9x */
3968 !lstrcmpiA(buf, "MS Sans Serif"), /* win2k3 */
3969 "Got %s\n", buf);
3970 cs = GetTextCharset(hdc);
3971 ok(cs == expected_cs || cs == ANSI_CHARSET, "expected %d, got %d\n", expected_cs, cs);
3972 DeleteObject(SelectObject(hdc, hfont));
3974 memset(&lf, 0, sizeof(lf));
3975 lf.lfHeight = -13;
3976 lf.lfWeight = FW_REGULAR;
3977 strcpy(lf.lfFaceName, "Nonexistent font");
3978 hfont = CreateFontIndirectA(&lf);
3979 hfont = SelectObject(hdc, hfont);
3980 GetTextFaceA(hdc, sizeof(buf), buf);
3981 ok(!lstrcmpiA(buf, "Arial") /* XP, Vista */ ||
3982 !lstrcmpiA(buf, "Times New Roman") /* Win9x */, "Got %s\n", buf);
3983 cs = GetTextCharset(hdc);
3984 ok(cs == ANSI_CHARSET, "expected ANSI_CHARSET, got %d\n", cs);
3985 DeleteObject(SelectObject(hdc, hfont));
3987 memset(&lf, 0, sizeof(lf));
3988 lf.lfHeight = -13;
3989 lf.lfWeight = FW_DONTCARE;
3990 strcpy(lf.lfFaceName, "Times New Roman");
3991 hfont = CreateFontIndirectA(&lf);
3992 hfont = SelectObject(hdc, hfont);
3993 GetTextFaceA(hdc, sizeof(buf), buf);
3994 ok(!lstrcmpiA(buf, "Times New Roman"), "Got %s\n", buf);
3995 cs = GetTextCharset(hdc);
3996 ok(cs == ANSI_CHARSET, "expected ANSI_CHARSET, got %d\n", cs);
3997 DeleteObject(SelectObject(hdc, hfont));
3999 for (i = 0; i < sizeof(font_subst)/sizeof(font_subst[0]); i++)
4001 memset(&lf, 0, sizeof(lf));
4002 lf.lfHeight = -13;
4003 lf.lfWeight = FW_REGULAR;
4004 strcpy(lf.lfFaceName, font_subst[i].name);
4005 hfont = CreateFontIndirectA(&lf);
4006 hfont = SelectObject(hdc, hfont);
4007 cs = GetTextCharset(hdc);
4008 if (font_subst[i].charset == expected_cs)
4010 ok(cs == expected_cs, "expected %d, got %d for font %s\n", expected_cs, cs, font_subst[i].name);
4011 GetTextFaceA(hdc, sizeof(buf), buf);
4012 ok(!lstrcmpiA(buf, font_subst[i].name), "expected %s, got %s\n", font_subst[i].name, buf);
4014 else
4016 ok(cs == ANSI_CHARSET, "expected ANSI_CHARSET, got %d for font %s\n", cs, font_subst[i].name);
4017 GetTextFaceA(hdc, sizeof(buf), buf);
4018 ok(!lstrcmpiA(buf, "Arial") /* XP, Vista */ ||
4019 !lstrcmpiA(buf, "Times New Roman") /* Win9x */, "got %s for font %s\n", buf, font_subst[i].name);
4021 DeleteObject(SelectObject(hdc, hfont));
4023 memset(&lf, 0, sizeof(lf));
4024 lf.lfHeight = -13;
4025 lf.lfWeight = FW_DONTCARE;
4026 strcpy(lf.lfFaceName, font_subst[i].name);
4027 hfont = CreateFontIndirectA(&lf);
4028 hfont = SelectObject(hdc, hfont);
4029 GetTextFaceA(hdc, sizeof(buf), buf);
4030 ok(!lstrcmpiA(buf, "Arial") /* Wine */ ||
4031 !lstrcmpiA(buf, font_subst[i].name) /* XP, Vista */ ||
4032 !lstrcmpiA(buf, "MS Serif") /* Win9x */ ||
4033 !lstrcmpiA(buf, "MS Sans Serif"), /* win2k3 */
4034 "got %s for font %s\n", buf, font_subst[i].name);
4035 cs = GetTextCharset(hdc);
4036 ok(cs == expected_cs || cs == ANSI_CHARSET, "expected %d, got %d for font %s\n", expected_cs, cs, font_subst[i].name);
4037 DeleteObject(SelectObject(hdc, hfont));
4040 ReleaseDC(0, hdc);
4043 static void test_GdiRealizationInfo(void)
4045 HDC hdc;
4046 DWORD info[4];
4047 BOOL r;
4048 HFONT hfont, hfont_old;
4049 LOGFONTA lf;
4051 if(!pGdiRealizationInfo)
4053 win_skip("GdiRealizationInfo not available\n");
4054 return;
4057 hdc = GetDC(0);
4059 memset(info, 0xcc, sizeof(info));
4060 r = pGdiRealizationInfo(hdc, info);
4061 ok(r != 0, "ret 0\n");
4062 ok((info[0] & 0xf) == 1, "info[0] = %x for the system font\n", info[0]);
4063 ok(info[3] == 0xcccccccc, "structure longer than 3 dwords\n");
4065 if (!is_truetype_font_installed("Arial"))
4067 skip("skipping GdiRealizationInfo with truetype font\n");
4068 goto end;
4071 memset(&lf, 0, sizeof(lf));
4072 strcpy(lf.lfFaceName, "Arial");
4073 lf.lfHeight = 20;
4074 lf.lfWeight = FW_NORMAL;
4075 hfont = CreateFontIndirectA(&lf);
4076 hfont_old = SelectObject(hdc, hfont);
4078 memset(info, 0xcc, sizeof(info));
4079 r = pGdiRealizationInfo(hdc, info);
4080 ok(r != 0, "ret 0\n");
4081 ok((info[0] & 0xf) == 3, "info[0] = %x for arial\n", info[0]);
4082 ok(info[3] == 0xcccccccc, "structure longer than 3 dwords\n");
4084 DeleteObject(SelectObject(hdc, hfont_old));
4086 end:
4087 ReleaseDC(0, hdc);
4090 /* Tests on XP SP2 show that the ANSI version of GetTextFace does NOT include
4091 the nul in the count of characters copied when the face name buffer is not
4092 NULL, whereas it does if the buffer is NULL. Further, the Unicode version
4093 always includes it. */
4094 static void test_GetTextFace(void)
4096 static const char faceA[] = "Tahoma";
4097 static const WCHAR faceW[] = {'T','a','h','o','m','a', 0};
4098 LOGFONTA fA = {0};
4099 LOGFONTW fW = {0};
4100 char bufA[LF_FACESIZE];
4101 WCHAR bufW[LF_FACESIZE];
4102 HFONT f, g;
4103 HDC dc;
4104 int n;
4106 if(!is_font_installed("Tahoma"))
4108 skip("Tahoma is not installed so skipping this test\n");
4109 return;
4112 /* 'A' case. */
4113 memcpy(fA.lfFaceName, faceA, sizeof faceA);
4114 f = CreateFontIndirectA(&fA);
4115 ok(f != NULL, "CreateFontIndirectA failed\n");
4117 dc = GetDC(NULL);
4118 g = SelectObject(dc, f);
4119 n = GetTextFaceA(dc, sizeof bufA, bufA);
4120 ok(n == sizeof faceA - 1, "GetTextFaceA returned %d\n", n);
4121 ok(lstrcmpA(faceA, bufA) == 0, "GetTextFaceA\n");
4123 /* Play with the count arg. */
4124 bufA[0] = 'x';
4125 n = GetTextFaceA(dc, 0, bufA);
4126 ok(n == 0, "GetTextFaceA returned %d\n", n);
4127 ok(bufA[0] == 'x', "GetTextFaceA buf[0] == %d\n", bufA[0]);
4129 bufA[0] = 'x';
4130 n = GetTextFaceA(dc, 1, bufA);
4131 ok(n == 0, "GetTextFaceA returned %d\n", n);
4132 ok(bufA[0] == '\0', "GetTextFaceA buf[0] == %d\n", bufA[0]);
4134 bufA[0] = 'x'; bufA[1] = 'y';
4135 n = GetTextFaceA(dc, 2, bufA);
4136 ok(n == 1, "GetTextFaceA returned %d\n", n);
4137 ok(bufA[0] == faceA[0] && bufA[1] == '\0', "GetTextFaceA didn't copy\n");
4139 n = GetTextFaceA(dc, 0, NULL);
4140 ok(n == sizeof faceA ||
4141 broken(n == 0), /* win98, winMe */
4142 "GetTextFaceA returned %d\n", n);
4144 DeleteObject(SelectObject(dc, g));
4145 ReleaseDC(NULL, dc);
4147 /* 'W' case. */
4148 memcpy(fW.lfFaceName, faceW, sizeof faceW);
4149 SetLastError(0xdeadbeef);
4150 f = CreateFontIndirectW(&fW);
4151 if (!f && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
4153 win_skip("CreateFontIndirectW is not implemented\n");
4154 return;
4156 ok(f != NULL, "CreateFontIndirectW failed\n");
4158 dc = GetDC(NULL);
4159 g = SelectObject(dc, f);
4160 n = GetTextFaceW(dc, sizeof bufW / sizeof bufW[0], bufW);
4161 ok(n == sizeof faceW / sizeof faceW[0], "GetTextFaceW returned %d\n", n);
4162 ok(lstrcmpW(faceW, bufW) == 0, "GetTextFaceW\n");
4164 /* Play with the count arg. */
4165 bufW[0] = 'x';
4166 n = GetTextFaceW(dc, 0, bufW);
4167 ok(n == 0, "GetTextFaceW returned %d\n", n);
4168 ok(bufW[0] == 'x', "GetTextFaceW buf[0] == %d\n", bufW[0]);
4170 bufW[0] = 'x';
4171 n = GetTextFaceW(dc, 1, bufW);
4172 ok(n == 1, "GetTextFaceW returned %d\n", n);
4173 ok(bufW[0] == '\0', "GetTextFaceW buf[0] == %d\n", bufW[0]);
4175 bufW[0] = 'x'; bufW[1] = 'y';
4176 n = GetTextFaceW(dc, 2, bufW);
4177 ok(n == 2, "GetTextFaceW returned %d\n", n);
4178 ok(bufW[0] == faceW[0] && bufW[1] == '\0', "GetTextFaceW didn't copy\n");
4180 n = GetTextFaceW(dc, 0, NULL);
4181 ok(n == sizeof faceW / sizeof faceW[0], "GetTextFaceW returned %d\n", n);
4183 DeleteObject(SelectObject(dc, g));
4184 ReleaseDC(NULL, dc);
4187 static void test_orientation(void)
4189 static const char test_str[11] = "Test String";
4190 HDC hdc;
4191 LOGFONTA lf;
4192 HFONT hfont, old_hfont;
4193 SIZE size;
4195 if (!is_truetype_font_installed("Arial"))
4197 skip("Arial is not installed\n");
4198 return;
4201 hdc = CreateCompatibleDC(0);
4202 memset(&lf, 0, sizeof(lf));
4203 lstrcpyA(lf.lfFaceName, "Arial");
4204 lf.lfHeight = 72;
4205 lf.lfOrientation = lf.lfEscapement = 900;
4206 hfont = create_font("orientation", &lf);
4207 old_hfont = SelectObject(hdc, hfont);
4208 ok(GetTextExtentExPointA(hdc, test_str, sizeof(test_str), 32767, NULL, NULL, &size), "GetTextExtentExPointA failed\n");
4209 ok(near_match(311, size.cx), "cx should be about 311, got %d\n", size.cx);
4210 ok(near_match(75, size.cy), "cy should be about 75, got %d\n", size.cy);
4211 SelectObject(hdc, old_hfont);
4212 DeleteObject(hfont);
4213 DeleteDC(hdc);
4216 static void test_oemcharset(void)
4218 HDC hdc;
4219 LOGFONTA lf, clf;
4220 HFONT hfont, old_hfont;
4221 int charset;
4223 hdc = CreateCompatibleDC(0);
4224 ZeroMemory(&lf, sizeof(lf));
4225 lf.lfHeight = 12;
4226 lf.lfCharSet = OEM_CHARSET;
4227 lf.lfPitchAndFamily = FIXED_PITCH | FF_MODERN;
4228 lstrcpyA(lf.lfFaceName, "Terminal");
4229 hfont = CreateFontIndirectA(&lf);
4230 old_hfont = SelectObject(hdc, hfont);
4231 charset = GetTextCharset(hdc);
4232 todo_wine
4233 ok(charset == OEM_CHARSET, "expected %d charset, got %d\n", OEM_CHARSET, charset);
4234 hfont = SelectObject(hdc, old_hfont);
4235 GetObjectA(hfont, sizeof(clf), &clf);
4236 ok(!lstrcmpA(clf.lfFaceName, lf.lfFaceName), "expected %s face name, got %s\n", lf.lfFaceName, clf.lfFaceName);
4237 ok(clf.lfPitchAndFamily == lf.lfPitchAndFamily, "expected %x family, got %x\n", lf.lfPitchAndFamily, clf.lfPitchAndFamily);
4238 ok(clf.lfCharSet == lf.lfCharSet, "expected %d charset, got %d\n", lf.lfCharSet, clf.lfCharSet);
4239 ok(clf.lfHeight == lf.lfHeight, "expected %d height, got %d\n", lf.lfHeight, clf.lfHeight);
4240 DeleteObject(hfont);
4241 DeleteDC(hdc);
4244 static int CALLBACK create_fixed_pitch_font_proc(const LOGFONTA *lpelfe,
4245 const TEXTMETRICA *lpntme,
4246 DWORD FontType, LPARAM lParam)
4248 const NEWTEXTMETRICEXA *lpntmex = (const NEWTEXTMETRICEXA *)lpntme;
4249 CHARSETINFO csi;
4250 LOGFONTA lf = *lpelfe;
4251 HFONT hfont;
4252 DWORD found_subset;
4254 /* skip bitmap, proportional or vertical font */
4255 if ((FontType & TRUETYPE_FONTTYPE) == 0 ||
4256 (lf.lfPitchAndFamily & 0xf) != FIXED_PITCH ||
4257 lf.lfFaceName[0] == '@')
4258 return 1;
4260 /* skip linked font */
4261 if (!TranslateCharsetInfo((DWORD*)(INT_PTR)lpelfe->lfCharSet, &csi, TCI_SRCCHARSET) ||
4262 (lpntmex->ntmFontSig.fsCsb[0] & csi.fs.fsCsb[0]) == 0)
4263 return 1;
4265 /* skip linked font, like SimSun-ExtB */
4266 switch (lpelfe->lfCharSet) {
4267 case SHIFTJIS_CHARSET:
4268 found_subset = lpntmex->ntmFontSig.fsUsb[1] & (1 << 17); /* Hiragana */
4269 break;
4270 case GB2312_CHARSET:
4271 case CHINESEBIG5_CHARSET:
4272 found_subset = lpntmex->ntmFontSig.fsUsb[1] & (1 << 16); /* CJK Symbols And Punctuation */
4273 break;
4274 case HANGEUL_CHARSET:
4275 found_subset = lpntmex->ntmFontSig.fsUsb[1] & (1 << 24); /* Hangul Syllables */
4276 break;
4277 default:
4278 found_subset = lpntmex->ntmFontSig.fsUsb[0] & (1 << 0); /* Basic Latin */
4279 break;
4281 if (!found_subset)
4282 return 1;
4284 /* test with an odd height */
4285 lf.lfHeight = -19;
4286 lf.lfWidth = 0;
4287 hfont = CreateFontIndirectA(&lf);
4288 if (hfont)
4290 *(HFONT *)lParam = hfont;
4291 return 0;
4293 return 1;
4296 static void test_GetGlyphOutline(void)
4298 HDC hdc;
4299 GLYPHMETRICS gm, gm2;
4300 LOGFONTA lf;
4301 HFONT hfont, old_hfont;
4302 INT ret, ret2;
4303 const UINT fmt[] = { GGO_METRICS, GGO_BITMAP, GGO_GRAY2_BITMAP,
4304 GGO_GRAY4_BITMAP, GGO_GRAY8_BITMAP };
4305 static const struct
4307 UINT cs;
4308 UINT a;
4309 UINT w;
4310 } c[] =
4312 {ANSI_CHARSET, 0x30, 0x30},
4313 {SHIFTJIS_CHARSET, 0x82a0, 0x3042},
4314 {HANGEUL_CHARSET, 0x8141, 0xac02},
4315 {GB2312_CHARSET, 0x8141, 0x4e04},
4316 {CHINESEBIG5_CHARSET, 0xa142, 0x3001}
4318 UINT i;
4320 if (!is_truetype_font_installed("Tahoma"))
4322 skip("Tahoma is not installed\n");
4323 return;
4326 hdc = CreateCompatibleDC(0);
4327 memset(&lf, 0, sizeof(lf));
4328 lf.lfHeight = 72;
4329 lstrcpyA(lf.lfFaceName, "Tahoma");
4330 SetLastError(0xdeadbeef);
4331 hfont = CreateFontIndirectA(&lf);
4332 ok(hfont != 0, "CreateFontIndirectA error %u\n", GetLastError());
4333 old_hfont = SelectObject(hdc, hfont);
4335 memset(&gm, 0, sizeof(gm));
4336 SetLastError(0xdeadbeef);
4337 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
4338 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %u\n", GetLastError());
4340 memset(&gm, 0, sizeof(gm));
4341 SetLastError(0xdeadbeef);
4342 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, NULL);
4343 ok(ret == GDI_ERROR, "GetGlyphOutlineA should fail\n");
4344 ok(GetLastError() == 0xdeadbeef ||
4345 GetLastError() == ERROR_INVALID_PARAMETER, /* win98, winMe */
4346 "expected 0xdeadbeef, got %u\n", GetLastError());
4348 memset(&gm, 0, sizeof(gm));
4349 SetLastError(0xdeadbeef);
4350 ret = GetGlyphOutlineW(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
4351 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
4352 ok(ret != GDI_ERROR, "GetGlyphOutlineW error %u\n", GetLastError());
4354 memset(&gm, 0, sizeof(gm));
4355 SetLastError(0xdeadbeef);
4356 ret = GetGlyphOutlineW(hdc, 'A', GGO_METRICS, &gm, 0, NULL, NULL);
4357 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
4359 ok(ret == GDI_ERROR, "GetGlyphOutlineW should fail\n");
4360 ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %u\n", GetLastError());
4363 /* test for needed buffer size request on space char */
4364 memset(&gm, 0, sizeof(gm));
4365 SetLastError(0xdeadbeef);
4366 ret = GetGlyphOutlineW(hdc, ' ', GGO_NATIVE, &gm, 0, NULL, &mat);
4367 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
4369 ok(ret == 0, "GetGlyphOutlineW should return 0 buffer size for space char\n");
4370 ok(gm.gmBlackBoxX == 1, "Expected 1, got %u\n", gm.gmBlackBoxX);
4371 ok(gm.gmBlackBoxY == 1, "Expected 1, got %u\n", gm.gmBlackBoxY);
4374 /* requesting buffer size for space char + error */
4375 memset(&gm, 0, sizeof(gm));
4376 SetLastError(0xdeadbeef);
4377 ret = GetGlyphOutlineW(0, ' ', GGO_NATIVE, &gm, 0, NULL, NULL);
4378 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
4380 ok(ret == GDI_ERROR, "GetGlyphOutlineW should return GDI_ERROR\n");
4381 ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %u\n", GetLastError());
4382 ok(gm.gmBlackBoxX == 0, "Expected 0, got %u\n", gm.gmBlackBoxX);
4383 ok(gm.gmBlackBoxY == 0, "Expected 0, got %u\n", gm.gmBlackBoxY);
4386 /* test GetGlyphOutline with a buffer too small */
4387 SetLastError(0xdeadbeef);
4388 ret = GetGlyphOutlineA(hdc, 'A', GGO_NATIVE, &gm, sizeof(i), &i, &mat);
4389 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
4390 ok(ret == GDI_ERROR, "GetGlyphOutlineW should return an error when the buffer size is too small.\n");
4392 for (i = 0; i < sizeof(fmt) / sizeof(fmt[0]); ++i)
4394 DWORD dummy;
4396 memset(&gm, 0xab, sizeof(gm));
4397 SetLastError(0xdeadbeef);
4398 ret = GetGlyphOutlineW(hdc, ' ', fmt[i], &gm, 0, NULL, &mat);
4399 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
4401 if (fmt[i] == GGO_METRICS)
4402 ok(ret != GDI_ERROR, "%2d:GetGlyphOutlineW should succeed, got %d\n", fmt[i], ret);
4403 else
4404 ok(ret == 0, "%2d:GetGlyphOutlineW should return 0, got %d\n", fmt[i], ret);
4405 ok(gm.gmBlackBoxX == 1, "%2d:expected 1, got %u\n", fmt[i], gm.gmBlackBoxX);
4406 ok(gm.gmBlackBoxY == 1, "%2d:expected 1, got %u\n", fmt[i], gm.gmBlackBoxY);
4409 memset(&gm, 0xab, sizeof(gm));
4410 SetLastError(0xdeadbeef);
4411 ret = GetGlyphOutlineW(hdc, ' ', fmt[i], &gm, 0, &dummy, &mat);
4412 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
4414 if (fmt[i] == GGO_METRICS)
4415 ok(ret != GDI_ERROR, "%2d:GetGlyphOutlineW should succeed, got %d\n", fmt[i], ret);
4416 else
4417 ok(ret == 0, "%2d:GetGlyphOutlineW should return 0, got %d\n", fmt[i], ret);
4418 ok(gm.gmBlackBoxX == 1, "%2d:expected 1, got %u\n", fmt[i], gm.gmBlackBoxX);
4419 ok(gm.gmBlackBoxY == 1, "%2d:expected 1, got %u\n", fmt[i], gm.gmBlackBoxY);
4422 memset(&gm, 0xab, sizeof(gm));
4423 SetLastError(0xdeadbeef);
4424 ret = GetGlyphOutlineW(hdc, ' ', fmt[i], &gm, sizeof(dummy), NULL, &mat);
4425 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
4427 if (fmt[i] == GGO_METRICS)
4428 ok(ret != GDI_ERROR, "%2d:GetGlyphOutlineW should succeed, got %d\n", fmt[i], ret);
4429 else
4430 ok(ret == 0, "%2d:GetGlyphOutlineW should return 0, got %d\n", fmt[i], ret);
4431 ok(gm.gmBlackBoxX == 1, "%2d:expected 1, got %u\n", fmt[i], gm.gmBlackBoxX);
4432 ok(gm.gmBlackBoxY == 1, "%2d:expected 1, got %u\n", fmt[i], gm.gmBlackBoxY);
4435 memset(&gm, 0xab, sizeof(gm));
4436 SetLastError(0xdeadbeef);
4437 ret = GetGlyphOutlineW(hdc, ' ', fmt[i], &gm, sizeof(dummy), &dummy, &mat);
4438 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
4440 if (fmt[i] == GGO_METRICS) {
4441 ok(ret != GDI_ERROR, "%2d:GetGlyphOutlineW should succeed, got %d\n", fmt[i], ret);
4442 ok(gm.gmBlackBoxX == 1, "%2d:expected 1, got %u\n", fmt[i], gm.gmBlackBoxX);
4443 ok(gm.gmBlackBoxY == 1, "%2d:expected 1, got %u\n", fmt[i], gm.gmBlackBoxY);
4445 else
4447 ok(ret == GDI_ERROR, "%2d:GetGlyphOutlineW should return GDI_ERROR, got %d\n", fmt[i], ret);
4448 memset(&gm2, 0xab, sizeof(gm2));
4449 ok(memcmp(&gm, &gm2, sizeof(GLYPHMETRICS)) == 0,
4450 "%2d:GLYPHMETRICS shouldn't be touched on error\n", fmt[i]);
4455 SelectObject(hdc, old_hfont);
4456 DeleteObject(hfont);
4458 for (i = 0; i < sizeof c / sizeof c[0]; ++i)
4460 static const MAT2 rotate_mat = {{0, 0}, {0, -1}, {0, 1}, {0, 0}};
4461 TEXTMETRICA tm;
4463 lf.lfFaceName[0] = '\0';
4464 lf.lfCharSet = c[i].cs;
4465 lf.lfPitchAndFamily = 0;
4466 if (EnumFontFamiliesExA(hdc, &lf, create_font_proc, (LPARAM)&hfont, 0))
4468 skip("TrueType font for charset %u is not installed\n", c[i].cs);
4469 continue;
4472 old_hfont = SelectObject(hdc, hfont);
4474 /* expected to ignore superfluous bytes (sigle-byte character) */
4475 ret = GetGlyphOutlineA(hdc, 0x8041, GGO_BITMAP, &gm, 0, NULL, &mat);
4476 ret2 = GetGlyphOutlineA(hdc, 0x41, GGO_BITMAP, &gm2, 0, NULL, &mat);
4477 ok(ret == ret2 && memcmp(&gm, &gm2, sizeof gm) == 0, "%d %d\n", ret, ret2);
4479 ret = GetGlyphOutlineA(hdc, 0xcc8041, GGO_BITMAP, &gm, 0, NULL, &mat);
4480 ok(ret == ret2 && memcmp(&gm, &gm2, sizeof gm) == 0,
4481 "Expected to ignore superfluous bytes, got %d %d\n", ret, ret2);
4483 /* expected to ignore superfluous bytes (double-byte character) */
4484 ret = GetGlyphOutlineA(hdc, c[i].a, GGO_BITMAP, &gm, 0, NULL, &mat);
4485 ret2 = GetGlyphOutlineA(hdc, c[i].a | 0xdead0000, GGO_BITMAP, &gm2, 0, NULL, &mat);
4486 ok(ret == ret2 && memcmp(&gm, &gm2, sizeof gm) == 0,
4487 "Expected to ignore superfluous bytes, got %d %d\n", ret, ret2);
4489 /* expected to match wide-char version results */
4490 ret2 = GetGlyphOutlineW(hdc, c[i].w, GGO_BITMAP, &gm2, 0, NULL, &mat);
4491 ok(ret == ret2 && memcmp(&gm, &gm2, sizeof gm) == 0, "%d %d\n", ret, ret2);
4493 if (EnumFontFamiliesExA(hdc, &lf, create_fixed_pitch_font_proc, (LPARAM)&hfont, 0))
4495 skip("Fixed-pitch TrueType font for charset %u is not available\n", c[i].cs);
4496 continue;
4498 DeleteObject(SelectObject(hdc, hfont));
4499 if (c[i].a <= 0xff)
4501 DeleteObject(SelectObject(hdc, old_hfont));
4502 continue;
4505 ret = GetObjectA(hfont, sizeof lf, &lf);
4506 ok(ret > 0, "GetObject error %u\n", GetLastError());
4508 ret = GetTextMetricsA(hdc, &tm);
4509 ok(ret, "GetTextMetrics error %u\n", GetLastError());
4510 ret = GetGlyphOutlineA(hdc, c[i].a, GGO_METRICS, &gm2, 0, NULL, &mat);
4511 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %u\n", GetLastError());
4512 trace("Tests with height=%d,avg=%d,full=%d,face=%s,charset=%d\n",
4513 -lf.lfHeight, tm.tmAveCharWidth, gm2.gmCellIncX, lf.lfFaceName, lf.lfCharSet);
4514 ok(gm2.gmCellIncX == tm.tmAveCharWidth * 2 || broken(gm2.gmCellIncX == -lf.lfHeight),
4515 "expected %d, got %d (%s:%d)\n",
4516 tm.tmAveCharWidth * 2, gm2.gmCellIncX, lf.lfFaceName, lf.lfCharSet);
4518 ret = GetGlyphOutlineA(hdc, c[i].a, GGO_METRICS, &gm2, 0, NULL, &rotate_mat);
4519 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %u\n", GetLastError());
4520 ok(gm2.gmCellIncY == -lf.lfHeight,
4521 "expected %d, got %d (%s:%d)\n",
4522 -lf.lfHeight, gm2.gmCellIncY, lf.lfFaceName, lf.lfCharSet);
4524 lf.lfItalic = TRUE;
4525 hfont = CreateFontIndirectA(&lf);
4526 ok(hfont != NULL, "CreateFontIndirect error %u\n", GetLastError());
4527 DeleteObject(SelectObject(hdc, hfont));
4528 ret = GetTextMetricsA(hdc, &tm);
4529 ok(ret, "GetTextMetrics error %u\n", GetLastError());
4530 ret = GetGlyphOutlineA(hdc, c[i].a, GGO_METRICS, &gm2, 0, NULL, &mat);
4531 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %u\n", GetLastError());
4532 ok(gm2.gmCellIncX == tm.tmAveCharWidth * 2 || broken(gm2.gmCellIncX == -lf.lfHeight),
4533 "expected %d, got %d (%s:%d)\n",
4534 tm.tmAveCharWidth * 2, gm2.gmCellIncX, lf.lfFaceName, lf.lfCharSet);
4536 lf.lfItalic = FALSE;
4537 lf.lfEscapement = lf.lfOrientation = 2700;
4538 hfont = CreateFontIndirectA(&lf);
4539 ok(hfont != NULL, "CreateFontIndirect error %u\n", GetLastError());
4540 DeleteObject(SelectObject(hdc, hfont));
4541 ret = GetGlyphOutlineA(hdc, c[i].a, GGO_METRICS, &gm2, 0, NULL, &mat);
4542 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %u\n", GetLastError());
4543 ok(gm2.gmCellIncY == -lf.lfHeight,
4544 "expected %d, got %d (%s:%d)\n",
4545 -lf.lfHeight, gm2.gmCellIncY, lf.lfFaceName, lf.lfCharSet);
4547 hfont = SelectObject(hdc, old_hfont);
4548 DeleteObject(hfont);
4551 DeleteDC(hdc);
4554 /* bug #9995: there is a limit to the character width that can be specified */
4555 static void test_GetTextMetrics2(const char *fontname, int font_height)
4557 HFONT of, hf;
4558 HDC hdc;
4559 TEXTMETRICA tm;
4560 BOOL ret;
4561 int ave_width, height, width, ratio, scale;
4563 if (!is_truetype_font_installed( fontname)) {
4564 skip("%s is not installed\n", fontname);
4565 return;
4567 hdc = CreateCompatibleDC(0);
4568 ok( hdc != NULL, "CreateCompatibleDC failed\n");
4569 /* select width = 0 */
4570 hf = CreateFontA(font_height, 0, 0, 0, FW_REGULAR, FALSE, FALSE, FALSE,
4571 DEFAULT_CHARSET, OUT_TT_PRECIS, CLIP_LH_ANGLES,
4572 DEFAULT_QUALITY, VARIABLE_PITCH,
4573 fontname);
4574 ok( hf != NULL, "CreateFontA(%s, %d) failed\n", fontname, font_height);
4575 of = SelectObject( hdc, hf);
4576 ret = GetTextMetricsA( hdc, &tm);
4577 ok(ret, "GetTextMetricsA error %u\n", GetLastError());
4578 height = tm.tmHeight;
4579 ave_width = tm.tmAveCharWidth;
4580 SelectObject( hdc, of);
4581 DeleteObject( hf);
4583 trace("height %d, ave width %d\n", height, ave_width);
4585 for (width = ave_width * 2; /* nothing*/; width += ave_width)
4587 hf = CreateFontA(height, width, 0, 0, FW_REGULAR, FALSE, FALSE, FALSE,
4588 DEFAULT_CHARSET, OUT_TT_PRECIS, CLIP_LH_ANGLES,
4589 DEFAULT_QUALITY, VARIABLE_PITCH, fontname);
4590 ok(hf != 0, "CreateFont failed\n");
4591 of = SelectObject(hdc, hf);
4592 ret = GetTextMetricsA(hdc, &tm);
4593 ok(ret, "GetTextMetrics error %u\n", GetLastError());
4594 SelectObject(hdc, of);
4595 DeleteObject(hf);
4597 if (match_off_by_1(tm.tmAveCharWidth, ave_width, FALSE) || width / height > 200)
4598 break;
4601 DeleteDC(hdc);
4603 ratio = width / height;
4604 scale = width / ave_width;
4606 trace("max width/height ratio (%d / %d) %d, max width scale (%d / %d) %d\n",
4607 width, height, ratio, width, ave_width, scale);
4609 ok(ratio >= 90 && ratio <= 110, "expected width/height ratio 90-110, got %d\n", ratio);
4612 static void test_CreateFontIndirect(void)
4614 LOGFONTA lf, getobj_lf;
4615 int ret, i;
4616 HFONT hfont;
4617 char TestName[][16] = {"Arial", "Arial Bold", "Arial Italic", "Arial Baltic"};
4619 memset(&lf, 0, sizeof(lf));
4620 lf.lfCharSet = ANSI_CHARSET;
4621 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
4622 lf.lfHeight = 16;
4623 lf.lfWidth = 16;
4624 lf.lfQuality = DEFAULT_QUALITY;
4625 lf.lfItalic = FALSE;
4626 lf.lfWeight = FW_DONTCARE;
4628 for (i = 0; i < sizeof(TestName)/sizeof(TestName[0]); i++)
4630 lstrcpyA(lf.lfFaceName, TestName[i]);
4631 hfont = CreateFontIndirectA(&lf);
4632 ok(hfont != 0, "CreateFontIndirectA failed\n");
4633 SetLastError(0xdeadbeef);
4634 ret = GetObjectA(hfont, sizeof(getobj_lf), &getobj_lf);
4635 ok(ret, "GetObject failed: %d\n", GetLastError());
4636 ok(lf.lfItalic == getobj_lf.lfItalic, "lfItalic: expect %02x got %02x\n", lf.lfItalic, getobj_lf.lfItalic);
4637 ok(lf.lfWeight == getobj_lf.lfWeight ||
4638 broken((SHORT)lf.lfWeight == getobj_lf.lfWeight), /* win9x */
4639 "lfWeight: expect %08x got %08x\n", lf.lfWeight, getobj_lf.lfWeight);
4640 ok(!lstrcmpA(lf.lfFaceName, getobj_lf.lfFaceName) ||
4641 broken(!memcmp(lf.lfFaceName, getobj_lf.lfFaceName, LF_FACESIZE-1)), /* win9x doesn't ensure '\0' termination */
4642 "font names don't match: %s != %s\n", lf.lfFaceName, getobj_lf.lfFaceName);
4643 DeleteObject(hfont);
4647 static void test_CreateFontIndirectEx(void)
4649 ENUMLOGFONTEXDVA lfex;
4650 HFONT hfont;
4652 if (!pCreateFontIndirectExA)
4654 win_skip("CreateFontIndirectExA is not available\n");
4655 return;
4658 if (!is_truetype_font_installed("Arial"))
4660 skip("Arial is not installed\n");
4661 return;
4664 SetLastError(0xdeadbeef);
4665 hfont = pCreateFontIndirectExA(NULL);
4666 ok(hfont == NULL, "got %p\n", hfont);
4667 ok(GetLastError() == 0xdeadbeef, "got error %d\n", GetLastError());
4669 memset(&lfex, 0, sizeof(lfex));
4670 lstrcpyA(lfex.elfEnumLogfontEx.elfLogFont.lfFaceName, "Arial");
4671 hfont = pCreateFontIndirectExA(&lfex);
4672 ok(hfont != 0, "CreateFontIndirectEx failed\n");
4673 if (hfont)
4674 check_font("Arial", &lfex.elfEnumLogfontEx.elfLogFont, hfont);
4675 DeleteObject(hfont);
4678 static void free_font(void *font)
4680 UnmapViewOfFile(font);
4683 static void *load_font(const char *font_name, DWORD *font_size)
4685 char file_name[MAX_PATH];
4686 HANDLE file, mapping;
4687 void *font;
4689 if (!GetWindowsDirectoryA(file_name, sizeof(file_name))) return NULL;
4690 strcat(file_name, "\\fonts\\");
4691 strcat(file_name, font_name);
4693 file = CreateFileA(file_name, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, 0);
4694 if (file == INVALID_HANDLE_VALUE) return NULL;
4696 *font_size = GetFileSize(file, NULL);
4698 mapping = CreateFileMappingA(file, NULL, PAGE_READONLY, 0, 0, NULL);
4699 if (!mapping)
4701 CloseHandle(file);
4702 return NULL;
4705 font = MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0);
4707 CloseHandle(file);
4708 CloseHandle(mapping);
4709 return font;
4712 static void test_AddFontMemResource(void)
4714 void *font;
4715 DWORD font_size, num_fonts;
4716 HANDLE ret;
4717 BOOL bRet;
4719 if (!pAddFontMemResourceEx || !pRemoveFontMemResourceEx)
4721 win_skip("AddFontMemResourceEx is not available on this platform\n");
4722 return;
4725 font = load_font("sserife.fon", &font_size);
4726 if (!font)
4728 skip("Unable to locate and load font sserife.fon\n");
4729 return;
4732 SetLastError(0xdeadbeef);
4733 ret = pAddFontMemResourceEx(NULL, 0, NULL, NULL);
4734 ok(!ret, "AddFontMemResourceEx should fail\n");
4735 ok(GetLastError() == ERROR_INVALID_PARAMETER,
4736 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
4737 GetLastError());
4739 SetLastError(0xdeadbeef);
4740 ret = pAddFontMemResourceEx(NULL, 10, NULL, NULL);
4741 ok(!ret, "AddFontMemResourceEx should fail\n");
4742 ok(GetLastError() == ERROR_INVALID_PARAMETER,
4743 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
4744 GetLastError());
4746 SetLastError(0xdeadbeef);
4747 ret = pAddFontMemResourceEx(NULL, 0, NULL, &num_fonts);
4748 ok(!ret, "AddFontMemResourceEx should fail\n");
4749 ok(GetLastError() == ERROR_INVALID_PARAMETER,
4750 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
4751 GetLastError());
4753 SetLastError(0xdeadbeef);
4754 ret = pAddFontMemResourceEx(NULL, 10, NULL, &num_fonts);
4755 ok(!ret, "AddFontMemResourceEx should fail\n");
4756 ok(GetLastError() == ERROR_INVALID_PARAMETER,
4757 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
4758 GetLastError());
4760 SetLastError(0xdeadbeef);
4761 ret = pAddFontMemResourceEx(font, 0, NULL, NULL);
4762 ok(!ret, "AddFontMemResourceEx should fail\n");
4763 ok(GetLastError() == ERROR_INVALID_PARAMETER,
4764 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
4765 GetLastError());
4767 SetLastError(0xdeadbeef);
4768 ret = pAddFontMemResourceEx(font, 10, NULL, NULL);
4769 ok(!ret, "AddFontMemResourceEx should fail\n");
4770 ok(GetLastError() == ERROR_INVALID_PARAMETER,
4771 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
4772 GetLastError());
4774 num_fonts = 0xdeadbeef;
4775 SetLastError(0xdeadbeef);
4776 ret = pAddFontMemResourceEx(font, 0, NULL, &num_fonts);
4777 ok(!ret, "AddFontMemResourceEx should fail\n");
4778 ok(GetLastError() == ERROR_INVALID_PARAMETER,
4779 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
4780 GetLastError());
4781 ok(num_fonts == 0xdeadbeef, "number of loaded fonts should be 0xdeadbeef\n");
4783 if (0) /* hangs under windows 2000 */
4785 num_fonts = 0xdeadbeef;
4786 SetLastError(0xdeadbeef);
4787 ret = pAddFontMemResourceEx(font, 10, NULL, &num_fonts);
4788 ok(!ret, "AddFontMemResourceEx should fail\n");
4789 ok(GetLastError() == 0xdeadbeef,
4790 "Expected GetLastError() to return 0xdeadbeef, got %u\n",
4791 GetLastError());
4792 ok(num_fonts == 0xdeadbeef, "number of loaded fonts should be 0xdeadbeef\n");
4795 num_fonts = 0xdeadbeef;
4796 SetLastError(0xdeadbeef);
4797 ret = pAddFontMemResourceEx(font, font_size, NULL, &num_fonts);
4798 ok(ret != 0, "AddFontMemResourceEx error %d\n", GetLastError());
4799 ok(num_fonts != 0xdeadbeef, "number of loaded fonts should not be 0xdeadbeef\n");
4800 ok(num_fonts != 0, "number of loaded fonts should not be 0\n");
4802 free_font(font);
4804 SetLastError(0xdeadbeef);
4805 bRet = pRemoveFontMemResourceEx(ret);
4806 ok(bRet, "RemoveFontMemResourceEx error %d\n", GetLastError());
4808 /* test invalid pointer to number of loaded fonts */
4809 font = load_font("sserife.fon", &font_size);
4810 ok(font != NULL, "Unable to locate and load font sserife.fon\n");
4812 SetLastError(0xdeadbeef);
4813 ret = pAddFontMemResourceEx(font, font_size, NULL, (void *)0xdeadbeef);
4814 ok(!ret, "AddFontMemResourceEx should fail\n");
4815 ok(GetLastError() == 0xdeadbeef,
4816 "Expected GetLastError() to return 0xdeadbeef, got %u\n",
4817 GetLastError());
4819 SetLastError(0xdeadbeef);
4820 ret = pAddFontMemResourceEx(font, font_size, NULL, NULL);
4821 ok(!ret, "AddFontMemResourceEx should fail\n");
4822 ok(GetLastError() == ERROR_INVALID_PARAMETER,
4823 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
4824 GetLastError());
4826 free_font(font);
4829 static INT CALLBACK enum_fonts_proc(const LOGFONTA *elf, const TEXTMETRICA *ntm, DWORD type, LPARAM lparam)
4831 LOGFONTA *lf;
4833 if (type != TRUETYPE_FONTTYPE) return 1;
4835 ok(ntm->tmWeight == elf->lfWeight, "expected %d got %d\n", ntm->tmWeight, elf->lfWeight);
4837 lf = (LOGFONTA *)lparam;
4838 *lf = *elf;
4839 return 0;
4842 static INT CALLBACK enum_all_fonts_proc(const LOGFONTA *elf, const TEXTMETRICA *ntm, DWORD type, LPARAM lparam)
4844 int ret;
4845 LOGFONTA *lf;
4847 if (type != TRUETYPE_FONTTYPE) return 1;
4849 lf = (LOGFONTA *)lparam;
4850 ret = strcmp(lf->lfFaceName, elf->lfFaceName);
4851 if(ret == 0)
4853 ok(ntm->tmWeight == elf->lfWeight, "expected %d got %d\n", ntm->tmWeight, elf->lfWeight);
4854 *lf = *elf;
4855 return 0;
4857 return 1;
4860 static INT CALLBACK enum_with_magic_retval_proc(const LOGFONTA *elf, const TEXTMETRICA *ntm, DWORD type, LPARAM lparam)
4862 return lparam;
4865 static void test_EnumFonts(void)
4867 int ret;
4868 LOGFONTA lf;
4869 HDC hdc;
4870 struct enum_fullname_data efnd;
4872 if (!is_truetype_font_installed("Arial"))
4874 skip("Arial is not installed\n");
4875 return;
4878 /* Windows uses localized font face names, so Arial Bold won't be found */
4879 if (PRIMARYLANGID(GetUserDefaultLangID()) != LANG_ENGLISH)
4881 skip("User locale is not English, skipping the test\n");
4882 return;
4885 hdc = CreateCompatibleDC(0);
4887 /* check that the enumproc's retval is returned */
4888 ret = EnumFontFamiliesA(hdc, NULL, enum_with_magic_retval_proc, 0xcafe);
4889 ok(ret == 0xcafe, "got %08x\n", ret);
4891 ret = EnumFontFamiliesA(hdc, "Arial", enum_fonts_proc, (LPARAM)&lf);
4892 ok(!ret, "font Arial is not enumerated\n");
4893 ret = strcmp(lf.lfFaceName, "Arial");
4894 ok(!ret, "expected Arial got %s\n", lf.lfFaceName);
4895 ok(lf.lfWeight == FW_NORMAL, "expected FW_NORMAL got %d\n", lf.lfWeight);
4897 strcpy(lf.lfFaceName, "Arial");
4898 ret = EnumFontFamiliesA(hdc, NULL, enum_all_fonts_proc, (LPARAM)&lf);
4899 ok(!ret, "font Arial is not enumerated\n");
4900 ret = strcmp(lf.lfFaceName, "Arial");
4901 ok(!ret, "expected Arial got %s\n", lf.lfFaceName);
4902 ok(lf.lfWeight == FW_NORMAL, "expected FW_NORMAL got %d\n", lf.lfWeight);
4904 ret = EnumFontFamiliesA(hdc, "Arial Bold", enum_fonts_proc, (LPARAM)&lf);
4905 ok(!ret, "font Arial Bold is not enumerated\n");
4906 ret = strcmp(lf.lfFaceName, "Arial");
4907 ok(!ret, "expected Arial got %s\n", lf.lfFaceName);
4908 ok(lf.lfWeight == FW_BOLD, "expected FW_BOLD got %d\n", lf.lfWeight);
4910 strcpy(lf.lfFaceName, "Arial Bold");
4911 ret = EnumFontFamiliesA(hdc, NULL, enum_all_fonts_proc, (LPARAM)&lf);
4912 ok(ret, "font Arial Bold should not be enumerated\n");
4914 ret = EnumFontFamiliesA(hdc, "Arial Bold Italic", enum_fonts_proc, (LPARAM)&lf);
4915 ok(!ret, "font Arial Bold Italic is not enumerated\n");
4916 ret = strcmp(lf.lfFaceName, "Arial");
4917 ok(!ret, "expected Arial got %s\n", lf.lfFaceName);
4918 ok(lf.lfWeight == FW_BOLD, "expected FW_BOLD got %d\n", lf.lfWeight);
4920 strcpy(lf.lfFaceName, "Arial Bold Italic");
4921 ret = EnumFontFamiliesA(hdc, NULL, enum_all_fonts_proc, (LPARAM)&lf);
4922 ok(ret, "font Arial Bold Italic should not be enumerated\n");
4924 ret = EnumFontFamiliesA(hdc, "Arial Italic Bold", enum_fonts_proc, (LPARAM)&lf);
4925 ok(ret, "font Arial Italic Bold should not be enumerated\n");
4927 strcpy(lf.lfFaceName, "Arial Italic Bold");
4928 ret = EnumFontFamiliesA(hdc, NULL, enum_all_fonts_proc, (LPARAM)&lf);
4929 ok(ret, "font Arial Italic Bold should not be enumerated\n");
4931 /* MS Shell Dlg and MS Shell Dlg 2 must exist */
4932 memset(&lf, 0, sizeof(lf));
4933 lf.lfCharSet = DEFAULT_CHARSET;
4935 memset(&efnd, 0, sizeof(efnd));
4936 strcpy(lf.lfFaceName, "MS Shell Dlg");
4937 ret = EnumFontFamiliesExA(hdc, &lf, enum_fullname_data_proc, (LPARAM)&efnd, 0);
4938 ok(ret, "font MS Shell Dlg is not enumerated\n");
4939 ret = strcmp((char*)efnd.elf[0].elfLogFont.lfFaceName, "MS Shell Dlg");
4940 todo_wine ok(!ret, "expected MS Shell Dlg got %s\n", efnd.elf[0].elfLogFont.lfFaceName);
4941 ret = strcmp((char*)efnd.elf[0].elfFullName, "MS Shell Dlg");
4942 ok(ret, "did not expect MS Shell Dlg\n");
4944 memset(&efnd, 0, sizeof(efnd));
4945 strcpy(lf.lfFaceName, "MS Shell Dlg 2");
4946 ret = EnumFontFamiliesExA(hdc, &lf, enum_fullname_data_proc, (LPARAM)&efnd, 0);
4947 ok(ret, "font MS Shell Dlg 2 is not enumerated\n");
4948 ret = strcmp((char*)efnd.elf[0].elfLogFont.lfFaceName, "MS Shell Dlg 2");
4949 todo_wine ok(!ret, "expected MS Shell Dlg 2 got %s\n", efnd.elf[0].elfLogFont.lfFaceName);
4950 ret = strcmp((char*)efnd.elf[0].elfFullName, "MS Shell Dlg 2");
4951 ok(ret, "did not expect MS Shell Dlg 2\n");
4953 DeleteDC(hdc);
4956 static INT CALLBACK is_font_installed_fullname_proc(const LOGFONTA *lf, const TEXTMETRICA *ntm, DWORD type, LPARAM lParam)
4958 const ENUMLOGFONTA *elf = (const ENUMLOGFONTA *)lf;
4959 const char *fullname = (const char *)lParam;
4961 if (!strcmp((const char *)elf->elfFullName, fullname)) return 0;
4963 return 1;
4966 static BOOL is_font_installed_fullname(const char *family, const char *fullname)
4968 HDC hdc = GetDC(0);
4969 BOOL ret = FALSE;
4971 if(!EnumFontFamiliesA(hdc, family, is_font_installed_fullname_proc, (LPARAM)fullname))
4972 ret = TRUE;
4974 ReleaseDC(0, hdc);
4975 return ret;
4978 static void test_fullname(void)
4980 static const char *TestName[] = {"Lucida Sans Demibold Roman", "Lucida Sans Italic", "Lucida Sans Regular"};
4981 WCHAR bufW[LF_FULLFACESIZE];
4982 char bufA[LF_FULLFACESIZE];
4983 HFONT hfont, of;
4984 LOGFONTA lf;
4985 HDC hdc;
4986 int i;
4987 DWORD ret;
4989 hdc = CreateCompatibleDC(0);
4990 ok(hdc != NULL, "CreateCompatibleDC failed\n");
4992 memset(&lf, 0, sizeof(lf));
4993 lf.lfCharSet = ANSI_CHARSET;
4994 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
4995 lf.lfHeight = 16;
4996 lf.lfWidth = 16;
4997 lf.lfQuality = DEFAULT_QUALITY;
4998 lf.lfItalic = FALSE;
4999 lf.lfWeight = FW_DONTCARE;
5001 for (i = 0; i < sizeof(TestName) / sizeof(TestName[0]); i++)
5003 if (!is_font_installed_fullname("Lucida Sans", TestName[i]))
5005 skip("%s is not installed\n", TestName[i]);
5006 continue;
5009 lstrcpyA(lf.lfFaceName, TestName[i]);
5010 hfont = CreateFontIndirectA(&lf);
5011 ok(hfont != 0, "CreateFontIndirectA failed\n");
5013 of = SelectObject(hdc, hfont);
5014 bufW[0] = 0;
5015 bufA[0] = 0;
5016 ret = get_ttf_nametable_entry(hdc, TT_NAME_ID_FULL_NAME, bufW, sizeof(bufW), TT_MS_LANGID_ENGLISH_UNITED_STATES);
5017 ok(ret, "face full name could not be read\n");
5018 WideCharToMultiByte(CP_ACP, 0, bufW, -1, bufA, sizeof(bufA), NULL, FALSE);
5019 ok(!lstrcmpA(bufA, TestName[i]), "font full names don't match: %s != %s\n", TestName[i], bufA);
5020 SelectObject(hdc, of);
5021 DeleteObject(hfont);
5023 DeleteDC(hdc);
5026 static WCHAR *prepend_at(WCHAR *family)
5028 if (!family)
5029 return NULL;
5031 memmove(family + 1, family, (lstrlenW(family) + 1) * sizeof(WCHAR));
5032 family[0] = '@';
5033 return family;
5036 static void test_fullname2_helper(const char *Family)
5038 char *FamilyName, *FaceName, *StyleName, *otmStr;
5039 struct enum_fullname_data efnd;
5040 WCHAR *bufW;
5041 char *bufA;
5042 HFONT hfont, of;
5043 LOGFONTA lf;
5044 HDC hdc;
5045 int i;
5046 DWORD otm_size, ret, buf_size;
5047 OUTLINETEXTMETRICA *otm;
5048 BOOL want_vertical, get_vertical;
5049 want_vertical = ( Family[0] == '@' );
5051 hdc = CreateCompatibleDC(0);
5052 ok(hdc != NULL, "CreateCompatibleDC failed\n");
5054 memset(&lf, 0, sizeof(lf));
5055 lf.lfCharSet = DEFAULT_CHARSET;
5056 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
5057 lf.lfHeight = 16;
5058 lf.lfWidth = 16;
5059 lf.lfQuality = DEFAULT_QUALITY;
5060 lf.lfItalic = FALSE;
5061 lf.lfWeight = FW_DONTCARE;
5062 strcpy(lf.lfFaceName, Family);
5063 efnd.total = 0;
5064 EnumFontFamiliesExA(hdc, &lf, enum_fullname_data_proc, (LPARAM)&efnd, 0);
5065 if (efnd.total == 0)
5066 skip("%s is not installed\n", lf.lfFaceName);
5068 for (i = 0; i < efnd.total; i++)
5070 FamilyName = (char *)efnd.elf[i].elfLogFont.lfFaceName;
5071 FaceName = (char *)efnd.elf[i].elfFullName;
5072 StyleName = (char *)efnd.elf[i].elfStyle;
5074 get_vertical = ( FamilyName[0] == '@' );
5075 ok(get_vertical == want_vertical, "Vertical flags don't match: %s %s\n", Family, FamilyName);
5077 lstrcpyA(lf.lfFaceName, FaceName);
5078 hfont = CreateFontIndirectA(&lf);
5079 ok(hfont != 0, "CreateFontIndirectA failed\n");
5081 of = SelectObject(hdc, hfont);
5082 buf_size = GetFontData(hdc, MS_NAME_TAG, 0, NULL, 0);
5083 ok(buf_size != GDI_ERROR, "no name table found\n");
5084 if (buf_size == GDI_ERROR) continue;
5086 bufW = HeapAlloc(GetProcessHeap(), 0, buf_size);
5087 bufA = HeapAlloc(GetProcessHeap(), 0, buf_size);
5089 otm_size = GetOutlineTextMetricsA(hdc, 0, NULL);
5090 otm = HeapAlloc(GetProcessHeap(), 0, otm_size);
5091 memset(otm, 0, otm_size);
5092 ret = GetOutlineTextMetricsA(hdc, otm_size, otm);
5093 ok(ret != 0, "GetOutlineTextMetrics fails!\n");
5094 if (ret == 0) continue;
5096 bufW[0] = 0;
5097 bufA[0] = 0;
5098 ret = get_ttf_nametable_entry(hdc, TT_NAME_ID_FONT_FAMILY, bufW, buf_size, GetSystemDefaultLangID());
5099 if (!ret) ret = get_ttf_nametable_entry(hdc, TT_NAME_ID_FONT_FAMILY, bufW, buf_size, TT_MS_LANGID_ENGLISH_UNITED_STATES);
5100 ok(ret, "%s: FAMILY (family name) could not be read\n", FamilyName);
5101 if (want_vertical) bufW = prepend_at(bufW);
5102 WideCharToMultiByte(CP_ACP, 0, bufW, -1, bufA, buf_size, NULL, FALSE);
5103 ok(!lstrcmpA(FamilyName, bufA), "font family names don't match: returned %s, expect %s\n", FamilyName, bufA);
5104 otmStr = (LPSTR)otm + (UINT_PTR)otm->otmpFamilyName;
5105 ok(!lstrcmpA(FamilyName, otmStr), "FamilyName %s doesn't match otmpFamilyName %s\n", FamilyName, otmStr);
5107 bufW[0] = 0;
5108 bufA[0] = 0;
5109 ret = get_ttf_nametable_entry(hdc, TT_NAME_ID_FULL_NAME, bufW, buf_size, GetSystemDefaultLangID());
5110 if (!ret) ret = get_ttf_nametable_entry(hdc, TT_NAME_ID_FULL_NAME, bufW, buf_size, TT_MS_LANGID_ENGLISH_UNITED_STATES);
5111 ok(ret, "FULL_NAME (face name) could not be read\n");
5112 if (want_vertical) bufW = prepend_at(bufW);
5113 WideCharToMultiByte(CP_ACP, 0, bufW, -1, bufA, buf_size, NULL, FALSE);
5114 ok(!lstrcmpA(FaceName, bufA), "%s: font face names don't match: returned %s, expect %s\n", FamilyName, FaceName, bufA);
5115 otmStr = (LPSTR)otm + (UINT_PTR)otm->otmpFaceName;
5116 ok(!lstrcmpA(FaceName, otmStr), "%s: FaceName %s doesn't match otmpFaceName %s\n", FamilyName, FaceName, otmStr);
5118 bufW[0] = 0;
5119 bufA[0] = 0;
5120 ret = get_ttf_nametable_entry(hdc, TT_NAME_ID_FONT_SUBFAMILY, bufW, buf_size, GetSystemDefaultLangID());
5121 if (!ret) ret = get_ttf_nametable_entry(hdc, TT_NAME_ID_FONT_SUBFAMILY, bufW, buf_size, TT_MS_LANGID_ENGLISH_UNITED_STATES);
5122 ok(ret, "%s: SUBFAMILY (style name) could not be read\n", FamilyName);
5123 WideCharToMultiByte(CP_ACP, 0, bufW, -1, bufA, buf_size, NULL, FALSE);
5124 ok(!lstrcmpA(StyleName, bufA), "%s: style names don't match: returned %s, expect %s\n", FamilyName, StyleName, bufA);
5125 otmStr = (LPSTR)otm + (UINT_PTR)otm->otmpStyleName;
5126 ok(!lstrcmpA(StyleName, otmStr), "%s: StyleName %s doesn't match otmpStyleName %s\n", FamilyName, StyleName, otmStr);
5128 bufW[0] = 0;
5129 bufA[0] = 0;
5130 ret = get_ttf_nametable_entry(hdc, TT_NAME_ID_UNIQUE_ID, bufW, buf_size, GetSystemDefaultLangID());
5131 if (!ret) ret = get_ttf_nametable_entry(hdc, TT_NAME_ID_UNIQUE_ID, bufW, buf_size, TT_MS_LANGID_ENGLISH_UNITED_STATES);
5132 ok(ret, "%s: UNIQUE_ID (full name) could not be read\n", FamilyName);
5133 WideCharToMultiByte(CP_ACP, 0, bufW, -1, bufA, buf_size, NULL, FALSE);
5134 otmStr = (LPSTR)otm + (UINT_PTR)otm->otmpFullName;
5135 ok(!lstrcmpA(otmStr, bufA), "%s: UNIQUE ID (full name) doesn't match: returned %s, expect %s\n", FamilyName, otmStr, bufA);
5137 SelectObject(hdc, of);
5138 DeleteObject(hfont);
5140 HeapFree(GetProcessHeap(), 0, otm);
5141 HeapFree(GetProcessHeap(), 0, bufW);
5142 HeapFree(GetProcessHeap(), 0, bufA);
5144 DeleteDC(hdc);
5147 static void test_fullname2(void)
5149 test_fullname2_helper("Arial");
5150 test_fullname2_helper("DejaVu Sans");
5151 test_fullname2_helper("Lucida Sans");
5152 test_fullname2_helper("Tahoma");
5153 test_fullname2_helper("Webdings");
5154 test_fullname2_helper("Wingdings");
5155 test_fullname2_helper("SimSun");
5156 test_fullname2_helper("NSimSun");
5157 test_fullname2_helper("MingLiu");
5158 test_fullname2_helper("PMingLiu");
5159 test_fullname2_helper("WenQuanYi Micro Hei");
5160 test_fullname2_helper("MS UI Gothic");
5161 test_fullname2_helper("Ume UI Gothic");
5162 test_fullname2_helper("MS Gothic");
5163 test_fullname2_helper("Ume Gothic");
5164 test_fullname2_helper("MS PGothic");
5165 test_fullname2_helper("Ume P Gothic");
5166 test_fullname2_helper("Gulim");
5167 test_fullname2_helper("Batang");
5168 test_fullname2_helper("UnBatang");
5169 test_fullname2_helper("UnDotum");
5170 test_fullname2_helper("@SimSun");
5171 test_fullname2_helper("@NSimSun");
5172 test_fullname2_helper("@MingLiu");
5173 test_fullname2_helper("@PMingLiu");
5174 test_fullname2_helper("@WenQuanYi Micro Hei");
5175 test_fullname2_helper("@MS UI Gothic");
5176 test_fullname2_helper("@Ume UI Gothic");
5177 test_fullname2_helper("@MS Gothic");
5178 test_fullname2_helper("@Ume Gothic");
5179 test_fullname2_helper("@MS PGothic");
5180 test_fullname2_helper("@Ume P Gothic");
5181 test_fullname2_helper("@Gulim");
5182 test_fullname2_helper("@Batang");
5183 test_fullname2_helper("@UnBatang");
5184 test_fullname2_helper("@UnDotum");
5188 static void test_GetGlyphOutline_empty_contour(void)
5190 HDC hdc;
5191 LOGFONTA lf;
5192 HFONT hfont, hfont_prev;
5193 TTPOLYGONHEADER *header;
5194 GLYPHMETRICS gm;
5195 char buf[1024];
5196 DWORD ret;
5198 memset(&lf, 0, sizeof(lf));
5199 lf.lfHeight = 72;
5200 lstrcpyA(lf.lfFaceName, "wine_test");
5202 hfont = CreateFontIndirectA(&lf);
5203 ok(hfont != 0, "CreateFontIndirectA error %u\n", GetLastError());
5205 hdc = GetDC(NULL);
5207 hfont_prev = SelectObject(hdc, hfont);
5208 ok(hfont_prev != NULL, "SelectObject failed\n");
5210 ret = GetGlyphOutlineW(hdc, 0xa8, GGO_NATIVE, &gm, 0, NULL, &mat);
5211 ok(ret == 228, "GetGlyphOutline returned %d, expected 228\n", ret);
5213 header = (TTPOLYGONHEADER*)buf;
5214 ret = GetGlyphOutlineW(hdc, 0xa8, GGO_NATIVE, &gm, sizeof(buf), buf, &mat);
5215 ok(ret == 228, "GetGlyphOutline returned %d, expected 228\n", ret);
5216 ok(header->cb == 36, "header->cb = %d, expected 36\n", header->cb);
5217 ok(header->dwType == TT_POLYGON_TYPE, "header->dwType = %d, expected TT_POLYGON_TYPE\n", header->dwType);
5218 header = (TTPOLYGONHEADER*)((char*)header+header->cb);
5219 ok(header->cb == 96, "header->cb = %d, expected 96\n", header->cb);
5220 header = (TTPOLYGONHEADER*)((char*)header+header->cb);
5221 ok(header->cb == 96, "header->cb = %d, expected 96\n", header->cb);
5223 SelectObject(hdc, hfont_prev);
5224 DeleteObject(hfont);
5225 ReleaseDC(NULL, hdc);
5228 static void test_GetGlyphOutline_metric_clipping(void)
5230 HDC hdc;
5231 LOGFONTA lf;
5232 HFONT hfont, hfont_prev;
5233 GLYPHMETRICS gm;
5234 TEXTMETRICA tm;
5235 TEXTMETRICW tmW;
5236 DWORD ret;
5238 memset(&lf, 0, sizeof(lf));
5239 lf.lfHeight = 72;
5240 lstrcpyA(lf.lfFaceName, "wine_test");
5242 SetLastError(0xdeadbeef);
5243 hfont = CreateFontIndirectA(&lf);
5244 ok(hfont != 0, "CreateFontIndirectA error %u\n", GetLastError());
5246 hdc = GetDC(NULL);
5248 hfont_prev = SelectObject(hdc, hfont);
5249 ok(hfont_prev != NULL, "SelectObject failed\n");
5251 SetLastError(0xdeadbeef);
5252 ret = GetTextMetricsA(hdc, &tm);
5253 ok(ret, "GetTextMetrics error %u\n", GetLastError());
5255 GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
5256 ok(gm.gmptGlyphOrigin.y <= tm.tmAscent,
5257 "Glyph top(%d) exceeds ascent(%d)\n",
5258 gm.gmptGlyphOrigin.y, tm.tmAscent);
5259 GetGlyphOutlineA(hdc, 'D', GGO_METRICS, &gm, 0, NULL, &mat);
5260 ok(gm.gmptGlyphOrigin.y - gm.gmBlackBoxY >= -tm.tmDescent,
5261 "Glyph bottom(%d) exceeds descent(%d)\n",
5262 gm.gmptGlyphOrigin.y - gm.gmBlackBoxY, -tm.tmDescent);
5264 /* Test tmLastChar - wine_test has code points fffb-fffe mapped to glyph 0 */
5265 GetTextMetricsW(hdc, &tmW);
5266 todo_wine
5267 ok( tmW.tmLastChar == 0xfffe, "got %04x\n", tmW.tmLastChar);
5269 SelectObject(hdc, hfont_prev);
5270 DeleteObject(hfont);
5271 ReleaseDC(NULL, hdc);
5274 static void test_CreateScalableFontResource(void)
5276 char ttf_name[MAX_PATH];
5277 char tmp_path[MAX_PATH];
5278 char fot_name[MAX_PATH];
5279 char *file_part;
5280 DWORD ret;
5281 int i;
5283 if (!pAddFontResourceExA || !pRemoveFontResourceExA)
5285 win_skip("AddFontResourceExA is not available on this platform\n");
5286 return;
5289 if (!write_ttf_file("wine_test.ttf", ttf_name))
5291 skip("Failed to create ttf file for testing\n");
5292 return;
5295 trace("created %s\n", ttf_name);
5297 ret = is_truetype_font_installed("wine_test");
5298 ok(!ret, "font wine_test should not be enumerated\n");
5300 ret = GetTempPathA(MAX_PATH, tmp_path);
5301 ok(ret, "GetTempPath() error %d\n", GetLastError());
5302 ret = GetTempFileNameA(tmp_path, "fot", 0, fot_name);
5303 ok(ret, "GetTempFileName() error %d\n", GetLastError());
5305 ret = GetFileAttributesA(fot_name);
5306 ok(ret != INVALID_FILE_ATTRIBUTES, "file %s does not exist\n", fot_name);
5308 SetLastError(0xdeadbeef);
5309 ret = CreateScalableFontResourceA(0, fot_name, ttf_name, NULL);
5310 ok(!ret, "CreateScalableFontResource() should fail\n");
5311 ok(GetLastError() == ERROR_FILE_EXISTS, "not expected error %d\n", GetLastError());
5313 SetLastError(0xdeadbeef);
5314 ret = CreateScalableFontResourceA(0, fot_name, ttf_name, "");
5315 ok(!ret, "CreateScalableFontResource() should fail\n");
5316 ok(GetLastError() == ERROR_FILE_EXISTS, "not expected error %d\n", GetLastError());
5318 file_part = strrchr(ttf_name, '\\');
5319 SetLastError(0xdeadbeef);
5320 ret = CreateScalableFontResourceA(0, fot_name, file_part, tmp_path);
5321 ok(!ret, "CreateScalableFontResource() should fail\n");
5322 ok(GetLastError() == ERROR_FILE_EXISTS, "not expected error %d\n", GetLastError());
5324 SetLastError(0xdeadbeef);
5325 ret = CreateScalableFontResourceA(0, fot_name, "random file name", tmp_path);
5326 ok(!ret, "CreateScalableFontResource() should fail\n");
5327 ok(GetLastError() == ERROR_INVALID_PARAMETER, "not expected error %d\n", GetLastError());
5329 SetLastError(0xdeadbeef);
5330 ret = CreateScalableFontResourceA(0, fot_name, NULL, ttf_name);
5331 ok(!ret, "CreateScalableFontResource() should fail\n");
5332 ok(GetLastError() == ERROR_INVALID_PARAMETER, "not expected error %d\n", GetLastError());
5334 ret = DeleteFileA(fot_name);
5335 ok(ret, "DeleteFile() error %d\n", GetLastError());
5337 ret = pRemoveFontResourceExA(fot_name, 0, 0);
5338 ok(!ret, "RemoveFontResourceEx() should fail\n");
5340 /* test public font resource */
5341 SetLastError(0xdeadbeef);
5342 ret = CreateScalableFontResourceA(0, fot_name, ttf_name, NULL);
5343 ok(ret, "CreateScalableFontResource() error %d\n", GetLastError());
5345 ret = is_truetype_font_installed("wine_test");
5346 ok(!ret, "font wine_test should not be enumerated\n");
5348 SetLastError(0xdeadbeef);
5349 ret = pAddFontResourceExA(fot_name, 0, 0);
5350 ok(ret, "AddFontResourceEx() error %d\n", GetLastError());
5352 ret = is_truetype_font_installed("wine_test");
5353 ok(ret, "font wine_test should be enumerated\n");
5355 test_GetGlyphOutline_empty_contour();
5356 test_GetGlyphOutline_metric_clipping();
5358 ret = pRemoveFontResourceExA(fot_name, FR_PRIVATE, 0);
5359 ok(!ret, "RemoveFontResourceEx() with not matching flags should fail\n");
5361 SetLastError(0xdeadbeef);
5362 ret = pRemoveFontResourceExA(fot_name, 0, 0);
5363 ok(ret, "RemoveFontResourceEx() error %d\n", GetLastError());
5365 ret = is_truetype_font_installed("wine_test");
5366 ok(!ret, "font wine_test should not be enumerated\n");
5368 ret = pRemoveFontResourceExA(fot_name, 0, 0);
5369 ok(!ret, "RemoveFontResourceEx() should fail\n");
5371 /* test refcounting */
5372 for (i = 0; i < 5; i++)
5374 SetLastError(0xdeadbeef);
5375 ret = pAddFontResourceExA(fot_name, 0, 0);
5376 ok(ret, "AddFontResourceEx() error %d\n", GetLastError());
5378 for (i = 0; i < 5; i++)
5380 SetLastError(0xdeadbeef);
5381 ret = pRemoveFontResourceExA(fot_name, 0, 0);
5382 ok(ret, "RemoveFontResourceEx() error %d\n", GetLastError());
5384 ret = pRemoveFontResourceExA(fot_name, 0, 0);
5385 ok(!ret, "RemoveFontResourceEx() should fail\n");
5387 DeleteFileA(fot_name);
5389 /* test hidden font resource */
5390 SetLastError(0xdeadbeef);
5391 ret = CreateScalableFontResourceA(1, fot_name, ttf_name, NULL);
5392 ok(ret, "CreateScalableFontResource() error %d\n", GetLastError());
5394 ret = is_truetype_font_installed("wine_test");
5395 ok(!ret, "font wine_test should not be enumerated\n");
5397 SetLastError(0xdeadbeef);
5398 ret = pAddFontResourceExA(fot_name, 0, 0);
5399 ok(ret, "AddFontResourceEx() error %d\n", GetLastError());
5401 ret = is_truetype_font_installed("wine_test");
5402 todo_wine
5403 ok(!ret, "font wine_test should not be enumerated\n");
5405 /* XP allows removing a private font added with 0 flags */
5406 SetLastError(0xdeadbeef);
5407 ret = pRemoveFontResourceExA(fot_name, FR_PRIVATE, 0);
5408 ok(ret, "RemoveFontResourceEx() error %d\n", GetLastError());
5410 ret = is_truetype_font_installed("wine_test");
5411 ok(!ret, "font wine_test should not be enumerated\n");
5413 ret = pRemoveFontResourceExA(fot_name, 0, 0);
5414 ok(!ret, "RemoveFontResourceEx() should fail\n");
5416 DeleteFileA(fot_name);
5417 DeleteFileA(ttf_name);
5420 static void check_vertical_font(const char *name, BOOL *installed, BOOL *selected, GLYPHMETRICS *gm, WORD *gi)
5422 LOGFONTA lf;
5423 HFONT hfont, hfont_prev;
5424 HDC hdc;
5425 char facename[100];
5426 DWORD ret;
5427 static const WCHAR str[] = { 0x2025 };
5429 *installed = is_truetype_font_installed(name);
5431 lf.lfHeight = -18;
5432 lf.lfWidth = 0;
5433 lf.lfEscapement = 0;
5434 lf.lfOrientation = 0;
5435 lf.lfWeight = FW_DONTCARE;
5436 lf.lfItalic = 0;
5437 lf.lfUnderline = 0;
5438 lf.lfStrikeOut = 0;
5439 lf.lfCharSet = DEFAULT_CHARSET;
5440 lf.lfOutPrecision = OUT_TT_ONLY_PRECIS;
5441 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
5442 lf.lfQuality = DEFAULT_QUALITY;
5443 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
5444 strcpy(lf.lfFaceName, name);
5446 hfont = CreateFontIndirectA(&lf);
5447 ok(hfont != NULL, "CreateFontIndirectA failed\n");
5449 hdc = GetDC(NULL);
5451 hfont_prev = SelectObject(hdc, hfont);
5452 ok(hfont_prev != NULL, "SelectObject failed\n");
5454 ret = GetTextFaceA(hdc, sizeof facename, facename);
5455 ok(ret, "GetTextFaceA failed\n");
5456 *selected = !strcmp(facename, name);
5458 ret = GetGlyphOutlineW(hdc, 0x2025, GGO_METRICS, gm, 0, NULL, &mat);
5459 ok(ret != GDI_ERROR, "GetGlyphOutlineW failed\n");
5460 if (!*selected)
5461 memset(gm, 0, sizeof *gm);
5463 ret = pGetGlyphIndicesW(hdc, str, 1, gi, 0);
5464 ok(ret != GDI_ERROR, "GetGlyphIndicesW failed\n");
5466 SelectObject(hdc, hfont_prev);
5467 DeleteObject(hfont);
5468 ReleaseDC(NULL, hdc);
5471 static void check_vertical_metrics(const char *face)
5473 LOGFONTA lf;
5474 HFONT hfont, hfont_prev;
5475 HDC hdc;
5476 DWORD ret;
5477 GLYPHMETRICS rgm, vgm;
5478 const UINT code = 0x5EAD, height = 1000;
5479 WORD idx;
5480 ABC abc;
5481 OUTLINETEXTMETRICA otm;
5482 USHORT numOfLongVerMetrics;
5484 hdc = GetDC(NULL);
5486 memset(&lf, 0, sizeof(lf));
5487 strcpy(lf.lfFaceName, face);
5488 lf.lfHeight = -height;
5489 lf.lfCharSet = DEFAULT_CHARSET;
5490 lf.lfEscapement = lf.lfOrientation = 900;
5491 hfont = CreateFontIndirectA(&lf);
5492 hfont_prev = SelectObject(hdc, hfont);
5493 ret = GetGlyphOutlineW(hdc, code, GGO_METRICS, &rgm, 0, NULL, &mat);
5494 ok(ret != GDI_ERROR, "GetGlyphOutlineW failed\n");
5495 ret = GetCharABCWidthsW(hdc, code, code, &abc);
5496 ok(ret, "GetCharABCWidthsW failed\n");
5497 DeleteObject(SelectObject(hdc, hfont_prev));
5499 memset(&lf, 0, sizeof(lf));
5500 strcpy(lf.lfFaceName, "@");
5501 strcat(lf.lfFaceName, face);
5502 lf.lfHeight = -height;
5503 lf.lfCharSet = DEFAULT_CHARSET;
5504 hfont = CreateFontIndirectA(&lf);
5505 hfont_prev = SelectObject(hdc, hfont);
5506 ret = GetGlyphOutlineW(hdc, code, GGO_METRICS, &vgm, 0, NULL, &mat);
5507 ok(ret != GDI_ERROR, "GetGlyphOutlineW failed\n");
5509 memset(&otm, 0, sizeof(otm));
5510 otm.otmSize = sizeof(otm);
5511 ret = GetOutlineTextMetricsA(hdc, sizeof(otm), &otm);
5512 ok(ret != 0, "GetOutlineTextMetricsA failed\n");
5514 if (GetFontData(hdc, MS_MAKE_TAG('v','h','e','a'), sizeof(SHORT) * 17,
5515 &numOfLongVerMetrics, sizeof(numOfLongVerMetrics)) != GDI_ERROR) {
5516 int offset;
5517 SHORT topSideBearing;
5519 if (!pGetGlyphIndicesW) {
5520 win_skip("GetGlyphIndices is not available on this platform\n");
5522 else {
5523 ret = pGetGlyphIndicesW(hdc, (LPCWSTR)&code, 1, &idx, 0);
5524 ok(ret != 0, "GetGlyphIndicesW failed\n");
5525 numOfLongVerMetrics = GET_BE_WORD(numOfLongVerMetrics);
5526 if (numOfLongVerMetrics > idx)
5527 offset = idx * 2 + 1;
5528 else
5529 offset = numOfLongVerMetrics * 2 + (idx - numOfLongVerMetrics);
5530 ret = GetFontData(hdc, MS_MAKE_TAG('v','m','t','x'), offset * sizeof(SHORT),
5531 &topSideBearing, sizeof(SHORT));
5532 ok(ret != GDI_ERROR, "GetFontData(vmtx) failed\n");
5533 topSideBearing = GET_BE_WORD(topSideBearing);
5534 ok(match_off_by_1(vgm.gmptGlyphOrigin.x,
5535 MulDiv(topSideBearing, height, otm.otmEMSquare), FALSE),
5536 "expected %d, got %d\n",
5537 MulDiv(topSideBearing, height, otm.otmEMSquare), vgm.gmptGlyphOrigin.x);
5540 else
5542 ok(vgm.gmptGlyphOrigin.x == rgm.gmptGlyphOrigin.x + vgm.gmCellIncX + otm.otmDescent,
5543 "got %d, expected rgm.origin.x(%d) + vgm.cellIncX(%d) + descent(%d)\n",
5544 vgm.gmptGlyphOrigin.x, rgm.gmptGlyphOrigin.x, vgm.gmCellIncX, otm.otmDescent);
5547 ok(vgm.gmptGlyphOrigin.y == abc.abcA + abc.abcB + otm.otmDescent ||
5548 broken(vgm.gmptGlyphOrigin.y == abc.abcA + abc.abcB - otm.otmTextMetrics.tmDescent) /* win2k */,
5549 "got %d, expected abcA(%d) + abcB(%u) + descent(%d)\n",
5550 (INT)vgm.gmptGlyphOrigin.y, abc.abcA, abc.abcB, otm.otmDescent);
5552 DeleteObject(SelectObject(hdc, hfont_prev));
5553 ReleaseDC(NULL, hdc);
5556 static void test_vertical_font(void)
5558 char ttf_name[MAX_PATH];
5559 int num, i;
5560 BOOL ret, installed, selected;
5561 GLYPHMETRICS gm;
5562 WORD hgi, vgi;
5563 const char* face_list[] = {
5564 "@WineTestVertical", /* has vmtx table */
5565 "@Ume Gothic", /* doesn't have vmtx table */
5566 "@MS UI Gothic", /* has vmtx table, available on native */
5569 if (!pAddFontResourceExA || !pRemoveFontResourceExA || !pGetGlyphIndicesW)
5571 win_skip("AddFontResourceExA or GetGlyphIndicesW is not available on this platform\n");
5572 return;
5575 if (!write_ttf_file("vertical.ttf", ttf_name))
5577 skip("Failed to create ttf file for testing\n");
5578 return;
5581 num = pAddFontResourceExA(ttf_name, FR_PRIVATE, 0);
5582 ok(num == 2, "AddFontResourceExA should add 2 fonts from vertical.ttf\n");
5584 check_vertical_font("WineTestVertical", &installed, &selected, &gm, &hgi);
5585 ok(installed, "WineTestVertical is not installed\n");
5586 ok(selected, "WineTestVertical is not selected\n");
5587 ok(gm.gmBlackBoxX > gm.gmBlackBoxY,
5588 "gmBlackBoxX(%u) should be greater than gmBlackBoxY(%u) if horizontal\n",
5589 gm.gmBlackBoxX, gm.gmBlackBoxY);
5591 check_vertical_font("@WineTestVertical", &installed, &selected, &gm, &vgi);
5592 ok(installed, "@WineTestVertical is not installed\n");
5593 ok(selected, "@WineTestVertical is not selected\n");
5594 ok(gm.gmBlackBoxX > gm.gmBlackBoxY,
5595 "gmBlackBoxX(%u) should be less than gmBlackBoxY(%u) if vertical\n",
5596 gm.gmBlackBoxX, gm.gmBlackBoxY);
5598 ok(hgi != vgi, "same glyph h:%u v:%u\n", hgi, vgi);
5600 for (i = 0; i < sizeof(face_list)/sizeof(face_list[0]); i++) {
5601 const char* face = face_list[i];
5602 if (!is_truetype_font_installed(face)) {
5603 skip("%s is not installed\n", face);
5604 continue;
5606 trace("Testing %s...\n", face);
5607 check_vertical_metrics(&face[1]);
5610 ret = pRemoveFontResourceExA(ttf_name, FR_PRIVATE, 0);
5611 ok(ret, "RemoveFontResourceEx() error %d\n", GetLastError());
5613 DeleteFileA(ttf_name);
5616 static INT CALLBACK has_vertical_font_proc(const LOGFONTA *lf, const TEXTMETRICA *ntm,
5617 DWORD type, LPARAM lParam)
5619 if (lf->lfFaceName[0] == '@') {
5620 return 0;
5622 return 1;
5625 static void test_east_asian_font_selection(void)
5627 HDC hdc;
5628 UINT charset[] = { SHIFTJIS_CHARSET, HANGEUL_CHARSET, JOHAB_CHARSET,
5629 GB2312_CHARSET, CHINESEBIG5_CHARSET };
5630 size_t i;
5632 hdc = GetDC(NULL);
5634 for (i = 0; i < sizeof(charset)/sizeof(charset[0]); i++)
5636 LOGFONTA lf;
5637 HFONT hfont;
5638 char face_name[LF_FACESIZE];
5639 int ret;
5641 memset(&lf, 0, sizeof lf);
5642 lf.lfFaceName[0] = '\0';
5643 lf.lfCharSet = charset[i];
5645 if (EnumFontFamiliesExA(hdc, &lf, has_vertical_font_proc, 0, 0))
5647 skip("Vertical font for charset %u is not installed\n", charset[i]);
5648 continue;
5651 hfont = CreateFontIndirectA(&lf);
5652 hfont = SelectObject(hdc, hfont);
5653 memset(face_name, 0, sizeof face_name);
5654 ret = GetTextFaceA(hdc, sizeof face_name, face_name);
5655 ok(ret && face_name[0] != '@',
5656 "expected non-vertical face for charset %u, got %s\n", charset[i], face_name);
5657 DeleteObject(SelectObject(hdc, hfont));
5659 memset(&lf, 0, sizeof lf);
5660 strcpy(lf.lfFaceName, "@");
5661 lf.lfCharSet = charset[i];
5662 hfont = CreateFontIndirectA(&lf);
5663 hfont = SelectObject(hdc, hfont);
5664 memset(face_name, 0, sizeof face_name);
5665 ret = GetTextFaceA(hdc, sizeof face_name, face_name);
5666 ok(ret && face_name[0] == '@',
5667 "expected vertical face for charset %u, got %s\n", charset[i], face_name);
5668 DeleteObject(SelectObject(hdc, hfont));
5670 ReleaseDC(NULL, hdc);
5673 static int get_font_dpi(const LOGFONTA *lf, int *height)
5675 HDC hdc = CreateCompatibleDC(0);
5676 HFONT hfont;
5677 TEXTMETRICA tm;
5678 int ret;
5680 hfont = CreateFontIndirectA(lf);
5681 ok(hfont != 0, "CreateFontIndirect failed\n");
5683 SelectObject(hdc, hfont);
5684 ret = GetTextMetricsA(hdc, &tm);
5685 ok(ret, "GetTextMetrics failed\n");
5686 ret = tm.tmDigitizedAspectX;
5687 if (height) *height = tm.tmHeight;
5689 DeleteDC(hdc);
5690 DeleteObject(hfont);
5692 return ret;
5695 static void test_stock_fonts(void)
5697 static const int font[] =
5699 ANSI_FIXED_FONT, ANSI_VAR_FONT, SYSTEM_FONT, DEVICE_DEFAULT_FONT, DEFAULT_GUI_FONT
5700 /* SYSTEM_FIXED_FONT, OEM_FIXED_FONT */
5702 static const struct test_data
5704 int charset, weight, height, height_pixels, dpi;
5705 const char face_name[LF_FACESIZE];
5706 } td[][11] =
5708 { /* ANSI_FIXED_FONT */
5709 { DEFAULT_CHARSET, FW_NORMAL, 12, 13, 96, "Courier" },
5710 { DEFAULT_CHARSET, FW_NORMAL, 12, 13, 120, "Courier" },
5711 { 0 }
5713 { /* ANSI_VAR_FONT */
5714 { DEFAULT_CHARSET, FW_NORMAL, 12, 13, 96, "MS Sans Serif" },
5715 { DEFAULT_CHARSET, FW_NORMAL, 12, 13, 120, "MS Sans Serif" },
5716 { 0 }
5718 { /* SYSTEM_FONT */
5719 { SHIFTJIS_CHARSET, FW_NORMAL, 18, 18, 96, "System" },
5720 { SHIFTJIS_CHARSET, FW_NORMAL, 22, 22, 120, "System" },
5721 { HANGEUL_CHARSET, FW_NORMAL, 16, 16, 96, "System" },
5722 { HANGEUL_CHARSET, FW_NORMAL, 20, 20, 120, "System" },
5723 { DEFAULT_CHARSET, FW_BOLD, 16, 16, 96, "System" },
5724 { DEFAULT_CHARSET, FW_BOLD, 20, 20, 120, "System" },
5725 { 0 }
5727 { /* DEVICE_DEFAULT_FONT */
5728 { SHIFTJIS_CHARSET, FW_NORMAL, 18, 18, 96, "System" },
5729 { SHIFTJIS_CHARSET, FW_NORMAL, 22, 22, 120, "System" },
5730 { HANGEUL_CHARSET, FW_NORMAL, 16, 16, 96, "System" },
5731 { HANGEUL_CHARSET, FW_NORMAL, 20, 20, 120, "System" },
5732 { DEFAULT_CHARSET, FW_BOLD, 16, 16, 96, "System" },
5733 { DEFAULT_CHARSET, FW_BOLD, 20, 20, 120, "System" },
5734 { 0 }
5736 { /* DEFAULT_GUI_FONT */
5737 { SHIFTJIS_CHARSET, FW_NORMAL, -12, 15, 96, "?MS UI Gothic" },
5738 { SHIFTJIS_CHARSET, FW_NORMAL, -15, 18, 120, "?MS UI Gothic" },
5739 { HANGEUL_CHARSET, FW_NORMAL, -12, 15, 96, "?Gulim" },
5740 { HANGEUL_CHARSET, FW_NORMAL, -15, 18, 120, "?Gulim" },
5741 { GB2312_CHARSET, FW_NORMAL, -12, 15, 96, "?SimHei" },
5742 { GB2312_CHARSET, FW_NORMAL, -15, 18, 120, "?SimHei" },
5743 { CHINESEBIG5_CHARSET, FW_NORMAL, -12, 15, 96, "?MingLiU" },
5744 { CHINESEBIG5_CHARSET, FW_NORMAL, -15, 18, 120, "?MingLiU" },
5745 { DEFAULT_CHARSET, FW_NORMAL, -11, 13, 96, "MS Shell Dlg" },
5746 { DEFAULT_CHARSET, FW_NORMAL, -13, 16, 120, "MS Shell Dlg" },
5747 { 0 }
5750 int i, j;
5752 for (i = 0; i < sizeof(font)/sizeof(font[0]); i++)
5754 HFONT hfont;
5755 LOGFONTA lf;
5756 int ret, height;
5758 hfont = GetStockObject(font[i]);
5759 ok(hfont != 0, "%d: GetStockObject(%d) failed\n", i, font[i]);
5761 ret = GetObjectA(hfont, sizeof(lf), &lf);
5762 if (ret != sizeof(lf))
5764 /* NT4 */
5765 win_skip("%d: GetObject returned %d instead of sizeof(LOGFONT)\n", i, ret);
5766 continue;
5769 for (j = 0; td[i][j].face_name[0] != 0; j++)
5771 if (lf.lfCharSet != td[i][j].charset && td[i][j].charset != DEFAULT_CHARSET)
5773 continue;
5776 ret = get_font_dpi(&lf, &height);
5777 if (ret != td[i][j].dpi)
5779 trace("%d(%d): font %s %d dpi doesn't match test data %d\n",
5780 i, j, lf.lfFaceName, ret, td[i][j].dpi);
5781 continue;
5784 /* FIXME: Remove once Wine is fixed */
5785 if (td[i][j].dpi != 96 &&
5786 /* MS Sans Serif for 120 dpi and higher should include 12 pixel bitmap set */
5787 ((!strcmp(td[i][j].face_name, "MS Sans Serif") && td[i][j].height == 12) ||
5788 /* System for 120 dpi and higher should include 20 pixel bitmap set */
5789 (!strcmp(td[i][j].face_name, "System") && td[i][j].height > 16)))
5790 todo_wine
5791 ok(height == td[i][j].height_pixels, "%d(%d): expected height %d, got %d\n", i, j, td[i][j].height_pixels, height);
5792 else
5793 ok(height == td[i][j].height_pixels, "%d(%d): expected height %d, got %d\n", i, j, td[i][j].height_pixels, height);
5795 ok(td[i][j].weight == lf.lfWeight, "%d(%d): expected lfWeight %d, got %d\n", i, j, td[i][j].weight, lf.lfWeight);
5796 ok(td[i][j].height == lf.lfHeight, "%d(%d): expected lfHeight %d, got %d\n", i, j, td[i][j].height, lf.lfHeight);
5797 if (td[i][j].face_name[0] == '?')
5799 /* Wine doesn't have this font, skip this case for now.
5800 Actually, the face name is localized on Windows and varies
5801 dpending on Windows versions (e.g. Japanese NT4 vs win2k). */
5802 trace("%d(%d): default gui font is %s\n", i, j, lf.lfFaceName);
5804 else
5806 ok(!strcmp(td[i][j].face_name, lf.lfFaceName), "%d(%d): expected lfFaceName %s, got %s\n", i, j, td[i][j].face_name, lf.lfFaceName);
5808 break;
5813 static void test_max_height(void)
5815 HDC hdc;
5816 LOGFONTA lf;
5817 HFONT hfont, hfont_old;
5818 TEXTMETRICA tm1, tm;
5819 BOOL r;
5820 LONG invalid_height[] = { -65536, -123456, 123456 };
5821 size_t i;
5823 memset(&tm1, 0, sizeof(tm1));
5824 memset(&lf, 0, sizeof(lf));
5825 strcpy(lf.lfFaceName, "Tahoma");
5826 lf.lfHeight = -1;
5828 hdc = GetDC(NULL);
5830 /* get 1 ppem value */
5831 hfont = CreateFontIndirectA(&lf);
5832 hfont_old = SelectObject(hdc, hfont);
5833 r = GetTextMetricsA(hdc, &tm1);
5834 ok(r, "GetTextMetrics failed\n");
5835 ok(tm1.tmHeight > 0, "expected a positive value, got %d\n", tm1.tmHeight);
5836 ok(tm1.tmAveCharWidth > 0, "expected a positive value, got %d\n", tm1.tmHeight);
5837 DeleteObject(SelectObject(hdc, hfont_old));
5839 /* test the largest value */
5840 lf.lfHeight = -((1 << 16) - 1);
5841 hfont = CreateFontIndirectA(&lf);
5842 hfont_old = SelectObject(hdc, hfont);
5843 memset(&tm, 0, sizeof(tm));
5844 r = GetTextMetricsA(hdc, &tm);
5845 ok(r, "GetTextMetrics failed\n");
5846 ok(tm.tmHeight > tm1.tmHeight,
5847 "expected greater than 1 ppem value (%d), got %d\n", tm1.tmHeight, tm.tmHeight);
5848 ok(tm.tmAveCharWidth > tm1.tmAveCharWidth,
5849 "expected greater than 1 ppem value (%d), got %d\n", tm1.tmAveCharWidth, tm.tmAveCharWidth);
5850 DeleteObject(SelectObject(hdc, hfont_old));
5852 /* test an invalid value */
5853 for (i = 0; i < sizeof(invalid_height)/sizeof(invalid_height[0]); i++) {
5854 lf.lfHeight = invalid_height[i];
5855 hfont = CreateFontIndirectA(&lf);
5856 hfont_old = SelectObject(hdc, hfont);
5857 memset(&tm, 0, sizeof(tm));
5858 r = GetTextMetricsA(hdc, &tm);
5859 ok(r, "GetTextMetrics failed\n");
5860 ok(tm.tmHeight == tm1.tmHeight,
5861 "expected 1 ppem value (%d), got %d\n", tm1.tmHeight, tm.tmHeight);
5862 ok(tm.tmAveCharWidth == tm1.tmAveCharWidth,
5863 "expected 1 ppem value (%d), got %d\n", tm1.tmAveCharWidth, tm.tmAveCharWidth);
5864 DeleteObject(SelectObject(hdc, hfont_old));
5867 ReleaseDC(NULL, hdc);
5868 return;
5871 static void test_vertical_order(void)
5873 struct enum_font_data efd;
5874 LOGFONTA lf;
5875 HDC hdc;
5876 int i, j;
5878 hdc = CreateCompatibleDC(0);
5879 ok(hdc != NULL, "CreateCompatibleDC failed\n");
5881 memset(&lf, 0, sizeof(lf));
5882 lf.lfCharSet = DEFAULT_CHARSET;
5883 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
5884 lf.lfHeight = 16;
5885 lf.lfWidth = 16;
5886 lf.lfQuality = DEFAULT_QUALITY;
5887 lf.lfItalic = FALSE;
5888 lf.lfWeight = FW_DONTCARE;
5889 efd.total = 0;
5890 EnumFontFamiliesExA(hdc, &lf, enum_font_data_proc, (LPARAM)&efd, 0);
5891 for (i = 0; i < efd.total; i++)
5893 if (efd.lf[i].lfFaceName[0] != '@') continue;
5894 for (j = 0; j < efd.total; j++)
5896 if (!strcmp(efd.lf[i].lfFaceName + 1, efd.lf[j].lfFaceName))
5898 ok(i > j,"Found vertical font %s before its horizontal version\n", efd.lf[i].lfFaceName);
5899 break;
5903 DeleteDC( hdc );
5906 static void test_GetCharWidth32(void)
5908 BOOL ret;
5909 HDC hdc;
5910 LOGFONTA lf;
5911 HFONT hfont;
5912 INT bufferA;
5913 INT bufferW;
5914 HWND hwnd;
5916 if (!pGetCharWidth32A || !pGetCharWidth32W)
5918 win_skip("GetCharWidth32A/W not available on this platform\n");
5919 return;
5922 memset(&lf, 0, sizeof(lf));
5923 strcpy(lf.lfFaceName, "System");
5924 lf.lfHeight = 20;
5926 hfont = CreateFontIndirectA(&lf);
5927 hdc = GetDC(0);
5928 hfont = SelectObject(hdc, hfont);
5930 ret = pGetCharWidth32W(hdc, 'a', 'a', &bufferW);
5931 ok(ret, "GetCharWidth32W should have succeeded\n");
5932 ret = pGetCharWidth32A(hdc, 'a', 'a', &bufferA);
5933 ok(ret, "GetCharWidth32A should have succeeded\n");
5934 ok (bufferA == bufferW, "Widths should be the same\n");
5935 ok (bufferA > 0," Width should be greater than zero\n");
5937 hfont = SelectObject(hdc, hfont);
5938 DeleteObject(hfont);
5939 ReleaseDC(NULL, hdc);
5941 memset(&lf, 0, sizeof(lf));
5942 strcpy(lf.lfFaceName, "Tahoma");
5943 lf.lfHeight = 20;
5945 hfont = CreateFontIndirectA(&lf);
5946 hwnd = CreateWindowExA(0, "static", "", WS_POPUP, 0,0,100,100,
5947 0, 0, 0, NULL);
5948 hdc = GetDC(hwnd);
5949 SetMapMode( hdc, MM_ANISOTROPIC );
5950 SelectObject(hdc, hfont);
5952 ret = pGetCharWidth32W(hdc, 'a', 'a', &bufferW);
5953 ok(ret, "GetCharWidth32W should have succeeded\n");
5954 ok (bufferW > 0," Width should be greater than zero\n");
5955 SetWindowExtEx(hdc, -1,-1,NULL);
5956 SetGraphicsMode(hdc, GM_COMPATIBLE);
5957 ret = pGetCharWidth32W(hdc, 'a', 'a', &bufferW);
5958 ok(ret, "GetCharWidth32W should have succeeded\n");
5959 ok (bufferW > 0," Width should be greater than zero\n");
5960 SetGraphicsMode(hdc, GM_ADVANCED);
5961 ret = pGetCharWidth32W(hdc, 'a', 'a', &bufferW);
5962 ok(ret, "GetCharWidth32W should have succeeded\n");
5963 todo_wine ok (bufferW > 0," Width should be greater than zero\n");
5964 SetWindowExtEx(hdc, 1,1,NULL);
5965 SetGraphicsMode(hdc, GM_COMPATIBLE);
5966 ret = pGetCharWidth32W(hdc, 'a', 'a', &bufferW);
5967 ok(ret, "GetCharWidth32W should have succeeded\n");
5968 ok (bufferW > 0," Width should be greater than zero\n");
5969 SetGraphicsMode(hdc, GM_ADVANCED);
5970 ret = pGetCharWidth32W(hdc, 'a', 'a', &bufferW);
5971 ok(ret, "GetCharWidth32W should have succeeded\n");
5972 ok (bufferW > 0," Width should be greater than zero\n");
5974 ReleaseDC(hwnd, hdc);
5975 DestroyWindow(hwnd);
5977 hwnd = CreateWindowExA(WS_EX_LAYOUTRTL, "static", "", WS_POPUP, 0,0,100,100,
5978 0, 0, 0, NULL);
5979 hdc = GetDC(hwnd);
5980 SetMapMode( hdc, MM_ANISOTROPIC );
5981 SelectObject(hdc, hfont);
5983 ret = pGetCharWidth32W(hdc, 'a', 'a', &bufferW);
5984 ok(ret, "GetCharWidth32W should have succeeded\n");
5985 ok (bufferW > 0," Width should be greater than zero\n");
5986 SetWindowExtEx(hdc, -1,-1,NULL);
5987 SetGraphicsMode(hdc, GM_COMPATIBLE);
5988 ret = pGetCharWidth32W(hdc, 'a', 'a', &bufferW);
5989 ok(ret, "GetCharWidth32W should have succeeded\n");
5990 ok (bufferW > 0," Width should be greater than zero\n");
5991 SetGraphicsMode(hdc, GM_ADVANCED);
5992 ret = pGetCharWidth32W(hdc, 'a', 'a', &bufferW);
5993 ok(ret, "GetCharWidth32W should have succeeded\n");
5994 ok (bufferW > 0," Width should be greater than zero\n");
5995 SetWindowExtEx(hdc, 1,1,NULL);
5996 SetGraphicsMode(hdc, GM_COMPATIBLE);
5997 ret = pGetCharWidth32W(hdc, 'a', 'a', &bufferW);
5998 ok(ret, "GetCharWidth32W should have succeeded\n");
5999 ok (bufferW > 0," Width should be greater than zero\n");
6000 SetGraphicsMode(hdc, GM_ADVANCED);
6001 ret = pGetCharWidth32W(hdc, 'a', 'a', &bufferW);
6002 ok(ret, "GetCharWidth32W should have succeeded\n");
6003 todo_wine ok (bufferW > 0," Width should be greater than zero\n");
6005 ReleaseDC(hwnd, hdc);
6006 DestroyWindow(hwnd);
6007 DeleteObject(hfont);
6010 static void test_fake_bold_font(void)
6012 HDC hdc;
6013 HFONT hfont, hfont_old;
6014 LOGFONTA lf;
6015 BOOL ret;
6016 TEXTMETRICA tm[2];
6017 ABC abc[2];
6018 INT w[2];
6020 if (!pGetCharWidth32A || !pGetCharABCWidthsA) {
6021 win_skip("GetCharWidth32A/GetCharABCWidthA is not available on this platform\n");
6022 return;
6025 /* Test outline font */
6026 memset(&lf, 0, sizeof(lf));
6027 strcpy(lf.lfFaceName, "Wingdings");
6028 lf.lfWeight = FW_NORMAL;
6029 lf.lfCharSet = SYMBOL_CHARSET;
6030 hfont = CreateFontIndirectA(&lf);
6032 hdc = GetDC(NULL);
6033 hfont_old = SelectObject(hdc, hfont);
6035 /* base metrics */
6036 ret = GetTextMetricsA(hdc, &tm[0]);
6037 ok(ret, "got %d\n", ret);
6038 ret = pGetCharABCWidthsA(hdc, 0x76, 0x76, &abc[0]);
6039 ok(ret, "got %d\n", ret);
6041 lf.lfWeight = FW_BOLD;
6042 hfont = CreateFontIndirectA(&lf);
6043 DeleteObject(SelectObject(hdc, hfont));
6045 /* bold metrics */
6046 ret = GetTextMetricsA(hdc, &tm[1]);
6047 ok(ret, "got %d\n", ret);
6048 ret = pGetCharABCWidthsA(hdc, 0x76, 0x76, &abc[1]);
6049 ok(ret, "got %d\n", ret);
6051 DeleteObject(SelectObject(hdc, hfont_old));
6052 ReleaseDC(NULL, hdc);
6054 /* compare results (outline) */
6055 ok(tm[0].tmHeight == tm[1].tmHeight, "expected %d, got %d\n", tm[0].tmHeight, tm[1].tmHeight);
6056 ok(tm[0].tmAscent == tm[1].tmAscent, "expected %d, got %d\n", tm[0].tmAscent, tm[1].tmAscent);
6057 ok(tm[0].tmDescent == tm[1].tmDescent, "expected %d, got %d\n", tm[0].tmDescent, tm[1].tmDescent);
6058 ok((tm[0].tmAveCharWidth + 1) == tm[1].tmAveCharWidth,
6059 "expected %d, got %d\n", tm[0].tmAveCharWidth + 1, tm[1].tmAveCharWidth);
6060 ok((tm[0].tmMaxCharWidth + 1) == tm[1].tmMaxCharWidth,
6061 "expected %d, got %d\n", tm[0].tmMaxCharWidth + 1, tm[1].tmMaxCharWidth);
6062 ok(tm[0].tmOverhang == tm[1].tmOverhang, "expected %d, got %d\n", tm[0].tmOverhang, tm[1].tmOverhang);
6063 w[0] = abc[0].abcA + abc[0].abcB + abc[0].abcC;
6064 w[1] = abc[1].abcA + abc[1].abcB + abc[1].abcC;
6065 ok((w[0] + 1) == w[1], "expected %d, got %d\n", w[0] + 1, w[1]);
6069 static void test_bitmap_font_glyph_index(void)
6071 const WCHAR text[] = {'#','!','/','b','i','n','/','s','h',0};
6072 const struct {
6073 LPCSTR face;
6074 BYTE charset;
6075 } bitmap_font_list[] = {
6076 { "Courier", ANSI_CHARSET },
6077 { "Small Fonts", ANSI_CHARSET },
6078 { "Fixedsys", DEFAULT_CHARSET },
6079 { "System", DEFAULT_CHARSET }
6081 HDC hdc;
6082 LOGFONTA lf;
6083 HFONT hFont;
6084 CHAR facename[LF_FACESIZE];
6085 BITMAPINFO bmi;
6086 HBITMAP hBmp[2];
6087 void *pixels[2];
6088 int i, j;
6089 DWORD ret;
6090 BITMAP bmp;
6091 TEXTMETRICA tm;
6092 CHARSETINFO ci;
6093 BYTE chr = '\xA9';
6095 if (!pGetGlyphIndicesW || !pGetGlyphIndicesA) {
6096 win_skip("GetGlyphIndices is unavailable\n");
6097 return;
6100 hdc = CreateCompatibleDC(0);
6101 ok(hdc != NULL, "CreateCompatibleDC failed\n");
6103 memset(&bmi, 0, sizeof(bmi));
6104 bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
6105 bmi.bmiHeader.biBitCount = 32;
6106 bmi.bmiHeader.biPlanes = 1;
6107 bmi.bmiHeader.biWidth = 128;
6108 bmi.bmiHeader.biHeight = 32;
6109 bmi.bmiHeader.biCompression = BI_RGB;
6111 for (i = 0; i < sizeof(bitmap_font_list)/sizeof(bitmap_font_list[0]); i++) {
6112 memset(&lf, 0, sizeof(lf));
6113 lf.lfCharSet = bitmap_font_list[i].charset;
6114 strcpy(lf.lfFaceName, bitmap_font_list[i].face);
6115 hFont = CreateFontIndirectA(&lf);
6116 ok(hFont != NULL, "Can't create font (%s:%d)\n", lf.lfFaceName, lf.lfCharSet);
6117 hFont = SelectObject(hdc, hFont);
6118 ret = GetTextMetricsA(hdc, &tm);
6119 ok(ret, "GetTextMetric failed\n");
6120 ret = GetTextFaceA(hdc, sizeof(facename), facename);
6121 ok(ret, "GetTextFace failed\n");
6122 if (tm.tmPitchAndFamily & TMPF_TRUETYPE) {
6123 skip("TrueType font (%s) was selected for \"%s\"\n", facename, bitmap_font_list[i].face);
6124 continue;
6126 if (lstrcmpiA(facename, lf.lfFaceName) != 0) {
6127 skip("expected %s, got %s\n", lf.lfFaceName, facename);
6128 continue;
6131 for (j = 0; j < 2; j++) {
6132 HBITMAP hBmpPrev;
6133 hBmp[j] = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, &pixels[j], NULL, 0);
6134 ok(hBmp[j] != NULL, "Can't create DIB\n");
6135 hBmpPrev = SelectObject(hdc, hBmp[j]);
6136 switch (j) {
6137 case 0:
6138 ret = ExtTextOutW(hdc, 0, 0, 0, NULL, text, lstrlenW(text), NULL);
6139 break;
6140 case 1:
6142 int len = lstrlenW(text);
6143 LPWORD indices = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WORD));
6144 ret = pGetGlyphIndicesW(hdc, text, len, indices, 0);
6145 ok(ret, "GetGlyphIndices failed\n");
6146 ok(memcmp(indices, text, sizeof(WORD) * len) == 0,
6147 "Glyph indices and text are different for %s:%d\n", lf.lfFaceName, tm.tmCharSet);
6148 ret = ExtTextOutW(hdc, 0, 0, ETO_GLYPH_INDEX, NULL, indices, len, NULL);
6149 HeapFree(GetProcessHeap(), 0, indices);
6150 break;
6153 ok(ret, "ExtTextOutW failed\n");
6154 SelectObject(hdc, hBmpPrev);
6157 GetObjectA(hBmp[0], sizeof(bmp), &bmp);
6158 ok(memcmp(pixels[0], pixels[1], bmp.bmHeight * bmp.bmWidthBytes) == 0,
6159 "Images are different (%s:%d)\n", lf.lfFaceName, tm.tmCharSet);
6161 ret = TranslateCharsetInfo((LPDWORD)(DWORD_PTR)tm.tmCharSet, &ci, TCI_SRCCHARSET);
6162 if (!ret) {
6163 skip("Can't get charset info for (%s:%d)\n", lf.lfFaceName, tm.tmCharSet);
6164 goto next;
6166 if (IsDBCSLeadByteEx(ci.ciACP, chr)) {
6167 skip("High-ascii character is not defined in codepage %d\n", ci.ciACP);
6168 goto next;
6171 for (j = 0; j < 2; j++) {
6172 HBITMAP hBmpPrev;
6173 WORD code;
6174 hBmpPrev = SelectObject(hdc, hBmp[j]);
6175 switch (j) {
6176 case 0:
6177 ret = ExtTextOutA(hdc, 100, 0, 0, NULL, (LPCSTR)&chr, 1, NULL);
6178 break;
6179 case 1:
6180 ret = pGetGlyphIndicesA(hdc, (LPCSTR)&chr, 1, &code, 0);
6181 ok(ret, "GetGlyphIndices failed\n");
6182 ok(code == chr, "expected %02x, got %02x (%s:%d)\n", chr, code, lf.lfFaceName, tm.tmCharSet);
6183 ret = ExtTextOutA(hdc, 100, 0, ETO_GLYPH_INDEX, NULL, (LPCSTR)&code, 1, NULL);
6184 break;
6186 ok(ret, "ExtTextOutA failed\n");
6187 SelectObject(hdc, hBmpPrev);
6190 ok(memcmp(pixels[0], pixels[1], bmp.bmHeight * bmp.bmWidthBytes) == 0,
6191 "Images are different (%s:%d)\n", lf.lfFaceName, tm.tmCharSet);
6192 next:
6193 for (j = 0; j < 2; j++)
6194 DeleteObject(hBmp[j]);
6195 hFont = SelectObject(hdc, hFont);
6196 DeleteObject(hFont);
6199 DeleteDC(hdc);
6202 START_TEST(font)
6204 init();
6206 test_stock_fonts();
6207 test_logfont();
6208 test_bitmap_font();
6209 test_outline_font();
6210 test_bitmap_font_metrics();
6211 test_GdiGetCharDimensions();
6212 test_GetCharABCWidths();
6213 test_text_extents();
6214 test_GetGlyphIndices();
6215 test_GetKerningPairs();
6216 test_GetOutlineTextMetrics();
6217 test_SetTextJustification();
6218 test_font_charset();
6219 test_GdiGetCodePage();
6220 test_GetFontUnicodeRanges();
6221 test_nonexistent_font();
6222 test_orientation();
6223 test_height_selection();
6224 test_AddFontMemResource();
6225 test_EnumFonts();
6227 /* On Windows Arial has a lot of default charset aliases such as Arial Cyr,
6228 * I'd like to avoid them in this test.
6230 test_EnumFontFamilies("Arial Black", ANSI_CHARSET);
6231 test_EnumFontFamilies("Symbol", SYMBOL_CHARSET);
6232 if (is_truetype_font_installed("Arial Black") &&
6233 (is_truetype_font_installed("Symbol") || is_truetype_font_installed("Wingdings")))
6235 test_EnumFontFamilies("", ANSI_CHARSET);
6236 test_EnumFontFamilies("", SYMBOL_CHARSET);
6237 test_EnumFontFamilies("", DEFAULT_CHARSET);
6239 else
6240 skip("Arial Black or Symbol/Wingdings is not installed\n");
6241 test_EnumFontFamiliesEx_default_charset();
6242 test_GetTextMetrics();
6243 test_GdiRealizationInfo();
6244 test_GetTextFace();
6245 test_GetGlyphOutline();
6246 test_GetTextMetrics2("Tahoma", -11);
6247 test_GetTextMetrics2("Tahoma", -55);
6248 test_GetTextMetrics2("Tahoma", -110);
6249 test_GetTextMetrics2("Arial", -11);
6250 test_GetTextMetrics2("Arial", -55);
6251 test_GetTextMetrics2("Arial", -110);
6252 test_CreateFontIndirect();
6253 test_CreateFontIndirectEx();
6254 test_oemcharset();
6255 test_fullname();
6256 test_fullname2();
6257 test_east_asian_font_selection();
6258 test_max_height();
6259 test_vertical_order();
6260 test_GetCharWidth32();
6261 test_fake_bold_font();
6262 test_bitmap_font_glyph_index();
6264 /* These tests should be last test until RemoveFontResource
6265 * is properly implemented.
6267 test_vertical_font();
6268 test_CreateScalableFontResource();