天天看點

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 檔案的内容。 我們已經看到,我們可以添加新内容、删除現有内容,甚至可以将原始檔案中的文本替換為新内容。