Java案例如何生成PDF?

wen python案例 2

本文目录导读:

Java案例如何生成PDF?

  1. iText库(最常用)
  2. Apache POI(适用于简单PDF)
  3. JasperReports(报表生成)
  4. 使用模板引擎+HTML转PDF
  5. 完整实用示例:生成发票PDF
  6. 性能优化建议
  7. 选型建议

我来详细介绍Java生成PDF的几种主流方法及具体案例。

iText库(最常用)

Maven依赖

<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itext7-core</artifactId>
    <version>7.2.5</version>
    <type>pom</type>
</dependency>

基础案例:创建简单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 com.itextpdf.layout.element.Table;
public class SimplePdfGenerator {
    public static void createSimplePdf(String dest) throws Exception {
        // 1. 创建PdfWriter
        PdfWriter writer = new PdfWriter(dest);
        // 2. 创建PdfDocument
        PdfDocument pdfDoc = new PdfDocument(writer);
        // 3. 创建Document
        Document document = new Document(pdfDoc);
        // 4. 添加内容
        document.add(new Paragraph("Hello World!"));
        document.add(new Paragraph("这是中文PDF示例"));
        // 5. 关闭文档
        document.close();
    }
    public static void main(String[] args) throws Exception {
        createSimplePdf("simple.pdf");
        System.out.println("PDF生成成功!");
    }
}

高级案例:表格和样式

import com.itextpdf.kernel.colors.ColorConstants;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.layout.borders.Border;
import com.itextpdf.layout.element.Cell;
import com.itextpdf.layout.property.TextAlignment;
import com.itextpdf.layout.property.UnitValue;
public class AdvancedPdfGenerator {
    public static void createTablePdf(String dest) throws Exception {
        PdfWriter writer = new PdfWriter(dest);
        PdfDocument pdfDoc = new PdfDocument(writer);
        Document document = new Document(pdfDoc);
        // 添加中文标题
        document.add(new Paragraph("员工信息表")
            .setTextAlignment(TextAlignment.CENTER)
            .setFontSize(20)
            .setBold());
        // 创建表格
        float[] columnWidths = {1, 2, 3, 2};
        Table table = new Table(UnitValue.createPercentArray(columnWidths));
        // 添加表头
        String[] headers = {"ID", "姓名", "部门", "薪资"};
        for (String header : headers) {
            Cell cell = new Cell().add(new Paragraph(header))
                .setBackgroundColor(ColorConstants.LIGHT_GRAY)
                .setTextAlignment(TextAlignment.CENTER);
            table.addCell(cell);
        }
        // 添加数据
        String[][] data = {
            {"001", "张三", "技术部", "15000"},
            {"002", "李四", "市场部", "12000"},
            {"003", "王五", "人事部", "10000"}
        };
        for (String[] row : data) {
            for (String cellData : row) {
                table.addCell(new Cell().add(new Paragraph(cellData))
                    .setTextAlignment(TextAlignment.CENTER));
            }
        }
        document.add(table);
        document.close();
    }
}

Apache POI(适用于简单PDF)

Maven依赖

<dependency>
    <groupId>org.apache.pdfbox</groupId>
    <artifactId>pdfbox</artifactId>
    <version>2.0.27</version>
</dependency>

案例:使用PDFBox创建PDF

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.font.PDType0Font;
import org.apache.pdfbox.pdmodel.font.PDType1Font;
public class PdfBoxExample {
    public static void createPdf() throws Exception {
        try (PDDocument document = new PDDocument()) {
            PDPage page = new PDPage();
            document.addPage(page);
            PDPageContentStream contentStream = new PDPageContentStream(document, page);
            // 设置字体
            contentStream.setFont(PDType1Font.HELVETICA_BOLD, 24);
            contentStream.beginText();
            contentStream.newLineAtOffset(100, 700);
            contentStream.showText("Hello PDFBox!");
            contentStream.endText();
            contentStream.close();
            document.save("pdfbox-example.pdf");
        }
    }
}

JasperReports(报表生成)

Maven依赖

<dependency>
    <groupId>net.sf.jasperreports</groupId>
    <artifactId>jasperreports</artifactId>
    <version>6.20.5</version>
</dependency>

案例:从数据生成PDF报表

import net.sf.jasperreports.engine.*;
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
import java.util.*;
public class JasperReportExample {
    public static void generateReport() throws JRException {
        // 准备数据
        List<Employee> employees = Arrays.asList(
            new Employee("001", "张三", "技术部"),
            new Employee("002", "李四", "市场部")
        );
        // 创建数据源
        JRBeanCollectionDataSource dataSource = 
            new JRBeanCollectionDataSource(employees);
        // 编译报表模板
        JasperReport jasperReport = JasperCompileManager
            .compileReport("template.jrxml");
        // 填充数据
        JasperPrint jasperPrint = JasperFillManager
            .fillReport(jasperReport, new HashMap<>(), dataSource);
        // 导出PDF
        JasperExportManager.exportReportToPdfFile(
            jasperPrint, "report.pdf");
    }
}
class Employee {
    private String id;
    private String name;
    private String department;
    // 构造方法和getter/setter
}

使用模板引擎+HTML转PDF

Maven依赖(使用Flying Saucer)

<dependency>
    <groupId>org.xhtmlrenderer</groupId>
    <artifactId>flying-saucer-pdf</artifactId>
    <version>9.1.22</version>
</dependency>

案例:HTML模板转PDF

import org.xhtmlrenderer.pdf.ITextRenderer;
public class HtmlToPdfExample {
    public static void convertHtmlToPdf() throws Exception {
        String htmlContent = """
            <html>
                <head>
                    <style>
                        body { font-family: SimSun; }
                        h1 { color: blue; }
                        table { border-collapse: collapse; width: 100%; }
                        th, td { border: 1px solid black; padding: 8px; }
                    </style>
                </head>
                <body>
                    <h1>用户信息报告</h1>
                    <table>
                        <tr><th>ID</th><th>姓名</th><th>邮箱</th></tr>
                        <tr><td>001</td><td>张三</td><td>zhang@example.com</td></tr>
                        <tr><td>002</td><td>李四</td><td>li@example.com</td></tr>
                    </table>
                </body>
            </html>
        """;
        ITextRenderer renderer = new ITextRenderer();
        renderer.setDocumentFromString(htmlContent);
        renderer.layout();
        try (OutputStream os = new FileOutputStream("output.pdf")) {
            renderer.createPDF(os);
        }
    }
}

完整实用示例:生成发票PDF

import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.*;
import com.itextpdf.layout.property.TextAlignment;
public class InvoicePdfGenerator {
    public static void createInvoice(String fileName, 
                                    InvoiceData data) throws Exception {
        PdfWriter writer = new PdfWriter(fileName);
        PdfDocument pdfDoc = new PdfDocument(writer);
        Document document = new Document(pdfDoc);
        // 公司信息
        document.add(new Paragraph("XX科技有限公司")
            .setTextAlignment(TextAlignment.CENTER)
            .setFontSize(24).setBold());
        document.add(new Paragraph("发票号码:" + data.getInvoiceNo())
            .setTextAlignment(TextAlignment.RIGHT));
        // 客户信息
        document.add(new Paragraph("客户:" + data.getCustomerName()));
        document.add(new Paragraph("日期:" + data.getDate()));
        document.add(new Paragraph("\n"));
        // 商品明细表格
        float[] columnWidths = {1, 3, 2, 2, 2};
        Table table = new Table(UnitValue.createPercentArray(columnWidths));
        // 表头
        String[] headers = {"序号", "商品名称", "数量", "单价", "金额"};
        for (String header : headers) {
            table.addHeaderCell(new Cell().add(new Paragraph(header))
                .setBold().setTextAlignment(TextAlignment.CENTER));
        }
        // 商品数据
        double total = 0;
        int index = 1;
        for (InvoiceItem item : data.getItems()) {
            table.addCell(String.valueOf(index++));
            table.addCell(item.getName());
            table.addCell(String.valueOf(item.getQuantity()));
            table.addCell(String.format("%.2f", item.getPrice()));
            double amount = item.getQuantity() * item.getPrice();
            table.addCell(String.format("%.2f", amount));
            total += amount;
        }
        document.add(table);
        // 总计
        document.add(new Paragraph("\n"));
        document.add(new Paragraph("总计金额:¥" + String.format("%.2f", total))
            .setTextAlignment(TextAlignment.RIGHT)
            .setBold().setFontSize(16));
        document.close();
    }
    // 测试
    public static void main(String[] args) throws Exception {
        InvoiceData data = new InvoiceData();
        data.setInvoiceNo("INV-2024-001");
        data.setCustomerName("阿里巴巴集团");
        data.setDate("2024-01-15");
        data.setItems(Arrays.asList(
            new InvoiceItem("笔记本电脑", 5, 5999.00),
            new InvoiceItem("显示器", 10, 1599.00),
            new InvoiceItem("键盘", 20, 299.00)
        ));
        createInvoice("invoice.pdf", data);
        System.out.println("发票PDF生成成功!");
    }
}
// 数据模型
class InvoiceData {
    private String invoiceNo;
    private String customerName;
    private String date;
    private List<InvoiceItem> items;
    // getter/setter省略
}
class InvoiceItem {
    private String name;
    private int quantity;
    private double price;
    // 构造方法、getter/setter省略
}

性能优化建议

// 1. 使用连接池管理字体资源
public class PdfFontProvider {
    private static Map<String, PdfFont> fontCache = new HashMap<>();
    public static PdfFont getFont(String fontPath) throws Exception {
        return fontCache.computeIfAbsent(fontPath, 
            path -> PdfFontFactory.createFont(path));
    }
}
// 2. 批量处理时复用资源
public class BatchPdfGenerator {
    public void batchGenerate(List<Data> dataList) throws Exception {
        // 预编译模板,复用PdfWriter等资源
        PdfFont font = PdfFontFactory.createFont("STSong-Light");
        for (Data data : dataList) {
            try (PdfWriter writer = new PdfWriter(data.getFileName());
                 PdfDocument pdfDoc = new PdfDocument(writer);
                 Document document = new Document(pdfDoc)) {
                // 生成PDF
                generateContent(document, data, font);
            }
        }
    }
}

选型建议

场景 推荐方案 优点 缺点
简单PDF iText/iText7 功能强大,社区活跃 商业使用需授权
报表类 JasperReports 模板化,支持复杂报表 学习曲线较陡
基于HTML Flying Saucer 可用CSS样式 对CSS支持有限
官方格式 Apache PDFBox Apache许可证,免费 功能相对基础
跨平台 Aspose.Words 功能全面 商业授权费用高

选择合适的方案时,考虑以下因素:

  • 预算:商业使用要注意许可证
  • 复杂度:简单文档用iText,复杂报表用JasperReports
  • 团队技能:熟悉HTML可选HTML转PDF方式
  • 性能要求:大数据量时选择性能更好的方案

抱歉,评论功能暂时关闭!