[WIN32SS] Cleanup fonts at process destruction + implement font memory reference counting.

Thanks to everyone involved in reviewing this code! (See CR-112)
CORE-13056

svn path=/trunk/; revision=74309
This commit is contained in:
Mark Jansen
2017-04-14 18:22:57 +00:00
parent 9bb6d0de5b
commit 246f24bd7f
9 changed files with 523 additions and 161 deletions
+10 -2
View File
@@ -107,9 +107,17 @@ typedef struct _FLOATGDI {
ULONG Dummy;
} FLOATGDI;
typedef struct _SHARED_MEM {
PVOID Buffer;
ULONG BufferSize;
BOOL IsMapping;
LONG RefCount;
} SHARED_MEM, *PSHARED_MEM;
typedef struct _SHARED_FACE {
FT_Face Face;
LONG RefCount;
FT_Face Face;
LONG RefCount;
PSHARED_MEM Memory;
} SHARED_FACE, *PSHARED_FACE;
typedef struct _FONTGDI {
+1 -2
View File
@@ -54,8 +54,7 @@ typedef struct FONTSUBST_ENTRY
typedef struct GDI_LOAD_FONT
{
PUNICODE_STRING pFileName;
PVOID Buffer;
ULONG BufferSize;
PSHARED_MEM Memory;
DWORD Characteristics;
UNICODE_STRING RegValueName;
BOOL IsTrueType;
+250 -61
View File
@@ -58,39 +58,6 @@ static const UNICODE_STRING FixedSysW = RTL_CONSTANT_STRING(L"FixedSys");
static UNICODE_STRING FontRegPath =
RTL_CONSTANT_STRING(L"\\REGISTRY\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts");
static PSHARED_FACE
SharedFace_Create(FT_Face Face)
{
PSHARED_FACE Ptr;
Ptr = ExAllocatePoolWithTag(PagedPool, sizeof(SHARED_FACE), TAG_FONT);
if (Ptr)
{
Ptr->Face = Face;
Ptr->RefCount = 1;
}
return Ptr;
}
static void
SharedFace_AddRef(PSHARED_FACE Ptr)
{
++Ptr->RefCount;
}
static void
SharedFace_Release(PSHARED_FACE Ptr)
{
if (Ptr->RefCount <= 0)
return;
--Ptr->RefCount;
if (Ptr->RefCount == 0)
{
FT_Done_Face(Ptr->Face);
ExFreePoolWithTag(Ptr, TAG_FONT);
}
}
/* The FreeType library is not thread safe, so we have
to serialize access to it */
@@ -106,12 +73,18 @@ static BOOL RenderingEnabled = TRUE;
#define IntUnLockGlobalFonts \
ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(FontListLock)
#define ASSERT_GLOBALFONTS_LOCK_HELD() \
ASSERT(FreeTypeLock->Owner == KeGetCurrentThread())
#define IntLockFreeType \
ExEnterCriticalRegionAndAcquireFastMutexUnsafe(FreeTypeLock)
#define IntUnLockFreeType \
ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(FreeTypeLock)
#define ASSERT_FREETYPE_LOCK_HELD() \
ASSERT(FreeTypeLock->Owner == KeGetCurrentThread())
#define MAX_FONT_CACHE 256
static LIST_ENTRY FontCacheListHead;
@@ -189,6 +162,129 @@ static const CHARSETINFO FontTci[MAXTCIINDEX] =
/* list head */
static RTL_STATIC_LIST_HEAD(FontSubstListHead);
static void
SharedMem_AddRef(PSHARED_MEM Ptr)
{
ASSERT_FREETYPE_LOCK_HELD();
++Ptr->RefCount;
}
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;
SharedMem_AddRef(Memory);
DPRINT("Creating SharedFace for %s\n", 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);
FontCacheNumEntries--;
ASSERT(FontCacheNumEntries <= MAX_FONT_CACHE);
}
static void
RemoveCacheEntries(FT_Face Face)
{
PLIST_ENTRY CurrentEntry;
PFONT_CACHE_ENTRY FontEntry;
ASSERT_FREETYPE_LOCK_HELD();
CurrentEntry = FontCacheListHead.Flink;
while (CurrentEntry != &FontCacheListHead)
{
FontEntry = CONTAINING_RECORD(CurrentEntry, FONT_CACHE_ENTRY, ListEntry);
CurrentEntry = 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
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);
RemoveCacheEntries(Ptr->Face);
FT_Done_Face(Ptr->Face);
SharedMem_Release(Ptr->Memory);
ExFreePoolWithTag(Ptr, TAG_FONT);
}
IntUnLockFreeType;
}
/*
* IntLoadFontSubstList --- loads the list of font substitutes
*/
@@ -677,8 +773,6 @@ IntGdiLoadFontsFromMemory(PGDI_LOAD_FONT pLoadFont,
FT_WinFNT_HeaderRec WinFNT;
INT FontCount = 0, CharSetCount = 0;
PUNICODE_STRING pFileName = pLoadFont->pFileName;
PVOID Buffer = pLoadFont->Buffer;
ULONG BufferSize = pLoadFont->BufferSize;
DWORD Characteristics = pLoadFont->Characteristics;
PUNICODE_STRING pValueName = &pLoadFont->RegValueName;
TT_OS2 * pOS2;
@@ -693,30 +787,37 @@ IntGdiLoadFontsFromMemory(PGDI_LOAD_FONT pLoadFont,
IntLockFreeType;
Error = FT_New_Memory_Face(
library,
Buffer,
BufferSize,
pLoadFont->Memory->Buffer,
pLoadFont->Memory->BufferSize,
((FontIndex != -1) ? FontIndex : 0),
&Face);
if (!Error)
SharedFace = SharedFace_Create(Face, pLoadFont->Memory);
IntUnLockFreeType;
if (FT_IS_SFNT(Face))
pLoadFont->IsTrueType = TRUE;
if (!Error)
SharedFace = SharedFace_Create(Face);
if (Error || SharedFace == NULL)
{
if (SharedFace)
SharedFace_Release(SharedFace);
if (Error == FT_Err_Unknown_File_Format)
DPRINT1("Unknown font file format\n");
else
DPRINT1("Error reading font file (error code: %d)\n", Error);
DPRINT1("Error reading font (error code: %d)\n", Error);
return 0; /* failure */
}
}
else
{
Face = SharedFace->Face;
IntLockFreeType;
SharedFace_AddRef(SharedFace);
IntUnLockFreeType;
}
/* allocate a FONT_ENTRY */
@@ -1006,8 +1107,7 @@ IntGdiAddFontResource(PUNICODE_STRING FileName, DWORD Characteristics)
}
LoadFont.pFileName = FileName;
LoadFont.Buffer = Buffer;
LoadFont.BufferSize = ViewSize;
LoadFont.Memory = SharedMem_Create(Buffer, ViewSize, TRUE);
LoadFont.Characteristics = Characteristics;
RtlInitUnicodeString(&LoadFont.RegValueName, NULL);
LoadFont.IsTrueType = FALSE;
@@ -1016,6 +1116,11 @@ IntGdiAddFontResource(PUNICODE_STRING FileName, DWORD Characteristics)
ObDereferenceObject(SectionObject);
/* Release our copy */
IntLockFreeType;
SharedMem_Release(LoadFont.Memory);
IntUnLockFreeType;
if (FontCount > 0)
{
if (LoadFont.IsTrueType)
@@ -1070,17 +1175,17 @@ IntGdiAddFontMemResource(PVOID Buffer, DWORD dwSize, PDWORD pNumAdded)
INT FontCount;
HANDLE Ret = 0;
/* We leak this buffer for now, same as all fonts do with their buffer! */
LoadFont.Buffer = ExAllocatePoolWithTag(PagedPool, dwSize, TAG_FONT);
if (!LoadFont.Buffer)
PVOID BufferCopy = ExAllocatePoolWithTag(PagedPool, dwSize, TAG_FONT);
if (!BufferCopy)
{
*pNumAdded = 0;
return NULL;
}
memcpy(LoadFont.Buffer, Buffer, dwSize);
memcpy(BufferCopy, Buffer, dwSize);
LoadFont.pFileName = NULL;
LoadFont.BufferSize = dwSize;
LoadFont.Memory = SharedMem_Create(BufferCopy, dwSize, FALSE);
LoadFont.Characteristics = FR_PRIVATE | FR_NOT_ENUM;
RtlInitUnicodeString(&LoadFont.RegValueName, NULL);
LoadFont.IsTrueType = FALSE;
@@ -1089,7 +1194,11 @@ IntGdiAddFontMemResource(PVOID Buffer, DWORD dwSize, PDWORD pNumAdded)
RtlFreeUnicodeString(&LoadFont.RegValueName);
*pNumAdded = FontCount;
/* Release our copy */
IntLockFreeType;
SharedMem_Release(LoadFont.Memory);
IntUnLockFreeType;
if (FontCount > 0)
{
EntryCollection = ExAllocatePoolWithTag(PagedPool, sizeof(FONT_ENTRY_COLL_MEM), TAG_FONT);
@@ -1104,12 +1213,27 @@ IntGdiAddFontMemResource(PVOID Buffer, DWORD dwSize, PDWORD pNumAdded)
Ret = (HANDLE)EntryCollection->Handle;
}
}
*pNumAdded = FontCount;
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)
{
@@ -1121,14 +1245,31 @@ IntGdiCleanupMemEntry(PFONT_ENTRY_MEM Head)
Entry = RemoveHeadList(&Head->ListEntry);
FontEntry = CONTAINING_RECORD(Entry, FONT_ENTRY_MEM, ListEntry);
// Delete FontEntry->Entry (FONT_ENTRY*)
CleanupFontEntry(FontEntry->Entry);
ExFreePoolWithTag(FontEntry, TAG_FONT);
}
// Delete Head->Entry (FONT_ENTRY*)
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)
{
@@ -1146,7 +1287,7 @@ IntGdiRemoveFontMemResource(HANDLE hMMFont)
if (CurrentEntry->Handle == (UINT)hMMFont)
{
EntryCollection = CurrentEntry;
RemoveEntryList(Entry);
UnlinkFontMemCollection(CurrentEntry);
break;
}
@@ -1164,6 +1305,52 @@ IntGdiRemoveFontMemResource(HANDLE hMMFont)
}
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)
{
@@ -1737,7 +1924,7 @@ FindFaceNameInList(PUNICODE_STRING FaceName, PLIST_ENTRY Head)
Entry = Head->Flink;
while (Entry != Head)
{
CurrentEntry = (PFONT_ENTRY) CONTAINING_RECORD(Entry, FONT_ENTRY, ListEntry);
CurrentEntry = CONTAINING_RECORD(Entry, FONT_ENTRY, ListEntry);
FontGDI = CurrentEntry->Font;
ASSERT(FontGDI);
@@ -1774,7 +1961,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);
@@ -2333,10 +2521,12 @@ ftGdiGlyphCacheGet(
PLIST_ENTRY CurrentEntry;
PFONT_CACHE_ENTRY FontEntry;
ASSERT_FREETYPE_LOCK_HELD();
CurrentEntry = FontCacheListHead.Flink;
while (CurrentEntry != &FontCacheListHead)
{
FontEntry = (PFONT_CACHE_ENTRY)CurrentEntry;
FontEntry = CONTAINING_RECORD(CurrentEntry, FONT_CACHE_ENTRY, ListEntry);
if ((FontEntry->Face == Face) &&
(FontEntry->GlyphIndex == GlyphIndex) &&
(FontEntry->Height == Height) &&
@@ -2412,6 +2602,8 @@ ftGdiGlyphCacheSet(
FT_Bitmap AlignedBitmap;
FT_BitmapGlyph BitmapGlyph;
ASSERT_FREETYPE_LOCK_HELD();
error = FT_Get_Glyph(GlyphSlot, &GlyphCopy);
if (error)
{
@@ -2455,13 +2647,10 @@ ftGdiGlyphCacheSet(
NewEntry->mxWorldToDevice = *pmx;
InsertHeadList(&FontCacheListHead, &NewEntry->ListEntry);
if (FontCacheNumEntries++ > MAX_FONT_CACHE)
if (++FontCacheNumEntries > MAX_FONT_CACHE)
{
NewEntry = (PFONT_CACHE_ENTRY)FontCacheListHead.Blink;
FT_Done_Glyph((FT_Glyph)NewEntry->BitmapGlyph);
RemoveTailList(&FontCacheListHead);
ExFreePoolWithTag(NewEntry, TAG_FONT);
FontCacheNumEntries--;
NewEntry = CONTAINING_RECORD(FontCacheListHead.Blink, FONT_CACHE_ENTRY, ListEntry);
RemoveCachedEntry(NewEntry);
}
return BitmapGlyph;
@@ -4116,7 +4305,7 @@ FindBestFontFromList(FONTOBJ **FontObj, ULONG *MatchPenalty, LOGFONTW *LogFont,
Entry = Head->Flink;
while (Entry != Head)
{
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;
+2
View File
@@ -50,6 +50,8 @@ GdiProcessDestroy(PEPROCESS Process)
ASSERT(ppiCurrent);
ASSERT(ppiCurrent->peProcess == Process);
IntGdiCleanupPrivateFontsForProcess();
/* And GDI ones too */
GDI_CleanupForProcess(Process);
+1
View File
@@ -112,6 +112,7 @@ BOOL FASTCALL IntIsFontRenderingEnabled(VOID);
VOID FASTCALL IntEnableFontRendering(BOOL Enable);
ULONG FASTCALL FontGetObject(PTEXTOBJ TextObj, ULONG Count, PVOID Buffer);
VOID FASTCALL IntLoadSystemFonts(VOID);
VOID FASTCALL IntGdiCleanupPrivateFontsForProcess(VOID);
INT FASTCALL IntGdiAddFontResource(PUNICODE_STRING FileName, DWORD Characteristics);
HANDLE FASTCALL IntGdiAddFontMemResource(PVOID Buffer, DWORD dwSize, PDWORD pNumAdded);
BOOL FASTCALL IntGdiRemoveFontMemResource(HANDLE hMMFont);
+257 -96
View File
@@ -4,7 +4,9 @@
* PURPOSE: Test for AddFontMemResourceEx
* PROGRAMMERS: Mark Jansen
*
* PanosePitchTest by Katayama Hirofumi MZ, licensed under CC BY
* PanosePitchTest + TTCTestV by Katayama Hirofumi MZ, licensed under CC BY
* Shadows_Into_Light by Kimberly Geswein, licensed under OFL
* Captured from firefox, embedded on reactos.org
*/
@@ -12,102 +14,260 @@
#include <wingdi.h>
#include <winuser.h>
typedef struct _fnt_res
{
const char* FontName;
TEXTMETRICA tm;
} fnt_res;
static void test_font_caps(HDC hdc)
typedef struct _fnt_test
{
const char* ResourceName;
int NumFaces;
fnt_res res[4];
} fnt_test;
static fnt_test test_data[] =
{
{
.ResourceName = "PanosePitchTest.ttf",
.NumFaces = 2,
.res =
{
{
.FontName = "PanosePitchTest",
.tm.tmHeight = 11,
.tm.tmAscent = 11,
.tm.tmDescent = 0,
.tm.tmInternalLeading = -5,
.tm.tmExternalLeading = 1,
.tm.tmAveCharWidth = 8,
.tm.tmMaxCharWidth = 11,
.tm.tmWeight = FW_NORMAL,
.tm.tmOverhang = 0,
.tm.tmDigitizedAspectX = 96,
.tm.tmDigitizedAspectY = 96,
.tm.tmFirstChar = 63,
.tm.tmLastChar = 65,
.tm.tmDefaultChar = 165,
.tm.tmBreakChar = 65,
.tm.tmItalic = 0,
.tm.tmUnderlined = 0,
.tm.tmStruckOut = 0,
.tm.tmPitchAndFamily = TMPF_TRUETYPE | TMPF_VECTOR,
.tm.tmCharSet = SHIFTJIS_CHARSET,
},
{
.FontName = "@PanosePitchTest",
.tm.tmHeight = 11,
.tm.tmAscent = 11,
.tm.tmDescent = 0,
.tm.tmInternalLeading = -5,
.tm.tmExternalLeading = 1,
.tm.tmAveCharWidth = 8,
.tm.tmMaxCharWidth = 11,
.tm.tmWeight = FW_NORMAL,
.tm.tmOverhang = 0,
.tm.tmDigitizedAspectX = 96,
.tm.tmDigitizedAspectY = 96,
.tm.tmFirstChar = 63,
.tm.tmLastChar = 65,
.tm.tmDefaultChar = 165,
.tm.tmBreakChar = 65,
.tm.tmItalic = 0,
.tm.tmUnderlined = 0,
.tm.tmStruckOut = 0,
.tm.tmPitchAndFamily = TMPF_TRUETYPE | TMPF_VECTOR,
.tm.tmCharSet = SHIFTJIS_CHARSET,
},
},
},
{
.ResourceName = "TTCTestV.ttc",
.NumFaces = 3,
.res =
{
{
.FontName = "No1Of3in1",
.tm.tmHeight = 12,
.tm.tmAscent = 12,
.tm.tmDescent = 0,
.tm.tmInternalLeading = -4,
.tm.tmExternalLeading = 1,
.tm.tmAveCharWidth = -525,
.tm.tmMaxCharWidth = 6,
.tm.tmWeight = FW_NORMAL,
.tm.tmOverhang = 0,
.tm.tmDigitizedAspectX = 96,
.tm.tmDigitizedAspectY = 96,
.tm.tmFirstChar = 63,
.tm.tmLastChar = 65,
.tm.tmDefaultChar = 64,
.tm.tmBreakChar = 65,
.tm.tmItalic = 0,
.tm.tmUnderlined = 0,
.tm.tmStruckOut = 0,
.tm.tmPitchAndFamily = TMPF_TRUETYPE | TMPF_VECTOR | TMPF_FIXED_PITCH,
.tm.tmCharSet = ANSI_CHARSET,
},
{
.FontName = "No2Of3in1",
.tm.tmHeight = 12,
.tm.tmAscent = 12,
.tm.tmDescent = 0,
.tm.tmInternalLeading = -4,
.tm.tmExternalLeading = 1,
.tm.tmAveCharWidth = 8,
.tm.tmMaxCharWidth = 7,
.tm.tmWeight = FW_NORMAL,
.tm.tmOverhang = 0,
.tm.tmDigitizedAspectX = 96,
.tm.tmDigitizedAspectY = 96,
.tm.tmFirstChar = 63,
.tm.tmLastChar = 65,
.tm.tmDefaultChar = 64,
.tm.tmBreakChar = 65,
.tm.tmItalic = 0,
.tm.tmUnderlined = 0,
.tm.tmStruckOut = 0,
.tm.tmPitchAndFamily = TMPF_TRUETYPE | TMPF_VECTOR | TMPF_FIXED_PITCH,
.tm.tmCharSet = ANSI_CHARSET,
},
{
.FontName = "No3Of3in1V",
.tm.tmHeight = 12,
.tm.tmAscent = 12,
.tm.tmDescent = 0,
.tm.tmInternalLeading = -4,
.tm.tmExternalLeading = 1,
.tm.tmAveCharWidth = 8,
.tm.tmMaxCharWidth = 13,
.tm.tmWeight = FW_NORMAL,
.tm.tmOverhang = 0,
.tm.tmDigitizedAspectX = 96,
.tm.tmDigitizedAspectY = 96,
.tm.tmFirstChar = 63,
.tm.tmLastChar = 65,
.tm.tmDefaultChar = 64,
.tm.tmBreakChar = 65,
.tm.tmItalic = 0,
.tm.tmUnderlined = 0,
.tm.tmStruckOut = 0,
.tm.tmPitchAndFamily = FF_MODERN | TMPF_TRUETYPE | TMPF_VECTOR,
.tm.tmCharSet = ANSI_CHARSET,
},
},
},
{
.ResourceName = "Shadows_Into_Light.ttf",
.NumFaces = 1,
.res =
{
{
.FontName = "ufaXaAlLOxCUGYJ7KN51UP2Q==",
.tm.tmHeight = 26,
.tm.tmAscent = 19,
.tm.tmDescent = 7,
.tm.tmInternalLeading = 10,
.tm.tmExternalLeading = 0,
.tm.tmAveCharWidth = 7,
.tm.tmMaxCharWidth = 23,
.tm.tmWeight = FW_NORMAL,
.tm.tmOverhang = 0,
.tm.tmDigitizedAspectX = 96,
.tm.tmDigitizedAspectY = 96,
.tm.tmFirstChar = 30,
.tm.tmLastChar = 255,
.tm.tmDefaultChar = 31,
.tm.tmBreakChar = 32,
.tm.tmItalic = 0,
.tm.tmUnderlined = 0,
.tm.tmStruckOut = 0,
.tm.tmPitchAndFamily = TMPF_TRUETYPE | TMPF_VECTOR | TMPF_FIXED_PITCH,
.tm.tmCharSet = ANSI_CHARSET,
},
},
},
};
#define ok_int2(expression) \
do { \
int _value = (expression); \
ok(_value == (res->expression), "Wrong value for '%s', expected: %d, got: %d for %s/%s\n", \
#expression, (int)(res->expression), _value, test_name, res->FontName); \
} while (0)
#define ok_hex2(expression) \
do { \
int _value = (expression); \
ok(_value == (res->expression), "Wrong value for '%s', expected: 0x%x, got: 0x%x for %s/%s\n", \
#expression, (int)(res->expression), _value, test_name, res->FontName); \
} while (0)
static void test_font_caps(HDC hdc, int test_index)
{
HGDIOBJ old;
TEXTMETRICA tm = { 0 };
char name[64];
BOOL ret;
HFONT font = CreateFont(0, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, DEFAULT_CHARSET,
OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, TEXT("PanosePitchTest"));
HFONT font;
int n;
const char* test_name = test_data[test_index].ResourceName;
if (font)
for (n = 0; test_data[test_index].res[n].FontName; ++n)
{
old = SelectObject(hdc, font);
fnt_res* res = test_data[test_index].res + n;
font = CreateFontA(0, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, DEFAULT_CHARSET,
OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, res->FontName);
memset(&tm, 0xaa, sizeof(tm));
ret = GetTextMetricsA(hdc, &tm);
ok_int(ret, TRUE);
SetLastError(0xdeadbeef);
ret = GetTextFaceA(hdc, sizeof(name), name);
ok(ret, "GetTextFaceA error %lu\n", GetLastError());
if (ret)
if (font)
{
ok_str(name, "PanosePitchTest");
old = SelectObject(hdc, font);
memset(&tm, 0xaa, sizeof(tm));
ret = GetTextMetricsA(hdc, &tm);
ok(ret, "GetTextMetricsA() for %s/%s\n", test_name, res->FontName);
SetLastError(0xdeadbeef);
ret = GetTextFaceA(hdc, sizeof(name), name);
ok(ret, "GetTextFaceA error %lu for %s/%s\n", GetLastError(), test_name, res->FontName);
if (ret)
{
ok(!strcmp(name, res->FontName), "FontName was %s, expected %s for %s/%s", name, res->FontName, test_name, res->FontName);
}
ok_int2(tm.tmHeight);
ok_int2(tm.tmAscent);
ok_int2(tm.tmDescent);
ok_int2(tm.tmInternalLeading);
ok_int2(tm.tmExternalLeading);
ok_int2(tm.tmAveCharWidth);
ok_int2(tm.tmMaxCharWidth);
ok_int2(tm.tmWeight);
ok_int2(tm.tmOverhang);
ok_int2(tm.tmDigitizedAspectX);
ok_int2(tm.tmDigitizedAspectY);
ok_int2(tm.tmFirstChar);
ok_int2(tm.tmLastChar);
ok_int2(tm.tmDefaultChar);
ok_int2(tm.tmBreakChar);
ok_int2(tm.tmItalic);
ok_int2(tm.tmUnderlined);
ok_int2(tm.tmStruckOut);
ok_hex2(tm.tmPitchAndFamily);
ok_int2(tm.tmCharSet);
SelectObject(hdc, old);
DeleteObject(font);
}
ok_int(tm.tmHeight, 11);
ok_int(tm.tmAscent, 11);
ok_int(tm.tmDescent, 0);
ok_int(tm.tmInternalLeading, -5);
ok_int(tm.tmExternalLeading, 1);
ok_int(tm.tmAveCharWidth, 8);
ok_int(tm.tmMaxCharWidth, 11);
ok_int(tm.tmWeight, FW_NORMAL);
ok_int(tm.tmOverhang, 0);
ok_int(tm.tmDigitizedAspectX, 96);
ok_int(tm.tmDigitizedAspectY, 96);
ok_int(tm.tmFirstChar, 63);
ok_int(tm.tmLastChar, 65);
ok_int(tm.tmDefaultChar, 165);
ok_int(tm.tmBreakChar, 65);
ok_int(tm.tmItalic, 0);
ok_int(tm.tmUnderlined, 0);
ok_int(tm.tmStruckOut, 0);
ok_hex(tm.tmPitchAndFamily, TMPF_TRUETYPE | TMPF_VECTOR);
ok_int(tm.tmCharSet, SHIFTJIS_CHARSET);
SelectObject(hdc, old);
DeleteObject(font);
}
font = CreateFont(0, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, DEFAULT_CHARSET,
OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, TEXT("@PanosePitchTest"));
if (font)
{
old = SelectObject(hdc, font);
memset(&tm, 0xaa, sizeof(tm));
ret = GetTextMetricsA(hdc, &tm);
ok_int(ret, TRUE);
SetLastError(0xdeadbeef);
ret = GetTextFaceA(hdc, sizeof(name), name);
ok(ret, "GetTextFaceA error %lu\n", GetLastError());
if (ret)
{
ok_str(name, "@PanosePitchTest");
}
ok_int(tm.tmHeight, 11);
ok_int(tm.tmAscent, 11);
ok_int(tm.tmDescent, 0);
ok_int(tm.tmInternalLeading, -5);
ok_int(tm.tmExternalLeading, 1);
ok_int(tm.tmAveCharWidth, 8);
ok_int(tm.tmMaxCharWidth, 11);
ok_int(tm.tmWeight, FW_NORMAL);
ok_int(tm.tmOverhang, 0);
ok_int(tm.tmDigitizedAspectX, 96);
ok_int(tm.tmDigitizedAspectY, 96);
ok_int(tm.tmFirstChar, 63);
ok_int(tm.tmLastChar, 65);
ok_int(tm.tmDefaultChar, 165);
ok_int(tm.tmBreakChar, 65);
ok_int(tm.tmItalic, 0);
ok_int(tm.tmUnderlined, 0);
ok_int(tm.tmStruckOut, 0);
ok_hex(tm.tmPitchAndFamily, TMPF_TRUETYPE | TMPF_VECTOR);
ok_int(tm.tmCharSet, SHIFTJIS_CHARSET);
SelectObject(hdc, old);
DeleteObject(font);
}
}
/* Not working as of 2017-04-08 on ReactOS */
static BOOL is_font_available(HDC hdc, const char* fontName)
{
@@ -122,7 +282,7 @@ static BOOL is_font_available(HDC hdc, const char* fontName)
SetLastError(0xdeadbeef);
ret = GetTextFaceA(hdc, sizeof(name), name);
ok(ret, "GetTextFaceA error %lu\n", GetLastError());
ok(ret, "GetTextFaceA error %lu for %s\n", GetLastError(), fontName);
SelectObject(hdc, old);
DeleteObject(font);
@@ -145,16 +305,20 @@ START_TEST(AddFontMemResourceEx)
LPVOID pFont;
HANDLE hFont;
fnt_test* data;
int n;
HDC hdc = CreateCompatibleDC(NULL);
BOOL is_font_available_broken = is_font_available(hdc, "Nonexisting font name here");
ok(!is_font_available_broken, "Validating font is broken! (CORE-13053) !\n");
ok(!is_font_available_broken, "Validating font is broken! (CORE-13053)!\n");
if (is_font_available_broken || !is_font_available(hdc, "PanosePitchTest"))
for (n = 0; n < _countof(test_data); ++n)
{
data = test_data + n;
mod = GetModuleHandle(NULL);
hRsrc = FindResource(mod, TEXT("PanosePitchTest.ttf"), MAKEINTRESOURCE(RT_RCDATA));
hRsrc = FindResourceA(mod, data->ResourceName, MAKEINTRESOURCE(RT_RCDATA));
hTemplate = LoadResource(mod, hRsrc);
dwSize = SizeofResource(mod, hRsrc);
@@ -162,30 +326,27 @@ START_TEST(AddFontMemResourceEx)
dwNumFonts = 0;
hFont = AddFontMemResourceEx(pFont, dwSize, NULL, &dwNumFonts);
ok_int(dwNumFonts, 2);
ok(hFont != NULL, "Expected valid handle\n");
ok(dwNumFonts == data->NumFaces, "dwNumFonts was %lu, expected %d for %s\n", dwNumFonts, data->NumFaces, data->ResourceName);
ok(hFont != NULL, "Expected valid handle for %s\n", data->ResourceName);
if (hFont)
{
test_font_caps(hdc);
test_font_caps(hdc, n);
RemoveFontMemResourceEx(hFont);
if (!is_font_available_broken)
{
ok (!is_font_available(hdc, "PanosePitchTest"), "Expected font to be unregistered again\n");
ok (!is_font_available(hdc, data->ResourceName), "Expected font to be unregistered again for %s\n", data->ResourceName);
}
else
{
skip("Font unregister test\n");
skip("Font unregister test for %s\n", data->ResourceName);
}
}
UnlockResource(hTemplate);
FreeResource(hTemplate);
}
else
{
skip("Font PanosePitchTest already available\n");
}
DeleteDC(hdc);
}
Binary file not shown.
Binary file not shown.
+2
View File
@@ -2,3 +2,5 @@
ReactOSTestTahoma.ttf RCDATA ReactOSTestTahoma.ttf
PanosePitchTest.ttf RCDATA PanosePitchTest.ttf
TTCTestV.ttc RCDATA TTCTestV.ttc
Shadows_Into_Light.ttf RCDATA Shadows_Into_Light.ttf