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

使用Svg.Skia转换SVG到PDF时自定义字体不生效问题

解决Svg.Skia中自定义OTF字体在PDF中不生效的问题

你的代码中FromFamilyName方法已被触发并返回字体,但PDF中未生效,可能是字体匹配逻辑、字体元数据匹配或加载方式的问题,以下是具体排查和解决方法:

1. 确认加载字体的实际元数据

首先要明确你加载的OTF字体实际的FamilyName、Weight等参数,因为字体文件内部的元数据可能和你预期的不一致(比如你以为家族名是"Font",实际可能是"Font Bold")。修改你的AddTypeface方法,添加日志输出:

public void AddTypeface(Stream stream)
{
    var typeface = _fontManager.CreateTypeface(stream);
    if (typeface == null)
    {
        // 字体加载失败,需检查资源流是否正确
        return;
    }
    // 打印字体元数据,确认实际值
    Console.WriteLine($"加载字体:FamilyName={typeface.FamilyName}, Weight={typeface.FontStyle.Weight}, Slant={typeface.FontStyle.Slant}");
    _typefaces.Add(typeface);
}

运行后查看输出,对比SVG中font-family、font-weight的值是否和字体实际元数据匹配。比如如果输出的FamilyName是"Font Bold",那么SVG中的font-family应该改为"Font Bold",或者调整匹配逻辑。

2. 优化字体匹配逻辑

你当前的匹配逻辑是严格等于所有参数,这很容易因为字体实际参数和SVG中的设置不完全一致而失败(比如有些粗体字体的Weight是800而非标准的700)。修改FromFamilyName方法,采用更宽松的匹配策略:

public SKTypeface? FromFamilyName(string fontFamily, SKFontStyleWeight fontWeight, SKFontStyleWidth fontWidth, SKFontStyleSlant fontStyle)
{
    // 先匹配家族名(忽略大小写)
    var candidates = _typefaces
        .Where(t => string.Equals(t.FamilyName, fontFamily, StringComparison.OrdinalIgnoreCase))
        .ToList();

    if (!candidates.Any())
        return null;

    // 过滤倾斜样式匹配的
    candidates = candidates.Where(t => t.FontStyle.Slant == fontStyle).ToList();

    if (!candidates.Any())
        return null;

    // 选择权重最接近的字体,而非严格等于
    return candidates.OrderBy(t => Math.Abs((int)fontWeight - t.FontStyle.Weight)).FirstOrDefault();
}

这种方式会优先匹配家族名和倾斜样式,再选择与目标权重最接近的字体,大幅提升命中概率。

3. 尝试直接注册字体到默认字体管理器

自定义ITypefaceProvider并非必须,你可以直接将字体注册到SkiaSharp的默认字体管理器,让系统自动处理字体查找:

var fontResources = new[] { "SomeNamespace.fonts.Fonts.Font Bold.otf" };

foreach (var fontResource in fontResources)
{
    using var fontStream = typeof(ThisClass).Assembly.GetManifestResourceStream(fontResource)!;
    // 将字体注册到默认字体管理器
    if (!SKFontManager.Default.Register(fontStream))
    {
        // 注册失败处理
    }
}

var svg = new SKSvg();
// 无需设置TypefaceProviders,默认会从系统和注册的字体中查找
svg.FromSvg(svgContent);

var picture = svg.Picture!;
var memoryStream = new MemoryStream();
picture.ToPdf(memoryStream, SKColors.Transparent, 1f, 1f);

return memoryStream;

这种方式更简洁,且能避免自定义Provider可能出现的匹配问题。

4. 检查PDF字体嵌入情况

如果以上步骤都无效,可能是PDF未嵌入字体导致查看器使用替代字体。你可以通过SKDocument手动生成PDF,确保字体嵌入:

using var document = SKDocument.CreatePdf(memoryStream);
using var canvas = document.BeginPage(picture.CullRect.Width, picture.CullRect.Height);
canvas.DrawPicture(picture);
document.EndPage();
document.Close();

这种方式更底层,能确保SkiaSharp正确处理字体嵌入。

内容的提问来源于stack exchange,提问作者kthuis

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 12:58:19