From: Mark Jansen Date: Fri, 14 Apr 2017 18:22:57 +0000 (+0000) Subject: [WIN32SS] Cleanup fonts at process destruction + implement font memory reference... X-Git-Tag: ReactOS-0.4.5~124 X-Git-Url: https://git.reactos.org/?p=reactos.git;a=commitdiff_plain;h=246f24bd7f69916b171b8e12fc312e8fcd8c5ad0 [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 --- diff --git a/reactos/win32ss/gdi/eng/engobjects.h b/reactos/win32ss/gdi/eng/engobjects.h index 584ebe7e408..333b6e11bf4 100644 --- a/reactos/win32ss/gdi/eng/engobjects.h +++ b/reactos/win32ss/gdi/eng/engobjects.h @@ -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 { diff --git a/reactos/win32ss/gdi/ntgdi/font.h b/reactos/win32ss/gdi/ntgdi/font.h index 814b18d846f..0383fe5c510 100644 --- a/reactos/win32ss/gdi/ntgdi/font.h +++ b/reactos/win32ss/gdi/ntgdi/font.h @@ -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; diff --git a/reactos/win32ss/gdi/ntgdi/freetype.c b/reactos/win32ss/gdi/ntgdi/freetype.c index 14e41d5d511..b2bebd7d1cf 100644 --- a/reactos/win32ss/gdi/ntgdi/freetype.c +++ b/reactos/win32ss/gdi/ntgdi/freetype.c @@ -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; diff --git a/reactos/win32ss/gdi/ntgdi/init.c b/reactos/win32ss/gdi/ntgdi/init.c index 195fb19fd0a..35bab0f6029 100644 --- a/reactos/win32ss/gdi/ntgdi/init.c +++ b/reactos/win32ss/gdi/ntgdi/init.c @@ -50,6 +50,8 @@ GdiProcessDestroy(PEPROCESS Process) ASSERT(ppiCurrent); ASSERT(ppiCurrent->peProcess == Process); + IntGdiCleanupPrivateFontsForProcess(); + /* And GDI ones too */ GDI_CleanupForProcess(Process); diff --git a/reactos/win32ss/gdi/ntgdi/text.h b/reactos/win32ss/gdi/ntgdi/text.h index e0b7c38fcb9..b0e1e0c61f6 100644 --- a/reactos/win32ss/gdi/ntgdi/text.h +++ b/reactos/win32ss/gdi/ntgdi/text.h @@ -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); diff --git a/rostests/apitests/gdi32/AddFontMemResourceEx.c b/rostests/apitests/gdi32/AddFontMemResourceEx.c index b249998e785..d5587befcfa 100644 --- a/rostests/apitests/gdi32/AddFontMemResourceEx.c +++ b/rostests/apitests/gdi32/AddFontMemResourceEx.c @@ -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 #include +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 void test_font_caps(HDC hdc) +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); - - memset(&tm, 0xaa, sizeof(tm)); - ret = GetTextMetricsA(hdc, &tm); - ok_int(ret, TRUE); + 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); - SetLastError(0xdeadbeef); - ret = GetTextFaceA(hdc, sizeof(name), name); - ok(ret, "GetTextFaceA error %lu\n", GetLastError()); - if (ret) + if (font) { - 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); - } - - 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")); + old = SelectObject(hdc, font); - 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); - 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 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); + } - SetLastError(0xdeadbeef); - ret = GetTextFaceA(hdc, sizeof(name), name); - ok(ret, "GetTextFaceA error %lu\n", GetLastError()); - if (ret) - { - ok_str(name, "@PanosePitchTest"); + 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); } } + /* 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); } diff --git a/rostests/apitests/gdi32/Shadows_Into_Light.ttf b/rostests/apitests/gdi32/Shadows_Into_Light.ttf new file mode 100644 index 00000000000..620f7735528 Binary files /dev/null and b/rostests/apitests/gdi32/Shadows_Into_Light.ttf differ diff --git a/rostests/apitests/gdi32/TTCTestV.ttc b/rostests/apitests/gdi32/TTCTestV.ttc new file mode 100644 index 00000000000..e85ae5ee2d6 Binary files /dev/null and b/rostests/apitests/gdi32/TTCTestV.ttc differ diff --git a/rostests/apitests/gdi32/resource.rc b/rostests/apitests/gdi32/resource.rc index fbf571fdac5..71ccce92193 100644 --- a/rostests/apitests/gdi32/resource.rc +++ b/rostests/apitests/gdi32/resource.rc @@ -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