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);
    }
}

image

图 14-41 获取并显示当前系统的字体

注:如果想用程序将这些名称写入一个文本文件,需要注意 ofstream 不支持宽字符串的 流输出,可以用实例模板类 ofwstream 来定义一个新的文件输出流类型。还可以采用 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

图 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

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

图 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

图 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

图 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

图 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

图 14-48 文字绘制时的平滑处理方式 细心的同学可能会发现,除了渐变和路径刷及平滑处理外,大多数文本输出功能,GDI

都有。而且 GDI 还可以以任意角度绘制文本串,但是 GDI+好像不能。其实,这可以利用 GDI+的矩阵变换来实现。矩阵变换的内容,会在后面的 14.2.8 小节介绍。

专用字体集

如果你想使用系统中还没有被安装的字体,有如下两种方法可供选择。

(1)手工安装字体

选择 Windows XP 操作系统的“../控制面板/字体”图标,启动“字体”程序; 然后再选择“文件/安装新字体”菜单项,打开“添加字体”对话框(参见图 14-49);选择 字体所在的文件目录,会出现目录中所有字体的名称和类型列表;选中想安装的字体后,按 确定关闭对话框。这样,就完成了字体的安装工作。将字体装进系统后,就可以和其他已装 入字体一样正常使用了。

image

图 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

图 14-50 使用汉鼎繁印篆和汉鼎繁特行专用字体