24
votes

I have a MemoryStream with the contents of a Font File (.ttf) and I would like to be able to create a FontFamily WPF object from that stream WITHOUT writing the contents of the stream to disk. I know this is possible with a System.Drawing.FontFamily but I cannot find out how to do it with System.Windows.Media.FontFamily.

Note: I will only have the stream, so I can't pack it as a resource in the application and because of disk permissions issues, will not be able to write the font file to disk for reference as "content"

UPDATE:

The API docs how describe how an application resource can be used, though it is not clear to me whether that is an Embedded resource in the assembly or a file on disk.

You can use a base URI value when you reference a font that is packaged as part of the application. For example, the base URI value can be a "pack://application" URI, which lets you reference fonts that are packaged as application resources. The following code example shows a font reference that is composed of a base URI value and a relative URI value.

2
Not sure you can do that Bernard :-) DirectWrite (which is the underlying tech used) does support it but WPF (which has not been updated much since 15 years) pretty much hardcodes the fact it loads the fonts from Windows or a folder/file. Follow the source: referencesource.microsoft.com/#PresentationCore/Core/CSharp/…Simon Mourier
Thanks @SimonMourier: I added info about the pack:// URI format. If that uses embedded resources, would there be a way to extend the file loading to support a stream? I tried going down the rabbit whole but couldn't find a clear answer in referencesource.microsoft.com/#WindowsBase/Base/System/IO/….Bernard Vander Beken
I tried this stackoverflow.com/a/16459015/403671 but it doesn't work, never triggered. The various Uri parameters here are only supported with pack or file:// protocol.Simon Mourier
If you don't mind me asking, where is the MemoryStream coming from? Maybe there's another approach?Keith Stein

2 Answers

1
votes

The best approach I could think of, was to save the oldFont to a temp directory, and immediately load it using the newFont constructor that accepts a uri.

1
votes

There is a similar question here, which contains a supposed solution by converting a System.Drawing.FontFamily to a WPF font family, all in memory without any file IO:

public static void Load(MemoryStream stream)
{
    byte[] streamData = new byte[stream.Length];
    stream.Read(streamData, 0, streamData.Length);
    IntPtr data = Marshal.AllocCoTaskMem(streamData.Length); // Very important.
    Marshal.Copy(streamData, 0, data, streamData.Length);
    PrivateFontCollection pfc = new PrivateFontCollection();
    pfc.AddMemoryFont(data, streamData.Length);
    MemoryFonts.Add(pfc); // Your own collection of fonts here.
    Marshal.FreeCoTaskMem(data); // Very important.
}

public static System.Windows.Media.FontFamily LoadFont(int fontId)
{
    if (!Exists(fontId))
    {
        return null;
    }
    /*
    NOTE:
    This is basically how you convert a System.Drawing.FontFamily to System.Windows.Media.FontFamily, using PrivateFontCollection.
    */
    return new System.Windows.Media.FontFamily(MemoryFonts[fontId].Families[0].Name);
}

This seems to use the System.Drawing.PrivateFontCollection(^) to add a System.Drawing.Font created from a MemoryStream and then use the Families[0].Name of that font to pass into the System.Windows.Media.FontFamily constructor. I assume the family name would then be a URI to the instance of that font in the PrivateFontCollection but you'd probably have to try it out.