如何在C#中使用iText按正确顺序提取带格式的PDF文本?
结合iText文本顺序提取与格式信息捕获的实现方案
核心思路
要同时实现按视觉顺序提取文本和捕获字体格式信息,本质是把格式数据绑定到按视觉排序后的文本块上——前者解决文本乱序问题,后者解决格式提取问题,关键在于在遍历文本元素时同步记录位置与格式属性。
具体实现步骤
1. 搭建视觉顺序遍历的基础框架
沿用你提到的文本顺序提取方案,基于iText7的LocationTextExtractionStrategy扩展,通过自定义TextChunkLocationStrategy确保文本块按行、列的视觉顺序排列:
// 自定义文本块位置策略,适配你的PDF布局(比如多列、复杂排版) TextChunkLocationStrategy visualLocationStrategy = new TextChunkLocationStrategy() { @Override public TextChunkLocation createLocation(RenderTextEvent renderEvent) { // 这里实现按视觉位置排序的逻辑,比如按Y坐标(行)降序、X坐标(列)升序 return new DefaultTextChunkLocation( renderEvent.getFontSize(), renderEvent.getBaseline().getY(), renderEvent.getBaseline().getX(), renderEvent.getWidthOfSpace() ); } };
2. 扩展策略同步捕获格式信息
继承LocationTextExtractionStrategy,重写eventOccurred方法,在处理文本渲染事件时,同时记录字体名、大小、样式等属性:
public class FormattedVisualTextStrategy extends LocationTextExtractionStrategy { private List<FormattedTextChunk> formattedChunkList = new ArrayList<>(); public FormattedVisualTextStrategy(TextChunkLocationStrategy locationStrategy) { super(locationStrategy); } @Override public void eventOccurred(IEventData data, EventType type) { if (type == EventType.RENDER_TEXT) { RenderTextEvent renderEvent = (RenderTextEvent) data; PdfFont font = renderEvent.getFont(); FontProgram fontProgram = font.getFontProgram(); // 提取格式信息 String fontName = fontProgram.getFontNames().getFontName(); float fontSize = renderEvent.getFontSize(); boolean isBold = fontProgram.isBold(); boolean isItalic = fontProgram.isItalic(); // 创建带格式的文本块,绑定位置信息用于排序 FormattedTextChunk chunk = new FormattedTextChunk( renderEvent.getText(), fontName, fontSize, isBold, isItalic, getLocationStrategy().createLocation(renderEvent) ); formattedChunkList.add(chunk); } else { super.eventOccurred(data, type); } } // 获取排序后的带格式文本块列表 public List<FormattedTextChunk> getSortedFormattedChunks() { formattedChunkList.sort((c1, c2) -> c1.getLocation().compareTo(c2.getLocation())); return formattedChunkList; } // 自定义带格式文本块实体类 public static class FormattedTextChunk { private String text; private String fontName; private float fontSize; private boolean isBold; private boolean isItalic; private TextChunkLocation location; // 构造方法、getter/setter省略 } }
3. 生成最终的有序带格式文本
通过扩展后的策略处理PDF页面,获取排序后的带格式文本块,再按需拼接成结构化输出:
// 初始化PDF文档 PdfDocument pdfDoc = new PdfDocument(new PdfReader("target.pdf")); for (int pageNum = 1; pageNum <= pdfDoc.getNumberOfPages(); pageNum++) { PdfPage page = pdfDoc.getPage(pageNum); FormattedVisualTextStrategy strategy = new FormattedVisualTextStrategy(visualLocationStrategy); PdfCanvasProcessor processor = new PdfCanvasProcessor(strategy); processor.processPageContent(page); // 获取排序后的带格式文本块 List<FormattedVisualTextStrategy.FormattedTextChunk> sortedChunks = strategy.getSortedFormattedChunks(); // 示例:生成Markdown格式的输出 StringBuilder mdOutput = new StringBuilder(); for (FormattedVisualTextStrategy.FormattedTextChunk chunk : sortedChunks) { String styledText = chunk.getText(); if (chunk.isBold()) styledText = "**" + styledText + "**"; if (chunk.isItalic()) styledText = "*" + styledText + "*"; mdOutput.append(styledText); } System.out.println("第" + pageNum + "页输出:\n" + mdOutput); } pdfDoc.close();
关键注意事项
- 字体样式检测:优先使用
FontProgram.isBold()/isItalic()判断样式,比解析字体名更可靠,避免因字体名不规范导致的误判。 - 文本块合并:同一行、同格式的相邻文本块可合并,减少细碎的格式标记,提升输出可读性。
- 复杂布局适配:针对多列、浮动元素的PDF,需调整
TextChunkLocationStrategy的排序逻辑,确保列内顺序正确,列间按视觉流向排序。
内容的提问来源于stack exchange,提问作者Ehsan Sharifi Esfahani
相关产品推荐
相关产品推荐

