Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stop WPF using Century in place of Century Gothic

Tags:

wpf

fonts

Our design prefers to use Century Gothic if it's installed, but falls back to Arial or Lucidia if it's not available. This works fine for the most part, unless the machine doesn't have Century Gothic installed, but does have Century (which appears to be a lot of XP machines without Office). "Helpfully" it substitutes Century, which is horrible and serif-ey, for Century Gothic, and completely ignores our fallback fonts.

It's easily reproducible by having a machine with Century and not Century Gothic, then create a TextBlock such as:

<TextBlock FontFamily="Century Gothic, Wingdings">Should be gibberish</TextBlock>

It should show as gibberish Wingdings, but instead it shows in ugly but readable Century.

I've tried wrapping Century Gothic up in a compositefont, but that has the same issue:

<FontFamily
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/composite-font"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:System="clr-namespace:System;assembly=mscorlib">
  <FontFamily.FamilyNames>
    <System:String x:Key="en-US">CenturyGothic</System:String>
  </FontFamily.FamilyNames>
  <FontFamily.FamilyTypefaces>
    <FamilyTypeface Weight="Normal" Stretch="Normal" Style="Normal" />
    <FamilyTypeface Weight="Bold" Stretch="Normal" Style="Normal" />
  </FontFamily.FamilyTypefaces>
  <FontFamily.FamilyMaps>
    <FontFamilyMap Target="Century Gothic" Scale="1" />
  </FontFamily.FamilyMaps>
</FontFamily>

Embedding the font isn't an option (licensing) - is there any way to force it to ignore Century, or am I resigned to changing it to use a different font?

Edit: I've just tried using FontFace="'Century Gothic',Wingdings" but that never displays Century Gothic, even if it's installed.

like image 596
Steven Robbins Avatar asked Jul 28 '11 09:07

Steven Robbins


3 Answers

You can do this using WPF Styles and Merged Resource Dictionaries.

Create two resource dictionaries, one for your default fonts, and one for your fallback fonts:

DefaultFonts.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Style TargetType="TextBlock">
        <Setter Property="FontFamily" Value="Century Gothic"/>
    </Style>
</ResourceDictionary>

FallbackFonts.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Style TargetType="TextBlock">
        <Setter Property="FontFamily" Value="Arial"/>
    </Style>
</ResourceDictionary>

Add the two files to your project and set their Build Action to Resource.

Add the following code to your app startup:

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {            
        base.OnStartup(e);

        string fontsResourcePath = "/DefaultFonts.xaml";
        if (!Fonts.SystemFontFamilies.Any(f => f.FamilyNames.Any(kv => kv.Value == "Century Gothic")))
        {
            fontsResourcePath = "/FallbackFonts.xaml";
        }
        this.Resources.MergedDictionaries.Add(new ResourceDictionary() { Source = new Uri(fontsResourcePath, UriKind.Relative) });
    }
}

Doing it this way will automatically set the correct font on all controls throughout your application (unless you override the font locally on the window, page, or control).

Example:

<Grid>
    <TextBlock>This will show as Century Gothic or fall back to Arial</TextBlock>
</Grid>

Happy coding!

like image 140
Harlow Burgess Avatar answered Oct 03 '22 11:10

Harlow Burgess


A fully qualified path to the font might do the trick:

<TextBlock Margin="20" FontFamily="c:\windows\fonts\GOTHIC.TTF#Century Gothic, Wingdings">Should be gibberish</TextBlock>

Hard coding a path isn't ideal, but you could add a fallback that used the font's friendly name in case the user is running via a non-standard install path. You could also use code to lookup the font directory in the registry and use that.

Another option might be to use code to iterate over the installed fonts and pick the one you want that way. System.Windows.Media.Fonts.SystemFontFamilies has the installed fonts. Once you locate the font you want, you could assign that font family object to the control, rather than asking it to parse a string.

like image 39
NathanAW Avatar answered Oct 03 '22 10:10

NathanAW


The problem seems to be that WPF searches for fonts by Family, not Face. In your scenario, Century and Century Gothic are all part of the Century family of fonts. Thus, you need to map all of the Faces to the one you want. Please verify that this works for you:

<Label Content="Hello World" FontSize="32">
    <Label.FontFamily>
        <FontFamily xmlns:sys="clr-namespace:System;assembly=mscorlib">
            <FontFamily.FamilyNames>
                <sys:String x:Key="en-US">Century, Century Gothic, Century Schoolbook</sys:String>
            </FontFamily.FamilyNames>
            <FontFamily.FamilyMaps>
                <FontFamilyMap Target="Century Gothic" />

                <!-- used when Century Gothic is not available -->
                <FontFamilyMap Target="Arial" />
            </FontFamily.FamilyMaps>
        </FontFamily>
    </Label.FontFamily>
</Label>
like image 22
sellmeadog Avatar answered Oct 03 '22 12:10

sellmeadog