[4/5] gdiplus: Fetch font family metrics from truetype font data. Take 2.

Dmitry Timoshkov dmitry at baikal.ru
Fri May 11 05:21:00 CDT 2012


---
 dlls/gdiplus/font.c            |  213 ++++++++++++++++++++++++++++++++--------
 dlls/gdiplus/gdiplus_private.h |    3 +-
 2 files changed, 173 insertions(+), 43 deletions(-)

diff --git a/dlls/gdiplus/font.c b/dlls/gdiplus/font.c
index b08323e..4b059a0 100644
--- a/dlls/gdiplus/font.c
+++ b/dlls/gdiplus/font.c
@@ -34,21 +34,93 @@ WINE_DEFAULT_DEBUG_CHANNEL (gdiplus);
 #include "gdiplus.h"
 #include "gdiplus_private.h"
 
+/* 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 taken 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;
+
+typedef struct
+{
+    ULONG Version;
+    SHORT Ascender;
+    SHORT Descender;
+    SHORT LineGap;
+    USHORT advanceWidthMax;
+    SHORT minLeftSideBearing;
+    SHORT minRightSideBearing;
+    SHORT xMaxExtent;
+    SHORT caretSlopeRise;
+    SHORT caretSlopeRun;
+    SHORT caretOffset;
+    SHORT reserved[4];
+    SHORT metricDataFormat;
+    USHORT numberOfHMetrics;
+} TT_HHEA;
+#include "poppack.h"
+
+#ifdef WORDS_BIGENDIAN
+#define GET_BE_WORD(x) (x)
+#define GET_BE_DWORD(x) (x)
+#else
+#define GET_BE_WORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
+#define GET_BE_DWORD(x) MAKELONG(GET_BE_WORD(HIWORD(x)), GET_BE_WORD(LOWORD(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')
+#define MS_HHEA_TAG MS_MAKE_TAG('h','h','e','a')
+
 static const REAL mm_per_inch = 25.4;
 static const REAL inch_per_point = 1.0/72.0;
 
 static GpFontCollection installedFontCollection = {0};
 
-static inline LONG get_dpi(void)
-{
-    LONG dpi;
-    HDC hdc = GetDC(0);
-    dpi = GetDeviceCaps(hdc, LOGPIXELSY);
-    ReleaseDC (0, hdc);
-
-    return dpi;
-}
-
 static LONG em_size_to_pixel(REAL em_size, Unit unit, LONG dpi)
 {
     switch (unit)
@@ -121,7 +193,7 @@ GpStatus WINGDIPAPI GdipCreateFont(GDIPCONST GpFontFamily *fontFamily,
     stat = GdipGetFamilyName(fontFamily, lfw.lfFaceName, LANG_NEUTRAL);
     if (stat != Ok) return stat;
 
-    lfw.lfHeight = -em_size_to_pixel(emSize, unit, get_dpi());
+    lfw.lfHeight = -em_size_to_pixel(emSize, unit, fontFamily->dpi);
     lfw.lfWeight = style & FontStyleBold ? FW_BOLD : FW_REGULAR;
     lfw.lfItalic = style & FontStyleItalic;
     lfw.lfUnderline = style & FontStyleUnderline;
@@ -466,12 +538,15 @@ GpStatus WINGDIPAPI GdipGetFontHeight(GDIPCONST GpFont *font,
 
     TRACE("%p %p %p\n", font, graphics, height);
 
-    stat = GdipGetDpiY((GpGraphics*)graphics, &dpi);
-
-    if (stat == Ok)
-        stat = GdipGetFontHeightGivenDPI(font, dpi, height);
+    if (graphics)
+    {
+        stat = GdipGetDpiY((GpGraphics*)graphics, &dpi);
+        if (stat != Ok) return stat;
+    }
+    else
+        dpi = font->family->dpi;
 
-    return stat;
+    return GdipGetFontHeightGivenDPI(font, dpi, height);
 }
 
 /*******************************************************************************
@@ -550,35 +625,84 @@ GpStatus WINGDIPAPI GdipGetFontHeightGivenDPI(GDIPCONST GpFont *font, REAL dpi,
 static INT CALLBACK is_font_installed_proc(const LOGFONTW *elf,
                             const TEXTMETRICW *ntm, DWORD type, LPARAM lParam)
 {
-    if (!ntm || type == RASTER_FONTTYPE)
-    {
+    if (type != TRUETYPE_FONTTYPE)
         return 1;
-    }
 
     *(LOGFONTW *)lParam = *elf;
 
     return 0;
 }
 
-static BOOL find_installed_font(const WCHAR *name, OUTLINETEXTMETRICW *otm)
+struct font_metrics
+{
+    UINT16 em_height, ascent, descent, line_spacing; /* in font units */
+    int dpi;
+};
+
+static BOOL get_font_metrics(HDC hdc, struct font_metrics *fm)
+{
+    OUTLINETEXTMETRICW otm;
+    TT_OS2_V2 tt_os2;
+    TT_HHEA tt_hori;
+    LONG size;
+    UINT16 line_gap;
+
+    otm.otmSize = sizeof(otm);
+    if (!GetOutlineTextMetricsW(hdc, otm.otmSize, &otm)) return FALSE;
+
+    fm->em_height = otm.otmEMSquare;
+    fm->dpi = GetDeviceCaps(hdc, LOGPIXELSY);
+
+    memset(&tt_hori, 0, sizeof(tt_hori));
+    if (GetFontData(hdc, MS_HHEA_TAG, 0, &tt_hori, sizeof(tt_hori)) != GDI_ERROR)
+    {
+        fm->ascent = GET_BE_WORD(tt_hori.Ascender);
+        fm->descent = -GET_BE_WORD(tt_hori.Descender);
+        TRACE("hhea: ascent %d, descent %d\n", fm->ascent, fm->descent);
+        line_gap = GET_BE_WORD(tt_hori.LineGap);
+        fm->line_spacing = fm->ascent + fm->descent + line_gap;
+        TRACE("line_gap %u, line_spacing %u\n", line_gap, fm->line_spacing);
+        if (fm->ascent + fm->descent != 0) return TRUE;
+    }
+
+    size = GetFontData(hdc, MS_OS2_TAG, 0, NULL, 0);
+    if (size == GDI_ERROR) return FALSE;
+
+    if (size > sizeof(tt_os2)) size = sizeof(tt_os2);
+
+    memset(&tt_os2, 0, sizeof(tt_os2));
+    if (GetFontData(hdc, MS_OS2_TAG, 0, &tt_os2, size) != size) return FALSE;
+
+    fm->ascent = GET_BE_WORD(tt_os2.usWinAscent);
+    fm->descent = GET_BE_WORD(tt_os2.usWinDescent);
+    TRACE("usWinAscent %u, usWinDescent %u\n", fm->ascent, fm->descent);
+    if (fm->ascent + fm->descent == 0)
+    {
+        fm->ascent = GET_BE_WORD(tt_os2.sTypoAscender);
+        fm->descent = GET_BE_WORD(tt_os2.sTypoDescender);
+        TRACE("sTypoAscender %u, sTypoDescender %u\n", fm->ascent, fm->descent);
+    }
+    line_gap = GET_BE_WORD(tt_os2.sTypoLineGap);
+    fm->line_spacing = fm->ascent + fm->descent + line_gap;
+    TRACE("line_gap %u, line_spacing %u\n", line_gap, fm->line_spacing);
+    return TRUE;
+}
+
+static GpStatus find_installed_font(const WCHAR *name, struct font_metrics *fm)
 {
     LOGFONTW lf;
     HDC hdc = CreateCompatibleDC(0);
-    BOOL ret = FALSE;
+    GpStatus ret = FontFamilyNotFound;
 
     if(!EnumFontFamiliesW(hdc, name, is_font_installed_proc, (LPARAM)&lf))
     {
-        HFONT hfont;
+        HFONT hfont, old_font;
 
-        lf.lfHeight = -2048;
         hfont = CreateFontIndirectW(&lf);
-        hfont = SelectObject(hdc, hfont);
-
-        otm->otmSize = sizeof(*otm);
-        if (GetOutlineTextMetricsW(hdc, otm->otmSize, otm))
-            ret = TRUE;
-
-        DeleteObject(SelectObject(hdc, hfont));
+        old_font = SelectObject(hdc, hfont);
+        ret = get_font_metrics(hdc, fm) ? Ok : NotTrueTypeFont;
+        SelectObject(hdc, old_font);
+        DeleteObject(hfont);
     }
 
     DeleteDC(hdc);
@@ -609,8 +733,9 @@ GpStatus WINGDIPAPI GdipCreateFontFamilyFromName(GDIPCONST WCHAR *name,
                                         GpFontCollection *fontCollection,
                                         GpFontFamily **FontFamily)
 {
+    GpStatus stat;
     GpFontFamily* ffamily;
-    OUTLINETEXTMETRICW otm;
+    struct font_metrics fm;
 
     TRACE("%s, %p %p\n", debugstr_w(name), fontCollection, FontFamily);
 
@@ -619,14 +744,18 @@ GpStatus WINGDIPAPI GdipCreateFontFamilyFromName(GDIPCONST WCHAR *name,
     if (fontCollection)
         FIXME("No support for FontCollections yet!\n");
 
-    if (!find_installed_font(name, &otm))
-        return FontFamilyNotFound;
+    stat = find_installed_font(name, &fm);
+    if (stat != Ok) return stat;
 
     ffamily = GdipAlloc(sizeof (GpFontFamily));
     if (!ffamily) return OutOfMemory;
 
-    ffamily->otm = otm;
     lstrcpynW(ffamily->FamilyName, name, LF_FACESIZE);
+    ffamily->em_height = fm.em_height;
+    ffamily->ascent = fm.ascent;
+    ffamily->descent = fm.descent;
+    ffamily->line_spacing = fm.line_spacing;
+    ffamily->dpi = fm.dpi;
 
     *FontFamily = ffamily;
 
@@ -730,8 +859,8 @@ GpStatus WINGDIPAPI GdipGetCellAscent(GDIPCONST GpFontFamily *family,
 {
     if (!(family && CellAscent)) return InvalidParameter;
 
-    *CellAscent = family->otm.otmTextMetrics.tmAscent;
-    TRACE("%d => %u\n", family->otm.otmTextMetrics.tmHeight, *CellAscent);
+    *CellAscent = family->ascent;
+    TRACE("%s => %u\n", debugstr_w(family->FamilyName), *CellAscent);
 
     return Ok;
 }
@@ -743,8 +872,8 @@ GpStatus WINGDIPAPI GdipGetCellDescent(GDIPCONST GpFontFamily *family,
 
     if (!(family && CellDescent)) return InvalidParameter;
 
-    *CellDescent = family->otm.otmTextMetrics.tmDescent;
-    TRACE("%d => %u\n", family->otm.otmTextMetrics.tmHeight, *CellDescent);
+    *CellDescent = family->descent;
+    TRACE("%s => %u\n", debugstr_w(family->FamilyName), *CellDescent);
 
     return Ok;
 }
@@ -769,8 +898,8 @@ GpStatus WINGDIPAPI GdipGetEmHeight(GDIPCONST GpFontFamily *family, INT style, U
 
     TRACE("%p (%s), %d, %p\n", family, debugstr_w(family->FamilyName), style, EmHeight);
 
-    *EmHeight = family->otm.otmEMSquare;
-    TRACE("%d => %u\n", family->otm.otmTextMetrics.tmHeight, *EmHeight);
+    *EmHeight = family->em_height;
+    TRACE("%s => %u\n", debugstr_w(family->FamilyName), *EmHeight);
 
     return Ok;
 }
@@ -800,8 +929,8 @@ GpStatus WINGDIPAPI GdipGetLineSpacing(GDIPCONST GpFontFamily *family,
 
     if (style) FIXME("ignoring style\n");
 
-    *LineSpacing = family->otm.otmTextMetrics.tmAscent + family->otm.otmTextMetrics.tmDescent + family->otm.otmTextMetrics.tmExternalLeading;
-    TRACE("%d => %u\n", family->otm.otmTextMetrics.tmHeight, *LineSpacing);
+    *LineSpacing = family->line_spacing;
+    TRACE("%s => %u\n", debugstr_w(family->FamilyName), *LineSpacing);
 
     return Ok;
 }
diff --git a/dlls/gdiplus/gdiplus_private.h b/dlls/gdiplus/gdiplus_private.h
index 5267d74..1adfe09 100644
--- a/dlls/gdiplus/gdiplus_private.h
+++ b/dlls/gdiplus/gdiplus_private.h
@@ -375,8 +375,9 @@ struct GpFontCollection{
 };
 
 struct GpFontFamily{
-    OUTLINETEXTMETRICW otm;
     WCHAR FamilyName[LF_FACESIZE];
+    UINT16 em_height, ascent, descent, line_spacing; /* in font units */
+    int dpi;
 };
 
 /* internal use */
-- 
1.7.10.1




More information about the wine-patches mailing list