gdi32: Add a GetTextMetrics test, make it pass under Wine
Dmitry Timoshkov
dmitry at codeweavers.com
Sun Mar 11 09:01:26 CDT 2007
"Lei Zhang" <thestig at google.com> wrote:
> I tested it out on a couple machines and found many failure cases.
> (output attached) As a sanity check, I copied the deja vu family of
> fonts to a windows box and they passed the tests there.
Please don't send large logs to a public mailing list, attach them
to an appropriate bug instead.
This new version of the patch should pass all the tests now.
This patch should resolve a problem reported in the bug 5783.
Changelog:
gdi32: Add a GetTextMetrics test, make it pass under Wine.
---
dlls/gdi32/font.c | 47 ++++++----
dlls/gdi32/freetype.c | 68 +++++++++++---
dlls/gdi32/gdi_private.h | 4 +-
dlls/gdi32/tests/font.c | 225 +++++++++++++++++++++++++++++++++++++++++++++-
4 files changed, 307 insertions(+), 37 deletions(-)
diff --git a/dlls/gdi32/font.c b/dlls/gdi32/font.c
index 1b1d8b4..04b54cf 100644
--- a/dlls/gdi32/font.c
+++ b/dlls/gdi32/font.c
@@ -1335,29 +1335,17 @@ BOOL WINAPI GetTextExtentExPointW( HDC hdc, LPCWSTR str, INT count,
return ret;
}
-/***********************************************************************
- * GetTextMetricsA (GDI32.@)
- */
-BOOL WINAPI GetTextMetricsA( HDC hdc, TEXTMETRICA *metrics )
-{
- TEXTMETRICW tm32;
-
- if (!GetTextMetricsW( hdc, &tm32 )) return FALSE;
- FONT_TextMetricWToA( &tm32, metrics );
- return TRUE;
-}
-
-/***********************************************************************
- * GetTextMetricsW (GDI32.@)
- */
-BOOL WINAPI GetTextMetricsW( HDC hdc, TEXTMETRICW *metrics )
+static BOOL get_text_metrics( HDC hdc, TEXTMETRICW *tm, BOOL unicode )
{
+ TEXTMETRICW tmW, *metrics;
BOOL ret = FALSE;
DC * dc = DC_GetDCPtr( hdc );
if (!dc) return FALSE;
+ metrics = unicode ? tm : &tmW;
+
if (dc->gdiFont)
- ret = WineEngGetTextMetrics(dc->gdiFont, metrics);
+ ret = WineEngGetTextMetrics(dc->gdiFont, metrics, unicode);
else if (dc->funcs->pGetTextMetrics)
ret = dc->funcs->pGetTextMetrics( dc->physDev, metrics );
@@ -1366,6 +1354,9 @@ BOOL WINAPI GetTextMetricsW( HDC hdc, TEXTMETRICW *metrics )
/* device layer returns values in device units
* therefore we have to convert them to logical */
+ metrics->tmDigitizedAspectX = GetDeviceCaps(hdc, LOGPIXELSX);
+ metrics->tmDigitizedAspectY = GetDeviceCaps(hdc, LOGPIXELSY);
+
#define WDPTOLP(x) ((x<0)? \
(-abs(INTERNAL_XDSTOWS(dc, (x)))): \
(abs(INTERNAL_XDSTOWS(dc, (x)))))
@@ -1404,11 +1395,29 @@ BOOL WINAPI GetTextMetricsW( HDC hdc, TEXTMETRICW *metrics )
metrics->tmAscent,
metrics->tmDescent,
metrics->tmHeight );
+
+ if (!unicode)
+ FONT_TextMetricWToA( metrics, (TEXTMETRICA *)tm );
}
GDI_ReleaseObj( hdc );
return ret;
}
+/***********************************************************************
+ * GetTextMetricsW (GDI32.@)
+ */
+BOOL WINAPI GetTextMetricsW( HDC hdc, TEXTMETRICW *metrics )
+{
+ return get_text_metrics( hdc, metrics, TRUE );
+}
+
+/***********************************************************************
+ * GetTextMetricsA (GDI32.@)
+ */
+BOOL WINAPI GetTextMetricsA( HDC hdc, TEXTMETRICA *metrics )
+{
+ return get_text_metrics( hdc, (TEXTMETRICW *)metrics, FALSE );
+}
/***********************************************************************
* GetOutlineTextMetrics [GDI.308] Gets metrics for TrueType fonts.
@@ -1622,11 +1631,11 @@ UINT WINAPI GetOutlineTextMetricsW(
if(!dc) return 0;
if(dc->gdiFont) {
- ret = WineEngGetOutlineTextMetrics(dc->gdiFont, cbData, output);
+ ret = WineEngGetOutlineTextMetrics(dc->gdiFont, cbData, output, TRUE);
if(lpOTM && ret) {
if(ret > cbData) {
output = HeapAlloc(GetProcessHeap(), 0, ret);
- WineEngGetOutlineTextMetrics(dc->gdiFont, ret, output);
+ WineEngGetOutlineTextMetrics(dc->gdiFont, ret, output, TRUE);
}
#define WDPTOLP(x) ((x<0)? \
diff --git a/dlls/gdi32/freetype.c b/dlls/gdi32/freetype.c
index db990fa..6944896 100644
--- a/dlls/gdi32/freetype.c
+++ b/dlls/gdi32/freetype.c
@@ -3071,16 +3071,16 @@ static void GetEnumStructs(Face *face, LPENUMLOGFONTEXW pelf,
memset(&pelf->elfLogFont, 0, sizeof(LOGFONTW));
- size = WineEngGetOutlineTextMetrics(font, 0, NULL);
+ size = WineEngGetOutlineTextMetrics(font, 0, NULL, TRUE);
if(size) {
potm = HeapAlloc(GetProcessHeap(), 0, size);
- WineEngGetOutlineTextMetrics(font, size, potm);
+ WineEngGetOutlineTextMetrics(font, size, potm, TRUE);
ptm = (TEXTMETRICW*)&potm->otmTextMetrics;
} else {
- WineEngGetTextMetrics(font, &tm);
+ WineEngGetTextMetrics(font, &tm, TRUE);
ptm = &tm;
}
-
+
pntm->ntmTm.tmHeight = pelf->elfLogFont.lfHeight = ptm->tmHeight;
pntm->ntmTm.tmAscent = ptm->tmAscent;
pntm->ntmTm.tmDescent = ptm->tmDescent;
@@ -3337,7 +3337,7 @@ DWORD WineEngGetGlyphIndices(GdiFont *font, LPCWSTR lpstr, INT count,
{
if (!default_char)
{
- WineEngGetTextMetrics(font, &textm);
+ WineEngGetTextMetrics(font, &textm, TRUE);
default_char = textm.tmDefaultChar;
}
pgi[i] = default_char;
@@ -3873,20 +3873,43 @@ static BOOL get_bitmap_text_metrics(GdiFont *font)
return TRUE;
}
+static void fixup_ansi_metrics(GdiFont *font, TEXTMETRICW *tm)
+{
+ TT_OS2 *pOS2;
+
+ if (!FT_IS_SCALABLE(font->ft_face)) return;
+
+ /* This appears to be what Windows does */
+ tm->tmFirstChar = tm->tmBreakChar;
+ if (tm->tmFirstChar >= 2) tm->tmFirstChar -= 2;
+ if (tm->tmFirstChar > 255) tm->tmFirstChar = 255;
+
+ /* pFT_Get_Sfnt_Table should always succeed at this point */
+ pOS2 = pFT_Get_Sfnt_Table(font->ft_face, ft_sfnt_os2);
+
+ if (font->charset == SYMBOL_CHARSET)
+ tm->tmLastChar = min(pOS2->usLastCharIndex - 0xf000, 255);
+ else
+ tm->tmLastChar = min(pOS2->usLastCharIndex, 255);
+}
+
/*************************************************************
* WineEngGetTextMetrics
*
*/
-BOOL WineEngGetTextMetrics(GdiFont *font, LPTEXTMETRICW ptm)
+BOOL WineEngGetTextMetrics(GdiFont *font, LPTEXTMETRICW ptm, BOOL unicode)
{
if(!font->potm) {
- if(!WineEngGetOutlineTextMetrics(font, 0, NULL))
+ if(!WineEngGetOutlineTextMetrics(font, 0, NULL, TRUE))
if(!get_bitmap_text_metrics(font))
return FALSE;
}
if(!font->potm) return FALSE;
memcpy(ptm, &font->potm->otmTextMetrics, sizeof(*ptm));
+ if (!unicode)
+ fixup_ansi_metrics(font, ptm);
+
if (font->aveWidth) {
ptm->tmAveCharWidth = font->aveWidth * font->font_desc.matrix.eM11;
}
@@ -3899,7 +3922,7 @@ BOOL WineEngGetTextMetrics(GdiFont *font, LPTEXTMETRICW ptm)
*
*/
UINT WineEngGetOutlineTextMetrics(GdiFont *font, UINT cbSize,
- OUTLINETEXTMETRICW *potm)
+ OUTLINETEXTMETRICW *potm, BOOL unicode)
{
FT_Face ft_face = font->ft_face;
UINT needed, lenfam, lensty, ret;
@@ -3919,7 +3942,11 @@ UINT WineEngGetOutlineTextMetrics(GdiFont *font, UINT cbSize,
if(font->potm) {
if(cbSize >= font->potm->otmSize)
+ {
memcpy(potm, font->potm, font->potm->otmSize);
+ if (!unicode)
+ fixup_ansi_metrics(font, &potm->otmTextMetrics);
+ }
return font->potm->otmSize;
}
@@ -4022,10 +4049,21 @@ UINT WineEngGetOutlineTextMetrics(GdiFont *font, UINT cbSize,
TM.tmOverhang = 0;
TM.tmDigitizedAspectX = 300;
TM.tmDigitizedAspectY = 300;
- TM.tmFirstChar = pOS2->usFirstCharIndex;
- TM.tmLastChar = pOS2->usLastCharIndex;
- TM.tmDefaultChar = pOS2->usDefaultChar;
TM.tmBreakChar = pOS2->usBreakChar ? pOS2->usBreakChar : ' ';
+ /* It appears that for fonts with SYMBOL_CHARSET Windows always sets
+ * symbol range to 0 - f0ff
+ */
+ if (font->charset == SYMBOL_CHARSET)
+ {
+ TM.tmFirstChar = 0;
+ TM.tmLastChar = 0xf0ff;
+ }
+ else
+ {
+ TM.tmFirstChar = pOS2->usFirstCharIndex;
+ TM.tmLastChar = pOS2->usLastCharIndex;
+ }
+ TM.tmDefaultChar = pOS2->usDefaultChar ? pOS2->usDefaultChar : 0x1f;
TM.tmItalic = font->fake_italic ? 255 : ((ft_face->style_flags & FT_STYLE_FLAG_ITALIC) ? 255 : 0);
TM.tmUnderlined = font->underline;
TM.tmStruckOut = font->strikeout;
@@ -4306,7 +4344,7 @@ BOOL WineEngGetTextExtentExPoint(GdiFont *font, LPCWSTR wstr, INT count,
max_ext, size);
size->cx = 0;
- WineEngGetTextMetrics(font, &tm);
+ WineEngGetTextMetrics(font, &tm, TRUE);
size->cy = tm.tmHeight;
for(idx = 0; idx < count; idx++) {
@@ -4343,7 +4381,7 @@ BOOL WineEngGetTextExtentPointI(GdiFont *font, const WORD *indices, INT count,
TRACE("%p, %p, %d, %p\n", font, indices, count, size);
size->cx = 0;
- WineEngGetTextMetrics(font, &tm);
+ WineEngGetTextMetrics(font, &tm, TRUE);
size->cy = tm.tmHeight;
for(idx = 0; idx < count; idx++) {
@@ -4888,14 +4926,14 @@ DWORD WineEngGetGlyphOutline(GdiFont *font, UINT glyph, UINT format,
return GDI_ERROR;
}
-BOOL WineEngGetTextMetrics(GdiFont *font, LPTEXTMETRICW ptm)
+BOOL WineEngGetTextMetrics(GdiFont *font, LPTEXTMETRICW ptm, BOOL unicode)
{
ERR("called but we don't have FreeType\n");
return FALSE;
}
UINT WineEngGetOutlineTextMetrics(GdiFont *font, UINT cbSize,
- OUTLINETEXTMETRICW *potm)
+ OUTLINETEXTMETRICW *potm, BOOL unicode)
{
ERR("called but we don't have FreeType\n");
return 0;
diff --git a/dlls/gdi32/gdi_private.h b/dlls/gdi32/gdi_private.h
index e43ac30..47974c9 100644
--- a/dlls/gdi32/gdi_private.h
+++ b/dlls/gdi32/gdi_private.h
@@ -431,12 +431,12 @@ extern DWORD WineEngGetGlyphOutline(GdiFont*, UINT glyph, UINT format,
const MAT2*);
extern DWORD WineEngGetKerningPairs(GdiFont*, DWORD, KERNINGPAIR *);
extern BOOL WineEngGetLinkedHFont(DC *dc, WCHAR c, HFONT *new_hfont, UINT *glyph);
-extern UINT WineEngGetOutlineTextMetrics(GdiFont*, UINT, LPOUTLINETEXTMETRICW);
+extern UINT WineEngGetOutlineTextMetrics(GdiFont*, UINT, LPOUTLINETEXTMETRICW, BOOL);
extern UINT WineEngGetTextCharsetInfo(GdiFont *font, LPFONTSIGNATURE fs, DWORD flags);
extern BOOL WineEngGetTextExtentExPoint(GdiFont*, LPCWSTR, INT, INT, LPINT, LPINT, LPSIZE);
extern BOOL WineEngGetTextExtentPointI(GdiFont*, const WORD *, INT, LPSIZE);
extern INT WineEngGetTextFace(GdiFont*, INT, LPWSTR);
-extern BOOL WineEngGetTextMetrics(GdiFont*, LPTEXTMETRICW);
+extern BOOL WineEngGetTextMetrics(GdiFont*, LPTEXTMETRICW, BOOL);
extern BOOL WineEngInit(void);
extern BOOL WineEngRemoveFontResourceEx(LPCWSTR, DWORD, PVOID);
diff --git a/dlls/gdi32/tests/font.c b/dlls/gdi32/tests/font.c
index 43e9c3a..4918417 100644
--- a/dlls/gdi32/tests/font.c
+++ b/dlls/gdi32/tests/font.c
@@ -408,7 +408,7 @@ static void test_GdiGetCharDimensions(void)
if (!pGdiGetCharDimensions)
{
- skip("GetFontUnicodeRanges not available on this platform\n");
+ skip("GdiGetCharDimensions not available on this platform\n");
return;
}
@@ -1149,6 +1149,7 @@ static void test_GetFontUnicodeRanges(void)
size = pGetFontUnicodeRanges(hdc, gs);
ok(size, "GetFontUnicodeRanges failed\n");
+ ok(gs->cRanges != 0, "GetFontUnicodeRanges returned empty ranges\n");
#if 0
for (i = 0; i < gs->cRanges; i++)
trace("%03d wcLow %04x cGlyphs %u\n", i, gs->ranges[i].wcLow, gs->ranges[i].cGlyphs);
@@ -1397,6 +1398,227 @@ else
ReleaseDC(0, hdc);
}
+/* PANOSE is 10 bytes in size, need to pack the structure properly */
+#include "pshpack2.h"
+typedef struct
+{
+ USHORT version;
+ SHORT xAvgCharWidth;
+ USHORT usWeightClass;
+ USHORT usWidthClass;
+ SHORT fsType;
+ SHORT ySubscriptXSize;
+ SHORT ySubscriptYSize;
+ SHORT ySubscriptXOffset;
+ SHORT ySubscriptYOffset;
+ SHORT ySuperscriptXSize;
+ SHORT ySuperscriptYSize;
+ SHORT ySuperscriptXOffset;
+ SHORT ySuperscriptYOffset;
+ SHORT yStrikeoutSize;
+ SHORT yStrikeoutPosition;
+ SHORT sFamilyClass;
+ PANOSE panose;
+ ULONG ulUnicodeRange1;
+ ULONG ulUnicodeRange2;
+ ULONG ulUnicodeRange3;
+ ULONG ulUnicodeRange4;
+ CHAR achVendID[4];
+ USHORT fsSelection;
+ USHORT usFirstCharIndex;
+ USHORT usLastCharIndex;
+#if 0
+ /* According to the Apple spec, original version didn't have the below fields,
+ * version numbers were taked from the OpenType spec.
+ */
+ /* version 0 (TrueType 1.5) */
+ USHORT sTypoAscender;
+ USHORT sTypoDescender;
+ USHORT sTypoLineGap;
+ USHORT usWinAscent;
+ USHORT usWinDescent;
+ /* version 1 (TrueType 1.66) */
+ ULONG ulCodePageRange1;
+ ULONG ulCodePageRange2;
+ /* version 2 (OpenType 1.2) */
+ SHORT sxHeight;
+ SHORT sCapHeight;
+ USHORT usDefaultChar;
+ USHORT usBreakChar;
+ USHORT usMaxContext;
+#endif
+} TT_OS2_V0;
+#include "poppack.h"
+
+#ifdef WORDS_BIGENDIAN
+#define GET_BE_WORD(x) (x)
+#else
+#define GET_BE_WORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
+#endif
+
+#define MS_MAKE_TAG(ch0, ch1, ch2, ch3) \
+ ((DWORD)(BYTE)(ch0) | ((DWORD)(BYTE)(ch1) << 8) | \
+ ((DWORD)(BYTE)(ch2) << 16) | ((DWORD)(BYTE)(ch3) << 24))
+#define MS_OS2_TAG MS_MAKE_TAG('O','S','/','2')
+
+static void test_text_metrics(const LOGFONTA *lf)
+{
+ HDC hdc;
+ HFONT hfont, hfont_old;
+ TEXTMETRICA tmA;
+ TEXTMETRICW tmW;
+ UINT first_unicode_char, last_unicode_char;
+ INT test_char;
+ TT_OS2_V0 tt_os2;
+ USHORT version;
+ LONG size, ret;
+ const char *font_name = lf->lfFaceName;
+
+ trace("Testing font metrics for %s, charset %d\n", font_name, lf->lfCharSet);
+
+ hdc = GetDC(0);
+
+ SetLastError(0xdeadbeef);
+ hfont = CreateFontIndirectA(lf);
+ ok(hfont != 0, "CreateFontIndirect error %u\n", GetLastError());
+
+ hfont_old = SelectObject(hdc, hfont);
+
+ size = GetFontData(hdc, MS_OS2_TAG, 0, NULL, 0);
+ if (size == GDI_ERROR)
+ {
+ trace("OS/2 chunk was not found\n");
+ goto end_of_test;
+ }
+ if (size < sizeof(TT_OS2_V0))
+ {
+ trace("got too short OS/2 chunk of size %u, need at least %u\n", size, (UINT)sizeof(TT_OS2_V0));
+ goto end_of_test;
+ }
+ /* we don't need anything not in version 0 */
+ size = sizeof(tt_os2);
+
+ ret = GetFontData(hdc, MS_OS2_TAG, 0, &tt_os2, size);
+ ok(ret == size, "GetFontData should return %u not %u\n", size, ret);
+
+ ret = GetFontData(hdc, MS_OS2_TAG, 0, &tt_os2, size - 1);
+ ok(ret == size-1, "GetFontData should return %u not %u\n", size-1, ret);
+
+ ret = GetFontData(hdc, MS_OS2_TAG, 0, &tt_os2, size);
+ ok(ret == size, "GetFontData(OS/2) should return %u not %u\n", size, ret);
+
+ version = GET_BE_WORD(tt_os2.version);
+ if (version != 0 && version != 1 && version != 2)
+ {
+ trace("not expected OS/2 chunk version %u, (vendor %4.4s)\n",
+ version, (LPCSTR)&tt_os2.achVendID);
+ }
+
+ first_unicode_char = GET_BE_WORD(tt_os2.usFirstCharIndex);
+ last_unicode_char = GET_BE_WORD(tt_os2.usLastCharIndex);
+
+ trace("for %s first char %x, last char %x\n", font_name,
+ first_unicode_char, last_unicode_char);
+
+ SetLastError(0xdeadbeef);
+ ret = GetTextMetricsA(hdc, &tmA);
+ ok(ret, "GetTextMetricsA error %u\n", GetLastError());
+
+ trace("A: first %x, last %x, default %x, break %x\n",
+ tmA.tmFirstChar, tmA.tmLastChar, tmA.tmDefaultChar, tmA.tmBreakChar);
+
+ SetLastError(0xdeadbeef);
+ ret = GetTextMetricsW(hdc, &tmW);
+ ok(ret, "GetTextMetricsA error %u\n", GetLastError());
+
+ trace("W: first %x, last %x, default %x, break %x\n",
+ tmW.tmFirstChar, tmW.tmLastChar, tmW.tmDefaultChar, tmW.tmBreakChar);
+
+ /* This appears to be what Windows does */
+ test_char = tmW.tmBreakChar;
+ if (test_char >= 2) test_char -= 2;
+ if (test_char > 255) test_char = 255;
+ ok(tmA.tmFirstChar == test_char, "A: tmFirstChar for %s %02x != %02x\n",
+ font_name, tmA.tmFirstChar, test_char);
+
+ if (lf->lfCharSet == SYMBOL_CHARSET)
+ {
+ test_char = last_unicode_char - 0xf000;
+ if (test_char > 255) test_char = 255;
+ ok(tmA.tmLastChar == test_char, "A: tmLastChar for %s %02x != %02x\n",
+ font_name, tmA.tmLastChar, test_char);
+
+ /* It appears that for fonts with SYMBOL_CHARSET Windows always sets
+ * symbol range to 0 - f0ff
+ */
+ ok(tmW.tmFirstChar == 0, "W: tmFirstChar for %s %02x != 0\n",
+ font_name, tmW.tmFirstChar);
+ ok(tmW.tmLastChar == 0xf0ff, "W: tmLastChar for %s %02x != 0xf0ff\n",
+ font_name, tmW.tmLastChar);
+ }
+ else
+ {
+ test_char = tmW.tmLastChar > 255 ? 255 : tmW.tmLastChar;
+ ok(tmA.tmLastChar == test_char, "tmLastChar for %s %02x != %02x\n",
+ font_name, tmA.tmLastChar, test_char);
+
+ ok(tmW.tmFirstChar == first_unicode_char, "tmFirstChar for %s %02x != %02x\n",
+ font_name, tmW.tmFirstChar, first_unicode_char);
+ ok(tmW.tmLastChar == last_unicode_char, "tmLastChar for %s %02x != %02x\n",
+ font_name, tmW.tmLastChar, last_unicode_char);
+ }
+
+ ret = GetDeviceCaps(hdc, LOGPIXELSX);
+ ok(tmW.tmDigitizedAspectX == ret, "tmDigitizedAspectX %u != %u\n",
+ tmW.tmDigitizedAspectX, ret);
+ ret = GetDeviceCaps(hdc, LOGPIXELSY);
+ ok(tmW.tmDigitizedAspectX == ret, "tmDigitizedAspectY %u != %u\n",
+ tmW.tmDigitizedAspectX, ret);
+
+end_of_test:
+ SelectObject(hdc, hfont_old);
+ DeleteObject(hfont);
+
+ ReleaseDC(0, hdc);
+}
+
+static INT CALLBACK enum_truetype_font_proc(const LOGFONT *lf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
+{
+ INT *enumed = (INT *)lParam;
+
+ if (type == TRUETYPE_FONTTYPE)
+ {
+ (*enumed)++;
+ test_text_metrics(lf);
+ }
+ return 1;
+}
+
+static void test_GetTextMetrics(void)
+{
+ LOGFONTA lf;
+ HDC hdc;
+ INT enumed;
+
+ SetLastError(0xdeadbeef);
+ GetTextMetricsW(0, NULL);
+ if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
+ {
+ skip("Skipping GetTextMetrics test on a Win9x platform\n");
+ return;
+ }
+
+ hdc = GetDC(0);
+
+ memset(&lf, 0, sizeof(lf));
+ lf.lfCharSet = DEFAULT_CHARSET;
+ enumed = 0;
+ EnumFontFamiliesExA(hdc, &lf, enum_truetype_font_proc, (LPARAM)&enumed, 0);
+ trace("Tested metrics of %d truetype fonts\n", enumed);
+
+ ReleaseDC(0, hdc);
+}
+
START_TEST(font)
{
init();
@@ -1427,4 +1649,5 @@ START_TEST(font)
}
else
skip("Arial Black or Symbol/Wingdings is not installed\n");
+ test_GetTextMetrics();
}
--
1.5.0.2
More information about the wine-patches
mailing list