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

使用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跑下,看下是否可以读取到指定坐标位置的文本内容。

 /*** 获取文档坐标* @param  file PDF文件对象* @param sourceTex 匹配的字符* @return 坐标*/public static Point getPoint(File file,String sourceTex) {Point point = new Point();//获取文档坐标try {PDDocument document =  Loader.loadPDF(file);PDFTextStripper textStripper = new PDFTextStripper() {@Overrideprotected 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;/*** Desc: 验证pdfbox的可行性** @author admin* @date since 2023/8/8 18:44*/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();}/*** 获取pdf文件列表** @param folder 文件夹* @return {@code List<Map<String,Object>>}*/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;}/*** 行数据** @param file 文件* @return {@code Map<String,Object>}*/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;}/*** 获得PDF指定坐标点文本值** @param file       文件* @param rectangles 矩形坐标* @return {@code String[]}*/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);// 获取区域的textString 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() {@Overrideprotected 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. 文件比较多的情况下,建议增加多线程处理。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/35502.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

案例15 Spring Boot入门案例

1. 选择Spring Initializr快速构建项目 ​ 2. 设置项目信息 ​ 3. 选择依赖 ​ 4. 设置项目名称 ​ 5. 项目结构 ​ 6. 项目依赖 自动配置了Spring MVC、内置了Tomcat、配置了Logback(日志)、配置了JSON。 ​ 7. 创建HelloController类 com.wfit.boot.hello目录下创建HelloCo…

开发过程中遇到的问题以及解决方法

巩固基础&#xff0c;砥砺前行 。 只有不断重复&#xff0c;才能做到超越自己。 能坚持把简单的事情做到极致&#xff0c;也是不容易的。 开发过程中遇到的问题以及解决方法 简单易用的git命令 git命令&#xff1a; 查看有几个分支&#xff1a;git branch -a 切换分支&#…

Azure创建第一个虚拟机

首先&#xff0c;登录到 Azure 门户 (https://portal.azure.com/)。在 Azure 门户右上角&#xff0c;点击“虚拟机”按钮&#xff0c;并点击创建&#xff0c;创建Azure虚拟机。 在虚拟机创建页面中&#xff0c;选择所需的基本配置&#xff0c;包括虚拟机名称、操作系统类型和版…

【JVM】JVM 调优的参数都有哪些?

文章目录 1. 设置堆空间大小2. 虚拟机栈的设置3. 年轻代中Eden区和两个Survivor区的大小比例4. 年轻代晋升老年代阈值5. 设置垃圾回收收集器 1. 设置堆空间大小 设置堆的初始大小和最大大小&#xff0c;为了防止垃圾收集器在初始大小、最大大小之间收缩堆而产生额外的时间&…

python编程小游戏简单的,python小游戏编程100例

大家好&#xff0c;给大家分享一下python编程小游戏简单的&#xff0c;很多人还不知道这一点。下面详细解释一下。现在让我们来看看&#xff01; 不会python就不能用python开发入门级的小游戏&#xff1f; 当然不是&#xff0c;我收集了十个python入门小游戏的源码和教程&#…

分支语句和循环语句(1)

这篇文章我们详细的把分支语句和循环语句给大家进行讲解。 分支语句&#xff1a; if switch 循环语句&#xff1a; while for do while goto语句&#xff1a; 1.什么是语句&#xff1f; C语句可分为以下五类&#xff1a; 1. 表达式语句 2. 函数调用语句 3. 控制…

ORCA优化器浅析——CDXLOperator Base class for operators in a DXL tree

如上图所示&#xff0c;CDXLOperator作为Base class for operators in a DXL tree&#xff0c;其子类CDXLLogical、CDXLScalar、CDXLPhysical作为逻辑节点、物理节点和Scalar节点的DXL表示类&#xff0c;因此其包含了这些类的共同部分特性&#xff0c;比如获取其DXL节点表示的函…

Qt 文件对话框使用 Deepin风格

当你在Deepin或UOS 上开发 Qt 程序时&#xff0c;如果涉及到文件对话框功能&#xff0c;那么就会遇到调用原生窗口的问题。 如果你使用的是官方的Qt版本&#xff0c;那么在Deepin或者UOS系统上&#xff0c;弹出的文件对话框会是如下这样&#xff1a; 而Deepin或UOS系统提供的默…

可视化高级绘图技巧100篇-总论

前言 优秀的数据可视化作品可以用三个关键词概括&#xff1a;准确、清晰、优雅。 准确&#xff1a;精准地反馈数据的特征信息&#xff08;既不遗漏也不冗余&#xff0c;不造成读者疏漏&误读细节&#xff09; 清晰&#xff1a;获取图表特征信息的时间越短越好 优雅&…

Gitlab CI/CD笔记-第二天-主机套接字进行构建并push镜像。

一、安装gitlab-runner 1.可以是linux也可以是docker的 2.本文说的是docker安装部署的。 二、直接上.gitlab-ci.yml stages: # List of stages for jobs, and their order of execution - build-image build-image-job: stage: build-image image: harbor.com:543/docke…

企业计算机服务器中了360后缀勒索病毒怎么办,勒索病毒解密数据恢复

随着计算机技术的不断发展&#xff0c;企业的办公系统得到了很大提升&#xff0c;但是随之而来的网络安全威胁也不断增加&#xff0c;勒索病毒的攻击事件时有发生。近期&#xff0c;我们收到某地连锁超市的求助&#xff0c;企业的计算机服务器遭到了360后缀勒索病毒攻击&#x…

html练习

html练习 工具代码运行结果 工具 HBuilder X 代码 <!DOCTYPE html> <html><head><meta charset"utf-8"><title>图灵之家</title></head><body><h1>图灵之家</h1><br><br><h2>我的…

使用几何和线性代数从单个图像进行 3D 重建

使用几何和线性代数从单个图像进行 3D 重建 萨蒂亚 一、说明 3D重构是一个挑战性题目&#xff0c;而且这个新颖的题目正处于启发和膨胀阶段&#xff1b;因此&#xff0c;各种各样的尝试层出不穷&#xff0c;本篇说明尝试的一种&#xff0c;至于其它更多的尝试&#xff0c;我们在…

RTT(RT-Thread)IO设备模型

目录 IO设备模型 模型框架原理 IO设备类型 创建和注册IO设备 RTT设备管理程序实现原理 访问IO设备 查找设备 初始化设备 打开设备 关闭设备 控制设备 读写设备 数据收发回调 数据接收回调 数据发送回调 设备模型实例 IO设备模型 RT-Thread 提供了一套简单的 I/O …

网络编程(TFTP协议实验)

#include <stdio.h> #include <string.h> #include <stdlib.h> #include <head.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h>#define PORT 69 //端口号&#xf…

网络安全威胁与防御策略

第一章&#xff1a;引言 随着数字化时代的快速发展&#xff0c;网络已经成为人们生活和工作中不可或缺的一部分。然而&#xff0c;网络的广泛应用也引发了一系列严峻的网络安全威胁。恶意软件、网络攻击、数据泄露等问题层出不穷&#xff0c;给个人和企业带来了巨大的风险。本文…

mysql基础之触发器的简单使用

1.建立学生信息表 -- 触发器 -- 建立学生信息表 create table s1(id int unsigned auto_increment,name varchar(30),score tinyint unsigned,dept varchar(50),primary key(id) );2.建立学生补考信息表 -- 建立学生补考信息表 create table s2 like s1;3.建立触发器&#xf…

java 自定义xss校验注解实现

自定义一个注解Xss。名字随意 import javax.validation.Constraint; import javax.validation.Payload; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Targe…

C字符串练习题(6.3.1)

编写一个程序&#xff0c;从键盘上读入一个小于1000的正整数&#xff0c;然后创建并输出一个字符串&#xff0c;说明该整数的值。例如&#xff0c;输入941&#xff0c;程序产生的字符串是“Nine hundred and forty one”。 #include<stdlib.h> #include<string.h>…

Maven基础总结

前言 Maven 是一个项目管理工具&#xff0c;可以对 Java 项目进行构建、依赖管理。 基本要求掌握 配置Maven环境直接查。 得会在IDEA创建Maven的java项目吧、会创建Maven的web项目吧、会创建多模块项目吧。 得会配置插件pligin、依赖dependency吧 一、Maven四大特性 1、…