diff --git a/dlls/gdi/font.c b/dlls/gdi/font.c index 42b4925e736..ce8149aa1cf 100644 --- a/dlls/gdi/font.c +++ b/dlls/gdi/font.c @@ -2514,11 +2514,68 @@ BOOL WINAPI CreateScalableFontResourceW( DWORD fHidden, * GetKerningPairsA (GDI32.@) */ DWORD WINAPI GetKerningPairsA( HDC hDC, DWORD cPairs, - LPKERNINGPAIR lpKerningPairs ) + LPKERNINGPAIR kern_pairA ) { - return GetKerningPairsW( hDC, cPairs, lpKerningPairs ); -} + INT charset; + CHARSETINFO csi; + CPINFO cpi; + DWORD i, total_kern_pairs, kern_pairs_copied = 0; + KERNINGPAIR *kern_pairW; + if (!cPairs && kern_pairA) + { + SetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + + charset = GetTextCharset(hDC); + if (!TranslateCharsetInfo((DWORD *)charset, &csi, TCI_SRCCHARSET)) + { + FIXME("Can't find codepage for charset %d\n", charset); + return 0; + } + if (!GetCPInfo(csi.ciACP, &cpi)) + { + FIXME("Can't find codepage %u info\n", csi.ciACP); + return 0; + } + TRACE("charset %d => codepage %u\n", charset, csi.ciACP); + + total_kern_pairs = GetKerningPairsW(hDC, 0, NULL); + if (!total_kern_pairs) return 0; + + kern_pairW = HeapAlloc(GetProcessHeap(), 0, total_kern_pairs * sizeof(*kern_pairW)); + GetKerningPairsW(hDC, total_kern_pairs, kern_pairW); + + for (i = 0; i < total_kern_pairs; i++) + { + char first, second; + + if (!WideCharToMultiByte(csi.ciACP, 0, &kern_pairW[i].wFirst, 1, &first, 1, NULL, NULL)) + continue; + + if (!WideCharToMultiByte(csi.ciACP, 0, &kern_pairW[i].wSecond, 1, &second, 1, NULL, NULL)) + continue; + + if (first == cpi.DefaultChar[0] || second == cpi.DefaultChar[0]) + continue; + + if (kern_pairA) + { + if (kern_pairs_copied >= cPairs) break; + + kern_pairA->wFirst = (BYTE)first; + kern_pairA->wSecond = (BYTE)second; + kern_pairA->iKernAmount = kern_pairW[i].iKernAmount; + kern_pairA++; + } + kern_pairs_copied++; + } + + HeapFree(GetProcessHeap(), 0, kern_pairW); + + return kern_pairs_copied; +} /************************************************************************* * GetKerningPairsW (GDI32.@) @@ -2526,15 +2583,18 @@ DWORD WINAPI GetKerningPairsA( HDC hDC, DWORD cPairs, DWORD WINAPI GetKerningPairsW( HDC hDC, DWORD cPairs, LPKERNINGPAIR lpKerningPairs ) { - unsigned int i; - FIXME("(%p,%ld,%p): almost empty stub!\n", hDC, cPairs, lpKerningPairs); + DC *dc = DC_GetDCPtr(hDC); + DWORD ret = 0; - if(!lpKerningPairs) /* return the number of kerning pairs */ - return 0; + TRACE("(%p,%ld,%p)\n", hDC, cPairs, lpKerningPairs); - for (i = 0; i < cPairs; i++) - lpKerningPairs[i].iKernAmount = 0; - return 0; + if (!dc) return 0; + + if (dc->gdiFont) + ret = WineEngGetKerningPairs(dc->gdiFont, cPairs, lpKerningPairs); + + GDI_ReleaseObj(hDC); + return ret; } /************************************************************************* diff --git a/dlls/gdi/freetype.c b/dlls/gdi/freetype.c index d3fcf06e254..0e50de7ca47 100644 --- a/dlls/gdi/freetype.c +++ b/dlls/gdi/freetype.c @@ -2,6 +2,7 @@ * FreeType font engine interface * * Copyright 2001 Huw D M Davies for CodeWeavers. + * Copyright 2006 Dmitry Timoshkov for CodeWeavers. * * This file contains the WineEng* functions. * @@ -134,6 +135,7 @@ MAKE_FUNCPTR(FT_Vector_Transform); static void (*pFT_Library_Version)(FT_Library,FT_Int*,FT_Int*,FT_Int*); static FT_Error (*pFT_Load_Sfnt_Table)(FT_Face,FT_ULong,FT_Long,FT_Byte*,FT_ULong*); static FT_ULong (*pFT_Get_First_Char)(FT_Face,FT_UInt*); +static FT_ULong (*pFT_Get_Next_Char)(FT_Face,FT_ULong,FT_UInt*); static FT_TrueTypeEngineType (*pFT_Get_TrueType_Engine_Type)(FT_Library); #ifdef HAVE_FREETYPE_FTWINFNT_H MAKE_FUNCPTR(FT_Get_WinFNT_Header); @@ -268,6 +270,8 @@ struct tagGdiFont { SHORT yMax; SHORT yMin; OUTLINETEXTMETRICW *potm; + DWORD total_kern_pairs; + KERNINGPAIR *kern_pairs; FONTSIGNATURE fs; GdiFont base_font; struct list child_fonts; @@ -1652,6 +1656,7 @@ BOOL WineEngInit(void) pFT_Library_Version = wine_dlsym(ft_handle, "FT_Library_Version", NULL, 0); pFT_Load_Sfnt_Table = wine_dlsym(ft_handle, "FT_Load_Sfnt_Table", NULL, 0); pFT_Get_First_Char = wine_dlsym(ft_handle, "FT_Get_First_Char", NULL, 0); + pFT_Get_Next_Char = wine_dlsym(ft_handle, "FT_Get_Next_Char", NULL, 0); pFT_Get_TrueType_Engine_Type = wine_dlsym(ft_handle, "FT_Get_TrueType_Engine_Type", NULL, 0); #ifdef HAVE_FREETYPE_FTWINFNT_H pFT_Get_WinFNT_Header = wine_dlsym(ft_handle, "FT_Get_WinFNT_Header", NULL, 0); @@ -1938,6 +1943,8 @@ static GdiFont alloc_font(void) ret->gmsize * sizeof(*ret->gm)); ret->potm = NULL; ret->font_desc.matrix.eM11 = ret->font_desc.matrix.eM22 = 1.0; + ret->total_kern_pairs = (DWORD)-1; + ret->kern_pairs = NULL; list_init(&ret->hfontlist); list_init(&ret->child_fonts); return ret; @@ -1966,6 +1973,7 @@ static void free_font(GdiFont font) } if (font->ft_face) pFT_Done_Face(font->ft_face); + HeapFree(GetProcessHeap(), 0, font->kern_pairs); HeapFree(GetProcessHeap(), 0, font->potm); HeapFree(GetProcessHeap(), 0, font->name); HeapFree(GetProcessHeap(), 0, font->gm); @@ -4096,6 +4104,228 @@ BOOL WINAPI GetRasterizerCaps( LPRASTERIZER_STATUS lprs, UINT cbNumBytes) return TRUE; } +/************************************************************************* + * Kerning support for TrueType fonts + */ +#define MS_KERN_TAG MS_MAKE_TAG('k', 'e', 'r', 'n') + +struct TT_kern_table +{ + USHORT version; + USHORT nTables; +}; + +struct TT_kern_subtable +{ + USHORT version; + USHORT length; + union + { + USHORT word; + struct + { + USHORT horizontal : 1; + USHORT minimum : 1; + USHORT cross_stream: 1; + USHORT override : 1; + USHORT reserved1 : 4; + USHORT format : 8; + } bits; + } coverage; +}; + +struct TT_format0_kern_subtable +{ + USHORT nPairs; + USHORT searchRange; + USHORT entrySelector; + USHORT rangeShift; +}; + +struct TT_kern_pair +{ + USHORT left; + USHORT right; + short value; +}; + +static DWORD parse_format0_kern_subtable(GdiFont font, + const struct TT_format0_kern_subtable *tt_f0_ks, + const USHORT *glyph_to_char, + KERNINGPAIR *kern_pair, DWORD cPairs) +{ + USHORT i, nPairs; + const struct TT_kern_pair *tt_kern_pair; + + TRACE("font height %ld, units_per_EM %d\n", font->ppem, font->ft_face->units_per_EM); + + nPairs = GET_BE_WORD(tt_f0_ks->nPairs); + + TRACE("nPairs %u, searchRange %u, entrySelector %u, rangeShift %u\n", + nPairs, GET_BE_WORD(tt_f0_ks->searchRange), + GET_BE_WORD(tt_f0_ks->entrySelector), GET_BE_WORD(tt_f0_ks->rangeShift)); + + if (!kern_pair || !cPairs) + return nPairs; + + tt_kern_pair = (const struct TT_kern_pair *)(tt_f0_ks + 1); + + nPairs = min(nPairs, cPairs); + + for (i = 0; i < nPairs; i++) + { + kern_pair->wFirst = glyph_to_char[GET_BE_WORD(tt_kern_pair[i].left)]; + kern_pair->wSecond = glyph_to_char[GET_BE_WORD(tt_kern_pair[i].right)]; + kern_pair->iKernAmount = MulDiv((short)GET_BE_WORD(tt_kern_pair[i].value), font->ppem, font->ft_face->units_per_EM); + + TRACE("left %u right %u value %d\n", + kern_pair->wFirst, kern_pair->wSecond, kern_pair->iKernAmount); + + kern_pair++; + } + TRACE("copied %u entries\n", nPairs); + return nPairs; +} + +DWORD WineEngGetKerningPairs(GdiFont font, DWORD cPairs, KERNINGPAIR *kern_pair) +{ + DWORD length; + void *buf; + const struct TT_kern_table *tt_kern_table; + const struct TT_kern_subtable *tt_kern_subtable; + USHORT i, nTables; + USHORT *glyph_to_char; + + if (font->total_kern_pairs != (DWORD)-1) + { + if (cPairs && kern_pair) + { + cPairs = min(cPairs, font->total_kern_pairs); + memcpy(kern_pair, font->kern_pairs, cPairs * sizeof(*kern_pair)); + return cPairs; + } + return font->total_kern_pairs; + } + + font->total_kern_pairs = 0; + + length = WineEngGetFontData(font, MS_KERN_TAG, 0, NULL, 0); + + if (length == GDI_ERROR) + { + TRACE("no kerning data in the font\n"); + return 0; + } + + buf = HeapAlloc(GetProcessHeap(), 0, length); + if (!buf) + { + WARN("Out of memory\n"); + return 0; + } + + WineEngGetFontData(font, MS_KERN_TAG, 0, buf, length); + + /* build a glyph index to char code map */ + glyph_to_char = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(USHORT) * 65536); + if (!glyph_to_char) + { + WARN("Out of memory allocating a glyph index to char code map\n"); + HeapFree(GetProcessHeap(), 0, buf); + return 0; + } + + if (font->ft_face->charmap->encoding == FT_ENCODING_UNICODE && pFT_Get_First_Char) + { + FT_UInt glyph_code; + FT_ULong char_code; + + glyph_code = 0; + char_code = pFT_Get_First_Char(font->ft_face, &glyph_code); + + TRACE("face encoding FT_ENCODING_UNICODE, number of glyphs %ld, first glyph %u, first char %lu\n", + font->ft_face->num_glyphs, glyph_code, char_code); + + while (glyph_code) + { + /*TRACE("Char %04lX -> Index %u%s\n", char_code, glyph_code, glyph_to_char[glyph_code] ? " !" : "" );*/ + + /* FIXME: This doesn't match what Windows does: it does some fancy + * things with duplicate glyph index to char code mappings, while + * we just avoid overriding existing entries. + */ + if (glyph_code <= 65535 && !glyph_to_char[glyph_code]) + glyph_to_char[glyph_code] = (USHORT)char_code; + + char_code = pFT_Get_Next_Char(font->ft_face, char_code, &glyph_code); + } + } + else + { + ULONG n; + + FIXME("encoding %u not supported\n", font->ft_face->charmap->encoding); + for (n = 0; n <= 65535; n++) + glyph_to_char[n] = (USHORT)n; + } + + tt_kern_table = buf; + nTables = GET_BE_WORD(tt_kern_table->nTables); + TRACE("version %u, nTables %u\n", + GET_BE_WORD(tt_kern_table->version), nTables); + + tt_kern_subtable = (const struct TT_kern_subtable *)(tt_kern_table + 1); + + for (i = 0; i < nTables; i++) + { + struct TT_kern_subtable tt_kern_subtable_copy; + + tt_kern_subtable_copy.version = GET_BE_WORD(tt_kern_subtable->version); + tt_kern_subtable_copy.length = GET_BE_WORD(tt_kern_subtable->length); + tt_kern_subtable_copy.coverage.word = GET_BE_WORD(tt_kern_subtable->coverage.word); + + TRACE("version %u, length %u, coverage %u, subtable format %u\n", + tt_kern_subtable_copy.version, tt_kern_subtable_copy.length, + tt_kern_subtable_copy.coverage.word, tt_kern_subtable_copy.coverage.bits.format); + + /* According to the TrueType specification this is the only format + * that will be properly interpreted by Windows and OS/2 + */ + if (tt_kern_subtable_copy.coverage.bits.format == 0) + { + DWORD new_chunk, old_total = font->total_kern_pairs; + + new_chunk = parse_format0_kern_subtable(font, (const struct TT_format0_kern_subtable *)(tt_kern_subtable + 1), + glyph_to_char, NULL, 0); + font->total_kern_pairs += new_chunk; + + if (!font->kern_pairs) + font->kern_pairs = HeapAlloc(GetProcessHeap(), 0, + font->total_kern_pairs * sizeof(*font->kern_pairs)); + else + font->kern_pairs = HeapReAlloc(GetProcessHeap(), 0, font->kern_pairs, + font->total_kern_pairs * sizeof(*font->kern_pairs)); + + parse_format0_kern_subtable(font, (const struct TT_format0_kern_subtable *)(tt_kern_subtable + 1), + glyph_to_char, font->kern_pairs + old_total, new_chunk); + } + else + TRACE("skipping kerning table format %u\n", tt_kern_subtable_copy.coverage.bits.format); + + tt_kern_subtable = (const struct TT_kern_subtable *)((const char *)tt_kern_subtable + tt_kern_subtable_copy.length); + } + + HeapFree(GetProcessHeap(), 0, glyph_to_char); + HeapFree(GetProcessHeap(), 0, buf); + + if (cPairs && kern_pair) + { + cPairs = min(cPairs, font->total_kern_pairs); + memcpy(kern_pair, font->kern_pairs, cPairs * sizeof(*kern_pair)); + return cPairs; + } + return font->total_kern_pairs; +} #else /* HAVE_FREETYPE */ @@ -4233,4 +4463,10 @@ BOOL WINAPI GetRasterizerCaps( LPRASTERIZER_STATUS lprs, UINT cbNumBytes) return TRUE; } +DWORD WineEngGetKerningPairs(GdiFont font, DWORD cPairs, KERNINGPAIR *kern_pair) +{ + ERR("called but we don't have FreeType\n"); + return 0; +} + #endif /* HAVE_FREETYPE */ diff --git a/dlls/gdi/gdi_private.h b/dlls/gdi/gdi_private.h index a0de444d133..ad4a78f1476 100644 --- a/dlls/gdi/gdi_private.h +++ b/dlls/gdi/gdi_private.h @@ -376,6 +376,7 @@ extern DWORD WineEngGetGlyphIndices(GdiFont font, LPCWSTR lpstr, INT count, extern DWORD WineEngGetGlyphOutline(GdiFont, UINT glyph, UINT format, LPGLYPHMETRICS, DWORD buflen, LPVOID buf, 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 WineEngGetTextCharsetInfo(GdiFont font, LPFONTSIGNATURE fs, DWORD flags);