精通
英语
和
开源
,
擅长
开发
与
培训
,
胸怀四海
第一信赖
锐英源精品原创,禁止全文或局部转载,禁止任何形式的非法使用,侵权必究。点名“简易百科”和闲暇巴盗用锐英源原创内容。
显示文本是软件常用功能,而显示文本依赖字体,字体的安装卸载有时候很麻烦,内嵌字体资源比较保险。翻译本文,供大家参考学习。
每隔一段时间,我们开发的应用程序就需要用户系统中可能存在也可能不存在的字体。一个常见的解决方案是将字体文件与应用程序设置的其余部分捆绑在一起,并将它们与其他所有内容一起安装。诸如 MSI、NSIS 或 Inno 之类的软件包使这很容易。一个缺点是您的字体在系统范围内永久可见,例如,用户可以随时删除/卸载您的应用程序所依赖的字体。为了防止这种情况,您可以实现代码,在每次运行应用程序时检查字体的状态,如果需要,继续自行终止、警告用户或即时安装字体。
当然,如果您正在动态安装字体,您可以更进一步,将字体的生命周期限制为应用程序的执行。该方法将具有额外的优势,即在一定程度上保护字体的“隐私”,因为它仅在您的应用程序启动时可见。本文介绍了一个简单的类,它封装了这样的功能,即允许应用程序从编译的资源中动态安装/卸载字体,只需几行代码。
代码的直接使用将遵循以下步骤:
#include " AP_FontInstaller.h"
CAP_FontInstaller m_capFontInstaller;
m_capFontInstaller .AddFont( _T("Camelot MF"), IDR_FONT_CAMELOT, _T("FONTS") ) .AddFont( _T("Cigno MF"), IDR_FONT_CIGNO, _T("FONTS") ); m_capFontInstaller.InstallAllFonts();
m_capFontInstaller.UninstallAllFonts();
只有系统中不存在的字体才会被CAP_FontInstaller类安装和卸载。因此,如果您忘记卸载字体,下次运行应用程序时,它会将这些字体标记为已存在,您将无法卸载它们。当然,您也可以修改代码的行为以适应其他用途,这可能不是问题。
该类的代码CAP_FontInstaller旨在实现一个相当简单的公式,即保留一个可以安装和卸载的字体列表。要尊重的规则是,如果系统中已经存在任何这些字体,它们将不会被干预。
因此,首要任务是找出已经安装了哪些字体。这是通过调用 API 来完成的,而 APIEnumFontFamiliesEx又依赖于回调函数的实现。代码如下所示:
BOOL CAP_FontInstaller::IsFontAlreadyInSystem( const CString& csFontName ) { HDC hDC = GetDC( NULL ); LOGFONT lf = { 0, 0, 0, 0, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, NULL }; FONT_DETAILS fdFont; fdFont.m_csFontName = csFontName; fdFont.m_bInstalled = FALSE; EnumFontFamiliesEx( hDC, &lf, (FONTENUMPROC)_EnumFontFamExProc, (LPARAM)(LPVOID)&fdFont, 0 ); return fdFont.m_bInstalled; } int CALLBACK CAP_FontInstaller::_EnumFontFamExProc( ENUMLOGFONTEX * lpelfe, NEWTEXTMETRICEX * /*lpntme*/, int /*nFontType*/, LPARAM lParam ) { PFONT_DETAILS pfd = (PFONT_DETAILS)lParam; if( pfd->m_csFontName == lpelfe->elfLogFont.lfFaceName ) { pfd->m_bInstalled = TRUE; return FALSE; // Finished } else return TRUE; // Continue }
回调函数_EnumFontFamExProc由系统调用,一次只针对一种字体。请注意,face-name 不必(而且通常不是)与字体文件名相同。如有疑问,请使用字体查看器。
注:这个不相同,意义很大。
如前所述,安装程序会保留要安装/卸载的字体列表。此列表是一个结构数组,其声明如下所示:
typedef struct { CString m_csFontName; CString m_csInstalledFontFullPath; UINT m_uResID; CString m_csResType; BOOL m_bInstalled; BOOL m_bAlreadyInTheSystem; } FONT_DETAILS, *PFONT_DETAILS;
对于传递给公共方法的每种字体,都会填充一个结构并将其添加到数组中AddFont。
安装涉及从应用程序的编译资源中提取字体并在相应的系统文件夹中创建字体文件(视操作系统版本而异)。代码如下所示:
BOOL CAP_FontInstaller::WriteFontFile( const CString& csInstalledFontFullPath, UINT uResID, const CString& csResType ) { BOOL bSuccess = FALSE; HINSTANCE hInst = AfxGetResourceHandle(); HRSRC hResource = FindResource( hInst, MAKEINTRESOURCE( uResID ), csResType ); if( hResource ) { HGLOBAL hGlobal = LoadResource( hInst, hResource ); if( hGlobal ) { TCHAR* szTemp = (TCHAR*)LockResource( hGlobal ); UINT uSize = (UINT)SizeofResource( hInst, hResource ); DeleteObject( (HGDIOBJ)hGlobal ); CFile cf; if( cf.Open( csInstalledFontFullPath, CFile::modeWrite | CFile::modeCreate ) ) { cf.Write( szTemp, uSize ); cf.Close(); bSuccess = TRUE; } } } return bSuccess; } BOOL CAP_FontInstaller::InstallFont( const CString& csFontName ) { BOOL bSuccess = FALSE; PFONT_DETAILS pfd = NULL; if( FindFontDescription( csFontName, pfd ) && pfd->m_bInstalled == FALSE && pfd->m_bAlreadyInTheSystem == FALSE && WriteFontFile( pfd->m_csInstalledFontFullPath, pfd->m_uResID, pfd->m_csResType ) ) { bSuccess = ( AddFontResource( pfd->m_csInstalledFontFullPath ) != 0 ); pfd->m_bInstalled = bSuccess; } return bSuccess; }
如您所见,提取资源和编写字体文件很简单。如果成功,安装字体需要调用 API AddFontResource。
卸载更简单:首先调用 API RemoveFontResource,然后删除字体文件本身。
BOOL CAP_FontInstaller::UninstallFont( const CString& csFontName ) { BOOL bSuccess = FALSE; PFONT_DETAILS pfd = NULL; if( FindFontDescription( csFontName, pfd ) && pfd->m_bInstalled == TRUE && pfd->m_bAlreadyInTheSystem == FALSE && RemoveFontResource( pfd->m_csInstalledFontFullPath ) ) { _unlink( pfd->m_csInstalledFontFullPath ); pfd->m_bInstalled = FALSE; bSuccess = TRUE; } return bSuccess; }
请记住,如果字体文件被系统锁定,调用_unlink将失败。
Windows 在修改字体方面特别挑剔。因此,让我提醒您,代码按“原样”提供,没有任何明示或暗示的保证。继续需要您自担风险。
用于演示项目的字体(Camelot MF 和 Cigno MF)是 Rick W. Mueller 的财产。
演示应用程序/项目不是很复杂。如果任一字体已安装在您的系统中,则演示将无法安装/卸载字体,因此将无法正常工作。