PrivateFontCollection.AddMemoryFont producing random errors on Windows Server 2012 R2
Asked Answered
M

1

8

I have a .NET 3.5 application that loads fonts into memory using PrivateFontCollection.AddMemoryFont and uses these to produce images. I have recently installed this on Windows Server 2012 R2 and it is producing intermittent errors.

The problem is illustrated by this method:

private Bitmap getImage(byte[] fontFile)
{
    using (PrivateFontCollection fontCollection = new PrivateFontCollection())
    {
        IntPtr fontBuffer = Marshal.AllocCoTaskMem(fontFile.Length);
        Marshal.Copy(fontFile, 0, fontBuffer, fontFile.Length);
        fontCollection.AddMemoryFont(fontBuffer, fontFile.Length);

        Bitmap image = new Bitmap(200, 50);
        using (Font font = new Font(fontCollection.Families[0], 11f, FontStyle.Regular))
        {
            using (Graphics graphics = Graphics.FromImage(image))
            {
                graphics.DrawString(String.Format("{0:HH:mm:ss}", DateTime.Now), font, Brushes.White, new PointF(0f, 0f));
            }
        }
        return image;
    }
}

On Windows 7 this works consistently. On Windows Server 2012 R2 it fails if called repeatedly using more than one font. For example:

getImage(File.ReadAllBytes("c:\\Windows\\Fonts\\Arial.ttf"));

works even if called hundreds of times, but calling with more than one font:

getImage(File.ReadAllBytes("c:\\Windows\\Fonts\\Wingding.ttf"));
getImage(File.ReadAllBytes("c:\\Windows\\Fonts\\Arial.ttf"));

will work for the first several calls (20 or so) but will then start to produce random results (the second call will sometimes return an image with text in wingdings - ie it's mixing up the fonts).

I also occasionally (rarely) get "A generic error occurred in GDI+" on the DrawString call.

None of these errors occur on Windows 7.

I have tried various options to clean up without any success.

As a workaround I have tried writing the font file to disk, then loading with AddFontFile, but (on Windows 2012 R2) the font file gets locked for the life of the process so cannot be deleted. This makes this option unacceptable.

Any help with either getting AddMemoryFont to work consistently, or getting AddFontFile to unlock the file, would be hugely appreciated.

Mclendon answered 30/8, 2014 at 14:24 Comment(3)
Standard GDI+ lossage, disposing a Font does not actually destroy it. It gets put back into a cache, with the assumption that it will be used again. An important perf optimization, creating fonts is pretty expensive. That ends poorly for private fonts when you destroy their home, the font will use released memory. Producing bewildering results, including hard crashes. You'll need to keep the collection around, as well as the IntPtr.Southwards
Have you found the fix for this? I am having the same issue on Windows Server 2012R2 as well. The code works fine on Windows Server 2003.Peeved
@HansPassant I've come across a test suite where even without disposing any Font or FontFamily, GDI+ adds the wrong font family to the collection when calling AddMemoryFont().Claus
L
1

A late answer, but maybe somebody else will be happy with it: I had exactly the same problems and after hours of trial and error i found out that a working solution for me was saving the font (bytearray from database) to a local file and loaded the file with the addFontFile method.

All problems were gone. Not an ideal solution, but a working one.

var path = Path.Combine(TemporaryFontPath, customFont.FontFileName);
if (!Directory.Exists(Path.GetDirectoryName(path)))
    Directory.CreateDirectory(Path.GetDirectoryName(path));
if(!File.Exists(path))
    File.WriteAllBytes(path, customFont.FontBytes);

using (var pvc = new PrivateFontCollection())
{
    pvc.AddFontFile(path);
    return pvc.Families.FirstOrDefault();
}
Lodgment answered 2/12, 2018 at 20:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.