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,161 @@ { SYMBOL_CHARSET, CP_SYMBOL, {{0,0,0,0},{FS_SYMBOL,0}} } }; +/* + * SUBST_ENTRY --- font substitute entry + */ +typedef struct SUBST_ENTRY +{ + LIST_ENTRY ListEntry; + UNICODE_STRING FontNames[2]; /* from and to */ + BYTE CharSets[2]; /* from and to */ +} SUBST_ENTRY, *PSUBST_ENTRY; + +/* list head */ +static LIST_ENTRY 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; + LPWSTR pch; + BYTE CharSets[2]; + PSUBST_ENTRY pEntry; + + static UNICODE_STRING FontSubstKey = + RTL_CONSTANT_STRING(L"\\Registry\\Machine\\Software\\" + L"Microsoft\\Windows NT\\CurrentVersion\\" + L"FontSubstitutes"); + + /* initialize *pHead */ + InitializeListHead(pHead); + + /* 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; + pInfo->Name[pInfo->NameLength / sizeof(WCHAR)] = UNICODE_NULL; + Status = RtlCreateUnicodeString(&FromW, pInfo->Name); + if (!NT_SUCCESS(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; + pch[Length / sizeof(WCHAR)] = UNICODE_NULL; + Status = RtlCreateUnicodeString(&ToW, pch); + if (!NT_SUCCESS(Status)) + { + DPRINT("RtlCreateUnicodeString failed: 0x%08X\n", Status); + RtlFreeUnicodeString(&FromW); + break; /* failure */ + } + + CharSets[0] = DEFAULT_CHARSET; + CharSets[1] = DEFAULT_CHARSET; + + /* does charset exist? */ + pch = wcsrchr(FromW.Buffer, L','); + if (pch) + { + *pch = UNICODE_NULL; + FromW.Length = (pch - FromW.Buffer) * sizeof(WCHAR); + CharSets[0] = (BYTE)_wtoi(pch + 1); + } + pch = wcsrchr(ToW.Buffer, L','); + if (pch) + { + *pch = UNICODE_NULL; + ToW.Length = (pch - ToW.Buffer) * sizeof(WCHAR); + CharSets[1] = (BYTE)_wtoi(pch + 1); + } + + /* allocate an entry */ + pEntry = ExAllocatePoolWithTag(PagedPool, sizeof(SUBST_ENTRY), TAG_FONT); + if (pEntry == NULL) + { + RtlFreeUnicodeString(&FromW); + RtlFreeUnicodeString(&ToW); + break; /* failure */ + } + + /* store to *pEntry */ + pEntry->FontNames[0] = FromW; + pEntry->FontNames[1] = ToW; + pEntry->CharSets[0] = CharSets[0]; + pEntry->CharSets[1] = CharSets[1]; + + /* insert pEntry to *pHead */ + InsertTailList(pHead, &pEntry->ListEntry); + } + + /* close now */ + ZwClose(KeyHandle); + + return NT_SUCCESS(Status); +} + +BOOL FASTCALL InitFontSupport(VOID) { ULONG ulError; @@ -232,6 +386,7 @@ } IntLoadSystemFonts(); + IntLoadFontSubstList(&FontSubstListHead); return TRUE; } @@ -265,12 +420,103 @@ FT_Set_Transform(face, &ftmatrix, 0); } +static BOOL +SubstituteFontByList(PLIST_ENTRY pHead, + PUNICODE_STRING pOutputName, + PUNICODE_STRING pInputName, + BYTE RequestedCharSet, + BYTE CharSetMap[2]) +{ + NTSTATUS Status; + PSUBST_ENTRY pSubstEntry; + BYTE CharSets[2]; /* from and to */ + PLIST_ENTRY pListEntry; + BOOL Found = FALSE; + + CharSetMap[0] = DEFAULT_CHARSET; + CharSetMap[1] = RequestedCharSet; + + /* for each list entry */ + for (pListEntry = pHead->Flink; + pListEntry != pHead; + pListEntry = pListEntry->Flink) + { + pSubstEntry = + (PSUBST_ENTRY)CONTAINING_RECORD(pListEntry, FONT_ENTRY, ListEntry); + + CharSets[0] = DEFAULT_CHARSET; + CharSets[1] = RequestedCharSet; + if (pSubstEntry->CharSets[0] != DEFAULT_CHARSET) + { + CharSets[0] = pSubstEntry->CharSets[0]; + } + if (pSubstEntry->CharSets[1] != DEFAULT_CHARSET) + { + CharSets[1] = pSubstEntry->CharSets[1]; + } + + if (RtlEqualUnicodeString(&pSubstEntry->FontNames[0], pInputName, TRUE)) + { + if (CharSets[0] == DEFAULT_CHARSET || + CharSets[0] == RequestedCharSet) + { + RtlFreeUnicodeString(pOutputName); + Status = RtlCreateUnicodeString(pOutputName, + pSubstEntry->FontNames[1].Buffer); + if (NT_SUCCESS(Status)) + { + Found = TRUE; + if (CharSetMap[0] == DEFAULT_CHARSET) + { + CharSetMap[0] = CharSets[0]; + CharSetMap[1] = CharSets[1]; + } + } + } + } + } + + return Found; +} + +static BOOL +SubstituteFont(PUNICODE_STRING pInOutName, BYTE *pRequestedCharSet) +{ + UINT RecurseCount = 5; + UNICODE_STRING OutputNameW; + BYTE CharSetMap[2]; /* 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[0] == DEFAULT_CHARSET || + *pRequestedCharSet == CharSetMap[0]) + { + *pRequestedCharSet = CharSetMap[1]; + } + } + + return TRUE; /* success */ +} + /* * IntLoadSystemFonts * * Search the system font directory and adds each font found. */ - VOID FASTCALL IntLoadSystemFonts(VOID) { @@ -3296,7 +3542,6 @@ return TRUE; } - DWORD FASTCALL ftGdiGetFontData( @@ -3337,67 +3582,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 +3588,7 @@ PUNICODE_STRING RequestedNameW, PUNICODE_STRING ActualNameW, PUNICODE_STRING FullFaceNameW, + BYTE RequestedCharSet, PFONTGDI FontGDI, OUTLINETEXTMETRICW * Otm, TEXTMETRICW * TM, @@ -3443,7 +3628,7 @@ } else /* Request is non-"System" font */ { - Byte = LogFont->lfCharSet; + Byte = RequestedCharSet; if (Byte == DEFAULT_CHARSET) { if (RtlEqualUnicodeString(RequestedNameW, &MarlettW, TRUE)) @@ -3470,13 +3655,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 +3962,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 +4032,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 +4101,7 @@ ULONG MatchPenalty; LOGFONTW *pLogFont; FT_Face Face; + BYTE RequestedCharSet; if (!pTextObj) { @@ -3938,15 +4125,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); + SubstituteFont(&RequestedNameW, &RequestedCharSet); + DPRINT("'%ls,%u'.\n", RequestedNameW.Buffer, RequestedCharSet); MatchPenalty = 0xFFFFFFFF; TextObj->Font = NULL; @@ -3956,7 +4146,7 @@ /* Search private fonts */ IntLockProcessPrivateFonts(Win32Process); FindBestFontFromList(&TextObj->Font, &MatchPenalty, pLogFont, - &RequestedNameW, &ActualNameW, + &RequestedNameW, &ActualNameW, RequestedCharSet, &Win32Process->PrivateFontListHead); IntUnLockProcessPrivateFonts(Win32Process); @@ -3963,7 +4153,7 @@ /* Search system fonts */ IntLockGlobalFonts; FindBestFontFromList(&TextObj->Font, &MatchPenalty, pLogFont, - &RequestedNameW, &ActualNameW, + &RequestedNameW, &ActualNameW, RequestedCharSet, &FontListHead); IntUnLockGlobalFonts;