本文介绍PDFBox的简单用法。PDFBox是apache旗下的用于parse pdf文件的开源库,我们可以用它来提取pdf中的文字和图片,也可以用它来生成pdf文件(比如我们想自动做报表)。
目录
最近想把北师大版本的《汪曾祺全集》用OCR转成文字,这样方便搜索。上网能搜到PDF文件,这些PDF其实是扫描的图片,我们需要使用OCR把图片变成文字。第一步当然是需要解析PDF然后把每一页都变成图片。十多年前使用过PDFBox,这次搜索了一下”java pdf parser”,这个apache的项目依然还在。
PDF格式非常复杂,不过我的需求比较简单,只需要把每一页变成图片就行了。但是这里也会介绍更多的用法,比如我们需要parse一篇论文(当年我的毕设啊),提取标题、作者、摘要和引文等等信息,那么我们就需要知道每一行文字(甚至每个字符)的位置、大小等信息。比如标题的字体很大,而且通常出现在第一页的上方。
依赖
我们只需要在maven的依赖里加入:
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId>
<version>2.0.13</version>
</dependency>
另外可能还会用到一些工具,比如ImageIOUtil,那么还需要加入下面的依赖:
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox-tools</artifactId>
<version>2.0.13</version>
</dependency>
把PDF转换成图片
完整代码在这里。
public class ConvertToImages {
public static void main(String[] args) throws InvalidPasswordException, IOException {
System.setProperty("sun.java2d.cmm", "sun.java2d.cmm.kcms.KcmsServiceProvider");
String path="/home/lili/下载/books/汪曾祺全集1.pdf";
String dir="/home/lili/data/wang/book1";
new File(dir).mkdirs();
PDDocument document = PDDocument.load(new File(path));
PDFRenderer pdfRenderer = new PDFRenderer(document);
for (int page = 0; page < document.getNumberOfPages(); ++page) {
BufferedImage bim = pdfRenderer.renderImageWithDPI(page, 300, ImageType.RGB);
ImageIOUtil.writeImage(bim, dir + "/" + (page + 1) + ".png", 300);
}
document.close();
}
}
代码非常简单,注意第一行是因为JDK的问题,参考这里。
首先用PDDocument.load来加载PDDocument,然后用这个PDDocument对象构造一个PDFRenderer对象。然后用pdfRenderer.renderImageWithDPI来把每一页变成图片,注意第二个参数是dpi,这里是300。对于很多ocr软件来说,至少需要300以上的dpi才能达到比较好的效果。
非常简单!目的已经达到。不过下面的某些功能读者可能也会感兴趣,我们继续学习。
抽取PDF的文本
完整代码在这里。
public class ExtractTextExample {
public static void main(String[] args) throws InvalidPasswordException, IOException {
try (PDDocument document = PDDocument.load(new File("/home/lili/data/test.pdf"))) {
if (!document.isEncrypted()) {
PDFTextStripper tStripper = new PDFTextStripper();
// 如果想抽取某一页或者某几页,可以使用下面的方法限定范围。
// 目前是抽取所有页
tStripper.setStartPage(0);
tStripper.setEndPage(document.getNumberOfPages());
String pdfFileInText = tStripper.getText(document);
String lines[] = pdfFileInText.split("\\r?\\n");
for (String line : lines) {
System.out.println(line);
}
}
}
}
}
这里注意用到PDFTextStripper,调用getText(document)方法就可以得到所有的文字,然后用回车和/或换行split就得到每一行的文字。如果想只抽取一个pdf的某些页,那么可以使用setStartPage()和setEndPage()限定范围。
获取每行(个)文字的位置和大小信息
完整代码在这里。
public class PrintTextLocations extends PDFTextStripper {
public PrintTextLocations() throws IOException {
}
public static void main(String[] args) throws IOException {
PDDocument document = null;
try {
document = PDDocument.load(new File("/home/lili/data/test.pdf"));
PDFTextStripper stripper = new PrintTextLocations();
stripper.setSortByPosition(true);
stripper.setStartPage(0);
stripper.setEndPage(document.getNumberOfPages());
Writer dummy = new OutputStreamWriter(new ByteArrayOutputStream());
stripper.writeText(document, dummy);
} finally {
if (document != null) {
document.close();
}
}
}
@Override
protected void writeString(String string, List<TextPosition> textPositions) throws IOException {
for (TextPosition text : textPositions) {
System.out.println("String[" + text.getXDirAdj() + "," + text.getYDirAdj()
+ " fs=" + text.getFontSize()
+ " xscale=" + text.getXScale()
+ " height=" + text.getHeightDir()
+ " space=" + text.getWidthOfSpace()
+ " width=" + text.getWidthDirAdj() + "]"
+ text.getUnicode());
}
}
}
我们需要继承PDFTextStripper然后重载PDFTextStripper方法,当调用PDFTextStripper.writeText()的时候每parse一行文字就会回调writeString方法,第一个参数string就是这一行文字,而第二个参数List<TextPosition> textPositions是这一行每一个字符的位置信息。
更多示例
更多示例可以参考1.8的cookbook,虽然是1.8的,但是大部分可以在2.x上运行。
另外在pdfbox的源代码目录树下有一个示例项目。
- 显示Disqus评论(需要科学上网)