diff --git a/dlls/gdi32/font.c b/dlls/gdi32/font.c index 1b1d8b43bde..e8b96c429de 100644 --- a/dlls/gdi32/font.c +++ b/dlls/gdi32/font.c @@ -287,10 +287,17 @@ static void FONT_TextMetricWToA(const TEXTMETRICW *ptmW, LPTEXTMETRICA ptmA ) ptmA->tmOverhang = ptmW->tmOverhang; ptmA->tmDigitizedAspectX = ptmW->tmDigitizedAspectX; ptmA->tmDigitizedAspectY = ptmW->tmDigitizedAspectY; - ptmA->tmFirstChar = ptmW->tmFirstChar > 255 ? 255 : ptmW->tmFirstChar; - ptmA->tmLastChar = ptmW->tmLastChar > 255 ? 255 : ptmW->tmLastChar; - ptmA->tmDefaultChar = ptmW->tmDefaultChar > 255 ? 255 : ptmW->tmDefaultChar; - ptmA->tmBreakChar = ptmW->tmBreakChar > 255 ? 255 : ptmW->tmBreakChar; + ptmA->tmFirstChar = min(ptmW->tmFirstChar, 255); + if (ptmW->tmCharSet == SYMBOL_CHARSET) + { + UINT last_char = ptmW->tmLastChar; + if (last_char > 0xf000) last_char -= 0xf000; + ptmA->tmLastChar = min(last_char, 255); + } + else + ptmA->tmLastChar = min(ptmW->tmLastChar, 255); + ptmA->tmDefaultChar = min(ptmW->tmDefaultChar, 255); + ptmA->tmBreakChar = min(ptmW->tmBreakChar, 255); ptmA->tmItalic = ptmW->tmItalic; ptmA->tmUnderlined = ptmW->tmUnderlined; ptmA->tmStruckOut = ptmW->tmStruckOut; @@ -1366,6 +1373,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))))) diff --git a/dlls/gdi32/freetype.c b/dlls/gdi32/freetype.c index db990fa9602..4aa2c20c366 100644 --- a/dlls/gdi32/freetype.c +++ b/dlls/gdi32/freetype.c @@ -4022,9 +4022,15 @@ UINT WineEngGetOutlineTextMetrics(GdiFont *font, UINT cbSize, TM.tmOverhang = 0; TM.tmDigitizedAspectX = 300; TM.tmDigitizedAspectY = 300; - TM.tmFirstChar = pOS2->usFirstCharIndex; + /* It appears that for fonts with SYMBOL_CHARSET Windows always sets + * symbol range to 0 - f0ff + */ + if (font->charset == SYMBOL_CHARSET) + TM.tmFirstChar = 0; + else + TM.tmFirstChar = pOS2->usFirstCharIndex; TM.tmLastChar = pOS2->usLastCharIndex; - TM.tmDefaultChar = pOS2->usDefaultChar; + TM.tmDefaultChar = pOS2->usDefaultChar ? pOS2->usDefaultChar : 0x1f; TM.tmBreakChar = pOS2->usBreakChar ? pOS2->usBreakChar : ' '; TM.tmItalic = font->fake_italic ? 255 : ((ft_face->style_flags & FT_STYLE_FLAG_ITALIC) ? 255 : 0); TM.tmUnderlined = font->underline; diff --git a/dlls/gdi32/tests/font.c b/dlls/gdi32/tests/font.c index c695e13d3d5..2f7a6a994a5 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; } @@ -1397,6 +1397,222 @@ 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; + /* 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; +} TT_OS2_V2; +#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, default_char, break_char; + INT test_char; + TT_OS2_V2 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)) + { + trace("got too large OS/2 chunk of size %u\n", size); + size = sizeof(tt_os2); + } + + memset(&tt_os2, 0, 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); + + version = GET_BE_WORD(tt_os2.version); + trace("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); + default_char = GET_BE_WORD(tt_os2.usDefaultChar); + break_char = GET_BE_WORD(tt_os2.usBreakChar); + + trace("for %s first %x, last %x, default %x, break %x\n", font_name, + first_unicode_char, last_unicode_char, default_char, break_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); + + if (lf->lfCharSet == SYMBOL_CHARSET) + { + test_char = min(last_unicode_char - 0xf000, 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); + /* FIXME: Windows returns f0ff here, while Wine f0xx */ + ok(tmW.tmLastChar >= 0xf000, "W: tmLastChar for %s %02x != 0xf0ff\n", + font_name, tmW.tmLastChar); + + ok(tmA.tmDefaultChar == 0x1f, "A: tmDefaultChar for %s %02x != 0\n", + font_name, tmW.tmDefaultChar); + ok(tmA.tmBreakChar == 0x20, "A: tmBreakChar for %s %02x != 0xf0ff\n", + font_name, tmW.tmBreakChar); + ok(tmW.tmDefaultChar == 0x1f, "W: tmDefaultChar for %s %02x != 0\n", + font_name, tmW.tmDefaultChar); + ok(tmW.tmBreakChar == 0x20, "W: tmBreakChar for %s %02x != 0xf0ff\n", + font_name, tmW.tmBreakChar); + } + else + { + test_char = min(tmW.tmLastChar, 255); + ok(tmA.tmLastChar == test_char, "A: tmLastChar for %s %02x != %02x\n", + font_name, tmA.tmLastChar, test_char); + + ok(tmW.tmFirstChar == first_unicode_char, "W: tmFirstChar for %s %02x != %02x\n", + font_name, tmW.tmFirstChar, first_unicode_char); + ok(tmW.tmLastChar == last_unicode_char, "W: tmLastChar for %s %02x != %02x\n", + font_name, tmW.tmLastChar, last_unicode_char); + } +#if 0 /* FIXME: This doesn't appear to be what Windows does */ + test_char = min(tmW.tmFirstChar - 1, 255); + ok(tmA.tmFirstChar == test_char, "A: tmFirstChar for %s %02x != %02x\n", + font_name, tmA.tmFirstChar, test_char); +#endif + 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 +1643,5 @@ START_TEST(font) } else skip("Arial Black or Symbol/Wingdings is not installed\n"); + test_GetTextMetrics(); }