mirror of
git://source.winehq.org/git/wine.git
synced 2024-09-01 20:33:38 +00:00
![Jacek Caban](/assets/img/avatar_default.png)
Signed-off-by: Jacek Caban <jacek@codeweavers.com> Signed-off-by: Huw Davies <huw@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
6517 lines
216 KiB
C
6517 lines
216 KiB
C
/*
|
|
* GDI font objects
|
|
*
|
|
* Copyright 1993 Alexandre Julliard
|
|
* 1997 Alex Korobka
|
|
* Copyright 2002,2003 Shachar Shemesh
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#include <limits.h>
|
|
#include <stdarg.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include "winerror.h"
|
|
#include "windef.h"
|
|
#include "winbase.h"
|
|
#include "winnls.h"
|
|
#include "winternl.h"
|
|
#include "winreg.h"
|
|
#include "ntgdi_private.h"
|
|
#include "resource.h"
|
|
#include "wine/exception.h"
|
|
#include "wine/heap.h"
|
|
#include "wine/rbtree.h"
|
|
#include "wine/debug.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(font);
|
|
|
|
static HKEY wine_fonts_key;
|
|
static HKEY wine_fonts_cache_key;
|
|
|
|
struct font_physdev
|
|
{
|
|
struct gdi_physdev dev;
|
|
struct gdi_font *font;
|
|
};
|
|
|
|
static inline struct font_physdev *get_font_dev( PHYSDEV dev )
|
|
{
|
|
return (struct font_physdev *)dev;
|
|
}
|
|
|
|
struct gdi_font_family
|
|
{
|
|
struct wine_rb_entry name_entry;
|
|
struct wine_rb_entry second_name_entry;
|
|
unsigned int refcount;
|
|
WCHAR family_name[LF_FACESIZE];
|
|
WCHAR second_name[LF_FACESIZE];
|
|
struct list faces;
|
|
struct gdi_font_family *replacement;
|
|
};
|
|
|
|
struct gdi_font_face
|
|
{
|
|
struct list entry;
|
|
unsigned int refcount;
|
|
WCHAR *style_name;
|
|
WCHAR *full_name;
|
|
WCHAR *file;
|
|
void *data_ptr;
|
|
SIZE_T data_size;
|
|
UINT face_index;
|
|
FONTSIGNATURE fs;
|
|
DWORD ntmFlags;
|
|
DWORD version;
|
|
DWORD flags; /* ADDFONT flags */
|
|
BOOL scalable;
|
|
struct bitmap_font_size size; /* set if face is a bitmap */
|
|
struct gdi_font_family *family;
|
|
struct gdi_font_enum_data *cached_enum_data;
|
|
struct wine_rb_entry full_name_entry;
|
|
};
|
|
|
|
static const struct font_backend_funcs *font_funcs;
|
|
|
|
static const MAT2 identity = { {0,1}, {0,0}, {0,0}, {0,1} };
|
|
|
|
static UINT font_smoothing = GGO_BITMAP;
|
|
static UINT subpixel_orientation = GGO_GRAY4_BITMAP;
|
|
static BOOL antialias_fakes = TRUE;
|
|
static struct font_gamma_ramp font_gamma_ramp;
|
|
|
|
static void add_face_to_cache( struct gdi_font_face *face );
|
|
static void remove_face_from_cache( struct gdi_font_face *face );
|
|
|
|
static inline WCHAR facename_tolower( WCHAR c )
|
|
{
|
|
if (c >= 'A' && c <= 'Z') return c - 'A' + 'a';
|
|
else if (c > 127) return RtlDowncaseUnicodeChar( c );
|
|
else return c;
|
|
}
|
|
|
|
static inline int facename_compare( const WCHAR *str1, const WCHAR *str2, SIZE_T len )
|
|
{
|
|
while (len--)
|
|
{
|
|
WCHAR c1 = facename_tolower( *str1++ ), c2 = facename_tolower( *str2++ );
|
|
if (c1 != c2) return c1 - c2;
|
|
else if (!c1) return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Device -> World size conversion */
|
|
|
|
/* Performs a device to world transformation on the specified width (which
|
|
* is in integer format).
|
|
*/
|
|
static inline INT INTERNAL_XDSTOWS(DC *dc, INT width)
|
|
{
|
|
double floatWidth;
|
|
|
|
/* Perform operation with floating point */
|
|
floatWidth = (double)width * dc->xformVport2World.eM11;
|
|
/* Round to integers */
|
|
return GDI_ROUND(floatWidth);
|
|
}
|
|
|
|
/* Performs a device to world transformation on the specified size (which
|
|
* is in integer format).
|
|
*/
|
|
static inline INT INTERNAL_YDSTOWS(DC *dc, INT height)
|
|
{
|
|
double floatHeight;
|
|
|
|
/* Perform operation with floating point */
|
|
floatHeight = (double)height * dc->xformVport2World.eM22;
|
|
/* Round to integers */
|
|
return GDI_ROUND(floatHeight);
|
|
}
|
|
|
|
/* scale width and height but don't mirror them */
|
|
|
|
static inline INT width_to_LP( DC *dc, INT width )
|
|
{
|
|
return GDI_ROUND( (double)width * fabs( dc->xformVport2World.eM11 ));
|
|
}
|
|
|
|
static inline INT height_to_LP( DC *dc, INT height )
|
|
{
|
|
return GDI_ROUND( (double)height * fabs( dc->xformVport2World.eM22 ));
|
|
}
|
|
|
|
static inline INT INTERNAL_YWSTODS(DC *dc, INT height)
|
|
{
|
|
POINT pt[2];
|
|
pt[0].x = pt[0].y = 0;
|
|
pt[1].x = 0;
|
|
pt[1].y = height;
|
|
lp_to_dp(dc, pt, 2);
|
|
return pt[1].y - pt[0].y;
|
|
}
|
|
|
|
static inline BOOL is_win9x(void)
|
|
{
|
|
return GetVersion() & 0x80000000;
|
|
}
|
|
|
|
static inline WCHAR *strdupW( const WCHAR *p )
|
|
{
|
|
WCHAR *ret;
|
|
DWORD len = (lstrlenW(p) + 1) * sizeof(WCHAR);
|
|
ret = HeapAlloc(GetProcessHeap(), 0, len);
|
|
memcpy(ret, p, len);
|
|
return ret;
|
|
}
|
|
|
|
static INT FONT_GetObjectW( HGDIOBJ handle, INT count, LPVOID buffer );
|
|
static BOOL FONT_DeleteObject( HGDIOBJ handle );
|
|
|
|
static const struct gdi_obj_funcs fontobj_funcs =
|
|
{
|
|
FONT_GetObjectW, /* pGetObjectW */
|
|
NULL, /* pUnrealizeObject */
|
|
FONT_DeleteObject /* pDeleteObject */
|
|
};
|
|
|
|
typedef struct
|
|
{
|
|
struct gdi_obj_header obj;
|
|
LOGFONTW logfont;
|
|
} FONTOBJ;
|
|
|
|
/* for translate_charset_info */
|
|
static const CHARSETINFO charset_info[] = {
|
|
/* ANSI */
|
|
{ ANSI_CHARSET, 1252, {{0,0,0,0},{FS_LATIN1,0}} },
|
|
{ EASTEUROPE_CHARSET, 1250, {{0,0,0,0},{FS_LATIN2,0}} },
|
|
{ RUSSIAN_CHARSET, 1251, {{0,0,0,0},{FS_CYRILLIC,0}} },
|
|
{ GREEK_CHARSET, 1253, {{0,0,0,0},{FS_GREEK,0}} },
|
|
{ TURKISH_CHARSET, 1254, {{0,0,0,0},{FS_TURKISH,0}} },
|
|
{ HEBREW_CHARSET, 1255, {{0,0,0,0},{FS_HEBREW,0}} },
|
|
{ ARABIC_CHARSET, 1256, {{0,0,0,0},{FS_ARABIC,0}} },
|
|
{ BALTIC_CHARSET, 1257, {{0,0,0,0},{FS_BALTIC,0}} },
|
|
{ VIETNAMESE_CHARSET, 1258, {{0,0,0,0},{FS_VIETNAMESE,0}} },
|
|
/* reserved by ANSI */
|
|
{ DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
|
|
{ DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
|
|
{ DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
|
|
{ DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
|
|
{ DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
|
|
{ DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
|
|
{ DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
|
|
/* ANSI and OEM */
|
|
{ THAI_CHARSET, 874, {{0,0,0,0},{FS_THAI,0}} },
|
|
{ SHIFTJIS_CHARSET, 932, {{0,0,0,0},{FS_JISJAPAN,0}} },
|
|
{ GB2312_CHARSET, 936, {{0,0,0,0},{FS_CHINESESIMP,0}} },
|
|
{ HANGEUL_CHARSET, 949, {{0,0,0,0},{FS_WANSUNG,0}} },
|
|
{ CHINESEBIG5_CHARSET, 950, {{0,0,0,0},{FS_CHINESETRAD,0}} },
|
|
{ JOHAB_CHARSET, 1361, {{0,0,0,0},{FS_JOHAB,0}} },
|
|
/* reserved for alternate ANSI and OEM */
|
|
{ DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
|
|
{ DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
|
|
{ DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
|
|
{ DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
|
|
{ DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
|
|
{ DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
|
|
{ DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
|
|
{ DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
|
|
/* reserved for system */
|
|
{ DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
|
|
{ SYMBOL_CHARSET, CP_SYMBOL, {{0,0,0,0},{FS_SYMBOL,0}} }
|
|
};
|
|
|
|
static const WCHAR * const default_serif_list[3] =
|
|
{
|
|
L"Times New Roman",
|
|
L"Liberation Serif",
|
|
L"Bitstream Vera Serif"
|
|
};
|
|
static const WCHAR * const default_fixed_list[3] =
|
|
{
|
|
L"Courier New",
|
|
L"Liberation Mono",
|
|
L"Bitstream Vera Sans Mono"
|
|
};
|
|
static const WCHAR * const default_sans_list[3] =
|
|
{
|
|
L"Arial",
|
|
L"Liberation Sans",
|
|
L"Bitstream Vera Sans"
|
|
};
|
|
static WCHAR ff_roman_default[LF_FACESIZE];
|
|
static WCHAR ff_modern_default[LF_FACESIZE];
|
|
static WCHAR ff_swiss_default[LF_FACESIZE];
|
|
|
|
static const struct nls_update_font_list
|
|
{
|
|
UINT ansi_cp, oem_cp;
|
|
const char *oem, *fixed, *system;
|
|
const char *courier, *serif, *small, *sserif_96, *sserif_120;
|
|
/* these are for font substitutes */
|
|
const char *shelldlg, *tmsrmn;
|
|
const char *fixed_0, *system_0, *courier_0, *serif_0, *small_0, *sserif_0, *helv_0, *tmsrmn_0;
|
|
struct subst { const char *from, *to; } arial_0, courier_new_0, times_new_roman_0;
|
|
} nls_update_font_list[] =
|
|
{
|
|
/* Latin 1 (United States) */
|
|
{ 1252, 437, "vgaoem.fon", "vgafix.fon", "vgasys.fon",
|
|
"coure.fon", "serife.fon", "smalle.fon", "sserife.fon", "sseriff.fon",
|
|
"Tahoma","Times New Roman"
|
|
},
|
|
/* Latin 1 (Multilingual) */
|
|
{ 1252, 850, "vga850.fon", "vgafix.fon", "vgasys.fon",
|
|
"coure.fon", "serife.fon", "smalle.fon", "sserife.fon", "sseriff.fon",
|
|
"Tahoma","Times New Roman" /* FIXME unverified */
|
|
},
|
|
/* Eastern Europe */
|
|
{ 1250, 852, "vga852.fon", "vgafixe.fon", "vgasyse.fon",
|
|
"couree.fon", "serifee.fon", "smallee.fon", "sserifee.fon", "sseriffe.fon",
|
|
"Tahoma","Times New Roman", /* FIXME unverified */
|
|
"Fixedsys,238", "System,238",
|
|
"Courier New,238", "MS Serif,238", "Small Fonts,238",
|
|
"MS Sans Serif,238", "MS Sans Serif,238", "MS Serif,238",
|
|
{ "Arial CE,0", "Arial,238" },
|
|
{ "Courier New CE,0", "Courier New,238" },
|
|
{ "Times New Roman CE,0", "Times New Roman,238" }
|
|
},
|
|
/* Cyrillic */
|
|
{ 1251, 866, "vga866.fon", "vgafixr.fon", "vgasysr.fon",
|
|
"courer.fon", "serifer.fon", "smaller.fon", "sserifer.fon", "sseriffr.fon",
|
|
"Tahoma","Times New Roman", /* FIXME unverified */
|
|
"Fixedsys,204", "System,204",
|
|
"Courier New,204", "MS Serif,204", "Small Fonts,204",
|
|
"MS Sans Serif,204", "MS Sans Serif,204", "MS Serif,204",
|
|
{ "Arial Cyr,0", "Arial,204" },
|
|
{ "Courier New Cyr,0", "Courier New,204" },
|
|
{ "Times New Roman Cyr,0", "Times New Roman,204" }
|
|
},
|
|
/* Greek */
|
|
{ 1253, 737, "vga869.fon", "vgafixg.fon", "vgasysg.fon",
|
|
"coureg.fon", "serifeg.fon", "smalleg.fon", "sserifeg.fon", "sseriffg.fon",
|
|
"Tahoma","Times New Roman", /* FIXME unverified */
|
|
"Fixedsys,161", "System,161",
|
|
"Courier New,161", "MS Serif,161", "Small Fonts,161",
|
|
"MS Sans Serif,161", "MS Sans Serif,161", "MS Serif,161",
|
|
{ "Arial Greek,0", "Arial,161" },
|
|
{ "Courier New Greek,0", "Courier New,161" },
|
|
{ "Times New Roman Greek,0", "Times New Roman,161" }
|
|
},
|
|
/* Turkish */
|
|
{ 1254, 857, "vga857.fon", "vgafixt.fon", "vgasyst.fon",
|
|
"couret.fon", "serifet.fon", "smallet.fon", "sserifet.fon", "sserifft.fon",
|
|
"Tahoma","Times New Roman", /* FIXME unverified */
|
|
"Fixedsys,162", "System,162",
|
|
"Courier New,162", "MS Serif,162", "Small Fonts,162",
|
|
"MS Sans Serif,162", "MS Sans Serif,162", "MS Serif,162",
|
|
{ "Arial Tur,0", "Arial,162" },
|
|
{ "Courier New Tur,0", "Courier New,162" },
|
|
{ "Times New Roman Tur,0", "Times New Roman,162" }
|
|
},
|
|
/* Hebrew */
|
|
{ 1255, 862, "vgaoem.fon", "vgaf1255.fon", "vgas1255.fon",
|
|
"coue1255.fon", "sere1255.fon", "smae1255.fon", "ssee1255.fon", "ssef1255.fon",
|
|
"Tahoma","Times New Roman", /* FIXME unverified */
|
|
"Fixedsys,177", "System,177",
|
|
"Courier New,177", "MS Serif,177", "Small Fonts,177",
|
|
"MS Sans Serif,177", "MS Sans Serif,177", "MS Serif,177"
|
|
},
|
|
/* Arabic */
|
|
{ 1256, 720, "vgaoem.fon", "vgaf1256.fon", "vgas1256.fon",
|
|
"coue1256.fon", "sere1256.fon", "smae1256.fon", "ssee1256.fon", "ssef1256.fon",
|
|
"Microsoft Sans Serif","Times New Roman",
|
|
"Fixedsys,178", "System,178",
|
|
"Courier New,178", "MS Serif,178", "Small Fonts,178",
|
|
"MS Sans Serif,178", "MS Sans Serif,178", "MS Serif,178"
|
|
},
|
|
/* Baltic */
|
|
{ 1257, 775, "vga775.fon", "vgaf1257.fon", "vgas1257.fon",
|
|
"coue1257.fon", "sere1257.fon", "smae1257.fon", "ssee1257.fon", "ssef1257.fon",
|
|
"Tahoma","Times New Roman", /* FIXME unverified */
|
|
"Fixedsys,186", "System,186",
|
|
"Courier New,186", "MS Serif,186", "Small Fonts,186",
|
|
"MS Sans Serif,186", "MS Sans Serif,186", "MS Serif,186",
|
|
{ "Arial Baltic,0", "Arial,186" },
|
|
{ "Courier New Baltic,0", "Courier New,186" },
|
|
{ "Times New Roman Baltic,0", "Times New Roman,186" }
|
|
},
|
|
/* Vietnamese */
|
|
{ 1258, 1258, "vga850.fon", "vgafix.fon", "vgasys.fon",
|
|
"coure.fon", "serife.fon", "smalle.fon", "sserife.fon", "sseriff.fon",
|
|
"Tahoma","Times New Roman" /* FIXME unverified */
|
|
},
|
|
/* Thai */
|
|
{ 874, 874, "vga850.fon", "vgaf874.fon", "vgas874.fon",
|
|
"coure.fon", "serife.fon", "smalle.fon", "ssee874.fon", "ssef874.fon",
|
|
"Tahoma","Times New Roman" /* FIXME unverified */
|
|
},
|
|
/* Japanese */
|
|
{ 932, 932, "vga932.fon", "jvgafix.fon", "jvgasys.fon",
|
|
"coure.fon", "serife.fon", "jsmalle.fon", "sserife.fon", "sseriff.fon",
|
|
"MS UI Gothic","MS Serif"
|
|
},
|
|
/* Chinese Simplified */
|
|
{ 936, 936, "vga936.fon", "svgafix.fon", "svgasys.fon",
|
|
"coure.fon", "serife.fon", "smalle.fon", "sserife.fon", "sseriff.fon",
|
|
"SimSun", "NSimSun"
|
|
},
|
|
/* Korean */
|
|
{ 949, 949, "vga949.fon", "hvgafix.fon", "hvgasys.fon",
|
|
"coure.fon", "serife.fon", "smalle.fon", "sserife.fon", "sseriff.fon",
|
|
"Gulim", "Batang"
|
|
},
|
|
/* Chinese Traditional */
|
|
{ 950, 950, "vga950.fon", "cvgafix.fon", "cvgasys.fon",
|
|
"coure.fon", "serife.fon", "smalle.fon", "sserife.fon", "sseriff.fon",
|
|
"PMingLiU", "MingLiU"
|
|
}
|
|
};
|
|
|
|
static inline BOOL is_dbcs_ansi_cp(UINT ansi_cp)
|
|
{
|
|
return ( ansi_cp == 932 /* CP932 for Japanese */
|
|
|| ansi_cp == 936 /* CP936 for Chinese Simplified */
|
|
|| ansi_cp == 949 /* CP949 for Korean */
|
|
|| ansi_cp == 950 ); /* CP950 for Chinese Traditional */
|
|
}
|
|
|
|
static CRITICAL_SECTION font_cs;
|
|
static CRITICAL_SECTION_DEBUG critsect_debug =
|
|
{
|
|
0, 0, &font_cs,
|
|
{ &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
|
|
0, 0, { (DWORD_PTR)(__FILE__ ": font_cs") }
|
|
};
|
|
static CRITICAL_SECTION font_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
|
|
|
|
#ifndef WINE_FONT_DIR
|
|
#define WINE_FONT_DIR "fonts"
|
|
#endif
|
|
|
|
#ifdef WORDS_BIGENDIAN
|
|
#define GET_BE_WORD(x) (x)
|
|
#define GET_BE_DWORD(x) (x)
|
|
#else
|
|
#define GET_BE_WORD(x) RtlUshortByteSwap(x)
|
|
#define GET_BE_DWORD(x) RtlUlongByteSwap(x)
|
|
#endif
|
|
|
|
static void get_fonts_data_dir_path( const WCHAR *file, WCHAR *path )
|
|
{
|
|
if (GetEnvironmentVariableW( L"WINEDATADIR", path, MAX_PATH ))
|
|
lstrcatW( path, L"\\" WINE_FONT_DIR "\\" );
|
|
else if (GetEnvironmentVariableW( L"WINEBUILDDIR", path, MAX_PATH ))
|
|
lstrcatW( path, L"\\fonts\\" );
|
|
|
|
lstrcatW( path, file );
|
|
if (path[5] == ':') memmove( path, path + 4, (lstrlenW(path) - 3) * sizeof(WCHAR) );
|
|
else path[1] = '\\'; /* change \??\ to \\?\ */
|
|
}
|
|
|
|
static void get_fonts_win_dir_path( const WCHAR *file, WCHAR *path )
|
|
{
|
|
GetWindowsDirectoryW( path, MAX_PATH );
|
|
lstrcatW( path, L"\\fonts\\" );
|
|
lstrcatW( path, file );
|
|
}
|
|
|
|
/* font substitutions */
|
|
|
|
struct gdi_font_subst
|
|
{
|
|
struct list entry;
|
|
int from_charset;
|
|
int to_charset;
|
|
WCHAR names[1];
|
|
};
|
|
|
|
static struct list font_subst_list = LIST_INIT(font_subst_list);
|
|
|
|
static inline WCHAR *get_subst_to_name( struct gdi_font_subst *subst )
|
|
{
|
|
return subst->names + lstrlenW( subst->names ) + 1;
|
|
}
|
|
|
|
static void dump_gdi_font_subst(void)
|
|
{
|
|
struct gdi_font_subst *subst;
|
|
|
|
LIST_FOR_EACH_ENTRY( subst, &font_subst_list, struct gdi_font_subst, entry )
|
|
{
|
|
if (subst->from_charset != -1 || subst->to_charset != -1)
|
|
TRACE("%s,%d -> %s,%d\n", debugstr_w(subst->names),
|
|
subst->from_charset, debugstr_w(get_subst_to_name(subst)), subst->to_charset);
|
|
else
|
|
TRACE("%s -> %s\n", debugstr_w(subst->names), debugstr_w(get_subst_to_name(subst)));
|
|
}
|
|
}
|
|
|
|
static const WCHAR *get_gdi_font_subst( const WCHAR *from_name, int from_charset, int *to_charset )
|
|
{
|
|
struct gdi_font_subst *subst;
|
|
|
|
LIST_FOR_EACH_ENTRY( subst, &font_subst_list, struct gdi_font_subst, entry )
|
|
{
|
|
if (!facename_compare( subst->names, from_name, -1 ) &&
|
|
(subst->from_charset == from_charset || subst->from_charset == -1))
|
|
{
|
|
if (to_charset) *to_charset = subst->to_charset;
|
|
return get_subst_to_name( subst );
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static BOOL add_gdi_font_subst( const WCHAR *from_name, int from_charset, const WCHAR *to_name, int to_charset )
|
|
{
|
|
struct gdi_font_subst *subst;
|
|
int len = lstrlenW( from_name ) + lstrlenW( to_name ) + 2;
|
|
|
|
if (get_gdi_font_subst( from_name, from_charset, NULL )) return FALSE; /* already exists */
|
|
|
|
if (!(subst = HeapAlloc( GetProcessHeap(), 0,
|
|
offsetof( struct gdi_font_subst, names[len] ))))
|
|
return FALSE;
|
|
lstrcpyW( subst->names, from_name );
|
|
lstrcpyW( get_subst_to_name(subst), to_name );
|
|
subst->from_charset = from_charset;
|
|
subst->to_charset = to_charset;
|
|
list_add_tail( &font_subst_list, &subst->entry );
|
|
return TRUE;
|
|
}
|
|
|
|
static void load_gdi_font_subst(void)
|
|
{
|
|
HKEY hkey;
|
|
DWORD i = 0, type, dlen, vlen;
|
|
WCHAR value[64], data[64], *p;
|
|
|
|
if (RegOpenKeyW( HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes",
|
|
&hkey)) return;
|
|
|
|
dlen = sizeof(data);
|
|
vlen = ARRAY_SIZE(value);
|
|
while (!RegEnumValueW( hkey, i++, value, &vlen, NULL, &type, (BYTE *)data, &dlen ))
|
|
{
|
|
int from_charset = -1, to_charset = -1;
|
|
|
|
TRACE("Got %s=%s\n", debugstr_w(value), debugstr_w(data));
|
|
if ((p = wcsrchr( value, ',' )) && p[1])
|
|
{
|
|
*p++ = 0;
|
|
from_charset = wcstol( p, NULL, 10 );
|
|
}
|
|
if ((p = wcsrchr( data, ',' )) && p[1])
|
|
{
|
|
*p++ = 0;
|
|
to_charset = wcstol( p, NULL, 10 );
|
|
}
|
|
|
|
/* Win 2000 doesn't allow mapping between different charsets
|
|
or mapping of DEFAULT_CHARSET */
|
|
if ((!from_charset || to_charset == from_charset) && to_charset != DEFAULT_CHARSET)
|
|
add_gdi_font_subst( value, from_charset, data, to_charset );
|
|
|
|
/* reset dlen and vlen */
|
|
dlen = sizeof(data);
|
|
vlen = ARRAY_SIZE(value);
|
|
}
|
|
RegCloseKey( hkey );
|
|
}
|
|
|
|
/* font families */
|
|
|
|
static int family_namecmp( const WCHAR *str1, const WCHAR *str2 )
|
|
{
|
|
int prio1, prio2, vert1 = (str1[0] == '@' ? 1 : 0), vert2 = (str2[0] == '@' ? 1 : 0);
|
|
|
|
if (!facename_compare( str1, ff_swiss_default, LF_FACESIZE - 1 )) prio1 = 0;
|
|
else if (!facename_compare( str1, ff_modern_default, LF_FACESIZE - 1 )) prio1 = 1;
|
|
else if (!facename_compare( str1, ff_roman_default, LF_FACESIZE - 1 )) prio1 = 2;
|
|
else prio1 = 3;
|
|
|
|
if (!facename_compare( str2, ff_swiss_default, LF_FACESIZE - 1 )) prio2 = 0;
|
|
else if (!facename_compare( str2, ff_modern_default, LF_FACESIZE - 1 )) prio2 = 1;
|
|
else if (!facename_compare( str2, ff_roman_default, LF_FACESIZE - 1 )) prio2 = 2;
|
|
else prio2 = 3;
|
|
|
|
if (prio1 != prio2) return prio1 - prio2;
|
|
if (vert1 != vert2) return vert1 - vert2;
|
|
return facename_compare( str1 + vert1, str2 + vert2, LF_FACESIZE - 1 );
|
|
}
|
|
|
|
static int family_name_compare( const void *key, const struct wine_rb_entry *entry )
|
|
{
|
|
const struct gdi_font_family *family = WINE_RB_ENTRY_VALUE( entry, const struct gdi_font_family, name_entry );
|
|
return family_namecmp( (const WCHAR *)key, family->family_name );
|
|
}
|
|
|
|
static int family_second_name_compare( const void *key, const struct wine_rb_entry *entry )
|
|
{
|
|
const struct gdi_font_family *family = WINE_RB_ENTRY_VALUE( entry, const struct gdi_font_family, second_name_entry );
|
|
return family_namecmp( (const WCHAR *)key, family->second_name );
|
|
}
|
|
|
|
static int face_full_name_compare( const void *key, const struct wine_rb_entry *entry )
|
|
{
|
|
const struct gdi_font_face *face = WINE_RB_ENTRY_VALUE( entry, const struct gdi_font_face, full_name_entry );
|
|
return facename_compare( (const WCHAR *)key, face->full_name, LF_FULLFACESIZE - 1 );
|
|
}
|
|
|
|
static struct wine_rb_tree family_name_tree = { family_name_compare };
|
|
static struct wine_rb_tree family_second_name_tree = { family_second_name_compare };
|
|
static struct wine_rb_tree face_full_name_tree = { face_full_name_compare };
|
|
|
|
static int face_is_in_full_name_tree( const struct gdi_font_face *face )
|
|
{
|
|
return face->full_name_entry.parent || face_full_name_tree.root == &face->full_name_entry;
|
|
}
|
|
|
|
static struct gdi_font_family *create_family( const WCHAR *name, const WCHAR *second_name )
|
|
{
|
|
struct gdi_font_family *family = HeapAlloc( GetProcessHeap(), 0, sizeof(*family) );
|
|
|
|
family->refcount = 1;
|
|
lstrcpynW( family->family_name, name, LF_FACESIZE );
|
|
if (second_name && second_name[0] && wcsicmp( name, second_name ))
|
|
{
|
|
lstrcpynW( family->second_name, second_name, LF_FACESIZE );
|
|
add_gdi_font_subst( second_name, -1, name, -1 );
|
|
}
|
|
else family->second_name[0] = 0;
|
|
list_init( &family->faces );
|
|
family->replacement = NULL;
|
|
wine_rb_put( &family_name_tree, family->family_name, &family->name_entry );
|
|
if (family->second_name[0]) wine_rb_put( &family_second_name_tree, family->second_name, &family->second_name_entry );
|
|
return family;
|
|
}
|
|
|
|
static void release_family( struct gdi_font_family *family )
|
|
{
|
|
if (--family->refcount) return;
|
|
assert( list_empty( &family->faces ));
|
|
wine_rb_remove( &family_name_tree, &family->name_entry );
|
|
if (family->second_name[0]) wine_rb_remove( &family_second_name_tree, &family->second_name_entry );
|
|
if (family->replacement) release_family( family->replacement );
|
|
HeapFree( GetProcessHeap(), 0, family );
|
|
}
|
|
|
|
static struct gdi_font_family *find_family_from_name( const WCHAR *name )
|
|
{
|
|
struct wine_rb_entry *entry;
|
|
if (!(entry = wine_rb_get( &family_name_tree, name ))) return NULL;
|
|
return WINE_RB_ENTRY_VALUE( entry, struct gdi_font_family, name_entry );
|
|
}
|
|
|
|
static struct gdi_font_family *find_family_from_any_name( const WCHAR *name )
|
|
{
|
|
struct wine_rb_entry *entry;
|
|
struct gdi_font_family *family;
|
|
if ((family = find_family_from_name( name ))) return family;
|
|
if (!(entry = wine_rb_get( &family_second_name_tree, name ))) return NULL;
|
|
return WINE_RB_ENTRY_VALUE( entry, struct gdi_font_family, second_name_entry );
|
|
}
|
|
|
|
static struct gdi_font_face *find_face_from_full_name( const WCHAR *full_name )
|
|
{
|
|
struct wine_rb_entry *entry;
|
|
if (!(entry = wine_rb_get( &face_full_name_tree, full_name ))) return NULL;
|
|
return WINE_RB_ENTRY_VALUE( entry, struct gdi_font_face, full_name_entry );
|
|
}
|
|
|
|
static const struct list *get_family_face_list( const struct gdi_font_family *family )
|
|
{
|
|
return family->replacement ? &family->replacement->faces : &family->faces;
|
|
}
|
|
|
|
static struct gdi_font_face *family_find_face_from_filename( struct gdi_font_family *family, const WCHAR *file_name )
|
|
{
|
|
struct gdi_font_face *face;
|
|
const WCHAR *file;
|
|
LIST_FOR_EACH_ENTRY( face, get_family_face_list(family), struct gdi_font_face, entry )
|
|
{
|
|
if (!face->file) continue;
|
|
file = wcsrchr(face->file, '\\');
|
|
if (!file) file = face->file;
|
|
else file++;
|
|
if (wcsicmp( file, file_name )) continue;
|
|
face->refcount++;
|
|
return face;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static struct gdi_font_face *find_face_from_filename( const WCHAR *file_name, const WCHAR *family_name )
|
|
{
|
|
struct gdi_font_family *family;
|
|
struct gdi_font_face *face;
|
|
|
|
TRACE( "looking for file %s name %s\n", debugstr_w(file_name), debugstr_w(family_name) );
|
|
|
|
if (!family_name)
|
|
{
|
|
WINE_RB_FOR_EACH_ENTRY( family, &family_name_tree, struct gdi_font_family, name_entry )
|
|
if ((face = family_find_face_from_filename( family, file_name ))) return face;
|
|
return NULL;
|
|
}
|
|
|
|
if (!(family = find_family_from_name( family_name ))) return NULL;
|
|
return family_find_face_from_filename( family, file_name );
|
|
}
|
|
|
|
static BOOL add_family_replacement( const WCHAR *new_name, const WCHAR *replace )
|
|
{
|
|
struct gdi_font_family *new_family, *family;
|
|
struct gdi_font_face *face;
|
|
WCHAR new_name_vert[LF_FACESIZE], replace_vert[LF_FACESIZE];
|
|
|
|
if (!(family = find_family_from_any_name( replace )))
|
|
{
|
|
TRACE( "%s is not available. Skip this replacement.\n", debugstr_w(replace) );
|
|
return FALSE;
|
|
}
|
|
|
|
if (!(new_family = create_family( new_name, NULL ))) return FALSE;
|
|
new_family->replacement = family;
|
|
family->refcount++;
|
|
TRACE( "mapping %s to %s\n", debugstr_w(replace), debugstr_w(new_name) );
|
|
|
|
/* also add replacement for vertical font if necessary */
|
|
if (replace[0] == '@') return TRUE;
|
|
if (list_empty( &family->faces )) return TRUE;
|
|
face = LIST_ENTRY( list_head(&family->faces), struct gdi_font_face, entry );
|
|
if (!(face->fs.fsCsb[0] & FS_DBCS_MASK)) return TRUE;
|
|
|
|
new_name_vert[0] = '@';
|
|
lstrcpynW( new_name_vert + 1, new_name, LF_FACESIZE - 1 );
|
|
if (find_family_from_any_name( new_name_vert )) return TRUE; /* already exists */
|
|
|
|
replace_vert[0] = '@';
|
|
lstrcpynW( replace_vert + 1, replace, LF_FACESIZE - 1 );
|
|
add_family_replacement( new_name_vert, replace_vert );
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* The replacement list is a way to map an entire font
|
|
* family onto another family. For example adding
|
|
*
|
|
* [HKCU\Software\Wine\Fonts\Replacements]
|
|
* "Wingdings"="Winedings"
|
|
*
|
|
* would enumerate the Winedings font both as Winedings and
|
|
* Wingdings. However if a real Wingdings font is present the
|
|
* replacement does not take place.
|
|
*/
|
|
static void load_gdi_font_replacements(void)
|
|
{
|
|
HKEY hkey;
|
|
DWORD i = 0, type, dlen, vlen;
|
|
WCHAR value[LF_FACESIZE], data[1024];
|
|
|
|
/* @@ Wine registry key: HKCU\Software\Wine\Fonts\Replacements */
|
|
if (RegOpenKeyW( wine_fonts_key, L"Replacements", &hkey )) return;
|
|
|
|
dlen = sizeof(data);
|
|
vlen = ARRAY_SIZE(value);
|
|
while (!RegEnumValueW( hkey, i++, value, &vlen, NULL, &type, (BYTE *)data, &dlen ))
|
|
{
|
|
/* "NewName"="Oldname" */
|
|
if (!find_family_from_any_name( value ))
|
|
{
|
|
if (type == REG_MULTI_SZ)
|
|
{
|
|
WCHAR *replace = data;
|
|
while (*replace)
|
|
{
|
|
if (add_family_replacement( value, replace )) break;
|
|
replace += lstrlenW(replace) + 1;
|
|
}
|
|
}
|
|
else if (type == REG_SZ) add_family_replacement( value, data );
|
|
}
|
|
else TRACE("%s is available. Skip this replacement.\n", debugstr_w(value));
|
|
|
|
/* reset dlen and vlen */
|
|
dlen = sizeof(data);
|
|
vlen = ARRAY_SIZE(value);
|
|
}
|
|
RegCloseKey( hkey );
|
|
}
|
|
|
|
static void dump_gdi_font_list(void)
|
|
{
|
|
struct gdi_font_family *family;
|
|
struct gdi_font_face *face;
|
|
|
|
WINE_RB_FOR_EACH_ENTRY( family, &family_name_tree, struct gdi_font_family, name_entry )
|
|
{
|
|
TRACE( "Family: %s\n", debugstr_w(family->family_name) );
|
|
LIST_FOR_EACH_ENTRY( face, &family->faces, struct gdi_font_face, entry )
|
|
{
|
|
TRACE( "\t%s\t%s\t%08x", debugstr_w(face->style_name), debugstr_w(face->full_name),
|
|
face->fs.fsCsb[0] );
|
|
if (!face->scalable) TRACE(" %d", face->size.height );
|
|
TRACE("\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
static BOOL enum_fallbacks( DWORD pitch_and_family, int index, WCHAR buffer[LF_FACESIZE] )
|
|
{
|
|
if (index < 3)
|
|
{
|
|
const WCHAR * const *defaults;
|
|
|
|
if ((pitch_and_family & FIXED_PITCH) || (pitch_and_family & 0xf0) == FF_MODERN)
|
|
defaults = default_fixed_list;
|
|
else if ((pitch_and_family & 0xf0) == FF_ROMAN)
|
|
defaults = default_serif_list;
|
|
else
|
|
defaults = default_sans_list;
|
|
lstrcpynW( buffer, defaults[index], LF_FACESIZE );
|
|
return TRUE;
|
|
}
|
|
return font_funcs->enum_family_fallbacks( pitch_and_family, index - 3, buffer );
|
|
}
|
|
|
|
static void set_default_family( DWORD pitch_and_family, WCHAR *default_name )
|
|
{
|
|
struct wine_rb_entry *entry;
|
|
WCHAR name[LF_FACESIZE];
|
|
int i = 0;
|
|
|
|
while (enum_fallbacks( pitch_and_family, i++, name ))
|
|
{
|
|
if (!(entry = wine_rb_get( &family_name_tree, name ))) continue;
|
|
wine_rb_remove( &family_name_tree, entry );
|
|
lstrcpynW( default_name, name, LF_FACESIZE - 1 );
|
|
wine_rb_put( &family_name_tree, name, entry );
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void reorder_font_list(void)
|
|
{
|
|
set_default_family( FF_ROMAN, ff_roman_default );
|
|
set_default_family( FF_MODERN, ff_modern_default );
|
|
set_default_family( FF_SWISS, ff_swiss_default );
|
|
}
|
|
|
|
static void release_face( struct gdi_font_face *face )
|
|
{
|
|
if (--face->refcount) return;
|
|
if (face->family)
|
|
{
|
|
if (face->flags & ADDFONT_ADD_TO_CACHE) remove_face_from_cache( face );
|
|
list_remove( &face->entry );
|
|
release_family( face->family );
|
|
}
|
|
if (face_is_in_full_name_tree( face )) wine_rb_remove( &face_full_name_tree, &face->full_name_entry );
|
|
HeapFree( GetProcessHeap(), 0, face->file );
|
|
HeapFree( GetProcessHeap(), 0, face->style_name );
|
|
HeapFree( GetProcessHeap(), 0, face->full_name );
|
|
HeapFree( GetProcessHeap(), 0, face->cached_enum_data );
|
|
HeapFree( GetProcessHeap(), 0, face );
|
|
}
|
|
|
|
static int remove_font( const WCHAR *file, DWORD flags )
|
|
{
|
|
struct gdi_font_family *family, *family_next;
|
|
struct gdi_font_face *face, *face_next;
|
|
int count = 0;
|
|
|
|
EnterCriticalSection( &font_cs );
|
|
WINE_RB_FOR_EACH_ENTRY_DESTRUCTOR( family, family_next, &family_name_tree, struct gdi_font_family, name_entry )
|
|
{
|
|
family->refcount++;
|
|
LIST_FOR_EACH_ENTRY_SAFE( face, face_next, &family->faces, struct gdi_font_face, entry )
|
|
{
|
|
if (!face->file) continue;
|
|
if (LOWORD(face->flags) != LOWORD(flags)) continue;
|
|
if (!wcsicmp( face->file, file ))
|
|
{
|
|
TRACE( "removing matching face %s refcount %d\n", debugstr_w(face->file), face->refcount );
|
|
release_face( face );
|
|
count++;
|
|
}
|
|
}
|
|
release_family( family );
|
|
}
|
|
LeaveCriticalSection( &font_cs );
|
|
return count;
|
|
}
|
|
|
|
static inline BOOL faces_equal( const struct gdi_font_face *f1, const struct gdi_font_face *f2 )
|
|
{
|
|
if (facename_compare( f1->full_name, f2->full_name, -1 )) return FALSE;
|
|
if (f1->scalable) return TRUE;
|
|
if (f1->size.y_ppem != f2->size.y_ppem) return FALSE;
|
|
return !memcmp( &f1->fs, &f2->fs, sizeof(f1->fs) );
|
|
}
|
|
|
|
static inline int style_order( const struct gdi_font_face *face )
|
|
{
|
|
switch (face->ntmFlags & (NTM_REGULAR | NTM_BOLD | NTM_ITALIC))
|
|
{
|
|
case NTM_REGULAR:
|
|
return 0;
|
|
case NTM_BOLD:
|
|
return 1;
|
|
case NTM_ITALIC:
|
|
return 2;
|
|
case NTM_BOLD | NTM_ITALIC:
|
|
return 3;
|
|
default:
|
|
WARN( "Don't know how to order face %s with flags 0x%08x\n",
|
|
debugstr_w(face->full_name), face->ntmFlags );
|
|
return 9999;
|
|
}
|
|
}
|
|
|
|
static BOOL insert_face_in_family_list( struct gdi_font_face *face, struct gdi_font_family *family )
|
|
{
|
|
struct gdi_font_face *cursor;
|
|
|
|
LIST_FOR_EACH_ENTRY( cursor, &family->faces, struct gdi_font_face, entry )
|
|
{
|
|
if (faces_equal( face, cursor ))
|
|
{
|
|
TRACE( "Already loaded face %s in family %s, original version %x, new version %x\n",
|
|
debugstr_w(face->full_name), debugstr_w(family->family_name),
|
|
cursor->version, face->version );
|
|
|
|
if (face->file && cursor->file && !wcsicmp( face->file, cursor->file ))
|
|
{
|
|
cursor->refcount++;
|
|
TRACE("Font %s already in list, refcount now %d\n",
|
|
debugstr_w(face->file), cursor->refcount);
|
|
return FALSE;
|
|
}
|
|
if (face->version <= cursor->version)
|
|
{
|
|
TRACE("Original font %s is newer so skipping %s\n",
|
|
debugstr_w(cursor->file), debugstr_w(face->file));
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
TRACE("Replacing original %s with %s\n",
|
|
debugstr_w(cursor->file), debugstr_w(face->file));
|
|
list_add_before( &cursor->entry, &face->entry );
|
|
face->family = family;
|
|
family->refcount++;
|
|
face->refcount++;
|
|
if (face_is_in_full_name_tree( cursor ))
|
|
{
|
|
wine_rb_replace( &face_full_name_tree, &cursor->full_name_entry, &face->full_name_entry );
|
|
memset( &cursor->full_name_entry, 0, sizeof(cursor->full_name_entry) );
|
|
}
|
|
release_face( cursor );
|
|
return TRUE;
|
|
}
|
|
}
|
|
if (style_order( face ) < style_order( cursor )) break;
|
|
}
|
|
|
|
TRACE( "Adding face %s in family %s from %s\n", debugstr_w(face->full_name),
|
|
debugstr_w(family->family_name), debugstr_w(face->file) );
|
|
list_add_before( &cursor->entry, &face->entry );
|
|
if (face->scalable) wine_rb_put( &face_full_name_tree, face->full_name, &face->full_name_entry );
|
|
face->family = family;
|
|
family->refcount++;
|
|
face->refcount++;
|
|
return TRUE;
|
|
}
|
|
|
|
static struct gdi_font_face *create_face( struct gdi_font_family *family, const WCHAR *style,
|
|
const WCHAR *fullname, const WCHAR *file,
|
|
void *data_ptr, SIZE_T data_size, UINT index, FONTSIGNATURE fs,
|
|
DWORD ntmflags, DWORD version, DWORD flags,
|
|
const struct bitmap_font_size *size )
|
|
{
|
|
struct gdi_font_face *face = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*face) );
|
|
|
|
face->refcount = 1;
|
|
face->style_name = strdupW( style );
|
|
face->full_name = strdupW( fullname );
|
|
face->face_index = index;
|
|
face->fs = fs;
|
|
face->ntmFlags = ntmflags;
|
|
face->version = version;
|
|
face->flags = flags;
|
|
face->data_ptr = data_ptr;
|
|
face->data_size = data_size;
|
|
if (file) face->file = strdupW( file );
|
|
if (size) face->size = *size;
|
|
else face->scalable = TRUE;
|
|
if (insert_face_in_family_list( face, family )) return face;
|
|
release_face( face );
|
|
return NULL;
|
|
}
|
|
|
|
static int CDECL add_gdi_face( const WCHAR *family_name, const WCHAR *second_name,
|
|
const WCHAR *style, const WCHAR *fullname, const WCHAR *file,
|
|
void *data_ptr, SIZE_T data_size, UINT index, FONTSIGNATURE fs,
|
|
DWORD ntmflags, DWORD version, DWORD flags,
|
|
const struct bitmap_font_size *size )
|
|
{
|
|
struct gdi_font_face *face;
|
|
struct gdi_font_family *family;
|
|
int ret = 0;
|
|
|
|
if ((family = find_family_from_name( family_name ))) family->refcount++;
|
|
else if (!(family = create_family( family_name, second_name ))) return ret;
|
|
|
|
if ((face = create_face( family, style, fullname, file, data_ptr, data_size,
|
|
index, fs, ntmflags, version, flags, size )))
|
|
{
|
|
if (flags & ADDFONT_ADD_TO_CACHE) add_face_to_cache( face );
|
|
release_face( face );
|
|
}
|
|
release_family( family );
|
|
ret++;
|
|
|
|
if (fs.fsCsb[0] & FS_DBCS_MASK)
|
|
{
|
|
WCHAR vert_family[LF_FACESIZE], vert_second[LF_FACESIZE], vert_full[LF_FULLFACESIZE];
|
|
|
|
vert_family[0] = '@';
|
|
lstrcpynW( vert_family + 1, family_name, LF_FACESIZE - 1 );
|
|
|
|
if (second_name && second_name[0])
|
|
{
|
|
vert_second[0] = '@';
|
|
lstrcpynW( vert_second + 1, second_name, LF_FACESIZE - 1 );
|
|
}
|
|
else vert_second[0] = 0;
|
|
|
|
if (fullname)
|
|
{
|
|
vert_full[0] = '@';
|
|
lstrcpynW( vert_full + 1, fullname, LF_FULLFACESIZE - 1 );
|
|
fullname = vert_full;
|
|
}
|
|
|
|
if ((family = find_family_from_name( vert_family ))) family->refcount++;
|
|
else if (!(family = create_family( vert_family, vert_second ))) return ret;
|
|
|
|
if ((face = create_face( family, style, fullname, file, data_ptr, data_size,
|
|
index, fs, ntmflags, version, flags | ADDFONT_VERTICAL_FONT, size )))
|
|
{
|
|
if (flags & ADDFONT_ADD_TO_CACHE) add_face_to_cache( face );
|
|
release_face( face );
|
|
}
|
|
release_family( family );
|
|
ret++;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/* font cache */
|
|
|
|
struct cached_face
|
|
{
|
|
DWORD index;
|
|
DWORD flags;
|
|
DWORD ntmflags;
|
|
DWORD version;
|
|
struct bitmap_font_size size;
|
|
FONTSIGNATURE fs;
|
|
WCHAR full_name[1];
|
|
/* WCHAR file_name[]; */
|
|
};
|
|
|
|
static void load_face_from_cache( HKEY hkey_family, struct gdi_font_family *family,
|
|
void *buffer, DWORD buffer_size, BOOL scalable )
|
|
{
|
|
DWORD type, size, needed, index = 0;
|
|
struct gdi_font_face *face;
|
|
HKEY hkey_strike;
|
|
WCHAR name[256];
|
|
struct cached_face *cached = (struct cached_face *)buffer;
|
|
|
|
size = sizeof(name);
|
|
needed = buffer_size - sizeof(DWORD);
|
|
while (!RegEnumValueW( hkey_family, index++, name, &size, NULL, &type, buffer, &needed ))
|
|
{
|
|
if (type == REG_BINARY && needed > sizeof(*cached))
|
|
{
|
|
((DWORD *)buffer)[needed / sizeof(DWORD)] = 0;
|
|
if ((face = create_face( family, name, cached->full_name,
|
|
cached->full_name + lstrlenW(cached->full_name) + 1,
|
|
NULL, 0, cached->index, cached->fs, cached->ntmflags, cached->version,
|
|
cached->flags, scalable ? NULL : &cached->size )))
|
|
{
|
|
if (!scalable)
|
|
TRACE("Adding bitmap size h %d w %d size %d x_ppem %d y_ppem %d\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]);
|
|
|
|
release_face( face );
|
|
}
|
|
}
|
|
size = sizeof(name);
|
|
needed = buffer_size - sizeof(DWORD);
|
|
}
|
|
|
|
/* load bitmap strikes */
|
|
|
|
index = 0;
|
|
needed = buffer_size;
|
|
while (!RegEnumKeyExW( hkey_family, index++, buffer, &needed, NULL, NULL, NULL, NULL ))
|
|
{
|
|
if (!RegOpenKeyExW( hkey_family, buffer, 0, KEY_ALL_ACCESS, &hkey_strike ))
|
|
{
|
|
load_face_from_cache( hkey_strike, family, buffer, buffer_size, FALSE );
|
|
RegCloseKey( hkey_strike );
|
|
}
|
|
needed = buffer_size;
|
|
}
|
|
}
|
|
|
|
static void load_font_list_from_cache(void)
|
|
{
|
|
DWORD size, family_index = 0;
|
|
struct gdi_font_family *family;
|
|
HKEY hkey_family;
|
|
WCHAR buffer[4096], second_name[LF_FACESIZE];
|
|
|
|
size = sizeof(buffer);
|
|
while (!RegEnumKeyExW( wine_fonts_cache_key, family_index++, buffer, &size, NULL, NULL, NULL, NULL ))
|
|
{
|
|
RegOpenKeyExW( wine_fonts_cache_key, buffer, 0, KEY_ALL_ACCESS, &hkey_family );
|
|
TRACE("opened family key %s\n", debugstr_w(buffer));
|
|
size = sizeof(second_name);
|
|
if (RegQueryValueExW( hkey_family, NULL, NULL, NULL, (BYTE *)second_name, &size ))
|
|
second_name[0] = 0;
|
|
|
|
family = create_family( buffer, second_name );
|
|
|
|
load_face_from_cache( hkey_family, family, buffer, sizeof(buffer), TRUE );
|
|
|
|
RegCloseKey( hkey_family );
|
|
release_family( family );
|
|
size = sizeof(buffer);
|
|
}
|
|
}
|
|
|
|
static void add_face_to_cache( struct gdi_font_face *face )
|
|
{
|
|
HKEY hkey_family, hkey_face;
|
|
DWORD len, buffer[1024];
|
|
struct cached_face *cached = (struct cached_face *)buffer;
|
|
|
|
if (RegCreateKeyExW( wine_fonts_cache_key, face->family->family_name, 0, NULL, REG_OPTION_VOLATILE,
|
|
KEY_ALL_ACCESS, NULL, &hkey_family, NULL ))
|
|
return;
|
|
|
|
if (face->family->second_name[0])
|
|
RegSetValueExW( hkey_family, NULL, 0, REG_SZ, (BYTE *)face->family->second_name,
|
|
(lstrlenW( face->family->second_name ) + 1) * sizeof(WCHAR) );
|
|
|
|
if (!face->scalable)
|
|
{
|
|
WCHAR name[10];
|
|
|
|
swprintf( name, ARRAY_SIZE(name), L"%d", 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->version;
|
|
cached->fs = face->fs;
|
|
if (!face->scalable) cached->size = face->size;
|
|
lstrcpyW( cached->full_name, face->full_name );
|
|
len = lstrlenW( face->full_name ) + 1;
|
|
lstrcpyW( cached->full_name + len, face->file );
|
|
len += lstrlenW( 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 );
|
|
RegCloseKey( hkey_family );
|
|
}
|
|
|
|
static void remove_face_from_cache( struct gdi_font_face *face )
|
|
{
|
|
HKEY hkey_family;
|
|
|
|
if (RegOpenKeyExW( wine_fonts_cache_key, face->family->family_name, 0, KEY_ALL_ACCESS, &hkey_family ))
|
|
return;
|
|
|
|
if (!face->scalable)
|
|
{
|
|
WCHAR name[10];
|
|
swprintf( name, ARRAY_SIZE(name), L"%d", face->size.y_ppem );
|
|
RegDeleteKeyW( hkey_family, name );
|
|
}
|
|
else RegDeleteValueW( hkey_family, face->style_name );
|
|
|
|
RegCloseKey( hkey_family );
|
|
}
|
|
|
|
/* font links */
|
|
|
|
struct gdi_font_link
|
|
{
|
|
struct list entry;
|
|
struct list links;
|
|
WCHAR name[LF_FACESIZE];
|
|
FONTSIGNATURE fs;
|
|
};
|
|
|
|
struct gdi_font_link_entry
|
|
{
|
|
struct list entry;
|
|
FONTSIGNATURE fs;
|
|
WCHAR family_name[LF_FACESIZE];
|
|
};
|
|
|
|
static struct list font_links = LIST_INIT(font_links);
|
|
|
|
static struct gdi_font_link *find_gdi_font_link( const WCHAR *name )
|
|
{
|
|
struct gdi_font_link *link;
|
|
|
|
LIST_FOR_EACH_ENTRY( link, &font_links, struct gdi_font_link, entry )
|
|
if (!facename_compare( link->name, name, LF_FACESIZE - 1 )) return link;
|
|
return NULL;
|
|
}
|
|
|
|
static struct gdi_font_family *find_family_from_font_links( const WCHAR *name, const WCHAR *subst,
|
|
FONTSIGNATURE fs )
|
|
{
|
|
struct gdi_font_link *link;
|
|
struct gdi_font_link_entry *entry;
|
|
struct gdi_font_family *family;
|
|
|
|
LIST_FOR_EACH_ENTRY( link, &font_links, struct gdi_font_link, entry )
|
|
{
|
|
if (!facename_compare( link->name, name, LF_FACESIZE - 1 ) ||
|
|
(subst && !facename_compare( link->name, subst, LF_FACESIZE - 1 )))
|
|
{
|
|
TRACE("found entry in system list\n");
|
|
LIST_FOR_EACH_ENTRY( entry, &link->links, struct gdi_font_link_entry, entry )
|
|
{
|
|
const struct gdi_font_link *links;
|
|
|
|
family = find_family_from_name( entry->family_name );
|
|
if (!fs.fsCsb[0]) return family;
|
|
if (fs.fsCsb[0] & entry->fs.fsCsb[0]) return family;
|
|
if ((links = find_gdi_font_link( family->family_name )) && fs.fsCsb[0] & links->fs.fsCsb[0])
|
|
return family;
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static struct gdi_font_link *add_gdi_font_link( const WCHAR *name )
|
|
{
|
|
struct gdi_font_link *link = find_gdi_font_link( name );
|
|
|
|
if (link) return link;
|
|
if ((link = HeapAlloc( GetProcessHeap(), 0, sizeof(*link) )))
|
|
{
|
|
lstrcpynW( link->name, name, LF_FACESIZE );
|
|
memset( &link->fs, 0, sizeof(link->fs) );
|
|
list_init( &link->links );
|
|
list_add_tail( &font_links, &link->entry );
|
|
}
|
|
return link;
|
|
}
|
|
|
|
static void add_gdi_font_link_entry( struct gdi_font_link *link, const WCHAR *family_name, FONTSIGNATURE fs )
|
|
{
|
|
struct gdi_font_link_entry *entry;
|
|
|
|
entry = HeapAlloc( GetProcessHeap(), 0, sizeof(*entry) );
|
|
lstrcpynW( entry->family_name, family_name, LF_FACESIZE );
|
|
entry->fs = fs;
|
|
link->fs.fsCsb[0] |= fs.fsCsb[0];
|
|
link->fs.fsCsb[1] |= fs.fsCsb[1];
|
|
list_add_tail( &link->links, &entry->entry );
|
|
}
|
|
|
|
static const WCHAR * const font_links_list[] =
|
|
{
|
|
L"Lucida Sans Unicode",
|
|
L"Microsoft Sans Serif",
|
|
L"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 */
|
|
{ L"Tahoma", /* FIXME unverified ordering */
|
|
{ L"MS UI Gothic", L"SimSun", L"Gulim", L"PMingLiU", NULL }
|
|
},
|
|
/* Below lists are courtesy of
|
|
* http://blogs.msdn.com/michkap/archive/2005/06/18/430507.aspx
|
|
*/
|
|
/* Japanese */
|
|
{ L"MS UI Gothic",
|
|
{ L"MS UI Gothic", L"PMingLiU", L"SimSun", L"Gulim", NULL }
|
|
},
|
|
/* Chinese Simplified */
|
|
{ L"SimSun",
|
|
{ L"SimSun", L"PMingLiU", L"MS UI Gothic", L"Batang", NULL }
|
|
},
|
|
/* Korean */
|
|
{ L"Gulim",
|
|
{ L"Gulim", L"PMingLiU", L"MS UI Gothic", L"SimSun", NULL }
|
|
},
|
|
/* Chinese Traditional */
|
|
{ L"PMingLiU",
|
|
{ L"PMingLiU", L"SimSun", L"MS UI Gothic", L"Batang", NULL }
|
|
}
|
|
};
|
|
|
|
static void populate_system_links( const WCHAR *name, const WCHAR * const *values )
|
|
{
|
|
struct gdi_font_family *family;
|
|
struct gdi_font_face *face;
|
|
struct gdi_font_link *font_link;
|
|
const WCHAR *file, *value;
|
|
|
|
/* Don't store fonts that are only substitutes for other fonts */
|
|
if (get_gdi_font_subst( name, -1, NULL ))
|
|
{
|
|
TRACE( "%s: Internal SystemLink entry for substituted font, ignoring\n", debugstr_w(name) );
|
|
return;
|
|
}
|
|
font_link = add_gdi_font_link( name );
|
|
for ( ; *values; values++)
|
|
{
|
|
if (!facename_compare( name, *values, -1 )) continue;
|
|
if (!(value = get_gdi_font_subst( *values, -1, NULL ))) value = *values;
|
|
if (!(family = find_family_from_name( value ))) continue;
|
|
/* use first extant filename for this Family */
|
|
LIST_FOR_EACH_ENTRY( face, get_family_face_list(family), struct gdi_font_face, entry )
|
|
{
|
|
if (!face->file) continue;
|
|
file = wcsrchr(face->file, '\\');
|
|
if (!file) file = face->file;
|
|
else file++;
|
|
if ((face = find_face_from_filename( file, value )))
|
|
{
|
|
add_gdi_font_link_entry( font_link, face->family->family_name, face->fs );
|
|
TRACE( "added internal SystemLink for %s to %s in %s\n",
|
|
debugstr_w(name), debugstr_w(value), debugstr_w(file) );
|
|
}
|
|
else TRACE( "Unable to find file %s face name %s\n", debugstr_w(file), debugstr_w(value) );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void load_system_links(void)
|
|
{
|
|
HKEY hkey;
|
|
DWORD i, j;
|
|
const WCHAR *shelldlg_name;
|
|
struct gdi_font_link *font_link, *system_font_link;
|
|
struct gdi_font_face *face;
|
|
|
|
if (!RegOpenKeyW( HKEY_LOCAL_MACHINE,
|
|
L"Software\\Microsoft\\Windows NT\\CurrentVersion\\FontLink\\SystemLink", &hkey ))
|
|
{
|
|
WCHAR value[MAX_PATH], data[1024];
|
|
DWORD type, val_len, data_len;
|
|
WCHAR *entry, *next;
|
|
|
|
val_len = ARRAY_SIZE(value);
|
|
data_len = sizeof(data);
|
|
i = 0;
|
|
while (!RegEnumValueW( hkey, i++, value, &val_len, NULL, &type, (LPBYTE)data, &data_len))
|
|
{
|
|
/* Don't store fonts that are only substitutes for other fonts */
|
|
if (!get_gdi_font_subst( value, -1, NULL ))
|
|
{
|
|
font_link = add_gdi_font_link( value );
|
|
for (entry = data; (char *)entry < (char *)data + data_len && *entry; entry = next)
|
|
{
|
|
const WCHAR *family_name = NULL;
|
|
WCHAR *p;
|
|
|
|
TRACE("%s: %s\n", debugstr_w(value), debugstr_w(entry));
|
|
|
|
next = entry + lstrlenW(entry) + 1;
|
|
if ((p = wcschr( entry, ',' )))
|
|
{
|
|
*p++ = 0;
|
|
while (iswspace(*p)) p++;
|
|
if (!(family_name = get_gdi_font_subst( p, -1, NULL ))) family_name = p;
|
|
}
|
|
if ((face = find_face_from_filename( entry, family_name )))
|
|
{
|
|
add_gdi_font_link_entry( font_link, face->family->family_name, face->fs );
|
|
TRACE("Adding file %s index %u\n", debugstr_w(face->file), face->face_index);
|
|
}
|
|
else TRACE( "Unable to find file %s family %s\n",
|
|
debugstr_w(entry), debugstr_w(family_name) );
|
|
}
|
|
}
|
|
else TRACE("%s: SystemLink entry for substituted font, ignoring\n", debugstr_w(value));
|
|
|
|
val_len = ARRAY_SIZE(value);
|
|
data_len = sizeof(data);
|
|
}
|
|
RegCloseKey( hkey );
|
|
}
|
|
|
|
if ((shelldlg_name = get_gdi_font_subst( L"MS Shell Dlg", -1, NULL )))
|
|
{
|
|
for (i = 0; i < ARRAY_SIZE(font_links_defaults_list); i++)
|
|
{
|
|
const WCHAR *subst = get_gdi_font_subst( font_links_defaults_list[i].shelldlg, -1, NULL );
|
|
|
|
if ((!facename_compare( font_links_defaults_list[i].shelldlg, shelldlg_name, -1 ) ||
|
|
(subst && !facename_compare( subst, shelldlg_name, -1 ))))
|
|
{
|
|
for (j = 0; j < ARRAY_SIZE(font_links_list); j++)
|
|
populate_system_links( font_links_list[j], font_links_defaults_list[i].substitutes );
|
|
if (!facename_compare(shelldlg_name, font_links_defaults_list[i].substitutes[0], -1))
|
|
populate_system_links( shelldlg_name, font_links_defaults_list[i].substitutes );
|
|
}
|
|
}
|
|
}
|
|
else WARN( "could not find FontSubstitute for MS Shell Dlg\n" );
|
|
|
|
/* Explicitly add an entry for the system font, this links to Tahoma and any links
|
|
that Tahoma has */
|
|
|
|
system_font_link = add_gdi_font_link( L"System" );
|
|
if ((face = find_face_from_filename( L"tahoma.ttf", L"Tahoma" )))
|
|
{
|
|
add_gdi_font_link_entry( system_font_link, face->family->family_name, face->fs );
|
|
TRACE("Found Tahoma in %s index %u\n", debugstr_w(face->file), face->face_index);
|
|
}
|
|
if ((font_link = find_gdi_font_link( L"Tahoma" )))
|
|
{
|
|
struct gdi_font_link_entry *entry;
|
|
LIST_FOR_EACH_ENTRY( entry, &font_link->links, struct gdi_font_link_entry, entry )
|
|
add_gdi_font_link_entry( system_font_link, entry->family_name, entry->fs );
|
|
}
|
|
}
|
|
|
|
/* see TranslateCharsetInfo */
|
|
BOOL translate_charset_info( DWORD *src, CHARSETINFO *cs, DWORD flags )
|
|
{
|
|
int index = 0;
|
|
|
|
switch (flags)
|
|
{
|
|
case TCI_SRCFONTSIG:
|
|
while (index < ARRAY_SIZE(charset_info) && !(*src>>index & 0x0001)) index++;
|
|
break;
|
|
case TCI_SRCCODEPAGE:
|
|
while (index < ARRAY_SIZE(charset_info) && PtrToUlong(src) != charset_info[index].ciACP)
|
|
index++;
|
|
break;
|
|
case TCI_SRCCHARSET:
|
|
while (index < ARRAY_SIZE(charset_info) &&
|
|
PtrToUlong(src) != charset_info[index].ciCharset)
|
|
index++;
|
|
break;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
if (index >= ARRAY_SIZE(charset_info) || charset_info[index].ciCharset == DEFAULT_CHARSET) return FALSE;
|
|
*cs = charset_info[index];
|
|
return TRUE;
|
|
}
|
|
|
|
/* font matching */
|
|
|
|
static BOOL can_select_face( const struct gdi_font_face *face, FONTSIGNATURE fs, BOOL can_use_bitmap )
|
|
{
|
|
struct gdi_font_link *font_link;
|
|
|
|
if (!face->scalable && !can_use_bitmap) return FALSE;
|
|
if (!fs.fsCsb[0]) return TRUE;
|
|
if (fs.fsCsb[0] & face->fs.fsCsb[0]) return TRUE;
|
|
if (!(font_link = find_gdi_font_link( face->family->family_name ))) return FALSE;
|
|
if (fs.fsCsb[0] & font_link->fs.fsCsb[0]) return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
static struct gdi_font_face *find_best_matching_face( const struct gdi_font_family *family,
|
|
const LOGFONTW *lf, FONTSIGNATURE fs,
|
|
BOOL can_use_bitmap )
|
|
{
|
|
struct gdi_font_face *face = NULL, *best = NULL, *best_bitmap = NULL;
|
|
unsigned int best_score = 4;
|
|
int best_diff = 0;
|
|
int it = !!lf->lfItalic;
|
|
int bd = lf->lfWeight > 550;
|
|
int height = lf->lfHeight;
|
|
|
|
LIST_FOR_EACH_ENTRY( face, get_family_face_list(family), struct gdi_font_face, entry )
|
|
{
|
|
int italic = !!(face->ntmFlags & NTM_ITALIC);
|
|
int bold = !!(face->ntmFlags & NTM_BOLD);
|
|
int score = (italic ^ it) + (bold ^ bd);
|
|
|
|
if (!can_select_face( face, fs, can_use_bitmap )) continue;
|
|
if (score > best_score) continue;
|
|
TRACE( "(it=%d, bd=%d) is selected for (it=%d, bd=%d)\n", italic, bold, it, bd );
|
|
best_score = score;
|
|
best = face;
|
|
if (best->scalable && best_score == 0) break;
|
|
if (!best->scalable)
|
|
{
|
|
int diff;
|
|
if (height > 0)
|
|
diff = height - (signed int)best->size.height;
|
|
else
|
|
diff = -height - ((signed int)best->size.height - best->size.internal_leading);
|
|
if (!best_bitmap ||
|
|
(best_diff > 0 && diff >= 0 && diff < best_diff) ||
|
|
(best_diff < 0 && diff > best_diff))
|
|
{
|
|
TRACE( "%d is better for %d diff was %d\n", best->size.height, height, best_diff );
|
|
best_diff = diff;
|
|
best_bitmap = best;
|
|
if (best_score == 0 && best_diff == 0) break;
|
|
}
|
|
}
|
|
}
|
|
if (!best) return NULL;
|
|
return best->scalable ? best : best_bitmap;
|
|
}
|
|
|
|
static struct gdi_font_face *find_matching_face_by_name( const WCHAR *name, const WCHAR *subst,
|
|
const LOGFONTW *lf, FONTSIGNATURE fs,
|
|
BOOL can_use_bitmap )
|
|
{
|
|
struct gdi_font_family *family;
|
|
struct gdi_font_face *face;
|
|
|
|
family = find_family_from_any_name( name );
|
|
if (family && (face = find_best_matching_face( family, lf, fs, can_use_bitmap ))) return face;
|
|
if (subst)
|
|
{
|
|
family = find_family_from_any_name( subst );
|
|
if (family && (face = find_best_matching_face( family, lf, fs, can_use_bitmap ))) return face;
|
|
}
|
|
|
|
/* search by full face name */
|
|
WINE_RB_FOR_EACH_ENTRY( family, &family_name_tree, struct gdi_font_family, name_entry )
|
|
LIST_FOR_EACH_ENTRY( face, get_family_face_list(family), struct gdi_font_face, entry )
|
|
if (!facename_compare( face->full_name, name, LF_FACESIZE - 1 ) &&
|
|
can_select_face( face, fs, can_use_bitmap ))
|
|
return face;
|
|
|
|
if ((family = find_family_from_font_links( name, subst, fs )))
|
|
{
|
|
if ((face = find_best_matching_face( family, lf, fs, can_use_bitmap ))) return face;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static struct gdi_font_face *find_any_face( const LOGFONTW *lf, FONTSIGNATURE fs,
|
|
BOOL can_use_bitmap, BOOL want_vertical )
|
|
{
|
|
struct gdi_font_family *family;
|
|
struct gdi_font_face *face;
|
|
WCHAR name[LF_FACESIZE + 1];
|
|
int i = 0;
|
|
|
|
/* first try the family fallbacks */
|
|
while (enum_fallbacks( lf->lfPitchAndFamily, i++, name ))
|
|
{
|
|
if (want_vertical)
|
|
{
|
|
memmove(name + 1, name, min(lstrlenW(name), LF_FACESIZE));
|
|
name[0] = '@';
|
|
}
|
|
|
|
if (!(family = find_family_from_any_name(name))) continue;
|
|
if ((face = find_best_matching_face( family, lf, fs, FALSE ))) return face;
|
|
}
|
|
/* otherwise try only scalable */
|
|
WINE_RB_FOR_EACH_ENTRY( family, &family_name_tree, struct gdi_font_family, name_entry )
|
|
{
|
|
if ((family->family_name[0] == '@') == !want_vertical) continue;
|
|
if ((face = find_best_matching_face( family, lf, fs, FALSE ))) return face;
|
|
}
|
|
if (!can_use_bitmap) return NULL;
|
|
/* then also bitmap fonts */
|
|
WINE_RB_FOR_EACH_ENTRY( family, &family_name_tree, struct gdi_font_family, name_entry )
|
|
{
|
|
if ((family->family_name[0] == '@') == !want_vertical) continue;
|
|
if ((face = find_best_matching_face( family, lf, fs, can_use_bitmap ))) return face;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static struct gdi_font_face *find_matching_face( const LOGFONTW *lf, CHARSETINFO *csi, BOOL can_use_bitmap,
|
|
const WCHAR **orig_name )
|
|
{
|
|
BOOL want_vertical = (lf->lfFaceName[0] == '@');
|
|
struct gdi_font_face *face;
|
|
|
|
if (!translate_charset_info( (DWORD *)(INT_PTR)lf->lfCharSet, csi, TCI_SRCCHARSET ))
|
|
{
|
|
if (lf->lfCharSet != DEFAULT_CHARSET) FIXME( "Untranslated charset %d\n", lf->lfCharSet );
|
|
csi->fs.fsCsb[0] = 0;
|
|
}
|
|
|
|
if (lf->lfFaceName[0])
|
|
{
|
|
int subst_charset;
|
|
const WCHAR *subst = get_gdi_font_subst( lf->lfFaceName, lf->lfCharSet, &subst_charset );
|
|
|
|
if (subst)
|
|
{
|
|
TRACE( "substituting %s,%d -> %s,%d\n", debugstr_w(lf->lfFaceName), lf->lfCharSet,
|
|
debugstr_w(subst), (subst_charset != -1) ? subst_charset : lf->lfCharSet );
|
|
if (subst_charset != -1)
|
|
translate_charset_info( (DWORD *)(INT_PTR)subst_charset, csi, TCI_SRCCHARSET );
|
|
*orig_name = lf->lfFaceName;
|
|
}
|
|
|
|
if ((face = find_matching_face_by_name( lf->lfFaceName, subst, lf, csi->fs, can_use_bitmap )))
|
|
return face;
|
|
}
|
|
*orig_name = NULL; /* substitution is no longer relevant */
|
|
|
|
/* If requested charset was DEFAULT_CHARSET then try using charset
|
|
corresponding to the current ansi codepage */
|
|
if (!csi->fs.fsCsb[0])
|
|
{
|
|
INT acp = GetACP();
|
|
if (!translate_charset_info( (DWORD *)(INT_PTR)acp, csi, TCI_SRCCODEPAGE ))
|
|
{
|
|
FIXME( "TCI failed on codepage %d\n", acp );
|
|
csi->fs.fsCsb[0] = 0;
|
|
}
|
|
}
|
|
|
|
if ((face = find_any_face( lf, csi->fs, can_use_bitmap, want_vertical ))) return face;
|
|
if (csi->fs.fsCsb[0])
|
|
{
|
|
csi->fs.fsCsb[0] = 0;
|
|
if ((face = find_any_face( lf, csi->fs, can_use_bitmap, want_vertical ))) return face;
|
|
}
|
|
if (want_vertical && (face = find_any_face( lf, csi->fs, can_use_bitmap, FALSE ))) return face;
|
|
return NULL;
|
|
}
|
|
|
|
/* realized font objects */
|
|
|
|
#define FIRST_FONT_HANDLE 1
|
|
#define MAX_FONT_HANDLES 256
|
|
|
|
struct font_handle_entry
|
|
{
|
|
struct gdi_font *font;
|
|
WORD generation; /* generation count for reusing handle values */
|
|
};
|
|
|
|
static struct font_handle_entry font_handles[MAX_FONT_HANDLES];
|
|
static struct font_handle_entry *next_free;
|
|
static struct font_handle_entry *next_unused = font_handles;
|
|
|
|
static struct font_handle_entry *handle_entry( DWORD handle )
|
|
{
|
|
unsigned int idx = LOWORD(handle) - FIRST_FONT_HANDLE;
|
|
|
|
if (idx < MAX_FONT_HANDLES)
|
|
{
|
|
if (!HIWORD( handle ) || HIWORD( handle ) == font_handles[idx].generation)
|
|
return &font_handles[idx];
|
|
}
|
|
if (handle) WARN( "invalid handle 0x%08x\n", handle );
|
|
return NULL;
|
|
}
|
|
|
|
static struct gdi_font *get_font_from_handle( DWORD handle )
|
|
{
|
|
struct font_handle_entry *entry = handle_entry( handle );
|
|
|
|
if (entry) return entry->font;
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return NULL;
|
|
}
|
|
|
|
static DWORD alloc_font_handle( struct gdi_font *font )
|
|
{
|
|
struct font_handle_entry *entry;
|
|
|
|
entry = next_free;
|
|
if (entry)
|
|
next_free = (struct font_handle_entry *)entry->font;
|
|
else if (next_unused < font_handles + MAX_FONT_HANDLES)
|
|
entry = next_unused++;
|
|
else
|
|
{
|
|
ERR( "out of realized font handles\n" );
|
|
return 0;
|
|
}
|
|
entry->font = font;
|
|
if (++entry->generation == 0xffff) entry->generation = 1;
|
|
return MAKELONG( entry - font_handles + FIRST_FONT_HANDLE, entry->generation );
|
|
}
|
|
|
|
static void free_font_handle( DWORD handle )
|
|
{
|
|
struct font_handle_entry *entry;
|
|
|
|
if ((entry = handle_entry( handle )))
|
|
{
|
|
entry->font = (struct gdi_font *)next_free;
|
|
next_free = entry;
|
|
}
|
|
}
|
|
|
|
static struct gdi_font *alloc_gdi_font( const WCHAR *file, void *data_ptr, SIZE_T data_size )
|
|
{
|
|
UINT len = file ? lstrlenW(file) : 0;
|
|
struct gdi_font *font = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
|
|
offsetof( struct gdi_font, file[len + 1] ));
|
|
|
|
font->refcount = 1;
|
|
font->matrix.eM11 = font->matrix.eM22 = 1.0;
|
|
font->scale_y = 1;
|
|
font->kern_count = -1;
|
|
list_init( &font->child_fonts );
|
|
|
|
if (file)
|
|
{
|
|
WIN32_FILE_ATTRIBUTE_DATA info;
|
|
if (GetFileAttributesExW( file, GetFileExInfoStandard, &info ))
|
|
{
|
|
font->writetime = info.ftLastWriteTime;
|
|
font->data_size = (LONGLONG)info.nFileSizeHigh << 32 | info.nFileSizeLow;
|
|
memcpy( font->file, file, len * sizeof(WCHAR) );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
font->data_ptr = data_ptr;
|
|
font->data_size = data_size;
|
|
}
|
|
|
|
font->handle = alloc_font_handle( font );
|
|
return font;
|
|
}
|
|
|
|
static void free_gdi_font( struct gdi_font *font )
|
|
{
|
|
DWORD i;
|
|
struct gdi_font *child, *child_next;
|
|
|
|
if (font->private) font_funcs->destroy_font( font );
|
|
free_font_handle( font->handle );
|
|
LIST_FOR_EACH_ENTRY_SAFE( child, child_next, &font->child_fonts, struct gdi_font, entry )
|
|
{
|
|
list_remove( &child->entry );
|
|
free_gdi_font( child );
|
|
}
|
|
for (i = 0; i < font->gm_size; i++) HeapFree( GetProcessHeap(), 0, font->gm[i] );
|
|
HeapFree( GetProcessHeap(), 0, font->otm.otmpFamilyName );
|
|
HeapFree( GetProcessHeap(), 0, font->otm.otmpStyleName );
|
|
HeapFree( GetProcessHeap(), 0, font->otm.otmpFaceName );
|
|
HeapFree( GetProcessHeap(), 0, font->otm.otmpFullName );
|
|
HeapFree( GetProcessHeap(), 0, font->gm );
|
|
HeapFree( GetProcessHeap(), 0, font->kern_pairs );
|
|
HeapFree( GetProcessHeap(), 0, font->gsub_table );
|
|
HeapFree( GetProcessHeap(), 0, font );
|
|
}
|
|
|
|
static inline const WCHAR *get_gdi_font_name( struct gdi_font *font )
|
|
{
|
|
return (WCHAR *)font->otm.otmpFamilyName;
|
|
}
|
|
|
|
static struct gdi_font *create_gdi_font( const struct gdi_font_face *face, const WCHAR *family_name,
|
|
const LOGFONTW *lf )
|
|
{
|
|
struct gdi_font *font;
|
|
|
|
if (!(font = alloc_gdi_font( face->file, face->data_ptr, face->data_size ))) return NULL;
|
|
font->fs = face->fs;
|
|
font->lf = *lf;
|
|
font->fake_italic = (lf->lfItalic && !(face->ntmFlags & NTM_ITALIC));
|
|
font->fake_bold = (lf->lfWeight > 550 && !(face->ntmFlags & NTM_BOLD));
|
|
font->scalable = face->scalable;
|
|
font->face_index = face->face_index;
|
|
font->ntmFlags = face->ntmFlags;
|
|
font->aa_flags = HIWORD( face->flags );
|
|
if (!family_name) family_name = face->family->family_name;
|
|
font->otm.otmpFamilyName = (char *)strdupW( family_name );
|
|
font->otm.otmpStyleName = (char *)strdupW( face->style_name );
|
|
font->otm.otmpFaceName = (char *)strdupW( face->full_name );
|
|
return font;
|
|
}
|
|
|
|
struct glyph_metrics
|
|
{
|
|
GLYPHMETRICS gm;
|
|
ABC abc; /* metrics of the unrotated char */
|
|
BOOL init;
|
|
};
|
|
|
|
#define GM_BLOCK_SIZE 128
|
|
|
|
/* TODO: GGO format support */
|
|
static BOOL get_gdi_font_glyph_metrics( struct gdi_font *font, UINT index, GLYPHMETRICS *gm, ABC *abc )
|
|
{
|
|
UINT block = index / GM_BLOCK_SIZE;
|
|
UINT entry = index % GM_BLOCK_SIZE;
|
|
|
|
if (block < font->gm_size && font->gm[block] && font->gm[block][entry].init)
|
|
{
|
|
*gm = font->gm[block][entry].gm;
|
|
*abc = font->gm[block][entry].abc;
|
|
|
|
TRACE( "cached 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 );
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void set_gdi_font_glyph_metrics( struct gdi_font *font, UINT index,
|
|
const GLYPHMETRICS *gm, const ABC *abc )
|
|
{
|
|
UINT block = index / GM_BLOCK_SIZE;
|
|
UINT entry = index % GM_BLOCK_SIZE;
|
|
|
|
if (block >= font->gm_size)
|
|
{
|
|
struct glyph_metrics **ptr;
|
|
|
|
if (font->gm)
|
|
ptr = HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, font->gm, (block + 1) * sizeof(*ptr) );
|
|
else
|
|
ptr = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, (block + 1) * sizeof(*ptr) );
|
|
if (!ptr) return;
|
|
font->gm_size = block + 1;
|
|
font->gm = ptr;
|
|
}
|
|
if (!font->gm[block])
|
|
{
|
|
font->gm[block] = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(**font->gm) * GM_BLOCK_SIZE );
|
|
if (!font->gm[block]) return;
|
|
}
|
|
font->gm[block][entry].gm = *gm;
|
|
font->gm[block][entry].abc = *abc;
|
|
font->gm[block][entry].init = TRUE;
|
|
}
|
|
|
|
|
|
/* GSUB table support */
|
|
|
|
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;
|
|
|
|
static GSUB_Script *GSUB_get_script_table( GSUB_Header *header, const char *tag )
|
|
{
|
|
GSUB_ScriptList *script;
|
|
GSUB_Script *deflt = NULL;
|
|
int i;
|
|
|
|
script = (GSUB_ScriptList *)((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++)
|
|
{
|
|
int offset = GET_BE_WORD(script->ScriptRecord[i].Script);
|
|
GSUB_Script *scr = (GSUB_Script *)((BYTE *)script + offset);
|
|
if (!memcmp( script->ScriptRecord[i].ScriptTag, tag, 4 )) return scr;
|
|
if (!memcmp( script->ScriptRecord[i].ScriptTag, "dflt", 4 )) deflt = scr;
|
|
}
|
|
return deflt;
|
|
}
|
|
|
|
static GSUB_LangSys *GSUB_get_lang_table( GSUB_Script *script, const char *tag )
|
|
{
|
|
int i, offset;
|
|
GSUB_LangSys *lang;
|
|
|
|
TRACE("Deflang %x, LangCount %i\n",GET_BE_WORD(script->DefaultLangSys), GET_BE_WORD(script->LangSysCount));
|
|
|
|
for (i = 0; i < GET_BE_WORD(script->LangSysCount) ; i++)
|
|
{
|
|
offset = GET_BE_WORD(script->LangSysRecord[i].LangSys);
|
|
lang = (GSUB_LangSys *)((BYTE *)script + offset);
|
|
if (!memcmp( script->LangSysRecord[i].LangSysTag, tag, 4 )) return lang;
|
|
}
|
|
offset = GET_BE_WORD(script->DefaultLangSys);
|
|
if (offset) return (GSUB_LangSys *)((BYTE *)script + offset);
|
|
return NULL;
|
|
}
|
|
|
|
static GSUB_Feature *GSUB_get_feature( GSUB_Header *header, GSUB_LangSys *lang, const char *tag )
|
|
{
|
|
int i;
|
|
const GSUB_FeatureList *feature;
|
|
|
|
feature = (GSUB_FeatureList *)((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 (!memcmp( feature->FeatureRecord[index].FeatureTag, tag, 4 ))
|
|
return (GSUB_Feature *)((BYTE *)feature + GET_BE_WORD(feature->FeatureRecord[index].Feature));
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static const char *get_opentype_script( const struct gdi_font *font )
|
|
{
|
|
/*
|
|
* I am not sure if this is the correct way to generate our script tag
|
|
*/
|
|
switch (font->charset)
|
|
{
|
|
case ANSI_CHARSET: return "latn";
|
|
case BALTIC_CHARSET: return "latn"; /* ?? */
|
|
case CHINESEBIG5_CHARSET: return "hani";
|
|
case EASTEUROPE_CHARSET: return "latn"; /* ?? */
|
|
case GB2312_CHARSET: return "hani";
|
|
case GREEK_CHARSET: return "grek";
|
|
case HANGUL_CHARSET: return "hang";
|
|
case RUSSIAN_CHARSET: return "cyrl";
|
|
case SHIFTJIS_CHARSET: return "kana";
|
|
case TURKISH_CHARSET: return "latn"; /* ?? */
|
|
case VIETNAMESE_CHARSET: return "latn";
|
|
case JOHAB_CHARSET: return "latn"; /* ?? */
|
|
case ARABIC_CHARSET: return "arab";
|
|
case HEBREW_CHARSET: return "hebr";
|
|
case THAI_CHARSET: return "thai";
|
|
default: return "latn";
|
|
}
|
|
}
|
|
|
|
static void *get_GSUB_vert_feature( struct gdi_font *font )
|
|
{
|
|
GSUB_Header *header;
|
|
GSUB_Script *script;
|
|
GSUB_LangSys *language;
|
|
GSUB_Feature *feature;
|
|
DWORD length = font_funcs->get_font_data( font, MS_GSUB_TAG, 0, NULL, 0 );
|
|
|
|
if (length == GDI_ERROR) return NULL;
|
|
|
|
header = HeapAlloc( GetProcessHeap(), 0, length );
|
|
font_funcs->get_font_data( font, MS_GSUB_TAG, 0, header, length );
|
|
TRACE( "Loaded GSUB table of %i bytes\n", length );
|
|
|
|
if ((script = GSUB_get_script_table( header, get_opentype_script(font) )))
|
|
{
|
|
if ((language = GSUB_get_lang_table( script, "xxxx" ))) /* Need to get Lang tag */
|
|
{
|
|
feature = GSUB_get_feature( header, language, "vrt2" );
|
|
if (!feature) feature = GSUB_get_feature( header, language, "vert" );
|
|
if (feature)
|
|
{
|
|
font->gsub_table = header;
|
|
return feature;
|
|
}
|
|
TRACE("vrt2/vert feature not found\n");
|
|
}
|
|
else TRACE("Language not found\n");
|
|
}
|
|
else TRACE("Script not found\n");
|
|
|
|
HeapFree( GetProcessHeap(), 0, header );
|
|
return NULL;
|
|
}
|
|
|
|
static int GSUB_is_glyph_covered( void *table, UINT glyph )
|
|
{
|
|
GSUB_CoverageFormat1 *cf1 = table;
|
|
|
|
if (GET_BE_WORD(cf1->CoverageFormat) == 1)
|
|
{
|
|
int i, count = GET_BE_WORD(cf1->GlyphCount);
|
|
|
|
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)
|
|
{
|
|
int i, count;
|
|
GSUB_CoverageFormat2 *cf2 = table;
|
|
|
|
count = GET_BE_WORD(cf2->RangeCount);
|
|
TRACE("Coverage Format 2, %i ranges\n",count);
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
if (glyph < GET_BE_WORD(cf2->RangeRecord[i].Start)) return -1;
|
|
if ((glyph >= GET_BE_WORD(cf2->RangeRecord[i].Start)) &&
|
|
(glyph <= GET_BE_WORD(cf2->RangeRecord[i].End)))
|
|
{
|
|
return (GET_BE_WORD(cf2->RangeRecord[i].StartCoverageIndex) +
|
|
glyph - GET_BE_WORD(cf2->RangeRecord[i].Start));
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
else ERR("Unknown CoverageFormat %i\n",GET_BE_WORD(cf1->CoverageFormat));
|
|
|
|
return -1;
|
|
}
|
|
|
|
static UINT GSUB_apply_feature( GSUB_Header *header, GSUB_Feature *feature, UINT glyph )
|
|
{
|
|
GSUB_LookupList *lookup = (GSUB_LookupList *)((BYTE *)header + GET_BE_WORD(header->LookupList));
|
|
int i, j, offset;
|
|
|
|
TRACE("%i lookups\n", GET_BE_WORD(feature->LookupCount));
|
|
for (i = 0; i < GET_BE_WORD(feature->LookupCount); i++)
|
|
{
|
|
GSUB_LookupTable *look;
|
|
offset = GET_BE_WORD(lookup->Lookup[GET_BE_WORD(feature->LookupListIndex[i])]);
|
|
look = (GSUB_LookupTable *)((BYTE *)lookup + offset);
|
|
TRACE("type %i, flag %x, subtables %i\n",
|
|
GET_BE_WORD(look->LookupType),GET_BE_WORD(look->LookupFlag),GET_BE_WORD(look->SubTableCount));
|
|
if (GET_BE_WORD(look->LookupType) == 1)
|
|
{
|
|
for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
|
|
{
|
|
GSUB_SingleSubstFormat1 *ssf1;
|
|
offset = GET_BE_WORD(look->SubTable[j]);
|
|
ssf1 = (GSUB_SingleSubstFormat1 *)((BYTE *)look + offset);
|
|
if (GET_BE_WORD(ssf1->SubstFormat) == 1)
|
|
{
|
|
int offset = GET_BE_WORD(ssf1->Coverage);
|
|
TRACE(" subtype 1, delta %i\n", GET_BE_WORD(ssf1->DeltaGlyphID));
|
|
if (GSUB_is_glyph_covered( (BYTE *) ssf1 + offset, glyph ) != -1)
|
|
{
|
|
TRACE(" Glyph 0x%x ->",glyph);
|
|
glyph += GET_BE_WORD(ssf1->DeltaGlyphID);
|
|
TRACE(" 0x%x\n",glyph);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
GSUB_SingleSubstFormat2 *ssf2;
|
|
int index, offset;
|
|
|
|
ssf2 = (GSUB_SingleSubstFormat2 *)ssf1;
|
|
offset = GET_BE_WORD(ssf1->Coverage);
|
|
TRACE(" subtype 2, glyph count %i\n", GET_BE_WORD(ssf2->GlyphCount));
|
|
index = GSUB_is_glyph_covered( (BYTE *)ssf2 + offset, glyph );
|
|
TRACE(" Coverage index %i\n",index);
|
|
if (index != -1)
|
|
{
|
|
TRACE(" Glyph is 0x%x ->",glyph);
|
|
glyph = GET_BE_WORD(ssf2->Substitute[index]);
|
|
TRACE("0x%x\n",glyph);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else FIXME("We only handle SubType 1\n");
|
|
}
|
|
return glyph;
|
|
}
|
|
|
|
static UINT get_GSUB_vert_glyph( struct gdi_font *font, UINT glyph )
|
|
{
|
|
if (!glyph) return glyph;
|
|
if (!font->gsub_table) return glyph;
|
|
return GSUB_apply_feature( font->gsub_table, font->vert_feature, glyph );
|
|
}
|
|
|
|
static void add_child_font( struct gdi_font *font, const WCHAR *family_name )
|
|
{
|
|
FONTSIGNATURE fs = {{0}};
|
|
struct gdi_font *child;
|
|
struct gdi_font_face *face;
|
|
|
|
if (!(face = find_matching_face_by_name( family_name, NULL, &font->lf, fs, FALSE ))) return;
|
|
|
|
if (!(child = create_gdi_font( face, family_name, &font->lf ))) return;
|
|
child->matrix = font->matrix;
|
|
child->can_use_bitmap = font->can_use_bitmap;
|
|
child->scale_y = font->scale_y;
|
|
child->aveWidth = font->aveWidth;
|
|
child->charset = font->charset;
|
|
child->codepage = font->codepage;
|
|
child->base_font = font;
|
|
list_add_tail( &font->child_fonts, &child->entry );
|
|
TRACE( "created child font %p for base %p\n", child, font );
|
|
}
|
|
|
|
static void create_child_font_list( struct gdi_font *font )
|
|
{
|
|
struct gdi_font_link *font_link;
|
|
struct gdi_font_link_entry *entry;
|
|
const WCHAR* font_name;
|
|
|
|
if (!(font_name = get_gdi_font_subst( get_gdi_font_name(font), -1, NULL )))
|
|
font_name = get_gdi_font_name( font );
|
|
|
|
if ((font_link = find_gdi_font_link( font_name )))
|
|
{
|
|
TRACE("found entry in system list\n");
|
|
LIST_FOR_EACH_ENTRY( entry, &font_link->links, struct gdi_font_link_entry, entry )
|
|
add_child_font( font, entry->family_name );
|
|
}
|
|
/*
|
|
* if not SYMBOL or OEM then we also get all the fonts for Microsoft
|
|
* Sans Serif. This is how asian windows get default fallbacks for fonts
|
|
*/
|
|
if (is_dbcs_ansi_cp(GetACP()) && font->charset != SYMBOL_CHARSET && font->charset != OEM_CHARSET &&
|
|
facename_compare( font_name, L"Microsoft Sans Serif", -1 ) != 0)
|
|
{
|
|
if ((font_link = find_gdi_font_link( L"Microsoft Sans Serif" )))
|
|
{
|
|
TRACE("found entry in default fallback list\n");
|
|
LIST_FOR_EACH_ENTRY( entry, &font_link->links, struct gdi_font_link_entry, entry )
|
|
add_child_font( font, entry->family_name );
|
|
}
|
|
}
|
|
}
|
|
|
|
/* font cache */
|
|
|
|
static struct list gdi_font_list = LIST_INIT( gdi_font_list );
|
|
static struct list unused_gdi_font_list = LIST_INIT( unused_gdi_font_list );
|
|
static unsigned int unused_font_count;
|
|
#define UNUSED_CACHE_SIZE 10
|
|
|
|
static BOOL fontcmp( const struct gdi_font *font, DWORD hash, const LOGFONTW *lf,
|
|
const FMAT2 *matrix, BOOL can_use_bitmap )
|
|
{
|
|
if (font->hash != hash) return TRUE;
|
|
if (memcmp( &font->matrix, matrix, sizeof(*matrix))) return TRUE;
|
|
if (memcmp( &font->lf, lf, offsetof(LOGFONTW, lfFaceName))) return TRUE;
|
|
if (!font->can_use_bitmap != !can_use_bitmap) return TRUE;
|
|
return facename_compare( font->lf.lfFaceName, lf->lfFaceName, -1 );
|
|
}
|
|
|
|
static DWORD hash_font( const LOGFONTW *lf, const FMAT2 *matrix, BOOL can_use_bitmap )
|
|
{
|
|
DWORD hash = 0, *ptr, two_chars;
|
|
WORD *pwc;
|
|
unsigned int i;
|
|
|
|
for (i = 0, ptr = (DWORD *)matrix; i < sizeof(*matrix) / sizeof(DWORD); i++, ptr++)
|
|
hash ^= *ptr;
|
|
for(i = 0, ptr = (DWORD *)lf; i < 7; i++, ptr++)
|
|
hash ^= *ptr;
|
|
for(i = 0, ptr = (DWORD *)lf->lfFaceName; i < LF_FACESIZE/2; i++, ptr++)
|
|
{
|
|
two_chars = *ptr;
|
|
pwc = (WCHAR *)&two_chars;
|
|
if(!*pwc) break;
|
|
*pwc = towupper(*pwc);
|
|
pwc++;
|
|
*pwc = towupper(*pwc);
|
|
hash ^= two_chars;
|
|
if(!*pwc) break;
|
|
}
|
|
hash ^= !can_use_bitmap;
|
|
return hash;
|
|
}
|
|
|
|
static void cache_gdi_font( struct gdi_font *font )
|
|
{
|
|
static DWORD cache_num = 1;
|
|
|
|
font->cache_num = cache_num++;
|
|
font->hash = hash_font( &font->lf, &font->matrix, font->can_use_bitmap );
|
|
list_add_head( &gdi_font_list, &font->entry );
|
|
TRACE( "font %p\n", font );
|
|
}
|
|
|
|
static struct gdi_font *find_cached_gdi_font( const LOGFONTW *lf, const FMAT2 *matrix, BOOL can_use_bitmap )
|
|
{
|
|
struct gdi_font *font;
|
|
DWORD hash = hash_font( lf, matrix, can_use_bitmap );
|
|
|
|
/* try the in-use list */
|
|
LIST_FOR_EACH_ENTRY( font, &gdi_font_list, struct gdi_font, entry )
|
|
{
|
|
if (fontcmp( font, hash, lf, matrix, can_use_bitmap )) continue;
|
|
list_remove( &font->entry );
|
|
list_add_head( &gdi_font_list, &font->entry );
|
|
if (!font->refcount++)
|
|
{
|
|
list_remove( &font->unused_entry );
|
|
unused_font_count--;
|
|
}
|
|
return font;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void release_gdi_font( struct gdi_font *font )
|
|
{
|
|
if (!font) return;
|
|
if (--font->refcount) return;
|
|
|
|
TRACE( "font %p\n", font );
|
|
|
|
/* add it to the unused list */
|
|
EnterCriticalSection( &font_cs );
|
|
list_add_head( &unused_gdi_font_list, &font->unused_entry );
|
|
if (unused_font_count > UNUSED_CACHE_SIZE)
|
|
{
|
|
font = LIST_ENTRY( list_tail( &unused_gdi_font_list ), struct gdi_font, unused_entry );
|
|
TRACE( "freeing %p\n", font );
|
|
list_remove( &font->entry );
|
|
list_remove( &font->unused_entry );
|
|
free_gdi_font( font );
|
|
}
|
|
else unused_font_count++;
|
|
LeaveCriticalSection( &font_cs );
|
|
}
|
|
|
|
static void add_font_list(HKEY hkey, const struct nls_update_font_list *fl, int dpi)
|
|
{
|
|
const char *sserif = (dpi <= 108) ? fl->sserif_96 : fl->sserif_120;
|
|
|
|
RegSetValueExA(hkey, "Courier", 0, REG_SZ, (const BYTE *)fl->courier, strlen(fl->courier)+1);
|
|
RegSetValueExA(hkey, "MS Serif", 0, REG_SZ, (const BYTE *)fl->serif, strlen(fl->serif)+1);
|
|
RegSetValueExA(hkey, "MS Sans Serif", 0, REG_SZ, (const BYTE *)sserif, strlen(sserif)+1);
|
|
RegSetValueExA(hkey, "Small Fonts", 0, REG_SZ, (const BYTE *)fl->small, strlen(fl->small)+1);
|
|
}
|
|
|
|
static void set_value_key(HKEY hkey, const char *name, const char *value)
|
|
{
|
|
if (value)
|
|
RegSetValueExA(hkey, name, 0, REG_SZ, (const BYTE *)value, strlen(value) + 1);
|
|
else if (name)
|
|
RegDeleteValueA(hkey, name);
|
|
}
|
|
|
|
static void update_font_association_info(UINT current_ansi_codepage)
|
|
{
|
|
if (is_dbcs_ansi_cp(current_ansi_codepage))
|
|
{
|
|
HKEY hkey;
|
|
if (RegCreateKeyW(HKEY_LOCAL_MACHINE, L"System\\CurrentControlSet\\Control\\FontAssoc", &hkey) == ERROR_SUCCESS)
|
|
{
|
|
HKEY hsubkey;
|
|
if (RegCreateKeyW(hkey, L"Associated Charset", &hsubkey) == ERROR_SUCCESS)
|
|
{
|
|
switch (current_ansi_codepage)
|
|
{
|
|
case 932:
|
|
set_value_key(hsubkey, "ANSI(00)", "NO");
|
|
set_value_key(hsubkey, "OEM(FF)", "NO");
|
|
set_value_key(hsubkey, "SYMBOL(02)", "NO");
|
|
break;
|
|
case 936:
|
|
case 949:
|
|
case 950:
|
|
set_value_key(hsubkey, "ANSI(00)", "YES");
|
|
set_value_key(hsubkey, "OEM(FF)", "YES");
|
|
set_value_key(hsubkey, "SYMBOL(02)", "NO");
|
|
break;
|
|
}
|
|
RegCloseKey(hsubkey);
|
|
}
|
|
|
|
/* TODO: Associated DefaultFonts */
|
|
|
|
RegCloseKey(hkey);
|
|
}
|
|
}
|
|
else
|
|
RegDeleteTreeW(HKEY_LOCAL_MACHINE, L"System\\CurrentControlSet\\Control\\FontAssoc");
|
|
}
|
|
|
|
static void set_multi_value_key(HKEY hkey, const WCHAR *name, const WCHAR *value, DWORD len)
|
|
{
|
|
if (value)
|
|
RegSetValueExW(hkey, name, 0, REG_MULTI_SZ, (const BYTE *)value, len);
|
|
else if (name)
|
|
RegDeleteValueW(hkey, name);
|
|
}
|
|
|
|
static void update_font_system_link_info(UINT current_ansi_codepage)
|
|
{
|
|
static const WCHAR system_link_simplified_chinese[] =
|
|
L"SIMSUN.TTC,SimSun\0"
|
|
L"MINGLIU.TTC,PMingLiu\0"
|
|
L"MSGOTHIC.TTC,MS UI Gothic\0"
|
|
L"BATANG.TTC,Batang\0";
|
|
static const WCHAR system_link_traditional_chinese[] =
|
|
L"MINGLIU.TTC,PMingLiu\0"
|
|
L"SIMSUN.TTC,SimSun\0"
|
|
L"MSGOTHIC.TTC,MS UI Gothic\0"
|
|
L"BATANG.TTC,Batang\0";
|
|
static const WCHAR system_link_japanese[] =
|
|
L"MSGOTHIC.TTC,MS UI Gothic\0"
|
|
L"MINGLIU.TTC,PMingLiU\0"
|
|
L"SIMSUN.TTC,SimSun\0"
|
|
L"GULIM.TTC,Gulim\0";
|
|
static const WCHAR system_link_korean[] =
|
|
L"GULIM.TTC,Gulim\0"
|
|
L"MSGOTHIC.TTC,MS UI Gothic\0"
|
|
L"MINGLIU.TTC,PMingLiU\0"
|
|
L"SIMSUN.TTC,SimSun\0";
|
|
static const WCHAR system_link_non_cjk[] =
|
|
L"MSGOTHIC.TTC,MS UI Gothic\0"
|
|
L"MINGLIU.TTC,PMingLiU\0"
|
|
L"SIMSUN.TTC,SimSun\0"
|
|
L"GULIM.TTC,Gulim\0";
|
|
HKEY hkey;
|
|
|
|
if (!RegCreateKeyW(HKEY_LOCAL_MACHINE,
|
|
L"Software\\Microsoft\\Windows NT\\CurrentVersion\\FontLink\\SystemLink", &hkey))
|
|
{
|
|
const WCHAR *link;
|
|
DWORD len;
|
|
|
|
switch (current_ansi_codepage)
|
|
{
|
|
case 932:
|
|
link = system_link_japanese;
|
|
len = sizeof(system_link_japanese);
|
|
break;
|
|
case 936:
|
|
link = system_link_simplified_chinese;
|
|
len = sizeof(system_link_simplified_chinese);
|
|
break;
|
|
case 949:
|
|
link = system_link_korean;
|
|
len = sizeof(system_link_korean);
|
|
break;
|
|
case 950:
|
|
link = system_link_traditional_chinese;
|
|
len = sizeof(system_link_traditional_chinese);
|
|
break;
|
|
default:
|
|
link = system_link_non_cjk;
|
|
len = sizeof(system_link_non_cjk);
|
|
}
|
|
set_multi_value_key(hkey, L"Lucida Sans Unicode", link, len);
|
|
set_multi_value_key(hkey, L"Microsoft Sans Serif", link, len);
|
|
set_multi_value_key(hkey, L"Tahoma", link, len);
|
|
RegCloseKey(hkey);
|
|
}
|
|
}
|
|
|
|
static void update_codepage(void)
|
|
{
|
|
char buf[40], cpbuf[40];
|
|
HKEY hkey;
|
|
DWORD len, type, size;
|
|
UINT i, ansi_cp, oem_cp;
|
|
DWORD screen_dpi, font_dpi = 0;
|
|
BOOL done = FALSE;
|
|
|
|
screen_dpi = get_dpi();
|
|
if (!screen_dpi) screen_dpi = 96;
|
|
|
|
size = sizeof(DWORD);
|
|
if (RegQueryValueExW(wine_fonts_key, L"LogPixels", NULL, &type, (BYTE *)&font_dpi, &size) ||
|
|
type != REG_DWORD || size != sizeof(DWORD))
|
|
font_dpi = 0;
|
|
|
|
ansi_cp = GetACP();
|
|
oem_cp = GetOEMCP();
|
|
sprintf( cpbuf, "%u,%u", ansi_cp, oem_cp );
|
|
|
|
buf[0] = 0;
|
|
len = sizeof(buf);
|
|
if (!RegQueryValueExA(wine_fonts_key, "Codepages", 0, &type, (BYTE *)buf, &len) && type == REG_SZ)
|
|
{
|
|
if (!strcmp( buf, cpbuf ) && screen_dpi == font_dpi) return; /* already set correctly */
|
|
TRACE("updating registry, codepages/logpixels changed %s/%u -> %u,%u/%u\n",
|
|
buf, font_dpi, ansi_cp, oem_cp, screen_dpi);
|
|
}
|
|
else TRACE("updating registry, codepages/logpixels changed none -> %u,%u/%u\n",
|
|
ansi_cp, oem_cp, screen_dpi);
|
|
|
|
RegSetValueExA(wine_fonts_key, "Codepages", 0, REG_SZ, (const BYTE *)cpbuf, strlen(cpbuf)+1);
|
|
RegSetValueExW(wine_fonts_key, L"LogPixels", 0, REG_DWORD, (const BYTE *)&screen_dpi, sizeof(screen_dpi));
|
|
|
|
for (i = 0; i < ARRAY_SIZE(nls_update_font_list); i++)
|
|
{
|
|
if (nls_update_font_list[i].ansi_cp == ansi_cp && nls_update_font_list[i].oem_cp == oem_cp)
|
|
{
|
|
if (!RegCreateKeyW( HKEY_CURRENT_CONFIG, L"Software\\Fonts", &hkey ))
|
|
{
|
|
RegSetValueExA(hkey, "OEMFONT.FON", 0, REG_SZ, (const BYTE *)nls_update_font_list[i].oem,
|
|
strlen(nls_update_font_list[i].oem)+1);
|
|
RegSetValueExA(hkey, "FIXEDFON.FON", 0, REG_SZ, (const BYTE *)nls_update_font_list[i].fixed,
|
|
strlen(nls_update_font_list[i].fixed)+1);
|
|
RegSetValueExA(hkey, "FONTS.FON", 0, REG_SZ, (const BYTE *)nls_update_font_list[i].system,
|
|
strlen(nls_update_font_list[i].system)+1);
|
|
RegCloseKey(hkey);
|
|
}
|
|
if (!RegCreateKeyW( HKEY_LOCAL_MACHINE,
|
|
L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts", &hkey ))
|
|
{
|
|
add_font_list(hkey, &nls_update_font_list[i], screen_dpi);
|
|
RegCloseKey(hkey);
|
|
}
|
|
if (!RegCreateKeyW( HKEY_LOCAL_MACHINE,
|
|
L"Software\\Microsoft\\Windows\\CurrentVersion\\Fonts", &hkey ))
|
|
{
|
|
add_font_list(hkey, &nls_update_font_list[i], screen_dpi);
|
|
RegCloseKey(hkey);
|
|
}
|
|
/* Only update these if the Codepage changed. */
|
|
if (strcmp( buf, cpbuf ) && !RegCreateKeyW( HKEY_LOCAL_MACHINE,
|
|
L"Software\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes", &hkey ))
|
|
{
|
|
RegSetValueExA(hkey, "MS Shell Dlg", 0, REG_SZ, (const BYTE *)nls_update_font_list[i].shelldlg,
|
|
strlen(nls_update_font_list[i].shelldlg)+1);
|
|
RegSetValueExA(hkey, "Tms Rmn", 0, REG_SZ, (const BYTE *)nls_update_font_list[i].tmsrmn,
|
|
strlen(nls_update_font_list[i].tmsrmn)+1);
|
|
|
|
set_value_key(hkey, "Fixedsys,0", nls_update_font_list[i].fixed_0);
|
|
set_value_key(hkey, "System,0", nls_update_font_list[i].system_0);
|
|
set_value_key(hkey, "Courier,0", nls_update_font_list[i].courier_0);
|
|
set_value_key(hkey, "MS Serif,0", nls_update_font_list[i].serif_0);
|
|
set_value_key(hkey, "Small Fonts,0", nls_update_font_list[i].small_0);
|
|
set_value_key(hkey, "MS Sans Serif,0", nls_update_font_list[i].sserif_0);
|
|
set_value_key(hkey, "Helv,0", nls_update_font_list[i].helv_0);
|
|
set_value_key(hkey, "Tms Rmn,0", nls_update_font_list[i].tmsrmn_0);
|
|
|
|
set_value_key(hkey, nls_update_font_list[i].arial_0.from, nls_update_font_list[i].arial_0.to);
|
|
set_value_key(hkey, nls_update_font_list[i].courier_new_0.from, nls_update_font_list[i].courier_new_0.to);
|
|
set_value_key(hkey, nls_update_font_list[i].times_new_roman_0.from, nls_update_font_list[i].times_new_roman_0.to);
|
|
|
|
RegCloseKey(hkey);
|
|
}
|
|
done = TRUE;
|
|
}
|
|
else
|
|
{
|
|
/* Delete the FontSubstitutes from other locales */
|
|
if (!RegCreateKeyW( HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes", &hkey ))
|
|
{
|
|
set_value_key(hkey, nls_update_font_list[i].arial_0.from, NULL);
|
|
set_value_key(hkey, nls_update_font_list[i].courier_new_0.from, NULL);
|
|
set_value_key(hkey, nls_update_font_list[i].times_new_roman_0.from, NULL);
|
|
RegCloseKey(hkey);
|
|
}
|
|
}
|
|
}
|
|
if (!done)
|
|
FIXME("there is no font defaults for codepages %u,%u\n", ansi_cp, oem_cp);
|
|
|
|
/* update locale dependent font association info and font system link info in registry.
|
|
update only when codepages changed, not logpixels. */
|
|
if (strcmp(buf, cpbuf) != 0)
|
|
{
|
|
update_font_association_info(ansi_cp);
|
|
update_font_system_link_info(ansi_cp);
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************
|
|
* font_CreateDC
|
|
*/
|
|
static BOOL CDECL font_CreateDC( PHYSDEV *dev, LPCWSTR driver, LPCWSTR device,
|
|
LPCWSTR output, const DEVMODEW *devmode )
|
|
{
|
|
struct font_physdev *physdev;
|
|
|
|
if (!font_funcs) return TRUE;
|
|
if (!(physdev = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*physdev) ))) return FALSE;
|
|
push_dc_driver( dev, &physdev->dev, &font_driver );
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*************************************************************
|
|
* font_DeleteDC
|
|
*/
|
|
static BOOL CDECL font_DeleteDC( PHYSDEV dev )
|
|
{
|
|
struct font_physdev *physdev = get_font_dev( dev );
|
|
|
|
release_gdi_font( physdev->font );
|
|
HeapFree( GetProcessHeap(), 0, physdev );
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
struct gdi_font_enum_data
|
|
{
|
|
ENUMLOGFONTEXW elf;
|
|
NEWTEXTMETRICEXW ntm;
|
|
};
|
|
|
|
struct enum_charset
|
|
{
|
|
DWORD mask;
|
|
DWORD charset;
|
|
DWORD script;
|
|
};
|
|
|
|
static int load_script_name( UINT id, WCHAR buffer[LF_FACESIZE] )
|
|
{
|
|
HRSRC rsrc;
|
|
HGLOBAL hMem;
|
|
WCHAR *p;
|
|
int i;
|
|
|
|
id += IDS_FIRST_SCRIPT;
|
|
rsrc = FindResourceW( gdi32_module, (LPCWSTR)(ULONG_PTR)((id >> 4) + 1), (LPCWSTR)6 /*RT_STRING*/ );
|
|
if (!rsrc) return 0;
|
|
hMem = LoadResource( gdi32_module, rsrc );
|
|
if (!hMem) return 0;
|
|
|
|
p = LockResource( hMem );
|
|
id &= 0x000f;
|
|
while (id--) p += *p + 1;
|
|
|
|
i = min(LF_FACESIZE - 1, *p);
|
|
memcpy(buffer, p + 1, i * sizeof(WCHAR));
|
|
buffer[i] = 0;
|
|
return i;
|
|
}
|
|
|
|
static BOOL is_complex_script_ansi_cp( UINT ansi_cp )
|
|
{
|
|
return (ansi_cp == 874 /* Thai */
|
|
|| ansi_cp == 1255 /* Hebrew */
|
|
|| ansi_cp == 1256 /* Arabic */
|
|
);
|
|
}
|
|
|
|
/***************************************************
|
|
* create_enum_charset_list
|
|
*
|
|
* This function creates charset enumeration list because in DEFAULT_CHARSET
|
|
* case, the ANSI codepage's charset takes precedence over other charsets.
|
|
* Above rule doesn't apply if the ANSI codepage uses complex script (e.g. Thai).
|
|
* This function works as a filter other than DEFAULT_CHARSET case.
|
|
*/
|
|
static DWORD create_enum_charset_list(DWORD charset, struct enum_charset *list)
|
|
{
|
|
struct enum_charset *start = list;
|
|
CHARSETINFO csi;
|
|
int i;
|
|
|
|
if (translate_charset_info( ULongToPtr(charset), &csi, TCI_SRCCHARSET ) && csi.fs.fsCsb[0] != 0)
|
|
{
|
|
list->mask = csi.fs.fsCsb[0];
|
|
list->charset = csi.ciCharset;
|
|
for (i = 0; i < 32; i++) if (csi.fs.fsCsb[0] & (1u << i)) list->script = i;
|
|
list++;
|
|
}
|
|
else /* charset is DEFAULT_CHARSET or invalid. */
|
|
{
|
|
int acp = GetACP();
|
|
DWORD mask = 0;
|
|
|
|
/* Set the current codepage's charset as the first element. */
|
|
if (!is_complex_script_ansi_cp(acp) &&
|
|
translate_charset_info( (DWORD *)(INT_PTR)acp, &csi, TCI_SRCCODEPAGE ) &&
|
|
csi.fs.fsCsb[0] != 0)
|
|
{
|
|
list->mask = csi.fs.fsCsb[0];
|
|
list->charset = csi.ciCharset;
|
|
for (i = 0; i < 32; i++) if (csi.fs.fsCsb[0] & (1u << i)) list->script = i;
|
|
mask |= csi.fs.fsCsb[0];
|
|
list++;
|
|
}
|
|
|
|
/* Fill out left elements. */
|
|
for (i = 0; i < 32; i++)
|
|
{
|
|
FONTSIGNATURE fs;
|
|
fs.fsCsb[0] = 1u << i;
|
|
fs.fsCsb[1] = 0;
|
|
if (fs.fsCsb[0] & mask) continue; /* skip, already added. */
|
|
if (!translate_charset_info( fs.fsCsb, &csi, TCI_SRCFONTSIG ))
|
|
continue; /* skip, this is an invalid fsCsb bit. */
|
|
list->mask = fs.fsCsb[0];
|
|
list->charset = csi.ciCharset;
|
|
list->script = i;
|
|
mask |= fs.fsCsb[0];
|
|
list++;
|
|
}
|
|
/* add catch all mask for remaining bits */
|
|
if (~mask)
|
|
{
|
|
list->mask = ~mask;
|
|
list->charset = DEFAULT_CHARSET;
|
|
list->script = IDS_OTHER - IDS_FIRST_SCRIPT;
|
|
list++;
|
|
}
|
|
}
|
|
return list - start;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static BOOL get_face_enum_data( struct gdi_font_face *face, ENUMLOGFONTEXW *elf, NEWTEXTMETRICEXW *ntm )
|
|
{
|
|
struct gdi_font *font;
|
|
LOGFONTW lf = { .lfHeight = -4096 /* preferable EM Square size */ };
|
|
|
|
if (!face->scalable) lf.lfHeight = 0;
|
|
|
|
if (!(font = create_gdi_font( face, NULL, &lf ))) return FALSE;
|
|
|
|
if (!font_funcs->load_font( font ))
|
|
{
|
|
free_gdi_font( font );
|
|
return FALSE;
|
|
}
|
|
|
|
if (font->scalable && -lf.lfHeight % font->otm.otmEMSquare != 0)
|
|
{
|
|
/* reload with the original EM Square size */
|
|
lf.lfHeight = -font->otm.otmEMSquare;
|
|
free_gdi_font( font );
|
|
|
|
if (!(font = create_gdi_font( face, NULL, &lf ))) return FALSE;
|
|
if (!font_funcs->load_font( font ))
|
|
{
|
|
free_gdi_font( font );
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (font_funcs->set_outline_text_metrics( font ))
|
|
{
|
|
static const DWORD ntm_ppem = 32;
|
|
UINT cell_height;
|
|
|
|
#define TM font->otm.otmTextMetrics
|
|
#define SCALE_NTM(value) (MulDiv( ntm->ntmTm.tmHeight, (value), TM.tmHeight ))
|
|
cell_height = TM.tmHeight / ( -lf.lfHeight / font->otm.otmEMSquare );
|
|
ntm->ntmTm.tmHeight = MulDiv( ntm_ppem, cell_height, font->otm.otmEMSquare );
|
|
ntm->ntmTm.tmAscent = SCALE_NTM( TM.tmAscent );
|
|
ntm->ntmTm.tmDescent = ntm->ntmTm.tmHeight - ntm->ntmTm.tmAscent;
|
|
ntm->ntmTm.tmInternalLeading = SCALE_NTM( TM.tmInternalLeading );
|
|
ntm->ntmTm.tmExternalLeading = SCALE_NTM( TM.tmExternalLeading );
|
|
ntm->ntmTm.tmAveCharWidth = SCALE_NTM( TM.tmAveCharWidth );
|
|
ntm->ntmTm.tmMaxCharWidth = SCALE_NTM( TM.tmMaxCharWidth );
|
|
|
|
memcpy((char *)&ntm->ntmTm + offsetof( TEXTMETRICW, tmWeight ),
|
|
(const char *)&TM + offsetof( TEXTMETRICW, tmWeight ),
|
|
sizeof(TEXTMETRICW) - offsetof( TEXTMETRICW, tmWeight ));
|
|
ntm->ntmTm.ntmSizeEM = font->otm.otmEMSquare;
|
|
ntm->ntmTm.ntmCellHeight = cell_height;
|
|
ntm->ntmTm.ntmAvgWidth = font->ntmAvgWidth;
|
|
#undef SCALE_NTM
|
|
#undef TM
|
|
}
|
|
else if (font_funcs->set_bitmap_text_metrics( font ))
|
|
{
|
|
memcpy( &ntm->ntmTm, &font->otm.otmTextMetrics, sizeof(TEXTMETRICW) );
|
|
ntm->ntmTm.ntmSizeEM = ntm->ntmTm.tmHeight - ntm->ntmTm.tmInternalLeading;
|
|
ntm->ntmTm.ntmCellHeight = ntm->ntmTm.tmHeight;
|
|
ntm->ntmTm.ntmAvgWidth = ntm->ntmTm.tmAveCharWidth;
|
|
}
|
|
ntm->ntmTm.ntmFlags = font->ntmFlags;
|
|
ntm->ntmFontSig = font->fs;
|
|
|
|
elf->elfLogFont.lfEscapement = 0;
|
|
elf->elfLogFont.lfOrientation = 0;
|
|
elf->elfLogFont.lfHeight = ntm->ntmTm.tmHeight;
|
|
elf->elfLogFont.lfWidth = ntm->ntmTm.tmAveCharWidth;
|
|
elf->elfLogFont.lfWeight = ntm->ntmTm.tmWeight;
|
|
elf->elfLogFont.lfItalic = ntm->ntmTm.tmItalic;
|
|
elf->elfLogFont.lfUnderline = ntm->ntmTm.tmUnderlined;
|
|
elf->elfLogFont.lfStrikeOut = ntm->ntmTm.tmStruckOut;
|
|
elf->elfLogFont.lfCharSet = ntm->ntmTm.tmCharSet;
|
|
elf->elfLogFont.lfOutPrecision = OUT_STROKE_PRECIS;
|
|
elf->elfLogFont.lfClipPrecision = CLIP_STROKE_PRECIS;
|
|
elf->elfLogFont.lfQuality = DRAFT_QUALITY;
|
|
elf->elfLogFont.lfPitchAndFamily = (ntm->ntmTm.tmPitchAndFamily & 0xf1) + 1;
|
|
lstrcpynW( elf->elfLogFont.lfFaceName, (WCHAR *)font->otm.otmpFamilyName, LF_FACESIZE );
|
|
lstrcpynW( elf->elfFullName, (WCHAR *)font->otm.otmpFaceName, LF_FULLFACESIZE );
|
|
lstrcpynW( elf->elfStyle, (WCHAR *)font->otm.otmpStyleName, LF_FACESIZE );
|
|
|
|
free_gdi_font( font );
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL family_matches( struct gdi_font_family *family, const WCHAR *face_name )
|
|
{
|
|
struct gdi_font_face *face;
|
|
|
|
if (!facename_compare( face_name, family->family_name, LF_FACESIZE - 1 )) return TRUE;
|
|
LIST_FOR_EACH_ENTRY( face, get_family_face_list(family), struct gdi_font_face, entry )
|
|
if (!facename_compare( face_name, face->full_name, LF_FACESIZE - 1 )) return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOL face_matches( const WCHAR *family_name, struct gdi_font_face *face, const WCHAR *face_name )
|
|
{
|
|
if (!facename_compare( face_name, family_name, LF_FACESIZE - 1)) return TRUE;
|
|
return !facename_compare( face_name, face->full_name, LF_FACESIZE - 1 );
|
|
}
|
|
|
|
static BOOL enum_face_charsets( const struct gdi_font_family *family, struct gdi_font_face *face,
|
|
struct enum_charset *list, DWORD count, FONTENUMPROCW proc, LPARAM lparam,
|
|
const WCHAR *subst )
|
|
{
|
|
ENUMLOGFONTEXW elf;
|
|
NEWTEXTMETRICEXW ntm;
|
|
DWORD type, i;
|
|
|
|
if (!face->cached_enum_data)
|
|
{
|
|
struct gdi_font_enum_data *data;
|
|
|
|
if (!(data = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*data) )) ||
|
|
!get_face_enum_data( face, &data->elf, &data->ntm ))
|
|
{
|
|
HeapFree( GetProcessHeap(), 0, data );
|
|
return TRUE;
|
|
}
|
|
face->cached_enum_data = data;
|
|
}
|
|
|
|
elf = face->cached_enum_data->elf;
|
|
ntm = face->cached_enum_data->ntm;
|
|
type = get_font_type( &ntm );
|
|
|
|
/* font replacement */
|
|
if (family != face->family)
|
|
{
|
|
lstrcpynW( elf.elfLogFont.lfFaceName, family->family_name, LF_FACESIZE );
|
|
lstrcpynW( elf.elfFullName, face->full_name, LF_FULLFACESIZE );
|
|
}
|
|
if (subst) lstrcpynW( elf.elfLogFont.lfFaceName, subst, LF_FACESIZE );
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
if (face->fs.fsCsb[0] == 0) /* OEM */
|
|
{
|
|
elf.elfLogFont.lfCharSet = ntm.ntmTm.tmCharSet = OEM_CHARSET;
|
|
load_script_name( IDS_OEM_DOS - IDS_FIRST_SCRIPT, elf.elfScript );
|
|
i = count; /* break out of loop after enumeration */
|
|
}
|
|
else
|
|
{
|
|
if (!(face->fs.fsCsb[0] & list[i].mask)) continue;
|
|
/* use the DEFAULT_CHARSET case only if no other charset is present */
|
|
if (list[i].charset == DEFAULT_CHARSET && (face->fs.fsCsb[0] & ~list[i].mask)) continue;
|
|
elf.elfLogFont.lfCharSet = ntm.ntmTm.tmCharSet = list[i].charset;
|
|
load_script_name( list[i].script, elf.elfScript );
|
|
if (!elf.elfScript[0]) FIXME("Unknown elfscript for id %u\n", list[i].script);
|
|
}
|
|
TRACE( "face %s full %s style %s charset = %d type %d script %s it %d weight %d ntmflags %08x\n",
|
|
debugstr_w(elf.elfLogFont.lfFaceName), debugstr_w(elf.elfFullName), debugstr_w(elf.elfStyle),
|
|
elf.elfLogFont.lfCharSet, type, debugstr_w(elf.elfScript),
|
|
elf.elfLogFont.lfItalic, elf.elfLogFont.lfWeight, ntm.ntmTm.ntmFlags );
|
|
/* release section before callback (FIXME) */
|
|
LeaveCriticalSection( &font_cs );
|
|
if (!proc( &elf.elfLogFont, (TEXTMETRICW *)&ntm, type, lparam )) return FALSE;
|
|
EnterCriticalSection( &font_cs );
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/*************************************************************
|
|
* font_EnumFonts
|
|
*/
|
|
static BOOL CDECL font_EnumFonts( PHYSDEV dev, LOGFONTW *lf, FONTENUMPROCW proc, LPARAM lparam )
|
|
{
|
|
struct gdi_font_family *family;
|
|
struct gdi_font_face *face;
|
|
struct enum_charset enum_charsets[32];
|
|
DWORD count, charset;
|
|
|
|
charset = lf ? lf->lfCharSet : DEFAULT_CHARSET;
|
|
|
|
count = create_enum_charset_list( charset, enum_charsets );
|
|
|
|
EnterCriticalSection( &font_cs );
|
|
|
|
if (lf && lf->lfFaceName[0])
|
|
{
|
|
const WCHAR *face_name = get_gdi_font_subst( lf->lfFaceName, charset, NULL );
|
|
const WCHAR *orig_name = NULL;
|
|
|
|
TRACE( "facename = %s charset %d\n", debugstr_w(lf->lfFaceName), charset );
|
|
if (face_name)
|
|
{
|
|
orig_name = lf->lfFaceName;
|
|
TRACE( "substituting %s -> %s\n", debugstr_w(lf->lfFaceName), debugstr_w(face_name) );
|
|
}
|
|
else face_name = lf->lfFaceName;
|
|
|
|
WINE_RB_FOR_EACH_ENTRY( family, &family_name_tree, struct gdi_font_family, name_entry )
|
|
{
|
|
if (!family_matches(family, face_name)) continue;
|
|
LIST_FOR_EACH_ENTRY( face, get_family_face_list(family), struct gdi_font_face, entry )
|
|
{
|
|
if (!face_matches( family->family_name, face, face_name )) continue;
|
|
if (!enum_face_charsets( family, face, enum_charsets, count, proc, lparam, orig_name ))
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TRACE( "charset %d\n", charset );
|
|
WINE_RB_FOR_EACH_ENTRY( family, &family_name_tree, struct gdi_font_family, name_entry )
|
|
{
|
|
face = LIST_ENTRY( list_head(get_family_face_list(family)), struct gdi_font_face, entry );
|
|
if (!enum_face_charsets( family, face, enum_charsets, count, proc, lparam, NULL ))
|
|
return FALSE;
|
|
}
|
|
}
|
|
LeaveCriticalSection( &font_cs );
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static BOOL check_unicode_tategaki( WCHAR ch )
|
|
{
|
|
extern const unsigned short vertical_orientation_table[] DECLSPEC_HIDDEN;
|
|
unsigned short orientation = vertical_orientation_table[vertical_orientation_table[vertical_orientation_table[ch >> 8]+((ch >> 4) & 0x0f)]+ (ch & 0xf)];
|
|
|
|
/* We only reach this code if typographical substitution did not occur */
|
|
/* Type: U or Type: Tu */
|
|
return (orientation == 1 || orientation == 3);
|
|
}
|
|
|
|
static UINT get_glyph_index_symbol( struct gdi_font *font, UINT glyph )
|
|
{
|
|
UINT index;
|
|
|
|
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 */
|
|
index = glyph;
|
|
font_funcs->get_glyph_index( font, &index, FALSE );
|
|
if (!index)
|
|
{
|
|
index = glyph - 0xf000;
|
|
font_funcs->get_glyph_index( font, &index, FALSE );
|
|
}
|
|
return index;
|
|
}
|
|
|
|
static UINT get_glyph_index( struct gdi_font *font, UINT glyph )
|
|
{
|
|
WCHAR wc = glyph;
|
|
char ch;
|
|
BOOL used;
|
|
|
|
if (font_funcs->get_glyph_index( font, &glyph, TRUE )) return glyph;
|
|
|
|
if (font->codepage == CP_SYMBOL)
|
|
{
|
|
glyph = get_glyph_index_symbol( font, wc );
|
|
if (!glyph)
|
|
{
|
|
if (WideCharToMultiByte( CP_ACP, WC_NO_BEST_FIT_CHARS, &wc, 1, &ch, 1, NULL, NULL ))
|
|
glyph = get_glyph_index_symbol( font, (unsigned char)ch );
|
|
}
|
|
}
|
|
else if (WideCharToMultiByte( font->codepage, WC_NO_BEST_FIT_CHARS, &wc, 1, &ch, 1, NULL, &used ) && !used)
|
|
{
|
|
glyph = (unsigned char)ch;
|
|
font_funcs->get_glyph_index( font, &glyph, FALSE );
|
|
}
|
|
else return 0;
|
|
|
|
return glyph;
|
|
}
|
|
|
|
static UINT get_glyph_index_linked( struct gdi_font **font, UINT glyph )
|
|
{
|
|
struct gdi_font *child;
|
|
UINT res;
|
|
|
|
if ((res = get_glyph_index( *font, glyph ))) return res;
|
|
if (glyph < 32) return 0; /* don't check linked fonts for control characters */
|
|
|
|
LIST_FOR_EACH_ENTRY( child, &(*font)->child_fonts, struct gdi_font, entry )
|
|
{
|
|
if (!child->private && !font_funcs->load_font( child )) continue;
|
|
if ((res = get_glyph_index( child, glyph )))
|
|
{
|
|
*font = child;
|
|
return res;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static DWORD get_glyph_outline( struct gdi_font *font, UINT glyph, UINT format,
|
|
GLYPHMETRICS *gm_ret, ABC *abc_ret, DWORD buflen, void *buf,
|
|
const MAT2 *mat )
|
|
{
|
|
GLYPHMETRICS gm;
|
|
ABC abc;
|
|
DWORD ret = 1;
|
|
UINT index = glyph;
|
|
BOOL tategaki = (*get_gdi_font_name( font ) == '@');
|
|
|
|
if (format & GGO_GLYPH_INDEX)
|
|
{
|
|
/* Windows bitmap font, e.g. Small Fonts, uses ANSI character code
|
|
as glyph index. "Treasure Adventure Game" depends on this. */
|
|
font_funcs->get_glyph_index( font, &index, FALSE );
|
|
format &= ~GGO_GLYPH_INDEX;
|
|
/* TODO: Window also turns off tategaki for glyphs passed in by index
|
|
if their unicode code points fall outside of the range that is
|
|
rotated. */
|
|
}
|
|
else
|
|
{
|
|
index = get_glyph_index_linked( &font, glyph );
|
|
if (tategaki)
|
|
{
|
|
UINT orig = index;
|
|
index = get_GSUB_vert_glyph( font, index );
|
|
if (index == orig) tategaki = check_unicode_tategaki( glyph );
|
|
}
|
|
}
|
|
|
|
if (mat && !memcmp( mat, &identity, sizeof(*mat) )) mat = NULL;
|
|
|
|
if (format == GGO_METRICS && !mat && get_gdi_font_glyph_metrics( font, index, &gm, &abc ))
|
|
goto done;
|
|
|
|
ret = font_funcs->get_glyph_outline( font, index, format, &gm, &abc, buflen, buf, mat, tategaki );
|
|
if (ret == GDI_ERROR) return ret;
|
|
|
|
if ((format == GGO_METRICS || format == GGO_BITMAP || format == WINE_GGO_GRAY16_BITMAP) && !mat)
|
|
set_gdi_font_glyph_metrics( font, index, &gm, &abc );
|
|
|
|
done:
|
|
if (gm_ret) *gm_ret = gm;
|
|
if (abc_ret) *abc_ret = abc;
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*************************************************************
|
|
* font_FontIsLinked
|
|
*/
|
|
static BOOL CDECL font_FontIsLinked( PHYSDEV dev )
|
|
{
|
|
struct font_physdev *physdev = get_font_dev( dev );
|
|
|
|
if (!physdev->font)
|
|
{
|
|
dev = GET_NEXT_PHYSDEV( dev, pFontIsLinked );
|
|
return dev->funcs->pFontIsLinked( dev );
|
|
}
|
|
return !list_empty( &physdev->font->child_fonts );
|
|
}
|
|
|
|
|
|
/*************************************************************
|
|
* font_GetCharABCWidths
|
|
*/
|
|
static BOOL CDECL font_GetCharABCWidths( PHYSDEV dev, UINT first, UINT count,
|
|
WCHAR *chars, ABC *buffer )
|
|
{
|
|
struct font_physdev *physdev = get_font_dev( dev );
|
|
UINT c, i;
|
|
|
|
if (!physdev->font)
|
|
{
|
|
dev = GET_NEXT_PHYSDEV( dev, pGetCharABCWidths );
|
|
return dev->funcs->pGetCharABCWidths( dev, first, count, chars, buffer );
|
|
}
|
|
|
|
TRACE( "%p, %u, %u, %p\n", physdev->font, first, count, buffer );
|
|
|
|
EnterCriticalSection( &font_cs );
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
c = chars ? chars[i] : first + i;
|
|
get_glyph_outline( physdev->font, c, GGO_METRICS, NULL, &buffer[i], 0, NULL, NULL );
|
|
}
|
|
LeaveCriticalSection( &font_cs );
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*************************************************************
|
|
* font_GetCharABCWidthsI
|
|
*/
|
|
static BOOL CDECL font_GetCharABCWidthsI( PHYSDEV dev, UINT first, UINT count, WORD *gi, ABC *buffer )
|
|
{
|
|
struct font_physdev *physdev = get_font_dev( dev );
|
|
UINT c;
|
|
|
|
if (!physdev->font)
|
|
{
|
|
dev = GET_NEXT_PHYSDEV( dev, pGetCharABCWidthsI );
|
|
return dev->funcs->pGetCharABCWidthsI( dev, first, count, gi, buffer );
|
|
}
|
|
|
|
TRACE( "%p, %u, %u, %p\n", physdev->font, first, count, buffer );
|
|
|
|
EnterCriticalSection( &font_cs );
|
|
for (c = 0; c < count; c++, buffer++)
|
|
get_glyph_outline( physdev->font, gi ? gi[c] : first + c, GGO_METRICS | GGO_GLYPH_INDEX,
|
|
NULL, buffer, 0, NULL, NULL );
|
|
LeaveCriticalSection( &font_cs );
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*************************************************************
|
|
* font_GetCharWidth
|
|
*/
|
|
static BOOL CDECL font_GetCharWidth( PHYSDEV dev, UINT first, UINT count,
|
|
const WCHAR *chars, INT *buffer )
|
|
{
|
|
struct font_physdev *physdev = get_font_dev( dev );
|
|
UINT c, i;
|
|
ABC abc;
|
|
|
|
if (!physdev->font)
|
|
{
|
|
dev = GET_NEXT_PHYSDEV( dev, pGetCharWidth );
|
|
return dev->funcs->pGetCharWidth( dev, first, count, chars, buffer );
|
|
}
|
|
|
|
TRACE( "%p, %d, %d, %p\n", physdev->font, first, count, buffer );
|
|
|
|
EnterCriticalSection( &font_cs );
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
c = chars ? chars[i] : i + first;
|
|
if (get_glyph_outline( physdev->font, c, GGO_METRICS, NULL, &abc, 0, NULL, NULL ) == GDI_ERROR)
|
|
buffer[i] = 0;
|
|
else
|
|
buffer[i] = abc.abcA + abc.abcB + abc.abcC;
|
|
}
|
|
LeaveCriticalSection( &font_cs );
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*************************************************************
|
|
* font_GetCharWidthInfo
|
|
*/
|
|
static BOOL CDECL font_GetCharWidthInfo( PHYSDEV dev, void *ptr )
|
|
{
|
|
struct font_physdev *physdev = get_font_dev( dev );
|
|
struct char_width_info *info = ptr;
|
|
|
|
if (!physdev->font)
|
|
{
|
|
dev = GET_NEXT_PHYSDEV( dev, pGetCharWidthInfo );
|
|
return dev->funcs->pGetCharWidthInfo( dev, ptr );
|
|
}
|
|
|
|
info->unk = 0;
|
|
if (!physdev->font->scalable || !font_funcs->get_char_width_info( physdev->font, info ))
|
|
info->lsb = info->rsb = 0;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*************************************************************
|
|
* font_GetFontData
|
|
*/
|
|
static DWORD CDECL font_GetFontData( PHYSDEV dev, DWORD table, DWORD offset, void *buf, DWORD size )
|
|
{
|
|
struct font_physdev *physdev = get_font_dev( dev );
|
|
|
|
if (!physdev->font)
|
|
{
|
|
dev = GET_NEXT_PHYSDEV( dev, pGetFontData );
|
|
return dev->funcs->pGetFontData( dev, table, offset, buf, size );
|
|
}
|
|
return font_funcs->get_font_data( physdev->font, table, offset, buf, size );
|
|
}
|
|
|
|
|
|
/*************************************************************
|
|
* font_GetFontRealizationInfo
|
|
*/
|
|
static BOOL CDECL font_GetFontRealizationInfo( PHYSDEV dev, void *ptr )
|
|
{
|
|
struct font_physdev *physdev = get_font_dev( dev );
|
|
struct font_realization_info *info = ptr;
|
|
|
|
if (!physdev->font)
|
|
{
|
|
dev = GET_NEXT_PHYSDEV( dev, pGetFontRealizationInfo );
|
|
return dev->funcs->pGetFontRealizationInfo( dev, ptr );
|
|
}
|
|
|
|
TRACE( "(%p, %p)\n", physdev->font, info);
|
|
|
|
info->flags = 1;
|
|
if (physdev->font->scalable) info->flags |= 2;
|
|
|
|
info->cache_num = physdev->font->cache_num;
|
|
info->instance_id = physdev->font->handle;
|
|
if (info->size == sizeof(*info))
|
|
{
|
|
info->file_count = 1;
|
|
info->face_index = physdev->font->face_index;
|
|
info->simulations = 0;
|
|
if (physdev->font->fake_bold) info->simulations |= 0x1;
|
|
if (physdev->font->fake_italic) info->simulations |= 0x2;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*************************************************************
|
|
* font_GetFontUnicodeRanges
|
|
*/
|
|
static DWORD CDECL font_GetFontUnicodeRanges( PHYSDEV dev, GLYPHSET *glyphset )
|
|
{
|
|
struct font_physdev *physdev = get_font_dev( dev );
|
|
DWORD size, num_ranges;
|
|
|
|
if (!physdev->font)
|
|
{
|
|
dev = GET_NEXT_PHYSDEV( dev, pGetFontUnicodeRanges );
|
|
return dev->funcs->pGetFontUnicodeRanges( dev, glyphset );
|
|
}
|
|
|
|
num_ranges = font_funcs->get_unicode_ranges( physdev->font, glyphset );
|
|
size = offsetof( GLYPHSET, ranges[num_ranges] );
|
|
if (glyphset)
|
|
{
|
|
glyphset->cbThis = size;
|
|
glyphset->cRanges = num_ranges;
|
|
glyphset->flAccel = 0;
|
|
}
|
|
return size;
|
|
}
|
|
|
|
|
|
/*************************************************************
|
|
* font_GetGlyphIndices
|
|
*/
|
|
static DWORD CDECL font_GetGlyphIndices( PHYSDEV dev, const WCHAR *str, INT count, WORD *gi, DWORD flags )
|
|
{
|
|
struct font_physdev *physdev = get_font_dev( dev );
|
|
UINT default_char;
|
|
char ch;
|
|
BOOL used, got_default = FALSE;
|
|
int i;
|
|
|
|
if (!physdev->font)
|
|
{
|
|
dev = GET_NEXT_PHYSDEV( dev, pGetGlyphIndices );
|
|
return dev->funcs->pGetGlyphIndices( dev, str, count, gi, flags );
|
|
}
|
|
|
|
if (flags & GGI_MARK_NONEXISTING_GLYPHS)
|
|
{
|
|
default_char = 0xffff; /* XP would use 0x1f for bitmap fonts */
|
|
got_default = TRUE;
|
|
}
|
|
|
|
EnterCriticalSection( &font_cs );
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
UINT glyph = str[i];
|
|
|
|
if (!font_funcs->get_glyph_index( physdev->font, &glyph, TRUE ))
|
|
{
|
|
glyph = 0;
|
|
if (physdev->font->codepage == CP_SYMBOL)
|
|
{
|
|
if (str[i] >= 0xf020 && str[i] <= 0xf100) glyph = str[i] - 0xf000;
|
|
else if (str[i] < 0x100) glyph = str[i];
|
|
}
|
|
else if (WideCharToMultiByte( physdev->font->codepage, WC_NO_BEST_FIT_CHARS, &str[i], 1,
|
|
&ch, 1, NULL, &used ) && !used)
|
|
glyph = (unsigned char)ch;
|
|
}
|
|
if (!glyph)
|
|
{
|
|
if (!got_default)
|
|
{
|
|
default_char = font_funcs->get_default_glyph( physdev->font );
|
|
got_default = TRUE;
|
|
}
|
|
gi[i] = default_char;
|
|
}
|
|
else gi[i] = get_GSUB_vert_glyph( physdev->font, glyph );
|
|
}
|
|
|
|
LeaveCriticalSection( &font_cs );
|
|
return count;
|
|
}
|
|
|
|
|
|
/*************************************************************
|
|
* font_GetGlyphOutline
|
|
*/
|
|
static DWORD CDECL font_GetGlyphOutline( PHYSDEV dev, UINT glyph, UINT format,
|
|
GLYPHMETRICS *gm, DWORD buflen, void *buf, const MAT2 *mat )
|
|
{
|
|
struct font_physdev *physdev = get_font_dev( dev );
|
|
DWORD ret;
|
|
|
|
if (!physdev->font)
|
|
{
|
|
dev = GET_NEXT_PHYSDEV( dev, pGetGlyphOutline );
|
|
return dev->funcs->pGetGlyphOutline( dev, glyph, format, gm, buflen, buf, mat );
|
|
}
|
|
EnterCriticalSection( &font_cs );
|
|
ret = get_glyph_outline( physdev->font, glyph, format, gm, NULL, buflen, buf, mat );
|
|
LeaveCriticalSection( &font_cs );
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*************************************************************
|
|
* font_GetKerningPairs
|
|
*/
|
|
static DWORD CDECL font_GetKerningPairs( PHYSDEV dev, DWORD count, KERNINGPAIR *pairs )
|
|
{
|
|
struct font_physdev *physdev = get_font_dev( dev );
|
|
|
|
if (!physdev->font)
|
|
{
|
|
dev = GET_NEXT_PHYSDEV( dev, pGetKerningPairs );
|
|
return dev->funcs->pGetKerningPairs( dev, count, pairs );
|
|
}
|
|
|
|
EnterCriticalSection( &font_cs );
|
|
if (physdev->font->kern_count == -1)
|
|
physdev->font->kern_count = font_funcs->get_kerning_pairs( physdev->font,
|
|
&physdev->font->kern_pairs );
|
|
LeaveCriticalSection( &font_cs );
|
|
|
|
if (count && pairs)
|
|
{
|
|
count = min( count, physdev->font->kern_count );
|
|
memcpy( pairs, physdev->font->kern_pairs, count * sizeof(*pairs) );
|
|
}
|
|
else count = physdev->font->kern_count;
|
|
|
|
return count;
|
|
}
|
|
|
|
|
|
static void scale_outline_font_metrics( const struct gdi_font *font, OUTLINETEXTMETRICW *otm )
|
|
{
|
|
double scale_x, scale_y;
|
|
|
|
if (font->aveWidth)
|
|
{
|
|
scale_x = (double)font->aveWidth;
|
|
scale_x /= (double)font->otm.otmTextMetrics.tmAveCharWidth;
|
|
}
|
|
else
|
|
scale_x = font->scale_y;
|
|
|
|
scale_x *= fabs(font->matrix.eM11);
|
|
scale_y = font->scale_y * fabs(font->matrix.eM22);
|
|
|
|
/* Windows scales these values as signed integers even if they are unsigned */
|
|
#define SCALE_X(x) (x) = GDI_ROUND((int)(x) * (scale_x))
|
|
#define SCALE_Y(y) (y) = GDI_ROUND((int)(y) * (scale_y))
|
|
|
|
SCALE_Y(otm->otmTextMetrics.tmHeight);
|
|
SCALE_Y(otm->otmTextMetrics.tmAscent);
|
|
SCALE_Y(otm->otmTextMetrics.tmDescent);
|
|
SCALE_Y(otm->otmTextMetrics.tmInternalLeading);
|
|
SCALE_Y(otm->otmTextMetrics.tmExternalLeading);
|
|
|
|
SCALE_X(otm->otmTextMetrics.tmOverhang);
|
|
if (font->fake_bold)
|
|
{
|
|
if (!font->scalable) otm->otmTextMetrics.tmOverhang++;
|
|
otm->otmTextMetrics.tmAveCharWidth++;
|
|
otm->otmTextMetrics.tmMaxCharWidth++;
|
|
}
|
|
SCALE_X(otm->otmTextMetrics.tmAveCharWidth);
|
|
SCALE_X(otm->otmTextMetrics.tmMaxCharWidth);
|
|
|
|
SCALE_Y(otm->otmAscent);
|
|
SCALE_Y(otm->otmDescent);
|
|
SCALE_Y(otm->otmLineGap);
|
|
SCALE_Y(otm->otmsCapEmHeight);
|
|
SCALE_Y(otm->otmsXHeight);
|
|
SCALE_Y(otm->otmrcFontBox.top);
|
|
SCALE_Y(otm->otmrcFontBox.bottom);
|
|
SCALE_X(otm->otmrcFontBox.left);
|
|
SCALE_X(otm->otmrcFontBox.right);
|
|
SCALE_Y(otm->otmMacAscent);
|
|
SCALE_Y(otm->otmMacDescent);
|
|
SCALE_Y(otm->otmMacLineGap);
|
|
SCALE_X(otm->otmptSubscriptSize.x);
|
|
SCALE_Y(otm->otmptSubscriptSize.y);
|
|
SCALE_X(otm->otmptSubscriptOffset.x);
|
|
SCALE_Y(otm->otmptSubscriptOffset.y);
|
|
SCALE_X(otm->otmptSuperscriptSize.x);
|
|
SCALE_Y(otm->otmptSuperscriptSize.y);
|
|
SCALE_X(otm->otmptSuperscriptOffset.x);
|
|
SCALE_Y(otm->otmptSuperscriptOffset.y);
|
|
SCALE_Y(otm->otmsStrikeoutSize);
|
|
SCALE_Y(otm->otmsStrikeoutPosition);
|
|
SCALE_Y(otm->otmsUnderscoreSize);
|
|
SCALE_Y(otm->otmsUnderscorePosition);
|
|
|
|
#undef SCALE_X
|
|
#undef SCALE_Y
|
|
}
|
|
|
|
/*************************************************************
|
|
* font_GetOutlineTextMetrics
|
|
*/
|
|
static UINT CDECL font_GetOutlineTextMetrics( PHYSDEV dev, UINT size, OUTLINETEXTMETRICW *metrics )
|
|
{
|
|
struct font_physdev *physdev = get_font_dev( dev );
|
|
UINT ret = 0;
|
|
|
|
if (!physdev->font)
|
|
{
|
|
dev = GET_NEXT_PHYSDEV( dev, pGetOutlineTextMetrics );
|
|
return dev->funcs->pGetOutlineTextMetrics( dev, size, metrics );
|
|
}
|
|
|
|
if (!physdev->font->scalable) return 0;
|
|
|
|
EnterCriticalSection( &font_cs );
|
|
if (font_funcs->set_outline_text_metrics( physdev->font ))
|
|
{
|
|
ret = physdev->font->otm.otmSize;
|
|
if (metrics && size >= physdev->font->otm.otmSize)
|
|
{
|
|
WCHAR *ptr = (WCHAR *)(metrics + 1);
|
|
*metrics = physdev->font->otm;
|
|
metrics->otmpFamilyName = (char *)ptr - (ULONG_PTR)metrics;
|
|
lstrcpyW( ptr, (WCHAR *)physdev->font->otm.otmpFamilyName );
|
|
ptr += lstrlenW(ptr) + 1;
|
|
metrics->otmpStyleName = (char *)ptr - (ULONG_PTR)metrics;
|
|
lstrcpyW( ptr, (WCHAR *)physdev->font->otm.otmpStyleName );
|
|
ptr += lstrlenW(ptr) + 1;
|
|
metrics->otmpFaceName = (char *)ptr - (ULONG_PTR)metrics;
|
|
lstrcpyW( ptr, (WCHAR *)physdev->font->otm.otmpFaceName );
|
|
ptr += lstrlenW(ptr) + 1;
|
|
metrics->otmpFullName = (char *)ptr - (ULONG_PTR)metrics;
|
|
lstrcpyW( ptr, (WCHAR *)physdev->font->otm.otmpFullName );
|
|
scale_outline_font_metrics( physdev->font, metrics );
|
|
}
|
|
}
|
|
LeaveCriticalSection( &font_cs );
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*************************************************************
|
|
* font_GetTextCharsetInfo
|
|
*/
|
|
static UINT CDECL font_GetTextCharsetInfo( PHYSDEV dev, FONTSIGNATURE *fs, DWORD flags )
|
|
{
|
|
struct font_physdev *physdev = get_font_dev( dev );
|
|
|
|
if (!physdev->font)
|
|
{
|
|
dev = GET_NEXT_PHYSDEV( dev, pGetTextCharsetInfo );
|
|
return dev->funcs->pGetTextCharsetInfo( dev, fs, flags );
|
|
}
|
|
if (fs) *fs = physdev->font->fs;
|
|
return physdev->font->charset;
|
|
}
|
|
|
|
|
|
/*************************************************************
|
|
* font_GetTextExtentExPoint
|
|
*/
|
|
static BOOL CDECL font_GetTextExtentExPoint( PHYSDEV dev, const WCHAR *str, INT count, INT *dxs )
|
|
{
|
|
struct font_physdev *physdev = get_font_dev( dev );
|
|
INT i, pos;
|
|
ABC abc;
|
|
|
|
if (!physdev->font)
|
|
{
|
|
dev = GET_NEXT_PHYSDEV( dev, pGetTextExtentExPoint );
|
|
return dev->funcs->pGetTextExtentExPoint( dev, str, count, dxs );
|
|
}
|
|
|
|
TRACE( "%p, %s, %d\n", physdev->font, debugstr_wn(str, count), count );
|
|
|
|
EnterCriticalSection( &font_cs );
|
|
for (i = pos = 0; i < count; i++)
|
|
{
|
|
get_glyph_outline( physdev->font, str[i], GGO_METRICS, NULL, &abc, 0, NULL, NULL );
|
|
pos += abc.abcA + abc.abcB + abc.abcC;
|
|
dxs[i] = pos;
|
|
}
|
|
LeaveCriticalSection( &font_cs );
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*************************************************************
|
|
* font_GetTextExtentExPointI
|
|
*/
|
|
static BOOL CDECL font_GetTextExtentExPointI( PHYSDEV dev, const WORD *indices, INT count, INT *dxs )
|
|
{
|
|
struct font_physdev *physdev = get_font_dev( dev );
|
|
INT i, pos;
|
|
ABC abc;
|
|
|
|
if (!physdev->font)
|
|
{
|
|
dev = GET_NEXT_PHYSDEV( dev, pGetTextExtentExPointI );
|
|
return dev->funcs->pGetTextExtentExPointI( dev, indices, count, dxs );
|
|
}
|
|
|
|
TRACE( "%p, %p, %d\n", physdev->font, indices, count );
|
|
|
|
EnterCriticalSection( &font_cs );
|
|
for (i = pos = 0; i < count; i++)
|
|
{
|
|
get_glyph_outline( physdev->font, indices[i], GGO_METRICS | GGO_GLYPH_INDEX,
|
|
NULL, &abc, 0, NULL, NULL );
|
|
pos += abc.abcA + abc.abcB + abc.abcC;
|
|
dxs[i] = pos;
|
|
}
|
|
LeaveCriticalSection( &font_cs );
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*************************************************************
|
|
* font_GetTextFace
|
|
*/
|
|
static INT CDECL font_GetTextFace( PHYSDEV dev, INT count, WCHAR *str )
|
|
{
|
|
struct font_physdev *physdev = get_font_dev( dev );
|
|
INT len;
|
|
|
|
if (!physdev->font)
|
|
{
|
|
dev = GET_NEXT_PHYSDEV( dev, pGetTextFace );
|
|
return dev->funcs->pGetTextFace( dev, count, str );
|
|
}
|
|
len = lstrlenW( get_gdi_font_name(physdev->font) ) + 1;
|
|
if (str)
|
|
{
|
|
lstrcpynW( str, get_gdi_font_name(physdev->font), count );
|
|
len = min( count, len );
|
|
}
|
|
return len;
|
|
}
|
|
|
|
|
|
static void scale_font_metrics( struct gdi_font *font, TEXTMETRICW *tm )
|
|
{
|
|
double scale_x, scale_y;
|
|
|
|
/* Make sure that the font has sane width/height ratio */
|
|
if (font->aveWidth && (font->aveWidth + tm->tmHeight - 1) / tm->tmHeight > 100)
|
|
{
|
|
WARN( "Ignoring too large font->aveWidth %d\n", font->aveWidth );
|
|
font->aveWidth = 0;
|
|
}
|
|
|
|
if (font->aveWidth)
|
|
{
|
|
scale_x = (double)font->aveWidth;
|
|
scale_x /= (double)font->otm.otmTextMetrics.tmAveCharWidth;
|
|
}
|
|
else
|
|
scale_x = font->scale_y;
|
|
|
|
scale_x *= fabs(font->matrix.eM11);
|
|
scale_y = font->scale_y * fabs(font->matrix.eM22);
|
|
|
|
#define SCALE_X(x) (x) = GDI_ROUND((x) * scale_x)
|
|
#define SCALE_Y(y) (y) = GDI_ROUND((y) * scale_y)
|
|
|
|
SCALE_Y(tm->tmHeight);
|
|
SCALE_Y(tm->tmAscent);
|
|
SCALE_Y(tm->tmDescent);
|
|
SCALE_Y(tm->tmInternalLeading);
|
|
SCALE_Y(tm->tmExternalLeading);
|
|
|
|
SCALE_X(tm->tmOverhang);
|
|
if (font->fake_bold)
|
|
{
|
|
if (!font->scalable) tm->tmOverhang++;
|
|
tm->tmAveCharWidth++;
|
|
tm->tmMaxCharWidth++;
|
|
}
|
|
SCALE_X(tm->tmAveCharWidth);
|
|
SCALE_X(tm->tmMaxCharWidth);
|
|
|
|
#undef SCALE_X
|
|
#undef SCALE_Y
|
|
}
|
|
|
|
/*************************************************************
|
|
* font_GetTextMetrics
|
|
*/
|
|
static BOOL CDECL font_GetTextMetrics( PHYSDEV dev, TEXTMETRICW *metrics )
|
|
{
|
|
struct font_physdev *physdev = get_font_dev( dev );
|
|
BOOL ret = FALSE;
|
|
|
|
if (!physdev->font)
|
|
{
|
|
dev = GET_NEXT_PHYSDEV( dev, pGetTextMetrics );
|
|
return dev->funcs->pGetTextMetrics( dev, metrics );
|
|
}
|
|
|
|
EnterCriticalSection( &font_cs );
|
|
if (font_funcs->set_outline_text_metrics( physdev->font ) ||
|
|
font_funcs->set_bitmap_text_metrics( physdev->font ))
|
|
{
|
|
*metrics = physdev->font->otm.otmTextMetrics;
|
|
scale_font_metrics( physdev->font, metrics );
|
|
ret = TRUE;
|
|
}
|
|
LeaveCriticalSection( &font_cs );
|
|
return ret;
|
|
}
|
|
|
|
|
|
static void get_nearest_charset( const WCHAR *family_name, struct gdi_font_face *face, CHARSETINFO *csi )
|
|
{
|
|
/* 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 */
|
|
|
|
int i;
|
|
|
|
if (translate_charset_info( (DWORD*)(INT_PTR)GetACP(), csi, TCI_SRCCODEPAGE ))
|
|
{
|
|
const struct gdi_font_link *font_link;
|
|
|
|
if (csi->fs.fsCsb[0] & face->fs.fsCsb[0]) return;
|
|
font_link = find_gdi_font_link(family_name);
|
|
if (font_link && (csi->fs.fsCsb[0] & font_link->fs.fsCsb[0])) return;
|
|
}
|
|
for (i = 0; i < 32; i++)
|
|
{
|
|
DWORD fs0 = 1u << i;
|
|
if (face->fs.fsCsb[0] & fs0)
|
|
{
|
|
if (translate_charset_info(&fs0, csi, TCI_SRCFONTSIG)) return;
|
|
FIXME("TCI failing on %x\n", fs0);
|
|
}
|
|
}
|
|
|
|
FIXME("returning DEFAULT_CHARSET face->fs.fsCsb[0] = %08x file = %s\n",
|
|
face->fs.fsCsb[0], debugstr_w(face->file));
|
|
csi->ciACP = GetACP();
|
|
csi->ciCharset = DEFAULT_CHARSET;
|
|
}
|
|
|
|
static struct gdi_font *select_font( LOGFONTW *lf, FMAT2 dcmat, BOOL can_use_bitmap )
|
|
{
|
|
struct gdi_font *font;
|
|
struct gdi_font_face *face;
|
|
INT height;
|
|
CHARSETINFO csi;
|
|
const WCHAR *orig_name = NULL;
|
|
|
|
/* 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 (!facename_compare( lf->lfFaceName, L"Symbol", -1 )) lf->lfCharSet = SYMBOL_CHARSET;
|
|
|
|
/* check the cache first */
|
|
if ((font = find_cached_gdi_font( lf, &dcmat, can_use_bitmap )))
|
|
{
|
|
TRACE( "returning cached gdiFont(%p)\n", font );
|
|
return font;
|
|
}
|
|
if (!(face = find_matching_face( lf, &csi, can_use_bitmap, &orig_name )))
|
|
{
|
|
FIXME( "can't find a single appropriate font - bailing\n" );
|
|
return NULL;
|
|
}
|
|
height = lf->lfHeight;
|
|
|
|
font = create_gdi_font( face, orig_name, lf );
|
|
font->matrix = dcmat;
|
|
font->can_use_bitmap = can_use_bitmap;
|
|
if (!csi.fs.fsCsb[0]) get_nearest_charset( face->family->family_name, face, &csi );
|
|
font->charset = csi.ciCharset;
|
|
font->codepage = csi.ciACP;
|
|
|
|
TRACE( "Chosen: %s (%s/%p:%u)\n", debugstr_w(face->full_name), debugstr_w(face->file),
|
|
face->data_ptr, face->face_index );
|
|
|
|
font->aveWidth = height ? lf->lfWidth : 0;
|
|
if (!face->scalable)
|
|
{
|
|
/* Windows uses integer scaling factors for bitmap fonts */
|
|
INT scale, scaled_height, diff;
|
|
struct gdi_font *cachedfont;
|
|
|
|
if (height > 0)
|
|
diff = height - (signed int)face->size.height;
|
|
else
|
|
diff = -height - ((signed int)face->size.height - face->size.internal_leading);
|
|
|
|
/* FIXME: rotation of bitmap fonts is ignored */
|
|
height = abs(GDI_ROUND( (double)height * font->matrix.eM22 ));
|
|
if (font->aveWidth)
|
|
font->aveWidth = (double)font->aveWidth * font->matrix.eM11;
|
|
font->matrix.eM11 = font->matrix.eM22 = 1.0;
|
|
dcmat.eM11 = dcmat.eM22 = 1.0;
|
|
/* As we changed the matrix, we need to search the cache for the font again,
|
|
* otherwise we might explode the cache. */
|
|
if ((cachedfont = find_cached_gdi_font( lf, &dcmat, can_use_bitmap )))
|
|
{
|
|
TRACE("Found cached font after non-scalable matrix rescale!\n");
|
|
free_gdi_font( font );
|
|
return cachedfont;
|
|
}
|
|
|
|
if (height != 0) height = diff;
|
|
height += face->size.height;
|
|
|
|
scale = (height + face->size.height - 1) / face->size.height;
|
|
scaled_height = scale * face->size.height;
|
|
/* Only jump to the next height if the difference <= 25% original height */
|
|
if (scale > 2 && scaled_height - height > face->size.height / 4) scale--;
|
|
/* The jump between unscaled and doubled is delayed by 1 */
|
|
else if (scale == 2 && scaled_height - height > (face->size.height / 4 - 1)) scale--;
|
|
font->scale_y = scale;
|
|
TRACE("font scale y: %d\n", font->scale_y);
|
|
}
|
|
|
|
if (!font_funcs->load_font( font ))
|
|
{
|
|
free_gdi_font( font );
|
|
return NULL;
|
|
}
|
|
|
|
if (face->flags & ADDFONT_VERTICAL_FONT) /* We need to try to load the GSUB table */
|
|
font->vert_feature = get_GSUB_vert_feature( font );
|
|
|
|
create_child_font_list( font );
|
|
|
|
TRACE( "caching: gdiFont=%p\n", font );
|
|
cache_gdi_font( font );
|
|
return font;
|
|
}
|
|
|
|
/*************************************************************
|
|
* font_SelectFont
|
|
*/
|
|
static HFONT CDECL font_SelectFont( PHYSDEV dev, HFONT hfont, UINT *aa_flags )
|
|
{
|
|
struct font_physdev *physdev = get_font_dev( dev );
|
|
struct gdi_font *font = NULL, *prev = physdev->font;
|
|
DC *dc = get_physdev_dc( dev );
|
|
|
|
if (hfont)
|
|
{
|
|
LOGFONTW lf;
|
|
FMAT2 dcmat;
|
|
BOOL can_use_bitmap = !!(GetDeviceCaps( dc->hSelf, TEXTCAPS ) & TC_RA_ABLE);
|
|
|
|
GetObjectW( hfont, sizeof(lf), &lf );
|
|
switch (lf.lfQuality)
|
|
{
|
|
case NONANTIALIASED_QUALITY:
|
|
if (!*aa_flags) *aa_flags = GGO_BITMAP;
|
|
break;
|
|
case ANTIALIASED_QUALITY:
|
|
if (!*aa_flags) *aa_flags = GGO_GRAY4_BITMAP;
|
|
break;
|
|
}
|
|
|
|
lf.lfWidth = abs(lf.lfWidth);
|
|
|
|
TRACE( "%s, h=%d, it=%d, weight=%d, PandF=%02x, charset=%d orient %d escapement %d\n",
|
|
debugstr_w(lf.lfFaceName), lf.lfHeight, lf.lfItalic,
|
|
lf.lfWeight, lf.lfPitchAndFamily, lf.lfCharSet, lf.lfOrientation,
|
|
lf.lfEscapement );
|
|
|
|
if (dc->attr->graphics_mode == GM_ADVANCED)
|
|
{
|
|
memcpy( &dcmat, &dc->xformWorld2Vport, sizeof(FMAT2) );
|
|
/* try to avoid not necessary glyph transformations */
|
|
if (dcmat.eM21 == 0.0 && dcmat.eM12 == 0.0 && dcmat.eM11 == dcmat.eM22)
|
|
{
|
|
lf.lfHeight *= fabs(dcmat.eM11);
|
|
lf.lfWidth *= fabs(dcmat.eM11);
|
|
dcmat.eM11 = dcmat.eM22 = dcmat.eM11 < 0 ? -1 : 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Windows 3.1 compatibility mode GM_COMPATIBLE has only limited font scaling abilities */
|
|
dcmat.eM11 = dcmat.eM22 = 1.0;
|
|
dcmat.eM21 = dcmat.eM12 = 0;
|
|
lf.lfOrientation = lf.lfEscapement;
|
|
if (dc->vport2WorldValid)
|
|
{
|
|
if (dc->xformWorld2Vport.eM11 * dc->xformWorld2Vport.eM22 < 0)
|
|
lf.lfOrientation = -lf.lfOrientation;
|
|
lf.lfHeight *= fabs(dc->xformWorld2Vport.eM22);
|
|
lf.lfWidth *= fabs(dc->xformWorld2Vport.eM22);
|
|
}
|
|
}
|
|
TRACE( "DC transform %f %f %f %f\n", dcmat.eM11, dcmat.eM12, dcmat.eM21, dcmat.eM22 );
|
|
|
|
EnterCriticalSection( &font_cs );
|
|
|
|
font = select_font( &lf, dcmat, can_use_bitmap );
|
|
|
|
if (font)
|
|
{
|
|
if (!*aa_flags) *aa_flags = font->aa_flags;
|
|
if (!*aa_flags)
|
|
{
|
|
if (lf.lfQuality == CLEARTYPE_QUALITY || lf.lfQuality == CLEARTYPE_NATURAL_QUALITY)
|
|
*aa_flags = subpixel_orientation;
|
|
else
|
|
*aa_flags = font_smoothing;
|
|
}
|
|
*aa_flags = font_funcs->get_aa_flags( font, *aa_flags, antialias_fakes );
|
|
}
|
|
TRACE( "%p %s %d aa %x\n", hfont, debugstr_w(lf.lfFaceName), lf.lfHeight, *aa_flags );
|
|
LeaveCriticalSection( &font_cs );
|
|
}
|
|
physdev->font = font;
|
|
if (prev) release_gdi_font( prev );
|
|
return font ? hfont : 0;
|
|
}
|
|
|
|
|
|
const struct gdi_dc_funcs font_driver =
|
|
{
|
|
NULL, /* pAbortDoc */
|
|
NULL, /* pAbortPath */
|
|
NULL, /* pAlphaBlend */
|
|
NULL, /* pAngleArc */
|
|
NULL, /* pArc */
|
|
NULL, /* pArcTo */
|
|
NULL, /* pBeginPath */
|
|
NULL, /* pBlendImage */
|
|
NULL, /* pChord */
|
|
NULL, /* pCloseFigure */
|
|
NULL, /* pCreateCompatibleDC */
|
|
font_CreateDC, /* pCreateDC */
|
|
font_DeleteDC, /* pDeleteDC */
|
|
NULL, /* pDeleteObject */
|
|
NULL, /* pDeviceCapabilities */
|
|
NULL, /* pEllipse */
|
|
NULL, /* pEndDoc */
|
|
NULL, /* pEndPage */
|
|
NULL, /* pEndPath */
|
|
font_EnumFonts, /* pEnumFonts */
|
|
NULL, /* pEnumICMProfiles */
|
|
NULL, /* pExtDeviceMode */
|
|
NULL, /* pExtEscape */
|
|
NULL, /* pExtFloodFill */
|
|
NULL, /* pExtTextOut */
|
|
NULL, /* pFillPath */
|
|
NULL, /* pFillRgn */
|
|
font_FontIsLinked, /* pFontIsLinked */
|
|
NULL, /* pFrameRgn */
|
|
NULL, /* pGetBoundsRect */
|
|
font_GetCharABCWidths, /* pGetCharABCWidths */
|
|
font_GetCharABCWidthsI, /* pGetCharABCWidthsI */
|
|
font_GetCharWidth, /* pGetCharWidth */
|
|
font_GetCharWidthInfo, /* pGetCharWidthInfo */
|
|
NULL, /* pGetDeviceCaps */
|
|
NULL, /* pGetDeviceGammaRamp */
|
|
font_GetFontData, /* pGetFontData */
|
|
font_GetFontRealizationInfo, /* pGetFontRealizationInfo */
|
|
font_GetFontUnicodeRanges, /* pGetFontUnicodeRanges */
|
|
font_GetGlyphIndices, /* pGetGlyphIndices */
|
|
font_GetGlyphOutline, /* pGetGlyphOutline */
|
|
NULL, /* pGetICMProfile */
|
|
NULL, /* pGetImage */
|
|
font_GetKerningPairs, /* pGetKerningPairs */
|
|
NULL, /* pGetNearestColor */
|
|
font_GetOutlineTextMetrics, /* pGetOutlineTextMetrics */
|
|
NULL, /* pGetPixel */
|
|
NULL, /* pGetSystemPaletteEntries */
|
|
font_GetTextCharsetInfo, /* pGetTextCharsetInfo */
|
|
font_GetTextExtentExPoint, /* pGetTextExtentExPoint */
|
|
font_GetTextExtentExPointI, /* pGetTextExtentExPointI */
|
|
font_GetTextFace, /* pGetTextFace */
|
|
font_GetTextMetrics, /* pGetTextMetrics */
|
|
NULL, /* pGradientFill */
|
|
NULL, /* pInvertRgn */
|
|
NULL, /* pLineTo */
|
|
NULL, /* pMoveTo */
|
|
NULL, /* pPaintRgn */
|
|
NULL, /* pPatBlt */
|
|
NULL, /* pPie */
|
|
NULL, /* pPolyBezier */
|
|
NULL, /* pPolyBezierTo */
|
|
NULL, /* pPolyDraw */
|
|
NULL, /* pPolyPolygon */
|
|
NULL, /* pPolyPolyline */
|
|
NULL, /* pPolylineTo */
|
|
NULL, /* pPutImage */
|
|
NULL, /* pRealizeDefaultPalette */
|
|
NULL, /* pRealizePalette */
|
|
NULL, /* pRectangle */
|
|
NULL, /* pResetDC */
|
|
NULL, /* pRoundRect */
|
|
NULL, /* pSelectBitmap */
|
|
NULL, /* pSelectBrush */
|
|
font_SelectFont, /* pSelectFont */
|
|
NULL, /* pSelectPen */
|
|
NULL, /* pSetBkColor */
|
|
NULL, /* pSetBoundsRect */
|
|
NULL, /* pSetDCBrushColor */
|
|
NULL, /* pSetDCPenColor */
|
|
NULL, /* pSetDIBitsToDevice */
|
|
NULL, /* pSetDeviceClipping */
|
|
NULL, /* pSetDeviceGammaRamp */
|
|
NULL, /* pSetPixel */
|
|
NULL, /* pSetTextColor */
|
|
NULL, /* pStartDoc */
|
|
NULL, /* pStartPage */
|
|
NULL, /* pStretchBlt */
|
|
NULL, /* pStretchDIBits */
|
|
NULL, /* pStrokeAndFillPath */
|
|
NULL, /* pStrokePath */
|
|
NULL, /* pUnrealizePalette */
|
|
NULL, /* pD3DKMTCheckVidPnExclusiveOwnership */
|
|
NULL, /* pD3DKMTSetVidPnSourceOwner */
|
|
NULL, /* wine_get_wgl_driver */
|
|
NULL, /* wine_get_vulkan_driver */
|
|
GDI_PRIORITY_FONT_DRV /* priority */
|
|
};
|
|
|
|
static DWORD get_key_value( HKEY key, const WCHAR *name, DWORD *value )
|
|
{
|
|
WCHAR buf[12];
|
|
DWORD count = sizeof(buf), type, err;
|
|
|
|
err = RegQueryValueExW( key, name, NULL, &type, (BYTE *)buf, &count );
|
|
if (!err)
|
|
{
|
|
if (type == REG_DWORD) memcpy( value, buf, sizeof(*value) );
|
|
else *value = wcstol( buf, NULL, 10 );
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static void init_font_options(void)
|
|
{
|
|
HKEY key;
|
|
DWORD i, type, size, val, gamma = 1400;
|
|
WCHAR buffer[20];
|
|
|
|
size = sizeof(buffer);
|
|
if (!RegQueryValueExW( wine_fonts_key, L"AntialiasFakeBoldOrItalic", NULL,
|
|
&type, (BYTE *)buffer, &size) && type == REG_SZ && size >= 1)
|
|
{
|
|
antialias_fakes = (wcschr(L"yYtT1", buffer[0]) != NULL);
|
|
}
|
|
|
|
if (!RegOpenKeyW( HKEY_CURRENT_USER, L"Control Panel\\Desktop", &key ))
|
|
{
|
|
/* FIXME: handle vertical orientations even though Windows doesn't */
|
|
if (!get_key_value( key, L"FontSmoothingOrientation", &val ))
|
|
{
|
|
switch (val)
|
|
{
|
|
case 0: /* FE_FONTSMOOTHINGORIENTATIONBGR */
|
|
subpixel_orientation = WINE_GGO_HBGR_BITMAP;
|
|
break;
|
|
case 1: /* FE_FONTSMOOTHINGORIENTATIONRGB */
|
|
subpixel_orientation = WINE_GGO_HRGB_BITMAP;
|
|
break;
|
|
}
|
|
}
|
|
if (!get_key_value( key, L"FontSmoothing", &val ) && val /* enabled */)
|
|
{
|
|
if (!get_key_value( key, L"FontSmoothingType", &val ) && val == 2 /* FE_FONTSMOOTHINGCLEARTYPE */)
|
|
font_smoothing = subpixel_orientation;
|
|
else
|
|
font_smoothing = GGO_GRAY4_BITMAP;
|
|
}
|
|
if (!get_key_value( key, L"FontSmoothingGamma", &val ) && val)
|
|
{
|
|
gamma = min( max( val, 1000 ), 2200 );
|
|
}
|
|
RegCloseKey( key );
|
|
}
|
|
|
|
/* Calibrating the difference between the registry value and the Wine gamma value.
|
|
This looks roughly similar to Windows Native with the same registry value.
|
|
MS GDI seems to be rasterizing the outline at a different rate than FreeType. */
|
|
gamma = 1000 * gamma / 1400;
|
|
if (gamma != 1000)
|
|
{
|
|
for (i = 0; i < 256; i++)
|
|
{
|
|
font_gamma_ramp.encode[i] = pow( i / 255., 1000. / gamma ) * 255. + .5;
|
|
font_gamma_ramp.decode[i] = pow( i / 255., gamma / 1000. ) * 255. + .5;
|
|
}
|
|
}
|
|
font_gamma_ramp.gamma = gamma;
|
|
TRACE("gamma %d\n", font_gamma_ramp.gamma);
|
|
}
|
|
|
|
|
|
/* compute positions for text rendering, in device coords */
|
|
static BOOL get_char_positions( DC *dc, const WCHAR *str, INT count, INT *dx, SIZE *size )
|
|
{
|
|
TEXTMETRICW tm;
|
|
PHYSDEV dev;
|
|
|
|
size->cx = size->cy = 0;
|
|
if (!count) return TRUE;
|
|
|
|
dev = GET_DC_PHYSDEV( dc, pGetTextMetrics );
|
|
dev->funcs->pGetTextMetrics( dev, &tm );
|
|
|
|
dev = GET_DC_PHYSDEV( dc, pGetTextExtentExPoint );
|
|
if (!dev->funcs->pGetTextExtentExPoint( dev, str, count, dx )) return FALSE;
|
|
|
|
if (dc->breakExtra || dc->breakRem)
|
|
{
|
|
int i, space = 0, rem = dc->breakRem;
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
if (str[i] == tm.tmBreakChar)
|
|
{
|
|
space += dc->breakExtra;
|
|
if (rem > 0)
|
|
{
|
|
space++;
|
|
rem--;
|
|
}
|
|
}
|
|
dx[i] += space;
|
|
}
|
|
}
|
|
size->cx = dx[count - 1];
|
|
size->cy = tm.tmHeight;
|
|
return TRUE;
|
|
}
|
|
|
|
/* compute positions for text rendering, in device coords */
|
|
static BOOL get_char_positions_indices( DC *dc, const WORD *indices, INT count, INT *dx, SIZE *size )
|
|
{
|
|
TEXTMETRICW tm;
|
|
PHYSDEV dev;
|
|
|
|
size->cx = size->cy = 0;
|
|
if (!count) return TRUE;
|
|
|
|
dev = GET_DC_PHYSDEV( dc, pGetTextMetrics );
|
|
dev->funcs->pGetTextMetrics( dev, &tm );
|
|
|
|
dev = GET_DC_PHYSDEV( dc, pGetTextExtentExPointI );
|
|
if (!dev->funcs->pGetTextExtentExPointI( dev, indices, count, dx )) return FALSE;
|
|
|
|
if (dc->breakExtra || dc->breakRem)
|
|
{
|
|
WORD space_index;
|
|
int i, space = 0, rem = dc->breakRem;
|
|
|
|
dev = GET_DC_PHYSDEV( dc, pGetGlyphIndices );
|
|
dev->funcs->pGetGlyphIndices( dev, &tm.tmBreakChar, 1, &space_index, 0 );
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
if (indices[i] == space_index)
|
|
{
|
|
space += dc->breakExtra;
|
|
if (rem > 0)
|
|
{
|
|
space++;
|
|
rem--;
|
|
}
|
|
}
|
|
dx[i] += space;
|
|
}
|
|
}
|
|
size->cx = dx[count - 1];
|
|
size->cy = tm.tmHeight;
|
|
return TRUE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* get_text_charset_info
|
|
*
|
|
* Internal version of GetTextCharsetInfo() that takes a DC pointer.
|
|
*/
|
|
static UINT get_text_charset_info(DC *dc, FONTSIGNATURE *fs, DWORD flags)
|
|
{
|
|
UINT ret = DEFAULT_CHARSET;
|
|
PHYSDEV dev;
|
|
|
|
dev = GET_DC_PHYSDEV( dc, pGetTextCharsetInfo );
|
|
ret = dev->funcs->pGetTextCharsetInfo( dev, fs, flags );
|
|
|
|
if (ret == DEFAULT_CHARSET && fs)
|
|
memset(fs, 0, sizeof(FONTSIGNATURE));
|
|
return ret;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* NtGdiGetTextCharsetInfo (win32u.@)
|
|
*/
|
|
UINT WINAPI NtGdiGetTextCharsetInfo( HDC hdc, FONTSIGNATURE *fs, DWORD flags )
|
|
{
|
|
UINT ret = DEFAULT_CHARSET;
|
|
DC *dc = get_dc_ptr(hdc);
|
|
|
|
if (dc)
|
|
{
|
|
ret = get_text_charset_info( dc, fs, flags );
|
|
release_dc_ptr( dc );
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* NtGdiHfontCreate (win32u.@)
|
|
*/
|
|
HFONT WINAPI NtGdiHfontCreate( const ENUMLOGFONTEXDVW *penumex, ULONG size, ULONG type,
|
|
ULONG flags, void *data )
|
|
{
|
|
HFONT hFont;
|
|
FONTOBJ *fontPtr;
|
|
const LOGFONTW *plf;
|
|
|
|
if (!penumex) return 0;
|
|
|
|
if (penumex->elfEnumLogfontEx.elfFullName[0] ||
|
|
penumex->elfEnumLogfontEx.elfStyle[0] ||
|
|
penumex->elfEnumLogfontEx.elfScript[0])
|
|
{
|
|
FIXME("some fields ignored. fullname=%s, style=%s, script=%s\n",
|
|
debugstr_w(penumex->elfEnumLogfontEx.elfFullName),
|
|
debugstr_w(penumex->elfEnumLogfontEx.elfStyle),
|
|
debugstr_w(penumex->elfEnumLogfontEx.elfScript));
|
|
}
|
|
|
|
plf = &penumex->elfEnumLogfontEx.elfLogFont;
|
|
if (!(fontPtr = HeapAlloc( GetProcessHeap(), 0, sizeof(*fontPtr) ))) return 0;
|
|
|
|
fontPtr->logfont = *plf;
|
|
|
|
if (!(hFont = alloc_gdi_handle( &fontPtr->obj, NTGDI_OBJ_FONT, &fontobj_funcs )))
|
|
{
|
|
HeapFree( GetProcessHeap(), 0, fontPtr );
|
|
return 0;
|
|
}
|
|
|
|
TRACE("(%d %d %d %d %x %d %x %d %d) %s %s %s %s => %p\n",
|
|
plf->lfHeight, plf->lfWidth,
|
|
plf->lfEscapement, plf->lfOrientation,
|
|
plf->lfPitchAndFamily,
|
|
plf->lfOutPrecision, plf->lfClipPrecision,
|
|
plf->lfQuality, plf->lfCharSet,
|
|
debugstr_w(plf->lfFaceName),
|
|
plf->lfWeight > 400 ? "Bold" : "",
|
|
plf->lfItalic ? "Italic" : "",
|
|
plf->lfUnderline ? "Underline" : "", hFont);
|
|
|
|
return hFont;
|
|
}
|
|
|
|
#define ASSOC_CHARSET_OEM 1
|
|
#define ASSOC_CHARSET_ANSI 2
|
|
#define ASSOC_CHARSET_SYMBOL 4
|
|
|
|
static DWORD get_associated_charset_info(void)
|
|
{
|
|
static DWORD associated_charset = -1;
|
|
|
|
if (associated_charset == -1)
|
|
{
|
|
HKEY hkey;
|
|
WCHAR dataW[32];
|
|
DWORD type, data_len;
|
|
|
|
associated_charset = 0;
|
|
|
|
if (RegOpenKeyW(HKEY_LOCAL_MACHINE,
|
|
L"System\\CurrentControlSet\\Control\\FontAssoc\\Associated Charset", &hkey))
|
|
return 0;
|
|
|
|
data_len = sizeof(dataW);
|
|
if (!RegQueryValueExW(hkey, L"ANSI(00)", NULL, &type, (LPBYTE)dataW, &data_len) &&
|
|
type == REG_SZ && !wcsicmp(dataW, L"yes"))
|
|
associated_charset |= ASSOC_CHARSET_ANSI;
|
|
|
|
data_len = sizeof(dataW);
|
|
if (!RegQueryValueExW(hkey, L"OEM(FF)", NULL, &type, (LPBYTE)dataW, &data_len) &&
|
|
type == REG_SZ && !wcsicmp(dataW, L"yes"))
|
|
associated_charset |= ASSOC_CHARSET_OEM;
|
|
|
|
data_len = sizeof(dataW);
|
|
if (!RegQueryValueExW(hkey, L"SYMBOL(02)", NULL, &type, (LPBYTE)dataW, &data_len) &&
|
|
type == REG_SZ && !wcsicmp(dataW, L"yes"))
|
|
associated_charset |= ASSOC_CHARSET_SYMBOL;
|
|
|
|
RegCloseKey(hkey);
|
|
|
|
TRACE("associated_charset = %d\n", associated_charset);
|
|
}
|
|
|
|
return associated_charset;
|
|
}
|
|
|
|
static void update_font_code_page( DC *dc, HANDLE font )
|
|
{
|
|
CHARSETINFO csi;
|
|
int charset = get_text_charset_info( dc, NULL, 0 );
|
|
|
|
if (charset == ANSI_CHARSET && get_associated_charset_info() & ASSOC_CHARSET_ANSI)
|
|
{
|
|
LOGFONTW lf;
|
|
|
|
GetObjectW( font, sizeof(lf), &lf );
|
|
if (!(lf.lfClipPrecision & CLIP_DFA_DISABLE))
|
|
charset = DEFAULT_CHARSET;
|
|
}
|
|
|
|
/* Hmm, nicely designed api this one! */
|
|
if (translate_charset_info( ULongToPtr(charset), &csi, TCI_SRCCHARSET) )
|
|
dc->attr->font_code_page = csi.ciACP;
|
|
else {
|
|
switch(charset) {
|
|
case OEM_CHARSET:
|
|
dc->attr->font_code_page = GetOEMCP();
|
|
break;
|
|
case DEFAULT_CHARSET:
|
|
dc->attr->font_code_page = GetACP();
|
|
break;
|
|
|
|
case VISCII_CHARSET:
|
|
case TCVN_CHARSET:
|
|
case KOI8_CHARSET:
|
|
case ISO3_CHARSET:
|
|
case ISO4_CHARSET:
|
|
case ISO10_CHARSET:
|
|
case CELTIC_CHARSET:
|
|
/* FIXME: These have no place here, but because x11drv
|
|
enumerates fonts with these (made up) charsets some apps
|
|
might use them and then the FIXME below would become
|
|
annoying. Now we could pick the intended codepage for
|
|
each of these, but since it's broken anyway we'll just
|
|
use CP_ACP and hope it'll go away...
|
|
*/
|
|
dc->attr->font_code_page = CP_ACP;
|
|
break;
|
|
|
|
default:
|
|
FIXME("Can't find codepage for charset %d\n", charset);
|
|
dc->attr->font_code_page = CP_ACP;
|
|
break;
|
|
}
|
|
}
|
|
|
|
TRACE( "charset %d => cp %d\n", charset, dc->attr->font_code_page );
|
|
}
|
|
|
|
/***********************************************************************
|
|
* NtGdiSelectFont (win32u.@)
|
|
*/
|
|
HGDIOBJ WINAPI NtGdiSelectFont( HDC hdc, HGDIOBJ handle )
|
|
{
|
|
HGDIOBJ ret = 0;
|
|
DC *dc = get_dc_ptr( hdc );
|
|
PHYSDEV physdev;
|
|
UINT aa_flags = 0;
|
|
|
|
if (!dc) return 0;
|
|
|
|
if (!GDI_inc_ref_count( handle ))
|
|
{
|
|
release_dc_ptr( dc );
|
|
return 0;
|
|
}
|
|
|
|
physdev = GET_DC_PHYSDEV( dc, pSelectFont );
|
|
if (physdev->funcs->pSelectFont( physdev, handle, &aa_flags ))
|
|
{
|
|
ret = dc->hFont;
|
|
dc->hFont = handle;
|
|
dc->aa_flags = aa_flags ? aa_flags : GGO_BITMAP;
|
|
update_font_code_page( dc, handle );
|
|
if (dc->font_gamma_ramp == NULL)
|
|
dc->font_gamma_ramp = &font_gamma_ramp;
|
|
GDI_dec_ref_count( ret );
|
|
}
|
|
else GDI_dec_ref_count( handle );
|
|
|
|
release_dc_ptr( dc );
|
|
return ret;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* FONT_GetObjectW
|
|
*/
|
|
static INT FONT_GetObjectW( HGDIOBJ handle, INT count, LPVOID buffer )
|
|
{
|
|
FONTOBJ *font = GDI_GetObjPtr( handle, NTGDI_OBJ_FONT );
|
|
|
|
if (!font) return 0;
|
|
if (buffer)
|
|
{
|
|
if (count > sizeof(LOGFONTW)) count = sizeof(LOGFONTW);
|
|
memcpy( buffer, &font->logfont, count );
|
|
}
|
|
else count = sizeof(LOGFONTW);
|
|
GDI_ReleaseObj( handle );
|
|
return count;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* FONT_DeleteObject
|
|
*/
|
|
static BOOL FONT_DeleteObject( HGDIOBJ handle )
|
|
{
|
|
FONTOBJ *obj;
|
|
|
|
if (!(obj = free_gdi_handle( handle ))) return FALSE;
|
|
HeapFree( GetProcessHeap(), 0, obj );
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
struct font_enum
|
|
{
|
|
HDC hdc;
|
|
struct font_enum_entry *buf;
|
|
ULONG size;
|
|
ULONG count;
|
|
ULONG charset;
|
|
};
|
|
|
|
static INT WINAPI font_enum_proc( const LOGFONTW *lf, const TEXTMETRICW *tm,
|
|
DWORD type, LPARAM lp )
|
|
{
|
|
struct font_enum *fe = (struct font_enum *)lp;
|
|
|
|
if (fe->charset != DEFAULT_CHARSET && lf->lfCharSet != fe->charset) return 1;
|
|
if ((type & RASTER_FONTTYPE) && !(NtGdiGetDeviceCaps( fe->hdc, TEXTCAPS ) & TC_RA_ABLE))
|
|
return 1;
|
|
|
|
if (fe->buf && fe->count < fe->size)
|
|
{
|
|
fe->buf[fe->count].type = type;
|
|
fe->buf[fe->count].lf = *(const ENUMLOGFONTEXW *)lf;
|
|
fe->buf[fe->count].tm = *(const NEWTEXTMETRICEXW *)tm;
|
|
}
|
|
fe->count++;
|
|
return 1;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* NtGdiEnumFonts (win32u.@)
|
|
*/
|
|
BOOL WINAPI NtGdiEnumFonts( HDC hdc, ULONG type, ULONG win32_compat, ULONG face_name_len,
|
|
const WCHAR *face_name, ULONG charset, ULONG *count, void *buf )
|
|
{
|
|
struct font_enum fe;
|
|
PHYSDEV physdev;
|
|
LOGFONTW lf;
|
|
BOOL ret;
|
|
DC *dc;
|
|
|
|
if (!(dc = get_dc_ptr( hdc ))) return 0;
|
|
|
|
memset( &lf, 0, sizeof(lf) );
|
|
lf.lfCharSet = charset;
|
|
if (face_name_len) memcpy( lf.lfFaceName, face_name, face_name_len * sizeof(WCHAR) );
|
|
|
|
fe.hdc = hdc;
|
|
fe.buf = buf;
|
|
fe.size = *count / sizeof(*fe.buf);
|
|
fe.count = 0;
|
|
fe.charset = charset;
|
|
|
|
physdev = GET_DC_PHYSDEV( dc, pEnumFonts );
|
|
ret = physdev->funcs->pEnumFonts( physdev, &lf, font_enum_proc, (LPARAM)&fe );
|
|
if (ret && buf) ret = fe.count <= fe.size;
|
|
*count = fe.count * sizeof(*fe.buf);
|
|
|
|
release_dc_ptr( dc );
|
|
return ret;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* NtGdiSetTextJustification (win32u.@)
|
|
*/
|
|
BOOL WINAPI NtGdiSetTextJustification( HDC hdc, INT extra, INT breaks )
|
|
{
|
|
DC *dc;
|
|
|
|
if (!(dc = get_dc_ptr( hdc ))) return FALSE;
|
|
|
|
extra = abs( (extra * dc->attr->vport_ext.cx + dc->attr->wnd_ext.cx / 2) /
|
|
dc->attr->wnd_ext.cx );
|
|
if (!extra) breaks = 0;
|
|
if (breaks)
|
|
{
|
|
dc->breakExtra = extra / breaks;
|
|
dc->breakRem = extra - (breaks * dc->breakExtra);
|
|
}
|
|
else
|
|
{
|
|
dc->breakExtra = 0;
|
|
dc->breakRem = 0;
|
|
}
|
|
|
|
release_dc_ptr( dc );
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* NtGdiGetTextFaceW (win32u.@)
|
|
*/
|
|
INT WINAPI NtGdiGetTextFaceW( HDC hdc, INT count, WCHAR *name, BOOL alias_name )
|
|
{
|
|
PHYSDEV dev;
|
|
INT ret;
|
|
|
|
DC * dc = get_dc_ptr( hdc );
|
|
if (!dc) return 0;
|
|
|
|
dev = GET_DC_PHYSDEV( dc, pGetTextFace );
|
|
ret = dev->funcs->pGetTextFace( dev, count, name );
|
|
release_dc_ptr( dc );
|
|
return ret;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* NtGdiGetTextExtentExW (win32u.@)
|
|
*
|
|
* Return the size of the string as it would be if it was output properly by
|
|
* e.g. TextOut.
|
|
*/
|
|
BOOL WINAPI NtGdiGetTextExtentExW( HDC hdc, const WCHAR *str, INT count, INT max_ext,
|
|
INT *nfit, INT *dxs, SIZE *size, UINT flags )
|
|
{
|
|
DC *dc;
|
|
int i;
|
|
BOOL ret;
|
|
INT buffer[256], *pos = dxs;
|
|
|
|
if (count < 0) return FALSE;
|
|
|
|
dc = get_dc_ptr(hdc);
|
|
if (!dc) return FALSE;
|
|
|
|
if (!dxs)
|
|
{
|
|
pos = buffer;
|
|
if (count > 256 && !(pos = HeapAlloc( GetProcessHeap(), 0, count * sizeof(*pos) )))
|
|
{
|
|
release_dc_ptr( dc );
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
if (flags)
|
|
ret = get_char_positions_indices( dc, str, count, pos, size );
|
|
else
|
|
ret = get_char_positions( dc, str, count, pos, size );
|
|
if (ret)
|
|
{
|
|
if (dxs || nfit)
|
|
{
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
unsigned int dx = abs( INTERNAL_XDSTOWS( dc, pos[i] )) +
|
|
(i + 1) * dc->attr->char_extra;
|
|
if (nfit && dx > (unsigned int)max_ext) break;
|
|
if (dxs) dxs[i] = dx;
|
|
}
|
|
if (nfit) *nfit = i;
|
|
}
|
|
|
|
size->cx = abs( INTERNAL_XDSTOWS( dc, size->cx )) + count * dc->attr->char_extra;
|
|
size->cy = abs( INTERNAL_YDSTOWS( dc, size->cy ));
|
|
}
|
|
|
|
if (pos != buffer && pos != dxs) HeapFree( GetProcessHeap(), 0, pos );
|
|
release_dc_ptr( dc );
|
|
|
|
TRACE("(%p, %s, %d) returning %dx%d\n", hdc, debugstr_wn(str,count), max_ext, size->cx, size->cy );
|
|
return ret;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* NtGdiGetTextMetricsW (win32u.@)
|
|
*/
|
|
BOOL WINAPI NtGdiGetTextMetricsW( HDC hdc, TEXTMETRICW *metrics, ULONG flags )
|
|
{
|
|
PHYSDEV physdev;
|
|
BOOL ret = FALSE;
|
|
DC * dc = get_dc_ptr( hdc );
|
|
if (!dc) return FALSE;
|
|
|
|
physdev = GET_DC_PHYSDEV( dc, pGetTextMetrics );
|
|
ret = physdev->funcs->pGetTextMetrics( physdev, metrics );
|
|
|
|
if (ret)
|
|
{
|
|
/* device layer returns values in device units
|
|
* therefore we have to convert them to logical */
|
|
|
|
metrics->tmDigitizedAspectX = GetDeviceCaps(hdc, LOGPIXELSX);
|
|
metrics->tmDigitizedAspectY = GetDeviceCaps(hdc, LOGPIXELSY);
|
|
metrics->tmHeight = height_to_LP( dc, metrics->tmHeight );
|
|
metrics->tmAscent = height_to_LP( dc, metrics->tmAscent );
|
|
metrics->tmDescent = height_to_LP( dc, metrics->tmDescent );
|
|
metrics->tmInternalLeading = height_to_LP( dc, metrics->tmInternalLeading );
|
|
metrics->tmExternalLeading = height_to_LP( dc, metrics->tmExternalLeading );
|
|
metrics->tmAveCharWidth = width_to_LP( dc, metrics->tmAveCharWidth );
|
|
metrics->tmMaxCharWidth = width_to_LP( dc, metrics->tmMaxCharWidth );
|
|
metrics->tmOverhang = width_to_LP( dc, metrics->tmOverhang );
|
|
ret = TRUE;
|
|
|
|
TRACE("text metrics:\n"
|
|
" Weight = %03i\t FirstChar = %i\t AveCharWidth = %i\n"
|
|
" Italic = % 3i\t LastChar = %i\t\t MaxCharWidth = %i\n"
|
|
" UnderLined = %01i\t DefaultChar = %i\t Overhang = %i\n"
|
|
" StruckOut = %01i\t BreakChar = %i\t CharSet = %i\n"
|
|
" PitchAndFamily = %02x\n"
|
|
" --------------------\n"
|
|
" InternalLeading = %i\n"
|
|
" Ascent = %i\n"
|
|
" Descent = %i\n"
|
|
" Height = %i\n",
|
|
metrics->tmWeight, metrics->tmFirstChar, metrics->tmAveCharWidth,
|
|
metrics->tmItalic, metrics->tmLastChar, metrics->tmMaxCharWidth,
|
|
metrics->tmUnderlined, metrics->tmDefaultChar, metrics->tmOverhang,
|
|
metrics->tmStruckOut, metrics->tmBreakChar, metrics->tmCharSet,
|
|
metrics->tmPitchAndFamily,
|
|
metrics->tmInternalLeading,
|
|
metrics->tmAscent,
|
|
metrics->tmDescent,
|
|
metrics->tmHeight );
|
|
}
|
|
release_dc_ptr( dc );
|
|
return ret;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* NtGdiGetOutlineTextMetricsInternalW (win32u.@)
|
|
*/
|
|
UINT WINAPI NtGdiGetOutlineTextMetricsInternalW( HDC hdc, UINT cbData,
|
|
OUTLINETEXTMETRICW *lpOTM, ULONG opts )
|
|
{
|
|
DC *dc = get_dc_ptr( hdc );
|
|
OUTLINETEXTMETRICW *output = lpOTM;
|
|
PHYSDEV dev;
|
|
UINT ret;
|
|
|
|
TRACE("(%p,%d,%p)\n", hdc, cbData, lpOTM);
|
|
if(!dc) return 0;
|
|
|
|
dev = GET_DC_PHYSDEV( dc, pGetOutlineTextMetrics );
|
|
ret = dev->funcs->pGetOutlineTextMetrics( dev, cbData, output );
|
|
|
|
if (lpOTM && ret > cbData)
|
|
{
|
|
output = HeapAlloc(GetProcessHeap(), 0, ret);
|
|
ret = dev->funcs->pGetOutlineTextMetrics( dev, ret, output );
|
|
}
|
|
|
|
if (lpOTM && ret)
|
|
{
|
|
output->otmTextMetrics.tmDigitizedAspectX = GetDeviceCaps(hdc, LOGPIXELSX);
|
|
output->otmTextMetrics.tmDigitizedAspectY = GetDeviceCaps(hdc, LOGPIXELSY);
|
|
output->otmTextMetrics.tmHeight = height_to_LP( dc, output->otmTextMetrics.tmHeight );
|
|
output->otmTextMetrics.tmAscent = height_to_LP( dc, output->otmTextMetrics.tmAscent );
|
|
output->otmTextMetrics.tmDescent = height_to_LP( dc, output->otmTextMetrics.tmDescent );
|
|
output->otmTextMetrics.tmInternalLeading = height_to_LP( dc, output->otmTextMetrics.tmInternalLeading );
|
|
output->otmTextMetrics.tmExternalLeading = height_to_LP( dc, output->otmTextMetrics.tmExternalLeading );
|
|
output->otmTextMetrics.tmAveCharWidth = width_to_LP( dc, output->otmTextMetrics.tmAveCharWidth );
|
|
output->otmTextMetrics.tmMaxCharWidth = width_to_LP( dc, output->otmTextMetrics.tmMaxCharWidth );
|
|
output->otmTextMetrics.tmOverhang = width_to_LP( dc, output->otmTextMetrics.tmOverhang );
|
|
output->otmAscent = height_to_LP( dc, output->otmAscent);
|
|
output->otmDescent = height_to_LP( dc, output->otmDescent);
|
|
output->otmLineGap = INTERNAL_YDSTOWS(dc, output->otmLineGap);
|
|
output->otmsCapEmHeight = INTERNAL_YDSTOWS(dc, output->otmsCapEmHeight);
|
|
output->otmsXHeight = INTERNAL_YDSTOWS(dc, output->otmsXHeight);
|
|
output->otmrcFontBox.top = height_to_LP( dc, output->otmrcFontBox.top);
|
|
output->otmrcFontBox.bottom = height_to_LP( dc, output->otmrcFontBox.bottom);
|
|
output->otmrcFontBox.left = width_to_LP( dc, output->otmrcFontBox.left);
|
|
output->otmrcFontBox.right = width_to_LP( dc, output->otmrcFontBox.right);
|
|
output->otmMacAscent = height_to_LP( dc, output->otmMacAscent);
|
|
output->otmMacDescent = height_to_LP( dc, output->otmMacDescent);
|
|
output->otmMacLineGap = INTERNAL_YDSTOWS(dc, output->otmMacLineGap);
|
|
output->otmptSubscriptSize.x = width_to_LP( dc, output->otmptSubscriptSize.x);
|
|
output->otmptSubscriptSize.y = height_to_LP( dc, output->otmptSubscriptSize.y);
|
|
output->otmptSubscriptOffset.x = width_to_LP( dc, output->otmptSubscriptOffset.x);
|
|
output->otmptSubscriptOffset.y = height_to_LP( dc, output->otmptSubscriptOffset.y);
|
|
output->otmptSuperscriptSize.x = width_to_LP( dc, output->otmptSuperscriptSize.x);
|
|
output->otmptSuperscriptSize.y = height_to_LP( dc, output->otmptSuperscriptSize.y);
|
|
output->otmptSuperscriptOffset.x = width_to_LP( dc, output->otmptSuperscriptOffset.x);
|
|
output->otmptSuperscriptOffset.y = height_to_LP( dc, output->otmptSuperscriptOffset.y);
|
|
output->otmsStrikeoutSize = INTERNAL_YDSTOWS(dc, output->otmsStrikeoutSize);
|
|
output->otmsStrikeoutPosition = height_to_LP( dc, output->otmsStrikeoutPosition);
|
|
output->otmsUnderscoreSize = height_to_LP( dc, output->otmsUnderscoreSize);
|
|
output->otmsUnderscorePosition = height_to_LP( dc, output->otmsUnderscorePosition);
|
|
|
|
if(output != lpOTM)
|
|
{
|
|
memcpy(lpOTM, output, cbData);
|
|
HeapFree(GetProcessHeap(), 0, output);
|
|
ret = cbData;
|
|
}
|
|
}
|
|
release_dc_ptr(dc);
|
|
return ret;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* NtGdiGetCharWidthW (win32u.@)
|
|
*/
|
|
BOOL WINAPI NtGdiGetCharWidthW( HDC hdc, UINT first, UINT last, WCHAR *chars,
|
|
ULONG flags, void *buf )
|
|
{
|
|
UINT i, count = last;
|
|
BOOL ret;
|
|
PHYSDEV dev;
|
|
DC *dc;
|
|
|
|
if (flags & NTGDI_GETCHARWIDTH_INDICES)
|
|
{
|
|
ABC *abc;
|
|
unsigned int i;
|
|
|
|
if (!(abc = HeapAlloc(GetProcessHeap(), 0, count * sizeof(ABC))))
|
|
return FALSE;
|
|
|
|
if (!NtGdiGetCharABCWidthsW( hdc, first, last, chars,
|
|
NTGDI_GETCHARABCWIDTHS_INT | NTGDI_GETCHARABCWIDTHS_INDICES,
|
|
abc ))
|
|
{
|
|
HeapFree( GetProcessHeap(), 0, abc );
|
|
return FALSE;
|
|
}
|
|
|
|
for (i = 0; i < count; i++)
|
|
((INT *)buf)[i] = abc[i].abcA + abc[i].abcB + abc[i].abcC;
|
|
|
|
HeapFree(GetProcessHeap(), 0, abc);
|
|
return TRUE;
|
|
}
|
|
|
|
if (!(dc = get_dc_ptr( hdc ))) return FALSE;
|
|
|
|
if (!chars) count = last - first + 1;
|
|
dev = GET_DC_PHYSDEV( dc, pGetCharWidth );
|
|
ret = dev->funcs->pGetCharWidth( dev, first, count, chars, buf );
|
|
|
|
if (ret)
|
|
{
|
|
if (flags & NTGDI_GETCHARWIDTH_INT)
|
|
{
|
|
INT *buffer = buf;
|
|
/* convert device units to logical */
|
|
for (i = 0; i < count; i++)
|
|
buffer[i] = width_to_LP( dc, buffer[i] );
|
|
}
|
|
else
|
|
{
|
|
float scale = fabs( dc->xformVport2World.eM11 ) / 16.0f;
|
|
for (i = 0; i < count; i++)
|
|
((float *)buf)[i] = ((int *)buf)[i] * scale;
|
|
}
|
|
}
|
|
release_dc_ptr( dc );
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* helper for nulldrv_ExtTextOut */
|
|
static DWORD get_glyph_bitmap( HDC hdc, UINT index, UINT flags, UINT aa_flags,
|
|
GLYPHMETRICS *metrics, struct gdi_image_bits *image )
|
|
{
|
|
UINT indices[3] = {0, 0, 0x20};
|
|
unsigned int i;
|
|
DWORD ret, size;
|
|
int stride;
|
|
|
|
indices[0] = index;
|
|
if (flags & ETO_GLYPH_INDEX) aa_flags |= GGO_GLYPH_INDEX;
|
|
|
|
for (i = 0; i < ARRAY_SIZE( indices ); i++)
|
|
{
|
|
index = indices[i];
|
|
ret = GetGlyphOutlineW( hdc, index, aa_flags, metrics, 0, NULL, &identity );
|
|
if (ret != GDI_ERROR) break;
|
|
}
|
|
|
|
if (ret == GDI_ERROR) return ERROR_NOT_FOUND;
|
|
if (!image) return ERROR_SUCCESS;
|
|
|
|
image->ptr = NULL;
|
|
image->free = NULL;
|
|
if (!ret) /* empty glyph */
|
|
{
|
|
metrics->gmBlackBoxX = metrics->gmBlackBoxY = 0;
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
stride = get_dib_stride( metrics->gmBlackBoxX, 1 );
|
|
size = metrics->gmBlackBoxY * stride;
|
|
|
|
if (!(image->ptr = HeapAlloc( GetProcessHeap(), 0, size ))) return ERROR_OUTOFMEMORY;
|
|
image->is_copy = TRUE;
|
|
image->free = free_heap_bits;
|
|
|
|
ret = GetGlyphOutlineW( hdc, index, aa_flags, metrics, size, image->ptr, &identity );
|
|
if (ret == GDI_ERROR)
|
|
{
|
|
HeapFree( GetProcessHeap(), 0, image->ptr );
|
|
return ERROR_NOT_FOUND;
|
|
}
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
/* helper for nulldrv_ExtTextOut */
|
|
static RECT get_total_extents( HDC hdc, INT x, INT y, UINT flags, UINT aa_flags,
|
|
LPCWSTR str, UINT count, const INT *dx )
|
|
{
|
|
UINT i;
|
|
RECT rect, bounds;
|
|
|
|
reset_bounds( &bounds );
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
GLYPHMETRICS metrics;
|
|
|
|
if (get_glyph_bitmap( hdc, str[i], flags, aa_flags, &metrics, NULL )) continue;
|
|
|
|
rect.left = x + metrics.gmptGlyphOrigin.x;
|
|
rect.top = y - metrics.gmptGlyphOrigin.y;
|
|
rect.right = rect.left + metrics.gmBlackBoxX;
|
|
rect.bottom = rect.top + metrics.gmBlackBoxY;
|
|
add_bounds_rect( &bounds, &rect );
|
|
|
|
if (dx)
|
|
{
|
|
if (flags & ETO_PDY)
|
|
{
|
|
x += dx[ i * 2 ];
|
|
y += dx[ i * 2 + 1];
|
|
}
|
|
else x += dx[ i ];
|
|
}
|
|
else
|
|
{
|
|
x += metrics.gmCellIncX;
|
|
y += metrics.gmCellIncY;
|
|
}
|
|
}
|
|
return bounds;
|
|
}
|
|
|
|
/* helper for nulldrv_ExtTextOut */
|
|
static void draw_glyph( DC *dc, INT origin_x, INT origin_y, const GLYPHMETRICS *metrics,
|
|
const struct gdi_image_bits *image, const RECT *clip )
|
|
{
|
|
static const BYTE masks[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
|
|
UINT i, count, max_count;
|
|
LONG x, y;
|
|
BYTE *ptr = image->ptr;
|
|
int stride = get_dib_stride( metrics->gmBlackBoxX, 1 );
|
|
POINT *pts;
|
|
RECT rect, clipped_rect;
|
|
|
|
rect.left = origin_x + metrics->gmptGlyphOrigin.x;
|
|
rect.top = origin_y - metrics->gmptGlyphOrigin.y;
|
|
rect.right = rect.left + metrics->gmBlackBoxX;
|
|
rect.bottom = rect.top + metrics->gmBlackBoxY;
|
|
if (!clip) clipped_rect = rect;
|
|
else if (!intersect_rect( &clipped_rect, &rect, clip )) return;
|
|
|
|
max_count = (metrics->gmBlackBoxX + 1) * metrics->gmBlackBoxY;
|
|
pts = HeapAlloc( GetProcessHeap(), 0, max_count * sizeof(*pts) );
|
|
if (!pts) return;
|
|
|
|
count = 0;
|
|
ptr += (clipped_rect.top - rect.top) * stride;
|
|
for (y = clipped_rect.top; y < clipped_rect.bottom; y++, ptr += stride)
|
|
{
|
|
for (x = clipped_rect.left - rect.left; x < clipped_rect.right - rect.left; x++)
|
|
{
|
|
while (x < clipped_rect.right - rect.left && !(ptr[x / 8] & masks[x % 8])) x++;
|
|
pts[count].x = rect.left + x;
|
|
while (x < clipped_rect.right - rect.left && (ptr[x / 8] & masks[x % 8])) x++;
|
|
pts[count + 1].x = rect.left + x;
|
|
if (pts[count + 1].x > pts[count].x)
|
|
{
|
|
pts[count].y = pts[count + 1].y = y;
|
|
count += 2;
|
|
}
|
|
}
|
|
}
|
|
assert( count <= max_count );
|
|
dp_to_lp( dc, pts, count );
|
|
for (i = 0; i < count; i += 2) Polyline( dc->hSelf, pts + i, 2 );
|
|
HeapFree( GetProcessHeap(), 0, pts );
|
|
}
|
|
|
|
/***********************************************************************
|
|
* nulldrv_ExtTextOut
|
|
*/
|
|
BOOL CDECL nulldrv_ExtTextOut( PHYSDEV dev, INT x, INT y, UINT flags, const RECT *rect,
|
|
LPCWSTR str, UINT count, const INT *dx )
|
|
{
|
|
DC *dc = get_nulldrv_dc( dev );
|
|
UINT i;
|
|
DWORD err;
|
|
HGDIOBJ orig;
|
|
HPEN pen;
|
|
|
|
if (flags & ETO_OPAQUE)
|
|
{
|
|
RECT rc = *rect;
|
|
HBRUSH brush = CreateSolidBrush( NtGdiGetNearestColor( dev->hdc, dc->attr->background_color ));
|
|
|
|
if (brush)
|
|
{
|
|
orig = NtGdiSelectBrush( dev->hdc, brush );
|
|
dp_to_lp( dc, (POINT *)&rc, 2 );
|
|
NtGdiPatBlt( dev->hdc, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, PATCOPY );
|
|
NtGdiSelectBrush( dev->hdc, orig );
|
|
NtGdiDeleteObjectApp( brush );
|
|
}
|
|
}
|
|
|
|
if (!count) return TRUE;
|
|
|
|
if (dc->aa_flags != GGO_BITMAP)
|
|
{
|
|
char buffer[FIELD_OFFSET( BITMAPINFO, bmiColors[256] )];
|
|
BITMAPINFO *info = (BITMAPINFO *)buffer;
|
|
struct gdi_image_bits bits;
|
|
struct bitblt_coords src, dst;
|
|
PHYSDEV dst_dev;
|
|
/* FIXME Subpixel modes */
|
|
UINT aa_flags = GGO_GRAY4_BITMAP;
|
|
|
|
dst_dev = GET_DC_PHYSDEV( dc, pPutImage );
|
|
src.visrect = get_total_extents( dev->hdc, x, y, flags, aa_flags, str, count, dx );
|
|
if (flags & ETO_CLIPPED) intersect_rect( &src.visrect, &src.visrect, rect );
|
|
if (!clip_visrect( dc, &src.visrect, &src.visrect )) return TRUE;
|
|
|
|
/* FIXME: check for ETO_OPAQUE and avoid GetImage */
|
|
src.x = src.visrect.left;
|
|
src.y = src.visrect.top;
|
|
src.width = src.visrect.right - src.visrect.left;
|
|
src.height = src.visrect.bottom - src.visrect.top;
|
|
dst = src;
|
|
if ((flags & ETO_OPAQUE) && (src.visrect.left >= rect->left) && (src.visrect.top >= rect->top) &&
|
|
(src.visrect.right <= rect->right) && (src.visrect.bottom <= rect->bottom))
|
|
{
|
|
/* we can avoid the GetImage, just query the needed format */
|
|
memset( &info->bmiHeader, 0, sizeof(info->bmiHeader) );
|
|
info->bmiHeader.biSize = sizeof(info->bmiHeader);
|
|
info->bmiHeader.biWidth = src.width;
|
|
info->bmiHeader.biHeight = -src.height;
|
|
info->bmiHeader.biSizeImage = get_dib_image_size( info );
|
|
err = dst_dev->funcs->pPutImage( dst_dev, 0, info, NULL, NULL, NULL, 0 );
|
|
if (!err || err == ERROR_BAD_FORMAT)
|
|
{
|
|
/* make the source rectangle relative to the source bits */
|
|
src.x = src.y = 0;
|
|
src.visrect.left = src.visrect.top = 0;
|
|
src.visrect.right = src.width;
|
|
src.visrect.bottom = src.height;
|
|
|
|
bits.ptr = HeapAlloc( GetProcessHeap(), 0, info->bmiHeader.biSizeImage );
|
|
if (!bits.ptr) return ERROR_OUTOFMEMORY;
|
|
bits.is_copy = TRUE;
|
|
bits.free = free_heap_bits;
|
|
err = ERROR_SUCCESS;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PHYSDEV src_dev = GET_DC_PHYSDEV( dc, pGetImage );
|
|
err = src_dev->funcs->pGetImage( src_dev, info, &bits, &src );
|
|
if (!err && !bits.is_copy)
|
|
{
|
|
void *ptr = HeapAlloc( GetProcessHeap(), 0, info->bmiHeader.biSizeImage );
|
|
if (!ptr)
|
|
{
|
|
if (bits.free) bits.free( &bits );
|
|
return ERROR_OUTOFMEMORY;
|
|
}
|
|
memcpy( ptr, bits.ptr, info->bmiHeader.biSizeImage );
|
|
if (bits.free) bits.free( &bits );
|
|
bits.ptr = ptr;
|
|
bits.is_copy = TRUE;
|
|
bits.free = free_heap_bits;
|
|
}
|
|
}
|
|
if (!err)
|
|
{
|
|
/* make x,y relative to the image bits */
|
|
x += src.visrect.left - dst.visrect.left;
|
|
y += src.visrect.top - dst.visrect.top;
|
|
render_aa_text_bitmapinfo( dc, info, &bits, &src, x, y, flags,
|
|
aa_flags, str, count, dx );
|
|
err = dst_dev->funcs->pPutImage( dst_dev, 0, info, &bits, &src, &dst, SRCCOPY );
|
|
if (bits.free) bits.free( &bits );
|
|
return !err;
|
|
}
|
|
}
|
|
|
|
pen = CreatePen( PS_SOLID, 1, dc->attr->text_color );
|
|
orig = NtGdiSelectPen( dev->hdc, pen );
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
GLYPHMETRICS metrics;
|
|
struct gdi_image_bits image;
|
|
|
|
err = get_glyph_bitmap( dev->hdc, str[i], flags, GGO_BITMAP, &metrics, &image );
|
|
if (err) continue;
|
|
|
|
if (image.ptr) draw_glyph( dc, x, y, &metrics, &image, (flags & ETO_CLIPPED) ? rect : NULL );
|
|
if (image.free) image.free( &image );
|
|
|
|
if (dx)
|
|
{
|
|
if (flags & ETO_PDY)
|
|
{
|
|
x += dx[ i * 2 ];
|
|
y += dx[ i * 2 + 1];
|
|
}
|
|
else x += dx[ i ];
|
|
}
|
|
else
|
|
{
|
|
x += metrics.gmCellIncX;
|
|
y += metrics.gmCellIncY;
|
|
}
|
|
}
|
|
|
|
NtGdiSelectPen( dev->hdc, orig );
|
|
NtGdiDeleteObjectApp( pen );
|
|
return TRUE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* get_line_width
|
|
*
|
|
* Scale the underline / strikeout line width.
|
|
*/
|
|
static inline int get_line_width( DC *dc, int metric_size )
|
|
{
|
|
int width = abs( INTERNAL_YWSTODS( dc, metric_size ));
|
|
if (width == 0) width = 1;
|
|
if (metric_size < 0) width = -width;
|
|
return width;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* NtGdiExtTextOutW (win32u.@)
|
|
*
|
|
* Draws text using the currently selected font, background color, and text color.
|
|
*
|
|
*
|
|
* PARAMS
|
|
* x,y [I] coordinates of string
|
|
* flags [I]
|
|
* ETO_GRAYED - undocumented on MSDN
|
|
* ETO_OPAQUE - use background color for fill the rectangle
|
|
* ETO_CLIPPED - clipping text to the rectangle
|
|
* ETO_GLYPH_INDEX - Buffer is of glyph locations in fonts rather
|
|
* than encoded characters. Implies ETO_IGNORELANGUAGE
|
|
* ETO_RTLREADING - Paragraph is basically a right-to-left paragraph.
|
|
* Affects BiDi ordering
|
|
* ETO_IGNORELANGUAGE - Undocumented in MSDN - instructs ExtTextOut not to do BiDi reordering
|
|
* ETO_PDY - unimplemented
|
|
* ETO_NUMERICSLATIN - unimplemented always assumed -
|
|
* do not translate numbers into locale representations
|
|
* ETO_NUMERICSLOCAL - unimplemented - Numerals in Arabic/Farsi context should assume local form
|
|
* lprect [I] dimensions for clipping or/and opaquing
|
|
* str [I] text string
|
|
* count [I] number of symbols in string
|
|
* lpDx [I] optional parameter with distance between drawing characters
|
|
*
|
|
* RETURNS
|
|
* Success: TRUE
|
|
* Failure: FALSE
|
|
*/
|
|
BOOL WINAPI NtGdiExtTextOutW( HDC hdc, INT x, INT y, UINT flags, const RECT *lprect,
|
|
const WCHAR *str, UINT count, const INT *lpDx, DWORD cp )
|
|
{
|
|
BOOL ret = FALSE;
|
|
UINT align;
|
|
DWORD layout;
|
|
POINT pt;
|
|
TEXTMETRICW tm;
|
|
LOGFONTW lf;
|
|
double cosEsc, sinEsc;
|
|
INT char_extra;
|
|
SIZE sz;
|
|
RECT rc;
|
|
POINT *deltas = NULL, width = {0, 0};
|
|
DWORD type;
|
|
DC * dc = get_dc_ptr( hdc );
|
|
PHYSDEV physdev;
|
|
INT breakRem;
|
|
static int quietfixme = 0;
|
|
|
|
if (!dc) return FALSE;
|
|
if (count > INT_MAX) return FALSE;
|
|
|
|
align = dc->attr->text_align;
|
|
breakRem = dc->breakRem;
|
|
layout = dc->attr->layout;
|
|
|
|
if (quietfixme == 0 && flags & (ETO_NUMERICSLOCAL | ETO_NUMERICSLATIN))
|
|
{
|
|
FIXME("flags ETO_NUMERICSLOCAL | ETO_NUMERICSLATIN unimplemented\n");
|
|
quietfixme = 1;
|
|
}
|
|
|
|
update_dc( dc );
|
|
physdev = GET_DC_PHYSDEV( dc, pExtTextOut );
|
|
type = GetObjectType(hdc);
|
|
if(type == OBJ_METADC || type == OBJ_ENHMETADC)
|
|
{
|
|
ret = physdev->funcs->pExtTextOut( physdev, x, y, flags, lprect, str, count, lpDx );
|
|
release_dc_ptr( dc );
|
|
return ret;
|
|
}
|
|
|
|
if (flags & ETO_RTLREADING) align |= TA_RTLREADING;
|
|
if (layout & LAYOUT_RTL)
|
|
{
|
|
if ((align & TA_CENTER) != TA_CENTER) align ^= TA_RIGHT;
|
|
align ^= TA_RTLREADING;
|
|
}
|
|
|
|
TRACE("%p, %d, %d, %08x, %s, %s, %d, %p)\n", hdc, x, y, flags,
|
|
wine_dbgstr_rect(lprect), debugstr_wn(str, count), count, lpDx);
|
|
TRACE("align = %x bkmode = %x mapmode = %x\n", align, dc->attr->background_mode,
|
|
dc->attr->map_mode);
|
|
|
|
if(align & TA_UPDATECP)
|
|
{
|
|
pt = dc->attr->cur_pos;
|
|
x = pt.x;
|
|
y = pt.y;
|
|
}
|
|
|
|
GetTextMetricsW(hdc, &tm);
|
|
GetObjectW(dc->hFont, sizeof(lf), &lf);
|
|
|
|
if(!(tm.tmPitchAndFamily & TMPF_VECTOR)) /* Non-scalable fonts shouldn't be rotated */
|
|
lf.lfEscapement = 0;
|
|
|
|
if ((dc->attr->graphics_mode == GM_COMPATIBLE) &&
|
|
(dc->vport2WorldValid && dc->xformWorld2Vport.eM11 * dc->xformWorld2Vport.eM22 < 0))
|
|
{
|
|
lf.lfEscapement = -lf.lfEscapement;
|
|
}
|
|
|
|
if(lf.lfEscapement != 0)
|
|
{
|
|
cosEsc = cos(lf.lfEscapement * M_PI / 1800);
|
|
sinEsc = sin(lf.lfEscapement * M_PI / 1800);
|
|
}
|
|
else
|
|
{
|
|
cosEsc = 1;
|
|
sinEsc = 0;
|
|
}
|
|
|
|
if (lprect && (flags & (ETO_OPAQUE | ETO_CLIPPED)))
|
|
{
|
|
rc = *lprect;
|
|
lp_to_dp(dc, (POINT*)&rc, 2);
|
|
order_rect( &rc );
|
|
if (flags & ETO_OPAQUE)
|
|
physdev->funcs->pExtTextOut( physdev, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL );
|
|
}
|
|
else flags &= ~ETO_CLIPPED;
|
|
|
|
if(count == 0)
|
|
{
|
|
ret = TRUE;
|
|
goto done;
|
|
}
|
|
|
|
pt.x = x;
|
|
pt.y = y;
|
|
lp_to_dp(dc, &pt, 1);
|
|
x = pt.x;
|
|
y = pt.y;
|
|
|
|
char_extra = GetTextCharacterExtra(hdc);
|
|
if (char_extra && lpDx && GetDeviceCaps( hdc, TECHNOLOGY ) == DT_RASPRINTER)
|
|
char_extra = 0; /* Printer drivers don't add char_extra if lpDx is supplied */
|
|
|
|
if(char_extra || dc->breakExtra || breakRem || lpDx || lf.lfEscapement != 0)
|
|
{
|
|
UINT i;
|
|
POINT total = {0, 0}, desired[2];
|
|
|
|
deltas = HeapAlloc(GetProcessHeap(), 0, count * sizeof(*deltas));
|
|
if (lpDx)
|
|
{
|
|
if (flags & ETO_PDY)
|
|
{
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
deltas[i].x = lpDx[i * 2] + char_extra;
|
|
deltas[i].y = -lpDx[i * 2 + 1];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
deltas[i].x = lpDx[i] + char_extra;
|
|
deltas[i].y = 0;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
INT *dx = HeapAlloc( GetProcessHeap(), 0, count * sizeof(*dx) );
|
|
|
|
NtGdiGetTextExtentExW( hdc, str, count, -1, NULL, dx, &sz, !!(flags & ETO_GLYPH_INDEX) );
|
|
|
|
deltas[0].x = dx[0];
|
|
deltas[0].y = 0;
|
|
for (i = 1; i < count; i++)
|
|
{
|
|
deltas[i].x = dx[i] - dx[i - 1];
|
|
deltas[i].y = 0;
|
|
}
|
|
HeapFree( GetProcessHeap(), 0, dx );
|
|
}
|
|
|
|
for(i = 0; i < count; i++)
|
|
{
|
|
total.x += deltas[i].x;
|
|
total.y += deltas[i].y;
|
|
|
|
desired[0].x = desired[0].y = 0;
|
|
|
|
desired[1].x = cosEsc * total.x + sinEsc * total.y;
|
|
desired[1].y = -sinEsc * total.x + cosEsc * total.y;
|
|
|
|
lp_to_dp(dc, desired, 2);
|
|
desired[1].x -= desired[0].x;
|
|
desired[1].y -= desired[0].y;
|
|
|
|
if (dc->attr->graphics_mode == GM_COMPATIBLE)
|
|
{
|
|
if (dc->vport2WorldValid && dc->xformWorld2Vport.eM11 < 0)
|
|
desired[1].x = -desired[1].x;
|
|
if (dc->vport2WorldValid && dc->xformWorld2Vport.eM22 < 0)
|
|
desired[1].y = -desired[1].y;
|
|
}
|
|
|
|
deltas[i].x = desired[1].x - width.x;
|
|
deltas[i].y = desired[1].y - width.y;
|
|
|
|
width = desired[1];
|
|
}
|
|
flags |= ETO_PDY;
|
|
}
|
|
else
|
|
{
|
|
POINT desired[2];
|
|
|
|
if(flags & ETO_GLYPH_INDEX)
|
|
GetTextExtentPointI(hdc, str, count, &sz);
|
|
else
|
|
GetTextExtentPointW(hdc, str, count, &sz);
|
|
desired[0].x = desired[0].y = 0;
|
|
desired[1].x = sz.cx;
|
|
desired[1].y = 0;
|
|
lp_to_dp(dc, desired, 2);
|
|
desired[1].x -= desired[0].x;
|
|
desired[1].y -= desired[0].y;
|
|
|
|
if (dc->attr->graphics_mode == GM_COMPATIBLE)
|
|
{
|
|
if (dc->vport2WorldValid && dc->xformWorld2Vport.eM11 < 0)
|
|
desired[1].x = -desired[1].x;
|
|
if (dc->vport2WorldValid && dc->xformWorld2Vport.eM22 < 0)
|
|
desired[1].y = -desired[1].y;
|
|
}
|
|
width = desired[1];
|
|
}
|
|
|
|
tm.tmAscent = abs(INTERNAL_YWSTODS(dc, tm.tmAscent));
|
|
tm.tmDescent = abs(INTERNAL_YWSTODS(dc, tm.tmDescent));
|
|
switch( align & (TA_LEFT | TA_RIGHT | TA_CENTER) )
|
|
{
|
|
case TA_LEFT:
|
|
if (align & TA_UPDATECP)
|
|
{
|
|
pt.x = x + width.x;
|
|
pt.y = y + width.y;
|
|
dp_to_lp(dc, &pt, 1);
|
|
MoveToEx(hdc, pt.x, pt.y, NULL);
|
|
}
|
|
break;
|
|
|
|
case TA_CENTER:
|
|
x -= width.x / 2;
|
|
y -= width.y / 2;
|
|
break;
|
|
|
|
case TA_RIGHT:
|
|
x -= width.x;
|
|
y -= width.y;
|
|
if (align & TA_UPDATECP)
|
|
{
|
|
pt.x = x;
|
|
pt.y = y;
|
|
dp_to_lp(dc, &pt, 1);
|
|
MoveToEx(hdc, pt.x, pt.y, NULL);
|
|
}
|
|
break;
|
|
}
|
|
|
|
switch( align & (TA_TOP | TA_BOTTOM | TA_BASELINE) )
|
|
{
|
|
case TA_TOP:
|
|
y += tm.tmAscent * cosEsc;
|
|
x += tm.tmAscent * sinEsc;
|
|
break;
|
|
|
|
case TA_BOTTOM:
|
|
y -= tm.tmDescent * cosEsc;
|
|
x -= tm.tmDescent * sinEsc;
|
|
break;
|
|
|
|
case TA_BASELINE:
|
|
break;
|
|
}
|
|
|
|
if (dc->attr->background_mode != TRANSPARENT)
|
|
{
|
|
if(!((flags & ETO_CLIPPED) && (flags & ETO_OPAQUE)))
|
|
{
|
|
if(!(flags & ETO_OPAQUE) || !lprect ||
|
|
x < rc.left || x + width.x >= rc.right ||
|
|
y - tm.tmAscent < rc.top || y + tm.tmDescent >= rc.bottom)
|
|
{
|
|
RECT text_box;
|
|
text_box.left = x;
|
|
text_box.right = x + width.x;
|
|
text_box.top = y - tm.tmAscent;
|
|
text_box.bottom = y + tm.tmDescent;
|
|
|
|
if (flags & ETO_CLIPPED) intersect_rect( &text_box, &text_box, &rc );
|
|
if (!is_rect_empty( &text_box ))
|
|
physdev->funcs->pExtTextOut( physdev, 0, 0, ETO_OPAQUE, &text_box, NULL, 0, NULL );
|
|
}
|
|
}
|
|
}
|
|
|
|
ret = physdev->funcs->pExtTextOut( physdev, x, y, (flags & ~ETO_OPAQUE), &rc,
|
|
str, count, (INT*)deltas );
|
|
|
|
done:
|
|
HeapFree(GetProcessHeap(), 0, deltas);
|
|
|
|
if (ret && (lf.lfUnderline || lf.lfStrikeOut))
|
|
{
|
|
int underlinePos, strikeoutPos;
|
|
int underlineWidth, strikeoutWidth;
|
|
UINT size = NtGdiGetOutlineTextMetricsInternalW( hdc, 0, NULL, 0 );
|
|
OUTLINETEXTMETRICW* otm = NULL;
|
|
POINT pts[5];
|
|
HPEN hpen = NtGdiSelectPen( hdc, get_stock_object(NULL_PEN) );
|
|
HBRUSH hbrush = CreateSolidBrush( dc->attr->text_color );
|
|
|
|
hbrush = NtGdiSelectBrush(hdc, hbrush);
|
|
|
|
if(!size)
|
|
{
|
|
underlinePos = 0;
|
|
underlineWidth = tm.tmAscent / 20 + 1;
|
|
strikeoutPos = tm.tmAscent / 2;
|
|
strikeoutWidth = underlineWidth;
|
|
}
|
|
else
|
|
{
|
|
otm = HeapAlloc(GetProcessHeap(), 0, size);
|
|
NtGdiGetOutlineTextMetricsInternalW( hdc, size, otm, 0 );
|
|
underlinePos = abs( INTERNAL_YWSTODS( dc, otm->otmsUnderscorePosition ));
|
|
if (otm->otmsUnderscorePosition < 0) underlinePos = -underlinePos;
|
|
underlineWidth = get_line_width( dc, otm->otmsUnderscoreSize );
|
|
strikeoutPos = abs( INTERNAL_YWSTODS( dc, otm->otmsStrikeoutPosition ));
|
|
if (otm->otmsStrikeoutPosition < 0) strikeoutPos = -strikeoutPos;
|
|
strikeoutWidth = get_line_width( dc, otm->otmsStrikeoutSize );
|
|
HeapFree(GetProcessHeap(), 0, otm);
|
|
}
|
|
|
|
|
|
if (lf.lfUnderline)
|
|
{
|
|
const UINT cnt = 5;
|
|
pts[0].x = x - (underlinePos + underlineWidth / 2) * sinEsc;
|
|
pts[0].y = y - (underlinePos + underlineWidth / 2) * cosEsc;
|
|
pts[1].x = x + width.x - (underlinePos + underlineWidth / 2) * sinEsc;
|
|
pts[1].y = y + width.y - (underlinePos + underlineWidth / 2) * cosEsc;
|
|
pts[2].x = pts[1].x + underlineWidth * sinEsc;
|
|
pts[2].y = pts[1].y + underlineWidth * cosEsc;
|
|
pts[3].x = pts[0].x + underlineWidth * sinEsc;
|
|
pts[3].y = pts[0].y + underlineWidth * cosEsc;
|
|
pts[4].x = pts[0].x;
|
|
pts[4].y = pts[0].y;
|
|
dp_to_lp(dc, pts, 5);
|
|
NtGdiPolyPolyDraw( hdc, pts, &cnt, 1, NtGdiPolyPolygon );
|
|
}
|
|
|
|
if (lf.lfStrikeOut)
|
|
{
|
|
const UINT cnt = 5;
|
|
pts[0].x = x - (strikeoutPos + strikeoutWidth / 2) * sinEsc;
|
|
pts[0].y = y - (strikeoutPos + strikeoutWidth / 2) * cosEsc;
|
|
pts[1].x = x + width.x - (strikeoutPos + strikeoutWidth / 2) * sinEsc;
|
|
pts[1].y = y + width.y - (strikeoutPos + strikeoutWidth / 2) * cosEsc;
|
|
pts[2].x = pts[1].x + strikeoutWidth * sinEsc;
|
|
pts[2].y = pts[1].y + strikeoutWidth * cosEsc;
|
|
pts[3].x = pts[0].x + strikeoutWidth * sinEsc;
|
|
pts[3].y = pts[0].y + strikeoutWidth * cosEsc;
|
|
pts[4].x = pts[0].x;
|
|
pts[4].y = pts[0].y;
|
|
dp_to_lp(dc, pts, 5);
|
|
NtGdiPolyPolyDraw( hdc, pts, &cnt, 1, NtGdiPolyPolygon );
|
|
}
|
|
|
|
NtGdiSelectPen(hdc, hpen);
|
|
hbrush = NtGdiSelectBrush(hdc, hbrush);
|
|
NtGdiDeleteObjectApp( hbrush );
|
|
}
|
|
|
|
release_dc_ptr( dc );
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
* NtGdiGetCharABCWidthsW (win32u.@)
|
|
*
|
|
* Retrieves widths of characters in range.
|
|
*
|
|
* PARAMS
|
|
* hdc [I] Handle of device context
|
|
* firstChar [I] First character in range to query
|
|
* lastChar [I] Last character in range to query
|
|
* abc [O] Address of character-width structure
|
|
*
|
|
* NOTES
|
|
* Only works with TrueType fonts
|
|
*/
|
|
BOOL WINAPI NtGdiGetCharABCWidthsW( HDC hdc, UINT first, UINT last, WCHAR *chars,
|
|
ULONG flags, void *buffer )
|
|
{
|
|
DC *dc = get_dc_ptr(hdc);
|
|
PHYSDEV dev;
|
|
unsigned int i, count = last;
|
|
BOOL ret;
|
|
TEXTMETRICW tm;
|
|
|
|
if (!dc) return FALSE;
|
|
|
|
if (!buffer)
|
|
{
|
|
release_dc_ptr( dc );
|
|
return FALSE;
|
|
}
|
|
|
|
if (flags & NTGDI_GETCHARABCWIDTHS_INDICES)
|
|
{
|
|
dev = GET_DC_PHYSDEV( dc, pGetCharABCWidthsI );
|
|
ret = dev->funcs->pGetCharABCWidthsI( dev, first, count, chars, buffer );
|
|
}
|
|
else
|
|
{
|
|
if (flags & NTGDI_GETCHARABCWIDTHS_INT)
|
|
{
|
|
/* unlike float variant, this one is supposed to fail on non-scalable fonts */
|
|
dev = GET_DC_PHYSDEV( dc, pGetTextMetrics );
|
|
if (!dev->funcs->pGetTextMetrics( dev, &tm ) || !(tm.tmPitchAndFamily & TMPF_VECTOR))
|
|
{
|
|
release_dc_ptr( dc );
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (!chars) count = last - first + 1;
|
|
dev = GET_DC_PHYSDEV( dc, pGetCharABCWidths );
|
|
ret = dev->funcs->pGetCharABCWidths( dev, first, count, chars, buffer );
|
|
}
|
|
|
|
if (ret)
|
|
{
|
|
ABC *abc = buffer;
|
|
if (flags & NTGDI_GETCHARABCWIDTHS_INT)
|
|
{
|
|
/* convert device units to logical */
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
abc[i].abcA = width_to_LP( dc, abc[i].abcA );
|
|
abc[i].abcB = width_to_LP( dc, abc[i].abcB );
|
|
abc[i].abcC = width_to_LP( dc, abc[i].abcC );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* convert device units to logical */
|
|
FLOAT scale = fabs( dc->xformVport2World.eM11 );
|
|
ABCFLOAT *abcf = buffer;
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
abcf[i].abcfA = abc[i].abcA * scale;
|
|
abcf[i].abcfB = abc[i].abcB * scale;
|
|
abcf[i].abcfC = abc[i].abcC * scale;
|
|
}
|
|
}
|
|
}
|
|
|
|
release_dc_ptr( dc );
|
|
return ret;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* NtGdiGetGlyphOutlineW (win32u.@)
|
|
*/
|
|
DWORD WINAPI NtGdiGetGlyphOutlineW( HDC hdc, UINT ch, UINT format, GLYPHMETRICS *metrics,
|
|
DWORD size, void *buffer, const MAT2 *mat2,
|
|
BOOL ignore_rotation )
|
|
{
|
|
DC *dc;
|
|
DWORD ret;
|
|
PHYSDEV dev;
|
|
|
|
TRACE( "(%p, %04x, %04x, %p, %d, %p, %p)\n", hdc, ch, format, metrics, size, buffer, mat2 );
|
|
|
|
if (!mat2) return GDI_ERROR;
|
|
|
|
dc = get_dc_ptr(hdc);
|
|
if(!dc) return GDI_ERROR;
|
|
|
|
dev = GET_DC_PHYSDEV( dc, pGetGlyphOutline );
|
|
ret = dev->funcs->pGetGlyphOutline( dev, ch & 0xffff, format, metrics, size, buffer, mat2 );
|
|
release_dc_ptr( dc );
|
|
return ret;
|
|
}
|
|
|
|
|
|
#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 <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>
|
|
|
|
#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;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CreateScalableFontResourceW (GDI32.@)
|
|
*/
|
|
BOOL WINAPI CreateScalableFontResourceW( DWORD hidden, LPCWSTR resource_file,
|
|
LPCWSTR font_file, LPCWSTR font_path )
|
|
{
|
|
struct fontdir fontdir = { 0 };
|
|
struct gdi_font *font = NULL;
|
|
WCHAR path[MAX_PATH];
|
|
|
|
TRACE("(%d, %s, %s, %s)\n", hidden, debugstr_w(resource_file),
|
|
debugstr_w(font_file), debugstr_w(font_path) );
|
|
|
|
if (!font_funcs) return FALSE;
|
|
|
|
if (!font_file) goto done;
|
|
if (font_path && font_path[0])
|
|
{
|
|
int len = lstrlenW( font_path ) + lstrlenW( font_file ) + 2;
|
|
if (len > MAX_PATH) goto done;
|
|
lstrcpynW( path, font_path, MAX_PATH );
|
|
lstrcatW( path, L"\\" );
|
|
lstrcatW( path, font_file );
|
|
}
|
|
else if (!GetFullPathNameW( font_file, MAX_PATH, path, NULL )) goto done;
|
|
|
|
if (!(font = alloc_gdi_font( path, NULL, 0 ))) goto done;
|
|
font->lf.lfHeight = 100;
|
|
if (!font_funcs->load_font( font )) goto done;
|
|
if (!font_funcs->set_outline_text_metrics( font )) goto done;
|
|
|
|
if (!(font->otm.otmTextMetrics.tmPitchAndFamily & TMPF_TRUETYPE)) goto done;
|
|
|
|
fontdir.num_of_resources = 1;
|
|
fontdir.res_id = 0;
|
|
fontdir.dfVersion = 0x200;
|
|
fontdir.dfSize = sizeof(fontdir);
|
|
strcpy( fontdir.dfCopyright, "Wine fontdir" );
|
|
fontdir.dfType = 0x4003; /* 0x0080 set if private */
|
|
fontdir.dfPoints = font->otm.otmEMSquare;
|
|
fontdir.dfVertRes = 72;
|
|
fontdir.dfHorizRes = 72;
|
|
fontdir.dfAscent = font->otm.otmTextMetrics.tmAscent;
|
|
fontdir.dfInternalLeading = font->otm.otmTextMetrics.tmInternalLeading;
|
|
fontdir.dfExternalLeading = font->otm.otmTextMetrics.tmExternalLeading;
|
|
fontdir.dfItalic = font->otm.otmTextMetrics.tmItalic;
|
|
fontdir.dfUnderline = font->otm.otmTextMetrics.tmUnderlined;
|
|
fontdir.dfStrikeOut = font->otm.otmTextMetrics.tmStruckOut;
|
|
fontdir.dfWeight = font->otm.otmTextMetrics.tmWeight;
|
|
fontdir.dfCharSet = font->otm.otmTextMetrics.tmCharSet;
|
|
fontdir.dfPixWidth = 0;
|
|
fontdir.dfPixHeight = font->otm.otmTextMetrics.tmHeight;
|
|
fontdir.dfPitchAndFamily = font->otm.otmTextMetrics.tmPitchAndFamily;
|
|
fontdir.dfAvgWidth = font->otm.otmTextMetrics.tmAveCharWidth;
|
|
fontdir.dfMaxWidth = font->otm.otmTextMetrics.tmMaxCharWidth;
|
|
fontdir.dfFirstChar = font->otm.otmTextMetrics.tmFirstChar;
|
|
fontdir.dfLastChar = font->otm.otmTextMetrics.tmLastChar;
|
|
fontdir.dfDefaultChar = font->otm.otmTextMetrics.tmDefaultChar;
|
|
fontdir.dfBreakChar = font->otm.otmTextMetrics.tmBreakChar;
|
|
fontdir.dfWidthBytes = 0;
|
|
fontdir.dfDevice = 0;
|
|
fontdir.dfFace = FIELD_OFFSET( struct fontdir, szFaceName );
|
|
fontdir.dfReserved = 0;
|
|
WideCharToMultiByte( CP_ACP, 0, (WCHAR *)font->otm.otmpFamilyName, -1,
|
|
fontdir.szFaceName, LF_FACESIZE, NULL, NULL );
|
|
free_gdi_font( font );
|
|
|
|
if (hidden) fontdir.dfType |= 0x80;
|
|
return create_fot( resource_file, font_file, &fontdir );
|
|
|
|
done:
|
|
if (font) free_gdi_font( font );
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return FALSE;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* NtGdiGetKerningPairsW (win32u.@)
|
|
*/
|
|
DWORD WINAPI NtGdiGetKerningPairsW( HDC hdc, DWORD count, KERNINGPAIR *kern_pair )
|
|
{
|
|
DC *dc;
|
|
DWORD ret;
|
|
PHYSDEV dev;
|
|
|
|
TRACE( "(%p,%d,%p)\n", hdc, count, kern_pair );
|
|
|
|
if (!count && kern_pair)
|
|
{
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return 0;
|
|
}
|
|
|
|
dc = get_dc_ptr( hdc );
|
|
if (!dc) return 0;
|
|
|
|
dev = GET_DC_PHYSDEV( dc, pGetKerningPairs );
|
|
ret = dev->funcs->pGetKerningPairs( dev, count, kern_pair );
|
|
release_dc_ptr( dc );
|
|
return ret;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* NtGdiGetFontData (win32u.@)
|
|
*
|
|
* Retrieve data for TrueType font.
|
|
*
|
|
* RETURNS
|
|
*
|
|
* success: Number of bytes returned
|
|
* failure: GDI_ERROR
|
|
*
|
|
* NOTES
|
|
*
|
|
* Calls SetLastError()
|
|
*
|
|
*/
|
|
DWORD WINAPI NtGdiGetFontData( HDC hdc, DWORD table, DWORD offset, void *buffer, DWORD length )
|
|
{
|
|
DC *dc = get_dc_ptr(hdc);
|
|
PHYSDEV dev;
|
|
DWORD ret;
|
|
|
|
if(!dc) return GDI_ERROR;
|
|
|
|
dev = GET_DC_PHYSDEV( dc, pGetFontData );
|
|
ret = dev->funcs->pGetFontData( dev, table, offset, buffer, length );
|
|
release_dc_ptr( dc );
|
|
return ret;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* NtGdiGetGlyphIndicesW (win32u.@)
|
|
*/
|
|
DWORD WINAPI NtGdiGetGlyphIndicesW( HDC hdc, const WCHAR *str, INT count,
|
|
WORD *indices, DWORD flags )
|
|
{
|
|
DC *dc = get_dc_ptr(hdc);
|
|
PHYSDEV dev;
|
|
DWORD ret;
|
|
|
|
TRACE( "(%p, %s, %d, %p, 0x%x)\n", hdc, debugstr_wn(str, count), count, indices, flags );
|
|
|
|
if(!dc) return GDI_ERROR;
|
|
|
|
dev = GET_DC_PHYSDEV( dc, pGetGlyphIndices );
|
|
ret = dev->funcs->pGetGlyphIndices( dev, str, count, indices, flags );
|
|
release_dc_ptr( dc );
|
|
return ret;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* *
|
|
* Font Resource API *
|
|
* *
|
|
***********************************************************************/
|
|
|
|
static BOOL CALLBACK load_enumed_resource(HMODULE hModule, LPCWSTR type, LPWSTR name, LONG_PTR lParam)
|
|
{
|
|
HRSRC rsrc = FindResourceW(hModule, name, type);
|
|
HGLOBAL hMem = LoadResource(hModule, rsrc);
|
|
LPVOID *pMem = LockResource(hMem);
|
|
int *num_total = (int *)lParam;
|
|
DWORD num_in_res;
|
|
|
|
TRACE("Found resource %s - trying to load\n", wine_dbgstr_w(type));
|
|
if (!AddFontMemResourceEx(pMem, SizeofResource(hModule, rsrc), NULL, &num_in_res))
|
|
{
|
|
ERR("Failed to load PE font resource mod=%p ptr=%p\n", hModule, hMem);
|
|
return FALSE;
|
|
}
|
|
|
|
*num_total += num_in_res;
|
|
return TRUE;
|
|
}
|
|
|
|
static void *map_file( const WCHAR *filename, LARGE_INTEGER *size )
|
|
{
|
|
HANDLE file, mapping;
|
|
void *ptr;
|
|
|
|
file = CreateFileW( filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
|
|
if (file == INVALID_HANDLE_VALUE) return NULL;
|
|
|
|
if (!GetFileSizeEx( file, size ) || size->u.HighPart)
|
|
{
|
|
CloseHandle( file );
|
|
return NULL;
|
|
}
|
|
|
|
mapping = CreateFileMappingW( file, NULL, PAGE_READONLY, 0, 0, NULL );
|
|
CloseHandle( file );
|
|
if (!mapping) return NULL;
|
|
|
|
ptr = MapViewOfFile( mapping, FILE_MAP_READ, 0, 0, 0 );
|
|
CloseHandle( mapping );
|
|
|
|
return ptr;
|
|
}
|
|
|
|
static void *find_resource( BYTE *ptr, WORD type, DWORD rsrc_off, DWORD size, DWORD *len )
|
|
{
|
|
WORD align, type_id, count;
|
|
DWORD res_off;
|
|
|
|
if (size < rsrc_off + 10) return NULL;
|
|
align = *(WORD *)(ptr + rsrc_off);
|
|
rsrc_off += 2;
|
|
type_id = *(WORD *)(ptr + rsrc_off);
|
|
while (type_id && type_id != type)
|
|
{
|
|
count = *(WORD *)(ptr + rsrc_off + 2);
|
|
rsrc_off += 8 + count * 12;
|
|
if (size < rsrc_off + 8) return NULL;
|
|
type_id = *(WORD *)(ptr + rsrc_off);
|
|
}
|
|
if (!type_id) return NULL;
|
|
count = *(WORD *)(ptr + rsrc_off + 2);
|
|
if (size < rsrc_off + 8 + count * 12) return NULL;
|
|
res_off = *(WORD *)(ptr + rsrc_off + 8) << align;
|
|
*len = *(WORD *)(ptr + rsrc_off + 10) << align;
|
|
if (size < res_off + *len) return NULL;
|
|
return ptr + res_off;
|
|
}
|
|
|
|
static WCHAR *get_scalable_filename( const WCHAR *res, BOOL *hidden )
|
|
{
|
|
LARGE_INTEGER size;
|
|
BYTE *ptr = map_file( res, &size );
|
|
const IMAGE_DOS_HEADER *dos;
|
|
const IMAGE_OS2_HEADER *ne;
|
|
WORD *fontdir;
|
|
char *data;
|
|
WCHAR *name = NULL;
|
|
DWORD len;
|
|
|
|
if (!ptr) return NULL;
|
|
|
|
if (size.u.LowPart < sizeof( *dos )) goto fail;
|
|
dos = (const IMAGE_DOS_HEADER *)ptr;
|
|
if (dos->e_magic != IMAGE_DOS_SIGNATURE) goto fail;
|
|
if (size.u.LowPart < dos->e_lfanew + sizeof( *ne )) goto fail;
|
|
ne = (const IMAGE_OS2_HEADER *)(ptr + dos->e_lfanew);
|
|
|
|
fontdir = find_resource( ptr, 0x8007, dos->e_lfanew + ne->ne_rsrctab, size.u.LowPart, &len );
|
|
if (!fontdir) goto fail;
|
|
*hidden = (fontdir[35] & 0x80) != 0; /* fontdir->dfType */
|
|
|
|
data = find_resource( ptr, 0x80cc, dos->e_lfanew + ne->ne_rsrctab, size.u.LowPart, &len );
|
|
if (!data) goto fail;
|
|
if (!memchr( data, 0, len )) goto fail;
|
|
|
|
len = MultiByteToWideChar( CP_ACP, 0, data, -1, NULL, 0 );
|
|
name = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
|
|
if (name) MultiByteToWideChar( CP_ACP, 0, data, -1, name, len );
|
|
|
|
fail:
|
|
UnmapViewOfFile( ptr );
|
|
return name;
|
|
}
|
|
|
|
static int add_system_font_resource( const WCHAR *file, DWORD flags )
|
|
{
|
|
WCHAR path[MAX_PATH];
|
|
int ret;
|
|
|
|
/* try in %WINDIR%/fonts, needed for Fotobuch Designer */
|
|
get_fonts_win_dir_path( file, path );
|
|
EnterCriticalSection( &font_cs );
|
|
ret = font_funcs->add_font( path, flags );
|
|
LeaveCriticalSection( &font_cs );
|
|
/* try in datadir/fonts (or builddir/fonts), needed for Magic the Gathering Online */
|
|
if (!ret)
|
|
{
|
|
get_fonts_data_dir_path( file, path );
|
|
EnterCriticalSection( &font_cs );
|
|
ret = font_funcs->add_font( path, flags );
|
|
LeaveCriticalSection( &font_cs );
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static BOOL remove_system_font_resource( LPCWSTR file, DWORD flags )
|
|
{
|
|
WCHAR path[MAX_PATH];
|
|
int ret;
|
|
|
|
get_fonts_win_dir_path( file, path );
|
|
if (!(ret = remove_font( path, flags )))
|
|
{
|
|
get_fonts_data_dir_path( file, path );
|
|
ret = remove_font( path, flags );
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int add_font_resource( LPCWSTR file, DWORD flags )
|
|
{
|
|
WCHAR path[MAX_PATH];
|
|
int ret = 0;
|
|
|
|
if (GetFullPathNameW( file, MAX_PATH, path, NULL ))
|
|
{
|
|
DWORD addfont_flags = ADDFONT_ALLOW_BITMAP | ADDFONT_ADD_RESOURCE;
|
|
|
|
if (!(flags & FR_PRIVATE)) addfont_flags |= ADDFONT_ADD_TO_CACHE;
|
|
EnterCriticalSection( &font_cs );
|
|
ret = font_funcs->add_font( path, addfont_flags );
|
|
LeaveCriticalSection( &font_cs );
|
|
}
|
|
|
|
if (!ret && !wcschr( file, '\\' ))
|
|
ret = add_system_font_resource( file, ADDFONT_ALLOW_BITMAP | ADDFONT_ADD_RESOURCE );
|
|
|
|
return ret;
|
|
}
|
|
|
|
static BOOL remove_font_resource( LPCWSTR file, DWORD flags )
|
|
{
|
|
WCHAR path[MAX_PATH];
|
|
BOOL ret = FALSE;
|
|
|
|
if (GetFullPathNameW( file, MAX_PATH, path, NULL ))
|
|
{
|
|
DWORD addfont_flags = ADDFONT_ALLOW_BITMAP | ADDFONT_ADD_RESOURCE;
|
|
|
|
if (!(flags & FR_PRIVATE)) addfont_flags |= ADDFONT_ADD_TO_CACHE;
|
|
ret = remove_font( path, addfont_flags );
|
|
}
|
|
|
|
if (!ret && !wcschr( file, '\\' ))
|
|
ret = remove_system_font_resource( file, ADDFONT_ALLOW_BITMAP | ADDFONT_ADD_RESOURCE );
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void load_system_bitmap_fonts(void)
|
|
{
|
|
static const WCHAR * const fonts[] = { L"FONTS.FON", L"OEMFONT.FON", L"FIXEDFON.FON" };
|
|
HKEY hkey;
|
|
WCHAR data[MAX_PATH];
|
|
DWORD i, dlen, type;
|
|
|
|
if (RegOpenKeyW( HKEY_CURRENT_CONFIG, L"Software\\Fonts", &hkey )) return;
|
|
for (i = 0; i < ARRAY_SIZE(fonts); i++)
|
|
{
|
|
dlen = sizeof(data);
|
|
if (!RegQueryValueExW( hkey, fonts[i], 0, &type, (BYTE *)data, &dlen ) && type == REG_SZ)
|
|
add_system_font_resource( data, ADDFONT_ALLOW_BITMAP );
|
|
}
|
|
RegCloseKey( hkey );
|
|
}
|
|
|
|
static void load_directory_fonts( WCHAR *path, UINT flags )
|
|
{
|
|
HANDLE handle;
|
|
WIN32_FIND_DATAW data;
|
|
WCHAR *p;
|
|
|
|
p = path + lstrlenW(path) - 1;
|
|
TRACE( "loading fonts from %s\n", debugstr_w(path) );
|
|
handle = FindFirstFileW( path, &data );
|
|
if (handle == INVALID_HANDLE_VALUE) return;
|
|
do
|
|
{
|
|
if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) continue;
|
|
lstrcpyW( p, data.cFileName );
|
|
font_funcs->add_font( path, flags );
|
|
} while (FindNextFileW( handle, &data ));
|
|
FindClose( handle );
|
|
}
|
|
|
|
static void load_file_system_fonts(void)
|
|
{
|
|
WCHAR *ptr, *next, path[MAX_PATH], value[1024];
|
|
DWORD len = ARRAY_SIZE(value);
|
|
|
|
/* Windows directory */
|
|
get_fonts_win_dir_path( L"*", path );
|
|
load_directory_fonts( path, 0 );
|
|
|
|
/* Wine data directory */
|
|
get_fonts_data_dir_path( L"*", path );
|
|
load_directory_fonts( path, ADDFONT_EXTERNAL_FONT );
|
|
|
|
/* custom paths */
|
|
/* @@ Wine registry key: HKCU\Software\Wine\Fonts */
|
|
if (!RegQueryValueExW( wine_fonts_key, L"Path", NULL, NULL, (BYTE *)value, &len ))
|
|
{
|
|
for (ptr = value; ptr; ptr = next)
|
|
{
|
|
if ((next = wcschr( ptr, ';' ))) *next++ = 0;
|
|
if (next && next - ptr < 2) continue;
|
|
lstrcpynW( path, ptr, MAX_PATH - 2 );
|
|
lstrcatW( path, L"\\*" );
|
|
load_directory_fonts( path, ADDFONT_EXTERNAL_FONT );
|
|
}
|
|
}
|
|
}
|
|
|
|
struct external_key
|
|
{
|
|
struct list entry;
|
|
WCHAR value[LF_FULLFACESIZE + 12];
|
|
};
|
|
|
|
static void update_external_font_keys(void)
|
|
{
|
|
struct list external_keys = LIST_INIT(external_keys);
|
|
HKEY winnt_key = 0, win9x_key = 0;
|
|
struct gdi_font_family *family;
|
|
struct external_key *key, *next;
|
|
struct gdi_font_face *face;
|
|
DWORD len, i = 0, type, dlen, vlen;
|
|
WCHAR value[LF_FULLFACESIZE + 12], path[MAX_PATH], *tmp;
|
|
WCHAR *file;
|
|
HKEY hkey;
|
|
|
|
RegCreateKeyExW( HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts",
|
|
0, NULL, 0, KEY_ALL_ACCESS, NULL, &winnt_key, NULL );
|
|
RegCreateKeyExW( HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\Fonts",
|
|
0, NULL, 0, KEY_ALL_ACCESS, NULL, &win9x_key, NULL );
|
|
|
|
/* enumerate the fonts and add external ones to the two keys */
|
|
|
|
if (RegCreateKeyW( wine_fonts_key, L"External Fonts", &hkey )) return;
|
|
|
|
vlen = ARRAY_SIZE(value);
|
|
dlen = sizeof(path);
|
|
while (!RegEnumValueW( hkey, i++, value, &vlen, NULL, &type, (BYTE *)path, &dlen ))
|
|
{
|
|
if (type != REG_SZ) goto next;
|
|
if ((tmp = wcsrchr( value, ' ' )) && !facename_compare( tmp, L" (TrueType)", -1 )) *tmp = 0;
|
|
if ((face = find_face_from_full_name( value )) && !wcsicmp( face->file, path ))
|
|
{
|
|
face->flags |= ADDFONT_EXTERNAL_FOUND;
|
|
goto next;
|
|
}
|
|
if (tmp && !*tmp) *tmp = ' ';
|
|
if (!(key = HeapAlloc( GetProcessHeap(), 0, sizeof(*key) ))) break;
|
|
lstrcpyW( key->value, value );
|
|
list_add_tail( &external_keys, &key->entry );
|
|
next:
|
|
vlen = ARRAY_SIZE(value);
|
|
dlen = sizeof(path);
|
|
}
|
|
|
|
WINE_RB_FOR_EACH_ENTRY( family, &family_name_tree, struct gdi_font_family, name_entry )
|
|
{
|
|
LIST_FOR_EACH_ENTRY( face, &family->faces, struct gdi_font_face, entry )
|
|
{
|
|
if (!(face->flags & ADDFONT_EXTERNAL_FONT)) continue;
|
|
if ((face->flags & ADDFONT_EXTERNAL_FOUND)) continue;
|
|
|
|
lstrcpyW( value, face->full_name );
|
|
if (face->scalable) lstrcatW( value, L" (TrueType)" );
|
|
|
|
if (GetFullPathNameW( face->file, MAX_PATH, path, NULL ))
|
|
file = path;
|
|
else if ((file = wcsrchr( face->file, '\\' )))
|
|
file++;
|
|
else
|
|
file = face->file;
|
|
|
|
len = (lstrlenW(file) + 1) * sizeof(WCHAR);
|
|
RegSetValueExW( winnt_key, value, 0, REG_SZ, (BYTE *)file, len );
|
|
RegSetValueExW( win9x_key, value, 0, REG_SZ, (BYTE *)file, len );
|
|
RegSetValueExW( hkey, value, 0, REG_SZ, (BYTE *)file, len );
|
|
}
|
|
}
|
|
LIST_FOR_EACH_ENTRY_SAFE( key, next, &external_keys, struct external_key, entry )
|
|
{
|
|
RegDeleteValueW( win9x_key, key->value );
|
|
RegDeleteValueW( winnt_key, key->value );
|
|
RegDeleteValueW( hkey, key->value );
|
|
list_remove( &key->entry );
|
|
HeapFree( GetProcessHeap(), 0, key );
|
|
}
|
|
RegCloseKey( win9x_key );
|
|
RegCloseKey( winnt_key );
|
|
RegCloseKey( hkey );
|
|
}
|
|
|
|
static void load_registry_fonts(void)
|
|
{
|
|
WCHAR value[LF_FULLFACESIZE + 12], data[MAX_PATH], *tmp;
|
|
DWORD i = 0, type, dlen, vlen;
|
|
HKEY hkey;
|
|
|
|
/* Look under HKLM\Software\Microsoft\Windows[ NT]\CurrentVersion\Fonts
|
|
for any fonts not installed in %WINDOWSDIR%\Fonts. They will have their
|
|
full path as the entry. Also look for any .fon fonts, since ReadFontDir
|
|
will skip these. */
|
|
if (RegOpenKeyW( HKEY_LOCAL_MACHINE,
|
|
is_win9x() ? L"Software\\Microsoft\\Windows\\CurrentVersion\\Fonts" :
|
|
L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts", &hkey ))
|
|
return;
|
|
|
|
vlen = ARRAY_SIZE(value);
|
|
dlen = sizeof(data);
|
|
while (!RegEnumValueW( hkey, i++, value, &vlen, NULL, &type, NULL, NULL ))
|
|
{
|
|
if (type != REG_SZ) goto next;
|
|
dlen /= sizeof(WCHAR);
|
|
if ((tmp = wcsrchr( value, ' ' )) && !facename_compare( tmp, L" (TrueType)", -1 )) *tmp = 0;
|
|
if (find_face_from_full_name( value )) goto next;
|
|
if (tmp && !*tmp) *tmp = ' ';
|
|
|
|
if (RegQueryValueExW( hkey, value, NULL, NULL, (LPBYTE)data, &dlen ))
|
|
{
|
|
WARN( "Unable to get face path %s\n", debugstr_w(value) );
|
|
goto next;
|
|
}
|
|
|
|
dlen /= sizeof(WCHAR);
|
|
if (data[0] && data[1] == ':')
|
|
add_font_resource( data, ADDFONT_ALLOW_BITMAP );
|
|
else if (dlen >= 6 && !wcsicmp( data + dlen - 5, L".fon" ))
|
|
add_system_font_resource( data, ADDFONT_ALLOW_BITMAP );
|
|
next:
|
|
vlen = ARRAY_SIZE(value);
|
|
dlen = sizeof(data);
|
|
}
|
|
RegCloseKey( hkey );
|
|
}
|
|
|
|
static const struct font_callback_funcs callback_funcs = { add_gdi_face };
|
|
|
|
/***********************************************************************
|
|
* font_init
|
|
*/
|
|
void font_init(void)
|
|
{
|
|
HANDLE mutex;
|
|
DWORD disposition;
|
|
|
|
if (RegCreateKeyExW( HKEY_CURRENT_USER, L"Software\\Wine\\Fonts", 0, NULL, 0,
|
|
KEY_ALL_ACCESS, NULL, &wine_fonts_key, NULL ))
|
|
return;
|
|
|
|
init_font_options();
|
|
update_codepage();
|
|
if (__wine_init_unix_lib( gdi32_module, DLL_PROCESS_ATTACH, &callback_funcs, &font_funcs )) return;
|
|
|
|
load_system_bitmap_fonts();
|
|
load_file_system_fonts();
|
|
font_funcs->load_fonts();
|
|
|
|
if (!(mutex = CreateMutexW( NULL, FALSE, L"__WINE_FONT_MUTEX__" ))) return;
|
|
WaitForSingleObject( mutex, INFINITE );
|
|
|
|
RegCreateKeyExW( wine_fonts_key, L"Cache", 0, NULL, REG_OPTION_VOLATILE,
|
|
KEY_ALL_ACCESS, NULL, &wine_fonts_cache_key, &disposition );
|
|
|
|
if (disposition == REG_CREATED_NEW_KEY)
|
|
{
|
|
load_registry_fonts();
|
|
update_external_font_keys();
|
|
}
|
|
|
|
ReleaseMutex( mutex );
|
|
|
|
if (disposition != REG_CREATED_NEW_KEY)
|
|
{
|
|
load_registry_fonts();
|
|
load_font_list_from_cache();
|
|
}
|
|
|
|
reorder_font_list();
|
|
load_gdi_font_subst();
|
|
load_gdi_font_replacements();
|
|
load_system_links();
|
|
dump_gdi_font_list();
|
|
dump_gdi_font_subst();
|
|
}
|
|
|
|
/***********************************************************************
|
|
* NtGdiAddFontResourceW (win32u.@)
|
|
*/
|
|
INT WINAPI NtGdiAddFontResourceW( const WCHAR *str, ULONG size, ULONG files, DWORD flags,
|
|
DWORD tid, void *dv )
|
|
{
|
|
int ret;
|
|
WCHAR *filename;
|
|
BOOL hidden;
|
|
|
|
if (!font_funcs) return 1;
|
|
if (!(ret = add_font_resource( str, flags )))
|
|
{
|
|
/* FreeType <2.3.5 has problems reading resources wrapped in PE files. */
|
|
HMODULE hModule = LoadLibraryExW(str, NULL, LOAD_LIBRARY_AS_DATAFILE);
|
|
if (hModule != NULL)
|
|
{
|
|
int num_resources = 0;
|
|
LPWSTR rt_font = (LPWSTR)((ULONG_PTR)8); /* we don't want to include winuser.h */
|
|
|
|
TRACE("WineEngAddFontResourceEx failed on PE file %s - trying to load resources manually\n",
|
|
wine_dbgstr_w(str));
|
|
if (EnumResourceNamesW(hModule, rt_font, load_enumed_resource, (LONG_PTR)&num_resources))
|
|
ret = num_resources;
|
|
FreeLibrary(hModule);
|
|
}
|
|
else if ((filename = get_scalable_filename( str, &hidden )) != NULL)
|
|
{
|
|
if (hidden) flags |= FR_PRIVATE | FR_NOT_ENUM;
|
|
ret = add_font_resource( filename, flags );
|
|
HeapFree( GetProcessHeap(), 0, filename );
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* NtGdiAddFontMemResourceEx (win32u.@)
|
|
*/
|
|
HANDLE WINAPI NtGdiAddFontMemResourceEx( void *ptr, DWORD size, void *dv, ULONG dv_size,
|
|
DWORD *count )
|
|
{
|
|
HANDLE ret;
|
|
DWORD num_fonts;
|
|
void *copy;
|
|
|
|
if (!ptr || !size || !count)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return NULL;
|
|
}
|
|
if (!font_funcs) return NULL;
|
|
if (!(copy = HeapAlloc( GetProcessHeap(), 0, size ))) return NULL;
|
|
memcpy( copy, ptr, size );
|
|
|
|
EnterCriticalSection( &font_cs );
|
|
num_fonts = font_funcs->add_mem_font( copy, size, ADDFONT_ALLOW_BITMAP | ADDFONT_ADD_RESOURCE );
|
|
LeaveCriticalSection( &font_cs );
|
|
|
|
if (!num_fonts)
|
|
{
|
|
HeapFree( GetProcessHeap(), 0, copy );
|
|
return NULL;
|
|
}
|
|
|
|
/* FIXME: is the handle only for use in RemoveFontMemResourceEx or should it be a true handle?
|
|
* For now return something unique but quite random
|
|
*/
|
|
ret = (HANDLE)((INT_PTR)copy ^ 0x87654321);
|
|
|
|
__TRY
|
|
{
|
|
*count = num_fonts;
|
|
}
|
|
__EXCEPT_PAGE_FAULT
|
|
{
|
|
WARN( "page fault while writing to *count (%p)\n", count );
|
|
NtGdiRemoveFontMemResourceEx( ret );
|
|
ret = 0;
|
|
}
|
|
__ENDTRY
|
|
TRACE( "Returning handle %p\n", ret );
|
|
return ret;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* NtGdiRemoveFontMemResourceEx (win32u.@)
|
|
*/
|
|
BOOL WINAPI NtGdiRemoveFontMemResourceEx( HANDLE handle )
|
|
{
|
|
FIXME( "(%p) stub\n", handle );
|
|
return TRUE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* NtGdiRemoveFontResourceW (win32u.@)
|
|
*/
|
|
BOOL WINAPI NtGdiRemoveFontResourceW( const WCHAR *str, ULONG size, ULONG files, DWORD flags,
|
|
DWORD tid, void *dv )
|
|
{
|
|
int ret;
|
|
WCHAR *filename;
|
|
BOOL hidden;
|
|
|
|
if (!font_funcs) return TRUE;
|
|
|
|
if (!(ret = remove_font_resource( str, flags )))
|
|
{
|
|
/* FreeType <2.3.5 has problems reading resources wrapped in PE files. */
|
|
HMODULE hModule = LoadLibraryExW(str, NULL, LOAD_LIBRARY_AS_DATAFILE);
|
|
if (hModule != NULL)
|
|
{
|
|
WARN("Can't unload resources from PE file %s\n", wine_dbgstr_w(str));
|
|
FreeLibrary(hModule);
|
|
}
|
|
else if ((filename = get_scalable_filename( str, &hidden )) != NULL)
|
|
{
|
|
if (hidden) flags |= FR_PRIVATE | FR_NOT_ENUM;
|
|
ret = remove_font_resource( filename, flags );
|
|
HeapFree( GetProcessHeap(), 0, filename );
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* NtGdiGetFontUnicodeRanges (win32u.@)
|
|
*
|
|
* Retrieve a list of supported Unicode characters in a font.
|
|
*
|
|
* PARAMS
|
|
* hdc [I] Handle to a device context.
|
|
* lpgs [O] GLYPHSET structure specifying supported character ranges.
|
|
*
|
|
* RETURNS
|
|
* Success: Number of bytes written to the buffer pointed to by lpgs.
|
|
* Failure: 0
|
|
*
|
|
*/
|
|
DWORD WINAPI NtGdiGetFontUnicodeRanges( HDC hdc, GLYPHSET *lpgs )
|
|
{
|
|
DWORD ret;
|
|
PHYSDEV dev;
|
|
DC *dc = get_dc_ptr(hdc);
|
|
|
|
TRACE("(%p, %p)\n", hdc, lpgs);
|
|
|
|
if (!dc) return 0;
|
|
|
|
dev = GET_DC_PHYSDEV( dc, pGetFontUnicodeRanges );
|
|
ret = dev->funcs->pGetFontUnicodeRanges( dev, lpgs );
|
|
release_dc_ptr(dc);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*************************************************************
|
|
* NtGdiFontIsLinked (win32u.@)
|
|
*/
|
|
BOOL WINAPI NtGdiFontIsLinked( HDC hdc )
|
|
{
|
|
DC *dc = get_dc_ptr(hdc);
|
|
PHYSDEV dev;
|
|
BOOL ret;
|
|
|
|
if (!dc) return FALSE;
|
|
dev = GET_DC_PHYSDEV( dc, pFontIsLinked );
|
|
ret = dev->funcs->pFontIsLinked( dev );
|
|
release_dc_ptr(dc);
|
|
TRACE("returning %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/*************************************************************
|
|
* NtGdiGetRealizationInfo (win32u.@)
|
|
*/
|
|
BOOL WINAPI NtGdiGetRealizationInfo( HDC hdc, struct font_realization_info *info )
|
|
{
|
|
BOOL is_v0 = info->size == FIELD_OFFSET(struct font_realization_info, file_count);
|
|
PHYSDEV dev;
|
|
BOOL ret;
|
|
DC *dc;
|
|
|
|
if (info->size != sizeof(*info) && !is_v0)
|
|
return FALSE;
|
|
|
|
dc = get_dc_ptr(hdc);
|
|
if (!dc) return FALSE;
|
|
dev = GET_DC_PHYSDEV( dc, pGetFontRealizationInfo );
|
|
ret = dev->funcs->pGetFontRealizationInfo( dev, info );
|
|
release_dc_ptr(dc);
|
|
return ret;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* NtGdiGetRasterizerCaps (win32u.@)
|
|
*/
|
|
BOOL WINAPI NtGdiGetRasterizerCaps( RASTERIZER_STATUS *status, UINT size )
|
|
{
|
|
status->nSize = sizeof(RASTERIZER_STATUS);
|
|
status->wFlags = font_funcs ? (TT_AVAILABLE | TT_ENABLED) : 0;
|
|
status->nLanguageID = 0;
|
|
return TRUE;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* NtGdiGetFontFileData (win32u.@)
|
|
*/
|
|
BOOL WINAPI NtGdiGetFontFileData( DWORD instance_id, DWORD file_index, UINT64 *offset,
|
|
void *buff, DWORD buff_size )
|
|
{
|
|
struct gdi_font *font;
|
|
DWORD tag = 0, size;
|
|
BOOL ret = FALSE;
|
|
|
|
if (!font_funcs) return FALSE;
|
|
EnterCriticalSection( &font_cs );
|
|
if ((font = get_font_from_handle( instance_id )))
|
|
{
|
|
if (font->ttc_item_offset) tag = MS_TTCF_TAG;
|
|
size = font_funcs->get_font_data( font, tag, 0, NULL, 0 );
|
|
if (size != GDI_ERROR && size >= buff_size && *offset <= size - buff_size)
|
|
ret = font_funcs->get_font_data( font, tag, *offset, buff, buff_size ) != GDI_ERROR;
|
|
else
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
}
|
|
LeaveCriticalSection( &font_cs );
|
|
return ret;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* NtGdiGetFontFileInfo (win32u.@)
|
|
*/
|
|
BOOL WINAPI NtGdiGetFontFileInfo( DWORD instance_id, DWORD file_index, struct font_fileinfo *info,
|
|
SIZE_T size, SIZE_T *needed )
|
|
{
|
|
SIZE_T required_size = 0;
|
|
struct gdi_font *font;
|
|
BOOL ret = FALSE;
|
|
|
|
EnterCriticalSection( &font_cs );
|
|
|
|
if ((font = get_font_from_handle( instance_id )))
|
|
{
|
|
required_size = sizeof(*info) + lstrlenW( font->file ) * sizeof(WCHAR);
|
|
if (required_size <= size)
|
|
{
|
|
info->writetime = font->writetime;
|
|
info->size.QuadPart = font->data_size;
|
|
lstrcpyW( info->path, font->file );
|
|
ret = TRUE;
|
|
}
|
|
else SetLastError( ERROR_INSUFFICIENT_BUFFER );
|
|
}
|
|
|
|
LeaveCriticalSection( &font_cs );
|
|
if (needed) *needed = required_size;
|
|
return ret;
|
|
}
|
|
|
|
/*************************************************************
|
|
* NtGdiGetCharWidthInfo (win32u.@)
|
|
*/
|
|
BOOL WINAPI NtGdiGetCharWidthInfo( HDC hdc, struct char_width_info *info )
|
|
{
|
|
PHYSDEV dev;
|
|
BOOL ret;
|
|
DC *dc;
|
|
|
|
dc = get_dc_ptr(hdc);
|
|
if (!dc) return FALSE;
|
|
dev = GET_DC_PHYSDEV( dc, pGetCharWidthInfo );
|
|
ret = dev->funcs->pGetCharWidthInfo( dev, info );
|
|
|
|
if (ret)
|
|
{
|
|
info->lsb = width_to_LP( dc, info->lsb );
|
|
info->rsb = width_to_LP( dc, info->rsb );
|
|
}
|
|
release_dc_ptr(dc);
|
|
return ret;
|
|
}
|