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
30 #include "wine/test.h"
32 static void check_font(const char* test
, const LOGFONTA
* lf
, HFONT hfont
)
40 ret
= GetObject(hfont
, sizeof(getobj_lf
), &getobj_lf
);
41 /* NT4 tries to be clever and only returns the minimum length */
42 while (lf
->lfFaceName
[minlen
] && minlen
< LF_FACESIZE
-1)
44 minlen
+= FIELD_OFFSET(LOGFONTA
, lfFaceName
) + 1;
45 ok(ret
== sizeof(LOGFONTA
) || ret
== minlen
, "%s: GetObject returned %d\n", test
, ret
);
46 ok(!memcmp(&lf
, &lf
, FIELD_OFFSET(LOGFONTA
, lfFaceName
)), "%s: fonts don't match\n", test
);
47 ok(!lstrcmpA(lf
->lfFaceName
, getobj_lf
.lfFaceName
),
48 "%s: font names don't match: %s != %s\n", test
, lf
->lfFaceName
, getobj_lf
.lfFaceName
);
51 static HFONT
create_font(const char* test
, const LOGFONTA
* lf
)
53 HFONT hfont
= CreateFontIndirectA(lf
);
54 ok(hfont
!= 0, "%s: CreateFontIndirect failed\n", test
);
56 check_font(test
, lf
, hfont
);
60 static void test_logfont(void)
65 memset(&lf
, 0, sizeof lf
);
67 lf
.lfCharSet
= ANSI_CHARSET
;
68 lf
.lfClipPrecision
= CLIP_DEFAULT_PRECIS
;
69 lf
.lfWeight
= FW_DONTCARE
;
72 lf
.lfQuality
= DEFAULT_QUALITY
;
74 lstrcpyA(lf
.lfFaceName
, "Arial");
75 hfont
= create_font("Arial", &lf
);
78 memset(&lf
, 'A', sizeof(lf
));
79 hfont
= CreateFontIndirectA(&lf
);
80 ok(hfont
!= 0, "CreateFontIndirectA with strange LOGFONT failed\n");
82 lf
.lfFaceName
[LF_FACESIZE
- 1] = 0;
83 check_font("AAA...", &lf
, hfont
);
87 static INT CALLBACK
font_enum_proc(const LOGFONT
*elf
, const TEXTMETRIC
*ntm
, DWORD type
, LPARAM lParam
)
89 if (type
& RASTER_FONTTYPE
)
91 LOGFONT
*lf
= (LOGFONT
*)lParam
;
93 return 0; /* stop enumeration */
96 return 1; /* continue enumeration */
99 static void test_font_metrics(HDC hdc
, HFONT hfont
, const char *test_str
,
100 INT test_str_len
, const TEXTMETRICA
*tm_orig
,
101 const SIZE
*size_orig
, INT width_orig
,
102 INT scale_x
, INT scale_y
)
112 old_hfont
= SelectObject(hdc
, hfont
);
114 GetTextMetricsA(hdc
, &tm
);
116 ok(tm
.tmHeight
== tm_orig
->tmHeight
* scale_y
, "%ld != %ld\n", tm
.tmHeight
, tm_orig
->tmHeight
* scale_y
);
117 ok(tm
.tmAscent
== tm_orig
->tmAscent
* scale_y
, "%ld != %ld\n", tm
.tmAscent
, tm_orig
->tmAscent
* scale_y
);
118 ok(tm
.tmDescent
== tm_orig
->tmDescent
* scale_y
, "%ld != %ld\n", tm
.tmDescent
, tm_orig
->tmDescent
* scale_y
);
119 ok(tm
.tmAveCharWidth
== tm_orig
->tmAveCharWidth
* scale_x
, "%ld != %ld\n", tm
.tmAveCharWidth
, tm_orig
->tmAveCharWidth
* scale_x
);
121 GetTextExtentPoint32A(hdc
, test_str
, test_str_len
, &size
);
123 ok(size
.cx
== size_orig
->cx
* scale_x
, "%ld != %ld\n", size
.cx
, size_orig
->cx
* scale_x
);
124 ok(size
.cy
== size_orig
->cy
* scale_y
, "%ld != %ld\n", size
.cy
, size_orig
->cy
* scale_y
);
126 GetCharWidthA(hdc
, 'A', 'A', &width
);
128 ok(width
== width_orig
* scale_x
, "%d != %d\n", width
, width_orig
* scale_x
);
130 SelectObject(hdc
, old_hfont
);
133 /* see whether GDI scales bitmap font metrics */
134 static void test_bitmap_font(void)
136 static const char test_str
[11] = "Test String";
139 HFONT hfont
, old_hfont
;
142 INT ret
, i
, width_orig
, height_orig
;
146 /* "System" has only 1 pixel size defined, otherwise the test breaks */
147 ret
= EnumFontFamiliesA(hdc
, "System", font_enum_proc
, (LPARAM
)&bitmap_lf
);
151 trace("no bitmap fonts were found, skipping the test\n");
155 trace("found bitmap font %s, height %ld\n", bitmap_lf
.lfFaceName
, bitmap_lf
.lfHeight
);
157 height_orig
= bitmap_lf
.lfHeight
;
158 hfont
= create_font("bitmap", &bitmap_lf
);
160 old_hfont
= SelectObject(hdc
, hfont
);
161 ok(GetTextMetricsA(hdc
, &tm_orig
), "GetTextMetricsA failed\n");
162 ok(GetTextExtentPoint32A(hdc
, test_str
, sizeof(test_str
), &size_orig
), "GetTextExtentPoint32A failed\n");
163 ok(GetCharWidthA(hdc
, 'A', 'A', &width_orig
), "GetCharWidthA failed\n");
164 SelectObject(hdc
, old_hfont
);
167 /* test fractional scaling */
168 for (i
= 1; i
< height_orig
; i
++)
170 hfont
= create_font("fractional", &bitmap_lf
);
171 test_font_metrics(hdc
, hfont
, test_str
, sizeof(test_str
), &tm_orig
, &size_orig
, width_orig
, 1, 1);
175 /* test integer scaling 3x2 */
176 bitmap_lf
.lfHeight
= height_orig
* 2;
177 bitmap_lf
.lfWidth
*= 3;
178 hfont
= create_font("3x2", &bitmap_lf
);
181 test_font_metrics(hdc
, hfont
, test_str
, sizeof(test_str
), &tm_orig
, &size_orig
, width_orig
, 3, 2);
185 /* test integer scaling 3x3 */
186 bitmap_lf
.lfHeight
= height_orig
* 3;
187 bitmap_lf
.lfWidth
= 0;
188 hfont
= create_font("3x3", &bitmap_lf
);
192 test_font_metrics(hdc
, hfont
, test_str
, sizeof(test_str
), &tm_orig
, &size_orig
, width_orig
, 3, 3);
199 static INT CALLBACK
find_font_proc(const LOGFONT
*elf
, const TEXTMETRIC
*ntm
, DWORD type
, LPARAM lParam
)
201 LOGFONT
*lf
= (LOGFONT
*)lParam
;
203 if (elf
->lfHeight
== lf
->lfHeight
&& !strcmp(elf
->lfFaceName
, lf
->lfFaceName
))
206 return 0; /* stop enumeration */
208 return 1; /* continue enumeration */
211 static void test_bitmap_font_metrics(void)
213 static const struct font_data
215 const char face_name
[LF_FACESIZE
];
216 int weight
, height
, ascent
, descent
, int_leading
, ext_leading
;
217 int ave_char_width
, max_char_width
;
220 { "MS Sans Serif", FW_NORMAL
, 13, 11, 2, 2, 0, 5, 11 },
221 { "MS Sans Serif", FW_NORMAL
, 16, 13, 3, 3, 0, 7, 14 },
222 { "MS Sans Serif", FW_NORMAL
, 20, 16, 4, 4, 0, 8, 16 },
223 { "MS Sans Serif", FW_NORMAL
, 24, 19, 5, 6, 0, 9, 20 },
224 { "MS Sans Serif", FW_NORMAL
, 29, 23, 6, 5, 0, 12, 25 },
225 { "MS Sans Serif", FW_NORMAL
, 37, 29, 8, 5, 0, 16, 32 },
226 { "MS Serif", FW_NORMAL
, 10, 8, 2, 2, 0, 5, 8 },
227 { "MS Serif", FW_NORMAL
, 11, 9, 2, 2, 0, 5, 9 },
228 { "MS Serif", FW_NORMAL
, 13, 11, 2, 2, 0, 5, 12 },
229 { "MS Serif", FW_NORMAL
, 16, 13, 3, 3, 0, 6, 16 },
230 { "MS Serif", FW_NORMAL
, 19, 15, 4, 3, 0, 8, 19 },
231 { "MS Serif", FW_NORMAL
, 21, 16, 5, 3, 0, 9, 23 },
232 { "MS Serif", FW_NORMAL
, 27, 21, 6, 3, 0, 12, 27 },
233 { "MS Serif", FW_NORMAL
, 35, 27, 8, 3, 0, 16, 34 },
234 { "Courier", FW_NORMAL
, 13, 11, 2, 0, 0, 8, 8 },
235 { "Courier", FW_NORMAL
, 16, 13, 3, 0, 0, 9, 9 },
236 { "Courier", FW_NORMAL
, 20, 16, 4, 0, 0, 12, 12 },
237 { "System", FW_BOLD
, 16, 13, 3, 3, 0, 7, 15 },
238 { "Small Fonts", FW_NORMAL
, 3, 2, 1, 0, 0, 1, 2 },
239 { "Small Fonts", FW_NORMAL
, 5, 4, 1, 1, 0, 3, 4 },
240 { "Small Fonts", FW_NORMAL
, 6, 5, 1, 1, 0, 3, 13 },
241 { "Small Fonts", FW_NORMAL
, 8, 7, 1, 1, 0, 4, 7 },
242 { "Small Fonts", FW_NORMAL
, 10, 8, 2, 2, 0, 4, 8 },
243 { "Small Fonts", FW_NORMAL
, 11, 9, 2, 2, 0, 5, 9 }
244 /* FIXME: add "Fixedsys", "Terminal" */
248 HFONT hfont
, old_hfont
;
252 hdc
= CreateCompatibleDC(0);
255 for (i
= 0; i
< sizeof(fd
)/sizeof(fd
[0]); i
++)
257 memset(&lf
, 0, sizeof(lf
));
259 lf
.lfHeight
= fd
[i
].height
;
260 strcpy(lf
.lfFaceName
, fd
[i
].face_name
);
261 ret
= EnumFontFamilies(hdc
, fd
[i
].face_name
, find_font_proc
, (LPARAM
)&lf
);
264 trace("font %s height %d not found\n", fd
[i
].face_name
, fd
[i
].height
);
268 trace("found font %s, height %ld\n", lf
.lfFaceName
, lf
.lfHeight
);
270 hfont
= create_font(lf
.lfFaceName
, &lf
);
271 old_hfont
= SelectObject(hdc
, hfont
);
272 ok(GetTextMetrics(hdc
, &tm
), "GetTextMetrics error %ld\n", GetLastError());
274 ok(tm
.tmWeight
== fd
[i
].weight
, "%s(%d): tm.tmWeight %ld != %d\n", fd
[i
].face_name
, fd
[i
].height
, tm
.tmWeight
, fd
[i
].weight
);
275 ok(tm
.tmHeight
== fd
[i
].height
, "%s(%d): tm.tmHeight %ld != %d\n", fd
[i
].face_name
, fd
[i
].height
, tm
.tmHeight
, fd
[i
].height
);
276 ok(tm
.tmAscent
== fd
[i
].ascent
, "%s(%d): tm.tmAscent %ld != %d\n", fd
[i
].face_name
, fd
[i
].height
, tm
.tmAscent
, fd
[i
].ascent
);
277 ok(tm
.tmDescent
== fd
[i
].descent
, "%s(%d): tm.tmDescent %ld != %d\n", fd
[i
].face_name
, fd
[i
].height
, tm
.tmDescent
, fd
[i
].descent
);
278 ok(tm
.tmInternalLeading
== fd
[i
].int_leading
, "%s(%d): tm.tmInternalLeading %ld != %d\n", fd
[i
].face_name
, fd
[i
].height
, tm
.tmInternalLeading
, fd
[i
].int_leading
);
279 ok(tm
.tmExternalLeading
== fd
[i
].ext_leading
, "%s(%d): tm.tmExternalLeading %ld != %d\n", fd
[i
].face_name
, fd
[i
].height
, tm
.tmExternalLeading
, fd
[i
].ext_leading
);
280 ok(tm
.tmAveCharWidth
== fd
[i
].ave_char_width
, "%s(%d): tm.tmAveCharWidth %ld != %d\n", fd
[i
].face_name
, fd
[i
].height
, tm
.tmAveCharWidth
, fd
[i
].ave_char_width
);
281 ok(tm
.tmMaxCharWidth
== fd
[i
].max_char_width
, "%s(%d): tm.tmMaxCharWidth %ld != %d\n", fd
[i
].face_name
, fd
[i
].height
, tm
.tmMaxCharWidth
, fd
[i
].max_char_width
);
283 SelectObject(hdc
, old_hfont
);
290 static void test_GdiGetCharDimensions(void)
296 LONG avgwidth
, height
;
297 static const char szAlphabet
[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
298 typedef LONG (WINAPI
*fnGdiGetCharDimensions
)(HDC hdc
, LPTEXTMETRICW lptm
, LONG
*height
);
299 fnGdiGetCharDimensions GdiGetCharDimensions
= (fnGdiGetCharDimensions
)GetProcAddress(LoadLibrary("gdi32"), "GdiGetCharDimensions");
300 if (!GdiGetCharDimensions
) return;
302 hdc
= CreateCompatibleDC(NULL
);
304 GetTextExtentPoint(hdc
, szAlphabet
, strlen(szAlphabet
), &size
);
305 avgwidth
= ((size
.cx
/ 26) + 1) / 2;
307 ret
= GdiGetCharDimensions(hdc
, &tm
, &height
);
308 ok(ret
== avgwidth
, "GdiGetCharDimensions should have returned width of %ld instead of %ld\n", avgwidth
, ret
);
309 ok(height
== tm
.tmHeight
, "GdiGetCharDimensions should have set height to %ld instead of %ld\n", tm
.tmHeight
, height
);
311 ret
= GdiGetCharDimensions(hdc
, &tm
, NULL
);
312 ok(ret
== avgwidth
, "GdiGetCharDimensions should have returned width of %ld instead of %ld\n", avgwidth
, ret
);
314 ret
= GdiGetCharDimensions(hdc
, NULL
, NULL
);
315 ok(ret
== avgwidth
, "GdiGetCharDimensions should have returned width of %ld instead of %ld\n", avgwidth
, ret
);
318 ret
= GdiGetCharDimensions(hdc
, NULL
, &height
);
319 ok(ret
== avgwidth
, "GdiGetCharDimensions should have returned width of %ld instead of %ld\n", avgwidth
, ret
);
320 ok(height
== size
.cy
, "GdiGetCharDimensions should have set height to %ld instead of %ld\n", size
.cy
, height
);
325 static void test_GetCharABCWidthsW(void)
329 typedef BOOL (WINAPI
*fnGetCharABCWidthsW
)(HDC hdc
, UINT first
, UINT last
, LPABC abc
);
330 fnGetCharABCWidthsW GetCharABCWidthsW
= (fnGetCharABCWidthsW
)GetProcAddress(LoadLibrary("gdi32"), "GetCharABCWidthsW");
331 if (!GetCharABCWidthsW
) return;
333 ret
= GetCharABCWidthsW(NULL
, 'a', 'a', abc
);
334 ok(!ret
, "GetCharABCWidthsW should have returned FALSE\n");
337 static void test_text_extents(void)
339 static const WCHAR wt
[] = {'O','n','e','\n','t','w','o',' ','3',0};
341 INT i
, len
, fit1
, fit2
;
349 memset(&lf
, 0, sizeof(lf
));
350 strcpy(lf
.lfFaceName
, "Arial");
353 hfont
= CreateFontIndirectA(&lf
);
355 hfont
= SelectObject(hdc
, hfont
);
356 GetTextMetricsA(hdc
, &tm
);
357 GetTextExtentPointA(hdc
, "o", 1, &sz
);
358 ok(sz
.cy
== tm
.tmHeight
, "cy %ld tmHeight %ld\n", sz
.cy
, tm
.tmHeight
);
361 extents
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof extents
[0]);
362 memset(extents
, 0, len
* sizeof extents
[0]);
363 extents
[0] = 1; /* So that the increasing sequence test will fail
364 if the extents array is untouched. */
365 GetTextExtentExPointW(hdc
, wt
, len
, 32767, &fit1
, extents
, &sz1
);
366 GetTextExtentPointW(hdc
, wt
, len
, &sz2
);
367 ok(sz1
.cx
== sz2
.cx
&& sz1
.cy
== sz2
.cy
,
368 "results from GetTextExtentExPointW and GetTextExtentPointW differ\n");
369 for (i
= 1; i
< len
; ++i
)
370 ok(extents
[i
-1] <= extents
[i
],
371 "GetTextExtentExPointW generated a non-increasing sequence of partial extents (at position %d)\n",
373 ok(extents
[len
-1] == sz1
.cx
, "GetTextExtentExPointW extents and size don't match\n");
374 ok(0 <= fit1
&& fit1
<= len
, "GetTextExtentExPointW generated illegal value %d for fit\n", fit1
);
375 ok(0 < fit1
, "GetTextExtentExPointW says we can't even fit one letter in 32767 logical units\n");
376 GetTextExtentExPointW(hdc
, wt
, len
, extents
[2], &fit2
, NULL
, &sz2
);
377 ok(sz1
.cx
== sz2
.cx
&& sz1
.cy
== sz2
.cy
, "GetTextExtentExPointW returned different sizes for the same string\n");
378 ok(fit2
== 3, "GetTextExtentExPointW extents isn't consistent with fit\n");
379 GetTextExtentExPointW(hdc
, wt
, len
, extents
[2]-1, &fit2
, NULL
, &sz2
);
380 ok(fit2
== 2, "GetTextExtentExPointW extents isn't consistent with fit\n");
381 GetTextExtentExPointW(hdc
, wt
, 2, 0, NULL
, extents
+ 2, &sz2
);
382 ok(extents
[0] == extents
[2] && extents
[1] == extents
[3],
383 "GetTextExtentExPointW with lpnFit == NULL returns incorrect results\n");
384 GetTextExtentExPointW(hdc
, wt
, 2, 0, NULL
, NULL
, &sz1
);
385 ok(sz1
.cx
== sz2
.cx
&& sz1
.cy
== sz2
.cy
,
386 "GetTextExtentExPointW with lpnFit and alpDx both NULL returns incorrect results\n");
387 HeapFree(GetProcessHeap(), 0, extents
);
389 SelectObject(hdc
, hfont
);
391 ReleaseDC(NULL
, hdc
);
394 static void test_GetGlyphIndices()
401 WCHAR testtext
[] = {'T','e','s','t',0xffff,0};
402 WORD glyphs
[(sizeof(testtext
)/2)-1];
405 typedef BOOL (WINAPI
*fnGetGlyphIndicesW
)(HDC hdc
, LPCWSTR lpstr
, INT count
, LPWORD pgi
, DWORD flags
);
406 fnGetGlyphIndicesW GetGlyphIndicesW
= (fnGetGlyphIndicesW
)GetProcAddress(LoadLibrary("gdi32"),
408 if (!GetGlyphIndicesW
) {
409 trace("GetGlyphIndices not available on platform\n");
413 memset(&lf
, 0, sizeof(lf
));
414 strcpy(lf
.lfFaceName
, "Symbol");
417 hfont
= CreateFontIndirectA(&lf
);
420 ok(GetTextMetrics(hdc
, &textm
), "GetTextMetric failed\n");
421 flags
|= GGI_MARK_NONEXISTING_GLYPHS
;
422 charcount
= GetGlyphIndicesW(hdc
, testtext
, (sizeof(testtext
)/2)-1, glyphs
, flags
);
423 ok(charcount
== 5, "GetGlyphIndices count of glyphs should = 5 not %ld\n", charcount
);
424 ok(glyphs
[4] == 0x001f, "GetGlyphIndices should have returned a nonexistent char not %04x\n", glyphs
[4]);
426 charcount
= GetGlyphIndicesW(hdc
, testtext
, (sizeof(testtext
)/2)-1, glyphs
, flags
);
427 ok(charcount
== 5, "GetGlyphIndices count of glyphs should = 5 not %ld\n", charcount
);
428 ok(glyphs
[4] == textm
.tmDefaultChar
, "GetGlyphIndices should have returned a %04x not %04x\n",
429 textm
.tmDefaultChar
, glyphs
[4]);
436 test_bitmap_font_metrics();
437 test_GdiGetCharDimensions();
438 test_GetCharABCWidthsW();
440 test_GetGlyphIndices();