代碼示例使用 iText 2.1.7 (com.lowagie),該版本為 LGPL/MPL 雙許可,最后開源版本。iText 5+ (com.itextpdf) 為商業閉源,需購買許可。開源替代方案:Apache PDFBox、OpenPDF (iText 4 分支)。本文以 iText 2.1.7 為例,核心 API 思路通用。
<dependency>
<groupId>com.lowagie</groupId>
<artifactId>itext</artifactId>
<version>2.1.7</version>
</dependency>
<dependency>
<groupId>com.lowagie</groupId>
<artifactId>itext-asian</artifactId>
<version>2.1.7</version>
</dependency>
Document:文檔模型,設置頁面大小、邊距。PdfWriter:將 Document 寫入 OutputStream,綁定輸出目標。Paragraph:文本塊,可設置字體、顏色、對齊、間距。Chapter/Section:層級結構,自動生成書籤、目錄。FontFactory:字體工廠,註冊 TTF 字體文件支持中文。PdfPTable/PdfPCell:表格佈局。Image:圖片嵌入。public static void simplePdf(String dest) throws Exception {
Document doc = new Document(PageSize.A4, 50, 50, 50, 50);
PdfWriter.getInstance(doc, new FileOutputStream(dest));
doc.open();
doc.add(new Paragraph("Hello PDF!"));
doc.close();
}
public static void complexReport(String dest) throws Exception {
// 1. 註冊中文字體 (需 itext-asian 或手動註冊 TTF)
FontFactory.register("C:/Windows/Fonts/msyh.ttc", "Microsoft YaHei");
Font chineseFont = FontFactory.getFont("Microsoft YaHei", BaseFont.IDENTITY_H, BaseFont.EMBEDDED, 12);
Font titleFont = new Font(chineseFont.getBaseFont(), 18, Font.BOLDITALIC, BaseColor.BLUE);
Font sectionFont = new Font(chineseFont.getBaseFont(), 14, Font.BOLD, BaseColor.RED);
Document doc = new Document(PageSize.A4, 36, 36, 54, 36);
PdfWriter writer = PdfWriter.getInstance(doc, new FileOutputStream(dest));
// 可選:頁眉頁腳事件
writer.setPageEvent(new HeaderFooterEvent());
doc.open();
// 元數據
doc.addTitle("銷售報表");
doc.addAuthor("系統自動生成");
doc.addSubject("2024 Q1");
doc.addKeywords("PDF, iText, Report");
doc.addCreator("Java App");
// 標題
Paragraph title = new Paragraph("2024 年第一季度銷售報表", titleFont);
title.setAlignment(Element.ALIGN_CENTER);
title.setSpacingAfter(20);
doc.add(title);
// 章節 1
Chapter chapter1 = new Chapter(new Paragraph("1. 總體概況", sectionFont), 1);
chapter1.setNumberDepth(0); // 不顯示章節編號
Section sec1 = chapter1.addSection(new Paragraph("1.1 關鍵指標", new Font(chineseFont.getBaseFont(), 12, Font.BOLD)));
sec1.add(new Paragraph("本季度總銷售額達 1.2 億元,同比增長 15%。", chineseFont));
// 表格
PdfPTable table = new PdfPTable(new float[]{2, 1, 1, 1});
table.setWidthPercentage(100);
addTableHeader(table, chineseFont, "月份", "銷售額(萬)", "訂單數", "客單價");
addTableRow(table, chineseFont, "1月", "350", "1200", "291");
addTableRow(table, chineseFont, "2月", "420", "1450", "289");
addTableRow(table, chineseFont, "3月", "430", "1500", "286");
sec1.add(table);
doc.add(chapter1);
// 章節 2 - 新頁
doc.newPage();
Chapter chapter2 = new Chapter(new Paragraph("2. 區域明細", sectionFont), 2);
// ... 類似添加內容
doc.add(chapter2);
doc.close();
}
private static void addTableHeader(PdfPTable table, Font font, String... headers) {
for (String h : headers) {
PdfPCell cell = new PdfPCell(new Phrase(h, font));
cell.setBackgroundColor(BaseColor.LIGHT_GRAY);
cell.setHorizontalAlignment(Element.ALIGN_CENTER);
cell.setPadding(5);
table.addCell(cell);
}
}
private static void addTableRow(PdfPTable table, Font font, String... cols) {
for (String c : cols) {
PdfPCell cell = new PdfPCell(new Phrase(c, font));
cell.setHorizontalAlignment(Element.ALIGN_CENTER);
cell.setPadding(5);
table.addCell(cell);
}
}
class HeaderFooterEvent extends PdfPageEventHelper {
Font font = FontFactory.getFont(FontFactory.HELVETICA, 8, Font.NORMAL, BaseColor.GRAY);
@Override
public void onEndPage(PdfWriter writer, Document doc) {
PdfContentByte cb = writer.getDirectContent();
// 頁碼
ColumnText.showTextAligned(cb, Element.ALIGN_CENTER,
new Phrase(String.format("第 %d 頁", writer.getPageNumber()), font),
(doc.right() - doc.left()) / 2 + doc.leftMargin(),
doc.bottomMargin() - 10, 0);
// 頁眉
ColumnText.showTextAligned(cb, Element.ALIGN_LEFT,
new Phrase("機密文檔", font),
doc.left(), doc.top() + 10, 0);
}
}
| 問題 | 解決方案 |
|---|---|
| 中文顯示 □/亂碼 | 未嵌入字體或字體不支持該字符;檢查 FontFactory.register 路徑、BaseFont.IDENTITY_H、EMBEDDED |
| 大文件 OOM | 使用 `PdfWriter.setFullCompression()`、分頁寫入、避免一次性載入大量數據到內存 |
| 表格跨頁斷裂 | `table.setSplitLate(false)`、`cell.setNoWrap(false)`、設置 `setKeepTogether(false)` |
| 性能優化 | 複用 Font 對象、關閉不必要的調試日志、大批量數據考慮 PDFBox 流式寫入 |
| 數字簽名/水印 | 使用 `PdfStamper` 簽名、`PdfContentByte` 繪制水印 |
若項目無法承擔商業許可,建議遷移至 OpenPDF (groupId: com.github.librepdf, artifactId: openpdf),API 與 iText 2.1.7 高度兼容,僅包名變為 `com.lowagie` → `com.lowagie` (同包名) 或 `com.itextpdf` (iText 5+)。遷移步驟:替換依賴、修正包導入、測試中文表格分頁。
iText 以強大的佈局引擎、豐富的 PDF 操作 API(表格、書籤、簽名、加密、表單)成為 Java 生態 PDF 生成首選。掌握 Document/Writer/Paragraph/Chapter/Table/Font 核心組件協作,配合中文字體嵌入、頁面事件、流式寫入技巧,可構建專業級報表、合同、發票生成系統。