文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Java实战:高效提取PDF文件指定坐标的文本内容

2023-10-12 11:11

关注

使用java获取PDF文档指定坐标的文本内容

前言

临时接到一个紧急需要处理的事项。业务侧一个同事有几千个PDF文件需要整理:需要从文件中的指定位置获取对应的编号和地址。
要的急,工作量大。所以就问到技术部有没有好的解决方案。
问技术的话就只能写个demo跑下了。

解决办法

1. 研究下PDF文档,找出解决方案

PDF的文档看起来比较简单,因为只是需要读取两个坐标位置的文本内容,而且位置相对固定。所以就直接用java的第三方库pdfbox来操作PDF文档。

2. 找个能操作PDF的第三方库pdfbox。

  1. 先下载pdfbox的jar包。
    官网介绍
  2. pdfbox能干啥:
    • pdfbox是Apache软件基金会的一个开源项目,它提供API和工具来处理PDF文档。

    • pdfbox是Apache PDFBox的Java版本,它提供了一个类库,用于读取,写入,转换和创建PDF文档。

    • pdfbox支持处理各种PDF特性,如文本,字体,图像,表单字段,注释,书签,页面布局等。

    • pdfbox还提供了对加密和数字签名PDF文档的支持,以及对PDF文档的提取和合并。

    • pdfbox还提供了对PDF文档的验证,签名验证,加密验证和数字签名的支持。

    • PDFBox是一个用于处理PDF文档的Java库。它提供了一组功能强大的API,可以用于创建、修改和提取PDF文档的内容。PDFBox可以用于各种用途,包括生成PDF文档、提取文本和图像、合并和拆分PDF文件、添加水印和书签等。

    • PDFBox支持处理各种PDF特性,如文本、字体、图像、表单字段、注释、书签、页面布局等。它还提供了对加密和数字签名PDF文档的支持,以及对PDF文档的高级操作,如提取文本位置信息、提取图像和字体等。

3. maven加载包

      pdfbox有三个大的版本,每个版本差异较大,这个时候如果要引入的时候,要注意对应的版本了,否则demo就有可能跑不起来。      ![pdfbox三个版本官方说明](https://img-blog.csdnimg.cn/3a822ec1571f4e088431d58704756781.png)      pdfbox最新的大版本是3.0。作为新时代的青年,肯定要与时俱进。3.0肯定是要用上的。

3. 先验证下第三方库是否可行

下载jar包后,直接用java代码跑下demo。 demo读取pdf文档内容并输出文本数据到控制台

    import org.apache.pdfbox.pdmodel.PDDocument;    import org.apache.pdfbox.text.PDFTextStripper;    import java.io.File;    import java.io.IOException;    public class PDFBoxDemo {        public static void main(String[] args) throws IOException {            PDDocument document = PDDocument.load(new File("D:\\pdf\\test.pdf"));            PDFTextStripper stripper = new PDFTextStripper();            String text = stripper.getText(document);            System.out.println(text);            document.close();        }    }

发现demo跑起来后,报错。
原因是因为demo是2.0的版本,而当前的jar包是3.0的版本。PDDocument.load这个修改为Loader.load就OK了。

接下来,就是如何获取到指定坐标位置的文本内容。

4. 确认文本在PDF文档中的坐标位置。

确认PDF文本坐标一般有两种方案。

1. 代码校验(最精准)

先用demo跑下,看下是否可以读取到指定坐标位置的文本内容。

    public static Point getPoint(File file,String sourceTex) {       Point point = new Point();       //获取文档坐标      try {        PDDocument document =  Loader.loadPDF(file);        PDFTextStripper textStripper = new PDFTextStripper() {            @Override            protected void writeString(String text, List<TextPosition> textPositions) throws IOException {                if (text.contains(targetText)) {                    TextPosition textPositionStart = textPositions.get(0);                    TextPosition textPositionEnd = textPositions.get(textPositions.size()-1);                    point.setX(textPositionStart.getX());                    point.setY(textPositionStart.getY());                 }            }        };        textStripper.setSortByPosition(true);        textStripper.setStartPage(1);        textStripper.setEndPage(document.getNumberOfPages());        textStripper.getText(document);        document.close();        } catch (IOException e) {            e.printStackTrace();        }        return point;    }

跑完demo后,发现可以读取到指定坐标位置的文本内容。
这里会有个小问题,就是返回的坐标点有的会有小数。因为当前返回类型float,所以需要转换成int。

2. 最直接粗暴的方法。

  1. 福昕PDF文档工具。  2. 直接用福昕PDF文档定位工具定位坐标。  说实话,开发比较少用这种方式,因为感觉有点lower(其实是自己不太会用)

5. 整个demo先验证第三方库是否可行。

拿1个文件试试水

 public static void main(String[] args) {        String filePath = "D:\\test\\test.pdf";         try {            PDDocument document = Loader.loadPDF(file);            PDFTextStripperByArea  textStripper = new PDFTextStripperByArea ();            Rectangle rectangle = new Rectangle(80,120, 250,10);            String regionName = "regionName";            textStripper.addRegion(regionName, rectangle);            PDPage page = document.getPage(0);            textStripper.extractRegions(page);            String text = textStripper.getTextForRegion(regionName);              System.out.println(text);                      textStripper.setSortByPosition(true);            textStripper.setStartPage(1);            textStripper.setEndPage(document.getNumberOfPages());            textStripper.getText(document);            document.close();        }catch (IOException e) {            e.printStackTrace();        }          }

结果能够正常输出对应的文本内容。

6. 整活上代码。

奉上全部demo代码

package com.example.demo;import cn.hutool.poi.excel.ExcelUtil;import cn.hutool.poi.excel.ExcelWriter;import com.alibaba.fastjson2.JSON;import org.apache.pdfbox.Loader;import org.apache.pdfbox.pdmodel.PDDocument;import org.apache.pdfbox.pdmodel.PDPage;import org.apache.pdfbox.text.PDFTextStripper;import org.apache.pdfbox.text.PDFTextStripperByArea;import org.apache.pdfbox.text.TextPosition;import org.springframework.boot.test.autoconfigure.data.cassandra.DataCassandraTest;import java.awt.*;import java.awt.geom.Rectangle2D;import java.io.File;import java.io.IOException;import java.util.*;import java.util.List;import java.util.stream.Collectors;public class PdfDemo {//要匹配的位置内容点    private  static final String[] target= {"name", "address"};    public static void main(String[] args) {       ExcelWriter excelWriter= ExcelUtil.getWriter("D:\\test\\pdf\\test.xls");       String folderPath = "D:\\test\\pdf";       File folder = new File(folderPath);       if (folder.exists() && folder.isDirectory()) {           List<Map<String,Object>>  mps =  listPdfFiles(folder);           excelWriter.write(mps, true);       } else {           System.out.println("Invalid folder path.");       }       excelWriter.close();    }    private static  List<Map<String,Object>>  listPdfFiles(File folder) {        List<Map<String,Object>> mps = new ArrayList<>();        File[] files = folder.listFiles();        if (files != null) {            for (File file : files) {                if (file.isDirectory()) {                    listPdfFiles(file); // 递归调用,处理子文件夹                } else {                    String fileName = file.getName();                    if (fileName.toLowerCase().endsWith(".pdf")) {                        mps.add(getLineData(file));                    }                }            }        }        return mps;    }        public static Map<String,Object> getLineData(File file){        Map<String,Object> lineData = new HashMap<>(target.length+2);        List<Point> pointList =  getPoint(file);        String[]  arr=  getPointValue(file, pointList.stream().map(s -> new Rectangle(s.getX(), s.getY(), 260, 10)).toArray(Rectangle[]::new));        if(arr.length>=target.length) {        for(int i=0;i<target.length;i++)        {            lineData.put(target[i], arr[i]);        }            lineData.put("fileName", file.getName().toLowerCase().replace(".pdf", ""));        }      return lineData;    }     public  static String[] getPointValue( File file,Rectangle... rectangles){        String[] textArr = new String[rectangles.length];       // String text="";        try {            PDDocument document = Loader.loadPDF(file);            PDFTextStripperByArea  textStripper = new PDFTextStripperByArea ();            for(int i = 0; i < rectangles.length;i++   ) {                Rectangle rectangle =rectangles[i];                String regionName = "regionName"+rectangle.getX()+rectangle.getY();                textStripper.addRegion(regionName, rectangle);                PDPage page = document.getPage(0);                textStripper.extractRegions(page);                // 获取区域的text                String text = textStripper.getTextForRegion(regionName);                text = text.replace("\u0000","-").replace(" ","");                System.out.println(">>text"+text);                textArr[i]=text;            }            textStripper.setSortByPosition(true);            textStripper.setStartPage(1);            textStripper.setEndPage(document.getNumberOfPages());            textStripper.getText(document);            document.close();        }catch (IOException e) {            e.printStackTrace();        }        return  textArr;    }    public  static List<Point> getPoint( File file){        List<Point> pointList=new ArrayList<>();        try {        PDDocument document =  Loader.loadPDF(file);        PDFTextStripper textStripper = new PDFTextStripper() {            @Override            protected void writeString(String text, List<TextPosition> textPositions) throws IOException {                for(String target:target){                    if (text.contains(target)) {                        Point point = new Point();                        TextPosition textPositionEnd = textPositions.get(textPositions.size() - 1);                        point.setX((int) textPositionEnd.getEndX());                        point.setY((int) textPositionEnd.getY());                        pointList.add(point);                    }                }            }        };        textStripper.setSortByPosition(true);        textStripper.setStartPage(1);        textStripper.setEndPage(document.getNumberOfPages());        textStripper.getText(document);        document.close();        } catch (IOException e) {            e.printStackTrace();        }        System.out.println(">>>>>pointList" + JSON.toJSONString(pointList));        return pointList;    }} 

7. 验证代码可行性

整理出来的excel,检查里面有些空格没有处理,就让业务自己批量替换一下。
因为代码只是一次性用的,就没有怎么进行封装了。总体来讲业务同事比较满意。

结论

  1. 第三方库pdfbox可以操作PDF文档。3.0版本之后和历史版本相差比较大,最好先阅读下源码。
  2. 坐标定位的话,可以用第三方也可以代码定位
  3. 如果代码后续想复用的话,最好抽离出公共方法
  4. 文件比较多的情况下,建议增加多线程处理。

来源地址:https://blog.csdn.net/m290345792/article/details/132190030

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯