Addfontfile from Resources

addFontFile from Resources

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:

Sample Image

Using Resource File with PrivateFontCollection

You can't do this with the AddFontFile() method; the path string it expects cannot resolve into the resources embedded in your compiled program.

Instead, you will have to use AddMemoryFont()...and pass it a pointer to resource data you've fetched via resource-aware APIs.

There's a question from 2013 with someone doing this in C#: "addFontFile from resources". I don't know what other class libraries you are using, but if you were programming to straight Win32, getting a pointer and size for your font would look something like this:

HMODULE module = NULL; // search current process, override if needed
HRSRC resource = FindResource(module, L"Exo-Regular.ttf", RT_RCDATA);
if (!resource) {...error handling... }
HGLOBAL handle = LoadResource(module, resource);
if (!handle) {...error handling... }

// "It is not necessary to unlock resources because the system
// automatically deletes them when the process that created
// them terminates."
//
void *memory = LockResource(handle);
DWORD length = SizeofResource(module, resource);

privateFontCollection.AddMemoryFont(memory, length);

Issue Creating PrivateFontCollection from Resources

Turns out the issue was a ttf file that went corrupt when it was added to source control. As to the discussion above, deallocating the memory via FreeCoTaskMem() works fine (as it always did previously) because it's only needed temporarily to get the bytes to memory so that AddFontFromMem() can be utilized - which is the easiest way to get embedded data into the pfc. Removing the errant font resolved the issue entirely.

How to render a font from privatefontcollection memory to editable controls

After a lot of digging around, and coming up empty on this (including the drive-by downvote on this question which still puzzles me), I have found a solution to this problem which it seems many have faced and have also come up empty-handed resorting to just installing the font as a file on the users system. As I thought, this is not required at all and you CAN embed the font in a simple way which allows it to be used on any control including TextBox, ComboBox, etc. Whatever your heart desires.

I will write this as a step by-step guide on how to embed a font into your project AND have it display properly on any control you desire.

Step 1 - Choosing your victim (the font choice)

For demonstration purposes (and popular demand), I will be using the FontAwesome font (v 4.1 at the time of writing this)

  • Inside your project, go to Project > <your project name> Properties
  • Click Resources (should be on the left side)
  • Click the dropdown arrow to the right of the Add Resource button, and select Add Existing File.

Sample Image

  • Make sure the All Files (*.*) is selected from the drop down and then browse to where the font file is your wish to embed, select it, and click the [Open] button.
    Sample Image

You should now see something like the following :
Sample Image

  • Press [CTRL]+[S] to save the project.

Step 2 - Diving into the code

Copy the following code and save it as 'MemoryFonts.cs' then add it to your project

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

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

public static class MemoryFonts {

[DllImport( "gdi32.dll" )]
private static extern IntPtr AddFontMemResourceEx( IntPtr pbFont, uint cbFont, IntPtr pdv, [In] ref uint pcFonts );
private static PrivateFontCollection pfc { get; set; }

static MemoryFonts() {
if ( pfc==null ) { pfc=new PrivateFontCollection(); }
}

public static void AddMemoryFont(byte[] fontResource) {
IntPtr p;
uint c = 0;

p=Marshal.AllocCoTaskMem( fontResource.Length );
Marshal.Copy( fontResource, 0, p, fontResource.Length );
AddFontMemResourceEx( p, (uint)fontResource.Length, IntPtr.Zero, ref c );
pfc.AddMemoryFont( p, fontResource.Length );
Marshal.FreeCoTaskMem( p );

p = IntPtr.Zero;
}

public static Font GetFont( int fontIndex, float fontSize = 20, FontStyle fontStyle = FontStyle.Regular ) {
return new Font(pfc.Families[fontIndex], fontSize, fontStyle);
}

// Useful method for passing a 4 digit hex string to return the unicode character
// Some fonts like FontAwesome require this conversion in order to access the characters
public static string UnicodeToChar( string hex ) {
int code=int.Parse( hex, System.Globalization.NumberStyles.HexNumber );
string unicodeString=char.ConvertFromUtf32( code );
return unicodeString;
}

}

Step 3 - Making it pretty (using the font)

  • On the main form, add a TextBox control from your toolbox
    Sample Image

  • Inside the form_Load event, put the following code (in my case the resource is fontawesome_webfont. change that to whatever your font resource is named)

    private void Form1_Load( object sender, EventArgs e ) {
    MemoryFonts.AddMemoryFont( Properties.Resources.fontawesome_webfont );
    textBox1.Font = MemoryFonts.GetFont(
    // using 0 since this is the first font in the collection
    0,

    // this is the size of the font
    20,

    // the font style if any. Bold / Italic / etc
    FontStyle.Regular
    );

    // since I am using FontAwesome, I would like to display one of the icons
    // the icon I chose is the Automobile (fa-automobile). Looking up the unicode
    // value using the cheat sheet https://fortawesome.github.io/Font-Awesome/cheatsheet/
    // shows : fa-automobile (alias) []
    // so I pass 'f1b9' to my UnicodeToChar method which returns the Automobile icon
    textBox1.Text = MemoryFonts.UnicodeToChar( "f1b9" );
    }

The end result

Sample Image


You may or may not have noticed that I made this method keeping in mind that you may wish to add multiple embedded fonts. In which case, you just call AddMemoryFont() for each of the fonts you want to add, and then use the appropriate index value (zero based) when using GetFont()

Contrary to the document regarding PrivateFontCollection.AddMemoryFont()'s documentation from Microsoft, you DO NOT NEED to use UseCompatibleTextRendering or SetCompatibleTextRenderingDefault at all. The method I outlined above allows for natural rendering of the font in objects/controls.

Embedded resource font in C# does not work correctly

Well, then... I think I got it!

I'll explain what I've "discovered" (whether it can be obvious or not):

  • First: Application.SetCompatibleTextRenderingDefault must be set to true for Memory fonts to be rendered in the controls.
    (Also Control.UseCompatibleTextRendering can be used)
    It's perfectly specified in Microsoft documentation but I've missed that :-(

  • Second: PrivateFontCollection.Families return an array of added fonts, but.. Surprise! It's alphabetically ordered!
    No matter what's the order you add the fonts or the method you use (AddMemoryFont/AddFontFile), you'll get it alphabetically ordered!
    So if you're adding more than one font and then trying to get the last font you've added, you'll probably getting the wrong one.

  • Third: I've also tried doing FreeCoTaskMem() after adding the font in the collection or doing it on form closing. Both were working for me!
    I don't know the exact implications of this...

This is my final code:

    //This list is used to properly dispose PrivateFontCollection after usage
static private List<PrivateFontCollection> _fontCollections;

[STAThread]
private static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(true); //Mandatory in order to have Memory fonts rendered in the controls.

//Dispose all used PrivateFontCollections when exiting
Application.ApplicationExit += delegate {
if (_fontCollections != null) {
foreach (var fc in _fontCollections) if (fc != null) fc.Dispose();
_fontCollections = null;
}
};

Application.Run(new frmMain());
}

void frmMain_Load(object sender, EventArgs e)
{
Font font1 = GetCustomFont(Properties.Resources.Amatic_Bold, 25, FontStyle.Bold);
//or...
Font font1 = GetCustomFont("Amatic-Bold.ttf", 25, FontStyle.Bold);

labelTestFont1.Font = font1;

Font font2 = GetCustomFont(Properties.Resources.<font_resource>, 25, FontStyle.Bold);
//or...
Font font2 = GetCustomFont("<font_filename>", 25, FontStyle.Bold);

labelTestFont2.Font = font2;

//...

}
static public Font GetCustomFont (byte[] fontData, float size, FontStyle style)
{
if (_fontCollections == null) _fontCollections = new List<PrivateFontCollection>();
PrivateFontCollection fontCol = new PrivateFontCollection();
IntPtr fontPtr = Marshal.AllocCoTaskMem(fontData.Length);
Marshal.Copy(fontData, 0, fontPtr, fontData.Length);
fontCol.AddMemoryFont(fontPtr, fontData.Length);
Marshal.FreeCoTaskMem(fontPtr); //<-- It works!
_fontCollections.Add (fontCol);
return new Font(fontCol.Families[0], size, style);
}

static public Font GetCustomFont (string fontFile, float size, FontStyle style)
{
if (_fontCollections == null) _fontCollections = new List<PrivateFontCollection>();
PrivateFontCollection fontCol = new PrivateFontCollection();
fontCol.AddFontFile (fontFile);
_fontCollections.Add (fontCol);
return new Font(fontCol.Families[0], size, style);
}

As you can see, I've decided to create an exclusive PrivateFontCollection for each font, then store it to a List for a final disposal on application end.

This was tested in 3 different PC's (both with Windows 7, 32 and 64 bits) and 3 different .ttf fonts.

An example of the result:

Sample Image

I don't know if my approach is good enough, but I expect it could be useful for others!

One more detail:
Unlike what I expected, AddMemoryFont is slower then AddFontFile (21ms vs. 15 ms)

Again, thanks to all comments!



Related Topics



Leave a reply



Submit