1

I am working on a program and I want custom font for labels and buttons I searched a lot to find solution I tried with this code but it works only with Home form if I move the font to another form program working for maybe 5 seconds and stopped.

   [DllImport("gdi32.dll")]

   public static extern IntPtr AddFontMemResourceEx(IntPtr pbfont, uint cbfont,
                   IntPtr pdv, [In] ref uint pcFonts);

    FontFamily ff;
    Font font;
    public void AllcoBold(Font f, Control c, float size)
    {
        FontStyle fs = FontStyle.Bold;
        c.Font = new Font(ff, size, fs);
    }
       
    public void loadBold()
    {
        byte[] fontArray = Counter.Properties.Resources.TheSans;
        int datalength = Counter.Properties.Resources.TheSans.Length;
        IntPtr ptrData = Marshal.AllocCoTaskMem(datalength);
        Marshal.Copy(fontArray, 0, ptrData, datalength);

        uint cFonts = 0;
        AddFontMemResourceEx(ptrData, (uint)fontArray.Length, IntPtr.Zero, ref cFonts);
        PrivateFontCollection pfc = new PrivateFontCollection();
        pfc.AddMemoryFont(ptrData, datalength);
        Marshal.FreeCoTaskMem(ptrData);
        ff = pfc.Families[0];
        font = new Font(ff, 15f, FontStyle.Bold);
    }
     
    private void LoadFontStyleBold()
    {
        AllcoBold(font, startBtn, 14);
    }
2
  • pfc should be a class field of type PrivateFontCollection. Instantiate it in loadBold method and dispose of it ( pfc.Dispose(); ) in the FormClosed event. Get rid of 1) Marshal.FreeCoTaskMem(ptrData);, this releases the data required to keep the custom Font alive. 2) The class fields ff and font. You can get them from pfc. Also, you can write int datalength = fontArray.Length; instead.
    – dr.null
    Oct 24, 2022 at 7:52
  • Thanks for your replay. I am sorry but I didn't understand you Would you mind sir to change the code and I will check your comment to understand how it works ? Oct 24, 2022 at 8:46

1 Answer 1

2

Well, you need to create and destroy the PrivateFontCollection the right way. The loaded in-memory font's data should not be released as long as the Font(s) is/are in use. So, calling Marshal.FreeCoTaskMem(ptrData); is a common mistake in this context. Just dispose of the PrivateFontCollection instance when the container is disposing, in the FormClosed event if you have this code in a Form, or in the Dispose method implementation/override if you have it in a class that implements the IDisposable interface.

Accordingly, you should have something like:

public partial class YourForm : Form
{
    private PrivateFontCollection pfc;

    public YourForm()
    {
        InitializeComponent();

        // ...
    }

    protected override void OnFormClosed(FormClosedEventArgs e)
    {
        base.OnFormClosed(e);
        pfc?.Dispose();
    }

    public void AllcoBold(FontFamily ff, Control c, float size)
    {
        if (ff.IsStyleAvailable(FontStyle.Bold))
            c.Font = new Font(ff, size, FontStyle.Bold);
        else
            throw new NotSupportedException($"{ff.Name} does not support Bold style.");
    }

    public void LoadPfc()
    {
        if (pfc != null) return;

        uint cFonts = 0;
        byte[] fontArray = Counter.Properties.Resources.TheSans;
        IntPtr ptrData = Marshal.AllocCoTaskMem(fontArray.Length);

        Marshal.Copy(fontArray, 0, ptrData, fontArray.Length);
        AddFontMemResourceEx(ptrData, (uint)fontArray.Length, IntPtr.Zero, ref cFonts);

        pfc = new PrivateFontCollection();
        pfc.AddMemoryFont(ptrData, fontArray.Length);
    }
}

    private void LoadFontStyleBold()
    {
        AllcoBold(pfc.Families[0], startBtn, 14);
    }

    [DllImport("gdi32.dll")]
    private static extern IntPtr AddFontMemResourceEx(
        IntPtr pbFont, uint cbFont, IntPtr pdv, [In] ref uint pcFonts);

I don't know about your implementation, maybe you just need a function that picks up a FontFamily from the collection, create and return a Font object?

private Font GetPrivateFont(string fontFamily, FontStyle style, int size)
{
    var ff = pfc.Families.FirstOrDefault(x => x.Name.ToLower() == fontFamily.ToLower());

    if (ff == null)
        throw new KeyNotFoundException($"The collection does not contain {fontFamily}.");

    if (!ff.IsStyleAvailable(style))
        throw new NotSupportedException($"{ff.Name} does not support {style} style.");

    return new Font(ff, size, style);
}

If it's all about one FontFamily in the collection, then you can omit the fontFamily param and .FirstOrDefault query. It's pfc.Families[0].

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.