## 1.8 文字
GDI+的文本排版和字体处理的功能比 GDI 的更加强大。特别是 Windows XP 及以上版本,提供了对 LCD(液晶)显示器的特殊优化功能,GDI+也提供了对应的 ClearType(清晰活字)文字处理技术,以增强字体的清晰度。另外,GDI+还提供了构造专用字体集的功能,可以包含私有的临时字体(不需预先安装到系统中)。
Windows 中使用的字体,一般是 TrueType(真实活字) 字体(TTF=TrueType Font),它是 1991 年 Apple 和 Microsoft 联合开发的一种字体技术,采用二次贝塞尔曲线来描述字符 的轮廓。
![](https://box.kancloud.cn/2016-04-18_57144a8856675.png)
在 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);
}
}
```
![image](https://box.kancloud.cn/2016-04-18_57144a886bc95.jpg)
图 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;
}
```
![image](https://box.kancloud.cn/2016-04-18_57144a8884ae3.jpg)
图 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);
```
![image](https://box.kancloud.cn/2016-04-18_57144a88a3499.jpg)
```
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);
```
![image](https://box.kancloud.cn/2016-04-18_57144a88bec1c.jpg)
图 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);
```
![image](https://box.kancloud.cn/2016-04-18_57144a88d2ed7.jpg)
图 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);
```
![image](https://box.kancloud.cn/2016-04-18_57144a88e4d4b.jpg)
图 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);
```
中,由于彩心字用到了随机颜色,所以每次刷新时的颜色都不一样。
![image](https://box.kancloud.cn/2016-04-18_57144a8904f4f.jpg)
图 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);
}
```
![image](https://box.kancloud.cn/2016-04-18_57144a8925a0d.jpg)
图 14-48 文字绘制时的平滑处理方式 细心的同学可能会发现,除了渐变和路径刷及平滑处理外,大多数文本输出功能,GDI
都有。而且 GDI 还可以以任意角度绘制文本串,但是 GDI+好像不能。其实,这可以利用 GDI+的矩阵变换来实现。矩阵变换的内容,会在后面的 14.2.8 小节介绍。
专用字体集
如果你想使用系统中还没有被安装的字体,有如下两种方法可供选择。
(1)手工安装字体
选择 Windows XP 操作系统的“../控制面板/字体”图标,启动“字体”程序; 然后再选择“文件/安装新字体”菜单项,打开“添加字体”对话框(参见图 14-49);选择 字体所在的文件目录,会出现目录中所有字体的名称和类型列表;选中想安装的字体后,按 确定关闭对话框。这样,就完成了字体的安装工作。将字体装进系统后,就可以和其他已装 入字体一样正常使用了。
![image](https://box.kancloud.cn/2016-04-18_57144a893e0e9.jpg)
图 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);
```
![image](https://box.kancloud.cn/2016-04-18_57144a894cd5d.jpg)
图 14-50 使用汉鼎繁印篆和汉鼎繁特行专用字体