【追加版】itextpdf读取文本时上下行位置错乱

itextpdf + 相邻行内容错乱

背景: 之前博客中提到抽取PDF中表格数据的时候,spire.pdf无法读取表格外的内容。正常情况使用itextpdf,可以按纯文本形式读取所有内容,但是没有意外的意外又出现了,那就是那些肉眼看起来在一行的内容,使用itextpdf读取后发现,部分字段值要么跑到上面行去了,要么跑到下面行去了,有的却是正常的,这种不确定的现象肯定要解决的,不然线上问题处理不完。(说了那么多可能想想象不出来,也不能泄露数据,大家后面遇到再说)

博客内容精选:
1、Servlet请求体重复读&修改新姿势
2、根据请求获取后端接口详情
3、封装Springboot项目的starter-sdk新方式
4、Springboot全局处理完整版
5、itextpdf读取文本时上下行位置错乱
6、JAVA读取PDF表格内容
7、JAVA读取PDF出现内容错乱

根因:字段位置坐标存在微小差距,行坐标如果不一致,则itextpdf认为不是一行,但是这个差距极小,肉眼无法分辨(鼠标双击后可看到此现象)

解决办法有多种,以下将简述两种情况:
1、转图片OCR
这种方法直接,但识别效果依靠OCR的识别精确度,如果采用这种方式,还不如用OCR的表格识别功能,一次性解决了。如果需要商用级别的稳定度和准确度,基本都要付费使用腾讯或阿里的商用OCR了。

2、溯源定位找适配
既然知道了根据,那我们就看itextpdf读取pdf里面的内容,以及它是如何把这些内容封装后完整的展现出来的。具体代码流程此处忽略,下面直接贴出解决代码:

/**
 * 重写文本提取后的排序策略,其它逻辑保持不变
 */
private static final LocationTextExtractionStrategy.TextChunkLocationStrategy textChunkLocationStrategy = new LocationTextExtractionStrategy.TextChunkLocationStrategy() {
    @Override
    public LocationTextExtractionStrategy.TextChunkLocation createLocation(TextRenderInfo renderInfo, LineSegment baseline) {
        return new LocationTextExtractionStrategy.TextChunkLocationDefaultImp(baseline.getStartPoint(), baseline.getEndPoint(), renderInfo.getSingleSpaceWidth()) {
            @Override
            public int compareTo(LocationTextExtractionStrategy.TextChunkLocation other) {
                if (this == other) {
                    return 0;
                } else {
                    int rslt = Integer.compare(this.orientationMagnitude(), other.orientationMagnitude());
                    if (rslt != 0) {
                        return rslt;
                    } else {
                        // 行内容键值对相对应的纵坐标可能不一致,此处重写排序逻辑,消除肉眼内的坐标差值
                        rslt = Integer.compare(this.distPerpendicular(), other.distPerpendicular());
                        return rslt == 0 || Math.abs(this.distPerpendicular() - other.distPerpendicular()) == 1 ? Float.compare(this.distParallelStart(), other.distParallelStart()) : rslt;
                    }
                }
            }
        };
    }

};

处理入口:

import com.itextpdf.text.pdf.parser.LineSegment;
import com.itextpdf.text.pdf.parser.LocationTextExtractionStrategy;
import com.itextpdf.text.pdf.parser.PdfTextExtractor;
import com.itextpdf.text.pdf.parser.TextRenderInfo;

PdfReader pdfReader = new PdfReader("path/file");
for (int page = 1; page <= pdfReader.getNumberOfPages(); page++) {
	// 此处使用优化后的排序策略,即可解决坐标差异问题
	String text = PdfTextExtractor.getTextFromPage(pdfReader, page, new LocationTextExtractionStrategy(textChunkLocationStrategy));
	System.out.println(text);
}

总结:当初遇到这个问题也是一脸懵,itext也只是仅仅用过而已,没有完整了解其底层的原理。为了解决这个必须解决的问题,迫不得已静下心来逐步溯源分析,慢慢调试优化,在猜测与验证的过程中发现底层开放了扩展接口,进而在不影响效率及流程的情况下解决了此问题,在此记录供同行伙伴共勉。

  • 7
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值