当前位置: 首页 > 文档资料 > Gdip 编程基础 >

1.8 文字

优质
小牛编辑
142浏览
2023-12-01

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.331001.39416.6735.28
小特80600.8325021.17
初号56420.5817514.82
小初48360.515012.7
一号34.67260.36108.339.17
小一32240.331008.47
二号29.33220.3191.677.76
小二24180.25756.35
三号21.33160.2266.675.64
小三20150.2162.55.29
四号18.67140.1958.334.94
小四16120.17504.23
五号1410.50.1543.753.70
小五1290.12537.53.175
六号107.50.1031.252.65
小六8.676.50.0927.082.29
七号7.335.50.0822.921.94
八号6.6750.0720.831.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 使用汉鼎繁印篆和汉鼎繁特行专用字体