[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