天天看点

java编辑PDF文件

概述

在本文中,我们将了解如何使用 Java 编辑PDF 文件的内容。 首先,我们将添加新内容。 然后,我们将专注于删除或替换一些预先存在的内容。

添加iText7依赖

我们将使用 iText7 库向 PDF 文件添加内容。 稍后,我们将使用 pdfSweep 插件来删除或替换内容。

请注意,iText 在 AGPL 下获得许可,这可能会限制商业应用程序的分发:iText 许可模型。

首先,让我们将这些依赖项添加到我们的 pom.xml 中:

<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itext7-core</artifactId>
    <version>7.2.3</version>
    <type>pom</type>
</dependency>
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>cleanup</artifactId>
    <version>3.0.1</version>
</dependency>           

文件处理

让我们了解使用 iText7 处理 PDF 的步骤:

首先,我们打开一个 PdfReader 来读取源文件的内容。 如果在读取文件时随时发生错误,这将引发 IOException。

然后,我们打开一个 PdfWriter 到目标文件。 如果此文件不存在或无法创建,则会引发 FileNotFoundException。

之后,我们将打开一个使用 PdfReader 和 PdfWriter 的 PdfDocument。

最后,关闭 PdfDocument 会同时关闭底层 PdfReader 和 PdfWriter。

让我们编写一个 main() 方法来运行我们的整个处理。 为了简单起见,我们将重新抛出任何可能发生的异常:

public static void main(String[] args) throws IOException {
    PdfReader reader = new PdfReader("src/main/resources/a.pdf");
    PdfWriter writer = new PdfWriter("src/main/resources/a-modified.pdf");
    PdfDocument pdfDocument = new PdfDocument(reader, writer);
    addContentToDocument(pdfDocument);
    pdfDocument.close();
}           

在下一节中,我们将逐步完成 addContentToDocument() 方法,以便用新内容填充我们的 PDF。 源文档是一个 PDF 文件,左上角仅包含文本“你好,小王子”。 目标文件将由程序创建。

向文件中添加内容

我们现在将向文件添加各种类型的内容。

  • 添加表单

我们将从向文件中添加一个表单开始。 我们的表单将非常简单,并包含一个名为 name 的唯一字段。

此外,我们需要告诉 iText 在哪里放置该字段。 在这种情况下,我们将把它放在以下点:(35,400)。 坐标 (0,0) 指的是文档的左下角。 最后,我们将字段的尺寸设置为 100×30:

private static void addContentToDocument(PdfDocument pdfDocument) throws IOException {
        //添加表单
        PdfFormField personal = PdfFormField.createEmptyField(pdfDocument);
        personal.setFieldName("information");
        PdfTextFormField name = PdfFormField.createText(pdfDocument,
                new Rectangle(35, 400, 100, 30),
                "name", "");
        personal.addKid(name);
        PdfAcroForm.getAcroForm(pdfDocument, true)
                .addField(personal, pdfDocument.getFirstPage());
   }           

此外,我们明确指定 iText 将表单添加到文档的第一页。

java编辑PDF文件
  • 添加新页面

现在让我们看看如何向文档添加新页面。 我们将使用 addNewPage() 方法。

如果我们想指定它,这个方法可以接受添加页面的索引。 例如,我们可以在文档的开头添加一个新页面:

pdfDocument.addNewPage(1);           

完整方法如下:

private static void addContentToDocument(PdfDocument pdfDocument) throws IOException {
    //添加表单
    PdfFormField personal = PdfFormField.createEmptyField(pdfDocument);
    personal.setFieldName("information");
    PdfTextFormField name = PdfFormField.createText(pdfDocument,
            new Rectangle(35, 400, 100, 30),
            "name", "");
    personal.addKid(name);
    PdfAcroForm.getAcroForm(pdfDocument, true)
            .addField(personal, pdfDocument.getFirstPage());
    //添加空白页
    pdfDocument.addNewPage(1);、
}           
java编辑PDF文件
  • 添加注释

我们现在要为文档添加注释。 具体来说,注释看起来像一个方形的漫画气泡。

我们将把它添加到现在位于文档第二页的表单之上。 因此,我们将其放置在坐标 (40,435) 上。 此外,我们会给它一个简单的名称和内容。 这些只会在将鼠标悬停在注释上时显示:

PdfAnnotation ann = new PdfTextAnnotation(
                new Rectangle(40, 435, 0, 0))
                .setTitle(new PdfString("name"))
                .setContents("Your name");
        pdfDocument.getPage(2).addAnnotation(ann);           

这是我们第二页中间现在的样子:

java编辑PDF文件
  • 添加图片

从现在开始,我们将向页面添加布局元素。 为了做到这一点,我们将无法再直接操作 PdfDocument。 我们宁愿从它创建一个文档并使用它。 此外,我们最终需要关闭文档。 关闭文档会自动关闭基础 PdfDocument,那外层的关闭document就需要注释。

Document document = new Document(pdfDocument);           

//注释外面的关闭文档

addContentToDocument(pdfDocument);
//pdfDocument.close();           

现在,要添加图片,我们需要从它的位置加载它。 我们将使用 ImageDataFactory 类的 create() 方法来执行此操作。 如果无法解析传递的文件 URL,则会引发 MalformedURLException。 在此示例中,我们将使用放置在资源目录中的 rejoice图片:

ImageData imageData = ImageDataFactory.create("src/main/resources/rejoice.png");           

下一步是在文件中设置图片的属性。 我们将其大小设置为 550×100。 我们将把它放在 PDF 的第一页,在 (10,50) 坐标处。 让我们看看添加图片的代码:

Image image = new Image(imageData).scaleAbsolute(550,100)
        .setFixedPosition(1, 10, 50);
document.add(image);
// 关闭文档
document.close();           

图像会自动重新缩放到给定的大小。 如下图所示:

java编辑PDF文件
  • 添加段落

iText 库带来了一些工具来将文本添加到文件中。 字体可以在片段本身上进行参数化,也可以直接在段落元素上进行参数化。

例如,让我们在第一页的顶部添加以下句子:this is a demo from the princess runs to the end。 我们将这句话开头的字体大小设置为 16,将段落的全局字体大小设置为 8:

注意:只能用英文,如果想要用中文,需要手动添加字体包,这里为了方便不做演示
//添加段落
        Text title = new Text("this is a demo").setFontSize(16);
        Text author = new Text("the princess runs to the end");
        Paragraph p = new Paragraph().setFontSize(8)
                .add(title)
                .add(" from ")
                .add(author);
        document.add(p);           
  • 添加表格

最后但并非最不重要的一点是,我们还可以在文件中添加一个表。 例如,我们将定义一个复式表,其中包含两个单元格和两个表头。 我们不会指定任何位置。 所以它会自然地添加到文档顶部,就在我们刚刚添加的段落之后:

Table table = new Table(UnitValue.createPercentArray(2));
table.addHeaderCell("name");
table.addHeaderCell("address");
table.addCell("rejoice");
table.addCell("China Company");
document.add(table);           

现在让我们看看文档第一页的开头:

java编辑PDF文件

从文件中删除内容

现在让我们看看如何从 PDF 文件中删除内容。 为简单起见,我们将编写另一个 test 方法。

我们的源 PDF 文件将是 a-modified.pdf 文件,目标将是一个新的 a-cleaned.pdf 文件。 我们将直接处理 PdfDocument 对象。 从现在开始,我们将使用 iText 的 pdfSweep 插件。

  • 从文件中擦除文本

要从文件中擦除给定的文本,我们需要定义一个清理策略。 在此示例中,策略将只是查找与 the 匹配的所有文本。 最后一步是调用 PdfCleaner 的 autoSweepCleanUp() 静态方法。 此方法将创建一个自定义 PdfCleanUpTool,如果在文件处理期间发生任何错误,它将引发 IOException:

@Test
    public void testDelContent() throws IOException {
        PdfReader reader = new PdfReader(
                "src/main/resources/a-modified.pdf");
        PdfWriter writer = new PdfWriter(
                "src/main/resources/a-cleaned.pdf");
        PdfDocument pdfDocument = new PdfDocument(reader, writer);
        CompositeCleanupStrategy strategy = new CompositeCleanupStrategy();
        strategy.add(new RegexBasedCleanupStrategy("the"));
        PdfCleaner.autoSweepCleanUp(pdfDocument, strategy);
        pdfDocument.close();
    }           

如我们所见,源文件中出现的 the 词在结果文件中被黑色矩形覆盖。 例如,此功能适用于数据脱敏:

java编辑PDF文件
  • 从文件中擦除其他内容

不幸的是,很难检测到文件中的任何非文本内容。 但是,pdfSweep 提供了擦除文件一部分内容的可能性。 因此,如果我们知道要擦除的内容的位置,我们就可以利用这种可能性。

例如,我们将擦除位于第二页 (35,400) 的大小为 100×35 的矩形的内容。 这意味着我们将摆脱表单和注释的所有内容。 下面是执行所有这些操作的代码:

//擦除非文本内容
        List<PdfCleanUpLocation> cleanUpLocations =
                Arrays.asList(
                        new PdfCleanUpLocation(2, new Rectangle(35, 400, 100, 35)));
        PdfCleanUpTool cleaner = new PdfCleanUpTool(pdfDocument,
                cleanUpLocations, new CleanUpProperties());
        cleaner.cleanUp();           

如下图所示,第二页中的表单被删除了

java编辑PDF文件

替换文件中的内容

在本节中,我们将做与前面相同的工作,只是我们将用新文本替换以前的文本,而不是只删除它。

为了更清楚起见,我们将再次使用新的 test() 方法。我们的源文件将是 a-modified.pdf 文件。我们的目标文件将是一个新的 a-fixed.pdf 文件。

之前我们看到删除的文本被黑色背景覆盖。但是,此颜色是可配置的。由于我们知道文件中文本的背景是白色的,我们将强制覆盖为白色,与我们之前擦除的方法的类似。

但是,在调用 autoSweepCleanUp() 之后,我们将查询策略以获取已擦除代码的位置。然后,我们将实例化一个包含替换文本 abc的 PdfCanvas。此外,我们将删除上边距以使其与原始文本更好地对齐。默认对齐确实不太好。让我们看一下生成的代码:

@Test
    public void testReplaceContent() throws IOException {
        PdfReader reader = new PdfReader(
                "src/main/resources/a-modified.pdf");
        PdfWriter writer = new PdfWriter(
                "src/main/resources/a-fixed.pdf");
        PdfDocument pdfDocument = new PdfDocument(reader, writer);
        CompositeCleanupStrategy strategy = new CompositeCleanupStrategy();
        strategy.add(new RegexBasedCleanupStrategy("the")
                .setRedactionColor(ColorConstants.WHITE));
        PdfCleaner.autoSweepCleanUp(pdfDocument, strategy);
        for (IPdfTextLocation location : strategy.getResultantLocations()) {
            PdfPage page = pdfDocument.getPage(location.getPageNumber() + 1);
            PdfCanvas pdfCanvas = new PdfCanvas(page.newContentStreamAfter(),
                    page.getResources(), page.getDocument());
            Canvas canvas = new Canvas(pdfCanvas, location.getRectangle());
            canvas.add(new Paragraph("abc").setFontSize(6)
                    .setMarginTop(0f));
        }
        pdfDocument.close();
    }           

我们可以看一下效果:

java编辑PDF文件

结论

在本教程中,我们了解了如何编辑 PDF 文件的内容。 我们已经看到,我们可以添加新内容、删除现有内容,甚至可以将原始文件中的文本替换为新内容。