7591 lines
243 KiB
C
7591 lines
243 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 "config.h"
|
|
#include "wine/port.h"
|
|
|
|
#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 "gdi_private.h"
|
|
#include "resource.h"
|
|
#include "wine/exception.h"
|
|
#include "wine/heap.h"
|
|
#include "wine/unicode.h"
|
|
#include "wine/debug.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(font);
|
|
|
|
static HKEY wine_fonts_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;
|
|
}
|
|
|
|
static const struct font_backend_funcs *font_funcs;
|
|
|
|
static const MAT2 identity = { {0,1}, {0,0}, {0,0}, {0,1} };
|
|
|
|
/* 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 = (strlenW(p) + 1) * sizeof(WCHAR);
|
|
ret = HeapAlloc(GetProcessHeap(), 0, len);
|
|
memcpy(ret, p, len);
|
|
return ret;
|
|
}
|
|
|
|
static HGDIOBJ FONT_SelectObject( HGDIOBJ handle, HDC hdc );
|
|
static INT FONT_GetObjectA( HGDIOBJ handle, INT count, LPVOID buffer );
|
|
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_SelectObject, /* pSelectObject */
|
|
FONT_GetObjectA, /* pGetObjectA */
|
|
FONT_GetObjectW, /* pGetObjectW */
|
|
NULL, /* pUnrealizeObject */
|
|
FONT_DeleteObject /* pDeleteObject */
|
|
};
|
|
|
|
typedef struct
|
|
{
|
|
LOGFONTW logfont;
|
|
} FONTOBJ;
|
|
|
|
struct font_enum
|
|
{
|
|
LPLOGFONTW lpLogFontParam;
|
|
FONTENUMPROCW lpEnumFunc;
|
|
LPARAM lpData;
|
|
BOOL unicode;
|
|
HDC hdc;
|
|
INT retval;
|
|
};
|
|
|
|
/*
|
|
* For TranslateCharsetInfo
|
|
*/
|
|
#define MAXTCIINDEX 32
|
|
static const CHARSETINFO FONT_tci[MAXTCIINDEX] = {
|
|
/* 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 win9x_font_reg_key[] = {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
|
|
'W','i','n','d','o','w','s','\\',
|
|
'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
|
|
'F','o','n','t','s','\0'};
|
|
|
|
static const WCHAR winnt_font_reg_key[] = {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
|
|
'W','i','n','d','o','w','s',' ','N','T','\\',
|
|
'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
|
|
'F','o','n','t','s','\0'};
|
|
|
|
static const WCHAR system_fonts_reg_key[] = {'S','o','f','t','w','a','r','e','\\','F','o','n','t','s','\0'};
|
|
static const WCHAR system_link[] = {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
|
|
'W','i','n','d','o','w','s',' ','N','T','\\',
|
|
'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\','F','o','n','t','L','i','n','k','\\',
|
|
'S','y','s','t','e','m','L','i','n','k',0};
|
|
static const WCHAR Lucida_Sans_Unicode[] = {'L','u','c','i','d','a',' ','S','a','n','s',' ','U','n','i','c','o','d','e',0};
|
|
static const WCHAR Microsoft_Sans_Serif[] = {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0 };
|
|
static const WCHAR Tahoma[] = {'T','a','h','o','m','a',0};
|
|
static const WCHAR MS_UI_Gothic[] = {'M','S',' ','U','I',' ','G','o','t','h','i','c',0};
|
|
static const WCHAR SimSun[] = {'S','i','m','S','u','n',0};
|
|
static const WCHAR Gulim[] = {'G','u','l','i','m',0};
|
|
static const WCHAR PMingLiU[] = {'P','M','i','n','g','L','i','U',0};
|
|
static const WCHAR Batang[] = {'B','a','t','a','n','g',0};
|
|
|
|
static const 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 )
|
|
{
|
|
static const WCHAR fontsW[] = {'\\','f','o','n','t','s','\\',0};
|
|
static const WCHAR winedatadirW[] = {'W','I','N','E','D','A','T','A','D','I','R',0};
|
|
static const WCHAR winebuilddirW[] = {'W','I','N','E','B','U','I','L','D','D','I','R',0};
|
|
|
|
if (GetEnvironmentVariableW( winedatadirW, path, MAX_PATH ))
|
|
{
|
|
const char fontdir[] = "\\" WINE_FONT_DIR "\\";
|
|
MultiByteToWideChar( CP_ACP, 0, fontdir, -1, path + strlenW(path), MAX_PATH - strlenW(path) );
|
|
}
|
|
else if (GetEnvironmentVariableW( winebuilddirW, path, MAX_PATH ))
|
|
{
|
|
strcatW( path, fontsW );
|
|
}
|
|
strcatW( path, file );
|
|
if (path[5] == ':') memmove( path, path + 4, (strlenW(path) - 3) * sizeof(WCHAR) );
|
|
else path[1] = '\\'; /* change \??\ to \\?\ */
|
|
}
|
|
|
|
static void get_fonts_win_dir_path( const WCHAR *file, WCHAR *path )
|
|
{
|
|
static const WCHAR fontsW[] = {'\\','f','o','n','t','s','\\',0};
|
|
|
|
GetWindowsDirectoryW( path, MAX_PATH );
|
|
strcatW( path, fontsW );
|
|
strcatW( 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 + strlenW( 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)));
|
|
}
|
|
}
|
|
|
|
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 (!strcmpiW(subst->names, from_name) &&
|
|
(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 = strlenW( from_name ) + strlenW( 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;
|
|
strcpyW( subst->names, from_name );
|
|
strcpyW( 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 (RegOpenKeyA( HKEY_LOCAL_MACHINE, "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 = strrchrW( value, ',' )) && p[1])
|
|
{
|
|
*p++ = 0;
|
|
from_charset = strtolW( p, NULL, 10 );
|
|
}
|
|
if ((p = strrchrW( data, ',' )) && p[1])
|
|
{
|
|
*p++ = 0;
|
|
to_charset = strtolW( 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 */
|
|
|
|
struct list font_list = LIST_INIT(font_list);
|
|
|
|
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])
|
|
{
|
|
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;
|
|
list_add_tail( &font_list, &family->entry );
|
|
return family;
|
|
}
|
|
|
|
void release_family( struct gdi_font_family *family )
|
|
{
|
|
if (--family->refcount) return;
|
|
assert( list_empty( &family->faces ));
|
|
list_remove( &family->entry );
|
|
if (family->replacement) release_family( family->replacement );
|
|
HeapFree( GetProcessHeap(), 0, family );
|
|
}
|
|
|
|
struct gdi_font_family *find_family_from_name( const WCHAR *name )
|
|
{
|
|
struct gdi_font_family *family;
|
|
|
|
LIST_FOR_EACH_ENTRY( family, &font_list, struct gdi_font_family, entry )
|
|
if (!strncmpiW( family->family_name, name, LF_FACESIZE - 1 )) return family;
|
|
return NULL;
|
|
}
|
|
|
|
struct gdi_font_family *find_family_from_any_name( const WCHAR *name )
|
|
{
|
|
struct gdi_font_family *family;
|
|
|
|
LIST_FOR_EACH_ENTRY( family, &font_list, struct gdi_font_family, entry )
|
|
{
|
|
if (!strncmpiW( family->family_name, name, LF_FACESIZE - 1 )) return family;
|
|
if (!strncmpiW( family->second_name, name, LF_FACESIZE - 1 )) return family;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
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 *find_face_from_filename( const WCHAR *file_name, const WCHAR *family_name )
|
|
{
|
|
struct gdi_font_family *family;
|
|
struct gdi_font_face *face;
|
|
const WCHAR *file;
|
|
|
|
TRACE( "looking for file %s name %s\n", debugstr_w(file_name), debugstr_w(family_name) );
|
|
|
|
LIST_FOR_EACH_ENTRY( family, &font_list, struct gdi_font_family, entry )
|
|
{
|
|
if (family_name && strncmpiW( family_name, family->family_name, LF_FACESIZE - 1 )) continue;
|
|
LIST_FOR_EACH_ENTRY( face, get_family_face_list(family), struct gdi_font_face, entry )
|
|
{
|
|
if (!face->file) continue;
|
|
file = strrchrW(face->file, '\\');
|
|
if (!file) file = face->file;
|
|
else file++;
|
|
if (strcmpiW( file, file_name )) continue;
|
|
face->refcount++;
|
|
return face;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static 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 (RegOpenKeyA( wine_fonts_key, "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 += strlenW(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;
|
|
|
|
LIST_FOR_EACH_ENTRY( family, &font_list, struct gdi_font_family, 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");
|
|
}
|
|
}
|
|
}
|
|
|
|
struct gdi_font_face *create_face( const WCHAR *style, const WCHAR *fullname, const WCHAR *file,
|
|
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;
|
|
if (file) face->file = strdupW( file );
|
|
if (size) face->size = *size;
|
|
else face->scalable = TRUE;
|
|
return face;
|
|
}
|
|
|
|
/* font links */
|
|
|
|
static struct list font_links = LIST_INIT(font_links);
|
|
|
|
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 (!strncmpiW( link->name, name, LF_FACESIZE - 1 )) return link;
|
|
return NULL;
|
|
}
|
|
|
|
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 (!strncmpiW( link->name, name, LF_FACESIZE - 1) ||
|
|
(subst && !strncmpiW( 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[] =
|
|
{
|
|
Lucida_Sans_Unicode,
|
|
Microsoft_Sans_Serif,
|
|
Tahoma
|
|
};
|
|
|
|
static const struct font_links_defaults_list
|
|
{
|
|
/* Keyed off substitution for "MS Shell Dlg" */
|
|
const WCHAR *shelldlg;
|
|
/* Maximum of four substitutes, plus terminating NULL pointer */
|
|
const WCHAR *substitutes[5];
|
|
} font_links_defaults_list[] =
|
|
{
|
|
/* Non East-Asian */
|
|
{ Tahoma, /* FIXME unverified ordering */
|
|
{ MS_UI_Gothic, SimSun, Gulim, PMingLiU, NULL }
|
|
},
|
|
/* Below lists are courtesy of
|
|
* http://blogs.msdn.com/michkap/archive/2005/06/18/430507.aspx
|
|
*/
|
|
/* Japanese */
|
|
{ MS_UI_Gothic,
|
|
{ MS_UI_Gothic, PMingLiU, SimSun, Gulim, NULL }
|
|
},
|
|
/* Chinese Simplified */
|
|
{ SimSun,
|
|
{ SimSun, PMingLiU, MS_UI_Gothic, Batang, NULL }
|
|
},
|
|
/* Korean */
|
|
{ Gulim,
|
|
{ Gulim, PMingLiU, MS_UI_Gothic, SimSun, NULL }
|
|
},
|
|
/* Chinese Traditional */
|
|
{ PMingLiU,
|
|
{ PMingLiU, SimSun, MS_UI_Gothic, Batang, NULL }
|
|
}
|
|
};
|
|
|
|
static 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 (!strcmpiW( name, *values )) 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 = strrchrW(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)
|
|
{
|
|
static const WCHAR system_link[] = {'S','o','f','t','w','a','r','e','\\',
|
|
'M','i','c','r','o','s','o','f','t','\\',
|
|
'W','i','n','d','o','w','s',' ','N','T','\\',
|
|
'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
|
|
'F','o','n','t','L','i','n','k','\\',
|
|
'S','y','s','t','e','m','L','i','n','k',0};
|
|
static const WCHAR tahoma_ttf[] = {'t','a','h','o','m','a','.','t','t','f',0};
|
|
static const WCHAR System[] = {'S','y','s','t','e','m',0};
|
|
static const WCHAR MS_Shell_Dlg[] = {'M','S',' ','S','h','e','l','l',' ','D','l','g',0};
|
|
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, system_link, &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 + strlenW(entry) + 1;
|
|
if ((p = strchrW( entry, ',' )))
|
|
{
|
|
*p++ = 0;
|
|
while (isspaceW(*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( 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 ((!strcmpiW( font_links_defaults_list[i].shelldlg, shelldlg_name ) ||
|
|
(subst && !strcmpiW( subst, shelldlg_name ))))
|
|
{
|
|
for (j = 0; j < ARRAY_SIZE(font_links_list); j++)
|
|
populate_system_links( font_links_list[j], font_links_defaults_list[i].substitutes );
|
|
if (!strcmpiW(shelldlg_name, font_links_defaults_list[i].substitutes[0]))
|
|
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( System );
|
|
if ((face = find_face_from_filename( tahoma_ttf, 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( 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 );
|
|
}
|
|
}
|
|
|
|
/* 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 ? strlenW(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.0;
|
|
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;
|
|
}
|
|
|
|
if (!(font->handle = alloc_font_handle( font )))
|
|
{
|
|
HeapFree( GetProcessHeap(), 0, font );
|
|
return NULL;
|
|
}
|
|
return font;
|
|
}
|
|
|
|
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 );
|
|
}
|
|
|
|
void set_gdi_font_names( struct gdi_font *font, const WCHAR *family_name, const WCHAR *style_name,
|
|
const WCHAR *full_name )
|
|
{
|
|
font->otm.otmpFamilyName = (char *)strdupW( family_name );
|
|
font->otm.otmpStyleName = (char *)strdupW( style_name );
|
|
font->otm.otmpFaceName = (char *)strdupW( full_name );
|
|
}
|
|
|
|
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;
|
|
set_gdi_font_names( font, family_name, face->style_name, 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";
|
|
}
|
|
}
|
|
|
|
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 )
|
|
{
|
|
struct gdi_font *child;
|
|
struct gdi_font_family *family;
|
|
struct gdi_font_face *child_face, *best_face = NULL;
|
|
UINT penalty = 0, new_penalty = 0;
|
|
BOOL bold, italic, bd, it;
|
|
|
|
italic = !!font->lf.lfItalic;
|
|
bold = font->lf.lfWeight > FW_MEDIUM;
|
|
|
|
if (!(family = find_family_from_name( family_name ))) return;
|
|
|
|
LIST_FOR_EACH_ENTRY( child_face, get_family_face_list(family), struct gdi_font_face, entry )
|
|
{
|
|
it = !!(child_face->ntmFlags & NTM_ITALIC);
|
|
bd = !!(child_face->ntmFlags & NTM_BOLD);
|
|
new_penalty = ( it ^ italic ) + ( bd ^ bold );
|
|
if (!best_face || new_penalty < penalty)
|
|
{
|
|
penalty = new_penalty;
|
|
best_face = child_face;
|
|
}
|
|
}
|
|
if (!best_face) return;
|
|
|
|
if (!(child = create_gdi_font( best_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 );
|
|
}
|
|
|
|
void create_child_font_list( struct gdi_font *font )
|
|
{
|
|
static const WCHAR szDefaultFallbackLink[] = {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0};
|
|
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 &&
|
|
strcmpiW( font_name, szDefaultFallbackLink ) != 0)
|
|
{
|
|
if ((font_link = find_gdi_font_link( szDefaultFallbackLink )))
|
|
{
|
|
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 strcmpiW( font->lf.lfFaceName, lf->lfFaceName);
|
|
}
|
|
|
|
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 = toupperW(*pwc);
|
|
pwc++;
|
|
*pwc = toupperW(*pwc);
|
|
hash ^= two_chars;
|
|
if(!*pwc) break;
|
|
}
|
|
hash ^= !can_use_bitmap;
|
|
return hash;
|
|
}
|
|
|
|
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 );
|
|
}
|
|
|
|
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 */
|
|
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 );
|
|
return;
|
|
}
|
|
unused_font_count++;
|
|
}
|
|
|
|
static void add_font_list(HKEY hkey, const struct nls_update_font_list *fl, int dpi)
|
|
{
|
|
const char *sserif = (dpi <= 108) ? fl->sserif_96 : fl->sserif_120;
|
|
|
|
RegSetValueExA(hkey, "Courier", 0, REG_SZ, (const BYTE *)fl->courier, strlen(fl->courier)+1);
|
|
RegSetValueExA(hkey, "MS Serif", 0, REG_SZ, (const BYTE *)fl->serif, strlen(fl->serif)+1);
|
|
RegSetValueExA(hkey, "MS Sans Serif", 0, REG_SZ, (const BYTE *)sserif, strlen(sserif)+1);
|
|
RegSetValueExA(hkey, "Small Fonts", 0, REG_SZ, (const BYTE *)fl->small, strlen(fl->small)+1);
|
|
}
|
|
|
|
static void set_value_key(HKEY hkey, const char *name, const char *value)
|
|
{
|
|
if (value)
|
|
RegSetValueExA(hkey, name, 0, REG_SZ, (const BYTE *)value, strlen(value) + 1);
|
|
else if (name)
|
|
RegDeleteValueA(hkey, name);
|
|
}
|
|
|
|
static void update_font_association_info(UINT current_ansi_codepage)
|
|
{
|
|
static const char *font_assoc_reg_key = "System\\CurrentControlSet\\Control\\FontAssoc";
|
|
static const char *assoc_charset_subkey = "Associated Charset";
|
|
|
|
if (is_dbcs_ansi_cp(current_ansi_codepage))
|
|
{
|
|
HKEY hkey;
|
|
if (RegCreateKeyA(HKEY_LOCAL_MACHINE, font_assoc_reg_key, &hkey) == ERROR_SUCCESS)
|
|
{
|
|
HKEY hsubkey;
|
|
if (RegCreateKeyA(hkey, assoc_charset_subkey, &hsubkey) == ERROR_SUCCESS)
|
|
{
|
|
switch (current_ansi_codepage)
|
|
{
|
|
case 932:
|
|
set_value_key(hsubkey, "ANSI(00)", "NO");
|
|
set_value_key(hsubkey, "OEM(FF)", "NO");
|
|
set_value_key(hsubkey, "SYMBOL(02)", "NO");
|
|
break;
|
|
case 936:
|
|
case 949:
|
|
case 950:
|
|
set_value_key(hsubkey, "ANSI(00)", "YES");
|
|
set_value_key(hsubkey, "OEM(FF)", "YES");
|
|
set_value_key(hsubkey, "SYMBOL(02)", "NO");
|
|
break;
|
|
}
|
|
RegCloseKey(hsubkey);
|
|
}
|
|
|
|
/* TODO: Associated DefaultFonts */
|
|
|
|
RegCloseKey(hkey);
|
|
}
|
|
}
|
|
else
|
|
RegDeleteTreeA(HKEY_LOCAL_MACHINE, font_assoc_reg_key);
|
|
}
|
|
|
|
static void set_multi_value_key(HKEY hkey, const WCHAR *name, const WCHAR *value, DWORD len)
|
|
{
|
|
if (value)
|
|
RegSetValueExW(hkey, name, 0, REG_MULTI_SZ, (const BYTE *)value, len);
|
|
else if (name)
|
|
RegDeleteValueW(hkey, name);
|
|
}
|
|
|
|
static void update_font_system_link_info(UINT current_ansi_codepage)
|
|
{
|
|
static const WCHAR system_link_simplified_chinese[] =
|
|
{'S','I','M','S','U','N','.','T','T','C',',','S','i','m','S','u','n','\0',
|
|
'M','I','N','G','L','I','U','.','T','T','C',',','P','M','i','n','g','L','i','u','\0',
|
|
'M','S','G','O','T','H','I','C','.','T','T','C',',','M','S',' ','U','I',' ','G','o','t','h','i','c','\0',
|
|
'B','A','T','A','N','G','.','T','T','C',',','B','a','t','a','n','g','\0',
|
|
'\0'};
|
|
static const WCHAR system_link_traditional_chinese[] =
|
|
{'M','I','N','G','L','I','U','.','T','T','C',',','P','M','i','n','g','L','i','u','\0',
|
|
'S','I','M','S','U','N','.','T','T','C',',','S','i','m','S','u','n','\0',
|
|
'M','S','G','O','T','H','I','C','.','T','T','C',',','M','S',' ','U','I',' ','G','o','t','h','i','c','\0',
|
|
'B','A','T','A','N','G','.','T','T','C',',','B','a','t','a','n','g','\0',
|
|
'\0'};
|
|
static const WCHAR system_link_japanese[] =
|
|
{'M','S','G','O','T','H','I','C','.','T','T','C',',','M','S',' ','U','I',' ','G','o','t','h','i','c','\0',
|
|
'M','I','N','G','L','I','U','.','T','T','C',',','P','M','i','n','g','L','i','U','\0',
|
|
'S','I','M','S','U','N','.','T','T','C',',','S','i','m','S','u','n','\0',
|
|
'G','U','L','I','M','.','T','T','C',',','G','u','l','i','m','\0',
|
|
'\0'};
|
|
static const WCHAR system_link_korean[] =
|
|
{'G','U','L','I','M','.','T','T','C',',','G','u','l','i','m','\0',
|
|
'M','S','G','O','T','H','I','C','.','T','T','C',',','M','S',' ','U','I',' ','G','o','t','h','i','c','\0',
|
|
'M','I','N','G','L','I','U','.','T','T','C',',','P','M','i','n','g','L','i','U','\0',
|
|
'S','I','M','S','U','N','.','T','T','C',',','S','i','m','S','u','n','\0',
|
|
'\0'};
|
|
static const WCHAR system_link_non_cjk[] =
|
|
{'M','S','G','O','T','H','I','C','.','T','T','C',',','M','S',' ','U','I',' ','G','o','t','h','i','c','\0',
|
|
'M','I','N','G','L','I','U','.','T','T','C',',','P','M','i','n','g','L','i','U','\0',
|
|
'S','I','M','S','U','N','.','T','T','C',',','S','i','m','S','u','n','\0',
|
|
'G','U','L','I','M','.','T','T','C',',','G','u','l','i','m','\0',
|
|
'\0'};
|
|
HKEY hkey;
|
|
|
|
if (RegCreateKeyW(HKEY_LOCAL_MACHINE, system_link, &hkey) == ERROR_SUCCESS)
|
|
{
|
|
const WCHAR *link;
|
|
DWORD len;
|
|
|
|
switch (current_ansi_codepage)
|
|
{
|
|
case 932:
|
|
link = system_link_japanese;
|
|
len = sizeof(system_link_japanese);
|
|
break;
|
|
case 936:
|
|
link = system_link_simplified_chinese;
|
|
len = sizeof(system_link_simplified_chinese);
|
|
break;
|
|
case 949:
|
|
link = system_link_korean;
|
|
len = sizeof(system_link_korean);
|
|
break;
|
|
case 950:
|
|
link = system_link_traditional_chinese;
|
|
len = sizeof(system_link_traditional_chinese);
|
|
break;
|
|
default:
|
|
link = system_link_non_cjk;
|
|
len = sizeof(system_link_non_cjk);
|
|
}
|
|
set_multi_value_key(hkey, Lucida_Sans_Unicode, link, len);
|
|
set_multi_value_key(hkey, Microsoft_Sans_Serif, link, len);
|
|
set_multi_value_key(hkey, Tahoma, link, len);
|
|
RegCloseKey(hkey);
|
|
}
|
|
}
|
|
|
|
static void update_codepage(void)
|
|
{
|
|
static const WCHAR logpixels[] = { 'L','o','g','P','i','x','e','l','s',0 };
|
|
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, 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, 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, system_fonts_reg_key, &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, winnt_font_reg_key, &hkey ))
|
|
{
|
|
add_font_list(hkey, &nls_update_font_list[i], screen_dpi);
|
|
RegCloseKey(hkey);
|
|
}
|
|
if (!RegCreateKeyW( HKEY_LOCAL_MACHINE, win9x_font_reg_key, &hkey ))
|
|
{
|
|
add_font_list(hkey, &nls_update_font_list[i], screen_dpi);
|
|
RegCloseKey(hkey);
|
|
}
|
|
if (!RegCreateKeyA( HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes", &hkey ))
|
|
{
|
|
RegSetValueExA(hkey, "MS Shell Dlg", 0, REG_SZ, (const BYTE *)nls_update_font_list[i].shelldlg,
|
|
strlen(nls_update_font_list[i].shelldlg)+1);
|
|
RegSetValueExA(hkey, "Tms Rmn", 0, REG_SZ, (const BYTE *)nls_update_font_list[i].tmsrmn,
|
|
strlen(nls_update_font_list[i].tmsrmn)+1);
|
|
|
|
set_value_key(hkey, "Fixedsys,0", nls_update_font_list[i].fixed_0);
|
|
set_value_key(hkey, "System,0", nls_update_font_list[i].system_0);
|
|
set_value_key(hkey, "Courier,0", nls_update_font_list[i].courier_0);
|
|
set_value_key(hkey, "MS Serif,0", nls_update_font_list[i].serif_0);
|
|
set_value_key(hkey, "Small Fonts,0", nls_update_font_list[i].small_0);
|
|
set_value_key(hkey, "MS Sans Serif,0", nls_update_font_list[i].sserif_0);
|
|
set_value_key(hkey, "Helv,0", nls_update_font_list[i].helv_0);
|
|
set_value_key(hkey, "Tms Rmn,0", nls_update_font_list[i].tmsrmn_0);
|
|
|
|
set_value_key(hkey, nls_update_font_list[i].arial_0.from, nls_update_font_list[i].arial_0.to);
|
|
set_value_key(hkey, nls_update_font_list[i].courier_new_0.from, nls_update_font_list[i].courier_new_0.to);
|
|
set_value_key(hkey, nls_update_font_list[i].times_new_roman_0.from, nls_update_font_list[i].times_new_roman_0.to);
|
|
|
|
RegCloseKey(hkey);
|
|
}
|
|
done = TRUE;
|
|
}
|
|
else
|
|
{
|
|
/* Delete the FontSubstitutes from other locales */
|
|
if (!RegCreateKeyA( HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes", &hkey ))
|
|
{
|
|
set_value_key(hkey, nls_update_font_list[i].arial_0.from, NULL);
|
|
set_value_key(hkey, nls_update_font_list[i].courier_new_0.from, NULL);
|
|
set_value_key(hkey, nls_update_font_list[i].times_new_roman_0.from, NULL);
|
|
RegCloseKey(hkey);
|
|
}
|
|
}
|
|
}
|
|
if (!done)
|
|
FIXME("there is no font defaults for codepages %u,%u\n", ansi_cp, oem_cp);
|
|
|
|
/* update locale dependent font association info and font system link info in registry.
|
|
update only when codepages changed, not logpixels. */
|
|
if (strcmp(buf, cpbuf) != 0)
|
|
{
|
|
update_font_association_info(ansi_cp);
|
|
update_font_system_link_info(ansi_cp);
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************
|
|
* 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 (TranslateCharsetInfo( 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) &&
|
|
TranslateCharsetInfo( (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 (!TranslateCharsetInfo( 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 = 100 };
|
|
|
|
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 ))
|
|
{
|
|
memcpy( &ntm->ntmTm, &font->otm.otmTextMetrics, sizeof(TEXTMETRICW) );
|
|
ntm->ntmTm.ntmSizeEM = font->otm.otmEMSquare;
|
|
ntm->ntmTm.ntmCellHeight = font->ntmCellHeight;
|
|
ntm->ntmTm.ntmAvgWidth = font->ntmAvgWidth;
|
|
}
|
|
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 (!strncmpiW( 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 (!strncmpiW( 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 (!strncmpiW( face_name, family_name, LF_FACESIZE - 1)) return TRUE;
|
|
return !strncmpiW( 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) ))) return FALSE;
|
|
if (!get_face_enum_data( face, &data->elf, &data->ntm ))
|
|
{
|
|
HeapFree( GetProcessHeap(), 0, data );
|
|
return FALSE;
|
|
}
|
|
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->scalable && face->fs.fsCsb[0] == 0) /* OEM bitmap */
|
|
{
|
|
elf.elfLogFont.lfCharSet = ntm.ntmTm.tmCharSet = OEM_CHARSET;
|
|
load_script_name( IDS_OEM_DOS - IDS_FIRST_SCRIPT, elf.elfScript );
|
|
i = 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;
|
|
|
|
LIST_FOR_EACH_ENTRY( family, &font_list, struct gdi_font_family, 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 );
|
|
LIST_FOR_EACH_ENTRY( family, &font_list, struct gdi_font_family, 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, 0, &wc, 1, &ch, 1, NULL, NULL ))
|
|
glyph = get_glyph_index_symbol( font, (unsigned char)ch );
|
|
}
|
|
}
|
|
else if (WideCharToMultiByte( font->codepage, 0, &wc, 1, &ch, 1, NULL, &used ) && !used)
|
|
{
|
|
glyph = (unsigned char)ch;
|
|
font_funcs->get_glyph_index( font, &glyph, FALSE );
|
|
}
|
|
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 );
|
|
/* 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 );
|
|
}
|
|
}
|
|
|
|
format &= ~(GGO_GLYPH_INDEX | GGO_UNHINTED);
|
|
|
|
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 last, ABC *buffer )
|
|
{
|
|
struct font_physdev *physdev = get_font_dev( dev );
|
|
UINT c;
|
|
|
|
if (!physdev->font)
|
|
{
|
|
dev = GET_NEXT_PHYSDEV( dev, pGetCharABCWidths );
|
|
return dev->funcs->pGetCharABCWidths( dev, first, last, buffer );
|
|
}
|
|
|
|
TRACE( "%p, %u, %u, %p\n", physdev->font, first, last, buffer );
|
|
|
|
EnterCriticalSection( &font_cs );
|
|
for (c = first; c <= last; c++, buffer++)
|
|
get_glyph_outline( physdev->font, c, GGO_METRICS, NULL, buffer, 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 last, INT *buffer )
|
|
{
|
|
struct font_physdev *physdev = get_font_dev( dev );
|
|
ABC abc;
|
|
UINT c;
|
|
|
|
if (!physdev->font)
|
|
{
|
|
dev = GET_NEXT_PHYSDEV( dev, pGetCharWidth );
|
|
return dev->funcs->pGetCharWidth( dev, first, last, buffer );
|
|
}
|
|
|
|
TRACE( "%p, %d, %d, %p\n", physdev->font, first, last, buffer );
|
|
|
|
EnterCriticalSection( &font_cs );
|
|
for (c = first; c <= last; c++)
|
|
{
|
|
if (get_glyph_outline( physdev->font, c, GGO_METRICS, NULL, &abc, 0, NULL, NULL ) == GDI_ERROR)
|
|
buffer[c - first] = 0;
|
|
else
|
|
buffer[c - first] = 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->unk = 0;
|
|
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, 0, &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;
|
|
strcpyW( ptr, (WCHAR *)physdev->font->otm.otmpFamilyName );
|
|
ptr += strlenW(ptr) + 1;
|
|
metrics->otmpStyleName = (char *)ptr - (ULONG_PTR)metrics;
|
|
strcpyW( ptr, (WCHAR *)physdev->font->otm.otmpStyleName );
|
|
ptr += strlenW(ptr) + 1;
|
|
metrics->otmpFaceName = (char *)ptr - (ULONG_PTR)metrics;
|
|
strcpyW( ptr, (WCHAR *)physdev->font->otm.otmpFaceName );
|
|
ptr += strlenW(ptr) + 1;
|
|
metrics->otmpFullName = (char *)ptr - (ULONG_PTR)metrics;
|
|
strcpyW( 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 = strlenW( 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;
|
|
}
|
|
|
|
|
|
/*************************************************************
|
|
* font_SelectFont
|
|
*/
|
|
static HFONT CDECL font_SelectFont( PHYSDEV dev, HFONT hfont, UINT *aa_flags )
|
|
{
|
|
UINT default_aa_flags = *aa_flags;
|
|
struct font_physdev *physdev = get_font_dev( dev );
|
|
struct gdi_font *prev = physdev->font;
|
|
DC *dc = get_physdev_dc( dev );
|
|
|
|
if (!default_aa_flags)
|
|
{
|
|
PHYSDEV next = GET_NEXT_PHYSDEV( dev, pSelectFont );
|
|
next->funcs->pSelectFont( next, hfont, &default_aa_flags );
|
|
}
|
|
|
|
if (hfont)
|
|
{
|
|
EnterCriticalSection( &font_cs );
|
|
physdev->font = font_funcs->pSelectFont( dc, hfont, aa_flags, default_aa_flags );
|
|
LeaveCriticalSection( &font_cs );
|
|
}
|
|
else physdev->font = NULL; /* notification that the font has been changed by another driver */
|
|
|
|
if (prev) release_gdi_font( prev );
|
|
return physdev->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, /* pExcludeClipRect */
|
|
NULL, /* pExtDeviceMode */
|
|
NULL, /* pExtEscape */
|
|
NULL, /* pExtFloodFill */
|
|
NULL, /* pExtSelectClipRgn */
|
|
NULL, /* pExtTextOut */
|
|
NULL, /* pFillPath */
|
|
NULL, /* pFillRgn */
|
|
NULL, /* pFlattenPath */
|
|
font_FontIsLinked, /* pFontIsLinked */
|
|
NULL, /* pFrameRgn */
|
|
NULL, /* pGdiComment */
|
|
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, /* pIntersectClipRect */
|
|
NULL, /* pInvertRgn */
|
|
NULL, /* pLineTo */
|
|
NULL, /* pModifyWorldTransform */
|
|
NULL, /* pMoveTo */
|
|
NULL, /* pOffsetClipRgn */
|
|
NULL, /* pOffsetViewportOrg */
|
|
NULL, /* pOffsetWindowOrg */
|
|
NULL, /* pPaintRgn */
|
|
NULL, /* pPatBlt */
|
|
NULL, /* pPie */
|
|
NULL, /* pPolyBezier */
|
|
NULL, /* pPolyBezierTo */
|
|
NULL, /* pPolyDraw */
|
|
NULL, /* pPolyPolygon */
|
|
NULL, /* pPolyPolyline */
|
|
NULL, /* pPolygon */
|
|
NULL, /* pPolyline */
|
|
NULL, /* pPolylineTo */
|
|
NULL, /* pPutImage */
|
|
NULL, /* pRealizeDefaultPalette */
|
|
NULL, /* pRealizePalette */
|
|
NULL, /* pRectangle */
|
|
NULL, /* pResetDC */
|
|
NULL, /* pRestoreDC */
|
|
NULL, /* pRoundRect */
|
|
NULL, /* pSaveDC */
|
|
NULL, /* pScaleViewportExt */
|
|
NULL, /* pScaleWindowExt */
|
|
NULL, /* pSelectBitmap */
|
|
NULL, /* pSelectBrush */
|
|
NULL, /* pSelectClipPath */
|
|
font_SelectFont, /* pSelectFont */
|
|
NULL, /* pSelectPalette */
|
|
NULL, /* pSelectPen */
|
|
NULL, /* pSetArcDirection */
|
|
NULL, /* pSetBkColor */
|
|
NULL, /* pSetBkMode */
|
|
NULL, /* pSetBoundsRect */
|
|
NULL, /* pSetDCBrushColor */
|
|
NULL, /* pSetDCPenColor */
|
|
NULL, /* pSetDIBitsToDevice */
|
|
NULL, /* pSetDeviceClipping */
|
|
NULL, /* pSetDeviceGammaRamp */
|
|
NULL, /* pSetLayout */
|
|
NULL, /* pSetMapMode */
|
|
NULL, /* pSetMapperFlags */
|
|
NULL, /* pSetPixel */
|
|
NULL, /* pSetPolyFillMode */
|
|
NULL, /* pSetROP2 */
|
|
NULL, /* pSetRelAbs */
|
|
NULL, /* pSetStretchBltMode */
|
|
NULL, /* pSetTextAlign */
|
|
NULL, /* pSetTextCharacterExtra */
|
|
NULL, /* pSetTextColor */
|
|
NULL, /* pSetTextJustification */
|
|
NULL, /* pSetViewportExt */
|
|
NULL, /* pSetViewportOrg */
|
|
NULL, /* pSetWindowExt */
|
|
NULL, /* pSetWindowOrg */
|
|
NULL, /* pSetWorldTransform */
|
|
NULL, /* pStartDoc */
|
|
NULL, /* pStartPage */
|
|
NULL, /* pStretchBlt */
|
|
NULL, /* pStretchDIBits */
|
|
NULL, /* pStrokeAndFillPath */
|
|
NULL, /* pStrokePath */
|
|
NULL, /* pUnrealizePalette */
|
|
NULL, /* pWidenPath */
|
|
NULL, /* pD3DKMTCheckVidPnExclusiveOwnership */
|
|
NULL, /* pD3DKMTSetVidPnSourceOwner */
|
|
NULL, /* wine_get_wgl_driver */
|
|
NULL, /* wine_get_vulkan_driver */
|
|
GDI_PRIORITY_FONT_DRV /* priority */
|
|
};
|
|
|
|
/***********************************************************************
|
|
* font_init
|
|
*/
|
|
void font_init(void)
|
|
{
|
|
if (RegCreateKeyExA( HKEY_CURRENT_USER, "Software\\Wine\\Fonts", 0, NULL, 0,
|
|
KEY_ALL_ACCESS, NULL, &wine_fonts_key, NULL ))
|
|
return;
|
|
|
|
update_codepage();
|
|
WineEngInit( &font_funcs );
|
|
load_gdi_font_subst();
|
|
load_gdi_font_replacements();
|
|
load_system_links();
|
|
dump_gdi_font_list();
|
|
dump_gdi_font_subst();
|
|
}
|
|
|
|
|
|
static void FONT_LogFontAToW( const LOGFONTA *fontA, LPLOGFONTW fontW )
|
|
{
|
|
memcpy(fontW, fontA, sizeof(LOGFONTA) - LF_FACESIZE);
|
|
MultiByteToWideChar(CP_ACP, 0, fontA->lfFaceName, -1, fontW->lfFaceName,
|
|
LF_FACESIZE);
|
|
fontW->lfFaceName[LF_FACESIZE-1] = 0;
|
|
}
|
|
|
|
static void FONT_LogFontWToA( const LOGFONTW *fontW, LPLOGFONTA fontA )
|
|
{
|
|
memcpy(fontA, fontW, sizeof(LOGFONTA) - LF_FACESIZE);
|
|
WideCharToMultiByte(CP_ACP, 0, fontW->lfFaceName, -1, fontA->lfFaceName,
|
|
LF_FACESIZE, NULL, NULL);
|
|
fontA->lfFaceName[LF_FACESIZE-1] = 0;
|
|
}
|
|
|
|
static void FONT_EnumLogFontExWToA( const ENUMLOGFONTEXW *fontW, LPENUMLOGFONTEXA fontA )
|
|
{
|
|
FONT_LogFontWToA( &fontW->elfLogFont, &fontA->elfLogFont );
|
|
|
|
WideCharToMultiByte( CP_ACP, 0, fontW->elfFullName, -1,
|
|
(LPSTR) fontA->elfFullName, LF_FULLFACESIZE, NULL, NULL );
|
|
fontA->elfFullName[LF_FULLFACESIZE-1] = '\0';
|
|
WideCharToMultiByte( CP_ACP, 0, fontW->elfStyle, -1,
|
|
(LPSTR) fontA->elfStyle, LF_FACESIZE, NULL, NULL );
|
|
fontA->elfStyle[LF_FACESIZE-1] = '\0';
|
|
WideCharToMultiByte( CP_ACP, 0, fontW->elfScript, -1,
|
|
(LPSTR) fontA->elfScript, LF_FACESIZE, NULL, NULL );
|
|
fontA->elfScript[LF_FACESIZE-1] = '\0';
|
|
}
|
|
|
|
static void FONT_EnumLogFontExAToW( const ENUMLOGFONTEXA *fontA, LPENUMLOGFONTEXW fontW )
|
|
{
|
|
FONT_LogFontAToW( &fontA->elfLogFont, &fontW->elfLogFont );
|
|
|
|
MultiByteToWideChar( CP_ACP, 0, (LPCSTR)fontA->elfFullName, -1,
|
|
fontW->elfFullName, LF_FULLFACESIZE );
|
|
fontW->elfFullName[LF_FULLFACESIZE-1] = '\0';
|
|
MultiByteToWideChar( CP_ACP, 0, (LPCSTR)fontA->elfStyle, -1,
|
|
fontW->elfStyle, LF_FACESIZE );
|
|
fontW->elfStyle[LF_FACESIZE-1] = '\0';
|
|
MultiByteToWideChar( CP_ACP, 0, (LPCSTR)fontA->elfScript, -1,
|
|
fontW->elfScript, LF_FACESIZE );
|
|
fontW->elfScript[LF_FACESIZE-1] = '\0';
|
|
}
|
|
|
|
/***********************************************************************
|
|
* TEXTMETRIC conversion functions.
|
|
*/
|
|
static void FONT_TextMetricWToA(const TEXTMETRICW *ptmW, LPTEXTMETRICA ptmA )
|
|
{
|
|
ptmA->tmHeight = ptmW->tmHeight;
|
|
ptmA->tmAscent = ptmW->tmAscent;
|
|
ptmA->tmDescent = ptmW->tmDescent;
|
|
ptmA->tmInternalLeading = ptmW->tmInternalLeading;
|
|
ptmA->tmExternalLeading = ptmW->tmExternalLeading;
|
|
ptmA->tmAveCharWidth = ptmW->tmAveCharWidth;
|
|
ptmA->tmMaxCharWidth = ptmW->tmMaxCharWidth;
|
|
ptmA->tmWeight = ptmW->tmWeight;
|
|
ptmA->tmOverhang = ptmW->tmOverhang;
|
|
ptmA->tmDigitizedAspectX = ptmW->tmDigitizedAspectX;
|
|
ptmA->tmDigitizedAspectY = ptmW->tmDigitizedAspectY;
|
|
ptmA->tmFirstChar = min(ptmW->tmFirstChar, 255);
|
|
if (ptmW->tmCharSet == SYMBOL_CHARSET)
|
|
{
|
|
ptmA->tmFirstChar = 0x1e;
|
|
ptmA->tmLastChar = 0xff; /* win9x behaviour - we need the OS2 table data to calculate correctly */
|
|
}
|
|
else if (ptmW->tmPitchAndFamily & TMPF_TRUETYPE)
|
|
{
|
|
ptmA->tmFirstChar = ptmW->tmDefaultChar - 1;
|
|
ptmA->tmLastChar = min(ptmW->tmLastChar, 0xff);
|
|
}
|
|
else
|
|
{
|
|
ptmA->tmFirstChar = min(ptmW->tmFirstChar, 0xff);
|
|
ptmA->tmLastChar = min(ptmW->tmLastChar, 0xff);
|
|
}
|
|
ptmA->tmDefaultChar = ptmW->tmDefaultChar;
|
|
ptmA->tmBreakChar = ptmW->tmBreakChar;
|
|
ptmA->tmItalic = ptmW->tmItalic;
|
|
ptmA->tmUnderlined = ptmW->tmUnderlined;
|
|
ptmA->tmStruckOut = ptmW->tmStruckOut;
|
|
ptmA->tmPitchAndFamily = ptmW->tmPitchAndFamily;
|
|
ptmA->tmCharSet = ptmW->tmCharSet;
|
|
}
|
|
|
|
|
|
static void FONT_NewTextMetricExWToA(const NEWTEXTMETRICEXW *ptmW, NEWTEXTMETRICEXA *ptmA )
|
|
{
|
|
FONT_TextMetricWToA((const TEXTMETRICW *)ptmW, (LPTEXTMETRICA)ptmA);
|
|
ptmA->ntmTm.ntmFlags = ptmW->ntmTm.ntmFlags;
|
|
ptmA->ntmTm.ntmSizeEM = ptmW->ntmTm.ntmSizeEM;
|
|
ptmA->ntmTm.ntmCellHeight = ptmW->ntmTm.ntmCellHeight;
|
|
ptmA->ntmTm.ntmAvgWidth = ptmW->ntmTm.ntmAvgWidth;
|
|
memcpy(&ptmA->ntmFontSig, &ptmW->ntmFontSig, sizeof(FONTSIGNATURE));
|
|
}
|
|
|
|
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 = atoiW( buf );
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static UINT get_subpixel_orientation( HKEY key )
|
|
{
|
|
static const WCHAR smoothing_orientation[] = {'F','o','n','t','S','m','o','o','t','h','i','n','g',
|
|
'O','r','i','e','n','t','a','t','i','o','n',0};
|
|
DWORD orient;
|
|
|
|
/* FIXME: handle vertical orientations even though Windows doesn't */
|
|
if (get_key_value( key, smoothing_orientation, &orient )) return GGO_GRAY4_BITMAP;
|
|
|
|
switch (orient)
|
|
{
|
|
case 0: /* FE_FONTSMOOTHINGORIENTATIONBGR */
|
|
return WINE_GGO_HBGR_BITMAP;
|
|
case 1: /* FE_FONTSMOOTHINGORIENTATIONRGB */
|
|
return WINE_GGO_HRGB_BITMAP;
|
|
}
|
|
return GGO_GRAY4_BITMAP;
|
|
}
|
|
|
|
static UINT get_default_smoothing( HKEY key )
|
|
{
|
|
static const WCHAR smoothing[] = {'F','o','n','t','S','m','o','o','t','h','i','n','g',0};
|
|
static const WCHAR smoothing_type[] = {'F','o','n','t','S','m','o','o','t','h','i','n','g','T','y','p','e',0};
|
|
DWORD enabled, type;
|
|
|
|
if (get_key_value( key, smoothing, &enabled )) return 0;
|
|
if (!enabled) return GGO_BITMAP;
|
|
|
|
if (!get_key_value( key, smoothing_type, &type ) && type == 2 /* FE_FONTSMOOTHINGCLEARTYPE */)
|
|
return get_subpixel_orientation( key );
|
|
|
|
return GGO_GRAY4_BITMAP;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* GdiGetCodePage (GDI32.@)
|
|
*/
|
|
DWORD WINAPI GdiGetCodePage( HDC hdc )
|
|
{
|
|
UINT cp = CP_ACP;
|
|
DC *dc = get_dc_ptr( hdc );
|
|
|
|
if (dc)
|
|
{
|
|
cp = dc->font_code_page;
|
|
release_dc_ptr( dc );
|
|
}
|
|
return cp;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* 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;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* GetTextCharsetInfo (GDI32.@)
|
|
*/
|
|
UINT WINAPI GetTextCharsetInfo(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;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* FONT_mbtowc
|
|
*
|
|
* Returns a Unicode translation of str using the charset of the
|
|
* currently selected font in hdc. If count is -1 then str is assumed
|
|
* to be '\0' terminated, otherwise it contains the number of bytes to
|
|
* convert. If plenW is non-NULL, on return it will point to the
|
|
* number of WCHARs that have been written. If pCP is non-NULL, on
|
|
* return it will point to the codepage used in the conversion. The
|
|
* caller should free the returned LPWSTR from the process heap
|
|
* itself.
|
|
*/
|
|
static LPWSTR FONT_mbtowc(HDC hdc, LPCSTR str, INT count, INT *plenW, UINT *pCP)
|
|
{
|
|
UINT cp;
|
|
INT lenW;
|
|
LPWSTR strW;
|
|
|
|
cp = GdiGetCodePage( hdc );
|
|
|
|
if(count == -1) count = strlen(str);
|
|
lenW = MultiByteToWideChar(cp, 0, str, count, NULL, 0);
|
|
strW = HeapAlloc(GetProcessHeap(), 0, lenW*sizeof(WCHAR));
|
|
MultiByteToWideChar(cp, 0, str, count, strW, lenW);
|
|
TRACE("mapped %s -> %s\n", debugstr_an(str, count), debugstr_wn(strW, lenW));
|
|
if(plenW) *plenW = lenW;
|
|
if(pCP) *pCP = cp;
|
|
return strW;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CreateFontIndirectExA (GDI32.@)
|
|
*/
|
|
HFONT WINAPI CreateFontIndirectExA( const ENUMLOGFONTEXDVA *penumexA )
|
|
{
|
|
ENUMLOGFONTEXDVW enumexW;
|
|
|
|
if (!penumexA) return 0;
|
|
|
|
FONT_EnumLogFontExAToW( &penumexA->elfEnumLogfontEx, &enumexW.elfEnumLogfontEx );
|
|
enumexW.elfDesignVector = penumexA->elfDesignVector;
|
|
return CreateFontIndirectExW( &enumexW );
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CreateFontIndirectExW (GDI32.@)
|
|
*/
|
|
HFONT WINAPI CreateFontIndirectExW( const ENUMLOGFONTEXDVW *penumex )
|
|
{
|
|
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_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;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CreateFontIndirectA (GDI32.@)
|
|
*/
|
|
HFONT WINAPI CreateFontIndirectA( const LOGFONTA *plfA )
|
|
{
|
|
LOGFONTW lfW;
|
|
|
|
if (!plfA) return 0;
|
|
|
|
FONT_LogFontAToW( plfA, &lfW );
|
|
return CreateFontIndirectW( &lfW );
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CreateFontIndirectW (GDI32.@)
|
|
*/
|
|
HFONT WINAPI CreateFontIndirectW( const LOGFONTW *plf )
|
|
{
|
|
ENUMLOGFONTEXDVW exdv;
|
|
|
|
if (!plf) return 0;
|
|
|
|
exdv.elfEnumLogfontEx.elfLogFont = *plf;
|
|
exdv.elfEnumLogfontEx.elfFullName[0] = 0;
|
|
exdv.elfEnumLogfontEx.elfStyle[0] = 0;
|
|
exdv.elfEnumLogfontEx.elfScript[0] = 0;
|
|
return CreateFontIndirectExW( &exdv );
|
|
}
|
|
|
|
/*************************************************************************
|
|
* CreateFontA (GDI32.@)
|
|
*/
|
|
HFONT WINAPI CreateFontA( INT height, INT width, INT esc,
|
|
INT orient, INT weight, DWORD italic,
|
|
DWORD underline, DWORD strikeout, DWORD charset,
|
|
DWORD outpres, DWORD clippres, DWORD quality,
|
|
DWORD pitch, LPCSTR name )
|
|
{
|
|
LOGFONTA logfont;
|
|
|
|
logfont.lfHeight = height;
|
|
logfont.lfWidth = width;
|
|
logfont.lfEscapement = esc;
|
|
logfont.lfOrientation = orient;
|
|
logfont.lfWeight = weight;
|
|
logfont.lfItalic = italic;
|
|
logfont.lfUnderline = underline;
|
|
logfont.lfStrikeOut = strikeout;
|
|
logfont.lfCharSet = charset;
|
|
logfont.lfOutPrecision = outpres;
|
|
logfont.lfClipPrecision = clippres;
|
|
logfont.lfQuality = quality;
|
|
logfont.lfPitchAndFamily = pitch;
|
|
|
|
if (name)
|
|
lstrcpynA(logfont.lfFaceName,name,sizeof(logfont.lfFaceName));
|
|
else
|
|
logfont.lfFaceName[0] = '\0';
|
|
|
|
return CreateFontIndirectA( &logfont );
|
|
}
|
|
|
|
/*************************************************************************
|
|
* CreateFontW (GDI32.@)
|
|
*/
|
|
HFONT WINAPI CreateFontW( INT height, INT width, INT esc,
|
|
INT orient, INT weight, DWORD italic,
|
|
DWORD underline, DWORD strikeout, DWORD charset,
|
|
DWORD outpres, DWORD clippres, DWORD quality,
|
|
DWORD pitch, LPCWSTR name )
|
|
{
|
|
LOGFONTW logfont;
|
|
|
|
logfont.lfHeight = height;
|
|
logfont.lfWidth = width;
|
|
logfont.lfEscapement = esc;
|
|
logfont.lfOrientation = orient;
|
|
logfont.lfWeight = weight;
|
|
logfont.lfItalic = italic;
|
|
logfont.lfUnderline = underline;
|
|
logfont.lfStrikeOut = strikeout;
|
|
logfont.lfCharSet = charset;
|
|
logfont.lfOutPrecision = outpres;
|
|
logfont.lfClipPrecision = clippres;
|
|
logfont.lfQuality = quality;
|
|
logfont.lfPitchAndFamily = pitch;
|
|
|
|
if (name)
|
|
lstrcpynW(logfont.lfFaceName, name, ARRAY_SIZE(logfont.lfFaceName));
|
|
else
|
|
logfont.lfFaceName[0] = '\0';
|
|
|
|
return CreateFontIndirectW( &logfont );
|
|
}
|
|
|
|
#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)
|
|
{
|
|
static const WCHAR assoc_charset_reg_keyW[] = {'S','y','s','t','e','m','\\',
|
|
'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
|
|
'C','o','n','t','r','o','l','\\','F','o','n','t','A','s','s','o','c','\\',
|
|
'A','s','s','o','c','i','a','t','e','d',' ','C','h','a','r','s','e','t','\0'};
|
|
static const WCHAR ansiW[] = {'A','N','S','I','(','0','0',')','\0'};
|
|
static const WCHAR oemW[] = {'O','E','M','(','F','F',')','\0'};
|
|
static const WCHAR symbolW[] = {'S','Y','M','B','O','L','(','0','2',')','\0'};
|
|
static const WCHAR yesW[] = {'Y','E','S','\0'};
|
|
HKEY hkey;
|
|
WCHAR dataW[32];
|
|
DWORD type, data_len;
|
|
|
|
associated_charset = 0;
|
|
|
|
if (RegOpenKeyW(HKEY_LOCAL_MACHINE,
|
|
assoc_charset_reg_keyW, &hkey) != ERROR_SUCCESS)
|
|
return 0;
|
|
|
|
data_len = sizeof(dataW);
|
|
if (!RegQueryValueExW(hkey, ansiW, NULL, &type, (LPBYTE)dataW, &data_len) &&
|
|
type == REG_SZ && !strcmpiW(dataW, yesW))
|
|
associated_charset |= ASSOC_CHARSET_ANSI;
|
|
|
|
data_len = sizeof(dataW);
|
|
if (!RegQueryValueExW(hkey, oemW, NULL, &type, (LPBYTE)dataW, &data_len) &&
|
|
type == REG_SZ && !strcmpiW(dataW, yesW))
|
|
associated_charset |= ASSOC_CHARSET_OEM;
|
|
|
|
data_len = sizeof(dataW);
|
|
if (!RegQueryValueExW(hkey, symbolW, NULL, &type, (LPBYTE)dataW, &data_len) &&
|
|
type == REG_SZ && !strcmpiW(dataW, yesW))
|
|
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 (TranslateCharsetInfo( ULongToPtr(charset), &csi, TCI_SRCCHARSET) )
|
|
dc->font_code_page = csi.ciACP;
|
|
else {
|
|
switch(charset) {
|
|
case OEM_CHARSET:
|
|
dc->font_code_page = GetOEMCP();
|
|
break;
|
|
case DEFAULT_CHARSET:
|
|
dc->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->font_code_page = CP_ACP;
|
|
break;
|
|
|
|
default:
|
|
FIXME("Can't find codepage for charset %d\n", charset);
|
|
dc->font_code_page = CP_ACP;
|
|
break;
|
|
}
|
|
}
|
|
|
|
TRACE("charset %d => cp %d\n", charset, dc->font_code_page);
|
|
}
|
|
|
|
static BOOL WINAPI fill_font_gamma_ramp( INIT_ONCE *once, void *param, void **context )
|
|
{
|
|
static const WCHAR desktopW[] = { 'C','o','n','t','r','o','l',' ','P','a','n','e','l','\\',
|
|
'D','e','s','k','t','o','p',0 };
|
|
static const WCHAR smoothing_gamma[] = { 'F','o','n','t','S','m','o','o','t','h','i','n','g',
|
|
'G','a','m','m','a',0 };
|
|
struct font_gamma_ramp *ramp = param;
|
|
const DWORD gamma_default = 1400;
|
|
DWORD i, gamma;
|
|
HKEY key;
|
|
|
|
gamma = gamma_default;
|
|
if (RegOpenKeyW( HKEY_CURRENT_USER, desktopW, &key ) == ERROR_SUCCESS)
|
|
{
|
|
if (get_key_value( key, smoothing_gamma, &gamma ) || gamma == 0)
|
|
gamma = gamma_default;
|
|
RegCloseKey( key );
|
|
|
|
gamma = min( max( gamma, 1000 ), 2200 );
|
|
}
|
|
|
|
/* 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;
|
|
|
|
for (i = 0; i < 256; i++)
|
|
{
|
|
ramp->encode[i] = pow( i / 255., 1000. / gamma ) * 255. + .5;
|
|
ramp->decode[i] = pow( i / 255., gamma / 1000. ) * 255. + .5;
|
|
}
|
|
|
|
ramp->gamma = gamma;
|
|
TRACE("gamma %d\n", ramp->gamma);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static struct font_gamma_ramp *get_font_gamma_ramp( void )
|
|
{
|
|
static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT;
|
|
static struct font_gamma_ramp ramp;
|
|
|
|
InitOnceExecuteOnce( &init_once, fill_font_gamma_ramp, &ramp, NULL );
|
|
return &ramp;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* FONT_SelectObject
|
|
*/
|
|
static HGDIOBJ FONT_SelectObject( HGDIOBJ handle, HDC hdc )
|
|
{
|
|
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 = get_font_gamma_ramp();
|
|
GDI_dec_ref_count( ret );
|
|
}
|
|
else GDI_dec_ref_count( handle );
|
|
|
|
release_dc_ptr( dc );
|
|
return ret;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* FONT_GetObjectA
|
|
*/
|
|
static INT FONT_GetObjectA( HGDIOBJ handle, INT count, LPVOID buffer )
|
|
{
|
|
FONTOBJ *font = GDI_GetObjPtr( handle, OBJ_FONT );
|
|
LOGFONTA lfA;
|
|
|
|
if (!font) return 0;
|
|
if (buffer)
|
|
{
|
|
FONT_LogFontWToA( &font->logfont, &lfA );
|
|
if (count > sizeof(lfA)) count = sizeof(lfA);
|
|
memcpy( buffer, &lfA, count );
|
|
}
|
|
else count = sizeof(lfA);
|
|
GDI_ReleaseObj( handle );
|
|
return count;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* FONT_GetObjectW
|
|
*/
|
|
static INT FONT_GetObjectW( HGDIOBJ handle, INT count, LPVOID buffer )
|
|
{
|
|
FONTOBJ *font = GDI_GetObjPtr( handle, 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;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* nulldrv_SelectFont
|
|
*/
|
|
HFONT CDECL nulldrv_SelectFont( PHYSDEV dev, HFONT font, UINT *aa_flags )
|
|
{
|
|
static const WCHAR desktopW[] = { 'C','o','n','t','r','o','l',' ','P','a','n','e','l','\\',
|
|
'D','e','s','k','t','o','p',0 };
|
|
static int orientation = -1, smoothing = -1;
|
|
LOGFONTW lf;
|
|
HKEY key;
|
|
|
|
if (*aa_flags) return 0;
|
|
|
|
GetObjectW( font, sizeof(lf), &lf );
|
|
switch (lf.lfQuality)
|
|
{
|
|
case NONANTIALIASED_QUALITY:
|
|
*aa_flags = GGO_BITMAP;
|
|
break;
|
|
case ANTIALIASED_QUALITY:
|
|
*aa_flags = GGO_GRAY4_BITMAP;
|
|
break;
|
|
case CLEARTYPE_QUALITY:
|
|
case CLEARTYPE_NATURAL_QUALITY:
|
|
if (orientation == -1)
|
|
{
|
|
if (RegOpenKeyW( HKEY_CURRENT_USER, desktopW, &key )) break;
|
|
orientation = get_subpixel_orientation( key );
|
|
RegCloseKey( key );
|
|
}
|
|
*aa_flags = orientation;
|
|
break;
|
|
default:
|
|
if (smoothing == -1)
|
|
{
|
|
if (RegOpenKeyW( HKEY_CURRENT_USER, desktopW, &key )) break;
|
|
smoothing = get_default_smoothing( key );
|
|
RegCloseKey( key );
|
|
}
|
|
*aa_flags = smoothing;
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* FONT_EnumInstance
|
|
*
|
|
* Note: plf is really an ENUMLOGFONTEXW, and ptm is a NEWTEXTMETRICEXW.
|
|
* We have to use other types because of the FONTENUMPROCW definition.
|
|
*/
|
|
static INT CALLBACK FONT_EnumInstance( const LOGFONTW *plf, const TEXTMETRICW *ptm,
|
|
DWORD fType, LPARAM lp )
|
|
{
|
|
struct font_enum *pfe = (struct font_enum *)lp;
|
|
INT ret = 1;
|
|
|
|
/* lfCharSet is at the same offset in both LOGFONTA and LOGFONTW */
|
|
if ((!pfe->lpLogFontParam ||
|
|
pfe->lpLogFontParam->lfCharSet == DEFAULT_CHARSET ||
|
|
pfe->lpLogFontParam->lfCharSet == plf->lfCharSet) &&
|
|
(!(fType & RASTER_FONTTYPE) || GetDeviceCaps(pfe->hdc, TEXTCAPS) & TC_RA_ABLE) )
|
|
{
|
|
/* convert font metrics */
|
|
ENUMLOGFONTEXA logfont;
|
|
NEWTEXTMETRICEXA tmA;
|
|
|
|
if (!pfe->unicode)
|
|
{
|
|
FONT_EnumLogFontExWToA( (const ENUMLOGFONTEXW *)plf, &logfont);
|
|
FONT_NewTextMetricExWToA( (const NEWTEXTMETRICEXW *)ptm, &tmA );
|
|
plf = (LOGFONTW *)&logfont.elfLogFont;
|
|
ptm = (TEXTMETRICW *)&tmA;
|
|
}
|
|
ret = pfe->lpEnumFunc( plf, ptm, fType, pfe->lpData );
|
|
pfe->retval = ret;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* FONT_EnumFontFamiliesEx
|
|
*/
|
|
static INT FONT_EnumFontFamiliesEx( HDC hDC, LPLOGFONTW plf, FONTENUMPROCW efproc,
|
|
LPARAM lParam, BOOL unicode )
|
|
{
|
|
INT ret = 0;
|
|
DC *dc = get_dc_ptr( hDC );
|
|
struct font_enum fe;
|
|
|
|
if (dc)
|
|
{
|
|
PHYSDEV physdev = GET_DC_PHYSDEV( dc, pEnumFonts );
|
|
|
|
if (plf) TRACE("lfFaceName = %s lfCharset = %d\n", debugstr_w(plf->lfFaceName), plf->lfCharSet);
|
|
fe.lpLogFontParam = plf;
|
|
fe.lpEnumFunc = efproc;
|
|
fe.lpData = lParam;
|
|
fe.unicode = unicode;
|
|
fe.hdc = hDC;
|
|
fe.retval = 1;
|
|
ret = physdev->funcs->pEnumFonts( physdev, plf, FONT_EnumInstance, (LPARAM)&fe );
|
|
release_dc_ptr( dc );
|
|
}
|
|
return ret ? fe.retval : 0;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* EnumFontFamiliesExW (GDI32.@)
|
|
*/
|
|
INT WINAPI EnumFontFamiliesExW( HDC hDC, LPLOGFONTW plf,
|
|
FONTENUMPROCW efproc,
|
|
LPARAM lParam, DWORD dwFlags )
|
|
{
|
|
return FONT_EnumFontFamiliesEx( hDC, plf, efproc, lParam, TRUE );
|
|
}
|
|
|
|
/***********************************************************************
|
|
* EnumFontFamiliesExA (GDI32.@)
|
|
*/
|
|
INT WINAPI EnumFontFamiliesExA( HDC hDC, LPLOGFONTA plf,
|
|
FONTENUMPROCA efproc,
|
|
LPARAM lParam, DWORD dwFlags)
|
|
{
|
|
LOGFONTW lfW, *plfW;
|
|
|
|
if (plf)
|
|
{
|
|
FONT_LogFontAToW( plf, &lfW );
|
|
plfW = &lfW;
|
|
}
|
|
else plfW = NULL;
|
|
|
|
return FONT_EnumFontFamiliesEx( hDC, plfW, (FONTENUMPROCW)efproc, lParam, FALSE );
|
|
}
|
|
|
|
/***********************************************************************
|
|
* EnumFontFamiliesA (GDI32.@)
|
|
*/
|
|
INT WINAPI EnumFontFamiliesA( HDC hDC, LPCSTR lpFamily,
|
|
FONTENUMPROCA efproc, LPARAM lpData )
|
|
{
|
|
LOGFONTA lf, *plf;
|
|
|
|
if (lpFamily)
|
|
{
|
|
if (!*lpFamily) return 1;
|
|
lstrcpynA( lf.lfFaceName, lpFamily, LF_FACESIZE );
|
|
lf.lfCharSet = DEFAULT_CHARSET;
|
|
lf.lfPitchAndFamily = 0;
|
|
plf = &lf;
|
|
}
|
|
else plf = NULL;
|
|
|
|
return EnumFontFamiliesExA( hDC, plf, efproc, lpData, 0 );
|
|
}
|
|
|
|
/***********************************************************************
|
|
* EnumFontFamiliesW (GDI32.@)
|
|
*/
|
|
INT WINAPI EnumFontFamiliesW( HDC hDC, LPCWSTR lpFamily,
|
|
FONTENUMPROCW efproc, LPARAM lpData )
|
|
{
|
|
LOGFONTW lf, *plf;
|
|
|
|
if (lpFamily)
|
|
{
|
|
if (!*lpFamily) return 1;
|
|
lstrcpynW( lf.lfFaceName, lpFamily, LF_FACESIZE );
|
|
lf.lfCharSet = DEFAULT_CHARSET;
|
|
lf.lfPitchAndFamily = 0;
|
|
plf = &lf;
|
|
}
|
|
else plf = NULL;
|
|
|
|
return EnumFontFamiliesExW( hDC, plf, efproc, lpData, 0 );
|
|
}
|
|
|
|
/***********************************************************************
|
|
* EnumFontsA (GDI32.@)
|
|
*/
|
|
INT WINAPI EnumFontsA( HDC hDC, LPCSTR lpName, FONTENUMPROCA efproc,
|
|
LPARAM lpData )
|
|
{
|
|
return EnumFontFamiliesA( hDC, lpName, efproc, lpData );
|
|
}
|
|
|
|
/***********************************************************************
|
|
* EnumFontsW (GDI32.@)
|
|
*/
|
|
INT WINAPI EnumFontsW( HDC hDC, LPCWSTR lpName, FONTENUMPROCW efproc,
|
|
LPARAM lpData )
|
|
{
|
|
return EnumFontFamiliesW( hDC, lpName, efproc, lpData );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* GetTextCharacterExtra (GDI32.@)
|
|
*/
|
|
INT WINAPI GetTextCharacterExtra( HDC hdc )
|
|
{
|
|
INT ret;
|
|
DC *dc = get_dc_ptr( hdc );
|
|
if (!dc) return 0x80000000;
|
|
ret = dc->charExtra;
|
|
release_dc_ptr( dc );
|
|
return ret;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* SetTextCharacterExtra (GDI32.@)
|
|
*/
|
|
INT WINAPI SetTextCharacterExtra( HDC hdc, INT extra )
|
|
{
|
|
INT ret = 0x80000000;
|
|
DC * dc = get_dc_ptr( hdc );
|
|
|
|
if (dc)
|
|
{
|
|
PHYSDEV physdev = GET_DC_PHYSDEV( dc, pSetTextCharacterExtra );
|
|
extra = physdev->funcs->pSetTextCharacterExtra( physdev, extra );
|
|
if (extra != 0x80000000)
|
|
{
|
|
ret = dc->charExtra;
|
|
dc->charExtra = extra;
|
|
}
|
|
release_dc_ptr( dc );
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* SetTextJustification (GDI32.@)
|
|
*/
|
|
BOOL WINAPI SetTextJustification( HDC hdc, INT extra, INT breaks )
|
|
{
|
|
BOOL ret;
|
|
PHYSDEV physdev;
|
|
DC * dc = get_dc_ptr( hdc );
|
|
|
|
if (!dc) return FALSE;
|
|
|
|
physdev = GET_DC_PHYSDEV( dc, pSetTextJustification );
|
|
ret = physdev->funcs->pSetTextJustification( physdev, extra, breaks );
|
|
if (ret)
|
|
{
|
|
extra = abs((extra * dc->vport_ext.cx + dc->wnd_ext.cx / 2) / dc->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 ret;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* GetTextFaceA (GDI32.@)
|
|
*/
|
|
INT WINAPI GetTextFaceA( HDC hdc, INT count, LPSTR name )
|
|
{
|
|
INT res = GetTextFaceW(hdc, 0, NULL);
|
|
LPWSTR nameW = HeapAlloc( GetProcessHeap(), 0, res * 2 );
|
|
GetTextFaceW( hdc, res, nameW );
|
|
|
|
if (name)
|
|
{
|
|
if (count)
|
|
{
|
|
res = WideCharToMultiByte(CP_ACP, 0, nameW, -1, name, count, NULL, NULL);
|
|
if (res == 0)
|
|
res = count;
|
|
name[count-1] = 0;
|
|
/* GetTextFaceA does NOT include the nul byte in the return count. */
|
|
res--;
|
|
}
|
|
else
|
|
res = 0;
|
|
}
|
|
else
|
|
res = WideCharToMultiByte( CP_ACP, 0, nameW, -1, NULL, 0, NULL, NULL);
|
|
HeapFree( GetProcessHeap(), 0, nameW );
|
|
return res;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* GetTextFaceW (GDI32.@)
|
|
*/
|
|
INT WINAPI GetTextFaceW( HDC hdc, INT count, LPWSTR 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;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* GetTextExtentPoint32A (GDI32.@)
|
|
*
|
|
* See GetTextExtentPoint32W.
|
|
*/
|
|
BOOL WINAPI GetTextExtentPoint32A( HDC hdc, LPCSTR str, INT count,
|
|
LPSIZE size )
|
|
{
|
|
BOOL ret = FALSE;
|
|
INT wlen;
|
|
LPWSTR p;
|
|
|
|
if (count < 0) return FALSE;
|
|
|
|
p = FONT_mbtowc(hdc, str, count, &wlen, NULL);
|
|
|
|
if (p)
|
|
{
|
|
ret = GetTextExtentPoint32W( hdc, p, wlen, size );
|
|
HeapFree( GetProcessHeap(), 0, p );
|
|
}
|
|
|
|
TRACE("(%p %s %d %p): returning %d x %d\n",
|
|
hdc, debugstr_an (str, count), count, size, size->cx, size->cy );
|
|
return ret;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* GetTextExtentPoint32W [GDI32.@]
|
|
*
|
|
* Computes width/height for a string.
|
|
*
|
|
* Computes width and height of the specified string.
|
|
*
|
|
* RETURNS
|
|
* Success: TRUE
|
|
* Failure: FALSE
|
|
*/
|
|
BOOL WINAPI GetTextExtentPoint32W(
|
|
HDC hdc, /* [in] Handle of device context */
|
|
LPCWSTR str, /* [in] Address of text string */
|
|
INT count, /* [in] Number of characters in string */
|
|
LPSIZE size) /* [out] Address of structure for string size */
|
|
{
|
|
return GetTextExtentExPointW(hdc, str, count, 0, NULL, NULL, size);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* GetTextExtentExPointI [GDI32.@]
|
|
*
|
|
* Computes width and height of the array of glyph indices.
|
|
*
|
|
* PARAMS
|
|
* hdc [I] Handle of device context.
|
|
* indices [I] Glyph index array.
|
|
* count [I] Number of glyphs in array.
|
|
* max_ext [I] Maximum width in glyphs.
|
|
* nfit [O] Maximum number of characters.
|
|
* dxs [O] Partial string widths.
|
|
* size [O] Returned string size.
|
|
*
|
|
* RETURNS
|
|
* Success: TRUE
|
|
* Failure: FALSE
|
|
*/
|
|
BOOL WINAPI GetTextExtentExPointI( HDC hdc, const WORD *indices, INT count, INT max_ext,
|
|
LPINT nfit, LPINT dxs, LPSIZE size )
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
ret = get_char_positions_indices( dc, indices, 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->charExtra;
|
|
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->charExtra;
|
|
size->cy = abs( INTERNAL_YDSTOWS( dc, size->cy ));
|
|
}
|
|
|
|
if (pos != buffer && pos != dxs) HeapFree( GetProcessHeap(), 0, pos );
|
|
release_dc_ptr( dc );
|
|
|
|
TRACE("(%p %p %d %p): returning %d x %d\n",
|
|
hdc, indices, count, size, size->cx, size->cy );
|
|
return ret;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* GetTextExtentPointI [GDI32.@]
|
|
*
|
|
* Computes width and height of the array of glyph indices.
|
|
*
|
|
* PARAMS
|
|
* hdc [I] Handle of device context.
|
|
* indices [I] Glyph index array.
|
|
* count [I] Number of glyphs in array.
|
|
* size [O] Returned string size.
|
|
*
|
|
* RETURNS
|
|
* Success: TRUE
|
|
* Failure: FALSE
|
|
*/
|
|
BOOL WINAPI GetTextExtentPointI( HDC hdc, const WORD *indices, INT count, LPSIZE size )
|
|
{
|
|
return GetTextExtentExPointI( hdc, indices, count, 0, NULL, NULL, size );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* GetTextExtentPointA (GDI32.@)
|
|
*/
|
|
BOOL WINAPI GetTextExtentPointA( HDC hdc, LPCSTR str, INT count,
|
|
LPSIZE size )
|
|
{
|
|
TRACE("not bug compatible.\n");
|
|
return GetTextExtentPoint32A( hdc, str, count, size );
|
|
}
|
|
|
|
/***********************************************************************
|
|
* GetTextExtentPointW (GDI32.@)
|
|
*/
|
|
BOOL WINAPI GetTextExtentPointW( HDC hdc, LPCWSTR str, INT count,
|
|
LPSIZE size )
|
|
{
|
|
TRACE("not bug compatible.\n");
|
|
return GetTextExtentPoint32W( hdc, str, count, size );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* GetTextExtentExPointA (GDI32.@)
|
|
*/
|
|
BOOL WINAPI GetTextExtentExPointA( HDC hdc, LPCSTR str, INT count,
|
|
INT maxExt, LPINT lpnFit,
|
|
LPINT alpDx, LPSIZE size )
|
|
{
|
|
BOOL ret;
|
|
INT wlen;
|
|
INT *walpDx = NULL;
|
|
LPWSTR p = NULL;
|
|
|
|
if (count < 0) return FALSE;
|
|
if (maxExt < -1) return FALSE;
|
|
|
|
if (alpDx)
|
|
{
|
|
walpDx = HeapAlloc( GetProcessHeap(), 0, count * sizeof(INT) );
|
|
if (!walpDx) return FALSE;
|
|
}
|
|
|
|
p = FONT_mbtowc(hdc, str, count, &wlen, NULL);
|
|
ret = GetTextExtentExPointW( hdc, p, wlen, maxExt, lpnFit, walpDx, size);
|
|
if (walpDx)
|
|
{
|
|
INT n = lpnFit ? *lpnFit : wlen;
|
|
INT i, j;
|
|
for(i = 0, j = 0; i < n; i++, j++)
|
|
{
|
|
alpDx[j] = walpDx[i];
|
|
if (IsDBCSLeadByte(str[j])) alpDx[++j] = walpDx[i];
|
|
}
|
|
}
|
|
if (lpnFit) *lpnFit = WideCharToMultiByte(CP_ACP,0,p,*lpnFit,NULL,0,NULL,NULL);
|
|
HeapFree( GetProcessHeap(), 0, p );
|
|
HeapFree( GetProcessHeap(), 0, walpDx );
|
|
return ret;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* GetTextExtentExPointW (GDI32.@)
|
|
*
|
|
* Return the size of the string as it would be if it was output properly by
|
|
* e.g. TextOut.
|
|
*/
|
|
BOOL WINAPI GetTextExtentExPointW( HDC hdc, LPCWSTR str, INT count, INT max_ext,
|
|
LPINT nfit, LPINT dxs, LPSIZE size )
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
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->charExtra;
|
|
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->charExtra;
|
|
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;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* GetTextMetricsA (GDI32.@)
|
|
*/
|
|
BOOL WINAPI GetTextMetricsA( HDC hdc, TEXTMETRICA *metrics )
|
|
{
|
|
TEXTMETRICW tm32;
|
|
|
|
if (!GetTextMetricsW( hdc, &tm32 )) return FALSE;
|
|
FONT_TextMetricWToA( &tm32, metrics );
|
|
return TRUE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* GetTextMetricsW (GDI32.@)
|
|
*/
|
|
BOOL WINAPI GetTextMetricsW( HDC hdc, TEXTMETRICW *metrics )
|
|
{
|
|
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;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* GetOutlineTextMetricsA (GDI32.@)
|
|
* Gets metrics for TrueType fonts.
|
|
*
|
|
* NOTES
|
|
* If the supplied buffer isn't big enough Windows partially fills it up to
|
|
* its given length and returns that length.
|
|
*
|
|
* RETURNS
|
|
* Success: Non-zero or size of required buffer
|
|
* Failure: 0
|
|
*/
|
|
UINT WINAPI GetOutlineTextMetricsA(
|
|
HDC hdc, /* [in] Handle of device context */
|
|
UINT cbData, /* [in] Size of metric data array */
|
|
LPOUTLINETEXTMETRICA lpOTM) /* [out] Address of metric data array */
|
|
{
|
|
char buf[512], *ptr;
|
|
UINT ret, needed;
|
|
OUTLINETEXTMETRICW *lpOTMW = (OUTLINETEXTMETRICW *)buf;
|
|
OUTLINETEXTMETRICA *output = lpOTM;
|
|
INT left, len;
|
|
|
|
if((ret = GetOutlineTextMetricsW(hdc, 0, NULL)) == 0)
|
|
return 0;
|
|
if(ret > sizeof(buf))
|
|
lpOTMW = HeapAlloc(GetProcessHeap(), 0, ret);
|
|
GetOutlineTextMetricsW(hdc, ret, lpOTMW);
|
|
|
|
needed = sizeof(OUTLINETEXTMETRICA);
|
|
if(lpOTMW->otmpFamilyName)
|
|
needed += WideCharToMultiByte(CP_ACP, 0,
|
|
(WCHAR*)((char*)lpOTMW + (ptrdiff_t)lpOTMW->otmpFamilyName), -1,
|
|
NULL, 0, NULL, NULL);
|
|
if(lpOTMW->otmpFaceName)
|
|
needed += WideCharToMultiByte(CP_ACP, 0,
|
|
(WCHAR*)((char*)lpOTMW + (ptrdiff_t)lpOTMW->otmpFaceName), -1,
|
|
NULL, 0, NULL, NULL);
|
|
if(lpOTMW->otmpStyleName)
|
|
needed += WideCharToMultiByte(CP_ACP, 0,
|
|
(WCHAR*)((char*)lpOTMW + (ptrdiff_t)lpOTMW->otmpStyleName), -1,
|
|
NULL, 0, NULL, NULL);
|
|
if(lpOTMW->otmpFullName)
|
|
needed += WideCharToMultiByte(CP_ACP, 0,
|
|
(WCHAR*)((char*)lpOTMW + (ptrdiff_t)lpOTMW->otmpFullName), -1,
|
|
NULL, 0, NULL, NULL);
|
|
|
|
if(!lpOTM) {
|
|
ret = needed;
|
|
goto end;
|
|
}
|
|
|
|
TRACE("needed = %d\n", needed);
|
|
if(needed > cbData)
|
|
/* Since the supplied buffer isn't big enough, we'll alloc one
|
|
that is and memcpy the first cbData bytes into the lpOTM at
|
|
the end. */
|
|
output = HeapAlloc(GetProcessHeap(), 0, needed);
|
|
|
|
ret = output->otmSize = min(needed, cbData);
|
|
FONT_TextMetricWToA( &lpOTMW->otmTextMetrics, &output->otmTextMetrics );
|
|
output->otmFiller = 0;
|
|
output->otmPanoseNumber = lpOTMW->otmPanoseNumber;
|
|
output->otmfsSelection = lpOTMW->otmfsSelection;
|
|
output->otmfsType = lpOTMW->otmfsType;
|
|
output->otmsCharSlopeRise = lpOTMW->otmsCharSlopeRise;
|
|
output->otmsCharSlopeRun = lpOTMW->otmsCharSlopeRun;
|
|
output->otmItalicAngle = lpOTMW->otmItalicAngle;
|
|
output->otmEMSquare = lpOTMW->otmEMSquare;
|
|
output->otmAscent = lpOTMW->otmAscent;
|
|
output->otmDescent = lpOTMW->otmDescent;
|
|
output->otmLineGap = lpOTMW->otmLineGap;
|
|
output->otmsCapEmHeight = lpOTMW->otmsCapEmHeight;
|
|
output->otmsXHeight = lpOTMW->otmsXHeight;
|
|
output->otmrcFontBox = lpOTMW->otmrcFontBox;
|
|
output->otmMacAscent = lpOTMW->otmMacAscent;
|
|
output->otmMacDescent = lpOTMW->otmMacDescent;
|
|
output->otmMacLineGap = lpOTMW->otmMacLineGap;
|
|
output->otmusMinimumPPEM = lpOTMW->otmusMinimumPPEM;
|
|
output->otmptSubscriptSize = lpOTMW->otmptSubscriptSize;
|
|
output->otmptSubscriptOffset = lpOTMW->otmptSubscriptOffset;
|
|
output->otmptSuperscriptSize = lpOTMW->otmptSuperscriptSize;
|
|
output->otmptSuperscriptOffset = lpOTMW->otmptSuperscriptOffset;
|
|
output->otmsStrikeoutSize = lpOTMW->otmsStrikeoutSize;
|
|
output->otmsStrikeoutPosition = lpOTMW->otmsStrikeoutPosition;
|
|
output->otmsUnderscoreSize = lpOTMW->otmsUnderscoreSize;
|
|
output->otmsUnderscorePosition = lpOTMW->otmsUnderscorePosition;
|
|
|
|
|
|
ptr = (char*)(output + 1);
|
|
left = needed - sizeof(*output);
|
|
|
|
if(lpOTMW->otmpFamilyName) {
|
|
output->otmpFamilyName = (LPSTR)(ptr - (char*)output);
|
|
len = WideCharToMultiByte(CP_ACP, 0,
|
|
(WCHAR*)((char*)lpOTMW + (ptrdiff_t)lpOTMW->otmpFamilyName), -1,
|
|
ptr, left, NULL, NULL);
|
|
left -= len;
|
|
ptr += len;
|
|
} else
|
|
output->otmpFamilyName = 0;
|
|
|
|
if(lpOTMW->otmpFaceName) {
|
|
output->otmpFaceName = (LPSTR)(ptr - (char*)output);
|
|
len = WideCharToMultiByte(CP_ACP, 0,
|
|
(WCHAR*)((char*)lpOTMW + (ptrdiff_t)lpOTMW->otmpFaceName), -1,
|
|
ptr, left, NULL, NULL);
|
|
left -= len;
|
|
ptr += len;
|
|
} else
|
|
output->otmpFaceName = 0;
|
|
|
|
if(lpOTMW->otmpStyleName) {
|
|
output->otmpStyleName = (LPSTR)(ptr - (char*)output);
|
|
len = WideCharToMultiByte(CP_ACP, 0,
|
|
(WCHAR*)((char*)lpOTMW + (ptrdiff_t)lpOTMW->otmpStyleName), -1,
|
|
ptr, left, NULL, NULL);
|
|
left -= len;
|
|
ptr += len;
|
|
} else
|
|
output->otmpStyleName = 0;
|
|
|
|
if(lpOTMW->otmpFullName) {
|
|
output->otmpFullName = (LPSTR)(ptr - (char*)output);
|
|
len = WideCharToMultiByte(CP_ACP, 0,
|
|
(WCHAR*)((char*)lpOTMW + (ptrdiff_t)lpOTMW->otmpFullName), -1,
|
|
ptr, left, NULL, NULL);
|
|
left -= len;
|
|
} else
|
|
output->otmpFullName = 0;
|
|
|
|
assert(left == 0);
|
|
|
|
if(output != lpOTM) {
|
|
memcpy(lpOTM, output, cbData);
|
|
HeapFree(GetProcessHeap(), 0, output);
|
|
|
|
/* check if the string offsets really fit into the provided size */
|
|
/* FIXME: should we check string length as well? */
|
|
/* make sure that we don't read/write beyond the provided buffer */
|
|
if (lpOTM->otmSize >= FIELD_OFFSET(OUTLINETEXTMETRICA, otmpFamilyName) + sizeof(LPSTR))
|
|
{
|
|
if ((UINT_PTR)lpOTM->otmpFamilyName >= lpOTM->otmSize)
|
|
lpOTM->otmpFamilyName = 0; /* doesn't fit */
|
|
}
|
|
|
|
/* make sure that we don't read/write beyond the provided buffer */
|
|
if (lpOTM->otmSize >= FIELD_OFFSET(OUTLINETEXTMETRICA, otmpFaceName) + sizeof(LPSTR))
|
|
{
|
|
if ((UINT_PTR)lpOTM->otmpFaceName >= lpOTM->otmSize)
|
|
lpOTM->otmpFaceName = 0; /* doesn't fit */
|
|
}
|
|
|
|
/* make sure that we don't read/write beyond the provided buffer */
|
|
if (lpOTM->otmSize >= FIELD_OFFSET(OUTLINETEXTMETRICA, otmpStyleName) + sizeof(LPSTR))
|
|
{
|
|
if ((UINT_PTR)lpOTM->otmpStyleName >= lpOTM->otmSize)
|
|
lpOTM->otmpStyleName = 0; /* doesn't fit */
|
|
}
|
|
|
|
/* make sure that we don't read/write beyond the provided buffer */
|
|
if (lpOTM->otmSize >= FIELD_OFFSET(OUTLINETEXTMETRICA, otmpFullName) + sizeof(LPSTR))
|
|
{
|
|
if ((UINT_PTR)lpOTM->otmpFullName >= lpOTM->otmSize)
|
|
lpOTM->otmpFullName = 0; /* doesn't fit */
|
|
}
|
|
}
|
|
|
|
end:
|
|
if(lpOTMW != (OUTLINETEXTMETRICW *)buf)
|
|
HeapFree(GetProcessHeap(), 0, lpOTMW);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* GetOutlineTextMetricsW [GDI32.@]
|
|
*/
|
|
UINT WINAPI GetOutlineTextMetricsW(
|
|
HDC hdc, /* [in] Handle of device context */
|
|
UINT cbData, /* [in] Size of metric data array */
|
|
LPOUTLINETEXTMETRICW lpOTM) /* [out] Address of metric data array */
|
|
{
|
|
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;
|
|
}
|
|
|
|
static LPSTR FONT_GetCharsByRangeA(HDC hdc, UINT firstChar, UINT lastChar, PINT pByteLen)
|
|
{
|
|
INT i, count = lastChar - firstChar + 1;
|
|
UINT mbcp;
|
|
UINT c;
|
|
LPSTR str;
|
|
|
|
if (count <= 0)
|
|
return NULL;
|
|
|
|
mbcp = GdiGetCodePage(hdc);
|
|
switch (mbcp)
|
|
{
|
|
case 932:
|
|
case 936:
|
|
case 949:
|
|
case 950:
|
|
case 1361:
|
|
if (lastChar > 0xffff)
|
|
return NULL;
|
|
if ((firstChar ^ lastChar) > 0xff)
|
|
return NULL;
|
|
break;
|
|
default:
|
|
if (lastChar > 0xff)
|
|
return NULL;
|
|
mbcp = 0;
|
|
break;
|
|
}
|
|
|
|
str = HeapAlloc(GetProcessHeap(), 0, count * 2 + 1);
|
|
if (str == NULL)
|
|
return NULL;
|
|
|
|
for(i = 0, c = firstChar; c <= lastChar; i++, c++)
|
|
{
|
|
if (mbcp) {
|
|
if (c > 0xff)
|
|
str[i++] = (BYTE)(c >> 8);
|
|
if (c <= 0xff && IsDBCSLeadByteEx(mbcp, c))
|
|
str[i] = 0x1f; /* FIXME: use default character */
|
|
else
|
|
str[i] = (BYTE)c;
|
|
}
|
|
else
|
|
str[i] = (BYTE)c;
|
|
}
|
|
str[i] = '\0';
|
|
|
|
*pByteLen = i;
|
|
|
|
return str;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* GetCharWidthW (GDI32.@)
|
|
* GetCharWidth32W (GDI32.@)
|
|
*/
|
|
BOOL WINAPI GetCharWidth32W( HDC hdc, UINT firstChar, UINT lastChar,
|
|
LPINT buffer )
|
|
{
|
|
UINT i;
|
|
BOOL ret;
|
|
PHYSDEV dev;
|
|
DC * dc = get_dc_ptr( hdc );
|
|
|
|
if (!dc) return FALSE;
|
|
|
|
dev = GET_DC_PHYSDEV( dc, pGetCharWidth );
|
|
ret = dev->funcs->pGetCharWidth( dev, firstChar, lastChar, buffer );
|
|
|
|
if (ret)
|
|
{
|
|
/* convert device units to logical */
|
|
for( i = firstChar; i <= lastChar; i++, buffer++ )
|
|
*buffer = width_to_LP( dc, *buffer );
|
|
}
|
|
release_dc_ptr( dc );
|
|
return ret;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* GetCharWidthA (GDI32.@)
|
|
* GetCharWidth32A (GDI32.@)
|
|
*/
|
|
BOOL WINAPI GetCharWidth32A( HDC hdc, UINT firstChar, UINT lastChar,
|
|
LPINT buffer )
|
|
{
|
|
INT i, wlen;
|
|
LPSTR str;
|
|
LPWSTR wstr;
|
|
BOOL ret = TRUE;
|
|
|
|
str = FONT_GetCharsByRangeA(hdc, firstChar, lastChar, &i);
|
|
if(str == NULL)
|
|
return FALSE;
|
|
|
|
wstr = FONT_mbtowc(hdc, str, i, &wlen, NULL);
|
|
|
|
for(i = 0; i < wlen; i++)
|
|
{
|
|
if(!GetCharWidth32W(hdc, wstr[i], wstr[i], buffer))
|
|
{
|
|
ret = FALSE;
|
|
break;
|
|
}
|
|
buffer++;
|
|
}
|
|
|
|
HeapFree(GetProcessHeap(), 0, str);
|
|
HeapFree(GetProcessHeap(), 0, wstr);
|
|
|
|
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( GetNearestColor( dev->hdc, dc->backgroundColor ) );
|
|
|
|
if (brush)
|
|
{
|
|
orig = SelectObject( dev->hdc, brush );
|
|
dp_to_lp( dc, (POINT *)&rc, 2 );
|
|
PatBlt( dev->hdc, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, PATCOPY );
|
|
SelectObject( dev->hdc, orig );
|
|
DeleteObject( 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->textColor );
|
|
orig = SelectObject( 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;
|
|
}
|
|
}
|
|
|
|
SelectObject( dev->hdc, orig );
|
|
DeleteObject( pen );
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* ExtTextOutA (GDI32.@)
|
|
*
|
|
* See ExtTextOutW.
|
|
*/
|
|
BOOL WINAPI ExtTextOutA( HDC hdc, INT x, INT y, UINT flags,
|
|
const RECT *lprect, LPCSTR str, UINT count, const INT *lpDx )
|
|
{
|
|
INT wlen;
|
|
UINT codepage;
|
|
LPWSTR p;
|
|
BOOL ret;
|
|
LPINT lpDxW = NULL;
|
|
|
|
if (flags & ETO_GLYPH_INDEX)
|
|
return ExtTextOutW( hdc, x, y, flags, lprect, (LPCWSTR)str, count, lpDx );
|
|
|
|
p = FONT_mbtowc(hdc, str, count, &wlen, &codepage);
|
|
|
|
if (lpDx) {
|
|
unsigned int i = 0, j = 0;
|
|
|
|
/* allocate enough for a ETO_PDY */
|
|
lpDxW = HeapAlloc( GetProcessHeap(), 0, 2*wlen*sizeof(INT));
|
|
while(i < count) {
|
|
if(IsDBCSLeadByteEx(codepage, str[i]))
|
|
{
|
|
if(flags & ETO_PDY)
|
|
{
|
|
lpDxW[j++] = lpDx[i * 2] + lpDx[(i + 1) * 2];
|
|
lpDxW[j++] = lpDx[i * 2 + 1] + lpDx[(i + 1) * 2 + 1];
|
|
}
|
|
else
|
|
lpDxW[j++] = lpDx[i] + lpDx[i + 1];
|
|
i = i + 2;
|
|
}
|
|
else
|
|
{
|
|
if(flags & ETO_PDY)
|
|
{
|
|
lpDxW[j++] = lpDx[i * 2];
|
|
lpDxW[j++] = lpDx[i * 2 + 1];
|
|
}
|
|
else
|
|
lpDxW[j++] = lpDx[i];
|
|
i = i + 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
ret = ExtTextOutW( hdc, x, y, flags, lprect, p, wlen, lpDxW );
|
|
|
|
HeapFree( GetProcessHeap(), 0, p );
|
|
HeapFree( GetProcessHeap(), 0, lpDxW );
|
|
return ret;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* 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;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* ExtTextOutW (GDI32.@)
|
|
*
|
|
* 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 ExtTextOutW( HDC hdc, INT x, INT y, UINT flags,
|
|
const RECT *lprect, LPCWSTR str, UINT count, const INT *lpDx )
|
|
{
|
|
BOOL ret = FALSE;
|
|
LPWSTR reordered_str = (LPWSTR)str;
|
|
WORD *glyphs = NULL;
|
|
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;
|
|
|
|
align = dc->textAlign;
|
|
breakRem = dc->breakRem;
|
|
layout = dc->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;
|
|
}
|
|
|
|
if( !(flags & (ETO_GLYPH_INDEX | ETO_IGNORELANGUAGE)) && count > 0 )
|
|
{
|
|
INT cGlyphs;
|
|
reordered_str = HeapAlloc(GetProcessHeap(), 0, count*sizeof(WCHAR));
|
|
|
|
BIDI_Reorder( hdc, str, count, GCP_REORDER,
|
|
(align & TA_RTLREADING) ? WINE_GCPW_FORCE_RTL : WINE_GCPW_FORCE_LTR,
|
|
reordered_str, count, NULL, &glyphs, &cGlyphs);
|
|
|
|
flags |= ETO_IGNORELANGUAGE;
|
|
if (glyphs)
|
|
{
|
|
flags |= ETO_GLYPH_INDEX;
|
|
if (cGlyphs != count)
|
|
count = cGlyphs;
|
|
}
|
|
}
|
|
else if(flags & ETO_GLYPH_INDEX)
|
|
glyphs = reordered_str;
|
|
|
|
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->backgroundMode, dc->MapMode);
|
|
|
|
if(align & TA_UPDATECP)
|
|
{
|
|
pt = dc->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->GraphicsMode == 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) );
|
|
|
|
if (flags & ETO_GLYPH_INDEX)
|
|
GetTextExtentExPointI( hdc, glyphs, count, -1, NULL, dx, &sz );
|
|
else
|
|
GetTextExtentExPointW( hdc, reordered_str, count, -1, NULL, dx, &sz );
|
|
|
|
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->GraphicsMode == 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, glyphs, count, &sz);
|
|
else
|
|
GetTextExtentPointW(hdc, reordered_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->GraphicsMode == 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->backgroundMode != 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,
|
|
glyphs ? glyphs : reordered_str, count, (INT*)deltas );
|
|
|
|
done:
|
|
HeapFree(GetProcessHeap(), 0, deltas);
|
|
if(glyphs != reordered_str)
|
|
HeapFree(GetProcessHeap(), 0, glyphs);
|
|
if(reordered_str != str)
|
|
HeapFree(GetProcessHeap(), 0, reordered_str);
|
|
|
|
if (ret && (lf.lfUnderline || lf.lfStrikeOut))
|
|
{
|
|
int underlinePos, strikeoutPos;
|
|
int underlineWidth, strikeoutWidth;
|
|
UINT size = GetOutlineTextMetricsW(hdc, 0, NULL);
|
|
OUTLINETEXTMETRICW* otm = NULL;
|
|
POINT pts[5];
|
|
HPEN hpen = SelectObject(hdc, GetStockObject(NULL_PEN));
|
|
HBRUSH hbrush = CreateSolidBrush(dc->textColor);
|
|
|
|
hbrush = SelectObject(hdc, hbrush);
|
|
|
|
if(!size)
|
|
{
|
|
underlinePos = 0;
|
|
underlineWidth = tm.tmAscent / 20 + 1;
|
|
strikeoutPos = tm.tmAscent / 2;
|
|
strikeoutWidth = underlineWidth;
|
|
}
|
|
else
|
|
{
|
|
otm = HeapAlloc(GetProcessHeap(), 0, size);
|
|
GetOutlineTextMetricsW(hdc, size, otm);
|
|
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)
|
|
{
|
|
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);
|
|
Polygon(hdc, pts, 5);
|
|
}
|
|
|
|
if (lf.lfStrikeOut)
|
|
{
|
|
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);
|
|
Polygon(hdc, pts, 5);
|
|
}
|
|
|
|
SelectObject(hdc, hpen);
|
|
hbrush = SelectObject(hdc, hbrush);
|
|
DeleteObject(hbrush);
|
|
}
|
|
|
|
release_dc_ptr( dc );
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* TextOutA (GDI32.@)
|
|
*/
|
|
BOOL WINAPI TextOutA( HDC hdc, INT x, INT y, LPCSTR str, INT count )
|
|
{
|
|
return ExtTextOutA( hdc, x, y, 0, NULL, str, count, NULL );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* TextOutW (GDI32.@)
|
|
*/
|
|
BOOL WINAPI TextOutW(HDC hdc, INT x, INT y, LPCWSTR str, INT count)
|
|
{
|
|
return ExtTextOutW( hdc, x, y, 0, NULL, str, count, NULL );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* PolyTextOutA (GDI32.@)
|
|
*
|
|
* See PolyTextOutW.
|
|
*/
|
|
BOOL WINAPI PolyTextOutA( HDC hdc, const POLYTEXTA *pptxt, INT cStrings )
|
|
{
|
|
for (; cStrings>0; cStrings--, pptxt++)
|
|
if (!ExtTextOutA( hdc, pptxt->x, pptxt->y, pptxt->uiFlags, &pptxt->rcl, pptxt->lpstr, pptxt->n, pptxt->pdx ))
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
* PolyTextOutW (GDI32.@)
|
|
*
|
|
* Draw several Strings
|
|
*
|
|
* RETURNS
|
|
* TRUE: Success.
|
|
* FALSE: Failure.
|
|
*/
|
|
BOOL WINAPI PolyTextOutW( HDC hdc, const POLYTEXTW *pptxt, INT cStrings )
|
|
{
|
|
for (; cStrings>0; cStrings--, pptxt++)
|
|
if (!ExtTextOutW( hdc, pptxt->x, pptxt->y, pptxt->uiFlags, &pptxt->rcl, pptxt->lpstr, pptxt->n, pptxt->pdx ))
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* SetMapperFlags (GDI32.@)
|
|
*/
|
|
DWORD WINAPI SetMapperFlags( HDC hdc, DWORD flags )
|
|
{
|
|
DC *dc = get_dc_ptr( hdc );
|
|
DWORD ret = GDI_ERROR;
|
|
|
|
if (dc)
|
|
{
|
|
PHYSDEV physdev = GET_DC_PHYSDEV( dc, pSetMapperFlags );
|
|
flags = physdev->funcs->pSetMapperFlags( physdev, flags );
|
|
if (flags != GDI_ERROR)
|
|
{
|
|
ret = dc->mapperFlags;
|
|
dc->mapperFlags = flags;
|
|
}
|
|
release_dc_ptr( dc );
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* GetAspectRatioFilterEx (GDI32.@)
|
|
*/
|
|
BOOL WINAPI GetAspectRatioFilterEx( HDC hdc, LPSIZE pAspectRatio )
|
|
{
|
|
FIXME("(%p, %p): -- Empty Stub !\n", hdc, pAspectRatio);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* GetCharABCWidthsA (GDI32.@)
|
|
*
|
|
* See GetCharABCWidthsW.
|
|
*/
|
|
BOOL WINAPI GetCharABCWidthsA(HDC hdc, UINT firstChar, UINT lastChar,
|
|
LPABC abc )
|
|
{
|
|
INT i, wlen;
|
|
LPSTR str;
|
|
LPWSTR wstr;
|
|
BOOL ret = TRUE;
|
|
|
|
str = FONT_GetCharsByRangeA(hdc, firstChar, lastChar, &i);
|
|
if (str == NULL)
|
|
return FALSE;
|
|
|
|
wstr = FONT_mbtowc(hdc, str, i, &wlen, NULL);
|
|
if (wstr == NULL)
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, str);
|
|
return FALSE;
|
|
}
|
|
|
|
for(i = 0; i < wlen; i++)
|
|
{
|
|
if(!GetCharABCWidthsW(hdc, wstr[i], wstr[i], abc))
|
|
{
|
|
ret = FALSE;
|
|
break;
|
|
}
|
|
abc++;
|
|
}
|
|
|
|
HeapFree(GetProcessHeap(), 0, str);
|
|
HeapFree(GetProcessHeap(), 0, wstr);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
* GetCharABCWidthsW [GDI32.@]
|
|
*
|
|
* 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
|
|
*
|
|
* RETURNS
|
|
* Success: TRUE
|
|
* Failure: FALSE
|
|
*/
|
|
BOOL WINAPI GetCharABCWidthsW( HDC hdc, UINT firstChar, UINT lastChar,
|
|
LPABC abc )
|
|
{
|
|
DC *dc = get_dc_ptr(hdc);
|
|
PHYSDEV dev;
|
|
unsigned int i;
|
|
BOOL ret;
|
|
TEXTMETRICW tm;
|
|
|
|
if (!dc) return FALSE;
|
|
|
|
if (!abc)
|
|
{
|
|
release_dc_ptr( dc );
|
|
return FALSE;
|
|
}
|
|
|
|
/* unlike GetCharABCWidthsFloatW, 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;
|
|
}
|
|
|
|
dev = GET_DC_PHYSDEV( dc, pGetCharABCWidths );
|
|
ret = dev->funcs->pGetCharABCWidths( dev, firstChar, lastChar, abc );
|
|
if (ret)
|
|
{
|
|
/* convert device units to logical */
|
|
for( i = firstChar; i <= lastChar; i++, abc++ ) {
|
|
abc->abcA = width_to_LP(dc, abc->abcA);
|
|
abc->abcB = width_to_LP(dc, abc->abcB);
|
|
abc->abcC = width_to_LP(dc, abc->abcC);
|
|
}
|
|
}
|
|
|
|
release_dc_ptr( dc );
|
|
return ret;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
* GetCharABCWidthsI [GDI32.@]
|
|
*
|
|
* Retrieves widths of characters in range.
|
|
*
|
|
* PARAMS
|
|
* hdc [I] Handle of device context
|
|
* firstChar [I] First glyphs in range to query
|
|
* count [I] Last glyphs in range to query
|
|
* pgi [i] Array of glyphs to query
|
|
* abc [O] Address of character-width structure
|
|
*
|
|
* NOTES
|
|
* Only works with TrueType fonts
|
|
*
|
|
* RETURNS
|
|
* Success: TRUE
|
|
* Failure: FALSE
|
|
*/
|
|
BOOL WINAPI GetCharABCWidthsI( HDC hdc, UINT firstChar, UINT count,
|
|
LPWORD pgi, LPABC abc)
|
|
{
|
|
DC *dc = get_dc_ptr(hdc);
|
|
PHYSDEV dev;
|
|
unsigned int i;
|
|
BOOL ret;
|
|
|
|
if (!dc) return FALSE;
|
|
|
|
if (!abc)
|
|
{
|
|
release_dc_ptr( dc );
|
|
return FALSE;
|
|
}
|
|
|
|
dev = GET_DC_PHYSDEV( dc, pGetCharABCWidthsI );
|
|
ret = dev->funcs->pGetCharABCWidthsI( dev, firstChar, count, pgi, abc );
|
|
if (ret)
|
|
{
|
|
/* convert device units to logical */
|
|
for( i = 0; i < count; i++, abc++ ) {
|
|
abc->abcA = width_to_LP(dc, abc->abcA);
|
|
abc->abcB = width_to_LP(dc, abc->abcB);
|
|
abc->abcC = width_to_LP(dc, abc->abcC);
|
|
}
|
|
}
|
|
|
|
release_dc_ptr( dc );
|
|
return ret;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* GetGlyphOutlineA (GDI32.@)
|
|
*/
|
|
DWORD WINAPI GetGlyphOutlineA( HDC hdc, UINT uChar, UINT fuFormat,
|
|
LPGLYPHMETRICS lpgm, DWORD cbBuffer,
|
|
LPVOID lpBuffer, const MAT2 *lpmat2 )
|
|
{
|
|
if (!lpmat2) return GDI_ERROR;
|
|
|
|
if(!(fuFormat & GGO_GLYPH_INDEX)) {
|
|
UINT cp;
|
|
int len;
|
|
char mbchs[2];
|
|
WCHAR wChar;
|
|
|
|
cp = GdiGetCodePage(hdc);
|
|
if (IsDBCSLeadByteEx(cp, uChar >> 8)) {
|
|
len = 2;
|
|
mbchs[0] = (uChar & 0xff00) >> 8;
|
|
mbchs[1] = (uChar & 0xff);
|
|
} else {
|
|
len = 1;
|
|
mbchs[0] = (uChar & 0xff);
|
|
}
|
|
wChar = 0;
|
|
MultiByteToWideChar(cp, 0, mbchs, len, &wChar, 1);
|
|
uChar = wChar;
|
|
}
|
|
|
|
return GetGlyphOutlineW(hdc, uChar, fuFormat, lpgm, cbBuffer, lpBuffer,
|
|
lpmat2);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* GetGlyphOutlineW (GDI32.@)
|
|
*/
|
|
DWORD WINAPI GetGlyphOutlineW( HDC hdc, UINT uChar, UINT fuFormat,
|
|
LPGLYPHMETRICS lpgm, DWORD cbBuffer,
|
|
LPVOID lpBuffer, const MAT2 *lpmat2 )
|
|
{
|
|
DC *dc;
|
|
DWORD ret;
|
|
PHYSDEV dev;
|
|
|
|
TRACE("(%p, %04x, %04x, %p, %d, %p, %p)\n",
|
|
hdc, uChar, fuFormat, lpgm, cbBuffer, lpBuffer, lpmat2 );
|
|
|
|
if (!lpmat2) return GDI_ERROR;
|
|
|
|
dc = get_dc_ptr(hdc);
|
|
if(!dc) return GDI_ERROR;
|
|
|
|
uChar &= 0xffff;
|
|
|
|
dev = GET_DC_PHYSDEV( dc, pGetGlyphOutline );
|
|
ret = dev->funcs->pGetGlyphOutline( dev, uChar, fuFormat, lpgm, cbBuffer, lpBuffer, lpmat2 );
|
|
release_dc_ptr( dc );
|
|
return ret;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* CreateScalableFontResourceA (GDI32.@)
|
|
*/
|
|
BOOL WINAPI CreateScalableFontResourceA( DWORD fHidden,
|
|
LPCSTR lpszResourceFile,
|
|
LPCSTR lpszFontFile,
|
|
LPCSTR lpszCurrentPath )
|
|
{
|
|
LPWSTR lpszResourceFileW = NULL;
|
|
LPWSTR lpszFontFileW = NULL;
|
|
LPWSTR lpszCurrentPathW = NULL;
|
|
int len;
|
|
BOOL ret;
|
|
|
|
if (lpszResourceFile)
|
|
{
|
|
len = MultiByteToWideChar(CP_ACP, 0, lpszResourceFile, -1, NULL, 0);
|
|
lpszResourceFileW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
|
|
MultiByteToWideChar(CP_ACP, 0, lpszResourceFile, -1, lpszResourceFileW, len);
|
|
}
|
|
|
|
if (lpszFontFile)
|
|
{
|
|
len = MultiByteToWideChar(CP_ACP, 0, lpszFontFile, -1, NULL, 0);
|
|
lpszFontFileW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
|
|
MultiByteToWideChar(CP_ACP, 0, lpszFontFile, -1, lpszFontFileW, len);
|
|
}
|
|
|
|
if (lpszCurrentPath)
|
|
{
|
|
len = MultiByteToWideChar(CP_ACP, 0, lpszCurrentPath, -1, NULL, 0);
|
|
lpszCurrentPathW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
|
|
MultiByteToWideChar(CP_ACP, 0, lpszCurrentPath, -1, lpszCurrentPathW, len);
|
|
}
|
|
|
|
ret = CreateScalableFontResourceW(fHidden, lpszResourceFileW,
|
|
lpszFontFileW, lpszCurrentPathW);
|
|
|
|
HeapFree(GetProcessHeap(), 0, lpszResourceFileW);
|
|
HeapFree(GetProcessHeap(), 0, lpszFontFileW);
|
|
HeapFree(GetProcessHeap(), 0, lpszCurrentPathW);
|
|
|
|
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 )
|
|
{
|
|
static const WCHAR backslashW[] = {'\\',0};
|
|
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 = strlenW( font_path ) + strlenW( font_file ) + 2;
|
|
if (len > MAX_PATH) goto done;
|
|
lstrcpynW( path, font_path, MAX_PATH );
|
|
strcatW( path, backslashW );
|
|
strcatW( 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 );
|
|
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* GetKerningPairsA (GDI32.@)
|
|
*/
|
|
DWORD WINAPI GetKerningPairsA( HDC hDC, DWORD cPairs,
|
|
LPKERNINGPAIR kern_pairA )
|
|
{
|
|
UINT cp;
|
|
CPINFO cpi;
|
|
DWORD i, total_kern_pairs, kern_pairs_copied = 0;
|
|
KERNINGPAIR *kern_pairW;
|
|
|
|
if (!cPairs && kern_pairA)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return 0;
|
|
}
|
|
|
|
cp = GdiGetCodePage(hDC);
|
|
|
|
/* GetCPInfo() will fail on CP_SYMBOL, and WideCharToMultiByte is supposed
|
|
* to fail on an invalid character for CP_SYMBOL.
|
|
*/
|
|
cpi.DefaultChar[0] = 0;
|
|
if (cp != CP_SYMBOL && !GetCPInfo(cp, &cpi))
|
|
{
|
|
FIXME("Can't find codepage %u info\n", cp);
|
|
return 0;
|
|
}
|
|
|
|
total_kern_pairs = GetKerningPairsW(hDC, 0, NULL);
|
|
if (!total_kern_pairs) return 0;
|
|
|
|
kern_pairW = HeapAlloc(GetProcessHeap(), 0, total_kern_pairs * sizeof(*kern_pairW));
|
|
GetKerningPairsW(hDC, total_kern_pairs, kern_pairW);
|
|
|
|
for (i = 0; i < total_kern_pairs; i++)
|
|
{
|
|
char first, second;
|
|
|
|
if (!WideCharToMultiByte(cp, 0, &kern_pairW[i].wFirst, 1, &first, 1, NULL, NULL))
|
|
continue;
|
|
|
|
if (!WideCharToMultiByte(cp, 0, &kern_pairW[i].wSecond, 1, &second, 1, NULL, NULL))
|
|
continue;
|
|
|
|
if (first == cpi.DefaultChar[0] || second == cpi.DefaultChar[0])
|
|
continue;
|
|
|
|
if (kern_pairA)
|
|
{
|
|
if (kern_pairs_copied >= cPairs) break;
|
|
|
|
kern_pairA->wFirst = (BYTE)first;
|
|
kern_pairA->wSecond = (BYTE)second;
|
|
kern_pairA->iKernAmount = kern_pairW[i].iKernAmount;
|
|
kern_pairA++;
|
|
}
|
|
kern_pairs_copied++;
|
|
}
|
|
|
|
HeapFree(GetProcessHeap(), 0, kern_pairW);
|
|
|
|
return kern_pairs_copied;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* GetKerningPairsW (GDI32.@)
|
|
*/
|
|
DWORD WINAPI GetKerningPairsW( HDC hDC, DWORD cPairs,
|
|
LPKERNINGPAIR lpKerningPairs )
|
|
{
|
|
DC *dc;
|
|
DWORD ret;
|
|
PHYSDEV dev;
|
|
|
|
TRACE("(%p,%d,%p)\n", hDC, cPairs, lpKerningPairs);
|
|
|
|
if (!cPairs && lpKerningPairs)
|
|
{
|
|
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, cPairs, lpKerningPairs );
|
|
release_dc_ptr( dc );
|
|
return ret;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* TranslateCharsetInfo [GDI32.@]
|
|
*
|
|
* Fills a CHARSETINFO structure for a character set, code page, or
|
|
* font. This allows making the correspondence between different labels
|
|
* (character set, Windows, ANSI, and OEM codepages, and Unicode ranges)
|
|
* of the same encoding.
|
|
*
|
|
* Only one codepage will be set in lpCs->fs. If TCI_SRCFONTSIG is used,
|
|
* only one codepage should be set in *lpSrc.
|
|
*
|
|
* RETURNS
|
|
* TRUE on success, FALSE on failure.
|
|
*
|
|
*/
|
|
BOOL WINAPI TranslateCharsetInfo(
|
|
LPDWORD lpSrc, /* [in]
|
|
if flags == TCI_SRCFONTSIG: pointer to fsCsb of a FONTSIGNATURE
|
|
if flags == TCI_SRCCHARSET: a character set value
|
|
if flags == TCI_SRCCODEPAGE: a code page value
|
|
*/
|
|
LPCHARSETINFO lpCs, /* [out] structure to receive charset information */
|
|
DWORD flags /* [in] determines interpretation of lpSrc */)
|
|
{
|
|
int index = 0;
|
|
switch (flags) {
|
|
case TCI_SRCFONTSIG:
|
|
while (index < MAXTCIINDEX && !(*lpSrc>>index & 0x0001)) index++;
|
|
break;
|
|
case TCI_SRCCODEPAGE:
|
|
while (index < MAXTCIINDEX && PtrToUlong(lpSrc) != FONT_tci[index].ciACP) index++;
|
|
break;
|
|
case TCI_SRCCHARSET:
|
|
while (index < MAXTCIINDEX && PtrToUlong(lpSrc) != FONT_tci[index].ciCharset) index++;
|
|
break;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
if (index >= MAXTCIINDEX || FONT_tci[index].ciCharset == DEFAULT_CHARSET) return FALSE;
|
|
*lpCs = FONT_tci[index];
|
|
return TRUE;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* GetFontLanguageInfo (GDI32.@)
|
|
*/
|
|
DWORD WINAPI GetFontLanguageInfo(HDC hdc)
|
|
{
|
|
FONTSIGNATURE fontsig;
|
|
static const DWORD GCP_DBCS_MASK=FS_JISJAPAN|FS_CHINESESIMP|FS_WANSUNG|FS_CHINESETRAD|FS_JOHAB,
|
|
GCP_DIACRITIC_MASK=0x00000000,
|
|
FLI_GLYPHS_MASK=0x00000000,
|
|
GCP_GLYPHSHAPE_MASK=FS_ARABIC,
|
|
GCP_KASHIDA_MASK=0x00000000,
|
|
GCP_LIGATE_MASK=0x00000000,
|
|
GCP_REORDER_MASK=FS_HEBREW|FS_ARABIC;
|
|
|
|
DWORD result=0;
|
|
|
|
GetTextCharsetInfo( hdc, &fontsig, 0 );
|
|
/* We detect each flag we return using a bitmask on the Codepage Bitfields */
|
|
|
|
if( (fontsig.fsCsb[0]&GCP_DBCS_MASK)!=0 )
|
|
result|=GCP_DBCS;
|
|
|
|
if( (fontsig.fsCsb[0]&GCP_DIACRITIC_MASK)!=0 )
|
|
result|=GCP_DIACRITIC;
|
|
|
|
if( (fontsig.fsCsb[0]&FLI_GLYPHS_MASK)!=0 )
|
|
result|=FLI_GLYPHS;
|
|
|
|
if( (fontsig.fsCsb[0]&GCP_GLYPHSHAPE_MASK)!=0 )
|
|
result|=GCP_GLYPHSHAPE;
|
|
|
|
if( (fontsig.fsCsb[0]&GCP_KASHIDA_MASK)!=0 )
|
|
result|=GCP_KASHIDA;
|
|
|
|
if( (fontsig.fsCsb[0]&GCP_LIGATE_MASK)!=0 )
|
|
result|=GCP_LIGATE;
|
|
|
|
if( GetKerningPairsW( hdc, 0, NULL ) )
|
|
result|=GCP_USEKERNING;
|
|
|
|
/* this might need a test for a HEBREW- or ARABIC_CHARSET as well */
|
|
if( GetTextAlign( hdc) & TA_RTLREADING )
|
|
if( (fontsig.fsCsb[0]&GCP_REORDER_MASK)!=0 )
|
|
result|=GCP_REORDER;
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
* GetFontData [GDI32.@]
|
|
*
|
|
* Retrieve data for TrueType font.
|
|
*
|
|
* RETURNS
|
|
*
|
|
* success: Number of bytes returned
|
|
* failure: GDI_ERROR
|
|
*
|
|
* NOTES
|
|
*
|
|
* Calls SetLastError()
|
|
*
|
|
*/
|
|
DWORD WINAPI GetFontData(HDC hdc, DWORD table, DWORD offset,
|
|
LPVOID 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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* GetGlyphIndicesA [GDI32.@]
|
|
*/
|
|
DWORD WINAPI GetGlyphIndicesA(HDC hdc, LPCSTR lpstr, INT count,
|
|
LPWORD pgi, DWORD flags)
|
|
{
|
|
DWORD ret;
|
|
WCHAR *lpstrW;
|
|
INT countW;
|
|
|
|
TRACE("(%p, %s, %d, %p, 0x%x)\n",
|
|
hdc, debugstr_an(lpstr, count), count, pgi, flags);
|
|
|
|
lpstrW = FONT_mbtowc(hdc, lpstr, count, &countW, NULL);
|
|
ret = GetGlyphIndicesW(hdc, lpstrW, countW, pgi, flags);
|
|
HeapFree(GetProcessHeap(), 0, lpstrW);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* GetGlyphIndicesW [GDI32.@]
|
|
*/
|
|
DWORD WINAPI GetGlyphIndicesW(HDC hdc, LPCWSTR lpstr, INT count,
|
|
LPWORD pgi, DWORD flags)
|
|
{
|
|
DC *dc = get_dc_ptr(hdc);
|
|
PHYSDEV dev;
|
|
DWORD ret;
|
|
|
|
TRACE("(%p, %s, %d, %p, 0x%x)\n",
|
|
hdc, debugstr_wn(lpstr, count), count, pgi, flags);
|
|
|
|
if(!dc) return GDI_ERROR;
|
|
|
|
dev = GET_DC_PHYSDEV( dc, pGetGlyphIndices );
|
|
ret = dev->funcs->pGetGlyphIndices( dev, lpstr, count, pgi, flags );
|
|
release_dc_ptr( dc );
|
|
return ret;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* GetCharacterPlacementA [GDI32.@]
|
|
*
|
|
* See GetCharacterPlacementW.
|
|
*
|
|
* NOTES:
|
|
* the web browser control of ie4 calls this with dwFlags=0
|
|
*/
|
|
DWORD WINAPI
|
|
GetCharacterPlacementA(HDC hdc, LPCSTR lpString, INT uCount,
|
|
INT nMaxExtent, GCP_RESULTSA *lpResults,
|
|
DWORD dwFlags)
|
|
{
|
|
WCHAR *lpStringW;
|
|
INT uCountW;
|
|
GCP_RESULTSW resultsW;
|
|
DWORD ret;
|
|
UINT font_cp;
|
|
|
|
TRACE("%s, %d, %d, 0x%08x\n",
|
|
debugstr_an(lpString, uCount), uCount, nMaxExtent, dwFlags);
|
|
|
|
lpStringW = FONT_mbtowc(hdc, lpString, uCount, &uCountW, &font_cp);
|
|
|
|
if (!lpResults)
|
|
{
|
|
ret = GetCharacterPlacementW(hdc, lpStringW, uCountW, nMaxExtent, NULL, dwFlags);
|
|
HeapFree(GetProcessHeap(), 0, lpStringW);
|
|
return ret;
|
|
}
|
|
|
|
/* both structs are equal in size */
|
|
memcpy(&resultsW, lpResults, sizeof(resultsW));
|
|
|
|
if(lpResults->lpOutString)
|
|
resultsW.lpOutString = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*uCountW);
|
|
|
|
ret = GetCharacterPlacementW(hdc, lpStringW, uCountW, nMaxExtent, &resultsW, dwFlags);
|
|
|
|
lpResults->nGlyphs = resultsW.nGlyphs;
|
|
lpResults->nMaxFit = resultsW.nMaxFit;
|
|
|
|
if(lpResults->lpOutString) {
|
|
WideCharToMultiByte(font_cp, 0, resultsW.lpOutString, uCountW,
|
|
lpResults->lpOutString, uCount, NULL, NULL );
|
|
}
|
|
|
|
HeapFree(GetProcessHeap(), 0, lpStringW);
|
|
HeapFree(GetProcessHeap(), 0, resultsW.lpOutString);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int kern_pair(const KERNINGPAIR *kern, int count, WCHAR c1, WCHAR c2)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
if (kern[i].wFirst == c1 && kern[i].wSecond == c2)
|
|
return kern[i].iKernAmount;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int *kern_string(HDC hdc, const WCHAR *str, int len, int *kern_total)
|
|
{
|
|
int i, count;
|
|
KERNINGPAIR *kern = NULL;
|
|
int *ret;
|
|
|
|
*kern_total = 0;
|
|
|
|
ret = heap_alloc(len * sizeof(*ret));
|
|
if (!ret) return NULL;
|
|
|
|
count = GetKerningPairsW(hdc, 0, NULL);
|
|
if (count)
|
|
{
|
|
kern = heap_alloc(count * sizeof(*kern));
|
|
if (!kern)
|
|
{
|
|
heap_free(ret);
|
|
return NULL;
|
|
}
|
|
|
|
GetKerningPairsW(hdc, count, kern);
|
|
}
|
|
|
|
for (i = 0; i < len - 1; i++)
|
|
{
|
|
ret[i] = kern_pair(kern, count, str[i], str[i + 1]);
|
|
*kern_total += ret[i];
|
|
}
|
|
|
|
ret[len - 1] = 0; /* no kerning for last element */
|
|
|
|
heap_free(kern);
|
|
return ret;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* GetCharacterPlacementW [GDI32.@]
|
|
*
|
|
* Retrieve information about a string. This includes the width, reordering,
|
|
* Glyphing and so on.
|
|
*
|
|
* RETURNS
|
|
*
|
|
* The width and height of the string if successful, 0 if failed.
|
|
*
|
|
* BUGS
|
|
*
|
|
* All flags except GCP_REORDER are not yet implemented.
|
|
* Reordering is not 100% compliant to the Windows BiDi method.
|
|
* Caret positioning is not yet implemented for BiDi.
|
|
* Classes are not yet implemented.
|
|
*
|
|
*/
|
|
DWORD WINAPI
|
|
GetCharacterPlacementW(
|
|
HDC hdc, /* [in] Device context for which the rendering is to be done */
|
|
LPCWSTR lpString, /* [in] The string for which information is to be returned */
|
|
INT uCount, /* [in] Number of WORDS in string. */
|
|
INT nMaxExtent, /* [in] Maximum extent the string is to take (in HDC logical units) */
|
|
GCP_RESULTSW *lpResults, /* [in/out] A pointer to a GCP_RESULTSW struct */
|
|
DWORD dwFlags /* [in] Flags specifying how to process the string */
|
|
)
|
|
{
|
|
DWORD ret=0;
|
|
SIZE size;
|
|
UINT i, nSet;
|
|
int *kern = NULL, kern_total = 0;
|
|
|
|
TRACE("%s, %d, %d, 0x%08x\n",
|
|
debugstr_wn(lpString, uCount), uCount, nMaxExtent, dwFlags);
|
|
|
|
if (!uCount)
|
|
return 0;
|
|
|
|
if (!lpResults)
|
|
return GetTextExtentPoint32W(hdc, lpString, uCount, &size) ? MAKELONG(size.cx, size.cy) : 0;
|
|
|
|
TRACE("lStructSize=%d, lpOutString=%p, lpOrder=%p, lpDx=%p, lpCaretPos=%p\n"
|
|
"lpClass=%p, lpGlyphs=%p, nGlyphs=%u, nMaxFit=%d\n",
|
|
lpResults->lStructSize, lpResults->lpOutString, lpResults->lpOrder,
|
|
lpResults->lpDx, lpResults->lpCaretPos, lpResults->lpClass,
|
|
lpResults->lpGlyphs, lpResults->nGlyphs, lpResults->nMaxFit);
|
|
|
|
if (dwFlags & ~(GCP_REORDER | GCP_USEKERNING))
|
|
FIXME("flags 0x%08x ignored\n", dwFlags);
|
|
if (lpResults->lpClass)
|
|
FIXME("classes not implemented\n");
|
|
if (lpResults->lpCaretPos && (dwFlags & GCP_REORDER))
|
|
FIXME("Caret positions for complex scripts not implemented\n");
|
|
|
|
nSet = (UINT)uCount;
|
|
if (nSet > lpResults->nGlyphs)
|
|
nSet = lpResults->nGlyphs;
|
|
|
|
/* return number of initialized fields */
|
|
lpResults->nGlyphs = nSet;
|
|
|
|
if (!(dwFlags & GCP_REORDER))
|
|
{
|
|
/* Treat the case where no special handling was requested in a fastpath way */
|
|
/* copy will do if the GCP_REORDER flag is not set */
|
|
if (lpResults->lpOutString)
|
|
memcpy( lpResults->lpOutString, lpString, nSet * sizeof(WCHAR));
|
|
|
|
if (lpResults->lpOrder)
|
|
{
|
|
for (i = 0; i < nSet; i++)
|
|
lpResults->lpOrder[i] = i;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BIDI_Reorder(NULL, lpString, uCount, dwFlags, WINE_GCPW_FORCE_LTR, lpResults->lpOutString,
|
|
nSet, lpResults->lpOrder, NULL, NULL );
|
|
}
|
|
|
|
if (dwFlags & GCP_USEKERNING)
|
|
{
|
|
kern = kern_string(hdc, lpString, nSet, &kern_total);
|
|
if (!kern)
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* FIXME: Will use the placement chars */
|
|
if (lpResults->lpDx)
|
|
{
|
|
int c;
|
|
for (i = 0; i < nSet; i++)
|
|
{
|
|
if (GetCharWidth32W(hdc, lpString[i], lpString[i], &c))
|
|
{
|
|
lpResults->lpDx[i] = c;
|
|
if (dwFlags & GCP_USEKERNING)
|
|
lpResults->lpDx[i] += kern[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
if (lpResults->lpCaretPos && !(dwFlags & GCP_REORDER))
|
|
{
|
|
int pos = 0;
|
|
|
|
lpResults->lpCaretPos[0] = 0;
|
|
for (i = 0; i < nSet - 1; i++)
|
|
{
|
|
if (dwFlags & GCP_USEKERNING)
|
|
pos += kern[i];
|
|
|
|
if (GetTextExtentPoint32W(hdc, &lpString[i], 1, &size))
|
|
lpResults->lpCaretPos[i + 1] = (pos += size.cx);
|
|
}
|
|
}
|
|
|
|
if (lpResults->lpGlyphs)
|
|
GetGlyphIndicesW(hdc, lpString, nSet, lpResults->lpGlyphs, 0);
|
|
|
|
if (GetTextExtentPoint32W(hdc, lpString, uCount, &size))
|
|
ret = MAKELONG(size.cx + kern_total, size.cy);
|
|
|
|
heap_free(kern);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* GetCharABCWidthsFloatA [GDI32.@]
|
|
*
|
|
* See GetCharABCWidthsFloatW.
|
|
*/
|
|
BOOL WINAPI GetCharABCWidthsFloatA( HDC hdc, UINT first, UINT last, LPABCFLOAT abcf )
|
|
{
|
|
INT i, wlen;
|
|
LPSTR str;
|
|
LPWSTR wstr;
|
|
BOOL ret = TRUE;
|
|
|
|
str = FONT_GetCharsByRangeA(hdc, first, last, &i);
|
|
if (str == NULL)
|
|
return FALSE;
|
|
|
|
wstr = FONT_mbtowc( hdc, str, i, &wlen, NULL );
|
|
|
|
for (i = 0; i < wlen; i++)
|
|
{
|
|
if (!GetCharABCWidthsFloatW( hdc, wstr[i], wstr[i], abcf ))
|
|
{
|
|
ret = FALSE;
|
|
break;
|
|
}
|
|
abcf++;
|
|
}
|
|
|
|
HeapFree( GetProcessHeap(), 0, str );
|
|
HeapFree( GetProcessHeap(), 0, wstr );
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* GetCharABCWidthsFloatW [GDI32.@]
|
|
*
|
|
* Retrieves widths of a range of characters.
|
|
*
|
|
* PARAMS
|
|
* hdc [I] Handle to device context.
|
|
* first [I] First character in range to query.
|
|
* last [I] Last character in range to query.
|
|
* abcf [O] Array of LPABCFLOAT structures.
|
|
*
|
|
* RETURNS
|
|
* Success: TRUE
|
|
* Failure: FALSE
|
|
*/
|
|
BOOL WINAPI GetCharABCWidthsFloatW( HDC hdc, UINT first, UINT last, LPABCFLOAT abcf )
|
|
{
|
|
UINT i;
|
|
ABC *abc;
|
|
PHYSDEV dev;
|
|
BOOL ret = FALSE;
|
|
DC *dc = get_dc_ptr( hdc );
|
|
|
|
TRACE("%p, %d, %d, %p\n", hdc, first, last, abcf);
|
|
|
|
if (!dc) return FALSE;
|
|
|
|
if (!abcf) goto done;
|
|
if (!(abc = HeapAlloc( GetProcessHeap(), 0, (last - first + 1) * sizeof(*abc) ))) goto done;
|
|
|
|
dev = GET_DC_PHYSDEV( dc, pGetCharABCWidths );
|
|
ret = dev->funcs->pGetCharABCWidths( dev, first, last, abc );
|
|
if (ret)
|
|
{
|
|
/* convert device units to logical */
|
|
FLOAT scale = fabs( dc->xformVport2World.eM11 );
|
|
for (i = first; i <= last; i++, abcf++)
|
|
{
|
|
abcf->abcfA = abc[i - first].abcA * scale;
|
|
abcf->abcfB = abc[i - first].abcB * scale;
|
|
abcf->abcfC = abc[i - first].abcC * scale;
|
|
}
|
|
}
|
|
HeapFree( GetProcessHeap(), 0, abc );
|
|
|
|
done:
|
|
release_dc_ptr( dc );
|
|
return ret;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* GetCharWidthFloatA [GDI32.@]
|
|
*/
|
|
BOOL WINAPI GetCharWidthFloatA( HDC hdc, UINT first, UINT last, float *buffer )
|
|
{
|
|
WCHAR *wstr;
|
|
int i, wlen;
|
|
char *str;
|
|
|
|
if (!(str = FONT_GetCharsByRangeA( hdc, first, last, &i )))
|
|
return FALSE;
|
|
wstr = FONT_mbtowc( hdc, str, i, &wlen, NULL );
|
|
heap_free(str);
|
|
|
|
for (i = 0; i < wlen; ++i)
|
|
{
|
|
if (!GetCharWidthFloatW( hdc, wstr[i], wstr[i], &buffer[i] ))
|
|
{
|
|
heap_free(wstr);
|
|
return FALSE;
|
|
}
|
|
}
|
|
heap_free(wstr);
|
|
return TRUE;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* GetCharWidthFloatW [GDI32.@]
|
|
*/
|
|
BOOL WINAPI GetCharWidthFloatW( HDC hdc, UINT first, UINT last, float *buffer )
|
|
{
|
|
DC *dc = get_dc_ptr( hdc );
|
|
int *ibuffer;
|
|
PHYSDEV dev;
|
|
BOOL ret;
|
|
UINT i;
|
|
|
|
TRACE("dc %p, first %#x, last %#x, buffer %p\n", dc, first, last, buffer);
|
|
|
|
if (!dc) return FALSE;
|
|
|
|
if (!(ibuffer = heap_alloc( (last - first + 1) * sizeof(int) )))
|
|
{
|
|
release_dc_ptr( dc );
|
|
return FALSE;
|
|
}
|
|
|
|
dev = GET_DC_PHYSDEV( dc, pGetCharWidth );
|
|
if ((ret = dev->funcs->pGetCharWidth( dev, first, last, ibuffer )))
|
|
{
|
|
float scale = fabs( dc->xformVport2World.eM11 ) / 16.0f;
|
|
for (i = first; i <= last; ++i)
|
|
buffer[i - first] = ibuffer[i - first] * scale;
|
|
}
|
|
|
|
heap_free(ibuffer);
|
|
return ret;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* *
|
|
* Font Resource API *
|
|
* *
|
|
***********************************************************************/
|
|
|
|
/***********************************************************************
|
|
* AddFontResourceA (GDI32.@)
|
|
*/
|
|
INT WINAPI AddFontResourceA( LPCSTR str )
|
|
{
|
|
return AddFontResourceExA( str, 0, NULL);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* AddFontResourceW (GDI32.@)
|
|
*/
|
|
INT WINAPI AddFontResourceW( LPCWSTR str )
|
|
{
|
|
return AddFontResourceExW(str, 0, NULL);
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* AddFontResourceExA (GDI32.@)
|
|
*/
|
|
INT WINAPI AddFontResourceExA( LPCSTR str, DWORD fl, PVOID pdv )
|
|
{
|
|
DWORD len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
|
|
LPWSTR strW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
|
|
INT ret;
|
|
|
|
MultiByteToWideChar(CP_ACP, 0, str, -1, strW, len);
|
|
ret = AddFontResourceExW(strW, fl, pdv);
|
|
HeapFree(GetProcessHeap(), 0, strW);
|
|
return ret;
|
|
}
|
|
|
|
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 );
|
|
EnterCriticalSection( &font_cs );
|
|
ret = font_funcs->remove_font( path, flags );
|
|
LeaveCriticalSection( &font_cs );
|
|
if (!ret)
|
|
{
|
|
get_fonts_data_dir_path( file, path );
|
|
EnterCriticalSection( &font_cs );
|
|
ret = font_funcs->remove_font( path, flags );
|
|
LeaveCriticalSection( &font_cs );
|
|
}
|
|
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 && !strchrW( 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;
|
|
EnterCriticalSection( &font_cs );
|
|
ret = font_funcs->remove_font( path, addfont_flags );
|
|
LeaveCriticalSection( &font_cs );
|
|
}
|
|
|
|
if (!ret && !strchrW( file, '\\' ))
|
|
ret = remove_system_font_resource( file, ADDFONT_ALLOW_BITMAP | ADDFONT_ADD_RESOURCE );
|
|
|
|
return ret;
|
|
}
|
|
|
|
void load_system_bitmap_fonts(void)
|
|
{
|
|
static const WCHAR keyW[] = {'S','o','f','t','w','a','r','e','\\','F','o','n','t','s',0};
|
|
static const WCHAR fontsW[] = {'F','O','N','T','S','.','F','O','N',0};
|
|
static const WCHAR oemfontW[] = {'O','E','M','F','O','N','T','.','F','O','N',0};
|
|
static const WCHAR fixedfonW[] = {'F','I','X','E','D','F','O','N','.','F','O','N',0};
|
|
static const WCHAR * const fonts[] = { fontsW, oemfontW, fixedfonW };
|
|
|
|
HKEY hkey;
|
|
WCHAR data[MAX_PATH];
|
|
DWORD i, dlen, type;
|
|
|
|
if (RegOpenKeyW( HKEY_CURRENT_CONFIG, keyW, &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 | ADDFONT_ADD_TO_CACHE );
|
|
}
|
|
RegCloseKey( hkey );
|
|
}
|
|
|
|
static void load_directory_fonts( WCHAR *path, UINT flags )
|
|
{
|
|
HANDLE handle;
|
|
WIN32_FIND_DATAW data;
|
|
WCHAR *p;
|
|
|
|
p = path + strlenW(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;
|
|
strcpyW( p, data.cFileName );
|
|
font_funcs->add_font( path, flags );
|
|
} while (FindNextFileW( handle, &data ));
|
|
FindClose( handle );
|
|
}
|
|
|
|
void load_file_system_fonts(void)
|
|
{
|
|
static const WCHAR pathW[] = {'P','a','t','h',0};
|
|
static const WCHAR slashstarW[] = {'\\','*',0};
|
|
static const WCHAR starW[] = {'*',0};
|
|
WCHAR *ptr, *next, path[MAX_PATH], value[1024];
|
|
DWORD len = ARRAY_SIZE(value);
|
|
|
|
/* Windows directory */
|
|
get_fonts_win_dir_path( starW, path );
|
|
load_directory_fonts( path, ADDFONT_ADD_TO_CACHE );
|
|
|
|
/* Wine data directory */
|
|
get_fonts_data_dir_path( starW, path );
|
|
load_directory_fonts( path, ADDFONT_ADD_TO_CACHE | ADDFONT_EXTERNAL_FONT );
|
|
|
|
/* custom paths */
|
|
/* @@ Wine registry key: HKCU\Software\Wine\Fonts */
|
|
if (!RegQueryValueExW( wine_fonts_key, pathW, NULL, NULL, (BYTE *)value, &len ))
|
|
{
|
|
for (ptr = value; ptr; ptr = next)
|
|
{
|
|
if ((next = strchrW( ptr, ';' ))) *next++ = 0;
|
|
if (next && next - ptr < 2) continue;
|
|
lstrcpynW( path, ptr, MAX_PATH - 2 );
|
|
strcatW( path, slashstarW );
|
|
load_directory_fonts( path, ADDFONT_ADD_TO_CACHE | ADDFONT_EXTERNAL_FONT );
|
|
}
|
|
}
|
|
}
|
|
|
|
void load_registry_fonts(void)
|
|
{
|
|
static const WCHAR dot_fonW[] = {'.','f','o','n',0};
|
|
static const WCHAR win9x_key[] = {'S','o','f','t','w','a','r','e','\\',
|
|
'M','i','c','r','o','s','o','f','t','\\',
|
|
'W','i','n','d','o','w','s','\\',
|
|
'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
|
|
'F','o','n','t','s',0};
|
|
static const WCHAR winnt_key[] = {'S','o','f','t','w','a','r','e','\\',
|
|
'M','i','c','r','o','s','o','f','t','\\',
|
|
'W','i','n','d','o','w','s',' ','N','T','\\',
|
|
'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
|
|
'F','o','n','t','s',0};
|
|
WCHAR value[MAX_PATH], data[MAX_PATH];
|
|
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() ? win9x_key : winnt_key, &hkey ))
|
|
return;
|
|
|
|
vlen = ARRAY_SIZE(value);
|
|
dlen = sizeof(data);
|
|
while (!RegEnumValueW( hkey, i++, value, &vlen, NULL, &type, (LPBYTE)data, &dlen ))
|
|
{
|
|
dlen /= sizeof(WCHAR);
|
|
if (data[0] && data[1] == ':')
|
|
add_font_resource( data, ADDFONT_ALLOW_BITMAP | ADDFONT_ADD_TO_CACHE );
|
|
else if (dlen >= 6 && !strcmpiW( data + dlen - 5, dot_fonW ))
|
|
add_system_font_resource( data, ADDFONT_ALLOW_BITMAP | ADDFONT_ADD_TO_CACHE );
|
|
|
|
vlen = ARRAY_SIZE(value);
|
|
dlen = sizeof(data);
|
|
}
|
|
RegCloseKey( hkey );
|
|
}
|
|
|
|
/***********************************************************************
|
|
* AddFontResourceExW (GDI32.@)
|
|
*/
|
|
INT WINAPI AddFontResourceExW( LPCWSTR str, DWORD flags, PVOID pdv )
|
|
{
|
|
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;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* RemoveFontResourceA (GDI32.@)
|
|
*/
|
|
BOOL WINAPI RemoveFontResourceA( LPCSTR str )
|
|
{
|
|
return RemoveFontResourceExA(str, 0, 0);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* RemoveFontResourceW (GDI32.@)
|
|
*/
|
|
BOOL WINAPI RemoveFontResourceW( LPCWSTR str )
|
|
{
|
|
return RemoveFontResourceExW(str, 0, 0);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* AddFontMemResourceEx (GDI32.@)
|
|
*/
|
|
HANDLE WINAPI AddFontMemResourceEx( PVOID ptr, DWORD size, PVOID pdv, DWORD *pcFonts )
|
|
{
|
|
HANDLE ret;
|
|
DWORD num_fonts;
|
|
void *copy;
|
|
|
|
if (!ptr || !size || !pcFonts)
|
|
{
|
|
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
|
|
{
|
|
*pcFonts = num_fonts;
|
|
}
|
|
__EXCEPT_PAGE_FAULT
|
|
{
|
|
WARN("page fault while writing to *pcFonts (%p)\n", pcFonts);
|
|
RemoveFontMemResourceEx( ret );
|
|
ret = 0;
|
|
}
|
|
__ENDTRY
|
|
TRACE( "Returning handle %p\n", ret );
|
|
return ret;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* RemoveFontMemResourceEx (GDI32.@)
|
|
*/
|
|
BOOL WINAPI RemoveFontMemResourceEx( HANDLE fh )
|
|
{
|
|
FIXME("(%p) stub\n", fh);
|
|
return TRUE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* RemoveFontResourceExA (GDI32.@)
|
|
*/
|
|
BOOL WINAPI RemoveFontResourceExA( LPCSTR str, DWORD fl, PVOID pdv )
|
|
{
|
|
DWORD len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
|
|
LPWSTR strW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
|
|
INT ret;
|
|
|
|
MultiByteToWideChar(CP_ACP, 0, str, -1, strW, len);
|
|
ret = RemoveFontResourceExW(strW, fl, pdv);
|
|
HeapFree(GetProcessHeap(), 0, strW);
|
|
return ret;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* RemoveFontResourceExW (GDI32.@)
|
|
*/
|
|
BOOL WINAPI RemoveFontResourceExW( LPCWSTR str, DWORD flags, PVOID pdv )
|
|
{
|
|
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;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* GetFontResourceInfoW (GDI32.@)
|
|
*/
|
|
BOOL WINAPI GetFontResourceInfoW( LPCWSTR str, LPDWORD size, PVOID buffer, DWORD type )
|
|
{
|
|
FIXME("%s %p(%d) %p %d\n", debugstr_w(str), size, size ? *size : 0, buffer, type);
|
|
return FALSE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* GetTextCharset (GDI32.@)
|
|
*/
|
|
UINT WINAPI GetTextCharset(HDC hdc)
|
|
{
|
|
/* MSDN docs say this is equivalent */
|
|
return GetTextCharsetInfo(hdc, NULL, 0);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* GdiGetCharDimensions (GDI32.@)
|
|
*
|
|
* Gets the average width of the characters in the English alphabet.
|
|
*
|
|
* PARAMS
|
|
* hdc [I] Handle to the device context to measure on.
|
|
* lptm [O] Pointer to memory to store the text metrics into.
|
|
* height [O] On exit, the maximum height of characters in the English alphabet.
|
|
*
|
|
* RETURNS
|
|
* The average width of characters in the English alphabet.
|
|
*
|
|
* NOTES
|
|
* This function is used by the dialog manager to get the size of a dialog
|
|
* unit. It should also be used by other pieces of code that need to know
|
|
* the size of a dialog unit in logical units without having access to the
|
|
* window handle of the dialog.
|
|
* Windows caches the font metrics from this function, but we don't and
|
|
* there doesn't appear to be an immediate advantage to do so.
|
|
*
|
|
* SEE ALSO
|
|
* GetTextExtentPointW, GetTextMetricsW, MapDialogRect.
|
|
*/
|
|
LONG WINAPI GdiGetCharDimensions(HDC hdc, LPTEXTMETRICW lptm, LONG *height)
|
|
{
|
|
SIZE sz;
|
|
static const WCHAR alphabet[] = {
|
|
'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q',
|
|
'r','s','t','u','v','w','x','y','z','A','B','C','D','E','F','G','H',
|
|
'I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',0};
|
|
|
|
if(lptm && !GetTextMetricsW(hdc, lptm)) return 0;
|
|
|
|
if(!GetTextExtentPointW(hdc, alphabet, 52, &sz)) return 0;
|
|
|
|
if (height) *height = sz.cy;
|
|
return (sz.cx / 26 + 1) / 2;
|
|
}
|
|
|
|
BOOL WINAPI EnableEUDC(BOOL fEnableEUDC)
|
|
{
|
|
FIXME("(%d): stub\n", fEnableEUDC);
|
|
return FALSE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* GetCharWidthI (GDI32.@)
|
|
*
|
|
* Retrieve widths of characters.
|
|
*
|
|
* PARAMS
|
|
* hdc [I] Handle to a device context.
|
|
* first [I] First glyph in range to query.
|
|
* count [I] Number of glyph indices to query.
|
|
* glyphs [I] Array of glyphs to query.
|
|
* buffer [O] Buffer to receive character widths.
|
|
*
|
|
* NOTES
|
|
* Only works with TrueType fonts.
|
|
*
|
|
* RETURNS
|
|
* Success: TRUE
|
|
* Failure: FALSE
|
|
*/
|
|
BOOL WINAPI GetCharWidthI(HDC hdc, UINT first, UINT count, LPWORD glyphs, LPINT buffer)
|
|
{
|
|
ABC *abc;
|
|
unsigned int i;
|
|
|
|
TRACE("(%p, %d, %d, %p, %p)\n", hdc, first, count, glyphs, buffer);
|
|
|
|
if (!(abc = HeapAlloc(GetProcessHeap(), 0, count * sizeof(ABC))))
|
|
return FALSE;
|
|
|
|
if (!GetCharABCWidthsI(hdc, first, count, glyphs, abc))
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, abc);
|
|
return FALSE;
|
|
}
|
|
|
|
for (i = 0; i < count; i++)
|
|
buffer[i] = abc[i].abcA + abc[i].abcB + abc[i].abcC;
|
|
|
|
HeapFree(GetProcessHeap(), 0, abc);
|
|
return TRUE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* GetFontUnicodeRanges (GDI32.@)
|
|
*
|
|
* 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 GetFontUnicodeRanges(HDC hdc, LPGLYPHSET 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;
|
|
}
|
|
|
|
|
|
/*************************************************************
|
|
* FontIsLinked (GDI32.@)
|
|
*/
|
|
BOOL WINAPI FontIsLinked(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;
|
|
}
|
|
|
|
/*************************************************************
|
|
* GetFontRealizationInfo (GDI32.@)
|
|
*/
|
|
BOOL WINAPI GetFontRealizationInfo(HDC hdc, struct font_realization_info *info)
|
|
{
|
|
BOOL is_v0 = info->size == FIELD_OFFSET(struct font_realization_info, unk);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* GetRasterizerCaps (GDI32.@)
|
|
*/
|
|
BOOL WINAPI GetRasterizerCaps( LPRASTERIZER_STATUS lprs, UINT cbNumBytes)
|
|
{
|
|
lprs->nSize = sizeof(RASTERIZER_STATUS);
|
|
lprs->wFlags = font_funcs ? (TT_AVAILABLE | TT_ENABLED) : 0;
|
|
lprs->nLanguageID = 0;
|
|
return TRUE;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* GetFontFileData (GDI32.@)
|
|
*/
|
|
BOOL WINAPI GetFontFileData( DWORD instance_id, DWORD unknown, 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;
|
|
}
|
|
|
|
/* Undocumented structure filled in by GetFontFileInfo */
|
|
struct font_fileinfo
|
|
{
|
|
FILETIME writetime;
|
|
LARGE_INTEGER size;
|
|
WCHAR path[1];
|
|
};
|
|
|
|
/*************************************************************************
|
|
* GetFontFileInfo (GDI32.@)
|
|
*/
|
|
BOOL WINAPI GetFontFileInfo( DWORD instance_id, DWORD unknown, 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) + strlenW( font->file ) * sizeof(WCHAR);
|
|
if (required_size <= size)
|
|
{
|
|
info->writetime = font->writetime;
|
|
info->size.QuadPart = font->data_size;
|
|
strcpyW( info->path, font->file );
|
|
ret = TRUE;
|
|
}
|
|
else SetLastError( ERROR_INSUFFICIENT_BUFFER );
|
|
}
|
|
|
|
LeaveCriticalSection( &font_cs );
|
|
if (needed) *needed = required_size;
|
|
return ret;
|
|
}
|
|
|
|
struct realization_info
|
|
{
|
|
DWORD flags; /* 1 for bitmap fonts, 3 for scalable fonts */
|
|
DWORD cache_num; /* keeps incrementing - num of fonts that have been created allowing for caching?? */
|
|
DWORD instance_id; /* identifies a realized font instance */
|
|
};
|
|
|
|
/*************************************************************
|
|
* GdiRealizationInfo (GDI32.@)
|
|
*
|
|
* Returns a structure that contains some font information.
|
|
*/
|
|
BOOL WINAPI GdiRealizationInfo(HDC hdc, struct realization_info *info)
|
|
{
|
|
struct font_realization_info ri;
|
|
BOOL ret;
|
|
|
|
ri.size = sizeof(ri);
|
|
ret = GetFontRealizationInfo( hdc, &ri );
|
|
if (ret)
|
|
{
|
|
info->flags = ri.flags;
|
|
info->cache_num = ri.cache_num;
|
|
info->instance_id = ri.instance_id;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*************************************************************
|
|
* GetCharWidthInfo (GDI32.@)
|
|
*
|
|
*/
|
|
BOOL WINAPI GetCharWidthInfo(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;
|
|
}
|