java生成PDF合并单元格

需求

最近需要开发一个功能,涉及到Excel导出与pdf导出,其实pdf导出不适合表头太多的表格,不美观,但是需求如此,那就开发吧。

技术选型

参考了网上的资料,选用itextpdf做为技术支持,开始itextpdf-core对应的依赖,但是导出中文会有格式问题,并且乱码,需要引入本地的字体。

java 复制代码
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itext7-core</artifactId>
            <version>7.1.16</version>
            <type>pom</type>
        </dependency>
java 复制代码
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.itextpdf.io.font.PdfEncodings;
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.Table;
import com.itextpdf.layout.property.UnitValue;
import com.travelsky.commons.utils.StringUtil;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.http.HttpServletResponse;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * @Author maruko
 * @Date 2023/12/15 10:05
 * @Description: 导出pdf添加页数
 * @Version 1.0
 */
@Slf4j
public class PdfPageUtil {
    /**
     * 下载导出为PDF
     * @param fileName 文件名
     * @param headers 表头 注意中英文
     * @param data 数据集合  实体类可采用entityToList转换
     * @param response
     * @throws Exception
     */
    public static void downloadPdf(String fileName, List<String> headers, List<List<String>> data, HttpServletResponse response) throws Exception {
        // 设置编码格式
        response.setContentType("application/pdf;charset=UTF-8");
        response.setCharacterEncoding("utf-8");
        fileName = URLEncoder.encode(fileName, "UTF-8");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".pdf");
        PdfWriter writer = new PdfWriter(response.getOutputStream());
        PdfDocument pdfDoc = new PdfDocument(writer);
        Document document = new Document(pdfDoc);

        URL resource = PdfPageUtil.class.getClassLoader().getResource("simhei.ttf");
        String path = resource.getPath();
        PdfFont font = PdfFontFactory.createFont(path, PdfEncodings.IDENTITY_H, true);

        //根据表头数量设置列宽比例和宽度为页面宽度
        float[] columnWidths = new float[headers.size()];
        for (int i = 0; i < headers.size(); i++) {
            columnWidths[i] = 1;
        }
        // 创建表格并添加数据和表头
        Table table = new Table(UnitValue.createPercentArray(columnWidths)).useAllAvailableWidth();
        // 添加表头单元格
        for (String header : headers) {
            if (StringUtil.isEmpty(header)) {
                table.addHeaderCell("");
            } else {
                table.addHeaderCell(header).setFont(font);
            }

        }
        // 添加数据单元格到表格中
        for (List<String> rowData : data) {
            for (String cell : rowData) {
                if (StringUtil.isEmpty(cell)) {
                    table.addCell("");
                } else {
                    table.addCell(cell).setFont(font);
                }
            }
        }
        document.add(table);
        document.close();

    }

    public static List<List<String>> entityToList(List<?> list, String[] fields, Class<?> classType) {
        List<List<String>> dataList = new ArrayList<>();
        String temp = JSONObject.toJSONString(list);
        List<?> objects = JSONArray.parseArray(temp, classType);
        for (Object object : objects) {
            Map<String, String> map = JSONObject.parseObject(JSONObject.toJSONString(object),
                    new TypeReference<Map<String, String>>() {
                    });
            List<String> tempList = new ArrayList<>();
            for (String field : fields) {
                tempList.add(map.get(field));
            }
            dataList.add(tempList);
        }
        return dataList;
    }
}

问题

上述实现,对于中文有一定问题,还无法合并单元格,所以换了其它依赖

java 复制代码
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itextpdf</artifactId>
            <version>5.5.10</version>
        </dependency>
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itext-asian</artifactId>
            <version>5.2.0</version>
        </dependency>

实现方式

java 复制代码
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.itextpdf.text.*;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfWriter;
import com.travelsky.domain.pdf.PdfHeader;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.http.HttpServletResponse;
import java.io.FileOutputStream;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @Author maruko
 * @Date 2023/12/15 10:05
 * @Description: 导出pdf添加页数
 * @Version 1.0
 */
@Slf4j
public class PdfPageUtil {

    public static void main(String[] args) throws Exception {
    }

    /**
     * 下载导出为PDF
     *
     * @param fileName   文件名
     * @param headerSize 表头数量
     * @param headers    表头 注意中英文
     * @param data       数据集合  实体类可采用entityToList转换
     * @param response   HttpServletResponse
     * @throws Exception ex
     */
    public static void downloadPdf(String fileName, int headerSize, List<List<PdfHeader>> headers, List<List<String>> data, HttpServletResponse response) throws Exception {
        // 设置编码格式
        response.setContentType("application/pdf;charset=UTF-8");
        response.setCharacterEncoding("utf-8");
        fileName = URLEncoder.encode(fileName, "UTF-8");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".pdf");
        Document document = new Document();
        PdfWriter pdfWriter = PdfWriter.getInstance(document, response.getOutputStream());
        pdfWriter.setViewerPreferences(PdfWriter.PageModeUseThumbs);
        //设置A4
        document.setPageSize(PageSize.A4);
        document.open();

        // 创建表格并添加数据和表头 采用百分比模式 不用固定长度
        PdfPTable table = new PdfPTable(headerSize);
        table.setWidthPercentage(100.0F);

        // 添加表头单元格
        for (List<PdfHeader> header : headers) {
            for (PdfHeader pdfHeader : header) {
                fillHeaderCell(pdfHeader, getPdfChineseFont(), table);
            }
        }

        // 添加数据单元格到表格中
        for (List<String> rowData : data) {
            for (String cell : rowData) {
                fillCell(cell, getPdfChineseFont(), table);
            }
        }

        document.add(table);
        document.close();

    }

    /**
     * 填充单元格
     *
     * @param header PdfRowHeader
     * @param font   Font
     * @param table  PdfPTable
     */
    private static void fillHeaderCell(PdfHeader header, Font font, PdfPTable table) {
        PdfPCell headerCell = new PdfPCell();
        //不为0才有合并列
        if (header.getRowSpan() > 0) {
            headerCell.setRowspan(header.getRowSpan());
        }
        if (header.getColSpan() > 0) {
            headerCell.setColspan(header.getColSpan());
        }
        headerCell.setVerticalAlignment(Element.ALIGN_MIDDLE);
        headerCell.setHorizontalAlignment(Element.ALIGN_CENTER);
        headerCell.setFixedHeight(30);
        Paragraph paragraph = new Paragraph(header.getContent(), font);
        headerCell.setPhrase(paragraph);
        table.addCell(headerCell);
    }

    /**
     * 填充单元格
     *
     * @param content String
     * @param font    Font
     * @param table   PdfPTable
     */
    private static void fillCell(String content, Font font, PdfPTable table) {
        PdfPCell headerCell = new PdfPCell(new Paragraph(content, font));
        headerCell.setVerticalAlignment(Element.ALIGN_MIDDLE);
        headerCell.setHorizontalAlignment(Element.ALIGN_CENTER);
        headerCell.setFixedHeight(30);
        table.addCell(headerCell);
    }

    /**
     * 实体类转换成List
     *
     * @param list       数据集合
     * @param fields     字段
     * @param classType  实体类
     * @return List<List < String>>
     */
    public static List<List<String>> entityToList(List<?> list, String[] fields, Class<?> classType) {
        List<List<String>> dataList = new ArrayList<>();
        String temp = JSONObject.toJSONString(list);
        List<?> objects = JSONArray.parseArray(temp, classType);
        for (Object object : objects) {
            Map<String, String> map = JSONObject.parseObject(JSONObject.toJSONString(object),
                    new TypeReference<Map<String, String>>() {
                    });
            List<String> tempList = new ArrayList<>();
            for (String field : fields) {
                tempList.add(map.get(field));
            }
            dataList.add(tempList);
        }
        return dataList;
    }

    /**
     * 获取中文字体
     *
     * @return Font
     * @throws Exception ex
     */
    private static Font getPdfChineseFont() throws Exception {
        BaseFont bfChinese = BaseFont.createFont("STSongStd-Light", "UniGB-UCS2-H",
                BaseFont.NOT_EMBEDDED);
        return new Font(bfChinese, 12, Font.NORMAL);
    }
}
相关推荐
冯汉栩2 分钟前
Swift Control View,Label渐变颜色(源码)
开发语言·ios·swift
AI英德西牛仔5 分钟前
豆包导出 pdf 颜色不一样怎么办,选用 AI 导出鸭优化文档导出,结合行业白皮书数据解析色彩失真成因
人工智能·ai·chatgpt·pdf·deepseek·ai导出鸭
程序员黑豆23 分钟前
Java中的null与NullPointerException完全指南:安全处理、实战排查与面试题
java·前端·ai编程
晴天161 小时前
Electron面试题-Day19
java·javascript·electron
从小就看凹凸曼^o^1 小时前
Python基础5 - 字典与集合:(1)字典
开发语言·python
GeekZHR1 小时前
C语言指针进阶补充6:动态内存管理、mem系列内存函数、复杂指针声明,一次补齐指针的“三大盲区“
java·c语言·算法·指针
侧耳倾听1111 小时前
java 日志框架简介
java·开发语言
zlinear数据采集卡2 小时前
数据采集卡从入门到精通(9):分辨率与精度——16位卡不等于1/65536的精度
开发语言·数据库·fpga开发·开源·c#
她的男孩2 小时前
我用 LLM 把后台 CRUD 效率提升 10 倍:AI 代码生成器的架构与落地实践
java·后端·架构
全栈技术负责人2 小时前
解密 MCP(Model Context Protocol):大模型时代的“Type-C”总线与三驾马车架构深度解析
开发语言·ai·架构