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