需求:用java分頁提取PDF文本。
PDFBox是一個很好的可以滿足上述需求的開源工具。
1.PDF文檔結構
要解析PDF文本,我們首先要了解PDF文件的結構。
關於PDF文檔,最重要的幾點:
一,PDF文檔內容比較復雜,比如有純文本(可以提取出其中的文字,可以用PDF軟件中的“復制”功能)、圖片(無法使用PDF軟件中的“復制”功能)、表單、視頻、音頻等,總之形式比較復雜;
二,PDF文件采用二進制流與純文字混合的編碼模式,並且沒有采用 Unicode 等標准字符編碼方式,其字符編碼采用 Adobe 公司內建的編碼表( CMap),這使得對 PDF 的處理更加困難;
三,PDF有自己的文件結構:文件頭,對象集合,交叉引用表,結尾(准確地說,這是PDF文檔的物理結構,還有邏輯結構,詳情可以點擊查看這篇博文)。
2.PDFBox是個什么玩意
- 授權協議: Apache
- 開發語言: Java
- 操作系統: 跨平台
- 官網:http://pdfbox.apache.org/
3.PDFBox能干啥
- 從PDF提取文本
- 合並PDF文檔
- PDF 文檔加密與解密
- 與Lucene搜索引擎的集成
- 填充PDF/XFDF表單數據
- 從文本文件創建PDF文檔
- 從PDF頁面創建圖片
- 打印PDF文檔
4.准備工作
再次聲明,本demo功能是提取PDF文本(中文文本驗證也通過)。
1) 下載好jar包(3個):
a.fontbox-2.0.0-RC2.jar
b.pdfbox-2.0.0-RC2.jar
c.pdfbox-app-2.0.0-RC2.jar
下載地址1:我的下載
下載地址2:官網下載
2) myeclipse或eclipse。
5.開始編程
新建一個項目,寫入下面的源碼:
1 package com.primeton.pdfbox; 2 3 import java.io.File; 4 import java.io.FileOutputStream; 5 import java.io.OutputStreamWriter; 6 import java.io.Writer; 7 8 import org.apache.pdfbox.pdmodel.PDDocument; 9 import org.apache.pdfbox.text.PDFTextStripper; 10 11 12 /** 13 * PDFBox解析PDF文本實現 14 * @author MrChen 15 * 16 */ 17 18 public class PDFReader { 19 /** 20 * @param args 21 */ 22 public static void main(String[] args) { 23 // TODO Auto-generated method stub 24 PDFReader pdfReader = new PDFReader(); 25 System.out.println("E:\\AndroidStudio.pdf"); 26 try { 27 // 取得E盤下的SpringGuide.pdf的內容 28 System.out.println("開始提取"); 29 File file = new File("E:\\AndroidStudio.pdf"); 30 System.out.println("文件絕對路徑為:"+file.getAbsolutePath()); 31 pdfReader.readFdf(file); 32 System.out.println("提取結束"); 33 } catch (Exception e) { 34 e.printStackTrace(); 35 } 36 } 37 38 public void readFdf(File pdfFile) throws Exception { 39 // 是否排序 40 boolean sort = false; 41 // 輸入文本文件名稱 42 String textFileName = null; 43 // 編碼方式 44 String encoding = "UTF-8"; 45 // 開始提取頁數 46 int startPage = 1; 47 // 結束提取頁數 48 int endPage = 3; 49 // 文件輸入流,生成文本文件 50 Writer output = null; 51 // 內存中存儲的PDF Document 52 PDDocument document = null; 53 54 File outputFile = null; 55 try { 56 57 // 從本地裝載文件 58 //注意參數已不是以前版本中的URL.而是File。 59 System.out.println("開始裝載文件"+pdfFile.getName()); 60 document = PDDocument.load(pdfFile); 61 if (pdfFile.getName().length() > 4) { 62 textFileName = pdfFile.getName().substring(0, pdfFile.getName().length() - 4) + ".txt"; 63 outputFile = new File(pdfFile.getParent(),textFileName); 64 System.out.println("新文件絕對路徑為:"+outputFile.getAbsolutePath()); 65 66 67 } 68 System.out.println("裝載文件結束"); 69 70 71 System.out.println("開始寫到txt文件中"); 72 // 文件輸入流,寫入文件倒textFile 73 output = new OutputStreamWriter(new FileOutputStream(outputFile),encoding); 74 System.out.println("寫入txt文件結束"); 75 // PDFTextStripper來提取文本 76 PDFTextStripper stripper = null; 77 stripper = new PDFTextStripper(); 78 // 設置是否排序 79 stripper.setSortByPosition(sort); 80 // 設置起始頁 81 stripper.setStartPage(startPage); 82 // 設置結束頁 83 stripper.setEndPage(endPage); 84 // 調用PDFTextStripper的writeText提取並輸出文本 85 System.out.println("開始調用writeText方法"); 86 stripper.writeText(document, output); 87 System.out.println("調用writeText方法結束"); 88 }catch (Exception e) { 89 e.printStackTrace(); 90 }finally { 91 if (output != null) { 92 // 關閉輸出流 93 output.close(); 94 } 95 if (document != null) { 96 // 關閉PDF Document 97 document.close(); 98 } 99 } 100 } 101 }
有很多打樁的語句,可以自行去除。
同時,本程序也可以提取中文信息。
6.遇到的問題及解決方案
1)一開始使用的並不是PDFBox2.0版本,而是1.8版(2.0版本還是實驗版本,故選用了早期的1.8版本)。但選擇1.8版本,使用PDDocument.load(String)方法時,老是出現這個異常——“java.io.IOException: Push back buffer is full”。
解決方案:上述問題困擾了筆者很久。筆者為此重新復習了IO和NIO的一些知識,並查閱了PDFBox英文API文檔(1.8版本),均無解決思路。后大量查閱資料得知,這可能是1.8版本出現的bug,2.0版本修復了此bug。筆者將jar包改為2.0版本,果然就好了。需要提醒的是,2.0版本PDDocument.load()方法參數為File類型,不再是String類型。可以參閱官方API文檔。
