5

I enumerate the list of fontfamilies and bind to combobox, the problem is when there is a font in the system that is corrupted. The whole application will crashes. Any way i am able to bind to systemfontfamilies yet able to skip font that has error displaying?

THe following code runs fine if the fontfamily binding in the itemtemplate is commented.

 <ComboBox x:Name="comboFonts"
                          Grid.IsSharedSizeScope="True"
                          Grid.Row="0" Grid.Column="1"
                          ItemsSource="{Binding Source={x:Static Member=Fonts.SystemFontFamilies}}"
                          SelectedItem="{Binding FontFamily, Mode=TwoWay}"
                          HorizontalAlignment="Stretch">
        <ComboBox.ItemTemplate>
            <DataTemplate>
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto" SharedSizeGroup="FontName"></ColumnDefinition>
                        <ColumnDefinition Width="*"></ColumnDefinition>
                    </Grid.ColumnDefinitions>
                    <TextBlock Text="{Binding Source}" HorizontalAlignment="Left"/>
                    <Label FontFamily="{Binding FallbackValue=Verdana}" HorizontalAlignment="Right">Sample</Label>
                </Grid>

            </DataTemplate>
        </ComboBox.ItemTemplate>
    </ComboBox>

THe error message that get is as following

Message=Input file or data stream does not conform to the expected file format specification.
Source=PresentationCore
StackTrace:
   at MS.Internal.Text.TextInterface.Native.Util.ConvertHresultToException(Int32 hr)
   at MS.Internal.Text.TextInterface.Font.CreateFontFace()
   at MS.Internal.Text.TextInterface.Font.AddFontFaceToCache()
   at MS.Internal.Text.TextInterface.Font.GetFontFace()

Please help. THanks

Nathan Swannet
  • 220
  • 3
  • 12
sunny
  • 61
  • 1
  • 6

2 Answers2

5

I had the same problem. In a richtextbox editor, I fill a ribbon comobox with all the available font families and attach that font to that specific item in the combobox so that a user immediately sees how the font looks like.

When there was a font on the system which can't be rendered by WPF, the application would crash.

Looking at the stacktrace in the event viewer, I noticed that WPF tries to instantiate an object of the type System.Windows.Media.GlyphTypeface. I found out that, when I try to instantiate that object myself in code (via the System.Windows.Media.Typeface type) and the TryGetGlyphTypeface() function would return false for my specific font settings, that font is not usable in WPF.

The code which solved the problem for me:

    foreach (FontFamily aFontFamily in Fonts.SystemFontFamilies)
    {
        // Instantiate a TypeFace object with the font settings you want to use
        Typeface ltypFace = new Typeface(aFontFamily, FontStyles.Normal, FontWeights.Normal, FontStretches.Normal);
        // Try to create a GlyphTypeface object from the TypeFace object
        GlyphTypeface lglyphTypeFace;
        if (ltypFace.TryGetGlyphTypeface(out lglyphTypeFace))
        {
            // Creation of the GlyphTypeface worked. You can use the font
            RibbonGalleryItem lribItem = new RibbonGalleryItem();
            lribItem.Content = aFontFamily.Source;
            lribItem.FontFamily = aFontFamily;
            lribGalCatFont.Items.Add(lribItem);
        }
    }

To prevent that this code must be executed everytime I load the combobox (and that's quite a lot because it's in a user control), I do this one time at the start of the application and save the array of usable fonts in a global variable.

Nathan Swannet
  • 220
  • 3
  • 12
  • Note, however, that it is _normal_ for `TryGetGlyphTypeface()` to return `null` for valid font families, i.e. composite fonts. Just because it returns `null`, that doesn't necessarily mean the font file is corrupt (of course, if you are relying on the `GlyphTypeface` itself, you'll have to do more work to get it, by decomposing the composite font...but that's not part of the issue here). – Peter Duniho Apr 11 '16 at 07:32
0

Okay, 5 years later, here's another solution:

Declare this code at the Window.Resource:

<CollectionViewSource x:Key="MyFonts" Source="{Binding Source={x:Static Fonts.SystemFontFamilies}, Converter={StaticResource FontToSupportedGliphConverter}}">
    <CollectionViewSource.SortDescriptions>
        <componentModel:SortDescription PropertyName="Source" />
    </CollectionViewSource.SortDescriptions>
</CollectionViewSource>

<Style x:Key="FontStyle">
    <Setter Property="Control.FontFamily" Value="{Binding .}" />
    <Setter Property="Control.FontSize" Value="16" />
</Style>

<DataTemplate x:Key="FontTemplate">
    <VirtualizingStackPanel IsVirtualizing="True" VirtualizationMode="Recycling" ScrollViewer.IsDeferredScrollingEnabled="True">
        <TextBlock Style="{StaticResource FontStyle}" Text="{Binding .}" ToolTip="{Binding .}" />
    </VirtualizingStackPanel>
</DataTemplate>

And use this Converter:

public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
    var list = value as IReadOnlyCollection<FontFamily>;

    if (list == null)
        return DependencyProperty.UnsetValue;

    var returnList = new List<FontFamily>();
    foreach (FontFamily font in list)
    {
        //Instantiate a TypeFace object with the font settings you want to use
        Typeface ltypFace = new Typeface(font, FontStyles.Normal, FontWeights.Normal, FontStretches.Normal);

        //Try to create a GlyphTypeface object from the TypeFace object
        GlyphTypeface lglyphTypeFace;
        if (ltypFace.TryGetGlyphTypeface(out lglyphTypeFace))
        {
            returnList.Add(font);
        }
    }

    return returnList;
}

And apply the resources to the ComboBox:

<ComboBox x:Name="FreeTextFontComboBox" Margin="10,5" 
          MinWidth="100" MaxWidth="110" IsEditable="True"
          ItemTemplate="{DynamicResource FontTemplate}" 
          SelectedItem="{Binding Source={x:Static prop:Settings.Default}, 
          Path=FreeTextFontFamily}">
    <ComboBox.ItemsSource>
        <Binding Source="{StaticResource MyFonts}" />
    </ComboBox.ItemsSource>
</ComboBox>
Nicke Manarin
  • 3,026
  • 4
  • 37
  • 79
  • See note in the other answer. This approach will skip valid composite fonts, in addition to fonts with corrupt font files. – Peter Duniho Apr 11 '16 at 07:33
  • And what can we do to avoid missing some fonts? Wrapping inside the `try catch` with the `returnList.Add(font);` at the end? – Nicke Manarin Apr 11 '16 at 12:30
  • As I wrote in my other comment, when you come across a composite font, you need to do more work to get a real font. I.e. examine its `FamilyMaps` to find an appropriate `FontFamily` that supports the Unicode code points you're trying to render. And yes, as for invalid/corrupt fonts, handling exceptions can address that. Though, IMHO if you're going to do that, you should at least inform the user _which_ font caused the exception, so they can fix their system. – Peter Duniho Apr 11 '16 at 14:57