首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

iText 實戰:Java 服務端動態生成 PDF 報表完整指南

     編輯:Java 相關知識

為什麼選擇 iText?

  • 純 Java、開源 (AGPL/商業雙許可)、API 豐富、支持中文、支持 PDF/RTF/HTML/XML 多格式輸出。
  • 適合 B/S 架構:服務端生成 PDF 流直接輸出到 HttpServletResponse,瀏覽器內聯顯示或下載,無需客戶端插件。

核心 5 步生成流程

// 1. 創建文檔模型 (頁面大小、邊距)
Document document = new Document(PageSize.A4, 50, 50, 50, 50);

// 2. 綁定寫入器 (輸出目標: 文件/輸出流/HTTP 響應)
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream("report.pdf"));
// Web 場景: PdfWriter.getInstance(document, response.getOutputStream());

// 3. 打開文檔 (必須在添加內容前)
document.open();

// 4. 添加內容 (Paragraph, Table, Image, List, Chapter...)
// ... 見下文詳解 ...

// 5. 關閉文檔 (刷新緩衝區、寫入 xref 表、釋放資源)
document.close();

文檔屬性與頁面控制

功能API說明
元數據document.addTitle/Author/Subject/Keywords/Creator()PDF 屬性欄顯示
頁眉/頁腳document.setHeader/Footer(new HeaderFooter(...))需在 open() 前設置首頁;換頁自動重複
頁碼HeaderFooter(..., true) 最後一參為 true 自動生成頁碼可自定義格式 "第 {PAGE} 頁"
水印document.add(new Watermark("CONFIDENTIAL"))斜向灰色文字覆蓋
頁面大小/橫向document.setPageSize(PageSize.A4.rotate())新頁面生效;首頁需 open() 前設置
加密權限writer.setEncryption(userPass, ownerPass, PdfWriter.ALLOW_PRINTING, PdfWriter.STRENGTH128BITS)設置用戶/擁有者密碼、禁止複製/修改/打印
查看器偏好writer.setViewerPreferences(PdfWriter.PageLayoutSinglePage | PdfWriter.HideToolbar)控制 Acrobat 打開行為

文本處理三層模型

  1. Chunk:最小單位,一串同樣字體/顏色/大小的文本。new Chunk("text", font)
  2. Phrase:多個 Chunk 組成,自動換行。phrase.add(chunk)。可設置行距 setLeading(16f)。
  3. Paragraph:段落,繼承 Phrase,支持對齊、縮進、段前段後間距。para.setAlignment(Element.ALIGN_CENTER) (1=居中, 2=右對齊, 0=左對齊)。

中文字體解決方案 (關鍵!)

  1. 下載 itext-asian.jar (含 Adobe 亞洲字體包映射)。
  2. 代碼註冊:
BaseFont bfChinese = BaseFont.createFont(
    "STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED); // 不嵌入字體,依賴閱讀器
Font chineseFont = new Font(bfChinese, 12, Font.NORMAL);

Paragraph p = new Paragraph("中文報表標題", chineseFont);

注意:NOT_EMBEDDED 體積小但要求客戶端系統安裝對應字體 (Windows 通常有宋體/微軟雅黑);嵌入字體 (BaseFont.EMBEDDED) 文件大增但跨平台顯示一致。

表格 (Table/PdfPTable) 深度解析

推薦使用 com.lowagie.text.pdf.PdfPTable (在 com.lowagie.text.pdf 包下),功能完整、支持跨頁、單元格合併、邊框精細控制。舊 com.lowagie.text.Table 已棄用。

基礎構建

PdfPTable table = new PdfPTable(3); // 3 列
table.setWidthPercentage(100); // 佔頁面寬度 100%
table.setSpacingBefore(10f); table.setSpacingAfter(10f);
table.setWidths(new float[]{1, 2, 3}); // 相對列寬比例

// 表頭
PdfPCell header = new PdfPCell(new Phrase("報表標題", headerFont));
header.setColspan(3); header.setHorizontalAlignment(Element.ALIGN_CENTER);
header.setBackgroundColor(BaseColor.LIGHT_GRAY);
header.setBorderWidth(1);
table.addCell(header); // addCell 自動換行填充
table.completeRow(); // 可選:強制結束當前行

// 數據行
table.addCell(new PdfPCell(new Phrase("列1", cellFont)));
table.addCell(new PdfPCell(new Phrase("列2", cellFont)));
table.addCell(new PdfPCell(new Phrase("列3", cellFont)));
// 循環添加數據...
document.add(table);

進階單元格控制

  • 跨行/跨列:cell.setRowspan(2); cell.setColspan(2);
  • 邊框精細:cell.setBorder(Rectangle.NO_BORDER) 或組合 Rectangle.TOP | Rectangle.BOTTOM,setBorderColor()、setBorderWidth()。
  • 內邊距:cell.setPadding(5) / setPaddingLeft/Right/Top/Bottom。
  • 垂直對齊:cell.setVerticalAlignment(Element.ALIGN_MIDDLE)。
  • 不分頁:table.setSplitLate(false) (盡量不拆分行);cell.setNoWrap(true) (單元格內不換行)。

圖片處理

Image img = Image.getInstance("chart.png"); // 自動識別格式
img.setAlignment(Image.MIDDLE | Image.TEXTWRAP); // 居中 + 文字環繞
img.scalePercent(50); // 縮放 50%
img.setRotationDegrees(30); // 旋轉 30 度
document.add(img);

支持格式:JPEG, PNG, GIF, WMF, BMP, TIFF (需 JAI)。

Web 應用集成模式

@WebServlet("/pdf/report")
public class ReportServlet extends HttpServlet {
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
        resp.setContentType("application/pdf");
        // 內聯顯示: inline; 下載: attachment
        resp.setHeader("Content-Disposition", "inline; filename=report.pdf");

        try {
            Document doc = new Document(PageSize.A4);
            PdfWriter.getInstance(doc, resp.getOutputStream());
            doc.open();
            // ... 構建報表內容 ...
            doc.close(); // 關鍵:必須 close 才能輸出完整 PDF
        } catch (DocumentException | IOException e) {
            throw new ServletException(e);
        }
    }
}

常見坑與避坑指南

問題原因解決
中文顯示為 □/亂碼未註冊中文字體 / 字體未嵌入且客戶端無字體引入 itext-asian,使用 STSong-Light/UniGB-UCS2-H,或嵌入 TTF 字體
表格跨頁表頭不重複未設置 Header 行table.setHeaderRows(1) (前 N 行作為表頭自動重複)
生成的 PDF 打開報錯「文件損壞」document.close() 未調用 / 輸出流被提前關閉 / Servlet 又寫了其他內容確保 close() 在 finally 執行,Response 只輸出 PDF 流
大報表生成 OOM一次性構建巨大 Document 對象樹分頁生成、使用 PdfPTable 流式寫入、或升級 iText 7 (更高效)
圖片不顯示相對路徑錯誤 / 圖片流已關閉用 Image.getInstance(byte[]) 或 InputStream,確保流在 doc.close() 前有效

版本選擇建議

  • iText 5 (5.5.x):經典版本,大量存量代碼,AGPL 許可證 (商業用途需購買授權)。
  • iText 7 (7.x):重寫架構、模組化、性能更強、支持 PDF/UA、HTML2PDF 插件強大,同樣 AGPL/商業雙許可。
  • OpenPDF:iText 4 的 LGPL 分支,免費商用,API 與 iText 5 兼容,適合預算受限項目。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved