如何让PDFsharp自定义FontResolver兼容系统已安装标准字体?
解决PDFsharp自定义FontResolver后无法使用系统字体的问题
要同时支持自定义字节数组字体和系统预装字体,核心是让自定义IFontResolver在找不到自定义字体时,自动回退到系统字体的查找逻辑。PDFsharp没有公开默认解析器,但我们可以手动实现系统字体的路径查找和加载逻辑。
实现思路
- 自定义
IFontResolver实现类,优先处理你的自定义字节数组字体; - 当请求的字体不在自定义集合中时,遍历系统字体目录,匹配对应的字体文件;
- 在
GetFont方法中,根据标识区分是自定义字体还是系统字体,分别返回字节数组。
完整代码实现
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Drawing.Text; using PdfSharp.Drawing; public class CustomFontResolver : IFontResolver { // 存储自定义字体:键是字体标识(格式:名称_加粗状态_斜体状态),值是字体字节数组 private readonly Dictionary<string, byte[]> _customFontStore; public CustomFontResolver(Dictionary<string, byte[]> customFonts) { _customFontStore = customFonts ?? new Dictionary<string, byte[]>(); } public FontResolverInfo ResolveTypeface(string familyName, bool isBold, bool isItalic) { // 1. 先尝试匹配自定义字体 var customFontKey = $"{familyName.ToLower()}_{(isBold ? "bold" : "regular")}_{(isItalic ? "italic" : "normal")}"; if (_customFontStore.ContainsKey(customFontKey)) { return new FontResolverInfo(customFontKey); } // 2. 自定义字体找不到,查找系统字体 string systemFontDisplayName = BuildSystemFontDisplayName(familyName, isBold, isItalic); string systemFontPath = FindSystemFontPath(systemFontDisplayName); if (!string.IsNullOrEmpty(systemFontPath)) { // 用系统字体文件路径作为标识,后续GetFont直接读取该文件 return new FontResolverInfo(systemFontPath); } // 3. 兜底:找不到时返回默认字体(比如Arial),也可以抛出异常 return new FontResolverInfo(FindSystemFontPath("Arial") ?? throw new ArgumentException($"Font {familyName} not found in custom or system fonts.")); } public byte[] GetFont(string faceName) { // 先尝试取自定义字体 if (_customFontStore.TryGetValue(faceName, out var customBytes)) { return customBytes; } // 读取系统字体文件 if (File.Exists(faceName)) { return File.ReadAllBytes(faceName); } throw new ArgumentException($"Requested font '{faceName}' cannot be loaded."); } // 构造系统字体的显示名称(比如Verdana Bold Italic) private string BuildSystemFontDisplayName(string familyName, bool isBold, bool isItalic) { var nameParts = new List<string> { familyName }; if (isBold) nameParts.Add("Bold"); if (isItalic) nameParts.Add("Italic"); return string.Join(" ", nameParts); } // 在系统字体目录中查找对应字体的文件路径 private string FindSystemFontPath(string fontDisplayName) { string systemFontDir = Environment.GetFolderPath(Environment.SpecialFolder.Fonts); var fontFiles = Directory.GetFiles(systemFontDir, "*.ttf") .Concat(Directory.GetFiles(systemFontDir, "*.otf")); foreach (var filePath in fontFiles) { using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read); var fontCollection = new PrivateFontCollection(); try { fontCollection.AddFontFile(filePath); var fontFamily = fontCollection.Families.FirstOrDefault(f => f.Name.Equals(fontDisplayName, StringComparison.OrdinalIgnoreCase)); if (fontFamily != null) { return filePath; } } catch { // 跳过损坏的字体文件 continue; } } return null; } }
使用方式
- 加载自定义字体字节数组(比如从资源文件):
// 假设你的资源文件中有名为CustomFont_Regular和CustomFont_Bold的字体资源 var customFonts = new Dictionary<string, byte[]> { { "mycustomfont_regular_normal", Properties.Resources.CustomFont_Regular }, { "mycustomfont_bold_normal", Properties.Resources.CustomFont_Bold } };
- 设置全局FontResolver:
GlobalFontSettings.FontResolver = new CustomFontResolver(customFonts);
- 正常使用PDFsharp的字体API即可:
// 使用自定义字体 var customFont = new XFont("MyCustomFont", 12, XFontStyle.Regular); // 使用系统字体(比如Verdana) var systemFont = new XFont("Verdana", 12, XFontStyle.BoldItalic);
注意事项
- 字体名称匹配忽略大小写,避免因大小写不一致导致的查找失败;
- 系统字体查找逻辑会遍历字体目录,首次调用可能略慢,可考虑缓存常用系统字体的路径;
- 桌面应用默认有权限读取系统字体目录,Web应用需确保应用池账号有对应权限;
- 如果需要支持更多字体变种(比如Light、Black),可以扩展
BuildSystemFontDisplayName方法的逻辑。
内容的提问来源于stack exchange,提问作者Jayne
相关产品推荐
相关产品推荐

