X-Git-Url: https://git.reactos.org/?p=reactos.git;a=blobdiff_plain;f=win32ss%2Fgdi%2Fntgdi%2Ffreetype.c;h=e172c5c5376143a8662c6634c5bf87bdd08c1ee6;hp=5ac3b60af37580e119b0d815dc38a533ae885c6c;hb=c8749d379beeb3a078c296feca1f4cdb3e9d11f1;hpb=73d72624b097cb6c2f8481e75e99ed28ccbe907c diff --git a/win32ss/gdi/ntgdi/freetype.c b/win32ss/gdi/ntgdi/freetype.c index 5ac3b60af37..e172c5c5376 100644 --- a/win32ss/gdi/ntgdi/freetype.c +++ b/win32ss/gdi/ntgdi/freetype.c @@ -1,10 +1,11 @@ /* * PROJECT: ReactOS win32 kernel mode subsystem * LICENSE: GPL - See COPYING in the top level directory - * FILE: subsystems/win32/win32k/objects/freetype.c + * FILE: win32ss/gdi/ntgdi/freetype.c * PURPOSE: FreeType font engine interface - * PROGRAMMER: Copyright 2001 Huw D M Davies for CodeWeavers. + * PROGRAMMERS: Copyright 2001 Huw D M Davies for CodeWeavers. * Copyright 2006 Dmitry Timoshkov for CodeWeavers. + * Copyright 2016-2018 Katayama Hirofumi MZ. */ /** Includes ******************************************************************/ @@ -13,68 +14,84 @@ #include FT_GLYPH_H #include FT_TYPE1_TABLES_H -#include -#include -#include -#include -#include +#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 + #include FT_INTERNAL_INTERNAL_H +#endif +#include FT_INTERNAL_TRUETYPE_TYPES_H #include +#include "font.h" #define NDEBUG #include -#ifndef FT_MAKE_TAG -#define FT_MAKE_TAG( ch0, ch1, ch2, ch3 ) \ - ( ((DWORD)(BYTE)(ch0) << 24) | ((DWORD)(BYTE)(ch1) << 16) | \ - ((DWORD)(BYTE)(ch2) << 8) | (DWORD)(BYTE)(ch3) ) +/* TPMF_FIXED_PITCH is confusing; brain-dead api */ +#ifndef _TMPF_VARIABLE_PITCH + #define _TMPF_VARIABLE_PITCH TMPF_FIXED_PITCH #endif -FT_Library library; +extern const MATRIX gmxWorldToDeviceDefault; +extern const MATRIX gmxWorldToPageDefault; + +/* HACK!! Fix XFORMOBJ then use 1:16 / 16:1 */ +#define gmxWorldToDeviceDefault gmxWorldToPageDefault + +FT_Library g_FreeTypeLibrary; + +/* special font names */ +static const UNICODE_STRING g_MarlettW = RTL_CONSTANT_STRING(L"Marlett"); + +/* registry */ +static UNICODE_STRING g_FontRegPath = + RTL_CONSTANT_STRING(L"\\REGISTRY\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"); -typedef struct _FONT_ENTRY -{ - LIST_ENTRY ListEntry; - FONTGDI *Font; - UNICODE_STRING FaceName; - BYTE NotEnum; -} FONT_ENTRY, *PFONT_ENTRY; /* The FreeType library is not thread safe, so we have to serialize access to it */ -static PFAST_MUTEX FreeTypeLock; +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) -static LIST_ENTRY FontListHead; -static PFAST_MUTEX FontListLock; -static BOOL RenderingEnabled = TRUE; +#define IntUnLockGlobalFonts() \ + ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(g_FontListLock) -#define IntLockGlobalFonts \ - ExEnterCriticalRegionAndAcquireFastMutexUnsafe(FontListLock) +#define ASSERT_GLOBALFONTS_LOCK_HELD() \ + ASSERT(g_FontListLock->Owner == KeGetCurrentThread()) -#define IntUnLockGlobalFonts \ - ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(FontListLock) +#define IntLockFreeType() \ + ExEnterCriticalRegionAndAcquireFastMutexUnsafe(g_FreeTypeLock) -#define IntLockFreeType \ - ExEnterCriticalRegionAndAcquireFastMutexUnsafe(FreeTypeLock) +#define IntUnLockFreeType() \ + ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(g_FreeTypeLock) -#define IntUnLockFreeType \ - ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(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 -typedef struct _FONT_CACHE_ENTRY -{ - LIST_ENTRY ListEntry; - int GlyphIndex; - FT_Face Face; - FT_BitmapGlyph BitmapGlyph; - int Height; - MATRIX mxWorldToDevice; -} FONT_CACHE_ENTRY, *PFONT_CACHE_ENTRY; -static LIST_ENTRY FontCacheListHead; -static UINT FontCacheNumEntries; +static LIST_ENTRY g_FontCacheListHead; +static UINT g_FontCacheNumEntries; -static PWCHAR ElfScripts[32] = /* These are in the order of the fsCsb[0] bits */ +static PWCHAR g_ElfScripts[32] = /* These are in the order of the fsCsb[0] bits */ { L"Western", /* 00 */ L"Central_European", @@ -102,7 +119,7 @@ static PWCHAR ElfScripts[32] = /* These are in the order of the fsCsb[0] bits */ #define CP_SYMBOL 42 #define MAXTCIINDEX 32 -static const CHARSETINFO FontTci[MAXTCIINDEX] = +static const CHARSETINFO g_FontTci[MAXTCIINDEX] = { /* ANSI */ { ANSI_CHARSET, 1252, {{0,0,0,0},{FS_LATIN1,0}} }, @@ -143,21 +160,359 @@ static const CHARSETINFO FontTci[MAXTCIINDEX] = { 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 : ""); + } + 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 : ""); + 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(); +} + + +/* + * 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(&FontListHead); - InitializeListHead(&FontCacheListHead); - FontCacheNumEntries = 0; + InitializeListHead(&g_FontListHead); + InitializeListHead(&g_FontCacheListHead); + g_FontCacheNumEntries = 0; /* Fast Mutexes must be allocated from non paged pool */ - FontListLock = ExAllocatePoolWithTag(NonPagedPool, sizeof(FAST_MUTEX), TAG_INTERNAL_SYNC); - ExInitializeFastMutex(FontListLock); - FreeTypeLock = ExAllocatePoolWithTag(NonPagedPool, sizeof(FAST_MUTEX), TAG_INTERNAL_SYNC); - ExInitializeFastMutex(FreeTypeLock); + 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(&library); + ulError = FT_Init_FreeType(&g_FreeTypeLibrary); if (ulError) { DPRINT1("FT_Init_FreeType failed with error code 0x%x\n", ulError); @@ -165,6 +520,7 @@ InitFontSupport(VOID) } IntLoadSystemFonts(); + IntLoadFontSubstList(&g_FontSubstListHead); return TRUE; } @@ -198,32 +554,135 @@ FtSetCoordinateTransform( 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 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; + + RtlStringCchCopyW(pLogFont->lfFaceName, LF_FACESIZE, OutputNameW.Buffer); + + 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, SearchPattern, FileName, TempString; + 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") + }; RtlInitUnicodeString(&Directory, L"\\SystemRoot\\Fonts\\"); - /* FIXME: Add support for other font types */ - RtlInitUnicodeString(&SearchPattern, L"*.ttf"); InitializeObjectAttributes( &ObjectAttributes, &Directory, - OBJ_CASE_INSENSITIVE, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); @@ -237,190 +696,358 @@ IntLoadSystemFonts(VOID) if (NT_SUCCESS(Status)) { - DirInfoBuffer = ExAllocatePoolWithTag(PagedPool, 0x4000, TAG_FONT); - if (DirInfoBuffer == NULL) + for (i = 0; i < _countof(SearchPatterns); ++i) { - ZwClose(hDirectory); - return; - } + 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, - &SearchPattern, - bRestartScan); - - if (!NT_SUCCESS(Status) || Status == STATUS_NO_MORE_FILES) + FileName.Buffer = ExAllocatePoolWithTag(PagedPool, MAX_PATH * sizeof(WCHAR), TAG_FONT); + if (FileName.Buffer == NULL) { - break; + ExFreePoolWithTag(DirInfoBuffer, TAG_FONT); + ZwClose(hDirectory); + return; } + FileName.Length = 0; + FileName.MaximumLength = MAX_PATH * sizeof(WCHAR); - DirInfo = (PFILE_DIRECTORY_INFORMATION)DirInfoBuffer; while (1) { - TempString.Buffer = DirInfo->FileName; - TempString.Length = - TempString.MaximumLength = DirInfo->FileNameLength; - RtlCopyUnicodeString(&FileName, &Directory); - RtlAppendUnicodeStringToString(&FileName, &TempString); - IntGdiAddFontResource(&FileName, 0); - if (DirInfo->NextEntryOffset == 0) + 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)((ULONG_PTR)DirInfo + DirInfo->NextEntryOffset); + } + + DirInfo = (PFILE_DIRECTORY_INFORMATION)DirInfoBuffer; + while (1) + { + TempString.Buffer = DirInfo->FileName; + TempString.Length = + TempString.MaximumLength = DirInfo->FileNameLength; + RtlCopyUnicodeString(&FileName, &Directory); + RtlAppendUnicodeStringToString(&FileName, &TempString); + IntGdiAddFontResource(&FileName, 0); + if (DirInfo->NextEntryOffset == 0) + break; + DirInfo = (PFILE_DIRECTORY_INFORMATION)((ULONG_PTR)DirInfo + DirInfo->NextEntryOffset); + } + + bRestartScan = FALSE; } - bRestartScan = FALSE; + ExFreePoolWithTag(FileName.Buffer, TAG_FONT); + ExFreePoolWithTag(DirInfoBuffer, TAG_FONT); } - - ExFreePoolWithTag(FileName.Buffer, TAG_FONT); - ExFreePoolWithTag(DirInfoBuffer, TAG_FONT); ZwClose(hDirectory); } } +static BYTE +ItalicFromStyle(const char *style_name) +{ + if (style_name == NULL || style_name[0] == 0) + return FALSE; + if (strstr(style_name, "Italic") != NULL) + return TRUE; + if (strstr(style_name, "Oblique") != NULL) + return TRUE; + return FALSE; +} -/* - * IntGdiAddFontResource - * - * Adds the font resource from the specified file to the system. - */ - -INT FASTCALL -IntGdiAddFontResource(PUNICODE_STRING FileName, DWORD Characteristics) +static LONG +WeightFromStyle(const char *style_name) { - FONTGDI *FontGDI; - NTSTATUS Status; - HANDLE FileHandle, KeyHandle; - OBJECT_ATTRIBUTES ObjectAttributes; - PVOID Buffer = NULL; - IO_STATUS_BLOCK Iosb; - INT Error; - FT_Face Face; - ANSI_STRING AnsiFaceName; - PFONT_ENTRY Entry; - PVOID SectionObject; - ULONG ViewSize = 0; - LARGE_INTEGER SectionSize; - UNICODE_STRING FontRegPath = RTL_CONSTANT_STRING(L"\\REGISTRY\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"); + if (style_name == NULL || style_name[0] == 0) + return FW_NORMAL; + if (strstr(style_name, "Regular") != NULL) + return FW_REGULAR; + if (strstr(style_name, "Normal") != NULL) + return FW_NORMAL; + if (strstr(style_name, "SemiBold") != NULL) + return FW_SEMIBOLD; + if (strstr(style_name, "UltraBold") != NULL) + return FW_ULTRABOLD; + if (strstr(style_name, "DemiBold") != NULL) + return FW_DEMIBOLD; + if (strstr(style_name, "ExtraBold") != NULL) + return FW_EXTRABOLD; + if (strstr(style_name, "Bold") != NULL) + return FW_BOLD; + if (strstr(style_name, "UltraLight") != NULL) + return FW_ULTRALIGHT; + if (strstr(style_name, "ExtraLight") != NULL) + return FW_EXTRALIGHT; + if (strstr(style_name, "Light") != NULL) + return FW_LIGHT; + if (strstr(style_name, "Hairline") != NULL) + return 50; + if (strstr(style_name, "Book") != NULL) + return 350; + if (strstr(style_name, "ExtraBlack") != NULL) + return 950; + if (strstr(style_name, "UltraBlack") != NULL) + return 1000; + if (strstr(style_name, "Black") != NULL) + return FW_BLACK; + if (strstr(style_name, "Medium") != NULL) + return FW_MEDIUM; + if (strstr(style_name, "Thin") != NULL) + return FW_THIN; + if (strstr(style_name, "Heavy") != NULL) + return FW_HEAVY; + return FW_NORMAL; +} - /* Open the font file */ +static FT_Error +IntRequestFontSize(PDC dc, PFONTGDI FontGDI, LONG lfWidth, LONG lfHeight); - InitializeObjectAttributes(&ObjectAttributes, FileName, 0, NULL, NULL); - Status = ZwOpenFile( - &FileHandle, - FILE_GENERIC_READ | SYNCHRONIZE, - &ObjectAttributes, - &Iosb, - FILE_SHARE_READ, - FILE_SYNCHRONOUS_IO_NONALERT); +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 AnsiFaceName; + 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 (!NT_SUCCESS(Status)) + 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 { - DPRINT("Could not load font file: %wZ\n", FileName); - return 0; + Face = SharedFace->Face; + IntLockFreeType(); + SharedFace_AddRef(SharedFace); + IntUnLockFreeType(); } - SectionSize.QuadPart = 0LL; - Status = MmCreateSection(&SectionObject, SECTION_ALL_ACCESS, - NULL, &SectionSize, PAGE_READONLY, - SEC_COMMIT, FileHandle, NULL); - if (!NT_SUCCESS(Status)) + /* allocate a FONT_ENTRY */ + Entry = ExAllocatePoolWithTag(PagedPool, sizeof(FONT_ENTRY), TAG_FONT); + if (!Entry) { - DPRINT("Could not map file: %wZ\n", FileName); - ZwClose(FileHandle); - return 0; + SharedFace_Release(SharedFace); + EngSetLastError(ERROR_NOT_ENOUGH_MEMORY); + return 0; /* failure */ } - ZwClose(FileHandle); - - Status = MmMapViewInSystemSpace(SectionObject, &Buffer, &ViewSize); - if (!NT_SUCCESS(Status)) + /* allocate a FONTGDI */ + FontGDI = EngAllocMem(FL_ZERO_MEMORY, sizeof(FONTGDI), GDITAG_RFONT); + if (!FontGDI) { - DPRINT("Could not map file: %wZ\n", FileName); - ObDereferenceObject(SectionObject); - return 0; + SharedFace_Release(SharedFace); + ExFreePoolWithTag(Entry, TAG_FONT); + EngSetLastError(ERROR_NOT_ENOUGH_MEMORY); + return 0; /* failure */ } - IntLockFreeType; - Error = FT_New_Memory_Face( - library, - Buffer, - ViewSize, - 0, - &Face); - IntUnLockFreeType; - ObDereferenceObject(SectionObject); - - if (Error) + /* 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 { - if (Error == FT_Err_Unknown_File_Format) - DPRINT("Unknown font file format\n"); + 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 - DPRINT("Error reading font file (error code: %d)\n", Error); - return 0; + { + InitializeListHead(&PrivateEntry->ListEntry); + pLoadFont->PrivateEntry = PrivateEntry; + } } - Entry = ExAllocatePoolWithTag(PagedPool, sizeof(FONT_ENTRY), TAG_FONT); - if (!Entry) + /* set face */ + FontGDI->SharedFace = SharedFace; + FontGDI->CharSet = ANSI_CHARSET; + FontGDI->OriginalItalic = ItalicFromStyle(Face->style_name); + FontGDI->RequestItalic = FALSE; + FontGDI->OriginalWeight = WeightFromStyle(Face->style_name); + FontGDI->RequestWeight = FW_NORMAL; + + RtlInitAnsiString(&AnsiFaceName, Face->family_name); + Status = RtlAnsiStringToUnicodeString(&Entry->FaceName, &AnsiFaceName, TRUE); + if (!NT_SUCCESS(Status)) { - FT_Done_Face(Face); - EngSetLastError(ERROR_NOT_ENOUGH_MEMORY); + 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; } - FontGDI = EngAllocMem(FL_ZERO_MEMORY, sizeof(FONTGDI), GDITAG_RFONT); - if (FontGDI == NULL) + os2_version = 0; + IntLockFreeType(); + pOS2 = (TT_OS2 *)FT_Get_Sfnt_Table(Face, FT_SFNT_OS2); + if (pOS2) { - FT_Done_Face(Face); - ExFreePoolWithTag(Entry, TAG_FONT); - EngSetLastError(ERROR_NOT_ENOUGH_MEMORY); - return 0; + os2_version = pOS2->version; + os2_ulCodePageRange1 = pOS2->ulCodePageRange1; + os2_usWeightClass = pOS2->usWeightClass; } + IntUnLockFreeType(); - FontGDI->Filename = ExAllocatePoolWithTag(PagedPool, FileName->Length + sizeof(WCHAR), GDITAG_PFF); - if (FontGDI->Filename == NULL) + if (pOS2 && os2_version >= 1) { - EngFreeMem(FontGDI); - FT_Done_Face(Face); - ExFreePoolWithTag(Entry, TAG_FONT); - EngSetLastError(ERROR_NOT_ENOUGH_MEMORY); - return 0; + /* 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(); + } + + /* FIXME: CharSet is invalid on Marlett */ + if (RtlEqualUnicodeString(&Entry->FaceName, &g_MarlettW, TRUE)) + { + FontGDI->CharSet = SYMBOL_CHARSET; } - RtlCopyMemory(FontGDI->Filename, FileName->Buffer, FileName->Length); - FontGDI->Filename[FileName->Length / sizeof(WCHAR)] = L'\0'; - FontGDI->face = Face; - DPRINT("Font loaded: %s (%s)\n", Face->family_name, Face->style_name); + ++FaceCount; + DPRINT("Font loaded: %s (%s)\n", + Face->family_name ? Face->family_name : "", + Face->style_name ? Face->style_name : ""); DPRINT("Num glyphs: %d\n", Face->num_glyphs); + DPRINT("CharSet: %d\n", FontGDI->CharSet); - /* Add this font resource to the font table */ + IntLockFreeType(); + IntRequestFontSize(NULL, FontGDI, 0, 0); + IntUnLockFreeType(); + /* Add this font resource to the font table */ Entry->Font = FontGDI; Entry->NotEnum = (Characteristics & FR_NOT_ENUM); - RtlInitAnsiString(&AnsiFaceName, (LPSTR)Face->family_name); - RtlAnsiStringToUnicodeString(&Entry->FaceName, &AnsiFaceName, TRUE); if (Characteristics & FR_PRIVATE) { + /* private font */ PPROCESSINFO Win32Process = PsGetCurrentProcessWin32Process(); IntLockProcessPrivateFonts(Win32Process); InsertTailList(&Win32Process->PrivateFontListHead, &Entry->ListEntry); @@ -428,34 +1055,376 @@ IntGdiAddFontResource(PUNICODE_STRING FileName, DWORD Characteristics) } else { - IntLockGlobalFonts; - InsertTailList(&FontListHead, &Entry->ListEntry); - InitializeObjectAttributes(&ObjectAttributes, &FontRegPath, OBJ_CASE_INSENSITIVE, NULL, NULL); + /* 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; + + if (pLoadFont->RegValueName.Length == 0) + { + RtlCreateUnicodeString(pValueName, Entry->FaceName.Buffer); + } + else + { + UNICODE_STRING NewString; + USHORT Length = pValueName->Length + 3 * sizeof(WCHAR) + Entry->FaceName.Length; + 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; + } + + for (i = 1; i < CharSetCount; ++i) + { + /* Do not count charsets towards 'faces' loaded */ + IntGdiLoadFontsFromMemory(pLoadFont, SharedFace, FontIndex, i); + } + } + + return FaceCount; /* number of loaded faces */ +} + +/* + * IntGdiAddFontResource + * + * Adds the font resource from the specified file to the system. + */ + +INT FASTCALL +IntGdiAddFontResource(PUNICODE_STRING FileName, DWORD Characteristics) +{ + NTSTATUS Status; + HANDLE FileHandle; + PVOID Buffer = NULL; + IO_STATUS_BLOCK Iosb; + PVOID SectionObject; + SIZE_T ViewSize = 0; + LARGE_INTEGER SectionSize; + OBJECT_ATTRIBUTES ObjectAttributes; + GDI_LOAD_FONT LoadFont; + INT FontCount; + HANDLE KeyHandle; + static const UNICODE_STRING TrueTypePostfix = RTL_CONSTANT_STRING(L" (TrueType)"); + + /* Open the font file */ + InitializeObjectAttributes(&ObjectAttributes, FileName, 0, NULL, NULL); + Status = ZwOpenFile( + &FileHandle, + FILE_GENERIC_READ | SYNCHRONIZE, + &ObjectAttributes, + &Iosb, + FILE_SHARE_READ, + FILE_SYNCHRONOUS_IO_NONALERT); + if (!NT_SUCCESS(Status)) + { + DPRINT("Could not load font file: %wZ\n", FileName); + return 0; + } + + SectionSize.QuadPart = 0LL; + Status = MmCreateSection(&SectionObject, SECTION_ALL_ACCESS, + NULL, &SectionSize, PAGE_READONLY, + SEC_COMMIT, FileHandle, NULL); + if (!NT_SUCCESS(Status)) + { + DPRINT("Could not map file: %wZ\n", FileName); + ZwClose(FileHandle); + return 0; + } + ZwClose(FileHandle); + + Status = MmMapViewInSystemSpace(SectionObject, &Buffer, &ViewSize); + if (!NT_SUCCESS(Status)) + { + DPRINT("Could not map file: %wZ\n", FileName); + ObDereferenceObject(SectionObject); + return 0; + } + + LoadFont.pFileName = FileName; + LoadFont.Memory = SharedMem_Create(Buffer, ViewSize, TRUE); + LoadFont.Characteristics = Characteristics; + RtlInitUnicodeString(&LoadFont.RegValueName, NULL); + LoadFont.IsTrueType = FALSE; + LoadFont.PrivateEntry = NULL; + FontCount = IntGdiLoadFontsFromMemory(&LoadFont, NULL, -1, -1); + + ObDereferenceObject(SectionObject); + + /* Release our copy */ + IntLockFreeType(); + SharedMem_Release(LoadFont.Memory); + IntUnLockFreeType(); + + if (FontCount > 0) + { + if (LoadFont.IsTrueType) + { + /* append " (TrueType)" */ + UNICODE_STRING NewString; + USHORT Length; + + Length = LoadFont.RegValueName.Length + TrueTypePostfix.Length; + NewString.Length = 0; + NewString.MaximumLength = Length + sizeof(WCHAR); + NewString.Buffer = ExAllocatePoolWithTag(PagedPool, + NewString.MaximumLength, + TAG_USTR); + NewString.Buffer[0] = UNICODE_NULL; + + RtlAppendUnicodeStringToString(&NewString, &LoadFont.RegValueName); + RtlAppendUnicodeStringToString(&NewString, &TrueTypePostfix); + RtlFreeUnicodeString(&LoadFont.RegValueName); + LoadFont.RegValueName = NewString; + } + + /* registry */ + InitializeObjectAttributes(&ObjectAttributes, &g_FontRegPath, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, NULL); Status = ZwOpenKey(&KeyHandle, KEY_WRITE, &ObjectAttributes); if (NT_SUCCESS(Status)) { - LPWSTR pName = wcsrchr(FileName->Buffer, L'\\'); - if (pName) + SIZE_T DataSize; + LPWSTR pFileName = wcsrchr(FileName->Buffer, L'\\'); + if (pFileName) { - pName++; - ZwSetValueKey(KeyHandle, &Entry->FaceName, 0, REG_SZ, pName, (wcslen(pName) + 1) * sizeof(WCHAR)); + pFileName++; + DataSize = (wcslen(pFileName) + 1) * sizeof(WCHAR); + ZwSetValueKey(KeyHandle, &LoadFont.RegValueName, 0, REG_SZ, + pFileName, DataSize); } ZwClose(KeyHandle); } - IntUnLockGlobalFonts; } - return 1; + RtlFreeUnicodeString(&LoadFont.RegValueName); + + return FontCount; +} + +HANDLE FASTCALL +IntGdiAddFontMemResource(PVOID Buffer, DWORD dwSize, PDWORD pNumAdded) +{ + GDI_LOAD_FONT LoadFont; + FONT_ENTRY_COLL_MEM* EntryCollection; + INT FaceCount; + HANDLE Ret = 0; + + PVOID BufferCopy = ExAllocatePoolWithTag(PagedPool, dwSize, TAG_FONT); + + if (!BufferCopy) + { + *pNumAdded = 0; + return NULL; + } + memcpy(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 + +static VOID FASTCALL +CleanupFontEntry(PFONT_ENTRY FontEntry) +{ + PFONTGDI FontGDI = FontEntry->Font; + PSHARED_FACE SharedFace = FontGDI->SharedFace; + + if (FontGDI->Filename) + ExFreePoolWithTag(FontGDI->Filename, GDITAG_PFF); + + EngFreeMem(FontGDI); + SharedFace_Release(SharedFace); + ExFreePoolWithTag(FontEntry, TAG_FONT); +} + +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) { - BOOL Ret = RenderingEnabled; + BOOL Ret = g_RenderingEnabled; HDC hDC; hDC = IntGetScreenDC(); if (hDC) - Ret = (NtGdiGetDeviceCaps(hDC, BITSPIXEL) > 8) && RenderingEnabled; + Ret = (NtGdiGetDeviceCaps(hDC, BITSPIXEL) > 8) && g_RenderingEnabled; return Ret; } @@ -463,7 +1432,7 @@ IntIsFontRenderingEnabled(VOID) VOID FASTCALL IntEnableFontRendering(BOOL Enable) { - RenderingEnabled = Enable; + g_RenderingEnabled = Enable; } FT_Render_Mode FASTCALL @@ -471,6 +1440,8 @@ IntGetFontRenderMode(LOGFONTW *logfont) { switch (logfont->lfQuality) { + case ANTIALIASED_QUALITY: + break; case NONANTIALIASED_QUALITY: return FT_RENDER_MODE_MONO; case DRAFT_QUALITY: @@ -486,6 +1457,7 @@ NTSTATUS FASTCALL TextIntCreateFontIndirect(CONST LPLOGFONTW lf, HFONT *NewFont) { PLFONT plfont; + LOGFONTW *plf; plfont = LFONT_AllocFontWithHandle(); if (!plfont) @@ -495,12 +1467,12 @@ TextIntCreateFontIndirect(CONST LPLOGFONTW lf, HFONT *NewFont) ExInitializePushLock(&plfont->lock); *NewFont = plfont->BaseObject.hHmgr; - RtlCopyMemory(&plfont->logfont.elfEnumLogfontEx.elfLogFont, lf, sizeof(LOGFONTW)); + 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 */ - plfont->logfont.elfEnumLogfontEx.elfLogFont.lfOrientation = - plfont->logfont.elfEnumLogfontEx.elfLogFont.lfEscapement; + plf->lfOrientation = plf->lfEscapement; } LFONT_UnlockFont(plfont); @@ -541,13 +1513,13 @@ IntTranslateCharsetInfo(PDWORD Src, /* [in] } break; case TCI_SRCCODEPAGE: - while (Index < MAXTCIINDEX && *Src != FontTci[Index].ciACP) + while (Index < MAXTCIINDEX && *Src != g_FontTci[Index].ciACP) { Index++; } break; case TCI_SRCCHARSET: - while (Index < MAXTCIINDEX && *Src != FontTci[Index].ciCharset) + while (Index < MAXTCIINDEX && *Src != g_FontTci[Index].ciCharset) { Index++; } @@ -556,12 +1528,12 @@ IntTranslateCharsetInfo(PDWORD Src, /* [in] return FALSE; } - if (Index >= MAXTCIINDEX || DEFAULT_CHARSET == FontTci[Index].ciCharset) + if (Index >= MAXTCIINDEX || DEFAULT_CHARSET == g_FontTci[Index].ciCharset) { return FALSE; } - RtlCopyMemory(Cs, &FontTci[Index], sizeof(CHARSETINFO)); + RtlCopyMemory(Cs, &g_FontTci[Index], sizeof(CHARSETINFO)); return TRUE; } @@ -573,44 +1545,60 @@ static BOOL face_has_symbol_charmap(FT_Face ft_face) for(i = 0; i < ft_face->num_charmaps; i++) { - if(ft_face->charmaps[i]->encoding == FT_ENCODING_MS_SYMBOL) + 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 *pWin) +FillTMEx(TEXTMETRICW *TM, PFONTGDI FontGDI, + TT_OS2 *pOS2, TT_HoriHeader *pHori, + FT_WinFNT_HeaderRec *pFNT, BOOL RealFont) { FT_Fixed XScale, YScale; int Ascent, Descent; - FT_Face Face = FontGDI->face; + FT_Face Face = FontGDI->SharedFace->Face; XScale = Face->size->metrics.x_scale; YScale = Face->size->metrics.y_scale; - if (pWin) + if (pFNT) { - TM->tmHeight = pWin->pixel_height; - TM->tmAscent = pWin->ascent; + TM->tmHeight = pFNT->pixel_height; + TM->tmAscent = pFNT->ascent; TM->tmDescent = TM->tmHeight - TM->tmAscent; - TM->tmInternalLeading = pWin->internal_leading; - TM->tmExternalLeading = pWin->external_leading; - TM->tmAveCharWidth = pWin->avg_width; - TM->tmMaxCharWidth = pWin->max_width; - TM->tmWeight = pWin->weight; + 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 = pWin->horizontal_resolution; - TM->tmDigitizedAspectY = pWin->vertical_resolution; - TM->tmFirstChar = pWin->first_char; - TM->tmLastChar = pWin->last_char; - TM->tmDefaultChar = pWin->default_char + pWin->first_char; - TM->tmBreakChar = pWin->break_char + pWin->first_char; - TM->tmItalic = pWin->italic; - TM->tmUnderlined = FontGDI->Underline; - TM->tmStruckOut = FontGDI->StrikeOut; - TM->tmPitchAndFamily = pWin->pitch_and_family; - TM->tmCharSet = pWin->charset; + 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; + if (RealFont) + { + TM->tmWeight = FontGDI->OriginalWeight; + TM->tmItalic = FontGDI->OriginalItalic; + TM->tmUnderlined = pFNT->underline; + TM->tmStruckOut = pFNT->strike_out; + TM->tmCharSet = pFNT->charset; + } + else + { + TM->tmWeight = FontGDI->RequestWeight; + TM->tmItalic = FontGDI->RequestItalic; + TM->tmUnderlined = FontGDI->RequestUnderline; + TM->tmStruckOut = FontGDI->RequestStrikeOut; + TM->tmCharSet = FontGDI->CharSet; + } return; } @@ -625,16 +1613,14 @@ FillTM(TEXTMETRICW *TM, PFONTGDI FontGDI, TT_OS2 *pOS2, TT_HoriHeader *pHori, FT Descent = pOS2->usWinDescent; } -#if 0 /* This (Wine) code doesn't seem to work correctly for us, cmd issue */ - TM->tmAscent = (FT_MulFix(Ascent, YScale) + 32) >> 6; - TM->tmDescent = (FT_MulFix(Descent, YScale) + 32) >> 6; -#else /* This (ros) code was previously affected by a FreeType bug, but it works now */ - TM->tmAscent = (Face->size->metrics.ascender + 32) >> 6; /* Units above baseline */ - TM->tmDescent = (32 - Face->size->metrics.descender) >> 6; /* Units below baseline */ -#endif - TM->tmInternalLeading = (FT_MulFix(Ascent + Descent - Face->units_per_EM, YScale) + 32) >> 6; - + if (FontGDI->Magic != FONTGDI_MAGIC) + { + IntRequestFontSize(NULL, FontGDI, 0, 0); + } + 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))) @@ -653,11 +1639,28 @@ FillTM(TEXTMETRICW *TM, PFONTGDI FontGDI, TT_OS2 *pOS2, TT_HoriHeader *pHori, FT /* Correct forumla to get the maxcharwidth from unicode and ansi font */ TM->tmMaxCharWidth = (FT_MulFix(Face->max_advance_width, XScale) + 32) >> 6; - TM->tmWeight = pOS2->usWeightClass; + if (RealFont) + { + TM->tmWeight = FontGDI->OriginalWeight; + } + else + { + 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)) + if (face_has_symbol_charmap(Face) || + (pOS2->usFirstCharIndex >= 0xf000 && pOS2->usFirstCharIndex < 0xf100)) { USHORT cpOEM, cpAnsi; @@ -687,22 +1690,46 @@ FillTM(TEXTMETRICW *TM, PFONTGDI FontGDI, TT_OS2 *pOS2, TT_HoriHeader *pHori, FT TM->tmBreakChar = pOS2->usFirstCharIndex; TM->tmDefaultChar = TM->tmBreakChar - 1; } - TM->tmItalic = (Face->style_flags & FT_STYLE_FLAG_ITALIC) ? 255 : 0; - TM->tmUnderlined = FontGDI->Underline; - TM->tmStruckOut = FontGDI->StrikeOut; - /* Yes TPMF_FIXED_PITCH is correct; braindead api */ - if (! FT_IS_FIXED_WIDTH(Face)) + if (RealFont) { - TM->tmPitchAndFamily = TMPF_FIXED_PITCH; + TM->tmItalic = FontGDI->OriginalItalic; + TM->tmUnderlined = FALSE; + TM->tmStruckOut = FALSE; } else { - TM->tmPitchAndFamily = 0; - } - - switch (pOS2->panose[PAN_FAMILYTYPE_INDEX]) - { + 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; @@ -764,9 +1791,21 @@ FillTM(TEXTMETRICW *TM, PFONTGDI FontGDI, TT_OS2 *pOS2, TT_HoriHeader *pHori, FT TM->tmPitchAndFamily |= TMPF_TRUETYPE; } - TM->tmCharSet = DEFAULT_CHARSET; + TM->tmCharSet = FontGDI->CharSet; +} + +static void FASTCALL +FillTM(TEXTMETRICW *TM, PFONTGDI FontGDI, + TT_OS2 *pOS2, TT_HoriHeader *pHori, + FT_WinFNT_HeaderRec *pFNT) +{ + FillTMEx(TM, FontGDI, pOS2, pHori, pFNT, FALSE); } +static NTSTATUS +IntGetFontLocalizedName(PUNICODE_STRING pNameW, PSHARED_FACE SharedFace, + FT_UShort NameID, FT_UShort LangID); + /************************************************************* * IntGetOutlineTextMetrics * @@ -776,83 +1815,93 @@ IntGetOutlineTextMetrics(PFONTGDI FontGDI, UINT Size, OUTLINETEXTMETRICW *Otm) { - unsigned Needed; TT_OS2 *pOS2; TT_HoriHeader *pHori; TT_Postscript *pPost; FT_Fixed XScale, YScale; - ANSI_STRING FamilyNameA, StyleNameA; - UNICODE_STRING FamilyNameW, StyleNameW, Regular; FT_WinFNT_HeaderRec Win; FT_Error Error; char *Cp; + UNICODE_STRING FamilyNameW, FaceNameW, StyleNameW, FullNameW; + PSHARED_FACE SharedFace = FontGDI->SharedFace; + PSHARED_FACE_CACHE Cache = (PRIMARYLANGID(gusLanguageID) == LANG_ENGLISH) ? &SharedFace->EnglishUS : &SharedFace->UserLanguage; + FT_Face Face = SharedFace->Face; - Needed = sizeof(OUTLINETEXTMETRICW); + if (Cache->OutlineRequiredSize && Size < Cache->OutlineRequiredSize) + { + return Cache->OutlineRequiredSize; + } - RtlInitAnsiString(&FamilyNameA, FontGDI->face->family_name); - RtlAnsiStringToUnicodeString(&FamilyNameW, &FamilyNameA, TRUE); + /* family name */ + RtlInitUnicodeString(&FamilyNameW, NULL); + IntGetFontLocalizedName(&FamilyNameW, SharedFace, TT_NAME_ID_FONT_FAMILY, gusLanguageID); - RtlInitAnsiString(&StyleNameA, FontGDI->face->style_name); - RtlAnsiStringToUnicodeString(&StyleNameW, &StyleNameA, TRUE); + /* face name */ + RtlInitUnicodeString(&FaceNameW, NULL); + IntGetFontLocalizedName(&FaceNameW, SharedFace, TT_NAME_ID_FULL_NAME, gusLanguageID); - /* These names should be read from the TT name table */ + /* style name */ + RtlInitUnicodeString(&StyleNameW, NULL); + IntGetFontLocalizedName(&StyleNameW, SharedFace, TT_NAME_ID_FONT_SUBFAMILY, gusLanguageID); - /* Length of otmpFamilyName */ - Needed += FamilyNameW.Length + sizeof(WCHAR); + /* unique name (full name) */ + RtlInitUnicodeString(&FullNameW, NULL); + IntGetFontLocalizedName(&FullNameW, SharedFace, TT_NAME_ID_UNIQUE_ID, gusLanguageID); - RtlInitUnicodeString(&Regular, L"regular"); - /* Length of otmpFaceName */ - if (0 == RtlCompareUnicodeString(&StyleNameW, &Regular, TRUE)) - { - Needed += FamilyNameW.Length + sizeof(WCHAR); /* Just the family name */ - } - else + if (!Cache->OutlineRequiredSize) { - Needed += FamilyNameW.Length + StyleNameW.Length + (sizeof(WCHAR) << 1); /* family + " " + style */ - } - - /* Length of otmpStyleName */ - Needed += StyleNameW.Length + sizeof(WCHAR); + UINT Needed; + Needed = sizeof(OUTLINETEXTMETRICW); + Needed += FamilyNameW.Length + sizeof(WCHAR); + Needed += FaceNameW.Length + sizeof(WCHAR); + Needed += StyleNameW.Length + sizeof(WCHAR); + Needed += FullNameW.Length + sizeof(WCHAR); - /* Length of otmpFullName */ - Needed += FamilyNameW.Length + StyleNameW.Length + (sizeof(WCHAR) << 1); + Cache->OutlineRequiredSize = Needed; + } - if (Size < Needed) + if (Size < Cache->OutlineRequiredSize) { RtlFreeUnicodeString(&FamilyNameW); + RtlFreeUnicodeString(&FaceNameW); RtlFreeUnicodeString(&StyleNameW); - return Needed; + RtlFreeUnicodeString(&FullNameW); + return Cache->OutlineRequiredSize; } - XScale = FontGDI->face->size->metrics.x_scale; - YScale = FontGDI->face->size->metrics.y_scale; + XScale = Face->size->metrics.x_scale; + YScale = Face->size->metrics.y_scale; - IntLockFreeType; - pOS2 = FT_Get_Sfnt_Table(FontGDI->face, ft_sfnt_os2); + IntLockFreeType(); + pOS2 = FT_Get_Sfnt_Table(Face, ft_sfnt_os2); if (NULL == pOS2) { - IntUnLockFreeType; + IntUnLockFreeType(); DPRINT1("Can't find OS/2 table - not TT font?\n"); - RtlFreeUnicodeString(&StyleNameW); RtlFreeUnicodeString(&FamilyNameW); + RtlFreeUnicodeString(&FaceNameW); + RtlFreeUnicodeString(&StyleNameW); + RtlFreeUnicodeString(&FullNameW); return 0; } - pHori = FT_Get_Sfnt_Table(FontGDI->face, ft_sfnt_hhea); + pHori = FT_Get_Sfnt_Table(Face, ft_sfnt_hhea); if (NULL == pHori) { - IntUnLockFreeType; + IntUnLockFreeType(); DPRINT1("Can't find HHEA table - not TT font?\n"); - RtlFreeUnicodeString(&StyleNameW); RtlFreeUnicodeString(&FamilyNameW); + RtlFreeUnicodeString(&FaceNameW); + RtlFreeUnicodeString(&StyleNameW); + RtlFreeUnicodeString(&FullNameW); return 0; } - pPost = FT_Get_Sfnt_Table(FontGDI->face, ft_sfnt_post); /* We can live with this failing */ + pPost = FT_Get_Sfnt_Table(Face, ft_sfnt_post); /* We can live with this failing */ - Error = FT_Get_WinFNT_Header(FontGDI->face , &Win); + Error = FT_Get_WinFNT_Header(Face , &Win); - Otm->otmSize = Needed; + Otm->otmSize = Cache->OutlineRequiredSize; FillTM(&Otm->otmTextMetrics, FontGDI, pOS2, pHori, !Error ? &Win : 0); @@ -863,16 +1912,16 @@ IntGetOutlineTextMetrics(PFONTGDI FontGDI, Otm->otmsCharSlopeRise = pHori->caret_Slope_Rise; Otm->otmsCharSlopeRun = pHori->caret_Slope_Run; Otm->otmItalicAngle = 0; /* POST table */ - Otm->otmEMSquare = FontGDI->face->units_per_EM; + Otm->otmEMSquare = Face->units_per_EM; Otm->otmAscent = (FT_MulFix(pOS2->sTypoAscender, YScale) + 32) >> 6; Otm->otmDescent = (FT_MulFix(pOS2->sTypoDescender, YScale) + 32) >> 6; Otm->otmLineGap = (FT_MulFix(pOS2->sTypoLineGap, YScale) + 32) >> 6; Otm->otmsCapEmHeight = (FT_MulFix(pOS2->sCapHeight, YScale) + 32) >> 6; Otm->otmsXHeight = (FT_MulFix(pOS2->sxHeight, YScale) + 32) >> 6; - Otm->otmrcFontBox.left = (FT_MulFix(FontGDI->face->bbox.xMin, XScale) + 32) >> 6; - Otm->otmrcFontBox.right = (FT_MulFix(FontGDI->face->bbox.xMax, XScale) + 32) >> 6; - Otm->otmrcFontBox.top = (FT_MulFix(FontGDI->face->bbox.yMax, YScale) + 32) >> 6; - Otm->otmrcFontBox.bottom = (FT_MulFix(FontGDI->face->bbox.yMin, YScale) + 32) >> 6; + Otm->otmrcFontBox.left = (FT_MulFix(Face->bbox.xMin, XScale) + 32) >> 6; + Otm->otmrcFontBox.right = (FT_MulFix(Face->bbox.xMax, XScale) + 32) >> 6; + Otm->otmrcFontBox.top = (FT_MulFix(Face->bbox.yMax, YScale) + 32) >> 6; + Otm->otmrcFontBox.bottom = (FT_MulFix(Face->bbox.yMin, YScale) + 32) >> 6; Otm->otmMacAscent = Otm->otmTextMetrics.tmAscent; Otm->otmMacDescent = -Otm->otmTextMetrics.tmDescent; Otm->otmMacLineGap = Otm->otmLineGap; @@ -898,37 +1947,38 @@ IntGetOutlineTextMetrics(PFONTGDI FontGDI, Otm->otmsUnderscorePosition = (FT_MulFix(pPost->underlinePosition, YScale) + 32) >> 6; } - IntUnLockFreeType; + IntUnLockFreeType(); - /* otmp* members should clearly have type ptrdiff_t, but M$ knows best */ Cp = (char*) Otm + sizeof(OUTLINETEXTMETRICW); + + /* family name */ Otm->otmpFamilyName = (LPSTR)(Cp - (char*) Otm); wcscpy((WCHAR*) Cp, FamilyNameW.Buffer); Cp += FamilyNameW.Length + sizeof(WCHAR); + + /* face name */ + Otm->otmpFaceName = (LPSTR)(Cp - (char*) Otm); + wcscpy((WCHAR*) Cp, FaceNameW.Buffer); + Cp += FaceNameW.Length + sizeof(WCHAR); + + /* style name */ Otm->otmpStyleName = (LPSTR)(Cp - (char*) Otm); wcscpy((WCHAR*) Cp, StyleNameW.Buffer); Cp += StyleNameW.Length + sizeof(WCHAR); - Otm->otmpFaceName = (LPSTR)(Cp - (char*) Otm); - wcscpy((WCHAR*) Cp, FamilyNameW.Buffer); - if (0 != RtlCompareUnicodeString(&StyleNameW, &Regular, TRUE)) - { - wcscat((WCHAR*) Cp, L" "); - wcscat((WCHAR*) Cp, StyleNameW.Buffer); - Cp += FamilyNameW.Length + StyleNameW.Length + (sizeof(WCHAR) << 1); - } - else - { - Cp += FamilyNameW.Length + sizeof(WCHAR); - } + + /* unique name (full name) */ Otm->otmpFullName = (LPSTR)(Cp - (char*) Otm); - wcscpy((WCHAR*) Cp, FamilyNameW.Buffer); - wcscat((WCHAR*) Cp, L" "); - wcscat((WCHAR*) Cp, StyleNameW.Buffer); + wcscpy((WCHAR*) Cp, FullNameW.Buffer); + Cp += FullNameW.Length + sizeof(WCHAR); + + ASSERT(Cp - (char*)Otm == Cache->OutlineRequiredSize); - RtlFreeUnicodeString(&StyleNameW); RtlFreeUnicodeString(&FamilyNameW); + RtlFreeUnicodeString(&FaceNameW); + RtlFreeUnicodeString(&StyleNameW); + RtlFreeUnicodeString(&FullNameW); - return Needed; + return Cache->OutlineRequiredSize; } static PFONTGDI FASTCALL @@ -939,31 +1989,35 @@ FindFaceNameInList(PUNICODE_STRING FaceName, PLIST_ENTRY Head) ANSI_STRING EntryFaceNameA; UNICODE_STRING EntryFaceNameW; FONTGDI *FontGDI; + NTSTATUS status; - Entry = Head->Flink; - while (Entry != Head) + for (Entry = Head->Flink; Entry != Head; Entry = Entry->Flink) { - CurrentEntry = (PFONT_ENTRY) CONTAINING_RECORD(Entry, FONT_ENTRY, ListEntry); + CurrentEntry = CONTAINING_RECORD(Entry, FONT_ENTRY, ListEntry); FontGDI = CurrentEntry->Font; ASSERT(FontGDI); - RtlInitAnsiString(&EntryFaceNameA, FontGDI->face->family_name); - RtlAnsiStringToUnicodeString(&EntryFaceNameW, &EntryFaceNameA, TRUE); + RtlInitAnsiString(&EntryFaceNameA, FontGDI->SharedFace->Face->family_name); + status = RtlAnsiStringToUnicodeString(&EntryFaceNameW, &EntryFaceNameA, TRUE); + if (!NT_SUCCESS(status)) + { + break; + } + if ((LF_FACESIZE - 1) * sizeof(WCHAR) < EntryFaceNameW.Length) { EntryFaceNameW.Length = (LF_FACESIZE - 1) * sizeof(WCHAR); EntryFaceNameW.Buffer[LF_FACESIZE - 1] = L'\0'; } - if (0 == RtlCompareUnicodeString(FaceName, &EntryFaceNameW, TRUE)) + if (RtlEqualUnicodeString(FaceName, &EntryFaceNameW, TRUE)) { RtlFreeUnicodeString(&EntryFaceNameW); return FontGDI; } RtlFreeUnicodeString(&EntryFaceNameW); - Entry = Entry->Flink; } return NULL; @@ -975,7 +2029,8 @@ FindFaceNameInLists(PUNICODE_STRING FaceName) PPROCESSINFO Win32Process; PFONTGDI Font; - /* Search the process local list */ + /* Search the process local list. + We do not have to search the 'Mem' list, since those fonts are linked in the PrivateFontListHead */ Win32Process = PsGetCurrentProcessWin32Process(); IntLockProcessPrivateFonts(Win32Process); Font = FindFaceNameInList(FaceName, &Win32Process->PrivateFontListHead); @@ -986,15 +2041,258 @@ FindFaceNameInLists(PUNICODE_STRING FaceName) } /* Search the global list */ - IntLockGlobalFonts; - Font = FindFaceNameInList(FaceName, &FontListHead); - IntUnLockGlobalFonts; + IntLockGlobalFonts(); + Font = FindFaceNameInList(FaceName, &g_FontListHead); + IntUnLockGlobalFonts(); return Font; } +/* 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 +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 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, PCWSTR FaceName, PFONTGDI FontGDI) +FontFamilyFillInfo(PFONTFAMILYINFO Info, LPCWSTR FaceName, + LPCWSTR FullName, PFONTGDI FontGDI) { ANSI_STRING StyleA; UNICODE_STRING StyleW; @@ -1007,7 +2305,12 @@ FontFamilyFillInfo(PFONTFAMILYINFO Info, PCWSTR FaceName, PFONTGDI FontGDI) 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); @@ -1015,7 +2318,12 @@ FontFamilyFillInfo(PFONTFAMILYINFO Info, PCWSTR FaceName, PFONTGDI FontGDI) { return; } - IntGetOutlineTextMetrics(FontGDI, Size, Otm); + Size = IntGetOutlineTextMetrics(FontGDI, Size, Otm); + if (!Size) + { + ExFreePoolWithTag(Otm, GDITAG_TEXT); + return; + } Lf = &Info->EnumLogFontEx.elfLogFont; TM = &Otm->otmTextMetrics; @@ -1058,7 +2366,7 @@ FontFamilyFillInfo(PFONTFAMILYINFO Info, PCWSTR FaceName, PFONTGDI FontGDI) if (0 == Ntm->ntmFlags) Ntm->ntmFlags = NTM_REGULAR; Ntm->ntmSizeEM = Otm->otmEMSquare; - Ntm->ntmCellHeight = 0; + Ntm->ntmCellHeight = Otm->otmEMSquare; Ntm->ntmAvgWidth = 0; Info->FontType = (0 != (TM->tmPitchAndFamily & TMPF_TRUETYPE) @@ -1067,83 +2375,101 @@ FontFamilyFillInfo(PFONTFAMILYINFO Info, PCWSTR FaceName, PFONTGDI FontGDI) if (0 == (TM->tmPitchAndFamily & TMPF_VECTOR)) Info->FontType |= RASTER_FONTTYPE; - ExFreePoolWithTag(Otm, GDITAG_TEXT); - RtlStringCbCopyW(Info->EnumLogFontEx.elfLogFont.lfFaceName, - sizeof(Info->EnumLogFontEx.elfLogFont.lfFaceName), - FaceName); + /* 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), - FaceName); - RtlInitAnsiString(&StyleA, FontGDI->face->style_name); + FullName); + + ExFreePoolWithTag(Otm, GDITAG_TEXT); + + RtlInitAnsiString(&StyleA, Face->style_name); StyleW.Buffer = Info->EnumLogFontEx.elfStyle; StyleW.MaximumLength = sizeof(Info->EnumLogFontEx.elfStyle); - RtlAnsiStringToUnicodeString(&StyleW, &StyleA, FALSE); + status = RtlAnsiStringToUnicodeString(&StyleW, &StyleA, FALSE); + if (!NT_SUCCESS(status)) + { + return; + } + Info->EnumLogFontEx.elfScript[0] = UNICODE_NULL; - Info->EnumLogFontEx.elfLogFont.lfCharSet = DEFAULT_CHARSET; - Info->EnumLogFontEx.elfScript[0] = L'\0'; - IntLockFreeType; - pOS2 = FT_Get_Sfnt_Table(FontGDI->face, ft_sfnt_os2); - IntUnLockFreeType; - if (NULL != pOS2) + IntLockFreeType(); + pOS2 = FT_Get_Sfnt_Table(Face, ft_sfnt_os2); + + if (!pOS2) { - fs.fsCsb[0] = pOS2->ulCodePageRange1; - fs.fsCsb[1] = pOS2->ulCodePageRange2; - fs.fsUsb[0] = pOS2->ulUnicodeRange1; - fs.fsUsb[1] = pOS2->ulUnicodeRange2; - fs.fsUsb[2] = pOS2->ulUnicodeRange3; - fs.fsUsb[3] = pOS2->ulUnicodeRange4; + IntUnLockFreeType(); + return; + } - if (0 == pOS2->version) - { - FT_UInt Dummy; + 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 (FT_Get_First_Char(FontGDI->face, &Dummy) < 0x100) + 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; - else + break; + case FT_ENCODING_MS_SYMBOL: fs.fsCsb[0] |= FS_SYMBOL; - } - if (fs.fsCsb[0] == 0) - { /* Let's see if we can find any interesting cmaps */ - for (i = 0; i < FontGDI->face->num_charmaps; i++) - { - switch (FontGDI->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; - } + break; + default: + break; } } - for (i = 0; i < MAXTCIINDEX; i++) + } + + for (i = 0; i < MAXTCIINDEX; i++) + { + fs0 = 1L << i; + if (fs.fsCsb[0] & fs0) { - fs0 = 1L << i; - if (fs.fsCsb[0] & fs0) + if (!IntTranslateCharsetInfo(&fs0, &CharSetInfo, TCI_SRCFONTSIG)) { - if (!IntTranslateCharsetInfo(&fs0, &CharSetInfo, TCI_SRCFONTSIG)) - { - CharSetInfo.ciCharset = DEFAULT_CHARSET; - } - if (DEFAULT_CHARSET != CharSetInfo.ciCharset) + CharSetInfo.ciCharset = DEFAULT_CHARSET; + } + if (DEFAULT_CHARSET != CharSetInfo.ciCharset) + { + if (g_ElfScripts[i]) + wcscpy(Info->EnumLogFontEx.elfScript, g_ElfScripts[i]); + else { - Info->EnumLogFontEx.elfLogFont.lfCharSet = CharSetInfo.ciCharset; - if (NULL != ElfScripts[i]) - wcscpy(Info->EnumLogFontEx.elfScript, ElfScripts[i]); - else - { - DPRINT1("Unknown elfscript for bit %u\n", i); - } + DPRINT1("Unknown elfscript for bit %u\n", i); } } } - Info->NewTextMetricEx.ntmFontSig = fs; } + Info->NewTextMetricEx.ntmFontSig = fs; } static int FASTCALL @@ -1155,7 +2481,7 @@ FindFaceNameInInfo(PUNICODE_STRING FaceName, PFONTFAMILYINFO Info, DWORD InfoEnt for (i = 0; i < InfoEntries; i++) { RtlInitUnicodeString(&InfoFaceName, Info[i].EnumLogFontEx.elfLogFont.lfFaceName); - if (0 == RtlCompareUnicodeString(&InfoFaceName, FaceName, TRUE)) + if (RtlEqualUnicodeString(&InfoFaceName, FaceName, TRUE)) { return i; } @@ -1171,8 +2497,8 @@ FontFamilyInclude(LPLOGFONTW LogFont, PUNICODE_STRING FaceName, UNICODE_STRING LogFontFaceName; RtlInitUnicodeString(&LogFontFaceName, LogFont->lfFaceName); - if (0 != LogFontFaceName.Length - && 0 != RtlCompareUnicodeString(&LogFontFaceName, FaceName, TRUE)) + if (0 != LogFontFaceName.Length && + !RtlEqualUnicodeString(&LogFontFaceName, FaceName, TRUE)) { return FALSE; } @@ -1180,157 +2506,130 @@ FontFamilyInclude(LPLOGFONTW LogFont, PUNICODE_STRING FaceName, return FindFaceNameInInfo(FaceName, Info, InfoEntries) < 0; } +static BOOL FASTCALL +FontFamilyFound(PFONTFAMILYINFO InfoEntry, + PFONTFAMILYINFO Info, DWORD InfoCount) +{ + LPLOGFONTW plf1 = &InfoEntry->EnumLogFontEx.elfLogFont; + LPWSTR pFullName1 = InfoEntry->EnumLogFontEx.elfFullName; + LPWSTR pFullName2; + DWORD i; + + for (i = 0; i < InfoCount; ++i) + { + LPLOGFONTW plf2 = &Info[i].EnumLogFontEx.elfLogFont; + if (plf1->lfCharSet != plf2->lfCharSet) + continue; + + pFullName2 = Info[i].EnumLogFontEx.elfFullName; + if (_wcsicmp(pFullName1, pFullName2) != 0) + continue; + + return TRUE; + } + return FALSE; +} + static BOOLEAN FASTCALL GetFontFamilyInfoForList(LPLOGFONTW LogFont, PFONTFAMILYINFO Info, - DWORD *Count, - DWORD Size, + DWORD *pCount, + DWORD MaxCount, PLIST_ENTRY Head) { PLIST_ENTRY Entry; PFONT_ENTRY CurrentEntry; - ANSI_STRING EntryFaceNameA; - UNICODE_STRING EntryFaceNameW; FONTGDI *FontGDI; + FONTFAMILYINFO InfoEntry; + DWORD Count = *pCount; - Entry = Head->Flink; - while (Entry != Head) + for (Entry = Head->Flink; Entry != Head; Entry = Entry->Flink) { - CurrentEntry = (PFONT_ENTRY) CONTAINING_RECORD(Entry, FONT_ENTRY, ListEntry); - + CurrentEntry = CONTAINING_RECORD(Entry, FONT_ENTRY, ListEntry); FontGDI = CurrentEntry->Font; ASSERT(FontGDI); - RtlInitAnsiString(&EntryFaceNameA, FontGDI->face->family_name); - RtlAnsiStringToUnicodeString(&EntryFaceNameW, &EntryFaceNameA, TRUE); - if ((LF_FACESIZE - 1) * sizeof(WCHAR) < EntryFaceNameW.Length) + if (LogFont->lfCharSet != DEFAULT_CHARSET && + LogFont->lfCharSet != FontGDI->CharSet) { - EntryFaceNameW.Length = (LF_FACESIZE - 1) * sizeof(WCHAR); - EntryFaceNameW.Buffer[LF_FACESIZE - 1] = L'\0'; + continue; } - if (FontFamilyInclude(LogFont, &EntryFaceNameW, Info, min(*Count, Size))) + if (LogFont->lfFaceName[0] == UNICODE_NULL) { - if (*Count < Size) + if (Count < MaxCount) { - FontFamilyFillInfo(Info + *Count, EntryFaceNameW.Buffer, FontGDI); + FontFamilyFillInfo(&Info[Count], NULL, NULL, FontGDI); } - (*Count)++; + Count++; + continue; } - RtlFreeUnicodeString(&EntryFaceNameW); - Entry = Entry->Flink; - } - - return TRUE; -} - -typedef struct FontFamilyInfoCallbackContext -{ - LPLOGFONTW LogFont; - PFONTFAMILYINFO Info; - DWORD Count; - DWORD Size; -} FONT_FAMILY_INFO_CALLBACK_CONTEXT, *PFONT_FAMILY_INFO_CALLBACK_CONTEXT; - -static NTSTATUS APIENTRY -FontFamilyInfoQueryRegistryCallback(IN PWSTR ValueName, IN ULONG ValueType, - IN PVOID ValueData, IN ULONG ValueLength, - IN PVOID Context, IN PVOID EntryContext) -{ - PFONT_FAMILY_INFO_CALLBACK_CONTEXT InfoContext; - UNICODE_STRING RegistryName, RegistryValue; - int Existing; - PFONTGDI FontGDI; - - if (REG_SZ != ValueType) - { - return STATUS_SUCCESS; - } - InfoContext = (PFONT_FAMILY_INFO_CALLBACK_CONTEXT) Context; - RtlInitUnicodeString(&RegistryName, ValueName); - /* Do we need to include this font family? */ - if (FontFamilyInclude(InfoContext->LogFont, &RegistryName, InfoContext->Info, - min(InfoContext->Count, InfoContext->Size))) - { - RtlInitUnicodeString(&RegistryValue, (PCWSTR) ValueData); - Existing = FindFaceNameInInfo(&RegistryValue, InfoContext->Info, - min(InfoContext->Count, InfoContext->Size)); - if (0 <= Existing) - { - /* We already have the information about the "real" font. Just copy it */ - if (InfoContext->Count < InfoContext->Size) - { - InfoContext->Info[InfoContext->Count] = InfoContext->Info[Existing]; - RtlStringCbCopyNW(InfoContext->Info[InfoContext->Count].EnumLogFontEx.elfLogFont.lfFaceName, - sizeof(InfoContext->Info[InfoContext->Count].EnumLogFontEx.elfLogFont.lfFaceName), - RegistryName.Buffer, - RegistryName.Length); - } - InfoContext->Count++; - return STATUS_SUCCESS; - } + FontFamilyFillInfo(&InfoEntry, NULL, NULL, FontGDI); - /* Try to find information about the "real" font */ - FontGDI = FindFaceNameInLists(&RegistryValue); - if (NULL == FontGDI) + 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) { - /* "Real" font not found, discard this registry entry */ - return STATUS_SUCCESS; + continue; } - /* Return info about the "real" font but with the name of the alias */ - if (InfoContext->Count < InfoContext->Size) + if (!FontFamilyFound(&InfoEntry, Info, min(Count, MaxCount))) { - FontFamilyFillInfo(InfoContext->Info + InfoContext->Count, - RegistryName.Buffer, FontGDI); + if (Count < MaxCount) + { + RtlCopyMemory(&Info[Count], &InfoEntry, sizeof(InfoEntry)); + } + Count++; } - InfoContext->Count++; - return STATUS_SUCCESS; } - return STATUS_SUCCESS; + *pCount = Count; + + return TRUE; } static BOOLEAN FASTCALL GetFontFamilyInfoForSubstitutes(LPLOGFONTW LogFont, PFONTFAMILYINFO Info, - DWORD *Count, - DWORD Size) + DWORD *pCount, + DWORD MaxCount) { - RTL_QUERY_REGISTRY_TABLE QueryTable[2] = {{0}}; - FONT_FAMILY_INFO_CALLBACK_CONTEXT Context; - NTSTATUS Status; + PLIST_ENTRY pEntry, pHead = &g_FontSubstListHead; + PFONTSUBST_ENTRY pCurrentEntry; + PUNICODE_STRING pFromW; + FONTGDI *FontGDI; + LOGFONTW lf = *LogFont; + UNICODE_STRING NameW; - /* Enumerate font families found in HKLM\Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes - The real work is done in the registry callback function */ - Context.LogFont = LogFont; - Context.Info = Info; - Context.Count = *Count; - Context.Size = Size; - - QueryTable[0].QueryRoutine = FontFamilyInfoQueryRegistryCallback; - QueryTable[0].Flags = 0; - QueryTable[0].Name = NULL; - QueryTable[0].EntryContext = NULL; - QueryTable[0].DefaultType = REG_NONE; - QueryTable[0].DefaultData = NULL; - QueryTable[0].DefaultLength = 0; - - QueryTable[1].QueryRoutine = NULL; - QueryTable[1].Name = NULL; - - Status = RtlQueryRegistryValues(RTL_REGISTRY_WINDOWS_NT, - L"FontSubstitutes", - QueryTable, - &Context, - NULL); - if (NT_SUCCESS(Status)) + for (pEntry = pHead->Flink; pEntry != pHead; pEntry = pEntry->Flink) { - *Count = Context.Count; + pCurrentEntry = CONTAINING_RECORD(pEntry, FONTSUBST_ENTRY, ListEntry); + + pFromW = &pCurrentEntry->FontNames[FONTSUBST_FROM]; + if (LogFont->lfFaceName[0] != UNICODE_NULL) + { + if (!FontFamilyInclude(LogFont, pFromW, Info, min(*pCount, MaxCount))) + continue; /* mismatch */ + } + + RtlStringCchCopyW(lf.lfFaceName, LF_FACESIZE, pFromW->Buffer); + SubstituteFontRecurse(&lf); + + RtlInitUnicodeString(&NameW, lf.lfFaceName); + FontGDI = FindFaceNameInLists(&NameW); + if (FontGDI == NULL) + { + continue; /* no real font */ + } + + if (*pCount < MaxCount) + { + FontFamilyFillInfo(&Info[*pCount], pFromW->Buffer, NULL, FontGDI); + } + (*pCount)++; } - return NT_SUCCESS(Status) || STATUS_OBJECT_NAME_NOT_FOUND == Status; + return TRUE; } BOOL @@ -1365,33 +2664,79 @@ ftGdiGlyphCacheGet( FT_Face Face, INT GlyphIndex, INT Height, + FT_Render_Mode RenderMode, PMATRIX pmx) { PLIST_ENTRY CurrentEntry; PFONT_CACHE_ENTRY FontEntry; - CurrentEntry = FontCacheListHead.Flink; - while (CurrentEntry != &FontCacheListHead) + ASSERT_FREETYPE_LOCK_HELD(); + + for (CurrentEntry = g_FontCacheListHead.Flink; + CurrentEntry != &g_FontCacheListHead; + CurrentEntry = CurrentEntry->Flink) { - FontEntry = (PFONT_CACHE_ENTRY)CurrentEntry; + 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; - CurrentEntry = CurrentEntry->Flink; } - if (CurrentEntry == &FontCacheListHead) + if (CurrentEntry == &g_FontCacheListHead) { return NULL; } RemoveEntryList(CurrentEntry); - InsertHeadList(&FontCacheListHead, 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, @@ -1407,6 +2752,8 @@ ftGdiGlyphCacheSet( FT_Bitmap AlignedBitmap; FT_BitmapGlyph BitmapGlyph; + ASSERT_FREETYPE_LOCK_HELD(); + error = FT_Get_Glyph(GlyphSlot, &GlyphCopy); if (error) { @@ -1414,87 +2761,501 @@ ftGdiGlyphCacheSet( return NULL; }; - error = FT_Glyph_To_Bitmap(&GlyphCopy, RenderMode, 0, 1); - if (error) + 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 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 float. It puts the integer part + in the highest 16 bits and the decimal part in the lowest 16 bits of the FT_Fixed. + It fails if the integer part of the float number is greater than SHORT_MAX. +*/ +static __inline FT_Fixed FT_FixedFromFloat(float f) +{ + short value = f; + unsigned short fract = (f - value) * 0xFFFF; + return (FT_Fixed)((long)value << 16 | (unsigned long)fract); +} + +/* + 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); +} + +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) { - FT_Done_Glyph(GlyphCopy); - DPRINT1("Failure rendering glyph.\n"); - return NULL; - }; + error = FT_Get_WinFNT_Header(face, &WinFNT); + if (error) + 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; + + req.type = FT_SIZE_REQUEST_TYPE_NOMINAL; + req.width = 0; + req.height = (FT_Long)(FontGDI->EmHeight << 6); + req.horiResolution = 0; + req.vertResolution = 0; + return FT_Request_Size(face, &req); + } + + if (lfHeight > 0) + { + /* case (A): lfHeight is positive */ + Sum = pOS2->usWinAscent + pOS2->usWinDescent; + if (Sum == 0) + { + Ascent = pHori->Ascender; + Descent = -pHori->Descender; + Sum = Ascent + Descent; + } + else + { + Ascent = pOS2->usWinAscent; + Descent = pOS2->usWinDescent; + } - NewEntry = ExAllocatePoolWithTag(PagedPool, sizeof(FONT_CACHE_ENTRY), TAG_FONT); - if (!NewEntry) + 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) { - DPRINT1("Alloc failure caching glyph.\n"); - FT_Done_Glyph(GlyphCopy); - return NULL; + /* case (B): lfHeight is negative */ + FontGDI->tmAscent = FT_MulDiv(-lfHeight, pOS2->usWinAscent, face->units_per_EM); + FontGDI->tmDescent = FT_MulDiv(-lfHeight, pOS2->usWinDescent, face->units_per_EM); + FontGDI->tmHeight = FontGDI->tmAscent + FontGDI->tmDescent; + FontGDI->tmInternalLeading = FontGDI->tmHeight + lfHeight; } - BitmapGlyph = (FT_BitmapGlyph)GlyphCopy; - FT_Bitmap_New(&AlignedBitmap); - if(FT_Bitmap_Convert(GlyphSlot->library, &BitmapGlyph->bitmap, &AlignedBitmap, 4)) + FontGDI->EmHeight = FontGDI->tmHeight - FontGDI->tmInternalLeading; + FontGDI->EmHeight = max(FontGDI->EmHeight, 1); + FontGDI->EmHeight = min(FontGDI->EmHeight, USHORT_MAX); + FontGDI->Magic = FONTGDI_MAGIC; + + if (lfHeight > 0) + EmHeight64 = (FontGDI->EmHeight << 6) + 31; + else + 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) { - DPRINT1("Conversion failed\n"); - ExFreePoolWithTag(NewEntry, TAG_FONT); - FT_Done_Glyph((FT_Glyph)BitmapGlyph); - return 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->encoding == FT_ENCODING_MS_SYMBOL) + { + found = charmap; + break; + } + } + } + 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"); + } + } } - FT_Bitmap_Done(GlyphSlot->library, &BitmapGlyph->bitmap); - BitmapGlyph->bitmap = AlignedBitmap; + plf = &TextObj->logfont.elfEnumLogfontEx.elfLogFont; - NewEntry->GlyphIndex = GlyphIndex; - NewEntry->Face = Face; - NewEntry->BitmapGlyph = BitmapGlyph; - NewEntry->Height = Height; - NewEntry->mxWorldToDevice = *pmx; + error = IntRequestFontSize(dc, FontGDI, plf->lfWidth, plf->lfHeight); - InsertHeadList(&FontCacheListHead, &NewEntry->ListEntry); - if (FontCacheNumEntries++ > MAX_FONT_CACHE) + if (bDoLock) + IntUnLockFreeType(); + + if (error) { - NewEntry = (PFONT_CACHE_ENTRY)FontCacheListHead.Blink; - FT_Done_Glyph((FT_Glyph)NewEntry->BitmapGlyph); - RemoveTailList(&FontCacheListHead); - ExFreePoolWithTag(NewEntry, TAG_FONT); - FontCacheNumEntries--; + DPRINT1("Error in setting pixel sizes: %d\n", error); + return FALSE; } - return BitmapGlyph; + return TRUE; } - -static -void -FTVectorToPOINTFX(FT_Vector *vec, POINTFX *pt) +static inline FT_UInt FASTCALL +get_glyph_index_symbol(FT_Face ft_face, UINT glyph) { - pt->x.value = vec->x >> 6; - pt->x.fract = (vec->x & 0x3f) << 10; - pt->x.fract |= ((pt->x.fract >> 6) | (pt->x.fract >> 12)); - pt->y.value = vec->y >> 6; - pt->y.fract = (vec->y & 0x3f) << 10; - pt->y.fract |= ((pt->y.fract >> 6) | (pt->y.fract >> 12)); - return; + 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; } -/* - This function builds an FT_Fixed from a float. It puts the integer part - in the highest 16 bits and the decimal part in the lowest 16 bits of the FT_Fixed. - It fails if the integer part of the float number is greater than SHORT_MAX. -*/ -static __inline FT_Fixed FT_FixedFromFloat(float f) +static inline FT_UInt FASTCALL +get_glyph_index(FT_Face ft_face, UINT glyph) { - short value = f; - unsigned short fract = (f - value) * 0xFFFF; - return (FT_Fixed)((long)value << 16 | (unsigned long)fract); + 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); } -/* - 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) +static inline FT_UInt FASTCALL +get_glyph_index_flagged(FT_Face face, FT_ULong code, DWORD indexed_flag, DWORD flags) { - return (FT_Fixed)((long)f.value << 16 | (unsigned long)f.fract); + FT_UInt glyph_index; + if (flags & indexed_flag) + { + glyph_index = code; + } + else + { + glyph_index = get_glyph_index(face, code); + } + return glyph_index; } /* @@ -1535,9 +3296,8 @@ ftGdiGetGlyphOutline( LONG aveWidth; INT adv, lsb, bbx; /* These three hold to widths of the unrotated chars */ OUTLINETEXTMETRICW *potm; - int n = 0; - FT_CharMap found = 0, charmap; XFORM xForm; + LOGFONTW *plf; DPRINT("%u, %08x, %p, %08lx, %p, %p\n", wch, iFormat, pgm, cjBuf, pvBuf, pmat2); @@ -1556,10 +3316,11 @@ ftGdiGetGlyphOutline( return GDI_ERROR; } FontGDI = ObjToGDI(TextObj->Font, FONT); - ft_face = FontGDI->face; + ft_face = FontGDI->SharedFace->Face; - aveWidth = FT_IS_SCALABLE(ft_face) ? TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfWidth: 0; - orientation = FT_IS_SCALABLE(ft_face) ? TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfOrientation: 0; + 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); @@ -1569,52 +3330,23 @@ ftGdiGetGlyphOutline( TEXTOBJ_UnlockText(TextObj); return GDI_ERROR; } - IntGetOutlineTextMetrics(FontGDI, Size, potm); - - IntLockFreeType; - - /* During testing, I never saw this used. It is here just in case. */ - if (ft_face->charmap == NULL) + Size = IntGetOutlineTextMetrics(FontGDI, Size, potm); + if (!Size) { - DPRINT("WARNING: No charmap selected!\n"); - DPRINT("This font face has %d charmaps\n", ft_face->num_charmaps); - - for (n = 0; n < ft_face->num_charmaps; n++) - { - charmap = ft_face->charmaps[n]; - DPRINT("Found charmap encoding: %u\n", charmap->encoding); - if (charmap->encoding != 0) - { - found = charmap; - break; - } - } - if (!found) - { - DPRINT1("WARNING: Could not find desired charmap!\n"); - } - error = FT_Set_Charmap(ft_face, found); - if (error) - { - DPRINT1("WARNING: Could not set the charmap!\n"); - } + /* FIXME: last error? */ + ExFreePoolWithTag(potm, GDITAG_TEXT); + TEXTOBJ_UnlockText(TextObj); + return GDI_ERROR; } -// FT_Set_Pixel_Sizes(ft_face, -// TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfWidth, - /* FIXME: Should set character height if neg */ -// (TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight == 0 ? -// dc->ppdev->devinfo.lfDefaultFont.lfHeight : abs(TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight))); -// FtSetCoordinateTransform(face, DC_pmxWorldToDevice(dc)); + IntLockFreeType(); + TextIntUpdateSize(dc, TextObj, FontGDI, FALSE); + FtSetCoordinateTransform(ft_face, DC_pmxWorldToDevice(dc)); TEXTOBJ_UnlockText(TextObj); - if (iFormat & GGO_GLYPH_INDEX) - { - glyph_index = wch; - iFormat &= ~GGO_GLYPH_INDEX; - } - else glyph_index = FT_Get_Char_Index(ft_face, wch); + 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; @@ -1629,11 +3361,11 @@ ftGdiGetGlyphOutline( if (error) { DPRINT1("WARNING: Failed to load and render glyph! [index: %u]\n", glyph_index); - IntUnLockFreeType; + IntUnLockFreeType(); if (potm) ExFreePoolWithTag(potm, GDITAG_TEXT); return GDI_ERROR; } - IntUnLockFreeType; + IntUnLockFreeType(); if (aveWidth && potm) { @@ -1651,15 +3383,25 @@ ftGdiGetGlyphOutline( DPRINT("Advance = %d, lsb = %d, bbx = %d\n",adv, lsb, bbx); - IntLockFreeType; + IntLockFreeType(); - /* Scaling transform */ - /*if (aveWidth)*/ + /* Width scaling transform */ + if (widthRatio != 1.0) { + FT_Matrix scaleMat; + scaleMat.xx = FT_FixedFromFloat(widthRatio); + scaleMat.xy = 0; + scaleMat.yx = 0; + scaleMat.yy = FT_FixedFromFloat(1.0); + + FT_Matrix_Multiply(&scaleMat, &transMat); + needsTransform = TRUE; + } + /* World transform */ + { FT_Matrix ftmatrix; FLOATOBJ efTemp; - PMATRIX pmx = DC_pmxWorldToDevice(dc); /* Create a freetype matrix, by converting to 16.16 fixpoint format */ @@ -1679,21 +3421,11 @@ ftGdiGetGlyphOutline( FLOATOBJ_MulLong(&efTemp, 0x00010000); ftmatrix.yy = FLOATOBJ_GetLong(&efTemp); - FT_Matrix_Multiply(&ftmatrix, &transMat); - needsTransform = TRUE; - } - - /* Slant transform */ - if (potm->otmTextMetrics.tmItalic) - { - FT_Matrix slantMat; - DPRINT("Slant Trans!\n"); - slantMat.xx = (1 << 16); - slantMat.xy = ((1 << 16) >> 2); - slantMat.yx = 0; - slantMat.yy = (1 << 16); - FT_Matrix_Multiply(&slantMat, &transMat); - needsTransform = TRUE; + if (memcmp(&ftmatrix, &identityMat, sizeof(identityMat)) != 0) + { + FT_Matrix_Multiply(&ftmatrix, &transMat); + needsTransform = TRUE; + } } /* Rotation transform */ @@ -1786,13 +3518,13 @@ ftGdiGetGlyphOutline( gm.gmBlackBoxX, gm.gmBlackBoxY, gm.gmptGlyphOrigin.x, gm.gmptGlyphOrigin.y); - IntUnLockFreeType; + IntUnLockFreeType(); - if (pgm) RtlCopyMemory(pgm, &gm, sizeof(GLYPHMETRICS)); if (iFormat == GGO_METRICS) { DPRINT("GGO_METRICS Exit!\n"); + *pgm = gm; return 1; /* FIXME */ } @@ -1811,14 +3543,17 @@ ftGdiGetGlyphOutline( 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 = (ft_face->glyph->bitmap.width + 7) >> 3; - INT h = ft_face->glyph->bitmap.rows; + 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); @@ -1832,10 +3567,10 @@ ftGdiGetGlyphOutline( ft_bitmap.width = width; ft_bitmap.rows = height; ft_bitmap.pitch = pitch; - ft_bitmap.pixel_mode = ft_pixel_mode_mono; + ft_bitmap.pixel_mode = FT_PIXEL_MODE_MONO; ft_bitmap.buffer = pvBuf; - IntLockFreeType; + IntLockFreeType(); if (needsTransform) { FT_Outline_Transform(&ft_face->glyph->outline, &transMat); @@ -1843,8 +3578,8 @@ ftGdiGetGlyphOutline( 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(library, &ft_face->glyph->outline, &ft_bitmap); - IntUnLockFreeType; + FT_Outline_Get_Bitmap(g_FreeTypeLibrary, &ft_face->glyph->outline, &ft_bitmap); + IntUnLockFreeType(); break; default: @@ -1866,17 +3601,20 @@ ftGdiGetGlyphOutline( 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 = ft_face->glyph->bitmap.rows; + INT h = min( height, ft_face->glyph->bitmap.rows ); INT x; while (h--) { - for (x = 0; x < pitch; x++) + 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; @@ -1886,25 +3624,25 @@ ftGdiGetGlyphOutline( src += ft_face->glyph->bitmap.pitch; dst += pitch; } - return needed; + 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_grays; + ft_bitmap.pixel_mode = FT_PIXEL_MODE_GRAY; ft_bitmap.buffer = pvBuf; - IntLockFreeType; + 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(library, &ft_face->glyph->outline, &ft_bitmap); - IntUnLockFreeType; + FT_Outline_Get_Bitmap(g_FreeTypeLibrary, &ft_face->glyph->outline, &ft_bitmap); + IntUnLockFreeType(); if (iFormat == GGO_GRAY2_BITMAP) mult = 4; @@ -1916,225 +3654,70 @@ ftGdiGetGlyphOutline( { 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; } - 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; } case GGO_NATIVE: { - int contour, point = 0, first_pt; FT_Outline *outline = &ft_face->glyph->outline; - TTPOLYGONHEADER *pph; - TTPOLYCURVE *ppc; - DWORD pph_start, cpfx, type; if (cjBuf == 0) pvBuf = NULL; /* This is okay, need cjBuf to allocate. */ - IntLockFreeType; + IntLockFreeType(); if (needsTransform && pvBuf) FT_Outline_Transform(outline, &transMat); - for (contour = 0; contour < outline->n_contours; contour++) + needed = get_native_glyph_outline(outline, cjBuf, NULL); + + if (!pvBuf || !cjBuf) { - pph_start = needed; - pph = (TTPOLYGONHEADER *)((char *)pvBuf + needed); - first_pt = point; - if (pvBuf) - { - pph->dwType = TT_POLYGON_TYPE; - FTVectorToPOINTFX(&outline->points[point], &pph->pfxStart); - } - needed += sizeof(*pph); - point++; - while (point <= outline->contours[contour]) - { - ppc = (TTPOLYCURVE *)((char *)pvBuf + needed); - type = (outline->tags[point] & FT_Curve_Tag_On) ? - TT_PRIM_LINE : TT_PRIM_QSPLINE; - cpfx = 0; - do - { - if (pvBuf) - 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 (pvBuf) - 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 (pvBuf) - FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]); - cpfx++; - point++; - } - if (pvBuf) - { - ppc->wType = type; - ppc->cpfx = cpfx; - } - needed += sizeof(*ppc) + (cpfx - 1) * sizeof(POINTFX); - } - if (pvBuf) pph->cb = needed - pph_start; + IntUnLockFreeType(); + break; } - IntUnLockFreeType; + if (needed > cjBuf) + { + IntUnLockFreeType(); + return GDI_ERROR; + } + get_native_glyph_outline(outline, cjBuf, pvBuf); + IntUnLockFreeType(); break; } case GGO_BEZIER: { - /* Convert the quadratic Beziers to cubic Beziers. - The parametric eqn for a cubic Bezier is, from PLRM: - r(t) = at^3 + bt^2 + ct + r0 - with the control points: - r1 = r0 + c/3 - r2 = r1 + (c + b)/3 - r3 = r0 + c + b + a - - A quadratic Beizer has the form: - p(t) = (1-t)^2 p0 + 2(1-t)t p1 + t^2 p2 - - So equating powers of t leads to: - r1 = 2/3 p1 + 1/3 p0 - r2 = 2/3 p1 + 1/3 p2 - and of course r0 = p0, r3 = p2 - */ - - int contour, point = 0, first_pt; FT_Outline *outline = &ft_face->glyph->outline; - TTPOLYGONHEADER *pph; - TTPOLYCURVE *ppc; - DWORD pph_start, cpfx, type; - FT_Vector cubic_control[4]; if (cjBuf == 0) pvBuf = NULL; if (needsTransform && pvBuf) { - IntLockFreeType; + IntLockFreeType(); FT_Outline_Transform(outline, &transMat); - IntUnLockFreeType; + IntUnLockFreeType(); } + needed = get_bezier_glyph_outline(outline, cjBuf, NULL); - for (contour = 0; contour < outline->n_contours; contour++) - { - pph_start = needed; - pph = (TTPOLYGONHEADER *)((char *)pvBuf + needed); - first_pt = point; - if (pvBuf) - { - pph->dwType = TT_POLYGON_TYPE; - FTVectorToPOINTFX(&outline->points[point], &pph->pfxStart); - } - needed += sizeof(*pph); - point++; - while (point <= outline->contours[contour]) - { - ppc = (TTPOLYCURVE *)((char *)pvBuf + needed); - type = (outline->tags[point] & FT_Curve_Tag_On) ? - TT_PRIM_LINE : TT_PRIM_CSPLINE; - cpfx = 0; - do - { - if (type == TT_PRIM_LINE) - { - if (pvBuf) - FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]); - cpfx++; - point++; - } - else - { - /* Unlike QSPLINEs, CSPLINEs always have their endpoint - so cpfx = 3n */ + if (!pvBuf || !cjBuf) + break; + if (needed > cjBuf) + return GDI_ERROR; - /* 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 (pvBuf) - { - 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 (pvBuf) - { - ppc->wType = type; - ppc->cpfx = cpfx; - } - needed += sizeof(*ppc) + (cpfx - 1) * sizeof(POINTFX); - } - if (pvBuf) pph->cb = needed - pph_start; - } + get_bezier_glyph_outline(outline, cjBuf, pvBuf); break; } @@ -2144,6 +3727,7 @@ ftGdiGetGlyphOutline( } DPRINT("ftGdiGetGlyphOutline END and needed %lu\n", needed); + *pgm = gm; return needed; } @@ -2163,67 +3747,38 @@ TextIntGetTextExtentPoint(PDC dc, FT_Face face; FT_GlyphSlot glyph; FT_BitmapGlyph realglyph; - INT error, n, glyph_index, i, previous; + INT error, glyph_index, i, previous; ULONGLONG TotalWidth = 0; - FT_CharMap charmap, found = NULL; 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->face; + face = FontGDI->SharedFace->Face; if (NULL != Fit) { *Fit = 0; } - IntLockFreeType; - if (face->charmap == NULL) - { - DPRINT("WARNING: No charmap selected!\n"); - DPRINT("This font face has %d charmaps\n", face->num_charmaps); - - for (n = 0; n < face->num_charmaps; n++) - { - charmap = face->charmaps[n]; - DPRINT("Found charmap encoding: %u\n", charmap->encoding); - if (charmap->encoding != 0) - { - found = charmap; - break; - } - } + IntLockFreeType(); - if (! found) - { - DPRINT1("WARNING: Could not find desired charmap!\n"); - } + TextIntUpdateSize(dc, TextObj, FontGDI, FALSE); - error = FT_Set_Charmap(face, found); - if (error) - { - DPRINT1("WARNING: Could not set the charmap!\n"); - } - } + plf = &TextObj->logfont.elfEnumLogfontEx.elfLogFont; + EmuBold = (plf->lfWeight >= FW_BOLD && FontGDI->OriginalWeight <= FW_NORMAL); + EmuItalic = (plf->lfItalic && !FontGDI->OriginalItalic); Render = IntIsFontRenderingEnabled(); if (Render) - RenderMode = IntGetFontRenderMode(&TextObj->logfont.elfEnumLogfontEx.elfLogFont); + RenderMode = IntGetFontRenderMode(plf); else RenderMode = FT_RENDER_MODE_MONO; - error = FT_Set_Pixel_Sizes(face, - TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfWidth, - /* FIXME: Should set character height if neg */ - (TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight == 0 ? - dc->ppdev->devinfo.lfDefaultFont.lfHeight : abs(TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight))); - if (error) - { - DPRINT1("Error in setting pixel sizes: %d\n", error); - } - /* Get the DC's world-to-device transformation matrix */ pmxWorldToDevice = DC_pmxWorldToDevice(dc); FtSetCoordinateTransform(face, pmxWorldToDevice); @@ -2233,14 +3788,15 @@ TextIntGetTextExtentPoint(PDC dc, for (i = 0; i < Count; i++) { - if (fl & GTEF_INDICES) - glyph_index = *String; + glyph_index = get_glyph_index_flagged(face, *String, GTEF_INDICES, fl); + + if (EmuBold || EmuItalic) + realglyph = NULL; else - glyph_index = FT_Get_Char_Index(face, *String); + realglyph = ftGdiGlyphCacheGet(face, glyph_index, plf->lfHeight, + RenderMode, pmxWorldToDevice); - if (!(realglyph = ftGdiGlyphCacheGet(face, glyph_index, - TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight, - pmxWorldToDevice))) + if (EmuBold || EmuItalic || !realglyph) { error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT); if (error) @@ -2249,13 +3805,25 @@ TextIntGetTextExtentPoint(PDC dc, break; } - glyph = face->glyph; - realglyph = ftGdiGlyphCacheSet(face, - glyph_index, - TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight, - pmxWorldToDevice, - glyph, - RenderMode); + 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); @@ -2282,15 +3850,22 @@ TextIntGetTextExtentPoint(PDC dc, Dx[i] = (TotalWidth + 32) >> 6; } + if (EmuBold || EmuItalic) + { + FT_Done_Glyph((FT_Glyph)realglyph); + realglyph = NULL; + } + previous = glyph_index; String++; } - IntUnLockFreeType; + ASSERT(FontGDI->Magic == FONTGDI_MAGIC); + ascender = FontGDI->tmAscent; /* Units above baseline */ + descender = FontGDI->tmDescent; /* Units below baseline */ + IntUnLockFreeType(); Size->cx = (TotalWidth + 32) >> 6; - Size->cy = (TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight == 0 ? - dc->ppdev->devinfo.lfDefaultFont.lfHeight : abs(TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight)); - Size->cy = EngMulDiv(Size->cy, dc->ppdev->gdiinfo.ulLogPixelsY, 72); + Size->cy = ascender + descender; return TRUE; } @@ -2304,7 +3879,8 @@ ftGdiGetTextCharsetInfo( DWORD dwFlags) { PDC_ATTR pdcattr; - UINT Ret = DEFAULT_CHARSET, i; + UINT Ret = DEFAULT_CHARSET; + INT i; HFONT hFont; PTEXTOBJ TextObj; PFONTGDI FontGdi; @@ -2325,12 +3901,12 @@ ftGdiGetTextCharsetInfo( return Ret; } FontGdi = ObjToGDI(TextObj->Font, FONT); - Face = FontGdi->face; + Face = FontGdi->SharedFace->Face; TEXTOBJ_UnlockText(TextObj); - IntLockFreeType; + IntLockFreeType(); pOS2 = FT_Get_Sfnt_Table(Face, ft_sfnt_os2); - IntUnLockFreeType; + IntUnLockFreeType(); memset(&fs, 0, sizeof(FONTSIGNATURE)); if (NULL != pOS2) { @@ -2413,7 +3989,7 @@ ftGetFontUnicodeRanges(PFONTGDI Font, PGLYPHSET glyphset) { DWORD size = 0; DWORD num_ranges = 0; - FT_Face face = Font->face; + FT_Face face = Font->SharedFace->Face; if (face->charmap->encoding == FT_ENCODING_UNICODE) { @@ -2462,13 +4038,14 @@ ftGetFontUnicodeRanges(PFONTGDI Font, PGLYPHSET glyphset) } } else - DPRINT1("Encoding %u not supported\n", face->charmap->encoding); + 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; } @@ -2490,6 +4067,7 @@ ftGdiGetTextMetricsW( FT_WinFNT_HeaderRec Win; ULONG Error; NTSTATUS Status = STATUS_SUCCESS; + LOGFONTW *plf; if (!ptmwi) { @@ -2506,17 +4084,16 @@ ftGdiGetTextMetricsW( TextObj = RealizeFontInit(pdcattr->hlfntNew); if (NULL != TextObj) { + plf = &TextObj->logfont.elfEnumLogfontEx.elfLogFont; FontGDI = ObjToGDI(TextObj->Font, FONT); - Face = FontGDI->face; - IntLockFreeType; - Error = FT_Set_Pixel_Sizes(Face, - TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfWidth, - /* FIXME: Should set character height if neg */ - (TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight == 0 ? - dc->ppdev->devinfo.lfDefaultFont.lfHeight : abs(TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight))); + Face = FontGDI->SharedFace->Face; + + IntLockFreeType(); + Error = IntRequestFontSize(dc, FontGDI, plf->lfWidth, plf->lfHeight); FtSetCoordinateTransform(Face, DC_pmxWorldToDevice(dc)); - IntUnLockFreeType; + IntUnLockFreeType(); + if (0 != Error) { DPRINT1("Error in setting pixel sizes: %u\n", Error); @@ -2524,26 +4101,27 @@ ftGdiGetTextMetricsW( } else { + FT_Face Face = FontGDI->SharedFace->Face; Status = STATUS_SUCCESS; - IntLockFreeType; - pOS2 = FT_Get_Sfnt_Table(FontGDI->face, ft_sfnt_os2); + 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(FontGDI->face, ft_sfnt_hhea); + 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(FontGDI->face , &Win); + Error = FT_Get_WinFNT_Header(Face, &Win); - IntUnLockFreeType; + IntUnLockFreeType(); if (NT_SUCCESS(Status)) { @@ -2569,7 +4147,6 @@ ftGdiGetTextMetricsW( return TRUE; } - DWORD FASTCALL ftGdiGetFontData( @@ -2580,10 +4157,11 @@ ftGdiGetFontData( DWORD Size) { DWORD Result = GDI_ERROR; + FT_Face Face = FontGdi->SharedFace->Face; - IntLockFreeType; + IntLockFreeType(); - if (FT_IS_SFNT(FontGdi->face)) + if (FT_IS_SFNT(Face)) { if (Table) Table = Table >> 24 | Table << 24 | (Table >> 8 & 0xFF00) | @@ -2596,156 +4174,465 @@ ftGdiGetFontData( FT_Error Error; FT_ULong Needed = 0; - Error = FT_Load_Sfnt_Table(FontGdi->face, Table, Offset, NULL, &Needed); + Error = FT_Load_Sfnt_Table(Face, Table, Offset, NULL, &Needed); if ( !Error && Needed < Size) Size = Needed; } - if (!FT_Load_Sfnt_Table(FontGdi->face, Table, Offset, Buffer, &Size)) + if (!FT_Load_Sfnt_Table(Face, Table, Offset, Buffer, &Size)) Result = Size; } - IntUnLockFreeType; + IntUnLockFreeType(); return Result; } -static UINT FASTCALL -GetFontScore(LOGFONTW *LogFont, PUNICODE_STRING FaceName, PFONTGDI FontGDI) +// 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) { - ANSI_STRING EntryFaceNameA; - UNICODE_STRING EntryFaceNameW; - unsigned Size; - OUTLINETEXTMETRICW *Otm; - LONG WeightDiff; - NTSTATUS Status; - UINT Score = 1; + ULONG Penalty = 0; + BYTE Byte; + LONG Long; + BOOL fNeedScaling = FALSE; + const BYTE UserCharSet = CharSetFromLangID(gusLanguageID); + const TEXTMETRICW * TM = &Otm->otmTextMetrics; + WCHAR* ActualNameW; - RtlInitAnsiString(&EntryFaceNameA, FontGDI->face->family_name); - Status = RtlAnsiStringToUnicodeString(&EntryFaceNameW, &EntryFaceNameA, TRUE); - if (NT_SUCCESS(Status)) + ASSERT(Otm); + ASSERT(LogFont); + + /* FIXME: IntSizeSynth Penalty 20 */ + /* FIXME: SmallPenalty Penalty 1 */ + /* FIXME: FaceNameSubst Penalty 500 */ + + Byte = LogFont->lfCharSet; + if (Byte == DEFAULT_CHARSET) { - if ((LF_FACESIZE - 1) * sizeof(WCHAR) < EntryFaceNameW.Length) + if (_wcsicmp(LogFont->lfFaceName, L"Marlett") == 0) { - EntryFaceNameW.Length = (LF_FACESIZE - 1) * sizeof(WCHAR); - EntryFaceNameW.Buffer[LF_FACESIZE - 1] = L'\0'; + if (Byte == ANSI_CHARSET) + { + DPRINT("Warning: FIXME: It's Marlett but ANSI_CHARSET.\n"); + } + /* We assume SYMBOL_CHARSET for "Marlett" font */ + Byte = SYMBOL_CHARSET; } - if (0 == RtlCompareUnicodeString(FaceName, &EntryFaceNameW, TRUE)) + } + + if (Byte != TM->tmCharSet) + { + if (Byte != DEFAULT_CHARSET && Byte != ANSI_CHARSET) { - Score += 49; + /* CharSet Penalty 65000 */ + /* Requested charset does not match the candidate's. */ + Penalty += 65000; + } + else + { + if (UserCharSet != TM->tmCharSet) + { + /* UNDOCUMENTED */ + Penalty += 100; + if (ANSI_CHARSET != TM->tmCharSet) + { + /* UNDOCUMENTED */ + Penalty += 100; + } + } } - RtlFreeUnicodeString(&EntryFaceNameW); } - Size = IntGetOutlineTextMetrics(FontGDI, 0, NULL); - Otm = ExAllocatePoolWithTag(PagedPool, Size, GDITAG_TEXT); - if (NULL == Otm) + Byte = LogFont->lfOutPrecision; + if (Byte == OUT_DEFAULT_PRECIS) + Byte = OUT_OUTLINE_PRECIS; /* Is it OK? */ + switch (Byte) { - return Score; + 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. */ + Penalty += 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. */ + Penalty += 19000; + } + break; } - IntGetOutlineTextMetrics(FontGDI, Size, Otm); - if ((0 != LogFont->lfItalic && 0 != Otm->otmTextMetrics.tmItalic) || - (0 == LogFont->lfItalic && 0 == Otm->otmTextMetrics.tmItalic)) + Byte = (LogFont->lfPitchAndFamily & 0x0F); + if (Byte == DEFAULT_PITCH) + Byte = VARIABLE_PITCH; + if ((Byte & FIXED_PITCH) || (Byte & MONO_FONT)) { - Score += 25; + if (TM->tmPitchAndFamily & _TMPF_VARIABLE_PITCH) + { + /* FixedPitch Penalty 15000 */ + /* Requested a fixed pitch font, but the candidate is a + variable pitch font. */ + Penalty += 15000; + } } - if (LogFont->lfWeight != FW_DONTCARE) + if (Byte & VARIABLE_PITCH) { - if (LogFont->lfWeight < Otm->otmTextMetrics.tmWeight) + if (!(TM->tmPitchAndFamily & _TMPF_VARIABLE_PITCH)) { - WeightDiff = Otm->otmTextMetrics.tmWeight - LogFont->lfWeight; + /* PitchVariable Penalty 350 */ + /* Requested a variable pitch font, but the candidate is not a + variable pitch font. */ + Penalty += 350; } - else + } + + Byte = (LogFont->lfPitchAndFamily & 0x0F); + if (Byte == DEFAULT_PITCH) + { + if (!(TM->tmPitchAndFamily & _TMPF_VARIABLE_PITCH)) { - WeightDiff = LogFont->lfWeight - Otm->otmTextMetrics.tmWeight; + /* DefaultPitchFixed Penalty 1 */ + /* Requested DEFAULT_PITCH, but the candidate is fixed pitch. */ + Penalty += 1; } - Score += (1000 - WeightDiff) / (1000 / 25); } - else + + ActualNameW = (WCHAR*)((ULONG_PTR)Otm + (ULONG_PTR)Otm->otmpFamilyName); + + if (LogFont->lfFaceName[0]) { - Score += 25; + 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. */ + Penalty += 10000; + } } - ExFreePoolWithTag(Otm, GDITAG_TEXT); + Byte = (LogFont->lfPitchAndFamily & 0xF0); + if (Byte != FF_DONTCARE) + { + if (Byte & FF_MODERN) + { + if (TM->tmPitchAndFamily & _TMPF_VARIABLE_PITCH) + { + /* FixedPitch Penalty 15000 */ + /* Requested a fixed pitch font, but the candidate is a + variable pitch font. */ + Penalty += 15000; + } + } + + if ((Byte & FF_ROMAN) || (Byte & FF_SWISS)) + { + if (!(TM->tmPitchAndFamily & _TMPF_VARIABLE_PITCH)) + { + /* PitchVariable Penalty 350 */ + /* Requested a variable pitch font, but the candidate is not a + variable pitch font. */ + Penalty += 350; + } + } + +#define FF_MASK (FF_DECORATIVE | FF_SCRIPT | FF_SWISS) + if ((Byte & FF_MASK) != (TM->tmPitchAndFamily & FF_MASK)) + { + /* Family Penalty 9000 */ + /* Requested a family, but the candidate's family is different. */ + Penalty += 9000; + } +#undef FF_MASK + + if ((TM->tmPitchAndFamily & 0xF0) == FF_DONTCARE) + { + /* FamilyUnknown Penalty 8000 */ + /* Requested a family, but the candidate has no family. */ + Penalty += 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. */ + Penalty += 600; + /* HeightBiggerDifference Penalty 150 */ + /* The candidate is a raster font and is larger than the + requested height. Penalty * height difference */ + Penalty += 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 */ + Penalty += 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. */ + Penalty += 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. */ + Penalty += 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 */ + Penalty += 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. */ + Penalty += 50; + } + + if (!!LogFont->lfItalic != !!TM->tmItalic) + { + if (!LogFont->lfItalic && ItalicFromStyle(style_name)) + { + /* 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). */ + Penalty += 40; + } + else if (LogFont->lfItalic && !ItalicFromStyle(style_name)) + { + /* ItalicSim Penalty 1 */ + /* Requested italic font but the candidate is not italic, + although italics can be simulated. */ + Penalty += 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. */ + Penalty += 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) */ + Penalty += 3 * (labs(Long - TM->tmWeight) / 10); + } + + if (!LogFont->lfUnderline && TM->tmUnderlined) + { + /* Underline Penalty 3 */ + /* Requested font has no underline, but the candidate is + underlined. */ + Penalty += 3; + } + + if (!LogFont->lfStrikeOut && TM->tmStruckOut) + { + /* StrikeOut Penalty 3 */ + /* Requested font has no strike-out, but the candidate is + struck out. */ + Penalty += 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 */ + Penalty += 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 */ + Penalty += 1 * labs(TM->tmHeight - LogFont->lfHeight); + } + } + + if (!(TM->tmPitchAndFamily & TMPF_DEVICE)) + { + /* DeviceFavor Penalty 2 */ + /* Extra penalty for all nondevice fonts. */ + Penalty += 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. */ + Penalty += ((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. */ + Penalty += ((TM->tmHeight / TM->tmAveCharWidth) - 2) * 30; + } + } - return Score; + 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 */ } static __inline VOID -FindBestFontFromList(FONTOBJ **FontObj, UINT *MatchScore, LOGFONTW *LogFont, - PUNICODE_STRING FaceName, PLIST_ENTRY Head) +FindBestFontFromList(FONTOBJ **FontObj, ULONG *MatchPenalty, + const LOGFONTW *LogFont, + const PLIST_ENTRY Head) { + ULONG Penalty; PLIST_ENTRY Entry; PFONT_ENTRY CurrentEntry; FONTGDI *FontGDI; - UINT Score; -ASSERT(FontObj && MatchScore && LogFont && FaceName && Head); - Entry = Head->Flink; - while (Entry != Head) + 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 = (PFONT_ENTRY) CONTAINING_RECORD(Entry, FONT_ENTRY, ListEntry); + CurrentEntry = CONTAINING_RECORD(Entry, FONT_ENTRY, ListEntry); FontGDI = CurrentEntry->Font; ASSERT(FontGDI); + Face = FontGDI->SharedFace->Face; - Score = GetFontScore(LogFont, FaceName, FontGDI); - if (*MatchScore == 0 || *MatchScore < Score) + /* get text metrics */ + OtmSize = IntGetOutlineTextMetrics(FontGDI, 0, NULL); + if (OtmSize > OldOtmSize) { - *FontObj = GDIToObj(FontGDI, FONT); - *MatchScore = Score; + if (Otm) + ExFreePoolWithTag(Otm, GDITAG_TEXT); + Otm = ExAllocatePoolWithTag(PagedPool, OtmSize, GDITAG_TEXT); } - Entry = Entry->Flink; - } -} -static __inline BOOLEAN -SubstituteFontFamilyKey(PUNICODE_STRING FaceName, - LPCWSTR Key) -{ - RTL_QUERY_REGISTRY_TABLE QueryTable[2] = {{0}}; - NTSTATUS Status; - UNICODE_STRING Value; - - RtlInitUnicodeString(&Value, NULL); - - QueryTable[0].QueryRoutine = NULL; - QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_NOEXPAND | - RTL_QUERY_REGISTRY_REQUIRED; - QueryTable[0].Name = FaceName->Buffer; - QueryTable[0].EntryContext = &Value; - QueryTable[0].DefaultType = REG_NONE; - QueryTable[0].DefaultData = NULL; - QueryTable[0].DefaultLength = 0; - - QueryTable[1].QueryRoutine = NULL; - QueryTable[1].Name = NULL; - - Status = RtlQueryRegistryValues(RTL_REGISTRY_WINDOWS_NT, - Key, - QueryTable, - NULL, - NULL); - if (NT_SUCCESS(Status)) - { - RtlFreeUnicodeString(FaceName); - *FaceName = Value; - } + /* update FontObj if lowest penalty */ + if (Otm) + { + IntLockFreeType(); + IntRequestFontSize(NULL, FontGDI, LogFont->lfWidth, LogFont->lfHeight); + IntUnLockFreeType(); - return NT_SUCCESS(Status); -} + OtmSize = IntGetOutlineTextMetrics(FontGDI, OtmSize, Otm); + if (!OtmSize) + continue; -static __inline void -SubstituteFontFamily(PUNICODE_STRING FaceName, UINT Level) -{ - if (10 < Level) /* Enough is enough */ - { - return; - } + OldOtmSize = OtmSize; - if (SubstituteFontFamilyKey(FaceName, L"FontSubstitutes")) - { - SubstituteFontFamily(FaceName, Level + 1); + 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 @@ -2755,39 +4642,89 @@ IntFontType(PFONTGDI Font) { PS_FontInfoRec psfInfo; FT_ULong tmp_size = 0; + FT_Face Face = Font->SharedFace->Face; - if (FT_HAS_MULTIPLE_MASTERS(Font->face)) + if (FT_HAS_MULTIPLE_MASTERS(Face)) Font->FontObj.flFontType |= FO_MULTIPLEMASTER; - if (FT_HAS_VERTICAL( Font->face )) + if (FT_HAS_VERTICAL(Face)) Font->FontObj.flFontType |= FO_VERT_FACE; - if (FT_IS_SCALABLE( Font->face )) + if (!FT_IS_SCALABLE(Face)) Font->FontObj.flFontType |= FO_TYPE_RASTER; - if (FT_IS_SFNT(Font->face)) + if (FT_IS_SFNT(Face)) { Font->FontObj.flFontType |= FO_TYPE_TRUETYPE; - if (FT_Get_Sfnt_Table(Font->face, ft_sfnt_post)) + if (FT_Get_Sfnt_Table(Face, ft_sfnt_post)) Font->FontObj.flFontType |= FO_POSTSCRIPT; } - if (!FT_Get_PS_Font_Info(Font->face, &psfInfo )) + 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(Font->face, FT_MAKE_TAG('C','F','F',' '), 0, NULL, &tmp_size)) + if (!FT_Load_Sfnt_Table(Face, TTAG_CFF, 0, NULL, &tmp_size)) { Font->FontObj.flFontType |= (FO_CFF|FO_POSTSCRIPT); } } +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; - UNICODE_STRING FaceName; PPROCESSINFO Win32Process; - UINT MatchScore; + ULONG MatchPenalty; + LOGFONTW *pLogFont; + LOGFONTW SubstitutedLogFont; + FT_Face Face; if (!pTextObj) { @@ -2804,52 +4741,94 @@ TextIntRealizeFont(HFONT FontHandle, PTEXTOBJ pTextObj) } } else - TextObj = pTextObj; - - if (! RtlCreateUnicodeString(&FaceName, TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfFaceName)) { - if (!pTextObj) TEXTOBJ_UnlockText(TextObj); - return STATUS_NO_MEMORY; + TextObj = pTextObj; } - SubstituteFontFamily(&FaceName, 0); - MatchScore = 0; + + 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; - /* First search private fonts */ Win32Process = PsGetCurrentProcessWin32Process(); + + /* Search private fonts */ IntLockProcessPrivateFonts(Win32Process); - FindBestFontFromList(&TextObj->Font, &MatchScore, - &TextObj->logfont.elfEnumLogfontEx.elfLogFont, &FaceName, + FindBestFontFromList(&TextObj->Font, &MatchPenalty, &SubstitutedLogFont, &Win32Process->PrivateFontListHead); IntUnLockProcessPrivateFonts(Win32Process); /* Search system fonts */ - IntLockGlobalFonts; - FindBestFontFromList(&TextObj->Font, &MatchScore, - &TextObj->logfont.elfEnumLogfontEx.elfLogFont, &FaceName, - &FontListHead); - IntUnLockGlobalFonts; + IntLockGlobalFonts(); + FindBestFontFromList(&TextObj->Font, &MatchPenalty, &SubstitutedLogFont, + &g_FontListHead); + IntUnLockGlobalFonts(); + if (NULL == TextObj->Font) { - DPRINT1("Requested font %S not found, no fonts loaded at all\n", - TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfFaceName); + 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 */ + Name.Length = (USHORT)min(Name.Length, (LF_FACESIZE - 1) * sizeof(WCHAR)); + RtlStringCbCopyNW(TextObj->TextFace, Name.Length + sizeof(WCHAR), Name.Buffer, Name.Length); + + 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->Underline = TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfUnderline ? 0xff : 0; - FontGdi->StrikeOut = TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfStrikeOut ? 0xff : 0; + 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; + + Face = FontGdi->SharedFace->Face; + + //FontGdi->OriginalWeight = WeightFromStyle(Face->style_name); + + if (!FontGdi->OriginalItalic) + FontGdi->OriginalItalic = ItalicFromStyle(Face->style_name); + TextObj->fl |= TEXTOBJECT_INIT; Status = STATUS_SUCCESS; } - RtlFreeUnicodeString(&FaceName); if (!pTextObj) TEXTOBJ_UnlockText(TextObj); ASSERT((NT_SUCCESS(Status) ^ (NULL == TextObj->Font)) != 0); @@ -2874,7 +4853,7 @@ IntGetFullFileName( InitializeObjectAttributes(&ObjectAttributes, FileName, - OBJ_CASE_INSENSITIVE, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); @@ -2903,6 +4882,39 @@ IntGetFullFileName( 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( @@ -2915,9 +4927,14 @@ IntGdiGetFontResourceInfo( POBJECT_NAME_INFORMATION NameInfo1, NameInfo2; PLIST_ENTRY ListEntry; PFONT_ENTRY FontEntry; - FONTFAMILYINFO Info; - ULONG Size; - BOOL bFound = FALSE; + ULONG Size, i, Count; + LPBYTE pbBuffer; + BOOL IsEqual; + FONTFAMILYINFO *FamInfo; + const ULONG MaxFamInfo = 64; + BOOL bSuccess; + + DPRINT("IntGdiGetFontResourceInfo: dwType == %lu\n", dwType); /* Create buffer for full path name */ Size = sizeof(OBJECT_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR); @@ -2944,77 +4961,231 @@ IntGdiGetFontResourceInfo( return FALSE; } + FamInfo = ExAllocatePoolWithTag(PagedPool, + sizeof(FONTFAMILYINFO) * MaxFamInfo, + TAG_FINF); + if (!FamInfo) + { + ExFreePoolWithTag(NameInfo2, TAG_FINF); + ExFreePoolWithTag(NameInfo1, TAG_FINF); + EngSetLastError(ERROR_NOT_ENOUGH_MEMORY); + return FALSE; + } /* Try to find the pathname in the global font list */ - IntLockGlobalFonts; - for (ListEntry = FontListHead.Flink; - ListEntry != &FontListHead; - ListEntry = ListEntry->Flink) + Count = 0; + IntLockGlobalFonts(); + for (ListEntry = g_FontListHead.Flink; ListEntry != &g_FontListHead; + ListEntry = ListEntry->Flink) { FontEntry = CONTAINING_RECORD(ListEntry, FONT_ENTRY, ListEntry); - if (FontEntry->Font->Filename != NULL) + if (FontEntry->Font->Filename == NULL) + continue; + + RtlInitUnicodeString(&EntryFileName , FontEntry->Font->Filename); + if (!IntGetFullFileName(NameInfo2, 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) { - RtlInitUnicodeString(&EntryFileName , FontEntry->Font->Filename); - if (IntGetFullFileName(NameInfo2, Size, &EntryFileName)) + if (EqualFamilyInfo(&FamInfo[i], &FamInfo[Count])) { - if (RtlEqualUnicodeString(&NameInfo1->Name, &NameInfo2->Name, FALSE)) - { - /* Found */ - FontFamilyFillInfo(&Info, FontEntry->FaceName.Buffer, FontEntry->Font); - bFound = TRUE; - break; - } + IsEqual = TRUE; + break; } } + if (!IsEqual) + { + /* Found */ + ++Count; + if (Count >= MaxFamInfo) + break; + } } - IntUnLockGlobalFonts; + IntUnLockGlobalFonts(); /* Free the buffers */ ExFreePoolWithTag(NameInfo1, TAG_FINF); ExFreePool(NameInfo2); - if (!bFound && dwType != 5) + 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 */ - *(DWORD*)pBuffer = 1; - *pdwBytes = sizeof(DWORD); + 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 full font name */ - Size = wcslen(Info.EnumLogFontEx.elfFullName) + 1; - Size = min(Size , LF_FULLFACESIZE) * sizeof(WCHAR); - RtlCopyMemory(pBuffer, Info.EnumLogFontEx.elfFullName, Size); - // FIXME: Do we have to zeroterminate? - *pdwBytes = Size; + case 1: /* copy the font title */ + /* calculate the required size */ + Size = 0; + Size += wcslen(FamInfo[0].EnumLogFontEx.elfLogFont.lfFaceName); + if (FamInfo[0].EnumLogFontEx.elfStyle[0] && + _wcsicmp(FamInfo[0].EnumLogFontEx.elfStyle, L"Regular") != 0) + { + Size += 1 + wcslen(FamInfo[0].EnumLogFontEx.elfStyle); + } + for (i = 1; i < Count; ++i) + { + 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; + IntAddNameFromFamInfo(psz, &FamInfo[0]); + for (i = 1; i < Count; ++i) + { + 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 a LOGFONTW structure */ - Info.EnumLogFontEx.elfLogFont.lfWidth = 0; - RtlCopyMemory(pBuffer, &Info.EnumLogFontEx.elfLogFont, sizeof(LOGFONTW)); - *pdwBytes = sizeof(LOGFONTW); + 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: /* FIXME: What exactly is copied here? */ - *(DWORD*)pBuffer = 1; - *pdwBytes = sizeof(DWORD*); + 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 5: /* Looks like a BOOL that is copied, TRUE, if the font was not found */ - *(BOOL*)pBuffer = !bFound; - *pdwBytes = sizeof(BOOL); + 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; - default: - return FALSE; + 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 TRUE; + return bSuccess; } @@ -3022,11 +5193,11 @@ BOOL FASTCALL ftGdiRealizationInfo(PFONTGDI Font, PREALIZATION_INFO Info) { - if (FT_HAS_FIXED_SIZES(Font->face)) + if (FT_HAS_FIXED_SIZES(Font->SharedFace->Face)) Info->iTechnology = RI_TECH_BITMAP; else { - if (FT_IS_SCALABLE(Font->face)) + if (FT_IS_SCALABLE(Font->SharedFace->Face)) Info->iTechnology = RI_TECH_SCALABLE; else Info->iTechnology = RI_TECH_FIXED; @@ -3045,7 +5216,7 @@ ftGdiGetKerningPairs( PFONTGDI Font, { DWORD Count = 0; INT i = 0; - FT_Face face = Font->face; + FT_Face face = Font->SharedFace->Face; if (FT_HAS_KERNING(face) && face->charmap->encoding == FT_ENCODING_UNICODE) { @@ -3055,7 +5226,7 @@ ftGdiGetKerningPairs( PFONTGDI Font, char_previous = char_code = FT_Get_First_Char(face, &glyph_index); - IntLockFreeType; + IntLockFreeType(); while (glyph_index) { @@ -3077,7 +5248,7 @@ ftGdiGetKerningPairs( PFONTGDI Font, char_previous = char_code; char_code = FT_Get_Next_Char(face, char_code, &glyph_index); } - IntUnLockFreeType; + IntUnLockFreeType(); } return Count; } @@ -3117,15 +5288,15 @@ NtGdiGetFontFamilyInfo(HDC Dc, } /* Enumerate font families in the global list */ - IntLockGlobalFonts; + IntLockGlobalFonts(); Count = 0; - if (! GetFontFamilyInfoForList(&LogFont, Info, &Count, Size, &FontListHead) ) + if (! GetFontFamilyInfoForList(&LogFont, Info, &Count, Size, &g_FontListHead) ) { - IntUnLockGlobalFonts; + IntUnLockGlobalFonts(); ExFreePoolWithTag(Info, GDITAG_TEXT); return -1; } - IntUnLockGlobalFonts; + IntUnLockGlobalFonts(); /* Enumerate font families in the process local list */ Win32Process = PsGetCurrentProcessWin32Process(); @@ -3190,7 +5361,7 @@ GreExtTextOutW( IN INT YStart, IN UINT fuOptions, IN OPTIONAL PRECTL lprc, - IN LPWSTR String, + IN LPCWSTR String, IN INT Count, IN OPTIONAL LPINT Dx, IN DWORD dwCodePage) @@ -3205,19 +5376,18 @@ GreExtTextOutW( PDC_ATTR pdcattr; SURFOBJ *SurfObj; SURFACE *psurf = NULL; - int error, glyph_index, n, i; + 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, DummyRect = {0, 0, 0, 0}; + RECTL DestRect, MaskRect; POINTL SourcePoint, BrushOrigin; HBITMAP HSourceGlyph; SURFOBJ *SourceGlyphSurf; SIZEL bitSize; - FT_CharMap found = 0, charmap; INT yoff; FONTOBJ *FontObj; PFONTGDI FontGDI; @@ -3231,6 +5401,21 @@ GreExtTextOutW( 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; + } + + /* NOTE: This function locks the screen DC, so it must never be called + with a DC already locked */ + Render = IntIsFontRenderingEnabled(); // TODO: Write test-cases to exactly match real Windows in different // bad parameters (e.g. does Windows check the DC or the RECT first?). @@ -3240,53 +5425,48 @@ GreExtTextOutW( EngSetLastError(ERROR_INVALID_HANDLE); return FALSE; } - if (dc->dctype == DC_TYPE_INFO) - { - DC_UnlockDc(dc); - /* Yes, Windows really returns TRUE in this case */ - return TRUE; - } - - pdcattr = dc->pdcattr; - - if ((fuOptions & ETO_OPAQUE) || pdcattr->jBkMode == OPAQUE) - { - if (pdcattr->ulDirty_ & DIRTY_BACKGROUND) - DC_vUpdateBackgroundBrush(dc); - } - - /* Check if String is valid */ - if ((Count > 0xFFFF) || (Count > 0 && String == NULL)) - { - EngSetLastError(ERROR_INVALID_PARAMETER); - goto fail; - } - - DxShift = fuOptions & ETO_PDY ? 1 : 0; if (PATH_IsPathOpen(dc->dclevel)) { - if (!PATH_ExtTextOut( dc, + bResult = PATH_ExtTextOut(dc, XStart, YStart, fuOptions, (const RECTL *)lprc, String, Count, - (const INT *)Dx)) goto fail; - goto good; + (const INT *)Dx); + DC_UnlockDc(dc); + 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); } - Start.x = XStart; - Start.y = YStart; - IntLPtoDP(dc, &Start, 1); + if (pdcattr->lTextAlign & TA_UPDATECP) + { + Start.x = pdcattr->ptlCurrent.x; + Start.y = pdcattr->ptlCurrent.y; + } else { + Start.x = XStart; + Start.y = YStart; + } - RealXStart = (Start.x + dc->ptlDCOrig.x) << 6; + IntLPtoDP(dc, &Start, 1); + RealXStart = ((LONGLONG)Start.x + dc->ptlDCOrig.x) << 6; YStart = Start.y + dc->ptlDCOrig.y; SourcePoint.x = 0; @@ -3308,16 +5488,22 @@ GreExtTextOutW( DestRect.right += dc->ptlDCOrig.x; DestRect.bottom += dc->ptlDCOrig.y; - DC_vPrepareDCsForBlit(dc, DestRect, NULL, DestRect); + 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( - &dc->dclevel.pSurface->SurfObj, + &psurf->SurfObj, NULL, NULL, - dc->rosdc.CombinedClip, + (CLIPOBJ *)&dc->co, NULL, &DestRect, &SourcePoint, @@ -3325,8 +5511,11 @@ GreExtTextOutW( &dc->eboBackground.BrushObject, &BrushOrigin, ROP4_FROM_INDEX(R3_OPINDEX_PATCOPY)); + + if (dc->dctype == DCTYPE_DIRECT) + MouseSafetyOnDrawEnd(dc->ppdev); + fuOptions &= ~ETO_OPAQUE; - DC_vFinishBlit(dc, NULL); } else { @@ -3339,7 +5528,8 @@ GreExtTextOutW( TextObj = RealizeFontInit(pdcattr->hlfntNew); if (TextObj == NULL) { - goto fail; + bResult = FALSE; + goto Cleanup; } FontObj = TextObj->Font; @@ -3347,69 +5537,54 @@ GreExtTextOutW( FontGDI = ObjToGDI(FontObj, FONT); ASSERT(FontGDI); - IntLockFreeType; - face = FontGDI->face; - if (face->charmap == NULL) - { - DPRINT("WARNING: No charmap selected!\n"); - DPRINT("This font face has %d charmaps\n", face->num_charmaps); + IntLockFreeType(); + face = FontGDI->SharedFace->Face; - for (n = 0; n < face->num_charmaps; n++) - { - charmap = face->charmaps[n]; - DPRINT("Found charmap encoding: %u\n", charmap->encoding); - if (charmap->encoding != 0) - { - found = charmap; - break; - } - } - if (!found) - { - DPRINT1("WARNING: Could not find desired charmap!\n"); - } - error = FT_Set_Charmap(face, found); - if (error) - { - DPRINT1("WARNING: Could not set the charmap!\n"); - } - } + plf = &TextObj->logfont.elfEnumLogfontEx.elfLogFont; + EmuBold = (plf->lfWeight >= FW_BOLD && FontGDI->OriginalWeight <= FW_NORMAL); + EmuItalic = (plf->lfItalic && !FontGDI->OriginalItalic); - Render = IntIsFontRenderingEnabled(); if (Render) - RenderMode = IntGetFontRenderMode(&TextObj->logfont.elfEnumLogfontEx.elfLogFont); + RenderMode = IntGetFontRenderMode(plf); else RenderMode = FT_RENDER_MODE_MONO; - error = FT_Set_Pixel_Sizes( - face, - TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfWidth, - /* FIXME: Should set character height if neg */ - (TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight == 0 ? - dc->ppdev->devinfo.lfDefaultFont.lfHeight : abs(TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight))); - if (error) + if (!TextIntUpdateSize(dc, TextObj, FontGDI, FALSE)) { - DPRINT1("Error in setting pixel sizes: %d\n", error); - IntUnLockFreeType; - goto fail; + IntUnLockFreeType(); + bResult = FALSE; + goto Cleanup; } - pmxWorldToDevice = DC_pmxWorldToDevice(dc); - FtSetCoordinateTransform(face, pmxWorldToDevice); + /* 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. */ - - fixAscender = ScaleLong(face->size->metrics.ascender, &pmxWorldToDevice->efM22); - fixDescender = ScaleLong(face->size->metrics.descender, &pmxWorldToDevice->efM22); - - if (pdcattr->lTextAlign & TA_BASELINE) +#define VALIGN_MASK (TA_TOP | TA_BASELINE | TA_BOTTOM) + if ((pdcattr->lTextAlign & VALIGN_MASK) == TA_BASELINE) yoff = 0; - else if (pdcattr->lTextAlign & TA_BOTTOM) - yoff = -fixDescender >> 6; + else if ((pdcattr->lTextAlign & VALIGN_MASK) == TA_BOTTOM) + yoff = -(fixDescender >> 6); else /* TA_TOP */ yoff = fixAscender >> 6; +#undef VALIGN_MASK use_kerning = FT_HAS_KERNING(face); previous = 0; @@ -3417,12 +5592,12 @@ GreExtTextOutW( /* * Process the horizontal alignment and modify XStart accordingly. */ - + DxShift = fuOptions & ETO_PDY ? 1 : 0; if (pdcattr->lTextAlign & (TA_RIGHT | TA_CENTER)) { ULONGLONG TextWidth = 0; LPCWSTR TempText = String; - int Start; + int iStart; /* * Calculate width of the text. @@ -3430,25 +5605,25 @@ GreExtTextOutW( if (NULL != Dx) { - Start = Count < 2 ? 0 : Count - 2; + iStart = Count < 2 ? 0 : Count - 2; TextWidth = Count < 2 ? 0 : (Dx[(Count-2)<logfont.elfEnumLogfontEx.elfLogFont.lfHeight, - pmxWorldToDevice))) + if (EmuBold || EmuItalic) + realglyph = NULL; + else + realglyph = ftGdiGlyphCacheGet(face, glyph_index, plf->lfHeight, + RenderMode, pmxWorldToDevice); + if (!realglyph) { error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT); if (error) @@ -3457,17 +5632,28 @@ GreExtTextOutW( } glyph = face->glyph; - realglyph = ftGdiGlyphCacheSet(face, - glyph_index, - TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight, - pmxWorldToDevice, - glyph, - RenderMode); + 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; - goto fail; + IntUnLockFreeType(); + goto Cleanup; } } @@ -3481,77 +5667,212 @@ GreExtTextOutW( 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->lTextAlign & 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); + + 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(); + 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(); + 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<lTextAlign & TA_CENTER) == TA_CENTER) - { - RealXStart -= TextWidth / 2; - } - else - { - RealXStart -= TextWidth; + if (EmuBold || EmuItalic) + { + FT_Done_Glyph((FT_Glyph)realglyph); + realglyph = NULL; + } } } - TextLeft = RealXStart; - TextTop = YStart; - BackgroundLeft = (RealXStart + 32) >> 6; - - /* Lock blit with a dummy rect */ - DC_vPrepareDCsForBlit(dc, DummyRect, NULL, DummyRect); - - psurf = dc->dclevel.pSurface ; - if(!psurf) psurf = psurfDefaultBitmap; - SurfObj = &psurf->SurfObj ; - EXLATEOBJ_vInitialize(&exloRGB2Dst, &gpalRGB, psurf->ppal, 0, 0, 0); EXLATEOBJ_vInitialize(&exloDst2RGB, psurf->ppal, &gpalRGB, 0, 0, 0); - if ((fuOptions & ETO_OPAQUE) && (dc->pdcattr->ulDirty_ & DIRTY_BACKGROUND)) - DC_vUpdateBackgroundBrush(dc) ; - - if(dc->pdcattr->ulDirty_ & DIRTY_TEXT) - DC_vUpdateTextBrush(dc) ; + /* Assume success */ + bResult = TRUE; /* * The main rendering loop. */ - for (i = 0; i < Count; i++) + TextLeft = RealXStart; + TextTop = YStart; + BackgroundLeft = (RealXStart + 32) >> 6; + for (i = 0; i < Count; ++i) { - if (fuOptions & ETO_GLYPH_INDEX) - glyph_index = *String; - else - glyph_index = FT_Get_Char_Index(face, *String); + glyph_index = get_glyph_index_flagged(face, String[i], ETO_GLYPH_INDEX, fuOptions); - if (!(realglyph = ftGdiGlyphCacheGet(face, glyph_index, - TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight, - pmxWorldToDevice))) + if (EmuBold || EmuItalic) + realglyph = NULL; + else + realglyph = ftGdiGlyphCacheGet(face, glyph_index, plf->lfHeight, + RenderMode, pmxWorldToDevice); + if (!realglyph) { 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; - DC_vFinishBlit(dc, NULL); - goto fail2; + bResult = FALSE; + break; } + glyph = face->glyph; - realglyph = ftGdiGlyphCacheSet(face, - glyph_index, - TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight, - pmxWorldToDevice, - glyph, - RenderMode); + 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; - DC_vFinishBlit(dc, NULL); - goto fail2; + bResult = FALSE; + break; } } @@ -3566,18 +5887,25 @@ GreExtTextOutW( DPRINT("TextTop: %lu\n", TextTop); DPRINT("Advance: %d\n", realglyph->root.advance.x); - if (fuOptions & ETO_OPAQUE) + 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 = TextTop + yoff + ((32 - fixDescender) >> 6); - MouseSafetyOnDrawStart(dc->ppdev, DestRect.left, DestRect.top, DestRect.right, DestRect.bottom); + 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, - dc->rosdc.CombinedClip, + (CLIPOBJ *)&dc->co, NULL, &DestRect, &SourcePoint, @@ -3585,9 +5913,11 @@ GreExtTextOutW( &dc->eboBackground.BrushObject, &BrushOrigin, ROP4_FROM_INDEX(R3_OPINDEX_PATCOPY)); - MouseSafetyOnDrawEnd(dc->ppdev); - BackgroundLeft = DestRect.right; + if (dc->dctype == DCTYPE_DIRECT) + MouseSafetyOnDrawEnd(dc->ppdev); + + BackgroundLeft = DestRect.right; } DestRect.left = ((TextLeft + 32) >> 6) + realglyph->left; @@ -3600,85 +5930,137 @@ GreExtTextOutW( MaskRect.right = realglyph->bitmap.width; MaskRect.bottom = realglyph->bitmap.rows; - /* - * 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: EngLockSurface() failed!\n"); - // FT_Done_Glyph(realglyph); - IntUnLockFreeType; - DC_vFinishBlit(dc, NULL); - goto fail2; - } - SourceGlyphSurf = EngLockSurface((HSURF)HSourceGlyph); - if ( !SourceGlyphSurf ) + /* 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); - DPRINT1("WARNING: EngLockSurface() failed!\n"); - IntUnLockFreeType; - DC_vFinishBlit(dc, NULL); - goto fail2; } - /* - * 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) + if (DoBreak) { - // 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; + break; } - if (lprc && (fuOptions & ETO_CLIPPED) && - DestRect.bottom >= lprc->bottom + dc->ptlDCOrig.y) + + if (plf->lfUnderline) { - DestRect.bottom = lprc->bottom + dc->ptlDCOrig.y; + 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)); + } } - MouseSafetyOnDrawStart(dc->ppdev, DestRect.left, DestRect.top, DestRect.right, DestRect.bottom); - IntEngMaskBlt( - SurfObj, - SourceGlyphSurf, - dc->rosdc.CombinedClip, - &exloRGB2Dst.xlo, - &exloDst2RGB.xlo, - &DestRect, - (PPOINTL)&MaskRect, - &dc->eboText.BrushObject, - &BrushOrigin); - MouseSafetyOnDrawEnd(dc->ppdev) ; - - EngUnlockSurface(SourceGlyphSurf); - EngDeleteSurface((HSURF)HSourceGlyph); - - if (DoBreak) + if (plf->lfStrikeOut) { - break; + 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); + 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)) + if (FLOATOBJ_Equal0(&Scale)) FLOATOBJ_Set1(&Scale); - - FLOATOBJ_MulLong(&Scale, Dx[i<lTextAlign & TA_UPDATECP) { + pdcattr->ptlCurrent.x = DestRect.right - dc->ptlDCOrig.x; + } - return TRUE; + IntUnLockFreeType(); -fail2: EXLATEOBJ_vCleanup(&exloRGB2Dst); EXLATEOBJ_vCleanup(&exloDst2RGB); -fail: + +Cleanup: + + DC_vFinishBlit(dc, NULL); + if (TextObj != NULL) TEXTOBJ_UnlockText(TextObj); DC_UnlockDc(dc); - return FALSE; + return bResult; } #define STACK_TEXT_BUFFER_SIZE 100 @@ -3735,7 +6119,7 @@ NtGdiExtTextOutW( RECTL SafeRect; BYTE LocalBuffer[STACK_TEXT_BUFFER_SIZE]; PVOID Buffer = LocalBuffer; - LPWSTR SafeString = NULL; + LPCWSTR SafeString = NULL; LPINT SafeDx = NULL; ULONG BufSize, StringSize, DxSize = 0; @@ -3772,7 +6156,7 @@ NtGdiExtTextOutW( _SEH2_TRY { /* Put the Dx before the String to assure alignment of 4 */ - SafeString = (LPWSTR)(((ULONG_PTR)Buffer) + DxSize); + SafeString = (LPCWSTR)(((ULONG_PTR)Buffer) + DxSize); /* Probe and copy the string */ ProbeForRead(UnsafeString, StringSize, 1); @@ -3848,7 +6232,7 @@ NtGdiGetCharABCWidthsW( IN HDC hDC, IN UINT FirstChar, IN ULONG Count, - IN OPTIONAL PWCHAR pwch, + IN OPTIONAL PWCHAR UnSafepwch, IN FLONG fl, OUT PVOID Buffer) { @@ -3864,14 +6248,30 @@ NtGdiGetCharABCWidthsW( HFONT hFont = 0; NTSTATUS Status = STATUS_SUCCESS; PMATRIX pmxWorldToDevice; + PWCHAR Safepwch = NULL; + LOGFONTW *plf; + + if (!Buffer) + { + EngSetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } - if (pwch) + 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(pwch, - sizeof(PWSTR), - 1); + ProbeForRead(UnSafepwch, pwchSize, 1); + RtlCopyMemory(Safepwch, UnSafepwch, pwchSize); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { @@ -3879,15 +6279,13 @@ NtGdiGetCharABCWidthsW( } _SEH2_END; } + if (!NT_SUCCESS(Status)) { - EngSetLastError(Status); - return FALSE; - } + if(Safepwch) + ExFreePoolWithTag(Safepwch , GDITAG_TEXT); - if (!Buffer) - { - EngSetLastError(ERROR_INVALID_PARAMETER); + EngSetLastError(Status); return FALSE; } @@ -3896,6 +6294,10 @@ NtGdiGetCharABCWidthsW( if (!fl) SafeBuffF = (LPABCFLOAT) SafeBuff; if (SafeBuff == NULL) { + + if(Safepwch) + ExFreePoolWithTag(Safepwch , GDITAG_TEXT); + EngSetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } @@ -3904,6 +6306,10 @@ NtGdiGetCharABCWidthsW( if (dc == NULL) { ExFreePoolWithTag(SafeBuff, GDITAG_TEXT); + + if(Safepwch) + ExFreePoolWithTag(Safepwch , GDITAG_TEXT); + EngSetLastError(ERROR_INVALID_HANDLE); return FALSE; } @@ -3918,16 +6324,20 @@ NtGdiGetCharABCWidthsW( 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->face; + face = FontGDI->SharedFace->Face; if (face->charmap == NULL) { - for (i = 0; i < face->num_charmaps; i++) + for (i = 0; i < (UINT)face->num_charmaps; i++) { charmap = face->charmaps[i]; if (charmap->encoding != 0) @@ -3941,40 +6351,35 @@ NtGdiGetCharABCWidthsW( { 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; + IntLockFreeType(); FT_Set_Charmap(face, found); - IntUnLockFreeType; + IntUnLockFreeType(); } - IntLockFreeType; - FT_Set_Pixel_Sizes(face, - TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfWidth, - /* FIXME: Should set character height if neg */ - (TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight == 0 ? - dc->ppdev->devinfo.lfDefaultFont.lfHeight : abs(TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight))); + 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 (pwch) + if (Safepwch) { - if (fl & GCABCW_INDICES) - glyph_index = pwch[i - FirstChar]; - else - glyph_index = FT_Get_Char_Index(face, pwch[i - FirstChar]); + glyph_index = get_glyph_index_flagged(face, Safepwch[i - FirstChar], GCABCW_INDICES, fl); } else { - if (fl & GCABCW_INDICES) - glyph_index = i; - else - glyph_index = FT_Get_Char_Index(face, i); + glyph_index = get_glyph_index_flagged(face, i, GCABCW_INDICES, fl); } FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT); @@ -4003,16 +6408,21 @@ NtGdiGetCharABCWidthsW( SafeBuff[i - FirstChar].abcC = adv - lsb - bbx; } } - IntUnLockFreeType; + 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); - ExFreePoolWithTag(SafeBuff, GDITAG_TEXT); return FALSE; } - ExFreePoolWithTag(SafeBuff, GDITAG_TEXT); + DPRINT("NtGdiGetCharABCWidths Worked!\n"); return TRUE; } @@ -4026,7 +6436,7 @@ NtGdiGetCharWidthW( IN HDC hDC, IN UINT FirstChar, IN UINT Count, - IN OPTIONAL PWCHAR pwc, + IN OPTIONAL PWCHAR UnSafepwc, IN FLONG fl, OUT PVOID Buffer) { @@ -4042,14 +6452,23 @@ NtGdiGetCharWidthW( UINT i, glyph_index, BufferSize; HFONT hFont = 0; PMATRIX pmxWorldToDevice; + PWCHAR Safepwc = NULL; + LOGFONTW *plf; - if (pwc) + 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(pwc, - sizeof(PWSTR), - 1); + ProbeForRead(UnSafepwc, pwcSize, 1); + RtlCopyMemory(Safepwc, UnSafepwc, pwcSize); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { @@ -4057,6 +6476,7 @@ NtGdiGetCharWidthW( } _SEH2_END; } + if (!NT_SUCCESS(Status)) { EngSetLastError(Status); @@ -4068,6 +6488,9 @@ NtGdiGetCharWidthW( if (!fl) SafeBuffF = (PFLOAT) SafeBuff; if (SafeBuff == NULL) { + if(Safepwc) + ExFreePoolWithTag(Safepwc, GDITAG_TEXT); + EngSetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } @@ -4075,6 +6498,9 @@ NtGdiGetCharWidthW( dc = DC_LockDc(hDC); if (dc == NULL) { + if(Safepwc) + ExFreePoolWithTag(Safepwc, GDITAG_TEXT); + ExFreePoolWithTag(SafeBuff, GDITAG_TEXT); EngSetLastError(ERROR_INVALID_HANDLE); return FALSE; @@ -4088,6 +6514,9 @@ NtGdiGetCharWidthW( if (TextObj == NULL) { + if(Safepwc) + ExFreePoolWithTag(Safepwc, GDITAG_TEXT); + ExFreePoolWithTag(SafeBuff, GDITAG_TEXT); EngSetLastError(ERROR_INVALID_HANDLE); return FALSE; @@ -4095,10 +6524,10 @@ NtGdiGetCharWidthW( FontGDI = ObjToGDI(TextObj->Font, FONT); - face = FontGDI->face; + face = FontGDI->SharedFace->Face; if (face->charmap == NULL) { - for (i = 0; i < face->num_charmaps; i++) + for (i = 0; i < (UINT)face->num_charmaps; i++) { charmap = face->charmaps[i]; if (charmap->encoding != 0) @@ -4111,39 +6540,34 @@ NtGdiGetCharWidthW( if (!found) { DPRINT1("WARNING: Could not find desired charmap!\n"); - ExFreePool(SafeBuff); + + if(Safepwc) + ExFreePoolWithTag(Safepwc, GDITAG_TEXT); + + ExFreePoolWithTag(SafeBuff, GDITAG_TEXT); EngSetLastError(ERROR_INVALID_HANDLE); return FALSE; } - IntLockFreeType; + IntLockFreeType(); FT_Set_Charmap(face, found); - IntUnLockFreeType; + IntUnLockFreeType(); } - IntLockFreeType; - FT_Set_Pixel_Sizes(face, - TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfWidth, - /* FIXME: Should set character height if neg */ - (TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight == 0 ? - dc->ppdev->devinfo.lfDefaultFont.lfHeight : abs(TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight))); + plf = &TextObj->logfont.elfEnumLogfontEx.elfLogFont; + IntLockFreeType(); + IntRequestFontSize(dc, FontGDI, plf->lfWidth, plf->lfHeight); FtSetCoordinateTransform(face, pmxWorldToDevice); for (i = FirstChar; i < FirstChar+Count; i++) { - if (pwc) + if (Safepwc) { - if (fl & GCW_INDICES) - glyph_index = pwc[i - FirstChar]; - else - glyph_index = FT_Get_Char_Index(face, pwc[i - FirstChar]); + glyph_index = get_glyph_index_flagged(face, Safepwc[i - FirstChar], GCW_INDICES, fl); } else { - if (fl & GCW_INDICES) - glyph_index = i; - else - glyph_index = FT_Get_Char_Index(face, i); + glyph_index = get_glyph_index_flagged(face, i, GCW_INDICES, fl); } FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT); if (!fl) @@ -4151,137 +6575,80 @@ NtGdiGetCharWidthW( else SafeBuff[i - FirstChar] = (face->glyph->advance.x + 32) >> 6; } - IntUnLockFreeType; + 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 -FASTCALL -GreGetGlyphIndicesW( - HDC hdc, - LPWSTR pwc, - INT cwc, - LPWORD pgi, - DWORD iMode, - DWORD Unknown) +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 = 0; + HFONT hFont = NULL; + NTSTATUS Status = STATUS_SUCCESS; OUTLINETEXTMETRICW *potm; INT i; - FT_Face face; WCHAR DefChar = 0xffff; PWSTR Buffer = NULL; - ULONG Size; + ULONG Size, pwcSize; + PWSTR Safepwc = NULL; + LPCWSTR UnSafepwc = pwc; + LPWORD UnSafepgi = pgi; - if ((!pwc) && (!pgi)) return cwc; - - dc = DC_LockDc(hdc); - if (!dc) - { - EngSetLastError(ERROR_INVALID_HANDLE); - return GDI_ERROR; - } - pdcattr = dc->pdcattr; - hFont = pdcattr->hlfntNew; - TextObj = RealizeFontInit(hFont); - DC_UnlockDc(dc); - if (!TextObj) - { - EngSetLastError(ERROR_INVALID_HANDLE); + /* Check for integer overflow */ + if (cwc & 0x80000000) // (INT_MAX + 1) == INT_MIN return GDI_ERROR; - } - FontGDI = ObjToGDI(TextObj->Font, FONT); - TEXTOBJ_UnlockText(TextObj); + if (!UnSafepwc && !UnSafepgi) + return cwc; - Buffer = ExAllocatePoolWithTag(PagedPool, cwc*sizeof(WORD), GDITAG_TEXT); - if (!Buffer) + if (!UnSafepwc || !UnSafepgi) { - EngSetLastError(ERROR_NOT_ENOUGH_MEMORY); + DPRINT1("UnSafepwc == %p, UnSafepgi = %p\n", UnSafepwc, UnSafepgi); return GDI_ERROR; } - if (iMode & GGI_MARK_NONEXISTING_GLYPHS) DefChar = 0x001f; /* Indicate non existence */ - else + // TODO: Special undocumented case! + if (!pwc && !pgi && (cwc == 0)) { - Size = IntGetOutlineTextMetrics(FontGDI, 0, NULL); - potm = ExAllocatePoolWithTag(PagedPool, Size, GDITAG_TEXT); - if (!potm) - { - EngSetLastError(ERROR_NOT_ENOUGH_MEMORY); - cwc = GDI_ERROR; - goto ErrorRet; - } - IntGetOutlineTextMetrics(FontGDI, Size, potm); - DefChar = potm->otmTextMetrics.tmDefaultChar; // May need this. - ExFreePoolWithTag(potm, GDITAG_TEXT); + DPRINT1("ERR: NtGdiGetGlyphIndicesW with (!pwc && !pgi && (cwc == 0)) is UNIMPLEMENTED!\n"); + return 0; } - IntLockFreeType; - face = FontGDI->face; - - for (i = 0; i < cwc; i++) + // FIXME: This is a hack!! (triggered by e.g. Word 2010). See CORE-12825 + if (cwc == 0) { - Buffer[i] = FT_Get_Char_Index(face, pwc[i]); - if (Buffer[i] == 0) - { - if (DefChar == 0xffff && FT_IS_SFNT(face)) - { - TT_OS2 *pOS2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2); - DefChar = (pOS2->usDefaultChar ? FT_Get_Char_Index(face, pOS2->usDefaultChar) : 0); - } - Buffer[i] = DefChar; - } + DPRINT1("ERR: NtGdiGetGlyphIndicesW with (cwc == 0) is UNIMPLEMENTED!\n"); + return GDI_ERROR; } - IntUnLockFreeType; - - RtlCopyMemory( pgi, Buffer, cwc*sizeof(WORD)); - -ErrorRet: - if (Buffer) ExFreePoolWithTag(Buffer, GDITAG_TEXT); - return cwc; -} - - -/* -* @implemented -*/ -DWORD -APIENTRY -NtGdiGetGlyphIndicesW( - IN HDC hdc, - IN OPTIONAL LPWSTR UnSafepwc, - IN INT cwc, - OUT OPTIONAL LPWORD UnSafepgi, - IN DWORD iMode) -{ - PDC dc; - PDC_ATTR pdcattr; - PTEXTOBJ TextObj; - PFONTGDI FontGDI; - HFONT hFont = 0; - NTSTATUS Status = STATUS_SUCCESS; - OUTLINETEXTMETRICW *potm; - INT i; - FT_Face face; - WCHAR DefChar = 0xffff; - PWSTR Buffer = NULL; - ULONG Size; - - if ((!UnSafepwc) && (!UnSafepgi)) return cwc; - dc = DC_LockDc(hdc); if (!dc) { - EngSetLastError(ERROR_INVALID_HANDLE); return GDI_ERROR; } pdcattr = dc->pdcattr; @@ -4290,7 +6657,6 @@ NtGdiGetGlyphIndicesW( DC_UnlockDc(dc); if (!TextObj) { - EngSetLastError(ERROR_INVALID_HANDLE); return GDI_ERROR; } @@ -4300,30 +6666,50 @@ NtGdiGetGlyphIndicesW( Buffer = ExAllocatePoolWithTag(PagedPool, cwc*sizeof(WORD), GDITAG_TEXT); if (!Buffer) { - EngSetLastError(ERROR_NOT_ENOUGH_MEMORY); return GDI_ERROR; } - if (iMode & GGI_MARK_NONEXISTING_GLYPHS) DefChar = 0x001f; /* Indicate non existence */ + if (iMode & GGI_MARK_NONEXISTING_GLYPHS) + { + DefChar = 0xffff; + } else { - Size = IntGetOutlineTextMetrics(FontGDI, 0, NULL); - potm = ExAllocatePoolWithTag(PagedPool, Size, GDITAG_TEXT); - if (!potm) + FT_Face Face = FontGDI->SharedFace->Face; + if (FT_IS_SFNT(Face)) { - Status = ERROR_NOT_ENOUGH_MEMORY; - goto ErrorRet; + TT_OS2 *pOS2 = FT_Get_Sfnt_Table(Face, ft_sfnt_os2); + DefChar = (pOS2->usDefaultChar ? get_glyph_index(Face, pOS2->usDefaultChar) : 0); } - IntGetOutlineTextMetrics(FontGDI, Size, potm); - DefChar = potm->otmTextMetrics.tmDefaultChar; // May need this. - ExFreePoolWithTag(potm, GDITAG_TEXT); + else + { + Size = IntGetOutlineTextMetrics(FontGDI, 0, NULL); + potm = ExAllocatePoolWithTag(PagedPool, Size, GDITAG_TEXT); + if (!potm) + { + cwc = GDI_ERROR; + goto ErrorRet; + } + Size = IntGetOutlineTextMetrics(FontGDI, Size, potm); + if (Size) + DefChar = potm->otmTextMetrics.tmDefaultChar; + ExFreePoolWithTag(potm, GDITAG_TEXT); + } + } + + pwcSize = cwc * sizeof(WCHAR); + Safepwc = ExAllocatePoolWithTag(PagedPool, pwcSize, GDITAG_TEXT); + + if (!Safepwc) + { + Status = STATUS_NO_MEMORY; + goto ErrorRet; } _SEH2_TRY { - ProbeForRead(UnSafepwc, - sizeof(PWSTR), - 1); + ProbeForRead(UnSafepwc, pwcSize, 1); + RtlCopyMemory(Safepwc, UnSafepwc, pwcSize); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { @@ -4333,34 +6719,23 @@ NtGdiGetGlyphIndicesW( if (!NT_SUCCESS(Status)) goto ErrorRet; - IntLockFreeType; - face = FontGDI->face; - - if (DefChar == 0xffff && FT_IS_SFNT(face)) - { - TT_OS2 *pOS2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2); - DefChar = (pOS2->usDefaultChar ? FT_Get_Char_Index(face, pOS2->usDefaultChar) : 0); - } + IntLockFreeType(); for (i = 0; i < cwc; i++) { - Buffer[i] = FT_Get_Char_Index(face, UnSafepwc[i]); // FIXME: Unsafe! + Buffer[i] = get_glyph_index(FontGDI->SharedFace->Face, Safepwc[i]); if (Buffer[i] == 0) { Buffer[i] = DefChar; } } - IntUnLockFreeType; + IntUnLockFreeType(); _SEH2_TRY { - ProbeForWrite(UnSafepgi, - sizeof(WORD), - 1); - RtlCopyMemory(UnSafepgi, - Buffer, - cwc*sizeof(WORD)); + ProbeForWrite(UnSafepgi, cwc * sizeof(WORD), 1); + RtlCopyMemory(UnSafepgi, Buffer, cwc * sizeof(WORD)); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { @@ -4370,8 +6745,11 @@ NtGdiGetGlyphIndicesW( ErrorRet: ExFreePoolWithTag(Buffer, GDITAG_TEXT); + if (Safepwc != NULL) + { + ExFreePoolWithTag(Safepwc, GDITAG_TEXT); + } if (NT_SUCCESS(Status)) return cwc; - EngSetLastError(Status); return GDI_ERROR; }