enumfontfamiliesex是windows gdi中用于枚举设备上下文支持的已安装字体的核心函数,返回字体名称而非文件路径;需结合注册表映射或getfontresourceinfo等手段获取实际路径,且不保证字体名与文件名一一对应。

Windows 上用 GDI 获取字体列表和路径
Windows 自带的 GDI API 是最稳定、无需额外依赖的方式,EnumFontFamiliesEx 能枚举当前设备上下文支持的所有已安装字体,但注意:它**不直接返回字体文件路径**。你需要结合 GetFontResourceInfo 或从注册表/系统目录反查。
关键点:
-
EnumFontFamiliesEx返回的是字体名称(如"Microsoft YaHei"),不是文件名(如msyh.ttc) - 字体路径通常在
C:WindowsFonts,但部分字体可能通过AddFontResource动态加载,不在该目录 - 注册表路径
HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindows NTCurrentVersionFonts存有字体名 → 文件名映射,但不包含完整路径,需拼接 - 真正可靠的路径获取方式是调用
GetFontResourceInfo(需先用AddFontResourceEx加载字体再查,实际中常绕不开“先猜路径再验证”)
Linux 上解析 fontconfig 数据库
Linux 没有统一“已安装字体”概念,而是靠 fontconfig 管理字体搜索路径。直接读取 /usr/share/fonts、~/.local/share/fonts 等目录不可靠——很多字体被 fc-cache 索引后才生效,且可能被 fonts.conf 排除。
正确做法是调用 fontconfig C API:
- 用
FC_FAMILY和FC_FILE属性查询所有匹配字体:FcObjectSetCreate+FcFontList -
FcPatternGetString提取FC_FILE值,即真实字体文件路径(如"/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf") - 必须调用
FcInit()初始化,否则FcFontList返回空 - 注意:某些字体(如 Noto Color Emoji)可能是集合(.ttc),
FC_FILE指向容器文件,不是单个字形文件
macOS 上用 Core Text 枚举字体 URL
macOS 的 CTFontManagerCopyAvailableFontURLs 是唯一推荐方式,它返回 CFArrayRef,每个元素是 CFURLRef,可转为 POSIX 路径字符串。
常见误区:
- 不要用
ATSUFontIterator(已废弃)或遍历/System/Library/Fonts—— 用户字体在~/Library/Fonts,网络字体、可变字体、已禁用字体不会出现在目录里 -
CTFontManagerCopyAvailableFontURLs默认只返回启用字体;若要包含已禁用字体,需传kCTFontManagerScopeUser或kCTFontManagerScopeProcess参数并确保权限 - 返回的 URL 可能是包内路径(如
file:///System/Library/Fonts/Helvetica.ttc),需用CFURLGetFileSystemRepresentation转成 C 字符串 - 注意内存管理:返回的
CFArrayRef需CFRelease,URL 也要释放
跨平台方案为什么难落地
没有标准 C++ 接口能跨平台获取字体路径,因为三者底层机制完全不同:Windows 依赖 GDI + 注册表,Linux 依赖 fontconfig 缓存,macOS 依赖 Core Text 的字体管理器状态。
如果你硬要写一个统一接口:
- 预处理器分支不可避免:
#ifdef _WIN32/#ifdef __linux__/#ifdef __APPLE__ - Linux 下链接
-lfontconfig,macOS 下加-framework CoreText -framework ApplicationServices,Windows 下只需gdi32.lib - 路径格式差异大:Windows 是反斜杠+盘符,Linux/macOS 是正斜杠;macOS 可能返回
file://URL,Linux 返回纯路径,Windows 得自己拼"C:\Windows\Fonts\" - 最易踩的坑:认为“字体名 ↔ 文件名”是一一对应——实际上同一字体家族可能分散在多个文件(如
Roboto-Regular.ttf、Roboto-Bold.ttf),而一个 .ttc 文件又含多个字体
真正在意路径的场景(比如提取字体元数据、复制备份),建议按平台单独实现,别强求抽象层。字体名和路径之间的映射从来就不是操作系统保证的契约,只是约定俗成的实践。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











