Index: reactos/win32ss/gdi/ntgdi/freetype.c =================================================================== --- reactos/win32ss/gdi/ntgdi/freetype.c (revision 74140) +++ reactos/win32ss/gdi/ntgdi/freetype.c (working copy) @@ -43,7 +43,7 @@ extern const MATRIX gmxWorldToDeviceDefault; extern const MATRIX gmxWorldToPageDefault; -// HACK!! Fix XFORMOBJ then use 1:16 / 16:1 +/* HACK!! Fix XFORMOBJ then use 1:16 / 16:1 */ #define gmxWorldToDeviceDefault gmxWorldToPageDefault FT_Library library; @@ -201,7 +201,167 @@ { SYMBOL_CHARSET, CP_SYMBOL, {{0,0,0,0},{FS_SYMBOL,0}} } }; +/* + * FONTSUBST_... --- constants for font substitutes + */ +#define FONTSUBST_FROM 0 +#define FONTSUBST_TO 1 +#define FONTSUBST_FROM_AND_TO 2 + +/* + * FONTSUBST_ENTRY --- font substitute entry + */ +typedef struct FONTSUBST_ENTRY +{ + LIST_ENTRY ListEntry; + UNICODE_STRING FontNames[FONTSUBST_FROM_AND_TO]; + BYTE CharSets[FONTSUBST_FROM_AND_TO]; +} FONTSUBST_ENTRY, *PFONTSUBST_ENTRY; + +/* list head */ +static RTL_STATIC_LIST_HEAD(FontSubstListHead); + +/* + * IntLoadFontSubstList --- loads the list of font substitutes + */ BOOL FASTCALL +IntLoadFontSubstList(PLIST_ENTRY pHead) +{ + NTSTATUS Status; + HANDLE KeyHandle; + OBJECT_ATTRIBUTES ObjectAttributes; + KEY_FULL_INFORMATION KeyFullInfo; + ULONG i, Length; + UNICODE_STRING FromW, ToW; + BYTE InfoBuffer[128]; + PKEY_VALUE_FULL_INFORMATION pInfo; + BYTE CharSets[FONTSUBST_FROM_AND_TO]; + LPWSTR pch; + PFONTSUBST_ENTRY pEntry; + + /* the FontSubstitutes registry key */ + static UNICODE_STRING FontSubstKey = + RTL_CONSTANT_STRING(L"\\Registry\\Machine\\Software\\" + L"Microsoft\\Windows NT\\CurrentVersion\\" + L"FontSubstitutes"); + + /* open registry key */ + InitializeObjectAttributes(&ObjectAttributes, &FontSubstKey, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, NULL); + Status = ZwOpenKey(&KeyHandle, KEY_READ, &ObjectAttributes); + if (!NT_SUCCESS(Status)) + { + DPRINT("ZwOpenKey failed: 0x%08X\n", Status); + return FALSE; /* failure */ + } + + /* query count of values */ + Status = ZwQueryKey(KeyHandle, KeyFullInformation, + &KeyFullInfo, sizeof(KeyFullInfo), &Length); + if (!NT_SUCCESS(Status)) + { + DPRINT("ZwQueryKey failed: 0x%08X\n", Status); + return FALSE; /* failure */ + } + + /* for each value */ + for (i = 0; i < KeyFullInfo.Values; ++i) + { + /* get value name */ + Status = ZwEnumerateValueKey(KeyHandle, i, KeyValueFullInformation, + InfoBuffer, sizeof(InfoBuffer), &Length); + if (!NT_SUCCESS(Status)) + { + DPRINT("ZwEnumerateValueKey failed: 0x%08X\n", Status); + break; /* failure */ + } + + /* create FromW string */ + pInfo = (PKEY_VALUE_FULL_INFORMATION)InfoBuffer; + Length = pInfo->NameLength / sizeof(WCHAR); + pInfo->Name[Length] = UNICODE_NULL; /* truncate */ + Status = RtlCreateUnicodeString(&FromW, pInfo->Name); + if (!NT_SUCCESS(Status)) + { + DPRINT("RtlCreateUnicodeString failed: 0x%08X\n", Status); + break; /* failure */ + } + + /* query value */ + Status = ZwQueryValueKey(KeyHandle, &FromW, KeyValueFullInformation, + InfoBuffer, sizeof(InfoBuffer), &Length); + pInfo = (PKEY_VALUE_FULL_INFORMATION)InfoBuffer; + if (!NT_SUCCESS(Status) || !pInfo->DataLength) + { + DPRINT("ZwQueryValueKey failed: 0x%08X\n", Status); + RtlFreeUnicodeString(&FromW); + break; /* failure */ + } + + /* create ToW string */ + pch = (LPWSTR)((PUCHAR)pInfo + pInfo->DataOffset); + Length = pInfo->DataLength / sizeof(WCHAR); + pch[Length] = UNICODE_NULL; /* truncate */ + Status = RtlCreateUnicodeString(&ToW, pch); + if (!NT_SUCCESS(Status)) + { + DPRINT("RtlCreateUnicodeString failed: 0x%08X\n", Status); + RtlFreeUnicodeString(&FromW); + break; /* failure */ + } + + /* does charset exist? (from) */ + CharSets[FONTSUBST_FROM] = DEFAULT_CHARSET; + pch = wcsrchr(FromW.Buffer, L','); + if (pch) + { + /* truncate */ + *pch = UNICODE_NULL; + FromW.Length = (pch - FromW.Buffer) * sizeof(WCHAR); + /* parse charset number */ + CharSets[FONTSUBST_FROM] = (BYTE)_wtoi(pch + 1); + } + + /* does charset exist? (to) */ + CharSets[FONTSUBST_TO] = DEFAULT_CHARSET; + pch = wcsrchr(ToW.Buffer, L','); + if (pch) + { + /* truncate */ + *pch = UNICODE_NULL; + ToW.Length = (pch - ToW.Buffer) * sizeof(WCHAR); + /* parse charset number */ + CharSets[FONTSUBST_TO] = (BYTE)_wtoi(pch + 1); + } + + /* allocate an entry */ + pEntry = ExAllocatePoolWithTag(PagedPool, sizeof(FONTSUBST_ENTRY), TAG_FONT); + if (pEntry == NULL) + { + DPRINT("ExAllocatePoolWithTag failed\n"); + RtlFreeUnicodeString(&FromW); + RtlFreeUnicodeString(&ToW); + break; /* failure */ + } + + /* store to *pEntry */ + pEntry->FontNames[FONTSUBST_FROM] = FromW; + pEntry->FontNames[FONTSUBST_TO] = ToW; + pEntry->CharSets[FONTSUBST_FROM] = CharSets[FONTSUBST_FROM]; + pEntry->CharSets[FONTSUBST_TO] = CharSets[FONTSUBST_TO]; + + /* insert pEntry to *pHead */ + InsertTailList(pHead, &pEntry->ListEntry); + } + + /* close now */ + ZwClose(KeyHandle); + + return NT_SUCCESS(Status); +} + +BOOL FASTCALL InitFontSupport(VOID) { ULONG ulError; @@ -232,6 +392,7 @@ } IntLoadSystemFonts(); + IntLoadFontSubstList(&FontSubstListHead); return TRUE; } @@ -265,12 +426,123 @@ FT_Set_Transform(face, &ftmatrix, 0); } +static BOOL +SubstituteFontByList(PLIST_ENTRY pHead, + PUNICODE_STRING pOutputName, + PUNICODE_STRING pInputName, + BYTE RequestedCharSet, + BYTE CharSetMap[FONTSUBST_FROM_AND_TO]) +{ + NTSTATUS Status; + PLIST_ENTRY pListEntry; + PFONTSUBST_ENTRY pSubstEntry; + BYTE CharSets[FONTSUBST_FROM_AND_TO]; + BOOL Found = FALSE; + + CharSetMap[FONTSUBST_FROM] = DEFAULT_CHARSET; + CharSetMap[FONTSUBST_TO] = RequestedCharSet; + + /* for each list entry */ + for (pListEntry = pHead->Flink; + pListEntry != pHead; + pListEntry = pListEntry->Flink) + { + pSubstEntry = + (PFONTSUBST_ENTRY)CONTAINING_RECORD(pListEntry, FONT_ENTRY, ListEntry); + + /* does charset number exist? (from) */ + if (pSubstEntry->CharSets[FONTSUBST_FROM] != DEFAULT_CHARSET) + { + CharSets[FONTSUBST_FROM] = pSubstEntry->CharSets[FONTSUBST_FROM]; + } + else + { + CharSets[FONTSUBST_FROM] = DEFAULT_CHARSET; + } + + if (CharSets[FONTSUBST_FROM] != DEFAULT_CHARSET && + CharSets[FONTSUBST_FROM] != RequestedCharSet) + { + continue; /* not matched */ + } + + /* does charset number exist? (to) */ + if (pSubstEntry->CharSets[FONTSUBST_TO] != DEFAULT_CHARSET) + { + CharSets[FONTSUBST_TO] = pSubstEntry->CharSets[FONTSUBST_TO]; + } + else + { + CharSets[FONTSUBST_TO] = RequestedCharSet; + } + + /* does font name match? */ + if (!RtlEqualUnicodeString(&pSubstEntry->FontNames[FONTSUBST_FROM], + pInputName, TRUE)) + { + continue; /* not matched */ + } + + /* update *pOutputName */ + RtlFreeUnicodeString(pOutputName); + Status = RtlCreateUnicodeString(pOutputName, + pSubstEntry->FontNames[FONTSUBST_TO].Buffer); + if (!NT_SUCCESS(Status)) + { + DPRINT("RtlCreateUnicodeString failed: 0x%08X\n", Status); + continue; /* cannot create string */ + } + + if (CharSetMap[FONTSUBST_FROM] == DEFAULT_CHARSET) + { + /* update CharSetMap */ + CharSetMap[FONTSUBST_FROM] = CharSets[FONTSUBST_FROM]; + CharSetMap[FONTSUBST_TO] = CharSets[FONTSUBST_TO]; + } + Found = TRUE; /* success */ + } + + return Found; +} + +static BOOL +SubstituteFontRecurse(PUNICODE_STRING pInOutName, BYTE *pRequestedCharSet) +{ + UINT RecurseCount = 5; + UNICODE_STRING OutputNameW; + BYTE CharSetMap[FONTSUBST_FROM_AND_TO]; + BOOL Found; + + if (pInOutName->Buffer[0] == UNICODE_NULL) + return FALSE; + + while (RecurseCount-- > 0) + { + RtlInitUnicodeString(&OutputNameW, NULL); + Found = SubstituteFontByList(&FontSubstListHead, + &OutputNameW, pInOutName, + *pRequestedCharSet, CharSetMap); + if (!Found) + break; + + /* update *pInOutName and *pRequestedCharSet */ + RtlFreeUnicodeString(pInOutName); + *pInOutName = OutputNameW; + if (CharSetMap[FONTSUBST_FROM] == DEFAULT_CHARSET || + CharSetMap[FONTSUBST_FROM] == *pRequestedCharSet) + { + *pRequestedCharSet = CharSetMap[FONTSUBST_TO]; + } + } + + return TRUE; /* success */ +} + /* * IntLoadSystemFonts * * Search the system font directory and adds each font found. */ - VOID FASTCALL IntLoadSystemFonts(VOID) { @@ -3296,7 +3568,6 @@ return TRUE; } - DWORD FASTCALL ftGdiGetFontData( @@ -3337,67 +3608,6 @@ return Result; } -static __inline BOOLEAN -SubstituteFontNameByKey(PUNICODE_STRING FaceName, - LPCWSTR Key) -{ - RTL_QUERY_REGISTRY_TABLE QueryTable[2] = {{0}}; - NTSTATUS Status; - UNICODE_STRING Value; - - RtlInitUnicodeString(&Value, NULL); - - QueryTable[0].QueryRoutine = NULL; - QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_NOEXPAND | - RTL_QUERY_REGISTRY_REQUIRED; - QueryTable[0].Name = FaceName->Buffer; - QueryTable[0].EntryContext = &Value; - QueryTable[0].DefaultType = REG_NONE; - QueryTable[0].DefaultData = NULL; - QueryTable[0].DefaultLength = 0; - - QueryTable[1].QueryRoutine = NULL; - QueryTable[1].Name = NULL; - - Status = RtlQueryRegistryValues(RTL_REGISTRY_WINDOWS_NT, - Key, - QueryTable, - NULL, - NULL); - if (NT_SUCCESS(Status)) - { - RtlFreeUnicodeString(FaceName); - *FaceName = Value; - - /* truncate */ - if ((LF_FACESIZE - 1) * sizeof(WCHAR) < FaceName->Length) - { - FaceName->Length = (LF_FACESIZE - 1) * sizeof(WCHAR); - FaceName->Buffer[LF_FACESIZE - 1] = UNICODE_NULL; - } - } - - return NT_SUCCESS(Status); -} - -static __inline BOOL -SubstituteFontName(PUNICODE_STRING FaceName) -{ - UINT Level; - const UINT MaxLevel = 10; - - if (FaceName->Buffer[0] == 0) - return FALSE; - - for (Level = 0; Level < MaxLevel; ++Level) - { - /* NOTE: SubstituteFontNameByKey changes FaceName. Be careful... */ - if (!SubstituteFontNameByKey(FaceName, L"FontSubstitutes")) - break; - } - return (Level > 0); -} - // NOTE: See Table 1. of https://msdn.microsoft.com/en-us/library/ms969909.aspx static UINT FASTCALL GetFontPenalty(LOGFONTW * LogFont, @@ -3404,6 +3614,7 @@ PUNICODE_STRING RequestedNameW, PUNICODE_STRING ActualNameW, PUNICODE_STRING FullFaceNameW, + BYTE RequestedCharSet, PFONTGDI FontGDI, OUTLINETEXTMETRICW * Otm, TEXTMETRICW * TM, @@ -3443,7 +3654,7 @@ } else /* Request is non-"System" font */ { - Byte = LogFont->lfCharSet; + Byte = RequestedCharSet; if (Byte == DEFAULT_CHARSET) { if (RtlEqualUnicodeString(RequestedNameW, &MarlettW, TRUE)) @@ -3470,13 +3681,13 @@ if (UserCharSet != TM->tmCharSet) { /* UNDOCUMENTED */ - Penalty += 10; + Penalty += 100; + if (ANSI_CHARSET != TM->tmCharSet) + { + /* UNDOCUMENTED */ + Penalty += 100; + } } - if (ANSI_CHARSET != TM->tmCharSet) - { - /* UNDOCUMENTED */ - Penalty += 10; - } } } } @@ -3777,7 +3988,8 @@ static __inline VOID FindBestFontFromList(FONTOBJ **FontObj, ULONG *MatchPenalty, LOGFONTW *LogFont, PUNICODE_STRING pRequestedNameW, - PUNICODE_STRING pActualNameW, PLIST_ENTRY Head) + PUNICODE_STRING pActualNameW, BYTE RequestedCharSet, + PLIST_ENTRY Head) { ULONG Penalty; NTSTATUS Status; @@ -3846,8 +4058,8 @@ } Penalty = GetFontPenalty(LogFont, pRequestedNameW, &ActualNameW, - &FullFaceNameW, FontGDI, Otm, TM, - Face->style_name); + &FullFaceNameW, RequestedCharSet, + FontGDI, Otm, TM, Face->style_name); if (*MatchPenalty == 0xFFFFFFFF || Penalty < *MatchPenalty) { DPRINT("%ls Penalty: %lu\n", FullFaceNameW.Buffer, Penalty); @@ -3915,6 +4127,7 @@ ULONG MatchPenalty; LOGFONTW *pLogFont; FT_Face Face; + BYTE RequestedCharSet; if (!pTextObj) { @@ -3938,15 +4151,18 @@ RtlInitUnicodeString(&ActualNameW, NULL); pLogFont = &TextObj->logfont.elfEnumLogfontEx.elfLogFont; - if (! RtlCreateUnicodeString(&RequestedNameW, pLogFont->lfFaceName)) + if (!RtlCreateUnicodeString(&RequestedNameW, pLogFont->lfFaceName)) { if (!pTextObj) TEXTOBJ_UnlockText(TextObj); return STATUS_NO_MEMORY; } - DPRINT("Font '%ls' is substituted by: ", RequestedNameW.Buffer); - SubstituteFontName(&RequestedNameW); - DPRINT("'%ls'.\n", RequestedNameW.Buffer); + /* substitute */ + RequestedCharSet = pLogFont->lfCharSet; + DPRINT("Font '%ls,%u' is substituted by: ", + RequestedNameW.Buffer, RequestedCharSet); + SubstituteFontRecurse(&RequestedNameW, &RequestedCharSet); + DPRINT("'%ls,%u'.\n", RequestedNameW.Buffer, RequestedCharSet); MatchPenalty = 0xFFFFFFFF; TextObj->Font = NULL; @@ -3956,7 +4172,7 @@ /* Search private fonts */ IntLockProcessPrivateFonts(Win32Process); FindBestFontFromList(&TextObj->Font, &MatchPenalty, pLogFont, - &RequestedNameW, &ActualNameW, + &RequestedNameW, &ActualNameW, RequestedCharSet, &Win32Process->PrivateFontListHead); IntUnLockProcessPrivateFonts(Win32Process); @@ -3963,7 +4179,7 @@ /* Search system fonts */ IntLockGlobalFonts; FindBestFontFromList(&TextObj->Font, &MatchPenalty, pLogFont, - &RequestedNameW, &ActualNameW, + &RequestedNameW, &ActualNameW, RequestedCharSet, &FontListHead); IntUnLockGlobalFonts;