很多时候,由于内部图形设计人员会选择字体,因此需要在应用程序中使用特定字体。为了使应用程序使用字体,需要使用安装程序来安装字体。用户计算机上的字体过多可能会大大降低系统速度。
实际上,您无需安装字体就可以摆脱困境:作为程序员,GDI和GDI +分别为您提供了两种添加字体的方式,供应用程序使用而无需安装字体。我将在本文中向您展示!
首先让我谈谈GDI的两个向应用程序添加字体的功能。然后,我将讨论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
函数中指定其名称即可,就像其他已安装的字体一样。要知道字体的名称,只需在Windows资源管理器中右键单击TTF扩展名文件,然后选择“打开”即可看到其实际名称。或者,您可以使用我编写的TTF
和TTC
类来了解字体名称。
注意:本文中的字体文件名称(“ SkiCargo.ttf ”)实际上是其字体名称“ SkiCargo”;通常不是这种情况!为了安全起见,请使用Windows资源管理器右键单击方法或刚才提到的TTF
and TTC
类来查找名称!
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 it
CFont newFont;
if (!newFont.CreateFontIndirect(&lf))
return;
CFont* pOldFont = dc.SelectObject(&newFont);
// use a path to record how the text was drawn
wchar_t buf[] = _T("The quick brown fox jumps over the lazy dog!");
dc.TextOut( 10, 10, buf, wcslen(buf));
// Put back the old font
dc.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.
);
如果我们的字体位于资源DLL,cabinet文件或档案压缩文件中,则可以将其提取到内存中,然后用于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");
}
}
对于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 text
FontFamily 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
for AddFontFile
。所有添加的字体都会被PrivateFontCollection
的析构函数删除。
对于GDI +,您可以使用其PrivateFontCollection
类成员AddMemoryFont
在内存中添加字体。
Status AddMemoryFont(const VOID *memory, INT length);
这是AddMemoryFont
在资源中嵌入的字体文件上使用方法。与相似AddFontFile
,没有RemoveMemoryFont
呼叫。PrivateFontCollection
的析构函数将处理所有事情。注意:要了解如何将字体文件添加到资源中,可以在本文后面参考本节。
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 / OTF和TTC字体文件中读取字体名称。为了支持Matroska(mkv)文件字体读取或嵌入式字体资源读取,my TTF
和TTC
class支持解析内存中的字体文件。仅供参考,这些Matroska文件通常包含视频通道,多种语言的音频通道,字幕以及视频中字幕的字体。我的课程非常易于使用。下面是一个以物理方式或在内存中读取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<
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<
注意:您应该始终调用GetFontFamilyName
方法来获取字体名称,而不是GetFontName
方法。大多数字体属于字体家族。例如,在Arial字体家族下,有几种Arial字体,其字体名称为“ Arial Bold”,“ Arial Bold Italic”,等等。以下是有关如何将TTF
的GetFontFamilyName
方法与AddFontResourceEx
函数一起使用的示例:
TTF m_Ttf;
//... During Initialization
CString 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 method
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");
wcscpy_s(lf.lfFaceName, m_Ttf.GetFontFamilyName().c_str());
// create and select it
CFont newFont;
if (!newFont.CreateFontIndirect(&lf))
return;
CFont* pOldFont = dc.SelectObject(&newFont);
// use a path to record how the text was drawn
wchar_t buf[] = _T("The quick brown fox jumps over the lazy dog!");
dc.TextOut( 10, 10, buf, wcslen(buf));
// Put back the old font
dc.SelectObject(pOldFont);
注意:我在网络上找不到足够的信息来解析fon文件,该文件是带有“ fon”扩展名的字体文件。我尝试了反向工程来获取文件名,但是失败了。但是,我会继续尝试。
要将字体文件添加到资源部分,请遵循我的演练示例。请注意,我的方法是直接编辑资源文件,而不是通过IDE的资源编辑器添加它,因为根据我的经验,资源编辑器倾向于弄乱资源的rc文件,从而导致所见即所得对话框编辑器不可用。注意:现在,最新的资源编辑器可能更加强大和稳定。要添加字体文件,我们必须分配一个资源ID来引用该字体。为此,请关闭您关注的解决方案或项目(如果已打开)。要分配资源ID,请打开Resource.h:
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by TestGDI_AddFontMem.RC
//
#define IDR_MAINFRAME 128
#define IDD_TESTGDI_ADDFONTMEM_DIALOG 102
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 129
#define _APS_NEXT_CONTROL_VALUE 1000
#define _APS_NEXT_SYMED_VALUE 101
#define _APS_NEXT_COMMAND_VALUE 32771
#endif
#endif
我相信您比我的这个简单项目拥有更多的资源ID。让我们将定义的ID命名为“ IDR_MYFONT
”。当然,您可以使用您认为合适的任何方式来命名。我们分配IDR_MYFONT
,目前的价值_APS_NEXT_RESOURCE_VALUE
是129
。然后我们将增加_APS_NEXT_RESOURCE_VALUE
; 这很重要,我们必须这样做,否则下一个资源将与您的字体共享相同的数字ID。下面是手动编辑的Resource.h的外观:
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by TestGDI_AddFontMem.RC
//
#define IDR_MAINFRAME 128
#define IDD_TESTGDI_ADDFONTMEM_DIALOG 102
#define IDR_MYFONT 129
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 130
#define _APS_NEXT_CONTROL_VALUE 1000
#define _APS_NEXT_SYMED_VALUE 101
#define _APS_NEXT_COMMAND_VALUE 32771
#endif
#endif
接下来,我们将编辑rc扩展文件。在您喜欢的文本编辑器中打开文件。注意:Visual Studio不应打开此rc文件所在的项目。搜索下面列出的部分:
/
//
// BINARY
//
如果找不到此部分,则可以自己添加。接下来添加字体ID及其字体文件。您的二进制部分可能看起来像这样。
/
//
// BINARY
//
IDR_MYFONT BINARY "res\\SkiCargo.ttf"
如RC代码所示,IDR_MYFONT
是一个二进制资源,它引用项目文件夹的“ res ”子文件夹中的SkiCargo.ttf文件。
如果发现在资源中添加字体很麻烦,则可以重命名字体文件名及其扩展名,这样没人会知道该文件是一种字体并弄乱了它。您甚至可以加密或压缩它。在读取内存中的文件之前,只需对其解密或将其解压缩即可。
您已经从GDI和GDI +中看到了两种方法,它们可以物理加载字体文件或从内存加载字体文件并使用它们。我希望这可以消除程序员在用户计算机上安装字体以使用它们的需要。我介绍了两个类来读取TTF和TTC字体文件的字体名称。您喜欢或不喜欢这篇文章的任何地方,请告诉我,以便我可以对本文进行改进。我希望您喜欢阅读我的文章!