使用iTextPDF的TextRegionEventFilter提取文本时出现单字符返回问题
iTextPDF解析LinkedIn简历PDF时的单字符提取问题及调试方案
我在开发LinkedIn生成PDF的简历解析器时遇到iTextPDF相关问题:
之前用SimpleTextExtractionStrategy可以完整提取文本并按行分割,顺序和PDF内一致,代码如下:
public String getNameAndSurname(PdfDocument pdfDoc) { SimpleTextExtractionStrategy strategy = new SimpleTextExtractionStrategy(); PdfCanvasProcessor proc = new PdfCanvasProcessor(strategy); proc.processPageContent(pdfDoc.getFirstPage()); System.out.println(strategy.getResultantText()); return strategy.getResultantText().split(System.lineSeparator())[0]; }
但为了按行拆分教育、经历等简历区块,我自定义了继承TextRegionEventFilter的过滤器,大部分功能正常,但部分场景下提取的内容是单个字符(已确认目标行无特殊符号),尝试拼接字符但找不到规律,过滤器代码如下:
@Override public boolean accept(IEventData data, EventType type) { if (type.equals(EventType.RENDER_TEXT)) { TextRenderInfo renderInfo = (TextRenderInfo) data; if ( setBlock(renderInfo, BLOCK_START, BLOCK_END) && isFontMatch(renderInfo, LINE_FONT_NAME, LINE_FONT_SIZE) ) { super.elements.add(renderInfo.getText()); return true; } } return false; }
我试过把页面尺寸从文档获取改成直接设为PageSize.A4,问题依旧。当前输出示例:
{ "jobRole": "T", "company": "Company 4", "timePeriod": "XXX 2017 - XXX 2017 (3 months)" }, { "jobRole": "r", "company": "Company 3", "timePeriod": "XXX 2016 - XXX 2016 (9 months)" }, { "jobRole": "a", "company": "Company2", "timePeriod": "XXX 2014 - XXX 2015 (10 months)" }, { "jobRole": "i", "company": "Company1", "timePeriod": "XXX 2014 - XXX 2014 (7 months)" }
问题分析与调试建议
这大概率不是iTextPDF的Bug,而是LinkedIn生成PDF时的文本渲染特性导致的:
- 检查文本渲染单元:LinkedIn的PDF可能会把同一行的文字拆成多个独立的
TextRenderInfo对象(比如每个字母单独渲染),你的过滤器每次只取单个renderInfo.getText(),自然会得到单字符。可以在accept方法里打印每个renderInfo的文本、位置、字体信息,确认是否存在这种拆分。 - 验证区域过滤逻辑:
setBlock方法可能误判了文本的区域范围,只捕获到了单个字符所在的区域。可以输出renderInfo.getBaseline().getBoundingRectangle()的坐标,对比预期的区块范围,看是否存在区域匹配偏差。 - 字体匹配逻辑排查:
isFontMatch方法的判断条件可能过于严格,比如字体名大小写、字号精度(比如实际是11.99而你判断12),导致只有单个字符的字体符合条件。可以打印renderInfo.getFont().getFontProgram().getFontNames().getFontName()和renderInfo.getFontSize(),核对匹配规则。 - 参考SimpleTextExtractionStrategy的拼接逻辑:该策略内部会根据文本的位置(比如基线是否连续、间距是否正常)来拼接字符,你可以参考它的源码,在过滤器里实现类似的字符拼接逻辑,而不是直接添加单个
renderInfo的文本。
内容的提问来源于stack exchange,提问作者PanBorewicz
相关产品推荐
相关产品推荐

