---
# System prepended metadata

title: Java PDF教學

---

# Java PDF教學
## 前言
&nbsp;&nbsp;Java PDF這個功能有分成付費版、免費版，兩著的差異除了一個要錢、一個不用錢之外，對於工程師來說就是難易度了，付費版幾乎是已經套好，我們只要塞入值就可以、免費版要拼拼湊湊而且可能搞了半天還沒辦法符合需求方的樣子。

&nbsp;&nbsp;話雖如此，但是本文會採用免費的資源來做教學。

## pdfbox 教學

### 前置作業
&nbsp;&nbsp;搜尋 `pdfbox Maven` 加入依賴
網址：https://mvnrepository.com/artifact/org.apache.pdfbox/pdfbox

pdfbox Maven
``` xml=
<dependency>
    <groupId>org.apache.pdfbox</groupId>
    <artifactId>pdfbox</artifactId>
    <version>3.0.5</version>
</dependency>
```

到專案找到pom.xml加入剛剛複製的依賴
pom.xml
``` xml=
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>testPDF</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!-- PDF -->
        <dependency>
            <groupId>org.apache.pdfbox</groupId>
            <artifactId>pdfbox</artifactId>
            <version>3.0.5</version>
        </dependency>
    </dependencies>

</project>
```

### 實作
&nbsp;&nbsp;首先建立一個Java檔，命名為`CreateSimplePdf`

CreateSimplePdf.java
``` java=
package pdf;

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.font.PDType1Font;
import org.apache.pdfbox.pdmodel.font.Standard14Fonts;

public class CreateSimplePdf {
    public static void main(String[] args) throws IOException {
        // 建立 PDF 檔
        PDDocument document = new PDDocument();

        // 建立新的頁面
        PDPage page = new PDPage();
        document.addPage(page);

        // 建立內容
        PDPageContentStream contentStream = new PDPageContentStream(document, page);

        // 開始繪製內容
        contentStream.beginText();

        // 設定字體和大小
        contentStream.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD), 12);

        // 設定文字位置
        contentStream.newLineAtOffset(100, 700);

        // 寫入文字
        contentStream.showText("Hello, World! This is my first PDF with PDFBox.");

        // 結束繪製內容
        contentStream.endText();

        // 關閉建立內容
        contentStream.close();

        // 儲存 PDF 文件
        document.save("C:/Users/User-name/workspace/testPDF/simple.pdf");

        // 關閉文件
        document.close();

        System.out.println("PDF 檔案已成功建立！");
    }
}
```

### 結果
![image](https://hackmd.io/_uploads/rkoHOVspxg.png)


## iText  教學

### 前置作業
&nbsp;&nbsp;搜尋 `iText 7  Maven` 加入依賴
網址：https://mvnrepository.com/artifact/com.itextpdf/itext7-core

iText Maven
``` xml=
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itext7-core</artifactId>
    <version>7.1.16</version>
    <type>pom</type>
</dependency>
```

到專案找到pom.xml加入剛剛複製的依賴
pom.xml
``` xml=
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>testPDF</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!-- PDF -->
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itext7-core</artifactId>
            <version>7.1.16</version>
            <type>pom</type>
        </dependency>
    </dependencies>
    
</project>
```

### 實作
&nbsp;&nbsp;首先建立一個Java檔，命名為`CreateSimplePdf`

CreateSimplePdf.java
``` java=
package pdf;

import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.Paragraph;
import java.io.FileNotFoundException;

public class CreateSimplePdf {
    public static void main(String[] args) throws FileNotFoundException {
        // 設定 PDF 檔案的輸出路徑
        String path = "C:/Users/User-name/workspace/testPDF/simple.pdf";

        // 建立 PdfWriter 物件
        PdfWriter writer = new PdfWriter(path);

        // 建立 PdfDocument 物件
        PdfDocument pdf = new PdfDocument(writer);

        // 建立 Document 物件
        Document document = new Document(pdf);

        // 加入文字
        document.add(new Paragraph("Hello, World! This is my first PDF with iText."));

        // 關閉 Document
        document.close();

        System.out.println("PDF 檔案已成功建立！");
    }
}
```

### 結果
![image](https://hackmd.io/_uploads/HyOIoVi6el.png)

## PDFBox實作表格

CreateSimplePdf.java
``` java=
package pdf;

import org.apache.pdfbox.pdmodel.*;
import org.apache.pdfbox.pdmodel.font.*;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import java.io.IOException;
import org.apache.pdfbox.pdmodel.font.Standard14Fonts;

public class CreateSimplePdf {
    public static void main(String[] args) {
        try (PDDocument document = new PDDocument()) {
            PDPage page = new PDPage(PDRectangle.A4);
            document.addPage(page);

            PDPageContentStream contentStream = new PDPageContentStream(document, page);

            PDFont font = new PDType1Font(Standard14Fonts.FontName.HELVETICA);
            float fontSize = 12;
            float leading = 1.5f * fontSize;

            float margin = 50;
            float yStart = page.getMediaBox().getHeight() - margin;

            float tableWidth = page.getMediaBox().getWidth() - 2 * margin;
            float[] columnWidths = { tableWidth * 0.3f, tableWidth * 0.4f, tableWidth * 0.3f };
            String[] headers = { "name", "phone", "acount" };

            String[][] data = {
                    {"andy", "123456789", "$1,000"},
                    {"unan", "987654321", "$2,500"},
            };

            float yPosition = yStart;
            contentStream.setFont(font, fontSize);

            drawRow(contentStream, font, fontSize, margin, yPosition, columnWidths, headers);
            yPosition -= leading;

            for (String[] row : data) {
                drawRow(contentStream, font, fontSize, margin, yPosition, columnWidths, row);
                yPosition -= leading;
            }

            contentStream.close();
            document.save("C:/Users/User-name/workspace/testPDF/simple.pdf");
            System.out.println("PDF 已產生完成。");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void drawRow(PDPageContentStream contentStream, PDFont font, float fontSize,
                                float startX, float y, float[] columnWidths, String[] cells) throws IOException {
        float textX = startX;
        for (int i = 0; i < cells.length; i++) {
            contentStream.beginText();
            contentStream.setFont(font, fontSize);
            contentStream.newLineAtOffset(textX + 2, y);
            contentStream.showText(cells[i]);
            contentStream.endText();
            textX += columnWidths[i];
        }
    }
}
```

### 結果
![image](https://hackmd.io/_uploads/ByO3oLiTge.png)

## 問題來了，無法支援中文，該怎麼辦?
&nbsp;&nbsp;以上都因為沒有出現中文，看似沒有問題的Code，那假設我現在要產一個有中文的PDF會有問題嗎?

&nbsp;&nbsp;答案是：會，因為這接引用的套件根本不支援中文字，所以在編譯器跑的時候就會告訴你錯誤。

&nbsp;&nbsp;那該如何做才能讓我的PDF可以有中文呢?

### 實作
&nbsp;&nbsp;開始前，大家一定要知道自己電腦的字體包在哪
路徑：C:\Windows\Fonts

![image](https://hackmd.io/_uploads/SyqvivsTeg.png)

![image](https://hackmd.io/_uploads/S1Fsjwoalg.png)


&nbsp;&nbsp;強制引入設定中文包讓整個專案可以使用。
`String fontPath = "C:/Windows/Fonts/kaiu.ttf";`

備註：為何選用 `kaiu.ttf` 這個字體，是因為我嘗試了很多種 `mingliub.ttc` 、 `mingliu.ttc` 都無法成功，表示有許多字體其實是不支援的，最後使用標楷體就可以了。

CreateChinesePdf.java
``` java=
package pdf;

import org.apache.pdfbox.pdmodel.*;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.font.*;

import java.io.File;
import java.io.IOException;

public class CreateChinesePdf {
    public static void main(String[] args) {
        String outputPath = "C:/Users/User-Name/workspace/testPDF/test.pdf";
        String fontPath = "C:/Windows/Fonts/kaiu.ttf";

        String[] headers = {"姓名", "電話", "金額"};
        String[][] data = {
                {"王小明", "123456789", "$1,000"},
                {"陳月霞", "987654321", "$2,500"},
        };

        try (PDDocument document = new PDDocument()) {
            PDPage page = new PDPage(PDRectangle.A4);
            document.addPage(page);

            PDFont font = PDType0Font.load(document, new File(fontPath));
            float fontSize = 12;
            float rowHeight = 1.5f * fontSize;
            float margin = 50;
            float yStart = page.getMediaBox().getHeight() - margin;
            float tableWidth = page.getMediaBox().getWidth() - 2 * margin;
            float[] colWidths = {tableWidth * 0.3f, tableWidth * 0.4f, tableWidth * 0.3f};

            try (PDPageContentStream cs = new PDPageContentStream(document, page)) {
                cs.setFont(font, fontSize);

                float y = yStart;
                y = drawRow(cs, font, fontSize, margin, y, colWidths, headers, rowHeight);

                for (String[] row : data) {
                    y = drawRow(cs, font, fontSize, margin, y, colWidths, row, rowHeight);
                }
            }

            document.save(outputPath);
            System.out.println("建立完成");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static float drawRow(PDPageContentStream cs, PDFont font, float fontSize,
                                 float startX, float y, float[] colWidths, String[] cells, float rowHeight) throws IOException {
        float x = startX;

        for (int i = 0; i < cells.length; i++) {
            float w = colWidths[i];

            // 畫格線
            cs.addRect(x, y - rowHeight, w, rowHeight);
            cs.stroke();

            // 畫文字
            cs.beginText();
            cs.newLineAtOffset(x + 2, y - fontSize);
            cs.showText(cells[i]);
            cs.endText();

            x += w;
        }

        return y - rowHeight;
    }
}

```

### 結果
![image](https://hackmd.io/_uploads/HynRdDsale.png)