gdi32/tests: Use wide-char string literals.
[wine.git] / dlls / gdi32 / tests / font.c
blobad7408f5620b197f0da8c8e6e14baae6ab8e7e78
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 <stdio.h>
24 #include <assert.h>
26 #include "windef.h"
27 #include "winbase.h"
28 #include "wingdi.h"
29 #include "winuser.h"
30 #include "winnls.h"
32 #include "wine/heap.h"
33 #include "wine/test.h"
35 static inline BOOL match_off_by_n(int a, int b, unsigned int n)
37 return abs(a - b) <= n;
39 #define match_off_by_1(a, b, exact) match_off_by_n((a), (b), (exact) ? 0 : 1)
40 #define near_match(a, b) match_off_by_n((a), (b), 6)
41 #define expect(expected, got) ok(got == expected, "Expected %.8x, got %.8x\n", expected, got)
43 static LONG (WINAPI *pGdiGetCharDimensions)(HDC hdc, LPTEXTMETRICW lptm, LONG *height);
44 static DWORD (WINAPI *pGdiGetCodePage)(HDC hdc);
45 static BOOL (WINAPI *pGetCharABCWidthsFloatW)(HDC hdc, UINT first, UINT last, LPABCFLOAT abc);
46 static BOOL (WINAPI *pGetCharWidth32W)(HDC hdc, UINT first, UINT last, LPINT buffer);
47 static BOOL (WINAPI *pGetCharWidthInfo)(HDC hdc, void *);
48 static DWORD (WINAPI *pGetFontUnicodeRanges)(HDC hdc, LPGLYPHSET lpgs);
49 static DWORD (WINAPI *pGetGlyphIndicesW)(HDC hdc, LPCWSTR lpstr, INT count, LPWORD pgi, DWORD flags);
50 static BOOL (WINAPI *pGetTextExtentExPointI)(HDC hdc, const WORD *indices, INT count, INT max_ext,
51 LPINT nfit, LPINT dxs, LPSIZE size );
52 static BOOL (WINAPI *pGdiRealizationInfo)(HDC hdc, DWORD *);
53 static HFONT (WINAPI *pCreateFontIndirectExA)(const ENUMLOGFONTEXDVA *);
54 static HANDLE (WINAPI *pAddFontMemResourceEx)(PVOID, DWORD, PVOID, DWORD *);
55 static BOOL (WINAPI *pRemoveFontMemResourceEx)(HANDLE);
56 static INT (WINAPI *pAddFontResourceExA)(LPCSTR, DWORD, PVOID);
57 static BOOL (WINAPI *pRemoveFontResourceExA)(LPCSTR, DWORD, PVOID);
58 static BOOL (WINAPI *pGetFontRealizationInfo)(HDC hdc, DWORD *);
59 static BOOL (WINAPI *pGetFontFileInfo)(DWORD, DWORD, void *, SIZE_T, SIZE_T *);
60 static BOOL (WINAPI *pGetFontFileData)(DWORD, DWORD, UINT64, void *, DWORD);
62 static HMODULE hgdi32 = 0;
63 static const MAT2 mat = { {0,1}, {0,0}, {0,0}, {0,1} };
64 static WORD system_lang_id;
66 #ifdef WORDS_BIGENDIAN
67 #define GET_BE_WORD(x) (x)
68 #define GET_BE_DWORD(x) (x)
69 #else
70 #define GET_BE_WORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
71 #define GET_BE_DWORD(x) MAKELONG(GET_BE_WORD(HIWORD(x)), GET_BE_WORD(LOWORD(x)));
72 #endif
74 #define MS_MAKE_TAG(ch0, ch1, ch2, ch3) \
75 ((DWORD)(BYTE)(ch0) | ((DWORD)(BYTE)(ch1) << 8) | \
76 ((DWORD)(BYTE)(ch2) << 16) | ((DWORD)(BYTE)(ch3) << 24))
77 #define MS_OS2_TAG MS_MAKE_TAG('O','S','/','2')
78 #define MS_CMAP_TAG MS_MAKE_TAG('c','m','a','p')
79 #define MS_NAME_TAG MS_MAKE_TAG('n','a','m','e')
81 static void init(void)
83 hgdi32 = GetModuleHandleA("gdi32.dll");
85 pGdiGetCharDimensions = (void *)GetProcAddress(hgdi32, "GdiGetCharDimensions");
86 pGdiGetCodePage = (void *) GetProcAddress(hgdi32,"GdiGetCodePage");
87 pGetCharABCWidthsFloatW = (void *)GetProcAddress(hgdi32, "GetCharABCWidthsFloatW");
88 pGetCharWidth32W = (void *)GetProcAddress(hgdi32, "GetCharWidth32W");
89 pGetCharWidthInfo = (void *)GetProcAddress(hgdi32, "GetCharWidthInfo");
90 pGetFontUnicodeRanges = (void *)GetProcAddress(hgdi32, "GetFontUnicodeRanges");
91 pGetGlyphIndicesW = (void *)GetProcAddress(hgdi32, "GetGlyphIndicesW");
92 pGetTextExtentExPointI = (void *)GetProcAddress(hgdi32, "GetTextExtentExPointI");
93 pGdiRealizationInfo = (void *)GetProcAddress(hgdi32, "GdiRealizationInfo");
94 pCreateFontIndirectExA = (void *)GetProcAddress(hgdi32, "CreateFontIndirectExA");
95 pAddFontMemResourceEx = (void *)GetProcAddress(hgdi32, "AddFontMemResourceEx");
96 pRemoveFontMemResourceEx = (void *)GetProcAddress(hgdi32, "RemoveFontMemResourceEx");
97 pAddFontResourceExA = (void *)GetProcAddress(hgdi32, "AddFontResourceExA");
98 pRemoveFontResourceExA = (void *)GetProcAddress(hgdi32, "RemoveFontResourceExA");
99 pGetFontRealizationInfo = (void *)GetProcAddress(hgdi32, "GetFontRealizationInfo");
100 pGetFontFileInfo = (void *)GetProcAddress(hgdi32, "GetFontFileInfo");
101 pGetFontFileData = (void *)GetProcAddress(hgdi32, "GetFontFileData");
103 system_lang_id = PRIMARYLANGID(GetSystemDefaultLangID());
106 static INT CALLBACK is_truetype_font_installed_proc(const LOGFONTA *elf, const TEXTMETRICA *ntm, DWORD type, LPARAM lParam)
108 if (type != TRUETYPE_FONTTYPE) return 1;
110 return 0;
113 static BOOL is_truetype_font_installed(const char *name)
115 HDC hdc = GetDC(0);
116 BOOL ret = FALSE;
118 if (!EnumFontFamiliesA(hdc, name, is_truetype_font_installed_proc, 0))
119 ret = TRUE;
121 ReleaseDC(0, hdc);
122 return ret;
125 static INT CALLBACK is_font_installed_proc(const LOGFONTA *elf, const TEXTMETRICA *ntm, DWORD type, LPARAM lParam)
127 return 0;
130 static BOOL is_font_installed(const char *name)
132 HDC hdc = GetDC(0);
133 BOOL ret = FALSE;
135 if(!EnumFontFamiliesA(hdc, name, is_font_installed_proc, 0))
136 ret = TRUE;
138 ReleaseDC(0, hdc);
139 return ret;
142 static void *get_res_data(const char *fontname, DWORD *rsrc_size)
144 HRSRC rsrc;
145 void *rsrc_data;
147 rsrc = FindResourceA(GetModuleHandleA(NULL), fontname, (LPCSTR)RT_RCDATA);
148 if (!rsrc) return NULL;
150 rsrc_data = LockResource(LoadResource(GetModuleHandleA(NULL), rsrc));
151 if (!rsrc_data) return NULL;
153 *rsrc_size = SizeofResource(GetModuleHandleA(NULL), rsrc);
154 if (!*rsrc_size) return NULL;
156 return rsrc_data;
159 static BOOL write_tmp_file( const void *data, DWORD *size, char *tmp_name )
161 char tmp_path[MAX_PATH];
162 HANDLE hfile;
163 BOOL ret;
165 GetTempPathA(MAX_PATH, tmp_path);
166 GetTempFileNameA(tmp_path, "ttf", 0, tmp_name);
168 hfile = CreateFileA(tmp_name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
169 if (hfile == INVALID_HANDLE_VALUE) return FALSE;
171 ret = WriteFile(hfile, data, *size, size, NULL);
173 CloseHandle(hfile);
174 return ret;
177 static BOOL write_ttf_file(const char *fontname, char *tmp_name)
179 void *rsrc_data;
180 DWORD rsrc_size;
182 rsrc_data = get_res_data( fontname, &rsrc_size );
183 if (!rsrc_data) return FALSE;
185 return write_tmp_file( rsrc_data, &rsrc_size, tmp_name );
188 static void check_font(const char* test, const LOGFONTA* lf, HFONT hfont)
190 LOGFONTA getobj_lf;
191 int ret, minlen = 0;
193 if (!hfont)
194 return;
196 ret = GetObjectA(hfont, sizeof(getobj_lf), &getobj_lf);
197 /* NT4 tries to be clever and only returns the minimum length */
198 while (lf->lfFaceName[minlen] && minlen < LF_FACESIZE-1)
199 minlen++;
200 minlen += FIELD_OFFSET(LOGFONTA, lfFaceName) + 1;
201 ok(ret == sizeof(LOGFONTA) || ret == minlen, "%s: GetObject returned %d\n", test, ret);
202 ok(lf->lfHeight == getobj_lf.lfHeight ||
203 broken((SHORT)lf->lfHeight == getobj_lf.lfHeight), /* win9x */
204 "lfHeight: expect %08x got %08x\n", lf->lfHeight, getobj_lf.lfHeight);
205 ok(lf->lfWidth == getobj_lf.lfWidth ||
206 broken((SHORT)lf->lfWidth == getobj_lf.lfWidth), /* win9x */
207 "lfWidth: expect %08x got %08x\n", lf->lfWidth, getobj_lf.lfWidth);
208 ok(lf->lfEscapement == getobj_lf.lfEscapement ||
209 broken((SHORT)lf->lfEscapement == getobj_lf.lfEscapement), /* win9x */
210 "lfEscapement: expect %08x got %08x\n", lf->lfEscapement, getobj_lf.lfEscapement);
211 ok(lf->lfOrientation == getobj_lf.lfOrientation ||
212 broken((SHORT)lf->lfOrientation == getobj_lf.lfOrientation), /* win9x */
213 "lfOrientation: expect %08x got %08x\n", lf->lfOrientation, getobj_lf.lfOrientation);
214 ok(lf->lfWeight == getobj_lf.lfWeight ||
215 broken((SHORT)lf->lfWeight == getobj_lf.lfWeight), /* win9x */
216 "lfWeight: expect %08x got %08x\n", lf->lfWeight, getobj_lf.lfWeight);
217 ok(lf->lfItalic == getobj_lf.lfItalic, "lfItalic: expect %02x got %02x\n", lf->lfItalic, getobj_lf.lfItalic);
218 ok(lf->lfUnderline == getobj_lf.lfUnderline, "lfUnderline: expect %02x got %02x\n", lf->lfUnderline, getobj_lf.lfUnderline);
219 ok(lf->lfStrikeOut == getobj_lf.lfStrikeOut, "lfStrikeOut: expect %02x got %02x\n", lf->lfStrikeOut, getobj_lf.lfStrikeOut);
220 ok(lf->lfCharSet == getobj_lf.lfCharSet, "lfCharSet: expect %02x got %02x\n", lf->lfCharSet, getobj_lf.lfCharSet);
221 ok(lf->lfOutPrecision == getobj_lf.lfOutPrecision, "lfOutPrecision: expect %02x got %02x\n", lf->lfOutPrecision, getobj_lf.lfOutPrecision);
222 ok(lf->lfClipPrecision == getobj_lf.lfClipPrecision, "lfClipPrecision: expect %02x got %02x\n", lf->lfClipPrecision, getobj_lf.lfClipPrecision);
223 ok(lf->lfQuality == getobj_lf.lfQuality, "lfQuality: expect %02x got %02x\n", lf->lfQuality, getobj_lf.lfQuality);
224 ok(lf->lfPitchAndFamily == getobj_lf.lfPitchAndFamily, "lfPitchAndFamily: expect %02x got %02x\n", lf->lfPitchAndFamily, getobj_lf.lfPitchAndFamily);
225 ok(!lstrcmpA(lf->lfFaceName, getobj_lf.lfFaceName) ||
226 broken(!memcmp(lf->lfFaceName, getobj_lf.lfFaceName, LF_FACESIZE-1)), /* win9x doesn't ensure '\0' termination */
227 "%s: font names don't match: %s != %s\n", test, lf->lfFaceName, getobj_lf.lfFaceName);
230 static HFONT create_font(const char* test, const LOGFONTA* lf)
232 HFONT hfont = CreateFontIndirectA(lf);
233 ok(hfont != 0, "%s: CreateFontIndirect failed\n", test);
234 if (hfont)
235 check_font(test, lf, hfont);
236 return hfont;
239 static void test_logfont(void)
241 LOGFONTA lf;
242 HFONT hfont;
244 memset(&lf, 0, sizeof lf);
246 lf.lfCharSet = ANSI_CHARSET;
247 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
248 lf.lfWeight = FW_DONTCARE;
249 lf.lfHeight = 16;
250 lf.lfWidth = 16;
251 lf.lfQuality = DEFAULT_QUALITY;
253 lstrcpyA(lf.lfFaceName, "Arial");
254 hfont = create_font("Arial", &lf);
255 DeleteObject(hfont);
257 memset(&lf, 'A', sizeof(lf));
258 hfont = CreateFontIndirectA(&lf);
259 ok(hfont != 0, "CreateFontIndirectA with strange LOGFONT failed\n");
261 lf.lfFaceName[LF_FACESIZE - 1] = 0;
262 check_font("AAA...", &lf, hfont);
263 DeleteObject(hfont);
266 static INT CALLBACK font_enum_proc(const LOGFONTA *elf, const TEXTMETRICA *ntm, DWORD type, LPARAM lParam)
268 if (type & RASTER_FONTTYPE)
270 LOGFONTA *lf = (LOGFONTA *)lParam;
271 *lf = *elf;
272 return 0; /* stop enumeration */
275 return 1; /* continue enumeration */
278 static void compare_tm(const TEXTMETRICA *tm, const TEXTMETRICA *otm)
280 ok(tm->tmHeight == otm->tmHeight, "tmHeight %d != %d\n", tm->tmHeight, otm->tmHeight);
281 ok(tm->tmAscent == otm->tmAscent, "tmAscent %d != %d\n", tm->tmAscent, otm->tmAscent);
282 ok(tm->tmDescent == otm->tmDescent, "tmDescent %d != %d\n", tm->tmDescent, otm->tmDescent);
283 ok(tm->tmInternalLeading == otm->tmInternalLeading, "tmInternalLeading %d != %d\n", tm->tmInternalLeading, otm->tmInternalLeading);
284 ok(tm->tmExternalLeading == otm->tmExternalLeading, "tmExternalLeading %d != %d\n", tm->tmExternalLeading, otm->tmExternalLeading);
285 ok(tm->tmAveCharWidth == otm->tmAveCharWidth, "tmAveCharWidth %d != %d\n", tm->tmAveCharWidth, otm->tmAveCharWidth);
286 ok(tm->tmMaxCharWidth == otm->tmMaxCharWidth, "tmMaxCharWidth %d != %d\n", tm->tmMaxCharWidth, otm->tmMaxCharWidth);
287 ok(tm->tmWeight == otm->tmWeight, "tmWeight %d != %d\n", tm->tmWeight, otm->tmWeight);
288 ok(tm->tmOverhang == otm->tmOverhang, "tmOverhang %d != %d\n", tm->tmOverhang, otm->tmOverhang);
289 ok(tm->tmDigitizedAspectX == otm->tmDigitizedAspectX, "tmDigitizedAspectX %d != %d\n", tm->tmDigitizedAspectX, otm->tmDigitizedAspectX);
290 ok(tm->tmDigitizedAspectY == otm->tmDigitizedAspectY, "tmDigitizedAspectY %d != %d\n", tm->tmDigitizedAspectY, otm->tmDigitizedAspectY);
291 ok(tm->tmFirstChar == otm->tmFirstChar, "tmFirstChar %d != %d\n", tm->tmFirstChar, otm->tmFirstChar);
292 ok(tm->tmLastChar == otm->tmLastChar, "tmLastChar %d != %d\n", tm->tmLastChar, otm->tmLastChar);
293 ok(tm->tmDefaultChar == otm->tmDefaultChar, "tmDefaultChar %d != %d\n", tm->tmDefaultChar, otm->tmDefaultChar);
294 ok(tm->tmBreakChar == otm->tmBreakChar, "tmBreakChar %d != %d\n", tm->tmBreakChar, otm->tmBreakChar);
295 ok(tm->tmItalic == otm->tmItalic, "tmItalic %d != %d\n", tm->tmItalic, otm->tmItalic);
296 ok(tm->tmUnderlined == otm->tmUnderlined, "tmUnderlined %d != %d\n", tm->tmUnderlined, otm->tmUnderlined);
297 ok(tm->tmStruckOut == otm->tmStruckOut, "tmStruckOut %d != %d\n", tm->tmStruckOut, otm->tmStruckOut);
298 ok(tm->tmPitchAndFamily == otm->tmPitchAndFamily, "tmPitchAndFamily %d != %d\n", tm->tmPitchAndFamily, otm->tmPitchAndFamily);
299 ok(tm->tmCharSet == otm->tmCharSet, "tmCharSet %d != %d\n", tm->tmCharSet, otm->tmCharSet);
302 static void test_font_metrics(HDC hdc, HFONT hfont, LONG lfHeight,
303 LONG lfWidth, const char *test_str,
304 INT test_str_len, const TEXTMETRICA *tm_orig,
305 const SIZE *size_orig, INT width_of_A_orig,
306 INT scale_x, INT scale_y)
308 LOGFONTA lf;
309 OUTLINETEXTMETRICA otm;
310 TEXTMETRICA tm;
311 SIZE size;
312 INT width_of_A, cx, cy;
313 UINT ret;
315 if (!hfont)
316 return;
318 ok(GetCurrentObject(hdc, OBJ_FONT) == hfont, "hfont should be selected\n");
320 GetObjectA(hfont, sizeof(lf), &lf);
322 if (GetOutlineTextMetricsA(hdc, 0, NULL))
324 otm.otmSize = sizeof(otm) / 2;
325 ret = GetOutlineTextMetricsA(hdc, otm.otmSize, &otm);
326 ok(ret == sizeof(otm)/2 /* XP */ ||
327 ret == 1 /* Win9x */, "expected sizeof(otm)/2, got %u\n", ret);
329 memset(&otm, 0x1, sizeof(otm));
330 otm.otmSize = sizeof(otm);
331 ret = GetOutlineTextMetricsA(hdc, otm.otmSize, &otm);
332 ok(ret == sizeof(otm) /* XP */ ||
333 ret == 1 /* Win9x */, "expected sizeof(otm), got %u\n", ret);
335 memset(&tm, 0x2, sizeof(tm));
336 ret = GetTextMetricsA(hdc, &tm);
337 ok(ret, "GetTextMetricsA failed\n");
338 /* the structure size is aligned */
339 if (memcmp(&tm, &otm.otmTextMetrics, FIELD_OFFSET(TEXTMETRICA, tmCharSet) + 1))
341 ok(0, "tm != otm\n");
342 compare_tm(&tm, &otm.otmTextMetrics);
345 tm = otm.otmTextMetrics;
346 if (0) /* these metrics are scaled too, but with rounding errors */
348 ok(otm.otmAscent == tm.tmAscent, "ascent %d != %d\n", otm.otmAscent, tm.tmAscent);
349 ok(otm.otmDescent == -tm.tmDescent, "descent %d != %d\n", otm.otmDescent, -tm.tmDescent);
351 ok(otm.otmMacAscent == tm.tmAscent, "ascent %d != %d\n", otm.otmMacAscent, tm.tmAscent);
352 ok(otm.otmDescent < 0, "otm.otmDescent should be < 0\n");
353 ok(otm.otmMacDescent < 0, "otm.otmMacDescent should be < 0\n");
354 ok(tm.tmDescent > 0, "tm.tmDescent should be > 0\n");
355 ok(otm.otmMacDescent == -tm.tmDescent, "descent %d != %d\n", otm.otmMacDescent, -tm.tmDescent);
356 ok(otm.otmEMSquare == 2048, "expected 2048, got %d\n", otm.otmEMSquare);
358 else
360 ret = GetTextMetricsA(hdc, &tm);
361 ok(ret, "GetTextMetricsA failed\n");
364 cx = tm.tmAveCharWidth / tm_orig->tmAveCharWidth;
365 cy = tm.tmHeight / tm_orig->tmHeight;
366 ok(cx == scale_x && cy == scale_y, "height %d: expected scale_x %d, scale_y %d, got cx %d, cy %d\n",
367 lfHeight, scale_x, scale_y, cx, cy);
368 ok(tm.tmHeight == tm_orig->tmHeight * scale_y, "height %d != %d\n", tm.tmHeight, tm_orig->tmHeight * scale_y);
369 ok(tm.tmAscent == tm_orig->tmAscent * scale_y, "ascent %d != %d\n", tm.tmAscent, tm_orig->tmAscent * scale_y);
370 ok(tm.tmDescent == tm_orig->tmDescent * scale_y, "descent %d != %d\n", tm.tmDescent, tm_orig->tmDescent * scale_y);
371 ok(near_match(tm.tmAveCharWidth, tm_orig->tmAveCharWidth * scale_x), "ave width %d != %d\n", tm.tmAveCharWidth, tm_orig->tmAveCharWidth * scale_x);
372 ok(near_match(tm.tmMaxCharWidth, tm_orig->tmMaxCharWidth * scale_x), "max width %d != %d\n", tm.tmMaxCharWidth, tm_orig->tmMaxCharWidth * scale_x);
374 ok(lf.lfHeight == lfHeight, "lfHeight %d != %d\n", lf.lfHeight, lfHeight);
375 if (lf.lfHeight)
377 if (lf.lfWidth)
378 ok(lf.lfWidth == tm.tmAveCharWidth, "lfWidth %d != tm %d\n", lf.lfWidth, tm.tmAveCharWidth);
380 else
381 ok(lf.lfWidth == lfWidth, "lfWidth %d != %d\n", lf.lfWidth, lfWidth);
383 GetTextExtentPoint32A(hdc, test_str, test_str_len, &size);
385 ok(near_match(size.cx, size_orig->cx * scale_x), "cx %d != %d\n", size.cx, size_orig->cx * scale_x);
386 ok(size.cy == size_orig->cy * scale_y, "cy %d != %d\n", size.cy, size_orig->cy * scale_y);
388 GetCharWidthA(hdc, 'A', 'A', &width_of_A);
390 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);
393 /* Test how GDI scales bitmap font metrics */
394 static void test_bitmap_font(void)
396 static const char test_str[11] = "Test String";
397 HDC hdc;
398 LOGFONTA bitmap_lf;
399 HFONT hfont, old_hfont;
400 TEXTMETRICA tm_orig;
401 SIZE size_orig;
402 INT ret, i, width_orig, height_orig, scale, lfWidth;
404 hdc = CreateCompatibleDC(0);
406 /* "System" has only 1 pixel size defined, otherwise the test breaks */
407 ret = EnumFontFamiliesA(hdc, "System", font_enum_proc, (LPARAM)&bitmap_lf);
408 if (ret)
410 ReleaseDC(0, hdc);
411 skip("no bitmap fonts were found, skipping the test\n");
412 return;
415 trace("found bitmap font %s, height %d\n", bitmap_lf.lfFaceName, bitmap_lf.lfHeight);
417 height_orig = bitmap_lf.lfHeight;
418 lfWidth = bitmap_lf.lfWidth;
420 hfont = create_font("bitmap", &bitmap_lf);
421 old_hfont = SelectObject(hdc, hfont);
422 ok(GetTextMetricsA(hdc, &tm_orig), "GetTextMetricsA failed\n");
423 ok(GetTextExtentPoint32A(hdc, test_str, sizeof(test_str), &size_orig), "GetTextExtentPoint32A failed\n");
424 ok(GetCharWidthA(hdc, 'A', 'A', &width_orig), "GetCharWidthA failed\n");
425 SelectObject(hdc, old_hfont);
426 DeleteObject(hfont);
428 bitmap_lf.lfHeight = 0;
429 bitmap_lf.lfWidth = 4;
430 hfont = create_font("bitmap", &bitmap_lf);
431 old_hfont = SelectObject(hdc, hfont);
432 test_font_metrics(hdc, hfont, 0, 4, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 1, 1);
433 SelectObject(hdc, old_hfont);
434 DeleteObject(hfont);
436 bitmap_lf.lfHeight = height_orig;
437 bitmap_lf.lfWidth = lfWidth;
439 /* test fractional scaling */
440 for (i = 1; i <= height_orig * 6; i++)
442 INT nearest_height;
444 bitmap_lf.lfHeight = i;
445 hfont = create_font("fractional", &bitmap_lf);
446 scale = (i + height_orig - 1) / height_orig;
447 nearest_height = scale * height_orig;
448 /* Only jump to the next height if the difference <= 25% original height */
449 if (scale > 2 && nearest_height - i > height_orig / 4) scale--;
450 /* The jump between unscaled and doubled is delayed by 1 in winnt+ but not in win9x,
451 so we'll not test this particular height. */
452 else if(scale == 2 && nearest_height - i == (height_orig / 4)) continue;
453 else if(scale == 2 && nearest_height - i > (height_orig / 4 - 1)) scale--;
454 old_hfont = SelectObject(hdc, hfont);
455 test_font_metrics(hdc, hfont, bitmap_lf.lfHeight, 0, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 1, scale);
456 SelectObject(hdc, old_hfont);
457 DeleteObject(hfont);
460 /* test integer scaling 3x2 */
461 bitmap_lf.lfHeight = height_orig * 2;
462 bitmap_lf.lfWidth *= 3;
463 hfont = create_font("3x2", &bitmap_lf);
464 old_hfont = SelectObject(hdc, hfont);
465 test_font_metrics(hdc, hfont, bitmap_lf.lfHeight, 0, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 3, 2);
466 SelectObject(hdc, old_hfont);
467 DeleteObject(hfont);
469 /* test integer scaling 3x3 */
470 bitmap_lf.lfHeight = height_orig * 3;
471 bitmap_lf.lfWidth = 0;
472 hfont = create_font("3x3", &bitmap_lf);
473 old_hfont = SelectObject(hdc, hfont);
474 test_font_metrics(hdc, hfont, bitmap_lf.lfHeight, 0, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 3, 3);
475 SelectObject(hdc, old_hfont);
476 DeleteObject(hfont);
478 DeleteDC(hdc);
481 /* Test how GDI scales outline font metrics */
482 static void test_outline_font(void)
484 static const char test_str[11] = "Test String";
485 HDC hdc, hdc_2;
486 LOGFONTA lf;
487 HFONT hfont, old_hfont, old_hfont_2;
488 OUTLINETEXTMETRICA otm;
489 SIZE size_orig;
490 INT width_orig, height_orig, lfWidth;
491 XFORM xform;
492 GLYPHMETRICS gm;
493 MAT2 mat2 = { {0x8000,0}, {0,0}, {0,0}, {0x8000,0} };
494 POINT pt;
495 INT ret;
497 if (!is_truetype_font_installed("Arial"))
499 skip("Arial is not installed\n");
500 return;
503 hdc = CreateCompatibleDC(0);
505 memset(&lf, 0, sizeof(lf));
506 strcpy(lf.lfFaceName, "Arial");
507 lf.lfHeight = 72;
508 hfont = create_font("outline", &lf);
509 old_hfont = SelectObject(hdc, hfont);
510 otm.otmSize = sizeof(otm);
511 ok(GetOutlineTextMetricsA(hdc, sizeof(otm), &otm), "GetTextMetricsA failed\n");
512 ok(GetTextExtentPoint32A(hdc, test_str, sizeof(test_str), &size_orig), "GetTextExtentPoint32A failed\n");
513 ok(GetCharWidthA(hdc, 'A', 'A', &width_orig), "GetCharWidthA failed\n");
515 test_font_metrics(hdc, hfont, lf.lfHeight, otm.otmTextMetrics.tmAveCharWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
516 SelectObject(hdc, old_hfont);
517 DeleteObject(hfont);
519 /* font of otmEMSquare height helps to avoid a lot of rounding errors */
520 lf.lfHeight = otm.otmEMSquare;
521 lf.lfHeight = -lf.lfHeight;
522 hfont = create_font("outline", &lf);
523 old_hfont = SelectObject(hdc, hfont);
524 otm.otmSize = sizeof(otm);
525 ok(GetOutlineTextMetricsA(hdc, sizeof(otm), &otm), "GetTextMetricsA failed\n");
526 ok(GetTextExtentPoint32A(hdc, test_str, sizeof(test_str), &size_orig), "GetTextExtentPoint32A failed\n");
527 ok(GetCharWidthA(hdc, 'A', 'A', &width_orig), "GetCharWidthA failed\n");
528 SelectObject(hdc, old_hfont);
529 DeleteObject(hfont);
531 height_orig = otm.otmTextMetrics.tmHeight;
532 lfWidth = otm.otmTextMetrics.tmAveCharWidth;
534 /* test integer scaling 3x2 */
535 lf.lfHeight = height_orig * 2;
536 lf.lfWidth = lfWidth * 3;
537 hfont = create_font("3x2", &lf);
538 old_hfont = SelectObject(hdc, hfont);
539 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 3, 2);
540 SelectObject(hdc, old_hfont);
541 DeleteObject(hfont);
543 /* test integer scaling 3x3 */
544 lf.lfHeight = height_orig * 3;
545 lf.lfWidth = lfWidth * 3;
546 hfont = create_font("3x3", &lf);
547 old_hfont = SelectObject(hdc, hfont);
548 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 3, 3);
549 SelectObject(hdc, old_hfont);
550 DeleteObject(hfont);
552 /* test integer scaling 1x1 */
553 lf.lfHeight = height_orig * 1;
554 lf.lfWidth = lfWidth * 1;
555 hfont = create_font("1x1", &lf);
556 old_hfont = SelectObject(hdc, hfont);
557 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
558 SelectObject(hdc, old_hfont);
559 DeleteObject(hfont);
561 /* test integer scaling 1x1 */
562 lf.lfHeight = height_orig;
563 lf.lfWidth = 0;
564 hfont = create_font("1x1", &lf);
565 old_hfont = SelectObject(hdc, hfont);
566 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
568 /* with an identity matrix */
569 memset(&gm, 0, sizeof(gm));
570 SetLastError(0xdeadbeef);
571 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
572 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
573 ok(gm.gmCellIncX == width_orig, "incX %d != %d\n", gm.gmCellIncX, width_orig);
574 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
575 /* with a custom matrix */
576 memset(&gm, 0, sizeof(gm));
577 SetLastError(0xdeadbeef);
578 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat2);
579 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
580 ok(gm.gmCellIncX == width_orig/2, "incX %d != %d\n", gm.gmCellIncX, width_orig/2);
581 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
583 /* Test that changing the DC transformation affects only the font
584 * selected on this DC and doesn't affect the same font selected on
585 * another DC.
587 hdc_2 = CreateCompatibleDC(0);
588 old_hfont_2 = SelectObject(hdc_2, hfont);
589 test_font_metrics(hdc_2, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
591 SetMapMode(hdc, MM_ANISOTROPIC);
593 /* font metrics on another DC should be unchanged */
594 test_font_metrics(hdc_2, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
596 /* test restrictions of compatibility mode GM_COMPATIBLE */
597 /* part 1: rescaling only X should not change font scaling on screen.
598 So compressing the X axis by 2 is not done, and this
599 appears as X scaling of 2 that no one requested. */
600 SetWindowExtEx(hdc, 100, 100, NULL);
601 SetViewportExtEx(hdc, 50, 100, NULL);
602 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 2, 1);
603 /* font metrics on another DC should be unchanged */
604 test_font_metrics(hdc_2, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
606 /* part 2: rescaling only Y should change font scaling.
607 As also X is scaled by a factor of 2, but this is not
608 requested by the DC transformation, we get a scaling factor
609 of 2 in the X coordinate. */
610 SetViewportExtEx(hdc, 100, 200, NULL);
611 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 2, 1);
612 /* font metrics on another DC should be unchanged */
613 test_font_metrics(hdc_2, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
615 /* restore scaling */
616 SetMapMode(hdc, MM_TEXT);
618 /* font metrics on another DC should be unchanged */
619 test_font_metrics(hdc_2, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
621 SelectObject(hdc_2, old_hfont_2);
622 DeleteDC(hdc_2);
624 if (!SetGraphicsMode(hdc, GM_ADVANCED))
626 SelectObject(hdc, old_hfont);
627 DeleteObject(hfont);
628 DeleteDC(hdc);
629 skip("GM_ADVANCED is not supported on this platform\n");
630 return;
633 xform.eM11 = 20.0f;
634 xform.eM12 = 0.0f;
635 xform.eM21 = 0.0f;
636 xform.eM22 = 20.0f;
637 xform.eDx = 0.0f;
638 xform.eDy = 0.0f;
640 SetLastError(0xdeadbeef);
641 ret = SetWorldTransform(hdc, &xform);
642 ok(ret, "SetWorldTransform error %u\n", GetLastError());
644 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
646 /* with an identity matrix */
647 memset(&gm, 0, sizeof(gm));
648 SetLastError(0xdeadbeef);
649 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
650 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
651 pt.x = width_orig; pt.y = 0;
652 LPtoDP(hdc, &pt, 1);
653 ok(gm.gmCellIncX == pt.x, "incX %d != %d\n", gm.gmCellIncX, pt.x);
654 ok(gm.gmCellIncX == 20 * width_orig, "incX %d != %d\n", gm.gmCellIncX, 20 * width_orig);
655 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
656 /* with a custom matrix */
657 memset(&gm, 0, sizeof(gm));
658 SetLastError(0xdeadbeef);
659 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat2);
660 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
661 pt.x = width_orig; pt.y = 0;
662 LPtoDP(hdc, &pt, 1);
663 ok(gm.gmCellIncX == pt.x/2, "incX %d != %d\n", gm.gmCellIncX, pt.x/2);
664 ok(near_match(gm.gmCellIncX, 10 * width_orig), "incX %d != %d\n", gm.gmCellIncX, 10 * width_orig);
665 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
667 SetLastError(0xdeadbeef);
668 ret = SetMapMode(hdc, MM_LOMETRIC);
669 ok(ret == MM_TEXT, "expected MM_TEXT, got %d, error %u\n", ret, GetLastError());
671 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
673 /* with an identity matrix */
674 memset(&gm, 0, sizeof(gm));
675 SetLastError(0xdeadbeef);
676 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
677 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
678 pt.x = width_orig; pt.y = 0;
679 LPtoDP(hdc, &pt, 1);
680 ok(near_match(gm.gmCellIncX, pt.x), "incX %d != %d\n", gm.gmCellIncX, pt.x);
681 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
682 /* with a custom matrix */
683 memset(&gm, 0, sizeof(gm));
684 SetLastError(0xdeadbeef);
685 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat2);
686 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
687 pt.x = width_orig; pt.y = 0;
688 LPtoDP(hdc, &pt, 1);
689 ok(near_match(gm.gmCellIncX, (pt.x + 1)/2), "incX %d != %d\n", gm.gmCellIncX, (pt.x + 1)/2);
690 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
692 SetLastError(0xdeadbeef);
693 ret = SetMapMode(hdc, MM_TEXT);
694 ok(ret == MM_LOMETRIC, "expected MM_LOMETRIC, got %d, error %u\n", ret, GetLastError());
696 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
698 /* with an identity matrix */
699 memset(&gm, 0, sizeof(gm));
700 SetLastError(0xdeadbeef);
701 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
702 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
703 pt.x = width_orig; pt.y = 0;
704 LPtoDP(hdc, &pt, 1);
705 ok(gm.gmCellIncX == pt.x, "incX %d != %d\n", gm.gmCellIncX, pt.x);
706 ok(gm.gmCellIncX == 20 * width_orig, "incX %d != %d\n", gm.gmCellIncX, 20 * width_orig);
707 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
708 /* with a custom matrix */
709 memset(&gm, 0, sizeof(gm));
710 SetLastError(0xdeadbeef);
711 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat2);
712 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
713 pt.x = width_orig; pt.y = 0;
714 LPtoDP(hdc, &pt, 1);
715 ok(gm.gmCellIncX == pt.x/2, "incX %d != %d\n", gm.gmCellIncX, pt.x/2);
716 ok(gm.gmCellIncX == 10 * width_orig, "incX %d != %d\n", gm.gmCellIncX, 10 * width_orig);
717 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
719 SelectObject(hdc, old_hfont);
720 DeleteObject(hfont);
721 DeleteDC(hdc);
724 static INT CALLBACK find_font_proc(const LOGFONTA *elf, const TEXTMETRICA *ntm, DWORD type, LPARAM lParam)
726 LOGFONTA *lf = (LOGFONTA *)lParam;
728 if (elf->lfHeight == lf->lfHeight && !strcmp(elf->lfFaceName, lf->lfFaceName))
730 *lf = *elf;
731 return 0; /* stop enumeration */
733 return 1; /* continue enumeration */
736 static BOOL is_CJK(void)
738 return (system_lang_id == LANG_CHINESE || system_lang_id == LANG_JAPANESE || system_lang_id == LANG_KOREAN);
741 #define FH_SCALE 0x80000000
742 static void test_bitmap_font_metrics(void)
744 static const WORD skip_rtl[] = {LANG_ARABIC, LANG_HEBREW, 0};
745 static const struct font_data
747 const char face_name[LF_FACESIZE];
748 int weight, height, ascent, descent, int_leading, ext_leading;
749 int ave_char_width, max_char_width, dpi;
750 BYTE first_char, last_char, def_char, break_char;
751 DWORD ansi_bitfield;
752 const WORD *skip_lang_id;
753 int scaled_height;
754 } fd[] =
756 { "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 },
757 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 6, 11, 2, 2, 0, 5, 11, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC, 0, 13 },
758 { "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 },
759 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 8, 11, 2, 2, 0, 5, 11, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC, 0, 13 },
760 { "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 },
761 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 10, 11, 2, 2, 0, 5, 11, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC, 0, 13 },
762 { "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 },
763 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 14, 11, 2, 2, 0, 5, 11, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC, 0, 13 },
764 { "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 },
765 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 18, 13, 3, 3, 0, 7, 14, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC, 0, 16 },
767 { "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 },
768 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 6, 13, 3, 3, 0, 7, 14, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC, 0, 16 },
769 { "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 },
770 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 8, 13, 3, 3, 0, 7, 14, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC, 0, 16 },
771 { "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 },
772 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 10, 13, 3, 3, 0, 7, 14, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC, 0, 16 },
773 { "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 },
774 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 14, 13, 3, 3, 0, 7, 14, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC, 0, 16 },
775 { "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 },
776 { "MS Sans Serif", FW_NORMAL, FH_SCALE | 18, 13, 3, 3, 0, 7, 14, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC, 0, 16 },
778 { "MS Sans Serif", FW_NORMAL, 13, 11, 2, 2, 0, 5, 11, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2 },
779 { "MS Sans Serif", FW_NORMAL, 13, 11, 2, 2, 0, 5, 11, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
780 { "MS Sans Serif", FW_NORMAL, 16, 13, 3, 3, 0, 7, 14, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2 },
781 { "MS Sans Serif", FW_NORMAL, 16, 13, 3, 3, 0, 7, 14, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
782 { "MS Sans Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 16, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 },
783 { "MS Sans Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 18, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN2 },
784 { "MS Sans Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 16, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
785 { "MS Sans Serif", FW_NORMAL, 24, 19, 5, 6, 0, 9, 19, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 },
786 { "MS Sans Serif", FW_NORMAL, 24, 19, 5, 6, 0, 9, 24, 96, 0x20, 0xff, 0x81, 0x40, FS_LATIN2 },
787 { "MS Sans Serif", FW_NORMAL, 24, 19, 5, 6, 0, 9, 20, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
788 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 5, 0, 12, 24, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 },
789 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 6, 0, 12, 24, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN2 },
790 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 5, 0, 12, 25, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
791 { "MS Sans Serif", FW_NORMAL, 37, 29, 8, 5, 0, 16, 32, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 },
792 { "MS Sans Serif", FW_NORMAL, 37, 29, 8, 5, 0, 16, 32, 96, 0x20, 0xff, 0x81, 0x40, FS_LATIN2 },
793 { "MS Sans Serif", FW_NORMAL, 37, 29, 8, 5, 0, 16, 32, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
795 { "MS Sans Serif", FW_NORMAL, 16, 13, 3, 3, 0, 7, 14, 120, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2 },
796 { "MS Sans Serif", FW_NORMAL, 16, 13, 3, 3, 0, 7, 14, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
797 { "MS Sans Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 18, 120, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2 },
798 { "MS Sans Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 17, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
799 { "MS Sans Serif", FW_NORMAL, 25, 20, 5, 5, 0, 10, 21, 120, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2 },
800 { "MS Sans Serif", FW_NORMAL, 25, 20, 5, 5, 0, 10, 21, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
801 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 6, 0, 12, 24, 120, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2 },
802 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 5, 0, 12, 24, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
803 { "MS Sans Serif", FW_NORMAL, 36, 29, 7, 6, 0, 15, 30, 120, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2 },
804 { "MS Sans Serif", FW_NORMAL, 36, 29, 7, 6, 0, 15, 30, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
805 { "MS Sans Serif", FW_NORMAL, 46, 37, 9, 6, 0, 20, 40, 120, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2 },
806 { "MS Sans Serif", FW_NORMAL, 46, 37, 9, 6, 0, 20, 40, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
808 { "MS Serif", FW_NORMAL, 10, 8, 2, 2, 0, 4, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 },
809 { "MS Serif", FW_NORMAL, 10, 8, 2, 2, 0, 5, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
810 { "MS Serif", FW_NORMAL, 11, 9, 2, 2, 0, 5, 9, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
811 { "MS Serif", FW_NORMAL, 13, 11, 2, 2, 0, 5, 11, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 },
812 { "MS Serif", FW_NORMAL, 13, 11, 2, 2, 0, 5, 12, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
813 { "MS Serif", FW_NORMAL, 16, 13, 3, 3, 0, 6, 14, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 },
814 { "MS Serif", FW_NORMAL, 16, 13, 3, 3, 0, 6, 16, 96, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
815 { "MS Serif", FW_NORMAL, 19, 15, 4, 3, 0, 8, 18, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 },
816 { "MS Serif", FW_NORMAL, 19, 15, 4, 3, 0, 8, 19, 96, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
817 { "MS Serif", FW_NORMAL, 21, 16, 5, 3, 0, 9, 17, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 },
818 { "MS Serif", FW_NORMAL, 21, 16, 5, 3, 0, 9, 22, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 },
819 { "MS Serif", FW_NORMAL, 21, 16, 5, 3, 0, 9, 23, 96, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
820 { "MS Serif", FW_NORMAL, 27, 21, 6, 3, 0, 12, 23, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 },
821 { "MS Serif", FW_NORMAL, 27, 21, 6, 3, 0, 12, 26, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 },
822 { "MS Serif", FW_NORMAL, 27, 21, 6, 3, 0, 12, 27, 96, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
823 { "MS Serif", FW_NORMAL, 35, 27, 8, 3, 0, 16, 33, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 },
824 { "MS Serif", FW_NORMAL, 35, 27, 8, 3, 0, 16, 34, 96, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
826 { "MS Serif", FW_NORMAL, 16, 13, 3, 3, 0, 6, 14, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_CYRILLIC },
827 { "MS Serif", FW_NORMAL, 16, 13, 3, 3, 0, 6, 13, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 },
828 { "MS Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 18, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_CYRILLIC },
829 { "MS Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 15, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 },
830 { "MS Serif", FW_NORMAL, 23, 18, 5, 3, 0, 10, 21, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_CYRILLIC },
831 { "MS Serif", FW_NORMAL, 23, 18, 5, 3, 0, 10, 19, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 },
832 { "MS Serif", FW_NORMAL, 27, 21, 6, 4, 0, 12, 23, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 },
833 { "MS Serif", FW_MEDIUM, 27, 22, 5, 2, 0, 12, 30, 120, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
834 { "MS Serif", FW_NORMAL, 33, 26, 7, 3, 0, 14, 30, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 },
835 { "MS Serif", FW_MEDIUM, 32, 25, 7, 2, 0, 14, 32, 120, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
836 { "MS Serif", FW_NORMAL, 43, 34, 9, 3, 0, 19, 39, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
838 { "Courier", FW_NORMAL, 13, 11, 2, 0, 0, 8, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
839 { "Courier", FW_NORMAL, 16, 13, 3, 0, 0, 9, 9, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
840 { "Courier", FW_NORMAL, 20, 16, 4, 0, 0, 12, 12, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
842 { "Courier", FW_NORMAL, 16, 13, 3, 0, 0, 9, 9, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
843 { "Courier", FW_NORMAL, 20, 16, 4, 0, 0, 12, 12, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
844 { "Courier", FW_NORMAL, 25, 20, 5, 0, 0, 15, 15, 120, 0x20, 0xff, 0x40, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
846 { "System", FW_BOLD, 16, 13, 3, 3, 0, 7, 14, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 },
847 { "System", FW_BOLD, 16, 13, 3, 3, 0, 7, 15, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
848 { "System", FW_NORMAL, 18, 16, 2, 0, 2, 8, 16, 96, 0x20, 0xff, 0x80, 0x20, FS_JISJAPAN },
850 { "System", FW_BOLD, 20, 16, 4, 4, 0, 9, 14, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 },
851 { "System", FW_BOLD, 20, 16, 4, 4, 0, 9, 17, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
853 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 1, 2, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 },
854 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 1, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
855 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 2, 4, 96, 0x20, 0xff, 0x80, 0x20, FS_JISJAPAN },
856 { "Small Fonts", FW_NORMAL, 5, 4, 1, 1, 0, 3, 4, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1, skip_rtl},
857 { "Small Fonts", FW_NORMAL, 5, 4, 1, 1, 0, 2, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
858 { "Small Fonts", FW_NORMAL, 5, 4, 1, 0, 0, 3, 6, 96, 0x20, 0xff, 0x80, 0x20, FS_JISJAPAN },
859 { "Small Fonts", FW_NORMAL, 6, 5, 1, 1, 0, 3, 13, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1, skip_rtl},
860 { "Small Fonts", FW_NORMAL, 6, 5, 1, 1, 0, 3, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
861 { "Small Fonts", FW_NORMAL, 6, 5, 1, 1, 0, 3, 8, 96, 0x00, 0xff, 0x60, 0x00, FS_ARABIC },
862 { "Small Fonts", FW_NORMAL, 6, 5, 1, 0, 0, 4, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_JISJAPAN },
863 { "Small Fonts", FW_NORMAL, 8, 7, 1, 1, 0, 4, 7, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1, skip_rtl},
864 { "Small Fonts", FW_NORMAL, 8, 7, 1, 1, 0, 4, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
865 { "Small Fonts", FW_NORMAL, 8, 7, 1, 1, 0, 4, 8, 96, 0x00, 0xff, 0x60, 0x00, FS_ARABIC },
866 { "Small Fonts", FW_NORMAL, 8, 7, 1, 0, 0, 5, 10, 96, 0x20, 0xff, 0x80, 0x20, FS_JISJAPAN },
867 { "Small Fonts", FW_NORMAL, 10, 8, 2, 2, 0, 4, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2, skip_rtl},
868 { "Small Fonts", FW_NORMAL, 10, 8, 2, 2, 0, 5, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
869 { "Small Fonts", FW_NORMAL, 10, 8, 2, 2, 0, 4, 9, 96, 0x00, 0xff, 0x60, 0x00, FS_ARABIC },
870 { "Small Fonts", FW_NORMAL, 10, 8, 2, 0, 0, 6, 12, 96, 0x20, 0xff, 0x80, 0x20, FS_JISJAPAN },
871 { "Small Fonts", FW_NORMAL, 11, 9, 2, 2, 0, 5, 9, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC, skip_rtl},
872 { "Small Fonts", FW_NORMAL, 11, 9, 2, 2, 0, 4, 10, 96, 0x00, 0xff, 0x60, 0x00, FS_ARABIC },
873 { "Small Fonts", FW_NORMAL, 11, 9, 2, 0, 0, 7, 14, 96, 0x20, 0xff, 0x80, 0x20, FS_JISJAPAN },
875 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 1, 2, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_JISJAPAN },
876 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 1, 8, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
877 { "Small Fonts", FW_NORMAL, 6, 5, 1, 1, 0, 3, 5, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_JISJAPAN },
878 { "Small Fonts", FW_NORMAL, 6, 5, 1, 1, 0, 3, 8, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
879 { "Small Fonts", FW_NORMAL, 8, 7, 1, 1, 0, 4, 7, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_JISJAPAN },
880 { "Small Fonts", FW_NORMAL, 8, 7, 1, 1, 0, 4, 8, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
881 { "Small Fonts", FW_NORMAL, 10, 8, 2, 2, 0, 5, 9, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_JISJAPAN },
882 { "Small Fonts", FW_NORMAL, 10, 8, 2, 2, 0, 5, 8, 120, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
883 { "Small Fonts", FW_NORMAL, 12, 10, 2, 2, 0, 5, 10, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_JISJAPAN },
884 { "Small Fonts", FW_NORMAL, 12, 10, 2, 2, 0, 6, 10, 120, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
885 { "Small Fonts", FW_NORMAL, 13, 11, 2, 2, 0, 6, 12, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_JISJAPAN },
886 { "Small Fonts", FW_NORMAL, 13, 11, 2, 2, 0, 6, 11, 120, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
888 { "Fixedsys", FW_NORMAL, 15, 12, 3, 3, 0, 8, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 },
889 { "Fixedsys", FW_NORMAL, 16, 12, 4, 3, 0, 8, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
890 { "FixedSys", FW_NORMAL, 18, 16, 2, 0, 0, 8, 16, 96, 0x20, 0xff, 0xa0, 0x20, FS_JISJAPAN },
892 { "Fixedsys", FW_NORMAL, 20, 16, 4, 2, 0, 10, 10, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC }
894 /* FIXME: add "Terminal" */
896 static const int font_log_pixels[] = { 96, 120 };
897 HDC hdc;
898 LOGFONTA lf;
899 HFONT hfont, old_hfont;
900 TEXTMETRICA tm;
901 INT ret, i, expected_cs, screen_log_pixels, diff, font_res;
902 char face_name[LF_FACESIZE];
903 CHARSETINFO csi;
905 trace("system language id %04x\n", system_lang_id);
907 expected_cs = GetACP();
908 if (!TranslateCharsetInfo(ULongToPtr(expected_cs), &csi, TCI_SRCCODEPAGE))
910 skip("TranslateCharsetInfo failed for code page %d\n", expected_cs);
911 return;
913 expected_cs = csi.ciCharset;
914 trace("ACP %d -> charset %d\n", GetACP(), expected_cs);
916 hdc = CreateCompatibleDC(0);
917 ok(hdc != NULL, "failed to create hdc\n");
919 trace("logpixelsX %d, logpixelsY %d\n", GetDeviceCaps(hdc, LOGPIXELSX),
920 GetDeviceCaps(hdc, LOGPIXELSY));
922 screen_log_pixels = GetDeviceCaps(hdc, LOGPIXELSY);
923 diff = 32768;
924 font_res = 0;
925 for (i = 0; i < ARRAY_SIZE(font_log_pixels); i++)
927 int new_diff = abs(font_log_pixels[i] - screen_log_pixels);
928 if (new_diff < diff)
930 diff = new_diff;
931 font_res = font_log_pixels[i];
934 trace("best font resolution is %d\n", font_res);
936 for (i = 0; i < ARRAY_SIZE(fd); i++)
938 int bit, height;
940 memset(&lf, 0, sizeof(lf));
942 height = fd[i].height & ~FH_SCALE;
943 lf.lfHeight = height;
944 strcpy(lf.lfFaceName, fd[i].face_name);
946 for(bit = 0; bit < 32; bit++)
948 GLYPHMETRICS gm;
949 DWORD fs[2];
950 BOOL bRet;
952 fs[0] = 1L << bit;
953 fs[1] = 0;
954 if((fd[i].ansi_bitfield & fs[0]) == 0) continue;
955 if(!TranslateCharsetInfo( fs, &csi, TCI_SRCFONTSIG )) continue;
957 lf.lfCharSet = csi.ciCharset;
958 ret = EnumFontFamiliesExA(hdc, &lf, find_font_proc, (LPARAM)&lf, 0);
959 if (fd[i].height & FH_SCALE)
960 ok(ret, "scaled font height %d should not be enumerated\n", height);
961 else
963 if (font_res == fd[i].dpi && lf.lfCharSet == expected_cs)
965 todo_wine_if (ret) /* FIXME: Remove once Wine is fixed */
966 ok(!ret, "%s height %d charset %d dpi %d should be enumerated\n", lf.lfFaceName, lf.lfHeight, lf.lfCharSet, fd[i].dpi);
969 if (ret && !(fd[i].height & FH_SCALE))
970 continue;
972 hfont = create_font(lf.lfFaceName, &lf);
973 old_hfont = SelectObject(hdc, hfont);
975 SetLastError(0xdeadbeef);
976 ret = GetTextFaceA(hdc, sizeof(face_name), face_name);
977 ok(ret, "GetTextFace error %u\n", GetLastError());
979 if (strcmp(face_name, fd[i].face_name) != 0)
981 ok(ret != ANSI_CHARSET, "font charset should not be ANSI_CHARSET\n");
982 ok(ret != expected_cs, "font charset %d should not be %d\n", ret, expected_cs);
983 SelectObject(hdc, old_hfont);
984 DeleteObject(hfont);
985 continue;
988 memset(&gm, 0, sizeof(gm));
989 SetLastError(0xdeadbeef);
990 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
991 todo_wine
992 ok(ret == GDI_ERROR, "GetGlyphOutline should fail for a bitmap font\n");
993 ret = GetLastError();
994 ok(ret == ERROR_CAN_NOT_COMPLETE || ret == 0xdeadbeef /* Win10 */, "Unexpected error %d.\n", ret);
996 bRet = GetTextMetricsA(hdc, &tm);
997 ok(bRet, "GetTextMetrics error %d\n", GetLastError());
999 SetLastError(0xdeadbeef);
1000 ret = GetTextCharset(hdc);
1001 if (is_CJK() && lf.lfCharSet == ANSI_CHARSET)
1002 ok(ret == ANSI_CHARSET, "got charset %d, expected ANSI_CHARSETd\n", ret);
1003 else
1004 ok(ret == expected_cs, "got charset %d, expected %d\n", ret, expected_cs);
1006 if(fd[i].dpi == tm.tmDigitizedAspectX)
1008 int skipme = 0;
1009 if (fd[i].skip_lang_id)
1011 int si = 0;
1012 skipme = 0;
1013 while(!skipme && fd[i].skip_lang_id[si])
1014 if (fd[i].skip_lang_id[si++] == system_lang_id)
1015 skipme = 1;
1017 if (!skipme)
1019 ok(tm.tmWeight == fd[i].weight, "%s(%d): tm.tmWeight %d != %d\n", fd[i].face_name, height, tm.tmWeight, fd[i].weight);
1020 if (fd[i].height & FH_SCALE)
1021 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);
1022 else
1023 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);
1024 ok(tm.tmAscent == fd[i].ascent, "%s(%d): tm.tmAscent %d != %d\n", fd[i].face_name, height, tm.tmAscent, fd[i].ascent);
1025 ok(tm.tmDescent == fd[i].descent, "%s(%d): tm.tmDescent %d != %d\n", fd[i].face_name, height, tm.tmDescent, fd[i].descent);
1026 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);
1027 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);
1028 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);
1029 ok(tm.tmFirstChar == fd[i].first_char, "%s(%d): tm.tmFirstChar = %02x\n", fd[i].face_name, height, tm.tmFirstChar);
1030 ok(tm.tmLastChar == fd[i].last_char, "%s(%d): tm.tmLastChar = %02x\n", fd[i].face_name, height, tm.tmLastChar);
1031 /* Substitutions like MS Sans Serif,0=MS Sans Serif,204
1032 make default char test fail */
1033 if (tm.tmCharSet == lf.lfCharSet)
1034 ok(tm.tmDefaultChar == fd[i].def_char, "%s(%d): tm.tmDefaultChar = %02x\n", fd[i].face_name, height, tm.tmDefaultChar);
1035 ok(tm.tmBreakChar == fd[i].break_char, "%s(%d): tm.tmBreakChar = %02x\n", fd[i].face_name, height, tm.tmBreakChar);
1036 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);
1038 /* Don't run the max char width test on System/ANSI_CHARSET. We have extra characters in our font
1039 that make the max width bigger */
1040 if ((strcmp(lf.lfFaceName, "System") || lf.lfCharSet != ANSI_CHARSET) && tm.tmDigitizedAspectX == 96)
1041 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);
1043 else
1044 skip("Skipping font metrics test for system langid 0x%x\n",
1045 system_lang_id);
1047 SelectObject(hdc, old_hfont);
1048 DeleteObject(hfont);
1052 DeleteDC(hdc);
1055 static void test_GdiGetCharDimensions(void)
1057 HDC hdc;
1058 TEXTMETRICW tm;
1059 LONG ret;
1060 SIZE size;
1061 LONG avgwidth, height;
1062 static const char szAlphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
1064 if (!pGdiGetCharDimensions)
1066 win_skip("GdiGetCharDimensions not available on this platform\n");
1067 return;
1070 hdc = CreateCompatibleDC(NULL);
1072 GetTextExtentPointA(hdc, szAlphabet, strlen(szAlphabet), &size);
1073 avgwidth = ((size.cx / 26) + 1) / 2;
1075 ret = pGdiGetCharDimensions(hdc, &tm, &height);
1076 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
1077 ok(height == tm.tmHeight, "GdiGetCharDimensions should have set height to %d instead of %d\n", tm.tmHeight, height);
1079 ret = pGdiGetCharDimensions(hdc, &tm, NULL);
1080 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
1082 ret = pGdiGetCharDimensions(hdc, NULL, NULL);
1083 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
1085 height = 0;
1086 ret = pGdiGetCharDimensions(hdc, NULL, &height);
1087 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
1088 ok(height == size.cy, "GdiGetCharDimensions should have set height to %d instead of %d\n", size.cy, height);
1090 DeleteDC(hdc);
1093 static int CALLBACK create_font_proc(const LOGFONTA *lpelfe,
1094 const TEXTMETRICA *lpntme,
1095 DWORD FontType, LPARAM lParam)
1097 if (FontType & TRUETYPE_FONTTYPE)
1099 HFONT hfont;
1101 hfont = CreateFontIndirectA(lpelfe);
1102 if (hfont)
1104 *(HFONT *)lParam = hfont;
1105 return 0;
1109 return 1;
1112 static void ABCWidths_helper(const char* description, HDC hdc, WORD *glyphs, const ABC *base_abci, const ABC *base_abcw, const ABCFLOAT *base_abcf)
1114 ABC abc[1];
1115 ABCFLOAT abcf[1];
1116 BOOL ret = FALSE;
1118 ret = GetCharABCWidthsI(hdc, 0, 1, glyphs, abc);
1119 ok(ret, "%s: GetCharABCWidthsI should have succeeded\n", description);
1120 ok ((INT)abc->abcB > 0, "%s: abcB should be positive\n", description);
1121 ok(abc->abcA * base_abci->abcA >= 0, "%s: abcA's sign should be unchanged\n", description);
1122 ok(abc->abcC * base_abci->abcC >= 0, "%s: abcC's sign should be unchanged\n", description);
1124 ret = GetCharABCWidthsW(hdc, 'i', 'i', abc);
1125 ok(ret, "%s: GetCharABCWidthsW should have succeeded\n", description);
1126 ok ((INT)abc->abcB > 0, "%s: abcB should be positive\n", description);
1127 ok(abc->abcA * base_abcw->abcA >= 0, "%s: abcA's sign should be unchanged\n", description);
1128 ok(abc->abcC * base_abcw->abcC >= 0, "%s: abcC's sign should be unchanged\n", description);
1130 ret = pGetCharABCWidthsFloatW(hdc, 'i', 'i', abcf);
1131 ok(ret, "%s: GetCharABCWidthsFloatW should have succeeded\n", description);
1132 ok (abcf->abcfB > 0.0, "%s: abcfB should be positive\n", description);
1133 ok(abcf->abcfA * base_abcf->abcfA >= 0.0, "%s: abcfA's sign should be unchanged\n", description);
1134 ok(abcf->abcfC * base_abcf->abcfC >= 0.0, "%s: abcfC's sign should be unchanged\n", description);
1137 static void test_GetCharABCWidths(void)
1139 BOOL ret;
1140 HDC hdc;
1141 LOGFONTA lf;
1142 HFONT hfont;
1143 ABC abc[1];
1144 ABC abcw[1];
1145 ABCFLOAT abcf[1];
1146 WORD glyphs[1];
1147 DWORD nb;
1148 HWND hwnd;
1149 static const struct
1151 UINT first;
1152 UINT last;
1153 } range[] =
1155 {0xff, 0xff},
1156 {0x100, 0x100},
1157 {0xff, 0x100},
1158 {0x1ff, 0xff00},
1159 {0xffff, 0xffff},
1160 {0x10000, 0x10000},
1161 {0xffff, 0x10000},
1162 {0xffffff, 0xffffff},
1163 {0x1000000, 0x1000000},
1164 {0xffffff, 0x1000000},
1165 {0xffffffff, 0xffffffff},
1166 {0x00, 0xff}
1168 static const struct
1170 UINT cs;
1171 UINT a;
1172 UINT w;
1173 BOOL r[ARRAY_SIZE(range)];
1174 } c[] =
1176 {ANSI_CHARSET, 0x30, 0x30,
1177 {TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE}},
1178 {SHIFTJIS_CHARSET, 0x82a0, 0x3042,
1179 {TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE}},
1180 {HANGEUL_CHARSET, 0x8141, 0xac02,
1181 {TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE}},
1182 {GB2312_CHARSET, 0x8141, 0x4e04,
1183 {TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE}},
1184 {CHINESEBIG5_CHARSET, 0xa142, 0x3001,
1185 {TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE}}
1187 UINT i;
1189 if (!pGetCharABCWidthsFloatW)
1191 win_skip("GetCharABCWidthsFloatW is not available on this platform\n");
1192 return;
1195 memset(&lf, 0, sizeof(lf));
1196 strcpy(lf.lfFaceName, "System");
1197 lf.lfHeight = 20;
1199 hfont = CreateFontIndirectA(&lf);
1200 hdc = GetDC(0);
1201 hfont = SelectObject(hdc, hfont);
1203 nb = pGetGlyphIndicesW(hdc, L"i", 1, glyphs, 0);
1204 ok(nb == 1, "GetGlyphIndicesW should have returned 1\n");
1206 ret = GetCharABCWidthsI(NULL, 0, 1, glyphs, abc);
1207 ok(!ret, "GetCharABCWidthsI should have failed\n");
1209 ret = GetCharABCWidthsI(hdc, 0, 1, glyphs, NULL);
1210 ok(!ret, "GetCharABCWidthsI should have failed\n");
1212 ret = GetCharABCWidthsI(hdc, 0, 1, glyphs, abc);
1213 ok(ret, "GetCharABCWidthsI should have succeeded\n");
1215 ret = GetCharABCWidthsW(NULL, 'a', 'a', abc);
1216 ok(!ret, "GetCharABCWidthsW should have failed\n");
1218 ret = GetCharABCWidthsW(hdc, 'a', 'a', NULL);
1219 ok(!ret, "GetCharABCWidthsW should have failed\n");
1221 ret = GetCharABCWidthsW(hdc, 'a', 'a', abc);
1222 ok(!ret, "GetCharABCWidthsW should have failed\n");
1224 ret = pGetCharABCWidthsFloatW(NULL, 'a', 'a', abcf);
1225 ok(!ret, "GetCharABCWidthsFloatW should have failed\n");
1227 ret = pGetCharABCWidthsFloatW(hdc, 'a', 'a', NULL);
1228 ok(!ret, "GetCharABCWidthsFloatW should have failed\n");
1230 ret = pGetCharABCWidthsFloatW(hdc, 'a', 'a', abcf);
1231 ok(ret, "GetCharABCWidthsFloatW should have succeeded\n");
1233 hfont = SelectObject(hdc, hfont);
1234 DeleteObject(hfont);
1236 for (i = 0; i < ARRAY_SIZE(c); ++i)
1238 ABC a[2], w[2];
1239 ABC full[256];
1240 UINT code = 0x41, j;
1242 lf.lfFaceName[0] = '\0';
1243 lf.lfCharSet = c[i].cs;
1244 lf.lfPitchAndFamily = 0;
1245 if (EnumFontFamiliesExA(hdc, &lf, create_font_proc, (LPARAM)&hfont, 0))
1247 skip("TrueType font for charset %u is not installed\n", c[i].cs);
1248 continue;
1251 memset(a, 0, sizeof a);
1252 memset(w, 0, sizeof w);
1253 hfont = SelectObject(hdc, hfont);
1254 ok(GetCharABCWidthsA(hdc, c[i].a, c[i].a + 1, a) && GetCharABCWidthsW(hdc, c[i].w, c[i].w + 1, w)
1255 && !memcmp(a, w, sizeof(a)),
1256 "GetCharABCWidthsA and GetCharABCWidthsW should return same widths. charset = %u\n", c[i].cs);
1258 memset(a, 0xbb, sizeof a);
1259 ret = GetCharABCWidthsA(hdc, code, code, a);
1260 ok(ret, "GetCharABCWidthsA should have succeeded\n");
1261 memset(full, 0xcc, sizeof full);
1262 ret = GetCharABCWidthsA(hdc, 0x00, code, full);
1263 ok(ret, "GetCharABCWidthsA should have succeeded\n");
1264 ok(memcmp(&a[0], &full[code], sizeof(ABC)) == 0,
1265 "GetCharABCWidthsA info should match. codepage = %u\n", c[i].cs);
1267 for (j = 0; j < ARRAY_SIZE(range); ++j)
1269 memset(full, 0xdd, sizeof full);
1270 ret = GetCharABCWidthsA(hdc, range[j].first, range[j].last, full);
1271 ok(ret == c[i].r[j], "GetCharABCWidthsA %x - %x should have %s\n",
1272 range[j].first, range[j].last, c[i].r[j] ? "succeeded" : "failed");
1273 if (ret)
1275 UINT last = range[j].last - range[j].first;
1276 ret = GetCharABCWidthsA(hdc, range[j].last, range[j].last, a);
1277 ok(ret && memcmp(&full[last], &a[0], sizeof(ABC)) == 0,
1278 "GetCharABCWidthsA %x should match. codepage = %u\n",
1279 range[j].last, c[i].cs);
1283 hfont = SelectObject(hdc, hfont);
1284 DeleteObject(hfont);
1287 memset(&lf, 0, sizeof(lf));
1288 strcpy(lf.lfFaceName, "Tahoma");
1289 lf.lfHeight = 200;
1290 hfont = CreateFontIndirectA(&lf);
1292 /* test empty glyph's metrics */
1293 hfont = SelectObject(hdc, hfont);
1294 ret = pGetCharABCWidthsFloatW(hdc, ' ', ' ', abcf);
1295 ok(ret, "GetCharABCWidthsFloatW should have succeeded\n");
1296 ok(abcf[0].abcfB == 1.0, "got %f\n", abcf[0].abcfB);
1297 ret = GetCharABCWidthsW(hdc, ' ', ' ', abcw);
1298 ok(ret, "GetCharABCWidthsW should have succeeded\n");
1299 ok(abcw[0].abcB == 1, "got %u\n", abcw[0].abcB);
1301 /* 1) prepare unrotated font metrics */
1302 ret = GetCharABCWidthsW(hdc, 'a', 'a', abcw);
1303 ok(ret, "GetCharABCWidthsW should have succeeded\n");
1304 DeleteObject(SelectObject(hdc, hfont));
1306 /* 2) get rotated font metrics */
1307 lf.lfEscapement = lf.lfOrientation = 900;
1308 hfont = CreateFontIndirectA(&lf);
1309 hfont = SelectObject(hdc, hfont);
1310 ret = GetCharABCWidthsW(hdc, 'a', 'a', abc);
1311 ok(ret, "GetCharABCWidthsW should have succeeded\n");
1313 /* 3) compare ABC results */
1314 ok(match_off_by_1(abcw[0].abcA, abc[0].abcA, FALSE),
1315 "got %d, expected %d (A)\n", abc[0].abcA, abcw[0].abcA);
1316 ok(match_off_by_1(abcw[0].abcB, abc[0].abcB, FALSE),
1317 "got %d, expected %d (B)\n", abc[0].abcB, abcw[0].abcB);
1318 ok(match_off_by_1(abcw[0].abcC, abc[0].abcC, FALSE),
1319 "got %d, expected %d (C)\n", abc[0].abcC, abcw[0].abcC);
1321 DeleteObject(SelectObject(hdc, hfont));
1323 /* test abcA == gmptGlyphOrigin.x && abcB == gmBlackBoxX
1324 in various widths. */
1325 for (i = 1; i <= 2; i++)
1327 UINT j;
1328 UINT code;
1330 memset(&lf, 0, sizeof(lf));
1331 lf.lfHeight = 20;
1332 if (i == 1)
1334 strcpy(lf.lfFaceName, "Tahoma");
1335 code = 'a';
1337 else
1339 strcpy(lf.lfFaceName, "Times New Roman");
1340 lf.lfItalic = TRUE;
1341 code = 'f';
1343 if (!is_truetype_font_installed(lf.lfFaceName))
1345 skip("%s is not installed\n", lf.lfFaceName);
1346 continue;
1348 for (j = 1; j <= 80; j++)
1350 GLYPHMETRICS gm;
1352 lf.lfWidth = j;
1353 hfont = CreateFontIndirectA(&lf);
1354 hfont = SelectObject(hdc, hfont);
1356 nb = GetGlyphOutlineA(hdc, code, GGO_METRICS, &gm, 0, NULL, &mat);
1357 ok(nb, "GetGlyphOutlineA should have succeeded at width %d\n", i);
1359 ret = GetCharABCWidthsA(hdc, code, code, abc);
1360 ok(ret, "GetCharABCWidthsA should have succeeded at width %d\n", i);
1362 ok(abc[0].abcA == gm.gmptGlyphOrigin.x,
1363 "abcA(%d) and gmptGlyphOrigin.x(%d) values are different at width %d\n",
1364 abc[0].abcA, gm.gmptGlyphOrigin.x, i);
1365 ok(abc[0].abcB == gm.gmBlackBoxX,
1366 "abcB(%d) and gmBlackBoxX(%d) values are different at width %d\n",
1367 abc[0].abcB, gm.gmBlackBoxX, i);
1368 DeleteObject(SelectObject(hdc, hfont));
1371 ReleaseDC(NULL, hdc);
1373 /* ABC sign test for a variety of transforms */
1374 memset(&lf, 0, sizeof(lf));
1375 strcpy(lf.lfFaceName, "Tahoma");
1376 lf.lfHeight = 20;
1377 hfont = CreateFontIndirectA(&lf);
1378 hwnd = CreateWindowExA(0, "static", "", WS_POPUP, 0,0,100,100,
1379 0, 0, 0, NULL);
1380 hdc = GetDC(hwnd);
1381 SetMapMode(hdc, MM_ANISOTROPIC);
1382 SelectObject(hdc, hfont);
1384 nb = pGetGlyphIndicesW(hdc, L"i", 1, glyphs, 0);
1385 ok(nb == 1, "GetGlyphIndicesW should have returned 1\n");
1387 ret = GetCharABCWidthsI(hdc, 0, 1, glyphs, abc);
1388 ok(ret, "GetCharABCWidthsI should have succeeded\n");
1389 ret = GetCharABCWidthsW(hdc, 'i', 'i', abcw);
1390 ok(ret, "GetCharABCWidthsW should have succeeded\n");
1391 ret = pGetCharABCWidthsFloatW(hdc, 'i', 'i', abcf);
1392 ok(ret, "GetCharABCWidthsFloatW should have succeeded\n");
1394 ABCWidths_helper("LTR", hdc, glyphs, abc, abcw, abcf);
1395 SetWindowExtEx(hdc, -1, -1, NULL);
1396 SetGraphicsMode(hdc, GM_COMPATIBLE);
1397 ABCWidths_helper("LTR -1 compatible", hdc, glyphs, abc, abcw, abcf);
1398 SetGraphicsMode(hdc, GM_ADVANCED);
1399 ABCWidths_helper("LTR -1 advanced", hdc, glyphs, abc, abcw, abcf);
1400 SetWindowExtEx(hdc, 1, 1, NULL);
1401 SetGraphicsMode(hdc, GM_COMPATIBLE);
1402 ABCWidths_helper("LTR 1 compatible", hdc, glyphs, abc, abcw, abcf);
1403 SetGraphicsMode(hdc, GM_ADVANCED);
1404 ABCWidths_helper("LTR 1 advanced", hdc, glyphs, abc, abcw, abcf);
1406 ReleaseDC(hwnd, hdc);
1407 DestroyWindow(hwnd);
1409 /* RTL layout */
1410 hwnd = CreateWindowExA(WS_EX_LAYOUTRTL, "static", "", WS_POPUP, 0,0,100,100,
1411 0, 0, 0, NULL);
1412 hdc = GetDC(hwnd);
1413 SetMapMode(hdc, MM_ANISOTROPIC);
1414 SelectObject(hdc, hfont);
1416 ABCWidths_helper("RTL", hdc, glyphs, abc, abcw, abcf);
1417 SetWindowExtEx(hdc, -1, -1, NULL);
1418 SetGraphicsMode(hdc, GM_COMPATIBLE);
1419 ABCWidths_helper("RTL -1 compatible", hdc, glyphs, abc, abcw, abcf);
1420 SetGraphicsMode(hdc, GM_ADVANCED);
1421 ABCWidths_helper("RTL -1 advanced", hdc, glyphs, abc, abcw, abcf);
1422 SetWindowExtEx(hdc, 1, 1, NULL);
1423 SetGraphicsMode(hdc, GM_COMPATIBLE);
1424 ABCWidths_helper("RTL 1 compatible", hdc, glyphs, abc, abcw, abcf);
1425 SetGraphicsMode(hdc, GM_ADVANCED);
1426 ABCWidths_helper("RTL 1 advanced", hdc, glyphs, abc, abcw, abcf);
1428 ReleaseDC(hwnd, hdc);
1429 DestroyWindow(hwnd);
1430 DeleteObject(hfont);
1433 static void test_text_extents(void)
1435 static const WCHAR wt[] = L"One\ntwo 3";
1436 LPINT extents;
1437 INT i, len, fit1, fit2, extents2[3];
1438 LOGFONTA lf;
1439 TEXTMETRICA tm;
1440 HDC hdc;
1441 HFONT hfont;
1442 SIZE sz;
1443 SIZE sz1, sz2;
1444 BOOL ret;
1446 memset(&lf, 0, sizeof(lf));
1447 strcpy(lf.lfFaceName, "Arial");
1448 lf.lfHeight = 20;
1450 hfont = CreateFontIndirectA(&lf);
1451 hdc = GetDC(0);
1452 hfont = SelectObject(hdc, hfont);
1453 GetTextMetricsA(hdc, &tm);
1454 ret = GetTextExtentPointA(hdc, "o", 1, &sz);
1455 ok(ret, "got %d\n", ret);
1456 ok(sz.cy == tm.tmHeight, "cy %d tmHeight %d\n", sz.cy, tm.tmHeight);
1458 memset(&sz, 0xcc, sizeof(sz));
1459 ret = GetTextExtentPointA(hdc, "o", 0, &sz);
1460 ok(ret, "got %d\n", ret);
1461 ok(sz.cx == 0 && sz.cy == 0, "cx %d, cy %d\n", sz.cx, sz.cy);
1463 memset(&sz, 0xcc, sizeof(sz));
1464 ret = GetTextExtentPointA(hdc, "", 0, &sz);
1465 ok(ret, "got %d\n", ret);
1466 ok(sz.cx == 0 && sz.cy == 0, "cx %d, cy %d\n", sz.cx, sz.cy);
1468 SetLastError(0xdeadbeef);
1469 GetTextExtentExPointW(hdc, wt, 1, 1, &fit1, &fit2, &sz1);
1470 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
1472 win_skip("Skipping remainder of text extents test on a Win9x platform\n");
1473 hfont = SelectObject(hdc, hfont);
1474 DeleteObject(hfont);
1475 ReleaseDC(0, hdc);
1476 return;
1479 memset(&sz, 0xcc, sizeof(sz));
1480 ret = GetTextExtentPointW(hdc, wt, 0, &sz);
1481 ok(ret, "got %d\n", ret);
1482 ok(sz.cx == 0 && sz.cy == 0, "cx %d, cy %d\n", sz.cx, sz.cy);
1484 memset(&sz, 0xcc, sizeof(sz));
1485 ret = GetTextExtentPointW(hdc, L"", 0, &sz);
1486 ok(ret, "got %d\n", ret);
1487 ok(sz.cx == 0 && sz.cy == 0, "cx %d, cy %d\n", sz.cx, sz.cy);
1489 len = lstrlenW(wt);
1490 extents = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof extents[0]);
1491 extents[0] = 1; /* So that the increasing sequence test will fail
1492 if the extents array is untouched. */
1493 GetTextExtentExPointW(hdc, wt, len, 32767, &fit1, extents, &sz1);
1494 GetTextExtentPointW(hdc, wt, len, &sz2);
1495 ok(sz1.cy == sz2.cy,
1496 "cy from GetTextExtentExPointW (%d) and GetTextExtentPointW (%d) differ\n", sz1.cy, sz2.cy);
1497 /* Because of the '\n' in the string GetTextExtentExPoint and
1498 GetTextExtentPoint return different widths under Win2k, but
1499 under WinXP they return the same width. So we don't test that
1500 here. */
1502 for (i = 1; i < len; ++i)
1503 ok(extents[i-1] <= extents[i],
1504 "GetTextExtentExPointW generated a non-increasing sequence of partial extents (at position %d)\n",
1506 ok(extents[len-1] == sz1.cx, "GetTextExtentExPointW extents and size don't match\n");
1507 ok(0 <= fit1 && fit1 <= len, "GetTextExtentExPointW generated illegal value %d for fit\n", fit1);
1508 ok(0 < fit1, "GetTextExtentExPointW says we can't even fit one letter in 32767 logical units\n");
1509 GetTextExtentExPointW(hdc, wt, len, extents[2], &fit2, NULL, &sz2);
1510 ok(sz1.cx == sz2.cx && sz1.cy == sz2.cy, "GetTextExtentExPointW returned different sizes for the same string\n");
1511 ok(fit2 == 3, "GetTextExtentExPointW extents isn't consistent with fit\n");
1512 GetTextExtentExPointW(hdc, wt, len, extents[2]-1, &fit2, NULL, &sz2);
1513 ok(fit2 == 2, "GetTextExtentExPointW extents isn't consistent with fit\n");
1514 GetTextExtentExPointW(hdc, wt, 2, 0, NULL, extents + 2, &sz2);
1515 ok(extents[0] == extents[2] && extents[1] == extents[3],
1516 "GetTextExtentExPointW with lpnFit == NULL returns incorrect results\n");
1517 GetTextExtentExPointW(hdc, wt, 2, 0, NULL, NULL, &sz1);
1518 ok(sz1.cx == sz2.cx && sz1.cy == sz2.cy,
1519 "GetTextExtentExPointW with lpnFit and alpDx both NULL returns incorrect results\n");
1521 /* extents functions fail with -ve counts (the interesting case being -1) */
1522 ret = GetTextExtentPointA(hdc, "o", -1, &sz);
1523 ok(ret == FALSE, "got %d\n", ret);
1524 ret = GetTextExtentExPointA(hdc, "o", -1, 0, NULL, NULL, &sz);
1525 ok(ret == FALSE, "got %d\n", ret);
1526 ret = GetTextExtentExPointW(hdc, wt, -1, 0, NULL, NULL, &sz1);
1527 ok(ret == FALSE, "got %d\n", ret);
1529 /* max_extent = 0 succeeds and returns zero */
1530 fit1 = fit2 = -215;
1531 ret = GetTextExtentExPointA(hdc, NULL, 0, 0, &fit1, NULL, &sz);
1532 ok(ret == TRUE ||
1533 broken(ret == FALSE), /* NT4, 2k */
1534 "got %d\n", ret);
1535 ok(fit1 == 0 ||
1536 broken(fit1 == -215), /* NT4, 2k */
1537 "fit = %d\n", fit1);
1538 ret = GetTextExtentExPointW(hdc, NULL, 0, 0, &fit2, NULL, &sz1);
1539 ok(ret == TRUE, "got %d\n", ret);
1540 ok(fit2 == 0, "fit = %d\n", fit2);
1542 /* max_extent = -1 is interpreted as a very large width that will
1543 * definitely fit our three characters */
1544 fit1 = fit2 = -215;
1545 ret = GetTextExtentExPointA(hdc, "One", 3, -1, &fit1, NULL, &sz);
1546 ok(ret == TRUE, "got %d\n", ret);
1547 ok(fit1 == 3, "fit = %d\n", fit1);
1548 ret = GetTextExtentExPointW(hdc, wt, 3, -1, &fit2, NULL, &sz);
1549 ok(ret == TRUE, "got %d\n", ret);
1550 ok(fit2 == 3, "fit = %d\n", fit2);
1552 /* max_extent = -2 is interpreted similarly, but the Ansi version
1553 * rejects it while the Unicode one accepts it */
1554 fit1 = fit2 = -215;
1555 ret = GetTextExtentExPointA(hdc, "One", 3, -2, &fit1, NULL, &sz);
1556 ok(ret == FALSE, "got %d\n", ret);
1557 ok(fit1 == -215, "fit = %d\n", fit1);
1558 ret = GetTextExtentExPointW(hdc, wt, 3, -2, &fit2, NULL, &sz);
1559 ok(ret == TRUE, "got %d\n", ret);
1560 ok(fit2 == 3, "fit = %d\n", fit2);
1562 hfont = SelectObject(hdc, hfont);
1563 DeleteObject(hfont);
1565 /* non-MM_TEXT mapping mode */
1566 lf.lfHeight = 2000;
1567 hfont = CreateFontIndirectA(&lf);
1568 hfont = SelectObject(hdc, hfont);
1570 SetMapMode( hdc, MM_HIMETRIC );
1571 ret = GetTextExtentExPointW(hdc, wt, 3, 0, NULL, extents, &sz);
1572 ok(ret, "got %d\n", ret);
1573 ok(sz.cx == extents[2], "got %d vs %d\n", sz.cx, extents[2]);
1575 ret = GetTextExtentExPointW(hdc, wt, 3, extents[1], &fit1, extents2, &sz2);
1576 ok(ret, "got %d\n", ret);
1577 ok(fit1 == 2, "got %d\n", fit1);
1578 ok(sz2.cx == sz.cx, "got %d vs %d\n", sz2.cx, sz.cx);
1579 for(i = 0; i < 2; i++)
1580 ok(extents2[i] == extents[i], "%d: %d, %d\n", i, extents2[i], extents[i]);
1582 hfont = SelectObject(hdc, hfont);
1583 DeleteObject(hfont);
1584 HeapFree(GetProcessHeap(), 0, extents);
1585 ReleaseDC(NULL, hdc);
1588 static void free_font(void *font)
1590 UnmapViewOfFile(font);
1593 static void *load_font(const char *font_name, DWORD *font_size)
1595 char file_name[MAX_PATH];
1596 HANDLE file, mapping;
1597 void *font;
1599 if (font_name[1] == ':')
1600 strcpy(file_name, font_name);
1601 else
1603 if (!GetWindowsDirectoryA(file_name, sizeof(file_name))) return NULL;
1604 strcat(file_name, "\\fonts\\");
1605 strcat(file_name, font_name);
1608 file = CreateFileA(file_name, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, 0);
1609 if (file == INVALID_HANDLE_VALUE) return NULL;
1611 *font_size = GetFileSize(file, NULL);
1613 mapping = CreateFileMappingA(file, NULL, PAGE_READONLY, 0, 0, NULL);
1614 if (!mapping)
1616 CloseHandle(file);
1617 return NULL;
1620 font = MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0);
1622 CloseHandle(file);
1623 CloseHandle(mapping);
1624 return font;
1627 static void test_GetGlyphIndices(void)
1629 HDC hdc;
1630 HFONT hfont;
1631 DWORD charcount;
1632 LOGFONTA lf;
1633 DWORD flags = 0;
1634 WCHAR testtext[] = L"Test\xffff";
1635 WORD glyphs[(sizeof(testtext)/2)-1];
1636 TEXTMETRICA textm;
1637 HFONT hOldFont;
1638 HANDLE rsrc;
1639 DWORD ret, font_size, num_fonts;
1640 void *font;
1641 char ttf_name[MAX_PATH];
1643 if (!pGetGlyphIndicesW) {
1644 win_skip("GetGlyphIndicesW not available on platform\n");
1645 return;
1648 hdc = GetDC(0);
1650 memset(&lf, 0, sizeof(lf));
1651 strcpy(lf.lfFaceName, "System");
1652 lf.lfHeight = 16;
1653 lf.lfCharSet = ANSI_CHARSET;
1655 hfont = CreateFontIndirectA(&lf);
1656 ok(hfont != 0, "CreateFontIndirectEx failed\n");
1657 ok(GetTextMetricsA(hdc, &textm), "GetTextMetric failed\n");
1658 if (textm.tmCharSet == ANSI_CHARSET)
1660 flags |= GGI_MARK_NONEXISTING_GLYPHS;
1661 charcount = pGetGlyphIndicesW(hdc, testtext, (sizeof(testtext)/2)-1, glyphs, flags);
1662 ok(charcount == 5, "GetGlyphIndicesW count of glyphs should = 5 not %d\n", charcount);
1663 ok((glyphs[4] == 0x001f || glyphs[4] == 0xffff /* Vista */), "GetGlyphIndicesW should have returned a nonexistent char not %04x\n", glyphs[4]);
1664 flags = 0;
1665 charcount = pGetGlyphIndicesW(hdc, testtext, (sizeof(testtext)/2)-1, glyphs, flags);
1666 ok(charcount == 5, "GetGlyphIndicesW count of glyphs should = 5 not %d\n", charcount);
1667 ok(glyphs[4] == textm.tmDefaultChar, "GetGlyphIndicesW should have returned a %04x not %04x\n",
1668 textm.tmDefaultChar, glyphs[4]);
1670 else
1671 /* FIXME: Write tests for non-ANSI charsets. */
1672 skip("GetGlyphIndices System font tests only for ANSI_CHARSET\n");
1674 if(!is_font_installed("Tahoma"))
1676 skip("Tahoma is not installed so skipping this test\n");
1677 return;
1679 memset(&lf, 0, sizeof(lf));
1680 strcpy(lf.lfFaceName, "Tahoma");
1681 lf.lfHeight = 20;
1683 hfont = CreateFontIndirectA(&lf);
1684 hOldFont = SelectObject(hdc, hfont);
1685 ok(GetTextMetricsA(hdc, &textm), "GetTextMetric failed\n");
1686 flags |= GGI_MARK_NONEXISTING_GLYPHS;
1687 charcount = pGetGlyphIndicesW(hdc, testtext, (sizeof(testtext)/2)-1, glyphs, flags);
1688 ok(charcount == 5, "GetGlyphIndicesW count of glyphs should = 5 not %d\n", charcount);
1689 ok(glyphs[4] == 0xffff, "GetGlyphIndicesW should have returned 0xffff char not %04x\n", glyphs[4]);
1690 flags = 0;
1691 testtext[0] = textm.tmDefaultChar;
1692 charcount = pGetGlyphIndicesW(hdc, testtext, (sizeof(testtext)/2)-1, glyphs, flags);
1693 ok(charcount == 5, "GetGlyphIndicesW count of glyphs should = 5 not %d\n", charcount);
1694 ok(glyphs[0] == 0, "GetGlyphIndicesW for tmDefaultChar should be 0 not %04x\n", glyphs[0]);
1695 ok(glyphs[4] == 0, "GetGlyphIndicesW should have returned 0 not %04x\n", glyphs[4]);
1696 DeleteObject(SelectObject(hdc, hOldFont));
1698 ret = write_ttf_file("wine_nul.ttf", ttf_name);
1699 ok(ret, "Failed to create test font file.\n");
1700 font = load_font(ttf_name, &font_size);
1701 ok(font != NULL, "Failed to map font file.\n");
1702 num_fonts = 0;
1703 rsrc = pAddFontMemResourceEx(font, font_size, NULL, &num_fonts);
1704 ok(ret != 0, "Failed to add resource, %d.\n", GetLastError());
1705 ok(num_fonts == 1, "Unexpected number of fonts %u.\n", num_fonts);
1707 memset(&lf, 0, sizeof(lf));
1708 strcpy(lf.lfFaceName, "wine_nul");
1709 lf.lfHeight = 20;
1710 flags = 0;
1711 hfont = CreateFontIndirectA(&lf);
1712 hOldFont = SelectObject(hdc, hfont);
1713 ok(GetTextMetricsA(hdc, &textm), "GetTextMetric failed\n");
1714 testtext[0] = 'T';
1715 charcount = pGetGlyphIndicesW(hdc, testtext, (sizeof(testtext)/2)-1, glyphs, flags);
1716 ok(charcount == 5, "GetGlyphIndicesW count of glyphs should = 5 not %d\n", charcount);
1717 ok(glyphs[0] == 0, "GetGlyphIndicesW for tmDefaultChar should be 0 not %04x\n", glyphs[0]);
1718 ok(glyphs[4] == 0, "GetGlyphIndicesW should have returned 0 not %04x\n", glyphs[4]);
1719 DeleteObject(SelectObject(hdc, hOldFont));
1721 ret = pRemoveFontMemResourceEx(rsrc);
1722 ok(ret, "RemoveFontMemResourceEx error %d\n", GetLastError());
1723 free_font(font);
1724 ret = DeleteFileA(ttf_name);
1725 ok(ret, "Failed to delete font file, %d.\n", GetLastError());
1729 static void test_GetKerningPairs(void)
1731 static const struct kerning_data
1733 const char face_name[LF_FACESIZE];
1734 LONG height;
1735 /* some interesting fields from OUTLINETEXTMETRIC */
1736 LONG tmHeight, tmAscent, tmDescent;
1737 UINT otmEMSquare;
1738 INT otmAscent;
1739 INT otmDescent;
1740 UINT otmLineGap;
1741 UINT otmsCapEmHeight;
1742 UINT otmsXHeight;
1743 INT otmMacAscent;
1744 INT otmMacDescent;
1745 UINT otmMacLineGap;
1746 UINT otmusMinimumPPEM;
1747 /* small subset of kerning pairs to test */
1748 DWORD total_kern_pairs;
1749 const KERNINGPAIR kern_pair[26];
1750 } kd[] =
1752 {"Arial", 12, 12, 9, 3,
1753 2048, 7, -2, 1, 5, 2, 8, -2, 0, 9,
1756 {' ','A',-1},{' ','T',0},{' ','Y',0},{'1','1',-1},
1757 {'A',' ',-1},{'A','T',-1},{'A','V',-1},{'A','W',0},
1758 {'A','Y',-1},{'A','v',0},{'A','w',0},{'A','y',0},
1759 {'F',',',-1},{'F','.',-1},{'F','A',-1},{'L',' ',0},
1760 {'L','T',-1},{'L','V',-1},{'L','W',-1},{'L','Y',-1},
1761 {915,912,+1},{915,913,-1},{910,912,+1},{910,913,-1},
1762 {933,970,+1},{933,972,-1}
1765 {"Arial", -34, 39, 32, 7,
1766 2048, 25, -7, 5, 17, 9, 31, -7, 1, 9,
1769 {' ','A',-2},{' ','T',-1},{' ','Y',-1},{'1','1',-3},
1770 {'A',' ',-2},{'A','T',-3},{'A','V',-3},{'A','W',-1},
1771 {'A','Y',-3},{'A','v',-1},{'A','w',-1},{'A','y',-1},
1772 {'F',',',-4},{'F','.',-4},{'F','A',-2},{'L',' ',-1},
1773 {'L','T',-3},{'L','V',-3},{'L','W',-3},{'L','Y',-3},
1774 {915,912,+3},{915,913,-3},{910,912,+3},{910,913,-3},
1775 {933,970,+2},{933,972,-3}
1778 { "Arial", 120, 120, 97, 23,
1779 2048, 79, -23, 16, 54, 27, 98, -23, 4, 9,
1782 {' ','A',-6},{' ','T',-2},{' ','Y',-2},{'1','1',-8},
1783 {'A',' ',-6},{'A','T',-8},{'A','V',-8},{'A','W',-4},
1784 {'A','Y',-8},{'A','v',-2},{'A','w',-2},{'A','y',-2},
1785 {'F',',',-12},{'F','.',-12},{'F','A',-6},{'L',' ',-4},
1786 {'L','T',-8},{'L','V',-8},{'L','W',-8},{'L','Y',-8},
1787 {915,912,+9},{915,913,-10},{910,912,+9},{910,913,-8},
1788 {933,970,+6},{933,972,-10}
1791 #if 0 /* this set fails due to +1/-1 errors (rounding bug?), needs investigation. */
1792 { "Arial", 1024 /* usually 1/2 of EM Square */, 1024, 830, 194,
1793 2048, 668, -193, 137, 459, 229, 830, -194, 30, 9,
1796 {' ','A',-51},{' ','T',-17},{' ','Y',-17},{'1','1',-68},
1797 {'A',' ',-51},{'A','T',-68},{'A','V',-68},{'A','W',-34},
1798 {'A','Y',-68},{'A','v',-17},{'A','w',-17},{'A','y',-17},
1799 {'F',',',-102},{'F','.',-102},{'F','A',-51},{'L',' ',-34},
1800 {'L','T',-68},{'L','V',-68},{'L','W',-68},{'L','Y',-68},
1801 {915,912,+73},{915,913,-84},{910,912,+76},{910,913,-68},
1802 {933,970,+54},{933,972,-83}
1805 #endif
1807 LOGFONTA lf;
1808 HFONT hfont, hfont_old;
1809 KERNINGPAIR *kern_pair;
1810 HDC hdc;
1811 DWORD total_kern_pairs, ret, i, n, matches;
1813 hdc = GetDC(0);
1815 /* GetKerningPairsA maps unicode set of kerning pairs to current code page
1816 * which may render this test unusable, so we're trying to avoid that.
1818 SetLastError(0xdeadbeef);
1819 GetKerningPairsW(hdc, 0, NULL);
1820 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
1822 win_skip("Skipping the GetKerningPairs test on a Win9x platform\n");
1823 ReleaseDC(0, hdc);
1824 return;
1827 for (i = 0; i < ARRAY_SIZE(kd); i++)
1829 OUTLINETEXTMETRICW otm;
1830 UINT uiRet;
1832 if (!is_font_installed(kd[i].face_name))
1834 skip("%s is not installed so skipping this test\n", kd[i].face_name);
1835 continue;
1838 memset(&lf, 0, sizeof(lf));
1839 strcpy(lf.lfFaceName, kd[i].face_name);
1840 lf.lfHeight = kd[i].height;
1841 hfont = CreateFontIndirectA(&lf);
1842 ok(hfont != NULL, "failed to create a font, name %s\n", kd[i].face_name);
1844 hfont_old = SelectObject(hdc, hfont);
1846 SetLastError(0xdeadbeef);
1847 otm.otmSize = sizeof(otm); /* just in case for Win9x compatibility */
1848 uiRet = GetOutlineTextMetricsW(hdc, sizeof(otm), &otm);
1849 ok(uiRet == sizeof(otm), "GetOutlineTextMetricsW error %d\n", GetLastError());
1851 ok(match_off_by_1(kd[i].tmHeight, otm.otmTextMetrics.tmHeight, FALSE), "expected %d, got %d\n",
1852 kd[i].tmHeight, otm.otmTextMetrics.tmHeight);
1853 ok(match_off_by_1(kd[i].tmAscent, otm.otmTextMetrics.tmAscent, FALSE), "expected %d, got %d\n",
1854 kd[i].tmAscent, otm.otmTextMetrics.tmAscent);
1855 ok(kd[i].tmDescent == otm.otmTextMetrics.tmDescent, "expected %d, got %d\n",
1856 kd[i].tmDescent, otm.otmTextMetrics.tmDescent);
1858 ok(kd[i].otmEMSquare == otm.otmEMSquare, "expected %u, got %u\n",
1859 kd[i].otmEMSquare, otm.otmEMSquare);
1860 ok(kd[i].otmAscent == otm.otmAscent, "expected %d, got %d\n",
1861 kd[i].otmAscent, otm.otmAscent);
1862 ok(kd[i].otmDescent == otm.otmDescent, "expected %d, got %d\n",
1863 kd[i].otmDescent, otm.otmDescent);
1864 ok(kd[i].otmLineGap == otm.otmLineGap, "expected %u, got %u\n",
1865 kd[i].otmLineGap, otm.otmLineGap);
1866 ok(near_match(kd[i].otmMacDescent, otm.otmMacDescent), "expected %d, got %d\n",
1867 kd[i].otmMacDescent, otm.otmMacDescent);
1868 ok(near_match(kd[i].otmMacAscent, otm.otmMacAscent), "expected %d, got %d\n",
1869 kd[i].otmMacAscent, otm.otmMacAscent);
1870 todo_wine
1871 ok(kd[i].otmsCapEmHeight == otm.otmsCapEmHeight, "expected %u, got %u\n",
1872 kd[i].otmsCapEmHeight, otm.otmsCapEmHeight);
1873 todo_wine
1874 ok(kd[i].otmsXHeight == otm.otmsXHeight, "expected %u, got %u\n",
1875 kd[i].otmsXHeight, otm.otmsXHeight);
1876 ok(kd[i].otmMacLineGap == otm.otmMacLineGap, "expected %u, got %u\n",
1877 kd[i].otmMacLineGap, otm.otmMacLineGap);
1878 todo_wine
1879 ok(kd[i].otmusMinimumPPEM == otm.otmusMinimumPPEM, "expected %u, got %u\n",
1880 kd[i].otmusMinimumPPEM, otm.otmusMinimumPPEM);
1882 total_kern_pairs = GetKerningPairsW(hdc, 0, NULL);
1883 kern_pair = HeapAlloc(GetProcessHeap(), 0, total_kern_pairs * sizeof(*kern_pair));
1885 /* Win98 (GetKerningPairsA) and XP behave differently here, the test
1886 * passes on XP.
1888 SetLastError(0xdeadbeef);
1889 ret = GetKerningPairsW(hdc, 0, kern_pair);
1890 ok(GetLastError() == ERROR_INVALID_PARAMETER,
1891 "got error %u, expected ERROR_INVALID_PARAMETER\n", GetLastError());
1892 ok(ret == 0, "got %u, expected 0\n", ret);
1894 ret = GetKerningPairsW(hdc, 100, NULL);
1895 ok(ret == total_kern_pairs, "got %u, expected %u\n", ret, total_kern_pairs);
1897 ret = GetKerningPairsW(hdc, total_kern_pairs/2, kern_pair);
1898 ok(ret == total_kern_pairs/2, "got %u, expected %u\n", ret, total_kern_pairs/2);
1900 ret = GetKerningPairsW(hdc, total_kern_pairs, kern_pair);
1901 ok(ret == total_kern_pairs, "got %u, expected %u\n", ret, total_kern_pairs);
1903 matches = 0;
1905 for (n = 0; n < ret; n++)
1907 DWORD j;
1909 for (j = 0; j < kd[i].total_kern_pairs; j++)
1911 if (kern_pair[n].wFirst == kd[i].kern_pair[j].wFirst &&
1912 kern_pair[n].wSecond == kd[i].kern_pair[j].wSecond)
1914 ok(kern_pair[n].iKernAmount == kd[i].kern_pair[j].iKernAmount,
1915 "pair %d:%d got %d, expected %d\n",
1916 kern_pair[n].wFirst, kern_pair[n].wSecond,
1917 kern_pair[n].iKernAmount, kd[i].kern_pair[j].iKernAmount);
1918 matches++;
1923 ok(matches == kd[i].total_kern_pairs, "got matches %u, expected %u\n",
1924 matches, kd[i].total_kern_pairs);
1926 HeapFree(GetProcessHeap(), 0, kern_pair);
1928 SelectObject(hdc, hfont_old);
1929 DeleteObject(hfont);
1932 ReleaseDC(0, hdc);
1935 struct font_data
1937 const char face_name[LF_FACESIZE];
1938 int requested_height;
1939 int weight, height, ascent, descent, int_leading, ext_leading, dpi;
1940 BOOL exact;
1943 static void test_height( HDC hdc, const struct font_data *fd )
1945 LOGFONTA lf;
1946 HFONT hfont, old_hfont;
1947 TEXTMETRICA tm;
1948 INT ret, i;
1950 for (i = 0; fd[i].face_name[0]; i++)
1952 if (!is_truetype_font_installed(fd[i].face_name))
1954 skip("%s is not installed\n", fd[i].face_name);
1955 continue;
1958 memset(&lf, 0, sizeof(lf));
1959 lf.lfHeight = fd[i].requested_height;
1960 lf.lfWeight = fd[i].weight;
1961 strcpy(lf.lfFaceName, fd[i].face_name);
1963 hfont = CreateFontIndirectA(&lf);
1964 ok(hfont != NULL, "failed to create a font, name %s\n", fd[i].face_name);
1966 old_hfont = SelectObject(hdc, hfont);
1967 ret = GetTextMetricsA(hdc, &tm);
1968 ok(ret, "GetTextMetrics error %d\n", GetLastError());
1969 if(fd[i].dpi == tm.tmDigitizedAspectX)
1971 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);
1972 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);
1973 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);
1974 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);
1975 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);
1976 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);
1979 SelectObject(hdc, old_hfont);
1980 /* force GDI to use new font, otherwise Windows leaks the font reference */
1981 GetTextMetricsA(hdc, &tm);
1982 DeleteObject(hfont);
1986 static void *find_ttf_table( void *ttf, DWORD size, DWORD tag )
1988 WORD i, num_tables = GET_BE_WORD(*((WORD *)ttf + 2));
1989 DWORD *table = (DWORD *)ttf + 3;
1991 for (i = 0; i < num_tables; i++)
1993 if (table[0] == tag)
1994 return (BYTE *)ttf + GET_BE_DWORD(table[2]);
1995 table += 4;
1997 return NULL;
2000 static void test_height_selection_vdmx( HDC hdc )
2002 static const struct font_data charset_0[] = /* doesn't use VDMX */
2004 { "wine_vdmx", 10, FW_NORMAL, 10, 8, 2, 2, 0, 96, TRUE },
2005 { "wine_vdmx", 11, FW_NORMAL, 11, 9, 2, 2, 0, 96, TRUE },
2006 { "wine_vdmx", 12, FW_NORMAL, 12, 10, 2, 2, 0, 96, TRUE },
2007 { "wine_vdmx", 13, FW_NORMAL, 13, 11, 2, 2, 0, 96, TRUE },
2008 { "wine_vdmx", 14, FW_NORMAL, 14, 12, 2, 2, 0, 96, TRUE },
2009 { "wine_vdmx", 15, FW_NORMAL, 15, 12, 3, 3, 0, 96, FALSE },
2010 { "wine_vdmx", 16, FW_NORMAL, 16, 13, 3, 3, 0, 96, TRUE },
2011 { "wine_vdmx", 17, FW_NORMAL, 17, 14, 3, 3, 0, 96, TRUE },
2012 { "wine_vdmx", 18, FW_NORMAL, 18, 15, 3, 3, 0, 96, TRUE },
2013 { "wine_vdmx", 19, FW_NORMAL, 19, 16, 3, 3, 0, 96, TRUE },
2014 { "wine_vdmx", 20, FW_NORMAL, 20, 17, 3, 4, 0, 96, FALSE },
2015 { "wine_vdmx", 21, FW_NORMAL, 21, 17, 4, 4, 0, 96, TRUE },
2016 { "wine_vdmx", 22, FW_NORMAL, 22, 18, 4, 4, 0, 96, TRUE },
2017 { "wine_vdmx", 23, FW_NORMAL, 23, 19, 4, 4, 0, 96, TRUE },
2018 { "wine_vdmx", 24, FW_NORMAL, 24, 20, 4, 4, 0, 96, TRUE },
2019 { "wine_vdmx", 25, FW_NORMAL, 25, 21, 4, 4, 0, 96, TRUE },
2020 { "wine_vdmx", 26, FW_NORMAL, 26, 22, 4, 5, 0, 96, FALSE },
2021 { "wine_vdmx", 27, FW_NORMAL, 27, 22, 5, 5, 0, 96, TRUE },
2022 { "wine_vdmx", 28, FW_NORMAL, 28, 23, 5, 5, 0, 96, TRUE },
2023 { "wine_vdmx", 29, FW_NORMAL, 29, 24, 5, 5, 0, 96, TRUE },
2024 { "wine_vdmx", 30, FW_NORMAL, 30, 25, 5, 5, 0, 96, TRUE },
2025 { "wine_vdmx", 31, FW_NORMAL, 31, 26, 5, 5, 0, 96, TRUE },
2026 { "wine_vdmx", 32, FW_NORMAL, 32, 27, 5, 6, 0, 96, FALSE },
2027 { "wine_vdmx", 48, FW_NORMAL, 48, 40, 8, 8, 0, 96, TRUE },
2028 { "wine_vdmx", 64, FW_NORMAL, 64, 53, 11, 11, 0, 96, TRUE },
2029 { "wine_vdmx", 96, FW_NORMAL, 96, 80, 16, 17, 0, 96, FALSE },
2030 { "wine_vdmx", -10, FW_NORMAL, 12, 10, 2, 2, 0, 96, TRUE },
2031 { "wine_vdmx", -11, FW_NORMAL, 13, 11, 2, 2, 0, 96, TRUE },
2032 { "wine_vdmx", -12, FW_NORMAL, 14, 12, 2, 2, 0, 96, TRUE },
2033 { "wine_vdmx", -13, FW_NORMAL, 16, 13, 3, 3, 0, 96, TRUE },
2034 { "wine_vdmx", -14, FW_NORMAL, 17, 14, 3, 3, 0, 96, TRUE },
2035 { "wine_vdmx", -15, FW_NORMAL, 18, 15, 3, 3, 0, 96, TRUE },
2036 { "wine_vdmx", -16, FW_NORMAL, 19, 16, 3, 3, 0, 96, TRUE },
2037 { "wine_vdmx", -17, FW_NORMAL, 21, 17, 4, 4, 0, 96, TRUE },
2038 { "wine_vdmx", -18, FW_NORMAL, 22, 18, 4, 4, 0, 96, TRUE },
2039 { "wine_vdmx", -19, FW_NORMAL, 23, 19, 4, 4, 0, 96, TRUE },
2040 { "wine_vdmx", -20, FW_NORMAL, 24, 20, 4, 4, 0, 96, TRUE },
2041 { "wine_vdmx", -21, FW_NORMAL, 25, 21, 4, 4, 0, 96, TRUE },
2042 { "wine_vdmx", -22, FW_NORMAL, 27, 22, 5, 5, 0, 96, TRUE },
2043 { "wine_vdmx", -23, FW_NORMAL, 28, 23, 5, 5, 0, 96, TRUE },
2044 { "wine_vdmx", -24, FW_NORMAL, 29, 24, 5, 5, 0, 96, TRUE },
2045 { "wine_vdmx", -25, FW_NORMAL, 30, 25, 5, 5, 0, 96, TRUE },
2046 { "wine_vdmx", -26, FW_NORMAL, 31, 26, 5, 5, 0, 96, TRUE },
2047 { "wine_vdmx", -27, FW_NORMAL, 33, 27, 6, 6, 0, 96, TRUE },
2048 { "wine_vdmx", -28, FW_NORMAL, 34, 28, 6, 6, 0, 96, TRUE },
2049 { "wine_vdmx", -29, FW_NORMAL, 35, 29, 6, 6, 0, 96, TRUE },
2050 { "wine_vdmx", -30, FW_NORMAL, 36, 30, 6, 6, 0, 96, TRUE },
2051 { "wine_vdmx", -31, FW_NORMAL, 37, 31, 6, 6, 0, 96, TRUE },
2052 { "wine_vdmx", -32, FW_NORMAL, 39, 32, 7, 7, 0, 96, TRUE },
2053 { "wine_vdmx", -48, FW_NORMAL, 58, 48, 10, 10, 0, 96, TRUE },
2054 { "wine_vdmx", -64, FW_NORMAL, 77, 64, 13, 13, 0, 96, TRUE },
2055 { "wine_vdmx", -96, FW_NORMAL, 116, 96, 20, 20, 0, 96, TRUE },
2056 { "", 0, 0, 0, 0, 0, 0, 0, 0, 0 }
2059 static const struct font_data charset_1[] = /* Uses VDMX */
2061 { "wine_vdmx", 10, FW_NORMAL, 10, 8, 2, 2, 0, 96, TRUE },
2062 { "wine_vdmx", 11, FW_NORMAL, 11, 9, 2, 2, 0, 96, TRUE },
2063 { "wine_vdmx", 12, FW_NORMAL, 12, 10, 2, 2, 0, 96, TRUE },
2064 { "wine_vdmx", 13, FW_NORMAL, 13, 11, 2, 2, 0, 96, TRUE },
2065 { "wine_vdmx", 14, FW_NORMAL, 13, 11, 2, 2, 0, 96, TRUE },
2066 { "wine_vdmx", 15, FW_NORMAL, 13, 11, 2, 2, 0, 96, TRUE },
2067 { "wine_vdmx", 16, FW_NORMAL, 16, 13, 3, 4, 0, 96, TRUE },
2068 { "wine_vdmx", 17, FW_NORMAL, 16, 13, 3, 3, 0, 96, TRUE },
2069 { "wine_vdmx", 18, FW_NORMAL, 16, 13, 3, 3, 0, 96, TRUE },
2070 { "wine_vdmx", 19, FW_NORMAL, 19, 15, 4, 5, 0, 96, TRUE },
2071 { "wine_vdmx", 20, FW_NORMAL, 20, 16, 4, 5, 0, 96, TRUE },
2072 { "wine_vdmx", 21, FW_NORMAL, 21, 17, 4, 5, 0, 96, TRUE },
2073 { "wine_vdmx", 22, FW_NORMAL, 22, 18, 4, 5, 0, 96, TRUE },
2074 { "wine_vdmx", 23, FW_NORMAL, 23, 19, 4, 5, 0, 96, TRUE },
2075 { "wine_vdmx", 24, FW_NORMAL, 23, 19, 4, 5, 0, 96, TRUE },
2076 { "wine_vdmx", 25, FW_NORMAL, 25, 21, 4, 6, 0, 96, TRUE },
2077 { "wine_vdmx", 26, FW_NORMAL, 26, 22, 4, 6, 0, 96, TRUE },
2078 { "wine_vdmx", 27, FW_NORMAL, 27, 23, 4, 6, 0, 96, TRUE },
2079 { "wine_vdmx", 28, FW_NORMAL, 27, 23, 4, 5, 0, 96, TRUE },
2080 { "wine_vdmx", 29, FW_NORMAL, 29, 24, 5, 6, 0, 96, TRUE },
2081 { "wine_vdmx", 30, FW_NORMAL, 29, 24, 5, 6, 0, 96, TRUE },
2082 { "wine_vdmx", 31, FW_NORMAL, 29, 24, 5, 6, 0, 96, TRUE },
2083 { "wine_vdmx", 32, FW_NORMAL, 32, 26, 6, 8, 0, 96, TRUE },
2084 { "wine_vdmx", 48, FW_NORMAL, 48, 40, 8, 10, 0, 96, TRUE },
2085 { "wine_vdmx", 64, FW_NORMAL, 64, 54, 10, 13, 0, 96, TRUE },
2086 { "wine_vdmx", 96, FW_NORMAL, 95, 79, 16, 18, 0, 96, TRUE },
2087 { "wine_vdmx", -10, FW_NORMAL, 12, 10, 2, 2, 0, 96, TRUE },
2088 { "wine_vdmx", -11, FW_NORMAL, 13, 11, 2, 2, 0, 96, TRUE },
2089 { "wine_vdmx", -12, FW_NORMAL, 16, 13, 3, 4, 0, 96, TRUE },
2090 { "wine_vdmx", -13, FW_NORMAL, 16, 13, 3, 3, 0, 96, TRUE },
2091 { "wine_vdmx", -14, FW_NORMAL, 19, 15, 4, 5, 0, 96, TRUE },
2092 { "wine_vdmx", -15, FW_NORMAL, 20, 16, 4, 5, 0, 96, TRUE },
2093 { "wine_vdmx", -16, FW_NORMAL, 21, 17, 4, 5, 0, 96, TRUE },
2094 { "wine_vdmx", -17, FW_NORMAL, 22, 18, 4, 5, 0, 96, TRUE },
2095 { "wine_vdmx", -18, FW_NORMAL, 23, 19, 4, 5, 0, 96, TRUE },
2096 { "wine_vdmx", -19, FW_NORMAL, 25, 21, 4, 6, 0, 96, TRUE },
2097 { "wine_vdmx", -20, FW_NORMAL, 26, 22, 4, 6, 0, 96, TRUE },
2098 { "wine_vdmx", -21, FW_NORMAL, 27, 23, 4, 6, 0, 96, TRUE },
2099 { "wine_vdmx", -22, FW_NORMAL, 27, 23, 4, 5, 0, 96, TRUE },
2100 { "wine_vdmx", -23, FW_NORMAL, 29, 24, 5, 6, 0, 96, TRUE },
2101 { "wine_vdmx", -24, FW_NORMAL, 32, 26, 6, 8, 0, 96, TRUE },
2102 { "wine_vdmx", -25, FW_NORMAL, 32, 26, 6, 7, 0, 96, TRUE },
2103 { "wine_vdmx", -26, FW_NORMAL, 33, 27, 6, 7, 0, 96, TRUE },
2104 { "wine_vdmx", -27, FW_NORMAL, 35, 29, 6, 8, 0, 96, TRUE },
2105 { "wine_vdmx", -28, FW_NORMAL, 36, 30, 6, 8, 0, 96, TRUE },
2106 { "wine_vdmx", -29, FW_NORMAL, 36, 30, 6, 7, 0, 96, TRUE },
2107 { "wine_vdmx", -30, FW_NORMAL, 38, 32, 6, 8, 0, 96, TRUE },
2108 { "wine_vdmx", -31, FW_NORMAL, 39, 33, 6, 8, 0, 96, TRUE },
2109 { "wine_vdmx", -32, FW_NORMAL, 40, 33, 7, 8, 0, 96, TRUE },
2110 { "wine_vdmx", -48, FW_NORMAL, 60, 50, 10, 12, 0, 96, TRUE },
2111 { "wine_vdmx", -64, FW_NORMAL, 81, 67, 14, 17, 0, 96, TRUE },
2112 { "wine_vdmx", -96, FW_NORMAL, 119, 99, 20, 23, 0, 96, TRUE },
2113 { "", 0, 0, 0, 0, 0, 0, 0, 0, 0 }
2116 static const struct vdmx_data
2118 WORD version;
2119 BYTE bCharSet;
2120 const struct font_data *fd;
2121 } data[] =
2123 { 0, 0, charset_0 },
2124 { 0, 1, charset_1 },
2125 { 1, 0, charset_0 },
2126 { 1, 1, charset_1 }
2128 int i;
2129 DWORD size, num;
2130 WORD *vdmx_header;
2131 BYTE *ratio_rec;
2132 char ttf_name[MAX_PATH];
2133 void *res, *copy;
2134 BOOL ret;
2136 if (!pAddFontResourceExA)
2138 win_skip("AddFontResourceExA unavailable\n");
2139 return;
2142 for (i = 0; i < ARRAY_SIZE(data); i++)
2144 res = get_res_data( "wine_vdmx.ttf", &size );
2146 copy = HeapAlloc( GetProcessHeap(), 0, size );
2147 memcpy( copy, res, size );
2148 vdmx_header = find_ttf_table( copy, size, MS_MAKE_TAG('V','D','M','X') );
2149 vdmx_header[0] = GET_BE_WORD( data[i].version );
2150 ok( GET_BE_WORD( vdmx_header[1] ) == 1, "got %04x\n", GET_BE_WORD( vdmx_header[1] ) );
2151 ok( GET_BE_WORD( vdmx_header[2] ) == 1, "got %04x\n", GET_BE_WORD( vdmx_header[2] ) );
2152 ratio_rec = (BYTE *)&vdmx_header[3];
2153 ratio_rec[0] = data[i].bCharSet;
2155 write_tmp_file( copy, &size, ttf_name );
2156 HeapFree( GetProcessHeap(), 0, copy );
2158 ok( !is_truetype_font_installed("wine_vdmx"), "Already installed\n" );
2159 num = pAddFontResourceExA( ttf_name, FR_PRIVATE, 0 );
2160 if (!num) win_skip("Unable to add ttf font resource\n");
2161 else
2163 ok( is_truetype_font_installed("wine_vdmx"), "Not installed\n" );
2164 test_height( hdc, data[i].fd );
2165 pRemoveFontResourceExA( ttf_name, FR_PRIVATE, 0 );
2167 ret = DeleteFileA( ttf_name );
2168 ok(ret || broken(!ret && GetLastError() == ERROR_ACCESS_DENIED),
2169 "DeleteFile error %d\n", GetLastError());
2173 static void test_height_selection(void)
2175 static const struct font_data tahoma[] =
2177 {"Tahoma", -12, FW_NORMAL, 14, 12, 2, 2, 0, 96, TRUE },
2178 {"Tahoma", -24, FW_NORMAL, 29, 24, 5, 5, 0, 96, TRUE },
2179 {"Tahoma", -48, FW_NORMAL, 58, 48, 10, 10, 0, 96, TRUE },
2180 {"Tahoma", -96, FW_NORMAL, 116, 96, 20, 20, 0, 96, TRUE },
2181 {"Tahoma", -192, FW_NORMAL, 232, 192, 40, 40, 0, 96, TRUE },
2182 {"Tahoma", 12, FW_NORMAL, 12, 10, 2, 2, 0, 96, TRUE },
2183 {"Tahoma", 24, FW_NORMAL, 24, 20, 4, 4, 0, 96, TRUE },
2184 {"Tahoma", 48, FW_NORMAL, 48, 40, 8, 8, 0, 96, TRUE },
2185 {"Tahoma", 96, FW_NORMAL, 96, 80, 16, 17, 0, 96, FALSE },
2186 {"Tahoma", 192, FW_NORMAL, 192, 159, 33, 33, 0, 96, TRUE },
2187 {"", 0, 0, 0, 0, 0, 0, 0, 0, 0 }
2189 HDC hdc = CreateCompatibleDC(0);
2190 ok(hdc != NULL, "failed to create hdc\n");
2192 test_height( hdc, tahoma );
2193 test_height_selection_vdmx( hdc );
2195 DeleteDC(hdc);
2198 static UINT get_font_fsselection(LOGFONTA *lf)
2200 OUTLINETEXTMETRICA *otm;
2201 HFONT hfont, hfont_old;
2202 DWORD ret, otm_size;
2203 UINT fsSelection;
2204 HDC hdc;
2206 hdc = GetDC(0);
2207 hfont = CreateFontIndirectA(lf);
2208 ok(hfont != NULL, "failed to create a font\n");
2210 hfont_old = SelectObject(hdc, hfont);
2212 otm_size = GetOutlineTextMetricsA(hdc, 0, NULL);
2213 otm = HeapAlloc(GetProcessHeap(), 0, otm_size);
2214 otm->otmSize = sizeof(*otm);
2215 ret = GetOutlineTextMetricsA(hdc, otm->otmSize, otm);
2216 ok(ret == otm->otmSize, "expected %u, got %u, error %d\n", otm->otmSize, ret, GetLastError());
2217 fsSelection = otm->otmfsSelection;
2218 HeapFree(GetProcessHeap(), 0, otm);
2219 SelectObject(hdc, hfont_old);
2220 DeleteObject(hfont);
2221 ReleaseDC(0, hdc);
2223 return fsSelection;
2226 static void test_GetOutlineTextMetrics(void)
2228 OUTLINETEXTMETRICA *otm;
2229 LOGFONTA lf;
2230 HFONT hfont, hfont_old;
2231 HDC hdc;
2232 DWORD ret, otm_size;
2233 LPSTR unset_ptr;
2234 UINT fsSelection;
2236 /* check fsSelection field with bold simulation */
2237 memset(&lf, 0, sizeof(lf));
2238 strcpy(lf.lfFaceName, "Wingdings");
2239 lf.lfCharSet = SYMBOL_CHARSET;
2241 /* regular face */
2242 fsSelection = get_font_fsselection(&lf);
2243 ok((fsSelection & (1 << 5)) == 0, "got 0x%x\n", fsSelection);
2245 /* face with bold simulation */
2246 lf.lfWeight = FW_BOLD;
2247 fsSelection = get_font_fsselection(&lf);
2248 ok((fsSelection & (1 << 5)) != 0, "got 0x%x\n", fsSelection);
2250 /* check fsSelection field with oblique simulation */
2251 memset(&lf, 0, sizeof(lf));
2252 strcpy(lf.lfFaceName, "Tahoma");
2253 lf.lfHeight = -13;
2254 lf.lfWeight = FW_NORMAL;
2255 lf.lfPitchAndFamily = DEFAULT_PITCH;
2256 lf.lfQuality = PROOF_QUALITY;
2258 /* regular face */
2259 fsSelection = get_font_fsselection(&lf);
2260 ok((fsSelection & 1) == 0, "got 0x%x\n", fsSelection);
2262 lf.lfItalic = 1;
2263 /* face with oblique simulation */
2264 fsSelection = get_font_fsselection(&lf);
2265 ok((fsSelection & 1) == 1, "got 0x%x\n", fsSelection);
2267 if (!is_font_installed("Arial"))
2269 skip("Arial is not installed\n");
2270 return;
2273 hdc = GetDC(0);
2275 memset(&lf, 0, sizeof(lf));
2276 strcpy(lf.lfFaceName, "Arial");
2277 lf.lfHeight = -13;
2278 lf.lfWeight = FW_NORMAL;
2279 lf.lfPitchAndFamily = DEFAULT_PITCH;
2280 lf.lfQuality = PROOF_QUALITY;
2281 hfont = CreateFontIndirectA(&lf);
2282 ok(hfont != NULL, "failed to create a font\n");
2284 hfont_old = SelectObject(hdc, hfont);
2285 otm_size = GetOutlineTextMetricsA(hdc, 0, NULL);
2287 otm = HeapAlloc(GetProcessHeap(), 0, otm_size);
2289 memset(otm, 0xAA, otm_size);
2290 SetLastError(0xdeadbeef);
2291 otm->otmSize = sizeof(*otm); /* just in case for Win9x compatibility */
2292 ret = GetOutlineTextMetricsA(hdc, otm->otmSize, otm);
2293 ok(ret == 1 /* Win9x */ ||
2294 ret == otm->otmSize /* XP*/,
2295 "expected %u, got %u, error %d\n", otm->otmSize, ret, GetLastError());
2296 if (ret != 1) /* Win9x doesn't care about pointing beyond of the buffer */
2298 ok(otm->otmpFamilyName == NULL, "expected NULL got %p\n", otm->otmpFamilyName);
2299 ok(otm->otmpFaceName == NULL, "expected NULL got %p\n", otm->otmpFaceName);
2300 ok(otm->otmpStyleName == NULL, "expected NULL got %p\n", otm->otmpStyleName);
2301 ok(otm->otmpFullName == NULL, "expected NULL got %p\n", otm->otmpFullName);
2304 memset(otm, 0xAA, otm_size);
2305 SetLastError(0xdeadbeef);
2306 otm->otmSize = otm_size; /* just in case for Win9x compatibility */
2307 ret = GetOutlineTextMetricsA(hdc, otm->otmSize, otm);
2308 ok(ret == 1 /* Win9x */ ||
2309 ret == otm->otmSize /* XP*/,
2310 "expected %u, got %u, error %d\n", otm->otmSize, ret, GetLastError());
2311 if (ret != 1) /* Win9x doesn't care about pointing beyond of the buffer */
2313 ok(otm->otmpFamilyName != NULL, "expected not NULL got %p\n", otm->otmpFamilyName);
2314 ok(otm->otmpFaceName != NULL, "expected not NULL got %p\n", otm->otmpFaceName);
2315 ok(otm->otmpStyleName != NULL, "expected not NULL got %p\n", otm->otmpStyleName);
2316 ok(otm->otmpFullName != NULL, "expected not NULL got %p\n", otm->otmpFullName);
2319 /* ask about truncated data */
2320 memset(otm, 0xAA, otm_size);
2321 memset(&unset_ptr, 0xAA, sizeof(unset_ptr));
2322 SetLastError(0xdeadbeef);
2323 otm->otmSize = sizeof(*otm) - sizeof(LPSTR); /* just in case for Win9x compatibility */
2324 ret = GetOutlineTextMetricsA(hdc, otm->otmSize, otm);
2325 ok(ret == 1 /* Win9x */ ||
2326 ret == otm->otmSize /* XP*/,
2327 "expected %u, got %u, error %d\n", otm->otmSize, ret, GetLastError());
2328 if (ret != 1) /* Win9x doesn't care about pointing beyond of the buffer */
2330 ok(otm->otmpFamilyName == NULL, "expected NULL got %p\n", otm->otmpFamilyName);
2331 ok(otm->otmpFaceName == NULL, "expected NULL got %p\n", otm->otmpFaceName);
2332 ok(otm->otmpStyleName == NULL, "expected NULL got %p\n", otm->otmpStyleName);
2334 ok(otm->otmpFullName == unset_ptr, "expected %p got %p\n", unset_ptr, otm->otmpFullName);
2336 /* check handling of NULL pointer */
2337 SetLastError(0xdeadbeef);
2338 ret = GetOutlineTextMetricsA(hdc, otm_size, NULL);
2339 ok(ret == otm_size, "expected %u, got %u, error %d\n", otm_size, ret, GetLastError());
2341 HeapFree(GetProcessHeap(), 0, otm);
2343 SelectObject(hdc, hfont_old);
2344 DeleteObject(hfont);
2346 ReleaseDC(0, hdc);
2349 static void testJustification(HDC hdc, PCSTR str, RECT *clientArea)
2351 INT y,
2352 breakCount,
2353 areaWidth = clientArea->right - clientArea->left,
2354 nErrors = 0, e;
2355 const char *pFirstChar, *pLastChar;
2356 SIZE size;
2357 TEXTMETRICA tm;
2358 struct err
2360 const char *start;
2361 int len;
2362 int GetTextExtentExPointWWidth;
2363 } error[20];
2365 GetTextMetricsA(hdc, &tm);
2366 y = clientArea->top;
2367 do {
2368 breakCount = 0;
2369 while (*str == tm.tmBreakChar) str++; /* skip leading break chars */
2370 pFirstChar = str;
2372 do {
2373 pLastChar = str;
2375 /* if not at the end of the string, ... */
2376 if (*str == '\0') break;
2377 /* ... add the next word to the current extent */
2378 while (*str != '\0' && *str++ != tm.tmBreakChar);
2379 breakCount++;
2380 SetTextJustification(hdc, 0, 0);
2381 GetTextExtentPoint32A(hdc, pFirstChar, str - pFirstChar - 1, &size);
2382 } while ((int) size.cx < areaWidth);
2384 /* ignore trailing break chars */
2385 breakCount--;
2386 while (*(pLastChar - 1) == tm.tmBreakChar)
2388 pLastChar--;
2389 breakCount--;
2392 if (*str == '\0' || breakCount <= 0) pLastChar = str;
2394 SetTextJustification(hdc, 0, 0);
2395 GetTextExtentPoint32A(hdc, pFirstChar, pLastChar - pFirstChar, &size);
2397 /* do not justify the last extent */
2398 if (*str != '\0' && breakCount > 0)
2400 SetTextJustification(hdc, areaWidth - size.cx, breakCount);
2401 GetTextExtentPoint32A(hdc, pFirstChar, pLastChar - pFirstChar, &size);
2402 if (size.cx != areaWidth && nErrors < ARRAY_SIZE(error) - 1)
2404 error[nErrors].start = pFirstChar;
2405 error[nErrors].len = pLastChar - pFirstChar;
2406 error[nErrors].GetTextExtentExPointWWidth = size.cx;
2407 nErrors++;
2411 y += size.cy;
2412 str = pLastChar;
2413 } while (*str && y < clientArea->bottom);
2415 for (e = 0; e < nErrors; e++)
2417 /* The width returned by GetTextExtentPoint32() is exactly the same
2418 returned by GetTextExtentExPointW() - see dlls/gdi32/font.c */
2419 ok(error[e].GetTextExtentExPointWWidth == areaWidth,
2420 "GetTextExtentPointW() for \"%.*s\" should have returned a width of %d, not %d.\n",
2421 error[e].len, error[e].start, areaWidth, error[e].GetTextExtentExPointWWidth);
2425 static void test_SetTextJustification(void)
2427 HDC hdc;
2428 RECT clientArea;
2429 LOGFONTA lf;
2430 HFONT hfont;
2431 HWND hwnd;
2432 SIZE size, expect;
2433 int i;
2434 WORD indices[2];
2435 static const char testText[] =
2436 "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do "
2437 "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut "
2438 "enim ad minim veniam, quis nostrud exercitation ullamco laboris "
2439 "nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in "
2440 "reprehenderit in voluptate velit esse cillum dolore eu fugiat "
2441 "nulla pariatur. Excepteur sint occaecat cupidatat non proident, "
2442 "sunt in culpa qui officia deserunt mollit anim id est laborum.";
2444 hwnd = CreateWindowExA(0, "static", "", WS_POPUP, 0,0, 400,400, 0, 0, 0, NULL);
2445 GetClientRect( hwnd, &clientArea );
2446 hdc = GetDC( hwnd );
2448 if (!is_font_installed("Times New Roman"))
2450 skip("Times New Roman is not installed\n");
2451 return;
2454 memset(&lf, 0, sizeof lf);
2455 lf.lfCharSet = ANSI_CHARSET;
2456 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
2457 lf.lfWeight = FW_DONTCARE;
2458 lf.lfHeight = 20;
2459 lf.lfQuality = DEFAULT_QUALITY;
2460 lstrcpyA(lf.lfFaceName, "Times New Roman");
2461 hfont = create_font("Times New Roman", &lf);
2462 SelectObject(hdc, hfont);
2464 testJustification(hdc, testText, &clientArea);
2466 if (!pGetTextExtentExPointI) goto done;
2467 GetGlyphIndicesA( hdc, "A ", 2, indices, 0 );
2469 SetTextJustification(hdc, 0, 0);
2470 GetTextExtentPoint32A(hdc, " ", 1, &expect);
2471 GetTextExtentPoint32A(hdc, " ", 3, &size);
2472 ok( size.cx == 3 * expect.cx, "wrong size %d/%d\n", size.cx, expect.cx );
2473 SetTextJustification(hdc, 4, 1);
2474 GetTextExtentPoint32A(hdc, " ", 1, &size);
2475 ok( size.cx == expect.cx + 4, "wrong size %d/%d\n", size.cx, expect.cx );
2476 SetTextJustification(hdc, 9, 2);
2477 GetTextExtentPoint32A(hdc, " ", 2, &size);
2478 ok( size.cx == 2 * expect.cx + 9, "wrong size %d/%d\n", size.cx, expect.cx );
2479 SetTextJustification(hdc, 7, 3);
2480 GetTextExtentPoint32A(hdc, " ", 3, &size);
2481 ok( size.cx == 3 * expect.cx + 7, "wrong size %d/%d\n", size.cx, expect.cx );
2482 SetTextJustification(hdc, 7, 3);
2483 SetTextCharacterExtra(hdc, 2 );
2484 GetTextExtentPoint32A(hdc, " ", 3, &size);
2485 ok( size.cx == 3 * (expect.cx + 2) + 7, "wrong size %d/%d\n", size.cx, expect.cx );
2486 SetTextJustification(hdc, 0, 0);
2487 SetTextCharacterExtra(hdc, 0);
2488 size.cx = size.cy = 1234;
2489 GetTextExtentPoint32A(hdc, " ", 0, &size);
2490 ok( size.cx == 0 && size.cy == 0, "wrong size %d,%d\n", size.cx, size.cy );
2491 pGetTextExtentExPointI(hdc, indices, 2, -1, NULL, NULL, &expect);
2492 SetTextJustification(hdc, 5, 1);
2493 pGetTextExtentExPointI(hdc, indices, 2, -1, NULL, NULL, &size);
2494 ok( size.cx == expect.cx + 5, "wrong size %d/%d\n", size.cx, expect.cx );
2495 SetTextJustification(hdc, 0, 0);
2497 SetMapMode( hdc, MM_ANISOTROPIC );
2498 SetWindowExtEx( hdc, 2, 2, NULL );
2499 GetClientRect( hwnd, &clientArea );
2500 DPtoLP( hdc, (POINT *)&clientArea, 2 );
2501 testJustification(hdc, testText, &clientArea);
2503 GetTextExtentPoint32A(hdc, "A", 1, &expect);
2504 for (i = 0; i < 10; i++)
2506 SetTextCharacterExtra(hdc, i);
2507 GetTextExtentPoint32A(hdc, "A", 1, &size);
2508 ok( size.cx == expect.cx + i, "wrong size %d/%d+%d\n", size.cx, expect.cx, i );
2510 SetTextCharacterExtra(hdc, 0);
2511 pGetTextExtentExPointI(hdc, indices, 1, -1, NULL, NULL, &expect);
2512 for (i = 0; i < 10; i++)
2514 SetTextCharacterExtra(hdc, i);
2515 pGetTextExtentExPointI(hdc, indices, 1, -1, NULL, NULL, &size);
2516 ok( size.cx == expect.cx + i, "wrong size %d/%d+%d\n", size.cx, expect.cx, i );
2518 SetTextCharacterExtra(hdc, 0);
2520 SetViewportExtEx( hdc, 3, 3, NULL );
2521 GetClientRect( hwnd, &clientArea );
2522 DPtoLP( hdc, (POINT *)&clientArea, 2 );
2523 testJustification(hdc, testText, &clientArea);
2525 GetTextExtentPoint32A(hdc, "A", 1, &expect);
2526 for (i = 0; i < 10; i++)
2528 SetTextCharacterExtra(hdc, i);
2529 GetTextExtentPoint32A(hdc, "A", 1, &size);
2530 ok( size.cx == expect.cx + i, "wrong size %d/%d+%d\n", size.cx, expect.cx, i );
2533 done:
2534 DeleteObject(hfont);
2535 ReleaseDC(hwnd, hdc);
2536 DestroyWindow(hwnd);
2539 static BOOL get_glyph_indices(INT charset, UINT code_page, WORD *idx, UINT count, BOOL unicode)
2541 HDC hdc;
2542 LOGFONTA lf;
2543 HFONT hfont, hfont_old;
2544 CHARSETINFO csi;
2545 FONTSIGNATURE fs;
2546 INT cs;
2547 DWORD i, ret;
2548 char name[64];
2550 assert(count <= 128);
2552 memset(&lf, 0, sizeof(lf));
2554 lf.lfCharSet = charset;
2555 lf.lfHeight = 10;
2556 lstrcpyA(lf.lfFaceName, "Arial");
2557 SetLastError(0xdeadbeef);
2558 hfont = CreateFontIndirectA(&lf);
2559 ok(hfont != 0, "CreateFontIndirectA error %u\n", GetLastError());
2561 hdc = GetDC(0);
2562 hfont_old = SelectObject(hdc, hfont);
2564 cs = GetTextCharsetInfo(hdc, &fs, 0);
2565 ok(cs == charset, "expected %d, got %d\n", charset, cs);
2567 SetLastError(0xdeadbeef);
2568 ret = GetTextFaceA(hdc, sizeof(name), name);
2569 ok(ret, "GetTextFaceA error %u\n", GetLastError());
2571 if (charset == SYMBOL_CHARSET)
2573 ok(strcmp("Arial", name), "face name should NOT be Arial\n");
2574 ok(fs.fsCsb[0] & (1u << 31), "symbol encoding should be available\n");
2576 else
2578 ok(!strcmp("Arial", name), "face name should be Arial, not %s\n", name);
2579 ok(!(fs.fsCsb[0] & (1u << 31)), "symbol encoding should NOT be available\n");
2582 if (!TranslateCharsetInfo((DWORD *)(INT_PTR)cs, &csi, TCI_SRCCHARSET))
2584 trace("Can't find codepage for charset %d\n", cs);
2585 ReleaseDC(0, hdc);
2586 return FALSE;
2588 ok(csi.ciACP == code_page, "expected %d, got %d\n", code_page, csi.ciACP);
2590 if (pGdiGetCodePage != NULL && pGdiGetCodePage(hdc) != code_page)
2592 skip("Font code page %d, looking for code page %d\n",
2593 pGdiGetCodePage(hdc), code_page);
2594 ReleaseDC(0, hdc);
2595 return FALSE;
2598 if (unicode)
2600 char ansi_buf[128];
2601 WCHAR unicode_buf[128];
2603 for (i = 0; i < count; i++) ansi_buf[i] = (BYTE)(i + 128);
2605 MultiByteToWideChar(code_page, 0, ansi_buf, count, unicode_buf, count);
2607 SetLastError(0xdeadbeef);
2608 ret = pGetGlyphIndicesW(hdc, unicode_buf, count, idx, 0);
2609 ok(ret == count, "GetGlyphIndicesW expected %d got %d, error %u\n",
2610 count, ret, GetLastError());
2612 else
2614 char ansi_buf[128];
2616 for (i = 0; i < count; i++) ansi_buf[i] = (BYTE)(i + 128);
2618 SetLastError(0xdeadbeef);
2619 ret = GetGlyphIndicesA(hdc, ansi_buf, count, idx, 0);
2620 ok(ret == count, "GetGlyphIndicesA expected %d got %d, error %u\n",
2621 count, ret, GetLastError());
2624 SelectObject(hdc, hfont_old);
2625 DeleteObject(hfont);
2627 ReleaseDC(0, hdc);
2629 return TRUE;
2632 static void test_font_charset(void)
2634 static struct charset_data
2636 INT charset;
2637 UINT code_page;
2638 WORD font_idxA[128], font_idxW[128];
2639 } cd[] =
2641 { ANSI_CHARSET, 1252 },
2642 { RUSSIAN_CHARSET, 1251 },
2643 { SYMBOL_CHARSET, CP_SYMBOL } /* keep it as the last one */
2645 int i;
2647 if (!pGetGlyphIndicesW)
2649 win_skip("Skipping the font charset test on a Win9x platform\n");
2650 return;
2653 if (!is_font_installed("Arial"))
2655 skip("Arial is not installed\n");
2656 return;
2659 for (i = 0; i < ARRAY_SIZE(cd); i++)
2661 if (cd[i].charset == SYMBOL_CHARSET)
2663 if (!is_font_installed("Symbol") && !is_font_installed("Wingdings"))
2665 skip("Symbol or Wingdings is not installed\n");
2666 break;
2669 if (get_glyph_indices(cd[i].charset, cd[i].code_page, cd[i].font_idxA, 128, FALSE) &&
2670 get_glyph_indices(cd[i].charset, cd[i].code_page, cd[i].font_idxW, 128, TRUE))
2671 ok(!memcmp(cd[i].font_idxA, cd[i].font_idxW, 128*sizeof(WORD)), "%d: indices don't match\n", i);
2674 ok(memcmp(cd[0].font_idxW, cd[1].font_idxW, 128*sizeof(WORD)), "0 vs 1: indices shouldn't match\n");
2675 if (i > 2)
2677 ok(memcmp(cd[0].font_idxW, cd[2].font_idxW, 128*sizeof(WORD)), "0 vs 2: indices shouldn't match\n");
2678 ok(memcmp(cd[1].font_idxW, cd[2].font_idxW, 128*sizeof(WORD)), "1 vs 2: indices shouldn't match\n");
2680 else
2681 skip("Symbol or Wingdings is not installed\n");
2684 static void test_GdiGetCodePage(void)
2686 static const struct _matching_data
2688 UINT current_codepage;
2689 LPCSTR lfFaceName;
2690 UCHAR lfCharSet;
2691 UINT expected_codepage;
2692 } matching_data[] = {
2693 {1251, "Arial", ANSI_CHARSET, 1252},
2694 {1251, "Tahoma", ANSI_CHARSET, 1252},
2696 {1252, "Arial", ANSI_CHARSET, 1252},
2697 {1252, "Tahoma", ANSI_CHARSET, 1252},
2699 {1253, "Arial", ANSI_CHARSET, 1252},
2700 {1253, "Tahoma", ANSI_CHARSET, 1252},
2702 { 932, "Arial", ANSI_CHARSET, 1252}, /* Japanese Windows returns 1252, not 932 */
2703 { 932, "Tahoma", ANSI_CHARSET, 1252},
2704 { 932, "MS UI Gothic", ANSI_CHARSET, 1252},
2706 { 936, "Arial", ANSI_CHARSET, 936},
2707 { 936, "Tahoma", ANSI_CHARSET, 936},
2708 { 936, "Simsun", ANSI_CHARSET, 936},
2710 { 949, "Arial", ANSI_CHARSET, 949},
2711 { 949, "Tahoma", ANSI_CHARSET, 949},
2712 { 949, "Gulim", ANSI_CHARSET, 949},
2714 { 950, "Arial", ANSI_CHARSET, 950},
2715 { 950, "Tahoma", ANSI_CHARSET, 950},
2716 { 950, "PMingLiU", ANSI_CHARSET, 950},
2718 HDC hdc;
2719 LOGFONTA lf;
2720 HFONT hfont;
2721 UINT acp;
2722 DWORD codepage;
2723 int i;
2725 if (!pGdiGetCodePage)
2727 skip("GdiGetCodePage not available on this platform\n");
2728 return;
2731 acp = GetACP();
2733 for (i = 0; i < ARRAY_SIZE(matching_data); i++)
2735 /* only test data matched current locale codepage */
2736 if (matching_data[i].current_codepage != acp)
2737 continue;
2739 if (!is_font_installed(matching_data[i].lfFaceName))
2741 skip("%s is not installed\n", matching_data[i].lfFaceName);
2742 continue;
2745 hdc = GetDC(0);
2747 memset(&lf, 0, sizeof(lf));
2748 lf.lfHeight = -16;
2749 lf.lfCharSet = matching_data[i].lfCharSet;
2750 lstrcpyA(lf.lfFaceName, matching_data[i].lfFaceName);
2751 hfont = CreateFontIndirectA(&lf);
2752 ok(hfont != 0, "CreateFontIndirectA error %u\n", GetLastError());
2754 hfont = SelectObject(hdc, hfont);
2755 codepage = pGdiGetCodePage(hdc);
2756 ok(codepage == matching_data[i].expected_codepage,
2757 "GdiGetCodePage should have returned %d, got %d\n", matching_data[i].expected_codepage, codepage);
2759 hfont = SelectObject(hdc, hfont);
2760 DeleteObject(hfont);
2762 /* CLIP_DFA_DISABLE turns off the font association */
2763 lf.lfClipPrecision = CLIP_DFA_DISABLE;
2764 hfont = CreateFontIndirectA(&lf);
2765 ok(hfont != 0, "CreateFontIndirectA error %u\n", GetLastError());
2767 hfont = SelectObject(hdc, hfont);
2768 codepage = pGdiGetCodePage(hdc);
2769 ok(codepage == 1252, "GdiGetCodePage returned %d\n", codepage);
2771 hfont = SelectObject(hdc, hfont);
2772 DeleteObject(hfont);
2774 ReleaseDC(NULL, hdc);
2778 static void test_GetFontUnicodeRanges(void)
2780 LOGFONTA lf;
2781 HDC hdc;
2782 HFONT hfont, hfont_old;
2783 DWORD size;
2784 GLYPHSET *gs;
2786 if (!pGetFontUnicodeRanges)
2788 win_skip("GetFontUnicodeRanges not available before W2K\n");
2789 return;
2792 memset(&lf, 0, sizeof(lf));
2793 lstrcpyA(lf.lfFaceName, "Arial");
2794 hfont = create_font("Arial", &lf);
2796 hdc = GetDC(0);
2797 hfont_old = SelectObject(hdc, hfont);
2799 size = pGetFontUnicodeRanges(NULL, NULL);
2800 ok(!size, "GetFontUnicodeRanges succeeded unexpectedly\n");
2802 size = pGetFontUnicodeRanges(hdc, NULL);
2803 ok(size, "GetFontUnicodeRanges failed unexpectedly\n");
2805 gs = heap_alloc_zero(size);
2807 size = pGetFontUnicodeRanges(hdc, gs);
2808 ok(size, "GetFontUnicodeRanges failed\n");
2809 ok(gs->cRanges, "Unexpected ranges count.\n");
2811 heap_free(gs);
2813 SelectObject(hdc, hfont_old);
2814 DeleteObject(hfont);
2815 ReleaseDC(NULL, hdc);
2818 struct enum_font_data
2820 int total, size;
2821 LOGFONTA *lf;
2824 struct enum_fullname_data
2826 int total, size;
2827 ENUMLOGFONTA *elf;
2830 struct enum_fullname_data_w
2832 int total, size;
2833 ENUMLOGFONTW *elf;
2836 struct enum_font_dataW
2838 int total, size;
2839 LOGFONTW *lf;
2842 static INT CALLBACK arial_enum_proc(const LOGFONTA *lf, const TEXTMETRICA *tm, DWORD type, LPARAM lParam)
2844 struct enum_font_data *efd = (struct enum_font_data *)lParam;
2845 const NEWTEXTMETRICA *ntm = (const NEWTEXTMETRICA *)tm;
2847 ok(lf->lfHeight == tm->tmHeight, "lfHeight %d != tmHeight %d\n", lf->lfHeight, tm->tmHeight);
2848 ok(lf->lfHeight > 0 && lf->lfHeight < 200, "enumerated font height %d\n", lf->lfHeight);
2850 if (type != TRUETYPE_FONTTYPE) return 1;
2852 ok(ntm->ntmCellHeight + ntm->ntmCellHeight/5 >= ntm->ntmSizeEM, "ntmCellHeight %d should be close to ntmSizeEM %d\n", ntm->ntmCellHeight, ntm->ntmSizeEM);
2854 if (efd->total >= efd->size)
2856 efd->size = max( (efd->total + 1) * 2, 256 );
2857 efd->lf = heap_realloc( efd->lf, efd->size * sizeof(*efd->lf) );
2858 if (!efd->lf) return 0;
2860 efd->lf[efd->total++] = *lf;
2862 return 1;
2865 static INT CALLBACK arial_enum_procw(const LOGFONTW *lf, const TEXTMETRICW *tm, DWORD type, LPARAM lParam)
2867 struct enum_font_dataW *efd = (struct enum_font_dataW *)lParam;
2868 const NEWTEXTMETRICW *ntm = (const NEWTEXTMETRICW *)tm;
2870 ok(lf->lfHeight == tm->tmHeight, "lfHeight %d != tmHeight %d\n", lf->lfHeight, tm->tmHeight);
2871 ok(lf->lfHeight > 0 && lf->lfHeight < 200, "enumerated font height %d\n", lf->lfHeight);
2873 if (type != TRUETYPE_FONTTYPE) return 1;
2875 ok(ntm->ntmCellHeight + ntm->ntmCellHeight/5 >= ntm->ntmSizeEM, "ntmCellHeight %d should be close to ntmSizeEM %d\n", ntm->ntmCellHeight, ntm->ntmSizeEM);
2877 if (efd->total >= efd->size)
2879 efd->size = max( (efd->total + 1) * 2, 256 );
2880 efd->lf = heap_realloc( efd->lf, efd->size * sizeof(*efd->lf) );
2881 if (!efd->lf) return 0;
2883 efd->lf[efd->total++] = *lf;
2885 return 1;
2888 static void get_charset_stats(struct enum_font_data *efd,
2889 int *ansi_charset, int *symbol_charset,
2890 int *russian_charset)
2892 int i;
2894 *ansi_charset = 0;
2895 *symbol_charset = 0;
2896 *russian_charset = 0;
2898 for (i = 0; i < efd->total; i++)
2900 switch (efd->lf[i].lfCharSet)
2902 case ANSI_CHARSET:
2903 (*ansi_charset)++;
2904 break;
2905 case SYMBOL_CHARSET:
2906 (*symbol_charset)++;
2907 break;
2908 case RUSSIAN_CHARSET:
2909 (*russian_charset)++;
2910 break;
2915 static void get_charset_statsW(struct enum_font_dataW *efd,
2916 int *ansi_charset, int *symbol_charset,
2917 int *russian_charset)
2919 int i;
2921 *ansi_charset = 0;
2922 *symbol_charset = 0;
2923 *russian_charset = 0;
2925 for (i = 0; i < efd->total; i++)
2927 switch (efd->lf[i].lfCharSet)
2929 case ANSI_CHARSET:
2930 (*ansi_charset)++;
2931 break;
2932 case SYMBOL_CHARSET:
2933 (*symbol_charset)++;
2934 break;
2935 case RUSSIAN_CHARSET:
2936 (*russian_charset)++;
2937 break;
2942 static void test_EnumFontFamilies(const char *font_name, INT font_charset)
2944 struct enum_font_data efd;
2945 struct enum_font_dataW efdw;
2946 LOGFONTA lf;
2947 HDC hdc;
2948 int i, ret, ansi_charset, symbol_charset, russian_charset;
2950 if (*font_name && !is_truetype_font_installed(font_name))
2952 skip("%s is not installed\n", font_name);
2953 return;
2955 memset( &efd, 0, sizeof(efd) );
2956 memset( &efdw, 0, sizeof(efdw) );
2958 hdc = GetDC(0);
2960 /* Observed behaviour: EnumFontFamilies enumerates aliases like "Arial Cyr"
2961 * while EnumFontFamiliesEx doesn't.
2963 if (!*font_name && font_charset == DEFAULT_CHARSET) /* do it only once */
2966 * Use EnumFontFamiliesW since win98 crashes when the
2967 * second parameter is NULL using EnumFontFamilies
2969 efdw.total = 0;
2970 SetLastError(0xdeadbeef);
2971 ret = EnumFontFamiliesW(hdc, NULL, arial_enum_procw, (LPARAM)&efdw);
2972 ok(ret || GetLastError() == ERROR_CALL_NOT_IMPLEMENTED, "EnumFontFamiliesW error %u\n", GetLastError());
2973 if(ret)
2975 get_charset_statsW(&efdw, &ansi_charset, &symbol_charset, &russian_charset);
2976 ok(efdw.total > 0, "fonts enumerated: NULL\n");
2977 ok(ansi_charset > 0, "NULL family should enumerate ANSI_CHARSET\n");
2978 ok(symbol_charset > 0, "NULL family should enumerate SYMBOL_CHARSET\n");
2979 ok(russian_charset > 0 ||
2980 broken(russian_charset == 0), /* NT4 */
2981 "NULL family should enumerate RUSSIAN_CHARSET\n");
2984 efdw.total = 0;
2985 SetLastError(0xdeadbeef);
2986 ret = EnumFontFamiliesExW(hdc, NULL, arial_enum_procw, (LPARAM)&efdw, 0);
2987 ok(ret || GetLastError() == ERROR_CALL_NOT_IMPLEMENTED, "EnumFontFamiliesExW error %u\n", GetLastError());
2988 if(ret)
2990 get_charset_statsW(&efdw, &ansi_charset, &symbol_charset, &russian_charset);
2991 ok(efdw.total > 0, "fonts enumerated: NULL\n");
2992 ok(ansi_charset > 0, "NULL family should enumerate ANSI_CHARSET\n");
2993 ok(symbol_charset > 0, "NULL family should enumerate SYMBOL_CHARSET\n");
2994 ok(russian_charset > 0, "NULL family should enumerate RUSSIAN_CHARSET\n");
2998 efd.total = 0;
2999 SetLastError(0xdeadbeef);
3000 ret = EnumFontFamiliesA(hdc, font_name, arial_enum_proc, (LPARAM)&efd);
3001 ok(ret, "EnumFontFamilies error %u\n", GetLastError());
3002 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
3003 if (*font_name)
3004 ok(efd.total > 0, "no fonts enumerated: %s\n", font_name);
3005 else
3006 ok(!efd.total, "no fonts should be enumerated for empty font_name\n");
3007 for (i = 0; i < efd.total; i++)
3009 /* FIXME: remove completely once Wine is fixed */
3010 todo_wine_if(efd.lf[i].lfCharSet != font_charset)
3011 ok(efd.lf[i].lfCharSet == font_charset, "%d: got charset %d\n", i, efd.lf[i].lfCharSet);
3012 ok(!strcmp(efd.lf[i].lfFaceName, font_name), "expected %s, got %s\n",
3013 font_name, efd.lf[i].lfFaceName);
3016 memset(&lf, 0, sizeof(lf));
3017 lf.lfCharSet = ANSI_CHARSET;
3018 strcpy(lf.lfFaceName, font_name);
3019 efd.total = 0;
3020 SetLastError(0xdeadbeef);
3021 ret = EnumFontFamiliesExA(hdc, &lf, arial_enum_proc, (LPARAM)&efd, 0);
3022 ok(ret, "EnumFontFamiliesEx error %u\n", GetLastError());
3023 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
3024 if (font_charset == SYMBOL_CHARSET)
3026 if (*font_name)
3027 ok(efd.total == 0, "no fonts should be enumerated: %s ANSI_CHARSET\n", font_name);
3028 else
3029 ok(efd.total > 0, "no fonts enumerated: %s\n", font_name);
3031 else
3033 ok(efd.total > 0, "no fonts enumerated: %s ANSI_CHARSET\n", font_name);
3034 for (i = 0; i < efd.total; i++)
3036 ok(efd.lf[i].lfCharSet == ANSI_CHARSET, "%d: got charset %d\n", i, efd.lf[i].lfCharSet);
3037 if (*font_name)
3038 ok(!strcmp(efd.lf[i].lfFaceName, font_name), "expected %s, got %s\n",
3039 font_name, efd.lf[i].lfFaceName);
3043 /* DEFAULT_CHARSET should enumerate all available charsets */
3044 memset(&lf, 0, sizeof(lf));
3045 lf.lfCharSet = DEFAULT_CHARSET;
3046 strcpy(lf.lfFaceName, font_name);
3047 efd.total = 0;
3048 SetLastError(0xdeadbeef);
3049 EnumFontFamiliesExA(hdc, &lf, arial_enum_proc, (LPARAM)&efd, 0);
3050 ok(ret, "EnumFontFamiliesEx error %u\n", GetLastError());
3051 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
3052 ok(efd.total > 0, "no fonts enumerated: %s DEFAULT_CHARSET\n", font_name);
3053 for (i = 0; i < efd.total; i++)
3055 if (*font_name)
3056 ok(!strcmp(efd.lf[i].lfFaceName, font_name), "expected %s, got %s\n",
3057 font_name, efd.lf[i].lfFaceName);
3059 if (*font_name)
3061 switch (font_charset)
3063 case ANSI_CHARSET:
3064 ok(ansi_charset > 0,
3065 "ANSI_CHARSET should enumerate ANSI_CHARSET for %s\n", font_name);
3066 ok(!symbol_charset,
3067 "ANSI_CHARSET should NOT enumerate SYMBOL_CHARSET for %s\n", font_name);
3068 ok(russian_charset > 0,
3069 "ANSI_CHARSET should enumerate RUSSIAN_CHARSET for %s\n", font_name);
3070 break;
3071 case SYMBOL_CHARSET:
3072 ok(!ansi_charset,
3073 "SYMBOL_CHARSET should NOT enumerate ANSI_CHARSET for %s\n", font_name);
3074 ok(symbol_charset,
3075 "SYMBOL_CHARSET should enumerate SYMBOL_CHARSET for %s\n", font_name);
3076 ok(!russian_charset,
3077 "SYMBOL_CHARSET should NOT enumerate RUSSIAN_CHARSET for %s\n", font_name);
3078 break;
3079 case DEFAULT_CHARSET:
3080 ok(ansi_charset > 0,
3081 "DEFAULT_CHARSET should enumerate ANSI_CHARSET for %s\n", font_name);
3082 ok(symbol_charset > 0,
3083 "DEFAULT_CHARSET should enumerate SYMBOL_CHARSET for %s\n", font_name);
3084 ok(russian_charset > 0,
3085 "DEFAULT_CHARSET should enumerate RUSSIAN_CHARSET for %s\n", font_name);
3086 break;
3089 else
3091 ok(ansi_charset > 0,
3092 "DEFAULT_CHARSET should enumerate ANSI_CHARSET for %s\n", *font_name ? font_name : "<empty>");
3093 ok(symbol_charset > 0,
3094 "DEFAULT_CHARSET should enumerate SYMBOL_CHARSET for %s\n", *font_name ? font_name : "<empty>");
3095 ok(russian_charset > 0,
3096 "DEFAULT_CHARSET should enumerate RUSSIAN_CHARSET for %s\n", *font_name ? font_name : "<empty>");
3099 memset(&lf, 0, sizeof(lf));
3100 lf.lfCharSet = SYMBOL_CHARSET;
3101 strcpy(lf.lfFaceName, font_name);
3102 efd.total = 0;
3103 SetLastError(0xdeadbeef);
3104 EnumFontFamiliesExA(hdc, &lf, arial_enum_proc, (LPARAM)&efd, 0);
3105 ok(ret, "EnumFontFamiliesEx error %u\n", GetLastError());
3106 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
3107 if (*font_name && font_charset == ANSI_CHARSET)
3108 ok(efd.total == 0, "no fonts should be enumerated: %s SYMBOL_CHARSET\n", font_name);
3109 else
3111 ok(efd.total > 0, "no fonts enumerated: %s SYMBOL_CHARSET\n", font_name);
3112 for (i = 0; i < efd.total; i++)
3114 ok(efd.lf[i].lfCharSet == SYMBOL_CHARSET, "%d: got charset %d\n", i, efd.lf[i].lfCharSet);
3115 if (*font_name)
3116 ok(!strcmp(efd.lf[i].lfFaceName, font_name), "expected %s, got %s\n",
3117 font_name, efd.lf[i].lfFaceName);
3120 ok(!ansi_charset,
3121 "SYMBOL_CHARSET should NOT enumerate ANSI_CHARSET for %s\n", *font_name ? font_name : "<empty>");
3122 ok(symbol_charset > 0,
3123 "SYMBOL_CHARSET should enumerate SYMBOL_CHARSET for %s\n", *font_name ? font_name : "<empty>");
3124 ok(!russian_charset,
3125 "SYMBOL_CHARSET should NOT enumerate RUSSIAN_CHARSET for %s\n", *font_name ? font_name : "<empty>");
3128 ReleaseDC(0, hdc);
3130 heap_free( efd.lf );
3131 heap_free( efdw.lf );
3134 static INT CALLBACK enum_multi_charset_font_proc(const LOGFONTA *lf, const TEXTMETRICA *tm, DWORD type, LPARAM lParam)
3136 const NEWTEXTMETRICEXA *ntm = (const NEWTEXTMETRICEXA *)tm;
3137 LOGFONTA *target = (LOGFONTA *)lParam;
3138 const DWORD valid_bits = 0x003f01ff;
3139 CHARSETINFO csi;
3140 DWORD fs;
3142 if (type != TRUETYPE_FONTTYPE) return TRUE;
3144 if (TranslateCharsetInfo(ULongToPtr(target->lfCharSet), &csi, TCI_SRCCHARSET)) {
3145 fs = ntm->ntmFontSig.fsCsb[0] & valid_bits;
3146 if ((fs & csi.fs.fsCsb[0]) && (fs & ~csi.fs.fsCsb[0]) && (fs & FS_LATIN1)) {
3147 *target = *lf;
3148 return FALSE;
3152 return TRUE;
3155 static INT CALLBACK enum_font_data_proc(const LOGFONTA *lf, const TEXTMETRICA *ntm, DWORD type, LPARAM lParam)
3157 struct enum_font_data *efd = (struct enum_font_data *)lParam;
3159 if (type != TRUETYPE_FONTTYPE) return 1;
3161 if (efd->total >= efd->size)
3163 efd->size = max( (efd->total + 1) * 2, 256 );
3164 efd->lf = heap_realloc( efd->lf, efd->size * sizeof(*efd->lf) );
3165 if (!efd->lf) return 0;
3167 efd->lf[efd->total++] = *lf;
3169 return 1;
3172 static INT CALLBACK enum_fullname_data_proc(const LOGFONTA *lf, const TEXTMETRICA *ntm, DWORD type, LPARAM lParam)
3174 struct enum_fullname_data *efnd = (struct enum_fullname_data *)lParam;
3176 if (type != TRUETYPE_FONTTYPE) return 1;
3178 if (efnd->total >= efnd->size)
3180 efnd->size = max( (efnd->total + 1) * 2, 256 );
3181 efnd->elf = heap_realloc( efnd->elf, efnd->size * sizeof(*efnd->elf) );
3182 if (!efnd->elf) return 0;
3184 efnd->elf[efnd->total++] = *(ENUMLOGFONTA *)lf;
3186 return 1;
3189 static INT CALLBACK enum_fullname_data_proc_w( const LOGFONTW *lf, const TEXTMETRICW *ntm, DWORD type, LPARAM lParam )
3191 struct enum_fullname_data_w *efnd = (struct enum_fullname_data_w *)lParam;
3193 if (type != TRUETYPE_FONTTYPE) return 1;
3195 if (efnd->total >= efnd->size)
3197 efnd->size = max( (efnd->total + 1) * 2, 256 );
3198 efnd->elf = heap_realloc( efnd->elf, efnd->size * sizeof(*efnd->elf) );
3199 if (!efnd->elf) return 0;
3201 efnd->elf[efnd->total++] = *(ENUMLOGFONTW *)lf;
3203 return 1;
3206 static void test_EnumFontFamiliesEx_default_charset(void)
3208 struct enum_font_data efd;
3209 LOGFONTA target, enum_font;
3210 UINT acp;
3211 HDC hdc;
3212 CHARSETINFO csi;
3214 acp = GetACP();
3215 if (!TranslateCharsetInfo(ULongToPtr(acp), &csi, TCI_SRCCODEPAGE)) {
3216 skip("TranslateCharsetInfo failed for code page %u.\n", acp);
3217 return;
3220 hdc = GetDC(0);
3221 memset(&enum_font, 0, sizeof(enum_font));
3222 enum_font.lfCharSet = csi.ciCharset;
3223 target.lfFaceName[0] = '\0';
3224 target.lfCharSet = csi.ciCharset;
3225 EnumFontFamiliesExA(hdc, &enum_font, enum_multi_charset_font_proc, (LPARAM)&target, 0);
3226 if (target.lfFaceName[0] == '\0') {
3227 skip("suitable font isn't found for charset %d.\n", enum_font.lfCharSet);
3228 return;
3230 if (acp == 874 || acp == 1255 || acp == 1256) {
3231 /* these codepage use complex script, expecting ANSI_CHARSET here. */
3232 target.lfCharSet = ANSI_CHARSET;
3235 memset(&efd, 0, sizeof(efd));
3236 memset(&enum_font, 0, sizeof(enum_font));
3237 strcpy(enum_font.lfFaceName, target.lfFaceName);
3238 enum_font.lfCharSet = DEFAULT_CHARSET;
3239 EnumFontFamiliesExA(hdc, &enum_font, enum_font_data_proc, (LPARAM)&efd, 0);
3240 ReleaseDC(0, hdc);
3242 if (efd.total < 2)
3243 ok(0, "EnumFontFamilies is broken. Expected >= 2, got %d.\n", efd.total);
3244 else
3245 ok(efd.lf[0].lfCharSet == target.lfCharSet,
3246 "(%s) got charset %d expected %d\n",
3247 efd.lf[0].lfFaceName, efd.lf[0].lfCharSet, target.lfCharSet);
3249 heap_free(efd.lf);
3250 return;
3253 static void test_negative_width(HDC hdc, const LOGFONTA *lf)
3255 HFONT hfont, hfont_prev;
3256 DWORD ret;
3257 GLYPHMETRICS gm1, gm2;
3258 LOGFONTA lf2 = *lf;
3259 WORD idx;
3261 /* negative widths are handled just as positive ones */
3262 lf2.lfWidth = -lf->lfWidth;
3264 SetLastError(0xdeadbeef);
3265 hfont = CreateFontIndirectA(lf);
3266 ok(hfont != 0, "CreateFontIndirect error %u\n", GetLastError());
3267 check_font("original", lf, hfont);
3269 hfont_prev = SelectObject(hdc, hfont);
3271 ret = GetGlyphIndicesA(hdc, "x", 1, &idx, GGI_MARK_NONEXISTING_GLYPHS);
3272 if (ret == GDI_ERROR || idx == 0xffff)
3274 SelectObject(hdc, hfont_prev);
3275 DeleteObject(hfont);
3276 skip("Font %s doesn't contain 'x', skipping the test\n", lf->lfFaceName);
3277 return;
3280 /* filling with 0xaa causes false pass under WINEDEBUG=warn+heap */
3281 memset(&gm1, 0xab, sizeof(gm1));
3282 SetLastError(0xdeadbeef);
3283 ret = GetGlyphOutlineA(hdc, 'x', GGO_METRICS, &gm1, 0, NULL, &mat);
3284 ok(ret != GDI_ERROR, "GetGlyphOutline error 0x%x\n", GetLastError());
3286 SelectObject(hdc, hfont_prev);
3287 DeleteObject(hfont);
3289 SetLastError(0xdeadbeef);
3290 hfont = CreateFontIndirectA(&lf2);
3291 ok(hfont != 0, "CreateFontIndirect error %u\n", GetLastError());
3292 check_font("negative width", &lf2, hfont);
3294 hfont_prev = SelectObject(hdc, hfont);
3296 memset(&gm2, 0xbb, sizeof(gm2));
3297 SetLastError(0xdeadbeef);
3298 ret = GetGlyphOutlineA(hdc, 'x', GGO_METRICS, &gm2, 0, NULL, &mat);
3299 ok(ret != GDI_ERROR, "GetGlyphOutline error 0x%x\n", GetLastError());
3301 SelectObject(hdc, hfont_prev);
3302 DeleteObject(hfont);
3304 ok(gm1.gmBlackBoxX == gm2.gmBlackBoxX &&
3305 gm1.gmBlackBoxY == gm2.gmBlackBoxY &&
3306 gm1.gmptGlyphOrigin.x == gm2.gmptGlyphOrigin.x &&
3307 gm1.gmptGlyphOrigin.y == gm2.gmptGlyphOrigin.y &&
3308 gm1.gmCellIncX == gm2.gmCellIncX &&
3309 gm1.gmCellIncY == gm2.gmCellIncY,
3310 "gm1=%d,%d,%d,%d,%d,%d gm2=%d,%d,%d,%d,%d,%d\n",
3311 gm1.gmBlackBoxX, gm1.gmBlackBoxY, gm1.gmptGlyphOrigin.x,
3312 gm1.gmptGlyphOrigin.y, gm1.gmCellIncX, gm1.gmCellIncY,
3313 gm2.gmBlackBoxX, gm2.gmBlackBoxY, gm2.gmptGlyphOrigin.x,
3314 gm2.gmptGlyphOrigin.y, gm2.gmCellIncX, gm2.gmCellIncY);
3317 /* PANOSE is 10 bytes in size, need to pack the structure properly */
3318 #include "pshpack2.h"
3319 typedef struct
3321 USHORT version;
3322 SHORT xAvgCharWidth;
3323 USHORT usWeightClass;
3324 USHORT usWidthClass;
3325 SHORT fsType;
3326 SHORT ySubscriptXSize;
3327 SHORT ySubscriptYSize;
3328 SHORT ySubscriptXOffset;
3329 SHORT ySubscriptYOffset;
3330 SHORT ySuperscriptXSize;
3331 SHORT ySuperscriptYSize;
3332 SHORT ySuperscriptXOffset;
3333 SHORT ySuperscriptYOffset;
3334 SHORT yStrikeoutSize;
3335 SHORT yStrikeoutPosition;
3336 SHORT sFamilyClass;
3337 PANOSE panose;
3338 ULONG ulUnicodeRange1;
3339 ULONG ulUnicodeRange2;
3340 ULONG ulUnicodeRange3;
3341 ULONG ulUnicodeRange4;
3342 CHAR achVendID[4];
3343 USHORT fsSelection;
3344 USHORT usFirstCharIndex;
3345 USHORT usLastCharIndex;
3346 /* According to the Apple spec, original version didn't have the below fields,
3347 * version numbers were taken from the OpenType spec.
3349 /* version 0 (TrueType 1.5) */
3350 USHORT sTypoAscender;
3351 USHORT sTypoDescender;
3352 USHORT sTypoLineGap;
3353 USHORT usWinAscent;
3354 USHORT usWinDescent;
3355 /* version 1 (TrueType 1.66) */
3356 ULONG ulCodePageRange1;
3357 ULONG ulCodePageRange2;
3358 /* version 2 (OpenType 1.2) */
3359 SHORT sxHeight;
3360 SHORT sCapHeight;
3361 USHORT usDefaultChar;
3362 USHORT usBreakChar;
3363 USHORT usMaxContext;
3364 /* version 4 (OpenType 1.6) */
3365 USHORT usLowerOpticalPointSize;
3366 USHORT usUpperOpticalPointSize;
3367 } TT_OS2_V4;
3368 #include "poppack.h"
3370 #define TT_OS2_V0_SIZE (FIELD_OFFSET(TT_OS2_V4, ulCodePageRange1))
3372 typedef struct
3374 USHORT version;
3375 USHORT num_tables;
3376 } cmap_header;
3378 typedef struct
3380 USHORT plat_id;
3381 USHORT enc_id;
3382 ULONG offset;
3383 } cmap_encoding_record;
3385 typedef struct
3387 USHORT format;
3388 USHORT length;
3389 USHORT language;
3391 BYTE glyph_ids[256];
3392 } cmap_format_0;
3394 typedef struct
3396 USHORT format;
3397 USHORT length;
3398 USHORT language;
3400 USHORT seg_countx2;
3401 USHORT search_range;
3402 USHORT entry_selector;
3403 USHORT range_shift;
3405 USHORT end_count[1]; /* this is a variable-sized array of length seg_countx2 / 2 */
3406 /* Then follows:
3407 USHORT pad;
3408 USHORT start_count[seg_countx2 / 2];
3409 USHORT id_delta[seg_countx2 / 2];
3410 USHORT id_range_offset[seg_countx2 / 2];
3411 USHORT glyph_ids[];
3413 } cmap_format_4;
3415 typedef struct
3417 USHORT end_count;
3418 USHORT start_count;
3419 USHORT id_delta;
3420 USHORT id_range_offset;
3421 } cmap_format_4_seg;
3423 static void expect_ff(const TEXTMETRICA *tmA, const TT_OS2_V4 *os2, WORD family, const char *name)
3425 ok((tmA->tmPitchAndFamily & 0xf0) == family ||
3426 broken(PRIMARYLANGID(GetSystemDefaultLangID()) != LANG_ENGLISH),
3427 "%s: expected family %02x got %02x. panose %d-%d-%d-%d-...\n",
3428 name, family, tmA->tmPitchAndFamily, os2->panose.bFamilyType, os2->panose.bSerifStyle,
3429 os2->panose.bWeight, os2->panose.bProportion);
3432 static BOOL get_first_last_from_cmap0(void *ptr, DWORD *first, DWORD *last)
3434 int i;
3435 cmap_format_0 *cmap = (cmap_format_0*)ptr;
3437 *first = 256;
3439 for(i = 0; i < 256; i++)
3441 if(cmap->glyph_ids[i] == 0) continue;
3442 *last = i;
3443 if(*first == 256) *first = i;
3445 if(*first == 256) return FALSE;
3446 return TRUE;
3449 static void get_seg4(cmap_format_4 *cmap, USHORT seg_num, cmap_format_4_seg *seg)
3451 USHORT segs = GET_BE_WORD(cmap->seg_countx2) / 2;
3452 seg->end_count = GET_BE_WORD(cmap->end_count[seg_num]);
3453 seg->start_count = GET_BE_WORD(cmap->end_count[segs + 1 + seg_num]);
3454 seg->id_delta = GET_BE_WORD(cmap->end_count[2 * segs + 1 + seg_num]);
3455 seg->id_range_offset = GET_BE_WORD(cmap->end_count[3 * segs + 1 + seg_num]);
3458 static BOOL get_first_last_from_cmap4(void *ptr, DWORD *first, DWORD *last, DWORD limit)
3460 int i;
3461 cmap_format_4 *cmap = (cmap_format_4*)ptr;
3462 USHORT seg_count = GET_BE_WORD(cmap->seg_countx2) / 2;
3464 *first = 0x10000;
3466 for(i = 0; i < seg_count; i++)
3468 cmap_format_4_seg seg;
3470 get_seg4(cmap, i, &seg);
3472 if(seg.start_count > 0xfffe) break;
3474 if(*first == 0x10000) *first = seg.start_count;
3476 *last = min(seg.end_count, 0xfffe);
3479 if(*first == 0x10000) return FALSE;
3480 return TRUE;
3483 static void *get_cmap(cmap_header *header, USHORT plat_id, USHORT enc_id)
3485 USHORT i;
3486 cmap_encoding_record *record = (cmap_encoding_record *)(header + 1);
3488 for(i = 0; i < GET_BE_WORD(header->num_tables); i++)
3490 if(GET_BE_WORD(record->plat_id) == plat_id && GET_BE_WORD(record->enc_id) == enc_id)
3491 return (BYTE *)header + GET_BE_DWORD(record->offset);
3492 record++;
3494 return NULL;
3497 typedef enum
3499 cmap_none,
3500 cmap_ms_unicode,
3501 cmap_ms_symbol
3502 } cmap_type;
3504 static BOOL get_first_last_from_cmap(HDC hdc, DWORD *first, DWORD *last, cmap_type *cmap_type)
3506 LONG size, ret;
3507 cmap_header *header;
3508 void *cmap;
3509 BOOL r = FALSE;
3510 WORD format;
3512 size = GetFontData(hdc, MS_CMAP_TAG, 0, NULL, 0);
3513 ok(size != GDI_ERROR, "no cmap table found\n");
3514 if(size == GDI_ERROR) return FALSE;
3516 header = HeapAlloc(GetProcessHeap(), 0, size);
3517 ret = GetFontData(hdc, MS_CMAP_TAG, 0, header, size);
3518 ok(ret == size, "GetFontData should return %u not %u\n", size, ret);
3519 ok(GET_BE_WORD(header->version) == 0, "got cmap version %d\n", GET_BE_WORD(header->version));
3521 cmap = get_cmap(header, 3, 1);
3522 if(cmap)
3523 *cmap_type = cmap_ms_unicode;
3524 else
3526 cmap = get_cmap(header, 3, 0);
3527 if(cmap) *cmap_type = cmap_ms_symbol;
3529 if(!cmap)
3531 *cmap_type = cmap_none;
3532 goto end;
3535 format = GET_BE_WORD(*(WORD *)cmap);
3536 switch(format)
3538 case 0:
3539 r = get_first_last_from_cmap0(cmap, first, last);
3540 break;
3541 case 4:
3542 r = get_first_last_from_cmap4(cmap, first, last, size);
3543 break;
3544 default:
3545 skip("unhandled cmap format %d\n", format);
3546 break;
3549 end:
3550 HeapFree(GetProcessHeap(), 0, header);
3551 return r;
3554 #define TT_PLATFORM_APPLE_UNICODE 0
3555 #define TT_PLATFORM_MACINTOSH 1
3556 #define TT_PLATFORM_MICROSOFT 3
3557 #define TT_APPLE_ID_DEFAULT 0
3558 #define TT_APPLE_ID_ISO_10646 2
3559 #define TT_APPLE_ID_UNICODE_2_0 3
3560 #define TT_MS_ID_SYMBOL_CS 0
3561 #define TT_MS_ID_UNICODE_CS 1
3562 #define TT_MS_LANGID_ENGLISH_UNITED_STATES 0x0409
3563 #define TT_NAME_ID_FONT_FAMILY 1
3564 #define TT_NAME_ID_FONT_SUBFAMILY 2
3565 #define TT_NAME_ID_UNIQUE_ID 3
3566 #define TT_NAME_ID_FULL_NAME 4
3567 #define TT_MAC_ID_SIMPLIFIED_CHINESE 25
3569 typedef struct sfnt_name
3571 USHORT platform_id;
3572 USHORT encoding_id;
3573 USHORT language_id;
3574 USHORT name_id;
3575 USHORT length;
3576 USHORT offset;
3577 } sfnt_name;
3579 static const LANGID mac_langid_table[] =
3581 MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ENGLISH */
3582 MAKELANGID(LANG_FRENCH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_FRENCH */
3583 MAKELANGID(LANG_GERMAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_GERMAN */
3584 MAKELANGID(LANG_ITALIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ITALIAN */
3585 MAKELANGID(LANG_DUTCH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_DUTCH */
3586 MAKELANGID(LANG_SWEDISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SWEDISH */
3587 MAKELANGID(LANG_SPANISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SPANISH */
3588 MAKELANGID(LANG_DANISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_DANISH */
3589 MAKELANGID(LANG_PORTUGUESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_PORTUGUESE */
3590 MAKELANGID(LANG_NORWEGIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_NORWEGIAN */
3591 MAKELANGID(LANG_HEBREW,SUBLANG_DEFAULT), /* TT_MAC_LANGID_HEBREW */
3592 MAKELANGID(LANG_JAPANESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_JAPANESE */
3593 MAKELANGID(LANG_ARABIC,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ARABIC */
3594 MAKELANGID(LANG_FINNISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_FINNISH */
3595 MAKELANGID(LANG_GREEK,SUBLANG_DEFAULT), /* TT_MAC_LANGID_GREEK */
3596 MAKELANGID(LANG_ICELANDIC,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ICELANDIC */
3597 MAKELANGID(LANG_MALTESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MALTESE */
3598 MAKELANGID(LANG_TURKISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TURKISH */
3599 MAKELANGID(LANG_CROATIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_CROATIAN */
3600 MAKELANGID(LANG_CHINESE_TRADITIONAL,SUBLANG_DEFAULT), /* TT_MAC_LANGID_CHINESE_TRADITIONAL */
3601 MAKELANGID(LANG_URDU,SUBLANG_DEFAULT), /* TT_MAC_LANGID_URDU */
3602 MAKELANGID(LANG_HINDI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_HINDI */
3603 MAKELANGID(LANG_THAI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_THAI */
3604 MAKELANGID(LANG_KOREAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_KOREAN */
3605 MAKELANGID(LANG_LITHUANIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_LITHUANIAN */
3606 MAKELANGID(LANG_POLISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_POLISH */
3607 MAKELANGID(LANG_HUNGARIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_HUNGARIAN */
3608 MAKELANGID(LANG_ESTONIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ESTONIAN */
3609 MAKELANGID(LANG_LATVIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_LETTISH */
3610 MAKELANGID(LANG_SAMI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SAAMISK */
3611 MAKELANGID(LANG_FAEROESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_FAEROESE */
3612 MAKELANGID(LANG_FARSI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_FARSI */
3613 MAKELANGID(LANG_RUSSIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_RUSSIAN */
3614 MAKELANGID(LANG_CHINESE_SIMPLIFIED,SUBLANG_DEFAULT), /* TT_MAC_LANGID_CHINESE_SIMPLIFIED */
3615 MAKELANGID(LANG_DUTCH,SUBLANG_DUTCH_BELGIAN), /* TT_MAC_LANGID_FLEMISH */
3616 MAKELANGID(LANG_IRISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_IRISH */
3617 MAKELANGID(LANG_ALBANIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ALBANIAN */
3618 MAKELANGID(LANG_ROMANIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ROMANIAN */
3619 MAKELANGID(LANG_CZECH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_CZECH */
3620 MAKELANGID(LANG_SLOVAK,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SLOVAK */
3621 MAKELANGID(LANG_SLOVENIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SLOVENIAN */
3622 0, /* TT_MAC_LANGID_YIDDISH */
3623 MAKELANGID(LANG_SERBIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SERBIAN */
3624 MAKELANGID(LANG_MACEDONIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MACEDONIAN */
3625 MAKELANGID(LANG_BULGARIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_BULGARIAN */
3626 MAKELANGID(LANG_UKRAINIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_UKRAINIAN */
3627 MAKELANGID(LANG_BELARUSIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_BYELORUSSIAN */
3628 MAKELANGID(LANG_UZBEK,SUBLANG_DEFAULT), /* TT_MAC_LANGID_UZBEK */
3629 MAKELANGID(LANG_KAZAK,SUBLANG_DEFAULT), /* TT_MAC_LANGID_KAZAKH */
3630 MAKELANGID(LANG_AZERI,SUBLANG_AZERI_CYRILLIC), /* TT_MAC_LANGID_AZERBAIJANI */
3631 0, /* TT_MAC_LANGID_AZERBAIJANI_ARABIC_SCRIPT */
3632 MAKELANGID(LANG_ARMENIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ARMENIAN */
3633 MAKELANGID(LANG_GEORGIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_GEORGIAN */
3634 0, /* TT_MAC_LANGID_MOLDAVIAN */
3635 MAKELANGID(LANG_KYRGYZ,SUBLANG_DEFAULT), /* TT_MAC_LANGID_KIRGHIZ */
3636 MAKELANGID(LANG_TAJIK,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TAJIKI */
3637 MAKELANGID(LANG_TURKMEN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TURKMEN */
3638 MAKELANGID(LANG_MONGOLIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MONGOLIAN */
3639 MAKELANGID(LANG_MONGOLIAN,SUBLANG_MONGOLIAN_CYRILLIC_MONGOLIA), /* TT_MAC_LANGID_MONGOLIAN_CYRILLIC_SCRIPT */
3640 MAKELANGID(LANG_PASHTO,SUBLANG_DEFAULT), /* TT_MAC_LANGID_PASHTO */
3641 0, /* TT_MAC_LANGID_KURDISH */
3642 MAKELANGID(LANG_KASHMIRI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_KASHMIRI */
3643 MAKELANGID(LANG_SINDHI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SINDHI */
3644 MAKELANGID(LANG_TIBETAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TIBETAN */
3645 MAKELANGID(LANG_NEPALI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_NEPALI */
3646 MAKELANGID(LANG_SANSKRIT,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SANSKRIT */
3647 MAKELANGID(LANG_MARATHI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MARATHI */
3648 MAKELANGID(LANG_BENGALI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_BENGALI */
3649 MAKELANGID(LANG_ASSAMESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ASSAMESE */
3650 MAKELANGID(LANG_GUJARATI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_GUJARATI */
3651 MAKELANGID(LANG_PUNJABI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_PUNJABI */
3652 MAKELANGID(LANG_ORIYA,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ORIYA */
3653 MAKELANGID(LANG_MALAYALAM,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MALAYALAM */
3654 MAKELANGID(LANG_KANNADA,SUBLANG_DEFAULT), /* TT_MAC_LANGID_KANNADA */
3655 MAKELANGID(LANG_TAMIL,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TAMIL */
3656 MAKELANGID(LANG_TELUGU,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TELUGU */
3657 MAKELANGID(LANG_SINHALESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SINHALESE */
3658 0, /* TT_MAC_LANGID_BURMESE */
3659 MAKELANGID(LANG_KHMER,SUBLANG_DEFAULT), /* TT_MAC_LANGID_KHMER */
3660 MAKELANGID(LANG_LAO,SUBLANG_DEFAULT), /* TT_MAC_LANGID_LAO */
3661 MAKELANGID(LANG_VIETNAMESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_VIETNAMESE */
3662 MAKELANGID(LANG_INDONESIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_INDONESIAN */
3663 0, /* TT_MAC_LANGID_TAGALOG */
3664 MAKELANGID(LANG_MALAY,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MALAY_ROMAN_SCRIPT */
3665 0, /* TT_MAC_LANGID_MALAY_ARABIC_SCRIPT */
3666 MAKELANGID(LANG_AMHARIC,SUBLANG_DEFAULT), /* TT_MAC_LANGID_AMHARIC */
3667 MAKELANGID(LANG_TIGRIGNA,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TIGRINYA */
3668 0, /* TT_MAC_LANGID_GALLA */
3669 0, /* TT_MAC_LANGID_SOMALI */
3670 MAKELANGID(LANG_SWAHILI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SWAHILI */
3671 0, /* TT_MAC_LANGID_RUANDA */
3672 0, /* TT_MAC_LANGID_RUNDI */
3673 0, /* TT_MAC_LANGID_CHEWA */
3674 MAKELANGID(LANG_MALAGASY,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MALAGASY */
3675 MAKELANGID(LANG_ESPERANTO,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ESPERANTO */
3676 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 95-111 */
3677 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 112-127 */
3678 MAKELANGID(LANG_WELSH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_WELSH */
3679 MAKELANGID(LANG_BASQUE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_BASQUE */
3680 MAKELANGID(LANG_CATALAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_CATALAN */
3681 0, /* TT_MAC_LANGID_LATIN */
3682 MAKELANGID(LANG_QUECHUA,SUBLANG_DEFAULT), /* TT_MAC_LANGID_QUECHUA */
3683 0, /* TT_MAC_LANGID_GUARANI */
3684 0, /* TT_MAC_LANGID_AYMARA */
3685 MAKELANGID(LANG_TATAR,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TATAR */
3686 MAKELANGID(LANG_UIGHUR,SUBLANG_DEFAULT), /* TT_MAC_LANGID_UIGHUR */
3687 0, /* TT_MAC_LANGID_DZONGKHA */
3688 0, /* TT_MAC_LANGID_JAVANESE */
3689 0, /* TT_MAC_LANGID_SUNDANESE */
3690 MAKELANGID(LANG_GALICIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_GALICIAN */
3691 MAKELANGID(LANG_AFRIKAANS,SUBLANG_DEFAULT), /* TT_MAC_LANGID_AFRIKAANS */
3692 MAKELANGID(LANG_BRETON,SUBLANG_DEFAULT), /* TT_MAC_LANGID_BRETON */
3693 MAKELANGID(LANG_INUKTITUT,SUBLANG_DEFAULT), /* TT_MAC_LANGID_INUKTITUT */
3694 MAKELANGID(LANG_SCOTTISH_GAELIC,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SCOTTISH_GAELIC */
3695 MAKELANGID(LANG_MANX_GAELIC,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MANX_GAELIC */
3696 MAKELANGID(LANG_IRISH,SUBLANG_IRISH_IRELAND), /* TT_MAC_LANGID_IRISH_GAELIC */
3697 0, /* TT_MAC_LANGID_TONGAN */
3698 0, /* TT_MAC_LANGID_GREEK_POLYTONIC */
3699 MAKELANGID(LANG_GREENLANDIC,SUBLANG_DEFAULT), /* TT_MAC_LANGID_GREELANDIC */
3700 MAKELANGID(LANG_AZERI,SUBLANG_AZERI_LATIN), /* TT_MAC_LANGID_AZERBAIJANI_ROMAN_SCRIPT */
3703 static inline WORD get_mac_code_page( const sfnt_name *name )
3705 if (GET_BE_WORD(name->encoding_id) == TT_MAC_ID_SIMPLIFIED_CHINESE) return 10008; /* special case */
3706 return 10000 + GET_BE_WORD(name->encoding_id);
3709 static int match_name_table_language( const sfnt_name *name, LANGID lang )
3711 LANGID name_lang;
3712 int res = 0;
3714 switch (GET_BE_WORD(name->platform_id))
3716 case TT_PLATFORM_MICROSOFT:
3717 res += 5; /* prefer the Microsoft name */
3718 switch (GET_BE_WORD(name->encoding_id))
3720 case TT_MS_ID_UNICODE_CS:
3721 case TT_MS_ID_SYMBOL_CS:
3722 name_lang = GET_BE_WORD(name->language_id);
3723 break;
3724 default:
3725 return 0;
3727 break;
3728 case TT_PLATFORM_MACINTOSH:
3729 if (!IsValidCodePage( get_mac_code_page( name ))) return 0;
3730 if (GET_BE_WORD(name->language_id) >= ARRAY_SIZE(mac_langid_table)) return 0;
3731 name_lang = mac_langid_table[GET_BE_WORD(name->language_id)];
3732 break;
3733 case TT_PLATFORM_APPLE_UNICODE:
3734 res += 2; /* prefer Unicode encodings */
3735 switch (GET_BE_WORD(name->encoding_id))
3737 case TT_APPLE_ID_DEFAULT:
3738 case TT_APPLE_ID_ISO_10646:
3739 case TT_APPLE_ID_UNICODE_2_0:
3740 if (GET_BE_WORD(name->language_id) >= ARRAY_SIZE(mac_langid_table)) return 0;
3741 name_lang = mac_langid_table[GET_BE_WORD(name->language_id)];
3742 break;
3743 default:
3744 return 0;
3746 break;
3747 default:
3748 return 0;
3750 if (name_lang == lang) res += 30;
3751 else if (PRIMARYLANGID( name_lang ) == PRIMARYLANGID( lang )) res += 20;
3752 else if (name_lang == MAKELANGID( LANG_ENGLISH, SUBLANG_DEFAULT )) res += 10;
3753 return res;
3756 static BOOL get_ttf_nametable_entry(HDC hdc, WORD name_id, WCHAR *out_buf, SIZE_T out_size, LCID language_id)
3758 struct sfnt_name_header
3760 USHORT format;
3761 USHORT number_of_record;
3762 USHORT storage_offset;
3763 } *header;
3764 sfnt_name *entry;
3765 BOOL r = FALSE;
3766 LONG size, offset, length;
3767 LONG c, ret;
3768 WCHAR *name;
3769 BYTE *data;
3770 USHORT i;
3771 int res, best_lang = 0, best_index = -1;
3773 size = GetFontData(hdc, MS_NAME_TAG, 0, NULL, 0);
3774 ok(size != GDI_ERROR, "no name table found\n");
3775 if(size == GDI_ERROR) return FALSE;
3777 data = HeapAlloc(GetProcessHeap(), 0, size);
3778 ret = GetFontData(hdc, MS_NAME_TAG, 0, data, size);
3779 ok(ret == size, "GetFontData should return %u not %u\n", size, ret);
3781 header = (void *)data;
3782 header->format = GET_BE_WORD(header->format);
3783 header->number_of_record = GET_BE_WORD(header->number_of_record);
3784 header->storage_offset = GET_BE_WORD(header->storage_offset);
3785 if (header->format != 0)
3787 skip("got format %u\n", header->format);
3788 goto out;
3790 if (header->number_of_record == 0 || sizeof(*header) + header->number_of_record * sizeof(*entry) > size)
3792 skip("number records out of range: %d\n", header->number_of_record);
3793 goto out;
3795 if (header->storage_offset >= size)
3797 skip("storage_offset %u > size %u\n", header->storage_offset, size);
3798 goto out;
3801 entry = (void *)&header[1];
3802 for (i = 0; i < header->number_of_record; i++)
3804 if (GET_BE_WORD(entry[i].name_id) != name_id) continue;
3805 res = match_name_table_language( &entry[i], language_id);
3806 if (res > best_lang)
3808 best_lang = res;
3809 best_index = i;
3813 offset = header->storage_offset + GET_BE_WORD(entry[best_index].offset);
3814 length = GET_BE_WORD(entry[best_index].length);
3815 if (offset + length > size)
3817 skip("entry %d is out of range\n", best_index);
3818 goto out;
3820 if (length >= out_size)
3822 skip("buffer too small for entry %d\n", best_index);
3823 goto out;
3826 name = (WCHAR *)(data + offset);
3827 for (c = 0; c < length / 2; c++)
3828 out_buf[c] = GET_BE_WORD(name[c]);
3829 out_buf[c] = 0;
3831 r = TRUE;
3833 out:
3834 HeapFree(GetProcessHeap(), 0, data);
3835 return r;
3838 static void test_text_metrics(const LOGFONTA *lf, const NEWTEXTMETRICA *ntm)
3840 HDC hdc;
3841 HFONT hfont, hfont_old;
3842 TEXTMETRICA tmA;
3843 TT_OS2_V4 tt_os2;
3844 LONG size, ret;
3845 const char *font_name = lf->lfFaceName;
3846 DWORD cmap_first = 0, cmap_last = 0;
3847 UINT ascent, descent, cell_height;
3848 cmap_type cmap_type;
3849 BOOL sys_lang_non_english;
3851 sys_lang_non_english = PRIMARYLANGID(GetSystemDefaultLangID()) != LANG_ENGLISH;
3852 hdc = GetDC(0);
3854 SetLastError(0xdeadbeef);
3855 hfont = CreateFontIndirectA(lf);
3856 ok(hfont != 0, "CreateFontIndirect error %u\n", GetLastError());
3858 hfont_old = SelectObject(hdc, hfont);
3860 size = GetFontData(hdc, MS_OS2_TAG, 0, NULL, 0);
3861 if (size == GDI_ERROR)
3863 trace("OS/2 chunk was not found\n");
3864 goto end_of_test;
3866 if (size > sizeof(tt_os2))
3868 trace("got too large OS/2 chunk of size %u\n", size);
3869 size = sizeof(tt_os2);
3872 memset(&tt_os2, 0, sizeof(tt_os2));
3873 ret = GetFontData(hdc, MS_OS2_TAG, 0, &tt_os2, size);
3874 ok(ret >= TT_OS2_V0_SIZE && ret <= size, "GetFontData should return size from [%u,%u] not %u\n", TT_OS2_V0_SIZE,
3875 size, ret);
3877 SetLastError(0xdeadbeef);
3878 ret = GetTextMetricsA(hdc, &tmA);
3879 ok(ret, "GetTextMetricsA error %u\n", GetLastError());
3881 if(!get_first_last_from_cmap(hdc, &cmap_first, &cmap_last, &cmap_type))
3883 skip("%s is not a Windows font, OS/2 metrics may be invalid.\n",font_name);
3885 else
3887 USHORT expect_first_A, expect_last_A, expect_break_A, expect_default_A;
3888 USHORT expect_first_W, expect_last_W, expect_break_W, expect_default_W;
3889 UINT os2_first_char, os2_last_char, default_char, break_char;
3890 USHORT version;
3891 TEXTMETRICW tmW;
3893 ascent = GET_BE_WORD(tt_os2.usWinAscent);
3894 descent = abs((SHORT)GET_BE_WORD(tt_os2.usWinDescent));
3895 cell_height = ascent + descent;
3896 ok(ntm->ntmCellHeight == cell_height, "%s: ntmCellHeight %u != %u, os2.usWinAscent/os2.usWinDescent %u/%u\n",
3897 font_name, ntm->ntmCellHeight, cell_height, ascent, descent);
3899 version = GET_BE_WORD(tt_os2.version);
3901 os2_first_char = GET_BE_WORD(tt_os2.usFirstCharIndex);
3902 os2_last_char = GET_BE_WORD(tt_os2.usLastCharIndex);
3903 default_char = GET_BE_WORD(tt_os2.usDefaultChar);
3904 break_char = GET_BE_WORD(tt_os2.usBreakChar);
3906 if (winetest_debug > 1)
3907 trace("font %s charset %u: %x-%x (%x-%x) default %x break %x OS/2 version %u vendor %4.4s\n",
3908 font_name, lf->lfCharSet, os2_first_char, os2_last_char, cmap_first, cmap_last,
3909 default_char, break_char, version, (LPCSTR)&tt_os2.achVendID);
3911 if (cmap_type == cmap_ms_symbol || (cmap_first >= 0xf000 && cmap_first < 0xf100))
3913 expect_first_W = 0;
3914 switch(GetACP())
3916 case 1255: /* Hebrew */
3917 expect_last_W = 0xf896;
3918 break;
3919 case 1257: /* Baltic */
3920 expect_last_W = 0xf8fd;
3921 break;
3922 default:
3923 expect_last_W = 0xf0ff;
3925 expect_break_W = 0x20;
3926 expect_default_W = expect_break_W - 1;
3927 expect_first_A = 0x1e;
3928 expect_last_A = min(os2_last_char - os2_first_char + 0x20, 0xff);
3930 else
3932 expect_first_W = cmap_first;
3933 expect_last_W = cmap_last;
3934 if(os2_first_char <= 1)
3935 expect_break_W = os2_first_char + 2;
3936 else if(os2_first_char > 0xff)
3937 expect_break_W = 0x20;
3938 else
3939 expect_break_W = os2_first_char;
3940 expect_default_W = expect_break_W - 1;
3941 expect_first_A = expect_default_W - 1;
3942 expect_last_A = min(expect_last_W, 0xff);
3944 expect_break_A = expect_break_W;
3945 expect_default_A = expect_default_W;
3947 /* Wine currently uses SYMBOL_CHARSET to identify whether the ANSI metrics need special handling */
3948 todo_wine_if(cmap_type != cmap_ms_symbol && tmA.tmCharSet == SYMBOL_CHARSET && expect_first_A != 0x1e)
3949 ok(tmA.tmFirstChar == expect_first_A ||
3950 tmA.tmFirstChar == expect_first_A + 1 /* win9x */,
3951 "A: tmFirstChar for %s got %02x expected %02x\n", font_name, tmA.tmFirstChar, expect_first_A);
3952 if (pGdiGetCodePage == NULL || ! IsDBCSLeadByteEx(pGdiGetCodePage(hdc), tmA.tmLastChar))
3953 ok(tmA.tmLastChar == expect_last_A ||
3954 tmA.tmLastChar == 0xff /* win9x */,
3955 "A: tmLastChar for %s got %02x expected %02x\n", font_name, tmA.tmLastChar, expect_last_A);
3956 else
3957 skip("tmLastChar is DBCS lead byte\n");
3958 ok(tmA.tmBreakChar == expect_break_A, "A: tmBreakChar for %s got %02x expected %02x\n",
3959 font_name, tmA.tmBreakChar, expect_break_A);
3960 ok(tmA.tmDefaultChar == expect_default_A || broken(sys_lang_non_english),
3961 "A: tmDefaultChar for %s got %02x expected %02x\n",
3962 font_name, tmA.tmDefaultChar, expect_default_A);
3965 SetLastError(0xdeadbeef);
3966 ret = GetTextMetricsW(hdc, &tmW);
3967 ok(ret || GetLastError() == ERROR_CALL_NOT_IMPLEMENTED,
3968 "GetTextMetricsW error %u\n", GetLastError());
3969 if (ret)
3971 /* Wine uses the os2 first char */
3972 todo_wine_if(cmap_first != os2_first_char && cmap_type != cmap_ms_symbol)
3973 ok(tmW.tmFirstChar == expect_first_W, "W: tmFirstChar for %s got %02x expected %02x\n",
3974 font_name, tmW.tmFirstChar, expect_first_W);
3976 /* Wine uses the os2 last char */
3977 todo_wine_if(expect_last_W != os2_last_char && cmap_type != cmap_ms_symbol)
3978 ok(tmW.tmLastChar == expect_last_W, "W: tmLastChar for %s got %02x expected %02x\n",
3979 font_name, tmW.tmLastChar, expect_last_W);
3980 ok(tmW.tmBreakChar == expect_break_W, "W: tmBreakChar for %s got %02x expected %02x\n",
3981 font_name, tmW.tmBreakChar, expect_break_W);
3982 ok(tmW.tmDefaultChar == expect_default_W || broken(sys_lang_non_english),
3983 "W: tmDefaultChar for %s got %02x expected %02x\n",
3984 font_name, tmW.tmDefaultChar, expect_default_W);
3986 /* Test the aspect ratio while we have tmW */
3987 ret = GetDeviceCaps(hdc, LOGPIXELSX);
3988 ok(tmW.tmDigitizedAspectX == ret, "W: tmDigitizedAspectX %u != %u\n",
3989 tmW.tmDigitizedAspectX, ret);
3990 ret = GetDeviceCaps(hdc, LOGPIXELSY);
3991 ok(tmW.tmDigitizedAspectX == ret, "W: tmDigitizedAspectY %u != %u\n",
3992 tmW.tmDigitizedAspectX, ret);
3996 /* test FF_ values */
3997 switch(tt_os2.panose.bFamilyType)
3999 case PAN_ANY:
4000 case PAN_NO_FIT:
4001 case PAN_FAMILY_TEXT_DISPLAY:
4002 case PAN_FAMILY_PICTORIAL:
4003 default:
4004 if((tmA.tmPitchAndFamily & 1) == 0 || /* fixed */
4005 tt_os2.panose.bProportion == PAN_PROP_MONOSPACED)
4007 expect_ff(&tmA, &tt_os2, FF_MODERN, font_name);
4008 break;
4010 switch(tt_os2.panose.bSerifStyle)
4012 case PAN_ANY:
4013 case PAN_NO_FIT:
4014 default:
4015 expect_ff(&tmA, &tt_os2, FF_DONTCARE, font_name);
4016 break;
4018 case PAN_SERIF_COVE:
4019 case PAN_SERIF_OBTUSE_COVE:
4020 case PAN_SERIF_SQUARE_COVE:
4021 case PAN_SERIF_OBTUSE_SQUARE_COVE:
4022 case PAN_SERIF_SQUARE:
4023 case PAN_SERIF_THIN:
4024 case PAN_SERIF_BONE:
4025 case PAN_SERIF_EXAGGERATED:
4026 case PAN_SERIF_TRIANGLE:
4027 expect_ff(&tmA, &tt_os2, FF_ROMAN, font_name);
4028 break;
4030 case PAN_SERIF_NORMAL_SANS:
4031 case PAN_SERIF_OBTUSE_SANS:
4032 case PAN_SERIF_PERP_SANS:
4033 case PAN_SERIF_FLARED:
4034 case PAN_SERIF_ROUNDED:
4035 expect_ff(&tmA, &tt_os2, FF_SWISS, font_name);
4036 break;
4038 break;
4040 case PAN_FAMILY_SCRIPT:
4041 expect_ff(&tmA, &tt_os2, FF_SCRIPT, font_name);
4042 break;
4044 case PAN_FAMILY_DECORATIVE:
4045 expect_ff(&tmA, &tt_os2, FF_DECORATIVE, font_name);
4046 break;
4049 test_negative_width(hdc, lf);
4051 end_of_test:
4052 SelectObject(hdc, hfont_old);
4053 DeleteObject(hfont);
4055 ReleaseDC(0, hdc);
4058 static INT CALLBACK enum_truetype_font_proc(const LOGFONTA *lf, const TEXTMETRICA *ntm, DWORD type, LPARAM lParam)
4060 INT *enumed = (INT *)lParam;
4062 if (type == TRUETYPE_FONTTYPE)
4064 (*enumed)++;
4065 test_text_metrics(lf, (const NEWTEXTMETRICA *)ntm);
4067 return 1;
4070 static void test_GetTextMetrics(void)
4072 LOGFONTA lf;
4073 HDC hdc;
4074 INT enumed;
4076 hdc = GetDC(0);
4078 memset(&lf, 0, sizeof(lf));
4079 lf.lfCharSet = DEFAULT_CHARSET;
4080 enumed = 0;
4081 EnumFontFamiliesExA(hdc, &lf, enum_truetype_font_proc, (LPARAM)&enumed, 0);
4083 ReleaseDC(0, hdc);
4086 static void test_nonexistent_font(void)
4088 static const struct
4090 const char *name;
4091 int charset;
4092 } font_subst[] =
4094 { "Times New Roman Baltic", 186 },
4095 { "Times New Roman CE", 238 },
4096 { "Times New Roman CYR", 204 },
4097 { "Times New Roman Greek", 161 },
4098 { "Times New Roman TUR", 162 }
4100 static const struct
4102 const char *name;
4103 int charset;
4104 } shell_subst[] =
4106 { "MS Shell Dlg", 186 },
4107 { "MS Shell Dlg", 238 },
4108 { "MS Shell Dlg", 204 },
4109 { "MS Shell Dlg", 161 },
4110 { "MS Shell Dlg", 162 }
4112 LOGFONTA lf;
4113 HDC hdc;
4114 HFONT hfont;
4115 CHARSETINFO csi;
4116 INT cs, expected_cs, i, ret;
4117 char buf[LF_FACESIZE];
4119 expected_cs = GetACP();
4120 if (!TranslateCharsetInfo(ULongToPtr(expected_cs), &csi, TCI_SRCCODEPAGE))
4122 skip("TranslateCharsetInfo failed for code page %d\n", expected_cs);
4123 return;
4125 expected_cs = csi.ciCharset;
4126 trace("ACP %d -> charset %d\n", GetACP(), expected_cs);
4128 hdc = CreateCompatibleDC(0);
4130 for (i = 0; i < ARRAY_SIZE(shell_subst); i++)
4132 ret = is_font_installed(shell_subst[i].name);
4133 ok(ret || broken(!ret) /* win2000 */, "%s should be enumerated\n", shell_subst[i].name);
4134 ret = is_truetype_font_installed(shell_subst[i].name);
4135 ok(ret || broken(!ret) /* win2000 */, "%s should be enumerated\n", shell_subst[i].name);
4137 memset(&lf, 0, sizeof(lf));
4138 lf.lfHeight = -13;
4139 lf.lfWeight = FW_REGULAR;
4140 strcpy(lf.lfFaceName, shell_subst[i].name);
4141 hfont = CreateFontIndirectA(&lf);
4142 hfont = SelectObject(hdc, hfont);
4143 GetTextFaceA(hdc, sizeof(buf), buf);
4144 ok(!lstrcmpiA(buf, shell_subst[i].name), "expected %s, got %s\n", shell_subst[i].name, buf);
4145 cs = GetTextCharset(hdc);
4146 ok(cs == ANSI_CHARSET, "expected ANSI_CHARSET, got %d for font %s\n", cs, shell_subst[i].name);
4148 DeleteObject(SelectObject(hdc, hfont));
4150 memset(&lf, 0, sizeof(lf));
4151 lf.lfHeight = -13;
4152 lf.lfWeight = FW_DONTCARE;
4153 strcpy(lf.lfFaceName, shell_subst[i].name);
4154 hfont = CreateFontIndirectA(&lf);
4155 hfont = SelectObject(hdc, hfont);
4156 GetTextFaceA(hdc, sizeof(buf), buf);
4157 ok(!lstrcmpiA(buf, shell_subst[i].name), "expected %s, got %s\n", shell_subst[i].name, buf);
4158 cs = GetTextCharset(hdc);
4159 ok(cs == expected_cs || cs == ANSI_CHARSET, "expected %d, got %d for font %s\n", expected_cs, cs, shell_subst[i].name);
4160 DeleteObject(SelectObject(hdc, hfont));
4163 if (!is_truetype_font_installed("Arial") ||
4164 !is_truetype_font_installed("Times New Roman"))
4166 DeleteDC(hdc);
4167 skip("Arial or Times New Roman not installed\n");
4168 return;
4171 memset(&lf, 0, sizeof(lf));
4172 lf.lfHeight = 100;
4173 lf.lfWeight = FW_REGULAR;
4174 lf.lfCharSet = ANSI_CHARSET;
4175 lf.lfPitchAndFamily = FF_SWISS;
4176 strcpy(lf.lfFaceName, "Nonexistent font");
4177 hfont = CreateFontIndirectA(&lf);
4178 hfont = SelectObject(hdc, hfont);
4179 GetTextFaceA(hdc, sizeof(buf), buf);
4180 ok(!lstrcmpiA(buf, "Arial"), "Got %s\n", buf);
4181 cs = GetTextCharset(hdc);
4182 ok(cs == ANSI_CHARSET, "expected ANSI_CHARSET, got %d\n", cs);
4183 DeleteObject(SelectObject(hdc, hfont));
4185 memset(&lf, 0, sizeof(lf));
4186 lf.lfHeight = -13;
4187 lf.lfWeight = FW_DONTCARE;
4188 strcpy(lf.lfFaceName, "Nonexistent font");
4189 hfont = CreateFontIndirectA(&lf);
4190 hfont = SelectObject(hdc, hfont);
4191 GetTextFaceA(hdc, sizeof(buf), buf);
4192 todo_wine /* Wine uses Arial for all substitutions */
4193 ok(!lstrcmpiA(buf, "Nonexistent font") /* XP, Vista */ ||
4194 !lstrcmpiA(buf, "MS Serif") || /* Win9x */
4195 !lstrcmpiA(buf, "MS Sans Serif"), /* win2k3 */
4196 "Got %s\n", buf);
4197 cs = GetTextCharset(hdc);
4198 ok(cs == expected_cs || cs == ANSI_CHARSET, "expected %d, got %d\n", expected_cs, cs);
4199 DeleteObject(SelectObject(hdc, hfont));
4201 memset(&lf, 0, sizeof(lf));
4202 lf.lfHeight = -13;
4203 lf.lfWeight = FW_REGULAR;
4204 strcpy(lf.lfFaceName, "Nonexistent font");
4205 hfont = CreateFontIndirectA(&lf);
4206 hfont = SelectObject(hdc, hfont);
4207 GetTextFaceA(hdc, sizeof(buf), buf);
4208 ok(!lstrcmpiA(buf, "Arial") /* XP, Vista */ ||
4209 !lstrcmpiA(buf, "Times New Roman") /* Win9x */, "Got %s\n", buf);
4210 cs = GetTextCharset(hdc);
4211 ok(cs == ANSI_CHARSET, "expected ANSI_CHARSET, got %d\n", cs);
4212 DeleteObject(SelectObject(hdc, hfont));
4214 memset(&lf, 0, sizeof(lf));
4215 lf.lfHeight = -13;
4216 lf.lfWeight = FW_DONTCARE;
4217 strcpy(lf.lfFaceName, "Times New Roman");
4218 hfont = CreateFontIndirectA(&lf);
4219 hfont = SelectObject(hdc, hfont);
4220 GetTextFaceA(hdc, sizeof(buf), buf);
4221 ok(!lstrcmpiA(buf, "Times New Roman"), "Got %s\n", buf);
4222 cs = GetTextCharset(hdc);
4223 ok(cs == ANSI_CHARSET, "expected ANSI_CHARSET, got %d\n", cs);
4224 DeleteObject(SelectObject(hdc, hfont));
4226 for (i = 0; i < ARRAY_SIZE(font_subst); i++)
4228 ret = is_font_installed(font_subst[i].name);
4229 todo_wine
4230 ok(ret || broken(!ret && !i) /* win2000 doesn't have Times New Roman Baltic substitution */,
4231 "%s should be enumerated\n", font_subst[i].name);
4232 ret = is_truetype_font_installed(font_subst[i].name);
4233 todo_wine
4234 ok(ret || broken(!ret && !i) /* win2000 doesn't have Times New Roman Baltic substitution */,
4235 "%s should be enumerated\n", font_subst[i].name);
4237 memset(&lf, 0, sizeof(lf));
4238 lf.lfHeight = -13;
4239 lf.lfWeight = FW_REGULAR;
4240 strcpy(lf.lfFaceName, font_subst[i].name);
4241 hfont = CreateFontIndirectA(&lf);
4242 hfont = SelectObject(hdc, hfont);
4243 cs = GetTextCharset(hdc);
4244 if (font_subst[i].charset == expected_cs)
4246 ok(cs == expected_cs, "expected %d, got %d for font %s\n", expected_cs, cs, font_subst[i].name);
4247 GetTextFaceA(hdc, sizeof(buf), buf);
4248 ok(!lstrcmpiA(buf, font_subst[i].name), "expected %s, got %s\n", font_subst[i].name, buf);
4250 else
4252 ok(cs == ANSI_CHARSET, "expected ANSI_CHARSET, got %d for font %s\n", cs, font_subst[i].name);
4253 GetTextFaceA(hdc, sizeof(buf), buf);
4254 ok(!lstrcmpiA(buf, "Arial") /* XP, Vista */ ||
4255 !lstrcmpiA(buf, "Times New Roman") /* Win9x */, "got %s for font %s\n", buf, font_subst[i].name);
4257 DeleteObject(SelectObject(hdc, hfont));
4259 memset(&lf, 0, sizeof(lf));
4260 lf.lfHeight = -13;
4261 lf.lfWeight = FW_DONTCARE;
4262 strcpy(lf.lfFaceName, font_subst[i].name);
4263 hfont = CreateFontIndirectA(&lf);
4264 hfont = SelectObject(hdc, hfont);
4265 GetTextFaceA(hdc, sizeof(buf), buf);
4266 ok(!lstrcmpiA(buf, "Arial") /* Wine */ ||
4267 !lstrcmpiA(buf, font_subst[i].name) /* XP, Vista */ ||
4268 !lstrcmpiA(buf, "MS Serif") /* Win9x */ ||
4269 !lstrcmpiA(buf, "MS Sans Serif"), /* win2k3 */
4270 "got %s for font %s\n", buf, font_subst[i].name);
4271 cs = GetTextCharset(hdc);
4272 ok(cs == expected_cs || cs == ANSI_CHARSET, "expected %d, got %d for font %s\n", expected_cs, cs, font_subst[i].name);
4273 DeleteObject(SelectObject(hdc, hfont));
4276 DeleteDC(hdc);
4279 struct font_realization_info
4281 DWORD size;
4282 DWORD flags;
4283 DWORD cache_num;
4284 DWORD instance_id;
4285 DWORD unk;
4286 WORD face_index;
4287 WORD simulations;
4290 struct file_info
4292 FILETIME time;
4293 LARGE_INTEGER size;
4294 WCHAR path[MAX_PATH];
4297 static void test_RealizationInfo(void)
4299 struct realization_info_t
4301 DWORD flags;
4302 DWORD cache_num;
4303 DWORD instance_id;
4306 struct file_info file_info;
4307 HDC hdc;
4308 DWORD info[4], info2[32], read;
4309 HFONT hfont, hfont_old;
4310 SIZE_T needed;
4311 LOGFONTA lf;
4312 HANDLE h;
4313 BYTE file[16], data[14];
4314 FILETIME time;
4315 LARGE_INTEGER size;
4316 BOOL r;
4318 if(!pGdiRealizationInfo)
4320 win_skip("GdiRealizationInfo not available\n");
4321 return;
4324 hdc = GetDC(0);
4326 memset(info, 0xcc, sizeof(info));
4327 r = pGdiRealizationInfo(hdc, info);
4328 ok(r != 0, "ret 0\n");
4329 ok((info[0] & 0xf) == 1, "info[0] = %x for the system font\n", info[0]);
4330 ok(info[3] == 0xcccccccc, "structure longer than 3 dwords\n");
4332 if (!is_truetype_font_installed("Tahoma"))
4334 skip("skipping GdiRealizationInfo with truetype font\n");
4335 goto end;
4338 memset(&lf, 0, sizeof(lf));
4339 strcpy(lf.lfFaceName, "Tahoma");
4340 lf.lfHeight = 20;
4341 lf.lfWeight = FW_BOLD;
4342 lf.lfItalic = 1;
4343 hfont = CreateFontIndirectA(&lf);
4344 hfont_old = SelectObject(hdc, hfont);
4346 memset(info, 0xcc, sizeof(info));
4347 r = pGdiRealizationInfo(hdc, info);
4348 ok(r != 0, "ret 0\n");
4349 ok((info[0] & 0xf) == 3, "info[0] = %x for arial\n", info[0]);
4350 ok(info[3] == 0xcccccccc, "structure longer than 3 dwords\n");
4352 if (pGetFontRealizationInfo)
4354 struct font_realization_info *fri = (struct font_realization_info*)info2;
4355 struct realization_info_t *ri = (struct realization_info_t*)info;
4357 /* The first DWORD represents a struct size. On a
4358 newly rebooted system setting this to < 16 results
4359 in GetFontRealizationInfo failing. However there
4360 appears to be some caching going on which results
4361 in calls after a successful call also succeeding even
4362 if the size < 16. This means we can't reliably test
4363 this behaviour. */
4365 memset(info2, 0xcc, sizeof(info2));
4366 info2[0] = 16;
4367 r = pGetFontRealizationInfo(hdc, info2);
4368 ok(r != 0, "ret 0\n");
4369 /* We may get the '24' version here if that has been previously
4370 requested. */
4371 ok(fri->size == 16 || fri->size == 24, "got %d\n", info2[0]);
4372 ok(fri->flags == ri->flags, "flags mismatch\n");
4373 ok(fri->cache_num == ri->cache_num, "cache_num mismatch\n");
4374 ok(fri->instance_id == ri->instance_id, "instance id mismatch\n");
4375 ok(info2[6] == 0xcccccccc, "got wrong dword 6, 0x%08x\n", info2[6]);
4377 memset(info2, 0xcc, sizeof(info2));
4378 info2[0] = 28;
4379 r = pGetFontRealizationInfo(hdc, info2);
4380 ok(r == FALSE, "got %d\n", r);
4382 memset(info2, 0xcc, sizeof(info2));
4383 info2[0] = 24;
4384 r = pGetFontRealizationInfo(hdc, info2);
4385 ok(r != 0, "ret 0\n");
4386 ok(fri->size == 24, "got %d\n", fri->size);
4387 ok(fri->flags == ri->flags, "flags mismatch\n");
4388 ok(fri->cache_num == ri->cache_num, "cache_num mismatch\n");
4389 ok(fri->instance_id == ri->instance_id, "instance id mismatch\n");
4390 ok(fri->simulations == 0x2, "got simulations flags 0x%04x\n", fri->simulations);
4391 ok(fri->face_index == 0, "got wrong face index %u\n", fri->face_index);
4392 ok(info2[6] == 0xcccccccc, "structure longer than 6 dwords\n");
4394 /* Test GetFontFileInfo() */
4395 /* invalid font id */
4396 SetLastError(0xdeadbeef);
4397 r = pGetFontFileInfo(0xabababab, 0, &file_info, sizeof(file_info), &needed);
4398 ok(r == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "ret %d gle %d\n", r, GetLastError());
4400 needed = 0;
4401 r = pGetFontFileInfo(fri->instance_id, 0, &file_info, sizeof(file_info), &needed);
4402 ok(r != 0, "Failed to get font file info, error %d.\n", GetLastError());
4404 if (r)
4406 ok(needed > 0 && needed < sizeof(file_info), "Unexpected required size.\n");
4408 h = CreateFileW(file_info.path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
4409 ok(h != INVALID_HANDLE_VALUE, "Unable to open file %d\n", GetLastError());
4411 GetFileTime(h, NULL, NULL, &time);
4412 ok(!CompareFileTime(&file_info.time, &time), "time mismatch\n");
4413 GetFileSizeEx(h, &size);
4414 ok(file_info.size.QuadPart == size.QuadPart, "size mismatch\n");
4416 /* Read first 16 bytes from the file */
4417 ReadFile(h, file, sizeof(file), &read, NULL);
4418 CloseHandle(h);
4420 /* shorter buffer */
4421 SetLastError(0xdeadbeef);
4422 r = pGetFontFileInfo(fri->instance_id, 0, &file_info, needed - 1, &needed);
4423 ok(r == 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "ret %d gle %d\n", r, GetLastError());
4426 /* Get bytes 2 - 16 using GetFontFileData */
4427 r = pGetFontFileData(fri->instance_id, 0, 2, data, sizeof(data));
4428 ok(r != 0, "ret 0 gle %d\n", GetLastError());
4430 ok(!memcmp(data, file + 2, sizeof(data)), "mismatch\n");
4433 DeleteObject(SelectObject(hdc, hfont_old));
4435 end:
4436 ReleaseDC(0, hdc);
4439 /* Tests on XP SP2 show that the ANSI version of GetTextFace does NOT include
4440 the nul in the count of characters copied when the face name buffer is not
4441 NULL, whereas it does if the buffer is NULL. Further, the Unicode version
4442 always includes it. */
4443 static void test_GetTextFace(void)
4445 static const char faceA[] = "Tahoma";
4446 static const WCHAR faceW[] = L"Tahoma";
4447 LOGFONTA fA = {0};
4448 LOGFONTW fW = {0};
4449 char bufA[LF_FACESIZE];
4450 WCHAR bufW[LF_FACESIZE];
4451 HFONT f, g;
4452 HDC dc;
4453 int n;
4455 if(!is_font_installed("Tahoma"))
4457 skip("Tahoma is not installed so skipping this test\n");
4458 return;
4461 /* 'A' case. */
4462 memcpy(fA.lfFaceName, faceA, sizeof faceA);
4463 f = CreateFontIndirectA(&fA);
4464 ok(f != NULL, "CreateFontIndirectA failed\n");
4466 dc = GetDC(NULL);
4467 g = SelectObject(dc, f);
4468 n = GetTextFaceA(dc, sizeof bufA, bufA);
4469 ok(n == sizeof faceA - 1, "GetTextFaceA returned %d\n", n);
4470 ok(lstrcmpA(faceA, bufA) == 0, "GetTextFaceA\n");
4472 /* Play with the count arg. */
4473 bufA[0] = 'x';
4474 n = GetTextFaceA(dc, 0, bufA);
4475 ok(n == 0, "GetTextFaceA returned %d\n", n);
4476 ok(bufA[0] == 'x', "GetTextFaceA buf[0] == %d\n", bufA[0]);
4478 bufA[0] = 'x';
4479 n = GetTextFaceA(dc, 1, bufA);
4480 ok(n == 0, "GetTextFaceA returned %d\n", n);
4481 ok(bufA[0] == '\0', "GetTextFaceA buf[0] == %d\n", bufA[0]);
4483 bufA[0] = 'x'; bufA[1] = 'y';
4484 n = GetTextFaceA(dc, 2, bufA);
4485 ok(n == 1, "GetTextFaceA returned %d\n", n);
4486 ok(bufA[0] == faceA[0] && bufA[1] == '\0', "GetTextFaceA didn't copy\n");
4488 n = GetTextFaceA(dc, 0, NULL);
4489 ok(n == sizeof faceA ||
4490 broken(n == 0), /* win98, winMe */
4491 "GetTextFaceA returned %d\n", n);
4493 DeleteObject(SelectObject(dc, g));
4494 ReleaseDC(NULL, dc);
4496 /* 'W' case. */
4497 memcpy(fW.lfFaceName, faceW, sizeof faceW);
4498 SetLastError(0xdeadbeef);
4499 f = CreateFontIndirectW(&fW);
4500 if (!f && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
4502 win_skip("CreateFontIndirectW is not implemented\n");
4503 return;
4505 ok(f != NULL, "CreateFontIndirectW failed\n");
4507 dc = GetDC(NULL);
4508 g = SelectObject(dc, f);
4509 n = GetTextFaceW(dc, ARRAY_SIZE(bufW), bufW);
4510 ok(n == ARRAY_SIZE(faceW), "GetTextFaceW returned %d\n", n);
4511 ok(lstrcmpW(faceW, bufW) == 0, "GetTextFaceW\n");
4513 /* Play with the count arg. */
4514 bufW[0] = 'x';
4515 n = GetTextFaceW(dc, 0, bufW);
4516 ok(n == 0, "GetTextFaceW returned %d\n", n);
4517 ok(bufW[0] == 'x', "GetTextFaceW buf[0] == %d\n", bufW[0]);
4519 bufW[0] = 'x';
4520 n = GetTextFaceW(dc, 1, bufW);
4521 ok(n == 1, "GetTextFaceW returned %d\n", n);
4522 ok(bufW[0] == '\0', "GetTextFaceW buf[0] == %d\n", bufW[0]);
4524 bufW[0] = 'x'; bufW[1] = 'y';
4525 n = GetTextFaceW(dc, 2, bufW);
4526 ok(n == 2, "GetTextFaceW returned %d\n", n);
4527 ok(bufW[0] == faceW[0] && bufW[1] == '\0', "GetTextFaceW didn't copy\n");
4529 n = GetTextFaceW(dc, 0, NULL);
4530 ok(n == ARRAY_SIZE(faceW), "GetTextFaceW returned %d\n", n);
4532 DeleteObject(SelectObject(dc, g));
4533 ReleaseDC(NULL, dc);
4536 static void test_orientation(void)
4538 static const char test_str[11] = "Test String";
4539 HDC hdc;
4540 LOGFONTA lf;
4541 HFONT hfont, old_hfont;
4542 SIZE size;
4544 if (!is_truetype_font_installed("Arial"))
4546 skip("Arial is not installed\n");
4547 return;
4550 hdc = CreateCompatibleDC(0);
4551 memset(&lf, 0, sizeof(lf));
4552 lstrcpyA(lf.lfFaceName, "Arial");
4553 lf.lfHeight = 72;
4554 lf.lfOrientation = lf.lfEscapement = 900;
4555 hfont = create_font("orientation", &lf);
4556 old_hfont = SelectObject(hdc, hfont);
4557 ok(GetTextExtentExPointA(hdc, test_str, sizeof(test_str), 32767, NULL, NULL, &size), "GetTextExtentExPointA failed\n");
4558 ok(near_match(311, size.cx), "cx should be about 311, got %d\n", size.cx);
4559 ok(near_match(75, size.cy), "cy should be about 75, got %d\n", size.cy);
4560 SelectObject(hdc, old_hfont);
4561 DeleteObject(hfont);
4562 DeleteDC(hdc);
4565 static void test_oemcharset(void)
4567 HDC hdc;
4568 LOGFONTA lf, clf;
4569 HFONT hfont, old_hfont;
4570 int charset;
4572 hdc = CreateCompatibleDC(0);
4573 ZeroMemory(&lf, sizeof(lf));
4574 lf.lfHeight = 12;
4575 lf.lfCharSet = OEM_CHARSET;
4576 lf.lfPitchAndFamily = FIXED_PITCH | FF_MODERN;
4577 lstrcpyA(lf.lfFaceName, "Terminal");
4578 hfont = CreateFontIndirectA(&lf);
4579 old_hfont = SelectObject(hdc, hfont);
4580 charset = GetTextCharset(hdc);
4581 todo_wine
4582 ok(charset == OEM_CHARSET, "expected %d charset, got %d\n", OEM_CHARSET, charset);
4583 hfont = SelectObject(hdc, old_hfont);
4584 GetObjectA(hfont, sizeof(clf), &clf);
4585 ok(!lstrcmpA(clf.lfFaceName, lf.lfFaceName), "expected %s face name, got %s\n", lf.lfFaceName, clf.lfFaceName);
4586 ok(clf.lfPitchAndFamily == lf.lfPitchAndFamily, "expected %x family, got %x\n", lf.lfPitchAndFamily, clf.lfPitchAndFamily);
4587 ok(clf.lfCharSet == lf.lfCharSet, "expected %d charset, got %d\n", lf.lfCharSet, clf.lfCharSet);
4588 ok(clf.lfHeight == lf.lfHeight, "expected %d height, got %d\n", lf.lfHeight, clf.lfHeight);
4589 DeleteObject(hfont);
4590 DeleteDC(hdc);
4593 static int CALLBACK create_fixed_pitch_font_proc(const LOGFONTA *lpelfe,
4594 const TEXTMETRICA *lpntme,
4595 DWORD FontType, LPARAM lParam)
4597 const NEWTEXTMETRICEXA *lpntmex = (const NEWTEXTMETRICEXA *)lpntme;
4598 CHARSETINFO csi;
4599 LOGFONTA lf = *lpelfe;
4600 HFONT hfont;
4601 DWORD found_subset;
4603 /* skip bitmap, proportional or vertical font */
4604 if ((FontType & TRUETYPE_FONTTYPE) == 0 ||
4605 (lf.lfPitchAndFamily & 0xf) != FIXED_PITCH ||
4606 lf.lfFaceName[0] == '@')
4607 return 1;
4609 /* skip linked font */
4610 if (!TranslateCharsetInfo((DWORD*)(INT_PTR)lpelfe->lfCharSet, &csi, TCI_SRCCHARSET) ||
4611 (lpntmex->ntmFontSig.fsCsb[0] & csi.fs.fsCsb[0]) == 0)
4612 return 1;
4614 /* skip linked font, like SimSun-ExtB */
4615 switch (lpelfe->lfCharSet) {
4616 case SHIFTJIS_CHARSET:
4617 found_subset = lpntmex->ntmFontSig.fsUsb[1] & (1 << 17); /* Hiragana */
4618 break;
4619 case GB2312_CHARSET:
4620 case CHINESEBIG5_CHARSET:
4621 found_subset = lpntmex->ntmFontSig.fsUsb[1] & (1 << 16); /* CJK Symbols And Punctuation */
4622 break;
4623 case HANGEUL_CHARSET:
4624 found_subset = lpntmex->ntmFontSig.fsUsb[1] & (1 << 24); /* Hangul Syllables */
4625 break;
4626 default:
4627 found_subset = lpntmex->ntmFontSig.fsUsb[0] & (1 << 0); /* Basic Latin */
4628 break;
4630 if (!found_subset)
4631 return 1;
4633 /* test with an odd height */
4634 lf.lfHeight = -19;
4635 lf.lfWidth = 0;
4636 hfont = CreateFontIndirectA(&lf);
4637 if (hfont)
4639 *(HFONT *)lParam = hfont;
4640 return 0;
4642 return 1;
4645 static void test_GetGlyphOutline(void)
4647 HDC hdc;
4648 GLYPHMETRICS gm, gm2;
4649 LOGFONTA lf;
4650 HFONT hfont, old_hfont;
4651 INT ret, ret2;
4652 const UINT fmt[] = { GGO_METRICS, GGO_BITMAP, GGO_GRAY2_BITMAP,
4653 GGO_GRAY4_BITMAP, GGO_GRAY8_BITMAP };
4654 static const struct
4656 UINT cs;
4657 UINT a;
4658 UINT w;
4659 } c[] =
4661 {ANSI_CHARSET, 0x30, 0x30},
4662 {SHIFTJIS_CHARSET, 0x82a0, 0x3042},
4663 {HANGEUL_CHARSET, 0x8141, 0xac02},
4664 {GB2312_CHARSET, 0x8141, 0x4e04},
4665 {CHINESEBIG5_CHARSET, 0xa142, 0x3001}
4667 UINT i;
4669 if (!is_truetype_font_installed("Tahoma"))
4671 skip("Tahoma is not installed\n");
4672 return;
4675 hdc = CreateCompatibleDC(0);
4676 memset(&lf, 0, sizeof(lf));
4677 lf.lfHeight = 72;
4678 lstrcpyA(lf.lfFaceName, "Tahoma");
4679 SetLastError(0xdeadbeef);
4680 hfont = CreateFontIndirectA(&lf);
4681 ok(hfont != 0, "CreateFontIndirectA error %u\n", GetLastError());
4682 old_hfont = SelectObject(hdc, hfont);
4684 memset(&gm, 0, sizeof(gm));
4685 SetLastError(0xdeadbeef);
4686 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
4687 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %u\n", GetLastError());
4689 memset(&gm, 0, sizeof(gm));
4690 SetLastError(0xdeadbeef);
4691 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, NULL);
4692 ok(ret == GDI_ERROR, "GetGlyphOutlineA should fail\n");
4693 ok(GetLastError() == 0xdeadbeef ||
4694 GetLastError() == ERROR_INVALID_PARAMETER, /* win98, winMe */
4695 "expected 0xdeadbeef, got %u\n", GetLastError());
4697 memset(&gm, 0, sizeof(gm));
4698 SetLastError(0xdeadbeef);
4699 ret = GetGlyphOutlineW(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
4700 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
4701 ok(ret != GDI_ERROR, "GetGlyphOutlineW error %u\n", GetLastError());
4703 memset(&gm, 0, sizeof(gm));
4704 SetLastError(0xdeadbeef);
4705 ret = GetGlyphOutlineW(hdc, 'A', GGO_METRICS, &gm, 0, NULL, NULL);
4706 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
4708 ok(ret == GDI_ERROR, "GetGlyphOutlineW should fail\n");
4709 ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %u\n", GetLastError());
4712 /* test for needed buffer size request on space char */
4713 memset(&gm, 0, sizeof(gm));
4714 SetLastError(0xdeadbeef);
4715 ret = GetGlyphOutlineW(hdc, ' ', GGO_NATIVE, &gm, 0, NULL, &mat);
4716 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
4718 ok(ret == 0, "GetGlyphOutlineW should return 0 buffer size for space char\n");
4719 ok(gm.gmBlackBoxX == 1, "Expected 1, got %u\n", gm.gmBlackBoxX);
4720 ok(gm.gmBlackBoxY == 1, "Expected 1, got %u\n", gm.gmBlackBoxY);
4723 /* requesting buffer size for space char + error */
4724 memset(&gm, 0, sizeof(gm));
4725 SetLastError(0xdeadbeef);
4726 ret = GetGlyphOutlineW(0, ' ', GGO_NATIVE, &gm, 0, NULL, NULL);
4727 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
4729 ok(ret == GDI_ERROR, "GetGlyphOutlineW should return GDI_ERROR\n");
4730 ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %u\n", GetLastError());
4731 ok(gm.gmBlackBoxX == 0, "Expected 0, got %u\n", gm.gmBlackBoxX);
4732 ok(gm.gmBlackBoxY == 0, "Expected 0, got %u\n", gm.gmBlackBoxY);
4735 /* test GetGlyphOutline with a buffer too small */
4736 SetLastError(0xdeadbeef);
4737 ret = GetGlyphOutlineA(hdc, 'A', GGO_NATIVE, &gm, sizeof(i), &i, &mat);
4738 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
4739 ok(ret == GDI_ERROR, "GetGlyphOutlineW should return an error when the buffer size is too small.\n");
4741 for (i = 0; i < ARRAY_SIZE(fmt); ++i)
4743 DWORD dummy;
4745 memset(&gm, 0xab, sizeof(gm));
4746 SetLastError(0xdeadbeef);
4747 ret = GetGlyphOutlineW(hdc, ' ', fmt[i], &gm, 0, NULL, &mat);
4748 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
4750 if (fmt[i] == GGO_METRICS)
4751 ok(ret != GDI_ERROR, "%2d:GetGlyphOutlineW should succeed, got %d\n", fmt[i], ret);
4752 else
4753 ok(ret == 0, "%2d:GetGlyphOutlineW should return 0, got %d\n", fmt[i], ret);
4754 ok(gm.gmBlackBoxX == 1, "%2d:expected 1, got %u\n", fmt[i], gm.gmBlackBoxX);
4755 ok(gm.gmBlackBoxY == 1, "%2d:expected 1, got %u\n", fmt[i], gm.gmBlackBoxY);
4758 memset(&gm, 0xab, sizeof(gm));
4759 SetLastError(0xdeadbeef);
4760 ret = GetGlyphOutlineW(hdc, ' ', fmt[i], &gm, 0, &dummy, &mat);
4761 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
4763 if (fmt[i] == GGO_METRICS)
4764 ok(ret != GDI_ERROR, "%2d:GetGlyphOutlineW should succeed, got %d\n", fmt[i], ret);
4765 else
4766 ok(ret == 0, "%2d:GetGlyphOutlineW should return 0, got %d\n", fmt[i], ret);
4767 ok(gm.gmBlackBoxX == 1, "%2d:expected 1, got %u\n", fmt[i], gm.gmBlackBoxX);
4768 ok(gm.gmBlackBoxY == 1, "%2d:expected 1, got %u\n", fmt[i], gm.gmBlackBoxY);
4771 memset(&gm, 0xab, sizeof(gm));
4772 SetLastError(0xdeadbeef);
4773 ret = GetGlyphOutlineW(hdc, ' ', fmt[i], &gm, sizeof(dummy), NULL, &mat);
4774 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
4776 if (fmt[i] == GGO_METRICS)
4777 ok(ret != GDI_ERROR, "%2d:GetGlyphOutlineW should succeed, got %d\n", fmt[i], ret);
4778 else
4779 ok(ret == 0, "%2d:GetGlyphOutlineW should return 0, got %d\n", fmt[i], ret);
4780 ok(gm.gmBlackBoxX == 1, "%2d:expected 1, got %u\n", fmt[i], gm.gmBlackBoxX);
4781 ok(gm.gmBlackBoxY == 1, "%2d:expected 1, got %u\n", fmt[i], gm.gmBlackBoxY);
4784 memset(&gm, 0xab, sizeof(gm));
4785 SetLastError(0xdeadbeef);
4786 ret = GetGlyphOutlineW(hdc, ' ', fmt[i], &gm, sizeof(dummy), &dummy, &mat);
4787 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
4789 if (fmt[i] == GGO_METRICS) {
4790 ok(ret != GDI_ERROR, "%2d:GetGlyphOutlineW should succeed, got %d\n", fmt[i], ret);
4791 ok(gm.gmBlackBoxX == 1, "%2d:expected 1, got %u\n", fmt[i], gm.gmBlackBoxX);
4792 ok(gm.gmBlackBoxY == 1, "%2d:expected 1, got %u\n", fmt[i], gm.gmBlackBoxY);
4794 else
4796 ok(ret == GDI_ERROR, "%2d:GetGlyphOutlineW should return GDI_ERROR, got %d\n", fmt[i], ret);
4797 memset(&gm2, 0xab, sizeof(gm2));
4798 ok(memcmp(&gm, &gm2, sizeof(GLYPHMETRICS)) == 0,
4799 "%2d:GLYPHMETRICS shouldn't be touched on error\n", fmt[i]);
4804 SelectObject(hdc, old_hfont);
4805 DeleteObject(hfont);
4807 for (i = 0; i < ARRAY_SIZE(c); ++i)
4809 static const MAT2 rotate_mat = {{0, 0}, {0, -1}, {0, 1}, {0, 0}};
4810 TEXTMETRICA tm;
4812 lf.lfFaceName[0] = '\0';
4813 lf.lfCharSet = c[i].cs;
4814 lf.lfPitchAndFamily = 0;
4815 if (EnumFontFamiliesExA(hdc, &lf, create_font_proc, (LPARAM)&hfont, 0))
4817 skip("TrueType font for charset %u is not installed\n", c[i].cs);
4818 continue;
4821 old_hfont = SelectObject(hdc, hfont);
4823 /* expected to ignore superfluous bytes (single-byte character) */
4824 ret = GetGlyphOutlineA(hdc, 0x8041, GGO_BITMAP, &gm, 0, NULL, &mat);
4825 ret2 = GetGlyphOutlineA(hdc, 0x41, GGO_BITMAP, &gm2, 0, NULL, &mat);
4826 ok(ret == ret2 && memcmp(&gm, &gm2, sizeof gm) == 0, "%d %d\n", ret, ret2);
4828 ret = GetGlyphOutlineA(hdc, 0xcc8041, GGO_BITMAP, &gm, 0, NULL, &mat);
4829 ok(ret == ret2 && memcmp(&gm, &gm2, sizeof gm) == 0,
4830 "Expected to ignore superfluous bytes, got %d %d\n", ret, ret2);
4832 /* expected to ignore superfluous bytes (double-byte character) */
4833 ret = GetGlyphOutlineA(hdc, c[i].a, GGO_BITMAP, &gm, 0, NULL, &mat);
4834 ret2 = GetGlyphOutlineA(hdc, c[i].a | 0xdead0000, GGO_BITMAP, &gm2, 0, NULL, &mat);
4835 ok(ret == ret2 && memcmp(&gm, &gm2, sizeof gm) == 0,
4836 "Expected to ignore superfluous bytes, got %d %d\n", ret, ret2);
4838 /* expected to match wide-char version results */
4839 ret2 = GetGlyphOutlineW(hdc, c[i].w, GGO_BITMAP, &gm2, 0, NULL, &mat);
4840 ok(ret == ret2 && memcmp(&gm, &gm2, sizeof gm) == 0, "%d %d\n", ret, ret2);
4842 if (EnumFontFamiliesExA(hdc, &lf, create_fixed_pitch_font_proc, (LPARAM)&hfont, 0))
4844 skip("Fixed-pitch TrueType font for charset %u is not available\n", c[i].cs);
4845 continue;
4847 DeleteObject(SelectObject(hdc, hfont));
4848 if (c[i].a <= 0xff)
4850 DeleteObject(SelectObject(hdc, old_hfont));
4851 continue;
4854 ret = GetObjectA(hfont, sizeof lf, &lf);
4855 ok(ret > 0, "GetObject error %u\n", GetLastError());
4857 ret = GetTextMetricsA(hdc, &tm);
4858 ok(ret, "GetTextMetrics error %u\n", GetLastError());
4859 ret = GetGlyphOutlineA(hdc, c[i].a, GGO_METRICS, &gm2, 0, NULL, &mat);
4860 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %u\n", GetLastError());
4861 ok(gm2.gmCellIncX == tm.tmAveCharWidth * 2 || broken(gm2.gmCellIncX == -lf.lfHeight),
4862 "expected %d, got %d (%s:%d)\n",
4863 tm.tmAveCharWidth * 2, gm2.gmCellIncX, lf.lfFaceName, lf.lfCharSet);
4865 ret = GetGlyphOutlineA(hdc, c[i].a, GGO_METRICS, &gm2, 0, NULL, &rotate_mat);
4866 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %u\n", GetLastError());
4867 ok(gm2.gmCellIncY == -lf.lfHeight,
4868 "expected %d, got %d (%s:%d)\n",
4869 -lf.lfHeight, gm2.gmCellIncY, lf.lfFaceName, lf.lfCharSet);
4871 lf.lfItalic = TRUE;
4872 hfont = CreateFontIndirectA(&lf);
4873 ok(hfont != NULL, "CreateFontIndirect error %u\n", GetLastError());
4874 DeleteObject(SelectObject(hdc, hfont));
4875 ret = GetTextMetricsA(hdc, &tm);
4876 ok(ret, "GetTextMetrics error %u\n", GetLastError());
4877 ret = GetGlyphOutlineA(hdc, c[i].a, GGO_METRICS, &gm2, 0, NULL, &mat);
4878 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %u\n", GetLastError());
4879 ok(gm2.gmCellIncX == tm.tmAveCharWidth * 2 || broken(gm2.gmCellIncX == -lf.lfHeight),
4880 "expected %d, got %d (%s:%d)\n",
4881 tm.tmAveCharWidth * 2, gm2.gmCellIncX, lf.lfFaceName, lf.lfCharSet);
4883 lf.lfItalic = FALSE;
4884 lf.lfEscapement = lf.lfOrientation = 2700;
4885 hfont = CreateFontIndirectA(&lf);
4886 ok(hfont != NULL, "CreateFontIndirect error %u\n", GetLastError());
4887 DeleteObject(SelectObject(hdc, hfont));
4888 ret = GetGlyphOutlineA(hdc, c[i].a, GGO_METRICS, &gm2, 0, NULL, &mat);
4889 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %u\n", GetLastError());
4890 ok(gm2.gmCellIncY == -lf.lfHeight,
4891 "expected %d, got %d (%s:%d)\n",
4892 -lf.lfHeight, gm2.gmCellIncY, lf.lfFaceName, lf.lfCharSet);
4894 hfont = SelectObject(hdc, old_hfont);
4895 DeleteObject(hfont);
4898 DeleteDC(hdc);
4901 /* bug #9995: there is a limit to the character width that can be specified */
4902 static void test_GetTextMetrics2(const char *fontname, int font_height)
4904 HFONT of, hf;
4905 HDC hdc;
4906 TEXTMETRICA tm;
4907 BOOL ret;
4908 int ave_width, height, width, ratio;
4910 if (!is_truetype_font_installed( fontname)) {
4911 skip("%s is not installed\n", fontname);
4912 return;
4914 hdc = CreateCompatibleDC(0);
4915 ok( hdc != NULL, "CreateCompatibleDC failed\n");
4916 /* select width = 0 */
4917 hf = CreateFontA(font_height, 0, 0, 0, FW_REGULAR, FALSE, FALSE, FALSE,
4918 DEFAULT_CHARSET, OUT_TT_PRECIS, CLIP_LH_ANGLES,
4919 DEFAULT_QUALITY, VARIABLE_PITCH,
4920 fontname);
4921 ok( hf != NULL, "CreateFontA(%s, %d) failed\n", fontname, font_height);
4922 of = SelectObject( hdc, hf);
4923 ret = GetTextMetricsA( hdc, &tm);
4924 ok(ret, "GetTextMetricsA error %u\n", GetLastError());
4925 height = tm.tmHeight;
4926 ave_width = tm.tmAveCharWidth;
4927 SelectObject( hdc, of);
4928 DeleteObject( hf);
4930 for (width = ave_width * 2; /* nothing*/; width += ave_width)
4932 hf = CreateFontA(height, width, 0, 0, FW_REGULAR, FALSE, FALSE, FALSE,
4933 DEFAULT_CHARSET, OUT_TT_PRECIS, CLIP_LH_ANGLES,
4934 DEFAULT_QUALITY, VARIABLE_PITCH, fontname);
4935 ok(hf != 0, "CreateFont failed\n");
4936 of = SelectObject(hdc, hf);
4937 ret = GetTextMetricsA(hdc, &tm);
4938 ok(ret, "GetTextMetrics error %u\n", GetLastError());
4939 SelectObject(hdc, of);
4940 DeleteObject(hf);
4942 if (match_off_by_1(tm.tmAveCharWidth, ave_width, FALSE) || width / height > 200)
4943 break;
4946 DeleteDC(hdc);
4948 ratio = width / height;
4950 ok(ratio >= 90 && ratio <= 110, "expected width/height ratio 90-110, got %d\n", ratio);
4953 static void test_GetCharacterPlacement(void)
4955 GCP_RESULTSA result;
4956 DWORD size, size2;
4957 WCHAR glyphs[20];
4958 int pos[20];
4959 HDC hdc;
4961 hdc = CreateCompatibleDC(0);
4962 ok(!!hdc, "CreateCompatibleDC failed\n");
4964 memset(&result, 0, sizeof(result));
4965 result.lStructSize = sizeof(result);
4966 result.lpCaretPos = pos;
4967 result.lpGlyphs = glyphs;
4968 result.nGlyphs = 20;
4970 pos[0] = -1;
4971 glyphs[0] = '!';
4972 size = GetCharacterPlacementA(hdc, "Wine Test", 9, 0, &result, 0);
4973 ok(size, "GetCharacterPlacementA failed!\n");
4974 ok(result.nGlyphs == 9, "Unexpected number of glyphs %u\n", result.nGlyphs);
4975 ok(glyphs[0] == 'W', "Unexpected first glyph %s\n", wine_dbgstr_wn(glyphs, 1));
4976 ok(pos[0] == 0, "Unexpected caret position %d\n", pos[0]);
4978 pos[0] = -1;
4979 glyphs[0] = '!';
4980 result.nGlyphs = 20;
4981 size2 = GetCharacterPlacementA(hdc, "Wine Test", 0, 0, &result, 0);
4982 ok(!size2, "Expected GetCharacterPlacementA to fail\n");
4983 ok(result.nGlyphs == 20, "Unexpected number of glyphs %u\n", result.nGlyphs);
4984 ok(glyphs[0] == '!', "Unexpected first glyph %s\n", wine_dbgstr_wn(glyphs, 1));
4985 ok(pos[0] == -1, "Unexpected caret position %d\n", pos[0]);
4987 size2 = GetCharacterPlacementA(hdc, "Wine Test", 9, 0, NULL, 0);
4988 ok(size2, "GetCharacterPlacementA failed!\n");
4989 ok(size == size2, "GetCharacterPlacementA returned different result: %u vs %u\n", size2, size);
4991 size2 = GetCharacterPlacementA(hdc, "Wine Test", 9, 1024, NULL, GCP_REORDER);
4992 ok(size2, "GetCharacterPlacementA failed!\n");
4993 ok(size == size2, "GetCharacterPlacementA returned different result: %u vs %u\n", size2, size);
4995 pos[0] = -1;
4996 glyphs[0] = '!';
4997 result.nGlyphs = 20;
4998 size = GetCharacterPlacementA(hdc, "Wine Test", 9, 1024, &result, GCP_REORDER);
4999 ok(size, "GetCharacterPlacementA failed!\n");
5000 ok(size == size2, "GetCharacterPlacementA returned different result: %u vs %u\n", size2, size);
5001 ok(result.nGlyphs == 9, "Unexpected number of glyphs %u\n", result.nGlyphs);
5002 ok(glyphs[0] == 'W', "Unexpected first glyph %s\n", wine_dbgstr_wn(glyphs, 1));
5003 todo_wine ok(pos[0] == 0, "Unexpected caret position %d\n", pos[0]);
5005 DeleteDC(hdc);
5008 static void test_CreateFontIndirect(void)
5010 LOGFONTA lf, getobj_lf;
5011 int ret, i;
5012 HFONT hfont;
5013 char TestName[][16] = {"Arial", "Arial Bold", "Arial Italic", "Arial Baltic"};
5015 memset(&lf, 0, sizeof(lf));
5016 lf.lfCharSet = ANSI_CHARSET;
5017 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
5018 lf.lfHeight = 16;
5019 lf.lfWidth = 16;
5020 lf.lfQuality = DEFAULT_QUALITY;
5021 lf.lfItalic = FALSE;
5022 lf.lfWeight = FW_DONTCARE;
5024 for (i = 0; i < ARRAY_SIZE(TestName); i++)
5026 lstrcpyA(lf.lfFaceName, TestName[i]);
5027 hfont = CreateFontIndirectA(&lf);
5028 ok(hfont != 0, "CreateFontIndirectA failed\n");
5029 SetLastError(0xdeadbeef);
5030 ret = GetObjectA(hfont, sizeof(getobj_lf), &getobj_lf);
5031 ok(ret, "GetObject failed: %d\n", GetLastError());
5032 ok(lf.lfItalic == getobj_lf.lfItalic, "lfItalic: expect %02x got %02x\n", lf.lfItalic, getobj_lf.lfItalic);
5033 ok(lf.lfWeight == getobj_lf.lfWeight ||
5034 broken((SHORT)lf.lfWeight == getobj_lf.lfWeight), /* win9x */
5035 "lfWeight: expect %08x got %08x\n", lf.lfWeight, getobj_lf.lfWeight);
5036 ok(!lstrcmpA(lf.lfFaceName, getobj_lf.lfFaceName) ||
5037 broken(!memcmp(lf.lfFaceName, getobj_lf.lfFaceName, LF_FACESIZE-1)), /* win9x doesn't ensure '\0' termination */
5038 "font names don't match: %s != %s\n", lf.lfFaceName, getobj_lf.lfFaceName);
5039 DeleteObject(hfont);
5043 static void test_CreateFontIndirectEx(void)
5045 ENUMLOGFONTEXDVA lfex;
5046 HFONT hfont;
5048 if (!pCreateFontIndirectExA)
5050 win_skip("CreateFontIndirectExA is not available\n");
5051 return;
5054 if (!is_truetype_font_installed("Arial"))
5056 skip("Arial is not installed\n");
5057 return;
5060 SetLastError(0xdeadbeef);
5061 hfont = pCreateFontIndirectExA(NULL);
5062 ok(hfont == NULL, "got %p\n", hfont);
5063 ok(GetLastError() == 0xdeadbeef, "got error %d\n", GetLastError());
5065 memset(&lfex, 0, sizeof(lfex));
5066 lstrcpyA(lfex.elfEnumLogfontEx.elfLogFont.lfFaceName, "Arial");
5067 hfont = pCreateFontIndirectExA(&lfex);
5068 ok(hfont != 0, "CreateFontIndirectEx failed\n");
5069 if (hfont)
5070 check_font("Arial", &lfex.elfEnumLogfontEx.elfLogFont, hfont);
5071 DeleteObject(hfont);
5074 static void test_realization_info(const char *name, DWORD size, BOOL is_memory_resource)
5076 struct font_realization_info info;
5077 struct file_info file_info;
5078 HFONT hfont, hfont_prev;
5079 SIZE_T needed;
5080 LOGFONTA lf;
5081 BYTE *data;
5082 BOOL ret;
5083 HDC hdc;
5085 if (!pGetFontRealizationInfo)
5086 return;
5088 memset(&lf, 0, sizeof(lf));
5089 lf.lfHeight = 72;
5090 strcpy(lf.lfFaceName, name);
5092 hfont = CreateFontIndirectA(&lf);
5093 ok(hfont != 0, "Failed to create a font, %u.\n", GetLastError());
5095 hdc = GetDC(NULL);
5097 hfont_prev = SelectObject(hdc, hfont);
5098 ok(hfont_prev != NULL, "Failed to select font.\n");
5100 memset(&info, 0xcc, sizeof(info));
5101 info.size = sizeof(info);
5102 ret = pGetFontRealizationInfo(hdc, (DWORD *)&info);
5103 ok(ret != 0, "Unexpected return value %d.\n", ret);
5105 ok((info.flags & 0xf) == 0x3, "Unexpected flags %#x.\n", info.flags);
5106 ok(info.cache_num != 0, "Unexpected cache num %u.\n", info.cache_num);
5107 ok(info.instance_id != 0, "Unexpected instance id %u.\n", info.instance_id);
5108 ok(info.simulations == 0, "Unexpected simulations %#x.\n", info.simulations);
5109 ok(info.face_index == 0, "Unexpected face index %u.\n", info.face_index);
5111 ret = pGetFontFileInfo(info.instance_id, 0, NULL, 0, NULL);
5112 ok(ret == 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Unexpected return value %d, error %d.\n",
5113 ret, GetLastError());
5115 needed = 0;
5116 ret = pGetFontFileInfo(info.instance_id, 0, NULL, 0, &needed);
5117 ok(ret == 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Unexpected return value %d, error %d.\n",
5118 ret, GetLastError());
5120 ret = pGetFontFileInfo(info.instance_id, 0, &file_info, 0, NULL);
5121 ok(ret == 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Unexpected return value %d, error %d.\n",
5122 ret, GetLastError());
5124 ret = pGetFontFileInfo(info.instance_id, 0, &file_info, needed - 1, NULL);
5125 ok(ret == 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Unexpected return value %d, error %d.\n",
5126 ret, GetLastError());
5128 ret = pGetFontFileInfo(info.instance_id, 0, &file_info, needed, NULL);
5129 ok(ret != 0, "Failed to get font file info, ret %d gle %d.\n", ret, GetLastError());
5131 memset(&file_info, 0xcc, sizeof(file_info));
5132 ret = pGetFontFileInfo(info.instance_id, 0, &file_info, sizeof(file_info), NULL);
5133 ok(ret != 0, "Failed to get font file info, ret %d gle %d.\n", ret, GetLastError());
5134 if (ret)
5136 ok(is_memory_resource ? file_info.size.QuadPart == size : file_info.size.QuadPart > 0, "Unexpected file size.\n");
5137 ok(is_memory_resource ? !file_info.path[0] : file_info.path[0], "Unexpected file path %s.\n",
5138 wine_dbgstr_w(file_info.path));
5141 size = file_info.size.LowPart;
5142 data = HeapAlloc(GetProcessHeap(), 0, size + 16);
5144 memset(data, 0xcc, size);
5145 ret = pGetFontFileData(info.instance_id, 0, 0, data, size);
5146 ok(ret != 0, "Failed to get font file data, %d\n", GetLastError());
5147 ok(*(DWORD *)data == 0x00000100, "Unexpected sfnt header version %#x.\n", *(DWORD *)data);
5148 ok(*(WORD *)(data + 4) == 0x0e00, "Unexpected table count %#x.\n", *(WORD *)(data + 4));
5150 /* Larger than font data size. */
5151 memset(data, 0xcc, size);
5152 ret = pGetFontFileData(info.instance_id, 0, 0, data, size + 16);
5153 ok(ret == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "Unexpected return value %d, error %d\n",
5154 ret, GetLastError());
5155 ok(*(DWORD *)data == 0xcccccccc, "Unexpected buffer contents %#x.\n", *(DWORD *)data);
5157 /* With offset. */
5158 memset(data, 0xcc, size);
5159 ret = pGetFontFileData(info.instance_id, 0, 16, data, size - 16);
5160 ok(ret != 0, "Failed to get font file data, %d\n", GetLastError());
5161 ok(*(DWORD *)data == 0x1000000, "Unexpected buffer contents %#x.\n", *(DWORD *)data);
5163 memset(data, 0xcc, size);
5164 ret = pGetFontFileData(info.instance_id, 0, 16, data, size);
5165 ok(ret == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "Unexpected return value %d, error %d\n",
5166 ret, GetLastError());
5167 ok(*(DWORD *)data == 0xcccccccc, "Unexpected buffer contents %#x.\n", *(DWORD *)data);
5169 /* Zero buffer size. */
5170 memset(data, 0xcc, size);
5171 ret = pGetFontFileData(info.instance_id, 0, 16, data, 0);
5172 todo_wine
5173 ok(ret == 0 && GetLastError() == ERROR_NOACCESS, "Unexpected return value %d, error %d\n", ret, GetLastError());
5174 ok(*(DWORD *)data == 0xcccccccc, "Unexpected buffer contents %#x.\n", *(DWORD *)data);
5176 HeapFree(GetProcessHeap(), 0, data);
5178 SelectObject(hdc, hfont_prev);
5179 DeleteObject(hfont);
5180 ReleaseDC(NULL, hdc);
5183 static void test_AddFontMemResource(void)
5185 char ttf_name[MAX_PATH];
5186 void *font;
5187 DWORD font_size, num_fonts;
5188 HANDLE ret;
5189 BOOL bRet;
5191 if (!pAddFontMemResourceEx || !pRemoveFontMemResourceEx)
5193 win_skip("AddFontMemResourceEx is not available on this platform\n");
5194 return;
5197 SetLastError(0xdeadbeef);
5198 ret = pAddFontMemResourceEx(NULL, 0, NULL, NULL);
5199 ok(!ret, "AddFontMemResourceEx should fail\n");
5200 ok(GetLastError() == ERROR_INVALID_PARAMETER,
5201 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
5202 GetLastError());
5204 SetLastError(0xdeadbeef);
5205 ret = pAddFontMemResourceEx(NULL, 10, NULL, NULL);
5206 ok(!ret, "AddFontMemResourceEx should fail\n");
5207 ok(GetLastError() == ERROR_INVALID_PARAMETER,
5208 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
5209 GetLastError());
5211 SetLastError(0xdeadbeef);
5212 ret = pAddFontMemResourceEx(NULL, 0, NULL, &num_fonts);
5213 ok(!ret, "AddFontMemResourceEx should fail\n");
5214 ok(GetLastError() == ERROR_INVALID_PARAMETER,
5215 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
5216 GetLastError());
5218 SetLastError(0xdeadbeef);
5219 ret = pAddFontMemResourceEx(NULL, 10, NULL, &num_fonts);
5220 ok(!ret, "AddFontMemResourceEx should fail\n");
5221 ok(GetLastError() == ERROR_INVALID_PARAMETER,
5222 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
5223 GetLastError());
5225 /* Now with scalable font */
5226 bRet = write_ttf_file("wine_test.ttf", ttf_name);
5227 ok(bRet, "Failed to create test font file.\n");
5229 font = load_font(ttf_name, &font_size);
5230 ok(font != NULL, "Failed to map font file.\n");
5232 bRet = is_truetype_font_installed("wine_test");
5233 ok(!bRet, "Font wine_test should not be enumerated.\n");
5235 num_fonts = 0;
5236 ret = pAddFontMemResourceEx(font, font_size, NULL, &num_fonts);
5237 ok(ret != 0, "Failed to add resource, %d.\n", GetLastError());
5238 ok(num_fonts == 1, "Unexpected number of fonts %u.\n", num_fonts);
5240 bRet = is_truetype_font_installed("wine_test");
5241 todo_wine
5242 ok(!bRet, "Font wine_test should not be enumerated.\n");
5244 test_realization_info("wine_test", font_size, TRUE);
5246 bRet = pRemoveFontMemResourceEx(ret);
5247 ok(bRet, "RemoveFontMemResourceEx error %d\n", GetLastError());
5249 free_font(font);
5251 bRet = DeleteFileA(ttf_name);
5252 ok(bRet, "Failed to delete font file, %d.\n", GetLastError());
5254 font = load_font("sserife.fon", &font_size);
5255 if (!font)
5257 skip("Unable to locate and load font sserife.fon\n");
5258 return;
5261 SetLastError(0xdeadbeef);
5262 ret = pAddFontMemResourceEx(font, 0, NULL, NULL);
5263 ok(!ret, "AddFontMemResourceEx should fail\n");
5264 ok(GetLastError() == ERROR_INVALID_PARAMETER,
5265 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
5266 GetLastError());
5268 SetLastError(0xdeadbeef);
5269 ret = pAddFontMemResourceEx(font, 10, NULL, NULL);
5270 ok(!ret, "AddFontMemResourceEx should fail\n");
5271 ok(GetLastError() == ERROR_INVALID_PARAMETER,
5272 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
5273 GetLastError());
5275 num_fonts = 0xdeadbeef;
5276 SetLastError(0xdeadbeef);
5277 ret = pAddFontMemResourceEx(font, 0, NULL, &num_fonts);
5278 ok(!ret, "AddFontMemResourceEx should fail\n");
5279 ok(GetLastError() == ERROR_INVALID_PARAMETER,
5280 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
5281 GetLastError());
5282 ok(num_fonts == 0xdeadbeef, "number of loaded fonts should be 0xdeadbeef\n");
5284 if (0) /* hangs under windows 2000 */
5286 num_fonts = 0xdeadbeef;
5287 SetLastError(0xdeadbeef);
5288 ret = pAddFontMemResourceEx(font, 10, NULL, &num_fonts);
5289 ok(!ret, "AddFontMemResourceEx should fail\n");
5290 ok(GetLastError() == 0xdeadbeef,
5291 "Expected GetLastError() to return 0xdeadbeef, got %u\n",
5292 GetLastError());
5293 ok(num_fonts == 0xdeadbeef, "number of loaded fonts should be 0xdeadbeef\n");
5296 num_fonts = 0xdeadbeef;
5297 SetLastError(0xdeadbeef);
5298 ret = pAddFontMemResourceEx(font, font_size, NULL, &num_fonts);
5299 ok(ret != 0, "AddFontMemResourceEx error %d\n", GetLastError());
5300 ok(num_fonts != 0xdeadbeef, "number of loaded fonts should not be 0xdeadbeef\n");
5301 ok(num_fonts != 0, "number of loaded fonts should not be 0\n");
5303 free_font(font);
5305 SetLastError(0xdeadbeef);
5306 bRet = pRemoveFontMemResourceEx(ret);
5307 ok(bRet, "RemoveFontMemResourceEx error %d\n", GetLastError());
5309 /* test invalid pointer to number of loaded fonts */
5310 font = load_font("sserife.fon", &font_size);
5311 ok(font != NULL, "Unable to locate and load font sserife.fon\n");
5313 SetLastError(0xdeadbeef);
5314 ret = pAddFontMemResourceEx(font, font_size, NULL, (void *)0xdeadbeef);
5315 ok(!ret, "AddFontMemResourceEx should fail\n");
5316 ok(GetLastError() == 0xdeadbeef,
5317 "Expected GetLastError() to return 0xdeadbeef, got %u\n",
5318 GetLastError());
5320 SetLastError(0xdeadbeef);
5321 ret = pAddFontMemResourceEx(font, font_size, NULL, NULL);
5322 ok(!ret, "AddFontMemResourceEx should fail\n");
5323 ok(GetLastError() == ERROR_INVALID_PARAMETER,
5324 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
5325 GetLastError());
5327 free_font(font);
5330 static INT CALLBACK enum_fonts_proc(const LOGFONTA *elf, const TEXTMETRICA *ntm, DWORD type, LPARAM lparam)
5332 LOGFONTA *lf;
5334 if (type != TRUETYPE_FONTTYPE) return 1;
5336 ok(ntm->tmWeight == elf->lfWeight, "expected %d got %d\n", ntm->tmWeight, elf->lfWeight);
5338 lf = (LOGFONTA *)lparam;
5339 *lf = *elf;
5340 return 0;
5343 static INT CALLBACK enum_all_fonts_proc(const LOGFONTA *elf, const TEXTMETRICA *ntm, DWORD type, LPARAM lparam)
5345 int ret;
5346 LOGFONTA *lf;
5348 if (type != TRUETYPE_FONTTYPE) return 1;
5350 lf = (LOGFONTA *)lparam;
5351 ret = strcmp(lf->lfFaceName, elf->lfFaceName);
5352 if(ret == 0)
5354 ok(ntm->tmWeight == elf->lfWeight, "expected %d got %d\n", ntm->tmWeight, elf->lfWeight);
5355 *lf = *elf;
5356 return 0;
5358 return 1;
5361 static INT CALLBACK enum_with_magic_retval_proc(const LOGFONTA *elf, const TEXTMETRICA *ntm, DWORD type, LPARAM lparam)
5363 return lparam;
5366 static void test_EnumFonts(void)
5368 int ret;
5369 LOGFONTA lf;
5370 HDC hdc;
5372 if (!is_truetype_font_installed("Arial"))
5374 skip("Arial is not installed\n");
5375 return;
5378 /* Windows uses localized font face names, so Arial Bold won't be found */
5379 if (PRIMARYLANGID(GetUserDefaultLangID()) != LANG_ENGLISH)
5381 skip("User locale is not English, skipping the test\n");
5382 return;
5385 hdc = CreateCompatibleDC(0);
5387 /* check that the enumproc's retval is returned */
5388 ret = EnumFontFamiliesA(hdc, NULL, enum_with_magic_retval_proc, 0xcafe);
5389 ok(ret == 0xcafe, "got %08x\n", ret);
5391 ret = EnumFontFamiliesA(hdc, "Arial", enum_fonts_proc, (LPARAM)&lf);
5392 ok(!ret, "font Arial is not enumerated\n");
5393 ret = strcmp(lf.lfFaceName, "Arial");
5394 ok(!ret, "expected Arial got %s\n", lf.lfFaceName);
5395 ok(lf.lfWeight == FW_NORMAL, "expected FW_NORMAL got %d\n", lf.lfWeight);
5397 strcpy(lf.lfFaceName, "Arial");
5398 ret = EnumFontFamiliesA(hdc, NULL, enum_all_fonts_proc, (LPARAM)&lf);
5399 ok(!ret, "font Arial is not enumerated\n");
5400 ret = strcmp(lf.lfFaceName, "Arial");
5401 ok(!ret, "expected Arial got %s\n", lf.lfFaceName);
5402 ok(lf.lfWeight == FW_NORMAL, "expected FW_NORMAL got %d\n", lf.lfWeight);
5404 ret = EnumFontFamiliesA(hdc, "Arial Bold", enum_fonts_proc, (LPARAM)&lf);
5405 ok(!ret, "font Arial Bold is not enumerated\n");
5406 ret = strcmp(lf.lfFaceName, "Arial");
5407 ok(!ret, "expected Arial got %s\n", lf.lfFaceName);
5408 ok(lf.lfWeight == FW_BOLD, "expected FW_BOLD got %d\n", lf.lfWeight);
5410 strcpy(lf.lfFaceName, "Arial Bold");
5411 ret = EnumFontFamiliesA(hdc, NULL, enum_all_fonts_proc, (LPARAM)&lf);
5412 ok(ret, "font Arial Bold should not be enumerated\n");
5414 ret = EnumFontFamiliesA(hdc, "Arial Bold Italic", enum_fonts_proc, (LPARAM)&lf);
5415 ok(!ret, "font Arial Bold Italic is not enumerated\n");
5416 ret = strcmp(lf.lfFaceName, "Arial");
5417 ok(!ret, "expected Arial got %s\n", lf.lfFaceName);
5418 ok(lf.lfWeight == FW_BOLD, "expected FW_BOLD got %d\n", lf.lfWeight);
5420 strcpy(lf.lfFaceName, "Arial Bold Italic");
5421 ret = EnumFontFamiliesA(hdc, NULL, enum_all_fonts_proc, (LPARAM)&lf);
5422 ok(ret, "font Arial Bold Italic should not be enumerated\n");
5424 ret = EnumFontFamiliesA(hdc, "Arial Italic Bold", enum_fonts_proc, (LPARAM)&lf);
5425 ok(ret, "font Arial Italic Bold should not be enumerated\n");
5427 strcpy(lf.lfFaceName, "Arial Italic Bold");
5428 ret = EnumFontFamiliesA(hdc, NULL, enum_all_fonts_proc, (LPARAM)&lf);
5429 ok(ret, "font Arial Italic Bold should not be enumerated\n");
5431 DeleteDC(hdc);
5434 static INT CALLBACK enum_ms_shell_dlg_proc(const LOGFONTA *lf, const TEXTMETRICA *ntm, DWORD type, LPARAM lParam)
5436 struct enum_fullname_data *efnd = (struct enum_fullname_data *)lParam;
5438 if (0) /* Disabled to limit console spam */
5439 trace("enumed font \"%s\", charset %d, height %d, weight %d, italic %d\n",
5440 lf->lfFaceName, lf->lfCharSet, lf->lfHeight, lf->lfWeight, lf->lfItalic);
5442 if (type != TRUETYPE_FONTTYPE) return 1;
5443 if (strcmp(lf->lfFaceName, "MS Shell Dlg") != 0) return 1;
5445 if (efnd->total >= efnd->size)
5447 efnd->size = max( (efnd->total + 1) * 2, 256 );
5448 efnd->elf = heap_realloc( efnd->elf, efnd->size * sizeof(*efnd->elf) );
5449 if (!efnd->elf) return 0;
5451 efnd->elf[efnd->total++] = *(ENUMLOGFONTA *)lf;
5452 return 0;
5455 static INT CALLBACK enum_ms_shell_dlg2_proc(const LOGFONTA *lf, const TEXTMETRICA *ntm, DWORD type, LPARAM lParam)
5457 struct enum_fullname_data *efnd = (struct enum_fullname_data *)lParam;
5459 if (0) /* Disabled to limit console spam */
5460 trace("enumed font \"%s\", charset %d, height %d, weight %d, italic %d\n",
5461 lf->lfFaceName, lf->lfCharSet, lf->lfHeight, lf->lfWeight, lf->lfItalic);
5463 if (type != TRUETYPE_FONTTYPE) return 1;
5464 if (strcmp(lf->lfFaceName, "MS Shell Dlg 2") != 0) return 1;
5466 if (efnd->total >= efnd->size)
5468 efnd->size = max( (efnd->total + 1) * 2, 256 );
5469 efnd->elf = heap_realloc( efnd->elf, efnd->size * sizeof(*efnd->elf) );
5470 if (!efnd->elf) return 0;
5472 efnd->elf[efnd->total++] = *(ENUMLOGFONTA *)lf;
5473 return 0;
5476 static void test_EnumFonts_subst(void)
5478 int ret;
5479 LOGFONTA lf;
5480 HDC hdc;
5481 struct enum_fullname_data efnd;
5483 ret = is_font_installed("MS Shell Dlg");
5484 ok(ret, "MS Shell Dlg should be enumerated\n");
5485 ret = is_truetype_font_installed("MS Shell Dlg");
5486 ok(ret, "MS Shell Dlg should be enumerated as a TrueType font\n");
5488 ret = is_font_installed("MS Shell Dlg 2");
5489 ok(ret, "MS Shell Dlg 2 should be enumerated\n");
5490 ret = is_truetype_font_installed("MS Shell Dlg 2");
5491 ok(ret, "MS Shell Dlg 2 should be enumerated as a TrueType font\n");
5493 hdc = CreateCompatibleDC(0);
5495 memset(&efnd, 0, sizeof(efnd));
5496 ret = EnumFontFamiliesExA(hdc, NULL, enum_ms_shell_dlg_proc, (LPARAM)&efnd, 0);
5497 ok(ret, "MS Shell Dlg should not be enumerated\n");
5498 ok(!efnd.total, "MS Shell Dlg should not be enumerated\n");
5500 memset(&lf, 0, sizeof(lf));
5501 lf.lfCharSet = DEFAULT_CHARSET;
5503 efnd.total = 0;
5504 strcpy(lf.lfFaceName, "MS Shell Dlg");
5505 ret = EnumFontFamiliesExA(hdc, &lf, enum_ms_shell_dlg_proc, (LPARAM)&efnd, 0);
5506 ok(!ret, "MS Shell Dlg should be enumerated\n");
5507 ok(efnd.total > 0, "MS Shell Dlg should be enumerated\n");
5508 if (efnd.total)
5510 ret = strcmp((const char *)efnd.elf[0].elfLogFont.lfFaceName, "MS Shell Dlg");
5511 ok(!ret, "expected MS Shell Dlg, got %s\n", efnd.elf[0].elfLogFont.lfFaceName);
5512 ret = strcmp((const char *)efnd.elf[0].elfFullName, "MS Shell Dlg");
5513 ok(ret, "did not expect MS Shell Dlg\n");
5516 efnd.total = 0;
5517 ret = EnumFontFamiliesExA(hdc, NULL, enum_ms_shell_dlg2_proc, (LPARAM)&efnd, 0);
5518 ok(ret, "MS Shell Dlg 2 should not be enumerated\n");
5519 ok(!efnd.total, "MS Shell Dlg 2 should not be enumerated\n");
5521 efnd.total = 0;
5522 strcpy(lf.lfFaceName, "MS Shell Dlg 2");
5523 ret = EnumFontFamiliesExA(hdc, &lf, enum_ms_shell_dlg2_proc, (LPARAM)&efnd, 0);
5524 ok(!ret, "MS Shell Dlg 2 should be enumerated\n");
5525 ok(efnd.total > 0, "MS Shell Dlg 2 should be enumerated\n");
5526 if (efnd.total)
5528 ret = strcmp((const char *)efnd.elf[0].elfLogFont.lfFaceName, "MS Shell Dlg 2");
5529 ok(!ret, "expected MS Shell Dlg 2, got %s\n", efnd.elf[0].elfLogFont.lfFaceName);
5530 ret = strcmp((const char *)efnd.elf[0].elfFullName, "MS Shell Dlg 2");
5531 ok(ret, "did not expect MS Shell Dlg 2\n");
5534 heap_free(efnd.elf);
5535 DeleteDC(hdc);
5538 static INT CALLBACK is_font_installed_fullname_proc(const LOGFONTA *lf, const TEXTMETRICA *ntm, DWORD type, LPARAM lParam)
5540 const ENUMLOGFONTA *elf = (const ENUMLOGFONTA *)lf;
5541 const char *fullname = (const char *)lParam;
5543 if (!strcmp((const char *)elf->elfFullName, fullname)) return 0;
5545 return 1;
5548 static BOOL is_font_installed_fullname(const char *family, const char *fullname)
5550 HDC hdc = GetDC(0);
5551 BOOL ret = FALSE;
5553 if(!EnumFontFamiliesA(hdc, family, is_font_installed_fullname_proc, (LPARAM)fullname))
5554 ret = TRUE;
5556 ReleaseDC(0, hdc);
5557 return ret;
5560 static void test_fullname(void)
5562 static const char *TestName[] = {"Lucida Sans Demibold Roman", "Lucida Sans Italic", "Lucida Sans Regular"};
5563 WCHAR bufW[LF_FULLFACESIZE];
5564 char bufA[LF_FULLFACESIZE];
5565 HFONT hfont, of;
5566 LOGFONTA lf;
5567 HDC hdc;
5568 int i;
5569 DWORD ret;
5571 hdc = CreateCompatibleDC(0);
5572 ok(hdc != NULL, "CreateCompatibleDC failed\n");
5574 memset(&lf, 0, sizeof(lf));
5575 lf.lfCharSet = ANSI_CHARSET;
5576 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
5577 lf.lfHeight = 16;
5578 lf.lfWidth = 16;
5579 lf.lfQuality = DEFAULT_QUALITY;
5580 lf.lfItalic = FALSE;
5581 lf.lfWeight = FW_DONTCARE;
5583 for (i = 0; i < ARRAY_SIZE(TestName); i++)
5585 if (!is_font_installed_fullname("Lucida Sans", TestName[i]))
5587 skip("%s is not installed\n", TestName[i]);
5588 continue;
5591 lstrcpyA(lf.lfFaceName, TestName[i]);
5592 hfont = CreateFontIndirectA(&lf);
5593 ok(hfont != 0, "CreateFontIndirectA failed\n");
5595 of = SelectObject(hdc, hfont);
5596 bufW[0] = 0;
5597 bufA[0] = 0;
5598 ret = get_ttf_nametable_entry(hdc, TT_NAME_ID_FULL_NAME, bufW, sizeof(bufW), TT_MS_LANGID_ENGLISH_UNITED_STATES);
5599 ok(ret, "face full name could not be read\n");
5600 WideCharToMultiByte(CP_ACP, 0, bufW, -1, bufA, sizeof(bufA), NULL, FALSE);
5601 ok(!lstrcmpA(bufA, TestName[i]), "font full names don't match: %s != %s\n", TestName[i], bufA);
5602 SelectObject(hdc, of);
5603 DeleteObject(hfont);
5605 DeleteDC(hdc);
5608 static WCHAR *prepend_at(WCHAR *family)
5610 if (!family)
5611 return NULL;
5613 memmove(family + 1, family, (lstrlenW(family) + 1) * sizeof(WCHAR));
5614 family[0] = '@';
5615 return family;
5618 static void test_fullname2_helper(const char *Family)
5620 char *FamilyName, *FaceName, *StyleName, *otmStr;
5621 struct enum_fullname_data efnd;
5622 WCHAR *bufW;
5623 char *bufA;
5624 HFONT hfont, of;
5625 LOGFONTA lf;
5626 HDC hdc;
5627 int i;
5628 DWORD otm_size, ret, buf_size;
5629 OUTLINETEXTMETRICA *otm;
5630 BOOL want_vertical, get_vertical;
5631 want_vertical = ( Family[0] == '@' );
5633 hdc = CreateCompatibleDC(0);
5634 ok(hdc != NULL, "CreateCompatibleDC failed\n");
5636 memset(&lf, 0, sizeof(lf));
5637 lf.lfCharSet = DEFAULT_CHARSET;
5638 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
5639 lf.lfHeight = 16;
5640 lf.lfWidth = 16;
5641 lf.lfQuality = DEFAULT_QUALITY;
5642 lf.lfItalic = FALSE;
5643 lf.lfWeight = FW_DONTCARE;
5644 strcpy(lf.lfFaceName, Family);
5645 memset(&efnd, 0, sizeof(efnd));
5646 EnumFontFamiliesExA(hdc, &lf, enum_fullname_data_proc, (LPARAM)&efnd, 0);
5647 if (efnd.total == 0)
5648 skip("%s is not installed\n", lf.lfFaceName);
5650 for (i = 0; i < efnd.total; i++)
5652 FamilyName = (char *)efnd.elf[i].elfLogFont.lfFaceName;
5653 FaceName = (char *)efnd.elf[i].elfFullName;
5654 StyleName = (char *)efnd.elf[i].elfStyle;
5656 get_vertical = ( FamilyName[0] == '@' );
5657 ok(get_vertical == want_vertical, "Vertical flags don't match: %s %s\n", Family, FamilyName);
5659 lstrcpyA(lf.lfFaceName, FaceName);
5660 hfont = CreateFontIndirectA(&lf);
5661 ok(hfont != 0, "CreateFontIndirectA failed\n");
5663 of = SelectObject(hdc, hfont);
5664 buf_size = GetFontData(hdc, MS_NAME_TAG, 0, NULL, 0);
5665 ok(buf_size != GDI_ERROR, "no name table found\n");
5666 if (buf_size == GDI_ERROR) continue;
5668 bufW = HeapAlloc(GetProcessHeap(), 0, buf_size);
5669 bufA = HeapAlloc(GetProcessHeap(), 0, buf_size);
5671 otm_size = GetOutlineTextMetricsA(hdc, 0, NULL);
5672 otm = HeapAlloc(GetProcessHeap(), 0, otm_size);
5673 memset(otm, 0, otm_size);
5674 ret = GetOutlineTextMetricsA(hdc, otm_size, otm);
5675 ok(ret != 0, "GetOutlineTextMetrics fails!\n");
5676 if (ret == 0) continue;
5678 bufW[0] = 0;
5679 bufA[0] = 0;
5680 ret = get_ttf_nametable_entry(hdc, TT_NAME_ID_FONT_FAMILY, bufW, buf_size, GetSystemDefaultLangID());
5681 if (!ret) ret = get_ttf_nametable_entry(hdc, TT_NAME_ID_FONT_FAMILY, bufW, buf_size, TT_MS_LANGID_ENGLISH_UNITED_STATES);
5682 ok(ret, "%s: FAMILY (family name) could not be read\n", FamilyName);
5683 if (want_vertical) bufW = prepend_at(bufW);
5684 WideCharToMultiByte(CP_ACP, 0, bufW, -1, bufA, buf_size, NULL, FALSE);
5685 ok(!lstrcmpA(FamilyName, bufA), "font family names don't match: returned %s, expect %s\n", FamilyName, bufA);
5686 otmStr = (LPSTR)otm + (UINT_PTR)otm->otmpFamilyName;
5687 ok(!lstrcmpA(FamilyName, otmStr), "FamilyName %s doesn't match otmpFamilyName %s\n", FamilyName, otmStr);
5689 bufW[0] = 0;
5690 bufA[0] = 0;
5691 ret = get_ttf_nametable_entry(hdc, TT_NAME_ID_FULL_NAME, bufW, buf_size, GetSystemDefaultLangID());
5692 if (!ret) ret = get_ttf_nametable_entry(hdc, TT_NAME_ID_FULL_NAME, bufW, buf_size, TT_MS_LANGID_ENGLISH_UNITED_STATES);
5693 ok(ret, "FULL_NAME (face name) could not be read\n");
5694 if (want_vertical) bufW = prepend_at(bufW);
5695 WideCharToMultiByte(CP_ACP, 0, bufW, -1, bufA, buf_size, NULL, FALSE);
5696 ok(!lstrcmpA(FaceName, bufA), "%s: font face names don't match: returned %s, expect %s\n", FamilyName, FaceName, bufA);
5697 otmStr = (LPSTR)otm + (UINT_PTR)otm->otmpFaceName;
5698 ok(!lstrcmpA(FaceName, otmStr), "%s: FaceName %s doesn't match otmpFaceName %s\n", FamilyName, FaceName, otmStr);
5700 bufW[0] = 0;
5701 bufA[0] = 0;
5702 ret = get_ttf_nametable_entry(hdc, TT_NAME_ID_FONT_SUBFAMILY, bufW, buf_size, GetSystemDefaultLangID());
5703 if (!ret) ret = get_ttf_nametable_entry(hdc, TT_NAME_ID_FONT_SUBFAMILY, bufW, buf_size, TT_MS_LANGID_ENGLISH_UNITED_STATES);
5704 ok(ret, "%s: SUBFAMILY (style name) could not be read\n", FamilyName);
5705 WideCharToMultiByte(CP_ACP, 0, bufW, -1, bufA, buf_size, NULL, FALSE);
5706 ok(!lstrcmpA(StyleName, bufA), "%s: style names don't match: returned %s, expect %s\n", FamilyName, StyleName, bufA);
5707 otmStr = (LPSTR)otm + (UINT_PTR)otm->otmpStyleName;
5708 ok(!lstrcmpA(StyleName, otmStr), "%s: StyleName %s doesn't match otmpStyleName %s\n", FamilyName, StyleName, otmStr);
5710 bufW[0] = 0;
5711 bufA[0] = 0;
5712 ret = get_ttf_nametable_entry(hdc, TT_NAME_ID_UNIQUE_ID, bufW, buf_size, GetSystemDefaultLangID());
5713 if (!ret) ret = get_ttf_nametable_entry(hdc, TT_NAME_ID_UNIQUE_ID, bufW, buf_size, TT_MS_LANGID_ENGLISH_UNITED_STATES);
5714 ok(ret, "%s: UNIQUE_ID (full name) could not be read\n", FamilyName);
5715 WideCharToMultiByte(CP_ACP, 0, bufW, -1, bufA, buf_size, NULL, FALSE);
5716 otmStr = (LPSTR)otm + (UINT_PTR)otm->otmpFullName;
5717 ok(!lstrcmpA(otmStr, bufA), "%s: UNIQUE ID (full name) doesn't match: returned %s, expect %s\n", FamilyName, otmStr, bufA);
5719 SelectObject(hdc, of);
5720 DeleteObject(hfont);
5722 HeapFree(GetProcessHeap(), 0, otm);
5723 HeapFree(GetProcessHeap(), 0, bufW);
5724 HeapFree(GetProcessHeap(), 0, bufA);
5726 heap_free(efnd.elf);
5727 DeleteDC(hdc);
5730 static void test_fullname2(void)
5732 test_fullname2_helper("Arial");
5733 test_fullname2_helper("DejaVu Sans");
5734 test_fullname2_helper("Lucida Sans");
5735 test_fullname2_helper("Tahoma");
5736 test_fullname2_helper("Webdings");
5737 test_fullname2_helper("Wingdings");
5738 test_fullname2_helper("SimSun");
5739 test_fullname2_helper("NSimSun");
5740 test_fullname2_helper("MingLiu");
5741 test_fullname2_helper("PMingLiu");
5742 test_fullname2_helper("WenQuanYi Micro Hei");
5743 test_fullname2_helper("MS UI Gothic");
5744 test_fullname2_helper("Ume UI Gothic");
5745 test_fullname2_helper("MS Gothic");
5746 test_fullname2_helper("Ume Gothic");
5747 test_fullname2_helper("MS PGothic");
5748 test_fullname2_helper("Ume P Gothic");
5749 test_fullname2_helper("Gulim");
5750 test_fullname2_helper("Batang");
5751 test_fullname2_helper("UnBatang");
5752 test_fullname2_helper("UnDotum");
5753 test_fullname2_helper("@SimSun");
5754 test_fullname2_helper("@NSimSun");
5755 test_fullname2_helper("@MingLiu");
5756 test_fullname2_helper("@PMingLiu");
5757 test_fullname2_helper("@WenQuanYi Micro Hei");
5758 test_fullname2_helper("@MS UI Gothic");
5759 test_fullname2_helper("@Ume UI Gothic");
5760 test_fullname2_helper("@MS Gothic");
5761 test_fullname2_helper("@Ume Gothic");
5762 test_fullname2_helper("@MS PGothic");
5763 test_fullname2_helper("@Ume P Gothic");
5764 test_fullname2_helper("@Gulim");
5765 test_fullname2_helper("@Batang");
5766 test_fullname2_helper("@UnBatang");
5767 test_fullname2_helper("@UnDotum");
5771 static void test_GetGlyphOutline_empty_contour(void)
5773 HDC hdc;
5774 LOGFONTA lf;
5775 HFONT hfont, hfont_prev;
5776 TTPOLYGONHEADER *header;
5777 GLYPHMETRICS gm;
5778 char buf[1024];
5779 DWORD ret;
5781 memset(&lf, 0, sizeof(lf));
5782 lf.lfHeight = 72;
5783 lstrcpyA(lf.lfFaceName, "wine_test");
5785 hfont = CreateFontIndirectA(&lf);
5786 ok(hfont != 0, "CreateFontIndirectA error %u\n", GetLastError());
5788 hdc = GetDC(NULL);
5790 hfont_prev = SelectObject(hdc, hfont);
5791 ok(hfont_prev != NULL, "SelectObject failed\n");
5793 ret = GetGlyphOutlineW(hdc, 0xa8, GGO_NATIVE, &gm, 0, NULL, &mat);
5794 ok(ret == 228, "GetGlyphOutline returned %d, expected 228\n", ret);
5796 header = (TTPOLYGONHEADER*)buf;
5797 ret = GetGlyphOutlineW(hdc, 0xa8, GGO_NATIVE, &gm, sizeof(buf), buf, &mat);
5798 ok(ret == 228, "GetGlyphOutline returned %d, expected 228\n", ret);
5799 ok(header->cb == 36, "header->cb = %d, expected 36\n", header->cb);
5800 ok(header->dwType == TT_POLYGON_TYPE, "header->dwType = %d, expected TT_POLYGON_TYPE\n", header->dwType);
5801 header = (TTPOLYGONHEADER*)((char*)header+header->cb);
5802 ok(header->cb == 96, "header->cb = %d, expected 96\n", header->cb);
5803 header = (TTPOLYGONHEADER*)((char*)header+header->cb);
5804 ok(header->cb == 96, "header->cb = %d, expected 96\n", header->cb);
5806 SelectObject(hdc, hfont_prev);
5807 DeleteObject(hfont);
5808 ReleaseDC(NULL, hdc);
5811 static void test_GetGlyphOutline_metric_clipping(void)
5813 HDC hdc;
5814 LOGFONTA lf;
5815 HFONT hfont, hfont_prev;
5816 GLYPHMETRICS gm;
5817 TEXTMETRICA tm;
5818 TEXTMETRICW tmW;
5819 DWORD ret;
5821 memset(&lf, 0, sizeof(lf));
5822 lf.lfHeight = 72;
5823 lstrcpyA(lf.lfFaceName, "wine_test");
5825 SetLastError(0xdeadbeef);
5826 hfont = CreateFontIndirectA(&lf);
5827 ok(hfont != 0, "CreateFontIndirectA error %u\n", GetLastError());
5829 hdc = GetDC(NULL);
5831 hfont_prev = SelectObject(hdc, hfont);
5832 ok(hfont_prev != NULL, "SelectObject failed\n");
5834 SetLastError(0xdeadbeef);
5835 ret = GetTextMetricsA(hdc, &tm);
5836 ok(ret, "GetTextMetrics error %u\n", GetLastError());
5838 GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
5839 ok(gm.gmptGlyphOrigin.y <= tm.tmAscent,
5840 "Glyph top(%d) exceeds ascent(%d)\n",
5841 gm.gmptGlyphOrigin.y, tm.tmAscent);
5842 GetGlyphOutlineA(hdc, 'D', GGO_METRICS, &gm, 0, NULL, &mat);
5843 ok(gm.gmptGlyphOrigin.y - gm.gmBlackBoxY >= -tm.tmDescent,
5844 "Glyph bottom(%d) exceeds descent(%d)\n",
5845 gm.gmptGlyphOrigin.y - gm.gmBlackBoxY, -tm.tmDescent);
5847 /* Test tmLastChar - wine_test has code points fffb-fffe mapped to glyph 0 */
5848 GetTextMetricsW(hdc, &tmW);
5849 todo_wine
5850 ok( tmW.tmLastChar == 0xfffe, "got %04x\n", tmW.tmLastChar);
5852 SelectObject(hdc, hfont_prev);
5853 DeleteObject(hfont);
5854 ReleaseDC(NULL, hdc);
5857 static void test_GetGlyphOutline_character(void)
5859 HFONT hfont, hfont_old;
5860 LOGFONTA lf;
5861 HDC hdc;
5862 DWORD ret;
5863 GLYPHMETRICS gm1, gm2, gmn;
5864 char test_chars[] = { 'A', 'D', '!', '\0' };
5865 char *current_char;
5867 memset(&lf, 0, sizeof(lf));
5868 lf.lfHeight = 72;
5869 lstrcpyA(lf.lfFaceName, "wine_test");
5871 hfont = CreateFontIndirectA(&lf);
5872 ok(hfont != 0, "CreateFontIndirectA error %u\n", GetLastError());
5874 hdc = GetDC(NULL);
5876 hfont_old = SelectObject(hdc, hfont);
5877 ok(hfont_old != NULL, "SelectObject failed\n");
5879 ret = GetGlyphOutlineW(hdc, 'Z', GGO_METRICS, &gmn, 0, NULL, &mat);
5880 ok(ret != GDI_ERROR, "GetGlyphOutlineW failed to default to .notdef for character 'Z'\n");
5882 for (current_char = test_chars; *current_char != '\0'; current_char++)
5884 ret = GetGlyphOutlineW(hdc, *current_char, GGO_METRICS, &gm1, 0, NULL, &mat);
5885 ok(ret != GDI_ERROR, "GetGlyphOutlineW failed for '%c'\n", *current_char);
5886 ok(memcmp(&gm1, &gmn, sizeof(gmn)) != 0, "the test character '%c' matches .notdef\n", *current_char);
5888 ret = GetGlyphOutlineW(hdc, 0x10000 + *current_char, GGO_METRICS, &gm2, 0, NULL, &mat);
5889 ok(ret != GDI_ERROR, "GetGlyphOutlineW failed for 0x10000 + '%c'\n", *current_char);
5890 ok(memcmp(&gm1, &gm2, sizeof(gmn)) == 0, "GetGlyphOutlineW returned wrong metrics for character 0x10000 + '%c'\n", *current_char);
5893 ret = GetGlyphOutlineW(hdc, 0x3, GGO_METRICS|GGO_GLYPH_INDEX, &gm1, 0, NULL, &mat);
5894 ok(ret != GDI_ERROR, "GetGlyphOutlineW failed for glyph index 0x3\n");
5896 ret = GetGlyphOutlineW(hdc, 0xFFFF, GGO_METRICS|GGO_GLYPH_INDEX, &gm2, 0, NULL, &mat);
5897 ok(ret == GDI_ERROR, "GetGlyphOutlineW for nonexistent glyph index 0xFFFF has succeeded\n");
5899 ret = GetGlyphOutlineW(hdc, 0x10003, GGO_METRICS|GGO_GLYPH_INDEX, &gm2, 0, NULL, &mat);
5900 ok(ret != GDI_ERROR, "GetGlyphOutlineW for index 0x10003 has failed\n");
5901 ok(memcmp(&gm1, &gm2, sizeof(gmn)) == 0, "GetGlyphOutlineW returned wrong metrics for glyph 0x10003\n");
5903 SelectObject(hdc, hfont_old);
5904 DeleteObject(hfont);
5905 ReleaseDC(NULL, hdc);
5908 static void test_fstype_fixup(void)
5910 HDC hdc;
5911 LOGFONTA lf;
5912 HFONT hfont, hfont_prev;
5913 DWORD ret;
5914 OUTLINETEXTMETRICA *otm;
5915 DWORD otm_size;
5917 memset(&lf, 0, sizeof(lf));
5918 lf.lfHeight = 72;
5919 lstrcpyA(lf.lfFaceName, "wine_test");
5921 SetLastError(0xdeadbeef);
5922 hfont = CreateFontIndirectA(&lf);
5923 ok(hfont != 0, "CreateFontIndirectA error %u\n", GetLastError());
5925 hdc = GetDC(NULL);
5927 hfont_prev = SelectObject(hdc, hfont);
5928 ok(hfont_prev != NULL, "SelectObject failed\n");
5930 otm_size = GetOutlineTextMetricsA(hdc, 0, NULL);
5931 otm = HeapAlloc(GetProcessHeap(), 0, otm_size);
5932 otm->otmSize = sizeof(*otm);
5933 ret = GetOutlineTextMetricsA(hdc, otm->otmSize, otm);
5934 ok(ret == otm->otmSize, "expected %u, got %u, error %d\n", otm->otmSize, ret, GetLastError());
5936 /* Test font has fsType set to 0x7fff, test that reserved bits are filtered out,
5937 valid bits are 1, 2, 3, 8, 9. */
5938 ok((otm->otmfsType & ~0x30e) == 0, "fsType %#x\n", otm->otmfsType);
5940 HeapFree(GetProcessHeap(), 0, otm);
5942 SelectObject(hdc, hfont_prev);
5943 DeleteObject(hfont);
5944 ReleaseDC(NULL, hdc);
5947 static void test_CreateScalableFontResource(void)
5949 char ttf_name[MAX_PATH];
5950 char tmp_path[MAX_PATH];
5951 char fot_name[MAX_PATH];
5952 char *file_part;
5953 DWORD ret;
5954 int i;
5956 if (!pAddFontResourceExA || !pRemoveFontResourceExA)
5958 win_skip("AddFontResourceExA is not available on this platform\n");
5959 return;
5962 if (!write_ttf_file("wine_test.ttf", ttf_name))
5964 skip("Failed to create ttf file for testing\n");
5965 return;
5968 trace("created %s\n", ttf_name);
5970 ret = is_truetype_font_installed("wine_test");
5971 ok(!ret, "font wine_test should not be enumerated\n");
5973 ret = GetTempPathA(MAX_PATH, tmp_path);
5974 ok(ret, "GetTempPath() error %d\n", GetLastError());
5975 ret = GetTempFileNameA(tmp_path, "fot", 0, fot_name);
5976 ok(ret, "GetTempFileName() error %d\n", GetLastError());
5978 ret = GetFileAttributesA(fot_name);
5979 ok(ret != INVALID_FILE_ATTRIBUTES, "file %s does not exist\n", fot_name);
5981 SetLastError(0xdeadbeef);
5982 ret = CreateScalableFontResourceA(0, fot_name, ttf_name, NULL);
5983 ok(!ret, "CreateScalableFontResource() should fail\n");
5984 ok(GetLastError() == ERROR_FILE_EXISTS, "not expected error %d\n", GetLastError());
5986 SetLastError(0xdeadbeef);
5987 ret = CreateScalableFontResourceA(0, fot_name, ttf_name, "");
5988 ok(!ret, "CreateScalableFontResource() should fail\n");
5989 ok(GetLastError() == ERROR_FILE_EXISTS, "not expected error %d\n", GetLastError());
5991 file_part = strrchr(ttf_name, '\\');
5992 SetLastError(0xdeadbeef);
5993 ret = CreateScalableFontResourceA(0, fot_name, file_part, tmp_path);
5994 ok(!ret, "CreateScalableFontResource() should fail\n");
5995 ok(GetLastError() == ERROR_FILE_EXISTS, "not expected error %d\n", GetLastError());
5997 SetLastError(0xdeadbeef);
5998 ret = CreateScalableFontResourceA(0, fot_name, "random file name", tmp_path);
5999 ok(!ret, "CreateScalableFontResource() should fail\n");
6000 ok(GetLastError() == ERROR_INVALID_PARAMETER, "not expected error %d\n", GetLastError());
6002 SetLastError(0xdeadbeef);
6003 ret = CreateScalableFontResourceA(0, fot_name, NULL, ttf_name);
6004 ok(!ret, "CreateScalableFontResource() should fail\n");
6005 ok(GetLastError() == ERROR_INVALID_PARAMETER, "not expected error %d\n", GetLastError());
6007 ret = DeleteFileA(fot_name);
6008 ok(ret, "DeleteFile() error %d\n", GetLastError());
6010 ret = pRemoveFontResourceExA(fot_name, 0, 0);
6011 ok(!ret, "RemoveFontResourceEx() should fail\n");
6013 /* test public font resource */
6014 SetLastError(0xdeadbeef);
6015 ret = CreateScalableFontResourceA(0, fot_name, ttf_name, NULL);
6016 ok(ret, "CreateScalableFontResource() error %d\n", GetLastError());
6018 ret = is_truetype_font_installed("wine_test");
6019 ok(!ret, "font wine_test should not be enumerated\n");
6021 SetLastError(0xdeadbeef);
6022 ret = pAddFontResourceExA(fot_name, 0, 0);
6023 ok(ret, "AddFontResourceEx() error %d\n", GetLastError());
6025 ret = is_truetype_font_installed("wine_test");
6026 ok(ret, "font wine_test should be enumerated\n");
6028 test_GetGlyphOutline_empty_contour();
6029 test_GetGlyphOutline_metric_clipping();
6030 test_GetGlyphOutline_character();
6031 test_fstype_fixup();
6033 ret = pRemoveFontResourceExA(fot_name, FR_PRIVATE, 0);
6034 ok(!ret, "RemoveFontResourceEx() with not matching flags should fail\n");
6036 SetLastError(0xdeadbeef);
6037 ret = pRemoveFontResourceExA(fot_name, 0, 0);
6038 ok(ret, "RemoveFontResourceEx() error %d\n", GetLastError());
6040 ret = is_truetype_font_installed("wine_test");
6041 ok(!ret, "font wine_test should not be enumerated\n");
6043 ret = pRemoveFontResourceExA(fot_name, 0, 0);
6044 ok(!ret, "RemoveFontResourceEx() should fail\n");
6046 /* test refcounting */
6047 for (i = 0; i < 5; i++)
6049 SetLastError(0xdeadbeef);
6050 ret = pAddFontResourceExA(fot_name, 0, 0);
6051 ok(ret, "AddFontResourceEx() error %d\n", GetLastError());
6053 for (i = 0; i < 5; i++)
6055 SetLastError(0xdeadbeef);
6056 ret = pRemoveFontResourceExA(fot_name, 0, 0);
6057 ok(ret, "RemoveFontResourceEx() error %d\n", GetLastError());
6059 ret = pRemoveFontResourceExA(fot_name, 0, 0);
6060 ok(!ret, "RemoveFontResourceEx() should fail\n");
6062 DeleteFileA(fot_name);
6064 /* test hidden font resource */
6065 SetLastError(0xdeadbeef);
6066 ret = CreateScalableFontResourceA(1, fot_name, ttf_name, NULL);
6067 ok(ret, "CreateScalableFontResource() error %d\n", GetLastError());
6069 ret = is_truetype_font_installed("wine_test");
6070 ok(!ret, "font wine_test should not be enumerated\n");
6072 SetLastError(0xdeadbeef);
6073 ret = pAddFontResourceExA(fot_name, 0, 0);
6074 ok(ret, "AddFontResourceEx() error %d\n", GetLastError());
6076 ret = is_truetype_font_installed("wine_test");
6077 todo_wine
6078 ok(!ret, "font wine_test should not be enumerated\n");
6080 /* XP allows removing a private font added with 0 flags */
6081 SetLastError(0xdeadbeef);
6082 ret = pRemoveFontResourceExA(fot_name, FR_PRIVATE, 0);
6083 ok(ret, "RemoveFontResourceEx() error %d\n", GetLastError());
6085 ret = is_truetype_font_installed("wine_test");
6086 ok(!ret, "font wine_test should not be enumerated\n");
6088 ret = pRemoveFontResourceExA(fot_name, 0, 0);
6089 ok(!ret, "RemoveFontResourceEx() should fail\n");
6091 DeleteFileA(fot_name);
6092 DeleteFileA(ttf_name);
6095 static void check_vertical_font(const char *name, BOOL *installed, BOOL *selected, GLYPHMETRICS *gm, WORD *gi)
6097 LOGFONTA lf;
6098 HFONT hfont, hfont_prev;
6099 HDC hdc;
6100 char facename[100];
6101 DWORD ret;
6102 static const WCHAR str[] = { 0x2025 };
6104 *installed = is_truetype_font_installed(name);
6106 lf.lfHeight = -18;
6107 lf.lfWidth = 0;
6108 lf.lfEscapement = 0;
6109 lf.lfOrientation = 0;
6110 lf.lfWeight = FW_DONTCARE;
6111 lf.lfItalic = 0;
6112 lf.lfUnderline = 0;
6113 lf.lfStrikeOut = 0;
6114 lf.lfCharSet = DEFAULT_CHARSET;
6115 lf.lfOutPrecision = OUT_TT_ONLY_PRECIS;
6116 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
6117 lf.lfQuality = DEFAULT_QUALITY;
6118 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
6119 strcpy(lf.lfFaceName, name);
6121 hfont = CreateFontIndirectA(&lf);
6122 ok(hfont != NULL, "CreateFontIndirectA failed\n");
6124 hdc = GetDC(NULL);
6126 hfont_prev = SelectObject(hdc, hfont);
6127 ok(hfont_prev != NULL, "SelectObject failed\n");
6129 ret = GetTextFaceA(hdc, sizeof facename, facename);
6130 ok(ret, "GetTextFaceA failed\n");
6131 *selected = !strcmp(facename, name);
6133 ret = GetGlyphOutlineW(hdc, 0x2025, GGO_METRICS, gm, 0, NULL, &mat);
6134 ok(ret != GDI_ERROR, "GetGlyphOutlineW failed\n");
6135 if (!*selected)
6136 memset(gm, 0, sizeof *gm);
6138 ret = pGetGlyphIndicesW(hdc, str, 1, gi, 0);
6139 ok(ret != GDI_ERROR, "GetGlyphIndicesW failed\n");
6141 SelectObject(hdc, hfont_prev);
6142 DeleteObject(hfont);
6143 ReleaseDC(NULL, hdc);
6146 static void check_vertical_metrics(const char *face)
6148 LOGFONTA lf;
6149 HFONT hfont, hfont_prev;
6150 HDC hdc;
6151 DWORD ret;
6152 GLYPHMETRICS rgm, vgm;
6153 const UINT code = 0x5EAD, height = 1000;
6154 WORD idx;
6155 ABC abc, vabc;
6156 OUTLINETEXTMETRICA otm;
6157 USHORT numOfLongVerMetrics;
6159 hdc = GetDC(NULL);
6161 memset(&lf, 0, sizeof(lf));
6162 strcpy(lf.lfFaceName, face);
6163 lf.lfHeight = -height;
6164 lf.lfCharSet = DEFAULT_CHARSET;
6165 lf.lfEscapement = lf.lfOrientation = 900;
6166 hfont = CreateFontIndirectA(&lf);
6167 hfont_prev = SelectObject(hdc, hfont);
6168 ret = GetGlyphOutlineW(hdc, code, GGO_METRICS, &rgm, 0, NULL, &mat);
6169 ok(ret != GDI_ERROR, "GetGlyphOutlineW failed\n");
6170 ret = GetCharABCWidthsW(hdc, code, code, &abc);
6171 ok(ret, "GetCharABCWidthsW failed\n");
6172 DeleteObject(SelectObject(hdc, hfont_prev));
6174 memset(&lf, 0, sizeof(lf));
6175 strcpy(lf.lfFaceName, "@");
6176 strcat(lf.lfFaceName, face);
6177 lf.lfHeight = -height;
6178 lf.lfCharSet = DEFAULT_CHARSET;
6179 hfont = CreateFontIndirectA(&lf);
6180 hfont_prev = SelectObject(hdc, hfont);
6181 ret = GetGlyphOutlineW(hdc, code, GGO_METRICS, &vgm, 0, NULL, &mat);
6182 ok(ret != GDI_ERROR, "GetGlyphOutlineW failed\n");
6183 ret = GetCharABCWidthsW(hdc, code, code, &vabc);
6184 ok(ret, "GetCharABCWidthsW failed\n");
6185 ok(vabc.abcA == vgm.gmptGlyphOrigin.x, "expected %d, got %d\n",
6186 vabc.abcA, vgm.gmptGlyphOrigin.x);
6187 ok(vabc.abcB == vgm.gmBlackBoxX, "expected %d, got %d\n",
6188 vabc.abcB, vgm.gmBlackBoxX);
6189 ok(vabc.abcA + vabc.abcB + vabc.abcC == vgm.gmCellIncX,
6190 "expected %d, got %d\n",
6191 vabc.abcA + vabc.abcB + vabc.abcC, vgm.gmCellIncX);
6193 memset(&otm, 0, sizeof(otm));
6194 otm.otmSize = sizeof(otm);
6195 ret = GetOutlineTextMetricsA(hdc, sizeof(otm), &otm);
6196 ok(ret != 0, "GetOutlineTextMetricsA failed\n");
6198 if (GetFontData(hdc, MS_MAKE_TAG('v','h','e','a'), sizeof(SHORT) * 17,
6199 &numOfLongVerMetrics, sizeof(numOfLongVerMetrics)) != GDI_ERROR) {
6200 int offset;
6201 SHORT topSideBearing;
6203 if (!pGetGlyphIndicesW) {
6204 win_skip("GetGlyphIndices is not available on this platform\n");
6206 else {
6207 ret = pGetGlyphIndicesW(hdc, (LPCWSTR)&code, 1, &idx, 0);
6208 ok(ret != 0, "GetGlyphIndicesW failed\n");
6209 numOfLongVerMetrics = GET_BE_WORD(numOfLongVerMetrics);
6210 if (numOfLongVerMetrics > idx)
6211 offset = idx * 2 + 1;
6212 else
6213 offset = numOfLongVerMetrics * 2 + (idx - numOfLongVerMetrics);
6214 ret = GetFontData(hdc, MS_MAKE_TAG('v','m','t','x'), offset * sizeof(SHORT),
6215 &topSideBearing, sizeof(SHORT));
6216 ok(ret != GDI_ERROR, "GetFontData(vmtx) failed\n");
6217 topSideBearing = GET_BE_WORD(topSideBearing);
6218 ok(match_off_by_1(vgm.gmptGlyphOrigin.x,
6219 MulDiv(topSideBearing, height, otm.otmEMSquare), FALSE),
6220 "expected %d, got %d\n",
6221 MulDiv(topSideBearing, height, otm.otmEMSquare), vgm.gmptGlyphOrigin.x);
6224 else
6226 ok(vgm.gmptGlyphOrigin.x == rgm.gmptGlyphOrigin.x + vgm.gmCellIncX + otm.otmDescent,
6227 "got %d, expected rgm.origin.x(%d) + vgm.cellIncX(%d) + descent(%d)\n",
6228 vgm.gmptGlyphOrigin.x, rgm.gmptGlyphOrigin.x, vgm.gmCellIncX, otm.otmDescent);
6231 ok(vgm.gmptGlyphOrigin.y == abc.abcA + abc.abcB + otm.otmDescent ||
6232 broken(vgm.gmptGlyphOrigin.y == abc.abcA + abc.abcB - otm.otmTextMetrics.tmDescent) /* win2k */,
6233 "got %d, expected abcA(%d) + abcB(%u) + descent(%d)\n",
6234 (INT)vgm.gmptGlyphOrigin.y, abc.abcA, abc.abcB, otm.otmDescent);
6236 DeleteObject(SelectObject(hdc, hfont_prev));
6237 ReleaseDC(NULL, hdc);
6240 static void test_vertical_font(void)
6242 char ttf_name[MAX_PATH];
6243 int num, i;
6244 BOOL ret, installed, selected;
6245 GLYPHMETRICS gm;
6246 WORD hgi, vgi;
6247 const char* face_list[] = {
6248 "@WineTestVertical", /* has vmtx table */
6249 "@Ume Gothic", /* doesn't have vmtx table */
6250 "@MS UI Gothic", /* has vmtx table, available on native */
6253 if (!pAddFontResourceExA || !pRemoveFontResourceExA || !pGetGlyphIndicesW)
6255 win_skip("AddFontResourceExA or GetGlyphIndicesW is not available on this platform\n");
6256 return;
6259 if (!write_ttf_file("vertical.ttf", ttf_name))
6261 skip("Failed to create ttf file for testing\n");
6262 return;
6265 num = pAddFontResourceExA(ttf_name, FR_PRIVATE, 0);
6266 ok(num == 2, "AddFontResourceExA should add 2 fonts from vertical.ttf\n");
6268 check_vertical_font("WineTestVertical", &installed, &selected, &gm, &hgi);
6269 ok(installed, "WineTestVertical is not installed\n");
6270 ok(selected, "WineTestVertical is not selected\n");
6271 ok(gm.gmBlackBoxX > gm.gmBlackBoxY,
6272 "gmBlackBoxX(%u) should be greater than gmBlackBoxY(%u) if horizontal\n",
6273 gm.gmBlackBoxX, gm.gmBlackBoxY);
6275 check_vertical_font("@WineTestVertical", &installed, &selected, &gm, &vgi);
6276 ok(installed, "@WineTestVertical is not installed\n");
6277 ok(selected, "@WineTestVertical is not selected\n");
6278 ok(gm.gmBlackBoxX > gm.gmBlackBoxY,
6279 "gmBlackBoxX(%u) should be less than gmBlackBoxY(%u) if vertical\n",
6280 gm.gmBlackBoxX, gm.gmBlackBoxY);
6282 ok(hgi != vgi, "same glyph h:%u v:%u\n", hgi, vgi);
6284 for (i = 0; i < ARRAY_SIZE(face_list); i++) {
6285 const char* face = face_list[i];
6286 if (!is_truetype_font_installed(face)) {
6287 skip("%s is not installed\n", face);
6288 continue;
6290 check_vertical_metrics(&face[1]);
6293 ret = pRemoveFontResourceExA(ttf_name, FR_PRIVATE, 0);
6294 ok(ret, "RemoveFontResourceEx() error %d\n", GetLastError());
6296 DeleteFileA(ttf_name);
6299 static INT CALLBACK has_vertical_font_proc(const LOGFONTA *lf, const TEXTMETRICA *ntm,
6300 DWORD type, LPARAM lParam)
6302 if (lf->lfFaceName[0] == '@') {
6303 return 0;
6305 return 1;
6308 static void test_east_asian_font_selection(void)
6310 HDC hdc;
6311 UINT charset[] = { SHIFTJIS_CHARSET, HANGEUL_CHARSET, JOHAB_CHARSET,
6312 GB2312_CHARSET, CHINESEBIG5_CHARSET };
6313 size_t i;
6315 hdc = GetDC(NULL);
6317 for (i = 0; i < ARRAY_SIZE(charset); i++)
6319 LOGFONTA lf;
6320 HFONT hfont;
6321 char face_name[LF_FACESIZE];
6322 int ret;
6324 memset(&lf, 0, sizeof lf);
6325 lf.lfFaceName[0] = '\0';
6326 lf.lfCharSet = charset[i];
6328 if (EnumFontFamiliesExA(hdc, &lf, has_vertical_font_proc, 0, 0))
6330 skip("Vertical font for charset %u is not installed\n", charset[i]);
6331 continue;
6334 hfont = CreateFontIndirectA(&lf);
6335 hfont = SelectObject(hdc, hfont);
6336 memset(face_name, 0, sizeof face_name);
6337 ret = GetTextFaceA(hdc, sizeof face_name, face_name);
6338 ok(ret && face_name[0] != '@',
6339 "expected non-vertical face for charset %u, got %s\n", charset[i], face_name);
6340 DeleteObject(SelectObject(hdc, hfont));
6342 memset(&lf, 0, sizeof lf);
6343 strcpy(lf.lfFaceName, "@");
6344 lf.lfCharSet = charset[i];
6345 hfont = CreateFontIndirectA(&lf);
6346 hfont = SelectObject(hdc, hfont);
6347 memset(face_name, 0, sizeof face_name);
6348 ret = GetTextFaceA(hdc, sizeof face_name, face_name);
6349 ok(ret && face_name[0] == '@',
6350 "expected vertical face for charset %u, got %s\n", charset[i], face_name);
6351 DeleteObject(SelectObject(hdc, hfont));
6353 ReleaseDC(NULL, hdc);
6356 static int get_font_dpi(const LOGFONTA *lf, int *height)
6358 HDC hdc = CreateCompatibleDC(0);
6359 HFONT hfont;
6360 TEXTMETRICA tm;
6361 int ret;
6363 hfont = CreateFontIndirectA(lf);
6364 ok(hfont != 0, "CreateFontIndirect failed\n");
6366 SelectObject(hdc, hfont);
6367 ret = GetTextMetricsA(hdc, &tm);
6368 ok(ret, "GetTextMetrics failed\n");
6369 ret = tm.tmDigitizedAspectX;
6370 if (height) *height = tm.tmHeight;
6372 DeleteDC(hdc);
6373 DeleteObject(hfont);
6375 return ret;
6378 static void test_stock_fonts(void)
6380 static const int font[] =
6382 ANSI_FIXED_FONT, ANSI_VAR_FONT, SYSTEM_FONT, DEVICE_DEFAULT_FONT, DEFAULT_GUI_FONT
6383 /* SYSTEM_FIXED_FONT, OEM_FIXED_FONT */
6385 static const struct test_data
6387 int charset, weight, height, height_pixels, dpi;
6388 const char face_name[LF_FACESIZE];
6389 WORD lang_id;
6390 } td[][17] =
6392 { /* ANSI_FIXED_FONT */
6393 { ANSI_CHARSET, FW_NORMAL, 12, 12, 96, "Courier", LANG_ARABIC },
6394 { ANSI_CHARSET, FW_NORMAL, 12, 12, 96, "Courier", LANG_HEBREW},
6395 { DEFAULT_CHARSET, FW_NORMAL, 12, 13, 96, "Courier" },
6396 { DEFAULT_CHARSET, FW_NORMAL, 12, 13, 120, "Courier" },
6397 { 0 }
6399 { /* ANSI_VAR_FONT */
6400 { DEFAULT_CHARSET, FW_NORMAL, 12, 13, 96, "MS Sans Serif" },
6401 { DEFAULT_CHARSET, FW_NORMAL, 12, 13, 120, "MS Sans Serif" },
6402 { 0 }
6404 { /* SYSTEM_FONT */
6405 { SHIFTJIS_CHARSET, FW_NORMAL, 18, 18, 96, "System" },
6406 { SHIFTJIS_CHARSET, FW_NORMAL, 22, 22, 120, "System" },
6407 { HANGEUL_CHARSET, FW_NORMAL, 16, 16, 96, "System" },
6408 { HANGEUL_CHARSET, FW_NORMAL, 20, 20, 120, "System" },
6409 { DEFAULT_CHARSET, FW_BOLD, 16, 16, 96, "System" },
6410 { DEFAULT_CHARSET, FW_BOLD, 20, 20, 120, "System" },
6411 { 0 }
6413 { /* DEVICE_DEFAULT_FONT */
6414 { SHIFTJIS_CHARSET, FW_NORMAL, 18, 18, 96, "System" },
6415 { SHIFTJIS_CHARSET, FW_NORMAL, 22, 22, 120, "System" },
6416 { HANGEUL_CHARSET, FW_NORMAL, 16, 16, 96, "System" },
6417 { HANGEUL_CHARSET, FW_NORMAL, 20, 20, 120, "System" },
6418 { DEFAULT_CHARSET, FW_BOLD, 16, 16, 96, "System" },
6419 { DEFAULT_CHARSET, FW_BOLD, 20, 20, 120, "System" },
6420 { 0 }
6422 { /* DEFAULT_GUI_FONT */
6423 { SHIFTJIS_CHARSET, FW_NORMAL, -11, 13, 96, "MS Shell Dlg" },
6424 { SHIFTJIS_CHARSET, FW_NORMAL, -13, 16, 120, "MS Shell Dlg" },
6425 { SHIFTJIS_CHARSET, FW_NORMAL, -12, 15, 96, "?MS UI Gothic" },
6426 { SHIFTJIS_CHARSET, FW_NORMAL, -15, 18, 120, "?MS UI Gothic" },
6427 { HANGEUL_CHARSET, FW_NORMAL, -11, 13, 96, "MS Shell Dlg" },
6428 { HANGEUL_CHARSET, FW_NORMAL, -13, 16, 120, "MS Shell Dlg" },
6429 { HANGEUL_CHARSET, FW_NORMAL, -12, 15, 96, "?Gulim" },
6430 { HANGEUL_CHARSET, FW_NORMAL, -15, 18, 120, "?Gulim" },
6431 { GB2312_CHARSET, FW_NORMAL, -11, 13, 96, "MS Shell Dlg" },
6432 { GB2312_CHARSET, FW_NORMAL, -13, 16, 120, "MS Shell Dlg" },
6433 { GB2312_CHARSET, FW_NORMAL, -12, 15, 96, "?SimHei" },
6434 { GB2312_CHARSET, FW_NORMAL, -15, 18, 120, "?SimHei" },
6435 { CHINESEBIG5_CHARSET, FW_NORMAL, -12, 15, 96, "?MingLiU" },
6436 { CHINESEBIG5_CHARSET, FW_NORMAL, -15, 18, 120, "?MingLiU" },
6437 { DEFAULT_CHARSET, FW_NORMAL, -11, 13, 96, "MS Shell Dlg" },
6438 { DEFAULT_CHARSET, FW_NORMAL, -13, 16, 120, "MS Shell Dlg" },
6439 { 0 }
6442 int i, j;
6444 for (i = 0; i < ARRAY_SIZE(font); i++)
6446 HFONT hfont;
6447 LOGFONTA lf;
6448 int ret, height;
6450 hfont = GetStockObject(font[i]);
6451 ok(hfont != 0, "%d: GetStockObject(%d) failed\n", i, font[i]);
6453 ret = GetObjectA(hfont, sizeof(lf), &lf);
6454 if (ret != sizeof(lf))
6456 /* NT4 */
6457 win_skip("%d: GetObject returned %d instead of sizeof(LOGFONT)\n", i, ret);
6458 continue;
6461 for (j = 0; td[i][j].face_name[0] != 0; j++)
6463 if ((lf.lfCharSet != td[i][j].charset && td[i][j].charset != DEFAULT_CHARSET) ||
6464 (system_lang_id != td[i][j].lang_id && td[i][j].lang_id != LANG_NEUTRAL) ||
6465 (td[i][j].face_name[0] != '?' && strcmp(lf.lfFaceName, td[i][j].face_name)))
6467 continue;
6470 ret = get_font_dpi(&lf, &height);
6471 if (ret != td[i][j].dpi)
6473 trace("%d(%d): font %s %d dpi doesn't match test data %d\n",
6474 i, j, lf.lfFaceName, ret, td[i][j].dpi);
6475 continue;
6478 /* FIXME: Remove once Wine is fixed */
6479 todo_wine_if (td[i][j].dpi != 96 &&
6480 /* MS Sans Serif for 120 dpi and higher should include 12 pixel bitmap set */
6481 ((!strcmp(td[i][j].face_name, "MS Sans Serif") && td[i][j].height == 12) ||
6482 /* System for 120 dpi and higher should include 20 pixel bitmap set */
6483 (!strcmp(td[i][j].face_name, "System") && td[i][j].height > 16)))
6484 ok(height == td[i][j].height_pixels, "%d(%d): expected height %d, got %d\n", i, j, td[i][j].height_pixels, height);
6486 ok(td[i][j].weight == lf.lfWeight, "%d(%d): expected lfWeight %d, got %d\n", i, j, td[i][j].weight, lf.lfWeight);
6487 ok(td[i][j].height == lf.lfHeight, "%d(%d): expected lfHeight %d, got %d\n", i, j, td[i][j].height, lf.lfHeight);
6488 if (td[i][j].face_name[0] == '?')
6490 /* Wine doesn't have this font, skip this case for now.
6491 Actually, the face name is localized on Windows and varies
6492 dpending on Windows versions (e.g. Japanese NT4 vs win2k). */
6493 trace("%d(%d): default gui font is %s\n", i, j, lf.lfFaceName);
6495 else
6497 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);
6499 break;
6504 static void test_max_height(void)
6506 HDC hdc;
6507 LOGFONTA lf;
6508 HFONT hfont, hfont_old;
6509 TEXTMETRICA tm1, tm;
6510 BOOL r;
6511 LONG invalid_height[] = { -65536, -123456, 123456 };
6512 size_t i;
6514 memset(&tm1, 0, sizeof(tm1));
6515 memset(&lf, 0, sizeof(lf));
6516 strcpy(lf.lfFaceName, "Tahoma");
6517 lf.lfHeight = -1;
6519 hdc = GetDC(NULL);
6521 /* get 1 ppem value */
6522 hfont = CreateFontIndirectA(&lf);
6523 hfont_old = SelectObject(hdc, hfont);
6524 r = GetTextMetricsA(hdc, &tm1);
6525 ok(r, "GetTextMetrics failed\n");
6526 ok(tm1.tmHeight > 0, "expected a positive value, got %d\n", tm1.tmHeight);
6527 ok(tm1.tmAveCharWidth > 0, "expected a positive value, got %d\n", tm1.tmHeight);
6528 DeleteObject(SelectObject(hdc, hfont_old));
6530 /* test the largest value */
6531 lf.lfHeight = -((1 << 16) - 1);
6532 hfont = CreateFontIndirectA(&lf);
6533 hfont_old = SelectObject(hdc, hfont);
6534 memset(&tm, 0, sizeof(tm));
6535 r = GetTextMetricsA(hdc, &tm);
6536 ok(r, "GetTextMetrics failed\n");
6537 ok(tm.tmHeight > tm1.tmHeight,
6538 "expected greater than 1 ppem value (%d), got %d\n", tm1.tmHeight, tm.tmHeight);
6539 ok(tm.tmAveCharWidth > tm1.tmAveCharWidth,
6540 "expected greater than 1 ppem value (%d), got %d\n", tm1.tmAveCharWidth, tm.tmAveCharWidth);
6541 DeleteObject(SelectObject(hdc, hfont_old));
6543 /* test an invalid value */
6544 for (i = 0; i < ARRAY_SIZE(invalid_height); i++) {
6545 lf.lfHeight = invalid_height[i];
6546 hfont = CreateFontIndirectA(&lf);
6547 hfont_old = SelectObject(hdc, hfont);
6548 memset(&tm, 0, sizeof(tm));
6549 r = GetTextMetricsA(hdc, &tm);
6550 ok(r, "GetTextMetrics failed\n");
6551 ok(tm.tmHeight == tm1.tmHeight,
6552 "expected 1 ppem value (%d), got %d\n", tm1.tmHeight, tm.tmHeight);
6553 ok(tm.tmAveCharWidth == tm1.tmAveCharWidth,
6554 "expected 1 ppem value (%d), got %d\n", tm1.tmAveCharWidth, tm.tmAveCharWidth);
6555 DeleteObject(SelectObject(hdc, hfont_old));
6558 ReleaseDC(NULL, hdc);
6559 return;
6562 static void test_vertical_order(void)
6564 struct enum_font_data efd;
6565 LOGFONTA lf;
6566 HDC hdc;
6567 int i, j;
6569 hdc = CreateCompatibleDC(0);
6570 ok(hdc != NULL, "CreateCompatibleDC failed\n");
6572 memset(&lf, 0, sizeof(lf));
6573 lf.lfCharSet = DEFAULT_CHARSET;
6574 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
6575 lf.lfHeight = 16;
6576 lf.lfWidth = 16;
6577 lf.lfQuality = DEFAULT_QUALITY;
6578 lf.lfItalic = FALSE;
6579 lf.lfWeight = FW_DONTCARE;
6580 memset( &efd, 0, sizeof(efd) );
6581 EnumFontFamiliesExA(hdc, &lf, enum_font_data_proc, (LPARAM)&efd, 0);
6582 for (i = 0; i < efd.total; i++)
6584 if (efd.lf[i].lfFaceName[0] != '@') continue;
6585 for (j = 0; j < efd.total; j++)
6587 if (!strcmp(efd.lf[i].lfFaceName + 1, efd.lf[j].lfFaceName))
6589 ok(i > j,"Found vertical font %s before its horizontal version\n", efd.lf[i].lfFaceName);
6590 break;
6594 heap_free( efd.lf );
6595 DeleteDC( hdc );
6598 static void test_GetCharWidth32(void)
6600 BOOL ret;
6601 HDC hdc;
6602 LOGFONTA lf;
6603 HFONT hfont;
6604 INT bufferA;
6605 INT bufferW;
6606 HWND hwnd;
6608 if (!pGetCharWidth32W)
6610 win_skip("GetCharWidth32W not available on this platform\n");
6611 return;
6614 memset(&lf, 0, sizeof(lf));
6615 strcpy(lf.lfFaceName, "System");
6616 lf.lfHeight = 20;
6618 hfont = CreateFontIndirectA(&lf);
6619 hdc = GetDC(0);
6620 hfont = SelectObject(hdc, hfont);
6622 ret = pGetCharWidth32W(hdc, 'a', 'a', &bufferW);
6623 ok(ret, "GetCharWidth32W should have succeeded\n");
6624 ret = GetCharWidth32A(hdc, 'a', 'a', &bufferA);
6625 ok(ret, "GetCharWidth32A should have succeeded\n");
6626 ok (bufferA == bufferW, "Widths should be the same\n");
6627 ok (bufferA > 0," Width should be greater than zero\n");
6629 hfont = SelectObject(hdc, hfont);
6630 DeleteObject(hfont);
6631 ReleaseDC(NULL, hdc);
6633 memset(&lf, 0, sizeof(lf));
6634 strcpy(lf.lfFaceName, "Tahoma");
6635 lf.lfHeight = 20;
6637 hfont = CreateFontIndirectA(&lf);
6638 hwnd = CreateWindowExA(0, "static", "", WS_POPUP, 0,0,100,100,
6639 0, 0, 0, NULL);
6640 hdc = GetDC(hwnd);
6641 SetMapMode( hdc, MM_ANISOTROPIC );
6642 SelectObject(hdc, hfont);
6644 ret = pGetCharWidth32W(hdc, 'a', 'a', &bufferW);
6645 ok(ret, "GetCharWidth32W should have succeeded\n");
6646 ok (bufferW > 0," Width should be greater than zero\n");
6647 SetWindowExtEx(hdc, -1,-1,NULL);
6648 SetGraphicsMode(hdc, GM_COMPATIBLE);
6649 ret = pGetCharWidth32W(hdc, 'a', 'a', &bufferW);
6650 ok(ret, "GetCharWidth32W should have succeeded\n");
6651 ok (bufferW > 0," Width should be greater than zero\n");
6652 SetGraphicsMode(hdc, GM_ADVANCED);
6653 ret = pGetCharWidth32W(hdc, 'a', 'a', &bufferW);
6654 ok(ret, "GetCharWidth32W should have succeeded\n");
6655 ok (bufferW > 0," Width should be greater than zero\n");
6656 SetWindowExtEx(hdc, 1,1,NULL);
6657 SetGraphicsMode(hdc, GM_COMPATIBLE);
6658 ret = pGetCharWidth32W(hdc, 'a', 'a', &bufferW);
6659 ok(ret, "GetCharWidth32W should have succeeded\n");
6660 ok (bufferW > 0," Width should be greater than zero\n");
6661 SetGraphicsMode(hdc, GM_ADVANCED);
6662 ret = pGetCharWidth32W(hdc, 'a', 'a', &bufferW);
6663 ok(ret, "GetCharWidth32W should have succeeded\n");
6664 ok (bufferW > 0," Width should be greater than zero\n");
6666 ReleaseDC(hwnd, hdc);
6667 DestroyWindow(hwnd);
6669 hwnd = CreateWindowExA(WS_EX_LAYOUTRTL, "static", "", WS_POPUP, 0,0,100,100,
6670 0, 0, 0, NULL);
6671 hdc = GetDC(hwnd);
6672 SetMapMode( hdc, MM_ANISOTROPIC );
6673 SelectObject(hdc, hfont);
6675 ret = pGetCharWidth32W(hdc, 'a', 'a', &bufferW);
6676 ok(ret, "GetCharWidth32W should have succeeded\n");
6677 ok (bufferW > 0," Width should be greater than zero\n");
6678 SetWindowExtEx(hdc, -1,-1,NULL);
6679 SetGraphicsMode(hdc, GM_COMPATIBLE);
6680 ret = pGetCharWidth32W(hdc, 'a', 'a', &bufferW);
6681 ok(ret, "GetCharWidth32W should have succeeded\n");
6682 ok (bufferW > 0," Width should be greater than zero\n");
6683 SetGraphicsMode(hdc, GM_ADVANCED);
6684 ret = pGetCharWidth32W(hdc, 'a', 'a', &bufferW);
6685 ok(ret, "GetCharWidth32W should have succeeded\n");
6686 ok (bufferW > 0," Width should be greater than zero\n");
6687 SetWindowExtEx(hdc, 1,1,NULL);
6688 SetGraphicsMode(hdc, GM_COMPATIBLE);
6689 ret = pGetCharWidth32W(hdc, 'a', 'a', &bufferW);
6690 ok(ret, "GetCharWidth32W should have succeeded\n");
6691 ok (bufferW > 0," Width should be greater than zero\n");
6692 SetGraphicsMode(hdc, GM_ADVANCED);
6693 ret = pGetCharWidth32W(hdc, 'a', 'a', &bufferW);
6694 ok(ret, "GetCharWidth32W should have succeeded\n");
6695 ok (bufferW > 0," Width should be greater than zero\n");
6697 ReleaseDC(hwnd, hdc);
6698 DestroyWindow(hwnd);
6699 DeleteObject(hfont);
6702 static void test_fake_bold_font(void)
6704 static const MAT2 x2_mat = { {0,2}, {0,0}, {0,0}, {0,2} };
6705 HDC hdc;
6706 LOGFONTA lf;
6707 BOOL ret;
6708 struct glyph_data {
6709 TEXTMETRICA tm;
6710 ABC abc;
6711 INT w;
6712 GLYPHMETRICS gm;
6713 } data[4];
6714 int i;
6715 DWORD r;
6717 /* Test outline font */
6718 memset(&lf, 0, sizeof(lf));
6719 strcpy(lf.lfFaceName, "Wingdings");
6720 lf.lfCharSet = SYMBOL_CHARSET;
6722 hdc = GetDC(NULL);
6724 for (i = 0; i <= 1; i++)
6726 HFONT hfont, hfont_old;
6728 lf.lfWeight = i ? FW_BOLD : FW_NORMAL;
6729 hfont = CreateFontIndirectA(&lf);
6730 hfont_old = SelectObject(hdc, hfont);
6732 ret = GetTextMetricsA(hdc, &data[i].tm);
6733 ok(ret, "got %d\n", ret);
6734 ret = GetCharABCWidthsA(hdc, 0x76, 0x76, &data[i].abc);
6735 ok(ret, "got %d\n", ret);
6736 data[i].w = data[i].abc.abcA + data[i].abc.abcB + data[i].abc.abcC;
6737 r = GetGlyphOutlineA(hdc, 0x76, GGO_METRICS, &data[i].gm, 0, NULL, &x2_mat);
6738 ok(r != GDI_ERROR, "got %d\n", ret);
6740 SelectObject(hdc, hfont_old);
6741 DeleteObject(hfont);
6743 ReleaseDC(NULL, hdc);
6745 /* compare results (outline) */
6746 ok(data[0].tm.tmHeight == data[1].tm.tmHeight,
6747 "expected %d, got %d\n", data[0].tm.tmHeight, data[1].tm.tmHeight);
6748 ok(data[0].tm.tmAscent == data[1].tm.tmAscent,
6749 "expected %d, got %d\n", data[0].tm.tmAscent, data[1].tm.tmAscent);
6750 ok(data[0].tm.tmDescent == data[1].tm.tmDescent,
6751 "expected %d, got %d\n", data[0].tm.tmDescent, data[1].tm.tmDescent);
6752 ok(data[0].tm.tmAveCharWidth + 1 == data[1].tm.tmAveCharWidth,
6753 "expected %d, got %d\n", data[0].tm.tmAveCharWidth + 1, data[1].tm.tmAveCharWidth);
6754 ok(data[0].tm.tmMaxCharWidth + 1 == data[1].tm.tmMaxCharWidth,
6755 "expected %d, got %d\n", data[0].tm.tmMaxCharWidth + 1, data[1].tm.tmMaxCharWidth);
6756 ok(data[0].tm.tmOverhang == data[1].tm.tmOverhang,
6757 "expected %d, got %d\n", data[0].tm.tmOverhang, data[1].tm.tmOverhang);
6758 ok(data[0].w + 1 == data[1].w,
6759 "expected %d, got %d\n", data[0].w + 1, data[1].w);
6761 ok(data[0].gm.gmCellIncX + 1 == data[1].gm.gmCellIncX,
6762 "expected %d, got %d\n", data[0].gm.gmCellIncX + 1, data[1].gm.gmCellIncX);
6763 ok(data[0].gm.gmCellIncY == data[1].gm.gmCellIncY,
6764 "expected %d, got %d\n", data[0].gm.gmCellIncY, data[1].gm.gmCellIncY);
6766 /* Test bitmap font */
6767 memset(&data, 0xaa, sizeof(data));
6768 memset(&lf, 0, sizeof(lf));
6769 strcpy(lf.lfFaceName, "Courier");
6770 lf.lfCharSet = ANSI_CHARSET;
6772 hdc = GetDC(NULL);
6774 for (i = 0; i < 4; i++)
6776 HFONT hfont, hfont_old;
6778 lf.lfWeight = (i % 2) ? FW_BOLD : FW_NORMAL;
6779 lf.lfHeight = (i > 1) ? data[0].tm.tmHeight * x2_mat.eM11.value : 0;
6780 hfont = CreateFontIndirectA(&lf);
6781 hfont_old = SelectObject(hdc, hfont);
6783 ret = GetTextMetricsA(hdc, &data[i].tm);
6784 ok(ret, "got %d\n", ret);
6785 ret = GetCharWidth32A(hdc, 0x76, 0x76, &data[i].w);
6786 ok(ret, "got %d\n", ret);
6788 SelectObject(hdc, hfont_old);
6789 DeleteObject(hfont);
6791 ReleaseDC(NULL, hdc);
6793 /* compare results (bitmap) */
6794 for (i = 0; i < 4; i+=2)
6796 int diff = (i > 1) ? x2_mat.eM11.value : 1;
6797 if (data[i].tm.tmPitchAndFamily & TMPF_TRUETYPE)
6799 skip("TrueType font is selected (expected a bitmap one)\n");
6800 continue;
6802 ok(data[i].tm.tmHeight == data[i+1].tm.tmHeight,
6803 "expected %d, got %d\n", data[i].tm.tmHeight, data[i+1].tm.tmHeight);
6804 ok(data[i].tm.tmAscent == data[i+1].tm.tmAscent,
6805 "expected %d, got %d\n", data[i].tm.tmAscent, data[i+1].tm.tmAscent);
6806 ok(data[i].tm.tmDescent == data[i+1].tm.tmDescent,
6807 "expected %d, got %d\n", data[i].tm.tmDescent, data[i+1].tm.tmDescent);
6808 ok(data[i+1].tm.tmAveCharWidth - data[i].tm.tmAveCharWidth == diff,
6809 "expected %d, got %d\n", diff, data[i+1].tm.tmAveCharWidth - data[i].tm.tmAveCharWidth);
6810 ok(data[i+1].tm.tmMaxCharWidth - data[i].tm.tmMaxCharWidth == diff,
6811 "expected %d, got %d\n", diff, data[i+1].tm.tmMaxCharWidth - data[i].tm.tmMaxCharWidth);
6812 ok(data[i].tm.tmOverhang == 0,
6813 "expected 0, got %d\n", data[i].tm.tmOverhang);
6814 ok(data[i+1].tm.tmOverhang == 1,
6815 "expected 1, got %d\n", data[i+1].tm.tmOverhang);
6816 ok(data[i].w + 1 == data[i+1].w,
6817 "expected %d, got %d\n", data[i].w + 1, data[i+1].w);
6821 static void test_bitmap_font_glyph_index(void)
6823 const WCHAR text[] = L"#!/bin/sh";
6824 const struct {
6825 LPCSTR face;
6826 BYTE charset;
6827 } bitmap_font_list[] = {
6828 { "Courier", ANSI_CHARSET },
6829 { "Small Fonts", ANSI_CHARSET },
6830 { "Fixedsys", DEFAULT_CHARSET },
6831 { "System", DEFAULT_CHARSET }
6833 HDC hdc;
6834 LOGFONTA lf;
6835 HFONT hFont;
6836 CHAR facename[LF_FACESIZE];
6837 BITMAPINFO bmi;
6838 HBITMAP hBmp[2];
6839 void *pixels[2];
6840 int i, j;
6841 DWORD ret;
6842 BITMAP bmp;
6843 TEXTMETRICA tm;
6844 CHARSETINFO ci;
6845 BYTE chr = '\xA9';
6847 if (!pGetGlyphIndicesW) {
6848 win_skip("GetGlyphIndices is unavailable\n");
6849 return;
6852 hdc = CreateCompatibleDC(0);
6853 ok(hdc != NULL, "CreateCompatibleDC failed\n");
6855 memset(&bmi, 0, sizeof(bmi));
6856 bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
6857 bmi.bmiHeader.biBitCount = 32;
6858 bmi.bmiHeader.biPlanes = 1;
6859 bmi.bmiHeader.biWidth = 128;
6860 bmi.bmiHeader.biHeight = 32;
6861 bmi.bmiHeader.biCompression = BI_RGB;
6863 for (i = 0; i < ARRAY_SIZE(bitmap_font_list); i++) {
6864 memset(&lf, 0, sizeof(lf));
6865 lf.lfCharSet = bitmap_font_list[i].charset;
6866 strcpy(lf.lfFaceName, bitmap_font_list[i].face);
6867 hFont = CreateFontIndirectA(&lf);
6868 ok(hFont != NULL, "Can't create font (%s:%d)\n", lf.lfFaceName, lf.lfCharSet);
6869 hFont = SelectObject(hdc, hFont);
6870 ret = GetTextMetricsA(hdc, &tm);
6871 ok(ret, "GetTextMetric failed\n");
6872 ret = GetTextFaceA(hdc, sizeof(facename), facename);
6873 ok(ret, "GetTextFace failed\n");
6874 if (tm.tmPitchAndFamily & TMPF_TRUETYPE) {
6875 skip("TrueType font (%s) was selected for \"%s\"\n", facename, bitmap_font_list[i].face);
6876 continue;
6878 if (lstrcmpiA(facename, lf.lfFaceName) != 0) {
6879 skip("expected %s, got %s\n", lf.lfFaceName, facename);
6880 continue;
6883 for (j = 0; j < 2; j++) {
6884 HBITMAP hBmpPrev;
6885 hBmp[j] = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, &pixels[j], NULL, 0);
6886 ok(hBmp[j] != NULL, "Can't create DIB\n");
6887 hBmpPrev = SelectObject(hdc, hBmp[j]);
6888 switch (j) {
6889 case 0:
6890 ret = ExtTextOutW(hdc, 0, 0, 0, NULL, text, lstrlenW(text), NULL);
6891 break;
6892 case 1:
6894 int len = lstrlenW(text);
6895 LPWORD indices = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WORD));
6896 ret = pGetGlyphIndicesW(hdc, text, len, indices, 0);
6897 ok(ret, "GetGlyphIndices failed\n");
6898 ok(memcmp(indices, text, sizeof(WORD) * len) == 0,
6899 "Glyph indices and text are different for %s:%d\n", lf.lfFaceName, tm.tmCharSet);
6900 ret = ExtTextOutW(hdc, 0, 0, ETO_GLYPH_INDEX, NULL, indices, len, NULL);
6901 HeapFree(GetProcessHeap(), 0, indices);
6902 break;
6905 ok(ret, "ExtTextOutW failed\n");
6906 SelectObject(hdc, hBmpPrev);
6909 GetObjectA(hBmp[0], sizeof(bmp), &bmp);
6910 ok(memcmp(pixels[0], pixels[1], bmp.bmHeight * bmp.bmWidthBytes) == 0,
6911 "Images are different (%s:%d)\n", lf.lfFaceName, tm.tmCharSet);
6913 ret = TranslateCharsetInfo((LPDWORD)(DWORD_PTR)tm.tmCharSet, &ci, TCI_SRCCHARSET);
6914 if (!ret) {
6915 skip("Can't get charset info for (%s:%d)\n", lf.lfFaceName, tm.tmCharSet);
6916 goto next;
6918 if (IsDBCSLeadByteEx(ci.ciACP, chr)) {
6919 skip("High-ascii character is not defined in codepage %d\n", ci.ciACP);
6920 goto next;
6923 for (j = 0; j < 2; j++) {
6924 HBITMAP hBmpPrev;
6925 WORD code;
6926 hBmpPrev = SelectObject(hdc, hBmp[j]);
6927 switch (j) {
6928 case 0:
6929 ret = ExtTextOutA(hdc, 100, 0, 0, NULL, (LPCSTR)&chr, 1, NULL);
6930 break;
6931 case 1:
6932 ret = GetGlyphIndicesA(hdc, (LPCSTR)&chr, 1, &code, 0);
6933 ok(ret, "GetGlyphIndices failed\n");
6934 ok(code == chr, "expected %02x, got %02x (%s:%d)\n", chr, code, lf.lfFaceName, tm.tmCharSet);
6935 ret = ExtTextOutA(hdc, 100, 0, ETO_GLYPH_INDEX, NULL, (LPCSTR)&code, 1, NULL);
6936 break;
6938 ok(ret, "ExtTextOutA failed\n");
6939 SelectObject(hdc, hBmpPrev);
6942 ok(memcmp(pixels[0], pixels[1], bmp.bmHeight * bmp.bmWidthBytes) == 0,
6943 "Images are different (%s:%d)\n", lf.lfFaceName, tm.tmCharSet);
6944 next:
6945 for (j = 0; j < 2; j++)
6946 DeleteObject(hBmp[j]);
6947 hFont = SelectObject(hdc, hFont);
6948 DeleteObject(hFont);
6951 DeleteDC(hdc);
6954 static void test_GetCharWidthI(void)
6956 static const char *teststr = "wine ";
6957 HFONT hfont, prev_hfont;
6958 WORD glyphs[5];
6959 INT widths[5];
6960 LOGFONTA lf;
6961 ABC abc[5];
6962 int len, i;
6963 DWORD nb;
6964 BOOL ret;
6965 HDC hdc;
6967 memset(&lf, 0, sizeof(lf));
6968 strcpy(lf.lfFaceName, "Tahoma");
6969 lf.lfHeight = -20;
6971 hdc = GetDC(0);
6973 hfont = CreateFontIndirectA(&lf);
6974 prev_hfont = SelectObject(hdc, hfont);
6976 len = strlen(teststr);
6977 nb = GetGlyphIndicesA(hdc, teststr, len, glyphs, 0);
6978 ok(nb == len, "\n");
6980 memset(abc, 0xcc, sizeof(abc));
6981 ret = GetCharABCWidthsI(hdc, 0, len, glyphs, abc);
6982 ok(ret, "GetCharABCWidthsI failed\n");
6984 memset(widths, 0xcc, sizeof(widths));
6985 ret = GetCharWidthI(hdc, 0, len, glyphs, widths);
6986 ok(ret, "GetCharWidthI failed\n");
6988 for (i = 0; i < len; i++)
6989 ok(widths[i] == abc[i].abcA + abc[i].abcB + abc[i].abcC, "%u, glyph %u, got width %d\n",
6990 i, glyphs[i], widths[i]);
6992 DeleteObject(SelectObject(hdc, prev_hfont));
6993 ReleaseDC(0, hdc);
6996 static INT CALLBACK long_enum_proc(const LOGFONTA *lf, const TEXTMETRICA *tm, DWORD type, LPARAM lparam)
6998 BOOL *found_font = (BOOL *)lparam;
6999 *found_font = TRUE;
7000 return 1;
7003 static void test_long_names(void)
7005 char ttf_name[MAX_PATH];
7006 LOGFONTA font = {0};
7007 HFONT handle_font;
7008 BOOL found_font;
7009 int ret;
7010 HDC dc;
7012 if (!write_ttf_file("wine_longname.ttf", ttf_name))
7014 skip("Failed to create ttf file for testing\n");
7015 return;
7018 dc = GetDC(NULL);
7020 ret = AddFontResourceExA(ttf_name, FR_PRIVATE, 0);
7021 ok(ret, "AddFontResourceEx() failed\n");
7023 strcpy(font.lfFaceName, "wine_3_this_is_a_very_long_name");
7024 found_font = FALSE;
7025 EnumFontFamiliesExA(dc, &font, long_enum_proc, (LPARAM)&found_font, 0);
7026 ok(found_font == TRUE, "EnumFontFamiliesExA didn't find font.\n");
7028 strcpy(font.lfFaceName, "wine_2_this_is_a_very_long_name");
7029 found_font = FALSE;
7030 EnumFontFamiliesExA(dc, &font, long_enum_proc, (LPARAM)&found_font, 0);
7031 ok(found_font == TRUE, "EnumFontFamiliesExA didn't find font.\n");
7033 strcpy(font.lfFaceName, "wine_1_this_is_a_very_long_name");
7034 found_font = FALSE;
7035 EnumFontFamiliesExA(dc, &font, long_enum_proc, (LPARAM)&found_font, 0);
7036 ok(found_font == FALSE, "EnumFontFamiliesExA must not find font.\n");
7038 handle_font = CreateFontIndirectA(&font);
7039 ok(handle_font != NULL, "CreateFontIndirectA failed\n");
7040 DeleteObject(handle_font);
7042 ret = RemoveFontResourceExA(ttf_name, FR_PRIVATE, 0);
7043 ok(ret, "RemoveFontResourceEx() failed\n");
7045 DeleteFileA(ttf_name);
7046 ReleaseDC(NULL, dc);
7049 static void test_ttf_names(void)
7051 struct enum_fullname_data efnd;
7052 char ttf_name[MAX_PATH], ttf_name_bold[MAX_PATH];
7053 LOGFONTA font = {0};
7054 HFONT handle_font;
7055 int ret;
7056 HDC dc;
7058 if (!write_ttf_file("wine_ttfnames.ttf", ttf_name))
7060 skip("Failed to create ttf file for testing\n");
7061 return;
7064 if (!write_ttf_file("wine_ttfnames_bold.ttf", ttf_name_bold))
7066 skip("Failed to create ttf file for testing\n");
7067 DeleteFileA(ttf_name);
7068 return;
7071 ret = AddFontResourceExA(ttf_name, FR_PRIVATE, 0);
7072 ok(ret, "AddFontResourceEx() failed\n");
7074 ret = AddFontResourceExA(ttf_name_bold, FR_PRIVATE, 0);
7075 ok(ret, "AddFontResourceEx() failed\n");
7077 dc = GetDC(NULL);
7079 strcpy(font.lfFaceName, "Wine_TTF_Names_Long_Family1_Con");
7080 memset(&efnd, 0, sizeof(efnd));
7081 EnumFontFamiliesExA(dc, &font, enum_fullname_data_proc, (LPARAM)&efnd, 0);
7082 ok(efnd.total == 0, "EnumFontFamiliesExA must not find font.\n");
7084 /* Windows doesn't match with Typographic/Preferred Family tags */
7085 strcpy(font.lfFaceName, "Wine TTF Names Long Family1");
7086 memset(&efnd, 0, sizeof(efnd));
7087 EnumFontFamiliesExA(dc, &font, enum_fullname_data_proc, (LPARAM)&efnd, 0);
7088 ok(efnd.total == 0, "EnumFontFamiliesExA must not find font.\n");
7090 strcpy(font.lfFaceName, "Wine TTF Names Long Family1 Ext");
7091 memset(&efnd, 0, sizeof(efnd));
7092 EnumFontFamiliesExA(dc, &font, enum_fullname_data_proc, (LPARAM)&efnd, 0);
7093 ok(efnd.total == 2, "EnumFontFamiliesExA found %d fonts, expected 2.\n", efnd.total);
7095 strcpy(font.lfFaceName, "Wine TTF Names Long Family1 Con");
7096 memset(&efnd, 0, sizeof(efnd));
7097 EnumFontFamiliesExA(dc, &font, enum_fullname_data_proc, (LPARAM)&efnd, 0);
7098 ok(efnd.total == 2, "EnumFontFamiliesExA found %d fonts, expected 2.\n", efnd.total);
7100 handle_font = CreateFontIndirectA(&font);
7101 ok(handle_font != NULL, "CreateFontIndirectA failed\n");
7102 DeleteObject(handle_font);
7104 ret = RemoveFontResourceExA(ttf_name_bold, FR_PRIVATE, 0);
7105 ok(ret, "RemoveFontResourceEx() failed\n");
7107 DeleteFileA(ttf_name_bold);
7109 ret = RemoveFontResourceExA(ttf_name, FR_PRIVATE, 0);
7110 ok(ret, "RemoveFontResourceEx() failed\n");
7112 DeleteFileA(ttf_name);
7113 ReleaseDC(NULL, dc);
7116 static void test_lang_names(void)
7118 static const WCHAR name_cond_ja_w[] = L"\x30d5\x30a9\x30f3\x30c8\x540d Cond (ja)";
7119 static const WCHAR name_cond_ja_reg_w[] = L"\x30d5\x30a9\x30f3\x30c8\x540d Cond (ja) Reg";
7120 static const WCHAR name_cond_ja_reg_ja_w[] = L"\x30d5\x30a9\x30f3\x30c8\x540d Cond (ja) Reg (ja)";
7121 static const WCHAR name_wws_ja_w[] = L"\x30d5\x30a9\x30f3\x30c8\x540d WWS (ja)";
7123 struct enum_fullname_data efnd;
7124 struct enum_fullname_data_w efnd_w;
7125 char ttf_name[MAX_PATH], ttf_name2[MAX_PATH], ttf_name3[MAX_PATH];
7126 LOGFONTA font = {0};
7127 LOGFONTW font_w = {0};
7128 int ret, i;
7129 HDC dc;
7130 const WCHAR *primary_family, *primary_fullname;
7132 if (PRIMARYLANGID(GetSystemDefaultLangID()) != LANG_ENGLISH && PRIMARYLANGID(GetSystemDefaultLangID()) != LANG_JAPANESE)
7134 skip( "Primary language is neither English nor Japanese, skipping test\n" );
7135 return;
7138 if (!write_ttf_file( "wine_langnames.ttf", ttf_name ))
7140 skip( "Failed to create ttf file for testing\n" );
7141 return;
7144 if (!write_ttf_file( "wine_langnames2.ttf", ttf_name2 ))
7146 skip( "Failed to create ttf file for testing\n" );
7147 DeleteFileA( ttf_name );
7148 return;
7151 if (!write_ttf_file( "wine_langnames3.ttf", ttf_name3 ))
7153 skip( "Failed to create ttf file for testing\n" );
7154 DeleteFileA( ttf_name2 );
7155 DeleteFileA( ttf_name );
7156 return;
7159 ret = AddFontResourceExA( ttf_name, FR_PRIVATE, 0 );
7160 ok( ret, "AddFontResourceEx() failed\n" );
7162 dc = GetDC( NULL );
7164 if (PRIMARYLANGID(GetSystemDefaultLangID()) == LANG_ENGLISH)
7166 primary_family = L"Wine Lang Cond (en)";
7167 primary_fullname = L"Wine Lang Cond Reg (en)";
7169 else
7171 primary_family = name_cond_ja_w;
7172 primary_fullname = name_cond_ja_reg_w;
7175 for (i = 0; i < 3; ++i)
7177 /* check that lookup by preferred or WWS family / full names or postscript FontName doesn't work */
7179 strcpy( font.lfFaceName, "Wine Lang (en)" );
7180 memset( &efnd, 0, sizeof(efnd) );
7181 EnumFontFamiliesExA( dc, &font, enum_fullname_data_proc, (LPARAM)&efnd, 0 );
7182 ok( efnd.total == 0, "%d: EnumFontFamiliesExA unexpected count %u.\n", i, efnd.total );
7184 strcpy( font.lfFaceName, "Wine Lang Condensed Bold (ko)" );
7185 memset( &efnd, 0, sizeof(efnd) );
7186 EnumFontFamiliesExA( dc, &font, enum_fullname_data_proc, (LPARAM)&efnd, 0 );
7187 ok( efnd.total == 0, "%d: EnumFontFamiliesExA unexpected count %u.\n", i, efnd.total );
7189 wcscpy( font_w.lfFaceName, name_wws_ja_w );
7190 memset( &efnd_w, 0, sizeof(efnd_w) );
7191 EnumFontFamiliesExW( dc, &font_w, enum_fullname_data_proc_w, (LPARAM)&efnd_w, 0 );
7192 ok( efnd_w.total == 0, "%d: EnumFontFamiliesExA unexpected count %u.\n", i, efnd_w.total );
7194 strcpy( font.lfFaceName, "Reg WWS (zh-tw)" );
7195 memset( &efnd, 0, sizeof(efnd) );
7196 EnumFontFamiliesExA( dc, &font, enum_fullname_data_proc, (LPARAM)&efnd, 0 );
7197 ok( efnd.total == 0, "%d: EnumFontFamiliesExA unexpected count %u.\n", i, efnd.total );
7199 strcpy( font.lfFaceName, "Wine Lang (en) Reg WWS (en)" );
7200 memset( &efnd, 0, sizeof(efnd) );
7201 EnumFontFamiliesExA( dc, &font, enum_fullname_data_proc, (LPARAM)&efnd, 0 );
7202 ok( efnd.total == 0, "%d: EnumFontFamiliesExA unexpected count %u.\n", i, efnd.total );
7204 strcpy( font.lfFaceName, "WineLangNamesRegular" );
7205 memset( &efnd, 0, sizeof(efnd) );
7206 EnumFontFamiliesExA( dc, &font, enum_fullname_data_proc, (LPARAM)&efnd, 0 );
7207 ok( efnd.total == 0, "%d: EnumFontFamiliesExA unexpected count %u.\n", i, efnd.total );
7209 /* then, the primary ttf family name always works */
7211 wcscpy( font_w.lfFaceName, primary_family );
7212 memset( &efnd_w, 0, sizeof(efnd_w) );
7213 EnumFontFamiliesExW( dc, &font_w, enum_fullname_data_proc_w, (LPARAM)&efnd_w, 0 );
7214 ok( efnd_w.total == min( 2, i + 1 ), "%d: EnumFontFamiliesExA unexpected count %u.\n", i, efnd_w.total );
7216 if (PRIMARYLANGID(GetSystemDefaultLangID()) == LANG_ENGLISH)
7218 wcscpy( font_w.lfFaceName, name_cond_ja_w );
7219 memset( &efnd_w, 0, sizeof(efnd_w) );
7220 EnumFontFamiliesExW( dc, &font_w, enum_fullname_data_proc_w, (LPARAM)&efnd_w, 0 );
7221 ok( efnd_w.total == 0, "%d: EnumFontFamiliesExA unexpected count %u.\n", i, efnd_w.total );
7224 /* if there is no primary ttf family name, the english ttf name, or postscript FamilyName are used instead */
7226 strcpy( font.lfFaceName, "Wine_Lang_Names" );
7227 memset( &efnd, 0, sizeof(efnd) );
7228 EnumFontFamiliesExA( dc, &font, enum_fullname_data_proc, (LPARAM)&efnd, 0 );
7229 if (i == 2)
7230 ok( efnd.total == 1, "%d: EnumFontFamiliesExA unexpected count %u.\n", i, efnd.total );
7231 else
7232 ok( efnd.total == 0, "%d: EnumFontFamiliesExA unexpected count %u.\n", i, efnd.total );
7234 /* same goes for ttf full names */
7236 wcscpy( font_w.lfFaceName, primary_fullname );
7237 memset( &efnd_w, 0, sizeof(efnd_w) );
7238 EnumFontFamiliesExW( dc, &font_w, enum_fullname_data_proc_w, (LPARAM)&efnd_w, 0 );
7239 ok( efnd_w.total == 1, "%d: EnumFontFamiliesExW unexpected count %u.\n", i, efnd_w.total );
7241 if (efnd_w.total >= 1)
7243 ok( !wcscmp( (WCHAR *)efnd_w.elf[0].elfLogFont.lfFaceName, primary_family ),
7244 "%d: (%d) unexpected lfFaceName %s\n", i, efnd_w.total, debugstr_w((WCHAR *)efnd_w.elf[0].elfLogFont.lfFaceName) );
7245 ok( !wcscmp( (WCHAR *)efnd_w.elf[0].elfFullName, primary_fullname ),
7246 "%d: (%d) unexpected elfFullName %s\n", i, efnd_w.total, debugstr_w((WCHAR *)efnd_w.elf[0].elfFullName) );
7247 ok( !wcscmp( (WCHAR *)efnd_w.elf[0].elfStyle, PRIMARYLANGID(GetSystemDefaultLangID()) == LANG_ENGLISH ? L"Reg (en)" : L"Reg (ja)" ),
7248 "%d: (%d) unexpected elfStyle %s\n", i, efnd_w.total, debugstr_w((WCHAR *)efnd_w.elf[0].elfStyle) );
7251 if (PRIMARYLANGID(GetSystemDefaultLangID()) == LANG_ENGLISH)
7253 wcscpy( font_w.lfFaceName, name_cond_ja_reg_w );
7254 memset( &efnd_w, 0, sizeof(efnd_w) );
7255 EnumFontFamiliesExW( dc, &font_w, enum_fullname_data_proc_w, (LPARAM)&efnd_w, 0 );
7256 ok( efnd_w.total == 0, "%d: EnumFontFamiliesExA unexpected count %u.\n", i, efnd_w.total );
7259 wcscpy( font_w.lfFaceName, L"Wine_Lang_Names_Regular" );
7260 memset( &efnd_w, 0, sizeof(efnd_w) );
7261 EnumFontFamiliesExW( dc, &font_w, enum_fullname_data_proc_w, (LPARAM)&efnd_w, 0 );
7262 ok( efnd_w.total == i, "%d: EnumFontFamiliesExW unexpected count %u.\n", i, efnd_w.total );
7264 while (efnd_w.total--)
7266 ok( !wcscmp( (WCHAR *)efnd_w.elf[efnd_w.total].elfLogFont.lfFaceName, efnd_w.total == 1 ? L"Wine_Lang_Names" : primary_family ),
7267 "%d: (%d) unexpected lfFaceName %s\n", i, efnd_w.total, debugstr_w((WCHAR *)efnd_w.elf[efnd_w.total].elfLogFont.lfFaceName) );
7268 ok( !wcscmp( (WCHAR *)efnd_w.elf[efnd_w.total].elfFullName, L"Wine_Lang_Names_Regular" ),
7269 "%d: (%d) unexpected elfFullName %s\n", i, efnd_w.total, debugstr_w((WCHAR *)efnd_w.elf[efnd_w.total].elfFullName) );
7270 if (PRIMARYLANGID(GetSystemDefaultLangID()) == LANG_ENGLISH)
7271 ok( !wcscmp( (WCHAR *)efnd_w.elf[efnd_w.total].elfStyle, efnd_w.total == 1 ? L"Regular" : L"Reg (en)" ),
7272 "%d: (%d) unexpected elfStyle %s\n", i, efnd_w.total, debugstr_w((WCHAR *)efnd_w.elf[efnd_w.total].elfStyle) );
7273 else
7274 ok( !wcscmp( (WCHAR *)efnd_w.elf[0].elfStyle, L"Reg (ja)" ),
7275 "%d: (%d) unexpected elfStyle %s\n", i, efnd_w.total, debugstr_w((WCHAR *)efnd_w.elf[0].elfStyle) );
7278 if (PRIMARYLANGID(GetSystemDefaultLangID()) == LANG_ENGLISH)
7280 wcscpy( font_w.lfFaceName, name_cond_ja_reg_ja_w );
7281 memset( &efnd_w, 0, sizeof(efnd_w) );
7282 EnumFontFamiliesExW( dc, &font_w, enum_fullname_data_proc_w, (LPARAM)&efnd_w, 0 );
7283 ok( efnd_w.total == 0, "%d: EnumFontFamiliesExA unexpected count %u.\n", i, efnd_w.total );
7286 /* another language can also be used for lookup, if the primary langid isn't english, then
7287 english seems to have priority, otherwise or if english is already the primary langid,
7288 the family name with the smallest langid is used as secondary lookup language. */
7290 strcpy( font.lfFaceName, "Wine Lang Cond (zh-tw)" );
7291 memset( &efnd, 0, sizeof(efnd) );
7292 EnumFontFamiliesExA( dc, &font, enum_fullname_data_proc, (LPARAM)&efnd, 0 );
7293 if (PRIMARYLANGID(GetSystemDefaultLangID()) == LANG_ENGLISH)
7294 ok( efnd.total == min( 2, i + 1 ), "%d: EnumFontFamiliesExA unexpected count %u.\n", i, efnd.total );
7295 else /* (zh-tw) doesn't match here probably because there's an (en) name too */
7296 ok( efnd.total == 0, "%d: EnumFontFamiliesExA unexpected count %u.\n", i, efnd.total );
7298 strcpy( font.lfFaceName, "Wine Lang Cond (en)" );
7299 memset( &efnd, 0, sizeof(efnd) );
7300 EnumFontFamiliesExA( dc, &font, enum_fullname_data_proc, (LPARAM)&efnd, 0 );
7301 /* either because it's the primary language, or because it's a secondary */
7302 ok( efnd.total == min( 2, i + 1 ), "%d: EnumFontFamiliesExA unexpected count %u.\n", i, efnd.total );
7304 wcscpy( font_w.lfFaceName, L"Wine Police d'\xe9" "criture (fr)" );
7305 memset( &efnd_w, 0, sizeof(efnd_w) );
7306 EnumFontFamiliesExW( dc, &font_w, enum_fullname_data_proc_w, (LPARAM)&efnd_w, 0 );
7307 /* as wine_langnames3.sfd does not specify (en) name, (fr) is preferred */
7308 if (i == 2) ok( efnd_w.total == 1, "%d: EnumFontFamiliesExW unexpected count %u.\n", i, efnd_w.total );
7309 else ok( efnd_w.total == 0, "%d: EnumFontFamiliesExW unexpected count %u.\n", i, efnd_w.total );
7311 /* case matching should not depend on the current locale */
7312 if (i == 2)
7314 wcscpy( font_w.lfFaceName, L"Wine POLICE D'\xc9" "CRITURE (fr)" );
7315 memset( &efnd_w, 0, sizeof(efnd_w) );
7316 EnumFontFamiliesExW( dc, &font_w, enum_fullname_data_proc_w, (LPARAM)&efnd_w, 0 );
7317 ok( efnd_w.total == 1, "%d: EnumFontFamiliesExW unexpected count %u.\n", i, efnd_w.total );
7320 strcpy( font.lfFaceName, "Wine Lang Cond (ko)" );
7321 memset( &efnd, 0, sizeof(efnd) );
7322 EnumFontFamiliesExA( dc, &font, enum_fullname_data_proc, (LPARAM)&efnd, 0 );
7323 ok( efnd.total == 0, "%d: EnumFontFamiliesExA unexpected count %u.\n", i, efnd.total );
7325 /* that doesn't apply to full names */
7327 strcpy( font.lfFaceName, "Wine Lang Cond Reg (zh-tw)" );
7328 memset( &efnd, 0, sizeof(efnd) );
7329 EnumFontFamiliesExA( dc, &font, enum_fullname_data_proc, (LPARAM)&efnd, 0 );
7330 ok( efnd.total == 0, "%d: EnumFontFamiliesExA unexpected count %u.\n", i, efnd.total );
7332 strcpy( font.lfFaceName, "Wine Lang Cond Reg (fr)" );
7333 memset( &efnd, 0, sizeof(efnd) );
7334 EnumFontFamiliesExA( dc, &font, enum_fullname_data_proc, (LPARAM)&efnd, 0 );
7335 ok( efnd.total == 0, "%d: EnumFontFamiliesExA unexpected count %u.\n", i, efnd.total );
7337 if (i == 0)
7339 ret = AddFontResourceExA( ttf_name2, FR_PRIVATE, 0 );
7340 ok( ret, "AddFontResourceEx() failed\n" );
7342 else if (i == 1)
7344 ret = AddFontResourceExA( ttf_name3, FR_PRIVATE, 0 );
7345 ok( ret, "AddFontResourceEx() failed\n" );
7349 ret = RemoveFontResourceExA( ttf_name3, FR_PRIVATE, 0 );
7350 ok( ret, "RemoveFontResourceEx() failed\n" );
7352 DeleteFileA( ttf_name3 );
7354 ret = RemoveFontResourceExA( ttf_name2, FR_PRIVATE, 0 );
7355 ok( ret, "RemoveFontResourceEx() failed\n" );
7357 DeleteFileA( ttf_name2 );
7359 ret = RemoveFontResourceExA( ttf_name, FR_PRIVATE, 0 );
7360 ok( ret, "RemoveFontResourceEx() failed\n" );
7362 DeleteFileA( ttf_name );
7363 ReleaseDC( NULL, dc );
7366 typedef struct
7368 USHORT majorVersion;
7369 USHORT minorVersion;
7370 SHORT ascender;
7371 SHORT descender;
7372 SHORT lineGap;
7373 USHORT advanceWidthMax;
7374 SHORT minLeftSideBearing;
7375 SHORT minRightSideBearing;
7376 SHORT xMaxExtent;
7377 SHORT caretSlopeRise;
7378 SHORT caretSlopeRun;
7379 SHORT caretOffset;
7380 SHORT reserved[4];
7381 SHORT metricDataFormat;
7382 SHORT numberOfHMetrics;
7383 } TT_Hori_Header;
7385 static void test_GetCharWidthInfo(void)
7387 HDC hdc;
7388 HFONT hfont, hfont_prev;
7389 LOGFONTA lf;
7390 BOOL r;
7391 DWORD ret, i;
7392 OUTLINETEXTMETRICA otm;
7393 TT_Hori_Header hhea;
7394 struct char_width_info
7396 INT lsb, rsb, unk;
7397 } info, info2;
7398 SHORT minLeftSideBearing, minRightSideBearing;
7399 POINT pt[2];
7400 const char* face_list[] = { "Symbol", "Ume Gothic", "MS Gothic" };
7402 if (!pGetCharWidthInfo)
7404 win_skip("GetCharWidthInfo is unavailable\n");
7405 return;
7408 hdc = GetDC(NULL);
7410 /* test default (System) font */
7411 memset(&info, 0xaa, sizeof(info));
7412 r = pGetCharWidthInfo(hdc, &info);
7413 if (r) /* win10 1803 succeeds */
7415 ok(info.lsb == 0, "expected 0, got %d\n", info.lsb);
7416 ok(info.rsb == 0, "expected 0, got %d\n", info.rsb);
7417 ok(info.unk == 0, "expected 0, got %d\n", info.unk);
7420 memset(&lf, 0, sizeof(lf));
7421 lf.lfWeight = FW_NORMAL;
7422 lf.lfCharSet = ANSI_CHARSET;
7423 strcpy(lf.lfFaceName, "Tahoma");
7424 hfont = CreateFontIndirectA(&lf);
7425 hfont_prev = SelectObject(hdc, hfont);
7426 ok(hfont_prev != NULL, "SelectObject failed\n");
7428 ret = GetOutlineTextMetricsA(hdc, sizeof(otm), &otm);
7429 ok(ret != 0, "GetOutlineTextMetricsA failed\n");
7430 DeleteObject(SelectObject(hdc, hfont_prev));
7432 /* test Tahoma at the em square size */
7433 lf.lfHeight = -(int)otm.otmEMSquare;
7434 hfont = CreateFontIndirectA(&lf);
7435 hfont_prev = SelectObject(hdc, hfont);
7436 ok(hfont_prev != NULL, "SelectObject failed\n");
7438 ret = GetFontData(hdc, MS_MAKE_TAG('h','h','e','a'), 0, &hhea, sizeof(hhea));
7439 ok(ret == sizeof(hhea), "got %u\n", ret);
7440 minLeftSideBearing = GET_BE_WORD(hhea.minLeftSideBearing);
7441 minRightSideBearing = GET_BE_WORD(hhea.minRightSideBearing);
7443 memset(&info, 0xaa, sizeof(info));
7444 r = pGetCharWidthInfo(hdc, &info);
7445 ok(r, "GetCharWidthInfo failed\n");
7446 ok(info.lsb == minLeftSideBearing, "expected %d, got %d\n", minLeftSideBearing, info.lsb);
7447 ok(info.rsb == minRightSideBearing, "expected %d, got %d\n", minRightSideBearing, info.rsb);
7449 DeleteObject(SelectObject(hdc, hfont_prev));
7451 /* these values are scaled, try with smaller size */
7452 lf.lfHeight /= 3;
7453 hfont = CreateFontIndirectA(&lf);
7454 hfont_prev = SelectObject(hdc, hfont);
7455 ok(hfont_prev != NULL, "SelectObject failed\n");
7457 memset(&info2, 0xaa, sizeof(info2));
7458 r = pGetCharWidthInfo(hdc, &info2);
7459 ok(r, "pGetCharWidthInfo failed\n");
7460 ok(info2.lsb == info.lsb/3, "expected %d, got %d\n", info.lsb/3, info2.lsb);
7461 ok(info2.rsb == info.rsb/3, "expected %d, got %d\n", info.rsb/3, info2.rsb);
7463 DeleteObject(SelectObject(hdc, hfont_prev));
7464 ReleaseDC(NULL, hdc);
7466 /* test with another mapping mode */
7467 hdc = GetDC(NULL);
7468 SetMapMode(hdc, MM_ISOTROPIC);
7469 SetWindowExtEx(hdc, 2, 2, NULL);
7470 SetViewportExtEx(hdc, 1, 1, NULL);
7472 memset(pt, 0, sizeof(pt));
7473 pt[0].y = otm.otmEMSquare;
7474 DPtoLP(hdc, pt, 1);
7476 memset(&lf, 0, sizeof(lf));
7477 lf.lfWeight = FW_NORMAL;
7478 lf.lfCharSet = ANSI_CHARSET;
7479 lf.lfHeight = -abs(pt[0].y);
7480 strcpy(lf.lfFaceName, "Tahoma");
7481 hfont = CreateFontIndirectA(&lf);
7482 hfont_prev = SelectObject(hdc, hfont);
7483 ok(hfont_prev != NULL, "SelectObject failed\n");
7485 memset(&info2, 0xaa, sizeof(info2));
7486 r = pGetCharWidthInfo(hdc, &info2);
7487 ok(r, "GetCharWidthInfo failed\n");
7488 pt[0].x = info.lsb; pt[0].y = 0;
7489 pt[1].x = info.rsb; pt[1].y = 0;
7490 DPtoLP(hdc, pt, 2);
7491 ok(pt[0].x == info2.lsb, "expected %d, got %d\n", pt[0].x, info2.lsb);
7492 ok(pt[1].x == info2.rsb, "expected %d, got %d\n", pt[1].x, info2.rsb);
7494 DeleteObject(SelectObject(hdc, hfont_prev));
7495 ReleaseDC(NULL, hdc);
7497 /* test with synthetic fonts */
7498 hdc = GetDC(NULL);
7499 for (i = 0; i < ARRAY_SIZE(face_list); i++)
7501 const char* face = face_list[i];
7502 if (!is_truetype_font_installed(face))
7504 skip("%s is not installed\n", face);
7505 continue;
7507 memset(&lf, 0, sizeof(lf));
7508 lf.lfWeight = FW_NORMAL;
7509 lf.lfItalic = FALSE;
7510 lf.lfCharSet = DEFAULT_CHARSET;
7511 lf.lfHeight = -256;
7512 strcpy(lf.lfFaceName, face);
7513 hfont = CreateFontIndirectA(&lf);
7514 hfont_prev = SelectObject(hdc, hfont);
7516 memset(&info, 0xaa, sizeof(info));
7517 r = pGetCharWidthInfo(hdc, &info);
7518 ok(r, "%s: GetCharWidthInfo failed\n", face);
7520 /* test with synthetic bold */
7521 lf.lfWeight = FW_BOLD;
7522 lf.lfItalic = FALSE;
7523 hfont = CreateFontIndirectA(&lf);
7524 DeleteObject(SelectObject(hdc, hfont));
7526 memset(&info2, 0xaa, sizeof(info2));
7527 r = pGetCharWidthInfo(hdc, &info2);
7528 ok(r, "%s: GetCharWidthInfo failed\n", face);
7529 ok(info.lsb == info2.lsb, "%s: expected %d, got %d\n", face, info.lsb, info2.lsb);
7530 ok(info.rsb == info2.rsb, "%s: expected %d, got %d\n", face, info.rsb, info2.rsb);
7532 /* test with synthetic italic */
7533 lf.lfWeight = FW_NORMAL;
7534 lf.lfItalic = TRUE;
7535 hfont = CreateFontIndirectA(&lf);
7536 DeleteObject(SelectObject(hdc, hfont));
7538 memset(&info2, 0xaa, sizeof(info2));
7539 r = pGetCharWidthInfo(hdc, &info2);
7540 ok(r, "%s: GetCharWidthInfo failed\n", face);
7541 todo_wine ok(info.lsb > info2.lsb, "%s: expected less than %d, got %d\n", face, info.lsb, info2.lsb);
7542 todo_wine ok(info.rsb > info2.rsb, "%s: expected less than %d, got %d\n", face, info.rsb, info2.rsb);
7543 DeleteObject(SelectObject(hdc, hfont_prev));
7546 ReleaseDC(NULL, hdc);
7549 static int CALLBACK get_char_width_proc(const LOGFONTA *lf,
7550 const TEXTMETRICA *tm, DWORD type, LPARAM ctx)
7552 HFONT font = CreateFontIndirectA(lf);
7553 HDC dc = GetDC(NULL);
7554 const char c = 'm';
7555 ABCFLOAT abcf;
7556 int i, i32;
7557 BOOL ret;
7558 float f;
7559 ABC abc;
7561 SelectObject(dc, font);
7563 ret = GetCharWidthFloatA(dc, c, c, &f);
7564 ok(ret, "%s: GetCharWidthFloat() failed\n", lf->lfFaceName);
7565 ret = GetCharWidth32A(dc, c, c, &i32);
7566 ok(ret, "%s: GetCharWidth32A() failed\n", lf->lfFaceName);
7567 ret = GetCharWidthA(dc, c, c, &i);
7568 ok(ret, "%s: GetCharWidthA() failed\n", lf->lfFaceName);
7569 ok(i == i32, "%s: mismatched widths %d/%d\n", lf->lfFaceName, i, i32);
7570 ok((float)i / 16.0f == f, "%s: mismatched widths %d/%.8e\n", lf->lfFaceName, i, f);
7572 ret = GetCharABCWidthsFloatA(dc, c, c, &abcf);
7573 ok(ret, "%s: GetCharABCWidths() failed\n", lf->lfFaceName);
7574 if (GetCharABCWidthsA(dc, c, c, &abc))
7575 ok((float)abc.abcB == abcf.abcfB, "%s: mismatched widths %d/%.8e\n",
7576 lf->lfFaceName, abc.abcB, abcf.abcfB);
7578 ReleaseDC(NULL, dc);
7579 DeleteObject(font);
7580 return 1;
7583 static void test_char_width(void)
7585 HDC dc = GetDC(NULL);
7586 LOGFONTA lf = {0};
7588 lf.lfCharSet = DEFAULT_CHARSET;
7589 EnumFontFamiliesExA(dc, &lf, get_char_width_proc, 0, 0);
7591 ReleaseDC(NULL, dc);
7594 static void test_GetCharacterPlacement_kerning(void)
7596 LOGFONTA lf;
7597 HFONT hfont, hfont_old;
7598 KERNINGPAIR *kp;
7599 HDC hdc;
7600 DWORD count, ret, i, size, width, width_kern, idx;
7601 WCHAR str[30];
7602 GCP_RESULTSW result;
7603 int kern[30], pos[30], pos_kern[30], dx[30], dx_kern[30], kern_amount;
7605 if (!is_font_installed("Arial"))
7607 skip("Arial is not installed, skipping the test\n");
7608 return;
7611 hdc = GetDC(0);
7613 memset(&lf, 0, sizeof(lf));
7614 strcpy(lf.lfFaceName, "Arial");
7615 lf.lfHeight = 120;
7616 hfont = CreateFontIndirectA(&lf);
7617 ok(hfont != NULL, "CreateFontIndirect failed\n");
7619 hfont_old = SelectObject(hdc, hfont);
7621 count = GetKerningPairsW(hdc, 0, NULL);
7622 kp = HeapAlloc(GetProcessHeap(), 0, count * sizeof(*kp));
7624 ret = GetKerningPairsW(hdc, count, kp);
7625 ok(ret == count, "got %u, expected %u\n", ret, count);
7627 size = kern_amount = idx = 0;
7628 for (i = 0; i < count; i++)
7630 if (kp[i].wFirst >= 'A' && kp[i].wFirst <= 'z' &&
7631 kp[i].wSecond >= 'A' && kp[i].wSecond <= 'z')
7633 str[size++] = kp[i].wFirst;
7634 str[size++] = kp[i].wSecond;
7635 str[size++] = 0;
7636 kern[idx] = kp[i].iKernAmount;
7637 idx++;
7638 kern_amount += kp[i].iKernAmount;
7639 if (size >= ARRAY_SIZE(str)) break;
7643 HeapFree(GetProcessHeap(), 0, kp);
7645 count = size;
7647 memset(&result, 0, sizeof(result));
7648 result.lStructSize = sizeof(result);
7649 result.lpCaretPos = pos;
7650 result.lpDx = dx;
7651 result.nGlyphs = count;
7652 ret = GetCharacterPlacementW(hdc, str, count, 0, &result, 0);
7653 ok(ret, "GetCharacterPlacement failed\n");
7654 ok(result.nGlyphs == count, "got %u\n", result.nGlyphs);
7655 width = LOWORD(ret);
7657 memset(&result, 0, sizeof(result));
7658 result.lStructSize = sizeof(result);
7659 result.lpCaretPos = pos_kern;
7660 result.lpDx = dx_kern;
7661 result.nGlyphs = count;
7662 ret = GetCharacterPlacementW(hdc, str, count, 0, &result, GCP_USEKERNING);
7663 ok(ret, "GetCharacterPlacement failed\n");
7664 ok(result.nGlyphs == count, "got %u\n", result.nGlyphs);
7665 width_kern = LOWORD(ret);
7667 if (width == width_kern)
7669 win_skip("GCP_USEKERNING is broken on this platform\n");
7670 goto done;
7673 ok(width + kern_amount == width_kern, "%d + %d != %d\n", width, kern_amount, width_kern);
7675 kern_amount = idx = 0;
7676 for (i = 0; i < count; i += 3, idx++)
7678 ok(pos[i] + kern_amount == pos_kern[i], "%d: %d + %d != %d\n", i, pos[i], kern_amount, pos_kern[i]);
7679 kern_amount += kern[idx];
7680 ok(pos[i+1] + kern_amount == pos_kern[i+1], "%d: %d + %d != %d\n", i, pos[i+1], kern_amount, pos_kern[i+1]);
7681 ok(pos[i+2] + kern_amount == pos_kern[i+2], "%d: %d + %d != %d\n", i, pos[i+2], kern_amount, pos_kern[i+2]);
7683 ok(dx[i] + kern[idx] == dx_kern[i], "%d: %d + %d != %d\n", i, dx[i], kern[idx], dx_kern[i]);
7684 ok(dx[i+1] == dx_kern[i+1], "%d: %d != %d\n", i, dx[i+1], dx_kern[i+1]);
7685 ok(dx[i+2] == dx_kern[i+2], "%d: %d != %d\n", i, dx[i+2], dx_kern[i+2]);
7688 done:
7689 SelectObject(hdc, hfont_old);
7690 DeleteObject(hfont);
7691 ReleaseDC(0, hdc);
7694 START_TEST(font)
7696 static const char *test_names[] =
7698 "AddFontMemResource",
7700 char path_name[MAX_PATH];
7701 STARTUPINFOA startup;
7702 char **argv;
7703 int argc, i;
7705 init();
7707 argc = winetest_get_mainargs(&argv);
7708 if (argc >= 3)
7710 if (!strcmp(argv[2], "AddFontMemResource"))
7711 test_AddFontMemResource();
7712 return;
7715 test_stock_fonts();
7716 test_logfont();
7717 test_bitmap_font();
7718 test_outline_font();
7719 test_bitmap_font_metrics();
7720 test_GdiGetCharDimensions();
7721 test_GetCharABCWidths();
7722 test_text_extents();
7723 test_GetGlyphIndices();
7724 test_GetKerningPairs();
7725 test_GetOutlineTextMetrics();
7726 test_SetTextJustification();
7727 test_font_charset();
7728 test_GdiGetCodePage();
7729 test_GetFontUnicodeRanges();
7730 test_nonexistent_font();
7731 test_orientation();
7732 test_height_selection();
7733 test_EnumFonts();
7734 test_EnumFonts_subst();
7736 /* On Windows Arial has a lot of default charset aliases such as Arial Cyr,
7737 * I'd like to avoid them in this test.
7739 test_EnumFontFamilies("Arial Black", ANSI_CHARSET);
7740 test_EnumFontFamilies("Symbol", SYMBOL_CHARSET);
7741 if (is_truetype_font_installed("Arial Black") &&
7742 (is_truetype_font_installed("Symbol") || is_truetype_font_installed("Wingdings")))
7744 test_EnumFontFamilies("", ANSI_CHARSET);
7745 test_EnumFontFamilies("", SYMBOL_CHARSET);
7746 test_EnumFontFamilies("", DEFAULT_CHARSET);
7748 else
7749 skip("Arial Black or Symbol/Wingdings is not installed\n");
7750 test_EnumFontFamiliesEx_default_charset();
7751 test_GetTextMetrics();
7752 test_RealizationInfo();
7753 test_GetTextFace();
7754 test_GetGlyphOutline();
7755 test_GetTextMetrics2("Tahoma", -11);
7756 test_GetTextMetrics2("Tahoma", -55);
7757 test_GetTextMetrics2("Tahoma", -110);
7758 test_GetTextMetrics2("Arial", -11);
7759 test_GetTextMetrics2("Arial", -55);
7760 test_GetTextMetrics2("Arial", -110);
7761 test_GetCharacterPlacement();
7762 test_GetCharacterPlacement_kerning();
7763 test_GetCharWidthInfo();
7764 test_CreateFontIndirect();
7765 test_CreateFontIndirectEx();
7766 test_oemcharset();
7767 test_fullname();
7768 test_fullname2();
7769 test_east_asian_font_selection();
7770 test_max_height();
7771 test_vertical_order();
7772 test_GetCharWidth32();
7773 test_fake_bold_font();
7774 test_bitmap_font_glyph_index();
7775 test_GetCharWidthI();
7776 test_long_names();
7777 test_ttf_names();
7778 test_lang_names();
7779 test_char_width();
7781 /* These tests should be last test until RemoveFontResource
7782 * is properly implemented.
7784 test_vertical_font();
7785 test_CreateScalableFontResource();
7787 winetest_get_mainargs( &argv );
7788 for (i = 0; i < ARRAY_SIZE(test_names); ++i)
7790 PROCESS_INFORMATION info;
7792 memset(&startup, 0, sizeof(startup));
7793 startup.cb = sizeof(startup);
7794 sprintf(path_name, "%s font %s", argv[0], test_names[i]);
7795 ok(CreateProcessA(NULL, path_name, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info),
7796 "CreateProcess failed.\n");
7797 wait_child_process(info.hProcess);
7798 CloseHandle(info.hProcess);
7799 CloseHandle(info.hThread);