Repository: microsoft/terminal (language: cpp)

Issue:
Cascadia Mono font not found
### Windows Terminal version

1.18.2681.0

### Windows build number

10.0.22621.2283

### Other Software

winget v1.6.2701

### Steps to reproduce

Install an older version of Terminal. Upgrade to 1.18.2681.0 using winget.

### Expected Behavior

Upgrade succeeds.

### Actual Behavior

Terminal says it can't find a font named Cascadia Mono, and falls back to Consolas


Discussion / hints:
I know this has been reported a million times, but it just happened to me and all of the previous issues on it seem to be closed, so I've written this to capture the fact that it clearly isn't resolved.

I do not see Cascadia Mono in the Windows Fonts UI or in C:\Windows\Fonts or in %localappdata%\Microsoft\Windows\Fonts, so something either uninstalled it, or it is not included in Windows 11 Home.

I tried doing a Repair, but it didn't accomplish anything.

Edit: Seems like someone should make a Cascadia Mono winget package and make Windows Terminal's package depend on it. Ironically, Chocolatey has a Cascadia Code package that probably includes this font.

Edit 2: Downloading the font from https://github.com/microsoft/cascadia-code and installing it manually for all users, then resetting Terminal works, but it seems like Terminal shouldn't depend on the user to do this.
This is wacky and very unexpected. The other issues are all closed, because we _thought_ we fixed this a few releases ago. We added a final fallback to try and load the font directly from a file in our package (not from the font cache).

What rendering engine are you using (could you share your [settings.json file](https://github.com/microsoft/terminal/wiki/Frequently-Asked-Questions-(FAQ)#where-can-i-find-the-settings-file))? (we're pretty sure the rendering engine _shouldn't_ matter here.) 


Our only theory right now is that the way we detect a failure to load the font might not be working anymore?

23d45a7e3a286f37924fd68d222168fce631282d was the last touch here, which only shipped in 1.18 preview. That we _might_ need to revert 😞
I just encountered this after upgrading using winget.

* Windows Terminal version (after upgrading): 1.18.2822.0
* Windows build number: 10.0.19045.0
* winget version: v1.6.2771
yes
> This is wacky and very unexpected. The other issues are all closed, because we _thought_ we fixed this a few releases ago. We added a final fallback to try and load the font directly from a file in our package (not from the font cache).
> 
> What rendering engine are you using (could you share your [settings.json file](https://github.com/microsoft/terminal/wiki/Frequently-Asked-Questions-(FAQ)#where-can-i-find-the-settings-file))? (we're pretty sure the rendering engine _shouldn't_ matter here.)

Here it is: [settings.json](https://github.com/microsoft/terminal/files/12907483/settings.json)

Reminder that Cascadia Mono did not seem to be installed in Windows 11 for some reason (which is weird because I was using an old version of Terminal without having ever seen that error), so it seems to be Terminal depending on something which isn't guaranteed to be present?
Here's my [settings.json](https://github.com/microsoft/terminal/files/12908917/settings.json), too.

I've not reconfigured anything related to rendering engines, fonts, or anything like that.

The plot thickens: I have `C:\Windows\Fonts\CascadiaMono.ttf`, and I was also able to use the Cascadia Mono font in Notepad without issue.

![image](https://github.com/microsoft/terminal/assets/39698/073f6820-92ff-4143-b2b7-6a1a069998dc)

![image](https://github.com/microsoft/terminal/assets/39698/abcb4f43-c6f0-40b3-a847-77667aa80016)


Fix patch:
diff --git a/src/renderer/atlas/AtlasEngine.api.cpp b/src/renderer/atlas/AtlasEngine.api.cpp
index 4459c97a8e5..fd1708abe8c 100644
--- a/src/renderer/atlas/AtlasEngine.api.cpp
+++ b/src/renderer/atlas/AtlasEngine.api.cpp
@@ -455,30 +455,34 @@ void AtlasEngine::SetWarningCallback(std::function<void(HRESULT)> pfn) noexcept
 
 [[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
 {
-    static constexpr std::array fallbackFaceNames{ static_cast<const wchar_t*>(nullptr), L"Consolas", L"Lucida Console", L"Courier New" };
-    auto it = fallbackFaceNames.begin();
-    const auto end = fallbackFaceNames.end();
+    try
+    {
+        _updateFont(fontInfoDesired.GetFaceName().c_str(), fontInfoDesired, fontInfo, features, axes);
+        return S_OK;
+    }
+    CATCH_LOG();
 
-    for (;;)
+    if constexpr (Feature_NearbyFontLoading::IsEnabled())
     {
         try
         {
-            _updateFont(*it, fontInfoDesired, fontInfo, features, axes);
+            // _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. See GH#9375.
+            // 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.
+            _api.s.write()->font.write()->fontCollection = FontCache::GetCached();
+            _updateFont(fontInfoDesired.GetFaceName().c_str(), fontInfoDesired, fontInfo, features, axes);
             return S_OK;
         }
-        catch (...)
-        {
-            ++it;
-            if (it == end)
-            {
-                RETURN_CAUGHT_EXCEPTION();
-            }
-            else
-            {
-                LOG_CAUGHT_EXCEPTION();
-            }
-        }
+        CATCH_LOG();
     }
+
+    try
+    {
+        _updateFont(nullptr, fontInfoDesired, fontInfo, features, axes);
+        return S_OK;
+    }
+    CATCH_RETURN();
 }
 
 void AtlasEngine::UpdateHyperlinkHoveredId(const uint16_t hoveredId) noexcept
@@ -598,11 +602,7 @@ void AtlasEngine::_resolveFontMetrics(const wchar_t* requestedFaceName, const Fo
 
     if (!requestedFaceName)
     {
-        requestedFaceName = fontInfoDesired.GetFaceName().c_str();
-        if (!requestedFaceName)
-        {
-            requestedFaceName = L"Consolas";
-        }
+        requestedFaceName = L"Consolas";
     }
     if (!requestedSize.height)
     {
@@ -614,22 +614,19 @@ void AtlasEngine::_resolveFontMetrics(const wchar_t* requestedFaceName, const Fo
         requestedWeight = DWRITE_FONT_WEIGHT_NORMAL;
     }
 
-    wil::com_ptr<IDWriteFontCollection> fontCollection;
-    THROW_IF_FAILED(_p.dwriteFactory->GetSystemFontCollection(fontCollection.addressof(), FALSE));
+    // UpdateFont() (and its NearbyFontLoading feature path specifically) sets `_api.s->font->fontCollection`
+    // to a custom font collection that includes .ttf files that are bundled with our app package. See GH#9375.
+    // 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.
+    auto fontCollection = _api.s->font->fontCollection;
+    if (!fontCollection)
+    {
+        THROW_IF_FAILED(_p.dwriteFactory->GetSystemFontCollection(fontCollection.addressof(), FALSE));
+    }
 
     u32 index = 0;
     BOOL exists = false;
     THROW_IF_FAILED(fontCollection->FindFamilyName(requestedFaceName, &index, &exists));
-
-    if constexpr (Feature_NearbyFontLoading::IsEnabled())
-    {
-        if (!exists)
-        {
-            fontCollection = FontCache::GetCached();
-            THROW_IF_FAILED(fontCollection->FindFamilyName(requestedFaceName, &index, &exists));
-        }
-    }
-
     THROW_HR_IF(DWRITE_E_NOFONT, !exists);
 
     wil::com_ptr<IDWriteFontFamily> fontFamily;
diff --git a/src/renderer/atlas/common.h b/src/renderer/atlas/common.h
index bb46ff61547..0a6d3773126 100644
--- a/src/renderer/atlas/common.h
+++ b/src/renderer/atlas/common.h
@@ -470,7 +470,6 @@ namespace Microsoft::Console::Render::Atlas
         wil::com_ptr<IDWriteFontFallback> systemFontFallback;
         wil::com_ptr<IDWriteFontFallback1> systemFontFallback1; // optional, might be nullptr
         wil::com_ptr<IDWriteTextAnalyzer1> textAnalyzer;
-        wil::com_ptr<IDWriteRenderingParams1> renderingParams;
         std::function<void(HRESULT)> warningCallback;
         std::function<void(HANDLE)> swapChainChangedCallback;
 
diff --git a/src/renderer/dx/DxFontInfo.cpp b/src/renderer/dx/DxFontInfo.cpp
index 4945939fd86..b1d4844a601 100644
--- a/src/renderer/dx/DxFontInfo.cpp
+++ b/src/renderer/dx/DxFontInfo.cpp
@@ -127,15 +127,6 @@ void DxFontInfo::SetFromEngine(const std::wstring_view familyName,
     {
         face = _FindFontFace(localeName);
 
-        if constexpr (Feature_NearbyFontLoading::IsEnabled())
-        {
-            if (!face)
-            {
-                _fontCollection = FontCache::GetCached();
-                face = _FindFontFace(localeName);
-            }
-        }
-
         if (!face)
         {
             // If we missed, try looking a little more by trimming the last word off the requested family name a few times.
@@ -167,6 +158,15 @@ void DxFontInfo::SetFromEngine(const std::wstring_view familyName,
     }
     CATCH_LOG();
 
+    if constexpr (Feature_NearbyFontLoading::IsEnabled())
+    {
+        if (!face)
+        {
+            _fontCollection = FontCache::GetCached();
+            face = _FindFontFace(localeName);
+        }
+    }
+
     // Alright, if our quick shot at trimming didn't work either...
     // move onto looking up a font from our hard-coded list of fonts
     // that should really always be available.


Test patch: