今天接到个新需求,因为我们平台有Excel导出功能,但是商务在使用的时候提出来要增加打印功能,而Excel需要下载下来,然后还要在office软件中打开才能实现打印功能,比较麻烦,他们希望我们平台直接有的打印按钮,将这个Excel打印,众所周知,Excel不能直接打印须要转化成PDF或者其他支持打印的文件类型,浏览器太能提供打印。因此领导就给我交代这个打印功能补上,然后就甩给我几个参考方向:
解决方案
1.easypoi+itext;
2.xdoc xoffice
https://gitee.com/xdoc/xoffice.git;
3.Spire.XLS收费
4.lodop
其他几个后续再熟悉吧,今天就 itext 的应用来讲讲
在网上Google后发现 itext官方文档是英文版(博主英文菜😂)所以只能边翻译边看;
而且网上针对复杂的表格处理几乎讲解很少,看的最多的就是入门案例,下面我就讲iText写成通用方法的过程,方便以后使用。
先简单说说思路:
我们之前Excel导出功能是 List–>byte[](Excel文件字节流)
这里我们就套用这一思路
四个入参
- @param columns 属性
- @param titles 标题
- @param datas 数据集
- @param title pdf title
@param columns 属性 是定义的List<?> ?中的属性名称
@param titles 标题 是定义的List ?中的属性的中文名称 表格中用作表头
@param datas 数据集合 (这里需要注意博主没有测试大数据的集合适不适用)
@param title pdf title pdf文档标题
import com.google.common.collect.Lists;
import com.itextpdf.text.*;
import com.itextpdf.text.pdf.*;
import com.yyigou.ddc.common.exception.BusinessException;
import com.yyigou.ddc.services.ddc.smc.validate.ValidatorUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
/**
* 用于导出PDF格式工具类
* <p>
* <p>
* 注意:
* 1.List<?> datas 中定义的类型不许是String类型
*
* @author xc
* @date 2020/5/13
*/
@Slf4j
public class ItextPDFUtils {
//设置表格占比pdf 112% (112 刚好填满)
private static final float TABLE_PROPORTION = 112;
//使用iTextAsian.jar中的字体
private static final String FONT_NAME = "STSong-Light";
private static final String FONT_ENCODING = "UniGB-UCS2-H";
//********* 标题属性 ***********
private static final int TITLE_FONT_SIZE = 28;
private static final int TITLE_SPAC_AFTER = 50;
private static final int TITLE_SPAC_BEFORE = 50;
private static final String TITLE_DEFAULT_NAME = "title";
//********* 表头属性 ***********
private static final int HEADER_FONT_SIZE = 9;
//********* 表格内容属性 ***********
private static final int CONTENT_FONT_SIZE = 8;
/**
* List -> byte[]
*
* @param columns 属性
* @param titles 标题
* @param datas 数据集
* @param title pdf title
* @return
*/
public static byte[] listToPDF(String[] columns, String[] titles, List<?> datas, String title) {
validateParams(columns, titles, datas);
boolean validate = validate(datas);
if (!validate) {
return null;
}
try {
//构建字节输出流
ByteArrayOutputStream baos = new ByteArrayOutputStream();
//创建文档
Document document = new Document();
PdfWriter.getInstance(document, baos);
document.open();
int i = getClassSize(datas);
PdfPTable table = new PdfPTable(i);
table.setWidthPercentage(TABLE_PROPORTION);
//封装标题
resetTitle(document, title);
//封装表头
resetHeader(table, datas, titles);
//封装表格内容
resetContent(table, datas, columns);
//组成一行
table.completeRow();
//开启文档分页
table.setSplitLate(false);
table.setSplitRows(true);
//分页带上表头
table.setHeaderRows(1);
//把表格添加进文档
document.add(table);
document.close();
baos.close();
return baos.toByteArray();
} catch (Exception e) {
e.printStackTrace();
log.error("使用 ItextPDFUtils 工具类错误");
}
return null;
}
private static void validateParams(String[] columns, String[] titles, List<?> datas) {
if (columns.length != titles.length) {
throw new BusinessException(ErrorCode.NOT_MATCH_CODE, "请核对入参:columns titles");
}
if (EmptyUtils.isNotEmpty(datas)) {
Class<?> aClass = datas.get(0).getClass();
Field[] declaredFields = aClass.getDeclaredFields();
if (declaredFields.length == 0) {
throw new BusinessException(ErrorCode.NOT_MATCH_CODE, "结果集的属性为空,不合法");
}
}
}
/**
* 校验入参非空
*
* @param datas
*/
private static boolean validate(List<?> datas) {
if (EmptyUtils.isEmpty(datas)) {
return false;
}
int size = datas.size();
if (size > 0) {
return true;
}
return false;
}
/**
* 获取 类型属性数量
*
* @param datas
* @return
*/
private static int getClassSize(List<?> datas) {
if (EmptyUtils.isNotEmpty(datas)) {
Object obj = datas.get(0);
Class<?> aClass = obj.getClass();
Field[] declaredFields = aClass.getDeclaredFields();
return declaredFields.length;
}
return 0;
}
/**
* 获取表头各属性值
*
* @param datas
* @return
*/
private static List<String> getClassField(List<?> datas) {
List<String> fieldStrs = Lists.newArrayList();
if (EmptyUtils.isNotEmpty(datas)) {
Object obj = datas.get(0);
Class<?> aClass = obj.getClass();
Field[] declaredFields = aClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
String name = declaredField.getName();
fieldStrs.add(name);
}
}
return fieldStrs;
}
/**
* 获取字体
*
* @return
*/
private static Font getFont() throws IOException, DocumentException {
BaseFont baseFont = BaseFont.createFont(FONT_NAME, FONT_ENCODING, BaseFont.NOT_EMBEDDED);
Font font = new Font(baseFont);
return font;
}
/**
* 封装 标题
*
* @param document
* @param title
* @throws Exception
*/
private static void resetTitle(Document document, String title) throws Exception {
if (StringUtils.isEmpty(title)) {
title = TITLE_DEFAULT_NAME;
}
Font font = getFont();
//标题
font.setSize(TITLE_FONT_SIZE);
font.setStyle(Font.BOLD);
Paragraph p = new Paragraph(title, font);
//设置标题段落前后距离
p.setSpacingAfter(TITLE_SPAC_AFTER);
p.setSpacingBefore(TITLE_SPAC_BEFORE);
//设置标题对齐方式
p.setAlignment(Element.ALIGN_CENTER);
document.add(p);
}
/**
* 封装 表头
*
* @param table
* @param datas
* @param titles
*/
private static void resetHeader(PdfPTable table, List<?> datas, String... titles) throws IOException, DocumentException {
validateTitle(datas, titles);
int k = titles.length;
//格子(用来装表头的数据)
PdfPCell[] cells = new PdfPCell[k];
Font font = getFont();
//表头
for (int i = 0; i < titles.length; i++) {
font.setSize(HEADER_FONT_SIZE);
font.setStyle(Font.BOLD);
cells[i] = new PdfPCell(new Phrase(titles[i], font));
//设置表头颜色
cells[i].setBackgroundColor(new BaseColor(110, 213, 60));
//启用/禁用基于最大升序的第一行高度调整。
cells[i].setUseAscender(true);
//设置单元格的垂直对齐方式。
cells[i].setVerticalAlignment(Element.ALIGN_MIDDLE);
//设置单元格的水平对齐方式。
//cells[i].setHorizontalAlignment(Element.ALIGN_CENTER);
table.addCell(cells[i]);
}
}
/**
* 校验 内容与标题长度是否一致
*
* @param datas
* @param titles
*/
private static void validateTitle(List<?> datas, String[] titles) {
int size = getFiledName(datas.get(0)).length;
int length = titles.length;
if (size != length) {
throw new BusinessException(ErrorCode.MOBILE_FORMAT_ERR_CODE, "入参集合与String[] titles 长度不一致");
}
}
/**
* 封装内容
*
* @param table
* @param datas
* @param columns
*/
private static void resetContent(PdfPTable table, List<?> datas, String... columns) throws IOException, DocumentException {
Font font = getFont();
font.setSize(CONTENT_FONT_SIZE);
font.setStyle(Font.NORMAL);
for (Object obj : datas) {
//validateObjectType(obj);
Class<?> aClass = obj.getClass();
Field[] declaredFields = aClass.getDeclaredFields();
Phrase[] ps = new Phrase[columns.length];
PdfPCell[] pc = new PdfPCell[columns.length];
//每个类型的属性
for (int i = 0; i < columns.length; i++) {
String fieldName = getFiledNameByName(columns[i], declaredFields);
ValidatorUtils.checkEmptyThrowEx(fieldName, "入参columns 与属性值不匹配");
String fileValue = getFieldValueByName(fieldName, obj) + "";
//实例化
ps[i] = new Phrase(fileValue, font);
//把内容放进格子
pc[i] = new PdfPCell(ps[i]);
pc[i].setUseAscender(true);
//把格子放入表格中
table.addCell(pc[i]);
}
}
}
/**
* @param column
* @param declaredFields
* @return
*/
private static String getFiledNameByName(String column, Field... declaredFields) {
for (int i = 0; i < declaredFields.length; i++) {
String name = declaredFields[i].getName();
if (StringUtils.equals(name, column)) {
return column;
}
}
return null;
}
/**
* 校验 object 对象的属性和类型
* <p>
* 暂时只识别 String 类型
*
* @param obj
*/
private static void validateObjectType(Object obj) {
if (obj instanceof java.lang.String) {
//todo:
} else if (obj instanceof java.math.BigDecimal) {
} else {
String typeName = obj.getClass().getTypeName();
throw new BusinessException(ErrorCode.MESSAGE_SEND_EXCEPTION_CODE, "目前ItextPDSUtils只支持String,BigDecimal类型,不支持:" + typeName + "类型");
}
}
/**
* 根据属性名获取属性值
*/
private static Object getFieldValueByName(String fieldName, Object o) {
try {
String firstLetter = fieldName.substring(0, 1).toUpperCase();
String getter = "get" + firstLetter + fieldName.substring(1);
Method method = o.getClass().getMethod(getter, new Class[]{});
Object value = method.invoke(o, new Object[]{});
return value;
} catch (Exception e) {
log.error(e.getMessage(), e);
return null;
}
}
/**
* 获取属性名数组
*/
private static String[] getFiledName(Object o) {
Field[] fields = o.getClass().getDeclaredFields();
String[] fieldNames = new String[fields.length];
for (int i = 0; i < fields.length; i++) {
System.out.println(fields[i].getType());
fieldNames[i] = fields[i].getName();
}
return fieldNames;
}
}
真的low