reactos/win32ss/gdi/ntgdi/freetype.c
2021-10-29 22:06:49 -04:00

7350 lines
212 KiB
C

/*
* PROJECT: ReactOS win32 kernel mode subsystem
* LICENSE: GPL - See COPYING in the top level directory
* FILE: win32ss/gdi/ntgdi/freetype.c
* PURPOSE: FreeType font engine interface
* PROGRAMMERS: Copyright 2001 Huw D M Davies for CodeWeavers.
* Copyright 2006 Dmitry Timoshkov for CodeWeavers.
* Copyright 2016-2019 Katayama Hirofumi MZ.
*/
/** Includes ******************************************************************/
#include <win32k.h>
#include FT_GLYPH_H
#include FT_TYPE1_TABLES_H
#include FT_TRUETYPE_TABLES_H
#include FT_TRUETYPE_TAGS_H
#include FT_TRIGONOMETRY_H
#include FT_BITMAP_H
#include FT_OUTLINE_H
#include FT_WINFONTS_H
#include FT_SFNT_NAMES_H
#include FT_SYNTHESIS_H
#include FT_TRUETYPE_IDS_H
#ifndef FT_INTERNAL_INTERNAL_H
#define FT_INTERNAL_INTERNAL_H <freetype/internal/internal.h>
#include FT_INTERNAL_INTERNAL_H
#endif
#include FT_INTERNAL_TRUETYPE_TYPES_H
#include <gdi/eng/floatobj.h>
#include "font.h"
#define NDEBUG
#include <debug.h>
/* TPMF_FIXED_PITCH is confusing; brain-dead api */
#ifndef _TMPF_VARIABLE_PITCH
#define _TMPF_VARIABLE_PITCH TMPF_FIXED_PITCH
#endif
/* Is bold emulation necessary? */
#define EMUBOLD_NEEDED(original, request) \
((request) != FW_DONTCARE) && ((request) - (original) >= FW_BOLD - FW_MEDIUM)
extern const MATRIX gmxWorldToDeviceDefault;
extern const MATRIX gmxWorldToPageDefault;
static const FT_Matrix identityMat = {(1 << 16), 0, 0, (1 << 16)};
/* HACK!! Fix XFORMOBJ then use 1:16 / 16:1 */
#define gmxWorldToDeviceDefault gmxWorldToPageDefault
FT_Library g_FreeTypeLibrary;
/* registry */
static UNICODE_STRING g_FontRegPath =
RTL_CONSTANT_STRING(L"\\REGISTRY\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts");
/* The FreeType library is not thread safe, so we have
to serialize access to it */
static PFAST_MUTEX g_FreeTypeLock;
static LIST_ENTRY g_FontListHead;
static PFAST_MUTEX g_FontListLock;
static BOOL g_RenderingEnabled = TRUE;
#define IntLockGlobalFonts() \
ExEnterCriticalRegionAndAcquireFastMutexUnsafe(g_FontListLock)
#define IntUnLockGlobalFonts() \
ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(g_FontListLock)
#define ASSERT_GLOBALFONTS_LOCK_HELD() \
ASSERT(g_FontListLock->Owner == KeGetCurrentThread())
#define IntLockFreeType() \
ExEnterCriticalRegionAndAcquireFastMutexUnsafe(g_FreeTypeLock)
#define IntUnLockFreeType() \
ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(g_FreeTypeLock)
#define ASSERT_FREETYPE_LOCK_HELD() \
ASSERT(g_FreeTypeLock->Owner == KeGetCurrentThread())
#define ASSERT_FREETYPE_LOCK_NOT_HELD() \
ASSERT(g_FreeTypeLock->Owner != KeGetCurrentThread())
#define MAX_FONT_CACHE 256
static LIST_ENTRY g_FontCacheListHead;
static UINT g_FontCacheNumEntries;
static PWCHAR g_ElfScripts[32] = /* These are in the order of the fsCsb[0] bits */
{
L"Western", /* 00 */
L"Central_European",
L"Cyrillic",
L"Greek",
L"Turkish",
L"Hebrew",
L"Arabic",
L"Baltic",
L"Vietnamese", /* 08 */
NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 15 */
L"Thai",
L"Japanese",
L"CHINESE_GB2312",
L"Hangul",
L"CHINESE_BIG5",
L"Hangul(Johab)",
NULL, NULL, /* 23 */
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
L"Symbol" /* 31 */
};
/*
* For TranslateCharsetInfo
*/
#define CP_SYMBOL 42
#define MAXTCIINDEX 32
static const CHARSETINFO g_FontTci[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}} }
};
#ifndef CP_OEMCP
#define CP_OEMCP 1
#define CP_MACCP 2
#endif
/* Get charset from specified codepage.
g_FontTci is used also in TranslateCharsetInfo. */
BYTE FASTCALL IntCharSetFromCodePage(UINT uCodePage)
{
UINT i;
if (uCodePage == CP_OEMCP)
return OEM_CHARSET;
if (uCodePage == CP_MACCP)
return MAC_CHARSET;
for (i = 0; i < MAXTCIINDEX; ++i)
{
if (g_FontTci[i].ciACP == 0)
continue;
if (g_FontTci[i].ciACP == uCodePage)
return g_FontTci[i].ciCharset;
}
return DEFAULT_CHARSET;
}
/* list head */
static RTL_STATIC_LIST_HEAD(g_FontSubstListHead);
static void
SharedMem_AddRef(PSHARED_MEM Ptr)
{
ASSERT_FREETYPE_LOCK_HELD();
++Ptr->RefCount;
}
static void
SharedFaceCache_Init(PSHARED_FACE_CACHE Cache)
{
Cache->OutlineRequiredSize = 0;
RtlInitUnicodeString(&Cache->FontFamily, NULL);
RtlInitUnicodeString(&Cache->FullName, NULL);
}
static PSHARED_FACE
SharedFace_Create(FT_Face Face, PSHARED_MEM Memory)
{
PSHARED_FACE Ptr;
Ptr = ExAllocatePoolWithTag(PagedPool, sizeof(SHARED_FACE), TAG_FONT);
if (Ptr)
{
Ptr->Face = Face;
Ptr->RefCount = 1;
Ptr->Memory = Memory;
SharedFaceCache_Init(&Ptr->EnglishUS);
SharedFaceCache_Init(&Ptr->UserLanguage);
SharedMem_AddRef(Memory);
DPRINT("Creating SharedFace for %s\n", Face->family_name ? Face->family_name : "<NULL>");
}
return Ptr;
}
static PSHARED_MEM
SharedMem_Create(PBYTE Buffer, ULONG BufferSize, BOOL IsMapping)
{
PSHARED_MEM Ptr;
Ptr = ExAllocatePoolWithTag(PagedPool, sizeof(SHARED_MEM), TAG_FONT);
if (Ptr)
{
Ptr->Buffer = Buffer;
Ptr->BufferSize = BufferSize;
Ptr->RefCount = 1;
Ptr->IsMapping = IsMapping;
DPRINT("Creating SharedMem for %p (%i, %p)\n", Buffer, IsMapping, Ptr);
}
return Ptr;
}
static void
SharedFace_AddRef(PSHARED_FACE Ptr)
{
ASSERT_FREETYPE_LOCK_HELD();
++Ptr->RefCount;
}
static void
RemoveCachedEntry(PFONT_CACHE_ENTRY Entry)
{
ASSERT_FREETYPE_LOCK_HELD();
FT_Done_Glyph((FT_Glyph)Entry->BitmapGlyph);
RemoveEntryList(&Entry->ListEntry);
ExFreePoolWithTag(Entry, TAG_FONT);
g_FontCacheNumEntries--;
ASSERT(g_FontCacheNumEntries <= MAX_FONT_CACHE);
}
static void
RemoveCacheEntries(FT_Face Face)
{
PLIST_ENTRY CurrentEntry, NextEntry;
PFONT_CACHE_ENTRY FontEntry;
ASSERT_FREETYPE_LOCK_HELD();
for (CurrentEntry = g_FontCacheListHead.Flink;
CurrentEntry != &g_FontCacheListHead;
CurrentEntry = NextEntry)
{
FontEntry = CONTAINING_RECORD(CurrentEntry, FONT_CACHE_ENTRY, ListEntry);
NextEntry = CurrentEntry->Flink;
if (FontEntry->Face == Face)
{
RemoveCachedEntry(FontEntry);
}
}
}
static void SharedMem_Release(PSHARED_MEM Ptr)
{
ASSERT_FREETYPE_LOCK_HELD();
ASSERT(Ptr->RefCount > 0);
if (Ptr->RefCount <= 0)
return;
--Ptr->RefCount;
if (Ptr->RefCount == 0)
{
DPRINT("Releasing SharedMem for %p (%i, %p)\n", Ptr->Buffer, Ptr->IsMapping, Ptr);
if (Ptr->IsMapping)
MmUnmapViewInSystemSpace(Ptr->Buffer);
else
ExFreePoolWithTag(Ptr->Buffer, TAG_FONT);
ExFreePoolWithTag(Ptr, TAG_FONT);
}
}
static void
SharedFaceCache_Release(PSHARED_FACE_CACHE Cache)
{
RtlFreeUnicodeString(&Cache->FontFamily);
RtlFreeUnicodeString(&Cache->FullName);
}
static void
SharedFace_Release(PSHARED_FACE Ptr)
{
IntLockFreeType();
ASSERT(Ptr->RefCount > 0);
if (Ptr->RefCount <= 0)
return;
--Ptr->RefCount;
if (Ptr->RefCount == 0)
{
DPRINT("Releasing SharedFace for %s\n", Ptr->Face->family_name ? Ptr->Face->family_name : "<NULL>");
RemoveCacheEntries(Ptr->Face);
FT_Done_Face(Ptr->Face);
SharedMem_Release(Ptr->Memory);
SharedFaceCache_Release(&Ptr->EnglishUS);
SharedFaceCache_Release(&Ptr->UserLanguage);
ExFreePoolWithTag(Ptr, TAG_FONT);
}
IntUnLockFreeType();
}
static VOID FASTCALL
CleanupFontEntryEx(PFONT_ENTRY FontEntry, PFONTGDI FontGDI)
{
// PFONTGDI FontGDI = FontEntry->Font;
PSHARED_FACE SharedFace = FontGDI->SharedFace;
if (FontGDI->Filename)
ExFreePoolWithTag(FontGDI->Filename, GDITAG_PFF);
if (FontEntry->StyleName.Buffer)
RtlFreeUnicodeString(&FontEntry->StyleName);
if (FontEntry->FaceName.Buffer)
RtlFreeUnicodeString(&FontEntry->FaceName);
EngFreeMem(FontGDI);
SharedFace_Release(SharedFace);
ExFreePoolWithTag(FontEntry, TAG_FONT);
}
static __inline VOID FASTCALL
CleanupFontEntry(PFONT_ENTRY FontEntry)
{
CleanupFontEntryEx(FontEntry, FontEntry->Font);
}
static __inline void FTVectorToPOINTFX(FT_Vector *vec, POINTFX *pt)
{
pt->x.value = vec->x >> 6;
pt->x.fract = (vec->x & 0x3f) << 10;
pt->x.fract |= ((pt->x.fract >> 6) | (pt->x.fract >> 12));
pt->y.value = vec->y >> 6;
pt->y.fract = (vec->y & 0x3f) << 10;
pt->y.fract |= ((pt->y.fract >> 6) | (pt->y.fract >> 12));
}
/*
This function builds an FT_Fixed from a FIXED. It simply put f.value
in the highest 16 bits and f.fract in the lowest 16 bits of the FT_Fixed.
*/
static __inline FT_Fixed FT_FixedFromFIXED(FIXED f)
{
return (FT_Fixed)((long)f.value << 16 | (unsigned long)f.fract);
}
#if DBG
VOID DumpFontEntry(PFONT_ENTRY FontEntry)
{
const char *family_name;
const char *style_name;
FT_Face Face;
PFONTGDI FontGDI = FontEntry->Font;
if (!FontGDI)
{
DPRINT("FontGDI NULL\n");
return;
}
Face = (FontGDI->SharedFace ? FontGDI->SharedFace->Face : NULL);
if (Face)
{
family_name = Face->family_name;
style_name = Face->style_name;
}
else
{
family_name = "<invalid>";
style_name = "<invalid>";
}
DPRINT("family_name '%s', style_name '%s', FaceName '%wZ', StyleName '%wZ', FontGDI %p, "
"FontObj %p, iUnique %lu, SharedFace %p, Face %p, CharSet %u, Filename '%S'\n",
family_name,
style_name,
&FontEntry->FaceName,
&FontEntry->StyleName,
FontGDI,
&FontGDI->FontObj,
FontGDI->iUnique,
FontGDI->SharedFace,
Face,
FontGDI->CharSet,
FontGDI->Filename);
}
VOID DumpFontList(PLIST_ENTRY Head)
{
PLIST_ENTRY Entry;
PFONT_ENTRY CurrentEntry;
DPRINT("## DumpFontList(%p)\n", Head);
for (Entry = Head->Flink; Entry != Head; Entry = Entry->Flink)
{
CurrentEntry = CONTAINING_RECORD(Entry, FONT_ENTRY, ListEntry);
DumpFontEntry(CurrentEntry);
}
}
VOID DumpFontSubstEntry(PFONTSUBST_ENTRY pSubstEntry)
{
DPRINT("%wZ,%u -> %wZ,%u\n",
&pSubstEntry->FontNames[FONTSUBST_FROM],
pSubstEntry->CharSets[FONTSUBST_FROM],
&pSubstEntry->FontNames[FONTSUBST_TO],
pSubstEntry->CharSets[FONTSUBST_TO]);
}
VOID DumpFontSubstList(VOID)
{
PLIST_ENTRY pHead = &g_FontSubstListHead;
PLIST_ENTRY pListEntry;
PFONTSUBST_ENTRY pSubstEntry;
DPRINT("## DumpFontSubstList\n");
for (pListEntry = pHead->Flink;
pListEntry != pHead;
pListEntry = pListEntry->Flink)
{
pSubstEntry =
(PFONTSUBST_ENTRY)CONTAINING_RECORD(pListEntry, FONT_ENTRY, ListEntry);
DumpFontSubstEntry(pSubstEntry);
}
}
VOID DumpPrivateFontList(BOOL bDoLock)
{
PPROCESSINFO Win32Process = PsGetCurrentProcessWin32Process();
if (!Win32Process)
return;
if (bDoLock)
IntLockProcessPrivateFonts(Win32Process);
DumpFontList(&Win32Process->PrivateFontListHead);
if (bDoLock)
IntUnLockProcessPrivateFonts(Win32Process);
}
VOID DumpGlobalFontList(BOOL bDoLock)
{
if (bDoLock)
IntLockGlobalFonts();
DumpFontList(&g_FontListHead);
if (bDoLock)
IntUnLockGlobalFonts();
}
VOID DumpFontInfo(BOOL bDoLock)
{
DumpGlobalFontList(bDoLock);
DumpPrivateFontList(bDoLock);
DumpFontSubstList();
}
#endif
/*
* IntLoadFontSubstList --- loads the list of font substitutes
*/
BOOL FASTCALL
IntLoadFontSubstList(PLIST_ENTRY pHead)
{
NTSTATUS Status;
HANDLE KeyHandle;
OBJECT_ATTRIBUTES ObjectAttributes;
KEY_FULL_INFORMATION KeyFullInfo;
ULONG i, Length;
UNICODE_STRING FromW, ToW;
BYTE InfoBuffer[128];
PKEY_VALUE_FULL_INFORMATION pInfo;
BYTE CharSets[FONTSUBST_FROM_AND_TO];
LPWSTR pch;
PFONTSUBST_ENTRY pEntry;
BOOLEAN Success;
/* the FontSubstitutes registry key */
static UNICODE_STRING FontSubstKey =
RTL_CONSTANT_STRING(L"\\Registry\\Machine\\Software\\"
L"Microsoft\\Windows NT\\CurrentVersion\\"
L"FontSubstitutes");
/* open registry key */
InitializeObjectAttributes(&ObjectAttributes, &FontSubstKey,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL, NULL);
Status = ZwOpenKey(&KeyHandle, KEY_READ, &ObjectAttributes);
if (!NT_SUCCESS(Status))
{
DPRINT("ZwOpenKey failed: 0x%08X\n", Status);
return FALSE; /* failure */
}
/* query count of values */
Status = ZwQueryKey(KeyHandle, KeyFullInformation,
&KeyFullInfo, sizeof(KeyFullInfo), &Length);
if (!NT_SUCCESS(Status))
{
DPRINT("ZwQueryKey failed: 0x%08X\n", Status);
ZwClose(KeyHandle);
return FALSE; /* failure */
}
/* for each value */
for (i = 0; i < KeyFullInfo.Values; ++i)
{
/* get value name */
Status = ZwEnumerateValueKey(KeyHandle, i, KeyValueFullInformation,
InfoBuffer, sizeof(InfoBuffer), &Length);
if (!NT_SUCCESS(Status))
{
DPRINT("ZwEnumerateValueKey failed: 0x%08X\n", Status);
break; /* failure */
}
/* create FromW string */
pInfo = (PKEY_VALUE_FULL_INFORMATION)InfoBuffer;
Length = pInfo->NameLength / sizeof(WCHAR);
pInfo->Name[Length] = UNICODE_NULL; /* truncate */
Success = RtlCreateUnicodeString(&FromW, pInfo->Name);
if (!Success)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
DPRINT("RtlCreateUnicodeString failed\n");
break; /* failure */
}
/* query value */
Status = ZwQueryValueKey(KeyHandle, &FromW, KeyValueFullInformation,
InfoBuffer, sizeof(InfoBuffer), &Length);
pInfo = (PKEY_VALUE_FULL_INFORMATION)InfoBuffer;
if (!NT_SUCCESS(Status) || !pInfo->DataLength)
{
DPRINT("ZwQueryValueKey failed: 0x%08X\n", Status);
RtlFreeUnicodeString(&FromW);
break; /* failure */
}
/* create ToW string */
pch = (LPWSTR)((PUCHAR)pInfo + pInfo->DataOffset);
Length = pInfo->DataLength / sizeof(WCHAR);
pch[Length] = UNICODE_NULL; /* truncate */
Success = RtlCreateUnicodeString(&ToW, pch);
if (!Success)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
DPRINT("RtlCreateUnicodeString failed\n");
RtlFreeUnicodeString(&FromW);
break; /* failure */
}
/* does charset exist? (from) */
CharSets[FONTSUBST_FROM] = DEFAULT_CHARSET;
pch = wcsrchr(FromW.Buffer, L',');
if (pch)
{
/* truncate */
*pch = UNICODE_NULL;
FromW.Length = (pch - FromW.Buffer) * sizeof(WCHAR);
/* parse charset number */
CharSets[FONTSUBST_FROM] = (BYTE)_wtoi(pch + 1);
}
/* does charset exist? (to) */
CharSets[FONTSUBST_TO] = DEFAULT_CHARSET;
pch = wcsrchr(ToW.Buffer, L',');
if (pch)
{
/* truncate */
*pch = UNICODE_NULL;
ToW.Length = (pch - ToW.Buffer) * sizeof(WCHAR);
/* parse charset number */
CharSets[FONTSUBST_TO] = (BYTE)_wtoi(pch + 1);
}
/* is it identical? */
if (RtlEqualUnicodeString(&FromW, &ToW, TRUE) &&
CharSets[FONTSUBST_FROM] == CharSets[FONTSUBST_TO])
{
RtlFreeUnicodeString(&FromW);
RtlFreeUnicodeString(&ToW);
continue;
}
/* allocate an entry */
pEntry = ExAllocatePoolWithTag(PagedPool, sizeof(FONTSUBST_ENTRY), TAG_FONT);
if (pEntry == NULL)
{
DPRINT("ExAllocatePoolWithTag failed\n");
RtlFreeUnicodeString(&FromW);
RtlFreeUnicodeString(&ToW);
break; /* failure */
}
/* store to *pEntry */
pEntry->FontNames[FONTSUBST_FROM] = FromW;
pEntry->FontNames[FONTSUBST_TO] = ToW;
pEntry->CharSets[FONTSUBST_FROM] = CharSets[FONTSUBST_FROM];
pEntry->CharSets[FONTSUBST_TO] = CharSets[FONTSUBST_TO];
/* insert pEntry to *pHead */
InsertTailList(pHead, &pEntry->ListEntry);
}
/* close now */
ZwClose(KeyHandle);
return NT_SUCCESS(Status);
}
BOOL FASTCALL
InitFontSupport(VOID)
{
ULONG ulError;
InitializeListHead(&g_FontListHead);
InitializeListHead(&g_FontCacheListHead);
g_FontCacheNumEntries = 0;
/* Fast Mutexes must be allocated from non paged pool */
g_FontListLock = ExAllocatePoolWithTag(NonPagedPool, sizeof(FAST_MUTEX), TAG_INTERNAL_SYNC);
if (g_FontListLock == NULL)
{
return FALSE;
}
ExInitializeFastMutex(g_FontListLock);
g_FreeTypeLock = ExAllocatePoolWithTag(NonPagedPool, sizeof(FAST_MUTEX), TAG_INTERNAL_SYNC);
if (g_FreeTypeLock == NULL)
{
return FALSE;
}
ExInitializeFastMutex(g_FreeTypeLock);
ulError = FT_Init_FreeType(&g_FreeTypeLibrary);
if (ulError)
{
DPRINT1("FT_Init_FreeType failed with error code 0x%x\n", ulError);
return FALSE;
}
if (!IntLoadFontsInRegistry())
{
DPRINT1("Fonts registry is empty.\n");
/* Load font(s) with writing registry */
IntLoadSystemFonts();
}
IntLoadFontSubstList(&g_FontSubstListHead);
#if DBG
DumpFontInfo(TRUE);
#endif
return TRUE;
}
LONG FASTCALL IntWidthMatrix(FT_Face face, FT_Matrix *pmat, LONG lfWidth)
{
LONG tmAveCharWidth;
TT_OS2 *pOS2;
FT_Fixed XScale;
*pmat = identityMat;
if (lfWidth == 0)
return 0;
ASSERT_FREETYPE_LOCK_HELD();
pOS2 = (TT_OS2 *)FT_Get_Sfnt_Table(face, FT_SFNT_OS2);
if (!pOS2)
return 0;
XScale = face->size->metrics.x_scale;
tmAveCharWidth = (FT_MulFix(pOS2->xAvgCharWidth, XScale) + 32) >> 6;
if (tmAveCharWidth == 0)
{
tmAveCharWidth = 1;
}
if (lfWidth == tmAveCharWidth)
return 0;
pmat->xx = INT_TO_FIXED(lfWidth) / tmAveCharWidth;
pmat->xy = 0;
pmat->yx = 0;
pmat->yy = INT_TO_FIXED(1);
return lfWidth;
}
VOID FASTCALL IntEscapeMatrix(FT_Matrix *pmat, LONG lfEscapement)
{
FT_Vector vecAngle;
/* Convert the angle in tenths of degrees into degrees as a 16.16 fixed-point value */
FT_Angle angle = INT_TO_FIXED(lfEscapement) / 10;
FT_Vector_Unit(&vecAngle, angle);
pmat->xx = vecAngle.x;
pmat->xy = -vecAngle.y;
pmat->yx = -pmat->xy;
pmat->yy = pmat->xx;
}
VOID FASTCALL
FtMatrixFromMx(FT_Matrix *pmat, PMATRIX pmx)
{
FLOATOBJ ef;
/* Create a freetype matrix, by converting to 16.16 fixpoint format */
ef = pmx->efM11;
FLOATOBJ_MulLong(&ef, 0x00010000);
pmat->xx = FLOATOBJ_GetLong(&ef);
ef = pmx->efM21;
FLOATOBJ_MulLong(&ef, 0x00010000);
pmat->xy = FLOATOBJ_GetLong(&ef);
ef = pmx->efM12;
FLOATOBJ_MulLong(&ef, 0x00010000);
pmat->yx = FLOATOBJ_GetLong(&ef);
ef = pmx->efM22;
FLOATOBJ_MulLong(&ef, 0x00010000);
pmat->yy = FLOATOBJ_GetLong(&ef);
}
VOID
FtSetCoordinateTransform(
FT_Face face,
PMATRIX pmx)
{
FT_Matrix ftmatrix;
FLOATOBJ efTemp;
/* Create a freetype matrix, by converting to 16.16 fixpoint format */
efTemp = pmx->efM11;
FLOATOBJ_MulLong(&efTemp, 0x00010000);
ftmatrix.xx = FLOATOBJ_GetLong(&efTemp);
efTemp = pmx->efM12;
FLOATOBJ_MulLong(&efTemp, 0x00010000);
ftmatrix.xy = FLOATOBJ_GetLong(&efTemp);
efTemp = pmx->efM21;
FLOATOBJ_MulLong(&efTemp, 0x00010000);
ftmatrix.yx = FLOATOBJ_GetLong(&efTemp);
efTemp = pmx->efM22;
FLOATOBJ_MulLong(&efTemp, 0x00010000);
ftmatrix.yy = FLOATOBJ_GetLong(&efTemp);
/* Set the transformation matrix */
FT_Set_Transform(face, &ftmatrix, 0);
}
static BOOL
SubstituteFontByList(PLIST_ENTRY pHead,
PUNICODE_STRING pOutputName,
PUNICODE_STRING pInputName,
BYTE RequestedCharSet,
BYTE CharSetMap[FONTSUBST_FROM_AND_TO])
{
PLIST_ENTRY pListEntry;
PFONTSUBST_ENTRY pSubstEntry;
BYTE CharSets[FONTSUBST_FROM_AND_TO];
CharSetMap[FONTSUBST_FROM] = DEFAULT_CHARSET;
CharSetMap[FONTSUBST_TO] = RequestedCharSet;
/* for each list entry */
for (pListEntry = pHead->Flink;
pListEntry != pHead;
pListEntry = pListEntry->Flink)
{
pSubstEntry =
(PFONTSUBST_ENTRY)CONTAINING_RECORD(pListEntry, FONT_ENTRY, ListEntry);
CharSets[FONTSUBST_FROM] = pSubstEntry->CharSets[FONTSUBST_FROM];
if (CharSets[FONTSUBST_FROM] != DEFAULT_CHARSET &&
CharSets[FONTSUBST_FROM] != RequestedCharSet)
{
continue; /* not matched */
}
/* does charset number exist? (to) */
if (pSubstEntry->CharSets[FONTSUBST_TO] != DEFAULT_CHARSET)
{
CharSets[FONTSUBST_TO] = pSubstEntry->CharSets[FONTSUBST_TO];
}
else
{
CharSets[FONTSUBST_TO] = RequestedCharSet;
}
/* does font name match? */
if (!RtlEqualUnicodeString(&pSubstEntry->FontNames[FONTSUBST_FROM],
pInputName, TRUE))
{
continue; /* not matched */
}
/* update *pOutputName */
*pOutputName = pSubstEntry->FontNames[FONTSUBST_TO];
if (CharSetMap[FONTSUBST_FROM] == DEFAULT_CHARSET)
{
/* update CharSetMap */
CharSetMap[FONTSUBST_FROM] = CharSets[FONTSUBST_FROM];
CharSetMap[FONTSUBST_TO] = CharSets[FONTSUBST_TO];
}
return TRUE; /* success */
}
return FALSE;
}
static VOID
IntUnicodeStringToBuffer(LPWSTR pszBuffer, SIZE_T cbBuffer, const UNICODE_STRING *pString)
{
SIZE_T cbLength = pString->Length;
if (cbBuffer < sizeof(UNICODE_NULL))
return;
if (cbLength > cbBuffer - sizeof(UNICODE_NULL))
cbLength = cbBuffer - sizeof(UNICODE_NULL);
RtlCopyMemory(pszBuffer, pString->Buffer, cbLength);
pszBuffer[cbLength / sizeof(WCHAR)] = UNICODE_NULL;
}
static NTSTATUS
DuplicateUnicodeString(PUNICODE_STRING Source, PUNICODE_STRING Destination)
{
NTSTATUS Status = STATUS_NO_MEMORY;
UNICODE_STRING Tmp;
Tmp.Buffer = ExAllocatePoolWithTag(PagedPool, Source->MaximumLength, TAG_USTR);
if (Tmp.Buffer)
{
Tmp.MaximumLength = Source->MaximumLength;
Tmp.Length = 0;
RtlCopyUnicodeString(&Tmp, Source);
Destination->MaximumLength = Tmp.MaximumLength;
Destination->Length = Tmp.Length;
Destination->Buffer = Tmp.Buffer;
Status = STATUS_SUCCESS;
}
return Status;
}
static BOOL
SubstituteFontRecurse(LOGFONTW* pLogFont)
{
UINT RecurseCount = 5;
UNICODE_STRING OutputNameW = { 0 };
BYTE CharSetMap[FONTSUBST_FROM_AND_TO];
BOOL Found;
UNICODE_STRING InputNameW;
if (pLogFont->lfFaceName[0] == UNICODE_NULL)
return FALSE;
RtlInitUnicodeString(&InputNameW, pLogFont->lfFaceName);
while (RecurseCount-- > 0)
{
Found = SubstituteFontByList(&g_FontSubstListHead,
&OutputNameW, &InputNameW,
pLogFont->lfCharSet, CharSetMap);
if (!Found)
break;
IntUnicodeStringToBuffer(pLogFont->lfFaceName, sizeof(pLogFont->lfFaceName), &OutputNameW);
if (CharSetMap[FONTSUBST_FROM] == DEFAULT_CHARSET ||
CharSetMap[FONTSUBST_FROM] == pLogFont->lfCharSet)
{
pLogFont->lfCharSet = CharSetMap[FONTSUBST_TO];
}
}
return TRUE; /* success */
}
/*
* IntLoadSystemFonts
*
* Search the system font directory and adds each font found.
*/
VOID FASTCALL
IntLoadSystemFonts(VOID)
{
OBJECT_ATTRIBUTES ObjectAttributes;
UNICODE_STRING Directory, FileName, TempString;
IO_STATUS_BLOCK Iosb;
HANDLE hDirectory;
BYTE *DirInfoBuffer;
PFILE_DIRECTORY_INFORMATION DirInfo;
BOOLEAN bRestartScan = TRUE;
NTSTATUS Status;
INT i;
static UNICODE_STRING SearchPatterns[] =
{
RTL_CONSTANT_STRING(L"*.ttf"),
RTL_CONSTANT_STRING(L"*.ttc"),
RTL_CONSTANT_STRING(L"*.otf"),
RTL_CONSTANT_STRING(L"*.otc"),
RTL_CONSTANT_STRING(L"*.fon"),
RTL_CONSTANT_STRING(L"*.fnt")
};
static UNICODE_STRING IgnoreFiles[] =
{
RTL_CONSTANT_STRING(L"."),
RTL_CONSTANT_STRING(L".."),
};
RtlInitUnicodeString(&Directory, L"\\SystemRoot\\Fonts\\");
InitializeObjectAttributes(
&ObjectAttributes,
&Directory,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL,
NULL);
Status = ZwOpenFile(
&hDirectory,
SYNCHRONIZE | FILE_LIST_DIRECTORY,
&ObjectAttributes,
&Iosb,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE);
if (NT_SUCCESS(Status))
{
for (i = 0; i < _countof(SearchPatterns); ++i)
{
DirInfoBuffer = ExAllocatePoolWithTag(PagedPool, 0x4000, TAG_FONT);
if (DirInfoBuffer == NULL)
{
ZwClose(hDirectory);
return;
}
FileName.Buffer = ExAllocatePoolWithTag(PagedPool, MAX_PATH * sizeof(WCHAR), TAG_FONT);
if (FileName.Buffer == NULL)
{
ExFreePoolWithTag(DirInfoBuffer, TAG_FONT);
ZwClose(hDirectory);
return;
}
FileName.Length = 0;
FileName.MaximumLength = MAX_PATH * sizeof(WCHAR);
while (1)
{
Status = ZwQueryDirectoryFile(
hDirectory,
NULL,
NULL,
NULL,
&Iosb,
DirInfoBuffer,
0x4000,
FileDirectoryInformation,
FALSE,
&SearchPatterns[i],
bRestartScan);
if (!NT_SUCCESS(Status) || Status == STATUS_NO_MORE_FILES)
{
break;
}
DirInfo = (PFILE_DIRECTORY_INFORMATION)DirInfoBuffer;
while (1)
{
SIZE_T ign;
TempString.Buffer = DirInfo->FileName;
TempString.Length = TempString.MaximumLength = DirInfo->FileNameLength;
/* Should we ignore this file? */
for (ign = 0; ign < _countof(IgnoreFiles); ++ign)
{
/* Yes.. */
if (RtlEqualUnicodeString(IgnoreFiles + ign, &TempString, FALSE))
break;
}
/* If we tried all Ignore patterns and there was no match, try to create a font */
if (ign == _countof(IgnoreFiles))
{
RtlCopyUnicodeString(&FileName, &Directory);
RtlAppendUnicodeStringToString(&FileName, &TempString);
if (!IntGdiAddFontResourceEx(&FileName, 0, AFRX_WRITE_REGISTRY))
{
DPRINT1("ERR: Failed to load %wZ\n", &FileName);
}
}
if (DirInfo->NextEntryOffset == 0)
break;
DirInfo = (PFILE_DIRECTORY_INFORMATION)((ULONG_PTR)DirInfo + DirInfo->NextEntryOffset);
}
bRestartScan = FALSE;
}
ExFreePoolWithTag(FileName.Buffer, TAG_FONT);
ExFreePoolWithTag(DirInfoBuffer, TAG_FONT);
}
ZwClose(hDirectory);
}
}
static FT_Error
IntRequestFontSize(PDC dc, PFONTGDI FontGDI, LONG lfWidth, LONG lfHeight);
/* NOTE: If nIndex < 0 then return the number of charsets. */
UINT FASTCALL IntGetCharSet(INT nIndex, FT_ULong CodePageRange1)
{
UINT BitIndex, CharSet;
UINT nCount = 0;
if (CodePageRange1 == 0)
{
return (nIndex < 0) ? 1 : DEFAULT_CHARSET;
}
for (BitIndex = 0; BitIndex < MAXTCIINDEX; ++BitIndex)
{
if (CodePageRange1 & (1 << BitIndex))
{
CharSet = g_FontTci[BitIndex].ciCharset;
if ((nIndex >= 0) && (nCount == (UINT)nIndex))
{
return CharSet;
}
++nCount;
}
}
return (nIndex < 0) ? nCount : ANSI_CHARSET;
}
/* pixels to points */
#define PX2PT(pixels) FT_MulDiv((pixels), 72, 96)
static INT FASTCALL
IntGdiLoadFontsFromMemory(PGDI_LOAD_FONT pLoadFont,
PSHARED_FACE SharedFace, FT_Long FontIndex, INT CharSetIndex)
{
FT_Error Error;
PFONT_ENTRY Entry;
FONT_ENTRY_MEM* PrivateEntry = NULL;
FONTGDI * FontGDI;
NTSTATUS Status;
FT_Face Face;
ANSI_STRING AnsiString;
FT_WinFNT_HeaderRec WinFNT;
INT FaceCount = 0, CharSetCount = 0;
PUNICODE_STRING pFileName = pLoadFont->pFileName;
DWORD Characteristics = pLoadFont->Characteristics;
PUNICODE_STRING pValueName = &pLoadFont->RegValueName;
TT_OS2 * pOS2;
INT BitIndex;
FT_UShort os2_version;
FT_ULong os2_ulCodePageRange1;
FT_UShort os2_usWeightClass;
if (SharedFace == NULL && CharSetIndex == -1)
{
/* load a face from memory */
IntLockFreeType();
Error = FT_New_Memory_Face(
g_FreeTypeLibrary,
pLoadFont->Memory->Buffer,
pLoadFont->Memory->BufferSize,
((FontIndex != -1) ? FontIndex : 0),
&Face);
if (!Error)
SharedFace = SharedFace_Create(Face, pLoadFont->Memory);
IntUnLockFreeType();
if (!Error && FT_IS_SFNT(Face))
pLoadFont->IsTrueType = TRUE;
if (Error || SharedFace == NULL)
{
if (SharedFace)
SharedFace_Release(SharedFace);
if (Error == FT_Err_Unknown_File_Format)
DPRINT1("Unknown font file format\n");
else
DPRINT1("Error reading font (error code: %d)\n", Error);
return 0; /* failure */
}
}
else
{
Face = SharedFace->Face;
IntLockFreeType();
SharedFace_AddRef(SharedFace);
IntUnLockFreeType();
}
/* allocate a FONT_ENTRY */
Entry = ExAllocatePoolWithTag(PagedPool, sizeof(FONT_ENTRY), TAG_FONT);
if (!Entry)
{
SharedFace_Release(SharedFace);
EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
return 0; /* failure */
}
/* allocate a FONTGDI */
FontGDI = EngAllocMem(FL_ZERO_MEMORY, sizeof(FONTGDI), GDITAG_RFONT);
if (!FontGDI)
{
SharedFace_Release(SharedFace);
ExFreePoolWithTag(Entry, TAG_FONT);
EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
return 0; /* failure */
}
/* set file name */
if (pFileName)
{
FontGDI->Filename = ExAllocatePoolWithTag(PagedPool,
pFileName->Length + sizeof(UNICODE_NULL),
GDITAG_PFF);
if (FontGDI->Filename == NULL)
{
EngFreeMem(FontGDI);
SharedFace_Release(SharedFace);
ExFreePoolWithTag(Entry, TAG_FONT);
EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
return 0; /* failure */
}
RtlCopyMemory(FontGDI->Filename, pFileName->Buffer, pFileName->Length);
FontGDI->Filename[pFileName->Length / sizeof(WCHAR)] = UNICODE_NULL;
}
else
{
FontGDI->Filename = NULL;
PrivateEntry = ExAllocatePoolWithTag(PagedPool, sizeof(FONT_ENTRY_MEM), TAG_FONT);
if (!PrivateEntry)
{
if (FontGDI->Filename)
ExFreePoolWithTag(FontGDI->Filename, GDITAG_PFF);
EngFreeMem(FontGDI);
SharedFace_Release(SharedFace);
ExFreePoolWithTag(Entry, TAG_FONT);
return 0;
}
PrivateEntry->Entry = Entry;
if (pLoadFont->PrivateEntry)
{
InsertTailList(&pLoadFont->PrivateEntry->ListEntry, &PrivateEntry->ListEntry);
}
else
{
InitializeListHead(&PrivateEntry->ListEntry);
pLoadFont->PrivateEntry = PrivateEntry;
}
}
/* set face */
FontGDI->SharedFace = SharedFace;
FontGDI->CharSet = ANSI_CHARSET;
FontGDI->OriginalItalic = FALSE;
FontGDI->RequestItalic = FALSE;
FontGDI->OriginalWeight = FALSE;
FontGDI->RequestWeight = FW_NORMAL;
IntLockFreeType();
pOS2 = (TT_OS2 *)FT_Get_Sfnt_Table(Face, FT_SFNT_OS2);
if (pOS2)
{
FontGDI->OriginalItalic = !!(pOS2->fsSelection & 0x1);
FontGDI->OriginalWeight = pOS2->usWeightClass;
}
else
{
Error = FT_Get_WinFNT_Header(Face, &WinFNT);
if (!Error)
{
FontGDI->OriginalItalic = !!WinFNT.italic;
FontGDI->OriginalWeight = WinFNT.weight;
}
}
IntUnLockFreeType();
RtlInitAnsiString(&AnsiString, Face->family_name);
Status = RtlAnsiStringToUnicodeString(&Entry->FaceName, &AnsiString, TRUE);
if (NT_SUCCESS(Status))
{
if (Face->style_name && Face->style_name[0] &&
strcmp(Face->style_name, "Regular") != 0)
{
RtlInitAnsiString(&AnsiString, Face->style_name);
Status = RtlAnsiStringToUnicodeString(&Entry->StyleName, &AnsiString, TRUE);
if (!NT_SUCCESS(Status))
{
RtlFreeUnicodeString(&Entry->FaceName);
}
}
else
{
RtlInitUnicodeString(&Entry->StyleName, NULL);
}
}
if (!NT_SUCCESS(Status))
{
if (PrivateEntry)
{
if (pLoadFont->PrivateEntry == PrivateEntry)
{
pLoadFont->PrivateEntry = NULL;
}
else
{
RemoveEntryList(&PrivateEntry->ListEntry);
}
ExFreePoolWithTag(PrivateEntry, TAG_FONT);
}
if (FontGDI->Filename)
ExFreePoolWithTag(FontGDI->Filename, GDITAG_PFF);
EngFreeMem(FontGDI);
SharedFace_Release(SharedFace);
ExFreePoolWithTag(Entry, TAG_FONT);
return 0;
}
os2_version = 0;
IntLockFreeType();
pOS2 = (TT_OS2 *)FT_Get_Sfnt_Table(Face, FT_SFNT_OS2);
if (pOS2)
{
os2_version = pOS2->version;
os2_ulCodePageRange1 = pOS2->ulCodePageRange1;
os2_usWeightClass = pOS2->usWeightClass;
}
IntUnLockFreeType();
if (pOS2 && os2_version >= 1)
{
/* get charset and weight from OS/2 header */
/* Make sure we do not use this pointer anymore */
pOS2 = NULL;
for (BitIndex = 0; BitIndex < MAXTCIINDEX; ++BitIndex)
{
if (os2_ulCodePageRange1 & (1 << BitIndex))
{
if (g_FontTci[BitIndex].ciCharset == DEFAULT_CHARSET)
continue;
if ((CharSetIndex == -1 && CharSetCount == 0) ||
CharSetIndex == CharSetCount)
{
FontGDI->CharSet = g_FontTci[BitIndex].ciCharset;
}
++CharSetCount;
}
}
/* set actual weight */
FontGDI->OriginalWeight = os2_usWeightClass;
}
else
{
/* get charset from WinFNT header */
IntLockFreeType();
Error = FT_Get_WinFNT_Header(Face, &WinFNT);
if (!Error)
{
FontGDI->CharSet = WinFNT.charset;
}
IntUnLockFreeType();
}
++FaceCount;
DPRINT("Font loaded: %s (%s)\n",
Face->family_name ? Face->family_name : "<NULL>",
Face->style_name ? Face->style_name : "<NULL>");
DPRINT("Num glyphs: %d\n", Face->num_glyphs);
DPRINT("CharSet: %d\n", FontGDI->CharSet);
IntLockFreeType();
IntRequestFontSize(NULL, FontGDI, 0, 0);
IntUnLockFreeType();
/* Add this font resource to the font table */
Entry->Font = FontGDI;
Entry->NotEnum = (Characteristics & FR_NOT_ENUM);
if (Characteristics & FR_PRIVATE)
{
/* private font */
PPROCESSINFO Win32Process = PsGetCurrentProcessWin32Process();
IntLockProcessPrivateFonts(Win32Process);
InsertTailList(&Win32Process->PrivateFontListHead, &Entry->ListEntry);
IntUnLockProcessPrivateFonts(Win32Process);
}
else
{
/* global font */
IntLockGlobalFonts();
InsertTailList(&g_FontListHead, &Entry->ListEntry);
IntUnLockGlobalFonts();
}
if (FontIndex == -1)
{
if (FT_IS_SFNT(Face))
{
TT_Face TrueType = (TT_Face)Face;
if (TrueType->ttc_header.count > 1)
{
FT_Long i;
for (i = 1; i < TrueType->ttc_header.count; ++i)
{
FaceCount += IntGdiLoadFontsFromMemory(pLoadFont, NULL, i, -1);
}
}
}
FontIndex = 0;
}
if (CharSetIndex == -1)
{
INT i;
USHORT NameLength = Entry->FaceName.Length;
if (Entry->StyleName.Length)
NameLength += Entry->StyleName.Length + sizeof(WCHAR);
if (pLoadFont->RegValueName.Length == 0)
{
pValueName->Length = 0;
pValueName->MaximumLength = NameLength + sizeof(WCHAR);
pValueName->Buffer = ExAllocatePoolWithTag(PagedPool,
pValueName->MaximumLength,
TAG_USTR);
pValueName->Buffer[0] = UNICODE_NULL;
RtlAppendUnicodeStringToString(pValueName, &Entry->FaceName);
}
else
{
UNICODE_STRING NewString;
USHORT Length = pValueName->Length + 3 * sizeof(WCHAR) + NameLength;
NewString.Length = 0;
NewString.MaximumLength = Length + sizeof(WCHAR);
NewString.Buffer = ExAllocatePoolWithTag(PagedPool,
NewString.MaximumLength,
TAG_USTR);
NewString.Buffer[0] = UNICODE_NULL;
RtlAppendUnicodeStringToString(&NewString, pValueName);
RtlAppendUnicodeToString(&NewString, L" & ");
RtlAppendUnicodeStringToString(&NewString, &Entry->FaceName);
RtlFreeUnicodeString(pValueName);
*pValueName = NewString;
}
if (Entry->StyleName.Length)
{
RtlAppendUnicodeToString(pValueName, L" ");
RtlAppendUnicodeStringToString(pValueName, &Entry->StyleName);
}
for (i = 1; i < CharSetCount; ++i)
{
/* Do not count charsets towards 'faces' loaded */
IntGdiLoadFontsFromMemory(pLoadFont, SharedFace, FontIndex, i);
}
}
return FaceCount; /* number of loaded faces */
}
static LPCWSTR FASTCALL
NameFromCharSet(BYTE CharSet)
{
switch (CharSet)
{
case ANSI_CHARSET: return L"ANSI";
case DEFAULT_CHARSET: return L"Default";
case SYMBOL_CHARSET: return L"Symbol";
case SHIFTJIS_CHARSET: return L"Shift_JIS";
case HANGUL_CHARSET: return L"Hangul";
case GB2312_CHARSET: return L"GB 2312";
case CHINESEBIG5_CHARSET: return L"Chinese Big5";
case OEM_CHARSET: return L"OEM";
case JOHAB_CHARSET: return L"Johab";
case HEBREW_CHARSET: return L"Hebrew";
case ARABIC_CHARSET: return L"Arabic";
case GREEK_CHARSET: return L"Greek";
case TURKISH_CHARSET: return L"Turkish";
case VIETNAMESE_CHARSET: return L"Vietnamese";
case THAI_CHARSET: return L"Thai";
case EASTEUROPE_CHARSET: return L"Eastern European";
case RUSSIAN_CHARSET: return L"Russian";
case MAC_CHARSET: return L"Mac";
case BALTIC_CHARSET: return L"Baltic";
default: return L"Unknown";
}
}
/*
* IntGdiAddFontResource
*
* Adds the font resource from the specified file to the system.
*/
INT FASTCALL
IntGdiAddFontResourceEx(PUNICODE_STRING FileName, DWORD Characteristics,
DWORD dwFlags)
{
NTSTATUS Status;
HANDLE FileHandle;
PVOID Buffer = NULL;
IO_STATUS_BLOCK Iosb;
PVOID SectionObject;
SIZE_T ViewSize = 0, Length;
LARGE_INTEGER SectionSize;
OBJECT_ATTRIBUTES ObjectAttributes;
GDI_LOAD_FONT LoadFont;
INT FontCount;
HANDLE KeyHandle;
UNICODE_STRING PathName;
LPWSTR pszBuffer;
PFILE_OBJECT FileObject;
static const UNICODE_STRING TrueTypePostfix = RTL_CONSTANT_STRING(L" (TrueType)");
static const UNICODE_STRING DosPathPrefix = RTL_CONSTANT_STRING(L"\\??\\");
/* Build PathName */
if (dwFlags & AFRX_DOS_DEVICE_PATH)
{
Length = DosPathPrefix.Length + FileName->Length + sizeof(UNICODE_NULL);
pszBuffer = ExAllocatePoolWithTag(PagedPool, Length, TAG_USTR);
if (!pszBuffer)
return 0; /* failure */
RtlInitEmptyUnicodeString(&PathName, pszBuffer, Length);
RtlAppendUnicodeStringToString(&PathName, &DosPathPrefix);
RtlAppendUnicodeStringToString(&PathName, FileName);
}
else
{
Status = DuplicateUnicodeString(FileName, &PathName);
if (!NT_SUCCESS(Status))
return 0; /* failure */
}
/* Open the font file */
InitializeObjectAttributes(&ObjectAttributes, &PathName,
OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL);
Status = ZwOpenFile(
&FileHandle,
FILE_GENERIC_READ | SYNCHRONIZE,
&ObjectAttributes,
&Iosb,
FILE_SHARE_READ,
FILE_SYNCHRONOUS_IO_NONALERT);
if (!NT_SUCCESS(Status))
{
DPRINT1("Could not load font file: %wZ\n", &PathName);
RtlFreeUnicodeString(&PathName);
return 0;
}
Status = ObReferenceObjectByHandle(FileHandle, FILE_READ_DATA, NULL,
KernelMode, (PVOID*)&FileObject, NULL);
if (!NT_SUCCESS(Status))
{
DPRINT1("ObReferenceObjectByHandle failed.\n");
ZwClose(FileHandle);
RtlFreeUnicodeString(&PathName);
return 0;
}
SectionSize.QuadPart = 0LL;
Status = MmCreateSection(&SectionObject,
STANDARD_RIGHTS_REQUIRED | SECTION_QUERY | SECTION_MAP_READ,
NULL, &SectionSize, PAGE_READONLY,
SEC_COMMIT, FileHandle, FileObject);
if (!NT_SUCCESS(Status))
{
DPRINT1("Could not map file: %wZ\n", &PathName);
ZwClose(FileHandle);
ObDereferenceObject(FileObject);
RtlFreeUnicodeString(&PathName);
return 0;
}
ZwClose(FileHandle);
Status = MmMapViewInSystemSpace(SectionObject, &Buffer, &ViewSize);
if (!NT_SUCCESS(Status))
{
DPRINT1("Could not map file: %wZ\n", &PathName);
ObDereferenceObject(SectionObject);
ObDereferenceObject(FileObject);
RtlFreeUnicodeString(&PathName);
return 0;
}
LoadFont.pFileName = &PathName;
LoadFont.Memory = SharedMem_Create(Buffer, ViewSize, TRUE);
LoadFont.Characteristics = Characteristics;
RtlInitUnicodeString(&LoadFont.RegValueName, NULL);
LoadFont.IsTrueType = FALSE;
LoadFont.CharSet = DEFAULT_CHARSET;
LoadFont.PrivateEntry = NULL;
FontCount = IntGdiLoadFontsFromMemory(&LoadFont, NULL, -1, -1);
/* Release our copy */
IntLockFreeType();
SharedMem_Release(LoadFont.Memory);
IntUnLockFreeType();
ObDereferenceObject(SectionObject);
ObDereferenceObject(FileObject);
/* Save the loaded font name into the registry */
if (FontCount > 0 && (dwFlags & AFRX_WRITE_REGISTRY))
{
UNICODE_STRING NewString;
SIZE_T Length;
PWCHAR pszBuffer;
LPCWSTR CharSetName;
if (LoadFont.IsTrueType)
{
/* Append " (TrueType)" */
Length = LoadFont.RegValueName.Length + TrueTypePostfix.Length + sizeof(UNICODE_NULL);
pszBuffer = ExAllocatePoolWithTag(PagedPool, Length, TAG_USTR);
if (pszBuffer)
{
RtlInitEmptyUnicodeString(&NewString, pszBuffer, Length);
NewString.Buffer[0] = UNICODE_NULL;
RtlAppendUnicodeStringToString(&NewString, &LoadFont.RegValueName);
RtlAppendUnicodeStringToString(&NewString, &TrueTypePostfix);
RtlFreeUnicodeString(&LoadFont.RegValueName);
LoadFont.RegValueName = NewString;
}
else
{
// FIXME!
}
}
else if (LoadFont.CharSet != DEFAULT_CHARSET)
{
/* Append " (CharSetName)" */
CharSetName = NameFromCharSet(LoadFont.CharSet);
Length = LoadFont.RegValueName.Length +
(wcslen(CharSetName) + 3) * sizeof(WCHAR) +
sizeof(UNICODE_NULL);
pszBuffer = ExAllocatePoolWithTag(PagedPool, Length, TAG_USTR);
if (pszBuffer)
{
RtlInitEmptyUnicodeString(&NewString, pszBuffer, Length);
NewString.Buffer[0] = UNICODE_NULL;
RtlAppendUnicodeStringToString(&NewString, &LoadFont.RegValueName);
RtlAppendUnicodeToString(&NewString, L" (");
RtlAppendUnicodeToString(&NewString, CharSetName);
RtlAppendUnicodeToString(&NewString, L")");
RtlFreeUnicodeString(&LoadFont.RegValueName);
LoadFont.RegValueName = NewString;
}
else
{
// FIXME!
}
}
InitializeObjectAttributes(&ObjectAttributes, &g_FontRegPath,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL, NULL);
Status = ZwOpenKey(&KeyHandle, KEY_WRITE, &ObjectAttributes);
if (NT_SUCCESS(Status))
{
SIZE_T DataSize;
LPWSTR pFileName;
if (dwFlags & AFRX_ALTERNATIVE_PATH)
{
pFileName = PathName.Buffer;
}
else
{
pFileName = wcsrchr(PathName.Buffer, L'\\');
}
if (pFileName)
{
if (!(dwFlags & AFRX_ALTERNATIVE_PATH))
{
pFileName++;
}
DataSize = (wcslen(pFileName) + 1) * sizeof(WCHAR);
ZwSetValueKey(KeyHandle, &LoadFont.RegValueName, 0, REG_SZ,
pFileName, DataSize);
}
ZwClose(KeyHandle);
}
}
RtlFreeUnicodeString(&LoadFont.RegValueName);
RtlFreeUnicodeString(&PathName);
return FontCount;
}
INT FASTCALL
IntGdiAddFontResource(PUNICODE_STRING FileName, DWORD Characteristics)
{
return IntGdiAddFontResourceEx(FileName, Characteristics, 0);
}
/* Borrowed from shlwapi!PathIsRelativeW */
BOOL WINAPI PathIsRelativeW(LPCWSTR lpszPath)
{
if (!lpszPath || !*lpszPath)
return TRUE;
if (*lpszPath == L'\\' || (*lpszPath && lpszPath[1] == L':'))
return FALSE;
return TRUE;
}
BOOL FASTCALL
IntLoadFontsInRegistry(VOID)
{
NTSTATUS Status;
HANDLE KeyHandle;
OBJECT_ATTRIBUTES ObjectAttributes;
KEY_FULL_INFORMATION KeyFullInfo;
ULONG i, Length;
UNICODE_STRING FontTitleW, FileNameW;
SIZE_T InfoSize;
LPBYTE InfoBuffer;
PKEY_VALUE_FULL_INFORMATION pInfo;
LPWSTR pchPath;
BOOLEAN Success;
WCHAR szPath[MAX_PATH];
INT nFontCount = 0;
DWORD dwFlags;
/* open registry key */
InitializeObjectAttributes(&ObjectAttributes, &g_FontRegPath,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL, NULL);
Status = ZwOpenKey(&KeyHandle, KEY_READ, &ObjectAttributes);
if (!NT_SUCCESS(Status))
{
DPRINT1("ZwOpenKey failed: 0x%08X\n", Status);
return FALSE; /* failure */
}
/* query count of values */
Status = ZwQueryKey(KeyHandle, KeyFullInformation,
&KeyFullInfo, sizeof(KeyFullInfo), &Length);
if (!NT_SUCCESS(Status))
{
DPRINT1("ZwQueryKey failed: 0x%08X\n", Status);
ZwClose(KeyHandle);
return FALSE; /* failure */
}
/* allocate buffer */
InfoSize = (MAX_PATH + 256) * sizeof(WCHAR);
InfoBuffer = ExAllocatePoolWithTag(PagedPool, InfoSize, TAG_FONT);
if (!InfoBuffer)
{
DPRINT1("ExAllocatePoolWithTag failed\n");
ZwClose(KeyHandle);
return FALSE;
}
/* for each value */
for (i = 0; i < KeyFullInfo.Values; ++i)
{
/* get value name */
Status = ZwEnumerateValueKey(KeyHandle, i, KeyValueFullInformation,
InfoBuffer, InfoSize, &Length);
if (Status == STATUS_BUFFER_OVERFLOW || Status == STATUS_BUFFER_TOO_SMALL)
{
/* too short buffer */
ExFreePoolWithTag(InfoBuffer, TAG_FONT);
InfoSize *= 2;
InfoBuffer = ExAllocatePoolWithTag(PagedPool, InfoSize, TAG_FONT);
if (!InfoBuffer)
{
DPRINT1("ExAllocatePoolWithTag failed\n");
break;
}
/* try again */
Status = ZwEnumerateValueKey(KeyHandle, i, KeyValueFullInformation,
InfoBuffer, InfoSize, &Length);
}
if (!NT_SUCCESS(Status))
{
DPRINT1("ZwEnumerateValueKey failed: 0x%08X\n", Status);
break; /* failure */
}
/* create FontTitleW string */
pInfo = (PKEY_VALUE_FULL_INFORMATION)InfoBuffer;
Length = pInfo->NameLength / sizeof(WCHAR);
pInfo->Name[Length] = UNICODE_NULL; /* truncate */
Success = RtlCreateUnicodeString(&FontTitleW, pInfo->Name);
if (!Success)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
DPRINT1("RtlCreateUnicodeString failed\n");
break; /* failure */
}
/* query value */
Status = ZwQueryValueKey(KeyHandle, &FontTitleW, KeyValueFullInformation,
InfoBuffer, InfoSize, &Length);
if (Status == STATUS_BUFFER_OVERFLOW || Status == STATUS_BUFFER_TOO_SMALL)
{
/* too short buffer */
ExFreePoolWithTag(InfoBuffer, TAG_FONT);
InfoSize *= 2;
InfoBuffer = ExAllocatePoolWithTag(PagedPool, InfoSize, TAG_FONT);
if (!InfoBuffer)
{
DPRINT1("ExAllocatePoolWithTag failed\n");
break;
}
/* try again */
Status = ZwQueryValueKey(KeyHandle, &FontTitleW, KeyValueFullInformation,
InfoBuffer, InfoSize, &Length);
}
pInfo = (PKEY_VALUE_FULL_INFORMATION)InfoBuffer;
if (!NT_SUCCESS(Status) || !pInfo->DataLength)
{
DPRINT1("ZwQueryValueKey failed: 0x%08X\n", Status);
RtlFreeUnicodeString(&FontTitleW);
break; /* failure */
}
/* Build pchPath */
pchPath = (LPWSTR)((PUCHAR)pInfo + pInfo->DataOffset);
Length = pInfo->DataLength / sizeof(WCHAR);
pchPath[Length] = UNICODE_NULL; /* truncate */
/* Load font(s) without writing registry */
if (PathIsRelativeW(pchPath))
{
dwFlags = 0;
Status = RtlStringCbPrintfW(szPath, sizeof(szPath),
L"\\SystemRoot\\Fonts\\%s", pchPath);
}
else
{
dwFlags = AFRX_ALTERNATIVE_PATH | AFRX_DOS_DEVICE_PATH;
Status = RtlStringCbCopyW(szPath, sizeof(szPath), pchPath);
}
if (NT_SUCCESS(Status))
{
RtlCreateUnicodeString(&FileNameW, szPath);
nFontCount += IntGdiAddFontResourceEx(&FileNameW, 0, dwFlags);
RtlFreeUnicodeString(&FileNameW);
}
RtlFreeUnicodeString(&FontTitleW);
}
/* close now */
ZwClose(KeyHandle);
/* free memory block */
if (InfoBuffer)
{
ExFreePoolWithTag(InfoBuffer, TAG_FONT);
}
return (KeyFullInfo.Values != 0 && nFontCount != 0);
}
HANDLE FASTCALL
IntGdiAddFontMemResource(PVOID Buffer, DWORD dwSize, PDWORD pNumAdded)
{
HANDLE Ret = NULL;
GDI_LOAD_FONT LoadFont;
PFONT_ENTRY_COLL_MEM EntryCollection;
INT FaceCount;
PVOID BufferCopy = ExAllocatePoolWithTag(PagedPool, dwSize, TAG_FONT);
if (!BufferCopy)
{
*pNumAdded = 0;
return NULL;
}
RtlCopyMemory(BufferCopy, Buffer, dwSize);
LoadFont.pFileName = NULL;
LoadFont.Memory = SharedMem_Create(BufferCopy, dwSize, FALSE);
LoadFont.Characteristics = FR_PRIVATE | FR_NOT_ENUM;
RtlInitUnicodeString(&LoadFont.RegValueName, NULL);
LoadFont.IsTrueType = FALSE;
LoadFont.PrivateEntry = NULL;
FaceCount = IntGdiLoadFontsFromMemory(&LoadFont, NULL, -1, -1);
RtlFreeUnicodeString(&LoadFont.RegValueName);
/* Release our copy */
IntLockFreeType();
SharedMem_Release(LoadFont.Memory);
IntUnLockFreeType();
if (FaceCount > 0)
{
EntryCollection = ExAllocatePoolWithTag(PagedPool, sizeof(FONT_ENTRY_COLL_MEM), TAG_FONT);
if (EntryCollection)
{
PPROCESSINFO Win32Process = PsGetCurrentProcessWin32Process();
EntryCollection->Entry = LoadFont.PrivateEntry;
IntLockProcessPrivateFonts(Win32Process);
EntryCollection->Handle = ULongToHandle(++Win32Process->PrivateMemFontHandleCount);
InsertTailList(&Win32Process->PrivateMemFontListHead, &EntryCollection->ListEntry);
IntUnLockProcessPrivateFonts(Win32Process);
Ret = EntryCollection->Handle;
}
}
*pNumAdded = FaceCount;
return Ret;
}
// FIXME: Add RemoveFontResource
VOID FASTCALL
IntGdiCleanupMemEntry(PFONT_ENTRY_MEM Head)
{
PLIST_ENTRY Entry;
PFONT_ENTRY_MEM FontEntry;
while (!IsListEmpty(&Head->ListEntry))
{
Entry = RemoveHeadList(&Head->ListEntry);
FontEntry = CONTAINING_RECORD(Entry, FONT_ENTRY_MEM, ListEntry);
CleanupFontEntry(FontEntry->Entry);
ExFreePoolWithTag(FontEntry, TAG_FONT);
}
CleanupFontEntry(Head->Entry);
ExFreePoolWithTag(Head, TAG_FONT);
}
static VOID FASTCALL
UnlinkFontMemCollection(PFONT_ENTRY_COLL_MEM Collection)
{
PFONT_ENTRY_MEM FontMemEntry = Collection->Entry;
PLIST_ENTRY ListEntry;
RemoveEntryList(&Collection->ListEntry);
do {
/* Also unlink the FONT_ENTRY stuff from the PrivateFontListHead */
RemoveEntryList(&FontMemEntry->Entry->ListEntry);
ListEntry = FontMemEntry->ListEntry.Flink;
FontMemEntry = CONTAINING_RECORD(ListEntry, FONT_ENTRY_MEM, ListEntry);
} while (FontMemEntry != Collection->Entry);
}
BOOL FASTCALL
IntGdiRemoveFontMemResource(HANDLE hMMFont)
{
PLIST_ENTRY Entry;
PFONT_ENTRY_COLL_MEM CurrentEntry;
PFONT_ENTRY_COLL_MEM EntryCollection = NULL;
PPROCESSINFO Win32Process = PsGetCurrentProcessWin32Process();
IntLockProcessPrivateFonts(Win32Process);
for (Entry = Win32Process->PrivateMemFontListHead.Flink;
Entry != &Win32Process->PrivateMemFontListHead;
Entry = Entry->Flink)
{
CurrentEntry = CONTAINING_RECORD(Entry, FONT_ENTRY_COLL_MEM, ListEntry);
if (CurrentEntry->Handle == hMMFont)
{
EntryCollection = CurrentEntry;
UnlinkFontMemCollection(CurrentEntry);
break;
}
}
IntUnLockProcessPrivateFonts(Win32Process);
if (EntryCollection)
{
IntGdiCleanupMemEntry(EntryCollection->Entry);
ExFreePoolWithTag(EntryCollection, TAG_FONT);
return TRUE;
}
return FALSE;
}
VOID FASTCALL
IntGdiCleanupPrivateFontsForProcess(VOID)
{
PPROCESSINFO Win32Process = PsGetCurrentProcessWin32Process();
PLIST_ENTRY Entry;
PFONT_ENTRY_COLL_MEM EntryCollection;
DPRINT("IntGdiCleanupPrivateFontsForProcess()\n");
do {
Entry = NULL;
EntryCollection = NULL;
IntLockProcessPrivateFonts(Win32Process);
if (!IsListEmpty(&Win32Process->PrivateMemFontListHead))
{
Entry = Win32Process->PrivateMemFontListHead.Flink;
EntryCollection = CONTAINING_RECORD(Entry, FONT_ENTRY_COLL_MEM, ListEntry);
UnlinkFontMemCollection(EntryCollection);
}
IntUnLockProcessPrivateFonts(Win32Process);
if (EntryCollection)
{
IntGdiCleanupMemEntry(EntryCollection->Entry);
ExFreePoolWithTag(EntryCollection, TAG_FONT);
}
else
{
/* No Mem fonts anymore, see if we have any other private fonts left */
Entry = NULL;
IntLockProcessPrivateFonts(Win32Process);
if (!IsListEmpty(&Win32Process->PrivateFontListHead))
{
Entry = RemoveHeadList(&Win32Process->PrivateFontListHead);
}
IntUnLockProcessPrivateFonts(Win32Process);
if (Entry)
{
CleanupFontEntry(CONTAINING_RECORD(Entry, FONT_ENTRY, ListEntry));
}
}
} while (Entry);
}
BOOL FASTCALL
IntIsFontRenderingEnabled(VOID)
{
return (gpsi->BitsPixel > 8) && g_RenderingEnabled;
}
VOID FASTCALL
IntEnableFontRendering(BOOL Enable)
{
g_RenderingEnabled = Enable;
}
FT_Render_Mode FASTCALL
IntGetFontRenderMode(LOGFONTW *logfont)
{
switch (logfont->lfQuality)
{
case ANTIALIASED_QUALITY:
break;
case NONANTIALIASED_QUALITY:
return FT_RENDER_MODE_MONO;
case DRAFT_QUALITY:
return FT_RENDER_MODE_LIGHT;
case CLEARTYPE_QUALITY:
if (!gspv.bFontSmoothing)
break;
if (!gspv.uiFontSmoothingType)
break;
return FT_RENDER_MODE_LCD;
}
return FT_RENDER_MODE_NORMAL;
}
NTSTATUS FASTCALL
TextIntCreateFontIndirect(CONST LPLOGFONTW lf, HFONT *NewFont)
{
PLFONT plfont;
LOGFONTW *plf;
ASSERT(lf);
plfont = LFONT_AllocFontWithHandle();
if (!plfont)
{
return STATUS_NO_MEMORY;
}
ExInitializePushLock(&plfont->lock);
*NewFont = plfont->BaseObject.hHmgr;
plf = &plfont->logfont.elfEnumLogfontEx.elfLogFont;
RtlCopyMemory(plf, lf, sizeof(LOGFONTW));
if (lf->lfEscapement != lf->lfOrientation)
{
/* This should really depend on whether GM_ADVANCED is set */
plf->lfOrientation = plf->lfEscapement;
}
LFONT_UnlockFont(plfont);
return STATUS_SUCCESS;
}
/*************************************************************************
* TranslateCharsetInfo
*
* Fills a CHARSETINFO structure for a character set, code page, or
* font. This allows making the correspondance between different labelings
* (character set, Windows, ANSI, and OEM codepages, and Unicode ranges)
* of the same encoding.
*
* Only one codepage will be set in Cs->fs. If TCI_SRCFONTSIG is used,
* only one codepage should be set in *Src.
*
* RETURNS
* TRUE on success, FALSE on failure.
*
*/
static BOOLEAN APIENTRY
IntTranslateCharsetInfo(PDWORD Src, /* [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 Cs, /* [out] structure to receive charset information */
DWORD Flags /* [in] determines interpretation of lpSrc */)
{
int Index = 0;
switch (Flags)
{
case TCI_SRCFONTSIG:
while (Index < MAXTCIINDEX && 0 == (*Src >> Index & 0x0001))
{
Index++;
}
break;
case TCI_SRCCODEPAGE:
while (Index < MAXTCIINDEX && *Src != g_FontTci[Index].ciACP)
{
Index++;
}
break;
case TCI_SRCCHARSET:
while (Index < MAXTCIINDEX && *Src != g_FontTci[Index].ciCharset)
{
Index++;
}
break;
case TCI_SRCLOCALE:
UNIMPLEMENTED;
return FALSE;
default:
return FALSE;
}
if (Index >= MAXTCIINDEX || DEFAULT_CHARSET == g_FontTci[Index].ciCharset)
{
return FALSE;
}
RtlCopyMemory(Cs, &g_FontTci[Index], sizeof(CHARSETINFO));
return TRUE;
}
static BOOL face_has_symbol_charmap(FT_Face ft_face)
{
int i;
for(i = 0; i < ft_face->num_charmaps; i++)
{
if (ft_face->charmaps[i]->platform_id == TT_PLATFORM_MICROSOFT &&
ft_face->charmaps[i]->encoding == FT_ENCODING_MS_SYMBOL)
{
return TRUE;
}
}
return FALSE;
}
static void FASTCALL
FillTM(TEXTMETRICW *TM, PFONTGDI FontGDI,
TT_OS2 *pOS2, TT_HoriHeader *pHori,
FT_WinFNT_HeaderRec *pFNT)
{
FT_Fixed XScale, YScale;
int Ascent, Descent;
FT_Face Face = FontGDI->SharedFace->Face;
ASSERT_FREETYPE_LOCK_HELD();
XScale = Face->size->metrics.x_scale;
YScale = Face->size->metrics.y_scale;
if (pFNT)
{
TM->tmHeight = pFNT->pixel_height;
TM->tmAscent = pFNT->ascent;
TM->tmDescent = TM->tmHeight - TM->tmAscent;
TM->tmInternalLeading = pFNT->internal_leading;
TM->tmExternalLeading = pFNT->external_leading;
TM->tmAveCharWidth = pFNT->avg_width;
TM->tmMaxCharWidth = pFNT->max_width;
TM->tmOverhang = 0;
TM->tmDigitizedAspectX = pFNT->horizontal_resolution;
TM->tmDigitizedAspectY = pFNT->vertical_resolution;
TM->tmFirstChar = pFNT->first_char;
TM->tmLastChar = pFNT->last_char;
TM->tmDefaultChar = pFNT->default_char + pFNT->first_char;
TM->tmBreakChar = pFNT->break_char + pFNT->first_char;
TM->tmPitchAndFamily = pFNT->pitch_and_family;
TM->tmWeight = FontGDI->RequestWeight;
TM->tmItalic = FontGDI->RequestItalic;
TM->tmUnderlined = FontGDI->RequestUnderline;
TM->tmStruckOut = FontGDI->RequestStrikeOut;
TM->tmCharSet = FontGDI->CharSet;
return;
}
ASSERT(pOS2);
if (!pOS2)
return;
if ((FT_Short)pOS2->usWinAscent + (FT_Short)pOS2->usWinDescent == 0)
{
Ascent = pHori->Ascender;
Descent = -pHori->Descender;
}
else
{
Ascent = (FT_Short)pOS2->usWinAscent;
Descent = (FT_Short)pOS2->usWinDescent;
}
TM->tmAscent = FontGDI->tmAscent;
TM->tmDescent = FontGDI->tmDescent;
TM->tmHeight = TM->tmAscent + TM->tmDescent;
TM->tmInternalLeading = FontGDI->tmInternalLeading;
/* MSDN says:
* el = MAX(0, LineGap - ((WinAscent + WinDescent) - (Ascender - Descender)))
*/
TM->tmExternalLeading = max(0, (FT_MulFix(pHori->Line_Gap
- ((Ascent + Descent)
- (pHori->Ascender - pHori->Descender)),
YScale) + 32) >> 6);
TM->tmAveCharWidth = (FT_MulFix(pOS2->xAvgCharWidth, XScale) + 32) >> 6;
if (TM->tmAveCharWidth == 0)
{
TM->tmAveCharWidth = 1;
}
/* Correct forumla to get the maxcharwidth from unicode and ansi font */
TM->tmMaxCharWidth = (FT_MulFix(Face->max_advance_width, XScale) + 32) >> 6;
if (FontGDI->OriginalWeight != FW_DONTCARE &&
FontGDI->OriginalWeight != FW_NORMAL)
{
TM->tmWeight = FontGDI->OriginalWeight;
}
else
{
TM->tmWeight = FontGDI->RequestWeight;
}
TM->tmOverhang = 0;
TM->tmDigitizedAspectX = 96;
TM->tmDigitizedAspectY = 96;
if (face_has_symbol_charmap(Face) ||
(pOS2->usFirstCharIndex >= 0xf000 && pOS2->usFirstCharIndex < 0xf100))
{
USHORT cpOEM, cpAnsi;
EngGetCurrentCodePage(&cpOEM, &cpAnsi);
TM->tmFirstChar = 0;
switch(cpAnsi)
{
case 1257: /* Baltic */
TM->tmLastChar = 0xf8fd;
break;
default:
TM->tmLastChar = 0xf0ff;
}
TM->tmBreakChar = 0x20;
TM->tmDefaultChar = 0x1f;
}
else
{
TM->tmFirstChar = pOS2->usFirstCharIndex; /* Should be the first char in the cmap */
TM->tmLastChar = pOS2->usLastCharIndex; /* Should be min(cmap_last, os2_last) */
if(pOS2->usFirstCharIndex <= 1)
TM->tmBreakChar = pOS2->usFirstCharIndex + 2;
else if (pOS2->usFirstCharIndex > 0xff)
TM->tmBreakChar = 0x20;
else
TM->tmBreakChar = pOS2->usFirstCharIndex;
TM->tmDefaultChar = TM->tmBreakChar - 1;
}
if (FontGDI->OriginalItalic || FontGDI->RequestItalic)
{
TM->tmItalic = 0xFF;
}
else
{
TM->tmItalic = 0;
}
TM->tmUnderlined = (FontGDI->RequestUnderline ? 0xFF : 0);
TM->tmStruckOut = (FontGDI->RequestStrikeOut ? 0xFF : 0);
if (!FT_IS_FIXED_WIDTH(Face))
{
switch (pOS2->panose[PAN_PROPORTION_INDEX])
{
case PAN_PROP_MONOSPACED:
TM->tmPitchAndFamily = 0;
break;
default:
TM->tmPitchAndFamily = _TMPF_VARIABLE_PITCH;
break;
}
}
else
{
TM->tmPitchAndFamily = 0;
}
switch (pOS2->panose[PAN_FAMILYTYPE_INDEX])
{
case PAN_FAMILY_SCRIPT:
TM->tmPitchAndFamily |= FF_SCRIPT;
break;
case PAN_FAMILY_DECORATIVE:
TM->tmPitchAndFamily |= FF_DECORATIVE;
break;
case PAN_ANY:
case PAN_NO_FIT:
case PAN_FAMILY_TEXT_DISPLAY:
case PAN_FAMILY_PICTORIAL: /* Symbol fonts get treated as if they were text */
/* Which is clearly not what the panose spec says. */
if (TM->tmPitchAndFamily == 0) /* Fixed */
{
TM->tmPitchAndFamily = FF_MODERN;
}
else
{
switch (pOS2->panose[PAN_SERIFSTYLE_INDEX])
{
case PAN_ANY:
case PAN_NO_FIT:
default:
TM->tmPitchAndFamily |= FF_DONTCARE;
break;
case PAN_SERIF_COVE:
case PAN_SERIF_OBTUSE_COVE:
case PAN_SERIF_SQUARE_COVE:
case PAN_SERIF_OBTUSE_SQUARE_COVE:
case PAN_SERIF_SQUARE:
case PAN_SERIF_THIN:
case PAN_SERIF_BONE:
case PAN_SERIF_EXAGGERATED:
case PAN_SERIF_TRIANGLE:
TM->tmPitchAndFamily |= FF_ROMAN;
break;
case PAN_SERIF_NORMAL_SANS:
case PAN_SERIF_OBTUSE_SANS:
case PAN_SERIF_PERP_SANS:
case PAN_SERIF_FLARED:
case PAN_SERIF_ROUNDED:
TM->tmPitchAndFamily |= FF_SWISS;
break;
}
}
break;
default:
TM->tmPitchAndFamily |= FF_DONTCARE;
}
if (FT_IS_SCALABLE(Face))
{
TM->tmPitchAndFamily |= TMPF_VECTOR;
}
if (FT_IS_SFNT(Face))
{
TM->tmPitchAndFamily |= TMPF_TRUETYPE;
}
TM->tmCharSet = FontGDI->CharSet;
}
static NTSTATUS
IntGetFontLocalizedName(PUNICODE_STRING pNameW, PSHARED_FACE SharedFace,
FT_UShort NameID, FT_UShort LangID);
typedef struct FONT_NAMES
{
UNICODE_STRING FamilyNameW; /* family name (TT_NAME_ID_FONT_FAMILY) */
UNICODE_STRING FaceNameW; /* face name (TT_NAME_ID_FULL_NAME) */
UNICODE_STRING StyleNameW; /* style name (TT_NAME_ID_FONT_SUBFAMILY) */
UNICODE_STRING FullNameW; /* unique name (TT_NAME_ID_UNIQUE_ID) */
ULONG OtmSize; /* size of OUTLINETEXTMETRICW with extra data */
} FONT_NAMES, *LPFONT_NAMES;
static __inline void FASTCALL
IntInitFontNames(FONT_NAMES *Names, PSHARED_FACE SharedFace)
{
ULONG OtmSize;
RtlInitUnicodeString(&Names->FamilyNameW, NULL);
RtlInitUnicodeString(&Names->FaceNameW, NULL);
RtlInitUnicodeString(&Names->StyleNameW, NULL);
RtlInitUnicodeString(&Names->FullNameW, NULL);
/* family name */
IntGetFontLocalizedName(&Names->FamilyNameW, SharedFace, TT_NAME_ID_FONT_FAMILY, gusLanguageID);
/* face name */
IntGetFontLocalizedName(&Names->FaceNameW, SharedFace, TT_NAME_ID_FULL_NAME, gusLanguageID);
/* style name */
IntGetFontLocalizedName(&Names->StyleNameW, SharedFace, TT_NAME_ID_FONT_SUBFAMILY, gusLanguageID);
/* unique name (full name) */
IntGetFontLocalizedName(&Names->FullNameW, SharedFace, TT_NAME_ID_UNIQUE_ID, gusLanguageID);
/* Calculate the size of OUTLINETEXTMETRICW with extra data */
OtmSize = sizeof(OUTLINETEXTMETRICW) +
Names->FamilyNameW.Length + sizeof(UNICODE_NULL) +
Names->FaceNameW.Length + sizeof(UNICODE_NULL) +
Names->StyleNameW.Length + sizeof(UNICODE_NULL) +
Names->FullNameW.Length + sizeof(UNICODE_NULL);
Names->OtmSize = OtmSize;
}
static __inline SIZE_T FASTCALL
IntStoreName(const UNICODE_STRING *pName, BYTE *pb)
{
RtlCopyMemory(pb, pName->Buffer, pName->Length);
*(WCHAR *)&pb[pName->Length] = UNICODE_NULL;
return pName->Length + sizeof(UNICODE_NULL);
}
static __inline BYTE *FASTCALL
IntStoreFontNames(const FONT_NAMES *Names, OUTLINETEXTMETRICW *Otm)
{
BYTE *pb = (BYTE *)Otm + sizeof(OUTLINETEXTMETRICW);
/* family name */
Otm->otmpFamilyName = (LPSTR)(pb - (BYTE*) Otm);
pb += IntStoreName(&Names->FamilyNameW, pb);
/* face name */
Otm->otmpFaceName = (LPSTR)(pb - (BYTE*) Otm);
pb += IntStoreName(&Names->FaceNameW, pb);
/* style name */
Otm->otmpStyleName = (LPSTR)(pb - (BYTE*) Otm);
pb += IntStoreName(&Names->StyleNameW, pb);
/* unique name (full name) */
Otm->otmpFullName = (LPSTR)(pb - (BYTE*) Otm);
pb += IntStoreName(&Names->FullNameW, pb);
return pb;
}
static __inline void FASTCALL
IntFreeFontNames(FONT_NAMES *Names)
{
RtlFreeUnicodeString(&Names->FamilyNameW);
RtlFreeUnicodeString(&Names->FaceNameW);
RtlFreeUnicodeString(&Names->StyleNameW);
RtlFreeUnicodeString(&Names->FullNameW);
}
/*************************************************************
* IntGetOutlineTextMetrics
*
*/
INT FASTCALL
IntGetOutlineTextMetrics(PFONTGDI FontGDI,
UINT Size,
OUTLINETEXTMETRICW *Otm)
{
TT_OS2 *pOS2;
TT_HoriHeader *pHori;
TT_Postscript *pPost;
FT_Fixed XScale, YScale;
FT_WinFNT_HeaderRec WinFNT;
FT_Error Error;
BYTE *pb;
FONT_NAMES FontNames;
PSHARED_FACE SharedFace = FontGDI->SharedFace;
PSHARED_FACE_CACHE Cache;
FT_Face Face = SharedFace->Face;
if (PRIMARYLANGID(gusLanguageID) == LANG_ENGLISH)
{
Cache = &SharedFace->EnglishUS;
}
else
{
Cache = &SharedFace->UserLanguage;
}
if (Size == 0 && Cache->OutlineRequiredSize > 0)
{
ASSERT(Otm == NULL);
return Cache->OutlineRequiredSize;
}
IntInitFontNames(&FontNames, SharedFace);
Cache->OutlineRequiredSize = FontNames.OtmSize;
if (Size == 0)
{
ASSERT(Otm == NULL);
IntFreeFontNames(&FontNames);
return Cache->OutlineRequiredSize;
}
ASSERT(Otm != NULL);
if (Size < Cache->OutlineRequiredSize)
{
DPRINT1("Size %u < OutlineRequiredSize %u\n", Size,
Cache->OutlineRequiredSize);
IntFreeFontNames(&FontNames);
return 0; /* failure */
}
XScale = Face->size->metrics.x_scale;
YScale = Face->size->metrics.y_scale;
IntLockFreeType();
pOS2 = FT_Get_Sfnt_Table(Face, FT_SFNT_OS2);
pHori = FT_Get_Sfnt_Table(Face, FT_SFNT_HHEA);
pPost = FT_Get_Sfnt_Table(Face, FT_SFNT_POST); /* We can live with this failing */
Error = FT_Get_WinFNT_Header(Face, &WinFNT);
if (pOS2 == NULL && Error)
{
IntUnLockFreeType();
DPRINT1("Can't find OS/2 table - not TT font?\n");
IntFreeFontNames(&FontNames);
return 0;
}
if (pHori == NULL && Error)
{
IntUnLockFreeType();
DPRINT1("Can't find HHEA table - not TT font?\n");
IntFreeFontNames(&FontNames);
return 0;
}
Otm->otmSize = Cache->OutlineRequiredSize;
FillTM(&Otm->otmTextMetrics, FontGDI, pOS2, pHori, !Error ? &WinFNT : 0);
if (!pOS2)
goto skip_os2;
Otm->otmFiller = 0;
RtlCopyMemory(&Otm->otmPanoseNumber, pOS2->panose, PANOSE_COUNT);
Otm->otmfsSelection = pOS2->fsSelection;
Otm->otmfsType = pOS2->fsType;
Otm->otmsCharSlopeRise = pHori->caret_Slope_Rise;
Otm->otmsCharSlopeRun = pHori->caret_Slope_Run;
Otm->otmItalicAngle = 0; /* POST table */
Otm->otmEMSquare = Face->units_per_EM;
#define SCALE_X(value) ((FT_MulFix((value), XScale) + 32) >> 6)
#define SCALE_Y(value) ((FT_MulFix((value), YScale) + 32) >> 6)
Otm->otmAscent = SCALE_Y(pOS2->sTypoAscender);
Otm->otmDescent = SCALE_Y(pOS2->sTypoDescender);
Otm->otmLineGap = SCALE_Y(pOS2->sTypoLineGap);
Otm->otmsCapEmHeight = SCALE_Y(pOS2->sCapHeight);
Otm->otmsXHeight = SCALE_Y(pOS2->sxHeight);
Otm->otmrcFontBox.left = SCALE_X(Face->bbox.xMin);
Otm->otmrcFontBox.right = SCALE_X(Face->bbox.xMax);
Otm->otmrcFontBox.top = SCALE_Y(Face->bbox.yMax);
Otm->otmrcFontBox.bottom = SCALE_Y(Face->bbox.yMin);
Otm->otmMacAscent = Otm->otmTextMetrics.tmAscent;
Otm->otmMacDescent = -Otm->otmTextMetrics.tmDescent;
Otm->otmMacLineGap = Otm->otmLineGap;
Otm->otmusMinimumPPEM = 0; /* TT Header */
Otm->otmptSubscriptSize.x = SCALE_X(pOS2->ySubscriptXSize);
Otm->otmptSubscriptSize.y = SCALE_Y(pOS2->ySubscriptYSize);
Otm->otmptSubscriptOffset.x = SCALE_X(pOS2->ySubscriptXOffset);
Otm->otmptSubscriptOffset.y = SCALE_Y(pOS2->ySubscriptYOffset);
Otm->otmptSuperscriptSize.x = SCALE_X(pOS2->ySuperscriptXSize);
Otm->otmptSuperscriptSize.y = SCALE_Y(pOS2->ySuperscriptYSize);
Otm->otmptSuperscriptOffset.x = SCALE_X(pOS2->ySuperscriptXOffset);
Otm->otmptSuperscriptOffset.y = SCALE_Y(pOS2->ySuperscriptYOffset);
Otm->otmsStrikeoutSize = SCALE_Y(pOS2->yStrikeoutSize);
Otm->otmsStrikeoutPosition = SCALE_Y(pOS2->yStrikeoutPosition);
if (!pPost)
{
Otm->otmsUnderscoreSize = 0;
Otm->otmsUnderscorePosition = 0;
}
else
{
Otm->otmsUnderscoreSize = SCALE_Y(pPost->underlineThickness);
Otm->otmsUnderscorePosition = SCALE_Y(pPost->underlinePosition);
}
#undef SCALE_X
#undef SCALE_Y
skip_os2:
IntUnLockFreeType();
pb = IntStoreFontNames(&FontNames, Otm);
ASSERT(pb - (BYTE*)Otm == Cache->OutlineRequiredSize);
IntFreeFontNames(&FontNames);
return Cache->OutlineRequiredSize;
}
/* See https://msdn.microsoft.com/en-us/library/bb165625(v=vs.90).aspx */
static BYTE
CharSetFromLangID(LANGID LangID)
{
/* FIXME: Add more and fix if wrong */
switch (PRIMARYLANGID(LangID))
{
case LANG_CHINESE:
switch (SUBLANGID(LangID))
{
case SUBLANG_CHINESE_TRADITIONAL:
return CHINESEBIG5_CHARSET;
case SUBLANG_CHINESE_SIMPLIFIED:
default:
break;
}
return GB2312_CHARSET;
case LANG_CZECH: case LANG_HUNGARIAN: case LANG_POLISH:
case LANG_SLOVAK: case LANG_SLOVENIAN: case LANG_ROMANIAN:
return EASTEUROPE_CHARSET;
case LANG_RUSSIAN: case LANG_BULGARIAN: case LANG_MACEDONIAN:
case LANG_SERBIAN: case LANG_UKRAINIAN:
return RUSSIAN_CHARSET;
case LANG_ARABIC: return ARABIC_CHARSET;
case LANG_GREEK: return GREEK_CHARSET;
case LANG_HEBREW: return HEBREW_CHARSET;
case LANG_JAPANESE: return SHIFTJIS_CHARSET;
case LANG_KOREAN: return JOHAB_CHARSET;
case LANG_TURKISH: return TURKISH_CHARSET;
case LANG_THAI: return THAI_CHARSET;
case LANG_LATVIAN: return BALTIC_CHARSET;
case LANG_VIETNAMESE: return VIETNAMESE_CHARSET;
case LANG_ENGLISH: case LANG_BASQUE: case LANG_CATALAN:
case LANG_DANISH: case LANG_DUTCH: case LANG_FINNISH:
case LANG_FRENCH: case LANG_GERMAN: case LANG_ITALIAN:
case LANG_NORWEGIAN: case LANG_PORTUGUESE: case LANG_SPANISH:
case LANG_SWEDISH: default:
return ANSI_CHARSET;
}
}
static void
SwapEndian(LPVOID pvData, DWORD Size)
{
BYTE b, *pb = pvData;
Size /= 2;
while (Size-- > 0)
{
b = pb[0];
pb[0] = pb[1];
pb[1] = b;
++pb; ++pb;
}
}
static NTSTATUS
IntGetFontLocalizedName(PUNICODE_STRING pNameW, PSHARED_FACE SharedFace,
FT_UShort NameID, FT_UShort LangID)
{
FT_SfntName Name;
INT i, Count, BestIndex, Score, BestScore;
FT_Error Error;
NTSTATUS Status = STATUS_NOT_FOUND;
ANSI_STRING AnsiName;
PSHARED_FACE_CACHE Cache;
FT_Face Face = SharedFace->Face;
RtlFreeUnicodeString(pNameW);
/* select cache */
if (PRIMARYLANGID(LangID) == LANG_ENGLISH)
{
Cache = &SharedFace->EnglishUS;
}
else
{
Cache = &SharedFace->UserLanguage;
}
/* use cache if available */
if (NameID == TT_NAME_ID_FONT_FAMILY && Cache->FontFamily.Buffer)
{
return DuplicateUnicodeString(&Cache->FontFamily, pNameW);
}
if (NameID == TT_NAME_ID_FULL_NAME && Cache->FullName.Buffer)
{
return DuplicateUnicodeString(&Cache->FullName, pNameW);
}
BestIndex = -1;
BestScore = 0;
Count = FT_Get_Sfnt_Name_Count(Face);
for (i = 0; i < Count; ++i)
{
Error = FT_Get_Sfnt_Name(Face, i, &Name);
if (Error)
{
continue; /* failure */
}
if (Name.name_id != NameID)
{
continue; /* mismatched */
}
if (Name.platform_id != TT_PLATFORM_MICROSOFT ||
(Name.encoding_id != TT_MS_ID_UNICODE_CS &&
Name.encoding_id != TT_MS_ID_SYMBOL_CS))
{
continue; /* not Microsoft Unicode name */
}
if (Name.string == NULL || Name.string_len == 0 ||
(Name.string[0] == 0 && Name.string[1] == 0))
{
continue; /* invalid string */
}
if (Name.language_id == LangID)
{
Score = 30;
BestIndex = i;
break; /* best match */
}
else if (PRIMARYLANGID(Name.language_id) == PRIMARYLANGID(LangID))
{
Score = 20;
}
else if (PRIMARYLANGID(Name.language_id) == LANG_ENGLISH)
{
Score = 10;
}
else
{
Score = 0;
}
if (Score > BestScore)
{
BestScore = Score;
BestIndex = i;
}
}
if (BestIndex >= 0)
{
/* store the best name */
Error = (Score == 30) ? 0 : FT_Get_Sfnt_Name(Face, BestIndex, &Name);
if (!Error)
{
/* NOTE: Name.string is not null-terminated */
UNICODE_STRING Tmp;
Tmp.Buffer = (PWCH)Name.string;
Tmp.Length = Tmp.MaximumLength = Name.string_len;
pNameW->Length = 0;
pNameW->MaximumLength = Name.string_len + sizeof(WCHAR);
pNameW->Buffer = ExAllocatePoolWithTag(PagedPool, pNameW->MaximumLength, TAG_USTR);
if (pNameW->Buffer)
{
Status = RtlAppendUnicodeStringToString(pNameW, &Tmp);
if (Status == STATUS_SUCCESS)
{
/* Convert UTF-16 big endian to little endian */
SwapEndian(pNameW->Buffer, pNameW->Length);
}
}
else
{
Status = STATUS_INSUFFICIENT_RESOURCES;
}
}
}
if (!NT_SUCCESS(Status))
{
/* defaulted */
if (NameID == TT_NAME_ID_FONT_SUBFAMILY)
{
RtlInitAnsiString(&AnsiName, Face->style_name);
Status = RtlAnsiStringToUnicodeString(pNameW, &AnsiName, TRUE);
}
else
{
RtlInitAnsiString(&AnsiName, Face->family_name);
Status = RtlAnsiStringToUnicodeString(pNameW, &AnsiName, TRUE);
}
}
if (NT_SUCCESS(Status))
{
/* make cache */
if (NameID == TT_NAME_ID_FONT_FAMILY)
{
ASSERT_FREETYPE_LOCK_NOT_HELD();
IntLockFreeType();
if (!Cache->FontFamily.Buffer)
DuplicateUnicodeString(pNameW, &Cache->FontFamily);
IntUnLockFreeType();
}
else if (NameID == TT_NAME_ID_FULL_NAME)
{
ASSERT_FREETYPE_LOCK_NOT_HELD();
IntLockFreeType();
if (!Cache->FullName.Buffer)
DuplicateUnicodeString(pNameW, &Cache->FullName);
IntUnLockFreeType();
}
}
return Status;
}
static void FASTCALL
FontFamilyFillInfo(PFONTFAMILYINFO Info, LPCWSTR FaceName,
LPCWSTR FullName, PFONTGDI FontGDI)
{
ANSI_STRING StyleA;
UNICODE_STRING StyleW;
TT_OS2 *pOS2;
FONTSIGNATURE fs;
CHARSETINFO CharSetInfo;
unsigned i, Size;
OUTLINETEXTMETRICW *Otm;
LOGFONTW *Lf;
TEXTMETRICW *TM;
NEWTEXTMETRICW *Ntm;
DWORD fs0;
NTSTATUS status;
PSHARED_FACE SharedFace = FontGDI->SharedFace;
FT_Face Face = SharedFace->Face;
UNICODE_STRING NameW;
RtlInitUnicodeString(&NameW, NULL);
RtlZeroMemory(Info, sizeof(FONTFAMILYINFO));
Size = IntGetOutlineTextMetrics(FontGDI, 0, NULL);
Otm = ExAllocatePoolWithTag(PagedPool, Size, GDITAG_TEXT);
if (!Otm)
{
return;
}
Size = IntGetOutlineTextMetrics(FontGDI, Size, Otm);
if (!Size)
{
ExFreePoolWithTag(Otm, GDITAG_TEXT);
return;
}
Lf = &Info->EnumLogFontEx.elfLogFont;
TM = &Otm->otmTextMetrics;
Lf->lfHeight = TM->tmHeight;
Lf->lfWidth = TM->tmAveCharWidth;
Lf->lfWeight = TM->tmWeight;
Lf->lfItalic = TM->tmItalic;
Lf->lfPitchAndFamily = (TM->tmPitchAndFamily & 0xf1) + 1;
Lf->lfCharSet = TM->tmCharSet;
Lf->lfOutPrecision = OUT_OUTLINE_PRECIS;
Lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
Lf->lfQuality = PROOF_QUALITY;
Ntm = &Info->NewTextMetricEx.ntmTm;
Ntm->tmHeight = TM->tmHeight;
Ntm->tmAscent = TM->tmAscent;
Ntm->tmDescent = TM->tmDescent;
Ntm->tmInternalLeading = TM->tmInternalLeading;
Ntm->tmExternalLeading = TM->tmExternalLeading;
Ntm->tmAveCharWidth = TM->tmAveCharWidth;
Ntm->tmMaxCharWidth = TM->tmMaxCharWidth;
Ntm->tmWeight = TM->tmWeight;
Ntm->tmOverhang = TM->tmOverhang;
Ntm->tmDigitizedAspectX = TM->tmDigitizedAspectX;
Ntm->tmDigitizedAspectY = TM->tmDigitizedAspectY;
Ntm->tmFirstChar = TM->tmFirstChar;
Ntm->tmLastChar = TM->tmLastChar;
Ntm->tmDefaultChar = TM->tmDefaultChar;
Ntm->tmBreakChar = TM->tmBreakChar;
Ntm->tmItalic = TM->tmItalic;
Ntm->tmUnderlined = TM->tmUnderlined;
Ntm->tmStruckOut = TM->tmStruckOut;
Ntm->tmPitchAndFamily = TM->tmPitchAndFamily;
Ntm->tmCharSet = TM->tmCharSet;
Ntm->ntmFlags = TM->tmItalic ? NTM_ITALIC : 0;
if (550 < TM->tmWeight) Ntm->ntmFlags |= NTM_BOLD;
if (0 == Ntm->ntmFlags) Ntm->ntmFlags = NTM_REGULAR;
Info->FontType = (0 != (TM->tmPitchAndFamily & TMPF_TRUETYPE)
? TRUETYPE_FONTTYPE : 0);
if (0 == (TM->tmPitchAndFamily & TMPF_VECTOR))
Info->FontType |= RASTER_FONTTYPE;
/* face name */
if (!FaceName)
FaceName = (WCHAR*)((ULONG_PTR)Otm + (ULONG_PTR)Otm->otmpFamilyName);
RtlStringCbCopyW(Lf->lfFaceName, sizeof(Lf->lfFaceName), FaceName);
/* full name */
if (!FullName)
FullName = (WCHAR*)((ULONG_PTR) Otm + (ULONG_PTR)Otm->otmpFaceName);
RtlStringCbCopyW(Info->EnumLogFontEx.elfFullName,
sizeof(Info->EnumLogFontEx.elfFullName),
FullName);
RtlInitAnsiString(&StyleA, Face->style_name);
StyleW.Buffer = Info->EnumLogFontEx.elfStyle;
StyleW.MaximumLength = sizeof(Info->EnumLogFontEx.elfStyle);
status = RtlAnsiStringToUnicodeString(&StyleW, &StyleA, FALSE);
if (!NT_SUCCESS(status))
{
ExFreePoolWithTag(Otm, GDITAG_TEXT);
return;
}
Info->EnumLogFontEx.elfScript[0] = UNICODE_NULL;
IntLockFreeType();
pOS2 = FT_Get_Sfnt_Table(Face, ft_sfnt_os2);
if (!pOS2)
{
IntUnLockFreeType();
ExFreePoolWithTag(Otm, GDITAG_TEXT);
return;
}
Ntm->ntmSizeEM = Otm->otmEMSquare;
Ntm->ntmCellHeight = (FT_Short)pOS2->usWinAscent + (FT_Short)pOS2->usWinDescent;
Ntm->ntmAvgWidth = 0;
ExFreePoolWithTag(Otm, GDITAG_TEXT);
fs.fsCsb[0] = pOS2->ulCodePageRange1;
fs.fsCsb[1] = pOS2->ulCodePageRange2;
fs.fsUsb[0] = pOS2->ulUnicodeRange1;
fs.fsUsb[1] = pOS2->ulUnicodeRange2;
fs.fsUsb[2] = pOS2->ulUnicodeRange3;
fs.fsUsb[3] = pOS2->ulUnicodeRange4;
if (0 == pOS2->version)
{
FT_UInt Dummy;
if (FT_Get_First_Char(Face, &Dummy) < 0x100)
fs.fsCsb[0] |= FS_LATIN1;
else
fs.fsCsb[0] |= FS_SYMBOL;
}
IntUnLockFreeType();
if (fs.fsCsb[0] == 0)
{
/* Let's see if we can find any interesting cmaps */
for (i = 0; i < (UINT)Face->num_charmaps; i++)
{
switch (Face->charmaps[i]->encoding)
{
case FT_ENCODING_UNICODE:
case FT_ENCODING_APPLE_ROMAN:
fs.fsCsb[0] |= FS_LATIN1;
break;
case FT_ENCODING_MS_SYMBOL:
fs.fsCsb[0] |= FS_SYMBOL;
break;
default:
break;
}
}
}
for (i = 0; i < MAXTCIINDEX; i++)
{
fs0 = 1L << i;
if (fs.fsCsb[0] & fs0)
{
if (!IntTranslateCharsetInfo(&fs0, &CharSetInfo, TCI_SRCFONTSIG))
{
CharSetInfo.ciCharset = DEFAULT_CHARSET;
}
if (DEFAULT_CHARSET != CharSetInfo.ciCharset)
{
if (g_ElfScripts[i])
wcscpy(Info->EnumLogFontEx.elfScript, g_ElfScripts[i]);
else
{
DPRINT1("Unknown elfscript for bit %u\n", i);
}
}
}
}
Info->NewTextMetricEx.ntmFontSig = fs;
}
static BOOLEAN FASTCALL
GetFontFamilyInfoForList(const LOGFONTW *LogFont,
PFONTFAMILYINFO Info,
LPCWSTR NominalName,
LONG *pCount,
LONG MaxCount,
PLIST_ENTRY Head)
{
PLIST_ENTRY Entry;
PFONT_ENTRY CurrentEntry;
FONTGDI *FontGDI;
FONTFAMILYINFO InfoEntry;
LONG Count = *pCount;
for (Entry = Head->Flink; Entry != Head; Entry = Entry->Flink)
{
CurrentEntry = CONTAINING_RECORD(Entry, FONT_ENTRY, ListEntry);
FontGDI = CurrentEntry->Font;
ASSERT(FontGDI);
if (LogFont->lfCharSet != DEFAULT_CHARSET &&
LogFont->lfCharSet != FontGDI->CharSet)
{
continue; /* charset mismatch */
}
/* get one info entry */
FontFamilyFillInfo(&InfoEntry, NULL, NULL, FontGDI);
if (LogFont->lfFaceName[0] != UNICODE_NULL)
{
/* check name */
if (_wcsnicmp(LogFont->lfFaceName,
InfoEntry.EnumLogFontEx.elfLogFont.lfFaceName,
RTL_NUMBER_OF(LogFont->lfFaceName) - 1) != 0 &&
_wcsnicmp(LogFont->lfFaceName,
InfoEntry.EnumLogFontEx.elfFullName,
RTL_NUMBER_OF(LogFont->lfFaceName) - 1) != 0)
{
continue;
}
}
if (NominalName)
{
/* store the nominal name */
RtlStringCbCopyW(InfoEntry.EnumLogFontEx.elfLogFont.lfFaceName,
sizeof(InfoEntry.EnumLogFontEx.elfLogFont.lfFaceName),
NominalName);
}
/* store one entry to Info */
if (0 <= Count && Count < MaxCount)
{
RtlCopyMemory(&Info[Count], &InfoEntry, sizeof(InfoEntry));
}
Count++;
}
*pCount = Count;
return TRUE;
}
static BOOLEAN FASTCALL
GetFontFamilyInfoForSubstitutes(const LOGFONTW *LogFont,
PFONTFAMILYINFO Info,
LONG *pCount,
LONG MaxCount)
{
PLIST_ENTRY pEntry, pHead = &g_FontSubstListHead;
PFONTSUBST_ENTRY pCurrentEntry;
PUNICODE_STRING pFromW, pToW;
LOGFONTW lf = *LogFont;
PPROCESSINFO Win32Process = PsGetCurrentProcessWin32Process();
for (pEntry = pHead->Flink; pEntry != pHead; pEntry = pEntry->Flink)
{
pCurrentEntry = CONTAINING_RECORD(pEntry, FONTSUBST_ENTRY, ListEntry);
pFromW = &pCurrentEntry->FontNames[FONTSUBST_FROM];
if (LogFont->lfFaceName[0] != UNICODE_NULL)
{
/* check name */
if (_wcsicmp(LogFont->lfFaceName, pFromW->Buffer) != 0)
continue; /* mismatch */
}
pToW = &pCurrentEntry->FontNames[FONTSUBST_TO];
if (RtlEqualUnicodeString(pFromW, pToW, TRUE) &&
pCurrentEntry->CharSets[FONTSUBST_FROM] ==
pCurrentEntry->CharSets[FONTSUBST_TO])
{
/* identical mapping */
continue;
}
/* substitute and get the real name */
IntUnicodeStringToBuffer(lf.lfFaceName, sizeof(lf.lfFaceName), pFromW);
SubstituteFontRecurse(&lf);
if (LogFont->lfCharSet != DEFAULT_CHARSET && LogFont->lfCharSet != lf.lfCharSet)
continue;
/* search in global fonts */
IntLockGlobalFonts();
GetFontFamilyInfoForList(&lf, Info, pFromW->Buffer, pCount, MaxCount, &g_FontListHead);
IntUnLockGlobalFonts();
/* search in private fonts */
IntLockProcessPrivateFonts(Win32Process);
GetFontFamilyInfoForList(&lf, Info, pFromW->Buffer, pCount, MaxCount,
&Win32Process->PrivateFontListHead);
IntUnLockProcessPrivateFonts(Win32Process);
if (LogFont->lfFaceName[0] != UNICODE_NULL)
{
/* it's already matched to the exact name and charset if the name
was specified at here, then so don't scan more for another name */
break;
}
}
return TRUE;
}
BOOL
FASTCALL
ftGdiGetRasterizerCaps(LPRASTERIZER_STATUS lprs)
{
if ( lprs )
{
lprs->nSize = sizeof(RASTERIZER_STATUS);
lprs->wFlags = TT_AVAILABLE | TT_ENABLED;
lprs->nLanguageID = gusLanguageID;
return TRUE;
}
EngSetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
static
BOOL
SameScaleMatrix(
PMATRIX pmx1,
PMATRIX pmx2)
{
return (FLOATOBJ_Equal(&pmx1->efM11, &pmx2->efM11) &&
FLOATOBJ_Equal(&pmx1->efM12, &pmx2->efM12) &&
FLOATOBJ_Equal(&pmx1->efM21, &pmx2->efM21) &&
FLOATOBJ_Equal(&pmx1->efM22, &pmx2->efM22));
}
FT_BitmapGlyph APIENTRY
ftGdiGlyphCacheGet(
FT_Face Face,
INT GlyphIndex,
INT Height,
FT_Render_Mode RenderMode,
PMATRIX pmx)
{
PLIST_ENTRY CurrentEntry;
PFONT_CACHE_ENTRY FontEntry;
ASSERT_FREETYPE_LOCK_HELD();
for (CurrentEntry = g_FontCacheListHead.Flink;
CurrentEntry != &g_FontCacheListHead;
CurrentEntry = CurrentEntry->Flink)
{
FontEntry = CONTAINING_RECORD(CurrentEntry, FONT_CACHE_ENTRY, ListEntry);
if ((FontEntry->Face == Face) &&
(FontEntry->GlyphIndex == GlyphIndex) &&
(FontEntry->Height == Height) &&
(FontEntry->RenderMode == RenderMode) &&
(SameScaleMatrix(&FontEntry->mxWorldToDevice, pmx)))
break;
}
if (CurrentEntry == &g_FontCacheListHead)
{
return NULL;
}
RemoveEntryList(CurrentEntry);
InsertHeadList(&g_FontCacheListHead, CurrentEntry);
return FontEntry->BitmapGlyph;
}
/* no cache */
FT_BitmapGlyph APIENTRY
ftGdiGlyphSet(
FT_Face Face,
FT_GlyphSlot GlyphSlot,
FT_Render_Mode RenderMode)
{
FT_Glyph Glyph;
INT error;
FT_Bitmap AlignedBitmap;
FT_BitmapGlyph BitmapGlyph;
error = FT_Get_Glyph(GlyphSlot, &Glyph);
if (error)
{
DPRINT1("Failure getting glyph.\n");
return NULL;
}
error = FT_Glyph_To_Bitmap(&Glyph, RenderMode, 0, 1);
if (error)
{
FT_Done_Glyph(Glyph);
DPRINT1("Failure rendering glyph.\n");
return NULL;
}
BitmapGlyph = (FT_BitmapGlyph)Glyph;
FT_Bitmap_New(&AlignedBitmap);
if (FT_Bitmap_Convert(GlyphSlot->library, &BitmapGlyph->bitmap, &AlignedBitmap, 4))
{
DPRINT1("Conversion failed\n");
FT_Done_Glyph((FT_Glyph)BitmapGlyph);
return NULL;
}
FT_Bitmap_Done(GlyphSlot->library, &BitmapGlyph->bitmap);
BitmapGlyph->bitmap = AlignedBitmap;
return BitmapGlyph;
}
FT_BitmapGlyph APIENTRY
ftGdiGlyphCacheSet(
FT_Face Face,
INT GlyphIndex,
INT Height,
PMATRIX pmx,
FT_GlyphSlot GlyphSlot,
FT_Render_Mode RenderMode)
{
FT_Glyph GlyphCopy;
INT error;
PFONT_CACHE_ENTRY NewEntry;
FT_Bitmap AlignedBitmap;
FT_BitmapGlyph BitmapGlyph;
ASSERT_FREETYPE_LOCK_HELD();
error = FT_Get_Glyph(GlyphSlot, &GlyphCopy);
if (error)
{
DPRINT1("Failure caching glyph.\n");
return NULL;
};
error = FT_Glyph_To_Bitmap(&GlyphCopy, RenderMode, 0, 1);
if (error)
{
FT_Done_Glyph(GlyphCopy);
DPRINT1("Failure rendering glyph.\n");
return NULL;
};
NewEntry = ExAllocatePoolWithTag(PagedPool, sizeof(FONT_CACHE_ENTRY), TAG_FONT);
if (!NewEntry)
{
DPRINT1("Alloc failure caching glyph.\n");
FT_Done_Glyph(GlyphCopy);
return NULL;
}
BitmapGlyph = (FT_BitmapGlyph)GlyphCopy;
FT_Bitmap_New(&AlignedBitmap);
if(FT_Bitmap_Convert(GlyphSlot->library, &BitmapGlyph->bitmap, &AlignedBitmap, 4))
{
DPRINT1("Conversion failed\n");
ExFreePoolWithTag(NewEntry, TAG_FONT);
FT_Bitmap_Done(GlyphSlot->library, &AlignedBitmap);
FT_Done_Glyph((FT_Glyph)BitmapGlyph);
return NULL;
}
FT_Bitmap_Done(GlyphSlot->library, &BitmapGlyph->bitmap);
BitmapGlyph->bitmap = AlignedBitmap;
NewEntry->GlyphIndex = GlyphIndex;
NewEntry->Face = Face;
NewEntry->BitmapGlyph = BitmapGlyph;
NewEntry->Height = Height;
NewEntry->RenderMode = RenderMode;
NewEntry->mxWorldToDevice = *pmx;
InsertHeadList(&g_FontCacheListHead, &NewEntry->ListEntry);
if (++g_FontCacheNumEntries > MAX_FONT_CACHE)
{
NewEntry = CONTAINING_RECORD(g_FontCacheListHead.Blink, FONT_CACHE_ENTRY, ListEntry);
RemoveCachedEntry(NewEntry);
}
return BitmapGlyph;
}
static unsigned int get_native_glyph_outline(FT_Outline *outline, unsigned int buflen, char *buf)
{
TTPOLYGONHEADER *pph;
TTPOLYCURVE *ppc;
int needed = 0, point = 0, contour, first_pt;
unsigned int pph_start, cpfx;
DWORD type;
for (contour = 0; contour < outline->n_contours; contour++)
{
/* Ignore contours containing one point */
if (point == outline->contours[contour])
{
point++;
continue;
}
pph_start = needed;
pph = (TTPOLYGONHEADER *)(buf + needed);
first_pt = point;
if (buf)
{
pph->dwType = TT_POLYGON_TYPE;
FTVectorToPOINTFX(&outline->points[point], &pph->pfxStart);
}
needed += sizeof(*pph);
point++;
while (point <= outline->contours[contour])
{
ppc = (TTPOLYCURVE *)(buf + needed);
type = (outline->tags[point] & FT_Curve_Tag_On) ?
TT_PRIM_LINE : TT_PRIM_QSPLINE;
cpfx = 0;
do
{
if (buf)
FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
cpfx++;
point++;
} while (point <= outline->contours[contour] &&
(outline->tags[point] & FT_Curve_Tag_On) ==
(outline->tags[point-1] & FT_Curve_Tag_On));
/* At the end of a contour Windows adds the start point, but
only for Beziers */
if (point > outline->contours[contour] &&
!(outline->tags[point-1] & FT_Curve_Tag_On))
{
if (buf)
FTVectorToPOINTFX(&outline->points[first_pt], &ppc->apfx[cpfx]);
cpfx++;
}
else if (point <= outline->contours[contour] &&
outline->tags[point] & FT_Curve_Tag_On)
{
/* add closing pt for bezier */
if (buf)
FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
cpfx++;
point++;
}
if (buf)
{
ppc->wType = type;
ppc->cpfx = cpfx;
}
needed += sizeof(*ppc) + (cpfx - 1) * sizeof(POINTFX);
}
if (buf)
pph->cb = needed - pph_start;
}
return needed;
}
static unsigned int get_bezier_glyph_outline(FT_Outline *outline, unsigned int buflen, char *buf)
{
/* Convert the quadratic Beziers to cubic Beziers.
The parametric eqn for a cubic Bezier is, from PLRM:
r(t) = at^3 + bt^2 + ct + r0
with the control points:
r1 = r0 + c/3
r2 = r1 + (c + b)/3
r3 = r0 + c + b + a
A quadratic Bezier has the form:
p(t) = (1-t)^2 p0 + 2(1-t)t p1 + t^2 p2
So equating powers of t leads to:
r1 = 2/3 p1 + 1/3 p0
r2 = 2/3 p1 + 1/3 p2
and of course r0 = p0, r3 = p2
*/
int contour, point = 0, first_pt;
TTPOLYGONHEADER *pph;
TTPOLYCURVE *ppc;
DWORD pph_start, cpfx, type;
FT_Vector cubic_control[4];
unsigned int needed = 0;
for (contour = 0; contour < outline->n_contours; contour++)
{
pph_start = needed;
pph = (TTPOLYGONHEADER *)(buf + needed);
first_pt = point;
if (buf)
{
pph->dwType = TT_POLYGON_TYPE;
FTVectorToPOINTFX(&outline->points[point], &pph->pfxStart);
}
needed += sizeof(*pph);
point++;
while (point <= outline->contours[contour])
{
ppc = (TTPOLYCURVE *)(buf + needed);
type = (outline->tags[point] & FT_Curve_Tag_On) ?
TT_PRIM_LINE : TT_PRIM_CSPLINE;
cpfx = 0;
do
{
if (type == TT_PRIM_LINE)
{
if (buf)
FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
cpfx++;
point++;
}
else
{
/* Unlike QSPLINEs, CSPLINEs always have their endpoint
so cpfx = 3n */
/* FIXME: Possible optimization in endpoint calculation
if there are two consecutive curves */
cubic_control[0] = outline->points[point-1];
if (!(outline->tags[point-1] & FT_Curve_Tag_On))
{
cubic_control[0].x += outline->points[point].x + 1;
cubic_control[0].y += outline->points[point].y + 1;
cubic_control[0].x >>= 1;
cubic_control[0].y >>= 1;
}
if (point+1 > outline->contours[contour])
cubic_control[3] = outline->points[first_pt];
else
{
cubic_control[3] = outline->points[point+1];
if (!(outline->tags[point+1] & FT_Curve_Tag_On))
{
cubic_control[3].x += outline->points[point].x + 1;
cubic_control[3].y += outline->points[point].y + 1;
cubic_control[3].x >>= 1;
cubic_control[3].y >>= 1;
}
}
/* r1 = 1/3 p0 + 2/3 p1
r2 = 1/3 p2 + 2/3 p1 */
cubic_control[1].x = (2 * outline->points[point].x + 1) / 3;
cubic_control[1].y = (2 * outline->points[point].y + 1) / 3;
cubic_control[2] = cubic_control[1];
cubic_control[1].x += (cubic_control[0].x + 1) / 3;
cubic_control[1].y += (cubic_control[0].y + 1) / 3;
cubic_control[2].x += (cubic_control[3].x + 1) / 3;
cubic_control[2].y += (cubic_control[3].y + 1) / 3;
if (buf)
{
FTVectorToPOINTFX(&cubic_control[1], &ppc->apfx[cpfx]);
FTVectorToPOINTFX(&cubic_control[2], &ppc->apfx[cpfx+1]);
FTVectorToPOINTFX(&cubic_control[3], &ppc->apfx[cpfx+2]);
}
cpfx += 3;
point++;
}
} while (point <= outline->contours[contour] &&
(outline->tags[point] & FT_Curve_Tag_On) ==
(outline->tags[point-1] & FT_Curve_Tag_On));
/* At the end of a contour Windows adds the start point,
but only for Beziers and we've already done that.
*/
if (point <= outline->contours[contour] &&
outline->tags[point] & FT_Curve_Tag_On)
{
/* This is the closing pt of a bezier, but we've already
added it, so just inc point and carry on */
point++;
}
if (buf)
{
ppc->wType = type;
ppc->cpfx = cpfx;
}
needed += sizeof(*ppc) + (cpfx - 1) * sizeof(POINTFX);
}
if (buf)
pph->cb = needed - pph_start;
}
return needed;
}
static FT_Error
IntRequestFontSize(PDC dc, PFONTGDI FontGDI, LONG lfWidth, LONG lfHeight)
{
FT_Error error;
FT_Size_RequestRec req;
FT_Face face = FontGDI->SharedFace->Face;
TT_OS2 *pOS2;
TT_HoriHeader *pHori;
FT_WinFNT_HeaderRec WinFNT;
LONG Ascent, Descent, Sum, EmHeight64;
lfWidth = abs(lfWidth);
if (lfHeight == 0)
{
if (lfWidth == 0)
{
DPRINT("lfHeight and lfWidth are zero.\n");
lfHeight = -16;
}
else
{
lfHeight = lfWidth;
}
}
if (lfHeight == -1)
lfHeight = -2;
ASSERT_FREETYPE_LOCK_HELD();
pOS2 = (TT_OS2 *)FT_Get_Sfnt_Table(face, FT_SFNT_OS2);
pHori = (TT_HoriHeader *)FT_Get_Sfnt_Table(face, FT_SFNT_HHEA);
if (!pOS2 || !pHori)
{
error = FT_Get_WinFNT_Header(face, &WinFNT);
if (error)
{
DPRINT1("%s: Failed to request font size.\n", face->family_name);
ASSERT(FALSE);
return error;
}
FontGDI->tmHeight = WinFNT.pixel_height;
FontGDI->tmAscent = WinFNT.ascent;
FontGDI->tmDescent = FontGDI->tmHeight - FontGDI->tmAscent;
FontGDI->tmInternalLeading = WinFNT.internal_leading;
FontGDI->EmHeight = FontGDI->tmHeight - FontGDI->tmInternalLeading;
FontGDI->EmHeight = max(FontGDI->EmHeight, 1);
FontGDI->EmHeight = min(FontGDI->EmHeight, USHORT_MAX);
FontGDI->Magic = FONTGDI_MAGIC;
return 0;
}
/*
* NOTE: We cast TT_OS2.usWinAscent and TT_OS2.usWinDescent to signed FT_Short.
* Why? See: https://docs.microsoft.com/en-us/typography/opentype/spec/os2#uswindescent
*
* > usWinDescent is "usually" a positive value ...
*
* We can read it as "not always". See CORE-14994.
* See also: https://docs.microsoft.com/en-us/typography/opentype/spec/os2#fsselection
*/
#define FM_SEL_USE_TYPO_METRICS 0x80
if (lfHeight > 0)
{
/* case (A): lfHeight is positive */
Sum = (FT_Short)pOS2->usWinAscent + (FT_Short)pOS2->usWinDescent;
if (Sum == 0 || (pOS2->fsSelection & FM_SEL_USE_TYPO_METRICS))
{
Ascent = pHori->Ascender;
Descent = -pHori->Descender;
Sum = Ascent + Descent;
}
else
{
Ascent = (FT_Short)pOS2->usWinAscent;
Descent = (FT_Short)pOS2->usWinDescent;
}
FontGDI->tmAscent = FT_MulDiv(lfHeight, Ascent, Sum);
FontGDI->tmDescent = FT_MulDiv(lfHeight, Descent, Sum);
FontGDI->tmHeight = FontGDI->tmAscent + FontGDI->tmDescent;
FontGDI->tmInternalLeading = FontGDI->tmHeight - FT_MulDiv(lfHeight, face->units_per_EM, Sum);
}
else if (lfHeight < 0)
{
/* case (B): lfHeight is negative */
if (pOS2->fsSelection & FM_SEL_USE_TYPO_METRICS)
{
FontGDI->tmAscent = FT_MulDiv(-lfHeight, pHori->Ascender, face->units_per_EM);
FontGDI->tmDescent = FT_MulDiv(-lfHeight, -pHori->Descender, face->units_per_EM);
}
else
{
FontGDI->tmAscent = FT_MulDiv(-lfHeight, (FT_Short)pOS2->usWinAscent, face->units_per_EM);
FontGDI->tmDescent = FT_MulDiv(-lfHeight, (FT_Short)pOS2->usWinDescent, face->units_per_EM);
}
FontGDI->tmHeight = FontGDI->tmAscent + FontGDI->tmDescent;
FontGDI->tmInternalLeading = FontGDI->tmHeight + lfHeight;
}
#undef FM_SEL_USE_TYPO_METRICS
FontGDI->EmHeight = FontGDI->tmHeight - FontGDI->tmInternalLeading;
FontGDI->EmHeight = max(FontGDI->EmHeight, 1);
FontGDI->EmHeight = min(FontGDI->EmHeight, USHORT_MAX);
FontGDI->Magic = FONTGDI_MAGIC;
EmHeight64 = (FontGDI->EmHeight << 6);
req.type = FT_SIZE_REQUEST_TYPE_NOMINAL;
req.width = 0;
req.height = EmHeight64;
req.horiResolution = 0;
req.vertResolution = 0;
return FT_Request_Size(face, &req);
}
BOOL
FASTCALL
TextIntUpdateSize(PDC dc,
PTEXTOBJ TextObj,
PFONTGDI FontGDI,
BOOL bDoLock)
{
FT_Face face;
INT error, n;
FT_CharMap charmap, found;
LOGFONTW *plf;
if (bDoLock)
IntLockFreeType();
face = FontGDI->SharedFace->Face;
if (face->charmap == NULL)
{
DPRINT("WARNING: No charmap selected!\n");
DPRINT("This font face has %d charmaps\n", face->num_charmaps);
found = NULL;
for (n = 0; n < face->num_charmaps; n++)
{
charmap = face->charmaps[n];
if (charmap->encoding == FT_ENCODING_UNICODE)
{
found = charmap;
break;
}
}
if (!found)
{
for (n = 0; n < face->num_charmaps; n++)
{
charmap = face->charmaps[n];
if (charmap->platform_id == TT_PLATFORM_APPLE_UNICODE)
{
found = charmap;
break;
}
}
}
if (!found)
{
for (n = 0; n < face->num_charmaps; n++)
{
charmap = face->charmaps[n];
if (charmap->encoding == FT_ENCODING_MS_SYMBOL)
{
found = charmap;
break;
}
}
}
if (!found && face->num_charmaps > 0)
{
found = face->charmaps[0];
}
if (!found)
{
DPRINT1("WARNING: Could not find desired charmap!\n");
}
else
{
DPRINT("Found charmap encoding: %i\n", found->encoding);
error = FT_Set_Charmap(face, found);
if (error)
{
DPRINT1("WARNING: Could not set the charmap!\n");
}
}
}
plf = &TextObj->logfont.elfEnumLogfontEx.elfLogFont;
error = IntRequestFontSize(dc, FontGDI, plf->lfWidth, plf->lfHeight);
if (bDoLock)
IntUnLockFreeType();
if (error)
{
DPRINT1("Error in setting pixel sizes: %d\n", error);
return FALSE;
}
return TRUE;
}
static inline FT_UInt FASTCALL
get_glyph_index_symbol(FT_Face ft_face, UINT glyph)
{
FT_UInt ret;
if (glyph < 0x100) glyph += 0xf000;
/* there are a number of old pre-Unicode "broken" TTFs, which
do have symbols at U+00XX instead of U+f0XX */
if (!(ret = FT_Get_Char_Index(ft_face, glyph)))
ret = FT_Get_Char_Index(ft_face, glyph - 0xf000);
return ret;
}
static inline FT_UInt FASTCALL
get_glyph_index(FT_Face ft_face, UINT glyph)
{
FT_UInt ret;
if (face_has_symbol_charmap(ft_face))
{
ret = get_glyph_index_symbol(ft_face, glyph);
if (ret != 0)
return ret;
}
return FT_Get_Char_Index(ft_face, glyph);
}
static inline FT_UInt FASTCALL
get_glyph_index_flagged(FT_Face face, FT_ULong code, DWORD indexed_flag, DWORD flags)
{
FT_UInt glyph_index;
if (flags & indexed_flag)
{
glyph_index = code;
}
else
{
glyph_index = get_glyph_index(face, code);
}
return glyph_index;
}
/*
* Based on WineEngGetGlyphOutline
*
*/
ULONG
FASTCALL
ftGdiGetGlyphOutline(
PDC dc,
WCHAR wch,
UINT iFormat,
LPGLYPHMETRICS pgm,
ULONG cjBuf,
PVOID pvBuf,
LPMAT2 pmat2,
BOOL bIgnoreRotation)
{
PDC_ATTR pdcattr;
PTEXTOBJ TextObj;
PFONTGDI FontGDI;
HFONT hFont = 0;
GLYPHMETRICS gm;
ULONG Size;
FT_Face ft_face;
FT_UInt glyph_index;
DWORD width, height, pitch, needed = 0;
FT_Bitmap ft_bitmap;
FT_Error error;
INT left, right, top = 0, bottom = 0;
FT_Int load_flags = FT_LOAD_DEFAULT | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
FLOATOBJ eM11, widthRatio, eTemp;
FT_Matrix transMat = identityMat;
BOOL needsTransform = FALSE;
INT orientation;
LONG aveWidth;
INT adv, lsb, bbx; /* These three hold to widths of the unrotated chars */
OUTLINETEXTMETRICW *potm;
XFORMOBJ xo;
XFORML xform;
LOGFONTW *plf;
DPRINT("%u, %08x, %p, %08lx, %p, %p\n", wch, iFormat, pgm,
cjBuf, pvBuf, pmat2);
pdcattr = dc->pdcattr;
XFORMOBJ_vInit(&xo, &dc->pdcattr->mxWorldToDevice);
XFORMOBJ_iGetXform(&xo, &xform);
FLOATOBJ_SetFloat(&eM11, xform.eM11);
hFont = pdcattr->hlfntNew;
TextObj = RealizeFontInit(hFont);
if (!TextObj)
{
EngSetLastError(ERROR_INVALID_HANDLE);
return GDI_ERROR;
}
FontGDI = ObjToGDI(TextObj->Font, FONT);
ft_face = FontGDI->SharedFace->Face;
plf = &TextObj->logfont.elfEnumLogfontEx.elfLogFont;
aveWidth = FT_IS_SCALABLE(ft_face) ? abs(plf->lfWidth) : 0;
orientation = FT_IS_SCALABLE(ft_face) ? plf->lfOrientation : 0;
Size = IntGetOutlineTextMetrics(FontGDI, 0, NULL);
potm = ExAllocatePoolWithTag(PagedPool, Size, GDITAG_TEXT);
if (!potm)
{
EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
TEXTOBJ_UnlockText(TextObj);
return GDI_ERROR;
}
Size = IntGetOutlineTextMetrics(FontGDI, Size, potm);
if (!Size)
{
/* FIXME: last error? */
ExFreePoolWithTag(potm, GDITAG_TEXT);
TEXTOBJ_UnlockText(TextObj);
return GDI_ERROR;
}
IntLockFreeType();
TextIntUpdateSize(dc, TextObj, FontGDI, FALSE);
FtSetCoordinateTransform(ft_face, DC_pmxWorldToDevice(dc));
TEXTOBJ_UnlockText(TextObj);
glyph_index = get_glyph_index_flagged(ft_face, wch, GGO_GLYPH_INDEX, iFormat);
iFormat &= ~GGO_GLYPH_INDEX;
if (orientation || (iFormat != GGO_METRICS && iFormat != GGO_BITMAP) || aveWidth || pmat2)
load_flags |= FT_LOAD_NO_BITMAP;
if (iFormat & GGO_UNHINTED)
{
load_flags |= FT_LOAD_NO_HINTING;
iFormat &= ~GGO_UNHINTED;
}
error = FT_Load_Glyph(ft_face, glyph_index, load_flags);
if (error)
{
DPRINT1("WARNING: Failed to load and render glyph! [index: %u]\n", glyph_index);
IntUnLockFreeType();
if (potm) ExFreePoolWithTag(potm, GDITAG_TEXT);
return GDI_ERROR;
}
IntUnLockFreeType();
FLOATOBJ_Set1(&widthRatio);
if (aveWidth && potm)
{
// widthRatio = aveWidth * eM11 / potm->otmTextMetrics.tmAveCharWidth
FLOATOBJ_SetLong(&widthRatio, aveWidth);
FLOATOBJ_Mul(&widthRatio, &eM11);
FLOATOBJ_DivLong(&widthRatio, potm->otmTextMetrics.tmAveCharWidth);
}
//left = (INT)(ft_face->glyph->metrics.horiBearingX * widthRatio) & -64;
FLOATOBJ_SetLong(&eTemp, ft_face->glyph->metrics.horiBearingX);
FLOATOBJ_Mul(&eTemp, &widthRatio);
left = FLOATOBJ_GetLong(&eTemp) & -64;
//right = (INT)((ft_face->glyph->metrics.horiBearingX +
// ft_face->glyph->metrics.width) * widthRatio + 63) & -64;
FLOATOBJ_SetLong(&eTemp, ft_face->glyph->metrics.horiBearingX * ft_face->glyph->metrics.width);
FLOATOBJ_Mul(&eTemp, &widthRatio);
FLOATOBJ_AddLong(&eTemp, 63);
right = FLOATOBJ_GetLong(&eTemp) & -64;
//adv = (INT)((ft_face->glyph->metrics.horiAdvance * widthRatio) + 63) >> 6;
FLOATOBJ_SetLong(&eTemp, ft_face->glyph->metrics.horiAdvance);
FLOATOBJ_Mul(&eTemp, &widthRatio);
FLOATOBJ_AddLong(&eTemp, 63);
adv = FLOATOBJ_GetLong(&eTemp) >> 6;
lsb = left >> 6;
bbx = (right - left) >> 6;
DPRINT("Advance = %d, lsb = %d, bbx = %d\n",adv, lsb, bbx);
IntLockFreeType();
/* Width scaling transform */
if (!FLOATOBJ_Equal1(&widthRatio))
{
FT_Matrix scaleMat;
eTemp = widthRatio;
FLOATOBJ_MulLong(&eTemp, 1 << 16);
scaleMat.xx = FLOATOBJ_GetLong(&eTemp);
scaleMat.xy = 0;
scaleMat.yx = 0;
scaleMat.yy = INT_TO_FIXED(1);
FT_Matrix_Multiply(&scaleMat, &transMat);
needsTransform = TRUE;
}
/* World transform */
{
FT_Matrix ftmatrix;
PMATRIX pmx = DC_pmxWorldToDevice(dc);
/* Create a freetype matrix, by converting to 16.16 fixpoint format */
FtMatrixFromMx(&ftmatrix, pmx);
if (memcmp(&ftmatrix, &identityMat, sizeof(identityMat)) != 0)
{
FT_Matrix_Multiply(&ftmatrix, &transMat);
needsTransform = TRUE;
}
}
/* Rotation transform */
if (orientation)
{
FT_Matrix rotationMat;
DPRINT("Rotation Trans!\n");
IntEscapeMatrix(&rotationMat, orientation);
FT_Matrix_Multiply(&rotationMat, &transMat);
needsTransform = TRUE;
}
/* Extra transformation specified by caller */
if (pmat2)
{
FT_Matrix extraMat;
DPRINT("MAT2 Matrix Trans!\n");
extraMat.xx = FT_FixedFromFIXED(pmat2->eM11);
extraMat.xy = FT_FixedFromFIXED(pmat2->eM21);
extraMat.yx = FT_FixedFromFIXED(pmat2->eM12);
extraMat.yy = FT_FixedFromFIXED(pmat2->eM22);
FT_Matrix_Multiply(&extraMat, &transMat);
needsTransform = TRUE;
}
if (potm) ExFreePoolWithTag(potm, GDITAG_TEXT); /* It looks like we are finished with potm ATM. */
if (!needsTransform)
{
DPRINT("No Need to be Transformed!\n");
top = (ft_face->glyph->metrics.horiBearingY + 63) & -64;
bottom = (ft_face->glyph->metrics.horiBearingY -
ft_face->glyph->metrics.height) & -64;
gm.gmCellIncX = adv;
gm.gmCellIncY = 0;
}
else
{
INT xc, yc;
FT_Vector vec;
for (xc = 0; xc < 2; xc++)
{
for (yc = 0; yc < 2; yc++)
{
vec.x = (ft_face->glyph->metrics.horiBearingX +
xc * ft_face->glyph->metrics.width);
vec.y = ft_face->glyph->metrics.horiBearingY -
yc * ft_face->glyph->metrics.height;
DPRINT("Vec %ld,%ld\n", vec.x, vec.y);
FT_Vector_Transform(&vec, &transMat);
if (xc == 0 && yc == 0)
{
left = right = vec.x;
top = bottom = vec.y;
}
else
{
if (vec.x < left) left = vec.x;
else if (vec.x > right) right = vec.x;
if (vec.y < bottom) bottom = vec.y;
else if (vec.y > top) top = vec.y;
}
}
}
left = left & -64;
right = (right + 63) & -64;
bottom = bottom & -64;
top = (top + 63) & -64;
DPRINT("Transformed box: (%d,%d - %d,%d)\n", left, top, right, bottom);
vec.x = ft_face->glyph->metrics.horiAdvance;
vec.y = 0;
FT_Vector_Transform(&vec, &transMat);
gm.gmCellIncX = (vec.x+63) >> 6;
gm.gmCellIncY = -((vec.y+63) >> 6);
}
gm.gmBlackBoxX = (right - left) >> 6;
gm.gmBlackBoxY = (top - bottom) >> 6;
gm.gmptGlyphOrigin.x = left >> 6;
gm.gmptGlyphOrigin.y = top >> 6;
DPRINT("CX %d CY %d BBX %u BBY %u GOX %d GOY %d\n",
gm.gmCellIncX, gm.gmCellIncY,
gm.gmBlackBoxX, gm.gmBlackBoxY,
gm.gmptGlyphOrigin.x, gm.gmptGlyphOrigin.y);
IntUnLockFreeType();
if (iFormat == GGO_METRICS)
{
DPRINT("GGO_METRICS Exit!\n");
*pgm = gm;
return 1; /* FIXME */
}
if (ft_face->glyph->format != ft_glyph_format_outline && iFormat != GGO_BITMAP)
{
DPRINT1("Loaded a bitmap\n");
return GDI_ERROR;
}
switch (iFormat)
{
case GGO_BITMAP:
{
width = gm.gmBlackBoxX;
height = gm.gmBlackBoxY;
pitch = ((width + 31) >> 5) << 2;
needed = pitch * height;
if (!pvBuf || !cjBuf) break;
if (!needed) return GDI_ERROR; /* empty glyph */
if (needed > cjBuf)
return GDI_ERROR;
switch (ft_face->glyph->format)
{
case ft_glyph_format_bitmap:
{
BYTE *src = ft_face->glyph->bitmap.buffer, *dst = pvBuf;
INT w = min( pitch, (ft_face->glyph->bitmap.width + 7) >> 3 );
INT h = min( height, ft_face->glyph->bitmap.rows );
while (h--)
{
RtlCopyMemory(dst, src, w);
src += ft_face->glyph->bitmap.pitch;
dst += pitch;
}
break;
}
case ft_glyph_format_outline:
{
ft_bitmap.width = width;
ft_bitmap.rows = height;
ft_bitmap.pitch = pitch;
ft_bitmap.pixel_mode = FT_PIXEL_MODE_MONO;
ft_bitmap.buffer = pvBuf;
IntLockFreeType();
if (needsTransform)
{
FT_Outline_Transform(&ft_face->glyph->outline, &transMat);
}
FT_Outline_Translate(&ft_face->glyph->outline, -left, -bottom );
/* Note: FreeType will only set 'black' bits for us. */
RtlZeroMemory(pvBuf, needed);
FT_Outline_Get_Bitmap(g_FreeTypeLibrary, &ft_face->glyph->outline, &ft_bitmap);
IntUnLockFreeType();
break;
}
default:
DPRINT1("Loaded glyph format %x\n", ft_face->glyph->format);
return GDI_ERROR;
}
break;
}
case GGO_GRAY2_BITMAP:
case GGO_GRAY4_BITMAP:
case GGO_GRAY8_BITMAP:
{
unsigned int mult, row, col;
BYTE *start, *ptr;
width = gm.gmBlackBoxX;
height = gm.gmBlackBoxY;
pitch = (width + 3) / 4 * 4;
needed = pitch * height;
if (!pvBuf || !cjBuf) break;
if (!needed) return GDI_ERROR; /* empty glyph */
if (needed > cjBuf)
return GDI_ERROR;
switch (ft_face->glyph->format)
{
case ft_glyph_format_bitmap:
{
BYTE *src = ft_face->glyph->bitmap.buffer, *dst = pvBuf;
INT h = min( height, ft_face->glyph->bitmap.rows );
INT x;
while (h--)
{
for (x = 0; (UINT)x < pitch; x++)
{
if (x < ft_face->glyph->bitmap.width)
dst[x] = (src[x / 8] & (1 << ( (7 - (x % 8))))) ? 0xff : 0;
else
dst[x] = 0;
}
src += ft_face->glyph->bitmap.pitch;
dst += pitch;
}
break;
}
case ft_glyph_format_outline:
{
ft_bitmap.width = width;
ft_bitmap.rows = height;
ft_bitmap.pitch = pitch;
ft_bitmap.pixel_mode = FT_PIXEL_MODE_GRAY;
ft_bitmap.buffer = pvBuf;
IntLockFreeType();
if (needsTransform)
{
FT_Outline_Transform(&ft_face->glyph->outline, &transMat);
}
FT_Outline_Translate(&ft_face->glyph->outline, -left, -bottom );
RtlZeroMemory(ft_bitmap.buffer, cjBuf);
FT_Outline_Get_Bitmap(g_FreeTypeLibrary, &ft_face->glyph->outline, &ft_bitmap);
IntUnLockFreeType();
if (iFormat == GGO_GRAY2_BITMAP)
mult = 4;
else if (iFormat == GGO_GRAY4_BITMAP)
mult = 16;
else if (iFormat == GGO_GRAY8_BITMAP)
mult = 64;
else
{
return GDI_ERROR;
}
start = pvBuf;
for (row = 0; row < height; row++)
{
ptr = start;
for (col = 0; col < width; col++, ptr++)
{
*ptr = (((int)*ptr) * mult + 128) / 256;
}
start += pitch;
}
break;
}
default:
DPRINT1("Loaded glyph format %x\n", ft_face->glyph->format);
return GDI_ERROR;
}
break;
}
case GGO_NATIVE:
{
FT_Outline *outline = &ft_face->glyph->outline;
if (cjBuf == 0) pvBuf = NULL; /* This is okay, need cjBuf to allocate. */
IntLockFreeType();
if (needsTransform && pvBuf) FT_Outline_Transform(outline, &transMat);
needed = get_native_glyph_outline(outline, cjBuf, NULL);
if (!pvBuf || !cjBuf)
{
IntUnLockFreeType();
break;
}
if (needed > cjBuf)
{
IntUnLockFreeType();
return GDI_ERROR;
}
get_native_glyph_outline(outline, cjBuf, pvBuf);
IntUnLockFreeType();
break;
}
case GGO_BEZIER:
{
FT_Outline *outline = &ft_face->glyph->outline;
if (cjBuf == 0) pvBuf = NULL;
if (needsTransform && pvBuf)
{
IntLockFreeType();
FT_Outline_Transform(outline, &transMat);
IntUnLockFreeType();
}
needed = get_bezier_glyph_outline(outline, cjBuf, NULL);
if (!pvBuf || !cjBuf)
break;
if (needed > cjBuf)
return GDI_ERROR;
get_bezier_glyph_outline(outline, cjBuf, pvBuf);
break;
}
default:
DPRINT1("Unsupported format %u\n", iFormat);
return GDI_ERROR;
}
DPRINT("ftGdiGetGlyphOutline END and needed %lu\n", needed);
if (gm.gmBlackBoxX == 0)
gm.gmBlackBoxX = 1;
if (gm.gmBlackBoxY == 0)
gm.gmBlackBoxY = 1;
*pgm = gm;
return needed;
}
BOOL
FASTCALL
TextIntGetTextExtentPoint(PDC dc,
PTEXTOBJ TextObj,
LPCWSTR String,
INT Count,
ULONG MaxExtent,
LPINT Fit,
LPINT Dx,
LPSIZE Size,
FLONG fl)
{
PFONTGDI FontGDI;
FT_Face face;
FT_GlyphSlot glyph;
FT_BitmapGlyph realglyph;
INT error, glyph_index, i, previous;
ULONGLONG TotalWidth64 = 0;
BOOL use_kerning;
FT_Render_Mode RenderMode;
BOOLEAN Render;
PMATRIX pmxWorldToDevice;
LOGFONTW *plf;
BOOL EmuBold, EmuItalic;
LONG ascender, descender;
FontGDI = ObjToGDI(TextObj->Font, FONT);
face = FontGDI->SharedFace->Face;
if (NULL != Fit)
{
*Fit = 0;
}
IntLockFreeType();
TextIntUpdateSize(dc, TextObj, FontGDI, FALSE);
plf = &TextObj->logfont.elfEnumLogfontEx.elfLogFont;
EmuBold = EMUBOLD_NEEDED(FontGDI->OriginalWeight, plf->lfWeight);
EmuItalic = (plf->lfItalic && !FontGDI->OriginalItalic);
Render = IntIsFontRenderingEnabled();
if (Render)
RenderMode = IntGetFontRenderMode(plf);
else
RenderMode = FT_RENDER_MODE_MONO;
/* Get the DC's world-to-device transformation matrix */
pmxWorldToDevice = DC_pmxWorldToDevice(dc);
FtSetCoordinateTransform(face, pmxWorldToDevice);
use_kerning = FT_HAS_KERNING(face);
previous = 0;
for (i = 0; i < Count; i++)
{
glyph_index = get_glyph_index_flagged(face, *String, GTEF_INDICES, fl);
if (EmuBold || EmuItalic)
realglyph = NULL;
else
realglyph = ftGdiGlyphCacheGet(face, glyph_index, plf->lfHeight,
RenderMode, pmxWorldToDevice);
if (EmuBold || EmuItalic || !realglyph)
{
if (EmuItalic)
error = FT_Load_Glyph(face, glyph_index, FT_LOAD_NO_BITMAP);
else
error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
if (error)
{
DPRINT1("WARNING: Failed to load and render glyph! [index: %d]\n", glyph_index);
break;
}
glyph = face->glyph;
if (EmuBold || EmuItalic)
{
if (EmuBold)
FT_GlyphSlot_Embolden(glyph);
if (EmuItalic)
FT_GlyphSlot_Oblique(glyph);
realglyph = ftGdiGlyphSet(face, glyph, RenderMode);
}
else
{
realglyph = ftGdiGlyphCacheSet(face,
glyph_index,
plf->lfHeight,
pmxWorldToDevice,
glyph,
RenderMode);
}
if (!realglyph)
{
DPRINT1("Failed to render glyph! [index: %d]\n", glyph_index);
break;
}
}
/* Retrieve kerning distance */
if (use_kerning && previous && glyph_index)
{
FT_Vector delta;
FT_Get_Kerning(face, previous, glyph_index, 0, &delta);
TotalWidth64 += delta.x;
}
TotalWidth64 += realglyph->root.advance.x >> 10;
if (((TotalWidth64 + 32) >> 6) <= MaxExtent && NULL != Fit)
{
*Fit = i + 1;
}
if (NULL != Dx)
{
Dx[i] = (TotalWidth64 + 32) >> 6;
}
/* Bold and italic do not use the cache */
if (EmuBold || EmuItalic)
{
FT_Done_Glyph((FT_Glyph)realglyph);
}
previous = glyph_index;
String++;
}
ASSERT(FontGDI->Magic == FONTGDI_MAGIC);
ascender = FontGDI->tmAscent; /* Units above baseline */
descender = FontGDI->tmDescent; /* Units below baseline */
IntUnLockFreeType();
Size->cx = (TotalWidth64 + 32) >> 6;
Size->cy = ascender + descender;
return TRUE;
}
INT
FASTCALL
ftGdiGetTextCharsetInfo(
PDC Dc,
LPFONTSIGNATURE lpSig,
DWORD dwFlags)
{
PDC_ATTR pdcattr;
UINT Ret = DEFAULT_CHARSET;
INT i;
HFONT hFont;
PTEXTOBJ TextObj;
PFONTGDI FontGdi;
FONTSIGNATURE fs;
TT_OS2 *pOS2;
FT_Face Face;
CHARSETINFO csi;
DWORD cp, fs0;
USHORT usACP, usOEM;
pdcattr = Dc->pdcattr;
hFont = pdcattr->hlfntNew;
TextObj = RealizeFontInit(hFont);
if (!TextObj)
{
EngSetLastError(ERROR_INVALID_HANDLE);
return Ret;
}
FontGdi = ObjToGDI(TextObj->Font, FONT);
Face = FontGdi->SharedFace->Face;
TEXTOBJ_UnlockText(TextObj);
memset(&fs, 0, sizeof(FONTSIGNATURE));
IntLockFreeType();
pOS2 = FT_Get_Sfnt_Table(Face, ft_sfnt_os2);
if (NULL != pOS2)
{
fs.fsCsb[0] = pOS2->ulCodePageRange1;
fs.fsCsb[1] = pOS2->ulCodePageRange2;
fs.fsUsb[0] = pOS2->ulUnicodeRange1;
fs.fsUsb[1] = pOS2->ulUnicodeRange2;
fs.fsUsb[2] = pOS2->ulUnicodeRange3;
fs.fsUsb[3] = pOS2->ulUnicodeRange4;
if (pOS2->version == 0)
{
FT_UInt dummy;
if (FT_Get_First_Char( Face, &dummy ) < 0x100)
fs.fsCsb[0] |= FS_LATIN1;
else
fs.fsCsb[0] |= FS_SYMBOL;
}
}
pOS2 = NULL;
IntUnLockFreeType();
DPRINT("Csb 1=%x 0=%x\n", fs.fsCsb[1],fs.fsCsb[0]);
if (fs.fsCsb[0] == 0)
{ /* Let's see if we can find any interesting cmaps */
for (i = 0; i < Face->num_charmaps; i++)
{
switch (Face->charmaps[i]->encoding)
{
case FT_ENCODING_UNICODE:
case FT_ENCODING_APPLE_ROMAN:
fs.fsCsb[0] |= FS_LATIN1;
break;
case FT_ENCODING_MS_SYMBOL:
fs.fsCsb[0] |= FS_SYMBOL;
break;
default:
break;
}
}
}
if (lpSig)
{
RtlCopyMemory(lpSig, &fs, sizeof(FONTSIGNATURE));
}
RtlGetDefaultCodePage(&usACP, &usOEM);
cp = usACP;
if (IntTranslateCharsetInfo(&cp, &csi, TCI_SRCCODEPAGE))
if (csi.fs.fsCsb[0] & fs.fsCsb[0])
{
DPRINT("Hit 1\n");
Ret = csi.ciCharset;
goto Exit;
}
for (i = 0; i < MAXTCIINDEX; i++)
{
fs0 = 1L << i;
if (fs.fsCsb[0] & fs0)
{
if (IntTranslateCharsetInfo(&fs0, &csi, TCI_SRCFONTSIG))
{
// *cp = csi.ciACP;
DPRINT("Hit 2\n");
Ret = csi.ciCharset;
goto Exit;
}
else
DPRINT1("TCI failing on %x\n", fs0);
}
}
Exit:
DPRINT("CharSet %u CodePage %u\n", csi.ciCharset, csi.ciACP);
return (MAKELONG(csi.ciACP, csi.ciCharset));
}
DWORD
FASTCALL
ftGetFontUnicodeRanges(PFONTGDI Font, PGLYPHSET glyphset)
{
DWORD size = 0;
DWORD num_ranges = 0;
FT_Face face = Font->SharedFace->Face;
if (face->charmap->encoding == FT_ENCODING_UNICODE)
{
FT_UInt glyph_code = 0;
FT_ULong char_code, char_code_prev;
char_code_prev = char_code = FT_Get_First_Char(face, &glyph_code);
DPRINT("Face encoding FT_ENCODING_UNICODE, number of glyphs %ld, first glyph %u, first char %04lx\n",
face->num_glyphs, glyph_code, char_code);
if (!glyph_code) return 0;
if (glyphset)
{
glyphset->ranges[0].wcLow = (USHORT)char_code;
glyphset->ranges[0].cGlyphs = 0;
glyphset->cGlyphsSupported = 0;
}
num_ranges = 1;
while (glyph_code)
{
if (char_code < char_code_prev)
{
DPRINT1("Expected increasing char code from FT_Get_Next_Char\n");
return 0;
}
if (char_code - char_code_prev > 1)
{
num_ranges++;
if (glyphset)
{
glyphset->ranges[num_ranges - 1].wcLow = (USHORT)char_code;
glyphset->ranges[num_ranges - 1].cGlyphs = 1;
glyphset->cGlyphsSupported++;
}
}
else if (glyphset)
{
glyphset->ranges[num_ranges - 1].cGlyphs++;
glyphset->cGlyphsSupported++;
}
char_code_prev = char_code;
char_code = FT_Get_Next_Char(face, char_code, &glyph_code);
}
}
else
DPRINT1("Encoding %i not supported\n", face->charmap->encoding);
size = sizeof(GLYPHSET) + sizeof(WCRANGE) * (num_ranges - 1);
if (glyphset)
{
glyphset->cbThis = size;
glyphset->cRanges = num_ranges;
glyphset->flAccel = 0;
}
return size;
}
BOOL
FASTCALL
ftGdiGetTextMetricsW(
HDC hDC,
PTMW_INTERNAL ptmwi)
{
PDC dc;
PDC_ATTR pdcattr;
PTEXTOBJ TextObj;
PFONTGDI FontGDI;
FT_Face Face;
TT_OS2 *pOS2;
TT_HoriHeader *pHori;
FT_WinFNT_HeaderRec Win;
ULONG Error;
NTSTATUS Status = STATUS_SUCCESS;
LOGFONTW *plf;
if (!ptmwi)
{
EngSetLastError(STATUS_INVALID_PARAMETER);
return FALSE;
}
RtlZeroMemory(ptmwi, sizeof(TMW_INTERNAL));
if (!(dc = DC_LockDc(hDC)))
{
EngSetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
pdcattr = dc->pdcattr;
TextObj = RealizeFontInit(pdcattr->hlfntNew);
if (NULL != TextObj)
{
plf = &TextObj->logfont.elfEnumLogfontEx.elfLogFont;
FontGDI = ObjToGDI(TextObj->Font, FONT);
Face = FontGDI->SharedFace->Face;
IntLockFreeType();
Error = IntRequestFontSize(dc, FontGDI, plf->lfWidth, plf->lfHeight);
FtSetCoordinateTransform(Face, DC_pmxWorldToDevice(dc));
IntUnLockFreeType();
if (0 != Error)
{
DPRINT1("Error in setting pixel sizes: %u\n", Error);
Status = STATUS_UNSUCCESSFUL;
}
else
{
FT_Face Face = FontGDI->SharedFace->Face;
Status = STATUS_SUCCESS;
IntLockFreeType();
pOS2 = FT_Get_Sfnt_Table(Face, ft_sfnt_os2);
if (NULL == pOS2)
{
DPRINT1("Can't find OS/2 table - not TT font?\n");
Status = STATUS_INTERNAL_ERROR;
}
pHori = FT_Get_Sfnt_Table(Face, ft_sfnt_hhea);
if (NULL == pHori)
{
DPRINT1("Can't find HHEA table - not TT font?\n");
Status = STATUS_INTERNAL_ERROR;
}
Error = FT_Get_WinFNT_Header(Face, &Win);
if (NT_SUCCESS(Status) || !Error)
{
FillTM(&ptmwi->TextMetric, FontGDI, pOS2, pHori, !Error ? &Win : 0);
/* FIXME: Fill Diff member */
}
IntUnLockFreeType();
}
TEXTOBJ_UnlockText(TextObj);
}
else
{
Status = STATUS_INVALID_HANDLE;
}
DC_UnlockDc(dc);
if (!NT_SUCCESS(Status))
{
SetLastNtError(Status);
return FALSE;
}
return TRUE;
}
DWORD
FASTCALL
ftGdiGetFontData(
PFONTGDI FontGdi,
DWORD Table,
DWORD Offset,
PVOID Buffer,
DWORD Size)
{
DWORD Result = GDI_ERROR;
FT_Face Face = FontGdi->SharedFace->Face;
IntLockFreeType();
if (FT_IS_SFNT(Face))
{
if (Table)
Table = Table >> 24 | Table << 24 | (Table >> 8 & 0xFF00) |
(Table << 8 & 0xFF0000);
if (!Buffer) Size = 0;
if (Buffer && Size)
{
FT_Error Error;
FT_ULong Needed = 0;
Error = FT_Load_Sfnt_Table(Face, Table, Offset, NULL, &Needed);
if ( !Error && Needed < Size) Size = Needed;
}
if (!FT_Load_Sfnt_Table(Face, Table, Offset, Buffer, &Size))
Result = Size;
}
IntUnLockFreeType();
return Result;
}
#define GOT_PENALTY(name, value) Penalty += (value)
// NOTE: See Table 1. of https://msdn.microsoft.com/en-us/library/ms969909.aspx
static UINT
GetFontPenalty(const LOGFONTW * LogFont,
const OUTLINETEXTMETRICW * Otm,
const char * style_name)
{
ULONG Penalty = 0;
BYTE Byte;
LONG Long;
BOOL fNeedScaling = FALSE;
const BYTE UserCharSet = CharSetFromLangID(gusLanguageID);
const TEXTMETRICW * TM = &Otm->otmTextMetrics;
WCHAR* ActualNameW;
ASSERT(Otm);
ASSERT(LogFont);
/* FIXME: IntSizeSynth Penalty 20 */
/* FIXME: SmallPenalty Penalty 1 */
/* FIXME: FaceNameSubst Penalty 500 */
Byte = LogFont->lfCharSet;
if (Byte != TM->tmCharSet)
{
if (Byte != DEFAULT_CHARSET && Byte != ANSI_CHARSET)
{
/* CharSet Penalty 65000 */
/* Requested charset does not match the candidate's. */
GOT_PENALTY("CharSet", 65000);
}
else
{
if (UserCharSet != TM->tmCharSet)
{
/* UNDOCUMENTED: Not user language */
GOT_PENALTY("UNDOCUMENTED:NotUserLanguage", 100);
if (ANSI_CHARSET != TM->tmCharSet)
{
/* UNDOCUMENTED: Not ANSI charset */
GOT_PENALTY("UNDOCUMENTED:NotAnsiCharSet", 100);
}
}
}
}
Byte = LogFont->lfOutPrecision;
switch (Byte)
{
case OUT_DEFAULT_PRECIS:
/* nothing to do */
break;
case OUT_DEVICE_PRECIS:
if (!(TM->tmPitchAndFamily & TMPF_DEVICE) ||
!(TM->tmPitchAndFamily & (TMPF_VECTOR | TMPF_TRUETYPE)))
{
/* OutputPrecision Penalty 19000 */
/* Requested OUT_STROKE_PRECIS, but the device can't do it
or the candidate is not a vector font. */
GOT_PENALTY("OutputPrecision", 19000);
}
break;
default:
if (TM->tmPitchAndFamily & (TMPF_VECTOR | TMPF_TRUETYPE))
{
/* OutputPrecision Penalty 19000 */
/* Or OUT_STROKE_PRECIS not requested, and the candidate
is a vector font that requires GDI support. */
GOT_PENALTY("OutputPrecision", 19000);
}
break;
}
Byte = (LogFont->lfPitchAndFamily & 0x0F);
if (Byte == DEFAULT_PITCH)
Byte = VARIABLE_PITCH;
if (Byte == FIXED_PITCH)
{
if (TM->tmPitchAndFamily & _TMPF_VARIABLE_PITCH)
{
/* FixedPitch Penalty 15000 */
/* Requested a fixed pitch font, but the candidate is a
variable pitch font. */
GOT_PENALTY("FixedPitch", 15000);
}
}
if (Byte == VARIABLE_PITCH)
{
if (!(TM->tmPitchAndFamily & _TMPF_VARIABLE_PITCH))
{
/* PitchVariable Penalty 350 */
/* Requested a variable pitch font, but the candidate is not a
variable pitch font. */
GOT_PENALTY("PitchVariable", 350);
}
}
Byte = (LogFont->lfPitchAndFamily & 0x0F);
if (Byte == DEFAULT_PITCH)
{
if (!(TM->tmPitchAndFamily & _TMPF_VARIABLE_PITCH))
{
/* DefaultPitchFixed Penalty 1 */
/* Requested DEFAULT_PITCH, but the candidate is fixed pitch. */
GOT_PENALTY("DefaultPitchFixed", 1);
}
}
ActualNameW = (WCHAR*)((ULONG_PTR)Otm + (ULONG_PTR)Otm->otmpFamilyName);
if (LogFont->lfFaceName[0] != UNICODE_NULL)
{
BOOL Found = FALSE;
/* localized family name */
if (!Found)
{
Found = (_wcsicmp(LogFont->lfFaceName, ActualNameW) == 0);
}
/* localized full name */
if (!Found)
{
ActualNameW = (WCHAR*)((ULONG_PTR)Otm + (ULONG_PTR)Otm->otmpFaceName);
Found = (_wcsicmp(LogFont->lfFaceName, ActualNameW) == 0);
}
if (!Found)
{
/* FaceName Penalty 10000 */
/* Requested a face name, but the candidate's face name
does not match. */
GOT_PENALTY("FaceName", 10000);
}
}
Byte = (LogFont->lfPitchAndFamily & 0xF0);
if (Byte != FF_DONTCARE)
{
if (Byte != (TM->tmPitchAndFamily & 0xF0))
{
/* Family Penalty 9000 */
/* Requested a family, but the candidate's family is different. */
GOT_PENALTY("Family", 9000);
}
}
if ((TM->tmPitchAndFamily & 0xF0) == FF_DONTCARE)
{
/* FamilyUnknown Penalty 8000 */
/* Requested a family, but the candidate has no family. */
GOT_PENALTY("FamilyUnknown", 8000);
}
/* Is the candidate a non-vector font? */
if (!(TM->tmPitchAndFamily & (TMPF_TRUETYPE | TMPF_VECTOR)))
{
/* Is lfHeight specified? */
if (LogFont->lfHeight != 0)
{
if (labs(LogFont->lfHeight) < TM->tmHeight)
{
/* HeightBigger Penalty 600 */
/* The candidate is a nonvector font and is bigger than the
requested height. */
GOT_PENALTY("HeightBigger", 600);
/* HeightBiggerDifference Penalty 150 */
/* The candidate is a raster font and is larger than the
requested height. Penalty * height difference */
GOT_PENALTY("HeightBiggerDifference", 150 * labs(TM->tmHeight - labs(LogFont->lfHeight)));
fNeedScaling = TRUE;
}
if (TM->tmHeight < labs(LogFont->lfHeight))
{
/* HeightSmaller Penalty 150 */
/* The candidate is a raster font and is smaller than the
requested height. Penalty * height difference */
GOT_PENALTY("HeightSmaller", 150 * labs(TM->tmHeight - labs(LogFont->lfHeight)));
fNeedScaling = TRUE;
}
}
}
switch (LogFont->lfPitchAndFamily & 0xF0)
{
case FF_ROMAN: case FF_MODERN: case FF_SWISS:
switch (TM->tmPitchAndFamily & 0xF0)
{
case FF_DECORATIVE: case FF_SCRIPT:
/* FamilyUnlikely Penalty 50 */
/* Requested a roman/modern/swiss family, but the
candidate is decorative/script. */
GOT_PENALTY("FamilyUnlikely", 50);
break;
default:
break;
}
break;
case FF_DECORATIVE: case FF_SCRIPT:
switch (TM->tmPitchAndFamily & 0xF0)
{
case FF_ROMAN: case FF_MODERN: case FF_SWISS:
/* FamilyUnlikely Penalty 50 */
/* Or requested decorative/script, and the candidate is
roman/modern/swiss. */
GOT_PENALTY("FamilyUnlikely", 50);
break;
default:
break;
}
default:
break;
}
if (LogFont->lfWidth != 0)
{
if (LogFont->lfWidth != TM->tmAveCharWidth)
{
/* Width Penalty 50 */
/* Requested a nonzero width, but the candidate's width
doesn't match. Penalty * width difference */
GOT_PENALTY("Width", 50 * labs(LogFont->lfWidth - TM->tmAveCharWidth));
if (!(TM->tmPitchAndFamily & (TMPF_TRUETYPE | TMPF_VECTOR)))
fNeedScaling = TRUE;
}
}
if (fNeedScaling)
{
/* SizeSynth Penalty 50 */
/* The candidate is a raster font that needs scaling by GDI. */
GOT_PENALTY("SizeSynth", 50);
}
if (!LogFont->lfItalic && TM->tmItalic)
{
/* Italic Penalty 4 */
/* Requested font and candidate font do not agree on italic status,
and the desired result cannot be simulated. */
/* Adjusted to 40 to satisfy (Oblique Penalty > Book Penalty). */
GOT_PENALTY("Italic", 40);
}
else if (LogFont->lfItalic && !TM->tmItalic)
{
/* ItalicSim Penalty 1 */
/* Requested italic font but the candidate is not italic,
although italics can be simulated. */
GOT_PENALTY("ItalicSim", 1);
}
if (LogFont->lfOutPrecision == OUT_TT_PRECIS)
{
if (!(TM->tmPitchAndFamily & TMPF_TRUETYPE))
{
/* NotTrueType Penalty 4 */
/* Requested OUT_TT_PRECIS, but the candidate is not a
TrueType font. */
GOT_PENALTY("NotTrueType", 4);
}
}
Long = LogFont->lfWeight;
if (LogFont->lfWeight == FW_DONTCARE)
Long = FW_NORMAL;
if (Long != TM->tmWeight)
{
/* Weight Penalty 3 */
/* The candidate's weight does not match the requested weight.
Penalty * (weight difference/10) */
GOT_PENALTY("Weight", 3 * (labs(Long - TM->tmWeight) / 10));
}
if (!LogFont->lfUnderline && TM->tmUnderlined)
{
/* Underline Penalty 3 */
/* Requested font has no underline, but the candidate is
underlined. */
GOT_PENALTY("Underline", 3);
}
if (!LogFont->lfStrikeOut && TM->tmStruckOut)
{
/* StrikeOut Penalty 3 */
/* Requested font has no strike-out, but the candidate is
struck out. */
GOT_PENALTY("StrikeOut", 3);
}
/* Is the candidate a non-vector font? */
if (!(TM->tmPitchAndFamily & (TMPF_TRUETYPE | TMPF_VECTOR)))
{
if (LogFont->lfHeight != 0 && TM->tmHeight < LogFont->lfHeight)
{
/* VectorHeightSmaller Penalty 2 */
/* Candidate is a vector font that is smaller than the
requested height. Penalty * height difference */
GOT_PENALTY("VectorHeightSmaller", 2 * labs(TM->tmHeight - LogFont->lfHeight));
}
if (LogFont->lfHeight != 0 && TM->tmHeight > LogFont->lfHeight)
{
/* VectorHeightBigger Penalty 1 */
/* Candidate is a vector font that is bigger than the
requested height. Penalty * height difference */
GOT_PENALTY("VectorHeightBigger", 1 * labs(TM->tmHeight - LogFont->lfHeight));
}
}
if (!(TM->tmPitchAndFamily & TMPF_DEVICE))
{
/* DeviceFavor Penalty 2 */
/* Extra penalty for all nondevice fonts. */
GOT_PENALTY("DeviceFavor", 2);
}
if (TM->tmAveCharWidth >= 5 && TM->tmHeight >= 5)
{
if (TM->tmAveCharWidth / TM->tmHeight >= 3)
{
/* Aspect Penalty 30 */
/* The aspect rate is >= 3. It seems like a bad font. */
GOT_PENALTY("Aspect", ((TM->tmAveCharWidth / TM->tmHeight) - 2) * 30);
}
else if (TM->tmHeight / TM->tmAveCharWidth >= 3)
{
/* Aspect Penalty 30 */
/* The aspect rate is >= 3. It seems like a bad font. */
GOT_PENALTY("Aspect", ((TM->tmHeight / TM->tmAveCharWidth) - 2) * 30);
}
}
if (Penalty < 200)
{
DPRINT("WARNING: Penalty:%ld < 200: RequestedNameW:%ls, "
"ActualNameW:%ls, lfCharSet:%d, lfWeight:%ld, "
"tmCharSet:%d, tmWeight:%ld\n",
Penalty, LogFont->lfFaceName, ActualNameW,
LogFont->lfCharSet, LogFont->lfWeight,
TM->tmCharSet, TM->tmWeight);
}
return Penalty; /* success */
}
#undef GOT_PENALTY
static __inline VOID
FindBestFontFromList(FONTOBJ **FontObj, ULONG *MatchPenalty,
const LOGFONTW *LogFont,
const PLIST_ENTRY Head)
{
ULONG Penalty;
PLIST_ENTRY Entry;
PFONT_ENTRY CurrentEntry;
FONTGDI *FontGDI;
OUTLINETEXTMETRICW *Otm = NULL;
UINT OtmSize, OldOtmSize = 0;
FT_Face Face;
ASSERT(FontObj);
ASSERT(MatchPenalty);
ASSERT(LogFont);
ASSERT(Head);
/* Start with a pretty big buffer */
OldOtmSize = 0x200;
Otm = ExAllocatePoolWithTag(PagedPool, OldOtmSize, GDITAG_TEXT);
/* get the FontObj of lowest penalty */
for (Entry = Head->Flink; Entry != Head; Entry = Entry->Flink)
{
CurrentEntry = CONTAINING_RECORD(Entry, FONT_ENTRY, ListEntry);
FontGDI = CurrentEntry->Font;
ASSERT(FontGDI);
Face = FontGDI->SharedFace->Face;
/* get text metrics */
OtmSize = IntGetOutlineTextMetrics(FontGDI, 0, NULL);
if (OtmSize > OldOtmSize)
{
if (Otm)
ExFreePoolWithTag(Otm, GDITAG_TEXT);
Otm = ExAllocatePoolWithTag(PagedPool, OtmSize, GDITAG_TEXT);
}
/* update FontObj if lowest penalty */
if (Otm)
{
IntLockFreeType();
IntRequestFontSize(NULL, FontGDI, LogFont->lfWidth, LogFont->lfHeight);
IntUnLockFreeType();
OtmSize = IntGetOutlineTextMetrics(FontGDI, OtmSize, Otm);
if (!OtmSize)
continue;
OldOtmSize = OtmSize;
Penalty = GetFontPenalty(LogFont, Otm, Face->style_name);
if (*MatchPenalty == 0xFFFFFFFF || Penalty < *MatchPenalty)
{
*FontObj = GDIToObj(FontGDI, FONT);
*MatchPenalty = Penalty;
}
}
}
if (Otm)
ExFreePoolWithTag(Otm, GDITAG_TEXT);
}
static
VOID
FASTCALL
IntFontType(PFONTGDI Font)
{
PS_FontInfoRec psfInfo;
FT_ULong tmp_size = 0;
FT_Face Face = Font->SharedFace->Face;
ASSERT_FREETYPE_LOCK_NOT_HELD();
IntLockFreeType();
if (FT_HAS_MULTIPLE_MASTERS(Face))
Font->FontObj.flFontType |= FO_MULTIPLEMASTER;
if (FT_HAS_VERTICAL(Face))
Font->FontObj.flFontType |= FO_VERT_FACE;
if (!FT_IS_SCALABLE(Face))
Font->FontObj.flFontType |= FO_TYPE_RASTER;
if (FT_IS_SFNT(Face))
{
Font->FontObj.flFontType |= FO_TYPE_TRUETYPE;
if (FT_Get_Sfnt_Table(Face, ft_sfnt_post))
Font->FontObj.flFontType |= FO_POSTSCRIPT;
}
if (!FT_Get_PS_Font_Info(Face, &psfInfo ))
{
Font->FontObj.flFontType |= FO_POSTSCRIPT;
}
/* Check for the presence of the 'CFF ' table to check if the font is Type1 */
if (!FT_Load_Sfnt_Table(Face, TTAG_CFF, 0, NULL, &tmp_size))
{
Font->FontObj.flFontType |= (FO_CFF|FO_POSTSCRIPT);
}
IntUnLockFreeType();
}
static BOOL
MatchFontName(PSHARED_FACE SharedFace, LPCWSTR lfFaceName, FT_UShort NameID, FT_UShort LangID)
{
NTSTATUS Status;
UNICODE_STRING Name1, Name2;
if (lfFaceName[0] == UNICODE_NULL)
return FALSE;
RtlInitUnicodeString(&Name1, lfFaceName);
RtlInitUnicodeString(&Name2, NULL);
Status = IntGetFontLocalizedName(&Name2, SharedFace, NameID, LangID);
if (NT_SUCCESS(Status))
{
if (RtlCompareUnicodeString(&Name1, &Name2, TRUE) == 0)
{
RtlFreeUnicodeString(&Name2);
return TRUE;
}
RtlFreeUnicodeString(&Name2);
}
return FALSE;
}
static BOOL
MatchFontNames(PSHARED_FACE SharedFace, LPCWSTR lfFaceName)
{
if (MatchFontName(SharedFace, lfFaceName, TT_NAME_ID_FONT_FAMILY, LANG_ENGLISH) ||
MatchFontName(SharedFace, lfFaceName, TT_NAME_ID_FULL_NAME, LANG_ENGLISH))
{
return TRUE;
}
if (PRIMARYLANGID(gusLanguageID) != LANG_ENGLISH)
{
if (MatchFontName(SharedFace, lfFaceName, TT_NAME_ID_FONT_FAMILY, gusLanguageID) ||
MatchFontName(SharedFace, lfFaceName, TT_NAME_ID_FULL_NAME, gusLanguageID))
{
return TRUE;
}
}
return FALSE;
}
NTSTATUS
FASTCALL
TextIntRealizeFont(HFONT FontHandle, PTEXTOBJ pTextObj)
{
NTSTATUS Status = STATUS_SUCCESS;
PTEXTOBJ TextObj;
PPROCESSINFO Win32Process;
ULONG MatchPenalty;
LOGFONTW *pLogFont;
LOGFONTW SubstitutedLogFont;
if (!pTextObj)
{
TextObj = TEXTOBJ_LockText(FontHandle);
if (NULL == TextObj)
{
return STATUS_INVALID_HANDLE;
}
if (TextObj->fl & TEXTOBJECT_INIT)
{
TEXTOBJ_UnlockText(TextObj);
return STATUS_SUCCESS;
}
}
else
{
TextObj = pTextObj;
}
pLogFont = &TextObj->logfont.elfEnumLogfontEx.elfLogFont;
/* substitute */
SubstitutedLogFont = *pLogFont;
DPRINT("Font '%S,%u' is substituted by: ", pLogFont->lfFaceName, pLogFont->lfCharSet);
SubstituteFontRecurse(&SubstitutedLogFont);
DPRINT("'%S,%u'.\n", SubstitutedLogFont.lfFaceName, SubstitutedLogFont.lfCharSet);
MatchPenalty = 0xFFFFFFFF;
TextObj->Font = NULL;
Win32Process = PsGetCurrentProcessWin32Process();
/* Search private fonts */
IntLockProcessPrivateFonts(Win32Process);
FindBestFontFromList(&TextObj->Font, &MatchPenalty, &SubstitutedLogFont,
&Win32Process->PrivateFontListHead);
IntUnLockProcessPrivateFonts(Win32Process);
/* Search system fonts */
IntLockGlobalFonts();
FindBestFontFromList(&TextObj->Font, &MatchPenalty, &SubstitutedLogFont,
&g_FontListHead);
IntUnLockGlobalFonts();
if (NULL == TextObj->Font)
{
DPRINT1("Request font %S not found, no fonts loaded at all\n",
pLogFont->lfFaceName);
Status = STATUS_NOT_FOUND;
}
else
{
UNICODE_STRING Name;
PFONTGDI FontGdi = ObjToGDI(TextObj->Font, FONT);
PSHARED_FACE SharedFace = FontGdi->SharedFace;
IntLockFreeType();
IntRequestFontSize(NULL, FontGdi, pLogFont->lfWidth, pLogFont->lfHeight);
IntUnLockFreeType();
TextObj->TextFace[0] = UNICODE_NULL;
if (MatchFontNames(SharedFace, SubstitutedLogFont.lfFaceName))
{
RtlStringCchCopyW(TextObj->TextFace, _countof(TextObj->TextFace), pLogFont->lfFaceName);
}
else
{
RtlInitUnicodeString(&Name, NULL);
Status = IntGetFontLocalizedName(&Name, SharedFace, TT_NAME_ID_FONT_FAMILY, gusLanguageID);
if (NT_SUCCESS(Status))
{
/* truncated copy */
IntUnicodeStringToBuffer(TextObj->TextFace, sizeof(TextObj->TextFace), &Name);
RtlFreeUnicodeString(&Name);
}
}
// Need hdev, when freetype is loaded need to create DEVOBJ for
// Consumer and Producer.
TextObj->Font->iUniq = 1; // Now it can be cached.
IntFontType(FontGdi);
FontGdi->flType = TextObj->Font->flFontType;
FontGdi->RequestUnderline = pLogFont->lfUnderline ? 0xFF : 0;
FontGdi->RequestStrikeOut = pLogFont->lfStrikeOut ? 0xFF : 0;
FontGdi->RequestItalic = pLogFont->lfItalic ? 0xFF : 0;
if (pLogFont->lfWeight != FW_DONTCARE)
FontGdi->RequestWeight = pLogFont->lfWeight;
else
FontGdi->RequestWeight = FW_NORMAL;
TextObj->fl |= TEXTOBJECT_INIT;
Status = STATUS_SUCCESS;
}
if (!pTextObj) TEXTOBJ_UnlockText(TextObj);
ASSERT((NT_SUCCESS(Status) ^ (NULL == TextObj->Font)) != 0);
return Status;
}
static
BOOL
FASTCALL
IntGetFullFileName(
POBJECT_NAME_INFORMATION NameInfo,
ULONG Size,
PUNICODE_STRING FileName)
{
NTSTATUS Status;
OBJECT_ATTRIBUTES ObjectAttributes;
HANDLE hFile;
IO_STATUS_BLOCK IoStatusBlock;
ULONG Desired;
InitializeObjectAttributes(&ObjectAttributes,
FileName,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL,
NULL);
Status = ZwOpenFile(
&hFile,
0, // FILE_READ_ATTRIBUTES,
&ObjectAttributes,
&IoStatusBlock,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
0);
if (!NT_SUCCESS(Status))
{
DPRINT("ZwOpenFile() failed (Status = 0x%lx)\n", Status);
return FALSE;
}
Status = ZwQueryObject(hFile, ObjectNameInformation, NameInfo, Size, &Desired);
ZwClose(hFile);
if (!NT_SUCCESS(Status))
{
DPRINT("ZwQueryObject() failed (Status = %lx)\n", Status);
return FALSE;
}
return TRUE;
}
static BOOL
EqualFamilyInfo(const FONTFAMILYINFO *pInfo1, const FONTFAMILYINFO *pInfo2)
{
const ENUMLOGFONTEXW *pLog1 = &pInfo1->EnumLogFontEx;
const ENUMLOGFONTEXW *pLog2 = &pInfo2->EnumLogFontEx;
const LOGFONTW *plf1 = &pLog1->elfLogFont;
const LOGFONTW *plf2 = &pLog2->elfLogFont;
if (_wcsicmp(plf1->lfFaceName, plf2->lfFaceName) != 0)
{
return FALSE;
}
if (_wcsicmp(pLog1->elfStyle, pLog2->elfStyle) != 0)
{
return FALSE;
}
return TRUE;
}
static VOID
IntAddNameFromFamInfo(LPWSTR psz, FONTFAMILYINFO *FamInfo)
{
wcscat(psz, FamInfo->EnumLogFontEx.elfLogFont.lfFaceName);
if (FamInfo->EnumLogFontEx.elfStyle[0] &&
_wcsicmp(FamInfo->EnumLogFontEx.elfStyle, L"Regular") != 0)
{
wcscat(psz, L" ");
wcscat(psz, FamInfo->EnumLogFontEx.elfStyle);
}
}
BOOL
FASTCALL
IntGdiGetFontResourceInfo(
PUNICODE_STRING FileName,
PVOID pBuffer,
DWORD *pdwBytes,
DWORD dwType)
{
UNICODE_STRING EntryFileName;
POBJECT_NAME_INFORMATION NameInfo1 = NULL, NameInfo2 = NULL;
PLIST_ENTRY ListEntry;
PFONT_ENTRY FontEntry;
ULONG Size, i, Count;
LPBYTE pbBuffer;
BOOL IsEqual;
FONTFAMILYINFO *FamInfo;
const ULONG MaxFamInfo = 64;
const ULONG MAX_FAM_INFO_BYTES = sizeof(FONTFAMILYINFO) * MaxFamInfo;
BOOL bSuccess;
const ULONG NAMEINFO_SIZE = sizeof(OBJECT_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR);
DPRINT("IntGdiGetFontResourceInfo: dwType == %lu\n", dwType);
do
{
/* Create buffer for full path name */
NameInfo1 = ExAllocatePoolWithTag(PagedPool, NAMEINFO_SIZE, TAG_FINF);
if (!NameInfo1)
break;
/* Get the full path name */
if (!IntGetFullFileName(NameInfo1, NAMEINFO_SIZE, FileName))
break;
/* Create a buffer for the entries' names */
NameInfo2 = ExAllocatePoolWithTag(PagedPool, NAMEINFO_SIZE, TAG_FINF);
if (!NameInfo2)
break;
FamInfo = ExAllocatePoolWithTag(PagedPool, MAX_FAM_INFO_BYTES, TAG_FINF);
} while (0);
if (!NameInfo1 || !NameInfo2 || !FamInfo)
{
if (NameInfo2)
ExFreePoolWithTag(NameInfo2, TAG_FINF);
if (NameInfo1)
ExFreePoolWithTag(NameInfo1, TAG_FINF);
EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
}
Count = 0;
/* Try to find the pathname in the global font list */
IntLockGlobalFonts();
for (ListEntry = g_FontListHead.Flink; ListEntry != &g_FontListHead;
ListEntry = ListEntry->Flink)
{
FontEntry = CONTAINING_RECORD(ListEntry, FONT_ENTRY, ListEntry);
if (FontEntry->Font->Filename == NULL)
continue;
RtlInitUnicodeString(&EntryFileName , FontEntry->Font->Filename);
if (!IntGetFullFileName(NameInfo2, NAMEINFO_SIZE, &EntryFileName))
continue;
if (!RtlEqualUnicodeString(&NameInfo1->Name, &NameInfo2->Name, FALSE))
continue;
IsEqual = FALSE;
FontFamilyFillInfo(&FamInfo[Count], FontEntry->FaceName.Buffer,
NULL, FontEntry->Font);
for (i = 0; i < Count; ++i)
{
if (EqualFamilyInfo(&FamInfo[i], &FamInfo[Count]))
{
IsEqual = TRUE;
break;
}
}
if (!IsEqual)
{
/* Found */
++Count;
if (Count >= MaxFamInfo)
break;
}
}
IntUnLockGlobalFonts();
/* Free the buffers */
ExFreePoolWithTag(NameInfo1, TAG_FINF);
ExFreePoolWithTag(NameInfo2, TAG_FINF);
if (Count == 0 && dwType != 5)
{
/* Font could not be found in system table
dwType == 5 will still handle this */
ExFreePoolWithTag(FamInfo, TAG_FINF);
return FALSE;
}
bSuccess = FALSE;
switch (dwType)
{
case 0: /* FIXME: Returns 1 or 2, don't know what this is atm */
Size = sizeof(DWORD);
if (*pdwBytes == 0)
{
*pdwBytes = Size;
bSuccess = TRUE;
}
else if (pBuffer)
{
if (*pdwBytes >= Size)
{
*(DWORD*)pBuffer = Count;
}
*pdwBytes = Size;
bSuccess = TRUE;
}
break;
case 1: /* copy the font title */
/* calculate the required size */
Size = 0;
for (i = 0; i < Count; ++i)
{
if (i > 0)
Size += 3; /* " & " */
Size += wcslen(FamInfo[i].EnumLogFontEx.elfLogFont.lfFaceName);
if (FamInfo[i].EnumLogFontEx.elfStyle[0] &&
_wcsicmp(FamInfo[i].EnumLogFontEx.elfStyle, L"Regular") != 0)
{
Size += 1 + wcslen(FamInfo[i].EnumLogFontEx.elfStyle);
}
}
Size += 2; /* "\0\0" */
Size *= sizeof(WCHAR);
if (*pdwBytes == 0)
{
*pdwBytes = Size;
bSuccess = TRUE;
}
else if (pBuffer)
{
if (*pdwBytes >= Size)
{
/* store font title to buffer */
WCHAR *psz = pBuffer;
*psz = 0;
for (i = 0; i < Count; ++i)
{
if (i > 0)
wcscat(psz, L" & ");
IntAddNameFromFamInfo(psz, &FamInfo[i]);
}
psz[wcslen(psz) + 1] = UNICODE_NULL;
*pdwBytes = Size;
bSuccess = TRUE;
}
else
{
*pdwBytes = 1024; /* this is confirmed value */
}
}
break;
case 2: /* Copy an array of LOGFONTW */
Size = Count * sizeof(LOGFONTW);
if (*pdwBytes == 0)
{
*pdwBytes = Size;
bSuccess = TRUE;
}
else if (pBuffer)
{
if (*pdwBytes >= Size)
{
pbBuffer = (LPBYTE)pBuffer;
for (i = 0; i < Count; ++i)
{
FamInfo[i].EnumLogFontEx.elfLogFont.lfWidth = 0;
RtlCopyMemory(pbBuffer, &FamInfo[i].EnumLogFontEx.elfLogFont, sizeof(LOGFONTW));
pbBuffer += sizeof(LOGFONTW);
}
}
*pdwBytes = Size;
bSuccess = TRUE;
}
else
{
*pdwBytes = 1024; /* this is confirmed value */
}
break;
case 3:
Size = sizeof(DWORD);
if (*pdwBytes == 0)
{
*pdwBytes = Size;
bSuccess = TRUE;
}
else if (pBuffer)
{
if (*pdwBytes >= Size)
{
/* FIXME: What exactly is copied here? */
*(DWORD*)pBuffer = 1;
}
*pdwBytes = Size;
bSuccess = TRUE;
}
break;
case 4: /* full file path */
if (FileName->Length >= 4 * sizeof(WCHAR))
{
/* The beginning of FileName is \??\ */
LPWSTR pch = FileName->Buffer + 4;
DWORD Length = FileName->Length - 4 * sizeof(WCHAR);
Size = Length + sizeof(WCHAR);
if (*pdwBytes == 0)
{
*pdwBytes = Size;
bSuccess = TRUE;
}
else if (pBuffer)
{
if (*pdwBytes >= Size)
{
RtlCopyMemory(pBuffer, pch, Size);
}
*pdwBytes = Size;
bSuccess = TRUE;
}
}
break;
case 5: /* Looks like a BOOL that is copied, TRUE, if the font was not found */
Size = sizeof(BOOL);
if (*pdwBytes == 0)
{
*pdwBytes = Size;
bSuccess = TRUE;
}
else if (pBuffer)
{
if (*pdwBytes >= Size)
{
*(BOOL*)pBuffer = Count == 0;
}
*pdwBytes = Size;
bSuccess = TRUE;
}
break;
}
ExFreePoolWithTag(FamInfo, TAG_FINF);
return bSuccess;
}
BOOL
FASTCALL
ftGdiRealizationInfo(PFONTGDI Font, PREALIZATION_INFO Info)
{
if (FT_HAS_FIXED_SIZES(Font->SharedFace->Face))
Info->iTechnology = RI_TECH_BITMAP;
else
{
if (FT_IS_SCALABLE(Font->SharedFace->Face))
Info->iTechnology = RI_TECH_SCALABLE;
else
Info->iTechnology = RI_TECH_FIXED;
}
Info->iUniq = Font->FontObj.iUniq;
Info->dwUnknown = -1;
return TRUE;
}
DWORD
FASTCALL
ftGdiGetKerningPairs( PFONTGDI Font,
DWORD cPairs,
LPKERNINGPAIR pKerningPair)
{
DWORD Count = 0;
INT i = 0;
FT_Face face = Font->SharedFace->Face;
if (FT_HAS_KERNING(face) && face->charmap->encoding == FT_ENCODING_UNICODE)
{
FT_UInt previous_index = 0, glyph_index = 0;
FT_ULong char_code, char_previous;
FT_Vector delta;
char_previous = char_code = FT_Get_First_Char(face, &glyph_index);
IntLockFreeType();
while (glyph_index)
{
if (previous_index && glyph_index)
{
FT_Get_Kerning(face, previous_index, glyph_index, FT_KERNING_DEFAULT, &delta);
if (pKerningPair && cPairs)
{
pKerningPair[i].wFirst = char_previous;
pKerningPair[i].wSecond = char_code;
pKerningPair[i].iKernAmount = delta.x;
i++;
if (i == cPairs) break;
}
Count++;
}
previous_index = glyph_index;
char_previous = char_code;
char_code = FT_Get_Next_Char(face, char_code, &glyph_index);
}
IntUnLockFreeType();
}
return Count;
}
///////////////////////////////////////////////////////////////////////////
//
// Functions needing sorting.
//
///////////////////////////////////////////////////////////////////////////
LONG FASTCALL
IntGetFontFamilyInfo(HDC Dc,
const LOGFONTW *SafeLogFont,
PFONTFAMILYINFO SafeInfo,
LONG InfoCount)
{
LONG AvailCount = 0;
PPROCESSINFO Win32Process;
/* Enumerate font families in the global list */
IntLockGlobalFonts();
if (!GetFontFamilyInfoForList(SafeLogFont, SafeInfo, NULL, &AvailCount,
InfoCount, &g_FontListHead))
{
IntUnLockGlobalFonts();
return -1;
}
IntUnLockGlobalFonts();
/* Enumerate font families in the process local list */
Win32Process = PsGetCurrentProcessWin32Process();
IntLockProcessPrivateFonts(Win32Process);
if (!GetFontFamilyInfoForList(SafeLogFont, SafeInfo, NULL, &AvailCount, InfoCount,
&Win32Process->PrivateFontListHead))
{
IntUnLockProcessPrivateFonts(Win32Process);
return -1;
}
IntUnLockProcessPrivateFonts(Win32Process);
/* Enumerate font families in the registry */
if (!GetFontFamilyInfoForSubstitutes(SafeLogFont, SafeInfo, &AvailCount, InfoCount))
{
return -1;
}
return AvailCount;
}
LONG NTAPI
NtGdiGetFontFamilyInfo(HDC Dc,
const LOGFONTW *UnsafeLogFont,
PFONTFAMILYINFO UnsafeInfo,
LPLONG UnsafeInfoCount)
{
NTSTATUS Status;
LOGFONTW LogFont;
PFONTFAMILYINFO Info;
LONG GotCount, AvailCount, SafeInfoCount;
ULONG DataSize;
if (UnsafeLogFont == NULL || UnsafeInfo == NULL || UnsafeInfoCount == NULL)
{
EngSetLastError(ERROR_INVALID_PARAMETER);
return -1;
}
Status = MmCopyFromCaller(&SafeInfoCount, UnsafeInfoCount, sizeof(SafeInfoCount));
if (!NT_SUCCESS(Status))
{
EngSetLastError(ERROR_INVALID_PARAMETER);
return -1;
}
GotCount = 0;
Status = MmCopyToCaller(UnsafeInfoCount, &GotCount, sizeof(*UnsafeInfoCount));
if (!NT_SUCCESS(Status))
{
EngSetLastError(ERROR_INVALID_PARAMETER);
return -1;
}
Status = MmCopyFromCaller(&LogFont, UnsafeLogFont, sizeof(LOGFONTW));
if (!NT_SUCCESS(Status))
{
EngSetLastError(ERROR_INVALID_PARAMETER);
return -1;
}
if (SafeInfoCount <= 0)
{
EngSetLastError(ERROR_INVALID_PARAMETER);
return -1;
}
/* Allocate space for a safe copy */
Status = RtlULongMult(SafeInfoCount, sizeof(FONTFAMILYINFO), &DataSize);
if (!NT_SUCCESS(Status) || DataSize > LONG_MAX)
{
DPRINT1("Overflowed.\n");
EngSetLastError(ERROR_INVALID_PARAMETER);
return -1;
}
Info = ExAllocatePoolWithTag(PagedPool, DataSize, GDITAG_TEXT);
if (Info == NULL)
{
EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
return -1;
}
/* Retrieve the information */
AvailCount = IntGetFontFamilyInfo(Dc, &LogFont, Info, SafeInfoCount);
GotCount = min(AvailCount, SafeInfoCount);
SafeInfoCount = AvailCount;
/* Return data to caller */
if (GotCount > 0)
{
Status = RtlULongMult(GotCount, sizeof(FONTFAMILYINFO), &DataSize);
if (!NT_SUCCESS(Status) || DataSize > LONG_MAX)
{
DPRINT1("Overflowed.\n");
ExFreePoolWithTag(Info, GDITAG_TEXT);
EngSetLastError(ERROR_INVALID_PARAMETER);
return -1;
}
Status = MmCopyToCaller(UnsafeInfo, Info, DataSize);
if (!NT_SUCCESS(Status))
{
ExFreePoolWithTag(Info, GDITAG_TEXT);
EngSetLastError(ERROR_INVALID_PARAMETER);
return -1;
}
Status = MmCopyToCaller(UnsafeInfoCount, &SafeInfoCount, sizeof(*UnsafeInfoCount));
if (!NT_SUCCESS(Status))
{
ExFreePoolWithTag(Info, GDITAG_TEXT);
EngSetLastError(ERROR_INVALID_PARAMETER);
return -1;
}
}
ExFreePoolWithTag(Info, GDITAG_TEXT);
return GotCount;
}
static inline
LONG
ScaleLong(LONG lValue, PFLOATOBJ pef)
{
FLOATOBJ efTemp;
/* Check if we have scaling different from 1 */
if (!FLOATOBJ_Equal(pef, (PFLOATOBJ)&gef1))
{
/* Need to multiply */
FLOATOBJ_SetLong(&efTemp, lValue);
FLOATOBJ_Mul(&efTemp, pef);
lValue = FLOATOBJ_GetLong(&efTemp);
}
return lValue;
}
BOOL
APIENTRY
IntExtTextOutW(
IN PDC dc,
IN INT XStart,
IN INT YStart,
IN UINT fuOptions,
IN OPTIONAL PRECTL lprc,
IN LPCWSTR String,
IN INT Count,
IN OPTIONAL LPINT Dx,
IN DWORD dwCodePage)
{
/*
* FIXME:
* Call EngTextOut, which does the real work (calling DrvTextOut where
* appropriate)
*/
PDC_ATTR pdcattr;
SURFOBJ *SurfObj;
SURFACE *psurf = NULL;
int error, glyph_index, i;
FT_Face face;
FT_GlyphSlot glyph;
FT_BitmapGlyph realglyph;
LONGLONG TextLeft, RealXStart;
ULONG TextTop, previous, BackgroundLeft;
FT_Bool use_kerning;
RECTL DestRect, MaskRect;
POINTL SourcePoint, BrushOrigin;
HBITMAP HSourceGlyph;
SURFOBJ *SourceGlyphSurf;
SIZEL bitSize;
INT yoff;
FONTOBJ *FontObj;
PFONTGDI FontGDI;
PTEXTOBJ TextObj = NULL;
EXLATEOBJ exloRGB2Dst, exloDst2RGB;
FT_Render_Mode RenderMode;
BOOLEAN Render;
POINT Start;
BOOL DoBreak = FALSE;
USHORT DxShift;
PMATRIX pmxWorldToDevice;
LONG fixAscender, fixDescender;
FLOATOBJ Scale;
LOGFONTW *plf;
BOOL EmuBold, EmuItalic;
int thickness;
BOOL bResult;
/* Check if String is valid */
if ((Count > 0xFFFF) || (Count > 0 && String == NULL))
{
EngSetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
Render = IntIsFontRenderingEnabled();
if (PATH_IsPathOpen(dc->dclevel))
{
bResult = PATH_ExtTextOut(dc,
XStart,
YStart,
fuOptions,
(const RECTL *)lprc,
String,
Count,
(const INT *)Dx);
return bResult;
}
DC_vPrepareDCsForBlit(dc, NULL, NULL, NULL);
if (!dc->dclevel.pSurface)
{
/* Memory DC with no surface selected */
bResult = TRUE;
goto Cleanup;
}
pdcattr = dc->pdcattr;
if (lprc && (fuOptions & (ETO_OPAQUE | ETO_CLIPPED)))
{
IntLPtoDP(dc, (POINT *)lprc, 2);
}
if (pdcattr->flTextAlign & TA_UPDATECP)
{
Start.x = pdcattr->ptlCurrent.x;
Start.y = pdcattr->ptlCurrent.y;
} else {
Start.x = XStart;
Start.y = YStart;
}
IntLPtoDP(dc, &Start, 1);
RealXStart = ((LONGLONG)Start.x + dc->ptlDCOrig.x) << 6;
YStart = Start.y + dc->ptlDCOrig.y;
SourcePoint.x = 0;
SourcePoint.y = 0;
MaskRect.left = 0;
MaskRect.top = 0;
BrushOrigin.x = 0;
BrushOrigin.y = 0;
if ((fuOptions & ETO_OPAQUE) && lprc)
{
DestRect.left = lprc->left;
DestRect.top = lprc->top;
DestRect.right = lprc->right;
DestRect.bottom = lprc->bottom;
DestRect.left += dc->ptlDCOrig.x;
DestRect.top += dc->ptlDCOrig.y;
DestRect.right += dc->ptlDCOrig.x;
DestRect.bottom += dc->ptlDCOrig.y;
if (dc->fs & (DC_ACCUM_APP|DC_ACCUM_WMGR))
{
IntUpdateBoundsRect(dc, &DestRect);
}
if (pdcattr->ulDirty_ & DIRTY_BACKGROUND)
DC_vUpdateBackgroundBrush(dc);
if (dc->dctype == DCTYPE_DIRECT)
MouseSafetyOnDrawStart(dc->ppdev, DestRect.left, DestRect.top, DestRect.right, DestRect.bottom);
psurf = dc->dclevel.pSurface;
IntEngBitBlt(
&psurf->SurfObj,
NULL,
NULL,
(CLIPOBJ *)&dc->co,
NULL,
&DestRect,
&SourcePoint,
&SourcePoint,
&dc->eboBackground.BrushObject,
&BrushOrigin,
ROP4_FROM_INDEX(R3_OPINDEX_PATCOPY));
if (dc->dctype == DCTYPE_DIRECT)
MouseSafetyOnDrawEnd(dc->ppdev);
fuOptions &= ~ETO_OPAQUE;
}
else
{
if (pdcattr->jBkMode == OPAQUE)
{
fuOptions |= ETO_OPAQUE;
}
}
TextObj = RealizeFontInit(pdcattr->hlfntNew);
if (TextObj == NULL)
{
bResult = FALSE;
goto Cleanup;
}
FontObj = TextObj->Font;
ASSERT(FontObj);
FontGDI = ObjToGDI(FontObj, FONT);
ASSERT(FontGDI);
IntLockFreeType();
face = FontGDI->SharedFace->Face;
plf = &TextObj->logfont.elfEnumLogfontEx.elfLogFont;
EmuBold = EMUBOLD_NEEDED(FontGDI->OriginalWeight, plf->lfWeight);
EmuItalic = (plf->lfItalic && !FontGDI->OriginalItalic);
if (Render)
RenderMode = IntGetFontRenderMode(plf);
else
RenderMode = FT_RENDER_MODE_MONO;
if (!TextIntUpdateSize(dc, TextObj, FontGDI, FALSE))
{
IntUnLockFreeType();
bResult = FALSE;
goto Cleanup;
}
/* NOTE: Don't trust face->size->metrics.ascender and descender values. */
if (dc->pdcattr->iGraphicsMode == GM_ADVANCED)
{
pmxWorldToDevice = DC_pmxWorldToDevice(dc);
FtSetCoordinateTransform(face, pmxWorldToDevice);
fixAscender = ScaleLong(FontGDI->tmAscent, &pmxWorldToDevice->efM22) << 6;
fixDescender = ScaleLong(FontGDI->tmDescent, &pmxWorldToDevice->efM22) << 6;
}
else
{
pmxWorldToDevice = (PMATRIX)&gmxWorldToDeviceDefault;
FtSetCoordinateTransform(face, pmxWorldToDevice);
fixAscender = FontGDI->tmAscent << 6;
fixDescender = FontGDI->tmDescent << 6;
}
/*
* Process the vertical alignment and determine the yoff.
*/
#define VALIGN_MASK (TA_TOP | TA_BASELINE | TA_BOTTOM)
if ((pdcattr->flTextAlign & VALIGN_MASK) == TA_BASELINE)
yoff = 0;
else if ((pdcattr->flTextAlign & VALIGN_MASK) == TA_BOTTOM)
yoff = -(fixDescender >> 6);
else /* TA_TOP */
yoff = fixAscender >> 6;
#undef VALIGN_MASK
use_kerning = FT_HAS_KERNING(face);
previous = 0;
/*
* Process the horizontal alignment and modify XStart accordingly.
*/
DxShift = (fuOptions & ETO_PDY) ? 1 : 0;
if (pdcattr->flTextAlign & (TA_RIGHT | TA_CENTER))
{
ULONGLONG TextWidth = 0;
LPCWSTR TempText = String;
int iStart;
/*
* Calculate width of the text.
*/
if (NULL != Dx)
{
iStart = Count < 2 ? 0 : Count - 2;
TextWidth = Count < 2 ? 0 : (Dx[(Count-2)<<DxShift] << 6);
}
else
{
iStart = 0;
}
TempText = String + iStart;
for (i = iStart; i < Count; i++)
{
glyph_index = get_glyph_index_flagged(face, *TempText, ETO_GLYPH_INDEX, fuOptions);
if (EmuBold || EmuItalic)
realglyph = NULL;
else
realglyph = ftGdiGlyphCacheGet(face, glyph_index, plf->lfHeight,
RenderMode, pmxWorldToDevice);
if (!realglyph)
{
if (EmuItalic)
error = FT_Load_Glyph(face, glyph_index, FT_LOAD_NO_BITMAP);
else
error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
if (error)
{
DPRINT1("WARNING: Failed to load and render glyph! [index: %d]\n", glyph_index);
}
glyph = face->glyph;
if (EmuBold || EmuItalic)
{
if (EmuBold)
FT_GlyphSlot_Embolden(glyph);
if (EmuItalic)
FT_GlyphSlot_Oblique(glyph);
realglyph = ftGdiGlyphSet(face, glyph, RenderMode);
}
else
{
realglyph = ftGdiGlyphCacheSet(face,
glyph_index,
plf->lfHeight,
pmxWorldToDevice,
glyph,
RenderMode);
}
if (!realglyph)
{
DPRINT1("Failed to render glyph! [index: %d]\n", glyph_index);
IntUnLockFreeType();
bResult = FALSE;
goto Cleanup;
}
}
/* Retrieve kerning distance */
if (use_kerning && previous && glyph_index)
{
FT_Vector delta;
FT_Get_Kerning(face, previous, glyph_index, 0, &delta);
TextWidth += delta.x;
}
TextWidth += realglyph->root.advance.x >> 10;
if (EmuBold || EmuItalic)
{
FT_Done_Glyph((FT_Glyph)realglyph);
realglyph = NULL;
}
previous = glyph_index;
TempText++;
}
previous = 0;
if ((pdcattr->flTextAlign & TA_CENTER) == TA_CENTER)
{
RealXStart -= TextWidth / 2;
}
else
{
RealXStart -= TextWidth;
}
}
psurf = dc->dclevel.pSurface;
SurfObj = &psurf->SurfObj ;
if ((fuOptions & ETO_OPAQUE) && (dc->pdcattr->ulDirty_ & DIRTY_BACKGROUND))
DC_vUpdateBackgroundBrush(dc) ;
if(dc->pdcattr->ulDirty_ & DIRTY_TEXT)
DC_vUpdateTextBrush(dc) ;
if (!face->units_per_EM)
{
thickness = 1;
}
else
{
thickness = face->underline_thickness *
face->size->metrics.y_ppem / face->units_per_EM;
if (thickness <= 0)
thickness = 1;
}
if ((fuOptions & ETO_OPAQUE) && plf->lfItalic)
{
/* Draw background */
TextLeft = RealXStart;
TextTop = YStart;
BackgroundLeft = (RealXStart + 32) >> 6;
for (i = 0; i < Count; ++i)
{
glyph_index = get_glyph_index_flagged(face, String[i], ETO_GLYPH_INDEX, fuOptions);
if (EmuItalic)
error = FT_Load_Glyph(face, glyph_index, FT_LOAD_NO_BITMAP);
else
error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
if (error)
{
DPRINT1("Failed to load and render glyph! [index: %d]\n", glyph_index);
IntUnLockFreeType();
bResult = FALSE;
goto Cleanup;
}
glyph = face->glyph;
if (EmuBold)
FT_GlyphSlot_Embolden(glyph);
if (EmuItalic)
FT_GlyphSlot_Oblique(glyph);
realglyph = ftGdiGlyphSet(face, glyph, RenderMode);
if (!realglyph)
{
DPRINT1("Failed to render glyph! [index: %d]\n", glyph_index);
IntUnLockFreeType();
bResult = FALSE;
goto Cleanup;
}
/* retrieve kerning distance and move pen position */
if (use_kerning && previous && glyph_index && NULL == Dx)
{
FT_Vector delta;
FT_Get_Kerning(face, previous, glyph_index, 0, &delta);
TextLeft += delta.x;
}
DPRINT("TextLeft: %I64d\n", TextLeft);
DPRINT("TextTop: %lu\n", TextTop);
DPRINT("Advance: %d\n", realglyph->root.advance.x);
DestRect.left = BackgroundLeft;
DestRect.right = (TextLeft + (realglyph->root.advance.x >> 10) + 32) >> 6;
DestRect.top = TextTop + yoff - ((fixAscender + 32) >> 6);
DestRect.bottom = DestRect.top + ((fixAscender + fixDescender) >> 6);
MouseSafetyOnDrawStart(dc->ppdev, DestRect.left, DestRect.top, DestRect.right, DestRect.bottom);
if (dc->fs & (DC_ACCUM_APP|DC_ACCUM_WMGR))
{
IntUpdateBoundsRect(dc, &DestRect);
}
IntEngBitBlt(
&psurf->SurfObj,
NULL,
NULL,
(CLIPOBJ *)&dc->co,
NULL,
&DestRect,
&SourcePoint,
&SourcePoint,
&dc->eboBackground.BrushObject,
&BrushOrigin,
ROP4_FROM_INDEX(R3_OPINDEX_PATCOPY));
MouseSafetyOnDrawEnd(dc->ppdev);
BackgroundLeft = DestRect.right;
DestRect.left = ((TextLeft + 32) >> 6) + realglyph->left;
DestRect.right = DestRect.left + realglyph->bitmap.width;
DestRect.top = TextTop + yoff - realglyph->top;
DestRect.bottom = DestRect.top + realglyph->bitmap.rows;
bitSize.cx = realglyph->bitmap.width;
bitSize.cy = realglyph->bitmap.rows;
MaskRect.right = realglyph->bitmap.width;
MaskRect.bottom = realglyph->bitmap.rows;
if (NULL == Dx)
{
TextLeft += realglyph->root.advance.x >> 10;
DPRINT("New TextLeft: %I64d\n", TextLeft);
}
else
{
// FIXME this should probably be a matrix transform with TextTop as well.
Scale = pdcattr->mxWorldToDevice.efM11;
if (FLOATOBJ_Equal0(&Scale))
FLOATOBJ_Set1(&Scale);
/* do the shift before multiplying to preserve precision */
FLOATOBJ_MulLong(&Scale, Dx[i<<DxShift] << 6);
TextLeft += FLOATOBJ_GetLong(&Scale);
DPRINT("New TextLeft2: %I64d\n", TextLeft);
}
if (DxShift)
{
TextTop -= Dx[2 * i + 1] << 6;
}
previous = glyph_index;
if (EmuBold || EmuItalic)
{
FT_Done_Glyph((FT_Glyph)realglyph);
realglyph = NULL;
}
}
}
EXLATEOBJ_vInitialize(&exloRGB2Dst, &gpalRGB, psurf->ppal, 0, 0, 0);
EXLATEOBJ_vInitialize(&exloDst2RGB, psurf->ppal, &gpalRGB, 0, 0, 0);
/* Assume success */
bResult = TRUE;
/*
* The main rendering loop.
*/
TextLeft = RealXStart;
TextTop = YStart;
BackgroundLeft = (RealXStart + 32) >> 6;
for (i = 0; i < Count; ++i)
{
glyph_index = get_glyph_index_flagged(face, String[i], ETO_GLYPH_INDEX, fuOptions);
if (EmuBold || EmuItalic)
realglyph = NULL;
else
realglyph = ftGdiGlyphCacheGet(face, glyph_index, plf->lfHeight,
RenderMode, pmxWorldToDevice);
if (!realglyph)
{
if (EmuItalic)
error = FT_Load_Glyph(face, glyph_index, FT_LOAD_NO_BITMAP);
else
error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
if (error)
{
DPRINT1("Failed to load and render glyph! [index: %d]\n", glyph_index);
bResult = FALSE;
break;
}
glyph = face->glyph;
if (EmuBold || EmuItalic)
{
if (EmuBold)
FT_GlyphSlot_Embolden(glyph);
if (EmuItalic)
FT_GlyphSlot_Oblique(glyph);
realglyph = ftGdiGlyphSet(face, glyph, RenderMode);
}
else
{
realglyph = ftGdiGlyphCacheSet(face,
glyph_index,
plf->lfHeight,
pmxWorldToDevice,
glyph,
RenderMode);
}
if (!realglyph)
{
DPRINT1("Failed to render glyph! [index: %d]\n", glyph_index);
bResult = FALSE;
break;
}
}
/* retrieve kerning distance and move pen position */
if (use_kerning && previous && glyph_index && NULL == Dx)
{
FT_Vector delta;
FT_Get_Kerning(face, previous, glyph_index, 0, &delta);
TextLeft += delta.x;
}
DPRINT("TextLeft: %I64d\n", TextLeft);
DPRINT("TextTop: %lu\n", TextTop);
DPRINT("Advance: %d\n", realglyph->root.advance.x);
if ((fuOptions & ETO_OPAQUE) && !plf->lfItalic)
{
DestRect.left = BackgroundLeft;
DestRect.right = (TextLeft + (realglyph->root.advance.x >> 10) + 32) >> 6;
DestRect.top = TextTop + yoff - ((fixAscender + 32) >> 6);
DestRect.bottom = DestRect.top + ((fixAscender + fixDescender) >> 6);
if (dc->dctype == DCTYPE_DIRECT)
MouseSafetyOnDrawStart(dc->ppdev, DestRect.left, DestRect.top, DestRect.right, DestRect.bottom);
if (dc->fs & (DC_ACCUM_APP|DC_ACCUM_WMGR))
{
IntUpdateBoundsRect(dc, &DestRect);
}
IntEngBitBlt(
&psurf->SurfObj,
NULL,
NULL,
(CLIPOBJ *)&dc->co,
NULL,
&DestRect,
&SourcePoint,
&SourcePoint,
&dc->eboBackground.BrushObject,
&BrushOrigin,
ROP4_FROM_INDEX(R3_OPINDEX_PATCOPY));
if (dc->dctype == DCTYPE_DIRECT)
MouseSafetyOnDrawEnd(dc->ppdev);
BackgroundLeft = DestRect.right;
}
DestRect.left = ((TextLeft + 32) >> 6) + realglyph->left;
DestRect.right = DestRect.left + realglyph->bitmap.width;
DestRect.top = TextTop + yoff - realglyph->top;
DestRect.bottom = DestRect.top + realglyph->bitmap.rows;
bitSize.cx = realglyph->bitmap.width;
bitSize.cy = realglyph->bitmap.rows;
MaskRect.right = realglyph->bitmap.width;
MaskRect.bottom = realglyph->bitmap.rows;
/* Check if the bitmap has any pixels */
if ((bitSize.cx != 0) && (bitSize.cy != 0))
{
/*
* We should create the bitmap out of the loop at the biggest possible
* glyph size. Then use memset with 0 to clear it and sourcerect to
* limit the work of the transbitblt.
*/
HSourceGlyph = EngCreateBitmap(bitSize, realglyph->bitmap.pitch,
BMF_8BPP, BMF_TOPDOWN,
realglyph->bitmap.buffer);
if ( !HSourceGlyph )
{
DPRINT1("WARNING: EngCreateBitmap() failed!\n");
// FT_Done_Glyph(realglyph);
bResult = FALSE;
break;
}
SourceGlyphSurf = EngLockSurface((HSURF)HSourceGlyph);
if ( !SourceGlyphSurf )
{
EngDeleteSurface((HSURF)HSourceGlyph);
DPRINT1("WARNING: EngLockSurface() failed!\n");
bResult = FALSE;
break;
}
/*
* Use the font data as a mask to paint onto the DCs surface using a
* brush.
*/
if (lprc && (fuOptions & ETO_CLIPPED) &&
DestRect.right >= lprc->right + dc->ptlDCOrig.x)
{
// We do the check '>=' instead of '>' to possibly save an iteration
// through this loop, since it's breaking after the drawing is done,
// and x is always incremented.
DestRect.right = lprc->right + dc->ptlDCOrig.x;
DoBreak = TRUE;
}
if (lprc && (fuOptions & ETO_CLIPPED) &&
DestRect.bottom >= lprc->bottom + dc->ptlDCOrig.y)
{
DestRect.bottom = lprc->bottom + dc->ptlDCOrig.y;
}
if (dc->dctype == DCTYPE_DIRECT)
MouseSafetyOnDrawStart(dc->ppdev, DestRect.left, DestRect.top, DestRect.right, DestRect.bottom);
if (!IntEngMaskBlt(
SurfObj,
SourceGlyphSurf,
(CLIPOBJ *)&dc->co,
&exloRGB2Dst.xlo,
&exloDst2RGB.xlo,
&DestRect,
(PPOINTL)&MaskRect,
&dc->eboText.BrushObject,
&BrushOrigin))
{
DPRINT1("Failed to MaskBlt a glyph!\n");
}
if (dc->dctype == DCTYPE_DIRECT)
MouseSafetyOnDrawEnd(dc->ppdev) ;
EngUnlockSurface(SourceGlyphSurf);
EngDeleteSurface((HSURF)HSourceGlyph);
}
if (DoBreak)
{
break;
}
if (plf->lfUnderline)
{
int i, position;
if (!face->units_per_EM)
{
position = 0;
}
else
{
position = face->underline_position *
face->size->metrics.y_ppem / face->units_per_EM;
}
for (i = -thickness / 2; i < -thickness / 2 + thickness; ++i)
{
EngLineTo(SurfObj,
(CLIPOBJ *)&dc->co,
&dc->eboText.BrushObject,
(TextLeft >> 6),
TextTop + yoff - position + i,
((TextLeft + (realglyph->root.advance.x >> 10)) >> 6),
TextTop + yoff - position + i,
NULL,
ROP2_TO_MIX(R2_COPYPEN));
}
}
if (plf->lfStrikeOut)
{
int i;
for (i = -thickness / 2; i < -thickness / 2 + thickness; ++i)
{
EngLineTo(SurfObj,
(CLIPOBJ *)&dc->co,
&dc->eboText.BrushObject,
(TextLeft >> 6),
TextTop + yoff - (fixAscender >> 6) / 3 + i,
((TextLeft + (realglyph->root.advance.x >> 10)) >> 6),
TextTop + yoff - (fixAscender >> 6) / 3 + i,
NULL,
ROP2_TO_MIX(R2_COPYPEN));
}
}
if (NULL == Dx)
{
TextLeft += realglyph->root.advance.x >> 10;
DPRINT("New TextLeft: %I64d\n", TextLeft);
}
else
{
// FIXME this should probably be a matrix transform with TextTop as well.
Scale = pdcattr->mxWorldToDevice.efM11;
if (FLOATOBJ_Equal0(&Scale))
FLOATOBJ_Set1(&Scale);
/* do the shift before multiplying to preserve precision */
FLOATOBJ_MulLong(&Scale, Dx[i<<DxShift] << 6);
TextLeft += FLOATOBJ_GetLong(&Scale);
DPRINT("New TextLeft2: %I64d\n", TextLeft);
}
if (DxShift)
{
TextTop -= Dx[2 * i + 1] << 6;
}
previous = glyph_index;
if (EmuBold || EmuItalic)
{
FT_Done_Glyph((FT_Glyph)realglyph);
realglyph = NULL;
}
}
if (pdcattr->flTextAlign & TA_UPDATECP) {
pdcattr->ptlCurrent.x = DestRect.right - dc->ptlDCOrig.x;
}
IntUnLockFreeType();
EXLATEOBJ_vCleanup(&exloRGB2Dst);
EXLATEOBJ_vCleanup(&exloDst2RGB);
Cleanup:
DC_vFinishBlit(dc, NULL);
if (TextObj != NULL)
TEXTOBJ_UnlockText(TextObj);
return bResult;
}
BOOL
APIENTRY
GreExtTextOutW(
IN HDC hDC,
IN INT XStart,
IN INT YStart,
IN UINT fuOptions,
IN OPTIONAL PRECTL lprc,
IN LPCWSTR String,
IN INT Count,
IN OPTIONAL LPINT Dx,
IN DWORD dwCodePage)
{
BOOL bResult;
DC *dc;
// TODO: Write test-cases to exactly match real Windows in different
// bad parameters (e.g. does Windows check the DC or the RECT first?).
dc = DC_LockDc(hDC);
if (!dc)
{
EngSetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
bResult = IntExtTextOutW( dc,
XStart,
YStart,
fuOptions,
lprc,
String,
Count,
Dx,
dwCodePage );
DC_UnlockDc(dc);
return bResult;
}
#define STACK_TEXT_BUFFER_SIZE 100
BOOL
APIENTRY
NtGdiExtTextOutW(
IN HDC hDC,
IN INT XStart,
IN INT YStart,
IN UINT fuOptions,
IN OPTIONAL LPRECT UnsafeRect,
IN LPWSTR UnsafeString,
IN INT Count,
IN OPTIONAL LPINT UnsafeDx,
IN DWORD dwCodePage)
{
BOOL Result = FALSE;
NTSTATUS Status = STATUS_SUCCESS;
RECTL SafeRect;
BYTE LocalBuffer[STACK_TEXT_BUFFER_SIZE];
PVOID Buffer = LocalBuffer;
LPCWSTR SafeString = NULL;
LPINT SafeDx = NULL;
ULONG BufSize, StringSize, DxSize = 0;
/* Check if String is valid */
if ((Count > 0xFFFF) || (Count > 0 && UnsafeString == NULL))
{
EngSetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if (Count > 0)
{
/* Calculate buffer size for string and Dx values */
BufSize = StringSize = Count * sizeof(WCHAR);
if (UnsafeDx)
{
/* If ETO_PDY is specified, we have pairs of INTs */
DxSize = (Count * sizeof(INT)) * ((fuOptions & ETO_PDY) ? 2 : 1);
BufSize += DxSize;
}
/* Check if our local buffer is large enough */
if (BufSize > STACK_TEXT_BUFFER_SIZE)
{
/* It's not, allocate a temp buffer */
Buffer = ExAllocatePoolWithTag(PagedPool, BufSize, GDITAG_TEXT);
if (!Buffer)
{
return FALSE;
}
}
/* Probe and copy user mode data to the buffer */
_SEH2_TRY
{
/* Put the Dx before the String to assure alignment of 4 */
SafeString = (LPCWSTR)(((ULONG_PTR)Buffer) + DxSize);
/* Probe and copy the string */
ProbeForRead(UnsafeString, StringSize, 1);
RtlCopyMemory((PVOID)SafeString, UnsafeString, StringSize);
/* If we have Dx values... */
if (UnsafeDx)
{
/* ... probe and copy them */
SafeDx = Buffer;
ProbeForRead(UnsafeDx, DxSize, 1);
RtlCopyMemory(SafeDx, UnsafeDx, DxSize);
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END
if (!NT_SUCCESS(Status))
{
goto cleanup;
}
}
/* If we have a rect, copy it */
if (UnsafeRect)
{
_SEH2_TRY
{
ProbeForRead(UnsafeRect, sizeof(RECT), 1);
SafeRect = *UnsafeRect;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END
if (!NT_SUCCESS(Status))
{
goto cleanup;
}
}
/* Finally call the internal routine */
Result = GreExtTextOutW(hDC,
XStart,
YStart,
fuOptions,
&SafeRect,
SafeString,
Count,
SafeDx,
dwCodePage);
cleanup:
/* If we allocated a buffer, free it */
if (Buffer != LocalBuffer)
{
ExFreePoolWithTag(Buffer, GDITAG_TEXT);
}
return Result;
}
/*
* @implemented
*/
BOOL
APIENTRY
NtGdiGetCharABCWidthsW(
IN HDC hDC,
IN UINT FirstChar,
IN ULONG Count,
IN OPTIONAL PWCHAR UnSafepwch,
IN FLONG fl,
OUT PVOID Buffer)
{
LPABC SafeBuff;
LPABCFLOAT SafeBuffF = NULL;
PDC dc;
PDC_ATTR pdcattr;
PTEXTOBJ TextObj;
PFONTGDI FontGDI;
FT_Face face;
FT_CharMap charmap, found = NULL;
UINT i, glyph_index, BufferSize;
HFONT hFont = 0;
NTSTATUS Status = STATUS_SUCCESS;
PMATRIX pmxWorldToDevice;
PWCHAR Safepwch = NULL;
LOGFONTW *plf;
if (!Buffer)
{
EngSetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if (UnSafepwch)
{
UINT pwchSize = Count * sizeof(WCHAR);
Safepwch = ExAllocatePoolWithTag(PagedPool, pwchSize, GDITAG_TEXT);
if(!Safepwch)
{
EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
}
_SEH2_TRY
{
ProbeForRead(UnSafepwch, pwchSize, 1);
RtlCopyMemory(Safepwch, UnSafepwch, pwchSize);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
}
if (!NT_SUCCESS(Status))
{
if(Safepwch)
ExFreePoolWithTag(Safepwch , GDITAG_TEXT);
EngSetLastError(Status);
return FALSE;
}
BufferSize = Count * sizeof(ABC); // Same size!
SafeBuff = ExAllocatePoolWithTag(PagedPool, BufferSize, GDITAG_TEXT);
if (!fl) SafeBuffF = (LPABCFLOAT) SafeBuff;
if (SafeBuff == NULL)
{
if(Safepwch)
ExFreePoolWithTag(Safepwch , GDITAG_TEXT);
EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
}
dc = DC_LockDc(hDC);
if (dc == NULL)
{
ExFreePoolWithTag(SafeBuff, GDITAG_TEXT);
if(Safepwch)
ExFreePoolWithTag(Safepwch , GDITAG_TEXT);
EngSetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
pdcattr = dc->pdcattr;
hFont = pdcattr->hlfntNew;
TextObj = RealizeFontInit(hFont);
/* Get the DC's world-to-device transformation matrix */
pmxWorldToDevice = DC_pmxWorldToDevice(dc);
DC_UnlockDc(dc);
if (TextObj == NULL)
{
ExFreePoolWithTag(SafeBuff, GDITAG_TEXT);
if(Safepwch)
ExFreePoolWithTag(Safepwch , GDITAG_TEXT);
EngSetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
FontGDI = ObjToGDI(TextObj->Font, FONT);
face = FontGDI->SharedFace->Face;
if (face->charmap == NULL)
{
for (i = 0; i < (UINT)face->num_charmaps; i++)
{
charmap = face->charmaps[i];
if (charmap->encoding != 0)
{
found = charmap;
break;
}
}
if (!found)
{
DPRINT1("WARNING: Could not find desired charmap!\n");
ExFreePoolWithTag(SafeBuff, GDITAG_TEXT);
if(Safepwch)
ExFreePoolWithTag(Safepwch , GDITAG_TEXT);
EngSetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
IntLockFreeType();
FT_Set_Charmap(face, found);
IntUnLockFreeType();
}
plf = &TextObj->logfont.elfEnumLogfontEx.elfLogFont;
IntLockFreeType();
IntRequestFontSize(dc, FontGDI, plf->lfWidth, plf->lfHeight);
FtSetCoordinateTransform(face, pmxWorldToDevice);
for (i = FirstChar; i < FirstChar+Count; i++)
{
int adv, lsb, bbx, left, right;
if (Safepwch)
{
glyph_index = get_glyph_index_flagged(face, Safepwch[i - FirstChar], GCABCW_INDICES, fl);
}
else
{
glyph_index = get_glyph_index_flagged(face, i, GCABCW_INDICES, fl);
}
FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
left = (INT)face->glyph->metrics.horiBearingX & -64;
right = (INT)((face->glyph->metrics.horiBearingX + face->glyph->metrics.width) + 63) & -64;
adv = (face->glyph->advance.x + 32) >> 6;
// int test = (INT)(face->glyph->metrics.horiAdvance + 63) >> 6;
// DPRINT1("Advance Wine %d and Advance Ros %d\n",test, adv ); /* It's the same! */
lsb = left >> 6;
bbx = (right - left) >> 6;
/*
DPRINT1("lsb %d and bbx %d\n", lsb, bbx );
*/
if (!fl)
{
SafeBuffF[i - FirstChar].abcfA = (FLOAT) lsb;
SafeBuffF[i - FirstChar].abcfB = (FLOAT) bbx;
SafeBuffF[i - FirstChar].abcfC = (FLOAT) (adv - lsb - bbx);
}
else
{
SafeBuff[i - FirstChar].abcA = lsb;
SafeBuff[i - FirstChar].abcB = bbx;
SafeBuff[i - FirstChar].abcC = adv - lsb - bbx;
}
}
IntUnLockFreeType();
TEXTOBJ_UnlockText(TextObj);
Status = MmCopyToCaller(Buffer, SafeBuff, BufferSize);
ExFreePoolWithTag(SafeBuff, GDITAG_TEXT);
if(Safepwch)
ExFreePoolWithTag(Safepwch , GDITAG_TEXT);
if (!NT_SUCCESS(Status))
{
SetLastNtError(Status);
return FALSE;
}
DPRINT("NtGdiGetCharABCWidths Worked!\n");
return TRUE;
}
/*
* @implemented
*/
BOOL
APIENTRY
NtGdiGetCharWidthW(
IN HDC hDC,
IN UINT FirstChar,
IN UINT Count,
IN OPTIONAL PWCHAR UnSafepwc,
IN FLONG fl,
OUT PVOID Buffer)
{
NTSTATUS Status = STATUS_SUCCESS;
LPINT SafeBuff;
PFLOAT SafeBuffF = NULL;
PDC dc;
PDC_ATTR pdcattr;
PTEXTOBJ TextObj;
PFONTGDI FontGDI;
FT_Face face;
FT_CharMap charmap, found = NULL;
UINT i, glyph_index, BufferSize;
HFONT hFont = 0;
PMATRIX pmxWorldToDevice;
PWCHAR Safepwc = NULL;
LOGFONTW *plf;
if (UnSafepwc)
{
UINT pwcSize = Count * sizeof(WCHAR);
Safepwc = ExAllocatePoolWithTag(PagedPool, pwcSize, GDITAG_TEXT);
if(!Safepwc)
{
EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
}
_SEH2_TRY
{
ProbeForRead(UnSafepwc, pwcSize, 1);
RtlCopyMemory(Safepwc, UnSafepwc, pwcSize);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
}
if (!NT_SUCCESS(Status))
{
EngSetLastError(Status);
return FALSE;
}
BufferSize = Count * sizeof(INT); // Same size!
SafeBuff = ExAllocatePoolWithTag(PagedPool, BufferSize, GDITAG_TEXT);
if (!fl) SafeBuffF = (PFLOAT) SafeBuff;
if (SafeBuff == NULL)
{
if(Safepwc)
ExFreePoolWithTag(Safepwc, GDITAG_TEXT);
EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
}
dc = DC_LockDc(hDC);
if (dc == NULL)
{
if(Safepwc)
ExFreePoolWithTag(Safepwc, GDITAG_TEXT);
ExFreePoolWithTag(SafeBuff, GDITAG_TEXT);
EngSetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
pdcattr = dc->pdcattr;
hFont = pdcattr->hlfntNew;
TextObj = RealizeFontInit(hFont);
/* Get the DC's world-to-device transformation matrix */
pmxWorldToDevice = DC_pmxWorldToDevice(dc);
DC_UnlockDc(dc);
if (TextObj == NULL)
{
if(Safepwc)
ExFreePoolWithTag(Safepwc, GDITAG_TEXT);
ExFreePoolWithTag(SafeBuff, GDITAG_TEXT);
EngSetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
FontGDI = ObjToGDI(TextObj->Font, FONT);
face = FontGDI->SharedFace->Face;
if (face->charmap == NULL)
{
for (i = 0; i < (UINT)face->num_charmaps; i++)
{
charmap = face->charmaps[i];
if (charmap->encoding != 0)
{
found = charmap;
break;
}
}
if (!found)
{
DPRINT1("WARNING: Could not find desired charmap!\n");
if(Safepwc)
ExFreePoolWithTag(Safepwc, GDITAG_TEXT);
ExFreePoolWithTag(SafeBuff, GDITAG_TEXT);
EngSetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
IntLockFreeType();
FT_Set_Charmap(face, found);
IntUnLockFreeType();
}
plf = &TextObj->logfont.elfEnumLogfontEx.elfLogFont;
IntLockFreeType();
IntRequestFontSize(dc, FontGDI, plf->lfWidth, plf->lfHeight);
FtSetCoordinateTransform(face, pmxWorldToDevice);
for (i = FirstChar; i < FirstChar+Count; i++)
{
if (Safepwc)
{
glyph_index = get_glyph_index_flagged(face, Safepwc[i - FirstChar], GCW_INDICES, fl);
}
else
{
glyph_index = get_glyph_index_flagged(face, i, GCW_INDICES, fl);
}
FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
if (!fl)
SafeBuffF[i - FirstChar] = (FLOAT) ((face->glyph->advance.x + 32) >> 6);
else
SafeBuff[i - FirstChar] = (face->glyph->advance.x + 32) >> 6;
}
IntUnLockFreeType();
TEXTOBJ_UnlockText(TextObj);
MmCopyToCaller(Buffer, SafeBuff, BufferSize);
if(Safepwc)
ExFreePoolWithTag(Safepwc, GDITAG_TEXT);
ExFreePoolWithTag(SafeBuff, GDITAG_TEXT);
return TRUE;
}
/*
* @implemented
*/
// TODO: Move this code into NtGdiGetGlyphIndicesWInternal and wrap
// NtGdiGetGlyphIndicesW around NtGdiGetGlyphIndicesWInternal instead.
// NOTE: See also GreGetGlyphIndicesW.
__kernel_entry
W32KAPI
DWORD
APIENTRY
NtGdiGetGlyphIndicesW(
_In_ HDC hdc,
_In_reads_opt_(cwc) LPCWSTR pwc,
_In_ INT cwc,
_Out_writes_opt_(cwc) LPWORD pgi,
_In_ DWORD iMode)
{
PDC dc;
PDC_ATTR pdcattr;
PTEXTOBJ TextObj;
PFONTGDI FontGDI;
HFONT hFont = NULL;
NTSTATUS Status = STATUS_SUCCESS;
OUTLINETEXTMETRICW *potm;
INT i;
WCHAR DefChar = 0xffff;
PWSTR Buffer = NULL;
ULONG Size, pwcSize;
PWSTR Safepwc = NULL;
LPCWSTR UnSafepwc = pwc;
LPWORD UnSafepgi = pgi;
FT_Face Face;
TT_OS2 *pOS2;
if (cwc < 0)
{
DPRINT1("cwc < 0\n");
return GDI_ERROR;
}
if (!UnSafepwc && !UnSafepgi && cwc > 0)
{
DPRINT1("!UnSafepwc && !UnSafepgi && cwc > 0\n");
return GDI_ERROR;
}
if (!UnSafepwc != !UnSafepgi)
{
DPRINT1("UnSafepwc == %p, UnSafepgi = %p\n", UnSafepwc, UnSafepgi);
return GDI_ERROR;
}
/* Get FontGDI */
dc = DC_LockDc(hdc);
if (!dc)
{
DPRINT1("!DC_LockDC\n");
return GDI_ERROR;
}
pdcattr = dc->pdcattr;
hFont = pdcattr->hlfntNew;
TextObj = RealizeFontInit(hFont);
DC_UnlockDc(dc);
if (!TextObj)
{
DPRINT1("!TextObj\n");
return GDI_ERROR;
}
FontGDI = ObjToGDI(TextObj->Font, FONT);
TEXTOBJ_UnlockText(TextObj);
if (cwc == 0)
{
if (!UnSafepwc && !UnSafepgi)
{
Face = FontGDI->SharedFace->Face;
return Face->num_glyphs;
}
else
{
Status = STATUS_UNSUCCESSFUL;
goto ErrorRet;
}
}
Buffer = ExAllocatePoolWithTag(PagedPool, cwc * sizeof(WORD), GDITAG_TEXT);
if (!Buffer)
{
DPRINT1("ExAllocatePoolWithTag\n");
return GDI_ERROR;
}
/* Get DefChar */
if (iMode & GGI_MARK_NONEXISTING_GLYPHS)
{
DefChar = 0xffff;
}
else
{
Face = FontGDI->SharedFace->Face;
if (FT_IS_SFNT(Face))
{
IntLockFreeType();
pOS2 = FT_Get_Sfnt_Table(Face, ft_sfnt_os2);
DefChar = (pOS2->usDefaultChar ? get_glyph_index(Face, pOS2->usDefaultChar) : 0);
IntUnLockFreeType();
}
else
{
Size = IntGetOutlineTextMetrics(FontGDI, 0, NULL);
if (!Size)
{
Status = STATUS_UNSUCCESSFUL;
DPRINT1("!Size\n");
goto ErrorRet;
}
potm = ExAllocatePoolWithTag(PagedPool, Size, GDITAG_TEXT);
if (!potm)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
DPRINT1("!potm\n");
goto ErrorRet;
}
Size = IntGetOutlineTextMetrics(FontGDI, Size, potm);
if (Size)
DefChar = potm->otmTextMetrics.tmDefaultChar;
ExFreePoolWithTag(potm, GDITAG_TEXT);
}
}
/* Allocate for Safepwc */
pwcSize = cwc * sizeof(WCHAR);
Safepwc = ExAllocatePoolWithTag(PagedPool, pwcSize, GDITAG_TEXT);
if (!Safepwc)
{
Status = STATUS_NO_MEMORY;
DPRINT1("!Safepwc\n");
goto ErrorRet;
}
_SEH2_TRY
{
ProbeForRead(UnSafepwc, pwcSize, 1);
RtlCopyMemory(Safepwc, UnSafepwc, pwcSize);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
if (!NT_SUCCESS(Status))
{
DPRINT1("Status: %08lX\n", Status);
goto ErrorRet;
}
/* Get glyph indeces */
IntLockFreeType();
for (i = 0; i < cwc; i++)
{
Buffer[i] = get_glyph_index(FontGDI->SharedFace->Face, Safepwc[i]);
if (Buffer[i] == 0)
{
Buffer[i] = DefChar;
}
}
IntUnLockFreeType();
_SEH2_TRY
{
ProbeForWrite(UnSafepgi, cwc * sizeof(WORD), 1);
RtlCopyMemory(UnSafepgi, Buffer, cwc * sizeof(WORD));
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
ErrorRet:
if (Buffer != NULL)
{
ExFreePoolWithTag(Buffer, GDITAG_TEXT);
}
if (Safepwc != NULL)
{
ExFreePoolWithTag(Safepwc, GDITAG_TEXT);
}
if (NT_SUCCESS(Status))
return cwc;
return GDI_ERROR;
}
/* EOF */