Use nearby fonts for font fallback (#11764)

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
This commit is contained in:
Leonard Hecker 2021-11-17 00:22:02 +01:00 committed by GitHub
parent 7bfaad4592
commit 131f5d2b32
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 119 additions and 65 deletions

View file

@ -32,6 +32,7 @@ hyperlink
hyperlinking hyperlinking
hyperlinks hyperlinks
img img
inlined
It'd It'd
kje kje
liga liga

View file

@ -3,6 +3,7 @@
#include "pch.h" #include "pch.h"
#include "Profiles.h" #include "Profiles.h"
#include "PreviewConnection.h" #include "PreviewConnection.h"
#include "Profiles.g.cpp" #include "Profiles.g.cpp"
#include "EnumEntry.h" #include "EnumEntry.h"
@ -10,6 +11,54 @@
#include <LibraryResources.h> #include <LibraryResources.h>
#include "..\WinRTUtils\inc\Utils.h" #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<IDWriteFontCollection1> NearbyCollection(IDWriteFactory* dwriteFactory)
{
// The convenience interfaces for loading fonts from files
// are only available on Windows 10+.
wil::com_ptr<IDWriteFactory6> 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<IDWriteFontCollection1> systemFontCollection;
THROW_IF_FAILED(factory6->GetSystemFontCollection(false, systemFontCollection.addressof(), true));
wil::com_ptr<IDWriteFontSet> systemFontSet;
THROW_IF_FAILED(systemFontCollection->GetFontSet(systemFontSet.addressof()));
wil::com_ptr<IDWriteFontSetBuilder2> fontSetBuilder2;
THROW_IF_FAILED(factory6->CreateFontSetBuilder(fontSetBuilder2.addressof()));
THROW_IF_FAILED(fontSetBuilder2->AddFontSet(systemFontSet.get()));
{
const std::filesystem::path module{ wil::GetModuleFileNameW<std::wstring>(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<IDWriteFontSet> fontSet;
THROW_IF_FAILED(fontSetBuilder2->CreateFontSet(fontSet.addressof()));
wil::com_ptr<IDWriteFontCollection1> fontCollection;
THROW_IF_FAILED(factory6->CreateFontCollectionFromFontSet(fontSet.get(), &fontCollection));
return fontCollection;
}
using namespace winrt::Windows::UI::Text; using namespace winrt::Windows::UI::Text;
using namespace winrt::Windows::UI::Xaml; using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Windows::UI::Xaml::Controls; using namespace winrt::Windows::UI::Xaml::Controls;
@ -107,8 +156,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
reinterpret_cast<::IUnknown**>(factory.put()))); reinterpret_cast<::IUnknown**>(factory.put())));
// get the font collection; subscribe to updates // get the font collection; subscribe to updates
com_ptr<IDWriteFontCollection> fontCollection; const auto fontCollection = NearbyCollection(factory.get());
THROW_IF_FAILED(factory->GetSystemFontCollection(fontCollection.put(), TRUE));
for (UINT32 i = 0; i < fontCollection->GetFontFamilyCount(); ++i) for (UINT32 i = 0; i < fontCollection->GetFontFamilyCount(); ++i)
{ {

View file

@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation. // Copyright (c) Microsoft Corporation.
// Licensed under the MIT license. // Licensed under the MIT license.
// //
// pch.h // pch.h
@ -51,8 +51,7 @@
#include <shlobj.h> #include <shlobj.h>
#include <shobjidl_core.h> #include <shobjidl_core.h>
#include <dwrite.h> #include <dwrite_3.h>
#include <dwrite_1.h>
// Manually include til after we include Windows.Foundation to give it winrt superpowers // Manually include til after we include Windows.Foundation to give it winrt superpowers
#include "til.h" #include "til.h"

View file

@ -96,6 +96,11 @@ bool DxFontInfo::GetFallback() const noexcept
return _didFallback; return _didFallback;
} }
IDWriteFontCollection* DxFontInfo::GetNearbyCollection() const noexcept
{
return _nearbyCollection.Get();
}
void DxFontInfo::SetFromEngine(const std::wstring_view familyName, void DxFontInfo::SetFromEngine(const std::wstring_view familyName,
const DWRITE_FONT_WEIGHT weight, const DWRITE_FONT_WEIGHT weight,
const DWRITE_FONT_STYLE style, 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 the system collection missed, try the files sitting next to our binary.
if (withNearbyLookup && !familyExists) if (withNearbyLookup && !familyExists)
{ {
auto&& nearbyCollection = _NearbyCollection(dwriteFactory);
// May be null on OS below Windows 10. If null, just skip the attempt. // 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(nearbyCollection->FindFamilyName(_familyName.data(), &familyIndex, &familyExists));
THROW_IF_FAILED(fontCollection->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 // - dwriteFactory - The DWrite factory to use
// Return Value: // Return Value:
// - DirectWrite font collection. May be null if one cannot be created. // - DirectWrite font collection. May be null if one cannot be created.
[[nodiscard]] const Microsoft::WRL::ComPtr<IDWriteFontCollection1>& DxFontInfo::_NearbyCollection(gsl::not_null<IDWriteFactory1*> dwriteFactory) const [[nodiscard]] IDWriteFontCollection* DxFontInfo::_NearbyCollection(gsl::not_null<IDWriteFactory1*> dwriteFactory)
{ {
// Magic static so we only attempt to grovel the hard disk once no matter how many instances if (_nearbyCollection)
// of the font collection itself we require. {
static const auto knownPaths = s_GetNearbyFonts(); return _nearbyCollection.Get();
}
// The convenience interfaces for loading fonts from files // The convenience interfaces for loading fonts from files
// are only available on Windows 10+. // are only available on Windows 10+.
// Don't try to look up if below that OS version. ::Microsoft::WRL::ComPtr<IDWriteFactory6> factory6;
static const bool s_isWindows10OrGreater = IsWindows10OrGreater(); if (FAILED(dwriteFactory->QueryInterface<IDWriteFactory6>(&factory6)))
if (s_isWindows10OrGreater && !_nearbyCollection)
{ {
// Factory3 has a convenience to get us a font set builder. return nullptr;
::Microsoft::WRL::ComPtr<IDWriteFactory3> factory3;
THROW_IF_FAILED(dwriteFactory->QueryInterface<IDWriteFactory3>(&factory3));
::Microsoft::WRL::ComPtr<IDWriteFontSetBuilder> fontSetBuilder;
THROW_IF_FAILED(factory3->CreateFontSetBuilder(&fontSetBuilder));
// Builder2 has a convenience to just feed in paths to font files.
::Microsoft::WRL::ComPtr<IDWriteFontSetBuilder2> fontSetBuilder2;
THROW_IF_FAILED(fontSetBuilder.As(&fontSetBuilder2));
for (auto& p : knownPaths)
{
fontSetBuilder2->AddFontFile(p.c_str());
}
::Microsoft::WRL::ComPtr<IDWriteFontSet> fontSet;
THROW_IF_FAILED(fontSetBuilder2->CreateFontSet(&fontSet));
THROW_IF_FAILED(factory3->CreateFontCollectionFromFontSet(fontSet.Get(), &_nearbyCollection));
} }
return _nearbyCollection; ::Microsoft::WRL::ComPtr<IDWriteFontCollection1> systemFontCollection;
THROW_IF_FAILED(factory6->GetSystemFontCollection(false, &systemFontCollection, 0));
::Microsoft::WRL::ComPtr<IDWriteFontSet> systemFontSet;
THROW_IF_FAILED(systemFontCollection->GetFontSet(&systemFontSet));
::Microsoft::WRL::ComPtr<IDWriteFontSetBuilder2> 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<IDWriteFontSet> fontSet;
THROW_IF_FAILED(fontSetBuilder2->CreateFontSet(&fontSet));
::Microsoft::WRL::ComPtr<IDWriteFontCollection1> fontCollection;
THROW_IF_FAILED(factory6->CreateFontCollectionFromFontSet(fontSet.Get(), &fontCollection));
_nearbyCollection = fontCollection;
return _nearbyCollection.Get();
} }
// Routine Description: // Routine Description:
@ -386,18 +395,11 @@ void DxFontInfo::SetFromEngine(const std::wstring_view familyName,
const std::filesystem::path module{ wil::GetModuleFileNameW<std::wstring>(nullptr) }; const std::filesystem::path module{ wil::GetModuleFileNameW<std::wstring>(nullptr) };
const auto folder{ module.parent_path() }; 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(); paths.push_back(p.path());
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);
}
} }
} }

View file

@ -41,6 +41,8 @@ namespace Microsoft::Console::Render
bool GetFallback() const noexcept; bool GetFallback() const noexcept;
IDWriteFontCollection* GetNearbyCollection() const noexcept;
void SetFromEngine(const std::wstring_view familyName, void SetFromEngine(const std::wstring_view familyName,
const DWRITE_FONT_WEIGHT weight, const DWRITE_FONT_WEIGHT weight,
const DWRITE_FONT_STYLE style, const DWRITE_FONT_STYLE style,
@ -57,11 +59,11 @@ namespace Microsoft::Console::Render
[[nodiscard]] std::wstring _GetFontFamilyName(gsl::not_null<IDWriteFontFamily*> const fontFamily, [[nodiscard]] std::wstring _GetFontFamilyName(gsl::not_null<IDWriteFontFamily*> const fontFamily,
std::wstring& localeName); std::wstring& localeName);
[[nodiscard]] const Microsoft::WRL::ComPtr<IDWriteFontCollection1>& _NearbyCollection(gsl::not_null<IDWriteFactory1*> dwriteFactory) const; [[nodiscard]] IDWriteFontCollection* _NearbyCollection(gsl::not_null<IDWriteFactory1*> dwriteFactory);
[[nodiscard]] static std::vector<std::filesystem::path> s_GetNearbyFonts(); [[nodiscard]] static std::vector<std::filesystem::path> s_GetNearbyFonts();
mutable ::Microsoft::WRL::ComPtr<IDWriteFontCollection1> _nearbyCollection; ::Microsoft::WRL::ComPtr<IDWriteFontCollection> _nearbyCollection;
// The font name we should be looking for // The font name we should be looking for
std::wstring _familyName; std::wstring _familyName;

View file

@ -894,11 +894,11 @@ void DxFontRenderData::_BuildFontRenderData(const FontInfoDesired& desired, Font
_glyphCell = actual.GetSize(); _glyphCell = actual.GetSize();
} }
Microsoft::WRL::ComPtr<IDWriteTextFormat> DxFontRenderData::_BuildTextFormat(const DxFontInfo fontInfo, const std::wstring_view localeName) Microsoft::WRL::ComPtr<IDWriteTextFormat> DxFontRenderData::_BuildTextFormat(const DxFontInfo& fontInfo, const std::wstring_view localeName)
{ {
Microsoft::WRL::ComPtr<IDWriteTextFormat> format; Microsoft::WRL::ComPtr<IDWriteTextFormat> format;
THROW_IF_FAILED(_dwriteFactory->CreateTextFormat(fontInfo.GetFamilyName().data(), THROW_IF_FAILED(_dwriteFactory->CreateTextFormat(fontInfo.GetFamilyName().data(),
nullptr, fontInfo.GetNearbyCollection(),
fontInfo.GetWeight(), fontInfo.GetWeight(),
fontInfo.GetStyle(), fontInfo.GetStyle(),
fontInfo.GetStretch(), fontInfo.GetStretch(),

View file

@ -122,7 +122,7 @@ namespace Microsoft::Console::Render
float _FontStretchToWidthAxisValue(DWRITE_FONT_STRETCH fontStretch) noexcept; float _FontStretchToWidthAxisValue(DWRITE_FONT_STRETCH fontStretch) noexcept;
float _FontStyleToSlantFixedAxisValue(DWRITE_FONT_STYLE fontStyle) noexcept; float _FontStyleToSlantFixedAxisValue(DWRITE_FONT_STYLE fontStyle) noexcept;
void _BuildFontRenderData(const FontInfoDesired& desired, FontInfo& actual, const int dpi); void _BuildFontRenderData(const FontInfoDesired& desired, FontInfo& actual, const int dpi);
Microsoft::WRL::ComPtr<IDWriteTextFormat> _BuildTextFormat(const DxFontInfo fontInfo, const std::wstring_view localeName); Microsoft::WRL::ComPtr<IDWriteTextFormat> _BuildTextFormat(const DxFontInfo& fontInfo, const std::wstring_view localeName);
std::unordered_map<FontAttributeMapKey, ::Microsoft::WRL::ComPtr<IDWriteTextFormat>> _textFormatMap; std::unordered_map<FontAttributeMapKey, ::Microsoft::WRL::ComPtr<IDWriteTextFormat>> _textFormatMap;
std::unordered_map<FontAttributeMapKey, ::Microsoft::WRL::ComPtr<IDWriteFontFace1>> _fontFaceMap; std::unordered_map<FontAttributeMapKey, ::Microsoft::WRL::ComPtr<IDWriteFontFace1>> _fontFaceMap;

View file

@ -18,21 +18,21 @@
<ClCompile Include="..\BoxDrawingEffect.cpp" /> <ClCompile Include="..\BoxDrawingEffect.cpp" />
<ClCompile Include="..\CustomTextLayout.cpp" /> <ClCompile Include="..\CustomTextLayout.cpp" />
<ClCompile Include="..\CustomTextRenderer.cpp" /> <ClCompile Include="..\CustomTextRenderer.cpp" />
<ClCompile Include="..\precomp.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\DxFontInfo.cpp" /> <ClCompile Include="..\DxFontInfo.cpp" />
<ClCompile Include="..\DxFontRenderData.cpp" /> <ClCompile Include="..\DxFontRenderData.cpp" />
<ClCompile Include="..\DxRenderer.cpp" /> <ClCompile Include="..\DxRenderer.cpp" />
<ClCompile Include="..\precomp.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\BoxDrawingEffect.h" /> <ClInclude Include="..\BoxDrawingEffect.h" />
<ClInclude Include="..\CustomTextLayout.h" /> <ClInclude Include="..\CustomTextLayout.h" />
<ClInclude Include="..\CustomTextRenderer.h" /> <ClInclude Include="..\CustomTextRenderer.h" />
<ClInclude Include="..\precomp.h" />
<ClInclude Include="..\DxRenderer.hpp" />
<ClInclude Include="..\DxFontInfo.h" /> <ClInclude Include="..\DxFontInfo.h" />
<ClInclude Include="..\DxFontRenderData.h" /> <ClInclude Include="..\DxFontRenderData.h" />
<ClInclude Include="..\DxRenderer.hpp" />
<ClInclude Include="..\precomp.h" />
<ClInclude Include="..\ScreenPixelShader.h" /> <ClInclude Include="..\ScreenPixelShader.h" />
<ClInclude Include="..\ScreenVertexShader.h" /> <ClInclude Include="..\ScreenVertexShader.h" />
</ItemGroup> </ItemGroup>

View file

@ -4,24 +4,26 @@
<Natvis Include="$(SolutionDir)tools\ConsoleTypes.natvis" /> <Natvis Include="$(SolutionDir)tools\ConsoleTypes.natvis" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="..\BoxDrawingEffect.cpp" />
<ClCompile Include="..\CustomTextLayout.cpp" /> <ClCompile Include="..\CustomTextLayout.cpp" />
<ClCompile Include="..\CustomTextRenderer.cpp" /> <ClCompile Include="..\CustomTextRenderer.cpp" />
<ClCompile Include="..\precomp.cpp" /> <ClCompile Include="..\DxFontInfo.cpp" />
<ClCompile Include="..\DxFontRenderData.cpp" /> <ClCompile Include="..\DxFontRenderData.cpp" />
<ClCompile Include="..\DxRenderer.cpp" /> <ClCompile Include="..\DxRenderer.cpp" />
<ClCompile Include="..\BoxDrawingEffect.cpp" /> <ClCompile Include="..\precomp.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\BoxDrawingEffect.h" />
<ClInclude Include="..\CustomTextLayout.h" /> <ClInclude Include="..\CustomTextLayout.h" />
<ClInclude Include="..\CustomTextRenderer.h" /> <ClInclude Include="..\CustomTextRenderer.h" />
<ClInclude Include="..\precomp.h" /> <ClInclude Include="..\DxFontInfo.h" />
<ClInclude Include="..\DxFontRenderData.h"/> <ClInclude Include="..\DxFontRenderData.h" />
<ClInclude Include="..\DxRenderer.hpp" /> <ClInclude Include="..\DxRenderer.hpp" />
<ClInclude Include="..\precomp.h" />
<ClInclude Include="..\ScreenPixelShader.h" /> <ClInclude Include="..\ScreenPixelShader.h" />
<ClInclude Include="..\ScreenVertexShader.h" /> <ClInclude Include="..\ScreenVertexShader.h" />
<ClInclude Include="..\BoxDrawingEffect.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Midl Include="..\IBoxDrawingEffect.idl" /> <Midl Include="..\IBoxDrawingEffect.idl" />
</ItemGroup> </ItemGroup>
</Project> </Project>