详细内容见:https://iowiki.com/pdfbox/pdfbox_index.html
目录
PDFBox - 概述:
什么是PDFBox :
PDFBox的组件:
PDFBox的基本操作
1、创建一个空的pdf文件
2、给pdf文件添加一页
3、加载一个文件来创建一个pdf
4、删除一页
5、使用PDDocumentInformation 如何向PDF文档添加“ Author, Title, Date, and Subject等属性。
6、使用指定的字体和位置,在文件中写入指定的内容(一行内容)
6.1 中文的写入
7、添加多行
8、从pdf文档中提取全部文本
9、插入图像
10、使用StandardProtectionPolicy和AccessPermission classes提供的方法加密PDF文档。
11、PDF文档的拆分:Splitter的类将给定的PDF文档拆分为多个PDF文档,有几页就分成几个文档
12、使用PDFMergerUtility类的类将多个PDF文档合并到一个PDF文档中
13、将pdf转换成图片
14、pdf中添加矩形
15、添加矩形的基础上高亮标注(高亮的部分要是透明的,不能遮挡原有的文字)
每个PDF文件都包含固定布局平面文档的描述,包括现实和它所包含的文本、字体、图形和其他信息。
有几个库可用于程序创建和操作PDF文档,例如:
Adobe PDF Library - 该库提供C ++,.NET和Java等语言的API,使用它可以编辑,查看打印和从PDF文档中提取文本。
Formatting Objects Processor - 由XSL格式化对象和输出独立格式化程序驱动的开源打印格式化程序。 主要输出目标是PDF。
iText - 该库提供Java,C#和其他.NET语言等语言的API,使用该库我们可以创建和操作PDF,RTF和HTML文档。
JasperReports - 这是一个Java报告工具,可以生成PDF文档的报告,包括Microsoft Excel,RTF,ODT,逗号分隔值和XML文件。
Apache PDFBox是一个开源的Java库,支持PDF文档的开发和转换。使用此库,可以开发用于创建、转换和操作pDF文档的java程序。除此之外,PDFBox还包括一个命令行使用程序,用于使用可用的Jar文件对PDF执行各种操作。
以下是PDFBox的四个主要组成部分 -
PDFBox - 这是PDFBox的主要部分。 它包含与内容提取和操作相关的类和接口。
FontBox - 它包含与font相关的类和接口,使用这些类我们可以修改PDF文档的文本字体。
XmpBox - 包含处理XMP元数据的类和接口。
Preflight - 此组件用于根据PDF/A-1b标准验证PDF文件。
PDFBox环境,maven项目中需要添加的依赖:
org.apache.pdfbox
pdfbox
2.0.1
org.apache.pdfbox
fontbox
2.0.0
org.apache.pdfbox
jempbox
1.8.11
org.apache.pdfbox
xmpbox
2.0.0
org.apache.pdfbox
preflight
2.0.0
org.apache.pdfbox
pdfbox-tools
2.0.0
public class Document_Creation {
public static void main(String[] args) {
PDDocument document = new PDDocument();
try {
document.save("./my_doc.pdf");
System.out.println("PDF created");
document.close();
// 这时候直接打开创建的文件会发现打不开
} catch (IOException e) {
e.printStackTrace();
}
}
}
这时候直接打开创建的文件会发现打不开,打开时报错:
public static void main(String[] args) {
PDDocument document = new PDDocument();
try {
System.out.println("PDF created");
PDPage blankPage = new PDPage();
document.addPage(blankPage);
document.save("./my_doc.pdf");
document.close();
} catch (IOException e) {
e.printStackTrace();
}
}
这个时候创建的文档就可以打开了。注意save语句要写在addPage的后面
public static void main(String[] args) {
// File file = new File("/Users/qinwenjing/Documents/Projects/Test/src/main/java/com/pdfbox/Document_Creation"
// + ".java");
// File file = new File("/Users/qinwenjing/Documents/en_test.pdf");
File file = new File("/Users/qinwenjing/Documents/en_test_scan.pdf");
try {
PDDocument pdDocument = PDDocument.load(file);
System.out.println("PDF loaded");
pdDocument.addPage(new PDPage());
pdDocument.save("./sample.pdf");
pdDocument.close();
} catch (IOException e) {
e.printStackTrace();
}
}
注意:
1)加载的pdf文件是原文件或者扫描件都可以,写的结果和原文一样
2)重新执行这个程序的时候,指定的sample.pdf文件的内容会被覆盖
3)加载的文件必须是个pdf, 如果不是报错:java.io.IOException: Error: End-of-File, expected line
or java.io.IOException: Error: Header doesn't contain versioninfo 等
public static void main(String[] args) {
File file = new File("./sample.pdf");
try {
PDDocument document = PDDocument.load(file);
System.out.println(document.getNumberOfPages());
document.removePage(1);
document.save("./sample.pdf");
System.out.println(document.getNumberOfPages());
document.close();
} catch (IOException e) {
e.printStackTrace();
}
}
注意:
1)删除并并保存后在打印文件的页数发现已经少了一
2)设置删除的页为1, 实际上删除的是第2页,这个值是从0开始
3)删除不存在的页,会报错:java.lang.IndexOutOfBoundsException: Index out of bounds: 6
public class AddingDocumentAttributes {
public static void main(String[] args) {
PDDocument document = new PDDocument();
document.addPage(new PDPage());
PDDocumentInformation pdd = document.getDocumentInformation();
pdd.setAuthor("qinwenjing");
pdd.setTitle("sample document");
pdd.setCreator("pdf examples");
pdd.setSubject("example document");
Calendar date = new GregorianCalendar();
date.set(2020, 4, 8);
pdd.setCreationDate(date);
pdd.setKeywords("sample, first example, my pdf");
try {
document.save("./doc_attributes.pdf");
document.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
找到对应的文件,右键->显示简介, 看到如下信息:
public class AddingContent {
public static void main(String[] args) {
File file = new File("./doc_attributes.pdf");
try {
PDDocument document = PDDocument.load(file);
PDPage page = document.getPage(0);
PDPageContentStream contentStream = new PDPageContentStream(document, page);
contentStream.beginText();
contentStream.setFont(PDType1Font.TIMES_ROMAN, 12);
// 设置这一行起点的位置,25是距离左侧的长度,600是距离页面底端的长度
contentStream.newLineAtOffset(25, 600);
String text = "This is the sample document and we are adding content to it.";
contentStream.showText(text);
contentStream.endText();
// String text2 = "This is the sample document and we are adding content to it2222.";
// contentStream.showText(text2);
System.out.println("Content added");
contentStream.close();
document.save("./doc_attributes.pdf");
document.close();
} catch (IOException e) {
e.printStackTrace();
}
}
注意:
1) 如果换一个写入的位置,重新执行一遍,原先的文件内容会被完全覆盖掉
2)此程序,您只能添加适合单行的文本。 如果您尝试添加更多内容,则不会显示超出行间距的所有文本(超出一行的内容不会被显示)。
如果添加的内容text超出了一行的长度,则超出的部分不会被显示
如果多次调用showText()方法,会发现所有的文本内容都会被添加在一行(即不会自动换行),而且超出一行长度的部分不会被显示
3)这里默认的字体是PDType1Font.TIMES_ROMAN, 但是PDType1Font是对英文的字体才使用,如果写入的内容是中文,则报错java.lang.IllegalArgumentException: U+6211 ('.notdef') is not available in this font's encoding: WinAnsiEncoding
那么如何写入中文呢?见6.1
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.font.PDType0Font;
import com.itextpdf.io.font.otf.OpenTypeScript;
public class WritePdfText {
public static void main(String[] args) {
File file = new File("./writetest.pdf");
try {
PDDocument document = PDDocument.load(file);
PDPageContentStream contentStream = new PDPageContentStream(document, document.getPage(0), PDPageContentStream.AppendMode.APPEND, false);
contentStream.beginText();
contentStream.newLineAtOffset(10, 200);
PDFont pdFont = getDefaultFont(document);
contentStream.setFont(pdFont, 12);
contentStream.showText("大家好");
contentStream.endText();
contentStream.close();
System.out.println("写入汉字");
document.save("./writetest.pdf");
document.close();
// 这时候直接打开创建的文件会发现打不开
} catch (IOException e) {
e.printStackTrace();
}
}
private static PDFont getDefaultFont(PDDocument doc) {
String path = "/unicode.ttf";
PDFont font = null;
try (InputStream input = OpenTypeScript.class.getResourceAsStream(path)) {
font = PDType0Font.load(doc, input);
} catch (IOException e) {
e.printStackTrace();
}
return font;
}
}
注意:
写中文用的字体是PDType0Font, 要中文字体得用外部的 ttf 文件来load 一个 type0的字体,这里的unicode.ttf文件是。。。(额,没有找到如何添加文件)。其中unicode.ttf文件放在对应的resource文件夹下。
public static void main(String[] args) {
File file = new File("./doc_attributes.pdf");
try {
PDDocument document = PDDocument.load(file);
PDPage page = document.getPage(0);
PDPageContentStream contentStream = new PDPageContentStream(document, page);
contentStream.beginText();
contentStream.setFont(PDType1Font.TIMES_ROMAN, 12);
// 设置文本前导
contentStream.setLeading(14.5f);
contentStream.newLineAtOffset(25, 725);
String text1 = "This is an example of adding text to a page in the pdf document. we can add as many lines";
String text2 = "as we want like this using the ShowText() method of the ContentStream class";
contentStream.showText(text1);
contentStream.newLine();
contentStream.showText(text2);
System.out.println("Content added");
contentStream.close();
document.save("./doc_attributes.pdf");
document.close();
} catch (IOException e) {
e.printStackTrace();
}
}
注意:
1) 如果不设置setLeading(), 发现两段内容在同一行重叠了
2)加了newLine();两段内容是分行的
public static void main(String[] args) {
File file = new File("./doc_attributes.pdf");
try {
PDDocument document = PDDocument.load(file);
PDFTextStripper pdfTextStripper = new PDFTextStripper();
String text = pdfTextStripper.getText(document);
System.out.println(text);
document.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
File file = new File("./new.pdf");
try {
PDDocument doc = PDDocument.load(file);
PDImageXObject pdImage = PDImageXObject.createFromFile("./00.png", doc);
PDPage page = doc.getPage(0);
PDPageContentStream contentStream = new PDPageContentStream(doc, page);
contentStream.drawImage(pdImage, 70, 250);
contentStream.close();
doc.save("./new.pdf");
doc.close();
} catch (IOException e) {
e.printStackTrace();
}
}
注意:
开始用的图片是 jpg的格式,报错javax.imageio.IIOException: Not a JPEG file: starts with 0x4d 0x4d
直接改成png就可以了
public static void main(String[] args) {
File file = new File("./new.pdf");
try {
PDDocument document = PDDocument.load(file);
AccessPermission ap = new AccessPermission();
StandardProtectionPolicy spp = new StandardProtectionPolicy("1234", "1234", ap);
spp.setEncryptionKeyLength(128);
spp.setPermissions(ap);
document.protect(spp);
System.out.println("Document encrypted");
document.save(file);
document.close();
} catch (IOException e) {
e.printStackTrace();
}
}
import java.util.ListIterator;
import org.apache.pdfbox.multipdf.Splitter;
import org.apache.pdfbox.pdmodel.PDDocument;
public static void main(String[] args) {
File file = new File("./sample.pdf");
try {
PDDocument document = PDDocument.load(file);
Splitter spitter = new Splitter();
List pages = spitter.split(document);
ListIterator iterator = pages.listIterator();
int i = 1;
while (iterator.hasNext()) {
PDDocument pd = iterator.next();
pd.save("./sample" + i++ + ".pdf");
pd.close();
}
System.out.println("Multiple PDF’s created");
document.close();
} catch (IOException e) {
e.printStackTrace();
}
}
import org.apache.pdfbox.io.MemoryUsageSetting;
import org.apache.pdfbox.multipdf.PDFMergerUtility;
import org.apache.pdfbox.pdmodel.PDDocument;
public class MergePDFs {
public static void main(String[] args) {
File file1 = new File("./sample1.pdf");
File file2 = new File("./sample2.pdf");
File file3 = new File("./sample3.pdf");
try {
PDDocument doc1 = PDDocument.load(file1);
PDDocument doc2 = PDDocument.load(file2);
PDDocument doc3 = PDDocument.load(file3);
PDFMergerUtility pdfMergerUtility = new PDFMergerUtility();
pdfMergerUtility.setDestinationFileName("./mergesample.pdf");
pdfMergerUtility.addSource(file1);
pdfMergerUtility.addSource(file2);
//pdfMergerUtility.addSource(file3);
pdfMergerUtility.mergeDocuments(MemoryUsageSetting.setupMainMemoryOnly());
System.out.println("Documents merged");
doc1.close();
doc2.close();
doc3.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
注意:
1) file3是包含一个空页的文件,如果使用了pdfMergerUtility.addSource(file3);,则会报错:IllegalArgumentException: resourceDictionary is null
2)Pdfbox Merge Document with 1.8.xx as like mergePdf.mergeDocuments() it working fine .now pdfbox version 2.0.0 contain some argument like org.apache.pdfbox.multipdf.PDFMergerUtility.mergeDocuments(MemoryUsageSetting arg0)
那么如何设置参数呢?可以设置为null或者MemoryUsageSetting.setupMainMemoryOnly()
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer;
public class PdfToImage {
public static void main(String[] args) {
File file = new File("./sample.pdf");
try {
PDDocument document = PDDocument.load(file);
// 名为PDFRenderer的类将PDF文档呈现为AWT BufferedImage 。
PDFRenderer renderer = new PDFRenderer(document);
BufferedImage image = renderer.renderImage(0);
ImageIO.write(image, "JPEG",new File("./immm.jpg"));
System.out.println("Image created");
//Closing the document
document.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
import java.awt.Color;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
public class ShowColorBoxes {
public static void main(String[] args) {
File file = new File("./sample.pdf");
try {
PDDocument document = PDDocument.load(file);
PDPage page = document.getPage(0);
PDPageContentStream contentStream = new PDPageContentStream(document, page);
contentStream.setNonStrokingColor(Color.DARK_GRAY);
contentStream.addRect(200, 650, 100, 100);
contentStream.fill();
System.out.println("rectangle added");
//Closing the ContentStream object
contentStream.close();
document.save(file);
} catch (IOException e) {
e.printStackTrace();
}
}
}
注意:
1)像前面写文件一样,这个地方添加了个矩形,会把当前页面的内容也会给覆盖掉,执行完成后,发现第一页只有个矩形,原来的内容都没有了。
2)注意PDPageContentStream contentStream = new PDPageContentStream(document, page);
默认的是这个方式:this(document, sourcePage, AppendMode.OVERWRITE, true, false); 可以指定AppendMode的值
/**
* Create a new PDPage content stream.
*
* @param document The document the page is part of.
* @param sourcePage The page to write the contents to.
* @param appendContent Indicates whether content will be overwritten, appended or prepended.
* @param compress Tell if the content stream should compress the page contents.
* @param resetContext Tell if the graphic context should be reset. This is only relevant when
* the appendContent parameter is set to {@link AppendMode#APPEND}. You should use this when
* appending to an existing stream, because the existing stream may have changed graphic
* properties (e.g. scaling, rotation).
* @throws IOException If there is an error writing to the page contents.
*/
public PDPageContentStream(PDDocument document, PDPage sourcePage, AppendMode appendContent,
boolean compress, boolean resetContext) throws IOException
{
。。。。
}
import java.awt.Color;
import java.io.File;
import java.io.IOException;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.graphics.state.PDExtendedGraphicsState;
public class ShowColorBoxes {
public static void main(String[] args) {
File file = new File("./en_test.pdf");
try {
PDDocument document = PDDocument.load(file);
PDPage page = document.getPage(0);
PDPageContentStream contentStream = new PDPageContentStream(document, page,
PDPageContentStream.AppendMode.APPEND, true, true);
PDExtendedGraphicsState graphicsState = new PDExtendedGraphicsState();
// 设置透明度
graphicsState.setNonStrokingAlphaConstant(0.2f);
graphicsState.setAlphaSourceFlag(true);
contentStream.setGraphicsStateParameters(graphicsState);
contentStream.addRect(200, 650, 10, 10);
// 设置不划线颜色
contentStream.setNonStrokingColor(Color.GREEN);
contentStream.fill();
System.out.println("rectangle added");
//Closing the ContentStream object
contentStream.close();
document.save(file);
} catch (IOException e) {
e.printStackTrace();
}
}
}
注意:
1)new PDPageContentStream的倒数第二个参数如果是false,则无法进行透明的高亮标注
2)利用PDExtendedGraphicsState进行高亮标注
import java.io.File;
import java.io.IOException;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
public class WriteLinePdfText {
public static void main(String[] args) {
File file = new File("./writetest.pdf");
try {
PDDocument document = PDDocument.load(file);
PDPageContentStream contentStream = new PDPageContentStream(document, document.getPage(0));
contentStream.setStrokingColor(66, 177, 230);
contentStream.moveTo(10, 20);
contentStream.lineTo(200, 200);
// 使用该方法画出线
contentStream.stroke();
/* contentStream.setStrokingColor(66, 177, 230);
contentStream.drawLine(100, 100, 200, 100);*/
contentStream.close();
document.save(file);
} catch (IOException e) {
e.printStackTrace();
}
}
}
注意:
1)没有contentStream.stroke();和document.save(file);是保存不了画的线的。
2)/**/中的方式也可以实现画线,不过属于过时的方法。