java生成PDF合并单元格
需求
最近需要开发一个功能,涉及到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);//设置A4document.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);}
}