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 小时前
C语言 — 通讯录模拟实现
c语言·开发语言·算法
编程乐学(Arfan开发工程师)3 小时前
56、原生组件注入-原生注解与Spring方式注入
java·前端·后端·spring·tensorflow·bug·lua
♚卜卦3 小时前
面向对象 设计模式简述(1.创建型模式)
开发语言·设计模式
安全系统学习4 小时前
网络安全之RCE简单分析
开发语言·python·算法·安全·web安全
周某某~4 小时前
七.适配器模式
java·设计模式·适配器模式
Swift社区4 小时前
Swift 解法详解:如何在二叉树中寻找最长连续序列
开发语言·ios·swift
yutian06065 小时前
C# 支持 ToolTip 功能的控件,鼠标悬停弹提示框
开发语言·microsoft·c#
byte轻骑兵5 小时前
【C++特殊工具与技术】优化内存分配(四):定位new表达式、类特定的new、delete表达式
开发语言·c++
奔跑的小十一5 小时前
JDBC接口开发指南
java·数据库
刘大猫.5 小时前
业务:资产管理功能
java·资产管理·资产·资产统计·fau·bpb·mcb