From 9be0765be11dc21ccd011729ec0640c7e049524e Mon Sep 17 00:00:00 2001 From: Joachim Henze Date: Thu, 31 Dec 2020 22:10:01 +0100 Subject: [PATCH] [0.4.9][WIN32SS] Fix regression CORE-15558 Font enumeration Fixes CORE-15558 "AbiWord font listbox should display a specific font only once, but displays each of them many times instead" which regressed by SVN r75491 == 0.4.7-dev-32-g d10c0ec1123111f6d31f9447c88234dd7255e07c by porting back the commits: 0.4.12-dev-320-g 6e4e5a004c9da6276695d90187fb577812e8892f and 0.4.13-dev-107-g ae8417fd9068dd32ec180636117bba1dc0603325 to prevent introducing another regression CORE_15785 with the initial fix. and some more addendums to prevent introducing regression CORE_15755 I verified afterwards that all 3 issues: CORE-15558, CORE_15785 and CORE_15755 are in fixed state. Actually I don't really like the resulting code due to the qsort() that it introduces into win32ss/gdi/gdi32/objects/font.c We did not need that at all before 0.4.7-dev-32-g d10c0ec1123111f6d31f9447c88234dd7255e07c In case that qsort() would ever reveal any new trouble like reduced performance or stack overflow, we should structurally revert all that context to 0.4.7-dev-31 instead. IntEnumFontFamilies() is called immediately upon process initialization for many applications and the qsorted-list is *huge*, so this indeed could be relevant for performance. --- win32ss/gdi/gdi32/include/precomp.h | 1 + win32ss/gdi/gdi32/objects/font.c | 146 +++++++++-- win32ss/gdi/ntgdi/freetype.c | 387 +++++++++++++--------------- win32ss/include/ntgdibad.h | 7 +- 4 files changed, 307 insertions(+), 234 deletions(-) diff --git a/win32ss/gdi/gdi32/include/precomp.h b/win32ss/gdi/gdi32/include/precomp.h index 5f8614b3a92..3d4b823c2fe 100644 --- a/win32ss/gdi/gdi32/include/precomp.h +++ b/win32ss/gdi/gdi32/include/precomp.h @@ -58,5 +58,6 @@ #include #include +#include #endif /* _GDI32_PCH_ */ diff --git a/win32ss/gdi/gdi32/objects/font.c b/win32ss/gdi/gdi32/objects/font.c index 56998ab84bc..9d80d06a9f2 100644 --- a/win32ss/gdi/gdi32/objects/font.c +++ b/win32ss/gdi/gdi32/objects/font.c @@ -204,61 +204,162 @@ NewTextMetricExW2A(NEWTEXTMETRICEXA *tma, NEWTEXTMETRICEXW *tmw) tma->ntmFontSig = tmw->ntmFontSig; } +// IntFontFamilyCompareEx's flags +#define IFFCX_CHARSET 1 +#define IFFCX_STYLE 2 + +FORCEINLINE int FASTCALL +IntFontFamilyCompareEx(const FONTFAMILYINFO *ffi1, + const FONTFAMILYINFO *ffi2, DWORD dwCompareFlags) +{ + const LOGFONTW *plf1 = &ffi1->EnumLogFontEx.elfLogFont; + const LOGFONTW *plf2 = &ffi2->EnumLogFontEx.elfLogFont; + ULONG WeightDiff1, WeightDiff2; + int cmp = _wcsicmp(plf1->lfFaceName, plf2->lfFaceName); + if (cmp) + return cmp; + if (dwCompareFlags & IFFCX_CHARSET) + { + if (plf1->lfCharSet < plf2->lfCharSet) + return -1; + if (plf1->lfCharSet > plf2->lfCharSet) + return 1; + } + if (dwCompareFlags & IFFCX_STYLE) + { + WeightDiff1 = labs(plf1->lfWeight - FW_NORMAL); + WeightDiff2 = labs(plf2->lfWeight - FW_NORMAL); + if (WeightDiff1 < WeightDiff2) + return -1; + if (WeightDiff1 > WeightDiff2) + return 1; + if (plf1->lfItalic < plf2->lfItalic) + return -1; + if (plf1->lfItalic > plf2->lfItalic) + return 1; + } + return 0; +} + +static int __cdecl +IntFontFamilyCompare(const void *ffi1, const void *ffi2) +{ + return IntFontFamilyCompareEx(ffi1, ffi2, IFFCX_STYLE | IFFCX_CHARSET); +} + +// IntEnumFontFamilies' flags: +#define IEFF_UNICODE 1 +#define IEFF_EXTENDED 2 + +int FASTCALL +IntFontFamilyListUnique(FONTFAMILYINFO *InfoList, INT nCount, + const LOGFONTW *plf, DWORD dwFlags) +{ + FONTFAMILYINFO *first, *last, *result; + DWORD dwCompareFlags = 0; + + if (plf->lfFaceName[0]) + dwCompareFlags |= IFFCX_STYLE; + + if ((dwFlags & IEFF_EXTENDED) && plf->lfCharSet == DEFAULT_CHARSET) + dwCompareFlags |= IFFCX_CHARSET; + + first = InfoList; + last = &InfoList[nCount]; + + /* std::unique(first, last, IntFontFamilyCompareEx); */ + if (first == last) + return 0; + + result = first; + while (++first != last) + { + if (IntFontFamilyCompareEx(result, first, dwCompareFlags) != 0) + { + *(++result) = *first; + } + } + nCount = (int)(++result - InfoList); + + return nCount; +} + static int FASTCALL -IntEnumFontFamilies(HDC Dc, LPLOGFONTW LogFont, PVOID EnumProc, LPARAM lParam, - BOOL Unicode) +IntEnumFontFamilies(HDC Dc, const LOGFONTW *LogFont, PVOID EnumProc, LPARAM lParam, + DWORD dwFlags) { int FontFamilyCount; - int FontFamilySize; PFONTFAMILYINFO Info; int Ret = 1; int i; ENUMLOGFONTEXA EnumLogFontExA; NEWTEXTMETRICEXA NewTextMetricExA; LOGFONTW lfW; + LONG InfoCount; + ULONG DataSize; + NTSTATUS Status; - Info = RtlAllocateHeap(GetProcessHeap(), 0, - INITIAL_FAMILY_COUNT * sizeof(FONTFAMILYINFO)); - if (NULL == Info) + DataSize = INITIAL_FAMILY_COUNT * sizeof(FONTFAMILYINFO); + Info = RtlAllocateHeap(GetProcessHeap(), 0, DataSize); + if (Info == NULL) { return 1; } + /* Initialize the LOGFONT structure */ + ZeroMemory(&lfW, sizeof(lfW)); if (!LogFont) { lfW.lfCharSet = DEFAULT_CHARSET; - lfW.lfPitchAndFamily = 0; - lfW.lfFaceName[0] = 0; - LogFont = &lfW; + } + else + { + lfW.lfCharSet = LogFont->lfCharSet; + lfW.lfPitchAndFamily = LogFont->lfPitchAndFamily; + StringCbCopyW(lfW.lfFaceName, sizeof(lfW.lfFaceName), LogFont->lfFaceName); } - FontFamilyCount = NtGdiGetFontFamilyInfo(Dc, LogFont, Info, INITIAL_FAMILY_COUNT); + /* Retrieve the font information */ + InfoCount = INITIAL_FAMILY_COUNT; + FontFamilyCount = NtGdiGetFontFamilyInfo(Dc, &lfW, Info, &InfoCount); if (FontFamilyCount < 0) { RtlFreeHeap(GetProcessHeap(), 0, Info); return 1; } - if (INITIAL_FAMILY_COUNT < FontFamilyCount) + + /* Resize the buffer if the buffer is too small */ + if (INITIAL_FAMILY_COUNT < InfoCount) { - FontFamilySize = FontFamilyCount; RtlFreeHeap(GetProcessHeap(), 0, Info); - Info = RtlAllocateHeap(GetProcessHeap(), 0, - FontFamilyCount * sizeof(FONTFAMILYINFO)); - if (NULL == Info) + + Status = RtlULongMult(InfoCount, sizeof(FONTFAMILYINFO), &DataSize); + if (!NT_SUCCESS(Status) || DataSize > LONG_MAX) + { + DPRINT1("Overflowed.\n"); + return 1; + } + Info = RtlAllocateHeap(GetProcessHeap(), 0, DataSize); + if (Info == NULL) { return 1; } - FontFamilyCount = NtGdiGetFontFamilyInfo(Dc, LogFont, Info, FontFamilySize); - if (FontFamilyCount < 0 || FontFamilySize < FontFamilyCount) + FontFamilyCount = NtGdiGetFontFamilyInfo(Dc, &lfW, Info, &InfoCount); + if (FontFamilyCount < 0 || FontFamilyCount < InfoCount) { RtlFreeHeap(GetProcessHeap(), 0, Info); return 1; } } + /* Sort and remove redundant information */ + qsort(Info, FontFamilyCount, sizeof(*Info), IntFontFamilyCompare); + FontFamilyCount = IntFontFamilyListUnique(Info, FontFamilyCount, &lfW, dwFlags); + + /* call the callback */ for (i = 0; i < FontFamilyCount; i++) { - if (Unicode) + if (dwFlags & IEFF_UNICODE) { Ret = ((FONTENUMPROCW) EnumProc)( (VOID*)&Info[i].EnumLogFontEx, @@ -299,7 +400,8 @@ int WINAPI EnumFontFamiliesExW(HDC hdc, LPLOGFONTW lpLogfont, FONTENUMPROCW lpEnumFontFamExProc, LPARAM lParam, DWORD dwFlags) { - return IntEnumFontFamilies(hdc, lpLogfont, lpEnumFontFamExProc, lParam, TRUE); + return IntEnumFontFamilies(hdc, lpLogfont, lpEnumFontFamExProc, lParam, + IEFF_UNICODE | IEFF_EXTENDED); } @@ -320,7 +422,7 @@ EnumFontFamiliesW(HDC hdc, LPCWSTR lpszFamily, FONTENUMPROCW lpEnumFontFamProc, lstrcpynW(LogFont.lfFaceName, lpszFamily, LF_FACESIZE); } - return IntEnumFontFamilies(hdc, &LogFont, lpEnumFontFamProc, lParam, TRUE); + return IntEnumFontFamilies(hdc, &LogFont, lpEnumFontFamProc, lParam, IEFF_UNICODE); } @@ -341,7 +443,7 @@ EnumFontFamiliesExA (HDC hdc, LPLOGFONTA lpLogfont, FONTENUMPROCA lpEnumFontFamE else pLogFontW = NULL; /* no need to convert LogFontW back to lpLogFont b/c it's an [in] parameter only */ - return IntEnumFontFamilies(hdc, pLogFontW, lpEnumFontFamExProc, lParam, FALSE); + return IntEnumFontFamilies(hdc, pLogFontW, lpEnumFontFamExProc, lParam, IEFF_EXTENDED); } @@ -362,7 +464,7 @@ EnumFontFamiliesA(HDC hdc, LPCSTR lpszFamily, FONTENUMPROCA lpEnumFontFamProc, MultiByteToWideChar(CP_THREAD_ACP, 0, lpszFamily, -1, LogFont.lfFaceName, LF_FACESIZE); } - return IntEnumFontFamilies(hdc, &LogFont, lpEnumFontFamProc, lParam, FALSE); + return IntEnumFontFamilies(hdc, &LogFont, lpEnumFontFamProc, lParam, 0); } diff --git a/win32ss/gdi/ntgdi/freetype.c b/win32ss/gdi/ntgdi/freetype.c index 569951a48ee..567884a3c29 100644 --- a/win32ss/gdi/ntgdi/freetype.c +++ b/win32ss/gdi/ntgdi/freetype.c @@ -577,6 +577,21 @@ SubstituteFontByList(PLIST_ENTRY pHead, return FALSE; } +static VOID +IntUnicodeStringToBuffer(LPWSTR pszBuffer, USHORT cbBuffer, const UNICODE_STRING *pString) +{ + USHORT cbLength = pString->Length; + + if (cbBuffer < sizeof(UNICODE_NULL)) + return; + + if (cbLength > cbBuffer - sizeof(UNICODE_NULL)) + cbLength = cbBuffer - sizeof(UNICODE_NULL); + + RtlCopyMemory(pszBuffer, pString->Buffer, cbLength); + pszBuffer[cbLength / sizeof(WCHAR)] = UNICODE_NULL; +} + static BOOL SubstituteFontRecurse(LOGFONTW* pLogFont) { @@ -599,7 +614,7 @@ SubstituteFontRecurse(LOGFONTW* pLogFont) if (!Found) break; - RtlStringCchCopyW(pLogFont->lfFaceName, LF_FACESIZE, OutputNameW.Buffer); + IntUnicodeStringToBuffer(pLogFont->lfFaceName, sizeof(pLogFont->lfFaceName), &OutputNameW); if (CharSetMap[FONTSUBST_FROM] == DEFAULT_CHARSET || CharSetMap[FONTSUBST_FROM] == pLogFont->lfCharSet) @@ -870,6 +885,7 @@ IntGdiLoadFontsFromMemory(PGDI_LOAD_FONT pLoadFont, EngSetLastError(ERROR_NOT_ENOUGH_MEMORY); return 0; /* failure */ } + RtlCopyMemory(FontGDI->Filename, pFileName->Buffer, pFileName->Length); FontGDI->Filename[pFileName->Length / sizeof(WCHAR)] = UNICODE_NULL; } @@ -1966,75 +1982,6 @@ IntGetOutlineTextMetrics(PFONTGDI FontGDI, return Cache->OutlineRequiredSize; } -static PFONTGDI FASTCALL -FindFaceNameInList(PUNICODE_STRING FaceName, PLIST_ENTRY Head) -{ - PLIST_ENTRY Entry; - PFONT_ENTRY CurrentEntry; - ANSI_STRING EntryFaceNameA; - UNICODE_STRING EntryFaceNameW; - FONTGDI *FontGDI; - NTSTATUS status; - - Entry = Head->Flink; - while (Entry != Head) - { - CurrentEntry = CONTAINING_RECORD(Entry, FONT_ENTRY, ListEntry); - - FontGDI = CurrentEntry->Font; - ASSERT(FontGDI); - - RtlInitAnsiString(&EntryFaceNameA, FontGDI->SharedFace->Face->family_name); - status = RtlAnsiStringToUnicodeString(&EntryFaceNameW, &EntryFaceNameA, TRUE); - if (!NT_SUCCESS(status)) - { - break; - } - - if ((LF_FACESIZE - 1) * sizeof(WCHAR) < EntryFaceNameW.Length) - { - EntryFaceNameW.Length = (LF_FACESIZE - 1) * sizeof(WCHAR); - EntryFaceNameW.Buffer[LF_FACESIZE - 1] = L'\0'; - } - - if (RtlEqualUnicodeString(FaceName, &EntryFaceNameW, TRUE)) - { - RtlFreeUnicodeString(&EntryFaceNameW); - return FontGDI; - } - - RtlFreeUnicodeString(&EntryFaceNameW); - Entry = Entry->Flink; - } - - return NULL; -} - -static PFONTGDI FASTCALL -FindFaceNameInLists(PUNICODE_STRING FaceName) -{ - PPROCESSINFO Win32Process; - PFONTGDI Font; - - /* 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); - IntUnLockProcessPrivateFonts(Win32Process); - if (NULL != Font) - { - return Font; - } - - /* Search the global list */ - IntLockGlobalFonts; - Font = FindFaceNameInList(FaceName, &FontListHead); - IntUnLockGlobalFonts; - - return Font; -} - /* See https://msdn.microsoft.com/en-us/library/bb165625(v=vs.90).aspx */ static BYTE CharSetFromLangID(LANGID LangID) @@ -2459,76 +2406,19 @@ FontFamilyFillInfo(PFONTFAMILYINFO Info, LPCWSTR FaceName, Info->NewTextMetricEx.ntmFontSig = fs; } -static int FASTCALL -FindFaceNameInInfo(PUNICODE_STRING FaceName, PFONTFAMILYINFO Info, DWORD InfoEntries) -{ - DWORD i; - UNICODE_STRING InfoFaceName; - - for (i = 0; i < InfoEntries; i++) - { - RtlInitUnicodeString(&InfoFaceName, Info[i].EnumLogFontEx.elfLogFont.lfFaceName); - if (RtlEqualUnicodeString(&InfoFaceName, FaceName, TRUE)) - { - return i; - } - } - - return -1; -} - static BOOLEAN FASTCALL -FontFamilyInclude(LPLOGFONTW LogFont, PUNICODE_STRING FaceName, - PFONTFAMILYINFO Info, DWORD InfoEntries) -{ - UNICODE_STRING LogFontFaceName; - - RtlInitUnicodeString(&LogFontFaceName, LogFont->lfFaceName); - if (0 != LogFontFaceName.Length && - !RtlEqualUnicodeString(&LogFontFaceName, FaceName, TRUE)) - { - return FALSE; - } - - return FindFaceNameInInfo(FaceName, Info, InfoEntries) < 0; -} - -static BOOL FASTCALL -FontFamilyFound(PFONTFAMILYINFO InfoEntry, - PFONTFAMILYINFO Info, DWORD InfoCount) -{ - LPLOGFONTW plf1 = &InfoEntry->EnumLogFontEx.elfLogFont; - LPWSTR pFullName1 = InfoEntry->EnumLogFontEx.elfFullName; - LPWSTR pFullName2; - DWORD i; - - for (i = 0; i < InfoCount; ++i) - { - LPLOGFONTW plf2 = &Info[i].EnumLogFontEx.elfLogFont; - if (plf1->lfCharSet != plf2->lfCharSet) - continue; - - pFullName2 = Info[i].EnumLogFontEx.elfFullName; - if (_wcsicmp(pFullName1, pFullName2) != 0) - continue; - - return TRUE; - } - return FALSE; -} - -static BOOLEAN FASTCALL -GetFontFamilyInfoForList(LPLOGFONTW LogFont, +GetFontFamilyInfoForList(const LOGFONTW *LogFont, PFONTFAMILYINFO Info, - DWORD *pCount, - DWORD MaxCount, + LPCWSTR NominalName, + LONG *pCount, + LONG MaxCount, PLIST_ENTRY Head) { PLIST_ENTRY Entry; PFONT_ENTRY CurrentEntry; FONTGDI *FontGDI; FONTFAMILYINFO InfoEntry; - DWORD Count = *pCount; + LONG Count = *pCount; for (Entry = Head->Flink; Entry != Head; Entry = Entry->Flink) { @@ -2539,35 +2429,40 @@ GetFontFamilyInfoForList(LPLOGFONTW LogFont, if (LogFont->lfCharSet != DEFAULT_CHARSET && LogFont->lfCharSet != FontGDI->CharSet) { - continue; - } - - if (LogFont->lfFaceName[0] == UNICODE_NULL) - { - if (Count < MaxCount) - { - FontFamilyFillInfo(&Info[Count], NULL, NULL, FontGDI); - } - Count++; - continue; + continue; /* charset mismatch */ } + /* get one info entry */ FontFamilyFillInfo(&InfoEntry, NULL, NULL, FontGDI); - if (_wcsnicmp(LogFont->lfFaceName, InfoEntry.EnumLogFontEx.elfLogFont.lfFaceName, RTL_NUMBER_OF(LogFont->lfFaceName)-1) != 0 && - _wcsnicmp(LogFont->lfFaceName, InfoEntry.EnumLogFontEx.elfFullName, RTL_NUMBER_OF(LogFont->lfFaceName)-1) != 0) + if (LogFont->lfFaceName[0] != UNICODE_NULL) { - continue; + /* check name */ + if (_wcsnicmp(LogFont->lfFaceName, + InfoEntry.EnumLogFontEx.elfLogFont.lfFaceName, + RTL_NUMBER_OF(LogFont->lfFaceName) - 1) != 0 && + _wcsnicmp(LogFont->lfFaceName, + InfoEntry.EnumLogFontEx.elfFullName, + RTL_NUMBER_OF(LogFont->lfFaceName) - 1) != 0) + { + continue; + } } - if (!FontFamilyFound(&InfoEntry, Info, min(Count, MaxCount))) + if (NominalName) { - if (Count < MaxCount) - { - RtlCopyMemory(&Info[Count], &InfoEntry, sizeof(InfoEntry)); - } - Count++; + /* store the nominal name */ + RtlStringCbCopyW(InfoEntry.EnumLogFontEx.elfLogFont.lfFaceName, + sizeof(InfoEntry.EnumLogFontEx.elfLogFont.lfFaceName), + NominalName); } + + /* store one entry to Info */ + if (0 <= Count && Count < MaxCount) + { + RtlCopyMemory(&Info[Count], &InfoEntry, sizeof(InfoEntry)); + } + Count++; } *pCount = Count; @@ -2576,17 +2471,16 @@ GetFontFamilyInfoForList(LPLOGFONTW LogFont, } static BOOLEAN FASTCALL -GetFontFamilyInfoForSubstitutes(LPLOGFONTW LogFont, +GetFontFamilyInfoForSubstitutes(const LOGFONTW *LogFont, PFONTFAMILYINFO Info, - DWORD *pCount, - DWORD MaxCount) + LONG *pCount, + LONG MaxCount) { PLIST_ENTRY pEntry, pHead = &FontSubstListHead; PFONTSUBST_ENTRY pCurrentEntry; - PUNICODE_STRING pFromW; - FONTGDI *FontGDI; + PUNICODE_STRING pFromW, pToW; LOGFONTW lf = *LogFont; - UNICODE_STRING NameW; + PPROCESSINFO Win32Process = PsGetCurrentProcessWin32Process(); for (pEntry = pHead->Flink; pEntry != pHead; pEntry = pEntry->Flink) { @@ -2595,25 +2489,43 @@ GetFontFamilyInfoForSubstitutes(LPLOGFONTW LogFont, pFromW = &pCurrentEntry->FontNames[FONTSUBST_FROM]; if (LogFont->lfFaceName[0] != UNICODE_NULL) { - if (!FontFamilyInclude(LogFont, pFromW, Info, min(*pCount, MaxCount))) + /* check name */ + if (_wcsicmp(LogFont->lfFaceName, pFromW->Buffer) != 0) continue; /* mismatch */ } - RtlStringCchCopyW(lf.lfFaceName, LF_FACESIZE, pFromW->Buffer); + pToW = &pCurrentEntry->FontNames[FONTSUBST_TO]; + if (RtlEqualUnicodeString(pFromW, pToW, TRUE) && + pCurrentEntry->CharSets[FONTSUBST_FROM] == + pCurrentEntry->CharSets[FONTSUBST_TO]) + { + /* identical mapping */ + continue; + } + + /* substitute and get the real name */ + IntUnicodeStringToBuffer(lf.lfFaceName, sizeof(lf.lfFaceName), pFromW); SubstituteFontRecurse(&lf); + if (LogFont->lfCharSet != DEFAULT_CHARSET && LogFont->lfCharSet != lf.lfCharSet) + continue; - RtlInitUnicodeString(&NameW, lf.lfFaceName); - FontGDI = FindFaceNameInLists(&NameW); - if (FontGDI == NULL) - { - continue; /* no real font */ - } + /* search in global fonts */ + IntLockGlobalFonts; + GetFontFamilyInfoForList(&lf, Info, pFromW->Buffer, pCount, MaxCount, &FontListHead); + IntUnLockGlobalFonts; - if (*pCount < MaxCount) + /* search in private fonts */ + IntLockProcessPrivateFonts(Win32Process); + GetFontFamilyInfoForList(&lf, Info, pFromW->Buffer, pCount, MaxCount, + &Win32Process->PrivateFontListHead); + IntUnLockProcessPrivateFonts(Win32Process); + + if (LogFont->lfFaceName[0]) { - FontFamilyFillInfo(&Info[*pCount], pFromW->Buffer, NULL, FontGDI); + /* it's already matched to the exact name and charset if the name + was specified at here, then so don't scan more for another name */ + break; } - (*pCount)++; } return TRUE; @@ -5004,41 +4916,22 @@ ftGdiGetKerningPairs( PFONTGDI Font, // Functions needing sorting. // /////////////////////////////////////////////////////////////////////////// -int APIENTRY -NtGdiGetFontFamilyInfo(HDC Dc, - LPLOGFONTW UnsafeLogFont, - PFONTFAMILYINFO UnsafeInfo, - DWORD Size) + +LONG FASTCALL +IntGetFontFamilyInfo(HDC Dc, + const LOGFONTW *SafeLogFont, + PFONTFAMILYINFO SafeInfo, + LONG InfoCount) { - NTSTATUS Status; - LOGFONTW LogFont; - PFONTFAMILYINFO Info; - DWORD Count; + LONG AvailCount = 0; PPROCESSINFO Win32Process; - /* Make a safe copy */ - Status = MmCopyFromCaller(&LogFont, UnsafeLogFont, sizeof(LOGFONTW)); - if (! NT_SUCCESS(Status)) - { - EngSetLastError(ERROR_INVALID_PARAMETER); - return -1; - } - - /* Allocate space for a safe copy */ - Info = ExAllocatePoolWithTag(PagedPool, Size * sizeof(FONTFAMILYINFO), GDITAG_TEXT); - if (NULL == Info) - { - EngSetLastError(ERROR_NOT_ENOUGH_MEMORY); - return -1; - } - /* Enumerate font families in the global list */ IntLockGlobalFonts; - Count = 0; - if (! GetFontFamilyInfoForList(&LogFont, Info, &Count, Size, &FontListHead) ) + if (!GetFontFamilyInfoForList(SafeLogFont, SafeInfo, NULL, &AvailCount, + InfoCount, &FontListHead)) { IntUnLockGlobalFonts; - ExFreePoolWithTag(Info, GDITAG_TEXT); return -1; } IntUnLockGlobalFonts; @@ -5046,28 +4939,106 @@ NtGdiGetFontFamilyInfo(HDC Dc, /* Enumerate font families in the process local list */ Win32Process = PsGetCurrentProcessWin32Process(); IntLockProcessPrivateFonts(Win32Process); - if (! GetFontFamilyInfoForList(&LogFont, Info, &Count, Size, - &Win32Process->PrivateFontListHead)) + if (!GetFontFamilyInfoForList(SafeLogFont, SafeInfo, NULL, &AvailCount, InfoCount, + &Win32Process->PrivateFontListHead)) { IntUnLockProcessPrivateFonts(Win32Process); - ExFreePoolWithTag(Info, GDITAG_TEXT); return -1; } IntUnLockProcessPrivateFonts(Win32Process); /* Enumerate font families in the registry */ - if (! GetFontFamilyInfoForSubstitutes(&LogFont, Info, &Count, Size)) + if (!GetFontFamilyInfoForSubstitutes(SafeLogFont, SafeInfo, &AvailCount, InfoCount)) { - ExFreePoolWithTag(Info, GDITAG_TEXT); return -1; } - /* Return data to caller */ - if (0 != Count) + return AvailCount; +} + +LONG NTAPI +NtGdiGetFontFamilyInfo(HDC Dc, + const LOGFONTW *UnsafeLogFont, + PFONTFAMILYINFO UnsafeInfo, + LPLONG UnsafeInfoCount) +{ + NTSTATUS Status; + LOGFONTW LogFont; + PFONTFAMILYINFO Info; + LONG GotCount, AvailCount, SafeInfoCount; + ULONG DataSize; + + if (UnsafeLogFont == NULL || UnsafeInfo == NULL || UnsafeInfoCount == NULL) { - Status = MmCopyToCaller(UnsafeInfo, Info, - (Count < Size ? Count : Size) * sizeof(FONTFAMILYINFO)); - if (! NT_SUCCESS(Status)) + EngSetLastError(ERROR_INVALID_PARAMETER); + return -1; + } + + Status = MmCopyFromCaller(&SafeInfoCount, UnsafeInfoCount, sizeof(SafeInfoCount)); + if (!NT_SUCCESS(Status)) + { + EngSetLastError(ERROR_INVALID_PARAMETER); + return -1; + } + GotCount = 0; + Status = MmCopyToCaller(UnsafeInfoCount, &GotCount, sizeof(*UnsafeInfoCount)); + if (!NT_SUCCESS(Status)) + { + EngSetLastError(ERROR_INVALID_PARAMETER); + return -1; + } + Status = MmCopyFromCaller(&LogFont, UnsafeLogFont, sizeof(LOGFONTW)); + if (!NT_SUCCESS(Status)) + { + EngSetLastError(ERROR_INVALID_PARAMETER); + return -1; + } + if (SafeInfoCount <= 0) + { + EngSetLastError(ERROR_INVALID_PARAMETER); + return -1; + } + + /* Allocate space for a safe copy */ + Status = RtlULongMult(SafeInfoCount, sizeof(FONTFAMILYINFO), &DataSize); + if (!NT_SUCCESS(Status) || DataSize > LONG_MAX) + { + DPRINT1("Overflowed.\n"); + EngSetLastError(ERROR_INVALID_PARAMETER); + return -1; + } + Info = ExAllocatePoolWithTag(PagedPool, DataSize, GDITAG_TEXT); + if (Info == NULL) + { + EngSetLastError(ERROR_NOT_ENOUGH_MEMORY); + return -1; + } + + /* Retrieve the information */ + AvailCount = IntGetFontFamilyInfo(Dc, &LogFont, Info, SafeInfoCount); + GotCount = min(AvailCount, SafeInfoCount); + SafeInfoCount = AvailCount; + + /* Return data to caller */ + if (GotCount > 0) + { + Status = RtlULongMult(GotCount, sizeof(FONTFAMILYINFO), &DataSize); + if (!NT_SUCCESS(Status) || DataSize > LONG_MAX) + { + DPRINT1("Overflowed.\n"); + ExFreePoolWithTag(Info, GDITAG_TEXT); + EngSetLastError(ERROR_INVALID_PARAMETER); + return -1; + } + Status = MmCopyToCaller(UnsafeInfo, Info, DataSize); + if (!NT_SUCCESS(Status)) + { + ExFreePoolWithTag(Info, GDITAG_TEXT); + EngSetLastError(ERROR_INVALID_PARAMETER); + return -1; + } + Status = MmCopyToCaller(UnsafeInfoCount, &SafeInfoCount, sizeof(*UnsafeInfoCount)); + if (!NT_SUCCESS(Status)) { ExFreePoolWithTag(Info, GDITAG_TEXT); EngSetLastError(ERROR_INVALID_PARAMETER); @@ -5077,7 +5048,7 @@ NtGdiGetFontFamilyInfo(HDC Dc, ExFreePoolWithTag(Info, GDITAG_TEXT); - return Count; + return GotCount; } FORCEINLINE diff --git a/win32ss/include/ntgdibad.h b/win32ss/include/ntgdibad.h index ba8588f2f96..58d644e68e9 100644 --- a/win32ss/include/ntgdibad.h +++ b/win32ss/include/ntgdibad.h @@ -50,14 +50,13 @@ typedef struct tagFONTFAMILYINFO } FONTFAMILYINFO, *PFONTFAMILYINFO; /* Should be using NtGdiEnumFontChunk */ -INT +LONG NTAPI NtGdiGetFontFamilyInfo( HDC Dc, - LPLOGFONTW LogFont, + const LOGFONTW *LogFont, PFONTFAMILYINFO Info, - DWORD Size -); + LPLONG UnsafeInfoCount); /* Use NtGdiGetDCPoint with GdiGetViewPortExt */ BOOL APIENTRY NtGdiGetViewportExtEx(HDC hDC, LPSIZE viewportExt);