2001-09-12 22:21:06 +02:00
|
|
|
/*
|
|
|
|
* FreeType font engine interface
|
|
|
|
*
|
|
|
|
* Copyright 2001 Huw D M Davies for CodeWeavers.
|
2006-09-20 17:18:51 +02:00
|
|
|
* Copyright 2006 Dmitry Timoshkov for CodeWeavers.
|
2001-09-12 22:21:06 +02:00
|
|
|
*
|
|
|
|
* This file contains the WineEng* functions.
|
2002-03-10 00:29:33 +01:00
|
|
|
*
|
|
|
|
* 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
|
2006-05-18 14:49:52 +02:00
|
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
2001-09-12 22:21:06 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
2004-01-15 01:35:38 +01:00
|
|
|
#include "wine/port.h"
|
2001-09-12 22:21:06 +02:00
|
|
|
|
2003-09-06 01:08:26 +02:00
|
|
|
#include <stdarg.h>
|
|
|
|
#include <stdlib.h>
|
2005-05-06 18:22:54 +02:00
|
|
|
#ifdef HAVE_SYS_STAT_H
|
|
|
|
# include <sys/stat.h>
|
|
|
|
#endif
|
2006-12-06 21:31:53 +01:00
|
|
|
#ifdef HAVE_SYS_MMAN_H
|
|
|
|
# include <sys/mman.h>
|
|
|
|
#endif
|
2003-09-06 01:08:26 +02:00
|
|
|
#include <string.h>
|
2008-10-19 18:55:13 +02:00
|
|
|
#ifdef HAVE_DIRENT_H
|
|
|
|
# include <dirent.h>
|
|
|
|
#endif
|
2003-09-06 01:08:26 +02:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <assert.h>
|
|
|
|
|
2006-12-21 16:21:13 +01:00
|
|
|
#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 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 */
|
|
|
|
|
2013-11-30 11:22:25 +01:00
|
|
|
#ifdef HAVE_FT2BUILD_H
|
|
|
|
#include <ft2build.h>
|
|
|
|
#include FT_FREETYPE_H
|
|
|
|
#include FT_GLYPH_H
|
|
|
|
#include FT_TYPES_H
|
|
|
|
#include FT_TRUETYPE_TABLES_H
|
|
|
|
#include FT_SFNT_NAMES_H
|
|
|
|
#include FT_TRUETYPE_IDS_H
|
|
|
|
#include FT_OUTLINE_H
|
|
|
|
#include FT_TRIGONOMETRY_H
|
|
|
|
#include FT_MODULE_H
|
|
|
|
#include FT_WINFONTS_H
|
|
|
|
#ifdef FT_LCD_FILTER_H
|
|
|
|
#include FT_LCD_FILTER_H
|
|
|
|
#endif
|
|
|
|
#endif /* HAVE_FT2BUILD_H */
|
|
|
|
|
2001-09-12 22:21:06 +02:00
|
|
|
#include "windef.h"
|
2003-09-06 01:08:26 +02:00
|
|
|
#include "winbase.h"
|
2006-04-04 13:12:41 +02:00
|
|
|
#include "winternl.h"
|
2001-09-12 22:21:06 +02:00
|
|
|
#include "winerror.h"
|
|
|
|
#include "winreg.h"
|
|
|
|
#include "wingdi.h"
|
2004-01-15 01:35:38 +01:00
|
|
|
#include "gdi_private.h"
|
2001-09-12 22:21:06 +02:00
|
|
|
#include "wine/unicode.h"
|
2002-04-03 22:02:39 +02:00
|
|
|
#include "wine/debug.h"
|
2004-08-04 20:16:13 +02:00
|
|
|
#include "wine/list.h"
|
2001-09-12 22:21:06 +02:00
|
|
|
|
2011-11-03 14:11:35 +01:00
|
|
|
#include "resource.h"
|
|
|
|
|
2002-03-10 00:29:33 +01:00
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(font);
|
2001-09-12 22:21:06 +02:00
|
|
|
|
|
|
|
#ifdef HAVE_FREETYPE
|
|
|
|
|
2006-02-22 13:24:25 +01:00
|
|
|
#ifndef HAVE_FT_TRUETYPEENGINETYPE
|
|
|
|
typedef enum
|
|
|
|
{
|
|
|
|
FT_TRUETYPE_ENGINE_TYPE_NONE = 0,
|
|
|
|
FT_TRUETYPE_ENGINE_TYPE_UNPATENTED,
|
|
|
|
FT_TRUETYPE_ENGINE_TYPE_PATENTED
|
|
|
|
} FT_TrueTypeEngineType;
|
2006-01-24 11:04:20 +01:00
|
|
|
#endif
|
|
|
|
|
2001-09-12 22:21:06 +02:00
|
|
|
static FT_Library library = 0;
|
2002-08-17 20:34:34 +02:00
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
FT_Int major;
|
|
|
|
FT_Int minor;
|
|
|
|
FT_Int patch;
|
|
|
|
} FT_Version_t;
|
|
|
|
static FT_Version_t FT_Version;
|
2004-06-16 22:06:26 +02:00
|
|
|
static DWORD FT_SimpleVersion;
|
2019-02-08 02:51:32 +01:00
|
|
|
#define FT_VERSION_VALUE(major, minor, patch) (((major) << 16) | ((minor) << 8) | (patch))
|
2001-09-12 22:21:06 +02:00
|
|
|
|
2002-04-03 22:02:39 +02:00
|
|
|
static void *ft_handle = NULL;
|
|
|
|
|
2002-09-23 22:45:57 +02:00
|
|
|
#define MAKE_FUNCPTR(f) static typeof(f) * p##f = NULL
|
|
|
|
MAKE_FUNCPTR(FT_Done_Face);
|
|
|
|
MAKE_FUNCPTR(FT_Get_Char_Index);
|
2011-10-11 11:14:56 +02:00
|
|
|
MAKE_FUNCPTR(FT_Get_First_Char);
|
|
|
|
MAKE_FUNCPTR(FT_Get_Next_Char);
|
2006-04-05 13:45:25 +02:00
|
|
|
MAKE_FUNCPTR(FT_Get_Sfnt_Name);
|
|
|
|
MAKE_FUNCPTR(FT_Get_Sfnt_Name_Count);
|
2002-09-23 22:45:57 +02:00
|
|
|
MAKE_FUNCPTR(FT_Get_Sfnt_Table);
|
2011-10-11 11:14:56 +02:00
|
|
|
MAKE_FUNCPTR(FT_Get_WinFNT_Header);
|
2002-09-23 22:45:57 +02:00
|
|
|
MAKE_FUNCPTR(FT_Init_FreeType);
|
2011-10-11 11:14:56 +02:00
|
|
|
MAKE_FUNCPTR(FT_Library_Version);
|
2002-09-23 22:45:57 +02:00
|
|
|
MAKE_FUNCPTR(FT_Load_Glyph);
|
2011-10-10 15:15:56 +02:00
|
|
|
MAKE_FUNCPTR(FT_Load_Sfnt_Table);
|
2003-10-10 02:06:35 +02:00
|
|
|
MAKE_FUNCPTR(FT_Matrix_Multiply);
|
2008-09-03 13:14:17 +02:00
|
|
|
#ifdef FT_MULFIX_INLINED
|
|
|
|
#define pFT_MulFix FT_MULFIX_INLINED
|
|
|
|
#else
|
2002-09-23 22:45:57 +02:00
|
|
|
MAKE_FUNCPTR(FT_MulFix);
|
2008-09-03 13:14:17 +02:00
|
|
|
#endif
|
2002-09-23 22:45:57 +02:00
|
|
|
MAKE_FUNCPTR(FT_New_Face);
|
2006-12-06 21:31:53 +01:00
|
|
|
MAKE_FUNCPTR(FT_New_Memory_Face);
|
2002-09-23 22:45:57 +02:00
|
|
|
MAKE_FUNCPTR(FT_Outline_Get_Bitmap);
|
2013-10-17 14:25:10 +02:00
|
|
|
MAKE_FUNCPTR(FT_Outline_Get_CBox);
|
2002-09-23 22:45:57 +02:00
|
|
|
MAKE_FUNCPTR(FT_Outline_Transform);
|
|
|
|
MAKE_FUNCPTR(FT_Outline_Translate);
|
2011-10-10 15:15:56 +02:00
|
|
|
MAKE_FUNCPTR(FT_Render_Glyph);
|
2007-10-02 05:04:25 +02:00
|
|
|
MAKE_FUNCPTR(FT_Set_Charmap);
|
2002-09-23 22:45:57 +02:00
|
|
|
MAKE_FUNCPTR(FT_Set_Pixel_Sizes);
|
2016-01-13 15:09:08 +01:00
|
|
|
MAKE_FUNCPTR(FT_Vector_Length);
|
2003-10-10 02:06:35 +02:00
|
|
|
MAKE_FUNCPTR(FT_Vector_Transform);
|
2011-10-10 15:15:56 +02:00
|
|
|
MAKE_FUNCPTR(FT_Vector_Unit);
|
2013-10-17 14:25:10 +02:00
|
|
|
static FT_Error (*pFT_Outline_Embolden)(FT_Outline *, FT_Pos);
|
2006-02-22 13:24:25 +01:00
|
|
|
static FT_TrueTypeEngineType (*pFT_Get_TrueType_Engine_Type)(FT_Library);
|
2013-11-30 11:22:25 +01:00
|
|
|
#ifdef FT_LCD_FILTER_H
|
2008-12-23 12:34:01 +01:00
|
|
|
static FT_Error (*pFT_Library_SetLcdFilter)(FT_Library, FT_LcdFilter);
|
|
|
|
#endif
|
2019-02-19 11:25:41 +01:00
|
|
|
static FT_Error (*pFT_Property_Set)(FT_Library, const FT_String *, const FT_String *, const void *);
|
2002-04-03 22:02:39 +02:00
|
|
|
|
2007-07-02 17:31:48 +02:00
|
|
|
#ifdef SONAME_LIBFONTCONFIG
|
2003-11-09 01:30:13 +01:00
|
|
|
#include <fontconfig/fontconfig.h>
|
2012-11-01 12:35:52 +01:00
|
|
|
MAKE_FUNCPTR(FcConfigSubstitute);
|
2018-11-27 16:50:20 +01:00
|
|
|
MAKE_FUNCPTR(FcDefaultSubstitute);
|
2003-11-09 01:30:13 +01:00
|
|
|
MAKE_FUNCPTR(FcFontList);
|
2018-11-27 16:50:20 +01:00
|
|
|
MAKE_FUNCPTR(FcFontMatch);
|
2003-11-09 01:30:13 +01:00
|
|
|
MAKE_FUNCPTR(FcFontSetDestroy);
|
|
|
|
MAKE_FUNCPTR(FcInit);
|
2018-11-27 16:50:20 +01:00
|
|
|
MAKE_FUNCPTR(FcPatternAddString);
|
2003-11-09 01:30:13 +01:00
|
|
|
MAKE_FUNCPTR(FcPatternCreate);
|
|
|
|
MAKE_FUNCPTR(FcPatternDestroy);
|
2006-12-19 22:21:27 +01:00
|
|
|
MAKE_FUNCPTR(FcPatternGetBool);
|
2012-11-01 12:35:52 +01:00
|
|
|
MAKE_FUNCPTR(FcPatternGetInteger);
|
2006-04-28 13:51:36 +02:00
|
|
|
MAKE_FUNCPTR(FcPatternGetString);
|
2018-12-19 12:01:35 +01:00
|
|
|
#ifndef FC_NAMELANG
|
|
|
|
#define FC_NAMELANG "namelang"
|
2003-11-09 01:30:13 +01:00
|
|
|
#endif
|
2018-12-19 12:01:35 +01:00
|
|
|
#ifndef FC_PRGNAME
|
|
|
|
#define FC_PRGNAME "prgname"
|
|
|
|
#endif
|
|
|
|
#endif /* SONAME_LIBFONTCONFIG */
|
2003-11-09 01:30:13 +01:00
|
|
|
|
|
|
|
#undef MAKE_FUNCPTR
|
|
|
|
|
2007-09-20 12:10:31 +02:00
|
|
|
#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
|
|
|
|
|
2004-10-18 21:38:30 +02:00
|
|
|
#ifndef ft_encoding_none
|
2004-09-15 20:03:32 +02:00
|
|
|
#define FT_ENCODING_NONE ft_encoding_none
|
|
|
|
#endif
|
2004-10-18 21:38:30 +02:00
|
|
|
#ifndef ft_encoding_ms_symbol
|
2004-09-15 20:03:32 +02:00
|
|
|
#define FT_ENCODING_MS_SYMBOL ft_encoding_symbol
|
|
|
|
#endif
|
2004-10-18 21:38:30 +02:00
|
|
|
#ifndef ft_encoding_unicode
|
2004-09-15 20:03:32 +02:00
|
|
|
#define FT_ENCODING_UNICODE ft_encoding_unicode
|
|
|
|
#endif
|
2004-10-18 21:38:30 +02:00
|
|
|
#ifndef ft_encoding_apple_roman
|
2004-09-15 20:03:32 +02:00
|
|
|
#define FT_ENCODING_APPLE_ROMAN ft_encoding_apple_roman
|
|
|
|
#endif
|
2003-11-09 01:30:13 +01:00
|
|
|
|
2006-04-04 13:12:41 +02:00
|
|
|
#ifdef WORDS_BIGENDIAN
|
|
|
|
#define GET_BE_WORD(x) (x)
|
2016-08-12 12:14:50 +02:00
|
|
|
#define GET_BE_DWORD(x) (x)
|
2006-04-04 13:12:41 +02:00
|
|
|
#else
|
|
|
|
#define GET_BE_WORD(x) RtlUshortByteSwap(x)
|
2016-08-12 12:14:50 +02:00
|
|
|
#define GET_BE_DWORD(x) RtlUlongByteSwap(x)
|
2006-04-04 13:12:41 +02:00
|
|
|
#endif
|
2002-05-23 04:53:10 +02:00
|
|
|
|
2016-08-12 12:14:49 +02:00
|
|
|
/* 'gasp' flags */
|
|
|
|
#define GASP_GRIDFIT 0x01
|
|
|
|
#define GASP_DOGRAY 0x02
|
|
|
|
|
2016-03-17 09:23:24 +01:00
|
|
|
#ifndef WINE_FONT_DIR
|
|
|
|
#define WINE_FONT_DIR "fonts"
|
|
|
|
#endif
|
|
|
|
|
2008-03-25 18:35:45 +01:00
|
|
|
/* This is basically a copy of FT_Bitmap_Size with an extra element added */
|
2004-06-16 22:06:26 +02:00
|
|
|
typedef struct {
|
|
|
|
FT_Short height;
|
|
|
|
FT_Short width;
|
2004-08-18 00:08:31 +02:00
|
|
|
FT_Pos size;
|
2004-06-16 22:06:26 +02:00
|
|
|
FT_Pos x_ppem;
|
|
|
|
FT_Pos y_ppem;
|
|
|
|
FT_Short internal_leading;
|
|
|
|
} Bitmap_Size;
|
|
|
|
|
2004-07-14 23:42:35 +02:00
|
|
|
/* 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;
|
|
|
|
|
2008-01-16 07:46:24 +01:00
|
|
|
struct enum_data
|
|
|
|
{
|
|
|
|
ENUMLOGFONTEXW elf;
|
|
|
|
NEWTEXTMETRICEXW ntm;
|
|
|
|
DWORD type;
|
|
|
|
};
|
|
|
|
|
2001-09-12 22:21:06 +02:00
|
|
|
typedef struct tagFace {
|
2004-08-18 00:08:31 +02:00
|
|
|
struct list entry;
|
2013-01-14 15:19:14 +01:00
|
|
|
unsigned int refcount;
|
2020-09-07 15:18:57 +02:00
|
|
|
WCHAR *style_name;
|
2020-09-07 15:18:56 +02:00
|
|
|
WCHAR *full_name;
|
2012-11-01 13:16:26 +01:00
|
|
|
WCHAR *file;
|
2007-09-13 19:42:55 +02:00
|
|
|
void *font_data_ptr;
|
|
|
|
DWORD font_data_size;
|
2002-06-22 03:19:29 +02:00
|
|
|
FT_Long face_index;
|
2003-06-23 22:51:06 +02:00
|
|
|
FONTSIGNATURE fs;
|
2008-04-07 09:11:42 +02:00
|
|
|
DWORD ntmFlags;
|
2003-06-23 22:51:06 +02:00
|
|
|
FT_Fixed font_version;
|
2004-06-16 22:06:26 +02:00
|
|
|
BOOL scalable;
|
|
|
|
Bitmap_Size size; /* set if face is a bitmap */
|
2013-01-14 15:28:42 +01:00
|
|
|
DWORD flags; /* ADDFONT flags */
|
2003-06-23 22:51:06 +02:00
|
|
|
struct tagFamily *family;
|
2007-12-11 14:09:21 +01:00
|
|
|
/* Cached data for Enum */
|
2008-01-16 07:46:24 +01:00
|
|
|
struct enum_data *cached_enum_data;
|
2001-09-12 22:21:06 +02:00
|
|
|
} Face;
|
|
|
|
|
2020-06-04 11:43:15 +02:00
|
|
|
#define FS_DBCS_MASK (FS_JISJAPAN|FS_CHINESESIMP|FS_WANSUNG|FS_CHINESETRAD|FS_JOHAB)
|
|
|
|
|
2013-01-14 15:28:42 +01:00
|
|
|
#define ADDFONT_EXTERNAL_FONT 0x01
|
|
|
|
#define ADDFONT_ALLOW_BITMAP 0x02
|
|
|
|
#define ADDFONT_ADD_TO_CACHE 0x04
|
|
|
|
#define ADDFONT_ADD_RESOURCE 0x08 /* added through AddFontResource */
|
2013-01-15 14:13:09 +01:00
|
|
|
#define ADDFONT_VERTICAL_FONT 0x10
|
2013-01-14 15:28:42 +01:00
|
|
|
#define ADDFONT_AA_FLAGS(flags) ((flags) << 16)
|
|
|
|
|
2001-09-12 22:21:06 +02:00
|
|
|
typedef struct tagFamily {
|
2004-08-18 00:08:31 +02:00
|
|
|
struct list entry;
|
2013-01-14 15:19:14 +01:00
|
|
|
unsigned int refcount;
|
2020-09-07 15:18:58 +02:00
|
|
|
WCHAR family_name[LF_FACESIZE];
|
2020-09-17 19:30:43 +02:00
|
|
|
WCHAR second_name[LF_FACESIZE];
|
2004-08-18 00:08:31 +02:00
|
|
|
struct list faces;
|
2012-03-08 14:33:20 +01:00
|
|
|
struct list *replacement;
|
2001-09-12 22:21:06 +02:00
|
|
|
} Family;
|
|
|
|
|
2012-12-05 21:13:54 +01:00
|
|
|
typedef struct tagGdiFont GdiFont;
|
|
|
|
|
2005-09-06 11:24:07 +02:00
|
|
|
typedef struct {
|
|
|
|
struct list entry;
|
2007-09-06 20:40:00 +02:00
|
|
|
Face *face;
|
2006-09-28 08:11:06 +02:00
|
|
|
GdiFont *font;
|
2005-09-06 11:24:07 +02:00
|
|
|
} CHILD_FONT;
|
|
|
|
|
2001-09-12 22:21:06 +02:00
|
|
|
struct tagGdiFont {
|
2020-10-20 22:05:37 +02:00
|
|
|
struct gdi_font *gdi_font;
|
2008-02-05 21:16:06 +01:00
|
|
|
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 */
|
2001-09-12 22:21:06 +02:00
|
|
|
FT_Face ft_face;
|
2006-12-06 21:31:53 +01:00
|
|
|
struct font_mapping *mapping;
|
2006-09-28 08:11:06 +02:00
|
|
|
GdiFont *base_font;
|
2008-03-13 10:40:58 +01:00
|
|
|
VOID *GSUB_Table;
|
2013-05-20 18:24:21 +02:00
|
|
|
const VOID *vert_feature;
|
2001-09-12 22:21:06 +02:00
|
|
|
};
|
|
|
|
|
2020-10-20 22:05:37 +02:00
|
|
|
static inline GdiFont *get_font_ptr( struct gdi_font *font ) { return font->private; }
|
|
|
|
|
2005-09-06 11:24:07 +02:00
|
|
|
typedef struct {
|
|
|
|
struct list entry;
|
2006-05-26 06:34:50 +02:00
|
|
|
const WCHAR *font_name;
|
2012-03-08 14:33:02 +01:00
|
|
|
FONTSIGNATURE fs;
|
2005-09-06 11:24:07 +02:00
|
|
|
struct list links;
|
|
|
|
} SYSTEM_LINKS;
|
|
|
|
|
2011-04-01 14:20:00 +02:00
|
|
|
struct enum_charset_element {
|
|
|
|
DWORD mask;
|
|
|
|
DWORD charset;
|
2011-11-03 14:11:35 +01:00
|
|
|
WCHAR name[LF_FACESIZE];
|
2011-04-01 14:20:00 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
struct enum_charset_list {
|
|
|
|
DWORD total;
|
|
|
|
struct enum_charset_element element[32];
|
|
|
|
};
|
|
|
|
|
2005-09-06 11:24:07 +02:00
|
|
|
static struct list system_links = LIST_INIT(system_links);
|
2002-01-29 04:02:50 +01:00
|
|
|
|
2006-03-30 15:38:07 +02:00
|
|
|
static struct list font_subst_list = LIST_INIT(font_subst_list);
|
|
|
|
|
2004-08-18 00:08:31 +02:00
|
|
|
static struct list font_list = LIST_INIT(font_list);
|
2001-09-12 22:21:06 +02:00
|
|
|
|
2020-10-20 22:05:37 +02:00
|
|
|
static const struct font_backend_funcs font_funcs;
|
2011-10-18 11:44:41 +02:00
|
|
|
|
2007-09-12 08:05:28 +02:00
|
|
|
static const WCHAR fontsW[] = {'\\','f','o','n','t','s','\0'};
|
2004-03-09 04:43:54 +01:00
|
|
|
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'};
|
|
|
|
|
2004-06-16 22:06:26 +02:00
|
|
|
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'};
|
|
|
|
|
2011-12-16 13:08:31 +01:00
|
|
|
static const WCHAR * const SystemFontValues[] = {
|
2004-06-16 22:06:26 +02:00
|
|
|
System_Value,
|
|
|
|
OEMFont_Value,
|
2004-07-14 23:42:35 +02:00
|
|
|
FixedSys_Value,
|
2004-06-16 22:06:26 +02:00
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
2005-06-14 14:33:19 +02:00
|
|
|
static const WCHAR external_fonts_reg_key[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\',
|
2004-03-09 04:43:54 +01:00
|
|
|
'F','o','n','t','s','\\','E','x','t','e','r','n','a','l',' ','F','o','n','t','s','\0'};
|
2002-01-29 04:02:50 +01:00
|
|
|
|
2009-05-09 19:12:40 +02:00
|
|
|
/* 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};
|
|
|
|
|
2011-10-12 12:55:12 +02:00
|
|
|
static const WCHAR arial[] = {'A','r','i','a','l',0};
|
|
|
|
static const WCHAR bitstream_vera_sans[] = {'B','i','t','s','t','r','e','a','m',' ','V','e','r','a',' ','S','a','n','s',0};
|
|
|
|
static const WCHAR bitstream_vera_sans_mono[] = {'B','i','t','s','t','r','e','a','m',' ','V','e','r','a',' ','S','a','n','s',' ','M','o','n','o',0};
|
|
|
|
static const WCHAR bitstream_vera_serif[] = {'B','i','t','s','t','r','e','a','m',' ','V','e','r','a',' ','S','e','r','i','f',0};
|
|
|
|
static const WCHAR courier_new[] = {'C','o','u','r','i','e','r',' ','N','e','w',0};
|
2011-10-12 12:55:13 +02:00
|
|
|
static const WCHAR liberation_mono[] = {'L','i','b','e','r','a','t','i','o','n',' ','M','o','n','o',0};
|
|
|
|
static const WCHAR liberation_sans[] = {'L','i','b','e','r','a','t','i','o','n',' ','S','a','n','s',0};
|
|
|
|
static const WCHAR liberation_serif[] = {'L','i','b','e','r','a','t','i','o','n',' ','S','e','r','i','f',0};
|
2011-10-12 12:55:12 +02:00
|
|
|
static const WCHAR times_new_roman[] = {'T','i','m','e','s',' ','N','e','w',' ','R','o','m','a','n',0};
|
2011-11-03 14:11:35 +01:00
|
|
|
static const WCHAR SymbolW[] = {'S','y','m','b','o','l','\0'};
|
2011-10-12 12:55:12 +02:00
|
|
|
|
|
|
|
static const WCHAR *default_serif_list[] =
|
|
|
|
{
|
|
|
|
times_new_roman,
|
2011-10-12 12:55:13 +02:00
|
|
|
liberation_serif,
|
2011-10-12 12:55:12 +02:00
|
|
|
bitstream_vera_serif,
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
|
|
|
static const WCHAR *default_fixed_list[] =
|
|
|
|
{
|
|
|
|
courier_new,
|
2011-10-12 12:55:13 +02:00
|
|
|
liberation_mono,
|
2011-10-12 12:55:12 +02:00
|
|
|
bitstream_vera_sans_mono,
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
|
|
|
static const WCHAR *default_sans_list[] =
|
|
|
|
{
|
|
|
|
arial,
|
2011-10-12 12:55:13 +02:00
|
|
|
liberation_sans,
|
2011-10-12 12:55:12 +02:00
|
|
|
bitstream_vera_sans,
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
2018-12-20 11:32:14 +01:00
|
|
|
static const WCHAR *default_serif = times_new_roman;
|
|
|
|
static const WCHAR *default_fixed = courier_new;
|
|
|
|
static const WCHAR *default_sans = arial;
|
|
|
|
|
2002-04-03 22:51:20 +02:00
|
|
|
typedef struct {
|
2006-03-30 15:38:07 +02:00
|
|
|
WCHAR *name;
|
|
|
|
INT charset;
|
2002-04-03 22:51:20 +02:00
|
|
|
} NameCs;
|
|
|
|
|
|
|
|
typedef struct tagFontSubst {
|
2006-03-30 15:38:07 +02:00
|
|
|
struct list entry;
|
|
|
|
NameCs from;
|
|
|
|
NameCs to;
|
2002-04-03 22:51:20 +02:00
|
|
|
} FontSubst;
|
|
|
|
|
2011-10-06 23:26:06 +02:00
|
|
|
/* Registry font cache key and value names */
|
|
|
|
static const WCHAR wine_fonts_key[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\',
|
|
|
|
'F','o','n','t','s',0};
|
|
|
|
static const WCHAR wine_fonts_cache_key[] = {'C','a','c','h','e',0};
|
|
|
|
|
|
|
|
|
2006-12-06 21:31:53 +01:00
|
|
|
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 );
|
|
|
|
|
2012-11-05 22:32:51 +01:00
|
|
|
static UINT default_aa_flags;
|
2013-01-15 13:56:52 +01:00
|
|
|
static HKEY hkey_font_cache;
|
2015-09-29 09:27:15 +02:00
|
|
|
static BOOL antialias_fakes = TRUE;
|
2012-11-05 22:32:51 +01:00
|
|
|
|
2004-03-09 04:43:54 +01:00
|
|
|
static const WCHAR font_mutex_nameW[] = {'_','_','W','I','N','E','_','F','O','N','T','_','M','U','T','E','X','_','_','\0'};
|
|
|
|
|
2008-02-18 16:49:07 +01:00
|
|
|
static const WCHAR szDefaultFallbackLink[] = {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0};
|
|
|
|
|
2020-06-04 11:43:15 +02:00
|
|
|
static BOOL map_font_family(const WCHAR *orig, const WCHAR *repl);
|
2013-06-04 16:05:50 +02:00
|
|
|
static BOOL get_glyph_index_linked(GdiFont *font, UINT c, GdiFont **linked_font, FT_UInt *glyph, BOOL *vert);
|
2020-10-26 11:43:24 +01:00
|
|
|
static BOOL CDECL freetype_set_outline_text_metrics( struct gdi_font *font );
|
|
|
|
static BOOL CDECL freetype_set_bitmap_text_metrics( struct gdi_font *font );
|
2013-01-15 12:34:40 +01:00
|
|
|
static void remove_face_from_cache( Face *face );
|
2004-06-16 22:06:26 +02:00
|
|
|
|
2009-05-09 19:12:40 +02:00
|
|
|
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};
|
2012-02-07 14:18:34 +01:00
|
|
|
|
2004-06-16 22:06:26 +02:00
|
|
|
/****************************************
|
|
|
|
* 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
|
2005-01-05 18:12:07 +01:00
|
|
|
* OEMFONT.FON Terminal
|
2004-06-16 22:06:26 +02:00
|
|
|
* LogPixels Current dpi set by the display control panel applet
|
2008-03-25 18:35:45 +01:00
|
|
|
* (HKLM\\Software\\Microsoft\\Windows NT\\CurrentVersion\\FontDPI
|
2004-06-16 22:06:26 +02:00
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
|
2008-03-13 10:40:58 +01:00
|
|
|
/* These are all structures needed for the GSUB table */
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
DWORD version;
|
|
|
|
WORD ScriptList;
|
|
|
|
WORD FeatureList;
|
|
|
|
WORD LookupList;
|
|
|
|
} GSUB_Header;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
CHAR ScriptTag[4];
|
|
|
|
WORD Script;
|
|
|
|
} GSUB_ScriptRecord;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
WORD ScriptCount;
|
|
|
|
GSUB_ScriptRecord ScriptRecord[1];
|
|
|
|
} GSUB_ScriptList;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
CHAR LangSysTag[4];
|
|
|
|
WORD LangSys;
|
|
|
|
} GSUB_LangSysRecord;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
WORD DefaultLangSys;
|
|
|
|
WORD LangSysCount;
|
|
|
|
GSUB_LangSysRecord LangSysRecord[1];
|
|
|
|
} GSUB_Script;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
WORD LookupOrder; /* Reserved */
|
|
|
|
WORD ReqFeatureIndex;
|
|
|
|
WORD FeatureCount;
|
|
|
|
WORD FeatureIndex[1];
|
|
|
|
} GSUB_LangSys;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
CHAR FeatureTag[4];
|
|
|
|
WORD Feature;
|
|
|
|
} GSUB_FeatureRecord;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
WORD FeatureCount;
|
|
|
|
GSUB_FeatureRecord FeatureRecord[1];
|
|
|
|
} GSUB_FeatureList;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
WORD FeatureParams; /* Reserved */
|
|
|
|
WORD LookupCount;
|
|
|
|
WORD LookupListIndex[1];
|
|
|
|
} GSUB_Feature;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
WORD LookupCount;
|
|
|
|
WORD Lookup[1];
|
|
|
|
} GSUB_LookupList;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
WORD LookupType;
|
|
|
|
WORD LookupFlag;
|
|
|
|
WORD SubTableCount;
|
|
|
|
WORD SubTable[1];
|
|
|
|
} GSUB_LookupTable;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
WORD CoverageFormat;
|
|
|
|
WORD GlyphCount;
|
|
|
|
WORD GlyphArray[1];
|
|
|
|
} GSUB_CoverageFormat1;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
WORD Start;
|
|
|
|
WORD End;
|
|
|
|
WORD StartCoverageIndex;
|
|
|
|
} GSUB_RangeRecord;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
WORD CoverageFormat;
|
|
|
|
WORD RangeCount;
|
|
|
|
GSUB_RangeRecord RangeRecord[1];
|
|
|
|
} GSUB_CoverageFormat2;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
WORD SubstFormat; /* = 1 */
|
|
|
|
WORD Coverage;
|
|
|
|
WORD DeltaGlyphID;
|
|
|
|
} GSUB_SingleSubstFormat1;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
WORD SubstFormat; /* = 2 */
|
|
|
|
WORD Coverage;
|
|
|
|
WORD GlyphCount;
|
|
|
|
WORD Substitute[1];
|
|
|
|
}GSUB_SingleSubstFormat2;
|
|
|
|
|
2006-12-21 16:21:13 +01:00
|
|
|
#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;
|
2019-07-10 10:50:17 +02:00
|
|
|
ResFileRefNum res_ref;
|
2006-12-21 16:21:13 +01:00
|
|
|
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;
|
2007-09-20 12:10:31 +02:00
|
|
|
ResType fond_res = FT_MAKE_TAG('F','O','N','D');
|
2006-12-21 16:21:13 +01:00
|
|
|
|
2007-06-25 23:34:34 +02:00
|
|
|
fond = Get1IndResource(fond_res, idx);
|
2006-12-21 16:21:13 +01:00
|
|
|
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;
|
2007-09-20 12:10:31 +02:00
|
|
|
ResType sfnt_res = FT_MAKE_TAG('s','f','n','t');
|
2006-12-21 16:21:13 +01:00
|
|
|
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);
|
2007-06-25 23:34:34 +02:00
|
|
|
sfnt = GetResource(sfnt_res, font_id);
|
2006-12-21 16:21:13 +01:00
|
|
|
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 */
|
2004-06-16 22:06:26 +02:00
|
|
|
|
2004-03-09 04:43:54 +01:00
|
|
|
static inline BOOL is_win9x(void)
|
|
|
|
{
|
|
|
|
return GetVersion() & 0x80000000;
|
|
|
|
}
|
2003-10-10 02:06:35 +02:00
|
|
|
/*
|
2008-06-30 00:23:07 +02:00
|
|
|
This function builds an FT_Fixed from a double. It fails if the absolute
|
|
|
|
value of the float number is greater than 32768.
|
2003-10-10 02:06:35 +02:00
|
|
|
*/
|
2008-06-24 09:10:27 +02:00
|
|
|
static inline FT_Fixed FT_FixedFromFloat(double f)
|
2003-10-10 02:06:35 +02:00
|
|
|
{
|
2008-06-30 00:23:07 +02:00
|
|
|
return f * 0x10000;
|
2003-10-10 02:06:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
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)
|
|
|
|
{
|
2009-06-02 10:16:38 +02:00
|
|
|
return (FT_Fixed)((int)f.value << 16 | (unsigned int)f.fract);
|
2003-10-10 02:06:35 +02:00
|
|
|
}
|
|
|
|
|
2012-11-02 17:21:28 +01:00
|
|
|
static BOOL is_hinting_enabled(void)
|
|
|
|
{
|
|
|
|
static int enabled = -1;
|
|
|
|
|
|
|
|
if (enabled == -1)
|
|
|
|
{
|
|
|
|
/* Use the >= 2.2.0 function if available */
|
|
|
|
if (pFT_Get_TrueType_Engine_Type)
|
|
|
|
{
|
|
|
|
FT_TrueTypeEngineType type = pFT_Get_TrueType_Engine_Type(library);
|
|
|
|
enabled = (type == FT_TRUETYPE_ENGINE_TYPE_PATENTED);
|
|
|
|
}
|
|
|
|
else enabled = FALSE;
|
2012-11-05 22:31:31 +01:00
|
|
|
TRACE("hinting is %senabled\n", enabled ? "" : "NOT ");
|
2012-11-02 17:21:28 +01:00
|
|
|
}
|
|
|
|
return enabled;
|
|
|
|
}
|
|
|
|
|
|
|
|
static BOOL is_subpixel_rendering_enabled( void )
|
|
|
|
{
|
|
|
|
static int enabled = -1;
|
|
|
|
if (enabled == -1)
|
2012-11-05 22:31:31 +01:00
|
|
|
{
|
2018-10-09 07:11:07 +02:00
|
|
|
/* FreeType >= 2.8.1 offers LCD-optimezed rendering without lcd filters. */
|
2019-02-08 02:51:32 +01:00
|
|
|
if (FT_SimpleVersion >= FT_VERSION_VALUE(2, 8, 1))
|
2018-10-09 07:11:07 +02:00
|
|
|
enabled = TRUE;
|
|
|
|
#ifdef FT_LCD_FILTER_H
|
|
|
|
else if (pFT_Library_SetLcdFilter &&
|
|
|
|
pFT_Library_SetLcdFilter( NULL, 0 ) != FT_Err_Unimplemented_Feature)
|
|
|
|
enabled = TRUE;
|
|
|
|
#endif
|
|
|
|
else enabled = FALSE;
|
|
|
|
|
2012-11-05 22:31:31 +01:00
|
|
|
TRACE("subpixel rendering is %senabled\n", enabled ? "" : "NOT ");
|
|
|
|
}
|
2012-11-02 17:21:28 +01:00
|
|
|
return enabled;
|
|
|
|
}
|
|
|
|
|
2006-04-05 13:41:49 +02:00
|
|
|
|
2012-03-14 13:01:49 +01:00
|
|
|
static const struct list *get_face_list_from_family(const Family *family)
|
|
|
|
{
|
|
|
|
if (!list_empty(&family->faces))
|
|
|
|
return &family->faces;
|
|
|
|
else
|
|
|
|
return family->replacement;
|
|
|
|
}
|
|
|
|
|
2006-04-10 13:35:23 +02:00
|
|
|
static Face *find_face_from_filename(const WCHAR *file_name, const WCHAR *face_name)
|
2006-04-05 13:41:49 +02:00
|
|
|
{
|
|
|
|
Family *family;
|
|
|
|
Face *face;
|
2012-11-01 13:16:26 +01:00
|
|
|
const WCHAR *file;
|
2006-04-05 13:41:49 +02:00
|
|
|
|
2012-11-01 13:16:26 +01:00
|
|
|
TRACE("looking for file %s name %s\n", debugstr_w(file_name), debugstr_w(face_name));
|
2006-04-05 13:41:49 +02:00
|
|
|
|
|
|
|
LIST_FOR_EACH_ENTRY(family, &font_list, Family, entry)
|
|
|
|
{
|
2012-03-14 13:01:49 +01:00
|
|
|
const struct list *face_list;
|
2020-09-07 15:18:58 +02:00
|
|
|
if (face_name && strncmpiW( face_name, family->family_name, LF_FACESIZE - 1 )) continue;
|
2012-03-14 13:01:49 +01:00
|
|
|
face_list = get_face_list_from_family(family);
|
|
|
|
LIST_FOR_EACH_ENTRY(face, face_list, Face, entry)
|
2006-04-05 13:41:49 +02:00
|
|
|
{
|
2007-09-13 19:42:55 +02:00
|
|
|
if (!face->file)
|
|
|
|
continue;
|
2020-10-06 15:12:09 +02:00
|
|
|
file = strrchrW(face->file, '\\');
|
2006-04-05 13:41:49 +02:00
|
|
|
if(!file)
|
|
|
|
file = face->file;
|
|
|
|
else
|
|
|
|
file++;
|
2013-01-14 15:19:14 +01:00
|
|
|
if(strcmpiW(file, file_name)) continue;
|
|
|
|
face->refcount++;
|
|
|
|
return face;
|
2006-04-05 13:41:49 +02:00
|
|
|
}
|
|
|
|
}
|
2006-09-12 23:41:43 +02:00
|
|
|
return NULL;
|
2006-04-05 13:41:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static Family *find_family_from_name(const WCHAR *name)
|
|
|
|
{
|
|
|
|
Family *family;
|
|
|
|
|
|
|
|
LIST_FOR_EACH_ENTRY(family, &font_list, Family, entry)
|
2020-09-07 15:18:58 +02:00
|
|
|
if (!strncmpiW( family->family_name, name, LF_FACESIZE - 1 )) return family;
|
2006-04-05 13:41:49 +02:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
2007-03-07 06:45:11 +01:00
|
|
|
|
2012-02-08 15:18:27 +01:00
|
|
|
static Family *find_family_from_any_name(const WCHAR *name)
|
|
|
|
{
|
|
|
|
Family *family;
|
|
|
|
|
|
|
|
LIST_FOR_EACH_ENTRY(family, &font_list, Family, entry)
|
|
|
|
{
|
2020-09-07 15:18:58 +02:00
|
|
|
if (!strncmpiW( family->family_name, name, LF_FACESIZE - 1 )) return family;
|
2020-09-17 19:30:43 +02:00
|
|
|
if (!strncmpiW( family->second_name, name, LF_FACESIZE - 1 )) return family;
|
2012-02-08 15:18:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2006-04-05 13:41:49 +02:00
|
|
|
static void DumpSubstList(void)
|
|
|
|
{
|
|
|
|
FontSubst *psub;
|
|
|
|
|
|
|
|
LIST_FOR_EACH_ENTRY(psub, &font_subst_list, FontSubst, entry)
|
|
|
|
{
|
|
|
|
if(psub->from.charset != -1 || psub->to.charset != -1)
|
|
|
|
TRACE("%s:%d -> %s:%d\n", debugstr_w(psub->from.name),
|
|
|
|
psub->from.charset, debugstr_w(psub->to.name), psub->to.charset);
|
|
|
|
else
|
|
|
|
TRACE("%s -> %s\n", debugstr_w(psub->from.name),
|
|
|
|
debugstr_w(psub->to.name));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static LPWSTR strdupW(LPCWSTR p)
|
|
|
|
{
|
|
|
|
LPWSTR ret;
|
|
|
|
DWORD len = (strlenW(p) + 1) * sizeof(WCHAR);
|
|
|
|
ret = HeapAlloc(GetProcessHeap(), 0, len);
|
|
|
|
memcpy(ret, p, len);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static FontSubst *get_font_subst(const struct list *subst_list, const WCHAR *from_name,
|
|
|
|
INT from_charset)
|
|
|
|
{
|
|
|
|
FontSubst *element;
|
|
|
|
|
|
|
|
LIST_FOR_EACH_ENTRY(element, subst_list, FontSubst, entry)
|
|
|
|
{
|
|
|
|
if(!strcmpiW(element->from.name, from_name) &&
|
|
|
|
(element->from.charset == from_charset ||
|
|
|
|
element->from.charset == -1))
|
|
|
|
return element;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define ADD_FONT_SUBST_FORCE 1
|
|
|
|
|
|
|
|
static BOOL add_font_subst(struct list *subst_list, FontSubst *subst, INT flags)
|
|
|
|
{
|
|
|
|
FontSubst *from_exist, *to_exist;
|
|
|
|
|
|
|
|
from_exist = get_font_subst(subst_list, subst->from.name, subst->from.charset);
|
|
|
|
|
|
|
|
if(from_exist && (flags & ADD_FONT_SUBST_FORCE))
|
|
|
|
{
|
|
|
|
list_remove(&from_exist->entry);
|
2012-10-24 20:53:03 +02:00
|
|
|
HeapFree(GetProcessHeap(), 0, from_exist->from.name);
|
|
|
|
HeapFree(GetProcessHeap(), 0, from_exist->to.name);
|
2006-04-05 13:41:49 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2006-04-05 13:43:12 +02:00
|
|
|
HeapFree(GetProcessHeap(), 0, subst->from.name);
|
|
|
|
HeapFree(GetProcessHeap(), 0, subst->to.name);
|
|
|
|
HeapFree(GetProcessHeap(), 0, subst);
|
2006-04-05 13:41:49 +02:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2011-12-20 13:04:56 +01:00
|
|
|
static WCHAR *towstr(UINT cp, const char *str)
|
|
|
|
{
|
|
|
|
int len;
|
|
|
|
WCHAR *wstr;
|
|
|
|
|
|
|
|
len = MultiByteToWideChar(cp, 0, str, -1, NULL, 0);
|
|
|
|
wstr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
|
|
|
|
MultiByteToWideChar(cp, 0, str, -1, wstr, len);
|
|
|
|
return wstr;
|
|
|
|
}
|
|
|
|
|
2006-04-05 13:41:49 +02:00
|
|
|
static void split_subst_info(NameCs *nc, LPSTR str)
|
|
|
|
{
|
|
|
|
CHAR *p = strrchr(str, ',');
|
|
|
|
|
|
|
|
nc->charset = -1;
|
|
|
|
if(p && *(p+1)) {
|
|
|
|
nc->charset = strtol(p+1, NULL, 10);
|
|
|
|
*p = '\0';
|
|
|
|
}
|
2011-12-20 13:04:56 +01:00
|
|
|
nc->name = towstr(CP_ACP, str);
|
2006-04-05 13:41:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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 */
|
2008-06-22 15:41:47 +02:00
|
|
|
if ((psub->from.charset && psub->to.charset != psub->from.charset) ||
|
2006-04-05 13:41:49 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-09-18 13:23:08 +02:00
|
|
|
|
2013-06-20 15:04:11 +02:00
|
|
|
static const LANGID mac_langid_table[] =
|
|
|
|
{
|
|
|
|
MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ENGLISH */
|
|
|
|
MAKELANGID(LANG_FRENCH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_FRENCH */
|
|
|
|
MAKELANGID(LANG_GERMAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_GERMAN */
|
|
|
|
MAKELANGID(LANG_ITALIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ITALIAN */
|
|
|
|
MAKELANGID(LANG_DUTCH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_DUTCH */
|
|
|
|
MAKELANGID(LANG_SWEDISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SWEDISH */
|
|
|
|
MAKELANGID(LANG_SPANISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SPANISH */
|
|
|
|
MAKELANGID(LANG_DANISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_DANISH */
|
|
|
|
MAKELANGID(LANG_PORTUGUESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_PORTUGUESE */
|
|
|
|
MAKELANGID(LANG_NORWEGIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_NORWEGIAN */
|
|
|
|
MAKELANGID(LANG_HEBREW,SUBLANG_DEFAULT), /* TT_MAC_LANGID_HEBREW */
|
|
|
|
MAKELANGID(LANG_JAPANESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_JAPANESE */
|
|
|
|
MAKELANGID(LANG_ARABIC,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ARABIC */
|
|
|
|
MAKELANGID(LANG_FINNISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_FINNISH */
|
|
|
|
MAKELANGID(LANG_GREEK,SUBLANG_DEFAULT), /* TT_MAC_LANGID_GREEK */
|
|
|
|
MAKELANGID(LANG_ICELANDIC,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ICELANDIC */
|
|
|
|
MAKELANGID(LANG_MALTESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MALTESE */
|
|
|
|
MAKELANGID(LANG_TURKISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TURKISH */
|
|
|
|
MAKELANGID(LANG_CROATIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_CROATIAN */
|
|
|
|
MAKELANGID(LANG_CHINESE_TRADITIONAL,SUBLANG_DEFAULT), /* TT_MAC_LANGID_CHINESE_TRADITIONAL */
|
|
|
|
MAKELANGID(LANG_URDU,SUBLANG_DEFAULT), /* TT_MAC_LANGID_URDU */
|
|
|
|
MAKELANGID(LANG_HINDI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_HINDI */
|
|
|
|
MAKELANGID(LANG_THAI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_THAI */
|
|
|
|
MAKELANGID(LANG_KOREAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_KOREAN */
|
|
|
|
MAKELANGID(LANG_LITHUANIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_LITHUANIAN */
|
|
|
|
MAKELANGID(LANG_POLISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_POLISH */
|
|
|
|
MAKELANGID(LANG_HUNGARIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_HUNGARIAN */
|
|
|
|
MAKELANGID(LANG_ESTONIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ESTONIAN */
|
|
|
|
MAKELANGID(LANG_LATVIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_LETTISH */
|
|
|
|
MAKELANGID(LANG_SAMI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SAAMISK */
|
|
|
|
MAKELANGID(LANG_FAEROESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_FAEROESE */
|
|
|
|
MAKELANGID(LANG_FARSI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_FARSI */
|
|
|
|
MAKELANGID(LANG_RUSSIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_RUSSIAN */
|
|
|
|
MAKELANGID(LANG_CHINESE_SIMPLIFIED,SUBLANG_DEFAULT), /* TT_MAC_LANGID_CHINESE_SIMPLIFIED */
|
|
|
|
MAKELANGID(LANG_DUTCH,SUBLANG_DUTCH_BELGIAN), /* TT_MAC_LANGID_FLEMISH */
|
|
|
|
MAKELANGID(LANG_IRISH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_IRISH */
|
|
|
|
MAKELANGID(LANG_ALBANIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ALBANIAN */
|
|
|
|
MAKELANGID(LANG_ROMANIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ROMANIAN */
|
|
|
|
MAKELANGID(LANG_CZECH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_CZECH */
|
|
|
|
MAKELANGID(LANG_SLOVAK,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SLOVAK */
|
|
|
|
MAKELANGID(LANG_SLOVENIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SLOVENIAN */
|
|
|
|
0, /* TT_MAC_LANGID_YIDDISH */
|
|
|
|
MAKELANGID(LANG_SERBIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SERBIAN */
|
|
|
|
MAKELANGID(LANG_MACEDONIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MACEDONIAN */
|
|
|
|
MAKELANGID(LANG_BULGARIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_BULGARIAN */
|
|
|
|
MAKELANGID(LANG_UKRAINIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_UKRAINIAN */
|
|
|
|
MAKELANGID(LANG_BELARUSIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_BYELORUSSIAN */
|
|
|
|
MAKELANGID(LANG_UZBEK,SUBLANG_DEFAULT), /* TT_MAC_LANGID_UZBEK */
|
|
|
|
MAKELANGID(LANG_KAZAK,SUBLANG_DEFAULT), /* TT_MAC_LANGID_KAZAKH */
|
|
|
|
MAKELANGID(LANG_AZERI,SUBLANG_AZERI_CYRILLIC), /* TT_MAC_LANGID_AZERBAIJANI */
|
|
|
|
0, /* TT_MAC_LANGID_AZERBAIJANI_ARABIC_SCRIPT */
|
|
|
|
MAKELANGID(LANG_ARMENIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ARMENIAN */
|
|
|
|
MAKELANGID(LANG_GEORGIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_GEORGIAN */
|
|
|
|
0, /* TT_MAC_LANGID_MOLDAVIAN */
|
|
|
|
MAKELANGID(LANG_KYRGYZ,SUBLANG_DEFAULT), /* TT_MAC_LANGID_KIRGHIZ */
|
|
|
|
MAKELANGID(LANG_TAJIK,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TAJIKI */
|
|
|
|
MAKELANGID(LANG_TURKMEN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TURKMEN */
|
|
|
|
MAKELANGID(LANG_MONGOLIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MONGOLIAN */
|
|
|
|
MAKELANGID(LANG_MONGOLIAN,SUBLANG_MONGOLIAN_CYRILLIC_MONGOLIA), /* TT_MAC_LANGID_MONGOLIAN_CYRILLIC_SCRIPT */
|
|
|
|
MAKELANGID(LANG_PASHTO,SUBLANG_DEFAULT), /* TT_MAC_LANGID_PASHTO */
|
|
|
|
0, /* TT_MAC_LANGID_KURDISH */
|
|
|
|
MAKELANGID(LANG_KASHMIRI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_KASHMIRI */
|
|
|
|
MAKELANGID(LANG_SINDHI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SINDHI */
|
|
|
|
MAKELANGID(LANG_TIBETAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TIBETAN */
|
|
|
|
MAKELANGID(LANG_NEPALI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_NEPALI */
|
|
|
|
MAKELANGID(LANG_SANSKRIT,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SANSKRIT */
|
|
|
|
MAKELANGID(LANG_MARATHI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MARATHI */
|
|
|
|
MAKELANGID(LANG_BENGALI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_BENGALI */
|
|
|
|
MAKELANGID(LANG_ASSAMESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ASSAMESE */
|
|
|
|
MAKELANGID(LANG_GUJARATI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_GUJARATI */
|
|
|
|
MAKELANGID(LANG_PUNJABI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_PUNJABI */
|
|
|
|
MAKELANGID(LANG_ORIYA,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ORIYA */
|
|
|
|
MAKELANGID(LANG_MALAYALAM,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MALAYALAM */
|
|
|
|
MAKELANGID(LANG_KANNADA,SUBLANG_DEFAULT), /* TT_MAC_LANGID_KANNADA */
|
|
|
|
MAKELANGID(LANG_TAMIL,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TAMIL */
|
|
|
|
MAKELANGID(LANG_TELUGU,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TELUGU */
|
|
|
|
MAKELANGID(LANG_SINHALESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SINHALESE */
|
|
|
|
0, /* TT_MAC_LANGID_BURMESE */
|
|
|
|
MAKELANGID(LANG_KHMER,SUBLANG_DEFAULT), /* TT_MAC_LANGID_KHMER */
|
|
|
|
MAKELANGID(LANG_LAO,SUBLANG_DEFAULT), /* TT_MAC_LANGID_LAO */
|
|
|
|
MAKELANGID(LANG_VIETNAMESE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_VIETNAMESE */
|
|
|
|
MAKELANGID(LANG_INDONESIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_INDONESIAN */
|
|
|
|
0, /* TT_MAC_LANGID_TAGALOG */
|
|
|
|
MAKELANGID(LANG_MALAY,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MALAY_ROMAN_SCRIPT */
|
|
|
|
0, /* TT_MAC_LANGID_MALAY_ARABIC_SCRIPT */
|
|
|
|
MAKELANGID(LANG_AMHARIC,SUBLANG_DEFAULT), /* TT_MAC_LANGID_AMHARIC */
|
|
|
|
MAKELANGID(LANG_TIGRIGNA,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TIGRINYA */
|
|
|
|
0, /* TT_MAC_LANGID_GALLA */
|
|
|
|
0, /* TT_MAC_LANGID_SOMALI */
|
|
|
|
MAKELANGID(LANG_SWAHILI,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SWAHILI */
|
|
|
|
0, /* TT_MAC_LANGID_RUANDA */
|
|
|
|
0, /* TT_MAC_LANGID_RUNDI */
|
|
|
|
0, /* TT_MAC_LANGID_CHEWA */
|
|
|
|
MAKELANGID(LANG_MALAGASY,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MALAGASY */
|
|
|
|
MAKELANGID(LANG_ESPERANTO,SUBLANG_DEFAULT), /* TT_MAC_LANGID_ESPERANTO */
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 95-111 */
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 112-127 */
|
|
|
|
MAKELANGID(LANG_WELSH,SUBLANG_DEFAULT), /* TT_MAC_LANGID_WELSH */
|
|
|
|
MAKELANGID(LANG_BASQUE,SUBLANG_DEFAULT), /* TT_MAC_LANGID_BASQUE */
|
|
|
|
MAKELANGID(LANG_CATALAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_CATALAN */
|
|
|
|
0, /* TT_MAC_LANGID_LATIN */
|
|
|
|
MAKELANGID(LANG_QUECHUA,SUBLANG_DEFAULT), /* TT_MAC_LANGID_QUECHUA */
|
|
|
|
0, /* TT_MAC_LANGID_GUARANI */
|
|
|
|
0, /* TT_MAC_LANGID_AYMARA */
|
|
|
|
MAKELANGID(LANG_TATAR,SUBLANG_DEFAULT), /* TT_MAC_LANGID_TATAR */
|
|
|
|
MAKELANGID(LANG_UIGHUR,SUBLANG_DEFAULT), /* TT_MAC_LANGID_UIGHUR */
|
|
|
|
0, /* TT_MAC_LANGID_DZONGKHA */
|
|
|
|
0, /* TT_MAC_LANGID_JAVANESE */
|
|
|
|
0, /* TT_MAC_LANGID_SUNDANESE */
|
|
|
|
MAKELANGID(LANG_GALICIAN,SUBLANG_DEFAULT), /* TT_MAC_LANGID_GALICIAN */
|
|
|
|
MAKELANGID(LANG_AFRIKAANS,SUBLANG_DEFAULT), /* TT_MAC_LANGID_AFRIKAANS */
|
|
|
|
MAKELANGID(LANG_BRETON,SUBLANG_DEFAULT), /* TT_MAC_LANGID_BRETON */
|
|
|
|
MAKELANGID(LANG_INUKTITUT,SUBLANG_DEFAULT), /* TT_MAC_LANGID_INUKTITUT */
|
|
|
|
MAKELANGID(LANG_SCOTTISH_GAELIC,SUBLANG_DEFAULT), /* TT_MAC_LANGID_SCOTTISH_GAELIC */
|
|
|
|
MAKELANGID(LANG_MANX_GAELIC,SUBLANG_DEFAULT), /* TT_MAC_LANGID_MANX_GAELIC */
|
|
|
|
MAKELANGID(LANG_IRISH,SUBLANG_IRISH_IRELAND), /* TT_MAC_LANGID_IRISH_GAELIC */
|
|
|
|
0, /* TT_MAC_LANGID_TONGAN */
|
|
|
|
0, /* TT_MAC_LANGID_GREEK_POLYTONIC */
|
|
|
|
MAKELANGID(LANG_GREENLANDIC,SUBLANG_DEFAULT), /* TT_MAC_LANGID_GREELANDIC */
|
|
|
|
MAKELANGID(LANG_AZERI,SUBLANG_AZERI_LATIN), /* TT_MAC_LANGID_AZERBAIJANI_ROMAN_SCRIPT */
|
|
|
|
};
|
|
|
|
|
|
|
|
static inline WORD get_mac_code_page( const FT_SfntName *name )
|
|
|
|
{
|
|
|
|
if (name->encoding_id == TT_MAC_ID_SIMPLIFIED_CHINESE) return 10008; /* special case */
|
|
|
|
return 10000 + name->encoding_id;
|
|
|
|
}
|
|
|
|
|
2013-06-20 13:36:34 +02:00
|
|
|
static int match_name_table_language( const FT_SfntName *name, LANGID lang )
|
2006-04-05 13:45:25 +02:00
|
|
|
{
|
2013-06-20 13:36:34 +02:00
|
|
|
LANGID name_lang;
|
2013-08-16 19:11:33 +02:00
|
|
|
int res = 0;
|
2006-04-05 13:45:25 +02:00
|
|
|
|
2013-06-20 13:36:34 +02:00
|
|
|
switch (name->platform_id)
|
2006-04-05 13:45:25 +02:00
|
|
|
{
|
2013-06-20 13:36:34 +02:00
|
|
|
case TT_PLATFORM_MICROSOFT:
|
2013-08-16 19:11:33 +02:00
|
|
|
res += 5; /* prefer the Microsoft name */
|
2013-06-20 13:36:34 +02:00
|
|
|
switch (name->encoding_id)
|
2006-04-05 13:45:25 +02:00
|
|
|
{
|
2013-06-20 13:36:34 +02:00
|
|
|
case TT_MS_ID_UNICODE_CS:
|
|
|
|
case TT_MS_ID_SYMBOL_CS:
|
|
|
|
name_lang = name->language_id;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return 0;
|
2006-04-05 13:45:25 +02:00
|
|
|
}
|
2013-06-20 13:36:34 +02:00
|
|
|
break;
|
2013-06-20 15:04:11 +02:00
|
|
|
case TT_PLATFORM_MACINTOSH:
|
|
|
|
if (!IsValidCodePage( get_mac_code_page( name ))) return 0;
|
2018-10-05 21:35:35 +02:00
|
|
|
if (name->language_id >= ARRAY_SIZE( mac_langid_table )) return 0;
|
2013-06-20 15:04:11 +02:00
|
|
|
name_lang = mac_langid_table[name->language_id];
|
|
|
|
break;
|
2013-06-20 21:07:33 +02:00
|
|
|
case TT_PLATFORM_APPLE_UNICODE:
|
2013-08-16 19:11:33 +02:00
|
|
|
res += 2; /* prefer Unicode encodings */
|
2013-06-20 21:07:33 +02:00
|
|
|
switch (name->encoding_id)
|
|
|
|
{
|
|
|
|
case TT_APPLE_ID_DEFAULT:
|
|
|
|
case TT_APPLE_ID_ISO_10646:
|
|
|
|
case TT_APPLE_ID_UNICODE_2_0:
|
2018-10-05 21:35:35 +02:00
|
|
|
if (name->language_id >= ARRAY_SIZE( mac_langid_table )) return 0;
|
2013-06-20 21:07:33 +02:00
|
|
|
name_lang = mac_langid_table[name->language_id];
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
break;
|
2013-06-20 13:36:34 +02:00
|
|
|
default:
|
|
|
|
return 0;
|
2006-04-05 13:45:25 +02:00
|
|
|
}
|
2013-08-16 19:11:33 +02:00
|
|
|
if (name_lang == lang) res += 30;
|
|
|
|
else if (PRIMARYLANGID( name_lang ) == PRIMARYLANGID( lang )) res += 20;
|
|
|
|
else if (name_lang == MAKELANGID( LANG_ENGLISH, SUBLANG_DEFAULT )) res += 10;
|
2020-09-17 19:30:43 +02:00
|
|
|
else if (lang == MAKELANGID( LANG_NEUTRAL, SUBLANG_NEUTRAL )) res += 5 * (0x100000 - name_lang);
|
2013-08-16 19:11:33 +02:00
|
|
|
return res;
|
2013-06-20 13:36:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static WCHAR *copy_name_table_string( const FT_SfntName *name )
|
|
|
|
{
|
|
|
|
WCHAR *ret;
|
2013-06-20 15:04:11 +02:00
|
|
|
WORD codepage;
|
2013-06-20 13:36:34 +02:00
|
|
|
int i;
|
|
|
|
|
2013-06-20 15:04:11 +02:00
|
|
|
switch (name->platform_id)
|
|
|
|
{
|
2013-06-20 21:07:33 +02:00
|
|
|
case TT_PLATFORM_APPLE_UNICODE:
|
2013-06-20 15:04:11 +02:00
|
|
|
case TT_PLATFORM_MICROSOFT:
|
|
|
|
ret = HeapAlloc( GetProcessHeap(), 0, name->string_len + sizeof(WCHAR) );
|
|
|
|
for (i = 0; i < name->string_len / 2; i++)
|
|
|
|
ret[i] = (name->string[i * 2] << 8) | name->string[i * 2 + 1];
|
|
|
|
ret[i] = 0;
|
|
|
|
return ret;
|
|
|
|
case TT_PLATFORM_MACINTOSH:
|
|
|
|
codepage = get_mac_code_page( name );
|
|
|
|
i = MultiByteToWideChar( codepage, 0, (char *)name->string, name->string_len, NULL, 0 );
|
|
|
|
ret = HeapAlloc( GetProcessHeap(), 0, (i + 1) * sizeof(WCHAR) );
|
|
|
|
MultiByteToWideChar( codepage, 0, (char *)name->string, name->string_len, ret, i );
|
|
|
|
ret[i] = 0;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
return NULL;
|
2008-09-18 13:23:08 +02:00
|
|
|
}
|
2006-04-05 13:45:25 +02:00
|
|
|
|
2013-06-20 13:36:34 +02:00
|
|
|
static WCHAR *get_face_name(FT_Face ft_face, FT_UShort name_id, LANGID language_id)
|
2008-09-18 13:23:08 +02:00
|
|
|
{
|
|
|
|
FT_SfntName name;
|
2013-06-20 13:36:34 +02:00
|
|
|
FT_UInt num_names, name_index;
|
|
|
|
int res, best_lang = 0, best_index = -1;
|
2008-09-18 13:23:08 +02:00
|
|
|
|
2013-06-20 13:36:34 +02:00
|
|
|
if (!FT_IS_SFNT(ft_face)) return NULL;
|
2008-09-18 13:23:08 +02:00
|
|
|
|
2013-06-20 13:36:34 +02:00
|
|
|
num_names = pFT_Get_Sfnt_Name_Count( ft_face );
|
2008-09-18 13:23:08 +02:00
|
|
|
|
2013-06-20 13:36:34 +02:00
|
|
|
for (name_index = 0; name_index < num_names; name_index++)
|
|
|
|
{
|
|
|
|
if (pFT_Get_Sfnt_Name( ft_face, name_index, &name )) continue;
|
|
|
|
if (name.name_id != name_id) continue;
|
|
|
|
res = match_name_table_language( &name, language_id );
|
|
|
|
if (res > best_lang)
|
2008-09-18 13:23:08 +02:00
|
|
|
{
|
2013-06-20 13:36:34 +02:00
|
|
|
best_lang = res;
|
|
|
|
best_index = name_index;
|
2008-09-18 13:23:08 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-06-20 13:36:34 +02:00
|
|
|
if (best_index != -1 && !pFT_Get_Sfnt_Name( ft_face, best_index, &name ))
|
|
|
|
{
|
|
|
|
WCHAR *ret = copy_name_table_string( &name );
|
|
|
|
TRACE( "name %u found platform %u lang %04x %s\n",
|
|
|
|
name_id, name.platform_id, name.language_id, debugstr_w( ret ));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
return NULL;
|
2006-04-05 13:45:25 +02:00
|
|
|
}
|
|
|
|
|
2020-09-07 15:18:54 +02:00
|
|
|
static WCHAR *ft_face_get_family_name( FT_Face ft_face, LANGID langid )
|
|
|
|
{
|
|
|
|
WCHAR *family_name;
|
|
|
|
|
|
|
|
if ((family_name = get_face_name( ft_face, TT_NAME_ID_FONT_FAMILY, langid )))
|
|
|
|
return family_name;
|
|
|
|
|
|
|
|
return towstr( CP_ACP, ft_face->family_name );
|
|
|
|
}
|
|
|
|
|
|
|
|
static WCHAR *ft_face_get_style_name( FT_Face ft_face, LANGID langid )
|
|
|
|
{
|
|
|
|
WCHAR *style_name;
|
|
|
|
|
|
|
|
if ((style_name = get_face_name( ft_face, TT_NAME_ID_FONT_SUBFAMILY, langid )))
|
|
|
|
return style_name;
|
|
|
|
|
|
|
|
return towstr( CP_ACP, ft_face->style_name );
|
|
|
|
}
|
|
|
|
|
2020-09-07 15:18:56 +02:00
|
|
|
static WCHAR *ft_face_get_full_name( FT_Face ft_face, LANGID langid )
|
|
|
|
{
|
|
|
|
static const WCHAR space_w[] = {' ',0};
|
|
|
|
WCHAR *full_name, *style_name;
|
|
|
|
SIZE_T length;
|
|
|
|
|
|
|
|
if ((full_name = get_face_name( ft_face, TT_NAME_ID_FULL_NAME, langid )))
|
|
|
|
return full_name;
|
|
|
|
|
|
|
|
full_name = ft_face_get_family_name( ft_face, langid );
|
|
|
|
style_name = ft_face_get_style_name( ft_face, langid );
|
|
|
|
|
|
|
|
length = strlenW( full_name ) + strlenW( space_w ) + strlenW( style_name ) + 1;
|
|
|
|
full_name = HeapReAlloc( GetProcessHeap(), 0, full_name, length * sizeof(WCHAR) );
|
|
|
|
|
|
|
|
strcatW( full_name, space_w );
|
|
|
|
strcatW( full_name, style_name );
|
|
|
|
HeapFree( GetProcessHeap(), 0, style_name );
|
|
|
|
|
|
|
|
WARN( "full name not found, using %s instead\n", debugstr_w(full_name) );
|
|
|
|
return full_name;
|
|
|
|
}
|
|
|
|
|
2012-03-30 10:52:02 +02:00
|
|
|
static inline BOOL faces_equal( const Face *f1, const Face *f2 )
|
|
|
|
{
|
2020-09-07 15:18:59 +02:00
|
|
|
if (strcmpiW( f1->full_name, f2->full_name )) return FALSE;
|
2012-03-30 10:52:02 +02:00
|
|
|
if (f1->scalable) return TRUE;
|
2012-04-09 11:24:53 +02:00
|
|
|
if (f1->size.y_ppem != f2->size.y_ppem) return FALSE;
|
2012-03-30 10:52:02 +02:00
|
|
|
return !memcmp( &f1->fs, &f2->fs, sizeof(f1->fs) );
|
|
|
|
}
|
|
|
|
|
2013-01-14 15:19:14 +01:00
|
|
|
static void release_family( Family *family )
|
2012-03-30 10:52:02 +02:00
|
|
|
{
|
2013-01-14 15:19:14 +01:00
|
|
|
if (--family->refcount) return;
|
|
|
|
assert( list_empty( &family->faces ));
|
|
|
|
list_remove( &family->entry );
|
|
|
|
HeapFree( GetProcessHeap(), 0, family );
|
2012-03-30 10:52:02 +02:00
|
|
|
}
|
|
|
|
|
2013-01-14 15:19:14 +01:00
|
|
|
static void release_face( Face *face )
|
2012-03-30 10:52:02 +02:00
|
|
|
{
|
2013-01-14 15:19:14 +01:00
|
|
|
if (--face->refcount) return;
|
|
|
|
if (face->family)
|
2012-03-30 10:52:02 +02:00
|
|
|
{
|
2013-01-15 12:34:40 +01:00
|
|
|
if (face->flags & ADDFONT_ADD_TO_CACHE) remove_face_from_cache( face );
|
2012-03-30 10:52:02 +02:00
|
|
|
list_remove( &face->entry );
|
2013-01-14 15:19:14 +01:00
|
|
|
release_family( face->family );
|
2012-03-30 10:52:02 +02:00
|
|
|
}
|
2013-01-14 15:19:14 +01:00
|
|
|
HeapFree( GetProcessHeap(), 0, face->file );
|
2020-09-07 15:18:57 +02:00
|
|
|
HeapFree( GetProcessHeap(), 0, face->style_name );
|
2020-09-07 15:18:56 +02:00
|
|
|
HeapFree( GetProcessHeap(), 0, face->full_name );
|
2013-01-14 15:19:14 +01:00
|
|
|
HeapFree( GetProcessHeap(), 0, face->cached_enum_data );
|
|
|
|
HeapFree( GetProcessHeap(), 0, face );
|
2012-03-30 10:52:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline int style_order(const Face *face)
|
|
|
|
{
|
|
|
|
switch (face->ntmFlags & (NTM_REGULAR | NTM_BOLD | NTM_ITALIC))
|
|
|
|
{
|
|
|
|
case NTM_REGULAR:
|
|
|
|
return 0;
|
|
|
|
case NTM_BOLD:
|
|
|
|
return 1;
|
|
|
|
case NTM_ITALIC:
|
|
|
|
return 2;
|
|
|
|
case NTM_BOLD | NTM_ITALIC:
|
|
|
|
return 3;
|
|
|
|
default:
|
2020-09-07 15:18:56 +02:00
|
|
|
WARN( "Don't know how to order face %s with flags 0x%08x\n", debugstr_w(face->full_name), face->ntmFlags );
|
2012-03-30 10:52:02 +02:00
|
|
|
return 9999;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static BOOL insert_face_in_family_list( Face *face, Family *family )
|
|
|
|
{
|
|
|
|
Face *cursor;
|
|
|
|
|
|
|
|
LIST_FOR_EACH_ENTRY( cursor, &family->faces, Face, entry )
|
|
|
|
{
|
|
|
|
if (faces_equal( face, cursor ))
|
|
|
|
{
|
2020-09-07 15:18:56 +02:00
|
|
|
TRACE( "Already loaded face %s in family %s, original version %lx, new version %lx\n",
|
2020-09-07 15:18:58 +02:00
|
|
|
debugstr_w(face->full_name), debugstr_w(family->family_name),
|
2020-09-07 15:18:56 +02:00
|
|
|
cursor->font_version, face->font_version );
|
2012-03-30 10:52:02 +02:00
|
|
|
|
2020-10-06 15:40:25 +02:00
|
|
|
if (face->file && !strcmpiW( face->file, cursor->file ))
|
2013-03-04 11:35:50 +01:00
|
|
|
{
|
|
|
|
cursor->refcount++;
|
|
|
|
TRACE("Font %s already in list, refcount now %d\n",
|
|
|
|
debugstr_w(face->file), cursor->refcount);
|
|
|
|
return FALSE;
|
|
|
|
}
|
2012-03-30 10:52:02 +02:00
|
|
|
if (face->font_version <= cursor->font_version)
|
|
|
|
{
|
2012-04-09 11:25:11 +02:00
|
|
|
TRACE("Original font %s is newer so skipping %s\n",
|
2012-11-01 13:16:26 +01:00
|
|
|
debugstr_w(cursor->file), debugstr_w(face->file));
|
2012-03-30 10:52:02 +02:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-04-09 11:25:11 +02:00
|
|
|
TRACE("Replacing original %s with %s\n",
|
2012-11-01 13:16:26 +01:00
|
|
|
debugstr_w(cursor->file), debugstr_w(face->file));
|
2012-03-30 10:52:02 +02:00
|
|
|
list_add_before( &cursor->entry, &face->entry );
|
|
|
|
face->family = family;
|
2013-01-14 15:19:14 +01:00
|
|
|
family->refcount++;
|
|
|
|
face->refcount++;
|
|
|
|
release_face( cursor );
|
2012-03-30 10:52:02 +02:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (style_order( face ) < style_order( cursor )) break;
|
|
|
|
}
|
|
|
|
|
2020-09-07 15:18:56 +02:00
|
|
|
TRACE( "Adding face %s in family %s from %s\n", debugstr_w(face->full_name),
|
2020-09-07 15:18:58 +02:00
|
|
|
debugstr_w(family->family_name), debugstr_w(face->file) );
|
2012-03-30 10:52:02 +02:00
|
|
|
list_add_before( &cursor->entry, &face->entry );
|
|
|
|
face->family = family;
|
2013-01-14 15:19:14 +01:00
|
|
|
family->refcount++;
|
|
|
|
face->refcount++;
|
2012-03-30 10:52:02 +02:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************
|
|
|
|
* NB This function stores the ptrs to the strings to save copying.
|
|
|
|
* Don't free them after calling.
|
|
|
|
*/
|
2020-09-17 19:30:43 +02:00
|
|
|
static Family *create_family( WCHAR *family_name, WCHAR *second_name )
|
2012-03-30 10:52:02 +02:00
|
|
|
{
|
|
|
|
Family * const family = HeapAlloc( GetProcessHeap(), 0, sizeof(*family) );
|
2013-01-14 15:19:14 +01:00
|
|
|
family->refcount = 1;
|
2020-09-07 15:18:58 +02:00
|
|
|
lstrcpynW( family->family_name, family_name, LF_FACESIZE );
|
2020-09-17 19:30:43 +02:00
|
|
|
if (second_name) lstrcpynW( family->second_name, second_name, LF_FACESIZE );
|
|
|
|
else family->second_name[0] = 0;
|
2012-03-30 10:52:02 +02:00
|
|
|
list_init( &family->faces );
|
|
|
|
family->replacement = &family->faces;
|
2013-01-14 15:19:14 +01:00
|
|
|
list_add_tail( &font_list, &family->entry );
|
2012-03-30 10:52:02 +02:00
|
|
|
|
|
|
|
return family;
|
|
|
|
}
|
|
|
|
|
2020-10-06 19:58:11 +02:00
|
|
|
struct cached_face
|
2011-10-06 23:26:07 +02:00
|
|
|
{
|
2020-10-06 19:58:11 +02:00
|
|
|
DWORD index;
|
|
|
|
DWORD flags;
|
|
|
|
DWORD ntmflags;
|
|
|
|
DWORD version;
|
|
|
|
DWORD width;
|
|
|
|
DWORD height;
|
|
|
|
DWORD size;
|
|
|
|
DWORD x_ppem;
|
|
|
|
DWORD y_ppem;
|
|
|
|
DWORD internal_leading;
|
|
|
|
FONTSIGNATURE fs;
|
|
|
|
WCHAR full_name[1];
|
|
|
|
/* WCHAR file_name[]; */
|
|
|
|
};
|
2011-10-06 23:26:06 +02:00
|
|
|
|
2020-10-06 19:58:11 +02:00
|
|
|
static void load_face(HKEY hkey_family, Family *family, void *buffer, DWORD buffer_size, BOOL scalable)
|
2011-10-06 23:26:07 +02:00
|
|
|
{
|
2020-10-06 19:58:11 +02:00
|
|
|
DWORD type, size, needed, index = 0;
|
|
|
|
Face *face;
|
2012-11-01 12:35:32 +01:00
|
|
|
HKEY hkey_strike;
|
2020-10-06 19:58:11 +02:00
|
|
|
WCHAR name[256];
|
|
|
|
struct cached_face *cached = (struct cached_face *)buffer;
|
2011-10-06 23:26:07 +02:00
|
|
|
|
2020-10-06 19:58:11 +02:00
|
|
|
size = sizeof(name);
|
|
|
|
needed = buffer_size - sizeof(DWORD);
|
|
|
|
while (!RegEnumValueW( hkey_family, index++, name, &size, NULL, &type, buffer, &needed ))
|
2011-10-06 23:26:07 +02:00
|
|
|
{
|
2020-10-06 19:58:11 +02:00
|
|
|
if (type == REG_BINARY && needed > sizeof(*cached))
|
2020-09-07 15:18:56 +02:00
|
|
|
{
|
2020-10-06 19:58:11 +02:00
|
|
|
((DWORD *)buffer)[needed / sizeof(DWORD)] = 0;
|
|
|
|
|
|
|
|
face = HeapAlloc(GetProcessHeap(), 0, sizeof(*face));
|
|
|
|
face->cached_enum_data = NULL;
|
|
|
|
face->family = NULL;
|
|
|
|
face->refcount = 1;
|
|
|
|
face->style_name = strdupW( name );
|
|
|
|
face->face_index = cached->index;
|
|
|
|
face->flags = cached->flags;
|
|
|
|
face->ntmFlags = cached->ntmflags;
|
|
|
|
face->font_version = cached->version;
|
|
|
|
face->size.width = cached->width;
|
|
|
|
face->size.height = cached->height;
|
|
|
|
face->size.size = cached->size;
|
|
|
|
face->size.x_ppem = cached->x_ppem;
|
|
|
|
face->size.y_ppem = cached->y_ppem;
|
|
|
|
face->size.internal_leading = cached->internal_leading;
|
|
|
|
face->fs = cached->fs;
|
|
|
|
face->full_name = strdupW( cached->full_name );
|
|
|
|
face->file = strdupW( cached->full_name + strlenW(cached->full_name) + 1 );
|
|
|
|
face->scalable = scalable;
|
|
|
|
if (!face->scalable)
|
|
|
|
TRACE("Adding bitmap size h %d w %d size %ld x_ppem %ld y_ppem %ld\n",
|
|
|
|
face->size.height, face->size.width, face->size.size >> 6,
|
|
|
|
face->size.x_ppem >> 6, face->size.y_ppem >> 6);
|
|
|
|
|
|
|
|
TRACE("fsCsb = %08x %08x/%08x %08x %08x %08x\n",
|
|
|
|
face->fs.fsCsb[0], face->fs.fsCsb[1],
|
|
|
|
face->fs.fsUsb[0], face->fs.fsUsb[1],
|
|
|
|
face->fs.fsUsb[2], face->fs.fsUsb[3]);
|
|
|
|
|
|
|
|
if (insert_face_in_family_list(face, family))
|
|
|
|
TRACE( "Added face %s to family %s\n", debugstr_w(face->full_name), debugstr_w(family->family_name) );
|
2011-10-06 23:26:07 +02:00
|
|
|
|
2020-10-06 19:58:11 +02:00
|
|
|
release_face( face );
|
2011-10-06 23:26:07 +02:00
|
|
|
}
|
2020-10-06 19:58:11 +02:00
|
|
|
size = sizeof(name);
|
|
|
|
needed = buffer_size - sizeof(DWORD);
|
2011-10-06 23:26:07 +02:00
|
|
|
}
|
|
|
|
|
2012-11-01 12:35:32 +01:00
|
|
|
/* load bitmap strikes */
|
2011-10-06 23:26:07 +02:00
|
|
|
|
2020-10-06 19:58:11 +02:00
|
|
|
index = 0;
|
2012-11-01 12:35:32 +01:00
|
|
|
needed = buffer_size;
|
2020-10-06 19:58:11 +02:00
|
|
|
while (!RegEnumKeyExW(hkey_family, index++, buffer, &needed, NULL, NULL, NULL, NULL))
|
2012-11-01 12:35:32 +01:00
|
|
|
{
|
2020-10-06 19:58:11 +02:00
|
|
|
if (!RegOpenKeyExW(hkey_family, buffer, 0, KEY_ALL_ACCESS, &hkey_strike))
|
2011-10-06 23:26:07 +02:00
|
|
|
{
|
2020-10-06 19:58:11 +02:00
|
|
|
load_face(hkey_strike, family, buffer, buffer_size, FALSE);
|
2011-10-06 23:26:07 +02:00
|
|
|
RegCloseKey(hkey_strike);
|
|
|
|
}
|
2012-11-01 12:35:32 +01:00
|
|
|
needed = buffer_size;
|
2011-10-06 23:26:07 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-06-20 12:12:16 +02:00
|
|
|
/* move vertical fonts after their horizontal counterpart */
|
|
|
|
/* assumes that font_list is already sorted by family name */
|
|
|
|
static void reorder_vertical_fonts(void)
|
|
|
|
{
|
|
|
|
Family *family, *next, *vert_family;
|
|
|
|
struct list *ptr, *vptr;
|
|
|
|
struct list vertical_families = LIST_INIT( vertical_families );
|
|
|
|
|
|
|
|
LIST_FOR_EACH_ENTRY_SAFE( family, next, &font_list, Family, entry )
|
|
|
|
{
|
2020-09-07 15:18:58 +02:00
|
|
|
if (family->family_name[0] != '@') continue;
|
2013-06-20 12:12:16 +02:00
|
|
|
list_remove( &family->entry );
|
|
|
|
list_add_tail( &vertical_families, &family->entry );
|
|
|
|
}
|
|
|
|
|
|
|
|
ptr = list_head( &font_list );
|
|
|
|
vptr = list_head( &vertical_families );
|
|
|
|
while (ptr && vptr)
|
|
|
|
{
|
|
|
|
family = LIST_ENTRY( ptr, Family, entry );
|
|
|
|
vert_family = LIST_ENTRY( vptr, Family, entry );
|
2020-09-07 15:18:58 +02:00
|
|
|
if (strcmpiW( family->family_name, vert_family->family_name + 1 ) > 0)
|
2013-06-20 12:12:16 +02:00
|
|
|
{
|
|
|
|
list_remove( vptr );
|
|
|
|
list_add_before( ptr, vptr );
|
|
|
|
vptr = list_head( &vertical_families );
|
|
|
|
}
|
|
|
|
else ptr = list_next( &font_list, ptr );
|
|
|
|
}
|
|
|
|
list_move_tail( &font_list, &vertical_families );
|
|
|
|
}
|
|
|
|
|
2011-10-06 23:26:07 +02:00
|
|
|
static void load_font_list_from_cache(HKEY hkey_font_cache)
|
|
|
|
{
|
2012-11-01 12:35:32 +01:00
|
|
|
DWORD size, family_index = 0;
|
2011-10-06 23:26:07 +02:00
|
|
|
Family *family;
|
|
|
|
HKEY hkey_family;
|
2012-11-01 12:35:32 +01:00
|
|
|
WCHAR buffer[4096];
|
2011-10-06 23:26:07 +02:00
|
|
|
|
2012-11-01 12:35:32 +01:00
|
|
|
size = sizeof(buffer);
|
|
|
|
while (!RegEnumKeyExW(hkey_font_cache, family_index++, buffer, &size, NULL, NULL, NULL, NULL))
|
2011-10-06 23:26:07 +02:00
|
|
|
{
|
2020-09-17 19:30:43 +02:00
|
|
|
WCHAR *second_name = NULL;
|
2012-11-01 12:35:32 +01:00
|
|
|
WCHAR *family_name = strdupW( buffer );
|
2011-10-06 23:26:07 +02:00
|
|
|
|
|
|
|
RegOpenKeyExW(hkey_font_cache, family_name, 0, KEY_ALL_ACCESS, &hkey_family);
|
|
|
|
TRACE("opened family key %s\n", debugstr_w(family_name));
|
2012-11-01 12:35:32 +01:00
|
|
|
size = sizeof(buffer);
|
2020-10-06 19:58:11 +02:00
|
|
|
if (!RegQueryValueExW( hkey_family, NULL, NULL, NULL, (BYTE *)buffer, &size ))
|
2020-09-17 19:30:43 +02:00
|
|
|
second_name = strdupW( buffer );
|
2011-10-06 23:26:07 +02:00
|
|
|
|
2020-09-17 19:30:43 +02:00
|
|
|
family = create_family( family_name, second_name );
|
2011-10-06 23:26:07 +02:00
|
|
|
|
2020-09-17 19:30:43 +02:00
|
|
|
if (second_name)
|
2011-10-06 23:26:07 +02:00
|
|
|
{
|
|
|
|
FontSubst *subst = HeapAlloc(GetProcessHeap(), 0, sizeof(*subst));
|
2020-09-17 19:30:43 +02:00
|
|
|
subst->from.name = strdupW( second_name );
|
2011-10-06 23:26:07 +02:00
|
|
|
subst->from.charset = -1;
|
|
|
|
subst->to.name = strdupW(family_name);
|
|
|
|
subst->to.charset = -1;
|
|
|
|
add_font_subst(&font_subst_list, subst, 0);
|
|
|
|
}
|
|
|
|
|
2020-10-06 19:58:11 +02:00
|
|
|
load_face(hkey_family, family, buffer, sizeof(buffer), TRUE);
|
2020-09-07 15:18:58 +02:00
|
|
|
|
|
|
|
HeapFree( GetProcessHeap(), 0, family_name );
|
2020-09-17 19:30:43 +02:00
|
|
|
HeapFree( GetProcessHeap(), 0, second_name );
|
2020-09-07 15:18:58 +02:00
|
|
|
|
2011-10-06 23:26:07 +02:00
|
|
|
RegCloseKey(hkey_family);
|
2013-01-14 15:19:14 +01:00
|
|
|
release_family( family );
|
2012-11-01 12:35:32 +01:00
|
|
|
size = sizeof(buffer);
|
2011-10-06 23:26:07 +02:00
|
|
|
}
|
2013-06-20 12:12:16 +02:00
|
|
|
|
|
|
|
reorder_vertical_fonts();
|
2011-10-06 23:26:07 +02:00
|
|
|
}
|
|
|
|
|
2011-10-06 23:26:06 +02:00
|
|
|
static LONG create_font_cache_key(HKEY *hkey, DWORD *disposition)
|
|
|
|
{
|
|
|
|
LONG ret;
|
|
|
|
HKEY hkey_wine_fonts;
|
|
|
|
|
|
|
|
/* We don't want to create the fonts key as volatile, so open this first */
|
|
|
|
ret = RegCreateKeyExW(HKEY_CURRENT_USER, wine_fonts_key, 0, NULL, 0,
|
|
|
|
KEY_ALL_ACCESS, NULL, &hkey_wine_fonts, NULL);
|
|
|
|
if(ret != ERROR_SUCCESS)
|
|
|
|
{
|
|
|
|
WARN("Can't create %s\n", debugstr_w(wine_fonts_key));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = RegCreateKeyExW(hkey_wine_fonts, wine_fonts_cache_key, 0, NULL, REG_OPTION_VOLATILE,
|
|
|
|
KEY_ALL_ACCESS, NULL, hkey, disposition);
|
|
|
|
RegCloseKey(hkey_wine_fonts);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void add_face_to_cache(Face *face)
|
|
|
|
{
|
2013-01-15 13:56:52 +01:00
|
|
|
HKEY hkey_family, hkey_face;
|
2020-10-06 19:58:11 +02:00
|
|
|
DWORD len, buffer[1024];
|
|
|
|
struct cached_face *cached = (struct cached_face *)buffer;
|
2011-10-06 23:26:06 +02:00
|
|
|
|
2020-09-07 15:18:58 +02:00
|
|
|
RegCreateKeyExW( hkey_font_cache, face->family->family_name, 0, NULL, REG_OPTION_VOLATILE,
|
|
|
|
KEY_ALL_ACCESS, NULL, &hkey_family, NULL );
|
2020-09-17 19:30:43 +02:00
|
|
|
if (face->family->second_name[0])
|
2020-10-06 19:58:11 +02:00
|
|
|
RegSetValueExW( hkey_family, NULL, 0, REG_SZ, (BYTE *)face->family->second_name,
|
2020-09-17 19:30:43 +02:00
|
|
|
(strlenW( face->family->second_name ) + 1) * sizeof(WCHAR) );
|
2011-10-06 23:26:06 +02:00
|
|
|
|
2020-10-06 19:58:11 +02:00
|
|
|
if (!face->scalable)
|
2011-10-06 23:26:06 +02:00
|
|
|
{
|
2020-10-06 19:58:11 +02:00
|
|
|
static const WCHAR fmtW[] = {'%','d',0};
|
|
|
|
WCHAR name[10];
|
2011-10-06 23:26:06 +02:00
|
|
|
|
2020-10-06 19:58:11 +02:00
|
|
|
sprintfW( name, fmtW, face->size.y_ppem );
|
|
|
|
RegCreateKeyExW( hkey_family, name, 0, NULL, REG_OPTION_VOLATILE, KEY_ALL_ACCESS,
|
|
|
|
NULL, &hkey_face, NULL);
|
|
|
|
}
|
|
|
|
else hkey_face = hkey_family;
|
|
|
|
|
|
|
|
memset( cached, 0, sizeof(*cached) );
|
|
|
|
cached->index = face->face_index;
|
|
|
|
cached->flags = face->flags;
|
|
|
|
cached->ntmflags = face->ntmFlags;
|
|
|
|
cached->version = face->font_version;
|
|
|
|
cached->fs = face->fs;
|
|
|
|
if (!face->scalable)
|
2011-10-06 23:26:06 +02:00
|
|
|
{
|
2020-10-06 19:58:11 +02:00
|
|
|
cached->width = face->size.width;
|
|
|
|
cached->height = face->size.height;
|
|
|
|
cached->size = face->size.size;
|
|
|
|
cached->x_ppem = face->size.x_ppem;
|
|
|
|
cached->y_ppem = face->size.y_ppem;
|
|
|
|
cached->internal_leading = face->size.internal_leading;
|
2011-10-06 23:26:06 +02:00
|
|
|
}
|
2020-10-06 19:58:11 +02:00
|
|
|
strcpyW( cached->full_name, face->full_name );
|
|
|
|
len = strlenW( face->full_name ) + 1;
|
|
|
|
strcpyW( cached->full_name + len, face->file );
|
|
|
|
len += strlenW( face->file ) + 1;
|
|
|
|
|
|
|
|
RegSetValueExW( hkey_face, face->style_name, 0, REG_BINARY, (BYTE *)cached,
|
|
|
|
offsetof( struct cached_face, full_name[len] ));
|
|
|
|
|
|
|
|
if (hkey_face != hkey_family) RegCloseKey(hkey_face);
|
2011-10-06 23:26:06 +02:00
|
|
|
RegCloseKey(hkey_family);
|
|
|
|
}
|
2006-04-05 13:45:25 +02:00
|
|
|
|
2013-01-15 12:34:40 +01:00
|
|
|
static void remove_face_from_cache( Face *face )
|
|
|
|
{
|
|
|
|
HKEY hkey_family;
|
|
|
|
|
2020-09-07 15:18:58 +02:00
|
|
|
RegOpenKeyExW( hkey_font_cache, face->family->family_name, 0, KEY_ALL_ACCESS, &hkey_family );
|
2013-01-15 12:34:40 +01:00
|
|
|
|
|
|
|
if (face->scalable)
|
|
|
|
{
|
2020-10-06 19:58:11 +02:00
|
|
|
RegDeleteValueW( hkey_family, face->style_name );
|
2013-01-15 12:34:40 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-10-06 19:58:11 +02:00
|
|
|
static const WCHAR fmtW[] = {'%','d',0};
|
|
|
|
WCHAR name[10];
|
|
|
|
sprintfW( name, fmtW, face->size.y_ppem );
|
|
|
|
RegDeleteKeyW( hkey_family, name );
|
2013-01-15 12:34:40 +01:00
|
|
|
}
|
|
|
|
RegCloseKey(hkey_family);
|
|
|
|
}
|
|
|
|
|
2020-09-07 15:18:55 +02:00
|
|
|
static WCHAR *get_vertical_name( WCHAR *name )
|
2011-12-20 13:05:53 +01:00
|
|
|
{
|
2020-09-07 15:18:55 +02:00
|
|
|
SIZE_T length;
|
|
|
|
if (!name) return NULL;
|
|
|
|
if (name[0] == '@') return name;
|
2011-12-20 13:05:53 +01:00
|
|
|
|
2020-09-07 15:18:55 +02:00
|
|
|
length = strlenW( name ) + 1;
|
|
|
|
name = HeapReAlloc( GetProcessHeap(), 0, name, (length + 1) * sizeof(WCHAR) );
|
|
|
|
memmove( name + 1, name, length * sizeof(WCHAR) );
|
|
|
|
name[0] = '@';
|
|
|
|
return name;
|
2011-12-20 13:05:53 +01:00
|
|
|
}
|
|
|
|
|
2020-09-07 15:18:54 +02:00
|
|
|
static Family *get_family( FT_Face ft_face, BOOL vertical )
|
2012-03-27 12:48:38 +02:00
|
|
|
{
|
2020-09-07 15:18:54 +02:00
|
|
|
Family *family;
|
2020-09-17 19:30:43 +02:00
|
|
|
WCHAR *family_name, *second_name;
|
2012-03-27 12:48:38 +02:00
|
|
|
|
2020-09-07 15:18:54 +02:00
|
|
|
family_name = ft_face_get_family_name( ft_face, GetSystemDefaultLCID() );
|
2020-09-17 19:30:43 +02:00
|
|
|
second_name = ft_face_get_family_name( ft_face, MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT) );
|
2020-09-07 15:18:54 +02:00
|
|
|
|
2020-09-17 19:30:43 +02:00
|
|
|
/* try to find another secondary name, preferring the lowest langids */
|
|
|
|
if (!strcmpiW( family_name, second_name ))
|
2012-03-27 12:48:38 +02:00
|
|
|
{
|
2020-09-17 19:30:43 +02:00
|
|
|
HeapFree( GetProcessHeap(), 0, second_name );
|
|
|
|
second_name = ft_face_get_family_name( ft_face, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL) );
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!strcmpiW( family_name, second_name ))
|
|
|
|
{
|
|
|
|
HeapFree( GetProcessHeap(), 0, second_name );
|
|
|
|
second_name = NULL;
|
2012-03-27 12:48:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (vertical)
|
|
|
|
{
|
2020-09-07 15:18:55 +02:00
|
|
|
family_name = get_vertical_name( family_name );
|
2020-09-17 19:30:43 +02:00
|
|
|
second_name = get_vertical_name( second_name );
|
2012-03-27 12:48:38 +02:00
|
|
|
}
|
2012-03-27 12:48:39 +02:00
|
|
|
|
2020-09-07 15:18:58 +02:00
|
|
|
if ((family = find_family_from_name( family_name ))) family->refcount++;
|
2020-09-17 19:30:43 +02:00
|
|
|
else if ((family = create_family( family_name, second_name )) && second_name)
|
2012-03-27 12:48:39 +02:00
|
|
|
{
|
2020-09-07 15:18:58 +02:00
|
|
|
FontSubst *subst = HeapAlloc( GetProcessHeap(), 0, sizeof(*subst) );
|
2020-09-17 19:30:43 +02:00
|
|
|
subst->from.name = strdupW( second_name );
|
2020-09-07 15:18:58 +02:00
|
|
|
subst->from.charset = -1;
|
|
|
|
subst->to.name = strdupW( family_name );
|
|
|
|
subst->to.charset = -1;
|
|
|
|
add_font_subst( &font_subst_list, subst, 0 );
|
2012-03-27 12:48:40 +02:00
|
|
|
}
|
2012-03-27 12:48:39 +02:00
|
|
|
|
2020-09-07 15:18:58 +02:00
|
|
|
HeapFree( GetProcessHeap(), 0, family_name );
|
2020-09-17 19:30:43 +02:00
|
|
|
HeapFree( GetProcessHeap(), 0, second_name );
|
2020-09-07 15:18:58 +02:00
|
|
|
|
2012-03-27 12:48:39 +02:00
|
|
|
return family;
|
|
|
|
}
|
|
|
|
|
2012-03-27 12:48:41 +02:00
|
|
|
static inline FT_Fixed get_font_version( FT_Face ft_face )
|
|
|
|
{
|
|
|
|
FT_Fixed version = 0;
|
|
|
|
TT_Header *header;
|
|
|
|
|
|
|
|
header = pFT_Get_Sfnt_Table( ft_face, ft_sfnt_head );
|
|
|
|
if (header) version = header->Font_Revision;
|
|
|
|
|
|
|
|
return version;
|
|
|
|
}
|
2012-03-27 12:48:39 +02:00
|
|
|
|
2012-03-27 12:48:42 +02:00
|
|
|
static inline DWORD get_ntm_flags( FT_Face ft_face )
|
|
|
|
{
|
|
|
|
DWORD flags = 0;
|
|
|
|
FT_ULong table_size = 0;
|
2015-12-31 05:33:15 +01:00
|
|
|
FT_WinFNT_HeaderRec winfnt_header;
|
2012-03-27 12:48:42 +02:00
|
|
|
|
|
|
|
if (ft_face->style_flags & FT_STYLE_FLAG_ITALIC) flags |= NTM_ITALIC;
|
|
|
|
if (ft_face->style_flags & FT_STYLE_FLAG_BOLD) flags |= NTM_BOLD;
|
2015-12-31 05:33:15 +01:00
|
|
|
|
|
|
|
/* fixup the flag for our fake-bold implementation. */
|
|
|
|
if (!FT_IS_SCALABLE( ft_face ) &&
|
|
|
|
!pFT_Get_WinFNT_Header( ft_face, &winfnt_header ) &&
|
|
|
|
winfnt_header.weight > FW_NORMAL )
|
|
|
|
flags |= NTM_BOLD;
|
|
|
|
|
2012-03-27 12:48:42 +02:00
|
|
|
if (flags == 0) flags = NTM_REGULAR;
|
|
|
|
|
|
|
|
if (!pFT_Load_Sfnt_Table( ft_face, FT_MAKE_TAG( 'C','F','F',' ' ), 0, NULL, &table_size ))
|
|
|
|
flags |= NTM_PS_OPENTYPE;
|
|
|
|
|
|
|
|
return flags;
|
|
|
|
}
|
|
|
|
|
2013-07-31 16:16:26 +02:00
|
|
|
static inline void get_bitmap_size( FT_Face ft_face, Bitmap_Size *face_size )
|
2012-03-27 12:48:43 +02:00
|
|
|
{
|
2013-07-31 16:16:26 +02:00
|
|
|
My_FT_Bitmap_Size *size;
|
2012-03-27 12:48:43 +02:00
|
|
|
FT_WinFNT_HeaderRec winfnt_header;
|
|
|
|
|
2013-07-31 16:16:26 +02:00
|
|
|
size = (My_FT_Bitmap_Size *)ft_face->available_sizes;
|
|
|
|
TRACE("Adding bitmap size h %d w %d size %ld x_ppem %ld y_ppem %ld\n",
|
|
|
|
size->height, size->width, size->size >> 6,
|
|
|
|
size->x_ppem >> 6, size->y_ppem >> 6);
|
|
|
|
face_size->height = size->height;
|
|
|
|
face_size->width = size->width;
|
|
|
|
face_size->size = size->size;
|
|
|
|
face_size->x_ppem = size->x_ppem;
|
|
|
|
face_size->y_ppem = size->y_ppem;
|
2012-03-27 12:48:43 +02:00
|
|
|
|
2013-07-31 16:16:28 +02:00
|
|
|
if (!pFT_Get_WinFNT_Header( ft_face, &winfnt_header )) {
|
2013-07-31 16:16:26 +02:00
|
|
|
face_size->internal_leading = winfnt_header.internal_leading;
|
2013-07-31 16:16:28 +02:00
|
|
|
if (winfnt_header.external_leading > 0 &&
|
|
|
|
(face_size->height ==
|
|
|
|
winfnt_header.pixel_height + winfnt_header.external_leading))
|
|
|
|
face_size->height = winfnt_header.pixel_height;
|
|
|
|
}
|
2012-03-27 12:48:43 +02:00
|
|
|
}
|
|
|
|
|
2012-03-27 12:48:44 +02:00
|
|
|
static inline void get_fontsig( FT_Face ft_face, FONTSIGNATURE *fs )
|
|
|
|
{
|
|
|
|
TT_OS2 *os2;
|
|
|
|
CHARSETINFO csi;
|
|
|
|
FT_WinFNT_HeaderRec winfnt_header;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
memset( fs, 0, sizeof(*fs) );
|
|
|
|
|
|
|
|
os2 = pFT_Get_Sfnt_Table( ft_face, ft_sfnt_os2 );
|
|
|
|
if (os2)
|
|
|
|
{
|
|
|
|
fs->fsUsb[0] = os2->ulUnicodeRange1;
|
|
|
|
fs->fsUsb[1] = os2->ulUnicodeRange2;
|
|
|
|
fs->fsUsb[2] = os2->ulUnicodeRange3;
|
|
|
|
fs->fsUsb[3] = os2->ulUnicodeRange4;
|
|
|
|
|
|
|
|
if (os2->version == 0)
|
|
|
|
{
|
2018-11-29 10:28:15 +01:00
|
|
|
if (os2->usFirstCharIndex >= 0xf000 && os2->usFirstCharIndex < 0xf100)
|
2012-03-27 12:48:44 +02:00
|
|
|
fs->fsCsb[0] = FS_SYMBOL;
|
2018-11-29 10:28:15 +01:00
|
|
|
else
|
|
|
|
fs->fsCsb[0] = FS_LATIN1;
|
2012-03-27 12:48:44 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fs->fsCsb[0] = os2->ulCodePageRange1;
|
|
|
|
fs->fsCsb[1] = os2->ulCodePageRange2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (!pFT_Get_WinFNT_Header( ft_face, &winfnt_header ))
|
|
|
|
{
|
|
|
|
TRACE("pix_h %d charset %d dpi %dx%d pt %d\n", winfnt_header.pixel_height, winfnt_header.charset,
|
|
|
|
winfnt_header.vertical_resolution,winfnt_header.horizontal_resolution, winfnt_header.nominal_point_size);
|
|
|
|
if (TranslateCharsetInfo( (DWORD*)(UINT_PTR)winfnt_header.charset, &csi, TCI_SRCCHARSET ))
|
|
|
|
*fs = csi.fs;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fs->fsCsb[0] == 0)
|
|
|
|
{
|
|
|
|
/* let's see if we can find any interesting cmaps */
|
|
|
|
for (i = 0; i < ft_face->num_charmaps; i++)
|
|
|
|
{
|
|
|
|
switch (ft_face->charmaps[i]->encoding)
|
|
|
|
{
|
|
|
|
case FT_ENCODING_UNICODE:
|
|
|
|
case FT_ENCODING_APPLE_ROMAN:
|
|
|
|
fs->fsCsb[0] |= FS_LATIN1;
|
|
|
|
break;
|
|
|
|
case FT_ENCODING_MS_SYMBOL:
|
|
|
|
fs->fsCsb[0] |= FS_SYMBOL;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-06 15:40:25 +02:00
|
|
|
static Face *create_face( FT_Face ft_face, FT_Long face_index, const WCHAR *filename,
|
2020-10-06 15:12:09 +02:00
|
|
|
void *font_data_ptr, DWORD font_data_size, DWORD flags )
|
2011-12-20 13:05:43 +01:00
|
|
|
{
|
2012-03-27 12:48:47 +02:00
|
|
|
Face *face = HeapAlloc( GetProcessHeap(), 0, sizeof(*face) );
|
2011-12-20 13:05:43 +01:00
|
|
|
|
2013-01-14 15:19:14 +01:00
|
|
|
face->refcount = 1;
|
2020-09-07 15:18:57 +02:00
|
|
|
face->style_name = ft_face_get_style_name( ft_face, GetSystemDefaultLangID() );
|
2020-09-07 15:18:56 +02:00
|
|
|
face->full_name = ft_face_get_full_name( ft_face, GetSystemDefaultLangID() );
|
|
|
|
if (flags & ADDFONT_VERTICAL_FONT) face->full_name = get_vertical_name( face->full_name );
|
2012-09-12 15:51:24 +02:00
|
|
|
|
2020-10-06 15:40:25 +02:00
|
|
|
if (filename)
|
2012-03-27 12:48:46 +02:00
|
|
|
{
|
2020-10-06 15:40:25 +02:00
|
|
|
face->file = strdupW( filename );
|
2012-03-27 12:48:46 +02:00
|
|
|
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;
|
|
|
|
}
|
2012-03-27 12:48:47 +02:00
|
|
|
|
2012-03-27 12:48:46 +02:00
|
|
|
face->face_index = face_index;
|
2012-03-27 12:48:47 +02:00
|
|
|
get_fontsig( ft_face, &face->fs );
|
2012-03-27 12:48:46 +02:00
|
|
|
face->ntmFlags = get_ntm_flags( ft_face );
|
2012-03-27 12:48:47 +02:00
|
|
|
face->font_version = get_font_version( ft_face );
|
2012-03-27 12:48:46 +02:00
|
|
|
|
2012-03-27 12:48:47 +02:00
|
|
|
if (FT_IS_SCALABLE( ft_face ))
|
2012-03-27 12:48:46 +02:00
|
|
|
{
|
2012-03-27 12:48:47 +02:00
|
|
|
memset( &face->size, 0, sizeof(face->size) );
|
2012-03-27 12:48:46 +02:00
|
|
|
face->scalable = TRUE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-07-31 16:16:26 +02:00
|
|
|
get_bitmap_size( ft_face, &face->size );
|
2012-03-27 12:48:46 +02:00
|
|
|
face->scalable = FALSE;
|
|
|
|
}
|
2011-12-20 13:05:43 +01:00
|
|
|
|
2013-01-14 15:28:42 +01:00
|
|
|
if (!HIWORD( flags )) flags |= ADDFONT_AA_FLAGS( default_aa_flags );
|
|
|
|
face->flags = flags;
|
2012-03-27 12:48:47 +02:00
|
|
|
face->family = NULL;
|
|
|
|
face->cached_enum_data = NULL;
|
|
|
|
|
2012-03-27 12:48:46 +02:00
|
|
|
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]);
|
2011-12-20 13:05:43 +01:00
|
|
|
|
2012-03-27 12:48:47 +02:00
|
|
|
return face;
|
|
|
|
}
|
|
|
|
|
2020-10-06 15:40:25 +02:00
|
|
|
static void AddFaceToList(FT_Face ft_face, const WCHAR *file, void *font_data_ptr, DWORD font_data_size,
|
|
|
|
FT_Long face_index, DWORD flags )
|
2012-03-27 12:48:47 +02:00
|
|
|
{
|
|
|
|
Face *face;
|
|
|
|
Family *family;
|
|
|
|
|
2020-10-06 15:40:25 +02:00
|
|
|
face = create_face( ft_face, face_index, file, font_data_ptr, font_data_size, flags );
|
2013-01-15 14:13:09 +01:00
|
|
|
family = get_family( ft_face, flags & ADDFONT_VERTICAL_FONT );
|
2015-01-19 15:11:51 +01:00
|
|
|
|
2013-01-14 15:19:14 +01:00
|
|
|
if (insert_face_in_family_list( face, family ))
|
2012-03-27 12:48:47 +02:00
|
|
|
{
|
2013-01-14 15:19:14 +01:00
|
|
|
if (flags & ADDFONT_ADD_TO_CACHE)
|
|
|
|
add_face_to_cache( face );
|
2020-09-07 15:18:58 +02:00
|
|
|
TRACE( "Added face %s to family %s\n", debugstr_w(face->full_name), debugstr_w(family->family_name) );
|
2013-01-14 15:19:14 +01:00
|
|
|
}
|
|
|
|
release_face( face );
|
|
|
|
release_family( family );
|
2011-12-20 13:05:43 +01:00
|
|
|
}
|
|
|
|
|
2012-03-28 15:49:23 +02:00
|
|
|
static FT_Face new_ft_face( const char *file, void *font_data_ptr, DWORD font_data_size,
|
|
|
|
FT_Long face_index, BOOL allow_bitmap )
|
|
|
|
{
|
|
|
|
FT_Error err;
|
|
|
|
TT_OS2 *pOS2;
|
|
|
|
FT_Face ft_face;
|
|
|
|
|
|
|
|
if (file)
|
|
|
|
{
|
|
|
|
TRACE("Loading font file %s index %ld\n", debugstr_a(file), face_index);
|
|
|
|
err = pFT_New_Face(library, file, face_index, &ft_face);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
TRACE("Loading font from ptr %p size %d, index %ld\n", font_data_ptr, font_data_size, face_index);
|
|
|
|
err = pFT_New_Memory_Face(library, font_data_ptr, font_data_size, face_index, &ft_face);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (err != 0)
|
|
|
|
{
|
|
|
|
WARN("Unable to load font %s/%p err = %x\n", debugstr_a(file), font_data_ptr, err);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* There are too many bugs in FreeType < 2.1.9 for bitmap font support */
|
2019-02-08 02:51:32 +01:00
|
|
|
if (!FT_IS_SCALABLE( ft_face ) && FT_SimpleVersion < FT_VERSION_VALUE(2, 1, 9))
|
2012-03-28 15:49:23 +02:00
|
|
|
{
|
|
|
|
WARN("FreeType version < 2.1.9, skipping bitmap font %s/%p\n", debugstr_a(file), font_data_ptr);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!FT_IS_SFNT( ft_face ))
|
|
|
|
{
|
|
|
|
if (FT_IS_SCALABLE( ft_face ) || !allow_bitmap )
|
|
|
|
{
|
|
|
|
WARN("Ignoring font %s/%p\n", debugstr_a(file), font_data_ptr);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (!(pOS2 = pFT_Get_Sfnt_Table( ft_face, ft_sfnt_os2 )) ||
|
|
|
|
!pFT_Get_Sfnt_Table( ft_face, ft_sfnt_hhea ) ||
|
|
|
|
!pFT_Get_Sfnt_Table( ft_face, ft_sfnt_head ))
|
|
|
|
{
|
|
|
|
TRACE("Font %s/%p lacks either an OS2, HHEA or HEAD table.\n"
|
|
|
|
"Skipping this font.\n", debugstr_a(file), font_data_ptr);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Wine uses ttfs as an intermediate step in building its bitmap fonts;
|
|
|
|
we don't want to load these. */
|
|
|
|
if (!memcmp( pOS2->achVendID, "Wine", sizeof(pOS2->achVendID) ))
|
|
|
|
{
|
|
|
|
FT_ULong len = 0;
|
|
|
|
|
|
|
|
if (!pFT_Load_Sfnt_Table( ft_face, FT_MAKE_TAG('E','B','S','C'), 0, NULL, &len ))
|
|
|
|
{
|
|
|
|
TRACE("Skipping Wine bitmap-only TrueType font %s\n", debugstr_a(file));
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ft_face->family_name || !ft_face->style_name)
|
|
|
|
{
|
|
|
|
TRACE("Font %s/%p lacks either a family or style name\n", debugstr_a(file), font_data_ptr);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ft_face;
|
|
|
|
fail:
|
|
|
|
pFT_Done_Face( ft_face );
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2020-10-06 15:12:09 +02:00
|
|
|
static INT AddFontToList(const WCHAR *dos_name, const char *unix_name, void *font_data_ptr,
|
|
|
|
DWORD font_data_size, DWORD flags)
|
2001-09-12 22:21:06 +02:00
|
|
|
{
|
|
|
|
FT_Face ft_face;
|
2002-06-22 03:19:29 +02:00
|
|
|
FT_Long face_index = 0, num_faces;
|
2011-12-20 13:05:53 +01:00
|
|
|
INT ret = 0;
|
2020-10-06 15:40:25 +02:00
|
|
|
WCHAR *filename = NULL;
|
2012-11-05 22:32:51 +01:00
|
|
|
|
2007-09-13 19:42:55 +02:00
|
|
|
/* we always load external fonts from files - otherwise we would get a crash in update_reg_entries */
|
2020-10-06 15:12:09 +02:00
|
|
|
assert(unix_name || !(flags & ADDFONT_EXTERNAL_FONT));
|
2007-09-13 19:42:55 +02:00
|
|
|
|
2006-12-21 16:21:13 +01:00
|
|
|
#ifdef HAVE_CARBON_CARBON_H
|
2020-10-06 15:12:09 +02:00
|
|
|
if(unix_name)
|
2006-12-21 16:21:13 +01:00
|
|
|
{
|
2020-10-06 15:12:09 +02:00
|
|
|
char **mac_list = expand_mac_font(unix_name);
|
2006-12-21 16:21:13 +01:00
|
|
|
if(mac_list)
|
|
|
|
{
|
|
|
|
BOOL had_one = FALSE;
|
|
|
|
char **cursor;
|
|
|
|
for(cursor = mac_list; *cursor; cursor++)
|
|
|
|
{
|
|
|
|
had_one = TRUE;
|
2020-10-06 15:12:09 +02:00
|
|
|
AddFontToList(NULL, *cursor, NULL, 0, flags);
|
2006-12-21 16:21:13 +01:00
|
|
|
HeapFree(GetProcessHeap(), 0, *cursor);
|
|
|
|
}
|
|
|
|
HeapFree(GetProcessHeap(), 0, mac_list);
|
|
|
|
if(had_one)
|
2007-05-04 09:26:06 +02:00
|
|
|
return 1;
|
2006-12-21 16:21:13 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif /* HAVE_CARBON_CARBON_H */
|
|
|
|
|
2020-10-06 15:40:25 +02:00
|
|
|
if (!dos_name && unix_name) dos_name = filename = wine_get_dos_file_name( unix_name );
|
|
|
|
|
2002-06-22 03:19:29 +02:00
|
|
|
do {
|
2014-01-09 13:07:45 +01:00
|
|
|
FONTSIGNATURE fs;
|
|
|
|
|
2020-10-06 15:12:09 +02:00
|
|
|
ft_face = new_ft_face( unix_name, font_data_ptr, font_data_size, face_index, flags & ADDFONT_ALLOW_BITMAP );
|
2020-10-06 15:40:25 +02:00
|
|
|
if (!ft_face) break;
|
2003-06-23 22:51:06 +02:00
|
|
|
|
2008-03-21 11:32:45 +01:00
|
|
|
if(ft_face->family_name[0] == '.') /* Ignore fonts with names beginning with a dot */
|
|
|
|
{
|
2020-10-06 15:12:09 +02:00
|
|
|
TRACE("Ignoring %s since its family name begins with a dot\n", debugstr_a(unix_name));
|
2008-03-21 11:32:45 +01:00
|
|
|
pFT_Done_Face(ft_face);
|
2020-10-06 15:40:25 +02:00
|
|
|
break;
|
2008-03-21 11:32:45 +01:00
|
|
|
}
|
|
|
|
|
2020-10-06 15:40:25 +02:00
|
|
|
AddFaceToList(ft_face, dos_name, font_data_ptr, font_data_size, face_index, flags);
|
2011-12-20 13:05:53 +01:00
|
|
|
++ret;
|
|
|
|
|
2014-01-09 13:07:45 +01:00
|
|
|
get_fontsig(ft_face, &fs);
|
|
|
|
if (fs.fsCsb[0] & FS_DBCS_MASK)
|
2011-12-20 13:05:53 +01:00
|
|
|
{
|
2020-10-06 15:40:25 +02:00
|
|
|
AddFaceToList(ft_face, dos_name, font_data_ptr, font_data_size, face_index,
|
2013-01-15 14:13:09 +01:00
|
|
|
flags | ADDFONT_VERTICAL_FONT);
|
2011-12-20 13:05:53 +01:00
|
|
|
++ret;
|
|
|
|
}
|
2003-06-23 22:51:06 +02:00
|
|
|
|
2002-06-22 03:19:29 +02:00
|
|
|
num_faces = ft_face->num_faces;
|
|
|
|
pFT_Done_Face(ft_face);
|
|
|
|
} while(num_faces > ++face_index);
|
2020-10-06 15:40:25 +02:00
|
|
|
HeapFree( GetProcessHeap(), 0, filename );
|
2011-12-20 13:05:53 +01:00
|
|
|
return ret;
|
2001-09-12 22:21:06 +02:00
|
|
|
}
|
|
|
|
|
2020-04-17 12:39:01 +02:00
|
|
|
static int add_font_resource( const WCHAR *file, DWORD flags )
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
char *unixname = wine_get_unix_file_name( file );
|
|
|
|
|
|
|
|
if (unixname)
|
|
|
|
{
|
2020-10-06 15:12:09 +02:00
|
|
|
ret = AddFontToList( file, unixname, NULL, 0, flags );
|
2020-04-17 12:39:01 +02:00
|
|
|
HeapFree( GetProcessHeap(), 0, unixname );
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int remove_font_resource( const WCHAR *file, DWORD flags )
|
2013-01-15 12:34:40 +01:00
|
|
|
{
|
|
|
|
Family *family, *family_next;
|
|
|
|
Face *face, *face_next;
|
|
|
|
int count = 0;
|
|
|
|
|
|
|
|
LIST_FOR_EACH_ENTRY_SAFE( family, family_next, &font_list, Family, entry )
|
|
|
|
{
|
|
|
|
family->refcount++;
|
|
|
|
LIST_FOR_EACH_ENTRY_SAFE( face, face_next, &family->faces, Face, entry )
|
|
|
|
{
|
|
|
|
if (!face->file) continue;
|
|
|
|
if (LOWORD(face->flags) != LOWORD(flags)) continue;
|
2020-10-06 15:40:25 +02:00
|
|
|
if (!strcmpiW( face->file, file ))
|
2013-01-15 12:34:40 +01:00
|
|
|
{
|
2013-03-04 11:35:28 +01:00
|
|
|
TRACE( "removing matching face %s refcount %d\n", debugstr_w(face->file), face->refcount );
|
2013-01-15 12:34:40 +01:00
|
|
|
release_face( face );
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
release_family( family );
|
|
|
|
}
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2001-09-12 22:21:06 +02:00
|
|
|
static void DumpFontList(void)
|
|
|
|
{
|
|
|
|
Family *family;
|
|
|
|
Face *face;
|
|
|
|
|
2012-12-07 14:29:28 +01:00
|
|
|
LIST_FOR_EACH_ENTRY( family, &font_list, Family, entry ) {
|
2020-09-07 15:18:58 +02:00
|
|
|
TRACE( "Family: %s\n", debugstr_w(family->family_name) );
|
2012-12-07 14:29:28 +01:00
|
|
|
LIST_FOR_EACH_ENTRY( face, &family->faces, Face, entry ) {
|
2020-09-07 15:18:57 +02:00
|
|
|
TRACE( "\t%s\t%s\t%08x", debugstr_w(face->style_name), debugstr_w(face->full_name),
|
2020-09-07 15:18:56 +02:00
|
|
|
face->fs.fsCsb[0] );
|
2004-06-16 22:06:26 +02:00
|
|
|
if(!face->scalable)
|
2006-06-01 16:12:30 +02:00
|
|
|
TRACE(" %d", face->size.height);
|
2004-06-16 22:06:26 +02:00
|
|
|
TRACE("\n");
|
2001-09-12 22:21:06 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-04 11:43:15 +02:00
|
|
|
static BOOL map_vertical_font_family(const WCHAR *orig, const WCHAR *repl, const Family *family)
|
|
|
|
{
|
|
|
|
Face *face;
|
|
|
|
BOOL ret = FALSE;
|
|
|
|
WCHAR *at_orig, *at_repl = NULL;
|
|
|
|
|
|
|
|
face = LIST_ENTRY(list_head(&family->faces), Face, entry);
|
|
|
|
if (!face || !(face->fs.fsCsb[0] & FS_DBCS_MASK))
|
|
|
|
return FALSE;
|
|
|
|
|
2020-09-07 15:18:55 +02:00
|
|
|
at_orig = get_vertical_name( strdupW( orig ) );
|
2020-06-04 11:43:15 +02:00
|
|
|
if (at_orig && !find_family_from_any_name(at_orig))
|
|
|
|
{
|
2020-09-07 15:18:55 +02:00
|
|
|
at_repl = get_vertical_name( strdupW( repl ) );
|
2020-06-04 11:43:15 +02:00
|
|
|
if (at_repl)
|
|
|
|
ret = map_font_family(at_orig, at_repl);
|
|
|
|
}
|
|
|
|
|
|
|
|
HeapFree(GetProcessHeap(), 0, at_orig);
|
|
|
|
HeapFree(GetProcessHeap(), 0, at_repl);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-07-08 15:53:52 +02:00
|
|
|
static BOOL map_font_family(const WCHAR *orig, const WCHAR *repl)
|
|
|
|
{
|
|
|
|
Family *family = find_family_from_any_name(repl);
|
|
|
|
if (family != NULL)
|
|
|
|
{
|
|
|
|
Family *new_family = HeapAlloc(GetProcessHeap(), 0, sizeof(*new_family));
|
|
|
|
if (new_family != NULL)
|
|
|
|
{
|
|
|
|
TRACE("mapping %s to %s\n", debugstr_w(repl), debugstr_w(orig));
|
2020-09-07 15:18:58 +02:00
|
|
|
lstrcpynW( new_family->family_name, orig, LF_FACESIZE );
|
2020-09-17 19:30:43 +02:00
|
|
|
new_family->second_name[0] = 0;
|
2015-07-08 15:53:52 +02:00
|
|
|
list_init(&new_family->faces);
|
|
|
|
new_family->replacement = &family->faces;
|
|
|
|
list_add_tail(&font_list, &new_family->entry);
|
2020-06-04 11:43:15 +02:00
|
|
|
|
|
|
|
if (repl[0] != '@')
|
|
|
|
map_vertical_font_family(orig, repl, family);
|
|
|
|
|
2015-07-08 15:53:52 +02:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
TRACE("%s is not available. Skip this replacement.\n", debugstr_w(repl));
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2003-06-23 22:51:06 +02:00
|
|
|
/***********************************************************
|
|
|
|
* The replacement list is a way to map an entire font
|
|
|
|
* family onto another family. For example adding
|
|
|
|
*
|
2005-06-14 14:33:19 +02:00
|
|
|
* [HKCU\Software\Wine\Fonts\Replacements]
|
2003-06-23 22:51:06 +02:00
|
|
|
* "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;
|
2007-02-26 05:12:30 +01:00
|
|
|
LPWSTR value;
|
2003-06-23 22:51:06 +02:00
|
|
|
LPVOID data;
|
|
|
|
|
2005-06-14 14:33:19 +02:00
|
|
|
/* @@ Wine registry key: HKCU\Software\Wine\Fonts\Replacements */
|
|
|
|
if(RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Fonts\\Replacements", &hkey) == ERROR_SUCCESS)
|
|
|
|
{
|
2007-02-26 05:12:30 +01:00
|
|
|
RegQueryInfoKeyW(hkey, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
2003-06-23 22:51:06 +02:00
|
|
|
&valuelen, &datalen, NULL, NULL);
|
|
|
|
|
|
|
|
valuelen++; /* returned value doesn't include room for '\0' */
|
2007-02-26 05:12:30 +01:00
|
|
|
value = HeapAlloc(GetProcessHeap(), 0, valuelen * sizeof(WCHAR));
|
2003-06-23 22:51:06 +02:00
|
|
|
data = HeapAlloc(GetProcessHeap(), 0, datalen);
|
|
|
|
|
|
|
|
dlen = datalen;
|
|
|
|
vlen = valuelen;
|
2015-07-08 15:54:05 +02:00
|
|
|
while(RegEnumValueW(hkey, i++, value, &vlen, NULL, &type, data, &dlen) == ERROR_SUCCESS)
|
|
|
|
{
|
2003-06-23 22:51:06 +02:00
|
|
|
/* "NewName"="Oldname" */
|
2012-02-08 15:18:27 +01:00
|
|
|
if(!find_family_from_any_name(value))
|
2015-07-08 15:54:05 +02:00
|
|
|
{
|
|
|
|
if (type == REG_MULTI_SZ)
|
|
|
|
{
|
|
|
|
WCHAR *replace = data;
|
|
|
|
while(*replace)
|
|
|
|
{
|
|
|
|
if (map_font_family(value, replace))
|
|
|
|
break;
|
|
|
|
replace += strlenW(replace) + 1;
|
|
|
|
}
|
|
|
|
}
|
2016-02-11 23:50:49 +01:00
|
|
|
else if (type == REG_SZ)
|
2015-07-08 15:54:05 +02:00
|
|
|
map_font_family(value, data);
|
|
|
|
}
|
2012-02-15 11:40:52 +01:00
|
|
|
else
|
|
|
|
TRACE("%s is available. Skip this replacement.\n", debugstr_w(value));
|
2015-07-08 15:53:52 +02:00
|
|
|
|
2003-06-23 22:51:06 +02:00
|
|
|
/* reset dlen and vlen */
|
|
|
|
dlen = datalen;
|
|
|
|
vlen = valuelen;
|
|
|
|
}
|
|
|
|
HeapFree(GetProcessHeap(), 0, data);
|
|
|
|
HeapFree(GetProcessHeap(), 0, value);
|
|
|
|
RegCloseKey(hkey);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-13 17:45:27 +01:00
|
|
|
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 }
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2012-03-08 14:32:49 +01:00
|
|
|
static SYSTEM_LINKS *find_font_link(const WCHAR *name)
|
|
|
|
{
|
|
|
|
SYSTEM_LINKS *font_link;
|
|
|
|
|
|
|
|
LIST_FOR_EACH_ENTRY(font_link, &system_links, SYSTEM_LINKS, entry)
|
|
|
|
{
|
2017-11-26 14:33:17 +01:00
|
|
|
if(!strncmpiW(font_link->font_name, name, LF_FACESIZE - 1))
|
2012-03-08 14:32:49 +01:00
|
|
|
return font_link;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-02-13 17:45:27 +01:00
|
|
|
static void populate_system_links(const WCHAR *name, const WCHAR *const *values)
|
|
|
|
{
|
|
|
|
const WCHAR *value;
|
|
|
|
int i;
|
|
|
|
FontSubst *psub;
|
|
|
|
Family *family;
|
|
|
|
Face *face;
|
2012-11-01 13:16:26 +01:00
|
|
|
const WCHAR *file;
|
2012-02-13 17:45:27 +01:00
|
|
|
|
|
|
|
if (values)
|
|
|
|
{
|
|
|
|
SYSTEM_LINKS *font_link;
|
|
|
|
|
|
|
|
psub = get_font_subst(&font_subst_list, name, -1);
|
|
|
|
/* Don't store fonts that are only substitutes for other fonts */
|
|
|
|
if(psub)
|
|
|
|
{
|
|
|
|
TRACE("%s: Internal SystemLink entry for substituted font, ignoring\n", debugstr_w(name));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-03-08 14:32:49 +01:00
|
|
|
font_link = find_font_link(name);
|
|
|
|
if (font_link == NULL)
|
2012-02-13 17:45:27 +01:00
|
|
|
{
|
|
|
|
font_link = HeapAlloc(GetProcessHeap(), 0, sizeof(*font_link));
|
|
|
|
font_link->font_name = strdupW(name);
|
|
|
|
list_init(&font_link->links);
|
2012-03-08 14:32:49 +01:00
|
|
|
list_add_tail(&system_links, &font_link->entry);
|
2012-02-13 17:45:27 +01:00
|
|
|
}
|
|
|
|
|
2012-03-08 14:33:02 +01:00
|
|
|
memset(&font_link->fs, 0, sizeof font_link->fs);
|
2012-02-13 17:45:27 +01:00
|
|
|
for (i = 0; values[i] != NULL; i++)
|
|
|
|
{
|
2012-03-08 14:33:15 +01:00
|
|
|
const struct list *face_list;
|
2012-02-13 17:45:27 +01:00
|
|
|
CHILD_FONT *child_font;
|
|
|
|
|
|
|
|
value = values[i];
|
|
|
|
if (!strcmpiW(name,value))
|
|
|
|
continue;
|
|
|
|
psub = get_font_subst(&font_subst_list, value, -1);
|
|
|
|
if(psub)
|
|
|
|
value = psub->to.name;
|
|
|
|
family = find_family_from_name(value);
|
|
|
|
if (!family)
|
|
|
|
continue;
|
|
|
|
file = NULL;
|
|
|
|
/* Use first extant filename for this Family */
|
2012-03-08 14:33:15 +01:00
|
|
|
face_list = get_face_list_from_family(family);
|
|
|
|
LIST_FOR_EACH_ENTRY(face, face_list, Face, entry)
|
2012-02-13 17:45:27 +01:00
|
|
|
{
|
|
|
|
if (!face->file)
|
|
|
|
continue;
|
2020-10-06 15:12:09 +02:00
|
|
|
file = strrchrW(face->file, '\\');
|
2012-02-13 17:45:27 +01:00
|
|
|
if (!file)
|
|
|
|
file = face->file;
|
|
|
|
else
|
|
|
|
file++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!file)
|
|
|
|
continue;
|
2012-11-01 13:16:26 +01:00
|
|
|
face = find_face_from_filename(file, value);
|
2012-02-13 17:45:27 +01:00
|
|
|
if(!face)
|
|
|
|
{
|
2012-11-01 13:16:26 +01:00
|
|
|
TRACE("Unable to find file %s face name %s\n", debugstr_w(file), debugstr_w(value));
|
2012-02-13 17:45:27 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
child_font = HeapAlloc(GetProcessHeap(), 0, sizeof(*child_font));
|
|
|
|
child_font->face = face;
|
|
|
|
child_font->font = NULL;
|
2012-03-08 14:33:02 +01:00
|
|
|
font_link->fs.fsCsb[0] |= face->fs.fsCsb[0];
|
|
|
|
font_link->fs.fsCsb[1] |= face->fs.fsCsb[1];
|
2012-11-01 13:16:26 +01:00
|
|
|
TRACE("Adding file %s index %ld\n", debugstr_w(child_font->face->file),
|
|
|
|
child_font->face->face_index);
|
2012-02-13 17:45:27 +01:00
|
|
|
list_add_tail(&font_link->links, &child_font->entry);
|
|
|
|
|
2012-11-01 13:16:26 +01:00
|
|
|
TRACE("added internal SystemLink for %s to %s in %s\n", debugstr_w(name), debugstr_w(value),debugstr_w(file));
|
2012-02-13 17:45:27 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-09-06 11:24:07 +02:00
|
|
|
/*************************************************************
|
|
|
|
* init_system_links
|
|
|
|
*/
|
2019-03-12 21:19:19 +01:00
|
|
|
static void init_system_links(void)
|
2005-09-06 11:24:07 +02:00
|
|
|
{
|
|
|
|
HKEY hkey;
|
|
|
|
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;
|
2005-09-14 21:17:00 +02:00
|
|
|
static const WCHAR tahoma_ttf[] = {'t','a','h','o','m','a','.','t','t','f',0};
|
2005-09-06 11:24:07 +02:00
|
|
|
static const WCHAR System[] = {'S','y','s','t','e','m',0};
|
2012-02-13 17:45:27 +01:00
|
|
|
static const WCHAR MS_Shell_Dlg[] = {'M','S',' ','S','h','e','l','l',' ','D','l','g',0};
|
2005-09-06 11:24:07 +02:00
|
|
|
Face *face;
|
2006-09-12 23:41:50 +02:00
|
|
|
FontSubst *psub;
|
2012-02-13 17:45:27 +01:00
|
|
|
UINT i, j;
|
2005-09-06 11:24:07 +02:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2006-09-12 23:41:50 +02:00
|
|
|
psub = get_font_subst(&font_subst_list, value, -1);
|
2009-05-04 15:21:27 +02:00
|
|
|
/* 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));
|
2009-07-15 21:31:17 +02:00
|
|
|
goto next;
|
2009-05-04 15:21:27 +02:00
|
|
|
}
|
|
|
|
font_link = HeapAlloc(GetProcessHeap(), 0, sizeof(*font_link));
|
|
|
|
font_link->font_name = strdupW(value);
|
2012-03-08 14:33:02 +01:00
|
|
|
memset(&font_link->fs, 0, sizeof font_link->fs);
|
2005-09-06 11:24:07 +02:00
|
|
|
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;
|
|
|
|
|
2009-04-15 16:33:14 +02:00
|
|
|
TRACE("%s: %s\n", debugstr_w(value), debugstr_w(entry));
|
2005-09-06 11:24:07 +02:00
|
|
|
|
|
|
|
next = entry + strlenW(entry) + 1;
|
|
|
|
|
|
|
|
face_name = strchrW(entry, ',');
|
2006-04-10 13:35:23 +02:00
|
|
|
if(face_name)
|
2005-09-06 11:24:07 +02:00
|
|
|
{
|
|
|
|
*face_name++ = 0;
|
|
|
|
while(isspaceW(*face_name))
|
|
|
|
face_name++;
|
2006-09-12 23:41:50 +02:00
|
|
|
|
|
|
|
psub = get_font_subst(&font_subst_list, face_name, -1);
|
|
|
|
if(psub)
|
|
|
|
face_name = psub->to.name;
|
2005-09-06 11:24:07 +02:00
|
|
|
}
|
2006-04-10 13:35:23 +02:00
|
|
|
face = find_face_from_filename(entry, face_name);
|
2005-09-06 11:24:07 +02:00
|
|
|
if(!face)
|
|
|
|
{
|
2006-04-10 13:35:23 +02:00
|
|
|
TRACE("Unable to find file %s face name %s\n", debugstr_w(entry), debugstr_w(face_name));
|
2005-09-06 11:24:07 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
child_font = HeapAlloc(GetProcessHeap(), 0, sizeof(*child_font));
|
2007-09-06 20:40:00 +02:00
|
|
|
child_font->face = face;
|
2005-09-06 11:24:07 +02:00
|
|
|
child_font->font = NULL;
|
2012-03-08 14:33:02 +01:00
|
|
|
font_link->fs.fsCsb[0] |= face->fs.fsCsb[0];
|
|
|
|
font_link->fs.fsCsb[1] |= face->fs.fsCsb[1];
|
2012-11-01 13:16:26 +01:00
|
|
|
TRACE("Adding file %s index %ld\n",
|
|
|
|
debugstr_w(child_font->face->file), child_font->face->face_index);
|
2005-09-06 11:24:07 +02:00
|
|
|
list_add_tail(&font_link->links, &child_font->entry);
|
|
|
|
}
|
|
|
|
list_add_tail(&system_links, &font_link->entry);
|
2009-07-15 21:31:17 +02:00
|
|
|
next:
|
2005-09-06 11:24:07 +02:00
|
|
|
val_len = max_val + 1;
|
|
|
|
data_len = max_data;
|
|
|
|
}
|
|
|
|
|
|
|
|
HeapFree(GetProcessHeap(), 0, value);
|
|
|
|
HeapFree(GetProcessHeap(), 0, data);
|
|
|
|
RegCloseKey(hkey);
|
|
|
|
}
|
|
|
|
|
2012-02-07 14:18:34 +01:00
|
|
|
|
2012-02-13 17:45:27 +01:00
|
|
|
psub = get_font_subst(&font_subst_list, MS_Shell_Dlg, -1);
|
|
|
|
if (!psub) {
|
|
|
|
WARN("could not find FontSubstitute for MS Shell Dlg\n");
|
|
|
|
goto skip_internal;
|
|
|
|
}
|
2012-02-07 14:18:34 +01:00
|
|
|
|
2018-10-05 21:35:35 +02:00
|
|
|
for (i = 0; i < ARRAY_SIZE(font_links_defaults_list); i++)
|
2012-02-13 17:45:27 +01:00
|
|
|
{
|
|
|
|
const FontSubst *psub2;
|
|
|
|
psub2 = get_font_subst(&font_subst_list, font_links_defaults_list[i].shelldlg, -1);
|
2012-02-07 14:18:34 +01:00
|
|
|
|
2012-02-13 17:45:27 +01:00
|
|
|
if ((!strcmpiW(font_links_defaults_list[i].shelldlg, psub->to.name) || (psub2 && !strcmpiW(psub2->to.name,psub->to.name))))
|
|
|
|
{
|
2018-10-05 21:35:35 +02:00
|
|
|
for (j = 0; j < ARRAY_SIZE(font_links_list); j++)
|
2012-02-13 17:45:27 +01:00
|
|
|
populate_system_links(font_links_list[j], font_links_defaults_list[i].substitutes);
|
2012-02-07 14:18:34 +01:00
|
|
|
|
2012-02-13 17:45:27 +01:00
|
|
|
if (!strcmpiW(psub->to.name, font_links_defaults_list[i].substitutes[0]))
|
|
|
|
populate_system_links(psub->to.name, font_links_defaults_list[i].substitutes);
|
|
|
|
}
|
|
|
|
else if (strcmpiW(psub->to.name, font_links_defaults_list[i].substitutes[0]))
|
|
|
|
{
|
|
|
|
populate_system_links(font_links_defaults_list[i].substitutes[0], NULL);
|
2012-02-07 14:18:34 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-13 17:45:27 +01:00
|
|
|
skip_internal:
|
|
|
|
|
2005-09-06 11:24:07 +02:00
|
|
|
/* Explicitly add an entry for the system font, this links to Tahoma and any links
|
|
|
|
that Tahoma has */
|
2005-09-14 21:17:00 +02:00
|
|
|
|
2005-09-06 11:24:07 +02:00
|
|
|
system_font_link = HeapAlloc(GetProcessHeap(), 0, sizeof(*system_font_link));
|
|
|
|
system_font_link->font_name = strdupW(System);
|
2012-03-08 14:33:02 +01:00
|
|
|
memset(&system_font_link->fs, 0, sizeof system_font_link->fs);
|
2005-09-06 11:24:07 +02:00
|
|
|
list_init(&system_font_link->links);
|
2005-09-14 21:17:00 +02:00
|
|
|
|
2006-04-10 13:35:23 +02:00
|
|
|
face = find_face_from_filename(tahoma_ttf, Tahoma);
|
2005-09-14 21:17:00 +02:00
|
|
|
if(face)
|
|
|
|
{
|
|
|
|
child_font = HeapAlloc(GetProcessHeap(), 0, sizeof(*child_font));
|
2007-09-06 20:40:00 +02:00
|
|
|
child_font->face = face;
|
2005-09-14 21:17:00 +02:00
|
|
|
child_font->font = NULL;
|
2012-03-08 14:33:02 +01:00
|
|
|
system_font_link->fs.fsCsb[0] |= face->fs.fsCsb[0];
|
|
|
|
system_font_link->fs.fsCsb[1] |= face->fs.fsCsb[1];
|
2012-11-01 13:16:26 +01:00
|
|
|
TRACE("Found Tahoma in %s index %ld\n",
|
|
|
|
debugstr_w(child_font->face->file), child_font->face->face_index);
|
2005-09-14 21:17:00 +02:00
|
|
|
list_add_tail(&system_font_link->links, &child_font->entry);
|
|
|
|
}
|
2012-03-08 14:32:49 +01:00
|
|
|
font_link = find_font_link(Tahoma);
|
|
|
|
if (font_link != NULL)
|
2005-09-06 11:24:07 +02:00
|
|
|
{
|
2012-03-08 14:32:49 +01:00
|
|
|
CHILD_FONT *font_link_entry;
|
|
|
|
LIST_FOR_EACH_ENTRY(font_link_entry, &font_link->links, CHILD_FONT, entry)
|
2005-09-06 11:24:07 +02:00
|
|
|
{
|
2012-03-08 14:32:49 +01:00
|
|
|
CHILD_FONT *new_child;
|
|
|
|
new_child = HeapAlloc(GetProcessHeap(), 0, sizeof(*new_child));
|
|
|
|
new_child->face = font_link_entry->face;
|
|
|
|
new_child->font = NULL;
|
2013-01-14 15:19:14 +01:00
|
|
|
new_child->face->refcount++;
|
2012-03-08 14:33:02 +01:00
|
|
|
system_font_link->fs.fsCsb[0] |= font_link_entry->face->fs.fsCsb[0];
|
|
|
|
system_font_link->fs.fsCsb[1] |= font_link_entry->face->fs.fsCsb[1];
|
2012-03-08 14:32:49 +01:00
|
|
|
list_add_tail(&system_font_link->links, &new_child->entry);
|
2005-09-06 11:24:07 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
list_add_tail(&system_links, &system_font_link->entry);
|
|
|
|
}
|
2003-06-23 22:51:06 +02:00
|
|
|
|
2004-03-09 04:43:54 +01:00
|
|
|
static BOOL ReadFontDir(const char *dirname, BOOL external_fonts)
|
2001-09-12 22:21:06 +02:00
|
|
|
{
|
|
|
|
DIR *dir;
|
|
|
|
struct dirent *dent;
|
|
|
|
char path[MAX_PATH];
|
|
|
|
|
2002-01-29 04:02:50 +01:00
|
|
|
TRACE("Loading fonts from %s\n", debugstr_a(dirname));
|
|
|
|
|
2001-09-12 22:21:06 +02:00
|
|
|
dir = opendir(dirname);
|
|
|
|
if(!dir) {
|
2006-04-04 11:54:14 +02:00
|
|
|
WARN("Can't open directory %s\n", debugstr_a(dirname));
|
2001-09-12 22:21:06 +02:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
while((dent = readdir(dir)) != NULL) {
|
2002-01-29 04:02:50 +01:00
|
|
|
struct stat statbuf;
|
|
|
|
|
2001-09-12 22:21:06 +02:00
|
|
|
if(!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
|
|
|
|
continue;
|
2002-01-29 04:02:50 +01:00
|
|
|
|
|
|
|
TRACE("Found %s in %s\n", debugstr_a(dent->d_name), debugstr_a(dirname));
|
|
|
|
|
2001-09-12 22:21:06 +02:00
|
|
|
sprintf(path, "%s/%s", dirname, dent->d_name);
|
2002-01-29 04:02:50 +01:00
|
|
|
|
|
|
|
if(stat(path, &statbuf) == -1)
|
|
|
|
{
|
|
|
|
WARN("Can't stat %s\n", debugstr_a(path));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if(S_ISDIR(statbuf.st_mode))
|
2004-03-09 04:43:54 +01:00
|
|
|
ReadFontDir(path, external_fonts);
|
2002-01-29 04:02:50 +01:00
|
|
|
else
|
2011-10-06 23:26:06 +02:00
|
|
|
{
|
|
|
|
DWORD addfont_flags = ADDFONT_ADD_TO_CACHE;
|
|
|
|
if(external_fonts) addfont_flags |= ADDFONT_EXTERNAL_FONT;
|
2020-10-06 15:12:09 +02:00
|
|
|
AddFontToList(NULL, path, NULL, 0, addfont_flags);
|
2011-10-06 23:26:06 +02:00
|
|
|
}
|
2001-09-12 22:21:06 +02:00
|
|
|
}
|
2002-11-14 00:54:50 +01:00
|
|
|
closedir(dir);
|
2001-09-12 22:21:06 +02:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2020-04-17 12:39:15 +02:00
|
|
|
static void read_font_dir( const WCHAR *dirname, BOOL external_fonts )
|
|
|
|
{
|
|
|
|
char *unixname = wine_get_unix_file_name( dirname );
|
|
|
|
if (unixname)
|
|
|
|
{
|
|
|
|
ReadFontDir( unixname, external_fonts );
|
|
|
|
HeapFree( GetProcessHeap(), 0, unixname );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-04-05 00:41:10 +02:00
|
|
|
#ifdef SONAME_LIBFONTCONFIG
|
2012-11-05 22:32:51 +01:00
|
|
|
|
|
|
|
static BOOL fontconfig_enabled;
|
|
|
|
|
|
|
|
static UINT parse_aa_pattern( FcPattern *pattern )
|
|
|
|
{
|
|
|
|
FcBool antialias;
|
|
|
|
int rgba;
|
|
|
|
UINT aa_flags = 0;
|
|
|
|
|
|
|
|
if (pFcPatternGetBool( pattern, FC_ANTIALIAS, 0, &antialias ) == FcResultMatch)
|
|
|
|
aa_flags = antialias ? GGO_GRAY4_BITMAP : GGO_BITMAP;
|
|
|
|
|
|
|
|
if (pFcPatternGetInteger( pattern, FC_RGBA, 0, &rgba ) == FcResultMatch)
|
|
|
|
{
|
|
|
|
switch (rgba)
|
|
|
|
{
|
|
|
|
case FC_RGBA_RGB: aa_flags = WINE_GGO_HRGB_BITMAP; break;
|
|
|
|
case FC_RGBA_BGR: aa_flags = WINE_GGO_HBGR_BITMAP; break;
|
|
|
|
case FC_RGBA_VRGB: aa_flags = WINE_GGO_VRGB_BITMAP; break;
|
|
|
|
case FC_RGBA_VBGR: aa_flags = WINE_GGO_VBGR_BITMAP; break;
|
2018-10-29 13:50:21 +01:00
|
|
|
case FC_RGBA_NONE: aa_flags = aa_flags ? aa_flags : GGO_GRAY4_BITMAP; break;
|
2012-11-05 22:32:51 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return aa_flags;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void init_fontconfig(void)
|
|
|
|
{
|
2020-04-06 11:46:10 +02:00
|
|
|
void *fc_handle = dlopen(SONAME_LIBFONTCONFIG, RTLD_NOW);
|
2012-11-05 22:32:51 +01:00
|
|
|
|
|
|
|
if (!fc_handle)
|
|
|
|
{
|
|
|
|
TRACE("Wine cannot find the fontconfig library (%s).\n", SONAME_LIBFONTCONFIG);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-04-06 11:46:10 +02:00
|
|
|
#define LOAD_FUNCPTR(f) if((p##f = dlsym(fc_handle, #f)) == NULL){WARN("Can't find symbol %s\n", #f); return;}
|
2012-11-05 22:32:51 +01:00
|
|
|
LOAD_FUNCPTR(FcConfigSubstitute);
|
2018-11-27 16:50:20 +01:00
|
|
|
LOAD_FUNCPTR(FcDefaultSubstitute);
|
2012-11-05 22:32:51 +01:00
|
|
|
LOAD_FUNCPTR(FcFontList);
|
2018-11-27 16:50:20 +01:00
|
|
|
LOAD_FUNCPTR(FcFontMatch);
|
2012-11-05 22:32:51 +01:00
|
|
|
LOAD_FUNCPTR(FcFontSetDestroy);
|
|
|
|
LOAD_FUNCPTR(FcInit);
|
2018-11-27 16:50:20 +01:00
|
|
|
LOAD_FUNCPTR(FcPatternAddString);
|
2012-11-05 22:32:51 +01:00
|
|
|
LOAD_FUNCPTR(FcPatternCreate);
|
|
|
|
LOAD_FUNCPTR(FcPatternDestroy);
|
|
|
|
LOAD_FUNCPTR(FcPatternGetBool);
|
|
|
|
LOAD_FUNCPTR(FcPatternGetInteger);
|
|
|
|
LOAD_FUNCPTR(FcPatternGetString);
|
|
|
|
#undef LOAD_FUNCPTR
|
|
|
|
|
|
|
|
if (pFcInit())
|
|
|
|
{
|
|
|
|
FcPattern *pattern = pFcPatternCreate();
|
|
|
|
pFcConfigSubstitute( NULL, pattern, FcMatchFont );
|
|
|
|
default_aa_flags = parse_aa_pattern( pattern );
|
|
|
|
pFcPatternDestroy( pattern );
|
2018-10-23 18:07:11 +02:00
|
|
|
|
|
|
|
if (!default_aa_flags)
|
|
|
|
{
|
|
|
|
FcPattern *pattern = pFcPatternCreate();
|
|
|
|
pFcConfigSubstitute( NULL, pattern, FcMatchPattern );
|
|
|
|
default_aa_flags = parse_aa_pattern( pattern );
|
|
|
|
pFcPatternDestroy( pattern );
|
|
|
|
}
|
|
|
|
|
2012-11-05 22:32:51 +01:00
|
|
|
TRACE( "enabled, default flags = %x\n", default_aa_flags );
|
|
|
|
fontconfig_enabled = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-11-09 01:30:13 +01:00
|
|
|
static void load_fontconfig_fonts(void)
|
|
|
|
{
|
|
|
|
FcPattern *pat;
|
|
|
|
FcFontSet *fontset;
|
|
|
|
int i, len;
|
2006-10-30 22:05:43 +01:00
|
|
|
char *file;
|
|
|
|
const char *ext;
|
2003-11-09 01:30:13 +01:00
|
|
|
|
2012-11-05 22:32:51 +01:00
|
|
|
if (!fontconfig_enabled) return;
|
2012-11-01 12:35:52 +01:00
|
|
|
|
2003-11-09 01:30:13 +01:00
|
|
|
pat = pFcPatternCreate();
|
2018-10-29 13:50:20 +01:00
|
|
|
if (!pat) return;
|
|
|
|
|
|
|
|
fontset = pFcFontList(NULL, pat, NULL);
|
|
|
|
if (!fontset)
|
|
|
|
{
|
|
|
|
pFcPatternDestroy(pat);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2003-11-09 01:30:13 +01:00
|
|
|
for(i = 0; i < fontset->nfont; i++) {
|
2006-12-19 22:21:27 +01:00
|
|
|
FcBool scalable;
|
2012-11-05 22:32:51 +01:00
|
|
|
DWORD aa_flags;
|
2006-12-19 22:21:27 +01:00
|
|
|
|
2006-04-28 13:51:36 +02:00
|
|
|
if(pFcPatternGetString(fontset->fonts[i], FC_FILE, 0, (FcChar8**)&file) != FcResultMatch)
|
2003-11-09 01:30:13 +01:00
|
|
|
continue;
|
|
|
|
|
2012-11-01 12:35:52 +01:00
|
|
|
pFcConfigSubstitute( NULL, fontset->fonts[i], FcMatchFont );
|
|
|
|
|
2003-11-09 01:30:13 +01:00
|
|
|
/* We're just interested in OT/TT fonts for now, so this hack just
|
2006-12-19 22:21:27 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2012-11-05 22:32:51 +01:00
|
|
|
aa_flags = parse_aa_pattern( fontset->fonts[i] );
|
|
|
|
TRACE("fontconfig: %s aa %x\n", file, aa_flags);
|
2012-11-01 12:35:52 +01:00
|
|
|
|
2005-08-08 17:03:42 +02:00
|
|
|
len = strlen( file );
|
2003-11-09 01:30:13 +01:00
|
|
|
if(len < 4) continue;
|
2005-08-08 17:03:42 +02:00
|
|
|
ext = &file[ len - 3 ];
|
2019-04-08 14:30:23 +02:00
|
|
|
if(_strnicmp(ext, "pfa", -1) && _strnicmp(ext, "pfb", -1))
|
2020-10-06 15:12:09 +02:00
|
|
|
AddFontToList(NULL, file, NULL, 0,
|
2012-11-01 12:35:52 +01:00
|
|
|
ADDFONT_EXTERNAL_FONT | ADDFONT_ADD_TO_CACHE | ADDFONT_AA_FLAGS(aa_flags) );
|
2003-11-09 01:30:13 +01:00
|
|
|
}
|
|
|
|
pFcFontSetDestroy(fontset);
|
|
|
|
pFcPatternDestroy(pat);
|
|
|
|
}
|
2004-03-09 04:43:54 +01:00
|
|
|
|
2012-04-05 00:41:10 +02:00
|
|
|
#elif defined(HAVE_CARBON_CARBON_H)
|
|
|
|
|
|
|
|
static void load_mac_font_callback(const void *value, void *context)
|
|
|
|
{
|
|
|
|
CFStringRef pathStr = value;
|
|
|
|
CFIndex len;
|
|
|
|
char* path;
|
|
|
|
|
|
|
|
len = CFStringGetMaximumSizeOfFileSystemRepresentation(pathStr);
|
|
|
|
path = HeapAlloc(GetProcessHeap(), 0, len);
|
|
|
|
if (path && CFStringGetFileSystemRepresentation(pathStr, path, len))
|
|
|
|
{
|
|
|
|
TRACE("font file %s\n", path);
|
2020-10-06 15:12:09 +02:00
|
|
|
AddFontToList(NULL, path, NULL, 0, ADDFONT_EXTERNAL_FONT | ADDFONT_ADD_TO_CACHE);
|
2012-04-05 00:41:10 +02:00
|
|
|
}
|
|
|
|
HeapFree(GetProcessHeap(), 0, path);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void load_mac_fonts(void)
|
|
|
|
{
|
|
|
|
CFStringRef removeDupesKey;
|
|
|
|
CFBooleanRef removeDupesValue;
|
|
|
|
CFDictionaryRef options;
|
|
|
|
CTFontCollectionRef col;
|
|
|
|
CFArrayRef descs;
|
|
|
|
CFMutableSetRef paths;
|
|
|
|
CFIndex i;
|
|
|
|
|
|
|
|
removeDupesKey = kCTFontCollectionRemoveDuplicatesOption;
|
|
|
|
removeDupesValue = kCFBooleanTrue;
|
|
|
|
options = CFDictionaryCreate(NULL, (const void**)&removeDupesKey, (const void**)&removeDupesValue, 1,
|
|
|
|
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
|
|
|
col = CTFontCollectionCreateFromAvailableFonts(options);
|
|
|
|
if (options) CFRelease(options);
|
|
|
|
if (!col)
|
|
|
|
{
|
|
|
|
WARN("CTFontCollectionCreateFromAvailableFonts failed\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
descs = CTFontCollectionCreateMatchingFontDescriptors(col);
|
|
|
|
CFRelease(col);
|
|
|
|
if (!descs)
|
|
|
|
{
|
|
|
|
WARN("CTFontCollectionCreateMatchingFontDescriptors failed\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
paths = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
|
|
|
|
if (!paths)
|
|
|
|
{
|
|
|
|
WARN("CFSetCreateMutable failed\n");
|
|
|
|
CFRelease(descs);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < CFArrayGetCount(descs); i++)
|
|
|
|
{
|
|
|
|
CTFontDescriptorRef desc;
|
|
|
|
CFURLRef url;
|
|
|
|
CFStringRef ext;
|
|
|
|
CFStringRef path;
|
|
|
|
|
|
|
|
desc = CFArrayGetValueAtIndex(descs, i);
|
|
|
|
|
2016-11-29 18:22:32 +01:00
|
|
|
#if defined(MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
|
|
|
|
url = CTFontDescriptorCopyAttribute(desc, kCTFontURLAttribute);
|
|
|
|
#else
|
|
|
|
/* CTFontDescriptor doesn't support kCTFontURLAttribute prior to 10.6, so
|
2012-04-05 00:41:10 +02:00
|
|
|
we have to go CFFontDescriptor -> CTFont -> ATSFont -> FSRef -> CFURL. */
|
|
|
|
{
|
2016-11-29 18:22:32 +01:00
|
|
|
CTFontRef font;
|
|
|
|
ATSFontRef atsFont;
|
|
|
|
OSStatus status;
|
|
|
|
FSRef fsref;
|
2012-04-05 00:41:10 +02:00
|
|
|
|
2016-11-29 18:22:32 +01:00
|
|
|
font = CTFontCreateWithFontDescriptor(desc, 0, NULL);
|
|
|
|
if (!font) continue;
|
2012-04-05 00:41:10 +02:00
|
|
|
|
2016-11-29 18:22:32 +01:00
|
|
|
atsFont = CTFontGetPlatformFont(font, NULL);
|
|
|
|
if (!atsFont)
|
|
|
|
{
|
|
|
|
CFRelease(font);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
status = ATSFontGetFileReference(atsFont, &fsref);
|
|
|
|
CFRelease(font);
|
|
|
|
if (status != noErr) continue;
|
|
|
|
|
|
|
|
url = CFURLCreateFromFSRef(NULL, &fsref);
|
|
|
|
}
|
|
|
|
#endif
|
2012-04-05 00:41:10 +02:00
|
|
|
if (!url) continue;
|
|
|
|
|
|
|
|
ext = CFURLCopyPathExtension(url);
|
|
|
|
if (ext)
|
|
|
|
{
|
|
|
|
BOOL skip = (CFStringCompare(ext, CFSTR("pfa"), kCFCompareCaseInsensitive) == kCFCompareEqualTo ||
|
|
|
|
CFStringCompare(ext, CFSTR("pfb"), kCFCompareCaseInsensitive) == kCFCompareEqualTo);
|
|
|
|
CFRelease(ext);
|
|
|
|
if (skip)
|
|
|
|
{
|
|
|
|
CFRelease(url);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
path = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
|
|
|
|
CFRelease(url);
|
|
|
|
if (!path) continue;
|
|
|
|
|
|
|
|
CFSetAddValue(paths, path);
|
|
|
|
CFRelease(path);
|
|
|
|
}
|
|
|
|
|
|
|
|
CFRelease(descs);
|
|
|
|
|
|
|
|
CFSetApplyFunction(paths, load_mac_font_callback, NULL);
|
|
|
|
CFRelease(paths);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2020-04-17 12:39:15 +02:00
|
|
|
static void get_font_dir( WCHAR *path )
|
2016-03-17 09:23:24 +01:00
|
|
|
{
|
2020-04-17 12:39:15 +02:00
|
|
|
static const WCHAR slashW[] = {'\\',0};
|
|
|
|
static const WCHAR fontsW[] = {'\\','f','o','n','t','s',0};
|
|
|
|
static const WCHAR winedatadirW[] = {'W','I','N','E','D','A','T','A','D','I','R',0};
|
|
|
|
static const WCHAR winebuilddirW[] = {'W','I','N','E','B','U','I','L','D','D','I','R',0};
|
2016-03-17 09:23:24 +01:00
|
|
|
|
2020-04-17 12:39:15 +02:00
|
|
|
if (GetEnvironmentVariableW( winedatadirW, path, MAX_PATH ))
|
2016-03-17 09:23:24 +01:00
|
|
|
{
|
2020-04-17 12:39:15 +02:00
|
|
|
const char fontdir[] = WINE_FONT_DIR;
|
|
|
|
strcatW( path, slashW );
|
|
|
|
MultiByteToWideChar( CP_ACP, 0, fontdir, -1, path + strlenW(path), MAX_PATH - strlenW(path) );
|
2016-03-17 09:23:24 +01:00
|
|
|
}
|
2020-04-17 12:39:15 +02:00
|
|
|
else if (GetEnvironmentVariableW( winebuilddirW, path, MAX_PATH ))
|
2016-03-17 09:23:24 +01:00
|
|
|
{
|
2020-04-17 12:39:15 +02:00
|
|
|
strcatW( path, fontsW );
|
2016-03-17 09:23:24 +01:00
|
|
|
}
|
2020-10-06 15:12:09 +02:00
|
|
|
if (path[5] == ':') memmove( path, path + 4, (strlenW(path) - 3) * sizeof(WCHAR) );
|
|
|
|
else path[1] = '\\'; /* change \??\ to \\?\ */
|
2016-03-17 09:23:24 +01:00
|
|
|
}
|
|
|
|
|
2020-04-17 12:39:01 +02:00
|
|
|
static void get_data_dir_path( LPCWSTR file, WCHAR *path )
|
2006-03-09 08:13:08 +01:00
|
|
|
{
|
2020-04-17 12:39:01 +02:00
|
|
|
static const WCHAR slashW[] = {'\\','\0'};
|
2006-03-17 13:10:09 +01:00
|
|
|
|
2020-04-17 12:39:15 +02:00
|
|
|
get_font_dir( path );
|
|
|
|
strcatW( path, slashW );
|
|
|
|
strcatW( path, file );
|
2006-03-09 08:13:08 +01:00
|
|
|
}
|
2004-06-16 22:06:26 +02:00
|
|
|
|
2020-04-17 12:39:01 +02:00
|
|
|
static void get_winfonts_dir_path(LPCWSTR file, WCHAR *path)
|
2008-01-12 03:16:08 +01:00
|
|
|
{
|
|
|
|
static const WCHAR slashW[] = {'\\','\0'};
|
|
|
|
|
2020-04-17 12:39:01 +02:00
|
|
|
GetWindowsDirectoryW(path, MAX_PATH);
|
|
|
|
strcatW(path, fontsW);
|
|
|
|
strcatW(path, slashW);
|
|
|
|
strcatW(path, file);
|
2008-01-12 03:16:08 +01:00
|
|
|
}
|
|
|
|
|
2005-04-11 15:00:28 +02:00
|
|
|
static void load_system_fonts(void)
|
2004-06-16 22:06:26 +02:00
|
|
|
{
|
|
|
|
HKEY hkey;
|
2020-04-17 12:39:01 +02:00
|
|
|
WCHAR data[MAX_PATH], pathW[MAX_PATH];
|
2006-05-26 06:34:50 +02:00
|
|
|
const WCHAR * const *value;
|
2004-06-16 22:06:26 +02:00
|
|
|
DWORD dlen, type;
|
|
|
|
|
|
|
|
if(RegOpenKeyW(HKEY_CURRENT_CONFIG, system_fonts_reg_key, &hkey) == ERROR_SUCCESS) {
|
|
|
|
for(value = SystemFontValues; *value; value++) {
|
|
|
|
dlen = sizeof(data);
|
|
|
|
if(RegQueryValueExW(hkey, *value, 0, &type, (void*)data, &dlen) == ERROR_SUCCESS &&
|
|
|
|
type == REG_SZ) {
|
2020-04-17 12:39:01 +02:00
|
|
|
get_winfonts_dir_path( data, pathW );
|
|
|
|
if (!add_font_resource( pathW, ADDFONT_ALLOW_BITMAP | ADDFONT_ADD_TO_CACHE ))
|
|
|
|
{
|
|
|
|
get_data_dir_path( data, pathW );
|
|
|
|
add_font_resource( pathW, ADDFONT_ALLOW_BITMAP | ADDFONT_ADD_TO_CACHE );
|
2004-06-16 22:06:26 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
RegCloseKey(hkey);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-16 13:49:24 +02:00
|
|
|
static WCHAR *get_full_path_name(const WCHAR *name)
|
|
|
|
{
|
|
|
|
WCHAR *full_path;
|
|
|
|
DWORD len;
|
|
|
|
|
|
|
|
if (!(len = GetFullPathNameW(name, 0, NULL, NULL)))
|
|
|
|
{
|
|
|
|
ERR("GetFullPathNameW() failed, name %s.\n", debugstr_w(name));
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(full_path = HeapAlloc(GetProcessHeap(), 0, len * sizeof(*full_path))))
|
|
|
|
{
|
|
|
|
ERR("Could not get memory.\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (GetFullPathNameW(name, len, full_path, NULL) != len - 1)
|
|
|
|
{
|
|
|
|
ERR("Unexpected GetFullPathNameW() result, name %s.\n", debugstr_w(name));
|
|
|
|
HeapFree(GetProcessHeap(), 0, full_path);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return full_path;
|
|
|
|
}
|
|
|
|
|
2004-03-09 04:43:54 +01:00
|
|
|
/*************************************************************
|
|
|
|
*
|
|
|
|
* This adds registry entries for any externally loaded fonts
|
|
|
|
* (fonts from fontconfig or FontDirs). It also deletes entries
|
|
|
|
* of no longer existing fonts.
|
|
|
|
*
|
|
|
|
*/
|
2005-04-11 15:00:28 +02:00
|
|
|
static void update_reg_entries(void)
|
2004-03-09 04:43:54 +01:00
|
|
|
{
|
2008-03-07 18:02:32 +01:00
|
|
|
HKEY winnt_key = 0, win9x_key = 0, external_key = 0;
|
2004-03-09 04:43:54 +01:00
|
|
|
LPWSTR valueW;
|
2012-09-07 12:40:12 +02:00
|
|
|
DWORD len;
|
2004-03-09 04:43:54 +01:00
|
|
|
Family *family;
|
|
|
|
Face *face;
|
2020-10-06 15:12:09 +02:00
|
|
|
WCHAR *file, *path;
|
2004-04-19 22:12:14 +02:00
|
|
|
static const WCHAR TrueType[] = {' ','(','T','r','u','e','T','y','p','e',')','\0'};
|
2004-03-09 04:43:54 +01:00
|
|
|
|
2008-03-07 18:02:32 +01:00
|
|
|
if(RegCreateKeyExW(HKEY_LOCAL_MACHINE, winnt_font_reg_key,
|
|
|
|
0, NULL, 0, KEY_ALL_ACCESS, NULL, &winnt_key, NULL) != ERROR_SUCCESS) {
|
2004-03-09 04:43:54 +01:00
|
|
|
ERR("Can't create Windows font reg key\n");
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
2008-03-07 18:02:32 +01:00
|
|
|
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;
|
2004-03-09 04:43:54 +01:00
|
|
|
}
|
|
|
|
|
2005-06-28 21:15:17 +02:00
|
|
|
if(RegCreateKeyExW(HKEY_CURRENT_USER, external_fonts_reg_key,
|
2008-03-07 18:02:32 +01:00
|
|
|
0, NULL, 0, KEY_ALL_ACCESS, NULL, &external_key, NULL) != ERROR_SUCCESS) {
|
2004-03-09 04:43:54 +01:00
|
|
|
ERR("Can't create external font reg key\n");
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* enumerate the fonts and add external ones to the two keys */
|
|
|
|
|
2012-12-07 14:29:28 +01:00
|
|
|
LIST_FOR_EACH_ENTRY( family, &font_list, Family, entry ) {
|
|
|
|
LIST_FOR_EACH_ENTRY( face, &family->faces, Face, entry ) {
|
2013-01-14 15:28:42 +01:00
|
|
|
if (!(face->flags & ADDFONT_EXTERNAL_FONT)) continue;
|
2012-09-07 12:40:12 +02:00
|
|
|
|
2020-09-07 15:18:56 +02:00
|
|
|
len = strlenW( face->full_name ) + 1;
|
2016-08-14 22:52:16 +02:00
|
|
|
if (face->scalable)
|
2018-10-05 21:35:35 +02:00
|
|
|
len += ARRAY_SIZE(TrueType);
|
2016-08-14 22:52:16 +02:00
|
|
|
|
|
|
|
valueW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
|
2020-09-07 15:18:56 +02:00
|
|
|
strcpyW( valueW, face->full_name );
|
2016-08-14 22:52:16 +02:00
|
|
|
|
|
|
|
if (face->scalable)
|
|
|
|
strcatW(valueW, TrueType);
|
2008-03-10 13:31:43 +01:00
|
|
|
|
2020-10-06 15:12:09 +02:00
|
|
|
if ((path = get_full_path_name(face->file)))
|
2019-10-16 13:49:24 +02:00
|
|
|
{
|
2012-11-01 13:16:26 +01:00
|
|
|
file = path;
|
2019-10-16 13:49:24 +02:00
|
|
|
}
|
2020-10-06 15:12:09 +02:00
|
|
|
else if ((file = strrchrW(face->file, '\\')))
|
2019-10-16 13:49:24 +02:00
|
|
|
{
|
2012-11-01 13:16:26 +01:00
|
|
|
file++;
|
2019-10-16 13:49:24 +02:00
|
|
|
}
|
2004-03-09 04:43:54 +01:00
|
|
|
else
|
2019-10-16 13:49:24 +02:00
|
|
|
{
|
2012-11-01 13:16:26 +01:00
|
|
|
file = face->file;
|
2019-10-16 13:49:24 +02:00
|
|
|
}
|
2004-03-09 04:43:54 +01:00
|
|
|
|
2012-11-01 13:16:26 +01:00
|
|
|
len = strlenW(file) + 1;
|
2008-03-07 18:02:32 +01:00
|
|
|
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));
|
2004-03-09 04:43:54 +01:00
|
|
|
|
2012-11-01 13:16:26 +01:00
|
|
|
HeapFree(GetProcessHeap(), 0, path);
|
2004-03-09 04:43:54 +01:00
|
|
|
HeapFree(GetProcessHeap(), 0, valueW);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
end:
|
2008-03-07 18:02:32 +01:00
|
|
|
if(external_key) RegCloseKey(external_key);
|
|
|
|
if(win9x_key) RegCloseKey(win9x_key);
|
|
|
|
if(winnt_key) RegCloseKey(winnt_key);
|
2004-03-09 04:43:54 +01:00
|
|
|
}
|
|
|
|
|
2008-03-07 18:02:32 +01:00
|
|
|
static void delete_external_font_keys(void)
|
|
|
|
{
|
|
|
|
HKEY winnt_key = 0, win9x_key = 0, external_key = 0;
|
2017-04-25 12:39:29 +02:00
|
|
|
DWORD dlen, plen, vlen, datalen, valuelen, i, type, path_type;
|
2008-03-07 18:02:32 +01:00
|
|
|
LPWSTR valueW;
|
|
|
|
LPVOID data;
|
2017-04-25 12:39:29 +02:00
|
|
|
BYTE *path;
|
2008-03-07 18:02:32 +01:00
|
|
|
|
|
|
|
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));
|
2017-04-25 12:39:23 +02:00
|
|
|
data = HeapAlloc(GetProcessHeap(), 0, datalen);
|
2017-04-25 12:39:29 +02:00
|
|
|
path = HeapAlloc(GetProcessHeap(), 0, datalen);
|
2008-03-07 18:02:32 +01:00
|
|
|
|
2017-04-25 12:39:23 +02:00
|
|
|
dlen = datalen;
|
2008-03-07 18:02:32 +01:00
|
|
|
vlen = valuelen;
|
|
|
|
i = 0;
|
|
|
|
while(RegEnumValueW(external_key, i++, valueW, &vlen, NULL, &type, data,
|
|
|
|
&dlen) == ERROR_SUCCESS) {
|
2017-04-25 12:39:29 +02:00
|
|
|
plen = dlen;
|
|
|
|
if (RegQueryValueExW(winnt_key, valueW, 0, &path_type, path, &plen) == ERROR_SUCCESS &&
|
|
|
|
type == path_type && dlen == plen && !memcmp(data, path, plen))
|
|
|
|
RegDeleteValueW(winnt_key, valueW);
|
|
|
|
|
|
|
|
plen = dlen;
|
|
|
|
if (RegQueryValueExW(win9x_key, valueW, 0, &path_type, path, &plen) == ERROR_SUCCESS &&
|
|
|
|
type == path_type && dlen == plen && !memcmp(data, path, plen))
|
|
|
|
RegDeleteValueW(win9x_key, valueW);
|
2008-03-07 18:02:32 +01:00
|
|
|
|
|
|
|
/* reset dlen and vlen */
|
|
|
|
dlen = datalen;
|
|
|
|
vlen = valuelen;
|
|
|
|
}
|
2017-04-25 12:39:29 +02:00
|
|
|
HeapFree(GetProcessHeap(), 0, path);
|
2008-03-07 18:02:32 +01:00
|
|
|
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);
|
|
|
|
}
|
2004-03-09 04:43:54 +01:00
|
|
|
|
2002-06-22 03:19:29 +02:00
|
|
|
/*************************************************************
|
2020-10-20 22:07:06 +02:00
|
|
|
* freetype_AddFontResourceEx
|
2002-06-22 03:19:29 +02:00
|
|
|
*
|
|
|
|
*/
|
2020-10-20 22:07:06 +02:00
|
|
|
static INT CDECL freetype_AddFontResourceEx(LPCWSTR file, DWORD flags, PVOID pdv)
|
2002-06-22 03:19:29 +02:00
|
|
|
{
|
2020-04-17 12:39:01 +02:00
|
|
|
WCHAR path[MAX_PATH];
|
2008-01-12 03:16:08 +01:00
|
|
|
INT ret = 0;
|
2020-10-20 22:07:06 +02:00
|
|
|
DWORD addfont_flags = ADDFONT_ALLOW_BITMAP | ADDFONT_ADD_RESOURCE;
|
2009-01-28 19:38:15 +01:00
|
|
|
|
2020-10-20 22:07:06 +02:00
|
|
|
if (!(flags & FR_PRIVATE)) addfont_flags |= ADDFONT_ADD_TO_CACHE;
|
|
|
|
if (GetFullPathNameW( file, MAX_PATH, path, NULL ))
|
|
|
|
ret = add_font_resource( path, addfont_flags );
|
2011-10-06 23:26:06 +02:00
|
|
|
|
2020-10-20 22:07:06 +02:00
|
|
|
if (!ret && !strchrW(file, '\\')) {
|
|
|
|
/* Try in %WINDIR%/fonts, needed for Fotobuch Designer */
|
|
|
|
get_winfonts_dir_path( file, path );
|
|
|
|
ret = add_font_resource( path, ADDFONT_ALLOW_BITMAP | ADDFONT_ADD_RESOURCE );
|
|
|
|
/* Try in datadir/fonts (or builddir/fonts), needed for Magic the Gathering Online */
|
|
|
|
if (!ret)
|
|
|
|
{
|
|
|
|
get_data_dir_path( file, path );
|
2020-04-17 12:39:01 +02:00
|
|
|
ret = add_font_resource( path, ADDFONT_ALLOW_BITMAP | ADDFONT_ADD_RESOURCE );
|
2004-04-14 21:40:53 +02:00
|
|
|
}
|
2002-06-22 03:19:29 +02:00
|
|
|
}
|
2013-01-14 17:28:25 +01:00
|
|
|
return ret;
|
2002-06-22 03:19:29 +02:00
|
|
|
}
|
2002-01-29 04:02:50 +01:00
|
|
|
|
2007-09-13 19:42:55 +02:00
|
|
|
/*************************************************************
|
2020-10-20 22:07:06 +02:00
|
|
|
* freetype_AddFontMemResourceEx
|
2007-09-13 19:42:55 +02:00
|
|
|
*
|
|
|
|
*/
|
2020-10-20 22:07:06 +02:00
|
|
|
static HANDLE CDECL freetype_AddFontMemResourceEx(PVOID pbFont, DWORD cbFont, PVOID pdv, DWORD *pcFonts)
|
2007-09-13 19:42:55 +02:00
|
|
|
{
|
2020-10-20 22:07:06 +02:00
|
|
|
PVOID pFontCopy = HeapAlloc(GetProcessHeap(), 0, cbFont);
|
2007-09-13 19:42:55 +02:00
|
|
|
|
2020-10-20 22:07:06 +02:00
|
|
|
TRACE("Copying %d bytes of data from %p to %p\n", cbFont, pbFont, pFontCopy);
|
|
|
|
memcpy(pFontCopy, pbFont, cbFont);
|
2007-09-13 19:42:55 +02:00
|
|
|
|
2020-10-20 22:07:06 +02:00
|
|
|
*pcFonts = AddFontToList(NULL, NULL, pFontCopy, cbFont, ADDFONT_ALLOW_BITMAP | ADDFONT_ADD_RESOURCE);
|
|
|
|
if (*pcFonts == 0)
|
|
|
|
{
|
|
|
|
TRACE("AddFontToList failed\n");
|
|
|
|
HeapFree(GetProcessHeap(), 0, pFontCopy);
|
|
|
|
return 0;
|
2007-09-13 19:42:55 +02:00
|
|
|
}
|
2020-10-20 22:07:06 +02:00
|
|
|
/* 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);
|
2007-09-13 19:42:55 +02:00
|
|
|
}
|
|
|
|
|
2002-06-22 03:19:29 +02:00
|
|
|
/*************************************************************
|
2020-10-20 22:07:06 +02:00
|
|
|
* freetype_RemoveFontResourceEx
|
2002-06-22 03:19:29 +02:00
|
|
|
*
|
|
|
|
*/
|
2020-10-20 22:07:06 +02:00
|
|
|
static BOOL CDECL freetype_RemoveFontResourceEx(LPCWSTR file, DWORD flags, PVOID pdv)
|
2002-06-22 03:19:29 +02:00
|
|
|
{
|
2020-04-17 12:39:01 +02:00
|
|
|
WCHAR path[MAX_PATH];
|
2013-01-15 12:34:40 +01:00
|
|
|
INT ret = 0;
|
2020-10-20 22:07:06 +02:00
|
|
|
DWORD addfont_flags = ADDFONT_ALLOW_BITMAP | ADDFONT_ADD_RESOURCE;
|
2013-01-15 12:34:40 +01:00
|
|
|
|
2020-10-20 22:07:06 +02:00
|
|
|
if(!(flags & FR_PRIVATE)) addfont_flags |= ADDFONT_ADD_TO_CACHE;
|
|
|
|
if (GetFullPathNameW( file, MAX_PATH, path, NULL ))
|
|
|
|
ret = remove_font_resource( path, addfont_flags );
|
2013-01-15 12:34:40 +01:00
|
|
|
|
2020-10-20 22:07:06 +02:00
|
|
|
if (!ret && !strchrW(file, '\\'))
|
|
|
|
{
|
|
|
|
get_winfonts_dir_path( file, path );
|
|
|
|
ret = remove_font_resource( path, ADDFONT_ALLOW_BITMAP | ADDFONT_ADD_RESOURCE );
|
|
|
|
if (!ret)
|
2013-01-15 12:34:40 +01:00
|
|
|
{
|
2020-10-20 22:07:06 +02:00
|
|
|
get_data_dir_path( file, path );
|
2020-04-17 12:39:01 +02:00
|
|
|
ret = remove_font_resource( path, ADDFONT_ALLOW_BITMAP | ADDFONT_ADD_RESOURCE );
|
2013-01-15 12:34:40 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return ret;
|
2002-06-22 03:19:29 +02:00
|
|
|
}
|
2002-01-29 04:02:50 +01:00
|
|
|
|
2020-10-06 15:12:09 +02:00
|
|
|
static WCHAR *get_ttf_file_name( LPCWSTR font_file, LPCWSTR font_path )
|
2012-03-28 15:49:24 +02:00
|
|
|
{
|
|
|
|
WCHAR *fullname;
|
|
|
|
int file_len;
|
|
|
|
|
|
|
|
if (!font_file) return NULL;
|
|
|
|
|
|
|
|
file_len = strlenW( font_file );
|
|
|
|
|
|
|
|
if (font_path && font_path[0])
|
|
|
|
{
|
|
|
|
int path_len = strlenW( font_path );
|
|
|
|
fullname = HeapAlloc( GetProcessHeap(), 0, (file_len + path_len + 2) * sizeof(WCHAR) );
|
|
|
|
if (!fullname) return NULL;
|
|
|
|
memcpy( fullname, font_path, path_len * sizeof(WCHAR) );
|
|
|
|
fullname[path_len] = '\\';
|
|
|
|
memcpy( fullname + path_len + 1, font_file, (file_len + 1) * sizeof(WCHAR) );
|
2020-10-06 15:12:09 +02:00
|
|
|
return fullname;
|
2012-03-28 15:49:24 +02:00
|
|
|
}
|
2020-10-06 15:12:09 +02:00
|
|
|
return get_full_path_name( font_file );
|
2012-03-28 15:49:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#include <pshpack1.h>
|
|
|
|
struct fontdir
|
|
|
|
{
|
|
|
|
WORD num_of_resources;
|
|
|
|
WORD res_id;
|
|
|
|
WORD dfVersion;
|
|
|
|
DWORD dfSize;
|
|
|
|
CHAR dfCopyright[60];
|
|
|
|
WORD dfType;
|
|
|
|
WORD dfPoints;
|
|
|
|
WORD dfVertRes;
|
|
|
|
WORD dfHorizRes;
|
|
|
|
WORD dfAscent;
|
|
|
|
WORD dfInternalLeading;
|
|
|
|
WORD dfExternalLeading;
|
|
|
|
BYTE dfItalic;
|
|
|
|
BYTE dfUnderline;
|
|
|
|
BYTE dfStrikeOut;
|
|
|
|
WORD dfWeight;
|
|
|
|
BYTE dfCharSet;
|
|
|
|
WORD dfPixWidth;
|
|
|
|
WORD dfPixHeight;
|
|
|
|
BYTE dfPitchAndFamily;
|
|
|
|
WORD dfAvgWidth;
|
|
|
|
WORD dfMaxWidth;
|
|
|
|
BYTE dfFirstChar;
|
|
|
|
BYTE dfLastChar;
|
|
|
|
BYTE dfDefaultChar;
|
|
|
|
BYTE dfBreakChar;
|
|
|
|
WORD dfWidthBytes;
|
|
|
|
DWORD dfDevice;
|
|
|
|
DWORD dfFace;
|
|
|
|
DWORD dfReserved;
|
|
|
|
CHAR szFaceName[LF_FACESIZE];
|
|
|
|
};
|
|
|
|
|
|
|
|
#include <poppack.h>
|
|
|
|
|
2013-01-14 14:45:40 +01:00
|
|
|
static void GetEnumStructs(Face *face, const WCHAR *family_name, LPENUMLOGFONTEXW pelf,
|
2020-10-23 20:14:49 +02:00
|
|
|
NEWTEXTMETRICEXW *pntm);
|
2012-03-28 15:49:24 +02:00
|
|
|
|
2020-10-06 15:12:09 +02:00
|
|
|
static BOOL get_fontdir( const WCHAR *dos_name, struct fontdir *fd )
|
2012-03-28 15:49:24 +02:00
|
|
|
{
|
2020-10-06 15:12:09 +02:00
|
|
|
FT_Face ft_face;
|
|
|
|
Face *face = NULL;
|
|
|
|
char *unix_name;
|
2020-09-07 15:18:54 +02:00
|
|
|
WCHAR *family_name;
|
2012-03-28 15:49:24 +02:00
|
|
|
ENUMLOGFONTEXW elf;
|
|
|
|
NEWTEXTMETRICEXW ntm;
|
|
|
|
|
2020-10-06 15:12:09 +02:00
|
|
|
if (!(unix_name = wine_get_unix_file_name( dos_name ))) return FALSE;
|
|
|
|
ft_face = new_ft_face( unix_name, NULL, 0, 0, FALSE );
|
2020-10-06 15:40:25 +02:00
|
|
|
HeapFree( GetProcessHeap(), 0, unix_name );
|
|
|
|
if (!ft_face) return FALSE;
|
|
|
|
face = create_face( ft_face, 0, dos_name, NULL, 0, 0 );
|
|
|
|
if (face)
|
2020-10-06 15:12:09 +02:00
|
|
|
{
|
2020-10-06 15:40:25 +02:00
|
|
|
family_name = ft_face_get_family_name( ft_face, GetSystemDefaultLCID() );
|
2020-10-23 20:14:49 +02:00
|
|
|
GetEnumStructs( face, family_name, &elf, &ntm );
|
2020-10-06 15:40:25 +02:00
|
|
|
release_face( face );
|
|
|
|
HeapFree( GetProcessHeap(), 0, family_name );
|
2020-10-06 15:12:09 +02:00
|
|
|
}
|
2020-10-06 15:40:25 +02:00
|
|
|
pFT_Done_Face( ft_face );
|
2012-03-28 15:49:24 +02:00
|
|
|
|
2020-10-06 15:12:09 +02:00
|
|
|
if (!face) return FALSE;
|
2020-10-23 20:14:49 +02:00
|
|
|
if (!(ntm.ntmTm.tmPitchAndFamily & TMPF_TRUETYPE)) return FALSE;
|
2012-03-28 15:49:24 +02:00
|
|
|
|
|
|
|
memset( fd, 0, sizeof(*fd) );
|
|
|
|
|
|
|
|
fd->num_of_resources = 1;
|
|
|
|
fd->res_id = 0;
|
|
|
|
fd->dfVersion = 0x200;
|
|
|
|
fd->dfSize = sizeof(*fd);
|
|
|
|
strcpy( fd->dfCopyright, "Wine fontdir" );
|
|
|
|
fd->dfType = 0x4003; /* 0x0080 set if private */
|
|
|
|
fd->dfPoints = ntm.ntmTm.ntmSizeEM;
|
|
|
|
fd->dfVertRes = 72;
|
|
|
|
fd->dfHorizRes = 72;
|
|
|
|
fd->dfAscent = ntm.ntmTm.tmAscent;
|
|
|
|
fd->dfInternalLeading = ntm.ntmTm.tmInternalLeading;
|
|
|
|
fd->dfExternalLeading = ntm.ntmTm.tmExternalLeading;
|
|
|
|
fd->dfItalic = ntm.ntmTm.tmItalic;
|
|
|
|
fd->dfUnderline = ntm.ntmTm.tmUnderlined;
|
|
|
|
fd->dfStrikeOut = ntm.ntmTm.tmStruckOut;
|
|
|
|
fd->dfWeight = ntm.ntmTm.tmWeight;
|
|
|
|
fd->dfCharSet = ntm.ntmTm.tmCharSet;
|
|
|
|
fd->dfPixWidth = 0;
|
|
|
|
fd->dfPixHeight = ntm.ntmTm.tmHeight;
|
|
|
|
fd->dfPitchAndFamily = ntm.ntmTm.tmPitchAndFamily;
|
|
|
|
fd->dfAvgWidth = ntm.ntmTm.tmAveCharWidth;
|
|
|
|
fd->dfMaxWidth = ntm.ntmTm.tmMaxCharWidth;
|
|
|
|
fd->dfFirstChar = ntm.ntmTm.tmFirstChar;
|
|
|
|
fd->dfLastChar = ntm.ntmTm.tmLastChar;
|
|
|
|
fd->dfDefaultChar = ntm.ntmTm.tmDefaultChar;
|
|
|
|
fd->dfBreakChar = ntm.ntmTm.tmBreakChar;
|
|
|
|
fd->dfWidthBytes = 0;
|
|
|
|
fd->dfDevice = 0;
|
|
|
|
fd->dfFace = FIELD_OFFSET( struct fontdir, szFaceName );
|
|
|
|
fd->dfReserved = 0;
|
|
|
|
WideCharToMultiByte( CP_ACP, 0, elf.elfLogFont.lfFaceName, -1, fd->szFaceName, LF_FACESIZE, NULL, NULL );
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define NE_FFLAGS_LIBMODULE 0x8000
|
|
|
|
#define NE_OSFLAGS_WINDOWS 0x02
|
|
|
|
|
|
|
|
static const char dos_string[0x40] = "This is a TrueType resource file";
|
|
|
|
static const char FONTRES[] = {'F','O','N','T','R','E','S',':'};
|
|
|
|
|
|
|
|
#include <pshpack2.h>
|
|
|
|
|
|
|
|
struct ne_typeinfo
|
|
|
|
{
|
|
|
|
WORD type_id;
|
|
|
|
WORD count;
|
|
|
|
DWORD res;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ne_nameinfo
|
|
|
|
{
|
|
|
|
WORD off;
|
|
|
|
WORD len;
|
|
|
|
WORD flags;
|
|
|
|
WORD id;
|
|
|
|
DWORD res;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct rsrc_tab
|
|
|
|
{
|
|
|
|
WORD align;
|
|
|
|
struct ne_typeinfo fontdir_type;
|
|
|
|
struct ne_nameinfo fontdir_name;
|
|
|
|
struct ne_typeinfo scalable_type;
|
|
|
|
struct ne_nameinfo scalable_name;
|
|
|
|
WORD end_of_rsrc;
|
|
|
|
BYTE fontdir_res_name[8];
|
|
|
|
};
|
|
|
|
|
|
|
|
#include <poppack.h>
|
|
|
|
|
|
|
|
static BOOL create_fot( const WCHAR *resource, const WCHAR *font_file, const struct fontdir *fontdir )
|
|
|
|
{
|
|
|
|
BOOL ret = FALSE;
|
|
|
|
HANDLE file;
|
|
|
|
DWORD size, written;
|
|
|
|
BYTE *ptr, *start;
|
|
|
|
BYTE import_name_len, res_name_len, non_res_name_len, font_file_len;
|
|
|
|
char *font_fileA, *last_part, *ext;
|
|
|
|
IMAGE_DOS_HEADER dos;
|
|
|
|
IMAGE_OS2_HEADER ne =
|
|
|
|
{
|
|
|
|
IMAGE_OS2_SIGNATURE, 5, 1, 0, 0, 0, NE_FFLAGS_LIBMODULE, 0,
|
|
|
|
0, 0, 0, 0, 0, 0,
|
|
|
|
0, sizeof(ne), sizeof(ne), 0, 0, 0, 0,
|
|
|
|
0, 4, 2, NE_OSFLAGS_WINDOWS, 0, 0, 0, 0, 0x300
|
|
|
|
};
|
|
|
|
struct rsrc_tab rsrc_tab =
|
|
|
|
{
|
|
|
|
4,
|
|
|
|
{ 0x8007, 1, 0 },
|
|
|
|
{ 0, 0, 0x0c50, 0x2c, 0 },
|
|
|
|
{ 0x80cc, 1, 0 },
|
|
|
|
{ 0, 0, 0x0c50, 0x8001, 0 },
|
|
|
|
0,
|
|
|
|
{ 7,'F','O','N','T','D','I','R'}
|
|
|
|
};
|
|
|
|
|
|
|
|
memset( &dos, 0, sizeof(dos) );
|
|
|
|
dos.e_magic = IMAGE_DOS_SIGNATURE;
|
|
|
|
dos.e_lfanew = sizeof(dos) + sizeof(dos_string);
|
|
|
|
|
|
|
|
/* import name is last part\0, resident name is last part without extension
|
|
|
|
non-resident name is "FONTRES:" + lfFaceName */
|
|
|
|
|
|
|
|
font_file_len = WideCharToMultiByte( CP_ACP, 0, font_file, -1, NULL, 0, NULL, NULL );
|
|
|
|
font_fileA = HeapAlloc( GetProcessHeap(), 0, font_file_len );
|
|
|
|
WideCharToMultiByte( CP_ACP, 0, font_file, -1, font_fileA, font_file_len, NULL, NULL );
|
|
|
|
|
|
|
|
last_part = strrchr( font_fileA, '\\' );
|
|
|
|
if (last_part) last_part++;
|
|
|
|
else last_part = font_fileA;
|
|
|
|
import_name_len = strlen( last_part ) + 1;
|
|
|
|
|
|
|
|
ext = strchr( last_part, '.' );
|
|
|
|
if (ext) res_name_len = ext - last_part;
|
|
|
|
else res_name_len = import_name_len - 1;
|
|
|
|
|
|
|
|
non_res_name_len = sizeof( FONTRES ) + strlen( fontdir->szFaceName );
|
|
|
|
|
|
|
|
ne.ne_cbnrestab = 1 + non_res_name_len + 2 + 1; /* len + string + (WORD) ord_num + 1 byte eod */
|
|
|
|
ne.ne_restab = ne.ne_rsrctab + sizeof(rsrc_tab);
|
|
|
|
ne.ne_modtab = ne.ne_imptab = ne.ne_restab + 1 + res_name_len + 2 + 3; /* len + string + (WORD) ord_num + 3 bytes eod */
|
|
|
|
ne.ne_enttab = ne.ne_imptab + 1 + import_name_len; /* len + string */
|
|
|
|
ne.ne_cbenttab = 2;
|
|
|
|
ne.ne_nrestab = ne.ne_enttab + ne.ne_cbenttab + 2 + dos.e_lfanew; /* there are 2 bytes of 0 after entry tab */
|
|
|
|
|
|
|
|
rsrc_tab.scalable_name.off = (ne.ne_nrestab + ne.ne_cbnrestab + 0xf) >> 4;
|
|
|
|
rsrc_tab.scalable_name.len = (font_file_len + 0xf) >> 4;
|
|
|
|
rsrc_tab.fontdir_name.off = rsrc_tab.scalable_name.off + rsrc_tab.scalable_name.len;
|
|
|
|
rsrc_tab.fontdir_name.len = (fontdir->dfSize + 0xf) >> 4;
|
|
|
|
|
|
|
|
size = (rsrc_tab.fontdir_name.off + rsrc_tab.fontdir_name.len) << 4;
|
|
|
|
start = ptr = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, size );
|
|
|
|
|
|
|
|
if (!ptr)
|
|
|
|
{
|
|
|
|
HeapFree( GetProcessHeap(), 0, font_fileA );
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy( ptr, &dos, sizeof(dos) );
|
|
|
|
memcpy( ptr + sizeof(dos), dos_string, sizeof(dos_string) );
|
|
|
|
memcpy( ptr + dos.e_lfanew, &ne, sizeof(ne) );
|
|
|
|
|
|
|
|
ptr = start + dos.e_lfanew + ne.ne_rsrctab;
|
|
|
|
memcpy( ptr, &rsrc_tab, sizeof(rsrc_tab) );
|
|
|
|
|
|
|
|
ptr = start + dos.e_lfanew + ne.ne_restab;
|
|
|
|
*ptr++ = res_name_len;
|
|
|
|
memcpy( ptr, last_part, res_name_len );
|
|
|
|
|
|
|
|
ptr = start + dos.e_lfanew + ne.ne_imptab;
|
|
|
|
*ptr++ = import_name_len;
|
|
|
|
memcpy( ptr, last_part, import_name_len );
|
|
|
|
|
|
|
|
ptr = start + ne.ne_nrestab;
|
|
|
|
*ptr++ = non_res_name_len;
|
|
|
|
memcpy( ptr, FONTRES, sizeof(FONTRES) );
|
|
|
|
memcpy( ptr + sizeof(FONTRES), fontdir->szFaceName, strlen( fontdir->szFaceName ) );
|
|
|
|
|
|
|
|
ptr = start + (rsrc_tab.scalable_name.off << 4);
|
|
|
|
memcpy( ptr, font_fileA, font_file_len );
|
|
|
|
|
|
|
|
ptr = start + (rsrc_tab.fontdir_name.off << 4);
|
|
|
|
memcpy( ptr, fontdir, fontdir->dfSize );
|
|
|
|
|
|
|
|
file = CreateFileW( resource, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL );
|
|
|
|
if (file != INVALID_HANDLE_VALUE)
|
|
|
|
{
|
|
|
|
if (WriteFile( file, start, size, &written, NULL ) && written == size)
|
|
|
|
ret = TRUE;
|
|
|
|
CloseHandle( file );
|
|
|
|
}
|
|
|
|
|
|
|
|
HeapFree( GetProcessHeap(), 0, start );
|
|
|
|
HeapFree( GetProcessHeap(), 0, font_fileA );
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*************************************************************
|
2020-10-20 22:07:06 +02:00
|
|
|
* freetype_CreateScalableFontResource
|
2012-03-28 15:49:24 +02:00
|
|
|
*
|
|
|
|
*/
|
2020-10-20 22:07:06 +02:00
|
|
|
static BOOL CDECL freetype_CreateScalableFontResource( DWORD hidden, LPCWSTR resource,
|
|
|
|
LPCWSTR font_file, LPCWSTR font_path )
|
2012-03-28 15:49:24 +02:00
|
|
|
{
|
2020-10-06 15:12:09 +02:00
|
|
|
WCHAR *filename = get_ttf_file_name( font_file, font_path );
|
2012-03-28 15:49:24 +02:00
|
|
|
struct fontdir fontdir;
|
|
|
|
BOOL ret = FALSE;
|
|
|
|
|
2020-10-06 15:12:09 +02:00
|
|
|
if (!filename || !get_fontdir( filename, &fontdir ))
|
2012-03-28 15:49:24 +02:00
|
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (hidden) fontdir.dfType |= 0x80;
|
|
|
|
ret = create_fot( resource, font_file, &fontdir );
|
|
|
|
}
|
|
|
|
|
2020-10-06 15:12:09 +02:00
|
|
|
HeapFree( GetProcessHeap(), 0, filename );
|
2012-03-28 15:49:24 +02:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2008-06-06 23:14:39 +02:00
|
|
|
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 */
|
|
|
|
}
|
|
|
|
|
2008-03-12 14:45:13 +01:00
|
|
|
static BOOL init_freetype(void)
|
|
|
|
{
|
2020-04-06 11:46:10 +02:00
|
|
|
ft_handle = dlopen(SONAME_LIBFREETYPE, RTLD_NOW);
|
2002-04-03 22:02:39 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-04-06 11:46:10 +02:00
|
|
|
#define LOAD_FUNCPTR(f) if((p##f = dlsym(ft_handle, #f)) == NULL){WARN("Can't find symbol %s\n", #f); goto sym_not_found;}
|
2002-04-03 22:02:39 +02:00
|
|
|
|
|
|
|
LOAD_FUNCPTR(FT_Done_Face)
|
|
|
|
LOAD_FUNCPTR(FT_Get_Char_Index)
|
2011-10-11 11:14:56 +02:00
|
|
|
LOAD_FUNCPTR(FT_Get_First_Char)
|
|
|
|
LOAD_FUNCPTR(FT_Get_Next_Char)
|
2006-04-05 13:45:25 +02:00
|
|
|
LOAD_FUNCPTR(FT_Get_Sfnt_Name)
|
|
|
|
LOAD_FUNCPTR(FT_Get_Sfnt_Name_Count)
|
2002-04-03 22:02:39 +02:00
|
|
|
LOAD_FUNCPTR(FT_Get_Sfnt_Table)
|
2011-10-11 11:14:56 +02:00
|
|
|
LOAD_FUNCPTR(FT_Get_WinFNT_Header)
|
2002-04-03 22:02:39 +02:00
|
|
|
LOAD_FUNCPTR(FT_Init_FreeType)
|
2011-10-11 11:14:56 +02:00
|
|
|
LOAD_FUNCPTR(FT_Library_Version)
|
2002-04-03 22:02:39 +02:00
|
|
|
LOAD_FUNCPTR(FT_Load_Glyph)
|
2011-10-10 15:15:56 +02:00
|
|
|
LOAD_FUNCPTR(FT_Load_Sfnt_Table)
|
2003-10-10 02:06:35 +02:00
|
|
|
LOAD_FUNCPTR(FT_Matrix_Multiply)
|
2008-09-03 13:14:17 +02:00
|
|
|
#ifndef FT_MULFIX_INLINED
|
2002-04-03 22:02:39 +02:00
|
|
|
LOAD_FUNCPTR(FT_MulFix)
|
2008-09-03 13:14:17 +02:00
|
|
|
#endif
|
2002-04-03 22:02:39 +02:00
|
|
|
LOAD_FUNCPTR(FT_New_Face)
|
2006-12-06 21:31:53 +01:00
|
|
|
LOAD_FUNCPTR(FT_New_Memory_Face)
|
2002-04-03 22:02:39 +02:00
|
|
|
LOAD_FUNCPTR(FT_Outline_Get_Bitmap)
|
2013-10-17 14:25:10 +02:00
|
|
|
LOAD_FUNCPTR(FT_Outline_Get_CBox)
|
2002-04-03 22:02:39 +02:00
|
|
|
LOAD_FUNCPTR(FT_Outline_Transform)
|
|
|
|
LOAD_FUNCPTR(FT_Outline_Translate)
|
2011-10-10 15:15:56 +02:00
|
|
|
LOAD_FUNCPTR(FT_Render_Glyph)
|
2007-10-02 05:04:25 +02:00
|
|
|
LOAD_FUNCPTR(FT_Set_Charmap)
|
2002-04-03 22:02:39 +02:00
|
|
|
LOAD_FUNCPTR(FT_Set_Pixel_Sizes)
|
2016-01-13 15:09:08 +01:00
|
|
|
LOAD_FUNCPTR(FT_Vector_Length)
|
2003-10-10 02:06:35 +02:00
|
|
|
LOAD_FUNCPTR(FT_Vector_Transform)
|
2011-10-10 15:15:56 +02:00
|
|
|
LOAD_FUNCPTR(FT_Vector_Unit)
|
2002-04-03 22:02:39 +02:00
|
|
|
#undef LOAD_FUNCPTR
|
2008-03-12 14:45:13 +01:00
|
|
|
/* Don't warn if these ones are missing */
|
2020-04-06 11:46:10 +02:00
|
|
|
pFT_Outline_Embolden = dlsym(ft_handle, "FT_Outline_Embolden");
|
|
|
|
pFT_Get_TrueType_Engine_Type = dlsym(ft_handle, "FT_Get_TrueType_Engine_Type");
|
2013-11-30 11:22:25 +01:00
|
|
|
#ifdef FT_LCD_FILTER_H
|
2020-04-06 11:46:10 +02:00
|
|
|
pFT_Library_SetLcdFilter = dlsym(ft_handle, "FT_Library_SetLcdFilter");
|
2008-12-23 12:34:01 +01:00
|
|
|
#endif
|
2020-04-06 11:46:10 +02:00
|
|
|
pFT_Property_Set = dlsym(ft_handle, "FT_Property_Set");
|
2002-06-01 01:06:46 +02:00
|
|
|
|
2002-04-03 22:02:39 +02:00
|
|
|
if(pFT_Init_FreeType(&library) != 0) {
|
2001-09-12 22:21:06 +02:00
|
|
|
ERR("Can't init FreeType library\n");
|
2020-04-06 11:46:10 +02:00
|
|
|
dlclose(ft_handle);
|
2002-06-22 03:19:29 +02:00
|
|
|
ft_handle = NULL;
|
2001-09-12 22:21:06 +02:00
|
|
|
return FALSE;
|
|
|
|
}
|
2011-10-11 11:14:56 +02:00
|
|
|
pFT_Library_Version(library,&FT_Version.major,&FT_Version.minor,&FT_Version.patch);
|
2008-03-12 14:45:13 +01:00
|
|
|
|
2002-08-17 20:34:34 +02:00
|
|
|
TRACE("FreeType version is %d.%d.%d\n",FT_Version.major,FT_Version.minor,FT_Version.patch);
|
2004-06-16 22:06:26 +02:00
|
|
|
FT_SimpleVersion = ((FT_Version.major << 16) & 0xff0000) |
|
|
|
|
((FT_Version.minor << 8) & 0x00ff00) |
|
|
|
|
((FT_Version.patch ) & 0x0000ff);
|
2001-09-12 22:21:06 +02:00
|
|
|
|
2019-03-12 13:42:19 +01:00
|
|
|
/* In FreeType < 2.8.1 v40's FT_LOAD_TARGET_MONO has broken advance widths. */
|
2019-02-19 11:25:41 +01:00
|
|
|
if (pFT_Property_Set && FT_SimpleVersion < FT_VERSION_VALUE(2, 8, 1))
|
|
|
|
{
|
|
|
|
FT_UInt interpreter_version = 35;
|
|
|
|
pFT_Property_Set( library, "truetype", "interpreter-version", &interpreter_version );
|
|
|
|
}
|
|
|
|
|
2008-03-12 14:45:13 +01:00
|
|
|
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"
|
2011-10-10 15:15:56 +02:00
|
|
|
"FreeType to at least version 2.1.4.\n"
|
2008-03-12 14:45:13 +01:00
|
|
|
"http://www.freetype.org\n");
|
2020-04-06 11:46:10 +02:00
|
|
|
dlclose(ft_handle);
|
2008-03-12 14:45:13 +01:00
|
|
|
ft_handle = NULL;
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2011-10-06 23:26:05 +02:00
|
|
|
static void init_font_list(void)
|
2008-03-12 14:45:13 +01:00
|
|
|
{
|
|
|
|
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;
|
2020-04-17 12:39:15 +02:00
|
|
|
WCHAR path[MAX_PATH];
|
2008-03-12 14:45:13 +01:00
|
|
|
char *unixname;
|
|
|
|
|
2008-03-07 18:02:32 +01:00
|
|
|
delete_external_font_keys();
|
|
|
|
|
2006-03-17 16:36:11 +01:00
|
|
|
/* load the system bitmap fonts */
|
2004-07-14 23:42:35 +02:00
|
|
|
load_system_fonts();
|
|
|
|
|
2002-01-29 04:02:50 +01:00
|
|
|
/* load in the fonts from %WINDOWSDIR%\\Fonts first of all */
|
2020-04-17 12:39:15 +02:00
|
|
|
GetWindowsDirectoryW(path, ARRAY_SIZE(path));
|
|
|
|
strcatW(path, fontsW);
|
|
|
|
read_font_dir( path, FALSE );
|
2002-06-22 03:19:29 +02:00
|
|
|
|
2016-03-17 09:23:24 +01:00
|
|
|
/* load the wine fonts */
|
2020-04-17 12:39:15 +02:00
|
|
|
get_font_dir( path );
|
|
|
|
read_font_dir( path, TRUE );
|
2006-03-17 16:36:11 +01:00
|
|
|
|
2004-03-09 04:43:54 +01:00
|
|
|
/* now look under HKLM\Software\Microsoft\Windows[ NT]\CurrentVersion\Fonts
|
2002-06-22 03:19:29 +02:00
|
|
|
for any fonts not installed in %WINDOWSDIR%\Fonts. They will have their
|
2004-06-16 22:06:26 +02:00
|
|
|
full path as the entry. Also look for any .fon fonts, since ReadFontDir
|
|
|
|
will skip these. */
|
2004-03-09 04:43:54 +01:00
|
|
|
if(RegOpenKeyW(HKEY_LOCAL_MACHINE,
|
|
|
|
is_win9x() ? win9x_font_reg_key : winnt_font_reg_key,
|
2011-10-06 23:26:05 +02:00
|
|
|
&hkey) == ERROR_SUCCESS)
|
|
|
|
{
|
2009-01-26 15:09:18 +01:00
|
|
|
LPWSTR data, valueW;
|
2004-01-06 00:42:09 +01:00
|
|
|
RegQueryInfoKeyW(hkey, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
2011-10-06 23:26:05 +02:00
|
|
|
&valuelen, &datalen, NULL, NULL);
|
2002-06-22 03:19:29 +02:00
|
|
|
|
2011-10-06 23:26:05 +02:00
|
|
|
valuelen++; /* returned value doesn't include room for '\0' */
|
|
|
|
valueW = HeapAlloc(GetProcessHeap(), 0, valuelen * sizeof(WCHAR));
|
|
|
|
data = HeapAlloc(GetProcessHeap(), 0, datalen * sizeof(WCHAR));
|
2004-03-09 20:19:12 +01:00
|
|
|
if (valueW && data)
|
|
|
|
{
|
|
|
|
dlen = datalen * sizeof(WCHAR);
|
|
|
|
vlen = valuelen;
|
2009-01-26 15:09:18 +01:00
|
|
|
while(RegEnumValueW(hkey, i++, valueW, &vlen, NULL, &type, (LPBYTE)data,
|
2011-10-06 23:26:05 +02:00
|
|
|
&dlen) == ERROR_SUCCESS)
|
|
|
|
{
|
2009-01-26 15:09:18 +01:00
|
|
|
if(data[0] && (data[1] == ':'))
|
2004-06-16 22:06:26 +02:00
|
|
|
{
|
2020-04-17 12:39:01 +02:00
|
|
|
add_font_resource( data, ADDFONT_ALLOW_BITMAP | ADDFONT_ADD_TO_CACHE);
|
2004-06-16 22:06:26 +02:00
|
|
|
}
|
2009-01-26 15:09:18 +01:00
|
|
|
else if(dlen / 2 >= 6 && !strcmpiW(data + dlen / 2 - 5, dot_fonW))
|
2004-06-16 22:06:26 +02:00
|
|
|
{
|
|
|
|
WCHAR pathW[MAX_PATH];
|
2006-03-09 08:13:08 +01:00
|
|
|
|
2020-04-17 12:39:01 +02:00
|
|
|
get_winfonts_dir_path( data, pathW );
|
|
|
|
if (!add_font_resource( pathW, ADDFONT_ALLOW_BITMAP | ADDFONT_ADD_TO_CACHE ))
|
2004-06-16 22:06:26 +02:00
|
|
|
{
|
2020-04-17 12:39:01 +02:00
|
|
|
get_data_dir_path( data, pathW );
|
|
|
|
add_font_resource( pathW, ADDFONT_ALLOW_BITMAP | ADDFONT_ADD_TO_CACHE );
|
2004-04-14 21:40:53 +02:00
|
|
|
}
|
2004-06-16 22:06:26 +02:00
|
|
|
}
|
2004-03-09 20:19:12 +01:00
|
|
|
/* reset dlen and vlen */
|
|
|
|
dlen = datalen;
|
|
|
|
vlen = valuelen;
|
|
|
|
}
|
|
|
|
}
|
2004-12-23 18:06:43 +01:00
|
|
|
HeapFree(GetProcessHeap(), 0, data);
|
|
|
|
HeapFree(GetProcessHeap(), 0, valueW);
|
2011-10-06 23:26:05 +02:00
|
|
|
RegCloseKey(hkey);
|
2002-06-22 03:19:29 +02:00
|
|
|
}
|
|
|
|
|
2012-04-05 00:41:10 +02:00
|
|
|
#ifdef SONAME_LIBFONTCONFIG
|
2003-11-09 01:30:13 +01:00
|
|
|
load_fontconfig_fonts();
|
2012-04-05 00:41:10 +02:00
|
|
|
#elif defined(HAVE_CARBON_CARBON_H)
|
|
|
|
load_mac_fonts();
|
2016-04-07 09:59:48 +02:00
|
|
|
#elif defined(__ANDROID__)
|
|
|
|
ReadFontDir("/system/fonts", TRUE);
|
2012-04-05 00:41:10 +02:00
|
|
|
#endif
|
2002-01-29 04:02:50 +01:00
|
|
|
|
|
|
|
/* then look in any directories that we've specified in the config file */
|
2005-06-14 14:33:19 +02:00
|
|
|
/* @@ 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;
|
2001-09-12 22:21:06 +02:00
|
|
|
|
2005-06-14 14:33:19 +02:00
|
|
|
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)
|
|
|
|
{
|
2012-04-05 00:40:13 +02:00
|
|
|
const char* home;
|
2005-06-14 14:33:19 +02:00
|
|
|
LPSTR next = strchr( ptr, ':' );
|
|
|
|
if (next) *next++ = 0;
|
2011-10-26 19:27:51 +02:00
|
|
|
if (ptr[0] == '~' && ptr[1] == '/' && (home = getenv( "HOME" )) &&
|
|
|
|
(unixname = HeapAlloc( GetProcessHeap(), 0, strlen(ptr) + strlen(home) )))
|
|
|
|
{
|
|
|
|
strcpy( unixname, home );
|
|
|
|
strcat( unixname, ptr + 1 );
|
|
|
|
ReadFontDir( unixname, TRUE );
|
|
|
|
HeapFree( GetProcessHeap(), 0, unixname );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ReadFontDir( ptr, TRUE );
|
2005-06-14 14:33:19 +02:00
|
|
|
ptr = next;
|
|
|
|
}
|
|
|
|
HeapFree( GetProcessHeap(), 0, valueA );
|
|
|
|
}
|
|
|
|
HeapFree( GetProcessHeap(), 0, valueW );
|
|
|
|
}
|
|
|
|
RegCloseKey(hkey);
|
2001-09-12 22:21:06 +02:00
|
|
|
}
|
2011-10-06 23:26:05 +02:00
|
|
|
}
|
|
|
|
|
2011-10-06 23:26:08 +02:00
|
|
|
static BOOL move_to_front(const WCHAR *name)
|
|
|
|
{
|
|
|
|
Family *family, *cursor2;
|
|
|
|
LIST_FOR_EACH_ENTRY_SAFE(family, cursor2, &font_list, Family, entry)
|
|
|
|
{
|
2020-09-07 15:18:58 +02:00
|
|
|
if (!strncmpiW( family->family_name, name, LF_FACESIZE - 1 ))
|
2011-10-06 23:26:08 +02:00
|
|
|
{
|
|
|
|
list_remove(&family->entry);
|
|
|
|
list_add_head(&font_list, &family->entry);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2018-12-20 11:32:14 +01:00
|
|
|
static const WCHAR *set_default(const WCHAR **name_list)
|
2011-10-06 23:26:08 +02:00
|
|
|
{
|
2018-12-20 11:32:14 +01:00
|
|
|
const WCHAR **entry = name_list;
|
|
|
|
|
|
|
|
while (*entry)
|
2011-10-12 12:55:12 +02:00
|
|
|
{
|
2018-12-20 11:32:14 +01:00
|
|
|
if (move_to_front(*entry)) return *entry;
|
|
|
|
entry++;
|
2011-10-12 12:55:12 +02:00
|
|
|
}
|
2011-10-06 23:26:08 +02:00
|
|
|
|
2018-12-20 11:32:14 +01:00
|
|
|
return *name_list;
|
2011-10-12 12:55:12 +02:00
|
|
|
}
|
2011-10-06 23:26:08 +02:00
|
|
|
|
2011-10-12 12:55:12 +02:00
|
|
|
static void reorder_font_list(void)
|
|
|
|
{
|
2018-12-20 11:32:14 +01:00
|
|
|
default_serif = set_default( default_serif_list );
|
|
|
|
default_fixed = set_default( default_fixed_list );
|
|
|
|
default_sans = set_default( default_sans_list );
|
2011-10-06 23:26:08 +02:00
|
|
|
}
|
|
|
|
|
2011-10-06 23:26:05 +02:00
|
|
|
/*************************************************************
|
|
|
|
* WineEngInit
|
|
|
|
*
|
|
|
|
* Initialize FreeType library and create a list of available faces
|
|
|
|
*/
|
2020-10-20 22:05:37 +02:00
|
|
|
BOOL WineEngInit( const struct font_backend_funcs **funcs )
|
2011-10-06 23:26:05 +02:00
|
|
|
{
|
2015-09-29 09:27:15 +02:00
|
|
|
HKEY hkey;
|
2011-10-06 23:26:06 +02:00
|
|
|
DWORD disposition;
|
2011-10-06 23:26:05 +02:00
|
|
|
HANDLE font_mutex;
|
|
|
|
|
|
|
|
if(!init_freetype()) return FALSE;
|
|
|
|
|
2013-01-15 14:28:10 +01:00
|
|
|
#ifdef SONAME_LIBFONTCONFIG
|
|
|
|
init_fontconfig();
|
|
|
|
#endif
|
|
|
|
|
2020-10-20 22:05:37 +02:00
|
|
|
*funcs = &font_funcs;
|
|
|
|
|
2015-09-29 09:27:15 +02:00
|
|
|
if (!RegOpenKeyExW(HKEY_CURRENT_USER, wine_fonts_key, 0, KEY_READ, &hkey))
|
|
|
|
{
|
|
|
|
static const WCHAR antialias_fake_bold_or_italic[] = { 'A','n','t','i','a','l','i','a','s','F','a','k','e',
|
|
|
|
'B','o','l','d','O','r','I','t','a','l','i','c',0 };
|
|
|
|
static const WCHAR true_options[] = { 'y','Y','t','T','1',0 };
|
|
|
|
DWORD type, size;
|
|
|
|
WCHAR buffer[20];
|
|
|
|
|
|
|
|
size = sizeof(buffer);
|
|
|
|
if (!RegQueryValueExW(hkey, antialias_fake_bold_or_italic, NULL, &type, (BYTE*)buffer, &size) &&
|
|
|
|
type == REG_SZ && size >= 1)
|
|
|
|
{
|
|
|
|
antialias_fakes = (strchrW(true_options, buffer[0]) != NULL);
|
|
|
|
}
|
|
|
|
RegCloseKey(hkey);
|
|
|
|
}
|
|
|
|
|
2011-10-06 23:26:05 +02:00
|
|
|
if((font_mutex = CreateMutexW(NULL, FALSE, font_mutex_nameW)) == NULL)
|
|
|
|
{
|
|
|
|
ERR("Failed to create font mutex\n");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
WaitForSingleObject(font_mutex, INFINITE);
|
|
|
|
|
2011-10-06 23:26:06 +02:00
|
|
|
create_font_cache_key(&hkey_font_cache, &disposition);
|
|
|
|
|
2011-10-06 23:26:07 +02:00
|
|
|
if(disposition == REG_CREATED_NEW_KEY)
|
|
|
|
init_font_list();
|
|
|
|
else
|
|
|
|
load_font_list_from_cache(hkey_font_cache);
|
2002-01-29 04:02:50 +01:00
|
|
|
|
2011-10-06 23:26:08 +02:00
|
|
|
reorder_font_list();
|
|
|
|
|
2001-09-12 22:21:06 +02:00
|
|
|
DumpFontList();
|
2002-04-03 22:51:20 +02:00
|
|
|
LoadSubstList();
|
|
|
|
DumpSubstList();
|
2003-06-23 22:51:06 +02:00
|
|
|
LoadReplaceList();
|
2011-10-06 23:26:07 +02:00
|
|
|
|
|
|
|
if(disposition == REG_CREATED_NEW_KEY)
|
|
|
|
update_reg_entries();
|
2004-03-09 04:43:54 +01:00
|
|
|
|
2005-09-06 11:24:07 +02:00
|
|
|
init_system_links();
|
|
|
|
|
2004-03-09 04:43:54 +01:00
|
|
|
ReleaseMutex(font_mutex);
|
2001-09-12 22:21:06 +02:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2015-09-27 21:52:56 +02:00
|
|
|
/* Some fonts have large usWinDescent values, as a result of storing signed short
|
|
|
|
in unsigned field. That's probably caused by sTypoDescent vs usWinDescent confusion in
|
|
|
|
some font generation tools. */
|
|
|
|
static inline USHORT get_fixed_windescent(USHORT windescent)
|
|
|
|
{
|
|
|
|
return abs((SHORT)windescent);
|
|
|
|
}
|
2001-09-12 22:21:06 +02:00
|
|
|
|
2002-01-29 04:02:50 +01:00
|
|
|
static LONG calc_ppem_for_height(FT_Face ft_face, LONG height)
|
2001-09-12 22:21:06 +02:00
|
|
|
{
|
|
|
|
TT_OS2 *pOS2;
|
2003-12-08 22:53:15 +01:00
|
|
|
TT_HoriHeader *pHori;
|
|
|
|
|
2001-09-12 22:21:06 +02:00
|
|
|
LONG ppem;
|
2013-05-08 14:11:20 +02:00
|
|
|
const LONG MAX_PPEM = (1 << 16) - 1;
|
2001-09-12 22:21:06 +02:00
|
|
|
|
2002-04-03 22:02:39 +02:00
|
|
|
pOS2 = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_os2);
|
2003-12-08 22:53:15 +01:00
|
|
|
pHori = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_hhea);
|
2001-09-12 22:21:06 +02:00
|
|
|
|
|
|
|
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]
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2003-12-08 22:53:15 +01:00
|
|
|
if(height > 0) {
|
2015-09-27 21:52:56 +02:00
|
|
|
USHORT windescent = get_fixed_windescent(pOS2->usWinDescent);
|
|
|
|
if(pOS2->usWinAscent + windescent == 0)
|
2008-06-24 09:10:27 +02:00
|
|
|
ppem = MulDiv(ft_face->units_per_EM, height,
|
|
|
|
pHori->Ascender - pHori->Descender);
|
2003-12-08 22:53:15 +01:00
|
|
|
else
|
2008-06-24 09:10:27 +02:00
|
|
|
ppem = MulDiv(ft_face->units_per_EM, height,
|
2015-09-27 21:52:56 +02:00
|
|
|
pOS2->usWinAscent + windescent);
|
2013-05-08 14:11:20 +02:00
|
|
|
if(ppem > MAX_PPEM) {
|
|
|
|
WARN("Ignoring too large height %d, ppem %d\n", height, ppem);
|
|
|
|
ppem = 1;
|
|
|
|
}
|
2003-12-08 22:53:15 +01:00
|
|
|
}
|
2013-05-08 14:11:20 +02:00
|
|
|
else if(height >= -MAX_PPEM)
|
2001-09-12 22:21:06 +02:00
|
|
|
ppem = -height;
|
2013-05-08 14:11:20 +02:00
|
|
|
else {
|
|
|
|
WARN("Ignoring too large height %d\n", height);
|
|
|
|
ppem = 1;
|
|
|
|
}
|
2001-09-12 22:21:06 +02:00
|
|
|
|
2002-01-29 04:02:50 +01:00
|
|
|
return ppem;
|
|
|
|
}
|
|
|
|
|
2007-09-13 19:42:55 +02:00
|
|
|
static struct font_mapping *map_font_file( const char *name )
|
2006-12-06 21:31:53 +01:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2007-09-13 19:42:55 +02:00
|
|
|
static void unmap_font_file( struct font_mapping *mapping )
|
2006-12-06 21:31:53 +01:00
|
|
|
{
|
|
|
|
if (!--mapping->refcount)
|
|
|
|
{
|
|
|
|
list_remove( &mapping->entry );
|
|
|
|
munmap( mapping->data, mapping->size );
|
|
|
|
HeapFree( GetProcessHeap(), 0, mapping );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-09-28 08:11:06 +02:00
|
|
|
static LONG load_VDMX(GdiFont*, LONG);
|
2002-01-29 04:02:50 +01:00
|
|
|
|
2007-09-13 19:42:55 +02:00
|
|
|
static FT_Face OpenFontFace(GdiFont *font, Face *face, LONG width, LONG height)
|
2002-01-29 04:02:50 +01:00
|
|
|
{
|
2020-10-21 11:05:28 +02:00
|
|
|
struct gdi_font *gdi_font = font->gdi_font;
|
2002-01-29 04:02:50 +01:00
|
|
|
FT_Error err;
|
|
|
|
FT_Face ft_face;
|
2007-09-13 19:42:55 +02:00
|
|
|
void *data_ptr;
|
|
|
|
DWORD data_size;
|
2002-01-29 04:02:50 +01:00
|
|
|
|
2012-11-01 13:16:26 +01:00
|
|
|
TRACE("%s/%p, %ld, %d x %d\n", debugstr_w(face->file), face->font_data_ptr, face->face_index, width, height);
|
2006-12-06 21:31:53 +01:00
|
|
|
|
2007-09-13 19:42:55 +02:00
|
|
|
if (face->file)
|
2007-03-22 00:36:39 +01:00
|
|
|
{
|
2020-10-06 15:12:09 +02:00
|
|
|
char *filename = wine_get_unix_file_name( face->file );
|
2012-11-01 13:16:26 +01:00
|
|
|
font->mapping = map_font_file( filename );
|
|
|
|
HeapFree( GetProcessHeap(), 0, filename );
|
|
|
|
if (!font->mapping)
|
2007-09-13 19:42:55 +02:00
|
|
|
{
|
2012-11-01 13:16:26 +01:00
|
|
|
WARN("failed to map %s\n", debugstr_w(face->file));
|
2007-09-13 19:42:55 +02:00
|
|
|
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;
|
2007-03-22 00:36:39 +01:00
|
|
|
}
|
2006-12-06 21:31:53 +01:00
|
|
|
|
2007-09-13 19:42:55 +02:00
|
|
|
err = pFT_New_Memory_Face(library, data_ptr, data_size, face->face_index, &ft_face);
|
2002-01-29 04:02:50 +01:00
|
|
|
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;
|
2020-10-21 11:05:28 +02:00
|
|
|
gdi_font->scalable = FT_IS_SCALABLE(ft_face);
|
|
|
|
gdi_font->face_index = face->face_index;
|
2002-01-29 04:02:50 +01:00
|
|
|
|
2004-06-16 22:06:26 +02:00
|
|
|
if(FT_IS_SCALABLE(ft_face)) {
|
2016-08-12 12:14:50 +02:00
|
|
|
FT_ULong len;
|
|
|
|
DWORD header;
|
|
|
|
|
2004-06-16 22:06:26 +02:00
|
|
|
/* load the VDMX table if we have one */
|
2020-10-22 11:49:33 +02:00
|
|
|
gdi_font->ppem = load_VDMX(font, height);
|
|
|
|
if(gdi_font->ppem == 0)
|
|
|
|
gdi_font->ppem = calc_ppem_for_height(ft_face, height);
|
|
|
|
TRACE("height %d => ppem %d\n", height, gdi_font->ppem);
|
2002-01-29 04:02:50 +01:00
|
|
|
|
2020-10-22 11:49:33 +02:00
|
|
|
if((err = pFT_Set_Pixel_Sizes(ft_face, 0, gdi_font->ppem)) != 0)
|
|
|
|
WARN("FT_Set_Pixel_Sizes %d, %d rets %x\n", 0, gdi_font->ppem, err);
|
2016-08-12 12:14:50 +02:00
|
|
|
|
|
|
|
/* see if it's a TTC */
|
|
|
|
len = sizeof(header);
|
|
|
|
if (!pFT_Load_Sfnt_Table(ft_face, 0, 0, (void*)&header, &len)) {
|
|
|
|
if (header == MS_TTCF_TAG)
|
|
|
|
{
|
2020-10-22 11:57:48 +02:00
|
|
|
len = sizeof(gdi_font->ttc_item_offset);
|
2016-08-12 12:14:50 +02:00
|
|
|
if (pFT_Load_Sfnt_Table(ft_face, 0, (3 + face->face_index) * sizeof(DWORD),
|
2020-10-22 11:57:48 +02:00
|
|
|
(void*)&gdi_font->ttc_item_offset, &len))
|
|
|
|
gdi_font->ttc_item_offset = 0;
|
2016-08-12 12:14:50 +02:00
|
|
|
else
|
2020-10-22 11:57:48 +02:00
|
|
|
gdi_font->ttc_item_offset = GET_BE_DWORD(gdi_font->ttc_item_offset);
|
2016-08-12 12:14:50 +02:00
|
|
|
}
|
|
|
|
}
|
2004-06-16 22:06:26 +02:00
|
|
|
} else {
|
2020-10-22 11:49:33 +02:00
|
|
|
gdi_font->ppem = height;
|
2004-06-16 22:06:26 +02:00
|
|
|
if((err = pFT_Set_Pixel_Sizes(ft_face, width, height)) != 0)
|
2006-10-12 22:56:56 +02:00
|
|
|
WARN("FT_Set_Pixel_Sizes %d, %d rets %x\n", width, height, err);
|
2004-06-16 22:06:26 +02:00
|
|
|
}
|
2001-09-12 22:21:06 +02:00
|
|
|
return ft_face;
|
|
|
|
}
|
|
|
|
|
2002-01-29 04:02:50 +01:00
|
|
|
|
2020-10-21 11:04:35 +02:00
|
|
|
static UINT get_nearest_charset(const WCHAR *family_name, Face *face, UINT *cp)
|
2002-06-22 03:19:29 +02:00
|
|
|
{
|
|
|
|
/* 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 */
|
2002-01-29 04:02:50 +01:00
|
|
|
|
2002-06-22 03:19:29 +02:00
|
|
|
CHARSETINFO csi;
|
|
|
|
int acp = GetACP(), i;
|
|
|
|
DWORD fs0;
|
|
|
|
|
2004-08-26 20:24:03 +02:00
|
|
|
*cp = acp;
|
2006-07-19 09:17:49 +02:00
|
|
|
if(TranslateCharsetInfo((DWORD*)(INT_PTR)acp, &csi, TCI_SRCCODEPAGE))
|
2012-03-08 14:33:02 +01:00
|
|
|
{
|
|
|
|
const SYSTEM_LINKS *font_link;
|
|
|
|
|
|
|
|
if (csi.fs.fsCsb[0] & face->fs.fsCsb[0])
|
2002-06-22 03:19:29 +02:00
|
|
|
return csi.ciCharset;
|
|
|
|
|
2012-03-08 14:33:02 +01:00
|
|
|
font_link = find_font_link(family_name);
|
|
|
|
if (font_link != NULL && csi.fs.fsCsb[0] & font_link->fs.fsCsb[0])
|
|
|
|
return csi.ciCharset;
|
|
|
|
}
|
|
|
|
|
2002-06-22 03:19:29 +02:00
|
|
|
for(i = 0; i < 32; i++) {
|
|
|
|
fs0 = 1L << i;
|
2003-06-23 22:51:06 +02:00
|
|
|
if(face->fs.fsCsb[0] & fs0) {
|
2004-08-26 20:24:03 +02:00
|
|
|
if(TranslateCharsetInfo(&fs0, &csi, TCI_SRCFONTSIG)) {
|
|
|
|
*cp = csi.ciACP;
|
2002-06-22 03:19:29 +02:00
|
|
|
return csi.ciCharset;
|
2004-08-26 20:24:03 +02:00
|
|
|
}
|
2002-06-22 03:19:29 +02:00
|
|
|
else
|
2006-10-12 22:56:56 +02:00
|
|
|
FIXME("TCI failing on %x\n", fs0);
|
2002-06-22 03:19:29 +02:00
|
|
|
}
|
|
|
|
}
|
2002-01-29 04:02:50 +01:00
|
|
|
|
2006-10-12 22:56:56 +02:00
|
|
|
FIXME("returning DEFAULT_CHARSET face->fs.fsCsb[0] = %08x file = %s\n",
|
2012-11-01 13:16:26 +01:00
|
|
|
face->fs.fsCsb[0], debugstr_w(face->file));
|
2004-08-26 20:24:03 +02:00
|
|
|
*cp = acp;
|
2002-01-29 04:02:50 +01:00
|
|
|
return DEFAULT_CHARSET;
|
|
|
|
}
|
|
|
|
|
2020-10-20 22:05:37 +02:00
|
|
|
/*************************************************************
|
|
|
|
* freetype_alloc_font
|
|
|
|
*/
|
|
|
|
static BOOL CDECL freetype_alloc_font( struct gdi_font *font )
|
2002-01-29 04:02:50 +01:00
|
|
|
{
|
2006-09-28 08:11:06 +02:00
|
|
|
GdiFont *ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*ret));
|
2006-09-20 17:18:51 +02:00
|
|
|
ret->total_kern_pairs = (DWORD)-1;
|
|
|
|
ret->kern_pairs = NULL;
|
2005-09-07 11:21:50 +02:00
|
|
|
list_init(&ret->child_fonts);
|
2020-10-20 22:05:37 +02:00
|
|
|
ret->gdi_font = font;
|
|
|
|
font->private = ret;
|
|
|
|
return TRUE;
|
2002-01-29 04:02:50 +01:00
|
|
|
}
|
|
|
|
|
2020-10-20 22:05:37 +02:00
|
|
|
/*************************************************************
|
|
|
|
* freetype_destroy_font
|
|
|
|
*/
|
|
|
|
static void CDECL freetype_destroy_font( struct gdi_font *gdi_font )
|
2002-01-29 04:02:50 +01:00
|
|
|
{
|
2020-10-20 22:05:37 +02:00
|
|
|
GdiFont *font = get_font_ptr( gdi_font );
|
2012-12-07 14:29:28 +01:00
|
|
|
CHILD_FONT *child, *child_next;
|
2005-09-07 11:21:50 +02:00
|
|
|
|
2012-12-07 14:29:28 +01:00
|
|
|
LIST_FOR_EACH_ENTRY_SAFE( child, child_next, &font->child_fonts, CHILD_FONT, entry )
|
2005-09-07 11:21:50 +02:00
|
|
|
{
|
2012-12-07 14:29:28 +01:00
|
|
|
list_remove(&child->entry);
|
2005-09-14 13:15:05 +02:00
|
|
|
if(child->font)
|
2020-10-20 22:05:37 +02:00
|
|
|
free_gdi_font(child->font->gdi_font);
|
2013-01-14 15:19:14 +01:00
|
|
|
release_face( child->face );
|
2005-09-07 11:21:50 +02:00
|
|
|
HeapFree(GetProcessHeap(), 0, child);
|
|
|
|
}
|
|
|
|
|
2002-04-03 22:02:39 +02:00
|
|
|
if (font->ft_face) pFT_Done_Face(font->ft_face);
|
2007-09-13 19:42:55 +02:00
|
|
|
if (font->mapping) unmap_font_file( font->mapping );
|
2006-09-20 17:18:51 +02:00
|
|
|
HeapFree(GetProcessHeap(), 0, font->kern_pairs);
|
2008-03-13 10:40:58 +01:00
|
|
|
HeapFree(GetProcessHeap(), 0, font->GSUB_Table);
|
2002-01-29 04:02:50 +01:00
|
|
|
HeapFree(GetProcessHeap(), 0, font);
|
|
|
|
}
|
|
|
|
|
2020-10-22 11:57:48 +02:00
|
|
|
/*************************************************************
|
|
|
|
* freetype_get_font_data
|
|
|
|
*/
|
|
|
|
static DWORD CDECL freetype_get_font_data( struct gdi_font *font, DWORD table, DWORD offset,
|
|
|
|
void *buf, DWORD cbData)
|
2011-10-20 17:58:09 +02:00
|
|
|
{
|
2020-10-22 11:57:48 +02:00
|
|
|
FT_Face ft_face = get_font_ptr(font)->ft_face;
|
2011-10-20 17:58:09 +02:00
|
|
|
FT_ULong len;
|
|
|
|
FT_Error err;
|
|
|
|
|
|
|
|
if (!FT_IS_SFNT(ft_face)) return GDI_ERROR;
|
|
|
|
|
|
|
|
if(!buf)
|
|
|
|
len = 0;
|
|
|
|
else
|
|
|
|
len = cbData;
|
|
|
|
|
2016-08-12 12:14:50 +02:00
|
|
|
/* if font is a member of TTC, 'ttcf' tag allows reading from beginning of TTC file,
|
|
|
|
0 tag means to read from start of collection member data. */
|
|
|
|
if (font->ttc_item_offset)
|
|
|
|
{
|
|
|
|
if (table == MS_TTCF_TAG)
|
|
|
|
table = 0;
|
|
|
|
else if (table == 0)
|
|
|
|
offset += font->ttc_item_offset;
|
|
|
|
}
|
|
|
|
|
2011-10-20 17:58:09 +02:00
|
|
|
/* make sure value of len is the value freetype says it needs */
|
|
|
|
if (buf && len)
|
|
|
|
{
|
|
|
|
FT_ULong needed = 0;
|
2020-10-22 11:57:48 +02:00
|
|
|
err = pFT_Load_Sfnt_Table(ft_face, RtlUlongByteSwap(table), offset, NULL, &needed);
|
2011-10-20 17:58:09 +02:00
|
|
|
if( !err && needed < len) len = needed;
|
|
|
|
}
|
2020-10-22 11:57:48 +02:00
|
|
|
err = pFT_Load_Sfnt_Table(ft_face, RtlUlongByteSwap(table), offset, buf, &len);
|
2011-10-20 17:58:09 +02:00
|
|
|
if (err)
|
|
|
|
{
|
2016-08-14 22:52:15 +02:00
|
|
|
TRACE("Can't find table %s\n", debugstr_an((char*)&table, 4));
|
2011-10-20 17:58:09 +02:00
|
|
|
return GDI_ERROR;
|
|
|
|
}
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
2002-01-29 04:02:50 +01:00
|
|
|
/*************************************************************
|
|
|
|
* load_VDMX
|
|
|
|
*
|
|
|
|
* load the vdmx entry for the specified height
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-06-29 20:32:07 +02:00
|
|
|
typedef struct {
|
|
|
|
WORD version;
|
|
|
|
WORD numRecs;
|
|
|
|
WORD numRatios;
|
|
|
|
} VDMX_Header;
|
|
|
|
|
2002-01-29 04:02:50 +01:00
|
|
|
typedef struct {
|
|
|
|
BYTE bCharSet;
|
|
|
|
BYTE xRatio;
|
|
|
|
BYTE yStartRatio;
|
|
|
|
BYTE yEndRatio;
|
|
|
|
} Ratios;
|
|
|
|
|
2006-04-04 13:12:41 +02:00
|
|
|
typedef struct {
|
|
|
|
WORD recs;
|
|
|
|
BYTE startsz;
|
|
|
|
BYTE endsz;
|
|
|
|
} VDMX_group;
|
2002-01-29 04:02:50 +01:00
|
|
|
|
2015-06-29 20:32:07 +02:00
|
|
|
typedef struct {
|
|
|
|
WORD yPelHeight;
|
|
|
|
WORD yMax;
|
|
|
|
WORD yMin;
|
|
|
|
} VDMX_vTable;
|
|
|
|
|
2006-09-28 08:11:06 +02:00
|
|
|
static LONG load_VDMX(GdiFont *font, LONG height)
|
2002-01-29 04:02:50 +01:00
|
|
|
{
|
2020-10-22 11:49:33 +02:00
|
|
|
struct gdi_font *gdi_font = font->gdi_font;
|
2015-06-29 20:32:07 +02:00
|
|
|
VDMX_Header hdr;
|
2006-04-04 13:12:41 +02:00
|
|
|
VDMX_group group;
|
2002-01-29 04:02:50 +01:00
|
|
|
BYTE devXRatio, devYRatio;
|
|
|
|
USHORT numRecs, numRatios;
|
2004-09-08 03:23:57 +02:00
|
|
|
DWORD result, offset = -1;
|
2002-01-29 04:02:50 +01:00
|
|
|
LONG ppem = 0;
|
2004-09-08 03:23:57 +02:00
|
|
|
int i;
|
2002-01-29 04:02:50 +01:00
|
|
|
|
2020-10-22 11:57:48 +02:00
|
|
|
result = freetype_get_font_data(gdi_font, MS_VDMX_TAG, 0, &hdr, sizeof(hdr));
|
2002-01-29 04:02:50 +01:00
|
|
|
|
|
|
|
if(result == GDI_ERROR) /* no vdmx table present, use linear scaling */
|
|
|
|
return ppem;
|
|
|
|
|
|
|
|
/* FIXME: need the real device aspect ratio */
|
|
|
|
devXRatio = 1;
|
|
|
|
devYRatio = 1;
|
|
|
|
|
2015-06-29 20:32:07 +02:00
|
|
|
numRecs = GET_BE_WORD(hdr.numRecs);
|
|
|
|
numRatios = GET_BE_WORD(hdr.numRatios);
|
2002-01-29 04:02:50 +01:00
|
|
|
|
2015-06-29 20:32:07 +02:00
|
|
|
TRACE("version = %d numRecs = %d numRatios = %d\n", GET_BE_WORD(hdr.version), numRecs, numRatios);
|
2002-01-29 04:02:50 +01:00
|
|
|
for(i = 0; i < numRatios; i++) {
|
|
|
|
Ratios ratio;
|
2002-06-01 01:06:46 +02:00
|
|
|
|
2015-06-29 20:32:07 +02:00
|
|
|
offset = sizeof(hdr) + (i * sizeof(Ratios));
|
2020-10-22 11:57:48 +02:00
|
|
|
freetype_get_font_data(gdi_font, MS_VDMX_TAG, offset, &ratio, sizeof(Ratios));
|
2002-01-29 04:02:50 +01:00
|
|
|
offset = -1;
|
|
|
|
|
|
|
|
TRACE("Ratios[%d] %d %d : %d -> %d\n", i, ratio.bCharSet, ratio.xRatio, ratio.yStartRatio, ratio.yEndRatio);
|
|
|
|
|
2013-08-28 15:21:13 +02:00
|
|
|
if (!ratio.bCharSet) continue;
|
|
|
|
|
2002-06-01 01:06:46 +02:00
|
|
|
if((ratio.xRatio == 0 &&
|
2002-01-29 04:02:50 +01:00
|
|
|
ratio.yStartRatio == 0 &&
|
|
|
|
ratio.yEndRatio == 0) ||
|
2002-06-01 01:06:46 +02:00
|
|
|
(devXRatio == ratio.xRatio &&
|
2002-01-29 04:02:50 +01:00
|
|
|
devYRatio >= ratio.yStartRatio &&
|
2002-06-01 01:06:46 +02:00
|
|
|
devYRatio <= ratio.yEndRatio))
|
2002-01-29 04:02:50 +01:00
|
|
|
{
|
2015-06-29 20:32:07 +02:00
|
|
|
WORD group_offset;
|
2014-11-25 20:08:22 +01:00
|
|
|
|
2015-06-29 20:32:07 +02:00
|
|
|
offset = sizeof(hdr) + numRatios * sizeof(ratio) + i * sizeof(group_offset);
|
2020-10-22 11:57:48 +02:00
|
|
|
freetype_get_font_data(gdi_font, MS_VDMX_TAG, offset, &group_offset, sizeof(group_offset));
|
2015-06-29 20:32:07 +02:00
|
|
|
offset = GET_BE_WORD(group_offset);
|
2002-01-29 04:02:50 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-28 15:21:13 +02:00
|
|
|
if(offset == -1) return 0;
|
2002-01-29 04:02:50 +01:00
|
|
|
|
2020-10-22 11:57:48 +02:00
|
|
|
if(freetype_get_font_data(gdi_font, MS_VDMX_TAG, offset, &group, sizeof(group)) != GDI_ERROR) {
|
2002-01-29 04:02:50 +01:00
|
|
|
USHORT recs;
|
|
|
|
BYTE startsz, endsz;
|
2006-04-04 13:12:41 +02:00
|
|
|
WORD *vTable;
|
2002-01-29 04:02:50 +01:00
|
|
|
|
2006-04-04 13:12:41 +02:00
|
|
|
recs = GET_BE_WORD(group.recs);
|
|
|
|
startsz = group.startsz;
|
|
|
|
endsz = group.endsz;
|
2002-01-29 04:02:50 +01:00
|
|
|
|
|
|
|
TRACE("recs=%d startsz=%d endsz=%d\n", recs, startsz, endsz);
|
|
|
|
|
2015-06-29 20:32:07 +02:00
|
|
|
vTable = HeapAlloc(GetProcessHeap(), 0, recs * sizeof(VDMX_vTable));
|
2020-10-22 11:57:48 +02:00
|
|
|
result = freetype_get_font_data(gdi_font, MS_VDMX_TAG, offset + sizeof(group), vTable, recs * sizeof(VDMX_vTable));
|
2002-01-29 04:02:50 +01:00
|
|
|
if(result == GDI_ERROR) {
|
|
|
|
FIXME("Failed to retrieve vTable\n");
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(height > 0) {
|
|
|
|
for(i = 0; i < recs; i++) {
|
2006-04-04 13:12:41 +02:00
|
|
|
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]);
|
2002-01-29 04:02:50 +01:00
|
|
|
|
|
|
|
if(yMax + -yMin == height) {
|
2020-10-22 11:49:33 +02:00
|
|
|
gdi_font->yMax = yMax;
|
|
|
|
gdi_font->yMin = yMin;
|
|
|
|
TRACE("ppem %d found; height=%d yMax=%d yMin=%d\n", ppem, height, gdi_font->yMax, gdi_font->yMin);
|
2002-01-29 04:02:50 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if(yMax + -yMin > height) {
|
|
|
|
if(--i < 0) {
|
|
|
|
ppem = 0;
|
|
|
|
goto end; /* failed */
|
|
|
|
}
|
2020-10-22 11:49:33 +02:00
|
|
|
gdi_font->yMax = GET_BE_WORD(vTable[(i * 3) + 1]);
|
|
|
|
gdi_font->yMin = GET_BE_WORD(vTable[(i * 3) + 2]);
|
2006-09-20 12:53:06 +02:00
|
|
|
ppem = GET_BE_WORD(vTable[i * 3]);
|
2020-10-22 11:49:33 +02:00
|
|
|
TRACE("ppem %d found; height=%d yMax=%d yMin=%d\n", ppem, height, gdi_font->yMax, gdi_font->yMin);
|
2002-01-29 04:02:50 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2020-10-22 11:49:33 +02:00
|
|
|
if(!gdi_font->yMax) {
|
2002-01-29 04:02:50 +01:00
|
|
|
ppem = 0;
|
2006-10-12 22:56:56 +02:00
|
|
|
TRACE("ppem not found for height %d\n", height);
|
2002-01-29 04:02:50 +01:00
|
|
|
}
|
2013-08-28 15:21:11 +02:00
|
|
|
} else {
|
|
|
|
ppem = -height;
|
|
|
|
if(ppem < startsz || ppem > endsz)
|
2013-08-28 15:21:12 +02:00
|
|
|
{
|
|
|
|
ppem = 0;
|
|
|
|
goto end;
|
|
|
|
}
|
2013-08-28 15:21:11 +02:00
|
|
|
|
|
|
|
for(i = 0; i < recs; i++) {
|
|
|
|
USHORT yPelHeight;
|
|
|
|
yPelHeight = GET_BE_WORD(vTable[i * 3]);
|
|
|
|
|
|
|
|
if(yPelHeight > ppem)
|
2013-08-28 15:21:12 +02:00
|
|
|
{
|
|
|
|
ppem = 0;
|
|
|
|
break; /* failed */
|
|
|
|
}
|
2013-08-28 15:21:11 +02:00
|
|
|
|
|
|
|
if(yPelHeight == ppem) {
|
2020-10-22 11:49:33 +02:00
|
|
|
gdi_font->yMax = GET_BE_WORD(vTable[(i * 3) + 1]);
|
|
|
|
gdi_font->yMin = GET_BE_WORD(vTable[(i * 3) + 2]);
|
|
|
|
TRACE("ppem %d found; yMax=%d yMin=%d\n", ppem, gdi_font->yMax, gdi_font->yMin);
|
2013-08-28 15:21:11 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2002-01-29 04:02:50 +01:00
|
|
|
}
|
|
|
|
end:
|
|
|
|
HeapFree(GetProcessHeap(), 0, vTable);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ppem;
|
|
|
|
}
|
|
|
|
|
2005-09-07 11:21:50 +02:00
|
|
|
/*************************************************************
|
|
|
|
* create_child_font_list
|
|
|
|
*/
|
2006-09-28 08:11:06 +02:00
|
|
|
static BOOL create_child_font_list(GdiFont *font)
|
2005-09-07 11:21:50 +02:00
|
|
|
{
|
2020-10-21 11:03:51 +02:00
|
|
|
struct gdi_font *gdi_font = font->gdi_font;
|
2005-09-07 11:21:50 +02:00
|
|
|
BOOL ret = FALSE;
|
|
|
|
SYSTEM_LINKS *font_link;
|
|
|
|
CHILD_FONT *font_link_entry, *new_child;
|
2009-04-14 14:20:55 +02:00
|
|
|
FontSubst *psub;
|
2020-10-26 11:43:24 +01:00
|
|
|
const WCHAR* font_name;
|
2005-09-07 11:21:50 +02:00
|
|
|
|
2020-10-26 11:43:24 +01:00
|
|
|
psub = get_font_subst(&font_subst_list, get_gdi_font_name(gdi_font), -1);
|
|
|
|
font_name = psub ? psub->to.name : get_gdi_font_name(gdi_font);
|
2012-03-08 14:32:49 +01:00
|
|
|
font_link = find_font_link(font_name);
|
|
|
|
if (font_link != NULL)
|
2005-09-07 11:21:50 +02:00
|
|
|
{
|
2012-03-08 14:32:49 +01:00
|
|
|
TRACE("found entry in system list\n");
|
|
|
|
LIST_FOR_EACH_ENTRY(font_link_entry, &font_link->links, CHILD_FONT, entry)
|
2005-09-07 11:21:50 +02:00
|
|
|
{
|
2012-03-08 14:32:49 +01:00
|
|
|
new_child = HeapAlloc(GetProcessHeap(), 0, sizeof(*new_child));
|
|
|
|
new_child->face = font_link_entry->face;
|
|
|
|
new_child->font = NULL;
|
2013-01-14 15:19:14 +01:00
|
|
|
new_child->face->refcount++;
|
2012-03-08 14:32:49 +01:00
|
|
|
list_add_tail(&font->child_fonts, &new_child->entry);
|
2012-11-01 13:16:26 +01:00
|
|
|
TRACE("font %s %ld\n", debugstr_w(new_child->face->file), new_child->face->face_index);
|
2005-09-07 11:21:50 +02:00
|
|
|
}
|
2012-03-08 14:32:49 +01:00
|
|
|
ret = TRUE;
|
2005-09-07 11:21:50 +02:00
|
|
|
}
|
2008-02-18 16:49:07 +01:00
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
*/
|
2020-10-21 11:04:35 +02:00
|
|
|
if (is_dbcs_ansi_cp(GetACP()) && gdi_font->charset != SYMBOL_CHARSET &&
|
|
|
|
gdi_font->charset != OEM_CHARSET &&
|
2009-04-14 14:20:55 +02:00
|
|
|
strcmpiW(font_name,szDefaultFallbackLink) != 0)
|
2012-03-08 14:32:49 +01:00
|
|
|
{
|
|
|
|
font_link = find_font_link(szDefaultFallbackLink);
|
|
|
|
if (font_link != NULL)
|
2008-02-18 16:49:07 +01:00
|
|
|
{
|
2012-03-08 14:32:49 +01:00
|
|
|
TRACE("found entry in default fallback list\n");
|
|
|
|
LIST_FOR_EACH_ENTRY(font_link_entry, &font_link->links, CHILD_FONT, entry)
|
2008-02-18 16:49:07 +01:00
|
|
|
{
|
2012-03-08 14:32:49 +01:00
|
|
|
new_child = HeapAlloc(GetProcessHeap(), 0, sizeof(*new_child));
|
|
|
|
new_child->face = font_link_entry->face;
|
|
|
|
new_child->font = NULL;
|
2013-01-14 15:19:14 +01:00
|
|
|
new_child->face->refcount++;
|
2012-03-08 14:32:49 +01:00
|
|
|
list_add_tail(&font->child_fonts, &new_child->entry);
|
2012-11-01 13:16:26 +01:00
|
|
|
TRACE("font %s %ld\n", debugstr_w(new_child->face->file), new_child->face->face_index);
|
2008-02-18 16:49:07 +01:00
|
|
|
}
|
2012-03-08 14:32:49 +01:00
|
|
|
ret = TRUE;
|
2008-02-18 16:49:07 +01:00
|
|
|
}
|
2012-03-08 14:32:49 +01:00
|
|
|
}
|
2005-09-07 11:21:50 +02:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2007-10-02 05:04:25 +02:00
|
|
|
static BOOL select_charmap(FT_Face ft_face, FT_Encoding encoding)
|
|
|
|
{
|
|
|
|
FT_Error ft_err = FT_Err_Invalid_CharMap_Handle;
|
2017-09-26 11:13:46 +02:00
|
|
|
FT_CharMap cmap0, cmap1, cmap2, cmap3, cmap_def;
|
|
|
|
FT_Int i;
|
2007-10-02 05:04:25 +02:00
|
|
|
|
2017-09-26 11:13:46 +02:00
|
|
|
cmap0 = cmap1 = cmap2 = cmap3 = cmap_def = NULL;
|
2007-10-02 05:04:25 +02:00
|
|
|
|
2017-09-26 11:13:46 +02:00
|
|
|
for (i = 0; i < ft_face->num_charmaps; i++)
|
|
|
|
{
|
|
|
|
if (ft_face->charmaps[i]->encoding == encoding)
|
2007-10-02 05:04:25 +02:00
|
|
|
{
|
2017-09-26 11:13:46 +02:00
|
|
|
TRACE("found cmap with platform_id %u, encoding_id %u\n",
|
|
|
|
ft_face->charmaps[i]->platform_id, ft_face->charmaps[i]->encoding_id);
|
2007-10-02 05:04:25 +02:00
|
|
|
|
2017-09-26 11:13:46 +02:00
|
|
|
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;
|
2007-10-02 05:04:25 +02:00
|
|
|
}
|
|
|
|
}
|
2017-09-26 11:13:46 +02:00
|
|
|
|
|
|
|
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);
|
2007-10-02 05:04:25 +02:00
|
|
|
}
|
|
|
|
|
2017-09-26 11:13:46 +02:00
|
|
|
return ft_err == FT_Err_Ok;
|
2007-10-02 05:04:25 +02:00
|
|
|
}
|
|
|
|
|
2011-10-18 11:44:41 +02:00
|
|
|
|
2012-10-26 15:04:04 +02:00
|
|
|
static FT_Encoding pick_charmap( FT_Face face, int charset )
|
|
|
|
{
|
2012-10-26 15:04:05 +02:00
|
|
|
static const FT_Encoding regular_order[] = { FT_ENCODING_UNICODE, FT_ENCODING_APPLE_ROMAN, FT_ENCODING_MS_SYMBOL, 0 };
|
2012-10-26 15:04:04 +02:00
|
|
|
static const FT_Encoding symbol_order[] = { FT_ENCODING_MS_SYMBOL, FT_ENCODING_UNICODE, FT_ENCODING_APPLE_ROMAN, 0 };
|
|
|
|
const FT_Encoding *encs = regular_order;
|
|
|
|
|
|
|
|
if (charset == SYMBOL_CHARSET) encs = symbol_order;
|
|
|
|
|
|
|
|
while (*encs != 0)
|
|
|
|
{
|
|
|
|
if (select_charmap( face, *encs )) break;
|
|
|
|
encs++;
|
|
|
|
}
|
2017-09-26 11:13:47 +02:00
|
|
|
|
|
|
|
if (!face->charmap && face->num_charmaps)
|
|
|
|
{
|
|
|
|
if (!pFT_Set_Charmap(face, face->charmaps[0]))
|
|
|
|
return face->charmap->encoding;
|
|
|
|
}
|
|
|
|
|
2012-10-26 15:04:04 +02:00
|
|
|
return *encs;
|
|
|
|
}
|
2011-10-18 11:44:41 +02:00
|
|
|
|
2020-10-22 11:57:48 +02:00
|
|
|
static BOOL get_gasp_flags( struct gdi_font *gdi_font, WORD *flags )
|
2012-11-02 17:21:28 +01:00
|
|
|
{
|
2020-10-22 11:57:48 +02:00
|
|
|
GdiFont *font = get_font_ptr( gdi_font );
|
2012-11-02 17:21:28 +01:00
|
|
|
DWORD size;
|
|
|
|
WORD buf[16]; /* Enough for seven ranges before we need to alloc */
|
|
|
|
WORD *alloced = NULL, *ptr = buf;
|
|
|
|
WORD num_recs, version;
|
|
|
|
BOOL ret = FALSE;
|
|
|
|
|
|
|
|
*flags = 0;
|
2020-10-22 11:57:48 +02:00
|
|
|
size = freetype_get_font_data( gdi_font, MS_GASP_TAG, 0, NULL, 0 );
|
2012-11-02 17:21:28 +01:00
|
|
|
if (size == GDI_ERROR) return FALSE;
|
|
|
|
if (size < 4 * sizeof(WORD)) return FALSE;
|
|
|
|
if (size > sizeof(buf))
|
|
|
|
{
|
|
|
|
ptr = alloced = HeapAlloc( GetProcessHeap(), 0, size );
|
|
|
|
if (!ptr) return FALSE;
|
|
|
|
}
|
|
|
|
|
2020-10-22 11:57:48 +02:00
|
|
|
freetype_get_font_data( gdi_font, MS_GASP_TAG, 0, ptr, size );
|
2012-11-02 17:21:28 +01:00
|
|
|
|
|
|
|
version = GET_BE_WORD( *ptr++ );
|
|
|
|
num_recs = GET_BE_WORD( *ptr++ );
|
|
|
|
|
|
|
|
if (version > 1 || size < (num_recs * 2 + 2) * sizeof(WORD))
|
|
|
|
{
|
|
|
|
FIXME( "Unsupported gasp table: ver %d size %d recs %d\n", version, size, num_recs );
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (num_recs--)
|
|
|
|
{
|
|
|
|
*flags = GET_BE_WORD( *(ptr + 1) );
|
|
|
|
if (font->ft_face->size->metrics.y_ppem <= GET_BE_WORD( *ptr )) break;
|
|
|
|
ptr += 2;
|
|
|
|
}
|
|
|
|
TRACE( "got flags %04x for ppem %d\n", *flags, font->ft_face->size->metrics.y_ppem );
|
|
|
|
ret = TRUE;
|
|
|
|
|
|
|
|
done:
|
|
|
|
HeapFree( GetProcessHeap(), 0, alloced );
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-11-27 16:50:20 +01:00
|
|
|
#ifdef SONAME_LIBFONTCONFIG
|
2020-06-04 11:43:14 +02:00
|
|
|
static Family* get_fontconfig_family(DWORD pitch_and_family, const CHARSETINFO *csi, BOOL want_vertical)
|
2018-11-27 16:50:20 +01:00
|
|
|
{
|
|
|
|
const char *name;
|
|
|
|
WCHAR nameW[LF_FACESIZE];
|
|
|
|
FcChar8 *str;
|
|
|
|
FcPattern *pat = NULL, *best = NULL;
|
|
|
|
FcResult result;
|
|
|
|
FcBool r;
|
|
|
|
int ret, i;
|
|
|
|
Family *family = NULL;
|
|
|
|
|
|
|
|
if (!csi->fs.fsCsb[0]) return NULL;
|
|
|
|
|
|
|
|
if((pitch_and_family & FIXED_PITCH) ||
|
|
|
|
(pitch_and_family & 0xF0) == FF_MODERN)
|
|
|
|
name = "monospace";
|
|
|
|
else if((pitch_and_family & 0xF0) == FF_ROMAN)
|
|
|
|
name = "serif";
|
|
|
|
else
|
|
|
|
name = "sans-serif";
|
|
|
|
|
|
|
|
pat = pFcPatternCreate();
|
|
|
|
if (!pat) return NULL;
|
|
|
|
r = pFcPatternAddString(pat, FC_FAMILY, (const FcChar8 *)name);
|
|
|
|
if (!r) goto end;
|
|
|
|
r = pFcPatternAddString(pat, FC_NAMELANG, (const FcChar8 *)"en-us");
|
|
|
|
if (!r) goto end;
|
|
|
|
r = pFcPatternAddString(pat, FC_PRGNAME, (const FcChar8 *)"wine");
|
|
|
|
if (!r) goto end;
|
|
|
|
r = pFcConfigSubstitute(NULL, pat, FcMatchPattern);
|
|
|
|
if (!r) goto end;
|
|
|
|
pFcDefaultSubstitute(pat);
|
|
|
|
|
|
|
|
best = pFcFontMatch(NULL, pat, &result);
|
|
|
|
if (!best || result != FcResultMatch) goto end;
|
|
|
|
|
|
|
|
for (i = 0;
|
|
|
|
!family && pFcPatternGetString(best, FC_FAMILY, i, &str) == FcResultMatch;
|
|
|
|
i++)
|
|
|
|
{
|
|
|
|
Face *face;
|
|
|
|
const SYSTEM_LINKS *font_link;
|
|
|
|
const struct list *face_list;
|
|
|
|
|
2020-06-04 11:43:14 +02:00
|
|
|
if (!want_vertical)
|
|
|
|
{
|
|
|
|
ret = MultiByteToWideChar(CP_UTF8, 0, (const char*)str, -1,
|
|
|
|
nameW, ARRAY_SIZE(nameW));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
nameW[0] = '@';
|
|
|
|
ret = MultiByteToWideChar(CP_UTF8, 0, (const char*)str, -1,
|
|
|
|
nameW + 1, ARRAY_SIZE(nameW) - 1);
|
|
|
|
}
|
2018-11-27 16:50:20 +01:00
|
|
|
if (!ret) continue;
|
|
|
|
family = find_family_from_any_name(nameW);
|
|
|
|
if (!family) continue;
|
|
|
|
|
2020-09-07 15:18:58 +02:00
|
|
|
font_link = find_font_link( family->family_name );
|
2018-11-27 16:50:20 +01:00
|
|
|
face_list = get_face_list_from_family(family);
|
|
|
|
LIST_FOR_EACH_ENTRY( face, face_list, Face, entry ) {
|
|
|
|
if (!face->scalable)
|
|
|
|
continue;
|
|
|
|
if (csi->fs.fsCsb[0] & face->fs.fsCsb[0])
|
|
|
|
goto found;
|
|
|
|
if (font_link != NULL &&
|
|
|
|
csi->fs.fsCsb[0] & font_link->fs.fsCsb[0])
|
|
|
|
goto found;
|
|
|
|
}
|
|
|
|
family = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
found:
|
|
|
|
if (family)
|
|
|
|
TRACE("got %s\n", wine_dbgstr_w(nameW));
|
|
|
|
|
|
|
|
end:
|
2018-12-22 09:35:33 +01:00
|
|
|
pFcPatternDestroy(pat);
|
|
|
|
pFcPatternDestroy(best);
|
2018-11-27 16:50:20 +01:00
|
|
|
return family;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2013-05-20 18:24:21 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-10-21 11:04:35 +02:00
|
|
|
static const char* get_opentype_script(const struct gdi_font *font)
|
2013-05-20 18:24:21 +02:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* I am not sure if this is the correct way to generate our script tag
|
|
|
|
*/
|
|
|
|
|
|
|
|
switch (font->charset)
|
|
|
|
{
|
|
|
|
case ANSI_CHARSET: return "latn";
|
|
|
|
case BALTIC_CHARSET: return "latn"; /* ?? */
|
|
|
|
case CHINESEBIG5_CHARSET: return "hani";
|
|
|
|
case EASTEUROPE_CHARSET: return "latn"; /* ?? */
|
|
|
|
case GB2312_CHARSET: return "hani";
|
|
|
|
case GREEK_CHARSET: return "grek";
|
|
|
|
case HANGUL_CHARSET: return "hang";
|
|
|
|
case RUSSIAN_CHARSET: return "cyrl";
|
|
|
|
case SHIFTJIS_CHARSET: return "kana";
|
|
|
|
case TURKISH_CHARSET: return "latn"; /* ?? */
|
|
|
|
case VIETNAMESE_CHARSET: return "latn";
|
|
|
|
case JOHAB_CHARSET: return "latn"; /* ?? */
|
|
|
|
case ARABIC_CHARSET: return "arab";
|
|
|
|
case HEBREW_CHARSET: return "hebr";
|
|
|
|
case THAI_CHARSET: return "thai";
|
|
|
|
default: return "latn";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static const VOID * get_GSUB_vert_feature(const GdiFont *font)
|
|
|
|
{
|
|
|
|
const GSUB_Header *header;
|
|
|
|
const GSUB_Script *script;
|
|
|
|
const GSUB_LangSys *language;
|
|
|
|
const GSUB_Feature *feature;
|
|
|
|
|
|
|
|
if (!font->GSUB_Table)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
header = font->GSUB_Table;
|
|
|
|
|
2020-10-21 11:04:35 +02:00
|
|
|
script = GSUB_get_script_table(header, get_opentype_script(font->gdi_font));
|
2013-05-20 18:24:21 +02:00
|
|
|
if (!script)
|
|
|
|
{
|
|
|
|
TRACE("Script not found\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
language = GSUB_get_lang_table(script, "xxxx"); /* Need to get Lang tag */
|
|
|
|
if (!language)
|
|
|
|
{
|
|
|
|
TRACE("Language not found\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
feature = GSUB_get_feature(header, language, "vrt2");
|
|
|
|
if (!feature)
|
|
|
|
feature = GSUB_get_feature(header, language, "vert");
|
|
|
|
if (!feature)
|
|
|
|
{
|
|
|
|
TRACE("vrt2/vert feature not found\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return feature;
|
|
|
|
}
|
|
|
|
|
2001-09-12 22:21:06 +02:00
|
|
|
/*************************************************************
|
2011-10-18 13:10:58 +02:00
|
|
|
* freetype_SelectFont
|
2001-09-12 22:21:06 +02:00
|
|
|
*/
|
2020-10-21 11:01:50 +02:00
|
|
|
static struct gdi_font * CDECL freetype_SelectFont( DC *dc, HFONT hfont, UINT *aa_flags,
|
|
|
|
UINT default_aa_flags )
|
2001-09-12 22:21:06 +02:00
|
|
|
{
|
2020-10-21 11:01:50 +02:00
|
|
|
struct gdi_font *gdi_font;
|
2006-09-28 08:11:06 +02:00
|
|
|
GdiFont *ret;
|
2006-08-29 08:21:52 +02:00
|
|
|
Face *face, *best, *best_bitmap;
|
2005-10-31 22:03:28 +01:00
|
|
|
Family *family, *last_resort_family;
|
2012-12-07 14:29:28 +01:00
|
|
|
const struct list *face_list;
|
2004-06-16 22:06:26 +02:00
|
|
|
INT height, width = 0;
|
2006-08-29 08:21:52 +02:00
|
|
|
unsigned int score = 0, new_score;
|
2004-06-16 22:06:26 +02:00
|
|
|
signed int diff = 0, newdiff;
|
2012-02-08 17:33:00 +01:00
|
|
|
BOOL bd, it, can_use_bitmap, want_vertical;
|
2002-06-04 03:02:51 +02:00
|
|
|
LOGFONTW lf;
|
2002-06-22 03:19:29 +02:00
|
|
|
CHARSETINFO csi;
|
2008-07-07 17:18:10 +02:00
|
|
|
FMAT2 dcmat;
|
2008-06-22 15:41:47 +02:00
|
|
|
FontSubst *psub = NULL;
|
2012-03-08 14:33:02 +01:00
|
|
|
const SYSTEM_LINKS *font_link;
|
2011-10-18 13:10:58 +02:00
|
|
|
|
|
|
|
GetObjectW( hfont, sizeof(lf), &lf );
|
2008-04-07 14:23:55 +02:00
|
|
|
lf.lfWidth = abs(lf.lfWidth);
|
|
|
|
|
2020-10-21 11:05:28 +02:00
|
|
|
can_use_bitmap = !!(GetDeviceCaps(dc->hSelf, TEXTCAPS) & TC_RA_ABLE);
|
2001-09-12 22:21:06 +02:00
|
|
|
|
2006-10-12 22:56:56 +02:00
|
|
|
TRACE("%s, h=%d, it=%d, weight=%d, PandF=%02x, charset=%d orient %d escapement %d\n",
|
2002-06-04 03:02:51 +02:00
|
|
|
debugstr_w(lf.lfFaceName), lf.lfHeight, lf.lfItalic,
|
|
|
|
lf.lfWeight, lf.lfPitchAndFamily, lf.lfCharSet, lf.lfOrientation,
|
|
|
|
lf.lfEscapement);
|
2002-01-29 04:02:50 +01:00
|
|
|
|
2008-07-07 17:18:10 +02:00
|
|
|
if(dc->GraphicsMode == GM_ADVANCED)
|
2012-02-16 13:22:02 +01:00
|
|
|
{
|
2008-07-07 17:18:10 +02:00
|
|
|
memcpy(&dcmat, &dc->xformWorld2Vport, sizeof(FMAT2));
|
2012-02-16 13:22:02 +01:00
|
|
|
/* 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);
|
2012-12-10 14:33:53 +01:00
|
|
|
dcmat.eM11 = dcmat.eM22 = dcmat.eM11 < 0 ? -1 : 1;
|
2012-02-16 13:22:02 +01:00
|
|
|
}
|
|
|
|
}
|
2008-07-07 17:18:10 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Windows 3.1 compatibility mode GM_COMPATIBLE has only limited
|
|
|
|
font scaling abilities. */
|
2008-11-01 10:44:05 +01:00
|
|
|
dcmat.eM11 = dcmat.eM22 = 1.0;
|
2012-02-16 13:22:02 +01:00
|
|
|
dcmat.eM21 = dcmat.eM12 = 0;
|
2012-12-10 14:33:53 +01:00
|
|
|
lf.lfOrientation = lf.lfEscapement;
|
2012-02-16 13:22:02 +01:00
|
|
|
if (dc->vport2WorldValid)
|
|
|
|
{
|
|
|
|
if (dc->xformWorld2Vport.eM11 * dc->xformWorld2Vport.eM22 < 0)
|
|
|
|
lf.lfOrientation = -lf.lfOrientation;
|
|
|
|
lf.lfHeight *= fabs(dc->xformWorld2Vport.eM22);
|
|
|
|
lf.lfWidth *= fabs(dc->xformWorld2Vport.eM22);
|
|
|
|
}
|
2008-11-01 10:44:05 +01:00
|
|
|
}
|
|
|
|
|
2008-07-07 17:18:10 +02:00
|
|
|
TRACE("DC transform %f %f %f %f\n", dcmat.eM11, dcmat.eM12,
|
|
|
|
dcmat.eM21, dcmat.eM22);
|
2008-06-24 09:10:27 +02:00
|
|
|
|
2002-01-29 04:02:50 +01:00
|
|
|
/* check the cache first */
|
2020-10-21 11:01:50 +02:00
|
|
|
if ((gdi_font = find_cached_gdi_font( &lf, &dcmat, can_use_bitmap ))) {
|
|
|
|
ret = get_font_ptr( gdi_font );
|
2004-08-06 19:31:39 +02:00
|
|
|
TRACE("returning cached gdiFont(%p) for hFont %p\n", ret, hfont);
|
2011-10-18 13:10:58 +02:00
|
|
|
goto done;
|
2002-01-29 04:02:50 +01:00
|
|
|
}
|
|
|
|
|
2002-06-22 03:19:29 +02:00
|
|
|
/* 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;
|
|
|
|
|
2020-10-26 11:37:03 +01:00
|
|
|
it = !!lf.lfItalic;
|
|
|
|
bd = lf.lfWeight > 550;
|
|
|
|
|
2006-07-19 09:17:49 +02:00
|
|
|
if(!TranslateCharsetInfo((DWORD*)(INT_PTR)lf.lfCharSet, &csi, TCI_SRCCHARSET)) {
|
2002-06-22 03:19:29 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-08-18 00:08:31 +02:00
|
|
|
family = NULL;
|
2002-06-04 03:02:51 +02:00
|
|
|
if(lf.lfFaceName[0] != '\0') {
|
2007-03-07 06:45:11 +01:00
|
|
|
CHILD_FONT *font_link_entry;
|
2008-03-11 11:34:48 +01:00
|
|
|
LPWSTR FaceName = lf.lfFaceName;
|
2007-03-07 06:45:11 +01:00
|
|
|
|
2008-03-11 11:34:48 +01:00
|
|
|
psub = get_font_subst(&font_subst_list, FaceName, lf.lfCharSet);
|
2006-03-30 15:38:07 +02:00
|
|
|
|
2002-04-03 22:51:20 +02:00
|
|
|
if(psub) {
|
2008-06-22 15:41:47 +02:00
|
|
|
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;
|
2002-04-03 22:51:20 +02:00
|
|
|
}
|
|
|
|
|
2002-06-22 03:19:29 +02:00
|
|
|
/* 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.
|
|
|
|
*/
|
2012-12-07 14:29:28 +01:00
|
|
|
LIST_FOR_EACH_ENTRY( family, &font_list, Family, entry ) {
|
2020-09-07 15:18:58 +02:00
|
|
|
if (!strncmpiW( family->family_name, FaceName, LF_FACESIZE - 1 ) ||
|
|
|
|
(psub && !strncmpiW( family->family_name, psub->to.name, LF_FACESIZE - 1 )))
|
2008-06-22 15:41:47 +02:00
|
|
|
{
|
2020-09-07 15:18:58 +02:00
|
|
|
font_link = find_font_link( family->family_name );
|
2012-03-08 14:33:15 +01:00
|
|
|
face_list = get_face_list_from_family(family);
|
2012-12-07 14:29:28 +01:00
|
|
|
LIST_FOR_EACH_ENTRY( face, face_list, Face, entry ) {
|
2012-03-08 14:33:02 +01:00
|
|
|
if (!(face->scalable || can_use_bitmap))
|
|
|
|
continue;
|
|
|
|
if (csi.fs.fsCsb[0] & face->fs.fsCsb[0])
|
|
|
|
goto found;
|
|
|
|
if (font_link != NULL &&
|
|
|
|
csi.fs.fsCsb[0] & font_link->fs.fsCsb[0])
|
|
|
|
goto found;
|
|
|
|
if (!csi.fs.fsCsb[0])
|
|
|
|
goto found;
|
2005-08-19 11:58:32 +02:00
|
|
|
}
|
2004-08-18 00:08:31 +02:00
|
|
|
}
|
2002-01-29 04:02:50 +01:00
|
|
|
}
|
2007-03-07 06:45:11 +01:00
|
|
|
|
2011-05-10 22:38:07 +02:00
|
|
|
/* Search by full face name. */
|
2012-12-07 14:29:28 +01:00
|
|
|
LIST_FOR_EACH_ENTRY( family, &font_list, Family, entry ) {
|
2012-03-08 14:33:15 +01:00
|
|
|
face_list = get_face_list_from_family(family);
|
2012-12-07 14:29:28 +01:00
|
|
|
LIST_FOR_EACH_ENTRY( face, face_list, Face, entry ) {
|
2020-09-07 15:18:56 +02:00
|
|
|
if (!strncmpiW( face->full_name, FaceName, LF_FACESIZE - 1 ) && (face->scalable || can_use_bitmap))
|
2011-05-10 22:38:07 +02:00
|
|
|
{
|
2012-03-08 14:33:02 +01:00
|
|
|
if (csi.fs.fsCsb[0] & face->fs.fsCsb[0] || !csi.fs.fsCsb[0])
|
|
|
|
goto found_face;
|
2020-09-07 15:18:58 +02:00
|
|
|
font_link = find_font_link( family->family_name );
|
2012-03-08 14:33:02 +01:00
|
|
|
if (font_link != NULL &&
|
|
|
|
csi.fs.fsCsb[0] & font_link->fs.fsCsb[0])
|
2011-05-10 22:38:07 +02:00
|
|
|
goto found_face;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-03-07 06:45:11 +01:00
|
|
|
/*
|
|
|
|
* 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)
|
|
|
|
{
|
2017-11-26 14:33:17 +01:00
|
|
|
if(!strncmpiW(font_link->font_name, FaceName, LF_FACESIZE - 1) ||
|
|
|
|
(psub && !strncmpiW(font_link->font_name,psub->to.name, LF_FACESIZE - 1)))
|
2007-03-07 06:45:11 +01:00
|
|
|
{
|
|
|
|
TRACE("found entry in system list\n");
|
|
|
|
LIST_FOR_EACH_ENTRY(font_link_entry, &font_link->links, CHILD_FONT, entry)
|
|
|
|
{
|
2012-03-08 14:33:02 +01:00
|
|
|
const SYSTEM_LINKS *links;
|
|
|
|
|
2007-09-06 20:40:00 +02:00
|
|
|
face = font_link_entry->face;
|
2012-03-08 14:33:02 +01:00
|
|
|
if (!(face->scalable || can_use_bitmap))
|
|
|
|
continue;
|
2007-09-06 20:40:00 +02:00
|
|
|
family = face->family;
|
2012-03-08 14:33:02 +01:00
|
|
|
if (csi.fs.fsCsb[0] & face->fs.fsCsb[0] || !csi.fs.fsCsb[0])
|
|
|
|
goto found;
|
2020-09-07 15:18:58 +02:00
|
|
|
links = find_font_link( family->family_name );
|
2012-03-08 14:33:02 +01:00
|
|
|
if (links != NULL && csi.fs.fsCsb[0] & links->fs.fsCsb[0])
|
|
|
|
goto found;
|
2007-03-07 06:45:11 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2002-01-29 04:02:50 +01:00
|
|
|
}
|
|
|
|
|
2008-06-22 15:41:47 +02:00
|
|
|
psub = NULL; /* substitution is no more relevant */
|
|
|
|
|
2005-08-19 11:58:32 +02:00
|
|
|
/* If requested charset was DEFAULT_CHARSET then try using charset
|
|
|
|
corresponding to the current ansi codepage */
|
2008-10-09 07:30:00 +02:00
|
|
|
if (!csi.fs.fsCsb[0])
|
2008-05-13 15:10:05 +02:00
|
|
|
{
|
2005-08-19 11:58:32 +02:00
|
|
|
INT acp = GetACP();
|
2006-07-19 09:17:49 +02:00
|
|
|
if(!TranslateCharsetInfo((DWORD*)(INT_PTR)acp, &csi, TCI_SRCCODEPAGE)) {
|
2005-08-19 11:58:32 +02:00
|
|
|
FIXME("TCI failed on codepage %d\n", acp);
|
|
|
|
csi.fs.fsCsb[0] = 0;
|
|
|
|
} else
|
|
|
|
lf.lfCharSet = csi.ciCharset;
|
|
|
|
}
|
2003-10-04 06:00:08 +02:00
|
|
|
|
2012-02-08 17:33:00 +01:00
|
|
|
want_vertical = (lf.lfFaceName[0] == '@');
|
|
|
|
|
2005-08-19 11:58:32 +02:00
|
|
|
/* Face families are in the top 4 bits of lfPitchAndFamily,
|
|
|
|
so mask with 0xF0 before testing */
|
2003-10-04 06:00:08 +02:00
|
|
|
|
2005-08-19 11:58:32 +02:00
|
|
|
if((lf.lfPitchAndFamily & FIXED_PITCH) ||
|
|
|
|
(lf.lfPitchAndFamily & 0xF0) == FF_MODERN)
|
2018-12-20 11:32:14 +01:00
|
|
|
strcpyW(lf.lfFaceName, default_fixed);
|
2005-08-19 11:58:32 +02:00
|
|
|
else if((lf.lfPitchAndFamily & 0xF0) == FF_ROMAN)
|
2018-12-20 11:32:14 +01:00
|
|
|
strcpyW(lf.lfFaceName, default_serif);
|
2005-08-19 11:58:32 +02:00
|
|
|
else if((lf.lfPitchAndFamily & 0xF0) == FF_SWISS)
|
2018-12-20 11:32:14 +01:00
|
|
|
strcpyW(lf.lfFaceName, default_sans);
|
2005-08-19 11:58:32 +02:00
|
|
|
else
|
2018-12-20 11:32:14 +01:00
|
|
|
strcpyW(lf.lfFaceName, default_sans);
|
2012-12-07 14:29:28 +01:00
|
|
|
LIST_FOR_EACH_ENTRY( family, &font_list, Family, entry ) {
|
2020-09-07 15:18:58 +02:00
|
|
|
if (!strncmpiW( family->family_name, lf.lfFaceName, LF_FACESIZE - 1 ))
|
|
|
|
{
|
|
|
|
font_link = find_font_link( family->family_name );
|
2012-03-08 14:33:15 +01:00
|
|
|
face_list = get_face_list_from_family(family);
|
2012-12-07 14:29:28 +01:00
|
|
|
LIST_FOR_EACH_ENTRY( face, face_list, Face, entry ) {
|
2012-03-08 14:33:02 +01:00
|
|
|
if (!(face->scalable || can_use_bitmap))
|
|
|
|
continue;
|
|
|
|
if (csi.fs.fsCsb[0] & face->fs.fsCsb[0])
|
|
|
|
goto found;
|
|
|
|
if (font_link != NULL && csi.fs.fsCsb[0] & font_link->fs.fsCsb[0])
|
|
|
|
goto found;
|
2004-08-18 00:08:31 +02:00
|
|
|
}
|
2005-08-19 11:58:32 +02:00
|
|
|
}
|
2002-06-22 03:19:29 +02:00
|
|
|
}
|
|
|
|
|
2018-11-27 16:50:20 +01:00
|
|
|
#ifdef SONAME_LIBFONTCONFIG
|
|
|
|
/* Try FontConfig substitutions if the face isn't found */
|
2020-06-04 11:43:14 +02:00
|
|
|
family = get_fontconfig_family(lf.lfPitchAndFamily, &csi, want_vertical);
|
2018-11-27 16:50:20 +01:00
|
|
|
if (family) goto found;
|
|
|
|
#endif
|
|
|
|
|
2005-10-31 22:03:28 +01:00
|
|
|
last_resort_family = NULL;
|
2012-12-07 14:29:28 +01:00
|
|
|
LIST_FOR_EACH_ENTRY( family, &font_list, Family, entry ) {
|
2020-09-07 15:18:58 +02:00
|
|
|
font_link = find_font_link( family->family_name );
|
2012-03-08 14:33:15 +01:00
|
|
|
face_list = get_face_list_from_family(family);
|
2012-12-07 14:29:28 +01:00
|
|
|
LIST_FOR_EACH_ENTRY( face, face_list, Face, entry ) {
|
2013-01-15 14:13:09 +01:00
|
|
|
if(!(face->flags & ADDFONT_VERTICAL_FONT) == !want_vertical &&
|
2012-03-08 14:33:02 +01:00
|
|
|
(csi.fs.fsCsb[0] & face->fs.fsCsb[0] ||
|
|
|
|
(font_link != NULL && csi.fs.fsCsb[0] & font_link->fs.fsCsb[0]))) {
|
2005-10-31 22:03:28 +01:00
|
|
|
if(face->scalable)
|
2005-08-19 11:58:32 +02:00
|
|
|
goto found;
|
2005-10-31 22:03:28 +01:00
|
|
|
if(can_use_bitmap && !last_resort_family)
|
|
|
|
last_resort_family = family;
|
|
|
|
}
|
2005-08-19 11:58:32 +02:00
|
|
|
}
|
2001-09-12 22:21:06 +02:00
|
|
|
}
|
|
|
|
|
2005-10-31 22:03:28 +01:00
|
|
|
if(last_resort_family) {
|
|
|
|
family = last_resort_family;
|
|
|
|
csi.fs.fsCsb[0] = 0;
|
|
|
|
goto found;
|
|
|
|
}
|
|
|
|
|
2012-12-07 14:29:28 +01:00
|
|
|
LIST_FOR_EACH_ENTRY( family, &font_list, Family, entry ) {
|
2012-03-08 14:33:15 +01:00
|
|
|
face_list = get_face_list_from_family(family);
|
2012-12-07 14:29:28 +01:00
|
|
|
LIST_FOR_EACH_ENTRY( face, face_list, Face, entry ) {
|
2013-01-15 14:13:09 +01:00
|
|
|
if(face->scalable && !(face->flags & ADDFONT_VERTICAL_FONT) == !want_vertical) {
|
2004-08-11 20:49:34 +02:00
|
|
|
csi.fs.fsCsb[0] = 0;
|
2006-11-09 05:15:16 +01:00
|
|
|
WARN("just using first face for now\n");
|
2005-08-19 11:58:32 +02:00
|
|
|
goto found;
|
2004-08-11 20:49:34 +02:00
|
|
|
}
|
2005-10-31 22:03:28 +01:00
|
|
|
if(can_use_bitmap && !last_resort_family)
|
|
|
|
last_resort_family = family;
|
2004-08-11 20:49:34 +02:00
|
|
|
}
|
2001-09-12 22:21:06 +02:00
|
|
|
}
|
2005-10-31 22:03:28 +01:00
|
|
|
if(!last_resort_family) {
|
|
|
|
FIXME("can't find a single appropriate font - bailing\n");
|
2020-10-22 11:54:35 +02:00
|
|
|
return NULL;
|
2005-10-31 22:03:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
WARN("could only find a bitmap font - this will probably look awful!\n");
|
|
|
|
family = last_resort_family;
|
|
|
|
csi.fs.fsCsb[0] = 0;
|
2001-09-12 22:21:06 +02:00
|
|
|
|
2005-08-19 11:58:32 +02:00
|
|
|
found:
|
2001-09-12 22:21:06 +02:00
|
|
|
|
2008-06-24 09:11:40 +02:00
|
|
|
height = lf.lfHeight;
|
2004-06-16 22:06:26 +02:00
|
|
|
|
2006-08-29 08:21:52 +02:00
|
|
|
face = best = best_bitmap = NULL;
|
2020-09-07 15:18:58 +02:00
|
|
|
font_link = find_font_link( family->family_name );
|
2012-03-08 14:33:15 +01:00
|
|
|
face_list = get_face_list_from_family(family);
|
|
|
|
LIST_FOR_EACH_ENTRY(face, face_list, Face, entry)
|
2006-08-29 08:21:52 +02:00
|
|
|
{
|
2012-03-08 14:33:02 +01:00
|
|
|
if (csi.fs.fsCsb[0] & face->fs.fsCsb[0] ||
|
|
|
|
(font_link != NULL && csi.fs.fsCsb[0] & font_link->fs.fsCsb[0]) ||
|
|
|
|
!csi.fs.fsCsb[0])
|
2006-08-29 08:21:52 +02:00
|
|
|
{
|
2008-04-07 09:11:42 +02:00
|
|
|
BOOL italic, bold;
|
|
|
|
|
|
|
|
italic = (face->ntmFlags & NTM_ITALIC) ? 1 : 0;
|
|
|
|
bold = (face->ntmFlags & NTM_BOLD) ? 1 : 0;
|
|
|
|
new_score = (italic ^ it) + (bold ^ bd);
|
2006-08-29 08:21:52 +02:00
|
|
|
if(!best || new_score <= score)
|
|
|
|
{
|
|
|
|
TRACE("(it=%d, bd=%d) is selected for (it=%d, bd=%d)\n",
|
2008-04-07 09:11:42 +02:00
|
|
|
italic, bold, it, bd);
|
2006-08-29 08:21:52 +02:00
|
|
|
score = new_score;
|
2004-06-16 22:06:26 +02:00
|
|
|
best = face;
|
2006-08-29 08:21:52 +02:00
|
|
|
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;
|
|
|
|
}
|
2005-08-19 11:58:32 +02:00
|
|
|
}
|
2004-08-10 00:54:33 +02:00
|
|
|
}
|
|
|
|
}
|
2002-01-29 04:02:50 +01:00
|
|
|
}
|
2006-08-29 08:21:52 +02:00
|
|
|
if(best)
|
|
|
|
face = best->scalable ? best : best_bitmap;
|
2002-06-22 03:19:29 +02:00
|
|
|
|
2011-05-10 22:38:07 +02:00
|
|
|
found_face:
|
|
|
|
height = lf.lfHeight;
|
|
|
|
|
2020-10-26 11:37:03 +01:00
|
|
|
TRACE("not in cache\n");
|
|
|
|
gdi_font = alloc_gdi_font( face->file, face->font_data_ptr, face->font_data_size );
|
|
|
|
ret = get_font_ptr( gdi_font );
|
|
|
|
|
|
|
|
gdi_font->matrix = dcmat;
|
|
|
|
gdi_font->lf = lf;
|
|
|
|
gdi_font->can_use_bitmap = can_use_bitmap;
|
|
|
|
gdi_font->fake_italic = (it && !(face->ntmFlags & NTM_ITALIC));
|
|
|
|
gdi_font->fake_bold = (bd && !(face->ntmFlags & NTM_BOLD));
|
2020-10-21 11:04:35 +02:00
|
|
|
gdi_font->fs = face->fs;
|
2003-06-23 22:51:06 +02:00
|
|
|
|
2004-08-26 20:24:03 +02:00
|
|
|
if(csi.fs.fsCsb[0]) {
|
2020-10-21 11:04:35 +02:00
|
|
|
gdi_font->charset = lf.lfCharSet;
|
|
|
|
gdi_font->codepage = csi.ciACP;
|
2004-08-26 20:24:03 +02:00
|
|
|
}
|
2002-06-22 03:19:29 +02:00
|
|
|
else
|
2020-10-21 11:04:35 +02:00
|
|
|
gdi_font->charset = get_nearest_charset( family->family_name, face, &gdi_font->codepage );
|
2001-09-12 22:21:06 +02:00
|
|
|
|
2020-09-07 15:18:56 +02:00
|
|
|
TRACE( "Chosen: %s (%s/%p:%ld)\n", debugstr_w(face->full_name), debugstr_w(face->file),
|
|
|
|
face->font_data_ptr, face->face_index );
|
2001-09-12 22:21:06 +02:00
|
|
|
|
2020-10-22 11:49:33 +02:00
|
|
|
gdi_font->aveWidth = height ? lf.lfWidth : 0;
|
2008-01-09 14:58:00 +01:00
|
|
|
|
2004-06-16 22:06:26 +02:00
|
|
|
if(!face->scalable) {
|
2008-01-20 14:21:45 +01:00
|
|
|
/* Windows uses integer scaling factors for bitmap fonts */
|
|
|
|
INT scale, scaled_height;
|
2020-10-21 11:01:50 +02:00
|
|
|
struct gdi_font *cachedfont;
|
2008-01-20 14:21:45 +01:00
|
|
|
|
2008-08-21 08:02:15 +02:00
|
|
|
/* FIXME: rotation of bitmap fonts is ignored */
|
2020-10-21 11:01:50 +02:00
|
|
|
height = abs(GDI_ROUND( (double)height * gdi_font->matrix.eM22 ));
|
2020-10-22 11:49:33 +02:00
|
|
|
if (gdi_font->aveWidth)
|
|
|
|
gdi_font->aveWidth = (double)gdi_font->aveWidth * gdi_font->matrix.eM11;
|
2020-10-21 11:01:50 +02:00
|
|
|
gdi_font->matrix.eM11 = gdi_font->matrix.eM22 = 1.0;
|
2010-07-20 00:17:10 +02:00
|
|
|
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. */
|
2020-10-21 11:01:50 +02:00
|
|
|
if((cachedfont = find_cached_gdi_font( &lf, &dcmat, can_use_bitmap ))) {
|
2010-07-20 00:17:10 +02:00
|
|
|
TRACE("Found cached font after non-scalable matrix rescale!\n");
|
2020-10-21 11:01:50 +02:00
|
|
|
free_gdi_font( gdi_font );
|
|
|
|
gdi_font = cachedfont;
|
|
|
|
ret = get_font_ptr( gdi_font );
|
2011-10-18 13:10:58 +02:00
|
|
|
goto done;
|
2010-07-20 00:17:10 +02:00
|
|
|
}
|
2008-08-21 08:02:15 +02:00
|
|
|
|
2008-01-20 14:21:45 +01:00
|
|
|
if (height != 0) height = diff;
|
|
|
|
height += face->size.height;
|
|
|
|
|
|
|
|
scale = (height + face->size.height - 1) / face->size.height;
|
|
|
|
scaled_height = scale * face->size.height;
|
2009-02-20 15:40:44 +01:00
|
|
|
/* 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--;
|
2020-10-22 11:49:33 +02:00
|
|
|
gdi_font->scale_y = scale;
|
2008-01-09 14:58:00 +01:00
|
|
|
|
2004-06-16 22:06:26 +02:00
|
|
|
width = face->size.x_ppem >> 6;
|
|
|
|
height = face->size.y_ppem >> 6;
|
|
|
|
}
|
2020-10-22 11:49:33 +02:00
|
|
|
TRACE("font scale y: %f\n", gdi_font->scale_y);
|
2008-01-20 14:21:45 +01:00
|
|
|
|
2007-09-13 19:42:55 +02:00
|
|
|
ret->ft_face = OpenFontFace(ret, face, width, height);
|
2004-06-16 22:06:26 +02:00
|
|
|
|
2002-04-03 23:06:09 +02:00
|
|
|
if (!ret->ft_face)
|
|
|
|
{
|
2020-10-21 11:01:50 +02:00
|
|
|
free_gdi_font( gdi_font );
|
2020-10-22 11:54:35 +02:00
|
|
|
return NULL;
|
2002-04-03 23:06:09 +02:00
|
|
|
}
|
2003-10-28 01:08:28 +01:00
|
|
|
|
2020-10-22 11:49:33 +02:00
|
|
|
gdi_font->ntmFlags = face->ntmFlags;
|
|
|
|
gdi_font->aa_flags = HIWORD( face->flags );
|
2007-09-07 23:30:40 +02:00
|
|
|
|
2020-10-21 11:04:35 +02:00
|
|
|
pick_charmap( ret->ft_face, gdi_font->charset );
|
2001-09-12 22:21:06 +02:00
|
|
|
|
2020-10-26 11:43:24 +01:00
|
|
|
set_gdi_font_names( gdi_font, psub ? psub->from.name : family->family_name,
|
|
|
|
face->style_name, face->full_name );
|
2005-09-07 11:21:50 +02:00
|
|
|
create_child_font_list(ret);
|
2002-01-29 04:02:50 +01:00
|
|
|
|
2013-01-15 14:13:09 +01:00
|
|
|
if (face->flags & ADDFONT_VERTICAL_FONT) /* We need to try to load the GSUB table */
|
2008-03-13 10:40:58 +01:00
|
|
|
{
|
2020-10-22 11:57:48 +02:00
|
|
|
int length = freetype_get_font_data(gdi_font, MS_GSUB_TAG , 0, NULL, 0);
|
2008-03-13 10:40:58 +01:00
|
|
|
if (length != GDI_ERROR)
|
|
|
|
{
|
|
|
|
ret->GSUB_Table = HeapAlloc(GetProcessHeap(),0,length);
|
2020-10-22 11:57:48 +02:00
|
|
|
freetype_get_font_data(gdi_font, MS_GSUB_TAG , 0, ret->GSUB_Table, length);
|
2008-03-13 10:40:58 +01:00
|
|
|
TRACE("Loaded GSUB table of %i bytes\n",length);
|
2013-05-20 18:24:21 +02:00
|
|
|
ret->vert_feature = get_GSUB_vert_feature(ret);
|
|
|
|
if (!ret->vert_feature)
|
|
|
|
{
|
|
|
|
TRACE("Vertical feature not found\n");
|
|
|
|
HeapFree(GetProcessHeap(), 0, ret->GSUB_Table);
|
|
|
|
ret->GSUB_Table = NULL;
|
|
|
|
}
|
2008-03-13 10:40:58 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-11-22 23:16:53 +01:00
|
|
|
TRACE("caching: gdiFont=%p hfont=%p\n", ret, hfont);
|
2004-08-11 01:42:18 +02:00
|
|
|
|
2020-10-21 11:01:50 +02:00
|
|
|
cache_gdi_font( gdi_font );
|
2011-10-18 13:10:58 +02:00
|
|
|
done:
|
|
|
|
if (ret)
|
|
|
|
{
|
2012-11-05 12:32:15 +01:00
|
|
|
switch (lf.lfQuality)
|
2012-11-02 17:21:28 +01:00
|
|
|
{
|
2012-11-05 12:32:15 +01:00
|
|
|
case NONANTIALIASED_QUALITY:
|
|
|
|
case ANTIALIASED_QUALITY:
|
2020-10-20 22:05:37 +02:00
|
|
|
if (!*aa_flags) *aa_flags = default_aa_flags;
|
2012-11-05 12:32:15 +01:00
|
|
|
break;
|
|
|
|
case CLEARTYPE_QUALITY:
|
|
|
|
case CLEARTYPE_NATURAL_QUALITY:
|
|
|
|
default:
|
2020-10-22 11:49:33 +02:00
|
|
|
if (!*aa_flags) *aa_flags = gdi_font->aa_flags;
|
2020-10-20 22:05:37 +02:00
|
|
|
if (!*aa_flags) *aa_flags = default_aa_flags;
|
2012-11-05 12:32:15 +01:00
|
|
|
|
|
|
|
/* fixup the antialiasing flags for that font */
|
|
|
|
switch (*aa_flags)
|
2012-11-02 17:21:28 +01:00
|
|
|
{
|
2012-11-05 12:32:15 +01:00
|
|
|
case WINE_GGO_HRGB_BITMAP:
|
|
|
|
case WINE_GGO_HBGR_BITMAP:
|
|
|
|
case WINE_GGO_VRGB_BITMAP:
|
|
|
|
case WINE_GGO_VBGR_BITMAP:
|
|
|
|
if (is_subpixel_rendering_enabled()) break;
|
|
|
|
*aa_flags = GGO_GRAY4_BITMAP;
|
|
|
|
/* fall through */
|
|
|
|
case GGO_GRAY2_BITMAP:
|
|
|
|
case GGO_GRAY4_BITMAP:
|
|
|
|
case GGO_GRAY8_BITMAP:
|
|
|
|
case WINE_GGO_GRAY16_BITMAP:
|
2020-10-21 11:05:28 +02:00
|
|
|
if ((!antialias_fakes || (!gdi_font->fake_bold && !gdi_font->fake_italic)) && is_hinting_enabled())
|
2012-11-05 12:32:15 +01:00
|
|
|
{
|
|
|
|
WORD gasp_flags;
|
2020-10-22 11:57:48 +02:00
|
|
|
if (get_gasp_flags( gdi_font, &gasp_flags ) && !(gasp_flags & GASP_DOGRAY))
|
2012-11-05 22:32:51 +01:00
|
|
|
{
|
|
|
|
TRACE( "font %s %d aa disabled by GASP\n",
|
|
|
|
debugstr_w(lf.lfFaceName), lf.lfHeight );
|
2012-11-05 12:32:15 +01:00
|
|
|
*aa_flags = GGO_BITMAP;
|
2012-11-05 22:32:51 +01:00
|
|
|
}
|
2012-11-05 12:32:15 +01:00
|
|
|
}
|
2012-11-02 17:21:28 +01:00
|
|
|
}
|
|
|
|
}
|
2012-11-05 22:32:51 +01:00
|
|
|
TRACE( "%p %s %d aa %x\n", hfont, debugstr_w(lf.lfFaceName), lf.lfHeight, *aa_flags );
|
2011-10-18 13:10:58 +02:00
|
|
|
}
|
2020-10-21 11:01:50 +02:00
|
|
|
return gdi_font;
|
2001-09-12 22:21:06 +02:00
|
|
|
}
|
|
|
|
|
2011-11-03 14:11:35 +01:00
|
|
|
static INT load_script_name( UINT id, WCHAR buffer[LF_FACESIZE] )
|
|
|
|
{
|
|
|
|
HRSRC rsrc;
|
|
|
|
HGLOBAL hMem;
|
|
|
|
WCHAR *p;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
id += IDS_FIRST_SCRIPT;
|
|
|
|
rsrc = FindResourceW( gdi32_module, (LPCWSTR)(ULONG_PTR)((id >> 4) + 1), (LPCWSTR)6 /*RT_STRING*/ );
|
|
|
|
if (!rsrc) return 0;
|
|
|
|
hMem = LoadResource( gdi32_module, rsrc );
|
|
|
|
if (!hMem) return 0;
|
|
|
|
|
|
|
|
p = LockResource( hMem );
|
|
|
|
id &= 0x000f;
|
|
|
|
while (id--) p += *p + 1;
|
|
|
|
|
|
|
|
i = min(LF_FACESIZE - 1, *p);
|
|
|
|
memcpy(buffer, p + 1, i * sizeof(WCHAR));
|
|
|
|
buffer[i] = 0;
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
2014-03-06 17:22:19 +01:00
|
|
|
static inline BOOL is_complex_script_ansi_cp(UINT ansi_cp)
|
|
|
|
{
|
|
|
|
return (ansi_cp == 874 /* Thai */
|
|
|
|
|| ansi_cp == 1255 /* Hebrew */
|
|
|
|
|| ansi_cp == 1256 /* Arabic */
|
|
|
|
);
|
|
|
|
}
|
2011-11-03 14:11:35 +01:00
|
|
|
|
2011-04-01 14:20:00 +02:00
|
|
|
/***************************************************
|
|
|
|
* create_enum_charset_list
|
|
|
|
*
|
|
|
|
* This function creates charset enumeration list because in DEFAULT_CHARSET
|
|
|
|
* case, the ANSI codepage's charset takes precedence over other charsets.
|
2014-03-06 17:22:19 +01:00
|
|
|
* Above rule doesn't apply if the ANSI codepage uses complex script (e.g. Thai).
|
2011-04-01 14:20:00 +02:00
|
|
|
* This function works as a filter other than DEFAULT_CHARSET case.
|
|
|
|
*/
|
|
|
|
static DWORD create_enum_charset_list(DWORD charset, struct enum_charset_list *list)
|
|
|
|
{
|
|
|
|
CHARSETINFO csi;
|
|
|
|
DWORD n = 0;
|
|
|
|
|
2011-04-11 13:45:23 +02:00
|
|
|
if (TranslateCharsetInfo(ULongToPtr(charset), &csi, TCI_SRCCHARSET) &&
|
2011-04-01 14:20:00 +02:00
|
|
|
csi.fs.fsCsb[0] != 0) {
|
|
|
|
list->element[n].mask = csi.fs.fsCsb[0];
|
|
|
|
list->element[n].charset = csi.ciCharset;
|
2011-11-03 14:11:35 +01:00
|
|
|
load_script_name( ffs(csi.fs.fsCsb[0]) - 1, list->element[n].name );
|
2011-04-01 14:20:00 +02:00
|
|
|
n++;
|
|
|
|
}
|
|
|
|
else { /* charset is DEFAULT_CHARSET or invalid. */
|
|
|
|
INT acp, i;
|
2012-09-27 13:07:01 +02:00
|
|
|
DWORD mask = 0;
|
2011-04-01 14:20:00 +02:00
|
|
|
|
|
|
|
/* Set the current codepage's charset as the first element. */
|
|
|
|
acp = GetACP();
|
2014-03-06 17:22:19 +01:00
|
|
|
if (!is_complex_script_ansi_cp(acp) &&
|
|
|
|
TranslateCharsetInfo((DWORD*)(INT_PTR)acp, &csi, TCI_SRCCODEPAGE) &&
|
2011-04-01 14:20:00 +02:00
|
|
|
csi.fs.fsCsb[0] != 0) {
|
|
|
|
list->element[n].mask = csi.fs.fsCsb[0];
|
|
|
|
list->element[n].charset = csi.ciCharset;
|
2011-11-03 14:11:35 +01:00
|
|
|
load_script_name( ffs(csi.fs.fsCsb[0]) - 1, list->element[n].name );
|
2012-09-27 13:07:01 +02:00
|
|
|
mask |= csi.fs.fsCsb[0];
|
2011-04-01 14:20:00 +02:00
|
|
|
n++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Fill out left elements. */
|
|
|
|
for (i = 0; i < 32; i++) {
|
|
|
|
FONTSIGNATURE fs;
|
|
|
|
fs.fsCsb[0] = 1L << i;
|
|
|
|
fs.fsCsb[1] = 0;
|
2012-09-27 13:07:01 +02:00
|
|
|
if (fs.fsCsb[0] & mask)
|
2011-04-01 14:20:00 +02:00
|
|
|
continue; /* skip, already added. */
|
|
|
|
if (!TranslateCharsetInfo(fs.fsCsb, &csi, TCI_SRCFONTSIG))
|
|
|
|
continue; /* skip, this is an invalid fsCsb bit. */
|
|
|
|
|
|
|
|
list->element[n].mask = fs.fsCsb[0];
|
|
|
|
list->element[n].charset = csi.ciCharset;
|
2011-11-03 14:11:35 +01:00
|
|
|
load_script_name( i, list->element[n].name );
|
2012-09-27 13:07:01 +02:00
|
|
|
mask |= fs.fsCsb[0];
|
|
|
|
n++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* add catch all mask for remaining bits */
|
|
|
|
if (~mask)
|
|
|
|
{
|
|
|
|
list->element[n].mask = ~mask;
|
|
|
|
list->element[n].charset = DEFAULT_CHARSET;
|
|
|
|
load_script_name( IDS_OTHER - IDS_FIRST_SCRIPT, list->element[n].name );
|
2011-04-01 14:20:00 +02:00
|
|
|
n++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
list->total = n;
|
|
|
|
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
2020-10-23 20:14:49 +02:00
|
|
|
static UINT get_font_type( const NEWTEXTMETRICEXW *ntm )
|
|
|
|
{
|
|
|
|
UINT ret = 0;
|
|
|
|
|
|
|
|
if (ntm->ntmTm.tmPitchAndFamily & TMPF_TRUETYPE) ret |= TRUETYPE_FONTTYPE;
|
|
|
|
if (ntm->ntmTm.tmPitchAndFamily & TMPF_DEVICE) ret |= DEVICE_FONTTYPE;
|
|
|
|
if (!(ntm->ntmTm.tmPitchAndFamily & TMPF_VECTOR)) ret |= RASTER_FONTTYPE;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-01-14 14:45:40 +01:00
|
|
|
static void GetEnumStructs(Face *face, const WCHAR *family_name, LPENUMLOGFONTEXW pelf,
|
2020-10-23 20:14:49 +02:00
|
|
|
NEWTEXTMETRICEXW *pntm)
|
2001-09-12 22:21:06 +02:00
|
|
|
{
|
2020-10-21 11:03:51 +02:00
|
|
|
struct gdi_font *gdi_font;
|
2008-01-10 14:18:36 +01:00
|
|
|
GdiFont *font;
|
2004-06-16 22:06:26 +02:00
|
|
|
LONG width, height;
|
2001-09-12 22:21:06 +02:00
|
|
|
|
2008-01-16 07:46:24 +01:00
|
|
|
if (face->cached_enum_data)
|
2007-12-11 14:09:21 +01:00
|
|
|
{
|
|
|
|
TRACE("Cached\n");
|
2008-02-27 22:21:10 +01:00
|
|
|
*pelf = face->cached_enum_data->elf;
|
|
|
|
*pntm = face->cached_enum_data->ntm;
|
2007-12-11 14:09:21 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-10-26 11:37:03 +01:00
|
|
|
gdi_font = alloc_gdi_font( face->file, face->font_data_ptr, face->font_data_size );
|
2020-10-21 11:03:51 +02:00
|
|
|
font = get_font_ptr( gdi_font );
|
2008-01-10 14:18:36 +01:00
|
|
|
|
2004-06-16 22:06:26 +02:00
|
|
|
if(face->scalable) {
|
2012-04-24 10:03:12 +02:00
|
|
|
height = 100;
|
2004-06-16 22:06:26 +02:00
|
|
|
width = 0;
|
|
|
|
} else {
|
|
|
|
height = face->size.y_ppem >> 6;
|
|
|
|
width = face->size.x_ppem >> 6;
|
|
|
|
}
|
2020-10-22 11:49:33 +02:00
|
|
|
|
2007-09-13 19:42:55 +02:00
|
|
|
if (!(font->ft_face = OpenFontFace(font, face, width, height)))
|
2002-04-03 23:06:09 +02:00
|
|
|
{
|
2020-10-21 11:03:51 +02:00
|
|
|
free_gdi_font(gdi_font);
|
2002-04-03 23:06:09 +02:00
|
|
|
return;
|
|
|
|
}
|
2001-09-12 22:21:06 +02:00
|
|
|
|
2020-10-26 11:43:24 +01:00
|
|
|
set_gdi_font_names( gdi_font, family_name, face->style_name, face->full_name );
|
2020-10-22 11:49:33 +02:00
|
|
|
gdi_font->ntmFlags = face->ntmFlags;
|
2003-06-23 22:51:06 +02:00
|
|
|
|
2020-10-26 11:43:24 +01:00
|
|
|
if (freetype_set_outline_text_metrics(gdi_font))
|
2008-01-11 10:04:26 +01:00
|
|
|
{
|
2020-10-26 11:43:24 +01:00
|
|
|
memcpy(&pntm->ntmTm, &gdi_font->otm.otmTextMetrics, sizeof(TEXTMETRICW));
|
2004-06-16 22:06:26 +02:00
|
|
|
|
2020-10-26 11:43:24 +01:00
|
|
|
pntm->ntmTm.ntmSizeEM = gdi_font->otm.otmEMSquare;
|
2020-10-22 11:49:33 +02:00
|
|
|
pntm->ntmTm.ntmCellHeight = gdi_font->ntmCellHeight;
|
|
|
|
pntm->ntmTm.ntmAvgWidth = gdi_font->ntmAvgWidth;
|
2004-06-16 22:06:26 +02:00
|
|
|
|
2020-10-26 11:43:24 +01:00
|
|
|
lstrcpynW(pelf->elfLogFont.lfFaceName, (WCHAR *)gdi_font->otm.otmpFamilyName, LF_FACESIZE);
|
|
|
|
lstrcpynW(pelf->elfFullName, (WCHAR *)gdi_font->otm.otmpFaceName, LF_FULLFACESIZE);
|
|
|
|
lstrcpynW(pelf->elfStyle, (WCHAR *)gdi_font->otm.otmpStyleName, LF_FACESIZE);
|
2008-01-11 10:04:26 +01:00
|
|
|
}
|
2020-10-26 11:43:24 +01:00
|
|
|
else if (freetype_set_bitmap_text_metrics( gdi_font ))
|
2008-01-11 10:04:26 +01:00
|
|
|
{
|
2020-10-26 11:43:24 +01:00
|
|
|
memcpy(&pntm->ntmTm, &gdi_font->otm.otmTextMetrics, sizeof(TEXTMETRICW));
|
2004-06-16 22:06:26 +02:00
|
|
|
|
2004-12-21 15:45:38 +01:00
|
|
|
pntm->ntmTm.ntmSizeEM = pntm->ntmTm.tmHeight - pntm->ntmTm.tmInternalLeading;
|
2012-04-24 10:03:35 +02:00
|
|
|
pntm->ntmTm.ntmCellHeight = pntm->ntmTm.tmHeight;
|
|
|
|
pntm->ntmTm.ntmAvgWidth = pntm->ntmTm.tmAveCharWidth;
|
2004-12-21 15:45:38 +01:00
|
|
|
|
2013-01-14 14:45:40 +01:00
|
|
|
lstrcpynW(pelf->elfLogFont.lfFaceName, family_name, LF_FACESIZE);
|
2020-09-07 15:18:56 +02:00
|
|
|
lstrcpynW( pelf->elfFullName, face->full_name, LF_FULLFACESIZE );
|
2020-09-07 15:18:57 +02:00
|
|
|
lstrcpynW( pelf->elfStyle, face->style_name, LF_FACESIZE );
|
2004-06-16 22:06:26 +02:00
|
|
|
}
|
2001-09-12 22:21:06 +02:00
|
|
|
|
2008-01-11 10:04:26 +01:00
|
|
|
pntm->ntmTm.ntmFlags = face->ntmFlags;
|
2008-02-27 22:21:10 +01:00
|
|
|
pntm->ntmFontSig = face->fs;
|
2008-01-11 10:04:26 +01:00
|
|
|
|
2002-01-29 04:02:50 +01:00
|
|
|
pelf->elfScript[0] = '\0'; /* This will get set in WineEngEnumFonts */
|
2001-09-12 22:21:06 +02:00
|
|
|
|
2008-01-14 12:39:33 +01:00
|
|
|
pelf->elfLogFont.lfEscapement = 0;
|
|
|
|
pelf->elfLogFont.lfOrientation = 0;
|
2008-01-11 10:04:26 +01:00
|
|
|
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;
|
|
|
|
|
2008-01-16 07:46:24 +01:00
|
|
|
face->cached_enum_data = HeapAlloc(GetProcessHeap(), 0, sizeof(*face->cached_enum_data));
|
|
|
|
if (face->cached_enum_data)
|
|
|
|
{
|
2008-02-27 22:21:10 +01:00
|
|
|
face->cached_enum_data->elf = *pelf;
|
|
|
|
face->cached_enum_data->ntm = *pntm;
|
2008-01-16 07:46:24 +01:00
|
|
|
}
|
2007-12-11 14:09:21 +01:00
|
|
|
|
2020-10-21 11:03:51 +02:00
|
|
|
free_gdi_font(gdi_font);
|
2001-09-12 22:21:06 +02:00
|
|
|
}
|
|
|
|
|
2014-05-06 22:42:41 +02:00
|
|
|
static BOOL family_matches(Family *family, const WCHAR *face_name)
|
2011-02-14 10:54:25 +01:00
|
|
|
{
|
2012-12-07 14:29:28 +01:00
|
|
|
Face *face;
|
|
|
|
const struct list *face_list;
|
2011-02-14 10:54:25 +01:00
|
|
|
|
2020-09-07 15:18:58 +02:00
|
|
|
if (!strncmpiW( face_name, family->family_name, LF_FACESIZE - 1 )) return TRUE;
|
2011-02-14 10:54:25 +01:00
|
|
|
|
2012-03-08 14:33:15 +01:00
|
|
|
face_list = get_face_list_from_family(family);
|
2012-12-07 14:29:28 +01:00
|
|
|
LIST_FOR_EACH_ENTRY(face, face_list, Face, entry)
|
2020-09-07 15:18:56 +02:00
|
|
|
if (!strncmpiW( face_name, face->full_name, LF_FACESIZE - 1 )) return TRUE;
|
2011-02-14 10:54:25 +01:00
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2014-05-06 22:42:41 +02:00
|
|
|
static BOOL face_matches(const WCHAR *family_name, Face *face, const WCHAR *face_name)
|
2011-02-14 10:54:25 +01:00
|
|
|
{
|
2017-11-26 14:33:17 +01:00
|
|
|
if (!strncmpiW(face_name, family_name, LF_FACESIZE - 1)) return TRUE;
|
2020-09-07 15:18:56 +02:00
|
|
|
return !strncmpiW( face_name, face->full_name, LF_FACESIZE - 1 );
|
2011-02-14 10:54:25 +01:00
|
|
|
}
|
|
|
|
|
2012-03-08 14:33:20 +01:00
|
|
|
static BOOL enum_face_charsets(const Family *family, Face *face, struct enum_charset_list *list,
|
2015-07-17 11:23:19 +02:00
|
|
|
FONTENUMPROCW proc, LPARAM lparam, const WCHAR *subst)
|
2011-04-01 14:19:54 +02:00
|
|
|
{
|
|
|
|
ENUMLOGFONTEXW elf;
|
|
|
|
NEWTEXTMETRICEXW ntm;
|
2020-10-23 20:14:49 +02:00
|
|
|
DWORD type, i;
|
2011-04-01 14:19:54 +02:00
|
|
|
|
2020-10-23 20:14:49 +02:00
|
|
|
GetEnumStructs( face, face->family->family_name, &elf, &ntm );
|
|
|
|
type = get_font_type( &ntm );
|
2011-04-01 14:20:00 +02:00
|
|
|
for(i = 0; i < list->total; i++) {
|
2011-04-01 14:19:54 +02:00
|
|
|
if(!face->scalable && face->fs.fsCsb[0] == 0) { /* OEM bitmap */
|
|
|
|
elf.elfLogFont.lfCharSet = ntm.ntmTm.tmCharSet = OEM_CHARSET;
|
2012-09-27 13:07:01 +02:00
|
|
|
load_script_name( IDS_OEM_DOS - IDS_FIRST_SCRIPT, elf.elfScript );
|
2011-11-12 12:09:33 +01:00
|
|
|
i = list->total; /* break out of loop after enumeration */
|
2012-09-27 13:07:01 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if(!(face->fs.fsCsb[0] & list->element[i].mask)) continue;
|
|
|
|
/* use the DEFAULT_CHARSET case only if no other charset is present */
|
|
|
|
if (list->element[i].charset == DEFAULT_CHARSET &&
|
|
|
|
(face->fs.fsCsb[0] & ~list->element[i].mask)) continue;
|
2011-04-01 14:20:00 +02:00
|
|
|
elf.elfLogFont.lfCharSet = ntm.ntmTm.tmCharSet = list->element[i].charset;
|
2011-11-03 14:11:35 +01:00
|
|
|
strcpyW(elf.elfScript, list->element[i].name);
|
|
|
|
if (!elf.elfScript[0])
|
2011-04-01 14:20:00 +02:00
|
|
|
FIXME("Unknown elfscript for bit %d\n", ffs(list->element[i].mask) - 1);
|
2011-04-01 14:19:54 +02:00
|
|
|
}
|
2012-03-08 14:33:20 +01:00
|
|
|
/* Font Replacement */
|
|
|
|
if (family != face->family)
|
|
|
|
{
|
2020-09-07 15:18:58 +02:00
|
|
|
lstrcpynW( elf.elfLogFont.lfFaceName, family->family_name, LF_FACESIZE );
|
2020-09-07 15:18:56 +02:00
|
|
|
lstrcpynW( elf.elfFullName, face->full_name, LF_FULLFACESIZE );
|
2012-03-08 14:33:20 +01:00
|
|
|
}
|
2015-07-17 11:23:19 +02:00
|
|
|
if (subst)
|
|
|
|
strcpyW(elf.elfLogFont.lfFaceName, subst);
|
2011-04-01 14:19:54 +02:00
|
|
|
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),
|
2011-11-12 12:09:33 +01:00
|
|
|
elf.elfLogFont.lfCharSet, type, debugstr_w(elf.elfScript),
|
2011-04-01 14:19:54 +02:00
|
|
|
elf.elfLogFont.lfItalic, elf.elfLogFont.lfWeight,
|
|
|
|
ntm.ntmTm.ntmFlags);
|
|
|
|
/* release section before callback (FIXME) */
|
2020-10-22 11:54:35 +02:00
|
|
|
LeaveCriticalSection( &font_cs );
|
2011-04-01 14:19:54 +02:00
|
|
|
if (!proc(&elf.elfLogFont, (TEXTMETRICW *)&ntm, type, lparam)) return FALSE;
|
2020-10-22 11:54:35 +02:00
|
|
|
EnterCriticalSection( &font_cs );
|
2011-04-01 14:19:54 +02:00
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2001-09-12 22:21:06 +02:00
|
|
|
/*************************************************************
|
2011-10-19 15:37:10 +02:00
|
|
|
* freetype_EnumFonts
|
2001-09-12 22:21:06 +02:00
|
|
|
*/
|
2020-10-20 22:05:37 +02:00
|
|
|
static BOOL CDECL freetype_EnumFonts( LPLOGFONTW plf, FONTENUMPROCW proc, LPARAM lparam )
|
2001-09-12 22:21:06 +02:00
|
|
|
{
|
|
|
|
Family *family;
|
|
|
|
Face *face;
|
2012-12-07 14:29:28 +01:00
|
|
|
const struct list *face_list;
|
2003-06-23 22:51:06 +02:00
|
|
|
LOGFONTW lf;
|
2011-04-01 14:20:00 +02:00
|
|
|
struct enum_charset_list enum_charsets;
|
2001-09-12 22:21:06 +02:00
|
|
|
|
2008-01-15 09:02:29 +01:00
|
|
|
if (!plf)
|
|
|
|
{
|
|
|
|
lf.lfCharSet = DEFAULT_CHARSET;
|
|
|
|
lf.lfPitchAndFamily = 0;
|
|
|
|
lf.lfFaceName[0] = 0;
|
|
|
|
plf = &lf;
|
|
|
|
}
|
|
|
|
|
2002-01-29 04:02:50 +01:00
|
|
|
TRACE("facename = %s charset %d\n", debugstr_w(plf->lfFaceName), plf->lfCharSet);
|
2002-06-22 03:19:29 +02:00
|
|
|
|
2011-04-01 14:20:00 +02:00
|
|
|
create_enum_charset_list(plf->lfCharSet, &enum_charsets);
|
|
|
|
|
2020-10-22 11:54:35 +02:00
|
|
|
EnterCriticalSection( &font_cs );
|
2001-09-12 22:21:06 +02:00
|
|
|
if(plf->lfFaceName[0]) {
|
2014-05-06 22:42:41 +02:00
|
|
|
WCHAR *face_name = plf->lfFaceName;
|
|
|
|
FontSubst *psub = get_font_subst(&font_subst_list, plf->lfFaceName, plf->lfCharSet);
|
2006-03-30 15:38:07 +02:00
|
|
|
|
2003-06-23 22:51:06 +02:00
|
|
|
if(psub) {
|
|
|
|
TRACE("substituting %s -> %s\n", debugstr_w(plf->lfFaceName),
|
|
|
|
debugstr_w(psub->to.name));
|
2014-05-06 22:42:41 +02:00
|
|
|
face_name = psub->to.name;
|
2003-06-23 22:51:06 +02:00
|
|
|
}
|
2004-08-18 00:08:31 +02:00
|
|
|
|
2012-12-07 14:29:28 +01:00
|
|
|
LIST_FOR_EACH_ENTRY( family, &font_list, Family, entry ) {
|
2014-05-06 22:42:41 +02:00
|
|
|
if (!family_matches(family, face_name)) continue;
|
2012-12-07 14:29:28 +01:00
|
|
|
face_list = get_face_list_from_family(family);
|
|
|
|
LIST_FOR_EACH_ENTRY( face, face_list, Face, entry ) {
|
2020-09-07 15:18:58 +02:00
|
|
|
if (!face_matches( family->family_name, face, face_name )) continue;
|
2015-07-17 11:23:19 +02:00
|
|
|
if (!enum_face_charsets(family, face, &enum_charsets, proc, lparam, psub ? psub->from.name : NULL)) return FALSE;
|
2001-09-12 22:21:06 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2012-12-07 14:29:28 +01:00
|
|
|
LIST_FOR_EACH_ENTRY( family, &font_list, Family, entry ) {
|
2012-03-08 14:33:15 +01:00
|
|
|
face_list = get_face_list_from_family(family);
|
2012-12-07 14:29:28 +01:00
|
|
|
face = LIST_ENTRY(list_head(face_list), Face, entry);
|
2015-07-17 11:23:19 +02:00
|
|
|
if (!enum_face_charsets(family, face, &enum_charsets, proc, lparam, NULL)) return FALSE;
|
2001-09-12 22:21:06 +02:00
|
|
|
}
|
|
|
|
}
|
2020-10-22 11:54:35 +02:00
|
|
|
LeaveCriticalSection( &font_cs );
|
2011-10-19 15:34:21 +02:00
|
|
|
return TRUE;
|
2001-09-12 22:21:06 +02:00
|
|
|
}
|
|
|
|
|
2002-01-29 04:02:50 +01:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
|
2006-01-13 14:25:16 +01:00
|
|
|
/***************************************************
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-03-13 10:40:58 +01:00
|
|
|
/*
|
|
|
|
* GSUB Table handling functions
|
|
|
|
*/
|
|
|
|
|
|
|
|
static INT GSUB_is_glyph_covered(LPCVOID table , UINT glyph)
|
|
|
|
{
|
|
|
|
const GSUB_CoverageFormat1* cf1;
|
|
|
|
|
2009-01-26 11:01:12 +01:00
|
|
|
cf1 = table;
|
2008-03-13 10:40:58 +01:00
|
|
|
|
|
|
|
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;
|
2010-03-27 11:56:11 +01:00
|
|
|
cf2 = (const GSUB_CoverageFormat2*)cf1;
|
2008-03-13 10:40:58 +01:00
|
|
|
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
}
|
2008-03-19 05:29:21 +01:00
|
|
|
return -1;
|
2008-03-13 10:40:58 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
ERR("Unknown CoverageFormat %i\n",GET_BE_WORD(cf1->CoverageFormat));
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static FT_UInt GSUB_apply_feature(const GSUB_Header * header, const GSUB_Feature* feature, UINT glyph)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int offset;
|
|
|
|
const GSUB_LookupList *lookup;
|
2010-03-27 11:56:11 +01:00
|
|
|
lookup = (const GSUB_LookupList*)((const BYTE*)header + GET_BE_WORD(header->LookupList));
|
2008-03-13 10:40:58 +01:00
|
|
|
|
|
|
|
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])]);
|
2010-03-27 11:56:11 +01:00
|
|
|
look = (const GSUB_LookupTable*)((const BYTE*)lookup + offset);
|
2008-03-13 10:40:58 +01:00
|
|
|
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]);
|
2010-03-27 11:56:11 +01:00
|
|
|
ssf1 = (const GSUB_SingleSubstFormat1*)((const BYTE*)look+offset);
|
2008-03-13 10:40:58 +01:00
|
|
|
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));
|
2010-03-27 11:56:11 +01:00
|
|
|
if (GSUB_is_glyph_covered((const BYTE*)ssf1+offset, glyph) != -1)
|
2008-03-13 10:40:58 +01:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
|
2010-03-27 11:56:11 +01:00
|
|
|
ssf2 = (const GSUB_SingleSubstFormat2 *)ssf1;
|
2008-03-13 10:40:58 +01:00
|
|
|
offset = GET_BE_WORD(ssf1->Coverage);
|
|
|
|
TRACE(" subtype 2, glyph count %i\n", GET_BE_WORD(ssf2->GlyphCount));
|
2010-03-27 11:56:11 +01:00
|
|
|
index = GSUB_is_glyph_covered((const BYTE*)ssf2+offset, glyph);
|
2008-03-13 10:40:58 +01:00
|
|
|
TRACE(" Coverage index %i\n",index);
|
|
|
|
if (index != -1)
|
|
|
|
{
|
|
|
|
TRACE(" Glyph is 0x%x ->",glyph);
|
|
|
|
glyph = GET_BE_WORD(ssf2->Substitute[index]);
|
|
|
|
TRACE("0x%x\n",glyph);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return glyph;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static FT_UInt get_GSUB_vert_glyph(const GdiFont *font, UINT glyph)
|
|
|
|
{
|
|
|
|
const GSUB_Header *header;
|
|
|
|
const GSUB_Feature *feature;
|
|
|
|
|
|
|
|
if (!font->GSUB_Table)
|
|
|
|
return glyph;
|
|
|
|
|
|
|
|
header = font->GSUB_Table;
|
2013-05-20 18:24:21 +02:00
|
|
|
feature = font->vert_feature;
|
2008-03-13 10:40:58 +01:00
|
|
|
|
|
|
|
return GSUB_apply_feature(header, feature, glyph);
|
|
|
|
}
|
|
|
|
|
2018-06-22 12:35:43 +02:00
|
|
|
static FT_UInt get_glyph_index_symbol(const GdiFont *font, UINT glyph)
|
|
|
|
{
|
|
|
|
FT_UInt ret;
|
|
|
|
|
|
|
|
if (glyph < 0x100) glyph += 0xf000;
|
|
|
|
/* there are a number of old pre-Unicode "broken" TTFs, which
|
|
|
|
do have symbols at U+00XX instead of U+f0XX */
|
|
|
|
if (!(ret = pFT_Get_Char_Index(font->ft_face, glyph)))
|
|
|
|
ret = pFT_Get_Char_Index(font->ft_face, glyph - 0xf000);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2008-02-05 21:16:06 +01:00
|
|
|
static FT_UInt get_glyph_index(const GdiFont *font, UINT glyph)
|
2002-01-29 04:02:50 +01:00
|
|
|
{
|
2020-10-21 11:04:35 +02:00
|
|
|
struct gdi_font *gdi_font = font->gdi_font;
|
2018-06-22 12:35:43 +02:00
|
|
|
FT_UInt ret;
|
|
|
|
WCHAR wc;
|
|
|
|
char buf;
|
2008-03-13 10:40:58 +01:00
|
|
|
|
2018-06-22 12:35:43 +02:00
|
|
|
if (font->ft_face->charmap->encoding == FT_ENCODING_NONE)
|
|
|
|
{
|
2005-09-07 13:27:05 +02:00
|
|
|
BOOL default_used;
|
2006-01-13 14:25:16 +01:00
|
|
|
BOOL *default_used_pointer;
|
2018-06-22 12:35:43 +02:00
|
|
|
|
2006-01-13 14:25:16 +01:00
|
|
|
default_used_pointer = NULL;
|
|
|
|
default_used = FALSE;
|
2020-10-21 11:04:35 +02:00
|
|
|
if (codepage_sets_default_used(gdi_font->codepage))
|
2006-01-13 14:25:16 +01:00
|
|
|
default_used_pointer = &default_used;
|
2018-06-22 12:35:43 +02:00
|
|
|
wc = (WCHAR)glyph;
|
2020-10-21 11:04:35 +02:00
|
|
|
if (!WideCharToMultiByte(gdi_font->codepage, 0, &wc, 1, &buf, sizeof(buf), NULL, default_used_pointer) ||
|
2018-06-22 12:35:43 +02:00
|
|
|
default_used)
|
2012-10-25 11:54:33 +02:00
|
|
|
{
|
2020-10-21 11:04:35 +02:00
|
|
|
if (gdi_font->codepage == CP_SYMBOL)
|
2018-06-22 12:35:43 +02:00
|
|
|
{
|
|
|
|
ret = get_glyph_index_symbol(font, glyph);
|
|
|
|
if (!ret)
|
|
|
|
{
|
|
|
|
if (WideCharToMultiByte(CP_ACP, 0, &wc, 1, &buf, 1, NULL, NULL))
|
|
|
|
ret = get_glyph_index_symbol(font, buf);
|
|
|
|
}
|
|
|
|
}
|
2012-10-25 11:54:33 +02:00
|
|
|
else
|
|
|
|
ret = 0;
|
|
|
|
}
|
2005-09-07 13:27:05 +02:00
|
|
|
else
|
|
|
|
ret = pFT_Get_Char_Index(font->ft_face, (unsigned char)buf);
|
2013-12-11 13:11:15 +01:00
|
|
|
TRACE("%04x (%02x) -> ret %d def_used %d\n", glyph, (unsigned char)buf, ret, default_used);
|
2012-01-10 11:08:03 +01:00
|
|
|
return ret;
|
2004-08-26 20:24:03 +02:00
|
|
|
}
|
|
|
|
|
2018-06-22 12:35:43 +02:00
|
|
|
if (font->ft_face->charmap->encoding == FT_ENCODING_MS_SYMBOL)
|
2011-05-06 19:32:03 +02:00
|
|
|
{
|
2018-06-22 12:35:43 +02:00
|
|
|
ret = get_glyph_index_symbol(font, glyph);
|
|
|
|
if (!ret)
|
|
|
|
{
|
|
|
|
wc = (WCHAR)glyph;
|
|
|
|
if (WideCharToMultiByte(CP_ACP, 0, &wc, 1, &buf, 1, NULL, NULL))
|
|
|
|
ret = get_glyph_index_symbol(font, (unsigned char)buf);
|
|
|
|
}
|
|
|
|
return ret;
|
2011-05-06 19:32:03 +02:00
|
|
|
}
|
|
|
|
|
2018-06-22 12:35:43 +02:00
|
|
|
return pFT_Get_Char_Index(font->ft_face, glyph);
|
2002-01-29 04:02:50 +01:00
|
|
|
}
|
|
|
|
|
2020-10-22 13:13:52 +02:00
|
|
|
/*************************************************************
|
|
|
|
* freetype_get_glyph_index
|
|
|
|
*/
|
|
|
|
static BOOL CDECL freetype_get_glyph_index( struct gdi_font *gdi_font, UINT *glyph )
|
2013-12-11 13:11:15 +01:00
|
|
|
{
|
2020-10-22 13:13:52 +02:00
|
|
|
GdiFont *font = get_font_ptr( gdi_font );
|
2013-12-11 13:11:15 +01:00
|
|
|
|
2020-10-22 13:13:52 +02:00
|
|
|
if (font->ft_face->charmap->encoding == FT_ENCODING_NONE) return FALSE;
|
2013-12-11 13:11:15 +01:00
|
|
|
|
2020-10-22 13:13:52 +02:00
|
|
|
if (font->ft_face->charmap->encoding == FT_ENCODING_MS_SYMBOL)
|
2013-12-11 13:11:15 +01:00
|
|
|
{
|
2020-10-22 13:13:52 +02:00
|
|
|
if (!(*glyph = get_glyph_index_symbol( font, *glyph )))
|
|
|
|
{
|
|
|
|
WCHAR wc = *glyph;
|
|
|
|
char ch;
|
2013-05-22 12:46:28 +02:00
|
|
|
|
2020-10-22 13:13:52 +02:00
|
|
|
if (WideCharToMultiByte( CP_ACP, 0, &wc, 1, &ch, 1, NULL, NULL ))
|
|
|
|
*glyph = get_glyph_index_symbol( font, (unsigned char)ch );
|
|
|
|
}
|
|
|
|
return TRUE;
|
2013-05-22 12:46:28 +02:00
|
|
|
}
|
|
|
|
|
2020-10-22 13:13:52 +02:00
|
|
|
if ((*glyph = pFT_Get_Char_Index( font->ft_face, *glyph )))
|
|
|
|
*glyph = get_GSUB_vert_glyph( font, *glyph );
|
|
|
|
|
|
|
|
return TRUE;
|
2013-05-22 12:46:28 +02:00
|
|
|
}
|
|
|
|
|
2002-04-03 22:41:14 +02:00
|
|
|
/*************************************************************
|
2020-10-22 13:13:52 +02:00
|
|
|
* freetype_get_default_glyph
|
2002-04-03 22:41:14 +02:00
|
|
|
*/
|
2020-10-22 13:13:52 +02:00
|
|
|
static UINT CDECL freetype_get_default_glyph( struct gdi_font *gdi_font )
|
2002-04-03 22:41:14 +02:00
|
|
|
{
|
2020-10-22 13:13:52 +02:00
|
|
|
GdiFont *font = get_font_ptr( gdi_font );
|
|
|
|
FT_WinFNT_HeaderRec winfnt;
|
|
|
|
TT_OS2 *pOS2;
|
2002-04-03 22:41:14 +02:00
|
|
|
|
2020-10-22 13:13:52 +02:00
|
|
|
if ((pOS2 = pFT_Get_Sfnt_Table( font->ft_face, ft_sfnt_os2 )))
|
2006-08-23 16:49:13 +02:00
|
|
|
{
|
2020-10-22 13:13:52 +02:00
|
|
|
UINT glyph = pOS2->usDefaultChar;
|
|
|
|
freetype_get_glyph_index( gdi_font, &glyph );
|
|
|
|
return glyph;
|
2006-08-23 16:49:13 +02:00
|
|
|
}
|
2020-10-22 13:13:52 +02:00
|
|
|
if (!pFT_Get_WinFNT_Header( font->ft_face, &winfnt )) return winfnt.default_char + winfnt.first_char;
|
|
|
|
return 32;
|
2002-04-03 22:41:14 +02:00
|
|
|
}
|
|
|
|
|
2020-10-22 13:13:52 +02:00
|
|
|
|
2008-06-24 09:11:40 +02:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
|
2008-06-24 09:11:57 +02:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
|
2016-01-13 15:09:08 +01:00
|
|
|
static inline FT_Vector normalize_vector(FT_Vector *vec)
|
|
|
|
{
|
|
|
|
FT_Vector out;
|
|
|
|
FT_Fixed len;
|
|
|
|
len = pFT_Vector_Length(vec);
|
|
|
|
if (len) {
|
|
|
|
out.x = (vec->x << 6) / len;
|
|
|
|
out.y = (vec->y << 6) / len;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
out.x = out.y = 0;
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
2019-02-07 15:43:38 +01:00
|
|
|
/* get_glyph_outline() glyph transform matrices index */
|
|
|
|
enum matrices_index
|
|
|
|
{
|
|
|
|
matrix_hori,
|
|
|
|
matrix_vert,
|
|
|
|
matrix_unrotated
|
|
|
|
};
|
|
|
|
|
2020-10-26 11:43:24 +01:00
|
|
|
static BOOL get_transform_matrices( struct gdi_font *font, BOOL vertical, const MAT2 *user_transform,
|
2019-02-07 15:43:38 +01:00
|
|
|
FT_Matrix matrices[3] )
|
|
|
|
{
|
|
|
|
static const FT_Matrix identity_mat = { (1 << 16), 0, 0, (1 << 16) };
|
|
|
|
BOOL needs_transform = FALSE;
|
|
|
|
double width_ratio;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
matrices[matrix_unrotated] = identity_mat;
|
|
|
|
|
|
|
|
/* Scaling factor */
|
2020-10-26 11:43:24 +01:00
|
|
|
if (font->aveWidth)
|
2019-02-07 15:43:38 +01:00
|
|
|
{
|
2020-10-26 11:43:24 +01:00
|
|
|
if (!freetype_set_outline_text_metrics( font )) freetype_set_bitmap_text_metrics( font );
|
|
|
|
width_ratio = (double)font->aveWidth;
|
|
|
|
width_ratio /= (double)font->otm.otmTextMetrics.tmAveCharWidth;
|
2019-02-07 15:43:38 +01:00
|
|
|
}
|
|
|
|
else
|
2020-10-26 11:43:24 +01:00
|
|
|
width_ratio = font->scale_y;
|
2019-02-07 15:43:38 +01:00
|
|
|
|
|
|
|
/* Scaling transform */
|
2020-10-26 11:43:24 +01:00
|
|
|
if (width_ratio != 1.0 || font->scale_y != 1.0)
|
2019-02-07 15:43:38 +01:00
|
|
|
{
|
|
|
|
FT_Matrix scale_mat;
|
|
|
|
scale_mat.xx = FT_FixedFromFloat( width_ratio );
|
|
|
|
scale_mat.xy = 0;
|
|
|
|
scale_mat.yx = 0;
|
2020-10-26 11:43:24 +01:00
|
|
|
scale_mat.yy = FT_FixedFromFloat( font->scale_y );
|
2019-02-07 15:43:38 +01:00
|
|
|
|
|
|
|
pFT_Matrix_Multiply( &scale_mat, &matrices[matrix_unrotated] );
|
|
|
|
needs_transform = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Slant transform */
|
2020-10-26 11:43:24 +01:00
|
|
|
if (font->fake_italic)
|
2019-02-07 15:43:38 +01:00
|
|
|
{
|
|
|
|
FT_Matrix slant_mat;
|
|
|
|
slant_mat.xx = (1 << 16);
|
|
|
|
slant_mat.xy = (1 << 16) >> 2;
|
|
|
|
slant_mat.yx = 0;
|
|
|
|
slant_mat.yy = (1 << 16);
|
|
|
|
|
|
|
|
pFT_Matrix_Multiply( &slant_mat, &matrices[matrix_unrotated] );
|
|
|
|
needs_transform = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Rotation transform */
|
|
|
|
matrices[matrix_hori] = matrices[matrix_unrotated];
|
2020-10-26 11:43:24 +01:00
|
|
|
if (font->scalable && font->lf.lfOrientation % 3600)
|
2019-02-07 15:43:38 +01:00
|
|
|
{
|
|
|
|
FT_Matrix rotation_mat;
|
|
|
|
FT_Vector angle;
|
|
|
|
|
2020-10-26 11:43:24 +01:00
|
|
|
pFT_Vector_Unit( &angle, MulDiv( 1 << 16, font->lf.lfOrientation, 10 ) );
|
2019-02-07 15:43:38 +01:00
|
|
|
rotation_mat.xx = angle.x;
|
|
|
|
rotation_mat.xy = -angle.y;
|
|
|
|
rotation_mat.yx = angle.y;
|
|
|
|
rotation_mat.yy = angle.x;
|
|
|
|
pFT_Matrix_Multiply( &rotation_mat, &matrices[matrix_hori] );
|
|
|
|
needs_transform = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Vertical transform */
|
|
|
|
matrices[matrix_vert] = matrices[matrix_hori];
|
|
|
|
if (vertical)
|
|
|
|
{
|
|
|
|
FT_Matrix vertical_mat = { 0, -(1 << 16), 1 << 16, 0 }; /* 90 degrees rotation */
|
|
|
|
|
|
|
|
pFT_Matrix_Multiply( &vertical_mat, &matrices[matrix_vert] );
|
|
|
|
needs_transform = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* World transform */
|
2020-10-26 11:43:24 +01:00
|
|
|
if (!is_identity_FMAT2( &font->matrix ))
|
2019-02-07 15:43:38 +01:00
|
|
|
{
|
|
|
|
FT_Matrix world_mat;
|
2020-10-26 11:43:24 +01:00
|
|
|
world_mat.xx = FT_FixedFromFloat( font->matrix.eM11 );
|
|
|
|
world_mat.xy = -FT_FixedFromFloat( font->matrix.eM21 );
|
|
|
|
world_mat.yx = -FT_FixedFromFloat( font->matrix.eM12 );
|
|
|
|
world_mat.yy = FT_FixedFromFloat( font->matrix.eM22 );
|
2019-02-07 15:43:38 +01:00
|
|
|
|
|
|
|
for (i = 0; i < 3; i++)
|
|
|
|
pFT_Matrix_Multiply( &world_mat, &matrices[i] );
|
|
|
|
needs_transform = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Extra transformation specified by caller */
|
|
|
|
if (!is_identity_MAT2( user_transform ))
|
|
|
|
{
|
|
|
|
FT_Matrix user_mat;
|
|
|
|
user_mat.xx = FT_FixedFromFIXED( user_transform->eM11 );
|
|
|
|
user_mat.xy = FT_FixedFromFIXED( user_transform->eM21 );
|
|
|
|
user_mat.yx = FT_FixedFromFIXED( user_transform->eM12 );
|
|
|
|
user_mat.yy = FT_FixedFromFIXED( user_transform->eM22 );
|
|
|
|
|
|
|
|
for (i = 0; i < 3; i++)
|
|
|
|
pFT_Matrix_Multiply( &user_mat, &matrices[i] );
|
|
|
|
needs_transform = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return needs_transform;
|
|
|
|
}
|
|
|
|
|
2016-01-13 15:09:09 +01:00
|
|
|
static BOOL get_bold_glyph_outline(FT_GlyphSlot glyph, LONG ppem, FT_Glyph_Metrics *metrics)
|
2013-10-17 14:25:10 +02:00
|
|
|
{
|
|
|
|
FT_Error err;
|
2016-01-13 15:09:09 +01:00
|
|
|
FT_Pos strength;
|
|
|
|
FT_BBox bbox;
|
2013-10-17 14:25:10 +02:00
|
|
|
|
2016-01-13 15:09:09 +01:00
|
|
|
if(glyph->format != FT_GLYPH_FORMAT_OUTLINE)
|
|
|
|
return FALSE;
|
|
|
|
if(!pFT_Outline_Embolden)
|
|
|
|
return FALSE;
|
2013-10-17 14:25:10 +02:00
|
|
|
|
2016-01-13 15:09:09 +01:00
|
|
|
strength = MulDiv(ppem, 1 << 6, 24);
|
|
|
|
err = pFT_Outline_Embolden(&glyph->outline, strength);
|
|
|
|
if(err) {
|
|
|
|
TRACE("FT_Ouline_Embolden returns %d\n", err);
|
|
|
|
return FALSE;
|
2013-10-17 14:25:10 +02:00
|
|
|
}
|
2016-01-13 15:09:09 +01:00
|
|
|
|
|
|
|
pFT_Outline_Get_CBox(&glyph->outline, &bbox);
|
|
|
|
metrics->width = bbox.xMax - bbox.xMin;
|
|
|
|
metrics->height = bbox.yMax - bbox.yMin;
|
|
|
|
metrics->horiBearingX = bbox.xMin;
|
|
|
|
metrics->horiBearingY = bbox.yMax;
|
|
|
|
metrics->vertBearingX = metrics->horiBearingX - metrics->horiAdvance / 2;
|
|
|
|
metrics->vertBearingY = (metrics->vertAdvance - metrics->height) / 2;
|
|
|
|
return TRUE;
|
2013-10-17 14:25:10 +02:00
|
|
|
}
|
|
|
|
|
2011-11-16 12:20:50 +01:00
|
|
|
static inline BYTE get_max_level( UINT format )
|
|
|
|
{
|
|
|
|
switch( format )
|
|
|
|
{
|
|
|
|
case GGO_GRAY2_BITMAP: return 4;
|
|
|
|
case GGO_GRAY4_BITMAP: return 16;
|
|
|
|
case GGO_GRAY8_BITMAP: return 64;
|
|
|
|
}
|
|
|
|
return 255;
|
|
|
|
}
|
|
|
|
|
2016-02-24 05:39:01 +01:00
|
|
|
extern const unsigned short vertical_orientation_table[] DECLSPEC_HIDDEN;
|
2013-06-04 16:05:50 +02:00
|
|
|
|
|
|
|
static BOOL check_unicode_tategaki(WCHAR uchar)
|
|
|
|
{
|
2013-11-15 17:37:45 +01:00
|
|
|
unsigned short orientation = vertical_orientation_table[vertical_orientation_table[vertical_orientation_table[uchar >> 8]+((uchar >> 4) & 0x0f)]+ (uchar & 0xf)];
|
2013-06-04 16:05:50 +02:00
|
|
|
|
2013-11-15 17:37:45 +01:00
|
|
|
/* We only reach this code if typographical substitution did not occur */
|
|
|
|
/* Type: U or Type: Tu */
|
|
|
|
return (orientation == 1 || orientation == 3);
|
2013-06-04 16:05:50 +02:00
|
|
|
}
|
|
|
|
|
2020-10-26 11:43:24 +01:00
|
|
|
static FT_Vector get_advance_metric(struct gdi_font *incoming_font, struct gdi_font *font,
|
2016-01-13 15:09:06 +01:00
|
|
|
const FT_Glyph_Metrics *metrics,
|
|
|
|
const FT_Matrix *transMat, BOOL vertical_metrics)
|
|
|
|
{
|
|
|
|
FT_Vector adv;
|
|
|
|
FT_Fixed base_advance, em_scale = 0;
|
|
|
|
BOOL fixed_pitch_full = FALSE;
|
|
|
|
|
|
|
|
if (vertical_metrics)
|
|
|
|
base_advance = metrics->vertAdvance;
|
|
|
|
else
|
|
|
|
base_advance = metrics->horiAdvance;
|
|
|
|
|
|
|
|
adv.x = base_advance;
|
|
|
|
adv.y = 0;
|
|
|
|
|
|
|
|
/* In fixed-pitch font, we adjust the fullwidth character advance so that
|
|
|
|
they have double halfwidth character width. E.g. if the font is 19 ppem,
|
|
|
|
we return 20 (not 19) for fullwidth characters as we return 10 for
|
|
|
|
halfwidth characters. */
|
2020-10-26 11:43:24 +01:00
|
|
|
if (freetype_set_outline_text_metrics(incoming_font) &&
|
|
|
|
!(incoming_font->otm.otmTextMetrics.tmPitchAndFamily & TMPF_FIXED_PITCH)) {
|
2016-01-13 15:09:06 +01:00
|
|
|
UINT avg_advance;
|
2020-10-26 11:43:24 +01:00
|
|
|
em_scale = MulDiv(incoming_font->ppem, 1 << 16,
|
|
|
|
get_font_ptr(incoming_font)->ft_face->units_per_EM);
|
|
|
|
avg_advance = pFT_MulFix(incoming_font->ntmAvgWidth, em_scale);
|
2016-01-13 15:09:06 +01:00
|
|
|
fixed_pitch_full = (avg_advance > 0 &&
|
|
|
|
(base_advance + 63) >> 6 ==
|
2020-10-26 11:43:24 +01:00
|
|
|
pFT_MulFix(incoming_font->ntmAvgWidth*2, em_scale));
|
2016-01-13 15:09:06 +01:00
|
|
|
if (fixed_pitch_full && !transMat)
|
|
|
|
adv.x = (avg_advance * 2) << 6;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (transMat) {
|
|
|
|
pFT_Vector_Transform(&adv, transMat);
|
|
|
|
if (fixed_pitch_full && adv.y == 0) {
|
|
|
|
FT_Vector vec;
|
2020-10-26 11:43:24 +01:00
|
|
|
vec.x = incoming_font->ntmAvgWidth;
|
2016-01-13 15:09:06 +01:00
|
|
|
vec.y = 0;
|
|
|
|
pFT_Vector_Transform(&vec, transMat);
|
|
|
|
adv.x = (pFT_MulFix(vec.x, em_scale) * 2) << 6;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-26 11:43:24 +01:00
|
|
|
if (font->fake_bold) {
|
2016-01-13 15:09:08 +01:00
|
|
|
if (!transMat)
|
|
|
|
adv.x += 1 << 6;
|
|
|
|
else {
|
|
|
|
FT_Vector fake_bold_adv, vec = { 1 << 6, 0 };
|
|
|
|
pFT_Vector_Transform(&vec, transMat);
|
|
|
|
fake_bold_adv = normalize_vector(&vec);
|
|
|
|
adv.x += fake_bold_adv.x;
|
|
|
|
adv.y += fake_bold_adv.y;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-13 15:09:06 +01:00
|
|
|
adv.x = (adv.x + 63) & -64;
|
|
|
|
adv.y = -((adv.y + 63) & -64);
|
|
|
|
return adv;
|
|
|
|
}
|
|
|
|
|
2019-02-08 02:51:30 +01:00
|
|
|
static FT_BBox get_transformed_bbox( const FT_Glyph_Metrics *metrics,
|
|
|
|
BOOL needs_transform, const FT_Matrix metrices[3] )
|
|
|
|
{
|
|
|
|
FT_BBox bbox = { 0, 0, 0, 0 };
|
|
|
|
|
|
|
|
if (!needs_transform)
|
|
|
|
{
|
|
|
|
bbox.xMin = (metrics->horiBearingX) & -64;
|
|
|
|
bbox.xMax = (metrics->horiBearingX + metrics->width + 63) & -64;
|
|
|
|
bbox.yMax = (metrics->horiBearingY + 63) & -64;
|
|
|
|
bbox.yMin = (metrics->horiBearingY - metrics->height) & -64;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
FT_Vector vec;
|
|
|
|
INT xc, yc;
|
|
|
|
|
|
|
|
for (xc = 0; xc < 2; xc++)
|
|
|
|
{
|
|
|
|
for (yc = 0; yc < 2; yc++)
|
|
|
|
{
|
|
|
|
vec.x = metrics->horiBearingX + xc * metrics->width;
|
|
|
|
vec.y = metrics->horiBearingY - yc * metrics->height;
|
|
|
|
TRACE( "Vec %ld,i %ld\n", vec.x, vec.y );
|
|
|
|
pFT_Vector_Transform( &vec, &metrices[matrix_vert] );
|
|
|
|
if (xc == 0 && yc == 0)
|
|
|
|
{
|
|
|
|
bbox.xMin = bbox.xMax = vec.x;
|
|
|
|
bbox.yMin = bbox.yMax = vec.y;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (vec.x < bbox.xMin) bbox.xMin = vec.x;
|
|
|
|
else if (vec.x > bbox.xMax) bbox.xMax = vec.x;
|
|
|
|
if (vec.y < bbox.yMin) bbox.yMin = vec.y;
|
|
|
|
else if (vec.y > bbox.yMax) bbox.yMax = vec.y;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
bbox.xMin = bbox.xMin & -64;
|
|
|
|
bbox.xMax = (bbox.xMax + 63) & -64;
|
|
|
|
bbox.yMin = bbox.yMin & -64;
|
|
|
|
bbox.yMax = (bbox.yMax + 63) & -64;
|
|
|
|
TRACE( "transformed box: (%ld, %ld - %ld, %ld)\n", bbox.xMin, bbox.yMax, bbox.xMax, bbox.yMin );
|
|
|
|
}
|
|
|
|
|
|
|
|
return bbox;
|
|
|
|
}
|
|
|
|
|
2020-10-26 11:43:24 +01:00
|
|
|
static void compute_metrics( struct gdi_font *incoming_font, struct gdi_font *font,
|
2019-02-08 02:51:30 +01:00
|
|
|
FT_BBox bbox, const FT_Glyph_Metrics *metrics,
|
|
|
|
BOOL vertical, BOOL vertical_metrics,
|
|
|
|
BOOL needs_transform, const FT_Matrix matrices[3],
|
|
|
|
GLYPHMETRICS *gm, ABC *abc )
|
|
|
|
{
|
|
|
|
FT_Vector adv, vec, origin;
|
|
|
|
|
|
|
|
if (!needs_transform)
|
|
|
|
{
|
|
|
|
adv = get_advance_metric( incoming_font, font, metrics, NULL, vertical_metrics );
|
|
|
|
gm->gmCellIncX = adv.x >> 6;
|
|
|
|
gm->gmCellIncY = 0;
|
|
|
|
origin.x = bbox.xMin;
|
|
|
|
origin.y = bbox.yMax;
|
|
|
|
abc->abcA = origin.x >> 6;
|
|
|
|
abc->abcB = (metrics->width + 63) >> 6;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
FT_Pos lsb;
|
|
|
|
|
2020-10-26 11:43:24 +01:00
|
|
|
if (vertical && freetype_set_outline_text_metrics( font ))
|
2019-02-08 02:51:30 +01:00
|
|
|
{
|
|
|
|
if (vertical_metrics)
|
|
|
|
lsb = metrics->horiBearingY + metrics->vertBearingY;
|
|
|
|
else
|
2020-10-26 11:43:24 +01:00
|
|
|
lsb = metrics->vertAdvance + (font->otm.otmDescent << 6);
|
2019-02-08 02:51:30 +01:00
|
|
|
vec.x = lsb;
|
2020-10-26 11:43:24 +01:00
|
|
|
vec.y = font->otm.otmDescent << 6;
|
2019-02-08 02:51:30 +01:00
|
|
|
TRACE( "Vec %ld,%ld\n", vec.x>>6, vec.y>>6 );
|
|
|
|
pFT_Vector_Transform( &vec, &matrices[matrix_hori] );
|
|
|
|
origin.x = (vec.x + bbox.xMin) & -64;
|
|
|
|
origin.y = (vec.y + bbox.yMax + 63) & -64;
|
|
|
|
lsb -= metrics->horiBearingY;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
origin.x = bbox.xMin;
|
|
|
|
origin.y = bbox.yMax;
|
|
|
|
lsb = metrics->horiBearingX;
|
|
|
|
}
|
|
|
|
|
|
|
|
adv = get_advance_metric( incoming_font, font, metrics, &matrices[matrix_hori],
|
|
|
|
vertical_metrics );
|
|
|
|
gm->gmCellIncX = adv.x >> 6;
|
|
|
|
gm->gmCellIncY = adv.y >> 6;
|
|
|
|
|
|
|
|
adv = get_advance_metric( incoming_font, font, metrics, &matrices[matrix_unrotated],
|
|
|
|
vertical_metrics );
|
|
|
|
adv.x = pFT_Vector_Length( &adv );
|
|
|
|
adv.y = 0;
|
|
|
|
|
|
|
|
vec.x = lsb;
|
|
|
|
vec.y = 0;
|
|
|
|
pFT_Vector_Transform( &vec, &matrices[matrix_unrotated] );
|
|
|
|
if (lsb > 0) abc->abcA = pFT_Vector_Length( &vec ) >> 6;
|
|
|
|
else abc->abcA = -((pFT_Vector_Length( &vec ) + 63) >> 6);
|
|
|
|
|
|
|
|
/* We use lsb again to avoid rounding errors */
|
|
|
|
vec.x = lsb + (vertical ? metrics->height : metrics->width);
|
|
|
|
vec.y = 0;
|
|
|
|
pFT_Vector_Transform( &vec, &matrices[matrix_unrotated] );
|
|
|
|
abc->abcB = ((pFT_Vector_Length( &vec ) + 63) >> 6) - abc->abcA;
|
|
|
|
}
|
|
|
|
if (!abc->abcB) abc->abcB = 1;
|
|
|
|
abc->abcC = (adv.x >> 6) - abc->abcA - abc->abcB;
|
|
|
|
|
|
|
|
gm->gmptGlyphOrigin.x = origin.x >> 6;
|
|
|
|
gm->gmptGlyphOrigin.y = origin.y >> 6;
|
|
|
|
gm->gmBlackBoxX = (bbox.xMax - bbox.xMin) >> 6;
|
|
|
|
gm->gmBlackBoxY = (bbox.yMax - bbox.yMin) >> 6;
|
|
|
|
if (!gm->gmBlackBoxX) gm->gmBlackBoxX = 1;
|
|
|
|
if (!gm->gmBlackBoxY) gm->gmBlackBoxY = 1;
|
|
|
|
|
|
|
|
TRACE( "gm: %u, %u, %s, %d, %d abc %d, %u, %d\n",
|
|
|
|
gm->gmBlackBoxX, gm->gmBlackBoxY, wine_dbgstr_point(&gm->gmptGlyphOrigin),
|
|
|
|
gm->gmCellIncX, gm->gmCellIncY, abc->abcA, abc->abcB, abc->abcC );
|
|
|
|
}
|
|
|
|
|
2019-02-08 02:51:31 +01:00
|
|
|
|
|
|
|
static const BYTE masks[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
|
|
|
|
|
|
|
|
static DWORD get_mono_glyph_bitmap( FT_GlyphSlot glyph, FT_BBox bbox,
|
|
|
|
BOOL fake_bold, BOOL needs_transform, FT_Matrix matrices[3],
|
|
|
|
DWORD buflen, BYTE *buf )
|
|
|
|
{
|
|
|
|
DWORD width = (bbox.xMax - bbox.xMin ) >> 6;
|
|
|
|
DWORD height = (bbox.yMax - bbox.yMin ) >> 6;
|
|
|
|
DWORD pitch = ((width + 31) >> 5) << 2;
|
|
|
|
DWORD needed = pitch * height;
|
|
|
|
FT_Bitmap ft_bitmap;
|
|
|
|
BYTE *src, *dst;
|
|
|
|
INT w, h, x;
|
|
|
|
|
|
|
|
if (!buf || !buflen) return needed;
|
|
|
|
if (!needed) return GDI_ERROR; /* empty glyph */
|
|
|
|
if (needed > buflen) return GDI_ERROR;
|
|
|
|
|
|
|
|
switch (glyph->format)
|
|
|
|
{
|
|
|
|
case FT_GLYPH_FORMAT_BITMAP:
|
|
|
|
src = glyph->bitmap.buffer;
|
|
|
|
dst = buf;
|
|
|
|
w = min( pitch, (glyph->bitmap.width + 7) >> 3 );
|
|
|
|
h = min( height, glyph->bitmap.rows );
|
|
|
|
while (h--)
|
|
|
|
{
|
|
|
|
if (!fake_bold)
|
|
|
|
memcpy( dst, src, w );
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dst[0] = 0;
|
|
|
|
for (x = 0; x < w; x++)
|
|
|
|
{
|
|
|
|
dst[x] = (dst[x] & 0x80) | (src[x] >> 1) | src[x];
|
|
|
|
if (x + 1 < pitch)
|
|
|
|
dst[x + 1] = (src[x] & 0x01) << 7;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
src += 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 (needs_transform)
|
|
|
|
pFT_Outline_Transform( &glyph->outline, &matrices[matrix_vert] );
|
|
|
|
pFT_Outline_Translate( &glyph->outline, -bbox.xMin, -bbox.yMin );
|
|
|
|
|
|
|
|
/* Note: FreeType will only set 'black' bits for us. */
|
|
|
|
memset( buf, 0, buflen );
|
|
|
|
pFT_Outline_Get_Bitmap( library, &glyph->outline, &ft_bitmap );
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
FIXME( "loaded glyph format %x\n", glyph->format );
|
|
|
|
return GDI_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
return needed;
|
|
|
|
}
|
|
|
|
|
|
|
|
static DWORD get_antialias_glyph_bitmap( FT_GlyphSlot glyph, FT_BBox bbox, UINT format,
|
|
|
|
BOOL fake_bold, BOOL needs_transform, FT_Matrix matrices[3],
|
|
|
|
DWORD buflen, BYTE *buf )
|
|
|
|
{
|
|
|
|
DWORD width = (bbox.xMax - bbox.xMin ) >> 6;
|
|
|
|
DWORD height = (bbox.yMax - bbox.yMin ) >> 6;
|
|
|
|
DWORD pitch = (width + 3) / 4 * 4;
|
|
|
|
DWORD needed = pitch * height;
|
|
|
|
FT_Bitmap ft_bitmap;
|
|
|
|
INT w, h, x, max_level;
|
|
|
|
BYTE *src, *dst;
|
|
|
|
|
|
|
|
if (!buf || !buflen) return needed;
|
|
|
|
if (!needed) return GDI_ERROR; /* empty glyph */
|
|
|
|
if (needed > buflen) return GDI_ERROR;
|
|
|
|
|
|
|
|
max_level = get_max_level( format );
|
|
|
|
|
|
|
|
switch (glyph->format)
|
|
|
|
{
|
|
|
|
case FT_GLYPH_FORMAT_BITMAP:
|
|
|
|
src = glyph->bitmap.buffer;
|
|
|
|
dst = buf;
|
|
|
|
memset( buf, 0, buflen );
|
|
|
|
|
|
|
|
w = min( pitch, glyph->bitmap.width );
|
|
|
|
h = min( height, glyph->bitmap.rows );
|
|
|
|
while (h--)
|
|
|
|
{
|
|
|
|
for (x = 0; x < w; x++)
|
|
|
|
{
|
|
|
|
if (src[x / 8] & masks[x % 8])
|
|
|
|
{
|
|
|
|
dst[x] = max_level;
|
|
|
|
if (fake_bold && x + 1 < pitch) dst[x + 1] = max_level;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
src += 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_GRAY;
|
|
|
|
ft_bitmap.buffer = buf;
|
|
|
|
|
|
|
|
if (needs_transform)
|
|
|
|
pFT_Outline_Transform( &glyph->outline, &matrices[matrix_vert] );
|
|
|
|
pFT_Outline_Translate( &glyph->outline, -bbox.xMin, -bbox.yMin );
|
|
|
|
|
|
|
|
memset( buf, 0, buflen );
|
|
|
|
pFT_Outline_Get_Bitmap( library, &glyph->outline, &ft_bitmap );
|
|
|
|
|
|
|
|
if (max_level != 255)
|
|
|
|
{
|
|
|
|
INT row, col;
|
|
|
|
BYTE *ptr, *start;
|
|
|
|
|
|
|
|
for (row = 0, start = buf; row < height; row++)
|
|
|
|
{
|
|
|
|
for (col = 0, ptr = start; col < width; col++, ptr++)
|
|
|
|
*ptr = (((int)*ptr) * (max_level + 1)) / 256;
|
|
|
|
start += pitch;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
FIXME("loaded glyph format %x\n", glyph->format);
|
|
|
|
return GDI_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
return needed;
|
|
|
|
}
|
|
|
|
|
|
|
|
static DWORD get_subpixel_glyph_bitmap( FT_GlyphSlot glyph, FT_BBox bbox, UINT format,
|
|
|
|
BOOL fake_bold, BOOL needs_transform, FT_Matrix matrices[3],
|
|
|
|
GLYPHMETRICS *gm, DWORD buflen, BYTE *buf )
|
|
|
|
{
|
|
|
|
DWORD width = (bbox.xMax - bbox.xMin ) >> 6;
|
|
|
|
DWORD height = (bbox.yMax - bbox.yMin ) >> 6;
|
|
|
|
DWORD pitch, needed = 0;
|
|
|
|
BYTE *src, *dst;
|
|
|
|
INT w, h, x;
|
|
|
|
|
|
|
|
switch (glyph->format)
|
|
|
|
{
|
|
|
|
case FT_GLYPH_FORMAT_BITMAP:
|
|
|
|
pitch = width * 4;
|
|
|
|
needed = pitch * height;
|
|
|
|
|
|
|
|
if (!buf || !buflen) break;
|
|
|
|
if (!needed) return GDI_ERROR; /* empty glyph */
|
|
|
|
if (needed > buflen) return GDI_ERROR;
|
|
|
|
|
|
|
|
src = glyph->bitmap.buffer;
|
|
|
|
dst = buf;
|
|
|
|
memset( buf, 0, buflen );
|
|
|
|
|
|
|
|
w = min( width, glyph->bitmap.width );
|
|
|
|
h = min( height, glyph->bitmap.rows );
|
|
|
|
while (h--)
|
|
|
|
{
|
|
|
|
for (x = 0; x < w; x++)
|
|
|
|
{
|
|
|
|
if ( src[x / 8] & masks[x % 8] )
|
|
|
|
{
|
|
|
|
((unsigned int *)dst)[x] = ~0u;
|
|
|
|
if (fake_bold && x + 1 < width) ((unsigned int *)dst)[x + 1] = ~0u;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
src += glyph->bitmap.pitch;
|
|
|
|
dst += pitch;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case FT_GLYPH_FORMAT_OUTLINE:
|
|
|
|
{
|
|
|
|
INT src_pitch, src_width, src_height, x_shift, y_shift;
|
|
|
|
INT sub_stride, hmul, vmul;
|
|
|
|
const INT *sub_order;
|
|
|
|
const INT rgb_order[3] = { 0, 1, 2 };
|
|
|
|
const INT bgr_order[3] = { 2, 1, 0 };
|
|
|
|
FT_Render_Mode render_mode =
|
|
|
|
(format == WINE_GGO_HRGB_BITMAP ||
|
|
|
|
format == WINE_GGO_HBGR_BITMAP) ? FT_RENDER_MODE_LCD : FT_RENDER_MODE_LCD_V;
|
|
|
|
|
|
|
|
if (!width || !height) /* empty glyph */
|
|
|
|
{
|
|
|
|
if (!buf || !buflen) break;
|
|
|
|
return GDI_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( render_mode == FT_RENDER_MODE_LCD)
|
|
|
|
{
|
|
|
|
gm->gmBlackBoxX += 2;
|
|
|
|
gm->gmptGlyphOrigin.x -= 1;
|
|
|
|
bbox.xMin -= (1 << 6);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gm->gmBlackBoxY += 2;
|
|
|
|
gm->gmptGlyphOrigin.y += 1;
|
|
|
|
bbox.yMax += (1 << 6);
|
|
|
|
}
|
|
|
|
|
|
|
|
width = gm->gmBlackBoxX;
|
|
|
|
height = gm->gmBlackBoxY;
|
|
|
|
pitch = width * 4;
|
|
|
|
needed = pitch * height;
|
|
|
|
|
|
|
|
if (!buf || !buflen) return needed;
|
|
|
|
if (needed > buflen) return GDI_ERROR;
|
|
|
|
|
|
|
|
if (needs_transform)
|
|
|
|
pFT_Outline_Transform( &glyph->outline, &matrices[matrix_vert] );
|
|
|
|
|
|
|
|
#ifdef FT_LCD_FILTER_H
|
|
|
|
if (pFT_Library_SetLcdFilter)
|
|
|
|
pFT_Library_SetLcdFilter( library, FT_LCD_FILTER_DEFAULT );
|
|
|
|
#endif
|
|
|
|
pFT_Render_Glyph( glyph, render_mode );
|
|
|
|
|
|
|
|
src_pitch = glyph->bitmap.pitch;
|
|
|
|
src_width = glyph->bitmap.width;
|
|
|
|
src_height = glyph->bitmap.rows;
|
|
|
|
src = glyph->bitmap.buffer;
|
|
|
|
dst = buf;
|
|
|
|
memset( buf, 0, buflen );
|
|
|
|
|
|
|
|
sub_order = (format == WINE_GGO_HRGB_BITMAP ||
|
|
|
|
format == WINE_GGO_VRGB_BITMAP) ? rgb_order : bgr_order;
|
|
|
|
sub_stride = render_mode == FT_RENDER_MODE_LCD ? 1 : src_pitch;
|
|
|
|
hmul = render_mode == FT_RENDER_MODE_LCD ? 3 : 1;
|
|
|
|
vmul = render_mode == FT_RENDER_MODE_LCD ? 1 : 3;
|
|
|
|
|
|
|
|
x_shift = glyph->bitmap_left - (bbox.xMin >> 6);
|
|
|
|
if ( x_shift < 0 )
|
|
|
|
{
|
|
|
|
src += hmul * -x_shift;
|
|
|
|
src_width -= hmul * -x_shift;
|
|
|
|
}
|
|
|
|
else if ( x_shift > 0 )
|
|
|
|
{
|
|
|
|
dst += x_shift * sizeof(unsigned int);
|
|
|
|
width -= x_shift;
|
|
|
|
}
|
|
|
|
|
|
|
|
y_shift = (bbox.yMax >> 6) - glyph->bitmap_top;
|
|
|
|
if ( y_shift < 0 )
|
|
|
|
{
|
|
|
|
src += src_pitch * vmul * -y_shift;
|
|
|
|
src_height -= vmul * -y_shift;
|
|
|
|
}
|
|
|
|
else if ( y_shift > 0 )
|
|
|
|
{
|
|
|
|
dst += y_shift * pitch;
|
|
|
|
height -= y_shift;
|
|
|
|
}
|
|
|
|
|
|
|
|
w = min( width, src_width / hmul );
|
|
|
|
h = min( height, src_height / vmul );
|
|
|
|
while (h--)
|
|
|
|
{
|
|
|
|
for (x = 0; x < w; x++)
|
|
|
|
{
|
|
|
|
((unsigned int *)dst)[x] =
|
|
|
|
((unsigned int)src[hmul * x + sub_stride * sub_order[0]] << 16) |
|
|
|
|
((unsigned int)src[hmul * x + sub_stride * sub_order[1]] << 8) |
|
|
|
|
((unsigned int)src[hmul * x + sub_stride * sub_order[2]]);
|
|
|
|
}
|
|
|
|
src += src_pitch * vmul;
|
|
|
|
dst += pitch;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
FIXME ( "loaded glyph format %x\n", glyph->format );
|
|
|
|
return GDI_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
return needed;
|
|
|
|
}
|
|
|
|
|
2014-10-09 18:00:55 +02:00
|
|
|
static unsigned int get_native_glyph_outline(FT_Outline *outline, unsigned int buflen, char *buf)
|
|
|
|
{
|
|
|
|
TTPOLYGONHEADER *pph;
|
|
|
|
TTPOLYCURVE *ppc;
|
|
|
|
unsigned int needed = 0, point = 0, contour, first_pt;
|
|
|
|
unsigned int pph_start, cpfx;
|
|
|
|
DWORD type;
|
|
|
|
|
|
|
|
for (contour = 0; contour < outline->n_contours; contour++)
|
|
|
|
{
|
|
|
|
/* Ignore contours containing one point */
|
|
|
|
if (point == outline->contours[contour])
|
|
|
|
{
|
|
|
|
point++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
pph_start = needed;
|
|
|
|
pph = (TTPOLYGONHEADER *)(buf + needed);
|
|
|
|
first_pt = point;
|
|
|
|
if (buf)
|
|
|
|
{
|
|
|
|
pph->dwType = TT_POLYGON_TYPE;
|
|
|
|
FTVectorToPOINTFX(&outline->points[point], &pph->pfxStart);
|
|
|
|
}
|
|
|
|
needed += sizeof(*pph);
|
|
|
|
point++;
|
|
|
|
while (point <= outline->contours[contour])
|
|
|
|
{
|
|
|
|
ppc = (TTPOLYCURVE *)(buf + needed);
|
|
|
|
type = outline->tags[point] & FT_Curve_Tag_On ?
|
|
|
|
TT_PRIM_LINE : TT_PRIM_QSPLINE;
|
|
|
|
cpfx = 0;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
if (buf)
|
|
|
|
FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
|
|
|
|
cpfx++;
|
|
|
|
point++;
|
|
|
|
} while (point <= outline->contours[contour] &&
|
|
|
|
(outline->tags[point] & FT_Curve_Tag_On) ==
|
|
|
|
(outline->tags[point-1] & FT_Curve_Tag_On));
|
|
|
|
/* At the end of a contour Windows adds the start point, but
|
|
|
|
only for Beziers */
|
|
|
|
if (point > outline->contours[contour] &&
|
|
|
|
!(outline->tags[point-1] & FT_Curve_Tag_On))
|
|
|
|
{
|
|
|
|
if (buf)
|
|
|
|
FTVectorToPOINTFX(&outline->points[first_pt], &ppc->apfx[cpfx]);
|
|
|
|
cpfx++;
|
|
|
|
}
|
|
|
|
else if (point <= outline->contours[contour] &&
|
|
|
|
outline->tags[point] & FT_Curve_Tag_On)
|
|
|
|
{
|
|
|
|
/* add closing pt for bezier */
|
|
|
|
if (buf)
|
|
|
|
FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
|
|
|
|
cpfx++;
|
|
|
|
point++;
|
|
|
|
}
|
|
|
|
if (buf)
|
|
|
|
{
|
|
|
|
ppc->wType = type;
|
|
|
|
ppc->cpfx = cpfx;
|
|
|
|
}
|
|
|
|
needed += sizeof(*ppc) + (cpfx - 1) * sizeof(POINTFX);
|
|
|
|
}
|
|
|
|
if (buf)
|
|
|
|
pph->cb = needed - pph_start;
|
|
|
|
}
|
|
|
|
return needed;
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned int get_bezier_glyph_outline(FT_Outline *outline, unsigned int buflen, char *buf)
|
|
|
|
{
|
|
|
|
/* Convert the quadratic Beziers to cubic Beziers.
|
|
|
|
The parametric eqn for a cubic Bezier is, from PLRM:
|
|
|
|
r(t) = at^3 + bt^2 + ct + r0
|
|
|
|
with the control points:
|
|
|
|
r1 = r0 + c/3
|
|
|
|
r2 = r1 + (c + b)/3
|
|
|
|
r3 = r0 + c + b + a
|
|
|
|
|
|
|
|
A quadratic Bezier has the form:
|
|
|
|
p(t) = (1-t)^2 p0 + 2(1-t)t p1 + t^2 p2
|
|
|
|
|
|
|
|
So equating powers of t leads to:
|
|
|
|
r1 = 2/3 p1 + 1/3 p0
|
|
|
|
r2 = 2/3 p1 + 1/3 p2
|
|
|
|
and of course r0 = p0, r3 = p2
|
|
|
|
*/
|
|
|
|
int contour, point = 0, first_pt;
|
|
|
|
TTPOLYGONHEADER *pph;
|
|
|
|
TTPOLYCURVE *ppc;
|
|
|
|
DWORD pph_start, cpfx, type;
|
|
|
|
FT_Vector cubic_control[4];
|
|
|
|
unsigned int needed = 0;
|
|
|
|
|
|
|
|
for (contour = 0; contour < outline->n_contours; contour++)
|
|
|
|
{
|
|
|
|
pph_start = needed;
|
|
|
|
pph = (TTPOLYGONHEADER *)(buf + needed);
|
|
|
|
first_pt = point;
|
|
|
|
if (buf)
|
|
|
|
{
|
|
|
|
pph->dwType = TT_POLYGON_TYPE;
|
|
|
|
FTVectorToPOINTFX(&outline->points[point], &pph->pfxStart);
|
|
|
|
}
|
|
|
|
needed += sizeof(*pph);
|
|
|
|
point++;
|
|
|
|
while (point <= outline->contours[contour])
|
|
|
|
{
|
|
|
|
ppc = (TTPOLYCURVE *)(buf + needed);
|
|
|
|
type = outline->tags[point] & FT_Curve_Tag_On ?
|
|
|
|
TT_PRIM_LINE : TT_PRIM_CSPLINE;
|
|
|
|
cpfx = 0;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
if (type == TT_PRIM_LINE)
|
|
|
|
{
|
|
|
|
if (buf)
|
|
|
|
FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
|
|
|
|
cpfx++;
|
|
|
|
point++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Unlike QSPLINEs, CSPLINEs always have their endpoint
|
|
|
|
so cpfx = 3n */
|
|
|
|
|
|
|
|
/* FIXME: Possible optimization in endpoint calculation
|
|
|
|
if there are two consecutive curves */
|
|
|
|
cubic_control[0] = outline->points[point-1];
|
|
|
|
if (!(outline->tags[point-1] & FT_Curve_Tag_On))
|
|
|
|
{
|
|
|
|
cubic_control[0].x += outline->points[point].x + 1;
|
|
|
|
cubic_control[0].y += outline->points[point].y + 1;
|
|
|
|
cubic_control[0].x >>= 1;
|
|
|
|
cubic_control[0].y >>= 1;
|
|
|
|
}
|
|
|
|
if (point+1 > outline->contours[contour])
|
|
|
|
cubic_control[3] = outline->points[first_pt];
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cubic_control[3] = outline->points[point+1];
|
|
|
|
if (!(outline->tags[point+1] & FT_Curve_Tag_On))
|
|
|
|
{
|
|
|
|
cubic_control[3].x += outline->points[point].x + 1;
|
|
|
|
cubic_control[3].y += outline->points[point].y + 1;
|
|
|
|
cubic_control[3].x >>= 1;
|
|
|
|
cubic_control[3].y >>= 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* r1 = 1/3 p0 + 2/3 p1
|
|
|
|
r2 = 1/3 p2 + 2/3 p1 */
|
|
|
|
cubic_control[1].x = (2 * outline->points[point].x + 1) / 3;
|
|
|
|
cubic_control[1].y = (2 * outline->points[point].y + 1) / 3;
|
|
|
|
cubic_control[2] = cubic_control[1];
|
|
|
|
cubic_control[1].x += (cubic_control[0].x + 1) / 3;
|
|
|
|
cubic_control[1].y += (cubic_control[0].y + 1) / 3;
|
|
|
|
cubic_control[2].x += (cubic_control[3].x + 1) / 3;
|
|
|
|
cubic_control[2].y += (cubic_control[3].y + 1) / 3;
|
|
|
|
if (buf)
|
|
|
|
{
|
|
|
|
FTVectorToPOINTFX(&cubic_control[1], &ppc->apfx[cpfx]);
|
|
|
|
FTVectorToPOINTFX(&cubic_control[2], &ppc->apfx[cpfx+1]);
|
|
|
|
FTVectorToPOINTFX(&cubic_control[3], &ppc->apfx[cpfx+2]);
|
|
|
|
}
|
|
|
|
cpfx += 3;
|
|
|
|
point++;
|
|
|
|
}
|
|
|
|
} while (point <= outline->contours[contour] &&
|
|
|
|
(outline->tags[point] & FT_Curve_Tag_On) ==
|
|
|
|
(outline->tags[point-1] & FT_Curve_Tag_On));
|
|
|
|
/* At the end of a contour Windows adds the start point,
|
|
|
|
but only for Beziers and we've already done that.
|
|
|
|
*/
|
|
|
|
if (point <= outline->contours[contour] &&
|
|
|
|
outline->tags[point] & FT_Curve_Tag_On)
|
|
|
|
{
|
|
|
|
/* This is the closing pt of a bezier, but we've already
|
|
|
|
added it, so just inc point and carry on */
|
|
|
|
point++;
|
|
|
|
}
|
|
|
|
if (buf)
|
|
|
|
{
|
|
|
|
ppc->wType = type;
|
|
|
|
ppc->cpfx = cpfx;
|
|
|
|
}
|
|
|
|
needed += sizeof(*ppc) + (cpfx - 1) * sizeof(POINTFX);
|
|
|
|
}
|
|
|
|
if (buf)
|
|
|
|
pph->cb = needed - pph_start;
|
|
|
|
}
|
|
|
|
return needed;
|
|
|
|
}
|
|
|
|
|
2018-11-13 10:27:09 +01:00
|
|
|
static FT_Int get_load_flags( UINT format )
|
|
|
|
{
|
|
|
|
FT_Int load_flags = FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
|
|
|
|
|
|
|
|
if (format & GGO_UNHINTED)
|
|
|
|
return load_flags | FT_LOAD_NO_HINTING;
|
|
|
|
|
|
|
|
switch (format & ~GGO_GLYPH_INDEX)
|
|
|
|
{
|
|
|
|
case GGO_BITMAP:
|
|
|
|
load_flags |= FT_LOAD_TARGET_MONO;
|
|
|
|
break;
|
|
|
|
case GGO_GRAY2_BITMAP:
|
|
|
|
case GGO_GRAY4_BITMAP:
|
|
|
|
case GGO_GRAY8_BITMAP:
|
|
|
|
case WINE_GGO_GRAY16_BITMAP:
|
|
|
|
load_flags |= FT_LOAD_TARGET_NORMAL;
|
|
|
|
break;
|
|
|
|
case WINE_GGO_HRGB_BITMAP:
|
|
|
|
case WINE_GGO_HBGR_BITMAP:
|
|
|
|
load_flags |= FT_LOAD_TARGET_LCD;
|
|
|
|
break;
|
|
|
|
case WINE_GGO_VRGB_BITMAP:
|
|
|
|
case WINE_GGO_VBGR_BITMAP:
|
|
|
|
load_flags |= FT_LOAD_TARGET_LCD_V;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return load_flags;
|
|
|
|
}
|
|
|
|
|
2020-10-22 11:56:47 +02:00
|
|
|
/*************************************************************
|
|
|
|
* freetype_get_glyph_outline
|
|
|
|
*/
|
|
|
|
static DWORD CDECL freetype_get_glyph_outline( struct gdi_font *incoming_gdi_font, UINT glyph, UINT format,
|
|
|
|
GLYPHMETRICS *lpgm, ABC *abc, DWORD buflen, void *buf,
|
|
|
|
const MAT2 *lpmat )
|
2001-09-12 22:21:06 +02:00
|
|
|
{
|
2013-10-03 17:56:17 +02:00
|
|
|
GLYPHMETRICS gm;
|
2020-10-22 11:56:47 +02:00
|
|
|
GdiFont *incoming_font = get_font_ptr( incoming_gdi_font );
|
2007-05-02 18:15:12 +02:00
|
|
|
FT_Face ft_face = incoming_font->ft_face;
|
|
|
|
GdiFont *font = incoming_font;
|
2020-10-21 11:01:50 +02:00
|
|
|
struct gdi_font *gdi_font = font->gdi_font;
|
2013-04-30 09:20:57 +02:00
|
|
|
FT_Glyph_Metrics metrics;
|
2001-09-12 22:21:06 +02:00
|
|
|
FT_UInt glyph_index;
|
2019-02-08 02:51:31 +01:00
|
|
|
DWORD needed = 0;
|
2002-01-29 04:02:50 +01:00
|
|
|
FT_Error err;
|
2019-02-08 02:51:30 +01:00
|
|
|
FT_BBox bbox;
|
2018-11-13 10:27:09 +01:00
|
|
|
FT_Int load_flags = get_load_flags(format);
|
2019-02-07 15:43:38 +01:00
|
|
|
FT_Matrix matrices[3];
|
2003-10-10 02:06:35 +02:00
|
|
|
BOOL needsTransform = FALSE;
|
2020-10-26 11:43:24 +01:00
|
|
|
BOOL tategaki = (*get_gdi_font_name(gdi_font) == '@');
|
2013-05-24 18:00:41 +02:00
|
|
|
BOOL vertical_metrics;
|
2003-10-10 02:06:35 +02:00
|
|
|
|
2006-10-12 22:56:56 +02:00
|
|
|
TRACE("%p, %04x, %08x, %p, %08x, %p, %p\n", font, glyph, format, lpgm,
|
2001-09-12 22:21:06 +02:00
|
|
|
buflen, buf, lpmat);
|
|
|
|
|
2008-06-24 09:10:27 +02:00
|
|
|
TRACE("font transform %f %f %f %f\n",
|
2020-10-21 11:01:50 +02:00
|
|
|
gdi_font->matrix.eM11, gdi_font->matrix.eM12,
|
|
|
|
gdi_font->matrix.eM21, gdi_font->matrix.eM22);
|
2008-06-24 09:10:27 +02:00
|
|
|
|
2002-01-29 04:02:50 +01:00
|
|
|
if(format & GGO_GLYPH_INDEX) {
|
2013-12-11 13:11:17 +01:00
|
|
|
if(font->ft_face->charmap->encoding == FT_ENCODING_NONE) {
|
|
|
|
/* Windows bitmap font, e.g. Small Fonts, uses ANSI character code
|
2014-01-09 15:02:45 +01:00
|
|
|
as glyph index. "Treasure Adventure Game" depends on this. */
|
2013-12-11 13:11:17 +01:00
|
|
|
glyph_index = pFT_Get_Char_Index(font->ft_face, glyph);
|
|
|
|
TRACE("translate glyph index %04x -> %04x\n", glyph, glyph_index);
|
|
|
|
} else
|
|
|
|
glyph_index = glyph;
|
2002-01-29 04:02:50 +01:00
|
|
|
format &= ~GGO_GLYPH_INDEX;
|
2013-06-04 16:05:50 +02:00
|
|
|
/* TODO: Window also turns off tategaki for glyphs passed in by index
|
|
|
|
if their unicode code points fall outside of the range that is
|
|
|
|
rotated. */
|
2007-05-02 18:15:12 +02:00
|
|
|
} else {
|
2013-06-04 16:05:50 +02:00
|
|
|
BOOL vert;
|
|
|
|
get_glyph_index_linked(incoming_font, glyph, &font, &glyph_index, &vert);
|
2007-05-02 18:15:12 +02:00
|
|
|
ft_face = font->ft_face;
|
2020-10-21 11:01:50 +02:00
|
|
|
gdi_font = font->gdi_font;
|
2013-06-04 16:05:50 +02:00
|
|
|
if (!vert && tategaki)
|
|
|
|
tategaki = check_unicode_tategaki(glyph);
|
2007-05-02 18:15:12 +02:00
|
|
|
}
|
2002-01-29 04:02:50 +01:00
|
|
|
|
2018-11-13 10:27:09 +01:00
|
|
|
format &= ~GGO_UNHINTED;
|
2008-10-26 23:20:13 +01:00
|
|
|
|
2019-02-08 02:51:29 +01:00
|
|
|
if (format == GGO_METRICS && is_identity_MAT2(lpmat) &&
|
2020-10-22 11:51:07 +02:00
|
|
|
get_gdi_font_glyph_metrics( gdi_font, glyph_index, lpgm, abc ))
|
2019-02-08 02:51:29 +01:00
|
|
|
return 1; /* FIXME */
|
2007-08-30 12:17:23 +02:00
|
|
|
|
2020-10-26 11:43:24 +01:00
|
|
|
needsTransform = get_transform_matrices( gdi_font, tategaki, lpmat, matrices );
|
2003-10-10 02:06:35 +02:00
|
|
|
|
2013-05-24 18:00:41 +02:00
|
|
|
vertical_metrics = (tategaki && FT_HAS_VERTICAL(ft_face));
|
|
|
|
/* there is a freetype bug where vertical metrics are only
|
|
|
|
properly scaled and correct in 2.4.0 or greater */
|
2019-02-08 02:51:32 +01:00
|
|
|
if (vertical_metrics && FT_SimpleVersion < FT_VERSION_VALUE(2, 4, 0))
|
2013-05-24 18:00:41 +02:00
|
|
|
vertical_metrics = FALSE;
|
|
|
|
|
2012-11-15 13:29:21 +01:00
|
|
|
if (needsTransform || format != GGO_BITMAP) load_flags |= FT_LOAD_NO_BITMAP;
|
2013-05-24 18:00:41 +02:00
|
|
|
if (vertical_metrics) load_flags |= FT_LOAD_VERTICAL_LAYOUT;
|
2008-08-21 08:01:05 +02:00
|
|
|
|
|
|
|
err = pFT_Load_Glyph(ft_face, glyph_index, load_flags);
|
2020-02-05 11:17:35 +01:00
|
|
|
if (err && !(load_flags & FT_LOAD_NO_HINTING))
|
|
|
|
{
|
|
|
|
WARN("Failed to load glyph %#x, retrying without hinting. Error %#x.\n", glyph_index, err);
|
|
|
|
load_flags |= FT_LOAD_NO_HINTING;
|
|
|
|
err = pFT_Load_Glyph(ft_face, glyph_index, load_flags);
|
|
|
|
}
|
2008-08-21 08:01:05 +02:00
|
|
|
|
|
|
|
if(err) {
|
2020-02-05 11:17:35 +01:00
|
|
|
WARN("Failed to load glyph %#x, error %#x.\n", glyph_index, err);
|
2008-08-21 08:01:05 +02:00
|
|
|
return GDI_ERROR;
|
|
|
|
}
|
|
|
|
|
2013-10-17 14:25:10 +02:00
|
|
|
metrics = ft_face->glyph->metrics;
|
2020-10-21 11:05:28 +02:00
|
|
|
if(gdi_font->fake_bold) {
|
2020-10-22 11:49:33 +02:00
|
|
|
if (!get_bold_glyph_outline(ft_face->glyph, gdi_font->ppem, &metrics) && metrics.width)
|
2016-01-13 15:09:11 +01:00
|
|
|
metrics.width += 1 << 6;
|
|
|
|
}
|
2013-10-17 14:25:10 +02:00
|
|
|
|
2013-04-30 09:20:57 +02:00
|
|
|
/* Some poorly-created fonts contain glyphs that exceed the boundaries set
|
|
|
|
* by the text metrics. The proper behavior is to clip the glyph metrics to
|
|
|
|
* fit within the maximums specified in the text metrics. */
|
2020-10-26 11:43:24 +01:00
|
|
|
if (freetype_set_outline_text_metrics(incoming_gdi_font) ||
|
|
|
|
freetype_set_bitmap_text_metrics(incoming_gdi_font)) {
|
|
|
|
TEXTMETRICW *ptm = &incoming_gdi_font->otm.otmTextMetrics;
|
2019-02-08 02:51:30 +01:00
|
|
|
INT top = min( metrics.horiBearingY, ptm->tmAscent << 6 );
|
|
|
|
INT bottom = max( metrics.horiBearingY - metrics.height, -(ptm->tmDescent << 6) );
|
2013-04-30 09:20:57 +02:00
|
|
|
metrics.horiBearingY = top;
|
|
|
|
metrics.height = top - bottom;
|
|
|
|
|
|
|
|
/* TODO: Are we supposed to clip the width as well...? */
|
|
|
|
/* metrics.width = min( metrics.width, ptm->tmMaxCharWidth << 6 ); */
|
|
|
|
}
|
|
|
|
|
2019-02-08 02:51:30 +01:00
|
|
|
bbox = get_transformed_bbox( &metrics, needsTransform, matrices );
|
2020-10-26 11:43:24 +01:00
|
|
|
compute_metrics( incoming_gdi_font, gdi_font, bbox, &metrics,
|
2019-02-08 02:51:30 +01:00
|
|
|
tategaki, vertical_metrics, needsTransform, matrices,
|
|
|
|
&gm, abc );
|
2008-06-24 09:12:14 +02:00
|
|
|
|
2008-12-24 03:16:04 +01:00
|
|
|
if ((format == GGO_METRICS || format == GGO_BITMAP || format == WINE_GGO_GRAY16_BITMAP) &&
|
2009-02-03 10:55:19 +01:00
|
|
|
is_identity_MAT2(lpmat)) /* don't cache custom transforms */
|
2020-10-22 11:51:07 +02:00
|
|
|
set_gdi_font_glyph_metrics( gdi_font, glyph_index, &gm, abc );
|
2001-09-12 22:21:06 +02:00
|
|
|
|
|
|
|
if(format == GGO_METRICS)
|
2008-02-05 21:16:06 +01:00
|
|
|
{
|
2013-10-03 17:56:17 +02:00
|
|
|
*lpgm = gm;
|
2002-01-29 04:02:50 +01:00
|
|
|
return 1; /* FIXME */
|
2008-02-05 21:16:06 +01:00
|
|
|
}
|
2002-06-01 01:06:46 +02:00
|
|
|
|
2008-12-10 15:50:44 +01:00
|
|
|
if(ft_face->glyph->format != ft_glyph_format_outline &&
|
2011-11-16 12:20:51 +01:00
|
|
|
(format == GGO_NATIVE || format == GGO_BEZIER))
|
2008-12-10 15:50:44 +01:00
|
|
|
{
|
2004-06-16 22:06:26 +02:00
|
|
|
TRACE("loaded a bitmap\n");
|
2001-09-12 22:21:06 +02:00
|
|
|
return GDI_ERROR;
|
|
|
|
}
|
|
|
|
|
2019-02-08 02:51:31 +01:00
|
|
|
switch (format)
|
|
|
|
{
|
2002-01-29 04:02:50 +01:00
|
|
|
case GGO_BITMAP:
|
2020-10-21 11:05:28 +02:00
|
|
|
needed = get_mono_glyph_bitmap( ft_face->glyph, bbox, gdi_font->fake_bold,
|
2019-02-08 02:51:31 +01:00
|
|
|
needsTransform, matrices, buflen, buf );
|
|
|
|
break;
|
2002-01-29 04:02:50 +01:00
|
|
|
|
|
|
|
case GGO_GRAY2_BITMAP:
|
|
|
|
case GGO_GRAY4_BITMAP:
|
|
|
|
case GGO_GRAY8_BITMAP:
|
|
|
|
case WINE_GGO_GRAY16_BITMAP:
|
2020-10-21 11:05:28 +02:00
|
|
|
needed = get_antialias_glyph_bitmap( ft_face->glyph, bbox, format, gdi_font->fake_bold,
|
2019-02-08 02:51:31 +01:00
|
|
|
needsTransform, matrices, buflen, buf );
|
2002-01-29 04:02:50 +01:00
|
|
|
break;
|
|
|
|
|
2008-12-10 15:50:44 +01:00
|
|
|
case WINE_GGO_HRGB_BITMAP:
|
|
|
|
case WINE_GGO_HBGR_BITMAP:
|
|
|
|
case WINE_GGO_VRGB_BITMAP:
|
|
|
|
case WINE_GGO_VBGR_BITMAP:
|
2020-10-21 11:05:28 +02:00
|
|
|
needed = get_subpixel_glyph_bitmap( ft_face->glyph, bbox, format, gdi_font->fake_bold,
|
2019-02-08 02:51:31 +01:00
|
|
|
needsTransform, matrices, &gm, buflen, buf );
|
2008-12-10 15:50:44 +01:00
|
|
|
break;
|
|
|
|
|
2002-01-29 04:02:50 +01:00
|
|
|
case GGO_NATIVE:
|
|
|
|
{
|
2014-10-09 18:00:55 +02:00
|
|
|
FT_Outline *outline = &ft_face->glyph->outline;
|
2002-01-29 04:02:50 +01:00
|
|
|
|
2014-10-09 18:00:55 +02:00
|
|
|
if(buflen == 0) buf = NULL;
|
2002-01-29 04:02:50 +01:00
|
|
|
|
2014-10-09 18:00:55 +02:00
|
|
|
if (needsTransform && buf)
|
2019-02-07 15:43:38 +01:00
|
|
|
pFT_Outline_Transform( outline, &matrices[matrix_vert] );
|
2003-06-23 22:51:06 +02:00
|
|
|
|
2014-10-09 18:00:55 +02:00
|
|
|
needed = get_native_glyph_outline(outline, buflen, NULL);
|
2012-11-13 16:51:29 +01:00
|
|
|
|
2014-10-09 18:00:55 +02:00
|
|
|
if (!buf || !buflen)
|
|
|
|
break;
|
|
|
|
if (needed > buflen)
|
|
|
|
return GDI_ERROR;
|
|
|
|
|
|
|
|
get_native_glyph_outline(outline, buflen, buf);
|
|
|
|
break;
|
2002-01-29 04:02:50 +01:00
|
|
|
}
|
2002-06-22 03:19:29 +02:00
|
|
|
case GGO_BEZIER:
|
|
|
|
{
|
2014-10-09 18:00:55 +02:00
|
|
|
FT_Outline *outline = &ft_face->glyph->outline;
|
|
|
|
if(buflen == 0) buf = NULL;
|
2002-06-22 03:19:29 +02:00
|
|
|
|
2014-10-09 18:00:55 +02:00
|
|
|
if (needsTransform && buf)
|
2019-02-07 15:43:38 +01:00
|
|
|
pFT_Outline_Transform( outline, &matrices[matrix_vert] );
|
2003-06-23 22:51:06 +02:00
|
|
|
|
2014-10-09 18:00:55 +02:00
|
|
|
needed = get_bezier_glyph_outline(outline, buflen, NULL);
|
|
|
|
|
|
|
|
if (!buf || !buflen)
|
|
|
|
break;
|
|
|
|
if (needed > buflen)
|
|
|
|
return GDI_ERROR;
|
|
|
|
|
|
|
|
get_bezier_glyph_outline(outline, buflen, buf);
|
|
|
|
break;
|
2002-06-22 03:19:29 +02:00
|
|
|
}
|
|
|
|
|
2002-01-29 04:02:50 +01:00
|
|
|
default:
|
2001-09-12 22:21:06 +02:00
|
|
|
FIXME("Unsupported format %d\n", format);
|
|
|
|
return GDI_ERROR;
|
|
|
|
}
|
2019-02-08 02:51:31 +01:00
|
|
|
if (needed != GDI_ERROR)
|
|
|
|
*lpgm = gm;
|
|
|
|
|
2002-01-29 04:02:50 +01:00
|
|
|
return needed;
|
2001-09-12 22:21:06 +02:00
|
|
|
}
|
|
|
|
|
2020-10-26 11:43:24 +01:00
|
|
|
/*************************************************************
|
|
|
|
* freetype_set_bitmap_text_metrics
|
|
|
|
*/
|
|
|
|
static BOOL CDECL freetype_set_bitmap_text_metrics( struct gdi_font *font )
|
2004-06-16 22:06:26 +02:00
|
|
|
{
|
2020-10-26 11:43:24 +01:00
|
|
|
FT_Face ft_face = get_font_ptr( font )->ft_face;
|
2004-06-16 22:06:26 +02:00
|
|
|
FT_WinFNT_HeaderRec winfnt_header;
|
|
|
|
|
2020-10-26 11:43:24 +01:00
|
|
|
if (font->otm.otmSize) return TRUE; /* already set */
|
|
|
|
font->otm.otmSize = offsetof( OUTLINETEXTMETRICW, otmFiller );
|
|
|
|
|
|
|
|
#define TM font->otm.otmTextMetrics
|
2011-10-11 11:14:56 +02:00
|
|
|
if(!pFT_Get_WinFNT_Header(ft_face, &winfnt_header))
|
2004-06-16 22:06:26 +02:00
|
|
|
{
|
|
|
|
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;
|
2004-08-09 20:52:51 +02:00
|
|
|
TM.tmDefaultChar = winfnt_header.default_char + winfnt_header.first_char;
|
|
|
|
TM.tmBreakChar = winfnt_header.break_char + winfnt_header.first_char;
|
2004-06-16 22:06:26 +02:00
|
|
|
TM.tmItalic = winfnt_header.italic;
|
|
|
|
TM.tmPitchAndFamily = winfnt_header.pitch_and_family;
|
|
|
|
TM.tmCharSet = winfnt_header.charset;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
TM.tmAscent = ft_face->size->metrics.ascender >> 6;
|
|
|
|
TM.tmDescent = -ft_face->size->metrics.descender >> 6;
|
|
|
|
TM.tmHeight = TM.tmAscent + TM.tmDescent;
|
|
|
|
TM.tmInternalLeading = TM.tmHeight - ft_face->size->metrics.y_ppem;
|
|
|
|
TM.tmExternalLeading = (ft_face->size->metrics.height >> 6) - TM.tmHeight;
|
|
|
|
TM.tmMaxCharWidth = ft_face->size->metrics.max_advance >> 6;
|
|
|
|
TM.tmAveCharWidth = TM.tmMaxCharWidth * 2 / 3; /* FIXME */
|
|
|
|
TM.tmWeight = ft_face->style_flags & FT_STYLE_FLAG_BOLD ? FW_BOLD : FW_NORMAL;
|
|
|
|
TM.tmOverhang = 0;
|
|
|
|
TM.tmDigitizedAspectX = 96; /* FIXME */
|
|
|
|
TM.tmDigitizedAspectY = 96; /* FIXME */
|
|
|
|
TM.tmFirstChar = 1;
|
|
|
|
TM.tmLastChar = 255;
|
|
|
|
TM.tmDefaultChar = 32;
|
|
|
|
TM.tmBreakChar = 32;
|
|
|
|
TM.tmItalic = ft_face->style_flags & FT_STYLE_FLAG_ITALIC ? 1 : 0;
|
|
|
|
/* NB inverted meaning of TMPF_FIXED_PITCH */
|
2016-03-01 05:49:52 +01:00
|
|
|
TM.tmPitchAndFamily = FT_IS_FIXED_WIDTH(ft_face) ? 0 : TMPF_FIXED_PITCH;
|
2020-10-26 11:43:24 +01:00
|
|
|
TM.tmCharSet = font->charset;
|
2004-06-16 22:06:26 +02:00
|
|
|
}
|
2020-10-26 11:43:24 +01:00
|
|
|
TM.tmUnderlined = font->lf.lfUnderline ? 0xff : 0;
|
|
|
|
TM.tmStruckOut = font->lf.lfStrikeOut ? 0xff : 0;
|
2018-02-18 13:34:42 +01:00
|
|
|
|
2020-10-26 11:43:24 +01:00
|
|
|
if(font->fake_bold)
|
2018-02-18 13:34:42 +01:00
|
|
|
TM.tmWeight = FW_BOLD;
|
2004-06-16 22:06:26 +02:00
|
|
|
#undef TM
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2008-01-08 16:02:09 +01:00
|
|
|
|
2009-02-18 11:33:12 +01:00
|
|
|
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;
|
|
|
|
}
|
2002-06-22 03:19:29 +02:00
|
|
|
|
2020-10-26 11:43:24 +01:00
|
|
|
/*************************************************************
|
|
|
|
* freetype_set_outline_text_metrics
|
|
|
|
*/
|
|
|
|
static BOOL CDECL freetype_set_outline_text_metrics( struct gdi_font *font )
|
2001-09-12 22:21:06 +02:00
|
|
|
{
|
2020-10-26 11:43:24 +01:00
|
|
|
FT_Face ft_face = get_font_ptr( font )->ft_face;
|
|
|
|
UINT needed;
|
2001-09-12 22:21:06 +02:00
|
|
|
TT_OS2 *pOS2;
|
|
|
|
TT_HoriHeader *pHori;
|
2002-11-14 00:54:50 +01:00
|
|
|
TT_Postscript *pPost;
|
2013-03-14 12:08:14 +01:00
|
|
|
FT_Fixed em_scale;
|
2003-12-08 22:53:15 +01:00
|
|
|
INT ascent, descent;
|
2015-09-27 21:52:56 +02:00
|
|
|
USHORT windescent;
|
2001-09-12 22:21:06 +02:00
|
|
|
|
2002-01-29 04:02:50 +01:00
|
|
|
TRACE("font=%p\n", font);
|
|
|
|
|
2020-10-26 11:43:24 +01:00
|
|
|
if (!font->scalable) return FALSE;
|
|
|
|
if (font->otm.otmSize) return TRUE; /* already set */
|
2001-09-12 22:21:06 +02:00
|
|
|
|
2020-10-26 11:43:24 +01:00
|
|
|
/* note: we store actual pointers in the names instead of offsets,
|
|
|
|
they are fixed up when returned to the app */
|
|
|
|
if (!(font->otm.otmpFullName = (char *)get_face_name( ft_face, TT_NAME_ID_UNIQUE_ID,
|
|
|
|
GetSystemDefaultLangID() )))
|
2012-09-07 12:40:34 +02:00
|
|
|
{
|
2018-11-30 08:14:04 +01:00
|
|
|
static const WCHAR fake_nameW[] = {'f','a','k','e',' ','n','a','m','e', 0};
|
2020-10-26 11:43:24 +01:00
|
|
|
FIXME("failed to read full_nameW for font %s!\n", wine_dbgstr_w(get_gdi_font_name(font)));
|
|
|
|
font->otm.otmpFullName = (char *)strdupW(fake_nameW);
|
2012-09-07 12:40:34 +02:00
|
|
|
}
|
2020-10-26 11:43:24 +01:00
|
|
|
needed = sizeof(font->otm) + (strlenW( (WCHAR *)font->otm.otmpFamilyName ) + 1 +
|
|
|
|
strlenW( (WCHAR *)font->otm.otmpStyleName ) + 1 +
|
|
|
|
strlenW( (WCHAR *)font->otm.otmpFaceName ) + 1 +
|
|
|
|
strlenW( (WCHAR *)font->otm.otmpFullName ) + 1) * sizeof(WCHAR);
|
2001-09-12 22:21:06 +02:00
|
|
|
|
2020-10-26 11:43:24 +01:00
|
|
|
em_scale = (FT_Fixed)MulDiv(font->ppem, 1 << 16, ft_face->units_per_EM);
|
2001-09-12 22:21:06 +02:00
|
|
|
|
2002-04-03 22:02:39 +02:00
|
|
|
pOS2 = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_os2);
|
2001-09-12 22:21:06 +02:00
|
|
|
if(!pOS2) {
|
|
|
|
FIXME("Can't find OS/2 table - not TT font?\n");
|
2020-10-26 11:43:24 +01:00
|
|
|
return FALSE;
|
2001-09-12 22:21:06 +02:00
|
|
|
}
|
|
|
|
|
2002-04-03 22:02:39 +02:00
|
|
|
pHori = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_hhea);
|
2001-09-12 22:21:06 +02:00
|
|
|
if(!pHori) {
|
|
|
|
FIXME("Can't find HHEA table - not TT font?\n");
|
2020-10-26 11:43:24 +01:00
|
|
|
return FALSE;
|
2001-09-12 22:21:06 +02:00
|
|
|
}
|
|
|
|
|
2002-11-14 00:54:50 +01:00
|
|
|
pPost = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_post); /* we can live with this failing */
|
|
|
|
|
2015-09-27 21:52:56 +02:00
|
|
|
TRACE("OS/2 winA = %u winD = %u typoA = %d typoD = %d typoLG = %d avgW %d FT_Face a = %d, d = %d, h = %d: HORZ a = %d, d = %d lg = %d maxY = %ld minY = %ld\n",
|
2002-06-22 03:19:29 +02:00
|
|
|
pOS2->usWinAscent, pOS2->usWinDescent,
|
|
|
|
pOS2->sTypoAscender, pOS2->sTypoDescender, pOS2->sTypoLineGap,
|
2012-04-24 10:03:35 +02:00
|
|
|
pOS2->xAvgCharWidth,
|
2002-06-22 03:19:29 +02:00
|
|
|
ft_face->ascender, ft_face->descender, ft_face->height,
|
|
|
|
pHori->Ascender, pHori->Descender, pHori->Line_Gap,
|
|
|
|
ft_face->bbox.yMax, ft_face->bbox.yMin);
|
|
|
|
|
2020-10-26 11:43:24 +01:00
|
|
|
font->otm.otmSize = needed;
|
2002-06-22 03:19:29 +02:00
|
|
|
|
2020-10-26 11:43:24 +01:00
|
|
|
#define TM font->otm.otmTextMetrics
|
2002-06-22 03:19:29 +02:00
|
|
|
|
2015-09-27 21:52:56 +02:00
|
|
|
windescent = get_fixed_windescent(pOS2->usWinDescent);
|
|
|
|
if(pOS2->usWinAscent + windescent == 0) {
|
2003-12-08 22:53:15 +01:00
|
|
|
ascent = pHori->Ascender;
|
|
|
|
descent = -pHori->Descender;
|
|
|
|
} else {
|
|
|
|
ascent = pOS2->usWinAscent;
|
2015-09-27 21:52:56 +02:00
|
|
|
descent = windescent;
|
2003-12-08 22:53:15 +01:00
|
|
|
}
|
|
|
|
|
2020-10-26 11:43:24 +01:00
|
|
|
font->ntmCellHeight = ascent + descent;
|
|
|
|
font->ntmAvgWidth = pOS2->xAvgCharWidth;
|
2012-04-24 10:03:35 +02:00
|
|
|
|
2013-03-14 12:08:14 +01:00
|
|
|
#define SCALE_X(x) (pFT_MulFix(x, em_scale))
|
|
|
|
#define SCALE_Y(y) (pFT_MulFix(y, em_scale))
|
2013-03-14 12:08:12 +01:00
|
|
|
|
2020-10-26 11:43:24 +01:00
|
|
|
if(font->yMax) {
|
|
|
|
TM.tmAscent = font->yMax;
|
|
|
|
TM.tmDescent = -font->yMin;
|
2002-06-22 03:19:29 +02:00
|
|
|
TM.tmInternalLeading = (TM.tmAscent + TM.tmDescent) - ft_face->size->metrics.y_ppem;
|
|
|
|
} else {
|
2013-03-14 12:08:12 +01:00
|
|
|
TM.tmAscent = SCALE_Y(ascent);
|
|
|
|
TM.tmDescent = SCALE_Y(descent);
|
|
|
|
TM.tmInternalLeading = SCALE_Y(ascent + descent - ft_face->units_per_EM);
|
2002-06-22 03:19:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
TM.tmHeight = TM.tmAscent + TM.tmDescent;
|
|
|
|
|
|
|
|
/* MSDN says:
|
|
|
|
el = MAX(0, LineGap - ((WinAscent + WinDescent) - (Ascender - Descender)))
|
|
|
|
*/
|
2013-03-14 12:08:12 +01:00
|
|
|
TM.tmExternalLeading = max(0, SCALE_Y(pHori->Line_Gap -
|
|
|
|
((ascent + descent) -
|
|
|
|
(pHori->Ascender - pHori->Descender))));
|
2002-06-22 03:19:29 +02:00
|
|
|
|
2013-03-14 12:08:12 +01:00
|
|
|
TM.tmAveCharWidth = SCALE_X(pOS2->xAvgCharWidth);
|
2003-10-10 02:06:35 +02:00
|
|
|
if (TM.tmAveCharWidth == 0) {
|
|
|
|
TM.tmAveCharWidth = 1;
|
|
|
|
}
|
2013-03-14 12:08:12 +01:00
|
|
|
TM.tmMaxCharWidth = SCALE_X(ft_face->bbox.xMax - ft_face->bbox.xMin);
|
2009-07-31 15:21:51 +02:00
|
|
|
TM.tmWeight = FW_REGULAR;
|
2020-10-26 11:43:24 +01:00
|
|
|
if (font->fake_bold)
|
2009-07-31 15:21:51 +02:00
|
|
|
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;
|
|
|
|
}
|
2002-06-22 03:19:29 +02:00
|
|
|
TM.tmOverhang = 0;
|
2012-05-08 12:29:24 +02:00
|
|
|
TM.tmDigitizedAspectX = 96; /* FIXME */
|
|
|
|
TM.tmDigitizedAspectY = 96; /* FIXME */
|
2007-03-15 07:12:09 +01:00
|
|
|
/* It appears that for fonts with SYMBOL_CHARSET Windows always sets
|
|
|
|
* symbol range to 0 - f0ff
|
|
|
|
*/
|
2009-02-18 11:33:12 +01:00
|
|
|
|
|
|
|
if (face_has_symbol_charmap(ft_face) || (pOS2->usFirstCharIndex >= 0xf000 && pOS2->usFirstCharIndex < 0xf100))
|
2008-06-18 05:35:58 +02:00
|
|
|
{
|
2007-03-15 07:12:09 +01:00
|
|
|
TM.tmFirstChar = 0;
|
2009-02-17 12:49:02 +01:00
|
|
|
switch(GetACP())
|
|
|
|
{
|
2014-05-07 15:51:11 +02:00
|
|
|
case 1255: /* Hebrew */
|
|
|
|
TM.tmLastChar = 0xf896;
|
|
|
|
break;
|
2009-02-17 12:49:02 +01:00
|
|
|
case 1257: /* Baltic */
|
|
|
|
TM.tmLastChar = 0xf8fd;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
TM.tmLastChar = 0xf0ff;
|
|
|
|
}
|
2009-02-16 13:39:15 +01:00
|
|
|
TM.tmBreakChar = 0x20;
|
|
|
|
TM.tmDefaultChar = 0x1f;
|
2008-06-18 05:35:58 +02:00
|
|
|
}
|
2007-03-15 07:12:09 +01:00
|
|
|
else
|
2008-06-18 05:35:58 +02:00
|
|
|
{
|
2009-02-16 13:39:15 +01:00
|
|
|
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;
|
2008-06-18 05:35:58 +02:00
|
|
|
}
|
2020-10-26 11:43:24 +01:00
|
|
|
TM.tmItalic = font->fake_italic ? 255 : ((ft_face->style_flags & FT_STYLE_FLAG_ITALIC) ? 255 : 0);
|
|
|
|
TM.tmUnderlined = font->lf.lfUnderline ? 255 : 0;
|
|
|
|
TM.tmStruckOut = font->lf.lfStrikeOut ? 255 : 0;
|
2002-06-22 03:19:29 +02:00
|
|
|
|
|
|
|
/* Yes TPMF_FIXED_PITCH is correct; braindead api */
|
2005-02-25 14:59:22 +01:00
|
|
|
if(!FT_IS_FIXED_WIDTH(ft_face) &&
|
|
|
|
(pOS2->version == 0xFFFFU ||
|
|
|
|
pOS2->panose[PAN_PROPORTION_INDEX] != PAN_PROP_MONOSPACED))
|
2002-06-22 03:19:29 +02:00
|
|
|
TM.tmPitchAndFamily = TMPF_FIXED_PITCH;
|
|
|
|
else
|
|
|
|
TM.tmPitchAndFamily = 0;
|
|
|
|
|
2009-02-11 14:49:52 +01:00
|
|
|
switch(pOS2->panose[PAN_FAMILYTYPE_INDEX])
|
|
|
|
{
|
2002-06-22 03:19:29 +02:00
|
|
|
case PAN_FAMILY_SCRIPT:
|
|
|
|
TM.tmPitchAndFamily |= FF_SCRIPT;
|
2009-02-11 14:49:52 +01:00
|
|
|
break;
|
|
|
|
|
2002-06-22 03:19:29 +02:00
|
|
|
case PAN_FAMILY_DECORATIVE:
|
|
|
|
TM.tmPitchAndFamily |= FF_DECORATIVE;
|
2009-02-11 14:49:52 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case PAN_ANY:
|
|
|
|
case PAN_NO_FIT:
|
2002-06-22 03:19:29 +02:00
|
|
|
case PAN_FAMILY_TEXT_DISPLAY:
|
2009-02-11 14:49:52 +01:00
|
|
|
case PAN_FAMILY_PICTORIAL: /* symbol fonts get treated as if they were text */
|
|
|
|
/* which is clearly not what the panose spec says. */
|
|
|
|
default:
|
2009-02-19 11:41:34 +01:00
|
|
|
if(TM.tmPitchAndFamily == 0 || /* fixed */
|
|
|
|
pOS2->panose[PAN_PROPORTION_INDEX] == PAN_PROP_MONOSPACED)
|
2002-06-22 03:19:29 +02:00
|
|
|
TM.tmPitchAndFamily = FF_MODERN;
|
2009-02-11 14:49:52 +01:00
|
|
|
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:
|
2009-02-12 13:23:01 +01:00
|
|
|
case PAN_SERIF_FLARED:
|
|
|
|
case PAN_SERIF_ROUNDED:
|
2009-02-11 14:49:52 +01:00
|
|
|
TM.tmPitchAndFamily |= FF_SWISS;
|
|
|
|
break;
|
|
|
|
}
|
2002-06-22 03:19:29 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(FT_IS_SCALABLE(ft_face))
|
|
|
|
TM.tmPitchAndFamily |= TMPF_VECTOR;
|
2007-09-07 23:30:40 +02:00
|
|
|
|
2002-06-22 03:19:29 +02:00
|
|
|
if(FT_IS_SFNT(ft_face))
|
2007-09-07 23:30:40 +02:00
|
|
|
{
|
2020-10-26 11:43:24 +01:00
|
|
|
if (font->ntmFlags & NTM_PS_OPENTYPE)
|
2007-09-07 23:30:40 +02:00
|
|
|
TM.tmPitchAndFamily |= TMPF_DEVICE;
|
|
|
|
else
|
|
|
|
TM.tmPitchAndFamily |= TMPF_TRUETYPE;
|
|
|
|
}
|
2002-06-22 03:19:29 +02:00
|
|
|
|
2020-10-26 11:43:24 +01:00
|
|
|
TM.tmCharSet = font->charset;
|
2002-06-22 03:19:29 +02:00
|
|
|
|
2020-10-26 11:43:24 +01:00
|
|
|
font->otm.otmFiller = 0;
|
|
|
|
memcpy(&font->otm.otmPanoseNumber, pOS2->panose, PANOSE_COUNT);
|
|
|
|
font->otm.otmfsSelection = pOS2->fsSelection;
|
|
|
|
if (font->fake_italic)
|
|
|
|
font->otm.otmfsSelection |= 1;
|
|
|
|
if (font->fake_bold)
|
|
|
|
font->otm.otmfsSelection |= 1 << 5;
|
2017-03-05 11:44:59 +01:00
|
|
|
/* Only return valid bits that define embedding and subsetting restrictions */
|
2020-10-26 11:43:24 +01:00
|
|
|
font->otm.otmfsType = pOS2->fsType & 0x30e;
|
|
|
|
font->otm.otmsCharSlopeRise = pHori->caret_Slope_Rise;
|
|
|
|
font->otm.otmsCharSlopeRun = pHori->caret_Slope_Run;
|
|
|
|
font->otm.otmItalicAngle = 0; /* POST table */
|
|
|
|
font->otm.otmEMSquare = ft_face->units_per_EM;
|
|
|
|
font->otm.otmAscent = SCALE_Y(pOS2->sTypoAscender);
|
|
|
|
font->otm.otmDescent = SCALE_Y(pOS2->sTypoDescender);
|
|
|
|
font->otm.otmLineGap = SCALE_Y(pOS2->sTypoLineGap);
|
|
|
|
font->otm.otmsCapEmHeight = SCALE_Y(pOS2->sCapHeight);
|
|
|
|
font->otm.otmsXHeight = SCALE_Y(pOS2->sxHeight);
|
|
|
|
font->otm.otmrcFontBox.left = SCALE_X(ft_face->bbox.xMin);
|
|
|
|
font->otm.otmrcFontBox.right = SCALE_X(ft_face->bbox.xMax);
|
|
|
|
font->otm.otmrcFontBox.top = SCALE_Y(ft_face->bbox.yMax);
|
|
|
|
font->otm.otmrcFontBox.bottom = SCALE_Y(ft_face->bbox.yMin);
|
|
|
|
font->otm.otmMacAscent = TM.tmAscent;
|
|
|
|
font->otm.otmMacDescent = -TM.tmDescent;
|
|
|
|
font->otm.otmMacLineGap = SCALE_Y(pHori->Line_Gap);
|
|
|
|
font->otm.otmusMinimumPPEM = 0; /* TT Header */
|
|
|
|
font->otm.otmptSubscriptSize.x = SCALE_X(pOS2->ySubscriptXSize);
|
|
|
|
font->otm.otmptSubscriptSize.y = SCALE_Y(pOS2->ySubscriptYSize);
|
|
|
|
font->otm.otmptSubscriptOffset.x = SCALE_X(pOS2->ySubscriptXOffset);
|
|
|
|
font->otm.otmptSubscriptOffset.y = SCALE_Y(pOS2->ySubscriptYOffset);
|
|
|
|
font->otm.otmptSuperscriptSize.x = SCALE_X(pOS2->ySuperscriptXSize);
|
|
|
|
font->otm.otmptSuperscriptSize.y = SCALE_Y(pOS2->ySuperscriptYSize);
|
|
|
|
font->otm.otmptSuperscriptOffset.x = SCALE_X(pOS2->ySuperscriptXOffset);
|
|
|
|
font->otm.otmptSuperscriptOffset.y = SCALE_Y(pOS2->ySuperscriptYOffset);
|
|
|
|
font->otm.otmsStrikeoutSize = SCALE_Y(pOS2->yStrikeoutSize);
|
|
|
|
font->otm.otmsStrikeoutPosition = SCALE_Y(pOS2->yStrikeoutPosition);
|
2002-11-14 00:54:50 +01:00
|
|
|
if(!pPost) {
|
2020-10-26 11:43:24 +01:00
|
|
|
font->otm.otmsUnderscoreSize = 0;
|
|
|
|
font->otm.otmsUnderscorePosition = 0;
|
2002-11-14 00:54:50 +01:00
|
|
|
} else {
|
2020-10-26 11:43:24 +01:00
|
|
|
font->otm.otmsUnderscoreSize = SCALE_Y(pPost->underlineThickness);
|
|
|
|
font->otm.otmsUnderscorePosition = SCALE_Y(pPost->underlinePosition);
|
2002-11-14 00:54:50 +01:00
|
|
|
}
|
2013-03-14 12:08:12 +01:00
|
|
|
#undef SCALE_X
|
|
|
|
#undef SCALE_Y
|
2008-06-24 09:10:47 +02:00
|
|
|
#undef TM
|
2020-10-26 11:43:24 +01:00
|
|
|
return TRUE;
|
2001-09-12 22:21:06 +02:00
|
|
|
}
|
|
|
|
|
2006-09-28 08:11:06 +02:00
|
|
|
static BOOL load_child_font(GdiFont *font, CHILD_FONT *child)
|
2005-09-07 11:21:50 +02:00
|
|
|
{
|
2020-10-21 11:01:50 +02:00
|
|
|
struct gdi_font *gdi_font = font->gdi_font;
|
2020-10-26 11:37:03 +01:00
|
|
|
struct gdi_font *child_font;
|
2018-11-13 10:38:03 +01:00
|
|
|
const struct list *face_list;
|
|
|
|
Face *child_face = NULL, *best_face = NULL;
|
|
|
|
UINT penalty = 0, new_penalty = 0;
|
|
|
|
BOOL bold, italic, bd, it;
|
|
|
|
|
2020-10-21 11:01:50 +02:00
|
|
|
italic = !!gdi_font->lf.lfItalic;
|
|
|
|
bold = gdi_font->lf.lfWeight > FW_MEDIUM;
|
2018-11-13 10:38:03 +01:00
|
|
|
|
|
|
|
face_list = get_face_list_from_family( child->face->family );
|
|
|
|
LIST_FOR_EACH_ENTRY( child_face, face_list, Face, entry )
|
|
|
|
{
|
2019-02-04 23:16:42 +01:00
|
|
|
it = !!(child_face->ntmFlags & NTM_ITALIC);
|
|
|
|
bd = !!(child_face->ntmFlags & NTM_BOLD);
|
2018-11-13 10:38:03 +01:00
|
|
|
new_penalty = ( it ^ italic ) + ( bd ^ bold );
|
|
|
|
if (!best_face || new_penalty < penalty)
|
|
|
|
{
|
|
|
|
penalty = new_penalty;
|
|
|
|
best_face = child_face;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
child_face = best_face ? best_face : child->face;
|
|
|
|
|
2020-10-26 11:37:03 +01:00
|
|
|
child_font = alloc_gdi_font( child_face->file, child_face->font_data_ptr, child_face->font_data_size );
|
|
|
|
child->font = get_font_ptr( child_font );
|
2020-10-22 11:49:33 +02:00
|
|
|
child->font->ft_face = OpenFontFace( child->font, child_face, 0, -gdi_font->ppem );
|
2005-09-07 11:21:50 +02:00
|
|
|
if(!child->font->ft_face)
|
2005-09-14 13:15:05 +02:00
|
|
|
{
|
2020-10-26 11:37:03 +01:00
|
|
|
free_gdi_font(child_font);
|
2005-09-14 13:15:05 +02:00
|
|
|
child->font = NULL;
|
2005-09-07 11:21:50 +02:00
|
|
|
return FALSE;
|
2005-09-14 13:15:05 +02:00
|
|
|
}
|
2005-09-07 11:21:50 +02:00
|
|
|
|
2020-10-26 11:37:03 +01:00
|
|
|
child_font->fake_italic = italic && !( child_face->ntmFlags & NTM_ITALIC );
|
|
|
|
child_font->fake_bold = bold && !( child_face->ntmFlags & NTM_BOLD );
|
|
|
|
child_font->lf = gdi_font->lf;
|
|
|
|
child_font->matrix = gdi_font->matrix;
|
|
|
|
child_font->can_use_bitmap = gdi_font->can_use_bitmap;
|
|
|
|
child_font->ntmFlags = child_face->ntmFlags;
|
|
|
|
child_font->aa_flags = HIWORD( child_face->flags );
|
|
|
|
child_font->scale_y = gdi_font->scale_y;
|
2020-10-26 11:43:24 +01:00
|
|
|
set_gdi_font_names( child_font, child_face->family->family_name,
|
|
|
|
child_face->style_name, child_face->full_name );
|
2005-09-07 11:21:50 +02:00
|
|
|
child->font->base_font = font;
|
2012-12-05 21:13:29 +01:00
|
|
|
TRACE("created child font %p for base %p\n", child->font, font);
|
2005-09-07 11:21:50 +02:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2013-06-04 16:05:50 +02:00
|
|
|
static BOOL get_glyph_index_linked(GdiFont *font, UINT c, GdiFont **linked_font, FT_UInt *glyph, BOOL* vert)
|
2005-09-07 11:21:50 +02:00
|
|
|
{
|
2013-06-04 16:05:50 +02:00
|
|
|
FT_UInt g,o;
|
2005-09-07 11:21:50 +02:00
|
|
|
CHILD_FONT *child_font;
|
|
|
|
|
|
|
|
if(font->base_font)
|
|
|
|
font = font->base_font;
|
|
|
|
|
|
|
|
*linked_font = font;
|
|
|
|
|
|
|
|
if((*glyph = get_glyph_index(font, c)))
|
2012-01-10 11:08:03 +01:00
|
|
|
{
|
2013-06-04 16:05:50 +02:00
|
|
|
o = *glyph;
|
2012-01-10 11:08:03 +01:00
|
|
|
*glyph = get_GSUB_vert_glyph(font, *glyph);
|
2013-06-04 16:05:50 +02:00
|
|
|
*vert = (o != *glyph);
|
2005-09-07 11:21:50 +02:00
|
|
|
return TRUE;
|
2012-01-10 11:08:03 +01:00
|
|
|
}
|
2005-09-07 11:21:50 +02:00
|
|
|
|
2013-07-01 14:13:07 +02:00
|
|
|
if (c < 32) goto done; /* don't check linked fonts for control characters */
|
|
|
|
|
2005-09-07 11:21:50 +02:00
|
|
|
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);
|
2013-06-04 16:05:50 +02:00
|
|
|
o = g;
|
2012-01-10 11:08:03 +01:00
|
|
|
g = get_GSUB_vert_glyph(child_font->font, g);
|
2005-09-07 11:21:50 +02:00
|
|
|
if(g)
|
|
|
|
{
|
|
|
|
*glyph = g;
|
|
|
|
*linked_font = child_font->font;
|
2013-06-04 16:05:50 +02:00
|
|
|
*vert = (o != g);
|
2005-09-07 11:21:50 +02:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
}
|
2013-07-01 14:13:07 +02:00
|
|
|
|
|
|
|
done:
|
2013-06-04 16:05:50 +02:00
|
|
|
*vert = FALSE;
|
2005-09-07 11:21:50 +02:00
|
|
|
return FALSE;
|
|
|
|
}
|
2001-09-12 22:21:06 +02:00
|
|
|
|
2019-04-09 11:32:59 +02:00
|
|
|
/*************************************************************
|
|
|
|
* freetype_GetCharWidthInfo
|
|
|
|
*/
|
2020-10-20 22:05:37 +02:00
|
|
|
static BOOL CDECL freetype_GetCharWidthInfo( struct gdi_font *gdi_font, struct char_width_info *info )
|
2019-04-09 11:32:59 +02:00
|
|
|
{
|
2020-10-20 22:05:37 +02:00
|
|
|
GdiFont *font = get_font_ptr(gdi_font);
|
2019-04-09 11:32:59 +02:00
|
|
|
TT_HoriHeader *pHori;
|
|
|
|
|
2020-10-20 22:05:37 +02:00
|
|
|
TRACE("%p, %p\n", font, info);
|
2019-04-09 11:32:59 +02:00
|
|
|
|
2020-10-21 11:05:28 +02:00
|
|
|
if (gdi_font->scalable &&
|
2020-10-20 22:05:37 +02:00
|
|
|
(pHori = pFT_Get_Sfnt_Table(font->ft_face, ft_sfnt_hhea)))
|
2019-04-09 11:32:59 +02:00
|
|
|
{
|
|
|
|
FT_Fixed em_scale;
|
2020-10-22 11:49:33 +02:00
|
|
|
em_scale = MulDiv(gdi_font->ppem, 1 << 16, font->ft_face->units_per_EM);
|
2019-04-09 11:32:59 +02:00
|
|
|
info->lsb = (SHORT)pFT_MulFix(pHori->min_Left_Side_Bearing, em_scale);
|
|
|
|
info->rsb = (SHORT)pFT_MulFix(pHori->min_Right_Side_Bearing, em_scale);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
info->lsb = info->rsb = 0;
|
|
|
|
|
|
|
|
info->unk = 0;
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2001-10-23 22:06:32 +02:00
|
|
|
|
2007-03-01 20:30:12 +01:00
|
|
|
/* 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;
|
|
|
|
|
2011-10-11 11:14:56 +02:00
|
|
|
if (face->charmap->encoding == FT_ENCODING_UNICODE)
|
2007-03-01 20:30:12 +01:00
|
|
|
{
|
|
|
|
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
|
2017-04-06 13:07:05 +02:00
|
|
|
{
|
|
|
|
DWORD encoding = RtlUlongByteSwap(face->charmap->encoding);
|
|
|
|
FIXME("encoding %s not supported\n", debugstr_an((char *)&encoding, 4));
|
|
|
|
}
|
2007-03-01 20:30:12 +01:00
|
|
|
|
|
|
|
return num_ranges;
|
|
|
|
}
|
|
|
|
|
2011-10-20 16:26:26 +02:00
|
|
|
/*************************************************************
|
|
|
|
* freetype_GetFontUnicodeRanges
|
|
|
|
*/
|
2020-10-20 22:05:37 +02:00
|
|
|
static DWORD CDECL freetype_GetFontUnicodeRanges( struct gdi_font *font, GLYPHSET *glyphset )
|
2007-03-01 20:30:12 +01:00
|
|
|
{
|
2011-10-20 16:26:26 +02:00
|
|
|
DWORD size, num_ranges;
|
2007-03-01 20:30:12 +01:00
|
|
|
|
2020-10-20 22:05:37 +02:00
|
|
|
num_ranges = get_font_unicode_ranges(get_font_ptr(font)->ft_face, glyphset);
|
2007-09-26 20:17:13 +02:00
|
|
|
size = sizeof(GLYPHSET) + sizeof(WCRANGE) * (num_ranges - 1);
|
|
|
|
if (glyphset)
|
2007-03-01 20:30:12 +01:00
|
|
|
{
|
2007-09-26 20:17:13 +02:00
|
|
|
glyphset->cbThis = size;
|
|
|
|
glyphset->cRanges = num_ranges;
|
2010-12-07 12:37:14 +01:00
|
|
|
glyphset->flAccel = 0;
|
2007-03-01 20:30:12 +01:00
|
|
|
}
|
|
|
|
return size;
|
|
|
|
}
|
2005-09-07 15:26:49 +02:00
|
|
|
|
|
|
|
/*************************************************************
|
2011-10-21 11:37:48 +02:00
|
|
|
* freetype_FontIsLinked
|
2005-09-07 15:26:49 +02:00
|
|
|
*/
|
2020-10-20 22:05:37 +02:00
|
|
|
static BOOL CDECL freetype_FontIsLinked( struct gdi_font *font )
|
2005-09-07 15:26:49 +02:00
|
|
|
{
|
2020-10-22 11:54:35 +02:00
|
|
|
return !list_empty( &get_font_ptr(font)->child_fonts );
|
2005-09-07 15:26:49 +02:00
|
|
|
}
|
2005-09-07 11:21:50 +02:00
|
|
|
|
2006-09-20 17:18:51 +02:00
|
|
|
/*************************************************************************
|
|
|
|
* Kerning support for TrueType fonts
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct TT_kern_table
|
|
|
|
{
|
|
|
|
USHORT version;
|
|
|
|
USHORT nTables;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct TT_kern_subtable
|
|
|
|
{
|
|
|
|
USHORT version;
|
|
|
|
USHORT length;
|
|
|
|
union
|
|
|
|
{
|
|
|
|
USHORT word;
|
|
|
|
struct
|
|
|
|
{
|
|
|
|
USHORT horizontal : 1;
|
|
|
|
USHORT minimum : 1;
|
|
|
|
USHORT cross_stream: 1;
|
|
|
|
USHORT override : 1;
|
|
|
|
USHORT reserved1 : 4;
|
|
|
|
USHORT format : 8;
|
|
|
|
} bits;
|
|
|
|
} coverage;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct TT_format0_kern_subtable
|
|
|
|
{
|
|
|
|
USHORT nPairs;
|
|
|
|
USHORT searchRange;
|
|
|
|
USHORT entrySelector;
|
|
|
|
USHORT rangeShift;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct TT_kern_pair
|
|
|
|
{
|
|
|
|
USHORT left;
|
|
|
|
USHORT right;
|
|
|
|
short value;
|
|
|
|
};
|
|
|
|
|
2006-09-28 08:11:06 +02:00
|
|
|
static DWORD parse_format0_kern_subtable(GdiFont *font,
|
2006-09-20 17:18:51 +02:00
|
|
|
const struct TT_format0_kern_subtable *tt_f0_ks,
|
|
|
|
const USHORT *glyph_to_char,
|
|
|
|
KERNINGPAIR *kern_pair, DWORD cPairs)
|
|
|
|
{
|
2020-10-22 11:49:33 +02:00
|
|
|
struct gdi_font *gdi_font = font->gdi_font;
|
2006-09-20 17:18:51 +02:00
|
|
|
USHORT i, nPairs;
|
|
|
|
const struct TT_kern_pair *tt_kern_pair;
|
|
|
|
|
2020-10-22 11:49:33 +02:00
|
|
|
TRACE("font height %d, units_per_EM %d\n", gdi_font->ppem, font->ft_face->units_per_EM);
|
2006-09-20 17:18:51 +02:00
|
|
|
|
|
|
|
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)];
|
2006-09-27 16:43:23 +02:00
|
|
|
/* this algorithm appears to better match what Windows does */
|
2020-10-22 11:49:33 +02:00
|
|
|
kern_pair->iKernAmount = (short)GET_BE_WORD(tt_kern_pair[i].value) * gdi_font->ppem;
|
2006-09-27 16:43:23 +02:00
|
|
|
if (kern_pair->iKernAmount < 0)
|
|
|
|
{
|
|
|
|
kern_pair->iKernAmount -= font->ft_face->units_per_EM / 2;
|
2020-10-22 11:49:33 +02:00
|
|
|
kern_pair->iKernAmount -= gdi_font->ppem;
|
2006-09-27 16:43:23 +02:00
|
|
|
}
|
|
|
|
else if (kern_pair->iKernAmount > 0)
|
|
|
|
{
|
|
|
|
kern_pair->iKernAmount += font->ft_face->units_per_EM / 2;
|
2020-10-22 11:49:33 +02:00
|
|
|
kern_pair->iKernAmount += gdi_font->ppem;
|
2006-09-27 16:43:23 +02:00
|
|
|
}
|
|
|
|
kern_pair->iKernAmount /= font->ft_face->units_per_EM;
|
2006-09-20 17:18:51 +02:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2011-10-20 16:39:53 +02:00
|
|
|
/*************************************************************
|
|
|
|
* freetype_GetKerningPairs
|
|
|
|
*/
|
2020-10-20 22:05:37 +02:00
|
|
|
static DWORD CDECL freetype_GetKerningPairs( struct gdi_font *gdi_font, DWORD cPairs, KERNINGPAIR *kern_pair )
|
2006-09-20 17:18:51 +02:00
|
|
|
{
|
2020-10-20 22:05:37 +02:00
|
|
|
GdiFont *font = get_font_ptr(gdi_font);
|
2006-09-20 17:18:51 +02:00
|
|
|
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;
|
|
|
|
|
|
|
|
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));
|
|
|
|
}
|
2011-10-20 16:39:53 +02:00
|
|
|
else cPairs = font->total_kern_pairs;
|
|
|
|
return cPairs;
|
2006-09-20 17:18:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
font->total_kern_pairs = 0;
|
|
|
|
|
2020-10-22 11:57:48 +02:00
|
|
|
length = freetype_get_font_data(gdi_font, MS_KERN_TAG, 0, NULL, 0);
|
2006-09-20 17:18:51 +02:00
|
|
|
|
|
|
|
if (length == GDI_ERROR)
|
|
|
|
{
|
|
|
|
TRACE("no kerning data in the font\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf = HeapAlloc(GetProcessHeap(), 0, length);
|
2020-10-22 11:54:35 +02:00
|
|
|
if (!buf) return 0;
|
2006-09-20 17:18:51 +02:00
|
|
|
|
2020-10-22 11:57:48 +02:00
|
|
|
freetype_get_font_data(gdi_font, MS_KERN_TAG, 0, buf, length);
|
2006-09-20 17:18:51 +02:00
|
|
|
|
|
|
|
/* build a glyph index to char code map */
|
|
|
|
glyph_to_char = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(USHORT) * 65536);
|
|
|
|
if (!glyph_to_char)
|
|
|
|
{
|
|
|
|
HeapFree(GetProcessHeap(), 0, buf);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-10-11 11:14:56 +02:00
|
|
|
if (font->ft_face->charmap->encoding == FT_ENCODING_UNICODE)
|
2006-09-20 17:18:51 +02:00
|
|
|
{
|
|
|
|
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
|
|
|
|
{
|
2017-04-06 13:07:05 +02:00
|
|
|
DWORD encoding = RtlUlongByteSwap(font->ft_face->charmap->encoding);
|
2006-09-20 17:18:51 +02:00
|
|
|
ULONG n;
|
|
|
|
|
2017-04-06 13:07:05 +02:00
|
|
|
FIXME("encoding %s not supported\n", debugstr_an((char *)&encoding, 4));
|
2006-09-20 17:18:51 +02:00
|
|
|
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));
|
|
|
|
}
|
2011-10-20 16:39:53 +02:00
|
|
|
else cPairs = font->total_kern_pairs;
|
|
|
|
return cPairs;
|
2006-09-20 17:18:51 +02:00
|
|
|
}
|
2005-10-06 14:28:11 +02:00
|
|
|
|
2020-10-20 22:05:37 +02:00
|
|
|
static const struct font_backend_funcs font_funcs =
|
|
|
|
{
|
|
|
|
freetype_EnumFonts,
|
|
|
|
freetype_FontIsLinked,
|
|
|
|
freetype_GetCharWidthInfo,
|
|
|
|
freetype_GetFontUnicodeRanges,
|
|
|
|
freetype_GetKerningPairs,
|
|
|
|
freetype_SelectFont,
|
2020-10-20 22:07:06 +02:00
|
|
|
freetype_AddFontResourceEx,
|
|
|
|
freetype_RemoveFontResourceEx,
|
|
|
|
freetype_AddFontMemResourceEx,
|
|
|
|
freetype_CreateScalableFontResource,
|
2020-10-20 22:05:37 +02:00
|
|
|
freetype_alloc_font,
|
2020-10-22 11:57:48 +02:00
|
|
|
freetype_get_font_data,
|
2020-10-22 13:13:52 +02:00
|
|
|
freetype_get_glyph_index,
|
|
|
|
freetype_get_default_glyph,
|
2020-10-22 11:56:47 +02:00
|
|
|
freetype_get_glyph_outline,
|
2020-10-26 11:43:24 +01:00
|
|
|
freetype_set_outline_text_metrics,
|
|
|
|
freetype_set_bitmap_text_metrics,
|
2020-10-20 22:05:37 +02:00
|
|
|
freetype_destroy_font
|
2011-10-18 11:44:41 +02:00
|
|
|
};
|
|
|
|
|
2001-09-12 22:21:06 +02:00
|
|
|
#else /* HAVE_FREETYPE */
|
|
|
|
|
2006-05-12 00:34:55 +02:00
|
|
|
/*************************************************************************/
|
|
|
|
|
2020-10-20 22:05:37 +02:00
|
|
|
BOOL WineEngInit( const struct font_backend_funcs **funcs )
|
2001-09-12 22:21:06 +02:00
|
|
|
{
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* HAVE_FREETYPE */
|