前言: 之前想学习使用Java操作pdf的时候看过了IText的文档。确实IText的文档很全,也有一个官网可以很方便的查找信息。但IText的开源协议为AGPL,使用者必须传染性的开源代码,商业使用必须付费获取商业许可。所以有一些风险。所以转而来学习使用PDFBOX。现在pdfbox的文档并不是很多,列出如下链接以做参考。 https://iowiki.com/pdfbox/pdfbox_quick_guide.html 如果需要源码进行研究,可以在https://www.apache.org/dyn/closer.lua/pdfbox/2.0.25/pdfbox-2.0.25-src.zip下载使用 有一些坑在网上的资料也比较少,也可以到https://issues.apache.org/jira/browse/PDFBOX-5103里查找相关的issue(英文),如果你找到了bug也可以往里提。
一.加载已有的pdf文件
1.加载全文/页的文本
注意:PDFBOX依赖commons-logging,fontbox包,使用请确保fontbox与pdfbox的版本相同,不然可能会有兼容BUG(亲测)
具体信息可以查看:https://mvnrepository.com/artifact/org.apache.pdfbox/pdfbox/2.0.25
先准备一个pdf文件(有内容的),如下:
通过加载PDF文件获取文本内容:
public class LoadPDF {
public static void main(String[] args) throws FileNotFoundException, IOException {
PDDocument doc = PDDocument.load(new FileInputStream("src/Target.pdf"));
PDFTextStripper text = new PDFTextStripper();
String FinalText = text.getText(doc);
System.out.println(FinalText);
doc.close();
}
}
运行:
邮政银行卡类型查询指引
方法一:拨打中国邮政储蓄银行电话“95580”查询。
方法二:携带本人身份证及银行卡至邮政银行网点查询。
方法三:手机网银查询,步骤如下图:
打开手机APP<邮储银行>,点击”我的“,点击"银行卡",查看薪资卡类型。
这样便很容易通过正则表达式进行内容的提取了。比如常见的一些招聘模块,通常有上传pdf简历的功能,这样大致也可以实现,只是方法有些不同。
2.加载保存pdf中的图片信息
当然,我们也可以从PDF文件中读取图片信息。
PDDocument doc = PDDocument.load(new FileInputStream("src/Target.pdf"));
PDPage pageOne = doc.getPage(0);
PDResources resources = pageOne.getResources();
Iterable<COSName> xObjectNames = resources.getXObjectNames();
xObjectNames.forEach(item->{
try {
PDXObject xObject = resources.getXObject(item);
System.out.println(item.getName());
if(xObject instanceof PDImageXObject) {
PDImageXObject imgobject = (PDImageXObject)xObject;
BufferedImage image = imgobject.getImage();
ImageIO.write(image, "png", new File("第"+Count+"张图片.png"));
System.out.println("ImageSaved");
Count++;
}
} catch (IOException e) {
System.out.println("图片保存出错");
e.printStackTrace();
}
});
运行:
二.创建普通的pdf文件
创建一个空的pdf非常的简单,只需要创建然后保存即可
PDDocument doc = new PDDocument();
doc.save(null);
如果要写入内容,需要使用PDFBOX提供的流对象进行写入。
写入英文
写入英文文本是不需要设置任何额外的东西,直接向流里面丢就可以了。
public static void main(String[] args) throws IOException {
PDDocument doc = new PDDocument();
PDPage pageOne = new PDPage(PDRectangle.A4);
doc.addPage(pageOne);
PDPageContentStream pageStream = new PDPageContentStream(doc, pageOne);
PDFont font = PDType1Font.COURIER_BOLD_OBLIQUE;
pageStream.setFont(font,18);
pageStream.beginText();
pageStream.showText("hello pdfbox");
pageStream.endText();
pageStream.close();
doc.save(new File("src\\hello.pdf"));
doc.close();
}
看一下效果 内容是写上去了,但感觉位置如果不指定的话是随机的,下一节应该学习一下如下指定文字及图片绘制的位置。
写入中文
中文的写入则更加复杂。 如果直接把上述代码的写入字符串变成中文,则会出现如下异常
pageStream.showText("想要写入一些中文,但是会报错");
异常的详细信息:
Exception in thread "main" java.lang.IllegalArgumentException: U+6BD4 ('.notdef') is not available in this font Courier-BoldOblique encoding: WinAnsiEncoding
at org.apache.pdfbox.pdmodel.font.PDType1Font.encode(PDType1Font.java:428)
at org.apache.pdfbox.pdmodel.font.PDFont.encode(PDFont.java:333)
at org.apache.pdfbox.pdmodel.PDAbstractContentStream.showTextInternal(PDAbstractContentStream.java:300)
at org.apache.pdfbox.pdmodel.PDAbstractContentStream.showText(PDAbstractContentStream.java:254)
at org.apache.pdfbox.pdmodel.PDPageContentStream.showText(PDPageContentStream.java:37)
at PDFBOX3.MainTest.main(MainTest.java:35)
这是因为在pdf的14种原生的字体中并不支持中文(在PDFType1Font中可以通过静态属性列出), 如果需要写入中文,可以通过嵌入中文字体文件(支持中文的.ttf文件)或者直接从本地导入。 所以请先自备中文字体。(或者可以直接去C盘里找,路径为C:/Windows/Fonts) 现在,先加载中文字体就好了。
private static final float MARGIN_LEFT = 0.8f * 72;
private static final float MARGIN_TOP = 0.4f * 72;
private static final float LOGO_WIDTH = 72;
private static final float LOGO_MARGIN = 18;
private static final float FONT_SIZE_TITLE = 24;
private static final float FONT_SIZE_SMALL = 10;
private static final float LINE_OFFSET_FACTOR = -1.8f;
public static void main(String[] args) throws IOException {
PDDocument document = new PDDocument();
PDPage page = new PDPage(PDRectangle.A4);
document.addPage(page);
@SuppressWarnings("resource")
PDPageContentStream contentStream = new PDPageContentStream(document, page);
PDRectangle boundingBox = page.getBBox();
contentStream.beginText();
contentStream.setFont(PDType0Font.load(document, MainTest.class.getResourceAsStream("SimHei.ttf")),
24);
contentStream.newLineAtOffset(MARGIN_LEFT + LOGO_WIDTH + LOGO_MARGIN,
boundingBox.getHeight() - MARGIN_TOP - FONT_SIZE_TITLE);
contentStream.showText("中国你好!");
contentStream.newLineAtOffset(0, LINE_OFFSET_FACTOR * FONT_SIZE_SMALL);
contentStream.showText("RiderKick");
contentStream.endText();
contentStream.close();
document.save(new File("Chinese.pdf"));
document.close();
}
写入图片
写入图片和写入文字一样,只需要将图片加载到PDImageXObject里,然后再从对象流中写入文档即可 以下图为例
public static void main(String[] args) throws IOException {
PDDocument doc = new PDDocument();
PDPage pageOne = new PDPage(PDRectangle.A4);
doc.addPage(pageOne);
PDPageContentStream pageStream = new PDPageContentStream(doc, pageOne);
PDFont font = PDType1Font.COURIER_BOLD_OBLIQUE;
PDImageXObject img = PDImageXObject.createFromFile("src/PDFBOX3/AutoFac.png", doc);
pageStream.setFont(font,18);
pageStream.beginText();
pageStream.showText("hi this is AutoFac");
pageStream.endText();
pageStream.drawImage(img, 50, 50);
pageStream.close();
doc.save(new File("src\\hello.pdf"));
doc.close();
}
|