ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
## 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 使用汉鼎繁印篆和汉鼎繁特行专用字体