Skip to content

Commit

Permalink
Fix Windows 10 support for nearby font loading (#12554)
Browse files Browse the repository at this point in the history
By replacing `IDWriteFontSetBuilder2::AddFontFile` with
`IDWriteFactory5::CreateFontFileReference` and
`IDWriteFontSetBuilder1::AddFontFile` we add nearby
font loading support for Windows 10, build 15021.

This commit also fixes font fallback for AtlasEngine,
which was crashing during testing.

Finally it fixes a bug in DxEngine, where we only created a "nearby" font
collection if we couldn't find the font in the system collection. This doesn't
fix the bug, if the font is locked or broken in the system collection.

This is related to #11648.

* [x] Closes #12420
* [x] I work here
* [x] Tests added/passed

* Build a Debug version of Windows Terminal
* Put Jetbrains Mono into the writeable AppX directory
* Jetbrains Mono is present in the settings UI ✅
* DxEngine works with Jetbrains Mono ✅
* AtlasEngine works with Jetbrains Mono ✅

(cherry picked from commit f84ccad)
  • Loading branch information
lhecker authored and DHowett committed Mar 15, 2022
1 parent fca9d58 commit f42ff55
Show file tree
Hide file tree
Showing 10 changed files with 134 additions and 204 deletions.
60 changes: 3 additions & 57 deletions src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,55 +7,8 @@
#include "EnumEntry.h"

#include <LibraryResources.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;
}
#include "../WinRTUtils/inc/Utils.h"
#include "../../renderer/base/FontCache.h"

using namespace winrt::Windows::UI::Text;
using namespace winrt::Windows::UI::Xaml;
Expand Down Expand Up @@ -166,15 +119,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
std::vector<Editor::Font> fontList;
std::vector<Editor::Font> monospaceFontList;

// get a DWriteFactory
com_ptr<IDWriteFactory> factory;
THROW_IF_FAILED(DWriteCreateFactory(
DWRITE_FACTORY_TYPE_SHARED,
__uuidof(IDWriteFactory),
reinterpret_cast<::IUnknown**>(factory.put())));

// get the font collection; subscribe to updates
const auto fontCollection = NearbyCollection(factory.get());
const auto fontCollection = ::Microsoft::Console::Render::FontCache::GetFresh();

for (UINT32 i = 0; i < fontCollection->GetFontFamilyCount(); ++i)
{
Expand Down
9 changes: 9 additions & 0 deletions src/features.xml
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,13 @@
<!-- This feature will not ship to Stable until it is complete. -->
<alwaysDisabledReleaseTokens />
</feature>

<feature>
<name>Feature_NearbyFontLoading</name>
<description>Controls whether fonts in the same directory as the binary are used during rendering. Disabled for conhost so that it doesn't iterate the entire system32 directory.</description>
<stage>AlwaysEnabled</stage>
<alwaysDisabledBrandingTokens>
<brandingToken>WindowsInbox</brandingToken>
</alwaysDisabledBrandingTokens>
</feature>
</featureStaging>
96 changes: 96 additions & 0 deletions src/renderer/base/FontCache.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

#pragma once

namespace Microsoft::Console::Render::FontCache
{
namespace details
{
inline const std::vector<wil::com_ptr<IDWriteFontFile>>& getNearbyFontFiles(IDWriteFactory5* factory5)
{
static const auto fontFiles = [=]() {
std::vector<wil::com_ptr<IDWriteFontFile>> files;

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"))
{
wil::com_ptr<IDWriteFontFile> fontFile;
if (SUCCEEDED_LOG(factory5->CreateFontFileReference(p.path().c_str(), nullptr, fontFile.addressof())))
{
files.emplace_back(std::move(fontFile));
}
}
}

files.shrink_to_fit();
return files;
}();
return fontFiles;
}

inline wil::com_ptr<IDWriteFontCollection> getFontCollection(bool forceUpdate)
{
wil::com_ptr<IDWriteFactory> factory;
THROW_IF_FAILED(DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(factory), reinterpret_cast<::IUnknown**>(factory.addressof())));

wil::com_ptr<IDWriteFontCollection> systemFontCollection;
THROW_IF_FAILED(factory->GetSystemFontCollection(systemFontCollection.addressof(), forceUpdate));

if constexpr (Feature_NearbyFontLoading::IsEnabled())
{
// IDWriteFactory5 is supported since Windows 10, build 15021.
const auto factory5 = factory.try_query<IDWriteFactory5>();
if (!factory5)
{
return systemFontCollection;
}

const auto& nearbyFontFiles = getNearbyFontFiles(factory5.get());
if (nearbyFontFiles.empty())
{
return systemFontCollection;
}

wil::com_ptr<IDWriteFontSet> systemFontSet;
// IDWriteFontCollection1 is supported since Windows 7.
THROW_IF_FAILED(systemFontCollection.query<IDWriteFontCollection1>()->GetFontSet(systemFontSet.addressof()));

wil::com_ptr<IDWriteFontSetBuilder1> fontSetBuilder;
THROW_IF_FAILED(factory5->CreateFontSetBuilder(fontSetBuilder.addressof()));
THROW_IF_FAILED(fontSetBuilder->AddFontSet(systemFontSet.get()));

for (const auto& file : nearbyFontFiles)
{
LOG_IF_FAILED(fontSetBuilder->AddFontFile(file.get()));
}

wil::com_ptr<IDWriteFontSet> fontSet;
THROW_IF_FAILED(fontSetBuilder->CreateFontSet(fontSet.addressof()));

wil::com_ptr<IDWriteFontCollection1> fontCollection;
THROW_IF_FAILED(factory5->CreateFontCollectionFromFontSet(fontSet.get(), fontCollection.addressof()));

return std::move(fontCollection);
}
else
{
return systemFontCollection;
}
}
}

inline wil::com_ptr<IDWriteFontCollection> GetCached()
{
return details::getFontCollection(false);
}

inline wil::com_ptr<IDWriteFontCollection> GetFresh()
{
return details::getFontCollection(true);
}
}
1 change: 1 addition & 0 deletions src/renderer/base/lib/base.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
<ClInclude Include="..\..\inc\IRenderer.hpp" />
<ClInclude Include="..\..\inc\IRenderTarget.hpp" />
<ClInclude Include="..\..\inc\RenderEngineBase.hpp" />
<ClInclude Include="..\FontCache.h" />
<ClInclude Include="..\precomp.h" />
<ClInclude Include="..\renderer.hpp" />
<ClInclude Include="..\thread.hpp" />
Expand Down
5 changes: 4 additions & 1 deletion src/renderer/base/lib/base.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,11 @@
<ClInclude Include="..\..\inc\IRenderTarget.hpp">
<Filter>Header Files\inc</Filter>
</ClInclude>
<ClInclude Include="..\FontCache.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Natvis Include="$(SolutionDir)tools\ConsoleTypes.natvis" />
</ItemGroup>
</Project>
</Project>
Loading

0 comments on commit f42ff55

Please sign in to comment.