addFontFile from Resources

I have added a custom font using below code:

PrivateFontCollection pfc = new PrivateFontCollection();
pfc.AddFontFile("C:\\Path To\\YourFont.ttf");
label1.Font = new System.Drawing.Font(pfc.Families[0], 16, FontStyle.Regular);

I added the font file in resources. How do I add with addFontFile from resources?


private static void AddFontFromResource(PrivateFontCollection privateFontCollection, string fontResourceName)
{
    var fontBytes = GetFontResourceBytes(typeof (App).Assembly, fontResourceName);
    var fontData = Marshal.AllocCoTaskMem(fontBytes.Length);
    Marshal.Copy(fontBytes, 0, fontData, fontBytes.Length);
    privateFontCollection.AddMemoryFont(fontData, fontBytes.Length);
    // Marshal.FreeCoTaskMem(fontData);  Nasty bug alert, read the comment
}

private static byte[] GetFontResourceBytes(Assembly assembly, string fontResourceName)
{
    var resourceStream = assembly.GetManifestResourceStream(fontResourceName);
    if (resourceStream == null)
        throw new Exception(string.Format("Unable to find font '{0}' in embedded resources.", fontResourceName));
    var fontBytes = new byte[resourceStream.Length];
    resourceStream.Read(fontBytes, 0, (int)resourceStream.Length);
    resourceStream.Close();
    return fontBytes;
}

If you included your font in the resources

Try this function

private void AddFontFromMemory()
{
    Stream fontStream = this.GetType().Assembly.GetManifestResourceStream("yourfont.ttf");
 
    byte[] fontdata = new byte[fontStream.Length];
    fontStream.Read(fontdata,0,(int)fontStream.Length);
    fontStream.Close();

    unsafe
    {
        fixed(byte * pFontData = fontdata)
        {
            pfc.AddMemoryFont((System.IntPtr)pFontData,fontdata.Length);
        }
    }
}

Edited

How load resource from assembly:(YourNamespace.file.ttf)

Stream fontStream = Assembly.GetExecutingAssembly()
 .GetManifestResourceStream("WindowsFormsApplication1.SBADR.TTF");

My solution explorer:

enter image description here


This is the way I do it.

First get your Font.ttf file and using Visual Studio, drag and drop the file to the root folder or resource folder.

In Solution Explorer, right-click the file and click properties. Select Build Action = Content. This will show the file in the Application Files under Project Properties > Publish > Application Files. You will see that the file now can be selected (By default it's automatically included).

ClickOnce will now copy the file to the StartupPath

To use it, follow this sample:

PrivateFontCollection pfc = new PrivateFontCollection();

pfc.AddFontFile(Path.Combine(Application.StartupPath, "font_name.ttf"));

textBox1.Font = new Font(pfc.Families[0], 18, FontStyle.Regular);

A word of warning: If you value your sanity and it's possible to avoid this by loading this font from a file then do not struggle with packaging them as resources. I am telling you now: it isn't worth it.1

The answer

[DllImport("gdi32.dll")]
private static extern IntPtr AddFontMemResourceEx(IntPtr pbFont, uint cbFont, IntPtr pdv, [In] ref uint pcFonts);
private static PrivateFontCollection pfc = new PrivateFontCollection();
private static uint cFonts = 0;
private static void AddFont(byte[] fontdata)
{ 
    int fontLength;  System.IntPtr dataPointer;

    //We are going to need a pointer to the font data, so we are generating it here
    dataPointer = Marshal.AllocCoTaskMem(fontdata.Length);
            

    //Copying the fontdata into the memory designated by the pointer
    Marshal.Copy(fontdata, 0, dataPointer, (int)fontdata.Length);

    // Register the font with the system.
    AddFontMemResourceEx(dataPointer, (uint)fontdata.Length, IntPtr.Zero, ref cFonts);

    //Keep track of how many fonts we've added.
    cFonts += 1;

    //Finally, we can actually add the font to our collection
    pfc.AddMemoryFont(dataPointer, (int)fontdata.Length);
}

Okay, there is a lot to unpack here, but for those looking for a copy/paste, you're going to have a bad time without the line of code that follows. It must be run before your first form is created and before your first font is loaded:2

Application.SetCompatibleTextRenderingDefault(true);

And you can use this code by simply calling the above function like so:

AddFont(Properties.Resources.Roboto_Light);

Now, if you're having trouble with this, what you need to deal with is the AddFontMemResourceEx function, because it's really difficult to use correctly. Basically what this function does is keep track of fonts in memory. It seems that pfc.AddMemoryFont doesn't actually increment cFonts which causes it to silently fail to properly load every font after the first one. This counter must be increased by one every time a unique font family is added. Italic and Bold variants of font families that have already been added don't count as new families and the cFonts should therefore not be incremented for these.

In theory, the return value for AddFontMemResourceEx is a pointer referencing the location of the number of fonts currently stored in memory. It also seems impossible to get at the actual number, which is why I'm keeping track manually by incrementing cFonts. If you fail to properly increment cFonts then this method will silently fail. The only way to tell that it failed is to see that the font wasn't properly loaded.

Summary

This answer is quite lengthy. It's the only thing I found that works, but the fact that you need to jump through this many hoops to add a font from a resource is absurd. I don't understand why the AddFromMemory function is so broken and I feel confident I must be doing something wrong or misreading something, but this is it. This was a lot of work, and even now it's not the most stable solution when it comes to bold and italic variations of fonts. If you can explain what is going on here and why this is so weird, I would love to hear it. For now, this is the only way I know.

Footnotes

1: Also, it will break the WindowsFormsDesigner. There is no way to set compatibleTextRenderingDefault to true in the WindowsFormsDesigner environment, so you will constantly get errors and text won't render properly. I solved this by putting the font loading in a try{}catch{} and then making the catch just trying to load from a hardcoded path on my own hard drive. Since loading from a file doesn't require any of this mess, that works like a charm.

2: This is absolutely ridiculous. In theory, SetCompatibleTextRenderingDefault() sets the default text rendering technique used by controls. It determines whether they should use a legacy text rendering technique or a newer text rendering technique. Since (at least in my case) fonts are being loaded before even the first control has been generated, I have absolutely no idea why this would affect anything. And I know it's not the fonts being legacy fonts or anything because if I load them from a file (which presumably contains the same data) this setting doesn't matter. It makes no sense whatsoever.