| /* |
| * 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/library.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)); |
| goto next; |
| } |
| 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); |
| next: |
| 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("(%s, %x, %p): stub\n", debugstr_w(file), flags, pdv); |
| 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); |
| } |
| } |
| 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 = (const 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 = (const GSUB_ScriptList*)((const BYTE*)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 = (const GSUB_Script*)((const BYTE*)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 = (const GSUB_LangSys*)((const BYTE*)script + offset); |
| |
| if ( strncmp(script->LangSysRecord[i].LangSysTag,tag,4)==0) |
| return Lang; |
| } |
| offset = GET_BE_WORD(script->DefaultLangSys); |
| if (offset) |
| { |
| Lang = (const GSUB_LangSys*)((const BYTE*)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 = (const GSUB_FeatureList*)((const BYTE*)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 = (const GSUB_Feature*)((const BYTE*)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 = (const GSUB_LookupList*)((const BYTE*)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 = (const GSUB_LookupTable*)((const BYTE*)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 = (const GSUB_SingleSubstFormat1*)((const BYTE*)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((const BYTE*)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 = (const 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((const BYTE*)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; |
| } |
| |
| /* Make sure that the font has sane width/height ratio */ |
| if (font->aveWidth) |
| { |
| if ((font->aveWidth + font->potm->otmTextMetrics.tmHeight - 1) / font->potm->otmTextMetrics.tmHeight > 100) |
| { |
| WARN("Ignoring too large font->aveWidth %d\n", font->aveWidth); |
| font->aveWidth = 0; |
| } |
| } |
| } |
| |
| *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 = FW_REGULAR; |
| if (font->fake_bold) |
| TM.tmWeight = FW_BOLD; |
| else |
| { |
| if (ft_face->style_flags & FT_STYLE_FLAG_BOLD) |
| { |
| if (pOS2->usWeightClass > FW_MEDIUM) |
| TM.tmWeight = pOS2->usWeightClass; |
| } |
| else if (pOS2->usWeightClass <= FW_MEDIUM) |
| TM.tmWeight = pOS2->usWeightClass; |
| } |
| 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); |
| child->font->name = strdupW(child->face->family->FamilyName); |
| 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; |
| } |
| |
| /************************************************************* |
| * WineEngGetCharABCWidthsFloat |
| * |
| */ |
| BOOL WineEngGetCharABCWidthsFloat(GdiFont *font, UINT first, UINT last, LPABCFLOAT 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, first, last, buffer); |
| |
| GDI_CheckNotLock(); |
| EnterCriticalSection( &freetype_cs ); |
| |
| for (c = first; c <= last; 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 - first].abcfA = FONT_GM(linked_font, glyph_index)->lsb; |
| buffer[c - first].abcfB = FONT_GM(linked_font, glyph_index)->bbx; |
| buffer[c - first].abcfC = 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 WineEngGetCharABCWidthsFloat(GdiFont *font, UINT first, UINT last, LPABCFLOAT 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("(%s, %x, %p): stub\n", debugstr_w(file), flags, pdv); |
| return 1; |
| } |
| |
| INT WineEngRemoveFontResourceEx(LPCWSTR file, DWORD flags, PVOID pdv) |
| { |
| FIXME("(%s, %x, %p): stub\n", debugstr_w(file), flags, pdv); |
| return TRUE; |
| } |
| |
| HANDLE WineEngAddFontMemResourceEx(PVOID pbFont, DWORD cbFont, PVOID pdv, DWORD *pcFonts) |
| { |
| FIXME("(%p, %u, %p, %p): stub\n", pbFont, cbFont, pdv, pcFonts); |
| return NULL; |
| } |
| |
| UINT WineEngGetTextCharsetInfo(GdiFont *font, LPFONTSIGNATURE fs, DWORD flags) |
| { |
| FIXME("(%p, %p, %u): stub\n", font, fs, flags); |
| 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 */ |