gdi32: Correctly propagate the enumproc's return value.
[wine/multimedia.git] / dlls / gdi32 / tests / font.c
blobd104452e3ef15a17eea5df0185facb5cfe10dbb5
1 /*
2 * Unit test suite for fonts
4 * Copyright 2002 Mike McCormack
5 * Copyright 2004 Dmitry Timoshkov
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #include <stdarg.h>
23 #include <assert.h>
25 #include "windef.h"
26 #include "winbase.h"
27 #include "wingdi.h"
28 #include "winuser.h"
29 #include "winnls.h"
31 #include "wine/test.h"
33 /* Do not allow more than 1 deviation here */
34 #define match_off_by_1(a, b) (abs((a) - (b)) <= 1)
36 #define near_match(a, b) (abs((a) - (b)) <= 6)
37 #define expect(expected, got) ok(got == expected, "Expected %.8x, got %.8x\n", expected, got)
39 static LONG (WINAPI *pGdiGetCharDimensions)(HDC hdc, LPTEXTMETRICW lptm, LONG *height);
40 static DWORD (WINAPI *pGdiGetCodePage)(HDC hdc);
41 static BOOL (WINAPI *pGetCharABCWidthsI)(HDC hdc, UINT first, UINT count, LPWORD glyphs, LPABC abc);
42 static BOOL (WINAPI *pGetCharABCWidthsA)(HDC hdc, UINT first, UINT last, LPABC abc);
43 static BOOL (WINAPI *pGetCharABCWidthsW)(HDC hdc, UINT first, UINT last, LPABC abc);
44 static BOOL (WINAPI *pGetCharABCWidthsFloatW)(HDC hdc, UINT first, UINT last, LPABCFLOAT abc);
45 static DWORD (WINAPI *pGetFontUnicodeRanges)(HDC hdc, LPGLYPHSET lpgs);
46 static DWORD (WINAPI *pGetGlyphIndicesA)(HDC hdc, LPCSTR lpstr, INT count, LPWORD pgi, DWORD flags);
47 static DWORD (WINAPI *pGetGlyphIndicesW)(HDC hdc, LPCWSTR lpstr, INT count, LPWORD pgi, DWORD flags);
48 static BOOL (WINAPI *pGdiRealizationInfo)(HDC hdc, DWORD *);
49 static HFONT (WINAPI *pCreateFontIndirectExA)(const ENUMLOGFONTEXDV *);
50 static HANDLE (WINAPI *pAddFontMemResourceEx)(PVOID, DWORD, PVOID, DWORD *);
51 static BOOL (WINAPI *pRemoveFontMemResourceEx)(HANDLE);
52 static INT (WINAPI *pAddFontResourceExA)(LPCSTR, DWORD, PVOID);
53 static BOOL (WINAPI *pRemoveFontResourceExA)(LPCSTR, DWORD, PVOID);
55 static HMODULE hgdi32 = 0;
56 static const MAT2 mat = { {0,1}, {0,0}, {0,0}, {0,1} };
58 static void init(void)
60 hgdi32 = GetModuleHandleA("gdi32.dll");
62 pGdiGetCharDimensions = (void *)GetProcAddress(hgdi32, "GdiGetCharDimensions");
63 pGdiGetCodePage = (void *) GetProcAddress(hgdi32,"GdiGetCodePage");
64 pGetCharABCWidthsI = (void *)GetProcAddress(hgdi32, "GetCharABCWidthsI");
65 pGetCharABCWidthsA = (void *)GetProcAddress(hgdi32, "GetCharABCWidthsA");
66 pGetCharABCWidthsW = (void *)GetProcAddress(hgdi32, "GetCharABCWidthsW");
67 pGetCharABCWidthsFloatW = (void *)GetProcAddress(hgdi32, "GetCharABCWidthsFloatW");
68 pGetFontUnicodeRanges = (void *)GetProcAddress(hgdi32, "GetFontUnicodeRanges");
69 pGetGlyphIndicesA = (void *)GetProcAddress(hgdi32, "GetGlyphIndicesA");
70 pGetGlyphIndicesW = (void *)GetProcAddress(hgdi32, "GetGlyphIndicesW");
71 pGdiRealizationInfo = (void *)GetProcAddress(hgdi32, "GdiRealizationInfo");
72 pCreateFontIndirectExA = (void *)GetProcAddress(hgdi32, "CreateFontIndirectExA");
73 pAddFontMemResourceEx = (void *)GetProcAddress(hgdi32, "AddFontMemResourceEx");
74 pRemoveFontMemResourceEx = (void *)GetProcAddress(hgdi32, "RemoveFontMemResourceEx");
75 pAddFontResourceExA = (void *)GetProcAddress(hgdi32, "AddFontResourceExA");
76 pRemoveFontResourceExA = (void *)GetProcAddress(hgdi32, "RemoveFontResourceExA");
79 static INT CALLBACK is_truetype_font_installed_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
81 if (type != TRUETYPE_FONTTYPE) return 1;
83 return 0;
86 static BOOL is_truetype_font_installed(const char *name)
88 HDC hdc = GetDC(0);
89 BOOL ret = FALSE;
91 if (!EnumFontFamiliesA(hdc, name, is_truetype_font_installed_proc, 0))
92 ret = TRUE;
94 ReleaseDC(0, hdc);
95 return ret;
98 static INT CALLBACK is_font_installed_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
100 return 0;
103 static BOOL is_font_installed(const char *name)
105 HDC hdc = GetDC(0);
106 BOOL ret = FALSE;
108 if(!EnumFontFamiliesA(hdc, name, is_font_installed_proc, 0))
109 ret = TRUE;
111 ReleaseDC(0, hdc);
112 return ret;
115 static void check_font(const char* test, const LOGFONTA* lf, HFONT hfont)
117 LOGFONTA getobj_lf;
118 int ret, minlen = 0;
120 if (!hfont)
121 return;
123 ret = GetObject(hfont, sizeof(getobj_lf), &getobj_lf);
124 /* NT4 tries to be clever and only returns the minimum length */
125 while (lf->lfFaceName[minlen] && minlen < LF_FACESIZE-1)
126 minlen++;
127 minlen += FIELD_OFFSET(LOGFONTA, lfFaceName) + 1;
128 ok(ret == sizeof(LOGFONTA) || ret == minlen, "%s: GetObject returned %d\n", test, ret);
129 ok(lf->lfHeight == getobj_lf.lfHeight ||
130 broken((SHORT)lf->lfHeight == getobj_lf.lfHeight), /* win9x */
131 "lfHeight: expect %08x got %08x\n", lf->lfHeight, getobj_lf.lfHeight);
132 ok(lf->lfWidth == getobj_lf.lfWidth ||
133 broken((SHORT)lf->lfWidth == getobj_lf.lfWidth), /* win9x */
134 "lfWidth: expect %08x got %08x\n", lf->lfWidth, getobj_lf.lfWidth);
135 ok(lf->lfEscapement == getobj_lf.lfEscapement ||
136 broken((SHORT)lf->lfEscapement == getobj_lf.lfEscapement), /* win9x */
137 "lfEscapement: expect %08x got %08x\n", lf->lfEscapement, getobj_lf.lfEscapement);
138 ok(lf->lfOrientation == getobj_lf.lfOrientation ||
139 broken((SHORT)lf->lfOrientation == getobj_lf.lfOrientation), /* win9x */
140 "lfOrientation: expect %08x got %08x\n", lf->lfOrientation, getobj_lf.lfOrientation);
141 ok(lf->lfWeight == getobj_lf.lfWeight ||
142 broken((SHORT)lf->lfWeight == getobj_lf.lfWeight), /* win9x */
143 "lfWeight: expect %08x got %08x\n", lf->lfWeight, getobj_lf.lfWeight);
144 ok(lf->lfItalic == getobj_lf.lfItalic, "lfItalic: expect %02x got %02x\n", lf->lfItalic, getobj_lf.lfItalic);
145 ok(lf->lfUnderline == getobj_lf.lfUnderline, "lfUnderline: expect %02x got %02x\n", lf->lfUnderline, getobj_lf.lfUnderline);
146 ok(lf->lfStrikeOut == getobj_lf.lfStrikeOut, "lfStrikeOut: expect %02x got %02x\n", lf->lfStrikeOut, getobj_lf.lfStrikeOut);
147 ok(lf->lfCharSet == getobj_lf.lfCharSet, "lfCharSet: expect %02x got %02x\n", lf->lfCharSet, getobj_lf.lfCharSet);
148 ok(lf->lfOutPrecision == getobj_lf.lfOutPrecision, "lfOutPrecision: expect %02x got %02x\n", lf->lfOutPrecision, getobj_lf.lfOutPrecision);
149 ok(lf->lfClipPrecision == getobj_lf.lfClipPrecision, "lfClipPrecision: expect %02x got %02x\n", lf->lfClipPrecision, getobj_lf.lfClipPrecision);
150 ok(lf->lfQuality == getobj_lf.lfQuality, "lfQuality: expect %02x got %02x\n", lf->lfQuality, getobj_lf.lfQuality);
151 ok(lf->lfPitchAndFamily == getobj_lf.lfPitchAndFamily, "lfPitchAndFamily: expect %02x got %02x\n", lf->lfPitchAndFamily, getobj_lf.lfPitchAndFamily);
152 ok(!lstrcmpA(lf->lfFaceName, getobj_lf.lfFaceName) ||
153 broken(!memcmp(lf->lfFaceName, getobj_lf.lfFaceName, LF_FACESIZE-1)), /* win9x doesn't ensure '\0' termination */
154 "%s: font names don't match: %s != %s\n", test, lf->lfFaceName, getobj_lf.lfFaceName);
157 static HFONT create_font(const char* test, const LOGFONTA* lf)
159 HFONT hfont = CreateFontIndirectA(lf);
160 ok(hfont != 0, "%s: CreateFontIndirect failed\n", test);
161 if (hfont)
162 check_font(test, lf, hfont);
163 return hfont;
166 static void test_logfont(void)
168 LOGFONTA lf;
169 HFONT hfont;
171 memset(&lf, 0, sizeof lf);
173 lf.lfCharSet = ANSI_CHARSET;
174 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
175 lf.lfWeight = FW_DONTCARE;
176 lf.lfHeight = 16;
177 lf.lfWidth = 16;
178 lf.lfQuality = DEFAULT_QUALITY;
180 lstrcpyA(lf.lfFaceName, "Arial");
181 hfont = create_font("Arial", &lf);
182 DeleteObject(hfont);
184 memset(&lf, 'A', sizeof(lf));
185 hfont = CreateFontIndirectA(&lf);
186 ok(hfont != 0, "CreateFontIndirectA with strange LOGFONT failed\n");
188 lf.lfFaceName[LF_FACESIZE - 1] = 0;
189 check_font("AAA...", &lf, hfont);
190 DeleteObject(hfont);
193 static INT CALLBACK font_enum_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
195 if (type & RASTER_FONTTYPE)
197 LOGFONT *lf = (LOGFONT *)lParam;
198 *lf = *elf;
199 return 0; /* stop enumeration */
202 return 1; /* continue enumeration */
205 static void compare_tm(const TEXTMETRICA *tm, const TEXTMETRICA *otm)
207 ok(tm->tmHeight == otm->tmHeight, "tmHeight %d != %d\n", tm->tmHeight, otm->tmHeight);
208 ok(tm->tmAscent == otm->tmAscent, "tmAscent %d != %d\n", tm->tmAscent, otm->tmAscent);
209 ok(tm->tmDescent == otm->tmDescent, "tmDescent %d != %d\n", tm->tmDescent, otm->tmDescent);
210 ok(tm->tmInternalLeading == otm->tmInternalLeading, "tmInternalLeading %d != %d\n", tm->tmInternalLeading, otm->tmInternalLeading);
211 ok(tm->tmExternalLeading == otm->tmExternalLeading, "tmExternalLeading %d != %d\n", tm->tmExternalLeading, otm->tmExternalLeading);
212 ok(tm->tmAveCharWidth == otm->tmAveCharWidth, "tmAveCharWidth %d != %d\n", tm->tmAveCharWidth, otm->tmAveCharWidth);
213 ok(tm->tmMaxCharWidth == otm->tmMaxCharWidth, "tmMaxCharWidth %d != %d\n", tm->tmMaxCharWidth, otm->tmMaxCharWidth);
214 ok(tm->tmWeight == otm->tmWeight, "tmWeight %d != %d\n", tm->tmWeight, otm->tmWeight);
215 ok(tm->tmOverhang == otm->tmOverhang, "tmOverhang %d != %d\n", tm->tmOverhang, otm->tmOverhang);
216 ok(tm->tmDigitizedAspectX == otm->tmDigitizedAspectX, "tmDigitizedAspectX %d != %d\n", tm->tmDigitizedAspectX, otm->tmDigitizedAspectX);
217 ok(tm->tmDigitizedAspectY == otm->tmDigitizedAspectY, "tmDigitizedAspectY %d != %d\n", tm->tmDigitizedAspectY, otm->tmDigitizedAspectY);
218 ok(tm->tmFirstChar == otm->tmFirstChar, "tmFirstChar %d != %d\n", tm->tmFirstChar, otm->tmFirstChar);
219 ok(tm->tmLastChar == otm->tmLastChar, "tmLastChar %d != %d\n", tm->tmLastChar, otm->tmLastChar);
220 ok(tm->tmDefaultChar == otm->tmDefaultChar, "tmDefaultChar %d != %d\n", tm->tmDefaultChar, otm->tmDefaultChar);
221 ok(tm->tmBreakChar == otm->tmBreakChar, "tmBreakChar %d != %d\n", tm->tmBreakChar, otm->tmBreakChar);
222 ok(tm->tmItalic == otm->tmItalic, "tmItalic %d != %d\n", tm->tmItalic, otm->tmItalic);
223 ok(tm->tmUnderlined == otm->tmUnderlined, "tmUnderlined %d != %d\n", tm->tmUnderlined, otm->tmUnderlined);
224 ok(tm->tmStruckOut == otm->tmStruckOut, "tmStruckOut %d != %d\n", tm->tmStruckOut, otm->tmStruckOut);
225 ok(tm->tmPitchAndFamily == otm->tmPitchAndFamily, "tmPitchAndFamily %d != %d\n", tm->tmPitchAndFamily, otm->tmPitchAndFamily);
226 ok(tm->tmCharSet == otm->tmCharSet, "tmCharSet %d != %d\n", tm->tmCharSet, otm->tmCharSet);
229 static void test_font_metrics(HDC hdc, HFONT hfont, LONG lfHeight,
230 LONG lfWidth, const char *test_str,
231 INT test_str_len, const TEXTMETRICA *tm_orig,
232 const SIZE *size_orig, INT width_of_A_orig,
233 INT scale_x, INT scale_y)
235 LOGFONTA lf;
236 OUTLINETEXTMETRIC otm;
237 TEXTMETRICA tm;
238 SIZE size;
239 INT width_of_A, cx, cy;
240 UINT ret;
242 if (!hfont)
243 return;
245 ok(GetCurrentObject(hdc, OBJ_FONT) == hfont, "hfont should be selected\n");
247 GetObjectA(hfont, sizeof(lf), &lf);
249 if (GetOutlineTextMetricsA(hdc, 0, NULL))
251 otm.otmSize = sizeof(otm) / 2;
252 ret = GetOutlineTextMetricsA(hdc, otm.otmSize, &otm);
253 ok(ret == sizeof(otm)/2 /* XP */ ||
254 ret == 1 /* Win9x */, "expected sizeof(otm)/2, got %u\n", ret);
256 memset(&otm, 0x1, sizeof(otm));
257 otm.otmSize = sizeof(otm);
258 ret = GetOutlineTextMetricsA(hdc, otm.otmSize, &otm);
259 ok(ret == sizeof(otm) /* XP */ ||
260 ret == 1 /* Win9x */, "expected sizeof(otm), got %u\n", ret);
262 memset(&tm, 0x2, sizeof(tm));
263 ret = GetTextMetricsA(hdc, &tm);
264 ok(ret, "GetTextMetricsA failed\n");
265 /* the structure size is aligned */
266 if (memcmp(&tm, &otm.otmTextMetrics, FIELD_OFFSET(TEXTMETRICA, tmCharSet) + 1))
268 ok(0, "tm != otm\n");
269 compare_tm(&tm, &otm.otmTextMetrics);
272 tm = otm.otmTextMetrics;
273 if (0) /* these metrics are scaled too, but with rounding errors */
275 ok(otm.otmAscent == tm.tmAscent, "ascent %d != %d\n", otm.otmAscent, tm.tmAscent);
276 ok(otm.otmDescent == -tm.tmDescent, "descent %d != %d\n", otm.otmDescent, -tm.tmDescent);
278 ok(otm.otmMacAscent == tm.tmAscent, "ascent %d != %d\n", otm.otmMacAscent, tm.tmAscent);
279 ok(otm.otmDescent < 0, "otm.otmDescent should be < 0\n");
280 ok(otm.otmMacDescent < 0, "otm.otmMacDescent should be < 0\n");
281 ok(tm.tmDescent > 0, "tm.tmDescent should be > 0\n");
282 ok(otm.otmMacDescent == -tm.tmDescent, "descent %d != %d\n", otm.otmMacDescent, -tm.tmDescent);
283 ok(otm.otmEMSquare == 2048, "expected 2048, got %d\n", otm.otmEMSquare);
285 else
287 ret = GetTextMetricsA(hdc, &tm);
288 ok(ret, "GetTextMetricsA failed\n");
291 cx = tm.tmAveCharWidth / tm_orig->tmAveCharWidth;
292 cy = tm.tmHeight / tm_orig->tmHeight;
293 ok(cx == scale_x && cy == scale_y, "height %d: expected scale_x %d, scale_y %d, got cx %d, cy %d\n",
294 lfHeight, scale_x, scale_y, cx, cy);
295 ok(tm.tmHeight == tm_orig->tmHeight * scale_y, "height %d != %d\n", tm.tmHeight, tm_orig->tmHeight * scale_y);
296 ok(tm.tmAscent == tm_orig->tmAscent * scale_y, "ascent %d != %d\n", tm.tmAscent, tm_orig->tmAscent * scale_y);
297 ok(tm.tmDescent == tm_orig->tmDescent * scale_y, "descent %d != %d\n", tm.tmDescent, tm_orig->tmDescent * scale_y);
298 ok(near_match(tm.tmAveCharWidth, tm_orig->tmAveCharWidth * scale_x), "ave width %d != %d\n", tm.tmAveCharWidth, tm_orig->tmAveCharWidth * scale_x);
299 ok(near_match(tm.tmMaxCharWidth, tm_orig->tmMaxCharWidth * scale_x), "max width %d != %d\n", tm.tmMaxCharWidth, tm_orig->tmMaxCharWidth * scale_x);
301 ok(lf.lfHeight == lfHeight, "lfHeight %d != %d\n", lf.lfHeight, lfHeight);
302 if (lf.lfHeight)
304 if (lf.lfWidth)
305 ok(lf.lfWidth == tm.tmAveCharWidth, "lfWidth %d != tm %d\n", lf.lfWidth, tm.tmAveCharWidth);
307 else
308 ok(lf.lfWidth == lfWidth, "lfWidth %d != %d\n", lf.lfWidth, lfWidth);
310 GetTextExtentPoint32A(hdc, test_str, test_str_len, &size);
312 ok(near_match(size.cx, size_orig->cx * scale_x), "cx %d != %d\n", size.cx, size_orig->cx * scale_x);
313 ok(size.cy == size_orig->cy * scale_y, "cy %d != %d\n", size.cy, size_orig->cy * scale_y);
315 GetCharWidthA(hdc, 'A', 'A', &width_of_A);
317 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);
320 /* Test how GDI scales bitmap font metrics */
321 static void test_bitmap_font(void)
323 static const char test_str[11] = "Test String";
324 HDC hdc;
325 LOGFONTA bitmap_lf;
326 HFONT hfont, old_hfont;
327 TEXTMETRICA tm_orig;
328 SIZE size_orig;
329 INT ret, i, width_orig, height_orig, scale, lfWidth;
331 hdc = GetDC(0);
333 /* "System" has only 1 pixel size defined, otherwise the test breaks */
334 ret = EnumFontFamiliesA(hdc, "System", font_enum_proc, (LPARAM)&bitmap_lf);
335 if (ret)
337 ReleaseDC(0, hdc);
338 trace("no bitmap fonts were found, skipping the test\n");
339 return;
342 trace("found bitmap font %s, height %d\n", bitmap_lf.lfFaceName, bitmap_lf.lfHeight);
344 height_orig = bitmap_lf.lfHeight;
345 lfWidth = bitmap_lf.lfWidth;
347 hfont = create_font("bitmap", &bitmap_lf);
348 old_hfont = SelectObject(hdc, hfont);
349 ok(GetTextMetricsA(hdc, &tm_orig), "GetTextMetricsA failed\n");
350 ok(GetTextExtentPoint32A(hdc, test_str, sizeof(test_str), &size_orig), "GetTextExtentPoint32A failed\n");
351 ok(GetCharWidthA(hdc, 'A', 'A', &width_orig), "GetCharWidthA failed\n");
352 SelectObject(hdc, old_hfont);
353 DeleteObject(hfont);
355 bitmap_lf.lfHeight = 0;
356 bitmap_lf.lfWidth = 4;
357 hfont = create_font("bitmap", &bitmap_lf);
358 old_hfont = SelectObject(hdc, hfont);
359 test_font_metrics(hdc, hfont, 0, 4, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 1, 1);
360 SelectObject(hdc, old_hfont);
361 DeleteObject(hfont);
363 bitmap_lf.lfHeight = height_orig;
364 bitmap_lf.lfWidth = lfWidth;
366 /* test fractional scaling */
367 for (i = 1; i <= height_orig * 6; i++)
369 INT nearest_height;
371 bitmap_lf.lfHeight = i;
372 hfont = create_font("fractional", &bitmap_lf);
373 scale = (i + height_orig - 1) / height_orig;
374 nearest_height = scale * height_orig;
375 /* Only jump to the next height if the difference <= 25% original height */
376 if (scale > 2 && nearest_height - i > height_orig / 4) scale--;
377 /* The jump between unscaled and doubled is delayed by 1 in winnt+ but not in win9x,
378 so we'll not test this particular height. */
379 else if(scale == 2 && nearest_height - i == (height_orig / 4)) continue;
380 else if(scale == 2 && nearest_height - i > (height_orig / 4 - 1)) scale--;
381 old_hfont = SelectObject(hdc, hfont);
382 test_font_metrics(hdc, hfont, bitmap_lf.lfHeight, 0, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 1, scale);
383 SelectObject(hdc, old_hfont);
384 DeleteObject(hfont);
387 /* test integer scaling 3x2 */
388 bitmap_lf.lfHeight = height_orig * 2;
389 bitmap_lf.lfWidth *= 3;
390 hfont = create_font("3x2", &bitmap_lf);
391 old_hfont = SelectObject(hdc, hfont);
392 test_font_metrics(hdc, hfont, bitmap_lf.lfHeight, 0, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 3, 2);
393 SelectObject(hdc, old_hfont);
394 DeleteObject(hfont);
396 /* test integer scaling 3x3 */
397 bitmap_lf.lfHeight = height_orig * 3;
398 bitmap_lf.lfWidth = 0;
399 hfont = create_font("3x3", &bitmap_lf);
400 old_hfont = SelectObject(hdc, hfont);
401 test_font_metrics(hdc, hfont, bitmap_lf.lfHeight, 0, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 3, 3);
402 SelectObject(hdc, old_hfont);
403 DeleteObject(hfont);
405 ReleaseDC(0, hdc);
408 /* Test how GDI scales outline font metrics */
409 static void test_outline_font(void)
411 static const char test_str[11] = "Test String";
412 HDC hdc, hdc_2;
413 LOGFONTA lf;
414 HFONT hfont, old_hfont, old_hfont_2;
415 OUTLINETEXTMETRICA otm;
416 SIZE size_orig;
417 INT width_orig, height_orig, lfWidth;
418 XFORM xform;
419 GLYPHMETRICS gm;
420 MAT2 mat2 = { {0x8000,0}, {0,0}, {0,0}, {0x8000,0} };
421 POINT pt;
422 INT ret;
424 if (!is_truetype_font_installed("Arial"))
426 skip("Arial is not installed\n");
427 return;
430 hdc = CreateCompatibleDC(0);
432 memset(&lf, 0, sizeof(lf));
433 strcpy(lf.lfFaceName, "Arial");
434 lf.lfHeight = 72;
435 hfont = create_font("outline", &lf);
436 old_hfont = SelectObject(hdc, hfont);
437 otm.otmSize = sizeof(otm);
438 ok(GetOutlineTextMetricsA(hdc, sizeof(otm), &otm), "GetTextMetricsA failed\n");
439 ok(GetTextExtentPoint32A(hdc, test_str, sizeof(test_str), &size_orig), "GetTextExtentPoint32A failed\n");
440 ok(GetCharWidthA(hdc, 'A', 'A', &width_orig), "GetCharWidthA failed\n");
442 test_font_metrics(hdc, hfont, lf.lfHeight, otm.otmTextMetrics.tmAveCharWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
443 SelectObject(hdc, old_hfont);
444 DeleteObject(hfont);
446 /* font of otmEMSquare height helps to avoid a lot of rounding errors */
447 lf.lfHeight = otm.otmEMSquare;
448 lf.lfHeight = -lf.lfHeight;
449 hfont = create_font("outline", &lf);
450 old_hfont = SelectObject(hdc, hfont);
451 otm.otmSize = sizeof(otm);
452 ok(GetOutlineTextMetricsA(hdc, sizeof(otm), &otm), "GetTextMetricsA failed\n");
453 ok(GetTextExtentPoint32A(hdc, test_str, sizeof(test_str), &size_orig), "GetTextExtentPoint32A failed\n");
454 ok(GetCharWidthA(hdc, 'A', 'A', &width_orig), "GetCharWidthA failed\n");
455 SelectObject(hdc, old_hfont);
456 DeleteObject(hfont);
458 height_orig = otm.otmTextMetrics.tmHeight;
459 lfWidth = otm.otmTextMetrics.tmAveCharWidth;
461 /* test integer scaling 3x2 */
462 lf.lfHeight = height_orig * 2;
463 lf.lfWidth = lfWidth * 3;
464 hfont = create_font("3x2", &lf);
465 old_hfont = SelectObject(hdc, hfont);
466 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 3, 2);
467 SelectObject(hdc, old_hfont);
468 DeleteObject(hfont);
470 /* test integer scaling 3x3 */
471 lf.lfHeight = height_orig * 3;
472 lf.lfWidth = lfWidth * 3;
473 hfont = create_font("3x3", &lf);
474 old_hfont = SelectObject(hdc, hfont);
475 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 3, 3);
476 SelectObject(hdc, old_hfont);
477 DeleteObject(hfont);
479 /* test integer scaling 1x1 */
480 lf.lfHeight = height_orig * 1;
481 lf.lfWidth = lfWidth * 1;
482 hfont = create_font("1x1", &lf);
483 old_hfont = SelectObject(hdc, hfont);
484 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
485 SelectObject(hdc, old_hfont);
486 DeleteObject(hfont);
488 /* test integer scaling 1x1 */
489 lf.lfHeight = height_orig;
490 lf.lfWidth = 0;
491 hfont = create_font("1x1", &lf);
492 old_hfont = SelectObject(hdc, hfont);
493 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
495 /* with an identity matrix */
496 memset(&gm, 0, sizeof(gm));
497 SetLastError(0xdeadbeef);
498 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
499 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
500 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
501 ok(gm.gmCellIncX == width_orig, "incX %d != %d\n", gm.gmCellIncX, width_orig);
502 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
503 /* with a custom matrix */
504 memset(&gm, 0, sizeof(gm));
505 SetLastError(0xdeadbeef);
506 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat2);
507 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
508 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
509 ok(gm.gmCellIncX == width_orig/2, "incX %d != %d\n", gm.gmCellIncX, width_orig/2);
510 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
512 /* Test that changing the DC transformation affects only the font
513 * selected on this DC and doesn't affect the same font selected on
514 * another DC.
516 hdc_2 = CreateCompatibleDC(0);
517 old_hfont_2 = SelectObject(hdc_2, hfont);
518 test_font_metrics(hdc_2, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
520 SetMapMode(hdc, MM_ANISOTROPIC);
522 /* font metrics on another DC should be unchanged */
523 test_font_metrics(hdc_2, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
525 /* test restrictions of compatibility mode GM_COMPATIBLE */
526 /* part 1: rescaling only X should not change font scaling on screen.
527 So compressing the X axis by 2 is not done, and this
528 appears as X scaling of 2 that no one requested. */
529 SetWindowExtEx(hdc, 100, 100, NULL);
530 SetViewportExtEx(hdc, 50, 100, NULL);
531 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 2, 1);
532 /* font metrics on another DC should be unchanged */
533 test_font_metrics(hdc_2, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
535 /* part 2: rescaling only Y should change font scaling.
536 As also X is scaled by a factor of 2, but this is not
537 requested by the DC transformation, we get a scaling factor
538 of 2 in the X coordinate. */
539 SetViewportExtEx(hdc, 100, 200, NULL);
540 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 2, 1);
541 /* font metrics on another DC should be unchanged */
542 test_font_metrics(hdc_2, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
544 /* restore scaling */
545 SetMapMode(hdc, MM_TEXT);
547 /* font metrics on another DC should be unchanged */
548 test_font_metrics(hdc_2, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
550 SelectObject(hdc_2, old_hfont_2);
551 DeleteDC(hdc_2);
553 if (!SetGraphicsMode(hdc, GM_ADVANCED))
555 SelectObject(hdc, old_hfont);
556 DeleteObject(hfont);
557 DeleteDC(hdc);
558 skip("GM_ADVANCED is not supported on this platform\n");
559 return;
562 xform.eM11 = 20.0f;
563 xform.eM12 = 0.0f;
564 xform.eM21 = 0.0f;
565 xform.eM22 = 20.0f;
566 xform.eDx = 0.0f;
567 xform.eDy = 0.0f;
569 SetLastError(0xdeadbeef);
570 ret = SetWorldTransform(hdc, &xform);
571 ok(ret, "SetWorldTransform error %u\n", GetLastError());
573 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
575 /* with an identity matrix */
576 memset(&gm, 0, sizeof(gm));
577 SetLastError(0xdeadbeef);
578 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
579 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
580 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
581 pt.x = width_orig; pt.y = 0;
582 LPtoDP(hdc, &pt, 1);
583 ok(gm.gmCellIncX == pt.x, "incX %d != %d\n", gm.gmCellIncX, pt.x);
584 ok(gm.gmCellIncX == 20 * width_orig, "incX %d != %d\n", gm.gmCellIncX, 20 * width_orig);
585 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
586 /* with a custom matrix */
587 memset(&gm, 0, sizeof(gm));
588 SetLastError(0xdeadbeef);
589 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat2);
590 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
591 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
592 pt.x = width_orig; pt.y = 0;
593 LPtoDP(hdc, &pt, 1);
594 ok(gm.gmCellIncX == pt.x/2, "incX %d != %d\n", gm.gmCellIncX, pt.x/2);
595 ok(near_match(gm.gmCellIncX, 10 * width_orig), "incX %d != %d\n", gm.gmCellIncX, 10 * width_orig);
596 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
598 SetLastError(0xdeadbeef);
599 ret = SetMapMode(hdc, MM_LOMETRIC);
600 ok(ret == MM_TEXT, "expected MM_TEXT, got %d, error %u\n", ret, GetLastError());
602 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
604 /* with an identity matrix */
605 memset(&gm, 0, sizeof(gm));
606 SetLastError(0xdeadbeef);
607 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
608 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
609 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
610 pt.x = width_orig; pt.y = 0;
611 LPtoDP(hdc, &pt, 1);
612 ok(near_match(gm.gmCellIncX, pt.x), "incX %d != %d\n", gm.gmCellIncX, pt.x);
613 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
614 /* with a custom matrix */
615 memset(&gm, 0, sizeof(gm));
616 SetLastError(0xdeadbeef);
617 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat2);
618 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
619 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
620 pt.x = width_orig; pt.y = 0;
621 LPtoDP(hdc, &pt, 1);
622 ok(near_match(gm.gmCellIncX, (pt.x + 1)/2), "incX %d != %d\n", gm.gmCellIncX, (pt.x + 1)/2);
623 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
625 SetLastError(0xdeadbeef);
626 ret = SetMapMode(hdc, MM_TEXT);
627 ok(ret == MM_LOMETRIC, "expected MM_LOMETRIC, got %d, error %u\n", ret, GetLastError());
629 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
631 /* with an identity matrix */
632 memset(&gm, 0, sizeof(gm));
633 SetLastError(0xdeadbeef);
634 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
635 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
636 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
637 pt.x = width_orig; pt.y = 0;
638 LPtoDP(hdc, &pt, 1);
639 ok(gm.gmCellIncX == pt.x, "incX %d != %d\n", gm.gmCellIncX, pt.x);
640 ok(gm.gmCellIncX == 20 * width_orig, "incX %d != %d\n", gm.gmCellIncX, 20 * width_orig);
641 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
642 /* with a custom matrix */
643 memset(&gm, 0, sizeof(gm));
644 SetLastError(0xdeadbeef);
645 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat2);
646 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
647 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
648 pt.x = width_orig; pt.y = 0;
649 LPtoDP(hdc, &pt, 1);
650 ok(gm.gmCellIncX == pt.x/2, "incX %d != %d\n", gm.gmCellIncX, pt.x/2);
651 ok(gm.gmCellIncX == 10 * width_orig, "incX %d != %d\n", gm.gmCellIncX, 10 * width_orig);
652 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
654 SelectObject(hdc, old_hfont);
655 DeleteObject(hfont);
656 DeleteDC(hdc);
659 static INT CALLBACK find_font_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
661 LOGFONT *lf = (LOGFONT *)lParam;
663 if (elf->lfHeight == lf->lfHeight && !strcmp(elf->lfFaceName, lf->lfFaceName))
665 *lf = *elf;
666 return 0; /* stop enumeration */
668 return 1; /* continue enumeration */
671 static void test_bitmap_font_metrics(void)
673 static const struct font_data
675 const char face_name[LF_FACESIZE];
676 int weight, height, ascent, descent, int_leading, ext_leading;
677 int ave_char_width, max_char_width, dpi;
678 BYTE first_char, last_char, def_char, break_char;
679 DWORD ansi_bitfield;
680 WORD skip_lang_id;
681 } fd[] =
683 { "MS Sans Serif", FW_NORMAL, 13, 11, 2, 2, 0, 5, 11, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2 },
684 { "MS Sans Serif", FW_NORMAL, 13, 11, 2, 2, 0, 5, 11, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
685 { "MS Sans Serif", FW_NORMAL, 16, 13, 3, 3, 0, 7, 14, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2 },
686 { "MS Sans Serif", FW_NORMAL, 16, 13, 3, 3, 0, 7, 14, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
687 { "MS Sans Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 16, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 },
688 { "MS Sans Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 18, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN2 },
689 { "MS Sans Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 16, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
690 { "MS Sans Serif", FW_NORMAL, 24, 19, 5, 6, 0, 9, 19, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 },
691 { "MS Sans Serif", FW_NORMAL, 24, 19, 5, 6, 0, 9, 24, 96, 0x20, 0xff, 0x81, 0x40, FS_LATIN2 },
692 { "MS Sans Serif", FW_NORMAL, 24, 19, 5, 6, 0, 9, 20, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
693 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 5, 0, 12, 24, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 },
694 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 6, 0, 12, 24, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN2 },
695 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 5, 0, 12, 25, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
696 { "MS Sans Serif", FW_NORMAL, 37, 29, 8, 5, 0, 16, 32, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 },
697 { "MS Sans Serif", FW_NORMAL, 37, 29, 8, 5, 0, 16, 32, 96, 0x20, 0xff, 0x81, 0x40, FS_LATIN2 },
698 { "MS Sans Serif", FW_NORMAL, 37, 29, 8, 5, 0, 16, 32, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
700 { "MS Sans Serif", FW_NORMAL, 16, 13, 3, 3, 0, 7, 14, 120, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2 },
701 { "MS Sans Serif", FW_NORMAL, 16, 13, 3, 3, 0, 7, 14, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
702 { "MS Sans Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 18, 120, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2 },
703 { "MS Sans Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 17, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
704 { "MS Sans Serif", FW_NORMAL, 25, 20, 5, 5, 0, 10, 21, 120, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2 },
705 { "MS Sans Serif", FW_NORMAL, 25, 20, 5, 5, 0, 10, 21, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
706 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 6, 0, 12, 24, 120, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2 },
707 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 5, 0, 12, 24, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
708 { "MS Sans Serif", FW_NORMAL, 36, 29, 7, 6, 0, 15, 30, 120, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2 },
709 { "MS Sans Serif", FW_NORMAL, 36, 29, 7, 6, 0, 15, 30, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
710 { "MS Sans Serif", FW_NORMAL, 46, 37, 9, 6, 0, 20, 40, 120, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2 },
711 { "MS Sans Serif", FW_NORMAL, 46, 37, 9, 6, 0, 20, 40, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
713 { "MS Serif", FW_NORMAL, 10, 8, 2, 2, 0, 4, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 },
714 { "MS Serif", FW_NORMAL, 10, 8, 2, 2, 0, 5, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
715 { "MS Serif", FW_NORMAL, 11, 9, 2, 2, 0, 5, 9, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
716 { "MS Serif", FW_NORMAL, 13, 11, 2, 2, 0, 5, 11, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 },
717 { "MS Serif", FW_NORMAL, 13, 11, 2, 2, 0, 5, 12, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
718 { "MS Serif", FW_NORMAL, 16, 13, 3, 3, 0, 6, 14, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 },
719 { "MS Serif", FW_NORMAL, 16, 13, 3, 3, 0, 6, 16, 96, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
720 { "MS Serif", FW_NORMAL, 19, 15, 4, 3, 0, 8, 18, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 },
721 { "MS Serif", FW_NORMAL, 19, 15, 4, 3, 0, 8, 19, 96, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
722 { "MS Serif", FW_NORMAL, 21, 16, 5, 3, 0, 9, 17, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 },
723 { "MS Serif", FW_NORMAL, 21, 16, 5, 3, 0, 9, 22, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 },
724 { "MS Serif", FW_NORMAL, 21, 16, 5, 3, 0, 9, 23, 96, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
725 { "MS Serif", FW_NORMAL, 27, 21, 6, 3, 0, 12, 23, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 },
726 { "MS Serif", FW_NORMAL, 27, 21, 6, 3, 0, 12, 26, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 },
727 { "MS Serif", FW_NORMAL, 27, 21, 6, 3, 0, 12, 27, 96, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
728 { "MS Serif", FW_NORMAL, 35, 27, 8, 3, 0, 16, 33, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 },
729 { "MS Serif", FW_NORMAL, 35, 27, 8, 3, 0, 16, 34, 96, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
731 { "MS Serif", FW_NORMAL, 16, 13, 3, 3, 0, 6, 14, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_CYRILLIC },
732 { "MS Serif", FW_NORMAL, 16, 13, 3, 3, 0, 6, 13, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 },
733 { "MS Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 18, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_CYRILLIC },
734 { "MS Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 15, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 },
735 { "MS Serif", FW_NORMAL, 23, 18, 5, 3, 0, 10, 21, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_CYRILLIC },
736 { "MS Serif", FW_NORMAL, 23, 18, 5, 3, 0, 10, 19, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 },
737 { "MS Serif", FW_NORMAL, 27, 21, 6, 4, 0, 12, 23, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 },
738 { "MS Serif", FW_MEDIUM, 27, 22, 5, 2, 0, 12, 30, 120, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
739 { "MS Serif", FW_NORMAL, 33, 26, 7, 3, 0, 14, 30, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 },
740 { "MS Serif", FW_MEDIUM, 32, 25, 7, 2, 0, 14, 32, 120, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
741 { "MS Serif", FW_NORMAL, 43, 34, 9, 3, 0, 19, 39, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
743 { "Courier", FW_NORMAL, 13, 11, 2, 0, 0, 8, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
744 { "Courier", FW_NORMAL, 16, 13, 3, 0, 0, 9, 9, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
745 { "Courier", FW_NORMAL, 20, 16, 4, 0, 0, 12, 12, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
747 { "Courier", FW_NORMAL, 16, 13, 3, 0, 0, 9, 9, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
748 { "Courier", FW_NORMAL, 20, 16, 4, 0, 0, 12, 12, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
749 { "Courier", FW_NORMAL, 25, 20, 5, 0, 0, 15, 15, 120, 0x20, 0xff, 0x40, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
751 { "System", FW_BOLD, 16, 13, 3, 3, 0, 7, 14, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 },
752 { "System", FW_BOLD, 16, 13, 3, 3, 0, 7, 15, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
753 { "System", FW_NORMAL, 18, 16, 2, 0, 2, 8, 16, 96, 0, 0, 0, 0, FS_JISJAPAN },
755 { "System", FW_BOLD, 20, 16, 4, 4, 0, 9, 14, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 },
756 { "System", FW_BOLD, 20, 16, 4, 4, 0, 9, 17, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
758 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 1, 2, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 },
759 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 1, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
760 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 2, 4, 96, 0, 0, 0, 0, FS_JISJAPAN },
761 { "Small Fonts", FW_NORMAL, 5, 4, 1, 1, 0, 3, 4, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1, LANG_ARABIC },
762 { "Small Fonts", FW_NORMAL, 5, 4, 1, 1, 0, 2, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
763 { "Small Fonts", FW_NORMAL, 5, 4, 1, 0, 0, 3, 6, 96, 0, 0, 0, 0, FS_JISJAPAN },
764 { "Small Fonts", FW_NORMAL, 6, 5, 1, 1, 0, 3, 13, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1, LANG_ARABIC },
765 { "Small Fonts", FW_NORMAL, 6, 5, 1, 1, 0, 3, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
766 { "Small Fonts", FW_NORMAL, 6, 5, 1, 1, 0, 3, 8, 96, 0, 0, 0, 0, FS_ARABIC },
767 { "Small Fonts", FW_NORMAL, 6, 5, 1, 0, 0, 4, 8, 96, 0, 0, 0, 0, FS_JISJAPAN },
768 { "Small Fonts", FW_NORMAL, 8, 7, 1, 1, 0, 4, 7, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1, LANG_ARABIC },
769 { "Small Fonts", FW_NORMAL, 8, 7, 1, 1, 0, 4, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
770 { "Small Fonts", FW_NORMAL, 8, 7, 1, 1, 0, 4, 8, 96, 0, 0, 0, 0, FS_ARABIC },
771 { "Small Fonts", FW_NORMAL, 8, 7, 1, 0, 0, 5, 10, 96, 0, 0, 0, 0, FS_JISJAPAN },
772 { "Small Fonts", FW_NORMAL, 10, 8, 2, 2, 0, 4, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2, LANG_ARABIC },
773 { "Small Fonts", FW_NORMAL, 10, 8, 2, 2, 0, 5, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
774 { "Small Fonts", FW_NORMAL, 10, 8, 2, 2, 0, 4, 9, 96, 0, 0, 0, 0, FS_ARABIC },
775 { "Small Fonts", FW_NORMAL, 10, 8, 2, 0, 0, 6, 12, 96, 0, 0, 0, 0, FS_JISJAPAN },
776 { "Small Fonts", FW_NORMAL, 11, 9, 2, 2, 0, 5, 9, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC, LANG_ARABIC },
777 { "Small Fonts", FW_NORMAL, 11, 9, 2, 2, 0, 4, 10, 96, 0, 0, 0, 0, FS_ARABIC },
778 { "Small Fonts", FW_NORMAL, 11, 9, 2, 0, 0, 7, 14, 96, 0, 0, 0, 0, FS_JISJAPAN },
780 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 1, 2, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_JISJAPAN },
781 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 1, 8, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
782 { "Small Fonts", FW_NORMAL, 6, 5, 1, 1, 0, 3, 5, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_JISJAPAN },
783 { "Small Fonts", FW_NORMAL, 6, 5, 1, 1, 0, 3, 8, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
784 { "Small Fonts", FW_NORMAL, 8, 7, 1, 1, 0, 4, 7, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_JISJAPAN },
785 { "Small Fonts", FW_NORMAL, 8, 7, 1, 1, 0, 4, 8, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
786 { "Small Fonts", FW_NORMAL, 10, 8, 2, 2, 0, 5, 9, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_JISJAPAN },
787 { "Small Fonts", FW_NORMAL, 10, 8, 2, 2, 0, 5, 8, 120, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
788 { "Small Fonts", FW_NORMAL, 12, 10, 2, 2, 0, 5, 10, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_JISJAPAN },
789 { "Small Fonts", FW_NORMAL, 12, 10, 2, 2, 0, 6, 10, 120, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
790 { "Small Fonts", FW_NORMAL, 13, 11, 2, 2, 0, 6, 12, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_JISJAPAN },
791 { "Small Fonts", FW_NORMAL, 13, 11, 2, 2, 0, 6, 11, 120, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
793 { "Fixedsys", FW_NORMAL, 15, 12, 3, 3, 0, 8, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 },
794 { "Fixedsys", FW_NORMAL, 16, 12, 4, 3, 0, 8, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
795 { "FixedSys", FW_NORMAL, 18, 16, 2, 0, 0, 8, 16, 96, 0, 0, 0, 0, FS_JISJAPAN },
797 /* The 120dpi version still has its dpi marked as 96 */
798 { "Fixedsys", FW_NORMAL, 20, 16, 4, 2, 0, 10, 10, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC }
800 /* FIXME: add "Terminal" */
802 HDC hdc;
803 LOGFONT lf;
804 HFONT hfont, old_hfont;
805 TEXTMETRIC tm;
806 INT ret, i;
807 WORD system_lang_id;
809 system_lang_id = PRIMARYLANGID(GetSystemDefaultLangID());
811 hdc = CreateCompatibleDC(0);
812 assert(hdc);
814 for (i = 0; i < sizeof(fd)/sizeof(fd[0]); i++)
816 int bit;
818 memset(&lf, 0, sizeof(lf));
820 lf.lfHeight = fd[i].height;
821 strcpy(lf.lfFaceName, fd[i].face_name);
823 for(bit = 0; bit < 32; bit++)
825 DWORD fs[2];
826 CHARSETINFO csi;
827 BOOL bRet;
829 fs[0] = 1L << bit;
830 fs[1] = 0;
831 if((fd[i].ansi_bitfield & fs[0]) == 0) continue;
832 if(!TranslateCharsetInfo( fs, &csi, TCI_SRCFONTSIG )) continue;
834 lf.lfCharSet = csi.ciCharset;
835 ret = EnumFontFamiliesEx(hdc, &lf, find_font_proc, (LPARAM)&lf, 0);
836 if (ret) continue;
838 hfont = create_font(lf.lfFaceName, &lf);
839 old_hfont = SelectObject(hdc, hfont);
840 bRet = GetTextMetrics(hdc, &tm);
841 ok(bRet, "GetTextMetrics error %d\n", GetLastError());
842 if(fd[i].dpi == tm.tmDigitizedAspectX)
844 trace("found font %s, height %d charset %x dpi %d\n", lf.lfFaceName, lf.lfHeight, lf.lfCharSet, fd[i].dpi);
845 if (fd[i].skip_lang_id == 0 || system_lang_id != fd[i].skip_lang_id)
847 ok(tm.tmWeight == fd[i].weight, "%s(%d): tm.tmWeight %d != %d\n", fd[i].face_name, fd[i].height, tm.tmWeight, fd[i].weight);
848 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);
849 ok(tm.tmAscent == fd[i].ascent, "%s(%d): tm.tmAscent %d != %d\n", fd[i].face_name, fd[i].height, tm.tmAscent, fd[i].ascent);
850 ok(tm.tmDescent == fd[i].descent, "%s(%d): tm.tmDescent %d != %d\n", fd[i].face_name, fd[i].height, tm.tmDescent, fd[i].descent);
851 ok(tm.tmInternalLeading == fd[i].int_leading, "%s(%d): tm.tmInternalLeading %d != %d\n", fd[i].face_name, fd[i].height, tm.tmInternalLeading, fd[i].int_leading);
852 ok(tm.tmExternalLeading == fd[i].ext_leading, "%s(%d): tm.tmExternalLeading %d != %d\n", fd[i].face_name, fd[i].height, tm.tmExternalLeading, fd[i].ext_leading);
853 ok(tm.tmAveCharWidth == fd[i].ave_char_width, "%s(%d): tm.tmAveCharWidth %d != %d\n", fd[i].face_name, fd[i].height, tm.tmAveCharWidth, fd[i].ave_char_width);
854 ok(tm.tmFirstChar == fd[i].first_char, "%s(%d): tm.tmFirstChar = %02x\n", fd[i].face_name, fd[i].height, tm.tmFirstChar);
855 ok(tm.tmLastChar == fd[i].last_char, "%s(%d): tm.tmLastChar = %02x\n", fd[i].face_name, fd[i].height, tm.tmLastChar);
856 ok(tm.tmDefaultChar == fd[i].def_char, "%s(%d): tm.tmDefaultChar = %02x\n", fd[i].face_name, fd[i].height, tm.tmDefaultChar);
857 ok(tm.tmBreakChar == fd[i].break_char, "%s(%d): tm.tmBreakChar = %02x\n", fd[i].face_name, fd[i].height, tm.tmBreakChar);
859 /* Don't run the max char width test on System/ANSI_CHARSET. We have extra characters in our font
860 that make the max width bigger */
861 if(strcmp(lf.lfFaceName, "System") || lf.lfCharSet != ANSI_CHARSET)
862 ok(tm.tmMaxCharWidth == fd[i].max_char_width, "%s(%d): tm.tmMaxCharWidth %d != %d\n", fd[i].face_name, fd[i].height, tm.tmMaxCharWidth, fd[i].max_char_width);
864 else
865 skip("Skipping font metrics test for system langid 0x%x\n",
866 system_lang_id);
868 SelectObject(hdc, old_hfont);
869 DeleteObject(hfont);
873 DeleteDC(hdc);
876 static void test_GdiGetCharDimensions(void)
878 HDC hdc;
879 TEXTMETRICW tm;
880 LONG ret;
881 SIZE size;
882 LONG avgwidth, height;
883 static const char szAlphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
885 if (!pGdiGetCharDimensions)
887 win_skip("GdiGetCharDimensions not available on this platform\n");
888 return;
891 hdc = CreateCompatibleDC(NULL);
893 GetTextExtentPoint(hdc, szAlphabet, strlen(szAlphabet), &size);
894 avgwidth = ((size.cx / 26) + 1) / 2;
896 ret = pGdiGetCharDimensions(hdc, &tm, &height);
897 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
898 ok(height == tm.tmHeight, "GdiGetCharDimensions should have set height to %d instead of %d\n", tm.tmHeight, height);
900 ret = pGdiGetCharDimensions(hdc, &tm, NULL);
901 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
903 ret = pGdiGetCharDimensions(hdc, NULL, NULL);
904 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
906 height = 0;
907 ret = pGdiGetCharDimensions(hdc, NULL, &height);
908 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
909 ok(height == size.cy, "GdiGetCharDimensions should have set height to %d instead of %d\n", size.cy, height);
911 DeleteDC(hdc);
914 static int CALLBACK create_font_proc(const LOGFONT *lpelfe,
915 const TEXTMETRIC *lpntme,
916 DWORD FontType, LPARAM lParam)
918 if (FontType & TRUETYPE_FONTTYPE)
920 HFONT hfont;
922 hfont = CreateFontIndirect(lpelfe);
923 if (hfont)
925 *(HFONT *)lParam = hfont;
926 return 0;
930 return 1;
933 static void test_GetCharABCWidths(void)
935 static const WCHAR str[] = {'a',0};
936 BOOL ret;
937 HDC hdc;
938 LOGFONTA lf;
939 HFONT hfont;
940 ABC abc[1];
941 ABCFLOAT abcf[1];
942 WORD glyphs[1];
943 DWORD nb;
944 static const struct
946 UINT first;
947 UINT last;
948 } range[] =
950 {0xff, 0xff},
951 {0x100, 0x100},
952 {0xff, 0x100},
953 {0x1ff, 0xff00},
954 {0xffff, 0xffff},
955 {0x10000, 0x10000},
956 {0xffff, 0x10000},
957 {0xffffff, 0xffffff},
958 {0x1000000, 0x1000000},
959 {0xffffff, 0x1000000},
960 {0xffffffff, 0xffffffff},
961 {0x00, 0xff}
963 static const struct
965 UINT cs;
966 UINT a;
967 UINT w;
968 BOOL r[sizeof range / sizeof range[0]];
969 } c[] =
971 {ANSI_CHARSET, 0x30, 0x30,
972 {TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE}},
973 {SHIFTJIS_CHARSET, 0x82a0, 0x3042,
974 {TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE}},
975 {HANGEUL_CHARSET, 0x8141, 0xac02,
976 {TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE}},
977 {JOHAB_CHARSET, 0x8446, 0x3135,
978 {TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE}},
979 {GB2312_CHARSET, 0x8141, 0x4e04,
980 {TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE}},
981 {CHINESEBIG5_CHARSET, 0xa142, 0x3001,
982 {TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE}}
984 UINT i;
986 if (!pGetCharABCWidthsA || !pGetCharABCWidthsW || !pGetCharABCWidthsFloatW || !pGetCharABCWidthsI)
988 win_skip("GetCharABCWidthsA/W/I not available on this platform\n");
989 return;
992 memset(&lf, 0, sizeof(lf));
993 strcpy(lf.lfFaceName, "System");
994 lf.lfHeight = 20;
996 hfont = CreateFontIndirectA(&lf);
997 hdc = GetDC(0);
998 hfont = SelectObject(hdc, hfont);
1000 nb = pGetGlyphIndicesW(hdc, str, 1, glyphs, 0);
1001 ok(nb == 1, "GetGlyphIndicesW should have returned 1\n");
1003 ret = pGetCharABCWidthsI(NULL, 0, 1, glyphs, abc);
1004 ok(!ret, "GetCharABCWidthsI should have failed\n");
1006 ret = pGetCharABCWidthsI(hdc, 0, 1, glyphs, NULL);
1007 ok(!ret, "GetCharABCWidthsI should have failed\n");
1009 ret = pGetCharABCWidthsI(hdc, 0, 1, glyphs, abc);
1010 ok(ret, "GetCharABCWidthsI should have succeeded\n");
1012 ret = pGetCharABCWidthsW(NULL, 'a', 'a', abc);
1013 ok(!ret, "GetCharABCWidthsW should have failed\n");
1015 ret = pGetCharABCWidthsW(hdc, 'a', 'a', NULL);
1016 ok(!ret, "GetCharABCWidthsW should have failed\n");
1018 ret = pGetCharABCWidthsW(hdc, 'a', 'a', abc);
1019 ok(!ret, "GetCharABCWidthsW should have failed\n");
1021 ret = pGetCharABCWidthsFloatW(NULL, 'a', 'a', abcf);
1022 ok(!ret, "GetCharABCWidthsFloatW should have failed\n");
1024 ret = pGetCharABCWidthsFloatW(hdc, 'a', 'a', NULL);
1025 ok(!ret, "GetCharABCWidthsFloatW should have failed\n");
1027 ret = pGetCharABCWidthsFloatW(hdc, 'a', 'a', abcf);
1028 ok(ret, "GetCharABCWidthsFloatW should have succeeded\n");
1030 hfont = SelectObject(hdc, hfont);
1031 DeleteObject(hfont);
1033 for (i = 0; i < sizeof c / sizeof c[0]; ++i)
1035 ABC a[2], w[2];
1036 ABC full[256];
1037 UINT code = 0x41, j;
1039 lf.lfFaceName[0] = '\0';
1040 lf.lfCharSet = c[i].cs;
1041 lf.lfPitchAndFamily = 0;
1042 if (EnumFontFamiliesEx(hdc, &lf, create_font_proc, (LPARAM)&hfont, 0))
1044 skip("TrueType font for charset %u is not installed\n", c[i].cs);
1045 continue;
1048 memset(a, 0, sizeof a);
1049 memset(w, 0, sizeof w);
1050 hfont = SelectObject(hdc, hfont);
1051 ok(pGetCharABCWidthsA(hdc, c[i].a, c[i].a + 1, a) &&
1052 pGetCharABCWidthsW(hdc, c[i].w, c[i].w + 1, w) &&
1053 memcmp(a, w, sizeof a) == 0,
1054 "GetCharABCWidthsA and GetCharABCWidthsW should return same widths. charset = %u\n", c[i].cs);
1056 memset(a, 0xbb, sizeof a);
1057 ret = pGetCharABCWidthsA(hdc, code, code, a);
1058 ok(ret, "GetCharABCWidthsA should have succeeded\n");
1059 memset(full, 0xcc, sizeof full);
1060 ret = pGetCharABCWidthsA(hdc, 0x00, code, full);
1061 ok(ret, "GetCharABCWidthsA should have succeeded\n");
1062 ok(memcmp(&a[0], &full[code], sizeof(ABC)) == 0,
1063 "GetCharABCWidthsA info should match. codepage = %u\n", c[i].cs);
1065 for (j = 0; j < sizeof range / sizeof range[0]; ++j)
1067 memset(full, 0xdd, sizeof full);
1068 ret = pGetCharABCWidthsA(hdc, range[j].first, range[j].last, full);
1069 ok(ret == c[i].r[j], "GetCharABCWidthsA %x - %x should have %s\n",
1070 range[j].first, range[j].last, c[i].r[j] ? "succeeded" : "failed");
1071 if (ret)
1073 UINT last = range[j].last - range[j].first;
1074 ret = pGetCharABCWidthsA(hdc, range[j].last, range[j].last, a);
1075 ok(ret && memcmp(&full[last], &a[0], sizeof(ABC)) == 0,
1076 "GetCharABCWidthsA %x should match. codepage = %u\n",
1077 range[j].last, c[i].cs);
1081 hfont = SelectObject(hdc, hfont);
1082 DeleteObject(hfont);
1085 ReleaseDC(NULL, hdc);
1088 static void test_text_extents(void)
1090 static const WCHAR wt[] = {'O','n','e','\n','t','w','o',' ','3',0};
1091 LPINT extents;
1092 INT i, len, fit1, fit2;
1093 LOGFONTA lf;
1094 TEXTMETRICA tm;
1095 HDC hdc;
1096 HFONT hfont;
1097 SIZE sz;
1098 SIZE sz1, sz2;
1099 BOOL ret;
1101 memset(&lf, 0, sizeof(lf));
1102 strcpy(lf.lfFaceName, "Arial");
1103 lf.lfHeight = 20;
1105 hfont = CreateFontIndirectA(&lf);
1106 hdc = GetDC(0);
1107 hfont = SelectObject(hdc, hfont);
1108 GetTextMetricsA(hdc, &tm);
1109 GetTextExtentPointA(hdc, "o", 1, &sz);
1110 ok(sz.cy == tm.tmHeight, "cy %d tmHeight %d\n", sz.cy, tm.tmHeight);
1112 SetLastError(0xdeadbeef);
1113 GetTextExtentExPointW(hdc, wt, 1, 1, &fit1, &fit2, &sz1);
1114 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
1116 win_skip("Skipping remainder of text extents test on a Win9x platform\n");
1117 hfont = SelectObject(hdc, hfont);
1118 DeleteObject(hfont);
1119 ReleaseDC(0, hdc);
1120 return;
1123 len = lstrlenW(wt);
1124 extents = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof extents[0]);
1125 extents[0] = 1; /* So that the increasing sequence test will fail
1126 if the extents array is untouched. */
1127 GetTextExtentExPointW(hdc, wt, len, 32767, &fit1, extents, &sz1);
1128 GetTextExtentPointW(hdc, wt, len, &sz2);
1129 ok(sz1.cy == sz2.cy,
1130 "cy from GetTextExtentExPointW (%d) and GetTextExtentPointW (%d) differ\n", sz1.cy, sz2.cy);
1131 /* Because of the '\n' in the string GetTextExtentExPoint and
1132 GetTextExtentPoint return different widths under Win2k, but
1133 under WinXP they return the same width. So we don't test that
1134 here. */
1136 for (i = 1; i < len; ++i)
1137 ok(extents[i-1] <= extents[i],
1138 "GetTextExtentExPointW generated a non-increasing sequence of partial extents (at position %d)\n",
1140 ok(extents[len-1] == sz1.cx, "GetTextExtentExPointW extents and size don't match\n");
1141 ok(0 <= fit1 && fit1 <= len, "GetTextExtentExPointW generated illegal value %d for fit\n", fit1);
1142 ok(0 < fit1, "GetTextExtentExPointW says we can't even fit one letter in 32767 logical units\n");
1143 GetTextExtentExPointW(hdc, wt, len, extents[2], &fit2, NULL, &sz2);
1144 ok(sz1.cx == sz2.cx && sz1.cy == sz2.cy, "GetTextExtentExPointW returned different sizes for the same string\n");
1145 ok(fit2 == 3, "GetTextExtentExPointW extents isn't consistent with fit\n");
1146 GetTextExtentExPointW(hdc, wt, len, extents[2]-1, &fit2, NULL, &sz2);
1147 ok(fit2 == 2, "GetTextExtentExPointW extents isn't consistent with fit\n");
1148 GetTextExtentExPointW(hdc, wt, 2, 0, NULL, extents + 2, &sz2);
1149 ok(extents[0] == extents[2] && extents[1] == extents[3],
1150 "GetTextExtentExPointW with lpnFit == NULL returns incorrect results\n");
1151 GetTextExtentExPointW(hdc, wt, 2, 0, NULL, NULL, &sz1);
1152 ok(sz1.cx == sz2.cx && sz1.cy == sz2.cy,
1153 "GetTextExtentExPointW with lpnFit and alpDx both NULL returns incorrect results\n");
1154 HeapFree(GetProcessHeap(), 0, extents);
1156 /* extents functions fail with -ve counts (the interesting case being -1) */
1157 ret = GetTextExtentPointA(hdc, "o", -1, &sz);
1158 ok(ret == FALSE, "got %d\n", ret);
1159 ret = GetTextExtentExPointA(hdc, "o", -1, 0, NULL, NULL, &sz);
1160 ok(ret == FALSE, "got %d\n", ret);
1161 ret = GetTextExtentExPointW(hdc, wt, -1, 0, NULL, NULL, &sz1);
1162 ok(ret == FALSE, "got %d\n", ret);
1164 /* max_extent = 0 succeeds and returns zero */
1165 fit1 = fit2 = -215;
1166 ret = GetTextExtentExPointA(hdc, NULL, 0, 0, &fit1, NULL, &sz);
1167 ok(ret == TRUE ||
1168 broken(ret == FALSE), /* NT4, 2k */
1169 "got %d\n", ret);
1170 ok(fit1 == 0 ||
1171 broken(fit1 == -215), /* NT4, 2k */
1172 "fit = %d\n", fit1);
1173 ret = GetTextExtentExPointW(hdc, NULL, 0, 0, &fit2, NULL, &sz1);
1174 ok(ret == TRUE, "got %d\n", ret);
1175 ok(fit2 == 0, "fit = %d\n", fit2);
1177 /* max_extent = -1 is interpreted as a very large width that will
1178 * definitely fit our three characters */
1179 fit1 = fit2 = -215;
1180 ret = GetTextExtentExPointA(hdc, "One", 3, -1, &fit1, NULL, &sz);
1181 ok(ret == TRUE, "got %d\n", ret);
1182 todo_wine ok(fit1 == 3, "fit = %d\n", fit1);
1183 ret = GetTextExtentExPointW(hdc, wt, 3, -1, &fit2, NULL, &sz);
1184 ok(ret == TRUE, "got %d\n", ret);
1185 todo_wine ok(fit2 == 3, "fit = %d\n", fit2);
1187 /* max_extent = -2 is interpreted similarly, but the Ansi version
1188 * rejects it while the Unicode one accepts it */
1189 fit1 = fit2 = -215;
1190 ret = GetTextExtentExPointA(hdc, "One", 3, -2, &fit1, NULL, &sz);
1191 todo_wine ok(ret == FALSE, "got %d\n", ret);
1192 todo_wine ok(fit1 == -215, "fit = %d\n", fit1);
1193 ret = GetTextExtentExPointW(hdc, wt, 3, -2, &fit2, NULL, &sz);
1194 ok(ret == TRUE, "got %d\n", ret);
1195 todo_wine ok(fit2 == 3, "fit = %d\n", fit2);
1197 hfont = SelectObject(hdc, hfont);
1198 DeleteObject(hfont);
1199 ReleaseDC(NULL, hdc);
1202 static void test_GetGlyphIndices(void)
1204 HDC hdc;
1205 HFONT hfont;
1206 DWORD charcount;
1207 LOGFONTA lf;
1208 DWORD flags = 0;
1209 WCHAR testtext[] = {'T','e','s','t',0xffff,0};
1210 WORD glyphs[(sizeof(testtext)/2)-1];
1211 TEXTMETRIC textm;
1212 HFONT hOldFont;
1214 if (!pGetGlyphIndicesW) {
1215 win_skip("GetGlyphIndicesW not available on platform\n");
1216 return;
1219 hdc = GetDC(0);
1221 memset(&lf, 0, sizeof(lf));
1222 strcpy(lf.lfFaceName, "System");
1223 lf.lfHeight = 16;
1224 lf.lfCharSet = ANSI_CHARSET;
1226 hfont = CreateFontIndirectA(&lf);
1227 ok(hfont != 0, "CreateFontIndirectEx failed\n");
1228 ok(GetTextMetrics(hdc, &textm), "GetTextMetric failed\n");
1229 if (textm.tmCharSet == ANSI_CHARSET)
1231 flags |= GGI_MARK_NONEXISTING_GLYPHS;
1232 charcount = pGetGlyphIndicesW(hdc, testtext, (sizeof(testtext)/2)-1, glyphs, flags);
1233 ok(charcount == 5, "GetGlyphIndicesW count of glyphs should = 5 not %d\n", charcount);
1234 ok((glyphs[4] == 0x001f || glyphs[4] == 0xffff /* Vista */), "GetGlyphIndicesW should have returned a nonexistent char not %04x\n", glyphs[4]);
1235 flags = 0;
1236 charcount = pGetGlyphIndicesW(hdc, testtext, (sizeof(testtext)/2)-1, glyphs, flags);
1237 ok(charcount == 5, "GetGlyphIndicesW count of glyphs should = 5 not %d\n", charcount);
1238 ok(glyphs[4] == textm.tmDefaultChar, "GetGlyphIndicesW should have returned a %04x not %04x\n",
1239 textm.tmDefaultChar, glyphs[4]);
1241 else
1242 /* FIXME: Write tests for non-ANSI charsets. */
1243 skip("GetGlyphIndices System font tests only for ANSI_CHARSET\n");
1245 if(!is_font_installed("Tahoma"))
1247 skip("Tahoma is not installed so skipping this test\n");
1248 return;
1250 memset(&lf, 0, sizeof(lf));
1251 strcpy(lf.lfFaceName, "Tahoma");
1252 lf.lfHeight = 20;
1254 hfont = CreateFontIndirectA(&lf);
1255 hOldFont = SelectObject(hdc, hfont);
1256 ok(GetTextMetrics(hdc, &textm), "GetTextMetric failed\n");
1257 flags |= GGI_MARK_NONEXISTING_GLYPHS;
1258 charcount = pGetGlyphIndicesW(hdc, testtext, (sizeof(testtext)/2)-1, glyphs, flags);
1259 ok(charcount == 5, "GetGlyphIndicesW count of glyphs should = 5 not %d\n", charcount);
1260 ok(glyphs[4] == 0xffff, "GetGlyphIndicesW should have returned 0xffff char not %04x\n", glyphs[4]);
1261 flags = 0;
1262 testtext[0] = textm.tmDefaultChar;
1263 charcount = pGetGlyphIndicesW(hdc, testtext, (sizeof(testtext)/2)-1, glyphs, flags);
1264 ok(charcount == 5, "GetGlyphIndicesW count of glyphs should = 5 not %d\n", charcount);
1265 ok(glyphs[0] == 0, "GetGlyphIndicesW for tmDefaultChar should be 0 not %04x\n", glyphs[0]);
1266 ok(glyphs[4] == 0, "GetGlyphIndicesW should have returned 0 not %04x\n", glyphs[4]);
1267 DeleteObject(SelectObject(hdc, hOldFont));
1270 static void test_GetKerningPairs(void)
1272 static const struct kerning_data
1274 const char face_name[LF_FACESIZE];
1275 LONG height;
1276 /* some interesting fields from OUTLINETEXTMETRIC */
1277 LONG tmHeight, tmAscent, tmDescent;
1278 UINT otmEMSquare;
1279 INT otmAscent;
1280 INT otmDescent;
1281 UINT otmLineGap;
1282 UINT otmsCapEmHeight;
1283 UINT otmsXHeight;
1284 INT otmMacAscent;
1285 INT otmMacDescent;
1286 UINT otmMacLineGap;
1287 UINT otmusMinimumPPEM;
1288 /* small subset of kerning pairs to test */
1289 DWORD total_kern_pairs;
1290 const KERNINGPAIR kern_pair[26];
1291 } kd[] =
1293 {"Arial", 12, 12, 9, 3,
1294 2048, 7, -2, 1, 5, 2, 8, -2, 0, 9,
1297 {' ','A',-1},{' ','T',0},{' ','Y',0},{'1','1',-1},
1298 {'A',' ',-1},{'A','T',-1},{'A','V',-1},{'A','W',0},
1299 {'A','Y',-1},{'A','v',0},{'A','w',0},{'A','y',0},
1300 {'F',',',-1},{'F','.',-1},{'F','A',-1},{'L',' ',0},
1301 {'L','T',-1},{'L','V',-1},{'L','W',-1},{'L','Y',-1},
1302 {915,912,+1},{915,913,-1},{910,912,+1},{910,913,-1},
1303 {933,970,+1},{933,972,-1}
1306 {"Arial", -34, 39, 32, 7,
1307 2048, 25, -7, 5, 17, 9, 31, -7, 1, 9,
1310 {' ','A',-2},{' ','T',-1},{' ','Y',-1},{'1','1',-3},
1311 {'A',' ',-2},{'A','T',-3},{'A','V',-3},{'A','W',-1},
1312 {'A','Y',-3},{'A','v',-1},{'A','w',-1},{'A','y',-1},
1313 {'F',',',-4},{'F','.',-4},{'F','A',-2},{'L',' ',-1},
1314 {'L','T',-3},{'L','V',-3},{'L','W',-3},{'L','Y',-3},
1315 {915,912,+3},{915,913,-3},{910,912,+3},{910,913,-3},
1316 {933,970,+2},{933,972,-3}
1319 { "Arial", 120, 120, 97, 23,
1320 2048, 79, -23, 16, 54, 27, 98, -23, 4, 9,
1323 {' ','A',-6},{' ','T',-2},{' ','Y',-2},{'1','1',-8},
1324 {'A',' ',-6},{'A','T',-8},{'A','V',-8},{'A','W',-4},
1325 {'A','Y',-8},{'A','v',-2},{'A','w',-2},{'A','y',-2},
1326 {'F',',',-12},{'F','.',-12},{'F','A',-6},{'L',' ',-4},
1327 {'L','T',-8},{'L','V',-8},{'L','W',-8},{'L','Y',-8},
1328 {915,912,+9},{915,913,-10},{910,912,+9},{910,913,-8},
1329 {933,970,+6},{933,972,-10}
1332 #if 0 /* this set fails due to +1/-1 errors (rounding bug?), needs investigation. */
1333 { "Arial", 1024 /* usually 1/2 of EM Square */, 1024, 830, 194,
1334 2048, 668, -193, 137, 459, 229, 830, -194, 30, 9,
1337 {' ','A',-51},{' ','T',-17},{' ','Y',-17},{'1','1',-68},
1338 {'A',' ',-51},{'A','T',-68},{'A','V',-68},{'A','W',-34},
1339 {'A','Y',-68},{'A','v',-17},{'A','w',-17},{'A','y',-17},
1340 {'F',',',-102},{'F','.',-102},{'F','A',-51},{'L',' ',-34},
1341 {'L','T',-68},{'L','V',-68},{'L','W',-68},{'L','Y',-68},
1342 {915,912,+73},{915,913,-84},{910,912,+76},{910,913,-68},
1343 {933,970,+54},{933,972,-83}
1346 #endif
1348 LOGFONT lf;
1349 HFONT hfont, hfont_old;
1350 KERNINGPAIR *kern_pair;
1351 HDC hdc;
1352 DWORD total_kern_pairs, ret, i, n, matches;
1354 hdc = GetDC(0);
1356 /* GetKerningPairsA maps unicode set of kerning pairs to current code page
1357 * which may render this test unusable, so we're trying to avoid that.
1359 SetLastError(0xdeadbeef);
1360 GetKerningPairsW(hdc, 0, NULL);
1361 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
1363 win_skip("Skipping the GetKerningPairs test on a Win9x platform\n");
1364 ReleaseDC(0, hdc);
1365 return;
1368 for (i = 0; i < sizeof(kd)/sizeof(kd[0]); i++)
1370 OUTLINETEXTMETRICW otm;
1371 UINT uiRet;
1373 if (!is_font_installed(kd[i].face_name))
1375 trace("%s is not installed so skipping this test\n", kd[i].face_name);
1376 continue;
1379 trace("testing font %s, height %d\n", kd[i].face_name, kd[i].height);
1381 memset(&lf, 0, sizeof(lf));
1382 strcpy(lf.lfFaceName, kd[i].face_name);
1383 lf.lfHeight = kd[i].height;
1384 hfont = CreateFontIndirect(&lf);
1385 assert(hfont != 0);
1387 hfont_old = SelectObject(hdc, hfont);
1389 SetLastError(0xdeadbeef);
1390 otm.otmSize = sizeof(otm); /* just in case for Win9x compatibility */
1391 uiRet = GetOutlineTextMetricsW(hdc, sizeof(otm), &otm);
1392 ok(uiRet == sizeof(otm), "GetOutlineTextMetricsW error %d\n", GetLastError());
1394 ok(match_off_by_1(kd[i].tmHeight, otm.otmTextMetrics.tmHeight), "expected %d, got %d\n",
1395 kd[i].tmHeight, otm.otmTextMetrics.tmHeight);
1396 ok(match_off_by_1(kd[i].tmAscent, otm.otmTextMetrics.tmAscent), "expected %d, got %d\n",
1397 kd[i].tmAscent, otm.otmTextMetrics.tmAscent);
1398 ok(kd[i].tmDescent == otm.otmTextMetrics.tmDescent, "expected %d, got %d\n",
1399 kd[i].tmDescent, otm.otmTextMetrics.tmDescent);
1401 ok(kd[i].otmEMSquare == otm.otmEMSquare, "expected %u, got %u\n",
1402 kd[i].otmEMSquare, otm.otmEMSquare);
1403 ok(kd[i].otmAscent == otm.otmAscent, "expected %d, got %d\n",
1404 kd[i].otmAscent, otm.otmAscent);
1405 ok(kd[i].otmDescent == otm.otmDescent, "expected %d, got %d\n",
1406 kd[i].otmDescent, otm.otmDescent);
1407 ok(kd[i].otmLineGap == otm.otmLineGap, "expected %u, got %u\n",
1408 kd[i].otmLineGap, otm.otmLineGap);
1409 ok(near_match(kd[i].otmMacDescent, otm.otmMacDescent), "expected %d, got %d\n",
1410 kd[i].otmMacDescent, otm.otmMacDescent);
1411 ok(near_match(kd[i].otmMacAscent, otm.otmMacAscent), "expected %d, got %d\n",
1412 kd[i].otmMacAscent, otm.otmMacAscent);
1413 todo_wine {
1414 ok(kd[i].otmsCapEmHeight == otm.otmsCapEmHeight, "expected %u, got %u\n",
1415 kd[i].otmsCapEmHeight, otm.otmsCapEmHeight);
1416 ok(kd[i].otmsXHeight == otm.otmsXHeight, "expected %u, got %u\n",
1417 kd[i].otmsXHeight, otm.otmsXHeight);
1418 /* FIXME: this one sometimes succeeds due to expected 0, enable it when removing todo */
1419 if (0) ok(kd[i].otmMacLineGap == otm.otmMacLineGap, "expected %u, got %u\n",
1420 kd[i].otmMacLineGap, otm.otmMacLineGap);
1421 ok(kd[i].otmusMinimumPPEM == otm.otmusMinimumPPEM, "expected %u, got %u\n",
1422 kd[i].otmusMinimumPPEM, otm.otmusMinimumPPEM);
1425 total_kern_pairs = GetKerningPairsW(hdc, 0, NULL);
1426 trace("total_kern_pairs %u\n", total_kern_pairs);
1427 kern_pair = HeapAlloc(GetProcessHeap(), 0, total_kern_pairs * sizeof(*kern_pair));
1429 /* Win98 (GetKerningPairsA) and XP behave differently here, the test
1430 * passes on XP.
1432 SetLastError(0xdeadbeef);
1433 ret = GetKerningPairsW(hdc, 0, kern_pair);
1434 ok(GetLastError() == ERROR_INVALID_PARAMETER,
1435 "got error %u, expected ERROR_INVALID_PARAMETER\n", GetLastError());
1436 ok(ret == 0, "got %u, expected 0\n", ret);
1438 ret = GetKerningPairsW(hdc, 100, NULL);
1439 ok(ret == total_kern_pairs, "got %u, expected %u\n", ret, total_kern_pairs);
1441 ret = GetKerningPairsW(hdc, total_kern_pairs/2, kern_pair);
1442 ok(ret == total_kern_pairs/2, "got %u, expected %u\n", ret, total_kern_pairs/2);
1444 ret = GetKerningPairsW(hdc, total_kern_pairs, kern_pair);
1445 ok(ret == total_kern_pairs, "got %u, expected %u\n", ret, total_kern_pairs);
1447 matches = 0;
1449 for (n = 0; n < ret; n++)
1451 DWORD j;
1452 /* Disabled to limit console spam */
1453 if (0 && kern_pair[n].wFirst < 127 && kern_pair[n].wSecond < 127)
1454 trace("{'%c','%c',%d},\n",
1455 kern_pair[n].wFirst, kern_pair[n].wSecond, kern_pair[n].iKernAmount);
1456 for (j = 0; j < kd[i].total_kern_pairs; j++)
1458 if (kern_pair[n].wFirst == kd[i].kern_pair[j].wFirst &&
1459 kern_pair[n].wSecond == kd[i].kern_pair[j].wSecond)
1461 ok(kern_pair[n].iKernAmount == kd[i].kern_pair[j].iKernAmount,
1462 "pair %d:%d got %d, expected %d\n",
1463 kern_pair[n].wFirst, kern_pair[n].wSecond,
1464 kern_pair[n].iKernAmount, kd[i].kern_pair[j].iKernAmount);
1465 matches++;
1470 ok(matches == kd[i].total_kern_pairs, "got matches %u, expected %u\n",
1471 matches, kd[i].total_kern_pairs);
1473 HeapFree(GetProcessHeap(), 0, kern_pair);
1475 SelectObject(hdc, hfont_old);
1476 DeleteObject(hfont);
1479 ReleaseDC(0, hdc);
1482 static void test_height_selection(void)
1484 static const struct font_data
1486 const char face_name[LF_FACESIZE];
1487 int requested_height;
1488 int weight, height, ascent, descent, int_leading, ext_leading, dpi;
1489 } fd[] =
1491 {"Tahoma", -12, FW_NORMAL, 14, 12, 2, 2, 0, 96 },
1492 {"Tahoma", -24, FW_NORMAL, 29, 24, 5, 5, 0, 96 },
1493 {"Tahoma", -48, FW_NORMAL, 58, 48, 10, 10, 0, 96 },
1494 {"Tahoma", -96, FW_NORMAL, 116, 96, 20, 20, 0, 96 },
1495 {"Tahoma", -192, FW_NORMAL, 232, 192, 40, 40, 0, 96 },
1496 {"Tahoma", 12, FW_NORMAL, 12, 10, 2, 2, 0, 96 },
1497 {"Tahoma", 24, FW_NORMAL, 24, 20, 4, 4, 0, 96 },
1498 {"Tahoma", 48, FW_NORMAL, 48, 40, 8, 8, 0, 96 },
1499 {"Tahoma", 96, FW_NORMAL, 96, 80, 16, 17, 0, 96 },
1500 {"Tahoma", 192, FW_NORMAL, 192, 159, 33, 33, 0, 96 }
1502 HDC hdc;
1503 LOGFONT lf;
1504 HFONT hfont, old_hfont;
1505 TEXTMETRIC tm;
1506 INT ret, i;
1508 hdc = CreateCompatibleDC(0);
1509 assert(hdc);
1511 for (i = 0; i < sizeof(fd)/sizeof(fd[0]); i++)
1513 if (!is_truetype_font_installed(fd[i].face_name))
1515 skip("%s is not installed\n", fd[i].face_name);
1516 continue;
1519 memset(&lf, 0, sizeof(lf));
1520 lf.lfHeight = fd[i].requested_height;
1521 lf.lfWeight = fd[i].weight;
1522 strcpy(lf.lfFaceName, fd[i].face_name);
1524 hfont = CreateFontIndirect(&lf);
1525 assert(hfont);
1527 old_hfont = SelectObject(hdc, hfont);
1528 ret = GetTextMetrics(hdc, &tm);
1529 ok(ret, "GetTextMetrics error %d\n", GetLastError());
1530 if(fd[i].dpi == tm.tmDigitizedAspectX)
1532 trace("found font %s, height %d charset %x dpi %d\n", lf.lfFaceName, lf.lfHeight, lf.lfCharSet, fd[i].dpi);
1533 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);
1534 ok(match_off_by_1(tm.tmHeight, fd[i].height), "%s(%d): tm.tmHeight %d != %d\n", fd[i].face_name, fd[i].requested_height, tm.tmHeight, fd[i].height);
1535 ok(match_off_by_1(tm.tmAscent, fd[i].ascent), "%s(%d): tm.tmAscent %d != %d\n", fd[i].face_name, fd[i].requested_height, tm.tmAscent, fd[i].ascent);
1536 ok(match_off_by_1(tm.tmDescent, fd[i].descent), "%s(%d): tm.tmDescent %d != %d\n", fd[i].face_name, fd[i].requested_height, tm.tmDescent, fd[i].descent);
1537 #if 0 /* FIXME: calculation of tmInternalLeading in Wine doesn't match what Windows does */
1538 ok(tm.tmInternalLeading == fd[i].int_leading, "%s(%d): tm.tmInternalLeading %d != %d\n", fd[i].face_name, fd[i].requested_height, tm.tmInternalLeading, fd[i].int_leading);
1539 #endif
1540 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);
1543 SelectObject(hdc, old_hfont);
1544 DeleteObject(hfont);
1547 DeleteDC(hdc);
1550 static void test_GetOutlineTextMetrics(void)
1552 OUTLINETEXTMETRIC *otm;
1553 LOGFONT lf;
1554 HFONT hfont, hfont_old;
1555 HDC hdc;
1556 DWORD ret, otm_size;
1557 LPSTR unset_ptr;
1559 if (!is_font_installed("Arial"))
1561 skip("Arial is not installed\n");
1562 return;
1565 hdc = GetDC(0);
1567 memset(&lf, 0, sizeof(lf));
1568 strcpy(lf.lfFaceName, "Arial");
1569 lf.lfHeight = -13;
1570 lf.lfWeight = FW_NORMAL;
1571 lf.lfPitchAndFamily = DEFAULT_PITCH;
1572 lf.lfQuality = PROOF_QUALITY;
1573 hfont = CreateFontIndirect(&lf);
1574 assert(hfont != 0);
1576 hfont_old = SelectObject(hdc, hfont);
1577 otm_size = GetOutlineTextMetrics(hdc, 0, NULL);
1578 trace("otm buffer size %u (0x%x)\n", otm_size, otm_size);
1580 otm = HeapAlloc(GetProcessHeap(), 0, otm_size);
1582 memset(otm, 0xAA, otm_size);
1583 SetLastError(0xdeadbeef);
1584 otm->otmSize = sizeof(*otm); /* just in case for Win9x compatibility */
1585 ret = GetOutlineTextMetrics(hdc, otm->otmSize, otm);
1586 ok(ret == 1 /* Win9x */ ||
1587 ret == otm->otmSize /* XP*/,
1588 "expected %u, got %u, error %d\n", otm->otmSize, ret, GetLastError());
1589 if (ret != 1) /* Win9x doesn't care about pointing beyond of the buffer */
1591 ok(otm->otmpFamilyName == NULL, "expected NULL got %p\n", otm->otmpFamilyName);
1592 ok(otm->otmpFaceName == NULL, "expected NULL got %p\n", otm->otmpFaceName);
1593 ok(otm->otmpStyleName == NULL, "expected NULL got %p\n", otm->otmpStyleName);
1594 ok(otm->otmpFullName == NULL, "expected NULL got %p\n", otm->otmpFullName);
1597 memset(otm, 0xAA, otm_size);
1598 SetLastError(0xdeadbeef);
1599 otm->otmSize = otm_size; /* just in case for Win9x compatibility */
1600 ret = GetOutlineTextMetrics(hdc, otm->otmSize, otm);
1601 ok(ret == 1 /* Win9x */ ||
1602 ret == otm->otmSize /* XP*/,
1603 "expected %u, got %u, error %d\n", otm->otmSize, ret, GetLastError());
1604 if (ret != 1) /* Win9x doesn't care about pointing beyond of the buffer */
1606 ok(otm->otmpFamilyName != NULL, "expected not NULL got %p\n", otm->otmpFamilyName);
1607 ok(otm->otmpFaceName != NULL, "expected not NULL got %p\n", otm->otmpFaceName);
1608 ok(otm->otmpStyleName != NULL, "expected not NULL got %p\n", otm->otmpStyleName);
1609 ok(otm->otmpFullName != NULL, "expected not NULL got %p\n", otm->otmpFullName);
1612 /* ask about truncated data */
1613 memset(otm, 0xAA, otm_size);
1614 memset(&unset_ptr, 0xAA, sizeof(unset_ptr));
1615 SetLastError(0xdeadbeef);
1616 otm->otmSize = sizeof(*otm) - sizeof(LPSTR); /* just in case for Win9x compatibility */
1617 ret = GetOutlineTextMetrics(hdc, otm->otmSize, otm);
1618 ok(ret == 1 /* Win9x */ ||
1619 ret == otm->otmSize /* XP*/,
1620 "expected %u, got %u, error %d\n", otm->otmSize, ret, GetLastError());
1621 if (ret != 1) /* Win9x doesn't care about pointing beyond of the buffer */
1623 ok(otm->otmpFamilyName == NULL, "expected NULL got %p\n", otm->otmpFamilyName);
1624 ok(otm->otmpFaceName == NULL, "expected NULL got %p\n", otm->otmpFaceName);
1625 ok(otm->otmpStyleName == NULL, "expected NULL got %p\n", otm->otmpStyleName);
1627 ok(otm->otmpFullName == unset_ptr, "expected %p got %p\n", unset_ptr, otm->otmpFullName);
1629 HeapFree(GetProcessHeap(), 0, otm);
1631 SelectObject(hdc, hfont_old);
1632 DeleteObject(hfont);
1634 ReleaseDC(0, hdc);
1637 static void testJustification(HDC hdc, PSTR str, RECT *clientArea)
1639 INT y,
1640 breakCount,
1641 justifiedWidth = 0, /* to test GetTextExtentExPointW() */
1642 areaWidth = clientArea->right - clientArea->left,
1643 nErrors = 0, e;
1644 BOOL lastExtent = FALSE;
1645 PSTR pFirstChar, pLastChar;
1646 SIZE size;
1647 TEXTMETRICA tm;
1648 struct err
1650 char extent[100];
1651 int GetTextExtentExPointWWidth;
1652 } error[10];
1654 GetTextMetricsA(hdc, &tm);
1655 y = clientArea->top;
1656 do {
1657 breakCount = 0;
1658 while (*str == tm.tmBreakChar) str++; /* skip leading break chars */
1659 pFirstChar = str;
1661 do {
1662 pLastChar = str;
1664 /* if not at the end of the string, ... */
1665 if (*str == '\0') break;
1666 /* ... add the next word to the current extent */
1667 while (*str != '\0' && *str++ != tm.tmBreakChar);
1668 breakCount++;
1669 SetTextJustification(hdc, 0, 0);
1670 GetTextExtentPoint32(hdc, pFirstChar, str - pFirstChar - 1, &size);
1671 } while ((int) size.cx < areaWidth);
1673 /* ignore trailing break chars */
1674 breakCount--;
1675 while (*(pLastChar - 1) == tm.tmBreakChar)
1677 pLastChar--;
1678 breakCount--;
1681 if (*str == '\0' || breakCount <= 0) pLastChar = str;
1683 SetTextJustification(hdc, 0, 0);
1684 GetTextExtentPoint32(hdc, pFirstChar, pLastChar - pFirstChar, &size);
1686 /* do not justify the last extent */
1687 if (*str != '\0' && breakCount > 0)
1689 SetTextJustification(hdc, areaWidth - size.cx, breakCount);
1690 GetTextExtentPoint32(hdc, pFirstChar, pLastChar - pFirstChar, &size);
1691 justifiedWidth = size.cx;
1693 else lastExtent = TRUE;
1695 /* catch errors and report them */
1696 if (!lastExtent && (justifiedWidth != areaWidth))
1698 memset(error[nErrors].extent, 0, 100);
1699 memcpy(error[nErrors].extent, pFirstChar, pLastChar - pFirstChar);
1700 error[nErrors].GetTextExtentExPointWWidth = justifiedWidth;
1701 nErrors++;
1704 y += size.cy;
1705 str = pLastChar;
1706 } while (*str && y < clientArea->bottom);
1708 for (e = 0; e < nErrors; e++)
1710 /* The width returned by GetTextExtentPoint32() is exactly the same
1711 returned by GetTextExtentExPointW() - see dlls/gdi32/font.c */
1712 ok(error[e].GetTextExtentExPointWWidth == areaWidth,
1713 "GetTextExtentPointW() for \"%s\" should have returned a width of %d, not %d.\n",
1714 error[e].extent, areaWidth, error[e].GetTextExtentExPointWWidth);
1718 static void test_SetTextJustification(void)
1720 HDC hdc;
1721 RECT clientArea;
1722 LOGFONTA lf;
1723 HFONT hfont;
1724 HWND hwnd;
1725 static char testText[] =
1726 "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do "
1727 "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut "
1728 "enim ad minim veniam, quis nostrud exercitation ullamco laboris "
1729 "nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in "
1730 "reprehenderit in voluptate velit esse cillum dolore eu fugiat "
1731 "nulla pariatur. Excepteur sint occaecat cupidatat non proident, "
1732 "sunt in culpa qui officia deserunt mollit anim id est laborum.";
1734 hwnd = CreateWindowExA(0, "static", "", WS_POPUP, 0,0, 400,400, 0, 0, 0, NULL);
1735 GetClientRect( hwnd, &clientArea );
1736 hdc = GetDC( hwnd );
1738 memset(&lf, 0, sizeof lf);
1739 lf.lfCharSet = ANSI_CHARSET;
1740 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
1741 lf.lfWeight = FW_DONTCARE;
1742 lf.lfHeight = 20;
1743 lf.lfQuality = DEFAULT_QUALITY;
1744 lstrcpyA(lf.lfFaceName, "Times New Roman");
1745 hfont = create_font("Times New Roman", &lf);
1746 SelectObject(hdc, hfont);
1748 testJustification(hdc, testText, &clientArea);
1750 DeleteObject(hfont);
1751 ReleaseDC(hwnd, hdc);
1752 DestroyWindow(hwnd);
1755 static BOOL get_glyph_indices(INT charset, UINT code_page, WORD *idx, UINT count, BOOL unicode)
1757 HDC hdc;
1758 LOGFONTA lf;
1759 HFONT hfont, hfont_old;
1760 CHARSETINFO csi;
1761 FONTSIGNATURE fs;
1762 INT cs;
1763 DWORD i, ret;
1764 char name[64];
1766 assert(count <= 128);
1768 memset(&lf, 0, sizeof(lf));
1770 lf.lfCharSet = charset;
1771 lf.lfHeight = 10;
1772 lstrcpyA(lf.lfFaceName, "Arial");
1773 SetLastError(0xdeadbeef);
1774 hfont = CreateFontIndirectA(&lf);
1775 ok(hfont != 0, "CreateFontIndirectA error %u\n", GetLastError());
1777 hdc = GetDC(0);
1778 hfont_old = SelectObject(hdc, hfont);
1780 cs = GetTextCharsetInfo(hdc, &fs, 0);
1781 ok(cs == charset, "expected %d, got %d\n", charset, cs);
1783 SetLastError(0xdeadbeef);
1784 ret = GetTextFaceA(hdc, sizeof(name), name);
1785 ok(ret, "GetTextFaceA error %u\n", GetLastError());
1787 if (charset == SYMBOL_CHARSET)
1789 ok(strcmp("Arial", name), "face name should NOT be Arial\n");
1790 ok(fs.fsCsb[0] & (1 << 31), "symbol encoding should be available\n");
1792 else
1794 ok(!strcmp("Arial", name), "face name should be Arial, not %s\n", name);
1795 ok(!(fs.fsCsb[0] & (1 << 31)), "symbol encoding should NOT be available\n");
1798 if (!TranslateCharsetInfo((DWORD *)(INT_PTR)cs, &csi, TCI_SRCCHARSET))
1800 trace("Can't find codepage for charset %d\n", cs);
1801 ReleaseDC(0, hdc);
1802 return FALSE;
1804 ok(csi.ciACP == code_page, "expected %d, got %d\n", code_page, csi.ciACP);
1806 if (pGdiGetCodePage != NULL && pGdiGetCodePage(hdc) != code_page)
1808 skip("Font code page %d, looking for code page %d\n",
1809 pGdiGetCodePage(hdc), code_page);
1810 ReleaseDC(0, hdc);
1811 return FALSE;
1814 if (unicode)
1816 char ansi_buf[128];
1817 WCHAR unicode_buf[128];
1819 for (i = 0; i < count; i++) ansi_buf[i] = (BYTE)(i + 128);
1821 MultiByteToWideChar(code_page, 0, ansi_buf, count, unicode_buf, count);
1823 SetLastError(0xdeadbeef);
1824 ret = pGetGlyphIndicesW(hdc, unicode_buf, count, idx, 0);
1825 ok(ret == count, "GetGlyphIndicesW expected %d got %d, error %u\n",
1826 count, ret, GetLastError());
1828 else
1830 char ansi_buf[128];
1832 for (i = 0; i < count; i++) ansi_buf[i] = (BYTE)(i + 128);
1834 SetLastError(0xdeadbeef);
1835 ret = pGetGlyphIndicesA(hdc, ansi_buf, count, idx, 0);
1836 ok(ret == count, "GetGlyphIndicesA expected %d got %d, error %u\n",
1837 count, ret, GetLastError());
1840 SelectObject(hdc, hfont_old);
1841 DeleteObject(hfont);
1843 ReleaseDC(0, hdc);
1845 return TRUE;
1848 static void test_font_charset(void)
1850 static struct charset_data
1852 INT charset;
1853 UINT code_page;
1854 WORD font_idxA[128], font_idxW[128];
1855 } cd[] =
1857 { ANSI_CHARSET, 1252 },
1858 { RUSSIAN_CHARSET, 1251 },
1859 { SYMBOL_CHARSET, CP_SYMBOL } /* keep it as the last one */
1861 int i;
1863 if (!pGetGlyphIndicesA || !pGetGlyphIndicesW)
1865 win_skip("Skipping the font charset test on a Win9x platform\n");
1866 return;
1869 if (!is_font_installed("Arial"))
1871 skip("Arial is not installed\n");
1872 return;
1875 for (i = 0; i < sizeof(cd)/sizeof(cd[0]); i++)
1877 if (cd[i].charset == SYMBOL_CHARSET)
1879 if (!is_font_installed("Symbol") && !is_font_installed("Wingdings"))
1881 skip("Symbol or Wingdings is not installed\n");
1882 break;
1885 if (get_glyph_indices(cd[i].charset, cd[i].code_page, cd[i].font_idxA, 128, FALSE) &&
1886 get_glyph_indices(cd[i].charset, cd[i].code_page, cd[i].font_idxW, 128, TRUE))
1887 ok(!memcmp(cd[i].font_idxA, cd[i].font_idxW, 128*sizeof(WORD)), "%d: indices don't match\n", i);
1890 ok(memcmp(cd[0].font_idxW, cd[1].font_idxW, 128*sizeof(WORD)), "0 vs 1: indices shouldn't match\n");
1891 if (i > 2)
1893 ok(memcmp(cd[0].font_idxW, cd[2].font_idxW, 128*sizeof(WORD)), "0 vs 2: indices shouldn't match\n");
1894 ok(memcmp(cd[1].font_idxW, cd[2].font_idxW, 128*sizeof(WORD)), "1 vs 2: indices shouldn't match\n");
1896 else
1897 skip("Symbol or Wingdings is not installed\n");
1900 static void test_GetFontUnicodeRanges(void)
1902 LOGFONTA lf;
1903 HDC hdc;
1904 HFONT hfont, hfont_old;
1905 DWORD size;
1906 GLYPHSET *gs;
1907 DWORD i;
1909 if (!pGetFontUnicodeRanges)
1911 win_skip("GetFontUnicodeRanges not available before W2K\n");
1912 return;
1915 memset(&lf, 0, sizeof(lf));
1916 lstrcpyA(lf.lfFaceName, "Arial");
1917 hfont = create_font("Arial", &lf);
1919 hdc = GetDC(0);
1920 hfont_old = SelectObject(hdc, hfont);
1922 size = pGetFontUnicodeRanges(NULL, NULL);
1923 ok(!size, "GetFontUnicodeRanges succeeded unexpectedly\n");
1925 size = pGetFontUnicodeRanges(hdc, NULL);
1926 ok(size, "GetFontUnicodeRanges failed unexpectedly\n");
1928 gs = HeapAlloc(GetProcessHeap(), 0, size);
1930 size = pGetFontUnicodeRanges(hdc, gs);
1931 ok(size, "GetFontUnicodeRanges failed\n");
1933 if (0) /* Disabled to limit console spam */
1934 for (i = 0; i < gs->cRanges; i++)
1935 trace("%03d wcLow %04x cGlyphs %u\n", i, gs->ranges[i].wcLow, gs->ranges[i].cGlyphs);
1936 trace("found %u ranges\n", gs->cRanges);
1938 HeapFree(GetProcessHeap(), 0, gs);
1940 SelectObject(hdc, hfont_old);
1941 DeleteObject(hfont);
1942 ReleaseDC(NULL, hdc);
1945 #define MAX_ENUM_FONTS 4096
1947 struct enum_font_data
1949 int total;
1950 LOGFONT lf[MAX_ENUM_FONTS];
1953 struct enum_font_dataW
1955 int total;
1956 LOGFONTW lf[MAX_ENUM_FONTS];
1959 static INT CALLBACK arial_enum_proc(const LOGFONT *lf, const TEXTMETRIC *tm, DWORD type, LPARAM lParam)
1961 struct enum_font_data *efd = (struct enum_font_data *)lParam;
1963 ok(lf->lfHeight == tm->tmHeight, "lfHeight %d != tmHeight %d\n", lf->lfHeight, tm->tmHeight);
1965 if (type != TRUETYPE_FONTTYPE) return 1;
1966 if (0) /* Disabled to limit console spam */
1967 trace("enumed font \"%s\", charset %d, height %d, weight %d, italic %d\n",
1968 lf->lfFaceName, lf->lfCharSet, lf->lfHeight, lf->lfWeight, lf->lfItalic);
1969 if (efd->total < MAX_ENUM_FONTS)
1970 efd->lf[efd->total++] = *lf;
1971 else
1972 trace("enum tests invalid; you have more than %d fonts\n", MAX_ENUM_FONTS);
1974 return 1;
1977 static INT CALLBACK arial_enum_procw(const LOGFONTW *lf, const TEXTMETRICW *tm, DWORD type, LPARAM lParam)
1979 struct enum_font_dataW *efd = (struct enum_font_dataW *)lParam;
1981 ok(lf->lfHeight == tm->tmHeight, "lfHeight %d != tmHeight %d\n", lf->lfHeight, tm->tmHeight);
1983 if (type != TRUETYPE_FONTTYPE) return 1;
1984 if (0) /* Disabled to limit console spam */
1985 trace("enumed font %s, charset %d, height %d, weight %d, italic %d\n",
1986 wine_dbgstr_w(lf->lfFaceName), lf->lfCharSet, lf->lfHeight, lf->lfWeight, lf->lfItalic);
1987 if (efd->total < MAX_ENUM_FONTS)
1988 efd->lf[efd->total++] = *lf;
1989 else
1990 trace("enum tests invalid; you have more than %d fonts\n", MAX_ENUM_FONTS);
1992 return 1;
1995 static void get_charset_stats(struct enum_font_data *efd,
1996 int *ansi_charset, int *symbol_charset,
1997 int *russian_charset)
1999 int i;
2001 *ansi_charset = 0;
2002 *symbol_charset = 0;
2003 *russian_charset = 0;
2005 for (i = 0; i < efd->total; i++)
2007 switch (efd->lf[i].lfCharSet)
2009 case ANSI_CHARSET:
2010 (*ansi_charset)++;
2011 break;
2012 case SYMBOL_CHARSET:
2013 (*symbol_charset)++;
2014 break;
2015 case RUSSIAN_CHARSET:
2016 (*russian_charset)++;
2017 break;
2022 static void get_charset_statsW(struct enum_font_dataW *efd,
2023 int *ansi_charset, int *symbol_charset,
2024 int *russian_charset)
2026 int i;
2028 *ansi_charset = 0;
2029 *symbol_charset = 0;
2030 *russian_charset = 0;
2032 for (i = 0; i < efd->total; i++)
2034 switch (efd->lf[i].lfCharSet)
2036 case ANSI_CHARSET:
2037 (*ansi_charset)++;
2038 break;
2039 case SYMBOL_CHARSET:
2040 (*symbol_charset)++;
2041 break;
2042 case RUSSIAN_CHARSET:
2043 (*russian_charset)++;
2044 break;
2049 static void test_EnumFontFamilies(const char *font_name, INT font_charset)
2051 struct enum_font_data efd;
2052 struct enum_font_dataW efdw;
2053 LOGFONT lf;
2054 HDC hdc;
2055 int i, ret, ansi_charset, symbol_charset, russian_charset;
2057 trace("Testing font %s, charset %d\n", *font_name ? font_name : "<empty>", font_charset);
2059 if (*font_name && !is_truetype_font_installed(font_name))
2061 skip("%s is not installed\n", font_name);
2062 return;
2065 hdc = GetDC(0);
2067 /* Observed behaviour: EnumFontFamilies enumerates aliases like "Arial Cyr"
2068 * while EnumFontFamiliesEx doesn't.
2070 if (!*font_name && font_charset == DEFAULT_CHARSET) /* do it only once */
2073 * Use EnumFontFamiliesW since win98 crashes when the
2074 * second parameter is NULL using EnumFontFamilies
2076 efdw.total = 0;
2077 SetLastError(0xdeadbeef);
2078 ret = EnumFontFamiliesW(hdc, NULL, arial_enum_procw, (LPARAM)&efdw);
2079 ok(ret || GetLastError() == ERROR_CALL_NOT_IMPLEMENTED, "EnumFontFamiliesW error %u\n", GetLastError());
2080 if(ret)
2082 get_charset_statsW(&efdw, &ansi_charset, &symbol_charset, &russian_charset);
2083 trace("enumerated ansi %d, symbol %d, russian %d fonts for NULL\n",
2084 ansi_charset, symbol_charset, russian_charset);
2085 ok(efdw.total > 0, "fonts enumerated: NULL\n");
2086 ok(ansi_charset > 0, "NULL family should enumerate ANSI_CHARSET\n");
2087 ok(symbol_charset > 0, "NULL family should enumerate SYMBOL_CHARSET\n");
2088 ok(russian_charset > 0 ||
2089 broken(russian_charset == 0), /* NT4 */
2090 "NULL family should enumerate RUSSIAN_CHARSET\n");
2093 efdw.total = 0;
2094 SetLastError(0xdeadbeef);
2095 ret = EnumFontFamiliesExW(hdc, NULL, arial_enum_procw, (LPARAM)&efdw, 0);
2096 ok(ret || GetLastError() == ERROR_CALL_NOT_IMPLEMENTED, "EnumFontFamiliesExW error %u\n", GetLastError());
2097 if(ret)
2099 get_charset_statsW(&efdw, &ansi_charset, &symbol_charset, &russian_charset);
2100 trace("enumerated ansi %d, symbol %d, russian %d fonts for NULL\n",
2101 ansi_charset, symbol_charset, russian_charset);
2102 ok(efdw.total > 0, "fonts enumerated: NULL\n");
2103 ok(ansi_charset > 0, "NULL family should enumerate ANSI_CHARSET\n");
2104 ok(symbol_charset > 0, "NULL family should enumerate SYMBOL_CHARSET\n");
2105 ok(russian_charset > 0, "NULL family should enumerate RUSSIAN_CHARSET\n");
2109 efd.total = 0;
2110 SetLastError(0xdeadbeef);
2111 ret = EnumFontFamilies(hdc, font_name, arial_enum_proc, (LPARAM)&efd);
2112 ok(ret, "EnumFontFamilies error %u\n", GetLastError());
2113 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
2114 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s\n",
2115 ansi_charset, symbol_charset, russian_charset,
2116 *font_name ? font_name : "<empty>");
2117 if (*font_name)
2118 ok(efd.total > 0, "no fonts enumerated: %s\n", font_name);
2119 else
2120 ok(!efd.total, "no fonts should be enumerated for empty font_name\n");
2121 for (i = 0; i < efd.total; i++)
2123 /* FIXME: remove completely once Wine is fixed */
2124 if (efd.lf[i].lfCharSet != font_charset)
2126 todo_wine
2127 ok(efd.lf[i].lfCharSet == font_charset, "%d: got charset %d\n", i, efd.lf[i].lfCharSet);
2129 else
2130 ok(efd.lf[i].lfCharSet == font_charset, "%d: got charset %d\n", i, efd.lf[i].lfCharSet);
2131 ok(!lstrcmp(efd.lf[i].lfFaceName, font_name), "expected %s, got %s\n",
2132 font_name, efd.lf[i].lfFaceName);
2135 memset(&lf, 0, sizeof(lf));
2136 lf.lfCharSet = ANSI_CHARSET;
2137 lstrcpy(lf.lfFaceName, font_name);
2138 efd.total = 0;
2139 SetLastError(0xdeadbeef);
2140 ret = EnumFontFamiliesEx(hdc, &lf, arial_enum_proc, (LPARAM)&efd, 0);
2141 ok(ret, "EnumFontFamiliesEx error %u\n", GetLastError());
2142 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
2143 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s ANSI_CHARSET\n",
2144 ansi_charset, symbol_charset, russian_charset,
2145 *font_name ? font_name : "<empty>");
2146 if (font_charset == SYMBOL_CHARSET)
2148 if (*font_name)
2149 ok(efd.total == 0, "no fonts should be enumerated: %s ANSI_CHARSET\n", font_name);
2150 else
2151 ok(efd.total > 0, "no fonts enumerated: %s\n", font_name);
2153 else
2155 ok(efd.total > 0, "no fonts enumerated: %s ANSI_CHARSET\n", font_name);
2156 for (i = 0; i < efd.total; i++)
2158 ok(efd.lf[i].lfCharSet == ANSI_CHARSET, "%d: got charset %d\n", i, efd.lf[i].lfCharSet);
2159 if (*font_name)
2160 ok(!lstrcmp(efd.lf[i].lfFaceName, font_name), "expected %s, got %s\n",
2161 font_name, efd.lf[i].lfFaceName);
2165 /* DEFAULT_CHARSET should enumerate all available charsets */
2166 memset(&lf, 0, sizeof(lf));
2167 lf.lfCharSet = DEFAULT_CHARSET;
2168 lstrcpy(lf.lfFaceName, font_name);
2169 efd.total = 0;
2170 SetLastError(0xdeadbeef);
2171 EnumFontFamiliesEx(hdc, &lf, arial_enum_proc, (LPARAM)&efd, 0);
2172 ok(ret, "EnumFontFamiliesEx error %u\n", GetLastError());
2173 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
2174 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s DEFAULT_CHARSET\n",
2175 ansi_charset, symbol_charset, russian_charset,
2176 *font_name ? font_name : "<empty>");
2177 ok(efd.total > 0, "no fonts enumerated: %s DEFAULT_CHARSET\n", font_name);
2178 for (i = 0; i < efd.total; i++)
2180 if (*font_name)
2181 ok(!lstrcmp(efd.lf[i].lfFaceName, font_name), "expected %s, got %s\n",
2182 font_name, efd.lf[i].lfFaceName);
2184 if (*font_name)
2186 switch (font_charset)
2188 case ANSI_CHARSET:
2189 ok(ansi_charset > 0,
2190 "ANSI_CHARSET should enumerate ANSI_CHARSET for %s\n", font_name);
2191 ok(!symbol_charset,
2192 "ANSI_CHARSET should NOT enumerate SYMBOL_CHARSET for %s\n", font_name);
2193 ok(russian_charset > 0,
2194 "ANSI_CHARSET should enumerate RUSSIAN_CHARSET for %s\n", font_name);
2195 break;
2196 case SYMBOL_CHARSET:
2197 ok(!ansi_charset,
2198 "SYMBOL_CHARSET should NOT enumerate ANSI_CHARSET for %s\n", font_name);
2199 ok(symbol_charset,
2200 "SYMBOL_CHARSET should enumerate SYMBOL_CHARSET for %s\n", font_name);
2201 ok(!russian_charset,
2202 "SYMBOL_CHARSET should NOT enumerate RUSSIAN_CHARSET for %s\n", font_name);
2203 break;
2204 case DEFAULT_CHARSET:
2205 ok(ansi_charset > 0,
2206 "DEFAULT_CHARSET should enumerate ANSI_CHARSET for %s\n", font_name);
2207 ok(symbol_charset > 0,
2208 "DEFAULT_CHARSET should enumerate SYMBOL_CHARSET for %s\n", font_name);
2209 ok(russian_charset > 0,
2210 "DEFAULT_CHARSET should enumerate RUSSIAN_CHARSET for %s\n", font_name);
2211 break;
2214 else
2216 ok(ansi_charset > 0,
2217 "DEFAULT_CHARSET should enumerate ANSI_CHARSET for %s\n", *font_name ? font_name : "<empty>");
2218 ok(symbol_charset > 0,
2219 "DEFAULT_CHARSET should enumerate SYMBOL_CHARSET for %s\n", *font_name ? font_name : "<empty>");
2220 ok(russian_charset > 0,
2221 "DEFAULT_CHARSET should enumerate RUSSIAN_CHARSET for %s\n", *font_name ? font_name : "<empty>");
2224 memset(&lf, 0, sizeof(lf));
2225 lf.lfCharSet = SYMBOL_CHARSET;
2226 lstrcpy(lf.lfFaceName, font_name);
2227 efd.total = 0;
2228 SetLastError(0xdeadbeef);
2229 EnumFontFamiliesEx(hdc, &lf, arial_enum_proc, (LPARAM)&efd, 0);
2230 ok(ret, "EnumFontFamiliesEx error %u\n", GetLastError());
2231 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
2232 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s SYMBOL_CHARSET\n",
2233 ansi_charset, symbol_charset, russian_charset,
2234 *font_name ? font_name : "<empty>");
2235 if (*font_name && font_charset == ANSI_CHARSET)
2236 ok(efd.total == 0, "no fonts should be enumerated: %s SYMBOL_CHARSET\n", font_name);
2237 else
2239 ok(efd.total > 0, "no fonts enumerated: %s SYMBOL_CHARSET\n", font_name);
2240 for (i = 0; i < efd.total; i++)
2242 ok(efd.lf[i].lfCharSet == SYMBOL_CHARSET, "%d: got charset %d\n", i, efd.lf[i].lfCharSet);
2243 if (*font_name)
2244 ok(!lstrcmp(efd.lf[i].lfFaceName, font_name), "expected %s, got %s\n",
2245 font_name, efd.lf[i].lfFaceName);
2248 ok(!ansi_charset,
2249 "SYMBOL_CHARSET should NOT enumerate ANSI_CHARSET for %s\n", *font_name ? font_name : "<empty>");
2250 ok(symbol_charset > 0,
2251 "SYMBOL_CHARSET should enumerate SYMBOL_CHARSET for %s\n", *font_name ? font_name : "<empty>");
2252 ok(!russian_charset,
2253 "SYMBOL_CHARSET should NOT enumerate RUSSIAN_CHARSET for %s\n", *font_name ? font_name : "<empty>");
2256 ReleaseDC(0, hdc);
2259 static INT CALLBACK enum_font_data_proc(const LOGFONT *lf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
2261 struct enum_font_data *efd = (struct enum_font_data *)lParam;
2263 if (type != TRUETYPE_FONTTYPE) return 1;
2265 if (efd->total < MAX_ENUM_FONTS)
2266 efd->lf[efd->total++] = *lf;
2267 else
2268 trace("enum tests invalid; you have more than %d fonts\n", MAX_ENUM_FONTS);
2270 return 1;
2273 static void test_EnumFontFamiliesEx_default_charset(void)
2275 struct enum_font_data efd;
2276 LOGFONT gui_font, enum_font;
2277 DWORD ret;
2278 HDC hdc;
2280 ret = GetObject(GetStockObject(DEFAULT_GUI_FONT), sizeof(gui_font), &gui_font);
2281 ok(ret, "GetObject failed.\n");
2282 if (!ret)
2283 return;
2285 efd.total = 0;
2287 hdc = GetDC(0);
2288 memset(&enum_font, 0, sizeof(enum_font));
2289 lstrcpy(enum_font.lfFaceName, gui_font.lfFaceName);
2290 enum_font.lfCharSet = DEFAULT_CHARSET;
2291 EnumFontFamiliesEx(hdc, &enum_font, enum_font_data_proc, (LPARAM)&efd, 0);
2292 ReleaseDC(0, hdc);
2294 if (efd.total == 0) {
2295 skip("'%s' is not found or not a TrueType font.\n", gui_font.lfFaceName);
2296 return;
2298 trace("'%s' has %d charsets.\n", gui_font.lfFaceName, efd.total);
2300 ok(efd.lf[0].lfCharSet == gui_font.lfCharSet,
2301 "(%s) got charset %d expected %d\n",
2302 efd.lf[0].lfFaceName, efd.lf[0].lfCharSet, gui_font.lfCharSet);
2304 return;
2307 static void test_negative_width(HDC hdc, const LOGFONTA *lf)
2309 HFONT hfont, hfont_prev;
2310 DWORD ret;
2311 GLYPHMETRICS gm1, gm2;
2312 LOGFONTA lf2 = *lf;
2313 WORD idx;
2315 if(!pGetGlyphIndicesA)
2316 return;
2318 /* negative widths are handled just as positive ones */
2319 lf2.lfWidth = -lf->lfWidth;
2321 SetLastError(0xdeadbeef);
2322 hfont = CreateFontIndirectA(lf);
2323 ok(hfont != 0, "CreateFontIndirect error %u\n", GetLastError());
2324 check_font("original", lf, hfont);
2326 hfont_prev = SelectObject(hdc, hfont);
2328 ret = pGetGlyphIndicesA(hdc, "x", 1, &idx, GGI_MARK_NONEXISTING_GLYPHS);
2329 if (ret == GDI_ERROR || idx == 0xffff)
2331 SelectObject(hdc, hfont_prev);
2332 DeleteObject(hfont);
2333 skip("Font %s doesn't contain 'x', skipping the test\n", lf->lfFaceName);
2334 return;
2337 /* filling with 0xaa causes false pass under WINEDEBUG=warn+heap */
2338 memset(&gm1, 0xab, sizeof(gm1));
2339 SetLastError(0xdeadbeef);
2340 ret = GetGlyphOutlineA(hdc, 'x', GGO_METRICS, &gm1, 0, NULL, &mat);
2341 ok(ret != GDI_ERROR, "GetGlyphOutline error 0x%x\n", GetLastError());
2343 SelectObject(hdc, hfont_prev);
2344 DeleteObject(hfont);
2346 SetLastError(0xdeadbeef);
2347 hfont = CreateFontIndirectA(&lf2);
2348 ok(hfont != 0, "CreateFontIndirect error %u\n", GetLastError());
2349 check_font("negative width", &lf2, hfont);
2351 hfont_prev = SelectObject(hdc, hfont);
2353 memset(&gm2, 0xbb, sizeof(gm2));
2354 SetLastError(0xdeadbeef);
2355 ret = GetGlyphOutlineA(hdc, 'x', GGO_METRICS, &gm2, 0, NULL, &mat);
2356 ok(ret != GDI_ERROR, "GetGlyphOutline error 0x%x\n", GetLastError());
2358 SelectObject(hdc, hfont_prev);
2359 DeleteObject(hfont);
2361 ok(gm1.gmBlackBoxX == gm2.gmBlackBoxX &&
2362 gm1.gmBlackBoxY == gm2.gmBlackBoxY &&
2363 gm1.gmptGlyphOrigin.x == gm2.gmptGlyphOrigin.x &&
2364 gm1.gmptGlyphOrigin.y == gm2.gmptGlyphOrigin.y &&
2365 gm1.gmCellIncX == gm2.gmCellIncX &&
2366 gm1.gmCellIncY == gm2.gmCellIncY,
2367 "gm1=%d,%d,%d,%d,%d,%d gm2=%d,%d,%d,%d,%d,%d\n",
2368 gm1.gmBlackBoxX, gm1.gmBlackBoxY, gm1.gmptGlyphOrigin.x,
2369 gm1.gmptGlyphOrigin.y, gm1.gmCellIncX, gm1.gmCellIncY,
2370 gm2.gmBlackBoxX, gm2.gmBlackBoxY, gm2.gmptGlyphOrigin.x,
2371 gm2.gmptGlyphOrigin.y, gm2.gmCellIncX, gm2.gmCellIncY);
2374 /* PANOSE is 10 bytes in size, need to pack the structure properly */
2375 #include "pshpack2.h"
2376 typedef struct
2378 USHORT version;
2379 SHORT xAvgCharWidth;
2380 USHORT usWeightClass;
2381 USHORT usWidthClass;
2382 SHORT fsType;
2383 SHORT ySubscriptXSize;
2384 SHORT ySubscriptYSize;
2385 SHORT ySubscriptXOffset;
2386 SHORT ySubscriptYOffset;
2387 SHORT ySuperscriptXSize;
2388 SHORT ySuperscriptYSize;
2389 SHORT ySuperscriptXOffset;
2390 SHORT ySuperscriptYOffset;
2391 SHORT yStrikeoutSize;
2392 SHORT yStrikeoutPosition;
2393 SHORT sFamilyClass;
2394 PANOSE panose;
2395 ULONG ulUnicodeRange1;
2396 ULONG ulUnicodeRange2;
2397 ULONG ulUnicodeRange3;
2398 ULONG ulUnicodeRange4;
2399 CHAR achVendID[4];
2400 USHORT fsSelection;
2401 USHORT usFirstCharIndex;
2402 USHORT usLastCharIndex;
2403 /* According to the Apple spec, original version didn't have the below fields,
2404 * version numbers were taken from the OpenType spec.
2406 /* version 0 (TrueType 1.5) */
2407 USHORT sTypoAscender;
2408 USHORT sTypoDescender;
2409 USHORT sTypoLineGap;
2410 USHORT usWinAscent;
2411 USHORT usWinDescent;
2412 /* version 1 (TrueType 1.66) */
2413 ULONG ulCodePageRange1;
2414 ULONG ulCodePageRange2;
2415 /* version 2 (OpenType 1.2) */
2416 SHORT sxHeight;
2417 SHORT sCapHeight;
2418 USHORT usDefaultChar;
2419 USHORT usBreakChar;
2420 USHORT usMaxContext;
2421 } TT_OS2_V2;
2422 #include "poppack.h"
2424 #ifdef WORDS_BIGENDIAN
2425 #define GET_BE_WORD(x) (x)
2426 #define GET_BE_DWORD(x) (x)
2427 #else
2428 #define GET_BE_WORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
2429 #define GET_BE_DWORD(x) MAKELONG(GET_BE_WORD(HIWORD(x)), GET_BE_WORD(LOWORD(x)));
2430 #endif
2432 #define MS_MAKE_TAG(ch0, ch1, ch2, ch3) \
2433 ((DWORD)(BYTE)(ch0) | ((DWORD)(BYTE)(ch1) << 8) | \
2434 ((DWORD)(BYTE)(ch2) << 16) | ((DWORD)(BYTE)(ch3) << 24))
2435 #define MS_OS2_TAG MS_MAKE_TAG('O','S','/','2')
2436 #define MS_CMAP_TAG MS_MAKE_TAG('c','m','a','p')
2437 #define MS_NAME_TAG MS_MAKE_TAG('n','a','m','e')
2439 typedef struct
2441 USHORT version;
2442 USHORT num_tables;
2443 } cmap_header;
2445 typedef struct
2447 USHORT plat_id;
2448 USHORT enc_id;
2449 ULONG offset;
2450 } cmap_encoding_record;
2452 typedef struct
2454 USHORT format;
2455 USHORT length;
2456 USHORT language;
2458 BYTE glyph_ids[256];
2459 } cmap_format_0;
2461 typedef struct
2463 USHORT format;
2464 USHORT length;
2465 USHORT language;
2467 USHORT seg_countx2;
2468 USHORT search_range;
2469 USHORT entry_selector;
2470 USHORT range_shift;
2472 USHORT end_count[1]; /* this is a variable-sized array of length seg_countx2 / 2 */
2473 /* Then follows:
2474 USHORT pad;
2475 USHORT start_count[seg_countx2 / 2];
2476 USHORT id_delta[seg_countx2 / 2];
2477 USHORT id_range_offset[seg_countx2 / 2];
2478 USHORT glyph_ids[];
2480 } cmap_format_4;
2482 typedef struct
2484 USHORT end_count;
2485 USHORT start_count;
2486 USHORT id_delta;
2487 USHORT id_range_offset;
2488 } cmap_format_4_seg;
2490 static void expect_ff(const TEXTMETRICA *tmA, const TT_OS2_V2 *os2, WORD family, const char *name)
2492 ok((tmA->tmPitchAndFamily & 0xf0) == family ||
2493 broken(PRIMARYLANGID(GetSystemDefaultLangID()) != LANG_ENGLISH),
2494 "%s: expected family %02x got %02x. panose %d-%d-%d-%d-...\n",
2495 name, family, tmA->tmPitchAndFamily, os2->panose.bFamilyType, os2->panose.bSerifStyle,
2496 os2->panose.bWeight, os2->panose.bProportion);
2499 static BOOL get_first_last_from_cmap0(void *ptr, DWORD *first, DWORD *last)
2501 int i;
2502 cmap_format_0 *cmap = (cmap_format_0*)ptr;
2504 *first = 256;
2506 for(i = 0; i < 256; i++)
2508 if(cmap->glyph_ids[i] == 0) continue;
2509 *last = i;
2510 if(*first == 256) *first = i;
2512 if(*first == 256) return FALSE;
2513 return TRUE;
2516 static void get_seg4(cmap_format_4 *cmap, USHORT seg_num, cmap_format_4_seg *seg)
2518 USHORT segs = GET_BE_WORD(cmap->seg_countx2) / 2;
2519 seg->end_count = GET_BE_WORD(cmap->end_count[seg_num]);
2520 seg->start_count = GET_BE_WORD(cmap->end_count[segs + 1 + seg_num]);
2521 seg->id_delta = GET_BE_WORD(cmap->end_count[2 * segs + 1 + seg_num]);
2522 seg->id_range_offset = GET_BE_WORD(cmap->end_count[3 * segs + 1 + seg_num]);
2525 static BOOL get_first_last_from_cmap4(void *ptr, DWORD *first, DWORD *last, DWORD limit)
2527 int i;
2528 cmap_format_4 *cmap = (cmap_format_4*)ptr;
2529 USHORT seg_count = GET_BE_WORD(cmap->seg_countx2) / 2;
2530 USHORT const *glyph_ids = cmap->end_count + 4 * seg_count + 1;
2532 *first = 0x10000;
2534 for(i = 0; i < seg_count; i++)
2536 DWORD code, index;
2537 cmap_format_4_seg seg;
2539 get_seg4(cmap, i, &seg);
2540 for(code = seg.start_count; code <= seg.end_count; code++)
2542 if(seg.id_range_offset == 0)
2543 index = (seg.id_delta + code) & 0xffff;
2544 else
2546 index = seg.id_range_offset / 2
2547 + code - seg.start_count
2548 + i - seg_count;
2550 /* some fonts have broken last segment */
2551 if ((char *)(glyph_ids + index + 1) < (char *)ptr + limit)
2552 index = GET_BE_WORD(glyph_ids[index]);
2553 else
2555 trace("segment %04x/%04x index %04x points to nowhere\n",
2556 seg.start_count, seg.end_count, index);
2557 index = 0;
2559 if(index) index += seg.id_delta;
2561 if(*first == 0x10000)
2562 *last = *first = code;
2563 else if(index)
2564 *last = code;
2568 if(*first == 0x10000) return FALSE;
2569 return TRUE;
2572 static void *get_cmap(cmap_header *header, USHORT plat_id, USHORT enc_id)
2574 USHORT i;
2575 cmap_encoding_record *record = (cmap_encoding_record *)(header + 1);
2577 for(i = 0; i < GET_BE_WORD(header->num_tables); i++)
2579 if(GET_BE_WORD(record->plat_id) == plat_id && GET_BE_WORD(record->enc_id) == enc_id)
2580 return (BYTE *)header + GET_BE_DWORD(record->offset);
2581 record++;
2583 return NULL;
2586 typedef enum
2588 cmap_none,
2589 cmap_ms_unicode,
2590 cmap_ms_symbol
2591 } cmap_type;
2593 static BOOL get_first_last_from_cmap(HDC hdc, DWORD *first, DWORD *last, cmap_type *cmap_type)
2595 LONG size, ret;
2596 cmap_header *header;
2597 void *cmap;
2598 BOOL r = FALSE;
2599 WORD format;
2601 size = GetFontData(hdc, MS_CMAP_TAG, 0, NULL, 0);
2602 ok(size != GDI_ERROR, "no cmap table found\n");
2603 if(size == GDI_ERROR) return FALSE;
2605 header = HeapAlloc(GetProcessHeap(), 0, size);
2606 ret = GetFontData(hdc, MS_CMAP_TAG, 0, header, size);
2607 ok(ret == size, "GetFontData should return %u not %u\n", size, ret);
2608 ok(GET_BE_WORD(header->version) == 0, "got cmap version %d\n", GET_BE_WORD(header->version));
2610 cmap = get_cmap(header, 3, 1);
2611 if(cmap)
2612 *cmap_type = cmap_ms_unicode;
2613 else
2615 cmap = get_cmap(header, 3, 0);
2616 if(cmap) *cmap_type = cmap_ms_symbol;
2618 if(!cmap)
2620 *cmap_type = cmap_none;
2621 goto end;
2624 format = GET_BE_WORD(*(WORD *)cmap);
2625 switch(format)
2627 case 0:
2628 r = get_first_last_from_cmap0(cmap, first, last);
2629 break;
2630 case 4:
2631 r = get_first_last_from_cmap4(cmap, first, last, size);
2632 break;
2633 default:
2634 trace("unhandled cmap format %d\n", format);
2635 break;
2638 end:
2639 HeapFree(GetProcessHeap(), 0, header);
2640 return r;
2643 #define TT_PLATFORM_MICROSOFT 3
2644 #define TT_MS_ID_UNICODE_CS 1
2645 #define TT_MS_LANGID_ENGLISH_UNITED_STATES 0x0409
2646 #define TT_NAME_ID_FULL_NAME 4
2648 static BOOL get_ttf_nametable_entry(HDC hdc, WORD name_id, char *out_buf, SIZE_T out_size)
2650 struct sfnt_name_header
2652 USHORT format;
2653 USHORT number_of_record;
2654 USHORT storage_offset;
2655 } *header;
2656 struct sfnt_name
2658 USHORT platform_id;
2659 USHORT encoding_id;
2660 USHORT language_id;
2661 USHORT name_id;
2662 USHORT length;
2663 USHORT offset;
2664 } *entry;
2665 BOOL r = FALSE;
2666 LONG size, offset, length;
2667 LONG c, ret;
2668 WCHAR *name;
2669 BYTE *data;
2670 USHORT i;
2672 size = GetFontData(hdc, MS_NAME_TAG, 0, NULL, 0);
2673 ok(size != GDI_ERROR, "no name table found\n");
2674 if(size == GDI_ERROR) return FALSE;
2676 data = HeapAlloc(GetProcessHeap(), 0, size);
2677 ret = GetFontData(hdc, MS_NAME_TAG, 0, data, size);
2678 ok(ret == size, "GetFontData should return %u not %u\n", size, ret);
2680 header = (void *)data;
2681 header->format = GET_BE_WORD(header->format);
2682 header->number_of_record = GET_BE_WORD(header->number_of_record);
2683 header->storage_offset = GET_BE_WORD(header->storage_offset);
2684 if (header->format != 0)
2686 trace("got format %u\n", header->format);
2687 goto out;
2689 if (header->number_of_record == 0 || sizeof(*header) + header->number_of_record * sizeof(*entry) > size)
2691 trace("number records out of range: %d\n", header->number_of_record);
2692 goto out;
2694 if (header->storage_offset >= size)
2696 trace("storage_offset %u > size %u\n", header->storage_offset, size);
2697 goto out;
2700 entry = (void *)&header[1];
2701 for (i = 0; i < header->number_of_record; i++)
2703 if (GET_BE_WORD(entry[i].platform_id) != TT_PLATFORM_MICROSOFT ||
2704 GET_BE_WORD(entry[i].encoding_id) != TT_MS_ID_UNICODE_CS ||
2705 GET_BE_WORD(entry[i].language_id) != TT_MS_LANGID_ENGLISH_UNITED_STATES ||
2706 GET_BE_WORD(entry[i].name_id) != name_id)
2708 continue;
2711 offset = header->storage_offset + GET_BE_WORD(entry[i].offset);
2712 length = GET_BE_WORD(entry[i].length);
2713 if (offset + length > size)
2715 trace("entry %d is out of range\n", i);
2716 break;
2718 if (length >= out_size)
2720 trace("buffer too small for entry %d\n", i);
2721 break;
2724 name = (WCHAR *)(data + offset);
2725 for (c = 0; c < length / 2; c++)
2726 out_buf[c] = GET_BE_WORD(name[c]);
2727 out_buf[c] = 0;
2729 r = TRUE;
2730 break;
2733 out:
2734 HeapFree(GetProcessHeap(), 0, data);
2735 return r;
2738 static void test_text_metrics(const LOGFONTA *lf)
2740 HDC hdc;
2741 HFONT hfont, hfont_old;
2742 TEXTMETRICA tmA;
2743 TT_OS2_V2 tt_os2;
2744 LONG size, ret;
2745 const char *font_name = lf->lfFaceName;
2746 DWORD cmap_first = 0, cmap_last = 0;
2747 cmap_type cmap_type;
2748 BOOL sys_lang_non_english;
2750 sys_lang_non_english = PRIMARYLANGID(GetSystemDefaultLangID()) != LANG_ENGLISH;
2751 hdc = GetDC(0);
2753 SetLastError(0xdeadbeef);
2754 hfont = CreateFontIndirectA(lf);
2755 ok(hfont != 0, "CreateFontIndirect error %u\n", GetLastError());
2757 hfont_old = SelectObject(hdc, hfont);
2759 size = GetFontData(hdc, MS_OS2_TAG, 0, NULL, 0);
2760 if (size == GDI_ERROR)
2762 trace("OS/2 chunk was not found\n");
2763 goto end_of_test;
2765 if (size > sizeof(tt_os2))
2767 trace("got too large OS/2 chunk of size %u\n", size);
2768 size = sizeof(tt_os2);
2771 memset(&tt_os2, 0, sizeof(tt_os2));
2772 ret = GetFontData(hdc, MS_OS2_TAG, 0, &tt_os2, size);
2773 ok(ret == size, "GetFontData should return %u not %u\n", size, ret);
2775 SetLastError(0xdeadbeef);
2776 ret = GetTextMetricsA(hdc, &tmA);
2777 ok(ret, "GetTextMetricsA error %u\n", GetLastError());
2779 if(!get_first_last_from_cmap(hdc, &cmap_first, &cmap_last, &cmap_type))
2781 skip("Unable to retrieve first and last glyphs from cmap\n");
2783 else
2785 USHORT expect_first_A, expect_last_A, expect_break_A, expect_default_A;
2786 USHORT expect_first_W, expect_last_W, expect_break_W, expect_default_W;
2787 UINT os2_first_char, os2_last_char, default_char, break_char;
2788 USHORT version;
2789 TEXTMETRICW tmW;
2791 version = GET_BE_WORD(tt_os2.version);
2793 os2_first_char = GET_BE_WORD(tt_os2.usFirstCharIndex);
2794 os2_last_char = GET_BE_WORD(tt_os2.usLastCharIndex);
2795 default_char = GET_BE_WORD(tt_os2.usDefaultChar);
2796 break_char = GET_BE_WORD(tt_os2.usBreakChar);
2798 trace("font %s charset %u: %x-%x (%x-%x) default %x break %x OS/2 version %u vendor %4.4s\n",
2799 font_name, lf->lfCharSet, os2_first_char, os2_last_char, cmap_first, cmap_last,
2800 default_char, break_char, version, (LPCSTR)&tt_os2.achVendID);
2802 if (cmap_type == cmap_ms_symbol || (cmap_first >= 0xf000 && cmap_first < 0xf100))
2804 expect_first_W = 0;
2805 switch(GetACP())
2807 case 1257: /* Baltic */
2808 expect_last_W = 0xf8fd;
2809 break;
2810 default:
2811 expect_last_W = 0xf0ff;
2813 expect_break_W = 0x20;
2814 expect_default_W = expect_break_W - 1;
2815 expect_first_A = 0x1e;
2816 expect_last_A = min(os2_last_char - os2_first_char + 0x20, 0xff);
2818 else
2820 expect_first_W = cmap_first;
2821 expect_last_W = min(cmap_last, os2_last_char);
2822 if(os2_first_char <= 1)
2823 expect_break_W = os2_first_char + 2;
2824 else if(os2_first_char > 0xff)
2825 expect_break_W = 0x20;
2826 else
2827 expect_break_W = os2_first_char;
2828 expect_default_W = expect_break_W - 1;
2829 expect_first_A = expect_default_W - 1;
2830 expect_last_A = min(expect_last_W, 0xff);
2832 expect_break_A = expect_break_W;
2833 expect_default_A = expect_default_W;
2835 /* Wine currently uses SYMBOL_CHARSET to identify whether the ANSI metrics need special handling */
2836 if(cmap_type != cmap_ms_symbol && tmA.tmCharSet == SYMBOL_CHARSET && expect_first_A != 0x1e)
2837 todo_wine ok(tmA.tmFirstChar == expect_first_A ||
2838 tmA.tmFirstChar == expect_first_A + 1 /* win9x */,
2839 "A: tmFirstChar for %s got %02x expected %02x\n", font_name, tmA.tmFirstChar, expect_first_A);
2840 else
2841 ok(tmA.tmFirstChar == expect_first_A ||
2842 tmA.tmFirstChar == expect_first_A + 1 /* win9x */,
2843 "A: tmFirstChar for %s got %02x expected %02x\n", font_name, tmA.tmFirstChar, expect_first_A);
2844 if (pGdiGetCodePage == NULL || ! IsDBCSLeadByteEx(pGdiGetCodePage(hdc), tmA.tmLastChar))
2845 ok(tmA.tmLastChar == expect_last_A ||
2846 tmA.tmLastChar == 0xff /* win9x */,
2847 "A: tmLastChar for %s got %02x expected %02x\n", font_name, tmA.tmLastChar, expect_last_A);
2848 else
2849 skip("tmLastChar is DBCS lead byte\n");
2850 ok(tmA.tmBreakChar == expect_break_A, "A: tmBreakChar for %s got %02x expected %02x\n",
2851 font_name, tmA.tmBreakChar, expect_break_A);
2852 ok(tmA.tmDefaultChar == expect_default_A || broken(sys_lang_non_english),
2853 "A: tmDefaultChar for %s got %02x expected %02x\n",
2854 font_name, tmA.tmDefaultChar, expect_default_A);
2857 SetLastError(0xdeadbeef);
2858 ret = GetTextMetricsW(hdc, &tmW);
2859 ok(ret || GetLastError() == ERROR_CALL_NOT_IMPLEMENTED,
2860 "GetTextMetricsW error %u\n", GetLastError());
2861 if (ret)
2863 /* Wine uses the os2 first char */
2864 if(cmap_first != os2_first_char && cmap_type != cmap_ms_symbol)
2865 todo_wine ok(tmW.tmFirstChar == expect_first_W, "W: tmFirstChar for %s got %02x expected %02x\n",
2866 font_name, tmW.tmFirstChar, expect_first_W);
2867 else
2868 ok(tmW.tmFirstChar == expect_first_W, "W: tmFirstChar for %s got %02x expected %02x\n",
2869 font_name, tmW.tmFirstChar, expect_first_W);
2871 /* Wine uses the os2 last char */
2872 if(expect_last_W != os2_last_char && cmap_type != cmap_ms_symbol)
2873 todo_wine ok(tmW.tmLastChar == expect_last_W, "W: tmLastChar for %s got %02x expected %02x\n",
2874 font_name, tmW.tmLastChar, expect_last_W);
2875 else
2876 ok(tmW.tmLastChar == expect_last_W, "W: tmLastChar for %s got %02x expected %02x\n",
2877 font_name, tmW.tmLastChar, expect_last_W);
2878 ok(tmW.tmBreakChar == expect_break_W, "W: tmBreakChar for %s got %02x expected %02x\n",
2879 font_name, tmW.tmBreakChar, expect_break_W);
2880 ok(tmW.tmDefaultChar == expect_default_W || broken(sys_lang_non_english),
2881 "W: tmDefaultChar for %s got %02x expected %02x\n",
2882 font_name, tmW.tmDefaultChar, expect_default_W);
2884 /* Test the aspect ratio while we have tmW */
2885 ret = GetDeviceCaps(hdc, LOGPIXELSX);
2886 ok(tmW.tmDigitizedAspectX == ret, "W: tmDigitizedAspectX %u != %u\n",
2887 tmW.tmDigitizedAspectX, ret);
2888 ret = GetDeviceCaps(hdc, LOGPIXELSY);
2889 ok(tmW.tmDigitizedAspectX == ret, "W: tmDigitizedAspectY %u != %u\n",
2890 tmW.tmDigitizedAspectX, ret);
2894 /* test FF_ values */
2895 switch(tt_os2.panose.bFamilyType)
2897 case PAN_ANY:
2898 case PAN_NO_FIT:
2899 case PAN_FAMILY_TEXT_DISPLAY:
2900 case PAN_FAMILY_PICTORIAL:
2901 default:
2902 if((tmA.tmPitchAndFamily & 1) == 0 || /* fixed */
2903 tt_os2.panose.bProportion == PAN_PROP_MONOSPACED)
2905 expect_ff(&tmA, &tt_os2, FF_MODERN, font_name);
2906 break;
2908 switch(tt_os2.panose.bSerifStyle)
2910 case PAN_ANY:
2911 case PAN_NO_FIT:
2912 default:
2913 expect_ff(&tmA, &tt_os2, FF_DONTCARE, font_name);
2914 break;
2916 case PAN_SERIF_COVE:
2917 case PAN_SERIF_OBTUSE_COVE:
2918 case PAN_SERIF_SQUARE_COVE:
2919 case PAN_SERIF_OBTUSE_SQUARE_COVE:
2920 case PAN_SERIF_SQUARE:
2921 case PAN_SERIF_THIN:
2922 case PAN_SERIF_BONE:
2923 case PAN_SERIF_EXAGGERATED:
2924 case PAN_SERIF_TRIANGLE:
2925 expect_ff(&tmA, &tt_os2, FF_ROMAN, font_name);
2926 break;
2928 case PAN_SERIF_NORMAL_SANS:
2929 case PAN_SERIF_OBTUSE_SANS:
2930 case PAN_SERIF_PERP_SANS:
2931 case PAN_SERIF_FLARED:
2932 case PAN_SERIF_ROUNDED:
2933 expect_ff(&tmA, &tt_os2, FF_SWISS, font_name);
2934 break;
2936 break;
2938 case PAN_FAMILY_SCRIPT:
2939 expect_ff(&tmA, &tt_os2, FF_SCRIPT, font_name);
2940 break;
2942 case PAN_FAMILY_DECORATIVE:
2943 expect_ff(&tmA, &tt_os2, FF_DECORATIVE, font_name);
2944 break;
2947 test_negative_width(hdc, lf);
2949 end_of_test:
2950 SelectObject(hdc, hfont_old);
2951 DeleteObject(hfont);
2953 ReleaseDC(0, hdc);
2956 static INT CALLBACK enum_truetype_font_proc(const LOGFONT *lf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
2958 INT *enumed = (INT *)lParam;
2960 if (type == TRUETYPE_FONTTYPE)
2962 (*enumed)++;
2963 test_text_metrics(lf);
2965 return 1;
2968 static void test_GetTextMetrics(void)
2970 LOGFONTA lf;
2971 HDC hdc;
2972 INT enumed;
2974 /* Report only once */
2975 if(!pGetGlyphIndicesA)
2976 win_skip("GetGlyphIndicesA is unavailable, negative width will not be checked\n");
2978 hdc = GetDC(0);
2980 memset(&lf, 0, sizeof(lf));
2981 lf.lfCharSet = DEFAULT_CHARSET;
2982 enumed = 0;
2983 EnumFontFamiliesExA(hdc, &lf, enum_truetype_font_proc, (LPARAM)&enumed, 0);
2984 trace("Tested metrics of %d truetype fonts\n", enumed);
2986 ReleaseDC(0, hdc);
2989 static void test_nonexistent_font(void)
2991 static const struct
2993 const char *name;
2994 int charset;
2995 } font_subst[] =
2997 { "Times New Roman Baltic", 186 },
2998 { "Times New Roman CE", 238 },
2999 { "Times New Roman CYR", 204 },
3000 { "Times New Roman Greek", 161 },
3001 { "Times New Roman TUR", 162 }
3003 LOGFONTA lf;
3004 HDC hdc;
3005 HFONT hfont;
3006 CHARSETINFO csi;
3007 INT cs, expected_cs, i;
3008 char buf[LF_FACESIZE];
3010 if (!is_truetype_font_installed("Arial") ||
3011 !is_truetype_font_installed("Times New Roman"))
3013 skip("Arial or Times New Roman not installed\n");
3014 return;
3017 expected_cs = GetACP();
3018 if (!TranslateCharsetInfo(ULongToPtr(expected_cs), &csi, TCI_SRCCODEPAGE))
3020 skip("TranslateCharsetInfo failed for code page %d\n", expected_cs);
3021 return;
3023 expected_cs = csi.ciCharset;
3024 trace("ACP %d -> charset %d\n", GetACP(), expected_cs);
3026 hdc = GetDC(0);
3028 memset(&lf, 0, sizeof(lf));
3029 lf.lfHeight = 100;
3030 lf.lfWeight = FW_REGULAR;
3031 lf.lfCharSet = ANSI_CHARSET;
3032 lf.lfPitchAndFamily = FF_SWISS;
3033 strcpy(lf.lfFaceName, "Nonexistent font");
3034 hfont = CreateFontIndirectA(&lf);
3035 hfont = SelectObject(hdc, hfont);
3036 GetTextFaceA(hdc, sizeof(buf), buf);
3037 ok(!lstrcmpiA(buf, "Arial"), "Got %s\n", buf);
3038 cs = GetTextCharset(hdc);
3039 ok(cs == ANSI_CHARSET, "expected ANSI_CHARSET, got %d\n", cs);
3040 DeleteObject(SelectObject(hdc, hfont));
3042 memset(&lf, 0, sizeof(lf));
3043 lf.lfHeight = -13;
3044 lf.lfWeight = FW_DONTCARE;
3045 strcpy(lf.lfFaceName, "Nonexistent font");
3046 hfont = CreateFontIndirectA(&lf);
3047 hfont = SelectObject(hdc, hfont);
3048 GetTextFaceA(hdc, sizeof(buf), buf);
3049 todo_wine /* Wine uses Arial for all substitutions */
3050 ok(!lstrcmpiA(buf, "Nonexistent font") /* XP, Vista */ ||
3051 !lstrcmpiA(buf, "MS Serif") || /* Win9x */
3052 !lstrcmpiA(buf, "MS Sans Serif"), /* win2k3 */
3053 "Got %s\n", buf);
3054 cs = GetTextCharset(hdc);
3055 ok(cs == expected_cs || cs == ANSI_CHARSET, "expected %d, got %d\n", expected_cs, cs);
3056 DeleteObject(SelectObject(hdc, hfont));
3058 memset(&lf, 0, sizeof(lf));
3059 lf.lfHeight = -13;
3060 lf.lfWeight = FW_REGULAR;
3061 strcpy(lf.lfFaceName, "Nonexistent font");
3062 hfont = CreateFontIndirectA(&lf);
3063 hfont = SelectObject(hdc, hfont);
3064 GetTextFaceA(hdc, sizeof(buf), buf);
3065 ok(!lstrcmpiA(buf, "Arial") /* XP, Vista */ ||
3066 !lstrcmpiA(buf, "Times New Roman") /* Win9x */, "Got %s\n", buf);
3067 cs = GetTextCharset(hdc);
3068 ok(cs == ANSI_CHARSET, "expected ANSI_CHARSET, got %d\n", cs);
3069 DeleteObject(SelectObject(hdc, hfont));
3071 memset(&lf, 0, sizeof(lf));
3072 lf.lfHeight = -13;
3073 lf.lfWeight = FW_DONTCARE;
3074 strcpy(lf.lfFaceName, "Times New Roman");
3075 hfont = CreateFontIndirectA(&lf);
3076 hfont = SelectObject(hdc, hfont);
3077 GetTextFaceA(hdc, sizeof(buf), buf);
3078 ok(!lstrcmpiA(buf, "Times New Roman"), "Got %s\n", buf);
3079 cs = GetTextCharset(hdc);
3080 ok(cs == ANSI_CHARSET, "expected ANSI_CHARSET, got %d\n", cs);
3081 DeleteObject(SelectObject(hdc, hfont));
3083 for (i = 0; i < sizeof(font_subst)/sizeof(font_subst[0]); i++)
3085 memset(&lf, 0, sizeof(lf));
3086 lf.lfHeight = -13;
3087 lf.lfWeight = FW_REGULAR;
3088 strcpy(lf.lfFaceName, font_subst[i].name);
3089 hfont = CreateFontIndirectA(&lf);
3090 hfont = SelectObject(hdc, hfont);
3091 cs = GetTextCharset(hdc);
3092 if (font_subst[i].charset == expected_cs)
3094 ok(cs == expected_cs, "expected %d, got %d for font %s\n", expected_cs, cs, font_subst[i].name);
3095 GetTextFaceA(hdc, sizeof(buf), buf);
3096 ok(!lstrcmpiA(buf, font_subst[i].name), "expected %s, got %s\n", font_subst[i].name, buf);
3098 else
3100 ok(cs == ANSI_CHARSET, "expected ANSI_CHARSET, got %d for font %s\n", cs, font_subst[i].name);
3101 GetTextFaceA(hdc, sizeof(buf), buf);
3102 ok(!lstrcmpiA(buf, "Arial") /* XP, Vista */ ||
3103 !lstrcmpiA(buf, "Times New Roman") /* Win9x */, "got %s for font %s\n", buf, font_subst[i].name);
3105 DeleteObject(SelectObject(hdc, hfont));
3107 memset(&lf, 0, sizeof(lf));
3108 lf.lfHeight = -13;
3109 lf.lfWeight = FW_DONTCARE;
3110 strcpy(lf.lfFaceName, font_subst[i].name);
3111 hfont = CreateFontIndirectA(&lf);
3112 hfont = SelectObject(hdc, hfont);
3113 GetTextFaceA(hdc, sizeof(buf), buf);
3114 ok(!lstrcmpiA(buf, "Arial") /* Wine */ ||
3115 !lstrcmpiA(buf, font_subst[i].name) /* XP, Vista */ ||
3116 !lstrcmpiA(buf, "MS Serif") /* Win9x */ ||
3117 !lstrcmpiA(buf, "MS Sans Serif"), /* win2k3 */
3118 "got %s for font %s\n", buf, font_subst[i].name);
3119 cs = GetTextCharset(hdc);
3120 ok(cs == expected_cs || cs == ANSI_CHARSET, "expected %d, got %d for font %s\n", expected_cs, cs, font_subst[i].name);
3121 DeleteObject(SelectObject(hdc, hfont));
3124 ReleaseDC(0, hdc);
3127 static void test_GdiRealizationInfo(void)
3129 HDC hdc;
3130 DWORD info[4];
3131 BOOL r;
3132 HFONT hfont, hfont_old;
3133 LOGFONTA lf;
3135 if(!pGdiRealizationInfo)
3137 win_skip("GdiRealizationInfo not available\n");
3138 return;
3141 hdc = GetDC(0);
3143 memset(info, 0xcc, sizeof(info));
3144 r = pGdiRealizationInfo(hdc, info);
3145 ok(r != 0, "ret 0\n");
3146 ok((info[0] & 0xf) == 1, "info[0] = %x for the system font\n", info[0]);
3147 ok(info[3] == 0xcccccccc, "structure longer than 3 dwords\n");
3149 if (!is_truetype_font_installed("Arial"))
3151 skip("skipping GdiRealizationInfo with truetype font\n");
3152 goto end;
3155 memset(&lf, 0, sizeof(lf));
3156 strcpy(lf.lfFaceName, "Arial");
3157 lf.lfHeight = 20;
3158 lf.lfWeight = FW_NORMAL;
3159 hfont = CreateFontIndirectA(&lf);
3160 hfont_old = SelectObject(hdc, hfont);
3162 memset(info, 0xcc, sizeof(info));
3163 r = pGdiRealizationInfo(hdc, info);
3164 ok(r != 0, "ret 0\n");
3165 ok((info[0] & 0xf) == 3, "info[0] = %x for arial\n", info[0]);
3166 ok(info[3] == 0xcccccccc, "structure longer than 3 dwords\n");
3168 DeleteObject(SelectObject(hdc, hfont_old));
3170 end:
3171 ReleaseDC(0, hdc);
3174 /* Tests on XP SP2 show that the ANSI version of GetTextFace does NOT include
3175 the nul in the count of characters copied when the face name buffer is not
3176 NULL, whereas it does if the buffer is NULL. Further, the Unicode version
3177 always includes it. */
3178 static void test_GetTextFace(void)
3180 static const char faceA[] = "Tahoma";
3181 static const WCHAR faceW[] = {'T','a','h','o','m','a', 0};
3182 LOGFONTA fA = {0};
3183 LOGFONTW fW = {0};
3184 char bufA[LF_FACESIZE];
3185 WCHAR bufW[LF_FACESIZE];
3186 HFONT f, g;
3187 HDC dc;
3188 int n;
3190 if(!is_font_installed("Tahoma"))
3192 skip("Tahoma is not installed so skipping this test\n");
3193 return;
3196 /* 'A' case. */
3197 memcpy(fA.lfFaceName, faceA, sizeof faceA);
3198 f = CreateFontIndirectA(&fA);
3199 ok(f != NULL, "CreateFontIndirectA failed\n");
3201 dc = GetDC(NULL);
3202 g = SelectObject(dc, f);
3203 n = GetTextFaceA(dc, sizeof bufA, bufA);
3204 ok(n == sizeof faceA - 1, "GetTextFaceA returned %d\n", n);
3205 ok(lstrcmpA(faceA, bufA) == 0, "GetTextFaceA\n");
3207 /* Play with the count arg. */
3208 bufA[0] = 'x';
3209 n = GetTextFaceA(dc, 0, bufA);
3210 ok(n == 0, "GetTextFaceA returned %d\n", n);
3211 ok(bufA[0] == 'x', "GetTextFaceA buf[0] == %d\n", bufA[0]);
3213 bufA[0] = 'x';
3214 n = GetTextFaceA(dc, 1, bufA);
3215 ok(n == 0, "GetTextFaceA returned %d\n", n);
3216 ok(bufA[0] == '\0', "GetTextFaceA buf[0] == %d\n", bufA[0]);
3218 bufA[0] = 'x'; bufA[1] = 'y';
3219 n = GetTextFaceA(dc, 2, bufA);
3220 ok(n == 1, "GetTextFaceA returned %d\n", n);
3221 ok(bufA[0] == faceA[0] && bufA[1] == '\0', "GetTextFaceA didn't copy\n");
3223 n = GetTextFaceA(dc, 0, NULL);
3224 ok(n == sizeof faceA ||
3225 broken(n == 0), /* win98, winMe */
3226 "GetTextFaceA returned %d\n", n);
3228 DeleteObject(SelectObject(dc, g));
3229 ReleaseDC(NULL, dc);
3231 /* 'W' case. */
3232 memcpy(fW.lfFaceName, faceW, sizeof faceW);
3233 SetLastError(0xdeadbeef);
3234 f = CreateFontIndirectW(&fW);
3235 if (!f && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
3237 win_skip("CreateFontIndirectW is not implemented\n");
3238 return;
3240 ok(f != NULL, "CreateFontIndirectW failed\n");
3242 dc = GetDC(NULL);
3243 g = SelectObject(dc, f);
3244 n = GetTextFaceW(dc, sizeof bufW / sizeof bufW[0], bufW);
3245 ok(n == sizeof faceW / sizeof faceW[0], "GetTextFaceW returned %d\n", n);
3246 ok(lstrcmpW(faceW, bufW) == 0, "GetTextFaceW\n");
3248 /* Play with the count arg. */
3249 bufW[0] = 'x';
3250 n = GetTextFaceW(dc, 0, bufW);
3251 ok(n == 0, "GetTextFaceW returned %d\n", n);
3252 ok(bufW[0] == 'x', "GetTextFaceW buf[0] == %d\n", bufW[0]);
3254 bufW[0] = 'x';
3255 n = GetTextFaceW(dc, 1, bufW);
3256 ok(n == 1, "GetTextFaceW returned %d\n", n);
3257 ok(bufW[0] == '\0', "GetTextFaceW buf[0] == %d\n", bufW[0]);
3259 bufW[0] = 'x'; bufW[1] = 'y';
3260 n = GetTextFaceW(dc, 2, bufW);
3261 ok(n == 2, "GetTextFaceW returned %d\n", n);
3262 ok(bufW[0] == faceW[0] && bufW[1] == '\0', "GetTextFaceW didn't copy\n");
3264 n = GetTextFaceW(dc, 0, NULL);
3265 ok(n == sizeof faceW / sizeof faceW[0], "GetTextFaceW returned %d\n", n);
3267 DeleteObject(SelectObject(dc, g));
3268 ReleaseDC(NULL, dc);
3271 static void test_orientation(void)
3273 static const char test_str[11] = "Test String";
3274 HDC hdc;
3275 LOGFONTA lf;
3276 HFONT hfont, old_hfont;
3277 SIZE size;
3279 if (!is_truetype_font_installed("Arial"))
3281 skip("Arial is not installed\n");
3282 return;
3285 hdc = CreateCompatibleDC(0);
3286 memset(&lf, 0, sizeof(lf));
3287 lstrcpyA(lf.lfFaceName, "Arial");
3288 lf.lfHeight = 72;
3289 lf.lfOrientation = lf.lfEscapement = 900;
3290 hfont = create_font("orientation", &lf);
3291 old_hfont = SelectObject(hdc, hfont);
3292 ok(GetTextExtentExPointA(hdc, test_str, sizeof(test_str), 32767, NULL, NULL, &size), "GetTextExtentExPointA failed\n");
3293 ok(near_match(311, size.cx), "cx should be about 311, got %d\n", size.cx);
3294 ok(near_match(75, size.cy), "cy should be about 75, got %d\n", size.cy);
3295 SelectObject(hdc, old_hfont);
3296 DeleteObject(hfont);
3297 DeleteDC(hdc);
3300 static void test_oemcharset(void)
3302 HDC hdc;
3303 LOGFONTA lf, clf;
3304 HFONT hfont, old_hfont;
3305 int charset;
3307 hdc = CreateCompatibleDC(0);
3308 ZeroMemory(&lf, sizeof(lf));
3309 lf.lfHeight = 12;
3310 lf.lfCharSet = OEM_CHARSET;
3311 lf.lfPitchAndFamily = FIXED_PITCH | FF_MODERN;
3312 lstrcpyA(lf.lfFaceName, "Terminal");
3313 hfont = CreateFontIndirectA(&lf);
3314 old_hfont = SelectObject(hdc, hfont);
3315 charset = GetTextCharset(hdc);
3316 todo_wine
3317 ok(charset == OEM_CHARSET, "expected %d charset, got %d\n", OEM_CHARSET, charset);
3318 hfont = SelectObject(hdc, old_hfont);
3319 GetObjectA(hfont, sizeof(clf), &clf);
3320 ok(!lstrcmpA(clf.lfFaceName, lf.lfFaceName), "expected %s face name, got %s\n", lf.lfFaceName, clf.lfFaceName);
3321 ok(clf.lfPitchAndFamily == lf.lfPitchAndFamily, "expected %x family, got %x\n", lf.lfPitchAndFamily, clf.lfPitchAndFamily);
3322 ok(clf.lfCharSet == lf.lfCharSet, "expected %d charset, got %d\n", lf.lfCharSet, clf.lfCharSet);
3323 ok(clf.lfHeight == lf.lfHeight, "expected %d height, got %d\n", lf.lfHeight, clf.lfHeight);
3324 DeleteObject(hfont);
3325 DeleteDC(hdc);
3328 static void test_GetGlyphOutline(void)
3330 HDC hdc;
3331 GLYPHMETRICS gm, gm2;
3332 LOGFONTA lf;
3333 HFONT hfont, old_hfont;
3334 INT ret, ret2;
3335 static const struct
3337 UINT cs;
3338 UINT a;
3339 UINT w;
3340 } c[] =
3342 {ANSI_CHARSET, 0x30, 0x30},
3343 {SHIFTJIS_CHARSET, 0x82a0, 0x3042},
3344 {HANGEUL_CHARSET, 0x8141, 0xac02},
3345 {JOHAB_CHARSET, 0x8446, 0x3135},
3346 {GB2312_CHARSET, 0x8141, 0x4e04},
3347 {CHINESEBIG5_CHARSET, 0xa142, 0x3001}
3349 UINT i;
3351 if (!is_truetype_font_installed("Tahoma"))
3353 skip("Tahoma is not installed\n");
3354 return;
3357 hdc = CreateCompatibleDC(0);
3358 memset(&lf, 0, sizeof(lf));
3359 lf.lfHeight = 72;
3360 lstrcpyA(lf.lfFaceName, "Tahoma");
3361 SetLastError(0xdeadbeef);
3362 hfont = CreateFontIndirectA(&lf);
3363 ok(hfont != 0, "CreateFontIndirectA error %u\n", GetLastError());
3364 old_hfont = SelectObject(hdc, hfont);
3366 memset(&gm, 0, sizeof(gm));
3367 SetLastError(0xdeadbeef);
3368 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
3369 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %u\n", GetLastError());
3371 memset(&gm, 0, sizeof(gm));
3372 SetLastError(0xdeadbeef);
3373 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, NULL);
3374 ok(ret == GDI_ERROR, "GetGlyphOutlineA should fail\n");
3375 ok(GetLastError() == 0xdeadbeef ||
3376 GetLastError() == ERROR_INVALID_PARAMETER, /* win98, winMe */
3377 "expected 0xdeadbeef, got %u\n", GetLastError());
3379 memset(&gm, 0, sizeof(gm));
3380 SetLastError(0xdeadbeef);
3381 ret = GetGlyphOutlineW(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
3382 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
3383 ok(ret != GDI_ERROR, "GetGlyphOutlineW error %u\n", GetLastError());
3385 memset(&gm, 0, sizeof(gm));
3386 SetLastError(0xdeadbeef);
3387 ret = GetGlyphOutlineW(hdc, 'A', GGO_METRICS, &gm, 0, NULL, NULL);
3388 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
3390 ok(ret == GDI_ERROR, "GetGlyphOutlineW should fail\n");
3391 ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %u\n", GetLastError());
3394 /* test for needed buffer size request on space char */
3395 memset(&gm, 0, sizeof(gm));
3396 SetLastError(0xdeadbeef);
3397 ret = GetGlyphOutlineW(hdc, ' ', GGO_NATIVE, &gm, 0, NULL, &mat);
3398 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
3399 ok(ret == 0, "GetGlyphOutlineW should return 0 buffer size for space char\n");
3401 /* requesting buffer size for space char + error */
3402 memset(&gm, 0, sizeof(gm));
3403 SetLastError(0xdeadbeef);
3404 ret = GetGlyphOutlineW(0, ' ', GGO_NATIVE, &gm, 0, NULL, NULL);
3405 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
3407 ok(ret == GDI_ERROR, "GetGlyphOutlineW should return GDI_ERROR\n");
3408 ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %u\n", GetLastError());
3411 SelectObject(hdc, old_hfont);
3412 DeleteObject(hfont);
3414 for (i = 0; i < sizeof c / sizeof c[0]; ++i)
3416 lf.lfFaceName[0] = '\0';
3417 lf.lfCharSet = c[i].cs;
3418 lf.lfPitchAndFamily = 0;
3419 if (EnumFontFamiliesEx(hdc, &lf, create_font_proc, (LPARAM)&hfont, 0))
3421 skip("TrueType font for charset %u is not installed\n", c[i].cs);
3422 continue;
3425 old_hfont = SelectObject(hdc, hfont);
3427 /* expected to ignore superfluous bytes (sigle-byte character) */
3428 ret = GetGlyphOutlineA(hdc, 0x8041, GGO_BITMAP, &gm, 0, NULL, &mat);
3429 ret2 = GetGlyphOutlineA(hdc, 0x41, GGO_BITMAP, &gm2, 0, NULL, &mat);
3430 ok(ret == ret2 && memcmp(&gm, &gm2, sizeof gm) == 0, "%d %d\n", ret, ret2);
3432 ret = GetGlyphOutlineA(hdc, 0xcc8041, GGO_BITMAP, &gm, 0, NULL, &mat);
3433 ok(ret == ret2 && memcmp(&gm, &gm2, sizeof gm) == 0,
3434 "Expected to ignore superfluous bytes, got %d %d\n", ret, ret2);
3436 /* expected to ignore superfluous bytes (double-byte character) */
3437 ret = GetGlyphOutlineA(hdc, c[i].a, GGO_BITMAP, &gm, 0, NULL, &mat);
3438 ret2 = GetGlyphOutlineA(hdc, c[i].a | 0xdead0000, GGO_BITMAP, &gm2, 0, NULL, &mat);
3439 ok(ret == ret2 && memcmp(&gm, &gm2, sizeof gm) == 0,
3440 "Expected to ignore superfluous bytes, got %d %d\n", ret, ret2);
3442 /* expected to match wide-char version results */
3443 ret2 = GetGlyphOutlineW(hdc, c[i].w, GGO_BITMAP, &gm2, 0, NULL, &mat);
3444 ok(ret == ret2 && memcmp(&gm, &gm2, sizeof gm) == 0, "%d %d\n", ret, ret2);
3446 hfont = SelectObject(hdc, old_hfont);
3447 DeleteObject(hfont);
3450 DeleteDC(hdc);
3453 /* bug #9995: there is a limit to the character width that can be specified */
3454 static void test_GetTextMetrics2(const char *fontname, int font_height)
3456 HFONT of, hf;
3457 HDC hdc;
3458 TEXTMETRICA tm;
3459 BOOL ret;
3460 int ave_width, height, width, ratio, scale;
3462 if (!is_truetype_font_installed( fontname)) {
3463 skip("%s is not installed\n", fontname);
3464 return;
3466 hdc = CreateCompatibleDC(0);
3467 ok( hdc != NULL, "CreateCompatibleDC failed\n");
3468 /* select width = 0 */
3469 hf = CreateFontA(font_height, 0, 0, 0, FW_REGULAR, FALSE, FALSE, FALSE,
3470 DEFAULT_CHARSET, OUT_TT_PRECIS, CLIP_LH_ANGLES,
3471 DEFAULT_QUALITY, VARIABLE_PITCH,
3472 fontname);
3473 ok( hf != NULL, "CreateFontA(%s, %d) failed\n", fontname, font_height);
3474 of = SelectObject( hdc, hf);
3475 ret = GetTextMetricsA( hdc, &tm);
3476 ok(ret, "GetTextMetricsA error %u\n", GetLastError());
3477 height = tm.tmHeight;
3478 ave_width = tm.tmAveCharWidth;
3479 SelectObject( hdc, of);
3480 DeleteObject( hf);
3482 trace("height %d, ave width %d\n", height, ave_width);
3484 for (width = ave_width * 2; /* nothing*/; width += ave_width)
3486 hf = CreateFont(height, width, 0, 0, FW_REGULAR, FALSE, FALSE, FALSE,
3487 DEFAULT_CHARSET, OUT_TT_PRECIS, CLIP_LH_ANGLES,
3488 DEFAULT_QUALITY, VARIABLE_PITCH, fontname);
3489 ok(hf != 0, "CreateFont failed\n");
3490 of = SelectObject(hdc, hf);
3491 ret = GetTextMetrics(hdc, &tm);
3492 ok(ret, "GetTextMetrics error %u\n", GetLastError());
3493 SelectObject(hdc, of);
3494 DeleteObject(hf);
3496 if (match_off_by_1(tm.tmAveCharWidth, ave_width) || width / height > 200)
3497 break;
3500 DeleteDC(hdc);
3502 ratio = width / height;
3503 scale = width / ave_width;
3505 trace("max width/height ratio (%d / %d) %d, max width scale (%d / %d) %d\n",
3506 width, height, ratio, width, ave_width, scale);
3508 ok(ratio >= 90 && ratio <= 110, "expected width/height ratio 90-110, got %d\n", ratio);
3511 static void test_CreateFontIndirect(void)
3513 LOGFONTA lf, getobj_lf;
3514 int ret, i;
3515 HFONT hfont;
3516 char TestName[][16] = {"Arial", "Arial Bold", "Arial Italic", "Arial Baltic"};
3518 memset(&lf, 0, sizeof(lf));
3519 lf.lfCharSet = ANSI_CHARSET;
3520 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
3521 lf.lfHeight = 16;
3522 lf.lfWidth = 16;
3523 lf.lfQuality = DEFAULT_QUALITY;
3524 lf.lfItalic = FALSE;
3525 lf.lfWeight = FW_DONTCARE;
3527 for (i = 0; i < sizeof(TestName)/sizeof(TestName[0]); i++)
3529 lstrcpyA(lf.lfFaceName, TestName[i]);
3530 hfont = CreateFontIndirectA(&lf);
3531 ok(hfont != 0, "CreateFontIndirectA failed\n");
3532 SetLastError(0xdeadbeef);
3533 ret = GetObject(hfont, sizeof(getobj_lf), &getobj_lf);
3534 ok(ret, "GetObject failed: %d\n", GetLastError());
3535 ok(lf.lfItalic == getobj_lf.lfItalic, "lfItalic: expect %02x got %02x\n", lf.lfItalic, getobj_lf.lfItalic);
3536 ok(lf.lfWeight == getobj_lf.lfWeight ||
3537 broken((SHORT)lf.lfWeight == getobj_lf.lfWeight), /* win9x */
3538 "lfWeight: expect %08x got %08x\n", lf.lfWeight, getobj_lf.lfWeight);
3539 ok(!lstrcmpA(lf.lfFaceName, getobj_lf.lfFaceName) ||
3540 broken(!memcmp(lf.lfFaceName, getobj_lf.lfFaceName, LF_FACESIZE-1)), /* win9x doesn't ensure '\0' termination */
3541 "font names don't match: %s != %s\n", lf.lfFaceName, getobj_lf.lfFaceName);
3542 DeleteObject(hfont);
3546 static void test_CreateFontIndirectEx(void)
3548 ENUMLOGFONTEXDVA lfex;
3549 HFONT hfont;
3551 if (!pCreateFontIndirectExA)
3553 win_skip("CreateFontIndirectExA is not available\n");
3554 return;
3557 if (!is_truetype_font_installed("Arial"))
3559 skip("Arial is not installed\n");
3560 return;
3563 SetLastError(0xdeadbeef);
3564 hfont = pCreateFontIndirectExA(NULL);
3565 ok(hfont == NULL, "got %p\n", hfont);
3566 ok(GetLastError() == 0xdeadbeef, "got error %d\n", GetLastError());
3568 memset(&lfex, 0, sizeof(lfex));
3569 lstrcpyA(lfex.elfEnumLogfontEx.elfLogFont.lfFaceName, "Arial");
3570 hfont = pCreateFontIndirectExA(&lfex);
3571 ok(hfont != 0, "CreateFontIndirectEx failed\n");
3572 if (hfont)
3573 check_font("Arial", &lfex.elfEnumLogfontEx.elfLogFont, hfont);
3574 DeleteObject(hfont);
3577 static void free_font(void *font)
3579 UnmapViewOfFile(font);
3582 static void *load_font(const char *font_name, DWORD *font_size)
3584 char file_name[MAX_PATH];
3585 HANDLE file, mapping;
3586 void *font;
3588 if (!GetWindowsDirectory(file_name, sizeof(file_name))) return NULL;
3589 strcat(file_name, "\\fonts\\");
3590 strcat(file_name, font_name);
3592 file = CreateFile(file_name, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, 0);
3593 if (file == INVALID_HANDLE_VALUE) return NULL;
3595 *font_size = GetFileSize(file, NULL);
3597 mapping = CreateFileMapping(file, NULL, PAGE_READONLY, 0, 0, NULL);
3598 if (!mapping)
3600 CloseHandle(file);
3601 return NULL;
3604 font = MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0);
3606 CloseHandle(file);
3607 CloseHandle(mapping);
3608 return font;
3611 static void test_AddFontMemResource(void)
3613 void *font;
3614 DWORD font_size, num_fonts;
3615 HANDLE ret;
3616 BOOL bRet;
3618 if (!pAddFontMemResourceEx || !pRemoveFontMemResourceEx)
3620 win_skip("AddFontMemResourceEx is not available on this platform\n");
3621 return;
3624 font = load_font("sserife.fon", &font_size);
3625 if (!font)
3627 skip("Unable to locate and load font sserife.fon\n");
3628 return;
3631 SetLastError(0xdeadbeef);
3632 ret = pAddFontMemResourceEx(NULL, 0, NULL, NULL);
3633 ok(!ret, "AddFontMemResourceEx should fail\n");
3634 ok(GetLastError() == ERROR_INVALID_PARAMETER,
3635 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
3636 GetLastError());
3638 SetLastError(0xdeadbeef);
3639 ret = pAddFontMemResourceEx(NULL, 10, NULL, NULL);
3640 ok(!ret, "AddFontMemResourceEx should fail\n");
3641 ok(GetLastError() == ERROR_INVALID_PARAMETER,
3642 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
3643 GetLastError());
3645 SetLastError(0xdeadbeef);
3646 ret = pAddFontMemResourceEx(NULL, 0, NULL, &num_fonts);
3647 ok(!ret, "AddFontMemResourceEx should fail\n");
3648 ok(GetLastError() == ERROR_INVALID_PARAMETER,
3649 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
3650 GetLastError());
3652 SetLastError(0xdeadbeef);
3653 ret = pAddFontMemResourceEx(NULL, 10, NULL, &num_fonts);
3654 ok(!ret, "AddFontMemResourceEx should fail\n");
3655 ok(GetLastError() == ERROR_INVALID_PARAMETER,
3656 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
3657 GetLastError());
3659 SetLastError(0xdeadbeef);
3660 ret = pAddFontMemResourceEx(font, 0, NULL, NULL);
3661 ok(!ret, "AddFontMemResourceEx should fail\n");
3662 ok(GetLastError() == ERROR_INVALID_PARAMETER,
3663 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
3664 GetLastError());
3666 SetLastError(0xdeadbeef);
3667 ret = pAddFontMemResourceEx(font, 10, NULL, NULL);
3668 ok(!ret, "AddFontMemResourceEx should fail\n");
3669 ok(GetLastError() == ERROR_INVALID_PARAMETER,
3670 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
3671 GetLastError());
3673 num_fonts = 0xdeadbeef;
3674 SetLastError(0xdeadbeef);
3675 ret = pAddFontMemResourceEx(font, 0, NULL, &num_fonts);
3676 ok(!ret, "AddFontMemResourceEx should fail\n");
3677 ok(GetLastError() == ERROR_INVALID_PARAMETER,
3678 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
3679 GetLastError());
3680 ok(num_fonts == 0xdeadbeef, "number of loaded fonts should be 0xdeadbeef\n");
3682 if (0) /* hangs under windows 2000 */
3684 num_fonts = 0xdeadbeef;
3685 SetLastError(0xdeadbeef);
3686 ret = pAddFontMemResourceEx(font, 10, NULL, &num_fonts);
3687 ok(!ret, "AddFontMemResourceEx should fail\n");
3688 ok(GetLastError() == 0xdeadbeef,
3689 "Expected GetLastError() to return 0xdeadbeef, got %u\n",
3690 GetLastError());
3691 ok(num_fonts == 0xdeadbeef, "number of loaded fonts should be 0xdeadbeef\n");
3694 num_fonts = 0xdeadbeef;
3695 SetLastError(0xdeadbeef);
3696 ret = pAddFontMemResourceEx(font, font_size, NULL, &num_fonts);
3697 ok(ret != 0, "AddFontMemResourceEx error %d\n", GetLastError());
3698 ok(num_fonts != 0xdeadbeef, "number of loaded fonts should not be 0xdeadbeef\n");
3699 ok(num_fonts != 0, "number of loaded fonts should not be 0\n");
3701 free_font(font);
3703 SetLastError(0xdeadbeef);
3704 bRet = pRemoveFontMemResourceEx(ret);
3705 ok(bRet, "RemoveFontMemResourceEx error %d\n", GetLastError());
3707 /* test invalid pointer to number of loaded fonts */
3708 font = load_font("sserife.fon", &font_size);
3709 ok(font != NULL, "Unable to locate and load font sserife.fon\n");
3711 SetLastError(0xdeadbeef);
3712 ret = pAddFontMemResourceEx(font, font_size, NULL, (void *)0xdeadbeef);
3713 ok(!ret, "AddFontMemResourceEx should fail\n");
3714 ok(GetLastError() == 0xdeadbeef,
3715 "Expected GetLastError() to return 0xdeadbeef, got %u\n",
3716 GetLastError());
3718 SetLastError(0xdeadbeef);
3719 ret = pAddFontMemResourceEx(font, font_size, NULL, NULL);
3720 ok(!ret, "AddFontMemResourceEx should fail\n");
3721 ok(GetLastError() == ERROR_INVALID_PARAMETER,
3722 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
3723 GetLastError());
3725 free_font(font);
3728 static INT CALLBACK enum_fonts_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lparam)
3730 LOGFONT *lf;
3732 if (type != TRUETYPE_FONTTYPE) return 1;
3734 ok(ntm->tmWeight == elf->lfWeight, "expected %d got %d\n", ntm->tmWeight, elf->lfWeight);
3736 lf = (LOGFONT *)lparam;
3737 *lf = *elf;
3738 return 0;
3741 static INT CALLBACK enum_all_fonts_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lparam)
3743 int ret;
3744 LOGFONT *lf;
3746 if (type != TRUETYPE_FONTTYPE) return 1;
3748 lf = (LOGFONT *)lparam;
3749 ret = strcmp(lf->lfFaceName, elf->lfFaceName);
3750 if(ret == 0)
3752 ok(ntm->tmWeight == elf->lfWeight, "expected %d got %d\n", ntm->tmWeight, elf->lfWeight);
3753 *lf = *elf;
3754 return 0;
3756 return 1;
3759 static INT CALLBACK enum_with_magic_retval_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lparam)
3761 return lparam;
3764 static void test_EnumFonts(void)
3766 int ret;
3767 LOGFONT lf;
3768 HDC hdc;
3770 if (!is_truetype_font_installed("Arial"))
3772 skip("Arial is not installed\n");
3773 return;
3776 /* Windows uses localized font face names, so Arial Bold won't be found */
3777 if (PRIMARYLANGID(GetUserDefaultLangID()) != LANG_ENGLISH)
3779 skip("User locale is not English, skipping the test\n");
3780 return;
3783 hdc = CreateCompatibleDC(0);
3785 /* check that the enumproc's retval is returned */
3786 ret = EnumFontFamilies(hdc, NULL, enum_with_magic_retval_proc, 0xcafe);
3787 ok(ret == 0xcafe, "got %08x\n", ret);
3789 ret = EnumFontFamilies(hdc, "Arial", enum_fonts_proc, (LPARAM)&lf);
3790 ok(!ret, "font Arial is not enumerated\n");
3791 ret = strcmp(lf.lfFaceName, "Arial");
3792 ok(!ret, "expected Arial got %s\n", lf.lfFaceName);
3793 ok(lf.lfWeight == FW_NORMAL, "expected FW_NORMAL got %d\n", lf.lfWeight);
3795 lstrcpy(lf.lfFaceName, "Arial");
3796 ret = EnumFontFamilies(hdc, NULL, enum_all_fonts_proc, (LPARAM)&lf);
3797 ok(!ret, "font Arial is not enumerated\n");
3798 ret = strcmp(lf.lfFaceName, "Arial");
3799 ok(!ret, "expected Arial got %s\n", lf.lfFaceName);
3800 ok(lf.lfWeight == FW_NORMAL, "expected FW_NORMAL got %d\n", lf.lfWeight);
3802 ret = EnumFontFamilies(hdc, "Arial Bold", enum_fonts_proc, (LPARAM)&lf);
3803 ok(!ret, "font Arial Bold is not enumerated\n");
3804 ret = strcmp(lf.lfFaceName, "Arial");
3805 ok(!ret, "expected Arial got %s\n", lf.lfFaceName);
3806 ok(lf.lfWeight == FW_BOLD, "expected FW_BOLD got %d\n", lf.lfWeight);
3808 lstrcpy(lf.lfFaceName, "Arial Bold");
3809 ret = EnumFontFamilies(hdc, NULL, enum_all_fonts_proc, (LPARAM)&lf);
3810 ok(ret, "font Arial Bold should not be enumerated\n");
3812 ret = EnumFontFamilies(hdc, "Arial Bold Italic", enum_fonts_proc, (LPARAM)&lf);
3813 ok(!ret, "font Arial Bold Italic is not enumerated\n");
3814 ret = strcmp(lf.lfFaceName, "Arial");
3815 ok(!ret, "expected Arial got %s\n", lf.lfFaceName);
3816 ok(lf.lfWeight == FW_BOLD, "expected FW_BOLD got %d\n", lf.lfWeight);
3818 lstrcpy(lf.lfFaceName, "Arial Bold Italic");
3819 ret = EnumFontFamilies(hdc, NULL, enum_all_fonts_proc, (LPARAM)&lf);
3820 ok(ret, "font Arial Bold Italic should not be enumerated\n");
3822 ret = EnumFontFamilies(hdc, "Arial Italic Bold", enum_fonts_proc, (LPARAM)&lf);
3823 ok(ret, "font Arial Italic Bold should not be enumerated\n");
3825 lstrcpy(lf.lfFaceName, "Arial Italic Bold");
3826 ret = EnumFontFamilies(hdc, NULL, enum_all_fonts_proc, (LPARAM)&lf);
3827 ok(ret, "font Arial Italic Bold should not be enumerated\n");
3829 DeleteDC(hdc);
3832 static INT CALLBACK is_font_installed_fullname_proc(const LOGFONT *lf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
3834 const ENUMLOGFONT *elf = (const ENUMLOGFONT *)lf;
3835 const char *fullname = (const char *)lParam;
3837 if (!strcmp((const char *)elf->elfFullName, fullname)) return 0;
3839 return 1;
3842 static BOOL is_font_installed_fullname(const char *family, const char *fullname)
3844 HDC hdc = GetDC(0);
3845 BOOL ret = FALSE;
3847 if(!EnumFontFamiliesA(hdc, family, is_font_installed_fullname_proc, (LPARAM)fullname))
3848 ret = TRUE;
3850 ReleaseDC(0, hdc);
3851 return ret;
3854 static void test_fullname(void)
3856 static const char *TestName[] = {"Lucida Sans Demibold Roman", "Lucida Sans Italic", "Lucida Sans Regular"};
3857 char buf[LF_FULLFACESIZE];
3858 HFONT hfont, of;
3859 LOGFONTA lf;
3860 HDC hdc;
3861 int i;
3863 hdc = CreateCompatibleDC(0);
3864 ok(hdc != NULL, "CreateCompatibleDC failed\n");
3866 memset(&lf, 0, sizeof(lf));
3867 lf.lfCharSet = ANSI_CHARSET;
3868 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
3869 lf.lfHeight = 16;
3870 lf.lfWidth = 16;
3871 lf.lfQuality = DEFAULT_QUALITY;
3872 lf.lfItalic = FALSE;
3873 lf.lfWeight = FW_DONTCARE;
3875 for (i = 0; i < sizeof(TestName) / sizeof(TestName[0]); i++)
3877 if (!is_font_installed_fullname("Lucida Sans", TestName[i]))
3879 skip("%s is not installed\n", TestName[i]);
3880 continue;
3883 lstrcpyA(lf.lfFaceName, TestName[i]);
3884 hfont = CreateFontIndirectA(&lf);
3885 ok(hfont != 0, "CreateFontIndirectA failed\n");
3887 of = SelectObject(hdc, hfont);
3888 buf[0] = 0;
3889 ok(get_ttf_nametable_entry(hdc, TT_NAME_ID_FULL_NAME, buf, sizeof(buf)),
3890 "face full name could not be read\n");
3891 ok(!lstrcmpA(buf, TestName[i]), "font full names don't match: %s != %s\n", TestName[i], buf);
3892 SelectObject(hdc, of);
3893 DeleteObject(hfont);
3895 DeleteDC(hdc);
3898 static BOOL write_ttf_file(const char *fontname, char *tmp_name)
3900 char tmp_path[MAX_PATH];
3901 HRSRC rsrc;
3902 void *rsrc_data;
3903 DWORD rsrc_size;
3904 HANDLE hfile;
3905 BOOL ret;
3907 SetLastError(0xdeadbeef);
3908 rsrc = FindResource(GetModuleHandle(0), fontname, RT_RCDATA);
3909 ok(rsrc != 0, "FindResource error %d\n", GetLastError());
3910 if (!rsrc) return FALSE;
3911 SetLastError(0xdeadbeef);
3912 rsrc_data = LockResource(LoadResource(GetModuleHandle(0), rsrc));
3913 ok(rsrc_data != 0, "LockResource error %d\n", GetLastError());
3914 if (!rsrc_data) return FALSE;
3915 SetLastError(0xdeadbeef);
3916 rsrc_size = SizeofResource(GetModuleHandle(0), rsrc);
3917 ok(rsrc_size != 0, "SizeofResource error %d\n", GetLastError());
3918 if (!rsrc_size) return FALSE;
3920 SetLastError(0xdeadbeef);
3921 ret = GetTempPath(MAX_PATH, tmp_path);
3922 ok(ret, "GetTempPath() error %d\n", GetLastError());
3923 SetLastError(0xdeadbeef);
3924 ret = GetTempFileName(tmp_path, "ttf", 0, tmp_name);
3925 ok(ret, "GetTempFileName() error %d\n", GetLastError());
3927 SetLastError(0xdeadbeef);
3928 hfile = CreateFile(tmp_name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
3929 ok(hfile != INVALID_HANDLE_VALUE, "CreateFile() error %d\n", GetLastError());
3930 if (hfile == INVALID_HANDLE_VALUE) return FALSE;
3932 SetLastError(0xdeadbeef);
3933 ret = WriteFile(hfile, rsrc_data, rsrc_size, &rsrc_size, NULL);
3934 ok(ret, "WriteFile() error %d\n", GetLastError());
3936 CloseHandle(hfile);
3937 return ret;
3940 static void test_CreateScalableFontResource(void)
3942 char ttf_name[MAX_PATH];
3943 char tmp_path[MAX_PATH];
3944 char fot_name[MAX_PATH];
3945 char *file_part;
3946 DWORD ret;
3948 if (!pAddFontResourceExA || !pRemoveFontResourceExA)
3950 win_skip("AddFontResourceExA is not available on this platform\n");
3951 return;
3954 if (!write_ttf_file("wine_test.ttf", ttf_name))
3956 skip("Failed to create ttf file for testing\n");
3957 return;
3960 trace("created %s\n", ttf_name);
3962 ret = is_truetype_font_installed("wine_test");
3963 ok(!ret, "font wine_test should not be enumerated\n");
3965 ret = GetTempPath(MAX_PATH, tmp_path);
3966 ok(ret, "GetTempPath() error %d\n", GetLastError());
3967 ret = GetTempFileName(tmp_path, "fot", 0, fot_name);
3968 ok(ret, "GetTempFileName() error %d\n", GetLastError());
3970 ret = GetFileAttributes(fot_name);
3971 ok(ret != INVALID_FILE_ATTRIBUTES, "file %s does not exist\n", fot_name);
3973 SetLastError(0xdeadbeef);
3974 ret = CreateScalableFontResource(0, fot_name, ttf_name, NULL);
3975 ok(!ret, "CreateScalableFontResource() should fail\n");
3976 ok(GetLastError() == ERROR_FILE_EXISTS, "not expected error %d\n", GetLastError());
3978 SetLastError(0xdeadbeef);
3979 ret = CreateScalableFontResource(0, fot_name, ttf_name, "");
3980 ok(!ret, "CreateScalableFontResource() should fail\n");
3981 ok(GetLastError() == ERROR_FILE_EXISTS, "not expected error %d\n", GetLastError());
3983 file_part = strrchr(ttf_name, '\\');
3984 SetLastError(0xdeadbeef);
3985 ret = CreateScalableFontResource(0, fot_name, file_part, tmp_path);
3986 ok(!ret, "CreateScalableFontResource() should fail\n");
3987 ok(GetLastError() == ERROR_FILE_EXISTS, "not expected error %d\n", GetLastError());
3989 SetLastError(0xdeadbeef);
3990 ret = CreateScalableFontResource(0, fot_name, "random file name", tmp_path);
3991 ok(!ret, "CreateScalableFontResource() should fail\n");
3992 todo_wine
3993 ok(GetLastError() == ERROR_INVALID_PARAMETER, "not expected error %d\n", GetLastError());
3995 SetLastError(0xdeadbeef);
3996 ret = CreateScalableFontResource(0, fot_name, NULL, ttf_name);
3997 ok(!ret, "CreateScalableFontResource() should fail\n");
3998 todo_wine
3999 ok(GetLastError() == ERROR_INVALID_PARAMETER, "not expected error %d\n", GetLastError());
4001 ret = DeleteFile(fot_name);
4002 ok(ret, "DeleteFile() error %d\n", GetLastError());
4004 ret = pRemoveFontResourceExA(fot_name, 0, 0);
4005 todo_wine
4006 ok(!ret, "RemoveFontResourceEx() should fail\n");
4008 /* FIXME: since CreateScalableFontResource is a stub further testing is impossible */
4009 if (ret) return;
4011 /* test public font resource */
4012 SetLastError(0xdeadbeef);
4013 ret = CreateScalableFontResource(0, fot_name, ttf_name, NULL);
4014 ok(ret, "CreateScalableFontResource() error %d\n", GetLastError());
4016 ret = is_truetype_font_installed("wine_test");
4017 ok(!ret, "font wine_test should not be enumerated\n");
4019 SetLastError(0xdeadbeef);
4020 ret = pAddFontResourceExA(fot_name, 0, 0);
4021 ok(ret, "AddFontResourceEx() error %d\n", GetLastError());
4023 ret = is_truetype_font_installed("wine_test");
4024 ok(ret, "font wine_test should be enumerated\n");
4026 ret = pRemoveFontResourceExA(fot_name, FR_PRIVATE, 0);
4027 ok(!ret, "RemoveFontResourceEx() with not matching flags should fail\n");
4029 SetLastError(0xdeadbeef);
4030 ret = pRemoveFontResourceExA(fot_name, 0, 0);
4031 todo_wine
4032 ok(ret, "RemoveFontResourceEx() error %d\n", GetLastError());
4034 ret = is_truetype_font_installed("wine_test");
4035 todo_wine
4036 ok(!ret, "font wine_test should not be enumerated\n");
4038 /* FIXME: since RemoveFontResource is a stub correct testing is impossible */
4039 if (ret)
4041 /* remove once RemoveFontResource is implemented */
4042 DeleteFile(fot_name);
4043 DeleteFile(ttf_name);
4044 return;
4047 ret = pRemoveFontResourceExA(fot_name, 0, 0);
4048 ok(!ret, "RemoveFontResourceEx() should fail\n");
4050 DeleteFile(fot_name);
4052 /* test hidden font resource */
4053 SetLastError(0xdeadbeef);
4054 ret = CreateScalableFontResource(1, fot_name, ttf_name, NULL);
4055 ok(ret, "CreateScalableFontResource() error %d\n", GetLastError());
4057 ret = is_truetype_font_installed("wine_test");
4058 ok(!ret, "font wine_test should not be enumerated\n");
4060 SetLastError(0xdeadbeef);
4061 ret = pAddFontResourceExA(fot_name, 0, 0);
4062 ok(ret, "AddFontResourceEx() error %d\n", GetLastError());
4064 ret = is_truetype_font_installed("wine_test");
4065 ok(!ret, "font wine_test should not be enumerated\n");
4067 /* XP allows removing a private font added with 0 flags */
4068 SetLastError(0xdeadbeef);
4069 ret = pRemoveFontResourceExA(fot_name, FR_PRIVATE, 0);
4070 ok(ret, "RemoveFontResourceEx() error %d\n", GetLastError());
4072 ret = is_truetype_font_installed("wine_test");
4073 ok(!ret, "font wine_test should not be enumerated\n");
4075 ret = pRemoveFontResourceExA(fot_name, 0, 0);
4076 ok(!ret, "RemoveFontResourceEx() should fail\n");
4078 DeleteFile(fot_name);
4079 DeleteFile(ttf_name);
4082 static void check_vertical_font(const char *name, BOOL *installed, BOOL *selected, GLYPHMETRICS *gm, WORD *gi)
4084 LOGFONTA lf;
4085 HFONT hfont, hfont_prev;
4086 HDC hdc;
4087 char facename[100];
4088 DWORD ret;
4089 static const WCHAR str[] = { 0x2025 };
4091 *installed = is_truetype_font_installed(name);
4093 lf.lfHeight = -18;
4094 lf.lfWidth = 0;
4095 lf.lfEscapement = 0;
4096 lf.lfOrientation = 0;
4097 lf.lfWeight = FW_DONTCARE;
4098 lf.lfItalic = 0;
4099 lf.lfUnderline = 0;
4100 lf.lfStrikeOut = 0;
4101 lf.lfCharSet = DEFAULT_CHARSET;
4102 lf.lfOutPrecision = OUT_TT_ONLY_PRECIS;
4103 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
4104 lf.lfQuality = DEFAULT_QUALITY;
4105 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
4106 strcpy(lf.lfFaceName, name);
4108 hfont = CreateFontIndirectA(&lf);
4109 ok(hfont != NULL, "CreateFontIndirectA failed\n");
4111 hdc = GetDC(NULL);
4113 hfont_prev = SelectObject(hdc, hfont);
4114 ok(hfont_prev != NULL, "SelectObject failed\n");
4116 ret = GetTextFaceA(hdc, sizeof facename, facename);
4117 ok(ret, "GetTextFaceA failed\n");
4118 *selected = !strcmp(facename, name);
4120 ret = GetGlyphOutlineW(hdc, 0x2025, GGO_METRICS, gm, 0, NULL, &mat);
4121 ok(ret != GDI_ERROR, "GetGlyphOutlineW failed\n");
4122 if (!*selected)
4123 memset(gm, 0, sizeof *gm);
4125 ret = pGetGlyphIndicesW(hdc, str, 1, gi, 0);
4126 ok(ret != GDI_ERROR, "GetGlyphIndicesW failed\n");
4128 SelectObject(hdc, hfont_prev);
4129 DeleteObject(hfont);
4130 ReleaseDC(NULL, hdc);
4133 static void test_vertical_font(void)
4135 char ttf_name[MAX_PATH];
4136 int num;
4137 BOOL ret, installed, selected;
4138 GLYPHMETRICS gm;
4139 WORD hgi, vgi;
4141 if (!pAddFontResourceExA || !pRemoveFontResourceExA || !pGetGlyphIndicesW)
4143 win_skip("AddFontResourceExA or GetGlyphIndicesW is not available on this platform\n");
4144 return;
4147 if (!write_ttf_file("vertical.ttf", ttf_name))
4149 skip("Failed to create ttf file for testing\n");
4150 return;
4153 num = pAddFontResourceExA(ttf_name, FR_PRIVATE, 0);
4154 ok(num == 2, "AddFontResourceExA should add 2 fonts from vertical.ttf\n");
4156 check_vertical_font("@WineTestVertical", &installed, &selected, &gm, &hgi);
4157 ok(installed, "@WineTestVertical is not installed\n");
4158 ok(selected, "@WineTestVertical is not selected\n");
4159 ok(gm.gmBlackBoxX > gm.gmBlackBoxY,
4160 "gmBlackBoxX(%u) should be greater than gmBlackBoxY(%u) if horizontal\n",
4161 gm.gmBlackBoxX, gm.gmBlackBoxY);
4163 check_vertical_font("@@WineTestVertical", &installed, &selected, &gm, &vgi);
4164 ok(installed, "@@WineTestVertical is not installed\n");
4165 ok(selected, "@@WineTestVertical is not selected\n");
4166 ok(gm.gmBlackBoxX < gm.gmBlackBoxY,
4167 "gmBlackBoxX(%u) should be less than gmBlackBoxY(%u) if vertical\n",
4168 gm.gmBlackBoxX, gm.gmBlackBoxY);
4170 ok(hgi == vgi, "different glyph h:%u v:%u\n", hgi, vgi);
4172 ret = pRemoveFontResourceExA(ttf_name, FR_PRIVATE, 0);
4173 ok(ret, "RemoveFontResourceEx() error %d\n", GetLastError());
4175 DeleteFile(ttf_name);
4178 static INT CALLBACK has_vertical_font_proc(const LOGFONT *lf, const TEXTMETRIC *ntm,
4179 DWORD type, LPARAM lParam)
4181 if (lf->lfFaceName[0] == '@') {
4182 return 0;
4184 return 1;
4187 static void test_east_asian_font_selection(void)
4189 HDC hdc;
4190 UINT charset[] = { SHIFTJIS_CHARSET, HANGEUL_CHARSET, JOHAB_CHARSET,
4191 GB2312_CHARSET, CHINESEBIG5_CHARSET };
4192 size_t i;
4194 hdc = GetDC(NULL);
4196 for (i = 0; i < sizeof(charset)/sizeof(charset[0]); i++)
4198 LOGFONTA lf;
4199 HFONT hfont;
4200 char face_name[LF_FACESIZE];
4201 int ret;
4203 memset(&lf, 0, sizeof lf);
4204 lf.lfFaceName[0] = '\0';
4205 lf.lfCharSet = charset[i];
4207 if (EnumFontFamiliesEx(hdc, &lf, has_vertical_font_proc, 0, 0))
4209 skip("Vertical font for charset %u is not installed\n", charset[i]);
4210 continue;
4213 hfont = CreateFontIndirectA(&lf);
4214 hfont = SelectObject(hdc, hfont);
4215 memset(face_name, 0, sizeof face_name);
4216 ret = GetTextFaceA(hdc, sizeof face_name, face_name);
4217 ok(ret && face_name[0] != '@',
4218 "expected non-vertical face for charset %u, got %s\n", charset[i], face_name);
4219 DeleteObject(SelectObject(hdc, hfont));
4221 memset(&lf, 0, sizeof lf);
4222 strcpy(lf.lfFaceName, "@");
4223 lf.lfCharSet = charset[i];
4224 hfont = CreateFontIndirectA(&lf);
4225 hfont = SelectObject(hdc, hfont);
4226 memset(face_name, 0, sizeof face_name);
4227 ret = GetTextFaceA(hdc, sizeof face_name, face_name);
4228 ok(ret && face_name[0] == '@',
4229 "expected vertical face for charset %u, got %s\n", charset[i], face_name);
4230 DeleteObject(SelectObject(hdc, hfont));
4232 ReleaseDC(NULL, hdc);
4235 START_TEST(font)
4237 init();
4239 test_logfont();
4240 test_bitmap_font();
4241 test_outline_font();
4242 test_bitmap_font_metrics();
4243 test_GdiGetCharDimensions();
4244 test_GetCharABCWidths();
4245 test_text_extents();
4246 test_GetGlyphIndices();
4247 test_GetKerningPairs();
4248 test_GetOutlineTextMetrics();
4249 test_SetTextJustification();
4250 test_font_charset();
4251 test_GetFontUnicodeRanges();
4252 test_nonexistent_font();
4253 test_orientation();
4254 test_height_selection();
4255 test_AddFontMemResource();
4256 test_EnumFonts();
4258 /* On Windows Arial has a lot of default charset aliases such as Arial Cyr,
4259 * I'd like to avoid them in this test.
4261 test_EnumFontFamilies("Arial Black", ANSI_CHARSET);
4262 test_EnumFontFamilies("Symbol", SYMBOL_CHARSET);
4263 if (is_truetype_font_installed("Arial Black") &&
4264 (is_truetype_font_installed("Symbol") || is_truetype_font_installed("Wingdings")))
4266 test_EnumFontFamilies("", ANSI_CHARSET);
4267 test_EnumFontFamilies("", SYMBOL_CHARSET);
4268 test_EnumFontFamilies("", DEFAULT_CHARSET);
4270 else
4271 skip("Arial Black or Symbol/Wingdings is not installed\n");
4272 test_EnumFontFamiliesEx_default_charset();
4273 test_GetTextMetrics();
4274 test_GdiRealizationInfo();
4275 test_GetTextFace();
4276 test_GetGlyphOutline();
4277 test_GetTextMetrics2("Tahoma", -11);
4278 test_GetTextMetrics2("Tahoma", -55);
4279 test_GetTextMetrics2("Tahoma", -110);
4280 test_GetTextMetrics2("Arial", -11);
4281 test_GetTextMetrics2("Arial", -55);
4282 test_GetTextMetrics2("Arial", -110);
4283 test_CreateFontIndirect();
4284 test_CreateFontIndirectEx();
4285 test_oemcharset();
4286 test_fullname();
4287 test_east_asian_font_selection();
4289 /* These tests should be last test until RemoveFontResource
4290 * is properly implemented.
4292 test_vertical_font();
4293 test_CreateScalableFontResource();