Fix shell32.ExtractIcon with A->W and 16 -> 32 calls.
[wine] / dlls / wineps / truetype.c
index 5f851ca..573c7ee 100644 (file)
@@ -4,8 +4,29 @@
  *
  *  Copyright 2001  Ian Pilcher
  *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  NOTE:  Many of the functions in this file can return either fatal errors
+ *     (memory allocation failure or unexpected FreeType error) or non-fatal
+ *     errors (unusable font file).  Fatal errors are indicated by returning
+ *     FALSE; see individual function descriptions for how they indicate non-
+ *     fatal errors.
+ *
  */
 #include "config.h"
+#include "wine/port.h"
 
 #ifdef HAVE_FREETYPE
 
 #ifdef HAVE_FREETYPE_TTTABLES_H
 #include <freetype/tttables.h>
 #endif
-#ifdef HAVE_FREETYPE_FTNAMES_H
-#include <freetype/ftnames.h>
-#endif
 #ifdef HAVE_FREETYPE_FTSNAMES_H
 #include <freetype/ftsnames.h>
+#else
+# ifdef HAVE_FREETYPE_FTNAMES_H
+# include <freetype/ftnames.h>
+# endif
 #endif
 #ifdef HAVE_FREETYPE_TTNAMEID_H
 #include <freetype/ttnameid.h>
 #include <dirent.h>
 #include <string.h>
 #include <stdio.h>
+#include <errno.h>
 
-#include "winnt.h"
-#include "options.h"
+#include "windef.h"
+#include "winerror.h"
+#include "winreg.h"
 #include "psdrv.h"
-#include "debugtools.h"
-#include "heap.h"
-
-DEFAULT_DEBUG_CHANNEL(psdrv);
+#include "wine/debug.h"
 
+WINE_DEFAULT_DEBUG_CHANNEL(psdrv);
 
 #define REQUIRED_FACE_FLAGS    (   FT_FACE_FLAG_SCALABLE   |   \
                                    FT_FACE_FLAG_HORIZONTAL |   \
                                    FT_FACE_FLAG_SFNT       |   \
                                    FT_FACE_FLAG_GLYPH_NAMES    )
 
-static FT_Library      library;
-static FT_Face         face;
-static FT_CharMap      charmap;
-static TT_Header       *head;
-static TT_Postscript   *post;
-static TT_OS2          *os2;
-static TT_HoriHeader   *hhea;
+#define GLYPH_LOAD_FLAGS       (   FT_LOAD_NO_SCALE            |   \
+                                   FT_LOAD_IGNORE_TRANSFORM    |   \
+                                   FT_LOAD_LINEAR_DESIGN           )
+
+#ifndef SONAME_LIBFREETYPE
+#define SONAME_LIBFREETYPE "libfreetype.so"
+#endif
+
+static void *ft_handle = NULL;
+
+#define MAKE_FUNCPTR(f) static typeof(f) * p##f = NULL;
+MAKE_FUNCPTR(FT_Done_Face)
+MAKE_FUNCPTR(FT_Done_FreeType)
+MAKE_FUNCPTR(FT_Get_Char_Index)
+MAKE_FUNCPTR(FT_Get_Glyph_Name)
+MAKE_FUNCPTR(FT_Get_Sfnt_Name)
+MAKE_FUNCPTR(FT_Get_Sfnt_Name_Count)
+MAKE_FUNCPTR(FT_Get_Sfnt_Table)
+MAKE_FUNCPTR(FT_Init_FreeType)
+MAKE_FUNCPTR(FT_Load_Glyph)
+MAKE_FUNCPTR(FT_New_Face)
+MAKE_FUNCPTR(FT_Set_Charmap)
+#undef MAKE_FUNCPTR
 
 /*******************************************************************************
- *
  *  FindCharMap
  *
- *  Sets charmap and points afm->EncodingScheme to encoding name (in driver
- *  heap).  Leaves both uninitialized if font contains no Windows encoding.
+ *  Finds Windows character map and creates "EncodingScheme" string.  Returns
+ *  FALSE to indicate memory allocation or FreeType error; sets *p_charmap to
+ *  NULL if no Windows encoding is present.
  *
- *  Returns FALSE to indicate memory allocation error.
+ *  Returns Unicode character map if present; otherwise uses the first Windows
+ *  character map found.
  *
  */
-static const char *encoding_names[7] =
+static const LPCSTR encoding_names[7] =
 {
     "WindowsSymbol",       /* TT_MS_ID_SYMBOL_CS */
     "WindowsUnicode",      /* TT_MS_ID_UNICODE_CS */
@@ -79,248 +118,202 @@ static const char *encoding_names[7] =
     "WindowsBig5",         /* TT_MS_ID_BIG_5 */
     "WindowsWansung",      /* TT_MS_ID_WANSUNG */
     "WindowsJohab"         /* TT_MS_ID_JOHAB */
+/*  "WindowsUnknown65535"   is the longest possible (encoding_id is a UShort) */
 };
-static BOOL FindCharMap(AFM *afm)
+
+static BOOL FindCharMap(FT_Face face, FT_CharMap *p_charmap, LPSTR *p_sz)
 {
     FT_Int     i;
     FT_Error   error;
-    
-    charmap = NULL;
-    
+    FT_CharMap charmap = NULL;
+
     for (i = 0; i < face->num_charmaps; ++i)
     {
        if (face->charmaps[i]->platform_id != TT_PLATFORM_MICROSOFT)
            continue;
-           
+
        if (face->charmaps[i]->encoding_id == TT_MS_ID_UNICODE_CS)
-       {
+       {
            charmap = face->charmaps[i];
            break;
        }
-           
+
        if (charmap == NULL)
            charmap = face->charmaps[i];
     }
-    
+
+    *p_charmap = charmap;
+
     if (charmap == NULL)
-       return TRUE;
-       
-    error = FT_Set_Charmap(face, charmap);
+    {
+       WARN("No Windows character map found\n");
+       return TRUE;
+    }
+
+    error = pFT_Set_Charmap(face, charmap);
     if (error != FT_Err_Ok)
     {
-       ERR("%s returned %i\n", "FT_Set_CharMap", error);
+       ERR("%s returned %i\n", "FT_Set_Charmap", error);
        return FALSE;
     }
-       
+
+    *p_sz = HeapAlloc(PSDRV_Heap, 0, sizeof("WindowsUnknown65535"));
+    if (*p_sz == NULL)
+       return FALSE;
+
     if (charmap->encoding_id < 7)
-    {
-       afm->EncodingScheme = HEAP_strdupA(PSDRV_Heap, 0,
-               encoding_names[charmap->encoding_id]);
-       if (afm->EncodingScheme == NULL)
-           return FALSE;
-    }
+       strcpy(*p_sz, encoding_names[charmap->encoding_id]);
     else
-    {
-       afm->EncodingScheme = HeapAlloc(PSDRV_Heap, 0,      /* encoding_id   */
-               sizeof("WindowsUnknown65535"));             /*   is a UShort */
-       if (afm->EncodingScheme == NULL)
-           return FALSE;
-           
-       sprintf(afm->EncodingScheme, "%s%u", "WindowsUnknown",
-               charmap->encoding_id);
-    }
-    
+       sprintf(*p_sz, "%s%u", "WindowsUnknown", charmap->encoding_id);
+
     return TRUE;
 }
 
 /*******************************************************************************
- *  NameTableString
- *
- *  Converts a name table string to a null-terminated character string.  The
- *  space for the character string is allocated from the driver heap.
+ *  MSTTStrToSz
  *
- *  This function handles only platform_id = 3 (TT_PLATFORM_MICROSOFT) -- 16-bit
- *  big-endian strings.  It also only handles ASCII character codes (< 128).
+ *  Converts a string in the TrueType NAME table to a null-terminated ASCII
+ *  character string.  Space for the string is allocated from the driver heap.
+ *  Only handles platform_id = 3 (TT_PLATFORM_MICROSOFT) strings (16-bit, big
+ *  endian).  It also only handles ASCII character codes (< 128).
  *
- *  This function will set *sz to NULL if it cannot parse the string, but it
- *  will only return FALSE in the event of an unexpected error (memory
- *  allocation failure).
+ *  Sets *p_sz to NULL if string cannot be converted; only returns FALSE for
+ *  memory allocation failure.
  *
  */
-static BOOL NameTableString(LPSTR *sz, const FT_SfntName *name)
-{    
-    FT_UShort  i, len, *ws;
-    LPSTR      s;
-    
-    if (name->platform_id != TT_PLATFORM_MICROSOFT)
-    {
-       ERR("Unsupported encoding %i\n", name->platform_id);
-       return FALSE;       /* should never get here */
-    }
-    
-    len = name->string_len / 2;
-    s = *sz = HeapAlloc(PSDRV_Heap, 0, len + 1);
-    if (s == NULL)
+static BOOL MSTTStrToSz(const FT_SfntName *name, LPSTR *p_sz)
+{
+    FT_UShort  i;
+    INT        len;
+    BYTE       *wsz;
+    LPSTR      sz;
+
+    len = name->string_len / 2;                    /* # of 16-bit chars */
+
+    *p_sz = sz = HeapAlloc(PSDRV_Heap, 0, len + 1);
+    if (sz == NULL)
        return FALSE;
-    ws = (FT_UShort *)(name->string);
-       
-    for (i = 0; i < len; ++i, ++s, ++ws)
+
+    wsz = (BYTE *)name->string;
+
+    for (i = 0; i < len; ++i, ++sz)
     {
-       FT_UShort   wc = *ws;
-       
-#ifndef WORDS_BIGENDIAN
-       wc = (wc >> 8) | (wc << 8);
-#endif
+        USHORT wc = (wsz[0] << 8) + wsz[1];
+        wsz += 2;
 
        if (wc > 127)
        {
            WARN("Non-ASCII character 0x%.4x\n", wc);
-           HeapFree(PSDRV_Heap, 0, *sz);
-           *sz = NULL;
+           HeapFree(PSDRV_Heap, 0, *p_sz);
+           *p_sz = NULL;
            return TRUE;
        }
-       
-       *s = (CHAR)wc;
+
+       *sz = (CHAR)wc;
     }
-    
-    *s = '\0';
+
+    *sz = '\0';
+
     return TRUE;
 }
 
 /*******************************************************************************
- *  ReadNameTable
+ *  FindMSTTString
  *
- *  Reads various font names from the TrueType 'NAME' table.  Currently looks
- *  for U.S. English names only,
+ *  Finds the requested Microsoft platform string in the TrueType NAME table and
+ *  converts it to a null-terminated ASCII string.  Currently looks for U.S.
+ *  English names only.
  *
- *  May leave a pointer uninitialized if the desired string is not present;
- *  returns FALSE only in the event of an unexpected error.
+ *  Sets string to NULL if not present or cannot be converted; returns FALSE
+ *  only for memory allocation failure.
  *
  */
-static BOOL ReadNameTable(AFM *afm)
+static BOOL FindMSTTString(FT_Face face, FT_CharMap charmap, FT_UShort name_id,
+       LPSTR *p_sz)
 {
-    FT_UInt    numStrings, stringIndex;
-    FT_SfntName name;
-    FT_Error   error;
+    FT_UInt        num_strings, string_index;
+    FT_SfntName     name;
+    FT_Error       error;
+
+    num_strings = pFT_Get_Sfnt_Name_Count(face);
 
-    numStrings = FT_Get_Sfnt_Name_Count(face);
-    
-    for (stringIndex = 0; stringIndex < numStrings; ++stringIndex)
+    for (string_index = 0; string_index < num_strings; ++string_index)
     {
-       error = FT_Get_Sfnt_Name(face, stringIndex, &name);
+       error = pFT_Get_Sfnt_Name(face, string_index, &name);
        if (error != FT_Err_Ok)
        {
            ERR("%s returned %i\n", "FT_Get_Sfnt_Name", error);
            return FALSE;
        }
-       
+
        /* FIXME - Handle other languages? */
-       
-       if (name.language_id != TT_MS_LANGID_ENGLISH_UNITED_STATES ||
-               name.platform_id != charmap->platform_id ||
+
+       if (name.platform_id != TT_PLATFORM_MICROSOFT ||
+               name.language_id != TT_MS_LANGID_ENGLISH_UNITED_STATES)
+           continue;
+
+       if (name.platform_id != charmap->platform_id ||
                name.encoding_id != charmap->encoding_id)
            continue;
-       
-       switch (name.name_id)
-       {
-           case TT_NAME_ID_FONT_FAMILY:
-           
-               if (NameTableString(&(afm->FamilyName), &name) == FALSE)
-                   return FALSE;
-               break;
-           
-           case TT_NAME_ID_FULL_NAME:
-           
-               if (NameTableString(&(afm->FullName), &name) == FALSE)
-                   return FALSE;
-               break;
-           
-           case TT_NAME_ID_PS_NAME:
-           
-               if (NameTableString(&(afm->FontName), &name) == FALSE)
-                   return FALSE;
-               break;
-       }
+
+       if (name.name_id != name_id)
+           continue;
+
+       return MSTTStrToSz(&name, p_sz);
     }
-    
-    return TRUE;
-}
 
-/*******************************************************************************
- *  FreeAFM
- *
- *  Frees an AFM and all subsidiary objects.  For this function to work
- *  properly, the AFM must have been allocated with HEAP_ZERO_MEMORY, and the
- *  UNICODEVECTOR and it's associated array of UNICODEGLYPHs must have been
- *  allocated as a single object.
- */
-static void FreeAFM(AFM *afm)
-{
-    if (afm->FontName != NULL)
-       HeapFree(PSDRV_Heap, 0, afm->FontName);
-    if (afm->FullName != NULL)
-       HeapFree(PSDRV_Heap, 0, afm->FullName);
-    if (afm->FamilyName != NULL)
-       HeapFree(PSDRV_Heap, 0, afm->FamilyName);
-    if (afm->EncodingScheme != NULL)
-       HeapFree(PSDRV_Heap, 0, afm->EncodingScheme);
-    if (afm->Metrics != NULL)
-       HeapFree(PSDRV_Heap, 0, afm->Metrics);
-    if (afm->Encoding != NULL)
-       HeapFree(PSDRV_Heap, 0, afm->Encoding);
-       
-    HeapFree(PSDRV_Heap, 0, afm);
+    *p_sz = NULL;                  /* didn't find it */
+
+    return TRUE;
 }
 
 /*******************************************************************************
  *  PSUnits
  *
  *  Convert TrueType font units (relative to font em square) to PostScript
- *  units.  This is defined as a macro, so it can handle different TrueType
- *  data types as inputs.
+ *  units.
  *
  */
-#define PSUnits(x)  (((float)(x)) * 1000.0 / ((float)(head->Units_Per_EM)))
+inline static float PSUnits(LONG x, USHORT em_size)
+{
+    return 1000.0 * (float)x / (float)em_size;
+}
 
 /*******************************************************************************
- *  ReadMetricsTables
+ *  StartAFM
  *
- *  Reads basic font metrics from the 'head', 'post', and 'OS/2' tables.
- *  Returns FALSE if any table is missing.
+ *  Allocates space for the AFM on the driver heap and reads basic font metrics
+ *  from the HEAD, POST, HHEA, and OS/2 tables.  Returns FALSE for memory
+ *  allocation error; sets *p_afm to NULL if required information is missing.
  *
- */ 
-static BOOL ReadMetricsTables(AFM *afm)
+ */
+static BOOL StartAFM(FT_Face face, AFM **p_afm)
 {
-    head = FT_Get_Sfnt_Table(face, ft_sfnt_head);
-    post = FT_Get_Sfnt_Table(face, ft_sfnt_post);
-    hhea = FT_Get_Sfnt_Table(face, ft_sfnt_hhea);
-    os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2);
-    
-    if (head == NULL || post == NULL || hhea == NULL || os2 == NULL)
-       return FALSE;
-    
-    if (os2->version == 0xffff)            /* Old Macintosh font */
-       return FALSE;
-    
-    afm->Weight = os2->usWeightClass;
-    afm->ItalicAngle = ((float)(post->italicAngle)) / 65536.0;
-    afm->IsFixedPitch = (post->isFixedPitch == 0) ? FALSE : TRUE;
-    afm->UnderlinePosition = PSUnits(post->underlinePosition);
-    afm->UnderlineThickness = PSUnits(post->underlineThickness);
-           
-    afm->FontBBox.llx = PSUnits(head->xMin);
-    afm->FontBBox.lly = PSUnits(head->yMin);
-    afm->FontBBox.urx = PSUnits(head->xMax);
-    afm->FontBBox.ury = PSUnits(head->yMax);
-    
-    /* CapHeight & XHeight set by ReadCharMetrics */
-    
-    afm->Ascender = PSUnits(os2->sTypoAscender);
-    afm->Descender = PSUnits(os2->sTypoDescender);
-    afm->FullAscender = afm->FontBBox.ury;         /* get rid of this */
-    
-    afm->WinMetrics.usUnitsPerEm = head->Units_Per_EM;
+    TT_Header      *head;
+    TT_Postscript   *post;
+    TT_OS2         *os2;
+    TT_HoriHeader   *hhea;
+    USHORT         em_size;
+    AFM            *afm;
+
+    head = pFT_Get_Sfnt_Table(face, ft_sfnt_head);
+    post = pFT_Get_Sfnt_Table(face, ft_sfnt_post);
+    os2 = pFT_Get_Sfnt_Table(face, ft_sfnt_os2);
+    hhea = pFT_Get_Sfnt_Table(face, ft_sfnt_hhea);
+
+    if (head == NULL || post == NULL || os2 == NULL || hhea == NULL ||
+           os2->version == 0xffff)                     /* old Macintosh font */
+    {
+       WARN("Required table(s) missing\n");
+       *p_afm = NULL;
+       return TRUE;
+    }
+
+    *p_afm = afm = HeapAlloc(PSDRV_Heap, 0, sizeof(*afm));
+    if (afm == NULL)
+       return FALSE;
+
+    afm->WinMetrics.usUnitsPerEm = em_size = head->Units_Per_EM;
     afm->WinMetrics.sAscender = hhea->Ascender;
     afm->WinMetrics.sDescender = hhea->Descender;
     afm->WinMetrics.sLineGap = hhea->Line_Gap;
@@ -329,300 +322,343 @@ static BOOL ReadMetricsTables(AFM *afm)
     afm->WinMetrics.sTypoLineGap = os2->sTypoLineGap;
     afm->WinMetrics.usWinAscent = os2->usWinAscent;
     afm->WinMetrics.usWinDescent = os2->usWinDescent;
-    
+    afm->WinMetrics.sAvgCharWidth = os2->xAvgCharWidth;
+
+    afm->Weight = os2->usWeightClass;
+    afm->ItalicAngle = ((float)(post->italicAngle)) / 65536.0;
+    afm->IsFixedPitch = (post-> isFixedPitch == 0) ? FALSE : TRUE;
+    afm->UnderlinePosition = PSUnits(post->underlinePosition, em_size);
+    afm->UnderlineThickness = PSUnits(post->underlineThickness, em_size);
+
+    afm->FontBBox.llx = PSUnits(head->xMin, em_size);
+    afm->FontBBox.lly = PSUnits(head->yMin, em_size);
+    afm->FontBBox.urx = PSUnits(head->xMax, em_size);
+    afm->FontBBox.ury = PSUnits(head->yMax, em_size);
+
+    afm->Ascender = PSUnits(os2->sTypoAscender, em_size);
+    afm->Descender = PSUnits(os2->sTypoDescender, em_size);
+
     return TRUE;
 }
 
 /*******************************************************************************
  *  ReadCharMetrics
  *
- *  Reads metrics for each glyph in a TrueType font.  Since FreeAFM will try to
- *  free afm->Metrics and afm->Encoding if they are non-NULL, don't free them
- *  in the event of an error.  (FreeAFM depends on the fact that afm->Encoding
- *  and its associated array of UNICODEGLYPHs are allocated as a single object.)
+ *  Reads metrics for each glyph in a TrueType font.  Returns false for memory
+ *  allocation or FreeType error; sets *p_metrics to NULL for non-fatal error.
  *
  */
-static BOOL ReadCharMetrics(AFM *afm)
+static BOOL ReadCharMetrics(FT_Face face, AFM *afm, AFMMETRICS **p_metrics)
 {
-    FT_ULong       charcode, index;
-    UNICODEGLYPH    *glyphs;
-    
-    /*
-     * There does not seem to be an easy way to get the number of characters
-     * in an encoding out of a TrueType font.
-     */
+    FT_ULong   charcode, index;
+    AFMMETRICS *metrics;
+    USHORT     em_size = afm->WinMetrics.usUnitsPerEm;
+
     for (charcode = 0, index = 0; charcode < 65536; ++charcode)
-    {
-       if (FT_Get_Char_Index(face, charcode) != 0)
-           ++index;
-    }
-    
+       if (pFT_Get_Char_Index(face, charcode) != 0)
+           ++index;                                    /* count # of glyphs */
+
     afm->NumofMetrics = index;
-    
-    afm->Metrics = HeapAlloc(PSDRV_Heap, 0, index * sizeof(AFMMETRICS));
-    afm->Encoding = HeapAlloc(PSDRV_Heap, 0, sizeof(UNICODEVECTOR) +
-           index * sizeof(UNICODEGLYPH));
-    if (afm->Metrics == NULL || afm->Encoding == NULL)
+
+    *p_metrics = metrics = HeapAlloc(PSDRV_Heap, 0, index * sizeof(*metrics));
+    if (metrics == NULL)
        return FALSE;
-       
-    glyphs = (UNICODEGLYPH *)(afm->Encoding + 1);
-    afm->Encoding->size = index;
-    afm->Encoding->glyphs = glyphs;
-    
-    for (charcode = 0, index = 0; charcode <= 65536; ++charcode)
+
+    for (charcode = 0, index = 0; charcode < 65536; ++charcode)
     {
-       FT_UInt     glyph_index = FT_Get_Char_Index(face, charcode);
+       FT_UInt     glyph_index = pFT_Get_Char_Index(face, charcode);
        FT_Error    error;
-       FT_Glyph    glyph;
-       FT_BBox     bbox;
-       char        buffer[256];
-       
+       CHAR        buffer[128];                /* for glyph names */
+
        if (glyph_index == 0)
            continue;
-           
-       error = FT_Load_Glyph(face, glyph_index, FT_LOAD_NO_SCALE |
-               FT_LOAD_IGNORE_TRANSFORM | FT_LOAD_LINEAR_DESIGN);
+
+       error = pFT_Load_Glyph(face, glyph_index, GLYPH_LOAD_FLAGS);
        if (error != FT_Err_Ok)
        {
            ERR("%s returned %i\n", "FT_Load_Glyph", error);
-           return FALSE;
+           goto cleanup;
        }
-       
-       error = FT_Get_Glyph(face->glyph, &glyph);
-       if (error != FT_Err_Ok)
-       {
-           ERR("%s returned %i\n", "FT_Get_Glyph", error);
-           return FALSE;
-       }
-       
-       FT_Glyph_Get_CBox(glyph, ft_glyph_bbox_unscaled, &bbox);
-       
-       error = FT_Get_Glyph_Name(face, glyph_index, buffer, 255);
+
+       error = pFT_Get_Glyph_Name(face, glyph_index, buffer, sizeof(buffer));
        if (error != FT_Err_Ok)
        {
            ERR("%s returned %i\n", "FT_Get_Glyph_Name", error);
-           return FALSE;
-       }
-       
-       afm->Metrics[index].N = PSDRV_GlyphName(buffer);
-       if (afm->Metrics[index].N == NULL)
-           return FALSE;
-       
-       afm->Metrics[index].C = charcode;
-       afm->Metrics[index].UV = charcode;
-       afm->Metrics[index].WX = PSUnits(face->glyph->metrics.horiAdvance);
-       afm->Metrics[index].B.llx = PSUnits(bbox.xMin);
-       afm->Metrics[index].B.lly = PSUnits(bbox.yMin);
-       afm->Metrics[index].B.urx = PSUnits(bbox.xMax);
-       afm->Metrics[index].B.ury = PSUnits(bbox.yMax);
-       afm->Metrics[index].L = NULL;
-       
-       TRACE("Metrics for '%s' WX = %f B = %f,%f - %f,%f\n",
-               afm->Metrics[index].N->sz, afm->Metrics[index].WX,
-               afm->Metrics[index].B.llx, afm->Metrics[index].B.lly,
-               afm->Metrics[index].B.urx, afm->Metrics[index].B.ury);
-       
-       glyphs[index].UV = charcode;
-       glyphs[index].name = afm->Metrics[index].N;
-       
-       if (charcode == 0x0048)                     /* 'H' */
-           afm->CapHeight = PSUnits(bbox.yMax);
-       if (charcode == 0x0078)                     /* 'x' */
-           afm->XHeight = PSUnits(bbox.yMax);
-               
+           goto cleanup;
+       }
+
+       metrics[index].N = PSDRV_GlyphName(buffer);
+       if (metrics[index].N == NULL)
+           goto cleanup;
+
+       metrics[index].C = metrics[index].UV = charcode;
+       metrics[index].WX = PSUnits(face->glyph->metrics.horiAdvance, em_size);
+
        ++index;
     }
-    
+
+    if (afm->WinMetrics.sAvgCharWidth == 0)
+       afm->WinMetrics.sAvgCharWidth = PSDRV_CalcAvgCharWidth(afm);
+
     return TRUE;
+
+    cleanup:
+       HeapFree(PSDRV_Heap, 0, metrics);
+
+    return FALSE;
 }
 
 /*******************************************************************************
- *  ReadTrueTypeAFM
+ *  BuildTrueTypeAFM
  *
- *  Fills in AFM structure for opened TrueType font file.  Returns FALSE only on
- *  an unexpected error (memory allocation failure or FreeType error); otherwise
- *  returns TRUE.  Leaves it to the caller (ReadTrueTypeFile) to clean up.
+ *  Builds the AFM for a TrueType font and adds it to the driver font list.
+ *  Returns FALSE only on an unexpected error (memory allocation failure or
+ *  FreeType error).
  *
  */
-static BOOL ReadTrueTypeAFM(AFM *afm)
+static BOOL BuildTrueTypeAFM(FT_Face face)
 {
+    AFM        *afm;
+    AFMMETRICS *metrics;
+    LPSTR      font_name, full_name, family_name, encoding_scheme;
+    FT_CharMap charmap;
+    BOOL       retval, added;
 
-    if ((face->face_flags & REQUIRED_FACE_FLAGS) != REQUIRED_FACE_FLAGS)
-    {
-       WARN("Font flags do not match requirements\n");
-       return TRUE;
-    }
-    
-    if (FindCharMap(afm) == FALSE)
-       return FALSE;
-       
-    if (charmap == NULL)
-    {
-       WARN("No Windows encodings in font\n");
-       return TRUE;
-    }
-    
-    TRACE("Using encoding '%s'\n", afm->EncodingScheme);
+    retval = StartAFM(face, &afm);
+    if (retval == FALSE || afm == NULL)
+       return retval;
 
-    if (ReadNameTable(afm) == FALSE)
-       return FALSE;
-   
-    if (afm->FamilyName == NULL || afm->FullName == NULL ||
-           afm->FontName == NULL)
-    {
-       WARN("Required strings missing from font\n");
-       return TRUE;
-    }
+    retval = FindCharMap(face, &charmap, &encoding_scheme);
+    if (retval == FALSE || charmap == NULL)
+       goto cleanup_afm;
 
-    if (ReadMetricsTables(afm) == FALSE)    /* Non-fatal */
-    {
-       WARN("Required metrics tables missing from font\n");
-       return TRUE;
-    }
-    
-    if (ReadCharMetrics(afm) == FALSE)
-       return FALSE;
+    retval = FindMSTTString(face, charmap, TT_NAME_ID_PS_NAME, &font_name);
+    if (retval == FALSE || font_name == NULL)
+       goto cleanup_encoding_scheme;
+
+    retval = FindMSTTString(face, charmap, TT_NAME_ID_FULL_NAME, &full_name);
+    if (retval == FALSE || full_name == NULL)
+       goto cleanup_font_name;
+
+    retval = FindMSTTString(face, charmap, TT_NAME_ID_FONT_FAMILY,
+           &family_name);
+    if (retval == FALSE || family_name == NULL)
+       goto cleanup_full_name;
+
+    retval = ReadCharMetrics(face, afm, &metrics);
+    if (retval == FALSE || metrics == NULL)
+       goto cleanup_family_name;
+
+    afm->EncodingScheme = encoding_scheme; afm->FontName = font_name;
+    afm->FullName = full_name; afm->FamilyName = family_name;
+    afm->Metrics = metrics;
+
+    retval = PSDRV_AddAFMtoList(&PSDRV_AFMFontList, afm, &added);
+    if (retval == FALSE || added == FALSE)
+       goto cleanup_family_name;
 
     return TRUE;
+
+    /* clean up after fatal or non-fatal errors */
+
+    cleanup_family_name:
+       HeapFree(PSDRV_Heap, 0, family_name);
+    cleanup_full_name:
+       HeapFree(PSDRV_Heap, 0, full_name);
+    cleanup_font_name:
+       HeapFree(PSDRV_Heap, 0, font_name);
+    cleanup_encoding_scheme:
+       HeapFree(PSDRV_Heap, 0, encoding_scheme);
+    cleanup_afm:
+       HeapFree(PSDRV_Heap, 0, afm);
+
+    return retval;
 }
 
 /*******************************************************************************
  *  ReadTrueTypeFile
  *
- *  Reads PostScript-style font metrics from a TrueType font file.  Only returns
- *  FALSE for unexpected errors (memory allocation, etc.); returns TRUE if it's
- *  just a bad font file.
+ *  Reads font metrics from TrueType font file.  Only returns FALSE for
+ *  unexpected errors (memory allocation failure or FreeType error).
  *
  */
-static BOOL ReadTrueTypeFile(LPCSTR filename)
+static BOOL ReadTrueTypeFile(FT_Library library, LPCSTR filename)
 {
-    FT_Error   error;
-    AFM        *afm;
-    
-    TRACE("'%s'\n", filename);
+    FT_Error       error;
+    FT_Face        face;
 
-    afm = HeapAlloc(PSDRV_Heap, HEAP_ZERO_MEMORY, sizeof(AFM));
-    if (afm == NULL)
-       return FALSE;
-
-    error = FT_New_Face(library, filename, 0, &face);
+    TRACE("%s\n", filename);
 
+    error = pFT_New_Face(library, filename, 0, &face);
     if (error != FT_Err_Ok)
     {
-       WARN("FreeType error %i opening '%s'\n", error, filename);
-       HeapFree(PSDRV_Heap, 0, afm);
+       WARN("FreeType error %i opening %s\n", error, filename);
        return TRUE;
     }
-    
-    if (ReadTrueTypeAFM(afm) == FALSE)
+
+    if ((face->face_flags & REQUIRED_FACE_FLAGS) == REQUIRED_FACE_FLAGS)
     {
-       FreeAFM(afm);
-       FT_Done_Face(face);
-       return FALSE;
-    }    
+       if (BuildTrueTypeAFM(face) == FALSE)
+       {
+           pFT_Done_Face(face);
+           return FALSE;
+       }
+    }
+    else
+    {
+       WARN("Required information missing from %s\n", filename);
+    }
 
-    error = FT_Done_Face(face);
+    error = pFT_Done_Face(face);
     if (error != FT_Err_Ok)
     {
        ERR("%s returned %i\n", "FT_Done_Face", error);
-       FreeAFM(afm);
        return FALSE;
     }
-    
-    if (afm->Encoding == NULL)     /* last element to be set */
+
+    return TRUE;
+}
+
+/*******************************************************************************
+ *  ReadTrueTypeDir
+ *
+ *  Reads all TrueType font files in a directory.
+ *
+ */
+static BOOL ReadTrueTypeDir(FT_Library library, LPCSTR dirname)
+{
+    struct dirent   *dent;
+    DIR            *dir;
+    CHAR           filename[256];
+
+    dir = opendir(dirname);
+    if (dir == NULL)
     {
-       FreeAFM(afm);
+       WARN("'%s' opening %s\n", strerror(errno), dirname);
        return TRUE;
     }
-    
-    if (PSDRV_AddAFMtoList(&PSDRV_AFMFontList, afm) == FALSE)
+
+    while ((dent = readdir(dir)) != NULL)
     {
-       FreeAFM(afm);
-       return FALSE;
+       CHAR        *file_extension = strrchr(dent->d_name, '.');
+       int         fn_len;
+
+       if (file_extension == NULL || strcasecmp(file_extension, ".ttf") != 0)
+           continue;
+
+       fn_len = snprintf(filename, 256, "%s/%s", dirname, dent->d_name);
+       if (fn_len < 0 || fn_len > sizeof(filename) - 1)
+       {
+           WARN("Path '%s/%s' is too long\n", dirname, dent->d_name);
+           continue;
+       }
+
+       if (ReadTrueTypeFile(library, filename) ==  FALSE)
+       {
+           closedir(dir);
+           return FALSE;
+       }
     }
-    
+
+    closedir(dir);
+
     return TRUE;
 }
-    
-
 
 /*******************************************************************************
  *  PSDRV_GetTrueTypeMetrics
  *
- *  Reads PostScript-stype font metrics from TrueType font files in directories
- *  listed in the [TrueType Font Directories] section of the Wine configuration
- *  file.
+ *  Reads font metrics from TrueType font files in directories listed in the
+ *  [TrueType Font Directories] section of the Wine configuration file.
  *
- *  If this function fails, the driver will fail to initialize and the driver
- *  heap will be destroyed, so it's not necessary to HeapFree everything in
- *  that event.
+ *  If this function fails (returns FALSE), the driver will fail to initialize
+ *  and the driver heap will be destroyed, so it's not necessary to HeapFree
+ *  everything in that event.
  *
  */
-BOOL PSDRV_GetTrueTypeMetrics()
+BOOL PSDRV_GetTrueTypeMetrics(void)
 {
-    CHAR       keybuf[256], namebuf[256];
+    CHAR       name_buf[256], value_buf[256];
     INT        i = 0;
     FT_Error   error;
+    FT_Library library;
+    HKEY       hkey;
+    DWORD      type, name_len, value_len;
+
+    if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,
+           "Software\\Wine\\Wine\\Config\\TrueType Font Directories",
+           0, KEY_READ, &hkey) != ERROR_SUCCESS)
+       return TRUE;
 
-    error = FT_Init_FreeType(&library);
+
+    ft_handle = wine_dlopen(SONAME_LIBFREETYPE, RTLD_NOW, NULL, 0);
+    if(!ft_handle) {
+        WINE_MESSAGE(
+      "Wine cannot find the FreeType font library.  To enable Wine to\n"
+      "use TrueType fonts please install a version of FreeType greater than\n"
+      "or equal to 2.0.5.\n"
+      "http://www.freetype.org\n");
+       return TRUE;
+    }
+
+#define LOAD_FUNCPTR(f) if((p##f = wine_dlsym(ft_handle, #f, NULL, 0)) == NULL) goto sym_not_found;
+    LOAD_FUNCPTR(FT_Done_Face)
+    LOAD_FUNCPTR(FT_Done_FreeType)
+    LOAD_FUNCPTR(FT_Get_Char_Index)
+    LOAD_FUNCPTR(FT_Get_Glyph_Name)
+    LOAD_FUNCPTR(FT_Get_Sfnt_Name)
+    LOAD_FUNCPTR(FT_Get_Sfnt_Name_Count)
+    LOAD_FUNCPTR(FT_Get_Sfnt_Table)
+    LOAD_FUNCPTR(FT_Init_FreeType)
+    LOAD_FUNCPTR(FT_Load_Glyph)
+    LOAD_FUNCPTR(FT_New_Face)
+    LOAD_FUNCPTR(FT_Set_Charmap)
+#undef LOAD_FUNCPTR
+
+    error = pFT_Init_FreeType(&library);
     if (error != FT_Err_Ok)
     {
        ERR("%s returned %i\n", "FT_Init_FreeType", error);
+       wine_dlclose(ft_handle, NULL, 0);
+       RegCloseKey(hkey);
        return FALSE;
     }
 
-    while (PROFILE_EnumWineIniString("TrueType Font Directories", i++, keybuf,
-           sizeof(keybuf), namebuf, sizeof(namebuf)))
+    name_len = sizeof(name_buf);
+    value_len = sizeof(value_buf);
+
+    while (RegEnumValueA(hkey, i++, name_buf, &name_len, NULL, &type, value_buf,
+           &value_len) == ERROR_SUCCESS)
     {
-       struct dirent   *dent;
-       DIR             *dir;
-       INT             dnlen;      /* directory name length */
+       value_buf[sizeof(value_buf) - 1] = '\0';
 
-       namebuf[sizeof(namebuf) - 1] = '\0';
-       dir = opendir(namebuf);
-       if (dir == NULL)
-       {
-           WARN("Error opening directory '%s'\n", namebuf);
-           continue;
-       }
-       
-       dnlen = strlen(namebuf);
-       namebuf[dnlen] = '/';           /* 2 slashes is OK, 0 is not */
-       ++dnlen;
-       
-       while ((dent = readdir(dir)) != NULL)
+       if (ReadTrueTypeDir(library, value_buf) == FALSE)
        {
-           INT fnlen;      /* file name length */
-           
-           fnlen = strlen(dent->d_name);
-           
-           if (fnlen < 5 || strcasecmp(dent->d_name + fnlen - 4, ".ttf") != 0)
-           {
-               TRACE("Skipping filename '%s'\n", dent->d_name);
-               continue;
-           }
-           
-           if (dnlen + fnlen + 1 > sizeof(namebuf))    /* allow for '\0' */
-           {
-               WARN("Path '%s/%s' is too long\n", namebuf, dent->d_name);
-               continue;
-           }
-           
-           memcpy(namebuf + dnlen, dent->d_name, fnlen + 1);
-           
-           if (ReadTrueTypeFile(namebuf) == FALSE)
-           {
-               ERR("Error reading '%s'\n", namebuf);
-               closedir(dir);
-               FT_Done_FreeType(library);
-               return FALSE;
-           }
+           RegCloseKey(hkey);
+           pFT_Done_FreeType(library);
+           return FALSE;
        }
-       
-       closedir(dir);
+
+       /* initialize lengths for new iteration */
+
+       name_len = sizeof(name_buf);
+       value_len = sizeof(value_buf);
     }
-   
-    FT_Done_FreeType(library);
+
+    RegCloseKey(hkey);
+    pFT_Done_FreeType(library);
+    wine_dlclose(ft_handle, NULL, 0);
+    ft_handle = NULL;
+    return TRUE;
+
+sym_not_found:
+    WINE_MESSAGE(
+      "Wine cannot find certain functions that it needs inside the FreeType\n"
+      "font library.  To enable Wine to use TrueType fonts please upgrade\n"
+      "FreeType to at least version 2.0.5.\n"
+      "http://www.freetype.org\n");
+    wine_dlclose(ft_handle, NULL, 0);
+    ft_handle = NULL;
     return TRUE;
 }