# 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)