17

What is the best way to embed a truetype font within the application i'm developing? Basically i want to make sure a particular font is available to my application when installed on another machine. I have the *.ttf font file and just need a way of embedding it or automatically installing it when the application is run.

Do i need to set the installation program to install the font during installation or can i dynamically load the font during runtime of the application? In fact both would be nice to know.

The application is being developed in C# using .NET 2.0.

Gary Willoughby
  • 50,926
  • 41
  • 133
  • 199

4 Answers4

15

This blog post should help you.

Basically you add the font as an embedded resource then load it into a PrivateFontCollection object.

Shog9
  • 156,901
  • 35
  • 231
  • 235
dommer
  • 19,610
  • 14
  • 75
  • 137
  • I sometimes got exceptions thrown when I tried drawing some text using a font I created using this technique unless I kept a reference to the PrivateFontCollection around. I figured I had my font, why did I still need the collection? I'm assuming the font uses information from it and it got garbage collected or something. – Jon Turner Mar 13 '10 at 00:14
13

Here's Will's answer, translated to C# (untested):

PrivateFontCollection pfc = new PrivateFontCollection();

using (Stream fontStream = GetType().Assembly.GetManifestResourceStream("Alphd___.ttf"))
{
    if (null == fontStream)
    {
        return;
    }

    int fontStreamLength = (int) fontStream.Length;

    IntPtr data = Marshal.AllocCoTaskMem(fontStreamLength);

    byte[] fontData = new byte[fontStreamLength];
    fontStream.Read(fontData, 0, fontStreamLength);

    Marshal.Copy(fontData, 0, data, fontStreamLength);

    pfc.AddMemoryFont(data, fontStreamLength);

    Marshal.FreeCoTaskMem(data);
}

along with their Paint() method:

protected void Form1_Paint(object sender, PaintEventArgs e)
{
    bool bold = false;
    bool italic = false;

    e.Graphics.PageUnit = GraphicsUnit.Point;

    using (SolidBrush b = new SolidBrush(Color.Black))
    {
        int y = 5;

        foreach (FontFamily fontFamily in pfc.Families)
        {
            if (fontFamily.IsStyleAvailable(FontStyle.Regular))
            {
                using (Font font = new Font(fontFamily, 32, FontStyle.Regular))
                {
                    e.Graphics.DrawString(font.Name, font, b, 5, y, StringFormat.GenericTypographic);
                }
                y += 40;
            }
            if (fontFamily.IsStyleAvailable(FontStyle.Bold))
            {
                bold = true;
                using (Font font = new Font(fontFamily, 32, FontStyle.Bold))
                {
                    e.Graphics.DrawString(font.Name, font, b, 5, y, StringFormat.GenericTypographic);
                }
                y += 40;
            }
            if (fontFamily.IsStyleAvailable(FontStyle.Italic))
            {
                italic = true;
                using (Font font = new Font(fontFamily, 32, FontStyle.Italic))
                {
                    e.Graphics.DrawString(font.Name, font, b, 5, y, StringFormat.GenericTypographic);
                }
                y += 40;
            }

            if(bold && italic)
            {
                using(Font font = new Font(fontFamily, 32, FontStyle.Bold | FontStyle.Italic))
                {
                    e.Graphics.DrawString(font.Name, font, b, 5, y, StringFormat.GenericTypographic);
                }
                y += 40;
            }

            using (Font font = new Font(fontFamily, 32, FontStyle.Underline))
            {
                e.Graphics.DrawString(font.Name, font, b, 5, y, StringFormat.GenericTypographic);
                y += 40;
            }

            using (Font font = new Font(fontFamily, 32, FontStyle.Strikeout))
            {
                e.Graphics.DrawString(font.Name, font, b, 5, y, StringFormat.GenericTypographic);
            }
        }
    }
}
Chris Doggett
  • 19,959
  • 4
  • 61
  • 86
  • True, @coffee_machine. I'm sure it's covered by the fact that b is declared in a using block, and can be removed. – Chris Doggett Jan 17 '12 at 21:25
  • 1
    Actually you're disposing it inside the for loop. This will only work for the first iteration. – coffee_machine Jan 18 '12 at 12:28
  • Good catch. That's what I get for directly translating someone else's code. Thanks, @coffee_machine! – Chris Doggett Jan 18 '12 at 18:08
  • @ChrisDoggett, what is the difference between using Marshal.AllocHGlobal and Marshal.AllocCoTaskMem for allocating memory for font data. Just wondering if that has signifcant impact on my application performance and stability? – ajukraine Jan 16 '13 at 15:53
  • 1
    @ajukraine: This was basically a translation of someone else's now-deleted answer that was in VB. I'm not sure where I got the Marshal allocation stuff, so I can't answer you yet, but give me a little bit, and I'll see what I can dig up. – Chris Doggett Jan 16 '13 at 16:49
  • @ajukraine: Here's a good question/answer about which to use: http://stackoverflow.com/questions/1887288/marshal-allochglobal-vs-marshal-alloccotaskmem-marshal-sizeof-vs-sizeof Basically, AllocHGlobal allocates from the process heap, and the other allocates from the COM heap, so it depends if you're using COM interop, but both should work. – Chris Doggett Jan 16 '13 at 18:13
  • @ChrisDoggett, thank you for that) Honestly I've already read that SO question. In the case of Font memory management I use AllocHGlobal. – ajukraine Jan 16 '13 at 18:51
  • Where do you even put this? What file? The privatefontcollection link is like reading another f'n language. None of that makes sense and they repeat and repeat and never really give any f'n information besides a bunch of abstracted b.s. Why is it so fricken hard to do even the simplest thing in visual studio. So sick of this crap. I want my node.js back. – J-Roel Jun 08 '18 at 22:05
  • 1
    @J-Roel: Wish I could remember so I could tell you. Haven't done C# in almost 4 years at this point. – Chris Doggett Jun 09 '18 at 18:09
11

Its easier than this seems; you can embed the font as a resource in your app and access it as a strongly-typed property within the Properties namespace of your app. But the given link should be a good starting point.


For the VB-disabled:

Add the font as a resource in your application. Call the resource MyFontLol. You can access this resource (as a byte array) from Properties.Resources.MyFontLol.

I haven't tested the following, but it appears to be workable:

public void LoadMyFontLolKThx()
{
    // get our font and wrap it in a memory stream
    byte[] myFont = Properties.Resources.MyFontLol;
    using (var ms = new MemoryStream(myFont))
    {
        // used to store our font and make it available in our app
        PrivateFontCollection pfc = new PrivateFontCollection();
        // The next call requires a pointer to our memory font
        // I'm doing it this way; not sure what best practice is
        GCHandle handle = GCHandle.Alloc(ms, GCHandleType.Pinned);
        // If Length > int.MaxValue this will throw
        checked
        {
            pfc.AddMemoryFont(
                handle.AddrOfPinnedObject(), (int)ms.Length); 
        }
        var font = new Font(pfc.Families[0],12);

        // use your font here
    }
}

One last note. The PFC stores the font as a GDI+ font. These aren't compatible with some forms controls. From the docs:

To use the memory font, text on a control must be rendered with GDI+. Use the SetCompatibleTextRenderingDefault method, passing true, to set GDI+ rendering on the application, or on individual controls by setting the control's UseCompatibleTextRendering property to true. Some controls cannot be rendered with GDI+.

-1

it might not be the best way but couldn't you just include the font with your resources and then copy it to the font's folder on the windows dir?

Crash893
  • 11,428
  • 21
  • 88
  • 123