gdiplus的privatefontcollection仅支持可安装的.ttf/.otf字体,加载失败时addfontfile返回0且无异常;需用getfamilies()和isstyleavailable确认字重可用性;元数据须手动解析name/os/2表或借助skiasharp等库;跨平台应避免system.drawing.common依赖gdi+。

用 Gdiplus 加载 .ttf/.otf 文件失败:不是所有字体都能直接用 PrivateFontCollection
Windows GDI+ 的 PrivateFontCollection 确实能加载字体文件,但只支持“可安装字体”格式——也就是不含 DRM、未被系统锁定、且结构完整的 .ttf 或 .otf。很多从网页下载的字体(尤其是带子集或加密 hinting 的 OTF)会静默失败,AddFontFile 返回 0 且不抛异常。
- 先检查文件是否能双击在 Windows 字体预览器里打开;打不开的,
PrivateFontCollection基本也无解 - 别依赖
GetFamilies().Length判断是否加载成功——它可能返回空数组但不报错 - 实际加载后,必须调用
GetFamilies()并遍历每个FontFamily的IsStyleAvailable,才能确认某个字重(如 Bold)是否真正可用
读取字体元数据(名称、版权、版本)要用 GetFontData + 解析 OS/2 和 name 表
.NET 没有内置字体解析器,Font 或 FontFamily 对象只暴露渲染相关属性(如 IsBold),不提供版权、设计师、许可证等信息。这些藏在字体文件的二进制表中,得手动读取 name 表(偏移 0x0000000C)和 OS/2 表(偏移 0x00000005)。
- 用
File.ReadAllBytes读取 .ttf/.otf 文件,再按 TrueType 规范跳转到指定 offset 解析字符串(注意 big-endian 和 UTF-16 BE 编码) - 常见坑:OTF 可能用 CFF 轮廓而非 glyf 表,但
name表结构一致,可复用同一套解析逻辑 - 别硬写解析器——推荐直接用开源库
SkiaSharp的SKTypeface.FromFile(path),它内部已处理好跨平台兼容性,再调用GetTableData(SKTypefaceTableTag.Name)拿原始 name 表字节
在 WPF/UWP/MAUI 中绕过 GDI+ 限制:用 FontSource 或 FontManager
WPF 的 FontSource、MAUI 的 FontManager.Default.RegisterFont 不走 GDI+,而是绑定到 DirectWrite 或 Core Text,对 OTF 支持更好,且能处理嵌入式字体权限(如 fsType = 0x0008 表示仅预览)。
- WPF 示例:
new FontSource(new Uri("pack://application:,,,/Fonts/myfont.ttf")),然后传给TextBlock.FontFamily - MAUI 中注册后,XAML 里直接写
FontFamily="myfont.ttf#MyFontName"(# 后是 name 表里的完整字体名,不是文件名) - 注意:UWP 的
Windows.UI.Text不接受本地路径,必须把字体放Assets/Fonts/并设为 Content
跨平台(Linux/macOS)读字体信息只能靠 FontConfig 或 CoreText 绑定
.NET 6+ 的 System.Drawing.Common 在非 Windows 上默认禁用 GDI+,PrivateFontCollection 会直接抛 PlatformNotSupportedException。此时必须切换底层。
- Linux:用
FontConfig命令行工具辅助,比如fc-scan --format "%{family}\n%{copyright}\n%{version}" /path/to/font.ttf,再用Process.Start捕获输出 - macOS:调用
CoreText.CTFontManagerCreateFontDescriptorsFromURL(需NativeLibrary.Load("CoreText")) - 更稳方案:统一用
SvgNet或SharpFont这类纯 C# 字体解析库,它们不依赖系统 API,但要自己处理 OpenType 表校验和字符映射
字体文件的 name 表编码、子集标记、数字签名验证,这些细节在不同系统上表现不一。别假设“Windows 能读,Linux 就一定能”,每次换平台都得单独验证解析逻辑。










