iText7中TextRenderInfo.GetFontSize()返回错误值的排查与解决
PDF文本提取中字号返回1的原因及解决方案
问题背景
使用iText 7提取PDF文本时,4个测试PDF里有3个返回字号值为1,仅1个能获取正确字号;此前使用iTextSharp 5.5.13时可正常获取正确字号。相关代码及尝试的方法如下:
原提取代码
using System.Text; using iText.Kernel.Pdf; using iText.Kernel.Pdf.Canvas.Parser; using iText.Kernel.Pdf.Canvas.Parser.Data; using iText.Kernel.Pdf.Canvas.Parser.Listener; namespace PDFTextExtraction { class Program { static void Main(string[] args) { string pdfFilePath = "pathtopdf.pdf"; using (PdfReader pdfReader = new PdfReader(pdfFilePath)) { using (PdfDocument pdfDocument = new PdfDocument(pdfReader)) { MyTextEventListener listener = new MyTextEventListener(); for (int pageNum = 1; pageNum <= pdfDocument.GetNumberOfPages(); pageNum++) { PdfPage page = pdfDocument.GetPage(pageNum); PdfCanvasProcessor parser = new PdfCanvasProcessor(listener); parser.ProcessPageContent(page); } Console.WriteLine("提取的文本及字体信息:"); Console.WriteLine(listener.GetExtractedText()); } } } } public class MyTextEventListener : IEventListener { private readonly StringBuilder extractedText = new StringBuilder(); public void EventOccurred(IEventData data, EventType type) { if (type.Equals(EventType.RENDER_TEXT)) { TextRenderInfo renderInfo = (TextRenderInfo)data; string text = renderInfo.GetText(); string font = renderInfo.GetFont().GetFontProgram().ToString(); float fontSize = renderInfo.GetFontSize(); extractedText.AppendLine($"文本: {text} | 字体: {font} | 字号: {fontSize}"); } } public ICollection<EventType> GetSupportedEvents() { return new List<EventType> { EventType.RENDER_TEXT }; } public string GetExtractedText() { return extractedText.ToString(); } } }
已尝试的方法
float ascent = renderInfo.GetAscentLine().GetStartPoint().Get(1); float descent = renderInfo.GetDescentLine().GetStartPoint().Get(1); float fontSize = ascent - descent;
相关PDF操作符示例
BT 0 0.6 0.95 0 k /GS0 gs /T1_0 1 Tf 0.024 Tc -0.024 Tw 8 0 0 8 32.3386 171.361 Tm [(V)43.2(o)2.2(r)-1(n)1.5(ame)24( Na)7.9(c)-1.4(h)-1.1(n)1.4(a)0.5(me)] TJ ET
实际正确字号应为8pt,疑问:Tf里的1是字号吗?通过文本矩阵计算字号的方法是否可行?
原因分析
- PDF排版机制差异:在你提供的PDF操作符中,
T1_0 1 Tf是设置字体和原始字号参数(这里的1是Tf操作符的字号值),但后续的8 0 0 8 32.3386 171.361 Tm是文本矩阵操作,其中前两个8分别是X、Y方向的缩放因子。实际显示字号是Tf的字号值 × 文本矩阵的缩放因子,即1×8=8pt。 - iText版本行为变更:iTextSharp 5.5.13会自动结合
Tf的字号值和文本矩阵的缩放因子,返回实际显示的字号;而iText 7的GetFontSize()仅返回Tf操作符中的原始字号值(即1),不会自动应用文本矩阵的缩放。 - PDF生成工具的排版习惯:很多PDF生成工具为了排版灵活,会将
Tf的字号设为1,再通过文本矩阵的缩放来控制实际显示大小,这就导致GetFontSize()拿到的是原始值1,而非实际显示的字号。
解决方案
1. 通过文本矩阵计算实际字号(推荐)
你提到的方法是可行的,更准确的方式是取文本矩阵的Y方向缩放值(因为PDF中字体的大小对应Y轴方向的缩放),代码示例:
Matrix textMatrix = renderInfo.GetTextMatrix(); // 取Y方向缩放因子,绝对值处理防止翻转情况 float actualFontSize = Math.Abs(textMatrix.Get(Matrix.I22));
如果遇到非均匀缩放(X和Y缩放值不同),可根据需求取最大值或平均值:
float scaleX = Math.Abs(textMatrix.Get(Matrix.I11)); float scaleY = Math.Abs(textMatrix.Get(Matrix.I22)); float actualFontSize = Math.Max(scaleX, scaleY); // 或取平均值:float actualFontSize = (scaleX + scaleY) / 2;
2. 通过字体 ascent/descent 计算实际字号
你之前尝试的方法可以优化,通过计算ascent和descent线的Y坐标差,得到实际显示的字体高度(即视觉字号):
LineSegment ascentLine = renderInfo.GetAscentLine(); LineSegment descentLine = renderInfo.GetDescentLine(); // 计算Y坐标差,绝对值处理防止翻转 float actualFontSize = Math.Abs(ascentLine.GetStartPoint().Get(1) - descentLine.GetStartPoint().Get(1));
这个方法不受缩放是否均匀的影响,能直接得到视觉上的字号大小。
3. 兼容旧版本行为
如果需要和iTextSharp 5.5.13的行为一致,可在获取GetFontSize()后,结合文本矩阵的缩放因子计算:
float rawFontSize = renderInfo.GetFontSize(); Matrix textMatrix = renderInfo.GetTextMatrix(); float scaleY = Math.Abs(textMatrix.Get(Matrix.I22)); float actualFontSize = rawFontSize * scaleY;
内容的提问来源于stack exchange,提问作者Pronet66
相关产品推荐
相关产品推荐

