java 数据列表导出为PDF

32 阅读3分钟
package org.demo;

import com.lowagie.text.*;
import com.lowagie.text.pdf.BaseFont;
import com.lowagie.text.pdf.PdfPCell;
import com.lowagie.text.pdf.PdfPTable;
import com.lowagie.text.pdf.PdfWriter;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.List;

/**
 * 使用OpenPDF将列表数据导出为PDF文件的工具类
 */
public class ListToPdfExporter {

    private static BaseFont getBaseFont() {
        try {
            // 加载本地自定义字体(以思源黑体为例)
            BaseFont baseFont = BaseFont.createFont(
                    "xx/NotoSansSC-Black.ttf",  // 字体文件路径(相对于resources目录)
                    BaseFont.IDENTITY_H,                 // 支持中文的编码方式
                    BaseFont.EMBEDDED                    // 将字体嵌入PDF,确保在其他设备上正常显示
            );
            return baseFont;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 将列表数据导出为PDF文件
     *
     * @param dataList 要导出的数据列表
     * @param filePath 导出的PDF文件路径
     * @param title    PDF文档标题
     * @param <T>      数据类型
     * @throws DocumentException      PDF文档处理异常
     * @throws FileNotFoundException  文件未找到异常
     * @throws IllegalAccessException 反射访问异常
     */
    public static <T> void export(List<T> dataList, String filePath, String title)
            throws DocumentException, IOException, IllegalAccessException {

        // 检查输入参数
        if (dataList == null || dataList.isEmpty()) {
            throw new IllegalArgumentException("数据列表不能为空");
        }
        if (filePath == null || filePath.trim().isEmpty()) {
            throw new IllegalArgumentException("文件路径不能为空");
        }

        // 创建文档对象,设置页面大小为A4,左右上下边距为50
        Document document = new Document(PageSize.A4, 50, 50, 50, 50);

        // 创建PdfWriter对象,将文档写入指定文件
        PdfWriter.getInstance(document, new FileOutputStream(filePath));

        // 打开文档
        document.open();

        // 添加标题
        Font titleFont = new Font(getBaseFont(), 18, Font.BOLD);
        Paragraph titleParagraph = new Paragraph(title, titleFont);
        titleParagraph.setAlignment(Element.ALIGN_CENTER);
        titleParagraph.setSpacingAfter(20); // 标题后间距
        document.add(titleParagraph);

        // 获取数据类的所有字段(作为表格列)
        Class<?> dataClass = dataList.get(0).getClass();
        Field[] fields = dataClass.getDeclaredFields();
        int columnCount = fields.length;

        // 创建表格,列数为字段数量
        PdfPTable table = new PdfPTable(columnCount);
        table.setWidthPercentage(100); // 表格宽度占页面100%
        table.setSpacingBefore(10f); // 表格前间距
        table.setSpacingAfter(10f); // 表格后间距

        // 设置表格列宽比例(平均分配)
        float[] columnWidths = new float[columnCount];
        for (int i = 0; i < columnCount; i++) {
            columnWidths[i] = 1.0f;
        }
        table.setWidths(columnWidths);

        // 添加表头(使用字段名)
        Font headerFont = new Font(getBaseFont(), 12, Font.BOLD);
        for (Field field : fields) {
            PdfPCell cell = new PdfPCell(new Phrase(field.getName(), headerFont));
            cell.setHorizontalAlignment(Element.ALIGN_CENTER);
            //cell.setBackgroundColor(BaseColor.LIGHT_GRAY);
            table.addCell(cell);
        }

        // 添加数据行
        //  Font dataFont = new Font(Font.HELVETICA, 10, Font.NORMAL);
        // 使用自定义字体创建Font对象,替代原来的Font.HELVETICA
        Font dataFont = new Font(getBaseFont(), 10, Font.NORMAL);
        for (T data : dataList) {
            for (Field field : fields) {
                field.setAccessible(true); // 设置可访问私有字段
                Object value = field.get(data);
                String cellValue = value != null ? value.toString() : "";

                PdfPCell cell = new PdfPCell(new Phrase(cellValue, dataFont));
                cell.setHorizontalAlignment(Element.ALIGN_LEFT);
                table.addCell(cell);
            }
        }

        // 将表格添加到文档
        document.add(table);

        // 添加页脚信息
        Paragraph footer = new Paragraph("导出时间: " + new java.util.Date(), new Font(Font.HELVETICA, 8));
        footer.setAlignment(Element.ALIGN_RIGHT);
        footer.setSpacingBefore(20);
        document.add(footer);

        // 关闭文档
        document.close();
        System.out.println("PDF文件已成功导出至: " + filePath);
    }

    // 测试用的实体类
    public static class User {
        private String name;
        private int age;
        private String email;
        private String address;

        public User(String name, int age, String email, String address) {
            this.name = name;
            this.age = age;
            this.email = email;
            this.address = address;
        }

        // getter和setter方法
        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }

        public String getEmail() {
            return email;
        }

        public void setEmail(String email) {
            this.email = email;
        }

        public String getAddress() {
            return address;
        }

        public void setAddress(String address) {
            this.address = address;
        }
    }

    // 测试方法
    public static void main(String[] args) {
        try {
            // 创建测试数据列表
            List<User> userList = List.of(
                    new User("张三", 25, "zhangsan@example.com", "北京市朝阳区"),
                    new User("李四", 30, "lisi@example.com", "上海市浦东新区"),
                    new User("王五", 35, "wangwu@example.com", "广州市天河区"),
                    new User("赵六", 28, "zhaoliu@example.com", "深圳市南山区")
            );

            // 导出为PDF
            export(userList, "用户列表.pdf", "系统用户信息列表");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
// 字体库 https://github.com/adobe-fonts/source-han-sans/releases/tag/2.005R

不使用字体库,会导致生成的PDF不显示中文,因为默认的字体库不支持中文编码

OpenPDF依赖

<dependency>
    <groupId>com.github.librepdf</groupId>
    <artifactId>openpdf</artifactId>
    <version>1.3.30</version>
</dependency>