Implement GetKerningPairs for TrueType fonts
Dmitry Timoshkov
dmitry at codeweavers.com
Mon Sep 18 11:12:10 CDT 2006
Hello,
this code produces kerning values which exactly match what Windows returns,
except that our WideCharToMultiByte silently replaces composite unicode
characters by a decomposed main character, while Windows returns DefaultChar
in that case.
Actually I won't notice that if I wasn't writing GetKerningPairsA
implementation under Windows and compared values returned by it to
the ones returned by Wine.
Changelog:
Implement GetKerningPairs for TrueType fonts.
diff -up cvs/hq/wine/dlls/gdi/font.c wine/dlls/gdi/font.c
--- cvs/hq/wine/dlls/gdi/font.c 2006-08-15 14:51:46.000000000 +0900
+++ wine/dlls/gdi/font.c 2006-09-19 00:52:39.000000000 +0900
@@ -2500,11 +2500,68 @@ BOOL WINAPI CreateScalableFontResourceW(
* 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.@)
@@ -2512,15 +2569,18 @@ DWORD WINAPI GetKerningPairsA( HDC hDC,
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 -up cvs/hq/wine/dlls/gdi/freetype.c wine/dlls/gdi/freetype.c
--- cvs/hq/wine/dlls/gdi/freetype.c 2006-08-25 13:23:17.000000000 +0900
+++ wine/dlls/gdi/freetype.c 2006-09-19 01:00:00.000000000 +0900
@@ -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;
@@ -1645,6 +1649,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);
@@ -1931,6 +1936,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;
@@ -4103,6 +4110,228 @@ BOOL WINAPI GetRasterizerCaps( LPRASTERI
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, struct TT_format0_kern_subtable *tt_f0_ks,
+ USHORT *glyph_to_char,
+ KERNINGPAIR *kern_pair, DWORD cPairs)
+{
+ USHORT i, kern_pairs_copied;
+ struct TT_kern_pair *tt_kern_pair;
+
+ TRACE("font height %ld, units_per_EM %d\n", font->ppem, font->ft_face->units_per_EM);
+
+ tt_f0_ks->nPairs = GET_BE_WORD(tt_f0_ks->nPairs);
+ tt_f0_ks->searchRange = GET_BE_WORD(tt_f0_ks->searchRange);
+ tt_f0_ks->entrySelector = GET_BE_WORD(tt_f0_ks->entrySelector);
+ tt_f0_ks->rangeShift = GET_BE_WORD(tt_f0_ks->rangeShift);
+
+ TRACE("nPairs %u, searchRange %u, entrySelector %u, rangeShift %u\n",
+ tt_f0_ks->nPairs, tt_f0_ks->searchRange, tt_f0_ks->entrySelector, tt_f0_ks->rangeShift);
+
+ if (!kern_pair || !cPairs)
+ return tt_f0_ks->nPairs;
+
+ tt_kern_pair = (struct TT_kern_pair *)(tt_f0_ks + 1);
+
+ kern_pairs_copied = 0;
+
+ for (i = 0; i < tt_f0_ks->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++;
+ kern_pairs_copied++;
+ if (!--cPairs) break;
+ }
+ TRACE("copied %u entries\n", kern_pairs_copied);
+ return kern_pairs_copied;
+}
+
+DWORD WineEngGetKerningPairs(GdiFont font, DWORD cPairs, KERNINGPAIR *kern_pair)
+{
+ DWORD length;
+ void *buf;
+ struct TT_kern_table *tt_kern_table;
+ struct TT_kern_subtable *tt_kern_subtable;
+ USHORT i;
+ 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 < 65536 && !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 < 65536; n++)
+ glyph_to_char[n] = (USHORT)n;
+ }
+
+ tt_kern_table = buf;
+ tt_kern_table->version = GET_BE_WORD(tt_kern_table->version);
+ tt_kern_table->nTables = GET_BE_WORD(tt_kern_table->nTables);
+ TRACE("version %u, nTables %u\n",
+ tt_kern_table->version, tt_kern_table->nTables);
+
+ tt_kern_subtable = (struct TT_kern_subtable *)(tt_kern_table + 1);
+
+ for (i = 0; i < tt_kern_table->nTables; i++)
+ {
+ tt_kern_subtable->version = GET_BE_WORD(tt_kern_subtable->version);
+ tt_kern_subtable->length = GET_BE_WORD(tt_kern_subtable->length);
+ tt_kern_subtable->coverage.word = GET_BE_WORD(tt_kern_subtable->coverage.word);
+
+ TRACE("version %u, length %u, coverage %u, subtable format %u\n",
+ tt_kern_subtable->version, tt_kern_subtable->length,
+ tt_kern_subtable->coverage.word, tt_kern_subtable->coverage.bits.format);
+
+ /* Accordibg to the TrueType specification this is the only format
+ * that will be properly interpreted by Windows and OS/2
+ */
+ if (tt_kern_subtable->coverage.bits.format == 0)
+ {
+ DWORD new_chunk, old_total = font->total_kern_pairs;
+
+ new_chunk = parse_format0_kern_subtable(font, (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, (struct TT_format0_kern_subtable *)(tt_kern_subtable + 1),
+ glyph_to_char, font->kern_pairs + old_total, new_chunk);
+ }
+
+ tt_kern_subtable = (struct TT_kern_subtable *)((char *)tt_kern_subtable + tt_kern_subtable->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 */
@@ -4240,4 +4469,10 @@ BOOL WINAPI GetRasterizerCaps( LPRASTERI
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 -up cvs/hq/wine/dlls/gdi/gdi_private.h wine/dlls/gdi/gdi_private.h
--- cvs/hq/wine/dlls/gdi/gdi_private.h 2006-07-13 12:50:14.000000000 +0900
+++ wine/dlls/gdi/gdi_private.h 2006-09-18 22:00:35.000000000 +0900
@@ -376,6 +376,7 @@ extern DWORD WineEngGetGlyphIndices(GdiF
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);
More information about the wine-patches
mailing list