msvcrt: Don't include MSVC 7.0+ exception functions in SOs for older DLLs.
[wine.git] / dlls / gdi32 / tests / font.c
blob117a14f1534cecc672d79b30e93e1f226507f6f8
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 static inline BOOL match_off_by_n(int a, int b, unsigned int n)
35 return abs(a - b) <= n;
37 #define match_off_by_1(a, b, exact) match_off_by_n((a), (b), (exact) ? 0 : 1)
38 #define near_match(a, b) match_off_by_n((a), (b), 6)
39 #define expect(expected, got) ok(got == expected, "Expected %.8x, got %.8x\n", expected, got)
41 static LONG (WINAPI *pGdiGetCharDimensions)(HDC hdc, LPTEXTMETRICW lptm, LONG *height);
42 static DWORD (WINAPI *pGdiGetCodePage)(HDC hdc);
43 static BOOL (WINAPI *pGetCharABCWidthsI)(HDC hdc, UINT first, UINT count, LPWORD glyphs, LPABC abc);
44 static BOOL (WINAPI *pGetCharABCWidthsA)(HDC hdc, UINT first, UINT last, LPABC abc);
45 static BOOL (WINAPI *pGetCharABCWidthsW)(HDC hdc, UINT first, UINT last, LPABC abc);
46 static BOOL (WINAPI *pGetCharABCWidthsFloatW)(HDC hdc, UINT first, UINT last, LPABCFLOAT abc);
47 static BOOL (WINAPI *pGetCharWidth32A)(HDC hdc, UINT first, UINT last, LPINT buffer);
48 static BOOL (WINAPI *pGetCharWidth32W)(HDC hdc, UINT first, UINT last, LPINT buffer);
49 static DWORD (WINAPI *pGetFontUnicodeRanges)(HDC hdc, LPGLYPHSET lpgs);
50 static DWORD (WINAPI *pGetGlyphIndicesA)(HDC hdc, LPCSTR lpstr, INT count, LPWORD pgi, DWORD flags);
51 static DWORD (WINAPI *pGetGlyphIndicesW)(HDC hdc, LPCWSTR lpstr, INT count, LPWORD pgi, DWORD flags);
52 static BOOL (WINAPI *pGetTextExtentExPointI)(HDC hdc, const WORD *indices, INT count, INT max_ext,
53 LPINT nfit, LPINT dxs, LPSIZE size );
54 static BOOL (WINAPI *pGdiRealizationInfo)(HDC hdc, DWORD *);
55 static HFONT (WINAPI *pCreateFontIndirectExA)(const ENUMLOGFONTEXDVA *);
56 static HANDLE (WINAPI *pAddFontMemResourceEx)(PVOID, DWORD, PVOID, DWORD *);
57 static BOOL (WINAPI *pRemoveFontMemResourceEx)(HANDLE);
58 static INT (WINAPI *pAddFontResourceExA)(LPCSTR, DWORD, PVOID);
59 static BOOL (WINAPI *pRemoveFontResourceExA)(LPCSTR, DWORD, PVOID);
60 static BOOL (WINAPI *pGetFontRealizationInfo)(HDC hdc, DWORD *);
61 static BOOL (WINAPI *pGetFontFileInfo)(DWORD, DWORD, void *, DWORD, DWORD *);
62 static BOOL (WINAPI *pGetFontFileData)(DWORD, DWORD, ULONGLONG, void *, DWORD);
64 static HMODULE hgdi32 = 0;
65 static const MAT2 mat = { {0,1}, {0,0}, {0,0}, {0,1} };
66 static WORD system_lang_id;
68 #ifdef WORDS_BIGENDIAN
69 #define GET_BE_WORD(x) (x)
70 #define GET_BE_DWORD(x) (x)
71 #else
72 #define GET_BE_WORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
73 #define GET_BE_DWORD(x) MAKELONG(GET_BE_WORD(HIWORD(x)), GET_BE_WORD(LOWORD(x)));
74 #endif
76 #define MS_MAKE_TAG(ch0, ch1, ch2, ch3) \
77 ((DWORD)(BYTE)(ch0) | ((DWORD)(BYTE)(ch1) << 8) | \
78 ((DWORD)(BYTE)(ch2) << 16) | ((DWORD)(BYTE)(ch3) << 24))
79 #define MS_OS2_TAG MS_MAKE_TAG('O','S','/','2')
80 #define MS_CMAP_TAG MS_MAKE_TAG('c','m','a','p')
81 #define MS_NAME_TAG MS_MAKE_TAG('n','a','m','e')
83 static void init(void)
85 hgdi32 = GetModuleHandleA("gdi32.dll");
87 pGdiGetCharDimensions = (void *)GetProcAddress(hgdi32, "GdiGetCharDimensions");
88 pGdiGetCodePage = (void *) GetProcAddress(hgdi32,"GdiGetCodePage");
89 pGetCharABCWidthsI = (void *)GetProcAddress(hgdi32, "GetCharABCWidthsI");
90 pGetCharABCWidthsA = (void *)GetProcAddress(hgdi32, "GetCharABCWidthsA");
91 pGetCharABCWidthsW = (void *)GetProcAddress(hgdi32, "GetCharABCWidthsW");
92 pGetCharABCWidthsFloatW = (void *)GetProcAddress(hgdi32, "GetCharABCWidthsFloatW");
93 pGetCharWidth32A = (void *)GetProcAddress(hgdi32, "GetCharWidth32A");
94 pGetCharWidth32W = (void *)GetProcAddress(hgdi32, "GetCharWidth32W");
95 pGetFontUnicodeRanges = (void *)GetProcAddress(hgdi32, "GetFontUnicodeRanges");
96 pGetGlyphIndicesA = (void *)GetProcAddress(hgdi32, "GetGlyphIndicesA");
97 pGetGlyphIndicesW = (void *)GetProcAddress(hgdi32, "GetGlyphIndicesW");
98 pGetTextExtentExPointI = (void *)GetProcAddress(hgdi32, "GetTextExtentExPointI");
99 pGdiRealizationInfo = (void *)GetProcAddress(hgdi32, "GdiRealizationInfo");
100 pCreateFontIndirectExA = (void *)GetProcAddress(hgdi32, "CreateFontIndirectExA");
101 pAddFontMemResourceEx = (void *)GetProcAddress(hgdi32, "AddFontMemResourceEx");
102 pRemoveFontMemResourceEx = (void *)GetProcAddress(hgdi32, "RemoveFontMemResourceEx");
103 pAddFontResourceExA = (void *)GetProcAddress(hgdi32, "AddFontResourceExA");
104 pRemoveFontResourceExA = (void *)GetProcAddress(hgdi32, "RemoveFontResourceExA");
105 pGetFontRealizationInfo = (void *)GetProcAddress(hgdi32, "GetFontRealizationInfo");
106 pGetFontFileInfo = (void *)GetProcAddress(hgdi32, "GetFontFileInfo");
107 pGetFontFileData = (void *)GetProcAddress(hgdi32, "GetFontFileData");
109 system_lang_id = PRIMARYLANGID(GetSystemDefaultLangID());
112 static inline void* __WINE_ALLOC_SIZE(1) heap_alloc(size_t size)
114 return HeapAlloc(GetProcessHeap(), 0, size);
117 static inline void* __WINE_ALLOC_SIZE(2) heap_realloc(void *mem, size_t size)
119 if (!mem) return heap_alloc(size);
120 return HeapReAlloc(GetProcessHeap(), 0, mem, size);
123 static inline BOOL heap_free(void *mem)
125 return HeapFree(GetProcessHeap(), 0, mem);
128 static INT CALLBACK is_truetype_font_installed_proc(const LOGFONTA *elf, const TEXTMETRICA *ntm, DWORD type, LPARAM lParam)
130 if (type != TRUETYPE_FONTTYPE) return 1;
132 return 0;
135 static BOOL is_truetype_font_installed(const char *name)
137 HDC hdc = GetDC(0);
138 BOOL ret = FALSE;
140 if (!EnumFontFamiliesA(hdc, name, is_truetype_font_installed_proc, 0))
141 ret = TRUE;
143 ReleaseDC(0, hdc);
144 return ret;
147 static INT CALLBACK is_font_installed_proc(const LOGFONTA *elf, const TEXTMETRICA *ntm, DWORD type, LPARAM lParam)
149 return 0;
152 static BOOL is_font_installed(const char *name)
154 HDC hdc = GetDC(0);
155 BOOL ret = FALSE;
157 if(!EnumFontFamiliesA(hdc, name, is_font_installed_proc, 0))
158 ret = TRUE;
160 ReleaseDC(0, hdc);
161 return ret;
164 static void *get_res_data(const char *fontname, DWORD *rsrc_size)
166 HRSRC rsrc;
167 void *rsrc_data;
169 rsrc = FindResourceA(GetModuleHandleA(NULL), fontname, (LPCSTR)RT_RCDATA);
170 if (!rsrc) return NULL;
172 rsrc_data = LockResource(LoadResource(GetModuleHandleA(NULL), rsrc));
173 if (!rsrc_data) return NULL;
175 *rsrc_size = SizeofResource(GetModuleHandleA(NULL), rsrc);
176 if (!*rsrc_size) return NULL;
178 return rsrc_data;
181 static BOOL write_tmp_file( const void *data, DWORD *size, char *tmp_name )
183 char tmp_path[MAX_PATH];
184 HANDLE hfile;
185 BOOL ret;
187 GetTempPathA(MAX_PATH, tmp_path);
188 GetTempFileNameA(tmp_path, "ttf", 0, tmp_name);
190 hfile = CreateFileA(tmp_name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
191 if (hfile == INVALID_HANDLE_VALUE) return FALSE;
193 ret = WriteFile(hfile, data, *size, size, NULL);
195 CloseHandle(hfile);
196 return ret;
199 static BOOL write_ttf_file(const char *fontname, char *tmp_name)
201 void *rsrc_data;
202 DWORD rsrc_size;
204 rsrc_data = get_res_data( fontname, &rsrc_size );
205 if (!rsrc_data) return FALSE;
207 return write_tmp_file( rsrc_data, &rsrc_size, tmp_name );
210 static void check_font(const char* test, const LOGFONTA* lf, HFONT hfont)
212 LOGFONTA getobj_lf;
213 int ret, minlen = 0;
215 if (!hfont)
216 return;
218 ret = GetObjectA(hfont, sizeof(getobj_lf), &getobj_lf);
219 /* NT4 tries to be clever and only returns the minimum length */
220 while (lf->lfFaceName[minlen] && minlen < LF_FACESIZE-1)
221 minlen++;
222 minlen += FIELD_OFFSET(LOGFONTA, lfFaceName) + 1;
223 ok(ret == sizeof(LOGFONTA) || ret == minlen, "%s: GetObject returned %d\n", test, ret);
224 ok(lf->lfHeight == getobj_lf.lfHeight ||
225 broken((SHORT)lf->lfHeight == getobj_lf.lfHeight), /* win9x */
226 "lfHeight: expect %08x got %08x\n", lf->lfHeight, getobj_lf.lfHeight);
227 ok(lf->lfWidth == getobj_lf.lfWidth ||
228 broken((SHORT)lf->lfWidth == getobj_lf.lfWidth), /* win9x */
229 "lfWidth: expect %08x got %08x\n", lf->lfWidth, getobj_lf.lfWidth);
230 ok(lf->lfEscapement == getobj_lf.lfEscapement ||
231 broken((SHORT)lf->lfEscapement == getobj_lf.lfEscapement), /* win9x */
232 "lfEscapement: expect %08x got %08x\n", lf->lfEscapement, getobj_lf.lfEscapement);
233 ok(lf->lfOrientation == getobj_lf.lfOrientation ||
234 broken((SHORT)lf->lfOrientation == getobj_lf.lfOrientation), /* win9x */
235 "lfOrientation: expect %08x got %08x\n", lf->lfOrientation, getobj_lf.lfOrientation);
236 ok(lf->lfWeight == getobj_lf.lfWeight ||
237 broken((SHORT)lf->lfWeight == getobj_lf.lfWeight), /* win9x */
238 "lfWeight: expect %08x got %08x\n", lf->lfWeight, getobj_lf.lfWeight);
239 ok(lf->lfItalic == getobj_lf.lfItalic, "lfItalic: expect %02x got %02x\n", lf->lfItalic, getobj_lf.lfItalic);
240 ok(lf->lfUnderline == getobj_lf.lfUnderline, "lfUnderline: expect %02x got %02x\n", lf->lfUnderline, getobj_lf.lfUnderline);
241 ok(lf->lfStrikeOut == getobj_lf.lfStrikeOut, "lfStrikeOut: expect %02x got %02x\n", lf->lfStrikeOut, getobj_lf.lfStrikeOut);
242 ok(lf->lfCharSet == getobj_lf.lfCharSet, "lfCharSet: expect %02x got %02x\n", lf->lfCharSet, getobj_lf.lfCharSet);
243 ok(lf->lfOutPrecision == getobj_lf.lfOutPrecision, "lfOutPrecision: expect %02x got %02x\n", lf->lfOutPrecision, getobj_lf.lfOutPrecision);
244 ok(lf->lfClipPrecision == getobj_lf.lfClipPrecision, "lfClipPrecision: expect %02x got %02x\n", lf->lfClipPrecision, getobj_lf.lfClipPrecision);
245 ok(lf->lfQuality == getobj_lf.lfQuality, "lfQuality: expect %02x got %02x\n", lf->lfQuality, getobj_lf.lfQuality);
246 ok(lf->lfPitchAndFamily == getobj_lf.lfPitchAndFamily, "lfPitchAndFamily: expect %02x got %02x\n", lf->lfPitchAndFamily, getobj_lf.lfPitchAndFamily);
247 ok(!lstrcmpA(lf->lfFaceName, getobj_lf.lfFaceName) ||
248 broken(!memcmp(lf->lfFaceName, getobj_lf.lfFaceName, LF_FACESIZE-1)), /* win9x doesn't ensure '\0' termination */
249 "%s: font names don't match: %s != %s\n", test, lf->lfFaceName, getobj_lf.lfFaceName);
252 static HFONT create_font(const char* test, const LOGFONTA* lf)
254 HFONT hfont = CreateFontIndirectA(lf);
255 ok(hfont != 0, "%s: CreateFontIndirect failed\n", test);
256 if (hfont)
257 check_font(test, lf, hfont);
258 return hfont;
261 static void test_logfont(void)
263 LOGFONTA lf;
264 HFONT hfont;
266 memset(&lf, 0, sizeof lf);
268 lf.lfCharSet = ANSI_CHARSET;
269 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
270 lf.lfWeight = FW_DONTCARE;
271 lf.lfHeight = 16;
272 lf.lfWidth = 16;
273 lf.lfQuality = DEFAULT_QUALITY;
275 lstrcpyA(lf.lfFaceName, "Arial");
276 hfont = create_font("Arial", &lf);
277 DeleteObject(hfont);
279 memset(&lf, 'A', sizeof(lf));
280 hfont = CreateFontIndirectA(&lf);
281 ok(hfont != 0, "CreateFontIndirectA with strange LOGFONT failed\n");
283 lf.lfFaceName[LF_FACESIZE - 1] = 0;
284 check_font("AAA...", &lf, hfont);
285 DeleteObject(hfont);
288 static INT CALLBACK font_enum_proc(const LOGFONTA *elf, const TEXTMETRICA *ntm, DWORD type, LPARAM lParam)
290 if (type & RASTER_FONTTYPE)
292 LOGFONTA *lf = (LOGFONTA *)lParam;
293 *lf = *elf;
294 return 0; /* stop enumeration */
297 return 1; /* continue enumeration */
300 static void compare_tm(const TEXTMETRICA *tm, const TEXTMETRICA *otm)
302 ok(tm->tmHeight == otm->tmHeight, "tmHeight %d != %d\n", tm->tmHeight, otm->tmHeight);
303 ok(tm->tmAscent == otm->tmAscent, "tmAscent %d != %d\n", tm->tmAscent, otm->tmAscent);
304 ok(tm->tmDescent == otm->tmDescent, "tmDescent %d != %d\n", tm->tmDescent, otm->tmDescent);
305 ok(tm->tmInternalLeading == otm->tmInternalLeading, "tmInternalLeading %d != %d\n", tm->tmInternalLeading, otm->tmInternalLeading);
306 ok(tm->tmExternalLeading == otm->tmExternalLeading, "tmExternalLeading %d != %d\n", tm->tmExternalLeading, otm->tmExternalLeading);
307 ok(tm->tmAveCharWidth == otm->tmAveCharWidth, "tmAveCharWidth %d != %d\n", tm->tmAveCharWidth, otm->tmAveCharWidth);
308 ok(tm->tmMaxCharWidth == otm->tmMaxCharWidth, "tmMaxCharWidth %d != %d\n", tm->tmMaxCharWidth, otm->tmMaxCharWidth);
309 ok(tm->tmWeight == otm->tmWeight, "tmWeight %d != %d\n", tm->tmWeight, otm->tmWeight);
310 ok(tm->tmOverhang == otm->tmOverhang, "tmOverhang %d != %d\n", tm->tmOverhang, otm->tmOverhang);
311 ok(tm->tmDigitizedAspectX == otm->tmDigitizedAspectX, "tmDigitizedAspectX %d != %d\n", tm->tmDigitizedAspectX, otm->tmDigitizedAspectX);
312 ok(tm->tmDigitizedAspectY == otm->tmDigitizedAspectY, "tmDigitizedAspectY %d != %d\n", tm->tmDigitizedAspectY, otm->tmDigitizedAspectY);
313 ok(tm->tmFirstChar == otm->tmFirstChar, "tmFirstChar %d != %d\n", tm->tmFirstChar, otm->tmFirstChar);
314 ok(tm->tmLastChar == otm->tmLastChar, "tmLastChar %d != %d\n", tm->tmLastChar, otm->tmLastChar);
315 ok(tm->tmDefaultChar == otm->tmDefaultChar, "tmDefaultChar %d != %d\n", tm->tmDefaultChar, otm->tmDefaultChar);
316 ok(tm->tmBreakChar == otm->tmBreakChar, "tmBreakChar %d != %d\n", tm->tmBreakChar, otm->tmBreakChar);
317 ok(tm->tmItalic == otm->tmItalic, "tmItalic %d != %d\n", tm->tmItalic, otm->tmItalic);
318 ok(tm->tmUnderlined == otm->tmUnderlined, "tmUnderlined %d != %d\n", tm->tmUnderlined, otm->tmUnderlined);
319 ok(tm->tmStruckOut == otm->tmStruckOut, "tmStruckOut %d != %d\n", tm->tmStruckOut, otm->tmStruckOut);
320 ok(tm->tmPitchAndFamily == otm->tmPitchAndFamily, "tmPitchAndFamily %d != %d\n", tm->tmPitchAndFamily, otm->tmPitchAndFamily);
321 ok(tm->tmCharSet == otm->tmCharSet, "tmCharSet %d != %d\n", tm->tmCharSet, otm->tmCharSet);
324 static void test_font_metrics(HDC hdc, HFONT hfont, LONG lfHeight,
325 LONG lfWidth, const char *test_str,
326 INT test_str_len, const TEXTMETRICA *tm_orig,
327 const SIZE *size_orig, INT width_of_A_orig,
328 INT scale_x, INT scale_y)
330 LOGFONTA lf;
331 OUTLINETEXTMETRICA otm;
332 TEXTMETRICA tm;
333 SIZE size;
334 INT width_of_A, cx, cy;
335 UINT ret;
337 if (!hfont)
338 return;
340 ok(GetCurrentObject(hdc, OBJ_FONT) == hfont, "hfont should be selected\n");
342 GetObjectA(hfont, sizeof(lf), &lf);
344 if (GetOutlineTextMetricsA(hdc, 0, NULL))
346 otm.otmSize = sizeof(otm) / 2;
347 ret = GetOutlineTextMetricsA(hdc, otm.otmSize, &otm);
348 ok(ret == sizeof(otm)/2 /* XP */ ||
349 ret == 1 /* Win9x */, "expected sizeof(otm)/2, got %u\n", ret);
351 memset(&otm, 0x1, sizeof(otm));
352 otm.otmSize = sizeof(otm);
353 ret = GetOutlineTextMetricsA(hdc, otm.otmSize, &otm);
354 ok(ret == sizeof(otm) /* XP */ ||
355 ret == 1 /* Win9x */, "expected sizeof(otm), got %u\n", ret);
357 memset(&tm, 0x2, sizeof(tm));
358 ret = GetTextMetricsA(hdc, &tm);
359 ok(ret, "GetTextMetricsA failed\n");
360 /* the structure size is aligned */
361 if (memcmp(&tm, &otm.otmTextMetrics, FIELD_OFFSET(TEXTMETRICA, tmCharSet) + 1))
363 ok(0, "tm != otm\n");
364 compare_tm(&tm, &otm.otmTextMetrics);
367 tm = otm.otmTextMetrics;
368 if (0) /* these metrics are scaled too, but with rounding errors */
370 ok(otm.otmAscent == tm.tmAscent, "ascent %d != %d\n", otm.otmAscent, tm.tmAscent);
371 ok(otm.otmDescent == -tm.tmDescent, "descent %d != %d\n", otm.otmDescent, -tm.tmDescent);
373 ok(otm.otmMacAscent == tm.tmAscent, "ascent %d != %d\n", otm.otmMacAscent, tm.tmAscent);
374 ok(otm.otmDescent < 0, "otm.otmDescent should be < 0\n");
375 ok(otm.otmMacDescent < 0, "otm.otmMacDescent should be < 0\n");
376 ok(tm.tmDescent > 0, "tm.tmDescent should be > 0\n");
377 ok(otm.otmMacDescent == -tm.tmDescent, "descent %d != %d\n", otm.otmMacDescent, -tm.tmDescent);
378 ok(otm.otmEMSquare == 2048, "expected 2048, got %d\n", otm.otmEMSquare);
380 else
382 ret = GetTextMetricsA(hdc, &tm);
383 ok(ret, "GetTextMetricsA failed\n");
386 cx = tm.tmAveCharWidth / tm_orig->tmAveCharWidth;
387 cy = tm.tmHeight / tm_orig->tmHeight;
388 ok(cx == scale_x && cy == scale_y, "height %d: expected scale_x %d, scale_y %d, got cx %d, cy %d\n",
389 lfHeight, scale_x, scale_y, cx, cy);
390 ok(tm.tmHeight == tm_orig->tmHeight * scale_y, "height %d != %d\n", tm.tmHeight, tm_orig->tmHeight * scale_y);
391 ok(tm.tmAscent == tm_orig->tmAscent * scale_y, "ascent %d != %d\n", tm.tmAscent, tm_orig->tmAscent * scale_y);
392 ok(tm.tmDescent == tm_orig->tmDescent * scale_y, "descent %d != %d\n", tm.tmDescent, tm_orig->tmDescent * scale_y);
393 ok(near_match(tm.tmAveCharWidth, tm_orig->tmAveCharWidth * scale_x), "ave width %d != %d\n", tm.tmAveCharWidth, tm_orig->tmAveCharWidth * scale_x);
394 ok(near_match(tm.tmMaxCharWidth, tm_orig->tmMaxCharWidth * scale_x), "max width %d != %d\n", tm.tmMaxCharWidth, tm_orig->tmMaxCharWidth * scale_x);
396 ok(lf.lfHeight == lfHeight, "lfHeight %d != %d\n", lf.lfHeight, lfHeight);
397 if (lf.lfHeight)
399 if (lf.lfWidth)
400 ok(lf.lfWidth == tm.tmAveCharWidth, "lfWidth %d != tm %d\n", lf.lfWidth, tm.tmAveCharWidth);
402 else
403 ok(lf.lfWidth == lfWidth, "lfWidth %d != %d\n", lf.lfWidth, lfWidth);
405 GetTextExtentPoint32A(hdc, test_str, test_str_len, &size);
407 ok(near_match(size.cx, size_orig->cx * scale_x), "cx %d != %d\n", size.cx, size_orig->cx * scale_x);
408 ok(size.cy == size_orig->cy * scale_y, "cy %d != %d\n", size.cy, size_orig->cy * scale_y);
410 GetCharWidthA(hdc, 'A', 'A', &width_of_A);
412 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);
415 /* Test how GDI scales bitmap font metrics */
416 static void test_bitmap_font(void)
418 static const char test_str[11] = "Test String";
419 HDC hdc;
420 LOGFONTA bitmap_lf;
421 HFONT hfont, old_hfont;
422 TEXTMETRICA tm_orig;
423 SIZE size_orig;
424 INT ret, i, width_orig, height_orig, scale, lfWidth;
426 hdc = CreateCompatibleDC(0);
428 /* "System" has only 1 pixel size defined, otherwise the test breaks */
429 ret = EnumFontFamiliesA(hdc, "System", font_enum_proc, (LPARAM)&bitmap_lf);
430 if (ret)
432 ReleaseDC(0, hdc);
433 trace("no bitmap fonts were found, skipping the test\n");
434 return;
437 trace("found bitmap font %s, height %d\n", bitmap_lf.lfFaceName, bitmap_lf.lfHeight);
439 height_orig = bitmap_lf.lfHeight;
440 lfWidth = bitmap_lf.lfWidth;
442 hfont = create_font("bitmap", &bitmap_lf);
443 old_hfont = SelectObject(hdc, hfont);
444 ok(GetTextMetricsA(hdc, &tm_orig), "GetTextMetricsA failed\n");
445 ok(GetTextExtentPoint32A(hdc, test_str, sizeof(test_str), &size_orig), "GetTextExtentPoint32A failed\n");
446 ok(GetCharWidthA(hdc, 'A', 'A', &width_orig), "GetCharWidthA failed\n");
447 SelectObject(hdc, old_hfont);
448 DeleteObject(hfont);
450 bitmap_lf.lfHeight = 0;
451 bitmap_lf.lfWidth = 4;
452 hfont = create_font("bitmap", &bitmap_lf);
453 old_hfont = SelectObject(hdc, hfont);
454 test_font_metrics(hdc, hfont, 0, 4, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 1, 1);
455 SelectObject(hdc, old_hfont);
456 DeleteObject(hfont);
458 bitmap_lf.lfHeight = height_orig;
459 bitmap_lf.lfWidth = lfWidth;
461 /* test fractional scaling */
462 for (i = 1; i <= height_orig * 6; i++)
464 INT nearest_height;
466 bitmap_lf.lfHeight = i;
467 hfont = create_font("fractional", &bitmap_lf);
468 scale = (i + height_orig - 1) / height_orig;
469 nearest_height = scale * height_orig;
470 /* Only jump to the next height if the difference <= 25% original height */
471 if (scale > 2 && nearest_height - i > height_orig / 4) scale--;
472 /* The jump between unscaled and doubled is delayed by 1 in winnt+ but not in win9x,
473 so we'll not test this particular height. */
474 else if(scale == 2 && nearest_height - i == (height_orig / 4)) continue;
475 else if(scale == 2 && nearest_height - i > (height_orig / 4 - 1)) scale--;
476 old_hfont = SelectObject(hdc, hfont);
477 test_font_metrics(hdc, hfont, bitmap_lf.lfHeight, 0, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 1, scale);
478 SelectObject(hdc, old_hfont);
479 DeleteObject(hfont);
482 /* test integer scaling 3x2 */
483 bitmap_lf.lfHeight = height_orig * 2;
484 bitmap_lf.lfWidth *= 3;
485 hfont = create_font("3x2", &bitmap_lf);
486 old_hfont = SelectObject(hdc, hfont);
487 test_font_metrics(hdc, hfont, bitmap_lf.lfHeight, 0, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 3, 2);
488 SelectObject(hdc, old_hfont);
489 DeleteObject(hfont);
491 /* test integer scaling 3x3 */
492 bitmap_lf.lfHeight = height_orig * 3;
493 bitmap_lf.lfWidth = 0;
494 hfont = create_font("3x3", &bitmap_lf);
495 old_hfont = SelectObject(hdc, hfont);
496 test_font_metrics(hdc, hfont, bitmap_lf.lfHeight, 0, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 3, 3);
497 SelectObject(hdc, old_hfont);
498 DeleteObject(hfont);
500 DeleteDC(hdc);
503 /* Test how GDI scales outline font metrics */
504 static void test_outline_font(void)
506 static const char test_str[11] = "Test String";
507 HDC hdc, hdc_2;
508 LOGFONTA lf;
509 HFONT hfont, old_hfont, old_hfont_2;
510 OUTLINETEXTMETRICA otm;
511 SIZE size_orig;
512 INT width_orig, height_orig, lfWidth;
513 XFORM xform;
514 GLYPHMETRICS gm;
515 MAT2 mat2 = { {0x8000,0}, {0,0}, {0,0}, {0x8000,0} };
516 POINT pt;
517 INT ret;
519 if (!is_truetype_font_installed("Arial"))
521 skip("Arial is not installed\n");
522 return;
525 hdc = CreateCompatibleDC(0);
527 memset(&lf, 0, sizeof(lf));
528 strcpy(lf.lfFaceName, "Arial");
529 lf.lfHeight = 72;
530 hfont = create_font("outline", &lf);
531 old_hfont = SelectObject(hdc, hfont);
532 otm.otmSize = sizeof(otm);
533 ok(GetOutlineTextMetricsA(hdc, sizeof(otm), &otm), "GetTextMetricsA failed\n");
534 ok(GetTextExtentPoint32A(hdc, test_str, sizeof(test_str), &size_orig), "GetTextExtentPoint32A failed\n");
535 ok(GetCharWidthA(hdc, 'A', 'A', &width_orig), "GetCharWidthA failed\n");
537 test_font_metrics(hdc, hfont, lf.lfHeight, otm.otmTextMetrics.tmAveCharWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
538 SelectObject(hdc, old_hfont);
539 DeleteObject(hfont);
541 /* font of otmEMSquare height helps to avoid a lot of rounding errors */
542 lf.lfHeight = otm.otmEMSquare;
543 lf.lfHeight = -lf.lfHeight;
544 hfont = create_font("outline", &lf);
545 old_hfont = SelectObject(hdc, hfont);
546 otm.otmSize = sizeof(otm);
547 ok(GetOutlineTextMetricsA(hdc, sizeof(otm), &otm), "GetTextMetricsA failed\n");
548 ok(GetTextExtentPoint32A(hdc, test_str, sizeof(test_str), &size_orig), "GetTextExtentPoint32A failed\n");
549 ok(GetCharWidthA(hdc, 'A', 'A', &width_orig), "GetCharWidthA failed\n");
550 SelectObject(hdc, old_hfont);
551 DeleteObject(hfont);
553 height_orig = otm.otmTextMetrics.tmHeight;
554 lfWidth = otm.otmTextMetrics.tmAveCharWidth;
556 /* test integer scaling 3x2 */
557 lf.lfHeight = height_orig * 2;
558 lf.lfWidth = lfWidth * 3;
559 hfont = create_font("3x2", &lf);
560 old_hfont = SelectObject(hdc, hfont);
561 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 3, 2);
562 SelectObject(hdc, old_hfont);
563 DeleteObject(hfont);
565 /* test integer scaling 3x3 */
566 lf.lfHeight = height_orig * 3;
567 lf.lfWidth = lfWidth * 3;
568 hfont = create_font("3x3", &lf);
569 old_hfont = SelectObject(hdc, hfont);
570 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 3, 3);
571 SelectObject(hdc, old_hfont);
572 DeleteObject(hfont);
574 /* test integer scaling 1x1 */
575 lf.lfHeight = height_orig * 1;
576 lf.lfWidth = lfWidth * 1;
577 hfont = create_font("1x1", &lf);
578 old_hfont = SelectObject(hdc, hfont);
579 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
580 SelectObject(hdc, old_hfont);
581 DeleteObject(hfont);
583 /* test integer scaling 1x1 */
584 lf.lfHeight = height_orig;
585 lf.lfWidth = 0;
586 hfont = create_font("1x1", &lf);
587 old_hfont = SelectObject(hdc, hfont);
588 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
590 /* with an identity matrix */
591 memset(&gm, 0, sizeof(gm));
592 SetLastError(0xdeadbeef);
593 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
594 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
595 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
596 ok(gm.gmCellIncX == width_orig, "incX %d != %d\n", gm.gmCellIncX, width_orig);
597 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
598 /* with a custom matrix */
599 memset(&gm, 0, sizeof(gm));
600 SetLastError(0xdeadbeef);
601 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat2);
602 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
603 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
604 ok(gm.gmCellIncX == width_orig/2, "incX %d != %d\n", gm.gmCellIncX, width_orig/2);
605 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
607 /* Test that changing the DC transformation affects only the font
608 * selected on this DC and doesn't affect the same font selected on
609 * another DC.
611 hdc_2 = CreateCompatibleDC(0);
612 old_hfont_2 = SelectObject(hdc_2, hfont);
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 SetMapMode(hdc, MM_ANISOTROPIC);
617 /* font metrics on another DC should be unchanged */
618 test_font_metrics(hdc_2, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
620 /* test restrictions of compatibility mode GM_COMPATIBLE */
621 /* part 1: rescaling only X should not change font scaling on screen.
622 So compressing the X axis by 2 is not done, and this
623 appears as X scaling of 2 that no one requested. */
624 SetWindowExtEx(hdc, 100, 100, NULL);
625 SetViewportExtEx(hdc, 50, 100, NULL);
626 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 2, 1);
627 /* font metrics on another DC should be unchanged */
628 test_font_metrics(hdc_2, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
630 /* part 2: rescaling only Y should change font scaling.
631 As also X is scaled by a factor of 2, but this is not
632 requested by the DC transformation, we get a scaling factor
633 of 2 in the X coordinate. */
634 SetViewportExtEx(hdc, 100, 200, NULL);
635 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 2, 1);
636 /* font metrics on another DC should be unchanged */
637 test_font_metrics(hdc_2, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
639 /* restore scaling */
640 SetMapMode(hdc, MM_TEXT);
642 /* font metrics on another DC should be unchanged */
643 test_font_metrics(hdc_2, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
645 SelectObject(hdc_2, old_hfont_2);
646 DeleteDC(hdc_2);
648 if (!SetGraphicsMode(hdc, GM_ADVANCED))
650 SelectObject(hdc, old_hfont);
651 DeleteObject(hfont);
652 DeleteDC(hdc);
653 skip("GM_ADVANCED is not supported on this platform\n");
654 return;
657 xform.eM11 = 20.0f;
658 xform.eM12 = 0.0f;
659 xform.eM21 = 0.0f;
660 xform.eM22 = 20.0f;
661 xform.eDx = 0.0f;
662 xform.eDy = 0.0f;
664 SetLastError(0xdeadbeef);
665 ret = SetWorldTransform(hdc, &xform);
666 ok(ret, "SetWorldTransform error %u\n", GetLastError());
668 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
670 /* with an identity matrix */
671 memset(&gm, 0, sizeof(gm));
672 SetLastError(0xdeadbeef);
673 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
674 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
675 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
676 pt.x = width_orig; pt.y = 0;
677 LPtoDP(hdc, &pt, 1);
678 ok(gm.gmCellIncX == pt.x, "incX %d != %d\n", gm.gmCellIncX, pt.x);
679 ok(gm.gmCellIncX == 20 * width_orig, "incX %d != %d\n", gm.gmCellIncX, 20 * width_orig);
680 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
681 /* with a custom matrix */
682 memset(&gm, 0, sizeof(gm));
683 SetLastError(0xdeadbeef);
684 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat2);
685 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
686 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
687 pt.x = width_orig; pt.y = 0;
688 LPtoDP(hdc, &pt, 1);
689 ok(gm.gmCellIncX == pt.x/2, "incX %d != %d\n", gm.gmCellIncX, pt.x/2);
690 ok(near_match(gm.gmCellIncX, 10 * width_orig), "incX %d != %d\n", gm.gmCellIncX, 10 * width_orig);
691 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
693 SetLastError(0xdeadbeef);
694 ret = SetMapMode(hdc, MM_LOMETRIC);
695 ok(ret == MM_TEXT, "expected MM_TEXT, got %d, error %u\n", ret, GetLastError());
697 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
699 /* with an identity matrix */
700 memset(&gm, 0, sizeof(gm));
701 SetLastError(0xdeadbeef);
702 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
703 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
704 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
705 pt.x = width_orig; pt.y = 0;
706 LPtoDP(hdc, &pt, 1);
707 ok(near_match(gm.gmCellIncX, pt.x), "incX %d != %d\n", gm.gmCellIncX, pt.x);
708 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
709 /* with a custom matrix */
710 memset(&gm, 0, sizeof(gm));
711 SetLastError(0xdeadbeef);
712 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat2);
713 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
714 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
715 pt.x = width_orig; pt.y = 0;
716 LPtoDP(hdc, &pt, 1);
717 ok(near_match(gm.gmCellIncX, (pt.x + 1)/2), "incX %d != %d\n", gm.gmCellIncX, (pt.x + 1)/2);
718 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
720 SetLastError(0xdeadbeef);
721 ret = SetMapMode(hdc, MM_TEXT);
722 ok(ret == MM_LOMETRIC, "expected MM_LOMETRIC, got %d, error %u\n", ret, GetLastError());
724 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
726 /* with an identity matrix */
727 memset(&gm, 0, sizeof(gm));
728 SetLastError(0xdeadbeef);
729 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
730 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
731 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
732 pt.x = width_orig; pt.y = 0;
733 LPtoDP(hdc, &pt, 1);
734 ok(gm.gmCellIncX == pt.x, "incX %d != %d\n", gm.gmCellIncX, pt.x);
735 ok(gm.gmCellIncX == 20 * width_orig, "incX %d != %d\n", gm.gmCellIncX, 20 * width_orig);
736 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
737 /* with a custom matrix */
738 memset(&gm, 0, sizeof(gm));
739 SetLastError(0xdeadbeef);
740 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat2);
741 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
742 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
743 pt.x = width_orig; pt.y = 0;
744 LPtoDP(hdc, &pt, 1);
745 ok(gm.gmCellIncX == pt.x/2, "incX %d != %d\n", gm.gmCellIncX, pt.x/2);
746 ok(gm.gmCellIncX == 10 * width_orig, "incX %d != %d\n", gm.gmCellIncX, 10 * width_orig);
747 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
749 SelectObject(hdc, old_hfont);
750 DeleteObject(hfont);
751 DeleteDC(hdc);
754 static INT CALLBACK find_font_proc(const LOGFONTA *elf, const TEXTMETRICA *ntm, DWORD type, LPARAM lParam)
756 LOGFONTA *lf = (LOGFONTA *)lParam;
758 if (elf->lfHeight == lf->lfHeight && !strcmp(elf->lfFaceName, lf->lfFaceName))
760 *lf = *elf;
761 return 0; /* stop enumeration */
763 return 1; /* continue enumeration */
766 static BOOL is_CJK(void)
768 return (system_lang_id == LANG_CHINESE || system_lang_id == LANG_JAPANESE || system_lang_id == LANG_KOREAN);
771 #define FH_SCALE 0x80000000
772 static void test_bitmap_font_metrics(void)
774 static const WORD skip_rtl[] = {LANG_ARABIC, LANG_HEBREW, 0};
775 static const struct font_data
777 const char face_name[LF_FACESIZE];
778 int weight, height, ascent, descent, int_leading, ext_leading;
779 int ave_char_width, max_char_width, dpi;
780 BYTE first_char, last_char, def_char, break_char;
781 DWORD ansi_bitfield;
782 const WORD *skip_lang_id;
783 int scaled_height;
784 } fd[] =
786 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 6, 11, 2, 2, 0, 5, 11, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2, skip_rtl, 13 },
787 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 6, 11, 2, 2, 0, 5, 11, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC, 0, 13 },
788 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 8, 11, 2, 2, 0, 5, 11, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2, skip_rtl, 13 },
789 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 8, 11, 2, 2, 0, 5, 11, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC, 0, 13 },
790 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 10, 11, 2, 2, 0, 5, 11, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2, skip_rtl, 13 },
791 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 10, 11, 2, 2, 0, 5, 11, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC, 0, 13 },
792 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 14, 11, 2, 2, 0, 5, 11, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2, skip_rtl, 13 },
793 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 14, 11, 2, 2, 0, 5, 11, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC, 0, 13 },
794 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 18, 13, 3, 3, 0, 7, 14, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2, skip_rtl, 16 },
795 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 18, 13, 3, 3, 0, 7, 14, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC, 0, 16 },
797 { "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 },
798 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 6, 13, 3, 3, 0, 7, 14, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC, 0, 16 },
799 { "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 },
800 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 8, 13, 3, 3, 0, 7, 14, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC, 0, 16 },
801 { "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 },
802 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 10, 13, 3, 3, 0, 7, 14, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC, 0, 16 },
803 { "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 },
804 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 14, 13, 3, 3, 0, 7, 14, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC, 0, 16 },
805 { "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 },
806 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 18, 13, 3, 3, 0, 7, 14, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC, 0, 16 },
808 { "MS Sans Serif", FW_NORMAL, 13, 11, 2, 2, 0, 5, 11, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2 },
809 { "MS Sans Serif", FW_NORMAL, 13, 11, 2, 2, 0, 5, 11, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
810 { "MS Sans Serif", FW_NORMAL, 16, 13, 3, 3, 0, 7, 14, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2 },
811 { "MS Sans Serif", FW_NORMAL, 16, 13, 3, 3, 0, 7, 14, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
812 { "MS Sans Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 16, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 },
813 { "MS Sans Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 18, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN2 },
814 { "MS Sans Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 16, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
815 { "MS Sans Serif", FW_NORMAL, 24, 19, 5, 6, 0, 9, 19, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 },
816 { "MS Sans Serif", FW_NORMAL, 24, 19, 5, 6, 0, 9, 24, 96, 0x20, 0xff, 0x81, 0x40, FS_LATIN2 },
817 { "MS Sans Serif", FW_NORMAL, 24, 19, 5, 6, 0, 9, 20, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
818 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 5, 0, 12, 24, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 },
819 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 6, 0, 12, 24, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN2 },
820 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 5, 0, 12, 25, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
821 { "MS Sans Serif", FW_NORMAL, 37, 29, 8, 5, 0, 16, 32, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 },
822 { "MS Sans Serif", FW_NORMAL, 37, 29, 8, 5, 0, 16, 32, 96, 0x20, 0xff, 0x81, 0x40, FS_LATIN2 },
823 { "MS Sans Serif", FW_NORMAL, 37, 29, 8, 5, 0, 16, 32, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
825 { "MS Sans Serif", FW_NORMAL, 16, 13, 3, 3, 0, 7, 14, 120, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2 },
826 { "MS Sans Serif", FW_NORMAL, 16, 13, 3, 3, 0, 7, 14, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
827 { "MS Sans Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 18, 120, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2 },
828 { "MS Sans Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 17, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
829 { "MS Sans Serif", FW_NORMAL, 25, 20, 5, 5, 0, 10, 21, 120, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2 },
830 { "MS Sans Serif", FW_NORMAL, 25, 20, 5, 5, 0, 10, 21, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
831 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 6, 0, 12, 24, 120, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2 },
832 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 5, 0, 12, 24, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
833 { "MS Sans Serif", FW_NORMAL, 36, 29, 7, 6, 0, 15, 30, 120, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2 },
834 { "MS Sans Serif", FW_NORMAL, 36, 29, 7, 6, 0, 15, 30, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
835 { "MS Sans Serif", FW_NORMAL, 46, 37, 9, 6, 0, 20, 40, 120, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2 },
836 { "MS Sans Serif", FW_NORMAL, 46, 37, 9, 6, 0, 20, 40, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
838 { "MS Serif", FW_NORMAL, 10, 8, 2, 2, 0, 4, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 },
839 { "MS Serif", FW_NORMAL, 10, 8, 2, 2, 0, 5, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
840 { "MS Serif", FW_NORMAL, 11, 9, 2, 2, 0, 5, 9, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
841 { "MS Serif", FW_NORMAL, 13, 11, 2, 2, 0, 5, 11, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 },
842 { "MS Serif", FW_NORMAL, 13, 11, 2, 2, 0, 5, 12, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
843 { "MS Serif", FW_NORMAL, 16, 13, 3, 3, 0, 6, 14, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 },
844 { "MS Serif", FW_NORMAL, 16, 13, 3, 3, 0, 6, 16, 96, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
845 { "MS Serif", FW_NORMAL, 19, 15, 4, 3, 0, 8, 18, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 },
846 { "MS Serif", FW_NORMAL, 19, 15, 4, 3, 0, 8, 19, 96, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
847 { "MS Serif", FW_NORMAL, 21, 16, 5, 3, 0, 9, 17, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 },
848 { "MS Serif", FW_NORMAL, 21, 16, 5, 3, 0, 9, 22, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 },
849 { "MS Serif", FW_NORMAL, 21, 16, 5, 3, 0, 9, 23, 96, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
850 { "MS Serif", FW_NORMAL, 27, 21, 6, 3, 0, 12, 23, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 },
851 { "MS Serif", FW_NORMAL, 27, 21, 6, 3, 0, 12, 26, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 },
852 { "MS Serif", FW_NORMAL, 27, 21, 6, 3, 0, 12, 27, 96, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
853 { "MS Serif", FW_NORMAL, 35, 27, 8, 3, 0, 16, 33, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 },
854 { "MS Serif", FW_NORMAL, 35, 27, 8, 3, 0, 16, 34, 96, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
856 { "MS Serif", FW_NORMAL, 16, 13, 3, 3, 0, 6, 14, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_CYRILLIC },
857 { "MS Serif", FW_NORMAL, 16, 13, 3, 3, 0, 6, 13, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 },
858 { "MS Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 18, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_CYRILLIC },
859 { "MS Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 15, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 },
860 { "MS Serif", FW_NORMAL, 23, 18, 5, 3, 0, 10, 21, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_CYRILLIC },
861 { "MS Serif", FW_NORMAL, 23, 18, 5, 3, 0, 10, 19, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 },
862 { "MS Serif", FW_NORMAL, 27, 21, 6, 4, 0, 12, 23, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 },
863 { "MS Serif", FW_MEDIUM, 27, 22, 5, 2, 0, 12, 30, 120, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
864 { "MS Serif", FW_NORMAL, 33, 26, 7, 3, 0, 14, 30, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 },
865 { "MS Serif", FW_MEDIUM, 32, 25, 7, 2, 0, 14, 32, 120, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
866 { "MS Serif", FW_NORMAL, 43, 34, 9, 3, 0, 19, 39, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
868 { "Courier", FW_NORMAL, 13, 11, 2, 0, 0, 8, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
869 { "Courier", FW_NORMAL, 16, 13, 3, 0, 0, 9, 9, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
870 { "Courier", FW_NORMAL, 20, 16, 4, 0, 0, 12, 12, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
872 { "Courier", FW_NORMAL, 16, 13, 3, 0, 0, 9, 9, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
873 { "Courier", FW_NORMAL, 20, 16, 4, 0, 0, 12, 12, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
874 { "Courier", FW_NORMAL, 25, 20, 5, 0, 0, 15, 15, 120, 0x20, 0xff, 0x40, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
876 { "System", FW_BOLD, 16, 13, 3, 3, 0, 7, 14, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 },
877 { "System", FW_BOLD, 16, 13, 3, 3, 0, 7, 15, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
878 { "System", FW_NORMAL, 18, 16, 2, 0, 2, 8, 16, 96, 0x20, 0xff, 0x80, 0x20, FS_JISJAPAN },
880 { "System", FW_BOLD, 20, 16, 4, 4, 0, 9, 14, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 },
881 { "System", FW_BOLD, 20, 16, 4, 4, 0, 9, 17, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
883 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 1, 2, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 },
884 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 1, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
885 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 2, 4, 96, 0x20, 0xff, 0x80, 0x20, FS_JISJAPAN },
886 { "Small Fonts", FW_NORMAL, 5, 4, 1, 1, 0, 3, 4, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1, skip_rtl},
887 { "Small Fonts", FW_NORMAL, 5, 4, 1, 1, 0, 2, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
888 { "Small Fonts", FW_NORMAL, 5, 4, 1, 0, 0, 3, 6, 96, 0x20, 0xff, 0x80, 0x20, FS_JISJAPAN },
889 { "Small Fonts", FW_NORMAL, 6, 5, 1, 1, 0, 3, 13, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1, skip_rtl},
890 { "Small Fonts", FW_NORMAL, 6, 5, 1, 1, 0, 3, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
891 { "Small Fonts", FW_NORMAL, 6, 5, 1, 1, 0, 3, 8, 96, 0x00, 0xff, 0x60, 0x00, FS_ARABIC },
892 { "Small Fonts", FW_NORMAL, 6, 5, 1, 0, 0, 4, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_JISJAPAN },
893 { "Small Fonts", FW_NORMAL, 8, 7, 1, 1, 0, 4, 7, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1, skip_rtl},
894 { "Small Fonts", FW_NORMAL, 8, 7, 1, 1, 0, 4, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
895 { "Small Fonts", FW_NORMAL, 8, 7, 1, 1, 0, 4, 8, 96, 0x00, 0xff, 0x60, 0x00, FS_ARABIC },
896 { "Small Fonts", FW_NORMAL, 8, 7, 1, 0, 0, 5, 10, 96, 0x20, 0xff, 0x80, 0x20, FS_JISJAPAN },
897 { "Small Fonts", FW_NORMAL, 10, 8, 2, 2, 0, 4, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2, skip_rtl},
898 { "Small Fonts", FW_NORMAL, 10, 8, 2, 2, 0, 5, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
899 { "Small Fonts", FW_NORMAL, 10, 8, 2, 2, 0, 4, 9, 96, 0x00, 0xff, 0x60, 0x00, FS_ARABIC },
900 { "Small Fonts", FW_NORMAL, 10, 8, 2, 0, 0, 6, 12, 96, 0x20, 0xff, 0x80, 0x20, FS_JISJAPAN },
901 { "Small Fonts", FW_NORMAL, 11, 9, 2, 2, 0, 5, 9, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC, skip_rtl},
902 { "Small Fonts", FW_NORMAL, 11, 9, 2, 2, 0, 4, 10, 96, 0x00, 0xff, 0x60, 0x00, FS_ARABIC },
903 { "Small Fonts", FW_NORMAL, 11, 9, 2, 0, 0, 7, 14, 96, 0x20, 0xff, 0x80, 0x20, FS_JISJAPAN },
905 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 1, 2, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_JISJAPAN },
906 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 1, 8, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
907 { "Small Fonts", FW_NORMAL, 6, 5, 1, 1, 0, 3, 5, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_JISJAPAN },
908 { "Small Fonts", FW_NORMAL, 6, 5, 1, 1, 0, 3, 8, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
909 { "Small Fonts", FW_NORMAL, 8, 7, 1, 1, 0, 4, 7, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_JISJAPAN },
910 { "Small Fonts", FW_NORMAL, 8, 7, 1, 1, 0, 4, 8, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
911 { "Small Fonts", FW_NORMAL, 10, 8, 2, 2, 0, 5, 9, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_JISJAPAN },
912 { "Small Fonts", FW_NORMAL, 10, 8, 2, 2, 0, 5, 8, 120, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
913 { "Small Fonts", FW_NORMAL, 12, 10, 2, 2, 0, 5, 10, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_JISJAPAN },
914 { "Small Fonts", FW_NORMAL, 12, 10, 2, 2, 0, 6, 10, 120, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
915 { "Small Fonts", FW_NORMAL, 13, 11, 2, 2, 0, 6, 12, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_JISJAPAN },
916 { "Small Fonts", FW_NORMAL, 13, 11, 2, 2, 0, 6, 11, 120, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
918 { "Fixedsys", FW_NORMAL, 15, 12, 3, 3, 0, 8, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 },
919 { "Fixedsys", FW_NORMAL, 16, 12, 4, 3, 0, 8, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
920 { "FixedSys", FW_NORMAL, 18, 16, 2, 0, 0, 8, 16, 96, 0x20, 0xff, 0xa0, 0x20, FS_JISJAPAN },
922 { "Fixedsys", FW_NORMAL, 20, 16, 4, 2, 0, 10, 10, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC }
924 /* FIXME: add "Terminal" */
926 static const int font_log_pixels[] = { 96, 120 };
927 HDC hdc;
928 LOGFONTA lf;
929 HFONT hfont, old_hfont;
930 TEXTMETRICA tm;
931 INT ret, i, expected_cs, screen_log_pixels, diff, font_res;
932 char face_name[LF_FACESIZE];
933 CHARSETINFO csi;
935 trace("system language id %04x\n", system_lang_id);
937 expected_cs = GetACP();
938 if (!TranslateCharsetInfo(ULongToPtr(expected_cs), &csi, TCI_SRCCODEPAGE))
940 skip("TranslateCharsetInfo failed for code page %d\n", expected_cs);
941 return;
943 expected_cs = csi.ciCharset;
944 trace("ACP %d -> charset %d\n", GetACP(), expected_cs);
946 hdc = CreateCompatibleDC(0);
947 ok(hdc != NULL, "failed to create hdc\n");
949 trace("logpixelsX %d, logpixelsY %d\n", GetDeviceCaps(hdc, LOGPIXELSX),
950 GetDeviceCaps(hdc, LOGPIXELSY));
952 screen_log_pixels = GetDeviceCaps(hdc, LOGPIXELSY);
953 diff = 32768;
954 font_res = 0;
955 for (i = 0; i < sizeof(font_log_pixels)/sizeof(font_log_pixels[0]); i++)
957 int new_diff = abs(font_log_pixels[i] - screen_log_pixels);
958 if (new_diff < diff)
960 diff = new_diff;
961 font_res = font_log_pixels[i];
964 trace("best font resolution is %d\n", font_res);
966 for (i = 0; i < sizeof(fd)/sizeof(fd[0]); i++)
968 int bit, height;
970 memset(&lf, 0, sizeof(lf));
972 height = fd[i].height & ~FH_SCALE;
973 lf.lfHeight = height;
974 strcpy(lf.lfFaceName, fd[i].face_name);
976 for(bit = 0; bit < 32; bit++)
978 GLYPHMETRICS gm;
979 DWORD fs[2];
980 BOOL bRet;
982 fs[0] = 1L << bit;
983 fs[1] = 0;
984 if((fd[i].ansi_bitfield & fs[0]) == 0) continue;
985 if(!TranslateCharsetInfo( fs, &csi, TCI_SRCFONTSIG )) continue;
987 lf.lfCharSet = csi.ciCharset;
988 ret = EnumFontFamiliesExA(hdc, &lf, find_font_proc, (LPARAM)&lf, 0);
989 if (fd[i].height & FH_SCALE)
990 ok(ret, "scaled font height %d should not be enumerated\n", height);
991 else
993 if (font_res == fd[i].dpi && lf.lfCharSet == expected_cs)
995 todo_wine_if (ret) /* FIXME: Remove once Wine is fixed */
996 ok(!ret, "%s height %d charset %d dpi %d should be enumerated\n", lf.lfFaceName, lf.lfHeight, lf.lfCharSet, fd[i].dpi);
999 if (ret && !(fd[i].height & FH_SCALE))
1000 continue;
1002 hfont = create_font(lf.lfFaceName, &lf);
1003 old_hfont = SelectObject(hdc, hfont);
1005 SetLastError(0xdeadbeef);
1006 ret = GetTextFaceA(hdc, sizeof(face_name), face_name);
1007 ok(ret, "GetTextFace error %u\n", GetLastError());
1009 if (strcmp(face_name, fd[i].face_name) != 0)
1011 ok(ret != ANSI_CHARSET, "font charset should not be ANSI_CHARSET\n");
1012 ok(ret != expected_cs, "font charset %d should not be %d\n", ret, expected_cs);
1013 SelectObject(hdc, old_hfont);
1014 DeleteObject(hfont);
1015 continue;
1018 memset(&gm, 0, sizeof(gm));
1019 SetLastError(0xdeadbeef);
1020 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
1021 todo_wine {
1022 ok(ret == GDI_ERROR, "GetGlyphOutline should fail for a bitmap font\n");
1023 ok(GetLastError() == ERROR_CAN_NOT_COMPLETE, "expected ERROR_CAN_NOT_COMPLETE, got %u\n", GetLastError());
1026 bRet = GetTextMetricsA(hdc, &tm);
1027 ok(bRet, "GetTextMetrics error %d\n", GetLastError());
1029 SetLastError(0xdeadbeef);
1030 ret = GetTextCharset(hdc);
1031 if (is_CJK() && lf.lfCharSet == ANSI_CHARSET)
1032 ok(ret == ANSI_CHARSET, "got charset %d, expected ANSI_CHARSETd\n", ret);
1033 else
1034 ok(ret == expected_cs, "got charset %d, expected %d\n", ret, expected_cs);
1036 trace("created %s, height %d charset %x dpi %d\n", face_name, tm.tmHeight, tm.tmCharSet, tm.tmDigitizedAspectX);
1037 trace("expected %s, height %d scaled_height %d, dpi %d\n", fd[i].face_name, height, fd[i].scaled_height, fd[i].dpi);
1039 if(fd[i].dpi == tm.tmDigitizedAspectX)
1041 int skipme = 0;
1042 trace("matched %s, height %d charset %x dpi %d\n", lf.lfFaceName, lf.lfHeight, lf.lfCharSet, fd[i].dpi);
1043 if (fd[i].skip_lang_id)
1045 int si = 0;
1046 skipme = 0;
1047 while(!skipme && fd[i].skip_lang_id[si])
1048 if (fd[i].skip_lang_id[si++] == system_lang_id)
1049 skipme = 1;
1051 if (!skipme)
1053 ok(tm.tmWeight == fd[i].weight, "%s(%d): tm.tmWeight %d != %d\n", fd[i].face_name, height, tm.tmWeight, fd[i].weight);
1054 if (fd[i].height & FH_SCALE)
1055 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);
1056 else
1057 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);
1058 ok(tm.tmAscent == fd[i].ascent, "%s(%d): tm.tmAscent %d != %d\n", fd[i].face_name, height, tm.tmAscent, fd[i].ascent);
1059 ok(tm.tmDescent == fd[i].descent, "%s(%d): tm.tmDescent %d != %d\n", fd[i].face_name, height, tm.tmDescent, fd[i].descent);
1060 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);
1061 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);
1062 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);
1063 ok(tm.tmFirstChar == fd[i].first_char, "%s(%d): tm.tmFirstChar = %02x\n", fd[i].face_name, height, tm.tmFirstChar);
1064 ok(tm.tmLastChar == fd[i].last_char, "%s(%d): tm.tmLastChar = %02x\n", fd[i].face_name, height, tm.tmLastChar);
1065 /* Substitutions like MS Sans Serif,0=MS Sans Serif,204
1066 make default char test fail */
1067 if (tm.tmCharSet == lf.lfCharSet)
1068 ok(tm.tmDefaultChar == fd[i].def_char, "%s(%d): tm.tmDefaultChar = %02x\n", fd[i].face_name, height, tm.tmDefaultChar);
1069 ok(tm.tmBreakChar == fd[i].break_char, "%s(%d): tm.tmBreakChar = %02x\n", fd[i].face_name, height, tm.tmBreakChar);
1070 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);
1072 /* Don't run the max char width test on System/ANSI_CHARSET. We have extra characters in our font
1073 that make the max width bigger */
1074 if ((strcmp(lf.lfFaceName, "System") || lf.lfCharSet != ANSI_CHARSET) && tm.tmDigitizedAspectX == 96)
1075 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);
1077 else
1078 skip("Skipping font metrics test for system langid 0x%x\n",
1079 system_lang_id);
1081 SelectObject(hdc, old_hfont);
1082 DeleteObject(hfont);
1086 DeleteDC(hdc);
1089 static void test_GdiGetCharDimensions(void)
1091 HDC hdc;
1092 TEXTMETRICW tm;
1093 LONG ret;
1094 SIZE size;
1095 LONG avgwidth, height;
1096 static const char szAlphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
1098 if (!pGdiGetCharDimensions)
1100 win_skip("GdiGetCharDimensions not available on this platform\n");
1101 return;
1104 hdc = CreateCompatibleDC(NULL);
1106 GetTextExtentPointA(hdc, szAlphabet, strlen(szAlphabet), &size);
1107 avgwidth = ((size.cx / 26) + 1) / 2;
1109 ret = pGdiGetCharDimensions(hdc, &tm, &height);
1110 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
1111 ok(height == tm.tmHeight, "GdiGetCharDimensions should have set height to %d instead of %d\n", tm.tmHeight, height);
1113 ret = pGdiGetCharDimensions(hdc, &tm, NULL);
1114 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
1116 ret = pGdiGetCharDimensions(hdc, NULL, NULL);
1117 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
1119 height = 0;
1120 ret = pGdiGetCharDimensions(hdc, NULL, &height);
1121 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
1122 ok(height == size.cy, "GdiGetCharDimensions should have set height to %d instead of %d\n", size.cy, height);
1124 DeleteDC(hdc);
1127 static int CALLBACK create_font_proc(const LOGFONTA *lpelfe,
1128 const TEXTMETRICA *lpntme,
1129 DWORD FontType, LPARAM lParam)
1131 if (FontType & TRUETYPE_FONTTYPE)
1133 HFONT hfont;
1135 hfont = CreateFontIndirectA(lpelfe);
1136 if (hfont)
1138 *(HFONT *)lParam = hfont;
1139 return 0;
1143 return 1;
1146 static void ABCWidths_helper(const char* description, HDC hdc, WORD *glyphs, ABC *base_abci, ABC *base_abcw, ABCFLOAT *base_abcf, INT todo)
1148 ABC abc[1];
1149 ABCFLOAT abcf[1];
1150 BOOL ret = FALSE;
1152 ret = pGetCharABCWidthsI(hdc, 0, 1, glyphs, abc);
1153 ok(ret, "%s: GetCharABCWidthsI should have succeeded\n", description);
1154 ok ((INT)abc->abcB > 0, "%s: abcB should be positive\n", description);
1155 todo_wine_if (todo)
1156 ok(abc->abcA * base_abci->abcA >= 0, "%s: abcA's sign should be unchanged\n", description);
1157 todo_wine_if (todo)
1158 ok(abc->abcC * base_abci->abcC >= 0, "%s: abcC's sign should be unchanged\n", description);
1160 ret = pGetCharABCWidthsW(hdc, 'i', 'i', abc);
1161 ok(ret, "%s: GetCharABCWidthsW should have succeeded\n", description);
1162 ok ((INT)abc->abcB > 0, "%s: abcB should be positive\n", description);
1163 todo_wine_if (todo)
1164 ok(abc->abcA * base_abcw->abcA >= 0, "%s: abcA's sign should be unchanged\n", description);
1165 todo_wine_if (todo)
1166 ok(abc->abcC * base_abcw->abcC >= 0, "%s: abcC's sign should be unchanged\n", description);
1168 ret = pGetCharABCWidthsFloatW(hdc, 'i', 'i', abcf);
1169 ok(ret, "%s: GetCharABCWidthsFloatW should have succeeded\n", description);
1170 ok (abcf->abcfB > 0.0, "%s: abcfB should be positive\n", description);
1171 todo_wine_if (todo)
1172 ok(abcf->abcfA * base_abcf->abcfA >= 0.0, "%s: abcfA's sign should be unchanged\n", description);
1173 todo_wine_if (todo)
1174 ok(abcf->abcfC * base_abcf->abcfC >= 0.0, "%s: abcfC's sign should be unchanged\n", description);
1177 static void test_GetCharABCWidths(void)
1179 static const WCHAR str[] = {'i',0};
1180 BOOL ret;
1181 HDC hdc;
1182 LOGFONTA lf;
1183 HFONT hfont;
1184 ABC abc[1];
1185 ABC abcw[1];
1186 ABCFLOAT abcf[1];
1187 WORD glyphs[1];
1188 DWORD nb;
1189 HWND hwnd;
1190 static const struct
1192 UINT first;
1193 UINT last;
1194 } range[] =
1196 {0xff, 0xff},
1197 {0x100, 0x100},
1198 {0xff, 0x100},
1199 {0x1ff, 0xff00},
1200 {0xffff, 0xffff},
1201 {0x10000, 0x10000},
1202 {0xffff, 0x10000},
1203 {0xffffff, 0xffffff},
1204 {0x1000000, 0x1000000},
1205 {0xffffff, 0x1000000},
1206 {0xffffffff, 0xffffffff},
1207 {0x00, 0xff}
1209 static const struct
1211 UINT cs;
1212 UINT a;
1213 UINT w;
1214 BOOL r[sizeof range / sizeof range[0]];
1215 } c[] =
1217 {ANSI_CHARSET, 0x30, 0x30,
1218 {TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE}},
1219 {SHIFTJIS_CHARSET, 0x82a0, 0x3042,
1220 {TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE}},
1221 {HANGEUL_CHARSET, 0x8141, 0xac02,
1222 {TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE}},
1223 {GB2312_CHARSET, 0x8141, 0x4e04,
1224 {TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE}},
1225 {CHINESEBIG5_CHARSET, 0xa142, 0x3001,
1226 {TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE}}
1228 UINT i;
1230 if (!pGetCharABCWidthsA || !pGetCharABCWidthsW || !pGetCharABCWidthsFloatW || !pGetCharABCWidthsI)
1232 win_skip("GetCharABCWidthsA/W/I not available on this platform\n");
1233 return;
1236 memset(&lf, 0, sizeof(lf));
1237 strcpy(lf.lfFaceName, "System");
1238 lf.lfHeight = 20;
1240 hfont = CreateFontIndirectA(&lf);
1241 hdc = GetDC(0);
1242 hfont = SelectObject(hdc, hfont);
1244 nb = pGetGlyphIndicesW(hdc, str, 1, glyphs, 0);
1245 ok(nb == 1, "GetGlyphIndicesW should have returned 1\n");
1247 ret = pGetCharABCWidthsI(NULL, 0, 1, glyphs, abc);
1248 ok(!ret, "GetCharABCWidthsI should have failed\n");
1250 ret = pGetCharABCWidthsI(hdc, 0, 1, glyphs, NULL);
1251 ok(!ret, "GetCharABCWidthsI should have failed\n");
1253 ret = pGetCharABCWidthsI(hdc, 0, 1, glyphs, abc);
1254 ok(ret, "GetCharABCWidthsI should have succeeded\n");
1256 ret = pGetCharABCWidthsW(NULL, 'a', 'a', abc);
1257 ok(!ret, "GetCharABCWidthsW should have failed\n");
1259 ret = pGetCharABCWidthsW(hdc, 'a', 'a', NULL);
1260 ok(!ret, "GetCharABCWidthsW should have failed\n");
1262 ret = pGetCharABCWidthsW(hdc, 'a', 'a', abc);
1263 ok(!ret, "GetCharABCWidthsW should have failed\n");
1265 ret = pGetCharABCWidthsFloatW(NULL, 'a', 'a', abcf);
1266 ok(!ret, "GetCharABCWidthsFloatW should have failed\n");
1268 ret = pGetCharABCWidthsFloatW(hdc, 'a', 'a', NULL);
1269 ok(!ret, "GetCharABCWidthsFloatW should have failed\n");
1271 ret = pGetCharABCWidthsFloatW(hdc, 'a', 'a', abcf);
1272 ok(ret, "GetCharABCWidthsFloatW should have succeeded\n");
1274 hfont = SelectObject(hdc, hfont);
1275 DeleteObject(hfont);
1277 for (i = 0; i < sizeof c / sizeof c[0]; ++i)
1279 ABC a[2], w[2];
1280 ABC full[256];
1281 UINT code = 0x41, j;
1283 lf.lfFaceName[0] = '\0';
1284 lf.lfCharSet = c[i].cs;
1285 lf.lfPitchAndFamily = 0;
1286 if (EnumFontFamiliesExA(hdc, &lf, create_font_proc, (LPARAM)&hfont, 0))
1288 skip("TrueType font for charset %u is not installed\n", c[i].cs);
1289 continue;
1292 memset(a, 0, sizeof a);
1293 memset(w, 0, sizeof w);
1294 hfont = SelectObject(hdc, hfont);
1295 ok(pGetCharABCWidthsA(hdc, c[i].a, c[i].a + 1, a) &&
1296 pGetCharABCWidthsW(hdc, c[i].w, c[i].w + 1, w) &&
1297 memcmp(a, w, sizeof a) == 0,
1298 "GetCharABCWidthsA and GetCharABCWidthsW should return same widths. charset = %u\n", c[i].cs);
1300 memset(a, 0xbb, sizeof a);
1301 ret = pGetCharABCWidthsA(hdc, code, code, a);
1302 ok(ret, "GetCharABCWidthsA should have succeeded\n");
1303 memset(full, 0xcc, sizeof full);
1304 ret = pGetCharABCWidthsA(hdc, 0x00, code, full);
1305 ok(ret, "GetCharABCWidthsA should have succeeded\n");
1306 ok(memcmp(&a[0], &full[code], sizeof(ABC)) == 0,
1307 "GetCharABCWidthsA info should match. codepage = %u\n", c[i].cs);
1309 for (j = 0; j < sizeof range / sizeof range[0]; ++j)
1311 memset(full, 0xdd, sizeof full);
1312 ret = pGetCharABCWidthsA(hdc, range[j].first, range[j].last, full);
1313 ok(ret == c[i].r[j], "GetCharABCWidthsA %x - %x should have %s\n",
1314 range[j].first, range[j].last, c[i].r[j] ? "succeeded" : "failed");
1315 if (ret)
1317 UINT last = range[j].last - range[j].first;
1318 ret = pGetCharABCWidthsA(hdc, range[j].last, range[j].last, a);
1319 ok(ret && memcmp(&full[last], &a[0], sizeof(ABC)) == 0,
1320 "GetCharABCWidthsA %x should match. codepage = %u\n",
1321 range[j].last, c[i].cs);
1325 hfont = SelectObject(hdc, hfont);
1326 DeleteObject(hfont);
1329 memset(&lf, 0, sizeof(lf));
1330 strcpy(lf.lfFaceName, "Tahoma");
1331 lf.lfHeight = 200;
1332 hfont = CreateFontIndirectA(&lf);
1334 /* test empty glyph's metrics */
1335 hfont = SelectObject(hdc, hfont);
1336 ret = pGetCharABCWidthsFloatW(hdc, ' ', ' ', abcf);
1337 ok(ret, "GetCharABCWidthsFloatW should have succeeded\n");
1338 ok(abcf[0].abcfB == 1.0, "got %f\n", abcf[0].abcfB);
1339 ret = pGetCharABCWidthsW(hdc, ' ', ' ', abcw);
1340 ok(ret, "GetCharABCWidthsW should have succeeded\n");
1341 ok(abcw[0].abcB == 1, "got %u\n", abcw[0].abcB);
1343 /* 1) prepare unrotated font metrics */
1344 ret = pGetCharABCWidthsW(hdc, 'a', 'a', abcw);
1345 ok(ret, "GetCharABCWidthsW should have succeeded\n");
1346 DeleteObject(SelectObject(hdc, hfont));
1348 /* 2) get rotated font metrics */
1349 lf.lfEscapement = lf.lfOrientation = 900;
1350 hfont = CreateFontIndirectA(&lf);
1351 hfont = SelectObject(hdc, hfont);
1352 ret = pGetCharABCWidthsW(hdc, 'a', 'a', abc);
1353 ok(ret, "GetCharABCWidthsW should have succeeded\n");
1355 /* 3) compare ABC results */
1356 ok(match_off_by_1(abcw[0].abcA, abc[0].abcA, FALSE),
1357 "got %d, expected %d (A)\n", abc[0].abcA, abcw[0].abcA);
1358 ok(match_off_by_1(abcw[0].abcB, abc[0].abcB, FALSE),
1359 "got %d, expected %d (B)\n", abc[0].abcB, abcw[0].abcB);
1360 ok(match_off_by_1(abcw[0].abcC, abc[0].abcC, FALSE),
1361 "got %d, expected %d (C)\n", abc[0].abcC, abcw[0].abcC);
1363 DeleteObject(SelectObject(hdc, hfont));
1364 ReleaseDC(NULL, hdc);
1366 trace("ABC sign test for a variety of transforms:\n");
1367 memset(&lf, 0, sizeof(lf));
1368 strcpy(lf.lfFaceName, "Tahoma");
1369 lf.lfHeight = 20;
1370 hfont = CreateFontIndirectA(&lf);
1371 hwnd = CreateWindowExA(0, "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 nb = pGetGlyphIndicesW(hdc, str, 1, glyphs, 0);
1378 ok(nb == 1, "GetGlyphIndicesW should have returned 1\n");
1380 ret = pGetCharABCWidthsI(hdc, 0, 1, glyphs, abc);
1381 ok(ret, "GetCharABCWidthsI should have succeeded\n");
1382 ret = pGetCharABCWidthsW(hdc, 'i', 'i', abcw);
1383 ok(ret, "GetCharABCWidthsW should have succeeded\n");
1384 ret = pGetCharABCWidthsFloatW(hdc, 'i', 'i', abcf);
1385 ok(ret, "GetCharABCWidthsFloatW should have succeeded\n");
1387 ABCWidths_helper("LTR", hdc, glyphs, abc, abcw, abcf, 0);
1388 SetWindowExtEx(hdc, -1, -1, NULL);
1389 SetGraphicsMode(hdc, GM_COMPATIBLE);
1390 ABCWidths_helper("LTR -1 compatible", hdc, glyphs, abc, abcw, abcf, 0);
1391 SetGraphicsMode(hdc, GM_ADVANCED);
1392 ABCWidths_helper("LTR -1 advanced", hdc, glyphs, abc, abcw, abcf, 1);
1393 SetWindowExtEx(hdc, 1, 1, NULL);
1394 SetGraphicsMode(hdc, GM_COMPATIBLE);
1395 ABCWidths_helper("LTR 1 compatible", hdc, glyphs, abc, abcw, abcf, 0);
1396 SetGraphicsMode(hdc, GM_ADVANCED);
1397 ABCWidths_helper("LTR 1 advanced", hdc, glyphs, abc, abcw, abcf, 0);
1399 ReleaseDC(hwnd, hdc);
1400 DestroyWindow(hwnd);
1402 trace("RTL layout\n");
1403 hwnd = CreateWindowExA(WS_EX_LAYOUTRTL, "static", "", WS_POPUP, 0,0,100,100,
1404 0, 0, 0, NULL);
1405 hdc = GetDC(hwnd);
1406 SetMapMode(hdc, MM_ANISOTROPIC);
1407 SelectObject(hdc, hfont);
1409 ABCWidths_helper("RTL", hdc, glyphs, abc, abcw, abcf, 0);
1410 SetWindowExtEx(hdc, -1, -1, NULL);
1411 SetGraphicsMode(hdc, GM_COMPATIBLE);
1412 ABCWidths_helper("RTL -1 compatible", hdc, glyphs, abc, abcw, abcf, 0);
1413 SetGraphicsMode(hdc, GM_ADVANCED);
1414 ABCWidths_helper("RTL -1 advanced", hdc, glyphs, abc, abcw, abcf, 0);
1415 SetWindowExtEx(hdc, 1, 1, NULL);
1416 SetGraphicsMode(hdc, GM_COMPATIBLE);
1417 ABCWidths_helper("RTL 1 compatible", hdc, glyphs, abc, abcw, abcf, 0);
1418 SetGraphicsMode(hdc, GM_ADVANCED);
1419 ABCWidths_helper("RTL 1 advanced", hdc, glyphs, abc, abcw, abcf, 1);
1421 ReleaseDC(hwnd, hdc);
1422 DestroyWindow(hwnd);
1423 DeleteObject(hfont);
1426 static void test_text_extents(void)
1428 static const WCHAR wt[] = {'O','n','e','\n','t','w','o',' ','3',0};
1429 static const WCHAR emptyW[] = {0};
1430 LPINT extents;
1431 INT i, len, fit1, fit2, extents2[3];
1432 LOGFONTA lf;
1433 TEXTMETRICA tm;
1434 HDC hdc;
1435 HFONT hfont;
1436 SIZE sz;
1437 SIZE sz1, sz2;
1438 BOOL ret;
1440 memset(&lf, 0, sizeof(lf));
1441 strcpy(lf.lfFaceName, "Arial");
1442 lf.lfHeight = 20;
1444 hfont = CreateFontIndirectA(&lf);
1445 hdc = GetDC(0);
1446 hfont = SelectObject(hdc, hfont);
1447 GetTextMetricsA(hdc, &tm);
1448 ret = GetTextExtentPointA(hdc, "o", 1, &sz);
1449 ok(ret, "got %d\n", ret);
1450 ok(sz.cy == tm.tmHeight, "cy %d tmHeight %d\n", sz.cy, tm.tmHeight);
1452 memset(&sz, 0xcc, sizeof(sz));
1453 ret = GetTextExtentPointA(hdc, "o", 0, &sz);
1454 ok(ret, "got %d\n", ret);
1455 ok(sz.cx == 0 && sz.cy == 0, "cx %d, cy %d\n", sz.cx, sz.cy);
1457 memset(&sz, 0xcc, sizeof(sz));
1458 ret = GetTextExtentPointA(hdc, "", 0, &sz);
1459 ok(ret, "got %d\n", ret);
1460 ok(sz.cx == 0 && sz.cy == 0, "cx %d, cy %d\n", sz.cx, sz.cy);
1462 SetLastError(0xdeadbeef);
1463 GetTextExtentExPointW(hdc, wt, 1, 1, &fit1, &fit2, &sz1);
1464 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
1466 win_skip("Skipping remainder of text extents test on a Win9x platform\n");
1467 hfont = SelectObject(hdc, hfont);
1468 DeleteObject(hfont);
1469 ReleaseDC(0, hdc);
1470 return;
1473 memset(&sz, 0xcc, sizeof(sz));
1474 ret = GetTextExtentPointW(hdc, wt, 0, &sz);
1475 ok(ret, "got %d\n", ret);
1476 ok(sz.cx == 0 && sz.cy == 0, "cx %d, cy %d\n", sz.cx, sz.cy);
1478 memset(&sz, 0xcc, sizeof(sz));
1479 ret = GetTextExtentPointW(hdc, emptyW, 0, &sz);
1480 ok(ret, "got %d\n", ret);
1481 ok(sz.cx == 0 && sz.cy == 0, "cx %d, cy %d\n", sz.cx, sz.cy);
1483 len = lstrlenW(wt);
1484 extents = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof extents[0]);
1485 extents[0] = 1; /* So that the increasing sequence test will fail
1486 if the extents array is untouched. */
1487 GetTextExtentExPointW(hdc, wt, len, 32767, &fit1, extents, &sz1);
1488 GetTextExtentPointW(hdc, wt, len, &sz2);
1489 ok(sz1.cy == sz2.cy,
1490 "cy from GetTextExtentExPointW (%d) and GetTextExtentPointW (%d) differ\n", sz1.cy, sz2.cy);
1491 /* Because of the '\n' in the string GetTextExtentExPoint and
1492 GetTextExtentPoint return different widths under Win2k, but
1493 under WinXP they return the same width. So we don't test that
1494 here. */
1496 for (i = 1; i < len; ++i)
1497 ok(extents[i-1] <= extents[i],
1498 "GetTextExtentExPointW generated a non-increasing sequence of partial extents (at position %d)\n",
1500 ok(extents[len-1] == sz1.cx, "GetTextExtentExPointW extents and size don't match\n");
1501 ok(0 <= fit1 && fit1 <= len, "GetTextExtentExPointW generated illegal value %d for fit\n", fit1);
1502 ok(0 < fit1, "GetTextExtentExPointW says we can't even fit one letter in 32767 logical units\n");
1503 GetTextExtentExPointW(hdc, wt, len, extents[2], &fit2, NULL, &sz2);
1504 ok(sz1.cx == sz2.cx && sz1.cy == sz2.cy, "GetTextExtentExPointW returned different sizes for the same string\n");
1505 ok(fit2 == 3, "GetTextExtentExPointW extents isn't consistent with fit\n");
1506 GetTextExtentExPointW(hdc, wt, len, extents[2]-1, &fit2, NULL, &sz2);
1507 ok(fit2 == 2, "GetTextExtentExPointW extents isn't consistent with fit\n");
1508 GetTextExtentExPointW(hdc, wt, 2, 0, NULL, extents + 2, &sz2);
1509 ok(extents[0] == extents[2] && extents[1] == extents[3],
1510 "GetTextExtentExPointW with lpnFit == NULL returns incorrect results\n");
1511 GetTextExtentExPointW(hdc, wt, 2, 0, NULL, NULL, &sz1);
1512 ok(sz1.cx == sz2.cx && sz1.cy == sz2.cy,
1513 "GetTextExtentExPointW with lpnFit and alpDx both NULL returns incorrect results\n");
1515 /* extents functions fail with -ve counts (the interesting case being -1) */
1516 ret = GetTextExtentPointA(hdc, "o", -1, &sz);
1517 ok(ret == FALSE, "got %d\n", ret);
1518 ret = GetTextExtentExPointA(hdc, "o", -1, 0, NULL, NULL, &sz);
1519 ok(ret == FALSE, "got %d\n", ret);
1520 ret = GetTextExtentExPointW(hdc, wt, -1, 0, NULL, NULL, &sz1);
1521 ok(ret == FALSE, "got %d\n", ret);
1523 /* max_extent = 0 succeeds and returns zero */
1524 fit1 = fit2 = -215;
1525 ret = GetTextExtentExPointA(hdc, NULL, 0, 0, &fit1, NULL, &sz);
1526 ok(ret == TRUE ||
1527 broken(ret == FALSE), /* NT4, 2k */
1528 "got %d\n", ret);
1529 ok(fit1 == 0 ||
1530 broken(fit1 == -215), /* NT4, 2k */
1531 "fit = %d\n", fit1);
1532 ret = GetTextExtentExPointW(hdc, NULL, 0, 0, &fit2, NULL, &sz1);
1533 ok(ret == TRUE, "got %d\n", ret);
1534 ok(fit2 == 0, "fit = %d\n", fit2);
1536 /* max_extent = -1 is interpreted as a very large width that will
1537 * definitely fit our three characters */
1538 fit1 = fit2 = -215;
1539 ret = GetTextExtentExPointA(hdc, "One", 3, -1, &fit1, NULL, &sz);
1540 ok(ret == TRUE, "got %d\n", ret);
1541 ok(fit1 == 3, "fit = %d\n", fit1);
1542 ret = GetTextExtentExPointW(hdc, wt, 3, -1, &fit2, NULL, &sz);
1543 ok(ret == TRUE, "got %d\n", ret);
1544 ok(fit2 == 3, "fit = %d\n", fit2);
1546 /* max_extent = -2 is interpreted similarly, but the Ansi version
1547 * rejects it while the Unicode one accepts it */
1548 fit1 = fit2 = -215;
1549 ret = GetTextExtentExPointA(hdc, "One", 3, -2, &fit1, NULL, &sz);
1550 ok(ret == FALSE, "got %d\n", ret);
1551 ok(fit1 == -215, "fit = %d\n", fit1);
1552 ret = GetTextExtentExPointW(hdc, wt, 3, -2, &fit2, NULL, &sz);
1553 ok(ret == TRUE, "got %d\n", ret);
1554 ok(fit2 == 3, "fit = %d\n", fit2);
1556 hfont = SelectObject(hdc, hfont);
1557 DeleteObject(hfont);
1559 /* non-MM_TEXT mapping mode */
1560 lf.lfHeight = 2000;
1561 hfont = CreateFontIndirectA(&lf);
1562 hfont = SelectObject(hdc, hfont);
1564 SetMapMode( hdc, MM_HIMETRIC );
1565 ret = GetTextExtentExPointW(hdc, wt, 3, 0, NULL, extents, &sz);
1566 ok(ret, "got %d\n", ret);
1567 ok(sz.cx == extents[2], "got %d vs %d\n", sz.cx, extents[2]);
1569 ret = GetTextExtentExPointW(hdc, wt, 3, extents[1], &fit1, extents2, &sz2);
1570 ok(ret, "got %d\n", ret);
1571 ok(fit1 == 2, "got %d\n", fit1);
1572 ok(sz2.cx == sz.cx, "got %d vs %d\n", sz2.cx, sz.cx);
1573 for(i = 0; i < 2; i++)
1574 ok(extents2[i] == extents[i], "%d: %d, %d\n", i, extents2[i], extents[i]);
1576 hfont = SelectObject(hdc, hfont);
1577 DeleteObject(hfont);
1578 HeapFree(GetProcessHeap(), 0, extents);
1579 ReleaseDC(NULL, hdc);
1582 static void test_GetGlyphIndices(void)
1584 HDC hdc;
1585 HFONT hfont;
1586 DWORD charcount;
1587 LOGFONTA lf;
1588 DWORD flags = 0;
1589 WCHAR testtext[] = {'T','e','s','t',0xffff,0};
1590 WORD glyphs[(sizeof(testtext)/2)-1];
1591 TEXTMETRICA textm;
1592 HFONT hOldFont;
1594 if (!pGetGlyphIndicesW) {
1595 win_skip("GetGlyphIndicesW not available on platform\n");
1596 return;
1599 hdc = GetDC(0);
1601 memset(&lf, 0, sizeof(lf));
1602 strcpy(lf.lfFaceName, "System");
1603 lf.lfHeight = 16;
1604 lf.lfCharSet = ANSI_CHARSET;
1606 hfont = CreateFontIndirectA(&lf);
1607 ok(hfont != 0, "CreateFontIndirectEx failed\n");
1608 ok(GetTextMetricsA(hdc, &textm), "GetTextMetric failed\n");
1609 if (textm.tmCharSet == ANSI_CHARSET)
1611 flags |= GGI_MARK_NONEXISTING_GLYPHS;
1612 charcount = pGetGlyphIndicesW(hdc, testtext, (sizeof(testtext)/2)-1, glyphs, flags);
1613 ok(charcount == 5, "GetGlyphIndicesW count of glyphs should = 5 not %d\n", charcount);
1614 ok((glyphs[4] == 0x001f || glyphs[4] == 0xffff /* Vista */), "GetGlyphIndicesW should have returned a nonexistent char not %04x\n", glyphs[4]);
1615 flags = 0;
1616 charcount = pGetGlyphIndicesW(hdc, testtext, (sizeof(testtext)/2)-1, glyphs, flags);
1617 ok(charcount == 5, "GetGlyphIndicesW count of glyphs should = 5 not %d\n", charcount);
1618 ok(glyphs[4] == textm.tmDefaultChar, "GetGlyphIndicesW should have returned a %04x not %04x\n",
1619 textm.tmDefaultChar, glyphs[4]);
1621 else
1622 /* FIXME: Write tests for non-ANSI charsets. */
1623 skip("GetGlyphIndices System font tests only for ANSI_CHARSET\n");
1625 if(!is_font_installed("Tahoma"))
1627 skip("Tahoma is not installed so skipping this test\n");
1628 return;
1630 memset(&lf, 0, sizeof(lf));
1631 strcpy(lf.lfFaceName, "Tahoma");
1632 lf.lfHeight = 20;
1634 hfont = CreateFontIndirectA(&lf);
1635 hOldFont = SelectObject(hdc, hfont);
1636 ok(GetTextMetricsA(hdc, &textm), "GetTextMetric failed\n");
1637 flags |= GGI_MARK_NONEXISTING_GLYPHS;
1638 charcount = pGetGlyphIndicesW(hdc, testtext, (sizeof(testtext)/2)-1, glyphs, flags);
1639 ok(charcount == 5, "GetGlyphIndicesW count of glyphs should = 5 not %d\n", charcount);
1640 ok(glyphs[4] == 0xffff, "GetGlyphIndicesW should have returned 0xffff char not %04x\n", glyphs[4]);
1641 flags = 0;
1642 testtext[0] = textm.tmDefaultChar;
1643 charcount = pGetGlyphIndicesW(hdc, testtext, (sizeof(testtext)/2)-1, glyphs, flags);
1644 ok(charcount == 5, "GetGlyphIndicesW count of glyphs should = 5 not %d\n", charcount);
1645 ok(glyphs[0] == 0, "GetGlyphIndicesW for tmDefaultChar should be 0 not %04x\n", glyphs[0]);
1646 ok(glyphs[4] == 0, "GetGlyphIndicesW should have returned 0 not %04x\n", glyphs[4]);
1647 DeleteObject(SelectObject(hdc, hOldFont));
1650 static void test_GetKerningPairs(void)
1652 static const struct kerning_data
1654 const char face_name[LF_FACESIZE];
1655 LONG height;
1656 /* some interesting fields from OUTLINETEXTMETRIC */
1657 LONG tmHeight, tmAscent, tmDescent;
1658 UINT otmEMSquare;
1659 INT otmAscent;
1660 INT otmDescent;
1661 UINT otmLineGap;
1662 UINT otmsCapEmHeight;
1663 UINT otmsXHeight;
1664 INT otmMacAscent;
1665 INT otmMacDescent;
1666 UINT otmMacLineGap;
1667 UINT otmusMinimumPPEM;
1668 /* small subset of kerning pairs to test */
1669 DWORD total_kern_pairs;
1670 const KERNINGPAIR kern_pair[26];
1671 } kd[] =
1673 {"Arial", 12, 12, 9, 3,
1674 2048, 7, -2, 1, 5, 2, 8, -2, 0, 9,
1677 {' ','A',-1},{' ','T',0},{' ','Y',0},{'1','1',-1},
1678 {'A',' ',-1},{'A','T',-1},{'A','V',-1},{'A','W',0},
1679 {'A','Y',-1},{'A','v',0},{'A','w',0},{'A','y',0},
1680 {'F',',',-1},{'F','.',-1},{'F','A',-1},{'L',' ',0},
1681 {'L','T',-1},{'L','V',-1},{'L','W',-1},{'L','Y',-1},
1682 {915,912,+1},{915,913,-1},{910,912,+1},{910,913,-1},
1683 {933,970,+1},{933,972,-1}
1686 {"Arial", -34, 39, 32, 7,
1687 2048, 25, -7, 5, 17, 9, 31, -7, 1, 9,
1690 {' ','A',-2},{' ','T',-1},{' ','Y',-1},{'1','1',-3},
1691 {'A',' ',-2},{'A','T',-3},{'A','V',-3},{'A','W',-1},
1692 {'A','Y',-3},{'A','v',-1},{'A','w',-1},{'A','y',-1},
1693 {'F',',',-4},{'F','.',-4},{'F','A',-2},{'L',' ',-1},
1694 {'L','T',-3},{'L','V',-3},{'L','W',-3},{'L','Y',-3},
1695 {915,912,+3},{915,913,-3},{910,912,+3},{910,913,-3},
1696 {933,970,+2},{933,972,-3}
1699 { "Arial", 120, 120, 97, 23,
1700 2048, 79, -23, 16, 54, 27, 98, -23, 4, 9,
1703 {' ','A',-6},{' ','T',-2},{' ','Y',-2},{'1','1',-8},
1704 {'A',' ',-6},{'A','T',-8},{'A','V',-8},{'A','W',-4},
1705 {'A','Y',-8},{'A','v',-2},{'A','w',-2},{'A','y',-2},
1706 {'F',',',-12},{'F','.',-12},{'F','A',-6},{'L',' ',-4},
1707 {'L','T',-8},{'L','V',-8},{'L','W',-8},{'L','Y',-8},
1708 {915,912,+9},{915,913,-10},{910,912,+9},{910,913,-8},
1709 {933,970,+6},{933,972,-10}
1712 #if 0 /* this set fails due to +1/-1 errors (rounding bug?), needs investigation. */
1713 { "Arial", 1024 /* usually 1/2 of EM Square */, 1024, 830, 194,
1714 2048, 668, -193, 137, 459, 229, 830, -194, 30, 9,
1717 {' ','A',-51},{' ','T',-17},{' ','Y',-17},{'1','1',-68},
1718 {'A',' ',-51},{'A','T',-68},{'A','V',-68},{'A','W',-34},
1719 {'A','Y',-68},{'A','v',-17},{'A','w',-17},{'A','y',-17},
1720 {'F',',',-102},{'F','.',-102},{'F','A',-51},{'L',' ',-34},
1721 {'L','T',-68},{'L','V',-68},{'L','W',-68},{'L','Y',-68},
1722 {915,912,+73},{915,913,-84},{910,912,+76},{910,913,-68},
1723 {933,970,+54},{933,972,-83}
1726 #endif
1728 LOGFONTA lf;
1729 HFONT hfont, hfont_old;
1730 KERNINGPAIR *kern_pair;
1731 HDC hdc;
1732 DWORD total_kern_pairs, ret, i, n, matches;
1734 hdc = GetDC(0);
1736 /* GetKerningPairsA maps unicode set of kerning pairs to current code page
1737 * which may render this test unusable, so we're trying to avoid that.
1739 SetLastError(0xdeadbeef);
1740 GetKerningPairsW(hdc, 0, NULL);
1741 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
1743 win_skip("Skipping the GetKerningPairs test on a Win9x platform\n");
1744 ReleaseDC(0, hdc);
1745 return;
1748 for (i = 0; i < sizeof(kd)/sizeof(kd[0]); i++)
1750 OUTLINETEXTMETRICW otm;
1751 UINT uiRet;
1753 if (!is_font_installed(kd[i].face_name))
1755 trace("%s is not installed so skipping this test\n", kd[i].face_name);
1756 continue;
1759 trace("testing font %s, height %d\n", kd[i].face_name, kd[i].height);
1761 memset(&lf, 0, sizeof(lf));
1762 strcpy(lf.lfFaceName, kd[i].face_name);
1763 lf.lfHeight = kd[i].height;
1764 hfont = CreateFontIndirectA(&lf);
1765 ok(hfont != NULL, "failed to create a font, name %s\n", kd[i].face_name);
1767 hfont_old = SelectObject(hdc, hfont);
1769 SetLastError(0xdeadbeef);
1770 otm.otmSize = sizeof(otm); /* just in case for Win9x compatibility */
1771 uiRet = GetOutlineTextMetricsW(hdc, sizeof(otm), &otm);
1772 ok(uiRet == sizeof(otm), "GetOutlineTextMetricsW error %d\n", GetLastError());
1774 ok(match_off_by_1(kd[i].tmHeight, otm.otmTextMetrics.tmHeight, FALSE), "expected %d, got %d\n",
1775 kd[i].tmHeight, otm.otmTextMetrics.tmHeight);
1776 ok(match_off_by_1(kd[i].tmAscent, otm.otmTextMetrics.tmAscent, FALSE), "expected %d, got %d\n",
1777 kd[i].tmAscent, otm.otmTextMetrics.tmAscent);
1778 ok(kd[i].tmDescent == otm.otmTextMetrics.tmDescent, "expected %d, got %d\n",
1779 kd[i].tmDescent, otm.otmTextMetrics.tmDescent);
1781 ok(kd[i].otmEMSquare == otm.otmEMSquare, "expected %u, got %u\n",
1782 kd[i].otmEMSquare, otm.otmEMSquare);
1783 ok(kd[i].otmAscent == otm.otmAscent, "expected %d, got %d\n",
1784 kd[i].otmAscent, otm.otmAscent);
1785 ok(kd[i].otmDescent == otm.otmDescent, "expected %d, got %d\n",
1786 kd[i].otmDescent, otm.otmDescent);
1787 ok(kd[i].otmLineGap == otm.otmLineGap, "expected %u, got %u\n",
1788 kd[i].otmLineGap, otm.otmLineGap);
1789 ok(near_match(kd[i].otmMacDescent, otm.otmMacDescent), "expected %d, got %d\n",
1790 kd[i].otmMacDescent, otm.otmMacDescent);
1791 ok(near_match(kd[i].otmMacAscent, otm.otmMacAscent), "expected %d, got %d\n",
1792 kd[i].otmMacAscent, otm.otmMacAscent);
1793 todo_wine
1794 ok(kd[i].otmsCapEmHeight == otm.otmsCapEmHeight, "expected %u, got %u\n",
1795 kd[i].otmsCapEmHeight, otm.otmsCapEmHeight);
1796 todo_wine
1797 ok(kd[i].otmsXHeight == otm.otmsXHeight, "expected %u, got %u\n",
1798 kd[i].otmsXHeight, otm.otmsXHeight);
1799 ok(kd[i].otmMacLineGap == otm.otmMacLineGap, "expected %u, got %u\n",
1800 kd[i].otmMacLineGap, otm.otmMacLineGap);
1801 todo_wine
1802 ok(kd[i].otmusMinimumPPEM == otm.otmusMinimumPPEM, "expected %u, got %u\n",
1803 kd[i].otmusMinimumPPEM, otm.otmusMinimumPPEM);
1805 total_kern_pairs = GetKerningPairsW(hdc, 0, NULL);
1806 trace("total_kern_pairs %u\n", total_kern_pairs);
1807 kern_pair = HeapAlloc(GetProcessHeap(), 0, total_kern_pairs * sizeof(*kern_pair));
1809 /* Win98 (GetKerningPairsA) and XP behave differently here, the test
1810 * passes on XP.
1812 SetLastError(0xdeadbeef);
1813 ret = GetKerningPairsW(hdc, 0, kern_pair);
1814 ok(GetLastError() == ERROR_INVALID_PARAMETER,
1815 "got error %u, expected ERROR_INVALID_PARAMETER\n", GetLastError());
1816 ok(ret == 0, "got %u, expected 0\n", ret);
1818 ret = GetKerningPairsW(hdc, 100, NULL);
1819 ok(ret == total_kern_pairs, "got %u, expected %u\n", ret, total_kern_pairs);
1821 ret = GetKerningPairsW(hdc, total_kern_pairs/2, kern_pair);
1822 ok(ret == total_kern_pairs/2, "got %u, expected %u\n", ret, total_kern_pairs/2);
1824 ret = GetKerningPairsW(hdc, total_kern_pairs, kern_pair);
1825 ok(ret == total_kern_pairs, "got %u, expected %u\n", ret, total_kern_pairs);
1827 matches = 0;
1829 for (n = 0; n < ret; n++)
1831 DWORD j;
1832 /* Disabled to limit console spam */
1833 if (0 && kern_pair[n].wFirst < 127 && kern_pair[n].wSecond < 127)
1834 trace("{'%c','%c',%d},\n",
1835 kern_pair[n].wFirst, kern_pair[n].wSecond, kern_pair[n].iKernAmount);
1836 for (j = 0; j < kd[i].total_kern_pairs; j++)
1838 if (kern_pair[n].wFirst == kd[i].kern_pair[j].wFirst &&
1839 kern_pair[n].wSecond == kd[i].kern_pair[j].wSecond)
1841 ok(kern_pair[n].iKernAmount == kd[i].kern_pair[j].iKernAmount,
1842 "pair %d:%d got %d, expected %d\n",
1843 kern_pair[n].wFirst, kern_pair[n].wSecond,
1844 kern_pair[n].iKernAmount, kd[i].kern_pair[j].iKernAmount);
1845 matches++;
1850 ok(matches == kd[i].total_kern_pairs, "got matches %u, expected %u\n",
1851 matches, kd[i].total_kern_pairs);
1853 HeapFree(GetProcessHeap(), 0, kern_pair);
1855 SelectObject(hdc, hfont_old);
1856 DeleteObject(hfont);
1859 ReleaseDC(0, hdc);
1862 struct font_data
1864 const char face_name[LF_FACESIZE];
1865 int requested_height;
1866 int weight, height, ascent, descent, int_leading, ext_leading, dpi;
1867 BOOL exact;
1870 static void test_height( HDC hdc, const struct font_data *fd )
1872 LOGFONTA lf;
1873 HFONT hfont, old_hfont;
1874 TEXTMETRICA tm;
1875 INT ret, i;
1877 for (i = 0; fd[i].face_name[0]; i++)
1879 if (!is_truetype_font_installed(fd[i].face_name))
1881 skip("%s is not installed\n", fd[i].face_name);
1882 continue;
1885 memset(&lf, 0, sizeof(lf));
1886 lf.lfHeight = fd[i].requested_height;
1887 lf.lfWeight = fd[i].weight;
1888 strcpy(lf.lfFaceName, fd[i].face_name);
1890 hfont = CreateFontIndirectA(&lf);
1891 ok(hfont != NULL, "failed to create a font, name %s\n", fd[i].face_name);
1893 old_hfont = SelectObject(hdc, hfont);
1894 ret = GetTextMetricsA(hdc, &tm);
1895 ok(ret, "GetTextMetrics error %d\n", GetLastError());
1896 if(fd[i].dpi == tm.tmDigitizedAspectX)
1898 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);
1899 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);
1900 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);
1901 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);
1902 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);
1903 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);
1906 SelectObject(hdc, old_hfont);
1907 /* force GDI to use new font, otherwise Windows leaks the font reference */
1908 GetTextMetricsA(hdc, &tm);
1909 DeleteObject(hfont);
1913 static void *find_ttf_table( void *ttf, DWORD size, DWORD tag )
1915 WORD i, num_tables = GET_BE_WORD(*((WORD *)ttf + 2));
1916 DWORD *table = (DWORD *)ttf + 3;
1918 for (i = 0; i < num_tables; i++)
1920 if (table[0] == tag)
1921 return (BYTE *)ttf + GET_BE_DWORD(table[2]);
1922 table += 4;
1924 return NULL;
1927 static void test_height_selection_vdmx( HDC hdc )
1929 static const struct font_data charset_0[] = /* doesn't use VDMX */
1931 { "wine_vdmx", 10, FW_NORMAL, 10, 8, 2, 2, 0, 96, TRUE },
1932 { "wine_vdmx", 11, FW_NORMAL, 11, 9, 2, 2, 0, 96, TRUE },
1933 { "wine_vdmx", 12, FW_NORMAL, 12, 10, 2, 2, 0, 96, TRUE },
1934 { "wine_vdmx", 13, FW_NORMAL, 13, 11, 2, 2, 0, 96, TRUE },
1935 { "wine_vdmx", 14, FW_NORMAL, 14, 12, 2, 2, 0, 96, TRUE },
1936 { "wine_vdmx", 15, FW_NORMAL, 15, 12, 3, 3, 0, 96, FALSE },
1937 { "wine_vdmx", 16, FW_NORMAL, 16, 13, 3, 3, 0, 96, TRUE },
1938 { "wine_vdmx", 17, FW_NORMAL, 17, 14, 3, 3, 0, 96, TRUE },
1939 { "wine_vdmx", 18, FW_NORMAL, 18, 15, 3, 3, 0, 96, TRUE },
1940 { "wine_vdmx", 19, FW_NORMAL, 19, 16, 3, 3, 0, 96, TRUE },
1941 { "wine_vdmx", 20, FW_NORMAL, 20, 17, 3, 4, 0, 96, FALSE },
1942 { "wine_vdmx", 21, FW_NORMAL, 21, 17, 4, 4, 0, 96, TRUE },
1943 { "wine_vdmx", 22, FW_NORMAL, 22, 18, 4, 4, 0, 96, TRUE },
1944 { "wine_vdmx", 23, FW_NORMAL, 23, 19, 4, 4, 0, 96, TRUE },
1945 { "wine_vdmx", 24, FW_NORMAL, 24, 20, 4, 4, 0, 96, TRUE },
1946 { "wine_vdmx", 25, FW_NORMAL, 25, 21, 4, 4, 0, 96, TRUE },
1947 { "wine_vdmx", 26, FW_NORMAL, 26, 22, 4, 5, 0, 96, FALSE },
1948 { "wine_vdmx", 27, FW_NORMAL, 27, 22, 5, 5, 0, 96, TRUE },
1949 { "wine_vdmx", 28, FW_NORMAL, 28, 23, 5, 5, 0, 96, TRUE },
1950 { "wine_vdmx", 29, FW_NORMAL, 29, 24, 5, 5, 0, 96, TRUE },
1951 { "wine_vdmx", 30, FW_NORMAL, 30, 25, 5, 5, 0, 96, TRUE },
1952 { "wine_vdmx", 31, FW_NORMAL, 31, 26, 5, 5, 0, 96, TRUE },
1953 { "wine_vdmx", 32, FW_NORMAL, 32, 27, 5, 6, 0, 96, FALSE },
1954 { "wine_vdmx", 48, FW_NORMAL, 48, 40, 8, 8, 0, 96, TRUE },
1955 { "wine_vdmx", 64, FW_NORMAL, 64, 53, 11, 11, 0, 96, TRUE },
1956 { "wine_vdmx", 96, FW_NORMAL, 96, 80, 16, 17, 0, 96, FALSE },
1957 { "wine_vdmx", -10, FW_NORMAL, 12, 10, 2, 2, 0, 96, TRUE },
1958 { "wine_vdmx", -11, FW_NORMAL, 13, 11, 2, 2, 0, 96, TRUE },
1959 { "wine_vdmx", -12, FW_NORMAL, 14, 12, 2, 2, 0, 96, TRUE },
1960 { "wine_vdmx", -13, FW_NORMAL, 16, 13, 3, 3, 0, 96, TRUE },
1961 { "wine_vdmx", -14, FW_NORMAL, 17, 14, 3, 3, 0, 96, TRUE },
1962 { "wine_vdmx", -15, FW_NORMAL, 18, 15, 3, 3, 0, 96, TRUE },
1963 { "wine_vdmx", -16, FW_NORMAL, 19, 16, 3, 3, 0, 96, TRUE },
1964 { "wine_vdmx", -17, FW_NORMAL, 21, 17, 4, 4, 0, 96, TRUE },
1965 { "wine_vdmx", -18, FW_NORMAL, 22, 18, 4, 4, 0, 96, TRUE },
1966 { "wine_vdmx", -19, FW_NORMAL, 23, 19, 4, 4, 0, 96, TRUE },
1967 { "wine_vdmx", -20, FW_NORMAL, 24, 20, 4, 4, 0, 96, TRUE },
1968 { "wine_vdmx", -21, FW_NORMAL, 25, 21, 4, 4, 0, 96, TRUE },
1969 { "wine_vdmx", -22, FW_NORMAL, 27, 22, 5, 5, 0, 96, TRUE },
1970 { "wine_vdmx", -23, FW_NORMAL, 28, 23, 5, 5, 0, 96, TRUE },
1971 { "wine_vdmx", -24, FW_NORMAL, 29, 24, 5, 5, 0, 96, TRUE },
1972 { "wine_vdmx", -25, FW_NORMAL, 30, 25, 5, 5, 0, 96, TRUE },
1973 { "wine_vdmx", -26, FW_NORMAL, 31, 26, 5, 5, 0, 96, TRUE },
1974 { "wine_vdmx", -27, FW_NORMAL, 33, 27, 6, 6, 0, 96, TRUE },
1975 { "wine_vdmx", -28, FW_NORMAL, 34, 28, 6, 6, 0, 96, TRUE },
1976 { "wine_vdmx", -29, FW_NORMAL, 35, 29, 6, 6, 0, 96, TRUE },
1977 { "wine_vdmx", -30, FW_NORMAL, 36, 30, 6, 6, 0, 96, TRUE },
1978 { "wine_vdmx", -31, FW_NORMAL, 37, 31, 6, 6, 0, 96, TRUE },
1979 { "wine_vdmx", -32, FW_NORMAL, 39, 32, 7, 7, 0, 96, TRUE },
1980 { "wine_vdmx", -48, FW_NORMAL, 58, 48, 10, 10, 0, 96, TRUE },
1981 { "wine_vdmx", -64, FW_NORMAL, 77, 64, 13, 13, 0, 96, TRUE },
1982 { "wine_vdmx", -96, FW_NORMAL, 116, 96, 20, 20, 0, 96, TRUE },
1983 { "", 0, 0, 0, 0, 0, 0, 0, 0, 0 }
1986 static const struct font_data charset_1[] = /* Uses VDMX */
1988 { "wine_vdmx", 10, FW_NORMAL, 10, 8, 2, 2, 0, 96, TRUE },
1989 { "wine_vdmx", 11, FW_NORMAL, 11, 9, 2, 2, 0, 96, TRUE },
1990 { "wine_vdmx", 12, FW_NORMAL, 12, 10, 2, 2, 0, 96, TRUE },
1991 { "wine_vdmx", 13, FW_NORMAL, 13, 11, 2, 2, 0, 96, TRUE },
1992 { "wine_vdmx", 14, FW_NORMAL, 13, 11, 2, 2, 0, 96, TRUE },
1993 { "wine_vdmx", 15, FW_NORMAL, 13, 11, 2, 2, 0, 96, TRUE },
1994 { "wine_vdmx", 16, FW_NORMAL, 16, 13, 3, 4, 0, 96, TRUE },
1995 { "wine_vdmx", 17, FW_NORMAL, 16, 13, 3, 3, 0, 96, TRUE },
1996 { "wine_vdmx", 18, FW_NORMAL, 16, 13, 3, 3, 0, 96, TRUE },
1997 { "wine_vdmx", 19, FW_NORMAL, 19, 15, 4, 5, 0, 96, TRUE },
1998 { "wine_vdmx", 20, FW_NORMAL, 20, 16, 4, 5, 0, 96, TRUE },
1999 { "wine_vdmx", 21, FW_NORMAL, 21, 17, 4, 5, 0, 96, TRUE },
2000 { "wine_vdmx", 22, FW_NORMAL, 22, 18, 4, 5, 0, 96, TRUE },
2001 { "wine_vdmx", 23, FW_NORMAL, 23, 19, 4, 5, 0, 96, TRUE },
2002 { "wine_vdmx", 24, FW_NORMAL, 23, 19, 4, 5, 0, 96, TRUE },
2003 { "wine_vdmx", 25, FW_NORMAL, 25, 21, 4, 6, 0, 96, TRUE },
2004 { "wine_vdmx", 26, FW_NORMAL, 26, 22, 4, 6, 0, 96, TRUE },
2005 { "wine_vdmx", 27, FW_NORMAL, 27, 23, 4, 6, 0, 96, TRUE },
2006 { "wine_vdmx", 28, FW_NORMAL, 27, 23, 4, 5, 0, 96, TRUE },
2007 { "wine_vdmx", 29, FW_NORMAL, 29, 24, 5, 6, 0, 96, TRUE },
2008 { "wine_vdmx", 30, FW_NORMAL, 29, 24, 5, 6, 0, 96, TRUE },
2009 { "wine_vdmx", 31, FW_NORMAL, 29, 24, 5, 6, 0, 96, TRUE },
2010 { "wine_vdmx", 32, FW_NORMAL, 32, 26, 6, 8, 0, 96, TRUE },
2011 { "wine_vdmx", 48, FW_NORMAL, 48, 40, 8, 10, 0, 96, TRUE },
2012 { "wine_vdmx", 64, FW_NORMAL, 64, 54, 10, 13, 0, 96, TRUE },
2013 { "wine_vdmx", 96, FW_NORMAL, 95, 79, 16, 18, 0, 96, TRUE },
2014 { "wine_vdmx", -10, FW_NORMAL, 12, 10, 2, 2, 0, 96, TRUE },
2015 { "wine_vdmx", -11, FW_NORMAL, 13, 11, 2, 2, 0, 96, TRUE },
2016 { "wine_vdmx", -12, FW_NORMAL, 16, 13, 3, 4, 0, 96, TRUE },
2017 { "wine_vdmx", -13, FW_NORMAL, 16, 13, 3, 3, 0, 96, TRUE },
2018 { "wine_vdmx", -14, FW_NORMAL, 19, 15, 4, 5, 0, 96, TRUE },
2019 { "wine_vdmx", -15, FW_NORMAL, 20, 16, 4, 5, 0, 96, TRUE },
2020 { "wine_vdmx", -16, FW_NORMAL, 21, 17, 4, 5, 0, 96, TRUE },
2021 { "wine_vdmx", -17, FW_NORMAL, 22, 18, 4, 5, 0, 96, TRUE },
2022 { "wine_vdmx", -18, FW_NORMAL, 23, 19, 4, 5, 0, 96, TRUE },
2023 { "wine_vdmx", -19, FW_NORMAL, 25, 21, 4, 6, 0, 96, TRUE },
2024 { "wine_vdmx", -20, FW_NORMAL, 26, 22, 4, 6, 0, 96, TRUE },
2025 { "wine_vdmx", -21, FW_NORMAL, 27, 23, 4, 6, 0, 96, TRUE },
2026 { "wine_vdmx", -22, FW_NORMAL, 27, 23, 4, 5, 0, 96, TRUE },
2027 { "wine_vdmx", -23, FW_NORMAL, 29, 24, 5, 6, 0, 96, TRUE },
2028 { "wine_vdmx", -24, FW_NORMAL, 32, 26, 6, 8, 0, 96, TRUE },
2029 { "wine_vdmx", -25, FW_NORMAL, 32, 26, 6, 7, 0, 96, TRUE },
2030 { "wine_vdmx", -26, FW_NORMAL, 33, 27, 6, 7, 0, 96, TRUE },
2031 { "wine_vdmx", -27, FW_NORMAL, 35, 29, 6, 8, 0, 96, TRUE },
2032 { "wine_vdmx", -28, FW_NORMAL, 36, 30, 6, 8, 0, 96, TRUE },
2033 { "wine_vdmx", -29, FW_NORMAL, 36, 30, 6, 7, 0, 96, TRUE },
2034 { "wine_vdmx", -30, FW_NORMAL, 38, 32, 6, 8, 0, 96, TRUE },
2035 { "wine_vdmx", -31, FW_NORMAL, 39, 33, 6, 8, 0, 96, TRUE },
2036 { "wine_vdmx", -32, FW_NORMAL, 40, 33, 7, 8, 0, 96, TRUE },
2037 { "wine_vdmx", -48, FW_NORMAL, 60, 50, 10, 12, 0, 96, TRUE },
2038 { "wine_vdmx", -64, FW_NORMAL, 81, 67, 14, 17, 0, 96, TRUE },
2039 { "wine_vdmx", -96, FW_NORMAL, 119, 99, 20, 23, 0, 96, TRUE },
2040 { "", 0, 0, 0, 0, 0, 0, 0, 0, 0 }
2043 static const struct vdmx_data
2045 WORD version;
2046 BYTE bCharSet;
2047 const struct font_data *fd;
2048 } data[] =
2050 { 0, 0, charset_0 },
2051 { 0, 1, charset_1 },
2052 { 1, 0, charset_0 },
2053 { 1, 1, charset_1 }
2055 int i;
2056 DWORD size, num;
2057 WORD *vdmx_header;
2058 BYTE *ratio_rec;
2059 char ttf_name[MAX_PATH];
2060 void *res, *copy;
2061 BOOL ret;
2063 if (!pAddFontResourceExA)
2065 win_skip("AddFontResourceExA unavailable\n");
2066 return;
2069 for (i = 0; i < sizeof(data) / sizeof(data[0]); i++)
2071 res = get_res_data( "wine_vdmx.ttf", &size );
2073 copy = HeapAlloc( GetProcessHeap(), 0, size );
2074 memcpy( copy, res, size );
2075 vdmx_header = find_ttf_table( copy, size, MS_MAKE_TAG('V','D','M','X') );
2076 vdmx_header[0] = GET_BE_WORD( data[i].version );
2077 ok( GET_BE_WORD( vdmx_header[1] ) == 1, "got %04x\n", GET_BE_WORD( vdmx_header[1] ) );
2078 ok( GET_BE_WORD( vdmx_header[2] ) == 1, "got %04x\n", GET_BE_WORD( vdmx_header[2] ) );
2079 ratio_rec = (BYTE *)&vdmx_header[3];
2080 ratio_rec[0] = data[i].bCharSet;
2082 write_tmp_file( copy, &size, ttf_name );
2083 HeapFree( GetProcessHeap(), 0, copy );
2085 ok( !is_truetype_font_installed("wine_vdmx"), "Already installed\n" );
2086 num = pAddFontResourceExA( ttf_name, FR_PRIVATE, 0 );
2087 if (!num) win_skip("Unable to add ttf font resource\n");
2088 else
2090 ok( is_truetype_font_installed("wine_vdmx"), "Not installed\n" );
2091 test_height( hdc, data[i].fd );
2092 pRemoveFontResourceExA( ttf_name, FR_PRIVATE, 0 );
2094 ret = DeleteFileA( ttf_name );
2095 ok(ret || broken(!ret && GetLastError() == ERROR_ACCESS_DENIED),
2096 "DeleteFile error %d\n", GetLastError());
2100 static void test_height_selection(void)
2102 static const struct font_data tahoma[] =
2104 {"Tahoma", -12, FW_NORMAL, 14, 12, 2, 2, 0, 96, TRUE },
2105 {"Tahoma", -24, FW_NORMAL, 29, 24, 5, 5, 0, 96, TRUE },
2106 {"Tahoma", -48, FW_NORMAL, 58, 48, 10, 10, 0, 96, TRUE },
2107 {"Tahoma", -96, FW_NORMAL, 116, 96, 20, 20, 0, 96, TRUE },
2108 {"Tahoma", -192, FW_NORMAL, 232, 192, 40, 40, 0, 96, TRUE },
2109 {"Tahoma", 12, FW_NORMAL, 12, 10, 2, 2, 0, 96, TRUE },
2110 {"Tahoma", 24, FW_NORMAL, 24, 20, 4, 4, 0, 96, TRUE },
2111 {"Tahoma", 48, FW_NORMAL, 48, 40, 8, 8, 0, 96, TRUE },
2112 {"Tahoma", 96, FW_NORMAL, 96, 80, 16, 17, 0, 96, FALSE },
2113 {"Tahoma", 192, FW_NORMAL, 192, 159, 33, 33, 0, 96, TRUE },
2114 {"", 0, 0, 0, 0, 0, 0, 0, 0, 0 }
2116 HDC hdc = CreateCompatibleDC(0);
2117 ok(hdc != NULL, "failed to create hdc\n");
2119 test_height( hdc, tahoma );
2120 test_height_selection_vdmx( hdc );
2122 DeleteDC(hdc);
2125 static UINT get_font_fsselection(LOGFONTA *lf)
2127 OUTLINETEXTMETRICA *otm;
2128 HFONT hfont, hfont_old;
2129 DWORD ret, otm_size;
2130 UINT fsSelection;
2131 HDC hdc;
2133 hdc = GetDC(0);
2134 hfont = CreateFontIndirectA(lf);
2135 ok(hfont != NULL, "failed to create a font\n");
2137 hfont_old = SelectObject(hdc, hfont);
2139 otm_size = GetOutlineTextMetricsA(hdc, 0, NULL);
2140 otm = HeapAlloc(GetProcessHeap(), 0, otm_size);
2141 otm->otmSize = sizeof(*otm);
2142 ret = GetOutlineTextMetricsA(hdc, otm->otmSize, otm);
2143 ok(ret == otm->otmSize, "expected %u, got %u, error %d\n", otm->otmSize, ret, GetLastError());
2144 fsSelection = otm->otmfsSelection;
2145 HeapFree(GetProcessHeap(), 0, otm);
2146 SelectObject(hdc, hfont_old);
2147 DeleteObject(hfont);
2148 ReleaseDC(0, hdc);
2150 return fsSelection;
2153 static void test_GetOutlineTextMetrics(void)
2155 OUTLINETEXTMETRICA *otm;
2156 LOGFONTA lf;
2157 HFONT hfont, hfont_old;
2158 HDC hdc;
2159 DWORD ret, otm_size;
2160 LPSTR unset_ptr;
2161 UINT fsSelection;
2163 /* check fsSelection field with bold simulation */
2164 memset(&lf, 0, sizeof(lf));
2165 strcpy(lf.lfFaceName, "Wingdings");
2166 lf.lfCharSet = SYMBOL_CHARSET;
2168 /* regular face */
2169 fsSelection = get_font_fsselection(&lf);
2170 ok((fsSelection & (1 << 5)) == 0, "got 0x%x\n", fsSelection);
2172 /* face with bold simulation */
2173 lf.lfWeight = FW_BOLD;
2174 fsSelection = get_font_fsselection(&lf);
2175 ok((fsSelection & (1 << 5)) != 0, "got 0x%x\n", fsSelection);
2177 /* check fsSelection field with oblique simulation */
2178 memset(&lf, 0, sizeof(lf));
2179 strcpy(lf.lfFaceName, "Tahoma");
2180 lf.lfHeight = -13;
2181 lf.lfWeight = FW_NORMAL;
2182 lf.lfPitchAndFamily = DEFAULT_PITCH;
2183 lf.lfQuality = PROOF_QUALITY;
2185 /* regular face */
2186 fsSelection = get_font_fsselection(&lf);
2187 ok((fsSelection & 1) == 0, "got 0x%x\n", fsSelection);
2189 lf.lfItalic = 1;
2190 /* face with oblique simulation */
2191 fsSelection = get_font_fsselection(&lf);
2192 ok((fsSelection & 1) == 1, "got 0x%x\n", fsSelection);
2194 if (!is_font_installed("Arial"))
2196 skip("Arial is not installed\n");
2197 return;
2200 hdc = GetDC(0);
2202 memset(&lf, 0, sizeof(lf));
2203 strcpy(lf.lfFaceName, "Arial");
2204 lf.lfHeight = -13;
2205 lf.lfWeight = FW_NORMAL;
2206 lf.lfPitchAndFamily = DEFAULT_PITCH;
2207 lf.lfQuality = PROOF_QUALITY;
2208 hfont = CreateFontIndirectA(&lf);
2209 ok(hfont != NULL, "failed to create a font\n");
2211 hfont_old = SelectObject(hdc, hfont);
2212 otm_size = GetOutlineTextMetricsA(hdc, 0, NULL);
2213 trace("otm buffer size %u (0x%x)\n", otm_size, otm_size);
2215 otm = HeapAlloc(GetProcessHeap(), 0, otm_size);
2217 memset(otm, 0xAA, otm_size);
2218 SetLastError(0xdeadbeef);
2219 otm->otmSize = sizeof(*otm); /* just in case for Win9x compatibility */
2220 ret = GetOutlineTextMetricsA(hdc, otm->otmSize, otm);
2221 ok(ret == 1 /* Win9x */ ||
2222 ret == otm->otmSize /* XP*/,
2223 "expected %u, got %u, error %d\n", otm->otmSize, ret, GetLastError());
2224 if (ret != 1) /* Win9x doesn't care about pointing beyond of the buffer */
2226 ok(otm->otmpFamilyName == NULL, "expected NULL got %p\n", otm->otmpFamilyName);
2227 ok(otm->otmpFaceName == NULL, "expected NULL got %p\n", otm->otmpFaceName);
2228 ok(otm->otmpStyleName == NULL, "expected NULL got %p\n", otm->otmpStyleName);
2229 ok(otm->otmpFullName == NULL, "expected NULL got %p\n", otm->otmpFullName);
2232 memset(otm, 0xAA, otm_size);
2233 SetLastError(0xdeadbeef);
2234 otm->otmSize = otm_size; /* just in case for Win9x compatibility */
2235 ret = GetOutlineTextMetricsA(hdc, otm->otmSize, otm);
2236 ok(ret == 1 /* Win9x */ ||
2237 ret == otm->otmSize /* XP*/,
2238 "expected %u, got %u, error %d\n", otm->otmSize, ret, GetLastError());
2239 if (ret != 1) /* Win9x doesn't care about pointing beyond of the buffer */
2241 ok(otm->otmpFamilyName != NULL, "expected not NULL got %p\n", otm->otmpFamilyName);
2242 ok(otm->otmpFaceName != NULL, "expected not NULL got %p\n", otm->otmpFaceName);
2243 ok(otm->otmpStyleName != NULL, "expected not NULL got %p\n", otm->otmpStyleName);
2244 ok(otm->otmpFullName != NULL, "expected not NULL got %p\n", otm->otmpFullName);
2247 /* ask about truncated data */
2248 memset(otm, 0xAA, otm_size);
2249 memset(&unset_ptr, 0xAA, sizeof(unset_ptr));
2250 SetLastError(0xdeadbeef);
2251 otm->otmSize = sizeof(*otm) - sizeof(LPSTR); /* just in case for Win9x compatibility */
2252 ret = GetOutlineTextMetricsA(hdc, otm->otmSize, otm);
2253 ok(ret == 1 /* Win9x */ ||
2254 ret == otm->otmSize /* XP*/,
2255 "expected %u, got %u, error %d\n", otm->otmSize, ret, GetLastError());
2256 if (ret != 1) /* Win9x doesn't care about pointing beyond of the buffer */
2258 ok(otm->otmpFamilyName == NULL, "expected NULL got %p\n", otm->otmpFamilyName);
2259 ok(otm->otmpFaceName == NULL, "expected NULL got %p\n", otm->otmpFaceName);
2260 ok(otm->otmpStyleName == NULL, "expected NULL got %p\n", otm->otmpStyleName);
2262 ok(otm->otmpFullName == unset_ptr, "expected %p got %p\n", unset_ptr, otm->otmpFullName);
2264 /* check handling of NULL pointer */
2265 SetLastError(0xdeadbeef);
2266 ret = GetOutlineTextMetricsA(hdc, otm_size, NULL);
2267 ok(ret == otm_size, "expected %u, got %u, error %d\n", otm_size, ret, GetLastError());
2269 HeapFree(GetProcessHeap(), 0, otm);
2271 SelectObject(hdc, hfont_old);
2272 DeleteObject(hfont);
2274 ReleaseDC(0, hdc);
2277 static void testJustification(HDC hdc, PCSTR str, RECT *clientArea)
2279 INT y,
2280 breakCount,
2281 areaWidth = clientArea->right - clientArea->left,
2282 nErrors = 0, e;
2283 const char *pFirstChar, *pLastChar;
2284 SIZE size;
2285 TEXTMETRICA tm;
2286 struct err
2288 const char *start;
2289 int len;
2290 int GetTextExtentExPointWWidth;
2291 } error[20];
2293 GetTextMetricsA(hdc, &tm);
2294 y = clientArea->top;
2295 do {
2296 breakCount = 0;
2297 while (*str == tm.tmBreakChar) str++; /* skip leading break chars */
2298 pFirstChar = str;
2300 do {
2301 pLastChar = str;
2303 /* if not at the end of the string, ... */
2304 if (*str == '\0') break;
2305 /* ... add the next word to the current extent */
2306 while (*str != '\0' && *str++ != tm.tmBreakChar);
2307 breakCount++;
2308 SetTextJustification(hdc, 0, 0);
2309 GetTextExtentPoint32A(hdc, pFirstChar, str - pFirstChar - 1, &size);
2310 } while ((int) size.cx < areaWidth);
2312 /* ignore trailing break chars */
2313 breakCount--;
2314 while (*(pLastChar - 1) == tm.tmBreakChar)
2316 pLastChar--;
2317 breakCount--;
2320 if (*str == '\0' || breakCount <= 0) pLastChar = str;
2322 SetTextJustification(hdc, 0, 0);
2323 GetTextExtentPoint32A(hdc, pFirstChar, pLastChar - pFirstChar, &size);
2325 /* do not justify the last extent */
2326 if (*str != '\0' && breakCount > 0)
2328 SetTextJustification(hdc, areaWidth - size.cx, breakCount);
2329 GetTextExtentPoint32A(hdc, pFirstChar, pLastChar - pFirstChar, &size);
2330 if (size.cx != areaWidth && nErrors < sizeof(error)/sizeof(error[0]) - 1)
2332 error[nErrors].start = pFirstChar;
2333 error[nErrors].len = pLastChar - pFirstChar;
2334 error[nErrors].GetTextExtentExPointWWidth = size.cx;
2335 nErrors++;
2339 y += size.cy;
2340 str = pLastChar;
2341 } while (*str && y < clientArea->bottom);
2343 for (e = 0; e < nErrors; e++)
2345 /* The width returned by GetTextExtentPoint32() is exactly the same
2346 returned by GetTextExtentExPointW() - see dlls/gdi32/font.c */
2347 ok(error[e].GetTextExtentExPointWWidth == areaWidth,
2348 "GetTextExtentPointW() for \"%.*s\" should have returned a width of %d, not %d.\n",
2349 error[e].len, error[e].start, areaWidth, error[e].GetTextExtentExPointWWidth);
2353 static void test_SetTextJustification(void)
2355 HDC hdc;
2356 RECT clientArea;
2357 LOGFONTA lf;
2358 HFONT hfont;
2359 HWND hwnd;
2360 SIZE size, expect;
2361 int i;
2362 WORD indices[2];
2363 static const char testText[] =
2364 "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do "
2365 "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut "
2366 "enim ad minim veniam, quis nostrud exercitation ullamco laboris "
2367 "nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in "
2368 "reprehenderit in voluptate velit esse cillum dolore eu fugiat "
2369 "nulla pariatur. Excepteur sint occaecat cupidatat non proident, "
2370 "sunt in culpa qui officia deserunt mollit anim id est laborum.";
2372 hwnd = CreateWindowExA(0, "static", "", WS_POPUP, 0,0, 400,400, 0, 0, 0, NULL);
2373 GetClientRect( hwnd, &clientArea );
2374 hdc = GetDC( hwnd );
2376 if (!is_font_installed("Times New Roman"))
2378 skip("Times New Roman is not installed\n");
2379 return;
2382 memset(&lf, 0, sizeof lf);
2383 lf.lfCharSet = ANSI_CHARSET;
2384 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
2385 lf.lfWeight = FW_DONTCARE;
2386 lf.lfHeight = 20;
2387 lf.lfQuality = DEFAULT_QUALITY;
2388 lstrcpyA(lf.lfFaceName, "Times New Roman");
2389 hfont = create_font("Times New Roman", &lf);
2390 SelectObject(hdc, hfont);
2392 testJustification(hdc, testText, &clientArea);
2394 if (!pGetGlyphIndicesA || !pGetTextExtentExPointI) goto done;
2395 pGetGlyphIndicesA( hdc, "A ", 2, indices, 0 );
2397 SetTextJustification(hdc, 0, 0);
2398 GetTextExtentPoint32A(hdc, " ", 1, &expect);
2399 GetTextExtentPoint32A(hdc, " ", 3, &size);
2400 ok( size.cx == 3 * expect.cx, "wrong size %d/%d\n", size.cx, expect.cx );
2401 SetTextJustification(hdc, 4, 1);
2402 GetTextExtentPoint32A(hdc, " ", 1, &size);
2403 ok( size.cx == expect.cx + 4, "wrong size %d/%d\n", size.cx, expect.cx );
2404 SetTextJustification(hdc, 9, 2);
2405 GetTextExtentPoint32A(hdc, " ", 2, &size);
2406 ok( size.cx == 2 * expect.cx + 9, "wrong size %d/%d\n", size.cx, expect.cx );
2407 SetTextJustification(hdc, 7, 3);
2408 GetTextExtentPoint32A(hdc, " ", 3, &size);
2409 ok( size.cx == 3 * expect.cx + 7, "wrong size %d/%d\n", size.cx, expect.cx );
2410 SetTextJustification(hdc, 7, 3);
2411 SetTextCharacterExtra(hdc, 2 );
2412 GetTextExtentPoint32A(hdc, " ", 3, &size);
2413 ok( size.cx == 3 * (expect.cx + 2) + 7, "wrong size %d/%d\n", size.cx, expect.cx );
2414 SetTextJustification(hdc, 0, 0);
2415 SetTextCharacterExtra(hdc, 0);
2416 size.cx = size.cy = 1234;
2417 GetTextExtentPoint32A(hdc, " ", 0, &size);
2418 ok( size.cx == 0 && size.cy == 0, "wrong size %d,%d\n", size.cx, size.cy );
2419 pGetTextExtentExPointI(hdc, indices, 2, -1, NULL, NULL, &expect);
2420 SetTextJustification(hdc, 5, 1);
2421 pGetTextExtentExPointI(hdc, indices, 2, -1, NULL, NULL, &size);
2422 ok( size.cx == expect.cx + 5, "wrong size %d/%d\n", size.cx, expect.cx );
2423 SetTextJustification(hdc, 0, 0);
2425 SetMapMode( hdc, MM_ANISOTROPIC );
2426 SetWindowExtEx( hdc, 2, 2, NULL );
2427 GetClientRect( hwnd, &clientArea );
2428 DPtoLP( hdc, (POINT *)&clientArea, 2 );
2429 testJustification(hdc, testText, &clientArea);
2431 GetTextExtentPoint32A(hdc, "A", 1, &expect);
2432 for (i = 0; i < 10; i++)
2434 SetTextCharacterExtra(hdc, i);
2435 GetTextExtentPoint32A(hdc, "A", 1, &size);
2436 ok( size.cx == expect.cx + i, "wrong size %d/%d+%d\n", size.cx, expect.cx, i );
2438 SetTextCharacterExtra(hdc, 0);
2439 pGetTextExtentExPointI(hdc, indices, 1, -1, NULL, NULL, &expect);
2440 for (i = 0; i < 10; i++)
2442 SetTextCharacterExtra(hdc, i);
2443 pGetTextExtentExPointI(hdc, indices, 1, -1, NULL, NULL, &size);
2444 ok( size.cx == expect.cx + i, "wrong size %d/%d+%d\n", size.cx, expect.cx, i );
2446 SetTextCharacterExtra(hdc, 0);
2448 SetViewportExtEx( hdc, 3, 3, NULL );
2449 GetClientRect( hwnd, &clientArea );
2450 DPtoLP( hdc, (POINT *)&clientArea, 2 );
2451 testJustification(hdc, testText, &clientArea);
2453 GetTextExtentPoint32A(hdc, "A", 1, &expect);
2454 for (i = 0; i < 10; i++)
2456 SetTextCharacterExtra(hdc, i);
2457 GetTextExtentPoint32A(hdc, "A", 1, &size);
2458 ok( size.cx == expect.cx + i, "wrong size %d/%d+%d\n", size.cx, expect.cx, i );
2461 done:
2462 DeleteObject(hfont);
2463 ReleaseDC(hwnd, hdc);
2464 DestroyWindow(hwnd);
2467 static BOOL get_glyph_indices(INT charset, UINT code_page, WORD *idx, UINT count, BOOL unicode)
2469 HDC hdc;
2470 LOGFONTA lf;
2471 HFONT hfont, hfont_old;
2472 CHARSETINFO csi;
2473 FONTSIGNATURE fs;
2474 INT cs;
2475 DWORD i, ret;
2476 char name[64];
2478 assert(count <= 128);
2480 memset(&lf, 0, sizeof(lf));
2482 lf.lfCharSet = charset;
2483 lf.lfHeight = 10;
2484 lstrcpyA(lf.lfFaceName, "Arial");
2485 SetLastError(0xdeadbeef);
2486 hfont = CreateFontIndirectA(&lf);
2487 ok(hfont != 0, "CreateFontIndirectA error %u\n", GetLastError());
2489 hdc = GetDC(0);
2490 hfont_old = SelectObject(hdc, hfont);
2492 cs = GetTextCharsetInfo(hdc, &fs, 0);
2493 ok(cs == charset, "expected %d, got %d\n", charset, cs);
2495 SetLastError(0xdeadbeef);
2496 ret = GetTextFaceA(hdc, sizeof(name), name);
2497 ok(ret, "GetTextFaceA error %u\n", GetLastError());
2499 if (charset == SYMBOL_CHARSET)
2501 ok(strcmp("Arial", name), "face name should NOT be Arial\n");
2502 ok(fs.fsCsb[0] & (1u << 31), "symbol encoding should be available\n");
2504 else
2506 ok(!strcmp("Arial", name), "face name should be Arial, not %s\n", name);
2507 ok(!(fs.fsCsb[0] & (1u << 31)), "symbol encoding should NOT be available\n");
2510 if (!TranslateCharsetInfo((DWORD *)(INT_PTR)cs, &csi, TCI_SRCCHARSET))
2512 trace("Can't find codepage for charset %d\n", cs);
2513 ReleaseDC(0, hdc);
2514 return FALSE;
2516 ok(csi.ciACP == code_page, "expected %d, got %d\n", code_page, csi.ciACP);
2518 if (pGdiGetCodePage != NULL && pGdiGetCodePage(hdc) != code_page)
2520 skip("Font code page %d, looking for code page %d\n",
2521 pGdiGetCodePage(hdc), code_page);
2522 ReleaseDC(0, hdc);
2523 return FALSE;
2526 if (unicode)
2528 char ansi_buf[128];
2529 WCHAR unicode_buf[128];
2531 for (i = 0; i < count; i++) ansi_buf[i] = (BYTE)(i + 128);
2533 MultiByteToWideChar(code_page, 0, ansi_buf, count, unicode_buf, count);
2535 SetLastError(0xdeadbeef);
2536 ret = pGetGlyphIndicesW(hdc, unicode_buf, count, idx, 0);
2537 ok(ret == count, "GetGlyphIndicesW expected %d got %d, error %u\n",
2538 count, ret, GetLastError());
2540 else
2542 char ansi_buf[128];
2544 for (i = 0; i < count; i++) ansi_buf[i] = (BYTE)(i + 128);
2546 SetLastError(0xdeadbeef);
2547 ret = pGetGlyphIndicesA(hdc, ansi_buf, count, idx, 0);
2548 ok(ret == count, "GetGlyphIndicesA expected %d got %d, error %u\n",
2549 count, ret, GetLastError());
2552 SelectObject(hdc, hfont_old);
2553 DeleteObject(hfont);
2555 ReleaseDC(0, hdc);
2557 return TRUE;
2560 static void test_font_charset(void)
2562 static struct charset_data
2564 INT charset;
2565 UINT code_page;
2566 WORD font_idxA[128], font_idxW[128];
2567 } cd[] =
2569 { ANSI_CHARSET, 1252 },
2570 { RUSSIAN_CHARSET, 1251 },
2571 { SYMBOL_CHARSET, CP_SYMBOL } /* keep it as the last one */
2573 int i;
2575 if (!pGetGlyphIndicesA || !pGetGlyphIndicesW)
2577 win_skip("Skipping the font charset test on a Win9x platform\n");
2578 return;
2581 if (!is_font_installed("Arial"))
2583 skip("Arial is not installed\n");
2584 return;
2587 for (i = 0; i < sizeof(cd)/sizeof(cd[0]); i++)
2589 if (cd[i].charset == SYMBOL_CHARSET)
2591 if (!is_font_installed("Symbol") && !is_font_installed("Wingdings"))
2593 skip("Symbol or Wingdings is not installed\n");
2594 break;
2597 if (get_glyph_indices(cd[i].charset, cd[i].code_page, cd[i].font_idxA, 128, FALSE) &&
2598 get_glyph_indices(cd[i].charset, cd[i].code_page, cd[i].font_idxW, 128, TRUE))
2599 ok(!memcmp(cd[i].font_idxA, cd[i].font_idxW, 128*sizeof(WORD)), "%d: indices don't match\n", i);
2602 ok(memcmp(cd[0].font_idxW, cd[1].font_idxW, 128*sizeof(WORD)), "0 vs 1: indices shouldn't match\n");
2603 if (i > 2)
2605 ok(memcmp(cd[0].font_idxW, cd[2].font_idxW, 128*sizeof(WORD)), "0 vs 2: indices shouldn't match\n");
2606 ok(memcmp(cd[1].font_idxW, cd[2].font_idxW, 128*sizeof(WORD)), "1 vs 2: indices shouldn't match\n");
2608 else
2609 skip("Symbol or Wingdings is not installed\n");
2612 static void test_GdiGetCodePage(void)
2614 static const struct _matching_data
2616 UINT current_codepage;
2617 LPCSTR lfFaceName;
2618 UCHAR lfCharSet;
2619 UINT expected_codepage;
2620 } matching_data[] = {
2621 {1251, "Arial", ANSI_CHARSET, 1252},
2622 {1251, "Tahoma", ANSI_CHARSET, 1252},
2624 {1252, "Arial", ANSI_CHARSET, 1252},
2625 {1252, "Tahoma", ANSI_CHARSET, 1252},
2627 {1253, "Arial", ANSI_CHARSET, 1252},
2628 {1253, "Tahoma", ANSI_CHARSET, 1252},
2630 { 932, "Arial", ANSI_CHARSET, 1252}, /* Japanese Windows returns 1252, not 932 */
2631 { 932, "Tahoma", ANSI_CHARSET, 1252},
2632 { 932, "MS UI Gothic", ANSI_CHARSET, 1252},
2634 { 936, "Arial", ANSI_CHARSET, 936},
2635 { 936, "Tahoma", ANSI_CHARSET, 936},
2636 { 936, "Simsun", ANSI_CHARSET, 936},
2638 { 949, "Arial", ANSI_CHARSET, 949},
2639 { 949, "Tahoma", ANSI_CHARSET, 949},
2640 { 949, "Gulim", ANSI_CHARSET, 949},
2642 { 950, "Arial", ANSI_CHARSET, 950},
2643 { 950, "Tahoma", ANSI_CHARSET, 950},
2644 { 950, "PMingLiU", ANSI_CHARSET, 950},
2646 HDC hdc;
2647 LOGFONTA lf;
2648 HFONT hfont;
2649 UINT charset, acp;
2650 DWORD codepage;
2651 int i;
2653 if (!pGdiGetCodePage)
2655 skip("GdiGetCodePage not available on this platform\n");
2656 return;
2659 acp = GetACP();
2661 for (i = 0; i < sizeof(matching_data) / sizeof(struct _matching_data); i++)
2663 /* only test data matched current locale codepage */
2664 if (matching_data[i].current_codepage != acp)
2665 continue;
2667 if (!is_font_installed(matching_data[i].lfFaceName))
2669 skip("%s is not installed\n", matching_data[i].lfFaceName);
2670 continue;
2673 hdc = GetDC(0);
2675 memset(&lf, 0, sizeof(lf));
2676 lf.lfHeight = -16;
2677 lf.lfCharSet = matching_data[i].lfCharSet;
2678 lstrcpyA(lf.lfFaceName, matching_data[i].lfFaceName);
2679 hfont = CreateFontIndirectA(&lf);
2680 ok(hfont != 0, "CreateFontIndirectA error %u\n", GetLastError());
2682 hfont = SelectObject(hdc, hfont);
2683 charset = GetTextCharset(hdc);
2684 codepage = pGdiGetCodePage(hdc);
2685 trace("acp=%d, lfFaceName=%s, lfCharSet=%d, GetTextCharset=%d, GdiGetCodePage=%d, expected codepage=%d\n",
2686 acp, lf.lfFaceName, lf.lfCharSet, charset, codepage, matching_data[i].expected_codepage);
2687 ok(codepage == matching_data[i].expected_codepage,
2688 "GdiGetCodePage should have returned %d, got %d\n", matching_data[i].expected_codepage, codepage);
2690 hfont = SelectObject(hdc, hfont);
2691 DeleteObject(hfont);
2693 /* CLIP_DFA_DISABLE turns off the font association */
2694 lf.lfClipPrecision = CLIP_DFA_DISABLE;
2695 hfont = CreateFontIndirectA(&lf);
2696 ok(hfont != 0, "CreateFontIndirectA error %u\n", GetLastError());
2698 hfont = SelectObject(hdc, hfont);
2699 charset = GetTextCharset(hdc);
2700 codepage = pGdiGetCodePage(hdc);
2701 trace("acp=%d, lfFaceName=%s, lfCharSet=%d, GetTextCharset=%d, GdiGetCodePage=%d\n",
2702 acp, lf.lfFaceName, lf.lfCharSet, charset, codepage);
2703 ok(codepage == 1252, "GdiGetCodePage returned %d\n", codepage);
2705 hfont = SelectObject(hdc, hfont);
2706 DeleteObject(hfont);
2708 ReleaseDC(NULL, hdc);
2712 static void test_GetFontUnicodeRanges(void)
2714 LOGFONTA lf;
2715 HDC hdc;
2716 HFONT hfont, hfont_old;
2717 DWORD size;
2718 GLYPHSET *gs;
2719 DWORD i;
2721 if (!pGetFontUnicodeRanges)
2723 win_skip("GetFontUnicodeRanges not available before W2K\n");
2724 return;
2727 memset(&lf, 0, sizeof(lf));
2728 lstrcpyA(lf.lfFaceName, "Arial");
2729 hfont = create_font("Arial", &lf);
2731 hdc = GetDC(0);
2732 hfont_old = SelectObject(hdc, hfont);
2734 size = pGetFontUnicodeRanges(NULL, NULL);
2735 ok(!size, "GetFontUnicodeRanges succeeded unexpectedly\n");
2737 size = pGetFontUnicodeRanges(hdc, NULL);
2738 ok(size, "GetFontUnicodeRanges failed unexpectedly\n");
2740 gs = HeapAlloc(GetProcessHeap(), 0, size);
2742 size = pGetFontUnicodeRanges(hdc, gs);
2743 ok(size, "GetFontUnicodeRanges failed\n");
2745 if (0) /* Disabled to limit console spam */
2746 for (i = 0; i < gs->cRanges; i++)
2747 trace("%03d wcLow %04x cGlyphs %u\n", i, gs->ranges[i].wcLow, gs->ranges[i].cGlyphs);
2748 trace("found %u ranges\n", gs->cRanges);
2750 HeapFree(GetProcessHeap(), 0, gs);
2752 SelectObject(hdc, hfont_old);
2753 DeleteObject(hfont);
2754 ReleaseDC(NULL, hdc);
2757 struct enum_font_data
2759 int total, size;
2760 LOGFONTA *lf;
2763 struct enum_fullname_data
2765 int total, size;
2766 ENUMLOGFONTA *elf;
2769 struct enum_font_dataW
2771 int total, size;
2772 LOGFONTW *lf;
2775 static INT CALLBACK arial_enum_proc(const LOGFONTA *lf, const TEXTMETRICA *tm, DWORD type, LPARAM lParam)
2777 struct enum_font_data *efd = (struct enum_font_data *)lParam;
2778 const NEWTEXTMETRICA *ntm = (const NEWTEXTMETRICA *)tm;
2780 ok(lf->lfHeight == tm->tmHeight, "lfHeight %d != tmHeight %d\n", lf->lfHeight, tm->tmHeight);
2781 ok(lf->lfHeight > 0 && lf->lfHeight < 200, "enumerated font height %d\n", lf->lfHeight);
2783 if (type != TRUETYPE_FONTTYPE) return 1;
2785 ok(ntm->ntmCellHeight + ntm->ntmCellHeight/5 >= ntm->ntmSizeEM, "ntmCellHeight %d should be close to ntmSizeEM %d\n", ntm->ntmCellHeight, ntm->ntmSizeEM);
2787 if (0) /* Disabled to limit console spam */
2788 trace("enumed font \"%s\", charset %d, height %d, weight %d, italic %d\n",
2789 lf->lfFaceName, lf->lfCharSet, lf->lfHeight, lf->lfWeight, lf->lfItalic);
2790 if (efd->total >= efd->size)
2792 efd->size = max( (efd->total + 1) * 2, 256 );
2793 efd->lf = heap_realloc( efd->lf, efd->size * sizeof(*efd->lf) );
2794 if (!efd->lf) return 0;
2796 efd->lf[efd->total++] = *lf;
2798 return 1;
2801 static INT CALLBACK arial_enum_procw(const LOGFONTW *lf, const TEXTMETRICW *tm, DWORD type, LPARAM lParam)
2803 struct enum_font_dataW *efd = (struct enum_font_dataW *)lParam;
2804 const NEWTEXTMETRICW *ntm = (const NEWTEXTMETRICW *)tm;
2806 ok(lf->lfHeight == tm->tmHeight, "lfHeight %d != tmHeight %d\n", lf->lfHeight, tm->tmHeight);
2807 ok(lf->lfHeight > 0 && lf->lfHeight < 200, "enumerated font height %d\n", lf->lfHeight);
2809 if (type != TRUETYPE_FONTTYPE) return 1;
2811 ok(ntm->ntmCellHeight + ntm->ntmCellHeight/5 >= ntm->ntmSizeEM, "ntmCellHeight %d should be close to ntmSizeEM %d\n", ntm->ntmCellHeight, ntm->ntmSizeEM);
2813 if (0) /* Disabled to limit console spam */
2814 trace("enumed font %s, charset %d, height %d, weight %d, italic %d\n",
2815 wine_dbgstr_w(lf->lfFaceName), lf->lfCharSet, lf->lfHeight, lf->lfWeight, lf->lfItalic);
2816 if (efd->total >= efd->size)
2818 efd->size = max( (efd->total + 1) * 2, 256 );
2819 efd->lf = heap_realloc( efd->lf, efd->size * sizeof(*efd->lf) );
2820 if (!efd->lf) return 0;
2822 efd->lf[efd->total++] = *lf;
2824 return 1;
2827 static void get_charset_stats(struct enum_font_data *efd,
2828 int *ansi_charset, int *symbol_charset,
2829 int *russian_charset)
2831 int i;
2833 *ansi_charset = 0;
2834 *symbol_charset = 0;
2835 *russian_charset = 0;
2837 for (i = 0; i < efd->total; i++)
2839 switch (efd->lf[i].lfCharSet)
2841 case ANSI_CHARSET:
2842 (*ansi_charset)++;
2843 break;
2844 case SYMBOL_CHARSET:
2845 (*symbol_charset)++;
2846 break;
2847 case RUSSIAN_CHARSET:
2848 (*russian_charset)++;
2849 break;
2854 static void get_charset_statsW(struct enum_font_dataW *efd,
2855 int *ansi_charset, int *symbol_charset,
2856 int *russian_charset)
2858 int i;
2860 *ansi_charset = 0;
2861 *symbol_charset = 0;
2862 *russian_charset = 0;
2864 for (i = 0; i < efd->total; i++)
2866 switch (efd->lf[i].lfCharSet)
2868 case ANSI_CHARSET:
2869 (*ansi_charset)++;
2870 break;
2871 case SYMBOL_CHARSET:
2872 (*symbol_charset)++;
2873 break;
2874 case RUSSIAN_CHARSET:
2875 (*russian_charset)++;
2876 break;
2881 static void test_EnumFontFamilies(const char *font_name, INT font_charset)
2883 struct enum_font_data efd;
2884 struct enum_font_dataW efdw;
2885 LOGFONTA lf;
2886 HDC hdc;
2887 int i, ret, ansi_charset, symbol_charset, russian_charset;
2889 trace("Testing font %s, charset %d\n", *font_name ? font_name : "<empty>", font_charset);
2891 if (*font_name && !is_truetype_font_installed(font_name))
2893 skip("%s is not installed\n", font_name);
2894 return;
2896 memset( &efd, 0, sizeof(efd) );
2897 memset( &efdw, 0, sizeof(efdw) );
2899 hdc = GetDC(0);
2901 /* Observed behaviour: EnumFontFamilies enumerates aliases like "Arial Cyr"
2902 * while EnumFontFamiliesEx doesn't.
2904 if (!*font_name && font_charset == DEFAULT_CHARSET) /* do it only once */
2907 * Use EnumFontFamiliesW since win98 crashes when the
2908 * second parameter is NULL using EnumFontFamilies
2910 efdw.total = 0;
2911 SetLastError(0xdeadbeef);
2912 ret = EnumFontFamiliesW(hdc, NULL, arial_enum_procw, (LPARAM)&efdw);
2913 ok(ret || GetLastError() == ERROR_CALL_NOT_IMPLEMENTED, "EnumFontFamiliesW error %u\n", GetLastError());
2914 if(ret)
2916 get_charset_statsW(&efdw, &ansi_charset, &symbol_charset, &russian_charset);
2917 trace("enumerated ansi %d, symbol %d, russian %d fonts for NULL\n",
2918 ansi_charset, symbol_charset, russian_charset);
2919 ok(efdw.total > 0, "fonts enumerated: NULL\n");
2920 ok(ansi_charset > 0, "NULL family should enumerate ANSI_CHARSET\n");
2921 ok(symbol_charset > 0, "NULL family should enumerate SYMBOL_CHARSET\n");
2922 ok(russian_charset > 0 ||
2923 broken(russian_charset == 0), /* NT4 */
2924 "NULL family should enumerate RUSSIAN_CHARSET\n");
2927 efdw.total = 0;
2928 SetLastError(0xdeadbeef);
2929 ret = EnumFontFamiliesExW(hdc, NULL, arial_enum_procw, (LPARAM)&efdw, 0);
2930 ok(ret || GetLastError() == ERROR_CALL_NOT_IMPLEMENTED, "EnumFontFamiliesExW error %u\n", GetLastError());
2931 if(ret)
2933 get_charset_statsW(&efdw, &ansi_charset, &symbol_charset, &russian_charset);
2934 trace("enumerated ansi %d, symbol %d, russian %d fonts for NULL\n",
2935 ansi_charset, symbol_charset, russian_charset);
2936 ok(efdw.total > 0, "fonts enumerated: NULL\n");
2937 ok(ansi_charset > 0, "NULL family should enumerate ANSI_CHARSET\n");
2938 ok(symbol_charset > 0, "NULL family should enumerate SYMBOL_CHARSET\n");
2939 ok(russian_charset > 0, "NULL family should enumerate RUSSIAN_CHARSET\n");
2943 efd.total = 0;
2944 SetLastError(0xdeadbeef);
2945 ret = EnumFontFamiliesA(hdc, font_name, arial_enum_proc, (LPARAM)&efd);
2946 ok(ret, "EnumFontFamilies error %u\n", GetLastError());
2947 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
2948 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s\n",
2949 ansi_charset, symbol_charset, russian_charset,
2950 *font_name ? font_name : "<empty>");
2951 if (*font_name)
2952 ok(efd.total > 0, "no fonts enumerated: %s\n", font_name);
2953 else
2954 ok(!efd.total, "no fonts should be enumerated for empty font_name\n");
2955 for (i = 0; i < efd.total; i++)
2957 /* FIXME: remove completely once Wine is fixed */
2958 todo_wine_if(efd.lf[i].lfCharSet != font_charset)
2959 ok(efd.lf[i].lfCharSet == font_charset, "%d: got charset %d\n", i, efd.lf[i].lfCharSet);
2960 ok(!strcmp(efd.lf[i].lfFaceName, font_name), "expected %s, got %s\n",
2961 font_name, efd.lf[i].lfFaceName);
2964 memset(&lf, 0, sizeof(lf));
2965 lf.lfCharSet = ANSI_CHARSET;
2966 strcpy(lf.lfFaceName, font_name);
2967 efd.total = 0;
2968 SetLastError(0xdeadbeef);
2969 ret = EnumFontFamiliesExA(hdc, &lf, arial_enum_proc, (LPARAM)&efd, 0);
2970 ok(ret, "EnumFontFamiliesEx error %u\n", GetLastError());
2971 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
2972 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s ANSI_CHARSET\n",
2973 ansi_charset, symbol_charset, russian_charset,
2974 *font_name ? font_name : "<empty>");
2975 if (font_charset == SYMBOL_CHARSET)
2977 if (*font_name)
2978 ok(efd.total == 0, "no fonts should be enumerated: %s ANSI_CHARSET\n", font_name);
2979 else
2980 ok(efd.total > 0, "no fonts enumerated: %s\n", font_name);
2982 else
2984 ok(efd.total > 0, "no fonts enumerated: %s ANSI_CHARSET\n", font_name);
2985 for (i = 0; i < efd.total; i++)
2987 ok(efd.lf[i].lfCharSet == ANSI_CHARSET, "%d: got charset %d\n", i, efd.lf[i].lfCharSet);
2988 if (*font_name)
2989 ok(!strcmp(efd.lf[i].lfFaceName, font_name), "expected %s, got %s\n",
2990 font_name, efd.lf[i].lfFaceName);
2994 /* DEFAULT_CHARSET should enumerate all available charsets */
2995 memset(&lf, 0, sizeof(lf));
2996 lf.lfCharSet = DEFAULT_CHARSET;
2997 strcpy(lf.lfFaceName, font_name);
2998 efd.total = 0;
2999 SetLastError(0xdeadbeef);
3000 EnumFontFamiliesExA(hdc, &lf, arial_enum_proc, (LPARAM)&efd, 0);
3001 ok(ret, "EnumFontFamiliesEx error %u\n", GetLastError());
3002 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
3003 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s DEFAULT_CHARSET\n",
3004 ansi_charset, symbol_charset, russian_charset,
3005 *font_name ? font_name : "<empty>");
3006 ok(efd.total > 0, "no fonts enumerated: %s DEFAULT_CHARSET\n", font_name);
3007 for (i = 0; i < efd.total; i++)
3009 if (*font_name)
3010 ok(!strcmp(efd.lf[i].lfFaceName, font_name), "expected %s, got %s\n",
3011 font_name, efd.lf[i].lfFaceName);
3013 if (*font_name)
3015 switch (font_charset)
3017 case ANSI_CHARSET:
3018 ok(ansi_charset > 0,
3019 "ANSI_CHARSET should enumerate ANSI_CHARSET for %s\n", font_name);
3020 ok(!symbol_charset,
3021 "ANSI_CHARSET should NOT enumerate SYMBOL_CHARSET for %s\n", font_name);
3022 ok(russian_charset > 0,
3023 "ANSI_CHARSET should enumerate RUSSIAN_CHARSET for %s\n", font_name);
3024 break;
3025 case SYMBOL_CHARSET:
3026 ok(!ansi_charset,
3027 "SYMBOL_CHARSET should NOT enumerate ANSI_CHARSET for %s\n", font_name);
3028 ok(symbol_charset,
3029 "SYMBOL_CHARSET should enumerate SYMBOL_CHARSET for %s\n", font_name);
3030 ok(!russian_charset,
3031 "SYMBOL_CHARSET should NOT enumerate RUSSIAN_CHARSET for %s\n", font_name);
3032 break;
3033 case DEFAULT_CHARSET:
3034 ok(ansi_charset > 0,
3035 "DEFAULT_CHARSET should enumerate ANSI_CHARSET for %s\n", font_name);
3036 ok(symbol_charset > 0,
3037 "DEFAULT_CHARSET should enumerate SYMBOL_CHARSET for %s\n", font_name);
3038 ok(russian_charset > 0,
3039 "DEFAULT_CHARSET should enumerate RUSSIAN_CHARSET for %s\n", font_name);
3040 break;
3043 else
3045 ok(ansi_charset > 0,
3046 "DEFAULT_CHARSET should enumerate ANSI_CHARSET for %s\n", *font_name ? font_name : "<empty>");
3047 ok(symbol_charset > 0,
3048 "DEFAULT_CHARSET should enumerate SYMBOL_CHARSET for %s\n", *font_name ? font_name : "<empty>");
3049 ok(russian_charset > 0,
3050 "DEFAULT_CHARSET should enumerate RUSSIAN_CHARSET for %s\n", *font_name ? font_name : "<empty>");
3053 memset(&lf, 0, sizeof(lf));
3054 lf.lfCharSet = SYMBOL_CHARSET;
3055 strcpy(lf.lfFaceName, font_name);
3056 efd.total = 0;
3057 SetLastError(0xdeadbeef);
3058 EnumFontFamiliesExA(hdc, &lf, arial_enum_proc, (LPARAM)&efd, 0);
3059 ok(ret, "EnumFontFamiliesEx error %u\n", GetLastError());
3060 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
3061 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s SYMBOL_CHARSET\n",
3062 ansi_charset, symbol_charset, russian_charset,
3063 *font_name ? font_name : "<empty>");
3064 if (*font_name && font_charset == ANSI_CHARSET)
3065 ok(efd.total == 0, "no fonts should be enumerated: %s SYMBOL_CHARSET\n", font_name);
3066 else
3068 ok(efd.total > 0, "no fonts enumerated: %s SYMBOL_CHARSET\n", font_name);
3069 for (i = 0; i < efd.total; i++)
3071 ok(efd.lf[i].lfCharSet == SYMBOL_CHARSET, "%d: got charset %d\n", i, efd.lf[i].lfCharSet);
3072 if (*font_name)
3073 ok(!strcmp(efd.lf[i].lfFaceName, font_name), "expected %s, got %s\n",
3074 font_name, efd.lf[i].lfFaceName);
3077 ok(!ansi_charset,
3078 "SYMBOL_CHARSET should NOT enumerate ANSI_CHARSET for %s\n", *font_name ? font_name : "<empty>");
3079 ok(symbol_charset > 0,
3080 "SYMBOL_CHARSET should enumerate SYMBOL_CHARSET for %s\n", *font_name ? font_name : "<empty>");
3081 ok(!russian_charset,
3082 "SYMBOL_CHARSET should NOT enumerate RUSSIAN_CHARSET for %s\n", *font_name ? font_name : "<empty>");
3085 ReleaseDC(0, hdc);
3087 heap_free( efd.lf );
3088 heap_free( efdw.lf );
3091 static INT CALLBACK enum_multi_charset_font_proc(const LOGFONTA *lf, const TEXTMETRICA *tm, DWORD type, LPARAM lParam)
3093 const NEWTEXTMETRICEXA *ntm = (const NEWTEXTMETRICEXA *)tm;
3094 LOGFONTA *target = (LOGFONTA *)lParam;
3095 const DWORD valid_bits = 0x003f01ff;
3096 CHARSETINFO csi;
3097 DWORD fs;
3099 if (type != TRUETYPE_FONTTYPE) return TRUE;
3101 if (TranslateCharsetInfo(ULongToPtr(target->lfCharSet), &csi, TCI_SRCCHARSET)) {
3102 fs = ntm->ntmFontSig.fsCsb[0] & valid_bits;
3103 if ((fs & csi.fs.fsCsb[0]) && (fs & ~csi.fs.fsCsb[0]) && (fs & FS_LATIN1)) {
3104 *target = *lf;
3105 return FALSE;
3109 return TRUE;
3112 static INT CALLBACK enum_font_data_proc(const LOGFONTA *lf, const TEXTMETRICA *ntm, DWORD type, LPARAM lParam)
3114 struct enum_font_data *efd = (struct enum_font_data *)lParam;
3116 if (type != TRUETYPE_FONTTYPE) return 1;
3118 if (efd->total >= efd->size)
3120 efd->size = max( (efd->total + 1) * 2, 256 );
3121 efd->lf = heap_realloc( efd->lf, efd->size * sizeof(*efd->lf) );
3122 if (!efd->lf) return 0;
3124 efd->lf[efd->total++] = *lf;
3126 return 1;
3129 static INT CALLBACK enum_fullname_data_proc(const LOGFONTA *lf, const TEXTMETRICA *ntm, DWORD type, LPARAM lParam)
3131 struct enum_fullname_data *efnd = (struct enum_fullname_data *)lParam;
3133 if (type != TRUETYPE_FONTTYPE) return 1;
3135 if (efnd->total >= efnd->size)
3137 efnd->size = max( (efnd->total + 1) * 2, 256 );
3138 efnd->elf = heap_realloc( efnd->elf, efnd->size * sizeof(*efnd->elf) );
3139 if (!efnd->elf) return 0;
3141 efnd->elf[efnd->total++] = *(ENUMLOGFONTA *)lf;
3143 return 1;
3146 static void test_EnumFontFamiliesEx_default_charset(void)
3148 struct enum_font_data efd;
3149 LOGFONTA target, enum_font;
3150 UINT acp;
3151 HDC hdc;
3152 CHARSETINFO csi;
3154 acp = GetACP();
3155 if (!TranslateCharsetInfo(ULongToPtr(acp), &csi, TCI_SRCCODEPAGE)) {
3156 skip("TranslateCharsetInfo failed for code page %u.\n", acp);
3157 return;
3160 hdc = GetDC(0);
3161 memset(&enum_font, 0, sizeof(enum_font));
3162 enum_font.lfCharSet = csi.ciCharset;
3163 target.lfFaceName[0] = '\0';
3164 target.lfCharSet = csi.ciCharset;
3165 EnumFontFamiliesExA(hdc, &enum_font, enum_multi_charset_font_proc, (LPARAM)&target, 0);
3166 if (target.lfFaceName[0] == '\0') {
3167 skip("suitable font isn't found for charset %d.\n", enum_font.lfCharSet);
3168 return;
3170 if (acp == 874 || acp == 1255 || acp == 1256) {
3171 /* these codepage use complex script, expecting ANSI_CHARSET here. */
3172 target.lfCharSet = ANSI_CHARSET;
3175 memset(&efd, 0, sizeof(efd));
3176 memset(&enum_font, 0, sizeof(enum_font));
3177 strcpy(enum_font.lfFaceName, target.lfFaceName);
3178 enum_font.lfCharSet = DEFAULT_CHARSET;
3179 EnumFontFamiliesExA(hdc, &enum_font, enum_font_data_proc, (LPARAM)&efd, 0);
3180 ReleaseDC(0, hdc);
3182 trace("'%s' has %d charsets.\n", target.lfFaceName, efd.total);
3183 if (efd.total < 2)
3184 ok(0, "EnumFontFamilies is broken. Expected >= 2, got %d.\n", efd.total);
3185 else
3186 ok(efd.lf[0].lfCharSet == target.lfCharSet,
3187 "(%s) got charset %d expected %d\n",
3188 efd.lf[0].lfFaceName, efd.lf[0].lfCharSet, target.lfCharSet);
3190 heap_free(efd.lf);
3191 return;
3194 static void test_negative_width(HDC hdc, const LOGFONTA *lf)
3196 HFONT hfont, hfont_prev;
3197 DWORD ret;
3198 GLYPHMETRICS gm1, gm2;
3199 LOGFONTA lf2 = *lf;
3200 WORD idx;
3202 if(!pGetGlyphIndicesA)
3203 return;
3205 /* negative widths are handled just as positive ones */
3206 lf2.lfWidth = -lf->lfWidth;
3208 SetLastError(0xdeadbeef);
3209 hfont = CreateFontIndirectA(lf);
3210 ok(hfont != 0, "CreateFontIndirect error %u\n", GetLastError());
3211 check_font("original", lf, hfont);
3213 hfont_prev = SelectObject(hdc, hfont);
3215 ret = pGetGlyphIndicesA(hdc, "x", 1, &idx, GGI_MARK_NONEXISTING_GLYPHS);
3216 if (ret == GDI_ERROR || idx == 0xffff)
3218 SelectObject(hdc, hfont_prev);
3219 DeleteObject(hfont);
3220 skip("Font %s doesn't contain 'x', skipping the test\n", lf->lfFaceName);
3221 return;
3224 /* filling with 0xaa causes false pass under WINEDEBUG=warn+heap */
3225 memset(&gm1, 0xab, sizeof(gm1));
3226 SetLastError(0xdeadbeef);
3227 ret = GetGlyphOutlineA(hdc, 'x', GGO_METRICS, &gm1, 0, NULL, &mat);
3228 ok(ret != GDI_ERROR, "GetGlyphOutline error 0x%x\n", GetLastError());
3230 SelectObject(hdc, hfont_prev);
3231 DeleteObject(hfont);
3233 SetLastError(0xdeadbeef);
3234 hfont = CreateFontIndirectA(&lf2);
3235 ok(hfont != 0, "CreateFontIndirect error %u\n", GetLastError());
3236 check_font("negative width", &lf2, hfont);
3238 hfont_prev = SelectObject(hdc, hfont);
3240 memset(&gm2, 0xbb, sizeof(gm2));
3241 SetLastError(0xdeadbeef);
3242 ret = GetGlyphOutlineA(hdc, 'x', GGO_METRICS, &gm2, 0, NULL, &mat);
3243 ok(ret != GDI_ERROR, "GetGlyphOutline error 0x%x\n", GetLastError());
3245 SelectObject(hdc, hfont_prev);
3246 DeleteObject(hfont);
3248 ok(gm1.gmBlackBoxX == gm2.gmBlackBoxX &&
3249 gm1.gmBlackBoxY == gm2.gmBlackBoxY &&
3250 gm1.gmptGlyphOrigin.x == gm2.gmptGlyphOrigin.x &&
3251 gm1.gmptGlyphOrigin.y == gm2.gmptGlyphOrigin.y &&
3252 gm1.gmCellIncX == gm2.gmCellIncX &&
3253 gm1.gmCellIncY == gm2.gmCellIncY,
3254 "gm1=%d,%d,%d,%d,%d,%d gm2=%d,%d,%d,%d,%d,%d\n",
3255 gm1.gmBlackBoxX, gm1.gmBlackBoxY, gm1.gmptGlyphOrigin.x,
3256 gm1.gmptGlyphOrigin.y, gm1.gmCellIncX, gm1.gmCellIncY,
3257 gm2.gmBlackBoxX, gm2.gmBlackBoxY, gm2.gmptGlyphOrigin.x,
3258 gm2.gmptGlyphOrigin.y, gm2.gmCellIncX, gm2.gmCellIncY);
3261 /* PANOSE is 10 bytes in size, need to pack the structure properly */
3262 #include "pshpack2.h"
3263 typedef struct
3265 USHORT version;
3266 SHORT xAvgCharWidth;
3267 USHORT usWeightClass;
3268 USHORT usWidthClass;
3269 SHORT fsType;
3270 SHORT ySubscriptXSize;
3271 SHORT ySubscriptYSize;
3272 SHORT ySubscriptXOffset;
3273 SHORT ySubscriptYOffset;
3274 SHORT ySuperscriptXSize;
3275 SHORT ySuperscriptYSize;
3276 SHORT ySuperscriptXOffset;
3277 SHORT ySuperscriptYOffset;
3278 SHORT yStrikeoutSize;
3279 SHORT yStrikeoutPosition;
3280 SHORT sFamilyClass;
3281 PANOSE panose;
3282 ULONG ulUnicodeRange1;
3283 ULONG ulUnicodeRange2;
3284 ULONG ulUnicodeRange3;
3285 ULONG ulUnicodeRange4;
3286 CHAR achVendID[4];
3287 USHORT fsSelection;
3288 USHORT usFirstCharIndex;
3289 USHORT usLastCharIndex;
3290 /* According to the Apple spec, original version didn't have the below fields,
3291 * version numbers were taken from the OpenType spec.
3293 /* version 0 (TrueType 1.5) */
3294 USHORT sTypoAscender;
3295 USHORT sTypoDescender;
3296 USHORT sTypoLineGap;
3297 USHORT usWinAscent;
3298 USHORT usWinDescent;
3299 /* version 1 (TrueType 1.66) */
3300 ULONG ulCodePageRange1;
3301 ULONG ulCodePageRange2;
3302 /* version 2 (OpenType 1.2) */
3303 SHORT sxHeight;
3304 SHORT sCapHeight;
3305 USHORT usDefaultChar;
3306 USHORT usBreakChar;
3307 USHORT usMaxContext;
3308 /* version 4 (OpenType 1.6) */
3309 USHORT usLowerOpticalPointSize;
3310 USHORT usUpperOpticalPointSize;
3311 } TT_OS2_V4;
3312 #include "poppack.h"
3314 #define TT_OS2_V0_SIZE (FIELD_OFFSET(TT_OS2_V4, ulCodePageRange1))
3316 typedef struct
3318 USHORT version;
3319 USHORT num_tables;
3320 } cmap_header;
3322 typedef struct
3324 USHORT plat_id;
3325 USHORT enc_id;
3326 ULONG offset;
3327 } cmap_encoding_record;
3329 typedef struct
3331 USHORT format;
3332 USHORT length;
3333 USHORT language;
3335 BYTE glyph_ids[256];
3336 } cmap_format_0;
3338 typedef struct
3340 USHORT format;
3341 USHORT length;
3342 USHORT language;
3344 USHORT seg_countx2;
3345 USHORT search_range;
3346 USHORT entry_selector;
3347 USHORT range_shift;
3349 USHORT end_count[1]; /* this is a variable-sized array of length seg_countx2 / 2 */
3350 /* Then follows:
3351 USHORT pad;
3352 USHORT start_count[seg_countx2 / 2];
3353 USHORT id_delta[seg_countx2 / 2];
3354 USHORT id_range_offset[seg_countx2 / 2];
3355 USHORT glyph_ids[];
3357 } cmap_format_4;
3359 typedef struct
3361 USHORT end_count;
3362 USHORT start_count;
3363 USHORT id_delta;
3364 USHORT id_range_offset;
3365 } cmap_format_4_seg;
3367 static void expect_ff(const TEXTMETRICA *tmA, const TT_OS2_V4 *os2, WORD family, const char *name)
3369 ok((tmA->tmPitchAndFamily & 0xf0) == family ||
3370 broken(PRIMARYLANGID(GetSystemDefaultLangID()) != LANG_ENGLISH),
3371 "%s: expected family %02x got %02x. panose %d-%d-%d-%d-...\n",
3372 name, family, tmA->tmPitchAndFamily, os2->panose.bFamilyType, os2->panose.bSerifStyle,
3373 os2->panose.bWeight, os2->panose.bProportion);
3376 static BOOL get_first_last_from_cmap0(void *ptr, DWORD *first, DWORD *last)
3378 int i;
3379 cmap_format_0 *cmap = (cmap_format_0*)ptr;
3381 *first = 256;
3383 for(i = 0; i < 256; i++)
3385 if(cmap->glyph_ids[i] == 0) continue;
3386 *last = i;
3387 if(*first == 256) *first = i;
3389 if(*first == 256) return FALSE;
3390 return TRUE;
3393 static void get_seg4(cmap_format_4 *cmap, USHORT seg_num, cmap_format_4_seg *seg)
3395 USHORT segs = GET_BE_WORD(cmap->seg_countx2) / 2;
3396 seg->end_count = GET_BE_WORD(cmap->end_count[seg_num]);
3397 seg->start_count = GET_BE_WORD(cmap->end_count[segs + 1 + seg_num]);
3398 seg->id_delta = GET_BE_WORD(cmap->end_count[2 * segs + 1 + seg_num]);
3399 seg->id_range_offset = GET_BE_WORD(cmap->end_count[3 * segs + 1 + seg_num]);
3402 static BOOL get_first_last_from_cmap4(void *ptr, DWORD *first, DWORD *last, DWORD limit)
3404 int i;
3405 cmap_format_4 *cmap = (cmap_format_4*)ptr;
3406 USHORT seg_count = GET_BE_WORD(cmap->seg_countx2) / 2;
3408 *first = 0x10000;
3410 for(i = 0; i < seg_count; i++)
3412 cmap_format_4_seg seg;
3414 get_seg4(cmap, i, &seg);
3416 if(seg.start_count > 0xfffe) break;
3418 if(*first == 0x10000) *first = seg.start_count;
3420 *last = min(seg.end_count, 0xfffe);
3423 if(*first == 0x10000) return FALSE;
3424 return TRUE;
3427 static void *get_cmap(cmap_header *header, USHORT plat_id, USHORT enc_id)
3429 USHORT i;
3430 cmap_encoding_record *record = (cmap_encoding_record *)(header + 1);
3432 for(i = 0; i < GET_BE_WORD(header->num_tables); i++)
3434 if(GET_BE_WORD(record->plat_id) == plat_id && GET_BE_WORD(record->enc_id) == enc_id)
3435 return (BYTE *)header + GET_BE_DWORD(record->offset);
3436 record++;
3438 return NULL;
3441 typedef enum
3443 cmap_none,
3444 cmap_ms_unicode,
3445 cmap_ms_symbol
3446 } cmap_type;
3448 static BOOL get_first_last_from_cmap(HDC hdc, DWORD *first, DWORD *last, cmap_type *cmap_type)
3450 LONG size, ret;
3451 cmap_header *header;
3452 void *cmap;
3453 BOOL r = FALSE;
3454 WORD format;
3456 size = GetFontData(hdc, MS_CMAP_TAG, 0, NULL, 0);
3457 ok(size != GDI_ERROR, "no cmap table found\n");
3458 if(size == GDI_ERROR) return FALSE;
3460 header = HeapAlloc(GetProcessHeap(), 0, size);
3461 ret = GetFontData(hdc, MS_CMAP_TAG, 0, header, size);
3462 ok(ret == size, "GetFontData should return %u not %u\n", size, ret);
3463 ok(GET_BE_WORD(header->version) == 0, "got cmap version %d\n", GET_BE_WORD(header->version));
3465 cmap = get_cmap(header, 3, 1);
3466 if(cmap)
3467 *cmap_type = cmap_ms_unicode;
3468 else
3470 cmap = get_cmap(header, 3, 0);
3471 if(cmap) *cmap_type = cmap_ms_symbol;
3473 if(!cmap)
3475 *cmap_type = cmap_none;
3476 goto end;
3479 format = GET_BE_WORD(*(WORD *)cmap);
3480 switch(format)
3482 case 0:
3483 r = get_first_last_from_cmap0(cmap, first, last);
3484 break;
3485 case 4:
3486 r = get_first_last_from_cmap4(cmap, first, last, size);
3487 break;
3488 default:
3489 trace("unhandled cmap format %d\n", format);
3490 break;
3493 end:
3494 HeapFree(GetProcessHeap(), 0, header);
3495 return r;
3498 #define TT_PLATFORM_APPLE_UNICODE 0
3499 #define TT_PLATFORM_MACINTOSH 1
3500 #define TT_PLATFORM_MICROSOFT 3
3501 #define TT_APPLE_ID_DEFAULT 0
3502 #define TT_APPLE_ID_ISO_10646 2
3503 #define TT_APPLE_ID_UNICODE_2_0 3
3504 #define TT_MS_ID_SYMBOL_CS 0
3505 #define TT_MS_ID_UNICODE_CS 1
3506 #define TT_MS_LANGID_ENGLISH_UNITED_STATES 0x0409
3507 #define TT_NAME_ID_FONT_FAMILY 1
3508 #define TT_NAME_ID_FONT_SUBFAMILY 2
3509 #define TT_NAME_ID_UNIQUE_ID 3
3510 #define TT_NAME_ID_FULL_NAME 4
3511 #define TT_MAC_ID_SIMPLIFIED_CHINESE 25
3513 typedef struct sfnt_name
3515 USHORT platform_id;
3516 USHORT encoding_id;
3517 USHORT language_id;
3518 USHORT name_id;
3519 USHORT length;
3520 USHORT offset;
3521 } sfnt_name;
3523 static const LANGID mac_langid_table[] =
3525 MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ENGLISH */
3526 MAKELANGID(LANG_FRENCH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_FRENCH */
3527 MAKELANGID(LANG_GERMAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_GERMAN */
3528 MAKELANGID(LANG_ITALIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ITALIAN */
3529 MAKELANGID(LANG_DUTCH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_DUTCH */
3530 MAKELANGID(LANG_SWEDISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SWEDISH */
3531 MAKELANGID(LANG_SPANISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SPANISH */
3532 MAKELANGID(LANG_DANISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_DANISH */
3533 MAKELANGID(LANG_PORTUGUESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_PORTUGUESE */
3534 MAKELANGID(LANG_NORWEGIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_NORWEGIAN */
3535 MAKELANGID(LANG_HEBREW,SUBLANG_DEFAULT), /* TT_MAC_LANGID_HEBREW */
3536 MAKELANGID(LANG_JAPANESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_JAPANESE */
3537 MAKELANGID(LANG_ARABIC,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ARABIC */
3538 MAKELANGID(LANG_FINNISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_FINNISH */
3539 MAKELANGID(LANG_GREEK,SUBLANG_DEFAULT), /* TT_MAC_LANGID_GREEK */
3540 MAKELANGID(LANG_ICELANDIC,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ICELANDIC */
3541 MAKELANGID(LANG_MALTESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MALTESE */
3542 MAKELANGID(LANG_TURKISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TURKISH */
3543 MAKELANGID(LANG_CROATIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_CROATIAN */
3544 MAKELANGID(LANG_CHINESE_TRADITIONAL,SUBLANG_DEFAULT), /* TT_MAC_LANGID_CHINESE_TRADITIONAL */
3545 MAKELANGID(LANG_URDU,SUBLANG_DEFAULT), /* TT_MAC_LANGID_URDU */
3546 MAKELANGID(LANG_HINDI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_HINDI */
3547 MAKELANGID(LANG_THAI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_THAI */
3548 MAKELANGID(LANG_KOREAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_KOREAN */
3549 MAKELANGID(LANG_LITHUANIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_LITHUANIAN */
3550 MAKELANGID(LANG_POLISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_POLISH */
3551 MAKELANGID(LANG_HUNGARIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_HUNGARIAN */
3552 MAKELANGID(LANG_ESTONIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ESTONIAN */
3553 MAKELANGID(LANG_LATVIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_LETTISH */
3554 MAKELANGID(LANG_SAMI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SAAMISK */
3555 MAKELANGID(LANG_FAEROESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_FAEROESE */
3556 MAKELANGID(LANG_FARSI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_FARSI */
3557 MAKELANGID(LANG_RUSSIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_RUSSIAN */
3558 MAKELANGID(LANG_CHINESE_SIMPLIFIED,SUBLANG_DEFAULT), /* TT_MAC_LANGID_CHINESE_SIMPLIFIED */
3559 MAKELANGID(LANG_DUTCH,SUBLANG_DUTCH_BELGIAN), /* TT_MAC_LANGID_FLEMISH */
3560 MAKELANGID(LANG_IRISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_IRISH */
3561 MAKELANGID(LANG_ALBANIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ALBANIAN */
3562 MAKELANGID(LANG_ROMANIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ROMANIAN */
3563 MAKELANGID(LANG_CZECH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_CZECH */
3564 MAKELANGID(LANG_SLOVAK,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SLOVAK */
3565 MAKELANGID(LANG_SLOVENIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SLOVENIAN */
3566 0, /* TT_MAC_LANGID_YIDDISH */
3567 MAKELANGID(LANG_SERBIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SERBIAN */
3568 MAKELANGID(LANG_MACEDONIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MACEDONIAN */
3569 MAKELANGID(LANG_BULGARIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_BULGARIAN */
3570 MAKELANGID(LANG_UKRAINIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_UKRAINIAN */
3571 MAKELANGID(LANG_BELARUSIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_BYELORUSSIAN */
3572 MAKELANGID(LANG_UZBEK,SUBLANG_DEFAULT), /* TT_MAC_LANGID_UZBEK */
3573 MAKELANGID(LANG_KAZAK,SUBLANG_DEFAULT), /* TT_MAC_LANGID_KAZAKH */
3574 MAKELANGID(LANG_AZERI,SUBLANG_AZERI_CYRILLIC), /* TT_MAC_LANGID_AZERBAIJANI */
3575 0, /* TT_MAC_LANGID_AZERBAIJANI_ARABIC_SCRIPT */
3576 MAKELANGID(LANG_ARMENIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ARMENIAN */
3577 MAKELANGID(LANG_GEORGIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_GEORGIAN */
3578 0, /* TT_MAC_LANGID_MOLDAVIAN */
3579 MAKELANGID(LANG_KYRGYZ,SUBLANG_DEFAULT), /* TT_MAC_LANGID_KIRGHIZ */
3580 MAKELANGID(LANG_TAJIK,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TAJIKI */
3581 MAKELANGID(LANG_TURKMEN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TURKMEN */
3582 MAKELANGID(LANG_MONGOLIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MONGOLIAN */
3583 MAKELANGID(LANG_MONGOLIAN,SUBLANG_MONGOLIAN_CYRILLIC_MONGOLIA), /* TT_MAC_LANGID_MONGOLIAN_CYRILLIC_SCRIPT */
3584 MAKELANGID(LANG_PASHTO,SUBLANG_DEFAULT), /* TT_MAC_LANGID_PASHTO */
3585 0, /* TT_MAC_LANGID_KURDISH */
3586 MAKELANGID(LANG_KASHMIRI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_KASHMIRI */
3587 MAKELANGID(LANG_SINDHI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SINDHI */
3588 MAKELANGID(LANG_TIBETAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TIBETAN */
3589 MAKELANGID(LANG_NEPALI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_NEPALI */
3590 MAKELANGID(LANG_SANSKRIT,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SANSKRIT */
3591 MAKELANGID(LANG_MARATHI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MARATHI */
3592 MAKELANGID(LANG_BENGALI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_BENGALI */
3593 MAKELANGID(LANG_ASSAMESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ASSAMESE */
3594 MAKELANGID(LANG_GUJARATI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_GUJARATI */
3595 MAKELANGID(LANG_PUNJABI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_PUNJABI */
3596 MAKELANGID(LANG_ORIYA,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ORIYA */
3597 MAKELANGID(LANG_MALAYALAM,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MALAYALAM */
3598 MAKELANGID(LANG_KANNADA,SUBLANG_DEFAULT), /* TT_MAC_LANGID_KANNADA */
3599 MAKELANGID(LANG_TAMIL,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TAMIL */
3600 MAKELANGID(LANG_TELUGU,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TELUGU */
3601 MAKELANGID(LANG_SINHALESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SINHALESE */
3602 0, /* TT_MAC_LANGID_BURMESE */
3603 MAKELANGID(LANG_KHMER,SUBLANG_DEFAULT), /* TT_MAC_LANGID_KHMER */
3604 MAKELANGID(LANG_LAO,SUBLANG_DEFAULT), /* TT_MAC_LANGID_LAO */
3605 MAKELANGID(LANG_VIETNAMESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_VIETNAMESE */
3606 MAKELANGID(LANG_INDONESIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_INDONESIAN */
3607 0, /* TT_MAC_LANGID_TAGALOG */
3608 MAKELANGID(LANG_MALAY,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MALAY_ROMAN_SCRIPT */
3609 0, /* TT_MAC_LANGID_MALAY_ARABIC_SCRIPT */
3610 MAKELANGID(LANG_AMHARIC,SUBLANG_DEFAULT), /* TT_MAC_LANGID_AMHARIC */
3611 MAKELANGID(LANG_TIGRIGNA,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TIGRINYA */
3612 0, /* TT_MAC_LANGID_GALLA */
3613 0, /* TT_MAC_LANGID_SOMALI */
3614 MAKELANGID(LANG_SWAHILI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SWAHILI */
3615 0, /* TT_MAC_LANGID_RUANDA */
3616 0, /* TT_MAC_LANGID_RUNDI */
3617 0, /* TT_MAC_LANGID_CHEWA */
3618 MAKELANGID(LANG_MALAGASY,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MALAGASY */
3619 MAKELANGID(LANG_ESPERANTO,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ESPERANTO */
3620 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 95-111 */
3621 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 112-127 */
3622 MAKELANGID(LANG_WELSH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_WELSH */
3623 MAKELANGID(LANG_BASQUE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_BASQUE */
3624 MAKELANGID(LANG_CATALAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_CATALAN */
3625 0, /* TT_MAC_LANGID_LATIN */
3626 MAKELANGID(LANG_QUECHUA,SUBLANG_DEFAULT), /* TT_MAC_LANGID_QUECHUA */
3627 0, /* TT_MAC_LANGID_GUARANI */
3628 0, /* TT_MAC_LANGID_AYMARA */
3629 MAKELANGID(LANG_TATAR,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TATAR */
3630 MAKELANGID(LANG_UIGHUR,SUBLANG_DEFAULT), /* TT_MAC_LANGID_UIGHUR */
3631 0, /* TT_MAC_LANGID_DZONGKHA */
3632 0, /* TT_MAC_LANGID_JAVANESE */
3633 0, /* TT_MAC_LANGID_SUNDANESE */
3634 MAKELANGID(LANG_GALICIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_GALICIAN */
3635 MAKELANGID(LANG_AFRIKAANS,SUBLANG_DEFAULT), /* TT_MAC_LANGID_AFRIKAANS */
3636 MAKELANGID(LANG_BRETON,SUBLANG_DEFAULT), /* TT_MAC_LANGID_BRETON */
3637 MAKELANGID(LANG_INUKTITUT,SUBLANG_DEFAULT), /* TT_MAC_LANGID_INUKTITUT */
3638 MAKELANGID(LANG_SCOTTISH_GAELIC,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SCOTTISH_GAELIC */
3639 MAKELANGID(LANG_MANX_GAELIC,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MANX_GAELIC */
3640 MAKELANGID(LANG_IRISH,SUBLANG_IRISH_IRELAND), /* TT_MAC_LANGID_IRISH_GAELIC */
3641 0, /* TT_MAC_LANGID_TONGAN */
3642 0, /* TT_MAC_LANGID_GREEK_POLYTONIC */
3643 MAKELANGID(LANG_GREENLANDIC,SUBLANG_DEFAULT), /* TT_MAC_LANGID_GREELANDIC */
3644 MAKELANGID(LANG_AZERI,SUBLANG_AZERI_LATIN), /* TT_MAC_LANGID_AZERBAIJANI_ROMAN_SCRIPT */
3647 static inline WORD get_mac_code_page( const sfnt_name *name )
3649 if (GET_BE_WORD(name->encoding_id) == TT_MAC_ID_SIMPLIFIED_CHINESE) return 10008; /* special case */
3650 return 10000 + GET_BE_WORD(name->encoding_id);
3653 static int match_name_table_language( const sfnt_name *name, LANGID lang )
3655 LANGID name_lang;
3656 int res = 0;
3658 switch (GET_BE_WORD(name->platform_id))
3660 case TT_PLATFORM_MICROSOFT:
3661 res += 5; /* prefer the Microsoft name */
3662 switch (GET_BE_WORD(name->encoding_id))
3664 case TT_MS_ID_UNICODE_CS:
3665 case TT_MS_ID_SYMBOL_CS:
3666 name_lang = GET_BE_WORD(name->language_id);
3667 break;
3668 default:
3669 return 0;
3671 break;
3672 case TT_PLATFORM_MACINTOSH:
3673 if (!IsValidCodePage( get_mac_code_page( name ))) return 0;
3674 if (GET_BE_WORD(name->language_id) >= sizeof(mac_langid_table)/sizeof(mac_langid_table[0])) return 0;
3675 name_lang = mac_langid_table[GET_BE_WORD(name->language_id)];
3676 break;
3677 case TT_PLATFORM_APPLE_UNICODE:
3678 res += 2; /* prefer Unicode encodings */
3679 switch (GET_BE_WORD(name->encoding_id))
3681 case TT_APPLE_ID_DEFAULT:
3682 case TT_APPLE_ID_ISO_10646:
3683 case TT_APPLE_ID_UNICODE_2_0:
3684 if (GET_BE_WORD(name->language_id) >= sizeof(mac_langid_table)/sizeof(mac_langid_table[0])) return 0;
3685 name_lang = mac_langid_table[GET_BE_WORD(name->language_id)];
3686 break;
3687 default:
3688 return 0;
3690 break;
3691 default:
3692 return 0;
3694 if (name_lang == lang) res += 30;
3695 else if (PRIMARYLANGID( name_lang ) == PRIMARYLANGID( lang )) res += 20;
3696 else if (name_lang == MAKELANGID( LANG_ENGLISH, SUBLANG_DEFAULT )) res += 10;
3697 return res;
3700 static BOOL get_ttf_nametable_entry(HDC hdc, WORD name_id, WCHAR *out_buf, SIZE_T out_size, LCID language_id)
3702 struct sfnt_name_header
3704 USHORT format;
3705 USHORT number_of_record;
3706 USHORT storage_offset;
3707 } *header;
3708 sfnt_name *entry;
3709 BOOL r = FALSE;
3710 LONG size, offset, length;
3711 LONG c, ret;
3712 WCHAR *name;
3713 BYTE *data;
3714 USHORT i;
3715 int res, best_lang = 0, best_index = -1;
3717 size = GetFontData(hdc, MS_NAME_TAG, 0, NULL, 0);
3718 ok(size != GDI_ERROR, "no name table found\n");
3719 if(size == GDI_ERROR) return FALSE;
3721 data = HeapAlloc(GetProcessHeap(), 0, size);
3722 ret = GetFontData(hdc, MS_NAME_TAG, 0, data, size);
3723 ok(ret == size, "GetFontData should return %u not %u\n", size, ret);
3725 header = (void *)data;
3726 header->format = GET_BE_WORD(header->format);
3727 header->number_of_record = GET_BE_WORD(header->number_of_record);
3728 header->storage_offset = GET_BE_WORD(header->storage_offset);
3729 if (header->format != 0)
3731 trace("got format %u\n", header->format);
3732 goto out;
3734 if (header->number_of_record == 0 || sizeof(*header) + header->number_of_record * sizeof(*entry) > size)
3736 trace("number records out of range: %d\n", header->number_of_record);
3737 goto out;
3739 if (header->storage_offset >= size)
3741 trace("storage_offset %u > size %u\n", header->storage_offset, size);
3742 goto out;
3745 entry = (void *)&header[1];
3746 for (i = 0; i < header->number_of_record; i++)
3748 if (GET_BE_WORD(entry[i].name_id) != name_id) continue;
3749 res = match_name_table_language( &entry[i], language_id);
3750 if (res > best_lang)
3752 best_lang = res;
3753 best_index = i;
3757 offset = header->storage_offset + GET_BE_WORD(entry[best_index].offset);
3758 length = GET_BE_WORD(entry[best_index].length);
3759 if (offset + length > size)
3761 trace("entry %d is out of range\n", best_index);
3762 goto out;
3764 if (length >= out_size)
3766 trace("buffer too small for entry %d\n", best_index);
3767 goto out;
3770 name = (WCHAR *)(data + offset);
3771 for (c = 0; c < length / 2; c++)
3772 out_buf[c] = GET_BE_WORD(name[c]);
3773 out_buf[c] = 0;
3775 r = TRUE;
3777 out:
3778 HeapFree(GetProcessHeap(), 0, data);
3779 return r;
3782 static void test_text_metrics(const LOGFONTA *lf, const NEWTEXTMETRICA *ntm)
3784 HDC hdc;
3785 HFONT hfont, hfont_old;
3786 TEXTMETRICA tmA;
3787 TT_OS2_V4 tt_os2;
3788 LONG size, ret;
3789 const char *font_name = lf->lfFaceName;
3790 DWORD cmap_first = 0, cmap_last = 0;
3791 UINT ascent, descent, cell_height;
3792 cmap_type cmap_type;
3793 BOOL sys_lang_non_english;
3795 sys_lang_non_english = PRIMARYLANGID(GetSystemDefaultLangID()) != LANG_ENGLISH;
3796 hdc = GetDC(0);
3798 SetLastError(0xdeadbeef);
3799 hfont = CreateFontIndirectA(lf);
3800 ok(hfont != 0, "CreateFontIndirect error %u\n", GetLastError());
3802 hfont_old = SelectObject(hdc, hfont);
3804 size = GetFontData(hdc, MS_OS2_TAG, 0, NULL, 0);
3805 if (size == GDI_ERROR)
3807 trace("OS/2 chunk was not found\n");
3808 goto end_of_test;
3810 if (size > sizeof(tt_os2))
3812 trace("got too large OS/2 chunk of size %u\n", size);
3813 size = sizeof(tt_os2);
3816 memset(&tt_os2, 0, sizeof(tt_os2));
3817 ret = GetFontData(hdc, MS_OS2_TAG, 0, &tt_os2, size);
3818 ok(ret >= TT_OS2_V0_SIZE && ret <= size, "GetFontData should return size from [%u,%u] not %u\n", TT_OS2_V0_SIZE,
3819 size, ret);
3821 SetLastError(0xdeadbeef);
3822 ret = GetTextMetricsA(hdc, &tmA);
3823 ok(ret, "GetTextMetricsA error %u\n", GetLastError());
3825 if(!get_first_last_from_cmap(hdc, &cmap_first, &cmap_last, &cmap_type))
3827 skip("%s is not a Windows font, OS/2 metrics may be invalid.\n",font_name);
3829 else
3831 USHORT expect_first_A, expect_last_A, expect_break_A, expect_default_A;
3832 USHORT expect_first_W, expect_last_W, expect_break_W, expect_default_W;
3833 UINT os2_first_char, os2_last_char, default_char, break_char;
3834 USHORT version;
3835 TEXTMETRICW tmW;
3837 ascent = GET_BE_WORD(tt_os2.usWinAscent);
3838 descent = abs((SHORT)GET_BE_WORD(tt_os2.usWinDescent));
3839 cell_height = ascent + descent;
3840 ok(ntm->ntmCellHeight == cell_height, "%s: ntmCellHeight %u != %u, os2.usWinAscent/os2.usWinDescent %u/%u\n",
3841 font_name, ntm->ntmCellHeight, cell_height, ascent, descent);
3843 version = GET_BE_WORD(tt_os2.version);
3845 os2_first_char = GET_BE_WORD(tt_os2.usFirstCharIndex);
3846 os2_last_char = GET_BE_WORD(tt_os2.usLastCharIndex);
3847 default_char = GET_BE_WORD(tt_os2.usDefaultChar);
3848 break_char = GET_BE_WORD(tt_os2.usBreakChar);
3850 if (winetest_debug > 1)
3851 trace("font %s charset %u: %x-%x (%x-%x) default %x break %x OS/2 version %u vendor %4.4s\n",
3852 font_name, lf->lfCharSet, os2_first_char, os2_last_char, cmap_first, cmap_last,
3853 default_char, break_char, version, (LPCSTR)&tt_os2.achVendID);
3855 if (cmap_type == cmap_ms_symbol || (cmap_first >= 0xf000 && cmap_first < 0xf100))
3857 expect_first_W = 0;
3858 switch(GetACP())
3860 case 1255: /* Hebrew */
3861 expect_last_W = 0xf896;
3862 break;
3863 case 1257: /* Baltic */
3864 expect_last_W = 0xf8fd;
3865 break;
3866 default:
3867 expect_last_W = 0xf0ff;
3869 expect_break_W = 0x20;
3870 expect_default_W = expect_break_W - 1;
3871 expect_first_A = 0x1e;
3872 expect_last_A = min(os2_last_char - os2_first_char + 0x20, 0xff);
3874 else
3876 expect_first_W = cmap_first;
3877 expect_last_W = cmap_last;
3878 if(os2_first_char <= 1)
3879 expect_break_W = os2_first_char + 2;
3880 else if(os2_first_char > 0xff)
3881 expect_break_W = 0x20;
3882 else
3883 expect_break_W = os2_first_char;
3884 expect_default_W = expect_break_W - 1;
3885 expect_first_A = expect_default_W - 1;
3886 expect_last_A = min(expect_last_W, 0xff);
3888 expect_break_A = expect_break_W;
3889 expect_default_A = expect_default_W;
3891 /* Wine currently uses SYMBOL_CHARSET to identify whether the ANSI metrics need special handling */
3892 todo_wine_if(cmap_type != cmap_ms_symbol && tmA.tmCharSet == SYMBOL_CHARSET && expect_first_A != 0x1e)
3893 ok(tmA.tmFirstChar == expect_first_A ||
3894 tmA.tmFirstChar == expect_first_A + 1 /* win9x */,
3895 "A: tmFirstChar for %s got %02x expected %02x\n", font_name, tmA.tmFirstChar, expect_first_A);
3896 if (pGdiGetCodePage == NULL || ! IsDBCSLeadByteEx(pGdiGetCodePage(hdc), tmA.tmLastChar))
3897 ok(tmA.tmLastChar == expect_last_A ||
3898 tmA.tmLastChar == 0xff /* win9x */,
3899 "A: tmLastChar for %s got %02x expected %02x\n", font_name, tmA.tmLastChar, expect_last_A);
3900 else
3901 skip("tmLastChar is DBCS lead byte\n");
3902 ok(tmA.tmBreakChar == expect_break_A, "A: tmBreakChar for %s got %02x expected %02x\n",
3903 font_name, tmA.tmBreakChar, expect_break_A);
3904 ok(tmA.tmDefaultChar == expect_default_A || broken(sys_lang_non_english),
3905 "A: tmDefaultChar for %s got %02x expected %02x\n",
3906 font_name, tmA.tmDefaultChar, expect_default_A);
3909 SetLastError(0xdeadbeef);
3910 ret = GetTextMetricsW(hdc, &tmW);
3911 ok(ret || GetLastError() == ERROR_CALL_NOT_IMPLEMENTED,
3912 "GetTextMetricsW error %u\n", GetLastError());
3913 if (ret)
3915 /* Wine uses the os2 first char */
3916 todo_wine_if(cmap_first != os2_first_char && cmap_type != cmap_ms_symbol)
3917 ok(tmW.tmFirstChar == expect_first_W, "W: tmFirstChar for %s got %02x expected %02x\n",
3918 font_name, tmW.tmFirstChar, expect_first_W);
3920 /* Wine uses the os2 last char */
3921 todo_wine_if(expect_last_W != os2_last_char && cmap_type != cmap_ms_symbol)
3922 ok(tmW.tmLastChar == expect_last_W, "W: tmLastChar for %s got %02x expected %02x\n",
3923 font_name, tmW.tmLastChar, expect_last_W);
3924 ok(tmW.tmBreakChar == expect_break_W, "W: tmBreakChar for %s got %02x expected %02x\n",
3925 font_name, tmW.tmBreakChar, expect_break_W);
3926 ok(tmW.tmDefaultChar == expect_default_W || broken(sys_lang_non_english),
3927 "W: tmDefaultChar for %s got %02x expected %02x\n",
3928 font_name, tmW.tmDefaultChar, expect_default_W);
3930 /* Test the aspect ratio while we have tmW */
3931 ret = GetDeviceCaps(hdc, LOGPIXELSX);
3932 ok(tmW.tmDigitizedAspectX == ret, "W: tmDigitizedAspectX %u != %u\n",
3933 tmW.tmDigitizedAspectX, ret);
3934 ret = GetDeviceCaps(hdc, LOGPIXELSY);
3935 ok(tmW.tmDigitizedAspectX == ret, "W: tmDigitizedAspectY %u != %u\n",
3936 tmW.tmDigitizedAspectX, ret);
3940 /* test FF_ values */
3941 switch(tt_os2.panose.bFamilyType)
3943 case PAN_ANY:
3944 case PAN_NO_FIT:
3945 case PAN_FAMILY_TEXT_DISPLAY:
3946 case PAN_FAMILY_PICTORIAL:
3947 default:
3948 if((tmA.tmPitchAndFamily & 1) == 0 || /* fixed */
3949 tt_os2.panose.bProportion == PAN_PROP_MONOSPACED)
3951 expect_ff(&tmA, &tt_os2, FF_MODERN, font_name);
3952 break;
3954 switch(tt_os2.panose.bSerifStyle)
3956 case PAN_ANY:
3957 case PAN_NO_FIT:
3958 default:
3959 expect_ff(&tmA, &tt_os2, FF_DONTCARE, font_name);
3960 break;
3962 case PAN_SERIF_COVE:
3963 case PAN_SERIF_OBTUSE_COVE:
3964 case PAN_SERIF_SQUARE_COVE:
3965 case PAN_SERIF_OBTUSE_SQUARE_COVE:
3966 case PAN_SERIF_SQUARE:
3967 case PAN_SERIF_THIN:
3968 case PAN_SERIF_BONE:
3969 case PAN_SERIF_EXAGGERATED:
3970 case PAN_SERIF_TRIANGLE:
3971 expect_ff(&tmA, &tt_os2, FF_ROMAN, font_name);
3972 break;
3974 case PAN_SERIF_NORMAL_SANS:
3975 case PAN_SERIF_OBTUSE_SANS:
3976 case PAN_SERIF_PERP_SANS:
3977 case PAN_SERIF_FLARED:
3978 case PAN_SERIF_ROUNDED:
3979 expect_ff(&tmA, &tt_os2, FF_SWISS, font_name);
3980 break;
3982 break;
3984 case PAN_FAMILY_SCRIPT:
3985 expect_ff(&tmA, &tt_os2, FF_SCRIPT, font_name);
3986 break;
3988 case PAN_FAMILY_DECORATIVE:
3989 expect_ff(&tmA, &tt_os2, FF_DECORATIVE, font_name);
3990 break;
3993 test_negative_width(hdc, lf);
3995 end_of_test:
3996 SelectObject(hdc, hfont_old);
3997 DeleteObject(hfont);
3999 ReleaseDC(0, hdc);
4002 static INT CALLBACK enum_truetype_font_proc(const LOGFONTA *lf, const TEXTMETRICA *ntm, DWORD type, LPARAM lParam)
4004 INT *enumed = (INT *)lParam;
4006 if (type == TRUETYPE_FONTTYPE)
4008 (*enumed)++;
4009 test_text_metrics(lf, (const NEWTEXTMETRICA *)ntm);
4011 return 1;
4014 static void test_GetTextMetrics(void)
4016 LOGFONTA lf;
4017 HDC hdc;
4018 INT enumed;
4020 /* Report only once */
4021 if(!pGetGlyphIndicesA)
4022 win_skip("GetGlyphIndicesA is unavailable, negative width will not be checked\n");
4024 hdc = GetDC(0);
4026 memset(&lf, 0, sizeof(lf));
4027 lf.lfCharSet = DEFAULT_CHARSET;
4028 enumed = 0;
4029 EnumFontFamiliesExA(hdc, &lf, enum_truetype_font_proc, (LPARAM)&enumed, 0);
4030 trace("Tested metrics of %d truetype fonts\n", enumed);
4032 ReleaseDC(0, hdc);
4035 static void test_nonexistent_font(void)
4037 static const struct
4039 const char *name;
4040 int charset;
4041 } font_subst[] =
4043 { "Times New Roman Baltic", 186 },
4044 { "Times New Roman CE", 238 },
4045 { "Times New Roman CYR", 204 },
4046 { "Times New Roman Greek", 161 },
4047 { "Times New Roman TUR", 162 }
4049 static const struct
4051 const char *name;
4052 int charset;
4053 } shell_subst[] =
4055 { "MS Shell Dlg", 186 },
4056 { "MS Shell Dlg", 238 },
4057 { "MS Shell Dlg", 204 },
4058 { "MS Shell Dlg", 161 },
4059 { "MS Shell Dlg", 162 }
4061 LOGFONTA lf;
4062 HDC hdc;
4063 HFONT hfont;
4064 CHARSETINFO csi;
4065 INT cs, expected_cs, i, ret;
4066 char buf[LF_FACESIZE];
4068 expected_cs = GetACP();
4069 if (!TranslateCharsetInfo(ULongToPtr(expected_cs), &csi, TCI_SRCCODEPAGE))
4071 skip("TranslateCharsetInfo failed for code page %d\n", expected_cs);
4072 return;
4074 expected_cs = csi.ciCharset;
4075 trace("ACP %d -> charset %d\n", GetACP(), expected_cs);
4077 hdc = CreateCompatibleDC(0);
4079 for (i = 0; i < sizeof(shell_subst)/sizeof(shell_subst[0]); i++)
4081 ret = is_font_installed(shell_subst[i].name);
4082 ok(ret || broken(!ret) /* win2000 */, "%s should be enumerated\n", shell_subst[i].name);
4083 ret = is_truetype_font_installed(shell_subst[i].name);
4084 ok(ret || broken(!ret) /* win2000 */, "%s should be enumerated\n", shell_subst[i].name);
4086 memset(&lf, 0, sizeof(lf));
4087 lf.lfHeight = -13;
4088 lf.lfWeight = FW_REGULAR;
4089 strcpy(lf.lfFaceName, shell_subst[i].name);
4090 hfont = CreateFontIndirectA(&lf);
4091 hfont = SelectObject(hdc, hfont);
4092 GetTextFaceA(hdc, sizeof(buf), buf);
4093 ok(!lstrcmpiA(buf, shell_subst[i].name), "expected %s, got %s\n", shell_subst[i].name, buf);
4094 cs = GetTextCharset(hdc);
4095 ok(cs == ANSI_CHARSET, "expected ANSI_CHARSET, got %d for font %s\n", cs, shell_subst[i].name);
4097 DeleteObject(SelectObject(hdc, hfont));
4099 memset(&lf, 0, sizeof(lf));
4100 lf.lfHeight = -13;
4101 lf.lfWeight = FW_DONTCARE;
4102 strcpy(lf.lfFaceName, shell_subst[i].name);
4103 hfont = CreateFontIndirectA(&lf);
4104 hfont = SelectObject(hdc, hfont);
4105 GetTextFaceA(hdc, sizeof(buf), buf);
4106 ok(!lstrcmpiA(buf, shell_subst[i].name), "expected %s, got %s\n", shell_subst[i].name, buf);
4107 cs = GetTextCharset(hdc);
4108 ok(cs == expected_cs || cs == ANSI_CHARSET, "expected %d, got %d for font %s\n", expected_cs, cs, shell_subst[i].name);
4109 DeleteObject(SelectObject(hdc, hfont));
4112 if (!is_truetype_font_installed("Arial") ||
4113 !is_truetype_font_installed("Times New Roman"))
4115 DeleteDC(hdc);
4116 skip("Arial or Times New Roman not installed\n");
4117 return;
4120 memset(&lf, 0, sizeof(lf));
4121 lf.lfHeight = 100;
4122 lf.lfWeight = FW_REGULAR;
4123 lf.lfCharSet = ANSI_CHARSET;
4124 lf.lfPitchAndFamily = FF_SWISS;
4125 strcpy(lf.lfFaceName, "Nonexistent font");
4126 hfont = CreateFontIndirectA(&lf);
4127 hfont = SelectObject(hdc, hfont);
4128 GetTextFaceA(hdc, sizeof(buf), buf);
4129 ok(!lstrcmpiA(buf, "Arial"), "Got %s\n", buf);
4130 cs = GetTextCharset(hdc);
4131 ok(cs == ANSI_CHARSET, "expected ANSI_CHARSET, got %d\n", cs);
4132 DeleteObject(SelectObject(hdc, hfont));
4134 memset(&lf, 0, sizeof(lf));
4135 lf.lfHeight = -13;
4136 lf.lfWeight = FW_DONTCARE;
4137 strcpy(lf.lfFaceName, "Nonexistent font");
4138 hfont = CreateFontIndirectA(&lf);
4139 hfont = SelectObject(hdc, hfont);
4140 GetTextFaceA(hdc, sizeof(buf), buf);
4141 todo_wine /* Wine uses Arial for all substitutions */
4142 ok(!lstrcmpiA(buf, "Nonexistent font") /* XP, Vista */ ||
4143 !lstrcmpiA(buf, "MS Serif") || /* Win9x */
4144 !lstrcmpiA(buf, "MS Sans Serif"), /* win2k3 */
4145 "Got %s\n", buf);
4146 cs = GetTextCharset(hdc);
4147 ok(cs == expected_cs || cs == ANSI_CHARSET, "expected %d, got %d\n", expected_cs, cs);
4148 DeleteObject(SelectObject(hdc, hfont));
4150 memset(&lf, 0, sizeof(lf));
4151 lf.lfHeight = -13;
4152 lf.lfWeight = FW_REGULAR;
4153 strcpy(lf.lfFaceName, "Nonexistent font");
4154 hfont = CreateFontIndirectA(&lf);
4155 hfont = SelectObject(hdc, hfont);
4156 GetTextFaceA(hdc, sizeof(buf), buf);
4157 ok(!lstrcmpiA(buf, "Arial") /* XP, Vista */ ||
4158 !lstrcmpiA(buf, "Times New Roman") /* Win9x */, "Got %s\n", buf);
4159 cs = GetTextCharset(hdc);
4160 ok(cs == ANSI_CHARSET, "expected ANSI_CHARSET, got %d\n", cs);
4161 DeleteObject(SelectObject(hdc, hfont));
4163 memset(&lf, 0, sizeof(lf));
4164 lf.lfHeight = -13;
4165 lf.lfWeight = FW_DONTCARE;
4166 strcpy(lf.lfFaceName, "Times New Roman");
4167 hfont = CreateFontIndirectA(&lf);
4168 hfont = SelectObject(hdc, hfont);
4169 GetTextFaceA(hdc, sizeof(buf), buf);
4170 ok(!lstrcmpiA(buf, "Times New Roman"), "Got %s\n", buf);
4171 cs = GetTextCharset(hdc);
4172 ok(cs == ANSI_CHARSET, "expected ANSI_CHARSET, got %d\n", cs);
4173 DeleteObject(SelectObject(hdc, hfont));
4175 for (i = 0; i < sizeof(font_subst)/sizeof(font_subst[0]); i++)
4177 ret = is_font_installed(font_subst[i].name);
4178 todo_wine
4179 ok(ret || broken(!ret && !i) /* win2000 doesn't have Times New Roman Baltic substitution */,
4180 "%s should be enumerated\n", font_subst[i].name);
4181 ret = is_truetype_font_installed(font_subst[i].name);
4182 todo_wine
4183 ok(ret || broken(!ret && !i) /* win2000 doesn't have Times New Roman Baltic substitution */,
4184 "%s should be enumerated\n", font_subst[i].name);
4186 memset(&lf, 0, sizeof(lf));
4187 lf.lfHeight = -13;
4188 lf.lfWeight = FW_REGULAR;
4189 strcpy(lf.lfFaceName, font_subst[i].name);
4190 hfont = CreateFontIndirectA(&lf);
4191 hfont = SelectObject(hdc, hfont);
4192 cs = GetTextCharset(hdc);
4193 if (font_subst[i].charset == expected_cs)
4195 ok(cs == expected_cs, "expected %d, got %d for font %s\n", expected_cs, cs, font_subst[i].name);
4196 GetTextFaceA(hdc, sizeof(buf), buf);
4197 ok(!lstrcmpiA(buf, font_subst[i].name), "expected %s, got %s\n", font_subst[i].name, buf);
4199 else
4201 ok(cs == ANSI_CHARSET, "expected ANSI_CHARSET, got %d for font %s\n", cs, font_subst[i].name);
4202 GetTextFaceA(hdc, sizeof(buf), buf);
4203 ok(!lstrcmpiA(buf, "Arial") /* XP, Vista */ ||
4204 !lstrcmpiA(buf, "Times New Roman") /* Win9x */, "got %s for font %s\n", buf, font_subst[i].name);
4206 DeleteObject(SelectObject(hdc, hfont));
4208 memset(&lf, 0, sizeof(lf));
4209 lf.lfHeight = -13;
4210 lf.lfWeight = FW_DONTCARE;
4211 strcpy(lf.lfFaceName, font_subst[i].name);
4212 hfont = CreateFontIndirectA(&lf);
4213 hfont = SelectObject(hdc, hfont);
4214 GetTextFaceA(hdc, sizeof(buf), buf);
4215 ok(!lstrcmpiA(buf, "Arial") /* Wine */ ||
4216 !lstrcmpiA(buf, font_subst[i].name) /* XP, Vista */ ||
4217 !lstrcmpiA(buf, "MS Serif") /* Win9x */ ||
4218 !lstrcmpiA(buf, "MS Sans Serif"), /* win2k3 */
4219 "got %s for font %s\n", buf, font_subst[i].name);
4220 cs = GetTextCharset(hdc);
4221 ok(cs == expected_cs || cs == ANSI_CHARSET, "expected %d, got %d for font %s\n", expected_cs, cs, font_subst[i].name);
4222 DeleteObject(SelectObject(hdc, hfont));
4225 DeleteDC(hdc);
4228 static void test_RealizationInfo(void)
4230 struct font_realization_info {
4231 DWORD size;
4232 DWORD flags;
4233 DWORD cache_num;
4234 DWORD instance_id;
4235 DWORD unk;
4236 WORD face_index;
4237 WORD simulations;
4240 struct realization_info_t
4242 DWORD flags;
4243 DWORD cache_num;
4244 DWORD instance_id;
4247 HDC hdc;
4248 DWORD info[4], info2[10];
4249 BOOL r, have_file = FALSE;
4250 HFONT hfont, hfont_old;
4251 LOGFONTA lf;
4252 DWORD needed, read;
4253 HANDLE h;
4254 BYTE file[16], data[14];
4255 struct file_info
4257 FILETIME time;
4258 LARGE_INTEGER size;
4259 WCHAR path[MAX_PATH];
4260 } file_info;
4261 FILETIME time;
4262 LARGE_INTEGER size;
4264 if(!pGdiRealizationInfo)
4266 win_skip("GdiRealizationInfo not available\n");
4267 return;
4270 hdc = GetDC(0);
4272 memset(info, 0xcc, sizeof(info));
4273 r = pGdiRealizationInfo(hdc, info);
4274 ok(r != 0, "ret 0\n");
4275 ok((info[0] & 0xf) == 1, "info[0] = %x for the system font\n", info[0]);
4276 ok(info[3] == 0xcccccccc, "structure longer than 3 dwords\n");
4278 if (!is_truetype_font_installed("Tahoma"))
4280 skip("skipping GdiRealizationInfo with truetype font\n");
4281 goto end;
4284 memset(&lf, 0, sizeof(lf));
4285 strcpy(lf.lfFaceName, "Tahoma");
4286 lf.lfHeight = 20;
4287 lf.lfWeight = FW_BOLD;
4288 lf.lfItalic = 1;
4289 hfont = CreateFontIndirectA(&lf);
4290 hfont_old = SelectObject(hdc, hfont);
4292 memset(info, 0xcc, sizeof(info));
4293 r = pGdiRealizationInfo(hdc, info);
4294 ok(r != 0, "ret 0\n");
4295 ok((info[0] & 0xf) == 3, "info[0] = %x for arial\n", info[0]);
4296 ok(info[3] == 0xcccccccc, "structure longer than 3 dwords\n");
4298 if (pGetFontRealizationInfo)
4300 struct font_realization_info *fri = (struct font_realization_info*)info2;
4301 struct realization_info_t *ri = (struct realization_info_t*)info;
4303 /* The first DWORD represents a struct size. On a
4304 newly rebooted system setting this to < 16 results
4305 in GetFontRealizationInfo failing. However there
4306 appears to be some caching going on which results
4307 in calls after a successful call also succeeding even
4308 if the size < 16. This means we can't reliably test
4309 this behaviour. */
4311 memset(info2, 0xcc, sizeof(info2));
4312 info2[0] = 16;
4313 r = pGetFontRealizationInfo(hdc, info2);
4314 ok(r != 0, "ret 0\n");
4315 /* We may get the '24' version here if that has been previously
4316 requested. */
4317 ok(fri->size == 16 || fri->size == 24, "got %d\n", info2[0]);
4318 ok(fri->flags == ri->flags, "flags mismatch\n");
4319 ok(fri->cache_num == ri->cache_num, "cache_num mismatch\n");
4320 ok(fri->instance_id == ri->instance_id, "instance id mismatch\n");
4321 ok(info2[6] == 0xcccccccc, "got wrong dword 6, 0x%08x\n", info2[6]);
4323 memset(info2, 0xcc, sizeof(info2));
4324 info2[0] = 28;
4325 r = pGetFontRealizationInfo(hdc, info2);
4326 ok(r == FALSE, "got %d\n", r);
4328 memset(info2, 0xcc, sizeof(info2));
4329 info2[0] = 24;
4330 r = pGetFontRealizationInfo(hdc, info2);
4331 ok(r != 0, "ret 0\n");
4332 ok(fri->size == 24, "got %d\n", fri->size);
4333 ok(fri->flags == ri->flags, "flags mismatch\n");
4334 ok(fri->cache_num == ri->cache_num, "cache_num mismatch\n");
4335 ok(fri->instance_id == ri->instance_id, "instance id mismatch\n");
4336 ok(fri->simulations == 0x2, "got simulations flags 0x%04x\n", fri->simulations);
4337 ok(fri->face_index == 0, "got wrong face index %u\n", fri->face_index);
4338 ok(info2[6] == 0xcccccccc, "structure longer than 6 dwords\n");
4340 /* Test GetFontFileInfo() */
4341 /* invalid font id */
4342 SetLastError(0xdeadbeef);
4343 r = pGetFontFileInfo(0xabababab, 0, &file_info, sizeof(file_info), &needed);
4344 ok(r == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "ret %d gle %d\n", r, GetLastError());
4346 needed = 0;
4347 r = pGetFontFileInfo(fri->instance_id, 0, &file_info, sizeof(file_info), &needed);
4348 ok(r != 0 || GetLastError() == ERROR_NOACCESS, "ret %d gle %d\n", r, GetLastError());
4350 if (r)
4352 ok(needed > 0 && needed < sizeof(file_info), "got needed size %u\n", needed);
4354 h = CreateFileW(file_info.path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
4355 ok(h != INVALID_HANDLE_VALUE, "Unable to open file %d\n", GetLastError());
4357 GetFileTime(h, NULL, NULL, &time);
4358 ok(!CompareFileTime(&file_info.time, &time), "time mismatch\n");
4359 GetFileSizeEx(h, &size);
4360 ok(file_info.size.QuadPart == size.QuadPart, "size mismatch\n");
4362 /* Read first 16 bytes from the file */
4363 ReadFile(h, file, sizeof(file), &read, NULL);
4364 CloseHandle(h);
4365 have_file = TRUE;
4367 /* shorter buffer */
4368 SetLastError(0xdeadbeef);
4369 r = pGetFontFileInfo(fri->instance_id, 0, &file_info, needed - 1, &needed);
4370 ok(r == 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "ret %d gle %d\n", r, GetLastError());
4373 if (pGetFontFileData) {
4374 /* Get bytes 2 - 16 using GetFontFileData */
4375 r = pGetFontFileData(fri->instance_id, 0, 2, data, sizeof(data));
4376 ok(r != 0, "ret 0 gle %d\n", GetLastError());
4378 if (have_file)
4379 ok(!memcmp(data, file + 2, sizeof(data)), "mismatch\n");
4380 else
4381 win_skip("GetFontFileInfo() failed, skipping\n");
4385 DeleteObject(SelectObject(hdc, hfont_old));
4387 end:
4388 ReleaseDC(0, hdc);
4391 /* Tests on XP SP2 show that the ANSI version of GetTextFace does NOT include
4392 the nul in the count of characters copied when the face name buffer is not
4393 NULL, whereas it does if the buffer is NULL. Further, the Unicode version
4394 always includes it. */
4395 static void test_GetTextFace(void)
4397 static const char faceA[] = "Tahoma";
4398 static const WCHAR faceW[] = {'T','a','h','o','m','a', 0};
4399 LOGFONTA fA = {0};
4400 LOGFONTW fW = {0};
4401 char bufA[LF_FACESIZE];
4402 WCHAR bufW[LF_FACESIZE];
4403 HFONT f, g;
4404 HDC dc;
4405 int n;
4407 if(!is_font_installed("Tahoma"))
4409 skip("Tahoma is not installed so skipping this test\n");
4410 return;
4413 /* 'A' case. */
4414 memcpy(fA.lfFaceName, faceA, sizeof faceA);
4415 f = CreateFontIndirectA(&fA);
4416 ok(f != NULL, "CreateFontIndirectA failed\n");
4418 dc = GetDC(NULL);
4419 g = SelectObject(dc, f);
4420 n = GetTextFaceA(dc, sizeof bufA, bufA);
4421 ok(n == sizeof faceA - 1, "GetTextFaceA returned %d\n", n);
4422 ok(lstrcmpA(faceA, bufA) == 0, "GetTextFaceA\n");
4424 /* Play with the count arg. */
4425 bufA[0] = 'x';
4426 n = GetTextFaceA(dc, 0, bufA);
4427 ok(n == 0, "GetTextFaceA returned %d\n", n);
4428 ok(bufA[0] == 'x', "GetTextFaceA buf[0] == %d\n", bufA[0]);
4430 bufA[0] = 'x';
4431 n = GetTextFaceA(dc, 1, bufA);
4432 ok(n == 0, "GetTextFaceA returned %d\n", n);
4433 ok(bufA[0] == '\0', "GetTextFaceA buf[0] == %d\n", bufA[0]);
4435 bufA[0] = 'x'; bufA[1] = 'y';
4436 n = GetTextFaceA(dc, 2, bufA);
4437 ok(n == 1, "GetTextFaceA returned %d\n", n);
4438 ok(bufA[0] == faceA[0] && bufA[1] == '\0', "GetTextFaceA didn't copy\n");
4440 n = GetTextFaceA(dc, 0, NULL);
4441 ok(n == sizeof faceA ||
4442 broken(n == 0), /* win98, winMe */
4443 "GetTextFaceA returned %d\n", n);
4445 DeleteObject(SelectObject(dc, g));
4446 ReleaseDC(NULL, dc);
4448 /* 'W' case. */
4449 memcpy(fW.lfFaceName, faceW, sizeof faceW);
4450 SetLastError(0xdeadbeef);
4451 f = CreateFontIndirectW(&fW);
4452 if (!f && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
4454 win_skip("CreateFontIndirectW is not implemented\n");
4455 return;
4457 ok(f != NULL, "CreateFontIndirectW failed\n");
4459 dc = GetDC(NULL);
4460 g = SelectObject(dc, f);
4461 n = GetTextFaceW(dc, sizeof bufW / sizeof bufW[0], bufW);
4462 ok(n == sizeof faceW / sizeof faceW[0], "GetTextFaceW returned %d\n", n);
4463 ok(lstrcmpW(faceW, bufW) == 0, "GetTextFaceW\n");
4465 /* Play with the count arg. */
4466 bufW[0] = 'x';
4467 n = GetTextFaceW(dc, 0, bufW);
4468 ok(n == 0, "GetTextFaceW returned %d\n", n);
4469 ok(bufW[0] == 'x', "GetTextFaceW buf[0] == %d\n", bufW[0]);
4471 bufW[0] = 'x';
4472 n = GetTextFaceW(dc, 1, bufW);
4473 ok(n == 1, "GetTextFaceW returned %d\n", n);
4474 ok(bufW[0] == '\0', "GetTextFaceW buf[0] == %d\n", bufW[0]);
4476 bufW[0] = 'x'; bufW[1] = 'y';
4477 n = GetTextFaceW(dc, 2, bufW);
4478 ok(n == 2, "GetTextFaceW returned %d\n", n);
4479 ok(bufW[0] == faceW[0] && bufW[1] == '\0', "GetTextFaceW didn't copy\n");
4481 n = GetTextFaceW(dc, 0, NULL);
4482 ok(n == sizeof faceW / sizeof faceW[0], "GetTextFaceW returned %d\n", n);
4484 DeleteObject(SelectObject(dc, g));
4485 ReleaseDC(NULL, dc);
4488 static void test_orientation(void)
4490 static const char test_str[11] = "Test String";
4491 HDC hdc;
4492 LOGFONTA lf;
4493 HFONT hfont, old_hfont;
4494 SIZE size;
4496 if (!is_truetype_font_installed("Arial"))
4498 skip("Arial is not installed\n");
4499 return;
4502 hdc = CreateCompatibleDC(0);
4503 memset(&lf, 0, sizeof(lf));
4504 lstrcpyA(lf.lfFaceName, "Arial");
4505 lf.lfHeight = 72;
4506 lf.lfOrientation = lf.lfEscapement = 900;
4507 hfont = create_font("orientation", &lf);
4508 old_hfont = SelectObject(hdc, hfont);
4509 ok(GetTextExtentExPointA(hdc, test_str, sizeof(test_str), 32767, NULL, NULL, &size), "GetTextExtentExPointA failed\n");
4510 ok(near_match(311, size.cx), "cx should be about 311, got %d\n", size.cx);
4511 ok(near_match(75, size.cy), "cy should be about 75, got %d\n", size.cy);
4512 SelectObject(hdc, old_hfont);
4513 DeleteObject(hfont);
4514 DeleteDC(hdc);
4517 static void test_oemcharset(void)
4519 HDC hdc;
4520 LOGFONTA lf, clf;
4521 HFONT hfont, old_hfont;
4522 int charset;
4524 hdc = CreateCompatibleDC(0);
4525 ZeroMemory(&lf, sizeof(lf));
4526 lf.lfHeight = 12;
4527 lf.lfCharSet = OEM_CHARSET;
4528 lf.lfPitchAndFamily = FIXED_PITCH | FF_MODERN;
4529 lstrcpyA(lf.lfFaceName, "Terminal");
4530 hfont = CreateFontIndirectA(&lf);
4531 old_hfont = SelectObject(hdc, hfont);
4532 charset = GetTextCharset(hdc);
4533 todo_wine
4534 ok(charset == OEM_CHARSET, "expected %d charset, got %d\n", OEM_CHARSET, charset);
4535 hfont = SelectObject(hdc, old_hfont);
4536 GetObjectA(hfont, sizeof(clf), &clf);
4537 ok(!lstrcmpA(clf.lfFaceName, lf.lfFaceName), "expected %s face name, got %s\n", lf.lfFaceName, clf.lfFaceName);
4538 ok(clf.lfPitchAndFamily == lf.lfPitchAndFamily, "expected %x family, got %x\n", lf.lfPitchAndFamily, clf.lfPitchAndFamily);
4539 ok(clf.lfCharSet == lf.lfCharSet, "expected %d charset, got %d\n", lf.lfCharSet, clf.lfCharSet);
4540 ok(clf.lfHeight == lf.lfHeight, "expected %d height, got %d\n", lf.lfHeight, clf.lfHeight);
4541 DeleteObject(hfont);
4542 DeleteDC(hdc);
4545 static int CALLBACK create_fixed_pitch_font_proc(const LOGFONTA *lpelfe,
4546 const TEXTMETRICA *lpntme,
4547 DWORD FontType, LPARAM lParam)
4549 const NEWTEXTMETRICEXA *lpntmex = (const NEWTEXTMETRICEXA *)lpntme;
4550 CHARSETINFO csi;
4551 LOGFONTA lf = *lpelfe;
4552 HFONT hfont;
4553 DWORD found_subset;
4555 /* skip bitmap, proportional or vertical font */
4556 if ((FontType & TRUETYPE_FONTTYPE) == 0 ||
4557 (lf.lfPitchAndFamily & 0xf) != FIXED_PITCH ||
4558 lf.lfFaceName[0] == '@')
4559 return 1;
4561 /* skip linked font */
4562 if (!TranslateCharsetInfo((DWORD*)(INT_PTR)lpelfe->lfCharSet, &csi, TCI_SRCCHARSET) ||
4563 (lpntmex->ntmFontSig.fsCsb[0] & csi.fs.fsCsb[0]) == 0)
4564 return 1;
4566 /* skip linked font, like SimSun-ExtB */
4567 switch (lpelfe->lfCharSet) {
4568 case SHIFTJIS_CHARSET:
4569 found_subset = lpntmex->ntmFontSig.fsUsb[1] & (1 << 17); /* Hiragana */
4570 break;
4571 case GB2312_CHARSET:
4572 case CHINESEBIG5_CHARSET:
4573 found_subset = lpntmex->ntmFontSig.fsUsb[1] & (1 << 16); /* CJK Symbols And Punctuation */
4574 break;
4575 case HANGEUL_CHARSET:
4576 found_subset = lpntmex->ntmFontSig.fsUsb[1] & (1 << 24); /* Hangul Syllables */
4577 break;
4578 default:
4579 found_subset = lpntmex->ntmFontSig.fsUsb[0] & (1 << 0); /* Basic Latin */
4580 break;
4582 if (!found_subset)
4583 return 1;
4585 /* test with an odd height */
4586 lf.lfHeight = -19;
4587 lf.lfWidth = 0;
4588 hfont = CreateFontIndirectA(&lf);
4589 if (hfont)
4591 *(HFONT *)lParam = hfont;
4592 return 0;
4594 return 1;
4597 static void test_GetGlyphOutline(void)
4599 HDC hdc;
4600 GLYPHMETRICS gm, gm2;
4601 LOGFONTA lf;
4602 HFONT hfont, old_hfont;
4603 INT ret, ret2;
4604 const UINT fmt[] = { GGO_METRICS, GGO_BITMAP, GGO_GRAY2_BITMAP,
4605 GGO_GRAY4_BITMAP, GGO_GRAY8_BITMAP };
4606 static const struct
4608 UINT cs;
4609 UINT a;
4610 UINT w;
4611 } c[] =
4613 {ANSI_CHARSET, 0x30, 0x30},
4614 {SHIFTJIS_CHARSET, 0x82a0, 0x3042},
4615 {HANGEUL_CHARSET, 0x8141, 0xac02},
4616 {GB2312_CHARSET, 0x8141, 0x4e04},
4617 {CHINESEBIG5_CHARSET, 0xa142, 0x3001}
4619 UINT i;
4621 if (!is_truetype_font_installed("Tahoma"))
4623 skip("Tahoma is not installed\n");
4624 return;
4627 hdc = CreateCompatibleDC(0);
4628 memset(&lf, 0, sizeof(lf));
4629 lf.lfHeight = 72;
4630 lstrcpyA(lf.lfFaceName, "Tahoma");
4631 SetLastError(0xdeadbeef);
4632 hfont = CreateFontIndirectA(&lf);
4633 ok(hfont != 0, "CreateFontIndirectA error %u\n", GetLastError());
4634 old_hfont = SelectObject(hdc, hfont);
4636 memset(&gm, 0, sizeof(gm));
4637 SetLastError(0xdeadbeef);
4638 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
4639 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %u\n", GetLastError());
4641 memset(&gm, 0, sizeof(gm));
4642 SetLastError(0xdeadbeef);
4643 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, NULL);
4644 ok(ret == GDI_ERROR, "GetGlyphOutlineA should fail\n");
4645 ok(GetLastError() == 0xdeadbeef ||
4646 GetLastError() == ERROR_INVALID_PARAMETER, /* win98, winMe */
4647 "expected 0xdeadbeef, got %u\n", GetLastError());
4649 memset(&gm, 0, sizeof(gm));
4650 SetLastError(0xdeadbeef);
4651 ret = GetGlyphOutlineW(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
4652 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
4653 ok(ret != GDI_ERROR, "GetGlyphOutlineW error %u\n", GetLastError());
4655 memset(&gm, 0, sizeof(gm));
4656 SetLastError(0xdeadbeef);
4657 ret = GetGlyphOutlineW(hdc, 'A', GGO_METRICS, &gm, 0, NULL, NULL);
4658 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
4660 ok(ret == GDI_ERROR, "GetGlyphOutlineW should fail\n");
4661 ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %u\n", GetLastError());
4664 /* test for needed buffer size request on space char */
4665 memset(&gm, 0, sizeof(gm));
4666 SetLastError(0xdeadbeef);
4667 ret = GetGlyphOutlineW(hdc, ' ', GGO_NATIVE, &gm, 0, NULL, &mat);
4668 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
4670 ok(ret == 0, "GetGlyphOutlineW should return 0 buffer size for space char\n");
4671 ok(gm.gmBlackBoxX == 1, "Expected 1, got %u\n", gm.gmBlackBoxX);
4672 ok(gm.gmBlackBoxY == 1, "Expected 1, got %u\n", gm.gmBlackBoxY);
4675 /* requesting buffer size for space char + error */
4676 memset(&gm, 0, sizeof(gm));
4677 SetLastError(0xdeadbeef);
4678 ret = GetGlyphOutlineW(0, ' ', GGO_NATIVE, &gm, 0, NULL, NULL);
4679 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
4681 ok(ret == GDI_ERROR, "GetGlyphOutlineW should return GDI_ERROR\n");
4682 ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %u\n", GetLastError());
4683 ok(gm.gmBlackBoxX == 0, "Expected 0, got %u\n", gm.gmBlackBoxX);
4684 ok(gm.gmBlackBoxY == 0, "Expected 0, got %u\n", gm.gmBlackBoxY);
4687 /* test GetGlyphOutline with a buffer too small */
4688 SetLastError(0xdeadbeef);
4689 ret = GetGlyphOutlineA(hdc, 'A', GGO_NATIVE, &gm, sizeof(i), &i, &mat);
4690 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
4691 ok(ret == GDI_ERROR, "GetGlyphOutlineW should return an error when the buffer size is too small.\n");
4693 for (i = 0; i < sizeof(fmt) / sizeof(fmt[0]); ++i)
4695 DWORD dummy;
4697 memset(&gm, 0xab, sizeof(gm));
4698 SetLastError(0xdeadbeef);
4699 ret = GetGlyphOutlineW(hdc, ' ', fmt[i], &gm, 0, NULL, &mat);
4700 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
4702 if (fmt[i] == GGO_METRICS)
4703 ok(ret != GDI_ERROR, "%2d:GetGlyphOutlineW should succeed, got %d\n", fmt[i], ret);
4704 else
4705 ok(ret == 0, "%2d:GetGlyphOutlineW should return 0, got %d\n", fmt[i], ret);
4706 ok(gm.gmBlackBoxX == 1, "%2d:expected 1, got %u\n", fmt[i], gm.gmBlackBoxX);
4707 ok(gm.gmBlackBoxY == 1, "%2d:expected 1, got %u\n", fmt[i], gm.gmBlackBoxY);
4710 memset(&gm, 0xab, sizeof(gm));
4711 SetLastError(0xdeadbeef);
4712 ret = GetGlyphOutlineW(hdc, ' ', fmt[i], &gm, 0, &dummy, &mat);
4713 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
4715 if (fmt[i] == GGO_METRICS)
4716 ok(ret != GDI_ERROR, "%2d:GetGlyphOutlineW should succeed, got %d\n", fmt[i], ret);
4717 else
4718 ok(ret == 0, "%2d:GetGlyphOutlineW should return 0, got %d\n", fmt[i], ret);
4719 ok(gm.gmBlackBoxX == 1, "%2d:expected 1, got %u\n", fmt[i], gm.gmBlackBoxX);
4720 ok(gm.gmBlackBoxY == 1, "%2d:expected 1, got %u\n", fmt[i], gm.gmBlackBoxY);
4723 memset(&gm, 0xab, sizeof(gm));
4724 SetLastError(0xdeadbeef);
4725 ret = GetGlyphOutlineW(hdc, ' ', fmt[i], &gm, sizeof(dummy), NULL, &mat);
4726 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
4728 if (fmt[i] == GGO_METRICS)
4729 ok(ret != GDI_ERROR, "%2d:GetGlyphOutlineW should succeed, got %d\n", fmt[i], ret);
4730 else
4731 ok(ret == 0, "%2d:GetGlyphOutlineW should return 0, got %d\n", fmt[i], ret);
4732 ok(gm.gmBlackBoxX == 1, "%2d:expected 1, got %u\n", fmt[i], gm.gmBlackBoxX);
4733 ok(gm.gmBlackBoxY == 1, "%2d:expected 1, got %u\n", fmt[i], gm.gmBlackBoxY);
4736 memset(&gm, 0xab, sizeof(gm));
4737 SetLastError(0xdeadbeef);
4738 ret = GetGlyphOutlineW(hdc, ' ', fmt[i], &gm, sizeof(dummy), &dummy, &mat);
4739 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
4741 if (fmt[i] == GGO_METRICS) {
4742 ok(ret != GDI_ERROR, "%2d:GetGlyphOutlineW should succeed, got %d\n", fmt[i], ret);
4743 ok(gm.gmBlackBoxX == 1, "%2d:expected 1, got %u\n", fmt[i], gm.gmBlackBoxX);
4744 ok(gm.gmBlackBoxY == 1, "%2d:expected 1, got %u\n", fmt[i], gm.gmBlackBoxY);
4746 else
4748 ok(ret == GDI_ERROR, "%2d:GetGlyphOutlineW should return GDI_ERROR, got %d\n", fmt[i], ret);
4749 memset(&gm2, 0xab, sizeof(gm2));
4750 ok(memcmp(&gm, &gm2, sizeof(GLYPHMETRICS)) == 0,
4751 "%2d:GLYPHMETRICS shouldn't be touched on error\n", fmt[i]);
4756 SelectObject(hdc, old_hfont);
4757 DeleteObject(hfont);
4759 for (i = 0; i < sizeof c / sizeof c[0]; ++i)
4761 static const MAT2 rotate_mat = {{0, 0}, {0, -1}, {0, 1}, {0, 0}};
4762 TEXTMETRICA tm;
4764 lf.lfFaceName[0] = '\0';
4765 lf.lfCharSet = c[i].cs;
4766 lf.lfPitchAndFamily = 0;
4767 if (EnumFontFamiliesExA(hdc, &lf, create_font_proc, (LPARAM)&hfont, 0))
4769 skip("TrueType font for charset %u is not installed\n", c[i].cs);
4770 continue;
4773 old_hfont = SelectObject(hdc, hfont);
4775 /* expected to ignore superfluous bytes (sigle-byte character) */
4776 ret = GetGlyphOutlineA(hdc, 0x8041, GGO_BITMAP, &gm, 0, NULL, &mat);
4777 ret2 = GetGlyphOutlineA(hdc, 0x41, GGO_BITMAP, &gm2, 0, NULL, &mat);
4778 ok(ret == ret2 && memcmp(&gm, &gm2, sizeof gm) == 0, "%d %d\n", ret, ret2);
4780 ret = GetGlyphOutlineA(hdc, 0xcc8041, GGO_BITMAP, &gm, 0, NULL, &mat);
4781 ok(ret == ret2 && memcmp(&gm, &gm2, sizeof gm) == 0,
4782 "Expected to ignore superfluous bytes, got %d %d\n", ret, ret2);
4784 /* expected to ignore superfluous bytes (double-byte character) */
4785 ret = GetGlyphOutlineA(hdc, c[i].a, GGO_BITMAP, &gm, 0, NULL, &mat);
4786 ret2 = GetGlyphOutlineA(hdc, c[i].a | 0xdead0000, GGO_BITMAP, &gm2, 0, NULL, &mat);
4787 ok(ret == ret2 && memcmp(&gm, &gm2, sizeof gm) == 0,
4788 "Expected to ignore superfluous bytes, got %d %d\n", ret, ret2);
4790 /* expected to match wide-char version results */
4791 ret2 = GetGlyphOutlineW(hdc, c[i].w, GGO_BITMAP, &gm2, 0, NULL, &mat);
4792 ok(ret == ret2 && memcmp(&gm, &gm2, sizeof gm) == 0, "%d %d\n", ret, ret2);
4794 if (EnumFontFamiliesExA(hdc, &lf, create_fixed_pitch_font_proc, (LPARAM)&hfont, 0))
4796 skip("Fixed-pitch TrueType font for charset %u is not available\n", c[i].cs);
4797 continue;
4799 DeleteObject(SelectObject(hdc, hfont));
4800 if (c[i].a <= 0xff)
4802 DeleteObject(SelectObject(hdc, old_hfont));
4803 continue;
4806 ret = GetObjectA(hfont, sizeof lf, &lf);
4807 ok(ret > 0, "GetObject error %u\n", GetLastError());
4809 ret = GetTextMetricsA(hdc, &tm);
4810 ok(ret, "GetTextMetrics error %u\n", GetLastError());
4811 ret = GetGlyphOutlineA(hdc, c[i].a, GGO_METRICS, &gm2, 0, NULL, &mat);
4812 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %u\n", GetLastError());
4813 trace("Tests with height=%d,avg=%d,full=%d,face=%s,charset=%d\n",
4814 -lf.lfHeight, tm.tmAveCharWidth, gm2.gmCellIncX, lf.lfFaceName, lf.lfCharSet);
4815 ok(gm2.gmCellIncX == tm.tmAveCharWidth * 2 || broken(gm2.gmCellIncX == -lf.lfHeight),
4816 "expected %d, got %d (%s:%d)\n",
4817 tm.tmAveCharWidth * 2, gm2.gmCellIncX, lf.lfFaceName, lf.lfCharSet);
4819 ret = GetGlyphOutlineA(hdc, c[i].a, GGO_METRICS, &gm2, 0, NULL, &rotate_mat);
4820 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %u\n", GetLastError());
4821 ok(gm2.gmCellIncY == -lf.lfHeight,
4822 "expected %d, got %d (%s:%d)\n",
4823 -lf.lfHeight, gm2.gmCellIncY, lf.lfFaceName, lf.lfCharSet);
4825 lf.lfItalic = TRUE;
4826 hfont = CreateFontIndirectA(&lf);
4827 ok(hfont != NULL, "CreateFontIndirect error %u\n", GetLastError());
4828 DeleteObject(SelectObject(hdc, hfont));
4829 ret = GetTextMetricsA(hdc, &tm);
4830 ok(ret, "GetTextMetrics error %u\n", GetLastError());
4831 ret = GetGlyphOutlineA(hdc, c[i].a, GGO_METRICS, &gm2, 0, NULL, &mat);
4832 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %u\n", GetLastError());
4833 ok(gm2.gmCellIncX == tm.tmAveCharWidth * 2 || broken(gm2.gmCellIncX == -lf.lfHeight),
4834 "expected %d, got %d (%s:%d)\n",
4835 tm.tmAveCharWidth * 2, gm2.gmCellIncX, lf.lfFaceName, lf.lfCharSet);
4837 lf.lfItalic = FALSE;
4838 lf.lfEscapement = lf.lfOrientation = 2700;
4839 hfont = CreateFontIndirectA(&lf);
4840 ok(hfont != NULL, "CreateFontIndirect error %u\n", GetLastError());
4841 DeleteObject(SelectObject(hdc, hfont));
4842 ret = GetGlyphOutlineA(hdc, c[i].a, GGO_METRICS, &gm2, 0, NULL, &mat);
4843 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %u\n", GetLastError());
4844 ok(gm2.gmCellIncY == -lf.lfHeight,
4845 "expected %d, got %d (%s:%d)\n",
4846 -lf.lfHeight, gm2.gmCellIncY, lf.lfFaceName, lf.lfCharSet);
4848 hfont = SelectObject(hdc, old_hfont);
4849 DeleteObject(hfont);
4852 DeleteDC(hdc);
4855 /* bug #9995: there is a limit to the character width that can be specified */
4856 static void test_GetTextMetrics2(const char *fontname, int font_height)
4858 HFONT of, hf;
4859 HDC hdc;
4860 TEXTMETRICA tm;
4861 BOOL ret;
4862 int ave_width, height, width, ratio, scale;
4864 if (!is_truetype_font_installed( fontname)) {
4865 skip("%s is not installed\n", fontname);
4866 return;
4868 hdc = CreateCompatibleDC(0);
4869 ok( hdc != NULL, "CreateCompatibleDC failed\n");
4870 /* select width = 0 */
4871 hf = CreateFontA(font_height, 0, 0, 0, FW_REGULAR, FALSE, FALSE, FALSE,
4872 DEFAULT_CHARSET, OUT_TT_PRECIS, CLIP_LH_ANGLES,
4873 DEFAULT_QUALITY, VARIABLE_PITCH,
4874 fontname);
4875 ok( hf != NULL, "CreateFontA(%s, %d) failed\n", fontname, font_height);
4876 of = SelectObject( hdc, hf);
4877 ret = GetTextMetricsA( hdc, &tm);
4878 ok(ret, "GetTextMetricsA error %u\n", GetLastError());
4879 height = tm.tmHeight;
4880 ave_width = tm.tmAveCharWidth;
4881 SelectObject( hdc, of);
4882 DeleteObject( hf);
4884 trace("height %d, ave width %d\n", height, ave_width);
4886 for (width = ave_width * 2; /* nothing*/; width += ave_width)
4888 hf = CreateFontA(height, width, 0, 0, FW_REGULAR, FALSE, FALSE, FALSE,
4889 DEFAULT_CHARSET, OUT_TT_PRECIS, CLIP_LH_ANGLES,
4890 DEFAULT_QUALITY, VARIABLE_PITCH, fontname);
4891 ok(hf != 0, "CreateFont failed\n");
4892 of = SelectObject(hdc, hf);
4893 ret = GetTextMetricsA(hdc, &tm);
4894 ok(ret, "GetTextMetrics error %u\n", GetLastError());
4895 SelectObject(hdc, of);
4896 DeleteObject(hf);
4898 if (match_off_by_1(tm.tmAveCharWidth, ave_width, FALSE) || width / height > 200)
4899 break;
4902 DeleteDC(hdc);
4904 ratio = width / height;
4905 scale = width / ave_width;
4907 trace("max width/height ratio (%d / %d) %d, max width scale (%d / %d) %d\n",
4908 width, height, ratio, width, ave_width, scale);
4910 ok(ratio >= 90 && ratio <= 110, "expected width/height ratio 90-110, got %d\n", ratio);
4913 static void test_CreateFontIndirect(void)
4915 LOGFONTA lf, getobj_lf;
4916 int ret, i;
4917 HFONT hfont;
4918 char TestName[][16] = {"Arial", "Arial Bold", "Arial Italic", "Arial Baltic"};
4920 memset(&lf, 0, sizeof(lf));
4921 lf.lfCharSet = ANSI_CHARSET;
4922 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
4923 lf.lfHeight = 16;
4924 lf.lfWidth = 16;
4925 lf.lfQuality = DEFAULT_QUALITY;
4926 lf.lfItalic = FALSE;
4927 lf.lfWeight = FW_DONTCARE;
4929 for (i = 0; i < sizeof(TestName)/sizeof(TestName[0]); i++)
4931 lstrcpyA(lf.lfFaceName, TestName[i]);
4932 hfont = CreateFontIndirectA(&lf);
4933 ok(hfont != 0, "CreateFontIndirectA failed\n");
4934 SetLastError(0xdeadbeef);
4935 ret = GetObjectA(hfont, sizeof(getobj_lf), &getobj_lf);
4936 ok(ret, "GetObject failed: %d\n", GetLastError());
4937 ok(lf.lfItalic == getobj_lf.lfItalic, "lfItalic: expect %02x got %02x\n", lf.lfItalic, getobj_lf.lfItalic);
4938 ok(lf.lfWeight == getobj_lf.lfWeight ||
4939 broken((SHORT)lf.lfWeight == getobj_lf.lfWeight), /* win9x */
4940 "lfWeight: expect %08x got %08x\n", lf.lfWeight, getobj_lf.lfWeight);
4941 ok(!lstrcmpA(lf.lfFaceName, getobj_lf.lfFaceName) ||
4942 broken(!memcmp(lf.lfFaceName, getobj_lf.lfFaceName, LF_FACESIZE-1)), /* win9x doesn't ensure '\0' termination */
4943 "font names don't match: %s != %s\n", lf.lfFaceName, getobj_lf.lfFaceName);
4944 DeleteObject(hfont);
4948 static void test_CreateFontIndirectEx(void)
4950 ENUMLOGFONTEXDVA lfex;
4951 HFONT hfont;
4953 if (!pCreateFontIndirectExA)
4955 win_skip("CreateFontIndirectExA is not available\n");
4956 return;
4959 if (!is_truetype_font_installed("Arial"))
4961 skip("Arial is not installed\n");
4962 return;
4965 SetLastError(0xdeadbeef);
4966 hfont = pCreateFontIndirectExA(NULL);
4967 ok(hfont == NULL, "got %p\n", hfont);
4968 ok(GetLastError() == 0xdeadbeef, "got error %d\n", GetLastError());
4970 memset(&lfex, 0, sizeof(lfex));
4971 lstrcpyA(lfex.elfEnumLogfontEx.elfLogFont.lfFaceName, "Arial");
4972 hfont = pCreateFontIndirectExA(&lfex);
4973 ok(hfont != 0, "CreateFontIndirectEx failed\n");
4974 if (hfont)
4975 check_font("Arial", &lfex.elfEnumLogfontEx.elfLogFont, hfont);
4976 DeleteObject(hfont);
4979 static void free_font(void *font)
4981 UnmapViewOfFile(font);
4984 static void *load_font(const char *font_name, DWORD *font_size)
4986 char file_name[MAX_PATH];
4987 HANDLE file, mapping;
4988 void *font;
4990 if (!GetWindowsDirectoryA(file_name, sizeof(file_name))) return NULL;
4991 strcat(file_name, "\\fonts\\");
4992 strcat(file_name, font_name);
4994 file = CreateFileA(file_name, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, 0);
4995 if (file == INVALID_HANDLE_VALUE) return NULL;
4997 *font_size = GetFileSize(file, NULL);
4999 mapping = CreateFileMappingA(file, NULL, PAGE_READONLY, 0, 0, NULL);
5000 if (!mapping)
5002 CloseHandle(file);
5003 return NULL;
5006 font = MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0);
5008 CloseHandle(file);
5009 CloseHandle(mapping);
5010 return font;
5013 static void test_AddFontMemResource(void)
5015 void *font;
5016 DWORD font_size, num_fonts;
5017 HANDLE ret;
5018 BOOL bRet;
5020 if (!pAddFontMemResourceEx || !pRemoveFontMemResourceEx)
5022 win_skip("AddFontMemResourceEx is not available on this platform\n");
5023 return;
5026 font = load_font("sserife.fon", &font_size);
5027 if (!font)
5029 skip("Unable to locate and load font sserife.fon\n");
5030 return;
5033 SetLastError(0xdeadbeef);
5034 ret = pAddFontMemResourceEx(NULL, 0, NULL, NULL);
5035 ok(!ret, "AddFontMemResourceEx should fail\n");
5036 ok(GetLastError() == ERROR_INVALID_PARAMETER,
5037 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
5038 GetLastError());
5040 SetLastError(0xdeadbeef);
5041 ret = pAddFontMemResourceEx(NULL, 10, NULL, NULL);
5042 ok(!ret, "AddFontMemResourceEx should fail\n");
5043 ok(GetLastError() == ERROR_INVALID_PARAMETER,
5044 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
5045 GetLastError());
5047 SetLastError(0xdeadbeef);
5048 ret = pAddFontMemResourceEx(NULL, 0, NULL, &num_fonts);
5049 ok(!ret, "AddFontMemResourceEx should fail\n");
5050 ok(GetLastError() == ERROR_INVALID_PARAMETER,
5051 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
5052 GetLastError());
5054 SetLastError(0xdeadbeef);
5055 ret = pAddFontMemResourceEx(NULL, 10, NULL, &num_fonts);
5056 ok(!ret, "AddFontMemResourceEx should fail\n");
5057 ok(GetLastError() == ERROR_INVALID_PARAMETER,
5058 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
5059 GetLastError());
5061 SetLastError(0xdeadbeef);
5062 ret = pAddFontMemResourceEx(font, 0, NULL, NULL);
5063 ok(!ret, "AddFontMemResourceEx should fail\n");
5064 ok(GetLastError() == ERROR_INVALID_PARAMETER,
5065 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
5066 GetLastError());
5068 SetLastError(0xdeadbeef);
5069 ret = pAddFontMemResourceEx(font, 10, NULL, NULL);
5070 ok(!ret, "AddFontMemResourceEx should fail\n");
5071 ok(GetLastError() == ERROR_INVALID_PARAMETER,
5072 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
5073 GetLastError());
5075 num_fonts = 0xdeadbeef;
5076 SetLastError(0xdeadbeef);
5077 ret = pAddFontMemResourceEx(font, 0, NULL, &num_fonts);
5078 ok(!ret, "AddFontMemResourceEx should fail\n");
5079 ok(GetLastError() == ERROR_INVALID_PARAMETER,
5080 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
5081 GetLastError());
5082 ok(num_fonts == 0xdeadbeef, "number of loaded fonts should be 0xdeadbeef\n");
5084 if (0) /* hangs under windows 2000 */
5086 num_fonts = 0xdeadbeef;
5087 SetLastError(0xdeadbeef);
5088 ret = pAddFontMemResourceEx(font, 10, NULL, &num_fonts);
5089 ok(!ret, "AddFontMemResourceEx should fail\n");
5090 ok(GetLastError() == 0xdeadbeef,
5091 "Expected GetLastError() to return 0xdeadbeef, got %u\n",
5092 GetLastError());
5093 ok(num_fonts == 0xdeadbeef, "number of loaded fonts should be 0xdeadbeef\n");
5096 num_fonts = 0xdeadbeef;
5097 SetLastError(0xdeadbeef);
5098 ret = pAddFontMemResourceEx(font, font_size, NULL, &num_fonts);
5099 ok(ret != 0, "AddFontMemResourceEx error %d\n", GetLastError());
5100 ok(num_fonts != 0xdeadbeef, "number of loaded fonts should not be 0xdeadbeef\n");
5101 ok(num_fonts != 0, "number of loaded fonts should not be 0\n");
5103 free_font(font);
5105 SetLastError(0xdeadbeef);
5106 bRet = pRemoveFontMemResourceEx(ret);
5107 ok(bRet, "RemoveFontMemResourceEx error %d\n", GetLastError());
5109 /* test invalid pointer to number of loaded fonts */
5110 font = load_font("sserife.fon", &font_size);
5111 ok(font != NULL, "Unable to locate and load font sserife.fon\n");
5113 SetLastError(0xdeadbeef);
5114 ret = pAddFontMemResourceEx(font, font_size, NULL, (void *)0xdeadbeef);
5115 ok(!ret, "AddFontMemResourceEx should fail\n");
5116 ok(GetLastError() == 0xdeadbeef,
5117 "Expected GetLastError() to return 0xdeadbeef, got %u\n",
5118 GetLastError());
5120 SetLastError(0xdeadbeef);
5121 ret = pAddFontMemResourceEx(font, font_size, NULL, NULL);
5122 ok(!ret, "AddFontMemResourceEx should fail\n");
5123 ok(GetLastError() == ERROR_INVALID_PARAMETER,
5124 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
5125 GetLastError());
5127 free_font(font);
5130 static INT CALLBACK enum_fonts_proc(const LOGFONTA *elf, const TEXTMETRICA *ntm, DWORD type, LPARAM lparam)
5132 LOGFONTA *lf;
5134 if (type != TRUETYPE_FONTTYPE) return 1;
5136 ok(ntm->tmWeight == elf->lfWeight, "expected %d got %d\n", ntm->tmWeight, elf->lfWeight);
5138 lf = (LOGFONTA *)lparam;
5139 *lf = *elf;
5140 return 0;
5143 static INT CALLBACK enum_all_fonts_proc(const LOGFONTA *elf, const TEXTMETRICA *ntm, DWORD type, LPARAM lparam)
5145 int ret;
5146 LOGFONTA *lf;
5148 if (type != TRUETYPE_FONTTYPE) return 1;
5150 lf = (LOGFONTA *)lparam;
5151 ret = strcmp(lf->lfFaceName, elf->lfFaceName);
5152 if(ret == 0)
5154 ok(ntm->tmWeight == elf->lfWeight, "expected %d got %d\n", ntm->tmWeight, elf->lfWeight);
5155 *lf = *elf;
5156 return 0;
5158 return 1;
5161 static INT CALLBACK enum_with_magic_retval_proc(const LOGFONTA *elf, const TEXTMETRICA *ntm, DWORD type, LPARAM lparam)
5163 return lparam;
5166 static void test_EnumFonts(void)
5168 int ret;
5169 LOGFONTA lf;
5170 HDC hdc;
5172 if (!is_truetype_font_installed("Arial"))
5174 skip("Arial is not installed\n");
5175 return;
5178 /* Windows uses localized font face names, so Arial Bold won't be found */
5179 if (PRIMARYLANGID(GetUserDefaultLangID()) != LANG_ENGLISH)
5181 skip("User locale is not English, skipping the test\n");
5182 return;
5185 hdc = CreateCompatibleDC(0);
5187 /* check that the enumproc's retval is returned */
5188 ret = EnumFontFamiliesA(hdc, NULL, enum_with_magic_retval_proc, 0xcafe);
5189 ok(ret == 0xcafe, "got %08x\n", ret);
5191 ret = EnumFontFamiliesA(hdc, "Arial", enum_fonts_proc, (LPARAM)&lf);
5192 ok(!ret, "font Arial is not enumerated\n");
5193 ret = strcmp(lf.lfFaceName, "Arial");
5194 ok(!ret, "expected Arial got %s\n", lf.lfFaceName);
5195 ok(lf.lfWeight == FW_NORMAL, "expected FW_NORMAL got %d\n", lf.lfWeight);
5197 strcpy(lf.lfFaceName, "Arial");
5198 ret = EnumFontFamiliesA(hdc, NULL, enum_all_fonts_proc, (LPARAM)&lf);
5199 ok(!ret, "font Arial is not enumerated\n");
5200 ret = strcmp(lf.lfFaceName, "Arial");
5201 ok(!ret, "expected Arial got %s\n", lf.lfFaceName);
5202 ok(lf.lfWeight == FW_NORMAL, "expected FW_NORMAL got %d\n", lf.lfWeight);
5204 ret = EnumFontFamiliesA(hdc, "Arial Bold", enum_fonts_proc, (LPARAM)&lf);
5205 ok(!ret, "font Arial Bold is not enumerated\n");
5206 ret = strcmp(lf.lfFaceName, "Arial");
5207 ok(!ret, "expected Arial got %s\n", lf.lfFaceName);
5208 ok(lf.lfWeight == FW_BOLD, "expected FW_BOLD got %d\n", lf.lfWeight);
5210 strcpy(lf.lfFaceName, "Arial Bold");
5211 ret = EnumFontFamiliesA(hdc, NULL, enum_all_fonts_proc, (LPARAM)&lf);
5212 ok(ret, "font Arial Bold should not be enumerated\n");
5214 ret = EnumFontFamiliesA(hdc, "Arial Bold Italic", enum_fonts_proc, (LPARAM)&lf);
5215 ok(!ret, "font Arial Bold Italic is not enumerated\n");
5216 ret = strcmp(lf.lfFaceName, "Arial");
5217 ok(!ret, "expected Arial got %s\n", lf.lfFaceName);
5218 ok(lf.lfWeight == FW_BOLD, "expected FW_BOLD got %d\n", lf.lfWeight);
5220 strcpy(lf.lfFaceName, "Arial Bold Italic");
5221 ret = EnumFontFamiliesA(hdc, NULL, enum_all_fonts_proc, (LPARAM)&lf);
5222 ok(ret, "font Arial Bold Italic should not be enumerated\n");
5224 ret = EnumFontFamiliesA(hdc, "Arial Italic Bold", enum_fonts_proc, (LPARAM)&lf);
5225 ok(ret, "font Arial Italic Bold should not be enumerated\n");
5227 strcpy(lf.lfFaceName, "Arial Italic Bold");
5228 ret = EnumFontFamiliesA(hdc, NULL, enum_all_fonts_proc, (LPARAM)&lf);
5229 ok(ret, "font Arial Italic Bold should not be enumerated\n");
5231 DeleteDC(hdc);
5234 static INT CALLBACK enum_ms_shell_dlg_proc(const LOGFONTA *lf, const TEXTMETRICA *ntm, DWORD type, LPARAM lParam)
5236 struct enum_fullname_data *efnd = (struct enum_fullname_data *)lParam;
5238 if (0) /* Disabled to limit console spam */
5239 trace("enumed font \"%s\", charset %d, height %d, weight %d, italic %d\n",
5240 lf->lfFaceName, lf->lfCharSet, lf->lfHeight, lf->lfWeight, lf->lfItalic);
5242 if (type != TRUETYPE_FONTTYPE) return 1;
5243 if (strcmp(lf->lfFaceName, "MS Shell Dlg") != 0) return 1;
5245 if (efnd->total >= efnd->size)
5247 efnd->size = max( (efnd->total + 1) * 2, 256 );
5248 efnd->elf = heap_realloc( efnd->elf, efnd->size * sizeof(*efnd->elf) );
5249 if (!efnd->elf) return 0;
5251 efnd->elf[efnd->total++] = *(ENUMLOGFONTA *)lf;
5252 return 0;
5255 static INT CALLBACK enum_ms_shell_dlg2_proc(const LOGFONTA *lf, const TEXTMETRICA *ntm, DWORD type, LPARAM lParam)
5257 struct enum_fullname_data *efnd = (struct enum_fullname_data *)lParam;
5259 if (0) /* Disabled to limit console spam */
5260 trace("enumed font \"%s\", charset %d, height %d, weight %d, italic %d\n",
5261 lf->lfFaceName, lf->lfCharSet, lf->lfHeight, lf->lfWeight, lf->lfItalic);
5263 if (type != TRUETYPE_FONTTYPE) return 1;
5264 if (strcmp(lf->lfFaceName, "MS Shell Dlg 2") != 0) return 1;
5266 if (efnd->total >= efnd->size)
5268 efnd->size = max( (efnd->total + 1) * 2, 256 );
5269 efnd->elf = heap_realloc( efnd->elf, efnd->size * sizeof(*efnd->elf) );
5270 if (!efnd->elf) return 0;
5272 efnd->elf[efnd->total++] = *(ENUMLOGFONTA *)lf;
5273 return 0;
5276 static void test_EnumFonts_subst(void)
5278 int ret;
5279 LOGFONTA lf;
5280 HDC hdc;
5281 struct enum_fullname_data efnd;
5283 ret = is_font_installed("MS Shell Dlg");
5284 ok(ret, "MS Shell Dlg should be enumerated\n");
5285 ret = is_truetype_font_installed("MS Shell Dlg");
5286 ok(ret, "MS Shell Dlg should be enumerated as a TrueType font\n");
5288 ret = is_font_installed("MS Shell Dlg 2");
5289 ok(ret, "MS Shell Dlg 2 should be enumerated\n");
5290 ret = is_truetype_font_installed("MS Shell Dlg 2");
5291 ok(ret, "MS Shell Dlg 2 should be enumerated as a TrueType font\n");
5293 hdc = CreateCompatibleDC(0);
5295 memset(&efnd, 0, sizeof(efnd));
5296 ret = EnumFontFamiliesExA(hdc, NULL, enum_ms_shell_dlg_proc, (LPARAM)&efnd, 0);
5297 ok(ret, "MS Shell Dlg should not be enumerated\n");
5298 ok(!efnd.total, "MS Shell Dlg should not be enumerated\n");
5300 memset(&lf, 0, sizeof(lf));
5301 lf.lfCharSet = DEFAULT_CHARSET;
5303 efnd.total = 0;
5304 strcpy(lf.lfFaceName, "MS Shell Dlg");
5305 ret = EnumFontFamiliesExA(hdc, &lf, enum_ms_shell_dlg_proc, (LPARAM)&efnd, 0);
5306 ok(!ret, "MS Shell Dlg should be enumerated\n");
5307 ok(efnd.total > 0, "MS Shell Dlg should be enumerated\n");
5308 if (efnd.total)
5310 ret = strcmp((const char *)efnd.elf[0].elfLogFont.lfFaceName, "MS Shell Dlg");
5311 ok(!ret, "expected MS Shell Dlg, got %s\n", efnd.elf[0].elfLogFont.lfFaceName);
5312 ret = strcmp((const char *)efnd.elf[0].elfFullName, "MS Shell Dlg");
5313 ok(ret, "did not expect MS Shell Dlg\n");
5316 efnd.total = 0;
5317 ret = EnumFontFamiliesExA(hdc, NULL, enum_ms_shell_dlg2_proc, (LPARAM)&efnd, 0);
5318 ok(ret, "MS Shell Dlg 2 should not be enumerated\n");
5319 ok(!efnd.total, "MS Shell Dlg 2 should not be enumerated\n");
5321 efnd.total = 0;
5322 strcpy(lf.lfFaceName, "MS Shell Dlg 2");
5323 ret = EnumFontFamiliesExA(hdc, &lf, enum_ms_shell_dlg2_proc, (LPARAM)&efnd, 0);
5324 ok(!ret, "MS Shell Dlg 2 should be enumerated\n");
5325 ok(efnd.total > 0, "MS Shell Dlg 2 should be enumerated\n");
5326 if (efnd.total)
5328 ret = strcmp((const char *)efnd.elf[0].elfLogFont.lfFaceName, "MS Shell Dlg 2");
5329 ok(!ret, "expected MS Shell Dlg 2, got %s\n", efnd.elf[0].elfLogFont.lfFaceName);
5330 ret = strcmp((const char *)efnd.elf[0].elfFullName, "MS Shell Dlg 2");
5331 ok(ret, "did not expect MS Shell Dlg 2\n");
5334 heap_free(efnd.elf);
5335 DeleteDC(hdc);
5338 static INT CALLBACK is_font_installed_fullname_proc(const LOGFONTA *lf, const TEXTMETRICA *ntm, DWORD type, LPARAM lParam)
5340 const ENUMLOGFONTA *elf = (const ENUMLOGFONTA *)lf;
5341 const char *fullname = (const char *)lParam;
5343 if (!strcmp((const char *)elf->elfFullName, fullname)) return 0;
5345 return 1;
5348 static BOOL is_font_installed_fullname(const char *family, const char *fullname)
5350 HDC hdc = GetDC(0);
5351 BOOL ret = FALSE;
5353 if(!EnumFontFamiliesA(hdc, family, is_font_installed_fullname_proc, (LPARAM)fullname))
5354 ret = TRUE;
5356 ReleaseDC(0, hdc);
5357 return ret;
5360 static void test_fullname(void)
5362 static const char *TestName[] = {"Lucida Sans Demibold Roman", "Lucida Sans Italic", "Lucida Sans Regular"};
5363 WCHAR bufW[LF_FULLFACESIZE];
5364 char bufA[LF_FULLFACESIZE];
5365 HFONT hfont, of;
5366 LOGFONTA lf;
5367 HDC hdc;
5368 int i;
5369 DWORD ret;
5371 hdc = CreateCompatibleDC(0);
5372 ok(hdc != NULL, "CreateCompatibleDC failed\n");
5374 memset(&lf, 0, sizeof(lf));
5375 lf.lfCharSet = ANSI_CHARSET;
5376 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
5377 lf.lfHeight = 16;
5378 lf.lfWidth = 16;
5379 lf.lfQuality = DEFAULT_QUALITY;
5380 lf.lfItalic = FALSE;
5381 lf.lfWeight = FW_DONTCARE;
5383 for (i = 0; i < sizeof(TestName) / sizeof(TestName[0]); i++)
5385 if (!is_font_installed_fullname("Lucida Sans", TestName[i]))
5387 skip("%s is not installed\n", TestName[i]);
5388 continue;
5391 lstrcpyA(lf.lfFaceName, TestName[i]);
5392 hfont = CreateFontIndirectA(&lf);
5393 ok(hfont != 0, "CreateFontIndirectA failed\n");
5395 of = SelectObject(hdc, hfont);
5396 bufW[0] = 0;
5397 bufA[0] = 0;
5398 ret = get_ttf_nametable_entry(hdc, TT_NAME_ID_FULL_NAME, bufW, sizeof(bufW), TT_MS_LANGID_ENGLISH_UNITED_STATES);
5399 ok(ret, "face full name could not be read\n");
5400 WideCharToMultiByte(CP_ACP, 0, bufW, -1, bufA, sizeof(bufA), NULL, FALSE);
5401 ok(!lstrcmpA(bufA, TestName[i]), "font full names don't match: %s != %s\n", TestName[i], bufA);
5402 SelectObject(hdc, of);
5403 DeleteObject(hfont);
5405 DeleteDC(hdc);
5408 static WCHAR *prepend_at(WCHAR *family)
5410 if (!family)
5411 return NULL;
5413 memmove(family + 1, family, (lstrlenW(family) + 1) * sizeof(WCHAR));
5414 family[0] = '@';
5415 return family;
5418 static void test_fullname2_helper(const char *Family)
5420 char *FamilyName, *FaceName, *StyleName, *otmStr;
5421 struct enum_fullname_data efnd;
5422 WCHAR *bufW;
5423 char *bufA;
5424 HFONT hfont, of;
5425 LOGFONTA lf;
5426 HDC hdc;
5427 int i;
5428 DWORD otm_size, ret, buf_size;
5429 OUTLINETEXTMETRICA *otm;
5430 BOOL want_vertical, get_vertical;
5431 want_vertical = ( Family[0] == '@' );
5433 hdc = CreateCompatibleDC(0);
5434 ok(hdc != NULL, "CreateCompatibleDC failed\n");
5436 memset(&lf, 0, sizeof(lf));
5437 lf.lfCharSet = DEFAULT_CHARSET;
5438 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
5439 lf.lfHeight = 16;
5440 lf.lfWidth = 16;
5441 lf.lfQuality = DEFAULT_QUALITY;
5442 lf.lfItalic = FALSE;
5443 lf.lfWeight = FW_DONTCARE;
5444 strcpy(lf.lfFaceName, Family);
5445 memset(&efnd, 0, sizeof(efnd));
5446 EnumFontFamiliesExA(hdc, &lf, enum_fullname_data_proc, (LPARAM)&efnd, 0);
5447 if (efnd.total == 0)
5448 skip("%s is not installed\n", lf.lfFaceName);
5450 for (i = 0; i < efnd.total; i++)
5452 FamilyName = (char *)efnd.elf[i].elfLogFont.lfFaceName;
5453 FaceName = (char *)efnd.elf[i].elfFullName;
5454 StyleName = (char *)efnd.elf[i].elfStyle;
5456 get_vertical = ( FamilyName[0] == '@' );
5457 ok(get_vertical == want_vertical, "Vertical flags don't match: %s %s\n", Family, FamilyName);
5459 lstrcpyA(lf.lfFaceName, FaceName);
5460 hfont = CreateFontIndirectA(&lf);
5461 ok(hfont != 0, "CreateFontIndirectA failed\n");
5463 of = SelectObject(hdc, hfont);
5464 buf_size = GetFontData(hdc, MS_NAME_TAG, 0, NULL, 0);
5465 ok(buf_size != GDI_ERROR, "no name table found\n");
5466 if (buf_size == GDI_ERROR) continue;
5468 bufW = HeapAlloc(GetProcessHeap(), 0, buf_size);
5469 bufA = HeapAlloc(GetProcessHeap(), 0, buf_size);
5471 otm_size = GetOutlineTextMetricsA(hdc, 0, NULL);
5472 otm = HeapAlloc(GetProcessHeap(), 0, otm_size);
5473 memset(otm, 0, otm_size);
5474 ret = GetOutlineTextMetricsA(hdc, otm_size, otm);
5475 ok(ret != 0, "GetOutlineTextMetrics fails!\n");
5476 if (ret == 0) continue;
5478 bufW[0] = 0;
5479 bufA[0] = 0;
5480 ret = get_ttf_nametable_entry(hdc, TT_NAME_ID_FONT_FAMILY, bufW, buf_size, GetSystemDefaultLangID());
5481 if (!ret) ret = get_ttf_nametable_entry(hdc, TT_NAME_ID_FONT_FAMILY, bufW, buf_size, TT_MS_LANGID_ENGLISH_UNITED_STATES);
5482 ok(ret, "%s: FAMILY (family name) could not be read\n", FamilyName);
5483 if (want_vertical) bufW = prepend_at(bufW);
5484 WideCharToMultiByte(CP_ACP, 0, bufW, -1, bufA, buf_size, NULL, FALSE);
5485 ok(!lstrcmpA(FamilyName, bufA), "font family names don't match: returned %s, expect %s\n", FamilyName, bufA);
5486 otmStr = (LPSTR)otm + (UINT_PTR)otm->otmpFamilyName;
5487 ok(!lstrcmpA(FamilyName, otmStr), "FamilyName %s doesn't match otmpFamilyName %s\n", FamilyName, otmStr);
5489 bufW[0] = 0;
5490 bufA[0] = 0;
5491 ret = get_ttf_nametable_entry(hdc, TT_NAME_ID_FULL_NAME, bufW, buf_size, GetSystemDefaultLangID());
5492 if (!ret) ret = get_ttf_nametable_entry(hdc, TT_NAME_ID_FULL_NAME, bufW, buf_size, TT_MS_LANGID_ENGLISH_UNITED_STATES);
5493 ok(ret, "FULL_NAME (face name) could not be read\n");
5494 if (want_vertical) bufW = prepend_at(bufW);
5495 WideCharToMultiByte(CP_ACP, 0, bufW, -1, bufA, buf_size, NULL, FALSE);
5496 ok(!lstrcmpA(FaceName, bufA), "%s: font face names don't match: returned %s, expect %s\n", FamilyName, FaceName, bufA);
5497 otmStr = (LPSTR)otm + (UINT_PTR)otm->otmpFaceName;
5498 ok(!lstrcmpA(FaceName, otmStr), "%s: FaceName %s doesn't match otmpFaceName %s\n", FamilyName, FaceName, otmStr);
5500 bufW[0] = 0;
5501 bufA[0] = 0;
5502 ret = get_ttf_nametable_entry(hdc, TT_NAME_ID_FONT_SUBFAMILY, bufW, buf_size, GetSystemDefaultLangID());
5503 if (!ret) ret = get_ttf_nametable_entry(hdc, TT_NAME_ID_FONT_SUBFAMILY, bufW, buf_size, TT_MS_LANGID_ENGLISH_UNITED_STATES);
5504 ok(ret, "%s: SUBFAMILY (style name) could not be read\n", FamilyName);
5505 WideCharToMultiByte(CP_ACP, 0, bufW, -1, bufA, buf_size, NULL, FALSE);
5506 ok(!lstrcmpA(StyleName, bufA), "%s: style names don't match: returned %s, expect %s\n", FamilyName, StyleName, bufA);
5507 otmStr = (LPSTR)otm + (UINT_PTR)otm->otmpStyleName;
5508 ok(!lstrcmpA(StyleName, otmStr), "%s: StyleName %s doesn't match otmpStyleName %s\n", FamilyName, StyleName, otmStr);
5510 bufW[0] = 0;
5511 bufA[0] = 0;
5512 ret = get_ttf_nametable_entry(hdc, TT_NAME_ID_UNIQUE_ID, bufW, buf_size, GetSystemDefaultLangID());
5513 if (!ret) ret = get_ttf_nametable_entry(hdc, TT_NAME_ID_UNIQUE_ID, bufW, buf_size, TT_MS_LANGID_ENGLISH_UNITED_STATES);
5514 ok(ret, "%s: UNIQUE_ID (full name) could not be read\n", FamilyName);
5515 WideCharToMultiByte(CP_ACP, 0, bufW, -1, bufA, buf_size, NULL, FALSE);
5516 otmStr = (LPSTR)otm + (UINT_PTR)otm->otmpFullName;
5517 ok(!lstrcmpA(otmStr, bufA), "%s: UNIQUE ID (full name) doesn't match: returned %s, expect %s\n", FamilyName, otmStr, bufA);
5519 SelectObject(hdc, of);
5520 DeleteObject(hfont);
5522 HeapFree(GetProcessHeap(), 0, otm);
5523 HeapFree(GetProcessHeap(), 0, bufW);
5524 HeapFree(GetProcessHeap(), 0, bufA);
5526 heap_free(efnd.elf);
5527 DeleteDC(hdc);
5530 static void test_fullname2(void)
5532 test_fullname2_helper("Arial");
5533 test_fullname2_helper("DejaVu Sans");
5534 test_fullname2_helper("Lucida Sans");
5535 test_fullname2_helper("Tahoma");
5536 test_fullname2_helper("Webdings");
5537 test_fullname2_helper("Wingdings");
5538 test_fullname2_helper("SimSun");
5539 test_fullname2_helper("NSimSun");
5540 test_fullname2_helper("MingLiu");
5541 test_fullname2_helper("PMingLiu");
5542 test_fullname2_helper("WenQuanYi Micro Hei");
5543 test_fullname2_helper("MS UI Gothic");
5544 test_fullname2_helper("Ume UI Gothic");
5545 test_fullname2_helper("MS Gothic");
5546 test_fullname2_helper("Ume Gothic");
5547 test_fullname2_helper("MS PGothic");
5548 test_fullname2_helper("Ume P Gothic");
5549 test_fullname2_helper("Gulim");
5550 test_fullname2_helper("Batang");
5551 test_fullname2_helper("UnBatang");
5552 test_fullname2_helper("UnDotum");
5553 test_fullname2_helper("@SimSun");
5554 test_fullname2_helper("@NSimSun");
5555 test_fullname2_helper("@MingLiu");
5556 test_fullname2_helper("@PMingLiu");
5557 test_fullname2_helper("@WenQuanYi Micro Hei");
5558 test_fullname2_helper("@MS UI Gothic");
5559 test_fullname2_helper("@Ume UI Gothic");
5560 test_fullname2_helper("@MS Gothic");
5561 test_fullname2_helper("@Ume Gothic");
5562 test_fullname2_helper("@MS PGothic");
5563 test_fullname2_helper("@Ume P Gothic");
5564 test_fullname2_helper("@Gulim");
5565 test_fullname2_helper("@Batang");
5566 test_fullname2_helper("@UnBatang");
5567 test_fullname2_helper("@UnDotum");
5571 static void test_GetGlyphOutline_empty_contour(void)
5573 HDC hdc;
5574 LOGFONTA lf;
5575 HFONT hfont, hfont_prev;
5576 TTPOLYGONHEADER *header;
5577 GLYPHMETRICS gm;
5578 char buf[1024];
5579 DWORD ret;
5581 memset(&lf, 0, sizeof(lf));
5582 lf.lfHeight = 72;
5583 lstrcpyA(lf.lfFaceName, "wine_test");
5585 hfont = CreateFontIndirectA(&lf);
5586 ok(hfont != 0, "CreateFontIndirectA error %u\n", GetLastError());
5588 hdc = GetDC(NULL);
5590 hfont_prev = SelectObject(hdc, hfont);
5591 ok(hfont_prev != NULL, "SelectObject failed\n");
5593 ret = GetGlyphOutlineW(hdc, 0xa8, GGO_NATIVE, &gm, 0, NULL, &mat);
5594 ok(ret == 228, "GetGlyphOutline returned %d, expected 228\n", ret);
5596 header = (TTPOLYGONHEADER*)buf;
5597 ret = GetGlyphOutlineW(hdc, 0xa8, GGO_NATIVE, &gm, sizeof(buf), buf, &mat);
5598 ok(ret == 228, "GetGlyphOutline returned %d, expected 228\n", ret);
5599 ok(header->cb == 36, "header->cb = %d, expected 36\n", header->cb);
5600 ok(header->dwType == TT_POLYGON_TYPE, "header->dwType = %d, expected TT_POLYGON_TYPE\n", header->dwType);
5601 header = (TTPOLYGONHEADER*)((char*)header+header->cb);
5602 ok(header->cb == 96, "header->cb = %d, expected 96\n", header->cb);
5603 header = (TTPOLYGONHEADER*)((char*)header+header->cb);
5604 ok(header->cb == 96, "header->cb = %d, expected 96\n", header->cb);
5606 SelectObject(hdc, hfont_prev);
5607 DeleteObject(hfont);
5608 ReleaseDC(NULL, hdc);
5611 static void test_GetGlyphOutline_metric_clipping(void)
5613 HDC hdc;
5614 LOGFONTA lf;
5615 HFONT hfont, hfont_prev;
5616 GLYPHMETRICS gm;
5617 TEXTMETRICA tm;
5618 TEXTMETRICW tmW;
5619 DWORD ret;
5621 memset(&lf, 0, sizeof(lf));
5622 lf.lfHeight = 72;
5623 lstrcpyA(lf.lfFaceName, "wine_test");
5625 SetLastError(0xdeadbeef);
5626 hfont = CreateFontIndirectA(&lf);
5627 ok(hfont != 0, "CreateFontIndirectA error %u\n", GetLastError());
5629 hdc = GetDC(NULL);
5631 hfont_prev = SelectObject(hdc, hfont);
5632 ok(hfont_prev != NULL, "SelectObject failed\n");
5634 SetLastError(0xdeadbeef);
5635 ret = GetTextMetricsA(hdc, &tm);
5636 ok(ret, "GetTextMetrics error %u\n", GetLastError());
5638 GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
5639 ok(gm.gmptGlyphOrigin.y <= tm.tmAscent,
5640 "Glyph top(%d) exceeds ascent(%d)\n",
5641 gm.gmptGlyphOrigin.y, tm.tmAscent);
5642 GetGlyphOutlineA(hdc, 'D', GGO_METRICS, &gm, 0, NULL, &mat);
5643 ok(gm.gmptGlyphOrigin.y - gm.gmBlackBoxY >= -tm.tmDescent,
5644 "Glyph bottom(%d) exceeds descent(%d)\n",
5645 gm.gmptGlyphOrigin.y - gm.gmBlackBoxY, -tm.tmDescent);
5647 /* Test tmLastChar - wine_test has code points fffb-fffe mapped to glyph 0 */
5648 GetTextMetricsW(hdc, &tmW);
5649 todo_wine
5650 ok( tmW.tmLastChar == 0xfffe, "got %04x\n", tmW.tmLastChar);
5652 SelectObject(hdc, hfont_prev);
5653 DeleteObject(hfont);
5654 ReleaseDC(NULL, hdc);
5657 static void test_fstype_fixup(void)
5659 HDC hdc;
5660 LOGFONTA lf;
5661 HFONT hfont, hfont_prev;
5662 DWORD ret;
5663 OUTLINETEXTMETRICA *otm;
5664 DWORD otm_size;
5666 memset(&lf, 0, sizeof(lf));
5667 lf.lfHeight = 72;
5668 lstrcpyA(lf.lfFaceName, "wine_test");
5670 SetLastError(0xdeadbeef);
5671 hfont = CreateFontIndirectA(&lf);
5672 ok(hfont != 0, "CreateFontIndirectA error %u\n", GetLastError());
5674 hdc = GetDC(NULL);
5676 hfont_prev = SelectObject(hdc, hfont);
5677 ok(hfont_prev != NULL, "SelectObject failed\n");
5679 otm_size = GetOutlineTextMetricsA(hdc, 0, NULL);
5680 otm = HeapAlloc(GetProcessHeap(), 0, otm_size);
5681 otm->otmSize = sizeof(*otm);
5682 ret = GetOutlineTextMetricsA(hdc, otm->otmSize, otm);
5683 ok(ret == otm->otmSize, "expected %u, got %u, error %d\n", otm->otmSize, ret, GetLastError());
5685 /* Test font has fsType set to 0x7fff, test that reserved bits are filtered out,
5686 valid bits are 1, 2, 3, 8, 9. */
5687 ok((otm->otmfsType & ~0x30e) == 0, "fsType %#x\n", otm->otmfsType);
5689 HeapFree(GetProcessHeap(), 0, otm);
5691 SelectObject(hdc, hfont_prev);
5692 DeleteObject(hfont);
5693 ReleaseDC(NULL, hdc);
5696 static void test_CreateScalableFontResource(void)
5698 char ttf_name[MAX_PATH];
5699 char tmp_path[MAX_PATH];
5700 char fot_name[MAX_PATH];
5701 char *file_part;
5702 DWORD ret;
5703 int i;
5705 if (!pAddFontResourceExA || !pRemoveFontResourceExA)
5707 win_skip("AddFontResourceExA is not available on this platform\n");
5708 return;
5711 if (!write_ttf_file("wine_test.ttf", ttf_name))
5713 skip("Failed to create ttf file for testing\n");
5714 return;
5717 trace("created %s\n", ttf_name);
5719 ret = is_truetype_font_installed("wine_test");
5720 ok(!ret, "font wine_test should not be enumerated\n");
5722 ret = GetTempPathA(MAX_PATH, tmp_path);
5723 ok(ret, "GetTempPath() error %d\n", GetLastError());
5724 ret = GetTempFileNameA(tmp_path, "fot", 0, fot_name);
5725 ok(ret, "GetTempFileName() error %d\n", GetLastError());
5727 ret = GetFileAttributesA(fot_name);
5728 ok(ret != INVALID_FILE_ATTRIBUTES, "file %s does not exist\n", fot_name);
5730 SetLastError(0xdeadbeef);
5731 ret = CreateScalableFontResourceA(0, fot_name, ttf_name, NULL);
5732 ok(!ret, "CreateScalableFontResource() should fail\n");
5733 ok(GetLastError() == ERROR_FILE_EXISTS, "not expected error %d\n", GetLastError());
5735 SetLastError(0xdeadbeef);
5736 ret = CreateScalableFontResourceA(0, fot_name, ttf_name, "");
5737 ok(!ret, "CreateScalableFontResource() should fail\n");
5738 ok(GetLastError() == ERROR_FILE_EXISTS, "not expected error %d\n", GetLastError());
5740 file_part = strrchr(ttf_name, '\\');
5741 SetLastError(0xdeadbeef);
5742 ret = CreateScalableFontResourceA(0, fot_name, file_part, tmp_path);
5743 ok(!ret, "CreateScalableFontResource() should fail\n");
5744 ok(GetLastError() == ERROR_FILE_EXISTS, "not expected error %d\n", GetLastError());
5746 SetLastError(0xdeadbeef);
5747 ret = CreateScalableFontResourceA(0, fot_name, "random file name", tmp_path);
5748 ok(!ret, "CreateScalableFontResource() should fail\n");
5749 ok(GetLastError() == ERROR_INVALID_PARAMETER, "not expected error %d\n", GetLastError());
5751 SetLastError(0xdeadbeef);
5752 ret = CreateScalableFontResourceA(0, fot_name, NULL, ttf_name);
5753 ok(!ret, "CreateScalableFontResource() should fail\n");
5754 ok(GetLastError() == ERROR_INVALID_PARAMETER, "not expected error %d\n", GetLastError());
5756 ret = DeleteFileA(fot_name);
5757 ok(ret, "DeleteFile() error %d\n", GetLastError());
5759 ret = pRemoveFontResourceExA(fot_name, 0, 0);
5760 ok(!ret, "RemoveFontResourceEx() should fail\n");
5762 /* test public font resource */
5763 SetLastError(0xdeadbeef);
5764 ret = CreateScalableFontResourceA(0, fot_name, ttf_name, NULL);
5765 ok(ret, "CreateScalableFontResource() error %d\n", GetLastError());
5767 ret = is_truetype_font_installed("wine_test");
5768 ok(!ret, "font wine_test should not be enumerated\n");
5770 SetLastError(0xdeadbeef);
5771 ret = pAddFontResourceExA(fot_name, 0, 0);
5772 ok(ret, "AddFontResourceEx() error %d\n", GetLastError());
5774 ret = is_truetype_font_installed("wine_test");
5775 ok(ret, "font wine_test should be enumerated\n");
5777 test_GetGlyphOutline_empty_contour();
5778 test_GetGlyphOutline_metric_clipping();
5779 test_fstype_fixup();
5781 ret = pRemoveFontResourceExA(fot_name, FR_PRIVATE, 0);
5782 ok(!ret, "RemoveFontResourceEx() with not matching flags should fail\n");
5784 SetLastError(0xdeadbeef);
5785 ret = pRemoveFontResourceExA(fot_name, 0, 0);
5786 ok(ret, "RemoveFontResourceEx() error %d\n", GetLastError());
5788 ret = is_truetype_font_installed("wine_test");
5789 ok(!ret, "font wine_test should not be enumerated\n");
5791 ret = pRemoveFontResourceExA(fot_name, 0, 0);
5792 ok(!ret, "RemoveFontResourceEx() should fail\n");
5794 /* test refcounting */
5795 for (i = 0; i < 5; i++)
5797 SetLastError(0xdeadbeef);
5798 ret = pAddFontResourceExA(fot_name, 0, 0);
5799 ok(ret, "AddFontResourceEx() error %d\n", GetLastError());
5801 for (i = 0; i < 5; i++)
5803 SetLastError(0xdeadbeef);
5804 ret = pRemoveFontResourceExA(fot_name, 0, 0);
5805 ok(ret, "RemoveFontResourceEx() error %d\n", GetLastError());
5807 ret = pRemoveFontResourceExA(fot_name, 0, 0);
5808 ok(!ret, "RemoveFontResourceEx() should fail\n");
5810 DeleteFileA(fot_name);
5812 /* test hidden font resource */
5813 SetLastError(0xdeadbeef);
5814 ret = CreateScalableFontResourceA(1, fot_name, ttf_name, NULL);
5815 ok(ret, "CreateScalableFontResource() error %d\n", GetLastError());
5817 ret = is_truetype_font_installed("wine_test");
5818 ok(!ret, "font wine_test should not be enumerated\n");
5820 SetLastError(0xdeadbeef);
5821 ret = pAddFontResourceExA(fot_name, 0, 0);
5822 ok(ret, "AddFontResourceEx() error %d\n", GetLastError());
5824 ret = is_truetype_font_installed("wine_test");
5825 todo_wine
5826 ok(!ret, "font wine_test should not be enumerated\n");
5828 /* XP allows removing a private font added with 0 flags */
5829 SetLastError(0xdeadbeef);
5830 ret = pRemoveFontResourceExA(fot_name, FR_PRIVATE, 0);
5831 ok(ret, "RemoveFontResourceEx() error %d\n", GetLastError());
5833 ret = is_truetype_font_installed("wine_test");
5834 ok(!ret, "font wine_test should not be enumerated\n");
5836 ret = pRemoveFontResourceExA(fot_name, 0, 0);
5837 ok(!ret, "RemoveFontResourceEx() should fail\n");
5839 DeleteFileA(fot_name);
5840 DeleteFileA(ttf_name);
5843 static void check_vertical_font(const char *name, BOOL *installed, BOOL *selected, GLYPHMETRICS *gm, WORD *gi)
5845 LOGFONTA lf;
5846 HFONT hfont, hfont_prev;
5847 HDC hdc;
5848 char facename[100];
5849 DWORD ret;
5850 static const WCHAR str[] = { 0x2025 };
5852 *installed = is_truetype_font_installed(name);
5854 lf.lfHeight = -18;
5855 lf.lfWidth = 0;
5856 lf.lfEscapement = 0;
5857 lf.lfOrientation = 0;
5858 lf.lfWeight = FW_DONTCARE;
5859 lf.lfItalic = 0;
5860 lf.lfUnderline = 0;
5861 lf.lfStrikeOut = 0;
5862 lf.lfCharSet = DEFAULT_CHARSET;
5863 lf.lfOutPrecision = OUT_TT_ONLY_PRECIS;
5864 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
5865 lf.lfQuality = DEFAULT_QUALITY;
5866 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
5867 strcpy(lf.lfFaceName, name);
5869 hfont = CreateFontIndirectA(&lf);
5870 ok(hfont != NULL, "CreateFontIndirectA failed\n");
5872 hdc = GetDC(NULL);
5874 hfont_prev = SelectObject(hdc, hfont);
5875 ok(hfont_prev != NULL, "SelectObject failed\n");
5877 ret = GetTextFaceA(hdc, sizeof facename, facename);
5878 ok(ret, "GetTextFaceA failed\n");
5879 *selected = !strcmp(facename, name);
5881 ret = GetGlyphOutlineW(hdc, 0x2025, GGO_METRICS, gm, 0, NULL, &mat);
5882 ok(ret != GDI_ERROR, "GetGlyphOutlineW failed\n");
5883 if (!*selected)
5884 memset(gm, 0, sizeof *gm);
5886 ret = pGetGlyphIndicesW(hdc, str, 1, gi, 0);
5887 ok(ret != GDI_ERROR, "GetGlyphIndicesW failed\n");
5889 SelectObject(hdc, hfont_prev);
5890 DeleteObject(hfont);
5891 ReleaseDC(NULL, hdc);
5894 static void check_vertical_metrics(const char *face)
5896 LOGFONTA lf;
5897 HFONT hfont, hfont_prev;
5898 HDC hdc;
5899 DWORD ret;
5900 GLYPHMETRICS rgm, vgm;
5901 const UINT code = 0x5EAD, height = 1000;
5902 WORD idx;
5903 ABC abc;
5904 OUTLINETEXTMETRICA otm;
5905 USHORT numOfLongVerMetrics;
5907 hdc = GetDC(NULL);
5909 memset(&lf, 0, sizeof(lf));
5910 strcpy(lf.lfFaceName, face);
5911 lf.lfHeight = -height;
5912 lf.lfCharSet = DEFAULT_CHARSET;
5913 lf.lfEscapement = lf.lfOrientation = 900;
5914 hfont = CreateFontIndirectA(&lf);
5915 hfont_prev = SelectObject(hdc, hfont);
5916 ret = GetGlyphOutlineW(hdc, code, GGO_METRICS, &rgm, 0, NULL, &mat);
5917 ok(ret != GDI_ERROR, "GetGlyphOutlineW failed\n");
5918 ret = GetCharABCWidthsW(hdc, code, code, &abc);
5919 ok(ret, "GetCharABCWidthsW failed\n");
5920 DeleteObject(SelectObject(hdc, hfont_prev));
5922 memset(&lf, 0, sizeof(lf));
5923 strcpy(lf.lfFaceName, "@");
5924 strcat(lf.lfFaceName, face);
5925 lf.lfHeight = -height;
5926 lf.lfCharSet = DEFAULT_CHARSET;
5927 hfont = CreateFontIndirectA(&lf);
5928 hfont_prev = SelectObject(hdc, hfont);
5929 ret = GetGlyphOutlineW(hdc, code, GGO_METRICS, &vgm, 0, NULL, &mat);
5930 ok(ret != GDI_ERROR, "GetGlyphOutlineW failed\n");
5932 memset(&otm, 0, sizeof(otm));
5933 otm.otmSize = sizeof(otm);
5934 ret = GetOutlineTextMetricsA(hdc, sizeof(otm), &otm);
5935 ok(ret != 0, "GetOutlineTextMetricsA failed\n");
5937 if (GetFontData(hdc, MS_MAKE_TAG('v','h','e','a'), sizeof(SHORT) * 17,
5938 &numOfLongVerMetrics, sizeof(numOfLongVerMetrics)) != GDI_ERROR) {
5939 int offset;
5940 SHORT topSideBearing;
5942 if (!pGetGlyphIndicesW) {
5943 win_skip("GetGlyphIndices is not available on this platform\n");
5945 else {
5946 ret = pGetGlyphIndicesW(hdc, (LPCWSTR)&code, 1, &idx, 0);
5947 ok(ret != 0, "GetGlyphIndicesW failed\n");
5948 numOfLongVerMetrics = GET_BE_WORD(numOfLongVerMetrics);
5949 if (numOfLongVerMetrics > idx)
5950 offset = idx * 2 + 1;
5951 else
5952 offset = numOfLongVerMetrics * 2 + (idx - numOfLongVerMetrics);
5953 ret = GetFontData(hdc, MS_MAKE_TAG('v','m','t','x'), offset * sizeof(SHORT),
5954 &topSideBearing, sizeof(SHORT));
5955 ok(ret != GDI_ERROR, "GetFontData(vmtx) failed\n");
5956 topSideBearing = GET_BE_WORD(topSideBearing);
5957 ok(match_off_by_1(vgm.gmptGlyphOrigin.x,
5958 MulDiv(topSideBearing, height, otm.otmEMSquare), FALSE),
5959 "expected %d, got %d\n",
5960 MulDiv(topSideBearing, height, otm.otmEMSquare), vgm.gmptGlyphOrigin.x);
5963 else
5965 ok(vgm.gmptGlyphOrigin.x == rgm.gmptGlyphOrigin.x + vgm.gmCellIncX + otm.otmDescent,
5966 "got %d, expected rgm.origin.x(%d) + vgm.cellIncX(%d) + descent(%d)\n",
5967 vgm.gmptGlyphOrigin.x, rgm.gmptGlyphOrigin.x, vgm.gmCellIncX, otm.otmDescent);
5970 ok(vgm.gmptGlyphOrigin.y == abc.abcA + abc.abcB + otm.otmDescent ||
5971 broken(vgm.gmptGlyphOrigin.y == abc.abcA + abc.abcB - otm.otmTextMetrics.tmDescent) /* win2k */,
5972 "got %d, expected abcA(%d) + abcB(%u) + descent(%d)\n",
5973 (INT)vgm.gmptGlyphOrigin.y, abc.abcA, abc.abcB, otm.otmDescent);
5975 DeleteObject(SelectObject(hdc, hfont_prev));
5976 ReleaseDC(NULL, hdc);
5979 static void test_vertical_font(void)
5981 char ttf_name[MAX_PATH];
5982 int num, i;
5983 BOOL ret, installed, selected;
5984 GLYPHMETRICS gm;
5985 WORD hgi, vgi;
5986 const char* face_list[] = {
5987 "@WineTestVertical", /* has vmtx table */
5988 "@Ume Gothic", /* doesn't have vmtx table */
5989 "@MS UI Gothic", /* has vmtx table, available on native */
5992 if (!pAddFontResourceExA || !pRemoveFontResourceExA || !pGetGlyphIndicesW)
5994 win_skip("AddFontResourceExA or GetGlyphIndicesW is not available on this platform\n");
5995 return;
5998 if (!write_ttf_file("vertical.ttf", ttf_name))
6000 skip("Failed to create ttf file for testing\n");
6001 return;
6004 num = pAddFontResourceExA(ttf_name, FR_PRIVATE, 0);
6005 ok(num == 2, "AddFontResourceExA should add 2 fonts from vertical.ttf\n");
6007 check_vertical_font("WineTestVertical", &installed, &selected, &gm, &hgi);
6008 ok(installed, "WineTestVertical is not installed\n");
6009 ok(selected, "WineTestVertical is not selected\n");
6010 ok(gm.gmBlackBoxX > gm.gmBlackBoxY,
6011 "gmBlackBoxX(%u) should be greater than gmBlackBoxY(%u) if horizontal\n",
6012 gm.gmBlackBoxX, gm.gmBlackBoxY);
6014 check_vertical_font("@WineTestVertical", &installed, &selected, &gm, &vgi);
6015 ok(installed, "@WineTestVertical is not installed\n");
6016 ok(selected, "@WineTestVertical is not selected\n");
6017 ok(gm.gmBlackBoxX > gm.gmBlackBoxY,
6018 "gmBlackBoxX(%u) should be less than gmBlackBoxY(%u) if vertical\n",
6019 gm.gmBlackBoxX, gm.gmBlackBoxY);
6021 ok(hgi != vgi, "same glyph h:%u v:%u\n", hgi, vgi);
6023 for (i = 0; i < sizeof(face_list)/sizeof(face_list[0]); i++) {
6024 const char* face = face_list[i];
6025 if (!is_truetype_font_installed(face)) {
6026 skip("%s is not installed\n", face);
6027 continue;
6029 trace("Testing %s...\n", face);
6030 check_vertical_metrics(&face[1]);
6033 ret = pRemoveFontResourceExA(ttf_name, FR_PRIVATE, 0);
6034 ok(ret, "RemoveFontResourceEx() error %d\n", GetLastError());
6036 DeleteFileA(ttf_name);
6039 static INT CALLBACK has_vertical_font_proc(const LOGFONTA *lf, const TEXTMETRICA *ntm,
6040 DWORD type, LPARAM lParam)
6042 if (lf->lfFaceName[0] == '@') {
6043 return 0;
6045 return 1;
6048 static void test_east_asian_font_selection(void)
6050 HDC hdc;
6051 UINT charset[] = { SHIFTJIS_CHARSET, HANGEUL_CHARSET, JOHAB_CHARSET,
6052 GB2312_CHARSET, CHINESEBIG5_CHARSET };
6053 size_t i;
6055 hdc = GetDC(NULL);
6057 for (i = 0; i < sizeof(charset)/sizeof(charset[0]); i++)
6059 LOGFONTA lf;
6060 HFONT hfont;
6061 char face_name[LF_FACESIZE];
6062 int ret;
6064 memset(&lf, 0, sizeof lf);
6065 lf.lfFaceName[0] = '\0';
6066 lf.lfCharSet = charset[i];
6068 if (EnumFontFamiliesExA(hdc, &lf, has_vertical_font_proc, 0, 0))
6070 skip("Vertical font for charset %u is not installed\n", charset[i]);
6071 continue;
6074 hfont = CreateFontIndirectA(&lf);
6075 hfont = SelectObject(hdc, hfont);
6076 memset(face_name, 0, sizeof face_name);
6077 ret = GetTextFaceA(hdc, sizeof face_name, face_name);
6078 ok(ret && face_name[0] != '@',
6079 "expected non-vertical face for charset %u, got %s\n", charset[i], face_name);
6080 DeleteObject(SelectObject(hdc, hfont));
6082 memset(&lf, 0, sizeof lf);
6083 strcpy(lf.lfFaceName, "@");
6084 lf.lfCharSet = charset[i];
6085 hfont = CreateFontIndirectA(&lf);
6086 hfont = SelectObject(hdc, hfont);
6087 memset(face_name, 0, sizeof face_name);
6088 ret = GetTextFaceA(hdc, sizeof face_name, face_name);
6089 ok(ret && face_name[0] == '@',
6090 "expected vertical face for charset %u, got %s\n", charset[i], face_name);
6091 DeleteObject(SelectObject(hdc, hfont));
6093 ReleaseDC(NULL, hdc);
6096 static int get_font_dpi(const LOGFONTA *lf, int *height)
6098 HDC hdc = CreateCompatibleDC(0);
6099 HFONT hfont;
6100 TEXTMETRICA tm;
6101 int ret;
6103 hfont = CreateFontIndirectA(lf);
6104 ok(hfont != 0, "CreateFontIndirect failed\n");
6106 SelectObject(hdc, hfont);
6107 ret = GetTextMetricsA(hdc, &tm);
6108 ok(ret, "GetTextMetrics failed\n");
6109 ret = tm.tmDigitizedAspectX;
6110 if (height) *height = tm.tmHeight;
6112 DeleteDC(hdc);
6113 DeleteObject(hfont);
6115 return ret;
6118 static void test_stock_fonts(void)
6120 static const int font[] =
6122 ANSI_FIXED_FONT, ANSI_VAR_FONT, SYSTEM_FONT, DEVICE_DEFAULT_FONT, DEFAULT_GUI_FONT
6123 /* SYSTEM_FIXED_FONT, OEM_FIXED_FONT */
6125 static const struct test_data
6127 int charset, weight, height, height_pixels, dpi;
6128 const char face_name[LF_FACESIZE];
6129 WORD lang_id;
6130 } td[][12] =
6132 { /* ANSI_FIXED_FONT */
6133 { ANSI_CHARSET, FW_NORMAL, 12, 12, 96, "Courier", LANG_ARABIC },
6134 { ANSI_CHARSET, FW_NORMAL, 12, 12, 96, "Courier", LANG_HEBREW},
6135 { DEFAULT_CHARSET, FW_NORMAL, 12, 13, 96, "Courier" },
6136 { DEFAULT_CHARSET, FW_NORMAL, 12, 13, 120, "Courier" },
6137 { 0 }
6139 { /* ANSI_VAR_FONT */
6140 { DEFAULT_CHARSET, FW_NORMAL, 12, 13, 96, "MS Sans Serif" },
6141 { DEFAULT_CHARSET, FW_NORMAL, 12, 13, 120, "MS Sans Serif" },
6142 { 0 }
6144 { /* SYSTEM_FONT */
6145 { SHIFTJIS_CHARSET, FW_NORMAL, 18, 18, 96, "System" },
6146 { SHIFTJIS_CHARSET, FW_NORMAL, 22, 22, 120, "System" },
6147 { HANGEUL_CHARSET, FW_NORMAL, 16, 16, 96, "System" },
6148 { HANGEUL_CHARSET, FW_NORMAL, 20, 20, 120, "System" },
6149 { DEFAULT_CHARSET, FW_BOLD, 16, 16, 96, "System" },
6150 { DEFAULT_CHARSET, FW_BOLD, 20, 20, 120, "System" },
6151 { 0 }
6153 { /* DEVICE_DEFAULT_FONT */
6154 { SHIFTJIS_CHARSET, FW_NORMAL, 18, 18, 96, "System" },
6155 { SHIFTJIS_CHARSET, FW_NORMAL, 22, 22, 120, "System" },
6156 { HANGEUL_CHARSET, FW_NORMAL, 16, 16, 96, "System" },
6157 { HANGEUL_CHARSET, FW_NORMAL, 20, 20, 120, "System" },
6158 { DEFAULT_CHARSET, FW_BOLD, 16, 16, 96, "System" },
6159 { DEFAULT_CHARSET, FW_BOLD, 20, 20, 120, "System" },
6160 { 0 }
6162 { /* DEFAULT_GUI_FONT */
6163 { SHIFTJIS_CHARSET, FW_NORMAL, -11, 13, 96, "MS Shell Dlg" },
6164 { SHIFTJIS_CHARSET, FW_NORMAL, -12, 15, 96, "?MS UI Gothic" },
6165 { SHIFTJIS_CHARSET, FW_NORMAL, -15, 18, 120, "?MS UI Gothic" },
6166 { HANGEUL_CHARSET, FW_NORMAL, -12, 15, 96, "?Gulim" },
6167 { HANGEUL_CHARSET, FW_NORMAL, -15, 18, 120, "?Gulim" },
6168 { GB2312_CHARSET, FW_NORMAL, -12, 15, 96, "?SimHei" },
6169 { GB2312_CHARSET, FW_NORMAL, -15, 18, 120, "?SimHei" },
6170 { CHINESEBIG5_CHARSET, FW_NORMAL, -12, 15, 96, "?MingLiU" },
6171 { CHINESEBIG5_CHARSET, FW_NORMAL, -15, 18, 120, "?MingLiU" },
6172 { DEFAULT_CHARSET, FW_NORMAL, -11, 13, 96, "MS Shell Dlg" },
6173 { DEFAULT_CHARSET, FW_NORMAL, -13, 16, 120, "MS Shell Dlg" },
6174 { 0 }
6177 int i, j;
6179 for (i = 0; i < sizeof(font)/sizeof(font[0]); i++)
6181 HFONT hfont;
6182 LOGFONTA lf;
6183 int ret, height;
6185 hfont = GetStockObject(font[i]);
6186 ok(hfont != 0, "%d: GetStockObject(%d) failed\n", i, font[i]);
6188 ret = GetObjectA(hfont, sizeof(lf), &lf);
6189 if (ret != sizeof(lf))
6191 /* NT4 */
6192 win_skip("%d: GetObject returned %d instead of sizeof(LOGFONT)\n", i, ret);
6193 continue;
6196 for (j = 0; td[i][j].face_name[0] != 0; j++)
6198 if ((lf.lfCharSet != td[i][j].charset && td[i][j].charset != DEFAULT_CHARSET) ||
6199 (system_lang_id != td[i][j].lang_id && td[i][j].lang_id != LANG_NEUTRAL) ||
6200 (td[i][j].face_name[0] != '?' && strcmp(lf.lfFaceName, td[i][j].face_name)))
6202 continue;
6205 ret = get_font_dpi(&lf, &height);
6206 if (ret != td[i][j].dpi)
6208 trace("%d(%d): font %s %d dpi doesn't match test data %d\n",
6209 i, j, lf.lfFaceName, ret, td[i][j].dpi);
6210 continue;
6213 /* FIXME: Remove once Wine is fixed */
6214 todo_wine_if (td[i][j].dpi != 96 &&
6215 /* MS Sans Serif for 120 dpi and higher should include 12 pixel bitmap set */
6216 ((!strcmp(td[i][j].face_name, "MS Sans Serif") && td[i][j].height == 12) ||
6217 /* System for 120 dpi and higher should include 20 pixel bitmap set */
6218 (!strcmp(td[i][j].face_name, "System") && td[i][j].height > 16)))
6219 ok(height == td[i][j].height_pixels, "%d(%d): expected height %d, got %d\n", i, j, td[i][j].height_pixels, height);
6221 ok(td[i][j].weight == lf.lfWeight, "%d(%d): expected lfWeight %d, got %d\n", i, j, td[i][j].weight, lf.lfWeight);
6222 ok(td[i][j].height == lf.lfHeight, "%d(%d): expected lfHeight %d, got %d\n", i, j, td[i][j].height, lf.lfHeight);
6223 if (td[i][j].face_name[0] == '?')
6225 /* Wine doesn't have this font, skip this case for now.
6226 Actually, the face name is localized on Windows and varies
6227 dpending on Windows versions (e.g. Japanese NT4 vs win2k). */
6228 trace("%d(%d): default gui font is %s\n", i, j, lf.lfFaceName);
6230 else
6232 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);
6234 break;
6239 static void test_max_height(void)
6241 HDC hdc;
6242 LOGFONTA lf;
6243 HFONT hfont, hfont_old;
6244 TEXTMETRICA tm1, tm;
6245 BOOL r;
6246 LONG invalid_height[] = { -65536, -123456, 123456 };
6247 size_t i;
6249 memset(&tm1, 0, sizeof(tm1));
6250 memset(&lf, 0, sizeof(lf));
6251 strcpy(lf.lfFaceName, "Tahoma");
6252 lf.lfHeight = -1;
6254 hdc = GetDC(NULL);
6256 /* get 1 ppem value */
6257 hfont = CreateFontIndirectA(&lf);
6258 hfont_old = SelectObject(hdc, hfont);
6259 r = GetTextMetricsA(hdc, &tm1);
6260 ok(r, "GetTextMetrics failed\n");
6261 ok(tm1.tmHeight > 0, "expected a positive value, got %d\n", tm1.tmHeight);
6262 ok(tm1.tmAveCharWidth > 0, "expected a positive value, got %d\n", tm1.tmHeight);
6263 DeleteObject(SelectObject(hdc, hfont_old));
6265 /* test the largest value */
6266 lf.lfHeight = -((1 << 16) - 1);
6267 hfont = CreateFontIndirectA(&lf);
6268 hfont_old = SelectObject(hdc, hfont);
6269 memset(&tm, 0, sizeof(tm));
6270 r = GetTextMetricsA(hdc, &tm);
6271 ok(r, "GetTextMetrics failed\n");
6272 ok(tm.tmHeight > tm1.tmHeight,
6273 "expected greater than 1 ppem value (%d), got %d\n", tm1.tmHeight, tm.tmHeight);
6274 ok(tm.tmAveCharWidth > tm1.tmAveCharWidth,
6275 "expected greater than 1 ppem value (%d), got %d\n", tm1.tmAveCharWidth, tm.tmAveCharWidth);
6276 DeleteObject(SelectObject(hdc, hfont_old));
6278 /* test an invalid value */
6279 for (i = 0; i < sizeof(invalid_height)/sizeof(invalid_height[0]); i++) {
6280 lf.lfHeight = invalid_height[i];
6281 hfont = CreateFontIndirectA(&lf);
6282 hfont_old = SelectObject(hdc, hfont);
6283 memset(&tm, 0, sizeof(tm));
6284 r = GetTextMetricsA(hdc, &tm);
6285 ok(r, "GetTextMetrics failed\n");
6286 ok(tm.tmHeight == tm1.tmHeight,
6287 "expected 1 ppem value (%d), got %d\n", tm1.tmHeight, tm.tmHeight);
6288 ok(tm.tmAveCharWidth == tm1.tmAveCharWidth,
6289 "expected 1 ppem value (%d), got %d\n", tm1.tmAveCharWidth, tm.tmAveCharWidth);
6290 DeleteObject(SelectObject(hdc, hfont_old));
6293 ReleaseDC(NULL, hdc);
6294 return;
6297 static void test_vertical_order(void)
6299 struct enum_font_data efd;
6300 LOGFONTA lf;
6301 HDC hdc;
6302 int i, j;
6304 hdc = CreateCompatibleDC(0);
6305 ok(hdc != NULL, "CreateCompatibleDC failed\n");
6307 memset(&lf, 0, sizeof(lf));
6308 lf.lfCharSet = DEFAULT_CHARSET;
6309 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
6310 lf.lfHeight = 16;
6311 lf.lfWidth = 16;
6312 lf.lfQuality = DEFAULT_QUALITY;
6313 lf.lfItalic = FALSE;
6314 lf.lfWeight = FW_DONTCARE;
6315 memset( &efd, 0, sizeof(efd) );
6316 EnumFontFamiliesExA(hdc, &lf, enum_font_data_proc, (LPARAM)&efd, 0);
6317 for (i = 0; i < efd.total; i++)
6319 if (efd.lf[i].lfFaceName[0] != '@') continue;
6320 for (j = 0; j < efd.total; j++)
6322 if (!strcmp(efd.lf[i].lfFaceName + 1, efd.lf[j].lfFaceName))
6324 ok(i > j,"Found vertical font %s before its horizontal version\n", efd.lf[i].lfFaceName);
6325 break;
6329 heap_free( efd.lf );
6330 DeleteDC( hdc );
6333 static void test_GetCharWidth32(void)
6335 BOOL ret;
6336 HDC hdc;
6337 LOGFONTA lf;
6338 HFONT hfont;
6339 INT bufferA;
6340 INT bufferW;
6341 HWND hwnd;
6343 if (!pGetCharWidth32A || !pGetCharWidth32W)
6345 win_skip("GetCharWidth32A/W not available on this platform\n");
6346 return;
6349 memset(&lf, 0, sizeof(lf));
6350 strcpy(lf.lfFaceName, "System");
6351 lf.lfHeight = 20;
6353 hfont = CreateFontIndirectA(&lf);
6354 hdc = GetDC(0);
6355 hfont = SelectObject(hdc, hfont);
6357 ret = pGetCharWidth32W(hdc, 'a', 'a', &bufferW);
6358 ok(ret, "GetCharWidth32W should have succeeded\n");
6359 ret = pGetCharWidth32A(hdc, 'a', 'a', &bufferA);
6360 ok(ret, "GetCharWidth32A should have succeeded\n");
6361 ok (bufferA == bufferW, "Widths should be the same\n");
6362 ok (bufferA > 0," Width should be greater than zero\n");
6364 hfont = SelectObject(hdc, hfont);
6365 DeleteObject(hfont);
6366 ReleaseDC(NULL, hdc);
6368 memset(&lf, 0, sizeof(lf));
6369 strcpy(lf.lfFaceName, "Tahoma");
6370 lf.lfHeight = 20;
6372 hfont = CreateFontIndirectA(&lf);
6373 hwnd = CreateWindowExA(0, "static", "", WS_POPUP, 0,0,100,100,
6374 0, 0, 0, NULL);
6375 hdc = GetDC(hwnd);
6376 SetMapMode( hdc, MM_ANISOTROPIC );
6377 SelectObject(hdc, hfont);
6379 ret = pGetCharWidth32W(hdc, 'a', 'a', &bufferW);
6380 ok(ret, "GetCharWidth32W should have succeeded\n");
6381 ok (bufferW > 0," Width should be greater than zero\n");
6382 SetWindowExtEx(hdc, -1,-1,NULL);
6383 SetGraphicsMode(hdc, GM_COMPATIBLE);
6384 ret = pGetCharWidth32W(hdc, 'a', 'a', &bufferW);
6385 ok(ret, "GetCharWidth32W should have succeeded\n");
6386 ok (bufferW > 0," Width should be greater than zero\n");
6387 SetGraphicsMode(hdc, GM_ADVANCED);
6388 ret = pGetCharWidth32W(hdc, 'a', 'a', &bufferW);
6389 ok(ret, "GetCharWidth32W should have succeeded\n");
6390 todo_wine ok (bufferW > 0," Width should be greater than zero\n");
6391 SetWindowExtEx(hdc, 1,1,NULL);
6392 SetGraphicsMode(hdc, GM_COMPATIBLE);
6393 ret = pGetCharWidth32W(hdc, 'a', 'a', &bufferW);
6394 ok(ret, "GetCharWidth32W should have succeeded\n");
6395 ok (bufferW > 0," Width should be greater than zero\n");
6396 SetGraphicsMode(hdc, GM_ADVANCED);
6397 ret = pGetCharWidth32W(hdc, 'a', 'a', &bufferW);
6398 ok(ret, "GetCharWidth32W should have succeeded\n");
6399 ok (bufferW > 0," Width should be greater than zero\n");
6401 ReleaseDC(hwnd, hdc);
6402 DestroyWindow(hwnd);
6404 hwnd = CreateWindowExA(WS_EX_LAYOUTRTL, "static", "", WS_POPUP, 0,0,100,100,
6405 0, 0, 0, NULL);
6406 hdc = GetDC(hwnd);
6407 SetMapMode( hdc, MM_ANISOTROPIC );
6408 SelectObject(hdc, hfont);
6410 ret = pGetCharWidth32W(hdc, 'a', 'a', &bufferW);
6411 ok(ret, "GetCharWidth32W should have succeeded\n");
6412 ok (bufferW > 0," Width should be greater than zero\n");
6413 SetWindowExtEx(hdc, -1,-1,NULL);
6414 SetGraphicsMode(hdc, GM_COMPATIBLE);
6415 ret = pGetCharWidth32W(hdc, 'a', 'a', &bufferW);
6416 ok(ret, "GetCharWidth32W should have succeeded\n");
6417 ok (bufferW > 0," Width should be greater than zero\n");
6418 SetGraphicsMode(hdc, GM_ADVANCED);
6419 ret = pGetCharWidth32W(hdc, 'a', 'a', &bufferW);
6420 ok(ret, "GetCharWidth32W should have succeeded\n");
6421 ok (bufferW > 0," Width should be greater than zero\n");
6422 SetWindowExtEx(hdc, 1,1,NULL);
6423 SetGraphicsMode(hdc, GM_COMPATIBLE);
6424 ret = pGetCharWidth32W(hdc, 'a', 'a', &bufferW);
6425 ok(ret, "GetCharWidth32W should have succeeded\n");
6426 ok (bufferW > 0," Width should be greater than zero\n");
6427 SetGraphicsMode(hdc, GM_ADVANCED);
6428 ret = pGetCharWidth32W(hdc, 'a', 'a', &bufferW);
6429 ok(ret, "GetCharWidth32W should have succeeded\n");
6430 todo_wine ok (bufferW > 0," Width should be greater than zero\n");
6432 ReleaseDC(hwnd, hdc);
6433 DestroyWindow(hwnd);
6434 DeleteObject(hfont);
6437 static void test_fake_bold_font(void)
6439 static const MAT2 x2_mat = { {0,2}, {0,0}, {0,0}, {0,2} };
6440 HDC hdc;
6441 LOGFONTA lf;
6442 BOOL ret;
6443 struct glyph_data {
6444 TEXTMETRICA tm;
6445 ABC abc;
6446 INT w;
6447 GLYPHMETRICS gm;
6448 } data[2];
6449 int i;
6450 DWORD r;
6452 if (!pGetCharWidth32A || !pGetCharABCWidthsA) {
6453 win_skip("GetCharWidth32A/GetCharABCWidthA is not available on this platform\n");
6454 return;
6457 /* Test outline font */
6458 memset(&lf, 0, sizeof(lf));
6459 strcpy(lf.lfFaceName, "Wingdings");
6460 lf.lfCharSet = SYMBOL_CHARSET;
6462 hdc = GetDC(NULL);
6464 for (i = 0; i <= 1; i++)
6466 HFONT hfont, hfont_old;
6468 lf.lfWeight = i ? FW_BOLD : FW_NORMAL;
6469 hfont = CreateFontIndirectA(&lf);
6470 hfont_old = SelectObject(hdc, hfont);
6472 ret = GetTextMetricsA(hdc, &data[i].tm);
6473 ok(ret, "got %d\n", ret);
6474 ret = pGetCharABCWidthsA(hdc, 0x76, 0x76, &data[i].abc);
6475 ok(ret, "got %d\n", ret);
6476 data[i].w = data[i].abc.abcA + data[i].abc.abcB + data[i].abc.abcC;
6477 r = GetGlyphOutlineA(hdc, 0x76, GGO_METRICS, &data[i].gm, 0, NULL, &x2_mat);
6478 ok(r != GDI_ERROR, "got %d\n", ret);
6480 SelectObject(hdc, hfont_old);
6481 DeleteObject(hfont);
6483 ReleaseDC(NULL, hdc);
6485 /* compare results (outline) */
6486 ok(data[0].tm.tmHeight == data[1].tm.tmHeight,
6487 "expected %d, got %d\n", data[0].tm.tmHeight, data[1].tm.tmHeight);
6488 ok(data[0].tm.tmAscent == data[1].tm.tmAscent,
6489 "expected %d, got %d\n", data[0].tm.tmAscent, data[1].tm.tmAscent);
6490 ok(data[0].tm.tmDescent == data[1].tm.tmDescent,
6491 "expected %d, got %d\n", data[0].tm.tmDescent, data[1].tm.tmDescent);
6492 ok(data[0].tm.tmAveCharWidth + 1 == data[1].tm.tmAveCharWidth,
6493 "expected %d, got %d\n", data[0].tm.tmAveCharWidth + 1, data[1].tm.tmAveCharWidth);
6494 ok(data[0].tm.tmMaxCharWidth + 1 == data[1].tm.tmMaxCharWidth,
6495 "expected %d, got %d\n", data[0].tm.tmMaxCharWidth + 1, data[1].tm.tmMaxCharWidth);
6496 ok(data[0].tm.tmOverhang == data[1].tm.tmOverhang,
6497 "expected %d, got %d\n", data[0].tm.tmOverhang, data[1].tm.tmOverhang);
6498 ok(data[0].w + 1 == data[1].w,
6499 "expected %d, got %d\n", data[0].w + 1, data[1].w);
6501 ok(data[0].gm.gmCellIncX + 1 == data[1].gm.gmCellIncX,
6502 "expected %d, got %d\n", data[0].gm.gmCellIncX + 1, data[1].gm.gmCellIncX);
6503 ok(data[0].gm.gmCellIncY == data[1].gm.gmCellIncY,
6504 "expected %d, got %d\n", data[0].gm.gmCellIncY, data[1].gm.gmCellIncY);
6507 static void test_bitmap_font_glyph_index(void)
6509 const WCHAR text[] = {'#','!','/','b','i','n','/','s','h',0};
6510 const struct {
6511 LPCSTR face;
6512 BYTE charset;
6513 } bitmap_font_list[] = {
6514 { "Courier", ANSI_CHARSET },
6515 { "Small Fonts", ANSI_CHARSET },
6516 { "Fixedsys", DEFAULT_CHARSET },
6517 { "System", DEFAULT_CHARSET }
6519 HDC hdc;
6520 LOGFONTA lf;
6521 HFONT hFont;
6522 CHAR facename[LF_FACESIZE];
6523 BITMAPINFO bmi;
6524 HBITMAP hBmp[2];
6525 void *pixels[2];
6526 int i, j;
6527 DWORD ret;
6528 BITMAP bmp;
6529 TEXTMETRICA tm;
6530 CHARSETINFO ci;
6531 BYTE chr = '\xA9';
6533 if (!pGetGlyphIndicesW || !pGetGlyphIndicesA) {
6534 win_skip("GetGlyphIndices is unavailable\n");
6535 return;
6538 hdc = CreateCompatibleDC(0);
6539 ok(hdc != NULL, "CreateCompatibleDC failed\n");
6541 memset(&bmi, 0, sizeof(bmi));
6542 bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
6543 bmi.bmiHeader.biBitCount = 32;
6544 bmi.bmiHeader.biPlanes = 1;
6545 bmi.bmiHeader.biWidth = 128;
6546 bmi.bmiHeader.biHeight = 32;
6547 bmi.bmiHeader.biCompression = BI_RGB;
6549 for (i = 0; i < sizeof(bitmap_font_list)/sizeof(bitmap_font_list[0]); i++) {
6550 memset(&lf, 0, sizeof(lf));
6551 lf.lfCharSet = bitmap_font_list[i].charset;
6552 strcpy(lf.lfFaceName, bitmap_font_list[i].face);
6553 hFont = CreateFontIndirectA(&lf);
6554 ok(hFont != NULL, "Can't create font (%s:%d)\n", lf.lfFaceName, lf.lfCharSet);
6555 hFont = SelectObject(hdc, hFont);
6556 ret = GetTextMetricsA(hdc, &tm);
6557 ok(ret, "GetTextMetric failed\n");
6558 ret = GetTextFaceA(hdc, sizeof(facename), facename);
6559 ok(ret, "GetTextFace failed\n");
6560 if (tm.tmPitchAndFamily & TMPF_TRUETYPE) {
6561 skip("TrueType font (%s) was selected for \"%s\"\n", facename, bitmap_font_list[i].face);
6562 continue;
6564 if (lstrcmpiA(facename, lf.lfFaceName) != 0) {
6565 skip("expected %s, got %s\n", lf.lfFaceName, facename);
6566 continue;
6569 for (j = 0; j < 2; j++) {
6570 HBITMAP hBmpPrev;
6571 hBmp[j] = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, &pixels[j], NULL, 0);
6572 ok(hBmp[j] != NULL, "Can't create DIB\n");
6573 hBmpPrev = SelectObject(hdc, hBmp[j]);
6574 switch (j) {
6575 case 0:
6576 ret = ExtTextOutW(hdc, 0, 0, 0, NULL, text, lstrlenW(text), NULL);
6577 break;
6578 case 1:
6580 int len = lstrlenW(text);
6581 LPWORD indices = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WORD));
6582 ret = pGetGlyphIndicesW(hdc, text, len, indices, 0);
6583 ok(ret, "GetGlyphIndices failed\n");
6584 ok(memcmp(indices, text, sizeof(WORD) * len) == 0,
6585 "Glyph indices and text are different for %s:%d\n", lf.lfFaceName, tm.tmCharSet);
6586 ret = ExtTextOutW(hdc, 0, 0, ETO_GLYPH_INDEX, NULL, indices, len, NULL);
6587 HeapFree(GetProcessHeap(), 0, indices);
6588 break;
6591 ok(ret, "ExtTextOutW failed\n");
6592 SelectObject(hdc, hBmpPrev);
6595 GetObjectA(hBmp[0], sizeof(bmp), &bmp);
6596 ok(memcmp(pixels[0], pixels[1], bmp.bmHeight * bmp.bmWidthBytes) == 0,
6597 "Images are different (%s:%d)\n", lf.lfFaceName, tm.tmCharSet);
6599 ret = TranslateCharsetInfo((LPDWORD)(DWORD_PTR)tm.tmCharSet, &ci, TCI_SRCCHARSET);
6600 if (!ret) {
6601 skip("Can't get charset info for (%s:%d)\n", lf.lfFaceName, tm.tmCharSet);
6602 goto next;
6604 if (IsDBCSLeadByteEx(ci.ciACP, chr)) {
6605 skip("High-ascii character is not defined in codepage %d\n", ci.ciACP);
6606 goto next;
6609 for (j = 0; j < 2; j++) {
6610 HBITMAP hBmpPrev;
6611 WORD code;
6612 hBmpPrev = SelectObject(hdc, hBmp[j]);
6613 switch (j) {
6614 case 0:
6615 ret = ExtTextOutA(hdc, 100, 0, 0, NULL, (LPCSTR)&chr, 1, NULL);
6616 break;
6617 case 1:
6618 ret = pGetGlyphIndicesA(hdc, (LPCSTR)&chr, 1, &code, 0);
6619 ok(ret, "GetGlyphIndices failed\n");
6620 ok(code == chr, "expected %02x, got %02x (%s:%d)\n", chr, code, lf.lfFaceName, tm.tmCharSet);
6621 ret = ExtTextOutA(hdc, 100, 0, ETO_GLYPH_INDEX, NULL, (LPCSTR)&code, 1, NULL);
6622 break;
6624 ok(ret, "ExtTextOutA failed\n");
6625 SelectObject(hdc, hBmpPrev);
6628 ok(memcmp(pixels[0], pixels[1], bmp.bmHeight * bmp.bmWidthBytes) == 0,
6629 "Images are different (%s:%d)\n", lf.lfFaceName, tm.tmCharSet);
6630 next:
6631 for (j = 0; j < 2; j++)
6632 DeleteObject(hBmp[j]);
6633 hFont = SelectObject(hdc, hFont);
6634 DeleteObject(hFont);
6637 DeleteDC(hdc);
6640 static void test_GetCharWidthI(void)
6642 static const char *teststr = "wine ";
6643 HFONT hfont, prev_hfont;
6644 WORD glyphs[5];
6645 INT widths[5];
6646 LOGFONTA lf;
6647 ABC abc[5];
6648 int len, i;
6649 DWORD nb;
6650 BOOL ret;
6651 HDC hdc;
6653 memset(&lf, 0, sizeof(lf));
6654 strcpy(lf.lfFaceName, "Tahoma");
6655 lf.lfHeight = -20;
6657 hdc = GetDC(0);
6659 hfont = CreateFontIndirectA(&lf);
6660 prev_hfont = SelectObject(hdc, hfont);
6662 len = strlen(teststr);
6663 nb = GetGlyphIndicesA(hdc, teststr, len, glyphs, 0);
6664 ok(nb == len, "\n");
6666 memset(abc, 0xcc, sizeof(abc));
6667 ret = GetCharABCWidthsI(hdc, 0, len, glyphs, abc);
6668 ok(ret, "GetCharABCWidthsI failed\n");
6670 memset(widths, 0xcc, sizeof(widths));
6671 ret = GetCharWidthI(hdc, 0, len, glyphs, widths);
6672 ok(ret, "GetCharWidthI failed\n");
6674 for (i = 0; i < len; i++)
6675 ok(widths[i] == abc[i].abcA + abc[i].abcB + abc[i].abcC, "%u, glyph %u, got width %d\n",
6676 i, glyphs[i], widths[i]);
6678 DeleteObject(SelectObject(hdc, prev_hfont));
6679 ReleaseDC(0, hdc);
6682 static INT CALLBACK long_enum_proc(const LOGFONTA *lf, const TEXTMETRICA *tm, DWORD type, LPARAM lparam)
6684 BOOL *found_font = (BOOL *)lparam;
6685 *found_font = TRUE;
6686 return 1;
6689 static void test_long_names(void)
6691 char ttf_name[MAX_PATH];
6692 LOGFONTA font = {0};
6693 HFONT handle_font;
6694 BOOL found_font;
6695 int ret;
6696 HDC dc;
6698 if (!write_ttf_file("wine_longname.ttf", ttf_name))
6700 skip("Failed to create ttf file for testing\n");
6701 return;
6704 dc = GetDC(NULL);
6706 ret = AddFontResourceExA(ttf_name, FR_PRIVATE, 0);
6707 ok(ret, "AddFontResourceEx() failed\n");
6709 strcpy(font.lfFaceName, "wine_3_this_is_a_very_long_name");
6710 found_font = FALSE;
6711 EnumFontFamiliesExA(dc, &font, long_enum_proc, (LPARAM)&found_font, 0);
6712 ok(found_font == TRUE, "EnumFontFamiliesExA didn't find font.\n");
6714 strcpy(font.lfFaceName, "wine_2_this_is_a_very_long_name");
6715 found_font = FALSE;
6716 EnumFontFamiliesExA(dc, &font, long_enum_proc, (LPARAM)&found_font, 0);
6717 ok(found_font == TRUE, "EnumFontFamiliesExA didn't find font.\n");
6719 strcpy(font.lfFaceName, "wine_1_this_is_a_very_long_name");
6720 found_font = FALSE;
6721 EnumFontFamiliesExA(dc, &font, long_enum_proc, (LPARAM)&found_font, 0);
6722 ok(found_font == FALSE, "EnumFontFamiliesExA must not find font.\n");
6724 handle_font = CreateFontIndirectA(&font);
6725 ok(handle_font != NULL, "CreateFontIndirectA failed\n");
6726 DeleteObject(handle_font);
6728 ret = RemoveFontResourceExA(ttf_name, FR_PRIVATE, 0);
6729 ok(ret, "RemoveFontResourceEx() failed\n");
6731 DeleteFileA(ttf_name);
6732 ReleaseDC(NULL, dc);
6735 START_TEST(font)
6737 init();
6739 test_stock_fonts();
6740 test_logfont();
6741 test_bitmap_font();
6742 test_outline_font();
6743 test_bitmap_font_metrics();
6744 test_GdiGetCharDimensions();
6745 test_GetCharABCWidths();
6746 test_text_extents();
6747 test_GetGlyphIndices();
6748 test_GetKerningPairs();
6749 test_GetOutlineTextMetrics();
6750 test_SetTextJustification();
6751 test_font_charset();
6752 test_GdiGetCodePage();
6753 test_GetFontUnicodeRanges();
6754 test_nonexistent_font();
6755 test_orientation();
6756 test_height_selection();
6757 test_AddFontMemResource();
6758 test_EnumFonts();
6759 test_EnumFonts_subst();
6761 /* On Windows Arial has a lot of default charset aliases such as Arial Cyr,
6762 * I'd like to avoid them in this test.
6764 test_EnumFontFamilies("Arial Black", ANSI_CHARSET);
6765 test_EnumFontFamilies("Symbol", SYMBOL_CHARSET);
6766 if (is_truetype_font_installed("Arial Black") &&
6767 (is_truetype_font_installed("Symbol") || is_truetype_font_installed("Wingdings")))
6769 test_EnumFontFamilies("", ANSI_CHARSET);
6770 test_EnumFontFamilies("", SYMBOL_CHARSET);
6771 test_EnumFontFamilies("", DEFAULT_CHARSET);
6773 else
6774 skip("Arial Black or Symbol/Wingdings is not installed\n");
6775 test_EnumFontFamiliesEx_default_charset();
6776 test_GetTextMetrics();
6777 test_RealizationInfo();
6778 test_GetTextFace();
6779 test_GetGlyphOutline();
6780 test_GetTextMetrics2("Tahoma", -11);
6781 test_GetTextMetrics2("Tahoma", -55);
6782 test_GetTextMetrics2("Tahoma", -110);
6783 test_GetTextMetrics2("Arial", -11);
6784 test_GetTextMetrics2("Arial", -55);
6785 test_GetTextMetrics2("Arial", -110);
6786 test_CreateFontIndirect();
6787 test_CreateFontIndirectEx();
6788 test_oemcharset();
6789 test_fullname();
6790 test_fullname2();
6791 test_east_asian_font_selection();
6792 test_max_height();
6793 test_vertical_order();
6794 test_GetCharWidth32();
6795 test_fake_bold_font();
6796 test_bitmap_font_glyph_index();
6797 test_GetCharWidthI();
6798 test_long_names();
6800 /* These tests should be last test until RemoveFontResource
6801 * is properly implemented.
6803 test_vertical_font();
6804 test_CreateScalableFontResource();