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 #define expect(expected, got) ok(got == expected, "Expected %.8x, got %.8x\n", expected, got)
35 LONG (WINAPI
*pGdiGetCharDimensions
)(HDC hdc
, LPTEXTMETRICW lptm
, LONG
*height
);
36 BOOL (WINAPI
*pGetCharABCWidthsI
)(HDC hdc
, UINT first
, UINT count
, LPWORD glyphs
, LPABC abc
);
37 BOOL (WINAPI
*pGetCharABCWidthsW
)(HDC hdc
, UINT first
, UINT last
, LPABC abc
);
38 DWORD (WINAPI
*pGetFontUnicodeRanges
)(HDC hdc
, LPGLYPHSET lpgs
);
39 DWORD (WINAPI
*pGetGlyphIndicesA
)(HDC hdc
, LPCSTR lpstr
, INT count
, LPWORD pgi
, DWORD flags
);
40 DWORD (WINAPI
*pGetGlyphIndicesW
)(HDC hdc
, LPCWSTR lpstr
, INT count
, LPWORD pgi
, DWORD flags
);
41 BOOL (WINAPI
*pGdiRealizationInfo
)(HDC hdc
, DWORD
*);
43 static HMODULE hgdi32
= 0;
45 static void init(void)
47 hgdi32
= GetModuleHandleA("gdi32.dll");
49 pGdiGetCharDimensions
= (void *)GetProcAddress(hgdi32
, "GdiGetCharDimensions");
50 pGetCharABCWidthsI
= (void *)GetProcAddress(hgdi32
, "GetCharABCWidthsI");
51 pGetCharABCWidthsW
= (void *)GetProcAddress(hgdi32
, "GetCharABCWidthsW");
52 pGetFontUnicodeRanges
= (void *)GetProcAddress(hgdi32
, "GetFontUnicodeRanges");
53 pGetGlyphIndicesA
= (void *)GetProcAddress(hgdi32
, "GetGlyphIndicesA");
54 pGetGlyphIndicesW
= (void *)GetProcAddress(hgdi32
, "GetGlyphIndicesW");
55 pGdiRealizationInfo
= (void *)GetProcAddress(hgdi32
, "GdiRealizationInfo");
58 static INT CALLBACK
is_truetype_font_installed_proc(const LOGFONT
*elf
, const TEXTMETRIC
*ntm
, DWORD type
, LPARAM lParam
)
60 if (type
!= TRUETYPE_FONTTYPE
) return 1;
65 static BOOL
is_truetype_font_installed(const char *name
)
70 if (!EnumFontFamiliesA(hdc
, name
, is_truetype_font_installed_proc
, 0))
77 static INT CALLBACK
is_font_installed_proc(const LOGFONT
*elf
, const TEXTMETRIC
*ntm
, DWORD type
, LPARAM lParam
)
82 static BOOL
is_font_installed(const char *name
)
87 if(!EnumFontFamiliesA(hdc
, name
, is_font_installed_proc
, 0))
94 static void check_font(const char* test
, const LOGFONTA
* lf
, HFONT hfont
)
102 ret
= GetObject(hfont
, sizeof(getobj_lf
), &getobj_lf
);
103 /* NT4 tries to be clever and only returns the minimum length */
104 while (lf
->lfFaceName
[minlen
] && minlen
< LF_FACESIZE
-1)
106 minlen
+= FIELD_OFFSET(LOGFONTA
, lfFaceName
) + 1;
107 ok(ret
== sizeof(LOGFONTA
) || ret
== minlen
, "%s: GetObject returned %d\n", test
, ret
);
108 ok(!memcmp(&lf
, &lf
, FIELD_OFFSET(LOGFONTA
, lfFaceName
)), "%s: fonts don't match\n", test
);
109 ok(!lstrcmpA(lf
->lfFaceName
, getobj_lf
.lfFaceName
),
110 "%s: font names don't match: %s != %s\n", test
, lf
->lfFaceName
, getobj_lf
.lfFaceName
);
113 static HFONT
create_font(const char* test
, const LOGFONTA
* lf
)
115 HFONT hfont
= CreateFontIndirectA(lf
);
116 ok(hfont
!= 0, "%s: CreateFontIndirect failed\n", test
);
118 check_font(test
, lf
, hfont
);
122 static void test_logfont(void)
127 memset(&lf
, 0, sizeof lf
);
129 lf
.lfCharSet
= ANSI_CHARSET
;
130 lf
.lfClipPrecision
= CLIP_DEFAULT_PRECIS
;
131 lf
.lfWeight
= FW_DONTCARE
;
134 lf
.lfQuality
= DEFAULT_QUALITY
;
136 lstrcpyA(lf
.lfFaceName
, "Arial");
137 hfont
= create_font("Arial", &lf
);
140 memset(&lf
, 'A', sizeof(lf
));
141 hfont
= CreateFontIndirectA(&lf
);
142 ok(hfont
!= 0, "CreateFontIndirectA with strange LOGFONT failed\n");
144 lf
.lfFaceName
[LF_FACESIZE
- 1] = 0;
145 check_font("AAA...", &lf
, hfont
);
149 static INT CALLBACK
font_enum_proc(const LOGFONT
*elf
, const TEXTMETRIC
*ntm
, DWORD type
, LPARAM lParam
)
151 if (type
& RASTER_FONTTYPE
)
153 LOGFONT
*lf
= (LOGFONT
*)lParam
;
155 return 0; /* stop enumeration */
158 return 1; /* continue enumeration */
161 static void test_font_metrics(HDC hdc
, HFONT hfont
, LONG lfHeight
,
162 LONG lfWidth
, const char *test_str
,
163 INT test_str_len
, const TEXTMETRICA
*tm_orig
,
164 const SIZE
*size_orig
, INT width_of_A_orig
,
165 INT scale_x
, INT scale_y
)
171 INT width_of_A
, cx
, cy
;
176 GetObjectA(hfont
, sizeof(lf
), &lf
);
178 old_hfont
= SelectObject(hdc
, hfont
);
180 GetTextMetricsA(hdc
, &tm
);
182 cx
= tm
.tmAveCharWidth
/ tm_orig
->tmAveCharWidth
;
183 cy
= tm
.tmHeight
/ tm_orig
->tmHeight
;
184 ok(cx
== scale_x
&& cy
== scale_y
, "expected scale_x %d, scale_y %d, got cx %d, cy %d\n",
185 scale_x
, scale_y
, cx
, cy
);
186 ok(tm
.tmHeight
== tm_orig
->tmHeight
* scale_y
, "%d != %d\n", tm
.tmHeight
, tm_orig
->tmHeight
* scale_y
);
187 ok(tm
.tmAscent
== tm_orig
->tmAscent
* scale_y
, "%d != %d\n", tm
.tmAscent
, tm_orig
->tmAscent
* scale_y
);
188 ok(tm
.tmDescent
== tm_orig
->tmDescent
* scale_y
, "%d != %d\n", tm
.tmDescent
, tm_orig
->tmDescent
* scale_y
);
189 ok(tm
.tmAveCharWidth
== tm_orig
->tmAveCharWidth
* scale_x
, "%d != %d\n", tm
.tmAveCharWidth
, tm_orig
->tmAveCharWidth
* scale_x
);
190 ok(tm
.tmMaxCharWidth
== tm_orig
->tmMaxCharWidth
* scale_x
, "%d != %d\n", tm
.tmAveCharWidth
, tm_orig
->tmMaxCharWidth
* scale_x
);
192 ok(lf
.lfHeight
== lfHeight
, "lf %d != %d\n", lf
.lfHeight
, lfHeight
);
196 ok(lf
.lfWidth
== tm
.tmAveCharWidth
, "lf %d != tm %d\n", lf
.lfWidth
, tm
.tmAveCharWidth
);
199 ok(lf
.lfWidth
== lfWidth
, "lf %d != %d\n", lf
.lfWidth
, lfWidth
);
201 GetTextExtentPoint32A(hdc
, test_str
, test_str_len
, &size
);
203 ok(size
.cx
== size_orig
->cx
* scale_x
, "%d != %d\n", size
.cx
, size_orig
->cx
* scale_x
);
204 ok(size
.cy
== size_orig
->cy
* scale_y
, "%d != %d\n", size
.cy
, size_orig
->cy
* scale_y
);
206 GetCharWidthA(hdc
, 'A', 'A', &width_of_A
);
208 ok(width_of_A
== width_of_A_orig
* scale_x
, "%d != %d\n", width_of_A
, width_of_A_orig
* scale_x
);
210 SelectObject(hdc
, old_hfont
);
213 /* Test how GDI scales bitmap font metrics */
214 static void test_bitmap_font(void)
216 static const char test_str
[11] = "Test String";
219 HFONT hfont
, old_hfont
;
222 INT ret
, i
, width_orig
, height_orig
, scale
, lfWidth
;
226 /* "System" has only 1 pixel size defined, otherwise the test breaks */
227 ret
= EnumFontFamiliesA(hdc
, "System", font_enum_proc
, (LPARAM
)&bitmap_lf
);
231 trace("no bitmap fonts were found, skipping the test\n");
235 trace("found bitmap font %s, height %d\n", bitmap_lf
.lfFaceName
, bitmap_lf
.lfHeight
);
237 height_orig
= bitmap_lf
.lfHeight
;
238 lfWidth
= bitmap_lf
.lfWidth
;
240 hfont
= create_font("bitmap", &bitmap_lf
);
241 old_hfont
= SelectObject(hdc
, hfont
);
242 ok(GetTextMetricsA(hdc
, &tm_orig
), "GetTextMetricsA failed\n");
243 ok(GetTextExtentPoint32A(hdc
, test_str
, sizeof(test_str
), &size_orig
), "GetTextExtentPoint32A failed\n");
244 ok(GetCharWidthA(hdc
, 'A', 'A', &width_orig
), "GetCharWidthA failed\n");
245 SelectObject(hdc
, old_hfont
);
248 bitmap_lf
.lfHeight
= 0;
249 bitmap_lf
.lfWidth
= 4;
250 hfont
= create_font("bitmap", &bitmap_lf
);
251 test_font_metrics(hdc
, hfont
, 0, 4, test_str
, sizeof(test_str
), &tm_orig
, &size_orig
, width_orig
, 1, 1);
254 bitmap_lf
.lfHeight
= height_orig
;
255 bitmap_lf
.lfWidth
= lfWidth
;
257 /* test fractional scaling */
258 for (i
= 1; i
<= height_orig
* 3; i
++)
262 bitmap_lf
.lfHeight
= i
;
263 hfont
= create_font("fractional", &bitmap_lf
);
264 scale
= (i
+ height_orig
- 1) / height_orig
;
265 nearest_height
= scale
* height_orig
;
266 /* XP allows not more than 10% deviation */
267 if (scale
> 1 && nearest_height
- i
> nearest_height
/ 10) scale
--;
268 test_font_metrics(hdc
, hfont
, bitmap_lf
.lfHeight
, 0, test_str
, sizeof(test_str
), &tm_orig
, &size_orig
, width_orig
, 1, scale
);
272 /* test integer scaling 3x2 */
273 bitmap_lf
.lfHeight
= height_orig
* 2;
274 bitmap_lf
.lfWidth
*= 3;
275 hfont
= create_font("3x2", &bitmap_lf
);
276 test_font_metrics(hdc
, hfont
, bitmap_lf
.lfHeight
, 0, test_str
, sizeof(test_str
), &tm_orig
, &size_orig
, width_orig
, 3, 2);
279 /* test integer scaling 3x3 */
280 bitmap_lf
.lfHeight
= height_orig
* 3;
281 bitmap_lf
.lfWidth
= 0;
282 hfont
= create_font("3x3", &bitmap_lf
);
283 test_font_metrics(hdc
, hfont
, bitmap_lf
.lfHeight
, 0, test_str
, sizeof(test_str
), &tm_orig
, &size_orig
, width_orig
, 3, 3);
289 static INT CALLBACK
find_font_proc(const LOGFONT
*elf
, const TEXTMETRIC
*ntm
, DWORD type
, LPARAM lParam
)
291 LOGFONT
*lf
= (LOGFONT
*)lParam
;
293 if (elf
->lfHeight
== lf
->lfHeight
&& !strcmp(elf
->lfFaceName
, lf
->lfFaceName
))
296 return 0; /* stop enumeration */
298 return 1; /* continue enumeration */
301 static void test_bitmap_font_metrics(void)
303 static const struct font_data
305 const char face_name
[LF_FACESIZE
];
306 int weight
, height
, ascent
, descent
, int_leading
, ext_leading
;
307 int ave_char_width
, max_char_width
;
311 { "MS Sans Serif", FW_NORMAL
, 13, 11, 2, 2, 0, 5, 11, FS_LATIN1
| FS_LATIN2
| FS_CYRILLIC
},
312 { "MS Sans Serif", FW_NORMAL
, 16, 13, 3, 3, 0, 7, 14, FS_LATIN1
| FS_LATIN2
| FS_CYRILLIC
},
313 { "MS Sans Serif", FW_NORMAL
, 20, 16, 4, 4, 0, 8, 16, FS_LATIN1
| FS_CYRILLIC
},
314 { "MS Sans Serif", FW_NORMAL
, 20, 16, 4, 4, 0, 8, 18, FS_LATIN2
},
315 { "MS Sans Serif", FW_NORMAL
, 24, 19, 5, 6, 0, 9, 19, FS_LATIN1
},
316 { "MS Sans Serif", FW_NORMAL
, 24, 19, 5, 6, 0, 9, 24, FS_LATIN2
},
317 { "MS Sans Serif", FW_NORMAL
, 24, 19, 5, 6, 0, 9, 20, FS_CYRILLIC
},
318 { "MS Sans Serif", FW_NORMAL
, 29, 23, 6, 5, 0, 12, 24, FS_LATIN1
},
319 { "MS Sans Serif", FW_NORMAL
, 29, 23, 6, 6, 0, 12, 24, FS_LATIN2
},
320 { "MS Sans Serif", FW_NORMAL
, 29, 23, 6, 5, 0, 12, 25, FS_CYRILLIC
},
321 { "MS Sans Serif", FW_NORMAL
, 37, 29, 8, 5, 0, 16, 32, FS_LATIN1
| FS_LATIN2
| FS_CYRILLIC
},
322 { "MS Serif", FW_NORMAL
, 10, 8, 2, 2, 0, 4, 8, FS_LATIN1
| FS_LATIN2
},
323 { "MS Serif", FW_NORMAL
, 10, 8, 2, 2, 0, 5, 8, FS_CYRILLIC
},
324 { "MS Serif", FW_NORMAL
, 11, 9, 2, 2, 0, 5, 9, FS_LATIN1
| FS_LATIN2
| FS_CYRILLIC
},
325 { "MS Serif", FW_NORMAL
, 13, 11, 2, 2, 0, 5, 11, FS_LATIN1
},
326 { "MS Serif", FW_NORMAL
, 13, 11, 2, 2, 0, 5, 12, FS_LATIN2
| FS_CYRILLIC
},
327 { "MS Serif", FW_NORMAL
, 16, 13, 3, 3, 0, 6, 14, FS_LATIN1
| FS_LATIN2
},
328 { "MS Serif", FW_NORMAL
, 16, 13, 3, 3, 0, 6, 16, FS_CYRILLIC
},
329 { "MS Serif", FW_NORMAL
, 19, 15, 4, 3, 0, 8, 18, FS_LATIN1
| FS_LATIN2
},
330 { "MS Serif", FW_NORMAL
, 19, 15, 4, 3, 0, 8, 19, FS_CYRILLIC
},
331 { "MS Serif", FW_NORMAL
, 21, 16, 5, 3, 0, 9, 17, FS_LATIN1
},
332 { "MS Serif", FW_NORMAL
, 21, 16, 5, 3, 0, 9, 22, FS_LATIN2
},
333 { "MS Serif", FW_NORMAL
, 21, 16, 5, 3, 0, 9, 23, FS_CYRILLIC
},
334 { "MS Serif", FW_NORMAL
, 27, 21, 6, 3, 0, 12, 23, FS_LATIN1
},
335 { "MS Serif", FW_NORMAL
, 27, 21, 6, 3, 0, 12, 26, FS_LATIN2
},
336 { "MS Serif", FW_NORMAL
, 27, 21, 6, 3, 0, 12, 27, FS_CYRILLIC
},
337 { "MS Serif", FW_NORMAL
, 35, 27, 8, 3, 0, 16, 33, FS_LATIN1
| FS_LATIN2
},
338 { "MS Serif", FW_NORMAL
, 35, 27, 8, 3, 0, 16, 34, FS_CYRILLIC
},
339 { "Courier", FW_NORMAL
, 13, 11, 2, 0, 0, 8, 8, FS_LATIN1
| FS_LATIN2
| FS_CYRILLIC
},
340 { "Courier", FW_NORMAL
, 16, 13, 3, 0, 0, 9, 9, FS_LATIN1
| FS_LATIN2
| FS_CYRILLIC
},
341 { "Courier", FW_NORMAL
, 20, 16, 4, 0, 0, 12, 12, FS_LATIN1
| FS_LATIN2
| FS_CYRILLIC
},
342 { "System", FW_BOLD
, 16, 13, 3, 3, 0, 7, 14, FS_LATIN1
},
343 { "System", FW_BOLD
, 16, 13, 3, 3, 0, 7, 15, FS_LATIN2
| FS_CYRILLIC
},
345 * TODO: the system for CP932 should be NORMAL, not BOLD. However that would
346 * require a new system.sfd for that font
348 { "System", FW_BOLD
, 18, 16, 2, 0, 2, 8, 16, FS_JISJAPAN
},
349 { "Small Fonts", FW_NORMAL
, 3, 2, 1, 0, 0, 1, 2, FS_LATIN1
},
350 { "Small Fonts", FW_NORMAL
, 3, 2, 1, 0, 0, 1, 8, FS_LATIN2
| FS_CYRILLIC
},
351 { "Small Fonts", FW_NORMAL
, 3, 2, 1, 0, 0, 2, 4, FS_JISJAPAN
},
352 { "Small Fonts", FW_NORMAL
, 5, 4, 1, 1, 0, 3, 4, FS_LATIN1
},
353 { "Small Fonts", FW_NORMAL
, 5, 4, 1, 1, 0, 2, 8, FS_LATIN2
| FS_CYRILLIC
},
354 { "Small Fonts", FW_NORMAL
, 5, 4, 1, 0, 0, 3, 6, FS_JISJAPAN
},
355 { "Small Fonts", FW_NORMAL
, 6, 5, 1, 1, 0, 3, 13, FS_LATIN1
},
356 { "Small Fonts", FW_NORMAL
, 6, 5, 1, 1, 0, 3, 8, FS_LATIN2
| FS_CYRILLIC
},
357 { "Small Fonts", FW_NORMAL
, 6, 5, 1, 0, 0, 4, 8, FS_JISJAPAN
},
358 { "Small Fonts", FW_NORMAL
, 8, 7, 1, 1, 0, 4, 7, FS_LATIN1
},
359 { "Small Fonts", FW_NORMAL
, 8, 7, 1, 1, 0, 4, 8, FS_LATIN2
| FS_CYRILLIC
},
360 { "Small Fonts", FW_NORMAL
, 8, 7, 1, 0, 0, 5, 10, FS_JISJAPAN
},
361 { "Small Fonts", FW_NORMAL
, 10, 8, 2, 2, 0, 4, 8, FS_LATIN1
| FS_LATIN2
},
362 { "Small Fonts", FW_NORMAL
, 10, 8, 2, 2, 0, 5, 8, FS_CYRILLIC
},
363 { "Small Fonts", FW_NORMAL
, 10, 8, 2, 0, 0, 6, 12, FS_JISJAPAN
},
364 { "Small Fonts", FW_NORMAL
, 11, 9, 2, 2, 0, 5, 9, FS_LATIN1
| FS_LATIN2
| FS_CYRILLIC
},
365 { "Small Fonts", FW_NORMAL
, 11, 9, 2, 0, 0, 7, 14, FS_JISJAPAN
},
366 { "Fixedsys", FW_NORMAL
, 15, 12, 3, 3, 0, 8, 8, FS_LATIN1
| FS_LATIN2
},
367 { "Fixedsys", FW_NORMAL
, 16, 12, 4, 3, 0, 8, 8, FS_CYRILLIC
},
368 { "FixedSys", FW_NORMAL
, 18, 16, 2, 0, 0, 8, 16, FS_JISJAPAN
}
370 /* FIXME: add "Terminal" */
374 HFONT hfont
, old_hfont
;
378 hdc
= CreateCompatibleDC(0);
381 for (i
= 0; i
< sizeof(fd
)/sizeof(fd
[0]); i
++)
385 memset(&lf
, 0, sizeof(lf
));
387 lf
.lfHeight
= fd
[i
].height
;
388 strcpy(lf
.lfFaceName
, fd
[i
].face_name
);
390 for(bit
= 0; bit
< 32; bit
++)
397 if((fd
[i
].ansi_bitfield
& fs
[0]) == 0) continue;
398 if(!TranslateCharsetInfo( fs
, &csi
, TCI_SRCFONTSIG
)) continue;
400 lf
.lfCharSet
= csi
.ciCharset
;
401 ret
= EnumFontFamiliesEx(hdc
, &lf
, find_font_proc
, (LPARAM
)&lf
, 0);
404 trace("found font %s, height %d charset %x\n", lf
.lfFaceName
, lf
.lfHeight
, lf
.lfCharSet
);
406 hfont
= create_font(lf
.lfFaceName
, &lf
);
407 old_hfont
= SelectObject(hdc
, hfont
);
408 ok(GetTextMetrics(hdc
, &tm
), "GetTextMetrics error %d\n", GetLastError());
410 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
);
411 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
);
412 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
);
413 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
);
414 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
);
415 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
);
416 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
);
418 /* Don't run the max char width test on System/ANSI_CHARSET. We have extra characters in our font
419 that make the max width bigger */
420 if(strcmp(lf
.lfFaceName
, "System") || lf
.lfCharSet
!= ANSI_CHARSET
)
421 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
);
423 SelectObject(hdc
, old_hfont
);
431 static void test_GdiGetCharDimensions(void)
437 LONG avgwidth
, height
;
438 static const char szAlphabet
[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
440 if (!pGdiGetCharDimensions
)
442 skip("GdiGetCharDimensions not available on this platform\n");
446 hdc
= CreateCompatibleDC(NULL
);
448 GetTextExtentPoint(hdc
, szAlphabet
, strlen(szAlphabet
), &size
);
449 avgwidth
= ((size
.cx
/ 26) + 1) / 2;
451 ret
= pGdiGetCharDimensions(hdc
, &tm
, &height
);
452 ok(ret
== avgwidth
, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth
, ret
);
453 ok(height
== tm
.tmHeight
, "GdiGetCharDimensions should have set height to %d instead of %d\n", tm
.tmHeight
, height
);
455 ret
= pGdiGetCharDimensions(hdc
, &tm
, NULL
);
456 ok(ret
== avgwidth
, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth
, ret
);
458 ret
= pGdiGetCharDimensions(hdc
, NULL
, NULL
);
459 ok(ret
== avgwidth
, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth
, ret
);
462 ret
= pGdiGetCharDimensions(hdc
, NULL
, &height
);
463 ok(ret
== avgwidth
, "GdiGetCharDimensions should have returned width of %d instead of %d\n", avgwidth
, ret
);
464 ok(height
== size
.cy
, "GdiGetCharDimensions should have set height to %d instead of %d\n", size
.cy
, height
);
469 static void test_GetCharABCWidths(void)
471 static const WCHAR str
[] = {'a',0};
480 if (!pGetCharABCWidthsW
|| !pGetCharABCWidthsI
)
482 skip("GetCharABCWidthsW/I not available on this platform\n");
486 memset(&lf
, 0, sizeof(lf
));
487 strcpy(lf
.lfFaceName
, "System");
490 hfont
= CreateFontIndirectA(&lf
);
492 hfont
= SelectObject(hdc
, hfont
);
494 nb
= pGetGlyphIndicesW(hdc
, str
, 1, glyphs
, 0);
495 ok(nb
== 1, "GetGlyphIndicesW should have returned 1\n");
497 ret
= pGetCharABCWidthsI(NULL
, 0, 1, glyphs
, abc
);
498 ok(!ret
, "GetCharABCWidthsI should have failed\n");
500 ret
= pGetCharABCWidthsI(hdc
, 0, 1, glyphs
, NULL
);
501 ok(!ret
, "GetCharABCWidthsI should have failed\n");
503 ret
= pGetCharABCWidthsI(hdc
, 0, 1, glyphs
, abc
);
504 ok(ret
, "GetCharABCWidthsI should have succeeded\n");
506 ret
= pGetCharABCWidthsW(NULL
, 'a', 'a', abc
);
507 ok(!ret
, "GetCharABCWidthsW should have failed\n");
509 ret
= pGetCharABCWidthsW(hdc
, 'a', 'a', NULL
);
510 ok(!ret
, "GetCharABCWidthsW should have failed\n");
512 ret
= pGetCharABCWidthsW(hdc
, 'a', 'a', abc
);
513 ok(!ret
, "GetCharABCWidthsW should have failed\n");
515 hfont
= SelectObject(hdc
, hfont
);
517 ReleaseDC(NULL
, hdc
);
520 static void test_text_extents(void)
522 static const WCHAR wt
[] = {'O','n','e','\n','t','w','o',' ','3',0};
524 INT i
, len
, fit1
, fit2
;
532 memset(&lf
, 0, sizeof(lf
));
533 strcpy(lf
.lfFaceName
, "Arial");
536 hfont
= CreateFontIndirectA(&lf
);
538 hfont
= SelectObject(hdc
, hfont
);
539 GetTextMetricsA(hdc
, &tm
);
540 GetTextExtentPointA(hdc
, "o", 1, &sz
);
541 ok(sz
.cy
== tm
.tmHeight
, "cy %d tmHeight %d\n", sz
.cy
, tm
.tmHeight
);
543 SetLastError(0xdeadbeef);
544 GetTextExtentExPointW(hdc
, wt
, 1, 1, &fit1
, &fit2
, &sz1
);
545 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED
)
547 skip("Skipping remainder of text extents test on a Win9x platform\n");
548 hfont
= SelectObject(hdc
, hfont
);
555 extents
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, len
* sizeof extents
[0]);
556 extents
[0] = 1; /* So that the increasing sequence test will fail
557 if the extents array is untouched. */
558 GetTextExtentExPointW(hdc
, wt
, len
, 32767, &fit1
, extents
, &sz1
);
559 GetTextExtentPointW(hdc
, wt
, len
, &sz2
);
561 "cy from GetTextExtentExPointW (%d) and GetTextExtentPointW (%d) differ\n", sz1
.cy
, sz2
.cy
);
562 /* Because of the '\n' in the string GetTextExtentExPoint and
563 GetTextExtentPoint return different widths under Win2k, but
564 under WinXP they return the same width. So we don't test that
567 for (i
= 1; i
< len
; ++i
)
568 ok(extents
[i
-1] <= extents
[i
],
569 "GetTextExtentExPointW generated a non-increasing sequence of partial extents (at position %d)\n",
571 ok(extents
[len
-1] == sz1
.cx
, "GetTextExtentExPointW extents and size don't match\n");
572 ok(0 <= fit1
&& fit1
<= len
, "GetTextExtentExPointW generated illegal value %d for fit\n", fit1
);
573 ok(0 < fit1
, "GetTextExtentExPointW says we can't even fit one letter in 32767 logical units\n");
574 GetTextExtentExPointW(hdc
, wt
, len
, extents
[2], &fit2
, NULL
, &sz2
);
575 ok(sz1
.cx
== sz2
.cx
&& sz1
.cy
== sz2
.cy
, "GetTextExtentExPointW returned different sizes for the same string\n");
576 ok(fit2
== 3, "GetTextExtentExPointW extents isn't consistent with fit\n");
577 GetTextExtentExPointW(hdc
, wt
, len
, extents
[2]-1, &fit2
, NULL
, &sz2
);
578 ok(fit2
== 2, "GetTextExtentExPointW extents isn't consistent with fit\n");
579 GetTextExtentExPointW(hdc
, wt
, 2, 0, NULL
, extents
+ 2, &sz2
);
580 ok(extents
[0] == extents
[2] && extents
[1] == extents
[3],
581 "GetTextExtentExPointW with lpnFit == NULL returns incorrect results\n");
582 GetTextExtentExPointW(hdc
, wt
, 2, 0, NULL
, NULL
, &sz1
);
583 ok(sz1
.cx
== sz2
.cx
&& sz1
.cy
== sz2
.cy
,
584 "GetTextExtentExPointW with lpnFit and alpDx both NULL returns incorrect results\n");
585 HeapFree(GetProcessHeap(), 0, extents
);
587 hfont
= SelectObject(hdc
, hfont
);
589 ReleaseDC(NULL
, hdc
);
592 static void test_GetGlyphIndices(void)
599 WCHAR testtext
[] = {'T','e','s','t',0xffff,0};
600 WORD glyphs
[(sizeof(testtext
)/2)-1];
604 if (!pGetGlyphIndicesW
) {
605 skip("GetGlyphIndicesW not available on platform\n");
611 ok(GetTextMetrics(hdc
, &textm
), "GetTextMetric failed\n");
612 flags
|= GGI_MARK_NONEXISTING_GLYPHS
;
613 charcount
= pGetGlyphIndicesW(hdc
, testtext
, (sizeof(testtext
)/2)-1, glyphs
, flags
);
614 ok(charcount
== 5, "GetGlyphIndicesW count of glyphs should = 5 not %d\n", charcount
);
615 ok((glyphs
[4] == 0x001f || glyphs
[4] == 0xffff /* Vista */), "GetGlyphIndicesW should have returned a nonexistent char not %04x\n", glyphs
[4]);
617 charcount
= pGetGlyphIndicesW(hdc
, testtext
, (sizeof(testtext
)/2)-1, glyphs
, flags
);
618 ok(charcount
== 5, "GetGlyphIndicesW count of glyphs should = 5 not %d\n", charcount
);
619 ok(glyphs
[4] == textm
.tmDefaultChar
, "GetGlyphIndicesW should have returned a %04x not %04x\n",
620 textm
.tmDefaultChar
, glyphs
[4]);
622 if(!is_font_installed("Tahoma"))
624 skip("Tahoma is not installed so skipping this test\n");
627 memset(&lf
, 0, sizeof(lf
));
628 strcpy(lf
.lfFaceName
, "Tahoma");
631 hfont
= CreateFontIndirectA(&lf
);
632 hOldFont
= SelectObject(hdc
, hfont
);
633 ok(GetTextMetrics(hdc
, &textm
), "GetTextMetric failed\n");
634 flags
|= GGI_MARK_NONEXISTING_GLYPHS
;
635 charcount
= pGetGlyphIndicesW(hdc
, testtext
, (sizeof(testtext
)/2)-1, glyphs
, flags
);
636 ok(charcount
== 5, "GetGlyphIndicesW count of glyphs should = 5 not %d\n", charcount
);
637 ok(glyphs
[4] == 0xffff, "GetGlyphIndicesW should have returned 0xffff char not %04x\n", glyphs
[4]);
639 testtext
[0] = textm
.tmDefaultChar
;
640 charcount
= pGetGlyphIndicesW(hdc
, testtext
, (sizeof(testtext
)/2)-1, glyphs
, flags
);
641 ok(charcount
== 5, "GetGlyphIndicesW count of glyphs should = 5 not %d\n", charcount
);
642 todo_wine
ok(glyphs
[0] == 0, "GetGlyphIndicesW for tmDefaultChar should be 0 not %04x\n", glyphs
[0]);
643 ok(glyphs
[4] == 0, "GetGlyphIndicesW should have returned 0 not %04x\n", glyphs
[4]);
644 DeleteObject(SelectObject(hdc
, hOldFont
));
647 static void test_GetKerningPairs(void)
649 static const struct kerning_data
651 const char face_name
[LF_FACESIZE
];
653 /* some interesting fields from OUTLINETEXTMETRIC */
654 LONG tmHeight
, tmAscent
, tmDescent
;
659 UINT otmsCapEmHeight
;
664 UINT otmusMinimumPPEM
;
665 /* small subset of kerning pairs to test */
666 DWORD total_kern_pairs
;
667 const KERNINGPAIR kern_pair
[26];
670 {"Arial", 12, 12, 9, 3,
671 2048, 7, -2, 1, 5, 2, 8, -2, 0, 9,
674 {' ','A',-1},{' ','T',0},{' ','Y',0},{'1','1',-1},
675 {'A',' ',-1},{'A','T',-1},{'A','V',-1},{'A','W',0},
676 {'A','Y',-1},{'A','v',0},{'A','w',0},{'A','y',0},
677 {'F',',',-1},{'F','.',-1},{'F','A',-1},{'L',' ',0},
678 {'L','T',-1},{'L','V',-1},{'L','W',-1},{'L','Y',-1},
679 {915,912,+1},{915,913,-1},{910,912,+1},{910,913,-1},
680 {933,970,+1},{933,972,-1}
683 {"Arial", -34, 39, 32, 7,
684 2048, 25, -7, 5, 17, 9, 31, -7, 1, 9,
687 {' ','A',-2},{' ','T',-1},{' ','Y',-1},{'1','1',-3},
688 {'A',' ',-2},{'A','T',-3},{'A','V',-3},{'A','W',-1},
689 {'A','Y',-3},{'A','v',-1},{'A','w',-1},{'A','y',-1},
690 {'F',',',-4},{'F','.',-4},{'F','A',-2},{'L',' ',-1},
691 {'L','T',-3},{'L','V',-3},{'L','W',-3},{'L','Y',-3},
692 {915,912,+3},{915,913,-3},{910,912,+3},{910,913,-3},
693 {933,970,+2},{933,972,-3}
696 { "Arial", 120, 120, 97, 23,
697 2048, 79, -23, 16, 54, 27, 98, -23, 4, 9,
700 {' ','A',-6},{' ','T',-2},{' ','Y',-2},{'1','1',-8},
701 {'A',' ',-6},{'A','T',-8},{'A','V',-8},{'A','W',-4},
702 {'A','Y',-8},{'A','v',-2},{'A','w',-2},{'A','y',-2},
703 {'F',',',-12},{'F','.',-12},{'F','A',-6},{'L',' ',-4},
704 {'L','T',-8},{'L','V',-8},{'L','W',-8},{'L','Y',-8},
705 {915,912,+9},{915,913,-10},{910,912,+9},{910,913,-8},
706 {933,970,+6},{933,972,-10}
709 #if 0 /* this set fails due to +1/-1 errors (rounding bug?), needs investigation. */
710 { "Arial", 1024 /* usually 1/2 of EM Square */, 1024, 830, 194,
711 2048, 668, -193, 137, 459, 229, 830, -194, 30, 9,
714 {' ','A',-51},{' ','T',-17},{' ','Y',-17},{'1','1',-68},
715 {'A',' ',-51},{'A','T',-68},{'A','V',-68},{'A','W',-34},
716 {'A','Y',-68},{'A','v',-17},{'A','w',-17},{'A','y',-17},
717 {'F',',',-102},{'F','.',-102},{'F','A',-51},{'L',' ',-34},
718 {'L','T',-68},{'L','V',-68},{'L','W',-68},{'L','Y',-68},
719 {915,912,+73},{915,913,-84},{910,912,+76},{910,913,-68},
720 {933,970,+54},{933,972,-83}
726 HFONT hfont
, hfont_old
;
727 KERNINGPAIR
*kern_pair
;
729 DWORD total_kern_pairs
, ret
, i
, n
, matches
;
733 /* GetKerningPairsA maps unicode set of kerning pairs to current code page
734 * which may render this test unusable, so we're trying to avoid that.
736 SetLastError(0xdeadbeef);
737 GetKerningPairsW(hdc
, 0, NULL
);
738 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED
)
740 skip("Skipping the GetKerningPairs test on a Win9x platform\n");
745 for (i
= 0; i
< sizeof(kd
)/sizeof(kd
[0]); i
++)
747 OUTLINETEXTMETRICW otm
;
749 if (!is_font_installed(kd
[i
].face_name
))
751 trace("%s is not installed so skipping this test\n", kd
[i
].face_name
);
755 trace("testing font %s, height %d\n", kd
[i
].face_name
, kd
[i
].height
);
757 memset(&lf
, 0, sizeof(lf
));
758 strcpy(lf
.lfFaceName
, kd
[i
].face_name
);
759 lf
.lfHeight
= kd
[i
].height
;
760 hfont
= CreateFontIndirect(&lf
);
763 hfont_old
= SelectObject(hdc
, hfont
);
765 SetLastError(0xdeadbeef);
766 otm
.otmSize
= sizeof(otm
); /* just in case for Win9x compatibility */
767 ok(GetOutlineTextMetricsW(hdc
, sizeof(otm
), &otm
) == sizeof(otm
), "GetOutlineTextMetricsW error %d\n", GetLastError());
769 ok(kd
[i
].tmHeight
== otm
.otmTextMetrics
.tmHeight
, "expected %d, got %d\n",
770 kd
[i
].tmHeight
, otm
.otmTextMetrics
.tmHeight
);
771 ok(kd
[i
].tmAscent
== otm
.otmTextMetrics
.tmAscent
, "expected %d, got %d\n",
772 kd
[i
].tmAscent
, otm
.otmTextMetrics
.tmAscent
);
773 ok(kd
[i
].tmDescent
== otm
.otmTextMetrics
.tmDescent
, "expected %d, got %d\n",
774 kd
[i
].tmDescent
, otm
.otmTextMetrics
.tmDescent
);
776 ok(kd
[i
].otmEMSquare
== otm
.otmEMSquare
, "expected %u, got %u\n",
777 kd
[i
].otmEMSquare
, otm
.otmEMSquare
);
778 ok(kd
[i
].otmAscent
== otm
.otmAscent
, "expected %d, got %d\n",
779 kd
[i
].otmAscent
, otm
.otmAscent
);
780 ok(kd
[i
].otmDescent
== otm
.otmDescent
, "expected %d, got %d\n",
781 kd
[i
].otmDescent
, otm
.otmDescent
);
782 ok(kd
[i
].otmLineGap
== otm
.otmLineGap
, "expected %u, got %u\n",
783 kd
[i
].otmLineGap
, otm
.otmLineGap
);
785 ok(kd
[i
].otmsCapEmHeight
== otm
.otmsCapEmHeight
, "expected %u, got %u\n",
786 kd
[i
].otmsCapEmHeight
, otm
.otmsCapEmHeight
);
787 ok(kd
[i
].otmsXHeight
== otm
.otmsXHeight
, "expected %u, got %u\n",
788 kd
[i
].otmsXHeight
, otm
.otmsXHeight
);
789 ok(kd
[i
].otmMacAscent
== otm
.otmMacAscent
, "expected %d, got %d\n",
790 kd
[i
].otmMacAscent
, otm
.otmMacAscent
);
791 ok(kd
[i
].otmMacDescent
== otm
.otmMacDescent
, "expected %d, got %d\n",
792 kd
[i
].otmMacDescent
, otm
.otmMacDescent
);
793 /* FIXME: this one sometimes succeeds due to expected 0, enable it when removing todo */
794 if (0) ok(kd
[i
].otmMacLineGap
== otm
.otmMacLineGap
, "expected %u, got %u\n",
795 kd
[i
].otmMacLineGap
, otm
.otmMacLineGap
);
796 ok(kd
[i
].otmusMinimumPPEM
== otm
.otmusMinimumPPEM
, "expected %u, got %u\n",
797 kd
[i
].otmusMinimumPPEM
, otm
.otmusMinimumPPEM
);
800 total_kern_pairs
= GetKerningPairsW(hdc
, 0, NULL
);
801 trace("total_kern_pairs %u\n", total_kern_pairs
);
802 kern_pair
= HeapAlloc(GetProcessHeap(), 0, total_kern_pairs
* sizeof(*kern_pair
));
804 #if 0 /* Win98 (GetKerningPairsA) and XP behave differently here, the test passes on XP */
805 SetLastError(0xdeadbeef);
806 ret
= GetKerningPairsW(hdc
, 0, kern_pair
);
807 ok(GetLastError() == ERROR_INVALID_PARAMETER
,
808 "got error %ld, expected ERROR_INVALID_PARAMETER\n", GetLastError());
809 ok(ret
== 0, "got %lu, expected 0\n", ret
);
812 ret
= GetKerningPairsW(hdc
, 100, NULL
);
813 ok(ret
== total_kern_pairs
, "got %u, expected %u\n", ret
, total_kern_pairs
);
815 ret
= GetKerningPairsW(hdc
, total_kern_pairs
/2, kern_pair
);
816 ok(ret
== total_kern_pairs
/2, "got %u, expected %u\n", ret
, total_kern_pairs
/2);
818 ret
= GetKerningPairsW(hdc
, total_kern_pairs
, kern_pair
);
819 ok(ret
== total_kern_pairs
, "got %u, expected %u\n", ret
, total_kern_pairs
);
823 for (n
= 0; n
< ret
; n
++)
827 if (kern_pair
[n
].wFirst
< 127 && kern_pair
[n
].wSecond
< 127)
828 trace("{'%c','%c',%d},\n",
829 kern_pair
[n
].wFirst
, kern_pair
[n
].wSecond
, kern_pair
[n
].iKernAmount
);
831 for (j
= 0; j
< kd
[i
].total_kern_pairs
; j
++)
833 if (kern_pair
[n
].wFirst
== kd
[i
].kern_pair
[j
].wFirst
&&
834 kern_pair
[n
].wSecond
== kd
[i
].kern_pair
[j
].wSecond
)
836 ok(kern_pair
[n
].iKernAmount
== kd
[i
].kern_pair
[j
].iKernAmount
,
837 "pair %d:%d got %d, expected %d\n",
838 kern_pair
[n
].wFirst
, kern_pair
[n
].wSecond
,
839 kern_pair
[n
].iKernAmount
, kd
[i
].kern_pair
[j
].iKernAmount
);
845 ok(matches
== kd
[i
].total_kern_pairs
, "got matches %u, expected %u\n",
846 matches
, kd
[i
].total_kern_pairs
);
848 HeapFree(GetProcessHeap(), 0, kern_pair
);
850 SelectObject(hdc
, hfont_old
);
857 static void test_GetOutlineTextMetrics(void)
859 OUTLINETEXTMETRIC
*otm
;
861 HFONT hfont
, hfont_old
;
865 if (!is_font_installed("Arial"))
867 skip("Arial is not installed\n");
873 memset(&lf
, 0, sizeof(lf
));
874 strcpy(lf
.lfFaceName
, "Arial");
876 lf
.lfWeight
= FW_NORMAL
;
877 lf
.lfPitchAndFamily
= DEFAULT_PITCH
;
878 lf
.lfQuality
= PROOF_QUALITY
;
879 hfont
= CreateFontIndirect(&lf
);
882 hfont_old
= SelectObject(hdc
, hfont
);
883 otm_size
= GetOutlineTextMetrics(hdc
, 0, NULL
);
884 trace("otm buffer size %u (0x%x)\n", otm_size
, otm_size
);
886 otm
= HeapAlloc(GetProcessHeap(), 0, otm_size
);
888 memset(otm
, 0xAA, otm_size
);
889 SetLastError(0xdeadbeef);
890 otm
->otmSize
= sizeof(*otm
); /* just in case for Win9x compatibility */
891 ret
= GetOutlineTextMetrics(hdc
, otm
->otmSize
, otm
);
892 ok(ret
== 1 /* Win9x */ ||
893 ret
== otm
->otmSize
/* XP*/,
894 "expected %u, got %u, error %d\n", otm
->otmSize
, ret
, GetLastError());
895 if (ret
!= 1) /* Win9x doesn't care about pointing beyond of the buffer */
897 ok(otm
->otmpFamilyName
== NULL
, "expected NULL got %p\n", otm
->otmpFamilyName
);
898 ok(otm
->otmpFaceName
== NULL
, "expected NULL got %p\n", otm
->otmpFaceName
);
899 ok(otm
->otmpStyleName
== NULL
, "expected NULL got %p\n", otm
->otmpStyleName
);
900 ok(otm
->otmpFullName
== NULL
, "expected NULL got %p\n", otm
->otmpFullName
);
903 memset(otm
, 0xAA, otm_size
);
904 SetLastError(0xdeadbeef);
905 otm
->otmSize
= otm_size
; /* just in case for Win9x compatibility */
906 ret
= GetOutlineTextMetrics(hdc
, otm
->otmSize
, otm
);
907 ok(ret
== 1 /* Win9x */ ||
908 ret
== otm
->otmSize
/* XP*/,
909 "expected %u, got %u, error %d\n", otm
->otmSize
, ret
, GetLastError());
910 if (ret
!= 1) /* Win9x doesn't care about pointing beyond of the buffer */
912 ok(otm
->otmpFamilyName
!= NULL
, "expected not NULL got %p\n", otm
->otmpFamilyName
);
913 ok(otm
->otmpFaceName
!= NULL
, "expected not NULL got %p\n", otm
->otmpFaceName
);
914 ok(otm
->otmpStyleName
!= NULL
, "expected not NULL got %p\n", otm
->otmpStyleName
);
915 ok(otm
->otmpFullName
!= NULL
, "expected not NULL got %p\n", otm
->otmpFullName
);
918 /* ask about truncated data */
919 memset(otm
, 0xAA, otm_size
);
920 SetLastError(0xdeadbeef);
921 otm
->otmSize
= sizeof(*otm
) - sizeof(LPSTR
); /* just in case for Win9x compatibility */
922 ret
= GetOutlineTextMetrics(hdc
, otm
->otmSize
, otm
);
923 ok(ret
== 1 /* Win9x */ ||
924 ret
== otm
->otmSize
/* XP*/,
925 "expected %u, got %u, error %d\n", otm
->otmSize
, ret
, GetLastError());
926 if (ret
!= 1) /* Win9x doesn't care about pointing beyond of the buffer */
928 ok(otm
->otmpFamilyName
== NULL
, "expected NULL got %p\n", otm
->otmpFamilyName
);
929 ok(otm
->otmpFaceName
== NULL
, "expected NULL got %p\n", otm
->otmpFaceName
);
930 ok(otm
->otmpStyleName
== NULL
, "expected NULL got %p\n", otm
->otmpStyleName
);
932 ok(otm
->otmpFullName
== (LPSTR
)0xAAAAAAAA, "expected 0xAAAAAAAA got %p\n", otm
->otmpFullName
);
934 HeapFree(GetProcessHeap(), 0, otm
);
936 SelectObject(hdc
, hfont_old
);
942 static void testJustification(HDC hdc
, PSTR str
, RECT
*clientArea
)
946 outputWidth
= 0, /* to test TabbedTextOut() */
947 justifiedWidth
= 0, /* to test GetTextExtentExPointW() */
948 areaWidth
= clientArea
->right
- clientArea
->left
,
950 BOOL lastExtent
= FALSE
;
951 PSTR pFirstChar
, pLastChar
;
957 int GetTextExtentExPointWWidth
;
958 int TabbedTextOutWidth
;
961 GetTextMetricsA(hdc
, &tm
);
965 while (*str
== tm
.tmBreakChar
) str
++; /* skip leading break chars */
971 /* if not at the end of the string, ... */
972 if (*str
== '\0') break;
973 /* ... add the next word to the current extent */
974 while (*str
!= '\0' && *str
++ != tm
.tmBreakChar
);
976 SetTextJustification(hdc
, 0, 0);
977 GetTextExtentPoint32(hdc
, pFirstChar
, str
- pFirstChar
- 1, &size
);
978 } while ((int) size
.cx
< areaWidth
);
980 /* ignore trailing break chars */
982 while (*(pLastChar
- 1) == tm
.tmBreakChar
)
988 if (*str
== '\0' || breakCount
<= 0) pLastChar
= str
;
990 SetTextJustification(hdc
, 0, 0);
991 GetTextExtentPoint32(hdc
, pFirstChar
, pLastChar
- pFirstChar
, &size
);
993 /* do not justify the last extent */
994 if (*str
!= '\0' && breakCount
> 0)
996 SetTextJustification(hdc
, areaWidth
- size
.cx
, breakCount
);
997 GetTextExtentPoint32(hdc
, pFirstChar
, pLastChar
- pFirstChar
, &size
);
998 justifiedWidth
= size
.cx
;
1000 else lastExtent
= TRUE
;
1002 x
= clientArea
->left
;
1004 outputWidth
= LOWORD(TabbedTextOut(
1005 hdc
, x
, y
, pFirstChar
, pLastChar
- pFirstChar
,
1007 /* catch errors and report them */
1008 if (!lastExtent
&& ((outputWidth
!= areaWidth
) || (justifiedWidth
!= areaWidth
)))
1010 memset(error
[nErrors
].extent
, 0, 100);
1011 memcpy(error
[nErrors
].extent
, pFirstChar
, pLastChar
- pFirstChar
);
1012 error
[nErrors
].TabbedTextOutWidth
= outputWidth
;
1013 error
[nErrors
].GetTextExtentExPointWWidth
= justifiedWidth
;
1019 } while (*str
&& y
< clientArea
->bottom
);
1021 for (e
= 0; e
< nErrors
; e
++)
1023 ok(error
[e
].TabbedTextOutWidth
== areaWidth
,
1024 "The output text (\"%s\") width should be %d, not %d.\n",
1025 error
[e
].extent
, areaWidth
, error
[e
].TabbedTextOutWidth
);
1026 /* The width returned by GetTextExtentPoint32() is exactly the same
1027 returned by GetTextExtentExPointW() - see dlls/gdi32/font.c */
1028 ok(error
[e
].GetTextExtentExPointWWidth
== areaWidth
,
1029 "GetTextExtentPointW() for \"%s\" should have returned a width of %d, not %d.\n",
1030 error
[e
].extent
, areaWidth
, error
[e
].GetTextExtentExPointWWidth
);
1034 static void test_SetTextJustification(void)
1041 static char testText
[] =
1042 "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do "
1043 "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut "
1044 "enim ad minim veniam, quis nostrud exercitation ullamco laboris "
1045 "nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in "
1046 "reprehenderit in voluptate velit esse cillum dolore eu fugiat "
1047 "nulla pariatur. Excepteur sint occaecat cupidatat non proident, "
1048 "sunt in culpa qui officia deserunt mollit anim id est laborum.";
1050 hwnd
= CreateWindowExA(0, "static", "", WS_POPUP
, 0,0, 400,400, 0, 0, 0, NULL
);
1051 GetClientRect( hwnd
, &clientArea
);
1052 hdc
= GetDC( hwnd
);
1054 memset(&lf
, 0, sizeof lf
);
1055 lf
.lfCharSet
= ANSI_CHARSET
;
1056 lf
.lfClipPrecision
= CLIP_DEFAULT_PRECIS
;
1057 lf
.lfWeight
= FW_DONTCARE
;
1059 lf
.lfQuality
= DEFAULT_QUALITY
;
1060 lstrcpyA(lf
.lfFaceName
, "Times New Roman");
1061 hfont
= create_font("Times New Roman", &lf
);
1062 SelectObject(hdc
, hfont
);
1064 testJustification(hdc
, testText
, &clientArea
);
1066 DeleteObject(hfont
);
1067 ReleaseDC(hwnd
, hdc
);
1068 DestroyWindow(hwnd
);
1071 static BOOL
get_glyph_indices(INT charset
, UINT code_page
, WORD
*idx
, UINT count
, BOOL unicode
)
1075 HFONT hfont
, hfont_old
;
1082 assert(count
<= 128);
1084 memset(&lf
, 0, sizeof(lf
));
1086 lf
.lfCharSet
= charset
;
1088 lstrcpyA(lf
.lfFaceName
, "Arial");
1089 SetLastError(0xdeadbeef);
1090 hfont
= CreateFontIndirectA(&lf
);
1091 ok(hfont
!= 0, "CreateFontIndirectA error %u\n", GetLastError());
1094 hfont_old
= SelectObject(hdc
, hfont
);
1096 cs
= GetTextCharsetInfo(hdc
, &fs
, 0);
1097 ok(cs
== charset
, "expected %d, got %d\n", charset
, cs
);
1099 SetLastError(0xdeadbeef);
1100 ret
= GetTextFace(hdc
, sizeof(name
), name
);
1101 ok(ret
, "GetTextFace error %u\n", GetLastError());
1103 if (charset
== SYMBOL_CHARSET
)
1105 ok(strcmp("Arial", name
), "face name should NOT be Arial\n");
1106 ok(fs
.fsCsb
[0] & (1 << 31), "symbol encoding should be available\n");
1110 ok(!strcmp("Arial", name
), "face name should be Arial, not %s\n", name
);
1111 ok(!(fs
.fsCsb
[0] & (1 << 31)), "symbol encoding should NOT be available\n");
1114 if (!TranslateCharsetInfo((DWORD
*)cs
, &csi
, TCI_SRCCHARSET
))
1116 trace("Can't find codepage for charset %d\n", cs
);
1120 ok(csi
.ciACP
== code_page
, "expected %d, got %d\n", code_page
, csi
.ciACP
);
1125 WCHAR unicode_buf
[128];
1127 for (i
= 0; i
< count
; i
++) ansi_buf
[i
] = (BYTE
)(i
+ 128);
1129 MultiByteToWideChar(code_page
, 0, ansi_buf
, count
, unicode_buf
, count
);
1131 SetLastError(0xdeadbeef);
1132 ret
= pGetGlyphIndicesW(hdc
, unicode_buf
, count
, idx
, 0);
1133 ok(ret
== count
, "GetGlyphIndicesW error %u\n", GetLastError());
1139 for (i
= 0; i
< count
; i
++) ansi_buf
[i
] = (BYTE
)(i
+ 128);
1141 SetLastError(0xdeadbeef);
1142 ret
= pGetGlyphIndicesA(hdc
, ansi_buf
, count
, idx
, 0);
1143 ok(ret
== count
, "GetGlyphIndicesA error %u\n", GetLastError());
1146 SelectObject(hdc
, hfont_old
);
1147 DeleteObject(hfont
);
1154 static void test_font_charset(void)
1156 static struct charset_data
1160 WORD font_idxA
[128], font_idxW
[128];
1163 { ANSI_CHARSET
, 1252 },
1164 { RUSSIAN_CHARSET
, 1251 },
1165 { SYMBOL_CHARSET
, CP_SYMBOL
} /* keep it as the last one */
1169 if (!pGetGlyphIndicesA
|| !pGetGlyphIndicesW
)
1171 skip("Skipping the font charset test on a Win9x platform\n");
1175 if (!is_font_installed("Arial"))
1177 skip("Arial is not installed\n");
1181 for (i
= 0; i
< sizeof(cd
)/sizeof(cd
[0]); i
++)
1183 if (cd
[i
].charset
== SYMBOL_CHARSET
)
1185 if (!is_font_installed("Symbol") && !is_font_installed("Wingdings"))
1187 skip("Symbol or Wingdings is not installed\n");
1191 get_glyph_indices(cd
[i
].charset
, cd
[i
].code_page
, cd
[i
].font_idxA
, 128, FALSE
);
1192 get_glyph_indices(cd
[i
].charset
, cd
[i
].code_page
, cd
[i
].font_idxW
, 128, TRUE
);
1193 ok(!memcmp(cd
[i
].font_idxA
, cd
[i
].font_idxW
, 128*sizeof(WORD
)), "%d: indices don't match\n", i
);
1196 ok(memcmp(cd
[0].font_idxW
, cd
[1].font_idxW
, 128*sizeof(WORD
)), "0 vs 1: indices shouldn't match\n");
1199 ok(memcmp(cd
[0].font_idxW
, cd
[2].font_idxW
, 128*sizeof(WORD
)), "0 vs 2: indices shouldn't match\n");
1200 ok(memcmp(cd
[1].font_idxW
, cd
[2].font_idxW
, 128*sizeof(WORD
)), "1 vs 2: indices shouldn't match\n");
1203 skip("Symbol or Wingdings is not installed\n");
1206 static void test_GetFontUnicodeRanges(void)
1210 HFONT hfont
, hfont_old
;
1214 if (!pGetFontUnicodeRanges
)
1216 skip("GetFontUnicodeRanges not available before W2K\n");
1220 memset(&lf
, 0, sizeof(lf
));
1221 lstrcpyA(lf
.lfFaceName
, "Arial");
1222 hfont
= create_font("Arial", &lf
);
1225 hfont_old
= SelectObject(hdc
, hfont
);
1227 size
= pGetFontUnicodeRanges(NULL
, NULL
);
1228 ok(!size
, "GetFontUnicodeRanges succeeded unexpectedly\n");
1230 size
= pGetFontUnicodeRanges(hdc
, NULL
);
1231 ok(size
, "GetFontUnicodeRanges failed unexpectedly\n");
1233 gs
= HeapAlloc(GetProcessHeap(), 0, size
);
1235 size
= pGetFontUnicodeRanges(hdc
, gs
);
1236 ok(size
, "GetFontUnicodeRanges failed\n");
1238 for (i
= 0; i
< gs
->cRanges
; i
++)
1239 trace("%03d wcLow %04x cGlyphs %u\n", i
, gs
->ranges
[i
].wcLow
, gs
->ranges
[i
].cGlyphs
);
1241 trace("found %u ranges\n", gs
->cRanges
);
1243 HeapFree(GetProcessHeap(), 0, gs
);
1245 SelectObject(hdc
, hfont_old
);
1246 DeleteObject(hfont
);
1247 ReleaseDC(NULL
, hdc
);
1250 #define MAX_ENUM_FONTS 256
1252 struct enum_font_data
1255 LOGFONT lf
[MAX_ENUM_FONTS
];
1258 static INT CALLBACK
arial_enum_proc(const LOGFONT
*lf
, const TEXTMETRIC
*tm
, DWORD type
, LPARAM lParam
)
1260 struct enum_font_data
*efd
= (struct enum_font_data
*)lParam
;
1262 if (type
!= TRUETYPE_FONTTYPE
) return 1;
1264 trace("enumed font \"%s\", charset %d, weight %d, italic %d\n",
1265 lf
->lfFaceName
, lf
->lfCharSet
, lf
->lfWeight
, lf
->lfItalic
);
1267 if (efd
->total
< MAX_ENUM_FONTS
)
1268 efd
->lf
[efd
->total
++] = *lf
;
1273 static void get_charset_stats(struct enum_font_data
*efd
,
1274 int *ansi_charset
, int *symbol_charset
,
1275 int *russian_charset
)
1280 *symbol_charset
= 0;
1281 *russian_charset
= 0;
1283 for (i
= 0; i
< efd
->total
; i
++)
1285 switch (efd
->lf
[i
].lfCharSet
)
1290 case SYMBOL_CHARSET
:
1291 (*symbol_charset
)++;
1293 case RUSSIAN_CHARSET
:
1294 (*russian_charset
)++;
1300 static void test_EnumFontFamilies(const char *font_name
, INT font_charset
)
1302 struct enum_font_data efd
;
1305 int i
, ret
, ansi_charset
, symbol_charset
, russian_charset
;
1307 trace("Testing font %s, charset %d\n", *font_name
? font_name
: "<empty>", font_charset
);
1309 if (*font_name
&& !is_truetype_font_installed(font_name
))
1311 skip("%s is not installed\n", font_name
);
1317 /* Observed behaviour: EnumFontFamilies enumerates aliases like "Arial Cyr"
1318 * while EnumFontFamiliesEx doesn't.
1320 if (!*font_name
&& font_charset
== DEFAULT_CHARSET
) /* do it only once */
1323 SetLastError(0xdeadbeef);
1324 ret
= EnumFontFamilies(hdc
, NULL
, arial_enum_proc
, (LPARAM
)&efd
);
1325 ok(ret
, "EnumFontFamilies error %u\n", GetLastError());
1326 get_charset_stats(&efd
, &ansi_charset
, &symbol_charset
, &russian_charset
);
1327 trace("enumerated ansi %d, symbol %d, russian %d fonts for NULL\n",
1328 ansi_charset
, symbol_charset
, russian_charset
);
1329 ok(efd
.total
> 0, "no fonts enumerated: NULL\n");
1330 ok(ansi_charset
> 0, "NULL family should enumerate ANSI_CHARSET\n");
1331 ok(symbol_charset
> 0, "NULL family should enumerate SYMBOL_CHARSET\n");
1332 ok(russian_charset
> 0, "NULL family should enumerate RUSSIAN_CHARSET\n");
1335 SetLastError(0xdeadbeef);
1336 ret
= EnumFontFamiliesEx(hdc
, NULL
, arial_enum_proc
, (LPARAM
)&efd
, 0);
1337 ok(ret
, "EnumFontFamiliesEx error %u\n", GetLastError());
1338 get_charset_stats(&efd
, &ansi_charset
, &symbol_charset
, &russian_charset
);
1339 trace("enumerated ansi %d, symbol %d, russian %d fonts for NULL\n",
1340 ansi_charset
, symbol_charset
, russian_charset
);
1341 ok(efd
.total
> 0, "no fonts enumerated: NULL\n");
1342 ok(ansi_charset
> 0, "NULL family should enumerate ANSI_CHARSET\n");
1343 ok(symbol_charset
> 0, "NULL family should enumerate SYMBOL_CHARSET\n");
1344 ok(russian_charset
> 0, "NULL family should enumerate RUSSIAN_CHARSET\n");
1348 SetLastError(0xdeadbeef);
1349 ret
= EnumFontFamilies(hdc
, font_name
, arial_enum_proc
, (LPARAM
)&efd
);
1350 ok(ret
, "EnumFontFamilies error %u\n", GetLastError());
1351 get_charset_stats(&efd
, &ansi_charset
, &symbol_charset
, &russian_charset
);
1352 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s\n",
1353 ansi_charset
, symbol_charset
, russian_charset
,
1354 *font_name
? font_name
: "<empty>");
1356 ok(efd
.total
> 0, "no fonts enumerated: %s\n", font_name
);
1358 ok(!efd
.total
, "no fonts should be enumerated for empty font_name\n");
1359 for (i
= 0; i
< efd
.total
; i
++)
1361 /* FIXME: remove completely once Wine is fixed */
1362 if (efd
.lf
[i
].lfCharSet
!= font_charset
)
1365 ok(efd
.lf
[i
].lfCharSet
== font_charset
, "%d: got charset %d\n", i
, efd
.lf
[i
].lfCharSet
);
1368 ok(efd
.lf
[i
].lfCharSet
== font_charset
, "%d: got charset %d\n", i
, efd
.lf
[i
].lfCharSet
);
1369 ok(!lstrcmp(efd
.lf
[i
].lfFaceName
, font_name
), "expected %s, got %s\n",
1370 font_name
, efd
.lf
[i
].lfFaceName
);
1373 memset(&lf
, 0, sizeof(lf
));
1374 lf
.lfCharSet
= ANSI_CHARSET
;
1375 lstrcpy(lf
.lfFaceName
, font_name
);
1377 SetLastError(0xdeadbeef);
1378 ret
= EnumFontFamiliesEx(hdc
, &lf
, arial_enum_proc
, (LPARAM
)&efd
, 0);
1379 ok(ret
, "EnumFontFamiliesEx error %u\n", GetLastError());
1380 get_charset_stats(&efd
, &ansi_charset
, &symbol_charset
, &russian_charset
);
1381 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s ANSI_CHARSET\n",
1382 ansi_charset
, symbol_charset
, russian_charset
,
1383 *font_name
? font_name
: "<empty>");
1384 if (font_charset
== SYMBOL_CHARSET
)
1387 ok(efd
.total
== 0, "no fonts should be enumerated: %s ANSI_CHARSET\n", font_name
);
1389 ok(efd
.total
> 0, "no fonts enumerated: %s\n", font_name
);
1393 ok(efd
.total
> 0, "no fonts enumerated: %s ANSI_CHARSET\n", font_name
);
1394 for (i
= 0; i
< efd
.total
; i
++)
1396 ok(efd
.lf
[i
].lfCharSet
== ANSI_CHARSET
, "%d: got charset %d\n", i
, efd
.lf
[i
].lfCharSet
);
1398 ok(!lstrcmp(efd
.lf
[i
].lfFaceName
, font_name
), "expected %s, got %s\n",
1399 font_name
, efd
.lf
[i
].lfFaceName
);
1403 /* DEFAULT_CHARSET should enumerate all available charsets */
1404 memset(&lf
, 0, sizeof(lf
));
1405 lf
.lfCharSet
= DEFAULT_CHARSET
;
1406 lstrcpy(lf
.lfFaceName
, font_name
);
1408 SetLastError(0xdeadbeef);
1409 EnumFontFamiliesEx(hdc
, &lf
, arial_enum_proc
, (LPARAM
)&efd
, 0);
1410 ok(ret
, "EnumFontFamiliesEx error %u\n", GetLastError());
1411 get_charset_stats(&efd
, &ansi_charset
, &symbol_charset
, &russian_charset
);
1412 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s DEFAULT_CHARSET\n",
1413 ansi_charset
, symbol_charset
, russian_charset
,
1414 *font_name
? font_name
: "<empty>");
1415 ok(efd
.total
> 0, "no fonts enumerated: %s DEFAULT_CHARSET\n", font_name
);
1416 for (i
= 0; i
< efd
.total
; i
++)
1419 ok(!lstrcmp(efd
.lf
[i
].lfFaceName
, font_name
), "expected %s, got %s\n",
1420 font_name
, efd
.lf
[i
].lfFaceName
);
1424 switch (font_charset
)
1427 ok(ansi_charset
> 0,
1428 "ANSI_CHARSET should enumerate ANSI_CHARSET for %s\n", font_name
);
1430 "ANSI_CHARSET should NOT enumerate SYMBOL_CHARSET for %s\n", font_name
);
1431 ok(russian_charset
> 0,
1432 "ANSI_CHARSET should enumerate RUSSIAN_CHARSET for %s\n", font_name
);
1434 case SYMBOL_CHARSET
:
1436 "SYMBOL_CHARSET should NOT enumerate ANSI_CHARSET for %s\n", font_name
);
1438 "SYMBOL_CHARSET should enumerate SYMBOL_CHARSET for %s\n", font_name
);
1439 ok(!russian_charset
,
1440 "SYMBOL_CHARSET should NOT enumerate RUSSIAN_CHARSET for %s\n", font_name
);
1442 case DEFAULT_CHARSET
:
1443 ok(ansi_charset
> 0,
1444 "DEFAULT_CHARSET should enumerate ANSI_CHARSET for %s\n", font_name
);
1445 ok(symbol_charset
> 0,
1446 "DEFAULT_CHARSET should enumerate SYMBOL_CHARSET for %s\n", font_name
);
1447 ok(russian_charset
> 0,
1448 "DEFAULT_CHARSET should enumerate RUSSIAN_CHARSET for %s\n", font_name
);
1454 ok(ansi_charset
> 0,
1455 "DEFAULT_CHARSET should enumerate ANSI_CHARSET for %s\n", *font_name
? font_name
: "<empty>");
1456 ok(symbol_charset
> 0,
1457 "DEFAULT_CHARSET should enumerate SYMBOL_CHARSET for %s\n", *font_name
? font_name
: "<empty>");
1458 ok(russian_charset
> 0,
1459 "DEFAULT_CHARSET should enumerate RUSSIAN_CHARSET for %s\n", *font_name
? font_name
: "<empty>");
1462 memset(&lf
, 0, sizeof(lf
));
1463 lf
.lfCharSet
= SYMBOL_CHARSET
;
1464 lstrcpy(lf
.lfFaceName
, font_name
);
1466 SetLastError(0xdeadbeef);
1467 EnumFontFamiliesEx(hdc
, &lf
, arial_enum_proc
, (LPARAM
)&efd
, 0);
1468 ok(ret
, "EnumFontFamiliesEx error %u\n", GetLastError());
1469 get_charset_stats(&efd
, &ansi_charset
, &symbol_charset
, &russian_charset
);
1470 trace("enumerated ansi %d, symbol %d, russian %d fonts for %s SYMBOL_CHARSET\n",
1471 ansi_charset
, symbol_charset
, russian_charset
,
1472 *font_name
? font_name
: "<empty>");
1473 if (*font_name
&& font_charset
== ANSI_CHARSET
)
1474 ok(efd
.total
== 0, "no fonts should be enumerated: %s SYMBOL_CHARSET\n", font_name
);
1477 ok(efd
.total
> 0, "no fonts enumerated: %s SYMBOL_CHARSET\n", font_name
);
1478 for (i
= 0; i
< efd
.total
; i
++)
1480 ok(efd
.lf
[i
].lfCharSet
== SYMBOL_CHARSET
, "%d: got charset %d\n", i
, efd
.lf
[i
].lfCharSet
);
1482 ok(!lstrcmp(efd
.lf
[i
].lfFaceName
, font_name
), "expected %s, got %s\n",
1483 font_name
, efd
.lf
[i
].lfFaceName
);
1487 "SYMBOL_CHARSET should NOT enumerate ANSI_CHARSET for %s\n", *font_name
? font_name
: "<empty>");
1488 ok(symbol_charset
> 0,
1489 "SYMBOL_CHARSET should enumerate SYMBOL_CHARSET for %s\n", *font_name
? font_name
: "<empty>");
1490 ok(!russian_charset
,
1491 "SYMBOL_CHARSET should NOT enumerate RUSSIAN_CHARSET for %s\n", *font_name
? font_name
: "<empty>");
1497 static void test_negative_width(HDC hdc
, const LOGFONTA
*lf
)
1499 HFONT hfont
, hfont_prev
;
1501 GLYPHMETRICS gm1
, gm2
;
1504 MAT2 mat
= { {0,1}, {0,0}, {0,0}, {0,1} };
1506 /* negative widths are handled just as positive ones */
1507 lf2
.lfWidth
= -lf
->lfWidth
;
1509 SetLastError(0xdeadbeef);
1510 hfont
= CreateFontIndirectA(lf
);
1511 ok(hfont
!= 0, "CreateFontIndirect error %u\n", GetLastError());
1512 check_font("original", lf
, hfont
);
1514 hfont_prev
= SelectObject(hdc
, hfont
);
1516 ret
= pGetGlyphIndicesA(hdc
, "x", 1, &idx
, GGI_MARK_NONEXISTING_GLYPHS
);
1517 if (ret
== GDI_ERROR
|| idx
== 0xffff)
1519 SelectObject(hdc
, hfont_prev
);
1520 DeleteObject(hfont
);
1521 skip("Font %s doesn't contain 'x', skipping the test\n", lf
->lfFaceName
);
1525 /* filling with 0xaa causes false pass under WINEDEBUG=warn+heap */
1526 memset(&gm1
, 0xab, sizeof(gm1
));
1527 SetLastError(0xdeadbeef);
1528 ret
= GetGlyphOutlineA(hdc
, 'x', GGO_METRICS
, &gm1
, 0, NULL
, &mat
);
1529 ok(ret
!= GDI_ERROR
, "GetGlyphOutline error 0x%x\n", GetLastError());
1531 SelectObject(hdc
, hfont_prev
);
1532 DeleteObject(hfont
);
1534 SetLastError(0xdeadbeef);
1535 hfont
= CreateFontIndirectA(&lf2
);
1536 ok(hfont
!= 0, "CreateFontIndirect error %u\n", GetLastError());
1537 check_font("negative width", &lf2
, hfont
);
1539 hfont_prev
= SelectObject(hdc
, hfont
);
1541 memset(&gm2
, 0xbb, sizeof(gm2
));
1542 SetLastError(0xdeadbeef);
1543 ret
= GetGlyphOutlineA(hdc
, 'x', GGO_METRICS
, &gm2
, 0, NULL
, &mat
);
1544 ok(ret
!= GDI_ERROR
, "GetGlyphOutline error 0x%x\n", GetLastError());
1546 SelectObject(hdc
, hfont_prev
);
1547 DeleteObject(hfont
);
1549 ok(gm1
.gmBlackBoxX
== gm2
.gmBlackBoxX
&&
1550 gm1
.gmBlackBoxY
== gm2
.gmBlackBoxY
&&
1551 gm1
.gmptGlyphOrigin
.x
== gm2
.gmptGlyphOrigin
.x
&&
1552 gm1
.gmptGlyphOrigin
.y
== gm2
.gmptGlyphOrigin
.y
&&
1553 gm1
.gmCellIncX
== gm2
.gmCellIncX
&&
1554 gm1
.gmCellIncY
== gm2
.gmCellIncY
,
1555 "gm1=%d,%d,%d,%d,%d,%d gm2=%d,%d,%d,%d,%d,%d\n",
1556 gm1
.gmBlackBoxX
, gm1
.gmBlackBoxY
, gm1
.gmptGlyphOrigin
.x
,
1557 gm1
.gmptGlyphOrigin
.y
, gm1
.gmCellIncX
, gm1
.gmCellIncY
,
1558 gm2
.gmBlackBoxX
, gm2
.gmBlackBoxY
, gm2
.gmptGlyphOrigin
.x
,
1559 gm2
.gmptGlyphOrigin
.y
, gm2
.gmCellIncX
, gm2
.gmCellIncY
);
1562 /* PANOSE is 10 bytes in size, need to pack the structure properly */
1563 #include "pshpack2.h"
1567 SHORT xAvgCharWidth
;
1568 USHORT usWeightClass
;
1569 USHORT usWidthClass
;
1571 SHORT ySubscriptXSize
;
1572 SHORT ySubscriptYSize
;
1573 SHORT ySubscriptXOffset
;
1574 SHORT ySubscriptYOffset
;
1575 SHORT ySuperscriptXSize
;
1576 SHORT ySuperscriptYSize
;
1577 SHORT ySuperscriptXOffset
;
1578 SHORT ySuperscriptYOffset
;
1579 SHORT yStrikeoutSize
;
1580 SHORT yStrikeoutPosition
;
1583 ULONG ulUnicodeRange1
;
1584 ULONG ulUnicodeRange2
;
1585 ULONG ulUnicodeRange3
;
1586 ULONG ulUnicodeRange4
;
1589 USHORT usFirstCharIndex
;
1590 USHORT usLastCharIndex
;
1591 /* According to the Apple spec, original version didn't have the below fields,
1592 * version numbers were taked from the OpenType spec.
1594 /* version 0 (TrueType 1.5) */
1595 USHORT sTypoAscender
;
1596 USHORT sTypoDescender
;
1597 USHORT sTypoLineGap
;
1599 USHORT usWinDescent
;
1600 /* version 1 (TrueType 1.66) */
1601 ULONG ulCodePageRange1
;
1602 ULONG ulCodePageRange2
;
1603 /* version 2 (OpenType 1.2) */
1606 USHORT usDefaultChar
;
1608 USHORT usMaxContext
;
1610 #include "poppack.h"
1612 #ifdef WORDS_BIGENDIAN
1613 #define GET_BE_WORD(x) (x)
1615 #define GET_BE_WORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
1618 #define MS_MAKE_TAG(ch0, ch1, ch2, ch3) \
1619 ((DWORD)(BYTE)(ch0) | ((DWORD)(BYTE)(ch1) << 8) | \
1620 ((DWORD)(BYTE)(ch2) << 16) | ((DWORD)(BYTE)(ch3) << 24))
1621 #define MS_OS2_TAG MS_MAKE_TAG('O','S','/','2')
1623 static void test_text_metrics(const LOGFONTA
*lf
)
1626 HFONT hfont
, hfont_old
;
1629 UINT first_unicode_char
, last_unicode_char
, default_char
, break_char
;
1634 const char *font_name
= lf
->lfFaceName
;
1636 trace("Testing font metrics for %s, charset %d\n", font_name
, lf
->lfCharSet
);
1640 SetLastError(0xdeadbeef);
1641 hfont
= CreateFontIndirectA(lf
);
1642 ok(hfont
!= 0, "CreateFontIndirect error %u\n", GetLastError());
1644 hfont_old
= SelectObject(hdc
, hfont
);
1646 size
= GetFontData(hdc
, MS_OS2_TAG
, 0, NULL
, 0);
1647 if (size
== GDI_ERROR
)
1649 trace("OS/2 chunk was not found\n");
1652 if (size
> sizeof(tt_os2
))
1654 trace("got too large OS/2 chunk of size %u\n", size
);
1655 size
= sizeof(tt_os2
);
1658 memset(&tt_os2
, 0, sizeof(tt_os2
));
1659 ret
= GetFontData(hdc
, MS_OS2_TAG
, 0, &tt_os2
, size
);
1660 ok(ret
== size
, "GetFontData should return %u not %u\n", size
, ret
);
1662 version
= GET_BE_WORD(tt_os2
.version
);
1663 trace("OS/2 chunk version %u, vendor %4.4s\n", version
, (LPCSTR
)&tt_os2
.achVendID
);
1665 first_unicode_char
= GET_BE_WORD(tt_os2
.usFirstCharIndex
);
1666 last_unicode_char
= GET_BE_WORD(tt_os2
.usLastCharIndex
);
1667 default_char
= GET_BE_WORD(tt_os2
.usDefaultChar
);
1668 break_char
= GET_BE_WORD(tt_os2
.usBreakChar
);
1670 trace("for %s first %x, last %x, default %x, break %x\n", font_name
,
1671 first_unicode_char
, last_unicode_char
, default_char
, break_char
);
1673 SetLastError(0xdeadbeef);
1674 ret
= GetTextMetricsA(hdc
, &tmA
);
1675 ok(ret
, "GetTextMetricsA error %u\n", GetLastError());
1676 trace("A: first %x, last %x, default %x, break %x\n",
1677 tmA
.tmFirstChar
, tmA
.tmLastChar
, tmA
.tmDefaultChar
, tmA
.tmBreakChar
);
1679 #if 0 /* FIXME: This doesn't appear to be what Windows does */
1680 test_char
= min(first_unicode_char
- 1, 255);
1681 ok(tmA
.tmFirstChar
== test_char
, "A: tmFirstChar for %s %02x != %02x\n",
1682 font_name
, tmA
.tmFirstChar
, test_char
);
1684 if (lf
->lfCharSet
== SYMBOL_CHARSET
)
1686 test_char
= min(last_unicode_char
- 0xf000, 255);
1687 ok(tmA
.tmLastChar
== test_char
, "A: tmLastChar for %s %02x != %02x\n",
1688 font_name
, tmA
.tmLastChar
, test_char
);
1692 test_char
= min(last_unicode_char
, 255);
1693 ok(tmA
.tmLastChar
== test_char
, "A: tmLastChar for %s %02x != %02x\n",
1694 font_name
, tmA
.tmLastChar
, test_char
);
1697 SetLastError(0xdeadbeef);
1698 ret
= GetTextMetricsW(hdc
, &tmW
);
1699 ok(ret
|| GetLastError() == ERROR_CALL_NOT_IMPLEMENTED
,
1700 "GetTextMetricsW error %u\n", GetLastError());
1703 trace("W: first %x, last %x, default %x, break %x\n",
1704 tmW
.tmFirstChar
, tmW
.tmLastChar
, tmW
.tmDefaultChar
,
1707 if (lf
->lfCharSet
== SYMBOL_CHARSET
)
1709 /* It appears that for fonts with SYMBOL_CHARSET Windows always
1710 * sets symbol range to 0 - f0ff
1712 ok(tmW
.tmFirstChar
== 0, "W: tmFirstChar for %s %02x != 0\n",
1713 font_name
, tmW
.tmFirstChar
);
1714 /* FIXME: Windows returns f0ff here, while Wine f0xx */
1715 ok(tmW
.tmLastChar
>= 0xf000, "W: tmLastChar for %s %02x < 0xf000\n",
1716 font_name
, tmW
.tmLastChar
);
1718 ok(tmW
.tmDefaultChar
== 0x1f, "W: tmDefaultChar for %s %02x != 0x1f\n",
1719 font_name
, tmW
.tmDefaultChar
);
1720 ok(tmW
.tmBreakChar
== 0x20, "W: tmBreakChar for %s %02x != 0x20\n",
1721 font_name
, tmW
.tmBreakChar
);
1725 ok(tmW
.tmFirstChar
== first_unicode_char
, "W: tmFirstChar for %s %02x != %02x\n",
1726 font_name
, tmW
.tmFirstChar
, first_unicode_char
);
1727 ok(tmW
.tmLastChar
== last_unicode_char
, "W: tmLastChar for %s %02x != %02x\n",
1728 font_name
, tmW
.tmLastChar
, last_unicode_char
);
1730 ret
= GetDeviceCaps(hdc
, LOGPIXELSX
);
1731 ok(tmW
.tmDigitizedAspectX
== ret
, "W: tmDigitizedAspectX %u != %u\n",
1732 tmW
.tmDigitizedAspectX
, ret
);
1733 ret
= GetDeviceCaps(hdc
, LOGPIXELSY
);
1734 ok(tmW
.tmDigitizedAspectX
== ret
, "W: tmDigitizedAspectY %u != %u\n",
1735 tmW
.tmDigitizedAspectX
, ret
);
1738 test_negative_width(hdc
, lf
);
1741 SelectObject(hdc
, hfont_old
);
1742 DeleteObject(hfont
);
1747 static INT CALLBACK
enum_truetype_font_proc(const LOGFONT
*lf
, const TEXTMETRIC
*ntm
, DWORD type
, LPARAM lParam
)
1749 INT
*enumed
= (INT
*)lParam
;
1751 if (type
== TRUETYPE_FONTTYPE
)
1754 test_text_metrics(lf
);
1759 static void test_GetTextMetrics(void)
1767 memset(&lf
, 0, sizeof(lf
));
1768 lf
.lfCharSet
= DEFAULT_CHARSET
;
1770 EnumFontFamiliesExA(hdc
, &lf
, enum_truetype_font_proc
, (LPARAM
)&enumed
, 0);
1771 trace("Tested metrics of %d truetype fonts\n", enumed
);
1776 static void test_nonexistent_font(void)
1781 char buf
[LF_FACESIZE
];
1783 if (!is_truetype_font_installed("Arial Black"))
1785 skip("Arial not installed\n");
1791 memset(&lf
, 0, sizeof(lf
));
1793 lf
.lfWeight
= FW_REGULAR
;
1794 lf
.lfCharSet
= ANSI_CHARSET
;
1795 lf
.lfPitchAndFamily
= FF_SWISS
;
1796 strcpy(lf
.lfFaceName
, "Nonexistent font");
1798 hfont
= CreateFontIndirectA(&lf
);
1799 hfont
= SelectObject(hdc
, hfont
);
1800 GetTextFaceA(hdc
, sizeof(buf
), buf
);
1801 ok(!lstrcmpiA(buf
, "Arial"), "Got %s\n", buf
);
1802 DeleteObject(SelectObject(hdc
, hfont
));
1806 static void test_GdiRealizationInfo(void)
1811 HFONT hfont
, hfont_old
;
1814 if(!pGdiRealizationInfo
)
1816 skip("GdiRealizationInfo not available\n");
1822 memset(info
, 0xcc, sizeof(info
));
1823 r
= pGdiRealizationInfo(hdc
, info
);
1824 ok(r
!= 0, "ret 0\n");
1825 ok(info
[0] == 1, "info[0] = %x for the system font\n", info
[0]);
1826 ok(info
[3] == 0xcccccccc, "structure longer than 3 dwords\n");
1828 if (!is_truetype_font_installed("Arial"))
1830 skip("skipping GdiRealizationInfo with truetype font\n");
1834 memset(&lf
, 0, sizeof(lf
));
1835 strcpy(lf
.lfFaceName
, "Arial");
1837 lf
.lfWeight
= FW_NORMAL
;
1838 hfont
= CreateFontIndirectA(&lf
);
1839 hfont_old
= SelectObject(hdc
, hfont
);
1841 memset(info
, 0xcc, sizeof(info
));
1842 r
= pGdiRealizationInfo(hdc
, info
);
1843 ok(r
!= 0, "ret 0\n");
1844 ok(info
[0] == 3, "info[0] = %x for arial\n", info
[0]);
1845 ok(info
[3] == 0xcccccccc, "structure longer than 3 dwords\n");
1847 DeleteObject(SelectObject(hdc
, hfont_old
));
1859 test_bitmap_font_metrics();
1860 test_GdiGetCharDimensions();
1861 test_GetCharABCWidths();
1862 test_text_extents();
1863 test_GetGlyphIndices();
1864 test_GetKerningPairs();
1865 test_GetOutlineTextMetrics();
1866 test_SetTextJustification();
1867 test_font_charset();
1868 test_GetFontUnicodeRanges();
1869 test_nonexistent_font();
1871 /* On Windows Arial has a lot of default charset aliases such as Arial Cyr,
1872 * I'd like to avoid them in this test.
1874 test_EnumFontFamilies("Arial Black", ANSI_CHARSET
);
1875 test_EnumFontFamilies("Symbol", SYMBOL_CHARSET
);
1876 if (is_truetype_font_installed("Arial Black") &&
1877 (is_truetype_font_installed("Symbol") || is_truetype_font_installed("Wingdings")))
1879 test_EnumFontFamilies("", ANSI_CHARSET
);
1880 test_EnumFontFamilies("", SYMBOL_CHARSET
);
1881 test_EnumFontFamilies("", DEFAULT_CHARSET
);
1884 skip("Arial Black or Symbol/Wingdings is not installed\n");
1885 test_GetTextMetrics();
1886 test_GdiRealizationInfo();