import com.itextpdf.text.Font;
import com.itextpdf.text.Image;
import com.itextpdf.text.Rectangle;
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 org.apache.commons.lang3.StringUtils;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartUtils;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.labels.ItemLabelAnchor;
import org.jfree.chart.labels.ItemLabelPosition;
import org.jfree.chart.labels.StandardCategoryItemLabelGenerator;
import org.jfree.chart.labels.StandardPieSectionLabelGenerator;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PiePlot3D;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.category.BarRenderer;
import org.jfree.chart.renderer.category.LineAndShapeRenderer;
import org.jfree.chart.title.TextTitle;
import org.jfree.chart.ui.TextAnchor;
import org.jfree.data.category.CategoryDataset;
import org.jfree.data.general.PieDataset;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.*;
/**
* @author X-MD
* @version 1.0.0
* @Description PDF导出工具类
* @createTime 2022年01月19日 16:47:00
*/
public class PdfExportUtil {
private static String templatePath = PdfExportUtil.class.getResource("/").getPath() + "/templates/";
private static final String FONTNAME = "微软雅黑";
private static final String FONTCONT = "宋体";
/**
* 需要导出的字段名称
*/
private static List<String> fieldNameList;
/**
* 需要导出的字段
*/
private static List<String> fieldList;
/**
* 需要增加的列
*/
private static int relativeWidths;
/**
* 字段所占列
*/
private static Map<String, Integer> colspanMap;
/**
* 字段所占行
*/
private static Map<String, Integer> rowspanMap;
private static List<Element> elementList = new ArrayList<>();
private static List<Element> newPageList = new ArrayList<>();
/**
* 纯文字
* @param content 文字内容
* @return
*/
public static void createParagraph(String content) {
createParagraph(content, 12, Font.NORMAL, BaseColor.BLACK, Element.ALIGN_LEFT);
}
/**
* 纯文字
* @param content 文字内容
* @param size 文字大小
* @param style 格式
* @param color 颜色
* @param alignment 位置
* @return
*/
public static void createParagraph(String content, float size, int style, BaseColor color, int alignment) {
try {
//设置中文样式(不设置,中文将不会显示)
BaseFont bfChinese = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
Font fontChineseContent = new Font(bfChinese, size,style, color);
Paragraph pieParagraph = new Paragraph(content, fontChineseContent);
pieParagraph.setAlignment(alignment);
elementList.add(pieParagraph);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 添加图片
* @param filename 文件路径
*/
public static void createImage(String filename) {
createImage(filename,Element.ALIGN_CENTER,400F, 200F);
}
/**
* 添加图片
* @param filename 文件路径
* @param alignment 位置 (Element 接口)
* ALIGN_CENTER = 1
* ALIGN_LEFT = 0
* ALIGN_RIGHT = 2
* @param newWidth 宽度
* @param newHeight 高度
*/
public static void createImage(String filename,int alignment,float newWidth, float newHeight) {
try {
Image image = Image.getInstance(filename);
image.setAlignment(alignment);
image.scaleAbsolute(newWidth, newHeight);
//依照比例缩放
//image.scalePercent(40);
elementList.add(image);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 导出表格
* @param dataSet 数据
* @param pojoClass 数据对应的类
* @return
*/
public static void createPdfPTable(List<?> dataSet, Class<?> pojoClass) {
createPdfPTable(dataSet,pojoClass, 15, Font.BOLD, BaseColor.BLUE, Font.DEFAULTSIZE, Font.NORMAL, BaseColor.BLACK);
}
/**
* 导出表格
* @param dataSet
* @param pojoClass
* @param titleSize 标题字体大小
* @param titleStyle 标题字体格式
* @param titleColor 标题字体颜色
* @param contentSize 内容字体大小
* @param contentStyle 内容字体格式
* @param contentColor 内容字体颜色
* @return
*/
public static void createPdfPTable(List<?> dataSet, Class<?> pojoClass, int titleSize, int titleStyle, BaseColor titleColor, int contentSize, int contentStyle, BaseColor contentColor) {
try {
fieldNameList = new ArrayList<>();
fieldList = new ArrayList<>();
relativeWidths = 0;
colspanMap = new HashMap<>();
rowspanMap = new HashMap<>();
filedTitle(pojoClass);
PdfPCell cell;
//标题字体设置
Font bigFont = createFont(titleSize, titleStyle, titleColor);
PdfPTable pdfPTable = createPdfPTable(fieldList.size() + relativeWidths);
pdfPTable.setWidthPercentage(100.0F);
//先设置需要导出的字段名称
for (String header : fieldNameList) {
cell = new PdfPCell(new Phrase(header, bigFont));
cell.setFixedHeight(60);
cell.setUseAscender(true);
if (colspanMap.containsKey(header)) {
Integer integer = colspanMap.get(header);
cell.setColspan(integer);
}
cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
//水平居中
cell.setHorizontalAlignment(Element.ALIGN_CENTER);
pdfPTable.addCell(cell);
}
//设置具体值
//具体值得字体设置
bigFont = createFont(contentSize, contentStyle, contentColor);
for (Object o : dataSet) {
for (String pama : fieldList) {
String afterData = getFieldValueByFieldName(pama, o);
//产品编号
cell = new PdfPCell(new Phrase(afterData, bigFont));
if (colspanMap.containsKey(pama)) {
Integer integer = colspanMap.get(pama);
cell.setColspan(integer);
}
if (rowspanMap.containsKey(pama)) {
Integer integer = rowspanMap.get(pama);
cell.setRowspan(integer);
cell.setRowspan(integer);
}
cellSet(cell);
pdfPTable.addCell(cell);
}
}
elementList.add(pdfPTable);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 创建饼状图
* @param dataset 数据
* @param title 标题
* @return String 图片地址
*/
public static String createPieChart3D(PieDataset dataset,String title) {
return createPieChart3D(dataset, title, 12, 12, 20);
}
/**
* 创建饼状图
* @param dataset 数据
* @param title 标题
* @param labelSize Label字体大小
* @param legendSize Legend字体大小
* @param textTitleSize 图标字体大小
* @return String 图片地址
* @return
*/
public static String createPieChart3D(PieDataset dataset,String title,int labelSize,int legendSize,int textTitleSize) {
String strUrl = templatePath + "pieChart3D.jpg";
try (
FileOutputStream fosJpg = new FileOutputStream(strUrl)
) {
JFreeChart chart = ChartFactory.createPieChart3D(title, dataset, true, true, false);
PiePlot3D plot = (PiePlot3D) chart.getPlot();
//设置Label字体
plot.setLabelFont(new java.awt.Font(FONTNAME, java.awt.Font.PLAIN, labelSize));
//设置legend字体
chart.getLegend().setItemFont(new java.awt.Font(FONTNAME, java.awt.Font.PLAIN, legendSize));
// 图片中显示百分比:默认方式
//plot.setLabelGenerator(new StandardPieSectionLabelGenerator(StandardPieToolTipGenerator.DEFAULT_TOOLTIP_FORMAT));
// 图片中显示百分比:自定义方式,{0} 表示选项, {1} 表示数值, {2} 表示所占比例 ,小数点后两位
plot.setLabelGenerator(new StandardPieSectionLabelGenerator("{0}={1}({2})", NumberFormat.getNumberInstance(), new DecimalFormat("0.00%")));
// 图例显示百分比:自定义方式, {0} 表示选项, {1} 表示数值, {2} 表示所占比例
plot.setLegendLabelGenerator(new StandardPieSectionLabelGenerator("{0}={1}({2})"));
// 设置背景色为白色
chart.setBackgroundPaint(Color.white);
// 指定图片的透明度(0.0-1.0)
plot.setForegroundAlpha(1.0f);
// 指定显示的饼图上圆形(false)还椭圆形(true)
plot.setCircular(true);
// 设置图标题的字体
java.awt.Font font = new java.awt.Font(FONTCONT, java.awt.Font.CENTER_BASELINE, textTitleSize);
TextTitle textTitle = new TextTitle(title);
textTitle.setFont(font);
chart.setTitle(textTitle);
ChartUtils.writeChartAsJPEG(fosJpg, 0.7f, chart, 800, 1000, null);
// createImage(strUrl,alignment,newWidth,newHeight);
return strUrl;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 折线图
* @param lineDataset 数据
* @param title 标题
* @param categoryAxisLabel
* @param valueAxisLabel
* @return Document
*/
public static String createLineChart(CategoryDataset lineDataset,String title,String categoryAxisLabel,String valueAxisLabel) {
return createLineChart(lineDataset, title, categoryAxisLabel, valueAxisLabel, 12, 15, false);
}
/**
* 折线图
* @param lineDataset 数据
* @param title 标题
* @param categoryAxisLabel
* @param valueAxisLabel
* @param legendSize
* @param titleSize
* @param bool 拐点是否显式小图标
* @return Document
*/
public static String createLineChart(CategoryDataset lineDataset, String title, String categoryAxisLabel,
String valueAxisLabel, int legendSize, int titleSize,boolean bool) {
String strUrl = templatePath + "lineChart.jpg";
try (
FileOutputStream fosJpg = new FileOutputStream(strUrl)
) {
JFreeChart lineChart = ChartFactory.createLineChart(title, categoryAxisLabel, valueAxisLabel, lineDataset, PlotOrientation.VERTICAL, true, true, true);
lineChart.getLegend().setItemFont(new java.awt.Font(FONTCONT, java.awt.Font.PLAIN, legendSize));
lineChart.getTitle().setFont(new java.awt.Font(FONTCONT, java.awt.Font.PLAIN, titleSize));
//获取绘图区对象
CategoryPlot linePlot = lineChart.getCategoryPlot();
//设置绘图区域背景的 alpha 透明度 ,在 0.0f 到 1.0f 的范围内
linePlot.setBackgroundAlpha(0.0f);
//区域背景色
//linePlot.setBackgroundPaint(Color.white);
//背景底部横虚线
linePlot.setRangeGridlinePaint(Color.gray);
//linePlot.setOutlinePaint(Color.RED);//边界线
// 设置水平方向背景线颜色
// 设置是否显示水平方向背景线,默认值为true
linePlot.setRangeGridlinesVisible(true);
// 设置垂直方向背景线颜色
linePlot.setDomainGridlinePaint(Color.gray);
// 设置是否显示垂直方向背景线,默认值为false
linePlot.setDomainGridlinesVisible(true);
//获取坐标轴对象
CategoryAxis lineAxis = linePlot.getDomainAxis();
//设置坐标轴字体
lineAxis.setLabelFont(new java.awt.Font(FONTCONT, java.awt.Font.PLAIN, 12));
//设置坐标轴标尺值字体(x轴)
lineAxis.setTickLabelFont(new java.awt.Font(FONTCONT, java.awt.Font.PLAIN, 12));
//获取数据轴对象(y轴)
ValueAxis rangeAxis = linePlot.getRangeAxis();
rangeAxis.setLabelFont(new java.awt.Font(FONTCONT, java.awt.Font.PLAIN, 12));
//将折线设置为虚线
LineAndShapeRenderer renderer = (LineAndShapeRenderer) linePlot.getRenderer();
// 设置显示小图标
renderer.setDefaultShapesVisible(bool);
//利用虚线绘制
// float[] dashes = {5.0f};
// BasicStroke brokenLine = new BasicStroke(2.2f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 8f, dashes, 0.6f);
// renderer.setSeriesStroke(0, brokenLine);
//折点显式数值
// DecimalFormat decimalformat1 = new DecimalFormat("##"); // 数据点显示数据值的格式
// renderer.setDefaultItemLabelGenerator(new StandardCategoryItemLabelGenerator("{2}", decimalformat1));
// renderer.setDefaultItemLabelsVisible(true); // 设置项标签显示
// renderer.setDefaultItemLabelsVisible(true); // 基本项标签显示
ChartUtils.writeChartAsJPEG(fosJpg, 0.7f, lineChart, 600, 300);
return strUrl;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 柱状图
* @param title
* @param categoryAxisLabel
* @param valueAxisLabel
* @param dataSet 数据
* @return
*/
public static String createBarChart(String title, String categoryAxisLabel, String valueAxisLabel, CategoryDataset dataSet) {
String strUrl = templatePath + "barChart.jpg";
try (
FileOutputStream fos = new FileOutputStream(strUrl)
) {
JFreeChart jfreechart = ChartFactory.createBarChart(title, categoryAxisLabel, valueAxisLabel, dataSet);
CategoryPlot categoryPlot = jfreechart.getCategoryPlot();
categoryPlot.setBackgroundAlpha(0.0f);
categoryPlot.setRangeGridlinePaint(Color.gray);
categoryPlot.setRangeGridlinesVisible(true);
// 设置网格beijingse
categoryPlot.setBackgroundPaint(Color.WHITE);
// 设置网格竖线颜色
categoryPlot.setDomainGridlinePaint(Color.pink);
// 设置网格横线颜色
categoryPlot.setRangeGridlinePaint(Color.pink);
// 显示每个柱的数值,并修改该数值的字体属性
BarRenderer renderer2 = new BarRenderer();
renderer2.setDefaultItemLabelGenerator(new StandardCategoryItemLabelGenerator());
renderer2.setDefaultItemLabelsVisible(true);
renderer2.setDefaultPositiveItemLabelPosition(new ItemLabelPosition(ItemLabelAnchor.INSIDE8, TextAnchor.BASELINE_CENTER));
renderer2.setItemLabelAnchorOffset(10D);
// 设置平行柱的之间距离
renderer2.setItemMargin(0.2);
categoryPlot.setRenderer(renderer2);
ChartUtils.writeChartAsJPEG(fos, 0.7f, jfreechart, 600, 300);
return strUrl;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 分页
*/
public static void newPage(){
Element element = elementList.get(elementList.size() - 1);
newPageList.add(element);
}
/**
* PDF预览
* @param response
* @param filename 文件名称
* @param rect PDF每张页面大小
* @param backgroundColor 背景颜色
* @param marginLeft 左页边距
* @param marginRight 右页边距
* @param marginTop 顶部页边距
* @param marginBottom 底部页面距
* @throws Exception
*/
public static void pdfExportPreview(HttpServletResponse response, String filename,Rectangle rect,BaseColor backgroundColor,
float marginLeft, float marginRight, float marginTop, float marginBottom) throws Exception {
response.setContentType("application/pdf");
pdfExport(response, filename, rect, backgroundColor, marginLeft, marginRight, marginTop, marginBottom);
}
/**
* 预览PDF
* @param response
* @param filename 文件名
* @throws Exception
*/
public static void pdfExportPreview(HttpServletResponse response, String filename) throws Exception {
response.setContentType("application/pdf");
pdfExport(response, filename, PageSize.A4,BaseColor.WHITE,20, 20, 20, 20);
}
/**
* PDF导出
* @param response
* @param filename 文件名称
* @param rect PDF每张页面大小
* @param backgroundColor 背景颜色
* @param marginLeft 左页边距
* @param marginRight 右页边距
* @param marginTop 顶部页边距
* @param marginBottom 底部页面距
* @throws Exception
*/
public static void pdfExportDownload(HttpServletResponse response, String filename,Rectangle rect,BaseColor backgroundColor,
float marginLeft, float marginRight, float marginTop, float marginBottom) throws Exception {
response.setContentType("application/x-download");
pdfExport(response, filename, rect, backgroundColor, marginLeft, marginRight, marginTop, marginBottom);
}
/**
* 下载PDF
* @param response
* @param filename 文件名
* @throws Exception
*/
public static void pdfExportDownload(HttpServletResponse response, String filename) throws Exception {
response.setContentType("application/x-download");
pdfExport(response, filename, PageSize.A4,BaseColor.WHITE,20, 20, 20, 20);
}
private static void pdfExport(HttpServletResponse response,String filename,Rectangle rect,BaseColor backgroundColor,
float marginLeft, float marginRight, float marginTop, float marginBottom) throws IOException {
Document document = null;
OutputStream os = null;
try {
response.addHeader("Content-Disposition", "inline;filename=" + URLEncoder.encode(filename, "UTF-8") + ".pdf");
document =createDocument(rect,backgroundColor,marginLeft, marginRight, marginTop, marginBottom);
os = new BufferedOutputStream(response.getOutputStream());
// 2. 获取writer
PdfWriter.getInstance(document, os);
// 3. open()
document.open();
for (Element element : elementList) {
document.add(element);
if (newPageList.contains(element)) {
document.newPage();
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
elementList.clear();
newPageList.clear();
if (document != null){
document.close();
}
if (os != null){
os.close();
}
}
}
/**
* 创建 Document
* @param marginLeft
* @param marginRight
* @param marginTop
* @param marginBottom
* @return
*/
public static Document createDocument(Rectangle rect, BaseColor backgroundColor, float marginLeft, float marginRight, float marginTop, float marginBottom) {
//生成pdf
Document document = new Document();
// 页面大小
Rectangle rectangle = new Rectangle(rect);
// 页面背景颜色
rectangle.setBackgroundColor(backgroundColor);
document.setPageSize(rectangle);
// 页边距 左,右,上,下
document.setMargins(marginLeft, marginRight, marginTop, marginBottom);
return document;
}
/**
* @param len 表格列数
* @return
*/
public static PdfPTable createPdfPTable(int len) {
PdfPTable pdfPTable = new PdfPTable(len);
pdfPTable.setSpacingBefore(5);
pdfPTable.setWidthPercentage(100.0F);
pdfPTable.setHorizontalAlignment(Element.ALIGN_CENTER);
return pdfPTable;
}
/**
* 创建字体
* @param size
* @param style
* @param fontColor
* @return
*/
public static Font createFont(int size, int style, BaseColor fontColor) {
//中文字体 ----不然中文会乱码
BaseFont bf = null;
try {
bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
return new Font(bf, size, style, fontColor);
} catch (Exception e) {
e.printStackTrace();
}
return new Font(bf, Font.DEFAULTSIZE, Font.NORMAL, BaseColor.BLACK);
}
/**
* 设置PdfPCell
* @param cell
*/
private static void cellSet(PdfPCell cell) {
cell.setFixedHeight(40);
cell.setUseAscender(true);
cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
//水平居中
cell.setHorizontalAlignment(Element.ALIGN_CENTER);
}
/**
* 根据属性名获取属性值
*
* @param fieldName
* @param object
* @return
*/
private static String getFieldValueByFieldName(String fieldName, Object object) {
try {
Field field = object.getClass().getDeclaredField(fieldName);
//设置对象的访问权限,保证对private的属性的访问
field.setAccessible(true);
Object ostr = field.get(object);
String value = (String) ostr;
PdfField excel = field.getAnnotation(PdfField.class);
String[] replace = excel.replace();
if (replace != null && replace.length > 0) {
value = (String) replaceValue(replace, (String) ostr);
} else if (StringUtils.isNotBlank(excel.format())) {
SimpleDateFormat formatDate = new SimpleDateFormat(excel.format());
Date parse = formatDate.parse(String.valueOf(ostr));
value = formatDate.format(parse);
}
return value;
} catch (Exception e) {
return null;
}
}
private static Object replaceValue(String[] replace, String value) {
String[] temp;
for (String str : replace) {
temp = str.split("_");
if (value.equals(temp[1])) {
value = temp[0];
break;
}
}
return value;
}
/**
* 获取所有字段名称
*
* @param pojoClass
* @return
*/
private static void filedTitle(Class<?> pojoClass) {
Field[] fileds = getClassFields(pojoClass);
for (Field filed : fileds) {
if (filed.getAnnotation(PdfField.class) != null) {
PdfField excel = filed.getAnnotation(PdfField.class);
fieldNameList.add(excel.name());
fieldList.add(filed.getName());
if (excel.colspan() > 0) {
relativeWidths += excel.colspan();
relativeWidths--;
colspanMap.put(excel.name(), excel.colspan());
colspanMap.put(filed.getName(), excel.colspan());
}
if (excel.rowspan() > 0) {
colspanMap.put(filed.getName(), excel.rowspan());
}
}
}
}
/**
* 根据class获取类字段
*
* @param clazz
* @return
*/
public static Field[] getClassFields(Class<?> clazz) {
List<Field> list = new ArrayList<>();
Field[] fields;
do {
fields = clazz.getDeclaredFields();
list.addAll(Arrays.asList(fields));
clazz = clazz.getSuperclass();
} while (clazz != Object.class && clazz != null);
return list.toArray(fields);
}
}