Using per-session fonts

| | Comments (3)

One thing which some applications like to do is to use custom fonts. However, you may not want to install that font into the user's fonts directory. It's entirely possible that you just want to use this font yourself and skip the installer, permissions issues, etc.

So what you want to do is install a font for the duration of your application's session. Well, as it turns out, this is relatively easy to do on Windows. You can make a call to AddFontResource, which lets you add the font to the internal Windows font table. Then you can just use the font as if it's been installed to the fonts directory. When you're done using the font, you can unload it using the RemoveFontResource API. The code for these looks like this:[rbcode]
Protected Sub TemporarilyInstallFont(fontFile as FolderItem, privateFont as Boolean = true)
#if TargetWin32
Soft Declare Sub AddFontResourceExW Lib "Gdi32" ( filename as WString, flags as Integer, reserved as Integer )
Soft Declare Sub AddFontResourceA Lib "Gdi32" ( filename as CString )
Soft Declare Sub AddFontResourceW Lib "Gdi32" ( filename as WString )

Const FR_PRIVATE = &h10

if privateFont and System.IsFunctionAvailable( "AddFontResourceExW", "Gdi32" ) then
// If the user wants to install it as a private font, then we need to
// use the Ex APIs. Otherwise, use the regular APIs. We know
// that AddFontResourceEx is available in Win2k and up, so if
// the private flag is specified, we have to check to make sure
// we can load the API as well. We won't bother with the A
// version of the call since we know the W version will be there.
AddFontResourceExW( fontFile.AbsolutePath, FR_PRIVATE, 0 )
else
// The user wants to install it as a public font, or they are running
// on an OS without the ability to make private fonts
if System.IsFunctionAvailable( "AddFontResourceW", "Gdi32" ) then
AddFontResourceW( fontFile.AbsolutePath )
else
AddFontResourceA( fontFile.AbsolutePath )
end if
end if
#endif
End Sub

Protected Sub UninstallTemporaryFont(fontFile as FolderItem)
#if TargetWin32
Soft Declare Sub RemoveFontResourceExW Lib "Gdi32" ( filename as WString, flags as Integer, reserved as Integer )
Soft Declare Sub RemoveFontResourceA Lib "Gdi32" ( filename as CString )
Soft Declare Sub RemoveFontResourceW Lib "Gdi32" ( filename as WString )

Const FR_PRIVATE = &h10

if System.IsFunctionAvailable( "RemoveFontResourceExW", "Gdi32" ) then
RemoveFontResourceExW( fontFile.AbsolutePath, FR_PRIVATE, 0 )
end if

if System.IsFunctionAvailable( "RemoveFontResourceW", "Gdi32" ) then
RemoveFontResourceW( fontFile.AbsolutePath )
else
RemoveFontResourceA( fontFile.AbsolutePath )
end if
#endif
End Sub[/rbcode]
As you can see, you need to specify which font you want to install. The private parameter lets you specify whether this font should be for your application only, or for other applications to use (note that this functionality depends on the version of Windows you're running).

But what if you want to make a single-file executable? It'd be pretty bad if the only files you have outside of your app are just some lowly font files. Have no fear -- you can still do this.

Drag the font file into your project so that it's included when the application is built. Now you've got a (binary) string that contains all of the font data. You can use this to write out a file to the temp directory (which you've always got write access to), and then you can load and unload from there. Something like this will work nicely:[rbcode]
// Get the font file
mFontFile = GetTemporaryFolderItem
// Write the font data to the file
mFontFile.CreateBinayFile( "" ).Write( TheFontDataDraggedIntoTheProject )
// Install the font
TemporarilyInstallFont( mFontFile, true )[/rbcode]
When you're done with the font, you just need to call UninstallTemporaryFont( mFontFile ) to remove the font resource. If you fail to call UninstallTemporaryFont, then what happens depends on the OS and whether you register the font as private or not. If it's not private, then the font stays in the resource table until the Windows session is restarted (a shutdown or reboot happens). So it's always best to clean up after yourself; just call UninstallTemporaryFont when your application closes. It'd be trivial to make a FontManager class which does all of this tom-foolery for you.

3 Comments

This is just too cool. Thanks, Aaron.

Great solution. Thanks!

Prior art for Mac users:

http://support.realsoftware.com/listarchives/realbasic-nug/2006-06/msg00917.html

You know, they're *right* about that whole imitation/flattery thing...! :D

Eric in Seattle

Leave a comment

Disclaimer

I'm currently an employee of REAL Software. My blog is mine. The opinions represented in this blog are mine as well and may not represent my employer's opinions. All original material is copyrighted and property of the author.

REALbasic® is a registered trademark of REAL Software, Inc. REAL SQL Server™ and Lingua™ are pending trademarks of REAL Software, Inc. All rights reserved.