From 0f9e889736f01cef58fd50cd1f5558becbe882d5 Mon Sep 17 00:00:00 2001 From: Katayama Hirofumi MZ Date: Sun, 30 Jun 2024 22:15:34 +0900 Subject: [PATCH] [NTGDI][FREETYPE][SETUP][INF] Support FontLink (#7009) If East Asian people were unable to see the Latin characters, it becomes a barrier to mutual understanding. FontLink will break that barrier. JIRA issue: CORE-9616 JIRA issue: CORE-15480 - Modify font substitutes. - Unify the lock variables. - Add FONTLINK and FONTLINK_CHAIN structures. - Add FontLink_Create and FontLink_Destroy functions. - Add FontLink_Chain_Init, FontLink_Chain_Free, FontLink_Chain_LoadReg, FontLink_Chain_Populate, and FontLink_Chain_FindGlyph functions. - Implement FontLink. - Add font file DroidSansFallback.ttf for LiveCD. --- base/setup/lib/muifonts.h | 26 +- boot/bootdata/hivesft.inf | 6 +- boot/bootdata/hivesys.inf | 7 + boot/bootdata/livecd.inf | 24 + media/inf/font.inf | 2 - modules/CMakeLists.txt | 6 + win32ss/gdi/ntgdi/font.c | 4 +- win32ss/gdi/ntgdi/freetype.c | 866 +++++++++++++++++++++++++++++++---- win32ss/gdi/ntgdi/text.h | 2 +- 9 files changed, 840 insertions(+), 103 deletions(-) diff --git a/base/setup/lib/muifonts.h b/base/setup/lib/muifonts.h index 8a8ef990bdc91..430630e2d28c2 100644 --- a/base/setup/lib/muifonts.h +++ b/base/setup/lib/muifonts.h @@ -148,7 +148,6 @@ MUI_SUBFONT ChineseSimplifiedFonts[] = { L"Courier New Greek,161", L"Courier New,161" }, { L"Courier New TUR,162", L"Courier New,162" }, { L"Fixedsys", L"Fixedsys Excelsior 3.01-L2" }, - { L"Franklin Gothic Medium", L"Droid Sans Fallback" }, { L"Helv", L"MS Sans Serif" }, { L"Helvetica", L"Arial" }, { L"MS Sans Serif", L"Droid Sans Fallback" }, @@ -161,9 +160,6 @@ MUI_SUBFONT ChineseSimplifiedFonts[] = { L"Segoe UI Symbol", L"Arial" }, { L"SimHei", L"Droid Sans Fallback" }, { L"SimSun", L"Droid Sans Fallback" }, - { L"Source Sans Pro", L"Droid Sans Fallback" }, - { L"System", L"Droid Sans Fallback" }, - { L"Tahoma", L"Droid Sans Fallback" }, { L"Terminal", L"Lucida Console" }, { L"Times", L"Times New Roman" }, { L"Times New Roman Baltic,186", L"Times New Roman,186" }, @@ -202,7 +198,6 @@ MUI_SUBFONT ChineseTraditionalFonts[] = { L"DLCMingBold", L"Droid Sans Fallback" }, { L"DLCMingMedium", L"Droid Sans Fallback" }, { L"Fixedsys", L"Fixedsys Excelsior 3.01-L2" }, - { L"Franklin Gothic Medium", L"Droid Sans Fallback" }, { L"Helv", L"MS Sans Serif" }, { L"Helvetica", L"Arial" }, { L"MS Sans Serif", L"Droid Sans Fallback" }, @@ -214,9 +209,6 @@ MUI_SUBFONT ChineseTraditionalFonts[] = { L"MingLiU", L"Droid Sans Fallback" }, { L"PMingLiU", L"Droid Sans Fallback" }, { L"Segoe UI Symbol", L"Arial" }, - { L"Source Sans Pro", L"Droid Sans Fallback" }, - { L"System", L"Droid Sans Fallback" }, - { L"Tahoma", L"Droid Sans Fallback" }, { L"Terminal", L"Lucida Console" }, { L"Times", L"Times New Roman" }, { L"Times New Roman Baltic,186", L"Times New Roman,186" }, @@ -252,7 +244,6 @@ MUI_SUBFONT JapaneseFonts[] = { L"Courier New Greek,161", L"Courier New,161" }, { L"Courier New TUR,162", L"Courier New,162" }, { L"Fixedsys", L"Fixedsys Excelsior 3.01-L2" }, - { L"Franklin Gothic Medium", L"Droid Sans Fallback" }, { L"Helv", L"MS Sans Serif" }, { L"Helvetica", L"Arial" }, { L"MS Gothic", L"Droid Sans Fallback" }, @@ -265,9 +256,6 @@ MUI_SUBFONT JapaneseFonts[] = { L"MS UI Gothic", L"Droid Sans Fallback" }, { L"MS UI Gothic 2", L"Droid Sans Fallback" }, { L"Segoe UI Symbol", L"Arial" }, - { L"Source Sans Pro", L"Droid Sans Fallback" }, - { L"System", L"Droid Sans Fallback" }, - { L"Tahoma", L"Droid Sans Fallback" }, { L"Terminal", L"Lucida Console" }, { L"Times", L"Times New Roman" }, { L"Times New Roman Baltic,186", L"Times New Roman,186" }, @@ -287,10 +275,12 @@ MUI_SUBFONT JapaneseFonts[] = WCHAR KF_LocalName0[] = {0xBC14, 0xD0D5, 0}; /* Batang */ WCHAR KF_LocalName1[] = {0xBC14, 0xD0D5, 0xCCB4, 0}; /* BatangChe */ -WCHAR KF_LocalName2[] = {0xAD81, 0xC11C, 0}; /* Gungsuh */ -WCHAR KF_LocalName3[] = {0xAD81, 0xC11C, 0xCCB4, 0}; /* GungsuhChe */ +WCHAR KF_LocalName2[] = {0xB3CB, 0xC6C0, 0}; /* Dotum */ +WCHAR KF_LocalName3[] = {0xB3CB, 0xC6C0, 0xCCB4, 0}; /* DotumChe */ WCHAR KF_LocalName4[] = {0xAD74, 0xB9BC, 0}; /* Gulim */ WCHAR KF_LocalName5[] = {0xAD74, 0xB9BC, 0xCCB4, 0}; /* GulimChe */ +WCHAR KF_LocalName6[] = {0xAD81, 0xC11C, 0}; /* Gungsuh */ +WCHAR KF_LocalName7[] = {0xAD81, 0xC11C, 0xCCB4, 0}; /* GungsuhChe */ MUI_SUBFONT KoreanFonts[] = { { L"Arial Baltic,186", L"Arial,186" }, @@ -306,8 +296,9 @@ MUI_SUBFONT KoreanFonts[] = { L"Courier New CYR,204", L"Courier New,204" }, { L"Courier New Greek,161", L"Courier New,161" }, { L"Courier New TUR,162", L"Courier New,162" }, + { L"Dotum", L"Droid Sans Fallback" }, + { L"DotumChe", L"Droid Sans Fallback" }, { L"Fixedsys", L"Fixedsys Excelsior 3.01-L2" }, - { L"Franklin Gothic Medium", L"Droid Sans Fallback" }, { L"Gulim", L"Droid Sans Fallback" }, { L"GulimChe", L"Droid Sans Fallback" }, { L"Gungsuh", L"Droid Sans Fallback" }, @@ -320,9 +311,6 @@ MUI_SUBFONT KoreanFonts[] = { L"MS UI Gothic", L"Droid Sans Fallback" }, { L"MS UI Gothic 2", L"Droid Sans Fallback" }, { L"Segoe UI Symbol", L"Arial" }, - { L"Source Sans Pro", L"Droid Sans Fallback" }, - { L"System", L"Droid Sans Fallback" }, - { L"Tahoma", L"Droid Sans Fallback" }, { L"Terminal", L"Lucida Console" }, { L"Times", L"Times New Roman" }, { L"Times New Roman Baltic,186", L"Times New Roman,186" }, @@ -339,6 +327,8 @@ MUI_SUBFONT KoreanFonts[] = { KF_LocalName3, L"Droid Sans Fallback" }, { KF_LocalName4, L"Droid Sans Fallback" }, { KF_LocalName5, L"Droid Sans Fallback" }, + { KF_LocalName6, L"Droid Sans Fallback" }, + { KF_LocalName7, L"Droid Sans Fallback" }, { NULL, NULL } }; diff --git a/boot/bootdata/hivesft.inf b/boot/bootdata/hivesft.inf index 4feaaf7dc50b5..bdcb5fa87cd8b 100644 --- a/boot/bootdata/hivesft.inf +++ b/boot/bootdata/hivesft.inf @@ -627,12 +627,14 @@ HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontLink\SystemLink","Microso "msgothic.ttc,MS UI Gothic",\ "mingliu.ttc,PMingLiU",\ "simsun.ttc,SimSun",\ - "gulim.ttc,Gulim" + "gulim.ttc,Gulim",\ + "tahoma.ttf,Tahoma" HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontLink\SystemLink","Lucida Sans Unicode",0x00010000,\ "msgothic.ttc,MS UI Gothic",\ "mingliu.ttc,PMingLiU",\ "simsun.ttc,SimSun",\ - "gulim.ttc,Gulim" + "gulim.ttc,Gulim",\ + "tahoma.ttf,Tahoma" ; FontLink (Chinese to others) HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontLink\SystemLink","MingLiU",0x00010000,\ diff --git a/boot/bootdata/hivesys.inf b/boot/bootdata/hivesys.inf index 319b74f288df2..bcd2bda1e164d 100644 --- a/boot/bootdata/hivesys.inf +++ b/boot/bootdata/hivesys.inf @@ -2227,6 +2227,13 @@ HKLM,"SYSTEM\Setup","SystemSetupInProgress",0x00010001,0x00000001 HKLM,"SYSTEM\CurrentControlSet\Control\PriorityControl",,0x00000012 HKLM,"SYSTEM\CurrentControlSet\Control\PriorityControl","Win32PrioritySeparation",0x00010001,0x2 +; FontLink settings +HKLM,"SYSTEM\CurrentControlSet\Control\FontAssoc\Associated Charset","ANSI(00)",0x00000000,"NO" +HKLM,"SYSTEM\CurrentControlSet\Control\FontAssoc\Associated Charset","OEM(FF)",0x00000000,"NO" +HKLM,"SYSTEM\CurrentControlSet\Control\FontAssoc\Associated Charset","SYMBOL(02)",0x00000000,"NO" +HKLM,"SYSTEM\CurrentControlSet\Control\FontAssoc\Associated DefaultFonts","AssocSystemFont",0x00000000,"DroidSansFallback.ttf" +HKLM,"SYSTEM\CurrentControlSet\Control\FontAssoc\Associated DefaultFonts","FontPackage",0x00000000,"Droid Sans Fallback" + [AddReg.NTarm] ; RAM Disk class driver HKLM,"SYSTEM\CurrentControlSet\Services\Disk","ErrorControl",0x00010001,0x00000000 diff --git a/boot/bootdata/livecd.inf b/boot/bootdata/livecd.inf index 41f2bd38d23c1..12fa29cddb74a 100644 --- a/boot/bootdata/livecd.inf +++ b/boot/bootdata/livecd.inf @@ -58,6 +58,30 @@ HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","Segoe UI Sy HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","Terminal",0x00000000,"Lucida Console" HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","Times",0x00000000,"Times New Roman" HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","Tms Rmn",0x00000000,"Times New Roman" +; Font Substitution (East Asian, English names) +HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","Batang",0x00000000,"Droid Sans Fallback" +HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","BatangChe",0x00000000,"Droid Sans Fallback" +HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","DLCMingBold",0x00000000,"Droid Sans Fallback" +HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","DLCMingMedium",0x00000000,"Droid Sans Fallback" +HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","Dotum",0x00000000,"Droid Sans Fallback" +HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","DotumChe",0x00000000,"Droid Sans Fallback" +HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","Gulim",0x00000000,"Droid Sans Fallback" +HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","GulimChe",0x00000000,"Droid Sans Fallback" +HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","Gungsuh",0x00000000,"Droid Sans Fallback" +HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","GungsuhChe",0x00000000,"Droid Sans Fallback" +HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","MS Gothic",0x00000000,"Droid Sans Fallback" +HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","MS Mincho",0x00000000,"Droid Sans Fallback" +HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","MS PGothic",0x00000000,"Droid Sans Fallback" +HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","MS PMincho",0x00000000,"Droid Sans Fallback" +HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","MS Song",0x00000000,"Droid Sans Fallback" +HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","MS UI Gothic",0x00000000,"Droid Sans Fallback" +HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","MS UI Gothic 2",0x00000000,"Droid Sans Fallback" +HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","Ming Light",0x00000000,"Droid Sans Fallback" +HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","MingLiU",0x00000000,"Droid Sans Fallback" +HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","NSimSun",0x00000000,"Droid Sans Fallback" +HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","PMingLiU",0x00000000,"Droid Sans Fallback" +HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","SimHei",0x00000000,"Droid Sans Fallback" +HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","SimSun",0x00000000,"Droid Sans Fallback" ; FIXME: Registration diff --git a/media/inf/font.inf b/media/inf/font.inf index e6dc0cf8647a0..817e394fe798e 100644 --- a/media/inf/font.inf +++ b/media/inf/font.inf @@ -87,8 +87,6 @@ HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","MS Shell Dl HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","MS UI Gothic",0x00000000,"Droid Sans Fallback" HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","MS UI Gothic 2",0x00000000,"Droid Sans Fallback" HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","Segoe UI Symbol",0x00000000,"Arial" -HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","Source Sans Pro",0x00000000,"Droid Sans Fallback" -HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","Tahoma",0x00000000,"Droid Sans Fallback" HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","Terminal",0x00000000,"Lucida Console" HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","Times",0x00000000,"Times New Roman" HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","Tms Rmn",0x00000000,"Times New Roman" diff --git a/modules/CMakeLists.txt b/modules/CMakeLists.txt index ca002635b33fe..618de5c940bdd 100644 --- a/modules/CMakeLists.txt +++ b/modules/CMakeLists.txt @@ -49,3 +49,9 @@ if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/) add_cd_file(FILE ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/${item} DESTINATION reactos/3rdParty NAME_ON_CD ${item} FOR bootcd) endforeach(item) endif() + +# Add font file DroidSansFallback.ttf for LiveCD +# See also boot/bootdata/packages/reactos.dff.in +if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/optional/DroidSansFallback.ttf) + add_cd_file(FILE "${CMAKE_CURRENT_SOURCE_DIR}/optional/DroidSansFallback.ttf" DESTINATION reactos/Fonts FOR livecd) +endif() diff --git a/win32ss/gdi/ntgdi/font.c b/win32ss/gdi/ntgdi/font.c index 1db1284615b0f..cd1b080861f5b 100644 --- a/win32ss/gdi/ntgdi/font.c +++ b/win32ss/gdi/ntgdi/font.c @@ -914,7 +914,7 @@ NtGdiGetOutlineTextMetricsInternalW (HDC hDC, } TextIntUpdateSize(dc, TextObj, FontGDI, TRUE); TEXTOBJ_UnlockText(TextObj); - Size = IntGetOutlineTextMetrics(FontGDI, 0, NULL); + Size = IntGetOutlineTextMetrics(FontGDI, 0, NULL, FALSE); if (!otm) return Size; if (Size > Data) { @@ -928,7 +928,7 @@ NtGdiGetOutlineTextMetricsInternalW (HDC hDC, return 0; } RtlZeroMemory(potm, Size); - IntGetOutlineTextMetrics(FontGDI, Size, potm); + IntGetOutlineTextMetrics(FontGDI, Size, potm, FALSE); _SEH2_TRY { diff --git a/win32ss/gdi/ntgdi/freetype.c b/win32ss/gdi/ntgdi/freetype.c index 1a47c6ec0ed67..267d4d923468e 100644 --- a/win32ss/gdi/ntgdi/freetype.c +++ b/win32ss/gdi/ntgdi/freetype.c @@ -4,7 +4,7 @@ * PURPOSE: FreeType font engine interface * PROGRAMMERS: Copyright 2001 Huw D M Davies for CodeWeavers. * Copyright 2006 Dmitry Timoshkov for CodeWeavers. - * Copyright 2016-2019 Katayama Hirofumi MZ. + * Copyright 2016-2024 Katayama Hirofumi MZ. */ /** Includes ******************************************************************/ @@ -35,6 +35,294 @@ #define NDEBUG #include +typedef struct _FONTLINK +{ + LIST_ENTRY ListEntry; //< Entry in the FONTLINK_CHAIN::FontLinkList + BOOL bIgnore; + LOGFONTW LogFont; + PSHARED_FACE SharedFace; +} FONTLINK, *PFONTLINK; + +typedef struct _FONTLINK_CHAIN +{ + LIST_ENTRY FontLinkList; //< List of FONTLINK's + LOGFONTW LogFont; + PZZWSTR pszzFontLink; + PTEXTOBJ pBaseTextObj; + FT_Face pDefFace; +} FONTLINK_CHAIN, *PFONTLINK_CHAIN; + +typedef struct _FONTLINK_CACHE +{ + LIST_ENTRY ListEntry; + LOGFONTW LogFont; + FONTLINK_CHAIN Chain; +} FONTLINK_CACHE, *PFONTLINK_CACHE; + +#define FONTLINK_DEFAULT_CHAR 0x30FB // U+30FB (KATAKANA MIDDLE DOT) + +static DWORD s_chFontLinkDefaultChar = FONTLINK_DEFAULT_CHAR; +static WCHAR s_szDefFontLinkFileName[MAX_PATH] = L""; +static WCHAR s_szDefFontLinkFontName[MAX_PATH] = L""; +static BOOL s_fFontLinkUseAnsi = FALSE; +static BOOL s_fFontLinkUseOem = FALSE; +static BOOL s_fFontLinkUseSymbol = FALSE; + +#define MAX_FONTLINK_CACHE 128 +static RTL_STATIC_LIST_HEAD(g_FontLinkCache); // The list of FONTLINK_CACHE +static LONG g_nFontLinkCacheCount = 0; + +static SIZE_T +SZZ_GetSize(_In_ PCZZWSTR pszz) +{ + SIZE_T ret = 0, cch; + const WCHAR *pch = pszz; + while (*pch) + { + cch = wcslen(pch) + 1; + ret += cch; + pch += cch; + } + ++ret; + return ret * sizeof(WCHAR); +} + +static inline NTSTATUS +FontLink_LoadSettings(VOID) +{ + NTSTATUS Status; + HKEY hKey; + DWORD cbData, dwValue; + + // Set the default values + s_chFontLinkDefaultChar = FONTLINK_DEFAULT_CHAR; + + // Open the registry key + Status = RegOpenKey( + L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion\\FontLink", + &hKey); + if (!NT_SUCCESS(Status)) + return Status; + + cbData = sizeof(dwValue); + Status = RegQueryValue(hKey, L"FontLinkDefaultChar", REG_DWORD, &dwValue, &cbData); + if (NT_SUCCESS(Status) && cbData == sizeof(dwValue)) + s_chFontLinkDefaultChar = dwValue; + + ZwClose(hKey); // Close the registry key + return STATUS_SUCCESS; +} + +static inline NTSTATUS +FontLink_LoadDefaultFonts(VOID) +{ + NTSTATUS Status; + HKEY hKey; + DWORD cbData; + WCHAR szValue[MAX_PATH]; + + // Set the default values + s_szDefFontLinkFileName[0] = s_szDefFontLinkFontName[0] = UNICODE_NULL; + + // Open the registry key + Status = RegOpenKey( + L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\FontAssoc\\Associated DefaultFonts", + &hKey); + if (!NT_SUCCESS(Status)) + return Status; + + cbData = sizeof(szValue); + Status = RegQueryValue(hKey, L"AssocSystemFont", REG_SZ, szValue, &cbData); + if (NT_SUCCESS(Status)) + { + szValue[_countof(szValue) - 1] = UNICODE_NULL; // Avoid buffer overrun + RtlStringCchCopyW(s_szDefFontLinkFileName, _countof(s_szDefFontLinkFileName), szValue); + } + + cbData = sizeof(szValue); + Status = RegQueryValue(hKey, L"FontPackage", REG_SZ, szValue, &cbData); + if (NT_SUCCESS(Status)) + { + szValue[_countof(szValue) - 1] = UNICODE_NULL; // Avoid buffer overrun + RtlStringCchCopyW(s_szDefFontLinkFontName, _countof(s_szDefFontLinkFontName), szValue); + } + + ZwClose(hKey); // Close the registry key + return STATUS_SUCCESS; +} + +static inline NTSTATUS +FontLink_LoadDefaultCharset(VOID) +{ + NTSTATUS Status; + HKEY hKey; + DWORD cbData; + WCHAR szValue[8]; + + // Set the default values + s_fFontLinkUseAnsi = s_fFontLinkUseOem = s_fFontLinkUseSymbol = FALSE; + + // Open the registry key + Status = RegOpenKey( + L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\FontAssoc\\Associated Charset", + &hKey); + if (!NT_SUCCESS(Status)) + return Status; + + cbData = sizeof(szValue); + Status = RegQueryValue(hKey, L"ANSI(00)", REG_SZ, szValue, &cbData); + if (NT_SUCCESS(Status)) + { + szValue[_countof(szValue) - 1] = UNICODE_NULL; // Avoid buffer overrun + s_fFontLinkUseAnsi = !_wcsicmp(szValue, L"YES"); + } + + cbData = sizeof(szValue); + Status = RegQueryValue(hKey, L"OEM(FF)", REG_SZ, szValue, &cbData); + if (NT_SUCCESS(Status)) + { + szValue[_countof(szValue) - 1] = UNICODE_NULL; // Avoid buffer overrun + s_fFontLinkUseOem = !_wcsicmp(szValue, L"YES"); + } + + cbData = sizeof(szValue); + Status = RegQueryValue(hKey, L"SYMBOL(02)", REG_SZ, szValue, &cbData); + if (NT_SUCCESS(Status)) + { + szValue[_countof(szValue) - 1] = UNICODE_NULL; // Avoid buffer overrun + s_fFontLinkUseSymbol = !_wcsicmp(szValue, L"YES"); + } + + ZwClose(hKey); // Close the registry key + return STATUS_SUCCESS; +} + +static inline VOID +FontLink_Destroy(_Inout_ PFONTLINK pLink) +{ + ASSERT(pLink); + ExFreePoolWithTag(pLink, TAG_FONT); +} + +static inline BOOL +FontLink_Chain_IsPopulated(const FONTLINK_CHAIN *pChain) +{ + return pChain->LogFont.lfFaceName[0]; +} + +static VOID +FontLink_Chain_Free( + _Inout_ PFONTLINK_CHAIN pChain) +{ + PLIST_ENTRY Entry; + PFONTLINK pLink; + + if (!FontLink_Chain_IsPopulated(pChain)) // The chain is not populated yet + return; + + ExFreePoolWithTag(pChain->pszzFontLink, TAG_FONT); + + while (!IsListEmpty(&pChain->FontLinkList)) + { + Entry = RemoveHeadList(&pChain->FontLinkList); + pLink = CONTAINING_RECORD(Entry, FONTLINK, ListEntry); + FontLink_Destroy(pLink); + } +} + +static inline VOID +FontLink_AddCache( + _In_ PFONTLINK_CACHE pCache) +{ + PLIST_ENTRY Entry; + + /* Add the new cache entry to the top of the cache list */ + ++g_nFontLinkCacheCount; + InsertHeadList(&g_FontLinkCache, &pCache->ListEntry); + + /* If there are too many cache entries in the list, remove the oldest one at the bottom */ + if (g_nFontLinkCacheCount > MAX_FONTLINK_CACHE) + { + ASSERT(!IsListEmpty(&g_FontLinkCache)); + Entry = RemoveTailList(&g_FontLinkCache); + --g_nFontLinkCacheCount; + pCache = CONTAINING_RECORD(Entry, FONTLINK_CACHE, ListEntry); + FontLink_Chain_Free(&pCache->Chain); + ExFreePoolWithTag(pCache, TAG_FONT); + } +} + +static inline VOID +IntRebaseList( + _Inout_ PLIST_ENTRY pNewHead, + _Inout_ PLIST_ENTRY pOldHead) +{ + PLIST_ENTRY Entry; + + ASSERT(pNewHead != pOldHead); + + InitializeListHead(pNewHead); + while (!IsListEmpty(pOldHead)) + { + Entry = RemoveTailList(pOldHead); + InsertHeadList(pNewHead, Entry); + } +} + +/// Add the chain to the cache (g_FontLinkCache) if the chain had been populated. +/// @param pChain The chain. +static inline VOID +FontLink_Chain_Finish( + _Inout_ PFONTLINK_CHAIN pChain) +{ + PFONTLINK_CACHE pCache; + + if (!FontLink_Chain_IsPopulated(pChain)) + return; // The chain is not populated yet + + pCache = ExAllocatePoolWithTag(PagedPool, sizeof(FONTLINK_CACHE), TAG_FONT); + if (!pCache) + return; // Out of memory + + pCache->LogFont = pChain->LogFont; + pCache->Chain = *pChain; + IntRebaseList(&pCache->Chain.FontLinkList, &pChain->FontLinkList); + + FontLink_AddCache(pCache); +} + +static inline PFONTLINK_CACHE +FontLink_FindCache( + _In_ const LOGFONTW* pLogFont) +{ + PLIST_ENTRY Entry; + PFONTLINK_CACHE pLinkCache; + for (Entry = g_FontLinkCache.Flink; Entry != &g_FontLinkCache; Entry = Entry->Flink) + { + pLinkCache = CONTAINING_RECORD(Entry, FONTLINK_CACHE, ListEntry); + if (RtlEqualMemory(&pLinkCache->LogFont, pLogFont, sizeof(LOGFONTW))) + return pLinkCache; + } + return NULL; +} + +static inline VOID +FontLink_CleanupCache(VOID) +{ + PLIST_ENTRY Entry; + PFONTLINK_CACHE pLinkCache; + + while (!IsListEmpty(&g_FontLinkCache)) + { + Entry = RemoveHeadList(&g_FontLinkCache); + pLinkCache = CONTAINING_RECORD(Entry, FONTLINK_CACHE, ListEntry); + FontLink_Chain_Free(&pLinkCache->Chain); + ExFreePoolWithTag(pLinkCache, TAG_FONT); + } + + g_nFontLinkCacheCount = 0; +} + /* The ranges of the surrogate pairs */ #define HIGH_SURROGATE_MIN 0xD800U #define HIGH_SURROGATE_MAX 0xDBFFU @@ -79,30 +367,26 @@ static UNICODE_STRING g_FontRegPath = static PFAST_MUTEX g_FreeTypeLock; static RTL_STATIC_LIST_HEAD(g_FontListHead); -static PFAST_MUTEX g_FontListLock; static BOOL g_RenderingEnabled = TRUE; -#define IntLockGlobalFonts() \ - ExEnterCriticalRegionAndAcquireFastMutexUnsafe(g_FontListLock) - -#define IntUnLockGlobalFonts() \ - ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(g_FontListLock) - -#define ASSERT_GLOBALFONTS_LOCK_HELD() \ - ASSERT(g_FontListLock->Owner == KeGetCurrentThread()) - -#define IntLockFreeType() \ - ExEnterCriticalRegionAndAcquireFastMutexUnsafe(g_FreeTypeLock) - -#define IntUnLockFreeType() \ - ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(g_FreeTypeLock) - #define ASSERT_FREETYPE_LOCK_HELD() \ ASSERT(g_FreeTypeLock->Owner == KeGetCurrentThread()) #define ASSERT_FREETYPE_LOCK_NOT_HELD() \ ASSERT(g_FreeTypeLock->Owner != KeGetCurrentThread()) +#define IntLockFreeType() \ +do { \ + ASSERT_FREETYPE_LOCK_NOT_HELD(); \ + ExEnterCriticalRegionAndAcquireFastMutexUnsafe(g_FreeTypeLock); \ +} while (0) + +#define IntUnLockFreeType() \ +do { \ + ASSERT_FREETYPE_LOCK_HELD(); \ + ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(g_FreeTypeLock); \ +} while(0) + #define MAX_FONT_CACHE 256 static RTL_STATIC_LIST_HEAD(g_FontCacheListHead); @@ -206,6 +490,65 @@ BYTE FASTCALL IntCharSetFromCodePage(UINT uCodePage) return DEFAULT_CHARSET; } +static __inline VOID +FindBestFontFromList(FONTOBJ **FontObj, ULONG *MatchPenalty, + const LOGFONTW *LogFont, + const PLIST_ENTRY Head); + +static BOOL +MatchFontName(PSHARED_FACE SharedFace, PUNICODE_STRING Name1, FT_UShort NameID, FT_UShort LangID); + +static BOOL +FontLink_PrepareFontInfo( + _Inout_ PFONTLINK pFontLink) +{ + FONTOBJ *pFontObj; + ULONG MatchPenalty; + UNICODE_STRING FaceName; + PPROCESSINFO Win32Process; + PFONTGDI pFontGDI; + + ASSERT_FREETYPE_LOCK_HELD(); + + if (pFontLink->bIgnore) + return FALSE; + + if (pFontLink->SharedFace) + return TRUE; + + MatchPenalty = MAXULONG; + pFontObj = NULL; + + // Search private fonts + Win32Process = PsGetCurrentProcessWin32Process(); + FindBestFontFromList(&pFontObj, &MatchPenalty, &pFontLink->LogFont, + &Win32Process->PrivateFontListHead); + + // Search system fonts + FindBestFontFromList(&pFontObj, &MatchPenalty, &pFontLink->LogFont, + &g_FontListHead); + + if (!pFontObj) // Not found? + { + pFontLink->bIgnore = TRUE; + return FALSE; + } + + pFontGDI = ObjToGDI(pFontObj, FONT); + pFontLink->SharedFace = pFontGDI->SharedFace; + + // FontLink uses family name + RtlInitUnicodeString(&FaceName, pFontLink->LogFont.lfFaceName); + if (!MatchFontName(pFontLink->SharedFace, &FaceName, TT_NAME_ID_FONT_FAMILY, LANG_ENGLISH) && + !MatchFontName(pFontLink->SharedFace, &FaceName, TT_NAME_ID_FONT_FAMILY, gusLanguageID)) + { + pFontLink->bIgnore = TRUE; + return FALSE; + } + + return TRUE; +} + /* list head */ static RTL_STATIC_LIST_HEAD(g_FontSubstListHead); @@ -500,12 +843,12 @@ VOID DumpPrivateFontList(BOOL bDoLock) VOID DumpGlobalFontList(BOOL bDoLock) { if (bDoLock) - IntLockGlobalFonts(); + IntLockFreeType(); DumpFontList(&g_FontListHead); if (bDoLock) - IntUnLockGlobalFonts(); + IntUnLockFreeType(); } VOID DumpFontInfo(BOOL bDoLock) @@ -672,14 +1015,7 @@ InitFontSupport(VOID) ULONG ulError; g_FontCacheNumEntries = 0; - /* Fast Mutexes must be allocated from non paged pool */ - g_FontListLock = ExAllocatePoolWithTag(NonPagedPool, sizeof(FAST_MUTEX), TAG_INTERNAL_SYNC); - if (g_FontListLock == NULL) - { - return FALSE; - } - ExInitializeFastMutex(g_FontListLock); g_FreeTypeLock = ExAllocatePoolWithTag(NonPagedPool, sizeof(FAST_MUTEX), TAG_INTERNAL_SYNC); if (g_FreeTypeLock == NULL) { @@ -704,10 +1040,14 @@ InitFontSupport(VOID) IntLoadFontSubstList(&g_FontSubstListHead); -#if DBG +#if 0 DumpFontInfo(TRUE); #endif + FontLink_LoadSettings(); + FontLink_LoadDefaultFonts(); + FontLink_LoadDefaultCharset(); + return TRUE; } @@ -719,7 +1059,8 @@ FreeFontSupport(VOID) PFONTSUBST_ENTRY pSubstEntry; PFONT_ENTRY pFontEntry; - IntLockGlobalFonts(); + // Cleanup the FontLink cache + FontLink_CleanupCache(); // Free font cache list pHead = &g_FontCacheListHead; @@ -754,11 +1095,6 @@ FreeFontSupport(VOID) g_FreeTypeLibrary = NULL; } - IntUnLockGlobalFonts(); - - ExFreePoolWithTag(g_FontListLock, TAG_INTERNAL_SYNC); - g_FontListLock = NULL; - ExFreePoolWithTag(g_FreeTypeLock, TAG_INTERNAL_SYNC); g_FreeTypeLock = NULL; } @@ -912,7 +1248,7 @@ DuplicateUnicodeString(PUNICODE_STRING Source, PUNICODE_STRING Destination) } static BOOL -SubstituteFontRecurse(LOGFONTW* pLogFont) +SubstituteFontRecurse(PLOGFONTW pLogFont) { UINT RecurseCount = 5; UNICODE_STRING OutputNameW = { 0 }; @@ -946,6 +1282,269 @@ SubstituteFontRecurse(LOGFONTW* pLogFont) return TRUE; /* success */ } +// Quickly initialize a FONTLINK_CHAIN +static inline VOID +FontLink_Chain_Init( + _Out_ PFONTLINK_CHAIN pChain, + _Inout_ PTEXTOBJ pTextObj, + _In_ FT_Face face) +{ + RtlZeroMemory(pChain, sizeof(*pChain)); + pChain->pBaseTextObj = pTextObj; + pChain->pDefFace = face; + ASSERT(!FontLink_Chain_IsPopulated(pChain)); +} + +// The default FontLink data +static const WCHAR s_szzDefFontLink[] = + L"tahoma.ttf,Tahoma\0" + L"msgothic.ttc,MS UI Gothic\0" + L"mingliu.ttc,PMingLiU\0" + L"simsun.ttc,SimSun\0" + L"gulim.ttc,Gulim\0" + L"\0"; +// The default fixed-pitch FontLink data +static const WCHAR s_szzDefFixedFontLink[] = + L"cour.ttf,Courier New\0" + L"msgothic.ttc,MS Gothic\0" + L"mingliu.ttc,MingLiU\0" + L"simsun.ttc,NSimSun\0" + L"gulim.ttc,GulimChe\0" + L"\0"; + +static NTSTATUS +FontLink_Chain_LoadReg( + _Inout_ PFONTLINK_CHAIN pChain, + _Inout_ PLOGFONTW pLF) +{ + NTSTATUS Status; + HKEY hKey; + DWORD cbData; + WCHAR szzFontLink[512]; + SIZE_T FontLinkSize; + PZZWSTR pszzFontLink = NULL; + + ASSERT(pLF->lfFaceName[0]); + + // Open the registry key + Status = RegOpenKey( + L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion\\FontLink\\SystemLink", + &hKey); + if (!NT_SUCCESS(Status)) + return Status; + + // Load the FontLink entry + cbData = sizeof(szzFontLink); + Status = RegQueryValue(hKey, pLF->lfFaceName, REG_MULTI_SZ, szzFontLink, &cbData); + if (!NT_SUCCESS(Status) && + (Status != STATUS_BUFFER_OVERFLOW) && (Status != STATUS_BUFFER_TOO_SMALL)) + { + // Retry with substituted + SubstituteFontRecurse(pLF); + cbData = sizeof(szzFontLink); + Status = RegQueryValue(hKey, pLF->lfFaceName, REG_MULTI_SZ, szzFontLink, &cbData); + } + + if ((Status == STATUS_BUFFER_OVERFLOW) || (Status == STATUS_BUFFER_TOO_SMALL)) + { + // Buffer is too small. Retry with larger buffer + if (cbData >= 2 * sizeof(WCHAR)) // Sanity check + { + FontLinkSize = cbData; + pszzFontLink = ExAllocatePoolWithTag(PagedPool, FontLinkSize, TAG_FONT); + if (!pszzFontLink) + { + ZwClose(hKey); // Close the registry key + return STATUS_NO_MEMORY; + } + Status = RegQueryValue(hKey, pLF->lfFaceName, REG_MULTI_SZ, pszzFontLink, &cbData); + if (!NT_SUCCESS(Status)) + { + ExFreePoolWithTag(pszzFontLink, TAG_FONT); + pszzFontLink = NULL; + } + } + } + + ZwClose(hKey); // Close the registry key + + if (!NT_SUCCESS(Status)) // Failed to get registry value + { + // Use default value + ASSERT(sizeof(szzFontLink) >= sizeof(s_szzDefFontLink)); + ASSERT(sizeof(szzFontLink) >= sizeof(s_szzDefFixedFontLink)); + if (!(pLF->lfPitchAndFamily & FIXED_PITCH)) + RtlCopyMemory(szzFontLink, s_szzDefFontLink, sizeof(s_szzDefFontLink)); + else + RtlCopyMemory(szzFontLink, s_szzDefFixedFontLink, sizeof(s_szzDefFixedFontLink)); + } + + if (pszzFontLink) + { + // Ensure double-NUL-terminated + ASSERT(FontLinkSize / sizeof(WCHAR) >= 2); + pszzFontLink[FontLinkSize / sizeof(WCHAR) - 1] = UNICODE_NULL; + pszzFontLink[FontLinkSize / sizeof(WCHAR) - 2] = UNICODE_NULL; + } + else + { + // Ensure double-NUL-terminated + szzFontLink[_countof(szzFontLink) - 1] = UNICODE_NULL; + szzFontLink[_countof(szzFontLink) - 2] = UNICODE_NULL; + + FontLinkSize = SZZ_GetSize(szzFontLink); + pszzFontLink = ExAllocatePoolWithTag(PagedPool, FontLinkSize, TAG_FONT); + if (!pszzFontLink) + return STATUS_NO_MEMORY; + RtlCopyMemory(pszzFontLink, szzFontLink, FontLinkSize); + } + pChain->pszzFontLink = pszzFontLink; + + return STATUS_SUCCESS; +} + +static inline PFONTLINK +FontLink_Chain_FindLink( + PFONTLINK_CHAIN pChain, + PLOGFONTW plf) +{ + PLIST_ENTRY Entry, Head = &pChain->FontLinkList; + PFONTLINK pLink; + + for (Entry = Head->Flink; Entry != Head; Entry = Entry->Flink) + { + pLink = CONTAINING_RECORD(Entry, FONTLINK, ListEntry); + if (RtlEqualMemory(&pLink->LogFont, plf, sizeof(*plf))) + return pLink; + } + + return NULL; +} + +static inline PFONTLINK +FontLink_Create( + _Inout_ PFONTLINK_CHAIN pChain, + _In_ const LOGFONTW *plfBase, + _In_ LPCWSTR pszLink) +{ + LPWSTR pch0, pch1; + LOGFONTW lf; + PFONTLINK pLink; + + lf = *plfBase; + + // pszLink: ",[,...]" + pch0 = wcschr(pszLink, L','); + if (!pch0) + { + DPRINT1("%S\n", pszLink); + return NULL; // Invalid FontLink data + } + ++pch0; + + pch1 = wcschr(pch0, L','); + if (pch1) + RtlStringCchCopyNW(lf.lfFaceName, _countof(lf.lfFaceName), pch0, pch1 - pch0); + else + RtlStringCchCopyW(lf.lfFaceName, _countof(lf.lfFaceName), pch0); + + SubstituteFontRecurse(&lf); + DPRINT("lfFaceName: %S\n", lf.lfFaceName); + + if (RtlEqualMemory(plfBase, &lf, sizeof(lf)) || FontLink_Chain_FindLink(pChain, &lf)) + return NULL; // Already exists + + pLink = ExAllocatePoolZero(PagedPool, sizeof(FONTLINK), TAG_FONT); + if (!pLink) + return NULL; // Out of memory + + pLink->LogFont = lf; + return pLink; +} + +static NTSTATUS +FontLink_Chain_Populate( + _Inout_ PFONTLINK_CHAIN pChain) +{ + NTSTATUS Status; + PFONTLINK pLink; + LOGFONTW lfBase; + PTEXTOBJ pTextObj = pChain->pBaseTextObj; + PFONTGDI pFontGDI; + PWSTR pszLink; + PFONTLINK_CACHE pLinkCache; + WCHAR szEntry[MAX_PATH]; + BOOL bFixCharSet; + + InitializeListHead(&pChain->FontLinkList); + + lfBase = pTextObj->logfont.elfEnumLogfontEx.elfLogFont; + pFontGDI = ObjToGDI(pTextObj->Font, FONT); + lfBase.lfHeight = pFontGDI->lfHeight; + lfBase.lfWidth = pFontGDI->lfWidth; + + // Use pTextObj->TextFace if lfFaceName was empty + if (!lfBase.lfFaceName[0]) + { + ASSERT(pTextObj->TextFace[0]); + RtlStringCchCopyW(lfBase.lfFaceName, _countof(lfBase.lfFaceName), pTextObj->TextFace); + } + + // Fix lfCharSet + switch (lfBase.lfCharSet) + { + case ANSI_CHARSET: bFixCharSet = !s_fFontLinkUseAnsi; break; + case OEM_CHARSET: bFixCharSet = !s_fFontLinkUseOem; break; + case SYMBOL_CHARSET: bFixCharSet = !s_fFontLinkUseSymbol; break; + default: bFixCharSet = TRUE; break; + } + if (bFixCharSet) + lfBase.lfCharSet = DEFAULT_CHARSET; + + // Use cache if any + pLinkCache = FontLink_FindCache(&lfBase); + if (pLinkCache) + { + RemoveEntryList(&pLinkCache->ListEntry); + *pChain = pLinkCache->Chain; + IntRebaseList(&pChain->FontLinkList, &pLinkCache->Chain.FontLinkList); + ExFreePoolWithTag(pLinkCache, TAG_FONT); + return STATUS_SUCCESS; + } + + pChain->LogFont = lfBase; + + // Load FontLink entry from registry + Status = FontLink_Chain_LoadReg(pChain, &pChain->LogFont); + if (!NT_SUCCESS(Status)) + return Status; + + pszLink = pChain->pszzFontLink; + while (*pszLink) + { + DPRINT("pszLink: '%S'\n", pszLink); + pLink = FontLink_Create(pChain, &lfBase, pszLink); + if (pLink) + InsertTailList(&pChain->FontLinkList, &pLink->ListEntry); + pszLink += wcslen(pszLink) + 1; + } + + // Use default settings (if any) + if (s_szDefFontLinkFileName[0] && s_szDefFontLinkFontName[0]) + { + RtlStringCchCopyW(szEntry, _countof(szEntry), s_szDefFontLinkFileName); + RtlStringCchCatW(szEntry, _countof(szEntry), L","); + RtlStringCchCatW(szEntry, _countof(szEntry), s_szDefFontLinkFontName); + DPRINT("szEntry: '%S'\n", szEntry); + pLink = FontLink_Create(pChain, &lfBase, szEntry); + if (pLink) + InsertTailList(&pChain->FontLinkList, &pLink->ListEntry); + } + + ASSERT(FontLink_Chain_IsPopulated(pChain)); + return Status; +} + /* * IntLoadSystemFonts * @@ -1373,9 +1972,9 @@ IntGdiLoadFontsFromMemory(PGDI_LOAD_FONT pLoadFont, else { /* global font */ - IntLockGlobalFonts(); + IntLockFreeType(); InsertTailList(&g_FontListHead, &Entry->ListEntry); - IntUnLockGlobalFonts(); + IntUnLockFreeType(); } if (FontIndex == -1) @@ -2460,7 +3059,8 @@ IntFreeFontNames(FONT_NAMES *Names) INT FASTCALL IntGetOutlineTextMetrics(PFONTGDI FontGDI, UINT Size, - OUTLINETEXTMETRICW *Otm) + OUTLINETEXTMETRICW *Otm, + BOOL bLocked) { TT_OS2 *pOS2; TT_HoriHeader *pHori; @@ -2474,6 +3074,11 @@ IntGetOutlineTextMetrics(PFONTGDI FontGDI, PSHARED_FACE_CACHE Cache; FT_Face Face = SharedFace->Face; + if (bLocked) + ASSERT_FREETYPE_LOCK_HELD(); + else + ASSERT_FREETYPE_LOCK_NOT_HELD(); + if (PRIMARYLANGID(gusLanguageID) == LANG_ENGLISH) { Cache = &SharedFace->EnglishUS; @@ -2489,6 +3094,9 @@ IntGetOutlineTextMetrics(PFONTGDI FontGDI, return Cache->OutlineRequiredSize; } + if (!bLocked) + IntLockFreeType(); + IntInitFontNames(&FontNames, SharedFace); Cache->OutlineRequiredSize = FontNames.OtmSize; @@ -2496,6 +3104,8 @@ IntGetOutlineTextMetrics(PFONTGDI FontGDI, { ASSERT(Otm == NULL); IntFreeFontNames(&FontNames); + if (!bLocked) + IntUnLockFreeType(); return Cache->OutlineRequiredSize; } @@ -2506,14 +3116,14 @@ IntGetOutlineTextMetrics(PFONTGDI FontGDI, DPRINT1("Size %u < OutlineRequiredSize %u\n", Size, Cache->OutlineRequiredSize); IntFreeFontNames(&FontNames); + if (!bLocked) + IntUnLockFreeType(); return 0; /* failure */ } XScale = Face->size->metrics.x_scale; YScale = Face->size->metrics.y_scale; - IntLockFreeType(); - pOS2 = FT_Get_Sfnt_Table(Face, FT_SFNT_OS2); pHori = FT_Get_Sfnt_Table(Face, FT_SFNT_HHEA); pPost = FT_Get_Sfnt_Table(Face, FT_SFNT_POST); /* We can live with this failing */ @@ -2521,7 +3131,8 @@ IntGetOutlineTextMetrics(PFONTGDI FontGDI, if (pOS2 == NULL && Error) { - IntUnLockFreeType(); + if (!bLocked) + IntUnLockFreeType(); DPRINT1("Can't find OS/2 table - not TT font?\n"); IntFreeFontNames(&FontNames); return 0; @@ -2529,7 +3140,8 @@ IntGetOutlineTextMetrics(PFONTGDI FontGDI, if (pHori == NULL && Error) { - IntUnLockFreeType(); + if (!bLocked) + IntUnLockFreeType(); DPRINT1("Can't find HHEA table - not TT font?\n"); IntFreeFontNames(&FontNames); return 0; @@ -2593,7 +3205,8 @@ IntGetOutlineTextMetrics(PFONTGDI FontGDI, #undef SCALE_Y skip_os2: - IntUnLockFreeType(); + if (!bLocked) + IntUnLockFreeType(); pb = IntStoreFontNames(&FontNames, Otm); ASSERT(pb - (BYTE*)Otm == Cache->OutlineRequiredSize); @@ -2803,19 +3416,15 @@ IntGetFontLocalizedName(PUNICODE_STRING pNameW, PSHARED_FACE SharedFace, /* make cache */ if (NameID == TT_NAME_ID_FONT_FAMILY) { - ASSERT_FREETYPE_LOCK_NOT_HELD(); - IntLockFreeType(); + ASSERT_FREETYPE_LOCK_HELD(); if (!Cache->FontFamily.Buffer) DuplicateUnicodeString(pNameW, &Cache->FontFamily); - IntUnLockFreeType(); } else if (NameID == TT_NAME_ID_FULL_NAME) { - ASSERT_FREETYPE_LOCK_NOT_HELD(); - IntLockFreeType(); + ASSERT_FREETYPE_LOCK_HELD(); if (!Cache->FullName.Buffer) DuplicateUnicodeString(pNameW, &Cache->FullName); - IntUnLockFreeType(); } } @@ -2844,13 +3453,15 @@ FontFamilyFillInfo(PFONTFAMILYINFO Info, LPCWSTR FaceName, RtlInitUnicodeString(&NameW, NULL); RtlZeroMemory(Info, sizeof(FONTFAMILYINFO)); - Size = IntGetOutlineTextMetrics(FontGDI, 0, NULL); + ASSERT_FREETYPE_LOCK_HELD(); + Size = IntGetOutlineTextMetrics(FontGDI, 0, NULL, TRUE); Otm = ExAllocatePoolWithTag(PagedPool, Size, GDITAG_TEXT); if (!Otm) { return; } - Size = IntGetOutlineTextMetrics(FontGDI, Size, Otm); + ASSERT_FREETYPE_LOCK_HELD(); + Size = IntGetOutlineTextMetrics(FontGDI, Size, Otm, TRUE); if (!Size) { ExFreePoolWithTag(Otm, GDITAG_TEXT); @@ -2929,12 +3540,10 @@ FontFamilyFillInfo(PFONTFAMILYINFO Info, LPCWSTR FaceName, } Info->EnumLogFontEx.elfScript[0] = UNICODE_NULL; - IntLockFreeType(); pOS2 = FT_Get_Sfnt_Table(Face, ft_sfnt_os2); if (!pOS2) { - IntUnLockFreeType(); ExFreePoolWithTag(Otm, GDITAG_TEXT); return; } @@ -2961,7 +3570,6 @@ FontFamilyFillInfo(PFONTFAMILYINFO Info, LPCWSTR FaceName, else fs.fsCsb[0] |= FS_SYMBOL; } - IntUnLockFreeType(); if (fs.fsCsb[0] == 0) { @@ -3110,9 +3718,9 @@ GetFontFamilyInfoForSubstitutes(const LOGFONTW *LogFont, continue; /* search in global fonts */ - IntLockGlobalFonts(); + IntLockFreeType(); GetFontFamilyInfoForList(&lf, Info, pFromW->Buffer, pCount, MaxCount, &g_FontListHead); - IntUnLockGlobalFonts(); + IntUnLockFreeType(); /* search in private fonts */ IntLockProcessPrivateFonts(Win32Process); @@ -3724,6 +4332,87 @@ get_glyph_index_flagged(FT_Face face, FT_ULong code, BOOL fCodeAsIndex) return (fCodeAsIndex ? code : get_glyph_index(face, code)); } +static inline VOID +FontLink_Chain_Dump( + _In_ PFONTLINK_CHAIN pChain) +{ +#if 0 + PLIST_ENTRY Entry, Head; + PFONTLINK pFontLink; + INT iLink = 0; + + DPRINT1("%S, %p, %p\n", pChain->LogFont.lfFaceName, pChain->pBaseTextObj, pChain->pDefFace); + + Head = &pChain->FontLinkList; + for (Entry = Head->Flink; Entry != Head; Entry = Entry->Flink) + { + pFontLink = CONTAINING_RECORD(Entry, FONTLINK, ListEntry); + DPRINT1("FontLink #%d: %p, %d, %S, %p, %p\n", + iLink, pFontLink, pFontLink->bIgnore, pFontLink->LogFont.lfFaceName, + pFontLink->pFontGDI, pFontLink->SharedFace); + ++iLink; + } +#endif +} + +/// Search the target glyph and update the current font info. +/// @return The glyph index +static UINT +FontLink_Chain_FindGlyph( + _Inout_ PFONTLINK_CHAIN pChain, + _Out_ PFONT_CACHE_ENTRY pCache, + _Inout_ FT_Face *pFace, + _In_ UINT code, + _In_ BOOL fCodeAsIndex) +{ + PFONTLINK pFontLink; + PLIST_ENTRY Entry, Head; + UINT index; + FT_Face face; + + // Try the default font at first + index = get_glyph_index_flagged(pChain->pDefFace, code, fCodeAsIndex); + if (index) + { + DPRINT("code: 0x%08X, index: 0x%08X, fCodeAsIndex:%d\n", code, index, fCodeAsIndex); + pCache->Hashed.Face = *pFace = pChain->pDefFace; + return index; // The glyph is found on the default font + } + + if (!FontLink_Chain_IsPopulated(pChain)) // The chain is not populated yet + { + FontLink_Chain_Populate(pChain); + FontLink_Chain_Dump(pChain); + } + + // Now the chain is populated. Looking for the target glyph... + Head = &pChain->FontLinkList; + for (Entry = Head->Flink; Entry != Head; Entry = Entry->Flink) + { + pFontLink = CONTAINING_RECORD(Entry, FONTLINK, ListEntry); + if (!FontLink_PrepareFontInfo(pFontLink)) + continue; // This link is not useful, check the next one + + face = pFontLink->SharedFace->Face; + index = get_glyph_index(face, code); + if (!index) + continue; // The glyph does not exist, continue searching + + // The target glyph is found in the chain + DPRINT("code: 0x%08X, index: 0x%08X\n", code, index); + pCache->Hashed.Face = *pFace = face; + FT_Set_Transform(face, &pCache->Hashed.matTransform, NULL); + return index; + } + + // No target glyph found in the chain: use default glyph + code = s_chFontLinkDefaultChar; + index = get_glyph_index(*pFace, code); + DPRINT("code: 0x%08X, index: 0x%08X\n", code, index); + pCache->Hashed.Face = *pFace = pChain->pDefFace; + return index; +} + /* * Based on WineEngGetGlyphOutline */ @@ -3787,7 +4476,8 @@ ftGdiGetGlyphOutline( aveWidth = FT_IS_SCALABLE(ft_face) ? abs(plf->lfWidth) : 0; orientation = FT_IS_SCALABLE(ft_face) ? plf->lfOrientation : 0; - Size = IntGetOutlineTextMetrics(FontGDI, 0, NULL); + ASSERT_FREETYPE_LOCK_NOT_HELD(); + Size = IntGetOutlineTextMetrics(FontGDI, 0, NULL, FALSE); potm = ExAllocatePoolWithTag(PagedPool, Size, GDITAG_TEXT); if (!potm) { @@ -3795,7 +4485,8 @@ ftGdiGetGlyphOutline( TEXTOBJ_UnlockText(TextObj); return GDI_ERROR; } - Size = IntGetOutlineTextMetrics(FontGDI, Size, potm); + ASSERT_FREETYPE_LOCK_NOT_HELD(); + Size = IntGetOutlineTextMetrics(FontGDI, Size, potm, FALSE); if (!Size) { /* FIXME: last error? */ @@ -3984,7 +4675,6 @@ ftGdiGetGlyphOutline( IntUnLockFreeType(); - if (iFormat == GGO_METRICS) { DPRINT("GGO_METRICS Exit!\n"); @@ -4269,6 +4959,7 @@ TextIntGetTextExtentPoint(PDC dc, LONG ascender, descender; FONT_CACHE_ENTRY Cache; DWORD ch0, ch1; + FONTLINK_CHAIN Chain; FontGDI = ObjToGDI(TextObj->Font, FONT); @@ -4299,6 +4990,8 @@ TextIntGetTextExtentPoint(PDC dc, Cache.Hashed.matTransform = identityMat; FT_Set_Transform(Cache.Hashed.Face, NULL, NULL); + FontLink_Chain_Init(&Chain, TextObj, Cache.Hashed.Face); + use_kerning = FT_HAS_KERNING(Cache.Hashed.Face); previous = 0; @@ -4316,7 +5009,8 @@ TextIntGetTextExtentPoint(PDC dc, ch0 = Utf32FromSurrogatePair(ch0, ch1); } - glyph_index = get_glyph_index_flagged(Cache.Hashed.Face, ch0, (fl & GTEF_INDICES)); + glyph_index = FontLink_Chain_FindGlyph(&Chain, &Cache, &Cache.Hashed.Face, ch0, + (fl & GTEF_INDICES)); Cache.Hashed.GlyphIndex = glyph_index; realglyph = IntGetRealGlyph(&Cache); @@ -4360,6 +5054,8 @@ TextIntGetTextExtentPoint(PDC dc, Size->cy = ascender + descender; } + FontLink_Chain_Finish(&Chain); + return TRUE; } @@ -5069,7 +5765,8 @@ FindBestFontFromList(FONTOBJ **FontObj, ULONG *MatchPenalty, Face = FontGDI->SharedFace->Face; /* get text metrics */ - OtmSize = IntGetOutlineTextMetrics(FontGDI, 0, NULL); + ASSERT_FREETYPE_LOCK_HELD(); + OtmSize = IntGetOutlineTextMetrics(FontGDI, 0, NULL, TRUE); if (OtmSize > OldOtmSize) { if (Otm) @@ -5080,18 +5777,18 @@ FindBestFontFromList(FONTOBJ **FontObj, ULONG *MatchPenalty, /* update FontObj if lowest penalty */ if (Otm) { - IntLockFreeType(); + ASSERT_FREETYPE_LOCK_HELD(); IntRequestFontSize(NULL, FontGDI, LogFont->lfWidth, LogFont->lfHeight); - IntUnLockFreeType(); - OtmSize = IntGetOutlineTextMetrics(FontGDI, OtmSize, Otm); + ASSERT_FREETYPE_LOCK_HELD(); + OtmSize = IntGetOutlineTextMetrics(FontGDI, OtmSize, Otm, TRUE); if (!OtmSize) continue; OldOtmSize = OtmSize; Penalty = GetFontPenalty(LogFont, Otm, Face->style_name); - if (*MatchPenalty == 0xFFFFFFFF || Penalty < *MatchPenalty) + if (*MatchPenalty == MAXULONG || Penalty < *MatchPenalty) { *FontObj = GDIToObj(FontGDI, FONT); *MatchPenalty = Penalty; @@ -5239,10 +5936,10 @@ TextIntRealizeFont(HFONT FontHandle, PTEXTOBJ pTextObj) IntUnLockProcessPrivateFonts(Win32Process); /* Search system fonts */ - IntLockGlobalFonts(); + IntLockFreeType(); FindBestFontFromList(&TextObj->Font, &MatchPenalty, &SubstitutedLogFont, &g_FontListHead); - IntUnLockGlobalFonts(); + IntUnLockFreeType(); if (NULL == TextObj->Font) { @@ -5257,14 +5954,17 @@ TextIntRealizeFont(HFONT FontHandle, PTEXTOBJ pTextObj) PSHARED_FACE SharedFace = FontGdi->SharedFace; TextObj->TextFace[0] = UNICODE_NULL; + IntLockFreeType(); if (MatchFontNames(SharedFace, SubstitutedLogFont.lfFaceName)) { + IntUnLockFreeType(); RtlStringCchCopyW(TextObj->TextFace, _countof(TextObj->TextFace), pLogFont->lfFaceName); } else { RtlInitUnicodeString(&Name, NULL); Status = IntGetFontLocalizedName(&Name, SharedFace, TT_NAME_ID_FONT_FAMILY, gusLanguageID); + IntUnLockFreeType(); if (NT_SUCCESS(Status)) { /* truncated copy */ @@ -5297,7 +5997,6 @@ TextIntRealizeFont(HFONT FontHandle, PTEXTOBJ pTextObj) return Status; } - static BOOL FASTCALL @@ -5433,7 +6132,7 @@ IntGdiGetFontResourceInfo( Count = 0; /* Try to find the pathname in the global font list */ - IntLockGlobalFonts(); + IntLockFreeType(); for (ListEntry = g_FontListHead.Flink; ListEntry != &g_FontListHead; ListEntry = ListEntry->Flink) { @@ -5467,7 +6166,7 @@ IntGdiGetFontResourceInfo( break; } } - IntUnLockGlobalFonts(); + IntUnLockFreeType(); /* Free the buffers */ ExFreePoolWithTag(NameInfo1, TAG_FINF); @@ -5725,14 +6424,14 @@ IntGetFontFamilyInfo(HDC Dc, PPROCESSINFO Win32Process; /* Enumerate font families in the global list */ - IntLockGlobalFonts(); + IntLockFreeType(); if (!GetFontFamilyInfoForList(SafeLogFont, SafeInfo, NULL, &AvailCount, InfoCount, &g_FontListHead)) { - IntUnLockGlobalFonts(); + IntUnLockFreeType(); return -1; } - IntUnLockGlobalFonts(); + IntUnLockFreeType(); /* Enumerate font families in the process local list */ Win32Process = PsGetCurrentProcessWin32Process(); @@ -5880,7 +6579,8 @@ IntGetTextDisposition( IN OPTIONAL LPINT Dx, IN OUT PFONT_CACHE_ENTRY Cache, IN UINT fuOptions, - IN BOOL bNoTransform) + IN BOOL bNoTransform, + IN OUT PFONTLINK_CHAIN pChain) { LONGLONG X64 = 0, Y64 = 0; INT i, glyph_index; @@ -5907,7 +6607,8 @@ IntGetTextDisposition( ch0 = Utf32FromSurrogatePair(ch0, ch1); } - glyph_index = get_glyph_index_flagged(face, ch0, (fuOptions & ETO_GLYPH_INDEX)); + glyph_index = FontLink_Chain_FindGlyph(pChain, Cache, &face, ch0, + (fuOptions & ETO_GLYPH_INDEX)); Cache->Hashed.GlyphIndex = glyph_index; realglyph = IntGetRealGlyph(Cache); @@ -6079,6 +6780,7 @@ IntExtTextOutW( FT_Matrix mat; BOOL bNoTransform; DWORD ch0, ch1; + FONTLINK_CHAIN Chain; /* Check if String is valid */ if (Count > 0xFFFF || (Count > 0 && String == NULL)) @@ -6194,6 +6896,8 @@ IntExtTextOutW( goto Cleanup; } + FontLink_Chain_Init(&Chain, TextObj, face); + /* Apply lfEscapement */ if (FT_IS_SCALABLE(face) && plf->lfEscapement != 0) IntEscapeMatrix(&Cache.Hashed.matTransform, plf->lfEscapement); @@ -6241,8 +6945,9 @@ IntExtTextOutW( if ((fuOptions & ETO_OPAQUE) || (pdcattr->flTextAlign & (TA_CENTER | TA_RIGHT))) { if (!IntGetTextDisposition(&DeltaX64, &DeltaY64, String, Count, Dx, &Cache, - fuOptions, bNoTransform)) + fuOptions, bNoTransform, &Chain)) { + FontLink_Chain_Finish(&Chain); IntUnLockFreeType(); bResult = FALSE; goto Cleanup; @@ -6316,7 +7021,8 @@ IntExtTextOutW( ch0 = Utf32FromSurrogatePair(ch0, ch1); } - glyph_index = get_glyph_index_flagged(face, ch0, (fuOptions & ETO_GLYPH_INDEX)); + glyph_index = FontLink_Chain_FindGlyph(&Chain, &Cache, &face, ch0, + (fuOptions & ETO_GLYPH_INDEX)); Cache.Hashed.GlyphIndex = glyph_index; realglyph = IntGetRealGlyph(&Cache); @@ -6541,6 +7247,8 @@ IntExtTextOutW( } } + FontLink_Chain_Finish(&Chain); + IntUnLockFreeType(); EXLATEOBJ_vCleanup(&exloRGB2Dst); @@ -7191,7 +7899,8 @@ NtGdiGetGlyphIndicesW( } else { - Size = IntGetOutlineTextMetrics(FontGDI, 0, NULL); + ASSERT_FREETYPE_LOCK_NOT_HELD(); + Size = IntGetOutlineTextMetrics(FontGDI, 0, NULL, FALSE); if (!Size) { Status = STATUS_UNSUCCESSFUL; @@ -7205,7 +7914,8 @@ NtGdiGetGlyphIndicesW( DPRINT1("!potm\n"); goto ErrorRet; } - Size = IntGetOutlineTextMetrics(FontGDI, Size, potm); + ASSERT_FREETYPE_LOCK_NOT_HELD(); + Size = IntGetOutlineTextMetrics(FontGDI, Size, potm, FALSE); if (Size) DefChar = potm->otmTextMetrics.tmDefaultChar; ExFreePoolWithTag(potm, GDITAG_TEXT); diff --git a/win32ss/gdi/ntgdi/text.h b/win32ss/gdi/ntgdi/text.h index dd4443bb363ce..1a8ca9202fc0a 100644 --- a/win32ss/gdi/ntgdi/text.h +++ b/win32ss/gdi/ntgdi/text.h @@ -124,7 +124,7 @@ INT FASTCALL IntGdiAddFontResourceEx(PUNICODE_STRING FileName, DWORD Characteris HANDLE FASTCALL IntGdiAddFontMemResource(PVOID Buffer, DWORD dwSize, PDWORD pNumAdded); BOOL FASTCALL IntGdiRemoveFontMemResource(HANDLE hMMFont); ULONG FASTCALL ftGdiGetGlyphOutline(PDC,WCHAR,UINT,LPGLYPHMETRICS,ULONG,PVOID,LPMAT2,BOOL); -INT FASTCALL IntGetOutlineTextMetrics(PFONTGDI,UINT,OUTLINETEXTMETRICW *); +INT FASTCALL IntGetOutlineTextMetrics(PFONTGDI, UINT, OUTLINETEXTMETRICW*, BOOL); BOOL FASTCALL TextIntUpdateSize(PDC,PTEXTOBJ,PFONTGDI,BOOL); BOOL FASTCALL ftGdiGetRasterizerCaps(LPRASTERIZER_STATUS); BOOL FASTCALL TextIntGetTextExtentPoint(PDC,PTEXTOBJ,LPCWSTR,INT,ULONG,LPINT,LPINT,LPSIZE,FLONG);