Index: reactos/win32ss/gdi/eng/engobjects.h =================================================================== --- reactos/win32ss/gdi/eng/engobjects.h (revision 74270) +++ reactos/win32ss/gdi/eng/engobjects.h (working copy) @@ -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 { Index: reactos/win32ss/gdi/ntgdi/font.h =================================================================== --- reactos/win32ss/gdi/ntgdi/font.h (revision 74285) +++ reactos/win32ss/gdi/ntgdi/font.h (working copy) @@ -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; Index: reactos/win32ss/gdi/ntgdi/freetype.c =================================================================== --- reactos/win32ss/gdi/ntgdi/freetype.c (revision 74286) +++ reactos/win32ss/gdi/ntgdi/freetype.c (working copy) @@ -58,40 +58,7 @@ 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 */ static PFAST_MUTEX FreeTypeLock; @@ -106,6 +73,9 @@ #define IntUnLockGlobalFonts \ ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(FontListLock) +#define ASSERT_GLOBALFONTS_LOCK_HELD() \ + ASSERT(FreeTypeLock->Owner == KeGetCurrentThread()) + #define IntLockFreeType \ ExEnterCriticalRegionAndAcquireFastMutexUnsafe(FreeTypeLock) @@ -112,6 +82,9 @@ #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 @@ /* 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 */ @@ -675,10 +771,8 @@ FT_Face Face; ANSI_STRING AnsiFaceName; FT_WinFNT_HeaderRec WinFNT; - INT FontCount = 0, CharSetCount = 0; + INT FaceCount = 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,23 +787,28 @@ 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 */ } } @@ -716,7 +815,9 @@ else { Face = SharedFace->Face; + IntLockFreeType; SharedFace_AddRef(SharedFace); + IntUnLockFreeType; } /* allocate a FONT_ENTRY */ @@ -870,7 +971,7 @@ FontGDI->CharSet = SYMBOL_CHARSET; } - ++FontCount; + ++FaceCount; DPRINT("Font loaded: %s (%s)\n", Face->family_name, Face->style_name); DPRINT("Num glyphs: %d\n", Face->num_glyphs); DPRINT("CharSet: %d\n", FontGDI->CharSet); @@ -905,7 +1006,7 @@ FT_Long i; for (i = 1; i < TrueType->ttc_header.count; ++i) { - FontCount += IntGdiLoadFontsFromMemory(pLoadFont, NULL, i, -1); + FaceCount += IntGdiLoadFontsFromMemory(pLoadFont, NULL, i, -1); } } } @@ -941,11 +1042,12 @@ for (i = 1; i < CharSetCount; ++i) { - FontCount += IntGdiLoadFontsFromMemory(pLoadFont, SharedFace, FontIndex, i); + /* Do not count charsets towards 'faces' loaded */ + IntGdiLoadFontsFromMemory(pLoadFont, SharedFace, FontIndex, i); } } - return FontCount; /* number of loaded fonts */ + return FaceCount; /* number of loaded faces */ } /* @@ -1006,8 +1108,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; @@ -1016,6 +1117,11 @@ ObDereferenceObject(SectionObject); + /* Release our copy */ + IntLockFreeType; + SharedMem_Release(LoadFont.Memory); + IntUnLockFreeType; + if (FontCount > 0) { if (LoadFont.IsTrueType) @@ -1067,30 +1173,34 @@ { GDI_LOAD_FONT LoadFont; FONT_ENTRY_COLL_MEM* EntryCollection; - INT FontCount; + INT FaceCount; 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; LoadFont.PrivateEntry = NULL; - FontCount = IntGdiLoadFontsFromMemory(&LoadFont, NULL, -1, -1); + FaceCount = IntGdiLoadFontsFromMemory(&LoadFont, NULL, -1, -1); RtlFreeUnicodeString(&LoadFont.RegValueName); - *pNumAdded = FontCount; - if (FontCount > 0) + /* Release our copy */ + IntLockFreeType; + SharedMem_Release(LoadFont.Memory); + IntUnLockFreeType; + + if (FaceCount > 0) { EntryCollection = ExAllocatePoolWithTag(PagedPool, sizeof(FONT_ENTRY_COLL_MEM), TAG_FONT); if (EntryCollection) @@ -1104,6 +1214,7 @@ Ret = (HANDLE)EntryCollection->Handle; } } + *pNumAdded = FaceCount; return Ret; } @@ -1110,6 +1221,20 @@ // 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 +1246,31 @@ 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 +1288,7 @@ if (CurrentEntry->Handle == (UINT)hMMFont) { EntryCollection = CurrentEntry; - RemoveEntryList(Entry); + UnlinkFontMemCollection(CurrentEntry); break; } @@ -1164,6 +1306,52 @@ } +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 +1925,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 +1962,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 +2522,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 +2603,8 @@ FT_Bitmap AlignedBitmap; FT_BitmapGlyph BitmapGlyph; + ASSERT_FREETYPE_LOCK_HELD(); + error = FT_Get_Glyph(GlyphSlot, &GlyphCopy); if (error) { @@ -2455,13 +2648,10 @@ 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 +4306,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; Index: reactos/win32ss/gdi/ntgdi/init.c =================================================================== --- reactos/win32ss/gdi/ntgdi/init.c (revision 74285) +++ reactos/win32ss/gdi/ntgdi/init.c (working copy) @@ -50,6 +50,8 @@ ASSERT(ppiCurrent); ASSERT(ppiCurrent->peProcess == Process); + IntGdiCleanupPrivateFontsForProcess(); + /* And GDI ones too */ GDI_CleanupForProcess(Process); Index: reactos/win32ss/gdi/ntgdi/text.h =================================================================== --- reactos/win32ss/gdi/ntgdi/text.h (revision 74285) +++ reactos/win32ss/gdi/ntgdi/text.h (working copy) @@ -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); Index: rostests/apitests/gdi32/AddFontMemResourceEx.c =================================================================== --- rostests/apitests/gdi32/AddFontMemResourceEx.c (revision 74287) +++ rostests/apitests/gdi32/AddFontMemResourceEx.c (working copy) @@ -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; -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); - 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); + memset(&tm, 0xaa, sizeof(tm)); + ret = GetTextMetricsA(hdc, &tm); + ok(ret, "GetTextMetricsA() for %s/%s\n", test_name, res->FontName); - SelectObject(hdc, old); - DeleteObject(font); - } + 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); + } - 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")); + 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); - 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"); + 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 @@ 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"); + 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,20 +326,20 @@ 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); } } @@ -182,10 +346,7 @@ UnlockResource(hTemplate); FreeResource(hTemplate); } - else - { - skip("Font PanosePitchTest already available\n"); - } + DeleteDC(hdc); } Index: rostests/apitests/gdi32/resource.rc =================================================================== --- rostests/apitests/gdi32/resource.rc (revision 74287) +++ rostests/apitests/gdi32/resource.rc (working copy) @@ -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 Index: rostests/apitests/gdi32/Shadows_Into_Light.ttf =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Index: rostests/apitests/gdi32/Shadows_Into_Light.ttf =================================================================== --- rostests/apitests/gdi32/Shadows_Into_Light.ttf (nonexistent) +++ rostests/apitests/gdi32/Shadows_Into_Light.ttf (working copy) Property changes on: rostests/apitests/gdi32/Shadows_Into_Light.ttf ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +application/octet-stream \ No newline at end of property Index: rostests/apitests/gdi32/TTCTestV.ttc =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Index: rostests/apitests/gdi32/TTCTestV.ttc =================================================================== --- rostests/apitests/gdi32/TTCTestV.ttc (nonexistent) +++ rostests/apitests/gdi32/TTCTestV.ttc (working copy) Property changes on: rostests/apitests/gdi32/TTCTestV.ttc ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +application/octet-stream \ No newline at end of property