定制需求改程序显示字体,这些API得掌握。
选择字体
系统提供了6种备用字体,前面说过GetStockObject函数用于获取备用(或者说库存)画笔、画刷、字体等的句柄”,获取字体句柄以后,可以通过调用SelectObject函数把字体选入DC中,以后通过GDI函数进行文本绘制就会使用新的DC属性。一些备用字体如下表所示。
宏含义 | 值 |
---|---|
等宽系统字体 | |
变宽系统字体 | |
设备默认字体 | |
(原始设备制造商)等宽字体 | |
系统字体,默认情况下使用系统字体绘制菜单、对话框控件和文本 | |
等宽系统字体 |
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头文件中定义了下表所列的常量。
宏常量 | 含义 |
---|---|
默认间距 | |
等宽 | |
变宽 |
4~7位指定字体系列,值的含义如下表所示。
宏常量 | 含义 |
---|---|
使用默认字体 | |
具有可变笔画宽度和衬线的字体 | |
笔画宽度可变且不带衬线的字体 | |
具有恒定笔画宽度、带或不带衬线的字体 | |
字体看起来像手写,草书就是例子 | |
古英语 |
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; }