需求
最近需要开发一个功能,涉及到Excel导出与pdf导出,其实pdf导出不适合表头太多的表格,不美观,但是需求如此,那就开发吧。
技术选型
参考了网上的资料,选用itextpdf做为技术支持,开始itextpdf-core对应的依赖,但是导出中文会有格式问题,并且乱码,需要引入本地的字体。
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext7-core</artifactId>
<version>7.1.16</version>
<type>pom</type>
</dependency>
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;
}
}
问题
上述实现,对于中文有一定问题,还无法合并单元格,所以换了其它依赖
<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>
实现方式
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);
}
}