[WIN32SS] Improve font substitutes by adding support for charset + loading the substitutes at boot. Patch by Katayama Hirofumi MZ. CORE-12902 #resolve #comment Thanks!

svn path=/trunk/; revision=74178
This commit is contained in:
Mark Jansen 2017-03-15 20:38:45 +00:00
parent 447c0daf7d
commit 0ad21ab1b8

View file

@ -43,7 +43,7 @@
extern const MATRIX gmxWorldToDeviceDefault; extern const MATRIX gmxWorldToDeviceDefault;
extern const MATRIX gmxWorldToPageDefault; 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 #define gmxWorldToDeviceDefault gmxWorldToPageDefault
FT_Library library; FT_Library library;
@ -201,6 +201,167 @@ static const CHARSETINFO FontTci[MAXTCIINDEX] =
{ SYMBOL_CHARSET, CP_SYMBOL, {{0,0,0,0},{FS_SYMBOL,0}} } { 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);
ZwClose(KeyHandle);
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 BOOL FASTCALL
InitFontSupport(VOID) InitFontSupport(VOID)
{ {
@ -232,6 +393,7 @@ InitFontSupport(VOID)
} }
IntLoadSystemFonts(); IntLoadSystemFonts();
IntLoadFontSubstList(&FontSubstListHead);
return TRUE; return TRUE;
} }
@ -265,12 +427,114 @@ FtSetCoordinateTransform(
FT_Set_Transform(face, &ftmatrix, 0); 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];
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);
CharSets[FONTSUBST_FROM] = pSubstEntry->CharSets[FONTSUBST_FROM];
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];
}
return TRUE; /* success */
}
return FALSE;
}
static BOOL
SubstituteFontRecurse(PUNICODE_STRING pInOutName, BYTE *pRequestedCharSet)
{
UINT RecurseCount = 5;
UNICODE_STRING OutputNameW = { 0 };
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 * IntLoadSystemFonts
* *
* Search the system font directory and adds each font found. * Search the system font directory and adds each font found.
*/ */
VOID FASTCALL VOID FASTCALL
IntLoadSystemFonts(VOID) IntLoadSystemFonts(VOID)
{ {
@ -3296,7 +3560,6 @@ ftGdiGetTextMetricsW(
return TRUE; return TRUE;
} }
DWORD DWORD
FASTCALL FASTCALL
ftGdiGetFontData( ftGdiGetFontData(
@ -3337,73 +3600,13 @@ ftGdiGetFontData(
return Result; 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 // NOTE: See Table 1. of https://msdn.microsoft.com/en-us/library/ms969909.aspx
static UINT FASTCALL static UINT FASTCALL
GetFontPenalty(LOGFONTW * LogFont, GetFontPenalty(LOGFONTW * LogFont,
PUNICODE_STRING RequestedNameW, PUNICODE_STRING RequestedNameW,
PUNICODE_STRING ActualNameW, PUNICODE_STRING ActualNameW,
PUNICODE_STRING FullFaceNameW, PUNICODE_STRING FullFaceNameW,
BYTE RequestedCharSet,
PFONTGDI FontGDI, PFONTGDI FontGDI,
OUTLINETEXTMETRICW * Otm, OUTLINETEXTMETRICW * Otm,
TEXTMETRICW * TM, TEXTMETRICW * TM,
@ -3443,7 +3646,7 @@ GetFontPenalty(LOGFONTW * LogFont,
} }
else /* Request is non-"System" font */ else /* Request is non-"System" font */
{ {
Byte = LogFont->lfCharSet; Byte = RequestedCharSet;
if (Byte == DEFAULT_CHARSET) if (Byte == DEFAULT_CHARSET)
{ {
if (RtlEqualUnicodeString(RequestedNameW, &MarlettW, TRUE)) if (RtlEqualUnicodeString(RequestedNameW, &MarlettW, TRUE))
@ -3470,12 +3673,12 @@ GetFontPenalty(LOGFONTW * LogFont,
if (UserCharSet != TM->tmCharSet) if (UserCharSet != TM->tmCharSet)
{ {
/* UNDOCUMENTED */ /* UNDOCUMENTED */
Penalty += 10; Penalty += 100;
} if (ANSI_CHARSET != TM->tmCharSet)
if (ANSI_CHARSET != TM->tmCharSet) {
{ /* UNDOCUMENTED */
/* UNDOCUMENTED */ Penalty += 100;
Penalty += 10; }
} }
} }
} }
@ -3777,7 +3980,8 @@ GetFontPenalty(LOGFONTW * LogFont,
static __inline VOID static __inline VOID
FindBestFontFromList(FONTOBJ **FontObj, ULONG *MatchPenalty, LOGFONTW *LogFont, FindBestFontFromList(FONTOBJ **FontObj, ULONG *MatchPenalty, LOGFONTW *LogFont,
PUNICODE_STRING pRequestedNameW, PUNICODE_STRING pRequestedNameW,
PUNICODE_STRING pActualNameW, PLIST_ENTRY Head) PUNICODE_STRING pActualNameW, BYTE RequestedCharSet,
PLIST_ENTRY Head)
{ {
ULONG Penalty; ULONG Penalty;
NTSTATUS Status; NTSTATUS Status;
@ -3846,8 +4050,8 @@ FindBestFontFromList(FONTOBJ **FontObj, ULONG *MatchPenalty, LOGFONTW *LogFont,
} }
Penalty = GetFontPenalty(LogFont, pRequestedNameW, &ActualNameW, Penalty = GetFontPenalty(LogFont, pRequestedNameW, &ActualNameW,
&FullFaceNameW, FontGDI, Otm, TM, &FullFaceNameW, RequestedCharSet,
Face->style_name); FontGDI, Otm, TM, Face->style_name);
if (*MatchPenalty == 0xFFFFFFFF || Penalty < *MatchPenalty) if (*MatchPenalty == 0xFFFFFFFF || Penalty < *MatchPenalty)
{ {
DPRINT("%ls Penalty: %lu\n", FullFaceNameW.Buffer, Penalty); DPRINT("%ls Penalty: %lu\n", FullFaceNameW.Buffer, Penalty);
@ -3915,6 +4119,7 @@ TextIntRealizeFont(HFONT FontHandle, PTEXTOBJ pTextObj)
ULONG MatchPenalty; ULONG MatchPenalty;
LOGFONTW *pLogFont; LOGFONTW *pLogFont;
FT_Face Face; FT_Face Face;
BYTE RequestedCharSet;
if (!pTextObj) if (!pTextObj)
{ {
@ -3938,15 +4143,18 @@ TextIntRealizeFont(HFONT FontHandle, PTEXTOBJ pTextObj)
RtlInitUnicodeString(&ActualNameW, NULL); RtlInitUnicodeString(&ActualNameW, NULL);
pLogFont = &TextObj->logfont.elfEnumLogfontEx.elfLogFont; pLogFont = &TextObj->logfont.elfEnumLogfontEx.elfLogFont;
if (! RtlCreateUnicodeString(&RequestedNameW, pLogFont->lfFaceName)) if (!RtlCreateUnicodeString(&RequestedNameW, pLogFont->lfFaceName))
{ {
if (!pTextObj) TEXTOBJ_UnlockText(TextObj); if (!pTextObj) TEXTOBJ_UnlockText(TextObj);
return STATUS_NO_MEMORY; return STATUS_NO_MEMORY;
} }
DPRINT("Font '%ls' is substituted by: ", RequestedNameW.Buffer); /* substitute */
SubstituteFontName(&RequestedNameW); RequestedCharSet = pLogFont->lfCharSet;
DPRINT("'%ls'.\n", RequestedNameW.Buffer); DPRINT("Font '%ls,%u' is substituted by: ",
RequestedNameW.Buffer, RequestedCharSet);
SubstituteFontRecurse(&RequestedNameW, &RequestedCharSet);
DPRINT("'%ls,%u'.\n", RequestedNameW.Buffer, RequestedCharSet);
MatchPenalty = 0xFFFFFFFF; MatchPenalty = 0xFFFFFFFF;
TextObj->Font = NULL; TextObj->Font = NULL;
@ -3956,14 +4164,14 @@ TextIntRealizeFont(HFONT FontHandle, PTEXTOBJ pTextObj)
/* Search private fonts */ /* Search private fonts */
IntLockProcessPrivateFonts(Win32Process); IntLockProcessPrivateFonts(Win32Process);
FindBestFontFromList(&TextObj->Font, &MatchPenalty, pLogFont, FindBestFontFromList(&TextObj->Font, &MatchPenalty, pLogFont,
&RequestedNameW, &ActualNameW, &RequestedNameW, &ActualNameW, RequestedCharSet,
&Win32Process->PrivateFontListHead); &Win32Process->PrivateFontListHead);
IntUnLockProcessPrivateFonts(Win32Process); IntUnLockProcessPrivateFonts(Win32Process);
/* Search system fonts */ /* Search system fonts */
IntLockGlobalFonts; IntLockGlobalFonts;
FindBestFontFromList(&TextObj->Font, &MatchPenalty, pLogFont, FindBestFontFromList(&TextObj->Font, &MatchPenalty, pLogFont,
&RequestedNameW, &ActualNameW, &RequestedNameW, &ActualNameW, RequestedCharSet,
&FontListHead); &FontListHead);
IntUnLockGlobalFonts; IntUnLockGlobalFonts;