Creating PDF Files in Java – 在Java中创建PDF文件

最后修改: 2017年 1月 13日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.引言

In this quick tutorial, we’ll focus on creating PDF documents from scratch based on the iText and PdfBox libraries.

在这个快速教程中,我们将专注于在iText和PdfBox库的基础上从头创建PDF文档。

2. Maven Dependencies

2.Maven的依赖性

First, we need to include the following Maven dependencies in our project:

首先,我们需要在项目中包含以下Maven依赖项。

<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itextpdf</artifactId>
    <version>5.5.10</version>
</dependency>
<dependency>
    <groupId>org.apache.pdfbox</groupId>
    <artifactId>pdfbox</artifactId>
    <version>2.0.4</version>
</dependency>

The latest version of the libraries can be found here: iText and PdfBox.

这些库的最新版本可以在这里找到。iTextPdfBox

It’s important to know that iText is available under the open-source AGPL license, as well as a commercial license. If we purchase a commercial license, we can keep our source code to ourselves, allowing us to retain our IP. If we use the AGPL version, we’ll need to release our source code free of charge. We can follow this link to determine how to make sure our software complies with AGPL.

重要的是要知道,iText可以在开源的AGPL许可证下使用,也可以购买商业许可证。如果我们购买了商业许可证,我们可以将我们的源代码留给自己,使我们能够保留我们的知识产权。如果我们使用AGPL版本,我们就需要免费发布我们的源代码。我们可以按照这个链接来确定如何确保我们的软件符合AGPL的规定。

We’ll also need to add one extra dependency in case we need to encrypt our file. The Bouncy Castle Provider package contains implementations of cryptographic algorithms, and is required by both libraries:

我们还需要添加一个额外的依赖,以防我们需要对文件进行加密。Bouncy Castle Provider包包含加密算法的实现,是两个库都需要的。

<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15on</artifactId>
    <version>1.56</version>
</dependency>

The latest version of the library can be found here: The Bouncy Castle Provider.

该库的最新版本可以在这里找到。充气城堡供应商

3. Overview

3.概述

iText and PdfBox are both Java libraries that we use for the creation and manipulation of pdf files. Although the final output of the libraries is the same, they operate in a different manner. Let’s take a closer look at each of them.

iText和PdfBox都是Java库,我们用它们来创建和操作pdf文件。虽然这两个库的最终输出结果是一样的,但它们的操作方式是不同的。让我们来仔细看看它们各自的情况。

4. Create Pdf in IText

4.在IText中创建Pdf

4.1. Insert Text in Pdf

4.1.在pdf中插入文本

Let’s look at how we insert a new file with “Hello World” text into a pdf file:

让我们来看看我们如何在一个pdf文件中插入一个带有 “Hello World “文字的新文件。

Document document = new Document();
PdfWriter.getInstance(document, new FileOutputStream("iTextHelloWorld.pdf"));

document.open();
Font font = FontFactory.getFont(FontFactory.COURIER, 16, BaseColor.BLACK);
Chunk chunk = new Chunk("Hello World", font);

document.add(chunk);
document.close();

Creating a pdf with the use of the iText library is based on manipulating objects implementing the Elements interface in Document (in version 5.5.10 there are 45 of those implementations).

使用iText库创建pdf是基于操作Document中实现Elements接口的对象(在5.5.10版本中,有45种实现)。

The smallest element we can add to the document and use is Chunk, which is basically a string with the applied font.

我们可以添加到文档中并使用的最小的元素是Chunk,它基本上是一个带有应用字体的字符串。

Additionally, we can combine Chunks with other elements, like Paragraphs, Section, etc., resulting in nice looking documents.

此外,我们可以将Chunks与其他元素相结合,如ParagraphsSection,等,从而形成漂亮的文件。

4.2. Inserting Image

4.2.插入图像

The iText library provides an easy way to add an image to the document. We simply need to create an Image instance and add it to the Document:

iText库提供了一种简单的方法来将图像添加到文档中。我们只需要创建一个图像实例并将其添加到文档中:

Path path = Paths.get(ClassLoader.getSystemResource("Java_logo.png").toURI());

Document document = new Document();
PdfWriter.getInstance(document, new FileOutputStream("iTextImageExample.pdf"));
document.open();
Image img = Image.getInstance(path.toAbsolutePath().toString());
document.add(img);

document.close();

4.3. Inserting Table

4.3.插入表格

We may face a problem if we want to add a table to our pdf. Luckily, iText provides this functionality out-of-the-box. 

如果我们想在我们的pdf中添加一个表格,我们可能会面临一个问题。幸运的是,iText提供了这个开箱即用的功能。

First, we need to create a PdfTable object and provide a number of columns for our table in the constructor.

首先,我们需要创建一个PdfTable对象,并在构造函数中为我们的表提供一些列。

Then we can simply add new cells by calling the addCell method on the newly created table object. iText will create table rows as long as all the necessary cells are defined. This means that once we create a table with three columns, and add eight cells to it, only two rows with three cells in each will be displayed.

然后,我们可以通过在新创建的表格对象上调用addCell方法简单地添加新的单元格。只要所有必要的单元格都被定义,iText就会创建表格行。这意味着,一旦我们创建了一个有三列的表格,并向其添加了八个单元格,那么将只显示两行,每行三个单元格。

Let’s look at the example:

让我们看一下这个例子。

Document document = new Document();
PdfWriter.getInstance(document, new FileOutputStream("iTextTable.pdf"));

document.open();

PdfPTable table = new PdfPTable(3);
addTableHeader(table);
addRows(table);
addCustomRows(table);

document.add(table);
document.close();

Now we’ll create a new table with three columns and three rows. We’ll treat the first row as a table header with a changed background color and border width:

现在我们将创建一个有三列三行的新表。我们将把第一行作为表头,改变背景颜色和边框宽度。

private void addTableHeader(PdfPTable table) {
    Stream.of("column header 1", "column header 2", "column header 3")
      .forEach(columnTitle -> {
        PdfPCell header = new PdfPCell();
        header.setBackgroundColor(BaseColor.LIGHT_GRAY);
        header.setBorderWidth(2);
        header.setPhrase(new Phrase(columnTitle));
        table.addCell(header);
    });
}

The second row will consist of three cells with just text, and no extra formatting:

第二行将由三个只有文本的单元格组成,没有额外的格式化。

private void addRows(PdfPTable table) {
    table.addCell("row 1, col 1");
    table.addCell("row 1, col 2");
    table.addCell("row 1, col 3");
}

We can also include images in cells. Furthermore, we can format each cell individually.

我们还可以在单元格中包含图片。此外,我们可以对每个单元格进行单独格式化。

In this example, we’re applying horizontal and vertical alignment adjustments:

在这个例子中,我们正在应用水平和垂直对齐调整。

private void addCustomRows(PdfPTable table) 
  throws URISyntaxException, BadElementException, IOException {
    Path path = Paths.get(ClassLoader.getSystemResource("Java_logo.png").toURI());
    Image img = Image.getInstance(path.toAbsolutePath().toString());
    img.scalePercent(10);

    PdfPCell imageCell = new PdfPCell(img);
    table.addCell(imageCell);

    PdfPCell horizontalAlignCell = new PdfPCell(new Phrase("row 2, col 2"));
    horizontalAlignCell.setHorizontalAlignment(Element.ALIGN_CENTER);
    table.addCell(horizontalAlignCell);

    PdfPCell verticalAlignCell = new PdfPCell(new Phrase("row 2, col 3"));
    verticalAlignCell.setVerticalAlignment(Element.ALIGN_BOTTOM);
    table.addCell(verticalAlignCell);
}

4.4. File Encryption

4.4.文件加密

In order to apply permissions using the iText library, we need to have already created the pdf document. In our example, we’ll use our previously generated iTextHelloWorld.pdf file.

为了使用iText库来应用权限,我们需要已经创建了pdf文档。在我们的例子中,我们将使用我们先前生成的iTextHelloWorld.pdf文件。

Once we load the file using PdfReader, we need to create a PdfStamper, which we’ll use to apply additional content to the file, like metadata, encryption, etc.:

一旦我们使用PdfReader加载文件,我们需要创建一个PdfStamper,,我们将用它来为文件应用额外的内容,如元数据、加密等等。

PdfReader pdfReader = new PdfReader("HelloWorld.pdf");
PdfStamper pdfStamper 
  = new PdfStamper(pdfReader, new FileOutputStream("encryptedPdf.pdf"));

pdfStamper.setEncryption(
  "userpass".getBytes(),
  ".getBytes(),
  0,
  PdfWriter.ENCRYPTION_AES_256
);

pdfStamper.close();

In our example, we encrypted the file with two passwords: the user password (“userpass”), where a user has read-only rights with no possibility to print it, and the owner password (“ownerpass”), which is used as a master key to allow a person full access to the pdf.

在我们的例子中,我们用两个密码对文件进行了加密:用户密码(”userpass”),用户有只读的权利,没有打印的可能;所有者密码(”ownerpass”),作为一个主密钥,允许一个人完全访问pdf文件。

If we want to allow the user to print the pdf, then instead of 0 (third parameter of setEncryption), we can pass:

如果我们想让用户打印pdf,那么可以用0(setEncryption的第三个参数)来代替。

PdfWriter.ALLOW_PRINTING

Of course, we can mix different permissions as well, like:

当然,我们也可以混合不同的权限,比如。

PdfWriter.ALLOW_PRINTING | PdfWriter.ALLOW_COPY

Keep in mind that when using iText to set access permissions, we’re also creating a temporary pdf, which should be deleted. If we don’t delete it, it could be fully accessible to anyone.

请记住,当使用iText来设置访问权限时,我们也在创建一个临时的pdf,它应该被删除。如果我们不删除它,它可能被任何人完全访问。

5. Create Pdf in PdfBox

5 在PdfBox中创建Pdf

5.1. Insert Text in Pdf

5.1.在pdf中插入文本

In contrast to iText, the PdfBox library provides an API based on stream manipulation. There are no classes like Chunk/Paragraph, etc. The PDDocument class is an in-memory Pdf representation, where the user writes data by manipulating PDPageContentStream class.

iText相比,PdfBox库提供了一个基于流操作的API。没有像Chunk/Paragraph,等的类。PDDocument类是一个内存中的Pdf表示,用户通过操作PDPageContentStream类来写入数据。

Let’s take a look at the code example:

让我们来看看这个代码例子。

PDDocument document = new PDDocument();
PDPage page = new PDPage();
document.addPage(page);

PDPageContentStream contentStream = new PDPageContentStream(document, page);

contentStream.setFont(PDType1Font.COURIER, 12);
contentStream.beginText();
contentStream.showText("Hello World");
contentStream.endText();
contentStream.close();

document.save("pdfBoxHelloWorld.pdf");
document.close();

5.2. Inserting Image

5.2.插入图像

Inserting images is also straightforward.

插入图像也很简单。

We need to load a file and create a PDImageXObject, subsequently drawing it on the document (need to provide exact x, y coordinates):

我们需要加载一个文件并创建一个PDImageXObject,随后在文档上绘制它(需要提供准确的x、y坐标)。

PDDocument document = new PDDocument();
PDPage page = new PDPage();
document.addPage(page);

Path path = Paths.get(ClassLoader.getSystemResource("Java_logo.png").toURI());
PDPageContentStream contentStream = new PDPageContentStream(document, page);
PDImageXObject image 
  = PDImageXObject.createFromFile(path.toAbsolutePath().toString(), document);
contentStream.drawImage(image, 0, 0);
contentStream.close();

document.save("pdfBoxImage.pdf");
document.close();

5.3. Inserting a Table

5.3.插入一个表

Unfortunately, PdfBox doesn’t provide any out-of-the-box methods that allow us to create tables. What we can do in this situation is draw it manually, literally drawing each line until our drawing resembles our desired table.

不幸的是,PdfBox并没有提供任何开箱即用的方法来让我们创建表格。在这种情况下,我们能做的就是手动绘制,按字面意思绘制每一条线,直到我们的绘图类似于我们想要的表格。

5.4. File Encryption

5.4.文件加密

The PdfBox library provides the ability to encrypt and adjust file permissions for the user. Compared to iText, it doesn’t require us to use an already existing file, as we simply use PDDocument. Pdf file permissions are handled by the AccessPermission class, where we can set if a user will be able to modify, extract content, or print a file.

PdfBox库提供了为用户加密和调整文件权限的能力。iText相比,它不要求我们使用一个已经存在的文件,因为我们只需使用PDDocument。Pdf文件的权限由AccessPermission类处理,我们可以设置用户是否能够修改、提取内容或打印文件。

Subsequently, we create a StandardProtectionPolicy object, which adds password-based protection to the document. We can specify two types of passwords. The user password allows the user to open a file with the applied access permissions, and the owner password has no limitations to the file:

随后,我们创建一个StandardProtectionPolicy对象,为文档添加基于密码的保护。我们可以指定两种类型的密码。用户密码允许用户以应用的访问权限打开文件,而所有者密码对文件没有限制。

PDDocument document = new PDDocument();
PDPage page = new PDPage();
document.addPage(page);

AccessPermission accessPermission = new AccessPermission();
accessPermission.setCanPrint(false);
accessPermission.setCanModify(false);

StandardProtectionPolicy standardProtectionPolicy 
  = new StandardProtectionPolicy("ownerpass", "userpass", accessPermission);
document.protect(standardProtectionPolicy);
document.save("pdfBoxEncryption.pdf");
document.close();

Our example demonstrates that if the user provides the user password, the file can’t be modified or printed.

我们的例子表明,如果用户提供了用户密码,文件就不能被修改或打印。

6. Conclusions

6.结论

In this article, we learned how to create a pdf file in two popular Java libraries.

在这篇文章中,我们学习了如何在两个流行的Java库中创建一个pdf文件。

Full examples from this article can be found in the Maven-based project over on GitHub.

本文的完整示例可在GitHub上基于Maven的项目中找到