很多時候,由於內部圖形設計人員會選擇字型,因此需要在應用程式中使用特定字型。爲了使應用程式使用字型,需要使用安裝程式來安裝字型。使用者計算機上的字型過多可能會大大降低系統速度。
實際上,您無需安裝字型就可以擺脫困境:作爲程式設計師,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<<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
方法。大多數位體屬於字型家族。例如,在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字型檔案的字型名稱。您喜歡或不喜歡這篇文章的任何地方,請告訴我,以便我可以對本文進行改進。我希望您喜歡閱讀我的文章!