Skip to content

Commit

Permalink
Fix bugs introduced in #16821 (custom font fallback) (#16993)
Browse files Browse the repository at this point in the history
* 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 ✅
  • Loading branch information
lhecker authored Apr 1, 2024
1 parent 3cc82a5 commit 9f3dbab
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 29 deletions.
10 changes: 5 additions & 5 deletions src/cascadia/TerminalSettingsEditor/Appearances.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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()));

Expand Down
1 change: 1 addition & 0 deletions src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
69 changes: 47 additions & 22 deletions src/renderer/atlas/AtlasEngine.api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
_updateFont(fontInfoDesired, fontInfo, features, axes);
return S_OK;
}
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
if (FAILED(hr) && _updateWithNearbyFontCollection())
{
_api.s.write()->font.write()->fontCollection = FontCache::GetCached();
hr = _updateFont(fontInfoDesired, fontInfo, features, axes);
}
CATCH_LOG();
}

try
{
_updateFont(fontInfoDesired, fontInfo, features, axes);
return S_OK;
}
CATCH_RETURN();
return hr;
}

void AtlasEngine::UpdateHyperlinkHoveredId(const uint16_t hoveredId) noexcept
Expand Down Expand Up @@ -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())
Expand Down Expand Up @@ -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);

return S_OK;
}
CATCH_RETURN()

void AtlasEngine::_resolveFontMetrics(const FontInfoDesired& fontInfoDesired, FontInfo& fontInfo, FontSettings* fontMetrics) const
void AtlasEngine::_resolveFontMetrics(const FontInfoDesired& fontInfoDesired, FontInfo& fontInfo, FontSettings* fontMetrics)
{
const auto& faceName = fontInfoDesired.GetFaceName();
const auto requestedFamily = fontInfoDesired.GetFamily();
Expand Down Expand Up @@ -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())
Expand Down Expand Up @@ -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;
}
5 changes: 3 additions & 2 deletions src/renderer/atlas/AtlasEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down

0 comments on commit 9f3dbab

Please sign in to comment.