gdi32: Fix character code metrics for bitmap fonts.
[wine/wine-gecko.git] / dlls / gdi32 / tests / font.c
blob3d245553f79b7f60dabf5cdad79d7eb851360576
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 DWORD (WINAPI *pGetFontUnicodeRanges)(HDC hdc, LPGLYPHSET lpgs);
45 static DWORD (WINAPI *pGetGlyphIndicesA)(HDC hdc, LPCSTR lpstr, INT count, LPWORD pgi, DWORD flags);
46 static DWORD (WINAPI *pGetGlyphIndicesW)(HDC hdc, LPCWSTR lpstr, INT count, LPWORD pgi, DWORD flags);
47 static BOOL (WINAPI *pGdiRealizationInfo)(HDC hdc, DWORD *);
48 static HFONT (WINAPI *pCreateFontIndirectExA)(const ENUMLOGFONTEXDV *);
49 static HANDLE (WINAPI *pAddFontMemResourceEx)(PVOID, DWORD, PVOID, DWORD *);
50 static BOOL (WINAPI *pRemoveFontMemResourceEx)(HANDLE);
51 static INT (WINAPI *pAddFontResourceExA)(LPCSTR, DWORD, PVOID);
52 static BOOL (WINAPI *pRemoveFontResourceExA)(LPCSTR, DWORD, PVOID);
54 static HMODULE hgdi32 = 0;
55 static const MAT2 mat = { {0,1}, {0,0}, {0,0}, {0,1} };
57 static void init(void)
59 hgdi32 = GetModuleHandleA("gdi32.dll");
61 pGdiGetCharDimensions = (void *)GetProcAddress(hgdi32, "GdiGetCharDimensions");
62 pGdiGetCodePage = (void *) GetProcAddress(hgdi32,"GdiGetCodePage");
63 pGetCharABCWidthsI = (void *)GetProcAddress(hgdi32, "GetCharABCWidthsI");
64 pGetCharABCWidthsA = (void *)GetProcAddress(hgdi32, "GetCharABCWidthsA");
65 pGetCharABCWidthsW = (void *)GetProcAddress(hgdi32, "GetCharABCWidthsW");
66 pGetFontUnicodeRanges = (void *)GetProcAddress(hgdi32, "GetFontUnicodeRanges");
67 pGetGlyphIndicesA = (void *)GetProcAddress(hgdi32, "GetGlyphIndicesA");
68 pGetGlyphIndicesW = (void *)GetProcAddress(hgdi32, "GetGlyphIndicesW");
69 pGdiRealizationInfo = (void *)GetProcAddress(hgdi32, "GdiRealizationInfo");
70 pCreateFontIndirectExA = (void *)GetProcAddress(hgdi32, "CreateFontIndirectExA");
71 pAddFontMemResourceEx = (void *)GetProcAddress(hgdi32, "AddFontMemResourceEx");
72 pRemoveFontMemResourceEx = (void *)GetProcAddress(hgdi32, "RemoveFontMemResourceEx");
73 pAddFontResourceExA = (void *)GetProcAddress(hgdi32, "AddFontResourceExA");
74 pRemoveFontResourceExA = (void *)GetProcAddress(hgdi32, "RemoveFontResourceExA");
77 static INT CALLBACK is_truetype_font_installed_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
79 if (type != TRUETYPE_FONTTYPE) return 1;
81 return 0;
84 static BOOL is_truetype_font_installed(const char *name)
86 HDC hdc = GetDC(0);
87 BOOL ret = FALSE;
89 if (!EnumFontFamiliesA(hdc, name, is_truetype_font_installed_proc, 0))
90 ret = TRUE;
92 ReleaseDC(0, hdc);
93 return ret;
96 static INT CALLBACK is_font_installed_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
98 return 0;
101 static BOOL is_font_installed(const char *name)
103 HDC hdc = GetDC(0);
104 BOOL ret = FALSE;
106 if(!EnumFontFamiliesA(hdc, name, is_font_installed_proc, 0))
107 ret = TRUE;
109 ReleaseDC(0, hdc);
110 return ret;
113 static void check_font(const char* test, const LOGFONTA* lf, HFONT hfont)
115 LOGFONTA getobj_lf;
116 int ret, minlen = 0;
118 if (!hfont)
119 return;
121 ret = GetObject(hfont, sizeof(getobj_lf), &getobj_lf);
122 /* NT4 tries to be clever and only returns the minimum length */
123 while (lf->lfFaceName[minlen] && minlen < LF_FACESIZE-1)
124 minlen++;
125 minlen += FIELD_OFFSET(LOGFONTA, lfFaceName) + 1;
126 ok(ret == sizeof(LOGFONTA) || ret == minlen, "%s: GetObject returned %d\n", test, ret);
127 ok(lf->lfHeight == getobj_lf.lfHeight ||
128 broken((SHORT)lf->lfHeight == getobj_lf.lfHeight), /* win9x */
129 "lfHeight: expect %08x got %08x\n", lf->lfHeight, getobj_lf.lfHeight);
130 ok(lf->lfWidth == getobj_lf.lfWidth ||
131 broken((SHORT)lf->lfWidth == getobj_lf.lfWidth), /* win9x */
132 "lfWidth: expect %08x got %08x\n", lf->lfWidth, getobj_lf.lfWidth);
133 ok(lf->lfEscapement == getobj_lf.lfEscapement ||
134 broken((SHORT)lf->lfEscapement == getobj_lf.lfEscapement), /* win9x */
135 "lfEscapement: expect %08x got %08x\n", lf->lfEscapement, getobj_lf.lfEscapement);
136 ok(lf->lfOrientation == getobj_lf.lfOrientation ||
137 broken((SHORT)lf->lfOrientation == getobj_lf.lfOrientation), /* win9x */
138 "lfOrientation: expect %08x got %08x\n", lf->lfOrientation, getobj_lf.lfOrientation);
139 ok(lf->lfWeight == getobj_lf.lfWeight ||
140 broken((SHORT)lf->lfWeight == getobj_lf.lfWeight), /* win9x */
141 "lfWeight: expect %08x got %08x\n", lf->lfWeight, getobj_lf.lfWeight);
142 ok(lf->lfItalic == getobj_lf.lfItalic, "lfItalic: expect %02x got %02x\n", lf->lfItalic, getobj_lf.lfItalic);
143 ok(lf->lfUnderline == getobj_lf.lfUnderline, "lfUnderline: expect %02x got %02x\n", lf->lfUnderline, getobj_lf.lfUnderline);
144 ok(lf->lfStrikeOut == getobj_lf.lfStrikeOut, "lfStrikeOut: expect %02x got %02x\n", lf->lfStrikeOut, getobj_lf.lfStrikeOut);
145 ok(lf->lfCharSet == getobj_lf.lfCharSet, "lfCharSet: expect %02x got %02x\n", lf->lfCharSet, getobj_lf.lfCharSet);
146 ok(lf->lfOutPrecision == getobj_lf.lfOutPrecision, "lfOutPrecision: expect %02x got %02x\n", lf->lfOutPrecision, getobj_lf.lfOutPrecision);
147 ok(lf->lfClipPrecision == getobj_lf.lfClipPrecision, "lfClipPrecision: expect %02x got %02x\n", lf->lfClipPrecision, getobj_lf.lfClipPrecision);
148 ok(lf->lfQuality == getobj_lf.lfQuality, "lfQuality: expect %02x got %02x\n", lf->lfQuality, getobj_lf.lfQuality);
149 ok(lf->lfPitchAndFamily == getobj_lf.lfPitchAndFamily, "lfPitchAndFamily: expect %02x got %02x\n", lf->lfPitchAndFamily, getobj_lf.lfPitchAndFamily);
150 ok(!lstrcmpA(lf->lfFaceName, getobj_lf.lfFaceName) ||
151 broken(!memcmp(lf->lfFaceName, getobj_lf.lfFaceName, LF_FACESIZE-1)), /* win9x doesn't ensure '\0' termination */
152 "%s: font names don't match: %s != %s\n", test, lf->lfFaceName, getobj_lf.lfFaceName);
155 static HFONT create_font(const char* test, const LOGFONTA* lf)
157 HFONT hfont = CreateFontIndirectA(lf);
158 ok(hfont != 0, "%s: CreateFontIndirect failed\n", test);
159 if (hfont)
160 check_font(test, lf, hfont);
161 return hfont;
164 static void test_logfont(void)
166 LOGFONTA lf;
167 HFONT hfont;
169 memset(&lf, 0, sizeof lf);
171 lf.lfCharSet = ANSI_CHARSET;
172 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
173 lf.lfWeight = FW_DONTCARE;
174 lf.lfHeight = 16;
175 lf.lfWidth = 16;
176 lf.lfQuality = DEFAULT_QUALITY;
178 lstrcpyA(lf.lfFaceName, "Arial");
179 hfont = create_font("Arial", &lf);
180 DeleteObject(hfont);
182 memset(&lf, 'A', sizeof(lf));
183 hfont = CreateFontIndirectA(&lf);
184 ok(hfont != 0, "CreateFontIndirectA with strange LOGFONT failed\n");
186 lf.lfFaceName[LF_FACESIZE - 1] = 0;
187 check_font("AAA...", &lf, hfont);
188 DeleteObject(hfont);
191 static INT CALLBACK font_enum_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
193 if (type & RASTER_FONTTYPE)
195 LOGFONT *lf = (LOGFONT *)lParam;
196 *lf = *elf;
197 return 0; /* stop enumeration */
200 return 1; /* continue enumeration */
203 static void compare_tm(const TEXTMETRICA *tm, const TEXTMETRICA *otm)
205 ok(tm->tmHeight == otm->tmHeight, "tmHeight %d != %d\n", tm->tmHeight, otm->tmHeight);
206 ok(tm->tmAscent == otm->tmAscent, "tmAscent %d != %d\n", tm->tmAscent, otm->tmAscent);
207 ok(tm->tmDescent == otm->tmDescent, "tmDescent %d != %d\n", tm->tmDescent, otm->tmDescent);
208 ok(tm->tmInternalLeading == otm->tmInternalLeading, "tmInternalLeading %d != %d\n", tm->tmInternalLeading, otm->tmInternalLeading);
209 ok(tm->tmExternalLeading == otm->tmExternalLeading, "tmExternalLeading %d != %d\n", tm->tmExternalLeading, otm->tmExternalLeading);
210 ok(tm->tmAveCharWidth == otm->tmAveCharWidth, "tmAveCharWidth %d != %d\n", tm->tmAveCharWidth, otm->tmAveCharWidth);
211 ok(tm->tmMaxCharWidth == otm->tmMaxCharWidth, "tmMaxCharWidth %d != %d\n", tm->tmMaxCharWidth, otm->tmMaxCharWidth);
212 ok(tm->tmWeight == otm->tmWeight, "tmWeight %d != %d\n", tm->tmWeight, otm->tmWeight);
213 ok(tm->tmOverhang == otm->tmOverhang, "tmOverhang %d != %d\n", tm->tmOverhang, otm->tmOverhang);
214 ok(tm->tmDigitizedAspectX == otm->tmDigitizedAspectX, "tmDigitizedAspectX %d != %d\n", tm->tmDigitizedAspectX, otm->tmDigitizedAspectX);
215 ok(tm->tmDigitizedAspectY == otm->tmDigitizedAspectY, "tmDigitizedAspectY %d != %d\n", tm->tmDigitizedAspectY, otm->tmDigitizedAspectY);
216 ok(tm->tmFirstChar == otm->tmFirstChar, "tmFirstChar %d != %d\n", tm->tmFirstChar, otm->tmFirstChar);
217 ok(tm->tmLastChar == otm->tmLastChar, "tmLastChar %d != %d\n", tm->tmLastChar, otm->tmLastChar);
218 ok(tm->tmDefaultChar == otm->tmDefaultChar, "tmDefaultChar %d != %d\n", tm->tmDefaultChar, otm->tmDefaultChar);
219 ok(tm->tmBreakChar == otm->tmBreakChar, "tmBreakChar %d != %d\n", tm->tmBreakChar, otm->tmBreakChar);
220 ok(tm->tmItalic == otm->tmItalic, "tmItalic %d != %d\n", tm->tmItalic, otm->tmItalic);
221 ok(tm->tmUnderlined == otm->tmUnderlined, "tmUnderlined %d != %d\n", tm->tmUnderlined, otm->tmUnderlined);
222 ok(tm->tmStruckOut == otm->tmStruckOut, "tmStruckOut %d != %d\n", tm->tmStruckOut, otm->tmStruckOut);
223 ok(tm->tmPitchAndFamily == otm->tmPitchAndFamily, "tmPitchAndFamily %d != %d\n", tm->tmPitchAndFamily, otm->tmPitchAndFamily);
224 ok(tm->tmCharSet == otm->tmCharSet, "tmCharSet %d != %d\n", tm->tmCharSet, otm->tmCharSet);
227 static void test_font_metrics(HDC hdc, HFONT hfont, LONG lfHeight,
228 LONG lfWidth, const char *test_str,
229 INT test_str_len, const TEXTMETRICA *tm_orig,
230 const SIZE *size_orig, INT width_of_A_orig,
231 INT scale_x, INT scale_y)
233 LOGFONTA lf;
234 OUTLINETEXTMETRIC otm;
235 TEXTMETRICA tm;
236 SIZE size;
237 INT width_of_A, cx, cy;
238 UINT ret;
240 if (!hfont)
241 return;
243 ok(GetCurrentObject(hdc, OBJ_FONT) == hfont, "hfont should be selected\n");
245 GetObjectA(hfont, sizeof(lf), &lf);
247 if (GetOutlineTextMetricsA(hdc, 0, NULL))
249 otm.otmSize = sizeof(otm) / 2;
250 ret = GetOutlineTextMetricsA(hdc, otm.otmSize, &otm);
251 ok(ret == sizeof(otm)/2 /* XP */ ||
252 ret == 1 /* Win9x */, "expected sizeof(otm)/2, got %u\n", ret);
254 memset(&otm, 0x1, sizeof(otm));
255 otm.otmSize = sizeof(otm);
256 ret = GetOutlineTextMetricsA(hdc, otm.otmSize, &otm);
257 ok(ret == sizeof(otm) /* XP */ ||
258 ret == 1 /* Win9x */, "expected sizeof(otm), got %u\n", ret);
260 memset(&tm, 0x2, sizeof(tm));
261 ret = GetTextMetricsA(hdc, &tm);
262 ok(ret, "GetTextMetricsA failed\n");
263 /* the structure size is aligned */
264 if (memcmp(&tm, &otm.otmTextMetrics, FIELD_OFFSET(TEXTMETRICA, tmCharSet) + 1))
266 ok(0, "tm != otm\n");
267 compare_tm(&tm, &otm.otmTextMetrics);
270 tm = otm.otmTextMetrics;
271 if (0) /* these metrics are scaled too, but with rounding errors */
273 ok(otm.otmAscent == tm.tmAscent, "ascent %d != %d\n", otm.otmAscent, tm.tmAscent);
274 ok(otm.otmDescent == -tm.tmDescent, "descent %d != %d\n", otm.otmDescent, -tm.tmDescent);
276 ok(otm.otmMacAscent == tm.tmAscent, "ascent %d != %d\n", otm.otmMacAscent, tm.tmAscent);
277 ok(otm.otmDescent < 0, "otm.otmDescent should be < 0\n");
278 ok(otm.otmMacDescent < 0, "otm.otmMacDescent should be < 0\n");
279 ok(tm.tmDescent > 0, "tm.tmDescent should be > 0\n");
280 ok(otm.otmMacDescent == -tm.tmDescent, "descent %d != %d\n", otm.otmMacDescent, -tm.tmDescent);
281 ok(otm.otmEMSquare == 2048, "expected 2048, got %d\n", otm.otmEMSquare);
283 else
285 ret = GetTextMetricsA(hdc, &tm);
286 ok(ret, "GetTextMetricsA failed\n");
289 cx = tm.tmAveCharWidth / tm_orig->tmAveCharWidth;
290 cy = tm.tmHeight / tm_orig->tmHeight;
291 ok(cx == scale_x && cy == scale_y, "height %d: expected scale_x %d, scale_y %d, got cx %d, cy %d\n",
292 lfHeight, scale_x, scale_y, cx, cy);
293 ok(tm.tmHeight == tm_orig->tmHeight * scale_y, "height %d != %d\n", tm.tmHeight, tm_orig->tmHeight * scale_y);
294 ok(tm.tmAscent == tm_orig->tmAscent * scale_y, "ascent %d != %d\n", tm.tmAscent, tm_orig->tmAscent * scale_y);
295 ok(tm.tmDescent == tm_orig->tmDescent * scale_y, "descent %d != %d\n", tm.tmDescent, tm_orig->tmDescent * scale_y);
296 ok(near_match(tm.tmAveCharWidth, tm_orig->tmAveCharWidth * scale_x), "ave width %d != %d\n", tm.tmAveCharWidth, tm_orig->tmAveCharWidth * scale_x);
297 ok(near_match(tm.tmMaxCharWidth, tm_orig->tmMaxCharWidth * scale_x), "max width %d != %d\n", tm.tmMaxCharWidth, tm_orig->tmMaxCharWidth * scale_x);
299 ok(lf.lfHeight == lfHeight, "lfHeight %d != %d\n", lf.lfHeight, lfHeight);
300 if (lf.lfHeight)
302 if (lf.lfWidth)
303 ok(lf.lfWidth == tm.tmAveCharWidth, "lfWidth %d != tm %d\n", lf.lfWidth, tm.tmAveCharWidth);
305 else
306 ok(lf.lfWidth == lfWidth, "lfWidth %d != %d\n", lf.lfWidth, lfWidth);
308 GetTextExtentPoint32A(hdc, test_str, test_str_len, &size);
310 ok(near_match(size.cx, size_orig->cx * scale_x), "cx %d != %d\n", size.cx, size_orig->cx * scale_x);
311 ok(size.cy == size_orig->cy * scale_y, "cy %d != %d\n", size.cy, size_orig->cy * scale_y);
313 GetCharWidthA(hdc, 'A', 'A', &width_of_A);
315 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);
318 /* Test how GDI scales bitmap font metrics */
319 static void test_bitmap_font(void)
321 static const char test_str[11] = "Test String";
322 HDC hdc;
323 LOGFONTA bitmap_lf;
324 HFONT hfont, old_hfont;
325 TEXTMETRICA tm_orig;
326 SIZE size_orig;
327 INT ret, i, width_orig, height_orig, scale, lfWidth;
329 hdc = GetDC(0);
331 /* "System" has only 1 pixel size defined, otherwise the test breaks */
332 ret = EnumFontFamiliesA(hdc, "System", font_enum_proc, (LPARAM)&bitmap_lf);
333 if (ret)
335 ReleaseDC(0, hdc);
336 trace("no bitmap fonts were found, skipping the test\n");
337 return;
340 trace("found bitmap font %s, height %d\n", bitmap_lf.lfFaceName, bitmap_lf.lfHeight);
342 height_orig = bitmap_lf.lfHeight;
343 lfWidth = bitmap_lf.lfWidth;
345 hfont = create_font("bitmap", &bitmap_lf);
346 old_hfont = SelectObject(hdc, hfont);
347 ok(GetTextMetricsA(hdc, &tm_orig), "GetTextMetricsA failed\n");
348 ok(GetTextExtentPoint32A(hdc, test_str, sizeof(test_str), &size_orig), "GetTextExtentPoint32A failed\n");
349 ok(GetCharWidthA(hdc, 'A', 'A', &width_orig), "GetCharWidthA failed\n");
350 SelectObject(hdc, old_hfont);
351 DeleteObject(hfont);
353 bitmap_lf.lfHeight = 0;
354 bitmap_lf.lfWidth = 4;
355 hfont = create_font("bitmap", &bitmap_lf);
356 old_hfont = SelectObject(hdc, hfont);
357 test_font_metrics(hdc, hfont, 0, 4, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 1, 1);
358 SelectObject(hdc, old_hfont);
359 DeleteObject(hfont);
361 bitmap_lf.lfHeight = height_orig;
362 bitmap_lf.lfWidth = lfWidth;
364 /* test fractional scaling */
365 for (i = 1; i <= height_orig * 6; i++)
367 INT nearest_height;
369 bitmap_lf.lfHeight = i;
370 hfont = create_font("fractional", &bitmap_lf);
371 scale = (i + height_orig - 1) / height_orig;
372 nearest_height = scale * height_orig;
373 /* Only jump to the next height if the difference <= 25% original height */
374 if (scale > 2 && nearest_height - i > height_orig / 4) scale--;
375 /* The jump between unscaled and doubled is delayed by 1 in winnt+ but not in win9x,
376 so we'll not test this particular height. */
377 else if(scale == 2 && nearest_height - i == (height_orig / 4)) continue;
378 else if(scale == 2 && nearest_height - i > (height_orig / 4 - 1)) scale--;
379 old_hfont = SelectObject(hdc, hfont);
380 test_font_metrics(hdc, hfont, bitmap_lf.lfHeight, 0, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 1, scale);
381 SelectObject(hdc, old_hfont);
382 DeleteObject(hfont);
385 /* test integer scaling 3x2 */
386 bitmap_lf.lfHeight = height_orig * 2;
387 bitmap_lf.lfWidth *= 3;
388 hfont = create_font("3x2", &bitmap_lf);
389 old_hfont = SelectObject(hdc, hfont);
390 test_font_metrics(hdc, hfont, bitmap_lf.lfHeight, 0, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 3, 2);
391 SelectObject(hdc, old_hfont);
392 DeleteObject(hfont);
394 /* test integer scaling 3x3 */
395 bitmap_lf.lfHeight = height_orig * 3;
396 bitmap_lf.lfWidth = 0;
397 hfont = create_font("3x3", &bitmap_lf);
398 old_hfont = SelectObject(hdc, hfont);
399 test_font_metrics(hdc, hfont, bitmap_lf.lfHeight, 0, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 3, 3);
400 SelectObject(hdc, old_hfont);
401 DeleteObject(hfont);
403 ReleaseDC(0, hdc);
406 /* Test how GDI scales outline font metrics */
407 static void test_outline_font(void)
409 static const char test_str[11] = "Test String";
410 HDC hdc, hdc_2;
411 LOGFONTA lf;
412 HFONT hfont, old_hfont, old_hfont_2;
413 OUTLINETEXTMETRICA otm;
414 SIZE size_orig;
415 INT width_orig, height_orig, lfWidth;
416 XFORM xform;
417 GLYPHMETRICS gm;
418 MAT2 mat2 = { {0x8000,0}, {0,0}, {0,0}, {0x8000,0} };
419 POINT pt;
420 INT ret;
422 if (!is_truetype_font_installed("Arial"))
424 skip("Arial is not installed\n");
425 return;
428 hdc = CreateCompatibleDC(0);
430 memset(&lf, 0, sizeof(lf));
431 strcpy(lf.lfFaceName, "Arial");
432 lf.lfHeight = 72;
433 hfont = create_font("outline", &lf);
434 old_hfont = SelectObject(hdc, hfont);
435 otm.otmSize = sizeof(otm);
436 ok(GetOutlineTextMetricsA(hdc, sizeof(otm), &otm), "GetTextMetricsA failed\n");
437 ok(GetTextExtentPoint32A(hdc, test_str, sizeof(test_str), &size_orig), "GetTextExtentPoint32A failed\n");
438 ok(GetCharWidthA(hdc, 'A', 'A', &width_orig), "GetCharWidthA failed\n");
440 test_font_metrics(hdc, hfont, lf.lfHeight, otm.otmTextMetrics.tmAveCharWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
441 SelectObject(hdc, old_hfont);
442 DeleteObject(hfont);
444 /* font of otmEMSquare height helps to avoid a lot of rounding errors */
445 lf.lfHeight = otm.otmEMSquare;
446 lf.lfHeight = -lf.lfHeight;
447 hfont = create_font("outline", &lf);
448 old_hfont = SelectObject(hdc, hfont);
449 otm.otmSize = sizeof(otm);
450 ok(GetOutlineTextMetricsA(hdc, sizeof(otm), &otm), "GetTextMetricsA failed\n");
451 ok(GetTextExtentPoint32A(hdc, test_str, sizeof(test_str), &size_orig), "GetTextExtentPoint32A failed\n");
452 ok(GetCharWidthA(hdc, 'A', 'A', &width_orig), "GetCharWidthA failed\n");
453 SelectObject(hdc, old_hfont);
454 DeleteObject(hfont);
456 height_orig = otm.otmTextMetrics.tmHeight;
457 lfWidth = otm.otmTextMetrics.tmAveCharWidth;
459 /* test integer scaling 3x2 */
460 lf.lfHeight = height_orig * 2;
461 lf.lfWidth = lfWidth * 3;
462 hfont = create_font("3x2", &lf);
463 old_hfont = SelectObject(hdc, hfont);
464 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 3, 2);
465 SelectObject(hdc, old_hfont);
466 DeleteObject(hfont);
468 /* test integer scaling 3x3 */
469 lf.lfHeight = height_orig * 3;
470 lf.lfWidth = lfWidth * 3;
471 hfont = create_font("3x3", &lf);
472 old_hfont = SelectObject(hdc, hfont);
473 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 3, 3);
474 SelectObject(hdc, old_hfont);
475 DeleteObject(hfont);
477 /* test integer scaling 1x1 */
478 lf.lfHeight = height_orig * 1;
479 lf.lfWidth = lfWidth * 1;
480 hfont = create_font("1x1", &lf);
481 old_hfont = SelectObject(hdc, hfont);
482 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
483 SelectObject(hdc, old_hfont);
484 DeleteObject(hfont);
486 /* test integer scaling 1x1 */
487 lf.lfHeight = height_orig;
488 lf.lfWidth = 0;
489 hfont = create_font("1x1", &lf);
490 old_hfont = SelectObject(hdc, hfont);
491 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
493 /* with an identity matrix */
494 memset(&gm, 0, sizeof(gm));
495 SetLastError(0xdeadbeef);
496 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
497 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
498 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
499 ok(gm.gmCellIncX == width_orig, "incX %d != %d\n", gm.gmCellIncX, width_orig);
500 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
501 /* with a custom matrix */
502 memset(&gm, 0, sizeof(gm));
503 SetLastError(0xdeadbeef);
504 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat2);
505 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
506 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
507 ok(gm.gmCellIncX == width_orig/2, "incX %d != %d\n", gm.gmCellIncX, width_orig/2);
508 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
510 /* Test that changing the DC transformation affects only the font
511 * selected on this DC and doesn't affect the same font selected on
512 * another DC.
514 hdc_2 = CreateCompatibleDC(0);
515 old_hfont_2 = SelectObject(hdc_2, hfont);
516 test_font_metrics(hdc_2, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
518 SetMapMode(hdc, MM_ANISOTROPIC);
520 /* font metrics on another DC should be unchanged */
521 test_font_metrics(hdc_2, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
523 /* test restrictions of compatibility mode GM_COMPATIBLE */
524 /* part 1: rescaling only X should not change font scaling on screen.
525 So compressing the X axis by 2 is not done, and this
526 appears as X scaling of 2 that no one requested. */
527 SetWindowExtEx(hdc, 100, 100, NULL);
528 SetViewportExtEx(hdc, 50, 100, NULL);
529 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 2, 1);
530 /* font metrics on another DC should be unchanged */
531 test_font_metrics(hdc_2, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
533 /* part 2: rescaling only Y should change font scaling.
534 As also X is scaled by a factor of 2, but this is not
535 requested by the DC transformation, we get a scaling factor
536 of 2 in the X coordinate. */
537 SetViewportExtEx(hdc, 100, 200, NULL);
538 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 2, 1);
539 /* font metrics on another DC should be unchanged */
540 test_font_metrics(hdc_2, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
542 /* restore scaling */
543 SetMapMode(hdc, MM_TEXT);
545 /* font metrics on another DC should be unchanged */
546 test_font_metrics(hdc_2, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
548 SelectObject(hdc_2, old_hfont_2);
549 DeleteDC(hdc_2);
551 if (!SetGraphicsMode(hdc, GM_ADVANCED))
553 SelectObject(hdc, old_hfont);
554 DeleteObject(hfont);
555 DeleteDC(hdc);
556 skip("GM_ADVANCED is not supported on this platform\n");
557 return;
560 xform.eM11 = 20.0f;
561 xform.eM12 = 0.0f;
562 xform.eM21 = 0.0f;
563 xform.eM22 = 20.0f;
564 xform.eDx = 0.0f;
565 xform.eDy = 0.0f;
567 SetLastError(0xdeadbeef);
568 ret = SetWorldTransform(hdc, &xform);
569 ok(ret, "SetWorldTransform error %u\n", GetLastError());
571 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
573 /* with an identity matrix */
574 memset(&gm, 0, sizeof(gm));
575 SetLastError(0xdeadbeef);
576 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
577 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
578 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
579 pt.x = width_orig; pt.y = 0;
580 LPtoDP(hdc, &pt, 1);
581 ok(gm.gmCellIncX == pt.x, "incX %d != %d\n", gm.gmCellIncX, pt.x);
582 ok(gm.gmCellIncX == 20 * width_orig, "incX %d != %d\n", gm.gmCellIncX, 20 * width_orig);
583 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
584 /* with a custom matrix */
585 memset(&gm, 0, sizeof(gm));
586 SetLastError(0xdeadbeef);
587 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat2);
588 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
589 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
590 pt.x = width_orig; pt.y = 0;
591 LPtoDP(hdc, &pt, 1);
592 ok(gm.gmCellIncX == pt.x/2, "incX %d != %d\n", gm.gmCellIncX, pt.x/2);
593 ok(near_match(gm.gmCellIncX, 10 * width_orig), "incX %d != %d\n", gm.gmCellIncX, 10 * width_orig);
594 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
596 SetLastError(0xdeadbeef);
597 ret = SetMapMode(hdc, MM_LOMETRIC);
598 ok(ret == MM_TEXT, "expected MM_TEXT, got %d, error %u\n", ret, GetLastError());
600 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
602 /* with an identity matrix */
603 memset(&gm, 0, sizeof(gm));
604 SetLastError(0xdeadbeef);
605 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
606 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
607 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
608 pt.x = width_orig; pt.y = 0;
609 LPtoDP(hdc, &pt, 1);
610 ok(near_match(gm.gmCellIncX, pt.x), "incX %d != %d\n", gm.gmCellIncX, pt.x);
611 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
612 /* with a custom matrix */
613 memset(&gm, 0, sizeof(gm));
614 SetLastError(0xdeadbeef);
615 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat2);
616 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
617 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
618 pt.x = width_orig; pt.y = 0;
619 LPtoDP(hdc, &pt, 1);
620 ok(near_match(gm.gmCellIncX, (pt.x + 1)/2), "incX %d != %d\n", gm.gmCellIncX, (pt.x + 1)/2);
621 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
623 SetLastError(0xdeadbeef);
624 ret = SetMapMode(hdc, MM_TEXT);
625 ok(ret == MM_LOMETRIC, "expected MM_LOMETRIC, got %d, error %u\n", ret, GetLastError());
627 test_font_metrics(hdc, hfont, lf.lfHeight, lf.lfWidth, test_str, sizeof(test_str), &otm.otmTextMetrics, &size_orig, width_orig, 1, 1);
629 /* with an identity matrix */
630 memset(&gm, 0, sizeof(gm));
631 SetLastError(0xdeadbeef);
632 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
633 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
634 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
635 pt.x = width_orig; pt.y = 0;
636 LPtoDP(hdc, &pt, 1);
637 ok(gm.gmCellIncX == pt.x, "incX %d != %d\n", gm.gmCellIncX, pt.x);
638 ok(gm.gmCellIncX == 20 * width_orig, "incX %d != %d\n", gm.gmCellIncX, 20 * width_orig);
639 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
640 /* with a custom matrix */
641 memset(&gm, 0, sizeof(gm));
642 SetLastError(0xdeadbeef);
643 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat2);
644 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %d\n", GetLastError());
645 trace("gm.gmCellIncX %d, width_orig %d\n", gm.gmCellIncX, width_orig);
646 pt.x = width_orig; pt.y = 0;
647 LPtoDP(hdc, &pt, 1);
648 ok(gm.gmCellIncX == pt.x/2, "incX %d != %d\n", gm.gmCellIncX, pt.x/2);
649 ok(gm.gmCellIncX == 10 * width_orig, "incX %d != %d\n", gm.gmCellIncX, 10 * width_orig);
650 ok(gm.gmCellIncY == 0, "incY %d != 0\n", gm.gmCellIncY);
652 SelectObject(hdc, old_hfont);
653 DeleteObject(hfont);
654 DeleteDC(hdc);
657 static INT CALLBACK find_font_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
659 LOGFONT *lf = (LOGFONT *)lParam;
661 if (elf->lfHeight == lf->lfHeight && !strcmp(elf->lfFaceName, lf->lfFaceName))
663 *lf = *elf;
664 return 0; /* stop enumeration */
666 return 1; /* continue enumeration */
669 static void test_bitmap_font_metrics(void)
671 static const struct font_data
673 const char face_name[LF_FACESIZE];
674 int weight, height, ascent, descent, int_leading, ext_leading;
675 int ave_char_width, max_char_width, dpi;
676 BYTE first_char, last_char, def_char, break_char;
677 DWORD ansi_bitfield;
678 WORD skip_lang_id;
679 } fd[] =
681 { "MS Sans Serif", FW_NORMAL, 13, 11, 2, 2, 0, 5, 11, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2 },
682 { "MS Sans Serif", FW_NORMAL, 13, 11, 2, 2, 0, 5, 11, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
683 { "MS Sans Serif", FW_NORMAL, 16, 13, 3, 3, 0, 7, 14, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2 },
684 { "MS Sans Serif", FW_NORMAL, 16, 13, 3, 3, 0, 7, 14, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
685 { "MS Sans Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 16, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 },
686 { "MS Sans Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 18, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN2 },
687 { "MS Sans Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 16, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
688 { "MS Sans Serif", FW_NORMAL, 24, 19, 5, 6, 0, 9, 19, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 },
689 { "MS Sans Serif", FW_NORMAL, 24, 19, 5, 6, 0, 9, 24, 96, 0x20, 0xff, 0x81, 0x40, FS_LATIN2 },
690 { "MS Sans Serif", FW_NORMAL, 24, 19, 5, 6, 0, 9, 20, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
691 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 5, 0, 12, 24, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 },
692 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 6, 0, 12, 24, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN2 },
693 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 5, 0, 12, 25, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
694 { "MS Sans Serif", FW_NORMAL, 37, 29, 8, 5, 0, 16, 32, 96, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 },
695 { "MS Sans Serif", FW_NORMAL, 37, 29, 8, 5, 0, 16, 32, 96, 0x20, 0xff, 0x81, 0x40, FS_LATIN2 },
696 { "MS Sans Serif", FW_NORMAL, 37, 29, 8, 5, 0, 16, 32, 96, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
698 { "MS Sans Serif", FW_NORMAL, 16, 13, 3, 3, 0, 7, 14, 120, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2 },
699 { "MS Sans Serif", FW_NORMAL, 16, 13, 3, 3, 0, 7, 14, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
700 { "MS Sans Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 18, 120, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2 },
701 { "MS Sans Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 17, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
702 { "MS Sans Serif", FW_NORMAL, 25, 20, 5, 5, 0, 10, 21, 120, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2 },
703 { "MS Sans Serif", FW_NORMAL, 25, 20, 5, 5, 0, 10, 21, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
704 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 6, 0, 12, 24, 120, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2 },
705 { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 5, 0, 12, 24, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
706 { "MS Sans Serif", FW_NORMAL, 36, 29, 7, 6, 0, 15, 30, 120, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2 },
707 { "MS Sans Serif", FW_NORMAL, 36, 29, 7, 6, 0, 15, 30, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
708 { "MS Sans Serif", FW_NORMAL, 46, 37, 9, 6, 0, 20, 40, 120, 0x20, 0xff, 0x81, 0x20, FS_LATIN1 | FS_LATIN2 },
709 { "MS Sans Serif", FW_NORMAL, 46, 37, 9, 6, 0, 20, 40, 120, 0x20, 0xff, 0x7f, 0x20, FS_CYRILLIC },
711 { "MS Serif", FW_NORMAL, 10, 8, 2, 2, 0, 4, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 },
712 { "MS Serif", FW_NORMAL, 10, 8, 2, 2, 0, 5, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
713 { "MS Serif", FW_NORMAL, 11, 9, 2, 2, 0, 5, 9, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
714 { "MS Serif", FW_NORMAL, 13, 11, 2, 2, 0, 5, 11, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 },
715 { "MS Serif", FW_NORMAL, 13, 11, 2, 2, 0, 5, 12, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
716 { "MS Serif", FW_NORMAL, 16, 13, 3, 3, 0, 6, 14, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 },
717 { "MS Serif", FW_NORMAL, 16, 13, 3, 3, 0, 6, 16, 96, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
718 { "MS Serif", FW_NORMAL, 19, 15, 4, 3, 0, 8, 18, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 },
719 { "MS Serif", FW_NORMAL, 19, 15, 4, 3, 0, 8, 19, 96, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
720 { "MS Serif", FW_NORMAL, 21, 16, 5, 3, 0, 9, 17, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 },
721 { "MS Serif", FW_NORMAL, 21, 16, 5, 3, 0, 9, 22, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 },
722 { "MS Serif", FW_NORMAL, 21, 16, 5, 3, 0, 9, 23, 96, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
723 { "MS Serif", FW_NORMAL, 27, 21, 6, 3, 0, 12, 23, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 },
724 { "MS Serif", FW_NORMAL, 27, 21, 6, 3, 0, 12, 26, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 },
725 { "MS Serif", FW_NORMAL, 27, 21, 6, 3, 0, 12, 27, 96, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
726 { "MS Serif", FW_NORMAL, 35, 27, 8, 3, 0, 16, 33, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 },
727 { "MS Serif", FW_NORMAL, 35, 27, 8, 3, 0, 16, 34, 96, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
729 { "MS Serif", FW_NORMAL, 16, 13, 3, 3, 0, 6, 14, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_CYRILLIC },
730 { "MS Serif", FW_NORMAL, 16, 13, 3, 3, 0, 6, 13, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 },
731 { "MS Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 18, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_CYRILLIC },
732 { "MS Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 15, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 },
733 { "MS Serif", FW_NORMAL, 23, 18, 5, 3, 0, 10, 21, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_CYRILLIC },
734 { "MS Serif", FW_NORMAL, 23, 18, 5, 3, 0, 10, 19, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 },
735 { "MS Serif", FW_NORMAL, 27, 21, 6, 4, 0, 12, 23, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 },
736 { "MS Serif", FW_MEDIUM, 27, 22, 5, 2, 0, 12, 30, 120, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
737 { "MS Serif", FW_NORMAL, 33, 26, 7, 3, 0, 14, 30, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 },
738 { "MS Serif", FW_MEDIUM, 32, 25, 7, 2, 0, 14, 32, 120, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
739 { "MS Serif", FW_NORMAL, 43, 34, 9, 3, 0, 19, 39, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
741 { "Courier", FW_NORMAL, 13, 11, 2, 0, 0, 8, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
742 { "Courier", FW_NORMAL, 16, 13, 3, 0, 0, 9, 9, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
743 { "Courier", FW_NORMAL, 20, 16, 4, 0, 0, 12, 12, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
745 { "Courier", FW_NORMAL, 16, 13, 3, 0, 0, 9, 9, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
746 { "Courier", FW_NORMAL, 20, 16, 4, 0, 0, 12, 12, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
747 { "Courier", FW_NORMAL, 25, 20, 5, 0, 0, 15, 15, 120, 0x20, 0xff, 0x40, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC },
749 { "System", FW_BOLD, 16, 13, 3, 3, 0, 7, 14, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 },
750 { "System", FW_BOLD, 16, 13, 3, 3, 0, 7, 15, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
751 { "System", FW_NORMAL, 18, 16, 2, 0, 2, 8, 16, 96, 0, 0, 0, 0, FS_JISJAPAN },
753 { "System", FW_BOLD, 20, 16, 4, 4, 0, 9, 14, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 },
754 { "System", FW_BOLD, 20, 16, 4, 4, 0, 9, 17, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
756 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 1, 2, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 },
757 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 1, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
758 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 2, 4, 96, 0, 0, 0, 0, FS_JISJAPAN },
759 { "Small Fonts", FW_NORMAL, 5, 4, 1, 1, 0, 3, 4, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1, LANG_ARABIC },
760 { "Small Fonts", FW_NORMAL, 5, 4, 1, 1, 0, 2, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
761 { "Small Fonts", FW_NORMAL, 5, 4, 1, 0, 0, 3, 6, 96, 0, 0, 0, 0, FS_JISJAPAN },
762 { "Small Fonts", FW_NORMAL, 6, 5, 1, 1, 0, 3, 13, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1, LANG_ARABIC },
763 { "Small Fonts", FW_NORMAL, 6, 5, 1, 1, 0, 3, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
764 { "Small Fonts", FW_NORMAL, 6, 5, 1, 1, 0, 3, 8, 96, 0, 0, 0, 0, FS_ARABIC },
765 { "Small Fonts", FW_NORMAL, 6, 5, 1, 0, 0, 4, 8, 96, 0, 0, 0, 0, FS_JISJAPAN },
766 { "Small Fonts", FW_NORMAL, 8, 7, 1, 1, 0, 4, 7, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1, LANG_ARABIC },
767 { "Small Fonts", FW_NORMAL, 8, 7, 1, 1, 0, 4, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
768 { "Small Fonts", FW_NORMAL, 8, 7, 1, 1, 0, 4, 8, 96, 0, 0, 0, 0, FS_ARABIC },
769 { "Small Fonts", FW_NORMAL, 8, 7, 1, 0, 0, 5, 10, 96, 0, 0, 0, 0, FS_JISJAPAN },
770 { "Small Fonts", FW_NORMAL, 10, 8, 2, 2, 0, 4, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2, LANG_ARABIC },
771 { "Small Fonts", FW_NORMAL, 10, 8, 2, 2, 0, 5, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
772 { "Small Fonts", FW_NORMAL, 10, 8, 2, 2, 0, 4, 9, 96, 0, 0, 0, 0, FS_ARABIC },
773 { "Small Fonts", FW_NORMAL, 10, 8, 2, 0, 0, 6, 12, 96, 0, 0, 0, 0, FS_JISJAPAN },
774 { "Small Fonts", FW_NORMAL, 11, 9, 2, 2, 0, 5, 9, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC, LANG_ARABIC },
775 { "Small Fonts", FW_NORMAL, 11, 9, 2, 2, 0, 4, 10, 96, 0, 0, 0, 0, FS_ARABIC },
776 { "Small Fonts", FW_NORMAL, 11, 9, 2, 0, 0, 7, 14, 96, 0, 0, 0, 0, FS_JISJAPAN },
778 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 1, 2, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_JISJAPAN },
779 { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 1, 8, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
780 { "Small Fonts", FW_NORMAL, 6, 5, 1, 1, 0, 3, 5, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_JISJAPAN },
781 { "Small Fonts", FW_NORMAL, 6, 5, 1, 1, 0, 3, 8, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
782 { "Small Fonts", FW_NORMAL, 8, 7, 1, 1, 0, 4, 7, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_JISJAPAN },
783 { "Small Fonts", FW_NORMAL, 8, 7, 1, 1, 0, 4, 8, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN2 | FS_CYRILLIC },
784 { "Small Fonts", FW_NORMAL, 10, 8, 2, 2, 0, 5, 9, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_JISJAPAN },
785 { "Small Fonts", FW_NORMAL, 10, 8, 2, 2, 0, 5, 8, 120, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
786 { "Small Fonts", FW_NORMAL, 12, 10, 2, 2, 0, 5, 10, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_JISJAPAN },
787 { "Small Fonts", FW_NORMAL, 12, 10, 2, 2, 0, 6, 10, 120, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
788 { "Small Fonts", FW_NORMAL, 13, 11, 2, 2, 0, 6, 12, 120, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_JISJAPAN },
789 { "Small Fonts", FW_NORMAL, 13, 11, 2, 2, 0, 6, 11, 120, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
791 { "Fixedsys", FW_NORMAL, 15, 12, 3, 3, 0, 8, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 },
792 { "Fixedsys", FW_NORMAL, 16, 12, 4, 3, 0, 8, 8, 96, 0x20, 0xff, 0x80, 0x20, FS_CYRILLIC },
793 { "FixedSys", FW_NORMAL, 18, 16, 2, 0, 0, 8, 16, 96, 0, 0, 0, 0, FS_JISJAPAN },
795 /* The 120dpi version still has its dpi marked as 96 */
796 { "Fixedsys", FW_NORMAL, 20, 16, 4, 2, 0, 10, 10, 96, 0x20, 0xff, 0x80, 0x20, FS_LATIN1 | FS_LATIN2 | FS_CYRILLIC }
798 /* FIXME: add "Terminal" */
800 HDC hdc;
801 LOGFONT lf;
802 HFONT hfont, old_hfont;
803 TEXTMETRIC tm;
804 INT ret, i;
805 WORD system_lang_id;
807 system_lang_id = PRIMARYLANGID(GetSystemDefaultLangID());
809 hdc = CreateCompatibleDC(0);
810 assert(hdc);
812 for (i = 0; i < sizeof(fd)/sizeof(fd[0]); i++)
814 int bit;
816 memset(&lf, 0, sizeof(lf));
818 lf.lfHeight = fd[i].height;
819 strcpy(lf.lfFaceName, fd[i].face_name);
821 for(bit = 0; bit < 32; bit++)
823 DWORD fs[2];
824 CHARSETINFO csi;
825 BOOL bRet;
827 fs[0] = 1L << bit;
828 fs[1] = 0;
829 if((fd[i].ansi_bitfield & fs[0]) == 0) continue;
830 if(!TranslateCharsetInfo( fs, &csi, TCI_SRCFONTSIG )) continue;
832 lf.lfCharSet = csi.ciCharset;
833 ret = EnumFontFamiliesEx(hdc, &lf, find_font_proc, (LPARAM)&lf, 0);
834 if (ret) continue;
836 hfont = create_font(lf.lfFaceName, &lf);
837 old_hfont = SelectObject(hdc, hfont);
838 bRet = GetTextMetrics(hdc, &tm);
839 ok(bRet, "GetTextMetrics error %d\n", GetLastError());
840 if(fd[i].dpi == tm.tmDigitizedAspectX)
842 trace("found font %s, height %d charset %x dpi %d\n", lf.lfFaceName, lf.lfHeight, lf.lfCharSet, fd[i].dpi);
843 if (fd[i].skip_lang_id == 0 || system_lang_id != fd[i].skip_lang_id)
845 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);
846 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);
847 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);
848 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);
849 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);
850 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);
851 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);
852 ok(tm.tmFirstChar == fd[i].first_char, "%s(%d): tm.tmFirstChar = %02x\n", fd[i].face_name, fd[i].height, tm.tmFirstChar);
853 ok(tm.tmLastChar == fd[i].last_char, "%s(%d): tm.tmLastChar = %02x\n", fd[i].face_name, fd[i].height, tm.tmLastChar);
854 ok(tm.tmDefaultChar == fd[i].def_char, "%s(%d): tm.tmDefaultChar = %02x\n", fd[i].face_name, fd[i].height, tm.tmDefaultChar);
855 ok(tm.tmBreakChar == fd[i].break_char, "%s(%d): tm.tmBreakChar = %02x\n", fd[i].face_name, fd[i].height, tm.tmBreakChar);
857 /* Don't run the max char width test on System/ANSI_CHARSET. We have extra characters in our font
858 that make the max width bigger */
859 if(strcmp(lf.lfFaceName, "System") || lf.lfCharSet != ANSI_CHARSET)
860 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);
862 else
863 skip("Skipping font metrics test for system langid 0x%x\n",
864 system_lang_id);
866 SelectObject(hdc, old_hfont);
867 DeleteObject(hfont);
871 DeleteDC(hdc);
874 static void test_GdiGetCharDimensions(void)
876 HDC hdc;
877 TEXTMETRICW tm;
878 LONG ret;
879 SIZE size;
880 LONG avgwidth, height;
881 static const char szAlphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
883 if (!pGdiGetCharDimensions)
885 win_skip("GdiGetCharDimensions not available on this platform\n");
886 return;
889 hdc = CreateCompatibleDC(NULL);
891 GetTextExtentPoint(hdc, szAlphabet, strlen(szAlphabet), &size);
892 avgwidth = ((size.cx / 26) + 1) / 2;
894 ret = pGdiGetCharDimensions(hdc, &tm, &height);
895 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
896 ok(height == tm.tmHeight, "GdiGetCharDimensions should have set height to %d instead of %d\n", tm.tmHeight, height);
898 ret = pGdiGetCharDimensions(hdc, &tm, NULL);
899 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
901 ret = pGdiGetCharDimensions(hdc, NULL, NULL);
902 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
904 height = 0;
905 ret = pGdiGetCharDimensions(hdc, NULL, &height);
906 ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth, ret);
907 ok(height == size.cy, "GdiGetCharDimensions should have set height to %d instead of %d\n", size.cy, height);
909 DeleteDC(hdc);
912 static int CALLBACK create_font_proc(const LOGFONT *lpelfe,
913 const TEXTMETRIC *lpntme,
914 DWORD FontType, LPARAM lParam)
916 if (FontType & TRUETYPE_FONTTYPE)
918 HFONT hfont;
920 hfont = CreateFontIndirect(lpelfe);
921 if (hfont)
923 *(HFONT *)lParam = hfont;
924 return 0;
928 return 1;
931 static void test_GetCharABCWidths(void)
933 static const WCHAR str[] = {'a',0};
934 BOOL ret;
935 HDC hdc;
936 LOGFONTA lf;
937 HFONT hfont;
938 ABC abc[1];
939 WORD glyphs[1];
940 DWORD nb;
941 static const struct
943 UINT first;
944 UINT last;
945 } range[] =
947 {0xff, 0xff},
948 {0x100, 0x100},
949 {0xff, 0x100},
950 {0x1ff, 0xff00},
951 {0xffff, 0xffff},
952 {0x10000, 0x10000},
953 {0xffff, 0x10000},
954 {0xffffff, 0xffffff},
955 {0x1000000, 0x1000000},
956 {0xffffff, 0x1000000},
957 {0xffffffff, 0xffffffff}
959 static const struct
961 UINT cs;
962 UINT a;
963 UINT w;
964 BOOL r[sizeof range / sizeof range[0]];
965 } c[] =
967 {ANSI_CHARSET, 0x30, 0x30, {TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE}},
968 {SHIFTJIS_CHARSET, 0x82a0, 0x3042, {TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE}},
969 {HANGEUL_CHARSET, 0x8141, 0xac02, {TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE}},
970 {JOHAB_CHARSET, 0x8446, 0x3135, {TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE}},
971 {GB2312_CHARSET, 0x8141, 0x4e04, {TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE}},
972 {CHINESEBIG5_CHARSET, 0xa142, 0x3001, {TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE}}
974 UINT i;
976 if (!pGetCharABCWidthsA || !pGetCharABCWidthsW || !pGetCharABCWidthsI)
978 win_skip("GetCharABCWidthsA/W/I not available on this platform\n");
979 return;
982 memset(&lf, 0, sizeof(lf));
983 strcpy(lf.lfFaceName, "System");
984 lf.lfHeight = 20;
986 hfont = CreateFontIndirectA(&lf);
987 hdc = GetDC(0);
988 hfont = SelectObject(hdc, hfont);
990 nb = pGetGlyphIndicesW(hdc, str, 1, glyphs, 0);
991 ok(nb == 1, "GetGlyphIndicesW should have returned 1\n");
993 ret = pGetCharABCWidthsI(NULL, 0, 1, glyphs, abc);
994 ok(!ret, "GetCharABCWidthsI should have failed\n");
996 ret = pGetCharABCWidthsI(hdc, 0, 1, glyphs, NULL);
997 ok(!ret, "GetCharABCWidthsI should have failed\n");
999 ret = pGetCharABCWidthsI(hdc, 0, 1, glyphs, abc);
1000 ok(ret, "GetCharABCWidthsI should have succeeded\n");
1002 ret = pGetCharABCWidthsW(NULL, 'a', 'a', abc);
1003 ok(!ret, "GetCharABCWidthsW should have failed\n");
1005 ret = pGetCharABCWidthsW(hdc, 'a', 'a', NULL);
1006 ok(!ret, "GetCharABCWidthsW should have failed\n");
1008 ret = pGetCharABCWidthsW(hdc, 'a', 'a', abc);
1009 ok(!ret, "GetCharABCWidthsW should have failed\n");
1011 hfont = SelectObject(hdc, hfont);
1012 DeleteObject(hfont);
1014 for (i = 0; i < sizeof c / sizeof c[0]; ++i)
1016 ABC a[2], w[2];
1017 ABC full[256];
1018 UINT code = 0x41, j;
1020 lf.lfFaceName[0] = '\0';
1021 lf.lfCharSet = c[i].cs;
1022 lf.lfPitchAndFamily = 0;
1023 if (EnumFontFamiliesEx(hdc, &lf, create_font_proc, (LPARAM)&hfont, 0))
1025 skip("TrueType font for charset %u is not installed\n", c[i].cs);
1026 continue;
1029 memset(a, 0, sizeof a);
1030 memset(w, 0, sizeof w);
1031 hfont = SelectObject(hdc, hfont);
1032 ok(pGetCharABCWidthsA(hdc, c[i].a, c[i].a + 1, a) &&
1033 pGetCharABCWidthsW(hdc, c[i].w, c[i].w + 1, w) &&
1034 memcmp(a, w, sizeof a) == 0,
1035 "GetCharABCWidthsA and GetCharABCWidthsW should return same widths. charset = %u\n", c[i].cs);
1037 memset(a, 0xbb, sizeof a);
1038 ret = pGetCharABCWidthsA(hdc, code, code, a);
1039 ok(ret, "GetCharABCWidthsA should have succeeded\n");
1040 memset(full, 0xcc, sizeof full);
1041 ret = pGetCharABCWidthsA(hdc, 0x00, code, full);
1042 ok(ret, "GetCharABCWidthsA should have succeeded\n");
1043 ok(memcmp(&a[0], &full[code], sizeof(ABC)) == 0,
1044 "GetCharABCWidthsA info should match. codepage = %u\n", c[i].cs);
1046 for (j = 0; j < sizeof range / sizeof range[0]; ++j)
1048 ret = pGetCharABCWidthsA(hdc, range[j].first, range[j].last, full);
1049 ok(ret == c[i].r[j], "GetCharABCWidthsA %x - %x should have %s\n",
1050 range[j].first, range[j].last, c[i].r[j] ? "succeeded" : "failed");
1053 hfont = SelectObject(hdc, hfont);
1054 DeleteObject(hfont);
1057 ReleaseDC(NULL, hdc);
1060 static void test_text_extents(void)
1062 static const WCHAR wt[] = {'O','n','e','\n','t','w','o',' ','3',0};
1063 LPINT extents;
1064 INT i, len, fit1, fit2;
1065 LOGFONTA lf;
1066 TEXTMETRICA tm;
1067 HDC hdc;
1068 HFONT hfont;
1069 SIZE sz;
1070 SIZE sz1, sz2;
1071 BOOL ret;
1073 memset(&lf, 0, sizeof(lf));
1074 strcpy(lf.lfFaceName, "Arial");
1075 lf.lfHeight = 20;
1077 hfont = CreateFontIndirectA(&lf);
1078 hdc = GetDC(0);
1079 hfont = SelectObject(hdc, hfont);
1080 GetTextMetricsA(hdc, &tm);
1081 GetTextExtentPointA(hdc, "o", 1, &sz);
1082 ok(sz.cy == tm.tmHeight, "cy %d tmHeight %d\n", sz.cy, tm.tmHeight);
1084 SetLastError(0xdeadbeef);
1085 GetTextExtentExPointW(hdc, wt, 1, 1, &fit1, &fit2, &sz1);
1086 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
1088 win_skip("Skipping remainder of text extents test on a Win9x platform\n");
1089 hfont = SelectObject(hdc, hfont);
1090 DeleteObject(hfont);
1091 ReleaseDC(0, hdc);
1092 return;
1095 len = lstrlenW(wt);
1096 extents = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof extents[0]);
1097 extents[0] = 1; /* So that the increasing sequence test will fail
1098 if the extents array is untouched. */
1099 GetTextExtentExPointW(hdc, wt, len, 32767, &fit1, extents, &sz1);
1100 GetTextExtentPointW(hdc, wt, len, &sz2);
1101 ok(sz1.cy == sz2.cy,
1102 "cy from GetTextExtentExPointW (%d) and GetTextExtentPointW (%d) differ\n", sz1.cy, sz2.cy);
1103 /* Because of the '\n' in the string GetTextExtentExPoint and
1104 GetTextExtentPoint return different widths under Win2k, but
1105 under WinXP they return the same width. So we don't test that
1106 here. */
1108 for (i = 1; i < len; ++i)
1109 ok(extents[i-1] <= extents[i],
1110 "GetTextExtentExPointW generated a non-increasing sequence of partial extents (at position %d)\n",
1112 ok(extents[len-1] == sz1.cx, "GetTextExtentExPointW extents and size don't match\n");
1113 ok(0 <= fit1 && fit1 <= len, "GetTextExtentExPointW generated illegal value %d for fit\n", fit1);
1114 ok(0 < fit1, "GetTextExtentExPointW says we can't even fit one letter in 32767 logical units\n");
1115 GetTextExtentExPointW(hdc, wt, len, extents[2], &fit2, NULL, &sz2);
1116 ok(sz1.cx == sz2.cx && sz1.cy == sz2.cy, "GetTextExtentExPointW returned different sizes for the same string\n");
1117 ok(fit2 == 3, "GetTextExtentExPointW extents isn't consistent with fit\n");
1118 GetTextExtentExPointW(hdc, wt, len, extents[2]-1, &fit2, NULL, &sz2);
1119 ok(fit2 == 2, "GetTextExtentExPointW extents isn't consistent with fit\n");
1120 GetTextExtentExPointW(hdc, wt, 2, 0, NULL, extents + 2, &sz2);
1121 ok(extents[0] == extents[2] && extents[1] == extents[3],
1122 "GetTextExtentExPointW with lpnFit == NULL returns incorrect results\n");
1123 GetTextExtentExPointW(hdc, wt, 2, 0, NULL, NULL, &sz1);
1124 ok(sz1.cx == sz2.cx && sz1.cy == sz2.cy,
1125 "GetTextExtentExPointW with lpnFit and alpDx both NULL returns incorrect results\n");
1126 HeapFree(GetProcessHeap(), 0, extents);
1128 /* extents functions fail with -ve counts (the interesting case being -1) */
1129 ret = GetTextExtentPointA(hdc, "o", -1, &sz);
1130 ok(ret == FALSE, "got %d\n", ret);
1131 ret = GetTextExtentExPointA(hdc, "o", -1, 0, NULL, NULL, &sz);
1132 ok(ret == FALSE, "got %d\n", ret);
1133 ret = GetTextExtentExPointW(hdc, wt, -1, 0, NULL, NULL, &sz1);
1134 ok(ret == FALSE, "got %d\n", ret);
1136 hfont = SelectObject(hdc, hfont);
1137 DeleteObject(hfont);
1138 ReleaseDC(NULL, hdc);
1141 static void test_GetGlyphIndices(void)
1143 HDC hdc;
1144 HFONT hfont;
1145 DWORD charcount;
1146 LOGFONTA lf;
1147 DWORD flags = 0;
1148 WCHAR testtext[] = {'T','e','s','t',0xffff,0};
1149 WORD glyphs[(sizeof(testtext)/2)-1];
1150 TEXTMETRIC textm;
1151 HFONT hOldFont;
1153 if (!pGetGlyphIndicesW) {
1154 win_skip("GetGlyphIndicesW not available on platform\n");
1155 return;
1158 hdc = GetDC(0);
1160 memset(&lf, 0, sizeof(lf));
1161 strcpy(lf.lfFaceName, "System");
1162 lf.lfHeight = 16;
1163 lf.lfCharSet = ANSI_CHARSET;
1165 hfont = CreateFontIndirectA(&lf);
1166 ok(hfont != 0, "CreateFontIndirectEx failed\n");
1167 ok(GetTextMetrics(hdc, &textm), "GetTextMetric failed\n");
1168 if (textm.tmCharSet == ANSI_CHARSET)
1170 flags |= GGI_MARK_NONEXISTING_GLYPHS;
1171 charcount = pGetGlyphIndicesW(hdc, testtext, (sizeof(testtext)/2)-1, glyphs, flags);
1172 ok(charcount == 5, "GetGlyphIndicesW count of glyphs should = 5 not %d\n", charcount);
1173 ok((glyphs[4] == 0x001f || glyphs[4] == 0xffff /* Vista */), "GetGlyphIndicesW should have returned a nonexistent char not %04x\n", glyphs[4]);
1174 flags = 0;
1175 charcount = pGetGlyphIndicesW(hdc, testtext, (sizeof(testtext)/2)-1, glyphs, flags);
1176 ok(charcount == 5, "GetGlyphIndicesW count of glyphs should = 5 not %d\n", charcount);
1177 ok(glyphs[4] == textm.tmDefaultChar, "GetGlyphIndicesW should have returned a %04x not %04x\n",
1178 textm.tmDefaultChar, glyphs[4]);
1180 else
1181 /* FIXME: Write tests for non-ANSI charsets. */
1182 skip("GetGlyphIndices System font tests only for ANSI_CHARSET\n");
1184 if(!is_font_installed("Tahoma"))
1186 skip("Tahoma is not installed so skipping this test\n");
1187 return;
1189 memset(&lf, 0, sizeof(lf));
1190 strcpy(lf.lfFaceName, "Tahoma");
1191 lf.lfHeight = 20;
1193 hfont = CreateFontIndirectA(&lf);
1194 hOldFont = SelectObject(hdc, hfont);
1195 ok(GetTextMetrics(hdc, &textm), "GetTextMetric failed\n");
1196 flags |= GGI_MARK_NONEXISTING_GLYPHS;
1197 charcount = pGetGlyphIndicesW(hdc, testtext, (sizeof(testtext)/2)-1, glyphs, flags);
1198 ok(charcount == 5, "GetGlyphIndicesW count of glyphs should = 5 not %d\n", charcount);
1199 ok(glyphs[4] == 0xffff, "GetGlyphIndicesW should have returned 0xffff char not %04x\n", glyphs[4]);
1200 flags = 0;
1201 testtext[0] = textm.tmDefaultChar;
1202 charcount = pGetGlyphIndicesW(hdc, testtext, (sizeof(testtext)/2)-1, glyphs, flags);
1203 ok(charcount == 5, "GetGlyphIndicesW count of glyphs should = 5 not %d\n", charcount);
1204 ok(glyphs[0] == 0, "GetGlyphIndicesW for tmDefaultChar should be 0 not %04x\n", glyphs[0]);
1205 ok(glyphs[4] == 0, "GetGlyphIndicesW should have returned 0 not %04x\n", glyphs[4]);
1206 DeleteObject(SelectObject(hdc, hOldFont));
1209 static void test_GetKerningPairs(void)
1211 static const struct kerning_data
1213 const char face_name[LF_FACESIZE];
1214 LONG height;
1215 /* some interesting fields from OUTLINETEXTMETRIC */
1216 LONG tmHeight, tmAscent, tmDescent;
1217 UINT otmEMSquare;
1218 INT otmAscent;
1219 INT otmDescent;
1220 UINT otmLineGap;
1221 UINT otmsCapEmHeight;
1222 UINT otmsXHeight;
1223 INT otmMacAscent;
1224 INT otmMacDescent;
1225 UINT otmMacLineGap;
1226 UINT otmusMinimumPPEM;
1227 /* small subset of kerning pairs to test */
1228 DWORD total_kern_pairs;
1229 const KERNINGPAIR kern_pair[26];
1230 } kd[] =
1232 {"Arial", 12, 12, 9, 3,
1233 2048, 7, -2, 1, 5, 2, 8, -2, 0, 9,
1236 {' ','A',-1},{' ','T',0},{' ','Y',0},{'1','1',-1},
1237 {'A',' ',-1},{'A','T',-1},{'A','V',-1},{'A','W',0},
1238 {'A','Y',-1},{'A','v',0},{'A','w',0},{'A','y',0},
1239 {'F',',',-1},{'F','.',-1},{'F','A',-1},{'L',' ',0},
1240 {'L','T',-1},{'L','V',-1},{'L','W',-1},{'L','Y',-1},
1241 {915,912,+1},{915,913,-1},{910,912,+1},{910,913,-1},
1242 {933,970,+1},{933,972,-1}
1245 {"Arial", -34, 39, 32, 7,
1246 2048, 25, -7, 5, 17, 9, 31, -7, 1, 9,
1249 {' ','A',-2},{' ','T',-1},{' ','Y',-1},{'1','1',-3},
1250 {'A',' ',-2},{'A','T',-3},{'A','V',-3},{'A','W',-1},
1251 {'A','Y',-3},{'A','v',-1},{'A','w',-1},{'A','y',-1},
1252 {'F',',',-4},{'F','.',-4},{'F','A',-2},{'L',' ',-1},
1253 {'L','T',-3},{'L','V',-3},{'L','W',-3},{'L','Y',-3},
1254 {915,912,+3},{915,913,-3},{910,912,+3},{910,913,-3},
1255 {933,970,+2},{933,972,-3}
1258 { "Arial", 120, 120, 97, 23,
1259 2048, 79, -23, 16, 54, 27, 98, -23, 4, 9,
1262 {' ','A',-6},{' ','T',-2},{' ','Y',-2},{'1','1',-8},
1263 {'A',' ',-6},{'A','T',-8},{'A','V',-8},{'A','W',-4},
1264 {'A','Y',-8},{'A','v',-2},{'A','w',-2},{'A','y',-2},
1265 {'F',',',-12},{'F','.',-12},{'F','A',-6},{'L',' ',-4},
1266 {'L','T',-8},{'L','V',-8},{'L','W',-8},{'L','Y',-8},
1267 {915,912,+9},{915,913,-10},{910,912,+9},{910,913,-8},
1268 {933,970,+6},{933,972,-10}
1271 #if 0 /* this set fails due to +1/-1 errors (rounding bug?), needs investigation. */
1272 { "Arial", 1024 /* usually 1/2 of EM Square */, 1024, 830, 194,
1273 2048, 668, -193, 137, 459, 229, 830, -194, 30, 9,
1276 {' ','A',-51},{' ','T',-17},{' ','Y',-17},{'1','1',-68},
1277 {'A',' ',-51},{'A','T',-68},{'A','V',-68},{'A','W',-34},
1278 {'A','Y',-68},{'A','v',-17},{'A','w',-17},{'A','y',-17},
1279 {'F',',',-102},{'F','.',-102},{'F','A',-51},{'L',' ',-34},
1280 {'L','T',-68},{'L','V',-68},{'L','W',-68},{'L','Y',-68},
1281 {915,912,+73},{915,913,-84},{910,912,+76},{910,913,-68},
1282 {933,970,+54},{933,972,-83}
1285 #endif
1287 LOGFONT lf;
1288 HFONT hfont, hfont_old;
1289 KERNINGPAIR *kern_pair;
1290 HDC hdc;
1291 DWORD total_kern_pairs, ret, i, n, matches;
1293 hdc = GetDC(0);
1295 /* GetKerningPairsA maps unicode set of kerning pairs to current code page
1296 * which may render this test unusable, so we're trying to avoid that.
1298 SetLastError(0xdeadbeef);
1299 GetKerningPairsW(hdc, 0, NULL);
1300 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
1302 win_skip("Skipping the GetKerningPairs test on a Win9x platform\n");
1303 ReleaseDC(0, hdc);
1304 return;
1307 for (i = 0; i < sizeof(kd)/sizeof(kd[0]); i++)
1309 OUTLINETEXTMETRICW otm;
1310 UINT uiRet;
1312 if (!is_font_installed(kd[i].face_name))
1314 trace("%s is not installed so skipping this test\n", kd[i].face_name);
1315 continue;
1318 trace("testing font %s, height %d\n", kd[i].face_name, kd[i].height);
1320 memset(&lf, 0, sizeof(lf));
1321 strcpy(lf.lfFaceName, kd[i].face_name);
1322 lf.lfHeight = kd[i].height;
1323 hfont = CreateFontIndirect(&lf);
1324 assert(hfont != 0);
1326 hfont_old = SelectObject(hdc, hfont);
1328 SetLastError(0xdeadbeef);
1329 otm.otmSize = sizeof(otm); /* just in case for Win9x compatibility */
1330 uiRet = GetOutlineTextMetricsW(hdc, sizeof(otm), &otm);
1331 ok(uiRet == sizeof(otm), "GetOutlineTextMetricsW error %d\n", GetLastError());
1333 ok(match_off_by_1(kd[i].tmHeight, otm.otmTextMetrics.tmHeight), "expected %d, got %d\n",
1334 kd[i].tmHeight, otm.otmTextMetrics.tmHeight);
1335 ok(match_off_by_1(kd[i].tmAscent, otm.otmTextMetrics.tmAscent), "expected %d, got %d\n",
1336 kd[i].tmAscent, otm.otmTextMetrics.tmAscent);
1337 ok(kd[i].tmDescent == otm.otmTextMetrics.tmDescent, "expected %d, got %d\n",
1338 kd[i].tmDescent, otm.otmTextMetrics.tmDescent);
1340 ok(kd[i].otmEMSquare == otm.otmEMSquare, "expected %u, got %u\n",
1341 kd[i].otmEMSquare, otm.otmEMSquare);
1342 ok(kd[i].otmAscent == otm.otmAscent, "expected %d, got %d\n",
1343 kd[i].otmAscent, otm.otmAscent);
1344 ok(kd[i].otmDescent == otm.otmDescent, "expected %d, got %d\n",
1345 kd[i].otmDescent, otm.otmDescent);
1346 ok(kd[i].otmLineGap == otm.otmLineGap, "expected %u, got %u\n",
1347 kd[i].otmLineGap, otm.otmLineGap);
1348 ok(near_match(kd[i].otmMacDescent, otm.otmMacDescent), "expected %d, got %d\n",
1349 kd[i].otmMacDescent, otm.otmMacDescent);
1350 ok(near_match(kd[i].otmMacAscent, otm.otmMacAscent), "expected %d, got %d\n",
1351 kd[i].otmMacAscent, otm.otmMacAscent);
1352 todo_wine {
1353 ok(kd[i].otmsCapEmHeight == otm.otmsCapEmHeight, "expected %u, got %u\n",
1354 kd[i].otmsCapEmHeight, otm.otmsCapEmHeight);
1355 ok(kd[i].otmsXHeight == otm.otmsXHeight, "expected %u, got %u\n",
1356 kd[i].otmsXHeight, otm.otmsXHeight);
1357 /* FIXME: this one sometimes succeeds due to expected 0, enable it when removing todo */
1358 if (0) ok(kd[i].otmMacLineGap == otm.otmMacLineGap, "expected %u, got %u\n",
1359 kd[i].otmMacLineGap, otm.otmMacLineGap);
1360 ok(kd[i].otmusMinimumPPEM == otm.otmusMinimumPPEM, "expected %u, got %u\n",
1361 kd[i].otmusMinimumPPEM, otm.otmusMinimumPPEM);
1364 total_kern_pairs = GetKerningPairsW(hdc, 0, NULL);
1365 trace("total_kern_pairs %u\n", total_kern_pairs);
1366 kern_pair = HeapAlloc(GetProcessHeap(), 0, total_kern_pairs * sizeof(*kern_pair));
1368 /* Win98 (GetKerningPairsA) and XP behave differently here, the test
1369 * passes on XP.
1371 SetLastError(0xdeadbeef);
1372 ret = GetKerningPairsW(hdc, 0, kern_pair);
1373 ok(GetLastError() == ERROR_INVALID_PARAMETER,
1374 "got error %u, expected ERROR_INVALID_PARAMETER\n", GetLastError());
1375 ok(ret == 0, "got %u, expected 0\n", ret);
1377 ret = GetKerningPairsW(hdc, 100, NULL);
1378 ok(ret == total_kern_pairs, "got %u, expected %u\n", ret, total_kern_pairs);
1380 ret = GetKerningPairsW(hdc, total_kern_pairs/2, kern_pair);
1381 ok(ret == total_kern_pairs/2, "got %u, expected %u\n", ret, total_kern_pairs/2);
1383 ret = GetKerningPairsW(hdc, total_kern_pairs, kern_pair);
1384 ok(ret == total_kern_pairs, "got %u, expected %u\n", ret, total_kern_pairs);
1386 matches = 0;
1388 for (n = 0; n < ret; n++)
1390 DWORD j;
1391 /* Disabled to limit console spam */
1392 if (0 && kern_pair[n].wFirst < 127 && kern_pair[n].wSecond < 127)
1393 trace("{'%c','%c',%d},\n",
1394 kern_pair[n].wFirst, kern_pair[n].wSecond, kern_pair[n].iKernAmount);
1395 for (j = 0; j < kd[i].total_kern_pairs; j++)
1397 if (kern_pair[n].wFirst == kd[i].kern_pair[j].wFirst &&
1398 kern_pair[n].wSecond == kd[i].kern_pair[j].wSecond)
1400 ok(kern_pair[n].iKernAmount == kd[i].kern_pair[j].iKernAmount,
1401 "pair %d:%d got %d, expected %d\n",
1402 kern_pair[n].wFirst, kern_pair[n].wSecond,
1403 kern_pair[n].iKernAmount, kd[i].kern_pair[j].iKernAmount);
1404 matches++;
1409 ok(matches == kd[i].total_kern_pairs, "got matches %u, expected %u\n",
1410 matches, kd[i].total_kern_pairs);
1412 HeapFree(GetProcessHeap(), 0, kern_pair);
1414 SelectObject(hdc, hfont_old);
1415 DeleteObject(hfont);
1418 ReleaseDC(0, hdc);
1421 static void test_height_selection(void)
1423 static const struct font_data
1425 const char face_name[LF_FACESIZE];
1426 int requested_height;
1427 int weight, height, ascent, descent, int_leading, ext_leading, dpi;
1428 } fd[] =
1430 {"Tahoma", -12, FW_NORMAL, 14, 12, 2, 2, 0, 96 },
1431 {"Tahoma", -24, FW_NORMAL, 29, 24, 5, 5, 0, 96 },
1432 {"Tahoma", -48, FW_NORMAL, 58, 48, 10, 10, 0, 96 },
1433 {"Tahoma", -96, FW_NORMAL, 116, 96, 20, 20, 0, 96 },
1434 {"Tahoma", -192, FW_NORMAL, 232, 192, 40, 40, 0, 96 },
1435 {"Tahoma", 12, FW_NORMAL, 12, 10, 2, 2, 0, 96 },
1436 {"Tahoma", 24, FW_NORMAL, 24, 20, 4, 4, 0, 96 },
1437 {"Tahoma", 48, FW_NORMAL, 48, 40, 8, 8, 0, 96 },
1438 {"Tahoma", 96, FW_NORMAL, 96, 80, 16, 17, 0, 96 },
1439 {"Tahoma", 192, FW_NORMAL, 192, 159, 33, 33, 0, 96 }
1441 HDC hdc;
1442 LOGFONT lf;
1443 HFONT hfont, old_hfont;
1444 TEXTMETRIC tm;
1445 INT ret, i;
1447 hdc = CreateCompatibleDC(0);
1448 assert(hdc);
1450 for (i = 0; i < sizeof(fd)/sizeof(fd[0]); i++)
1452 if (!is_truetype_font_installed(fd[i].face_name))
1454 skip("%s is not installed\n", fd[i].face_name);
1455 continue;
1458 memset(&lf, 0, sizeof(lf));
1459 lf.lfHeight = fd[i].requested_height;
1460 lf.lfWeight = fd[i].weight;
1461 strcpy(lf.lfFaceName, fd[i].face_name);
1463 hfont = CreateFontIndirect(&lf);
1464 assert(hfont);
1466 old_hfont = SelectObject(hdc, hfont);
1467 ret = GetTextMetrics(hdc, &tm);
1468 ok(ret, "GetTextMetrics error %d\n", GetLastError());
1469 if(fd[i].dpi == tm.tmDigitizedAspectX)
1471 trace("found font %s, height %d charset %x dpi %d\n", lf.lfFaceName, lf.lfHeight, lf.lfCharSet, fd[i].dpi);
1472 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);
1473 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);
1474 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);
1475 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);
1476 #if 0 /* FIXME: calculation of tmInternalLeading in Wine doesn't match what Windows does */
1477 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);
1478 #endif
1479 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);
1482 SelectObject(hdc, old_hfont);
1483 DeleteObject(hfont);
1486 DeleteDC(hdc);
1489 static void test_GetOutlineTextMetrics(void)
1491 OUTLINETEXTMETRIC *otm;
1492 LOGFONT lf;
1493 HFONT hfont, hfont_old;
1494 HDC hdc;
1495 DWORD ret, otm_size;
1496 LPSTR unset_ptr;
1498 if (!is_font_installed("Arial"))
1500 skip("Arial is not installed\n");
1501 return;
1504 hdc = GetDC(0);
1506 memset(&lf, 0, sizeof(lf));
1507 strcpy(lf.lfFaceName, "Arial");
1508 lf.lfHeight = -13;
1509 lf.lfWeight = FW_NORMAL;
1510 lf.lfPitchAndFamily = DEFAULT_PITCH;
1511 lf.lfQuality = PROOF_QUALITY;
1512 hfont = CreateFontIndirect(&lf);
1513 assert(hfont != 0);
1515 hfont_old = SelectObject(hdc, hfont);
1516 otm_size = GetOutlineTextMetrics(hdc, 0, NULL);
1517 trace("otm buffer size %u (0x%x)\n", otm_size, otm_size);
1519 otm = HeapAlloc(GetProcessHeap(), 0, otm_size);
1521 memset(otm, 0xAA, otm_size);
1522 SetLastError(0xdeadbeef);
1523 otm->otmSize = sizeof(*otm); /* just in case for Win9x compatibility */
1524 ret = GetOutlineTextMetrics(hdc, otm->otmSize, otm);
1525 ok(ret == 1 /* Win9x */ ||
1526 ret == otm->otmSize /* XP*/,
1527 "expected %u, got %u, error %d\n", otm->otmSize, ret, GetLastError());
1528 if (ret != 1) /* Win9x doesn't care about pointing beyond of the buffer */
1530 ok(otm->otmpFamilyName == NULL, "expected NULL got %p\n", otm->otmpFamilyName);
1531 ok(otm->otmpFaceName == NULL, "expected NULL got %p\n", otm->otmpFaceName);
1532 ok(otm->otmpStyleName == NULL, "expected NULL got %p\n", otm->otmpStyleName);
1533 ok(otm->otmpFullName == NULL, "expected NULL got %p\n", otm->otmpFullName);
1536 memset(otm, 0xAA, otm_size);
1537 SetLastError(0xdeadbeef);
1538 otm->otmSize = otm_size; /* just in case for Win9x compatibility */
1539 ret = GetOutlineTextMetrics(hdc, otm->otmSize, otm);
1540 ok(ret == 1 /* Win9x */ ||
1541 ret == otm->otmSize /* XP*/,
1542 "expected %u, got %u, error %d\n", otm->otmSize, ret, GetLastError());
1543 if (ret != 1) /* Win9x doesn't care about pointing beyond of the buffer */
1545 ok(otm->otmpFamilyName != NULL, "expected not NULL got %p\n", otm->otmpFamilyName);
1546 ok(otm->otmpFaceName != NULL, "expected not NULL got %p\n", otm->otmpFaceName);
1547 ok(otm->otmpStyleName != NULL, "expected not NULL got %p\n", otm->otmpStyleName);
1548 ok(otm->otmpFullName != NULL, "expected not NULL got %p\n", otm->otmpFullName);
1551 /* ask about truncated data */
1552 memset(otm, 0xAA, otm_size);
1553 memset(&unset_ptr, 0xAA, sizeof(unset_ptr));
1554 SetLastError(0xdeadbeef);
1555 otm->otmSize = sizeof(*otm) - sizeof(LPSTR); /* just in case for Win9x compatibility */
1556 ret = GetOutlineTextMetrics(hdc, otm->otmSize, otm);
1557 ok(ret == 1 /* Win9x */ ||
1558 ret == otm->otmSize /* XP*/,
1559 "expected %u, got %u, error %d\n", otm->otmSize, ret, GetLastError());
1560 if (ret != 1) /* Win9x doesn't care about pointing beyond of the buffer */
1562 ok(otm->otmpFamilyName == NULL, "expected NULL got %p\n", otm->otmpFamilyName);
1563 ok(otm->otmpFaceName == NULL, "expected NULL got %p\n", otm->otmpFaceName);
1564 ok(otm->otmpStyleName == NULL, "expected NULL got %p\n", otm->otmpStyleName);
1566 ok(otm->otmpFullName == unset_ptr, "expected %p got %p\n", unset_ptr, otm->otmpFullName);
1568 HeapFree(GetProcessHeap(), 0, otm);
1570 SelectObject(hdc, hfont_old);
1571 DeleteObject(hfont);
1573 ReleaseDC(0, hdc);
1576 static void testJustification(HDC hdc, PSTR str, RECT *clientArea)
1578 INT y,
1579 breakCount,
1580 justifiedWidth = 0, /* to test GetTextExtentExPointW() */
1581 areaWidth = clientArea->right - clientArea->left,
1582 nErrors = 0, e;
1583 BOOL lastExtent = FALSE;
1584 PSTR pFirstChar, pLastChar;
1585 SIZE size;
1586 TEXTMETRICA tm;
1587 struct err
1589 char extent[100];
1590 int GetTextExtentExPointWWidth;
1591 } error[10];
1593 GetTextMetricsA(hdc, &tm);
1594 y = clientArea->top;
1595 do {
1596 breakCount = 0;
1597 while (*str == tm.tmBreakChar) str++; /* skip leading break chars */
1598 pFirstChar = str;
1600 do {
1601 pLastChar = str;
1603 /* if not at the end of the string, ... */
1604 if (*str == '\0') break;
1605 /* ... add the next word to the current extent */
1606 while (*str != '\0' && *str++ != tm.tmBreakChar);
1607 breakCount++;
1608 SetTextJustification(hdc, 0, 0);
1609 GetTextExtentPoint32(hdc, pFirstChar, str - pFirstChar - 1, &size);
1610 } while ((int) size.cx < areaWidth);
1612 /* ignore trailing break chars */
1613 breakCount--;
1614 while (*(pLastChar - 1) == tm.tmBreakChar)
1616 pLastChar--;
1617 breakCount--;
1620 if (*str == '\0' || breakCount <= 0) pLastChar = str;
1622 SetTextJustification(hdc, 0, 0);
1623 GetTextExtentPoint32(hdc, pFirstChar, pLastChar - pFirstChar, &size);
1625 /* do not justify the last extent */
1626 if (*str != '\0' && breakCount > 0)
1628 SetTextJustification(hdc, areaWidth - size.cx, breakCount);
1629 GetTextExtentPoint32(hdc, pFirstChar, pLastChar - pFirstChar, &size);
1630 justifiedWidth = size.cx;
1632 else lastExtent = TRUE;
1634 /* catch errors and report them */
1635 if (!lastExtent && (justifiedWidth != areaWidth))
1637 memset(error[nErrors].extent, 0, 100);
1638 memcpy(error[nErrors].extent, pFirstChar, pLastChar - pFirstChar);
1639 error[nErrors].GetTextExtentExPointWWidth = justifiedWidth;
1640 nErrors++;
1643 y += size.cy;
1644 str = pLastChar;
1645 } while (*str && y < clientArea->bottom);
1647 for (e = 0; e < nErrors; e++)
1649 /* The width returned by GetTextExtentPoint32() is exactly the same
1650 returned by GetTextExtentExPointW() - see dlls/gdi32/font.c */
1651 ok(error[e].GetTextExtentExPointWWidth == areaWidth,
1652 "GetTextExtentPointW() for \"%s\" should have returned a width of %d, not %d.\n",
1653 error[e].extent, areaWidth, error[e].GetTextExtentExPointWWidth);
1657 static void test_SetTextJustification(void)
1659 HDC hdc;
1660 RECT clientArea;
1661 LOGFONTA lf;
1662 HFONT hfont;
1663 HWND hwnd;
1664 static char testText[] =
1665 "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do "
1666 "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut "
1667 "enim ad minim veniam, quis nostrud exercitation ullamco laboris "
1668 "nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in "
1669 "reprehenderit in voluptate velit esse cillum dolore eu fugiat "
1670 "nulla pariatur. Excepteur sint occaecat cupidatat non proident, "
1671 "sunt in culpa qui officia deserunt mollit anim id est laborum.";
1673 hwnd = CreateWindowExA(0, "static", "", WS_POPUP, 0,0, 400,400, 0, 0, 0, NULL);
1674 GetClientRect( hwnd, &clientArea );
1675 hdc = GetDC( hwnd );
1677 memset(&lf, 0, sizeof lf);
1678 lf.lfCharSet = ANSI_CHARSET;
1679 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
1680 lf.lfWeight = FW_DONTCARE;
1681 lf.lfHeight = 20;
1682 lf.lfQuality = DEFAULT_QUALITY;
1683 lstrcpyA(lf.lfFaceName, "Times New Roman");
1684 hfont = create_font("Times New Roman", &lf);
1685 SelectObject(hdc, hfont);
1687 testJustification(hdc, testText, &clientArea);
1689 DeleteObject(hfont);
1690 ReleaseDC(hwnd, hdc);
1691 DestroyWindow(hwnd);
1694 static BOOL get_glyph_indices(INT charset, UINT code_page, WORD *idx, UINT count, BOOL unicode)
1696 HDC hdc;
1697 LOGFONTA lf;
1698 HFONT hfont, hfont_old;
1699 CHARSETINFO csi;
1700 FONTSIGNATURE fs;
1701 INT cs;
1702 DWORD i, ret;
1703 char name[64];
1705 assert(count <= 128);
1707 memset(&lf, 0, sizeof(lf));
1709 lf.lfCharSet = charset;
1710 lf.lfHeight = 10;
1711 lstrcpyA(lf.lfFaceName, "Arial");
1712 SetLastError(0xdeadbeef);
1713 hfont = CreateFontIndirectA(&lf);
1714 ok(hfont != 0, "CreateFontIndirectA error %u\n", GetLastError());
1716 hdc = GetDC(0);
1717 hfont_old = SelectObject(hdc, hfont);
1719 cs = GetTextCharsetInfo(hdc, &fs, 0);
1720 ok(cs == charset, "expected %d, got %d\n", charset, cs);
1722 SetLastError(0xdeadbeef);
1723 ret = GetTextFaceA(hdc, sizeof(name), name);
1724 ok(ret, "GetTextFaceA error %u\n", GetLastError());
1726 if (charset == SYMBOL_CHARSET)
1728 ok(strcmp("Arial", name), "face name should NOT be Arial\n");
1729 ok(fs.fsCsb[0] & (1 << 31), "symbol encoding should be available\n");
1731 else
1733 ok(!strcmp("Arial", name), "face name should be Arial, not %s\n", name);
1734 ok(!(fs.fsCsb[0] & (1 << 31)), "symbol encoding should NOT be available\n");
1737 if (!TranslateCharsetInfo((DWORD *)(INT_PTR)cs, &csi, TCI_SRCCHARSET))
1739 trace("Can't find codepage for charset %d\n", cs);
1740 ReleaseDC(0, hdc);
1741 return FALSE;
1743 ok(csi.ciACP == code_page, "expected %d, got %d\n", code_page, csi.ciACP);
1745 if (pGdiGetCodePage != NULL && pGdiGetCodePage(hdc) != code_page)
1747 skip("Font code page %d, looking for code page %d\n",
1748 pGdiGetCodePage(hdc), code_page);
1749 ReleaseDC(0, hdc);
1750 return FALSE;
1753 if (unicode)
1755 char ansi_buf[128];
1756 WCHAR unicode_buf[128];
1758 for (i = 0; i < count; i++) ansi_buf[i] = (BYTE)(i + 128);
1760 MultiByteToWideChar(code_page, 0, ansi_buf, count, unicode_buf, count);
1762 SetLastError(0xdeadbeef);
1763 ret = pGetGlyphIndicesW(hdc, unicode_buf, count, idx, 0);
1764 ok(ret == count, "GetGlyphIndicesW expected %d got %d, error %u\n",
1765 count, ret, GetLastError());
1767 else
1769 char ansi_buf[128];
1771 for (i = 0; i < count; i++) ansi_buf[i] = (BYTE)(i + 128);
1773 SetLastError(0xdeadbeef);
1774 ret = pGetGlyphIndicesA(hdc, ansi_buf, count, idx, 0);
1775 ok(ret == count, "GetGlyphIndicesA expected %d got %d, error %u\n",
1776 count, ret, GetLastError());
1779 SelectObject(hdc, hfont_old);
1780 DeleteObject(hfont);
1782 ReleaseDC(0, hdc);
1784 return TRUE;
1787 static void test_font_charset(void)
1789 static struct charset_data
1791 INT charset;
1792 UINT code_page;
1793 WORD font_idxA[128], font_idxW[128];
1794 } cd[] =
1796 { ANSI_CHARSET, 1252 },
1797 { RUSSIAN_CHARSET, 1251 },
1798 { SYMBOL_CHARSET, CP_SYMBOL } /* keep it as the last one */
1800 int i;
1802 if (!pGetGlyphIndicesA || !pGetGlyphIndicesW)
1804 win_skip("Skipping the font charset test on a Win9x platform\n");
1805 return;
1808 if (!is_font_installed("Arial"))
1810 skip("Arial is not installed\n");
1811 return;
1814 for (i = 0; i < sizeof(cd)/sizeof(cd[0]); i++)
1816 if (cd[i].charset == SYMBOL_CHARSET)
1818 if (!is_font_installed("Symbol") && !is_font_installed("Wingdings"))
1820 skip("Symbol or Wingdings is not installed\n");
1821 break;
1824 if (get_glyph_indices(cd[i].charset, cd[i].code_page, cd[i].font_idxA, 128, FALSE) &&
1825 get_glyph_indices(cd[i].charset, cd[i].code_page, cd[i].font_idxW, 128, TRUE))
1826 ok(!memcmp(cd[i].font_idxA, cd[i].font_idxW, 128*sizeof(WORD)), "%d: indices don't match\n", i);
1829 ok(memcmp(cd[0].font_idxW, cd[1].font_idxW, 128*sizeof(WORD)), "0 vs 1: indices shouldn't match\n");
1830 if (i > 2)
1832 ok(memcmp(cd[0].font_idxW, cd[2].font_idxW, 128*sizeof(WORD)), "0 vs 2: indices shouldn't match\n");
1833 ok(memcmp(cd[1].font_idxW, cd[2].font_idxW, 128*sizeof(WORD)), "1 vs 2: indices shouldn't match\n");
1835 else
1836 skip("Symbol or Wingdings is not installed\n");
1839 static void test_GetFontUnicodeRanges(void)
1841 LOGFONTA lf;
1842 HDC hdc;
1843 HFONT hfont, hfont_old;
1844 DWORD size;
1845 GLYPHSET *gs;
1846 DWORD i;
1848 if (!pGetFontUnicodeRanges)
1850 win_skip("GetFontUnicodeRanges not available before W2K\n");
1851 return;
1854 memset(&lf, 0, sizeof(lf));
1855 lstrcpyA(lf.lfFaceName, "Arial");
1856 hfont = create_font("Arial", &lf);
1858 hdc = GetDC(0);
1859 hfont_old = SelectObject(hdc, hfont);
1861 size = pGetFontUnicodeRanges(NULL, NULL);
1862 ok(!size, "GetFontUnicodeRanges succeeded unexpectedly\n");
1864 size = pGetFontUnicodeRanges(hdc, NULL);
1865 ok(size, "GetFontUnicodeRanges failed unexpectedly\n");
1867 gs = HeapAlloc(GetProcessHeap(), 0, size);
1869 size = pGetFontUnicodeRanges(hdc, gs);
1870 ok(size, "GetFontUnicodeRanges failed\n");
1872 if (0) /* Disabled to limit console spam */
1873 for (i = 0; i < gs->cRanges; i++)
1874 trace("%03d wcLow %04x cGlyphs %u\n", i, gs->ranges[i].wcLow, gs->ranges[i].cGlyphs);
1875 trace("found %u ranges\n", gs->cRanges);
1877 HeapFree(GetProcessHeap(), 0, gs);
1879 SelectObject(hdc, hfont_old);
1880 DeleteObject(hfont);
1881 ReleaseDC(NULL, hdc);
1884 #define MAX_ENUM_FONTS 4096
1886 struct enum_font_data
1888 int total;
1889 LOGFONT lf[MAX_ENUM_FONTS];
1892 struct enum_font_dataW
1894 int total;
1895 LOGFONTW lf[MAX_ENUM_FONTS];
1898 static INT CALLBACK arial_enum_proc(const LOGFONT *lf, const TEXTMETRIC *tm, DWORD type, LPARAM lParam)
1900 struct enum_font_data *efd = (struct enum_font_data *)lParam;
1902 ok(lf->lfHeight == tm->tmHeight, "lfHeight %d != tmHeight %d\n", lf->lfHeight, tm->tmHeight);
1904 if (type != TRUETYPE_FONTTYPE) return 1;
1905 if (0) /* Disabled to limit console spam */
1906 trace("enumed font \"%s\", charset %d, height %d, weight %d, italic %d\n",
1907 lf->lfFaceName, lf->lfCharSet, lf->lfHeight, lf->lfWeight, lf->lfItalic);
1908 if (efd->total < MAX_ENUM_FONTS)
1909 efd->lf[efd->total++] = *lf;
1910 else
1911 trace("enum tests invalid; you have more than %d fonts\n", MAX_ENUM_FONTS);
1913 return 1;
1916 static INT CALLBACK arial_enum_procw(const LOGFONTW *lf, const TEXTMETRICW *tm, DWORD type, LPARAM lParam)
1918 struct enum_font_dataW *efd = (struct enum_font_dataW *)lParam;
1920 ok(lf->lfHeight == tm->tmHeight, "lfHeight %d != tmHeight %d\n", lf->lfHeight, tm->tmHeight);
1922 if (type != TRUETYPE_FONTTYPE) return 1;
1923 if (0) /* Disabled to limit console spam */
1924 trace("enumed font %s, charset %d, height %d, weight %d, italic %d\n",
1925 wine_dbgstr_w(lf->lfFaceName), lf->lfCharSet, lf->lfHeight, lf->lfWeight, lf->lfItalic);
1926 if (efd->total < MAX_ENUM_FONTS)
1927 efd->lf[efd->total++] = *lf;
1928 else
1929 trace("enum tests invalid; you have more than %d fonts\n", MAX_ENUM_FONTS);
1931 return 1;
1934 static void get_charset_stats(struct enum_font_data *efd,
1935 int *ansi_charset, int *symbol_charset,
1936 int *russian_charset)
1938 int i;
1940 *ansi_charset = 0;
1941 *symbol_charset = 0;
1942 *russian_charset = 0;
1944 for (i = 0; i < efd->total; i++)
1946 switch (efd->lf[i].lfCharSet)
1948 case ANSI_CHARSET:
1949 (*ansi_charset)++;
1950 break;
1951 case SYMBOL_CHARSET:
1952 (*symbol_charset)++;
1953 break;
1954 case RUSSIAN_CHARSET:
1955 (*russian_charset)++;
1956 break;
1961 static void get_charset_statsW(struct enum_font_dataW *efd,
1962 int *ansi_charset, int *symbol_charset,
1963 int *russian_charset)
1965 int i;
1967 *ansi_charset = 0;
1968 *symbol_charset = 0;
1969 *russian_charset = 0;
1971 for (i = 0; i < efd->total; i++)
1973 switch (efd->lf[i].lfCharSet)
1975 case ANSI_CHARSET:
1976 (*ansi_charset)++;
1977 break;
1978 case SYMBOL_CHARSET:
1979 (*symbol_charset)++;
1980 break;
1981 case RUSSIAN_CHARSET:
1982 (*russian_charset)++;
1983 break;
1988 static void test_EnumFontFamilies(const char *font_name, INT font_charset)
1990 struct enum_font_data efd;
1991 struct enum_font_dataW efdw;
1992 LOGFONT lf;
1993 HDC hdc;
1994 int i, ret, ansi_charset, symbol_charset, russian_charset;
1996 trace("Testing font %s, charset %d\n", *font_name ? font_name : "<empty>", font_charset);
1998 if (*font_name && !is_truetype_font_installed(font_name))
2000 skip("%s is not installed\n", font_name);
2001 return;
2004 hdc = GetDC(0);
2006 /* Observed behaviour: EnumFontFamilies enumerates aliases like "Arial Cyr"
2007 * while EnumFontFamiliesEx doesn't.
2009 if (!*font_name && font_charset == DEFAULT_CHARSET) /* do it only once */
2012 * Use EnumFontFamiliesW since win98 crashes when the
2013 * second parameter is NULL using EnumFontFamilies
2015 efdw.total = 0;
2016 SetLastError(0xdeadbeef);
2017 ret = EnumFontFamiliesW(hdc, NULL, arial_enum_procw, (LPARAM)&efdw);
2018 ok(ret || GetLastError() == ERROR_CALL_NOT_IMPLEMENTED, "EnumFontFamiliesW error %u\n", GetLastError());
2019 if(ret)
2021 get_charset_statsW(&efdw, &ansi_charset, &symbol_charset, &russian_charset);
2022 trace("enumerated ansi %d, symbol %d, russian %d fonts for NULL\n",
2023 ansi_charset, symbol_charset, russian_charset);
2024 ok(efdw.total > 0, "fonts enumerated: NULL\n");
2025 ok(ansi_charset > 0, "NULL family should enumerate ANSI_CHARSET\n");
2026 ok(symbol_charset > 0, "NULL family should enumerate SYMBOL_CHARSET\n");
2027 ok(russian_charset > 0 ||
2028 broken(russian_charset == 0), /* NT4 */
2029 "NULL family should enumerate RUSSIAN_CHARSET\n");
2032 efdw.total = 0;
2033 SetLastError(0xdeadbeef);
2034 ret = EnumFontFamiliesExW(hdc, NULL, arial_enum_procw, (LPARAM)&efdw, 0);
2035 ok(ret || GetLastError() == ERROR_CALL_NOT_IMPLEMENTED, "EnumFontFamiliesExW error %u\n", GetLastError());
2036 if(ret)
2038 get_charset_statsW(&efdw, &ansi_charset, &symbol_charset, &russian_charset);
2039 trace("enumerated ansi %d, symbol %d, russian %d fonts for NULL\n",
2040 ansi_charset, symbol_charset, russian_charset);
2041 ok(efdw.total > 0, "fonts enumerated: NULL\n");
2042 ok(ansi_charset > 0, "NULL family should enumerate ANSI_CHARSET\n");
2043 ok(symbol_charset > 0, "NULL family should enumerate SYMBOL_CHARSET\n");
2044 ok(russian_charset > 0, "NULL family should enumerate RUSSIAN_CHARSET\n");
2048 efd.total = 0;
2049 SetLastError(0xdeadbeef);
2050 ret = EnumFontFamilies(hdc, font_name, arial_enum_proc, (LPARAM)&efd);
2051 ok(ret, "EnumFontFamilies error %u\n", GetLastError());
2052 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
2053 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s\n",
2054 ansi_charset, symbol_charset, russian_charset,
2055 *font_name ? font_name : "<empty>");
2056 if (*font_name)
2057 ok(efd.total > 0, "no fonts enumerated: %s\n", font_name);
2058 else
2059 ok(!efd.total, "no fonts should be enumerated for empty font_name\n");
2060 for (i = 0; i < efd.total; i++)
2062 /* FIXME: remove completely once Wine is fixed */
2063 if (efd.lf[i].lfCharSet != font_charset)
2065 todo_wine
2066 ok(efd.lf[i].lfCharSet == font_charset, "%d: got charset %d\n", i, efd.lf[i].lfCharSet);
2068 else
2069 ok(efd.lf[i].lfCharSet == font_charset, "%d: got charset %d\n", i, efd.lf[i].lfCharSet);
2070 ok(!lstrcmp(efd.lf[i].lfFaceName, font_name), "expected %s, got %s\n",
2071 font_name, efd.lf[i].lfFaceName);
2074 memset(&lf, 0, sizeof(lf));
2075 lf.lfCharSet = ANSI_CHARSET;
2076 lstrcpy(lf.lfFaceName, font_name);
2077 efd.total = 0;
2078 SetLastError(0xdeadbeef);
2079 ret = EnumFontFamiliesEx(hdc, &lf, arial_enum_proc, (LPARAM)&efd, 0);
2080 ok(ret, "EnumFontFamiliesEx error %u\n", GetLastError());
2081 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
2082 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s ANSI_CHARSET\n",
2083 ansi_charset, symbol_charset, russian_charset,
2084 *font_name ? font_name : "<empty>");
2085 if (font_charset == SYMBOL_CHARSET)
2087 if (*font_name)
2088 ok(efd.total == 0, "no fonts should be enumerated: %s ANSI_CHARSET\n", font_name);
2089 else
2090 ok(efd.total > 0, "no fonts enumerated: %s\n", font_name);
2092 else
2094 ok(efd.total > 0, "no fonts enumerated: %s ANSI_CHARSET\n", font_name);
2095 for (i = 0; i < efd.total; i++)
2097 ok(efd.lf[i].lfCharSet == ANSI_CHARSET, "%d: got charset %d\n", i, efd.lf[i].lfCharSet);
2098 if (*font_name)
2099 ok(!lstrcmp(efd.lf[i].lfFaceName, font_name), "expected %s, got %s\n",
2100 font_name, efd.lf[i].lfFaceName);
2104 /* DEFAULT_CHARSET should enumerate all available charsets */
2105 memset(&lf, 0, sizeof(lf));
2106 lf.lfCharSet = DEFAULT_CHARSET;
2107 lstrcpy(lf.lfFaceName, font_name);
2108 efd.total = 0;
2109 SetLastError(0xdeadbeef);
2110 EnumFontFamiliesEx(hdc, &lf, arial_enum_proc, (LPARAM)&efd, 0);
2111 ok(ret, "EnumFontFamiliesEx error %u\n", GetLastError());
2112 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
2113 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s DEFAULT_CHARSET\n",
2114 ansi_charset, symbol_charset, russian_charset,
2115 *font_name ? font_name : "<empty>");
2116 ok(efd.total > 0, "no fonts enumerated: %s DEFAULT_CHARSET\n", font_name);
2117 for (i = 0; i < efd.total; i++)
2119 if (*font_name)
2120 ok(!lstrcmp(efd.lf[i].lfFaceName, font_name), "expected %s, got %s\n",
2121 font_name, efd.lf[i].lfFaceName);
2123 if (*font_name)
2125 switch (font_charset)
2127 case ANSI_CHARSET:
2128 ok(ansi_charset > 0,
2129 "ANSI_CHARSET should enumerate ANSI_CHARSET for %s\n", font_name);
2130 ok(!symbol_charset,
2131 "ANSI_CHARSET should NOT enumerate SYMBOL_CHARSET for %s\n", font_name);
2132 ok(russian_charset > 0,
2133 "ANSI_CHARSET should enumerate RUSSIAN_CHARSET for %s\n", font_name);
2134 break;
2135 case SYMBOL_CHARSET:
2136 ok(!ansi_charset,
2137 "SYMBOL_CHARSET should NOT enumerate ANSI_CHARSET for %s\n", font_name);
2138 ok(symbol_charset,
2139 "SYMBOL_CHARSET should enumerate SYMBOL_CHARSET for %s\n", font_name);
2140 ok(!russian_charset,
2141 "SYMBOL_CHARSET should NOT enumerate RUSSIAN_CHARSET for %s\n", font_name);
2142 break;
2143 case DEFAULT_CHARSET:
2144 ok(ansi_charset > 0,
2145 "DEFAULT_CHARSET should enumerate ANSI_CHARSET for %s\n", font_name);
2146 ok(symbol_charset > 0,
2147 "DEFAULT_CHARSET should enumerate SYMBOL_CHARSET for %s\n", font_name);
2148 ok(russian_charset > 0,
2149 "DEFAULT_CHARSET should enumerate RUSSIAN_CHARSET for %s\n", font_name);
2150 break;
2153 else
2155 ok(ansi_charset > 0,
2156 "DEFAULT_CHARSET should enumerate ANSI_CHARSET for %s\n", *font_name ? font_name : "<empty>");
2157 ok(symbol_charset > 0,
2158 "DEFAULT_CHARSET should enumerate SYMBOL_CHARSET for %s\n", *font_name ? font_name : "<empty>");
2159 ok(russian_charset > 0,
2160 "DEFAULT_CHARSET should enumerate RUSSIAN_CHARSET for %s\n", *font_name ? font_name : "<empty>");
2163 memset(&lf, 0, sizeof(lf));
2164 lf.lfCharSet = SYMBOL_CHARSET;
2165 lstrcpy(lf.lfFaceName, font_name);
2166 efd.total = 0;
2167 SetLastError(0xdeadbeef);
2168 EnumFontFamiliesEx(hdc, &lf, arial_enum_proc, (LPARAM)&efd, 0);
2169 ok(ret, "EnumFontFamiliesEx error %u\n", GetLastError());
2170 get_charset_stats(&efd, &ansi_charset, &symbol_charset, &russian_charset);
2171 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s SYMBOL_CHARSET\n",
2172 ansi_charset, symbol_charset, russian_charset,
2173 *font_name ? font_name : "<empty>");
2174 if (*font_name && font_charset == ANSI_CHARSET)
2175 ok(efd.total == 0, "no fonts should be enumerated: %s SYMBOL_CHARSET\n", font_name);
2176 else
2178 ok(efd.total > 0, "no fonts enumerated: %s SYMBOL_CHARSET\n", font_name);
2179 for (i = 0; i < efd.total; i++)
2181 ok(efd.lf[i].lfCharSet == SYMBOL_CHARSET, "%d: got charset %d\n", i, efd.lf[i].lfCharSet);
2182 if (*font_name)
2183 ok(!lstrcmp(efd.lf[i].lfFaceName, font_name), "expected %s, got %s\n",
2184 font_name, efd.lf[i].lfFaceName);
2187 ok(!ansi_charset,
2188 "SYMBOL_CHARSET should NOT enumerate ANSI_CHARSET for %s\n", *font_name ? font_name : "<empty>");
2189 ok(symbol_charset > 0,
2190 "SYMBOL_CHARSET should enumerate SYMBOL_CHARSET for %s\n", *font_name ? font_name : "<empty>");
2191 ok(!russian_charset,
2192 "SYMBOL_CHARSET should NOT enumerate RUSSIAN_CHARSET for %s\n", *font_name ? font_name : "<empty>");
2195 ReleaseDC(0, hdc);
2198 static INT CALLBACK enum_font_data_proc(const LOGFONT *lf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
2200 struct enum_font_data *efd = (struct enum_font_data *)lParam;
2202 if (type != TRUETYPE_FONTTYPE) return 1;
2204 if (efd->total < MAX_ENUM_FONTS)
2205 efd->lf[efd->total++] = *lf;
2206 else
2207 trace("enum tests invalid; you have more than %d fonts\n", MAX_ENUM_FONTS);
2209 return 1;
2212 static void test_EnumFontFamiliesEx_default_charset(void)
2214 struct enum_font_data efd;
2215 LOGFONT gui_font, enum_font;
2216 DWORD ret;
2217 HDC hdc;
2219 ret = GetObject(GetStockObject(DEFAULT_GUI_FONT), sizeof(gui_font), &gui_font);
2220 ok(ret, "GetObject failed.\n");
2221 if (!ret)
2222 return;
2224 efd.total = 0;
2226 hdc = GetDC(0);
2227 memset(&enum_font, 0, sizeof(enum_font));
2228 lstrcpy(enum_font.lfFaceName, gui_font.lfFaceName);
2229 enum_font.lfCharSet = DEFAULT_CHARSET;
2230 EnumFontFamiliesEx(hdc, &enum_font, enum_font_data_proc, (LPARAM)&efd, 0);
2231 ReleaseDC(0, hdc);
2233 if (efd.total == 0) {
2234 skip("'%s' is not found or not a TrueType font.\n", gui_font.lfFaceName);
2235 return;
2237 trace("'%s' has %d charsets.\n", gui_font.lfFaceName, efd.total);
2239 ok(efd.lf[0].lfCharSet == gui_font.lfCharSet,
2240 "(%s) got charset %d expected %d\n",
2241 efd.lf[0].lfFaceName, efd.lf[0].lfCharSet, gui_font.lfCharSet);
2243 return;
2246 static void test_negative_width(HDC hdc, const LOGFONTA *lf)
2248 HFONT hfont, hfont_prev;
2249 DWORD ret;
2250 GLYPHMETRICS gm1, gm2;
2251 LOGFONTA lf2 = *lf;
2252 WORD idx;
2254 if(!pGetGlyphIndicesA)
2255 return;
2257 /* negative widths are handled just as positive ones */
2258 lf2.lfWidth = -lf->lfWidth;
2260 SetLastError(0xdeadbeef);
2261 hfont = CreateFontIndirectA(lf);
2262 ok(hfont != 0, "CreateFontIndirect error %u\n", GetLastError());
2263 check_font("original", lf, hfont);
2265 hfont_prev = SelectObject(hdc, hfont);
2267 ret = pGetGlyphIndicesA(hdc, "x", 1, &idx, GGI_MARK_NONEXISTING_GLYPHS);
2268 if (ret == GDI_ERROR || idx == 0xffff)
2270 SelectObject(hdc, hfont_prev);
2271 DeleteObject(hfont);
2272 skip("Font %s doesn't contain 'x', skipping the test\n", lf->lfFaceName);
2273 return;
2276 /* filling with 0xaa causes false pass under WINEDEBUG=warn+heap */
2277 memset(&gm1, 0xab, sizeof(gm1));
2278 SetLastError(0xdeadbeef);
2279 ret = GetGlyphOutlineA(hdc, 'x', GGO_METRICS, &gm1, 0, NULL, &mat);
2280 ok(ret != GDI_ERROR, "GetGlyphOutline error 0x%x\n", GetLastError());
2282 SelectObject(hdc, hfont_prev);
2283 DeleteObject(hfont);
2285 SetLastError(0xdeadbeef);
2286 hfont = CreateFontIndirectA(&lf2);
2287 ok(hfont != 0, "CreateFontIndirect error %u\n", GetLastError());
2288 check_font("negative width", &lf2, hfont);
2290 hfont_prev = SelectObject(hdc, hfont);
2292 memset(&gm2, 0xbb, sizeof(gm2));
2293 SetLastError(0xdeadbeef);
2294 ret = GetGlyphOutlineA(hdc, 'x', GGO_METRICS, &gm2, 0, NULL, &mat);
2295 ok(ret != GDI_ERROR, "GetGlyphOutline error 0x%x\n", GetLastError());
2297 SelectObject(hdc, hfont_prev);
2298 DeleteObject(hfont);
2300 ok(gm1.gmBlackBoxX == gm2.gmBlackBoxX &&
2301 gm1.gmBlackBoxY == gm2.gmBlackBoxY &&
2302 gm1.gmptGlyphOrigin.x == gm2.gmptGlyphOrigin.x &&
2303 gm1.gmptGlyphOrigin.y == gm2.gmptGlyphOrigin.y &&
2304 gm1.gmCellIncX == gm2.gmCellIncX &&
2305 gm1.gmCellIncY == gm2.gmCellIncY,
2306 "gm1=%d,%d,%d,%d,%d,%d gm2=%d,%d,%d,%d,%d,%d\n",
2307 gm1.gmBlackBoxX, gm1.gmBlackBoxY, gm1.gmptGlyphOrigin.x,
2308 gm1.gmptGlyphOrigin.y, gm1.gmCellIncX, gm1.gmCellIncY,
2309 gm2.gmBlackBoxX, gm2.gmBlackBoxY, gm2.gmptGlyphOrigin.x,
2310 gm2.gmptGlyphOrigin.y, gm2.gmCellIncX, gm2.gmCellIncY);
2313 /* PANOSE is 10 bytes in size, need to pack the structure properly */
2314 #include "pshpack2.h"
2315 typedef struct
2317 USHORT version;
2318 SHORT xAvgCharWidth;
2319 USHORT usWeightClass;
2320 USHORT usWidthClass;
2321 SHORT fsType;
2322 SHORT ySubscriptXSize;
2323 SHORT ySubscriptYSize;
2324 SHORT ySubscriptXOffset;
2325 SHORT ySubscriptYOffset;
2326 SHORT ySuperscriptXSize;
2327 SHORT ySuperscriptYSize;
2328 SHORT ySuperscriptXOffset;
2329 SHORT ySuperscriptYOffset;
2330 SHORT yStrikeoutSize;
2331 SHORT yStrikeoutPosition;
2332 SHORT sFamilyClass;
2333 PANOSE panose;
2334 ULONG ulUnicodeRange1;
2335 ULONG ulUnicodeRange2;
2336 ULONG ulUnicodeRange3;
2337 ULONG ulUnicodeRange4;
2338 CHAR achVendID[4];
2339 USHORT fsSelection;
2340 USHORT usFirstCharIndex;
2341 USHORT usLastCharIndex;
2342 /* According to the Apple spec, original version didn't have the below fields,
2343 * version numbers were taken from the OpenType spec.
2345 /* version 0 (TrueType 1.5) */
2346 USHORT sTypoAscender;
2347 USHORT sTypoDescender;
2348 USHORT sTypoLineGap;
2349 USHORT usWinAscent;
2350 USHORT usWinDescent;
2351 /* version 1 (TrueType 1.66) */
2352 ULONG ulCodePageRange1;
2353 ULONG ulCodePageRange2;
2354 /* version 2 (OpenType 1.2) */
2355 SHORT sxHeight;
2356 SHORT sCapHeight;
2357 USHORT usDefaultChar;
2358 USHORT usBreakChar;
2359 USHORT usMaxContext;
2360 } TT_OS2_V2;
2361 #include "poppack.h"
2363 #ifdef WORDS_BIGENDIAN
2364 #define GET_BE_WORD(x) (x)
2365 #define GET_BE_DWORD(x) (x)
2366 #else
2367 #define GET_BE_WORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
2368 #define GET_BE_DWORD(x) MAKELONG(GET_BE_WORD(HIWORD(x)), GET_BE_WORD(LOWORD(x)));
2369 #endif
2371 #define MS_MAKE_TAG(ch0, ch1, ch2, ch3) \
2372 ((DWORD)(BYTE)(ch0) | ((DWORD)(BYTE)(ch1) << 8) | \
2373 ((DWORD)(BYTE)(ch2) << 16) | ((DWORD)(BYTE)(ch3) << 24))
2374 #define MS_OS2_TAG MS_MAKE_TAG('O','S','/','2')
2375 #define MS_CMAP_TAG MS_MAKE_TAG('c','m','a','p')
2376 #define MS_NAME_TAG MS_MAKE_TAG('n','a','m','e')
2378 typedef struct
2380 USHORT version;
2381 USHORT num_tables;
2382 } cmap_header;
2384 typedef struct
2386 USHORT plat_id;
2387 USHORT enc_id;
2388 ULONG offset;
2389 } cmap_encoding_record;
2391 typedef struct
2393 USHORT format;
2394 USHORT length;
2395 USHORT language;
2397 BYTE glyph_ids[256];
2398 } cmap_format_0;
2400 typedef struct
2402 USHORT format;
2403 USHORT length;
2404 USHORT language;
2406 USHORT seg_countx2;
2407 USHORT search_range;
2408 USHORT entry_selector;
2409 USHORT range_shift;
2411 USHORT end_count[1]; /* this is a variable-sized array of length seg_countx2 / 2 */
2412 /* Then follows:
2413 USHORT pad;
2414 USHORT start_count[seg_countx2 / 2];
2415 USHORT id_delta[seg_countx2 / 2];
2416 USHORT id_range_offset[seg_countx2 / 2];
2417 USHORT glyph_ids[];
2419 } cmap_format_4;
2421 typedef struct
2423 USHORT end_count;
2424 USHORT start_count;
2425 USHORT id_delta;
2426 USHORT id_range_offset;
2427 } cmap_format_4_seg;
2429 static void expect_ff(const TEXTMETRICA *tmA, const TT_OS2_V2 *os2, WORD family, const char *name)
2431 ok((tmA->tmPitchAndFamily & 0xf0) == family ||
2432 broken(PRIMARYLANGID(GetSystemDefaultLangID()) != LANG_ENGLISH),
2433 "%s: expected family %02x got %02x. panose %d-%d-%d-%d-...\n",
2434 name, family, tmA->tmPitchAndFamily, os2->panose.bFamilyType, os2->panose.bSerifStyle,
2435 os2->panose.bWeight, os2->panose.bProportion);
2438 static BOOL get_first_last_from_cmap0(void *ptr, DWORD *first, DWORD *last)
2440 int i;
2441 cmap_format_0 *cmap = (cmap_format_0*)ptr;
2443 *first = 256;
2445 for(i = 0; i < 256; i++)
2447 if(cmap->glyph_ids[i] == 0) continue;
2448 *last = i;
2449 if(*first == 256) *first = i;
2451 if(*first == 256) return FALSE;
2452 return TRUE;
2455 static void get_seg4(cmap_format_4 *cmap, USHORT seg_num, cmap_format_4_seg *seg)
2457 USHORT segs = GET_BE_WORD(cmap->seg_countx2) / 2;
2458 seg->end_count = GET_BE_WORD(cmap->end_count[seg_num]);
2459 seg->start_count = GET_BE_WORD(cmap->end_count[segs + 1 + seg_num]);
2460 seg->id_delta = GET_BE_WORD(cmap->end_count[2 * segs + 1 + seg_num]);
2461 seg->id_range_offset = GET_BE_WORD(cmap->end_count[3 * segs + 1 + seg_num]);
2464 static BOOL get_first_last_from_cmap4(void *ptr, DWORD *first, DWORD *last, DWORD limit)
2466 int i;
2467 cmap_format_4 *cmap = (cmap_format_4*)ptr;
2468 USHORT seg_count = GET_BE_WORD(cmap->seg_countx2) / 2;
2469 USHORT const *glyph_ids = cmap->end_count + 4 * seg_count + 1;
2471 *first = 0x10000;
2473 for(i = 0; i < seg_count; i++)
2475 DWORD code, index;
2476 cmap_format_4_seg seg;
2478 get_seg4(cmap, i, &seg);
2479 for(code = seg.start_count; code <= seg.end_count; code++)
2481 if(seg.id_range_offset == 0)
2482 index = (seg.id_delta + code) & 0xffff;
2483 else
2485 index = seg.id_range_offset / 2
2486 + code - seg.start_count
2487 + i - seg_count;
2489 /* some fonts have broken last segment */
2490 if ((char *)(glyph_ids + index + 1) < (char *)ptr + limit)
2491 index = GET_BE_WORD(glyph_ids[index]);
2492 else
2494 trace("segment %04x/%04x index %04x points to nowhere\n",
2495 seg.start_count, seg.end_count, index);
2496 index = 0;
2498 if(index) index += seg.id_delta;
2500 if(*first == 0x10000)
2501 *last = *first = code;
2502 else if(index)
2503 *last = code;
2507 if(*first == 0x10000) return FALSE;
2508 return TRUE;
2511 static void *get_cmap(cmap_header *header, USHORT plat_id, USHORT enc_id)
2513 USHORT i;
2514 cmap_encoding_record *record = (cmap_encoding_record *)(header + 1);
2516 for(i = 0; i < GET_BE_WORD(header->num_tables); i++)
2518 if(GET_BE_WORD(record->plat_id) == plat_id && GET_BE_WORD(record->enc_id) == enc_id)
2519 return (BYTE *)header + GET_BE_DWORD(record->offset);
2520 record++;
2522 return NULL;
2525 typedef enum
2527 cmap_none,
2528 cmap_ms_unicode,
2529 cmap_ms_symbol
2530 } cmap_type;
2532 static BOOL get_first_last_from_cmap(HDC hdc, DWORD *first, DWORD *last, cmap_type *cmap_type)
2534 LONG size, ret;
2535 cmap_header *header;
2536 void *cmap;
2537 BOOL r = FALSE;
2538 WORD format;
2540 size = GetFontData(hdc, MS_CMAP_TAG, 0, NULL, 0);
2541 ok(size != GDI_ERROR, "no cmap table found\n");
2542 if(size == GDI_ERROR) return FALSE;
2544 header = HeapAlloc(GetProcessHeap(), 0, size);
2545 ret = GetFontData(hdc, MS_CMAP_TAG, 0, header, size);
2546 ok(ret == size, "GetFontData should return %u not %u\n", size, ret);
2547 ok(GET_BE_WORD(header->version) == 0, "got cmap version %d\n", GET_BE_WORD(header->version));
2549 cmap = get_cmap(header, 3, 1);
2550 if(cmap)
2551 *cmap_type = cmap_ms_unicode;
2552 else
2554 cmap = get_cmap(header, 3, 0);
2555 if(cmap) *cmap_type = cmap_ms_symbol;
2557 if(!cmap)
2559 *cmap_type = cmap_none;
2560 goto end;
2563 format = GET_BE_WORD(*(WORD *)cmap);
2564 switch(format)
2566 case 0:
2567 r = get_first_last_from_cmap0(cmap, first, last);
2568 break;
2569 case 4:
2570 r = get_first_last_from_cmap4(cmap, first, last, size);
2571 break;
2572 default:
2573 trace("unhandled cmap format %d\n", format);
2574 break;
2577 end:
2578 HeapFree(GetProcessHeap(), 0, header);
2579 return r;
2582 #define TT_PLATFORM_MICROSOFT 3
2583 #define TT_MS_ID_UNICODE_CS 1
2584 #define TT_MS_LANGID_ENGLISH_UNITED_STATES 0x0409
2585 #define TT_NAME_ID_FULL_NAME 4
2587 static BOOL get_ttf_nametable_entry(HDC hdc, WORD name_id, char *out_buf, SIZE_T out_size)
2589 struct sfnt_name_header
2591 USHORT format;
2592 USHORT number_of_record;
2593 USHORT storage_offset;
2594 } *header;
2595 struct sfnt_name
2597 USHORT platform_id;
2598 USHORT encoding_id;
2599 USHORT language_id;
2600 USHORT name_id;
2601 USHORT length;
2602 USHORT offset;
2603 } *entry;
2604 BOOL r = FALSE;
2605 LONG size, offset, length;
2606 LONG c, ret;
2607 WCHAR *name;
2608 BYTE *data;
2609 USHORT i;
2611 size = GetFontData(hdc, MS_NAME_TAG, 0, NULL, 0);
2612 ok(size != GDI_ERROR, "no name table found\n");
2613 if(size == GDI_ERROR) return FALSE;
2615 data = HeapAlloc(GetProcessHeap(), 0, size);
2616 ret = GetFontData(hdc, MS_NAME_TAG, 0, data, size);
2617 ok(ret == size, "GetFontData should return %u not %u\n", size, ret);
2619 header = (void *)data;
2620 header->format = GET_BE_WORD(header->format);
2621 header->number_of_record = GET_BE_WORD(header->number_of_record);
2622 header->storage_offset = GET_BE_WORD(header->storage_offset);
2623 if (header->format != 0)
2625 trace("got format %u\n", header->format);
2626 goto out;
2628 if (header->number_of_record == 0 || sizeof(*header) + header->number_of_record * sizeof(*entry) > size)
2630 trace("number records out of range: %d\n", header->number_of_record);
2631 goto out;
2633 if (header->storage_offset >= size)
2635 trace("storage_offset %u > size %u\n", header->storage_offset, size);
2636 goto out;
2639 entry = (void *)&header[1];
2640 for (i = 0; i < header->number_of_record; i++)
2642 if (GET_BE_WORD(entry[i].platform_id) != TT_PLATFORM_MICROSOFT ||
2643 GET_BE_WORD(entry[i].encoding_id) != TT_MS_ID_UNICODE_CS ||
2644 GET_BE_WORD(entry[i].language_id) != TT_MS_LANGID_ENGLISH_UNITED_STATES ||
2645 GET_BE_WORD(entry[i].name_id) != name_id)
2647 continue;
2650 offset = header->storage_offset + GET_BE_WORD(entry[i].offset);
2651 length = GET_BE_WORD(entry[i].length);
2652 if (offset + length > size)
2654 trace("entry %d is out of range\n", i);
2655 break;
2657 if (length >= out_size)
2659 trace("buffer too small for entry %d\n", i);
2660 break;
2663 name = (WCHAR *)(data + offset);
2664 for (c = 0; c < length / 2; c++)
2665 out_buf[c] = GET_BE_WORD(name[c]);
2666 out_buf[c] = 0;
2668 r = TRUE;
2669 break;
2672 out:
2673 HeapFree(GetProcessHeap(), 0, data);
2674 return r;
2677 static void test_text_metrics(const LOGFONTA *lf)
2679 HDC hdc;
2680 HFONT hfont, hfont_old;
2681 TEXTMETRICA tmA;
2682 TT_OS2_V2 tt_os2;
2683 LONG size, ret;
2684 const char *font_name = lf->lfFaceName;
2685 DWORD cmap_first = 0, cmap_last = 0;
2686 cmap_type cmap_type;
2687 BOOL sys_lang_non_english;
2689 sys_lang_non_english = PRIMARYLANGID(GetSystemDefaultLangID()) != LANG_ENGLISH;
2690 hdc = GetDC(0);
2692 SetLastError(0xdeadbeef);
2693 hfont = CreateFontIndirectA(lf);
2694 ok(hfont != 0, "CreateFontIndirect error %u\n", GetLastError());
2696 hfont_old = SelectObject(hdc, hfont);
2698 size = GetFontData(hdc, MS_OS2_TAG, 0, NULL, 0);
2699 if (size == GDI_ERROR)
2701 trace("OS/2 chunk was not found\n");
2702 goto end_of_test;
2704 if (size > sizeof(tt_os2))
2706 trace("got too large OS/2 chunk of size %u\n", size);
2707 size = sizeof(tt_os2);
2710 memset(&tt_os2, 0, sizeof(tt_os2));
2711 ret = GetFontData(hdc, MS_OS2_TAG, 0, &tt_os2, size);
2712 ok(ret == size, "GetFontData should return %u not %u\n", size, ret);
2714 SetLastError(0xdeadbeef);
2715 ret = GetTextMetricsA(hdc, &tmA);
2716 ok(ret, "GetTextMetricsA error %u\n", GetLastError());
2718 if(!get_first_last_from_cmap(hdc, &cmap_first, &cmap_last, &cmap_type))
2720 skip("Unable to retrieve first and last glyphs from cmap\n");
2722 else
2724 USHORT expect_first_A, expect_last_A, expect_break_A, expect_default_A;
2725 USHORT expect_first_W, expect_last_W, expect_break_W, expect_default_W;
2726 UINT os2_first_char, os2_last_char, default_char, break_char;
2727 USHORT version;
2728 TEXTMETRICW tmW;
2730 version = GET_BE_WORD(tt_os2.version);
2732 os2_first_char = GET_BE_WORD(tt_os2.usFirstCharIndex);
2733 os2_last_char = GET_BE_WORD(tt_os2.usLastCharIndex);
2734 default_char = GET_BE_WORD(tt_os2.usDefaultChar);
2735 break_char = GET_BE_WORD(tt_os2.usBreakChar);
2737 trace("font %s charset %u: %x-%x (%x-%x) default %x break %x OS/2 version %u vendor %4.4s\n",
2738 font_name, lf->lfCharSet, os2_first_char, os2_last_char, cmap_first, cmap_last,
2739 default_char, break_char, version, (LPCSTR)&tt_os2.achVendID);
2741 if (cmap_type == cmap_ms_symbol || (cmap_first >= 0xf000 && cmap_first < 0xf100))
2743 expect_first_W = 0;
2744 switch(GetACP())
2746 case 1257: /* Baltic */
2747 expect_last_W = 0xf8fd;
2748 break;
2749 default:
2750 expect_last_W = 0xf0ff;
2752 expect_break_W = 0x20;
2753 expect_default_W = expect_break_W - 1;
2754 expect_first_A = 0x1e;
2755 expect_last_A = min(os2_last_char - os2_first_char + 0x20, 0xff);
2757 else
2759 expect_first_W = cmap_first;
2760 expect_last_W = min(cmap_last, os2_last_char);
2761 if(os2_first_char <= 1)
2762 expect_break_W = os2_first_char + 2;
2763 else if(os2_first_char > 0xff)
2764 expect_break_W = 0x20;
2765 else
2766 expect_break_W = os2_first_char;
2767 expect_default_W = expect_break_W - 1;
2768 expect_first_A = expect_default_W - 1;
2769 expect_last_A = min(expect_last_W, 0xff);
2771 expect_break_A = expect_break_W;
2772 expect_default_A = expect_default_W;
2774 /* Wine currently uses SYMBOL_CHARSET to identify whether the ANSI metrics need special handling */
2775 if(cmap_type != cmap_ms_symbol && tmA.tmCharSet == SYMBOL_CHARSET && expect_first_A != 0x1e)
2776 todo_wine ok(tmA.tmFirstChar == expect_first_A ||
2777 tmA.tmFirstChar == expect_first_A + 1 /* win9x */,
2778 "A: tmFirstChar for %s got %02x expected %02x\n", font_name, tmA.tmFirstChar, expect_first_A);
2779 else
2780 ok(tmA.tmFirstChar == expect_first_A ||
2781 tmA.tmFirstChar == expect_first_A + 1 /* win9x */,
2782 "A: tmFirstChar for %s got %02x expected %02x\n", font_name, tmA.tmFirstChar, expect_first_A);
2783 if (pGdiGetCodePage == NULL || ! IsDBCSLeadByteEx(pGdiGetCodePage(hdc), tmA.tmLastChar))
2784 ok(tmA.tmLastChar == expect_last_A ||
2785 tmA.tmLastChar == 0xff /* win9x */,
2786 "A: tmLastChar for %s got %02x expected %02x\n", font_name, tmA.tmLastChar, expect_last_A);
2787 else
2788 skip("tmLastChar is DBCS lead byte\n");
2789 ok(tmA.tmBreakChar == expect_break_A, "A: tmBreakChar for %s got %02x expected %02x\n",
2790 font_name, tmA.tmBreakChar, expect_break_A);
2791 ok(tmA.tmDefaultChar == expect_default_A || broken(sys_lang_non_english),
2792 "A: tmDefaultChar for %s got %02x expected %02x\n",
2793 font_name, tmA.tmDefaultChar, expect_default_A);
2796 SetLastError(0xdeadbeef);
2797 ret = GetTextMetricsW(hdc, &tmW);
2798 ok(ret || GetLastError() == ERROR_CALL_NOT_IMPLEMENTED,
2799 "GetTextMetricsW error %u\n", GetLastError());
2800 if (ret)
2802 /* Wine uses the os2 first char */
2803 if(cmap_first != os2_first_char && cmap_type != cmap_ms_symbol)
2804 todo_wine ok(tmW.tmFirstChar == expect_first_W, "W: tmFirstChar for %s got %02x expected %02x\n",
2805 font_name, tmW.tmFirstChar, expect_first_W);
2806 else
2807 ok(tmW.tmFirstChar == expect_first_W, "W: tmFirstChar for %s got %02x expected %02x\n",
2808 font_name, tmW.tmFirstChar, expect_first_W);
2810 /* Wine uses the os2 last char */
2811 if(expect_last_W != os2_last_char && cmap_type != cmap_ms_symbol)
2812 todo_wine ok(tmW.tmLastChar == expect_last_W, "W: tmLastChar for %s got %02x expected %02x\n",
2813 font_name, tmW.tmLastChar, expect_last_W);
2814 else
2815 ok(tmW.tmLastChar == expect_last_W, "W: tmLastChar for %s got %02x expected %02x\n",
2816 font_name, tmW.tmLastChar, expect_last_W);
2817 ok(tmW.tmBreakChar == expect_break_W, "W: tmBreakChar for %s got %02x expected %02x\n",
2818 font_name, tmW.tmBreakChar, expect_break_W);
2819 ok(tmW.tmDefaultChar == expect_default_W || broken(sys_lang_non_english),
2820 "W: tmDefaultChar for %s got %02x expected %02x\n",
2821 font_name, tmW.tmDefaultChar, expect_default_W);
2823 /* Test the aspect ratio while we have tmW */
2824 ret = GetDeviceCaps(hdc, LOGPIXELSX);
2825 ok(tmW.tmDigitizedAspectX == ret, "W: tmDigitizedAspectX %u != %u\n",
2826 tmW.tmDigitizedAspectX, ret);
2827 ret = GetDeviceCaps(hdc, LOGPIXELSY);
2828 ok(tmW.tmDigitizedAspectX == ret, "W: tmDigitizedAspectY %u != %u\n",
2829 tmW.tmDigitizedAspectX, ret);
2833 /* test FF_ values */
2834 switch(tt_os2.panose.bFamilyType)
2836 case PAN_ANY:
2837 case PAN_NO_FIT:
2838 case PAN_FAMILY_TEXT_DISPLAY:
2839 case PAN_FAMILY_PICTORIAL:
2840 default:
2841 if((tmA.tmPitchAndFamily & 1) == 0 || /* fixed */
2842 tt_os2.panose.bProportion == PAN_PROP_MONOSPACED)
2844 expect_ff(&tmA, &tt_os2, FF_MODERN, font_name);
2845 break;
2847 switch(tt_os2.panose.bSerifStyle)
2849 case PAN_ANY:
2850 case PAN_NO_FIT:
2851 default:
2852 expect_ff(&tmA, &tt_os2, FF_DONTCARE, font_name);
2853 break;
2855 case PAN_SERIF_COVE:
2856 case PAN_SERIF_OBTUSE_COVE:
2857 case PAN_SERIF_SQUARE_COVE:
2858 case PAN_SERIF_OBTUSE_SQUARE_COVE:
2859 case PAN_SERIF_SQUARE:
2860 case PAN_SERIF_THIN:
2861 case PAN_SERIF_BONE:
2862 case PAN_SERIF_EXAGGERATED:
2863 case PAN_SERIF_TRIANGLE:
2864 expect_ff(&tmA, &tt_os2, FF_ROMAN, font_name);
2865 break;
2867 case PAN_SERIF_NORMAL_SANS:
2868 case PAN_SERIF_OBTUSE_SANS:
2869 case PAN_SERIF_PERP_SANS:
2870 case PAN_SERIF_FLARED:
2871 case PAN_SERIF_ROUNDED:
2872 expect_ff(&tmA, &tt_os2, FF_SWISS, font_name);
2873 break;
2875 break;
2877 case PAN_FAMILY_SCRIPT:
2878 expect_ff(&tmA, &tt_os2, FF_SCRIPT, font_name);
2879 break;
2881 case PAN_FAMILY_DECORATIVE:
2882 expect_ff(&tmA, &tt_os2, FF_DECORATIVE, font_name);
2883 break;
2886 test_negative_width(hdc, lf);
2888 end_of_test:
2889 SelectObject(hdc, hfont_old);
2890 DeleteObject(hfont);
2892 ReleaseDC(0, hdc);
2895 static INT CALLBACK enum_truetype_font_proc(const LOGFONT *lf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
2897 INT *enumed = (INT *)lParam;
2899 if (type == TRUETYPE_FONTTYPE)
2901 (*enumed)++;
2902 test_text_metrics(lf);
2904 return 1;
2907 static void test_GetTextMetrics(void)
2909 LOGFONTA lf;
2910 HDC hdc;
2911 INT enumed;
2913 /* Report only once */
2914 if(!pGetGlyphIndicesA)
2915 win_skip("GetGlyphIndicesA is unavailable, negative width will not be checked\n");
2917 hdc = GetDC(0);
2919 memset(&lf, 0, sizeof(lf));
2920 lf.lfCharSet = DEFAULT_CHARSET;
2921 enumed = 0;
2922 EnumFontFamiliesExA(hdc, &lf, enum_truetype_font_proc, (LPARAM)&enumed, 0);
2923 trace("Tested metrics of %d truetype fonts\n", enumed);
2925 ReleaseDC(0, hdc);
2928 static void test_nonexistent_font(void)
2930 static const struct
2932 const char *name;
2933 int charset;
2934 } font_subst[] =
2936 { "Times New Roman Baltic", 186 },
2937 { "Times New Roman CE", 238 },
2938 { "Times New Roman CYR", 204 },
2939 { "Times New Roman Greek", 161 },
2940 { "Times New Roman TUR", 162 }
2942 LOGFONTA lf;
2943 HDC hdc;
2944 HFONT hfont;
2945 CHARSETINFO csi;
2946 INT cs, expected_cs, i;
2947 char buf[LF_FACESIZE];
2949 if (!is_truetype_font_installed("Arial") ||
2950 !is_truetype_font_installed("Times New Roman"))
2952 skip("Arial or Times New Roman not installed\n");
2953 return;
2956 expected_cs = GetACP();
2957 if (!TranslateCharsetInfo(ULongToPtr(expected_cs), &csi, TCI_SRCCODEPAGE))
2959 skip("TranslateCharsetInfo failed for code page %d\n", expected_cs);
2960 return;
2962 expected_cs = csi.ciCharset;
2963 trace("ACP %d -> charset %d\n", GetACP(), expected_cs);
2965 hdc = GetDC(0);
2967 memset(&lf, 0, sizeof(lf));
2968 lf.lfHeight = 100;
2969 lf.lfWeight = FW_REGULAR;
2970 lf.lfCharSet = ANSI_CHARSET;
2971 lf.lfPitchAndFamily = FF_SWISS;
2972 strcpy(lf.lfFaceName, "Nonexistent font");
2973 hfont = CreateFontIndirectA(&lf);
2974 hfont = SelectObject(hdc, hfont);
2975 GetTextFaceA(hdc, sizeof(buf), buf);
2976 ok(!lstrcmpiA(buf, "Arial"), "Got %s\n", buf);
2977 cs = GetTextCharset(hdc);
2978 ok(cs == ANSI_CHARSET, "expected ANSI_CHARSET, got %d\n", cs);
2979 DeleteObject(SelectObject(hdc, hfont));
2981 memset(&lf, 0, sizeof(lf));
2982 lf.lfHeight = -13;
2983 lf.lfWeight = FW_DONTCARE;
2984 strcpy(lf.lfFaceName, "Nonexistent font");
2985 hfont = CreateFontIndirectA(&lf);
2986 hfont = SelectObject(hdc, hfont);
2987 GetTextFaceA(hdc, sizeof(buf), buf);
2988 todo_wine /* Wine uses Arial for all substitutions */
2989 ok(!lstrcmpiA(buf, "Nonexistent font") /* XP, Vista */ ||
2990 !lstrcmpiA(buf, "MS Serif") || /* Win9x */
2991 !lstrcmpiA(buf, "MS Sans Serif"), /* win2k3 */
2992 "Got %s\n", buf);
2993 cs = GetTextCharset(hdc);
2994 ok(cs == expected_cs || cs == ANSI_CHARSET, "expected %d, got %d\n", expected_cs, cs);
2995 DeleteObject(SelectObject(hdc, hfont));
2997 memset(&lf, 0, sizeof(lf));
2998 lf.lfHeight = -13;
2999 lf.lfWeight = FW_REGULAR;
3000 strcpy(lf.lfFaceName, "Nonexistent font");
3001 hfont = CreateFontIndirectA(&lf);
3002 hfont = SelectObject(hdc, hfont);
3003 GetTextFaceA(hdc, sizeof(buf), buf);
3004 ok(!lstrcmpiA(buf, "Arial") /* XP, Vista */ ||
3005 !lstrcmpiA(buf, "Times New Roman") /* Win9x */, "Got %s\n", buf);
3006 cs = GetTextCharset(hdc);
3007 ok(cs == ANSI_CHARSET, "expected ANSI_CHARSET, got %d\n", cs);
3008 DeleteObject(SelectObject(hdc, hfont));
3010 memset(&lf, 0, sizeof(lf));
3011 lf.lfHeight = -13;
3012 lf.lfWeight = FW_DONTCARE;
3013 strcpy(lf.lfFaceName, "Times New Roman");
3014 hfont = CreateFontIndirectA(&lf);
3015 hfont = SelectObject(hdc, hfont);
3016 GetTextFaceA(hdc, sizeof(buf), buf);
3017 ok(!lstrcmpiA(buf, "Times New Roman"), "Got %s\n", buf);
3018 cs = GetTextCharset(hdc);
3019 ok(cs == ANSI_CHARSET, "expected ANSI_CHARSET, got %d\n", cs);
3020 DeleteObject(SelectObject(hdc, hfont));
3022 for (i = 0; i < sizeof(font_subst)/sizeof(font_subst[0]); i++)
3024 memset(&lf, 0, sizeof(lf));
3025 lf.lfHeight = -13;
3026 lf.lfWeight = FW_REGULAR;
3027 strcpy(lf.lfFaceName, font_subst[i].name);
3028 hfont = CreateFontIndirectA(&lf);
3029 hfont = SelectObject(hdc, hfont);
3030 cs = GetTextCharset(hdc);
3031 if (font_subst[i].charset == expected_cs)
3033 ok(cs == expected_cs, "expected %d, got %d for font %s\n", expected_cs, cs, font_subst[i].name);
3034 GetTextFaceA(hdc, sizeof(buf), buf);
3035 ok(!lstrcmpiA(buf, font_subst[i].name), "expected %s, got %s\n", font_subst[i].name, buf);
3037 else
3039 ok(cs == ANSI_CHARSET, "expected ANSI_CHARSET, got %d for font %s\n", cs, font_subst[i].name);
3040 GetTextFaceA(hdc, sizeof(buf), buf);
3041 ok(!lstrcmpiA(buf, "Arial") /* XP, Vista */ ||
3042 !lstrcmpiA(buf, "Times New Roman") /* Win9x */, "got %s for font %s\n", buf, font_subst[i].name);
3044 DeleteObject(SelectObject(hdc, hfont));
3046 memset(&lf, 0, sizeof(lf));
3047 lf.lfHeight = -13;
3048 lf.lfWeight = FW_DONTCARE;
3049 strcpy(lf.lfFaceName, font_subst[i].name);
3050 hfont = CreateFontIndirectA(&lf);
3051 hfont = SelectObject(hdc, hfont);
3052 GetTextFaceA(hdc, sizeof(buf), buf);
3053 ok(!lstrcmpiA(buf, "Arial") /* Wine */ ||
3054 !lstrcmpiA(buf, font_subst[i].name) /* XP, Vista */ ||
3055 !lstrcmpiA(buf, "MS Serif") /* Win9x */ ||
3056 !lstrcmpiA(buf, "MS Sans Serif"), /* win2k3 */
3057 "got %s for font %s\n", buf, font_subst[i].name);
3058 cs = GetTextCharset(hdc);
3059 ok(cs == expected_cs || cs == ANSI_CHARSET, "expected %d, got %d for font %s\n", expected_cs, cs, font_subst[i].name);
3060 DeleteObject(SelectObject(hdc, hfont));
3063 ReleaseDC(0, hdc);
3066 static void test_GdiRealizationInfo(void)
3068 HDC hdc;
3069 DWORD info[4];
3070 BOOL r;
3071 HFONT hfont, hfont_old;
3072 LOGFONTA lf;
3074 if(!pGdiRealizationInfo)
3076 win_skip("GdiRealizationInfo not available\n");
3077 return;
3080 hdc = GetDC(0);
3082 memset(info, 0xcc, sizeof(info));
3083 r = pGdiRealizationInfo(hdc, info);
3084 ok(r != 0, "ret 0\n");
3085 ok((info[0] & 0xf) == 1, "info[0] = %x for the system font\n", info[0]);
3086 ok(info[3] == 0xcccccccc, "structure longer than 3 dwords\n");
3088 if (!is_truetype_font_installed("Arial"))
3090 skip("skipping GdiRealizationInfo with truetype font\n");
3091 goto end;
3094 memset(&lf, 0, sizeof(lf));
3095 strcpy(lf.lfFaceName, "Arial");
3096 lf.lfHeight = 20;
3097 lf.lfWeight = FW_NORMAL;
3098 hfont = CreateFontIndirectA(&lf);
3099 hfont_old = SelectObject(hdc, hfont);
3101 memset(info, 0xcc, sizeof(info));
3102 r = pGdiRealizationInfo(hdc, info);
3103 ok(r != 0, "ret 0\n");
3104 ok((info[0] & 0xf) == 3, "info[0] = %x for arial\n", info[0]);
3105 ok(info[3] == 0xcccccccc, "structure longer than 3 dwords\n");
3107 DeleteObject(SelectObject(hdc, hfont_old));
3109 end:
3110 ReleaseDC(0, hdc);
3113 /* Tests on XP SP2 show that the ANSI version of GetTextFace does NOT include
3114 the nul in the count of characters copied when the face name buffer is not
3115 NULL, whereas it does if the buffer is NULL. Further, the Unicode version
3116 always includes it. */
3117 static void test_GetTextFace(void)
3119 static const char faceA[] = "Tahoma";
3120 static const WCHAR faceW[] = {'T','a','h','o','m','a', 0};
3121 LOGFONTA fA = {0};
3122 LOGFONTW fW = {0};
3123 char bufA[LF_FACESIZE];
3124 WCHAR bufW[LF_FACESIZE];
3125 HFONT f, g;
3126 HDC dc;
3127 int n;
3129 if(!is_font_installed("Tahoma"))
3131 skip("Tahoma is not installed so skipping this test\n");
3132 return;
3135 /* 'A' case. */
3136 memcpy(fA.lfFaceName, faceA, sizeof faceA);
3137 f = CreateFontIndirectA(&fA);
3138 ok(f != NULL, "CreateFontIndirectA failed\n");
3140 dc = GetDC(NULL);
3141 g = SelectObject(dc, f);
3142 n = GetTextFaceA(dc, sizeof bufA, bufA);
3143 ok(n == sizeof faceA - 1, "GetTextFaceA returned %d\n", n);
3144 ok(lstrcmpA(faceA, bufA) == 0, "GetTextFaceA\n");
3146 /* Play with the count arg. */
3147 bufA[0] = 'x';
3148 n = GetTextFaceA(dc, 0, bufA);
3149 ok(n == 0, "GetTextFaceA returned %d\n", n);
3150 ok(bufA[0] == 'x', "GetTextFaceA buf[0] == %d\n", bufA[0]);
3152 bufA[0] = 'x';
3153 n = GetTextFaceA(dc, 1, bufA);
3154 ok(n == 0, "GetTextFaceA returned %d\n", n);
3155 ok(bufA[0] == '\0', "GetTextFaceA buf[0] == %d\n", bufA[0]);
3157 bufA[0] = 'x'; bufA[1] = 'y';
3158 n = GetTextFaceA(dc, 2, bufA);
3159 ok(n == 1, "GetTextFaceA returned %d\n", n);
3160 ok(bufA[0] == faceA[0] && bufA[1] == '\0', "GetTextFaceA didn't copy\n");
3162 n = GetTextFaceA(dc, 0, NULL);
3163 ok(n == sizeof faceA ||
3164 broken(n == 0), /* win98, winMe */
3165 "GetTextFaceA returned %d\n", n);
3167 DeleteObject(SelectObject(dc, g));
3168 ReleaseDC(NULL, dc);
3170 /* 'W' case. */
3171 memcpy(fW.lfFaceName, faceW, sizeof faceW);
3172 SetLastError(0xdeadbeef);
3173 f = CreateFontIndirectW(&fW);
3174 if (!f && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
3176 win_skip("CreateFontIndirectW is not implemented\n");
3177 return;
3179 ok(f != NULL, "CreateFontIndirectW failed\n");
3181 dc = GetDC(NULL);
3182 g = SelectObject(dc, f);
3183 n = GetTextFaceW(dc, sizeof bufW / sizeof bufW[0], bufW);
3184 ok(n == sizeof faceW / sizeof faceW[0], "GetTextFaceW returned %d\n", n);
3185 ok(lstrcmpW(faceW, bufW) == 0, "GetTextFaceW\n");
3187 /* Play with the count arg. */
3188 bufW[0] = 'x';
3189 n = GetTextFaceW(dc, 0, bufW);
3190 ok(n == 0, "GetTextFaceW returned %d\n", n);
3191 ok(bufW[0] == 'x', "GetTextFaceW buf[0] == %d\n", bufW[0]);
3193 bufW[0] = 'x';
3194 n = GetTextFaceW(dc, 1, bufW);
3195 ok(n == 1, "GetTextFaceW returned %d\n", n);
3196 ok(bufW[0] == '\0', "GetTextFaceW buf[0] == %d\n", bufW[0]);
3198 bufW[0] = 'x'; bufW[1] = 'y';
3199 n = GetTextFaceW(dc, 2, bufW);
3200 ok(n == 2, "GetTextFaceW returned %d\n", n);
3201 ok(bufW[0] == faceW[0] && bufW[1] == '\0', "GetTextFaceW didn't copy\n");
3203 n = GetTextFaceW(dc, 0, NULL);
3204 ok(n == sizeof faceW / sizeof faceW[0], "GetTextFaceW returned %d\n", n);
3206 DeleteObject(SelectObject(dc, g));
3207 ReleaseDC(NULL, dc);
3210 static void test_orientation(void)
3212 static const char test_str[11] = "Test String";
3213 HDC hdc;
3214 LOGFONTA lf;
3215 HFONT hfont, old_hfont;
3216 SIZE size;
3218 if (!is_truetype_font_installed("Arial"))
3220 skip("Arial is not installed\n");
3221 return;
3224 hdc = CreateCompatibleDC(0);
3225 memset(&lf, 0, sizeof(lf));
3226 lstrcpyA(lf.lfFaceName, "Arial");
3227 lf.lfHeight = 72;
3228 lf.lfOrientation = lf.lfEscapement = 900;
3229 hfont = create_font("orientation", &lf);
3230 old_hfont = SelectObject(hdc, hfont);
3231 ok(GetTextExtentExPointA(hdc, test_str, sizeof(test_str), 32767, NULL, NULL, &size), "GetTextExtentExPointA failed\n");
3232 ok(near_match(311, size.cx), "cx should be about 311, got %d\n", size.cx);
3233 ok(near_match(75, size.cy), "cy should be about 75, got %d\n", size.cy);
3234 SelectObject(hdc, old_hfont);
3235 DeleteObject(hfont);
3236 DeleteDC(hdc);
3239 static void test_oemcharset(void)
3241 HDC hdc;
3242 LOGFONTA lf, clf;
3243 HFONT hfont, old_hfont;
3244 int charset;
3246 hdc = CreateCompatibleDC(0);
3247 ZeroMemory(&lf, sizeof(lf));
3248 lf.lfHeight = 12;
3249 lf.lfCharSet = OEM_CHARSET;
3250 lf.lfPitchAndFamily = FIXED_PITCH | FF_MODERN;
3251 lstrcpyA(lf.lfFaceName, "Terminal");
3252 hfont = CreateFontIndirectA(&lf);
3253 old_hfont = SelectObject(hdc, hfont);
3254 charset = GetTextCharset(hdc);
3255 todo_wine
3256 ok(charset == OEM_CHARSET, "expected %d charset, got %d\n", OEM_CHARSET, charset);
3257 hfont = SelectObject(hdc, old_hfont);
3258 GetObjectA(hfont, sizeof(clf), &clf);
3259 ok(!lstrcmpA(clf.lfFaceName, lf.lfFaceName), "expected %s face name, got %s\n", lf.lfFaceName, clf.lfFaceName);
3260 ok(clf.lfPitchAndFamily == lf.lfPitchAndFamily, "expected %x family, got %x\n", lf.lfPitchAndFamily, clf.lfPitchAndFamily);
3261 ok(clf.lfCharSet == lf.lfCharSet, "expected %d charset, got %d\n", lf.lfCharSet, clf.lfCharSet);
3262 ok(clf.lfHeight == lf.lfHeight, "expected %d height, got %d\n", lf.lfHeight, clf.lfHeight);
3263 DeleteObject(hfont);
3264 DeleteDC(hdc);
3267 static void test_GetGlyphOutline(void)
3269 HDC hdc;
3270 GLYPHMETRICS gm, gm2;
3271 LOGFONTA lf;
3272 HFONT hfont, old_hfont;
3273 INT ret, ret2;
3274 static const struct
3276 UINT cs;
3277 UINT a;
3278 UINT w;
3279 } c[] =
3281 {ANSI_CHARSET, 0x30, 0x30},
3282 {SHIFTJIS_CHARSET, 0x82a0, 0x3042},
3283 {HANGEUL_CHARSET, 0x8141, 0xac02},
3284 {JOHAB_CHARSET, 0x8446, 0x3135},
3285 {GB2312_CHARSET, 0x8141, 0x4e04},
3286 {CHINESEBIG5_CHARSET, 0xa142, 0x3001}
3288 UINT i;
3290 if (!is_truetype_font_installed("Tahoma"))
3292 skip("Tahoma is not installed\n");
3293 return;
3296 hdc = CreateCompatibleDC(0);
3297 memset(&lf, 0, sizeof(lf));
3298 lf.lfHeight = 72;
3299 lstrcpyA(lf.lfFaceName, "Tahoma");
3300 SetLastError(0xdeadbeef);
3301 hfont = CreateFontIndirectA(&lf);
3302 ok(hfont != 0, "CreateFontIndirectA error %u\n", GetLastError());
3303 old_hfont = SelectObject(hdc, hfont);
3305 memset(&gm, 0, sizeof(gm));
3306 SetLastError(0xdeadbeef);
3307 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
3308 ok(ret != GDI_ERROR, "GetGlyphOutlineA error %u\n", GetLastError());
3310 memset(&gm, 0, sizeof(gm));
3311 SetLastError(0xdeadbeef);
3312 ret = GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, NULL);
3313 ok(ret == GDI_ERROR, "GetGlyphOutlineA should fail\n");
3314 ok(GetLastError() == 0xdeadbeef ||
3315 GetLastError() == ERROR_INVALID_PARAMETER, /* win98, winMe */
3316 "expected 0xdeadbeef, got %u\n", GetLastError());
3318 memset(&gm, 0, sizeof(gm));
3319 SetLastError(0xdeadbeef);
3320 ret = GetGlyphOutlineW(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
3321 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
3322 ok(ret != GDI_ERROR, "GetGlyphOutlineW error %u\n", GetLastError());
3324 memset(&gm, 0, sizeof(gm));
3325 SetLastError(0xdeadbeef);
3326 ret = GetGlyphOutlineW(hdc, 'A', GGO_METRICS, &gm, 0, NULL, NULL);
3327 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
3329 ok(ret == GDI_ERROR, "GetGlyphOutlineW should fail\n");
3330 ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %u\n", GetLastError());
3333 /* test for needed buffer size request on space char */
3334 memset(&gm, 0, sizeof(gm));
3335 SetLastError(0xdeadbeef);
3336 ret = GetGlyphOutlineW(hdc, ' ', GGO_NATIVE, &gm, 0, NULL, &mat);
3337 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
3338 ok(ret == 0, "GetGlyphOutlineW should return 0 buffer size for space char\n");
3340 /* requesting buffer size for space char + error */
3341 memset(&gm, 0, sizeof(gm));
3342 SetLastError(0xdeadbeef);
3343 ret = GetGlyphOutlineW(0, ' ', GGO_NATIVE, &gm, 0, NULL, NULL);
3344 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
3346 ok(ret == GDI_ERROR, "GetGlyphOutlineW should return GDI_ERROR\n");
3347 ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %u\n", GetLastError());
3350 SelectObject(hdc, old_hfont);
3351 DeleteObject(hfont);
3353 for (i = 0; i < sizeof c / sizeof c[0]; ++i)
3355 lf.lfFaceName[0] = '\0';
3356 lf.lfCharSet = c[i].cs;
3357 lf.lfPitchAndFamily = 0;
3358 if (EnumFontFamiliesEx(hdc, &lf, create_font_proc, (LPARAM)&hfont, 0))
3360 skip("TrueType font for charset %u is not installed\n", c[i].cs);
3361 continue;
3364 old_hfont = SelectObject(hdc, hfont);
3366 /* expected to ignore superfluous bytes (sigle-byte character) */
3367 ret = GetGlyphOutlineA(hdc, 0x8041, GGO_BITMAP, &gm, 0, NULL, &mat);
3368 ret2 = GetGlyphOutlineA(hdc, 0x41, GGO_BITMAP, &gm2, 0, NULL, &mat);
3369 ok(ret == ret2 && memcmp(&gm, &gm2, sizeof gm) == 0, "%d %d\n", ret, ret2);
3371 ret = GetGlyphOutlineA(hdc, 0xcc8041, GGO_BITMAP, &gm, 0, NULL, &mat);
3372 ok(ret == ret2 && memcmp(&gm, &gm2, sizeof gm) == 0,
3373 "Expected to ignore superfluous bytes, got %d %d\n", ret, ret2);
3375 /* expected to ignore superfluous bytes (double-byte character) */
3376 ret = GetGlyphOutlineA(hdc, c[i].a, GGO_BITMAP, &gm, 0, NULL, &mat);
3377 ret2 = GetGlyphOutlineA(hdc, c[i].a | 0xdead0000, GGO_BITMAP, &gm2, 0, NULL, &mat);
3378 ok(ret == ret2 && memcmp(&gm, &gm2, sizeof gm) == 0,
3379 "Expected to ignore superfluous bytes, got %d %d\n", ret, ret2);
3381 /* expected to match wide-char version results */
3382 ret2 = GetGlyphOutlineW(hdc, c[i].w, GGO_BITMAP, &gm2, 0, NULL, &mat);
3383 ok(ret == ret2 && memcmp(&gm, &gm2, sizeof gm) == 0, "%d %d\n", ret, ret2);
3385 hfont = SelectObject(hdc, old_hfont);
3386 DeleteObject(hfont);
3389 DeleteDC(hdc);
3392 /* bug #9995: there is a limit to the character width that can be specified */
3393 static void test_GetTextMetrics2(const char *fontname, int font_height)
3395 HFONT of, hf;
3396 HDC hdc;
3397 TEXTMETRICA tm;
3398 BOOL ret;
3399 int ave_width, height, width, ratio, scale;
3401 if (!is_truetype_font_installed( fontname)) {
3402 skip("%s is not installed\n", fontname);
3403 return;
3405 hdc = CreateCompatibleDC(0);
3406 ok( hdc != NULL, "CreateCompatibleDC failed\n");
3407 /* select width = 0 */
3408 hf = CreateFontA(font_height, 0, 0, 0, FW_REGULAR, FALSE, FALSE, FALSE,
3409 DEFAULT_CHARSET, OUT_TT_PRECIS, CLIP_LH_ANGLES,
3410 DEFAULT_QUALITY, VARIABLE_PITCH,
3411 fontname);
3412 ok( hf != NULL, "CreateFontA(%s, %d) failed\n", fontname, font_height);
3413 of = SelectObject( hdc, hf);
3414 ret = GetTextMetricsA( hdc, &tm);
3415 ok(ret, "GetTextMetricsA error %u\n", GetLastError());
3416 height = tm.tmHeight;
3417 ave_width = tm.tmAveCharWidth;
3418 SelectObject( hdc, of);
3419 DeleteObject( hf);
3421 trace("height %d, ave width %d\n", height, ave_width);
3423 for (width = ave_width * 2; /* nothing*/; width += ave_width)
3425 hf = CreateFont(height, width, 0, 0, FW_REGULAR, FALSE, FALSE, FALSE,
3426 DEFAULT_CHARSET, OUT_TT_PRECIS, CLIP_LH_ANGLES,
3427 DEFAULT_QUALITY, VARIABLE_PITCH, fontname);
3428 ok(hf != 0, "CreateFont failed\n");
3429 of = SelectObject(hdc, hf);
3430 ret = GetTextMetrics(hdc, &tm);
3431 ok(ret, "GetTextMetrics error %u\n", GetLastError());
3432 SelectObject(hdc, of);
3433 DeleteObject(hf);
3435 if (match_off_by_1(tm.tmAveCharWidth, ave_width) || width / height > 200)
3436 break;
3439 DeleteDC(hdc);
3441 ratio = width / height;
3442 scale = width / ave_width;
3444 trace("max width/height ratio (%d / %d) %d, max width scale (%d / %d) %d\n",
3445 width, height, ratio, width, ave_width, scale);
3447 ok(ratio >= 90 && ratio <= 110, "expected width/height ratio 90-110, got %d\n", ratio);
3450 static void test_CreateFontIndirect(void)
3452 LOGFONTA lf, getobj_lf;
3453 int ret, i;
3454 HFONT hfont;
3455 char TestName[][16] = {"Arial", "Arial Bold", "Arial Italic", "Arial Baltic"};
3457 memset(&lf, 0, sizeof(lf));
3458 lf.lfCharSet = ANSI_CHARSET;
3459 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
3460 lf.lfHeight = 16;
3461 lf.lfWidth = 16;
3462 lf.lfQuality = DEFAULT_QUALITY;
3463 lf.lfItalic = FALSE;
3464 lf.lfWeight = FW_DONTCARE;
3466 for (i = 0; i < sizeof(TestName)/sizeof(TestName[0]); i++)
3468 lstrcpyA(lf.lfFaceName, TestName[i]);
3469 hfont = CreateFontIndirectA(&lf);
3470 ok(hfont != 0, "CreateFontIndirectA failed\n");
3471 SetLastError(0xdeadbeef);
3472 ret = GetObject(hfont, sizeof(getobj_lf), &getobj_lf);
3473 ok(ret, "GetObject failed: %d\n", GetLastError());
3474 ok(lf.lfItalic == getobj_lf.lfItalic, "lfItalic: expect %02x got %02x\n", lf.lfItalic, getobj_lf.lfItalic);
3475 ok(lf.lfWeight == getobj_lf.lfWeight ||
3476 broken((SHORT)lf.lfWeight == getobj_lf.lfWeight), /* win9x */
3477 "lfWeight: expect %08x got %08x\n", lf.lfWeight, getobj_lf.lfWeight);
3478 ok(!lstrcmpA(lf.lfFaceName, getobj_lf.lfFaceName) ||
3479 broken(!memcmp(lf.lfFaceName, getobj_lf.lfFaceName, LF_FACESIZE-1)), /* win9x doesn't ensure '\0' termination */
3480 "font names don't match: %s != %s\n", lf.lfFaceName, getobj_lf.lfFaceName);
3481 DeleteObject(hfont);
3485 static void test_CreateFontIndirectEx(void)
3487 ENUMLOGFONTEXDVA lfex;
3488 HFONT hfont;
3490 if (!pCreateFontIndirectExA)
3492 win_skip("CreateFontIndirectExA is not available\n");
3493 return;
3496 if (!is_truetype_font_installed("Arial"))
3498 skip("Arial is not installed\n");
3499 return;
3502 SetLastError(0xdeadbeef);
3503 hfont = pCreateFontIndirectExA(NULL);
3504 ok(hfont == NULL, "got %p\n", hfont);
3505 ok(GetLastError() == 0xdeadbeef, "got error %d\n", GetLastError());
3507 memset(&lfex, 0, sizeof(lfex));
3508 lstrcpyA(lfex.elfEnumLogfontEx.elfLogFont.lfFaceName, "Arial");
3509 hfont = pCreateFontIndirectExA(&lfex);
3510 ok(hfont != 0, "CreateFontIndirectEx failed\n");
3511 if (hfont)
3512 check_font("Arial", &lfex.elfEnumLogfontEx.elfLogFont, hfont);
3513 DeleteObject(hfont);
3516 static void free_font(void *font)
3518 UnmapViewOfFile(font);
3521 static void *load_font(const char *font_name, DWORD *font_size)
3523 char file_name[MAX_PATH];
3524 HANDLE file, mapping;
3525 void *font;
3527 if (!GetWindowsDirectory(file_name, sizeof(file_name))) return NULL;
3528 strcat(file_name, "\\fonts\\");
3529 strcat(file_name, font_name);
3531 file = CreateFile(file_name, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, 0);
3532 if (file == INVALID_HANDLE_VALUE) return NULL;
3534 *font_size = GetFileSize(file, NULL);
3536 mapping = CreateFileMapping(file, NULL, PAGE_READONLY, 0, 0, NULL);
3537 if (!mapping)
3539 CloseHandle(file);
3540 return NULL;
3543 font = MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0);
3545 CloseHandle(file);
3546 CloseHandle(mapping);
3547 return font;
3550 static void test_AddFontMemResource(void)
3552 void *font;
3553 DWORD font_size, num_fonts;
3554 HANDLE ret;
3555 BOOL bRet;
3557 if (!pAddFontMemResourceEx || !pRemoveFontMemResourceEx)
3559 win_skip("AddFontMemResourceEx is not available on this platform\n");
3560 return;
3563 font = load_font("sserife.fon", &font_size);
3564 if (!font)
3566 skip("Unable to locate and load font sserife.fon\n");
3567 return;
3570 SetLastError(0xdeadbeef);
3571 ret = pAddFontMemResourceEx(NULL, 0, NULL, NULL);
3572 ok(!ret, "AddFontMemResourceEx should fail\n");
3573 ok(GetLastError() == ERROR_INVALID_PARAMETER,
3574 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
3575 GetLastError());
3577 SetLastError(0xdeadbeef);
3578 ret = pAddFontMemResourceEx(NULL, 10, NULL, NULL);
3579 ok(!ret, "AddFontMemResourceEx should fail\n");
3580 ok(GetLastError() == ERROR_INVALID_PARAMETER,
3581 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
3582 GetLastError());
3584 SetLastError(0xdeadbeef);
3585 ret = pAddFontMemResourceEx(NULL, 0, NULL, &num_fonts);
3586 ok(!ret, "AddFontMemResourceEx should fail\n");
3587 ok(GetLastError() == ERROR_INVALID_PARAMETER,
3588 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
3589 GetLastError());
3591 SetLastError(0xdeadbeef);
3592 ret = pAddFontMemResourceEx(NULL, 10, NULL, &num_fonts);
3593 ok(!ret, "AddFontMemResourceEx should fail\n");
3594 ok(GetLastError() == ERROR_INVALID_PARAMETER,
3595 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
3596 GetLastError());
3598 SetLastError(0xdeadbeef);
3599 ret = pAddFontMemResourceEx(font, 0, NULL, NULL);
3600 ok(!ret, "AddFontMemResourceEx should fail\n");
3601 ok(GetLastError() == ERROR_INVALID_PARAMETER,
3602 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
3603 GetLastError());
3605 SetLastError(0xdeadbeef);
3606 ret = pAddFontMemResourceEx(font, 10, NULL, NULL);
3607 ok(!ret, "AddFontMemResourceEx should fail\n");
3608 ok(GetLastError() == ERROR_INVALID_PARAMETER,
3609 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
3610 GetLastError());
3612 num_fonts = 0xdeadbeef;
3613 SetLastError(0xdeadbeef);
3614 ret = pAddFontMemResourceEx(font, 0, NULL, &num_fonts);
3615 ok(!ret, "AddFontMemResourceEx should fail\n");
3616 ok(GetLastError() == ERROR_INVALID_PARAMETER,
3617 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
3618 GetLastError());
3619 ok(num_fonts == 0xdeadbeef, "number of loaded fonts should be 0xdeadbeef\n");
3621 if (0) /* hangs under windows 2000 */
3623 num_fonts = 0xdeadbeef;
3624 SetLastError(0xdeadbeef);
3625 ret = pAddFontMemResourceEx(font, 10, NULL, &num_fonts);
3626 ok(!ret, "AddFontMemResourceEx should fail\n");
3627 ok(GetLastError() == 0xdeadbeef,
3628 "Expected GetLastError() to return 0xdeadbeef, got %u\n",
3629 GetLastError());
3630 ok(num_fonts == 0xdeadbeef, "number of loaded fonts should be 0xdeadbeef\n");
3633 num_fonts = 0xdeadbeef;
3634 SetLastError(0xdeadbeef);
3635 ret = pAddFontMemResourceEx(font, font_size, NULL, &num_fonts);
3636 ok(ret != 0, "AddFontMemResourceEx error %d\n", GetLastError());
3637 ok(num_fonts != 0xdeadbeef, "number of loaded fonts should not be 0xdeadbeef\n");
3638 ok(num_fonts != 0, "number of loaded fonts should not be 0\n");
3640 free_font(font);
3642 SetLastError(0xdeadbeef);
3643 bRet = pRemoveFontMemResourceEx(ret);
3644 ok(bRet, "RemoveFontMemResourceEx error %d\n", GetLastError());
3646 /* test invalid pointer to number of loaded fonts */
3647 font = load_font("sserife.fon", &font_size);
3648 ok(font != NULL, "Unable to locate and load font sserife.fon\n");
3650 SetLastError(0xdeadbeef);
3651 ret = pAddFontMemResourceEx(font, font_size, NULL, (void *)0xdeadbeef);
3652 ok(!ret, "AddFontMemResourceEx should fail\n");
3653 ok(GetLastError() == 0xdeadbeef,
3654 "Expected GetLastError() to return 0xdeadbeef, got %u\n",
3655 GetLastError());
3657 SetLastError(0xdeadbeef);
3658 ret = pAddFontMemResourceEx(font, font_size, NULL, NULL);
3659 ok(!ret, "AddFontMemResourceEx should fail\n");
3660 ok(GetLastError() == ERROR_INVALID_PARAMETER,
3661 "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
3662 GetLastError());
3664 free_font(font);
3667 static INT CALLBACK enum_fonts_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lparam)
3669 LOGFONT *lf;
3671 if (type != TRUETYPE_FONTTYPE) return 1;
3673 ok(ntm->tmWeight == elf->lfWeight, "expected %d got %d\n", ntm->tmWeight, elf->lfWeight);
3675 lf = (LOGFONT *)lparam;
3676 *lf = *elf;
3677 return 0;
3680 static INT CALLBACK enum_all_fonts_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lparam)
3682 int ret;
3683 LOGFONT *lf;
3685 if (type != TRUETYPE_FONTTYPE) return 1;
3687 lf = (LOGFONT *)lparam;
3688 ret = strcmp(lf->lfFaceName, elf->lfFaceName);
3689 if(ret == 0)
3691 ok(ntm->tmWeight == elf->lfWeight, "expected %d got %d\n", ntm->tmWeight, elf->lfWeight);
3692 *lf = *elf;
3693 return 0;
3695 return 1;
3698 static void test_EnumFonts(void)
3700 int ret;
3701 LOGFONT lf;
3702 HDC hdc;
3704 if (!is_truetype_font_installed("Arial"))
3706 skip("Arial is not installed\n");
3707 return;
3710 /* Windows uses localized font face names, so Arial Bold won't be found */
3711 if (PRIMARYLANGID(GetUserDefaultLangID()) != LANG_ENGLISH)
3713 skip("User locale is not English, skipping the test\n");
3714 return;
3717 hdc = CreateCompatibleDC(0);
3719 ret = EnumFontFamilies(hdc, "Arial", enum_fonts_proc, (LPARAM)&lf);
3720 ok(!ret, "font Arial is not enumerated\n");
3721 ret = strcmp(lf.lfFaceName, "Arial");
3722 ok(!ret, "expected Arial got %s\n", lf.lfFaceName);
3723 ok(lf.lfWeight == FW_NORMAL, "expected FW_NORMAL got %d\n", lf.lfWeight);
3725 lstrcpy(lf.lfFaceName, "Arial");
3726 ret = EnumFontFamilies(hdc, NULL, enum_all_fonts_proc, (LPARAM)&lf);
3727 ok(!ret, "font Arial is not enumerated\n");
3728 ret = strcmp(lf.lfFaceName, "Arial");
3729 ok(!ret, "expected Arial got %s\n", lf.lfFaceName);
3730 ok(lf.lfWeight == FW_NORMAL, "expected FW_NORMAL got %d\n", lf.lfWeight);
3732 ret = EnumFontFamilies(hdc, "Arial Bold", enum_fonts_proc, (LPARAM)&lf);
3733 ok(!ret, "font Arial Bold is not enumerated\n");
3734 ret = strcmp(lf.lfFaceName, "Arial");
3735 ok(!ret, "expected Arial got %s\n", lf.lfFaceName);
3736 ok(lf.lfWeight == FW_BOLD, "expected FW_BOLD got %d\n", lf.lfWeight);
3738 lstrcpy(lf.lfFaceName, "Arial Bold");
3739 ret = EnumFontFamilies(hdc, NULL, enum_all_fonts_proc, (LPARAM)&lf);
3740 ok(ret, "font Arial Bold should not be enumerated\n");
3742 ret = EnumFontFamilies(hdc, "Arial Bold Italic", enum_fonts_proc, (LPARAM)&lf);
3743 ok(!ret, "font Arial Bold Italic is not enumerated\n");
3744 ret = strcmp(lf.lfFaceName, "Arial");
3745 ok(!ret, "expected Arial got %s\n", lf.lfFaceName);
3746 ok(lf.lfWeight == FW_BOLD, "expected FW_BOLD got %d\n", lf.lfWeight);
3748 lstrcpy(lf.lfFaceName, "Arial Bold Italic");
3749 ret = EnumFontFamilies(hdc, NULL, enum_all_fonts_proc, (LPARAM)&lf);
3750 ok(ret, "font Arial Bold Italic should not be enumerated\n");
3752 ret = EnumFontFamilies(hdc, "Arial Italic Bold", enum_fonts_proc, (LPARAM)&lf);
3753 ok(ret, "font Arial Italic Bold should not be enumerated\n");
3755 lstrcpy(lf.lfFaceName, "Arial Italic Bold");
3756 ret = EnumFontFamilies(hdc, NULL, enum_all_fonts_proc, (LPARAM)&lf);
3757 ok(ret, "font Arial Italic Bold should not be enumerated\n");
3759 DeleteDC(hdc);
3762 static INT CALLBACK is_font_installed_fullname_proc(const LOGFONT *lf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
3764 const ENUMLOGFONT *elf = (const ENUMLOGFONT *)lf;
3765 const char *fullname = (const char *)lParam;
3767 if (!strcmp((const char *)elf->elfFullName, fullname)) return 0;
3769 return 1;
3772 static BOOL is_font_installed_fullname(const char *family, const char *fullname)
3774 HDC hdc = GetDC(0);
3775 BOOL ret = FALSE;
3777 if(!EnumFontFamiliesA(hdc, family, is_font_installed_fullname_proc, (LPARAM)fullname))
3778 ret = TRUE;
3780 ReleaseDC(0, hdc);
3781 return ret;
3784 static void test_fullname(void)
3786 static const char *TestName[] = {"Lucida Sans Demibold Roman", "Lucida Sans Italic", "Lucida Sans Regular"};
3787 char buf[LF_FULLFACESIZE];
3788 HFONT hfont, of;
3789 LOGFONTA lf;
3790 HDC hdc;
3791 int i;
3793 hdc = CreateCompatibleDC(0);
3794 ok(hdc != NULL, "CreateCompatibleDC failed\n");
3796 memset(&lf, 0, sizeof(lf));
3797 lf.lfCharSet = ANSI_CHARSET;
3798 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
3799 lf.lfHeight = 16;
3800 lf.lfWidth = 16;
3801 lf.lfQuality = DEFAULT_QUALITY;
3802 lf.lfItalic = FALSE;
3803 lf.lfWeight = FW_DONTCARE;
3805 for (i = 0; i < sizeof(TestName) / sizeof(TestName[0]); i++)
3807 if (!is_font_installed_fullname("Lucida Sans", TestName[i]))
3809 skip("%s is not installed\n", TestName[i]);
3810 continue;
3813 lstrcpyA(lf.lfFaceName, TestName[i]);
3814 hfont = CreateFontIndirectA(&lf);
3815 ok(hfont != 0, "CreateFontIndirectA failed\n");
3817 of = SelectObject(hdc, hfont);
3818 buf[0] = 0;
3819 ok(get_ttf_nametable_entry(hdc, TT_NAME_ID_FULL_NAME, buf, sizeof(buf)),
3820 "face full name could not be read\n");
3821 ok(!lstrcmpA(buf, TestName[i]), "font full names don't match: %s != %s\n", TestName[i], buf);
3822 SelectObject(hdc, of);
3823 DeleteObject(hfont);
3825 DeleteDC(hdc);
3828 static BOOL write_ttf_file(char *tmp_name)
3830 char tmp_path[MAX_PATH];
3831 HRSRC rsrc;
3832 void *rsrc_data;
3833 DWORD rsrc_size;
3834 HANDLE hfile;
3835 BOOL ret;
3837 SetLastError(0xdeadbeef);
3838 rsrc = FindResource(GetModuleHandle(0), "wine_test.ttf", RT_RCDATA);
3839 ok(rsrc != 0, "FindResource error %d\n", GetLastError());
3840 if (!rsrc) return FALSE;
3841 SetLastError(0xdeadbeef);
3842 rsrc_data = LockResource(LoadResource(GetModuleHandle(0), rsrc));
3843 ok(rsrc_data != 0, "LockResource error %d\n", GetLastError());
3844 if (!rsrc_data) return FALSE;
3845 SetLastError(0xdeadbeef);
3846 rsrc_size = SizeofResource(GetModuleHandle(0), rsrc);
3847 ok(rsrc_size != 0, "SizeofResource error %d\n", GetLastError());
3848 if (!rsrc_size) return FALSE;
3850 SetLastError(0xdeadbeef);
3851 ret = GetTempPath(MAX_PATH, tmp_path);
3852 ok(ret, "GetTempPath() error %d\n", GetLastError());
3853 SetLastError(0xdeadbeef);
3854 ret = GetTempFileName(tmp_path, "ttf", 0, tmp_name);
3855 ok(ret, "GetTempFileName() error %d\n", GetLastError());
3857 SetLastError(0xdeadbeef);
3858 hfile = CreateFile(tmp_name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
3859 ok(hfile != INVALID_HANDLE_VALUE, "CreateFile() error %d\n", GetLastError());
3860 if (hfile == INVALID_HANDLE_VALUE) return FALSE;
3862 SetLastError(0xdeadbeef);
3863 ret = WriteFile(hfile, rsrc_data, rsrc_size, &rsrc_size, NULL);
3864 ok(ret, "WriteFile() error %d\n", GetLastError());
3866 CloseHandle(hfile);
3867 return ret;
3870 static void test_CreateScalableFontResource(void)
3872 char ttf_name[MAX_PATH];
3873 char tmp_path[MAX_PATH];
3874 char fot_name[MAX_PATH];
3875 char *file_part;
3876 DWORD ret;
3878 if (!pAddFontResourceExA || !pRemoveFontResourceExA)
3880 win_skip("AddFontResourceExA is not available on this platform\n");
3881 return;
3884 if (!write_ttf_file(ttf_name))
3886 skip("Failed to create ttf file for testing\n");
3887 return;
3890 trace("created %s\n", ttf_name);
3892 ret = is_truetype_font_installed("wine_test");
3893 ok(!ret, "font wine_test should not be enumerated\n");
3895 ret = GetTempPath(MAX_PATH, tmp_path);
3896 ok(ret, "GetTempPath() error %d\n", GetLastError());
3897 ret = GetTempFileName(tmp_path, "fot", 0, fot_name);
3898 ok(ret, "GetTempFileName() error %d\n", GetLastError());
3900 ret = GetFileAttributes(fot_name);
3901 ok(ret != INVALID_FILE_ATTRIBUTES, "file %s does not exist\n", fot_name);
3903 SetLastError(0xdeadbeef);
3904 ret = CreateScalableFontResource(0, fot_name, ttf_name, NULL);
3905 ok(!ret, "CreateScalableFontResource() should fail\n");
3906 ok(GetLastError() == ERROR_FILE_EXISTS, "not expected error %d\n", GetLastError());
3908 SetLastError(0xdeadbeef);
3909 ret = CreateScalableFontResource(0, fot_name, ttf_name, "");
3910 ok(!ret, "CreateScalableFontResource() should fail\n");
3911 ok(GetLastError() == ERROR_FILE_EXISTS, "not expected error %d\n", GetLastError());
3913 file_part = strrchr(ttf_name, '\\');
3914 SetLastError(0xdeadbeef);
3915 ret = CreateScalableFontResource(0, fot_name, file_part, tmp_path);
3916 ok(!ret, "CreateScalableFontResource() should fail\n");
3917 ok(GetLastError() == ERROR_FILE_EXISTS, "not expected error %d\n", GetLastError());
3919 SetLastError(0xdeadbeef);
3920 ret = CreateScalableFontResource(0, fot_name, "random file name", tmp_path);
3921 ok(!ret, "CreateScalableFontResource() should fail\n");
3922 todo_wine
3923 ok(GetLastError() == ERROR_INVALID_PARAMETER, "not expected error %d\n", GetLastError());
3925 SetLastError(0xdeadbeef);
3926 ret = CreateScalableFontResource(0, fot_name, NULL, ttf_name);
3927 ok(!ret, "CreateScalableFontResource() should fail\n");
3928 todo_wine
3929 ok(GetLastError() == ERROR_INVALID_PARAMETER, "not expected error %d\n", GetLastError());
3931 ret = DeleteFile(fot_name);
3932 ok(ret, "DeleteFile() error %d\n", GetLastError());
3934 ret = pRemoveFontResourceExA(fot_name, 0, 0);
3935 todo_wine
3936 ok(!ret, "RemoveFontResourceEx() should fail\n");
3938 /* FIXME: since CreateScalableFontResource is a stub further testing is impossible */
3939 if (ret) return;
3941 /* test public font resource */
3942 SetLastError(0xdeadbeef);
3943 ret = CreateScalableFontResource(0, fot_name, ttf_name, NULL);
3944 ok(ret, "CreateScalableFontResource() error %d\n", GetLastError());
3946 ret = is_truetype_font_installed("wine_test");
3947 ok(!ret, "font wine_test should not be enumerated\n");
3949 SetLastError(0xdeadbeef);
3950 ret = pAddFontResourceExA(fot_name, 0, 0);
3951 ok(ret, "AddFontResourceEx() error %d\n", GetLastError());
3953 ret = is_truetype_font_installed("wine_test");
3954 ok(ret, "font wine_test should be enumerated\n");
3956 ret = pRemoveFontResourceExA(fot_name, FR_PRIVATE, 0);
3957 ok(!ret, "RemoveFontResourceEx() with not matching flags should fail\n");
3959 SetLastError(0xdeadbeef);
3960 ret = pRemoveFontResourceExA(fot_name, 0, 0);
3961 todo_wine
3962 ok(ret, "RemoveFontResourceEx() error %d\n", GetLastError());
3964 ret = is_truetype_font_installed("wine_test");
3965 todo_wine
3966 ok(!ret, "font wine_test should not be enumerated\n");
3968 /* FIXME: since RemoveFontResource is a stub correct testing is impossible */
3969 if (ret)
3971 /* remove once RemoveFontResource is implemented */
3972 DeleteFile(fot_name);
3973 DeleteFile(ttf_name);
3974 return;
3977 ret = pRemoveFontResourceExA(fot_name, 0, 0);
3978 ok(!ret, "RemoveFontResourceEx() should fail\n");
3980 DeleteFile(fot_name);
3982 /* test hidden font resource */
3983 SetLastError(0xdeadbeef);
3984 ret = CreateScalableFontResource(1, fot_name, ttf_name, NULL);
3985 ok(ret, "CreateScalableFontResource() error %d\n", GetLastError());
3987 ret = is_truetype_font_installed("wine_test");
3988 ok(!ret, "font wine_test should not be enumerated\n");
3990 SetLastError(0xdeadbeef);
3991 ret = pAddFontResourceExA(fot_name, 0, 0);
3992 ok(ret, "AddFontResourceEx() error %d\n", GetLastError());
3994 ret = is_truetype_font_installed("wine_test");
3995 ok(!ret, "font wine_test should not be enumerated\n");
3997 /* XP allows removing a private font added with 0 flags */
3998 SetLastError(0xdeadbeef);
3999 ret = pRemoveFontResourceExA(fot_name, FR_PRIVATE, 0);
4000 ok(ret, "RemoveFontResourceEx() error %d\n", GetLastError());
4002 ret = is_truetype_font_installed("wine_test");
4003 ok(!ret, "font wine_test should not be enumerated\n");
4005 ret = pRemoveFontResourceExA(fot_name, 0, 0);
4006 ok(!ret, "RemoveFontResourceEx() should fail\n");
4008 DeleteFile(fot_name);
4009 DeleteFile(ttf_name);
4012 START_TEST(font)
4014 init();
4016 test_logfont();
4017 test_bitmap_font();
4018 test_outline_font();
4019 test_bitmap_font_metrics();
4020 test_GdiGetCharDimensions();
4021 test_GetCharABCWidths();
4022 test_text_extents();
4023 test_GetGlyphIndices();
4024 test_GetKerningPairs();
4025 test_GetOutlineTextMetrics();
4026 test_SetTextJustification();
4027 test_font_charset();
4028 test_GetFontUnicodeRanges();
4029 test_nonexistent_font();
4030 test_orientation();
4031 test_height_selection();
4032 test_AddFontMemResource();
4033 test_EnumFonts();
4035 /* On Windows Arial has a lot of default charset aliases such as Arial Cyr,
4036 * I'd like to avoid them in this test.
4038 test_EnumFontFamilies("Arial Black", ANSI_CHARSET);
4039 test_EnumFontFamilies("Symbol", SYMBOL_CHARSET);
4040 if (is_truetype_font_installed("Arial Black") &&
4041 (is_truetype_font_installed("Symbol") || is_truetype_font_installed("Wingdings")))
4043 test_EnumFontFamilies("", ANSI_CHARSET);
4044 test_EnumFontFamilies("", SYMBOL_CHARSET);
4045 test_EnumFontFamilies("", DEFAULT_CHARSET);
4047 else
4048 skip("Arial Black or Symbol/Wingdings is not installed\n");
4049 test_EnumFontFamiliesEx_default_charset();
4050 test_GetTextMetrics();
4051 test_GdiRealizationInfo();
4052 test_GetTextFace();
4053 test_GetGlyphOutline();
4054 test_GetTextMetrics2("Tahoma", -11);
4055 test_GetTextMetrics2("Tahoma", -55);
4056 test_GetTextMetrics2("Tahoma", -110);
4057 test_GetTextMetrics2("Arial", -11);
4058 test_GetTextMetrics2("Arial", -55);
4059 test_GetTextMetrics2("Arial", -110);
4060 test_CreateFontIndirect();
4061 test_CreateFontIndirectEx();
4062 test_oemcharset();
4063 test_fullname();
4065 /* CreateScalableFontResource should be last test until RemoveFontResource
4066 * is properly implemented.
4068 test_CreateScalableFontResource();