From 131f5d2b3209572cea6770da504290a386bf1491 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Wed, 17 Nov 2021 00:22:02 +0100 Subject: [PATCH] Use nearby fonts for font fallback (#11764) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit is a minimal fix in order to pass the `IDWriteFontCollection` we create out of .ttf files residing next to our binaries to the `IDWriteFontFallback::MapCharacters` call. The `IDWriteTextFormat` is used in order to carry the font collection over into `CustomTextLayout`. ## Validation * Put `JetBrainsMono-Regular.ttf` into the binary output directory * Modify `HKCU:\Console\*\FaceName` to `JetBrains Mono` * Launch OpenConsole.exe * OpenConsole uses JetBrains Mono ✔️ Closes #11032 Closes #11648 --- .github/actions/spelling/allow/allow.txt | 1 + .../TerminalSettingsEditor/Profiles.cpp | 52 ++++++++++- src/cascadia/TerminalSettingsEditor/pch.h | 5 +- src/renderer/dx/DxFontInfo.cpp | 90 ++++++++++--------- src/renderer/dx/DxFontInfo.h | 6 +- src/renderer/dx/DxFontRenderData.cpp | 4 +- src/renderer/dx/DxFontRenderData.h | 2 +- src/renderer/dx/lib/dx.vcxproj | 10 +-- src/renderer/dx/lib/dx.vcxproj.filters | 14 +-- 9 files changed, 119 insertions(+), 65 deletions(-) diff --git a/.github/actions/spelling/allow/allow.txt b/.github/actions/spelling/allow/allow.txt index 6025abf76..98ad6278b 100644 --- a/.github/actions/spelling/allow/allow.txt +++ b/.github/actions/spelling/allow/allow.txt @@ -32,6 +32,7 @@ hyperlink hyperlinking hyperlinks img +inlined It'd kje liga diff --git a/src/cascadia/TerminalSettingsEditor/Profiles.cpp b/src/cascadia/TerminalSettingsEditor/Profiles.cpp index 9c476fc3b..dc3773842 100644 --- a/src/cascadia/TerminalSettingsEditor/Profiles.cpp +++ b/src/cascadia/TerminalSettingsEditor/Profiles.cpp @@ -3,6 +3,7 @@ #include "pch.h" #include "Profiles.h" + #include "PreviewConnection.h" #include "Profiles.g.cpp" #include "EnumEntry.h" @@ -10,6 +11,54 @@ #include #include "..\WinRTUtils\inc\Utils.h" +// This function is a copy of DxFontInfo::_NearbyCollection() with +// * the call to DxFontInfo::s_GetNearbyFonts() inlined +// * checkForUpdates for GetSystemFontCollection() set to true +static wil::com_ptr NearbyCollection(IDWriteFactory* dwriteFactory) +{ + // The convenience interfaces for loading fonts from files + // are only available on Windows 10+. + wil::com_ptr factory6; + // wil's query() facilities don't work inside WinRT land at the moment. + // They produce a compilation error due to IUnknown and winrt::Windows::Foundation::IUnknown being ambiguous. + if (!SUCCEEDED(dwriteFactory->QueryInterface(__uuidof(IDWriteFactory6), factory6.put_void()))) + { + return nullptr; + } + + wil::com_ptr systemFontCollection; + THROW_IF_FAILED(factory6->GetSystemFontCollection(false, systemFontCollection.addressof(), true)); + + wil::com_ptr systemFontSet; + THROW_IF_FAILED(systemFontCollection->GetFontSet(systemFontSet.addressof())); + + wil::com_ptr fontSetBuilder2; + THROW_IF_FAILED(factory6->CreateFontSetBuilder(fontSetBuilder2.addressof())); + + THROW_IF_FAILED(fontSetBuilder2->AddFontSet(systemFontSet.get())); + + { + const std::filesystem::path module{ wil::GetModuleFileNameW(nullptr) }; + const auto folder{ module.parent_path() }; + + for (const auto& p : std::filesystem::directory_iterator(folder)) + { + if (til::ends_with(p.path().native(), L".ttf")) + { + fontSetBuilder2->AddFontFile(p.path().c_str()); + } + } + } + + wil::com_ptr fontSet; + THROW_IF_FAILED(fontSetBuilder2->CreateFontSet(fontSet.addressof())); + + wil::com_ptr fontCollection; + THROW_IF_FAILED(factory6->CreateFontCollectionFromFontSet(fontSet.get(), &fontCollection)); + + return fontCollection; +} + using namespace winrt::Windows::UI::Text; using namespace winrt::Windows::UI::Xaml; using namespace winrt::Windows::UI::Xaml::Controls; @@ -107,8 +156,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation reinterpret_cast<::IUnknown**>(factory.put()))); // get the font collection; subscribe to updates - com_ptr fontCollection; - THROW_IF_FAILED(factory->GetSystemFontCollection(fontCollection.put(), TRUE)); + const auto fontCollection = NearbyCollection(factory.get()); for (UINT32 i = 0; i < fontCollection->GetFontFamilyCount(); ++i) { diff --git a/src/cascadia/TerminalSettingsEditor/pch.h b/src/cascadia/TerminalSettingsEditor/pch.h index 7c427ebe2..23bfeeae7 100644 --- a/src/cascadia/TerminalSettingsEditor/pch.h +++ b/src/cascadia/TerminalSettingsEditor/pch.h @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT license. // // pch.h @@ -51,8 +51,7 @@ #include #include -#include -#include +#include // Manually include til after we include Windows.Foundation to give it winrt superpowers #include "til.h" diff --git a/src/renderer/dx/DxFontInfo.cpp b/src/renderer/dx/DxFontInfo.cpp index 03c224868..e5c6749f4 100644 --- a/src/renderer/dx/DxFontInfo.cpp +++ b/src/renderer/dx/DxFontInfo.cpp @@ -96,6 +96,11 @@ bool DxFontInfo::GetFallback() const noexcept return _didFallback; } +IDWriteFontCollection* DxFontInfo::GetNearbyCollection() const noexcept +{ + return _nearbyCollection.Get(); +} + void DxFontInfo::SetFromEngine(const std::wstring_view familyName, const DWRITE_FONT_WEIGHT weight, const DWRITE_FONT_STYLE style, @@ -223,13 +228,11 @@ void DxFontInfo::SetFromEngine(const std::wstring_view familyName, // If the system collection missed, try the files sitting next to our binary. if (withNearbyLookup && !familyExists) { - auto&& nearbyCollection = _NearbyCollection(dwriteFactory); - // May be null on OS below Windows 10. If null, just skip the attempt. - if (nearbyCollection) + if (const auto nearbyCollection = _NearbyCollection(dwriteFactory)) { - nearbyCollection.As(&fontCollection); - THROW_IF_FAILED(fontCollection->FindFamilyName(_familyName.data(), &familyIndex, &familyExists)); + THROW_IF_FAILED(nearbyCollection->FindFamilyName(_familyName.data(), &familyIndex, &familyExists)); + fontCollection = nearbyCollection; } } @@ -332,42 +335,48 @@ void DxFontInfo::SetFromEngine(const std::wstring_view familyName, // - dwriteFactory - The DWrite factory to use // Return Value: // - DirectWrite font collection. May be null if one cannot be created. -[[nodiscard]] const Microsoft::WRL::ComPtr& DxFontInfo::_NearbyCollection(gsl::not_null dwriteFactory) const +[[nodiscard]] IDWriteFontCollection* DxFontInfo::_NearbyCollection(gsl::not_null dwriteFactory) { - // Magic static so we only attempt to grovel the hard disk once no matter how many instances - // of the font collection itself we require. - static const auto knownPaths = s_GetNearbyFonts(); + if (_nearbyCollection) + { + return _nearbyCollection.Get(); + } // The convenience interfaces for loading fonts from files // are only available on Windows 10+. - // Don't try to look up if below that OS version. - static const bool s_isWindows10OrGreater = IsWindows10OrGreater(); - - if (s_isWindows10OrGreater && !_nearbyCollection) + ::Microsoft::WRL::ComPtr factory6; + if (FAILED(dwriteFactory->QueryInterface(&factory6))) { - // Factory3 has a convenience to get us a font set builder. - ::Microsoft::WRL::ComPtr factory3; - THROW_IF_FAILED(dwriteFactory->QueryInterface(&factory3)); - - ::Microsoft::WRL::ComPtr fontSetBuilder; - THROW_IF_FAILED(factory3->CreateFontSetBuilder(&fontSetBuilder)); - - // Builder2 has a convenience to just feed in paths to font files. - ::Microsoft::WRL::ComPtr fontSetBuilder2; - THROW_IF_FAILED(fontSetBuilder.As(&fontSetBuilder2)); - - for (auto& p : knownPaths) - { - fontSetBuilder2->AddFontFile(p.c_str()); - } - - ::Microsoft::WRL::ComPtr fontSet; - THROW_IF_FAILED(fontSetBuilder2->CreateFontSet(&fontSet)); - - THROW_IF_FAILED(factory3->CreateFontCollectionFromFontSet(fontSet.Get(), &_nearbyCollection)); + return nullptr; } - return _nearbyCollection; + ::Microsoft::WRL::ComPtr systemFontCollection; + THROW_IF_FAILED(factory6->GetSystemFontCollection(false, &systemFontCollection, 0)); + + ::Microsoft::WRL::ComPtr systemFontSet; + THROW_IF_FAILED(systemFontCollection->GetFontSet(&systemFontSet)); + + ::Microsoft::WRL::ComPtr fontSetBuilder2; + THROW_IF_FAILED(factory6->CreateFontSetBuilder(&fontSetBuilder2)); + + THROW_IF_FAILED(fontSetBuilder2->AddFontSet(systemFontSet.Get())); + + // Magic static so we only attempt to grovel the hard disk once no matter how many instances + // of the font collection itself we require. + static const auto knownPaths = s_GetNearbyFonts(); + for (auto& p : knownPaths) + { + fontSetBuilder2->AddFontFile(p.c_str()); + } + + ::Microsoft::WRL::ComPtr fontSet; + THROW_IF_FAILED(fontSetBuilder2->CreateFontSet(&fontSet)); + + ::Microsoft::WRL::ComPtr fontCollection; + THROW_IF_FAILED(factory6->CreateFontCollectionFromFontSet(fontSet.Get(), &fontCollection)); + + _nearbyCollection = fontCollection; + return _nearbyCollection.Get(); } // Routine Description: @@ -386,18 +395,11 @@ void DxFontInfo::SetFromEngine(const std::wstring_view familyName, const std::filesystem::path module{ wil::GetModuleFileNameW(nullptr) }; const auto folder{ module.parent_path() }; - for (auto& p : std::filesystem::directory_iterator(folder)) + for (const auto& p : std::filesystem::directory_iterator(folder)) { - if (p.is_regular_file()) + if (til::ends_with(p.path().native(), L".ttf")) { - auto extension = p.path().extension().wstring(); - std::transform(extension.begin(), extension.end(), extension.begin(), std::towlower); - - static constexpr std::wstring_view ttfExtension{ L".ttf" }; - if (ttfExtension == extension) - { - paths.push_back(p); - } + paths.push_back(p.path()); } } diff --git a/src/renderer/dx/DxFontInfo.h b/src/renderer/dx/DxFontInfo.h index ba38b9ab9..9b336accd 100644 --- a/src/renderer/dx/DxFontInfo.h +++ b/src/renderer/dx/DxFontInfo.h @@ -41,6 +41,8 @@ namespace Microsoft::Console::Render bool GetFallback() const noexcept; + IDWriteFontCollection* GetNearbyCollection() const noexcept; + void SetFromEngine(const std::wstring_view familyName, const DWRITE_FONT_WEIGHT weight, const DWRITE_FONT_STYLE style, @@ -57,11 +59,11 @@ namespace Microsoft::Console::Render [[nodiscard]] std::wstring _GetFontFamilyName(gsl::not_null const fontFamily, std::wstring& localeName); - [[nodiscard]] const Microsoft::WRL::ComPtr& _NearbyCollection(gsl::not_null dwriteFactory) const; + [[nodiscard]] IDWriteFontCollection* _NearbyCollection(gsl::not_null dwriteFactory); [[nodiscard]] static std::vector s_GetNearbyFonts(); - mutable ::Microsoft::WRL::ComPtr _nearbyCollection; + ::Microsoft::WRL::ComPtr _nearbyCollection; // The font name we should be looking for std::wstring _familyName; diff --git a/src/renderer/dx/DxFontRenderData.cpp b/src/renderer/dx/DxFontRenderData.cpp index be1047885..96c44ccb6 100644 --- a/src/renderer/dx/DxFontRenderData.cpp +++ b/src/renderer/dx/DxFontRenderData.cpp @@ -894,11 +894,11 @@ void DxFontRenderData::_BuildFontRenderData(const FontInfoDesired& desired, Font _glyphCell = actual.GetSize(); } -Microsoft::WRL::ComPtr DxFontRenderData::_BuildTextFormat(const DxFontInfo fontInfo, const std::wstring_view localeName) +Microsoft::WRL::ComPtr DxFontRenderData::_BuildTextFormat(const DxFontInfo& fontInfo, const std::wstring_view localeName) { Microsoft::WRL::ComPtr format; THROW_IF_FAILED(_dwriteFactory->CreateTextFormat(fontInfo.GetFamilyName().data(), - nullptr, + fontInfo.GetNearbyCollection(), fontInfo.GetWeight(), fontInfo.GetStyle(), fontInfo.GetStretch(), diff --git a/src/renderer/dx/DxFontRenderData.h b/src/renderer/dx/DxFontRenderData.h index 556fe58f7..ce69169c5 100644 --- a/src/renderer/dx/DxFontRenderData.h +++ b/src/renderer/dx/DxFontRenderData.h @@ -122,7 +122,7 @@ namespace Microsoft::Console::Render float _FontStretchToWidthAxisValue(DWRITE_FONT_STRETCH fontStretch) noexcept; float _FontStyleToSlantFixedAxisValue(DWRITE_FONT_STYLE fontStyle) noexcept; void _BuildFontRenderData(const FontInfoDesired& desired, FontInfo& actual, const int dpi); - Microsoft::WRL::ComPtr _BuildTextFormat(const DxFontInfo fontInfo, const std::wstring_view localeName); + Microsoft::WRL::ComPtr _BuildTextFormat(const DxFontInfo& fontInfo, const std::wstring_view localeName); std::unordered_map> _textFormatMap; std::unordered_map> _fontFaceMap; diff --git a/src/renderer/dx/lib/dx.vcxproj b/src/renderer/dx/lib/dx.vcxproj index 618d1b9ce..7eb6be151 100644 --- a/src/renderer/dx/lib/dx.vcxproj +++ b/src/renderer/dx/lib/dx.vcxproj @@ -18,21 +18,21 @@ - - Create - + + Create + - - + + diff --git a/src/renderer/dx/lib/dx.vcxproj.filters b/src/renderer/dx/lib/dx.vcxproj.filters index 86916f139..cee87653f 100644 --- a/src/renderer/dx/lib/dx.vcxproj.filters +++ b/src/renderer/dx/lib/dx.vcxproj.filters @@ -4,24 +4,26 @@ + - + - + + - - + + + - - \ No newline at end of file +