[try 2] Implement CreateScalableFontResource

Jeremy White jwhite at winehq.org
Thu Apr 10 22:05:18 CDT 2008


---
 dlls/gdi32/font.c        |  310 +++++++++++++++++++++++++++++++++++++++++++++-
 dlls/gdi32/freetype.c    |  213 +++++++++++++++++++++++++++++++
 dlls/gdi32/gdi_private.h |    1 +
 3 files changed, 522 insertions(+), 2 deletions(-)

diff --git a/dlls/gdi32/font.c b/dlls/gdi32/font.c
index 70a043e..70b1809 100644
--- a/dlls/gdi32/font.c
+++ b/dlls/gdi32/font.c
@@ -2359,6 +2359,259 @@ DWORD WINAPI GetGlyphOutlineW( HDC hdc, UINT uChar, UINT fuFormat,
 }


+/*****************************************************************************
+ * FONT_WriteScalableFont
+ *  Write out a .fot file that is nearly identical to the file written
+ * by CreateScalableFont on Windows XP.
+ *  The format for this file is not specified, so the implementation here
+ * uses a large number of observed values (e.g. 'magic'), rather than working
+ * from any sort of specification.
+ */
+static int FONT_WriteScalableFont(LPCWSTR output_name, FONTDIR16 *fontdir, LPCSTR ttf_file)
+{
+    HANDLE h;
+    IMAGE_DOS_HEADER dos;
+    IMAGE_OS2_HEADER ne;
+    NE_TYPEINFO types[2];
+    NE_NAMEINFO names[2];
+    WORD resource_shift = 4;
+    BYTE write_byte = 0;
+    WORD write_word = 0;
+    DWORD write_dword = 0;
+    char ttfpath[128];  /* Value of 128 is intentional and 'magic' */
+    char fontdir_string[] = "FONTDIR";
+    char buffer[4096];
+    char *bufp;
+    char shortname[128];
+    char fontres[256];
+    char *sp;
+    int extlen;
+    int i;
+    int rc = 0;
+    int ne_offset;
+    int name_0_offset;
+    int fontdir_len;
+    char *fp;
+
+#define TTF_RESOURCE_OFFSET 0x400
+#define FONTDIR_RESOURCE_OFFSET 0x480
+#define RESOURCE_FLAGS 0xc50
+
+    static const BYTE dos_stub[] = {
+                 0x0e,              /* Push CS */
+                 0x1f,              /* Pop  DS */
+                 0xba, 0x0e, 0x00,  /* Mov DX, 000E */
+                 0xb4, 0x09,        /* Mov AH, 0009 */
+                 0xcd, 0x21,        /* Int 21 - Print string*/
+                 0xb8, 0x01, 0x4c,  /* Mov AX, 4C01 */
+                 0xcd, 0x21,        /* Int 21 - Exit */
+                 /* Offset 0xE */
+                'T','h','i','s',' ','i','s',' ','a',' ','T','r','u','e','T','y','p','e',' ', 'f','o','n','t',',',' ',
+                'n','o','t',' ','a',' ','p','r','o','g','r','a','m','.','\r','\r','\n','$',
+                /* Magic - copied from an existing Windows XP .FOT file */
+                '\0','K','i','e','s','a','\0' };
+
+    TRACE("(%s,%s,%s):\n",
+          debugstr_w(output_name), fontdir->szDeviceName + 1, ttf_file);
+
+    h = CreateFileW(output_name, FILE_SHARE_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+    if (h == INVALID_HANDLE_VALUE)
+    {
+        TRACE("Error:  could not open %s for writing\n", debugstr_w(output_name));
+        return 1;
+    }
+
+    /* Get an 8.3 name to be used in the name tables */
+    if (GetShortPathNameA(ttf_file, shortname, sizeof(shortname)) == 0)
+        strcpy(shortname, ttf_file);
+    for (sp = shortname + strlen(shortname) - 1; *sp != '/' && *sp != '\\' && sp >= shortname; )
+        sp--;
+    sp++;
+    if (strstr(sp, "."))
+        extlen = strstr(sp, ".") - sp;
+    else
+        extlen = strlen(sp);
+
+    fp = fontdir->szDeviceName + 1;
+    fp += strlen(fp) + 1;
+    fp += strlen(fp) + 1;
+    fp += strlen(fp) + 1;
+    fontdir_len = fp - ((char *) fontdir);
+
+    /* Make a DOS header.  Note that many of these values seem 'magic'; they
+         are simply copied from the observed values in .fot files created on Windows */
+    memset(&dos, '\0', sizeof(dos));
+
+    dos.e_magic = IMAGE_DOS_SIGNATURE;
+    dos.e_cblp = 1;
+    dos.e_cp = 2;
+    dos.e_cparhdr = (sizeof(dos) + sizeof(dos_stub)) / 0x20;  /* Size of header in paragraphs */
+    dos.e_minalloc = 0xf;
+    dos.e_maxalloc = 0xffff;
+    dos.e_sp = 0xb8;
+    dos.e_lfarlc = sizeof(dos);
+    dos.e_lfanew = sizeof(dos) + sizeof(dos_stub);
+
+    memset(&ne, '\0', sizeof(ne));
+    ne.ne_magic = IMAGE_OS2_SIGNATURE;
+    ne.ne_ver = 5;
+    ne.ne_rev = 16;
+
+    ne.ne_cbenttab = 2;      /* Length of entry table in bytes */
+    ne.ne_flags =  NE_FFLAGS_LIBMODULE;
+    ne.ne_align =  0x4;      /* Logical sector alignment shift count */
+    ne.ne_cres =   0x2;      /* # of resource segemnts */
+    ne.ne_exetyp = 0x2;      /* 2 is MS Windows */
+    ne.ne_expver = 0x300;
+
+    memset(types, '\0', sizeof(types));
+    memset(names, '\0', sizeof(names));
+
+    types[0].type_id = NE_RSCTYPE_FONTDIR;
+    types[0].count = 1;
+    names[0].offset = FONTDIR_RESOURCE_OFFSET >> resource_shift;
+    names[0].length = (fontdir_len + 0x12) >> resource_shift;
+    names[0].flags = RESOURCE_FLAGS;
+    names[0].id = 0; /* To be computed and rewritten later */
+
+
+    types[1].type_id = NE_RSCTYPE_SCALABLE_FONTPATH;
+    types[1].count = 1;
+    names[1].offset = TTF_RESOURCE_OFFSET >> resource_shift;
+    names[1].length = sizeof(ttfpath) >> resource_shift;
+    names[1].flags = RESOURCE_FLAGS;
+    names[1].id = 1 | 0x8000;
+
+    bufp = buffer;
+    memset(buffer, 0, sizeof(buffer));
+
+    /*
+        Our NE file holds
+          * MS-DOS Header
+          * Windows Header (NE)
+          * Resource Table
+            o Type Information
+            o Name Information
+          * Resident-Name Table
+          * Imported-Name Table
+          * Entry Table
+          * Nonresident-Name Table
+          * Font resource data for FONTDIR
+          * Font resource data for 0x80cc (Path to scalable font)
+     */
+
+
+#define ADD_TO_BUFFER(p, s)  { memcpy(bufp, (p), (s)); bufp += (s); }
+
+    ADD_TO_BUFFER(&dos, sizeof(dos));
+    ADD_TO_BUFFER(&dos_stub, sizeof(dos_stub));
+
+    ne_offset = bufp - buffer;
+
+    ADD_TO_BUFFER(&ne, sizeof(ne));
+
+    ne.ne_segtab = bufp - buffer - ne_offset;
+    ne.ne_rsrctab = bufp - buffer - ne_offset;
+
+    /* Begin resource table */
+    ADD_TO_BUFFER(&resource_shift, sizeof(resource_shift));
+
+    for (i = 0; i < 2; i++)
+    {
+        ADD_TO_BUFFER(&types[i], sizeof(types[i]));
+        if (i == 0)
+            name_0_offset = bufp - buffer;
+        ADD_TO_BUFFER(&names[i], sizeof(names[i]));
+    }
+
+    write_word = 0;  /* End the list of type/names with a 0 id */
+    ADD_TO_BUFFER(&write_word, sizeof(write_word));
+
+    /* The id field of the first name points to the offset of the resource name */
+    names[0].id = (bufp - buffer) - ne.ne_rsrctab - ne_offset;
+    memcpy(buffer + name_0_offset, &names[0], sizeof(names[0]));
+
+    write_byte = strlen(fontdir_string);
+    ADD_TO_BUFFER(&write_byte, sizeof(write_byte));
+    ADD_TO_BUFFER(fontdir_string, strlen(fontdir_string));
+    /* End resource table */
+
+
+    /* Resident name table */
+    ne.ne_restab = bufp - buffer - ne_offset;
+
+    write_byte = extlen;
+    ADD_TO_BUFFER(&write_byte, sizeof(write_byte));
+    ADD_TO_BUFFER(sp, extlen);
+    write_byte = 0;
+    ADD_TO_BUFFER(&write_byte, sizeof(write_byte));
+
+    /* Uncertain filler; may just be padding */
+    write_dword = 0;
+    ADD_TO_BUFFER(&write_dword, sizeof(write_dword));
+
+    /*  The module reference table is pointed at the imported name table */
+    ne.ne_modtab = bufp - buffer - ne_offset;
+
+    /* Imported name table */
+    ne.ne_imptab = bufp - buffer - ne_offset;
+    write_byte = strlen(sp) + 1;
+    ADD_TO_BUFFER(&write_byte, sizeof(write_byte));
+    ADD_TO_BUFFER(sp, strlen(sp) + 1);
+
+    /* Entry table */
+    ne.ne_enttab = bufp - buffer - ne_offset;
+    write_word = 0;
+    ADD_TO_BUFFER(&write_word, sizeof(write_word));
+
+    /* Uncertain filler; may just be padding */
+    ADD_TO_BUFFER(&write_word, sizeof(write_word));
+
+    /* Nonresident name table */
+    memset(fontres, 0, sizeof(fontres));
+    strcpy(fontres, "FONTRES:");
+    fp = fontdir->szDeviceName + 1;
+    fp += strlen(fp) + 1;
+    strcat(fontres, fp);
+    ne.ne_cbnrestab = strlen(fontres) + 4;
+    ne.ne_nrestab = bufp - buffer;
+    write_byte = ne.ne_cbnrestab;
+    ADD_TO_BUFFER(&write_byte, sizeof(write_byte));
+    ADD_TO_BUFFER(fontres, ne.ne_cbnrestab);
+
+    /* No rationale; just observed that all .fot files generated
+         on Windows have this byte */
+    buffer[0x200] = 0xc3;
+
+    memset(ttfpath, 0, sizeof(ttfpath));
+    memcpy(ttfpath, ttf_file, min(strlen(ttf_file), sizeof(ttfpath) - 1));
+    bufp = buffer + TTF_RESOURCE_OFFSET;
+    ADD_TO_BUFFER(ttfpath, sizeof(ttfpath));
+
+    bufp = buffer + FONTDIR_RESOURCE_OFFSET;
+    write_dword = 1;
+    ADD_TO_BUFFER(&write_dword, sizeof(write_dword));
+    ADD_TO_BUFFER(fontdir,  fontdir_len);
+
+    /* Generated .fot files are always 1409 bytes long on Windows XP */
+    if (bufp - buffer < 1409)
+        bufp = buffer + 1409;
+
+    /* Copy back in the updated ne */
+    memcpy(buffer + ne_offset, &ne, sizeof(ne));
+
+#undef ADD_TO_BUFFER
+
+    if (!WriteFile(h, buffer, bufp - buffer, NULL, NULL))
+    {
+        TRACE("Error:  could not write %d bytes\n", bufp - buffer);
+        rc = 1;
+    }
+
+    CloseHandle(h);
+    return rc;
+}
+
 /***********************************************************************
  *           CreateScalableFontResourceA   (GDI32.@)
  */
@@ -2413,7 +2666,15 @@ BOOL WINAPI CreateScalableFontResourceW( DWORD fHidden,
                                              LPCWSTR lpszCurrentPath )
 {
     HANDLE f;
-    FIXME("(%d,%s,%s,%s): stub\n",
+    LPSTR unix_filename;
+    LPWSTR wide_path;
+    LPSTR ansi_path;
+    FONTDIR16 fontdir;
+    WCHAR slash[] = {'\\', 0};
+    int len;
+    int rc;
+
+    TRACE("(%d,%s,%s,%s):\n",
           fHidden, debugstr_w(lpszResourceFile), debugstr_w(lpszFontFile),
           debugstr_w(lpszCurrentPath) );

@@ -2426,9 +2687,54 @@ BOOL WINAPI CreateScalableFontResourceW( DWORD fHidden,
     if ((f = CreateFileW(lpszResourceFile, 0, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)) != INVALID_HANDLE_VALUE) {
         CloseHandle(f);
         SetLastError(ERROR_FILE_EXISTS);
+        TRACE("Error: %s file exists\n", debugstr_w(lpszResourceFile));
         return FALSE;
     }
-    return FALSE; /* create failed */
+
+    if (lpszCurrentPath)
+        len = strlenW(lpszCurrentPath) + 2;
+    else
+        len = 0;
+    len += strlenW(lpszFontFile) + 1;
+    wide_path = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+    if (lpszCurrentPath)
+    {
+        strcpyW(wide_path, lpszCurrentPath);
+        strcatW(wide_path, slash);
+        strcatW(wide_path, lpszFontFile);
+    }
+    else
+        strcpyW(wide_path, lpszFontFile);
+
+    unix_filename = wine_get_unix_file_name(wide_path);
+    HeapFree(GetProcessHeap(), 0, wide_path);
+    if (! unix_filename)
+    {
+        TRACE("Error: %s file has no Unix filename\n", debugstr_w(wide_path));
+        return FALSE;
+    }
+
+    rc = WineEngLoadFontdir(unix_filename, &fontdir);
+    HeapFree(GetProcessHeap(), 0, unix_filename);
+    if (rc)
+    {
+        TRACE("Error: could not load FONTDIR from %s\n", unix_filename);
+        return FALSE;
+    }
+
+    len = WideCharToMultiByte(CP_ACP, 0, lpszFontFile, -1, NULL, 0, NULL, NULL);
+    ansi_path = HeapAlloc(GetProcessHeap(), 0, len );
+    WideCharToMultiByte(CP_ACP, 0, lpszFontFile, -1, ansi_path, len, NULL, NULL);
+
+    rc = FONT_WriteScalableFont(lpszResourceFile, &fontdir, ansi_path);
+    HeapFree(GetProcessHeap(), 0, ansi_path);
+    if (rc)
+    {
+        TRACE("Error writing scalable font to %s with name %s\n", debugstr_w(lpszResourceFile), debugstr_w(lpszFontFile));
+        return FALSE;
+    }
+
+    return TRUE;
 }

 /*************************************************************************
diff --git a/dlls/gdi32/freetype.c b/dlls/gdi32/freetype.c
index aeb9c7d..f1e52a9 100644
--- a/dlls/gdi32/freetype.c
+++ b/dlls/gdi32/freetype.c
@@ -5717,6 +5717,219 @@ DWORD WineEngGetKerningPairs(GdiFont *font, DWORD cPairs, KERNINGPAIR *kern_pair
     return font->total_kern_pairs;
 }

+
+/*****************************************************************************
+ *  WineEngLoadFontdir
+ *      This function will create a FONTDIR16 structure populated with
+ *  the deduced data needed in the resource written to a .fot file
+ *  as a result of CreateScalableFont.
+ *
+ *      Much of this code is determined by examining a group of about 170
+ *  ttf files on a Windows XP system and examining the .FOT files that result
+ *  from running CreateScalableFont on that Windows XP system.  As a result,
+ *  many of the values are 'magic' - set the way they are just to recreate
+ *  the same result as exists on Windows.  By and large, this code replicates
+ *  those values, with a few exceptions, noted in the code.
+ */
+WORD WineEngLoadFontdir(LPCSTR filename, FONTDIR16 *fontdir)
+{
+    /* The trailing byte rationale is unknown; simply mirrored on Windows behavior */
+    char copyright[] = "Windows! Windows! Windows!\0\020\003\001\001";
+    char *p;
+
+    TT_OS2 *os2;
+
+    FT_Face face;
+    FT_Library lib;
+
+    int i;
+    char fullname[128];
+
+    TRACE("(%s, %s)\n", filename, fontdir->szDeviceName + 1);
+
+    if(pFT_Init_FreeType(&lib))
+    {
+        TRACE("FT_Init_FreeType failed\n");
+        return -1;
+    }
+
+    if(pFT_New_Face(lib, filename, 0, &face))
+    {
+        TRACE("FT_New_Face(%s) failed\n", filename);
+        return(-2);
+    }
+
+    os2 = pFT_Get_Sfnt_Table(face, ft_sfnt_os2);
+    if (!os2)
+    {
+        TRACE("FT_GetSfnt_Table os2 failed\n");
+        return(-3);
+    }
+
+    memset(fontdir, 0, sizeof(*fontdir));
+
+    /* Initialize some fields to apparently constant values.
+     *  dfFace and dfVersion seem to be clearly sensible values;
+     *  the rest are just based on observed results.
+     */
+    fontdir->dfFace = 0x76;
+    fontdir->dfSize = 0x95;
+    fontdir->dfVersion = 0x200;
+    fontdir->dfDefaultChar = 1;
+    fontdir->dfBreakChar =   2;
+    fontdir->dfWidthBytes =  0;
+    memcpy(fontdir->dfCopyright, copyright, sizeof(copyright));
+    fontdir->dfVertRes = 72;
+    fontdir->dfHorizRes = 72;
+
+    /* dfMaxWidth is infrequently computed by starting with face->bbox.xMax
+        instead of max_advanced_with.  FIXME:  Figure out when and why  */
+    fontdir->dfMaxWidth = face->max_advance_width;
+    fontdir->dfMaxWidth -= face->bbox.xMin;
+
+    /*  Note:  The 0xf000 comparison is 'magic' */
+    if (os2->usFirstCharIndex > 0xf000)
+    {
+        fontdir->dfLastChar = os2->usLastCharIndex - 0xf000;
+        fontdir->dfFirstChar = os2->usFirstCharIndex - 0xf000;
+        fontdir->dfReserved |= 0xf0000000;
+    }
+    else
+    {
+        fontdir->dfLastChar = 0xff;
+        fontdir->dfFirstChar = os2->usFirstCharIndex;
+    }
+    fontdir->dfFirstChar -= 2;  /* FIXME:  Magic - no rationale */
+
+
+    fontdir->dfAvgWidth = os2->xAvgCharWidth;
+    fontdir->dfWeight = os2->usWeightClass;
+    fontdir->dfAscent = os2->usWinAscent;
+
+    fontdir->dfCharSet = ANSI_CHARSET;
+    if ((os2->sFamilyClass >> 8) == 12)
+        fontdir->dfCharSet = SYMBOL_CHARSET;
+    /* FIXME:  This is not quite perfect; it gets close, but we
+               have a few cases of DEFAULT_CHARSET we don't catch */
+
+
+    fontdir->dfType = 0x3;  /* FIXME:  The 0x3 is magic; may be PF_OTHER2_TYPE */
+    fontdir->dfType |= (os2->fsSelection << 8);
+
+    fontdir->dfPoints = face->units_per_EM;
+
+    fontdir->dfItalic = (face->style_flags & FT_STYLE_FLAG_ITALIC) ? 0xff : 0;
+    fontdir->dfUnderline = 0;  /* FIXME - Underline and strikeout could probably be computed */
+    fontdir->dfStrikeOut = 0;
+
+    fontdir->dfPixHeight = os2->usWinAscent + os2->usWinDescent;
+    fontdir->dfInternalLeading = fontdir->dfPixHeight - fontdir->dfPoints;
+    if (face->max_advance_height - fontdir->dfPixHeight > 0)
+        fontdir->dfExternalLeading = face->max_advance_height - fontdir->dfPixHeight;
+    else
+        fontdir->dfExternalLeading = 0;
+
+    if (os2->panose[PAN_PROPORTION_INDEX] == PAN_PROP_MONOSPACED ||
+       (! (face->face_flags & FT_FACE_FLAG_FIXED_WIDTH)))
+        fontdir->dfPixWidth = 0;
+    else
+        fontdir->dfPixWidth = fontdir->dfAvgWidth;
+
+
+    /*
+     *  dfPitchAndFamily is a complex calculation.
+     *      The documented value indicates it is based on proportion,
+     *      family type, and serif style, but the recipe is not
+     *      documented.  This algorithm was arrived at by
+     *      experimentation.
+     */
+    if (os2->panose[PAN_PROPORTION_INDEX] != PAN_PROP_MONOSPACED)
+        fontdir->dfPitchAndFamily |= FIXED_PITCH;
+
+    if (FT_IS_SCALABLE(face))
+        fontdir->dfPitchAndFamily |= (TMPF_VECTOR | TMPF_TRUETYPE);
+
+    switch(os2->panose[PAN_FAMILYTYPE_INDEX])
+    {
+        case PAN_FAMILY_SCRIPT:
+            fontdir->dfPitchAndFamily |= FF_SCRIPT;
+	    break;
+        case PAN_FAMILY_DECORATIVE:
+            fontdir->dfPitchAndFamily |= FF_DECORATIVE;
+	    break;
+        default:
+            if (os2->panose[PAN_PROPORTION_INDEX] == PAN_PROP_MONOSPACED)
+                fontdir->dfPitchAndFamily |= FF_MODERN;
+            else
+            {
+                switch(os2->panose[PAN_SERIFSTYLE_INDEX])
+                {
+                    case 0:
+                    case 1:
+                        /* FIXME Magic, determined empirically */
+                        break;
+                    case PAN_SERIF_NORMAL_SANS:
+                    case PAN_SERIF_OBTUSE_SANS:
+                    case PAN_SERIF_PERP_SANS:
+                        fontdir->dfPitchAndFamily |= FF_SWISS;
+                        break;
+                    default:
+                        fontdir->dfPitchAndFamily |= FF_ROMAN;
+                }
+            }
+	break;
+    }
+
+
+    memset(fullname, 0, sizeof(fullname));
+    for (i = 0; i < pFT_Get_Sfnt_Name_Count(face); i++)
+    {
+        FT_SfntName name;
+
+        if (pFT_Get_Sfnt_Name(face, i, &name) == 0)
+            if (name.name_id == TT_NAME_ID_FULL_NAME)
+            {
+                memset(fullname, 0, sizeof(fullname));
+                memcpy(fullname, name.string, min(name.string_len, sizeof(fullname) - 1));
+                if (strlen(fullname) > 0)
+                    break;
+            }
+    }
+    if (strlen(fullname) == 0)
+        strcpy(fullname, face->family_name);
+
+    /* FIXME TODO:  dfReserved starts with a byte that is 6, 9, 0xa, or 0xb
+          It then has 2 null bytes, and the 4th byte seems to be 0xf0
+          in cases when the first char is > 0xf000 */
+
+    /* Less than ideal; we overload szDeviceName so as to continue using
+       the FONTDIR16 structure rather than defining a whole new structure
+       that exactly matches the .FOT binary layout */
+    fontdir->szDeviceName[0] = 0xff;
+    p = &(fontdir->szDeviceName[1]);
+
+    if ((strlen(face->family_name) + strlen(fullname) + strlen(face->style_name) + 3) >=
+            (sizeof(fontdir->szDeviceName) + sizeof(fontdir->szFaceName) - 1))
+    {
+        TRACE("Names [%s|%s|%s] too long, failing\n", face->family_name, fullname, face->style_name);
+        return -4;
+    }
+
+    strcpy(p, face->family_name);
+    p += strlen(p) + 1;
+
+    strcpy(p, fullname);
+    p += strlen(p) + 1;
+
+    strcpy(p, face->style_name);
+    p += strlen(p) + 1;
+
+    return 0;
+}
+
+
+
+
 #else /* HAVE_FREETYPE */

 /*************************************************************************/
diff --git a/dlls/gdi32/gdi_private.h b/dlls/gdi32/gdi_private.h
index e3a320a..3b8247b 100644
--- a/dlls/gdi32/gdi_private.h
+++ b/dlls/gdi32/gdi_private.h
@@ -447,6 +447,7 @@ extern BOOL WineEngGetTextMetrics(GdiFont*, LPTEXTMETRICW) DECLSPEC_HIDDEN;
 extern BOOL WineEngFontIsLinked(GdiFont*) DECLSPEC_HIDDEN;
 extern BOOL WineEngInit(void) DECLSPEC_HIDDEN;
 extern BOOL WineEngRemoveFontResourceEx(LPCWSTR, DWORD, PVOID) DECLSPEC_HIDDEN;
+extern WORD WineEngLoadFontdir(LPCSTR filename, FONTDIR16 *fontdir);

 /* gdiobj.c */
 extern BOOL GDI_Init(void) DECLSPEC_HIDDEN;
-- 
1.5.3.7





More information about the wine-patches mailing list