ReactOS 图形系统分析(33):字体子系统 --- font.c
1. 概述
font.c(file:///d:/reactos/win32ss/gdi/ntgdi/font.c)(约 1500 行)是 win32k.sys 中 ntgdi 的字体管理子系统 ,集中了 GDI 字体相关的 17 个 NtGdi* 系统服务入口,以及一批 Gre*/Int*/HfontCreate 内部函数。它负责四类职责:
- 字体创建 :
CreateFontIndirectW 的用户态调用最终落到 GreCreateFontIndirectW → HfontCreate,分配并初始化逻辑字体对象 TEXTOBJ(即 LFONT),真正的字形实现(FreeType FT_Face)延迟到字体被 DC 选中时(RealizeFontInit)才完成。
- 字体资源加载/卸载 :
AddFontResourceW / RemoveFontResourceW(文件字体)与 AddFontMemResourceEx / RemoveFontMemResourceEx(内存字体)的系统服务实现,负责把字体文件/缓冲挂入全局字体链表 g_FontListHead 或进程私有字体链表,并可同步写注册表。
- 字符度量查询 :
GetCharWidth / GetCharABCWidths / GetGlyphIndices / GetKerningPairs / GetOutlineTextMetrics / GetFontUnicodeRanges 等度量类 API 的内核入口。
- 字形轮廓/字体数据提取 :
GetGlyphOutline(字形轮廓或位图)、GetFontData(sfnt 表原始字节)、GetFontResourceInfoInternalW(字体文件信息)等。
值得注意的是:font.c 本身不包含任何 FreeType 调用 。它扮演的是"管理/查询层",所有 FT_* 操作(FT_Load_Glyph、FT_Get_Sfnt_Table、FT_Get_Char_Index......)都在 freetype.c(file:///d:/reactos/win32ss/gdi/ntgdi/freetype.c)(约 7800 行,详见《分析_16》)中实现;font.c 通过 ftGdiGetGlyphOutline、ftGdiGetFontData、ftGdiGetKerningPairs 等 ft 前缀函数桥接到 FreeType。这种"系统服务层 + 引擎实现层"的划分与 Windows NT 的 win32k.sys(syscall 层)和 ATMFD/字体引擎的职责划分一致。
1.1 ASCII 架构图
用户态 (user32.dll / gdi32full.dll)
CreateFontIndirectW AddFontResourceW GetGlyphOutline GetCharWidthW
│ │ │ │
▼ ▼ ▼ ▼
NtGdiHfontCreate NtGdiAddFontResourceW NtGdiGetGlyphOutline NtGdiGetCharWidthW
│ │ │ │
┌────┴────────────────────┴──────────────────┴────────────────┴─────────────┐
│ font.c (本文分析对象) │
│ ┌──────────────────┐ ┌───────────────────┐ ┌──────────────────────────┐ │
│ │ 字体创建 │ │ 资源加载/卸载 │ │ 度量/字形查询 │ │
│ │ GreCreateFont │ │ NtGdiAdd/Remove │ │ NtGdiGetCharWidthW │ │
│ │ IndirectW │ │ FontResource(W) │ │ NtGdiGetGlyphOutline │ │
│ │ → HfontCreate │ │ NtGdiAdd/Remove │ │ NtGdiGetFontData │ │
│ │ → RealizeFontInit│ │ FontMemResourceEx │ │ NtGdiGetKerningPairs │ │
│ └────────┬─────────┘ └─────────┬─────────┘ └───────────┬──────────────┘ │
└───────────┼──────────────────────┼────────────────────────┼────────────────┘
▼ ▼ ▼
TextIntCreateFontIndirect IntGdiAddFontResourceEx ftGdiGetGlyphOutline
TextIntRealizeFont IntGdiAddFontMemResource ftGdiGetFontData
(text.h 声明的内部接口) (注册表 g_FontRegPath) ftGdiGetKerningPairs
│ │ │
┌───────────┴──────────────────────┴────────────────────────┴────────────────┐
│ freetype.c(FreeType 引擎适配层) │
│ FT_Init_FreeType / FT_New_Memory_Face / FT_Load_Glyph / FT_Get_Sfnt_Table │
│ g_FontListHead(全局字体) + FONT_CACHE_ENTRY 缓存 + FontLink 字体链接 │
└─────────────────────────────────────────────────────────────────────────────┘
1.2 设计动机(为什么要这个文件)
- 系统调用隔离 :win32k 中所有可被用户态直接调用的 GDI 字体功能必须以
NtGdi* 系统服务形式导出,并遵循统一的"SEH 探测用户缓冲 → 拷贝到内核缓冲 → 锁 DC/锁文本对象 → 引擎查询 → 拷回结果"安全模式。font.c 集中承载这些入口,避免每个功能散落各处。
- 延迟实现(deferred realization) :Windows 的
CreateFontIndirect 只创建"逻辑字体描述",并不立即解析字体文件。font.c 的 HfontCreate 仅保存 ENUMLOGFONTEXDVW(LOGFONTW + 设计向量)并分配句柄;到 DC 选中字体、真正需要度量/绘制时,RealizeFontInit 才调用 TextIntRealizeFont 完成字体匹配(私有字体链 → 全局字体链)、字形参数(粗细/倾斜/下划线合成请求)设定。这种设计让"创建"廉价、"实现"按需。
- 句柄对象管理 :
LFONT_AllocFontWithHandle 走 GDI 句柄表(GDIOBJ_AllocObjWithHandle),保证字体句柄参与进程句柄表引用计数,进程退出时能统一回收;GdiHandleTable->cfPublic[] 缓存公共字体信息(NtGdiGetRealizationInfo 会写回实现信息)。
- 桥接 FreeType :ReactOS 的字体引擎基于 FreeType,但 FreeType 不是线程安全的,需要全局互斥(
g_FreeTypeLock)。font.c 把"锁管理"(TEXTOBJ_LockText 的 push lock)和"引擎锁"(freetype.c 的 IntLockFreeType)分离,前者保护逻辑字体对象,后者保护 FT 库/字体链表的并发访问。
2. 核心数据结构详解
2.1 TEXTOBJ / LFONT(text.h,逻辑字体对象)
text.h(file:///d:/reactos/win32ss/gdi/ntgdi/text.h) 第 56~72 行定义:
#define TEXTOBJECT_INIT 0x00010000
/* GDI logical font object */
typedef struct _LFONT
{
/* Header for all gdi objects in the handle table.
Do not (re)move this. */
BASEOBJECT BaseObject;
LFTYPE lft;
FLONG fl;
FONTOBJ *Font;
WCHAR TextFace[LF_FACESIZE];
DWORD dwOffsetEndArray;
// Fixed:
ENUMLOGFONTEXDVW logfont;
EX_PUSH_LOCK lock;
} TEXTOBJ, *PTEXTOBJ, LFONT, *PLFONT;
| 字段 |
类型 |
说明 |
BaseObject |
BASEOBJECT |
GDI 对象头,必须位于首位且不可移动 ;包含句柄表索引 hHmgr、对象类型、引用计数等,是 LFONT_AllocFontWithHandle()/GDIOBJ_ReferenceObjectByHandle 等宏的依赖 |
lft |
LFTYPE(=DWORD) |
逻辑字体类型标志(如设备字体、光栅字体等),由创建者传入 |
fl |
FLONG |
状态标志;TEXTOBJECT_INIT(0x00010000)表示字体已实现(realized) |
Font |
FONTOBJ * |
指向实现字体 FONTGDI 的内嵌 FontObj(FONTGDI 首成员);TextIntRealizeFont 完成匹配后填充,未实现时为 NULL |
TextFace |
WCHARLF_FACESIZE |
实际匹配到的字体面名(本地化名称),供 GetTextFace 使用 |
dwOffsetEndArray |
DWORD |
保留字段(扩展数组结束偏移) |
logfont |
ENUMLOGFONTEXDVW |
完整的逻辑字体描述:LOGFONTW + 全名/样式/脚本 + DESIGNVECTOR 设计向量,是 GetObject/GetTextFace/度量函数的输入源 |
lock |
EX_PUSH_LOCK |
文本对象自旋锁,TEXTOBJ_LockText/TEXTOBJ_UnlockText 用它做独占访问 |
TEXTOBJ 的配套访问宏/内联函数(text.h 第 76~101 行):
#define LFONT_AllocFontWithHandle() ((PLFONT)GDIOBJ_AllocObjWithHandle(GDI_OBJECT_TYPE_FONT, sizeof(TEXTOBJ)))
#define LFONT_ShareLockFont(hfont) (PLFONT)GDIOBJ_ReferenceObjectByHandle(hfont, GDIObjType_LFONT_TYPE)
#define LFONT_ShareUnlockFont(plfnt) GDIOBJ_vDereferenceObject((POBJ)plfnt)
#define LFONT_UnlockFont(plfnt) GDIOBJ_vUnlockObject((POBJ)plfnt)
FORCEINLINE
PTEXTOBJ
TEXTOBJ_LockText(HFONT hfont)
{
PLFONT plfnt = LFONT_ShareLockFont(hfont);
if (plfnt != 0)
{
KeEnterCriticalRegion();
ExAcquirePushLockExclusive(&plfnt->lock);
}
return plfnt;
}
FORCEINLINE
VOID
TEXTOBJ_UnlockText(PLFONT plfnt)
{
ExReleasePushLockExclusive(&plfnt->lock);
KeLeaveCriticalRegion();
LFONT_ShareUnlockFont(plfnt);
}
要点:TEXTOBJ_LockText = "句柄引用 + push lock 独占",TEXTOBJ_UnlockText 与之对称;KeEnterCriticalRegion 防止持有锁期间被 APC 打断。所有 font.c 度量函数都遵循 TextObj = RealizeFontInit(hfont)(内部已调用 TEXTOBJ_LockText)→ 使用 → TEXTOBJ_UnlockText(TextObj) 的模式。
2.2 STRGDI(text.h,扩展 STROBJ)
text.h 第 23~54 行:
typedef struct _STRGDI
{
STROBJ StrObj; // Text string object header.
FLONG flTO;
INT cgposCopied;
INT cgposPositionsEnumerated;
PVOID prfo; // PRFONT -> PFONTGDI
PGLYPHPOS pgpos;
POINTFIX ptfxRef;
POINTFIX ptfxUpdate;
POINTFIX ptfxEscapement;
RECTFX rcfx;
FIX fxExtent;
FIX fxExtra;
FIX fxBreakExtra;
DWORD dwCodePage;
INT cExtraRects;
RECTL arclExtra[3];
RECTL rclBackGroundSave;
PWCHAR pwcPartition;
PLONG plPartition;
PLONG plNext;
PGLYPHPOS pgpNext;
PLONG plCurrentFont;
POINTL ptlBaseLineAdjust;
INT cTTSysGlyphs;
INT cSysGlyphs;
INT cDefGlyphs;
INT cNumFaceNameGlyphs;
PVOID pacFaceNameGlyphs;
ULONG acFaceNameGlyphs[8];
} STRGDI, *PSTRGDI;
STRGDI 是 GDI 内核文本输出的扩展字符串对象(继承 STROBJ),字段含义:
| 字段 |
说明 |
StrObj |
STROBJ 头(字符数、缓冲区指针、字形句柄数组等) |
flTO |
文本对象标志:TO_MEM_ALLOCATED(0x0001)、TO_ALL_PTRS_VALID、TO_VALID、TO_ESC_NOT_ORIENT、TO_PWSZ_ALLOCATED、TSIM_UNDERLINE1/2、TSIM_STRIKEOUT、TO_HIGHRESTEXT、TO_BITMAPS、TO_PARTITION_INIT、TO_ALLOC_FACENAME、TO_SYS_PARTITION(text.h 第 7~19 行) |
cgposCopied / cgposPositionsEnumerated |
已拷贝/已枚举的字形位置计数 |
prfo |
指向 PFONTGDI(注释 PRFONT -> PFONTGDI) |
pgpos |
PGLYPHPOS 字形位置数组 |
ptfxRef/ptfxUpdate/ptfxEscapement |
参考点、更新点、文本矢量(16.16 定点) |
rcfx |
文本包围矩形(定点) |
fxExtent/fxExtra/fxBreakExtra |
行宽、字间距增量、断字增量 |
dwCodePage |
文本的代码页 |
arclExtra[3]/cExtraRects |
额外矩形(下划线/删除线/外框) |
pwcPartition/plPartition/plNext/pgpNext/plCurrentFont |
分区扫描状态(复杂文本排版) |
cTTSysGlyphs/cSysGlyphs/cDefGlyphs |
TT 系统字形/系统字形/默认字形计数 |
acFaceNameGlyphs[8]/cNumFaceNameGlyphs/pacFaceNameGlyphs |
面名字形缓存 |
STRGDI 由 text.c(file:///d:/reactos/win32ss/gdi/ntgdi/text.c) / freetype.c 的文本输出路径使用(IntExtTextOutW),font.c 不直接操作它,但 GreGetCharacterPlacementW 的设计与其排版语义相关。
2.3 FONTOBJ(winddi.h,DDI 字体对象头)
winddi.h(file:///d:/reactos/sdk/include/psdk/winddi.h) 第 761~772 行:
typedef struct _FONTOBJ {
ULONG iUniq;
ULONG iFace;
ULONG cxMax;
FLONG flFontType;
ULONG_PTR iTTUniq;
ULONG_PTR iFile;
SIZE sizLogResPpi;
ULONG ulStyleSize;
PVOID pvConsumer;
PVOID pvProducer;
} FONTOBJ;
| 字段 |
说明 |
iUniq |
字体唯一标识(实现后置 1,表示可缓存) |
iFace |
面索引 |
cxMax |
最大字符单元宽度 |
flFontType |
字体类型标志:FO_DEVICE_FONT(1)、FO_OUTLINE_CAPABLE(2)、FO_CLEARTYPENATURAL_X 等 |
iTTUniq |
TrueType 唯一标识 |
iFile |
文件句柄/文件标识 |
sizLogResPpi |
逻辑分辨率(每英寸像素) |
ulStyleSize |
样式大小 |
pvConsumer / pvProducer |
消费方/生产方私有数据(驱动用) |
FONTOBJ 是 GDI 与显示驱动/字体驱动之间的标准字体对象头;ReactOS 的 FONTGDI 把它作为第一个成员(继承)。
2.4 FONTGDI(engobjects.h,实现字体对象)
engobjects.h(file:///d:/reactos/win32ss/gdi/eng/engobjects.h) 第 139~171 行:
typedef struct _FONTGDI {
FONTOBJ FontObj;
ULONG iUnique;
FLONG flType;
DHPDEV dhpdev;
PSHARED_FACE SharedFace;
LONG lMaxNegA;
LONG lMaxNegC;
LONG lMinWidthD;
LPWSTR Filename;
BYTE RequestUnderline;
BYTE RequestStrikeOut;
BYTE RequestItalic;
LONG RequestWeight;
BYTE OriginalItalic;
LONG OriginalWeight;
BYTE CharSet;
/* Precomputed font metrics (supplements FreeType metrics) */
LONG tmHeight;
LONG tmAscent;
LONG tmDescent;
LONG tmInternalLeading;
LONG Magic;
LONG lfHeight;
LONG lfWidth;
} FONTGDI, *PFONTGDI;
/* The initialized 'Magic' value in FONTGDI */
#define FONTGDI_MAGIC 0x20110311
| 字段 |
说明 |
FontObj |
FONTOBJ 头(首成员,ObjToGDI(TextObj->Font, FONT) 即取其地址再按 FONTGDI 解释) |
iUnique |
字体唯一号 |
flType |
类型标志,含 FO_TYPE_TRUETYPE 等(NtGdiGetOutlineTextMetricsInternalW 用它判断是否 TrueType) |
dhpdev |
设备句柄(打印/显示驱动) |
SharedFace |
共享面:真正的 FT_Face + 引用计数 + 缓存(见 2.5) |
lMaxNegA / lMaxNegC / lMinWidthD |
A/C 最大负值、最小 D 宽度(ABC 宽度信息,CHWIDTHINFO 风格) |
Filename |
字体文件名字符串(卸载时用于匹配) |
RequestUnderline/StrikeOut/Italic |
合成请求:逻辑字体要求下划线/删除线/斜体(0xFF 表示要) |
RequestWeight / OriginalWeight |
请求字重 / 字体原始字重(合成粗体判定 EMUBOLD_NEEDED(OriginalWeight, lfWeight)) |
OriginalItalic |
原始斜体标志(合成斜体判定 lfItalic && !OriginalItalic) |
CharSet |
字符集 |
tmHeight/tmAscent/tmDescent/tmInternalLeading |
预计算 的文本度量(补充 FreeType 度量,TextIntGetTextExtentPoint 直接使用 tmAscent/tmDescent) |
Magic |
魔数 0x20110311,ASSERT(FontGDI->Magic == FONTGDI_MAGIC) 校验 |
lfHeight / lfWidth |
请求的逻辑高度/宽度 |
2.5 SHARED_FACE / SHARED_FACE_CACHE(engobjects.h)
engobjects.h 第 125~137 行:
typedef struct _SHARED_FACE_CACHE {
UINT OutlineRequiredSize;
UNICODE_STRING FontFamily;
UNICODE_STRING FullName;
} SHARED_FACE_CACHE, *PSHARED_FACE_CACHE;
typedef struct _SHARED_FACE {
FT_Face Face;
LONG RefCount;
PSHARED_MEM Memory;
SHARED_FACE_CACHE EnglishUS;
SHARED_FACE_CACHE UserLanguage;
} SHARED_FACE, *PSHARED_FACE;
| 字段 |
说明 |
Face |
FreeType 的 FT_Face(打开的字形面),所有字形操作的核心 |
RefCount |
共享引用计数(多个 FONTGDI 可共享同一 FT_Face) |
Memory |
共享内存(SharedMem_Create 创建,承载字体文件映射/内存拷贝,SharedMem_Release 释放) |
EnglishUS.EnglishUS / UserLanguage |
两个缓存槽:英文(美国)与用户语言的字体名缓存;每槽含 OutlineRequiredSize(OUTLINETEXTMETRIC 所需字节数,IntGetOutlineTextMetrics 首次计算后缓存)和 FontFamily/FullName(面名/全名) |
FT_Face 的获取:FontGDI->SharedFace->Face,例如 ftGdiGetFontData、ftGdiGetKerningPairs、ftGetFontUnicodeRanges、TextIntUpdateSize 等函数都从这里取面。
2.6 UNIVERSAL_FONT_ID(ntgdityp.h)
ntgdityp.h(file:///d:/reactos/win32ss/include/ntgdityp.h) 第 265~269 行:
typedef struct _UNIVERSAL_FONT_ID
{
ULONG CheckSum;
ULONG Index;
} UNIVERSAL_FONT_ID, *PUNIVERSAL_FONT_ID;
CheckSum 为字体文件校验和(GetFontResourceInfoInternalW 的 dwType 场景会计算),Index 为字体集合中的索引。用于唯一标识字体集合中的一个面,常配合 FONTOBJ.iTTUniq 使用。
2.7 ENUMLOGFONTEXDVW / ENUMLOGFONTEXW / DESIGNVECTOR(wingdi.h)
wingdi.h(file:///d:/reactos/sdk/include/psdk/wingdi.h):
typedef struct tagENUMLOGFONTEXW {
LOGFONTW elfLogFont;
WCHAR elfFullName[LF_FULLFACESIZE];
WCHAR elfStyle[LF_FACESIZE];
WCHAR elfScript[LF_FACESIZE];
} ENUMLOGFONTEXW,*LPENUMLOGFONTEXW;
typedef struct _DESIGNVECTOR {
DWORD dvReserved;
DWORD dvNumAxes;
LONG dvValues[MM_MAX_NUMAXES];
} DESIGNVECTOR, *PDESIGNVECTOR, *LPDESIGNVECTOR;
typedef struct tagENUMLOGFONTEXDVW
{
ENUMLOGFONTEXW elfEnumLogfontEx;
DESIGNVECTOR elfDesignVector;
} ENUMLOGFONTEXDVW, *PENUMLOGFONTEXDVW, *LPENUMLOGFONTEXDVW;
| 成员 |
说明 |
elfEnumLogfontEx.elfLogFont |
LOGFONTW:面名/高度/宽度/字重/斜体/下划线/删除线/字符集/精度/字体族/Escapement/Orientation/OutPrecision 等完整逻辑字体参数 |
elfEnumLogfontEx.elfFullName |
字体全名(LF_FULLFACESIZE = 64 WCHAR) |
elfEnumLogfontEx.elfStyle |
样式名 |
elfEnumLogfontEx.elfScript |
脚本名 |
elfDesignVector |
设计向量(MM_MAX_NUMAXES=16 轴;dvNumAxes=0 表示无多轴字体) |
TEXTOBJ.logfont 就是该结构;HfontCreate 将其整体拷入 TEXTOBJ。
2.8 REALIZATION_INFO / LFTYPE(ntgdityp.h)
typedef DWORD LFTYPE;
#define RI_TECH_BITMAP 1
#define RI_TECH_FIXED 2
#define RI_TECH_SCALABLE 3
typedef struct _REALIZATION_INFO
{
DWORD iTechnology;
DWORD iUniq;
DWORD dwUnknown;
} REALIZATION_INFO, *PREALIZATION_INFO;
REALIZATION_INFO 是 NtGdiGetRealizationInfo 的输出:iTechnology 取值 RI_TECH_BITMAP(位图字体)/RI_TECH_FIXED(固定字体)/RI_TECH_SCALABLE(可缩放字体,如 TrueType),iUniq 来自 FontObj.iUniq,dwUnknown 固定为 -1。ftGdiRealizationInfo(freetype.c 第 6432 行)通过 FT_HAS_FIXED_SIZES / FT_IS_SCALABLE 宏判定技术类型。
3. 函数逐一详解
font.c 共 26 个函数(含 1 个 static 辅助函数、1 个 #if 0 禁用的 Gre 函数)。按职责分四组逐一展开。
3.1 字体创建组
3.1.1 GreCreateFontIndirectW(font.c 第 29~51 行)
HFONT FASTCALL
GreCreateFontIndirectW(_In_ const LOGFONTW *lplf)
{
if (lplf)
{
ENUMLOGFONTEXDVW Logfont;
RtlCopyMemory( &Logfont.elfEnumLogfontEx.elfLogFont, lplf, sizeof(LOGFONTW));
RtlZeroMemory( &Logfont.elfEnumLogfontEx.elfFullName,
sizeof(Logfont.elfEnumLogfontEx.elfFullName));
RtlZeroMemory( &Logfont.elfEnumLogfontEx.elfStyle,
sizeof(Logfont.elfEnumLogfontEx.elfStyle));
RtlZeroMemory( &Logfont.elfEnumLogfontEx.elfScript,
sizeof(Logfont.elfEnumLogfontEx.elfScript));
Logfont.elfDesignVector.dvNumAxes = 0;
RtlZeroMemory( &Logfont.elfDesignVector, sizeof(DESIGNVECTOR));
return HfontCreate(&Logfont, 0, 0, 0, NULL);
}
else return NULL;
}
| 参数 |
类型 |
说明 |
lplf |
const LOGFONTW * |
用户态 LOGFONTW 指针(此时已被调用方拷贝为安全缓冲) |
- 作用 :
CreateFontIndirectW 的内核实现------把 LOGFONTW 包装成 ENUMLOGFONTEXDVW(清空全名/样式/脚本,dvNumAxes=0 表示无设计向量)后交给 HfontCreate。
- 实现流程 :① 校验指针非空;② 拷贝
elfLogFont,清零三个名称数组;③ dvNumAxes = 0 并清零整个 elfDesignVector;④ 调用 HfontCreate(&Logfont, 0, 0, 0, NULL)(lft=0、fl=0、pvCliData=NULL)。
- 调用方 :user32 的
CreateFontIndirectW 经 NtGdiHfontCreate 进入(实际路径是 gdi32full → NtGdiHfontCreate,Gre 版本供 win32k 内部/旧路径使用);NtGdiHfontCreate 与 HfontCreate 的包装关系见 3.1.2/3.1.3。
- 注意事项 :此函数假定输入已安全(不探测用户缓冲),因此它仅供内核内部调用;用户态入口必须走
NtGdiHfontCreate(带 ProbeForRead)。
3.1.2 NtGdiHfontCreate(font.c 第 1275~1313 行)
HFONT
APIENTRY
NtGdiHfontCreate(
_In_reads_bytes_(cjElfw) const ENUMLOGFONTEXDVW *pelfw,
_In_ ULONG cjElfw,
_In_ LFTYPE lft,
_In_ FLONG fl,
_In_opt_ PVOID pvCliData)
{
ENUMLOGFONTEXDVW SafeLogfont;
NTSTATUS Status = STATUS_SUCCESS;
/* Silence GCC warnings */
SafeLogfont.elfEnumLogfontEx.elfLogFont.lfEscapement = 0;
SafeLogfont.elfEnumLogfontEx.elfLogFont.lfOrientation = 0;
if (!pelfw)
{
return NULL;
}
_SEH2_TRY
{
ProbeForRead(pelfw, sizeof(ENUMLOGFONTEXDVW), 1);
RtlCopyMemory(&SafeLogfont, pelfw, sizeof(ENUMLOGFONTEXDVW));
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END
if (!NT_SUCCESS(Status))
{
return NULL;
}
return HfontCreate(&SafeLogfont, cjElfw, lft, fl, pvCliData);
}
| 参数 |
类型 |
说明 |
pelfw |
const ENUMLOGFONTEXDVW * |
用户缓冲(ENUMLOGFONTEXDVW),SEH 探测后拷入栈变量 |
cjElfw |
ULONG |
缓冲字节数(HfontCreate 未实际使用,保留兼容) |
lft |
LFTYPE |
逻辑字体类型 |
fl |
FLONG |
标志(如私有字体) |
pvCliData |
PVOID |
客户端私有数据(存入句柄表 Entry->UserData) |
- 作用 :用户态
CreateFontIndirectW/CreateFontIndirectExW 的系统服务入口;相比 GreCreateFontIndirectW 多了一层 SEH 用户缓冲安全拷贝,因此允许直接传 ENUMLOGFONTEXDVW(CreateFontIndirectExW 走此路径)。
- 实现流程 :① 空指针检查;②
ProbeForRead 探测 sizeof(ENUMLOGFONTEXDVW);③ 拷贝到栈上 SafeLogfont;④ 异常则返回 NULL;⑤ 转调 HfontCreate。
- 调用方 :user32/gdi32full 的
CreateFontIndirectW 与 CreateFontIndirectExW(后者是 win32k syscall,见 ntgdi.h 第 2888 行声明)。
- 注意事项 :
cjElfw 未用于大小校验(固定按 sizeof(ENUMLOGFONTEXDVW) 拷贝),是潜在精简点;pvCliData 最终被写入 GdiHandleTable->Entries[index].UserData(见 HfontCreate)。
3.1.3 HfontCreate(font.c 第 1221~1272 行)
HFONT
APIENTRY
HfontCreate(
const ENUMLOGFONTEXDVW *pelfw,
_In_ ULONG cjElfw,
_In_ LFTYPE lft,
_In_ FLONG fl,
_In_opt_ PVOID pvCliData)
{
HFONT hNewFont;
PLFONT plfont;
if (!pelfw)
{
return NULL;
}
plfont = LFONT_AllocFontWithHandle();
if (!plfont)
{
return NULL;
}
hNewFont = plfont->BaseObject.hHmgr;
plfont->lft = lft;
plfont->fl = fl;
RtlCopyMemory (&plfont->logfont, pelfw, sizeof(ENUMLOGFONTEXDVW));
ExInitializePushLock(&plfont->lock);
if (pelfw->elfEnumLogfontEx.elfLogFont.lfEscapement !=
pelfw->elfEnumLogfontEx.elfLogFont.lfOrientation)
{
/* This should really depend on whether GM_ADVANCED is set */
plfont->logfont.elfEnumLogfontEx.elfLogFont.lfOrientation =
plfont->logfont.elfEnumLogfontEx.elfLogFont.lfEscapement;
}
LFONT_UnlockFont(plfont);
if (pvCliData && hNewFont)
{
// FIXME: Use GDIOBJ_InsertUserData
KeEnterCriticalRegion();
{
INT Index = GDI_HANDLE_GET_INDEX((HGDIOBJ)hNewFont);
PGDI_TABLE_ENTRY Entry = &GdiHandleTable->Entries[Index];
Entry->UserData = pvCliData;
}
KeLeaveCriticalRegion();
}
return hNewFont;
}
| 参数 |
类型 |
说明 |
pelfw |
const ENUMLOGFONTEXDVW * |
逻辑字体完整描述(已安全) |
cjElfw |
ULONG |
未使用(保留参数) |
lft |
LFTYPE |
逻辑字体类型 |
fl |
FLONG |
标志 |
pvCliData |
PVOID |
客户端数据,存入句柄表 UserData |
- 作用 :真正的逻辑字体对象分配器------从 GDI 句柄表分配 TEXTOBJ,初始化全部字段;是创建路径的唯一收口。
- 实现流程 :① 空指针检查;②
LFONT_AllocFontWithHandle() → GDIOBJ_AllocObjWithHandle(GDI_OBJECT_TYPE_FONT, sizeof(TEXTOBJ)),句柄为 BaseObject.hHmgr;③ 赋值 lft/fl,整体拷贝 ENUMLOGFONTEXDVW 到 plfont->logfont;④ ExInitializePushLock 初始化锁;⑤ 规范化 :若 lfEscapement != lfOrientation 则把 Orientation 强制设为 Escapement(注释指出正确做法应依赖 GM_ADVANCED);⑥ LFONT_UnlockFont 释放初始锁;⑦ 若 pvCliData 非空,把句柄表 Entries[Index].UserData 置为 pvCliData(FIXME 注释建议改用 GDIOBJ_InsertUserData)。
- 调用方 :
GreCreateFontIndirectW、NtGdiHfontCreate;也被 ntuser(如 NtUserSetMenuFlagRTL 等)以及字体枚举/默认字体创建等内部路径间接使用。
- 注意事项 :此处不做字体匹配 ------
plfont->Font 保持 NULL,fl 不含 TEXTOBJECT_INIT,延迟到 RealizeFontInit。这体现了 Windows 逻辑字体与"已实现字体"(realized font)的分离。
3.1.4 RealizeFontInit(font.c 第 431~450 行)
PTEXTOBJ
FASTCALL
RealizeFontInit(_In_ HFONT hFont)
{
NTSTATUS Status = STATUS_SUCCESS;
PTEXTOBJ pTextObj;
pTextObj = TEXTOBJ_LockText(hFont);
if ( pTextObj && !(pTextObj->fl & TEXTOBJECT_INIT))
{
Status = TextIntRealizeFont(hFont, pTextObj);
if (!NT_SUCCESS(Status))
{
TEXTOBJ_UnlockText(pTextObj);
return NULL;
}
}
return pTextObj;
}
| 参数 |
类型 |
说明 |
hFont |
HFONT |
逻辑字体句柄 |
- 作用 :延迟实现(lazy realization)的触发器 :锁定 TEXTOBJ,若尚未初始化(
!TEXTOBJECT_INIT)则调用 TextIntRealizeFont 完成字体匹配,成功后才返回已实现的 TEXTOBJ(此时 TextObj->Font 非空)。
- 实现流程 :①
TEXTOBJ_LockText(句柄引用 + push lock);② 检查 TEXTOBJECT_INIT 标志;③ 未初始化 → TextIntRealizeFont(hFont, pTextObj)(freetype.c 第 6050 行,见 5.2);④ 失败则解锁并返回 NULL,成功则原样返回。
- 调用方 :几乎 font.c 的每个度量函数 (
GreGetKerningPairs、NtGdiGetFontData、NtGdiGetFontUnicodeRanges、NtGdiGetGlyphOutline、NtGdiGetKerningPairs、NtGdiGetOutlineTextMetricsInternalW、NtGdiGetRealizationInfo、IntGetCharDimensions 等),以及 freetype.c 的 GreGetCharWidthW/GreGetCharABCWidthsW/GreGetGlyphIndicesW/ftGdiGetTextMetricsW/ftGdiGetGlyphOutline/TextIntGetTextExtentPoint、text.c 的文本输出路径。
- 注意事项 :调用方拿到返回值后必须配对
TEXTOBJ_UnlockText ;返回值已持锁,失败时返回 NULL 且未持锁,调用方需 EngSetLastError(ERROR_INVALID_HANDLE)。注意 TextIntRealizeFont 内部要求 ASSERT_FREETYPE_LOCK_NOT_HELD()(freetype.c 第 6059 行),即调用者不得持有 FreeType 全局锁。
3.1.5 FontGetObject(font.c 第 296~333 行)
ULONG
FASTCALL
FontGetObject(
_Inout_ PTEXTOBJ plfont,
_In_ ULONG cjBuffer,
_Out_ PVOID pvBuffer)
{
ULONG cjMaxSize;
ENUMLOGFONTEXDVW *plf;
ASSERT(plfont);
plf = &plfont->logfont;
if (!(plfont->fl & TEXTOBJECT_INIT))
{
NTSTATUS Status;
DPRINT("FontGetObject font not initialized!\n");
Status = TextIntRealizeFont(plfont->BaseObject.hHmgr, plfont);
if (!NT_SUCCESS(Status))
{
DPRINT1("FontGetObject(TextIntRealizeFont) Status = 0x%lx\n", Status);
}
}
/* If buffer is NULL, only the size is requested */
if (pvBuffer == NULL) return sizeof(LOGFONTW);
/* Calculate the maximum size according to number of axes */
cjMaxSize = FIELD_OFFSET(ENUMLOGFONTEXDVW,
elfDesignVector.dvValues[plf->elfDesignVector.dvNumAxes]);
if (cjBuffer > cjMaxSize) cjBuffer = cjMaxSize;
RtlCopyMemory(pvBuffer, plf, cjBuffer);
return cjBuffer;
}
| 参数 |
类型 |
说明 |
plfont |
PTEXTOBJ |
已锁定的 TEXTOBJ |
cjBuffer |
ULONG |
目标缓冲大小 |
pvBuffer |
PVOID |
目标缓冲(可为 NULL 仅查询大小) |
- 作用 :
GetObject(hfont) 的内核实现------把 TEXTOBJ 中保存的 ENUMLOGFONTEXDVW 拷回调用者。未实现时会先触发 TextIntRealizeFont。
- 实现流程 :① 若未
TEXTOBJECT_INIT 先实现(失败仅 DPRINT,不中止);② pvBuffer==NULL 返回 sizeof(LOGFONTW)(与 Win32 GetObject 行为一致,只返回 LOGFONTW 大小);③ 按 dvNumAxes 计算最大可拷贝长度 FIELD_OFFSET(ENUMLOGFONTEXDVW, elfDesignVector.dvValues[dvNumAxes]);④ 截断拷贝并返回实际字节数。
- 调用方 :ntuser 的
NtUserGetObject(GetObject 系统服务)在对象类型为 FONT 时调用。
- 注意事项 :
pvBuffer==NULL 时返回 LOGFONTW 大小而非 ENUMLOGFONTEXDVW 大小,这是刻意与 Windows API 兼容的行为。
3.2 字体资源加载/卸载组
3.2.1 IntCheckFontPathNames(font.c 第 452~470 行,static)
static BOOL
IntCheckFontPathNames(
_In_reads_(cwc) PCWCH pwcFiles,
_In_ ULONG cFiles,
_In_ ULONG cwc)
{
ULONG ich, cRealFiles;
if (pwcFiles[cwc - 1] != UNICODE_NULL)
return FALSE;
for (ich = cRealFiles = 0; ich < cwc; ++ich)
{
if (!pwcFiles[ich])
++cRealFiles;
}
return cRealFiles >= cFiles;
}
| 参数 |
类型 |
说明 |
pwcFiles |
PCWCH |
多字符串(每个文件路径以 \0 结束,末尾再一个 \0) |
cFiles |
ULONG |
期望的文件数 |
cwc |
ULONG |
缓冲总字符数 |
- 作用 :校验
AddFontResource 传入的"多字符串"(double-null-terminated)格式:要求缓冲以 UNICODE_NULL 结尾,且内含的空字符数不少于 cFiles。
- 调用方 :
NtGdiAddFontResourceW、NtGdiRemoveFontResourceW(SEH 保护区内)。
- 注意事项 :
pwcFiles[cwc-1] 在 SEH 保护区(ProbeForRead 之后)访问,安全;cFiles 为 0 时任何以空结尾的缓冲都通过。
3.2.2 NtGdiAddFontResourceW(font.c 第 474~526 行)
INT
APIENTRY
NtGdiAddFontResourceW(
_In_reads_(cwc) PCWCH pwcFiles,
_In_ ULONG cwc,
_In_ ULONG cFiles,
_In_ FLONG fl,
_In_ DWORD dwPidTid,
_In_opt_ const DESIGNVECTOR *pdv)
{
UNICODE_STRING SafeFileName;
INT Ret;
DBG_UNREFERENCED_PARAMETER(dwPidTid);
DBG_UNREFERENCED_PARAMETER(pdv);
DPRINT("NtGdiAddFontResourceW\n");
/* cwc = Length + trailing zero. */
if ((cwc <= 1) || (cwc > UNICODE_STRING_MAX_CHARS))
return 0;
SafeFileName.Length = (USHORT)((cwc - 1) * sizeof(WCHAR));
SafeFileName.MaximumLength = SafeFileName.Length + sizeof(UNICODE_NULL);
SafeFileName.Buffer = ExAllocatePoolWithTag(PagedPool,
SafeFileName.MaximumLength,
TAG_STRING);
if (!SafeFileName.Buffer)
return 0;
_SEH2_TRY
{
ProbeForRead(pwcFiles, cwc * sizeof(WCHAR), sizeof(WCHAR));
if (!IntCheckFontPathNames(pwcFiles, cFiles, cwc))
return 0;
RtlCopyMemory(SafeFileName.Buffer, pwcFiles, SafeFileName.Length);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
ExFreePoolWithTag(SafeFileName.Buffer, TAG_STRING);
_SEH2_YIELD(return 0);
}
_SEH2_END;
SafeFileName.Buffer[SafeFileName.Length / sizeof(WCHAR)] = UNICODE_NULL;
Ret = IntGdiAddFontResourceEx(&SafeFileName, cFiles, fl, 0);
ExFreePoolWithTag(SafeFileName.Buffer, TAG_STRING);
return Ret;
}
| 参数 |
类型 |
说明 |
pwcFiles |
PCWCH |
字体文件路径多字符串(用户缓冲) |
cwc |
ULONG |
字符数(含末尾 \0) |
cFiles |
ULONG |
文件数量 |
fl |
FLONG |
特性标志(FR_PRIVATE 等) |
dwPidTid |
DWORD |
进程/线程 ID(未使用) |
pdv |
const DESIGNVECTOR * |
设计向量(未使用) |
- 作用 :
AddFontResourceW / AddFontResourceExW 的系统服务:把用户态路径多字符串安全拷贝成 UNICODE_STRING 后交给 IntGdiAddFontResourceEx 逐个加载。
- 实现流程 :① 长度检查(
cwc<=1 或超 UNICODE_STRING_MAX_CHARS 返回 0);② 分配 TAG_STRING 分页池缓冲,构造 SafeFileName(Length 不含末尾 \0);③ SEH:ProbeForRead → IntCheckFontPathNames → 拷贝;④ 补写末尾 UNICODE_NULL;⑤ IntGdiAddFontResourceEx(&SafeFileName, cFiles, fl, 0)(flags=0,即不写注册表------写注册表是 IntLoadSystemFonts 用的 AFRX_WRITE_REGISTRY 路径);⑥ 释放缓冲返回加载面数。
- 调用方 :gdi32full 的
AddFontResourceW/AddFontResourceExW;内核初始化时 IntLoadSystemFonts(freetype.c 第 1483 行)也调用 IntGdiAddFontResourceEx 直接加载(不经过本 syscall)。
- 注意事项 :返回值是成功加载的字体面总数 (多个 .ttc 集合文件可返回 >1);
dwPidTid/pdv 目前忽略(TAG 注释 DBG_UNREFERENCED_PARAMETER),说明 FR_PRIVATE 之外的私有文件字体语义尚未完全实现(私有文件字体走 NtGdiAddFontMemResourceEx)。
3.2.3 NtGdiRemoveFontResourceW(font.c 第 528~580 行)
BOOL
APIENTRY
NtGdiRemoveFontResourceW(
_In_reads_(cwc) PCWCH pwszFiles,
_In_ ULONG cwc,
_In_ ULONG cFiles,
_In_ ULONG fl,
_In_ DWORD dwPidTid,
_In_opt_ const DESIGNVECTOR *pdv)
{
UNICODE_STRING SafeFileName;
BOOL Ret;
DBG_UNREFERENCED_PARAMETER(dwPidTid);
DBG_UNREFERENCED_PARAMETER(pdv);
DPRINT("NtGdiRemoveFontResourceW\n");
/* cwc = Length + trailing zero. */
if ((cwc <= 1) || (cwc > UNICODE_STRING_MAX_CHARS))
return FALSE;
SafeFileName.Length = (USHORT)((cwc - 1) * sizeof(WCHAR));
SafeFileName.MaximumLength = SafeFileName.Length + sizeof(UNICODE_NULL);
SafeFileName.Buffer = ExAllocatePoolWithTag(PagedPool,
SafeFileName.MaximumLength,
TAG_STRING);
if (!SafeFileName.Buffer)
return FALSE;
_SEH2_TRY
{
ProbeForRead(pwszFiles, cwc * sizeof(WCHAR), sizeof(WCHAR));
if (!IntCheckFontPathNames(pwszFiles, cFiles, cwc))
return FALSE;
RtlCopyMemory(SafeFileName.Buffer, pwszFiles, SafeFileName.Length);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
ExFreePoolWithTag(SafeFileName.Buffer, TAG_STRING);
_SEH2_YIELD(return FALSE);
}
_SEH2_END;
SafeFileName.Buffer[SafeFileName.Length / sizeof(WCHAR)] = UNICODE_NULL;
Ret = IntGdiRemoveFontResource(&SafeFileName, cFiles, fl);
ExFreePoolWithTag(SafeFileName.Buffer, TAG_STRING);
return Ret;
}
| 参数 |
类型 |
说明 |
pwszFiles |
PCWCH |
字体文件路径多字符串(用户缓冲) |
cwc |
ULONG |
字符数(含末尾 \0) |
cFiles |
ULONG |
文件数量 |
fl |
ULONG |
特性标志(透传给 IntGdiRemoveFontResource) |
dwPidTid / pdv |
DWORD / DESIGNVECTOR* |
未使用 |
- 作用 :
RemoveFontResourceW 的系统服务:与 NtGdiAddFontResourceW 完全对称的安全拷贝,然后调 IntGdiRemoveFontResource。
- 实现流程 :同
NtGdiAddFontResourceW 的 ①~④,最后 IntGdiRemoveFontResource(&SafeFileName, cFiles, fl)(freetype.c 第 2218 行,逐个调用 IntGdiRemoveFontResourceSingle,按 Filename 与 PathFindFileNameW 提取的文件名匹配 g_FontListHead 中的 FONT_ENTRY,移除并 CleanupFontEntry,若带 AFRX_WRITE_REGISTRY 还会 IntDeleteRegFontEntries 清理注册表 g_FontRegPath 下同值条目)。
- 调用方 :gdi32full 的
RemoveFontResourceW/RemoveFontResourceExW。
- 注意事项 :
IntGdiRemoveFontResourceSingle 在持有 IntLockFreeType()(g_FreeTypeLock 快互斥)下遍历并删除链表项;删除依据是 FontGDI->Filename 与路径文件名的不区分大小写比较 (_wcsicmp)。
3.2.4 NtGdiAddFontMemResourceEx(font.c 第 582~635 行)
HANDLE
APIENTRY
NtGdiAddFontMemResourceEx(
_In_reads_bytes_(cjBuffer) const VOID *pvBuffer,
_In_ DWORD cjBuffer,
_In_reads_bytes_opt_(cjDV) const DESIGNVECTOR *pdv,
_In_ ULONG cjDV,
_Out_ PDWORD pNumFonts)
{
_SEH2_VOLATILE PVOID Buffer = NULL;
HANDLE Ret;
DWORD NumFonts = 0;
DPRINT("NtGdiAddFontMemResourceEx\n");
DBG_UNREFERENCED_PARAMETER(pdv);
DBG_UNREFERENCED_PARAMETER(cjDV);
if (!pvBuffer || !cjBuffer)
return NULL;
_SEH2_TRY
{
ProbeForRead(pvBuffer, cjBuffer, sizeof(BYTE));
Buffer = ExAllocatePoolWithQuotaTag(PagedPool, cjBuffer, TAG_FONT);
RtlCopyMemory(Buffer, pvBuffer, cjBuffer);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
if (Buffer != NULL)
{
ExFreePoolWithTag(Buffer, TAG_FONT);
}
_SEH2_YIELD(return NULL);
}
_SEH2_END;
Ret = IntGdiAddFontMemResource(Buffer, cjBuffer, &NumFonts);
ExFreePoolWithTag(Buffer, TAG_FONT);
_SEH2_TRY
{
ProbeForWrite(pNumFonts, sizeof(NumFonts), 1);
*pNumFonts = NumFonts;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Leak it? */
_SEH2_YIELD(return NULL);
}
_SEH2_END;
return Ret;
}
| 参数 |
类型 |
说明 |
pvBuffer |
const VOID * |
字体文件内存镜像(TTF/OTF 字节) |
cjBuffer |
DWORD |
缓冲字节数 |
pdv / cjDV |
DESIGNVECTOR* / ULONG |
设计向量(未使用) |
pNumFonts |
PDWORD |
输出:实际添加的字体面数 |
- 作用 :
AddFontMemResourceEx 的系统服务:把用户态字体内存镜像按字节拷贝 到内核池(ExAllocatePoolWithQuotaTag 计入进程配额),再交给 IntGdiAddFontMemResource 加载,返回字体集合句柄(HANDLE)。
- 实现流程 :① 空指针/空长度检查;② SEH:
ProbeForRead → 配额池分配 TAG_FONT → RtlCopyMemory;③ IntGdiAddFontMemResource(Buffer, cjBuffer, &NumFonts)(freetype.c 第 2407 行):再次 ExAllocatePoolWithTag 深拷贝 → SharedMem_Create(BufferCopy, dwSize, FALSE) → LoadFont.Characteristics = FR_PRIVATE | FR_NOT_ENUM → IntGdiLoadFontByIndexFromMemory(&LoadFont, -1) 解析全部面 → 释放临时拷贝 → 若 FaceCount>0 则分配 FONT_ENTRY_COLL_MEM 挂入进程 PrivateMemFontListHead,句柄为自增 PrivateMemFontHandleCount;④ 返回句柄并把面数 ProbeForWrite 写回 pNumFonts。
- 调用方 :gdi32full 的
AddFontMemResourceEx(被 DirectWrite/自绘控件等使用)。
- 注意事项 :返回的 HANDLE 只是进程内句柄号 (
ULongToHandle),不是内核对象句柄;进程退出时 IntGdiCleanupPrivateFontsForProcess(见 5.2)清理;pNumFonts 写失败时注释"Leak it?"------返回 NULL 但字体已加载(轻微资源泄漏语义,与 Windows 行为近似)。
3.2.5 NtGdiRemoveFontMemResourceEx(font.c 第 638~644 行)
BOOL
APIENTRY
NtGdiRemoveFontMemResourceEx(
_In_ HANDLE hMMFont)
{
return IntGdiRemoveFontMemResource(hMMFont);
}
| 参数 |
类型 |
说明 |
hMMFont |
HANDLE |
AddFontMemResourceEx 返回的字体集合句柄 |
- 作用 :
RemoveFontMemResourceEx 的系统服务:按句柄从进程 PrivateMemFontListHead 查找 FONT_ENTRY_COLL_MEM。
- 实现流程 :直接转发
IntGdiRemoveFontMemResource(freetype.c 第 2492 行):锁 FreeType + 锁进程私有字体列表 → 遍历匹配 CurrentEntry->Handle == hMMFont → UnlinkFontMemCollection(把集合内所有 FONT_ENTRY 从 PrivateFontListHead 摘除)→ 解锁 → IntGdiCleanupMemEntry 逐个 CleanupFontEntry 释放 FT 面与共享内存。
- 调用方 :gdi32full 的
RemoveFontMemResourceEx。
- 注意事项:找不到句柄返回 FALSE;对同一个句柄重复移除返回 FALSE。
3.3 度量与字形查询组
3.3.1 NtGdiGetCharWidthW(font.c 第 1316~1372 行)
/* This function is called from GetCharWidthA/W/I, GetCharWidth32A/W, and GetCharWidthFloatA/W. */
BOOL NTAPI
NtGdiGetCharWidthW(
_In_ HDC hDC,
_In_ UINT FirstChar,
_In_ UINT Count,
_In_reads_opt_(Count) PCWCH UnSafepwc,
_In_ FLONG fl,
_Out_writes_bytes_(Count * sizeof(INT)) PVOID Buffer)
{
BOOL ret = FALSE;
PVOID pTmpBuffer = NULL;
PWCHAR pSafePwc = NULL;
NTSTATUS Status;
WCHAR StackPwc[40];
INT StackBuffer[40];
if (!Count || Count > MAX_TEXT_BUFFER / sizeof(INT))
return FALSE;
if (UnSafepwc)
{
if (Count <= _countof(StackPwc))
pSafePwc = StackPwc;
else
pSafePwc = ExAllocatePoolWithTag(PagedPool, Count * sizeof(WCHAR), GDITAG_TEXT);
if (!pSafePwc)
return FALSE;
Status = MmCopyFromCaller(pSafePwc, UnSafepwc, Count * sizeof(WCHAR));
if (!NT_SUCCESS(Status))
goto Cleanup;
}
if (Count <= _countof(StackBuffer))
pTmpBuffer = StackBuffer;
else
pTmpBuffer = ExAllocatePoolWithTag(PagedPool, Count * sizeof(INT), GDITAG_TEXT);
if (!pTmpBuffer)
goto Cleanup;
ret = GreGetCharWidthW(hDC, FirstChar, Count, pSafePwc, fl, pTmpBuffer);
if (ret)
{
Status = MmCopyToCaller(Buffer, pTmpBuffer, Count * sizeof(INT));
ret = NT_SUCCESS(Status);
}
Cleanup:
if (pTmpBuffer && pTmpBuffer != StackBuffer)
ExFreePoolWithTag(pTmpBuffer, GDITAG_TEXT);
if (pSafePwc && pSafePwc != StackPwc)
ExFreePoolWithTag(pSafePwc, GDITAG_TEXT);
return ret;
}
| 参数 |
类型 |
说明 |
hDC |
HDC |
设备上下文(取当前选中字体) |
FirstChar |
UINT |
首字符码 |
Count |
UINT |
字符数 |
UnSafepwc |
PCWCH |
可选的字符数组(NULL 表示按 FirstChar 连续取) |
fl |
FLONG |
标志(GCW_INDICES 表示把输入当字形索引;0 表示输出 FLOAT) |
Buffer |
PVOID |
输出缓冲(Count * sizeof(INT) 或 FLOAT) |
- 作用 :
GetCharWidth(A/W/I)、GetCharWidth32(A/W)、GetCharWidthFloat(A/W) 的公共系统服务:批量取得字符的前进宽度(advance width)。
- 实现流程 :①
Count==0 或超 MAX_TEXT_BUFFER/sizeof(INT)(0x2710000≈40MB 上限)直接失败;② 字符数组用栈缓冲(40 个)或池缓冲拷贝(MmCopyFromCaller);③ 输出缓冲同样栈/池两级;④ GreGetCharWidthW(freetype.c 第 7603 行):DC_LockDc → RealizeFontInit 取得 TEXTOBJ → 解锁 DC → ObjToGDI(TextObj->Font, FONT) → IntSelectFaceCharmap → IntLockFreeType → IntRequestFontSize(lfWidth, lfHeight) → FT_Set_Transform(face, NULL, NULL)(忽略 XFORM/Escapement)→ 逐字符 get_glyph_index_flagged(fl & GCW_INDICES 时输入视为字形索引)→ FT_Load_Glyph → 取 face->glyph->advance.x(26.6 定点,(adv+32)>>6 四舍五入到像素)→ fl==0 时按 FLOAT 输出;⑤ MmCopyToCaller 拷回用户缓冲。
- 调用方 :gdi32full 的
GetCharWidthA/W、GetCharWidthI、GetCharWidth32A/W、GetCharWidthFloatA/W 全部映射到本入口。
- 注意事项 :
Count 上限 MAX_TEXT_BUFFER/sizeof(INT) 防溢出;UnSafepwc 可为 NULL(此时按 FirstChar 到 FirstChar+Count-1 连续字符码);GCW_INDICES 标志下 FirstChar 语义变为首字形索引。
3.3.2 NtGdiGetCharABCWidthsW(font.c 第 1374~1433 行)
/* This function is called from GetCharABCWidthsA/W/I and GetCharABCWidthsFloatA/W. */
BOOL NTAPI
NtGdiGetCharABCWidthsW(
_In_ HDC hDC,
_In_ UINT FirstChar,
_In_ UINT Count,
_In_reads_opt_(Count) PCWCH UnSafepwch,
_In_ FLONG fl,
_Out_writes_bytes_(Count * sizeof(ABC)) PVOID Buffer)
{
BOOL ret = FALSE;
PVOID SafeBuff = NULL;
PWCHAR Safepwch = NULL;
ULONG cbABCs;
NTSTATUS Status;
WCHAR Stackpwch[28];
ABC StackABCs[28];
if (!Buffer || (UnSafepwch && !Count) || Count > MAX_TEXT_BUFFER / sizeof(ABC))
return FALSE;
if (UnSafepwch)
{
UINT pwchSize = Count * sizeof(WCHAR);
if (Count <= _countof(Stackpwch))
Safepwch = Stackpwch;
else
Safepwch = ExAllocatePoolWithTag(PagedPool, pwchSize, GDITAG_TEXT);
if (!Safepwch)
return FALSE;
Status = MmCopyFromCaller(Safepwch, UnSafepwch, pwchSize);
if (!NT_SUCCESS(Status))
goto Cleanup;
}
cbABCs = Count * sizeof(ABC);
if (Count <= _countof(StackABCs))
SafeBuff = StackABCs;
else
SafeBuff = ExAllocatePoolWithTag(PagedPool, cbABCs, GDITAG_TEXT);
if (!SafeBuff)
goto Cleanup;
ret = GreGetCharABCWidthsW(hDC, FirstChar, Count, Safepwch, fl, SafeBuff);
if (ret)
{
Status = MmCopyToCaller(Buffer, SafeBuff, cbABCs);
ret = NT_SUCCESS(Status);
}
Cleanup:
if (SafeBuff && SafeBuff != StackABCs)
ExFreePoolWithTag(SafeBuff, GDITAG_TEXT);
if (Safepwch && Safepwch != Stackpwch)
ExFreePoolWithTag(Safepwch, GDITAG_TEXT);
return ret;
}
| 参数 |
类型 |
说明 |
hDC |
HDC |
设备上下文 |
FirstChar |
UINT |
首字符码 |
Count |
UINT |
字符数 |
UnSafepwch |
PCWCH |
可选的字符数组 |
fl |
FLONG |
标志(GCABCW_INDICES;0 表示输出 ABCFLOAT) |
Buffer |
PVOID |
输出缓冲(Count * sizeof(ABC) 或 sizeof(ABCFLOAT)) |
- 作用 :
GetCharABCWidths(A/W/I)、GetCharABCWidthsFloat(A/W) 的系统服务:批量取得字符的 A(左留白)/B(字形本体)/C(右留白) 三段宽度。
- 实现流程 :① 校验(
!Buffer 直接失败、UnSafepwch && !Count 失败、超上限失败);② 字符数组栈(28)/池拷贝;③ 输出缓冲栈(28 个 ABC)/池分配;④ GreGetCharABCWidthsW(freetype.c 第 7497 行):锁 DC → RealizeFontInit → IntSelectFaceCharmap → IntRequestFontSize + FT_Set_Transform(face, NULL, NULL) → 逐字符 FT_Load_Glyph 后计算:
left = horiBearingX & -64(26.6 向下取整)
right = ((horiBearingX + width) + 63) & -64
adv = (advance.x + 32) >> 6
lsb = left>>6、bbx = (right-left)>>6、C = adv - lsb - bbx
fl==0 输出 ABCFLOAT,否则输出 ABC(GCABCW_INDICES 时输入当字形索引);⑤ MmCopyToCaller 拷回。
- 调用方 :gdi32full 的
GetCharABCWidthsA/W、GetCharABCWidthsI、GetCharABCWidthsFloatA/W。
- 注意事项 :注意
fl 同时承担两个语义------"输出 FLOAT 还是 INT"和"输入是字形索引"。Buffer 必须非空(与 GetCharWidth 不同,这里没有"仅查询大小"模式)。
3.3.3 NtGdiGetGlyphIndicesW / NtGdiGetGlyphIndicesWInternal(font.c 第 1436~1509 行)
/* This function is called from GetGlyphIndicesA/W */
__kernel_entry
W32KAPI
DWORD
APIENTRY
NtGdiGetGlyphIndicesW(
_In_ HDC hdc,
_In_reads_opt_(cwc) PCWCH pwc,
_In_ INT cwc,
_Out_writes_opt_(cwc) PWORD pgi,
_In_ DWORD iMode)
{
return NtGdiGetGlyphIndicesWInternal(hdc, pwc, cwc, pgi, iMode, FALSE);
}
__kernel_entry
W32KAPI
DWORD
APIENTRY
NtGdiGetGlyphIndicesWInternal(
_In_ HDC hdc,
_In_reads_opt_(cwc) PCWCH pwc,
_In_ INT cwc,
_Out_writes_opt_(cwc) PWORD pgi,
_In_ DWORD iMode,
_In_ BOOL bSubset)
{
if (cwc < 0)
return GDI_ERROR;
if (!cwc && !pwc && !pgi && !iMode)
return GreGetGlyphIndicesW(hdc, NULL, 0, NULL, 0, bSubset);
if (cwc == 0)
return GDI_ERROR;
const SIZE_T elemSize = sizeof(WORD) + sizeof(WCHAR);
if ((SIZE_T)cwc > (MAX_TEXT_BUFFER / elemSize))
return GDI_ERROR;
// Allocate a working buffer (for pgi and pwc)
WORD stackBuf[80];
PWORD workBuf = NULL;
if ((SIZE_T)cwc <= (sizeof(stackBuf) / elemSize))
{
workBuf = stackBuf;
}
else
{
SIZE_T workBufSize = cwc * elemSize;
workBuf = ExAllocatePoolWithTag(PagedPool, workBufSize, GDITAG_TEXT);
if (!workBuf)
return GDI_ERROR;
}
DWORD ret = GDI_ERROR;
PWCHAR safePwc = (PWCHAR)&workBuf[cwc];
NTSTATUS Status = MmCopyFromCaller(safePwc, pwc, cwc * sizeof(WCHAR));
if (!NT_SUCCESS(Status))
goto Cleanup;
ret = GreGetGlyphIndicesW(hdc, safePwc, cwc, workBuf, iMode, bSubset);
if (ret == GDI_ERROR)
goto Cleanup;
Status = MmCopyToCaller(pgi, workBuf, cwc * sizeof(WORD));
if (!NT_SUCCESS(Status))
ret = GDI_ERROR;
Cleanup:
if (workBuf != stackBuf)
ExFreePoolWithTag(workBuf, GDITAG_TEXT);
return ret;
}
| 参数 |
类型 |
说明 |
hdc |
HDC |
设备上下文 |
pwc |
PCWCH |
字符数组(用户缓冲) |
cwc |
INT |
字符数 |
pgi |
PWORD |
输出字形索引数组 |
iMode |
DWORD |
标志:GGI_MARK_NONEXISTING_GLYPHS 等 |
bSubset |
BOOL |
子集化标志(Internal 版本独有,目前 FIXME 未实现) |
- 作用 :
GetGlyphIndices(A/W) 的系统服务:把 Unicode 字符码数组转换为字形索引(glyph index)数组 。外层 NtGdiGetGlyphIndicesW 仅是 Internal 版本 bSubset=FALSE 的薄包装。
- 实现流程 (Internal):①
cwc<0 返回 GDI_ERROR;② 特殊查询:!cwc && !pwc && !pgi && !iMode 时调用 GreGetGlyphIndicesW(hdc, NULL, 0, NULL, 0, bSubset)------该情形返回字体字形总数 Face->num_glyphs(freetype.c 第 7750 行);③ cwc==0(但其他参数非空)返回 GDI_ERROR;④ 上限检查 cwc > MAX_TEXT_BUFFER/(sizeof(WORD)+sizeof(WCHAR));⑤ 单一工作缓冲:前 cwc 个 WORD 放 pgi、后 cwc 个 WCHAR 放 pwc(栈 80 元素或池分配);⑥ MmCopyFromCaller 拷入字符;⑦ GreGetGlyphIndicesW(freetype.c 第 7716 行):锁 DC → RealizeFontInit → ObjToGDI → 若无 GGI_MARK_NONEXISTING_GLYPHS 则 IntGetFontDefaultChar 取默认字符(OS/2 的 usDefaultChar 或 WinFNT 的 default_char)并转成字形索引,然后逐字符 get_glyph_index(symbol 字体走 get_glyph_index_symbol 的 U+F0XX 映射),未命中(索引 0)的替换为默认字形索引;⑧ MmCopyToCaller 拷回 WORD 数组。
- 调用方 :gdi32full 的
GetGlyphIndicesW/A;GreGetCharacterPlacementW 内部也调用 GreGetGlyphIndicesW。
- 注意事项 :源码注释明确"Windows GetGlyphIndices 不支持代理对(surrogate pairs)"------高位/低位代理会各自独立查询;
bSubset 目前是 FIXME(未实现子集化语义);字符与输出共用一块缓冲是刻意优化(workBuf 前部是 pgi,后部是 pwc,safePwc = &workBuf[cwc])。
3.3.4 GreGetKerningPairs(font.c 第 53~109 行)
DWORD
FASTCALL
GreGetKerningPairs(
_In_ HDC hDC,
_In_ ULONG NumPairs,
_Out_writes_(NumPairs) LPKERNINGPAIR krnpair)
{
PDC dc;
PDC_ATTR pdcattr;
PTEXTOBJ TextObj;
PFONTGDI FontGDI;
DWORD Count;
KERNINGPAIR *pKP;
dc = DC_LockDc(hDC);
if (!dc)
{
EngSetLastError(ERROR_INVALID_HANDLE);
return 0;
}
pdcattr = dc->pdcattr;
TextObj = RealizeFontInit(pdcattr->hlfntNew);
DC_UnlockDc(dc);
if (!TextObj)
{
EngSetLastError(ERROR_INVALID_HANDLE);
return 0;
}
FontGDI = ObjToGDI(TextObj->Font, FONT);
TEXTOBJ_UnlockText(TextObj);
Count = ftGdiGetKerningPairs(FontGDI,0,NULL);
if ( Count && krnpair )
{
if (Count > NumPairs)
{
EngSetLastError(ERROR_INSUFFICIENT_BUFFER);
return 0;
}
pKP = ExAllocatePoolWithTag(PagedPool, Count * sizeof(KERNINGPAIR), GDITAG_TEXT);
if (!pKP)
{
EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
return 0;
}
ftGdiGetKerningPairs(FontGDI,Count,pKP);
RtlCopyMemory(krnpair, pKP, Count * sizeof(KERNINGPAIR));
ExFreePoolWithTag(pKP,GDITAG_TEXT);
}
return Count;
}
| 参数 |
类型 |
说明 |
hDC |
HDC |
设备上下文 |
NumPairs |
ULONG |
调用者缓冲可容纳的 KERNINGPAIR 数 |
krnpair |
LPKERNINGPAIR |
输出缓冲(可为 NULL 仅查询数量) |
- 作用 :
GetKerningPairs 的内核内部版 (Gre 层):取得当前字体全部字距对(kerning pair)。先查数量,再填数据。
- 实现流程 :① 锁 DC →
RealizeFontInit → 解 DC;② ObjToGDI 取 FONTGDI → TEXTOBJ_UnlockText(注意:先解锁文本对象,后续仅凭 FONTGDI 指针工作,这是本函数与大多数函数不同的锁纪律);③ ftGdiGetKerningPairs(FontGDI, 0, NULL) 先取数量;④ 数量超 NumPairs 报 ERROR_INSUFFICIENT_BUFFER;⑤ 分配池缓冲 → ftGdiGetKerningPairs(FontGDI, Count, pKP) 填充 → RtlCopyMemory 拷回用户缓冲(此处无 SEH/Probe 保护,是内部版本前提)→ 释放。
- 调用方 :ntuser 的
NtGdiGetKerningPairs 服务包装后调用(见 3.3.5);GreGetCharacterPlacementW 的 GCP_USEKERNING 分支也调用它。
- 注意事项 :
ftGdiGetKerningPairs(freetype.c 第 6449 行)只在 FT_HAS_KERNING(face) 且 charmap 为 FT_ENCODING_UNICODE 时工作:用 FT_Get_First_Char/FT_Get_Next_Char 遍历全部字符,对相邻字符对调用 FT_Get_Kerning,输出 wFirst/wSecond/iKernAmount(delta.x)。注意该函数把字符码存进 wFirst/wSecond(不是字形索引)。
3.3.5 NtGdiGetKerningPairs(font.c 第 888~957 行)
DWORD
APIENTRY
NtGdiGetKerningPairs(
_In_ HDC hDC,
_In_ ULONG NumPairs,
_Out_writes_(NumPairs) LPKERNINGPAIR krnpair)
{
PDC dc;
PDC_ATTR pdcattr;
PTEXTOBJ TextObj;
PFONTGDI FontGDI;
DWORD Count;
KERNINGPAIR *pKP;
NTSTATUS Status = STATUS_SUCCESS;
dc = DC_LockDc(hDC);
if (!dc)
{
EngSetLastError(ERROR_INVALID_HANDLE);
return 0;
}
pdcattr = dc->pdcattr;
TextObj = RealizeFontInit(pdcattr->hlfntNew);
DC_UnlockDc(dc);
if (!TextObj)
{
EngSetLastError(ERROR_INVALID_HANDLE);
return 0;
}
FontGDI = ObjToGDI(TextObj->Font, FONT);
TEXTOBJ_UnlockText(TextObj);
Count = ftGdiGetKerningPairs(FontGDI,0,NULL);
if ( Count && krnpair )
{
if (Count > NumPairs)
{
EngSetLastError(ERROR_INSUFFICIENT_BUFFER);
return 0;
}
pKP = ExAllocatePoolWithTag(PagedPool, Count * sizeof(KERNINGPAIR), GDITAG_TEXT);
if (!pKP)
{
EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
return 0;
}
ftGdiGetKerningPairs(FontGDI,Count,pKP);
_SEH2_TRY
{
ProbeForWrite(krnpair, Count * sizeof(KERNINGPAIR), 1);
RtlCopyMemory(krnpair, pKP, Count * sizeof(KERNINGPAIR));
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END
if (!NT_SUCCESS(Status))
{
EngSetLastError(ERROR_INVALID_PARAMETER);
Count = 0;
}
ExFreePoolWithTag(pKP,GDITAG_TEXT);
}
return Count;
}
| 参数 |
类型 |
说明 |
hDC |
HDC |
设备上下文 |
NumPairs |
ULONG |
缓冲容量 |
krnpair |
LPKERNINGPAIR |
输出缓冲(用户态,需 Probe) |
- 作用 :
GetKerningPairs 的系统服务版 :与 GreGetKerningPairs 逻辑几乎一致,区别在于把拷贝包进 ProbeForWrite + _SEH2_TRY,异常时置 ERROR_INVALID_PARAMETER 并清零 Count。
- 实现流程:同 3.3.4 的 ①~④,仅拷贝步骤带 SEH。
- 调用方 :gdi32full 的
GetKerningPairsW/A。
- 注意事项 :与 Gre 版一样先
TEXTOBJ_UnlockText 再使用 FONTGDI------依赖 FONTGDI 在持有 FreeType 锁期间(ftGdiGetKerningPairs 内部 IntLockFreeType)不会被释放的隐含前提。
3.3.6 NtGdiGetCharacterPlacementW(font.c 第 650~670 行,UNIMPLEMENTED)
/*
* @unimplemented
*/
DWORD
APIENTRY
NtGdiGetCharacterPlacementW(
_In_ HDC hdc,
_In_reads_(nCount) PCWCH pwsz,
_In_ INT nCount,
_In_ INT nMaxExtent,
_Inout_opt_ LPGCP_RESULTSW pgcpw,
_In_ DWORD dwFlags)
{
UNIMPLEMENTED;
return 0;
#if 0
return GreGetCharacterPlacementW( hdc,
pwsz,
nCount,
nMaxExtent,
pgcpw,
dwFlags);
#endif
}
| 参数 |
类型 |
说明 |
hdc |
HDC |
设备上下文 |
pwsz |
PCWCH |
输入字符串 |
nCount |
INT |
字符数 |
nMaxExtent |
INT |
最大范围 |
pgcpw |
LPGCP_RESULTSW |
GCP 结果结构(输出) |
dwFlags |
DWORD |
GCP_* 标志(GCP_JUSTIFY/GCP_USEKERNING/GCP_MAXEXTENT......) |
- 作用 :
GetCharacterPlacementW 的系统服务。当前是 UNIMPLEMENTED stub :UNIMPLEMENTED 宏打 WARNING: ... is UNIMPLEMENTED! 日志后返回 0;#if 0 中保留了调用 GreGetCharacterPlacementW 的骨架。
- 实现流程 :stub 无流程;恢复路径(
#if 0)为转发 GreGetCharacterPlacementW。
- 调用方 :gdi32full 的
GetCharacterPlacementW(复杂文本布局:调整位置、获取字形索引、分类、kashida 等)。
- 注意事项 :这是字体子系统中少数未实现 的 API,直接影响富文本/Uniscribe 回退路径(应用会走
GetTextExtentEx + 自绘或 GDI+)。
3.3.7 GreGetCharacterPlacementW(font.c 第 122~294 行,#if 0 禁用)
#if 0
DWORD
FASTCALL
GreGetCharacterPlacementW(
_In_ HDC hdc,
_In_reads_(nCount) PCWCH pwsz,
_In_ INT nCount,
_In_ INT nMaxExtent,
_Inout_opt_ LPGCP_RESULTSW pgcpw,
_In_ DWORD dwFlags)
{
GCP_RESULTSW gcpwSave;
UINT i, nSet, cSet;
INT *tmpDxCaretPos;
LONG Cx;
SIZE Size = {0,0};
...
}
#endif
- 作用 :
GreGetCharacterPlacementW 曾经是 GCP 的硬编码实现 ,现整个函数体被 #if 0 禁用,仅保留参考价值。文件头注释(font.c 第 111~121 行)说明了背景:MS 对每种语言发布内置的 GCP 支持;ReactOS 基于 FreeType,"这需要被支持",当时该实现硬编码 GCPCLASS_LATIN。
- 实现流程 (被禁用的旧实现):①
pgcpw==NULL 时退化为 GreGetTextExtentW 返回 MAKELONG(cx,cy);② 保存 gcpwSave,按 nGlyphs 截断 cSet;③ GCP_JUSTIFY 自动加 GCP_MAXEXTENT;④ 调 GreGetTextExtentExW 计算逐字符 Dx 并累计成 caret 位置;⑤ 处理 GCP_USEKERNING(调 GreGetKerningPairs 取字距表调整)、GCP_MAXEXTENT 截断、GCP_JUSTIFY(两处" We Need to Do Something HERE!" 未实现占位);⑥ 填 lpOutString(原样拷贝)、lpClass(全 GCPCLASS_LATIN)、lpOrder(恒等排列)、lpGlyphs(调 GreGetGlyphIndicesW)。
- 调用方 :被
NtGdiGetCharacterPlacementW 的 #if 0 分支引用(未启用)。
- 注意事项:该代码是"如何用宽度+字形索引组合出 GCP 语义"的教学性参考;真实实现应在 freetype.c 侧提供(利用 FT_Get_Advance/字形布局)。
3.3.8 IntGetCharDimensions(font.c 第 335~377 行)
DWORD
FASTCALL
IntGetCharDimensions(
_In_ HDC hdc,
_Out_opt_ PTEXTMETRICW ptm,
_Out_opt_ PDWORD height)
{
PDC pdc;
PDC_ATTR pdcattr;
PTEXTOBJ TextObj;
SIZE sz;
TMW_INTERNAL tmwi;
BOOL Good;
static const WCHAR alphabet[] = {
'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q',
'r','s','t','u','v','w','x','y','z','A','B','C','D','E','F','G','H',
'I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',0};
if(!ftGdiGetTextMetricsW(hdc, &tmwi)) return 0;
pdc = DC_LockDc(hdc);
if (!pdc) return 0;
pdcattr = pdc->pdcattr;
TextObj = RealizeFontInit(pdcattr->hlfntNew);
if ( !TextObj )
{
DC_UnlockDc(pdc);
return 0;
}
Good = TextIntGetTextExtentPoint(pdc, TextObj, alphabet, 52, 0, NULL, NULL, &sz, 0);
TEXTOBJ_UnlockText(TextObj);
DC_UnlockDc(pdc);
if (!Good) return 0;
if (ptm) *ptm = tmwi.TextMetric;
if (height) *height = tmwi.TextMetric.tmHeight;
return (sz.cx / 26 + 1) / 2;
}
| 参数 |
类型 |
说明 |
hdc |
HDC |
设备上下文 |
ptm |
PTEXTMETRICW |
可选输出 TEXTMETRICW |
height |
PDWORD |
可选输出 tmHeight |
- 作用 :计算平均字符宽度 (
GetCharDimensions 语义):用 52 个小写+大写字母组成的标准串测宽,(cx/26+1)/2 得平均宽;顺带输出 TEXTMETRIC 与行高。
- 实现流程 :①
ftGdiGetTextMetricsW 取度量(freetype.c 第 5335 行:锁 DC → RealizeFontInit → IntRequestFontSize → FT_Set_Transform(face,NULL,NULL) → 填充 TMW_INTERNAL,内部再根据 OS/2 表 FillTM);② 锁 DC → RealizeFontInit;③ TextIntGetTextExtentPoint(freetype.c 第 5028 行,52 字符无 MaxExtent);④ 解锁,输出可选参数,返回平均字符宽度。
- 调用方 :ntuser/win32k 内部(对话框字体度量、
GetDialogBaseUnits 相关路径)。
- 注意事项 :alphabet 串以 'a'...'z','A'...'Z' 共 52 字符测量,
(cx/26+1)/2 是"每 26 个字符宽度折算平均 + 上取整"的经验公式(cx 是 52 字符总宽,cx/26 是 26 个字符的平均宽,再加 1 除 2 与 Windows 的 GetCharDimensions 一致)。
3.3.9 IntGetFontLanguageInfo(font.c 第 380~429 行)
DWORD
FASTCALL
IntGetFontLanguageInfo(_In_ PDC Dc)
{
PDC_ATTR pdcattr;
FONTSIGNATURE fontsig;
static const DWORD GCP_DBCS_MASK=0x003F0000,
GCP_DIACRITIC_MASK=0x00000000,
FLI_GLYPHS_MASK=0x00000000,
GCP_GLYPHSHAPE_MASK=0x00000040,
GCP_KASHIDA_MASK=0x00000000,
GCP_LIGATE_MASK=0x00000000,
GCP_USEKERNING_MASK=0x00000000,
GCP_REORDER_MASK=0x00000060;
DWORD result=0;
ftGdiGetTextCharsetInfo( Dc, &fontsig, 0 );
/* We detect each flag we return using a bitmask on the Codepage Bitfields */
if( (fontsig.fsCsb[0]&GCP_DBCS_MASK)!=0 )
result|=GCP_DBCS;
if( (fontsig.fsCsb[0]&GCP_DIACRITIC_MASK)!=0 )
result|=GCP_DIACRITIC;
if( (fontsig.fsCsb[0]&FLI_GLYPHS_MASK)!=0 )
result|=FLI_GLYPHS;
if( (fontsig.fsCsb[0]&GCP_GLYPHSHAPE_MASK)!=0 )
result|=GCP_GLYPHSHAPE;
if( (fontsig.fsCsb[0]&GCP_KASHIDA_MASK)!=0 )
result|=GCP_KASHIDA;
if( (fontsig.fsCsb[0]&GCP_LIGATE_MASK)!=0 )
result|=GCP_LIGATE;
if( (fontsig.fsCsb[0]&GCP_USEKERNING_MASK)!=0 )
result|=GCP_USEKERNING;
pdcattr = Dc->pdcattr;
/* This might need a test for a HEBREW- or ARABIC_CHARSET as well */
if ( pdcattr->flTextAlign & TA_RTLREADING )
if( (fontsig.fsCsb[0]&GCP_REORDER_MASK)!=0 )
result|=GCP_REORDER;
return result;
}
- 作用 :
GetFontLanguageInfo 的内核实现:根据字体的 FONTSIGNATURE 代码页位图(fsCsb0) 与一组掩码按位与,报告该字体支持的 GCP/FLI 特性(GCP_DBCS、GCP_GLYPHSHAPE、GCP_REORDER 等)。
- 实现流程 :①
ftGdiGetTextCharsetInfo(Dc, &fontsig, 0) 取 FONTSIGNATURE(freetype.c 第 5148 行:从字体面名/OS2 表映射字符集与代码页位);② 依次与各掩码比较;③ TA_RTLREADING 时叠加 GCP_REORDER(掩码 0x60,即阿拉伯/希伯来双向重排位)。
- 调用方 :ntuser 的
NtGdiGetFontLanguageInfo。
- 注意事项:掩码是"每个特性对应代码页位图中的一个位段"的静态近似;多数掩码为 0(GCP_DIACRITIC/FLI_GLYPHS/GCP_KASHIDA/GCP_LIGATE/GCP_USEKERNING 恒为 0),因此这些特性对当前所有字体都不报告------这是刻意保守的"不支持就不声明"策略,应用会据此走简单路径。
3.3.10 NtGdiGetGlyphOutline(font.c 第 803~886 行)
ULONG
APIENTRY
NtGdiGetGlyphOutline(
_In_ HDC hdc,
_In_ WCHAR wch,
_In_ UINT iFormat,
_Out_ LPGLYPHMETRICS pgm,
_In_ ULONG cjBuf,
_Out_writes_bytes_opt_(cjBuf) PVOID UnsafeBuf,
_In_opt_ const MAT2 *pmat2,
_In_ BOOL bIgnoreRotation)
{
ULONG Ret = GDI_ERROR;
PDC dc;
PVOID pvBuf = NULL;
GLYPHMETRICS gm;
NTSTATUS Status = STATUS_SUCCESS;
dc = DC_LockDc(hdc);
if (!dc)
{
EngSetLastError(ERROR_INVALID_HANDLE);
return GDI_ERROR;
}
if (UnsafeBuf && cjBuf)
{
pvBuf = ExAllocatePoolZero(PagedPool, cjBuf, GDITAG_TEXT);
if (!pvBuf)
{
EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
goto Exit;
}
}
Ret = ftGdiGetGlyphOutline( dc,
wch,
iFormat,
pgm ? &gm : NULL,
cjBuf,
pvBuf,
pmat2,
bIgnoreRotation);
if (pvBuf)
{
_SEH2_TRY
{
ProbeForWrite(UnsafeBuf, cjBuf, 1);
RtlCopyMemory(UnsafeBuf, pvBuf, cjBuf);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END
ExFreePoolWithTag(pvBuf, GDITAG_TEXT);
}
if (pgm)
{
_SEH2_TRY
{
ProbeForWrite(pgm, sizeof(GLYPHMETRICS), 1);
RtlCopyMemory(pgm, &gm, sizeof(GLYPHMETRICS));
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END
}
if (! NT_SUCCESS(Status))
{
EngSetLastError(ERROR_INVALID_PARAMETER);
Ret = GDI_ERROR;
}
Exit:
DC_UnlockDc(dc);
return Ret;
}
| 参数 |
类型 |
说明 |
hdc |
HDC |
设备上下文 |
wch |
WCHAR |
字符(或 GGO_GLYPH_INDEX 时的字形索引) |
iFormat |
UINT |
GGO_METRICS/GGO_BITMAP/GGO_NATIVE/GGO_GLYPH_INDEX/GGO_UNHINTED 组合 |
pgm |
LPGLYPHMETRICS |
输出字形度量 |
cjBuf |
ULONG |
输出缓冲字节数(0 表示仅查大小) |
UnsafeBuf |
PVOID |
轮廓/位图数据缓冲(用户态) |
pmat2 |
const MAT2 * |
附加 2x2 变换矩阵 |
bIgnoreRotation |
BOOL |
忽略旋转标志 |
- 作用 :
GetGlyphOutline 的系统服务:取得单个字形的轮廓数据(GGO_NATIVE 的 TTPOLYGONHEADER 多边形链) 、位图(GGO_BITMAP)或仅度量(GGO_METRICS),并支持可选变换矩阵 pmat2。
- 实现流程 :① 锁 DC;②
UnsafeBuf && cjBuf 时 ExAllocatePoolZero 分配内核缓冲(零初始化);③ ftGdiGetGlyphOutline(freetype.c 第 4500 行,细节见 5.3):RealizeFontInit → 取 OTM(用于平均宽度比例)→ IntLockFreeType → TextIntUpdateSize → IntMatrixFromMx(世界→设备变换)+ FT_Set_Transform → get_glyph_index_flagged(iFormat & GGO_GLYPH_INDEX 时 wch 直接当索引)→ FT_Load_Glyph(GGO_UNHINTED 加 FT_LOAD_NO_HINTING)→ 计算 26.6 定点 box(horiBearingX/width/height)→ 若需变换(宽度比例/世界变换/旋转 IntEscapeMatrix/pmat2)则 FT_Vector_Transform 四个角点求外接盒 → 填 GLYPHMETRICS(gmCellIncX/Y、gmBlackBoxX/Y、gmptGlyphOrigin)→ GGO_METRICS 直接返回;GGO_BITMAP 时 FT_Render_Glyph 后按 ((width+31)>>5)<<2 对齐的 pitch 拷贝;④ 两个 SEH 块分别把 pvBuf 与 gm 拷回用户缓冲;⑤ 任一失败置 ERROR_INVALID_PARAMETER 返回 GDI_ERROR。
- 调用方 :gdi32full 的
GetGlyphOutline(GDI+、文字转曲线、打印矢量输出等)。
- 注意事项 :
cjBuf==0 且 UnsafeBuf==NULL 时返回所需字节数 (第一次调用查大小、第二次取数据的双趟模式);GGO_NATIVE 在当前实现下同样依赖 FT_Load_Glyph 的 outline 数据,若字形是位图字形(ft_glyph_format_bitmap)且非 GGO_BITMAP 会返回 GDI_ERROR(freetype.c 第 4770~4774 行)。
3.3.11 NtGdiGetFontData(font.c 第 672~729 行)
DWORD
APIENTRY
NtGdiGetFontData(
_In_ HDC hDC,
_In_ DWORD Table,
_In_ DWORD Offset,
_Out_writes_bytes_(Size) PVOID Buffer,
_In_ DWORD Size)
{
PDC Dc;
PDC_ATTR pdcattr;
HFONT hFont;
PTEXTOBJ TextObj;
PFONTGDI FontGdi;
DWORD Result = GDI_ERROR;
NTSTATUS Status = STATUS_SUCCESS;
if (Buffer && Size)
{
_SEH2_TRY
{
ProbeForRead(Buffer, Size, 1);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END
}
if (!NT_SUCCESS(Status)) return Result;
Dc = DC_LockDc(hDC);
if (Dc == NULL)
{
EngSetLastError(ERROR_INVALID_HANDLE);
return GDI_ERROR;
}
pdcattr = Dc->pdcattr;
hFont = pdcattr->hlfntNew;
TextObj = RealizeFontInit(hFont);
DC_UnlockDc(Dc);
if (TextObj == NULL)
{
EngSetLastError(ERROR_INVALID_HANDLE);
return GDI_ERROR;
}
FontGdi = ObjToGDI(TextObj->Font, FONT);
Result = ftGdiGetFontData(FontGdi, Table, Offset, Buffer, Size);
TEXTOBJ_UnlockText(TextObj);
return Result;
}
| 参数 |
类型 |
说明 |
hDC |
HDC |
设备上下文 |
Table |
DWORD |
sfnt 表标签(四字符码,如 'name'/'OS/2';0 表示整文件) |
Offset |
DWORD |
表内偏移 |
Buffer |
PVOID |
输出缓冲(Buffer==NULL 时返回所需字节数) |
Size |
DWORD |
缓冲大小 |
- 作用 :
GetFontData 的系统服务:直接从字体文件取 sfnt 表原始字节 (无需解析),Table==0 时取整个字体文件内容;常用于嵌入字体(PDF/打印)与字体工具。
- 实现流程 :① 若
Buffer && Size 先 ProbeForRead(注意:是读 探测,函数实际只写 Buffer,这是保守的双向探测);② 锁 DC → RealizeFontInit → 解 DC;③ ObjToGDI 取 FONTGDI → ftGdiGetFontData(freetype.c 第 5433 行):IntLockFreeType → 仅当 FT_IS_SFNT(Face) 时工作,Table 若非零做字节序翻转 (Table>>24 | Table<<24 | (Table>>8 & 0xFF00) | (Table<<8 & 0xFF0000),把 little-endian 传入值转成 FreeType 期望的 big-endian 标签)→ Buffer==NULL 时 Size=0 → 首次 FT_Load_Sfnt_Table(Face, Table, Offset, NULL, &Needed) 查所需大小(Needed < Size 时截断)→ 再次 FT_Load_Sfnt_Table 取数据,返回实际字节数;非 sfnt 字体返回 GDI_ERROR;④ 解锁文本对象。
- 调用方 :gdi32full 的
GetFontData。
- 注意事项 :返回值
GDI_ERROR(0xFFFFFFFF)表示失败;Table 的四字符码在调用者侧是 'n''a''m''e' 拼成的 DWORD(little-endian 内存序),故内核做字节序翻转;Buffer==NULL 且 Size==0 时返回整表所需大小(双趟模式)。
3.3.12 NtGdiGetFontUnicodeRanges(font.c 第 731~801 行)
/* @implemented */
DWORD
APIENTRY
NtGdiGetFontUnicodeRanges(
_In_ HDC hdc,
_Out_opt_ LPGLYPHSET pgs)
{
PDC pDc;
PDC_ATTR pdcattr;
HFONT hFont;
PTEXTOBJ TextObj;
PFONTGDI FontGdi;
DWORD Size = 0;
PGLYPHSET pgsSafe;
NTSTATUS Status = STATUS_SUCCESS;
pDc = DC_LockDc(hdc);
if (!pDc)
{
EngSetLastError(ERROR_INVALID_HANDLE);
return 0;
}
pdcattr = pDc->pdcattr;
hFont = pdcattr->hlfntNew;
TextObj = RealizeFontInit(hFont);
if ( TextObj == NULL)
{
EngSetLastError(ERROR_INVALID_HANDLE);
goto Exit;
}
FontGdi = ObjToGDI(TextObj->Font, FONT);
Size = ftGetFontUnicodeRanges( FontGdi, NULL);
if (Size && pgs)
{
pgsSafe = ExAllocatePoolWithTag(PagedPool, Size, GDITAG_TEXT);
if (!pgsSafe)
{
EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
Size = 0;
goto Exit;
}
Size = ftGetFontUnicodeRanges( FontGdi, pgsSafe);
if (Size)
{
_SEH2_TRY
{
ProbeForWrite(pgs, Size, 1);
RtlCopyMemory(pgs, pgsSafe, Size);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END
if (!NT_SUCCESS(Status)) Size = 0;
}
ExFreePoolWithTag(pgsSafe, GDITAG_TEXT);
}
Exit:
TEXTOBJ_UnlockText(TextObj);
DC_UnlockDc(pDc);
return Size;
}
| 参数 |
类型 |
说明 |
hdc |
HDC |
设备上下文 |
pgs |
LPGLYPHSET |
输出 GLYPHSET(可为 NULL 仅查大小) |
- 作用 :
GetFontUnicodeRanges 的系统服务:返回字体覆盖的 Unicode 范围(WCRANGE 数组),即哪些码点有字形。
- 实现流程 :① 锁 DC →
RealizeFontInit(注意这里先不解锁 DC,TextObj 判空后 goto Exit 统一解锁);② ftGetFontUnicodeRanges(FontGdi, NULL) 先算大小(freetype.c 第 5261 行):要求 charmap 为 FT_ENCODING_UNICODE;用 FT_Get_First_Char/FT_Get_Next_Char 遍历字体所有字符,遇到 char_code - char_code_prev > 1 即开新 range(wcLow=char_code, cGlyphs=1),连续则 cGlyphs++;size = sizeof(GLYPHSET) + sizeof(WCRANGE)*(num_ranges-1);③ pgs 非空时分配池缓冲二趟填充;④ SEH ProbeForWrite 拷回;⑤ Exit 统一 TEXTOBJ_UnlockText + DC_UnlockDc。
- 调用方 :gdi32full 的
GetFontUnicodeRanges(字体选择对话框、文本渲染器的 fallback 判定)。
- 注意事项 :非 Unicode charmap 的字体返回 0 且打印 "Encoding %i not supported";
cGlyphsSupported 与 ranges[0].cGlyphs 在首 range 初始化时置 0 后逐项累加(首个 range 的 wcLow 在 glyphset 分支外才设置,注意 ranges[0].cGlyphs 先 0 后 +1 的边界处理)。
3.3.13 NtGdiGetOutlineTextMetricsInternalW(font.c 第 963~1038 行)
/*
From "Undocumented Windows 2000 Secrets" Appendix B, Table B-2, page
472, this is NtGdiGetOutlineTextMetricsInternalW.
*/
ULONG
APIENTRY
NtGdiGetOutlineTextMetricsInternalW(
_In_ HDC hDC,
_In_ ULONG Data,
_Out_opt_ POUTLINETEXTMETRICW otm,
_In_ PTMDIFF Tmd)
{
PDC dc;
PDC_ATTR pdcattr;
PTEXTOBJ TextObj;
PFONTGDI FontGDI;
HFONT hFont = 0;
ULONG Size;
OUTLINETEXTMETRICW *potm;
NTSTATUS Status = STATUS_SUCCESS;
dc = DC_LockDc(hDC);
if (!dc)
{
EngSetLastError(ERROR_INVALID_HANDLE);
return 0;
}
pdcattr = dc->pdcattr;
hFont = pdcattr->hlfntNew;
TextObj = RealizeFontInit(hFont);
DC_UnlockDc(dc);
if (!TextObj)
{
EngSetLastError(ERROR_INVALID_HANDLE);
return 0;
}
FontGDI = ObjToGDI(TextObj->Font, FONT);
if (!(FontGDI->flType & FO_TYPE_TRUETYPE))
{
TEXTOBJ_UnlockText(TextObj);
return 0;
}
TextIntUpdateSize(TextObj, FontGDI, TRUE);
TEXTOBJ_UnlockText(TextObj);
Size = IntGetOutlineTextMetrics(FontGDI, 0, NULL, FALSE);
if (!otm) return Size;
if (Size > Data)
{
EngSetLastError(ERROR_INSUFFICIENT_BUFFER);
return 0;
}
potm = ExAllocatePoolWithTag(PagedPool, Size, GDITAG_TEXT);
if (!potm)
{
EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
return 0;
}
RtlZeroMemory(potm, Size);
IntGetOutlineTextMetrics(FontGDI, Size, potm, FALSE);
_SEH2_TRY
{
ProbeForWrite(otm, Size, 1);
RtlCopyMemory(otm, potm, Size);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END
if (!NT_SUCCESS(Status))
{
EngSetLastError(ERROR_INVALID_PARAMETER);
Size = 0;
}
ExFreePoolWithTag(potm,GDITAG_TEXT);
return Size;
}
| 参数 |
类型 |
说明 |
hDC |
HDC |
设备上下文 |
Data |
ULONG |
调用者缓冲大小 |
otm |
POUTLINETEXTMETRICW |
输出 OUTLINETEXTMETRICW(NULL 仅查大小) |
Tmd |
PTMDIFF |
度量差(未使用) |
- 作用 :
GetOutlineTextMetrics 的系统服务:返回 OUTLINETEXTMETRICW (含 em 方、上升/下降、panose、下标/上标尺寸等轮廓字体专属度量)。仅对 TrueType(FO_TYPE_TRUETYPE)有效。
- 实现流程 :① 锁 DC →
RealizeFontInit → 解 DC;② flType & FO_TYPE_TRUETYPE 检查,非 TT 返回 0;③ TextIntUpdateSize(TextObj, FontGDI, TRUE) 确保字体尺寸生效(内部 IntLockFreeType/IntUnLockFreeType);④ IntGetOutlineTextMetrics(FontGDI, 0, NULL, FALSE) 两趟:第一趟返回大小(利用 SHARED_FACE_CACHE.OutlineRequiredSize 缓存,见 5.4);⑤ otm==NULL 直接返回大小;Size > Data 报 ERROR_INSUFFICIENT_BUFFER;⑥ 分配缓冲 → 二趟填数据 → SEH 拷回。
- 调用方 :gdi32full 的
GetOutlineTextMetrics(Uniscribe、打印子系统)。
- 注意事项 :
Tmd(TMDIFF)未使用;IntGetOutlineTextMetrics(freetype.c 第 3018 行)从 FT_Get_Sfnt_Table(Face, FT_SFNT_OS2/HHEA/POST) 与 FT_Get_WinFNT_Header 取 OS/2、hhea、post 表,SCALE_X/SCALE_Y 用 FT_MulFix(value, XScale) 按 26.6 缩放;按 PRIMARYLANGID(gusLanguageID) 选择 EnglishUS 或 UserLanguage 缓存槽。
3.3.14 NtGdiGetFontResourceInfoInternalW(font.c 第 1040~1150 行)
W32KAPI
BOOL
APIENTRY
NtGdiGetFontResourceInfoInternalW(
_In_reads_(cwc) PCWCH pwszFiles,
_In_ ULONG cwc,
_In_ ULONG cFiles,
_In_ UINT cjIn,
_Inout_ PDWORD pdwBytes,
_Out_writes_bytes_(*pdwBytes) PVOID pvBuf,
_In_ DWORD dwType)
{
NTSTATUS Status = STATUS_SUCCESS;
DWORD dwBytes, dwBytesRequested;
UNICODE_STRING SafeFileNames;
BOOL bRet = FALSE;
ULONG cbStringSize;
LPVOID Buffer;
/* FIXME: Handle cFiles > 0 */
/* Check for valid dwType values */
if (dwType > 5)
{
EngSetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
/* Allocate a safe unicode string buffer */
cbStringSize = cwc * sizeof(WCHAR);
SafeFileNames.MaximumLength = SafeFileNames.Length = (USHORT)cbStringSize - sizeof(WCHAR);
SafeFileNames.Buffer = ExAllocatePoolWithTag(PagedPool,
cbStringSize,
TAG_USTR);
if (!SafeFileNames.Buffer)
{
EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
}
RtlZeroMemory(SafeFileNames.Buffer, SafeFileNames.MaximumLength);
/* Check buffers and copy pwszFiles to safe unicode string */
_SEH2_TRY
{
ProbeForRead(pwszFiles, cbStringSize, 1);
ProbeForWrite(pdwBytes, sizeof(DWORD), 1);
if (pvBuf)
ProbeForWrite(pvBuf, cjIn, 1);
dwBytes = *pdwBytes;
dwBytesRequested = dwBytes;
RtlCopyMemory(SafeFileNames.Buffer, pwszFiles, cbStringSize);
if (dwBytes > 0)
{
Buffer = ExAllocatePoolWithTag(PagedPool, dwBytes, TAG_FINF);
}
else
{
Buffer = ExAllocatePoolWithTag(PagedPool, sizeof(DWORD), TAG_FINF);
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END
if(!NT_SUCCESS(Status))
{
SetLastNtError(Status);
/* Free the string buffer for the safe filename */
ExFreePoolWithTag(SafeFileNames.Buffer, TAG_USTR);
return FALSE;
}
/* Do the actual call */
bRet = IntGdiGetFontResourceInfo(&SafeFileNames,
(pvBuf ? Buffer : NULL),
&dwBytes, dwType);
/* Check if succeeded */
if (bRet)
{
/* Copy the data back to caller */
_SEH2_TRY
{
/* Buffers are already probed */
if (pvBuf && dwBytesRequested > 0)
RtlCopyMemory(pvBuf, Buffer, min(dwBytesRequested, dwBytes));
*pdwBytes = dwBytes;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END
if(!NT_SUCCESS(Status))
{
SetLastNtError(Status);
bRet = FALSE;
}
}
ExFreePoolWithTag(Buffer, TAG_FINF);
/* Free the string for the safe filenames */
ExFreePoolWithTag(SafeFileNames.Buffer, TAG_USTR);
return bRet;
}
| 参数 |
类型 |
说明 |
pwszFiles |
PCWCH |
字体文件路径(用户缓冲) |
cwc |
ULONG |
字符数 |
cFiles |
ULONG |
文件数(FIXME:仅支持 1) |
cjIn |
ULONG |
输入缓冲大小 |
pdwBytes |
PDWORD |
输入输出:请求/实际字节数 |
pvBuf |
PVOID |
输出缓冲 |
dwType |
DWORD |
查询类型(0~5,见下) |
- 作用 :
GetFontResourceInfo 的系统服务:查询字体文件的资源信息 。dwType 取值范围(dwType > 5 直接拒绝):
- 0:字体文件大小 / 1:文件路径 / 2:字体校验和 / 3:字体文件名 / 4:字体面数 / 5:文件时间等(具体映射在
IntGdiGetFontResourceInfo 的 switch 中,freetype.c 第 6162 行起,按 dwType 枚举 g_FontListHead 中 Filename 匹配的 FONT_ENTRY 输出)。
- 实现流程 :① dwType 校验;② 分配
TAG_USTR 字符串缓冲;③ SEH 三连 Probe(pwszFiles 读、pdwBytes 读写、pvBuf 写)→ 读入 dwBytes → 分配 TAG_FINF 数据缓冲;④ IntGdiGetFontResourceInfo(&SafeFileNames, pvBuf ? Buffer : NULL, &dwBytes, dwType):用 IntGetFullFileName 求输入文件全路径,遍历全局字体链表匹配 FontGDI->Filename,按 dwType 填数据;⑤ 成功后 RtlCopyMemory(pvBuf, Buffer, min(dwBytesRequested, dwBytes)) 并回写 *pdwBytes = dwBytes;⑥ 异常统一 SetLastNtError。
- 调用方 :gdi32full 的
GetFontResourceInfoW(字体安装器、控制面板字体工具)。
- 注意事项 :
cFiles > 0 未处理(FIXME);Buffer 在 dwBytes==0 时也分配 sizeof(DWORD) 的占位缓冲,避免空指针路径。
3.3.15 NtGdiGetRealizationInfo(font.c 第 1152~1219 行)
/* @unimplemented */
BOOL
APIENTRY
NtGdiGetRealizationInfo(
_In_ HDC hdc,
_Out_ PREALIZATION_INFO pri,
_In_ HFONT hf)
{
PDC pDc;
PTEXTOBJ pTextObj;
PFONTGDI pFontGdi;
PDC_ATTR pdcattr;
BOOL Ret = FALSE;
INT i = 0;
REALIZATION_INFO ri;
pDc = DC_LockDc(hdc);
if (!pDc)
{
EngSetLastError(ERROR_INVALID_HANDLE);
return 0;
}
pdcattr = pDc->pdcattr;
pTextObj = RealizeFontInit(pdcattr->hlfntNew);
ASSERT(pTextObj != NULL);
pFontGdi = ObjToGDI(pTextObj->Font, FONT);
TEXTOBJ_UnlockText(pTextObj);
DC_UnlockDc(pDc);
Ret = ftGdiRealizationInfo(pFontGdi, &ri);
if (Ret)
{
if (pri)
{
NTSTATUS Status = STATUS_SUCCESS;
_SEH2_TRY
{
ProbeForWrite(pri, sizeof(REALIZATION_INFO), 1);
RtlCopyMemory(pri, &ri, sizeof(REALIZATION_INFO));
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END
if(!NT_SUCCESS(Status))
{
SetLastNtError(Status);
return FALSE;
}
}
do
{
if (GdiHandleTable->cfPublic[i].hf == hf)
{
GdiHandleTable->cfPublic[i].iTechnology = ri.iTechnology;
GdiHandleTable->cfPublic[i].iUniq = ri.iUniq;
GdiHandleTable->cfPublic[i].dwUnknown = ri.dwUnknown;
GdiHandleTable->cfPublic[i].dwCFCount = GdiHandleTable->dwCFCount;
GdiHandleTable->cfPublic[i].fl |= CFONT_REALIZATION;
}
i++;
}
while ( i < GDI_CFONT_MAX );
}
return Ret;
}
| 参数 |
类型 |
说明 |
hdc |
HDC |
设备上下文 |
pri |
PREALIZATION_INFO |
输出实现信息 |
hf |
HFONT |
需要更新公共字体表项的字体句柄 |
- 作用 :
GetFontRealizationInfo 的系统服务(标注 @unimplemented,但实现较完整):返回字体的实现信息,并同步更新 GdiHandleTable->cfPublic[] 公共字体缓存表 (CFONT_REALIZATION 标志)。
- 实现流程 :① 锁 DC →
RealizeFontInit(ASSERT(pTextObj != NULL)------失败即断言)→ ObjToGDI → 解锁;② ftGdiRealizationInfo(freetype.c 第 6432 行):FT_HAS_FIXED_SIZES → RI_TECH_BITMAP;否则 FT_IS_SCALABLE → RI_TECH_SCALABLE,再否则 RI_TECH_FIXED;iUniq = FontObj.iUniq、dwUnknown = -1;③ SEH 拷回 pri;④ 遍历 GdiHandleTable->cfPublic[0..GDI_CFONT_MAX),把 hf 匹配项更新为 ri 的三个字段并置 CFONT_REALIZATION。
- 调用方 :gdi32full 的
GetFontRealizationInfo(Text Services Framework / IME 用)。
- 注意事项 :
@unimplemented 注释与实际实现程度不符(实现基本完整,仅缺精确的 Windows 语义对齐);cfPublic[] 是 win32k 供 IME/TSF 查询"字体实现状态"的公共表,dwCFCount 记录条目代数。
3.3.16 函数总览
font.c 全部 26 个函数:创建组(3.1 节)5 个 ------GreCreateFontIndirectW、NtGdiHfontCreate、HfontCreate、RealizeFontInit、FontGetObject;资源组(3.2 节)5 个 ------IntCheckFontPathNames、NtGdiAddFontResourceW、NtGdiRemoveFontResourceW、NtGdiAddFontMemResourceEx、NtGdiRemoveFontMemResourceEx;度量/查询组(3.3 节)16 个 ------NtGdiGetCharWidthW、NtGdiGetCharABCWidthsW、NtGdiGetGlyphIndicesW、NtGdiGetGlyphIndicesWInternal、GreGetKerningPairs、NtGdiGetKerningPairs、NtGdiGetCharacterPlacementW(stub)、GreGetCharacterPlacementW(#if 0)、IntGetCharDimensions、IntGetFontLanguageInfo、NtGdiGetGlyphOutline、NtGdiGetFontData、NtGdiGetFontUnicodeRanges、NtGdiGetOutlineTextMetricsInternalW、NtGdiGetFontResourceInfoInternalW、NtGdiGetRealizationInfo。
4. 调用链(mermaid)
4.1 字体创建链
渲染错误: Mermaid 渲染失败: Parse error on line 14: ...rline/StrikeOut, fl |= TEXTOBJECT_INIT] -----------------------^ Expecting 'SQE', 'TAGEND', 'UNICODE_TEXT', 'TEXT', 'TAGSTART', got 'PIPE'
4.2 字形查询链(以 GetCharWidth 与 GetGlyphOutline 为例)
渲染错误: Mermaid 渲染失败: Parse error on line 10: ...取 advance.x 26.6 → (adv+32)\>\>6 A1 -----------------------^ Expecting 'SQE', 'DOUBLECIRCLEEND', 'PE', '-)', 'STADIUMEND', 'SUBROUTINEEND', 'PIPE', 'CYLINDEREND', 'DIAMOND_STOP', 'TAGEND', 'TRAPEND', 'INVTRAPEND', 'UNICODE_TEXT', 'TEXT', 'TAGSTART', got 'PS'
4.3 资源加载链(文件字体 + 内存字体)
渲染错误: Mermaid 渲染失败: Parse error on line 14: ...romMemory FR_PRIVATE|FR_NOT_ENUM] LD -----------------------^ Expecting 'SQE', 'TAGEND', 'UNICODE_TEXT', 'TEXT', 'TAGSTART', got 'PIPE'
5. 与 freetype.c / text.c / eng 的关系
5.1 分层职责
| 层 |
文件 |
职责 |
| 系统服务层 |
font.c |
NtGdi* 入口、SEH 安全拷贝、锁 DC/锁文本对象、双趟缓冲协议、句柄/对象管理 |
| 内部 Gre/Int 层 |
font.c + text.h |
RealizeFontInit、HfontCreate、FontGetObject、TEXTOBJ_LockText/UnlockText |
| 引擎适配层 |
freetype.c |
全部 FT_* 调用:面加载、charmap 选择、字形加载/渲染、sfnt 表读取、字距、字体匹配(FindBestFontFromList/SubstituteFontRecurse)、全局字体链表 g_FontListHead、字体缓存 FONT_CACHE_ENTRY、字体链接 FontLink |
| 文本输出层 |
text.c |
GreTextOutW/GreGetTextExtentW/GreGetTextExtentExW 的薄包装(见 text.h 第 145~162 行声明),内部转调 freetype.c 的 IntExtTextOutW/TextIntGetTextExtentPoint |
| 对象定义层 |
engobjects.h / ntgdityp.h / winddi.h / wingdi.h |
FONTGDI、SHARED_FACE、FONTOBJ、UNIVERSAL_FONT_ID、REALIZATION_INFO、ENUMLOGFONTEXDVW |
5.2 关键引擎侧函数(freetype.c)
| 函数 |
行号 |
说明 |
InitFontSupport |
947 |
分配 g_FreeTypeLock 快互斥、FT_Init_FreeType、IntLoadFontsInRegistry(注册表已有字体则跳过扫描)、IntLoadSystemFonts(扫描 \SystemRoot\Fonts\ 下 .ttf/ .ttc/.otf/ .otc/.fon/ .fnt,带 AFRX_WRITE_REGISTRY 写注册表)、IntLoadFontSubstList、FontLink_LoadSettings/DefaultFonts/DefaultCharset/PopulateEntries |
FreeFontSupport |
990 |
释放字体缓存链、字体替换链、全局字体链(CleanupFontEntry)、FT_Done_Library |
IntGdiAddFontResourceSingle |
1877 |
单个字体文件加载:打开/建 section/映射 → SharedMem_Create → IntGdiLoadFontByIndexFromMemory → 写注册表(TrueType 追加 " (TrueType)"、非默认 charset 追加 " (CharSetName)") |
IntGdiAddFontResourceEx |
2076 |
循环多文件调用 Single,返回面数总和 |
IntGdiRemoveFontResourceSingle |
2149 |
按 PathFindFileNameW 文件名与 FontGDI->Filename 匹配删除 FONT_ENTRY,可带注册表清理 |
IntLoadFontsInRegistry |
2254 |
枚举 g_FontRegPath(HKLM\Software\Microsoft\Windows NT\CurrentVersion\Fonts)逐值加载 |
IntGdiAddFontMemResource |
2407 |
内存字体:深拷贝 → SharedMem_Create → 加载 → 挂进程 PrivateMemFontListHead |
IntGdiRemoveFontMemResource |
2492 |
按句柄摘除集合并清理 |
IntGdiCleanupPrivateFontsForProcess |
2528 |
进程退出清理:先清内存字体集合,再清 PrivateFontListHead 文件字体 |
TextIntCreateFontIndirect |
2611 |
备用创建路径(LFONT 分配 + 拷贝 LOGFONTW + 规范化),text.h 声明 |
TextIntRealizeFont |
6050 |
实现核心 :替换字体 → 私有链匹配 → 全局链匹配(FindBestFontFromList 按 GetFontPenalty 惩罚分选最优)→ 填 TextFace(匹配面名或 IntGetFontLocalizedName 本地化名)→ FontObj.iUniq=1 → IntFontType 定 flType → 记录合成请求 → 置 TEXTOBJECT_INIT |
TextIntUpdateSize |
4269 |
确保 charmap 已选(优先 UNICODE→APPLE_UNICODE→MS_SYMBOL→首个)并 IntRequestFontSize(lfWidth, lfHeight) |
IntGetOutlineTextMetrics |
3018 |
组装 OUTLINETEXTMETRICW:OS/2+hhea+post 表经 FillTM/SCALE_X/SCALE_Y 缩放,缓存 OutlineRequiredSize,按语言选 EnglishUS/UserLanguage 缓存槽 |
ftGdiGetGlyphOutline |
4500 |
字形轮廓/位图核心:尺寸与变换(世界矩阵、旋转 IntEscapeMatrix、pmat2、宽度比例)→ FT_Load_Glyph → 26.6 定点外接盒/gmCellIncX → GGO_BITMAP 渲染 |
TextIntGetTextExtentPoint |
5028 |
文本范围:逐字符 FontLink_Chain_FindGlyph(字体链接回退)+ IntGetRealGlyph + 字距累加 TotalWidth64,支持代理对、竖排(Escapement-Orientation=±90°)交换 cx/cy |
ftGdiGetTextCharsetInfo |
5148 |
填 FONTSIGNATURE(fsCsb 代码页位图),供 IntGetFontLanguageInfo |
ftGetFontUnicodeRanges |
5261 |
遍历 FT_Get_First_Char/Next_Char 生成 WCRANGE 数组 |
ftGdiGetTextMetricsW |
5335 |
填 TMW_INTERNAL(TEXTMETRICW + 内部字段),FillTM 用 OS/2+hhea+WinFNT 表 |
ftGdiGetFontData |
5433 |
sfnt 表原始数据:Table 标签字节序翻转 → FT_Load_Sfnt_Table 两趟(查大小/取数据) |
ftGdiRealizationInfo |
6432 |
FT_HAS_FIXED_SIZES→RI_TECH_BITMAP;FT_IS_SCALABLE→RI_TECH_SCALABLE;否则 RI_TECH_FIXED;dwUnknown=-1 |
ftGdiGetKerningPairs |
6449 |
遍历字符对 FT_Get_Kerning 生成 KERNINGPAIR(wFirst/wSecond 为字符码) |
GreGetCharWidthW |
7603 |
批量字符 advance 宽度(GCW_INDICES 语义),fl==0 输出 FLOAT |
GreGetCharABCWidthsW |
7497 |
批量 A/B/C 宽度,fl==0 输出 ABCFLOAT |
GreGetGlyphIndicesW |
7716 |
字符→字形索引,未命中替换默认字形(IntGetFontDefaultChar);cwc==0 全空参数时返回 num_glyphs |
GreExtTextOutW |
7300 |
文本输出入口:锁 DC → IntExtTextOutW(字形渲染、下划线/删除线、字体链接);NtGdiExtTextOutW(7340)做 SEH 缓冲处理 |
GetFontPenalty |
5474 |
字体匹配惩罚分:CharSet 65000、OutputPrecision 19000、FixedPitch 15000 等,越小越匹配 |
5.3 与 eng(GDI 引擎)的关系
font.c/freetype.c 位于 win32ss/gdi/ntgdi(内核 GDI),与 win32ss/gdi/eng(引擎层)通过以下接口协作:
- 对象头 :FONTGDI 继承 eng 的 FONTOBJ 头(winddi.h),
ObjToGDI(ptr, FONT) 宏在 eng 的句柄对象管理上做类型安全转换;TEXTOBJ 的 BASEOBJECT 由 ntuser 的 GDIOBJ_* 管理。
- 字体缓存 :eng 的字体缓存服务(
EngFntCache*,《分析_28》)与 freetype.c 内部的 FONT_CACHE_ENTRY(g_FontCacheListHead、g_FontCacheNumEntries)协作:IntGetRealGlyph 先查缓存(按 Face+lfHeight+lfWidth+Emu+RenderMode+GlyphIndex 的散列键),未命中 FT_Load_Glyph/FT_Render_Glyph 后 CacheGlyph 写入,形成"字形光栅缓存"。
- 共享内存 :SHARED_FACE.Memory 由 eng/共享内存服务(
SharedMem_Create/SharedMem_Reference/SharedMem_Release)管理,承载字体文件映射或内存字体拷贝。
- 设备无关性:FONTGDI.dhpdev 记录设备句柄,轮廓字体在屏幕/打印 DC 间共享同一 FT_Face,仅尺寸/变换不同。
- 驱动路径 :eng 的字体驱动(ftfd/bmfd,《分析_21》EngMapFontFileFD)与 ntgdi 的 FreeType 路径并存;ntgdi 侧加载时
IntGdiLoadFontByIndexFromMemory 直接使用 FT_New_Memory_Face 而非驱动。
5.4 与 text.c 的关系
text.c(file:///d:/reactos/win32ss/gdi/ntgdi/text.c) 提供 GreTextOutW(第 60 行)、GreGetTextExtentW(第 77 行)、GreGetTextExtentExW(第 133 行)等 Gre 层包装,它们都只是"锁 DC + 调 freetype.c 实现"的薄层:
GreTextOutW → IntExtTextOutW (freetype.c 7300 系列,字形渲染)
GreGetTextExtentW → TextIntGetTextExtentPoint(freetype.c 5028,宽度累加)
GreGetTextExtentExW→ TextIntGetTextExtentPoint(带 MaxExtent/Fit/Dx)
font.c 与 text.c 的分工:font.c 管"字体对象与度量",text.c 管"字符串输出与范围"。二者共享 text.h 声明的接口(RealizeFontInit、TextIntGetTextExtentPoint、GreExtTextOutW 等)。GreGetCharacterPlacementW(#if 0)同时引用 GreGetTextExtentW、GreGetTextExtentExW、GreGetKerningPairs、GreGetGlyphIndicesW,可见 GCP 未来实现会跨两个文件。
6. 源码索引表
| 文件 |
关键内容(行号) |
| font.c(file:///d:/reactos/win32ss/gdi/ntgdi/font.c) |
全部 26 个函数:创建组 GreCreateFontIndirectW(29)/HfontCreate(1221)/NtGdiHfontCreate(1275)/RealizeFontInit(431)/FontGetObject(296);资源组 IntCheckFontPathNames(452)/NtGdiAddFontResourceW(474)/NtGdiRemoveFontResourceW(528)/NtGdiAddFontMemResourceEx(582)/NtGdiRemoveFontMemResourceEx(638);度量组 NtGdiGetCharWidthW(1316)/NtGdiGetCharABCWidthsW(1374)/NtGdiGetGlyphIndicesW(1436)/NtGdiGetGlyphIndicesWInternal(1450)/GreGetKerningPairs(53)/NtGdiGetKerningPairs(888)/NtGdiGetCharacterPlacementW(650)/GreGetCharacterPlacementW(122,#if0)/IntGetCharDimensions(335)/IntGetFontLanguageInfo(380)/NtGdiGetGlyphOutline(803)/NtGdiGetFontData(672)/NtGdiGetFontUnicodeRanges(731)/NtGdiGetOutlineTextMetricsInternalW(963)/NtGdiGetFontResourceInfoInternalW(1040)/NtGdiGetRealizationInfo(1152) |
| text.h(file:///d:/reactos/win32ss/gdi/ntgdi/text.h) |
TEXTOBJ/LFONT(59)、STRGDI(23)、TEXTOBJECT_INIT(56)、LFONT_* 宏(76)、TEXTOBJ_LockText/UnlockText(81~101)、AFRX_* 标志(103~106)、RealizeFontInit/TextIntRealizeFont/TextIntCreateFontIndirect/InitFontSupport/FreeFontSupport/IntIsFontRenderingEnabled/IntEnableFontRendering/FontGetObject/IntLoadSystemFonts/IntLoadFontsInRegistry/IntGdiCleanupPrivateFontsForProcess/IntGdiAddFontResourceEx/IntGdiRemoveFontResource/IntGdiAddFontMemResource/IntGdiRemoveFontMemResource/ftGdiGetGlyphOutline/IntGetOutlineTextMetrics/TextIntUpdateSize/ftGdiGetRasterizerCaps/TextIntGetTextExtentPoint/ftGdiGetTextMetricsW/IntGetFontLanguageInfo/ftGdiGetTextCharsetInfo/ftGetFontUnicodeRanges/ftGdiGetFontData/IntGdiGetFontResourceInfo/ftGdiRealizationInfo/ftGdiGetKerningPairs/GreExtTextOutW/IntGetCharDimensions/GreGetTextExtentW/GreGetTextExtentExW/GreTextOutW/GreCreateFontIndirectW/GreGetTextMetricsW/GreGetCharWidthW/GreGetCharABCWidthsW/GreGetGlyphIndicesW 声明(108~195)、IntLockProcessPrivateFonts 宏(164) |
| freetype.c(file:///d:/reactos/win32ss/gdi/ntgdi/freetype.c) |
InitFontSupport(947)/FreeFontSupport(990)/IntLoadSystemFonts(1377)/IntGdiAddFontResourceSingle(1877)/IntGdiAddFontResourceEx(2076)/IntDeleteRegFontEntries(2115)/IntGdiRemoveFontResourceSingle(2149)/IntGdiRemoveFontResource(2218)/IntLoadFontsInRegistry(2254)/IntGdiAddFontMemResource(2407)/IntGdiCleanupMemEntry(2456)/UnlinkFontMemCollection(2475)/IntGdiRemoveFontMemResource(2492)/IntGdiCleanupPrivateFontsForProcess(2528)/IntIsFontRenderingEnabled(2578)/IntEnableFontRendering(2584)/IntGetFontRenderMode(2590)/TextIntCreateFontIndirect(2611)/IntGetOutlineTextMetrics(3018)/TextIntUpdateSize(4269)/get_glyph_index 族(4358~4391)/ftGdiGetGlyphOutline(4500)/TextIntGetTextExtentPoint(5028)/ftGdiGetTextCharsetInfo(5148)/ftGetFontUnicodeRanges(5261)/ftGdiGetTextMetricsW(5335)/ftGdiGetFontData(5433)/GetFontPenalty(5474)/TextIntRealizeFont(6050)/IntGdiGetFontResourceInfo(6162)/ftGdiRealizationInfo(6432)/ftGdiGetKerningPairs(6449)/GreExtTextOutW(7300)/NtGdiExtTextOutW(7340)/GreGetCharABCWidthsW(7497)/GreGetCharWidthW(7603)/IntGetFontDefaultChar(7684)/GreGetGlyphIndicesW(7716) |
| engobjects.h(file:///d:/reactos/win32ss/gdi/eng/engobjects.h) |
SHARED_FACE_CACHE(125)/SHARED_FACE(131)/FONTGDI(139)/FONTGDI_MAGIC(171) |
| ntgdityp.h(file:///d:/reactos/win32ss/include/ntgdityp.h) |
LFTYPE(189)/CHWIDTHINFO(258)/UNIVERSAL_FONT_ID(265)/RI_TECH_*(271~273)/REALIZATION_INFO(275)/WIDTHDATA(282) |
| winddi.h(file:///d:/reactos/sdk/include/psdk/winddi.h) |
FONTOBJ(761)/FO_DEVICE_FONT 等标志(758~759) |
| wingdi.h(file:///d:/reactos/sdk/include/psdk/wingdi.h) |
ENUMLOGFONTEXW(3147)/DESIGNVECTOR(3213)/ENUMLOGFONTEXDVW(3223)/CreateFontIndirectExW(3698) |
| text.c(file:///d:/reactos/win32ss/gdi/ntgdi/text.c) |
GreTextOutW(60)/GreGetTextExtentW(77)/GreGetTextExtentExW(133) |
7. 常见注意事项
- 锁纪律 :
RealizeFontInit 返回已持锁的 TEXTOBJ,必须 配对 TEXTOBJ_UnlockText;未配对会泄漏句柄引用并破坏 push lock 状态。多数函数在解锁 DC 后才使用 TEXTOBJ,而 NtGdiGetFontUnicodeRanges 保持 DC 锁到 Exit 标签------改动时注意保持对称。
- FreeType 全局锁 :所有
FT_* 调用必须在 IntLockFreeType/IntUnLockFreeType 之间;TextIntRealizeFont、ftGdiGetGlyphOutline 内部会自己加锁(ASSERT_FREETYPE_LOCK_NOT_HELD 断言),调用方不得预先持锁,否则死锁或断言失败。
- 双趟缓冲协议 :
GetFontData、GetGlyphOutline、GetOutlineTextMetrics、GetFontUnicodeRanges、GetKerningPairs、GetFontResourceInfo 均支持"Buffer==NULL/Size==0 查大小 → 第二趟取数据"模式,第一趟返回所需字节数而非 0。
- SEH 与 Probe :所有用户缓冲访问必须
ProbeForRead/ProbeForWrite + _SEH2_TRY;异常统一 EngSetLastError 并返回失败码(GDI_ERROR / 0 / FALSE)。GreGetKerningPairs(内部版)的拷贝无 SEH,只能被内核内部调用。
- MAX_TEXT_BUFFER 上限 :
#define MAX_TEXT_BUFFER 0x2710000(约 40MB)约束 GetCharWidth/ABCWidths/GlyphIndices 的单次请求大小,防内核池过度分配;小请求优先用栈缓冲(40/28/80 元素),减少池分配压力。
fl 复用语义 :GetCharWidth/GetCharABCWidths 的 fl 同时编码"输出 INT 还是 FLOAT"(0=FLOAT)与"输入是字形索引"(GCW_INDICES/GCABCW_INDICES),勿混淆。
- 延迟实现 :HfontCreate 后
TextObj->Font==NULL、fl& TEXTOBJECT_INIT==0;在 RealizeFontInit/FontGetObject 之前调用度量函数会自动触发实现,但 GetObject 是唯一在实现失败时仍继续(仅 DPRINT)的路径。
- 私有内存字体 :
AddFontMemResourceEx 返回的 HANDLE 是进程内序号(PrivateMemFontHandleCount 自增),不是内核句柄;不 Remove 会在进程退出时由 IntGdiCleanupPrivateFontsForProcess 统一回收;FR_PRIVATE | FR_NOT_ENUM 特性使其不出现在 EnumFonts/字体对话框中。
- 注册表持久化 :
AddFontResourceW(flags=0)不写 注册表;只有 IntLoadSystemFonts 使用 AFRX_WRITE_REGISTRY 把 \SystemRoot\Fonts\ 扫描结果写入 HKLM\...\Fonts(g_FontRegPath)。注册表字体名含 " (TrueType)" 后缀。
- 未实现/降级点 :
NtGdiGetCharacterPlacementW 是 UNIMPLEMENTED stub;GreGetCharacterPlacementW 整体 #if 0;bSubset(GlyphIndices 子集化)、cjElfw 大小校验、cFiles>0(FontResourceInfo)均为 FIXME。IntGetFontLanguageInfo 多数特性掩码为 0(刻意保守)。
- 字距/Unicode 范围限制 :
ftGdiGetKerningPairs 与 ftGetFontUnicodeRanges 都要求 charmap 为 FT_ENCODING_UNICODE,symbol 字体/非 Unicode 字体相关查询返回 0。
- 字形合成 :FONTGDI 的
RequestWeight/RequestItalic/RequestUnderline/RequestStrikeOut 与 OriginalWeight/OriginalItalic 是粗体/斜体/下划线合成的输入(EMUBOLD_NEEDED 判定),由 TextIntRealizeFont 在实现时从 LOGFONTW 拷贝,TextIntGetTextExtentPoint 用其计算缓存键(Cache.Hashed.Aspect.Emu.Bold/Italic)。
本文档基于 ReactOS 源代码 win32ss/gdi/ntgdi/font.c(约 1500 行)及关联模块分析(text.h / freetype.c / engobjects.h / ntgdityp.h / winddi.h / wingdi.h,2026 年 8 月)。所有行号以当前源码为准。