Private fonts: for members only

by Michael S. Kaplan, published on 2005/11/20 04:01 -05:00, original URI: http://blogs.msdn.com/b/michkap/archive/2005/11/20/494829.aspx


About a month ago, I had someone ask me about the images that appeared in MSKLC for characters that had no visible representation, wondering whether they were part of a font or not. After I explained that they were, she asked me how this font was able to be used without it being on the machine (the font itself was not all that interesting to her, but the capability to do that with any font was).

Then this last week, Rick Engle was asking on an internal Microsoft distribution list whether a font could be included as a project resource.

So today I thought I'd take these two items and handle them both at once, since they are really the same item.

The answer is that yes you can do this, and it works quite well using:

The line between managed and unmanaged code can blur when you use managed code like WinForms, since some of the controls are unmanaged underneath (like the RichTextBox) and require you to use both techniques at the same time to help things work properly. There are several controls that fit into this category, so I generally find it better to be safe than sorry and just always include them both.

And it is especially important to use both techniques in Whidbey (VS 2005) since GDI/Uniscribe is used in many cases rather than GDI+, as I have discussed previously.

Also, be sure that you are following the licensing rules and restrictions for any font you wish to include!

Basically, start by adding the font file to your project, right-clicking on the file in the Project Explorer, and choosing 'Embedded Resource.' The name you will load is the font file with a fully qualified name (project namespace included), e.g. WindowsApplication1.MyPrivateFont.ttf.

Here are in namespaces we will need:

using System.Reflection;
using System.Drawing;
using System.Drawing.Text;
using System.IO;
using System.Runtime.InteropServices;

And here is the basic code:

// Adding a private font (Win2000 and later)
[DllImport("gdi32.dll", ExactSpelling=true)]
private static extern IntPtr AddFontMemResourceEx(byte[] pbFont, int cbFont, IntPtr pdv, out uint pcFonts);

// Cleanup of a private font (Win2000 and later)
[DllImport("gdi32.dll", ExactSpelling=true)]
internal static extern bool RemoveFontMemResourceEx(IntPtr fh);

// Some private holders of font information we are loading
static private IntPtr m_fh = IntPtr.Zero;
static private PrivateFontCollection m_pfc = null;

/////////////////////////////////////
//
// The GetSpecialFont procedure takes a size and
// create a font of that size using the hardcoded
// special font name it knows about.
//
/////////////////////////////////////
public Font GetSpecialFont(float size) {

   Font fnt = null;

   if(null == m_pfc) {

      // First load the font as a memory stream
      Stream stmFont = Assembly.GetExecutingAssembly().GetManifestResourceStream(
                              "WindowsApplication1.MyPrivateFont.ttf");

      if(null != stmFont) {

         // 
         // GDI+ wants a pointer to memory, GDI wants the memory.
         // We will make them both happy.
         //

         // First read the font into a buffer
         byte[] rgbyt = new Byte[stmFont.Length];
         stmFont.Read(rgbyt, 0, rgbyt.Length);

         // Then do the unmanaged font (Windows 2000 and later)
         // The reason this works is that GDI+ will create a font object for
         // controls like the RichTextBox and this call will make sure that GDI
         // recognizes the font name, later.

         uint cFonts;
         AddFontMemResourceEx(rgbyt, rgbyt.Length, IntPtr.Zero, ref cFonts);

         // Now do the managed font
         IntPtr pbyt = Marshal.AllocCoTaskMem(rgbyt.Length);
         if(null != pbyt) {
            Marshal.Copy(rgbyt, 0, pbyt, rgbyt.Length);
            m_pfc = new PrivateFontCollection();
            m_pfc.AddMemoryFont(pbyt, rgbyt.Length);
            Marshal.FreeCoTaskMem(pbyt);
         }
      }
   }

   if(m_pfc.Families.Length > 0) {
      // Handy how one of the Font constructors takes a
      // FontFamily object, huh? :-)

      fnt = new Font(m_pfc.Families[0], size);
   }

   return fnt;
}

Now this code is assuming you are only ever adding one font but plan to ask for different sizes of it; if you need to expand it to use multiple fonts then you will obviously want to take that PrivateFontCollection object and that IntPtr for the unmanaged font and convert them to be a collection or something to hold more than one font, perhaps using a Hashtable that index by the name?

Although I was tempted to make this procedure more generic, the requirement for the font to be an embedded resource and the fact that I am technically on vacation made this seem a little less critical for a quick sample. People who are interested can obviously do what they need to here, of course!

For cleanup, you will eventually want to dispose of your PrivateFontCollection and call RemoveFontMemResource, though for the latter the documentation claims that when the process goes away, the system will unload the fonts even if the process did not call RemoveFontMemResource (so you can base whether to call that on how clean you want your code to be!).

Those are the basics and should be enough to get you started....

As I think about the way that private fonts are implemented in both GDI and GDI+, a possible model for a future version of a "private CultureInfo" seems to emerge.

It is obvious that if you are creating a CultureInfo that can be enumerated and used by everyone on the machine that you should have administrative permissions. But perhaps this model could be used to create a more private CultureInfo for use in a particular application. Just as with the private fonts you could not enumerate them or use them systemwide, but there already is a mechanism to get those things done. Is it something you could use in an application yourself.

Seems like an interesting idea, right?

 

This post brought to you by "" (U+2030, a.k.a. PER MILLE SIGN)
(A character that was quite impressed to see that I managed to add something 'international' at the end of the post!)


# Andreas Magnusson on 21 Nov 2005 3:28 AM:

Cool, I've been wondering if it was possible to do this or if I would have to A) force an install of my desired font or B) let the system choose a "similar" font... but this is great!

# mg on 15 Jan 2006 11:16 PM:

Ohhh thanks so much. I was wrestling with PrivateFontCollection and nothing was working right or even rationally (managed directx font class). By using AddFontMemResourceEx(), everything began to work

RF on 13 Dec 2011 12:06 PM:

Replace 'ref' with 'out' in AddFontMemResourceEx(rgbyt, rgbyt.Length, IntPtr.Zero, ref cFonts); if you have compile issues. Great article and it's good to find those that help online!


referenced by

2015/04/23 Maybe they should *write* the freaking manual!

2008/09/08 On installing and removing fonts, Part 8: Sometimes being selfish makes you more trustworthy

2008/02/28 Font size scaling -- GDI vs. GDI+

2007/06/19 Why is the NULL GLYPH not the glyph representing NULL?

2007/05/17 A private CultureAndRegionInfoBuilder does not imply a private CultureInfo

2007/04/14 Rhymes with Amharic #3 (a.k.a. Read and write a language w/o even getting out of my [em]bed? Kewl!)

2007/04/14 Rhymes with Amharic (a.k.a. How about a little breakfast embed, dear?)

2006/12/30 PM agrees -- 'supporting Aero' does not mean we have to start flying (or get high by any other means)

2006/03/18 What about logical fonts?

go to newer or older post, or back to index or month or day