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.pswp.cn/news/35502.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/35502.shtml
英文地址,請注明出處:http://en.pswp.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…

Less和Sass的原理和用法

一、原理 1.1 Less定義&#xff1a;是一種動態的樣式語言,使CSS變成一種動態的語言特性&#xff0c;如變量、繼承、運算、函數。Less既可以在客戶端上面運行(支持IE6以上版本、Webkit、Firefox),也可以在服務端運行(Node.js) 1.2 SaSS定義&#xff1a;是一種動態樣式語言&#…

開發過程中遇到的問題以及解決方法

鞏固基礎&#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. 控制…

qt自己實現方便的線程管理類

看本博客之前&#xff0c;可以先看看我這篇多線程博客&#xff1a;qt多線程使用方式_我是標同學的博客-CSDN博客

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…

C#四個字節十六進制與單精度浮點數互轉

C#四個字節十六進制與單精度浮點數互轉可以使用自帶的函數,也可以自己寫 實例如下: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;namespace floatDemo {class Program{//首先設置:項目->屬性…

html練習

html練習 工具代碼運行結果 工具 HBuilder X 代碼 <!DOCTYPE html> <html><head><meta charset"utf-8"><title>圖靈之家</title></head><body><h1>圖靈之家</h1><br><br><h2>我的…

Maxwell 數據同步使用教程

Maxwell 數據同步使用教程 Maxwell 是一個開源的 MySQL 數據同步工具&#xff0c;它可以提供可靠的、實時的數據復制服務。它的特點是將 MySQL 的 binlog 解析成易于理解、易于使用的 JSON 格式&#xff0c;并將其發送到 Kafka 或其他消息隊列&#xff0c;方便消費者進行數據處…

使用幾何和線性代數從單個圖像進行 3D 重建

使用幾何和線性代數從單個圖像進行 3D 重建 薩蒂亞 一、說明 3D重構是一個挑戰性題目&#xff0c;而且這個新穎的題目正處于啟發和膨脹階段&#xff1b;因此&#xff0c;各種各樣的嘗試層出不窮&#xff0c;本篇說明嘗試的一種&#xff0c;至于其它更多的嘗試&#xff0c;我們在…

nlohmann json:通過at讀取及設置object和array

讀取及設置: #include <iostream> #include <nlohmann/json.hpp> using namespace std; using json = nlohmann::json;int main() {json data = R"({"name": "xiaoming","age": 10, "parent": [{"father&qu…

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…