Fix bugs introduced in #16821 (custom font fallback) (#16993)

* Since `FindFontWithLocalizedName` is broken (intentionally and
  temporarily until #16943 is fixed) we have to be extra be careful
  not to return a nullptr `Font`.
* Portable builds may not have a broken font cache, but also not have
  the given font (Cascadia Mono for instance) installed. This requires
  us to load the nearby fonts even if there aren't any exceptions.

## Validation Steps Performed
* Open `src/cascadia/CascadiaResources.build.items`
  and remove the `Condition` for .ttf files
* Deploy on a clean Windows 10 VM
* Cascadia Mono loads without issues 
* Open the `Settings > Defaults > Appearance`,
  enter a non-existing font and hit Save
* Doesn't crash 
This commit is contained in:
Leonard Hecker 2024-04-02 01:32:31 +02:00 committed by GitHub
parent 3cc82a51d8
commit 9f3dbab7bf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 57 additions and 30 deletions

View File

@ -419,6 +419,11 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
BOOL hasPowerlineCharacters = FALSE;
til::iterate_font_families(fontFace, [&](wil::zwstring_view name) {
if (primaryFontName.empty())
{
primaryFontName = name;
}
std::wstring* accumulator = nullptr;
UINT32 index = 0;
@ -434,11 +439,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
break;
}
if (primaryFontName.empty())
{
primaryFontName = name;
}
wil::com_ptr<IDWriteFontFamily> fontFamily;
THROW_IF_FAILED(fontCollection->GetFontFamily(index, fontFamily.addressof()));

View File

@ -187,6 +187,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
UpdateFontList();
}
const auto& currentFontList{ CompleteFontList() };
fallbackFont = currentFontList.First().Current();
for (const auto& font : currentFontList)
{
if (font.LocalizedName() == name)

View File

@ -482,32 +482,17 @@ void AtlasEngine::SetWarningCallback(std::function<void(HRESULT, wil::zwstring_v
// commit 9e86c98 (PR #16196), because it showed that it's definitely not due to FindFamilyName() failing.
//
// The workaround is to catch the exception and retry it with our nearby fonts manually loaded in.
HRESULT hr = _updateFont(fontInfoDesired, fontInfo, features, axes);
if constexpr (Feature_NearbyFontLoading::IsEnabled())
{
try
if (FAILED(hr) && _updateWithNearbyFontCollection())
{
_updateFont(fontInfoDesired, fontInfo, features, axes);
return S_OK;
hr = _updateFont(fontInfoDesired, fontInfo, features, axes);
}
CATCH_LOG();
// _resolveFontMetrics() checks `_api.s->font->fontCollection` for a pre-existing font collection,
// before falling back to using the system font collection. This way we can inject our custom one.
// Doing it this way is a bit hacky, but it does have the benefit that we can cache a font collection
// instance across font changes, like when zooming the font size rapidly using the scroll wheel.
try
{
_api.s.write()->font.write()->fontCollection = FontCache::GetCached();
}
CATCH_LOG();
}
try
{
_updateFont(fontInfoDesired, fontInfo, features, axes);
return S_OK;
}
CATCH_RETURN();
return hr;
}
void AtlasEngine::UpdateHyperlinkHoveredId(const uint16_t hoveredId) noexcept
@ -536,7 +521,8 @@ void AtlasEngine::_resolveTransparencySettings() noexcept
}
}
void AtlasEngine::_updateFont(const FontInfoDesired& fontInfoDesired, FontInfo& fontInfo, const std::unordered_map<std::wstring_view, uint32_t>& features, const std::unordered_map<std::wstring_view, float>& axes)
[[nodiscard]] HRESULT AtlasEngine::_updateFont(const FontInfoDesired& fontInfoDesired, FontInfo& fontInfo, const std::unordered_map<std::wstring_view, uint32_t>& features, const std::unordered_map<std::wstring_view, float>& axes) noexcept
try
{
std::vector<DWRITE_FONT_FEATURE> fontFeatures;
if (!features.empty())
@ -616,9 +602,12 @@ void AtlasEngine::_updateFont(const FontInfoDesired& fontInfoDesired, FontInfo&
_resolveFontMetrics(fontInfoDesired, fontInfo, font);
font->fontFeatures = std::move(fontFeatures);
font->fontAxisValues = std::move(fontAxisValues);
}
void AtlasEngine::_resolveFontMetrics(const FontInfoDesired& fontInfoDesired, FontInfo& fontInfo, FontSettings* fontMetrics) const
return S_OK;
}
CATCH_RETURN()
void AtlasEngine::_resolveFontMetrics(const FontInfoDesired& fontInfoDesired, FontInfo& fontInfo, FontSettings* fontMetrics)
{
const auto& faceName = fontInfoDesired.GetFaceName();
const auto requestedFamily = fontInfoDesired.GetFamily();
@ -659,6 +648,16 @@ void AtlasEngine::_resolveFontMetrics(const FontInfoDesired& fontInfoDesired, Fo
BOOL exists = false;
THROW_IF_FAILED(fontCollection->FindFamilyName(fontName.c_str(), &index, &exists));
// In case of a portable build, the given font may not be installed and instead be bundled next to our executable.
if constexpr (Feature_NearbyFontLoading::IsEnabled())
{
if (!exists && _updateWithNearbyFontCollection())
{
fontCollection = _api.s->font->fontCollection;
THROW_IF_FAILED(fontCollection->FindFamilyName(fontName.c_str(), &index, &exists));
}
}
if (!exists)
{
if (!missingFontNames.empty())
@ -869,3 +868,29 @@ void AtlasEngine::_resolveFontMetrics(const FontInfoDesired& fontInfoDesired, Fo
fontMetrics->colorGlyphs = fontInfoDesired.GetEnableColorGlyphs();
}
}
// Nearby fonts are described a couple of times throughout the file.
// This abstraction in particular helps us avoid retrying when it's pointless:
// After all, if the font collection didn't change (no nearby fonts, loading failed, it's already loaded),
// we don't need to try it again. It returns true if retrying is necessary.
[[nodiscard]] bool AtlasEngine::_updateWithNearbyFontCollection() noexcept
{
// _resolveFontMetrics() checks `_api.s->font->fontCollection` for a pre-existing font collection,
// before falling back to using the system font collection. This way we can inject our custom one.
// Doing it this way is a bit hacky, but it does have the benefit that we can cache a font collection
// instance across font changes, like when zooming the font size rapidly using the scroll wheel.
wil::com_ptr<IDWriteFontCollection> collection;
try
{
collection = FontCache::GetCached();
}
CATCH_LOG();
if (!collection || _api.s->font->fontCollection == collection)
{
return false;
}
_api.s.write()->font.write()->fontCollection = std::move(collection);
return true;
}

View File

@ -94,8 +94,9 @@ namespace Microsoft::Console::Render::Atlas
// AtlasEngine.api.cpp
void _resolveTransparencySettings() noexcept;
void _updateFont(const FontInfoDesired& fontInfoDesired, FontInfo& fontInfo, const std::unordered_map<std::wstring_view, uint32_t>& features, const std::unordered_map<std::wstring_view, float>& axes);
void _resolveFontMetrics(const FontInfoDesired& fontInfoDesired, FontInfo& fontInfo, FontSettings* fontMetrics = nullptr) const;
[[nodiscard]] HRESULT _updateFont(const FontInfoDesired& fontInfoDesired, FontInfo& fontInfo, const std::unordered_map<std::wstring_view, uint32_t>& features, const std::unordered_map<std::wstring_view, float>& axes) noexcept;
void _resolveFontMetrics(const FontInfoDesired& fontInfoDesired, FontInfo& fontInfo, FontSettings* fontMetrics = nullptr);
[[nodiscard]] bool _updateWithNearbyFontCollection() noexcept;
// AtlasEngine.r.cpp
ATLAS_ATTR_COLD void _recreateAdapter();