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
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 LONG (WINAPI
*pGdiGetCharDimensions
)(HDC hdc
, LPTEXTMETRICW lptm
, LONG
*height
);
40 BOOL (WINAPI
*pGetCharABCWidthsI
)(HDC hdc
, UINT first
, UINT count
, LPWORD glyphs
, LPABC abc
);
41 BOOL (WINAPI
*pGetCharABCWidthsW
)(HDC hdc
, UINT first
, UINT last
, LPABC abc
);
42 DWORD (WINAPI
*pGetFontUnicodeRanges
)(HDC hdc
, LPGLYPHSET lpgs
);
43 DWORD (WINAPI
*pGetGlyphIndicesA
)(HDC hdc
, LPCSTR lpstr
, INT count
, LPWORD pgi
, DWORD flags
);
44 DWORD (WINAPI
*pGetGlyphIndicesW
)(HDC hdc
, LPCWSTR lpstr
, INT count
, LPWORD pgi
, DWORD flags
);
45 BOOL (WINAPI
*pGdiRealizationInfo
)(HDC hdc
, DWORD
*);
46 HFONT (WINAPI
*pCreateFontIndirectExA
)(const ENUMLOGFONTEXDV
*);
48 static HMODULE hgdi32
= 0;
50 static void init(void)
52 hgdi32
= GetModuleHandleA("gdi32.dll");
54 pGdiGetCharDimensions
= (void *)GetProcAddress(hgdi32
, "GdiGetCharDimensions");
55 pGetCharABCWidthsI
= (void *)GetProcAddress(hgdi32
, "GetCharABCWidthsI");
56 pGetCharABCWidthsW
= (void *)GetProcAddress(hgdi32
, "GetCharABCWidthsW");
57 pGetFontUnicodeRanges
= (void *)GetProcAddress(hgdi32
, "GetFontUnicodeRanges");
58 pGetGlyphIndicesA
= (void *)GetProcAddress(hgdi32
, "GetGlyphIndicesA");
59 pGetGlyphIndicesW
= (void *)GetProcAddress(hgdi32
, "GetGlyphIndicesW");
60 pGdiRealizationInfo
= (void *)GetProcAddress(hgdi32
, "GdiRealizationInfo");
61 pCreateFontIndirectExA
= (void *)GetProcAddress(hgdi32
, "CreateFontIndirectExA");
64 static INT CALLBACK
is_truetype_font_installed_proc(const LOGFONT
*elf
, const TEXTMETRIC
*ntm
, DWORD type
, LPARAM lParam
)
66 if (type
!= TRUETYPE_FONTTYPE
) return 1;
71 static BOOL
is_truetype_font_installed(const char *name
)
76 if (!EnumFontFamiliesA(hdc
, name
, is_truetype_font_installed_proc
, 0))
83 static INT CALLBACK
is_font_installed_proc(const LOGFONT
*elf
, const TEXTMETRIC
*ntm
, DWORD type
, LPARAM lParam
)
88 static BOOL
is_font_installed(const char *name
)
93 if(!EnumFontFamiliesA(hdc
, name
, is_font_installed_proc
, 0))
100 static void check_font(const char* test
, const LOGFONTA
* lf
, HFONT hfont
)
108 ret
= GetObject(hfont
, sizeof(getobj_lf
), &getobj_lf
);
109 /* NT4 tries to be clever and only returns the minimum length */
110 while (lf
->lfFaceName
[minlen
] && minlen
< LF_FACESIZE
-1)
112 minlen
+= FIELD_OFFSET(LOGFONTA
, lfFaceName
) + 1;
113 ok(ret
== sizeof(LOGFONTA
) || ret
== minlen
, "%s: GetObject returned %d\n", test
, ret
);
114 ok(lf
->lfHeight
== getobj_lf
.lfHeight
||
115 broken((SHORT
)lf
->lfHeight
== getobj_lf
.lfHeight
), /* win9x */
116 "lfHeight: expect %08x got %08x\n", lf
->lfHeight
, getobj_lf
.lfHeight
);
117 ok(lf
->lfWidth
== getobj_lf
.lfWidth
||
118 broken((SHORT
)lf
->lfWidth
== getobj_lf
.lfWidth
), /* win9x */
119 "lfWidth: expect %08x got %08x\n", lf
->lfWidth
, getobj_lf
.lfWidth
);
120 ok(lf
->lfEscapement
== getobj_lf
.lfEscapement
||
121 broken((SHORT
)lf
->lfEscapement
== getobj_lf
.lfEscapement
), /* win9x */
122 "lfEscapement: expect %08x got %08x\n", lf
->lfEscapement
, getobj_lf
.lfEscapement
);
123 ok(lf
->lfOrientation
== getobj_lf
.lfOrientation
||
124 broken((SHORT
)lf
->lfOrientation
== getobj_lf
.lfOrientation
), /* win9x */
125 "lfOrientation: expect %08x got %08x\n", lf
->lfOrientation
, getobj_lf
.lfOrientation
);
126 ok(lf
->lfWeight
== getobj_lf
.lfWeight
||
127 broken((SHORT
)lf
->lfWeight
== getobj_lf
.lfWeight
), /* win9x */
128 "lfWeight: expect %08x got %08x\n", lf
->lfWeight
, getobj_lf
.lfWeight
);
129 ok(lf
->lfItalic
== getobj_lf
.lfItalic
, "lfItalic: expect %02x got %02x\n", lf
->lfItalic
, getobj_lf
.lfItalic
);
130 ok(lf
->lfUnderline
== getobj_lf
.lfUnderline
, "lfUnderline: expect %02x got %02x\n", lf
->lfUnderline
, getobj_lf
.lfUnderline
);
131 ok(lf
->lfStrikeOut
== getobj_lf
.lfStrikeOut
, "lfStrikeOut: expect %02x got %02x\n", lf
->lfStrikeOut
, getobj_lf
.lfStrikeOut
);
132 ok(lf
->lfCharSet
== getobj_lf
.lfCharSet
, "lfCharSet: expect %02x got %02x\n", lf
->lfCharSet
, getobj_lf
.lfCharSet
);
133 ok(lf
->lfOutPrecision
== getobj_lf
.lfOutPrecision
, "lfOutPrecision: expect %02x got %02x\n", lf
->lfOutPrecision
, getobj_lf
.lfOutPrecision
);
134 ok(lf
->lfClipPrecision
== getobj_lf
.lfClipPrecision
, "lfClipPrecision: expect %02x got %02x\n", lf
->lfClipPrecision
, getobj_lf
.lfClipPrecision
);
135 ok(lf
->lfQuality
== getobj_lf
.lfQuality
, "lfQuality: expect %02x got %02x\n", lf
->lfQuality
, getobj_lf
.lfQuality
);
136 ok(lf
->lfPitchAndFamily
== getobj_lf
.lfPitchAndFamily
, "lfPitchAndFamily: expect %02x got %02x\n", lf
->lfPitchAndFamily
, getobj_lf
.lfPitchAndFamily
);
137 ok(!lstrcmpA(lf
->lfFaceName
, getobj_lf
.lfFaceName
) ||
138 broken(!memcmp(lf
->lfFaceName
, getobj_lf
.lfFaceName
, LF_FACESIZE
-1)), /* win9x doesn't ensure '\0' termination */
139 "%s: font names don't match: %s != %s\n", test
, lf
->lfFaceName
, getobj_lf
.lfFaceName
);
142 static HFONT
create_font(const char* test
, const LOGFONTA
* lf
)
144 HFONT hfont
= CreateFontIndirectA(lf
);
145 ok(hfont
!= 0, "%s: CreateFontIndirect failed\n", test
);
147 check_font(test
, lf
, hfont
);
151 static void test_logfont(void)
156 memset(&lf
, 0, sizeof lf
);
158 lf
.lfCharSet
= ANSI_CHARSET
;
159 lf
.lfClipPrecision
= CLIP_DEFAULT_PRECIS
;
160 lf
.lfWeight
= FW_DONTCARE
;
163 lf
.lfQuality
= DEFAULT_QUALITY
;
165 lstrcpyA(lf
.lfFaceName
, "Arial");
166 hfont
= create_font("Arial", &lf
);
169 memset(&lf
, 'A', sizeof(lf
));
170 hfont
= CreateFontIndirectA(&lf
);
171 ok(hfont
!= 0, "CreateFontIndirectA with strange LOGFONT failed\n");
173 lf
.lfFaceName
[LF_FACESIZE
- 1] = 0;
174 check_font("AAA...", &lf
, hfont
);
178 static INT CALLBACK
font_enum_proc(const LOGFONT
*elf
, const TEXTMETRIC
*ntm
, DWORD type
, LPARAM lParam
)
180 if (type
& RASTER_FONTTYPE
)
182 LOGFONT
*lf
= (LOGFONT
*)lParam
;
184 return 0; /* stop enumeration */
187 return 1; /* continue enumeration */
190 static void compare_tm(const TEXTMETRICA
*tm
, const TEXTMETRICA
*otm
)
192 ok(tm
->tmHeight
== otm
->tmHeight
, "tmHeight %d != %d\n", tm
->tmHeight
, otm
->tmHeight
);
193 ok(tm
->tmAscent
== otm
->tmAscent
, "tmAscent %d != %d\n", tm
->tmAscent
, otm
->tmAscent
);
194 ok(tm
->tmDescent
== otm
->tmDescent
, "tmDescent %d != %d\n", tm
->tmDescent
, otm
->tmDescent
);
195 ok(tm
->tmInternalLeading
== otm
->tmInternalLeading
, "tmInternalLeading %d != %d\n", tm
->tmInternalLeading
, otm
->tmInternalLeading
);
196 ok(tm
->tmExternalLeading
== otm
->tmExternalLeading
, "tmExternalLeading %d != %d\n", tm
->tmExternalLeading
, otm
->tmExternalLeading
);
197 ok(tm
->tmAveCharWidth
== otm
->tmAveCharWidth
, "tmAveCharWidth %d != %d\n", tm
->tmAveCharWidth
, otm
->tmAveCharWidth
);
198 ok(tm
->tmMaxCharWidth
== otm
->tmMaxCharWidth
, "tmMaxCharWidth %d != %d\n", tm
->tmMaxCharWidth
, otm
->tmMaxCharWidth
);
199 ok(tm
->tmWeight
== otm
->tmWeight
, "tmWeight %d != %d\n", tm
->tmWeight
, otm
->tmWeight
);
200 ok(tm
->tmOverhang
== otm
->tmOverhang
, "tmOverhang %d != %d\n", tm
->tmOverhang
, otm
->tmOverhang
);
201 ok(tm
->tmDigitizedAspectX
== otm
->tmDigitizedAspectX
, "tmDigitizedAspectX %d != %d\n", tm
->tmDigitizedAspectX
, otm
->tmDigitizedAspectX
);
202 ok(tm
->tmDigitizedAspectY
== otm
->tmDigitizedAspectY
, "tmDigitizedAspectY %d != %d\n", tm
->tmDigitizedAspectY
, otm
->tmDigitizedAspectY
);
203 ok(tm
->tmFirstChar
== otm
->tmFirstChar
, "tmFirstChar %d != %d\n", tm
->tmFirstChar
, otm
->tmFirstChar
);
204 ok(tm
->tmLastChar
== otm
->tmLastChar
, "tmLastChar %d != %d\n", tm
->tmLastChar
, otm
->tmLastChar
);
205 ok(tm
->tmDefaultChar
== otm
->tmDefaultChar
, "tmDefaultChar %d != %d\n", tm
->tmDefaultChar
, otm
->tmDefaultChar
);
206 ok(tm
->tmBreakChar
== otm
->tmBreakChar
, "tmBreakChar %d != %d\n", tm
->tmBreakChar
, otm
->tmBreakChar
);
207 ok(tm
->tmItalic
== otm
->tmItalic
, "tmItalic %d != %d\n", tm
->tmItalic
, otm
->tmItalic
);
208 ok(tm
->tmUnderlined
== otm
->tmUnderlined
, "tmUnderlined %d != %d\n", tm
->tmUnderlined
, otm
->tmUnderlined
);
209 ok(tm
->tmStruckOut
== otm
->tmStruckOut
, "tmStruckOut %d != %d\n", tm
->tmStruckOut
, otm
->tmStruckOut
);
210 ok(tm
->tmPitchAndFamily
== otm
->tmPitchAndFamily
, "tmPitchAndFamily %d != %d\n", tm
->tmPitchAndFamily
, otm
->tmPitchAndFamily
);
211 ok(tm
->tmCharSet
== otm
->tmCharSet
, "tmCharSet %d != %d\n", tm
->tmCharSet
, otm
->tmCharSet
);
214 static void test_font_metrics(HDC hdc
, HFONT hfont
, LONG lfHeight
,
215 LONG lfWidth
, const char *test_str
,
216 INT test_str_len
, const TEXTMETRICA
*tm_orig
,
217 const SIZE
*size_orig
, INT width_of_A_orig
,
218 INT scale_x
, INT scale_y
)
221 OUTLINETEXTMETRIC otm
;
224 INT width_of_A
, cx
, cy
;
230 ok(GetCurrentObject(hdc
, OBJ_FONT
) == hfont
, "hfont should be selected\n");
232 GetObjectA(hfont
, sizeof(lf
), &lf
);
234 if (GetOutlineTextMetricsA(hdc
, 0, NULL
))
236 otm
.otmSize
= sizeof(otm
) / 2;
237 ret
= GetOutlineTextMetricsA(hdc
, otm
.otmSize
, &otm
);
238 ok(ret
== sizeof(otm
)/2 /* XP */ ||
239 ret
== 1 /* Win9x */, "expected sizeof(otm)/2, got %u\n", ret
);
241 memset(&otm
, 0x1, sizeof(otm
));
242 otm
.otmSize
= sizeof(otm
);
243 ret
= GetOutlineTextMetricsA(hdc
, otm
.otmSize
, &otm
);
244 ok(ret
== sizeof(otm
) /* XP */ ||
245 ret
== 1 /* Win9x */, "expected sizeof(otm), got %u\n", ret
);
247 memset(&tm
, 0x2, sizeof(tm
));
248 ret
= GetTextMetricsA(hdc
, &tm
);
249 ok(ret
, "GetTextMetricsA failed\n");
250 /* the structure size is aligned */
251 if (memcmp(&tm
, &otm
.otmTextMetrics
, FIELD_OFFSET(TEXTMETRICA
, tmCharSet
) + 1))
253 ok(0, "tm != otm\n");
254 compare_tm(&tm
, &otm
.otmTextMetrics
);
257 tm
= otm
.otmTextMetrics
;
258 if (0) /* these metrics are scaled too, but with rounding errors */
260 ok(otm
.otmAscent
== tm
.tmAscent
, "ascent %d != %d\n", otm
.otmAscent
, tm
.tmAscent
);
261 ok(otm
.otmDescent
== -tm
.tmDescent
, "descent %d != %d\n", otm
.otmDescent
, -tm
.tmDescent
);
263 ok(otm
.otmMacAscent
== tm
.tmAscent
, "ascent %d != %d\n", otm
.otmMacAscent
, tm
.tmAscent
);
264 ok(otm
.otmDescent
< 0, "otm.otmDescent should be < 0\n");
265 ok(otm
.otmMacDescent
< 0, "otm.otmMacDescent should be < 0\n");
266 ok(tm
.tmDescent
> 0, "tm.tmDescent should be > 0\n");
267 ok(otm
.otmMacDescent
== -tm
.tmDescent
, "descent %d != %d\n", otm
.otmMacDescent
, -tm
.tmDescent
);
268 ok(otm
.otmEMSquare
== 2048, "expected 2048, got %d\n", otm
.otmEMSquare
);
272 ret
= GetTextMetricsA(hdc
, &tm
);
273 ok(ret
, "GetTextMetricsA failed\n");
276 cx
= tm
.tmAveCharWidth
/ tm_orig
->tmAveCharWidth
;
277 cy
= tm
.tmHeight
/ tm_orig
->tmHeight
;
278 ok(cx
== scale_x
&& cy
== scale_y
, "height %d: expected scale_x %d, scale_y %d, got cx %d, cy %d\n",
279 lfHeight
, scale_x
, scale_y
, cx
, cy
);
280 ok(tm
.tmHeight
== tm_orig
->tmHeight
* scale_y
, "height %d != %d\n", tm
.tmHeight
, tm_orig
->tmHeight
* scale_y
);
281 ok(tm
.tmAscent
== tm_orig
->tmAscent
* scale_y
, "ascent %d != %d\n", tm
.tmAscent
, tm_orig
->tmAscent
* scale_y
);
282 ok(tm
.tmDescent
== tm_orig
->tmDescent
* scale_y
, "descent %d != %d\n", tm
.tmDescent
, tm_orig
->tmDescent
* scale_y
);
283 ok(near_match(tm
.tmAveCharWidth
, tm_orig
->tmAveCharWidth
* scale_x
), "ave width %d != %d\n", tm
.tmAveCharWidth
, tm_orig
->tmAveCharWidth
* scale_x
);
284 ok(near_match(tm
.tmMaxCharWidth
, tm_orig
->tmMaxCharWidth
* scale_x
), "max width %d != %d\n", tm
.tmMaxCharWidth
, tm_orig
->tmMaxCharWidth
* scale_x
);
286 ok(lf
.lfHeight
== lfHeight
, "lfHeight %d != %d\n", lf
.lfHeight
, lfHeight
);
290 ok(lf
.lfWidth
== tm
.tmAveCharWidth
, "lfWidth %d != tm %d\n", lf
.lfWidth
, tm
.tmAveCharWidth
);
293 ok(lf
.lfWidth
== lfWidth
, "lfWidth %d != %d\n", lf
.lfWidth
, lfWidth
);
295 GetTextExtentPoint32A(hdc
, test_str
, test_str_len
, &size
);
297 ok(near_match(size
.cx
, size_orig
->cx
* scale_x
), "cx %d != %d\n", size
.cx
, size_orig
->cx
* scale_x
);
298 ok(size
.cy
== size_orig
->cy
* scale_y
, "cy %d != %d\n", size
.cy
, size_orig
->cy
* scale_y
);
300 GetCharWidthA(hdc
, 'A', 'A', &width_of_A
);
302 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
);
305 /* Test how GDI scales bitmap font metrics */
306 static void test_bitmap_font(void)
308 static const char test_str
[11] = "Test String";
311 HFONT hfont
, old_hfont
;
314 INT ret
, i
, width_orig
, height_orig
, scale
, lfWidth
;
316 if(!winetest_interactive
)
318 skip("reactos bug 5401: Skipping bitmap font tests!\n");
324 /* "System" has only 1 pixel size defined, otherwise the test breaks */
325 ret
= EnumFontFamiliesA(hdc
, "System", font_enum_proc
, (LPARAM
)&bitmap_lf
);
329 trace("no bitmap fonts were found, skipping the test\n");
333 trace("found bitmap font %s, height %d\n", bitmap_lf
.lfFaceName
, bitmap_lf
.lfHeight
);
335 height_orig
= bitmap_lf
.lfHeight
;
336 lfWidth
= bitmap_lf
.lfWidth
;
338 hfont
= create_font("bitmap", &bitmap_lf
);
339 old_hfont
= SelectObject(hdc
, hfont
);
340 ok(GetTextMetricsA(hdc
, &tm_orig
), "GetTextMetricsA failed\n");
341 ok(GetTextExtentPoint32A(hdc
, test_str
, sizeof(test_str
), &size_orig
), "GetTextExtentPoint32A failed\n");
342 ok(GetCharWidthA(hdc
, 'A', 'A', &width_orig
), "GetCharWidthA failed\n");
343 SelectObject(hdc
, old_hfont
);
346 bitmap_lf
.lfHeight
= 0;
347 bitmap_lf
.lfWidth
= 4;
348 hfont
= create_font("bitmap", &bitmap_lf
);
349 old_hfont
= SelectObject(hdc
, hfont
);
350 test_font_metrics(hdc
, hfont
, 0, 4, test_str
, sizeof(test_str
), &tm_orig
, &size_orig
, width_orig
, 1, 1);
351 SelectObject(hdc
, old_hfont
);
354 bitmap_lf
.lfHeight
= height_orig
;
355 bitmap_lf
.lfWidth
= lfWidth
;
357 /* test fractional scaling */
358 for (i
= 1; i
<= height_orig
* 6; i
++)
362 bitmap_lf
.lfHeight
= i
;
363 hfont
= create_font("fractional", &bitmap_lf
);
364 scale
= (i
+ height_orig
- 1) / height_orig
;
365 nearest_height
= scale
* height_orig
;
366 /* Only jump to the next height if the difference <= 25% original height */
367 if (scale
> 2 && nearest_height
- i
> height_orig
/ 4) scale
--;
368 /* The jump between unscaled and doubled is delayed by 1 in winnt+ but not in win9x,
369 so we'll not test this particular height. */
370 else if(scale
== 2 && nearest_height
- i
== (height_orig
/ 4)) continue;
371 else if(scale
== 2 && nearest_height
- i
> (height_orig
/ 4 - 1)) scale
--;
372 old_hfont
= SelectObject(hdc
, hfont
);
373 test_font_metrics(hdc
, hfont
, bitmap_lf
.lfHeight
, 0, test_str
, sizeof(test_str
), &tm_orig
, &size_orig
, width_orig
, 1, scale
);
374 SelectObject(hdc
, old_hfont
);
378 /* test integer scaling 3x2 */
379 bitmap_lf
.lfHeight
= height_orig
* 2;
380 bitmap_lf
.lfWidth
*= 3;
381 hfont
= create_font("3x2", &bitmap_lf
);
382 old_hfont
= SelectObject(hdc
, hfont
);
383 test_font_metrics(hdc
, hfont
, bitmap_lf
.lfHeight
, 0, test_str
, sizeof(test_str
), &tm_orig
, &size_orig
, width_orig
, 3, 2);
384 SelectObject(hdc
, old_hfont
);
387 /* test integer scaling 3x3 */
388 bitmap_lf
.lfHeight
= height_orig
* 3;
389 bitmap_lf
.lfWidth
= 0;
390 hfont
= create_font("3x3", &bitmap_lf
);
391 old_hfont
= SelectObject(hdc
, hfont
);
392 test_font_metrics(hdc
, hfont
, bitmap_lf
.lfHeight
, 0, test_str
, sizeof(test_str
), &tm_orig
, &size_orig
, width_orig
, 3, 3);
393 SelectObject(hdc
, old_hfont
);
399 /* Test how GDI scales outline font metrics */
400 static void test_outline_font(void)
402 static const char test_str
[11] = "Test String";
405 HFONT hfont
, old_hfont
, old_hfont_2
;
406 OUTLINETEXTMETRICA otm
;
408 INT width_orig
, height_orig
, lfWidth
;
411 MAT2 mat
= { {0,1}, {0,0}, {0,0}, {0,1} };
412 MAT2 mat2
= { {0x8000,0}, {0,0}, {0,0}, {0x8000,0} };
416 if(!winetest_interactive
)
418 skip("reactos bug 5401: Skipping outline font tests!\n");
422 if (!is_truetype_font_installed("Arial"))
424 skip("Arial is not installed\n");
428 hdc
= CreateCompatibleDC(0);
430 memset(&lf
, 0, sizeof(lf
));
431 strcpy(lf
.lfFaceName
, "Arial");
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
);
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
);
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
);
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
);
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
);
486 /* test integer scaling 1x1 */
487 lf
.lfHeight
= height_orig
;
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
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
);
551 if (!SetGraphicsMode(hdc
, GM_ADVANCED
))
553 SelectObject(hdc
, old_hfont
);
556 skip("GM_ADVANCED is not supported on this platform\n");
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;
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;
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;
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;
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;
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;
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
);
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
))
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
;
679 { "MS Sans Serif", FW_NORMAL
, 13, 11, 2, 2, 0, 5, 11, 96, FS_LATIN1
| FS_LATIN2
| FS_CYRILLIC
},
680 { "MS Sans Serif", FW_NORMAL
, 16, 13, 3, 3, 0, 7, 14, 96, FS_LATIN1
| FS_LATIN2
| FS_CYRILLIC
},
681 { "MS Sans Serif", FW_NORMAL
, 20, 16, 4, 4, 0, 8, 16, 96, FS_LATIN1
| FS_CYRILLIC
},
682 { "MS Sans Serif", FW_NORMAL
, 20, 16, 4, 4, 0, 8, 18, 96, FS_LATIN2
},
683 { "MS Sans Serif", FW_NORMAL
, 24, 19, 5, 6, 0, 9, 19, 96, FS_LATIN1
},
684 { "MS Sans Serif", FW_NORMAL
, 24, 19, 5, 6, 0, 9, 24, 96, FS_LATIN2
},
685 { "MS Sans Serif", FW_NORMAL
, 24, 19, 5, 6, 0, 9, 20, 96, FS_CYRILLIC
},
686 { "MS Sans Serif", FW_NORMAL
, 29, 23, 6, 5, 0, 12, 24, 96, FS_LATIN1
},
687 { "MS Sans Serif", FW_NORMAL
, 29, 23, 6, 6, 0, 12, 24, 96, FS_LATIN2
},
688 { "MS Sans Serif", FW_NORMAL
, 29, 23, 6, 5, 0, 12, 25, 96, FS_CYRILLIC
},
689 { "MS Sans Serif", FW_NORMAL
, 37, 29, 8, 5, 0, 16, 32, 96, FS_LATIN1
| FS_LATIN2
| FS_CYRILLIC
},
691 { "MS Sans Serif", FW_NORMAL
, 16, 13, 3, 3, 0, 7, 14, 120, FS_LATIN1
| FS_LATIN2
| FS_CYRILLIC
},
692 { "MS Sans Serif", FW_NORMAL
, 20, 16, 4, 4, 0, 8, 18, 120, FS_LATIN1
| FS_LATIN2
},
693 { "MS Sans Serif", FW_NORMAL
, 20, 16, 4, 4, 0, 8, 17, 120, FS_CYRILLIC
},
694 { "MS Sans Serif", FW_NORMAL
, 25, 20, 5, 5, 0, 10, 21, 120, FS_LATIN1
| FS_LATIN2
| FS_CYRILLIC
},
695 { "MS Sans Serif", FW_NORMAL
, 29, 23, 6, 6, 0, 12, 24, 120, FS_LATIN1
| FS_LATIN2
},
696 { "MS Sans Serif", FW_NORMAL
, 29, 23, 6, 5, 0, 12, 24, 120, FS_CYRILLIC
},
697 { "MS Sans Serif", FW_NORMAL
, 36, 29, 7, 6, 0, 15, 30, 120, FS_LATIN1
| FS_LATIN2
| FS_CYRILLIC
},
698 { "MS Sans Serif", FW_NORMAL
, 46, 37, 9, 6, 0, 20, 40, 120, FS_LATIN1
| FS_LATIN2
| FS_CYRILLIC
},
700 { "MS Serif", FW_NORMAL
, 10, 8, 2, 2, 0, 4, 8, 96, FS_LATIN1
| FS_LATIN2
},
701 { "MS Serif", FW_NORMAL
, 10, 8, 2, 2, 0, 5, 8, 96, FS_CYRILLIC
},
702 { "MS Serif", FW_NORMAL
, 11, 9, 2, 2, 0, 5, 9, 96, FS_LATIN1
| FS_LATIN2
| FS_CYRILLIC
},
703 { "MS Serif", FW_NORMAL
, 13, 11, 2, 2, 0, 5, 11, 96, FS_LATIN1
},
704 { "MS Serif", FW_NORMAL
, 13, 11, 2, 2, 0, 5, 12, 96, FS_LATIN2
| FS_CYRILLIC
},
705 { "MS Serif", FW_NORMAL
, 16, 13, 3, 3, 0, 6, 14, 96, FS_LATIN1
| FS_LATIN2
},
706 { "MS Serif", FW_NORMAL
, 16, 13, 3, 3, 0, 6, 16, 96, FS_CYRILLIC
},
707 { "MS Serif", FW_NORMAL
, 19, 15, 4, 3, 0, 8, 18, 96, FS_LATIN1
| FS_LATIN2
},
708 { "MS Serif", FW_NORMAL
, 19, 15, 4, 3, 0, 8, 19, 96, FS_CYRILLIC
},
709 { "MS Serif", FW_NORMAL
, 21, 16, 5, 3, 0, 9, 17, 96, FS_LATIN1
},
710 { "MS Serif", FW_NORMAL
, 21, 16, 5, 3, 0, 9, 22, 96, FS_LATIN2
},
711 { "MS Serif", FW_NORMAL
, 21, 16, 5, 3, 0, 9, 23, 96, FS_CYRILLIC
},
712 { "MS Serif", FW_NORMAL
, 27, 21, 6, 3, 0, 12, 23, 96, FS_LATIN1
},
713 { "MS Serif", FW_NORMAL
, 27, 21, 6, 3, 0, 12, 26, 96, FS_LATIN2
},
714 { "MS Serif", FW_NORMAL
, 27, 21, 6, 3, 0, 12, 27, 96, FS_CYRILLIC
},
715 { "MS Serif", FW_NORMAL
, 35, 27, 8, 3, 0, 16, 33, 96, FS_LATIN1
| FS_LATIN2
},
716 { "MS Serif", FW_NORMAL
, 35, 27, 8, 3, 0, 16, 34, 96, FS_CYRILLIC
},
718 { "MS Serif", FW_NORMAL
, 16, 13, 3, 3, 0, 6, 14, 120, FS_LATIN1
| FS_CYRILLIC
},
719 { "MS Serif", FW_NORMAL
, 16, 13, 3, 3, 0, 6, 13, 120, FS_LATIN2
},
720 { "MS Serif", FW_NORMAL
, 20, 16, 4, 4, 0, 8, 18, 120, FS_LATIN1
| FS_CYRILLIC
},
721 { "MS Serif", FW_NORMAL
, 20, 16, 4, 4, 0, 8, 15, 120, FS_LATIN2
},
722 { "MS Serif", FW_NORMAL
, 23, 18, 5, 3, 0, 10, 21, 120, FS_LATIN1
| FS_CYRILLIC
},
723 { "MS Serif", FW_NORMAL
, 23, 18, 5, 3, 0, 10, 19, 120, FS_LATIN2
},
724 { "MS Serif", FW_NORMAL
, 27, 21, 6, 4, 0, 12, 23, 120, FS_LATIN1
| FS_LATIN2
},
725 { "MS Serif", FW_MEDIUM
, 27, 22, 5, 2, 0, 12, 30, 120, FS_CYRILLIC
},
726 { "MS Serif", FW_NORMAL
, 33, 26, 7, 3, 0, 14, 30, 120, FS_LATIN1
| FS_LATIN2
},
727 { "MS Serif", FW_MEDIUM
, 32, 25, 7, 2, 0, 14, 32, 120, FS_CYRILLIC
},
728 { "MS Serif", FW_NORMAL
, 43, 34, 9, 3, 0, 19, 39, 120, FS_LATIN1
| FS_LATIN2
| FS_CYRILLIC
},
730 { "Courier", FW_NORMAL
, 13, 11, 2, 0, 0, 8, 8, 96, FS_LATIN1
| FS_LATIN2
| FS_CYRILLIC
},
731 { "Courier", FW_NORMAL
, 16, 13, 3, 0, 0, 9, 9, 96, FS_LATIN1
| FS_LATIN2
| FS_CYRILLIC
},
732 { "Courier", FW_NORMAL
, 20, 16, 4, 0, 0, 12, 12, 96, FS_LATIN1
| FS_LATIN2
| FS_CYRILLIC
},
734 { "Courier", FW_NORMAL
, 16, 13, 3, 0, 0, 9, 9, 120, FS_LATIN1
| FS_LATIN2
| FS_CYRILLIC
},
735 { "Courier", FW_NORMAL
, 20, 16, 4, 0, 0, 12, 12, 120, FS_LATIN1
| FS_LATIN2
| FS_CYRILLIC
},
736 { "Courier", FW_NORMAL
, 25, 20, 5, 0, 0, 15, 15, 120, FS_LATIN1
| FS_LATIN2
| FS_CYRILLIC
},
738 { "System", FW_BOLD
, 16, 13, 3, 3, 0, 7, 14, 96, FS_LATIN1
},
739 { "System", FW_BOLD
, 16, 13, 3, 3, 0, 7, 15, 96, FS_LATIN2
| FS_CYRILLIC
},
741 * TODO: the system for CP932 should be NORMAL, not BOLD. However that would
742 * require a new system.sfd for that font
744 { "System", FW_BOLD
, 18, 16, 2, 0, 2, 8, 16, 96, FS_JISJAPAN
},
746 { "System", FW_BOLD
, 20, 16, 4, 4, 0, 9, 14, 120, FS_LATIN1
},
747 { "System", FW_BOLD
, 20, 16, 4, 4, 0, 9, 17, 120, FS_LATIN2
| FS_CYRILLIC
},
749 { "Small Fonts", FW_NORMAL
, 3, 2, 1, 0, 0, 1, 2, 96, FS_LATIN1
},
750 { "Small Fonts", FW_NORMAL
, 3, 2, 1, 0, 0, 1, 8, 96, FS_LATIN2
| FS_CYRILLIC
},
751 { "Small Fonts", FW_NORMAL
, 3, 2, 1, 0, 0, 2, 4, 96, FS_JISJAPAN
},
752 { "Small Fonts", FW_NORMAL
, 5, 4, 1, 1, 0, 3, 4, 96, FS_LATIN1
},
753 { "Small Fonts", FW_NORMAL
, 5, 4, 1, 1, 0, 2, 8, 96, FS_LATIN2
| FS_CYRILLIC
},
754 { "Small Fonts", FW_NORMAL
, 5, 4, 1, 0, 0, 3, 6, 96, FS_JISJAPAN
},
755 { "Small Fonts", FW_NORMAL
, 6, 5, 1, 1, 0, 3, 13, 96, FS_LATIN1
},
756 { "Small Fonts", FW_NORMAL
, 6, 5, 1, 1, 0, 3, 8, 96, FS_LATIN2
| FS_CYRILLIC
},
757 { "Small Fonts", FW_NORMAL
, 6, 5, 1, 0, 0, 4, 8, 96, FS_JISJAPAN
},
758 { "Small Fonts", FW_NORMAL
, 8, 7, 1, 1, 0, 4, 7, 96, FS_LATIN1
},
759 { "Small Fonts", FW_NORMAL
, 8, 7, 1, 1, 0, 4, 8, 96, FS_LATIN2
| FS_CYRILLIC
},
760 { "Small Fonts", FW_NORMAL
, 8, 7, 1, 0, 0, 5, 10, 96, FS_JISJAPAN
},
761 { "Small Fonts", FW_NORMAL
, 10, 8, 2, 2, 0, 4, 8, 96, FS_LATIN1
| FS_LATIN2
},
762 { "Small Fonts", FW_NORMAL
, 10, 8, 2, 2, 0, 5, 8, 96, FS_CYRILLIC
},
763 { "Small Fonts", FW_NORMAL
, 10, 8, 2, 0, 0, 6, 12, 96, FS_JISJAPAN
},
764 { "Small Fonts", FW_NORMAL
, 11, 9, 2, 2, 0, 5, 9, 96, FS_LATIN1
| FS_LATIN2
| FS_CYRILLIC
},
765 { "Small Fonts", FW_NORMAL
, 11, 9, 2, 0, 0, 7, 14, 96, FS_JISJAPAN
},
767 { "Small Fonts", FW_NORMAL
, 3, 2, 1, 0, 0, 1, 2, 120, FS_LATIN1
| FS_JISJAPAN
},
768 { "Small Fonts", FW_NORMAL
, 3, 2, 1, 0, 0, 1, 8, 120, FS_LATIN2
| FS_CYRILLIC
},
769 { "Small Fonts", FW_NORMAL
, 6, 5, 1, 1, 0, 3, 5, 120, FS_LATIN1
| FS_JISJAPAN
},
770 { "Small Fonts", FW_NORMAL
, 6, 5, 1, 1, 0, 3, 8, 120, FS_LATIN2
| FS_CYRILLIC
},
771 { "Small Fonts", FW_NORMAL
, 8, 7, 1, 1, 0, 4, 7, 120, FS_LATIN1
| FS_JISJAPAN
},
772 { "Small Fonts", FW_NORMAL
, 8, 7, 1, 1, 0, 4, 8, 120, FS_LATIN2
| FS_CYRILLIC
},
773 { "Small Fonts", FW_NORMAL
, 10, 8, 2, 2, 0, 5, 9, 120, FS_LATIN1
| FS_LATIN2
| FS_JISJAPAN
},
774 { "Small Fonts", FW_NORMAL
, 10, 8, 2, 2, 0, 5, 8, 120, FS_CYRILLIC
},
775 { "Small Fonts", FW_NORMAL
, 12, 10, 2, 2, 0, 5, 10, 120, FS_LATIN1
| FS_LATIN2
| FS_JISJAPAN
},
776 { "Small Fonts", FW_NORMAL
, 12, 10, 2, 2, 0, 6, 10, 120, FS_CYRILLIC
},
777 { "Small Fonts", FW_NORMAL
, 13, 11, 2, 2, 0, 6, 12, 120, FS_LATIN1
| FS_LATIN2
| FS_JISJAPAN
},
778 { "Small Fonts", FW_NORMAL
, 13, 11, 2, 2, 0, 6, 11, 120, FS_CYRILLIC
},
780 { "Fixedsys", FW_NORMAL
, 15, 12, 3, 3, 0, 8, 8, 96, FS_LATIN1
| FS_LATIN2
},
781 { "Fixedsys", FW_NORMAL
, 16, 12, 4, 3, 0, 8, 8, 96, FS_CYRILLIC
},
782 { "FixedSys", FW_NORMAL
, 18, 16, 2, 0, 0, 8, 16, 96, FS_JISJAPAN
},
784 /* The 120dpi version still has its dpi marked as 96 */
785 { "Fixedsys", FW_NORMAL
, 20, 16, 4, 2, 0, 10, 10, 96, FS_LATIN1
| FS_LATIN2
| FS_CYRILLIC
}
787 /* FIXME: add "Terminal" */
791 HFONT hfont
, old_hfont
;
795 hdc
= CreateCompatibleDC(0);
798 for (i
= 0; i
< sizeof(fd
)/sizeof(fd
[0]); i
++)
802 memset(&lf
, 0, sizeof(lf
));
804 lf
.lfHeight
= fd
[i
].height
;
805 strcpy(lf
.lfFaceName
, fd
[i
].face_name
);
807 for(bit
= 0; bit
< 32; bit
++)
814 if((fd
[i
].ansi_bitfield
& fs
[0]) == 0) continue;
815 if(!TranslateCharsetInfo( fs
, &csi
, TCI_SRCFONTSIG
)) continue;
817 lf
.lfCharSet
= csi
.ciCharset
;
818 ret
= EnumFontFamiliesEx(hdc
, &lf
, find_font_proc
, (LPARAM
)&lf
, 0);
821 hfont
= create_font(lf
.lfFaceName
, &lf
);
822 old_hfont
= SelectObject(hdc
, hfont
);
823 ok(GetTextMetrics(hdc
, &tm
), "GetTextMetrics error %d\n", GetLastError());
824 if(fd
[i
].dpi
== tm
.tmDigitizedAspectX
)
826 trace("found font %s, height %d charset %x dpi %d\n", lf
.lfFaceName
, lf
.lfHeight
, lf
.lfCharSet
, fd
[i
].dpi
);
827 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
);
828 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
);
829 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
);
830 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
);
831 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
);
832 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
);
833 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
);
835 /* Don't run the max char width test on System/ANSI_CHARSET. We have extra characters in our font
836 that make the max width bigger */
837 if(strcmp(lf
.lfFaceName
, "System") || lf
.lfCharSet
!= ANSI_CHARSET
)
838 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
);
840 SelectObject(hdc
, old_hfont
);
848 static void test_GdiGetCharDimensions(void)
854 LONG avgwidth
, height
;
855 static const char szAlphabet
[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
857 if (!pGdiGetCharDimensions
)
859 win_skip("GdiGetCharDimensions not available on this platform\n");
863 hdc
= CreateCompatibleDC(NULL
);
865 GetTextExtentPoint(hdc
, szAlphabet
, strlen(szAlphabet
), &size
);
866 avgwidth
= ((size
.cx
/ 26) + 1) / 2;
868 ret
= pGdiGetCharDimensions(hdc
, &tm
, &height
);
869 ok(ret
== avgwidth
, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth
, ret
);
870 ok(height
== tm
.tmHeight
, "GdiGetCharDimensions should have set height to %d instead of %d\n", tm
.tmHeight
, height
);
872 ret
= pGdiGetCharDimensions(hdc
, &tm
, NULL
);
873 ok(ret
== avgwidth
, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth
, ret
);
875 ret
= pGdiGetCharDimensions(hdc
, NULL
, NULL
);
876 ok(ret
== avgwidth
, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth
, ret
);
879 ret
= pGdiGetCharDimensions(hdc
, NULL
, &height
);
880 ok(ret
== avgwidth
, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth
, ret
);
881 ok(height
== size
.cy
, "GdiGetCharDimensions should have set height to %d instead of %d\n", size
.cy
, height
);
886 static void test_GetCharABCWidths(void)
888 static const WCHAR str
[] = {'a',0};
897 if (!pGetCharABCWidthsW
|| !pGetCharABCWidthsI
)
899 win_skip("GetCharABCWidthsW/I not available on this platform\n");
903 memset(&lf
, 0, sizeof(lf
));
904 strcpy(lf
.lfFaceName
, "System");
907 hfont
= CreateFontIndirectA(&lf
);
909 hfont
= SelectObject(hdc
, hfont
);
911 nb
= pGetGlyphIndicesW(hdc
, str
, 1, glyphs
, 0);
912 ok(nb
== 1, "GetGlyphIndicesW should have returned 1\n");
914 ret
= pGetCharABCWidthsI(NULL
, 0, 1, glyphs
, abc
);
915 ok(!ret
, "GetCharABCWidthsI should have failed\n");
917 ret
= pGetCharABCWidthsI(hdc
, 0, 1, glyphs
, NULL
);
918 ok(!ret
, "GetCharABCWidthsI should have failed\n");
920 ret
= pGetCharABCWidthsI(hdc
, 0, 1, glyphs
, abc
);
921 ok(ret
, "GetCharABCWidthsI should have succeeded\n");
923 ret
= pGetCharABCWidthsW(NULL
, 'a', 'a', abc
);
924 ok(!ret
, "GetCharABCWidthsW should have failed\n");
926 ret
= pGetCharABCWidthsW(hdc
, 'a', 'a', NULL
);
927 ok(!ret
, "GetCharABCWidthsW should have failed\n");
929 ret
= pGetCharABCWidthsW(hdc
, 'a', 'a', abc
);
930 ok(!ret
, "GetCharABCWidthsW should have failed\n");
932 hfont
= SelectObject(hdc
, hfont
);
934 ReleaseDC(NULL
, hdc
);
937 static void test_text_extents(void)
939 static const WCHAR wt
[] = {'O','n','e','\n','t','w','o',' ','3',0};
941 INT i
, len
, fit1
, fit2
;
949 memset(&lf
, 0, sizeof(lf
));
950 strcpy(lf
.lfFaceName
, "Arial");
953 hfont
= CreateFontIndirectA(&lf
);
955 hfont
= SelectObject(hdc
, hfont
);
956 GetTextMetricsA(hdc
, &tm
);
957 GetTextExtentPointA(hdc
, "o", 1, &sz
);
958 ok(sz
.cy
== tm
.tmHeight
, "cy %d tmHeight %d\n", sz
.cy
, tm
.tmHeight
);
960 SetLastError(0xdeadbeef);
961 GetTextExtentExPointW(hdc
, wt
, 1, 1, &fit1
, &fit2
, &sz1
);
962 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED
)
964 win_skip("Skipping remainder of text extents test on a Win9x platform\n");
965 hfont
= SelectObject(hdc
, hfont
);
972 extents
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, len
* sizeof extents
[0]);
973 extents
[0] = 1; /* So that the increasing sequence test will fail
974 if the extents array is untouched. */
975 GetTextExtentExPointW(hdc
, wt
, len
, 32767, &fit1
, extents
, &sz1
);
976 GetTextExtentPointW(hdc
, wt
, len
, &sz2
);
978 "cy from GetTextExtentExPointW (%d) and GetTextExtentPointW (%d) differ\n", sz1
.cy
, sz2
.cy
);
979 /* Because of the '\n' in the string GetTextExtentExPoint and
980 GetTextExtentPoint return different widths under Win2k, but
981 under WinXP they return the same width. So we don't test that
984 for (i
= 1; i
< len
; ++i
)
985 ok(extents
[i
-1] <= extents
[i
],
986 "GetTextExtentExPointW generated a non-increasing sequence of partial extents (at position %d)\n",
988 ok(extents
[len
-1] == sz1
.cx
, "GetTextExtentExPointW extents and size don't match\n");
989 ok(0 <= fit1
&& fit1
<= len
, "GetTextExtentExPointW generated illegal value %d for fit\n", fit1
);
990 ok(0 < fit1
, "GetTextExtentExPointW says we can't even fit one letter in 32767 logical units\n");
991 GetTextExtentExPointW(hdc
, wt
, len
, extents
[2], &fit2
, NULL
, &sz2
);
992 ok(sz1
.cx
== sz2
.cx
&& sz1
.cy
== sz2
.cy
, "GetTextExtentExPointW returned different sizes for the same string\n");
993 ok(fit2
== 3, "GetTextExtentExPointW extents isn't consistent with fit\n");
994 GetTextExtentExPointW(hdc
, wt
, len
, extents
[2]-1, &fit2
, NULL
, &sz2
);
995 ok(fit2
== 2, "GetTextExtentExPointW extents isn't consistent with fit\n");
996 GetTextExtentExPointW(hdc
, wt
, 2, 0, NULL
, extents
+ 2, &sz2
);
997 ok(extents
[0] == extents
[2] && extents
[1] == extents
[3],
998 "GetTextExtentExPointW with lpnFit == NULL returns incorrect results\n");
999 GetTextExtentExPointW(hdc
, wt
, 2, 0, NULL
, NULL
, &sz1
);
1000 ok(sz1
.cx
== sz2
.cx
&& sz1
.cy
== sz2
.cy
,
1001 "GetTextExtentExPointW with lpnFit and alpDx both NULL returns incorrect results\n");
1002 HeapFree(GetProcessHeap(), 0, extents
);
1004 hfont
= SelectObject(hdc
, hfont
);
1005 DeleteObject(hfont
);
1006 ReleaseDC(NULL
, hdc
);
1009 static void test_GetGlyphIndices(void)
1016 WCHAR testtext
[] = {'T','e','s','t',0xffff,0};
1017 WORD glyphs
[(sizeof(testtext
)/2)-1];
1021 if (!pGetGlyphIndicesW
) {
1022 win_skip("GetGlyphIndicesW not available on platform\n");
1028 ok(GetTextMetrics(hdc
, &textm
), "GetTextMetric failed\n");
1029 flags
|= GGI_MARK_NONEXISTING_GLYPHS
;
1030 charcount
= pGetGlyphIndicesW(hdc
, testtext
, (sizeof(testtext
)/2)-1, glyphs
, flags
);
1031 ok(charcount
== 5, "GetGlyphIndicesW count of glyphs should = 5 not %d\n", charcount
);
1032 ok((glyphs
[4] == 0x001f || glyphs
[4] == 0xffff /* Vista */), "GetGlyphIndicesW should have returned a nonexistent char not %04x\n", glyphs
[4]);
1034 charcount
= pGetGlyphIndicesW(hdc
, testtext
, (sizeof(testtext
)/2)-1, glyphs
, flags
);
1035 ok(charcount
== 5, "GetGlyphIndicesW count of glyphs should = 5 not %d\n", charcount
);
1036 ok(glyphs
[4] == textm
.tmDefaultChar
, "GetGlyphIndicesW should have returned a %04x not %04x\n",
1037 textm
.tmDefaultChar
, glyphs
[4]);
1039 if(!is_font_installed("Tahoma"))
1041 skip("Tahoma is not installed so skipping this test\n");
1044 memset(&lf
, 0, sizeof(lf
));
1045 strcpy(lf
.lfFaceName
, "Tahoma");
1048 hfont
= CreateFontIndirectA(&lf
);
1049 hOldFont
= SelectObject(hdc
, hfont
);
1050 ok(GetTextMetrics(hdc
, &textm
), "GetTextMetric failed\n");
1051 flags
|= GGI_MARK_NONEXISTING_GLYPHS
;
1052 charcount
= pGetGlyphIndicesW(hdc
, testtext
, (sizeof(testtext
)/2)-1, glyphs
, flags
);
1053 ok(charcount
== 5, "GetGlyphIndicesW count of glyphs should = 5 not %d\n", charcount
);
1054 ok(glyphs
[4] == 0xffff, "GetGlyphIndicesW should have returned 0xffff char not %04x\n", glyphs
[4]);
1056 testtext
[0] = textm
.tmDefaultChar
;
1057 charcount
= pGetGlyphIndicesW(hdc
, testtext
, (sizeof(testtext
)/2)-1, glyphs
, flags
);
1058 ok(charcount
== 5, "GetGlyphIndicesW count of glyphs should = 5 not %d\n", charcount
);
1059 ok(glyphs
[0] == 0, "GetGlyphIndicesW for tmDefaultChar should be 0 not %04x\n", glyphs
[0]);
1060 ok(glyphs
[4] == 0, "GetGlyphIndicesW should have returned 0 not %04x\n", glyphs
[4]);
1061 DeleteObject(SelectObject(hdc
, hOldFont
));
1064 static void test_GetKerningPairs(void)
1066 static const struct kerning_data
1068 const char face_name
[LF_FACESIZE
];
1070 /* some interesting fields from OUTLINETEXTMETRIC */
1071 LONG tmHeight
, tmAscent
, tmDescent
;
1076 UINT otmsCapEmHeight
;
1081 UINT otmusMinimumPPEM
;
1082 /* small subset of kerning pairs to test */
1083 DWORD total_kern_pairs
;
1084 const KERNINGPAIR kern_pair
[26];
1087 {"Arial", 12, 12, 9, 3,
1088 2048, 7, -2, 1, 5, 2, 8, -2, 0, 9,
1091 {' ','A',-1},{' ','T',0},{' ','Y',0},{'1','1',-1},
1092 {'A',' ',-1},{'A','T',-1},{'A','V',-1},{'A','W',0},
1093 {'A','Y',-1},{'A','v',0},{'A','w',0},{'A','y',0},
1094 {'F',',',-1},{'F','.',-1},{'F','A',-1},{'L',' ',0},
1095 {'L','T',-1},{'L','V',-1},{'L','W',-1},{'L','Y',-1},
1096 {915,912,+1},{915,913,-1},{910,912,+1},{910,913,-1},
1097 {933,970,+1},{933,972,-1}
1100 {"Arial", -34, 39, 32, 7,
1101 2048, 25, -7, 5, 17, 9, 31, -7, 1, 9,
1104 {' ','A',-2},{' ','T',-1},{' ','Y',-1},{'1','1',-3},
1105 {'A',' ',-2},{'A','T',-3},{'A','V',-3},{'A','W',-1},
1106 {'A','Y',-3},{'A','v',-1},{'A','w',-1},{'A','y',-1},
1107 {'F',',',-4},{'F','.',-4},{'F','A',-2},{'L',' ',-1},
1108 {'L','T',-3},{'L','V',-3},{'L','W',-3},{'L','Y',-3},
1109 {915,912,+3},{915,913,-3},{910,912,+3},{910,913,-3},
1110 {933,970,+2},{933,972,-3}
1113 { "Arial", 120, 120, 97, 23,
1114 2048, 79, -23, 16, 54, 27, 98, -23, 4, 9,
1117 {' ','A',-6},{' ','T',-2},{' ','Y',-2},{'1','1',-8},
1118 {'A',' ',-6},{'A','T',-8},{'A','V',-8},{'A','W',-4},
1119 {'A','Y',-8},{'A','v',-2},{'A','w',-2},{'A','y',-2},
1120 {'F',',',-12},{'F','.',-12},{'F','A',-6},{'L',' ',-4},
1121 {'L','T',-8},{'L','V',-8},{'L','W',-8},{'L','Y',-8},
1122 {915,912,+9},{915,913,-10},{910,912,+9},{910,913,-8},
1123 {933,970,+6},{933,972,-10}
1126 #if 0 /* this set fails due to +1/-1 errors (rounding bug?), needs investigation. */
1127 { "Arial", 1024 /* usually 1/2 of EM Square */, 1024, 830, 194,
1128 2048, 668, -193, 137, 459, 229, 830, -194, 30, 9,
1131 {' ','A',-51},{' ','T',-17},{' ','Y',-17},{'1','1',-68},
1132 {'A',' ',-51},{'A','T',-68},{'A','V',-68},{'A','W',-34},
1133 {'A','Y',-68},{'A','v',-17},{'A','w',-17},{'A','y',-17},
1134 {'F',',',-102},{'F','.',-102},{'F','A',-51},{'L',' ',-34},
1135 {'L','T',-68},{'L','V',-68},{'L','W',-68},{'L','Y',-68},
1136 {915,912,+73},{915,913,-84},{910,912,+76},{910,913,-68},
1137 {933,970,+54},{933,972,-83}
1143 HFONT hfont
, hfont_old
;
1144 KERNINGPAIR
*kern_pair
;
1146 DWORD total_kern_pairs
, ret
, i
, n
, matches
;
1150 /* GetKerningPairsA maps unicode set of kerning pairs to current code page
1151 * which may render this test unusable, so we're trying to avoid that.
1153 SetLastError(0xdeadbeef);
1154 GetKerningPairsW(hdc
, 0, NULL
);
1155 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED
)
1157 win_skip("Skipping the GetKerningPairs test on a Win9x platform\n");
1162 for (i
= 0; i
< sizeof(kd
)/sizeof(kd
[0]); i
++)
1164 OUTLINETEXTMETRICW otm
;
1166 if (!is_font_installed(kd
[i
].face_name
))
1168 trace("%s is not installed so skipping this test\n", kd
[i
].face_name
);
1172 trace("testing font %s, height %d\n", kd
[i
].face_name
, kd
[i
].height
);
1174 memset(&lf
, 0, sizeof(lf
));
1175 strcpy(lf
.lfFaceName
, kd
[i
].face_name
);
1176 lf
.lfHeight
= kd
[i
].height
;
1177 hfont
= CreateFontIndirect(&lf
);
1180 hfont_old
= SelectObject(hdc
, hfont
);
1182 SetLastError(0xdeadbeef);
1183 otm
.otmSize
= sizeof(otm
); /* just in case for Win9x compatibility */
1184 ok(GetOutlineTextMetricsW(hdc
, sizeof(otm
), &otm
) == sizeof(otm
), "GetOutlineTextMetricsW error %d\n", GetLastError());
1186 ok(match_off_by_1(kd
[i
].tmHeight
, otm
.otmTextMetrics
.tmHeight
), "expected %d, got %d\n",
1187 kd
[i
].tmHeight
, otm
.otmTextMetrics
.tmHeight
);
1188 ok(match_off_by_1(kd
[i
].tmAscent
, otm
.otmTextMetrics
.tmAscent
), "expected %d, got %d\n",
1189 kd
[i
].tmAscent
, otm
.otmTextMetrics
.tmAscent
);
1190 ok(kd
[i
].tmDescent
== otm
.otmTextMetrics
.tmDescent
, "expected %d, got %d\n",
1191 kd
[i
].tmDescent
, otm
.otmTextMetrics
.tmDescent
);
1193 ok(kd
[i
].otmEMSquare
== otm
.otmEMSquare
, "expected %u, got %u\n",
1194 kd
[i
].otmEMSquare
, otm
.otmEMSquare
);
1195 ok(kd
[i
].otmAscent
== otm
.otmAscent
, "expected %d, got %d\n",
1196 kd
[i
].otmAscent
, otm
.otmAscent
);
1197 ok(kd
[i
].otmDescent
== otm
.otmDescent
, "expected %d, got %d\n",
1198 kd
[i
].otmDescent
, otm
.otmDescent
);
1199 ok(kd
[i
].otmLineGap
== otm
.otmLineGap
, "expected %u, got %u\n",
1200 kd
[i
].otmLineGap
, otm
.otmLineGap
);
1201 ok(near_match(kd
[i
].otmMacDescent
, otm
.otmMacDescent
), "expected %d, got %d\n",
1202 kd
[i
].otmMacDescent
, otm
.otmMacDescent
);
1203 ok(near_match(kd
[i
].otmMacAscent
, otm
.otmMacAscent
), "expected %d, got %d\n",
1204 kd
[i
].otmMacAscent
, otm
.otmMacAscent
);
1206 ok(kd
[i
].otmsCapEmHeight
== otm
.otmsCapEmHeight
, "expected %u, got %u\n",
1207 kd
[i
].otmsCapEmHeight
, otm
.otmsCapEmHeight
);
1208 ok(kd
[i
].otmsXHeight
== otm
.otmsXHeight
, "expected %u, got %u\n",
1209 kd
[i
].otmsXHeight
, otm
.otmsXHeight
);
1210 /* FIXME: this one sometimes succeeds due to expected 0, enable it when removing todo */
1211 if (0) ok(kd
[i
].otmMacLineGap
== otm
.otmMacLineGap
, "expected %u, got %u\n",
1212 kd
[i
].otmMacLineGap
, otm
.otmMacLineGap
);
1213 ok(kd
[i
].otmusMinimumPPEM
== otm
.otmusMinimumPPEM
, "expected %u, got %u\n",
1214 kd
[i
].otmusMinimumPPEM
, otm
.otmusMinimumPPEM
);
1217 total_kern_pairs
= GetKerningPairsW(hdc
, 0, NULL
);
1218 trace("total_kern_pairs %u\n", total_kern_pairs
);
1219 kern_pair
= HeapAlloc(GetProcessHeap(), 0, total_kern_pairs
* sizeof(*kern_pair
));
1221 #if 0 /* Win98 (GetKerningPairsA) and XP behave differently here, the test passes on XP */
1222 SetLastError(0xdeadbeef);
1223 ret
= GetKerningPairsW(hdc
, 0, kern_pair
);
1224 ok(GetLastError() == ERROR_INVALID_PARAMETER
,
1225 "got error %ld, expected ERROR_INVALID_PARAMETER\n", GetLastError());
1226 ok(ret
== 0, "got %lu, expected 0\n", ret
);
1229 ret
= GetKerningPairsW(hdc
, 100, NULL
);
1230 ok(ret
== total_kern_pairs
, "got %u, expected %u\n", ret
, total_kern_pairs
);
1232 ret
= GetKerningPairsW(hdc
, total_kern_pairs
/2, kern_pair
);
1233 ok(ret
== total_kern_pairs
/2, "got %u, expected %u\n", ret
, total_kern_pairs
/2);
1235 ret
= GetKerningPairsW(hdc
, total_kern_pairs
, kern_pair
);
1236 ok(ret
== total_kern_pairs
, "got %u, expected %u\n", ret
, total_kern_pairs
);
1240 for (n
= 0; n
< ret
; n
++)
1244 if (kern_pair
[n
].wFirst
< 127 && kern_pair
[n
].wSecond
< 127)
1245 trace("{'%c','%c',%d},\n",
1246 kern_pair
[n
].wFirst
, kern_pair
[n
].wSecond
, kern_pair
[n
].iKernAmount
);
1248 for (j
= 0; j
< kd
[i
].total_kern_pairs
; j
++)
1250 if (kern_pair
[n
].wFirst
== kd
[i
].kern_pair
[j
].wFirst
&&
1251 kern_pair
[n
].wSecond
== kd
[i
].kern_pair
[j
].wSecond
)
1253 ok(kern_pair
[n
].iKernAmount
== kd
[i
].kern_pair
[j
].iKernAmount
,
1254 "pair %d:%d got %d, expected %d\n",
1255 kern_pair
[n
].wFirst
, kern_pair
[n
].wSecond
,
1256 kern_pair
[n
].iKernAmount
, kd
[i
].kern_pair
[j
].iKernAmount
);
1262 ok(matches
== kd
[i
].total_kern_pairs
, "got matches %u, expected %u\n",
1263 matches
, kd
[i
].total_kern_pairs
);
1265 HeapFree(GetProcessHeap(), 0, kern_pair
);
1267 SelectObject(hdc
, hfont_old
);
1268 DeleteObject(hfont
);
1274 static void test_height_selection(void)
1276 static const struct font_data
1278 const char face_name
[LF_FACESIZE
];
1279 int requested_height
;
1280 int weight
, height
, ascent
, descent
, int_leading
, ext_leading
, dpi
;
1283 {"Tahoma", -12, FW_NORMAL
, 14, 12, 2, 2, 0, 96 },
1284 {"Tahoma", -24, FW_NORMAL
, 29, 24, 5, 5, 0, 96 },
1285 {"Tahoma", -48, FW_NORMAL
, 58, 48, 10, 10, 0, 96 },
1286 {"Tahoma", -96, FW_NORMAL
, 116, 96, 20, 20, 0, 96 },
1287 {"Tahoma", -192, FW_NORMAL
, 232, 192, 40, 40, 0, 96 },
1288 {"Tahoma", 12, FW_NORMAL
, 12, 10, 2, 2, 0, 96 },
1289 {"Tahoma", 24, FW_NORMAL
, 24, 20, 4, 4, 0, 96 },
1290 {"Tahoma", 48, FW_NORMAL
, 48, 40, 8, 8, 0, 96 },
1291 {"Tahoma", 96, FW_NORMAL
, 96, 80, 16, 17, 0, 96 },
1292 {"Tahoma", 192, FW_NORMAL
, 192, 159, 33, 33, 0, 96 }
1296 HFONT hfont
, old_hfont
;
1300 hdc
= CreateCompatibleDC(0);
1303 for (i
= 0; i
< sizeof(fd
)/sizeof(fd
[0]); i
++)
1305 if (!is_truetype_font_installed(fd
[i
].face_name
))
1307 skip("%s is not installed\n", fd
[i
].face_name
);
1311 memset(&lf
, 0, sizeof(lf
));
1312 lf
.lfHeight
= fd
[i
].requested_height
;
1313 lf
.lfWeight
= fd
[i
].weight
;
1314 strcpy(lf
.lfFaceName
, fd
[i
].face_name
);
1316 hfont
= CreateFontIndirect(&lf
);
1319 old_hfont
= SelectObject(hdc
, hfont
);
1320 ret
= GetTextMetrics(hdc
, &tm
);
1321 ok(ret
, "GetTextMetrics error %d\n", GetLastError());
1322 if(fd
[i
].dpi
== tm
.tmDigitizedAspectX
)
1324 trace("found font %s, height %d charset %x dpi %d\n", lf
.lfFaceName
, lf
.lfHeight
, lf
.lfCharSet
, fd
[i
].dpi
);
1325 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
);
1326 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
);
1327 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
);
1328 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
);
1329 #if 0 /* FIXME: calculation of tmInternalLeading in Wine doesn't match what Windows does */
1330 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
);
1332 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
);
1335 SelectObject(hdc
, old_hfont
);
1336 DeleteObject(hfont
);
1342 static void test_GetOutlineTextMetrics(void)
1344 OUTLINETEXTMETRIC
*otm
;
1346 HFONT hfont
, hfont_old
;
1348 DWORD ret
, otm_size
;
1351 if (!is_font_installed("Arial"))
1353 skip("Arial is not installed\n");
1359 memset(&lf
, 0, sizeof(lf
));
1360 strcpy(lf
.lfFaceName
, "Arial");
1362 lf
.lfWeight
= FW_NORMAL
;
1363 lf
.lfPitchAndFamily
= DEFAULT_PITCH
;
1364 lf
.lfQuality
= PROOF_QUALITY
;
1365 hfont
= CreateFontIndirect(&lf
);
1368 hfont_old
= SelectObject(hdc
, hfont
);
1369 otm_size
= GetOutlineTextMetrics(hdc
, 0, NULL
);
1370 trace("otm buffer size %u (0x%x)\n", otm_size
, otm_size
);
1372 otm
= HeapAlloc(GetProcessHeap(), 0, otm_size
);
1374 memset(otm
, 0xAA, otm_size
);
1375 SetLastError(0xdeadbeef);
1376 otm
->otmSize
= sizeof(*otm
); /* just in case for Win9x compatibility */
1377 ret
= GetOutlineTextMetrics(hdc
, otm
->otmSize
, otm
);
1378 ok(ret
== 1 /* Win9x */ ||
1379 ret
== otm
->otmSize
/* XP*/,
1380 "expected %u, got %u, error %d\n", otm
->otmSize
, ret
, GetLastError());
1381 if (ret
!= 1) /* Win9x doesn't care about pointing beyond of the buffer */
1383 ok(otm
->otmpFamilyName
== NULL
, "expected NULL got %p\n", otm
->otmpFamilyName
);
1384 ok(otm
->otmpFaceName
== NULL
, "expected NULL got %p\n", otm
->otmpFaceName
);
1385 ok(otm
->otmpStyleName
== NULL
, "expected NULL got %p\n", otm
->otmpStyleName
);
1386 ok(otm
->otmpFullName
== NULL
, "expected NULL got %p\n", otm
->otmpFullName
);
1389 memset(otm
, 0xAA, otm_size
);
1390 SetLastError(0xdeadbeef);
1391 otm
->otmSize
= otm_size
; /* just in case for Win9x compatibility */
1392 ret
= GetOutlineTextMetrics(hdc
, otm
->otmSize
, otm
);
1393 ok(ret
== 1 /* Win9x */ ||
1394 ret
== otm
->otmSize
/* XP*/,
1395 "expected %u, got %u, error %d\n", otm
->otmSize
, ret
, GetLastError());
1396 if (ret
!= 1) /* Win9x doesn't care about pointing beyond of the buffer */
1398 ok(otm
->otmpFamilyName
!= NULL
, "expected not NULL got %p\n", otm
->otmpFamilyName
);
1399 ok(otm
->otmpFaceName
!= NULL
, "expected not NULL got %p\n", otm
->otmpFaceName
);
1400 ok(otm
->otmpStyleName
!= NULL
, "expected not NULL got %p\n", otm
->otmpStyleName
);
1401 ok(otm
->otmpFullName
!= NULL
, "expected not NULL got %p\n", otm
->otmpFullName
);
1404 /* ask about truncated data */
1405 memset(otm
, 0xAA, otm_size
);
1406 memset(&unset_ptr
, 0xAA, sizeof(unset_ptr
));
1407 SetLastError(0xdeadbeef);
1408 otm
->otmSize
= sizeof(*otm
) - sizeof(LPSTR
); /* just in case for Win9x compatibility */
1409 ret
= GetOutlineTextMetrics(hdc
, otm
->otmSize
, otm
);
1410 ok(ret
== 1 /* Win9x */ ||
1411 ret
== otm
->otmSize
/* XP*/,
1412 "expected %u, got %u, error %d\n", otm
->otmSize
, ret
, GetLastError());
1413 if (ret
!= 1) /* Win9x doesn't care about pointing beyond of the buffer */
1415 ok(otm
->otmpFamilyName
== NULL
, "expected NULL got %p\n", otm
->otmpFamilyName
);
1416 ok(otm
->otmpFaceName
== NULL
, "expected NULL got %p\n", otm
->otmpFaceName
);
1417 ok(otm
->otmpStyleName
== NULL
, "expected NULL got %p\n", otm
->otmpStyleName
);
1419 ok(otm
->otmpFullName
== unset_ptr
, "expected %p got %p\n", unset_ptr
, otm
->otmpFullName
);
1421 HeapFree(GetProcessHeap(), 0, otm
);
1423 SelectObject(hdc
, hfont_old
);
1424 DeleteObject(hfont
);
1429 static void testJustification(HDC hdc
, PSTR str
, RECT
*clientArea
)
1433 justifiedWidth
= 0, /* to test GetTextExtentExPointW() */
1434 areaWidth
= clientArea
->right
- clientArea
->left
,
1436 BOOL lastExtent
= FALSE
;
1437 PSTR pFirstChar
, pLastChar
;
1443 int GetTextExtentExPointWWidth
;
1446 GetTextMetricsA(hdc
, &tm
);
1447 y
= clientArea
->top
;
1450 while (*str
== tm
.tmBreakChar
) str
++; /* skip leading break chars */
1456 /* if not at the end of the string, ... */
1457 if (*str
== '\0') break;
1458 /* ... add the next word to the current extent */
1459 while (*str
!= '\0' && *str
++ != tm
.tmBreakChar
);
1461 SetTextJustification(hdc
, 0, 0);
1462 GetTextExtentPoint32(hdc
, pFirstChar
, str
- pFirstChar
- 1, &size
);
1463 } while ((int) size
.cx
< areaWidth
);
1465 /* ignore trailing break chars */
1467 while (*(pLastChar
- 1) == tm
.tmBreakChar
)
1473 if (*str
== '\0' || breakCount
<= 0) pLastChar
= str
;
1475 SetTextJustification(hdc
, 0, 0);
1476 GetTextExtentPoint32(hdc
, pFirstChar
, pLastChar
- pFirstChar
, &size
);
1478 /* do not justify the last extent */
1479 if (*str
!= '\0' && breakCount
> 0)
1481 SetTextJustification(hdc
, areaWidth
- size
.cx
, breakCount
);
1482 GetTextExtentPoint32(hdc
, pFirstChar
, pLastChar
- pFirstChar
, &size
);
1483 justifiedWidth
= size
.cx
;
1485 else lastExtent
= TRUE
;
1487 /* catch errors and report them */
1488 if (!lastExtent
&& (justifiedWidth
!= areaWidth
))
1490 memset(error
[nErrors
].extent
, 0, 100);
1491 memcpy(error
[nErrors
].extent
, pFirstChar
, pLastChar
- pFirstChar
);
1492 error
[nErrors
].GetTextExtentExPointWWidth
= justifiedWidth
;
1498 } while (*str
&& y
< clientArea
->bottom
);
1500 for (e
= 0; e
< nErrors
; e
++)
1502 /* The width returned by GetTextExtentPoint32() is exactly the same
1503 returned by GetTextExtentExPointW() - see dlls/gdi32/font.c */
1504 ok(error
[e
].GetTextExtentExPointWWidth
== areaWidth
,
1505 "GetTextExtentPointW() for \"%s\" should have returned a width of %d, not %d.\n",
1506 error
[e
].extent
, areaWidth
, error
[e
].GetTextExtentExPointWWidth
);
1510 static void test_SetTextJustification(void)
1517 static char testText
[] =
1518 "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do "
1519 "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut "
1520 "enim ad minim veniam, quis nostrud exercitation ullamco laboris "
1521 "nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in "
1522 "reprehenderit in voluptate velit esse cillum dolore eu fugiat "
1523 "nulla pariatur. Excepteur sint occaecat cupidatat non proident, "
1524 "sunt in culpa qui officia deserunt mollit anim id est laborum.";
1526 hwnd
= CreateWindowExA(0, "static", "", WS_POPUP
, 0,0, 400,400, 0, 0, 0, NULL
);
1527 GetClientRect( hwnd
, &clientArea
);
1528 hdc
= GetDC( hwnd
);
1530 memset(&lf
, 0, sizeof lf
);
1531 lf
.lfCharSet
= ANSI_CHARSET
;
1532 lf
.lfClipPrecision
= CLIP_DEFAULT_PRECIS
;
1533 lf
.lfWeight
= FW_DONTCARE
;
1535 lf
.lfQuality
= DEFAULT_QUALITY
;
1536 lstrcpyA(lf
.lfFaceName
, "Times New Roman");
1537 hfont
= create_font("Times New Roman", &lf
);
1538 SelectObject(hdc
, hfont
);
1540 testJustification(hdc
, testText
, &clientArea
);
1542 DeleteObject(hfont
);
1543 ReleaseDC(hwnd
, hdc
);
1544 DestroyWindow(hwnd
);
1547 static BOOL
get_glyph_indices(INT charset
, UINT code_page
, WORD
*idx
, UINT count
, BOOL unicode
)
1551 HFONT hfont
, hfont_old
;
1558 assert(count
<= 128);
1560 memset(&lf
, 0, sizeof(lf
));
1562 lf
.lfCharSet
= charset
;
1564 lstrcpyA(lf
.lfFaceName
, "Arial");
1565 SetLastError(0xdeadbeef);
1566 hfont
= CreateFontIndirectA(&lf
);
1567 ok(hfont
!= 0, "CreateFontIndirectA error %u\n", GetLastError());
1570 hfont_old
= SelectObject(hdc
, hfont
);
1572 cs
= GetTextCharsetInfo(hdc
, &fs
, 0);
1573 ok(cs
== charset
, "expected %d, got %d\n", charset
, cs
);
1575 SetLastError(0xdeadbeef);
1576 ret
= GetTextFaceA(hdc
, sizeof(name
), name
);
1577 ok(ret
, "GetTextFaceA error %u\n", GetLastError());
1579 if (charset
== SYMBOL_CHARSET
)
1581 ok(strcmp("Arial", name
), "face name should NOT be Arial\n");
1582 ok(fs
.fsCsb
[0] & (1 << 31), "symbol encoding should be available\n");
1586 ok(!strcmp("Arial", name
), "face name should be Arial, not %s\n", name
);
1587 ok(!(fs
.fsCsb
[0] & (1 << 31)), "symbol encoding should NOT be available\n");
1590 if (!TranslateCharsetInfo((DWORD
*)(INT_PTR
)cs
, &csi
, TCI_SRCCHARSET
))
1592 trace("Can't find codepage for charset %d\n", cs
);
1596 ok(csi
.ciACP
== code_page
, "expected %d, got %d\n", code_page
, csi
.ciACP
);
1601 WCHAR unicode_buf
[128];
1603 for (i
= 0; i
< count
; i
++) ansi_buf
[i
] = (BYTE
)(i
+ 128);
1605 MultiByteToWideChar(code_page
, 0, ansi_buf
, count
, unicode_buf
, count
);
1607 SetLastError(0xdeadbeef);
1608 ret
= pGetGlyphIndicesW(hdc
, unicode_buf
, count
, idx
, 0);
1609 ok(ret
== count
, "GetGlyphIndicesW error %u\n", GetLastError());
1615 for (i
= 0; i
< count
; i
++) ansi_buf
[i
] = (BYTE
)(i
+ 128);
1617 SetLastError(0xdeadbeef);
1618 ret
= pGetGlyphIndicesA(hdc
, ansi_buf
, count
, idx
, 0);
1619 ok(ret
== count
, "GetGlyphIndicesA error %u\n", GetLastError());
1622 SelectObject(hdc
, hfont_old
);
1623 DeleteObject(hfont
);
1630 static void test_font_charset(void)
1632 static struct charset_data
1636 WORD font_idxA
[128], font_idxW
[128];
1639 { ANSI_CHARSET
, 1252 },
1640 { RUSSIAN_CHARSET
, 1251 },
1641 { SYMBOL_CHARSET
, CP_SYMBOL
} /* keep it as the last one */
1645 if (!pGetGlyphIndicesA
|| !pGetGlyphIndicesW
)
1647 win_skip("Skipping the font charset test on a Win9x platform\n");
1651 if (!is_font_installed("Arial"))
1653 skip("Arial is not installed\n");
1657 for (i
= 0; i
< sizeof(cd
)/sizeof(cd
[0]); i
++)
1659 if (cd
[i
].charset
== SYMBOL_CHARSET
)
1661 if (!is_font_installed("Symbol") && !is_font_installed("Wingdings"))
1663 skip("Symbol or Wingdings is not installed\n");
1667 get_glyph_indices(cd
[i
].charset
, cd
[i
].code_page
, cd
[i
].font_idxA
, 128, FALSE
);
1668 get_glyph_indices(cd
[i
].charset
, cd
[i
].code_page
, cd
[i
].font_idxW
, 128, TRUE
);
1669 ok(!memcmp(cd
[i
].font_idxA
, cd
[i
].font_idxW
, 128*sizeof(WORD
)), "%d: indices don't match\n", i
);
1672 ok(memcmp(cd
[0].font_idxW
, cd
[1].font_idxW
, 128*sizeof(WORD
)), "0 vs 1: indices shouldn't match\n");
1675 ok(memcmp(cd
[0].font_idxW
, cd
[2].font_idxW
, 128*sizeof(WORD
)), "0 vs 2: indices shouldn't match\n");
1676 ok(memcmp(cd
[1].font_idxW
, cd
[2].font_idxW
, 128*sizeof(WORD
)), "1 vs 2: indices shouldn't match\n");
1679 skip("Symbol or Wingdings is not installed\n");
1682 static void test_GetFontUnicodeRanges(void)
1686 HFONT hfont
, hfont_old
;
1690 if (!pGetFontUnicodeRanges
)
1692 win_skip("GetFontUnicodeRanges not available before W2K\n");
1696 memset(&lf
, 0, sizeof(lf
));
1697 lstrcpyA(lf
.lfFaceName
, "Arial");
1698 hfont
= create_font("Arial", &lf
);
1701 hfont_old
= SelectObject(hdc
, hfont
);
1703 size
= pGetFontUnicodeRanges(NULL
, NULL
);
1704 ok(!size
, "GetFontUnicodeRanges succeeded unexpectedly\n");
1706 size
= pGetFontUnicodeRanges(hdc
, NULL
);
1707 ok(size
, "GetFontUnicodeRanges failed unexpectedly\n");
1709 gs
= HeapAlloc(GetProcessHeap(), 0, size
);
1711 size
= pGetFontUnicodeRanges(hdc
, gs
);
1712 ok(size
, "GetFontUnicodeRanges failed\n");
1714 for (i
= 0; i
< gs
->cRanges
; i
++)
1715 trace("%03d wcLow %04x cGlyphs %u\n", i
, gs
->ranges
[i
].wcLow
, gs
->ranges
[i
].cGlyphs
);
1717 trace("found %u ranges\n", gs
->cRanges
);
1719 HeapFree(GetProcessHeap(), 0, gs
);
1721 SelectObject(hdc
, hfont_old
);
1722 DeleteObject(hfont
);
1723 ReleaseDC(NULL
, hdc
);
1726 #define MAX_ENUM_FONTS 4096
1728 struct enum_font_data
1731 LOGFONT lf
[MAX_ENUM_FONTS
];
1734 struct enum_font_dataW
1737 LOGFONTW lf
[MAX_ENUM_FONTS
];
1740 static INT CALLBACK
arial_enum_proc(const LOGFONT
*lf
, const TEXTMETRIC
*tm
, DWORD type
, LPARAM lParam
)
1742 struct enum_font_data
*efd
= (struct enum_font_data
*)lParam
;
1744 ok(lf
->lfHeight
== tm
->tmHeight
, "lfHeight %d != tmHeight %d\n", lf
->lfHeight
, tm
->tmHeight
);
1746 if (type
!= TRUETYPE_FONTTYPE
) return 1;
1748 trace("enumed font \"%s\", charset %d, height %d, weight %d, italic %d\n",
1749 lf
->lfFaceName
, lf
->lfCharSet
, lf
->lfHeight
, lf
->lfWeight
, lf
->lfItalic
);
1751 if (efd
->total
< MAX_ENUM_FONTS
)
1752 efd
->lf
[efd
->total
++] = *lf
;
1754 trace("enum tests invalid; you have more than %d fonts\n", MAX_ENUM_FONTS
);
1759 static INT CALLBACK
arial_enum_procw(const LOGFONTW
*lf
, const TEXTMETRICW
*tm
, DWORD type
, LPARAM lParam
)
1761 struct enum_font_dataW
*efd
= (struct enum_font_dataW
*)lParam
;
1763 ok(lf
->lfHeight
== tm
->tmHeight
, "lfHeight %d != tmHeight %d\n", lf
->lfHeight
, tm
->tmHeight
);
1765 if (type
!= TRUETYPE_FONTTYPE
) return 1;
1767 trace("enumed font \"%s\", charset %d, height %d, weight %d, italic %d\n",
1768 lf
->lfFaceName
, lf
->lfCharSet
, lf
->lfHeight
, lf
->lfWeight
, lf
->lfItalic
);
1770 if (efd
->total
< MAX_ENUM_FONTS
)
1771 efd
->lf
[efd
->total
++] = *lf
;
1773 trace("enum tests invalid; you have more than %d fonts\n", MAX_ENUM_FONTS
);
1778 static void get_charset_stats(struct enum_font_data
*efd
,
1779 int *ansi_charset
, int *symbol_charset
,
1780 int *russian_charset
)
1785 *symbol_charset
= 0;
1786 *russian_charset
= 0;
1788 for (i
= 0; i
< efd
->total
; i
++)
1790 switch (efd
->lf
[i
].lfCharSet
)
1795 case SYMBOL_CHARSET
:
1796 (*symbol_charset
)++;
1798 case RUSSIAN_CHARSET
:
1799 (*russian_charset
)++;
1805 static void get_charset_statsW(struct enum_font_dataW
*efd
,
1806 int *ansi_charset
, int *symbol_charset
,
1807 int *russian_charset
)
1812 *symbol_charset
= 0;
1813 *russian_charset
= 0;
1815 for (i
= 0; i
< efd
->total
; i
++)
1817 switch (efd
->lf
[i
].lfCharSet
)
1822 case SYMBOL_CHARSET
:
1823 (*symbol_charset
)++;
1825 case RUSSIAN_CHARSET
:
1826 (*russian_charset
)++;
1832 static void test_EnumFontFamilies(const char *font_name
, INT font_charset
)
1834 struct enum_font_data efd
;
1835 struct enum_font_dataW efdw
;
1838 int i
, ret
, ansi_charset
, symbol_charset
, russian_charset
;
1840 trace("Testing font %s, charset %d\n", *font_name
? font_name
: "<empty>", font_charset
);
1842 if (*font_name
&& !is_truetype_font_installed(font_name
))
1844 skip("%s is not installed\n", font_name
);
1850 /* Observed behaviour: EnumFontFamilies enumerates aliases like "Arial Cyr"
1851 * while EnumFontFamiliesEx doesn't.
1853 if (!*font_name
&& font_charset
== DEFAULT_CHARSET
) /* do it only once */
1856 * Use EnumFontFamiliesW since win98 crashes when the
1857 * second parameter is NULL using EnumFontFamilies
1860 SetLastError(0xdeadbeef);
1861 ret
= EnumFontFamiliesW(hdc
, NULL
, arial_enum_procw
, (LPARAM
)&efdw
);
1862 ok(ret
|| GetLastError() == ERROR_CALL_NOT_IMPLEMENTED
, "EnumFontFamiliesW error %u\n", GetLastError());
1865 get_charset_statsW(&efdw
, &ansi_charset
, &symbol_charset
, &russian_charset
);
1866 trace("enumerated ansi %d, symbol %d, russian %d fonts for NULL\n",
1867 ansi_charset
, symbol_charset
, russian_charset
);
1868 ok(efdw
.total
> 0, "fonts enumerated: NULL\n");
1869 ok(ansi_charset
> 0, "NULL family should enumerate ANSI_CHARSET\n");
1870 ok(symbol_charset
> 0, "NULL family should enumerate SYMBOL_CHARSET\n");
1871 ok(russian_charset
> 0 ||
1872 broken(russian_charset
== 0), /* NT4 */
1873 "NULL family should enumerate RUSSIAN_CHARSET\n");
1877 SetLastError(0xdeadbeef);
1878 ret
= EnumFontFamiliesExW(hdc
, NULL
, arial_enum_procw
, (LPARAM
)&efdw
, 0);
1879 ok(ret
|| GetLastError() == ERROR_CALL_NOT_IMPLEMENTED
, "EnumFontFamiliesExW error %u\n", GetLastError());
1882 get_charset_statsW(&efdw
, &ansi_charset
, &symbol_charset
, &russian_charset
);
1883 trace("enumerated ansi %d, symbol %d, russian %d fonts for NULL\n",
1884 ansi_charset
, symbol_charset
, russian_charset
);
1885 ok(efdw
.total
> 0, "fonts enumerated: NULL\n");
1886 ok(ansi_charset
> 0, "NULL family should enumerate ANSI_CHARSET\n");
1887 ok(symbol_charset
> 0, "NULL family should enumerate SYMBOL_CHARSET\n");
1888 ok(russian_charset
> 0, "NULL family should enumerate RUSSIAN_CHARSET\n");
1893 SetLastError(0xdeadbeef);
1894 ret
= EnumFontFamilies(hdc
, font_name
, arial_enum_proc
, (LPARAM
)&efd
);
1895 ok(ret
, "EnumFontFamilies error %u\n", GetLastError());
1896 get_charset_stats(&efd
, &ansi_charset
, &symbol_charset
, &russian_charset
);
1897 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s\n",
1898 ansi_charset
, symbol_charset
, russian_charset
,
1899 *font_name
? font_name
: "<empty>");
1901 ok(efd
.total
> 0, "no fonts enumerated: %s\n", font_name
);
1903 ok(!efd
.total
, "no fonts should be enumerated for empty font_name\n");
1904 for (i
= 0; i
< efd
.total
; i
++)
1906 /* FIXME: remove completely once Wine is fixed */
1907 if (efd
.lf
[i
].lfCharSet
!= font_charset
)
1910 ok(efd
.lf
[i
].lfCharSet
== font_charset
, "%d: got charset %d\n", i
, efd
.lf
[i
].lfCharSet
);
1913 ok(efd
.lf
[i
].lfCharSet
== font_charset
, "%d: got charset %d\n", i
, efd
.lf
[i
].lfCharSet
);
1914 ok(!lstrcmp(efd
.lf
[i
].lfFaceName
, font_name
), "expected %s, got %s\n",
1915 font_name
, efd
.lf
[i
].lfFaceName
);
1918 memset(&lf
, 0, sizeof(lf
));
1919 lf
.lfCharSet
= ANSI_CHARSET
;
1920 lstrcpy(lf
.lfFaceName
, font_name
);
1922 SetLastError(0xdeadbeef);
1923 ret
= EnumFontFamiliesEx(hdc
, &lf
, arial_enum_proc
, (LPARAM
)&efd
, 0);
1924 ok(ret
, "EnumFontFamiliesEx error %u\n", GetLastError());
1925 get_charset_stats(&efd
, &ansi_charset
, &symbol_charset
, &russian_charset
);
1926 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s ANSI_CHARSET\n",
1927 ansi_charset
, symbol_charset
, russian_charset
,
1928 *font_name
? font_name
: "<empty>");
1929 if (font_charset
== SYMBOL_CHARSET
)
1932 ok(efd
.total
== 0, "no fonts should be enumerated: %s ANSI_CHARSET\n", font_name
);
1934 ok(efd
.total
> 0, "no fonts enumerated: %s\n", font_name
);
1938 ok(efd
.total
> 0, "no fonts enumerated: %s ANSI_CHARSET\n", font_name
);
1939 for (i
= 0; i
< efd
.total
; i
++)
1941 ok(efd
.lf
[i
].lfCharSet
== ANSI_CHARSET
, "%d: got charset %d\n", i
, efd
.lf
[i
].lfCharSet
);
1943 ok(!lstrcmp(efd
.lf
[i
].lfFaceName
, font_name
), "expected %s, got %s\n",
1944 font_name
, efd
.lf
[i
].lfFaceName
);
1948 /* DEFAULT_CHARSET should enumerate all available charsets */
1949 memset(&lf
, 0, sizeof(lf
));
1950 lf
.lfCharSet
= DEFAULT_CHARSET
;
1951 lstrcpy(lf
.lfFaceName
, font_name
);
1953 SetLastError(0xdeadbeef);
1954 EnumFontFamiliesEx(hdc
, &lf
, arial_enum_proc
, (LPARAM
)&efd
, 0);
1955 ok(ret
, "EnumFontFamiliesEx error %u\n", GetLastError());
1956 get_charset_stats(&efd
, &ansi_charset
, &symbol_charset
, &russian_charset
);
1957 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s DEFAULT_CHARSET\n",
1958 ansi_charset
, symbol_charset
, russian_charset
,
1959 *font_name
? font_name
: "<empty>");
1960 ok(efd
.total
> 0, "no fonts enumerated: %s DEFAULT_CHARSET\n", font_name
);
1961 for (i
= 0; i
< efd
.total
; i
++)
1964 ok(!lstrcmp(efd
.lf
[i
].lfFaceName
, font_name
), "expected %s, got %s\n",
1965 font_name
, efd
.lf
[i
].lfFaceName
);
1969 switch (font_charset
)
1972 ok(ansi_charset
> 0,
1973 "ANSI_CHARSET should enumerate ANSI_CHARSET for %s\n", font_name
);
1975 "ANSI_CHARSET should NOT enumerate SYMBOL_CHARSET for %s\n", font_name
);
1976 ok(russian_charset
> 0,
1977 "ANSI_CHARSET should enumerate RUSSIAN_CHARSET for %s\n", font_name
);
1979 case SYMBOL_CHARSET
:
1981 "SYMBOL_CHARSET should NOT enumerate ANSI_CHARSET for %s\n", font_name
);
1983 "SYMBOL_CHARSET should enumerate SYMBOL_CHARSET for %s\n", font_name
);
1984 ok(!russian_charset
,
1985 "SYMBOL_CHARSET should NOT enumerate RUSSIAN_CHARSET for %s\n", font_name
);
1987 case DEFAULT_CHARSET
:
1988 ok(ansi_charset
> 0,
1989 "DEFAULT_CHARSET should enumerate ANSI_CHARSET for %s\n", font_name
);
1990 ok(symbol_charset
> 0,
1991 "DEFAULT_CHARSET should enumerate SYMBOL_CHARSET for %s\n", font_name
);
1992 ok(russian_charset
> 0,
1993 "DEFAULT_CHARSET should enumerate RUSSIAN_CHARSET for %s\n", font_name
);
1999 ok(ansi_charset
> 0,
2000 "DEFAULT_CHARSET should enumerate ANSI_CHARSET for %s\n", *font_name
? font_name
: "<empty>");
2001 ok(symbol_charset
> 0,
2002 "DEFAULT_CHARSET should enumerate SYMBOL_CHARSET for %s\n", *font_name
? font_name
: "<empty>");
2003 ok(russian_charset
> 0,
2004 "DEFAULT_CHARSET should enumerate RUSSIAN_CHARSET for %s\n", *font_name
? font_name
: "<empty>");
2007 memset(&lf
, 0, sizeof(lf
));
2008 lf
.lfCharSet
= SYMBOL_CHARSET
;
2009 lstrcpy(lf
.lfFaceName
, font_name
);
2011 SetLastError(0xdeadbeef);
2012 EnumFontFamiliesEx(hdc
, &lf
, arial_enum_proc
, (LPARAM
)&efd
, 0);
2013 ok(ret
, "EnumFontFamiliesEx error %u\n", GetLastError());
2014 get_charset_stats(&efd
, &ansi_charset
, &symbol_charset
, &russian_charset
);
2015 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s SYMBOL_CHARSET\n",
2016 ansi_charset
, symbol_charset
, russian_charset
,
2017 *font_name
? font_name
: "<empty>");
2018 if (*font_name
&& font_charset
== ANSI_CHARSET
)
2019 ok(efd
.total
== 0, "no fonts should be enumerated: %s SYMBOL_CHARSET\n", font_name
);
2022 ok(efd
.total
> 0, "no fonts enumerated: %s SYMBOL_CHARSET\n", font_name
);
2023 for (i
= 0; i
< efd
.total
; i
++)
2025 ok(efd
.lf
[i
].lfCharSet
== SYMBOL_CHARSET
, "%d: got charset %d\n", i
, efd
.lf
[i
].lfCharSet
);
2027 ok(!lstrcmp(efd
.lf
[i
].lfFaceName
, font_name
), "expected %s, got %s\n",
2028 font_name
, efd
.lf
[i
].lfFaceName
);
2032 "SYMBOL_CHARSET should NOT enumerate ANSI_CHARSET for %s\n", *font_name
? font_name
: "<empty>");
2033 ok(symbol_charset
> 0,
2034 "SYMBOL_CHARSET should enumerate SYMBOL_CHARSET for %s\n", *font_name
? font_name
: "<empty>");
2035 ok(!russian_charset
,
2036 "SYMBOL_CHARSET should NOT enumerate RUSSIAN_CHARSET for %s\n", *font_name
? font_name
: "<empty>");
2042 static void test_negative_width(HDC hdc
, const LOGFONTA
*lf
)
2044 HFONT hfont
, hfont_prev
;
2046 GLYPHMETRICS gm1
, gm2
;
2049 MAT2 mat
= { {0,1}, {0,0}, {0,0}, {0,1} };
2051 if(!pGetGlyphIndicesA
)
2054 /* negative widths are handled just as positive ones */
2055 lf2
.lfWidth
= -lf
->lfWidth
;
2057 SetLastError(0xdeadbeef);
2058 hfont
= CreateFontIndirectA(lf
);
2059 ok(hfont
!= 0, "CreateFontIndirect error %u\n", GetLastError());
2060 check_font("original", lf
, hfont
);
2062 hfont_prev
= SelectObject(hdc
, hfont
);
2064 ret
= pGetGlyphIndicesA(hdc
, "x", 1, &idx
, GGI_MARK_NONEXISTING_GLYPHS
);
2065 if (ret
== GDI_ERROR
|| idx
== 0xffff)
2067 SelectObject(hdc
, hfont_prev
);
2068 DeleteObject(hfont
);
2069 skip("Font %s doesn't contain 'x', skipping the test\n", lf
->lfFaceName
);
2073 /* filling with 0xaa causes false pass under WINEDEBUG=warn+heap */
2074 memset(&gm1
, 0xab, sizeof(gm1
));
2075 SetLastError(0xdeadbeef);
2076 ret
= GetGlyphOutlineA(hdc
, 'x', GGO_METRICS
, &gm1
, 0, NULL
, &mat
);
2077 ok(ret
!= GDI_ERROR
, "GetGlyphOutline error 0x%x\n", GetLastError());
2079 SelectObject(hdc
, hfont_prev
);
2080 DeleteObject(hfont
);
2082 SetLastError(0xdeadbeef);
2083 hfont
= CreateFontIndirectA(&lf2
);
2084 ok(hfont
!= 0, "CreateFontIndirect error %u\n", GetLastError());
2085 check_font("negative width", &lf2
, hfont
);
2087 hfont_prev
= SelectObject(hdc
, hfont
);
2089 memset(&gm2
, 0xbb, sizeof(gm2
));
2090 SetLastError(0xdeadbeef);
2091 ret
= GetGlyphOutlineA(hdc
, 'x', GGO_METRICS
, &gm2
, 0, NULL
, &mat
);
2092 ok(ret
!= GDI_ERROR
, "GetGlyphOutline error 0x%x\n", GetLastError());
2094 SelectObject(hdc
, hfont_prev
);
2095 DeleteObject(hfont
);
2097 ok(gm1
.gmBlackBoxX
== gm2
.gmBlackBoxX
&&
2098 gm1
.gmBlackBoxY
== gm2
.gmBlackBoxY
&&
2099 gm1
.gmptGlyphOrigin
.x
== gm2
.gmptGlyphOrigin
.x
&&
2100 gm1
.gmptGlyphOrigin
.y
== gm2
.gmptGlyphOrigin
.y
&&
2101 gm1
.gmCellIncX
== gm2
.gmCellIncX
&&
2102 gm1
.gmCellIncY
== gm2
.gmCellIncY
,
2103 "gm1=%d,%d,%d,%d,%d,%d gm2=%d,%d,%d,%d,%d,%d\n",
2104 gm1
.gmBlackBoxX
, gm1
.gmBlackBoxY
, gm1
.gmptGlyphOrigin
.x
,
2105 gm1
.gmptGlyphOrigin
.y
, gm1
.gmCellIncX
, gm1
.gmCellIncY
,
2106 gm2
.gmBlackBoxX
, gm2
.gmBlackBoxY
, gm2
.gmptGlyphOrigin
.x
,
2107 gm2
.gmptGlyphOrigin
.y
, gm2
.gmCellIncX
, gm2
.gmCellIncY
);
2110 /* PANOSE is 10 bytes in size, need to pack the structure properly */
2111 #include "pshpack2.h"
2115 SHORT xAvgCharWidth
;
2116 USHORT usWeightClass
;
2117 USHORT usWidthClass
;
2119 SHORT ySubscriptXSize
;
2120 SHORT ySubscriptYSize
;
2121 SHORT ySubscriptXOffset
;
2122 SHORT ySubscriptYOffset
;
2123 SHORT ySuperscriptXSize
;
2124 SHORT ySuperscriptYSize
;
2125 SHORT ySuperscriptXOffset
;
2126 SHORT ySuperscriptYOffset
;
2127 SHORT yStrikeoutSize
;
2128 SHORT yStrikeoutPosition
;
2131 ULONG ulUnicodeRange1
;
2132 ULONG ulUnicodeRange2
;
2133 ULONG ulUnicodeRange3
;
2134 ULONG ulUnicodeRange4
;
2137 USHORT usFirstCharIndex
;
2138 USHORT usLastCharIndex
;
2139 /* According to the Apple spec, original version didn't have the below fields,
2140 * version numbers were taked from the OpenType spec.
2142 /* version 0 (TrueType 1.5) */
2143 USHORT sTypoAscender
;
2144 USHORT sTypoDescender
;
2145 USHORT sTypoLineGap
;
2147 USHORT usWinDescent
;
2148 /* version 1 (TrueType 1.66) */
2149 ULONG ulCodePageRange1
;
2150 ULONG ulCodePageRange2
;
2151 /* version 2 (OpenType 1.2) */
2154 USHORT usDefaultChar
;
2156 USHORT usMaxContext
;
2158 #include "poppack.h"
2160 #ifdef WORDS_BIGENDIAN
2161 #define GET_BE_WORD(x) (x)
2162 #define GET_BE_DWORD(x) (x)
2164 #define GET_BE_WORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
2165 #define GET_BE_DWORD(x) MAKELONG(GET_BE_WORD(HIWORD(x)), GET_BE_WORD(LOWORD(x)));
2168 #define MS_MAKE_TAG(ch0, ch1, ch2, ch3) \
2169 ((DWORD)(BYTE)(ch0) | ((DWORD)(BYTE)(ch1) << 8) | \
2170 ((DWORD)(BYTE)(ch2) << 16) | ((DWORD)(BYTE)(ch3) << 24))
2171 #define MS_OS2_TAG MS_MAKE_TAG('O','S','/','2')
2172 #define MS_CMAP_TAG MS_MAKE_TAG('c','m','a','p')
2185 } cmap_encoding_record
;
2193 BYTE glyph_ids
[256];
2203 USHORT search_range
;
2204 USHORT entry_selector
;
2207 USHORT end_count
[1]; /* this is a variable-sized array of length seg_countx2 / 2 */
2210 USHORT start_count[seg_countx2 / 2];
2211 USHORT id_delta[seg_countx2 / 2];
2212 USHORT id_range_offset[seg_countx2 / 2];
2222 USHORT id_range_offset
;
2223 } cmap_format_4_seg
;
2225 static void expect_ff(const TEXTMETRICA
*tmA
, const TT_OS2_V2
*os2
, WORD family
, const char *name
)
2227 ok((tmA
->tmPitchAndFamily
& 0xf0) == family
, "%s: expected family %02x got %02x. panose %d-%d-%d-%d-...\n",
2228 name
, family
, tmA
->tmPitchAndFamily
, os2
->panose
.bFamilyType
, os2
->panose
.bSerifStyle
,
2229 os2
->panose
.bWeight
, os2
->panose
.bProportion
);
2232 static BOOL
get_first_last_from_cmap0(void *ptr
, DWORD
*first
, DWORD
*last
)
2235 cmap_format_0
*cmap
= (cmap_format_0
*)ptr
;
2239 for(i
= 0; i
< 256; i
++)
2241 if(cmap
->glyph_ids
[i
] == 0) continue;
2243 if(*first
== 256) *first
= i
;
2245 if(*first
== 256) return FALSE
;
2249 static void get_seg4(cmap_format_4
*cmap
, USHORT seg_num
, cmap_format_4_seg
*seg
)
2251 USHORT segs
= GET_BE_WORD(cmap
->seg_countx2
) / 2;
2252 seg
->end_count
= GET_BE_WORD(cmap
->end_count
[seg_num
]);
2253 seg
->start_count
= GET_BE_WORD(cmap
->end_count
[segs
+ 1 + seg_num
]);
2254 seg
->id_delta
= GET_BE_WORD(cmap
->end_count
[2 * segs
+ 1 + seg_num
]);
2255 seg
->id_range_offset
= GET_BE_WORD(cmap
->end_count
[3 * segs
+ 1 + seg_num
]);
2258 static BOOL
get_first_last_from_cmap4(void *ptr
, DWORD
*first
, DWORD
*last
, DWORD limit
)
2261 cmap_format_4
*cmap
= (cmap_format_4
*)ptr
;
2262 USHORT seg_count
= GET_BE_WORD(cmap
->seg_countx2
) / 2;
2263 USHORT
const *glyph_ids
= cmap
->end_count
+ 4 * seg_count
+ 1;
2267 for(i
= 0; i
< seg_count
; i
++)
2270 cmap_format_4_seg seg
;
2272 get_seg4(cmap
, i
, &seg
);
2273 for(code
= seg
.start_count
; code
<= seg
.end_count
; code
++)
2275 if(seg
.id_range_offset
== 0)
2276 index
= (seg
.id_delta
+ code
) & 0xffff;
2279 index
= seg
.id_range_offset
/ 2
2280 + code
- seg
.start_count
2283 /* some fonts have broken last segment */
2284 if ((char *)(glyph_ids
+ index
+ sizeof(*glyph_ids
)) < (char *)ptr
+ limit
)
2285 index
= GET_BE_WORD(glyph_ids
[index
]);
2288 trace("segment %04x/%04x index %04x points to nowhere\n",
2289 seg
.start_count
, seg
.end_count
, index
);
2292 if(index
) index
+= seg
.id_delta
;
2294 if(*first
== 0x10000)
2295 *last
= *first
= code
;
2301 if(*first
== 0x10000) return FALSE
;
2305 static void *get_cmap(cmap_header
*header
, USHORT plat_id
, USHORT enc_id
)
2308 cmap_encoding_record
*record
= (cmap_encoding_record
*)(header
+ 1);
2310 for(i
= 0; i
< GET_BE_WORD(header
->num_tables
); i
++)
2312 if(GET_BE_WORD(record
->plat_id
) == plat_id
&& GET_BE_WORD(record
->enc_id
) == enc_id
)
2313 return (BYTE
*)header
+ GET_BE_DWORD(record
->offset
);
2326 static BOOL
get_first_last_from_cmap(HDC hdc
, DWORD
*first
, DWORD
*last
, cmap_type
*cmap_type
)
2329 cmap_header
*header
;
2334 size
= GetFontData(hdc
, MS_CMAP_TAG
, 0, NULL
, 0);
2335 ok(size
!= GDI_ERROR
, "no cmap table found\n");
2336 if(size
== GDI_ERROR
) return FALSE
;
2338 header
= HeapAlloc(GetProcessHeap(), 0, size
);
2339 ret
= GetFontData(hdc
, MS_CMAP_TAG
, 0, header
, size
);
2340 ok(ret
== size
, "GetFontData should return %u not %u\n", size
, ret
);
2341 ok(GET_BE_WORD(header
->version
) == 0, "got cmap version %d\n", GET_BE_WORD(header
->version
));
2343 cmap
= get_cmap(header
, 3, 1);
2345 *cmap_type
= cmap_ms_unicode
;
2348 cmap
= get_cmap(header
, 3, 0);
2349 if(cmap
) *cmap_type
= cmap_ms_symbol
;
2353 *cmap_type
= cmap_none
;
2357 format
= GET_BE_WORD(*(WORD
*)cmap
);
2361 r
= get_first_last_from_cmap0(cmap
, first
, last
);
2364 r
= get_first_last_from_cmap4(cmap
, first
, last
, size
);
2367 trace("unhandled cmap format %d\n", format
);
2372 HeapFree(GetProcessHeap(), 0, header
);
2376 static void test_text_metrics(const LOGFONTA
*lf
)
2379 HFONT hfont
, hfont_old
;
2383 const char *font_name
= lf
->lfFaceName
;
2384 DWORD cmap_first
= 0, cmap_last
= 0;
2385 cmap_type cmap_type
;
2389 SetLastError(0xdeadbeef);
2390 hfont
= CreateFontIndirectA(lf
);
2391 ok(hfont
!= 0, "CreateFontIndirect error %u\n", GetLastError());
2393 hfont_old
= SelectObject(hdc
, hfont
);
2395 size
= GetFontData(hdc
, MS_OS2_TAG
, 0, NULL
, 0);
2396 if (size
== GDI_ERROR
)
2398 trace("OS/2 chunk was not found\n");
2401 if (size
> sizeof(tt_os2
))
2403 trace("got too large OS/2 chunk of size %u\n", size
);
2404 size
= sizeof(tt_os2
);
2407 memset(&tt_os2
, 0, sizeof(tt_os2
));
2408 ret
= GetFontData(hdc
, MS_OS2_TAG
, 0, &tt_os2
, size
);
2409 ok(ret
== size
, "GetFontData should return %u not %u\n", size
, ret
);
2411 SetLastError(0xdeadbeef);
2412 ret
= GetTextMetricsA(hdc
, &tmA
);
2413 ok(ret
, "GetTextMetricsA error %u\n", GetLastError());
2415 if(!get_first_last_from_cmap(hdc
, &cmap_first
, &cmap_last
, &cmap_type
))
2417 skip("Unable to retrieve first and last glyphs from cmap\n");
2421 USHORT expect_first_A
, expect_last_A
, expect_break_A
, expect_default_A
;
2422 USHORT expect_first_W
, expect_last_W
, expect_break_W
, expect_default_W
;
2423 UINT os2_first_char
, os2_last_char
, default_char
, break_char
;
2427 version
= GET_BE_WORD(tt_os2
.version
);
2429 os2_first_char
= GET_BE_WORD(tt_os2
.usFirstCharIndex
);
2430 os2_last_char
= GET_BE_WORD(tt_os2
.usLastCharIndex
);
2431 default_char
= GET_BE_WORD(tt_os2
.usDefaultChar
);
2432 break_char
= GET_BE_WORD(tt_os2
.usBreakChar
);
2434 trace("font %s charset %u: %x-%x (%x-%x) default %x break %x OS/2 version %u vendor %4.4s\n",
2435 font_name
, lf
->lfCharSet
, os2_first_char
, os2_last_char
, cmap_first
, cmap_last
,
2436 default_char
, break_char
, version
, (LPCSTR
)&tt_os2
.achVendID
);
2438 if (cmap_type
== cmap_ms_symbol
|| (cmap_first
>= 0xf000 && cmap_first
< 0xf100))
2443 case 1257: /* Baltic */
2444 expect_last_W
= 0xf8fd;
2447 expect_last_W
= 0xf0ff;
2449 expect_break_W
= 0x20;
2450 expect_default_W
= expect_break_W
- 1;
2451 expect_first_A
= 0x1e;
2452 expect_last_A
= min(os2_last_char
- os2_first_char
+ 0x20, 0xff);
2456 expect_first_W
= cmap_first
;
2457 expect_last_W
= min(cmap_last
, os2_last_char
);
2458 if(os2_first_char
<= 1)
2459 expect_break_W
= os2_first_char
+ 2;
2460 else if(os2_first_char
> 0xff)
2461 expect_break_W
= 0x20;
2463 expect_break_W
= os2_first_char
;
2464 expect_default_W
= expect_break_W
- 1;
2465 expect_first_A
= expect_default_W
- 1;
2466 expect_last_A
= min(expect_last_W
, 0xff);
2468 expect_break_A
= expect_break_W
;
2469 expect_default_A
= expect_default_W
;
2471 /* Wine currently uses SYMBOL_CHARSET to identify whether the ANSI metrics need special handling */
2472 if(cmap_type
!= cmap_ms_symbol
&& tmA
.tmCharSet
== SYMBOL_CHARSET
&& expect_first_A
!= 0x1e)
2473 todo_wine
ok(tmA
.tmFirstChar
== expect_first_A
||
2474 tmA
.tmFirstChar
== expect_first_A
+ 1 /* win9x */,
2475 "A: tmFirstChar for %s got %02x expected %02x\n", font_name
, tmA
.tmFirstChar
, expect_first_A
);
2477 ok(tmA
.tmFirstChar
== expect_first_A
||
2478 tmA
.tmFirstChar
== expect_first_A
+ 1 /* win9x */,
2479 "A: tmFirstChar for %s got %02x expected %02x\n", font_name
, tmA
.tmFirstChar
, expect_first_A
);
2480 ok(tmA
.tmLastChar
== expect_last_A
||
2481 tmA
.tmLastChar
== 0xff /* win9x */,
2482 "A: tmLastChar for %s got %02x expected %02x\n", font_name
, tmA
.tmLastChar
, expect_last_A
);
2483 ok(tmA
.tmBreakChar
== expect_break_A
, "A: tmBreakChar for %s got %02x expected %02x\n",
2484 font_name
, tmA
.tmBreakChar
, expect_break_A
);
2485 ok(tmA
.tmDefaultChar
== expect_default_A
, "A: tmDefaultChar for %s got %02x expected %02x\n",
2486 font_name
, tmA
.tmDefaultChar
, expect_default_A
);
2489 SetLastError(0xdeadbeef);
2490 ret
= GetTextMetricsW(hdc
, &tmW
);
2491 ok(ret
|| GetLastError() == ERROR_CALL_NOT_IMPLEMENTED
,
2492 "GetTextMetricsW error %u\n", GetLastError());
2495 /* Wine uses the os2 first char */
2496 if(cmap_first
!= os2_first_char
&& cmap_type
!= cmap_ms_symbol
)
2497 todo_wine
ok(tmW
.tmFirstChar
== expect_first_W
, "W: tmFirstChar for %s got %02x expected %02x\n",
2498 font_name
, tmW
.tmFirstChar
, expect_first_W
);
2500 ok(tmW
.tmFirstChar
== expect_first_W
, "W: tmFirstChar for %s got %02x expected %02x\n",
2501 font_name
, tmW
.tmFirstChar
, expect_first_W
);
2503 /* Wine uses the os2 last char */
2504 if(expect_last_W
!= os2_last_char
&& cmap_type
!= cmap_ms_symbol
)
2505 todo_wine
ok(tmW
.tmLastChar
== expect_last_W
, "W: tmLastChar for %s got %02x expected %02x\n",
2506 font_name
, tmW
.tmLastChar
, expect_last_W
);
2508 ok(tmW
.tmLastChar
== expect_last_W
, "W: tmLastChar for %s got %02x expected %02x\n",
2509 font_name
, tmW
.tmLastChar
, expect_last_W
);
2510 ok(tmW
.tmBreakChar
== expect_break_W
, "W: tmBreakChar for %s got %02x expected %02x\n",
2511 font_name
, tmW
.tmBreakChar
, expect_break_W
);
2512 ok(tmW
.tmDefaultChar
== expect_default_W
, "W: tmDefaultChar for %s got %02x expected %02x\n",
2513 font_name
, tmW
.tmDefaultChar
, expect_default_W
);
2515 /* Test the aspect ratio while we have tmW */
2516 ret
= GetDeviceCaps(hdc
, LOGPIXELSX
);
2517 ok(tmW
.tmDigitizedAspectX
== ret
, "W: tmDigitizedAspectX %u != %u\n",
2518 tmW
.tmDigitizedAspectX
, ret
);
2519 ret
= GetDeviceCaps(hdc
, LOGPIXELSY
);
2520 ok(tmW
.tmDigitizedAspectX
== ret
, "W: tmDigitizedAspectY %u != %u\n",
2521 tmW
.tmDigitizedAspectX
, ret
);
2525 /* test FF_ values */
2526 switch(tt_os2
.panose
.bFamilyType
)
2530 case PAN_FAMILY_TEXT_DISPLAY
:
2531 case PAN_FAMILY_PICTORIAL
:
2533 if((tmA
.tmPitchAndFamily
& 1) == 0 || /* fixed */
2534 tt_os2
.panose
.bProportion
== PAN_PROP_MONOSPACED
)
2536 expect_ff(&tmA
, &tt_os2
, FF_MODERN
, font_name
);
2539 switch(tt_os2
.panose
.bSerifStyle
)
2544 expect_ff(&tmA
, &tt_os2
, FF_DONTCARE
, font_name
);
2547 case PAN_SERIF_COVE
:
2548 case PAN_SERIF_OBTUSE_COVE
:
2549 case PAN_SERIF_SQUARE_COVE
:
2550 case PAN_SERIF_OBTUSE_SQUARE_COVE
:
2551 case PAN_SERIF_SQUARE
:
2552 case PAN_SERIF_THIN
:
2553 case PAN_SERIF_BONE
:
2554 case PAN_SERIF_EXAGGERATED
:
2555 case PAN_SERIF_TRIANGLE
:
2556 expect_ff(&tmA
, &tt_os2
, FF_ROMAN
, font_name
);
2559 case PAN_SERIF_NORMAL_SANS
:
2560 case PAN_SERIF_OBTUSE_SANS
:
2561 case PAN_SERIF_PERP_SANS
:
2562 case PAN_SERIF_FLARED
:
2563 case PAN_SERIF_ROUNDED
: