0

After much trial and error I have finally got my application to work with custom fonts on machines other than the development machine, however, I'm not sure which of the two methods I have gotten to work is the preferred way of doing things.

Either embedding the font as a resource and using Assembly.GetManifestResourceStream() to push the font files data into unmanaged memory and adding it to a PrivateFontCollection using AddMemoryFont() like this:

(All error handling and superfluous code removed)

Imports System.Runtime.InteropServices
Imports System.Drawing.Text

Public Class FormExample

    Private ReadOnly pfc As New PrivateFontCollection
    Private f As Font
    Private ReadOnly Bytes() As Byte = GetFontData(Reflection.Assembly.GetExecutingAssembly, "MyFont.ttf")
    Private ReadOnly fontPtr As IntPtr = Marshal.AllocCoTaskMem(Me.Bytes.Length)

    Private Sub FormExample_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Marshal.Copy(Me.Bytes, 0, Me.fontPtr, Me.Bytes.Length)
        Me.pfc.AddMemoryFont(Me.fontPtr, Me.Bytes.Length)
        Me.f = New Font(Me.pfc.Families(0), 14, FontStyle.Regular)
        'Iterate through forms controls and set font where valid
        SetFormsCustomFont(Me, Me.f)
    End Sub

    Private Sub FormExample_Disposed(sender As Object, e As EventArgs) Handles Me.Disposed
        Marshal.FreeCoTaskMem(Me.fontPtr)
        Me.pfc.Dispose()
    End Sub

    Private Function GetFontData(Asm As Assembly, Name As String) As Byte()
        Using Stream As Stream = Asm.GetManifestResourceStream(Name)
            Dim Buffer() As Byte = New Byte(CInt(Stream.Length - 1)) {}
            Stream.Read(Buffer, 0, CInt(Stream.Length))
            Return Buffer
        End Using
    End Function

End Class

Or by simply including the font with the application rather than embedding it as a resource and using PrivateFontCollection.AddFontFile():

Public Class FormExample2

    Private ReadOnly pfc As New PrivateFontCollection
    Private f As Font

    Private Sub FormExample2_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Dim fontFile As String = ".\Path\To\MyFont.ttf"
        Me.pfc.AddFontFile(fontFile)
        Me.f = New Font(Me.pfc.Families(0), 14)
        'Iterate through forms controls and set font where valid
        SetFormsCustomFont(Me, Me.f)
    End Sub

    Private Sub FormExample2_Disposed(sender As Object, e As EventArgs) Handles Me.Disposed
        Me.pfc.Dispose()
    End Sub

End Class

In both methods the font collections need to be kept around for the life of the form, and I have to repeat the code and have font objects for each form in the application so that I don't trample over the unmanaged memory.

Other than the fact that the second method gives users access to your font file, is there any other benefit to doing it the first way?

OR, is there another preferred way of doing this that I am unaware of?

MILO
  • 195
  • 1
  • 2
  • 11
  • 1
    I, personally, use the former, adding Fonts to a satellite library, in its Resources. Note that you don't need to keep a pointer to a Font you create, you need to keep a *solid* reference to the `PrivateFontCollection`. You also don't need to call `Marshal.FreeCoTaskMem()`, see the Docs about it. You're using it correctly, though, only in the end, not right after the Font is added to the collection, as sometimes you can see around. You **have** to dispose of the `PrivateFontCollection`, this also you're doing correctly. I usually use a `try/finally` block, to make sure. – Jimi Sep 01 '20 at 07:06
  • Unhandled exceptions handling is also setup to dispose of the collection. -- You don't need that Stream to read the Font from the resources, the Font is a byte array, so you can have just `byte[] fontData = (byte[])Properties.Resources.ResourceManager.GetObject(fontName);` – Jimi Sep 01 '20 at 07:09
  • Does this answer your question? [How do I embed my own fonts in a WinForms app?](https://stackoverflow.com/questions/556147/how-do-i-embed-my-own-fonts-in-a-winforms-app) – TylerH Sep 01 '20 at 13:51
  • See also [Custom .ttf fonts to use in C# windows.Form](https://stackoverflow.com/questions/544590/custom-ttf-fonts-to-use-in-c-sharp-windows-form) – TylerH Sep 01 '20 at 13:52
  • @TylerH No, I have achieved the desired result already, what I would like to know is whether there is any particular benefit to doing it one way over the other, or if both ways I've used are not preferable and I should be looking at other options. With the first method, the only benefit I can see for the added complexity is that the client wont have access to a custom font file that may be a licensed file, but in the case where it is just any old free font I don't see the benefit. However Jimi has pointed out that I needn't stream the data so that reduces some of the extra efforts. – MILO Sep 01 '20 at 22:19

0 Answers0