In my last article I showed a simple, but naive way to embed fonts in your application. The problem with the other approach is that the operating system combines multiple font files into a single font family, each file containing a style (such as bold or italic) that is available. So if you added Arial regular and Arial bold font files to the PrivateFontCollection, you would only have a single FontFamily in the collection.
A better approach is to continue using font family names as strings to create the font. The FontFamily class has a constructor that takes a FontCollection which allows you to send in the PrivateFontCollection that you added your embedded font to.
Here is a better solution for embedding fonts. You can register fonts externally during application startup, then, whenever you need a font, just call FontHelper.Create. This method will create any font, including fonts installed on the system.
public class FontHelper
{
private static PrivateFontCollection sCustomFonts;
private static Dictionary<string, FontFamily> sFamilies;
static FontHelper()
{
sCustomFonts = new PrivateFontCollection();
sFamilies = new Dictionary<string, FontFamily>(
StringComparer.InvariantCultureIgnoreCase);
}
public static void RegisterFont(byte[] font)
{
var buffer = Marshal.AllocCoTaskMem(font.Length);
Marshal.Copy(font, 0, buffer, font.Length);
sCustomFonts.AddMemoryFont(buffer, font.Length);
}
public static void RegisterFont(string fontFilePath)
{
sCustomFonts.AddFontFile(fontFilePath);
}
public static FontFamily[] GetAllFamilies()
{
var families = new List<FontFamily>();
families.AddRange(FontFamily.Families);
families.AddRange(sCustomFonts.Families);
families.Sort((f1, f2) => { return f1.Name.CompareTo(f2.Name); });
return families.ToArray();
}
public static FontFamily GetFamily(string family)
{
try
{
// cache the family in a dictionary for fast lookup.
if (!sFamilies.ContainsKey(family))
sFamilies.Add(family, new FontFamily(family, sCustomFonts));
}
catch (ArgumentException)
{
sFamilies.Add(family, new FontFamily(family));
}
return sFamilies[family];
}
public static Font Create(
string family,
float emSize,
FontStyle style = FontStyle.Regular,
GraphicsUnit unit = GraphicsUnit.Pixel)
{
var fam = GetFamily(family);
return new Font(family, emSize, style, unit);
}
}
You would use this class like this. MyFonts is simply a resource file (.resx) that I added to my project and dragged the font file (.ttf) into.
FontHelper.RegisterFont(MyFonts.Consolas);
using(var font = FontHelper.Create("Consolas", 12))
{
// Use the font
}
Unfortunately I am not able to use this code for work. One of the requirements we have is that we must embed the fonts into PDF files. As much as I would like to deploy without installing fonts, it is much easier to embed fonts with the PDF library we are using when they are installed on the machine.