wine/dlls/gdi32/freetype.c

6684 lines
216 KiB
C

/*
* FreeType font engine interface
*
* Copyright 2001 Huw D M Davies for CodeWeavers.
* Copyright 2006 Dmitry Timoshkov for CodeWeavers.
*
* This file contains the WineEng* functions.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "config.h"
#include "wine/port.h"
#include <stdarg.h>
#include <stdlib.h>
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif
#ifdef HAVE_SYS_MMAN_H
# include <sys/mman.h>
#endif
#include <string.h>
#ifdef HAVE_DIRENT_H
# include <dirent.h>
#endif
#include <stdio.h>
#include <assert.h>
#ifdef HAVE_CARBON_CARBON_H
#define LoadResource __carbon_LoadResource
#define CompareString __carbon_CompareString
#define GetCurrentThread __carbon_GetCurrentThread
#define GetCurrentProcess __carbon_GetCurrentProcess
#define AnimatePalette __carbon_AnimatePalette
#define EqualRgn __carbon_EqualRgn
#define FillRgn __carbon_FillRgn
#define FrameRgn __carbon_FrameRgn
#define GetPixel __carbon_GetPixel
#define InvertRgn __carbon_InvertRgn
#define LineTo __carbon_LineTo
#define OffsetRgn __carbon_OffsetRgn
#define PaintRgn __carbon_PaintRgn
#define Polygon __carbon_Polygon
#define ResizePalette __carbon_ResizePalette
#define SetRectRgn __carbon_SetRectRgn
#include <Carbon/Carbon.h>
#undef LoadResource
#undef CompareString
#undef GetCurrentThread
#undef _CDECL
#undef DPRINTF
#undef GetCurrentProcess
#undef AnimatePalette
#undef EqualRgn
#undef FillRgn
#undef FrameRgn
#undef GetPixel
#undef InvertRgn
#undef LineTo
#undef OffsetRgn
#undef PaintRgn
#undef Polygon
#undef ResizePalette
#undef SetRectRgn
#endif /* HAVE_CARBON_CARBON_H */
#include "windef.h"
#include "winbase.h"
#include "winternl.h"
#include "winerror.h"
#include "winreg.h"
#include "wingdi.h"
#include "gdi_private.h"
#include "wine/unicode.h"
#include "wine/debug.h"
#include "wine/list.h"
WINE_DEFAULT_DEBUG_CHANNEL(font);
#ifdef HAVE_FREETYPE
#ifdef HAVE_FT2BUILD_H
#include <ft2build.h>
#endif
#ifdef HAVE_FREETYPE_FREETYPE_H
#include <freetype/freetype.h>
#endif
#ifdef HAVE_FREETYPE_FTGLYPH_H
#include <freetype/ftglyph.h>
#endif
#ifdef HAVE_FREETYPE_TTTABLES_H
#include <freetype/tttables.h>
#endif
#ifdef HAVE_FREETYPE_FTTYPES_H
#include <freetype/fttypes.h>
#endif
#ifdef HAVE_FREETYPE_FTSNAMES_H
#include <freetype/ftsnames.h>
#endif
#ifdef HAVE_FREETYPE_TTNAMEID_H
#include <freetype/ttnameid.h>
#endif
#ifdef HAVE_FREETYPE_FTOUTLN_H
#include <freetype/ftoutln.h>
#endif
#ifdef HAVE_FREETYPE_INTERNAL_SFNT_H
#include <freetype/internal/sfnt.h>
#endif
#ifdef HAVE_FREETYPE_FTTRIGON_H
#include <freetype/fttrigon.h>
#endif
#ifdef HAVE_FREETYPE_FTWINFNT_H
#include <freetype/ftwinfnt.h>
#endif
#ifdef HAVE_FREETYPE_FTMODAPI_H
#include <freetype/ftmodapi.h>
#endif
#ifdef HAVE_FREETYPE_FTLCDFIL_H
#include <freetype/ftlcdfil.h>
#endif
#ifndef HAVE_FT_TRUETYPEENGINETYPE
typedef enum
{
FT_TRUETYPE_ENGINE_TYPE_NONE = 0,
FT_TRUETYPE_ENGINE_TYPE_UNPATENTED,
FT_TRUETYPE_ENGINE_TYPE_PATENTED
} FT_TrueTypeEngineType;
#endif
static FT_Library library = 0;
typedef struct
{
FT_Int major;
FT_Int minor;
FT_Int patch;
} FT_Version_t;
static FT_Version_t FT_Version;
static DWORD FT_SimpleVersion;
static void *ft_handle = NULL;
#define MAKE_FUNCPTR(f) static typeof(f) * p##f = NULL
MAKE_FUNCPTR(FT_Vector_Unit);
MAKE_FUNCPTR(FT_Done_Face);
MAKE_FUNCPTR(FT_Get_Char_Index);
MAKE_FUNCPTR(FT_Get_Module);
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_Matrix_Multiply);
#ifdef FT_MULFIX_INLINED
#define pFT_MulFix FT_MULFIX_INLINED
#else
MAKE_FUNCPTR(FT_MulFix);
#endif
MAKE_FUNCPTR(FT_New_Face);
MAKE_FUNCPTR(FT_New_Memory_Face);
MAKE_FUNCPTR(FT_Outline_Get_Bitmap);
MAKE_FUNCPTR(FT_Outline_Transform);
MAKE_FUNCPTR(FT_Outline_Translate);
MAKE_FUNCPTR(FT_Select_Charmap);
MAKE_FUNCPTR(FT_Set_Charmap);
MAKE_FUNCPTR(FT_Set_Pixel_Sizes);
MAKE_FUNCPTR(FT_Vector_Transform);
MAKE_FUNCPTR(FT_Render_Glyph);
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_FTLCDFIL_H
static FT_Error (*pFT_Library_SetLcdFilter)(FT_Library, FT_LcdFilter);
#endif
#ifdef HAVE_FREETYPE_FTWINFNT_H
MAKE_FUNCPTR(FT_Get_WinFNT_Header);
#endif
#ifdef SONAME_LIBFONTCONFIG
#include <fontconfig/fontconfig.h>
MAKE_FUNCPTR(FcConfigGetCurrent);
MAKE_FUNCPTR(FcFontList);
MAKE_FUNCPTR(FcFontSetDestroy);
MAKE_FUNCPTR(FcInit);
MAKE_FUNCPTR(FcObjectSetAdd);
MAKE_FUNCPTR(FcObjectSetCreate);
MAKE_FUNCPTR(FcObjectSetDestroy);
MAKE_FUNCPTR(FcPatternCreate);
MAKE_FUNCPTR(FcPatternDestroy);
MAKE_FUNCPTR(FcPatternGetBool);
MAKE_FUNCPTR(FcPatternGetString);
#endif
#undef MAKE_FUNCPTR
#ifndef FT_MAKE_TAG
#define FT_MAKE_TAG( ch0, ch1, ch2, ch3 ) \
( ((DWORD)(BYTE)(ch0) << 24) | ((DWORD)(BYTE)(ch1) << 16) | \
((DWORD)(BYTE)(ch2) << 8) | (DWORD)(BYTE)(ch3) )
#endif
#ifndef ft_encoding_none
#define FT_ENCODING_NONE ft_encoding_none
#endif
#ifndef ft_encoding_ms_symbol
#define FT_ENCODING_MS_SYMBOL ft_encoding_symbol
#endif
#ifndef ft_encoding_unicode
#define FT_ENCODING_UNICODE ft_encoding_unicode
#endif
#ifndef ft_encoding_apple_roman
#define FT_ENCODING_APPLE_ROMAN ft_encoding_apple_roman
#endif
#ifdef WORDS_BIGENDIAN
#define GET_BE_WORD(x) (x)
#else
#define GET_BE_WORD(x) RtlUshortByteSwap(x)
#endif
/* This is basically a copy of FT_Bitmap_Size with an extra element added */
typedef struct {
FT_Short height;
FT_Short width;
FT_Pos size;
FT_Pos x_ppem;
FT_Pos y_ppem;
FT_Short internal_leading;
} Bitmap_Size;
/* FT_Bitmap_Size gained 3 new elements between FreeType 2.1.4 and 2.1.5
So to let this compile on older versions of FreeType we'll define the
new structure here. */
typedef struct {
FT_Short height, width;
FT_Pos size, x_ppem, y_ppem;
} My_FT_Bitmap_Size;
struct enum_data
{
ENUMLOGFONTEXW elf;
NEWTEXTMETRICEXW ntm;
DWORD type;
};
typedef struct tagFace {
struct list entry;
WCHAR *StyleName;
char *file;
void *font_data_ptr;
DWORD font_data_size;
FT_Long face_index;
FONTSIGNATURE fs;
FONTSIGNATURE fs_links;
DWORD ntmFlags;
FT_Fixed font_version;
BOOL scalable;
Bitmap_Size size; /* set if face is a bitmap */
BOOL external; /* TRUE if we should manually add this font to the registry */
struct tagFamily *family;
/* Cached data for Enum */
struct enum_data *cached_enum_data;
} Face;
typedef struct tagFamily {
struct list entry;
const WCHAR *FamilyName;
struct list faces;
} Family;
typedef struct {
GLYPHMETRICS gm;
INT adv; /* These three hold to widths of the unrotated chars */
INT lsb;
INT bbx;
BOOL init;
} GM;
typedef struct {
FLOAT eM11, eM12;
FLOAT eM21, eM22;
} FMAT2;
typedef struct {
DWORD hash;
LOGFONTW lf;
FMAT2 matrix;
BOOL can_use_bitmap;
} FONT_DESC;
typedef struct tagHFONTLIST {
struct list entry;
HFONT hfont;
} HFONTLIST;
typedef struct {
struct list entry;
Face *face;
GdiFont *font;
} CHILD_FONT;
struct tagGdiFont {
struct list entry;
GM **gm;
DWORD gmsize;
struct list hfontlist;
OUTLINETEXTMETRICW *potm;
DWORD total_kern_pairs;
KERNINGPAIR *kern_pairs;
struct list child_fonts;
/* the following members can be accessed without locking, they are never modified after creation */
FT_Face ft_face;
struct font_mapping *mapping;
LPWSTR name;
int charset;
int codepage;
BOOL fake_italic;
BOOL fake_bold;
BYTE underline;
BYTE strikeout;
INT orientation;
FONT_DESC font_desc;
LONG aveWidth, ppem;
double scale_y;
SHORT yMax;
SHORT yMin;
DWORD ntmFlags;
FONTSIGNATURE fs;
GdiFont *base_font;
VOID *GSUB_Table;
DWORD cache_num;
};
typedef struct {
struct list entry;
const WCHAR *font_name;
struct list links;
} SYSTEM_LINKS;
#define GM_BLOCK_SIZE 128
#define FONT_GM(font,idx) (&(font)->gm[(idx) / GM_BLOCK_SIZE][(idx) % GM_BLOCK_SIZE])
static struct list gdi_font_list = LIST_INIT(gdi_font_list);
static struct list unused_gdi_font_list = LIST_INIT(unused_gdi_font_list);
#define UNUSED_CACHE_SIZE 10
static struct list child_font_list = LIST_INIT(child_font_list);
static struct list system_links = LIST_INIT(system_links);
static struct list font_subst_list = LIST_INIT(font_subst_list);
static struct list font_list = LIST_INIT(font_list);
static const WCHAR defSerif[] = {'T','i','m','e','s',' ','N','e','w',' ','R','o','m','a','n','\0'};
static const WCHAR defSans[] = {'A','r','i','a','l','\0'};
static const WCHAR defFixed[] = {'C','o','u','r','i','e','r',' ','N','e','w','\0'};
static const WCHAR fontsW[] = {'\\','f','o','n','t','s','\0'};
static const WCHAR win9x_font_reg_key[] = {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
'W','i','n','d','o','w','s','\\',
'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
'F','o','n','t','s','\0'};
static const WCHAR winnt_font_reg_key[] = {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
'W','i','n','d','o','w','s',' ','N','T','\\',
'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
'F','o','n','t','s','\0'};
static const WCHAR system_fonts_reg_key[] = {'S','o','f','t','w','a','r','e','\\','F','o','n','t','s','\0'};
static const WCHAR FixedSys_Value[] = {'F','I','X','E','D','F','O','N','.','F','O','N','\0'};
static const WCHAR System_Value[] = {'F','O','N','T','S','.','F','O','N','\0'};
static const WCHAR OEMFont_Value[] = {'O','E','M','F','O','N','T','.','F','O','N','\0'};
static const WCHAR * const SystemFontValues[4] = {
System_Value,
OEMFont_Value,
FixedSys_Value,
NULL
};
static const WCHAR external_fonts_reg_key[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\',
'F','o','n','t','s','\\','E','x','t','e','r','n','a','l',' ','F','o','n','t','s','\0'};
/* Interesting and well-known (frequently-assumed!) font names */
static const WCHAR Lucida_Sans_Unicode[] = {'L','u','c','i','d','a',' ','S','a','n','s',' ','U','n','i','c','o','d','e',0};
static const WCHAR Microsoft_Sans_Serif[] = {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0 };
static const WCHAR Tahoma[] = {'T','a','h','o','m','a',0};
static const WCHAR MS_UI_Gothic[] = {'M','S',' ','U','I',' ','G','o','t','h','i','c',0};
static const WCHAR SimSun[] = {'S','i','m','S','u','n',0};
static const WCHAR Gulim[] = {'G','u','l','i','m',0};
static const WCHAR PMingLiU[] = {'P','M','i','n','g','L','i','U',0};
static const WCHAR Batang[] = {'B','a','t','a','n','g',0};
static const WCHAR ArabicW[] = {'A','r','a','b','i','c','\0'};
static const WCHAR BalticW[] = {'B','a','l','t','i','c','\0'};
static const WCHAR CHINESE_BIG5W[] = {'C','H','I','N','E','S','E','_','B','I','G','5','\0'};
static const WCHAR CHINESE_GB2312W[] = {'C','H','I','N','E','S','E','_','G','B','2','3','1','2','\0'};
static const WCHAR Central_EuropeanW[] = {'C','e','n','t','r','a','l',' ',
'E','u','r','o','p','e','a','n','\0'};
static const WCHAR CyrillicW[] = {'C','y','r','i','l','l','i','c','\0'};
static const WCHAR GreekW[] = {'G','r','e','e','k','\0'};
static const WCHAR HangulW[] = {'H','a','n','g','u','l','\0'};
static const WCHAR Hangul_Johab_W[] = {'H','a','n','g','u','l','(','J','o','h','a','b',')','\0'};
static const WCHAR HebrewW[] = {'H','e','b','r','e','w','\0'};
static const WCHAR JapaneseW[] = {'J','a','p','a','n','e','s','e','\0'};
static const WCHAR SymbolW[] = {'S','y','m','b','o','l','\0'};
static const WCHAR ThaiW[] = {'T','h','a','i','\0'};
static const WCHAR TurkishW[] = {'T','u','r','k','i','s','h','\0'};
static const WCHAR VietnameseW[] = {'V','i','e','t','n','a','m','e','s','e','\0'};
static const WCHAR WesternW[] = {'W','e','s','t','e','r','n','\0'};
static const WCHAR OEM_DOSW[] = {'O','E','M','/','D','O','S','\0'};
static const WCHAR * const ElfScriptsW[32] = { /* these are in the order of the fsCsb[0] bits */
WesternW, /*00*/
Central_EuropeanW,
CyrillicW,
GreekW,
TurkishW,
HebrewW,
ArabicW,
BalticW,
VietnameseW, /*08*/
NULL, NULL, NULL, NULL, NULL, NULL, NULL, /*15*/
ThaiW,
JapaneseW,
CHINESE_GB2312W,
HangulW,
CHINESE_BIG5W,
Hangul_Johab_W,
NULL, NULL, /*23*/
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
SymbolW /*31*/
};
typedef struct {
WCHAR *name;
INT charset;
} NameCs;
typedef struct tagFontSubst {
struct list entry;
NameCs from;
NameCs to;
} FontSubst;
struct font_mapping
{
struct list entry;
int refcount;
dev_t dev;
ino_t ino;
void *data;
size_t size;
};
static struct list mappings_list = LIST_INIT( mappings_list );
static BOOL have_installed_roman_font = FALSE; /* CreateFontInstance will fail if this is still FALSE */
static CRITICAL_SECTION freetype_cs;
static CRITICAL_SECTION_DEBUG critsect_debug =
{
0, 0, &freetype_cs,
{ &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
0, 0, { (DWORD_PTR)(__FILE__ ": freetype_cs") }
};
static CRITICAL_SECTION freetype_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
static const WCHAR font_mutex_nameW[] = {'_','_','W','I','N','E','_','F','O','N','T','_','M','U','T','E','X','_','_','\0'};
static const WCHAR szDefaultFallbackLink[] = {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0};
static BOOL use_default_fallback = FALSE;
static BOOL get_glyph_index_linked(GdiFont *font, UINT c, GdiFont **linked_font, FT_UInt *glyph);
static const WCHAR system_link[] = {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
'W','i','n','d','o','w','s',' ','N','T','\\',
'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\','F','o','n','t','L','i','n','k','\\',
'S','y','s','t','e','m','L','i','n','k',0};
/****************************************
* Notes on .fon files
*
* The fonts System, FixedSys and Terminal are special. There are typically multiple
* versions installed for different resolutions and codepages. Windows stores which one to use
* in HKEY_CURRENT_CONFIG\\Software\\Fonts.
* Key Meaning
* FIXEDFON.FON FixedSys
* FONTS.FON System
* OEMFONT.FON Terminal
* LogPixels Current dpi set by the display control panel applet
* (HKLM\\Software\\Microsoft\\Windows NT\\CurrentVersion\\FontDPI
* also has a LogPixels value that appears to mirror this)
*
* On my system these values have data: vgafix.fon, vgasys.fon, vga850.fon and 96 respectively
* (vgaoem.fon would be your oemfont.fon if you have a US setup).
* If the resolution is changed to be >= 109dpi then the fonts goto 8514fix, 8514sys and 8514oem
* (not sure what's happening to the oem codepage here). 109 is nicely halfway between 96 and 120dpi,
* so that makes sense.
*
* Additionally Windows also loads the fonts listed in the [386enh] section of system.ini (this doesn't appear
* to be mapped into the registry on Windows 2000 at least).
* I have
* woafont=app850.fon
* ega80woa.fon=ega80850.fon
* ega40woa.fon=ega40850.fon
* cga80woa.fon=cga80850.fon
* cga40woa.fon=cga40850.fon
*/
/* These are all structures needed for the GSUB table */
#define GSUB_TAG MS_MAKE_TAG('G', 'S', 'U', 'B')
#define TATEGAKI_LOWER_BOUND 0x02F1
typedef struct {
DWORD version;
WORD ScriptList;
WORD FeatureList;
WORD LookupList;
} GSUB_Header;
typedef struct {
CHAR ScriptTag[4];
WORD Script;
} GSUB_ScriptRecord;
typedef struct {
WORD ScriptCount;
GSUB_ScriptRecord ScriptRecord[1];
} GSUB_ScriptList;
typedef struct {
CHAR LangSysTag[4];
WORD LangSys;
} GSUB_LangSysRecord;
typedef struct {
WORD DefaultLangSys;
WORD LangSysCount;
GSUB_LangSysRecord LangSysRecord[1];
} GSUB_Script;
typedef struct {
WORD LookupOrder; /* Reserved */
WORD ReqFeatureIndex;
WORD FeatureCount;
WORD FeatureIndex[1];
} GSUB_LangSys;
typedef struct {
CHAR FeatureTag[4];
WORD Feature;
} GSUB_FeatureRecord;
typedef struct {
WORD FeatureCount;
GSUB_FeatureRecord FeatureRecord[1];
} GSUB_FeatureList;
typedef struct {
WORD FeatureParams; /* Reserved */
WORD LookupCount;
WORD LookupListIndex[1];
} GSUB_Feature;
typedef struct {
WORD LookupCount;
WORD Lookup[1];
} GSUB_LookupList;
typedef struct {
WORD LookupType;
WORD LookupFlag;
WORD SubTableCount;
WORD SubTable[1];
} GSUB_LookupTable;
typedef struct {
WORD CoverageFormat;
WORD GlyphCount;
WORD GlyphArray[1];
} GSUB_CoverageFormat1;
typedef struct {
WORD Start;
WORD End;
WORD StartCoverageIndex;
} GSUB_RangeRecord;
typedef struct {
WORD CoverageFormat;
WORD RangeCount;
GSUB_RangeRecord RangeRecord[1];
} GSUB_CoverageFormat2;
typedef struct {
WORD SubstFormat; /* = 1 */
WORD Coverage;
WORD DeltaGlyphID;
} GSUB_SingleSubstFormat1;
typedef struct {
WORD SubstFormat; /* = 2 */
WORD Coverage;
WORD GlyphCount;
WORD Substitute[1];
}GSUB_SingleSubstFormat2;
#ifdef HAVE_CARBON_CARBON_H
static char *find_cache_dir(void)
{
FSRef ref;
OSErr err;
static char cached_path[MAX_PATH];
static const char *wine = "/Wine", *fonts = "/Fonts";
if(*cached_path) return cached_path;
err = FSFindFolder(kUserDomain, kCachedDataFolderType, kCreateFolder, &ref);
if(err != noErr)
{
WARN("can't create cached data folder\n");
return NULL;
}
err = FSRefMakePath(&ref, (unsigned char*)cached_path, sizeof(cached_path));
if(err != noErr)
{
WARN("can't create cached data path\n");
*cached_path = '\0';
return NULL;
}
if(strlen(cached_path) + strlen(wine) + strlen(fonts) + 1 > sizeof(cached_path))
{
ERR("Could not create full path\n");
*cached_path = '\0';
return NULL;
}
strcat(cached_path, wine);
if(mkdir(cached_path, 0700) == -1 && errno != EEXIST)
{
WARN("Couldn't mkdir %s\n", cached_path);
*cached_path = '\0';
return NULL;
}
strcat(cached_path, fonts);
if(mkdir(cached_path, 0700) == -1 && errno != EEXIST)
{
WARN("Couldn't mkdir %s\n", cached_path);
*cached_path = '\0';
return NULL;
}
return cached_path;
}
/******************************************************************
* expand_mac_font
*
* Extracts individual TrueType font files from a Mac suitcase font
* and saves them into the user's caches directory (see
* find_cache_dir()).
* Returns a NULL terminated array of filenames.
*
* We do this because they are apps that try to read ttf files
* themselves and they don't like Mac suitcase files.
*/
static char **expand_mac_font(const char *path)
{
FSRef ref;
SInt16 res_ref;
OSStatus s;
unsigned int idx;
const char *out_dir;
const char *filename;
int output_len;
struct {
char **array;
unsigned int size, max_size;
} ret;
TRACE("path %s\n", path);
s = FSPathMakeRef((unsigned char*)path, &ref, FALSE);
if(s != noErr)
{
WARN("failed to get ref\n");
return NULL;
}
s = FSOpenResourceFile(&ref, 0, NULL, fsRdPerm, &res_ref);
if(s != noErr)
{
TRACE("no data fork, so trying resource fork\n");
res_ref = FSOpenResFile(&ref, fsRdPerm);
if(res_ref == -1)
{
TRACE("unable to open resource fork\n");
return NULL;
}
}
ret.size = 0;
ret.max_size = 10;
ret.array = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ret.max_size * sizeof(*ret.array));
if(!ret.array)
{
CloseResFile(res_ref);
return NULL;
}
out_dir = find_cache_dir();
filename = strrchr(path, '/');
if(!filename) filename = path;
else filename++;
/* output filename has the form out_dir/filename_%04x.ttf */
output_len = strlen(out_dir) + 1 + strlen(filename) + 5 + 5;
UseResFile(res_ref);
idx = 1;
while(1)
{
FamRec *fam_rec;
unsigned short *num_faces_ptr, num_faces, face;
AsscEntry *assoc;
Handle fond;
ResType fond_res = FT_MAKE_TAG('F','O','N','D');
fond = Get1IndResource(fond_res, idx);
if(!fond) break;
TRACE("got fond resource %d\n", idx);
HLock(fond);
fam_rec = *(FamRec**)fond;
num_faces_ptr = (unsigned short *)(fam_rec + 1);
num_faces = GET_BE_WORD(*num_faces_ptr);
num_faces++;
assoc = (AsscEntry*)(num_faces_ptr + 1);
TRACE("num faces %04x\n", num_faces);
for(face = 0; face < num_faces; face++, assoc++)
{
Handle sfnt;
ResType sfnt_res = FT_MAKE_TAG('s','f','n','t');
unsigned short size, font_id;
char *output;
size = GET_BE_WORD(assoc->fontSize);
font_id = GET_BE_WORD(assoc->fontID);
if(size != 0)
{
TRACE("skipping id %04x because it's not scalable (fixed size %d)\n", font_id, size);
continue;
}
TRACE("trying to load sfnt id %04x\n", font_id);
sfnt = GetResource(sfnt_res, font_id);
if(!sfnt)
{
TRACE("can't get sfnt resource %04x\n", font_id);
continue;
}
output = HeapAlloc(GetProcessHeap(), 0, output_len);
if(output)
{
int fd;
sprintf(output, "%s/%s_%04x.ttf", out_dir, filename, font_id);
fd = open(output, O_CREAT | O_EXCL | O_WRONLY, 0600);
if(fd != -1 || errno == EEXIST)
{
if(fd != -1)
{
unsigned char *sfnt_data;
HLock(sfnt);
sfnt_data = *(unsigned char**)sfnt;
write(fd, sfnt_data, GetHandleSize(sfnt));
HUnlock(sfnt);
close(fd);
}
if(ret.size >= ret.max_size - 1) /* Always want the last element to be NULL */
{
ret.max_size *= 2;
ret.array = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ret.array, ret.max_size * sizeof(*ret.array));
}
ret.array[ret.size++] = output;
}
else
{
WARN("unable to create %s\n", output);
HeapFree(GetProcessHeap(), 0, output);
}
}
ReleaseResource(sfnt);
}
HUnlock(fond);
ReleaseResource(fond);
idx++;
}
CloseResFile(res_ref);
return ret.array;
}
#endif /* HAVE_CARBON_CARBON_H */
static inline BOOL is_win9x(void)
{
return GetVersion() & 0x80000000;
}
/*
This function builds an FT_Fixed from a double. It fails if the absolute
value of the float number is greater than 32768.
*/
static inline FT_Fixed FT_FixedFromFloat(double f)
{
return f * 0x10000;
}
/*
This function builds an FT_Fixed from a FIXED. It simply put f.value
in the highest 16 bits and f.fract in the lowest 16 bits of the FT_Fixed.
*/
static inline FT_Fixed FT_FixedFromFIXED(FIXED f)
{
return (FT_Fixed)((int)f.value << 16 | (unsigned int)f.fract);
}
static Face *find_face_from_filename(const WCHAR *file_name, const WCHAR *face_name)
{
Family *family;
Face *face;
const char *file;
DWORD len = WideCharToMultiByte(CP_UNIXCP, 0, file_name, -1, NULL, 0, NULL, NULL);
char *file_nameA = HeapAlloc(GetProcessHeap(), 0, len);
WideCharToMultiByte(CP_UNIXCP, 0, file_name, -1, file_nameA, len, NULL, NULL);
TRACE("looking for file %s name %s\n", debugstr_a(file_nameA), debugstr_w(face_name));
LIST_FOR_EACH_ENTRY(family, &font_list, Family, entry)
{
if(face_name && strcmpiW(face_name, family->FamilyName))
continue;
LIST_FOR_EACH_ENTRY(face, &family->faces, Face, entry)
{
if (!face->file)
continue;
file = strrchr(face->file, '/');
if(!file)
file = face->file;
else
file++;
if(!strcasecmp(file, file_nameA))
{
HeapFree(GetProcessHeap(), 0, file_nameA);
return face;
}
}
}
HeapFree(GetProcessHeap(), 0, file_nameA);
return NULL;
}
static Family *find_family_from_name(const WCHAR *name)
{
Family *family;
LIST_FOR_EACH_ENTRY(family, &font_list, Family, entry)
{
if(!strcmpiW(family->FamilyName, name))
return family;
}
return NULL;
}
static void DumpSubstList(void)
{
FontSubst *psub;
LIST_FOR_EACH_ENTRY(psub, &font_subst_list, FontSubst, entry)
{
if(psub->from.charset != -1 || psub->to.charset != -1)
TRACE("%s:%d -> %s:%d\n", debugstr_w(psub->from.name),
psub->from.charset, debugstr_w(psub->to.name), psub->to.charset);
else
TRACE("%s -> %s\n", debugstr_w(psub->from.name),
debugstr_w(psub->to.name));
}
return;
}
static LPWSTR strdupW(LPCWSTR p)
{
LPWSTR ret;
DWORD len = (strlenW(p) + 1) * sizeof(WCHAR);
ret = HeapAlloc(GetProcessHeap(), 0, len);
memcpy(ret, p, len);
return ret;
}
static LPSTR strdupA(LPCSTR p)
{
LPSTR ret;
DWORD len = (strlen(p) + 1);
ret = HeapAlloc(GetProcessHeap(), 0, len);
memcpy(ret, p, len);
return ret;
}
static FontSubst *get_font_subst(const struct list *subst_list, const WCHAR *from_name,
INT from_charset)
{
FontSubst *element;
LIST_FOR_EACH_ENTRY(element, subst_list, FontSubst, entry)
{
if(!strcmpiW(element->from.name, from_name) &&
(element->from.charset == from_charset ||
element->from.charset == -1))
return element;
}
return NULL;
}
#define ADD_FONT_SUBST_FORCE 1
static BOOL add_font_subst(struct list *subst_list, FontSubst *subst, INT flags)
{
FontSubst *from_exist, *to_exist;
from_exist = get_font_subst(subst_list, subst->from.name, subst->from.charset);
if(from_exist && (flags & ADD_FONT_SUBST_FORCE))
{
list_remove(&from_exist->entry);
HeapFree(GetProcessHeap(), 0, &from_exist->from.name);
HeapFree(GetProcessHeap(), 0, &from_exist->to.name);
HeapFree(GetProcessHeap(), 0, from_exist);
from_exist = NULL;
}
if(!from_exist)
{
to_exist = get_font_subst(subst_list, subst->to.name, subst->to.charset);
if(to_exist)
{
HeapFree(GetProcessHeap(), 0, subst->to.name);
subst->to.name = strdupW(to_exist->to.name);
}
list_add_tail(subst_list, &subst->entry);
return TRUE;
}
HeapFree(GetProcessHeap(), 0, subst->from.name);
HeapFree(GetProcessHeap(), 0, subst->to.name);
HeapFree(GetProcessHeap(), 0, subst);
return FALSE;
}
static void split_subst_info(NameCs *nc, LPSTR str)
{
CHAR *p = strrchr(str, ',');
DWORD len;
nc->charset = -1;
if(p && *(p+1)) {
nc->charset = strtol(p+1, NULL, 10);
*p = '\0';
}
len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
nc->name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
MultiByteToWideChar(CP_ACP, 0, str, -1, nc->name, len);
}
static void LoadSubstList(void)
{
FontSubst *psub;
HKEY hkey;
DWORD valuelen, datalen, i = 0, type, dlen, vlen;
LPSTR value;
LPVOID data;
if(RegOpenKeyA(HKEY_LOCAL_MACHINE,
"Software\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes",
&hkey) == ERROR_SUCCESS) {
RegQueryInfoKeyA(hkey, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
&valuelen, &datalen, NULL, NULL);
valuelen++; /* returned value doesn't include room for '\0' */
value = HeapAlloc(GetProcessHeap(), 0, valuelen * sizeof(CHAR));
data = HeapAlloc(GetProcessHeap(), 0, datalen);
dlen = datalen;
vlen = valuelen;
while(RegEnumValueA(hkey, i++, value, &vlen, NULL, &type, data,
&dlen) == ERROR_SUCCESS) {
TRACE("Got %s=%s\n", debugstr_a(value), debugstr_a(data));
psub = HeapAlloc(GetProcessHeap(), 0, sizeof(*psub));
split_subst_info(&psub->from, value);
split_subst_info(&psub->to, data);
/* Win 2000 doesn't allow mapping between different charsets
or mapping of DEFAULT_CHARSET */
if ((psub->from.charset && psub->to.charset != psub->from.charset) ||
psub->to.charset == DEFAULT_CHARSET) {
HeapFree(GetProcessHeap(), 0, psub->to.name);
HeapFree(GetProcessHeap(), 0, psub->from.name);
HeapFree(GetProcessHeap(), 0, psub);
} else {
add_font_subst(&font_subst_list, psub, 0);
}
/* reset dlen and vlen */
dlen = datalen;
vlen = valuelen;
}
HeapFree(GetProcessHeap(), 0, data);
HeapFree(GetProcessHeap(), 0, value);
RegCloseKey(hkey);
}
}
/*****************************************************************
* get_name_table_entry
*
* Supply the platform, encoding, language and name ids in req
* and if the name exists the function will fill in the string
* and string_len members. The string is owned by FreeType so
* don't free it. Returns TRUE if the name is found else FALSE.
*/
static BOOL get_name_table_entry(FT_Face ft_face, FT_SfntName *req)
{
FT_SfntName name;
FT_UInt num_names, name_index;
if(FT_IS_SFNT(ft_face))
{
num_names = pFT_Get_Sfnt_Name_Count(ft_face);
for(name_index = 0; name_index < num_names; name_index++)
{
if(!pFT_Get_Sfnt_Name(ft_face, name_index, &name))
{
if((name.platform_id == req->platform_id) &&
(name.encoding_id == req->encoding_id) &&
(name.language_id == req->language_id) &&
(name.name_id == req->name_id))
{
req->string = name.string;
req->string_len = name.string_len;
return TRUE;
}
}
}
}
req->string = NULL;
req->string_len = 0;
return FALSE;
}
static WCHAR *get_familyname(FT_Face ft_face)
{
WCHAR *family = NULL;
FT_SfntName name;
name.platform_id = TT_PLATFORM_MICROSOFT;
name.encoding_id = TT_MS_ID_UNICODE_CS;
name.language_id = GetUserDefaultLCID();
name.name_id = TT_NAME_ID_FONT_FAMILY;
if(get_name_table_entry(ft_face, &name))
{
FT_UInt i;
/* String is not nul terminated and string_len is a byte length. */
family = HeapAlloc(GetProcessHeap(), 0, name.string_len + 2);
for(i = 0; i < name.string_len / 2; i++)
{
WORD *tmp = (WORD *)&name.string[i * 2];
family[i] = GET_BE_WORD(*tmp);
}
family[i] = 0;
TRACE("Got localised name %s\n", debugstr_w(family));
}
return family;
}
/*****************************************************************
* load_sfnt_table
*
* Wrapper around FT_Load_Sfnt_Table to cope with older versions
* of FreeType that don't export this function.
*
*/
static FT_Error load_sfnt_table(FT_Face ft_face, FT_ULong table, FT_Long offset, FT_Byte *buf, FT_ULong *len)
{
FT_Error err;
/* If the FT_Load_Sfnt_Table function is there we'll use it */
if(pFT_Load_Sfnt_Table)
{
err = pFT_Load_Sfnt_Table(ft_face, table, offset, buf, len);
}
#ifdef HAVE_FREETYPE_INTERNAL_SFNT_H
else /* Do it the hard way */
{
TT_Face tt_face = (TT_Face) ft_face;
SFNT_Interface *sfnt;
if (FT_Version.major==2 && FT_Version.minor==0)
{
/* 2.0.x */
sfnt = *(SFNT_Interface**)((char*)tt_face + 528);
}
else
{
/* A field was added in the middle of the structure in 2.1.x */
sfnt = *(SFNT_Interface**)((char*)tt_face + 532);
}
err = sfnt->load_any(tt_face, table, offset, buf, len);
}
#else
else
{
static int msg;
if(!msg)
{
MESSAGE("This version of Wine was compiled with freetype headers later than 2.2.0\n"
"but is being run with a freetype library without the FT_Load_Sfnt_Table function.\n"
"Please upgrade your freetype library.\n");
msg++;
}
err = FT_Err_Unimplemented_Feature;
}
#endif
return err;
}
static inline int TestStyles(DWORD flags, DWORD styles)
{
return (flags & styles) == styles;
}
static int StyleOrdering(Face *face)
{
if (TestStyles(face->ntmFlags, NTM_BOLD | NTM_ITALIC))
return 3;
if (TestStyles(face->ntmFlags, NTM_ITALIC))
return 2;
if (TestStyles(face->ntmFlags, NTM_BOLD))
return 1;
if (TestStyles(face->ntmFlags, NTM_REGULAR))
return 0;
WARN("Don't know how to order font %s %s with flags 0x%08x\n",
debugstr_w(face->family->FamilyName),
debugstr_w(face->StyleName),
face->ntmFlags);
return 9999;
}
/* Add a style of face to a font family using an ordering of the list such
that regular fonts come before bold and italic, and single styles come
before compound styles. */
static void AddFaceToFamily(Face *face, Family *family)
{
struct list *entry;
LIST_FOR_EACH( entry, &family->faces )
{
Face *ent = LIST_ENTRY(entry, Face, entry);
if (StyleOrdering(face) < StyleOrdering(ent)) break;
}
list_add_before( entry, &face->entry );
}
#define ADDFONT_EXTERNAL_FONT 0x01
#define ADDFONT_FORCE_BITMAP 0x02
static INT AddFontToList(const char *file, void *font_data_ptr, DWORD font_data_size, char *fake_family, const WCHAR *target_family, DWORD flags)
{
FT_Face ft_face;
TT_OS2 *pOS2;
TT_Header *pHeader = NULL;
WCHAR *english_family, *localised_family, *StyleW;
DWORD len;
Family *family;
Face *face;
struct list *family_elem_ptr, *face_elem_ptr;
FT_Error err;
FT_Long face_index = 0, num_faces;
#ifdef HAVE_FREETYPE_FTWINFNT_H
FT_WinFNT_HeaderRec winfnt_header;
#endif
int i, bitmap_num, internal_leading;
FONTSIGNATURE fs;
/* we always load external fonts from files - otherwise we would get a crash in update_reg_entries */
assert(file || !(flags & ADDFONT_EXTERNAL_FONT));
#ifdef HAVE_CARBON_CARBON_H
if(file && !fake_family)
{
char **mac_list = expand_mac_font(file);
if(mac_list)
{
BOOL had_one = FALSE;
char **cursor;
for(cursor = mac_list; *cursor; cursor++)
{
had_one = TRUE;
AddFontToList(*cursor, NULL, 0, NULL, NULL, flags);
HeapFree(GetProcessHeap(), 0, *cursor);
}
HeapFree(GetProcessHeap(), 0, mac_list);
if(had_one)
return 1;
}
}
#endif /* HAVE_CARBON_CARBON_H */
do {
char *family_name = fake_family;
if (file)
{
TRACE("Loading font file %s index %ld\n", debugstr_a(file), face_index);
err = pFT_New_Face(library, file, face_index, &ft_face);
} else
{
TRACE("Loading font from ptr %p size %d, index %ld\n", font_data_ptr, font_data_size, face_index);
err = pFT_New_Memory_Face(library, font_data_ptr, font_data_size, face_index, &ft_face);
}
if(err != 0) {
WARN("Unable to load font %s/%p err = %x\n", debugstr_a(file), font_data_ptr, err);
return 0;
}
if(!FT_IS_SFNT(ft_face) && (FT_IS_SCALABLE(ft_face) || !(flags & ADDFONT_FORCE_BITMAP))) { /* for now we'll accept TT/OT or bitmap fonts*/
WARN("Ignoring font %s/%p\n", debugstr_a(file), font_data_ptr);
pFT_Done_Face(ft_face);
return 0;
}
/* There are too many bugs in FreeType < 2.1.9 for bitmap font support */
if(!FT_IS_SCALABLE(ft_face) && FT_SimpleVersion < ((2 << 16) | (1 << 8) | (9 << 0))) {
WARN("FreeType version < 2.1.9, skipping bitmap font %s/%p\n", debugstr_a(file), font_data_ptr);
pFT_Done_Face(ft_face);
return 0;
}
if(FT_IS_SFNT(ft_face))
{
if(!(pOS2 = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_os2)) ||
!pFT_Get_Sfnt_Table(ft_face, ft_sfnt_hhea) ||
!(pHeader = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_head)))
{
TRACE("Font %s/%p lacks either an OS2, HHEA or HEAD table.\n"
"Skipping this font.\n", debugstr_a(file), font_data_ptr);
pFT_Done_Face(ft_face);
return 0;
}
/* Wine uses ttfs as an intermediate step in building its bitmap fonts;
we don't want to load these. */
if(!memcmp(pOS2->achVendID, "Wine", sizeof(pOS2->achVendID)))
{
FT_ULong len = 0;
if(!load_sfnt_table(ft_face, FT_MAKE_TAG('E','B','S','C'), 0, NULL, &len))
{
TRACE("Skipping Wine bitmap-only TrueType font %s\n", debugstr_a(file));
pFT_Done_Face(ft_face);
return 0;
}
}
}
if(!ft_face->family_name || !ft_face->style_name) {
TRACE("Font %s/%p lacks either a family or style name\n", debugstr_a(file), font_data_ptr);
pFT_Done_Face(ft_face);
return 0;
}
if(ft_face->family_name[0] == '.') /* Ignore fonts with names beginning with a dot */
{
TRACE("Ignoring %s since its family name begins with a dot\n", debugstr_a(file));
pFT_Done_Face(ft_face);
return 0;
}
if (target_family)
{
localised_family = get_familyname(ft_face);
if (localised_family && strcmpiW(localised_family,target_family)!=0)
{
TRACE("Skipping Index %i: Incorrect Family name for replacement\n",(INT)face_index);
HeapFree(GetProcessHeap(), 0, localised_family);
num_faces = ft_face->num_faces;
pFT_Done_Face(ft_face);
continue;
}
HeapFree(GetProcessHeap(), 0, localised_family);
}
if(!family_name)
family_name = ft_face->family_name;
bitmap_num = 0;
do {
My_FT_Bitmap_Size *size = NULL;
FT_ULong tmp_size;
if(!FT_IS_SCALABLE(ft_face))
size = (My_FT_Bitmap_Size *)ft_face->available_sizes + bitmap_num;
len = MultiByteToWideChar(CP_ACP, 0, family_name, -1, NULL, 0);
english_family = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
MultiByteToWideChar(CP_ACP, 0, family_name, -1, english_family, len);
localised_family = NULL;
if(!fake_family) {
localised_family = get_familyname(ft_face);
if(localised_family && !strcmpiW(localised_family, english_family)) {
HeapFree(GetProcessHeap(), 0, localised_family);
localised_family = NULL;
}
}
family = NULL;
LIST_FOR_EACH(family_elem_ptr, &font_list) {
family = LIST_ENTRY(family_elem_ptr, Family, entry);
if(!strcmpiW(family->FamilyName, localised_family ? localised_family : english_family))
break;
family = NULL;
}
if(!family) {
family = HeapAlloc(GetProcessHeap(), 0, sizeof(*family));
family->FamilyName = strdupW(localised_family ? localised_family : english_family);
list_init(&family->faces);
list_add_tail(&font_list, &family->entry);
if(localised_family) {
FontSubst *subst = HeapAlloc(GetProcessHeap(), 0, sizeof(*subst));
subst->from.name = strdupW(english_family);
subst->from.charset = -1;
subst->to.name = strdupW(localised_family);
subst->to.charset = -1;
add_font_subst(&font_subst_list, subst, 0);
}
}
HeapFree(GetProcessHeap(), 0, localised_family);
HeapFree(GetProcessHeap(), 0, english_family);
len = MultiByteToWideChar(CP_ACP, 0, ft_face->style_name, -1, NULL, 0);
StyleW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
MultiByteToWideChar(CP_ACP, 0, ft_face->style_name, -1, StyleW, len);
internal_leading = 0;
memset(&fs, 0, sizeof(fs));
pOS2 = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_os2);
if(pOS2) {
fs.fsCsb[0] = pOS2->ulCodePageRange1;
fs.fsCsb[1] = pOS2->ulCodePageRange2;
fs.fsUsb[0] = pOS2->ulUnicodeRange1;
fs.fsUsb[1] = pOS2->ulUnicodeRange2;
fs.fsUsb[2] = pOS2->ulUnicodeRange3;
fs.fsUsb[3] = pOS2->ulUnicodeRange4;
if(pOS2->version == 0) {
FT_UInt dummy;
if(!pFT_Get_First_Char || (pFT_Get_First_Char( ft_face, &dummy ) < 0x100))
fs.fsCsb[0] |= FS_LATIN1;
else
fs.fsCsb[0] |= FS_SYMBOL;
}
}
#ifdef HAVE_FREETYPE_FTWINFNT_H
else if(pFT_Get_WinFNT_Header && !pFT_Get_WinFNT_Header(ft_face, &winfnt_header)) {
CHARSETINFO csi;
TRACE("pix_h %d charset %d dpi %dx%d pt %d\n", winfnt_header.pixel_height, winfnt_header.charset,
winfnt_header.vertical_resolution,winfnt_header.horizontal_resolution, winfnt_header.nominal_point_size);
if(TranslateCharsetInfo((DWORD*)(UINT_PTR)winfnt_header.charset, &csi, TCI_SRCCHARSET))
fs = csi.fs;
internal_leading = winfnt_header.internal_leading;
}
#endif
face_elem_ptr = list_head(&family->faces);
while(face_elem_ptr) {
face = LIST_ENTRY(face_elem_ptr, Face, entry);
face_elem_ptr = list_next(&family->faces, face_elem_ptr);
if(!strcmpiW(face->StyleName, StyleW) &&
(FT_IS_SCALABLE(ft_face) || ((size->y_ppem == face->size.y_ppem) && !memcmp(&fs, &face->fs, sizeof(fs)) ))) {
TRACE("Already loaded font %s %s original version is %lx, this version is %lx\n",
debugstr_w(family->FamilyName), debugstr_w(StyleW),
face->font_version, pHeader ? pHeader->Font_Revision : 0);
if(fake_family) {
TRACE("This font is a replacement but the original really exists, so we'll skip the replacement\n");
HeapFree(GetProcessHeap(), 0, StyleW);
pFT_Done_Face(ft_face);
return 1;
}
if(!pHeader || pHeader->Font_Revision <= face->font_version) {
TRACE("Original font is newer so skipping this one\n");
HeapFree(GetProcessHeap(), 0, StyleW);
pFT_Done_Face(ft_face);
return 1;
} else {
TRACE("Replacing original with this one\n");
list_remove(&face->entry);
HeapFree(GetProcessHeap(), 0, face->file);
HeapFree(GetProcessHeap(), 0, face->StyleName);
HeapFree(GetProcessHeap(), 0, face);
break;
}
}
}
face = HeapAlloc(GetProcessHeap(), 0, sizeof(*face));
face->cached_enum_data = NULL;
face->StyleName = StyleW;
if (file)
{
face->file = strdupA(file);
face->font_data_ptr = NULL;
face->font_data_size = 0;
}
else
{
face->file = NULL;
face->font_data_ptr = font_data_ptr;
face->font_data_size = font_data_size;
}
face->face_index = face_index;
face->ntmFlags = 0;
if (ft_face->style_flags & FT_STYLE_FLAG_ITALIC)
face->ntmFlags |= NTM_ITALIC;
if (ft_face->style_flags & FT_STYLE_FLAG_BOLD)
face->ntmFlags |= NTM_BOLD;
if (face->ntmFlags == 0) face->ntmFlags = NTM_REGULAR;
face->font_version = pHeader ? pHeader->Font_Revision : 0;
face->family = family;
face->external = (flags & ADDFONT_EXTERNAL_FONT) ? TRUE : FALSE;
face->fs = fs;
memset(&face->fs_links, 0, sizeof(face->fs_links));
if(FT_IS_SCALABLE(ft_face)) {
memset(&face->size, 0, sizeof(face->size));
face->scalable = TRUE;
} else {
TRACE("Adding bitmap size h %d w %d size %ld x_ppem %ld y_ppem %ld\n",
size->height, size->width, size->size >> 6,
size->x_ppem >> 6, size->y_ppem >> 6);
face->size.height = size->height;
face->size.width = size->width;
face->size.size = size->size;
face->size.x_ppem = size->x_ppem;
face->size.y_ppem = size->y_ppem;
face->size.internal_leading = internal_leading;
face->scalable = FALSE;
}
/* check for the presence of the 'CFF ' table to check if the font is Type1 */
tmp_size = 0;
if (pFT_Load_Sfnt_Table && !pFT_Load_Sfnt_Table(ft_face, FT_MAKE_TAG('C','F','F',' '), 0, NULL, &tmp_size))
{
TRACE("Font %s/%p is OTF Type1\n", wine_dbgstr_a(file), font_data_ptr);
face->ntmFlags |= NTM_PS_OPENTYPE;
}
TRACE("fsCsb = %08x %08x/%08x %08x %08x %08x\n",
face->fs.fsCsb[0], face->fs.fsCsb[1],
face->fs.fsUsb[0], face->fs.fsUsb[1],
face->fs.fsUsb[2], face->fs.fsUsb[3]);
if(face->fs.fsCsb[0] == 0) { /* let's see if we can find any interesting cmaps */
for(i = 0; i < ft_face->num_charmaps; i++) {
switch(ft_face->charmaps[i]->encoding) {
case FT_ENCODING_UNICODE:
case FT_ENCODING_APPLE_ROMAN:
face->fs.fsCsb[0] |= FS_LATIN1;
break;
case FT_ENCODING_MS_SYMBOL:
face->fs.fsCsb[0] |= FS_SYMBOL;
break;
default:
break;
}
}
}
if (!(face->fs.fsCsb[0] & FS_SYMBOL))
have_installed_roman_font = TRUE;
AddFaceToFamily(face, family);
} while(!FT_IS_SCALABLE(ft_face) && ++bitmap_num < ft_face->num_fixed_sizes);
num_faces = ft_face->num_faces;
pFT_Done_Face(ft_face);
TRACE("Added font %s %s\n", debugstr_w(family->FamilyName),
debugstr_w(StyleW));
} while(num_faces > ++face_index);
return num_faces;
}
static INT AddFontFileToList(const char *file, char *fake_family, const WCHAR *target_family, DWORD flags)
{
return AddFontToList(file, NULL, 0, fake_family, target_family, flags);
}
static void DumpFontList(void)
{
Family *family;
Face *face;
struct list *family_elem_ptr, *face_elem_ptr;
LIST_FOR_EACH(family_elem_ptr, &font_list) {
family = LIST_ENTRY(family_elem_ptr, Family, entry);
TRACE("Family: %s\n", debugstr_w(family->FamilyName));
LIST_FOR_EACH(face_elem_ptr, &family->faces) {
face = LIST_ENTRY(face_elem_ptr, Face, entry);
TRACE("\t%s\t%08x", debugstr_w(face->StyleName), face->fs.fsCsb[0]);
if(!face->scalable)
TRACE(" %d", face->size.height);
TRACE("\n");
}
}
return;
}
/***********************************************************
* The replacement list is a way to map an entire font
* family onto another family. For example adding
*
* [HKCU\Software\Wine\Fonts\Replacements]
* "Wingdings"="Winedings"
*
* would enumerate the Winedings font both as Winedings and
* Wingdings. However if a real Wingdings font is present the
* replacement does not take place.
*
*/
static void LoadReplaceList(void)
{
HKEY hkey;
DWORD valuelen, datalen, i = 0, type, dlen, vlen;
LPWSTR value;
LPVOID data;
Family *family;
Face *face;
struct list *family_elem_ptr, *face_elem_ptr;
CHAR familyA[400];
/* @@ Wine registry key: HKCU\Software\Wine\Fonts\Replacements */
if(RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Fonts\\Replacements", &hkey) == ERROR_SUCCESS)
{
RegQueryInfoKeyW(hkey, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
&valuelen, &datalen, NULL, NULL);
valuelen++; /* returned value doesn't include room for '\0' */
value = HeapAlloc(GetProcessHeap(), 0, valuelen * sizeof(WCHAR));
data = HeapAlloc(GetProcessHeap(), 0, datalen);
dlen = datalen;
vlen = valuelen;
while(RegEnumValueW(hkey, i++, value, &vlen, NULL, &type, data,
&dlen) == ERROR_SUCCESS) {
TRACE("Got %s=%s\n", debugstr_w(value), debugstr_w(data));
/* "NewName"="Oldname" */
WideCharToMultiByte(CP_ACP, 0, value, -1, familyA, sizeof(familyA), NULL, NULL);
/* Find the old family and hence all of the font files
in that family */
LIST_FOR_EACH(family_elem_ptr, &font_list) {
family = LIST_ENTRY(family_elem_ptr, Family, entry);
if(!strcmpiW(family->FamilyName, data)) {
LIST_FOR_EACH(face_elem_ptr, &family->faces) {
face = LIST_ENTRY(face_elem_ptr, Face, entry);
TRACE("mapping %s %s to %s\n", debugstr_w(family->FamilyName),
debugstr_w(face->StyleName), familyA);
/* Now add a new entry with the new family name */
AddFontToList(face->file, face->font_data_ptr, face->font_data_size, familyA, family->FamilyName, ADDFONT_FORCE_BITMAP | (face->external ? ADDFONT_EXTERNAL_FONT : 0));
}
break;
}
}
/* reset dlen and vlen */
dlen = datalen;
vlen = valuelen;
}
HeapFree(GetProcessHeap(), 0, data);
HeapFree(GetProcessHeap(), 0, value);
RegCloseKey(hkey);
}
}
/*************************************************************
* init_system_links
*/
static BOOL init_system_links(void)
{
HKEY hkey;
BOOL ret = FALSE;
DWORD type, max_val, max_data, val_len, data_len, index;
WCHAR *value, *data;
WCHAR *entry, *next;
SYSTEM_LINKS *font_link, *system_font_link;
CHILD_FONT *child_font;
static const WCHAR tahoma_ttf[] = {'t','a','h','o','m','a','.','t','t','f',0};
static const WCHAR System[] = {'S','y','s','t','e','m',0};
FONTSIGNATURE fs;
Family *family;
Face *face;
FontSubst *psub;
if(RegOpenKeyW(HKEY_LOCAL_MACHINE, system_link, &hkey) == ERROR_SUCCESS)
{
RegQueryInfoKeyW(hkey, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &max_val, &max_data, NULL, NULL);
value = HeapAlloc(GetProcessHeap(), 0, (max_val + 1) * sizeof(WCHAR));
data = HeapAlloc(GetProcessHeap(), 0, max_data);
val_len = max_val + 1;
data_len = max_data;
index = 0;
while(RegEnumValueW(hkey, index++, value, &val_len, NULL, &type, (LPBYTE)data, &data_len) == ERROR_SUCCESS)
{
memset(&fs, 0, sizeof(fs));
psub = get_font_subst(&font_subst_list, value, -1);
/* Don't store fonts that are only substitutes for other fonts */
if(psub)
{
TRACE("%s: SystemLink entry for substituted font, ignoring\n", debugstr_w(value));
continue;
}
font_link = HeapAlloc(GetProcessHeap(), 0, sizeof(*font_link));
font_link->font_name = strdupW(value);
list_init(&font_link->links);
for(entry = data; (char*)entry < (char*)data + data_len && *entry != 0; entry = next)
{
WCHAR *face_name;
CHILD_FONT *child_font;
TRACE("%s: %s\n", debugstr_w(value), debugstr_w(entry));
next = entry + strlenW(entry) + 1;
face_name = strchrW(entry, ',');
if(face_name)
{
*face_name++ = 0;
while(isspaceW(*face_name))
face_name++;
psub = get_font_subst(&font_subst_list, face_name, -1);
if(psub)
face_name = psub->to.name;
}
face = find_face_from_filename(entry, face_name);
if(!face)
{
TRACE("Unable to find file %s face name %s\n", debugstr_w(entry), debugstr_w(face_name));
continue;
}
child_font = HeapAlloc(GetProcessHeap(), 0, sizeof(*child_font));
child_font->face = face;
child_font->font = NULL;
fs.fsCsb[0] |= face->fs.fsCsb[0];
fs.fsCsb[1] |= face->fs.fsCsb[1];
TRACE("Adding file %s index %ld\n", child_font->face->file, child_font->face->face_index);
list_add_tail(&font_link->links, &child_font->entry);
}
family = find_family_from_name(font_link->font_name);
if(family)
{
LIST_FOR_EACH_ENTRY(face, &family->faces, Face, entry)
{
face->fs_links = fs;
}
}
list_add_tail(&system_links, &font_link->entry);
val_len = max_val + 1;
data_len = max_data;
}
HeapFree(GetProcessHeap(), 0, value);
HeapFree(GetProcessHeap(), 0, data);
RegCloseKey(hkey);
}
/* Explicitly add an entry for the system font, this links to Tahoma and any links
that Tahoma has */
system_font_link = HeapAlloc(GetProcessHeap(), 0, sizeof(*system_font_link));
system_font_link->font_name = strdupW(System);
list_init(&system_font_link->links);
face = find_face_from_filename(tahoma_ttf, Tahoma);
if(face)
{
child_font = HeapAlloc(GetProcessHeap(), 0, sizeof(*child_font));
child_font->face = face;
child_font->font = NULL;
TRACE("Found Tahoma in %s index %ld\n", child_font->face->file, child_font->face->face_index);
list_add_tail(&system_font_link->links, &child_font->entry);
}
LIST_FOR_EACH_ENTRY(font_link, &system_links, SYSTEM_LINKS, entry)
{
if(!strcmpiW(font_link->font_name, Tahoma))
{
CHILD_FONT *font_link_entry;
LIST_FOR_EACH_ENTRY(font_link_entry, &font_link->links, CHILD_FONT, entry)
{
CHILD_FONT *new_child;
new_child = HeapAlloc(GetProcessHeap(), 0, sizeof(*new_child));
new_child->face = font_link_entry->face;
new_child->font = NULL;
list_add_tail(&system_font_link->links, &new_child->entry);
}
break;
}
}
list_add_tail(&system_links, &system_font_link->entry);
return ret;
}
static BOOL ReadFontDir(const char *dirname, BOOL external_fonts)
{
DIR *dir;
struct dirent *dent;
char path[MAX_PATH];
TRACE("Loading fonts from %s\n", debugstr_a(dirname));
dir = opendir(dirname);
if(!dir) {
WARN("Can't open directory %s\n", debugstr_a(dirname));
return FALSE;
}
while((dent = readdir(dir)) != NULL) {
struct stat statbuf;
if(!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
continue;
TRACE("Found %s in %s\n", debugstr_a(dent->d_name), debugstr_a(dirname));
sprintf(path, "%s/%s", dirname, dent->d_name);
if(stat(path, &statbuf) == -1)
{
WARN("Can't stat %s\n", debugstr_a(path));
continue;
}
if(S_ISDIR(statbuf.st_mode))
ReadFontDir(path, external_fonts);
else
AddFontFileToList(path, NULL, NULL, external_fonts ? ADDFONT_EXTERNAL_FONT : 0);
}
closedir(dir);
return TRUE;
}
static void load_fontconfig_fonts(void)
{
#ifdef SONAME_LIBFONTCONFIG
void *fc_handle = NULL;
FcConfig *config;
FcPattern *pat;
FcObjectSet *os;
FcFontSet *fontset;
int i, len;
char *file;
const char *ext;
fc_handle = wine_dlopen(SONAME_LIBFONTCONFIG, RTLD_NOW, NULL, 0);
if(!fc_handle) {
TRACE("Wine cannot find the fontconfig library (%s).\n",
SONAME_LIBFONTCONFIG);
return;
}
#define LOAD_FUNCPTR(f) if((p##f = wine_dlsym(fc_handle, #f, NULL, 0)) == NULL){WARN("Can't find symbol %s\n", #f); goto sym_not_found;}
LOAD_FUNCPTR(FcConfigGetCurrent);
LOAD_FUNCPTR(FcFontList);
LOAD_FUNCPTR(FcFontSetDestroy);
LOAD_FUNCPTR(FcInit);
LOAD_FUNCPTR(FcObjectSetAdd);
LOAD_FUNCPTR(FcObjectSetCreate);
LOAD_FUNCPTR(FcObjectSetDestroy);
LOAD_FUNCPTR(FcPatternCreate);
LOAD_FUNCPTR(FcPatternDestroy);
LOAD_FUNCPTR(FcPatternGetBool);
LOAD_FUNCPTR(FcPatternGetString);
#undef LOAD_FUNCPTR
if(!pFcInit()) return;
config = pFcConfigGetCurrent();
pat = pFcPatternCreate();
os = pFcObjectSetCreate();
pFcObjectSetAdd(os, FC_FILE);
pFcObjectSetAdd(os, FC_SCALABLE);
fontset = pFcFontList(config, pat, os);
if(!fontset) return;
for(i = 0; i < fontset->nfont; i++) {
FcBool scalable;
if(pFcPatternGetString(fontset->fonts[i], FC_FILE, 0, (FcChar8**)&file) != FcResultMatch)
continue;
TRACE("fontconfig: %s\n", file);
/* We're just interested in OT/TT fonts for now, so this hack just
picks up the scalable fonts without extensions .pf[ab] to save time
loading every other font */
if(pFcPatternGetBool(fontset->fonts[i], FC_SCALABLE, 0, &scalable) == FcResultMatch && !scalable)
{
TRACE("not scalable\n");
continue;
}
len = strlen( file );
if(len < 4) continue;
ext = &file[ len - 3 ];
if(strcasecmp(ext, "pfa") && strcasecmp(ext, "pfb"))
AddFontFileToList(file, NULL, NULL, ADDFONT_EXTERNAL_FONT);
}
pFcFontSetDestroy(fontset);
pFcObjectSetDestroy(os);
pFcPatternDestroy(pat);
sym_not_found:
#endif
return;
}
static BOOL load_font_from_data_dir(LPCWSTR file)
{
BOOL ret = FALSE;
const char *data_dir = wine_get_data_dir();
if (!data_dir) data_dir = wine_get_build_dir();
if (data_dir)
{
INT len;
char *unix_name;
len = WideCharToMultiByte(CP_UNIXCP, 0, file, -1, NULL, 0, NULL, NULL);
unix_name = HeapAlloc(GetProcessHeap(), 0, strlen(data_dir) + len + sizeof("/fonts/"));
strcpy(unix_name, data_dir);
strcat(unix_name, "/fonts/");
WideCharToMultiByte(CP_UNIXCP, 0, file, -1, unix_name + strlen(unix_name), len, NULL, NULL);
EnterCriticalSection( &freetype_cs );
ret = AddFontFileToList(unix_name, NULL, NULL, ADDFONT_FORCE_BITMAP);
LeaveCriticalSection( &freetype_cs );
HeapFree(GetProcessHeap(), 0, unix_name);
}
return ret;
}
static BOOL load_font_from_winfonts_dir(LPCWSTR file)
{
static const WCHAR slashW[] = {'\\','\0'};
BOOL ret = FALSE;
WCHAR windowsdir[MAX_PATH];
char *unixname;
GetWindowsDirectoryW(windowsdir, sizeof(windowsdir) / sizeof(WCHAR));
strcatW(windowsdir, fontsW);
strcatW(windowsdir, slashW);
strcatW(windowsdir, file);
if ((unixname = wine_get_unix_file_name(windowsdir))) {
EnterCriticalSection( &freetype_cs );
ret = AddFontFileToList(unixname, NULL, NULL, ADDFONT_FORCE_BITMAP);
LeaveCriticalSection( &freetype_cs );
HeapFree(GetProcessHeap(), 0, unixname);
}
return ret;
}
static void load_system_fonts(void)
{
HKEY hkey;
WCHAR data[MAX_PATH], windowsdir[MAX_PATH], pathW[MAX_PATH];
const WCHAR * const *value;
DWORD dlen, type;
static const WCHAR fmtW[] = {'%','s','\\','%','s','\0'};
char *unixname;
if(RegOpenKeyW(HKEY_CURRENT_CONFIG, system_fonts_reg_key, &hkey) == ERROR_SUCCESS) {
GetWindowsDirectoryW(windowsdir, sizeof(windowsdir) / sizeof(WCHAR));
strcatW(windowsdir, fontsW);
for(value = SystemFontValues; *value; value++) {
dlen = sizeof(data);
if(RegQueryValueExW(hkey, *value, 0, &type, (void*)data, &dlen) == ERROR_SUCCESS &&
type == REG_SZ) {
BOOL added = FALSE;
sprintfW(pathW, fmtW, windowsdir, data);
if((unixname = wine_get_unix_file_name(pathW))) {
added = AddFontFileToList(unixname, NULL, NULL, ADDFONT_FORCE_BITMAP);
HeapFree(GetProcessHeap(), 0, unixname);
}
if (!added)
load_font_from_data_dir(data);
}
}
RegCloseKey(hkey);
}
}
/*************************************************************
*
* This adds registry entries for any externally loaded fonts
* (fonts from fontconfig or FontDirs). It also deletes entries
* of no longer existing fonts.
*
*/
static void update_reg_entries(void)
{
HKEY winnt_key = 0, win9x_key = 0, external_key = 0;
LPWSTR valueW;
DWORD len, len_fam;
Family *family;
Face *face;
struct list *family_elem_ptr, *face_elem_ptr;
WCHAR *file;
static const WCHAR TrueType[] = {' ','(','T','r','u','e','T','y','p','e',')','\0'};
static const WCHAR spaceW[] = {' ', '\0'};
char *path;
if(RegCreateKeyExW(HKEY_LOCAL_MACHINE, winnt_font_reg_key,
0, NULL, 0, KEY_ALL_ACCESS, NULL, &winnt_key, NULL) != ERROR_SUCCESS) {
ERR("Can't create Windows font reg key\n");
goto end;
}
if(RegCreateKeyExW(HKEY_LOCAL_MACHINE, win9x_font_reg_key,
0, NULL, 0, KEY_ALL_ACCESS, NULL, &win9x_key, NULL) != ERROR_SUCCESS) {
ERR("Can't create Windows font reg key\n");
goto end;
}
if(RegCreateKeyExW(HKEY_CURRENT_USER, external_fonts_reg_key,
0, NULL, 0, KEY_ALL_ACCESS, NULL, &external_key, NULL) != ERROR_SUCCESS) {
ERR("Can't create external font reg key\n");
goto end;
}
/* enumerate the fonts and add external ones to the two keys */
LIST_FOR_EACH(family_elem_ptr, &font_list) {
family = LIST_ENTRY(family_elem_ptr, Family, entry);
len_fam = strlenW(family->FamilyName) + sizeof(TrueType) / sizeof(WCHAR) + 1;
LIST_FOR_EACH(face_elem_ptr, &family->faces) {
face = LIST_ENTRY(face_elem_ptr, Face, entry);
if(!face->external) continue;
len = len_fam;
if (!(face->ntmFlags & NTM_REGULAR))
len = len_fam + strlenW(face->StyleName) + 1;
valueW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
strcpyW(valueW, family->FamilyName);
if(len != len_fam) {
strcatW(valueW, spaceW);
strcatW(valueW, face->StyleName);
}
strcatW(valueW, TrueType);
file = wine_get_dos_file_name(face->file);
if(file)
len = strlenW(file) + 1;
else
{
if((path = strrchr(face->file, '/')) == NULL)
path = face->file;
else
path++;
len = MultiByteToWideChar(CP_ACP, 0, path, -1, NULL, 0);
file = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
MultiByteToWideChar(CP_ACP, 0, path, -1, file, len);
}
RegSetValueExW(winnt_key, valueW, 0, REG_SZ, (BYTE*)file, len * sizeof(WCHAR));
RegSetValueExW(win9x_key, valueW, 0, REG_SZ, (BYTE*)file, len * sizeof(WCHAR));
RegSetValueExW(external_key, valueW, 0, REG_SZ, (BYTE*)file, len * sizeof(WCHAR));
HeapFree(GetProcessHeap(), 0, file);
HeapFree(GetProcessHeap(), 0, valueW);
}
}
end:
if(external_key) RegCloseKey(external_key);
if(win9x_key) RegCloseKey(win9x_key);
if(winnt_key) RegCloseKey(winnt_key);
return;
}
static void delete_external_font_keys(void)
{
HKEY winnt_key = 0, win9x_key = 0, external_key = 0;
DWORD dlen, vlen, datalen, valuelen, i, type;
LPWSTR valueW;
LPVOID data;
if(RegCreateKeyExW(HKEY_LOCAL_MACHINE, winnt_font_reg_key,
0, NULL, 0, KEY_ALL_ACCESS, NULL, &winnt_key, NULL) != ERROR_SUCCESS) {
ERR("Can't create Windows font reg key\n");
goto end;
}
if(RegCreateKeyExW(HKEY_LOCAL_MACHINE, win9x_font_reg_key,
0, NULL, 0, KEY_ALL_ACCESS, NULL, &win9x_key, NULL) != ERROR_SUCCESS) {
ERR("Can't create Windows font reg key\n");
goto end;
}
if(RegCreateKeyW(HKEY_CURRENT_USER, external_fonts_reg_key, &external_key) != ERROR_SUCCESS) {
ERR("Can't create external font reg key\n");
goto end;
}
/* Delete all external fonts added last time */
RegQueryInfoKeyW(external_key, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
&valuelen, &datalen, NULL, NULL);
valuelen++; /* returned value doesn't include room for '\0' */
valueW = HeapAlloc(GetProcessHeap(), 0, valuelen * sizeof(WCHAR));
data = HeapAlloc(GetProcessHeap(), 0, datalen * sizeof(WCHAR));
dlen = datalen * sizeof(WCHAR);
vlen = valuelen;
i = 0;
while(RegEnumValueW(external_key, i++, valueW, &vlen, NULL, &type, data,
&dlen) == ERROR_SUCCESS) {
RegDeleteValueW(winnt_key, valueW);
RegDeleteValueW(win9x_key, valueW);
/* reset dlen and vlen */
dlen = datalen;
vlen = valuelen;
}
HeapFree(GetProcessHeap(), 0, data);
HeapFree(GetProcessHeap(), 0, valueW);
/* Delete the old external fonts key */
RegCloseKey(external_key);
RegDeleteKeyW(HKEY_CURRENT_USER, external_fonts_reg_key);
end:
if(win9x_key) RegCloseKey(win9x_key);
if(winnt_key) RegCloseKey(winnt_key);
}
/*************************************************************
* WineEngAddFontResourceEx
*
*/
INT WineEngAddFontResourceEx(LPCWSTR file, DWORD flags, PVOID pdv)
{
INT ret = 0;
GDI_CheckNotLock();
if (ft_handle) /* do it only if we have freetype up and running */
{
char *unixname;
if(flags)
FIXME("Ignoring flags %x\n", flags);
if((unixname = wine_get_unix_file_name(file)))
{
EnterCriticalSection( &freetype_cs );
ret = AddFontFileToList(unixname, NULL, NULL, ADDFONT_FORCE_BITMAP);
LeaveCriticalSection( &freetype_cs );
HeapFree(GetProcessHeap(), 0, unixname);
}
if (!ret && !strchrW(file, '\\')) {
/* Try in %WINDIR%/fonts, needed for Fotobuch Designer */
ret = load_font_from_winfonts_dir(file);
if (!ret) {
/* Try in datadir/fonts (or builddir/fonts),
* needed for Magic the Gathering Online
*/
ret = load_font_from_data_dir(file);
}
}
}
return ret;
}
/*************************************************************
* WineEngAddFontMemResourceEx
*
*/
HANDLE WineEngAddFontMemResourceEx(PVOID pbFont, DWORD cbFont, PVOID pdv, DWORD *pcFonts)
{
GDI_CheckNotLock();
if (ft_handle) /* do it only if we have freetype up and running */
{
PVOID pFontCopy = HeapAlloc(GetProcessHeap(), 0, cbFont);
TRACE("Copying %d bytes of data from %p to %p\n", cbFont, pbFont, pFontCopy);
memcpy(pFontCopy, pbFont, cbFont);
EnterCriticalSection( &freetype_cs );
*pcFonts = AddFontToList(NULL, pFontCopy, cbFont, NULL, NULL, ADDFONT_FORCE_BITMAP);
LeaveCriticalSection( &freetype_cs );
if (*pcFonts == 0)
{
TRACE("AddFontToList failed\n");
HeapFree(GetProcessHeap(), 0, pFontCopy);
return NULL;
}
/* FIXME: is the handle only for use in RemoveFontMemResourceEx or should it be a true handle?
* For now return something unique but quite random
*/
TRACE("Returning handle %lx\n", ((INT_PTR)pFontCopy)^0x87654321);
return (HANDLE)(((INT_PTR)pFontCopy)^0x87654321);
}
*pcFonts = 0;
return 0;
}
/*************************************************************
* WineEngRemoveFontResourceEx
*
*/
BOOL WineEngRemoveFontResourceEx(LPCWSTR file, DWORD flags, PVOID pdv)
{
GDI_CheckNotLock();
FIXME(":stub\n");
return TRUE;
}
static const struct nls_update_font_list
{
UINT ansi_cp, oem_cp;
const char *oem, *fixed, *system;
const char *courier, *serif, *small, *sserif;
/* these are for font substitutes */
const char *shelldlg, *tmsrmn;
const char *fixed_0, *system_0, *courier_0, *serif_0, *small_0, *sserif_0,
*helv_0, *tmsrmn_0;
const struct subst
{
const char *from, *to;
} arial_0, courier_new_0, times_new_roman_0;
} nls_update_font_list[] =
{
/* Latin 1 (United States) */
{ 1252, 437, "vgaoem.fon", "vgafix.fon", "vgasys.fon",
"coure.fon", "serife.fon", "smalle.fon", "sserife.fon",
"Tahoma","Times New Roman",
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
{ 0 }, { 0 }, { 0 }
},
/* Latin 1 (Multilingual) */
{ 1252, 850, "vga850.fon", "vgafix.fon", "vgasys.fon",
"coure.fon", "serife.fon", "smalle.fon", "sserife.fon",
"Tahoma","Times New Roman", /* FIXME unverified */
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
{ 0 }, { 0 }, { 0 }
},
/* Eastern Europe */
{ 1250, 852, "vga852.fon", "vgafixe.fon", "vgasyse.fon",
"couree.fon", "serifee.fon", "smallee.fon", "sserifee.fon",
"Tahoma","Times New Roman", /* FIXME unverified */
"Fixedsys,238", "System,238",
"Courier New,238", "MS Serif,238", "Small Fonts,238",
"MS Sans Serif,238", "MS Sans Serif,238", "MS Serif,238",
{ "Arial CE,0", "Arial,238" },
{ "Courier New CE,0", "Courier New,238" },
{ "Times New Roman CE,0", "Times New Roman,238" }
},
/* Cyrillic */
{ 1251, 866, "vga866.fon", "vgafixr.fon", "vgasysr.fon",
"courer.fon", "serifer.fon", "smaller.fon", "sserifer.fon",
"Tahoma","Times New Roman", /* FIXME unverified */
"Fixedsys,204", "System,204",
"Courier New,204", "MS Serif,204", "Small Fonts,204",
"MS Sans Serif,204", "MS Sans Serif,204", "MS Serif,204",
{ "Arial Cyr,0", "Arial,204" },
{ "Courier New Cyr,0", "Courier New,204" },
{ "Times New Roman Cyr,0", "Times New Roman,204" }
},
/* Greek */
{ 1253, 737, "vga869.fon", "vgafixg.fon", "vgasysg.fon",
"coureg.fon", "serifeg.fon", "smalleg.fon", "sserifeg.fon",
"Tahoma","Times New Roman", /* FIXME unverified */
"Fixedsys,161", "System,161",
"Courier New,161", "MS Serif,161", "Small Fonts,161",
"MS Sans Serif,161", "MS Sans Serif,161", "MS Serif,161",
{ "Arial Greek,0", "Arial,161" },
{ "Courier New Greek,0", "Courier New,161" },
{ "Times New Roman Greek,0", "Times New Roman,161" }
},
/* Turkish */
{ 1254, 857, "vga857.fon", "vgafixt.fon", "vgasyst.fon",
"couret.fon", "serifet.fon", "smallet.fon", "sserifet.fon",
"Tahoma","Times New Roman", /* FIXME unverified */
"Fixedsys,162", "System,162",
"Courier New,162", "MS Serif,162", "Small Fonts,162",
"MS Sans Serif,162", "MS Sans Serif,162", "MS Serif,162",
{ "Arial Tur,0", "Arial,162" },
{ "Courier New Tur,0", "Courier New,162" },
{ "Times New Roman Tur,0", "Times New Roman,162" }
},
/* Hebrew */
{ 1255, 862, "vgaoem.fon", "vgaf1255.fon", "vgas1255.fon",
"coue1255.fon", "sere1255.fon", "smae1255.fon", "ssee1255.fon",
"Tahoma","Times New Roman", /* FIXME unverified */
"Fixedsys,177", "System,177",
"Courier New,177", "MS Serif,177", "Small Fonts,177",
"MS Sans Serif,177", "MS Sans Serif,177", "MS Serif,177",
{ 0 }, { 0 }, { 0 }
},
/* Arabic */
{ 1256, 720, "vgaoem.fon", "vgaf1256.fon", "vgas1256.fon",
"coue1256.fon", "sere1256.fon", "smae1256.fon", "ssee1256.fon",
"Tahoma","Times New Roman", /* FIXME unverified */
"Fixedsys,178", "System,178",
"Courier New,178", "MS Serif,178", "Small Fonts,178",
"MS Sans Serif,178", "MS Sans Serif,178", "MS Serif,178",
{ 0 }, { 0 }, { 0 }
},
/* Baltic */
{ 1257, 775, "vga775.fon", "vgaf1257.fon", "vgas1257.fon",
"coue1257.fon", "sere1257.fon", "smae1257.fon", "ssee1257.fon",
"Tahoma","Times New Roman", /* FIXME unverified */
"Fixedsys,186", "System,186",
"Courier New,186", "MS Serif,186", "Small Fonts,186",
"MS Sans Serif,186", "MS Sans Serif,186", "MS Serif,186",
{ "Arial Baltic,0", "Arial,186" },
{ "Courier New Baltic,0", "Courier New,186" },
{ "Times New Roman Baltic,0", "Times New Roman,186" }
},
/* Vietnamese */
{ 1258, 1258, "vga850.fon", "vgafix.fon", "vgasys.fon",
"coure.fon", "serife.fon", "smalle.fon", "sserife.fon",
"Tahoma","Times New Roman", /* FIXME unverified */
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
{ 0 }, { 0 }, { 0 }
},
/* Thai */
{ 874, 874, "vga850.fon", "vgaf874.fon", "vgas874.fon",
"coure.fon", "serife.fon", "smalle.fon", "ssee874.fon",
"Tahoma","Times New Roman", /* FIXME unverified */
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
{ 0 }, { 0 }, { 0 }
},
/* Japanese */
{ 932, 932, "vga932.fon", "jvgafix.fon", "jvgasys.fon",
"coure.fon", "serife.fon", "jsmalle.fon", "sserife.fon",
"MS UI Gothic","MS Serif",
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
{ 0 }, { 0 }, { 0 }
},
/* Chinese Simplified */
{ 936, 936, "vga936.fon", "svgafix.fon", "svgasys.fon",
"coure.fon", "serife.fon", "smalle.fon", "sserife.fon",
"SimSun", "NSimSun",
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
{ 0 }, { 0 }, { 0 }
},
/* Korean */
{ 949, 949, "vga949.fon", "hvgafix.fon", "hvgasys.fon",
"coure.fon", "serife.fon", "smalle.fon", "sserife.fon",
"Gulim", "Batang",
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
{ 0 }, { 0 }, { 0 }
},
/* Chinese Traditional */
{ 950, 950, "vga950.fon", "cvgafix.fon", "cvgasys.fon",
"coure.fon", "serife.fon", "smalle.fon", "sserife.fon",
"PMingLiU", "MingLiU",
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
{ 0 }, { 0 }, { 0 }
}
};
static const WCHAR *font_links_list[] =
{
Lucida_Sans_Unicode,
Microsoft_Sans_Serif,
Tahoma
};
static const struct font_links_defaults_list
{
/* Keyed off substitution for "MS Shell Dlg" */
const WCHAR *shelldlg;
/* Maximum of four substitutes, plus terminating NULL pointer */
const WCHAR *substitutes[5];
} font_links_defaults_list[] =
{
/* Non East-Asian */
{ Tahoma, /* FIXME unverified ordering */
{ MS_UI_Gothic, SimSun, Gulim, PMingLiU, NULL }
},
/* Below lists are courtesy of
* http://blogs.msdn.com/michkap/archive/2005/06/18/430507.aspx
*/
/* Japanese */
{ MS_UI_Gothic,
{ MS_UI_Gothic, PMingLiU, SimSun, Gulim, NULL }
},
/* Chinese Simplified */
{ SimSun,
{ SimSun, PMingLiU, MS_UI_Gothic, Batang, NULL }
},
/* Korean */
{ Gulim,
{ Gulim, PMingLiU, MS_UI_Gothic, SimSun, NULL }
},
/* Chinese Traditional */
{ PMingLiU,
{ PMingLiU, SimSun, MS_UI_Gothic, Batang, NULL }
}
};
static inline BOOL is_dbcs_ansi_cp(UINT ansi_cp)
{
return ( ansi_cp == 932 /* CP932 for Japanese */
|| ansi_cp == 936 /* CP936 for Chinese Simplified */
|| ansi_cp == 949 /* CP949 for Korean */
|| ansi_cp == 950 ); /* CP950 for Chinese Traditional */
}
static inline HKEY create_fonts_NT_registry_key(void)
{
HKEY hkey = 0;
RegCreateKeyExW(HKEY_LOCAL_MACHINE, winnt_font_reg_key, 0, NULL,
0, KEY_ALL_ACCESS, NULL, &hkey, NULL);
return hkey;
}
static inline HKEY create_fonts_9x_registry_key(void)
{
HKEY hkey = 0;
RegCreateKeyExW(HKEY_LOCAL_MACHINE, win9x_font_reg_key, 0, NULL,
0, KEY_ALL_ACCESS, NULL, &hkey, NULL);
return hkey;
}
static inline HKEY create_config_fonts_registry_key(void)
{
HKEY hkey = 0;
RegCreateKeyExW(HKEY_CURRENT_CONFIG, system_fonts_reg_key, 0, NULL,
0, KEY_ALL_ACCESS, NULL, &hkey, NULL);
return hkey;
}
static void add_font_list(HKEY hkey, const struct nls_update_font_list *fl)
{
RegSetValueExA(hkey, "Courier", 0, REG_SZ, (const BYTE *)fl->courier, strlen(fl->courier)+1);
RegSetValueExA(hkey, "MS Serif", 0, REG_SZ, (const BYTE *)fl->serif, strlen(fl->serif)+1);
RegSetValueExA(hkey, "MS Sans Serif", 0, REG_SZ, (const BYTE *)fl->sserif, strlen(fl->sserif)+1);
RegSetValueExA(hkey, "Small Fonts", 0, REG_SZ, (const BYTE *)fl->small, strlen(fl->small)+1);
}
static void set_value_key(HKEY hkey, const char *name, const char *value)
{
if (value)
RegSetValueExA(hkey, name, 0, REG_SZ, (const BYTE *)value, strlen(value) + 1);
else if (name)
RegDeleteValueA(hkey, name);
}
static void update_font_info(void)
{
char buf[40], cpbuf[40];
DWORD len, type;
HKEY hkey = 0;
UINT i, ansi_cp = 0, oem_cp = 0;
BOOL done = FALSE;
if (RegCreateKeyExA(HKEY_CURRENT_USER, "Software\\Wine\\Fonts", 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hkey, NULL) != ERROR_SUCCESS)
return;
GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_IDEFAULTANSICODEPAGE|LOCALE_RETURN_NUMBER|LOCALE_NOUSEROVERRIDE,
(WCHAR *)&ansi_cp, sizeof(ansi_cp)/sizeof(WCHAR));
GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_IDEFAULTCODEPAGE|LOCALE_RETURN_NUMBER|LOCALE_NOUSEROVERRIDE,
(WCHAR *)&oem_cp, sizeof(oem_cp)/sizeof(WCHAR));
sprintf( cpbuf, "%u,%u", ansi_cp, oem_cp );
/* Setup Default_Fallback usage for DBCS ANSI codepages */
if (is_dbcs_ansi_cp(ansi_cp))
use_default_fallback = TRUE;
len = sizeof(buf);
if (RegQueryValueExA(hkey, "Codepages", 0, &type, (BYTE *)buf, &len) == ERROR_SUCCESS && type == REG_SZ)
{
if (!strcmp( buf, cpbuf )) /* already set correctly */
{
RegCloseKey(hkey);
return;
}
TRACE("updating registry, codepages changed %s -> %u,%u\n", buf, ansi_cp, oem_cp);
}
else TRACE("updating registry, codepages changed none -> %u,%u\n", ansi_cp, oem_cp);
RegSetValueExA(hkey, "Codepages", 0, REG_SZ, (const BYTE *)cpbuf, strlen(cpbuf)+1);
RegCloseKey(hkey);
for (i = 0; i < sizeof(nls_update_font_list)/sizeof(nls_update_font_list[0]); i++)
{
HKEY hkey;
if (nls_update_font_list[i].ansi_cp == ansi_cp &&
nls_update_font_list[i].oem_cp == oem_cp)
{
hkey = create_config_fonts_registry_key();
RegSetValueExA(hkey, "OEMFONT.FON", 0, REG_SZ, (const BYTE *)nls_update_font_list[i].oem, strlen(nls_update_font_list[i].oem)+1);
RegSetValueExA(hkey, "FIXEDFON.FON", 0, REG_SZ, (const BYTE *)nls_update_font_list[i].fixed, strlen(nls_update_font_list[i].fixed)+1);
RegSetValueExA(hkey, "FONTS.FON", 0, REG_SZ, (const BYTE *)nls_update_font_list[i].system, strlen(nls_update_font_list[i].system)+1);
RegCloseKey(hkey);
hkey = create_fonts_NT_registry_key();
add_font_list(hkey, &nls_update_font_list[i]);
RegCloseKey(hkey);
hkey = create_fonts_9x_registry_key();
add_font_list(hkey, &nls_update_font_list[i]);
RegCloseKey(hkey);
if (!RegCreateKeyA( HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes", &hkey ))
{
RegSetValueExA(hkey, "MS Shell Dlg", 0, REG_SZ, (const BYTE *)nls_update_font_list[i].shelldlg,
strlen(nls_update_font_list[i].shelldlg)+1);
RegSetValueExA(hkey, "Tms Rmn", 0, REG_SZ, (const BYTE *)nls_update_font_list[i].tmsrmn,
strlen(nls_update_font_list[i].tmsrmn)+1);
set_value_key(hkey, "Fixedsys,0", nls_update_font_list[i].fixed_0);
set_value_key(hkey, "System,0", nls_update_font_list[i].system_0);
set_value_key(hkey, "Courier,0", nls_update_font_list[i].courier_0);
set_value_key(hkey, "MS Serif,0", nls_update_font_list[i].serif_0);
set_value_key(hkey, "Small Fonts,0", nls_update_font_list[i].small_0);
set_value_key(hkey, "MS Sans Serif,0", nls_update_font_list[i].sserif_0);
set_value_key(hkey, "Helv,0", nls_update_font_list[i].helv_0);
set_value_key(hkey, "Tms Rmn,0", nls_update_font_list[i].tmsrmn_0);
set_value_key(hkey, nls_update_font_list[i].arial_0.from, nls_update_font_list[i].arial_0.to);
set_value_key(hkey, nls_update_font_list[i].courier_new_0.from, nls_update_font_list[i].courier_new_0.to);
set_value_key(hkey, nls_update_font_list[i].times_new_roman_0.from, nls_update_font_list[i].times_new_roman_0.to);
RegCloseKey(hkey);
}
done = TRUE;
}
else
{
/* Delete the FontSubstitutes from other locales */
if (!RegCreateKeyA( HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes", &hkey ))
{
set_value_key(hkey, nls_update_font_list[i].arial_0.from, NULL);
set_value_key(hkey, nls_update_font_list[i].courier_new_0.from, NULL);
set_value_key(hkey, nls_update_font_list[i].times_new_roman_0.from, NULL);
RegCloseKey(hkey);
}
}
}
if (!done)
FIXME("there is no font defaults for codepages %u,%u\n", ansi_cp, oem_cp);
/* Clear out system links */
RegDeleteKeyW(HKEY_LOCAL_MACHINE, system_link);
}
static void populate_system_links(HKEY hkey, const WCHAR *name, const WCHAR *const *values)
{
const WCHAR *value;
int i;
FontSubst *psub;
Family *family;
Face *face;
const char *file;
WCHAR *fileW;
int fileLen;
WCHAR buff[MAX_PATH];
WCHAR *data;
int entryLen;
static const WCHAR comma[] = {',',0};
RegDeleteValueW(hkey, name);
if (values)
{
data = buff;
data[0] = '\0';
for (i = 0; values[i] != NULL; i++)
{
value = values[i];
if (!strcmpiW(name,value))
continue;
psub = get_font_subst(&font_subst_list, value, -1);
if(psub)
value = psub->to.name;
family = find_family_from_name(value);
if (!family)
continue;
file = NULL;
/* Use first extant filename for this Family */
LIST_FOR_EACH_ENTRY(face, &family->faces, Face, entry)
{
if (!face->file)
continue;
file = strrchr(face->file, '/');
if (!file)
file = face->file;
else
file++;
break;
}
if (!file)
continue;
fileLen = MultiByteToWideChar(CP_UNIXCP, 0, file, -1, NULL, 0);
fileW = HeapAlloc(GetProcessHeap(), 0, fileLen * sizeof(WCHAR));
MultiByteToWideChar(CP_UNIXCP, 0, file, -1, fileW, fileLen);
entryLen = strlenW(fileW) + 1 + strlenW(value) + 1;
if (sizeof(buff)-(data-buff) < entryLen + 1)
{
WARN("creating SystemLink for %s, ran out of buffer space\n", debugstr_w(name));
HeapFree(GetProcessHeap(), 0, fileW);
break;
}
strcpyW(data, fileW);
strcatW(data, comma);
strcatW(data, value);
data += entryLen;
TRACE("added SystemLink for %s to %s in %s\n", debugstr_w(name), debugstr_w(value),debugstr_w(fileW));
HeapFree(GetProcessHeap(), 0, fileW);
}
if (data != buff)
{
*data='\0';
data++;
RegSetValueExW(hkey, name, 0, REG_MULTI_SZ, (BYTE*)buff, (data-buff) * sizeof(WCHAR));
} else
TRACE("no SystemLink fonts found for %s\n", debugstr_w(name));
} else
TRACE("removed SystemLink for %s\n", debugstr_w(name));
}
static void update_system_links(void)
{
HKEY hkey = 0;
UINT i, j;
BOOL done = FALSE;
DWORD disposition;
FontSubst *psub;
static const WCHAR MS_Shell_Dlg[] = {'M','S',' ','S','h','e','l','l',' ','D','l','g',0};
if (!RegCreateKeyExW(HKEY_LOCAL_MACHINE, system_link, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hkey, &disposition))
{
if (disposition == REG_OPENED_EXISTING_KEY)
{
TRACE("SystemLink key already exists, doing nothing\n");
RegCloseKey(hkey);
return;
}
psub = get_font_subst(&font_subst_list, MS_Shell_Dlg, -1);
if (!psub) {
WARN("could not find FontSubstitute for MS Shell Dlg\n");
RegCloseKey(hkey);
return;
}
for (i = 0; i < sizeof(font_links_defaults_list)/sizeof(font_links_defaults_list[0]); i++)
{
if (!strcmpiW(font_links_defaults_list[i].shelldlg, psub->to.name))
{
for (j = 0; j < sizeof(font_links_list)/sizeof(font_links_list[0]); j++)
populate_system_links(hkey, font_links_list[j], font_links_defaults_list[i].substitutes);
if (!strcmpiW(psub->to.name, font_links_defaults_list[i].substitutes[0]))
populate_system_links(hkey, psub->to.name, font_links_defaults_list[i].substitutes);
done = TRUE;
}
else if (strcmpiW(psub->to.name, font_links_defaults_list[i].substitutes[0]))
{
populate_system_links(hkey, font_links_defaults_list[i].substitutes[0], NULL);
}
}
RegCloseKey(hkey);
if (!done)
WARN("there is no SystemLink default list for MS Shell Dlg %s\n", debugstr_w(psub->to.name));
} else
WARN("failed to create SystemLink key\n");
}
static BOOL init_freetype(void)
{
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 FALSE;
}
#define LOAD_FUNCPTR(f) if((p##f = wine_dlsym(ft_handle, #f, NULL, 0)) == NULL){WARN("Can't find symbol %s\n", #f); goto sym_not_found;}
LOAD_FUNCPTR(FT_Vector_Unit)
LOAD_FUNCPTR(FT_Done_Face)
LOAD_FUNCPTR(FT_Get_Char_Index)
LOAD_FUNCPTR(FT_Get_Module)
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_Matrix_Multiply)
#ifndef FT_MULFIX_INLINED
LOAD_FUNCPTR(FT_MulFix)
#endif
LOAD_FUNCPTR(FT_New_Face)
LOAD_FUNCPTR(FT_New_Memory_Face)
LOAD_FUNCPTR(FT_Outline_Get_Bitmap)
LOAD_FUNCPTR(FT_Outline_Transform)
LOAD_FUNCPTR(FT_Outline_Translate)
LOAD_FUNCPTR(FT_Select_Charmap)
LOAD_FUNCPTR(FT_Set_Charmap)
LOAD_FUNCPTR(FT_Set_Pixel_Sizes)
LOAD_FUNCPTR(FT_Vector_Transform)
LOAD_FUNCPTR(FT_Render_Glyph)
#undef LOAD_FUNCPTR
/* Don't warn if these ones are missing */
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_FTLCDFIL_H
pFT_Library_SetLcdFilter = wine_dlsym(ft_handle, "FT_Library_SetLcdFilter", NULL, 0);
#endif
#ifdef HAVE_FREETYPE_FTWINFNT_H
pFT_Get_WinFNT_Header = wine_dlsym(ft_handle, "FT_Get_WinFNT_Header", NULL, 0);
#endif
if(!wine_dlsym(ft_handle, "FT_Get_Postscript_Name", NULL, 0) &&
!wine_dlsym(ft_handle, "FT_Sqrt64", NULL, 0)) {
/* try to avoid 2.0.4: >= 2.0.5 has FT_Get_Postscript_Name and
<= 2.0.3 has FT_Sqrt64 */
goto sym_not_found;
}
if(pFT_Init_FreeType(&library) != 0) {
ERR("Can't init FreeType library\n");
wine_dlclose(ft_handle, NULL, 0);
ft_handle = NULL;
return FALSE;
}
FT_Version.major = FT_Version.minor = FT_Version.patch = -1;
if (pFT_Library_Version)
pFT_Library_Version(library,&FT_Version.major,&FT_Version.minor,&FT_Version.patch);
if (FT_Version.major<=0)
{
FT_Version.major=2;
FT_Version.minor=0;
FT_Version.patch=5;
}
TRACE("FreeType version is %d.%d.%d\n",FT_Version.major,FT_Version.minor,FT_Version.patch);
FT_SimpleVersion = ((FT_Version.major << 16) & 0xff0000) |
((FT_Version.minor << 8) & 0x00ff00) |
((FT_Version.patch ) & 0x0000ff);
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 FALSE;
}
/*************************************************************
* WineEngInit
*
* Initialize FreeType library and create a list of available faces
*/
BOOL WineEngInit(void)
{
static const WCHAR dot_fonW[] = {'.','f','o','n','\0'};
static const WCHAR pathW[] = {'P','a','t','h',0};
HKEY hkey;
DWORD valuelen, datalen, i = 0, type, dlen, vlen;
WCHAR windowsdir[MAX_PATH];
char *unixname;
HANDLE font_mutex;
const char *data_dir;
TRACE("\n");
/* update locale dependent font info in registry */
update_font_info();
if(!init_freetype()) return FALSE;
if((font_mutex = CreateMutexW(NULL, FALSE, font_mutex_nameW)) == NULL) {
ERR("Failed to create font mutex\n");
return FALSE;
}
WaitForSingleObject(font_mutex, INFINITE);
delete_external_font_keys();
/* load the system bitmap fonts */
load_system_fonts();
/* load in the fonts from %WINDOWSDIR%\\Fonts first of all */
GetWindowsDirectoryW(windowsdir, sizeof(windowsdir) / sizeof(WCHAR));
strcatW(windowsdir, fontsW);
if((unixname = wine_get_unix_file_name(windowsdir)))
{
ReadFontDir(unixname, FALSE);
HeapFree(GetProcessHeap(), 0, unixname);
}
/* load the system truetype fonts */
data_dir = wine_get_data_dir();
if (!data_dir) data_dir = wine_get_build_dir();
if (data_dir && (unixname = HeapAlloc(GetProcessHeap(), 0, strlen(data_dir) + sizeof("/fonts/")))) {
strcpy(unixname, data_dir);
strcat(unixname, "/fonts/");
ReadFontDir(unixname, TRUE);
HeapFree(GetProcessHeap(), 0, unixname);
}
/* now look under HKLM\Software\Microsoft\Windows[ NT]\CurrentVersion\Fonts
for any fonts not installed in %WINDOWSDIR%\Fonts. They will have their
full path as the entry. Also look for any .fon fonts, since ReadFontDir
will skip these. */
if(RegOpenKeyW(HKEY_LOCAL_MACHINE,
is_win9x() ? win9x_font_reg_key : winnt_font_reg_key,
&hkey) == ERROR_SUCCESS) {
LPWSTR data, valueW;
RegQueryInfoKeyW(hkey, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
&valuelen, &datalen, NULL, NULL);
valuelen++; /* returned value doesn't include room for '\0' */
valueW = HeapAlloc(GetProcessHeap(), 0, valuelen * sizeof(WCHAR));
data = HeapAlloc(GetProcessHeap(), 0, datalen * sizeof(WCHAR));
if (valueW && data)
{
dlen = datalen * sizeof(WCHAR);
vlen = valuelen;
while(RegEnumValueW(hkey, i++, valueW, &vlen, NULL, &type, (LPBYTE)data,
&dlen) == ERROR_SUCCESS) {
if(data[0] && (data[1] == ':'))
{
if((unixname = wine_get_unix_file_name(data)))
{
AddFontFileToList(unixname, NULL, NULL, ADDFONT_FORCE_BITMAP);
HeapFree(GetProcessHeap(), 0, unixname);
}
}
else if(dlen / 2 >= 6 && !strcmpiW(data + dlen / 2 - 5, dot_fonW))
{
WCHAR pathW[MAX_PATH];
static const WCHAR fmtW[] = {'%','s','\\','%','s','\0'};
BOOL added = FALSE;
sprintfW(pathW, fmtW, windowsdir, data);
if((unixname = wine_get_unix_file_name(pathW)))
{
added = AddFontFileToList(unixname, NULL, NULL, ADDFONT_FORCE_BITMAP);
HeapFree(GetProcessHeap(), 0, unixname);
}
if (!added)
load_font_from_data_dir(data);
}
/* reset dlen and vlen */
dlen = datalen;
vlen = valuelen;
}
}
HeapFree(GetProcessHeap(), 0, data);
HeapFree(GetProcessHeap(), 0, valueW);
RegCloseKey(hkey);
}
load_fontconfig_fonts();
/* then look in any directories that we've specified in the config file */
/* @@ Wine registry key: HKCU\Software\Wine\Fonts */
if(RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Fonts", &hkey) == ERROR_SUCCESS)
{
DWORD len;
LPWSTR valueW;
LPSTR valueA, ptr;
if (RegQueryValueExW( hkey, pathW, NULL, NULL, NULL, &len ) == ERROR_SUCCESS)
{
len += sizeof(WCHAR);
valueW = HeapAlloc( GetProcessHeap(), 0, len );
if (RegQueryValueExW( hkey, pathW, NULL, NULL, (LPBYTE)valueW, &len ) == ERROR_SUCCESS)
{
len = WideCharToMultiByte( CP_UNIXCP, 0, valueW, -1, NULL, 0, NULL, NULL );
valueA = HeapAlloc( GetProcessHeap(), 0, len );
WideCharToMultiByte( CP_UNIXCP, 0, valueW, -1, valueA, len, NULL, NULL );
TRACE( "got font path %s\n", debugstr_a(valueA) );
ptr = valueA;
while (ptr)
{
LPSTR next = strchr( ptr, ':' );
if (next) *next++ = 0;
ReadFontDir( ptr, TRUE );
ptr = next;
}
HeapFree( GetProcessHeap(), 0, valueA );
}
HeapFree( GetProcessHeap(), 0, valueW );
}
RegCloseKey(hkey);
}
DumpFontList();
LoadSubstList();
DumpSubstList();
LoadReplaceList();
update_reg_entries();
update_system_links();
init_system_links();
ReleaseMutex(font_mutex);
return TRUE;
}
static LONG calc_ppem_for_height(FT_Face ft_face, LONG height)
{
TT_OS2 *pOS2;
TT_HoriHeader *pHori;
LONG ppem;
pOS2 = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_os2);
pHori = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_hhea);
if(height == 0) height = 16;
/* Calc. height of EM square:
*
* For +ve lfHeight we have
* lfHeight = (winAscent + winDescent) * ppem / units_per_em
* Re-arranging gives:
* ppem = units_per_em * lfheight / (winAscent + winDescent)
*
* For -ve lfHeight we have
* |lfHeight| = ppem
* [i.e. |lfHeight| = (winAscent + winDescent - il) * ppem / units_per_em
* with il = winAscent + winDescent - units_per_em]
*
*/
if(height > 0) {
if(pOS2->usWinAscent + pOS2->usWinDescent == 0)
ppem = MulDiv(ft_face->units_per_EM, height,
pHori->Ascender - pHori->Descender);
else
ppem = MulDiv(ft_face->units_per_EM, height,
pOS2->usWinAscent + pOS2->usWinDescent);
}
else
ppem = -height;
return ppem;
}
static struct font_mapping *map_font_file( const char *name )
{
struct font_mapping *mapping;
struct stat st;
int fd;
if ((fd = open( name, O_RDONLY )) == -1) return NULL;
if (fstat( fd, &st ) == -1) goto error;
LIST_FOR_EACH_ENTRY( mapping, &mappings_list, struct font_mapping, entry )
{
if (mapping->dev == st.st_dev && mapping->ino == st.st_ino)
{
mapping->refcount++;
close( fd );
return mapping;
}
}
if (!(mapping = HeapAlloc( GetProcessHeap(), 0, sizeof(*mapping) )))
goto error;
mapping->data = mmap( NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0 );
close( fd );
if (mapping->data == MAP_FAILED)
{
HeapFree( GetProcessHeap(), 0, mapping );
return NULL;
}
mapping->refcount = 1;
mapping->dev = st.st_dev;
mapping->ino = st.st_ino;
mapping->size = st.st_size;
list_add_tail( &mappings_list, &mapping->entry );
return mapping;
error:
close( fd );
return NULL;
}
static void unmap_font_file( struct font_mapping *mapping )
{
if (!--mapping->refcount)
{
list_remove( &mapping->entry );
munmap( mapping->data, mapping->size );
HeapFree( GetProcessHeap(), 0, mapping );
}
}
static LONG load_VDMX(GdiFont*, LONG);
static FT_Face OpenFontFace(GdiFont *font, Face *face, LONG width, LONG height)
{
FT_Error err;
FT_Face ft_face;
void *data_ptr;
DWORD data_size;
TRACE("%s/%p, %ld, %d x %d\n", debugstr_a(face->file), face->font_data_ptr, face->face_index, width, height);
if (face->file)
{
if (!(font->mapping = map_font_file( face->file )))
{
WARN("failed to map %s\n", debugstr_a(face->file));
return 0;
}
data_ptr = font->mapping->data;
data_size = font->mapping->size;
}
else
{
data_ptr = face->font_data_ptr;
data_size = face->font_data_size;
}
err = pFT_New_Memory_Face(library, data_ptr, data_size, face->face_index, &ft_face);
if(err) {
ERR("FT_New_Face rets %d\n", err);
return 0;
}
/* set it here, as load_VDMX needs it */
font->ft_face = ft_face;
if(FT_IS_SCALABLE(ft_face)) {
/* load the VDMX table if we have one */
font->ppem = load_VDMX(font, height);
if(font->ppem == 0)
font->ppem = calc_ppem_for_height(ft_face, height);
TRACE("height %d => ppem %d\n", height, font->ppem);
if((err = pFT_Set_Pixel_Sizes(ft_face, 0, font->ppem)) != 0)
WARN("FT_Set_Pixel_Sizes %d, %d rets %x\n", 0, font->ppem, err);
} else {
font->ppem = height;
if((err = pFT_Set_Pixel_Sizes(ft_face, width, height)) != 0)
WARN("FT_Set_Pixel_Sizes %d, %d rets %x\n", width, height, err);
}
return ft_face;
}
static int get_nearest_charset(Face *face, int *cp)
{
/* Only get here if lfCharSet == DEFAULT_CHARSET or we couldn't find
a single face with the requested charset. The idea is to check if
the selected font supports the current ANSI codepage, if it does
return the corresponding charset, else return the first charset */
CHARSETINFO csi;
int acp = GetACP(), i;
DWORD fs0;
*cp = acp;
if(TranslateCharsetInfo((DWORD*)(INT_PTR)acp, &csi, TCI_SRCCODEPAGE))
if(csi.fs.fsCsb[0] & (face->fs.fsCsb[0] | face->fs_links.fsCsb[0]))
return csi.ciCharset;
for(i = 0; i < 32; i++) {
fs0 = 1L << i;
if(face->fs.fsCsb[0] & fs0) {
if(TranslateCharsetInfo(&fs0, &csi, TCI_SRCFONTSIG)) {
*cp = csi.ciACP;
return csi.ciCharset;
}
else
FIXME("TCI failing on %x\n", fs0);
}
}
FIXME("returning DEFAULT_CHARSET face->fs.fsCsb[0] = %08x file = %s\n",
face->fs.fsCsb[0], face->file);
*cp = acp;
return DEFAULT_CHARSET;
}
static GdiFont *alloc_font(void)
{
GdiFont *ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*ret));
ret->gmsize = 1;
ret->gm = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(GM*));
ret->gm[0] = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(GM) * GM_BLOCK_SIZE);
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;
}
static void free_font(GdiFont *font)
{
struct list *cursor, *cursor2;
DWORD i;
LIST_FOR_EACH_SAFE(cursor, cursor2, &font->child_fonts)
{
CHILD_FONT *child = LIST_ENTRY(cursor, CHILD_FONT, entry);
struct list *first_hfont;
HFONTLIST *hfontlist;
list_remove(cursor);
if(child->font)
{
first_hfont = list_head(&child->font->hfontlist);
hfontlist = LIST_ENTRY(first_hfont, HFONTLIST, entry);
DeleteObject(hfontlist->hfont);
HeapFree(GetProcessHeap(), 0, hfontlist);
free_font(child->font);
}
HeapFree(GetProcessHeap(), 0, child);
}
if (font->ft_face) pFT_Done_Face(font->ft_face);
if (font->mapping) unmap_font_file( font->mapping );
HeapFree(GetProcessHeap(), 0, font->kern_pairs);
HeapFree(GetProcessHeap(), 0, font->potm);
HeapFree(GetProcessHeap(), 0, font->name);
for (i = 0; i < font->gmsize; i++)
HeapFree(GetProcessHeap(),0,font->gm[i]);
HeapFree(GetProcessHeap(), 0, font->gm);
HeapFree(GetProcessHeap(), 0, font->GSUB_Table);
HeapFree(GetProcessHeap(), 0, font);
}
/*************************************************************
* load_VDMX
*
* load the vdmx entry for the specified height
*/
#define MS_MAKE_TAG( _x1, _x2, _x3, _x4 ) \
( ( (FT_ULong)_x4 << 24 ) | \
( (FT_ULong)_x3 << 16 ) | \
( (FT_ULong)_x2 << 8 ) | \
(FT_ULong)_x1 )
#define MS_VDMX_TAG MS_MAKE_TAG('V', 'D', 'M', 'X')
typedef struct {
BYTE bCharSet;
BYTE xRatio;
BYTE yStartRatio;
BYTE yEndRatio;
} Ratios;
typedef struct {
WORD recs;
BYTE startsz;
BYTE endsz;
} VDMX_group;
static LONG load_VDMX(GdiFont *font, LONG height)
{
WORD hdr[3], tmp;
VDMX_group group;
BYTE devXRatio, devYRatio;
USHORT numRecs, numRatios;
DWORD result, offset = -1;
LONG ppem = 0;
int i;
result = WineEngGetFontData(font, MS_VDMX_TAG, 0, hdr, 6);
if(result == GDI_ERROR) /* no vdmx table present, use linear scaling */
return ppem;
/* FIXME: need the real device aspect ratio */
devXRatio = 1;
devYRatio = 1;
numRecs = GET_BE_WORD(hdr[1]);
numRatios = GET_BE_WORD(hdr[2]);
TRACE("numRecs = %d numRatios = %d\n", numRecs, numRatios);
for(i = 0; i < numRatios; i++) {
Ratios ratio;
offset = (3 * 2) + (i * sizeof(Ratios));
WineEngGetFontData(font, MS_VDMX_TAG, offset, &ratio, sizeof(Ratios));
offset = -1;
TRACE("Ratios[%d] %d %d : %d -> %d\n", i, ratio.bCharSet, ratio.xRatio, ratio.yStartRatio, ratio.yEndRatio);
if((ratio.xRatio == 0 &&
ratio.yStartRatio == 0 &&
ratio.yEndRatio == 0) ||
(devXRatio == ratio.xRatio &&
devYRatio >= ratio.yStartRatio &&
devYRatio <= ratio.yEndRatio))
{
offset = (3 * 2) + (numRatios * 4) + (i * 2);
WineEngGetFontData(font, MS_VDMX_TAG, offset, &tmp, 2);
offset = GET_BE_WORD(tmp);
break;
}
}
if(offset == -1) {
FIXME("No suitable ratio found\n");
return ppem;
}
if(WineEngGetFontData(font, MS_VDMX_TAG, offset, &group, 4) != GDI_ERROR) {
USHORT recs;
BYTE startsz, endsz;
WORD *vTable;
recs = GET_BE_WORD(group.recs);
startsz = group.startsz;
endsz = group.endsz;
TRACE("recs=%d startsz=%d endsz=%d\n", recs, startsz, endsz);
vTable = HeapAlloc(GetProcessHeap(), 0, recs * 6);
result = WineEngGetFontData(font, MS_VDMX_TAG, offset + 4, vTable, recs * 6);
if(result == GDI_ERROR) {
FIXME("Failed to retrieve vTable\n");
goto end;
}
if(height > 0) {
for(i = 0; i < recs; i++) {
SHORT yMax = GET_BE_WORD(vTable[(i * 3) + 1]);
SHORT yMin = GET_BE_WORD(vTable[(i * 3) + 2]);
ppem = GET_BE_WORD(vTable[i * 3]);
if(yMax + -yMin == height) {
font->yMax = yMax;
font->yMin = yMin;
TRACE("ppem %d found; height=%d yMax=%d yMin=%d\n", ppem, height, font->yMax, font->yMin);
break;
}
if(yMax + -yMin > height) {
if(--i < 0) {
ppem = 0;
goto end; /* failed */
}
font->yMax = GET_BE_WORD(vTable[(i * 3) + 1]);
font->yMin = GET_BE_WORD(vTable[(i * 3) + 2]);
ppem = GET_BE_WORD(vTable[i * 3]);
TRACE("ppem %d found; height=%d yMax=%d yMin=%d\n", ppem, height, font->yMax, font->yMin);
break;
}
}
if(!font->yMax) {
ppem = 0;
TRACE("ppem not found for height %d\n", height);
}
} else {
ppem = -height;
if(ppem < startsz || ppem > endsz)
goto end;
for(i = 0; i < recs; i++) {
USHORT yPelHeight;
yPelHeight = GET_BE_WORD(vTable[i * 3]);
if(yPelHeight > ppem)
break; /* failed */
if(yPelHeight == ppem) {
font->yMax = GET_BE_WORD(vTable[(i * 3) + 1]);
font->yMin = GET_BE_WORD(vTable[(i * 3) + 2]);
TRACE("ppem %d found; yMax=%d yMin=%d\n", ppem, font->yMax, font->yMin);
break;
}
}
}
end:
HeapFree(GetProcessHeap(), 0, vTable);
}
return ppem;
}
static BOOL fontcmp(const GdiFont *font, FONT_DESC *fd)
{
if(font->font_desc.hash != fd->hash) return TRUE;
if(memcmp(&font->font_desc.matrix, &fd->matrix, sizeof(fd->matrix))) return TRUE;
if(memcmp(&font->font_desc.lf, &fd->lf, offsetof(LOGFONTW, lfFaceName))) return TRUE;
if(!font->font_desc.can_use_bitmap != !fd->can_use_bitmap) return TRUE;
return strcmpiW(font->font_desc.lf.lfFaceName, fd->lf.lfFaceName);
}
static void calc_hash(FONT_DESC *pfd)
{
DWORD hash = 0, *ptr, two_chars;
WORD *pwc;
unsigned int i;
for(i = 0, ptr = (DWORD*)&pfd->matrix; i < sizeof(FMAT2)/sizeof(DWORD); i++, ptr++)
hash ^= *ptr;
for(i = 0, ptr = (DWORD*)&pfd->lf; i < 7; i++, ptr++)
hash ^= *ptr;
for(i = 0, ptr = (DWORD*)pfd->lf.lfFaceName; i < LF_FACESIZE/2; i++, ptr++) {
two_chars = *ptr;
pwc = (WCHAR *)&two_chars;
if(!*pwc) break;
*pwc = toupperW(*pwc);
pwc++;
*pwc = toupperW(*pwc);
hash ^= two_chars;
if(!*pwc) break;
}
hash ^= !pfd->can_use_bitmap;
pfd->hash = hash;
return;
}
static GdiFont *find_in_cache(HFONT hfont, const LOGFONTW *plf, const FMAT2 *pmat, BOOL can_use_bitmap)
{
GdiFont *ret;
FONT_DESC fd;
HFONTLIST *hflist;
struct list *font_elem_ptr, *hfontlist_elem_ptr;
fd.lf = *plf;
fd.matrix = *pmat;
fd.can_use_bitmap = can_use_bitmap;
calc_hash(&fd);
/* try the child list */
LIST_FOR_EACH(font_elem_ptr, &child_font_list) {
ret = LIST_ENTRY(font_elem_ptr, struct tagGdiFont, entry);
if(!fontcmp(ret, &fd)) {
if(!can_use_bitmap && !FT_IS_SCALABLE(ret->ft_face)) continue;
LIST_FOR_EACH(hfontlist_elem_ptr, &ret->hfontlist) {
hflist = LIST_ENTRY(hfontlist_elem_ptr, struct tagHFONTLIST, entry);
if(hflist->hfont == hfont)
return ret;
}
}
}
/* try the in-use list */
LIST_FOR_EACH(font_elem_ptr, &gdi_font_list) {
ret = LIST_ENTRY(font_elem_ptr, struct tagGdiFont, entry);
if(!fontcmp(ret, &fd)) {
if(!can_use_bitmap && !FT_IS_SCALABLE(ret->ft_face)) continue;
LIST_FOR_EACH(hfontlist_elem_ptr, &ret->hfontlist) {
hflist = LIST_ENTRY(hfontlist_elem_ptr, struct tagHFONTLIST, entry);
if(hflist->hfont == hfont)
return ret;
}
hflist = HeapAlloc(GetProcessHeap(), 0, sizeof(*hflist));
hflist->hfont = hfont;
list_add_head(&ret->hfontlist, &hflist->entry);
return ret;
}
}
/* then the unused list */
font_elem_ptr = list_head(&unused_gdi_font_list);
while(font_elem_ptr) {
ret = LIST_ENTRY(font_elem_ptr, struct tagGdiFont, entry);
font_elem_ptr = list_next(&unused_gdi_font_list, font_elem_ptr);
if(!fontcmp(ret, &fd)) {
if(!can_use_bitmap && !FT_IS_SCALABLE(ret->ft_face)) continue;
assert(list_empty(&ret->hfontlist));
TRACE("Found %p in unused list\n", ret);
list_remove(&ret->entry);
list_add_head(&gdi_font_list, &ret->entry);
hflist = HeapAlloc(GetProcessHeap(), 0, sizeof(*hflist));
hflist->hfont = hfont;
list_add_head(&ret->hfontlist, &hflist->entry);
return ret;
}
}
return NULL;
}
static void add_to_cache(GdiFont *font)
{
static DWORD cache_num = 1;
font->cache_num = cache_num++;
list_add_head(&gdi_font_list, &font->entry);
}
/*************************************************************
* create_child_font_list
*/
static BOOL create_child_font_list(GdiFont *font)
{
BOOL ret = FALSE;
SYSTEM_LINKS *font_link;
CHILD_FONT *font_link_entry, *new_child;
FontSubst *psub;
WCHAR* font_name;
psub = get_font_subst(&font_subst_list, font->name, -1);
font_name = psub ? psub->to.name : font->name;
LIST_FOR_EACH_ENTRY(font_link, &system_links, SYSTEM_LINKS, entry)
{
if(!strcmpiW(font_link->font_name, font_name))
{
TRACE("found entry in system list\n");
LIST_FOR_EACH_ENTRY(font_link_entry, &font_link->links, CHILD_FONT, entry)
{
new_child = HeapAlloc(GetProcessHeap(), 0, sizeof(*new_child));
new_child->face = font_link_entry->face;
new_child->font = NULL;
list_add_tail(&font->child_fonts, &new_child->entry);
TRACE("font %s %ld\n", debugstr_a(new_child->face->file), new_child->face->face_index);
}
ret = TRUE;
break;
}
}
/*
* if not SYMBOL or OEM then we also get all the fonts for Microsoft
* Sans Serif. This is how asian windows get default fallbacks for fonts
*/
if (use_default_fallback && font->charset != SYMBOL_CHARSET &&
font->charset != OEM_CHARSET &&
strcmpiW(font_name,szDefaultFallbackLink) != 0)
LIST_FOR_EACH_ENTRY(font_link, &system_links, SYSTEM_LINKS, entry)
{
if(!strcmpiW(font_link->font_name,szDefaultFallbackLink))
{
TRACE("found entry in default fallback list\n");
LIST_FOR_EACH_ENTRY(font_link_entry, &font_link->links, CHILD_FONT, entry)
{
new_child = HeapAlloc(GetProcessHeap(), 0, sizeof(*new_child));
new_child->face = font_link_entry->face;
new_child->font = NULL;
list_add_tail(&font->child_fonts, &new_child->entry);
TRACE("font %s %ld\n", debugstr_a(new_child->face->file), new_child->face->face_index);
}
ret = TRUE;
break;
}
}
return ret;
}
static BOOL select_charmap(FT_Face ft_face, FT_Encoding encoding)
{
FT_Error ft_err = FT_Err_Invalid_CharMap_Handle;
if (pFT_Set_Charmap)
{
FT_Int i;
FT_CharMap cmap0, cmap1, cmap2, cmap3, cmap_def;
cmap0 = cmap1 = cmap2 = cmap3 = cmap_def = NULL;
for (i = 0; i < ft_face->num_charmaps; i++)
{
if (ft_face->charmaps[i]->encoding == encoding)
{
TRACE("found cmap with platform_id %u, encoding_id %u\n",
ft_face->charmaps[i]->platform_id, ft_face->charmaps[i]->encoding_id);
switch (ft_face->charmaps[i]->platform_id)
{
default:
cmap_def = ft_face->charmaps[i];
break;
case 0: /* Apple Unicode */
cmap0 = ft_face->charmaps[i];
break;
case 1: /* Macintosh */
cmap1 = ft_face->charmaps[i];
break;
case 2: /* ISO */
cmap2 = ft_face->charmaps[i];
break;
case 3: /* Microsoft */
cmap3 = ft_face->charmaps[i];
break;
}
}
if (cmap3) /* prefer Microsoft cmap table */
ft_err = pFT_Set_Charmap(ft_face, cmap3);
else if (cmap1)
ft_err = pFT_Set_Charmap(ft_face, cmap1);
else if (cmap2)
ft_err = pFT_Set_Charmap(ft_face, cmap2);
else if (cmap0)
ft_err = pFT_Set_Charmap(ft_face, cmap0);
else if (cmap_def)
ft_err = pFT_Set_Charmap(ft_face, cmap_def);
}
return ft_err == FT_Err_Ok;
}
return pFT_Select_Charmap(ft_face, encoding) == FT_Err_Ok;
}
/*************************************************************
* WineEngCreateFontInstance
*
*/
GdiFont *WineEngCreateFontInstance(DC *dc, HFONT hfont)
{
GdiFont *ret;
Face *face, *best, *best_bitmap;
Family *family, *last_resort_family;
struct list *family_elem_ptr, *face_elem_ptr;
INT height, width = 0;
unsigned int score = 0, new_score;
signed int diff = 0, newdiff;
BOOL bd, it, can_use_bitmap;
LOGFONTW lf;
CHARSETINFO csi;
HFONTLIST *hflist;
FMAT2 dcmat;
FontSubst *psub = NULL;
if (!GetObjectW( hfont, sizeof(lf), &lf )) return NULL;
lf.lfWidth = abs(lf.lfWidth);
can_use_bitmap = GetDeviceCaps(dc->hSelf, TEXTCAPS) & TC_RA_ABLE;
TRACE("%s, h=%d, it=%d, weight=%d, PandF=%02x, charset=%d orient %d escapement %d\n",
debugstr_w(lf.lfFaceName), lf.lfHeight, lf.lfItalic,
lf.lfWeight, lf.lfPitchAndFamily, lf.lfCharSet, lf.lfOrientation,
lf.lfEscapement);
if(dc->GraphicsMode == GM_ADVANCED)
memcpy(&dcmat, &dc->xformWorld2Vport, sizeof(FMAT2));
else
{
/* Windows 3.1 compatibility mode GM_COMPATIBLE has only limited
font scaling abilities. */
dcmat.eM11 = dcmat.eM22 = dc->vport2WorldValid ? fabs(dc->xformWorld2Vport.eM22) : 1.0;
dcmat.eM21 = dcmat.eM12 = 0;
}
/* Try to avoid not necessary glyph transformations */
if (dcmat.eM21 == 0.0 && dcmat.eM12 == 0.0 && dcmat.eM11 == dcmat.eM22)
{
lf.lfHeight *= fabs(dcmat.eM11);
lf.lfWidth *= fabs(dcmat.eM11);
dcmat.eM11 = dcmat.eM22 = 1.0;
}
TRACE("DC transform %f %f %f %f\n", dcmat.eM11, dcmat.eM12,
dcmat.eM21, dcmat.eM22);
GDI_CheckNotLock();
EnterCriticalSection( &freetype_cs );
/* check the cache first */
if((ret = find_in_cache(hfont, &lf, &dcmat, can_use_bitmap)) != NULL) {
TRACE("returning cached gdiFont(%p) for hFont %p\n", ret, hfont);
LeaveCriticalSection( &freetype_cs );
return ret;
}
TRACE("not in cache\n");
if(list_empty(&font_list)) /* No fonts installed */
{
TRACE("No fonts installed\n");
LeaveCriticalSection( &freetype_cs );
return NULL;
}
if(!have_installed_roman_font)
{
TRACE("No roman font installed\n");
LeaveCriticalSection( &freetype_cs );
return NULL;
}
ret = alloc_font();
ret->font_desc.matrix = dcmat;
ret->font_desc.lf = lf;
ret->font_desc.can_use_bitmap = can_use_bitmap;
calc_hash(&ret->font_desc);
hflist = HeapAlloc(GetProcessHeap(), 0, sizeof(*hflist));
hflist->hfont = hfont;
list_add_head(&ret->hfontlist, &hflist->entry);
/* If lfFaceName is "Symbol" then Windows fixes up lfCharSet to
SYMBOL_CHARSET so that Symbol gets picked irrespective of the
original value lfCharSet. Note this is a special case for
Symbol and doesn't happen at least for "Wingdings*" */
if(!strcmpiW(lf.lfFaceName, SymbolW))
lf.lfCharSet = SYMBOL_CHARSET;
if(!TranslateCharsetInfo((DWORD*)(INT_PTR)lf.lfCharSet, &csi, TCI_SRCCHARSET)) {
switch(lf.lfCharSet) {
case DEFAULT_CHARSET:
csi.fs.fsCsb[0] = 0;
break;
default:
FIXME("Untranslated charset %d\n", lf.lfCharSet);
csi.fs.fsCsb[0] = 0;
break;
}
}
family = NULL;
if(lf.lfFaceName[0] != '\0') {
SYSTEM_LINKS *font_link;
CHILD_FONT *font_link_entry;
LPWSTR FaceName = lf.lfFaceName;
/*
* Check for a leading '@' this signals that the font is being
* requested in tategaki mode (vertical writing substitution) but
* does not affect the fontface that is to be selected.
*/
if (lf.lfFaceName[0]=='@')
FaceName = &lf.lfFaceName[1];
psub = get_font_subst(&font_subst_list, FaceName, lf.lfCharSet);
if(psub) {
TRACE("substituting %s,%d -> %s,%d\n", debugstr_w(FaceName), lf.lfCharSet,
debugstr_w(psub->to.name), (psub->to.charset != -1) ? psub->to.charset : lf.lfCharSet);
if (psub->to.charset != -1)
lf.lfCharSet = psub->to.charset;
}
/* We want a match on name and charset or just name if
charset was DEFAULT_CHARSET. If the latter then
we fixup the returned charset later in get_nearest_charset
where we'll either use the charset of the current ansi codepage
or if that's unavailable the first charset that the font supports.
*/
LIST_FOR_EACH(family_elem_ptr, &font_list) {
family = LIST_ENTRY(family_elem_ptr, Family, entry);
if (!strcmpiW(family->FamilyName, FaceName) ||
(psub && !strcmpiW(family->FamilyName, psub->to.name)))
{
LIST_FOR_EACH(face_elem_ptr, &family->faces) {
face = LIST_ENTRY(face_elem_ptr, Face, entry);
if((csi.fs.fsCsb[0] & (face->fs.fsCsb[0] | face->fs_links.fsCsb[0])) || !csi.fs.fsCsb[0])
if(face->scalable || can_use_bitmap)
goto found;
}
}
}
/*
* Try check the SystemLink list first for a replacement font.
* We may find good replacements there.
*/
LIST_FOR_EACH_ENTRY(font_link, &system_links, SYSTEM_LINKS, entry)
{
if(!strcmpiW(font_link->font_name, FaceName) ||
(psub && !strcmpiW(font_link->font_name,psub->to.name)))
{
TRACE("found entry in system list\n");
LIST_FOR_EACH_ENTRY(font_link_entry, &font_link->links, CHILD_FONT, entry)
{
face = font_link_entry->face;
family = face->family;
if(csi.fs.fsCsb[0] &
(face->fs.fsCsb[0] | face->fs_links.fsCsb[0]) || !csi.fs.fsCsb[0])
{
if(face->scalable || can_use_bitmap)
goto found;
}
}
}
}
}
psub = NULL; /* substitution is no more relevant */
/* If requested charset was DEFAULT_CHARSET then try using charset
corresponding to the current ansi codepage */
if (!csi.fs.fsCsb[0])
{
INT acp = GetACP();
if(!TranslateCharsetInfo((DWORD*)(INT_PTR)acp, &csi, TCI_SRCCODEPAGE)) {
FIXME("TCI failed on codepage %d\n", acp);
csi.fs.fsCsb[0] = 0;
} else
lf.lfCharSet = csi.ciCharset;
}
/* Face families are in the top 4 bits of lfPitchAndFamily,
so mask with 0xF0 before testing */
if((lf.lfPitchAndFamily & FIXED_PITCH) ||
(lf.lfPitchAndFamily & 0xF0) == FF_MODERN)
strcpyW(lf.lfFaceName, defFixed);
else if((lf.lfPitchAndFamily & 0xF0) == FF_ROMAN)
strcpyW(lf.lfFaceName, defSerif);
else if((lf.lfPitchAndFamily & 0xF0) == FF_SWISS)
strcpyW(lf.lfFaceName, defSans);
else
strcpyW(lf.lfFaceName, defSans);
LIST_FOR_EACH(family_elem_ptr, &font_list) {
family = LIST_ENTRY(family_elem_ptr, Family, entry);
if(!strcmpiW(family->FamilyName, lf.lfFaceName)) {
LIST_FOR_EACH(face_elem_ptr, &family->faces) {
face = LIST_ENTRY(face_elem_ptr, Face, entry);
if(csi.fs.fsCsb[0] & (face->fs.fsCsb[0] | face->fs_links.fsCsb[0]))
if(face->scalable || can_use_bitmap)
goto found;
}
}
}
last_resort_family = NULL;
LIST_FOR_EACH(family_elem_ptr, &font_list) {
family = LIST_ENTRY(family_elem_ptr, Family, entry);
LIST_FOR_EACH(face_elem_ptr, &family->faces) {
face = LIST_ENTRY(face_elem_ptr, Face, entry);
if(csi.fs.fsCsb[0] & (face->fs.fsCsb[0] | face->fs_links.fsCsb[0])) {
if(face->scalable)
goto found;
if(can_use_bitmap && !last_resort_family)
last_resort_family = family;
}
}
}
if(last_resort_family) {
family = last_resort_family;
csi.fs.fsCsb[0] = 0;
goto found;
}
LIST_FOR_EACH(family_elem_ptr, &font_list) {
family = LIST_ENTRY(family_elem_ptr, Family, entry);
LIST_FOR_EACH(face_elem_ptr, &family->faces) {
face = LIST_ENTRY(face_elem_ptr, Face, entry);
if(face->scalable) {
csi.fs.fsCsb[0] = 0;
WARN("just using first face for now\n");
goto found;
}
if(can_use_bitmap && !last_resort_family)
last_resort_family = family;
}
}
if(!last_resort_family) {
FIXME("can't find a single appropriate font - bailing\n");
free_font(ret);
LeaveCriticalSection( &freetype_cs );
return NULL;
}
WARN("could only find a bitmap font - this will probably look awful!\n");
family = last_resort_family;
csi.fs.fsCsb[0] = 0;
found:
it = lf.lfItalic ? 1 : 0;
bd = lf.lfWeight > 550 ? 1 : 0;
height = lf.lfHeight;
face = best = best_bitmap = NULL;
LIST_FOR_EACH_ENTRY(face, &family->faces, Face, entry)
{
if((csi.fs.fsCsb[0] & (face->fs.fsCsb[0] | face->fs_links.fsCsb[0])) || !csi.fs.fsCsb[0])
{
BOOL italic, bold;
italic = (face->ntmFlags & NTM_ITALIC) ? 1 : 0;
bold = (face->ntmFlags & NTM_BOLD) ? 1 : 0;
new_score = (italic ^ it) + (bold ^ bd);
if(!best || new_score <= score)
{
TRACE("(it=%d, bd=%d) is selected for (it=%d, bd=%d)\n",
italic, bold, it, bd);
score = new_score;
best = face;
if(best->scalable && score == 0) break;
if(!best->scalable)
{
if(height > 0)
newdiff = height - (signed int)(best->size.height);
else
newdiff = -height - ((signed int)(best->size.height) - best->size.internal_leading);
if(!best_bitmap || new_score < score ||
(diff > 0 && newdiff < diff && newdiff >= 0) || (diff < 0 && newdiff > diff))
{
TRACE("%d is better for %d diff was %d\n", best->size.height, height, diff);
diff = newdiff;
best_bitmap = best;
if(score == 0 && diff == 0) break;
}
}
}
}
}
if(best)
face = best->scalable ? best : best_bitmap;
ret->fake_italic = (it && !(face->ntmFlags & NTM_ITALIC));
ret->fake_bold = (bd && !(face->ntmFlags & NTM_BOLD));
ret->fs = face->fs;
if(csi.fs.fsCsb[0]) {
ret->charset = lf.lfCharSet;
ret->codepage = csi.ciACP;
}
else
ret->charset = get_nearest_charset(face, &ret->codepage);
TRACE("Chosen: %s %s (%s/%p:%ld)\n", debugstr_w(family->FamilyName),
debugstr_w(face->StyleName), face->file, face->font_data_ptr, face->face_index);
ret->aveWidth = height ? lf.lfWidth : 0;
if(!face->scalable) {
/* Windows uses integer scaling factors for bitmap fonts */
INT scale, scaled_height;
/* FIXME: rotation of bitmap fonts is ignored */
height = abs(GDI_ROUND( (double)height * ret->font_desc.matrix.eM22 ));
if (ret->aveWidth)
ret->aveWidth = (double)ret->aveWidth * ret->font_desc.matrix.eM11;
ret->font_desc.matrix.eM11 = ret->font_desc.matrix.eM22 = 1.0;
if (height != 0) height = diff;
height += face->size.height;
scale = (height + face->size.height - 1) / face->size.height;
scaled_height = scale * face->size.height;
/* Only jump to the next height if the difference <= 25% original height */
if (scale > 2 && scaled_height - height > face->size.height / 4) scale--;
/* The jump between unscaled and doubled is delayed by 1 */
else if (scale == 2 && scaled_height - height > (face->size.height / 4 - 1)) scale--;
ret->scale_y = scale;
width = face->size.x_ppem >> 6;
height = face->size.y_ppem >> 6;
}
else
ret->scale_y = 1.0;
TRACE("font scale y: %f\n", ret->scale_y);
ret->ft_face = OpenFontFace(ret, face, width, height);
if (!ret->ft_face)
{
free_font( ret );
LeaveCriticalSection( &freetype_cs );
return 0;
}
ret->ntmFlags = face->ntmFlags;
if (ret->charset == SYMBOL_CHARSET &&
select_charmap(ret->ft_face, FT_ENCODING_MS_SYMBOL)) {
/* No ops */
}
else if (select_charmap(ret->ft_face, FT_ENCODING_UNICODE)) {
/* No ops */
}
else {
select_charmap(ret->ft_face, FT_ENCODING_APPLE_ROMAN);
}
ret->orientation = FT_IS_SCALABLE(ret->ft_face) ? lf.lfOrientation : 0;
ret->name = psub ? strdupW(psub->from.name) : strdupW(family->FamilyName);
ret->underline = lf.lfUnderline ? 0xff : 0;
ret->strikeout = lf.lfStrikeOut ? 0xff : 0;
create_child_font_list(ret);
if (lf.lfFaceName[0]=='@') /* We need to try to load the GSUB table */
{
int length = WineEngGetFontData (ret, GSUB_TAG , 0, NULL, 0);
if (length != GDI_ERROR)
{
ret->GSUB_Table = HeapAlloc(GetProcessHeap(),0,length);
WineEngGetFontData(ret, GSUB_TAG , 0, ret->GSUB_Table, length);
TRACE("Loaded GSUB table of %i bytes\n",length);
}
}
TRACE("caching: gdiFont=%p hfont=%p\n", ret, hfont);
add_to_cache(ret);
LeaveCriticalSection( &freetype_cs );
return ret;
}
static void dump_gdi_font_list(void)
{
GdiFont *gdiFont;
struct list *elem_ptr;
TRACE("---------- gdiFont Cache ----------\n");
LIST_FOR_EACH(elem_ptr, &gdi_font_list) {
gdiFont = LIST_ENTRY(elem_ptr, struct tagGdiFont, entry);
TRACE("gdiFont=%p %s %d\n",
gdiFont, debugstr_w(gdiFont->font_desc.lf.lfFaceName), gdiFont->font_desc.lf.lfHeight);
}
TRACE("---------- Unused gdiFont Cache ----------\n");
LIST_FOR_EACH(elem_ptr, &unused_gdi_font_list) {
gdiFont = LIST_ENTRY(elem_ptr, struct tagGdiFont, entry);
TRACE("gdiFont=%p %s %d\n",
gdiFont, debugstr_w(gdiFont->font_desc.lf.lfFaceName), gdiFont->font_desc.lf.lfHeight);
}
}
/*************************************************************
* WineEngDestroyFontInstance
*
* free the gdiFont associated with this handle
*
*/
BOOL WineEngDestroyFontInstance(HFONT handle)
{
GdiFont *gdiFont;
HFONTLIST *hflist;
BOOL ret = FALSE;
struct list *font_elem_ptr, *hfontlist_elem_ptr;
int i = 0;
GDI_CheckNotLock();
EnterCriticalSection( &freetype_cs );
LIST_FOR_EACH_ENTRY(gdiFont, &child_font_list, struct tagGdiFont, entry)
{
struct list *first_hfont = list_head(&gdiFont->hfontlist);
hflist = LIST_ENTRY(first_hfont, HFONTLIST, entry);
if(hflist->hfont == handle)
{
TRACE("removing child font %p from child list\n", gdiFont);
list_remove(&gdiFont->entry);
LeaveCriticalSection( &freetype_cs );
return TRUE;
}
}
TRACE("destroying hfont=%p\n", handle);
if(TRACE_ON(font))
dump_gdi_font_list();
font_elem_ptr = list_head(&gdi_font_list);
while(font_elem_ptr) {
gdiFont = LIST_ENTRY(font_elem_ptr, struct tagGdiFont, entry);
font_elem_ptr = list_next(&gdi_font_list, font_elem_ptr);
hfontlist_elem_ptr = list_head(&gdiFont->hfontlist);
while(hfontlist_elem_ptr) {
hflist = LIST_ENTRY(hfontlist_elem_ptr, struct tagHFONTLIST, entry);
hfontlist_elem_ptr = list_next(&gdiFont->hfontlist, hfontlist_elem_ptr);
if(hflist->hfont == handle) {
list_remove(&hflist->entry);
HeapFree(GetProcessHeap(), 0, hflist);
ret = TRUE;
}
}
if(list_empty(&gdiFont->hfontlist)) {
TRACE("Moving to Unused list\n");
list_remove(&gdiFont->entry);
list_add_head(&unused_gdi_font_list, &gdiFont->entry);
}
}
font_elem_ptr = list_head(&unused_gdi_font_list);
while(font_elem_ptr && i++ < UNUSED_CACHE_SIZE)
font_elem_ptr = list_next(&unused_gdi_font_list, font_elem_ptr);
while(font_elem_ptr) {
gdiFont = LIST_ENTRY(font_elem_ptr, struct tagGdiFont, entry);
font_elem_ptr = list_next(&unused_gdi_font_list, font_elem_ptr);
TRACE("freeing %p\n", gdiFont);
list_remove(&gdiFont->entry);
free_font(gdiFont);
}
LeaveCriticalSection( &freetype_cs );
return ret;
}
static void GetEnumStructs(Face *face, LPENUMLOGFONTEXW pelf,
NEWTEXTMETRICEXW *pntm, LPDWORD ptype)
{
GdiFont *font;
LONG width, height;
if (face->cached_enum_data)
{
TRACE("Cached\n");
*pelf = face->cached_enum_data->elf;
*pntm = face->cached_enum_data->ntm;
*ptype = face->cached_enum_data->type;
return;
}
font = alloc_font();
if(face->scalable) {
height = -2048; /* 2048 is the most common em size */
width = 0;
} else {
height = face->size.y_ppem >> 6;
width = face->size.x_ppem >> 6;
}
font->scale_y = 1.0;
if (!(font->ft_face = OpenFontFace(font, face, width, height)))
{
free_font(font);
return;
}
font->name = strdupW(face->family->FamilyName);
font->ntmFlags = face->ntmFlags;
if (WineEngGetOutlineTextMetrics(font, 0, NULL))
{
memcpy(&pntm->ntmTm, &font->potm->otmTextMetrics, sizeof(TEXTMETRICW));
pntm->ntmTm.ntmSizeEM = font->potm->otmEMSquare;
lstrcpynW(pelf->elfLogFont.lfFaceName,
(WCHAR*)((char*)font->potm + (ULONG_PTR)font->potm->otmpFamilyName),
LF_FACESIZE);
lstrcpynW(pelf->elfFullName,
(WCHAR*)((char*)font->potm + (ULONG_PTR)font->potm->otmpFaceName),
LF_FULLFACESIZE);
lstrcpynW(pelf->elfStyle,
(WCHAR*)((char*)font->potm + (ULONG_PTR)font->potm->otmpStyleName),
LF_FACESIZE);
}
else
{
WineEngGetTextMetrics(font, (TEXTMETRICW *)&pntm->ntmTm);
pntm->ntmTm.ntmSizeEM = pntm->ntmTm.tmHeight - pntm->ntmTm.tmInternalLeading;
lstrcpynW(pelf->elfLogFont.lfFaceName, face->family->FamilyName, LF_FACESIZE);
lstrcpynW(pelf->elfFullName, face->family->FamilyName, LF_FULLFACESIZE);
lstrcpynW(pelf->elfStyle, face->StyleName, LF_FACESIZE);
}
pntm->ntmTm.ntmFlags = face->ntmFlags;
pntm->ntmTm.ntmCellHeight = pntm->ntmTm.tmHeight;
pntm->ntmTm.ntmAvgWidth = pntm->ntmTm.tmAveCharWidth;
pntm->ntmFontSig = face->fs;
pelf->elfScript[0] = '\0'; /* This will get set in WineEngEnumFonts */
pelf->elfLogFont.lfEscapement = 0;
pelf->elfLogFont.lfOrientation = 0;
pelf->elfLogFont.lfHeight = pntm->ntmTm.tmHeight;
pelf->elfLogFont.lfWidth = pntm->ntmTm.tmAveCharWidth;
pelf->elfLogFont.lfWeight = pntm->ntmTm.tmWeight;
pelf->elfLogFont.lfItalic = pntm->ntmTm.tmItalic;
pelf->elfLogFont.lfUnderline = pntm->ntmTm.tmUnderlined;
pelf->elfLogFont.lfStrikeOut = pntm->ntmTm.tmStruckOut;
pelf->elfLogFont.lfCharSet = pntm->ntmTm.tmCharSet;
pelf->elfLogFont.lfOutPrecision = OUT_STROKE_PRECIS;
pelf->elfLogFont.lfClipPrecision = CLIP_STROKE_PRECIS;
pelf->elfLogFont.lfQuality = DRAFT_QUALITY;
pelf->elfLogFont.lfPitchAndFamily = (pntm->ntmTm.tmPitchAndFamily & 0xf1) + 1;
*ptype = 0;
if (pntm->ntmTm.tmPitchAndFamily & TMPF_TRUETYPE)
*ptype |= TRUETYPE_FONTTYPE;
if (pntm->ntmTm.tmPitchAndFamily & TMPF_DEVICE)
*ptype |= DEVICE_FONTTYPE;
if(!(pntm->ntmTm.tmPitchAndFamily & TMPF_VECTOR))
*ptype |= RASTER_FONTTYPE;
face->cached_enum_data = HeapAlloc(GetProcessHeap(), 0, sizeof(*face->cached_enum_data));
if (face->cached_enum_data)
{
face->cached_enum_data->elf = *pelf;
face->cached_enum_data->ntm = *pntm;
face->cached_enum_data->type = *ptype;
}
free_font(font);
}
/*************************************************************
* WineEngEnumFonts
*
*/
DWORD WineEngEnumFonts(LPLOGFONTW plf, FONTENUMPROCW proc, LPARAM lparam)
{
Family *family;
Face *face;
struct list *family_elem_ptr, *face_elem_ptr;
ENUMLOGFONTEXW elf;
NEWTEXTMETRICEXW ntm;
DWORD type;
FONTSIGNATURE fs;
CHARSETINFO csi;
LOGFONTW lf;
int i;
if (!plf)
{
lf.lfCharSet = DEFAULT_CHARSET;
lf.lfPitchAndFamily = 0;
lf.lfFaceName[0] = 0;
plf = &lf;
}
TRACE("facename = %s charset %d\n", debugstr_w(plf->lfFaceName), plf->lfCharSet);
GDI_CheckNotLock();
EnterCriticalSection( &freetype_cs );
if(plf->lfFaceName[0]) {
FontSubst *psub;
psub = get_font_subst(&font_subst_list, plf->lfFaceName, plf->lfCharSet);
if(psub) {
TRACE("substituting %s -> %s\n", debugstr_w(plf->lfFaceName),
debugstr_w(psub->to.name));
lf = *plf;
strcpyW(lf.lfFaceName, psub->to.name);
plf = &lf;
}
LIST_FOR_EACH(family_elem_ptr, &font_list) {
family = LIST_ENTRY(family_elem_ptr, Family, entry);
if(!strcmpiW(plf->lfFaceName, family->FamilyName)) {
LIST_FOR_EACH(face_elem_ptr, &family->faces) {
face = LIST_ENTRY(face_elem_ptr, Face, entry);
GetEnumStructs(face, &elf, &ntm, &type);
for(i = 0; i < 32; i++) {
if(!face->scalable && face->fs.fsCsb[0] == 0) { /* OEM bitmap */
elf.elfLogFont.lfCharSet = ntm.ntmTm.tmCharSet = OEM_CHARSET;
strcpyW(elf.elfScript, OEM_DOSW);
i = 32; /* break out of loop */
} else if(!(face->fs.fsCsb[0] & (1L << i)))
continue;
else {
fs.fsCsb[0] = 1L << i;
fs.fsCsb[1] = 0;
if(!TranslateCharsetInfo(fs.fsCsb, &csi,
TCI_SRCFONTSIG))
csi.ciCharset = DEFAULT_CHARSET;
if(i == 31) csi.ciCharset = SYMBOL_CHARSET;
if(csi.ciCharset != DEFAULT_CHARSET) {
elf.elfLogFont.lfCharSet =
ntm.ntmTm.tmCharSet = csi.ciCharset;
if(ElfScriptsW[i])
strcpyW(elf.elfScript, ElfScriptsW[i]);
else
FIXME("Unknown elfscript for bit %d\n", i);
}
}
TRACE("enuming face %s full %s style %s charset %d type %d script %s it %d weight %d ntmflags %08x\n",
debugstr_w(elf.elfLogFont.lfFaceName),
debugstr_w(elf.elfFullName), debugstr_w(elf.elfStyle),
csi.ciCharset, type, debugstr_w(elf.elfScript),
elf.elfLogFont.lfItalic, elf.elfLogFont.lfWeight,
ntm.ntmTm.ntmFlags);
/* release section before callback (FIXME) */
LeaveCriticalSection( &freetype_cs );
if (!proc(&elf.elfLogFont, (TEXTMETRICW *)&ntm, type, lparam)) return 0;
EnterCriticalSection( &freetype_cs );
}
}
}
}
} else {
LIST_FOR_EACH(family_elem_ptr, &font_list) {
family = LIST_ENTRY(family_elem_ptr, Family, entry);
face_elem_ptr = list_head(&family->faces);
face = LIST_ENTRY(face_elem_ptr, Face, entry);
GetEnumStructs(face, &elf, &ntm, &type);
for(i = 0; i < 32; i++) {
if(!face->scalable && face->fs.fsCsb[0] == 0) { /* OEM bitmap */
elf.elfLogFont.lfCharSet = ntm.ntmTm.tmCharSet = OEM_CHARSET;
strcpyW(elf.elfScript, OEM_DOSW);
i = 32; /* break out of loop */
} else if(!(face->fs.fsCsb[0] & (1L << i)))
continue;
else {
fs.fsCsb[0] = 1L << i;
fs.fsCsb[1] = 0;
if(!TranslateCharsetInfo(fs.fsCsb, &csi,
TCI_SRCFONTSIG))
csi.ciCharset = DEFAULT_CHARSET;
if(i == 31) csi.ciCharset = SYMBOL_CHARSET;
if(csi.ciCharset != DEFAULT_CHARSET) {
elf.elfLogFont.lfCharSet = ntm.ntmTm.tmCharSet =
csi.ciCharset;
if(ElfScriptsW[i])
strcpyW(elf.elfScript, ElfScriptsW[i]);
else
FIXME("Unknown elfscript for bit %d\n", i);
}
}
TRACE("enuming face %s full %s style %s charset = %d type %d script %s it %d weight %d ntmflags %08x\n",
debugstr_w(elf.elfLogFont.lfFaceName),
debugstr_w(elf.elfFullName), debugstr_w(elf.elfStyle),
csi.ciCharset, type, debugstr_w(elf.elfScript),
elf.elfLogFont.lfItalic, elf.elfLogFont.lfWeight,
ntm.ntmTm.ntmFlags);
/* release section before callback (FIXME) */
LeaveCriticalSection( &freetype_cs );
if (!proc(&elf.elfLogFont, (TEXTMETRICW *)&ntm, type, lparam)) return 0;
EnterCriticalSection( &freetype_cs );
}
}
}
LeaveCriticalSection( &freetype_cs );
return 1;
}
static void FTVectorToPOINTFX(FT_Vector *vec, POINTFX *pt)
{
pt->x.value = vec->x >> 6;
pt->x.fract = (vec->x & 0x3f) << 10;
pt->x.fract |= ((pt->x.fract >> 6) | (pt->x.fract >> 12));
pt->y.value = vec->y >> 6;
pt->y.fract = (vec->y & 0x3f) << 10;
pt->y.fract |= ((pt->y.fract >> 6) | (pt->y.fract >> 12));
return;
}
/***************************************************
* According to the MSDN documentation on WideCharToMultiByte,
* certain codepages cannot set the default_used parameter.
* This returns TRUE if the codepage can set that parameter, false else
* so that calls to WideCharToMultiByte don't fail with ERROR_INVALID_PARAMETER
*/
static BOOL codepage_sets_default_used(UINT codepage)
{
switch (codepage)
{
case CP_UTF7:
case CP_UTF8:
case CP_SYMBOL:
return FALSE;
default:
return TRUE;
}
}
/*
* GSUB Table handling functions
*/
static INT GSUB_is_glyph_covered(LPCVOID table , UINT glyph)
{
const GSUB_CoverageFormat1* cf1;
cf1 = table;
if (GET_BE_WORD(cf1->CoverageFormat) == 1)
{
int count = GET_BE_WORD(cf1->GlyphCount);
int i;
TRACE("Coverage Format 1, %i glyphs\n",count);
for (i = 0; i < count; i++)
if (glyph == GET_BE_WORD(cf1->GlyphArray[i]))
return i;
return -1;
}
else if (GET_BE_WORD(cf1->CoverageFormat) == 2)
{
const GSUB_CoverageFormat2* cf2;
int i;
int count;
cf2 = (GSUB_CoverageFormat2*)cf1;
count = GET_BE_WORD(cf2->RangeCount);
TRACE("Coverage Format 2, %i ranges\n",count);
for (i = 0; i < count; i++)
{
if (glyph < GET_BE_WORD(cf2->RangeRecord[i].Start))
return -1;
if ((glyph >= GET_BE_WORD(cf2->RangeRecord[i].Start)) &&
(glyph <= GET_BE_WORD(cf2->RangeRecord[i].End)))
{
return (GET_BE_WORD(cf2->RangeRecord[i].StartCoverageIndex) +
glyph - GET_BE_WORD(cf2->RangeRecord[i].Start));
}
}
return -1;
}
else
ERR("Unknown CoverageFormat %i\n",GET_BE_WORD(cf1->CoverageFormat));
return -1;
}
static const GSUB_Script* GSUB_get_script_table( const GSUB_Header* header, const char* tag)
{
const GSUB_ScriptList *script;
const GSUB_Script *deflt = NULL;
int i;
script = (GSUB_ScriptList*)((LPBYTE)header + GET_BE_WORD(header->ScriptList));
TRACE("%i scripts in this font\n",GET_BE_WORD(script->ScriptCount));
for (i = 0; i < GET_BE_WORD(script->ScriptCount); i++)
{
const GSUB_Script *scr;
int offset;
offset = GET_BE_WORD(script->ScriptRecord[i].Script);
scr = (GSUB_Script*)((LPBYTE)script + offset);
if (strncmp(script->ScriptRecord[i].ScriptTag, tag,4)==0)
return scr;
if (strncmp(script->ScriptRecord[i].ScriptTag, "dflt",4)==0)
deflt = scr;
}
return deflt;
}
static const GSUB_LangSys* GSUB_get_lang_table( const GSUB_Script* script, const char* tag)
{
int i;
int offset;
const GSUB_LangSys *Lang;
TRACE("Deflang %x, LangCount %i\n",GET_BE_WORD(script->DefaultLangSys), GET_BE_WORD(script->LangSysCount));
for (i = 0; i < GET_BE_WORD(script->LangSysCount) ; i++)
{
offset = GET_BE_WORD(script->LangSysRecord[i].LangSys);
Lang = (GSUB_LangSys*)((LPBYTE)script + offset);
if ( strncmp(script->LangSysRecord[i].LangSysTag,tag,4)==0)
return Lang;
}
offset = GET_BE_WORD(script->DefaultLangSys);
if (offset)
{
Lang = (GSUB_LangSys*)((LPBYTE)script + offset);
return Lang;
}
return NULL;
}
static const GSUB_Feature * GSUB_get_feature(const GSUB_Header *header, const GSUB_LangSys *lang, const char* tag)
{
int i;
const GSUB_FeatureList *feature;
feature = (GSUB_FeatureList*)((LPBYTE)header + GET_BE_WORD(header->FeatureList));
TRACE("%i features\n",GET_BE_WORD(lang->FeatureCount));
for (i = 0; i < GET_BE_WORD(lang->FeatureCount); i++)
{
int index = GET_BE_WORD(lang->FeatureIndex[i]);
if (strncmp(feature->FeatureRecord[index].FeatureTag,tag,4)==0)
{
const GSUB_Feature *feat;
feat = (GSUB_Feature*)((LPBYTE)feature + GET_BE_WORD(feature->FeatureRecord[index].Feature));
return feat;
}
}
return NULL;
}
static FT_UInt GSUB_apply_feature(const GSUB_Header * header, const GSUB_Feature* feature, UINT glyph)
{
int i;
int offset;
const GSUB_LookupList *lookup;
lookup = (GSUB_LookupList*)((LPBYTE)header + GET_BE_WORD(header->LookupList));
TRACE("%i lookups\n", GET_BE_WORD(feature->LookupCount));
for (i = 0; i < GET_BE_WORD(feature->LookupCount); i++)
{
const GSUB_LookupTable *look;
offset = GET_BE_WORD(lookup->Lookup[GET_BE_WORD(feature->LookupListIndex[i])]);
look = (GSUB_LookupTable*)((LPBYTE)lookup + offset);
TRACE("type %i, flag %x, subtables %i\n",GET_BE_WORD(look->LookupType),GET_BE_WORD(look->LookupFlag),GET_BE_WORD(look->SubTableCount));
if (GET_BE_WORD(look->LookupType) != 1)
FIXME("We only handle SubType 1\n");
else
{
int j;
for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
{
const GSUB_SingleSubstFormat1 *ssf1;
offset = GET_BE_WORD(look->SubTable[j]);
ssf1 = (GSUB_SingleSubstFormat1*)((LPBYTE)look+offset);
if (GET_BE_WORD(ssf1->SubstFormat) == 1)
{
int offset = GET_BE_WORD(ssf1->Coverage);
TRACE(" subtype 1, delta %i\n", GET_BE_WORD(ssf1->DeltaGlyphID));
if (GSUB_is_glyph_covered((LPBYTE)ssf1+offset, glyph) != -1)
{
TRACE(" Glyph 0x%x ->",glyph);
glyph += GET_BE_WORD(ssf1->DeltaGlyphID);
TRACE(" 0x%x\n",glyph);
}
}
else
{
const GSUB_SingleSubstFormat2 *ssf2;
INT index;
INT offset;
ssf2 = (GSUB_SingleSubstFormat2 *)ssf1;
offset = GET_BE_WORD(ssf1->Coverage);
TRACE(" subtype 2, glyph count %i\n", GET_BE_WORD(ssf2->GlyphCount));
index = GSUB_is_glyph_covered((LPBYTE)ssf2+offset, glyph);
TRACE(" Coverage index %i\n",index);
if (index != -1)
{
TRACE(" Glyph is 0x%x ->",glyph);
glyph = GET_BE_WORD(ssf2->Substitute[index]);
TRACE("0x%x\n",glyph);
}
}
}
}
}
return glyph;
}
static const char* get_opentype_script(const GdiFont *font)
{
/*
* I am not sure if this is the correct way to generate our script tag
*/
switch (font->charset)
{
case ANSI_CHARSET: return "latn";
case BALTIC_CHARSET: return "latn"; /* ?? */
case CHINESEBIG5_CHARSET: return "hani";
case EASTEUROPE_CHARSET: return "latn"; /* ?? */
case GB2312_CHARSET: return "hani";
case GREEK_CHARSET: return "grek";
case HANGUL_CHARSET: return "hang";
case RUSSIAN_CHARSET: return "cyrl";
case SHIFTJIS_CHARSET: return "kana";
case TURKISH_CHARSET: return "latn"; /* ?? */
case VIETNAMESE_CHARSET: return "latn";
case JOHAB_CHARSET: return "latn"; /* ?? */
case ARABIC_CHARSET: return "arab";
case HEBREW_CHARSET: return "hebr";
case THAI_CHARSET: return "thai";
default: return "latn";
}
}
static FT_UInt get_GSUB_vert_glyph(const GdiFont *font, UINT glyph)
{
const GSUB_Header *header;
const GSUB_Script *script;
const GSUB_LangSys *language;
const GSUB_Feature *feature;
if (!font->GSUB_Table)
return glyph;
header = font->GSUB_Table;
script = GSUB_get_script_table(header, get_opentype_script(font));
if (!script)
{
TRACE("Script not found\n");
return glyph;
}
language = GSUB_get_lang_table(script, "xxxx"); /* Need to get Lang tag */
if (!language)
{
TRACE("Language not found\n");
return glyph;
}
feature = GSUB_get_feature(header, language, "vrt2");
if (!feature)
feature = GSUB_get_feature(header, language, "vert");
if (!feature)
{
TRACE("vrt2/vert feature not found\n");
return glyph;
}
return GSUB_apply_feature(header, feature, glyph);
}
static FT_UInt get_glyph_index(const GdiFont *font, UINT glyph)
{
FT_UInt glyphId;
if(font->ft_face->charmap->encoding == FT_ENCODING_NONE) {
WCHAR wc = (WCHAR)glyph;
BOOL default_used;
BOOL *default_used_pointer;
FT_UInt ret;
char buf;
default_used_pointer = NULL;
default_used = FALSE;
if (codepage_sets_default_used(font->codepage))
default_used_pointer = &default_used;
if(!WideCharToMultiByte(font->codepage, 0, &wc, 1, &buf, sizeof(buf), NULL, default_used_pointer) || default_used)
ret = 0;
else
ret = pFT_Get_Char_Index(font->ft_face, (unsigned char)buf);
TRACE("%04x (%02x) -> ret %d def_used %d\n", glyph, buf, ret, default_used);
return get_GSUB_vert_glyph(font,ret);
}
if(font->ft_face->charmap->encoding == FT_ENCODING_MS_SYMBOL && glyph < 0x100)
glyph = glyph + 0xf000;
glyphId = pFT_Get_Char_Index(font->ft_face, glyph);
return get_GSUB_vert_glyph(font,glyphId);
}
/*************************************************************
* WineEngGetGlyphIndices
*
*/
DWORD WineEngGetGlyphIndices(GdiFont *font, LPCWSTR lpstr, INT count,
LPWORD pgi, DWORD flags)
{
int i;
int default_char = -1;
if (flags & GGI_MARK_NONEXISTING_GLYPHS) default_char = 0xffff; /* XP would use 0x1f for bitmap fonts */
for(i = 0; i < count; i++)
{
pgi[i] = get_glyph_index(font, lpstr[i]);
if (pgi[i] == 0)
{
if (default_char == -1)
{
if (FT_IS_SFNT(font->ft_face))
{
TT_OS2 *pOS2 = pFT_Get_Sfnt_Table(font->ft_face, ft_sfnt_os2);
default_char = (pOS2->usDefaultChar ? get_glyph_index(font, pOS2->usDefaultChar) : 0);
}
else
{
TEXTMETRICW textm;
WineEngGetTextMetrics(font, &textm);
default_char = textm.tmDefaultChar;
}
}
pgi[i] = default_char;
}
}
return count;
}
static inline BOOL is_identity_FMAT2(const FMAT2 *matrix)
{
static const FMAT2 identity = { 1.0, 0.0, 0.0, 1.0 };
return !memcmp(matrix, &identity, sizeof(FMAT2));
}
static inline BOOL is_identity_MAT2(const MAT2 *matrix)
{
static const MAT2 identity = { {0,1}, {0,0}, {0,0}, {0,1} };
return !memcmp(matrix, &identity, sizeof(MAT2));
}
/*************************************************************
* WineEngGetGlyphOutline
*
* Behaves in exactly the same way as the win32 api GetGlyphOutline
* except that the first parameter is the HWINEENGFONT of the font in
* question rather than an HDC.
*
*/
DWORD WineEngGetGlyphOutline(GdiFont *incoming_font, UINT glyph, UINT format,
LPGLYPHMETRICS lpgm, DWORD buflen, LPVOID buf,
const MAT2* lpmat)
{
static const FT_Matrix identityMat = {(1 << 16), 0, 0, (1 << 16)};
FT_Face ft_face = incoming_font->ft_face;
GdiFont *font = incoming_font;
FT_UInt glyph_index;
DWORD width, height, pitch, needed = 0;
FT_Bitmap ft_bitmap;
FT_Error err;
INT left, right, top = 0, bottom = 0, adv, lsb, bbx;
FT_Angle angle = 0;
FT_Int load_flags = FT_LOAD_DEFAULT | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
double widthRatio = 1.0;
FT_Matrix transMat = identityMat;
FT_Matrix transMatUnrotated;
BOOL needsTransform = FALSE;
BOOL tategaki = (font->GSUB_Table != NULL);
UINT original_index;
TRACE("%p, %04x, %08x, %p, %08x, %p, %p\n", font, glyph, format, lpgm,
buflen, buf, lpmat);
TRACE("font transform %f %f %f %f\n",
font->font_desc.matrix.eM11, font->font_desc.matrix.eM12,
font->font_desc.matrix.eM21, font->font_desc.matrix.eM22);
GDI_CheckNotLock();
EnterCriticalSection( &freetype_cs );
if(format & GGO_GLYPH_INDEX) {
glyph_index = get_GSUB_vert_glyph(incoming_font,glyph);
original_index = glyph;
format &= ~GGO_GLYPH_INDEX;
} else {
get_glyph_index_linked(incoming_font, glyph, &font, &glyph_index);
ft_face = font->ft_face;
original_index = glyph_index;
}
if(format & GGO_UNHINTED) {
load_flags |= FT_LOAD_NO_HINTING;
format &= ~GGO_UNHINTED;
}
/* tategaki never appears to happen to lower glyph index */
if (glyph_index < TATEGAKI_LOWER_BOUND )
tategaki = FALSE;
if(original_index >= font->gmsize * GM_BLOCK_SIZE) {
font->gmsize = (original_index / GM_BLOCK_SIZE + 1);
font->gm = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, font->gm,
font->gmsize * sizeof(GM*));
} else {
if (format == GGO_METRICS && font->gm[original_index / GM_BLOCK_SIZE] != NULL &&
FONT_GM(font,original_index)->init && is_identity_MAT2(lpmat))
{
*lpgm = FONT_GM(font,original_index)->gm;
TRACE("cached: %u,%u,%s,%d,%d\n", lpgm->gmBlackBoxX, lpgm->gmBlackBoxY,
wine_dbgstr_point(&lpgm->gmptGlyphOrigin),
lpgm->gmCellIncX, lpgm->gmCellIncY);
LeaveCriticalSection( &freetype_cs );
return 1; /* FIXME */
}
}
if (!font->gm[original_index / GM_BLOCK_SIZE])
font->gm[original_index / GM_BLOCK_SIZE] = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY, sizeof(GM) * GM_BLOCK_SIZE);
/* Scaling factor */
if (font->aveWidth)
{
TEXTMETRICW tm;
WineEngGetTextMetrics(font, &tm);
widthRatio = (double)font->aveWidth;
widthRatio /= (double)font->potm->otmTextMetrics.tmAveCharWidth;
}
else
widthRatio = font->scale_y;
/* Scaling transform */
if (widthRatio != 1.0 || font->scale_y != 1.0)
{
FT_Matrix scaleMat;
scaleMat.xx = FT_FixedFromFloat(widthRatio);
scaleMat.xy = 0;
scaleMat.yx = 0;
scaleMat.yy = FT_FixedFromFloat(font->scale_y);
pFT_Matrix_Multiply(&scaleMat, &transMat);
needsTransform = TRUE;
}
/* Slant transform */
if (font->fake_italic) {
FT_Matrix slantMat;
slantMat.xx = (1 << 16);
slantMat.xy = ((1 << 16) >> 2);
slantMat.yx = 0;
slantMat.yy = (1 << 16);
pFT_Matrix_Multiply(&slantMat, &transMat);
needsTransform = TRUE;
}
/* Rotation transform */
transMatUnrotated = transMat;
if(font->orientation && !tategaki) {
FT_Matrix rotationMat;
FT_Vector vecAngle;
angle = FT_FixedFromFloat((double)font->orientation / 10.0);
pFT_Vector_Unit(&vecAngle, angle);
rotationMat.xx = vecAngle.x;
rotationMat.xy = -vecAngle.y;
rotationMat.yx = -rotationMat.xy;
rotationMat.yy = rotationMat.xx;
pFT_Matrix_Multiply(&rotationMat, &transMat);
needsTransform = TRUE;
}
/* World transform */
if (!is_identity_FMAT2(&font->font_desc.matrix))
{
FT_Matrix worldMat;
worldMat.xx = FT_FixedFromFloat(font->font_desc.matrix.eM11);
worldMat.xy = FT_FixedFromFloat(font->font_desc.matrix.eM12);
worldMat.yx = FT_FixedFromFloat(font->font_desc.matrix.eM21);
worldMat.yy = FT_FixedFromFloat(font->font_desc.matrix.eM22);
pFT_Matrix_Multiply(&worldMat, &transMat);
pFT_Matrix_Multiply(&worldMat, &transMatUnrotated);
needsTransform = TRUE;
}
/* Extra transformation specified by caller */
if (!is_identity_MAT2(lpmat))
{
FT_Matrix extraMat;
extraMat.xx = FT_FixedFromFIXED(lpmat->eM11);
extraMat.xy = FT_FixedFromFIXED(lpmat->eM12);
extraMat.yx = FT_FixedFromFIXED(lpmat->eM21);
extraMat.yy = FT_FixedFromFIXED(lpmat->eM22);
pFT_Matrix_Multiply(&extraMat, &transMat);
pFT_Matrix_Multiply(&extraMat, &transMatUnrotated);
needsTransform = TRUE;
}
if (needsTransform || (format == GGO_NATIVE || format == GGO_BEZIER ||
format == GGO_GRAY2_BITMAP || format == GGO_GRAY4_BITMAP ||
format == GGO_GRAY8_BITMAP))
{
load_flags |= FT_LOAD_NO_BITMAP;
}
err = pFT_Load_Glyph(ft_face, glyph_index, load_flags);
if(err) {
WARN("FT_Load_Glyph on index %x returns %d\n", glyph_index, err);
LeaveCriticalSection( &freetype_cs );
return GDI_ERROR;
}
left = (INT)(ft_face->glyph->metrics.horiBearingX) & -64;
right = (INT)((ft_face->glyph->metrics.horiBearingX + ft_face->glyph->metrics.width) + 63) & -64;
adv = (INT)((ft_face->glyph->metrics.horiAdvance) + 63) >> 6;
lsb = left >> 6;
bbx = (right - left) >> 6;
if(!needsTransform) {
top = (ft_face->glyph->metrics.horiBearingY + 63) & -64;
bottom = (ft_face->glyph->metrics.horiBearingY -
ft_face->glyph->metrics.height) & -64;
lpgm->gmCellIncX = adv;
lpgm->gmCellIncY = 0;
} else {
INT xc, yc;
FT_Vector vec;
for(xc = 0; xc < 2; xc++) {
for(yc = 0; yc < 2; yc++) {
vec.x = (ft_face->glyph->metrics.horiBearingX +
xc * ft_face->glyph->metrics.width);
vec.y = ft_face->glyph->metrics.horiBearingY -
yc * ft_face->glyph->metrics.height;
TRACE("Vec %ld,%ld\n", vec.x, vec.y);
pFT_Vector_Transform(&vec, &transMat);
if(xc == 0 && yc == 0) {
left = right = vec.x;
top = bottom = vec.y;
} else {
if(vec.x < left) left = vec.x;
else if(vec.x > right) right = vec.x;
if(vec.y < bottom) bottom = vec.y;
else if(vec.y > top) top = vec.y;
}
}
}
left = left & -64;
right = (right + 63) & -64;
bottom = bottom & -64;
top = (top + 63) & -64;
TRACE("transformed box: (%d,%d - %d,%d)\n", left, top, right, bottom);
vec.x = ft_face->glyph->metrics.horiAdvance;
vec.y = 0;
pFT_Vector_Transform(&vec, &transMat);
lpgm->gmCellIncX = (vec.x+63) >> 6;
lpgm->gmCellIncY = -((vec.y+63) >> 6);
vec.x = ft_face->glyph->metrics.horiAdvance;
vec.y = 0;
pFT_Vector_Transform(&vec, &transMatUnrotated);
adv = (vec.x+63) >> 6;
}
lpgm->gmBlackBoxX = (right - left) >> 6;
lpgm->gmBlackBoxY = (top - bottom) >> 6;
lpgm->gmptGlyphOrigin.x = left >> 6;
lpgm->gmptGlyphOrigin.y = top >> 6;
TRACE("%u,%u,%s,%d,%d\n", lpgm->gmBlackBoxX, lpgm->gmBlackBoxY,
wine_dbgstr_point(&lpgm->gmptGlyphOrigin),
lpgm->gmCellIncX, lpgm->gmCellIncY);
if ((format == GGO_METRICS || format == GGO_BITMAP || format == WINE_GGO_GRAY16_BITMAP) &&
is_identity_MAT2(lpmat)) /* don't cache custom transforms */
{
FONT_GM(font,original_index)->gm = *lpgm;
FONT_GM(font,original_index)->adv = adv;
FONT_GM(font,original_index)->lsb = lsb;
FONT_GM(font,original_index)->bbx = bbx;
FONT_GM(font,original_index)->init = TRUE;
}
if(format == GGO_METRICS)
{
LeaveCriticalSection( &freetype_cs );
return 1; /* FIXME */
}
if(ft_face->glyph->format != ft_glyph_format_outline &&
(format == GGO_NATIVE || format == GGO_BEZIER ||
format == GGO_GRAY2_BITMAP || format == GGO_GRAY4_BITMAP ||
format == GGO_GRAY8_BITMAP))
{
TRACE("loaded a bitmap\n");
LeaveCriticalSection( &freetype_cs );
return GDI_ERROR;
}
switch(format) {
case GGO_BITMAP:
width = lpgm->gmBlackBoxX;
height = lpgm->gmBlackBoxY;
pitch = ((width + 31) >> 5) << 2;
needed = pitch * height;
if(!buf || !buflen) break;
switch(ft_face->glyph->format) {
case ft_glyph_format_bitmap:
{
BYTE *src = ft_face->glyph->bitmap.buffer, *dst = buf;
INT w = (ft_face->glyph->bitmap.width + 7) >> 3;
INT h = ft_face->glyph->bitmap.rows;
while(h--) {
memcpy(dst, src, w);
src += ft_face->glyph->bitmap.pitch;
dst += pitch;
}
break;
}
case ft_glyph_format_outline:
ft_bitmap.width = width;
ft_bitmap.rows = height;
ft_bitmap.pitch = pitch;
ft_bitmap.pixel_mode = ft_pixel_mode_mono;
ft_bitmap.buffer = buf;
if(needsTransform)
pFT_Outline_Transform(&ft_face->glyph->outline, &transMat);
pFT_Outline_Translate(&ft_face->glyph->outline, -left, -bottom );
/* Note: FreeType will only set 'black' bits for us. */
memset(buf, 0, needed);
pFT_Outline_Get_Bitmap(library, &ft_face->glyph->outline, &ft_bitmap);
break;
default:
FIXME("loaded glyph format %x\n", ft_face->glyph->format);
LeaveCriticalSection( &freetype_cs );
return GDI_ERROR;
}
break;
case GGO_GRAY2_BITMAP:
case GGO_GRAY4_BITMAP:
case GGO_GRAY8_BITMAP:
case WINE_GGO_GRAY16_BITMAP:
{
unsigned int mult, row, col;
BYTE *start, *ptr;
width = lpgm->gmBlackBoxX;
height = lpgm->gmBlackBoxY;
pitch = (width + 3) / 4 * 4;
needed = pitch * height;
if(!buf || !buflen) break;
switch(ft_face->glyph->format) {
case ft_glyph_format_bitmap:
{
BYTE *src = ft_face->glyph->bitmap.buffer, *dst = buf;
INT h = ft_face->glyph->bitmap.rows;
INT x;
while(h--) {
for(x = 0; x < pitch; x++)
{
if(x < ft_face->glyph->bitmap.width)
dst[x] = (src[x / 8] & (1 << ( (7 - (x % 8))))) ? 0xff : 0;
else
dst[x] = 0;
}
src += ft_face->glyph->bitmap.pitch;
dst += pitch;
}
LeaveCriticalSection( &freetype_cs );
return needed;
}
case ft_glyph_format_outline:
{
ft_bitmap.width = width;
ft_bitmap.rows = height;
ft_bitmap.pitch = pitch;
ft_bitmap.pixel_mode = ft_pixel_mode_grays;
ft_bitmap.buffer = buf;
if(needsTransform)
pFT_Outline_Transform(&ft_face->glyph->outline, &transMat);
pFT_Outline_Translate(&ft_face->glyph->outline, -left, -bottom );
memset(ft_bitmap.buffer, 0, buflen);
pFT_Outline_Get_Bitmap(library, &ft_face->glyph->outline, &ft_bitmap);
if(format == GGO_GRAY2_BITMAP)
mult = 4;
else if(format == GGO_GRAY4_BITMAP)
mult = 16;
else if(format == GGO_GRAY8_BITMAP)
mult = 64;
else /* format == WINE_GGO_GRAY16_BITMAP */
{
LeaveCriticalSection( &freetype_cs );
return needed;
}
break;
}
default:
FIXME("loaded glyph format %x\n", ft_face->glyph->format);
LeaveCriticalSection( &freetype_cs );
return GDI_ERROR;
}
start = buf;
for(row = 0; row < height; row++) {
ptr = start;
for(col = 0; col < width; col++, ptr++) {
*ptr = (((int)*ptr) * mult + 128) / 256;
}
start += pitch;
}
break;
}
case WINE_GGO_HRGB_BITMAP:
case WINE_GGO_HBGR_BITMAP:
case WINE_GGO_VRGB_BITMAP:
case WINE_GGO_VBGR_BITMAP:
#ifdef HAVE_FREETYPE_FTLCDFIL_H
{
switch (ft_face->glyph->format)
{
case FT_GLYPH_FORMAT_BITMAP:
{
BYTE *src, *dst;
INT src_pitch, x;
width = lpgm->gmBlackBoxX;
height = lpgm->gmBlackBoxY;
pitch = width * 4;
needed = pitch * height;
if (!buf || !buflen) break;
memset(buf, 0, buflen);
dst = buf;
src = ft_face->glyph->bitmap.buffer;
src_pitch = ft_face->glyph->bitmap.pitch;
while ( height-- )
{
for (x = 0; x < width; x++)
{
if ( src[x / 8] & (1 << ( (7 - (x % 8)))) )
((unsigned int *)dst)[x] = ~0u;
}
src += src_pitch;
dst += pitch;
}
break;
}
case FT_GLYPH_FORMAT_OUTLINE:
{
unsigned int *dst;
BYTE *src;
INT x, src_pitch, src_width, src_height, rgb_interval, hmul, vmul;
INT x_shift, y_shift;
BOOL rgb;
FT_LcdFilter lcdfilter = FT_LCD_FILTER_DEFAULT;
FT_Render_Mode render_mode =
(format == WINE_GGO_HRGB_BITMAP || format == WINE_GGO_HBGR_BITMAP)?
FT_RENDER_MODE_LCD: FT_RENDER_MODE_LCD_V;
if ( lcdfilter == FT_LCD_FILTER_DEFAULT || lcdfilter == FT_LCD_FILTER_LIGHT )
{
if ( render_mode == FT_RENDER_MODE_LCD)
{
lpgm->gmBlackBoxX += 2;
lpgm->gmptGlyphOrigin.x -= 1;
}
else
{
lpgm->gmBlackBoxY += 2;
lpgm->gmptGlyphOrigin.y += 1;
}
}
width = lpgm->gmBlackBoxX;
height = lpgm->gmBlackBoxY;
pitch = width * 4;
needed = pitch * height;
if (!buf || !buflen) break;
memset(buf, 0, buflen);
dst = buf;
rgb = (format == WINE_GGO_HRGB_BITMAP || format == WINE_GGO_VRGB_BITMAP);
if ( needsTransform )
pFT_Outline_Transform (&ft_face->glyph->outline, &transMat);
if ( pFT_Library_SetLcdFilter )
pFT_Library_SetLcdFilter( library, lcdfilter );
pFT_Render_Glyph (ft_face->glyph, render_mode);
src = ft_face->glyph->bitmap.buffer;
src_pitch = ft_face->glyph->bitmap.pitch;
src_width = ft_face->glyph->bitmap.width;
src_height = ft_face->glyph->bitmap.rows;
if ( render_mode == FT_RENDER_MODE_LCD)
{
rgb_interval = 1;
hmul = 3;
vmul = 1;
}
else
{
rgb_interval = src_pitch;
hmul = 1;
vmul = 3;
}
x_shift = ft_face->glyph->bitmap_left - lpgm->gmptGlyphOrigin.x;
if ( x_shift < 0 ) x_shift = 0;
if ( x_shift + (src_width / hmul) > width )
x_shift = width - (src_width / hmul);
y_shift = lpgm->gmptGlyphOrigin.y - ft_face->glyph->bitmap_top;
if ( y_shift < 0 ) y_shift = 0;
if ( y_shift + (src_height / vmul) > height )
y_shift = height - (src_height / vmul);
dst += x_shift + y_shift * ( pitch / 4 );
while ( src_height )
{
for ( x = 0; x < src_width / hmul; x++ )
{
if ( rgb )
{
dst[x] = ((unsigned int)src[hmul * x + rgb_interval * 0] << 16) |
((unsigned int)src[hmul * x + rgb_interval * 1] << 8) |
((unsigned int)src[hmul * x + rgb_interval * 2] << 0) |
((unsigned int)src[hmul * x + rgb_interval * 1] << 24) ;
}
else
{
dst[x] = ((unsigned int)src[hmul * x + rgb_interval * 2] << 16) |
((unsigned int)src[hmul * x + rgb_interval * 1] << 8) |
((unsigned int)src[hmul * x + rgb_interval * 0] << 0) |
((unsigned int)src[hmul * x + rgb_interval * 1] << 24) ;
}
}
src += src_pitch * vmul;
dst += pitch / 4;
src_height -= vmul;
}
break;
}
default:
FIXME ("loaded glyph format %x\n", ft_face->glyph->format);
LeaveCriticalSection ( &freetype_cs );
return GDI_ERROR;
}
break;
}
#else
LeaveCriticalSection( &freetype_cs );
return GDI_ERROR;
#endif
case GGO_NATIVE:
{
int contour, point = 0, first_pt;
FT_Outline *outline = &ft_face->glyph->outline;
TTPOLYGONHEADER *pph;
TTPOLYCURVE *ppc;
DWORD pph_start, cpfx, type;
if(buflen == 0) buf = NULL;
if (needsTransform && buf) {
pFT_Outline_Transform(outline, &transMat);
}
for(contour = 0; contour < outline->n_contours; contour++) {
pph_start = needed;
pph = (TTPOLYGONHEADER *)((char *)buf + needed);
first_pt = point;
if(buf) {
pph->dwType = TT_POLYGON_TYPE;
FTVectorToPOINTFX(&outline->points[point], &pph->pfxStart);
}
needed += sizeof(*pph);
point++;
while(point <= outline->contours[contour]) {
ppc = (TTPOLYCURVE *)((char *)buf + needed);
type = (outline->tags[point] & FT_Curve_Tag_On) ?
TT_PRIM_LINE : TT_PRIM_QSPLINE;
cpfx = 0;
do {
if(buf)
FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
cpfx++;
point++;
} while(point <= outline->contours[contour] &&
(outline->tags[point] & FT_Curve_Tag_On) ==
(outline->tags[point-1] & FT_Curve_Tag_On));
/* At the end of a contour Windows adds the start point, but
only for Beziers */
if(point > outline->contours[contour] &&
!(outline->tags[point-1] & FT_Curve_Tag_On)) {
if(buf)
FTVectorToPOINTFX(&outline->points[first_pt], &ppc->apfx[cpfx]);
cpfx++;
} else if(point <= outline->contours[contour] &&
outline->tags[point] & FT_Curve_Tag_On) {
/* add closing pt for bezier */
if(buf)
FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
cpfx++;
point++;
}
if(buf) {
ppc->wType = type;
ppc->cpfx = cpfx;
}
needed += sizeof(*ppc) + (cpfx - 1) * sizeof(POINTFX);
}
if(buf)
pph->cb = needed - pph_start;
}
break;
}
case GGO_BEZIER:
{
/* Convert the quadratic Beziers to cubic Beziers.
The parametric eqn for a cubic Bezier is, from PLRM:
r(t) = at^3 + bt^2 + ct + r0
with the control points:
r1 = r0 + c/3
r2 = r1 + (c + b)/3
r3 = r0 + c + b + a
A quadratic Beizer has the form:
p(t) = (1-t)^2 p0 + 2(1-t)t p1 + t^2 p2
So equating powers of t leads to:
r1 = 2/3 p1 + 1/3 p0
r2 = 2/3 p1 + 1/3 p2
and of course r0 = p0, r3 = p2
*/
int contour, point = 0, first_pt;
FT_Outline *outline = &ft_face->glyph->outline;
TTPOLYGONHEADER *pph;
TTPOLYCURVE *ppc;
DWORD pph_start, cpfx, type;
FT_Vector cubic_control[4];
if(buflen == 0) buf = NULL;
if (needsTransform && buf) {
pFT_Outline_Transform(outline, &transMat);
}
for(contour = 0; contour < outline->n_contours; contour++) {
pph_start = needed;
pph = (TTPOLYGONHEADER *)((char *)buf + needed);
first_pt = point;
if(buf) {
pph->dwType = TT_POLYGON_TYPE;
FTVectorToPOINTFX(&outline->points[point], &pph->pfxStart);
}
needed += sizeof(*pph);
point++;
while(point <= outline->contours[contour]) {
ppc = (TTPOLYCURVE *)((char *)buf + needed);
type = (outline->tags[point] & FT_Curve_Tag_On) ?
TT_PRIM_LINE : TT_PRIM_CSPLINE;
cpfx = 0;
do {
if(type == TT_PRIM_LINE) {
if(buf)
FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
cpfx++;
point++;
} else {
/* Unlike QSPLINEs, CSPLINEs always have their endpoint
so cpfx = 3n */
/* FIXME: Possible optimization in endpoint calculation
if there are two consecutive curves */
cubic_control[0] = outline->points[point-1];
if(!(outline->tags[point-1] & FT_Curve_Tag_On)) {
cubic_control[0].x += outline->points[point].x + 1;
cubic_control[0].y += outline->points[point].y + 1;
cubic_control[0].x >>= 1;
cubic_control[0].y >>= 1;
}
if(point+1 > outline->contours[contour])
cubic_control[3] = outline->points[first_pt];
else {
cubic_control[3] = outline->points[point+1];
if(!(outline->tags[point+1] & FT_Curve_Tag_On)) {
cubic_control[3].x += outline->points[point].x + 1;
cubic_control[3].y += outline->points[point].y + 1;
cubic_control[3].x >>= 1;
cubic_control[3].y >>= 1;
}
}
/* r1 = 1/3 p0 + 2/3 p1
r2 = 1/3 p2 + 2/3 p1 */
cubic_control[1].x = (2 * outline->points[point].x + 1) / 3;
cubic_control[1].y = (2 * outline->points[point].y + 1) / 3;
cubic_control[2] = cubic_control[1];
cubic_control[1].x += (cubic_control[0].x + 1) / 3;
cubic_control[1].y += (cubic_control[0].y + 1) / 3;
cubic_control[2].x += (cubic_control[3].x + 1) / 3;
cubic_control[2].y += (cubic_control[3].y + 1) / 3;
if(buf) {
FTVectorToPOINTFX(&cubic_control[1], &ppc->apfx[cpfx]);
FTVectorToPOINTFX(&cubic_control[2], &ppc->apfx[cpfx+1]);
FTVectorToPOINTFX(&cubic_control[3], &ppc->apfx[cpfx+2]);
}
cpfx += 3;
point++;
}
} while(point <= outline->contours[contour] &&
(outline->tags[point] & FT_Curve_Tag_On) ==
(outline->tags[point-1] & FT_Curve_Tag_On));
/* At the end of a contour Windows adds the start point,
but only for Beziers and we've already done that.
*/
if(point <= outline->contours[contour] &&
outline->tags[point] & FT_Curve_Tag_On) {
/* This is the closing pt of a bezier, but we've already
added it, so just inc point and carry on */
point++;
}
if(buf) {
ppc->wType = type;
ppc->cpfx = cpfx;
}
needed += sizeof(*ppc) + (cpfx - 1) * sizeof(POINTFX);
}
if(buf)
pph->cb = needed - pph_start;
}
break;
}
default:
FIXME("Unsupported format %d\n", format);
LeaveCriticalSection( &freetype_cs );
return GDI_ERROR;
}
LeaveCriticalSection( &freetype_cs );
return needed;
}
static BOOL get_bitmap_text_metrics(GdiFont *font)
{
FT_Face ft_face = font->ft_face;
#ifdef HAVE_FREETYPE_FTWINFNT_H
FT_WinFNT_HeaderRec winfnt_header;
#endif
const DWORD size = offsetof(OUTLINETEXTMETRICW, otmFiller);
font->potm = HeapAlloc(GetProcessHeap(), 0, size);
font->potm->otmSize = size;
#define TM font->potm->otmTextMetrics
#ifdef HAVE_FREETYPE_FTWINFNT_H
if(pFT_Get_WinFNT_Header && !pFT_Get_WinFNT_Header(ft_face, &winfnt_header))
{
TM.tmHeight = winfnt_header.pixel_height;
TM.tmAscent = winfnt_header.ascent;
TM.tmDescent = TM.tmHeight - TM.tmAscent;
TM.tmInternalLeading = winfnt_header.internal_leading;
TM.tmExternalLeading = winfnt_header.external_leading;
TM.tmAveCharWidth = winfnt_header.avg_width;
TM.tmMaxCharWidth = winfnt_header.max_width;
TM.tmWeight = winfnt_header.weight;
TM.tmOverhang = 0;
TM.tmDigitizedAspectX = winfnt_header.horizontal_resolution;
TM.tmDigitizedAspectY = winfnt_header.vertical_resolution;
TM.tmFirstChar = winfnt_header.first_char;
TM.tmLastChar = winfnt_header.last_char;
TM.tmDefaultChar = winfnt_header.default_char + winfnt_header.first_char;
TM.tmBreakChar = winfnt_header.break_char + winfnt_header.first_char;
TM.tmItalic = winfnt_header.italic;
TM.tmUnderlined = font->underline;
TM.tmStruckOut = font->strikeout;
TM.tmPitchAndFamily = winfnt_header.pitch_and_family;
TM.tmCharSet = winfnt_header.charset;
}
else
#endif
{
TM.tmAscent = ft_face->size->metrics.ascender >> 6;
TM.tmDescent = -ft_face->size->metrics.descender >> 6;
TM.tmHeight = TM.tmAscent + TM.tmDescent;
TM.tmInternalLeading = TM.tmHeight - ft_face->size->metrics.y_ppem;
TM.tmExternalLeading = (ft_face->size->metrics.height >> 6) - TM.tmHeight;
TM.tmMaxCharWidth = ft_face->size->metrics.max_advance >> 6;
TM.tmAveCharWidth = TM.tmMaxCharWidth * 2 / 3; /* FIXME */
TM.tmWeight = ft_face->style_flags & FT_STYLE_FLAG_BOLD ? FW_BOLD : FW_NORMAL;
TM.tmOverhang = 0;
TM.tmDigitizedAspectX = 96; /* FIXME */
TM.tmDigitizedAspectY = 96; /* FIXME */
TM.tmFirstChar = 1;
TM.tmLastChar = 255;
TM.tmDefaultChar = 32;
TM.tmBreakChar = 32;
TM.tmItalic = ft_face->style_flags & FT_STYLE_FLAG_ITALIC ? 1 : 0;
TM.tmUnderlined = font->underline;
TM.tmStruckOut = font->strikeout;
/* NB inverted meaning of TMPF_FIXED_PITCH */
TM.tmPitchAndFamily = ft_face->face_flags & FT_FACE_FLAG_FIXED_WIDTH ? 0 : TMPF_FIXED_PITCH;
TM.tmCharSet = font->charset;
}
#undef TM
return TRUE;
}
static void scale_font_metrics(const GdiFont *font, LPTEXTMETRICW ptm)
{
double scale_x, scale_y;
if (font->aveWidth)
{
scale_x = (double)font->aveWidth;
scale_x /= (double)font->potm->otmTextMetrics.tmAveCharWidth;
}
else
scale_x = font->scale_y;
scale_x *= fabs(font->font_desc.matrix.eM11);
scale_y = font->scale_y * fabs(font->font_desc.matrix.eM22);
#define SCALE_X(x) (x) = GDI_ROUND((double)(x) * (scale_x))
#define SCALE_Y(y) (y) = GDI_ROUND((double)(y) * (scale_y))
SCALE_Y(ptm->tmHeight);
SCALE_Y(ptm->tmAscent);
SCALE_Y(ptm->tmDescent);
SCALE_Y(ptm->tmInternalLeading);
SCALE_Y(ptm->tmExternalLeading);
SCALE_Y(ptm->tmOverhang);
SCALE_X(ptm->tmAveCharWidth);
SCALE_X(ptm->tmMaxCharWidth);
#undef SCALE_X
#undef SCALE_Y
}
static void scale_outline_font_metrics(const GdiFont *font, OUTLINETEXTMETRICW *potm)
{
double scale_x, scale_y;
if (font->aveWidth)
{
scale_x = (double)font->aveWidth;
scale_x /= (double)font->potm->otmTextMetrics.tmAveCharWidth;
}
else
scale_x = font->scale_y;
scale_x *= fabs(font->font_desc.matrix.eM11);
scale_y = font->scale_y * fabs(font->font_desc.matrix.eM22);
scale_font_metrics(font, &potm->otmTextMetrics);
#define SCALE_X(x) (x) = GDI_ROUND((double)(x) * (scale_x))
#define SCALE_Y(y) (y) = GDI_ROUND((double)(y) * (scale_y))
SCALE_Y(potm->otmAscent);
SCALE_Y(potm->otmDescent);
SCALE_Y(potm->otmLineGap);
SCALE_Y(potm->otmsCapEmHeight);
SCALE_Y(potm->otmsXHeight);
SCALE_Y(potm->otmrcFontBox.top);
SCALE_Y(potm->otmrcFontBox.bottom);
SCALE_X(potm->otmrcFontBox.left);
SCALE_X(potm->otmrcFontBox.right);
SCALE_Y(potm->otmMacAscent);
SCALE_Y(potm->otmMacDescent);
SCALE_Y(potm->otmMacLineGap);
SCALE_X(potm->otmptSubscriptSize.x);
SCALE_Y(potm->otmptSubscriptSize.y);
SCALE_X(potm->otmptSubscriptOffset.x);
SCALE_Y(potm->otmptSubscriptOffset.y);
SCALE_X(potm->otmptSuperscriptSize.x);
SCALE_Y(potm->otmptSuperscriptSize.y);
SCALE_X(potm->otmptSuperscriptOffset.x);
SCALE_Y(potm->otmptSuperscriptOffset.y);
SCALE_Y(potm->otmsStrikeoutSize);
SCALE_Y(potm->otmsStrikeoutPosition);
SCALE_Y(potm->otmsUnderscoreSize);
SCALE_Y(potm->otmsUnderscorePosition);
#undef SCALE_X
#undef SCALE_Y
}
/*************************************************************
* WineEngGetTextMetrics
*
*/
BOOL WineEngGetTextMetrics(GdiFont *font, LPTEXTMETRICW ptm)
{
GDI_CheckNotLock();
EnterCriticalSection( &freetype_cs );
if(!font->potm) {
if(!WineEngGetOutlineTextMetrics(font, 0, NULL))
if(!get_bitmap_text_metrics(font))
{
LeaveCriticalSection( &freetype_cs );
return FALSE;
}
}
if(!font->potm)
{
LeaveCriticalSection( &freetype_cs );
return FALSE;
}
*ptm = font->potm->otmTextMetrics;
scale_font_metrics(font, ptm);
LeaveCriticalSection( &freetype_cs );
return TRUE;
}
static BOOL face_has_symbol_charmap(FT_Face ft_face)
{
int i;
for(i = 0; i < ft_face->num_charmaps; i++)
{
if(ft_face->charmaps[i]->encoding == FT_ENCODING_MS_SYMBOL)
return TRUE;
}
return FALSE;
}
/*************************************************************
* WineEngGetOutlineTextMetrics
*
*/
UINT WineEngGetOutlineTextMetrics(GdiFont *font, UINT cbSize,
OUTLINETEXTMETRICW *potm)
{
FT_Face ft_face = font->ft_face;
UINT needed, lenfam, lensty, ret;
TT_OS2 *pOS2;
TT_HoriHeader *pHori;
TT_Postscript *pPost;
FT_Fixed x_scale, y_scale;
WCHAR *family_nameW, *style_nameW;
static const WCHAR spaceW[] = {' ', '\0'};
char *cp;
INT ascent, descent;
TRACE("font=%p\n", font);
if(!FT_IS_SCALABLE(ft_face))
return 0;
GDI_CheckNotLock();
EnterCriticalSection( &freetype_cs );
if(font->potm) {
if(cbSize >= font->potm->otmSize)
{
memcpy(potm, font->potm, font->potm->otmSize);
scale_outline_font_metrics(font, potm);
}
LeaveCriticalSection( &freetype_cs );
return font->potm->otmSize;
}
needed = sizeof(*potm);
lenfam = (strlenW(font->name) + 1) * sizeof(WCHAR);
family_nameW = strdupW(font->name);
lensty = MultiByteToWideChar(CP_ACP, 0, ft_face->style_name, -1, NULL, 0)
* sizeof(WCHAR);
style_nameW = HeapAlloc(GetProcessHeap(), 0, lensty);
MultiByteToWideChar(CP_ACP, 0, ft_face->style_name, -1,
style_nameW, lensty/sizeof(WCHAR));
/* These names should be read from the TT name table */
/* length of otmpFamilyName */
needed += lenfam;
/* length of otmpFaceName */
if ((ft_face->style_flags & (FT_STYLE_FLAG_ITALIC | FT_STYLE_FLAG_BOLD)) == 0) {
needed += lenfam; /* just the family name */
} else {
needed += lenfam + lensty; /* family + " " + style */
}
/* length of otmpStyleName */
needed += lensty;
/* length of otmpFullName */
needed += lenfam + lensty;
x_scale = ft_face->size->metrics.x_scale;
y_scale = ft_face->size->metrics.y_scale;
pOS2 = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_os2);
if(!pOS2) {
FIXME("Can't find OS/2 table - not TT font?\n");
ret = 0;
goto end;
}
pHori = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_hhea);
if(!pHori) {
FIXME("Can't find HHEA table - not TT font?\n");
ret = 0;
goto end;
}
pPost = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_post); /* we can live with this failing */
TRACE("OS/2 winA = %d winD = %d typoA = %d typoD = %d typoLG = %d FT_Face a = %d, d = %d, h = %d: HORZ a = %d, d = %d lg = %d maxY = %ld minY = %ld\n",
pOS2->usWinAscent, pOS2->usWinDescent,
pOS2->sTypoAscender, pOS2->sTypoDescender, pOS2->sTypoLineGap,
ft_face->ascender, ft_face->descender, ft_face->height,
pHori->Ascender, pHori->Descender, pHori->Line_Gap,
ft_face->bbox.yMax, ft_face->bbox.yMin);
font->potm = HeapAlloc(GetProcessHeap(), 0, needed);
font->potm->otmSize = needed;
#define TM font->potm->otmTextMetrics
if(pOS2->usWinAscent + pOS2->usWinDescent == 0) {
ascent = pHori->Ascender;
descent = -pHori->Descender;
} else {
ascent = pOS2->usWinAscent;
descent = pOS2->usWinDescent;
}
if(font->yMax) {
TM.tmAscent = font->yMax;
TM.tmDescent = -font->yMin;
TM.tmInternalLeading = (TM.tmAscent + TM.tmDescent) - ft_face->size->metrics.y_ppem;
} else {
TM.tmAscent = (pFT_MulFix(ascent, y_scale) + 32) >> 6;
TM.tmDescent = (pFT_MulFix(descent, y_scale) + 32) >> 6;
TM.tmInternalLeading = (pFT_MulFix(ascent + descent
- ft_face->units_per_EM, y_scale) + 32) >> 6;
}
TM.tmHeight = TM.tmAscent + TM.tmDescent;
/* MSDN says:
el = MAX(0, LineGap - ((WinAscent + WinDescent) - (Ascender - Descender)))
*/
TM.tmExternalLeading = max(0, (pFT_MulFix(pHori->Line_Gap -
((ascent + descent) -
(pHori->Ascender - pHori->Descender)), y_scale) + 32) >> 6);
TM.tmAveCharWidth = (pFT_MulFix(pOS2->xAvgCharWidth, x_scale) + 32) >> 6;
if (TM.tmAveCharWidth == 0) {
TM.tmAveCharWidth = 1;
}
TM.tmMaxCharWidth = (pFT_MulFix(ft_face->bbox.xMax - ft_face->bbox.xMin, x_scale) + 32) >> 6;
TM.tmWeight = (font->fake_bold || (ft_face->style_flags & FT_STYLE_FLAG_BOLD)) ? FW_BOLD : FW_REGULAR;
TM.tmOverhang = 0;
TM.tmDigitizedAspectX = 300;
TM.tmDigitizedAspectY = 300;
/* It appears that for fonts with SYMBOL_CHARSET Windows always sets
* symbol range to 0 - f0ff
*/
if (face_has_symbol_charmap(ft_face) || (pOS2->usFirstCharIndex >= 0xf000 && pOS2->usFirstCharIndex < 0xf100))
{
TM.tmFirstChar = 0;
switch(GetACP())
{
case 1257: /* Baltic */
TM.tmLastChar = 0xf8fd;
break;
default:
TM.tmLastChar = 0xf0ff;
}
TM.tmBreakChar = 0x20;
TM.tmDefaultChar = 0x1f;
}
else
{
TM.tmFirstChar = pOS2->usFirstCharIndex; /* Should be the first char in the cmap */
TM.tmLastChar = pOS2->usLastCharIndex; /* Should be min(cmap_last, os2_last) */
if(pOS2->usFirstCharIndex <= 1)
TM.tmBreakChar = pOS2->usFirstCharIndex + 2;
else if (pOS2->usFirstCharIndex > 0xff)
TM.tmBreakChar = 0x20;
else
TM.tmBreakChar = pOS2->usFirstCharIndex;
TM.tmDefaultChar = TM.tmBreakChar - 1;
}
TM.tmItalic = font->fake_italic ? 255 : ((ft_face->style_flags & FT_STYLE_FLAG_ITALIC) ? 255 : 0);
TM.tmUnderlined = font->underline;
TM.tmStruckOut = font->strikeout;
/* Yes TPMF_FIXED_PITCH is correct; braindead api */
if(!FT_IS_FIXED_WIDTH(ft_face) &&
(pOS2->version == 0xFFFFU ||
pOS2->panose[PAN_PROPORTION_INDEX] != PAN_PROP_MONOSPACED))
TM.tmPitchAndFamily = TMPF_FIXED_PITCH;
else
TM.tmPitchAndFamily = 0;
switch(pOS2->panose[PAN_FAMILYTYPE_INDEX])
{
case PAN_FAMILY_SCRIPT:
TM.tmPitchAndFamily |= FF_SCRIPT;
break;
case PAN_FAMILY_DECORATIVE:
TM.tmPitchAndFamily |= FF_DECORATIVE;
break;
case PAN_ANY:
case PAN_NO_FIT:
case PAN_FAMILY_TEXT_DISPLAY:
case PAN_FAMILY_PICTORIAL: /* symbol fonts get treated as if they were text */
/* which is clearly not what the panose spec says. */
default:
if(TM.tmPitchAndFamily == 0 || /* fixed */
pOS2->panose[PAN_PROPORTION_INDEX] == PAN_PROP_MONOSPACED)
TM.tmPitchAndFamily = FF_MODERN;
else
{
switch(pOS2->panose[PAN_SERIFSTYLE_INDEX])
{
case PAN_ANY:
case PAN_NO_FIT:
default:
TM.tmPitchAndFamily |= FF_DONTCARE;
break;
case PAN_SERIF_COVE:
case PAN_SERIF_OBTUSE_COVE:
case PAN_SERIF_SQUARE_COVE:
case PAN_SERIF_OBTUSE_SQUARE_COVE:
case PAN_SERIF_SQUARE:
case PAN_SERIF_THIN:
case PAN_SERIF_BONE:
case PAN_SERIF_EXAGGERATED:
case PAN_SERIF_TRIANGLE:
TM.tmPitchAndFamily |= FF_ROMAN;
break;
case PAN_SERIF_NORMAL_SANS:
case PAN_SERIF_OBTUSE_SANS:
case PAN_SERIF_PERP_SANS:
case PAN_SERIF_FLARED:
case PAN_SERIF_ROUNDED:
TM.tmPitchAndFamily |= FF_SWISS;
break;
}
}
break;
}
if(FT_IS_SCALABLE(ft_face))
TM.tmPitchAndFamily |= TMPF_VECTOR;
if(FT_IS_SFNT(ft_face))
{
if (font->ntmFlags & NTM_PS_OPENTYPE)
TM.tmPitchAndFamily |= TMPF_DEVICE;
else
TM.tmPitchAndFamily |= TMPF_TRUETYPE;
}
TM.tmCharSet = font->charset;
font->potm->otmFiller = 0;
memcpy(&font->potm->otmPanoseNumber, pOS2->panose, PANOSE_COUNT);
font->potm->otmfsSelection = pOS2->fsSelection;
font->potm->otmfsType = pOS2->fsType;
font->potm->otmsCharSlopeRise = pHori->caret_Slope_Rise;
font->potm->otmsCharSlopeRun = pHori->caret_Slope_Run;
font->potm->otmItalicAngle = 0; /* POST table */
font->potm->otmEMSquare = ft_face->units_per_EM;
font->potm->otmAscent = (pFT_MulFix(pOS2->sTypoAscender, y_scale) + 32) >> 6;
font->potm->otmDescent = (pFT_MulFix(pOS2->sTypoDescender, y_scale) + 32) >> 6;
font->potm->otmLineGap = (pFT_MulFix(pOS2->sTypoLineGap, y_scale) + 32) >> 6;
font->potm->otmsCapEmHeight = (pFT_MulFix(pOS2->sCapHeight, y_scale) + 32) >> 6;
font->potm->otmsXHeight = (pFT_MulFix(pOS2->sxHeight, y_scale) + 32) >> 6;
font->potm->otmrcFontBox.left = (pFT_MulFix(ft_face->bbox.xMin, x_scale) + 32) >> 6;
font->potm->otmrcFontBox.right = (pFT_MulFix(ft_face->bbox.xMax, x_scale) + 32) >> 6;
font->potm->otmrcFontBox.top = (pFT_MulFix(ft_face->bbox.yMax, y_scale) + 32) >> 6;
font->potm->otmrcFontBox.bottom = (pFT_MulFix(ft_face->bbox.yMin, y_scale) + 32) >> 6;
font->potm->otmMacAscent = TM.tmAscent;
font->potm->otmMacDescent = -TM.tmDescent;
font->potm->otmMacLineGap = font->potm->otmLineGap;
font->potm->otmusMinimumPPEM = 0; /* TT Header */
font->potm->otmptSubscriptSize.x = (pFT_MulFix(pOS2->ySubscriptXSize, x_scale) + 32) >> 6;
font->potm->otmptSubscriptSize.y = (pFT_MulFix(pOS2->ySubscriptYSize, y_scale) + 32) >> 6;
font->potm->otmptSubscriptOffset.x = (pFT_MulFix(pOS2->ySubscriptXOffset, x_scale) + 32) >> 6;
font->potm->otmptSubscriptOffset.y = (pFT_MulFix(pOS2->ySubscriptYOffset, y_scale) + 32) >> 6;
font->potm->otmptSuperscriptSize.x = (pFT_MulFix(pOS2->ySuperscriptXSize, x_scale) + 32) >> 6;
font->potm->otmptSuperscriptSize.y = (pFT_MulFix(pOS2->ySuperscriptYSize, y_scale) + 32) >> 6;
font->potm->otmptSuperscriptOffset.x = (pFT_MulFix(pOS2->ySuperscriptXOffset, x_scale) + 32) >> 6;
font->potm->otmptSuperscriptOffset.y = (pFT_MulFix(pOS2->ySuperscriptYOffset, y_scale) + 32) >> 6;
font->potm->otmsStrikeoutSize = (pFT_MulFix(pOS2->yStrikeoutSize, y_scale) + 32) >> 6;
font->potm->otmsStrikeoutPosition = (pFT_MulFix(pOS2->yStrikeoutPosition, y_scale) + 32) >> 6;
if(!pPost) {
font->potm->otmsUnderscoreSize = 0;
font->potm->otmsUnderscorePosition = 0;
} else {
font->potm->otmsUnderscoreSize = (pFT_MulFix(pPost->underlineThickness, y_scale) + 32) >> 6;
font->potm->otmsUnderscorePosition = (pFT_MulFix(pPost->underlinePosition, y_scale) + 32) >> 6;
}
#undef TM
/* otmp* members should clearly have type ptrdiff_t, but M$ knows best */
cp = (char*)font->potm + sizeof(*font->potm);
font->potm->otmpFamilyName = (LPSTR)(cp - (char*)font->potm);
strcpyW((WCHAR*)cp, family_nameW);
cp += lenfam;
font->potm->otmpStyleName = (LPSTR)(cp - (char*)font->potm);
strcpyW((WCHAR*)cp, style_nameW);
cp += lensty;
font->potm->otmpFaceName = (LPSTR)(cp - (char*)font->potm);
strcpyW((WCHAR*)cp, family_nameW);
if (ft_face->style_flags & (FT_STYLE_FLAG_ITALIC | FT_STYLE_FLAG_BOLD)) {
strcatW((WCHAR*)cp, spaceW);
strcatW((WCHAR*)cp, style_nameW);
cp += lenfam + lensty;
} else
cp += lenfam;
font->potm->otmpFullName = (LPSTR)(cp - (char*)font->potm);
strcpyW((WCHAR*)cp, family_nameW);
strcatW((WCHAR*)cp, spaceW);
strcatW((WCHAR*)cp, style_nameW);
ret = needed;
if(potm && needed <= cbSize)
{
memcpy(potm, font->potm, font->potm->otmSize);
scale_outline_font_metrics(font, potm);
}
end:
HeapFree(GetProcessHeap(), 0, style_nameW);
HeapFree(GetProcessHeap(), 0, family_nameW);
LeaveCriticalSection( &freetype_cs );
return ret;
}
static BOOL load_child_font(GdiFont *font, CHILD_FONT *child)
{
HFONTLIST *hfontlist;
child->font = alloc_font();
child->font->ft_face = OpenFontFace(child->font, child->face, 0, -font->ppem);
if(!child->font->ft_face)
{
free_font(child->font);
child->font = NULL;
return FALSE;
}
child->font->font_desc = font->font_desc;
child->font->ntmFlags = child->face->ntmFlags;
child->font->orientation = font->orientation;
child->font->scale_y = font->scale_y;
hfontlist = HeapAlloc(GetProcessHeap(), 0, sizeof(*hfontlist));
hfontlist->hfont = CreateFontIndirectW(&font->font_desc.lf);
list_add_head(&child->font->hfontlist, &hfontlist->entry);
child->font->base_font = font;
list_add_head(&child_font_list, &child->font->entry);
TRACE("created child font hfont %p for base %p child %p\n", hfontlist->hfont, font, child->font);
return TRUE;
}
static BOOL get_glyph_index_linked(GdiFont *font, UINT c, GdiFont **linked_font, FT_UInt *glyph)
{
FT_UInt g;
CHILD_FONT *child_font;
if(font->base_font)
font = font->base_font;
*linked_font = font;
if((*glyph = get_glyph_index(font, c)))
return TRUE;
LIST_FOR_EACH_ENTRY(child_font, &font->child_fonts, CHILD_FONT, entry)
{
if(!child_font->font)
if(!load_child_font(font, child_font))
continue;
if(!child_font->font->ft_face)
continue;
g = get_glyph_index(child_font->font, c);
if(g)
{
*glyph = g;
*linked_font = child_font->font;
return TRUE;
}
}
return FALSE;
}
/*************************************************************
* WineEngGetCharWidth
*
*/
BOOL WineEngGetCharWidth(GdiFont *font, UINT firstChar, UINT lastChar,
LPINT buffer)
{
static const MAT2 identity = { {0,1},{0,0},{0,0},{0,1} };
UINT c;
GLYPHMETRICS gm;
FT_UInt glyph_index;
GdiFont *linked_font;
TRACE("%p, %d, %d, %p\n", font, firstChar, lastChar, buffer);
GDI_CheckNotLock();
EnterCriticalSection( &freetype_cs );
for(c = firstChar; c <= lastChar; c++) {
get_glyph_index_linked(font, c, &linked_font, &glyph_index);
WineEngGetGlyphOutline(linked_font, glyph_index, GGO_METRICS | GGO_GLYPH_INDEX,
&gm, 0, NULL, &identity);
buffer[c - firstChar] = FONT_GM(linked_font,glyph_index)->adv;
}
LeaveCriticalSection( &freetype_cs );
return TRUE;
}
/*************************************************************
* WineEngGetCharABCWidths
*
*/
BOOL WineEngGetCharABCWidths(GdiFont *font, UINT firstChar, UINT lastChar,
LPABC buffer)
{
static const MAT2 identity = { {0,1},{0,0},{0,0},{0,1} };
UINT c;
GLYPHMETRICS gm;
FT_UInt glyph_index;
GdiFont *linked_font;
TRACE("%p, %d, %d, %p\n", font, firstChar, lastChar, buffer);
if(!FT_IS_SCALABLE(font->ft_face))
return FALSE;
GDI_CheckNotLock();
EnterCriticalSection( &freetype_cs );
for(c = firstChar; c <= lastChar; c++) {
get_glyph_index_linked(font, c, &linked_font, &glyph_index);
WineEngGetGlyphOutline(linked_font, glyph_index, GGO_METRICS | GGO_GLYPH_INDEX,
&gm, 0, NULL, &identity);
buffer[c - firstChar].abcA = FONT_GM(linked_font,glyph_index)->lsb;
buffer[c - firstChar].abcB = FONT_GM(linked_font,glyph_index)->bbx;
buffer[c - firstChar].abcC = FONT_GM(linked_font,glyph_index)->adv - FONT_GM(linked_font,glyph_index)->lsb -
FONT_GM(linked_font,glyph_index)->bbx;
}
LeaveCriticalSection( &freetype_cs );
return TRUE;
}
/*************************************************************
* WineEngGetCharABCWidthsI
*
*/
BOOL WineEngGetCharABCWidthsI(GdiFont *font, UINT firstChar, UINT count, LPWORD pgi,
LPABC buffer)
{
static const MAT2 identity = { {0,1},{0,0},{0,0},{0,1} };
UINT c;
GLYPHMETRICS gm;
FT_UInt glyph_index;
GdiFont *linked_font;
if(!FT_HAS_HORIZONTAL(font->ft_face))
return FALSE;
GDI_CheckNotLock();
EnterCriticalSection( &freetype_cs );
get_glyph_index_linked(font, 'a', &linked_font, &glyph_index);
if (!pgi)
for(c = firstChar; c < firstChar+count; c++) {
WineEngGetGlyphOutline(linked_font, c, GGO_METRICS | GGO_GLYPH_INDEX,
&gm, 0, NULL, &identity);
buffer[c - firstChar].abcA = FONT_GM(linked_font,c)->lsb;
buffer[c - firstChar].abcB = FONT_GM(linked_font,c)->bbx;
buffer[c - firstChar].abcC = FONT_GM(linked_font,c)->adv - FONT_GM(linked_font,c)->lsb
- FONT_GM(linked_font,c)->bbx;
}
else
for(c = 0; c < count; c++) {
WineEngGetGlyphOutline(linked_font, pgi[c], GGO_METRICS | GGO_GLYPH_INDEX,
&gm, 0, NULL, &identity);
buffer[c].abcA = FONT_GM(linked_font,pgi[c])->lsb;
buffer[c].abcB = FONT_GM(linked_font,pgi[c])->bbx;
buffer[c].abcC = FONT_GM(linked_font,pgi[c])->adv
- FONT_GM(linked_font,pgi[c])->lsb - FONT_GM(linked_font,pgi[c])->bbx;
}
LeaveCriticalSection( &freetype_cs );
return TRUE;
}
/*************************************************************
* WineEngGetTextExtentExPoint
*
*/
BOOL WineEngGetTextExtentExPoint(GdiFont *font, LPCWSTR wstr, INT count,
INT max_ext, LPINT pnfit, LPINT dxs, LPSIZE size)
{
static const MAT2 identity = { {0,1},{0,0},{0,0},{0,1} };
INT idx;
INT nfit = 0, ext;
GLYPHMETRICS gm;
TEXTMETRICW tm;
FT_UInt glyph_index;
GdiFont *linked_font;
TRACE("%p, %s, %d, %d, %p\n", font, debugstr_wn(wstr, count), count,
max_ext, size);
GDI_CheckNotLock();
EnterCriticalSection( &freetype_cs );
size->cx = 0;
WineEngGetTextMetrics(font, &tm);
size->cy = tm.tmHeight;
for(idx = 0; idx < count; idx++) {
get_glyph_index_linked(font, wstr[idx], &linked_font, &glyph_index);
WineEngGetGlyphOutline(linked_font, glyph_index, GGO_METRICS | GGO_GLYPH_INDEX,
&gm, 0, NULL, &identity);
size->cx += FONT_GM(linked_font,glyph_index)->adv;
ext = size->cx;
if (! pnfit || ext <= max_ext) {
++nfit;
if (dxs)
dxs[idx] = ext;
}
}
if (pnfit)
*pnfit = nfit;
LeaveCriticalSection( &freetype_cs );
TRACE("return %d, %d, %d\n", size->cx, size->cy, nfit);
return TRUE;
}
/*************************************************************
* WineEngGetTextExtentExPointI
*
*/
BOOL WineEngGetTextExtentExPointI(GdiFont *font, const WORD *indices, INT count,
INT max_ext, LPINT pnfit, LPINT dxs, LPSIZE size)
{
static const MAT2 identity = { {0,1},{0,0},{0,0},{0,1} };
INT idx;
INT nfit = 0, ext;
GLYPHMETRICS gm;
TEXTMETRICW tm;
TRACE("%p, %p, %d, %d, %p\n", font, indices, count, max_ext, size);
GDI_CheckNotLock();
EnterCriticalSection( &freetype_cs );
size->cx = 0;
WineEngGetTextMetrics(font, &tm);
size->cy = tm.tmHeight;
for(idx = 0; idx < count; idx++) {
WineEngGetGlyphOutline(font, indices[idx],
GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, NULL,
&identity);
size->cx += FONT_GM(font,indices[idx])->adv;
ext = size->cx;
if (! pnfit || ext <= max_ext) {
++nfit;
if (dxs)
dxs[idx] = ext;
}
}
if (pnfit)
*pnfit = nfit;
LeaveCriticalSection( &freetype_cs );
TRACE("return %d, %d, %d\n", size->cx, size->cy, nfit);
return TRUE;
}
/*************************************************************
* WineEngGetFontData
*
*/
DWORD WineEngGetFontData(GdiFont *font, DWORD table, DWORD offset, LPVOID buf,
DWORD cbData)
{
FT_Face ft_face = font->ft_face;
FT_ULong len;
FT_Error err;
TRACE("font=%p, table=%c%c%c%c, offset=0x%x, buf=%p, cbData=0x%x\n",
font, LOBYTE(LOWORD(table)), HIBYTE(LOWORD(table)),
LOBYTE(HIWORD(table)), HIBYTE(HIWORD(table)), offset, buf, cbData);
if(!FT_IS_SFNT(ft_face))
return GDI_ERROR;
if(!buf || !cbData)
len = 0;
else
len = cbData;
if(table) { /* MS tags differ in endianness from FT ones */
table = table >> 24 | table << 24 |
(table >> 8 & 0xff00) | (table << 8 & 0xff0000);
}
/* make sure value of len is the value freetype says it needs */
if(buf && len)
{
FT_ULong needed = 0;
err = load_sfnt_table(ft_face, table, offset, NULL, &needed);
if( !err && needed < len) len = needed;
}
err = load_sfnt_table(ft_face, table, offset, buf, &len);
if(err) {
TRACE("Can't find table %c%c%c%c\n",
/* bytes were reversed */
HIBYTE(HIWORD(table)), LOBYTE(HIWORD(table)),
HIBYTE(LOWORD(table)), LOBYTE(LOWORD(table)));
return GDI_ERROR;
}
return len;
}
/*************************************************************
* WineEngGetTextFace
*
*/
INT WineEngGetTextFace(GdiFont *font, INT count, LPWSTR str)
{
INT n = strlenW(font->name) + 1;
if(str) {
lstrcpynW(str, font->name, count);
return min(count, n);
} else
return n;
}
UINT WineEngGetTextCharsetInfo(GdiFont *font, LPFONTSIGNATURE fs, DWORD flags)
{
if (fs) *fs = font->fs;
return font->charset;
}
BOOL WineEngGetLinkedHFont(DC *dc, WCHAR c, HFONT *new_hfont, UINT *glyph)
{
GdiFont *font = dc->gdiFont, *linked_font;
struct list *first_hfont;
BOOL ret;
GDI_CheckNotLock();
EnterCriticalSection( &freetype_cs );
ret = get_glyph_index_linked(font, c, &linked_font, glyph);
TRACE("get_glyph_index_linked glyph %d font %p\n", *glyph, linked_font);
if(font == linked_font)
*new_hfont = dc->hFont;
else
{
first_hfont = list_head(&linked_font->hfontlist);
*new_hfont = LIST_ENTRY(first_hfont, struct tagHFONTLIST, entry)->hfont;
}
LeaveCriticalSection( &freetype_cs );
return ret;
}
/* Retrieve a list of supported Unicode ranges for a given font.
* Can be called with NULL gs to calculate the buffer size. Returns
* the number of ranges found.
*/
static DWORD get_font_unicode_ranges(FT_Face face, GLYPHSET *gs)
{
DWORD num_ranges = 0;
if (face->charmap->encoding == FT_ENCODING_UNICODE && pFT_Get_First_Char)
{
FT_UInt glyph_code;
FT_ULong char_code, char_code_prev;
glyph_code = 0;
char_code_prev = char_code = pFT_Get_First_Char(face, &glyph_code);
TRACE("face encoding FT_ENCODING_UNICODE, number of glyphs %ld, first glyph %u, first char %04lx\n",
face->num_glyphs, glyph_code, char_code);
if (!glyph_code) return 0;
if (gs)
{
gs->ranges[0].wcLow = (USHORT)char_code;
gs->ranges[0].cGlyphs = 0;
gs->cGlyphsSupported = 0;
}
num_ranges = 1;
while (glyph_code)
{
if (char_code < char_code_prev)
{
ERR("expected increasing char code from FT_Get_Next_Char\n");
return 0;
}
if (char_code - char_code_prev > 1)
{
num_ranges++;
if (gs)
{
gs->ranges[num_ranges - 1].wcLow = (USHORT)char_code;
gs->ranges[num_ranges - 1].cGlyphs = 1;
gs->cGlyphsSupported++;
}
}
else if (gs)
{
gs->ranges[num_ranges - 1].cGlyphs++;
gs->cGlyphsSupported++;
}
char_code_prev = char_code;
char_code = pFT_Get_Next_Char(face, char_code, &glyph_code);
}
}
else
FIXME("encoding %u not supported\n", face->charmap->encoding);
return num_ranges;
}
DWORD WineEngGetFontUnicodeRanges(GdiFont *font, LPGLYPHSET glyphset)
{
DWORD size = 0;
DWORD num_ranges = get_font_unicode_ranges(font->ft_face, glyphset);
size = sizeof(GLYPHSET) + sizeof(WCRANGE) * (num_ranges - 1);
if (glyphset)
{
glyphset->cbThis = size;
glyphset->cRanges = num_ranges;
}
return size;
}
/*************************************************************
* FontIsLinked
*/
BOOL WineEngFontIsLinked(GdiFont *font)
{
BOOL ret;
GDI_CheckNotLock();
EnterCriticalSection( &freetype_cs );
ret = !list_empty(&font->child_fonts);
LeaveCriticalSection( &freetype_cs );
return ret;
}
static BOOL is_hinting_enabled(void)
{
/* Use the >= 2.2.0 function if available */
if(pFT_Get_TrueType_Engine_Type)
{
FT_TrueTypeEngineType type = pFT_Get_TrueType_Engine_Type(library);
return type == FT_TRUETYPE_ENGINE_TYPE_PATENTED;
}
#ifdef FT_DRIVER_HAS_HINTER
else
{
FT_Module mod;
/* otherwise if we've been compiled with < 2.2.0 headers
use the internal macro */
mod = pFT_Get_Module(library, "truetype");
if(mod && FT_DRIVER_HAS_HINTER(mod))
return TRUE;
}
#endif
return FALSE;
}
static BOOL is_subpixel_rendering_enabled( void )
{
#ifdef HAVE_FREETYPE_FTLCDFIL_H
return pFT_Library_SetLcdFilter &&
pFT_Library_SetLcdFilter( NULL, 0 ) != FT_Err_Unimplemented_Feature;
#else
return FALSE;
#endif
}
/*************************************************************************
* GetRasterizerCaps (GDI32.@)
*/
BOOL WINAPI GetRasterizerCaps( LPRASTERIZER_STATUS lprs, UINT cbNumBytes)
{
static int hinting = -1;
static int subpixel = -1;
if(hinting == -1)
{
hinting = is_hinting_enabled();
TRACE("hinting is %senabled\n", hinting ? "" : "NOT ");
}
if ( subpixel == -1 )
{
subpixel = is_subpixel_rendering_enabled();
TRACE("subpixel rendering is %senabled\n", subpixel ? "" : "NOT ");
}
lprs->nSize = sizeof(RASTERIZER_STATUS);
lprs->wFlags = TT_AVAILABLE | TT_ENABLED | (hinting ? WINE_TT_HINTER_ENABLED : 0);
if ( subpixel )
lprs->wFlags |= WINE_TT_SUBPIXEL_RENDERING_ENABLED;
lprs->nLanguageID = 0;
return TRUE;
}
/*************************************************************
* WineEngRealizationInfo
*/
BOOL WineEngRealizationInfo(GdiFont *font, realization_info_t *info)
{
FIXME("(%p, %p): stub!\n", font, info);
info->flags = 1;
if(FT_IS_SCALABLE(font->ft_face))
info->flags |= 2;
info->cache_num = font->cache_num;
info->unknown2 = -1;
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,
const struct TT_format0_kern_subtable *tt_f0_ks,
const USHORT *glyph_to_char,
KERNINGPAIR *kern_pair, DWORD cPairs)
{
USHORT i, nPairs;
const struct TT_kern_pair *tt_kern_pair;
TRACE("font height %d, units_per_EM %d\n", font->ppem, font->ft_face->units_per_EM);
nPairs = GET_BE_WORD(tt_f0_ks->nPairs);
TRACE("nPairs %u, searchRange %u, entrySelector %u, rangeShift %u\n",
nPairs, GET_BE_WORD(tt_f0_ks->searchRange),
GET_BE_WORD(tt_f0_ks->entrySelector), GET_BE_WORD(tt_f0_ks->rangeShift));
if (!kern_pair || !cPairs)
return nPairs;
tt_kern_pair = (const struct TT_kern_pair *)(tt_f0_ks + 1);
nPairs = min(nPairs, cPairs);
for (i = 0; i < 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)];
/* this algorithm appears to better match what Windows does */
kern_pair->iKernAmount = (short)GET_BE_WORD(tt_kern_pair[i].value) * font->ppem;
if (kern_pair->iKernAmount < 0)
{
kern_pair->iKernAmount -= font->ft_face->units_per_EM / 2;
kern_pair->iKernAmount -= font->ppem;
}
else if (kern_pair->iKernAmount > 0)
{
kern_pair->iKernAmount += font->ft_face->units_per_EM / 2;
kern_pair->iKernAmount += font->ppem;
}
kern_pair->iKernAmount /= 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++;
}
TRACE("copied %u entries\n", nPairs);
return nPairs;
}
DWORD WineEngGetKerningPairs(GdiFont *font, DWORD cPairs, KERNINGPAIR *kern_pair)
{
DWORD length;
void *buf;
const struct TT_kern_table *tt_kern_table;
const struct TT_kern_subtable *tt_kern_subtable;
USHORT i, nTables;
USHORT *glyph_to_char;
GDI_CheckNotLock();
EnterCriticalSection( &freetype_cs );
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));
LeaveCriticalSection( &freetype_cs );
return cPairs;
}
LeaveCriticalSection( &freetype_cs );
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");
LeaveCriticalSection( &freetype_cs );
return 0;
}
buf = HeapAlloc(GetProcessHeap(), 0, length);
if (!buf)
{
WARN("Out of memory\n");
LeaveCriticalSection( &freetype_cs );
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);
LeaveCriticalSection( &freetype_cs );
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 <= 65535 && !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 <= 65535; n++)
glyph_to_char[n] = (USHORT)n;
}
tt_kern_table = buf;
nTables = GET_BE_WORD(tt_kern_table->nTables);
TRACE("version %u, nTables %u\n",
GET_BE_WORD(tt_kern_table->version), nTables);
tt_kern_subtable = (const struct TT_kern_subtable *)(tt_kern_table + 1);
for (i = 0; i < nTables; i++)
{
struct TT_kern_subtable tt_kern_subtable_copy;
tt_kern_subtable_copy.version = GET_BE_WORD(tt_kern_subtable->version);
tt_kern_subtable_copy.length = GET_BE_WORD(tt_kern_subtable->length);
tt_kern_subtable_copy.coverage.word = GET_BE_WORD(tt_kern_subtable->coverage.word);
TRACE("version %u, length %u, coverage %u, subtable format %u\n",
tt_kern_subtable_copy.version, tt_kern_subtable_copy.length,
tt_kern_subtable_copy.coverage.word, tt_kern_subtable_copy.coverage.bits.format);
/* According to the TrueType specification this is the only format
* that will be properly interpreted by Windows and OS/2
*/
if (tt_kern_subtable_copy.coverage.bits.format == 0)
{
DWORD new_chunk, old_total = font->total_kern_pairs;
new_chunk = parse_format0_kern_subtable(font, (const 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, (const struct TT_format0_kern_subtable *)(tt_kern_subtable + 1),
glyph_to_char, font->kern_pairs + old_total, new_chunk);
}
else
TRACE("skipping kerning table format %u\n", tt_kern_subtable_copy.coverage.bits.format);
tt_kern_subtable = (const struct TT_kern_subtable *)((const char *)tt_kern_subtable + tt_kern_subtable_copy.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));
LeaveCriticalSection( &freetype_cs );
return cPairs;
}
LeaveCriticalSection( &freetype_cs );
return font->total_kern_pairs;
}
#else /* HAVE_FREETYPE */
/*************************************************************************/
BOOL WineEngInit(void)
{
return FALSE;
}
GdiFont *WineEngCreateFontInstance(DC *dc, HFONT hfont)
{
return NULL;
}
BOOL WineEngDestroyFontInstance(HFONT hfont)
{
return FALSE;
}
DWORD WineEngEnumFonts(LPLOGFONTW plf, FONTENUMPROCW proc, LPARAM lparam)
{
return 1;
}
DWORD WineEngGetGlyphIndices(GdiFont *font, LPCWSTR lpstr, INT count,
LPWORD pgi, DWORD flags)
{
return GDI_ERROR;
}
DWORD WineEngGetGlyphOutline(GdiFont *font, UINT glyph, UINT format,
LPGLYPHMETRICS lpgm, DWORD buflen, LPVOID buf,
const MAT2* lpmat)
{
ERR("called but we don't have FreeType\n");
return GDI_ERROR;
}
BOOL WineEngGetTextMetrics(GdiFont *font, LPTEXTMETRICW ptm)
{
ERR("called but we don't have FreeType\n");
return FALSE;
}
UINT WineEngGetOutlineTextMetrics(GdiFont *font, UINT cbSize,
OUTLINETEXTMETRICW *potm)
{
ERR("called but we don't have FreeType\n");
return 0;
}
BOOL WineEngGetCharWidth(GdiFont *font, UINT firstChar, UINT lastChar,
LPINT buffer)
{
ERR("called but we don't have FreeType\n");
return FALSE;
}
BOOL WineEngGetCharABCWidths(GdiFont *font, UINT firstChar, UINT lastChar,
LPABC buffer)
{
ERR("called but we don't have FreeType\n");
return FALSE;
}
BOOL WineEngGetCharABCWidthsI(GdiFont *font, UINT firstChar, UINT count, LPWORD pgi,
LPABC buffer)
{
ERR("called but we don't have FreeType\n");
return FALSE;
}
BOOL WineEngGetTextExtentExPoint(GdiFont *font, LPCWSTR wstr, INT count,
INT max_ext, LPINT nfit, LPINT dx, LPSIZE size)
{
ERR("called but we don't have FreeType\n");
return FALSE;
}
BOOL WineEngGetTextExtentExPointI(GdiFont *font, const WORD *indices, INT count,
INT max_ext, LPINT nfit, LPINT dx, LPSIZE size)
{
ERR("called but we don't have FreeType\n");
return FALSE;
}
DWORD WineEngGetFontData(GdiFont *font, DWORD table, DWORD offset, LPVOID buf,
DWORD cbData)
{
ERR("called but we don't have FreeType\n");
return GDI_ERROR;
}
INT WineEngGetTextFace(GdiFont *font, INT count, LPWSTR str)
{
ERR("called but we don't have FreeType\n");
return 0;
}
INT WineEngAddFontResourceEx(LPCWSTR file, DWORD flags, PVOID pdv)
{
FIXME(":stub\n");
return 1;
}
INT WineEngRemoveFontResourceEx(LPCWSTR file, DWORD flags, PVOID pdv)
{
FIXME(":stub\n");
return TRUE;
}
HANDLE WineEngAddFontMemResourceEx(PVOID pbFont, DWORD cbFont, PVOID pdv, DWORD *pcFonts)
{
FIXME(":stub\n");
return NULL;
}
UINT WineEngGetTextCharsetInfo(GdiFont *font, LPFONTSIGNATURE fs, DWORD flags)
{
FIXME(":stub\n");
return DEFAULT_CHARSET;
}
BOOL WineEngGetLinkedHFont(DC *dc, WCHAR c, HFONT *new_hfont, UINT *glyph)
{
return FALSE;
}
DWORD WineEngGetFontUnicodeRanges(GdiFont *font, LPGLYPHSET glyphset)
{
FIXME("(%p, %p): stub\n", font, glyphset);
return 0;
}
BOOL WineEngFontIsLinked(GdiFont *font)
{
return FALSE;
}
/*************************************************************************
* GetRasterizerCaps (GDI32.@)
*/
BOOL WINAPI GetRasterizerCaps( LPRASTERIZER_STATUS lprs, UINT cbNumBytes)
{
lprs->nSize = sizeof(RASTERIZER_STATUS);
lprs->wFlags = 0;
lprs->nLanguageID = 0;
return TRUE;
}
DWORD WineEngGetKerningPairs(GdiFont *font, DWORD cPairs, KERNINGPAIR *kern_pair)
{
ERR("called but we don't have FreeType\n");
return 0;
}
BOOL WineEngRealizationInfo(GdiFont *font, realization_info_t *info)
{
ERR("called but we don't have FreeType\n");
return FALSE;
}
#endif /* HAVE_FREETYPE */