Allow users to set font features and font axes (#10525)

Adds support for users to be able to set font features and axes (see the spec for more details!)

## Detailed Description

**CustomTextLayout**
- Asks the `DxFontRenderData` for the font features when getting glyphs
- _If any features have been set/updated, we always skip the "isTextSimple" shortcut_
- Asks the `_formatInUse` for any font axes when mapping characters in `_AnalyzeFontFallback`

**DxFontRenderData**
- Stores a map of font features (initialized to the [standard feature list])
- Stores a map of font axes
- Has methods to add font features/axes to the map or update existing ones
- Has methods to retrieve the font features/axes
- Sets the font axes in the `IDWriteTextFormat` when creating it

## Validation Steps Performed
It works!

[standard feature list]: ac5aef67d1/DrawableObject.ixx (L802)

Specified in #10457
Related to #1790 
Closes #759
Closes #5828
This commit is contained in:
PankajBhojwani 2021-07-22 16:15:44 -07:00 committed by GitHub
parent 335f69e099
commit 4c16cb278e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 529 additions and 58 deletions

View file

@ -1,6 +1,9 @@
apc apc
calt
ccmp
Apc Apc
clickable clickable
clig
copyable copyable
dalet dalet
dcs dcs
@ -14,6 +17,7 @@ dzhe
Enum'd Enum'd
formattings formattings
ftp ftp
fvar
geeksforgeeks geeksforgeeks
ghe ghe
gje gje
@ -27,7 +31,9 @@ It'd
kje kje
liga liga
lje lje
locl
maxed maxed
mkmk
mru mru
nje nje
ogonek ogonek
@ -37,10 +43,12 @@ postmodern
ptys ptys
qof qof
qps qps
rclt
reimplementation reimplementation
reserialization reserialization
reserialize reserialize
reserializes reserializes
rlig
runtimes runtimes
shcha shcha
slnt slnt

View file

@ -155,6 +155,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// Set up the DX Engine // Set up the DX Engine
auto dxEngine = std::make_unique<::Microsoft::Console::Render::DxEngine>(); auto dxEngine = std::make_unique<::Microsoft::Console::Render::DxEngine>();
_renderer->AddRenderEngine(dxEngine.get()); _renderer->AddRenderEngine(dxEngine.get());
_renderEngine = std::move(dxEngine);
// Initialize our font with the renderer // Initialize our font with the renderer
// We don't have to care about DPI. We'll get a change message immediately if it's not 96 // We don't have to care about DPI. We'll get a change message immediately if it's not 96
@ -168,12 +169,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// Then, using the font, get the number of characters that can fit. // Then, using the font, get the number of characters that can fit.
// Resize our terminal connection to match that size, and initialize the terminal with that size. // Resize our terminal connection to match that size, and initialize the terminal with that size.
const auto viewInPixels = Viewport::FromDimensions({ 0, 0 }, windowSize); const auto viewInPixels = Viewport::FromDimensions({ 0, 0 }, windowSize);
LOG_IF_FAILED(dxEngine->SetWindowSize({ viewInPixels.Width(), viewInPixels.Height() })); LOG_IF_FAILED(_renderEngine->SetWindowSize({ viewInPixels.Width(), viewInPixels.Height() }));
// Update DxEngine's SelectionBackground // Update DxEngine's SelectionBackground
dxEngine->SetSelectionBackground(til::color{ _settings.SelectionBackground() }); _renderEngine->SetSelectionBackground(til::color{ _settings.SelectionBackground() });
const auto vp = dxEngine->GetViewportInCharacters(viewInPixels); const auto vp = _renderEngine->GetViewportInCharacters(viewInPixels);
const auto width = vp.Width(); const auto width = vp.Width();
const auto height = vp.Height(); const auto height = vp.Height();
_connection.Resize(height, width); _connection.Resize(height, width);
@ -188,27 +189,26 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// after Enable, then it'll be possible to paint the frame once // after Enable, then it'll be possible to paint the frame once
// _before_ the warning handler is set up, and then warnings from // _before_ the warning handler is set up, and then warnings from
// the first paint will be ignored! // the first paint will be ignored!
dxEngine->SetWarningCallback(std::bind(&ControlCore::_rendererWarning, this, std::placeholders::_1)); _renderEngine->SetWarningCallback(std::bind(&ControlCore::_rendererWarning, this, std::placeholders::_1));
// Tell the DX Engine to notify us when the swap chain changes. // Tell the DX Engine to notify us when the swap chain changes.
// We do this after we initially set the swapchain so as to avoid unnecessary callbacks (and locking problems) // We do this after we initially set the swapchain so as to avoid unnecessary callbacks (and locking problems)
dxEngine->SetCallback(std::bind(&ControlCore::_renderEngineSwapChainChanged, this)); _renderEngine->SetCallback(std::bind(&ControlCore::_renderEngineSwapChainChanged, this));
dxEngine->SetRetroTerminalEffect(_settings.RetroTerminalEffect()); _renderEngine->SetRetroTerminalEffect(_settings.RetroTerminalEffect());
dxEngine->SetPixelShaderPath(_settings.PixelShaderPath()); _renderEngine->SetPixelShaderPath(_settings.PixelShaderPath());
dxEngine->SetForceFullRepaintRendering(_settings.ForceFullRepaintRendering()); _renderEngine->SetForceFullRepaintRendering(_settings.ForceFullRepaintRendering());
dxEngine->SetSoftwareRendering(_settings.SoftwareRendering()); _renderEngine->SetSoftwareRendering(_settings.SoftwareRendering());
_updateAntiAliasingMode(dxEngine.get()); _updateAntiAliasingMode(_renderEngine.get());
// GH#5098: Inform the engine of the opacity of the default text background. // GH#5098: Inform the engine of the opacity of the default text background.
if (_settings.UseAcrylic()) if (_settings.UseAcrylic())
{ {
dxEngine->SetDefaultTextBackgroundOpacity(::base::saturated_cast<float>(_settings.TintOpacity())); _renderEngine->SetDefaultTextBackgroundOpacity(::base::saturated_cast<float>(_settings.TintOpacity()));
} }
THROW_IF_FAILED(dxEngine->Enable()); THROW_IF_FAILED(_renderEngine->Enable());
_renderEngine = std::move(dxEngine);
_initializedTerminal = true; _initializedTerminal = true;
} // scope for TerminalLock } // scope for TerminalLock
@ -603,9 +603,34 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_terminal->SetFontInfo(_actualFont); _terminal->SetFontInfo(_actualFont);
// TODO: MSFT:20895307 If the font doesn't exist, this doesn't if (_renderEngine)
// actually fail. We need a way to gracefully fallback. {
_renderer->TriggerFontChange(newDpi, _desiredFont, _actualFont); std::unordered_map<std::wstring_view, uint32_t> featureMap;
if (const auto fontFeatures = _settings.FontFeatures())
{
featureMap.reserve(fontFeatures.Size());
for (const auto& [tag, param] : fontFeatures)
{
featureMap.emplace(tag, param);
}
}
std::unordered_map<std::wstring_view, float> axesMap;
if (const auto fontAxes = _settings.FontAxes())
{
axesMap.reserve(fontAxes.Size());
for (const auto& [axis, value] : fontAxes)
{
axesMap.emplace(axis, value);
}
}
// TODO: MSFT:20895307 If the font doesn't exist, this doesn't
// actually fail. We need a way to gracefully fallback.
LOG_IF_FAILED(_renderEngine->UpdateDpi(newDpi));
LOG_IF_FAILED(_renderEngine->UpdateFont(_desiredFont, _actualFont, featureMap, axesMap));
}
// If the actual font isn't what was requested... // If the actual font isn't what was requested...
if (_actualFont.GetFaceName() != _desiredFont.GetFaceName()) if (_actualFont.GetFaceName() != _desiredFont.GetFaceName())

View file

@ -36,6 +36,8 @@ namespace Microsoft.Terminal.Control
Int32 FontSize; Int32 FontSize;
Windows.UI.Text.FontWeight FontWeight; Windows.UI.Text.FontWeight FontWeight;
String Padding; String Padding;
Windows.Foundation.Collections.IMap<String, UInt32> FontFeatures;
Windows.Foundation.Collections.IMap<String, Single> FontAxes;
Microsoft.Terminal.Control.IKeyBindings KeyBindings; Microsoft.Terminal.Control.IKeyBindings KeyBindings;

View file

@ -335,6 +335,8 @@ winrt::Microsoft::Terminal::Settings::Model::Profile CascadiaSettings::Duplicate
DUPLICATE_SETTING_MACRO_SUB(font, target, FontFace); DUPLICATE_SETTING_MACRO_SUB(font, target, FontFace);
DUPLICATE_SETTING_MACRO_SUB(font, target, FontSize); DUPLICATE_SETTING_MACRO_SUB(font, target, FontSize);
DUPLICATE_SETTING_MACRO_SUB(font, target, FontWeight); DUPLICATE_SETTING_MACRO_SUB(font, target, FontWeight);
DUPLICATE_SETTING_MACRO_SUB(font, target, FontFeatures);
DUPLICATE_SETTING_MACRO_SUB(font, target, FontAxes);
} }
{ {

View file

@ -14,6 +14,8 @@ static constexpr std::string_view FontInfoKey{ "font" };
static constexpr std::string_view FontFaceKey{ "face" }; static constexpr std::string_view FontFaceKey{ "face" };
static constexpr std::string_view FontSizeKey{ "size" }; static constexpr std::string_view FontSizeKey{ "size" };
static constexpr std::string_view FontWeightKey{ "weight" }; static constexpr std::string_view FontWeightKey{ "weight" };
static constexpr std::string_view FontFeaturesKey{ "features" };
static constexpr std::string_view FontAxesKey{ "axes" };
static constexpr std::string_view LegacyFontFaceKey{ "fontFace" }; static constexpr std::string_view LegacyFontFaceKey{ "fontFace" };
static constexpr std::string_view LegacyFontSizeKey{ "fontSize" }; static constexpr std::string_view LegacyFontSizeKey{ "fontSize" };
static constexpr std::string_view LegacyFontWeightKey{ "fontWeight" }; static constexpr std::string_view LegacyFontWeightKey{ "fontWeight" };
@ -29,6 +31,8 @@ winrt::com_ptr<FontConfig> FontConfig::CopyFontInfo(const winrt::com_ptr<FontCon
fontInfo->_FontFace = source->_FontFace; fontInfo->_FontFace = source->_FontFace;
fontInfo->_FontSize = source->_FontSize; fontInfo->_FontSize = source->_FontSize;
fontInfo->_FontWeight = source->_FontWeight; fontInfo->_FontWeight = source->_FontWeight;
fontInfo->_FontAxes = source->_FontAxes;
fontInfo->_FontFeatures = source->_FontFeatures;
return fontInfo; return fontInfo;
} }
@ -39,6 +43,8 @@ Json::Value FontConfig::ToJson() const
JsonUtils::SetValueForKey(json, FontFaceKey, _FontFace); JsonUtils::SetValueForKey(json, FontFaceKey, _FontFace);
JsonUtils::SetValueForKey(json, FontSizeKey, _FontSize); JsonUtils::SetValueForKey(json, FontSizeKey, _FontSize);
JsonUtils::SetValueForKey(json, FontWeightKey, _FontWeight); JsonUtils::SetValueForKey(json, FontWeightKey, _FontWeight);
JsonUtils::SetValueForKey(json, FontAxesKey, _FontAxes);
JsonUtils::SetValueForKey(json, FontFeaturesKey, _FontFeatures);
return json; return json;
} }
@ -65,6 +71,8 @@ void FontConfig::LayerJson(const Json::Value& json)
JsonUtils::GetValueForKey(fontInfoJson, FontFaceKey, _FontFace); JsonUtils::GetValueForKey(fontInfoJson, FontFaceKey, _FontFace);
JsonUtils::GetValueForKey(fontInfoJson, FontSizeKey, _FontSize); JsonUtils::GetValueForKey(fontInfoJson, FontSizeKey, _FontSize);
JsonUtils::GetValueForKey(fontInfoJson, FontWeightKey, _FontWeight); JsonUtils::GetValueForKey(fontInfoJson, FontWeightKey, _FontWeight);
JsonUtils::GetValueForKey(fontInfoJson, FontFeaturesKey, _FontFeatures);
JsonUtils::GetValueForKey(fontInfoJson, FontAxesKey, _FontAxes);
} }
else else
{ {

View file

@ -23,6 +23,9 @@ Author(s):
#include "IInheritable.h" #include "IInheritable.h"
#include <DefaultSettings.h> #include <DefaultSettings.h>
using IFontAxesMap = winrt::Windows::Foundation::Collections::IMap<winrt::hstring, float>;
using IFontFeatureMap = winrt::Windows::Foundation::Collections::IMap<winrt::hstring, uint32_t>;
namespace winrt::Microsoft::Terminal::Settings::Model::implementation namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{ {
struct FontConfig : FontConfigT<FontConfig>, IInheritable<FontConfig> struct FontConfig : FontConfigT<FontConfig>, IInheritable<FontConfig>
@ -39,6 +42,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
INHERITABLE_SETTING(Model::FontConfig, hstring, FontFace, DEFAULT_FONT_FACE); INHERITABLE_SETTING(Model::FontConfig, hstring, FontFace, DEFAULT_FONT_FACE);
INHERITABLE_SETTING(Model::FontConfig, int32_t, FontSize, DEFAULT_FONT_SIZE); INHERITABLE_SETTING(Model::FontConfig, int32_t, FontSize, DEFAULT_FONT_SIZE);
INHERITABLE_SETTING(Model::FontConfig, Windows::UI::Text::FontWeight, FontWeight, DEFAULT_FONT_WEIGHT); INHERITABLE_SETTING(Model::FontConfig, Windows::UI::Text::FontWeight, FontWeight, DEFAULT_FONT_WEIGHT);
INHERITABLE_SETTING(Model::FontConfig, IFontAxesMap, FontAxes);
INHERITABLE_SETTING(Model::FontConfig, IFontFeatureMap, FontFeatures);
private: private:
winrt::weak_ref<Profile> _sourceProfile; winrt::weak_ref<Profile> _sourceProfile;

View file

@ -8,6 +8,8 @@ import "Profile.idl";
_BASE_INHERITABLE_SETTING(Type, Name); \ _BASE_INHERITABLE_SETTING(Type, Name); \
Microsoft.Terminal.Settings.Model.FontConfig Name##OverrideSource { get; } Microsoft.Terminal.Settings.Model.FontConfig Name##OverrideSource { get; }
#define COMMA ,
namespace Microsoft.Terminal.Settings.Model namespace Microsoft.Terminal.Settings.Model
{ {
[default_interface] runtimeclass FontConfig { [default_interface] runtimeclass FontConfig {
@ -16,5 +18,8 @@ namespace Microsoft.Terminal.Settings.Model
INHERITABLE_FONT_SETTING(String, FontFace); INHERITABLE_FONT_SETTING(String, FontFace);
INHERITABLE_FONT_SETTING(Int32, FontSize); INHERITABLE_FONT_SETTING(Int32, FontSize);
INHERITABLE_FONT_SETTING(Windows.UI.Text.FontWeight, FontWeight); INHERITABLE_FONT_SETTING(Windows.UI.Text.FontWeight, FontWeight);
INHERITABLE_FONT_SETTING(Windows.Foundation.Collections.IMap<String COMMA UInt32>, FontFeatures);
INHERITABLE_FONT_SETTING(Windows.Foundation.Collections.IMap<String COMMA Single>, FontAxes);
} }
} }

View file

@ -177,6 +177,58 @@ namespace Microsoft::Terminal::Settings::Model::JsonUtils
} }
}; };
template<typename T>
struct ConversionTrait<std::unordered_map<std::string, T>>
{
std::unordered_map<std::string, T> FromJson(const Json::Value& json) const
{
std::unordered_map<std::string, T> val;
val.reserve(json.size());
ConversionTrait<T> trait;
for (auto it = json.begin(), end = json.end(); it != end; ++it)
{
GetValue(*it, val[it.name()], trait);
}
return val;
}
bool CanConvert(const Json::Value& json) const
{
if (!json.isObject())
{
return false;
}
ConversionTrait<T> trait;
for (const auto& v : json)
{
if (!trait.CanConvert(v))
{
return false;
}
}
return true;
}
Json::Value ToJson(const std::unordered_map<std::string, T>& val)
{
Json::Value json{ Json::objectValue };
for (const auto& [k, v] : val)
{
SetValueForKey(json, k, v);
}
return json;
}
std::string TypeDescription() const
{
return fmt::format("map (string, {})", ConversionTrait<T>{}.TypeDescription());
}
};
#ifdef WINRT_BASE_H #ifdef WINRT_BASE_H
template<> template<>
struct ConversionTrait<winrt::hstring> : public ConversionTrait<std::wstring> struct ConversionTrait<winrt::hstring> : public ConversionTrait<std::wstring>
@ -206,6 +258,58 @@ namespace Microsoft::Terminal::Settings::Model::JsonUtils
return ConversionTrait<std::wstring>::CanConvert(json) || json.isNull(); return ConversionTrait<std::wstring>::CanConvert(json) || json.isNull();
} }
}; };
template<typename T>
struct ConversionTrait<winrt::Windows::Foundation::Collections::IMap<winrt::hstring, T>>
{
winrt::Windows::Foundation::Collections::IMap<winrt::hstring, T> FromJson(const Json::Value& json) const
{
std::unordered_map<winrt::hstring, T> val;
val.reserve(json.size());
ConversionTrait<T> trait;
for (auto it = json.begin(), end = json.end(); it != end; ++it)
{
GetValue(*it, val[winrt::to_hstring(it.name())], trait);
}
return winrt::single_threaded_map<winrt::hstring, T>(std::move(val));
}
bool CanConvert(const Json::Value& json) const
{
if (!json.isObject())
{
return false;
}
ConversionTrait<T> trait;
for (const auto& v : json)
{
if (!trait.CanConvert(v))
{
return false;
}
}
return true;
}
Json::Value ToJson(const winrt::Windows::Foundation::Collections::IMap<winrt::hstring, T>& val)
{
Json::Value json{ Json::objectValue };
for (const auto& [k, v] : val)
{
SetValueForKey(json, til::u16u8(k), v);
}
return json;
}
std::string TypeDescription() const
{
return fmt::format("map (string, {})", ConversionTrait<T>{}.TypeDescription());
}
};
#endif #endif
template<> template<>

View file

@ -279,6 +279,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
_FontFace = profile.FontInfo().FontFace(); _FontFace = profile.FontInfo().FontFace();
_FontSize = profile.FontInfo().FontSize(); _FontSize = profile.FontInfo().FontSize();
_FontWeight = profile.FontInfo().FontWeight(); _FontWeight = profile.FontInfo().FontWeight();
_FontFeatures = profile.FontInfo().FontFeatures();
_FontAxes = profile.FontInfo().FontAxes();
_Padding = profile.Padding(); _Padding = profile.Padding();
_Commandline = profile.Commandline(); _Commandline = profile.Commandline();

View file

@ -21,6 +21,9 @@ Author(s):
#include <DefaultSettings.h> #include <DefaultSettings.h>
#include <conattrs.hpp> #include <conattrs.hpp>
using IFontAxesMap = winrt::Windows::Foundation::Collections::IMap<winrt::hstring, float>;
using IFontFeatureMap = winrt::Windows::Foundation::Collections::IMap<winrt::hstring, uint32_t>;
// fwdecl unittest classes // fwdecl unittest classes
namespace SettingsModelLocalTests namespace SettingsModelLocalTests
{ {
@ -123,6 +126,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
INHERITABLE_SETTING(Model::TerminalSettings, int32_t, FontSize, DEFAULT_FONT_SIZE); INHERITABLE_SETTING(Model::TerminalSettings, int32_t, FontSize, DEFAULT_FONT_SIZE);
INHERITABLE_SETTING(Model::TerminalSettings, winrt::Windows::UI::Text::FontWeight, FontWeight); INHERITABLE_SETTING(Model::TerminalSettings, winrt::Windows::UI::Text::FontWeight, FontWeight);
INHERITABLE_SETTING(Model::TerminalSettings, IFontAxesMap, FontAxes);
INHERITABLE_SETTING(Model::TerminalSettings, IFontFeatureMap, FontFeatures);
INHERITABLE_SETTING(Model::TerminalSettings, hstring, BackgroundImage); INHERITABLE_SETTING(Model::TerminalSettings, hstring, BackgroundImage);
INHERITABLE_SETTING(Model::TerminalSettings, double, BackgroundImageOpacity, 1.0); INHERITABLE_SETTING(Model::TerminalSettings, double, BackgroundImageOpacity, 1.0);

View file

@ -8,6 +8,9 @@ Licensed under the MIT license.
#include <DefaultSettings.h> #include <DefaultSettings.h>
#include <conattrs.hpp> #include <conattrs.hpp>
using IFontFeatureMap = winrt::Windows::Foundation::Collections::IMap<winrt::hstring, uint32_t>;
using IFontAxesMap = winrt::Windows::Foundation::Collections::IMap<winrt::hstring, float>;
namespace ControlUnitTests namespace ControlUnitTests
{ {
class MockControlSettings : public winrt::implements<MockControlSettings, winrt::Microsoft::Terminal::Core::ICoreSettings, winrt::Microsoft::Terminal::Control::IControlSettings, winrt::Microsoft::Terminal::Core::ICoreAppearance, winrt::Microsoft::Terminal::Control::IControlAppearance> class MockControlSettings : public winrt::implements<MockControlSettings, winrt::Microsoft::Terminal::Core::ICoreSettings, winrt::Microsoft::Terminal::Control::IControlSettings, winrt::Microsoft::Terminal::Core::ICoreAppearance, winrt::Microsoft::Terminal::Control::IControlAppearance>
@ -80,6 +83,9 @@ namespace ControlUnitTests
WINRT_PROPERTY(winrt::hstring, PixelShaderPath); WINRT_PROPERTY(winrt::hstring, PixelShaderPath);
WINRT_PROPERTY(IFontFeatureMap, FontFeatures);
WINRT_PROPERTY(IFontAxesMap, FontAxes);
private: private:
std::array<winrt::Microsoft::Terminal::Core::Color, COLOR_TABLE_SIZE> _ColorTable; std::array<winrt::Microsoft::Terminal::Core::Color, COLOR_TABLE_SIZE> _ColorTable;

View file

@ -357,7 +357,7 @@ CATCH_RETURN()
_glyphIndices.resize(totalGlyphsArrayCount); _glyphIndices.resize(totalGlyphsArrayCount);
} }
if (_isEntireTextSimple) if (_isEntireTextSimple && !_fontRenderData->DidUserSetFeatures())
{ {
// When the entire text is simple, we can skip GetGlyphs and directly retrieve glyph indices and // When the entire text is simple, we can skip GetGlyphs and directly retrieve glyph indices and
// advances(in font design unit). With the help of font metrics, we can calculate the actual glyph // advances(in font design unit). With the help of font metrics, we can calculate the actual glyph
@ -396,10 +396,18 @@ CATCH_RETURN()
std::vector<DWRITE_SHAPING_TEXT_PROPERTIES> textProps(textLength); std::vector<DWRITE_SHAPING_TEXT_PROPERTIES> textProps(textLength);
std::vector<DWRITE_SHAPING_GLYPH_PROPERTIES> glyphProps(maxGlyphCount); std::vector<DWRITE_SHAPING_GLYPH_PROPERTIES> glyphProps(maxGlyphCount);
// Get the features to apply to the font
auto features = _fontRenderData->DefaultFontFeatures();
DWRITE_FONT_FEATURE* featureList = features.data();
DWRITE_TYPOGRAPHIC_FEATURES typographicFeatures = { &featureList[0], gsl::narrow<uint32_t>(features.size()) };
DWRITE_TYPOGRAPHIC_FEATURES const* typographicFeaturesPointer = &typographicFeatures;
const uint32_t fontFeatureLengths[] = { textLength };
// Get the glyphs from the text, retrying if needed. // Get the glyphs from the text, retrying if needed.
int tries = 0; int tries = 0;
#pragma warning(suppress : 26485) // so we can pass in the fontFeatureLengths to GetGlyphs without the analyzer complaining
HRESULT hr = S_OK; HRESULT hr = S_OK;
do do
{ {
@ -412,9 +420,9 @@ CATCH_RETURN()
&run.script, &run.script,
_localeName.data(), _localeName.data(),
(run.isNumberSubstituted) ? _numberSubstitution.Get() : nullptr, (run.isNumberSubstituted) ? _numberSubstitution.Get() : nullptr,
nullptr, // features &typographicFeaturesPointer, // features
nullptr, // featureLengths &fontFeatureLengths[0], // featureLengths
0, // featureCount 1, // featureCount
maxGlyphCount, // maxGlyphCount maxGlyphCount, // maxGlyphCount
&_glyphClusters.at(textStart), &_glyphClusters.at(textStart),
&textProps.at(0), &textProps.at(0),
@ -462,9 +470,9 @@ CATCH_RETURN()
(run.bidiLevel & 1), // isRightToLeft (run.bidiLevel & 1), // isRightToLeft
&run.script, &run.script,
_localeName.data(), _localeName.data(),
nullptr, // features &typographicFeaturesPointer, // features
nullptr, // featureRangeLengths &fontFeatureLengths[0], // featureLengths
0, // featureRanges 1, // featureCount
&_glyphAdvances.at(glyphStart), &_glyphAdvances.at(glyphStart),
&_glyphOffsets.at(glyphStart)); &_glyphOffsets.at(glyphStart));
@ -1264,29 +1272,71 @@ CATCH_RETURN();
fallback = _fontRenderData->SystemFontFallback(); fallback = _fontRenderData->SystemFontFallback();
} }
// Walk through and analyze the entire string ::Microsoft::WRL::ComPtr<IDWriteFontFallback1> fallback1;
while (textLength > 0) ::Microsoft::WRL::ComPtr<IDWriteTextFormat3> format3;
// If the OS supports IDWriteFontFallback1 and IDWriteTextFormat3, we can use the
// newer MapCharacters to apply axes of variation to the font
if (!FAILED(_formatInUse->QueryInterface(IID_PPV_ARGS(&format3))) && !FAILED(fallback->QueryInterface(IID_PPV_ARGS(&fallback1))))
{ {
UINT32 mappedLength = 0; const auto axesVector = _fontRenderData->GetAxisVector(weight, stretch, style, format3.Get());
::Microsoft::WRL::ComPtr<IDWriteFont> mappedFont; // Walk through and analyze the entire string
FLOAT scale = 0.0f; while (textLength > 0)
{
UINT32 mappedLength = 0;
::Microsoft::WRL::ComPtr<IDWriteFontFace5> mappedFont;
FLOAT scale = 0.0f;
fallback->MapCharacters(source, fallback1->MapCharacters(source,
textPosition, textPosition,
textLength, textLength,
collection.Get(), collection.Get(),
familyName.data(), familyName.data(),
weight, axesVector.data(),
style, gsl::narrow<uint32_t>(axesVector.size()),
stretch, &mappedLength,
&mappedLength, &scale,
&mappedFont, &mappedFont);
&scale);
RETURN_IF_FAILED(_SetMappedFont(textPosition, mappedLength, mappedFont.Get(), scale)); RETURN_IF_FAILED(_SetMappedFontFace(textPosition, mappedLength, mappedFont, scale));
textPosition += mappedLength; textPosition += mappedLength;
textLength -= mappedLength; textLength -= mappedLength;
}
}
else
{
// The chunk of code below is very similar to the one above, unfortunately this needs
// to stay for Win7 compatibility reasons. It is also not possible to combine the two
// because they call different versions of MapCharacters
// Walk through and analyze the entire string
while (textLength > 0)
{
UINT32 mappedLength = 0;
::Microsoft::WRL::ComPtr<IDWriteFont> mappedFont;
FLOAT scale = 0.0f;
fallback->MapCharacters(source,
textPosition,
textLength,
collection.Get(),
familyName.data(),
weight,
style,
stretch,
&mappedLength,
&mappedFont,
&scale);
RETURN_LAST_ERROR_IF(!mappedFont);
::Microsoft::WRL::ComPtr<IDWriteFontFace> face;
RETURN_IF_FAILED(mappedFont->CreateFontFace(&face));
RETURN_IF_FAILED(_SetMappedFontFace(textPosition, mappedLength, face, scale));
textPosition += mappedLength;
textLength -= mappedLength;
}
} }
} }
CATCH_RETURN(); CATCH_RETURN();
@ -1300,14 +1350,14 @@ CATCH_RETURN();
// Arguments: // Arguments:
// - textPosition - the index to start the substring operation // - textPosition - the index to start the substring operation
// - textLength - the length of the substring operation // - textLength - the length of the substring operation
// - font - the font that applies to the substring range // - fontFace - the fontFace that applies to the substring range
// - scale - the scale of the font to apply // - scale - the scale of the font to apply
// Return Value: // Return Value:
// - S_OK or appropriate STL/GSL failure code. // - S_OK or appropriate STL/GSL failure code.
[[nodiscard]] HRESULT STDMETHODCALLTYPE CustomTextLayout::_SetMappedFont(UINT32 textPosition, [[nodiscard]] HRESULT STDMETHODCALLTYPE CustomTextLayout::_SetMappedFontFace(UINT32 textPosition,
UINT32 textLength, UINT32 textLength,
_In_ IDWriteFont* const font, const ::Microsoft::WRL::ComPtr<IDWriteFontFace>& fontFace,
FLOAT const scale) FLOAT const scale)
{ {
try try
{ {
@ -1317,14 +1367,9 @@ CATCH_RETURN();
{ {
auto& run = _FetchNextRun(textLength); auto& run = _FetchNextRun(textLength);
if (font != nullptr) if (fontFace != nullptr)
{ {
// Get font face from font metadata RETURN_IF_FAILED(fontFace.As(&run.fontFace));
::Microsoft::WRL::ComPtr<IDWriteFontFace> face;
RETURN_IF_FAILED(font->CreateFontFace(&face));
// QI for Face5 interface from base face interface, store into run
RETURN_IF_FAILED(face.As(&run.fontFace));
} }
else else
{ {

View file

@ -126,7 +126,7 @@ namespace Microsoft::Console::Render
void _OrderRuns(); void _OrderRuns();
[[nodiscard]] HRESULT STDMETHODCALLTYPE _AnalyzeFontFallback(IDWriteTextAnalysisSource* const source, UINT32 textPosition, UINT32 textLength); [[nodiscard]] HRESULT STDMETHODCALLTYPE _AnalyzeFontFallback(IDWriteTextAnalysisSource* const source, UINT32 textPosition, UINT32 textLength);
[[nodiscard]] HRESULT STDMETHODCALLTYPE _SetMappedFont(UINT32 textPosition, UINT32 textLength, IDWriteFont* const font, FLOAT const scale); [[nodiscard]] HRESULT STDMETHODCALLTYPE _SetMappedFontFace(UINT32 textPosition, UINT32 textLength, const ::Microsoft::WRL::ComPtr<IDWriteFontFace>& fontFace, FLOAT const scale);
[[nodiscard]] HRESULT STDMETHODCALLTYPE _AnalyzeBoxDrawing(gsl::not_null<IDWriteTextAnalysisSource*> const source, UINT32 textPosition, UINT32 textLength); [[nodiscard]] HRESULT STDMETHODCALLTYPE _AnalyzeBoxDrawing(gsl::not_null<IDWriteTextAnalysisSource*> const source, UINT32 textPosition, UINT32 textLength);
[[nodiscard]] HRESULT STDMETHODCALLTYPE _SetBoxEffect(UINT32 textPosition, UINT32 textLength); [[nodiscard]] HRESULT STDMETHODCALLTYPE _SetBoxEffect(UINT32 textPosition, UINT32 textLength);

View file

@ -12,6 +12,7 @@
static constexpr float POINTS_PER_INCH = 72.0f; static constexpr float POINTS_PER_INCH = 72.0f;
static constexpr std::wstring_view FALLBACK_FONT_FACES[] = { L"Consolas", L"Lucida Console", L"Courier New" }; static constexpr std::wstring_view FALLBACK_FONT_FACES[] = { L"Consolas", L"Lucida Console", L"Courier New" };
static constexpr std::wstring_view FALLBACK_LOCALE = L"en-us"; static constexpr std::wstring_view FALLBACK_LOCALE = L"en-us";
static constexpr size_t TAG_LENGTH = 4;
using namespace Microsoft::Console::Render; using namespace Microsoft::Console::Render;
@ -93,6 +94,11 @@ DxFontRenderData::DxFontRenderData(::Microsoft::WRL::ComPtr<IDWriteFactory1> dwr
return _defaultFontInfo.GetStretch(); return _defaultFontInfo.GetStretch();
} }
[[nodiscard]] const std::vector<DWRITE_FONT_FEATURE>& DxFontRenderData::DefaultFontFeatures() const noexcept
{
return _featureVector;
}
[[nodiscard]] Microsoft::WRL::ComPtr<IDWriteTextFormat> DxFontRenderData::DefaultTextFormat() [[nodiscard]] Microsoft::WRL::ComPtr<IDWriteTextFormat> DxFontRenderData::DefaultTextFormat()
{ {
return TextFormatWithAttribute(_defaultFontInfo.GetWeight(), _defaultFontInfo.GetStyle(), _defaultFontInfo.GetStretch()); return TextFormatWithAttribute(_defaultFontInfo.GetWeight(), _defaultFontInfo.GetStyle(), _defaultFontInfo.GetStretch());
@ -178,7 +184,7 @@ DxFontRenderData::DxFontRenderData(::Microsoft::WRL::ComPtr<IDWriteFactory1> dwr
// - dpi - The DPI of the screen // - dpi - The DPI of the screen
// Return Value: // Return Value:
// - S_OK or relevant DirectX error // - S_OK or relevant DirectX error
[[nodiscard]] HRESULT DxFontRenderData::UpdateFont(const FontInfoDesired& desired, FontInfo& actual, const int dpi) noexcept [[nodiscard]] HRESULT DxFontRenderData::UpdateFont(const FontInfoDesired& desired, FontInfo& actual, const int dpi, const std::unordered_map<std::wstring_view, uint32_t>& features, const std::unordered_map<std::wstring_view, float>& axes) noexcept
{ {
try try
{ {
@ -193,6 +199,9 @@ DxFontRenderData::DxFontRenderData(::Microsoft::WRL::ComPtr<IDWriteFactory1> dwr
DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STYLE_NORMAL,
DWRITE_FONT_STRETCH_NORMAL); DWRITE_FONT_STRETCH_NORMAL);
_SetFeatures(features);
_SetAxes(axes);
_BuildFontRenderData(desired, actual, dpi); _BuildFontRenderData(desired, actual, dpi);
} }
CATCH_RETURN(); CATCH_RETURN();
@ -441,6 +450,195 @@ try
} }
CATCH_RETURN() CATCH_RETURN()
// Routine Description:
// - Returns whether the user set or updated any of the font features to be applied
bool DxFontRenderData::DidUserSetFeatures() const noexcept
{
return _didUserSetFeatures;
}
// Routine Description:
// - Updates our internal map of font features with the given features
// - NOTE TO CALLER: Make sure to call _BuildFontRenderData after calling this for the feature changes
// to take place
// Arguments:
// - features - the features to update our map with
void DxFontRenderData::_SetFeatures(const std::unordered_map<std::wstring_view, uint32_t>& features)
{
// Populate the feature map with the standard list first
std::unordered_map<DWRITE_FONT_FEATURE_TAG, uint32_t> featureMap{
{ DWRITE_MAKE_FONT_FEATURE_TAG('r', 'l', 'i', 'g'), 1 }, // Required Ligatures
{ DWRITE_MAKE_FONT_FEATURE_TAG('r', 'c', 'l', 't'), 1 }, // Required Contextual Alternates
{ DWRITE_MAKE_FONT_FEATURE_TAG('l', 'o', 'c', 'l'), 1 }, // Localized Forms
{ DWRITE_MAKE_FONT_FEATURE_TAG('c', 'c', 'm', 'p'), 1 }, // Glyph Composition / Decomposition
{ DWRITE_MAKE_FONT_FEATURE_TAG('c', 'a', 'l', 't'), 1 }, // Contextual Alternates
{ DWRITE_MAKE_FONT_FEATURE_TAG('l', 'i', 'g', 'a'), 1 }, // Standard Ligatures
{ DWRITE_MAKE_FONT_FEATURE_TAG('c', 'l', 'i', 'g'), 1 }, // Contextual Ligatures
{ DWRITE_MAKE_FONT_FEATURE_TAG('k', 'e', 'r', 'n'), 1 }, // Kerning
{ DWRITE_MAKE_FONT_FEATURE_TAG('m', 'a', 'r', 'k'), 1 }, // Mark Positioning
{ DWRITE_MAKE_FONT_FEATURE_TAG('m', 'k', 'm', 'k'), 1 }, // Mark to Mark Positioning
{ DWRITE_MAKE_FONT_FEATURE_TAG('d', 'i', 's', 't'), 1 } // Distances
};
// Update our feature map with the provided features
if (!features.empty())
{
for (const auto [tag, param] : features)
{
if (tag.length() == TAG_LENGTH)
{
featureMap.insert_or_assign(DWRITE_MAKE_FONT_FEATURE_TAG(til::at(tag, 0), til::at(tag, 1), til::at(tag, 2), til::at(tag, 3)), param);
}
}
_didUserSetFeatures = true;
}
else
{
_didUserSetFeatures = false;
}
// Convert the data to DWRITE_FONT_FEATURE and store it in a vector for CustomTextLayout
_featureVector.clear();
for (const auto [tag, param] : featureMap)
{
_featureVector.push_back(DWRITE_FONT_FEATURE{ tag, param });
}
}
// Routine Description:
// - Updates our internal map of font axes with the given axes
// - NOTE TO CALLER: Make sure to call _BuildFontRenderData after calling this for the axes changes
// to take place
// Arguments:
// - axes - the axes to update our map with
void DxFontRenderData::_SetAxes(const std::unordered_map<std::wstring_view, float>& axes)
{
_axesVector.clear();
// Update our axis map with the provided axes
#pragma warning(suppress : 26445) // the analyzer doesn't like reference to string_view
for (const auto& [axis, value] : axes)
{
if (axis.length() == TAG_LENGTH)
{
const auto dwriteTag = DWRITE_MAKE_FONT_AXIS_TAG(til::at(axis, 0), til::at(axis, 1), til::at(axis, 2), til::at(axis, 3));
_axesVector.emplace_back(DWRITE_FONT_AXIS_VALUE{ dwriteTag, value });
}
}
}
// Method Description:
// - Converts a DWRITE_FONT_STRETCH enum into the corresponding float value to
// create a DWRITE_FONT_AXIS_VALUE with
// Arguments:
// - fontStretch: the old DWRITE_FONT_STRETCH enum to be converted into an axis value
// Return value:
// - The float value corresponding to the passed in fontStretch
float DxFontRenderData::_FontStretchToWidthAxisValue(DWRITE_FONT_STRETCH fontStretch) noexcept
{
// 10 elements from DWRITE_FONT_STRETCH_UNDEFINED (0) to DWRITE_FONT_STRETCH_ULTRA_EXPANDED (9)
static constexpr auto fontStretchEnumToVal = std::array{ 100.0f, 50.0f, 62.5f, 75.0f, 87.5f, 100.0f, 112.5f, 125.0f, 150.0f, 200.0f };
if (gsl::narrow_cast<size_t>(fontStretch) > fontStretchEnumToVal.size())
{
fontStretch = DWRITE_FONT_STRETCH_NORMAL;
}
return til::at(fontStretchEnumToVal, fontStretch);
}
// Method Description:
// - Converts a DWRITE_FONT_STYLE enum into the corresponding float value to
// create a DWRITE_FONT_AXIS_VALUE with
// Arguments:
// - fontStyle: the old DWRITE_FONT_STYLE enum to be converted into an axis value
// Return value:
// - The float value corresponding to the passed in fontStyle
float DxFontRenderData::_FontStyleToSlantFixedAxisValue(DWRITE_FONT_STYLE fontStyle) noexcept
{
// DWRITE_FONT_STYLE_NORMAL (0), DWRITE_FONT_STYLE_OBLIQUE (1), DWRITE_FONT_STYLE_ITALIC (2)
static constexpr auto fontStyleEnumToVal = std::array{ 0.0f, -20.0f, -12.0f };
// Both DWRITE_FONT_STYLE_OBLIQUE and DWRITE_FONT_STYLE_ITALIC default to having slant.
// Though an italic font technically need not have slant (there exist upright ones), the
// vast majority of italic fonts are also slanted. Ideally the slant comes from the
// 'slnt' value in the STAT or fvar table, or the post table italic angle.
if (gsl::narrow_cast<size_t>(fontStyle) > fontStyleEnumToVal.size())
{
fontStyle = DWRITE_FONT_STYLE_NORMAL;
}
return til::at(fontStyleEnumToVal, fontStyle);
}
// Method Description:
// - Fill any missing axis values that might be known but were unspecified, such as omitting
// the 'wght' axis tag but specifying the old DWRITE_FONT_WEIGHT enum
// - This function will only be called with a valid IDWriteTextFormat3
// (on platforms where IDWriteTextFormat3 is supported)
// Arguments:
// - fontWeight: the old DWRITE_FONT_WEIGHT enum to be converted into an axis value
// - fontStretch: the old DWRITE_FONT_STRETCH enum to be converted into an axis value
// - fontStyle: the old DWRITE_FONT_STYLE enum to be converted into an axis value
// - fontSize: the number to convert into an axis value
// - format: the IDWriteTextFormat3 to get the defined axes from
// Return value:
// - The fully formed axes vector
#pragma warning(suppress : 26429) // the analyzer doesn't detect that our FAIL_FAST_IF_NULL macro \
// checks format for nullness
std::vector<DWRITE_FONT_AXIS_VALUE> DxFontRenderData::GetAxisVector(const DWRITE_FONT_WEIGHT fontWeight,
const DWRITE_FONT_STRETCH fontStretch,
const DWRITE_FONT_STYLE fontStyle,
IDWriteTextFormat3* format)
{
FAIL_FAST_IF_NULL(format);
const auto axesCount = format->GetFontAxisValueCount();
std::vector<DWRITE_FONT_AXIS_VALUE> axesVector;
axesVector.resize(axesCount);
format->GetFontAxisValues(axesVector.data(), axesCount);
auto axisTagPresence = AxisTagPresence::None;
for (const auto& fontAxisValue : axesVector)
{
switch (fontAxisValue.axisTag)
{
case DWRITE_FONT_AXIS_TAG_WEIGHT:
WI_SetFlag(axisTagPresence, AxisTagPresence::Weight);
break;
case DWRITE_FONT_AXIS_TAG_WIDTH:
WI_SetFlag(axisTagPresence, AxisTagPresence::Width);
break;
case DWRITE_FONT_AXIS_TAG_ITALIC:
WI_SetFlag(axisTagPresence, AxisTagPresence::Italic);
break;
case DWRITE_FONT_AXIS_TAG_SLANT:
WI_SetFlag(axisTagPresence, AxisTagPresence::Slant);
break;
}
}
if (WI_IsFlagClear(axisTagPresence, AxisTagPresence::Weight))
{
axesVector.emplace_back(DWRITE_FONT_AXIS_VALUE{ DWRITE_FONT_AXIS_TAG_WEIGHT, gsl::narrow<float>(fontWeight) });
}
if (WI_IsFlagClear(axisTagPresence, AxisTagPresence::Width))
{
axesVector.emplace_back(DWRITE_FONT_AXIS_VALUE{ DWRITE_FONT_AXIS_TAG_WIDTH, _FontStretchToWidthAxisValue(fontStretch) });
}
if (WI_IsFlagClear(axisTagPresence, AxisTagPresence::Italic))
{
axesVector.emplace_back(DWRITE_FONT_AXIS_VALUE{ DWRITE_FONT_AXIS_TAG_ITALIC, (fontStyle == DWRITE_FONT_STYLE_ITALIC ? 1.0f : 0.0f) });
}
if (WI_IsFlagClear(axisTagPresence, AxisTagPresence::Slant))
{
axesVector.emplace_back(DWRITE_FONT_AXIS_VALUE{ DWRITE_FONT_AXIS_TAG_SLANT, _FontStyleToSlantFixedAxisValue(fontStyle) });
}
return axesVector;
}
// Routine Description: // Routine Description:
// - Build the needed data for rendering according to the font used // - Build the needed data for rendering according to the font used
// Arguments: // Arguments:
@ -649,5 +847,14 @@ Microsoft::WRL::ComPtr<IDWriteTextFormat> DxFontRenderData::_BuildTextFormat(con
_fontSize, _fontSize,
localeName.data(), localeName.data(),
&format)); &format));
// If the OS supports IDWriteTextFormat3, set the font axes
::Microsoft::WRL::ComPtr<IDWriteTextFormat3> format3;
if (!_axesVector.empty() && !FAILED(format->QueryInterface(IID_PPV_ARGS(&format3))))
{
DWRITE_FONT_AXIS_VALUE const* axesList = _axesVector.data();
format3->SetFontAxisValues(axesList, gsl::narrow<uint32_t>(_axesVector.size()));
}
return format; return format;
} }

View file

@ -16,6 +16,16 @@
namespace Microsoft::Console::Render namespace Microsoft::Console::Render
{ {
enum class AxisTagPresence : BYTE
{
None = 0x00,
Weight = 0x01,
Width = 0x02,
Italic = 0x04,
Slant = 0x08,
};
DEFINE_ENUM_FLAG_OPERATORS(AxisTagPresence);
class DxFontRenderData class DxFontRenderData
{ {
public: public:
@ -51,6 +61,9 @@ namespace Microsoft::Console::Render
// The stretch of default font // The stretch of default font
[[nodiscard]] DWRITE_FONT_STRETCH DefaultFontStretch() noexcept; [[nodiscard]] DWRITE_FONT_STRETCH DefaultFontStretch() noexcept;
// The font features of the default font
[[nodiscard]] const std::vector<DWRITE_FONT_FEATURE>& DefaultFontFeatures() const noexcept;
// The DirectWrite format object representing the size and other text properties to be applied (by default) // The DirectWrite format object representing the size and other text properties to be applied (by default)
[[nodiscard]] Microsoft::WRL::ComPtr<IDWriteTextFormat> DefaultTextFormat(); [[nodiscard]] Microsoft::WRL::ComPtr<IDWriteTextFormat> DefaultTextFormat();
@ -70,19 +83,37 @@ namespace Microsoft::Console::Render
DWRITE_FONT_STYLE style, DWRITE_FONT_STYLE style,
DWRITE_FONT_STRETCH stretch); DWRITE_FONT_STRETCH stretch);
[[nodiscard]] HRESULT UpdateFont(const FontInfoDesired& desired, FontInfo& fiFontInfo, const int dpi) noexcept; [[nodiscard]] HRESULT UpdateFont(const FontInfoDesired& desired, FontInfo& fiFontInfo, const int dpi, const std::unordered_map<std::wstring_view, uint32_t>& features = {}, const std::unordered_map<std::wstring_view, float>& axes = {}) noexcept;
[[nodiscard]] static HRESULT STDMETHODCALLTYPE s_CalculateBoxEffect(IDWriteTextFormat* format, size_t widthPixels, IDWriteFontFace1* face, float fontScale, IBoxDrawingEffect** effect) noexcept; [[nodiscard]] static HRESULT STDMETHODCALLTYPE s_CalculateBoxEffect(IDWriteTextFormat* format, size_t widthPixels, IDWriteFontFace1* face, float fontScale, IBoxDrawingEffect** effect) noexcept;
bool DidUserSetFeatures() const noexcept;
std::vector<DWRITE_FONT_AXIS_VALUE> GetAxisVector(const DWRITE_FONT_WEIGHT fontWeight,
const DWRITE_FONT_STRETCH fontStretch,
const DWRITE_FONT_STYLE fontStyle,
IDWriteTextFormat3* format);
private: private:
using FontAttributeMapKey = uint32_t; using FontAttributeMapKey = uint32_t;
bool _didUserSetFeatures{ false };
// The font features to apply to the text
std::vector<DWRITE_FONT_FEATURE> _featureVector;
// The font axes to apply to the text
std::vector<DWRITE_FONT_AXIS_VALUE> _axesVector;
// We use this to identify font variants with different attributes. // We use this to identify font variants with different attributes.
static FontAttributeMapKey _ToMapKey(DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style, DWRITE_FONT_STRETCH stretch) noexcept static FontAttributeMapKey _ToMapKey(DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style, DWRITE_FONT_STRETCH stretch) noexcept
{ {
return (weight << 16) | (style << 8) | stretch; return (weight << 16) | (style << 8) | stretch;
}; };
void _SetFeatures(const std::unordered_map<std::wstring_view, uint32_t>& features);
void _SetAxes(const std::unordered_map<std::wstring_view, float>& axes);
float _FontStretchToWidthAxisValue(DWRITE_FONT_STRETCH fontStretch) 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);

View file

@ -1978,15 +1978,30 @@ CATCH_RETURN()
// Routine Description: // Routine Description:
// - Updates the font used for drawing // - Updates the font used for drawing
// - This is the version that complies with the IRenderEngine interface
// Arguments: // Arguments:
// - pfiFontInfoDesired - Information specifying the font that is requested // - pfiFontInfoDesired - Information specifying the font that is requested
// - fiFontInfo - Filled with the nearest font actually chosen for drawing // - fiFontInfo - Filled with the nearest font actually chosen for drawing
// Return Value: // Return Value:
// - S_OK or relevant DirectX error // - S_OK or relevant DirectX error
[[nodiscard]] HRESULT DxEngine::UpdateFont(const FontInfoDesired& pfiFontInfoDesired, FontInfo& fiFontInfo) noexcept [[nodiscard]] HRESULT DxEngine::UpdateFont(const FontInfoDesired& pfiFontInfoDesired, FontInfo& fiFontInfo) noexcept
{
return UpdateFont(pfiFontInfoDesired, fiFontInfo, {}, {});
}
// Routine Description:
// - Updates the font used for drawing
// Arguments:
// - pfiFontInfoDesired - Information specifying the font that is requested
// - fiFontInfo - Filled with the nearest font actually chosen for drawing
// - features - The map of font features to use
// - axes - The map of font axes to use
// Return Value:
// - S_OK or relevant DirectX error
[[nodiscard]] HRESULT DxEngine::UpdateFont(const FontInfoDesired& pfiFontInfoDesired, FontInfo& fiFontInfo, const std::unordered_map<std::wstring_view, uint32_t>& features, const std::unordered_map<std::wstring_view, float>& axes) noexcept
try try
{ {
RETURN_IF_FAILED(_fontRenderData->UpdateFont(pfiFontInfoDesired, fiFontInfo, _dpi)); RETURN_IF_FAILED(_fontRenderData->UpdateFont(pfiFontInfoDesired, fiFontInfo, _dpi, features, axes));
// Prepare the text layout. // Prepare the text layout.
_customLayout = WRL::Make<CustomTextLayout>(_fontRenderData.get()); _customLayout = WRL::Make<CustomTextLayout>(_fontRenderData.get());

View file

@ -109,6 +109,7 @@ namespace Microsoft::Console::Render
const gsl::not_null<IRenderData*> pData, const gsl::not_null<IRenderData*> pData,
const bool isSettingDefaultBrushes) noexcept override; const bool isSettingDefaultBrushes) noexcept override;
[[nodiscard]] HRESULT UpdateFont(const FontInfoDesired& fiFontInfoDesired, FontInfo& fiFontInfo) noexcept override; [[nodiscard]] HRESULT UpdateFont(const FontInfoDesired& fiFontInfoDesired, FontInfo& fiFontInfo) noexcept override;
[[nodiscard]] HRESULT UpdateFont(const FontInfoDesired& fiFontInfoDesired, FontInfo& fiFontInfo, const std::unordered_map<std::wstring_view, uint32_t>& features, const std::unordered_map<std::wstring_view, float>& axes) noexcept;
[[nodiscard]] HRESULT UpdateDpi(int const iDpi) noexcept override; [[nodiscard]] HRESULT UpdateDpi(int const iDpi) noexcept override;
[[nodiscard]] HRESULT UpdateViewport(const SMALL_RECT srNewViewport) noexcept override; [[nodiscard]] HRESULT UpdateViewport(const SMALL_RECT srNewViewport) noexcept override;