## 1.8 文字
GDI+的文本排版和字體處理的功能比 GDI 的更加強大。特別是 Windows XP 及以上版本,提供了對 LCD(液晶)顯示器的特殊優化功能,GDI+也提供了對應的 ClearType(清晰活字)文字處理技術,以增強字體的清晰度。另外,GDI+還提供了構造專用字體集的功能,可以包含私有的臨時字體(不需預先安裝到系統中)。
Windows 中使用的字體,一般是 TrueType(真實活字) 字體(TTF=TrueType Font),它是 1991 年 Apple 和 Microsoft 聯合開發的一種字體技術,采用二次貝塞爾曲線來描述字符 的輪廓。

在 GDI+中,與文字相關的類有(參見圖 14-40):字體族類 FontFamily、字體類 Font 和字體集類 FontCollection 及其兩個派生類 InstalledFontCollection (已安裝字體集)和 PrivateFontCollection(專用字體集)。而在 GDI 中,則只有 CFont 一個字體類。
圖 14-40 字體類的層次結構
### 1.8.1 字體
下面介紹字體族類 FontFamily 和字體類 Font 及相關參數。
(1)字體族類 FontFamily
字體族(font family)是一組具有同一字樣(typeface),但是風格(style)不同的字體(font)。其中,字樣是指字體的種類,如 Arial、Times New Roman、宋體、楷體_GB2312。 風格是指:正常(regular)、粗體(bold)、斜體(italic)、粗斜體(bold and italic)、下劃線(underline)、刪除線(strikeout)等。
1)構造函數
字體族類 FontFamily 有兩個構造函數:
```
FontFamily( VOID); // 構造一個空字體族(少用)
// 構造具有指定名稱 name,位于指定字體集 fontCollection 中的字體族
FontFamily(const WCHAR *name, const FontCollection *fontCollection = NULL);
```
只要不是使用專用字體集中的字體,一般不需要設置第二個輸入參數,取默認的 NULL 即可。例如:
```
FontFamily fontFamily(L"宋體"); 或
FontFamily fontFamily(L"Times New Roman");
```
2)顯示當前系統已裝入的字體(族)名稱
可先利用(字體集 FontCollection 的派生類)已安裝字體集類 InstalledFontCollection 的 方法 GetFamilyCount 和 GetFamilies 來分別獲取當前系統中已經安裝字體集中字體族的數目 和對象指針:
```
INT GetFamilyCount( VOID) const;
Status GetFamilies(INT numSought, FontFamily *gpfamilies, INT *numFound) const;
```
然后再利用字體族類的方法 GetFamilyName 來獲取每個字體族的名稱:
```
Status GetFamilyName(WCHAR name[LF_FACESIZE], WCHAR language = LANG_NEUTRAL) const;
```
其中 LANG_NEUTRAL 表示采用中立語言,即用戶的默認語言。
例如(可創建一個帶滾動視圖類的單文檔 MFC 應用程序 Fonts,添加對 GDI+的支持, 輸出結果如圖 14-41 所示):
```
void CFontsView::OnDraw(CDC* pDC) {
……
InstalledFontCollection ifc; int n = ifc.GetFamilyCount();
FontFamily *ffs = new FontFamily[n];
int found;
ifc.GetFamilies(n, ffs, &found); wchar_t name[LF_FACESIZE];
Font font(L"宋體", 18);
SolidBrush textBrush(Color::Black);
Graphics graph(pDC->m_hDC); wchar_t str[40];
swprintf_s(str, 40, L"當前系統中,總共裝有如下%d 種字體:", n);
graph.DrawString(str, INT(wcslen(str)), &font, PointF(10.0f, 10.0f), &textBrush);
for (int i = 0; i < n; i++) {
ffs[i].**GetFamilyName**(name);
graph.DrawString(name, INT(wcslen(name)), &font,
PointF(10.0f, 80.0f + 40 * i), &textBrush);
graph.DrawString(L"Font Family 字體族", 15,
&Font(name, 18),
PointF(300.0f, 80.0f + 40 * i), &textBrush);
}
}
```

圖 14-41 獲取并顯示當前系統的字體
注:如果想用程序將這些名稱寫入一個文本文件,需要注意 ofstream 不支持寬字符串的 流輸出,可以用實例模板類 of**w**stream 來定義一個新的文件輸出流類型。還可以采用 CFile 來輸出,但要注意寬字符串采用的是 UTF-16 編碼,需要在文本文件的開始處,添加用 0xFE 和 0xFF 這兩個字節表示的 UTF-16 編碼標志。
(2)字體類 Font
字體類 Font 的構造函數有 6 個,常用的是如下兩個:
```
Font(const FontFamily *family, REAL emSize, INT style = FontStyleRegular, Unit unit = UnitPoint);
Font(const WCHAR *familyName, REAL emSize, INT style = FontStyleRegular, Unit unit = UnitPoint, const FontCollection *fontCollection = NULL);
```
其中的第一個構造函數,其第一個輸入參數是字體族的指針,所以必須先創建字體族對象。
而第 2 個構造函數的第一個輸入參數則是字體族(字樣)的名稱,不需要創建字體族對象, 并且還多了可以選擇的字體集作為最后一個輸入參數。其余的構造函數都與 API 中的字體 句柄、邏輯結構和 DC 中的當前字體有關,在 GDI 中已經討論過。
注意:在使用 VC08 SP1 和 VC10 時,需要注釋掉(默認)位于 c:\program files\microsoft visual studio 9.0\vc\include\目錄中的 VC 頭文件 comdef.h 中的第 309~315 行:
```
// hard-coded smart pointer defs
/*#if defined( IFontDisp_INTERFACE_DEFINED )
if_not_exists(Font)
{
struct Font : IFontDisp {};
}
_COM_SMARTPTR_TYPEDEF(Font, uuidof(IDispatch));
#endif*/
```
不然,編譯時會出現兩個 Font 類定義沖突問題的錯誤。也可以不改 comdef.h,而在代碼中 的每個 Font 類名的前面,都加上命名空間限定符“Gdiplus::”,如 Gdiplus::Font,不過這樣 又太麻煩。
下面我們重點討論第二個構造函數的使用,先介紹其中的各個參數。
1)字體種類 familyName(字體族名)—— 寬字符串表示的字體名稱
+ 常用的英文字體族名有:
+ Times New Roman:Font Family Name 字體族名(有襯線)
+ Arial: Font Family Name 字體族名(無襯線)
+ Arial Narrow: Font Family Name 字體族名(窄體)
+ Courier New: Font Family Name 字體族名(等寬)
+ 常用的中文字體族名有:
+ 宋體: Font Family Name 字體族名(正文)
+ 楷體_GB2312: Font Family Name 字體族名(正文、標題)
+ 黑體: Font Family Name 字體族名(標題、美術)
+ 仿宋_GB2312: Font Family Name 字體族名(標題、美術)
+ 隸書: Font Family Name 字體族名(標題、美術)
2)字體風格 style—— 字體的風格,可以取如下枚舉常量(默認為 FontStyleRegular):
```
typedef enum {
FontStyleRegular = 0, // 正常(默認值)
FontStyleBold = 1, // 粗體
FontStyleItalic = 2, // 斜體
FontStyleBoldItalic = 3, // 粗斜體
FontStyleUnderline = 4, // 下劃線
FontStyleStrikeout = 8 // 刪除線
} FontStyle;
```
3)字體單位 unit 與大小 emSize——字體的大小與有單位關,可用單位有:
```
typedef enum {
UnitWor ld = 0, // 邏輯單位(非物理單位,默認為像素)
UnitDisplay = 1, // 設備單位,如對顯示器為像素、對打印機為墨點
UnitPixel = 2, // 像素(1/54 或 1/96 英寸?與屏幕大小和分辨率有關)
UnitPoint = 3, // 點或 1/72 英寸(默認值)
UnitInch = 4, // 英寸
UnitDocument = 5,../300 英寸
UnitMillimeter = 6 // 毫米 mm
} Unit;
```
其中,em = M,在印刷行業中表示一個西文印刷符號的全長或全寬。
在 GDI 的 CFont 部分,已經介紹了中文字號與英文磅數(相當于這里的 UnitPoint 點值) 的關系,表 14-1 列出了中文字號與幾種主要 Unit 單位的關系(設 1 像素=1/54 英寸)。
表 14-1 中文字號與 Unit 單位的關系
| 漢字字號 | Pixel像素 | Point點 | Inch英寸 | Document文檔 | Millimeter毫米 |
| --- | --- | --- | --- | --- | --- |
| 特號 | 133.33 | 100 | 1.39 | 416.67 | 35.28 |
| 小特 | 80 | 60 | 0.83 | 250 | 21.17 |
| 初號 | 56 | 42 | 0.58 | 175 | 14.82 |
| 小初 | 48 | 36 | 0.5 | 150 | 12.7 |
| 一號 | 34.67 | 26 | 0.36 | 108.33 | 9.17 |
| 小一 | 32 | 24 | 0.33 | 100 | 8.47 |
| 二號 | 29.33 | 22 | 0.31 | 91.67 | 7.76 |
| 小二 | 24 | 18 | 0.25 | 75 | 6.35 |
| 三號 | 21.33 | 16 | 0.22 | 66.67 | 5.64 |
| 小三 | 20 | 15 | 0.21 | 62.5 | 5.29 |
| 四號 | 18.67 | 14 | 0.19 | 58.33 | 4.94 |
| 小四 | 16 | 12 | 0.17 | 50 | 4.23 |
| 五號 | 14 | 10.5 | 0.15 | 43.75 | 3.70 |
| 小五 | 12 | 9 | 0.125 | 37.5 | 3.175 |
| 六號 | 10 | 7.5 | 0.10 | 31.25 | 2.65 |
| 小六 | 8.67 | 6.5 | 0.09 | 27.08 | 2.29 |
| 七號 | 7.33 | 5.5 | 0.08 | 22.92 | 1.94 |
| 八號 | 6.67 | 5 | 0.07 | 20.83 | 1.76 |
例如(參見圖 14-42):
```
REAL fs[] = {100, 60, 42, 36, 26, 24, 22, 18, 16, 15, 14, 12,
10.5, 9, 7.5, 6.5, 5.5, 5};
CString fno[] = {L"特", L"小特", L"初", L"小初", L"一",
L"小一", L"二", L"小二", L"三", L"小三", L"四", L"小四",
L"五", L"小五", L"六", L"小六", L"七", L"八"};
wchar_t str[100]; REAL size, y = 10.0f;
SolidBrush textBrush(Color::Black);
Graphics graph(pDC->m_hDC);
for (int i = 0; i < 18; i++) {
size = fs[i]; swprintf_s(str, 100,
L"這是%s 號字(%.4g 像素 %g 點 %.4g 英寸 %.4g 文檔 %.4g 毫米)",
fno[i], size * 4 / 3.0, size, size / 72.0,
size * 300 / 72.0, size / 72.0 * 25.4);
graph.DrawString(str, INT(wcslen(str)),
&Font(L"宋體", size), PointF(10.0f, y), &textBrush);
y += size * 1.5f;
}
```

圖 14-42 中文字號與 Unit 單位
### 1.8.2 繪制文本
在 GDI 中,我們用 CDC 類的方法 TextOut、DrawText 和 ExtTextOut 等來輸出文本串。 在 GDI+中,我們則是利用 Graphics 類的重載方法 DrawString 來繪制文本。
(1)畫串方法 DrawString
```
Status DrawString(const WCHAR *string, INT length, const Font *font, const PointF &origin, const Brush *brush);
Status DrawString(const WCHAR *string, INT length, const Font *font, const PointF &origin, const StringFormat *stringFormat, const Brush *brush);
Status DrawString(const WCHAR *string, INT length, const Font *font, const RectF &layoutRect, const StringFormat *stringFormat, const Brush *brush);
```
這三個同名的 Graphics 類重載方法,都以寬字符串作為第一個輸入參數(不支持普通 字符串)、串長為第二個參數(對以 null 結尾的字符串,可以使用-1 來代替)、最后一個參 數則都是繪制文本用的畫刷指針。
不同的是第三個輸入參數(都是浮點數版本,不支持整數版本):前兩個方法的是浮點 數版的點類 PointF 對象,表示文本串的位置(默認是左上角);最后一個方法的是浮點數版 的矩形類 RectF 對象,表示繪制文本的范圍(超出部分會被截掉)。
另 一 個不 同之 處是 ,后 兩個 方法 比第 一個 方法 多了 一個 輸入 參數 —— 串格 式 類 StringFormat 對象的指針,用于設置文本的對齊方式、輸出方向、自動換行、制表符定制、 剪裁等。
第一個畫串方法最簡單,使用得也最多。例如:
```
graph.DrawString(str, INT(wcslen(str)), &Font(L" 宋體 ", 12), PointF(10.0f, 10.0f), &brush);
graph.DrawString(str, -1, &font, &rect, &stringFormat, &brush);
```
(2)串格式類 StringFormat
StringFormat 是從 Gdiplus Base 類直接派生的一個 GDI+類,用于設置繪制字符串時的各 種格式。其主要的構造函數為:
```
StringFormat(INT formatFlags = 0, LANGID language = LANG_NEUTRAL);
```
其中:
formatFlags(格式標志位)—— 用于設置各種輸出格式,取值為 StringFormatFlags 枚舉的下列常量之位或“|”:
```
typedef enum {
StringFormatFlagsDirectionRightToLeft = 0x00000001, // 方向從右到左(默認為從左到右)
StringFormatFlagsDirectionVertical = 0x00000002, // 垂直方向(默認為水平)
StringFormatFlagsNoFitBlackBox = 0x00000004, // 允許字符尾部懸于矩形之外
StringFormatFlagsDisplayFormatControl = 0x00000020, // Unicode 布局控制符起作用
StringFormatFlagsNoFontFallback = 0x00000400, // 有替換用的“缺少字體”(默認為開方形符)
StringFormatFlagsMeasureTrailingSpaces = 0x00000800, // 測量時包含尾部空格符(默認不包含)
StringFormatFlagsNoWrap = 0x00001000, // 不自動換行
StringFormatFlagsLineLimit = 0x00002000, // 最后一行必須為整行高,避免半行高的輸出
StringFormatFlagsNoClip = 0x00004000 // 不使用剪裁
} StringFormatFlags;
```
language (語言) —— 取值為 16 位 語 言 標識 符 類 型 LANGID , 默認 值為 LANG_NEUTRAL(語言中立),表示采用用戶的默認語言。
(3)輸出方向
默認的文本串輸出方向是從左到右水平繪制。也可以在 StringFormat 類的構造函數中使 用參數值:StringFormatFlagsDirectionRightToLeft 和 StringFormatFlagsDirectionVertical 來修 改文本串的輸出方向為從右到左水平繪制和從上到下垂直繪制。
(4)剪裁與換行
默認情況下,使用矩形輸出長文本串時,會自動換行和剪裁。但是也可以在 StringFormatF 類的構造函數中,利用第一個輸入參數的 StringFormatFlags 枚舉值,來改變默認的設置。
(5)對齊
可以通過 StringFormat 類的方法來設置輸出文本串的對齊方式:
```
Status SetAlignment(StringAlignment align); // 設置水平對齊
Status SetLineAlignment(StringAlignment align); // 設置垂直對齊
```
其中的輸入參數為枚舉常量:
```
typedef enum {
StringAlignmentNear = 0, // 靠近(左上)
StringAlignmentCenter = 1, // 中心(對中)
StringAlignmentFar = 2 // 遠離(右下)
} StringAlignment;
```
### 1.8.3 美術字
下面介紹陰影、條紋、紋理、漸變、空心字和彩心字等繪制美術字的方法,它們是利用不同顏色、條紋和漸變的畫刷,以及多次繪圖的方式,來實現特定美術效果的。
(1)陰影字
可以使用兩種不同顏色的畫刷,經過在不同的為位置多次繪制同一文本串,就可以達到 輸出陰影字的效果。例如(參見圖 14-43):
```
Graphics graph(pDC->m_hDC);
SolidBrush textBrush(Color::Red), shadowBrush(Color::Gray); HatchBrush hatchBrush(HatchStyleForwardDiagonal,
Color::Black, Color::White);
CString str = L"陰影字符串";
Font font(L"華文新魏", 100);
REAL d = 10.0f, dd = 5.0f;
graph.DrawString(str, str.GetLength(), &font, PointF(d + dd, d + dd), &shadowBrush);
graph.DrawString(str, str.GetLength(), &font, PointF(d, d), &textBrush);
for (int i = 0; i < 20; i++)
graph.DrawString(str, str.GetLength(), &font,
PointF(d + i, 150 + d + i + 2), &hatchBrush);
```

```
graph.DrawString(str, str.GetLength(), &font, PointF(d, 150 + d), &textBrush);
```
圖 14-43 陰影字
(2)條紋字
也可以直接利用條紋刷,來繪制條紋狀的字符串。例如(參見圖 14-44):
```
Graphics graph(pDC->m_hDC);
CString str = L"條紋字符串";
Font font(L"華文新魏", 140);
HatchBrush hatchBrush1(HatchStyleForwardDiagonal, Color::Red, Color::White);
graph.DrawString(str, str.GetLength(), &font, PointF(0.0f, 0.0f), &hatchBrush1);
HatchBrush hatchBrush2(HatchStyleBackwardDiagonal, Color::Green, Color::White);
graph.DrawString(str, str.GetLength(), &font, PointF(0.0f, 200.0f), &hatchBrush2);
HatchBrush hatchBrush3(HatchStyleCross, Color::Blue, Color::White);
graph.DrawString(str, str.GetLength(), &font, PointF(0.0f, 400.0f), &hatchBrush3);
```

圖 14-44 條紋字
(3)紋理字
還可以利用紋理(圖像)刷來繪制紋理字符串。例如(參見圖 14-45):
```
Graphics graph(pDC->m_hDC);
CString str = L"紋理字符串";
Font font(L"華文新魏", 140);
TextureBrush textureBrush(&Image(L"張東健.bmp"));
graph.DrawString(str, str.GetLength(), &font, PointF(10.0f, 10.0f), &textureBrush);
```

圖 14-45 紋理字
(4)漸變字
當然,也可以利用線性漸變刷來繪制色彩變幻的字符串。例如,使用前面的多色漸變刷 代碼,可以得到很好的變色效果(參見圖 14-46):
```
Graphics graph(pDC->m_hDC);
CString str = L"顏色漸變字符串";
Font font(L"華文新魏", 100);
Color cols[] = {Color::Red, Color::Orange, Color::Yellow, Color::Green, Color::Cyan, Color::Blue, Color::Purple, Color::Magenta};
REAL bps[] = {0.0f, 0.15f, 0.3f, 0.45f, 0.6f, 0.75f, 0.875f, 1.0f};
LinearGradientBrush brush(Point(10, 10), Point(810, 10), Color::Black, Color::White);
brush.SetInterpolationColors(cols, bps, 8);
graph.DrawString(str, str.GetLength(), &font, PointF(10.0f, 10.0f), &brush);
```

圖 14-46 漸變字
(5)空心字與彩心字
還可以利用 GDI+的路徑和路徑漸變刷,來繪制空心和彩心字符串。例如(參見圖 14-47):
```
Graphics graph(pDC->m_hDC);
FontFamily ff(L"隸書");
wchar_t str[] = L"測試字符串";
REAL emSize = 120; // UnitWorld
graph.DrawString(str, -1, &Font(L"隸書", emSize, FontStyleRegular, UnitWorld), PointF(0, 0),
&SolidBrush(Color::Green));
GraphicsPath path, *pOutlinePath;
path.AddString(str, -1, &ff, FontStyleRegular, emSize, Point(0, 100), NULL); // 847 個點
Pen pen(Color::Red); graph.DrawPath(&pen, &path);
pOutlinePath = path.Clone();
pOutlinePath->Outline();
PathGradientBrush pgBrush(pOutlinePath);
int n = pOutlinePath->GetPointCount(); // 1023 個點
Color *cols = new Color[n];
for (int i = 0; i < n; i++)
cols[i] = Color(rand() % 255, rand() % 255, rand() % 255);
pgBrush.SetCenterColor(Color(rand() % 255, rand() % 255, rand() % 255));
pgBrush.SetSurroundColors(cols, &n);
graph.TranslateTransform(0.0f, 100.0f);
graph.FillPath(&pgBrush, &path);
```
中,由于彩心字用到了隨機顏色,所以每次刷新時的顏色都不一樣。

圖 14-47 普通、空心和彩心字符串
### 1.8.4 平滑處理與 ClearType 技術
為了提高文字的清晰度,需要對繪制的文本串進行平滑處理,防止在(特別是點陣)文 字被放大后出現明顯的鋸齒(馬賽克 mosaic)現象。ClearType(清晰活字)是微軟公司于 1999 年 4 月 7 日推出的一種圖形顯示技術,主要用于改善 LCD(Liquid Crystal Display,液 晶顯示)顯示器的顯示效果,提高圖形和文字的清晰度。
可以在 GDI+程序中,利用 Graphics 類的兩個文本繪制提示(hint)方法:
```
TextRenderingHint GetTextRender ingHint(VOID) const;
Status SetTextRenderingHint(TextRender ingHint newMode);
```
來獲取和設置文字繪制時的平滑處理方法。其中的枚舉類型 TextRenderingHint 的定義為:
```
typedef enum {
TextRenderingHintSystemDefault = 0, // 同系統平滑方式
TextRenderingHintSingleBitPerPixelGr idFit = 1, // 不消鋸齒,網格匹配
TextRenderingHintSingleBitPerPixel = 2, // 不消鋸齒,不網格匹配
TextRenderingHintAntiAliasGridFit = 3, // 消鋸齒,網格匹配
TextRenderingHintAntiAlias = 4, // 鋸齒,不網格匹配
TextRenderingHintClearTypeGridFit = 5 // 使用 ClearType 技術,不網格匹配
} TextRenderingHint;
```
這里的網格匹配(grid fit),主要是指在文本繪制時,通過調整字形的平直和垂直筆畫的寬 度,以達到提高文字輸出質量的一種方法。
例如(輸出結果如圖 14-48,為放大后的效果):
```
Graphics graph(pDC->m_hDC);
SolidBrush textBrush(Color::Black);
Font font(L"Arial", 16);
CString str = L"font smoothing";
wchar_t buf[5];
for (int i = 0; i < 6; i++) {
_itow_s(i, buf, 5, 10);
graph.SetTextRenderingHint(TextRenderingHint(i));
graph.DrawString(buf, -1, &font, PointF(5.0f, 20.0f * i), &textBrush);
graph.DrawString(str, str.GetLength(), &font, PointF(30.0f, 20.0f * i), &textBrush);
}
```

圖 14-48 文字繪制時的平滑處理方式 細心的同學可能會發現,除了漸變和路徑刷及平滑處理外,大多數文本輸出功能,GDI
都有。而且 GDI 還可以以任意角度繪制文本串,但是 GDI+好像不能。其實,這可以利用 GDI+的矩陣變換來實現。矩陣變換的內容,會在后面的 14.2.8 小節介紹。
專用字體集
如果你想使用系統中還沒有被安裝的字體,有如下兩種方法可供選擇。
(1)手工安裝字體
選擇 Windows XP 操作系統的“../控制面板/字體”圖標,啟動“字體”程序; 然后再選擇“文件/安裝新字體”菜單項,打開“添加字體”對話框(參見圖 14-49);選擇 字體所在的文件目錄,會出現目錄中所有字體的名稱和類型列表;選中想安裝的字體后,按 確定關閉對話框。這樣,就完成了字體的安裝工作。將字體裝進系統后,就可以和其他已裝 入字體一樣正常使用了。

圖 14-49 添加字體對話框
(2)使用專用字體集
與已安裝字體集類 InstalledFontCollection 一 樣 , 專 用 ( 私 有 ) 字 體 集 類 PrivateFontCollection 也是字體集類 FontCollection 的派生類。
PrivateFontCollection 類只有一個構造一個空的字體集默認構造函數:
```
PrivateFontCollection(VOID);
```
但是可用方法 AddFontFile 來向字體集中添加字體文件:
```
Status AddFontFile(const WCHAR* filename);
```
將字體文件加入專用字體集后,我們就可以利用其父類的方法
```
Status GetLastStatus(VOID);
```
和 GetFamilyCount、GetFamilies 等,來判斷裝入是否成功、字體集中共有多少種字體、獲 取指定數目的字體族 FontFamily 對象的數組(指針)。這些都與前面“顯示當前系統已裝入 的字體(族)名稱”部分所講的類似。包括用 FontFamily 類的 GetFamilyName 方法獲取字 體名稱,并用該名稱來創建字體對象(使用帶字體集參數的構造函數),最后繪制文本串。
下面的例子,使用漢鼎繁印篆和漢鼎繁特行兩種專用字體,輸出文本串“專用字體集: 字體名稱”和詩句“三顧頻煩天下計 兩朝開濟老臣心”(參見圖 14-50)。這兩種字體所對應的字體文件 HDZB_25.TTF 和 HDZB_16.TTF,已經放入我個人網頁的資源子目錄 res 中。
你也可以自己從網上下載其他字體文件來進行試驗。
```
// 裝入專用字體文件
PrivateFontCollection pfc;
pfc.AddFontFile(L"res\\HDZB_16.TTF");
if(pfc.GetLastStatus() != Ok)
{
MessageBox(L"裝入字體文件出錯!");
return;
}
pfc.AddFontFile(L"res\\HDZB_25.TTF");
if(pfc.GetLastStatus() != Ok)
{
MessageBox(L"裝入字體文件出錯!");
return;
}
int n = pfc.GetFamilyCount();
if (n < 2)
{
MessageBox(L"字體集中的字體數不夠!");
return;
}
// 獲取字體族對象數組
FontFamily ffs[2];
int found;
pfc.GetFamilies(2, ffs, &found);
// 定義輸出字符串
CString str0 = L"專用字體集:", str;
CString str1 = L"三顧頻煩天下計\r\n 兩朝開濟老臣心";
// 設置中對齊
StringFormat stringFormat;
stringFormat.SetAlignment(StringAlignmentCenter);
RECT rect;
GetClientRect(&rect);
// 創建圖形和文本刷對象
Graphics graph(pDC->m_hDC);
SolidBrush textBrush(Color::Black);
// 獲取字體名稱 1,構造字體 1,并輸出字符串
wchar_t name[LF_FACESIZE];
ffs[0].GetFamilyName(name);
Font font1(name, 60, FontStyleRegular, UnitPixel, &pfc);
str = str0 + name;
graph.DrawString(str, str.GetLength(), &font1, PointF(0.0f, 0.0f), &textBrush);
graph.DrawString(str1, str1.GetLength(), &font1,
PointF(rect.right / 2.0f, 80.0f), &stringFormat,
&textBrush);
// 獲取字體名稱 2,構造字體 2,并輸出字符串 ffs[1].GetFamilyName(name);
Font font2(name, 60, FontStyleRegular, UnitPixel, &pfc);
str = str0 + name;
graph.DrawString(str, str.GetLength(), &font2,
PointF(0.0f, 220.0f), &textBrush); graph.DrawString(str1, str1.GetLength(), &font2,
PointF(rect.right / 2.0f, 300.0f), &stringFormat,
&textBrush);
```

圖 14-50 使用漢鼎繁印篆和漢鼎繁特行專用字體