简介:本文系统解析GDI字体度量结构TEXTMETRIC的20个成员含义、逻辑单位与映射模式的关系,以及tmPitchAndFamily的位编码规则。读者将掌握通过GetTextMetrics获取字体度量、计算字符宽度与行高、判定字体类型的方法,并了解其与高级字体度量结构的关联,从而在文本布局与渲染中实现精确控制。
在Windows图形设备接口(GDI)的字体体系中,TEXTMETRIC 是一个基础且关键的数据结构。它承载了当前设备上下文中被选入字体的物理度量信息,是开发者进行字符宽度计算、行高规划、字体类型判定的直接数据来源。无论是使用原生 SDK 还是 MFC 框架,只要涉及文本排版与渲染,几乎都绕不开这个仅有 20 个成员的结构体。
本文从结构定义出发,逐层剖析各字段的语义、单位约定与位编码规则,并结合典型使用场景给出可落地的实践方法。
TEXTMETRIC 定义于 wingdi.h 头文件,用于描述物理字体的基本度量信息。理解它的第一个前提是:所有尺寸均以逻辑单元表示,其实际物理含义取决于目标设备上下文当前的映射模式。在 MM_TEXT 映射模式下,逻辑单位即像素;而在 MM_LOMETRIC、MM_HIMETRIC 等映射模式下,同一数值对应的物理尺寸截然不同。因此,在跨设备、跨分辨率场景中直接硬编码度量数值是不安全的,应始终基于当前 DC 的映射模式进行换算。
结构体共包含 20 个成员,可归纳为四组:纵向度量、横向度量、字符集信息与样式标志、间距与家族编码。
纵向度量的核心是三个相互约束的字段:
在此之上还有两个与行间距相关的字段:
由此可得两个常用的派生量:
// 字符单元高度(不含外部行距)int cyChar = tm.tmHeight + tm.tmExternalLeading;// 若仅需字形本体高度int cyGlyph = tm.tmHeight;
需要强调的是,tmHeight 已包含 tmInternalLeading,因此行高计算中不应再加一次,这是实践中常见的错误来源。
横向度量决定文本布局的横向推进量:
对于固定间距字体,tmAveCharWidth 与 tmMaxCharWidth 通常相等;对于可变间距字体,二者差异明显。开发者不应假设二者关系,而应通过实际测量验证。
三个布尔型字段以字节形式表达样式信息:
这些标志反映的是字体本身的属性,而非通过绘图状态临时施加的效果。在需要判断”当前字体是否原生支持斜体”时,应读取此字段,而不是仅依据创建字体时传入的请求参数——因为 GDI 可能通过合成方式模拟不存在的样式。
tmPitchAndFamily 是整个结构中最需要谨慎处理的字段,它用同一个字节承载了两类完全不同的信息:低 4 位描述间距与技术属性,高 4 位描述字体系列。
低 4 位的技术标志:
| 常量 | 含义 |
|---|---|
| TMPF_FIXED_PITCH | 注意其语义与名称相反:置位表示可变间距,清零才表示固定间距 |
| TMPF_VECTOR | 向量字体 |
| TMPF_TRUETYPE | TrueType 字体 |
| TMPF_DEVICE | 设备字体 |
其中 TMPF_FIXED_PITCH 的反直觉语义是历史遗留问题,必须通过位测试而非字面理解来判断。正确的判定方式为:
BOOL bFixedPitch = !(tm.tmPitchAndFamily & TMPF_FIXED_PITCH);BOOL bTrueType = (tm.tmPitchAndFamily & TMPF_TRUETYPE) != 0;
高 4 位的家族信息: 通过 0xF0 掩码提取,取值对应 FF_DONTCARE、FF_ROMAN、FF_SWISS、FF_MODERN、FF_SCRIPT、FF_DECORATIVE 等系列常量。应用程序应将其视为”建议性分类”,用于字体选择时的偏好匹配,而不应作为严格保证。
官方文档特别提示:应用程序应仔细测试这些位所编码的品质,不应进行任意假设。不同字体驱动对这些位的填充可能存在差异,尤其在设备字体与合成字体场景下。
在窗口创建阶段获取度量并缓存,是常见且高效的做法。以下示例在 WM_CREATE 中完成初始化:
case WM_CREATE: {HDC hdc = GetDC(hwnd);TEXTMETRIC tm;GetTextMetrics(hdc, &tm);// 行高:包含外部行距int cyChar = tm.tmHeight + tm.tmExternalLeading;// 平均字符宽度int cxChar = tm.tmAveCharWidth;// 大写字母宽度估算:固定间距字体下按平均宽度与最大宽度推导int cxCaps = (tm.tmPitchAndFamily & TMPF_FIXED_PITCH)? (cxChar * 3) / 2: cxChar;ReleaseDC(hwnd, hdc);// 将 cyChar、cxChar、cxCaps 缓存到窗口实例数据中return 0;}
此处 cxCaps 的经验式推导源于:大写字母通常比小写字母宽,在可变间距字体中约为平均宽度的 1.5 倍。这一估算适用于快速布局,若需精确值,应使用 GetCharWidth 或 GetTextExtentPoint32 逐字符测量。
TEXTMETRIC 提供的是基础度量。当需要更丰富的排版信息——如字体的全名、版权信息、各字符的精确宽度数组、下划线与删除线的位置等——可使用 OUTLINETEXTMETRIC 结构。后者在 TrueType 字体下可提供更完整的字形级数据,并通过 GetOutlineTextMetrics 获取。两者的关系是层次化的:TEXTMETRIC 满足常规布局需求,OUTLINETEXTMETRIC 面向需要精细排版控制的场景。在字体管理系统的整体架构中,它们共同构成从基础度量到扩展度量的数据支撑。
掌握 TEXTMETRIC 的字段语义与位编码细节,是构建可靠文本布局逻辑的基础。它既是一个数据容器,也是理解 GDI 字体渲染决策链路的入口。