使用iText获取Word与CorelDraw生成PDF中指定字符串位置的问题
解决iText提取不同来源PDF指定字符串位置的问题
问题背景
使用iText提取Word、CorelDraw等工具生成的PDF中指定字符串位置时,CorelDraw生成的PDF能正常获取完整字符串的位置,但Word生成的PDF里字符串会被拆分成如“T”“est”这类片段,导致原代码无法匹配到完整目标字符串。
问题原因
PDF的文本渲染单元并非严格按自然语言的字符串划分:
- CorelDraw生成PDF时,通常会将连续字符串作为单个文本块输出
- Word生成PDF时,可能因字体子集嵌入、排版优化(如字距调整、连字)或压缩策略,将完整字符串拆分为多个小文本块,以满足渲染或文件大小优化需求。
通过Word导出设置尝试解决
可以尝试调整Word导出PDF的参数,部分场景下能减少文本块拆分:
- 导出时选择**「最佳打印质量」**而非「最小文件大小」:后者为压缩文件会拆分文本块
- 关闭**「PDF/A兼容性」**:PDF/A的规范可能强制调整文本结构
- 选择**「嵌入所有字体」**而非「仅嵌入所用字符」:完整字体嵌入可能保留更完整的文本块
注意:这些设置不保证100%生效,Word的PDF导出逻辑因版本不同可能有差异。
代码层面的通用解决方案
如果无法通过设置解决,需要通过代码收集并拼接文本块,再匹配目标字符串并合并其位置信息。以下是修改后的实现:
1. 定义文本块存储类
用于记录每个文本块的内容和对应的渲染信息:
public class TextChunk { public string Text { get; set; } public TextRenderInfo RenderInfo { get; set; } }
2. 修改事件监听器收集文本块
将所有渲染的文本块收集起来,而非单次匹配:
class MyEventListener : IEventListener { private readonly List<TextChunk> _textChunks = new List<TextChunk>(); public List<TextChunk> TextChunks => _textChunks; public void EventOccurred(IEventData data, EventType type) { if (type == EventType.RENDER_TEXT) { var renderInfo = (TextRenderInfo)data; _textChunks.Add(new TextChunk { Text = renderInfo.GetText(), RenderInfo = renderInfo }); } } public ICollection<EventType> GetSupportedEvents() { return new List<EventType> { EventType.RENDER_TEXT }; } }
3. 主逻辑:拼接文本、匹配目标、合并位置
private static readonly string specificText = "Test"; private void BtnNewPDF_Click(object sender, RoutedEventArgs e) { using var pdfDoc = new PdfDocument(new PdfReader(@"D:\Word-test.pdf")); var pdfPage = pdfDoc.GetPage(1); var listener = new MyEventListener(); using var canvasProcessor = new PdfCanvasProcessor(listener); canvasProcessor.ProcessPageContent(pdfPage); // 拼接所有文本块得到完整页面文本 var fullText = string.Join("", listener.TextChunks.Select(c => c.Text)); var startIndex = fullText.IndexOf(specificText, StringComparison.OrdinalIgnoreCase); if (startIndex >= 0) { var endIndex = startIndex + specificText.Length; var currentPos = 0; var targetRenderInfos = new List<TextRenderInfo>(); // 定位包含目标字符串的所有文本块 foreach (var chunk in listener.TextChunks) { var chunkEnd = currentPos + chunk.Text.Length; // 检查当前块与目标字符串的重叠范围 if (!(chunkEnd <= startIndex || currentPos >= endIndex)) { targetRenderInfos.Add(chunk.RenderInfo); } currentPos = chunkEnd; if (currentPos >= endIndex) break; } // 合并所有目标块的边界框 if (targetRenderInfos.Any()) { var combinedRect = targetRenderInfos[0].GetDescentLine().GetBoundingRectangle(); foreach (var info in targetRenderInfos.Skip(1)) { var rect = info.GetDescentLine().GetBoundingRectangle(); combinedRect = combinedRect.Merge(rect); } MessageBox.Show($"X坐标:{combinedRect.GetX()}"); MessageBox.Show($"Y坐标:{combinedRect.GetY()}"); // 可选:显示完整边界框信息 // MessageBox.Show($"边界框:{combinedRect.ToString()}"); } } else { MessageBox.Show("未找到目标字符串"); } }
方案优势
- 通用性强:不管PDF由什么工具生成,只要文本按视觉顺序渲染,就能正确拼接并匹配目标字符串
- 准确性高:通过合并对应文本块的边界框,得到完整目标字符串的位置
内容的提问来源于stack exchange,提问作者CodeCatia
相关产品推荐
相关产品推荐

