使用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转换过程中未加载对应字体文件。
解决步骤
注册Lucida Console字体
在转换逻辑执行前,将字体文件注册到iText的字体工厂:// 替换为Lucida Console的实际路径,Windows下通常为C:\Windows\Fonts\lucon.ttf FontProgramFactory.RegisterFont(@"C:\Windows\Fonts\lucon.ttf");配置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); // 后续画布操作代码保持不变 }完善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(); }确保字体嵌入PDF
配置PdfWriter确保字体被嵌入PDF,避免在无该字体的设备上显示异常:PdfWriterProperties writerProps = new PdfWriterProperties(); // 设置字体嵌入策略为自动嵌入所有使用的字体 writerProps.SetFontEmbedding(true); using (PdfWriter pdfWriter = new PdfWriter(outputPdf, writerProps)) { // 后续文档处理代码保持不变 }
内容的提问来源于stack exchange,提问作者Truc
相关产品推荐
相关产品推荐

