ReactOS 图形系统分析(33):字体子系统 — font.c

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 内部函数。它负责四类职责:

  1. 字体创建CreateFontIndirectW 的用户态调用最终落到 GreCreateFontIndirectWHfontCreate,分配并初始化逻辑字体对象 TEXTOBJ(即 LFONT),真正的字形实现(FreeType FT_Face)延迟到字体被 DC 选中时(RealizeFontInit)才完成。
  2. 字体资源加载/卸载AddFontResourceW / RemoveFontResourceW(文件字体)与 AddFontMemResourceEx / RemoveFontMemResourceEx(内存字体)的系统服务实现,负责把字体文件/缓冲挂入全局字体链表 g_FontListHead 或进程私有字体链表,并可同步写注册表。
  3. 字符度量查询GetCharWidth / GetCharABCWidths / GetGlyphIndices / GetKerningPairs / GetOutlineTextMetrics / GetFontUnicodeRanges 等度量类 API 的内核入口。
  4. 字形轮廓/字体数据提取GetGlyphOutline(字形轮廓或位图)、GetFontData(sfnt 表原始字节)、GetFontResourceInfoInternalW(字体文件信息)等。

值得注意的是:font.c 本身不包含任何 FreeType 调用 。它扮演的是"管理/查询层",所有 FT_* 操作(FT_Load_GlyphFT_Get_Sfnt_TableFT_Get_Char_Index......)都在 freetype.c(file:///d:/reactos/win32ss/gdi/ntgdi/freetype.c)(约 7800 行,详见《分析_16》)中实现;font.c 通过 ftGdiGetGlyphOutlineftGdiGetFontDataftGdiGetKerningPairsft 前缀函数桥接到 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 行定义:

c 复制代码
#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内嵌 FontObjFONTGDI 首成员);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 行):

c 复制代码
#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 行:

c 复制代码
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_VALIDTO_VALIDTO_ESC_NOT_ORIENTTO_PWSZ_ALLOCATEDTSIM_UNDERLINE1/2TSIM_STRIKEOUTTO_HIGHRESTEXTTO_BITMAPSTO_PARTITION_INITTO_ALLOC_FACENAMETO_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 行:

c 复制代码
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 行:

c 复制代码
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 魔数 0x20110311ASSERT(FontGDI->Magic == FONTGDI_MAGIC) 校验
lfHeight / lfWidth 请求的逻辑高度/宽度

2.5 SHARED_FACE / SHARED_FACE_CACHE(engobjects.h)

engobjects.h 第 125~137 行:

c 复制代码
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,例如 ftGdiGetFontDataftGdiGetKerningPairsftGetFontUnicodeRangesTextIntUpdateSize 等函数都从这里取面。

2.6 UNIVERSAL_FONT_ID(ntgdityp.h)

ntgdityp.h(file:///d:/reactos/win32ss/include/ntgdityp.h) 第 265~269 行:

c 复制代码
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):

c 复制代码
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)

c 复制代码
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_INFONtGdiGetRealizationInfo 的输出:iTechnology 取值 RI_TECH_BITMAP(位图字体)/RI_TECH_FIXED(固定字体)/RI_TECH_SCALABLE(可缩放字体,如 TrueType),iUniq 来自 FontObj.iUniqdwUnknown 固定为 -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 行)
c 复制代码
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 的 CreateFontIndirectWNtGdiHfontCreate 进入(实际路径是 gdi32full → NtGdiHfontCreate,Gre 版本供 win32k 内部/旧路径使用);NtGdiHfontCreateHfontCreate 的包装关系见 3.1.2/3.1.3。
  • 注意事项 :此函数假定输入已安全(不探测用户缓冲),因此它仅供内核内部调用;用户态入口必须走 NtGdiHfontCreate(带 ProbeForRead)。
3.1.2 NtGdiHfontCreate(font.c 第 1275~1313 行)
c 复制代码
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 的 CreateFontIndirectWCreateFontIndirectExW(后者是 win32k syscall,见 ntgdi.h 第 2888 行声明)。
  • 注意事项cjElfw 未用于大小校验(固定按 sizeof(ENUMLOGFONTEXDVW) 拷贝),是潜在精简点;pvCliData 最终被写入 GdiHandleTable->Entries[index].UserData(见 HfontCreate)。
3.1.3 HfontCreate(font.c 第 1221~1272 行)
c 复制代码
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)。
  • 调用方GreCreateFontIndirectWNtGdiHfontCreate;也被 ntuser(如 NtUserSetMenuFlagRTL 等)以及字体枚举/默认字体创建等内部路径间接使用。
  • 注意事项 :此处不做字体匹配 ------plfont->Font 保持 NULL,fl 不含 TEXTOBJECT_INIT,延迟到 RealizeFontInit。这体现了 Windows 逻辑字体与"已实现字体"(realized font)的分离。
3.1.4 RealizeFontInit(font.c 第 431~450 行)
c 复制代码
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 的每个度量函数GreGetKerningPairsNtGdiGetFontDataNtGdiGetFontUnicodeRangesNtGdiGetGlyphOutlineNtGdiGetKerningPairsNtGdiGetOutlineTextMetricsInternalWNtGdiGetRealizationInfoIntGetCharDimensions 等),以及 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 行)
c 复制代码
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 的 NtUserGetObjectGetObject 系统服务)在对象类型为 FONT 时调用。
  • 注意事项pvBuffer==NULL 时返回 LOGFONTW 大小而非 ENUMLOGFONTEXDVW 大小,这是刻意与 Windows API 兼容的行为。

3.2 字体资源加载/卸载组

3.2.1 IntCheckFontPathNames(font.c 第 452~470 行,static)
c 复制代码
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
  • 调用方NtGdiAddFontResourceWNtGdiRemoveFontResourceW(SEH 保护区内)。
  • 注意事项pwcFiles[cwc-1] 在 SEH 保护区(ProbeForRead 之后)访问,安全;cFiles 为 0 时任何以空结尾的缓冲都通过。
3.2.2 NtGdiAddFontResourceW(font.c 第 474~526 行)
c 复制代码
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:ProbeForReadIntCheckFontPathNames → 拷贝;④ 补写末尾 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 行)
c 复制代码
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,按 FilenamePathFindFileNameW 提取的文件名匹配 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 行)
c 复制代码
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_FONTRtlCopyMemory;③ IntGdiAddFontMemResource(Buffer, cjBuffer, &NumFonts)(freetype.c 第 2407 行):再次 ExAllocatePoolWithTag 深拷贝 → SharedMem_Create(BufferCopy, dwSize, FALSE)LoadFont.Characteristics = FR_PRIVATE | FR_NOT_ENUMIntGdiLoadFontByIndexFromMemory(&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 行)
c 复制代码
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 == hMMFontUnlinkFontMemCollection(把集合内所有 FONT_ENTRY 从 PrivateFontListHead 摘除)→ 解锁 → IntGdiCleanupMemEntry 逐个 CleanupFontEntry 释放 FT 面与共享内存。
  • 调用方 :gdi32full 的 RemoveFontMemResourceEx
  • 注意事项:找不到句柄返回 FALSE;对同一个句柄重复移除返回 FALSE。

3.3 度量与字形查询组

3.3.1 NtGdiGetCharWidthW(font.c 第 1316~1372 行)
c 复制代码
/* 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_LockDcRealizeFontInit 取得 TEXTOBJ → 解锁 DC → ObjToGDI(TextObj->Font, FONT)IntSelectFaceCharmapIntLockFreeTypeIntRequestFontSize(lfWidth, lfHeight)FT_Set_Transform(face, NULL, NULL)(忽略 XFORM/Escapement)→ 逐字符 get_glyph_index_flaggedfl & GCW_INDICES 时输入视为字形索引)→ FT_Load_Glyph → 取 face->glyph->advance.x(26.6 定点,(adv+32)>>6 四舍五入到像素)→ fl==0 时按 FLOAT 输出;⑤ MmCopyToCaller 拷回用户缓冲。
  • 调用方 :gdi32full 的 GetCharWidthA/WGetCharWidthIGetCharWidth32A/WGetCharWidthFloatA/W 全部映射到本入口。
  • 注意事项Count 上限 MAX_TEXT_BUFFER/sizeof(INT) 防溢出;UnSafepwc 可为 NULL(此时按 FirstCharFirstChar+Count-1 连续字符码);GCW_INDICES 标志下 FirstChar 语义变为首字形索引。
3.3.2 NtGdiGetCharABCWidthsW(font.c 第 1374~1433 行)
c 复制代码
/* 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 → RealizeFontInitIntSelectFaceCharmapIntRequestFontSize + 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>>6bbx = (right-left)>>6C = adv - lsb - bbx
    • fl==0 输出 ABCFLOAT,否则输出 ABCGCABCW_INDICES 时输入当字形索引);⑤ MmCopyToCaller 拷回。
  • 调用方 :gdi32full 的 GetCharABCWidthsA/WGetCharABCWidthsIGetCharABCWidthsFloatA/W
  • 注意事项 :注意 fl 同时承担两个语义------"输出 FLOAT 还是 INT"和"输入是字形索引"。Buffer 必须非空(与 GetCharWidth 不同,这里没有"仅查询大小"模式)。
3.3.3 NtGdiGetGlyphIndicesW / NtGdiGetGlyphIndicesWInternal(font.c 第 1436~1509 行)
c 复制代码
/* 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 → RealizeFontInitObjToGDI → 若无 GGI_MARK_NONEXISTING_GLYPHSIntGetFontDefaultChar 取默认字符(OS/2 的 usDefaultChar 或 WinFNT 的 default_char)并转成字形索引,然后逐字符 get_glyph_index(symbol 字体走 get_glyph_index_symbol 的 U+F0XX 映射),未命中(索引 0)的替换为默认字形索引;⑧ MmCopyToCaller 拷回 WORD 数组。
  • 调用方 :gdi32full 的 GetGlyphIndicesW/AGreGetCharacterPlacementW 内部也调用 GreGetGlyphIndicesW
  • 注意事项 :源码注释明确"Windows GetGlyphIndices 不支持代理对(surrogate pairs)"------高位/低位代理会各自独立查询;bSubset 目前是 FIXME(未实现子集化语义);字符与输出共用一块缓冲是刻意优化(workBuf 前部是 pgi,后部是 pwc,safePwc = &workBuf[cwc])。
3.3.4 GreGetKerningPairs(font.c 第 53~109 行)
c 复制代码
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) 先取数量;④ 数量超 NumPairsERROR_INSUFFICIENT_BUFFER;⑤ 分配池缓冲 → ftGdiGetKerningPairs(FontGDI, Count, pKP) 填充 → RtlCopyMemory 拷回用户缓冲(此处无 SEH/Probe 保护,是内部版本前提)→ 释放。
  • 调用方 :ntuser 的 NtGdiGetKerningPairs 服务包装后调用(见 3.3.5);GreGetCharacterPlacementWGCP_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 行)
c 复制代码
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)
c 复制代码
 /*
 * @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 stubUNIMPLEMENTED 宏打 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 禁用)
c 复制代码
#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 行)
c 复制代码
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 → IntRequestFontSizeFT_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 行)
c 复制代码
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;
}
参数 类型 说明
Dc PDC 已锁定的 DC
  • 作用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 行)
c 复制代码
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 && cjBufExAllocatePoolZero 分配内核缓冲(零初始化);③ ftGdiGetGlyphOutline(freetype.c 第 4500 行,细节见 5.3):RealizeFontInit → 取 OTM(用于平均宽度比例)→ IntLockFreeTypeTextIntUpdateSizeIntMatrixFromMx(世界→设备变换)+ FT_Set_Transformget_glyph_index_flaggediFormat & GGO_GLYPH_INDEX 时 wch 直接当索引)→ FT_Load_GlyphGGO_UNHINTEDFT_LOAD_NO_HINTING)→ 计算 26.6 定点 box(horiBearingX/width/height)→ 若需变换(宽度比例/世界变换/旋转 IntEscapeMatrix/pmat2)则 FT_Vector_Transform 四个角点求外接盒 → 填 GLYPHMETRICS(gmCellIncX/Y、gmBlackBoxX/Y、gmptGlyphOrigin)→ GGO_METRICS 直接返回;GGO_BITMAPFT_Render_Glyph 后按 ((width+31)>>5)<<2 对齐的 pitch 拷贝;④ 两个 SEH 块分别把 pvBuf 与 gm 拷回用户缓冲;⑤ 任一失败置 ERROR_INVALID_PARAMETER 返回 GDI_ERROR。
  • 调用方 :gdi32full 的 GetGlyphOutline(GDI+、文字转曲线、打印矢量输出等)。
  • 注意事项cjBuf==0UnsafeBuf==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 行)
c 复制代码
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 && SizeProbeForRead(注意:是 探测,函数实际只写 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==NULLSize=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==NULLSize==0 时返回整表所需大小(双趟模式)。
3.3.12 NtGdiGetFontUnicodeRanges(font.c 第 731~801 行)
c 复制代码
/* @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";cGlyphsSupportedranges[0].cGlyphs 在首 range 初始化时置 0 后逐项累加(首个 range 的 wcLow 在 glyphset 分支外才设置,注意 ranges[0].cGlyphs 先 0 后 +1 的边界处理)。
3.3.13 NtGdiGetOutlineTextMetricsInternalW(font.c 第 963~1038 行)
c 复制代码
/*
 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 > DataERROR_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_YFT_MulFix(value, XScale) 按 26.6 缩放;按 PRIMARYLANGID(gusLanguageID) 选择 EnglishUS 或 UserLanguage 缓存槽。
3.3.14 NtGdiGetFontResourceInfoInternalW(font.c 第 1040~1150 行)
c 复制代码
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_FontListHeadFilename 匹配的 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);BufferdwBytes==0 时也分配 sizeof(DWORD) 的占位缓冲,避免空指针路径。
3.3.15 NtGdiGetRealizationInfo(font.c 第 1152~1219 行)
c 复制代码
/* @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 → RealizeFontInitASSERT(pTextObj != NULL)------失败即断言)→ ObjToGDI → 解锁;② ftGdiRealizationInfo(freetype.c 第 6432 行):FT_HAS_FIXED_SIZESRI_TECH_BITMAP;否则 FT_IS_SCALABLERI_TECH_SCALABLE,再否则 RI_TECH_FIXEDiUniq = FontObj.iUniqdwUnknown = -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 个 ------GreCreateFontIndirectWNtGdiHfontCreateHfontCreateRealizeFontInitFontGetObject资源组(3.2 节)5 个 ------IntCheckFontPathNamesNtGdiAddFontResourceWNtGdiRemoveFontResourceWNtGdiAddFontMemResourceExNtGdiRemoveFontMemResourceEx度量/查询组(3.3 节)16 个 ------NtGdiGetCharWidthWNtGdiGetCharABCWidthsWNtGdiGetGlyphIndicesWNtGdiGetGlyphIndicesWInternalGreGetKerningPairsNtGdiGetKerningPairsNtGdiGetCharacterPlacementW(stub)、GreGetCharacterPlacementW(#if 0)、IntGetCharDimensionsIntGetFontLanguageInfoNtGdiGetGlyphOutlineNtGdiGetFontDataNtGdiGetFontUnicodeRangesNtGdiGetOutlineTextMetricsInternalWNtGdiGetFontResourceInfoInternalWNtGdiGetRealizationInfo

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_FreeTypeIntLoadFontsInRegistry(注册表已有字体则跳过扫描)、IntLoadSystemFonts(扫描 \SystemRoot\Fonts\.ttf/ .ttc/.otf/ .otc/.fon/ .fnt,带 AFRX_WRITE_REGISTRY 写注册表)、IntLoadFontSubstListFontLink_LoadSettings/DefaultFonts/DefaultCharset/PopulateEntries
FreeFontSupport 990 释放字体缓存链、字体替换链、全局字体链(CleanupFontEntry)、FT_Done_Library
IntGdiAddFontResourceSingle 1877 单个字体文件加载:打开/建 section/映射 → SharedMem_CreateIntGdiLoadFontByIndexFromMemory → 写注册表(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 实现核心 :替换字体 → 私有链匹配 → 全局链匹配(FindBestFontFromListGetFontPenalty 惩罚分选最优)→ 填 TextFace(匹配面名或 IntGetFontLocalizedName 本地化名)→ FontObj.iUniq=1IntFontTypeflType → 记录合成请求 → 置 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 定点外接盒/gmCellIncXGGO_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_ENTRYg_FontCacheListHeadg_FontCacheNumEntries)协作:IntGetRealGlyph 先查缓存(按 Face+lfHeight+lfWidth+Emu+RenderMode+GlyphIndex 的散列键),未命中 FT_Load_Glyph/FT_Render_GlyphCacheGlyph 写入,形成"字形光栅缓存"。
  • 共享内存 :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)同时引用 GreGetTextExtentWGreGetTextExtentExWGreGetKerningPairsGreGetGlyphIndicesW,可见 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. 常见注意事项

  1. 锁纪律RealizeFontInit 返回已持锁的 TEXTOBJ,必须 配对 TEXTOBJ_UnlockText;未配对会泄漏句柄引用并破坏 push lock 状态。多数函数在解锁 DC 后才使用 TEXTOBJ,而 NtGdiGetFontUnicodeRanges 保持 DC 锁到 Exit 标签------改动时注意保持对称。
  2. FreeType 全局锁 :所有 FT_* 调用必须在 IntLockFreeType/IntUnLockFreeType 之间;TextIntRealizeFontftGdiGetGlyphOutline 内部会自己加锁(ASSERT_FREETYPE_LOCK_NOT_HELD 断言),调用方不得预先持锁,否则死锁或断言失败。
  3. 双趟缓冲协议GetFontDataGetGlyphOutlineGetOutlineTextMetricsGetFontUnicodeRangesGetKerningPairsGetFontResourceInfo 均支持"Buffer==NULL/Size==0 查大小 → 第二趟取数据"模式,第一趟返回所需字节数而非 0。
  4. SEH 与 Probe :所有用户缓冲访问必须 ProbeForRead/ProbeForWrite + _SEH2_TRY;异常统一 EngSetLastError 并返回失败码(GDI_ERROR / 0 / FALSE)。GreGetKerningPairs(内部版)的拷贝 SEH,只能被内核内部调用。
  5. MAX_TEXT_BUFFER 上限#define MAX_TEXT_BUFFER 0x2710000(约 40MB)约束 GetCharWidth/ABCWidths/GlyphIndices 的单次请求大小,防内核池过度分配;小请求优先用栈缓冲(40/28/80 元素),减少池分配压力。
  6. fl 复用语义 :GetCharWidth/GetCharABCWidths 的 fl 同时编码"输出 INT 还是 FLOAT"(0=FLOAT)与"输入是字形索引"(GCW_INDICES/GCABCW_INDICES),勿混淆。
  7. 延迟实现 :HfontCreate 后 TextObj->Font==NULLfl& TEXTOBJECT_INIT==0;在 RealizeFontInit/FontGetObject 之前调用度量函数会自动触发实现,但 GetObject唯一在实现失败时仍继续(仅 DPRINT)的路径。
  8. 私有内存字体AddFontMemResourceEx 返回的 HANDLE 是进程内序号(PrivateMemFontHandleCount 自增),不是内核句柄;不 Remove 会在进程退出时由 IntGdiCleanupPrivateFontsForProcess 统一回收;FR_PRIVATE | FR_NOT_ENUM 特性使其不出现在 EnumFonts/字体对话框中。
  9. 注册表持久化AddFontResourceW(flags=0)不写 注册表;只有 IntLoadSystemFonts 使用 AFRX_WRITE_REGISTRY\SystemRoot\Fonts\ 扫描结果写入 HKLM\...\Fontsg_FontRegPath)。注册表字体名含 " (TrueType)" 后缀。
  10. 未实现/降级点NtGdiGetCharacterPlacementW 是 UNIMPLEMENTED stub;GreGetCharacterPlacementW 整体 #if 0bSubset(GlyphIndices 子集化)、cjElfw 大小校验、cFiles>0(FontResourceInfo)均为 FIXME。IntGetFontLanguageInfo 多数特性掩码为 0(刻意保守)。
  11. 字距/Unicode 范围限制ftGdiGetKerningPairsftGetFontUnicodeRanges 都要求 charmap 为 FT_ENCODING_UNICODE,symbol 字体/非 Unicode 字体相关查询返回 0。
  12. 字形合成 :FONTGDI 的 RequestWeight/RequestItalic/RequestUnderline/RequestStrikeOutOriginalWeight/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 月)。所有行号以当前源码为准。

相关推荐
cxr8281 小时前
HyperMind Lab M1 架构地基 Implementation Plan <二>
开发语言·人工智能·架构
灵晔君1 小时前
【Linux】进程控制(一)——进程创建、进程终止与进程等待
linux·c语言·算法
特立独行的猫a1 小时前
Tauri v2的Rust应用 → HarmonyOS(鸿蒙 PC)移植30分钟速成指南
开发语言·rust·harmonyos·tauri·移植·鸿蒙pc
迷迭香yy1 小时前
大宗交易折溢价因子怎么挖掘本地化Python全流程实战
开发语言·人工智能·python
xieliyu.1 小时前
UPD协议结构以及开发中注意事项
java·开发语言·笔记·java-ee
Yweir1 小时前
AI大模型开发-Python介绍、版本说明
开发语言·人工智能·python
峥嵘life9 小时前
Android16 311Y3 EAP-TLS 网络连接失败分析与修复总结
android·开发语言·人工智能·php
mqiqe12 小时前
AgentScope Java Harness:4. 双层记忆系统 让 Agent 拥有真正的“长期大脑“
java·开发语言
gugucoding12 小时前
46. 【Java】JUC并发工具:让并发更简单
java·开发语言