需求:用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
下载地址:进官网一看便知(注意版本)。
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)一开始使用的并不是PDF2.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。改为2.0版本,果然就好了。需要提醒的是,2.0版本PDDocument.load()方法参数为File类型,不再是String类型。可以参阅官方API文档。