介绍
为了使应用程序使用字体,需要使用安装程序来安装字体。用户计算机上的字体过多可能会大大降低系统速度。
实际上你可以不用安装字体:GDI和GDI+都提供了两种方式,供程序员在不安装的情况下为应用程序添加字体。
GDI的AddFontResourceEx
让我首先讨论GDI的两个函数,用于向应用程序添加字体以供使用,可以使用AddFontResourceEx
添加用于应用程序的字体文件。
int AddFontResourceEx( LPCTSTR lpszFilename, // font file name DWORD fl, // font characteristics PVOID pdv // reserved);
以下是如何使用AddFontResourceEx
的示例:
CString szFontFile = "D:SkiCargo.ttf";int nResults = AddFontResourceEx( m_szFontFile, // font file name FR_PRIVATE, // font characteristics NULL);
要使用你添加的字体,只需在CreateFont
或CreateFontIndirect
函数中指定它名称,就像其他已安装的字体一样,
注:本文中的字体文件名("SkiCargo.ttf")实际上是它的字体名"SkiCargo";通常情况下不是这样的!
CClientDC dc(this); dc.SetBkMode(TRANSPARENT); LOGFONT lf; memset(&lf, 0, sizeof(lf)); lf.lfHeight = -MulDiv(24, pDC->GetDeviceCaps(LOGPIXELSY), 72); lf.lfWeight = FW_NORMAL; lf.lfOutPrecision = OUT_TT_ONLY_PRECIS; wcscpy_s(lf.lfFaceName, L"SkiCargo");// create and select itCFont newFont;if (!newFont.CreateFontIndirect(&lf)) return; CFont* pOldFont = dc.SelectObject(&newFont);// use a path to record how the text was drawnwchar_t buf[] = _T("The quick brown fox jumps over the lazy dog!"); dc.TextOut( 10, 10, buf, wcslen(buf));// Put back the old fontdc.SelectObject(pOldFont);
必须记住在应用程序退出之前调用RemoveFontResourceEx
,你应该注意,参数必须与提供给AddFontResourceEx
的参数相同!
BOOL RemoveFontResourceEx( LPCTSTR lpFileName, // name of font file DWORD fl, // font characteristics PVOID pdv // Reserved.); CString szFontFile = "D:SkiCargo.ttf"; BOOL b = RemoveFontResourceEx( m_szFontFile, // name of font file FR_PRIVATE, // font characteristics NULL // Reserved. );
GDI的AddFontMemResourceEx
如果字体位于资源DLL,cab文件或归档压缩文件中,则可以将它提取到内存中,然后使用AddFontMemResourceEx
从内存读取它。
HANDLE AddFontMemResourceEx( PVOID pbFont, // font resource DWORD cbFont, // number of bytes in font resource PVOID pdv, // Reserved. Must be 0. DWORD *pcFonts // number of fonts installed);
以下是如何在嵌入在资源的字体文件上使用AddFontMemResourceEx
的示例,注意:要了解如何向资源添加字体文件,可以在本文的后面参考这个部分。
HINSTANCE hResInstance = AfxGetResourceHandle( ); HRSRC res = FindResource(hResInstance, MAKEINTRESOURCE(IDR_MYFONT),L"BINARY");if (res) { HGLOBAL mem = LoadResource(hResInstance, res); void *data = LockResource(mem); size_t len = SizeofResource(hResInstance, res); DWORD nFonts; m_fonthandle = AddFontMemResourceEx( data, // font resource len, // number of bytes in font resource NULL, // Reserved. Must be 0. &nFonts // number of fonts installed ); if(m_fonthandle==0) { MessageBox(L"Font add fails", L"Error"); } }
要使用你添加的字体,请参考前面的AddFontResourceEx
例子,它们是一样的,只要像其他已安装的字体一样使用,你应该在应用程序退出之前调用RemoveFontMemResourceEx
,当进程离开时,系统将卸载字体,即使你不调用RemoveFontMemResourceEx
,注:参数必须与提供给AddFontResourceEx
的参数相同!
BOOL RemoveFontMemResourceEx( HANDLE fh // handle to the font resource);if(m_fonthandle) { BOOL b = RemoveFontMemResourceEx(m_fonthandle); if(b==0) { MessageBox(L"Font remove fails", L"Error"); } }
PrivateFontCollection =s共GDI+ AddFontFile
对于GDI+,可以使用它PrivateFontCollection
类成员AddFontFile
添加物理字体文件。
Status AddFontFile(const WCHAR* filename);
以下是使用AddFontFile
添加字体文件的方法:
Gdiplus::PrivateFontCollection m_fontcollection;//...CString szFontFile = szExePath + L"SkiCargo.ttf"; Gdiplus::Status nResults = m_fontcollection.AddFontFile(szFontFile);
以下是我们刚刚添加到PrivateFontCollection
对象m_fontcollection
中的字体的用法。
// When painting the textFontFamily fontFamily;int nNumFound=0; m_fontcollection.GetFamilies(1,&fontFamily,&nNumFound);if(nNumFound>0) { Font font(&fontFamily,28,FontStyleRegular,UnitPixel); StringFormat strformat; wchar_t buf[] = L"The quick brown fox jumps over the lazy dog!"; graphics.DrawString(buf,wcslen(buf),&font, PointF(10.0f,10.0f),&strformat,&brush); }
注:与GDI的AddFontResourceEx
和AddFontMemResourceEx
不同,RemoveFontFile
没有用于AddFontFile
,所有添加的字体都将由PrivateFontCollection
=s析构函数删除。
PrivateFontCollection =s共GDI+ AddMemoryFont
对于GDI+,可以使用它PrivateFontCollection
类成员AddMemoryFont
在内存中添加字体。
Status AddMemoryFont(const VOID *memory, INT length);
以下是如何在资源中嵌入的字体文件上使用AddMemoryFont
,类似于AddFontFile
,没有要调用的RemoveMemoryFont
,一切都将由PrivateFontCollection
=s析构函数处理,注意:要了解如何向资源添加字体文件,可以在本文的后面参考这个部分。
HINSTANCE hResInstance = AfxGetResourceHandle( ); HRSRC res = FindResource(hResInstance, MAKEINTRESOURCE(IDR_MYFONT),L"BINARY");if (res) { HGLOBAL mem = LoadResource(hResInstance, res); void *data = LockResource(mem); size_t len = SizeofResource(hResInstance, res); Gdiplus::Status nResults = m_fontcollection.AddMemoryFont(data,len); if(nResults!=Gdiplus::Ok) { MessageBox(L"Font add fails", L"Error"); } }
关于如何使用刚刚添加到PrivateFontCollection
对象的字体m_fontcollection
,请参考前面的AddFontFile
示例,它们是相同的。
获取TTF和TTC字体名称
我编写了两个类,即TTF
和TTC
,分别从ttf otf和TTC字体文件中读取字体名称,为了支持Matroska (mkv )文件字体读取或嵌入字体资源读取,TTF
和TTC
类支持在内存中解析字体文件,以下是一个物理或在内存中读取TTF文件并显示其信息的示例:
void TestReadTtfFromFile(const std::wstring& szFile) { TTF ttf; ttf.Parse(szFile); Display(ttf); }void TestReadTtfFromMemory(const std::wstring& szFile) { struct _stat bufferStat; int nRet = _wstat(szFile.c_str(), &bufferStat); FILE* pFile = _wfopen(szFile.c_str(), L"rb"); if(pFile == NULL) { std::wcout<<L"Failed to create file"<<std::endl; return; } BYTE* buf = new BYTE[bufferStat.st_size]; fread(buf,bufferStat.st_size,1,pFile); fclose(pFile); TTF ttf; ttf.Parse(buf, bufferStat.st_size); delete [] buf; Display(ttf); }void Display(TTF& ttf) { std::wcout<<L"FontName :"<<ttf.GetFontName()<<std::endl; std::wcout<<L"Copyright :"<<ttf.GetCopyright()<<std::endl; std::wcout<<L"FontFamilyName :"<<ttf.GetFontFamilyName()<<std::endl; std::wcout<<L"FontSubFamilyName :"<<ttf.GetFontSubFamilyName()<<std::endl; std::wcout<<L"FontID :"<<ttf.GetFontID()<<std::endl; std::wcout<<L"Version :"<<ttf.GetVersion()<<std::endl; std::wcout<<L"PostScriptName :"<<ttf.GetPostScriptName()<<std::endl; std::wcout<<L"Trademark :"<<ttf.GetTrademark()<<std::endl; std::wstring szBold = ttf.IsBold()? L"true" : L"false"; std::wstring szItalic = ttf.IsItalic()? L"true" : L"false"; std::wstring szRegular = ttf.IsRegular()? L"true" : L"false"; std::wcout<<L"Bold :"<<szBold<<std::endl; std::wcout<<L"Italic :"<<szItalic<<std::endl; std::wcout<<L"Regular :"<<szRegular<<std::endl; std::wcout<<std::endl; }
TTC是一个包含TTF字体集合的字体文件,下面是读取TTC文件的物理或内存中,并显示它信息的示例。
void TestReadTtcFromFile(const std::wstring& szFile) { TTC ttc; ttc.Parse(szFile); Display(ttc); }void TestReadTtcFromMemory(const std::wstring& szFile) { struct _stat bufferStat; int nRet = _wstat(szFile.c_str(), &bufferStat); FILE* pFile = _wfopen(szFile.c_str(), L"rb"); if(pFile == NULL) { std::wcout<<L"Failed to create file"<<std::endl; return; } BYTE* buf = new BYTE[bufferStat.st_size]; fread(buf,bufferStat.st_size,1,pFile); fclose(pFile); TTC ttc; ttc.Parse(buf, bufferStat.st_size); delete [] buf; Display(ttc); }void Display(TTC& ttc) { for(size_t i=0; i<ttc.Size(); ++i ) { std::wcout<<L"FontName :"<<ttc.GetFontName(i)<<std::endl; std::wcout<<L"Copyright :"<<ttc.GetCopyright(i)<<std::endl; std::wcout<<L"FontFamilyName :"<<ttc.GetFontFamilyName(i)<<std::endl; std::wcout<<L"FontSubFamilyName :"<<ttc.GetFontSubFamilyName(i)<<std::endl; std::wcout<<L"FontID :"<<ttc.GetFontID(i)<<std::endl; std::wcout<<L"Version :"<<ttc.GetVersion(i)<<std::endl; std::wcout<<L"PostScriptName :"<<ttc.GetPostScriptName(i)<<std::endl; std::wcout<<L"Trademark :"<<ttc.GetTrademark(i)<<std::endl; std::wstring szBold = ttc.IsBold(i)? L"true" : L"false"; std::wstring szItalic = ttc.IsItalic(i)? L"true" : L"false"; std::wstring szRegular = ttc.IsRegular(i)? L"true" : L"false"; std::wcout<<L"Bold :"<<szBold<<std::endl; std::wcout<<L"Italic :"<<szItalic<<std::endl; std::wcout<<L"Regular :"<<szRegular<<std::endl; std::wcout<<std::endl; } }
注意:你应该始终调用GetFontFamilyName
方法来获取字体名称,而不是GetFontName
方法,大多数字体都属于font-family,例如,在Arial字体家族下,有几种Arial字体,其字体名称为"Arial Bold","Arial Bold Italic",依此类推。以下是有关如何将TTF
=s GetFontFamilyName
方法与AddFontResourceEx
函数一起使用的示例:
TTF m_Ttf;//... During InitializationCString szFontFile = "D:SkiCargo.ttf";int nResults = AddFontResourceEx( m_szFontFile, // font file name FR_PRIVATE, // font characteristics NULL); m_Ttf.Parse((LPCWSTR)(m_szFontFile)); //... In the OnPaint methodCClientDC dc(this); dc.SetBkMode(TRANSPARENT); LOGFONT lf; memset(&lf, 0, sizeof(lf)); lf.lfHeight = -MulDiv(24, pDC->GetDeviceCaps(LOGPIXELSY), 72); lf.lfWeight = FW_NORMAL; lf.lfOutPrecision = OUT_TT_ONLY_PRECIS;//wcscpy_s(lf.lfFaceName, L"SkiCargo");wcscpy_s(lf.lfFaceName, m_Ttf.GetFontFamilyName().c_str());// create and select itCFont newFont;if (!newFont.CreateFontIndirect(&lf)) return; CFont* pOldFont = dc.SelectObject(&newFont);// use a path to record how the text was drawnwchar_t buf[] = _T("The quick brown fox jumps over the lazy dog!"); dc.TextOut( 10, 10, buf, wcslen(buf));// Put back the old fontdc.SelectObject(pOldFont);