I have added a Google font to my application using the method
I have tried something similar to the below where you list the FamilyNames in the XAML, but you're unable to add the URI as the XAML value in this case without a syntax editor.
<FontFamily x:Key="Montserrat">
<FontFamily.FamilyNames>
<System:String x:Key="en-US">Montserrat</System:String>
</FontFamily.FamilyNames>
!!! syntax error
pack://application:,,,/Themes/General/Fonts/#Montserrat
</FontFamily>
So, what's the best way to get this font's friendly name to show in my combobox?
CodePudding user response:
You need to Iterate over the embedded fonts, then extract the name from the Typeface
object.
Here is a working demo project where I added all of the Montserrat
font variations (.tff files) top the projects' .\Resources
folder as Content
and copy if newer
. My .csproj
file looks like:
<ItemGroup>
<Content Include="Resources\Montserrat-Black.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Resources\Montserrat-BlackItalic.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Resources\Montserrat-Bold.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Resources\Montserrat-BoldItalic.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Resources\Montserrat-ExtraBold.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Resources\Montserrat-ExtraBoldItalic.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Resources\Montserrat-ExtraLight.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Resources\Montserrat-ExtraLightItalic.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Resources\Montserrat-Italic.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Resources\Montserrat-Light.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Resources\Montserrat-LightItalic.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Resources\Montserrat-Medium.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Resources\Montserrat-MediumItalic.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Resources\Montserrat-Regular.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Resources\Montserrat-SemiBold.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Resources\Montserrat-SemiBoldItalic.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Resources\Montserrat-Thin.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Resources\Montserrat-ThinItalic.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
Here is the code-behind:
public partial class MainWindow : Window, INotifyPropertyChanged
{
private string selectedFontName;
private string fontFamilyPath;
private static string uriPath= @"pack://application:,,,/";
private static string resPath = "./resources/";
public MainWindow()
{
InitializeComponent();
GetFontNames();
SetDefontFont();
}
public ObservableCollection<string> FontNames { get; } = new();
public string SelectedFontName
{
get => selectedFontName;
set
{
SetFontFamilyPath(value);
Set(ref selectedFontName, value);
}
}
public string FontFamilyPath
{
get => fontFamilyPath;
set => Set(ref fontFamilyPath, value);
}
private void GetFontNames()
{
foreach (Typeface typeface in Fonts.GetTypefaces(new Uri(uriPath), resPath))
{
string typefaceName = typeface.FontFamily.FamilyNames.First().Value;
foreach (KeyValuePair<XmlLanguage, string> weight in typeface.FaceNames)
{
// get type weight name
string typeWeightName = weight.Value;
// get type weight value
int? typeWeightValue = typeface
.FontFamily
.FamilyTypefaces
.ToList() // convert collection to List for working with Linq
.FirstOrDefault(x => x.Weight.ToString().Equals(weight.Value) &&
x.Weight.ToOpenTypeWeight() > 0)?
.Weight
.ToOpenTypeWeight();
FontNames.Add($"{typefaceName} {typeWeightName}{(typeWeightValue is null ? "" : $" ({typeWeightValue})")}");
}
}
}
private void SetDefontFont()
=> SetFontFamilyPath(FontNames.First());
private void SetFontFamilyPath(string name)
=> FontFamilyPath = $"./Resources/#{name}";
public event PropertyChangedEventHandler? PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
protected bool Set<T>(ref T field, T value, [CallerMemberName] string? propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
}
and here is the Window:
<Window x:Class="WpfEmbeddedGoogleTtfFont.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfEmbeddedGoogleTtfFont"
mc:Ignorable="d" x:Name="Window"
Title="MainWindow" Height="450" Width="800">
<Grid DataContext="{Binding ElementName=Window}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Text="The quick brown fox..."
FontFamily="{Binding FontFamilyPath}" FontSize="32" />
<ListBox Grid.Row="1"
ItemsSource="{Binding FontNames}"
SelectedItem="{Binding SelectedFontName}"/>
</Grid>
</Window>
The sample project will extract all of the embedded fonts with their weight names values and display in a list. You can then select the font and the sample text will use it.