/* * 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 #include #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_SYS_MMAN_H # include #endif #include #ifdef HAVE_DIRENT_H # include #endif #include #include #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 #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 */ #ifdef HAVE_FT2BUILD_H #include #include FT_FREETYPE_H #include FT_GLYPH_H #include FT_TYPES_H #include FT_TRUETYPE_TABLES_H #include FT_SFNT_NAMES_H #include FT_TRUETYPE_IDS_H #include FT_OUTLINE_H #include FT_TRIGONOMETRY_H #include FT_MODULE_H #include FT_WINFONTS_H #ifdef FT_LCD_FILTER_H #include FT_LCD_FILTER_H #endif #endif /* HAVE_FT2BUILD_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" #include "resource.h" WINE_DEFAULT_DEBUG_CHANNEL(font); #ifdef HAVE_FREETYPE #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_Done_Face); MAKE_FUNCPTR(FT_Get_Char_Index); MAKE_FUNCPTR(FT_Get_First_Char); MAKE_FUNCPTR(FT_Get_Next_Char); MAKE_FUNCPTR(FT_Get_Sfnt_Name); MAKE_FUNCPTR(FT_Get_Sfnt_Name_Count); MAKE_FUNCPTR(FT_Get_Sfnt_Table); MAKE_FUNCPTR(FT_Get_WinFNT_Header); MAKE_FUNCPTR(FT_Init_FreeType); MAKE_FUNCPTR(FT_Library_Version); MAKE_FUNCPTR(FT_Load_Glyph); MAKE_FUNCPTR(FT_Load_Sfnt_Table); 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_Get_CBox); MAKE_FUNCPTR(FT_Outline_Transform); MAKE_FUNCPTR(FT_Outline_Translate); MAKE_FUNCPTR(FT_Render_Glyph); MAKE_FUNCPTR(FT_Set_Charmap); MAKE_FUNCPTR(FT_Set_Pixel_Sizes); MAKE_FUNCPTR(FT_Vector_Length); MAKE_FUNCPTR(FT_Vector_Transform); MAKE_FUNCPTR(FT_Vector_Unit); static FT_Error (*pFT_Outline_Embolden)(FT_Outline *, FT_Pos); static FT_TrueTypeEngineType (*pFT_Get_TrueType_Engine_Type)(FT_Library); #ifdef FT_LCD_FILTER_H static FT_Error (*pFT_Library_SetLcdFilter)(FT_Library, FT_LcdFilter); #endif #ifdef SONAME_LIBFONTCONFIG #include MAKE_FUNCPTR(FcConfigSubstitute); MAKE_FUNCPTR(FcFontList); MAKE_FUNCPTR(FcFontSetDestroy); MAKE_FUNCPTR(FcInit); MAKE_FUNCPTR(FcPatternCreate); MAKE_FUNCPTR(FcPatternDestroy); MAKE_FUNCPTR(FcPatternGetBool); MAKE_FUNCPTR(FcPatternGetInteger); 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) #define GET_BE_DWORD(x) (x) #else #define GET_BE_WORD(x) RtlUshortByteSwap(x) #define GET_BE_DWORD(x) RtlUlongByteSwap(x) #endif #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_GASP_TAG MS_MAKE_TAG('g', 'a', 's', 'p') #define MS_GSUB_TAG MS_MAKE_TAG('G', 'S', 'U', 'B') #define MS_KERN_TAG MS_MAKE_TAG('k', 'e', 'r', 'n') #define MS_TTCF_TAG MS_MAKE_TAG('t', 't', 'c', 'f') #define MS_VDMX_TAG MS_MAKE_TAG('V', 'D', 'M', 'X') /* 'gasp' flags */ #define GASP_GRIDFIT 0x01 #define GASP_DOGRAY 0x02 #ifndef WINE_FONT_DIR #define WINE_FONT_DIR "fonts" #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; unsigned int refcount; WCHAR *StyleName; WCHAR *FullName; WCHAR *file; dev_t dev; ino_t ino; void *font_data_ptr; DWORD font_data_size; FT_Long face_index; FONTSIGNATURE fs; DWORD ntmFlags; FT_Fixed font_version; BOOL scalable; Bitmap_Size size; /* set if face is a bitmap */ DWORD flags; /* ADDFONT flags */ struct tagFamily *family; /* Cached data for Enum */ struct enum_data *cached_enum_data; } Face; #define ADDFONT_EXTERNAL_FONT 0x01 #define ADDFONT_ALLOW_BITMAP 0x02 #define ADDFONT_ADD_TO_CACHE 0x04 #define ADDFONT_ADD_RESOURCE 0x08 /* added through AddFontResource */ #define ADDFONT_VERTICAL_FONT 0x10 #define ADDFONT_AA_FLAGS(flags) ((flags) << 16) typedef struct tagFamily { struct list entry; unsigned int refcount; WCHAR *FamilyName; WCHAR *EnglishName; struct list faces; struct list *replacement; } Family; typedef struct { GLYPHMETRICS gm; ABC abc; /* metrics of the unrotated char */ 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 tagGdiFont GdiFont; #define FIRST_FONT_HANDLE 1 #define MAX_FONT_HANDLES 256 struct font_handle_entry { void *obj; WORD generation; /* generation count for reusing handle values */ }; static struct font_handle_entry font_handles[MAX_FONT_HANDLES]; static struct font_handle_entry *next_free; static struct font_handle_entry *next_unused = font_handles; static inline DWORD entry_to_handle( struct font_handle_entry *entry ) { unsigned int idx = entry - font_handles + FIRST_FONT_HANDLE; return idx | (entry->generation << 16); } static inline struct font_handle_entry *handle_entry( DWORD handle ) { unsigned int idx = LOWORD(handle) - FIRST_FONT_HANDLE; if (idx < MAX_FONT_HANDLES) { if (!HIWORD( handle ) || HIWORD( handle ) == font_handles[idx].generation) return &font_handles[idx]; } if (handle) WARN( "invalid handle 0x%08x\n", handle ); return NULL; } static DWORD alloc_font_handle( void *obj ) { struct font_handle_entry *entry; entry = next_free; if (entry) next_free = entry->obj; else if (next_unused < font_handles + MAX_FONT_HANDLES) entry = next_unused++; else { ERR( "out of realized font handles\n" ); return 0; } entry->obj = obj; if (++entry->generation == 0xffff) entry->generation = 1; return entry_to_handle( entry ); } static void free_font_handle( DWORD handle ) { struct font_handle_entry *entry; if ((entry = handle_entry( handle ))) { entry->obj = next_free; next_free = entry; } } typedef struct { struct list entry; Face *face; GdiFont *font; } CHILD_FONT; struct font_fileinfo { FILETIME writetime; LARGE_INTEGER size; WCHAR path[1]; }; struct tagGdiFont { struct list entry; struct list unused_entry; unsigned int refcount; GM **gm; DWORD gmsize; 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; DWORD aa_flags; UINT ntmCellHeight, ntmAvgWidth; FONTSIGNATURE fs; GdiFont *base_font; VOID *GSUB_Table; const VOID *vert_feature; ULONG ttc_item_offset; /* 0 if font is not a part of TrueType collection */ DWORD cache_num; DWORD instance_id; struct font_fileinfo *fileinfo; }; typedef struct { struct list entry; const WCHAR *font_name; FONTSIGNATURE fs; struct list links; } SYSTEM_LINKS; struct enum_charset_element { DWORD mask; DWORD charset; WCHAR name[LF_FACESIZE]; }; struct enum_charset_list { DWORD total; struct enum_charset_element element[32]; }; #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); static unsigned int unused_font_count; #define UNUSED_CACHE_SIZE 10 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); struct freetype_physdev { struct gdi_physdev dev; GdiFont *font; }; static inline struct freetype_physdev *get_freetype_dev( PHYSDEV dev ) { return (struct freetype_physdev *)dev; } static const struct gdi_dc_funcs freetype_funcs; 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[] = { 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 arial[] = {'A','r','i','a','l',0}; static const WCHAR bitstream_vera_sans[] = {'B','i','t','s','t','r','e','a','m',' ','V','e','r','a',' ','S','a','n','s',0}; static const WCHAR bitstream_vera_sans_mono[] = {'B','i','t','s','t','r','e','a','m',' ','V','e','r','a',' ','S','a','n','s',' ','M','o','n','o',0}; static const WCHAR bitstream_vera_serif[] = {'B','i','t','s','t','r','e','a','m',' ','V','e','r','a',' ','S','e','r','i','f',0}; static const WCHAR courier_new[] = {'C','o','u','r','i','e','r',' ','N','e','w',0}; static const WCHAR liberation_mono[] = {'L','i','b','e','r','a','t','i','o','n',' ','M','o','n','o',0}; static const WCHAR liberation_sans[] = {'L','i','b','e','r','a','t','i','o','n',' ','S','a','n','s',0}; static const WCHAR liberation_serif[] = {'L','i','b','e','r','a','t','i','o','n',' ','S','e','r','i','f',0}; static const WCHAR times_new_roman[] = {'T','i','m','e','s',' ','N','e','w',' ','R','o','m','a','n',0}; static const WCHAR SymbolW[] = {'S','y','m','b','o','l','\0'}; static const WCHAR *default_serif_list[] = { times_new_roman, liberation_serif, bitstream_vera_serif, NULL }; static const WCHAR *default_fixed_list[] = { courier_new, liberation_mono, bitstream_vera_sans_mono, NULL }; static const WCHAR *default_sans_list[] = { arial, liberation_sans, bitstream_vera_sans, NULL }; typedef struct { WCHAR *name; INT charset; } NameCs; typedef struct tagFontSubst { struct list entry; NameCs from; NameCs to; } FontSubst; /* Registry font cache key and value names */ static const WCHAR wine_fonts_key[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\', 'F','o','n','t','s',0}; static const WCHAR wine_fonts_cache_key[] = {'C','a','c','h','e',0}; static const WCHAR english_name_value[] = {'E','n','g','l','i','s','h',' ','N','a','m','e',0}; static const WCHAR face_index_value[] = {'I','n','d','e','x',0}; static const WCHAR face_ntmflags_value[] = {'N','t','m','f','l','a','g','s',0}; static const WCHAR face_version_value[] = {'V','e','r','s','i','o','n',0}; static const WCHAR face_height_value[] = {'H','e','i','g','h','t',0}; static const WCHAR face_width_value[] = {'W','i','d','t','h',0}; static const WCHAR face_size_value[] = {'S','i','z','e',0}; static const WCHAR face_x_ppem_value[] = {'X','p','p','e','m',0}; static const WCHAR face_y_ppem_value[] = {'Y','p','p','e','m',0}; static const WCHAR face_flags_value[] = {'F','l','a','g','s',0}; static const WCHAR face_internal_leading_value[] = {'I','n','t','e','r','n','a','l',' ','L','e','a','d','i','n','g',0}; static const WCHAR face_font_sig_value[] = {'F','o','n','t',' ','S','i','g','n','a','t','u','r','e',0}; static const WCHAR face_file_name_value[] = {'F','i','l','e',' ','N','a','m','e','\0'}; static const WCHAR face_full_name_value[] = {'F','u','l','l',' ','N','a','m','e','\0'}; 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 UINT default_aa_flags; static HKEY hkey_font_cache; static BOOL antialias_fakes = TRUE; 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, BOOL *vert); static BOOL get_outline_text_metrics(GdiFont *font); static BOOL get_bitmap_text_metrics(GdiFont *font); static BOOL get_text_metrics(GdiFont *font, LPTEXTMETRICW ptm); static void remove_face_from_cache( Face *face ); 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 */ 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 BOOL is_hinting_enabled(void) { static int enabled = -1; if (enabled == -1) { /* Use the >= 2.2.0 function if available */ if (pFT_Get_TrueType_Engine_Type) { FT_TrueTypeEngineType type = pFT_Get_TrueType_Engine_Type(library); enabled = (type == FT_TRUETYPE_ENGINE_TYPE_PATENTED); } else enabled = FALSE; TRACE("hinting is %senabled\n", enabled ? "" : "NOT "); } return enabled; } static BOOL is_subpixel_rendering_enabled( void ) { static int enabled = -1; if (enabled == -1) { /* FreeType >= 2.8.1 offers LCD-optimezed rendering without lcd filters. */ if (FT_SimpleVersion >= ((2 << 16) | (8 << 8) | (1 << 0))) enabled = TRUE; #ifdef FT_LCD_FILTER_H else if (pFT_Library_SetLcdFilter && pFT_Library_SetLcdFilter( NULL, 0 ) != FT_Err_Unimplemented_Feature) enabled = TRUE; #endif else enabled = FALSE; TRACE("subpixel rendering is %senabled\n", enabled ? "" : "NOT "); } return enabled; } static const struct list *get_face_list_from_family(const Family *family) { if (!list_empty(&family->faces)) return &family->faces; else return family->replacement; } static Face *find_face_from_filename(const WCHAR *file_name, const WCHAR *face_name) { Family *family; Face *face; const WCHAR *file; TRACE("looking for file %s name %s\n", debugstr_w(file_name), debugstr_w(face_name)); LIST_FOR_EACH_ENTRY(family, &font_list, Family, entry) { const struct list *face_list; if(face_name && strncmpiW(face_name, family->FamilyName, LF_FACESIZE - 1)) continue; face_list = get_face_list_from_family(family); LIST_FOR_EACH_ENTRY(face, face_list, Face, entry) { if (!face->file) continue; file = strrchrW(face->file, '/'); if(!file) file = face->file; else file++; if(strcmpiW(file, file_name)) continue; face->refcount++; return face; } } return NULL; } static Family *find_family_from_name(const WCHAR *name) { Family *family; LIST_FOR_EACH_ENTRY(family, &font_list, Family, entry) { if(!strncmpiW(family->FamilyName, name, LF_FACESIZE -1)) return family; } return NULL; } static Family *find_family_from_any_name(const WCHAR *name) { Family *family; LIST_FOR_EACH_ENTRY(family, &font_list, Family, entry) { if(!strncmpiW(family->FamilyName, name, LF_FACESIZE - 1)) return family; if(family->EnglishName && !strncmpiW(family->EnglishName, name, LF_FACESIZE - 1)) 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)); } } 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 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 WCHAR *towstr(UINT cp, const char *str) { int len; WCHAR *wstr; len = MultiByteToWideChar(cp, 0, str, -1, NULL, 0); wstr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); MultiByteToWideChar(cp, 0, str, -1, wstr, len); return wstr; } static char *strWtoA(UINT cp, const WCHAR *str) { int len = WideCharToMultiByte( cp, 0, str, -1, NULL, 0, NULL, NULL ); char *ret = HeapAlloc( GetProcessHeap(), 0, len ); WideCharToMultiByte( cp, 0, str, -1, ret, len, NULL, NULL ); return ret; } static void split_subst_info(NameCs *nc, LPSTR str) { CHAR *p = strrchr(str, ','); nc->charset = -1; if(p && *(p+1)) { nc->charset = strtol(p+1, NULL, 10); *p = '\0'; } nc->name = towstr(CP_ACP, str); } 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); } } static const LANGID mac_langid_table[] = { MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ENGLISH */ MAKELANGID(LANG_FRENCH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_FRENCH */ MAKELANGID(LANG_GERMAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_GERMAN */ MAKELANGID(LANG_ITALIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ITALIAN */ MAKELANGID(LANG_DUTCH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_DUTCH */ MAKELANGID(LANG_SWEDISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SWEDISH */ MAKELANGID(LANG_SPANISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SPANISH */ MAKELANGID(LANG_DANISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_DANISH */ MAKELANGID(LANG_PORTUGUESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_PORTUGUESE */ MAKELANGID(LANG_NORWEGIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_NORWEGIAN */ MAKELANGID(LANG_HEBREW,SUBLANG_DEFAULT), /* TT_MAC_LANGID_HEBREW */ MAKELANGID(LANG_JAPANESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_JAPANESE */ MAKELANGID(LANG_ARABIC,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ARABIC */ MAKELANGID(LANG_FINNISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_FINNISH */ MAKELANGID(LANG_GREEK,SUBLANG_DEFAULT), /* TT_MAC_LANGID_GREEK */ MAKELANGID(LANG_ICELANDIC,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ICELANDIC */ MAKELANGID(LANG_MALTESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MALTESE */ MAKELANGID(LANG_TURKISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TURKISH */ MAKELANGID(LANG_CROATIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_CROATIAN */ MAKELANGID(LANG_CHINESE_TRADITIONAL,SUBLANG_DEFAULT), /* TT_MAC_LANGID_CHINESE_TRADITIONAL */ MAKELANGID(LANG_URDU,SUBLANG_DEFAULT), /* TT_MAC_LANGID_URDU */ MAKELANGID(LANG_HINDI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_HINDI */ MAKELANGID(LANG_THAI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_THAI */ MAKELANGID(LANG_KOREAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_KOREAN */ MAKELANGID(LANG_LITHUANIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_LITHUANIAN */ MAKELANGID(LANG_POLISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_POLISH */ MAKELANGID(LANG_HUNGARIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_HUNGARIAN */ MAKELANGID(LANG_ESTONIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ESTONIAN */ MAKELANGID(LANG_LATVIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_LETTISH */ MAKELANGID(LANG_SAMI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SAAMISK */ MAKELANGID(LANG_FAEROESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_FAEROESE */ MAKELANGID(LANG_FARSI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_FARSI */ MAKELANGID(LANG_RUSSIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_RUSSIAN */ MAKELANGID(LANG_CHINESE_SIMPLIFIED,SUBLANG_DEFAULT), /* TT_MAC_LANGID_CHINESE_SIMPLIFIED */ MAKELANGID(LANG_DUTCH,SUBLANG_DUTCH_BELGIAN), /* TT_MAC_LANGID_FLEMISH */ MAKELANGID(LANG_IRISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_IRISH */ MAKELANGID(LANG_ALBANIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ALBANIAN */ MAKELANGID(LANG_ROMANIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ROMANIAN */ MAKELANGID(LANG_CZECH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_CZECH */ MAKELANGID(LANG_SLOVAK,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SLOVAK */ MAKELANGID(LANG_SLOVENIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SLOVENIAN */ 0, /* TT_MAC_LANGID_YIDDISH */ MAKELANGID(LANG_SERBIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SERBIAN */ MAKELANGID(LANG_MACEDONIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MACEDONIAN */ MAKELANGID(LANG_BULGARIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_BULGARIAN */ MAKELANGID(LANG_UKRAINIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_UKRAINIAN */ MAKELANGID(LANG_BELARUSIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_BYELORUSSIAN */ MAKELANGID(LANG_UZBEK,SUBLANG_DEFAULT), /* TT_MAC_LANGID_UZBEK */ MAKELANGID(LANG_KAZAK,SUBLANG_DEFAULT), /* TT_MAC_LANGID_KAZAKH */ MAKELANGID(LANG_AZERI,SUBLANG_AZERI_CYRILLIC), /* TT_MAC_LANGID_AZERBAIJANI */ 0, /* TT_MAC_LANGID_AZERBAIJANI_ARABIC_SCRIPT */ MAKELANGID(LANG_ARMENIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ARMENIAN */ MAKELANGID(LANG_GEORGIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_GEORGIAN */ 0, /* TT_MAC_LANGID_MOLDAVIAN */ MAKELANGID(LANG_KYRGYZ,SUBLANG_DEFAULT), /* TT_MAC_LANGID_KIRGHIZ */ MAKELANGID(LANG_TAJIK,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TAJIKI */ MAKELANGID(LANG_TURKMEN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TURKMEN */ MAKELANGID(LANG_MONGOLIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MONGOLIAN */ MAKELANGID(LANG_MONGOLIAN,SUBLANG_MONGOLIAN_CYRILLIC_MONGOLIA), /* TT_MAC_LANGID_MONGOLIAN_CYRILLIC_SCRIPT */ MAKELANGID(LANG_PASHTO,SUBLANG_DEFAULT), /* TT_MAC_LANGID_PASHTO */ 0, /* TT_MAC_LANGID_KURDISH */ MAKELANGID(LANG_KASHMIRI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_KASHMIRI */ MAKELANGID(LANG_SINDHI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SINDHI */ MAKELANGID(LANG_TIBETAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TIBETAN */ MAKELANGID(LANG_NEPALI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_NEPALI */ MAKELANGID(LANG_SANSKRIT,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SANSKRIT */ MAKELANGID(LANG_MARATHI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MARATHI */ MAKELANGID(LANG_BENGALI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_BENGALI */ MAKELANGID(LANG_ASSAMESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ASSAMESE */ MAKELANGID(LANG_GUJARATI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_GUJARATI */ MAKELANGID(LANG_PUNJABI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_PUNJABI */ MAKELANGID(LANG_ORIYA,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ORIYA */ MAKELANGID(LANG_MALAYALAM,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MALAYALAM */ MAKELANGID(LANG_KANNADA,SUBLANG_DEFAULT), /* TT_MAC_LANGID_KANNADA */ MAKELANGID(LANG_TAMIL,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TAMIL */ MAKELANGID(LANG_TELUGU,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TELUGU */ MAKELANGID(LANG_SINHALESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SINHALESE */ 0, /* TT_MAC_LANGID_BURMESE */ MAKELANGID(LANG_KHMER,SUBLANG_DEFAULT), /* TT_MAC_LANGID_KHMER */ MAKELANGID(LANG_LAO,SUBLANG_DEFAULT), /* TT_MAC_LANGID_LAO */ MAKELANGID(LANG_VIETNAMESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_VIETNAMESE */ MAKELANGID(LANG_INDONESIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_INDONESIAN */ 0, /* TT_MAC_LANGID_TAGALOG */ MAKELANGID(LANG_MALAY,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MALAY_ROMAN_SCRIPT */ 0, /* TT_MAC_LANGID_MALAY_ARABIC_SCRIPT */ MAKELANGID(LANG_AMHARIC,SUBLANG_DEFAULT), /* TT_MAC_LANGID_AMHARIC */ MAKELANGID(LANG_TIGRIGNA,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TIGRINYA */ 0, /* TT_MAC_LANGID_GALLA */ 0, /* TT_MAC_LANGID_SOMALI */ MAKELANGID(LANG_SWAHILI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SWAHILI */ 0, /* TT_MAC_LANGID_RUANDA */ 0, /* TT_MAC_LANGID_RUNDI */ 0, /* TT_MAC_LANGID_CHEWA */ MAKELANGID(LANG_MALAGASY,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MALAGASY */ MAKELANGID(LANG_ESPERANTO,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ESPERANTO */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 95-111 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 112-127 */ MAKELANGID(LANG_WELSH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_WELSH */ MAKELANGID(LANG_BASQUE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_BASQUE */ MAKELANGID(LANG_CATALAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_CATALAN */ 0, /* TT_MAC_LANGID_LATIN */ MAKELANGID(LANG_QUECHUA,SUBLANG_DEFAULT), /* TT_MAC_LANGID_QUECHUA */ 0, /* TT_MAC_LANGID_GUARANI */ 0, /* TT_MAC_LANGID_AYMARA */ MAKELANGID(LANG_TATAR,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TATAR */ MAKELANGID(LANG_UIGHUR,SUBLANG_DEFAULT), /* TT_MAC_LANGID_UIGHUR */ 0, /* TT_MAC_LANGID_DZONGKHA */ 0, /* TT_MAC_LANGID_JAVANESE */ 0, /* TT_MAC_LANGID_SUNDANESE */ MAKELANGID(LANG_GALICIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_GALICIAN */ MAKELANGID(LANG_AFRIKAANS,SUBLANG_DEFAULT), /* TT_MAC_LANGID_AFRIKAANS */ MAKELANGID(LANG_BRETON,SUBLANG_DEFAULT), /* TT_MAC_LANGID_BRETON */ MAKELANGID(LANG_INUKTITUT,SUBLANG_DEFAULT), /* TT_MAC_LANGID_INUKTITUT */ MAKELANGID(LANG_SCOTTISH_GAELIC,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SCOTTISH_GAELIC */ MAKELANGID(LANG_MANX_GAELIC,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MANX_GAELIC */ MAKELANGID(LANG_IRISH,SUBLANG_IRISH_IRELAND), /* TT_MAC_LANGID_IRISH_GAELIC */ 0, /* TT_MAC_LANGID_TONGAN */ 0, /* TT_MAC_LANGID_GREEK_POLYTONIC */ MAKELANGID(LANG_GREENLANDIC,SUBLANG_DEFAULT), /* TT_MAC_LANGID_GREELANDIC */ MAKELANGID(LANG_AZERI,SUBLANG_AZERI_LATIN), /* TT_MAC_LANGID_AZERBAIJANI_ROMAN_SCRIPT */ }; static inline WORD get_mac_code_page( const FT_SfntName *name ) { if (name->encoding_id == TT_MAC_ID_SIMPLIFIED_CHINESE) return 10008; /* special case */ return 10000 + name->encoding_id; } static int match_name_table_language( const FT_SfntName *name, LANGID lang ) { LANGID name_lang; int res = 0; switch (name->platform_id) { case TT_PLATFORM_MICROSOFT: res += 5; /* prefer the Microsoft name */ switch (name->encoding_id) { case TT_MS_ID_UNICODE_CS: case TT_MS_ID_SYMBOL_CS: name_lang = name->language_id; break; default: return 0; } break; case TT_PLATFORM_MACINTOSH: if (!IsValidCodePage( get_mac_code_page( name ))) return 0; if (name->language_id >= ARRAY_SIZE( mac_langid_table )) return 0; name_lang = mac_langid_table[name->language_id]; break; case TT_PLATFORM_APPLE_UNICODE: res += 2; /* prefer Unicode encodings */ switch (name->encoding_id) { case TT_APPLE_ID_DEFAULT: case TT_APPLE_ID_ISO_10646: case TT_APPLE_ID_UNICODE_2_0: if (name->language_id >= ARRAY_SIZE( mac_langid_table )) return 0; name_lang = mac_langid_table[name->language_id]; break; default: return 0; } break; default: return 0; } if (name_lang == lang) res += 30; else if (PRIMARYLANGID( name_lang ) == PRIMARYLANGID( lang )) res += 20; else if (name_lang == MAKELANGID( LANG_ENGLISH, SUBLANG_DEFAULT )) res += 10; return res; } static WCHAR *copy_name_table_string( const FT_SfntName *name ) { WCHAR *ret; WORD codepage; int i; switch (name->platform_id) { case TT_PLATFORM_APPLE_UNICODE: case TT_PLATFORM_MICROSOFT: ret = HeapAlloc( GetProcessHeap(), 0, name->string_len + sizeof(WCHAR) ); for (i = 0; i < name->string_len / 2; i++) ret[i] = (name->string[i * 2] << 8) | name->string[i * 2 + 1]; ret[i] = 0; return ret; case TT_PLATFORM_MACINTOSH: codepage = get_mac_code_page( name ); i = MultiByteToWideChar( codepage, 0, (char *)name->string, name->string_len, NULL, 0 ); ret = HeapAlloc( GetProcessHeap(), 0, (i + 1) * sizeof(WCHAR) ); MultiByteToWideChar( codepage, 0, (char *)name->string, name->string_len, ret, i ); ret[i] = 0; return ret; } return NULL; } static WCHAR *get_face_name(FT_Face ft_face, FT_UShort name_id, LANGID language_id) { FT_SfntName name; FT_UInt num_names, name_index; int res, best_lang = 0, best_index = -1; if (!FT_IS_SFNT(ft_face)) return NULL; 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 )) continue; if (name.name_id != name_id) continue; res = match_name_table_language( &name, language_id ); if (res > best_lang) { best_lang = res; best_index = name_index; } } if (best_index != -1 && !pFT_Get_Sfnt_Name( ft_face, best_index, &name )) { WCHAR *ret = copy_name_table_string( &name ); TRACE( "name %u found platform %u lang %04x %s\n", name_id, name.platform_id, name.language_id, debugstr_w( ret )); return ret; } return NULL; } static inline BOOL faces_equal( const Face *f1, const Face *f2 ) { if (strcmpiW( f1->StyleName, f2->StyleName )) return FALSE; if (f1->scalable) return TRUE; if (f1->size.y_ppem != f2->size.y_ppem) return FALSE; return !memcmp( &f1->fs, &f2->fs, sizeof(f1->fs) ); } static void release_family( Family *family ) { if (--family->refcount) return; assert( list_empty( &family->faces )); list_remove( &family->entry ); HeapFree( GetProcessHeap(), 0, family->FamilyName ); HeapFree( GetProcessHeap(), 0, family->EnglishName ); HeapFree( GetProcessHeap(), 0, family ); } static void release_face( Face *face ) { if (--face->refcount) return; if (face->family) { if (face->flags & ADDFONT_ADD_TO_CACHE) remove_face_from_cache( face ); list_remove( &face->entry ); release_family( face->family ); } HeapFree( GetProcessHeap(), 0, face->file ); HeapFree( GetProcessHeap(), 0, face->StyleName ); HeapFree( GetProcessHeap(), 0, face->FullName ); HeapFree( GetProcessHeap(), 0, face->cached_enum_data ); HeapFree( GetProcessHeap(), 0, face ); } static inline int style_order(const Face *face) { switch (face->ntmFlags & (NTM_REGULAR | NTM_BOLD | NTM_ITALIC)) { case NTM_REGULAR: return 0; case NTM_BOLD: return 1; case NTM_ITALIC: return 2; case NTM_BOLD | NTM_ITALIC: return 3; default: 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; } } static BOOL insert_face_in_family_list( Face *face, Family *family ) { Face *cursor; LIST_FOR_EACH_ENTRY( cursor, &family->faces, Face, entry ) { if (faces_equal( face, cursor )) { TRACE("Already loaded font %s %s original version is %lx, this version is %lx\n", debugstr_w(family->FamilyName), debugstr_w(face->StyleName), cursor->font_version, face->font_version); if (face->file && face->dev == cursor->dev && face->ino == cursor->ino) { cursor->refcount++; TRACE("Font %s already in list, refcount now %d\n", debugstr_w(face->file), cursor->refcount); return FALSE; } if (face->font_version <= cursor->font_version) { TRACE("Original font %s is newer so skipping %s\n", debugstr_w(cursor->file), debugstr_w(face->file)); return FALSE; } else { TRACE("Replacing original %s with %s\n", debugstr_w(cursor->file), debugstr_w(face->file)); list_add_before( &cursor->entry, &face->entry ); face->family = family; family->refcount++; face->refcount++; release_face( cursor ); return TRUE; } } else TRACE("Adding new %s\n", debugstr_w(face->file)); if (style_order( face ) < style_order( cursor )) break; } list_add_before( &cursor->entry, &face->entry ); face->family = family; family->refcount++; face->refcount++; return TRUE; } /**************************************************************** * NB This function stores the ptrs to the strings to save copying. * Don't free them after calling. */ static Family *create_family( WCHAR *name, WCHAR *english_name ) { Family * const family = HeapAlloc( GetProcessHeap(), 0, sizeof(*family) ); family->refcount = 1; family->FamilyName = name; family->EnglishName = english_name; list_init( &family->faces ); family->replacement = &family->faces; list_add_tail( &font_list, &family->entry ); return family; } static LONG reg_load_dword(HKEY hkey, const WCHAR *value, DWORD *data) { DWORD type, size = sizeof(DWORD); if (RegQueryValueExW(hkey, value, NULL, &type, (BYTE *)data, &size) || type != REG_DWORD || size != sizeof(DWORD)) { *data = 0; return ERROR_BAD_CONFIGURATION; } return ERROR_SUCCESS; } static inline LONG reg_load_ftlong(HKEY hkey, const WCHAR *value, FT_Long *data) { DWORD dw; LONG ret = reg_load_dword(hkey, value, &dw); *data = dw; return ret; } static inline LONG reg_load_ftshort(HKEY hkey, const WCHAR *value, FT_Short *data) { DWORD dw; LONG ret = reg_load_dword(hkey, value, &dw); *data = dw; return ret; } static inline LONG reg_save_dword(HKEY hkey, const WCHAR *value, DWORD data) { return RegSetValueExW(hkey, value, 0, REG_DWORD, (BYTE*)&data, sizeof(DWORD)); } static void load_face(HKEY hkey_face, WCHAR *face_name, Family *family, void *buffer, DWORD buffer_size) { DWORD needed, strike_index = 0; HKEY hkey_strike; /* If we have a File Name key then this is a real font, not just the parent key of a bunch of non-scalable strikes */ needed = buffer_size; if (RegQueryValueExW(hkey_face, face_file_name_value, NULL, NULL, buffer, &needed) == ERROR_SUCCESS) { Face *face; face = HeapAlloc(GetProcessHeap(), 0, sizeof(*face)); face->cached_enum_data = NULL; face->family = NULL; face->refcount = 1; face->file = strdupW( buffer ); face->StyleName = strdupW(face_name); needed = buffer_size; if(RegQueryValueExW(hkey_face, face_full_name_value, NULL, NULL, buffer, &needed) == ERROR_SUCCESS) face->FullName = strdupW( buffer ); else face->FullName = NULL; reg_load_ftlong(hkey_face, face_index_value, &face->face_index); reg_load_dword(hkey_face, face_ntmflags_value, &face->ntmFlags); reg_load_ftlong(hkey_face, face_version_value, &face->font_version); reg_load_dword(hkey_face, face_flags_value, &face->flags); needed = sizeof(face->fs); RegQueryValueExW(hkey_face, face_font_sig_value, NULL, NULL, (BYTE*)&face->fs, &needed); if(reg_load_ftshort(hkey_face, face_height_value, &face->size.height) != ERROR_SUCCESS) { face->scalable = TRUE; memset(&face->size, 0, sizeof(face->size)); } else { face->scalable = FALSE; reg_load_ftshort(hkey_face, face_width_value, &face->size.width); reg_load_ftlong(hkey_face, face_size_value, &face->size.size); reg_load_ftlong(hkey_face, face_x_ppem_value, &face->size.x_ppem); reg_load_ftlong(hkey_face, face_y_ppem_value, &face->size.y_ppem); reg_load_ftshort(hkey_face, face_internal_leading_value, &face->size.internal_leading); TRACE("Adding bitmap size h %d w %d size %ld x_ppem %ld y_ppem %ld\n", face->size.height, face->size.width, face->size.size >> 6, face->size.x_ppem >> 6, face->size.y_ppem >> 6); } 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 (insert_face_in_family_list(face, family)) TRACE("Added font %s %s\n", debugstr_w(family->FamilyName), debugstr_w(face->StyleName)); release_face( face ); } /* load bitmap strikes */ needed = buffer_size; while (!RegEnumKeyExW(hkey_face, strike_index++, buffer, &needed, NULL, NULL, NULL, NULL)) { if (!RegOpenKeyExW(hkey_face, buffer, 0, KEY_ALL_ACCESS, &hkey_strike)) { load_face(hkey_strike, face_name, family, buffer, buffer_size); RegCloseKey(hkey_strike); } needed = buffer_size; } } /* move vertical fonts after their horizontal counterpart */ /* assumes that font_list is already sorted by family name */ static void reorder_vertical_fonts(void) { Family *family, *next, *vert_family; struct list *ptr, *vptr; struct list vertical_families = LIST_INIT( vertical_families ); LIST_FOR_EACH_ENTRY_SAFE( family, next, &font_list, Family, entry ) { if (family->FamilyName[0] != '@') continue; list_remove( &family->entry ); list_add_tail( &vertical_families, &family->entry ); } ptr = list_head( &font_list ); vptr = list_head( &vertical_families ); while (ptr && vptr) { family = LIST_ENTRY( ptr, Family, entry ); vert_family = LIST_ENTRY( vptr, Family, entry ); if (strcmpiW( family->FamilyName, vert_family->FamilyName + 1 ) > 0) { list_remove( vptr ); list_add_before( ptr, vptr ); vptr = list_head( &vertical_families ); } else ptr = list_next( &font_list, ptr ); } list_move_tail( &font_list, &vertical_families ); } static void load_font_list_from_cache(HKEY hkey_font_cache) { DWORD size, family_index = 0; Family *family; HKEY hkey_family; WCHAR buffer[4096]; size = sizeof(buffer); while (!RegEnumKeyExW(hkey_font_cache, family_index++, buffer, &size, NULL, NULL, NULL, NULL)) { WCHAR *english_family = NULL; WCHAR *family_name = strdupW( buffer ); DWORD face_index = 0; RegOpenKeyExW(hkey_font_cache, family_name, 0, KEY_ALL_ACCESS, &hkey_family); TRACE("opened family key %s\n", debugstr_w(family_name)); size = sizeof(buffer); if (!RegQueryValueExW(hkey_family, english_name_value, NULL, NULL, (BYTE *)buffer, &size)) english_family = strdupW( buffer ); family = create_family(family_name, english_family); if(english_family) { FontSubst *subst = HeapAlloc(GetProcessHeap(), 0, sizeof(*subst)); subst->from.name = strdupW(english_family); subst->from.charset = -1; subst->to.name = strdupW(family_name); subst->to.charset = -1; add_font_subst(&font_subst_list, subst, 0); } size = sizeof(buffer); while (!RegEnumKeyExW(hkey_family, face_index++, buffer, &size, NULL, NULL, NULL, NULL)) { WCHAR *face_name = strdupW( buffer ); HKEY hkey_face; if (!RegOpenKeyExW(hkey_family, face_name, 0, KEY_ALL_ACCESS, &hkey_face)) { load_face(hkey_face, face_name, family, buffer, sizeof(buffer)); RegCloseKey(hkey_face); } HeapFree( GetProcessHeap(), 0, face_name ); size = sizeof(buffer); } RegCloseKey(hkey_family); release_family( family ); size = sizeof(buffer); } reorder_vertical_fonts(); } static LONG create_font_cache_key(HKEY *hkey, DWORD *disposition) { LONG ret; HKEY hkey_wine_fonts; /* We don't want to create the fonts key as volatile, so open this first */ ret = RegCreateKeyExW(HKEY_CURRENT_USER, wine_fonts_key, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hkey_wine_fonts, NULL); if(ret != ERROR_SUCCESS) { WARN("Can't create %s\n", debugstr_w(wine_fonts_key)); return ret; } ret = RegCreateKeyExW(hkey_wine_fonts, wine_fonts_cache_key, 0, NULL, REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, hkey, disposition); RegCloseKey(hkey_wine_fonts); return ret; } static void add_face_to_cache(Face *face) { HKEY hkey_family, hkey_face; WCHAR *face_key_name; RegCreateKeyExW(hkey_font_cache, face->family->FamilyName, 0, NULL, REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey_family, NULL); if(face->family->EnglishName) RegSetValueExW(hkey_family, english_name_value, 0, REG_SZ, (BYTE*)face->family->EnglishName, (strlenW(face->family->EnglishName) + 1) * sizeof(WCHAR)); if(face->scalable) face_key_name = face->StyleName; else { static const WCHAR fmtW[] = {'%','s','\\','%','d',0}; face_key_name = HeapAlloc(GetProcessHeap(), 0, (strlenW(face->StyleName) + 10) * sizeof(WCHAR)); sprintfW(face_key_name, fmtW, face->StyleName, face->size.y_ppem); } RegCreateKeyExW(hkey_family, face_key_name, 0, NULL, REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey_face, NULL); if(!face->scalable) HeapFree(GetProcessHeap(), 0, face_key_name); RegSetValueExW(hkey_face, face_file_name_value, 0, REG_SZ, (BYTE *)face->file, (strlenW(face->file) + 1) * sizeof(WCHAR)); if (face->FullName) RegSetValueExW(hkey_face, face_full_name_value, 0, REG_SZ, (BYTE*)face->FullName, (strlenW(face->FullName) + 1) * sizeof(WCHAR)); reg_save_dword(hkey_face, face_index_value, face->face_index); reg_save_dword(hkey_face, face_ntmflags_value, face->ntmFlags); reg_save_dword(hkey_face, face_version_value, face->font_version); if (face->flags) reg_save_dword(hkey_face, face_flags_value, face->flags); RegSetValueExW(hkey_face, face_font_sig_value, 0, REG_BINARY, (BYTE*)&face->fs, sizeof(face->fs)); if(!face->scalable) { reg_save_dword(hkey_face, face_height_value, face->size.height); reg_save_dword(hkey_face, face_width_value, face->size.width); reg_save_dword(hkey_face, face_size_value, face->size.size); reg_save_dword(hkey_face, face_x_ppem_value, face->size.x_ppem); reg_save_dword(hkey_face, face_y_ppem_value, face->size.y_ppem); reg_save_dword(hkey_face, face_internal_leading_value, face->size.internal_leading); } RegCloseKey(hkey_face); RegCloseKey(hkey_family); } static void remove_face_from_cache( Face *face ) { HKEY hkey_family; RegOpenKeyExW( hkey_font_cache, face->family->FamilyName, 0, KEY_ALL_ACCESS, &hkey_family ); if (face->scalable) { RegDeleteKeyW( hkey_family, face->StyleName ); } else { static const WCHAR fmtW[] = {'%','s','\\','%','d',0}; WCHAR *face_key_name = HeapAlloc(GetProcessHeap(), 0, (strlenW(face->StyleName) + 10) * sizeof(WCHAR)); sprintfW(face_key_name, fmtW, face->StyleName, face->size.y_ppem); RegDeleteKeyW( hkey_family, face_key_name ); HeapFree(GetProcessHeap(), 0, face_key_name); } RegCloseKey(hkey_family); } static WCHAR *prepend_at(WCHAR *family) { WCHAR *str; if (!family) return NULL; str = HeapAlloc(GetProcessHeap(), 0, sizeof (WCHAR) * (strlenW(family) + 2)); str[0] = '@'; strcpyW(str + 1, family); HeapFree(GetProcessHeap(), 0, family); return str; } static void get_family_names( FT_Face ft_face, WCHAR **name, WCHAR **english, BOOL vertical ) { *english = get_face_name( ft_face, TT_NAME_ID_FONT_FAMILY, MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT) ); if (!*english) *english = towstr( CP_ACP, ft_face->family_name ); *name = get_face_name( ft_face, TT_NAME_ID_FONT_FAMILY, GetSystemDefaultLCID() ); if (!*name) { *name = *english; *english = NULL; } else if (!strcmpiW( *name, *english )) { HeapFree( GetProcessHeap(), 0, *english ); *english = NULL; } if (vertical) { *name = prepend_at( *name ); *english = prepend_at( *english ); } } static Family *get_family( FT_Face ft_face, BOOL vertical ) { Family *family; WCHAR *name, *english_name; get_family_names( ft_face, &name, &english_name, vertical ); family = find_family_from_name( name ); if (!family) { family = create_family( name, english_name ); if (english_name) { FontSubst *subst = HeapAlloc( GetProcessHeap(), 0, sizeof(*subst) ); subst->from.name = strdupW( english_name ); subst->from.charset = -1; subst->to.name = strdupW( name ); subst->to.charset = -1; add_font_subst( &font_subst_list, subst, 0 ); } } else { HeapFree( GetProcessHeap(), 0, name ); HeapFree( GetProcessHeap(), 0, english_name ); family->refcount++; } return family; } static inline FT_Fixed get_font_version( FT_Face ft_face ) { FT_Fixed version = 0; TT_Header *header; header = pFT_Get_Sfnt_Table( ft_face, ft_sfnt_head ); if (header) version = header->Font_Revision; return version; } static inline DWORD get_ntm_flags( FT_Face ft_face ) { DWORD flags = 0; FT_ULong table_size = 0; FT_WinFNT_HeaderRec winfnt_header; if (ft_face->style_flags & FT_STYLE_FLAG_ITALIC) flags |= NTM_ITALIC; if (ft_face->style_flags & FT_STYLE_FLAG_BOLD) flags |= NTM_BOLD; /* fixup the flag for our fake-bold implementation. */ if (!FT_IS_SCALABLE( ft_face ) && !pFT_Get_WinFNT_Header( ft_face, &winfnt_header ) && winfnt_header.weight > FW_NORMAL ) flags |= NTM_BOLD; if (flags == 0) flags = NTM_REGULAR; if (!pFT_Load_Sfnt_Table( ft_face, FT_MAKE_TAG( 'C','F','F',' ' ), 0, NULL, &table_size )) flags |= NTM_PS_OPENTYPE; return flags; } static inline void get_bitmap_size( FT_Face ft_face, Bitmap_Size *face_size ) { My_FT_Bitmap_Size *size; FT_WinFNT_HeaderRec winfnt_header; size = (My_FT_Bitmap_Size *)ft_face->available_sizes; 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; if (!pFT_Get_WinFNT_Header( ft_face, &winfnt_header )) { face_size->internal_leading = winfnt_header.internal_leading; if (winfnt_header.external_leading > 0 && (face_size->height == winfnt_header.pixel_height + winfnt_header.external_leading)) face_size->height = winfnt_header.pixel_height; } } static inline void get_fontsig( FT_Face ft_face, FONTSIGNATURE *fs ) { TT_OS2 *os2; FT_UInt dummy; CHARSETINFO csi; FT_WinFNT_HeaderRec winfnt_header; int i; memset( fs, 0, sizeof(*fs) ); os2 = pFT_Get_Sfnt_Table( ft_face, ft_sfnt_os2 ); if (os2) { fs->fsUsb[0] = os2->ulUnicodeRange1; fs->fsUsb[1] = os2->ulUnicodeRange2; fs->fsUsb[2] = os2->ulUnicodeRange3; fs->fsUsb[3] = os2->ulUnicodeRange4; if (os2->version == 0) { if (pFT_Get_First_Char( ft_face, &dummy ) < 0x100) fs->fsCsb[0] = FS_LATIN1; else fs->fsCsb[0] = FS_SYMBOL; } else { fs->fsCsb[0] = os2->ulCodePageRange1; fs->fsCsb[1] = os2->ulCodePageRange2; } } else { if (!pFT_Get_WinFNT_Header( ft_face, &winfnt_header )) { 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; } } if (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: fs->fsCsb[0] |= FS_LATIN1; break; case FT_ENCODING_MS_SYMBOL: fs->fsCsb[0] |= FS_SYMBOL; break; default: break; } } } } static Face *create_face( FT_Face ft_face, FT_Long face_index, const char *file, void *font_data_ptr, DWORD font_data_size, DWORD flags ) { struct stat st; Face *face = HeapAlloc( GetProcessHeap(), 0, sizeof(*face) ); face->refcount = 1; face->StyleName = get_face_name( ft_face, TT_NAME_ID_FONT_SUBFAMILY, GetSystemDefaultLangID() ); if (!face->StyleName) face->StyleName = towstr( CP_ACP, ft_face->style_name ); face->FullName = get_face_name( ft_face, TT_NAME_ID_FULL_NAME, GetSystemDefaultLangID() ); if (flags & ADDFONT_VERTICAL_FONT) face->FullName = prepend_at( face->FullName ); face->dev = 0; face->ino = 0; if (file) { face->file = towstr( CP_UNIXCP, file ); face->font_data_ptr = NULL; face->font_data_size = 0; if (!stat( file, &st )) { face->dev = st.st_dev; face->ino = st.st_ino; } } else { face->file = NULL; face->font_data_ptr = font_data_ptr; face->font_data_size = font_data_size; } face->face_index = face_index; get_fontsig( ft_face, &face->fs ); face->ntmFlags = get_ntm_flags( ft_face ); face->font_version = get_font_version( ft_face ); if (FT_IS_SCALABLE( ft_face )) { memset( &face->size, 0, sizeof(face->size) ); face->scalable = TRUE; } else { get_bitmap_size( ft_face, &face->size ); face->scalable = FALSE; } if (!HIWORD( flags )) flags |= ADDFONT_AA_FLAGS( default_aa_flags ); face->flags = flags; face->family = NULL; face->cached_enum_data = NULL; 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]); return face; } static void AddFaceToList(FT_Face ft_face, const char *file, void *font_data_ptr, DWORD font_data_size, FT_Long face_index, DWORD flags ) { Face *face; Family *family; face = create_face( ft_face, face_index, file, font_data_ptr, font_data_size, flags ); family = get_family( ft_face, flags & ADDFONT_VERTICAL_FONT ); if (insert_face_in_family_list( face, family )) { if (flags & ADDFONT_ADD_TO_CACHE) add_face_to_cache( face ); TRACE("Added font %s %s\n", debugstr_w(family->FamilyName), debugstr_w(face->StyleName)); } release_face( face ); release_family( family ); } static FT_Face new_ft_face( const char *file, void *font_data_ptr, DWORD font_data_size, FT_Long face_index, BOOL allow_bitmap ) { FT_Error err; TT_OS2 *pOS2; FT_Face ft_face; 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 NULL; } /* 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); goto fail; } if (!FT_IS_SFNT( ft_face )) { if (FT_IS_SCALABLE( ft_face ) || !allow_bitmap ) { WARN("Ignoring font %s/%p\n", debugstr_a(file), font_data_ptr); goto fail; } } else { if (!(pOS2 = pFT_Get_Sfnt_Table( ft_face, ft_sfnt_os2 )) || !pFT_Get_Sfnt_Table( ft_face, ft_sfnt_hhea ) || !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); goto fail; } /* 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 (!pFT_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)); goto fail; } } } 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); goto fail; } return ft_face; fail: pFT_Done_Face( ft_face ); return NULL; } static INT AddFontToList(const char *file, void *font_data_ptr, DWORD font_data_size, DWORD flags) { FT_Face ft_face; FT_Long face_index = 0, num_faces; INT ret = 0; /* 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) { 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, flags); HeapFree(GetProcessHeap(), 0, *cursor); } HeapFree(GetProcessHeap(), 0, mac_list); if(had_one) return 1; } } #endif /* HAVE_CARBON_CARBON_H */ do { const DWORD FS_DBCS_MASK = FS_JISJAPAN|FS_CHINESESIMP|FS_WANSUNG|FS_CHINESETRAD|FS_JOHAB; FONTSIGNATURE fs; ft_face = new_ft_face( file, font_data_ptr, font_data_size, face_index, flags & ADDFONT_ALLOW_BITMAP ); if (!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; } AddFaceToList(ft_face, file, font_data_ptr, font_data_size, face_index, flags); ++ret; get_fontsig(ft_face, &fs); if (fs.fsCsb[0] & FS_DBCS_MASK) { AddFaceToList(ft_face, file, font_data_ptr, font_data_size, face_index, flags | ADDFONT_VERTICAL_FONT); ++ret; } num_faces = ft_face->num_faces; pFT_Done_Face(ft_face); } while(num_faces > ++face_index); return ret; } static int remove_font_resource( const char *file, DWORD flags ) { Family *family, *family_next; Face *face, *face_next; struct stat st; int count = 0; if (stat( file, &st ) == -1) return 0; LIST_FOR_EACH_ENTRY_SAFE( family, family_next, &font_list, Family, entry ) { family->refcount++; LIST_FOR_EACH_ENTRY_SAFE( face, face_next, &family->faces, Face, entry ) { if (!face->file) continue; if (LOWORD(face->flags) != LOWORD(flags)) continue; if (st.st_dev == face->dev && st.st_ino == face->ino) { TRACE( "removing matching face %s refcount %d\n", debugstr_w(face->file), face->refcount ); release_face( face ); count++; } } release_family( family ); } return count; } static void DumpFontList(void) { Family *family; Face *face; LIST_FOR_EACH_ENTRY( family, &font_list, Family, entry ) { TRACE("Family: %s\n", debugstr_w(family->FamilyName)); LIST_FOR_EACH_ENTRY( face, &family->faces, 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"); } } } static BOOL map_font_family(const WCHAR *orig, const WCHAR *repl) { Family *family = find_family_from_any_name(repl); if (family != NULL) { Family *new_family = HeapAlloc(GetProcessHeap(), 0, sizeof(*new_family)); if (new_family != NULL) { TRACE("mapping %s to %s\n", debugstr_w(repl), debugstr_w(orig)); new_family->FamilyName = strdupW(orig); new_family->EnglishName = NULL; list_init(&new_family->faces); new_family->replacement = &family->faces; list_add_tail(&font_list, &new_family->entry); return TRUE; } } TRACE("%s is not available. Skip this replacement.\n", debugstr_w(repl)); return FALSE; } /*********************************************************** * 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; /* @@ 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) { /* "NewName"="Oldname" */ if(!find_family_from_any_name(value)) { if (type == REG_MULTI_SZ) { WCHAR *replace = data; while(*replace) { if (map_font_family(value, replace)) break; replace += strlenW(replace) + 1; } } else if (type == REG_SZ) map_font_family(value, data); } else TRACE("%s is available. Skip this replacement.\n", debugstr_w(value)); /* reset dlen and vlen */ dlen = datalen; vlen = valuelen; } HeapFree(GetProcessHeap(), 0, data); HeapFree(GetProcessHeap(), 0, value); RegCloseKey(hkey); } } 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 SYSTEM_LINKS *find_font_link(const WCHAR *name) { SYSTEM_LINKS *font_link; LIST_FOR_EACH_ENTRY(font_link, &system_links, SYSTEM_LINKS, entry) { if(!strncmpiW(font_link->font_name, name, LF_FACESIZE - 1)) return font_link; } return NULL; } static void populate_system_links(const WCHAR *name, const WCHAR *const *values) { const WCHAR *value; int i; FontSubst *psub; Family *family; Face *face; const WCHAR *file; if (values) { SYSTEM_LINKS *font_link; psub = get_font_subst(&font_subst_list, name, -1); /* Don't store fonts that are only substitutes for other fonts */ if(psub) { TRACE("%s: Internal SystemLink entry for substituted font, ignoring\n", debugstr_w(name)); return; } font_link = find_font_link(name); if (font_link == NULL) { font_link = HeapAlloc(GetProcessHeap(), 0, sizeof(*font_link)); font_link->font_name = strdupW(name); list_init(&font_link->links); list_add_tail(&system_links, &font_link->entry); } memset(&font_link->fs, 0, sizeof font_link->fs); for (i = 0; values[i] != NULL; i++) { const struct list *face_list; CHILD_FONT *child_font; 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 */ face_list = get_face_list_from_family(family); LIST_FOR_EACH_ENTRY(face, face_list, Face, entry) { if (!face->file) continue; file = strrchrW(face->file, '/'); if (!file) file = face->file; else file++; break; } if (!file) continue; face = find_face_from_filename(file, value); if(!face) { TRACE("Unable to find file %s face name %s\n", debugstr_w(file), debugstr_w(value)); continue; } child_font = HeapAlloc(GetProcessHeap(), 0, sizeof(*child_font)); child_font->face = face; child_font->font = NULL; font_link->fs.fsCsb[0] |= face->fs.fsCsb[0]; font_link->fs.fsCsb[1] |= face->fs.fsCsb[1]; TRACE("Adding file %s index %ld\n", debugstr_w(child_font->face->file), child_font->face->face_index); list_add_tail(&font_link->links, &child_font->entry); TRACE("added internal SystemLink for %s to %s in %s\n", debugstr_w(name), debugstr_w(value),debugstr_w(file)); } } } /************************************************************* * 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}; static const WCHAR MS_Shell_Dlg[] = {'M','S',' ','S','h','e','l','l',' ','D','l','g',0}; Face *face; FontSubst *psub; UINT i, j; 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) { 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); memset(&font_link->fs, 0, sizeof font_link->fs); 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; font_link->fs.fsCsb[0] |= face->fs.fsCsb[0]; font_link->fs.fsCsb[1] |= face->fs.fsCsb[1]; TRACE("Adding file %s index %ld\n", debugstr_w(child_font->face->file), child_font->face->face_index); list_add_tail(&font_link->links, &child_font->entry); } 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); } psub = get_font_subst(&font_subst_list, MS_Shell_Dlg, -1); if (!psub) { WARN("could not find FontSubstitute for MS Shell Dlg\n"); goto skip_internal; } for (i = 0; i < ARRAY_SIZE(font_links_defaults_list); i++) { const FontSubst *psub2; psub2 = get_font_subst(&font_subst_list, font_links_defaults_list[i].shelldlg, -1); if ((!strcmpiW(font_links_defaults_list[i].shelldlg, psub->to.name) || (psub2 && !strcmpiW(psub2->to.name,psub->to.name)))) { for (j = 0; j < ARRAY_SIZE(font_links_list); j++) populate_system_links(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(psub->to.name, font_links_defaults_list[i].substitutes); } else if (strcmpiW(psub->to.name, font_links_defaults_list[i].substitutes[0])) { populate_system_links(font_links_defaults_list[i].substitutes[0], NULL); } } skip_internal: /* 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); memset(&system_font_link->fs, 0, sizeof system_font_link->fs); 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; system_font_link->fs.fsCsb[0] |= face->fs.fsCsb[0]; system_font_link->fs.fsCsb[1] |= face->fs.fsCsb[1]; TRACE("Found Tahoma in %s index %ld\n", debugstr_w(child_font->face->file), child_font->face->face_index); list_add_tail(&system_font_link->links, &child_font->entry); } font_link = find_font_link(Tahoma); if (font_link != NULL) { 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; new_child->face->refcount++; system_font_link->fs.fsCsb[0] |= font_link_entry->face->fs.fsCsb[0]; system_font_link->fs.fsCsb[1] |= font_link_entry->face->fs.fsCsb[1]; list_add_tail(&system_font_link->links, &new_child->entry); } } 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 { DWORD addfont_flags = ADDFONT_ADD_TO_CACHE; if(external_fonts) addfont_flags |= ADDFONT_EXTERNAL_FONT; AddFontToList(path, NULL, 0, addfont_flags); } } closedir(dir); return TRUE; } #ifdef SONAME_LIBFONTCONFIG static BOOL fontconfig_enabled; static UINT parse_aa_pattern( FcPattern *pattern ) { FcBool antialias; int rgba; UINT aa_flags = 0; if (pFcPatternGetBool( pattern, FC_ANTIALIAS, 0, &antialias ) == FcResultMatch) aa_flags = antialias ? GGO_GRAY4_BITMAP : GGO_BITMAP; if (pFcPatternGetInteger( pattern, FC_RGBA, 0, &rgba ) == FcResultMatch) { switch (rgba) { case FC_RGBA_RGB: aa_flags = WINE_GGO_HRGB_BITMAP; break; case FC_RGBA_BGR: aa_flags = WINE_GGO_HBGR_BITMAP; break; case FC_RGBA_VRGB: aa_flags = WINE_GGO_VRGB_BITMAP; break; case FC_RGBA_VBGR: aa_flags = WINE_GGO_VBGR_BITMAP; break; case FC_RGBA_NONE: aa_flags = aa_flags ? aa_flags : GGO_GRAY4_BITMAP; break; } } return aa_flags; } static void init_fontconfig(void) { void *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); return;} LOAD_FUNCPTR(FcConfigSubstitute); LOAD_FUNCPTR(FcFontList); LOAD_FUNCPTR(FcFontSetDestroy); LOAD_FUNCPTR(FcInit); LOAD_FUNCPTR(FcPatternCreate); LOAD_FUNCPTR(FcPatternDestroy); LOAD_FUNCPTR(FcPatternGetBool); LOAD_FUNCPTR(FcPatternGetInteger); LOAD_FUNCPTR(FcPatternGetString); #undef LOAD_FUNCPTR if (pFcInit()) { FcPattern *pattern = pFcPatternCreate(); pFcConfigSubstitute( NULL, pattern, FcMatchFont ); default_aa_flags = parse_aa_pattern( pattern ); pFcPatternDestroy( pattern ); if (!default_aa_flags) { FcPattern *pattern = pFcPatternCreate(); pFcConfigSubstitute( NULL, pattern, FcMatchPattern ); default_aa_flags = parse_aa_pattern( pattern ); pFcPatternDestroy( pattern ); } TRACE( "enabled, default flags = %x\n", default_aa_flags ); fontconfig_enabled = TRUE; } } static void load_fontconfig_fonts(void) { FcPattern *pat; FcFontSet *fontset; int i, len; char *file; const char *ext; if (!fontconfig_enabled) return; pat = pFcPatternCreate(); if (!pat) return; fontset = pFcFontList(NULL, pat, NULL); if (!fontset) { pFcPatternDestroy(pat); return; } for(i = 0; i < fontset->nfont; i++) { FcBool scalable; DWORD aa_flags; if(pFcPatternGetString(fontset->fonts[i], FC_FILE, 0, (FcChar8**)&file) != FcResultMatch) continue; pFcConfigSubstitute( NULL, fontset->fonts[i], FcMatchFont ); /* 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; } aa_flags = parse_aa_pattern( fontset->fonts[i] ); TRACE("fontconfig: %s aa %x\n", file, aa_flags); len = strlen( file ); if(len < 4) continue; ext = &file[ len - 3 ]; if(strcasecmp(ext, "pfa") && strcasecmp(ext, "pfb")) AddFontToList(file, NULL, 0, ADDFONT_EXTERNAL_FONT | ADDFONT_ADD_TO_CACHE | ADDFONT_AA_FLAGS(aa_flags) ); } pFcFontSetDestroy(fontset); pFcPatternDestroy(pat); } #elif defined(HAVE_CARBON_CARBON_H) static void load_mac_font_callback(const void *value, void *context) { CFStringRef pathStr = value; CFIndex len; char* path; len = CFStringGetMaximumSizeOfFileSystemRepresentation(pathStr); path = HeapAlloc(GetProcessHeap(), 0, len); if (path && CFStringGetFileSystemRepresentation(pathStr, path, len)) { TRACE("font file %s\n", path); AddFontToList(path, NULL, 0, ADDFONT_EXTERNAL_FONT | ADDFONT_ADD_TO_CACHE); } HeapFree(GetProcessHeap(), 0, path); } static void load_mac_fonts(void) { CFStringRef removeDupesKey; CFBooleanRef removeDupesValue; CFDictionaryRef options; CTFontCollectionRef col; CFArrayRef descs; CFMutableSetRef paths; CFIndex i; removeDupesKey = kCTFontCollectionRemoveDuplicatesOption; removeDupesValue = kCFBooleanTrue; options = CFDictionaryCreate(NULL, (const void**)&removeDupesKey, (const void**)&removeDupesValue, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); col = CTFontCollectionCreateFromAvailableFonts(options); if (options) CFRelease(options); if (!col) { WARN("CTFontCollectionCreateFromAvailableFonts failed\n"); return; } descs = CTFontCollectionCreateMatchingFontDescriptors(col); CFRelease(col); if (!descs) { WARN("CTFontCollectionCreateMatchingFontDescriptors failed\n"); return; } paths = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks); if (!paths) { WARN("CFSetCreateMutable failed\n"); CFRelease(descs); return; } for (i = 0; i < CFArrayGetCount(descs); i++) { CTFontDescriptorRef desc; CFURLRef url; CFStringRef ext; CFStringRef path; desc = CFArrayGetValueAtIndex(descs, i); #if defined(MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 url = CTFontDescriptorCopyAttribute(desc, kCTFontURLAttribute); #else /* CTFontDescriptor doesn't support kCTFontURLAttribute prior to 10.6, so we have to go CFFontDescriptor -> CTFont -> ATSFont -> FSRef -> CFURL. */ { CTFontRef font; ATSFontRef atsFont; OSStatus status; FSRef fsref; font = CTFontCreateWithFontDescriptor(desc, 0, NULL); if (!font) continue; atsFont = CTFontGetPlatformFont(font, NULL); if (!atsFont) { CFRelease(font); continue; } status = ATSFontGetFileReference(atsFont, &fsref); CFRelease(font); if (status != noErr) continue; url = CFURLCreateFromFSRef(NULL, &fsref); } #endif if (!url) continue; ext = CFURLCopyPathExtension(url); if (ext) { BOOL skip = (CFStringCompare(ext, CFSTR("pfa"), kCFCompareCaseInsensitive) == kCFCompareEqualTo || CFStringCompare(ext, CFSTR("pfb"), kCFCompareCaseInsensitive) == kCFCompareEqualTo); CFRelease(ext); if (skip) { CFRelease(url); continue; } } path = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle); CFRelease(url); if (!path) continue; CFSetAddValue(paths, path); CFRelease(path); } CFRelease(descs); CFSetApplyFunction(paths, load_mac_font_callback, NULL); CFRelease(paths); } #endif static char *get_font_dir(void) { const char *build_dir, *data_dir; char *name = NULL; if ((data_dir = wine_get_data_dir())) { if (!(name = HeapAlloc( GetProcessHeap(), 0, strlen(data_dir) + 1 + sizeof(WINE_FONT_DIR) ))) return NULL; strcpy( name, data_dir ); strcat( name, "/" ); strcat( name, WINE_FONT_DIR ); } else if ((build_dir = wine_get_build_dir())) { if (!(name = HeapAlloc( GetProcessHeap(), 0, strlen(build_dir) + sizeof("/fonts") ))) return NULL; strcpy( name, build_dir ); strcat( name, "/fonts" ); } return name; } static char *get_data_dir_path( LPCWSTR file ) { char *unix_name = NULL; char *font_dir = get_font_dir(); if (font_dir) { INT len = WideCharToMultiByte(CP_UNIXCP, 0, file, -1, NULL, 0, NULL, NULL); unix_name = HeapAlloc(GetProcessHeap(), 0, strlen(font_dir) + len + 1 ); strcpy(unix_name, font_dir); strcat(unix_name, "/"); WideCharToMultiByte(CP_UNIXCP, 0, file, -1, unix_name + strlen(unix_name), len, NULL, NULL); HeapFree( GetProcessHeap(), 0, font_dir ); } return unix_name; } static BOOL load_font_from_data_dir(LPCWSTR file) { BOOL ret = FALSE; char *unix_name = get_data_dir_path( file ); if (unix_name) { EnterCriticalSection( &freetype_cs ); ret = AddFontToList(unix_name, NULL, 0, ADDFONT_ALLOW_BITMAP | ADDFONT_ADD_TO_CACHE); LeaveCriticalSection( &freetype_cs ); HeapFree(GetProcessHeap(), 0, unix_name); } return ret; } static char *get_winfonts_dir_path(LPCWSTR file) { static const WCHAR slashW[] = {'\\','\0'}; WCHAR windowsdir[MAX_PATH]; GetWindowsDirectoryW(windowsdir, ARRAY_SIZE(windowsdir)); strcatW(windowsdir, fontsW); strcatW(windowsdir, slashW); strcatW(windowsdir, file); return wine_get_unix_file_name( windowsdir ); } 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, ARRAY_SIZE(windowsdir)); 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 = AddFontToList(unixname, NULL, 0, ADDFONT_ALLOW_BITMAP | ADDFONT_ADD_TO_CACHE); 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; Family *family; Face *face; WCHAR *file, *path; static const WCHAR TrueType[] = {' ','(','T','r','u','e','T','y','p','e',')','\0'}; 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_ENTRY( family, &font_list, Family, entry ) { LIST_FOR_EACH_ENTRY( face, &family->faces, Face, entry ) { char *buffer; WCHAR *name; if (!(face->flags & ADDFONT_EXTERNAL_FONT)) continue; name = face->FullName ? face->FullName : family->FamilyName; len = strlenW(name) + 1; if (face->scalable) len += ARRAY_SIZE(TrueType); valueW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); strcpyW(valueW, name); if (face->scalable) strcatW(valueW, TrueType); buffer = strWtoA( CP_UNIXCP, face->file ); path = wine_get_dos_file_name( buffer ); HeapFree( GetProcessHeap(), 0, buffer ); if (path) file = path; else if ((file = strrchrW(face->file, '/'))) file++; else file = face->file; len = strlenW(file) + 1; 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, path); HeapFree(GetProcessHeap(), 0, valueW); } } end: if(external_key) RegCloseKey(external_key); if(win9x_key) RegCloseKey(win9x_key); if(winnt_key) RegCloseKey(winnt_key); } static void delete_external_font_keys(void) { HKEY winnt_key = 0, win9x_key = 0, external_key = 0; DWORD dlen, plen, vlen, datalen, valuelen, i, type, path_type; LPWSTR valueW; LPVOID data; BYTE *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(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); path = HeapAlloc(GetProcessHeap(), 0, datalen); dlen = datalen; vlen = valuelen; i = 0; while(RegEnumValueW(external_key, i++, valueW, &vlen, NULL, &type, data, &dlen) == ERROR_SUCCESS) { plen = dlen; if (RegQueryValueExW(winnt_key, valueW, 0, &path_type, path, &plen) == ERROR_SUCCESS && type == path_type && dlen == plen && !memcmp(data, path, plen)) RegDeleteValueW(winnt_key, valueW); plen = dlen; if (RegQueryValueExW(win9x_key, valueW, 0, &path_type, path, &plen) == ERROR_SUCCESS && type == path_type && dlen == plen && !memcmp(data, path, plen)) RegDeleteValueW(win9x_key, valueW); /* reset dlen and vlen */ dlen = datalen; vlen = valuelen; } HeapFree(GetProcessHeap(), 0, path); 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; EnterCriticalSection( &freetype_cs ); if((unixname = wine_get_unix_file_name(file))) { DWORD addfont_flags = ADDFONT_ALLOW_BITMAP | ADDFONT_ADD_RESOURCE; if(!(flags & FR_PRIVATE)) addfont_flags |= ADDFONT_ADD_TO_CACHE; ret = AddFontToList(unixname, NULL, 0, addfont_flags); HeapFree(GetProcessHeap(), 0, unixname); } if (!ret && !strchrW(file, '\\')) { /* Try in %WINDIR%/fonts, needed for Fotobuch Designer */ if ((unixname = get_winfonts_dir_path( file ))) { ret = AddFontToList(unixname, NULL, 0, ADDFONT_ALLOW_BITMAP | ADDFONT_ADD_RESOURCE); HeapFree(GetProcessHeap(), 0, unixname); } /* Try in datadir/fonts (or builddir/fonts), needed for Magic the Gathering Online */ if (!ret && (unixname = get_data_dir_path( file ))) { ret = AddFontToList(unixname, NULL, 0, ADDFONT_ALLOW_BITMAP | ADDFONT_ADD_RESOURCE); HeapFree(GetProcessHeap(), 0, unixname); } } LeaveCriticalSection( &freetype_cs ); } 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, ADDFONT_ALLOW_BITMAP | ADDFONT_ADD_RESOURCE); LeaveCriticalSection( &freetype_cs ); if (*pcFonts == 0) { TRACE("AddFontToList failed\n"); HeapFree(GetProcessHeap(), 0, pFontCopy); return 0; } /* 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) { INT ret = 0; GDI_CheckNotLock(); if (ft_handle) /* do it only if we have freetype up and running */ { char *unixname; EnterCriticalSection( &freetype_cs ); if ((unixname = wine_get_unix_file_name(file))) { DWORD addfont_flags = ADDFONT_ALLOW_BITMAP | ADDFONT_ADD_RESOURCE; if(!(flags & FR_PRIVATE)) addfont_flags |= ADDFONT_ADD_TO_CACHE; ret = remove_font_resource( unixname, addfont_flags ); HeapFree(GetProcessHeap(), 0, unixname); } if (!ret && !strchrW(file, '\\')) { if ((unixname = get_winfonts_dir_path( file ))) { ret = remove_font_resource( unixname, ADDFONT_ALLOW_BITMAP | ADDFONT_ADD_RESOURCE ); HeapFree(GetProcessHeap(), 0, unixname); } if (!ret && (unixname = get_data_dir_path( file ))) { ret = remove_font_resource( unixname, ADDFONT_ALLOW_BITMAP | ADDFONT_ADD_RESOURCE ); HeapFree(GetProcessHeap(), 0, unixname); } } LeaveCriticalSection( &freetype_cs ); } return ret; } static char *get_ttf_file_name( LPCWSTR font_file, LPCWSTR font_path ) { WCHAR *fullname; char *unix_name; int file_len; if (!font_file) return NULL; file_len = strlenW( font_file ); if (font_path && font_path[0]) { int path_len = strlenW( font_path ); fullname = HeapAlloc( GetProcessHeap(), 0, (file_len + path_len + 2) * sizeof(WCHAR) ); if (!fullname) return NULL; memcpy( fullname, font_path, path_len * sizeof(WCHAR) ); fullname[path_len] = '\\'; memcpy( fullname + path_len + 1, font_file, (file_len + 1) * sizeof(WCHAR) ); } else { int len = GetFullPathNameW( font_file, 0, NULL, NULL ); if (!len) return NULL; fullname = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ); if (!fullname) return NULL; GetFullPathNameW( font_file, len, fullname, NULL ); } unix_name = wine_get_unix_file_name( fullname ); HeapFree( GetProcessHeap(), 0, fullname ); return unix_name; } #include struct fontdir { WORD num_of_resources; WORD res_id; WORD dfVersion; DWORD dfSize; CHAR dfCopyright[60]; WORD dfType; WORD dfPoints; WORD dfVertRes; WORD dfHorizRes; WORD dfAscent; WORD dfInternalLeading; WORD dfExternalLeading; BYTE dfItalic; BYTE dfUnderline; BYTE dfStrikeOut; WORD dfWeight; BYTE dfCharSet; WORD dfPixWidth; WORD dfPixHeight; BYTE dfPitchAndFamily; WORD dfAvgWidth; WORD dfMaxWidth; BYTE dfFirstChar; BYTE dfLastChar; BYTE dfDefaultChar; BYTE dfBreakChar; WORD dfWidthBytes; DWORD dfDevice; DWORD dfFace; DWORD dfReserved; CHAR szFaceName[LF_FACESIZE]; }; #include static void GetEnumStructs(Face *face, const WCHAR *family_name, LPENUMLOGFONTEXW pelf, NEWTEXTMETRICEXW *pntm, LPDWORD ptype); static BOOL get_fontdir( const char *unix_name, struct fontdir *fd ) { FT_Face ft_face = new_ft_face( unix_name, NULL, 0, 0, FALSE ); Face *face; WCHAR *name, *english_name; ENUMLOGFONTEXW elf; NEWTEXTMETRICEXW ntm; DWORD type; if (!ft_face) return FALSE; face = create_face( ft_face, 0, unix_name, NULL, 0, 0 ); get_family_names( ft_face, &name, &english_name, FALSE ); pFT_Done_Face( ft_face ); GetEnumStructs( face, name, &elf, &ntm, &type ); release_face( face ); HeapFree( GetProcessHeap(), 0, name ); HeapFree( GetProcessHeap(), 0, english_name ); if ((type & TRUETYPE_FONTTYPE) == 0) return FALSE; memset( fd, 0, sizeof(*fd) ); fd->num_of_resources = 1; fd->res_id = 0; fd->dfVersion = 0x200; fd->dfSize = sizeof(*fd); strcpy( fd->dfCopyright, "Wine fontdir" ); fd->dfType = 0x4003; /* 0x0080 set if private */ fd->dfPoints = ntm.ntmTm.ntmSizeEM; fd->dfVertRes = 72; fd->dfHorizRes = 72; fd->dfAscent = ntm.ntmTm.tmAscent; fd->dfInternalLeading = ntm.ntmTm.tmInternalLeading; fd->dfExternalLeading = ntm.ntmTm.tmExternalLeading; fd->dfItalic = ntm.ntmTm.tmItalic; fd->dfUnderline = ntm.ntmTm.tmUnderlined; fd->dfStrikeOut = ntm.ntmTm.tmStruckOut; fd->dfWeight = ntm.ntmTm.tmWeight; fd->dfCharSet = ntm.ntmTm.tmCharSet; fd->dfPixWidth = 0; fd->dfPixHeight = ntm.ntmTm.tmHeight; fd->dfPitchAndFamily = ntm.ntmTm.tmPitchAndFamily; fd->dfAvgWidth = ntm.ntmTm.tmAveCharWidth; fd->dfMaxWidth = ntm.ntmTm.tmMaxCharWidth; fd->dfFirstChar = ntm.ntmTm.tmFirstChar; fd->dfLastChar = ntm.ntmTm.tmLastChar; fd->dfDefaultChar = ntm.ntmTm.tmDefaultChar; fd->dfBreakChar = ntm.ntmTm.tmBreakChar; fd->dfWidthBytes = 0; fd->dfDevice = 0; fd->dfFace = FIELD_OFFSET( struct fontdir, szFaceName ); fd->dfReserved = 0; WideCharToMultiByte( CP_ACP, 0, elf.elfLogFont.lfFaceName, -1, fd->szFaceName, LF_FACESIZE, NULL, NULL ); return TRUE; } #define NE_FFLAGS_LIBMODULE 0x8000 #define NE_OSFLAGS_WINDOWS 0x02 static const char dos_string[0x40] = "This is a TrueType resource file"; static const char FONTRES[] = {'F','O','N','T','R','E','S',':'}; #include struct ne_typeinfo { WORD type_id; WORD count; DWORD res; }; struct ne_nameinfo { WORD off; WORD len; WORD flags; WORD id; DWORD res; }; struct rsrc_tab { WORD align; struct ne_typeinfo fontdir_type; struct ne_nameinfo fontdir_name; struct ne_typeinfo scalable_type; struct ne_nameinfo scalable_name; WORD end_of_rsrc; BYTE fontdir_res_name[8]; }; #include static BOOL create_fot( const WCHAR *resource, const WCHAR *font_file, const struct fontdir *fontdir ) { BOOL ret = FALSE; HANDLE file; DWORD size, written; BYTE *ptr, *start; BYTE import_name_len, res_name_len, non_res_name_len, font_file_len; char *font_fileA, *last_part, *ext; IMAGE_DOS_HEADER dos; IMAGE_OS2_HEADER ne = { IMAGE_OS2_SIGNATURE, 5, 1, 0, 0, 0, NE_FFLAGS_LIBMODULE, 0, 0, 0, 0, 0, 0, 0, 0, sizeof(ne), sizeof(ne), 0, 0, 0, 0, 0, 4, 2, NE_OSFLAGS_WINDOWS, 0, 0, 0, 0, 0x300 }; struct rsrc_tab rsrc_tab = { 4, { 0x8007, 1, 0 }, { 0, 0, 0x0c50, 0x2c, 0 }, { 0x80cc, 1, 0 }, { 0, 0, 0x0c50, 0x8001, 0 }, 0, { 7,'F','O','N','T','D','I','R'} }; memset( &dos, 0, sizeof(dos) ); dos.e_magic = IMAGE_DOS_SIGNATURE; dos.e_lfanew = sizeof(dos) + sizeof(dos_string); /* import name is last part\0, resident name is last part without extension non-resident name is "FONTRES:" + lfFaceName */ font_file_len = WideCharToMultiByte( CP_ACP, 0, font_file, -1, NULL, 0, NULL, NULL ); font_fileA = HeapAlloc( GetProcessHeap(), 0, font_file_len ); WideCharToMultiByte( CP_ACP, 0, font_file, -1, font_fileA, font_file_len, NULL, NULL ); last_part = strrchr( font_fileA, '\\' ); if (last_part) last_part++; else last_part = font_fileA; import_name_len = strlen( last_part ) + 1; ext = strchr( last_part, '.' ); if (ext) res_name_len = ext - last_part; else res_name_len = import_name_len - 1; non_res_name_len = sizeof( FONTRES ) + strlen( fontdir->szFaceName ); ne.ne_cbnrestab = 1 + non_res_name_len + 2 + 1; /* len + string + (WORD) ord_num + 1 byte eod */ ne.ne_restab = ne.ne_rsrctab + sizeof(rsrc_tab); ne.ne_modtab = ne.ne_imptab = ne.ne_restab + 1 + res_name_len + 2 + 3; /* len + string + (WORD) ord_num + 3 bytes eod */ ne.ne_enttab = ne.ne_imptab + 1 + import_name_len; /* len + string */ ne.ne_cbenttab = 2; ne.ne_nrestab = ne.ne_enttab + ne.ne_cbenttab + 2 + dos.e_lfanew; /* there are 2 bytes of 0 after entry tab */ rsrc_tab.scalable_name.off = (ne.ne_nrestab + ne.ne_cbnrestab + 0xf) >> 4; rsrc_tab.scalable_name.len = (font_file_len + 0xf) >> 4; rsrc_tab.fontdir_name.off = rsrc_tab.scalable_name.off + rsrc_tab.scalable_name.len; rsrc_tab.fontdir_name.len = (fontdir->dfSize + 0xf) >> 4; size = (rsrc_tab.fontdir_name.off + rsrc_tab.fontdir_name.len) << 4; start = ptr = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, size ); if (!ptr) { HeapFree( GetProcessHeap(), 0, font_fileA ); return FALSE; } memcpy( ptr, &dos, sizeof(dos) ); memcpy( ptr + sizeof(dos), dos_string, sizeof(dos_string) ); memcpy( ptr + dos.e_lfanew, &ne, sizeof(ne) ); ptr = start + dos.e_lfanew + ne.ne_rsrctab; memcpy( ptr, &rsrc_tab, sizeof(rsrc_tab) ); ptr = start + dos.e_lfanew + ne.ne_restab; *ptr++ = res_name_len; memcpy( ptr, last_part, res_name_len ); ptr = start + dos.e_lfanew + ne.ne_imptab; *ptr++ = import_name_len; memcpy( ptr, last_part, import_name_len ); ptr = start + ne.ne_nrestab; *ptr++ = non_res_name_len; memcpy( ptr, FONTRES, sizeof(FONTRES) ); memcpy( ptr + sizeof(FONTRES), fontdir->szFaceName, strlen( fontdir->szFaceName ) ); ptr = start + (rsrc_tab.scalable_name.off << 4); memcpy( ptr, font_fileA, font_file_len ); ptr = start + (rsrc_tab.fontdir_name.off << 4); memcpy( ptr, fontdir, fontdir->dfSize ); file = CreateFileW( resource, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL ); if (file != INVALID_HANDLE_VALUE) { if (WriteFile( file, start, size, &written, NULL ) && written == size) ret = TRUE; CloseHandle( file ); } HeapFree( GetProcessHeap(), 0, start ); HeapFree( GetProcessHeap(), 0, font_fileA ); return ret; } /************************************************************* * WineEngCreateScalableFontResource * */ BOOL WineEngCreateScalableFontResource( DWORD hidden, LPCWSTR resource, LPCWSTR font_file, LPCWSTR font_path ) { char *unix_name = get_ttf_file_name( font_file, font_path ); struct fontdir fontdir; BOOL ret = FALSE; if (!unix_name || !get_fontdir( unix_name, &fontdir )) SetLastError( ERROR_INVALID_PARAMETER ); else { if (hidden) fontdir.dfType |= 0x80; ret = create_fot( resource, font_file, &fontdir ); } HeapFree( GetProcessHeap(), 0, unix_name ); return ret; } static const struct nls_update_font_list { UINT ansi_cp, oem_cp; const char *oem, *fixed, *system; const char *courier, *serif, *small, *sserif_96, *sserif_120; /* 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", "sseriff.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", "sseriff.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", "sseriffe.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", "sseriffr.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", "sseriffg.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", "sserifft.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", "ssef1255.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", "ssef1256.fon", "Microsoft Sans Serif","Times New Roman", "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", "ssef1257.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", "sseriff.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", "ssef874.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", "sseriff.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", "sseriff.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", "sseriff.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", "sseriff.fon", "PMingLiU", "MingLiU", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, { 0 }, { 0 }, { 0 } } }; 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, int dpi) { const char *sserif = (dpi <= 108) ? fl->sserif_96 : fl->sserif_120; 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 *)sserif, strlen(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_association_info(UINT current_ansi_codepage) { static const char *font_assoc_reg_key = "System\\CurrentControlSet\\Control\\FontAssoc"; static const char *assoc_charset_subkey = "Associated Charset"; if (is_dbcs_ansi_cp(current_ansi_codepage)) { HKEY hkey; if (RegCreateKeyA(HKEY_LOCAL_MACHINE, font_assoc_reg_key, &hkey) == ERROR_SUCCESS) { HKEY hsubkey; if (RegCreateKeyA(hkey, assoc_charset_subkey, &hsubkey) == ERROR_SUCCESS) { switch (current_ansi_codepage) { case 932: set_value_key(hsubkey, "ANSI(00)", "NO"); set_value_key(hsubkey, "OEM(FF)", "NO"); set_value_key(hsubkey, "SYMBOL(02)", "NO"); break; case 936: case 949: case 950: set_value_key(hsubkey, "ANSI(00)", "YES"); set_value_key(hsubkey, "OEM(FF)", "YES"); set_value_key(hsubkey, "SYMBOL(02)", "NO"); break; } RegCloseKey(hsubkey); } /* TODO: Associated DefaultFonts */ RegCloseKey(hkey); } } else RegDeleteTreeA(HKEY_LOCAL_MACHINE, font_assoc_reg_key); } static void set_multi_value_key(HKEY hkey, const WCHAR *name, const WCHAR *value, DWORD len) { if (value) RegSetValueExW(hkey, name, 0, REG_MULTI_SZ, (const BYTE *)value, len); else if (name) RegDeleteValueW(hkey, name); } static void update_font_system_link_info(UINT current_ansi_codepage) { static const WCHAR system_link_simplified_chinese[] = {'S','I','M','S','U','N','.','T','T','C',',','S','i','m','S','u','n','\0', 'M','I','N','G','L','I','U','.','T','T','C',',','P','M','i','n','g','L','i','u','\0', 'M','S','G','O','T','H','I','C','.','T','T','C',',','M','S',' ','U','I',' ','G','o','t','h','i','c','\0', 'B','A','T','A','N','G','.','T','T','C',',','B','a','t','a','n','g','\0', '\0'}; static const WCHAR system_link_traditional_chinese[] = {'M','I','N','G','L','I','U','.','T','T','C',',','P','M','i','n','g','L','i','u','\0', 'S','I','M','S','U','N','.','T','T','C',',','S','i','m','S','u','n','\0', 'M','S','G','O','T','H','I','C','.','T','T','C',',','M','S',' ','U','I',' ','G','o','t','h','i','c','\0', 'B','A','T','A','N','G','.','T','T','C',',','B','a','t','a','n','g','\0', '\0'}; static const WCHAR system_link_japanese[] = {'M','S','G','O','T','H','I','C','.','T','T','C',',','M','S',' ','U','I',' ','G','o','t','h','i','c','\0', 'M','I','N','G','L','I','U','.','T','T','C',',','P','M','i','n','g','L','i','U','\0', 'S','I','M','S','U','N','.','T','T','C',',','S','i','m','S','u','n','\0', 'G','U','L','I','M','.','T','T','C',',','G','u','l','i','m','\0', '\0'}; static const WCHAR system_link_korean[] = {'G','U','L','I','M','.','T','T','C',',','G','u','l','i','m','\0', 'M','S','G','O','T','H','I','C','.','T','T','C',',','M','S',' ','U','I',' ','G','o','t','h','i','c','\0', 'M','I','N','G','L','I','U','.','T','T','C',',','P','M','i','n','g','L','i','U','\0', 'S','I','M','S','U','N','.','T','T','C',',','S','i','m','S','u','n','\0', '\0'}; static const WCHAR system_link_non_cjk[] = {'M','S','G','O','T','H','I','C','.','T','T','C',',','M','S',' ','U','I',' ','G','o','t','h','i','c','\0', 'M','I','N','G','L','I','U','.','T','T','C',',','P','M','i','n','g','L','i','U','\0', 'S','I','M','S','U','N','.','T','T','C',',','S','i','m','S','u','n','\0', 'G','U','L','I','M','.','T','T','C',',','G','u','l','i','m','\0', '\0'}; HKEY hkey; if (RegCreateKeyW(HKEY_LOCAL_MACHINE, system_link, &hkey) == ERROR_SUCCESS) { const WCHAR *link; DWORD len; switch (current_ansi_codepage) { case 932: link = system_link_japanese; len = sizeof(system_link_japanese); break; case 936: link = system_link_simplified_chinese; len = sizeof(system_link_simplified_chinese); break; case 949: link = system_link_korean; len = sizeof(system_link_korean); break; case 950: link = system_link_traditional_chinese; len = sizeof(system_link_traditional_chinese); break; default: link = system_link_non_cjk; len = sizeof(system_link_non_cjk); } set_multi_value_key(hkey, Lucida_Sans_Unicode, link, len); set_multi_value_key(hkey, Microsoft_Sans_Serif, link, len); set_multi_value_key(hkey, Tahoma, link, len); RegCloseKey(hkey); } } static void update_font_info(void) { static const WCHAR logpixels[] = { 'L','o','g','P','i','x','e','l','s',0 }; char buf[40], cpbuf[40]; DWORD len, type; HKEY hkey = 0; UINT i, ansi_cp = 0, oem_cp = 0; DWORD screen_dpi, font_dpi = 0; BOOL done = FALSE; screen_dpi = get_dpi(); if (!screen_dpi) screen_dpi = 96; if (RegCreateKeyExA(HKEY_CURRENT_USER, "Software\\Wine\\Fonts", 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hkey, NULL) != ERROR_SUCCESS) return; reg_load_dword(hkey, logpixels, &font_dpi); 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; buf[0] = 0; len = sizeof(buf); if (RegQueryValueExA(hkey, "Codepages", 0, &type, (BYTE *)buf, &len) == ERROR_SUCCESS && type == REG_SZ) { if (!strcmp( buf, cpbuf ) && screen_dpi == font_dpi) /* already set correctly */ { RegCloseKey(hkey); return; } TRACE("updating registry, codepages/logpixels changed %s/%u -> %u,%u/%u\n", buf, font_dpi, ansi_cp, oem_cp, screen_dpi); } else TRACE("updating registry, codepages/logpixels changed none -> %u,%u/%u\n", ansi_cp, oem_cp, screen_dpi); RegSetValueExA(hkey, "Codepages", 0, REG_SZ, (const BYTE *)cpbuf, strlen(cpbuf)+1); RegSetValueExW(hkey, logpixels, 0, REG_DWORD, (const BYTE *)&screen_dpi, sizeof(screen_dpi)); RegCloseKey(hkey); for (i = 0; i < ARRAY_SIZE(nls_update_font_list); 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], screen_dpi); RegCloseKey(hkey); hkey = create_fonts_9x_registry_key(); add_font_list(hkey, &nls_update_font_list[i], screen_dpi); 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); /* update locale dependent font association info and font system link info in registry. update only when codepages changed, not logpixels. */ if (strcmp(buf, cpbuf) != 0) { update_font_association_info(ansi_cp); update_font_system_link_info(ansi_cp); } } 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_Done_Face) LOAD_FUNCPTR(FT_Get_Char_Index) LOAD_FUNCPTR(FT_Get_First_Char) LOAD_FUNCPTR(FT_Get_Next_Char) LOAD_FUNCPTR(FT_Get_Sfnt_Name) LOAD_FUNCPTR(FT_Get_Sfnt_Name_Count) LOAD_FUNCPTR(FT_Get_Sfnt_Table) LOAD_FUNCPTR(FT_Get_WinFNT_Header) LOAD_FUNCPTR(FT_Init_FreeType) LOAD_FUNCPTR(FT_Library_Version) LOAD_FUNCPTR(FT_Load_Glyph) LOAD_FUNCPTR(FT_Load_Sfnt_Table) 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_Get_CBox) LOAD_FUNCPTR(FT_Outline_Transform) LOAD_FUNCPTR(FT_Outline_Translate) LOAD_FUNCPTR(FT_Render_Glyph) LOAD_FUNCPTR(FT_Set_Charmap) LOAD_FUNCPTR(FT_Set_Pixel_Sizes) LOAD_FUNCPTR(FT_Vector_Length) LOAD_FUNCPTR(FT_Vector_Transform) LOAD_FUNCPTR(FT_Vector_Unit) #undef LOAD_FUNCPTR /* Don't warn if these ones are missing */ pFT_Outline_Embolden = wine_dlsym(ft_handle, "FT_Outline_Embolden", NULL, 0); pFT_Get_TrueType_Engine_Type = wine_dlsym(ft_handle, "FT_Get_TrueType_Engine_Type", NULL, 0); #ifdef FT_LCD_FILTER_H pFT_Library_SetLcdFilter = wine_dlsym(ft_handle, "FT_Library_SetLcdFilter", NULL, 0); #endif if(pFT_Init_FreeType(&library) != 0) { ERR("Can't init FreeType library\n"); wine_dlclose(ft_handle, NULL, 0); ft_handle = NULL; return FALSE; } pFT_Library_Version(library,&FT_Version.major,&FT_Version.minor,&FT_Version.patch); 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); font_driver = &freetype_funcs; 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.1.4.\n" "http://www.freetype.org\n"); wine_dlclose(ft_handle, NULL, 0); ft_handle = NULL; return FALSE; } static void init_font_list(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; delete_external_font_keys(); /* load the system bitmap fonts */ load_system_fonts(); /* load in the fonts from %WINDOWSDIR%\\Fonts first of all */ GetWindowsDirectoryW(windowsdir, ARRAY_SIZE(windowsdir)); strcatW(windowsdir, fontsW); if((unixname = wine_get_unix_file_name(windowsdir))) { ReadFontDir(unixname, FALSE); HeapFree(GetProcessHeap(), 0, unixname); } /* load the wine fonts */ if ((unixname = get_font_dir())) { 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))) { AddFontToList(unixname, NULL, 0, ADDFONT_ALLOW_BITMAP | ADDFONT_ADD_TO_CACHE); 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 = AddFontToList(unixname, NULL, 0, ADDFONT_ALLOW_BITMAP | ADDFONT_ADD_TO_CACHE); 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); } #ifdef SONAME_LIBFONTCONFIG load_fontconfig_fonts(); #elif defined(HAVE_CARBON_CARBON_H) load_mac_fonts(); #elif defined(__ANDROID__) ReadFontDir("/system/fonts", TRUE); #endif /* 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) { const char* home; LPSTR next = strchr( ptr, ':' ); if (next) *next++ = 0; if (ptr[0] == '~' && ptr[1] == '/' && (home = getenv( "HOME" )) && (unixname = HeapAlloc( GetProcessHeap(), 0, strlen(ptr) + strlen(home) ))) { strcpy( unixname, home ); strcat( unixname, ptr + 1 ); ReadFontDir( unixname, TRUE ); HeapFree( GetProcessHeap(), 0, unixname ); } else ReadFontDir( ptr, TRUE ); ptr = next; } HeapFree( GetProcessHeap(), 0, valueA ); } HeapFree( GetProcessHeap(), 0, valueW ); } RegCloseKey(hkey); } } static BOOL move_to_front(const WCHAR *name) { Family *family, *cursor2; LIST_FOR_EACH_ENTRY_SAFE(family, cursor2, &font_list, Family, entry) { if(!strncmpiW(family->FamilyName, name, LF_FACESIZE - 1)) { list_remove(&family->entry); list_add_head(&font_list, &family->entry); return TRUE; } } return FALSE; } static BOOL set_default(const WCHAR **name_list) { while (*name_list) { if (move_to_front(*name_list)) return TRUE; name_list++; } return FALSE; } static void reorder_font_list(void) { set_default( default_serif_list ); set_default( default_fixed_list ); set_default( default_sans_list ); } /************************************************************* * WineEngInit * * Initialize FreeType library and create a list of available faces */ BOOL WineEngInit(void) { HKEY hkey; DWORD disposition; HANDLE font_mutex; /* update locale dependent font info in registry */ update_font_info(); if(!init_freetype()) return FALSE; #ifdef SONAME_LIBFONTCONFIG init_fontconfig(); #endif if (!RegOpenKeyExW(HKEY_CURRENT_USER, wine_fonts_key, 0, KEY_READ, &hkey)) { static const WCHAR antialias_fake_bold_or_italic[] = { 'A','n','t','i','a','l','i','a','s','F','a','k','e', 'B','o','l','d','O','r','I','t','a','l','i','c',0 }; static const WCHAR true_options[] = { 'y','Y','t','T','1',0 }; DWORD type, size; WCHAR buffer[20]; size = sizeof(buffer); if (!RegQueryValueExW(hkey, antialias_fake_bold_or_italic, NULL, &type, (BYTE*)buffer, &size) && type == REG_SZ && size >= 1) { antialias_fakes = (strchrW(true_options, buffer[0]) != NULL); } RegCloseKey(hkey); } if((font_mutex = CreateMutexW(NULL, FALSE, font_mutex_nameW)) == NULL) { ERR("Failed to create font mutex\n"); return FALSE; } WaitForSingleObject(font_mutex, INFINITE); create_font_cache_key(&hkey_font_cache, &disposition); if(disposition == REG_CREATED_NEW_KEY) init_font_list(); else load_font_list_from_cache(hkey_font_cache); reorder_font_list(); DumpFontList(); LoadSubstList(); DumpSubstList(); LoadReplaceList(); if(disposition == REG_CREATED_NEW_KEY) update_reg_entries(); init_system_links(); ReleaseMutex(font_mutex); return TRUE; } /* Some fonts have large usWinDescent values, as a result of storing signed short in unsigned field. That's probably caused by sTypoDescent vs usWinDescent confusion in some font generation tools. */ static inline USHORT get_fixed_windescent(USHORT windescent) { return abs((SHORT)windescent); } static LONG calc_ppem_for_height(FT_Face ft_face, LONG height) { TT_OS2 *pOS2; TT_HoriHeader *pHori; LONG ppem; const LONG MAX_PPEM = (1 << 16) - 1; 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) { USHORT windescent = get_fixed_windescent(pOS2->usWinDescent); if(pOS2->usWinAscent + windescent == 0) ppem = MulDiv(ft_face->units_per_EM, height, pHori->Ascender - pHori->Descender); else ppem = MulDiv(ft_face->units_per_EM, height, pOS2->usWinAscent + windescent); if(ppem > MAX_PPEM) { WARN("Ignoring too large height %d, ppem %d\n", height, ppem); ppem = 1; } } else if(height >= -MAX_PPEM) ppem = -height; else { WARN("Ignoring too large height %d\n", height); ppem = 1; } 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_w(face->file), face->font_data_ptr, face->face_index, width, height); if (face->file) { char *filename = strWtoA( CP_UNIXCP, face->file ); font->mapping = map_font_file( filename ); HeapFree( GetProcessHeap(), 0, filename ); if (!font->mapping) { WARN("failed to map %s\n", debugstr_w(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)) { FT_ULong len; DWORD header; /* 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); /* see if it's a TTC */ len = sizeof(header); if (!pFT_Load_Sfnt_Table(ft_face, 0, 0, (void*)&header, &len)) { if (header == MS_TTCF_TAG) { len = sizeof(font->ttc_item_offset); if (pFT_Load_Sfnt_Table(ft_face, 0, (3 + face->face_index) * sizeof(DWORD), (void*)&font->ttc_item_offset, &len)) font->ttc_item_offset = 0; else font->ttc_item_offset = GET_BE_DWORD(font->ttc_item_offset); } } } 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(const WCHAR *family_name, 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)) { const SYSTEM_LINKS *font_link; if (csi.fs.fsCsb[0] & face->fs.fsCsb[0]) return csi.ciCharset; font_link = find_font_link(family_name); if (font_link != NULL && csi.fs.fsCsb[0] & font_link->fs.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], debugstr_w(face->file)); *cp = acp; return DEFAULT_CHARSET; } static GdiFont *alloc_font(void) { GdiFont *ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*ret)); ret->refcount = 1; 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; ret->instance_id = alloc_font_handle(ret); list_init(&ret->child_fonts); return ret; } static void free_font(GdiFont *font) { CHILD_FONT *child, *child_next; DWORD i; LIST_FOR_EACH_ENTRY_SAFE( child, child_next, &font->child_fonts, CHILD_FONT, entry ) { list_remove(&child->entry); if(child->font) free_font(child->font); release_face( child->face ); HeapFree(GetProcessHeap(), 0, child); } HeapFree(GetProcessHeap(), 0, font->fileinfo); free_font_handle(font->instance_id); 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); } static DWORD get_font_data( GdiFont *font, DWORD table, DWORD offset, LPVOID buf, DWORD cbData) { FT_Face ft_face = font->ft_face; FT_ULong len; FT_Error err; if (!FT_IS_SFNT(ft_face)) return GDI_ERROR; if(!buf) len = 0; else len = cbData; /* if font is a member of TTC, 'ttcf' tag allows reading from beginning of TTC file, 0 tag means to read from start of collection member data. */ if (font->ttc_item_offset) { if (table == MS_TTCF_TAG) table = 0; else if (table == 0) offset += font->ttc_item_offset; } table = RtlUlongByteSwap( table ); /* MS tags differ in endianness from FT ones */ /* make sure value of len is the value freetype says it needs */ if (buf && len) { FT_ULong needed = 0; err = pFT_Load_Sfnt_Table(ft_face, table, offset, NULL, &needed); if( !err && needed < len) len = needed; } err = pFT_Load_Sfnt_Table(ft_face, table, offset, buf, &len); if (err) { table = RtlUlongByteSwap( table ); TRACE("Can't find table %s\n", debugstr_an((char*)&table, 4)); return GDI_ERROR; } return len; } /************************************************************* * load_VDMX * * load the vdmx entry for the specified height */ typedef struct { WORD version; WORD numRecs; WORD numRatios; } VDMX_Header; typedef struct { BYTE bCharSet; BYTE xRatio; BYTE yStartRatio; BYTE yEndRatio; } Ratios; typedef struct { WORD recs; BYTE startsz; BYTE endsz; } VDMX_group; typedef struct { WORD yPelHeight; WORD yMax; WORD yMin; } VDMX_vTable; static LONG load_VDMX(GdiFont *font, LONG height) { VDMX_Header hdr; VDMX_group group; BYTE devXRatio, devYRatio; USHORT numRecs, numRatios; DWORD result, offset = -1; LONG ppem = 0; int i; result = get_font_data(font, MS_VDMX_TAG, 0, &hdr, sizeof(hdr)); 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.numRecs); numRatios = GET_BE_WORD(hdr.numRatios); TRACE("version = %d numRecs = %d numRatios = %d\n", GET_BE_WORD(hdr.version), numRecs, numRatios); for(i = 0; i < numRatios; i++) { Ratios ratio; offset = sizeof(hdr) + (i * sizeof(Ratios)); get_font_data(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.bCharSet) continue; if((ratio.xRatio == 0 && ratio.yStartRatio == 0 && ratio.yEndRatio == 0) || (devXRatio == ratio.xRatio && devYRatio >= ratio.yStartRatio && devYRatio <= ratio.yEndRatio)) { WORD group_offset; offset = sizeof(hdr) + numRatios * sizeof(ratio) + i * sizeof(group_offset); get_font_data(font, MS_VDMX_TAG, offset, &group_offset, sizeof(group_offset)); offset = GET_BE_WORD(group_offset); break; } } if(offset == -1) return 0; if(get_font_data(font, MS_VDMX_TAG, offset, &group, sizeof(group)) != 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 * sizeof(VDMX_vTable)); result = get_font_data(font, MS_VDMX_TAG, offset + sizeof(group), vTable, recs * sizeof(VDMX_vTable)); if(result == GDI_ERROR) { FIXME("Failed to retrieve vTable\n"); goto end; } if(height > 0) { for(i = 0; i < recs; i++) { SHORT yMax = GET_BE_WORD(vTable[(i * 3) + 1]); SHORT yMin = GET_BE_WORD(vTable[(i * 3) + 2]); ppem = GET_BE_WORD(vTable[i * 3]); if(yMax + -yMin == height) { font->yMax = yMax; font->yMin = yMin; TRACE("ppem %d found; height=%d yMax=%d yMin=%d\n", ppem, height, font->yMax, font->yMin); break; } if(yMax + -yMin > height) { if(--i < 0) { ppem = 0; goto end; /* failed */ } font->yMax = GET_BE_WORD(vTable[(i * 3) + 1]); font->yMin = GET_BE_WORD(vTable[(i * 3) + 2]); ppem = GET_BE_WORD(vTable[i * 3]); TRACE("ppem %d found; height=%d yMax=%d yMin=%d\n", ppem, height, font->yMax, font->yMin); break; } } if(!font->yMax) { ppem = 0; TRACE("ppem not found for height %d\n", height); } } else { ppem = -height; if(ppem < startsz || ppem > endsz) { ppem = 0; goto end; } for(i = 0; i < recs; i++) { USHORT yPelHeight; yPelHeight = GET_BE_WORD(vTable[i * 3]); if(yPelHeight > ppem) { ppem = 0; break; /* failed */ } if(yPelHeight == ppem) { font->yMax = GET_BE_WORD(vTable[(i * 3) + 1]); font->yMin = GET_BE_WORD(vTable[(i * 3) + 2]); TRACE("ppem %d found; yMax=%d yMin=%d\n", ppem, font->yMax, font->yMin); break; } } } end: HeapFree(GetProcessHeap(), 0, vTable); } return ppem; } static void dump_gdi_font_list(void) { GdiFont *font; TRACE("---------- Font Cache ----------\n"); LIST_FOR_EACH_ENTRY( font, &gdi_font_list, struct tagGdiFont, entry ) TRACE("font=%p ref=%u %s %d\n", font, font->refcount, debugstr_w(font->font_desc.lf.lfFaceName), font->font_desc.lf.lfHeight); } static void grab_font( GdiFont *font ) { if (!font->refcount++) { list_remove( &font->unused_entry ); unused_font_count--; } } static void release_font( GdiFont *font ) { if (!font) return; if (!--font->refcount) { TRACE( "font %p\n", font ); /* add it to the unused list */ list_add_head( &unused_gdi_font_list, &font->unused_entry ); if (unused_font_count > UNUSED_CACHE_SIZE) { font = LIST_ENTRY( list_tail( &unused_gdi_font_list ), struct tagGdiFont, unused_entry ); TRACE( "freeing %p\n", font ); list_remove( &font->entry ); list_remove( &font->unused_entry ); free_font( font ); } else unused_font_count++; if (TRACE_ON(font)) dump_gdi_font_list(); } } 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; } static GdiFont *find_in_cache(HFONT hfont, const LOGFONTW *plf, const FMAT2 *pmat, BOOL can_use_bitmap) { GdiFont *ret; FONT_DESC fd; fd.lf = *plf; fd.matrix = *pmat; fd.can_use_bitmap = can_use_bitmap; calc_hash(&fd); /* try the in-use list */ LIST_FOR_EACH_ENTRY( ret, &gdi_font_list, struct tagGdiFont, entry ) { if(fontcmp(ret, &fd)) continue; if(!can_use_bitmap && !FT_IS_SCALABLE(ret->ft_face)) continue; list_remove( &ret->entry ); list_add_head( &gdi_font_list, &ret->entry ); grab_font( ret ); 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); TRACE( "font %p\n", font ); } /************************************************************* * 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; font_link = find_font_link(font_name); if (font_link != NULL) { 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; new_child->face->refcount++; list_add_tail(&font->child_fonts, &new_child->entry); TRACE("font %s %ld\n", debugstr_w(new_child->face->file), new_child->face->face_index); } ret = TRUE; } /* * 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) { font_link = find_font_link(szDefaultFallbackLink); if (font_link != NULL) { 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; new_child->face->refcount++; list_add_tail(&font->child_fonts, &new_child->entry); TRACE("font %s %ld\n", debugstr_w(new_child->face->file), new_child->face->face_index); } ret = TRUE; } } return ret; } static BOOL select_charmap(FT_Face ft_face, FT_Encoding encoding) { FT_Error ft_err = FT_Err_Invalid_CharMap_Handle; FT_CharMap cmap0, cmap1, cmap2, cmap3, cmap_def; FT_Int i; 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; } /************************************************************* * freetype_CreateDC */ static BOOL freetype_CreateDC( PHYSDEV *dev, LPCWSTR driver, LPCWSTR device, LPCWSTR output, const DEVMODEW *devmode ) { struct freetype_physdev *physdev = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*physdev) ); if (!physdev) return FALSE; push_dc_driver( dev, &physdev->dev, &freetype_funcs ); return TRUE; } /************************************************************* * freetype_DeleteDC */ static BOOL freetype_DeleteDC( PHYSDEV dev ) { struct freetype_physdev *physdev = get_freetype_dev( dev ); release_font( physdev->font ); HeapFree( GetProcessHeap(), 0, physdev ); return TRUE; } static FT_Encoding pick_charmap( FT_Face face, int charset ) { static const FT_Encoding regular_order[] = { FT_ENCODING_UNICODE, FT_ENCODING_APPLE_ROMAN, FT_ENCODING_MS_SYMBOL, 0 }; static const FT_Encoding symbol_order[] = { FT_ENCODING_MS_SYMBOL, FT_ENCODING_UNICODE, FT_ENCODING_APPLE_ROMAN, 0 }; const FT_Encoding *encs = regular_order; if (charset == SYMBOL_CHARSET) encs = symbol_order; while (*encs != 0) { if (select_charmap( face, *encs )) break; encs++; } if (!face->charmap && face->num_charmaps) { if (!pFT_Set_Charmap(face, face->charmaps[0])) return face->charmap->encoding; } return *encs; } static BOOL get_gasp_flags( GdiFont *font, WORD *flags ) { DWORD size; WORD buf[16]; /* Enough for seven ranges before we need to alloc */ WORD *alloced = NULL, *ptr = buf; WORD num_recs, version; BOOL ret = FALSE; *flags = 0; size = get_font_data( font, MS_GASP_TAG, 0, NULL, 0 ); if (size == GDI_ERROR) return FALSE; if (size < 4 * sizeof(WORD)) return FALSE; if (size > sizeof(buf)) { ptr = alloced = HeapAlloc( GetProcessHeap(), 0, size ); if (!ptr) return FALSE; } get_font_data( font, MS_GASP_TAG, 0, ptr, size ); version = GET_BE_WORD( *ptr++ ); num_recs = GET_BE_WORD( *ptr++ ); if (version > 1 || size < (num_recs * 2 + 2) * sizeof(WORD)) { FIXME( "Unsupported gasp table: ver %d size %d recs %d\n", version, size, num_recs ); goto done; } while (num_recs--) { *flags = GET_BE_WORD( *(ptr + 1) ); if (font->ft_face->size->metrics.y_ppem <= GET_BE_WORD( *ptr )) break; ptr += 2; } TRACE( "got flags %04x for ppem %d\n", *flags, font->ft_face->size->metrics.y_ppem ); ret = TRUE; done: HeapFree( GetProcessHeap(), 0, alloced ); return ret; } 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 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 const VOID * get_GSUB_vert_feature(const GdiFont *font) { const GSUB_Header *header; const GSUB_Script *script; const GSUB_LangSys *language; const GSUB_Feature *feature; if (!font->GSUB_Table) return NULL; header = font->GSUB_Table; script = GSUB_get_script_table(header, get_opentype_script(font)); if (!script) { TRACE("Script not found\n"); return NULL; } language = GSUB_get_lang_table(script, "xxxx"); /* Need to get Lang tag */ if (!language) { TRACE("Language not found\n"); return NULL; } 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 NULL; } return feature; } static void fill_fileinfo_from_face( GdiFont *font, Face *face ) { WIN32_FILE_ATTRIBUTE_DATA info; int len; if (!face->file) { font->fileinfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*font->fileinfo)); return; } len = strlenW(face->file); font->fileinfo = HeapAlloc(GetProcessHeap(), 0, sizeof(*font->fileinfo) + len * sizeof(WCHAR)); if (GetFileAttributesExW(face->file, GetFileExInfoStandard, &info)) { font->fileinfo->writetime = info.ftLastWriteTime; font->fileinfo->size.QuadPart = (LONGLONG)info.nFileSizeHigh << 32 | info.nFileSizeLow; strcpyW(font->fileinfo->path, face->file); } else memset(font->fileinfo, 0, sizeof(*font->fileinfo) + len * sizeof(WCHAR)); } /************************************************************* * freetype_SelectFont */ static HFONT freetype_SelectFont( PHYSDEV dev, HFONT hfont, UINT *aa_flags ) { struct freetype_physdev *physdev = get_freetype_dev( dev ); GdiFont *ret; Face *face, *best, *best_bitmap; Family *family, *last_resort_family; const struct list *face_list; INT height, width = 0; unsigned int score = 0, new_score; signed int diff = 0, newdiff; BOOL bd, it, can_use_bitmap, want_vertical; LOGFONTW lf; CHARSETINFO csi; FMAT2 dcmat; FontSubst *psub = NULL; DC *dc = get_physdev_dc( dev ); const SYSTEM_LINKS *font_link; if (!hfont) /* notification that the font has been changed by another driver */ { release_font( physdev->font ); physdev->font = NULL; return 0; } GetObjectW( hfont, sizeof(lf), &lf ); lf.lfWidth = abs(lf.lfWidth); can_use_bitmap = GetDeviceCaps(dev->hdc, 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)); /* 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 = dcmat.eM11 < 0 ? -1 : 1; } } else { /* Windows 3.1 compatibility mode GM_COMPATIBLE has only limited font scaling abilities. */ dcmat.eM11 = dcmat.eM22 = 1.0; dcmat.eM21 = dcmat.eM12 = 0; lf.lfOrientation = lf.lfEscapement; if (dc->vport2WorldValid) { if (dc->xformWorld2Vport.eM11 * dc->xformWorld2Vport.eM22 < 0) lf.lfOrientation = -lf.lfOrientation; lf.lfHeight *= fabs(dc->xformWorld2Vport.eM22); lf.lfWidth *= fabs(dc->xformWorld2Vport.eM22); } } 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); goto done; } TRACE("not in cache\n"); 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); /* 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') { CHILD_FONT *font_link_entry; LPWSTR FaceName = lf.lfFaceName; 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_ENTRY( family, &font_list, Family, entry ) { if (!strncmpiW(family->FamilyName, FaceName, LF_FACESIZE - 1) || (psub && !strncmpiW(family->FamilyName, psub->to.name, LF_FACESIZE - 1))) { font_link = find_font_link(family->FamilyName); face_list = get_face_list_from_family(family); LIST_FOR_EACH_ENTRY( face, face_list, Face, entry ) { if (!(face->scalable || can_use_bitmap)) continue; if (csi.fs.fsCsb[0] & face->fs.fsCsb[0]) goto found; if (font_link != NULL && csi.fs.fsCsb[0] & font_link->fs.fsCsb[0]) goto found; if (!csi.fs.fsCsb[0]) goto found; } } } /* Search by full face name. */ LIST_FOR_EACH_ENTRY( family, &font_list, Family, entry ) { face_list = get_face_list_from_family(family); LIST_FOR_EACH_ENTRY( face, face_list, Face, entry ) { if(face->FullName && !strncmpiW(face->FullName, FaceName, LF_FACESIZE - 1) && (face->scalable || can_use_bitmap)) { if (csi.fs.fsCsb[0] & face->fs.fsCsb[0] || !csi.fs.fsCsb[0]) goto found_face; font_link = find_font_link(family->FamilyName); if (font_link != NULL && csi.fs.fsCsb[0] & font_link->fs.fsCsb[0]) goto found_face; } } } /* * 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(!strncmpiW(font_link->font_name, FaceName, LF_FACESIZE - 1) || (psub && !strncmpiW(font_link->font_name,psub->to.name, LF_FACESIZE - 1))) { TRACE("found entry in system list\n"); LIST_FOR_EACH_ENTRY(font_link_entry, &font_link->links, CHILD_FONT, entry) { const SYSTEM_LINKS *links; face = font_link_entry->face; if (!(face->scalable || can_use_bitmap)) continue; family = face->family; if (csi.fs.fsCsb[0] & face->fs.fsCsb[0] || !csi.fs.fsCsb[0]) goto found; links = find_font_link(family->FamilyName); if (links != NULL && csi.fs.fsCsb[0] & links->fs.fsCsb[0]) 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; } want_vertical = (lf.lfFaceName[0] == '@'); /* 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_ENTRY( family, &font_list, Family, entry ) { if(!strncmpiW(family->FamilyName, lf.lfFaceName, LF_FACESIZE - 1)) { font_link = find_font_link(family->FamilyName); face_list = get_face_list_from_family(family); LIST_FOR_EACH_ENTRY( face, face_list, Face, entry ) { if (!(face->scalable || can_use_bitmap)) continue; if (csi.fs.fsCsb[0] & face->fs.fsCsb[0]) goto found; if (font_link != NULL && csi.fs.fsCsb[0] & font_link->fs.fsCsb[0]) goto found; } } } last_resort_family = NULL; LIST_FOR_EACH_ENTRY( family, &font_list, Family, entry ) { font_link = find_font_link(family->FamilyName); face_list = get_face_list_from_family(family); LIST_FOR_EACH_ENTRY( face, face_list, Face, entry ) { if(!(face->flags & ADDFONT_VERTICAL_FONT) == !want_vertical && (csi.fs.fsCsb[0] & face->fs.fsCsb[0] || (font_link != NULL && csi.fs.fsCsb[0] & font_link->fs.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_ENTRY( family, &font_list, Family, entry ) { face_list = get_face_list_from_family(family); LIST_FOR_EACH_ENTRY( face, face_list, Face, entry ) { if(face->scalable && !(face->flags & ADDFONT_VERTICAL_FONT) == !want_vertical) { 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); ret = NULL; goto done; } 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; font_link = find_font_link(family->FamilyName); face_list = get_face_list_from_family(family); LIST_FOR_EACH_ENTRY(face, face_list, Face, entry) { if (csi.fs.fsCsb[0] & face->fs.fsCsb[0] || (font_link != NULL && csi.fs.fsCsb[0] & font_link->fs.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)); found_face: height = lf.lfHeight; ret->fs = face->fs; if(csi.fs.fsCsb[0]) { ret->charset = lf.lfCharSet; ret->codepage = csi.ciACP; } else ret->charset = get_nearest_charset(family->FamilyName, face, &ret->codepage); TRACE("Chosen: %s %s (%s/%p:%ld)\n", debugstr_w(family->FamilyName), debugstr_w(face->StyleName), debugstr_w(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; GdiFont *cachedfont; /* 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; dcmat.eM11 = dcmat.eM22 = 1.0; /* As we changed the matrix, we need to search the cache for the font again, * otherwise we might explode the cache. */ if((cachedfont = find_in_cache(hfont, &lf, &dcmat, can_use_bitmap)) != NULL) { TRACE("Found cached font after non-scalable matrix rescale!\n"); free_font( ret ); ret = cachedfont; goto done; } calc_hash(&ret->font_desc); 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 ); ret = NULL; goto done; } fill_fileinfo_from_face( ret, face ); ret->ntmFlags = face->ntmFlags; pick_charmap( ret->ft_face, ret->charset ); 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 (face->flags & ADDFONT_VERTICAL_FONT) /* We need to try to load the GSUB table */ { int length = get_font_data(ret, MS_GSUB_TAG , 0, NULL, 0); if (length != GDI_ERROR) { ret->GSUB_Table = HeapAlloc(GetProcessHeap(),0,length); get_font_data(ret, MS_GSUB_TAG , 0, ret->GSUB_Table, length); TRACE("Loaded GSUB table of %i bytes\n",length); ret->vert_feature = get_GSUB_vert_feature(ret); if (!ret->vert_feature) { TRACE("Vertical feature not found\n"); HeapFree(GetProcessHeap(), 0, ret->GSUB_Table); ret->GSUB_Table = NULL; } } } ret->aa_flags = HIWORD( face->flags ); TRACE("caching: gdiFont=%p hfont=%p\n", ret, hfont); add_to_cache(ret); done: if (ret) { PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSelectFont ); switch (lf.lfQuality) { case NONANTIALIASED_QUALITY: case ANTIALIASED_QUALITY: next->funcs->pSelectFont( dev, hfont, aa_flags ); break; case CLEARTYPE_QUALITY: case CLEARTYPE_NATURAL_QUALITY: default: if (!*aa_flags) *aa_flags = ret->aa_flags; next->funcs->pSelectFont( dev, hfont, aa_flags ); /* fixup the antialiasing flags for that font */ switch (*aa_flags) { case WINE_GGO_HRGB_BITMAP: case WINE_GGO_HBGR_BITMAP: case WINE_GGO_VRGB_BITMAP: case WINE_GGO_VBGR_BITMAP: if (is_subpixel_rendering_enabled()) break; *aa_flags = GGO_GRAY4_BITMAP; /* fall through */ case GGO_GRAY2_BITMAP: case GGO_GRAY4_BITMAP: case GGO_GRAY8_BITMAP: case WINE_GGO_GRAY16_BITMAP: if ((!antialias_fakes || (!ret->fake_bold && !ret->fake_italic)) && is_hinting_enabled()) { WORD gasp_flags; if (get_gasp_flags( ret, &gasp_flags ) && !(gasp_flags & GASP_DOGRAY)) { TRACE( "font %s %d aa disabled by GASP\n", debugstr_w(lf.lfFaceName), lf.lfHeight ); *aa_flags = GGO_BITMAP; } } } } TRACE( "%p %s %d aa %x\n", hfont, debugstr_w(lf.lfFaceName), lf.lfHeight, *aa_flags ); release_font( physdev->font ); physdev->font = ret; } LeaveCriticalSection( &freetype_cs ); return ret ? hfont : 0; } static INT load_script_name( UINT id, WCHAR buffer[LF_FACESIZE] ) { HRSRC rsrc; HGLOBAL hMem; WCHAR *p; int i; id += IDS_FIRST_SCRIPT; rsrc = FindResourceW( gdi32_module, (LPCWSTR)(ULONG_PTR)((id >> 4) + 1), (LPCWSTR)6 /*RT_STRING*/ ); if (!rsrc) return 0; hMem = LoadResource( gdi32_module, rsrc ); if (!hMem) return 0; p = LockResource( hMem ); id &= 0x000f; while (id--) p += *p + 1; i = min(LF_FACESIZE - 1, *p); memcpy(buffer, p + 1, i * sizeof(WCHAR)); buffer[i] = 0; return i; } static inline BOOL is_complex_script_ansi_cp(UINT ansi_cp) { return (ansi_cp == 874 /* Thai */ || ansi_cp == 1255 /* Hebrew */ || ansi_cp == 1256 /* Arabic */ ); } /*************************************************** * create_enum_charset_list * * This function creates charset enumeration list because in DEFAULT_CHARSET * case, the ANSI codepage's charset takes precedence over other charsets. * Above rule doesn't apply if the ANSI codepage uses complex script (e.g. Thai). * This function works as a filter other than DEFAULT_CHARSET case. */ static DWORD create_enum_charset_list(DWORD charset, struct enum_charset_list *list) { CHARSETINFO csi; DWORD n = 0; if (TranslateCharsetInfo(ULongToPtr(charset), &csi, TCI_SRCCHARSET) && csi.fs.fsCsb[0] != 0) { list->element[n].mask = csi.fs.fsCsb[0]; list->element[n].charset = csi.ciCharset; load_script_name( ffs(csi.fs.fsCsb[0]) - 1, list->element[n].name ); n++; } else { /* charset is DEFAULT_CHARSET or invalid. */ INT acp, i; DWORD mask = 0; /* Set the current codepage's charset as the first element. */ acp = GetACP(); if (!is_complex_script_ansi_cp(acp) && TranslateCharsetInfo((DWORD*)(INT_PTR)acp, &csi, TCI_SRCCODEPAGE) && csi.fs.fsCsb[0] != 0) { list->element[n].mask = csi.fs.fsCsb[0]; list->element[n].charset = csi.ciCharset; load_script_name( ffs(csi.fs.fsCsb[0]) - 1, list->element[n].name ); mask |= csi.fs.fsCsb[0]; n++; } /* Fill out left elements. */ for (i = 0; i < 32; i++) { FONTSIGNATURE fs; fs.fsCsb[0] = 1L << i; fs.fsCsb[1] = 0; if (fs.fsCsb[0] & mask) continue; /* skip, already added. */ if (!TranslateCharsetInfo(fs.fsCsb, &csi, TCI_SRCFONTSIG)) continue; /* skip, this is an invalid fsCsb bit. */ list->element[n].mask = fs.fsCsb[0]; list->element[n].charset = csi.ciCharset; load_script_name( i, list->element[n].name ); mask |= fs.fsCsb[0]; n++; } /* add catch all mask for remaining bits */ if (~mask) { list->element[n].mask = ~mask; list->element[n].charset = DEFAULT_CHARSET; load_script_name( IDS_OTHER - IDS_FIRST_SCRIPT, list->element[n].name ); n++; } } list->total = n; return n; } static void GetEnumStructs(Face *face, const WCHAR *family_name, 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 = 100; 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( family_name ); font->ntmFlags = face->ntmFlags; if (get_outline_text_metrics(font)) { memcpy(&pntm->ntmTm, &font->potm->otmTextMetrics, sizeof(TEXTMETRICW)); pntm->ntmTm.ntmSizeEM = font->potm->otmEMSquare; pntm->ntmTm.ntmCellHeight = font->ntmCellHeight; pntm->ntmTm.ntmAvgWidth = font->ntmAvgWidth; 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 { get_text_metrics(font, (TEXTMETRICW *)&pntm->ntmTm); pntm->ntmTm.ntmSizeEM = pntm->ntmTm.tmHeight - pntm->ntmTm.tmInternalLeading; pntm->ntmTm.ntmCellHeight = pntm->ntmTm.tmHeight; pntm->ntmTm.ntmAvgWidth = pntm->ntmTm.tmAveCharWidth; lstrcpynW(pelf->elfLogFont.lfFaceName, family_name, LF_FACESIZE); if (face->FullName) lstrcpynW(pelf->elfFullName, face->FullName, LF_FULLFACESIZE); else lstrcpynW(pelf->elfFullName, family_name, LF_FULLFACESIZE); lstrcpynW(pelf->elfStyle, face->StyleName, LF_FACESIZE); } pntm->ntmTm.ntmFlags = face->ntmFlags; 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); } static BOOL family_matches(Family *family, const WCHAR *face_name) { Face *face; const struct list *face_list; if (!strncmpiW(face_name, family->FamilyName, LF_FACESIZE - 1)) return TRUE; face_list = get_face_list_from_family(family); LIST_FOR_EACH_ENTRY(face, face_list, Face, entry) if (face->FullName && !strncmpiW(face_name, face->FullName, LF_FACESIZE - 1)) return TRUE; return FALSE; } static BOOL face_matches(const WCHAR *family_name, Face *face, const WCHAR *face_name) { if (!strncmpiW(face_name, family_name, LF_FACESIZE - 1)) return TRUE; return (face->FullName && !strncmpiW(face_name, face->FullName, LF_FACESIZE - 1)); } static BOOL enum_face_charsets(const Family *family, Face *face, struct enum_charset_list *list, FONTENUMPROCW proc, LPARAM lparam, const WCHAR *subst) { ENUMLOGFONTEXW elf; NEWTEXTMETRICEXW ntm; DWORD type = 0; DWORD i; GetEnumStructs(face, face->family->FamilyName, &elf, &ntm, &type); for(i = 0; i < list->total; i++) { if(!face->scalable && face->fs.fsCsb[0] == 0) { /* OEM bitmap */ elf.elfLogFont.lfCharSet = ntm.ntmTm.tmCharSet = OEM_CHARSET; load_script_name( IDS_OEM_DOS - IDS_FIRST_SCRIPT, elf.elfScript ); i = list->total; /* break out of loop after enumeration */ } else { if(!(face->fs.fsCsb[0] & list->element[i].mask)) continue; /* use the DEFAULT_CHARSET case only if no other charset is present */ if (list->element[i].charset == DEFAULT_CHARSET && (face->fs.fsCsb[0] & ~list->element[i].mask)) continue; elf.elfLogFont.lfCharSet = ntm.ntmTm.tmCharSet = list->element[i].charset; strcpyW(elf.elfScript, list->element[i].name); if (!elf.elfScript[0]) FIXME("Unknown elfscript for bit %d\n", ffs(list->element[i].mask) - 1); } /* Font Replacement */ if (family != face->family) { lstrcpynW(elf.elfLogFont.lfFaceName, family->FamilyName, LF_FACESIZE); if (face->FullName) lstrcpynW(elf.elfFullName, face->FullName, LF_FULLFACESIZE); else lstrcpynW(elf.elfFullName, family->FamilyName, LF_FULLFACESIZE); } if (subst) strcpyW(elf.elfLogFont.lfFaceName, subst); 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), elf.elfLogFont.lfCharSet, 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 FALSE; EnterCriticalSection( &freetype_cs ); } return TRUE; } /************************************************************* * freetype_EnumFonts */ static BOOL freetype_EnumFonts( PHYSDEV dev, LPLOGFONTW plf, FONTENUMPROCW proc, LPARAM lparam ) { Family *family; Face *face; const struct list *face_list; LOGFONTW lf; struct enum_charset_list enum_charsets; 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); create_enum_charset_list(plf->lfCharSet, &enum_charsets); GDI_CheckNotLock(); EnterCriticalSection( &freetype_cs ); if(plf->lfFaceName[0]) { WCHAR *face_name = plf->lfFaceName; FontSubst *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)); face_name = psub->to.name; } LIST_FOR_EACH_ENTRY( family, &font_list, Family, entry ) { if (!family_matches(family, face_name)) continue; face_list = get_face_list_from_family(family); LIST_FOR_EACH_ENTRY( face, face_list, Face, entry ) { if (!face_matches(family->FamilyName, face, face_name)) continue; if (!enum_face_charsets(family, face, &enum_charsets, proc, lparam, psub ? psub->from.name : NULL)) return FALSE; } } } else { LIST_FOR_EACH_ENTRY( family, &font_list, Family, entry ) { face_list = get_face_list_from_family(family); face = LIST_ENTRY(list_head(face_list), Face, entry); if (!enum_face_charsets(family, face, &enum_charsets, proc, lparam, NULL)) return FALSE; } } LeaveCriticalSection( &freetype_cs ); return TRUE; } 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)); } /*************************************************** * 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 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 FT_UInt get_GSUB_vert_glyph(const GdiFont *font, UINT glyph) { const GSUB_Header *header; const GSUB_Feature *feature; if (!font->GSUB_Table) return glyph; header = font->GSUB_Table; feature = font->vert_feature; return GSUB_apply_feature(header, feature, glyph); } static FT_UInt get_glyph_index_symbol(const GdiFont *font, UINT glyph) { FT_UInt ret; if (glyph < 0x100) glyph += 0xf000; /* there are a number of old pre-Unicode "broken" TTFs, which do have symbols at U+00XX instead of U+f0XX */ if (!(ret = pFT_Get_Char_Index(font->ft_face, glyph))) ret = pFT_Get_Char_Index(font->ft_face, glyph - 0xf000); return ret; } static FT_UInt get_glyph_index(const GdiFont *font, UINT glyph) { FT_UInt ret; WCHAR wc; char buf; if (font->ft_face->charmap->encoding == FT_ENCODING_NONE) { BOOL default_used; BOOL *default_used_pointer; default_used_pointer = NULL; default_used = FALSE; if (codepage_sets_default_used(font->codepage)) default_used_pointer = &default_used; wc = (WCHAR)glyph; if (!WideCharToMultiByte(font->codepage, 0, &wc, 1, &buf, sizeof(buf), NULL, default_used_pointer) || default_used) { if (font->codepage == CP_SYMBOL) { ret = get_glyph_index_symbol(font, glyph); if (!ret) { if (WideCharToMultiByte(CP_ACP, 0, &wc, 1, &buf, 1, NULL, NULL)) ret = get_glyph_index_symbol(font, buf); } } else ret = 0; } else ret = pFT_Get_Char_Index(font->ft_face, (unsigned char)buf); TRACE("%04x (%02x) -> ret %d def_used %d\n", glyph, (unsigned char)buf, ret, default_used); return ret; } if (font->ft_face->charmap->encoding == FT_ENCODING_MS_SYMBOL) { ret = get_glyph_index_symbol(font, glyph); if (!ret) { wc = (WCHAR)glyph; if (WideCharToMultiByte(CP_ACP, 0, &wc, 1, &buf, 1, NULL, NULL)) ret = get_glyph_index_symbol(font, (unsigned char)buf); } return ret; } return pFT_Get_Char_Index(font->ft_face, glyph); } /* helper for freetype_GetGlyphIndices */ static FT_UInt get_gdi_glyph_index(const GdiFont *font, UINT glyph) { WCHAR wc = (WCHAR)glyph; BOOL default_used = FALSE; BOOL *default_used_pointer = NULL; FT_UInt ret; char buf; if(font->ft_face->charmap->encoding != FT_ENCODING_NONE) return get_glyph_index(font, glyph); 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) { if (font->codepage == CP_SYMBOL && wc < 0x100) ret = (unsigned char)wc; else ret = 0; } else ret = (unsigned char)buf; TRACE("%04x (%02x) -> ret %d def_used %d\n", glyph, (unsigned char)buf, ret, default_used); return ret; } static FT_UInt get_default_char_index(GdiFont *font) { FT_UInt default_char; 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; get_text_metrics(font, &textm); default_char = textm.tmDefaultChar; } return default_char; } /************************************************************* * freetype_GetGlyphIndices */ static DWORD freetype_GetGlyphIndices( PHYSDEV dev, LPCWSTR lpstr, INT count, LPWORD pgi, DWORD flags ) { struct freetype_physdev *physdev = get_freetype_dev( dev ); int i; WORD default_char; BOOL got_default = FALSE; if (!physdev->font) { dev = GET_NEXT_PHYSDEV( dev, pGetGlyphIndices ); return dev->funcs->pGetGlyphIndices( dev, lpstr, count, pgi, flags ); } if (flags & GGI_MARK_NONEXISTING_GLYPHS) { default_char = 0xffff; /* XP would use 0x1f for bitmap fonts */ got_default = TRUE; } GDI_CheckNotLock(); EnterCriticalSection( &freetype_cs ); for(i = 0; i < count; i++) { pgi[i] = get_gdi_glyph_index(physdev->font, lpstr[i]); if (pgi[i] == 0) { if (!got_default) { default_char = get_default_char_index(physdev->font); got_default = TRUE; } pgi[i] = default_char; } else pgi[i] = get_GSUB_vert_glyph(physdev->font, pgi[i]); } LeaveCriticalSection( &freetype_cs ); 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)); } static inline FT_Vector normalize_vector(FT_Vector *vec) { FT_Vector out; FT_Fixed len; len = pFT_Vector_Length(vec); if (len) { out.x = (vec->x << 6) / len; out.y = (vec->y << 6) / len; } else out.x = out.y = 0; return out; } static BOOL get_bold_glyph_outline(FT_GlyphSlot glyph, LONG ppem, FT_Glyph_Metrics *metrics) { FT_Error err; FT_Pos strength; FT_BBox bbox; if(glyph->format != FT_GLYPH_FORMAT_OUTLINE) return FALSE; if(!pFT_Outline_Embolden) return FALSE; strength = MulDiv(ppem, 1 << 6, 24); err = pFT_Outline_Embolden(&glyph->outline, strength); if(err) { TRACE("FT_Ouline_Embolden returns %d\n", err); return FALSE; } pFT_Outline_Get_CBox(&glyph->outline, &bbox); metrics->width = bbox.xMax - bbox.xMin; metrics->height = bbox.yMax - bbox.yMin; metrics->horiBearingX = bbox.xMin; metrics->horiBearingY = bbox.yMax; metrics->vertBearingX = metrics->horiBearingX - metrics->horiAdvance / 2; metrics->vertBearingY = (metrics->vertAdvance - metrics->height) / 2; return TRUE; } static inline BYTE get_max_level( UINT format ) { switch( format ) { case GGO_GRAY2_BITMAP: return 4; case GGO_GRAY4_BITMAP: return 16; case GGO_GRAY8_BITMAP: return 64; } return 255; } extern const unsigned short vertical_orientation_table[] DECLSPEC_HIDDEN; static BOOL check_unicode_tategaki(WCHAR uchar) { unsigned short orientation = vertical_orientation_table[vertical_orientation_table[vertical_orientation_table[uchar >> 8]+((uchar >> 4) & 0x0f)]+ (uchar & 0xf)]; /* We only reach this code if typographical substitution did not occur */ /* Type: U or Type: Tu */ return (orientation == 1 || orientation == 3); } static FT_Vector get_advance_metric(GdiFont *incoming_font, GdiFont *font, const FT_Glyph_Metrics *metrics, const FT_Matrix *transMat, BOOL vertical_metrics) { FT_Vector adv; FT_Fixed base_advance, em_scale = 0; BOOL fixed_pitch_full = FALSE; if (vertical_metrics) base_advance = metrics->vertAdvance; else base_advance = metrics->horiAdvance; adv.x = base_advance; adv.y = 0; /* In fixed-pitch font, we adjust the fullwidth character advance so that they have double halfwidth character width. E.g. if the font is 19 ppem, we return 20 (not 19) for fullwidth characters as we return 10 for halfwidth characters. */ if(FT_IS_SCALABLE(incoming_font->ft_face) && (incoming_font->potm || get_outline_text_metrics(incoming_font)) && !(incoming_font->potm->otmTextMetrics.tmPitchAndFamily & TMPF_FIXED_PITCH)) { UINT avg_advance; em_scale = MulDiv(incoming_font->ppem, 1 << 16, incoming_font->ft_face->units_per_EM); avg_advance = pFT_MulFix(incoming_font->ntmAvgWidth, em_scale); fixed_pitch_full = (avg_advance > 0 && (base_advance + 63) >> 6 == pFT_MulFix(incoming_font->ntmAvgWidth*2, em_scale)); if (fixed_pitch_full && !transMat) adv.x = (avg_advance * 2) << 6; } if (transMat) { pFT_Vector_Transform(&adv, transMat); if (fixed_pitch_full && adv.y == 0) { FT_Vector vec; vec.x = incoming_font->ntmAvgWidth; vec.y = 0; pFT_Vector_Transform(&vec, transMat); adv.x = (pFT_MulFix(vec.x, em_scale) * 2) << 6; } } if (font->fake_bold) { if (!transMat) adv.x += 1 << 6; else { FT_Vector fake_bold_adv, vec = { 1 << 6, 0 }; pFT_Vector_Transform(&vec, transMat); fake_bold_adv = normalize_vector(&vec); adv.x += fake_bold_adv.x; adv.y += fake_bold_adv.y; } } adv.x = (adv.x + 63) & -64; adv.y = -((adv.y + 63) & -64); return adv; } static unsigned int get_native_glyph_outline(FT_Outline *outline, unsigned int buflen, char *buf) { TTPOLYGONHEADER *pph; TTPOLYCURVE *ppc; unsigned int needed = 0, point = 0, contour, first_pt; unsigned int pph_start, cpfx; DWORD type; for (contour = 0; contour < outline->n_contours; contour++) { /* Ignore contours containing one point */ if (point == outline->contours[contour]) { point++; continue; } pph_start = needed; pph = (TTPOLYGONHEADER *)(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 *)(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; } return needed; } static unsigned int get_bezier_glyph_outline(FT_Outline *outline, unsigned int buflen, char *buf) { /* 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 Bezier 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; TTPOLYGONHEADER *pph; TTPOLYCURVE *ppc; DWORD pph_start, cpfx, type; FT_Vector cubic_control[4]; unsigned int needed = 0; for (contour = 0; contour < outline->n_contours; contour++) { pph_start = needed; pph = (TTPOLYGONHEADER *)(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 *)(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; } return needed; } static const BYTE masks[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01}; static DWORD get_glyph_outline(GdiFont *incoming_font, UINT glyph, UINT format, LPGLYPHMETRICS lpgm, ABC *abc, DWORD buflen, LPVOID buf, const MAT2* lpmat) { static const FT_Matrix identityMat = {(1 << 16), 0, 0, (1 << 16)}; GLYPHMETRICS gm; FT_Face ft_face = incoming_font->ft_face; GdiFont *font = incoming_font; FT_Glyph_Metrics metrics; FT_UInt glyph_index; DWORD width, height, pitch, needed = 0; FT_Bitmap ft_bitmap; FT_Error err; INT left, right, top = 0, bottom = 0; FT_Vector adv; INT origin_x = 0, origin_y = 0; 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; FT_Matrix transMatTategaki; BOOL needsTransform = FALSE; BOOL tategaki = (font->name[0] == '@'); BOOL vertical_metrics; 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); if(format & GGO_GLYPH_INDEX) { if(font->ft_face->charmap->encoding == FT_ENCODING_NONE) { /* Windows bitmap font, e.g. Small Fonts, uses ANSI character code as glyph index. "Treasure Adventure Game" depends on this. */ glyph_index = pFT_Get_Char_Index(font->ft_face, glyph); TRACE("translate glyph index %04x -> %04x\n", glyph, glyph_index); } else glyph_index = glyph; original_index = glyph_index; format &= ~GGO_GLYPH_INDEX; /* TODO: Window also turns off tategaki for glyphs passed in by index if their unicode code points fall outside of the range that is rotated. */ } else { BOOL vert; get_glyph_index_linked(incoming_font, glyph, &font, &glyph_index, &vert); ft_face = font->ft_face; original_index = glyph_index; if (!vert && tategaki) tategaki = check_unicode_tategaki(glyph); } if(format & GGO_UNHINTED) { load_flags |= FT_LOAD_NO_HINTING; format &= ~GGO_UNHINTED; } 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; *abc = FONT_GM(font,original_index)->abc; TRACE("cached: %u,%u,%s,%d,%d\n", lpgm->gmBlackBoxX, lpgm->gmBlackBoxY, wine_dbgstr_point(&lpgm->gmptGlyphOrigin), lpgm->gmCellIncX, lpgm->gmCellIncY); 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; get_text_metrics(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; transMatTategaki = transMat; if(font->orientation || tategaki) { FT_Matrix rotationMat; FT_Matrix taterotationMat; FT_Vector vecAngle; double orient = font->orientation / 10.0; double tate_orient = 0.f; if (tategaki) tate_orient = ((font->orientation+900)%3600)/10.0; else tate_orient = font->orientation/10.0; if (orient) { angle = FT_FixedFromFloat(orient); 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); } if (tate_orient) { angle = FT_FixedFromFloat(tate_orient); pFT_Vector_Unit(&vecAngle, angle); taterotationMat.xx = vecAngle.x; taterotationMat.xy = -vecAngle.y; taterotationMat.yx = -taterotationMat.xy; taterotationMat.yy = taterotationMat.xx; pFT_Matrix_Multiply(&taterotationMat, &transMatTategaki); } 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.eM21); worldMat.yx = -FT_FixedFromFloat(font->font_desc.matrix.eM12); worldMat.yy = FT_FixedFromFloat(font->font_desc.matrix.eM22); pFT_Matrix_Multiply(&worldMat, &transMat); pFT_Matrix_Multiply(&worldMat, &transMatUnrotated); pFT_Matrix_Multiply(&worldMat, &transMatTategaki); 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->eM21); extraMat.yx = FT_FixedFromFIXED(lpmat->eM12); extraMat.yy = FT_FixedFromFIXED(lpmat->eM22); pFT_Matrix_Multiply(&extraMat, &transMat); pFT_Matrix_Multiply(&extraMat, &transMatUnrotated); pFT_Matrix_Multiply(&extraMat, &transMatTategaki); needsTransform = TRUE; } vertical_metrics = (tategaki && FT_HAS_VERTICAL(ft_face)); /* there is a freetype bug where vertical metrics are only properly scaled and correct in 2.4.0 or greater */ if ((vertical_metrics) && (FT_Version.major < 2 || (FT_Version.major == 2 && FT_Version.minor < 4))) vertical_metrics = FALSE; if (needsTransform || format != GGO_BITMAP) load_flags |= FT_LOAD_NO_BITMAP; if (vertical_metrics) load_flags |= FT_LOAD_VERTICAL_LAYOUT; 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); return GDI_ERROR; } metrics = ft_face->glyph->metrics; if(font->fake_bold) { if (!get_bold_glyph_outline(ft_face->glyph, font->ppem, &metrics) && metrics.width) metrics.width += 1 << 6; } /* Some poorly-created fonts contain glyphs that exceed the boundaries set * by the text metrics. The proper behavior is to clip the glyph metrics to * fit within the maximums specified in the text metrics. */ if(incoming_font->potm || get_outline_text_metrics(incoming_font) || get_bitmap_text_metrics(incoming_font)) { TEXTMETRICW *ptm = &incoming_font->potm->otmTextMetrics; top = min( metrics.horiBearingY, ptm->tmAscent << 6 ); bottom = max( metrics.horiBearingY - metrics.height, -(ptm->tmDescent << 6) ); metrics.horiBearingY = top; metrics.height = top - bottom; /* TODO: Are we supposed to clip the width as well...? */ /* metrics.width = min( metrics.width, ptm->tmMaxCharWidth << 6 ); */ } if(!needsTransform) { left = (INT)(metrics.horiBearingX) & -64; right = (INT)((metrics.horiBearingX + metrics.width) + 63) & -64; top = (metrics.horiBearingY + 63) & -64; bottom = (metrics.horiBearingY - metrics.height) & -64; adv = get_advance_metric(incoming_font, font, &metrics, NULL, vertical_metrics); gm.gmCellIncX = adv.x >> 6; gm.gmCellIncY = 0; origin_x = left; origin_y = top; abc->abcA = origin_x >> 6; abc->abcB = (metrics.width + 63) >> 6; } else { INT xc, yc; FT_Vector vec; FT_Pos lsb; left = right = 0; for(xc = 0; xc < 2; xc++) { for(yc = 0; yc < 2; yc++) { vec.x = metrics.horiBearingX + xc * metrics.width; vec.y = metrics.horiBearingY - yc * metrics.height; TRACE("Vec %ld,%ld\n", vec.x, vec.y); pFT_Vector_Transform(&vec, &transMatTategaki); 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; if (tategaki && (font->potm || get_outline_text_metrics(font))) { if (vertical_metrics) lsb = metrics.horiBearingY + metrics.vertBearingY; else lsb = metrics.vertAdvance + (font->potm->otmDescent << 6); vec.x = lsb; vec.y = font->potm->otmDescent << 6; TRACE ("Vec %ld,%ld\n", vec.x>>6, vec.y>>6); pFT_Vector_Transform(&vec, &transMat); origin_x = (vec.x + left) & -64; origin_y = (vec.y + top + 63) & -64; lsb -= metrics.horiBearingY; } else { origin_x = left; origin_y = top; lsb = metrics.horiBearingX; } TRACE("transformed box: (%d,%d - %d,%d)\n", left, top, right, bottom); adv = get_advance_metric(incoming_font, font, &metrics, &transMat, vertical_metrics); gm.gmCellIncX = adv.x >> 6; gm.gmCellIncY = adv.y >> 6; adv = get_advance_metric(incoming_font, font, &metrics, &transMatUnrotated, vertical_metrics); adv.x = pFT_Vector_Length(&adv); adv.y = 0; vec.x = lsb; vec.y = 0; pFT_Vector_Transform(&vec, &transMatUnrotated); if(lsb > 0) abc->abcA = pFT_Vector_Length(&vec) >> 6; else abc->abcA = -((pFT_Vector_Length(&vec) + 63) >> 6); /* We use lsb again to avoid rounding errors */ vec.x = lsb + (tategaki ? metrics.height : metrics.width); vec.y = 0; pFT_Vector_Transform(&vec, &transMatUnrotated); abc->abcB = ((pFT_Vector_Length(&vec) + 63) >> 6) - abc->abcA; } width = (right - left) >> 6; height = (top - bottom) >> 6; gm.gmBlackBoxX = width ? width : 1; gm.gmBlackBoxY = height ? height : 1; gm.gmptGlyphOrigin.x = origin_x >> 6; gm.gmptGlyphOrigin.y = origin_y >> 6; if (!abc->abcB) abc->abcB = 1; abc->abcC = (adv.x >> 6) - abc->abcA - abc->abcB; TRACE("%u,%u,%s,%d,%d\n", gm.gmBlackBoxX, gm.gmBlackBoxY, wine_dbgstr_point(&gm.gmptGlyphOrigin), gm.gmCellIncX, gm.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 = gm; FONT_GM(font,original_index)->abc = *abc; FONT_GM(font,original_index)->init = TRUE; } if(format == GGO_METRICS) { *lpgm = gm; return 1; /* FIXME */ } if(ft_face->glyph->format != ft_glyph_format_outline && (format == GGO_NATIVE || format == GGO_BEZIER)) { TRACE("loaded a bitmap\n"); return GDI_ERROR; } switch(format) { case GGO_BITMAP: pitch = ((width + 31) >> 5) << 2; needed = pitch * height; if(!buf || !buflen) break; if (!needed) return GDI_ERROR; /* empty glyph */ if (needed > buflen) return GDI_ERROR; switch(ft_face->glyph->format) { case ft_glyph_format_bitmap: { BYTE *src = ft_face->glyph->bitmap.buffer, *dst = buf; INT w = min( pitch, (ft_face->glyph->bitmap.width + 7) >> 3 ); INT h = min( height, ft_face->glyph->bitmap.rows ); while(h--) { if (!font->fake_bold) memcpy(dst, src, w); else { INT x; dst[0] = 0; for (x = 0; x < w; x++) { dst[x ] = (dst[x] & 0x80) | (src[x] >> 1) | src[x]; if (x+1 < pitch) dst[x+1] = (src[x] & 0x01) << 7; } } 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, &transMatTategaki); 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); return GDI_ERROR; } break; case GGO_GRAY2_BITMAP: case GGO_GRAY4_BITMAP: case GGO_GRAY8_BITMAP: case WINE_GGO_GRAY16_BITMAP: { unsigned int max_level, row, col; BYTE *start, *ptr; pitch = (width + 3) / 4 * 4; needed = pitch * height; if(!buf || !buflen) break; if (!needed) return GDI_ERROR; /* empty glyph */ if (needed > buflen) return GDI_ERROR; max_level = get_max_level( format ); switch(ft_face->glyph->format) { case ft_glyph_format_bitmap: { BYTE *src = ft_face->glyph->bitmap.buffer, *dst = buf; INT h = min( height, ft_face->glyph->bitmap.rows ); INT x; memset( buf, 0, needed ); while(h--) { for(x = 0; x < pitch && x < ft_face->glyph->bitmap.width; x++) { if (src[x / 8] & masks[x % 8]) { dst[x] = max_level; if (font->fake_bold && x+1 < pitch) dst[x+1] = max_level; } } 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_grays; ft_bitmap.buffer = buf; if(needsTransform) pFT_Outline_Transform(&ft_face->glyph->outline, &transMatTategaki); 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 (max_level != 255) { for (row = 0, start = buf; row < height; row++) { for (col = 0, ptr = start; col < width; col++, ptr++) *ptr = (((int)*ptr) * (max_level + 1)) / 256; start += pitch; } } break; } default: FIXME("loaded glyph format %x\n", ft_face->glyph->format); return GDI_ERROR; } break; } case WINE_GGO_HRGB_BITMAP: case WINE_GGO_HBGR_BITMAP: case WINE_GGO_VRGB_BITMAP: case WINE_GGO_VBGR_BITMAP: { switch (ft_face->glyph->format) { case FT_GLYPH_FORMAT_BITMAP: { BYTE *src, *dst; INT src_pitch, x; pitch = width * 4; needed = pitch * height; if (!buf || !buflen) break; if (!needed) return GDI_ERROR; /* empty glyph */ if (needed > buflen) return GDI_ERROR; memset(buf, 0, buflen); dst = buf; src = ft_face->glyph->bitmap.buffer; src_pitch = ft_face->glyph->bitmap.pitch; height = min( height, ft_face->glyph->bitmap.rows ); while ( height-- ) { for (x = 0; x < width && x < ft_face->glyph->bitmap.width; x++) { if ( src[x / 8] & masks[x % 8] ) { ((unsigned int *)dst)[x] = ~0u; if (font->fake_bold && x+1 < width) ((unsigned int *)dst)[x+1] = ~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; const INT *sub_order; const INT rgb_order[3] = { 0, 1, 2 }; const INT bgr_order[3] = { 2, 1, 0 }; 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 (!width || !height) { if (!buf || !buflen) break; return GDI_ERROR; } if ( render_mode == FT_RENDER_MODE_LCD) { gm.gmBlackBoxX += 2; gm.gmptGlyphOrigin.x -= 1; left -= (1 << 6); } else { gm.gmBlackBoxY += 2; gm.gmptGlyphOrigin.y += 1; top += (1 << 6); } width = gm.gmBlackBoxX; height = gm.gmBlackBoxY; pitch = width * 4; needed = pitch * height; if (!buf || !buflen) break; if (needed > buflen) return GDI_ERROR; memset(buf, 0, buflen); dst = buf; if ( needsTransform ) pFT_Outline_Transform (&ft_face->glyph->outline, &transMatTategaki); #ifdef FT_LCD_FILTER_H if ( pFT_Library_SetLcdFilter ) pFT_Library_SetLcdFilter( library, FT_LCD_FILTER_DEFAULT ); #endif 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 - (left >> 6); if ( x_shift < 0 ) { src += hmul * -x_shift; src_width -= hmul * -x_shift; } else if ( x_shift > 0 ) { dst += x_shift; width -= x_shift; } y_shift = (top >> 6) - ft_face->glyph->bitmap_top; if ( y_shift < 0 ) { src += src_pitch * vmul * -y_shift; src_height -= vmul * -y_shift; } else if ( y_shift > 0 ) { dst += y_shift * ( pitch / sizeof(*dst) ); height -= y_shift; } width = min( width, src_width / hmul ); height = min( height, src_height / vmul ); sub_order = (format == WINE_GGO_HRGB_BITMAP || format == WINE_GGO_VRGB_BITMAP)? rgb_order : bgr_order; while ( height-- ) { for ( x = 0; x < width; x++ ) { dst[x] = ((unsigned int)src[hmul * x + rgb_interval * sub_order[0]] << 16) | ((unsigned int)src[hmul * x + rgb_interval * sub_order[1]] << 8) | ((unsigned int)src[hmul * x + rgb_interval * sub_order[2]]); } src += src_pitch * vmul; dst += pitch / sizeof(*dst); } break; } default: FIXME ("loaded glyph format %x\n", ft_face->glyph->format); return GDI_ERROR; } break; } case GGO_NATIVE: { FT_Outline *outline = &ft_face->glyph->outline; if(buflen == 0) buf = NULL; if (needsTransform && buf) pFT_Outline_Transform(outline, &transMatTategaki); needed = get_native_glyph_outline(outline, buflen, NULL); if (!buf || !buflen) break; if (needed > buflen) return GDI_ERROR; get_native_glyph_outline(outline, buflen, buf); break; } case GGO_BEZIER: { FT_Outline *outline = &ft_face->glyph->outline; if(buflen == 0) buf = NULL; if (needsTransform && buf) pFT_Outline_Transform(outline, &transMat); needed = get_bezier_glyph_outline(outline, buflen, NULL); if (!buf || !buflen) break; if (needed > buflen) return GDI_ERROR; get_bezier_glyph_outline(outline, buflen, buf); break; } default: FIXME("Unsupported format %d\n", format); return GDI_ERROR; } *lpgm = gm; return needed; } static BOOL get_bitmap_text_metrics(GdiFont *font) { FT_Face ft_face = font->ft_face; FT_WinFNT_HeaderRec winfnt_header; const DWORD size = offsetof(OUTLINETEXTMETRICW, otmFiller); font->potm = HeapAlloc(GetProcessHeap(), 0, size); font->potm->otmSize = size; #define TM font->potm->otmTextMetrics if(!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 { 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_IS_FIXED_WIDTH(ft_face) ? 0 : TMPF_FIXED_PITCH; TM.tmCharSet = font->charset; } if(font->fake_bold) TM.tmWeight = FW_BOLD; #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_X(ptm->tmOverhang); if(font->fake_bold) { if(!FT_IS_SCALABLE(font->ft_face)) ptm->tmOverhang++; ptm->tmAveCharWidth++; ptm->tmMaxCharWidth++; } 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); /* Windows scales these values as signed integers even if they are unsigned */ #define SCALE_X(x) (x) = GDI_ROUND((int)(x) * (scale_x)) #define SCALE_Y(y) (y) = GDI_ROUND((int)(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 } static BOOL get_text_metrics(GdiFont *font, LPTEXTMETRICW ptm) { if(!font->potm) { if (!get_outline_text_metrics(font) && !get_bitmap_text_metrics(font)) 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); 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; } static BOOL get_outline_text_metrics(GdiFont *font) { BOOL ret = FALSE; FT_Face ft_face = font->ft_face; UINT needed, lenfam, lensty, lenface, lenfull; TT_OS2 *pOS2; TT_HoriHeader *pHori; TT_Postscript *pPost; FT_Fixed em_scale; WCHAR *family_nameW, *style_nameW, *face_nameW, *full_nameW; char *cp; INT ascent, descent; USHORT windescent; TRACE("font=%p\n", font); if(!FT_IS_SCALABLE(ft_face)) return FALSE; needed = sizeof(*font->potm); lenfam = (strlenW(font->name) + 1) * sizeof(WCHAR); family_nameW = strdupW(font->name); style_nameW = get_face_name( ft_face, TT_NAME_ID_FONT_SUBFAMILY, GetSystemDefaultLangID() ); if (!style_nameW) { FIXME("failed to read style_nameW for font %s!\n", wine_dbgstr_w(font->name)); style_nameW = towstr( CP_ACP, ft_face->style_name ); } lensty = (strlenW(style_nameW) + 1) * sizeof(WCHAR); face_nameW = get_face_name( ft_face, TT_NAME_ID_FULL_NAME, GetSystemDefaultLangID() ); if (!face_nameW) { FIXME("failed to read face_nameW for font %s!\n", wine_dbgstr_w(font->name)); face_nameW = strdupW(font->name); } if (font->name[0] == '@') face_nameW = prepend_at( face_nameW ); lenface = (strlenW(face_nameW) + 1) * sizeof(WCHAR); full_nameW = get_face_name( ft_face, TT_NAME_ID_UNIQUE_ID, GetSystemDefaultLangID() ); if (!full_nameW) { WCHAR fake_nameW[] = {'f','a','k','e',' ','n','a','m','e', 0}; FIXME("failed to read full_nameW for font %s!\n", wine_dbgstr_w(font->name)); full_nameW = strdupW(fake_nameW); } lenfull = (strlenW(full_nameW) + 1) * sizeof(WCHAR); /* These names should be read from the TT name table */ /* length of otmpFamilyName */ needed += lenfam; /* length of otmpFaceName */ needed += lenface; /* length of otmpStyleName */ needed += lensty; /* length of otmpFullName */ needed += lenfull; em_scale = (FT_Fixed)MulDiv(font->ppem, 1 << 16, ft_face->units_per_EM); pOS2 = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_os2); if(!pOS2) { FIXME("Can't find OS/2 table - not TT font?\n"); goto end; } pHori = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_hhea); if(!pHori) { FIXME("Can't find HHEA table - not TT font?\n"); goto end; } pPost = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_post); /* we can live with this failing */ TRACE("OS/2 winA = %u winD = %u typoA = %d typoD = %d typoLG = %d avgW %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, pOS2->xAvgCharWidth, 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 windescent = get_fixed_windescent(pOS2->usWinDescent); if(pOS2->usWinAscent + windescent == 0) { ascent = pHori->Ascender; descent = -pHori->Descender; } else { ascent = pOS2->usWinAscent; descent = windescent; } font->ntmCellHeight = ascent + descent; font->ntmAvgWidth = pOS2->xAvgCharWidth; #define SCALE_X(x) (pFT_MulFix(x, em_scale)) #define SCALE_Y(y) (pFT_MulFix(y, em_scale)) 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 = SCALE_Y(ascent); TM.tmDescent = SCALE_Y(descent); TM.tmInternalLeading = SCALE_Y(ascent + descent - ft_face->units_per_EM); } TM.tmHeight = TM.tmAscent + TM.tmDescent; /* MSDN says: el = MAX(0, LineGap - ((WinAscent + WinDescent) - (Ascender - Descender))) */ TM.tmExternalLeading = max(0, SCALE_Y(pHori->Line_Gap - ((ascent + descent) - (pHori->Ascender - pHori->Descender)))); TM.tmAveCharWidth = SCALE_X(pOS2->xAvgCharWidth); if (TM.tmAveCharWidth == 0) { TM.tmAveCharWidth = 1; } TM.tmMaxCharWidth = SCALE_X(ft_face->bbox.xMax - ft_face->bbox.xMin); 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 = 96; /* FIXME */ TM.tmDigitizedAspectY = 96; /* FIXME */ /* 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 1255: /* Hebrew */ TM.tmLastChar = 0xf896; break; 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; if (font->fake_italic) font->potm->otmfsSelection |= 1; if (font->fake_bold) font->potm->otmfsSelection |= 1 << 5; /* Only return valid bits that define embedding and subsetting restrictions */ font->potm->otmfsType = pOS2->fsType & 0x30e; 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 = SCALE_Y(pOS2->sTypoAscender); font->potm->otmDescent = SCALE_Y(pOS2->sTypoDescender); font->potm->otmLineGap = SCALE_Y(pOS2->sTypoLineGap); font->potm->otmsCapEmHeight = SCALE_Y(pOS2->sCapHeight); font->potm->otmsXHeight = SCALE_Y(pOS2->sxHeight); font->potm->otmrcFontBox.left = SCALE_X(ft_face->bbox.xMin); font->potm->otmrcFontBox.right = SCALE_X(ft_face->bbox.xMax); font->potm->otmrcFontBox.top = SCALE_Y(ft_face->bbox.yMax); font->potm->otmrcFontBox.bottom = SCALE_Y(ft_face->bbox.yMin); font->potm->otmMacAscent = TM.tmAscent; font->potm->otmMacDescent = -TM.tmDescent; font->potm->otmMacLineGap = SCALE_Y(pHori->Line_Gap); font->potm->otmusMinimumPPEM = 0; /* TT Header */ font->potm->otmptSubscriptSize.x = SCALE_X(pOS2->ySubscriptXSize); font->potm->otmptSubscriptSize.y = SCALE_Y(pOS2->ySubscriptYSize); font->potm->otmptSubscriptOffset.x = SCALE_X(pOS2->ySubscriptXOffset); font->potm->otmptSubscriptOffset.y = SCALE_Y(pOS2->ySubscriptYOffset); font->potm->otmptSuperscriptSize.x = SCALE_X(pOS2->ySuperscriptXSize); font->potm->otmptSuperscriptSize.y = SCALE_Y(pOS2->ySuperscriptYSize); font->potm->otmptSuperscriptOffset.x = SCALE_X(pOS2->ySuperscriptXOffset); font->potm->otmptSuperscriptOffset.y = SCALE_Y(pOS2->ySuperscriptYOffset); font->potm->otmsStrikeoutSize = SCALE_Y(pOS2->yStrikeoutSize); font->potm->otmsStrikeoutPosition = SCALE_Y(pOS2->yStrikeoutPosition); if(!pPost) { font->potm->otmsUnderscoreSize = 0; font->potm->otmsUnderscorePosition = 0; } else { font->potm->otmsUnderscoreSize = SCALE_Y(pPost->underlineThickness); font->potm->otmsUnderscorePosition = SCALE_Y(pPost->underlinePosition); } #undef SCALE_X #undef SCALE_Y #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, face_nameW); cp += lenface; font->potm->otmpFullName = (LPSTR)(cp - (char*)font->potm); strcpyW((WCHAR*)cp, full_nameW); ret = TRUE; end: HeapFree(GetProcessHeap(), 0, style_nameW); HeapFree(GetProcessHeap(), 0, family_nameW); HeapFree(GetProcessHeap(), 0, face_nameW); HeapFree(GetProcessHeap(), 0, full_nameW); return ret; } /************************************************************* * freetype_GetGlyphOutline */ static DWORD freetype_GetGlyphOutline( PHYSDEV dev, UINT glyph, UINT format, LPGLYPHMETRICS lpgm, DWORD buflen, LPVOID buf, const MAT2 *lpmat ) { struct freetype_physdev *physdev = get_freetype_dev( dev ); DWORD ret; ABC abc; if (!physdev->font) { dev = GET_NEXT_PHYSDEV( dev, pGetGlyphOutline ); return dev->funcs->pGetGlyphOutline( dev, glyph, format, lpgm, buflen, buf, lpmat ); } GDI_CheckNotLock(); EnterCriticalSection( &freetype_cs ); ret = get_glyph_outline( physdev->font, glyph, format, lpgm, &abc, buflen, buf, lpmat ); LeaveCriticalSection( &freetype_cs ); return ret; } /************************************************************* * freetype_GetTextMetrics */ static BOOL freetype_GetTextMetrics( PHYSDEV dev, TEXTMETRICW *metrics ) { struct freetype_physdev *physdev = get_freetype_dev( dev ); BOOL ret; if (!physdev->font) { dev = GET_NEXT_PHYSDEV( dev, pGetTextMetrics ); return dev->funcs->pGetTextMetrics( dev, metrics ); } GDI_CheckNotLock(); EnterCriticalSection( &freetype_cs ); ret = get_text_metrics( physdev->font, metrics ); LeaveCriticalSection( &freetype_cs ); return ret; } /************************************************************* * freetype_GetOutlineTextMetrics */ static UINT freetype_GetOutlineTextMetrics( PHYSDEV dev, UINT cbSize, OUTLINETEXTMETRICW *potm ) { struct freetype_physdev *physdev = get_freetype_dev( dev ); UINT ret = 0; if (!physdev->font) { dev = GET_NEXT_PHYSDEV( dev, pGetOutlineTextMetrics ); return dev->funcs->pGetOutlineTextMetrics( dev, cbSize, potm ); } TRACE("font=%p\n", physdev->font); if (!FT_IS_SCALABLE( physdev->font->ft_face )) return 0; GDI_CheckNotLock(); EnterCriticalSection( &freetype_cs ); if (physdev->font->potm || get_outline_text_metrics( physdev->font )) { if(potm && cbSize >= physdev->font->potm->otmSize) { memcpy(potm, physdev->font->potm, physdev->font->potm->otmSize); scale_outline_font_metrics(physdev->font, potm); } ret = physdev->font->potm->otmSize; } LeaveCriticalSection( &freetype_cs ); return ret; } static BOOL load_child_font(GdiFont *font, CHILD_FONT *child) { 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; child->font->name = strdupW(child->face->family->FamilyName); child->font->base_font = font; TRACE("created child font %p for base %p\n", child->font, font); return TRUE; } static BOOL get_glyph_index_linked(GdiFont *font, UINT c, GdiFont **linked_font, FT_UInt *glyph, BOOL* vert) { FT_UInt g,o; CHILD_FONT *child_font; if(font->base_font) font = font->base_font; *linked_font = font; if((*glyph = get_glyph_index(font, c))) { o = *glyph; *glyph = get_GSUB_vert_glyph(font, *glyph); *vert = (o != *glyph); return TRUE; } if (c < 32) goto done; /* don't check linked fonts for control characters */ 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); o = g; g = get_GSUB_vert_glyph(child_font->font, g); if(g) { *glyph = g; *linked_font = child_font->font; *vert = (o != g); return TRUE; } } done: *vert = FALSE; return FALSE; } /************************************************************* * freetype_GetCharWidth */ static BOOL freetype_GetCharWidth( PHYSDEV dev, UINT firstChar, UINT lastChar, LPINT buffer ) { static const MAT2 identity = { {0,1},{0,0},{0,0},{0,1} }; UINT c; GLYPHMETRICS gm; ABC abc; struct freetype_physdev *physdev = get_freetype_dev( dev ); if (!physdev->font) { dev = GET_NEXT_PHYSDEV( dev, pGetCharWidth ); return dev->funcs->pGetCharWidth( dev, firstChar, lastChar, buffer ); } TRACE("%p, %d, %d, %p\n", physdev->font, firstChar, lastChar, buffer); GDI_CheckNotLock(); EnterCriticalSection( &freetype_cs ); for(c = firstChar; c <= lastChar; c++) { get_glyph_outline( physdev->font, c, GGO_METRICS, &gm, &abc, 0, NULL, &identity ); buffer[c - firstChar] = abc.abcA + abc.abcB + abc.abcC; } LeaveCriticalSection( &freetype_cs ); return TRUE; } /************************************************************* * freetype_GetCharABCWidths */ static BOOL freetype_GetCharABCWidths( PHYSDEV dev, UINT firstChar, UINT lastChar, LPABC buffer ) { static const MAT2 identity = { {0,1},{0,0},{0,0},{0,1} }; UINT c; GLYPHMETRICS gm; struct freetype_physdev *physdev = get_freetype_dev( dev ); if (!physdev->font) { dev = GET_NEXT_PHYSDEV( dev, pGetCharABCWidths ); return dev->funcs->pGetCharABCWidths( dev, firstChar, lastChar, buffer ); } TRACE("%p, %d, %d, %p\n", physdev->font, firstChar, lastChar, buffer); GDI_CheckNotLock(); EnterCriticalSection( &freetype_cs ); for(c = firstChar; c <= lastChar; c++, buffer++) get_glyph_outline( physdev->font, c, GGO_METRICS, &gm, buffer, 0, NULL, &identity ); LeaveCriticalSection( &freetype_cs ); return TRUE; } /************************************************************* * freetype_GetCharABCWidthsI */ static BOOL freetype_GetCharABCWidthsI( PHYSDEV dev, UINT firstChar, UINT count, LPWORD pgi, LPABC buffer ) { static const MAT2 identity = { {0,1},{0,0},{0,0},{0,1} }; UINT c; GLYPHMETRICS gm; struct freetype_physdev *physdev = get_freetype_dev( dev ); if (!physdev->font) { dev = GET_NEXT_PHYSDEV( dev, pGetCharABCWidthsI ); return dev->funcs->pGetCharABCWidthsI( dev, firstChar, count, pgi, buffer ); } if(!FT_HAS_HORIZONTAL(physdev->font->ft_face)) return FALSE; GDI_CheckNotLock(); EnterCriticalSection( &freetype_cs ); for(c = 0; c < count; c++, buffer++) get_glyph_outline( physdev->font, pgi ? pgi[c] : firstChar + c, GGO_METRICS | GGO_GLYPH_INDEX, &gm, buffer, 0, NULL, &identity ); LeaveCriticalSection( &freetype_cs ); return TRUE; } /************************************************************* * freetype_GetTextExtentExPoint */ static BOOL freetype_GetTextExtentExPoint( PHYSDEV dev, LPCWSTR wstr, INT count, LPINT dxs ) { static const MAT2 identity = { {0,1},{0,0},{0,0},{0,1} }; INT idx, pos; ABC abc; GLYPHMETRICS gm; struct freetype_physdev *physdev = get_freetype_dev( dev ); if (!physdev->font) { dev = GET_NEXT_PHYSDEV( dev, pGetTextExtentExPoint ); return dev->funcs->pGetTextExtentExPoint( dev, wstr, count, dxs ); } TRACE("%p, %s, %d\n", physdev->font, debugstr_wn(wstr, count), count); GDI_CheckNotLock(); EnterCriticalSection( &freetype_cs ); for (idx = pos = 0; idx < count; idx++) { get_glyph_outline( physdev->font, wstr[idx], GGO_METRICS, &gm, &abc, 0, NULL, &identity ); pos += abc.abcA + abc.abcB + abc.abcC; dxs[idx] = pos; } LeaveCriticalSection( &freetype_cs ); return TRUE; } /************************************************************* * freetype_GetTextExtentExPointI */ static BOOL freetype_GetTextExtentExPointI( PHYSDEV dev, const WORD *indices, INT count, LPINT dxs ) { static const MAT2 identity = { {0,1},{0,0},{0,0},{0,1} }; INT idx, pos; ABC abc; GLYPHMETRICS gm; struct freetype_physdev *physdev = get_freetype_dev( dev ); if (!physdev->font) { dev = GET_NEXT_PHYSDEV( dev, pGetTextExtentExPointI ); return dev->funcs->pGetTextExtentExPointI( dev, indices, count, dxs ); } TRACE("%p, %p, %d\n", physdev->font, indices, count); GDI_CheckNotLock(); EnterCriticalSection( &freetype_cs ); for (idx = pos = 0; idx < count; idx++) { get_glyph_outline( physdev->font, indices[idx], GGO_METRICS | GGO_GLYPH_INDEX, &gm, &abc, 0, NULL, &identity ); pos += abc.abcA + abc.abcB + abc.abcC; dxs[idx] = pos; } LeaveCriticalSection( &freetype_cs ); return TRUE; } /************************************************************* * freetype_GetFontData */ static DWORD freetype_GetFontData( PHYSDEV dev, DWORD table, DWORD offset, LPVOID buf, DWORD cbData ) { struct freetype_physdev *physdev = get_freetype_dev( dev ); if (!physdev->font) { dev = GET_NEXT_PHYSDEV( dev, pGetFontData ); return dev->funcs->pGetFontData( dev, table, offset, buf, cbData ); } TRACE("font=%p, table=%s, offset=0x%x, buf=%p, cbData=0x%x\n", physdev->font, debugstr_an((char*)&table, 4), offset, buf, cbData); return get_font_data( physdev->font, table, offset, buf, cbData ); } /************************************************************* * freetype_GetTextFace */ static INT freetype_GetTextFace( PHYSDEV dev, INT count, LPWSTR str ) { INT n; struct freetype_physdev *physdev = get_freetype_dev( dev ); if (!physdev->font) { dev = GET_NEXT_PHYSDEV( dev, pGetTextFace ); return dev->funcs->pGetTextFace( dev, count, str ); } n = strlenW(physdev->font->name) + 1; if (str) { lstrcpynW(str, physdev->font->name, count); n = min(count, n); } return n; } /************************************************************* * freetype_GetTextCharsetInfo */ static UINT freetype_GetTextCharsetInfo( PHYSDEV dev, LPFONTSIGNATURE fs, DWORD flags ) { struct freetype_physdev *physdev = get_freetype_dev( dev ); if (!physdev->font) { dev = GET_NEXT_PHYSDEV( dev, pGetTextCharsetInfo ); return dev->funcs->pGetTextCharsetInfo( dev, fs, flags ); } if (fs) *fs = physdev->font->fs; return physdev->font->charset; } /* 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) { 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 { DWORD encoding = RtlUlongByteSwap(face->charmap->encoding); FIXME("encoding %s not supported\n", debugstr_an((char *)&encoding, 4)); } return num_ranges; } /************************************************************* * freetype_GetFontUnicodeRanges */ static DWORD freetype_GetFontUnicodeRanges( PHYSDEV dev, LPGLYPHSET glyphset ) { struct freetype_physdev *physdev = get_freetype_dev( dev ); DWORD size, num_ranges; if (!physdev->font) { dev = GET_NEXT_PHYSDEV( dev, pGetFontUnicodeRanges ); return dev->funcs->pGetFontUnicodeRanges( dev, glyphset ); } num_ranges = get_font_unicode_ranges(physdev->font->ft_face, glyphset); size = sizeof(GLYPHSET) + sizeof(WCRANGE) * (num_ranges - 1); if (glyphset) { glyphset->cbThis = size; glyphset->cRanges = num_ranges; glyphset->flAccel = 0; } return size; } /************************************************************* * freetype_FontIsLinked */ static BOOL freetype_FontIsLinked( PHYSDEV dev ) { struct freetype_physdev *physdev = get_freetype_dev( dev ); BOOL ret; if (!physdev->font) { dev = GET_NEXT_PHYSDEV( dev, pFontIsLinked ); return dev->funcs->pFontIsLinked( dev ); } GDI_CheckNotLock(); EnterCriticalSection( &freetype_cs ); ret = !list_empty(&physdev->font->child_fonts); LeaveCriticalSection( &freetype_cs ); return ret; } /************************************************************************* * GetRasterizerCaps (GDI32.@) */ BOOL WINAPI GetRasterizerCaps( LPRASTERIZER_STATUS lprs, UINT cbNumBytes) { lprs->nSize = sizeof(RASTERIZER_STATUS); lprs->wFlags = TT_AVAILABLE | TT_ENABLED; lprs->nLanguageID = 0; return TRUE; } /************************************************************* * freetype_GetFontRealizationInfo */ static BOOL freetype_GetFontRealizationInfo( PHYSDEV dev, void *ptr ) { struct freetype_physdev *physdev = get_freetype_dev( dev ); struct font_realization_info *info = ptr; if (!physdev->font) { dev = GET_NEXT_PHYSDEV( dev, pGetFontRealizationInfo ); return dev->funcs->pGetFontRealizationInfo( dev, ptr ); } TRACE("(%p, %p)\n", physdev->font, info); info->flags = 1; if(FT_IS_SCALABLE(physdev->font->ft_face)) info->flags |= 2; info->cache_num = physdev->font->cache_num; info->instance_id = physdev->font->instance_id; if (info->size == sizeof(*info)) { info->unk = 0; info->face_index = physdev->font->ft_face->face_index; info->simulations = 0; if (physdev->font->fake_bold) info->simulations |= 0x1; if (physdev->font->fake_italic) info->simulations |= 0x2; } return TRUE; } /************************************************************************* * GetFontFileInfo (GDI32.@) */ BOOL WINAPI GetFontFileInfo( DWORD instance_id, DWORD unknown, struct font_fileinfo *info, DWORD size, DWORD *needed ) { struct font_handle_entry *entry = handle_entry( instance_id ); const GdiFont *font; if (!entry) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } font = entry->obj; *needed = sizeof(*info) + strlenW(font->fileinfo->path) * sizeof(WCHAR); if (*needed > size) { SetLastError(ERROR_INSUFFICIENT_BUFFER); return FALSE; } /* path is included too */ memcpy(info, font->fileinfo, *needed); return TRUE; } /************************************************************************* * Kerning support for TrueType fonts */ 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; } /************************************************************* * freetype_GetKerningPairs */ static DWORD freetype_GetKerningPairs( PHYSDEV dev, 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; GdiFont *font; struct freetype_physdev *physdev = get_freetype_dev( dev ); if (!(font = physdev->font)) { dev = GET_NEXT_PHYSDEV( dev, pGetKerningPairs ); return dev->funcs->pGetKerningPairs( dev, cPairs, kern_pair ); } 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)); } else cPairs = font->total_kern_pairs; LeaveCriticalSection( &freetype_cs ); return cPairs; } font->total_kern_pairs = 0; length = get_font_data(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; } get_font_data(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) { 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 { DWORD encoding = RtlUlongByteSwap(font->ft_face->charmap->encoding); ULONG n; FIXME("encoding %s not supported\n", debugstr_an((char *)&encoding, 4)); 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)); } else cPairs = font->total_kern_pairs; LeaveCriticalSection( &freetype_cs ); return cPairs; } static const struct gdi_dc_funcs freetype_funcs = { NULL, /* pAbortDoc */ NULL, /* pAbortPath */ NULL, /* pAlphaBlend */ NULL, /* pAngleArc */ NULL, /* pArc */ NULL, /* pArcTo */ NULL, /* pBeginPath */ NULL, /* pBlendImage */ NULL, /* pChord */ NULL, /* pCloseFigure */ NULL, /* pCreateCompatibleDC */ freetype_CreateDC, /* pCreateDC */ freetype_DeleteDC, /* pDeleteDC */ NULL, /* pDeleteObject */ NULL, /* pDeviceCapabilities */ NULL, /* pEllipse */ NULL, /* pEndDoc */ NULL, /* pEndPage */ NULL, /* pEndPath */ freetype_EnumFonts, /* pEnumFonts */ NULL, /* pEnumICMProfiles */ NULL, /* pExcludeClipRect */ NULL, /* pExtDeviceMode */ NULL, /* pExtEscape */ NULL, /* pExtFloodFill */ NULL, /* pExtSelectClipRgn */ NULL, /* pExtTextOut */ NULL, /* pFillPath */ NULL, /* pFillRgn */ NULL, /* pFlattenPath */ freetype_FontIsLinked, /* pFontIsLinked */ NULL, /* pFrameRgn */ NULL, /* pGdiComment */ NULL, /* pGetBoundsRect */ freetype_GetCharABCWidths, /* pGetCharABCWidths */ freetype_GetCharABCWidthsI, /* pGetCharABCWidthsI */ freetype_GetCharWidth, /* pGetCharWidth */ NULL, /* pGetDeviceCaps */ NULL, /* pGetDeviceGammaRamp */ freetype_GetFontData, /* pGetFontData */ freetype_GetFontRealizationInfo, /* pGetFontRealizationInfo */ freetype_GetFontUnicodeRanges, /* pGetFontUnicodeRanges */ freetype_GetGlyphIndices, /* pGetGlyphIndices */ freetype_GetGlyphOutline, /* pGetGlyphOutline */ NULL, /* pGetICMProfile */ NULL, /* pGetImage */ freetype_GetKerningPairs, /* pGetKerningPairs */ NULL, /* pGetNearestColor */ freetype_GetOutlineTextMetrics, /* pGetOutlineTextMetrics */ NULL, /* pGetPixel */ NULL, /* pGetSystemPaletteEntries */ freetype_GetTextCharsetInfo, /* pGetTextCharsetInfo */ freetype_GetTextExtentExPoint, /* pGetTextExtentExPoint */ freetype_GetTextExtentExPointI, /* pGetTextExtentExPointI */ freetype_GetTextFace, /* pGetTextFace */ freetype_GetTextMetrics, /* pGetTextMetrics */ NULL, /* pGradientFill */ NULL, /* pIntersectClipRect */ NULL, /* pInvertRgn */ NULL, /* pLineTo */ NULL, /* pModifyWorldTransform */ NULL, /* pMoveTo */ NULL, /* pOffsetClipRgn */ NULL, /* pOffsetViewportOrg */ NULL, /* pOffsetWindowOrg */ NULL, /* pPaintRgn */ NULL, /* pPatBlt */ NULL, /* pPie */ NULL, /* pPolyBezier */ NULL, /* pPolyBezierTo */ NULL, /* pPolyDraw */ NULL, /* pPolyPolygon */ NULL, /* pPolyPolyline */ NULL, /* pPolygon */ NULL, /* pPolyline */ NULL, /* pPolylineTo */ NULL, /* pPutImage */ NULL, /* pRealizeDefaultPalette */ NULL, /* pRealizePalette */ NULL, /* pRectangle */ NULL, /* pResetDC */ NULL, /* pRestoreDC */ NULL, /* pRoundRect */ NULL, /* pSaveDC */ NULL, /* pScaleViewportExt */ NULL, /* pScaleWindowExt */ NULL, /* pSelectBitmap */ NULL, /* pSelectBrush */ NULL, /* pSelectClipPath */ freetype_SelectFont, /* pSelectFont */ NULL, /* pSelectPalette */ NULL, /* pSelectPen */ NULL, /* pSetArcDirection */ NULL, /* pSetBkColor */ NULL, /* pSetBkMode */ NULL, /* pSetDCBrushColor */ NULL, /* pSetDCPenColor */ NULL, /* pSetDIBColorTable */ NULL, /* pSetDIBitsToDevice */ NULL, /* pSetDeviceClipping */ NULL, /* pSetDeviceGammaRamp */ NULL, /* pSetLayout */ NULL, /* pSetMapMode */ NULL, /* pSetMapperFlags */ NULL, /* pSetPixel */ NULL, /* pSetPolyFillMode */ NULL, /* pSetROP2 */ NULL, /* pSetRelAbs */ NULL, /* pSetStretchBltMode */ NULL, /* pSetTextAlign */ NULL, /* pSetTextCharacterExtra */ NULL, /* pSetTextColor */ NULL, /* pSetTextJustification */ NULL, /* pSetViewportExt */ NULL, /* pSetViewportOrg */ NULL, /* pSetWindowExt */ NULL, /* pSetWindowOrg */ NULL, /* pSetWorldTransform */ NULL, /* pStartDoc */ NULL, /* pStartPage */ NULL, /* pStretchBlt */ NULL, /* pStretchDIBits */ NULL, /* pStrokeAndFillPath */ NULL, /* pStrokePath */ NULL, /* pUnrealizePalette */ NULL, /* pWidenPath */ NULL, /* wine_get_wgl_driver */ NULL, /* wine_get_vulkan_driver */ GDI_PRIORITY_FONT_DRV /* priority */ }; #else /* HAVE_FREETYPE */ struct font_fileinfo; /*************************************************************************/ BOOL WineEngInit(void) { return FALSE; } 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; } BOOL WineEngCreateScalableFontResource( DWORD hidden, LPCWSTR resource, LPCWSTR font_file, LPCWSTR font_path ) { FIXME("stub\n"); 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; } /************************************************************************* * GetFontFileInfo (GDI32.@) */ BOOL WINAPI GetFontFileInfo( DWORD instance_id, DWORD unknown, struct font_fileinfo *info, DWORD size, DWORD *needed) { *needed = 0; return FALSE; } #endif /* HAVE_FREETYPE */