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?re... ============================================================================== --- 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?re... ============================================================================== --- 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?re... ============================================================================== --- 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/AddFontMemR... ============================================================================== --- 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_Int... ============================================================================== 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.tt... ============================================================================== 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.rc... ============================================================================== --- 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