Author: mjansen
Date: Fri Apr 14 18:22:57 2017
New Revision: 74309
URL:
http://svn.reactos.org/svn/reactos?rev=74309&view=rev
Log:
[WIN32SS] Cleanup fonts at process destruction + implement font memory reference
counting.
Thanks to everyone involved in reviewing this code! (See CR-112)
CORE-13056
Added:
trunk/rostests/apitests/gdi32/Shadows_Into_Light.ttf (with props)
trunk/rostests/apitests/gdi32/TTCTestV.ttc (with props)
Modified:
trunk/reactos/win32ss/gdi/eng/engobjects.h
trunk/reactos/win32ss/gdi/ntgdi/font.h
trunk/reactos/win32ss/gdi/ntgdi/freetype.c
trunk/reactos/win32ss/gdi/ntgdi/init.c
trunk/reactos/win32ss/gdi/ntgdi/text.h
trunk/rostests/apitests/gdi32/AddFontMemResourceEx.c
trunk/rostests/apitests/gdi32/resource.rc
Modified: trunk/reactos/win32ss/gdi/eng/engobjects.h
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/win32ss/gdi/eng/engobjects…
==============================================================================
--- trunk/reactos/win32ss/gdi/eng/engobjects.h [iso-8859-1] (original)
+++ trunk/reactos/win32ss/gdi/eng/engobjects.h [iso-8859-1] Fri Apr 14 18:22:57 2017
@@ -107,9 +107,17 @@
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 {
Modified: trunk/reactos/win32ss/gdi/ntgdi/font.h
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/win32ss/gdi/ntgdi/font.h?r…
==============================================================================
--- trunk/reactos/win32ss/gdi/ntgdi/font.h [iso-8859-1] (original)
+++ trunk/reactos/win32ss/gdi/ntgdi/font.h [iso-8859-1] Fri Apr 14 18:22:57 2017
@@ -54,8 +54,7 @@
typedef struct GDI_LOAD_FONT
{
PUNICODE_STRING pFileName;
- PVOID Buffer;
- ULONG BufferSize;
+ PSHARED_MEM Memory;
DWORD Characteristics;
UNICODE_STRING RegValueName;
BOOL IsTrueType;
Modified: trunk/reactos/win32ss/gdi/ntgdi/freetype.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/win32ss/gdi/ntgdi/freetype…
==============================================================================
--- trunk/reactos/win32ss/gdi/ntgdi/freetype.c [iso-8859-1] (original)
+++ trunk/reactos/win32ss/gdi/ntgdi/freetype.c [iso-8859-1] Fri Apr 14 18:22:57 2017
@@ -58,39 +58,6 @@
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,11 +73,17 @@
#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
@@ -188,6 +161,129 @@
/* 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 @@
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 @@
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 @@
}
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;
@@ -1015,6 +1115,11 @@
FontCount = IntGdiLoadFontsFromMemory(&LoadFont, NULL, -1, -1);
ObDereferenceObject(SectionObject);
+
+ /* Release our copy */
+ IntLockFreeType;
+ SharedMem_Release(LoadFont.Memory);
+ IntUnLockFreeType;
if (FontCount > 0)
{
@@ -1070,17 +1175,17 @@
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 @@
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,11 +1213,26 @@
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,12 +1245,29 @@
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
@@ -1146,7 +1287,7 @@
if (CurrentEntry->Handle == (UINT)hMMFont)
{
EntryCollection = CurrentEntry;
- RemoveEntryList(Entry);
+ UnlinkFontMemCollection(CurrentEntry);
break;
}
@@ -1163,6 +1304,52 @@
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)
@@ -1737,7 +1924,7 @@
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 @@
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 @@
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 @@
FT_Bitmap AlignedBitmap;
FT_BitmapGlyph BitmapGlyph;
+ ASSERT_FREETYPE_LOCK_HELD();
+
error = FT_Get_Glyph(GlyphSlot, &GlyphCopy);
if (error)
{
@@ -2455,13 +2647,10 @@
NewEntry->mxWorldToDevice = *pmx;
InsertHeadList(&FontCacheListHead, &NewEntry->ListEntry);
- 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--;
+ if (++FontCacheNumEntries > MAX_FONT_CACHE)
+ {
+ NewEntry = CONTAINING_RECORD(FontCacheListHead.Blink, FONT_CACHE_ENTRY,
ListEntry);
+ RemoveCachedEntry(NewEntry);
}
return BitmapGlyph;
@@ -4116,7 +4305,7 @@
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;
Modified: trunk/reactos/win32ss/gdi/ntgdi/init.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/win32ss/gdi/ntgdi/init.c?r…
==============================================================================
--- trunk/reactos/win32ss/gdi/ntgdi/init.c [iso-8859-1] (original)
+++ trunk/reactos/win32ss/gdi/ntgdi/init.c [iso-8859-1] Fri Apr 14 18:22:57 2017
@@ -50,6 +50,8 @@
ASSERT(ppiCurrent);
ASSERT(ppiCurrent->peProcess == Process);
+ IntGdiCleanupPrivateFontsForProcess();
+
/* And GDI ones too */
GDI_CleanupForProcess(Process);
Modified: trunk/reactos/win32ss/gdi/ntgdi/text.h
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/win32ss/gdi/ntgdi/text.h?r…
==============================================================================
--- trunk/reactos/win32ss/gdi/ntgdi/text.h [iso-8859-1] (original)
+++ trunk/reactos/win32ss/gdi/ntgdi/text.h [iso-8859-1] Fri Apr 14 18:22:57 2017
@@ -112,6 +112,7 @@
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);
Modified: trunk/rostests/apitests/gdi32/AddFontMemResourceEx.c
URL:
http://svn.reactos.org/svn/reactos/trunk/rostests/apitests/gdi32/AddFontMem…
==============================================================================
--- trunk/rostests/apitests/gdi32/AddFontMemResourceEx.c [iso-8859-1] (original)
+++ trunk/rostests/apitests/gdi32/AddFontMemResourceEx.c [iso-8859-1] Fri Apr 14 18:22:57
2017
@@ -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,101 +14,259 @@
#include <wingdi.h>
#include <winuser.h>
-
-static void test_font_caps(HDC hdc)
+typedef struct _fnt_res
+{
+ const char* FontName;
+ TEXTMETRICA tm;
+} fnt_res;
+
+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"));
-
- 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");
+ HFONT font;
+ int n;
+ const char* test_name = test_data[test_index].ResourceName;
+
+ for (n = 0; test_data[test_index].res[n].FontName; ++n)
+ {
+ 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);
+
+ if (font)
+ {
+ 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 @@
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 @@
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");
-
- if (is_font_available_broken || !is_font_available(hdc,
"PanosePitchTest"))
- {
+ ok(!is_font_available_broken, "Validating font is broken!
(CORE-13053)!\n");
+
+ 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 @@
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);
}
Added: trunk/rostests/apitests/gdi32/Shadows_Into_Light.ttf
URL:
http://svn.reactos.org/svn/reactos/trunk/rostests/apitests/gdi32/Shadows_In…
==============================================================================
Binary file - no diff available.
Propchange: trunk/rostests/apitests/gdi32/Shadows_Into_Light.ttf
------------------------------------------------------------------------------
svn:mime-type = application/octet-stream
Added: trunk/rostests/apitests/gdi32/TTCTestV.ttc
URL:
http://svn.reactos.org/svn/reactos/trunk/rostests/apitests/gdi32/TTCTestV.t…
==============================================================================
Binary file - no diff available.
Propchange: trunk/rostests/apitests/gdi32/TTCTestV.ttc
------------------------------------------------------------------------------
svn:mime-type = application/octet-stream
Modified: trunk/rostests/apitests/gdi32/resource.rc
URL:
http://svn.reactos.org/svn/reactos/trunk/rostests/apitests/gdi32/resource.r…
==============================================================================
--- trunk/rostests/apitests/gdi32/resource.rc [iso-8859-1] (original)
+++ trunk/rostests/apitests/gdi32/resource.rc [iso-8859-1] Fri Apr 14 18:22:57 2017
@@ -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