I have a function which returns a PrivateFontCollection:
Public Shared Function GetCustomFonts() As PrivateFontCollection
Dim result = New PrivateFontCollection
Dim customFontFiles = {"Garamond.TTF", "Garamond-Bold.TTF", "Garamond-Italic.TTF", "EurostileExtended-Roman-DTC.TTF"}
For Each fontFile In customFontFiles
result.AddFontFile(Hosting.HostingEnvironment.MapPath("/Includes/" & fontFile))
Next
Return result
End Function
I then use the function as follows:
Using customFonts = Common.GetCustomFonts()
' Do some stuff here
End Using
I would expect that the files would be released, but they are still locked: I get the following error: 'The action can't be completed because the file is open in System. Close the file and try again.'
Shutting down the website in IIS doesn't help; we have to recycle the app pool for it to release.
Can anyone advise on how to use PrivateFontCollection in such a way that the files are released inbetween uses?
As a workaround, I loaded the fonts into memory and used 'AddMemoryFont' instead. See code below. Bear in mind this is the first time I've touched unmanaged resources in .NET so I can't guarantee that the memory management below is OK.
Imports System.Drawing.Text
Imports System.Runtime.InteropServices
Public Class CustomFontService
Implements IDisposable
Dim _fontBuffers As List(Of IntPtr)
Dim _collection As PrivateFontCollection
Public Sub New()
_collection = New PrivateFontCollection
_fontBuffers = New List(Of IntPtr)
Dim customFontFiles = {"Garamond.TTF", "Garamond-Bold.TTF", "Garamond-Italic.TTF", "EurostileExtended-Roman-DTC.TTF"}
For Each fontFile In customFontFiles
Dim fontBytes = System.IO.File.ReadAllBytes(Hosting.HostingEnvironment.MapPath("/Includes/" & fontFile))
Dim fontBuffer As IntPtr = Marshal.AllocHGlobal(fontBytes.Length)
Marshal.Copy(fontBytes, 0, fontBuffer, fontBytes.Length)
_fontBuffers.Add(fontBuffer)
_collection.AddMemoryFont(fontBuffer, fontBytes.Length)
Next
End Sub
Public Function GetCustomFonts() As PrivateFontCollection
Return _collection
End Function
#Region "IDisposable Support"
Private disposedValue As Boolean ' To detect redundant calls
' IDisposable
Protected Overridable Sub Dispose(disposing As Boolean)
If Not Me.disposedValue Then
If disposing Then
' TODO: dispose managed state (managed objects).
End If
For Each buf In _fontBuffers
If (buf <> IntPtr.Zero) Then
Marshal.FreeHGlobal(buf)
End If
Next
' TODO: free unmanaged resources (unmanaged objects) and override Finalize() below.
' TODO: set large fields to null.
End If
Me.disposedValue = True
End Sub
' TODO: override Finalize() only if Dispose(ByVal disposing As Boolean) above has code to free unmanaged resources.
Protected Overrides Sub Finalize()
' Do not change this code. Put cleanup code in Dispose(ByVal disposing As Boolean) above.
Dispose(False)
MyBase.Finalize()
End Sub
' This code added by Visual Basic to correctly implement the disposable pattern.
Public Sub Dispose() Implements IDisposable.Dispose
' Do not change this code. Put cleanup code in Dispose(disposing As Boolean) above.
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
#End Region
End Class
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With