38_定制需求改程序显示字体,这些API得掌握。

定制需求改程序显示字体,这些API得掌握。

选择字体

系统提供了6种备用字体,前面说过GetStockObject函数用于获取备用(或者说库存)画笔、画刷、字体等的句柄”,获取字体句柄以后,可以通过调用SelectObject函数把字体选入DC中,以后通过GDI函数进行文本绘制就会使用新的DC属性。一些备用字体如下表所示。

宏含义
ANSI_FIXED_FONT 等宽系统字体
ANSI_VAR_FONT 变宽系统字体
DEVICE_DEFAULT_FONT 设备默认字体
OEM_FIXED_FONTOEM (原始设备制造商)等宽字体
SYSTEM_FONT 系统字体,默认情况下使用系统字体绘制菜单、对话框控件和文本
SYSTEM_FIXED_FONT 等宽系统字体

SelectObject函数可以把一个GDI对象选入指定的DC中∶

HGDIOB] SelectObject(
_In_HDChdc,//设备环境句柄
_In_HGDIOBJ hgdiobj  // GDI对象句柄
);

函数执行成功,返回原来(也就是被替换掉的)对象的句柄。通常需要保存一下返回值,在用新对象完成绘制操作以后,应该再调用一次SelectObject函数,用原来的对象替换掉新对象,也就是恢复DC属性。

例如,我们通过SystemMetrics程序用OEM_FIXED_FONT字体输出文本看一下效果︰

  • Metrics.h
#pragma once

struct
{
    int     m_nIndex;
    PTSTR   m_pLabel;
    PTSTR   m_pDesc;
}METRICS[] = {
    SM_CXSCREEN,                    TEXT("SM_CXSCREEN"),                    TEXT("屏幕的宽度"),
    SM_CYSCREEN,                    TEXT("SM_CYSCREEN"),                    TEXT("屏幕的高度"),
    SM_CXFULLSCREEN,                TEXT("SM_CXFULLSCREEN"),                TEXT("全屏窗口的客户区宽度"),
    SM_CYFULLSCREEN,                TEXT("SM_CYFULLSCREEN"),                TEXT("全屏窗口的客户区高度"),
    SM_ARRANGE,                     TEXT("SM_ARRANGE"),                     TEXT("如何排列最小化窗口"),
    SM_CLEANBOOT,                   TEXT("SM_CLEANBOOT"),                   TEXT("系统启动方式"),
    SM_CMONITORS,                   TEXT("SM_CMONITORS"),                   TEXT("监视器的数量"),
    SM_CMOUSEBUTTONS,               TEXT("SM_CMOUSEBUTTONS"),               TEXT("鼠标上的按钮数"),
    SM_CONVERTIBLESLATEMODE,        TEXT("SM_CONVERTIBLESLATEMODE"),        TEXT("笔记本电脑或平板电脑模式"),
    SM_CXBORDER,                    TEXT("SM_CXBORDER"),                    TEXT("窗口边框的宽度"),
    SM_CYBORDER,                    TEXT("SM_CYBORDER"),                    TEXT("窗口边框的高度"),
    SM_CXCURSOR,                    TEXT("SM_CXCURSOR"),                    TEXT("光标的宽度"),
    SM_CYCURSOR,                    TEXT("SM_CYCURSOR"),                    TEXT("光标的高度"),
    SM_CXDLGFRAME,                  TEXT("SM_CXDLGFRAME"),                  TEXT("同SM_CXFIXEDFRAME,有标题但不可调整大小的窗口边框的宽度"),
    SM_CYDLGFRAME,                  TEXT("SM_CYDLGFRAME"),                  TEXT("同SM_CYFIXEDFRAME,有标题但不可调整大小的窗口边框的高度"),
    SM_CXDOUBLECLK,                 TEXT("SM_CXDOUBLECLK"),                 TEXT("鼠标双击事件两次点击的X坐标不可以超过这个值"),
    SM_CYDOUBLECLK,                 TEXT("SM_CYDOUBLECLK"),                 TEXT("鼠标双击事件两次点击的Y坐标不可以超过这个值"),
    SM_CXDRAG,                      TEXT("SM_CXDRAG"),                      TEXT("拖动操作开始之前,鼠标指针可以移动的鼠标下方点的任意一侧的像素数"),
    SM_CYDRAG,                      TEXT("SM_CYDRAG"),                      TEXT("拖动操作开始之前,鼠标指针可以移动的鼠标下移点上方和下方的像素数"),
    SM_CXEDGE,                      TEXT("SM_CXEDGE"),                      TEXT("三维边框的宽度"),
    SM_CYEDGE,                      TEXT("SM_CYEDGE"),                      TEXT("三维边框的高度"),
    SM_CXFIXEDFRAME,                TEXT("SM_CXFIXEDFRAME"),                TEXT("同SM_CXDLGFRAME,有标题但不可调整大小的窗口边框的宽度"),
    SM_CYFIXEDFRAME,                TEXT("SM_CYFIXEDFRAME"),                TEXT("同SM_CYDLGFRAME,有标题但不可调整大小的窗口边框的高度"),
    SM_CXFOCUSBORDER,               TEXT("SM_CXFOCUSBORDER"),               TEXT("DrawFocusRect绘制的焦点矩形的左边缘和右边缘的宽度"),
    SM_CYFOCUSBORDER,               TEXT("SM_CYFOCUSBORDER"),               TEXT("DrawFocusRect绘制的焦点矩形的上边缘和下边缘的高度"),
    SM_CXFRAME,                     TEXT("SM_CXFRAME"),                     TEXT("同SM_CXSIZEFRAME,可调大小窗口边框的宽度"),
    SM_CYFRAME,                     TEXT("SM_CYFRAME"),                     TEXT("同SM_CYSIZEFRAME,可调大小窗口边框的高度"),
    SM_CXHSCROLL,                   TEXT("SM_CXHSCROLL"),                   TEXT("水平滚动条中箭头位图的宽度"),
    SM_CYHSCROLL,                   TEXT("SM_CYHSCROLL"),                   TEXT("水平滚动条中箭头位图的高度"),
    SM_CXVSCROLL,                   TEXT("SM_CXVSCROLL"),                   TEXT("垂直滚动条中箭头位图的宽度"),
    SM_CYVSCROLL,                   TEXT("SM_CYVSCROLL"),                   TEXT("垂直滚动条中箭头位图的高度"),
    SM_CXHTHUMB,                    TEXT("SM_CXHTHUMB"),                    TEXT("水平滚动条中滚动框(滑块)的高度"),
    SM_CYVTHUMB,                    TEXT("SM_CYVTHUMB"),                    TEXT("垂直滚动条中滚动框(滑块)的宽度"),
    SM_CXICON,                      TEXT("SM_CXICON"),                      TEXT("图标的默认宽度"),
    SM_CYICON,                      TEXT("SM_CYICON"),                      TEXT("图标的默认高度"),
    SM_CXICONSPACING,               TEXT("SM_CXICONSPACING"),               TEXT("大图标视图中项目的网格单元格宽度"),
    SM_CYICONSPACING,               TEXT("SM_CYICONSPACING"),               TEXT("大图标视图中项目的网格单元格高度"),
    SM_CXMAXIMIZED,                 TEXT("SM_CXMAXIMIZED"),                 TEXT("最大化顶层窗口的默认宽度"),
    SM_CYMAXIMIZED,                 TEXT("SM_CYMAXIMIZED"),                 TEXT("最大化顶层窗口的默认高度"),
    SM_CXMAXTRACK,                  TEXT("SM_CXMAXTRACK"),                  TEXT("具有标题和大小调整边框的窗口可以拖动的最大宽度"),
    SM_CYMAXTRACK,                  TEXT("SM_CYMAXTRACK"),                  TEXT("具有标题和大小调整边框的窗口可以拖动的最大高度"),
    SM_CXMENUCHECK,                 TEXT("SM_CXMENUCHECK"),                 TEXT("菜单项前面复选框位图的宽度"),
    SM_CYMENUCHECK,                 TEXT("SM_CYMENUCHECK"),                 TEXT("菜单项前面复选框位图的高度"),
    SM_CXMENUSIZE,                  TEXT("SM_CXMENUSIZE"),                  TEXT("菜单栏按钮的宽度"),
    SM_CYMENUSIZE,                  TEXT("SM_CYMENUSIZE"),                  TEXT("菜单栏按钮的高度"),
    SM_CXMIN,                       TEXT("SM_CXMIN"),                       TEXT("窗口的最小宽度"),
    SM_CYMIN,                       TEXT("SM_CYMIN"),                       TEXT("窗口的最小高度"),
    SM_CXMINIMIZED,                 TEXT("SM_CXMINIMIZED"),                 TEXT("最小化窗口的宽度"),
    SM_CYMINIMIZED,                 TEXT("SM_CYMINIMIZED"),                 TEXT("最小化窗口的高度"),
    SM_CXMINSPACING,                TEXT("SM_CXMINSPACING"),                TEXT("最小化窗口的网格单元宽度"),
    SM_CYMINSPACING,                TEXT("SM_CYMINSPACING"),                TEXT("最小化窗口的网格单元高度"),
    SM_CXMINTRACK,                  TEXT("SM_CXMINTRACK"),                  TEXT("窗口的最小拖动宽度,用户无法将窗口拖动到小于这些尺寸"),
    SM_CYMINTRACK,                  TEXT("SM_CYMINTRACK"),                  TEXT("窗口的最小拖动高度,用户无法将窗口拖动到小于这些尺寸"),
    SM_CXPADDEDBORDER,              TEXT("SM_CXPADDEDBORDER"),              TEXT("标题窗口的边框填充量"),
    SM_CXSIZE,                      TEXT("SM_CXSIZE"),                      TEXT("窗口标题或标题栏中按钮的宽度"),
    SM_CYSIZE,                      TEXT("SM_CYSIZE"),                      TEXT("窗口标题或标题栏中按钮的高度"),
    SM_CXSIZEFRAME,                 TEXT("SM_CXSIZEFRAME"),                 TEXT("同SM_CXFRAME,可调大小窗口边框的宽度"),
    SM_CYSIZEFRAME,                 TEXT("SM_CYSIZEFRAME"),                 TEXT("同SM_CYFRAME,可调大小窗口边框的厚度"),
    SM_CXSMICON,                    TEXT("SM_CXSMICON"),                    TEXT("小图标的建议宽度"),
    SM_CYSMICON,                    TEXT("SM_CYSMICON"),                    TEXT("小图标的建议高度"),
    SM_CXSMSIZE,                    TEXT("SM_CXSMSIZE"),                    TEXT("小标题按钮的宽度"),
    SM_CYSMSIZE,                    TEXT("SM_CYSMSIZE"),                    TEXT("小标题按钮的高度"),
    SM_CXVIRTUALSCREEN,             TEXT("SM_CXVIRTUALSCREEN"),             TEXT("虚拟屏幕的宽度"),
    SM_CYVIRTUALSCREEN,             TEXT("SM_CYVIRTUALSCREEN"),             TEXT("虚拟屏幕的高度"),
    SM_CYCAPTION,                   TEXT("SM_CYCAPTION"),                   TEXT("标题区域的高度"),
    SM_CYKANJIWINDOW,               TEXT("SM_CYKANJIWINDOW"),               TEXT("屏幕底部的日文汉字窗口的高度"),
    SM_CYMENU,                      TEXT("SM_CYMENU"),                      TEXT("单行菜单栏的高度"),
    SM_CYSMCAPTION,                 TEXT("SM_CYSMCAPTION"),                 TEXT("小标题的高度"),
    SM_DBCSENABLED,                 TEXT("SM_DBCSENABLED"),                 TEXT("User32.dll是否支持DBCS"),
    SM_DEBUG,                       TEXT("SM_DEBUG"),                       TEXT("是否安装了User.exe的调试版本"),
    SM_DIGITIZER,                   TEXT("SM_DIGITIZER"),                   TEXT("设备支持的数字转换器输入类型"),
    SM_IMMENABLED,                  TEXT("SM_IMMENABLED"),                  TEXT("是否启用了输入法管理器/输入法编辑器功能"),
    SM_MAXIMUMTOUCHES,              TEXT("SM_MAXIMUMTOUCHES"),              TEXT("系统中是否有数字化仪"),
    SM_MEDIACENTER,                 TEXT("SM_MEDIACENTER"),                 TEXT("当前操作系统是不是Windows XP Media Center"),
    SM_MENUDROPALIGNMENT,           TEXT("SM_MENUDROPALIGNMENT"),           TEXT("下拉菜单是否与相应的菜单栏项右对齐"),
    SM_MIDEASTENABLED,              TEXT("SM_MIDEASTENABLED"),              TEXT("系统是否启用希伯来语和阿拉伯语"),
    SM_MOUSEHORIZONTALWHEELPRESENT, TEXT("SM_MOUSEHORIZONTALWHEELPRESENT"), TEXT("是否安装了带有水平滚轮的鼠标"),
    SM_MOUSEPRESENT,                TEXT("SM_MOUSEPRESENT"),                TEXT("是否安装了鼠标"),
    SM_MOUSEWHEELPRESENT,           TEXT("SM_MOUSEWHEELPRESENT"),           TEXT("是否安装了带有垂直滚轮的鼠标"),
    SM_NETWORK,                     TEXT("SM_NETWORK"),                     TEXT("是否存在网络"),
    SM_PENWINDOWS,                  TEXT("SM_PENWINDOWS"),                  TEXT("是否安装了Microsoft Windows for Pen Computing扩展"),
    SM_REMOTECONTROL,               TEXT("SM_REMOTECONTROL"),               TEXT("当前终端服务器会话是否被远程控制"),
    SM_REMOTESESSION,               TEXT("SM_REMOTESESSION"),               TEXT("调用进程是否与终端服务客户机会话关联"),
    SM_SAMEDISPLAYFORMAT,           TEXT("SM_SAMEDISPLAYFORMAT"),           TEXT("所有显示器的颜色格式是否相同"),
    SM_SECURE,                      TEXT("SM_SECURE"),                      TEXT("始终返回0"),
    SM_SERVERR2,                    TEXT("SM_SERVERR2"),                    TEXT("系统是否是Windows Server 2003 R2"),
    SM_SHOWSOUNDS,                  TEXT("SM_SHOWSOUNDS"),                  TEXT("用户是否要求应用程序在其他情况下以可视方式呈现信息"),
    SM_SHUTTINGDOWN,                TEXT("SM_SHUTTINGDOWN"),                TEXT("当前会话是否正在关闭"),
    SM_SLOWMACHINE,                 TEXT("SM_SLOWMACHINE"),                 TEXT("计算机是否具有低端(慢速)处理器"),
    SM_STARTER,                     TEXT("SM_STARTER"),                     TEXT("当前操作系统版本"),
    SM_SWAPBUTTON,                  TEXT("SM_SWAPBUTTON"),                  TEXT("鼠标左键和右键的功能是否互换了"),
    SM_SYSTEMDOCKED,                TEXT("SM_SYSTEMDOCKED"),                TEXT("停靠模式的状态"),
    SM_TABLETPC,                    TEXT("SM_TABLETPC"),                    TEXT("是否启动了Tablet PC输入服务"),
    SM_XVIRTUALSCREEN,              TEXT("SM_XVIRTUALSCREEN"),              TEXT("虚拟屏幕左侧的坐标"),
    SM_YVIRTUALSCREEN,              TEXT("SM_YVIRTUALSCREEN"),              TEXT("虚拟屏幕顶部的坐标")
};
  • SystemMetrics.cpp
#include <Windows.h>
#include <tchar.h>
#include<strsafe.h>
#include "Metrics.h"

const int NUMLINES = sizeof(METRICS) / sizeof(METRICS[0]); //写入文本的行数
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	WNDCLASSEX wndclass;                            // RegisterClassEx函数用的WNDCLASSEX结构
	TCHAR szClassName[] = TEXT("MyWindow");         // RegisterClassEx函数注册的窗口类的名称
	TCHAR szAppName[] = TEXT("GetSystemMetrics");   // 窗口标题
	HWND hwnd;                                      // CreateWindowEx函数创建的窗口的句柄
	MSG msg;                                        // 消息循环所用的消息结构体

	wndclass.cbSize = sizeof(WNDCLASSEX);
	wndclass.style = CS_HREDRAW | CS_VREDRAW;
	wndclass.lpfnWndProc = WindowProc;
	wndclass.cbClsExtra = 0;
	wndclass.cbWndExtra = 0;
	wndclass.hInstance = hInstance;
	wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndclass.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);    // 窗口背景使用标准系统颜色
	wndclass.lpszMenuName = NULL;
	wndclass.lpszClassName = szClassName;
	wndclass.hIconSm = NULL;
	::RegisterClassEx(&wndclass);
	hwnd = ::CreateWindowEx(0, szClassName, szAppName, WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
	::ShowWindow(hwnd, nCmdShow);
	::UpdateWindow(hwnd);
	while (::GetMessage(&msg, NULL, 0, 0) != 0)
	{
		::TranslateMessage(&msg);
		::DispatchMessage(&msg);
	}
	return msg.wParam;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	HDC hdc;
	PAINTSTRUCT ps;
	static SIZE size = { 0 };
	TCHAR szBuf[10];
	int y;
	if (uMsg == WM_CREATE)
	{
		//查字符串高度和宽度
		hdc = GetDC(hwnd);
		GetTextExtentPoint32(hdc,METRICS[0].m_pLabel,_tcslen(METRICS[0].m_pLabel),&size);
		ReleaseDC(hwnd,hdc);
		return 0;
	}
	else if (uMsg == WM_PAINT)
	{
		hdc = ::BeginPaint(hwnd, &ps);

		int preSetBkMode =  SetBkMode(hdc,TRANSPARENT);
		int preSetTextColor =  SetTextColor(hdc, RGB(0, 0, 255));
		HGDIOBJ preSetFont = SelectObject(hdc,GetStockObject(OEM_FIXED_FONT)); //直接上,系统默认字体

		for (int i = 0; i < NUMLINES; i++)
		{
			y = size.cy * i; //计算行间距
			::TextOut(hdc, 0, y, METRICS[i].m_pLabel, _tcslen(METRICS[i].m_pLabel));
			::TextOut(hdc, 240, y, METRICS[i].m_pDesc, _tcslen(METRICS[i].m_pDesc));
			::TextOut(hdc, 760, y, szBuf, wsprintf(szBuf, TEXT("%d"), ::GetSystemMetrics(METRICS[i].m_nIndex)));
			::SetTextCharacterExtra(hdc, 0);
		}

		SetBkMode(hdc, preSetBkMode);
		SetTextColor(hdc, preSetTextColor);
		SelectObject(hdc, preSetFont);

		::EndPaint(hwnd, &ps);
		return 0;
	}
	else if (uMsg == WM_DESTROY)
	{
		::PostQuitMessage(0);
		return 0;
	}
	return ::DefWindowProc(hwnd, uMsg, wParam, lParam);
}

在这里插入图片描述

重新编译运行,效果稍微好看了一些,但是备用字体比较少,接下来我们学习创建自己喜欢的逻辑字体。

用CreateFont函数创建具有指定特征的逻辑字体︰

HFONT CreateFont(
_In_ int nHeight, //字符高度,设置为0表示使用默认的字符高度
_In_ int nWidth, //字符宽度,通常设置为0,表示根据字符的高度来选择合适的字体
_In_ int nEscapement,//字符串的倾斜角度,以0.1度为单位,没有特殊需要一般设置为0
_In_ int nOrientation,//单个字符的倾斜角度,以0.1度为单位,通常这个字段会被忽略
_In_ int fnWeight,//字体粗细,如果设置为0,则使用默认粗细
_In_ DWORD fdwltalic, //是否斜体,设置为TRUE表示使用斜体
_In_ DWORD fdwUnderline, //是否有下划线,设置为TRUE表示使用下划线
_In_ DWORD fdwStrikeOut, //是否有删除线,设置为TRUE表示使用删除线
_In_ DWORD fdwCharSet,//字符集
_In_DWORD fdwOutputPrecision,//指定Windows通过字体的大小特征来匹配真实字体的方式
_In_ DWORD fdwClipPrecision,//指定裁剪方式,也就是当字符在显示区域以外时,如何只显示部分字符
_In_ DWORD fdwQuality, //指定如何将逻辑字体属性与实际物理字体属性匹配
_In_ DWORD fdwPitchAndFamily,//指定字符间距和字体系列
_In_ LPCTSTR lpszFace //字体名称,字符串长度不得超过LF_FACESIZE(32)个字符
); 
  • 前两个参数nHeight和nWidth均是逻辑单位。

  • 第5个参数fnWeight指定字体的粗细,字体的粗细在0~1000,400是正常粗细,700是粗体的,如果该参数设置为0,则使用默认粗细。wingdi.h头文件中定义了常量用于表示字体粗细,如下表所示。

    宏常量
    FW_DONTCARE 0
    FW_THIN 100
    FW_EXTRALIGHT 200
    FW_ULTRALIGHT 200
    FW_LIGHT 300
    FW_NORMAL 400
    FW_REGULAR 400
    FW_MEDIUM 500
    FW_SEMIBOLD 600
    FW_DEMIBOLD 600
    FW_EXTRABOLD 800
    FW_ULTRABOLD 800
    FW_HEAVY 900
    FW_BLACK 900
  • 第9个参数fdwCharSet指定字体的字符集,OEM_CHARSET表示OEM字符集,DEFAULT_CHARSET表示基于当前系统区域的字符集。

    一些可用的预定义值如下表所示。

    宏常量 含义
    ANSI_CHARSET 0 ANSI(美国、西欧)
    GB2312_CHARSET 134 简体中文
    CHINESEBIG5_CHARSET 136 繁体中文
    DEFAULT_CHARSET 1 默认字符集
    OEM_CHARSET 255 原始设备制造字符集
    SYMBOL_CHARSET 2 标准符号
    EASTEUROPE_CHARSET 238 东欧字符集
    GREEK_CHARSET 161 希腊语字符集
    MAC_CHARSET 77 Apple Macintosh
    RUSSIAN_CHARSET 204 俄语字符集
  • 第10个参数 fdwOutputPrecision指定输出精度,也就是指定实际获得的字体与所请求字体的高度、宽度、字符方向、转义、间距和字体类型匹配的程度,一般不使用这个参数。

  • 第12个参数fdwQuality指定如何将逻辑字体属性与实际物理字体属性匹配。值的含义如下表所示。

    宏常量 含义
    ANTIALIASED_QUALIT 如果字体支持,并且字体大小不太小或太大,则字体是抗锯齿的或平滑的。
    CLEARTYPE_QUALITY 使用Clearlype抗锯齿方法显示文本
    DEFAULT QUALITY 字体的外观并不重要
    DRAFT_QUALITY 对于GDI光栅字体后用缩放,这意味着可以使用更多的字体大小,但质量可能更低
    NONANTIALIASED_QUALITY 字体不会消除锯齿
    PROOF_QUALITY 字体的字符质量比逻辑字体属性的精确匹配更重要。对于GDI光栅字体,禁用缩放,并选择最接近大小的字体,虽然使用PROOF_QUALITY时所选字体大小可能无法精确映射,但字体质量高,外观无变形
  • 第13个参数fdwPitchAndFamily指定字符间距和字体系列,最低两个位表示该字体是否是一个等宽字体(所有字符的宽度都相同)或是一个变宽字体。wingdi.h头文件中定义了下表所列的常量。

宏常量 含义
DEFAULT PITCHO 默认间距
FIXED PITCH1 等宽
VARIABLE_PITCH2 变宽

4~7位指定字体系列,值的含义如下表所示。

宏常量 含义
FF_DONTCARE(0<<4) 使用默认字体
FF_ROMAN(1<<4) 具有可变笔画宽度和衬线的字体
FF _SWISS(2<<4) 笔画宽度可变且不带衬线的字体
FF_MODERN(3<<4) 具有恒定笔画宽度、带或不带衬线的字体
FF_SCRIPT(4<<4) 字体看起来像手写,草书就是例子
FF_DECORATIVE(5<<4) 古英语

CreateFont函数虽然参数比较多,但是除了字符高度、字符集和字体名称以外,其他参数通常均可以指定为0。 HFONT是逻辑字体句柄类型,如果函数执行成功,则返回值是所创建逻辑字体的句柄;如果函数执行失败,则返回值为NULL.

当不再需要创建字体时,需要调用DeleteObject函数将其删除。DeleteObject函数用于删除创建的逻辑画笔、逻辑画刷、逻辑字体、位图、区域等,释放与对象相关联的所有系统资源,对象删除后,指定的句柄不再有效。

BOOL DeleteObject(
    _In_HGDIOBj hObject //GDI对象句柄 
     );

我们在SystemMetrics程序的3个TextOut前加上如下语句∶

#include <Windows.h>
#include <tchar.h>
#include<strsafe.h>
#include "Metrics.h"

const int NUMLINES = sizeof(METRICS) / sizeof(METRICS[0]); //写入文本的行数
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	WNDCLASSEX wndclass;                            // RegisterClassEx函数用的WNDCLASSEX结构
	TCHAR szClassName[] = TEXT("MyWindow");         // RegisterClassEx函数注册的窗口类的名称
	TCHAR szAppName[] = TEXT("GetSystemMetrics");   // 窗口标题
	HWND hwnd;                                      // CreateWindowEx函数创建的窗口的句柄
	MSG msg;                                        // 消息循环所用的消息结构体

	wndclass.cbSize = sizeof(WNDCLASSEX);
	wndclass.style = CS_HREDRAW | CS_VREDRAW;
	wndclass.lpfnWndProc = WindowProc;
	wndclass.cbClsExtra = 0;
	wndclass.cbWndExtra = 0;
	wndclass.hInstance = hInstance;
	wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndclass.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);    // 窗口背景使用标准系统颜色
	wndclass.lpszMenuName = NULL;
	wndclass.lpszClassName = szClassName;
	wndclass.hIconSm = NULL;
	::RegisterClassEx(&wndclass);
	hwnd = ::CreateWindowEx(0, szClassName, szAppName, WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
	::ShowWindow(hwnd, nCmdShow);
	::UpdateWindow(hwnd);
	while (::GetMessage(&msg, NULL, 0, 0) != 0)
	{
		::TranslateMessage(&msg);
		::DispatchMessage(&msg);
	}
	return msg.wParam;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	HDC hdc;
	PAINTSTRUCT ps;
	static SIZE size = { 0 };
	TCHAR szBuf[10];
	int y;
	if (uMsg == WM_CREATE)
	{
		//查字符串高度和宽度
		hdc = GetDC(hwnd);
		GetTextExtentPoint32(hdc,METRICS[0].m_pLabel,_tcslen(METRICS[0].m_pLabel),&size);
		ReleaseDC(hwnd,hdc);
		return 0;
	}
	else if (uMsg == WM_PAINT)
	{
		hdc = ::BeginPaint(hwnd, &ps);

		int preSetBkMode =  SetBkMode(hdc,TRANSPARENT);
		int preSetTextColor =  SetTextColor(hdc, RGB(0, 0, 255));
		HFONT settingFont =  CreateFont(12, 0, 0, 0, 0, 0, 0, 0, GB2312_CHARSET, 0, 0, 0, 0, TEXT("宋体"));
		HGDIOBJ preSetFont = SelectObject(hdc, settingFont); //直接上,系统中默认装好的宋体

		for (int i = 0; i < NUMLINES; i++)
		{
			y = size.cy * i; //计算行间距
			::TextOut(hdc, 0, y, METRICS[i].m_pLabel, _tcslen(METRICS[i].m_pLabel));
			::TextOut(hdc, 240, y, METRICS[i].m_pDesc, _tcslen(METRICS[i].m_pDesc));
			::TextOut(hdc, 760, y, szBuf, wsprintf(szBuf, TEXT("%d"), ::GetSystemMetrics(METRICS[i].m_nIndex)));
			::SetTextCharacterExtra(hdc, 0);
		}

		SetBkMode(hdc, preSetBkMode);
		SetTextColor(hdc, preSetTextColor);
		SelectObject(hdc, preSetFont);

		::EndPaint(hwnd, &ps);
		return 0;
	}
	else if (uMsg == WM_DESTROY)
	{
		::PostQuitMessage(0);
		return 0;
	}
	return ::DefWindowProc(hwnd, uMsg, wParam, lParam);
}

在这里插入图片描述

可以看到,字体美观了不少。

CreateFontIndirect函数的功能与CreateFont函数完全相同,不同的是CreateFontIndirect函数只需要一个LOGFONT结构参数∶

HFONT CreateFontlndirect
( 
    _In_ const LOGFONT* lplf
);

LOGFONT结构的字段与CreateFont函数的14个参数是一一对应的:

typedef struct tagLOGFONT 
{
	LONG lfHeight;
	LONG lfWidth;
	LONG lfEscapement;
	LONG lfOrientation;
    LONG lfWeight;
    BYTE lfltalic;
	BYTE lfUnderline;
	BYTE lfStrikeOut;
	BYTE lfCharSet;
	BYTE lfOutPrecision;
	BYTE lIfClipPrecision;
	BYTE lfQuality;
	BYTE lfPitchAndFamily;
	TCHAR lfFaceName[LF_FACESIZE];
}LOGFONT,*PLOGFONT;

EnumFontFamiliesEx函数可以根据提供的LOGFONT结构枚举系统中的字体∶

int EnumFontFamiliesEx(
_In_ HDC hdc, //设备环境句柄
_In_ LPLOGFONT lpLogfont, //指定字体特征的LOGFONT结构
_In_ FONTENUMPROC lpEnumFontFamExProc,//回调函数
_In_ LPARAM lParam, //传递给回调函数的参数
DWORD dwFlags //未使用,必须为0
);
  • 参数lpLogfont是指定字体特征的LOGFONT结构,函数只使用lfCharSet lfFaceName和lfPitchAndFamily共3个字段,如下进行说明:

    • lfCharSet如果设置为DEFAULT_CHARSET,函数将枚举所有字符集中指定名称的字体。如果有两种字体同名,则只枚举一种字体﹔如果设置为其他有效的字符集,则函数仅枚举指定字符集中的字体 简单的说就是通过字符集过滤字体。
    • lfFaceName如果设置为空字符串,则函数将在所有字体名称中枚举一种字体;如果设置为有效的字体名称,则函数将枚举具有指定名称的字体。 简单的说就是通过字体名称进行过滤
    • lfPitchAndFamily必须设置为0

    也就是说函数是基于字体名称或字符集或两者共同来枚举字体。

    参数lpEnumFontFamExProc是EnumFontFamiliesEx函数的回调函数,对于枚举到的每个字体,都会调用一次这个回调函数。回调函数的格式如下∶

    int CALLBACK EnumFontFamExProc(
    const LOGFONT *lpelfe, //有关字体逻辑属性信息的LOGFONT结构
    const TEXTMETRIC *lpntme,//有关字体物理属性信息的TEXTMETRIC结构
    DWORD FontType, //字体类型,例如DEVICE_FONTTYPE RASTER_ FONTTYPE
        			// TRUETYPE_FONTTYPE
    LPARAM lParam //EnumFontFamiliesEx函数的lParam参数
    );
    

    如果需要继续枚举,则回调函数应返回非雾﹔如果需要停止枚举,则返回0

    EnumFontFamiliesEx函数的返回值是最后一次回调函数调用返回的值。这样我们很轻易的遍历得到操作系统所有可用的字体了。上来就有。

    int CALLBACK EnumFontFamExProc(const LOGFONT* lpelfe,const TEXTMETRIC* lpntme,DWORD FontType, LPARAM lParam )
    {
    	TCHAR szBuf[MAX_PATH] = { 0 };
    	StringCchCopy(szBuf, _countof(szBuf), lpelfe->lfFaceName);
    
    	//TODO: Show In UI ? Or Save To vector....
    
    	return 1;
    }
    
    int bizFun
    {    
     	//查全部的字体
    	EnumFontFamiliesEx(hdc,NULL, EnumFontFamExProc,NULL,NULL);  
    }
    

获取字体的度量值

GetTextMetrics函数可以获取当前选定字体的度量值,该函数通常用于英文字体︰

BOOL GetTextMetrics(
_In_	HDC	hdc, //设备环境句柄
_out_	LPTEXTMETRIC lptm //在这个TEXTMETRIC结构中返回字体度量值,逻辑单位
); 

函数执行成功,在lptm指定的TEXTMETRIC结构中返回字体的信息。在wingdi.h头文件中TEXTMETRIC结构的定义如下∶

typedef struct tagTEXTMETRIC 
{
    LONG tmHeight; //字符高度(等于tmAscent + tmDescent)
    LONG tmAscent; //字符基线以上的高度
    LONG tmDescent; //字符基线以下的高度
    LONG tmInternalLeading; //字符高度范围内的一部分顶部空间,可用于重音符号和其他音调符号
    LONG tmExternalLeading; //在行之间添加的额外高度空间
    LONG tmAveCharWidth; //字体中字符(小写字母)的平均宽度(通常定义为字母x的宽度)
    LONG tmMaxCharWidth; //字体中最宽字符的宽度
    LONG tmWeight; //字体的粗细
    LONG tmOverhang; //添加到某些合成字体中的额外宽度
    LONG tmDigitizedAspectx;
    LONG tmDigitizedAspectY;
    TCHAR tmFirstChar; //字体中定义的第一个字符
    TCHAR tmLastChar; //字体中定义的最后一个字符
    TCHAR tmDefaultChar;//字体中所没有字符的替代字符
    TCHAR tmBreakChar; //单词之间的分隔字符,通常是空格
    BYTE tmltalic; //字体为斜体时非零
    BYTE tmUnderlined; //字体有下划线时非零
    BYTE tmStruckOut; //字体有删除线时非雩
    BYTE tmPitchAndFamily; //字体间距(低4位)和字体系列(高4位)
    BYTE tmCharSet; //字体的字符集
}TEXTMETRIC,*PTEXTMETRIC;

tmOverhang字段表示添加到某些合成字体中(例如粗体或斜体)的每个字符串的额外宽度,例如,GDI通过扩展每个字符的间距并用偏移量值重写字符串,使字符串变为粗体。

TEXTMETRIC结构虽然字段比较多,但是常用的也就是前面几个。字段tmHeight tmAscent tmDescent和tmInternalLeading之间的关系如下图所示。在下图中,字段tmExternalLeading没有表示出来。

在这里插入图片描述

对于等宽字体,大写字母宽度等于字符的平均宽度﹔对于变宽字体,大写字母宽度通常是字符平均宽度的1.5倍。对于变宽字体,
TEXTMETRIC结构中的tmPitchAndFamily字段的低4位为1,对于等宽字体则为0。计算大写字母宽度的方式为

cxCaps = (tm.tmPitchAndFamily & 1 ? 3: 2) cxChar / 2;

好像是GDI API 不是很人性化,后面微软推出了GDI Plus 已增强其使用。笔者这里给出一个函数,改函数作用为查操作系统所有的字体。供读者进一步进行学习。

BOOL CFontUtil::GetInstallFontFamilyName(vector<CString> vecFontFamilyName)
{
	ULONG_PTR gdiplusToken; //GDI+ 进入环境时得到的TOKEN
	Gdiplus::InstalledFontCollection* pInstalledFontCollection = NULL;
	Gdiplus::GdiplusStartupInput gdiplusStartupInput;
	Gdiplus::FontFamily* pFontFamily = NULL;
	int nNumFound = 0;
	int fontFamilyCount = 0;
	vecFontFamilyName.clear();
	Gdiplus::Status gdiStatus;

	//初始化 GDI+ 环境

	gdiStatus = Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

	if (gdiStatus != Gdiplus::Ok)
	{
		return FALSE;
	}



	//创建已安装字体集合对象
	pInstalledFontCollection = new Gdiplus::InstalledFontCollection;



	//查在此字体对象中已经安装的字体系列总数
	fontFamilyCount = pInstalledFontCollection->GetFamilyCount();
	pFontFamily = new Gdiplus::FontFamily[fontFamilyCount];



	pInstalledFontCollection->GetFamilies(fontFamilyCount, pFontFamily, &nNumFound);

	// if(nNumFound == fontFamilyCount) 必然相等

	for (size_t i = 0; i < fontFamilyCount; i++)
	{
		WCHAR wszFontName[32] = { 0 };
		pFontFamily[i].GetFamilyName(wszFontName);
		vecFontFamilyName.push_back(wszFontName);
	}



	//释放资源
	if (pFontFamily)
	{
		delete[] pFontFamily; pFontFamily = NULL;
	}
	if (pInstalledFontCollection)
	{
		delete pInstalledFontCollection; pInstalledFontCollection = NULL;
	}


	//退出GDI+ 环境
	Gdiplus::GdiplusShutdown(gdiplusToken);

	return TRUE;

}