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

使用iText7将SVG插入PDF时字体变更问题排查

问题

使用iText7 8.0.4版本将PDF中的JPG替换为SVG后,SVG内的“Lucida Console”字体在PDF中变成了“Times New Roman”,但本地保存的SVG文件显示正常。以下是相关.NET代码:

public bool ReplaceImageWithSvg(string inputPdf, string outputPdf)
{
    try
    {
        using (PdfReader pdfReader = new PdfReader(inputPdf))
        using (PdfWriter pdfWriter = new PdfWriter(outputPdf))
        using (PdfDocument pdfDocument = new PdfDocument(pdfReader, pdfWriter))
        {
            int numPages = pdfDocument.GetNumberOfPages();
            string tempPath = System.IO.Path.GetTempPath();

            for (int pageNum = 1; pageNum <= numPages; pageNum++)
            {
                PdfPage page = pdfDocument.GetPage(pageNum);

                IList<PdfAnnotation> annotations = new List<PdfAnnotation>(page.GetAnnotations());

                foreach (PdfAnnotation annotation in annotations)
                {
                    if (annotation.GetSubtype().Equals(PdfName.Link))
                    {
                        PdfLinkAnnotation linkAnnotation = (PdfLinkAnnotation)annotation;

                        // Check if the link annotation is associated with an image
                        PdfArray rect = linkAnnotation.GetRectangle();
                        if (rect != null && rect.Size() == 4)
                        {
                            var tempSvgFile = System.IO.Path.Combine(tempPath, Guid.NewGuid().ToString() + ".svg");
                            var imageUrl = ExtractURL(linkAnnotation.GetAction().ToString());

                            var svgContent = FetchSvgImageAsync(imageUrl);

                            var imageStream = ModifySvgFontAttributes(svgContent, _newFontFamily, _increaseFontSizeBy, _increaseFontWeight, _newStrokeWidth);

                            SaveSvgToFile(imageStream, tempSvgFile);

                            ReplaceImageWithSvg(page, rect, tempSvgFile);

                            page.RemoveAnnotation(linkAnnotation);

                            if (File.Exists(tempSvgFile))
                            {
                                File.Delete(tempSvgFile);
                            }
                        }
                    }
                }
            }
        }

        return true;
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Error replacing image with SVG: {ex.Message}");
        return false;
    }
}

private void ReplaceImageWithSvg(PdfPage page, PdfArray rect, string svgImagePath)
{
    try
    {
        // Extract position and dimensions of the existing image
        float x = rect.GetAsNumber(0).FloatValue();
        float y = rect.GetAsNumber(1).FloatValue();
        // Added on to get rid of the border
        float width = rect.GetAsNumber(2).FloatValue() - x + _border;
        float height = rect.GetAsNumber(3).FloatValue() - y + _border;


        // Convert the SVG image to PdfFormXObject
        PdfDocument pdfDoc = page.GetDocument();
        using (Stream svgStream = File.OpenRead(svgImagePath))
        {
            PdfFormXObject svgObject = SvgConverter.ConvertToXObject(svgStream, pdfDoc);

            // Get the content stream of the page
            PdfCanvas canvas = new PdfCanvas(page);
            // Set fill color to white(or match the page background)
            canvas.SetFillColorGray(1.0f); // White color
            // Cover the area with a rectangle
            canvas.Rectangle(x, y, width, height);
            canvas.Fill();

            // Calculate the scale to fit the SVG in the specified rectangle
            // Assumes the SVG's aspect ratio matches the target rectangle.
            float scaleX = width / svgObject.GetWidth();
            float scaleY = height / svgObject.GetHeight();

            // Create transformation matrix for scaling and then translating the image
            float[] matrix = new float[] { scaleX, 0, 0, scaleY, x, y };

            // Add the SVG Form XObject to the canvas with transformation
            canvas.AddXObjectWithTransformationMatrix(svgObject, matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]);
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Error replacing image with SVG: {ex.Message}");
    }
}

private string ModifySvgFontAttributes(string svgContent, string newFontFamily, string fontSizeIncrease, int fontWeightIncrease, string strokeWidth)
{
    XDocument svgDoc = XDocument.Parse(svgContent);

    // Update font-family
    var elementsWithFontFamily = svgDoc.Descendants().Where(e => e.Attribute("font-family") != null);
    foreach (var elem in elementsWithFontFamily)
    {
        elem.SetAttributeValue("font-family", newFontFamily);
    }

    return svgDoc.ToString();
}
原因与解决办法

核心原因

iText的SvgConverter处理SVG字体时,若无法定位到指定字体(Lucida Console未被正确注册或嵌入),会自动切换至默认字体Times New Roman。本地SVG显示正常是因为系统字体库能找到该字体,但iText转换过程中未加载对应字体文件。

解决步骤

  1. 注册Lucida Console字体
    在转换逻辑执行前,将字体文件注册到iText的字体工厂:

    // 替换为Lucida Console的实际路径,Windows下通常为C:\Windows\Fonts\lucon.ttf
    FontProgramFactory.RegisterFont(@"C:\Windows\Fonts\lucon.ttf");
    
  2. 配置SVG转换的字体提供者
    修改ReplaceImageWithSvg方法中的转换代码,通过SvgConverterProperties指定字体来源,确保转换器能找到目标字体:

    using (Stream svgStream = File.OpenRead(svgImagePath))
    {
        SvgConverterProperties properties = new SvgConverterProperties();
        FontProvider fontProvider = new FontProvider();
        fontProvider.AddFont(@"C:\Windows\Fonts\lucon.ttf");
        properties.SetFontProvider(fontProvider);
        // 可选:禁用字体回退,强制使用指定字体,避免自动替换
        properties.SetUseFontFallback(false);
    
        PdfFormXObject svgObject = SvgConverter.ConvertToXObject(svgStream, pdfDoc, properties);
        // 后续画布操作代码保持不变
    }
    
  3. 完善SVG字体属性修改逻辑
    若SVG使用font简写属性(而非单独的font-family),现有代码无法覆盖该场景,需补充处理:

    private string ModifySvgFontAttributes(string svgContent, string newFontFamily, string fontSizeIncrease, int fontWeightIncrease, string strokeWidth)
    {
        XDocument svgDoc = XDocument.Parse(svgContent);
    
        // 处理单独的font-family属性
        var elementsWithFontFamily = svgDoc.Descendants().Where(e => e.Attribute("font-family") != null);
        foreach (var elem in elementsWithFontFamily)
        {
            elem.SetAttributeValue("font-family", newFontFamily);
        }
    
        // 处理font简写属性
        var elementsWithFont = svgDoc.Descendants().Where(e => e.Attribute("font") != null);
        foreach (var elem in elementsWithFont)
        {
            string fontAttr = elem.Attribute("font").Value;
            string[] parts = fontAttr.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries);
            if (parts.Length >= 4)
            {
                // 替换最后一部分(字体族)
                parts[parts.Length - 1] = newFontFamily;
                elem.SetAttributeValue("font", string.Join(" ", parts));
            }
        }
    
        return svgDoc.ToString();
    }
    
  4. 确保字体嵌入PDF
    配置PdfWriter确保字体被嵌入PDF,避免在无该字体的设备上显示异常:

    PdfWriterProperties writerProps = new PdfWriterProperties();
    // 设置字体嵌入策略为自动嵌入所有使用的字体
    writerProps.SetFontEmbedding(true);
    using (PdfWriter pdfWriter = new PdfWriter(outputPdf, writerProps))
    {
        // 后续文档处理代码保持不变
    }
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 18:18:12