You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让PDFsharp自定义FontResolver兼容系统已安装标准字体?

解决PDFsharp自定义FontResolver后无法使用系统字体的问题

要同时支持自定义字节数组字体和系统预装字体,核心是让自定义IFontResolver在找不到自定义字体时,自动回退到系统字体的查找逻辑。PDFsharp没有公开默认解析器,但我们可以手动实现系统字体的路径查找和加载逻辑。

实现思路

  1. 自定义IFontResolver实现类,优先处理你的自定义字节数组字体;
  2. 当请求的字体不在自定义集合中时,遍历系统字体目录,匹配对应的字体文件;
  3. 在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;
    }
}

使用方式

  1. 加载自定义字体字节数组(比如从资源文件):
// 假设你的资源文件中有名为CustomFont_Regular和CustomFont_Bold的字体资源
var customFonts = new Dictionary<string, byte[]>
{
    { "mycustomfont_regular_normal", Properties.Resources.CustomFont_Regular },
    { "mycustomfont_bold_normal", Properties.Resources.CustomFont_Bold }
};
  1. 设置全局FontResolver:
GlobalFontSettings.FontResolver = new CustomFontResolver(customFonts);
  1. 正常使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 10:20:35