【生成PDF】Java如何根据前台Echarts图表生成PDF,并下载_小小小鱼生的博客-CSDN博客_echart导出pdf
原帖子 填充数据有问题,无法填充,注意!注意!注意!!!!!
现已修改完成
导入pom坐标
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.13.3</version>
</dependency>
前端获取echart图片方式
var myChart1;
//加载实时调用
function loadChart1(res)
{
var colorList = ['#73DDFF', '#73ACFF'];
myChart1 = echarts.init(document.getElementById('chart-visitTrend'));
option = {
/* title: {
text: '审计调用统计'
}, */
tooltip: {
trigger: 'axis'
},
legend: {
data: ['接收', '发送']
},
grid: {
top:'5%',
left: '1%',
right: '3%',
bottom: '0%',
containLabel: true
},
/* toolbox: { //保存图片
show: true,
orient: "vertical",
itemSize: 12,
feature: {
saveAsImage: {
name:"实时调用下载"
}
}
}, */
xAxis: {
type: 'category',
boundaryGap: false,
//data: ['2022-01-01', '2022-01-02', '2022-01-03', '2022-01-04', '2022-01-05', '2022-01-06', '2022-01-07']
data:res.timeData
},
yAxis: {
type: 'value',
axisLabel: {
formatter: '{value} 次'
}
},
series: [
{
//name: '',
type: 'line',
stack: 'Total',
color: colorList[0],
//data: [120, 132, 101, 134, 90, 230, 210]
data:res.countData
}
]
};
// 使用刚指定的配置项和数据显示图表。
myChart1.clear();
myChart1.setOption(option);
}
// 导出
doExport = function()
{
//获取echarts图的base64编码,为png格式。
var picBase64Info = myChart1.getDataURL();
var con = getCondition();//其它条件
con['picBase64Info'] = picBase64Info;
var form =document.createElement("form");
form.style.display='none';
form.action ='/reportform/exportform';
form.method = 'post';
document.body.append(form);
for(var key in con)
{
var input = document.createElement("input");
input.type = "hidden";
input.name = key;
input.value = con[key];
form.target = "exportFrame";
form.appendChild(input);
}
form.submit();
form.remove();
// 以下通过ajax请求方式行不通,无法触发浏览器的下载动作
/*
$.ajax({
url: '/reportform/exportform',
type: "post",
async: true,
cache: false,
data: JSON.stringify(con),
contentType : "application/json",
dataType: "json",
error: function(xhr)
{
// 操作失败
}
}); */
}
picBase64Info 就是加密的数据
后端接口数据即可。
到控制层操作
public void generatePdf(HttpServletResponse response, String picBase64Info) {
// 1.创建文档,设置文档页面大小,页边距
Document document =
new Document(
PageSize.A4,
Float.parseFloat("0"),
Float.parseFloat("10"),
Float.parseFloat("44"),
Float.parseFloat("50"));
// 2.解析前台传来的echart图,生成pdf段落元素
Paragraph pictureEle =
PDFUtil.createImageFromEncodeBase64(
picBase64Info,
"结果",
Float.parseFloat("60"),
Float.parseFloat("80"),
false);
// 生成table表格段落
// 获取所有的list数据,实体对应下面list<String>的实体对象
List<BasicAnalysis> basicAnalyses = details("1579758070935814145").getBasicAnalyses();
// 对应实体的属性名 方便后续的反射取值
ArrayList<String> list = new ArrayList<>();
list.add("flowcell");
list.add("sampleName");
list.add("sampleCode");
list.add("totalBase");
list.add("onTargetGt1000");
list.add("conversionRatio");
list.add("qcConclusion");
list.add("ucas");
Paragraph table1 =
PDFUtil.createTable(
basicAnalyses,
"报告",
// 表格标题列数 与list<String> 数量对应
new String[] {
"名称", "姓名", "编号", "数据量", "靶", "转", "s", "评分"
},
list);
//主页标头
Paragraph elements = new Paragraph("123123",PDFUtil.getCoverFontFont());
// 将所有需要放到文档里的元素,汇总
List<Paragraph> paragraphs = new ArrayList<>();
paragraphs.add(elements);
paragraphs.add(pictureEle);
paragraphs.add(table1);
// paragraphs.add(table3);
// 导出pdf文档:将paragraphs塞到document中,并下载document
PDFUtil.exportDocument(document, null, paragraphs, response, "报表");
}
package com.euler.eucas.util;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import com.euler.eucas.analysis.model.BasicAnalysis;
import com.euler.eucas.analysis.mybatis.entity.QualityControlEntity;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Chunk;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.Font;
import com.itextpdf.text.Image;
import com.itextpdf.text.Paragraph;
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.springframework.beans.BeanUtils;
/**
* @since JDK 1.8
*/
public class PDFUtil {
private static final Logger logger = LogManager.getLogger(PDFUtil.class);
/** fontSize_normal : (正文字体大小11号). */
public static final float FONTSIZE_NORMAL = Integer.parseInt("11");
/** fontSize_titile : (标题字体大小14号). */
public static final float FONTSIZE_TITILE = Integer.parseInt("14");
/** FONTSIZE_COVER : (封面字体大小32号). */
public static final float FONTSIZE_COVER = Integer.parseInt("19");
/** normalFont : (通用字体样式:宋体、11号). */
private static Font normalFont = null;
/** titleFont : (通用标题字体样式:宋体、14号、加粗). */
private static Font titleFont = null;
/** coverFont : (通用封面字体样式:宋体、28号、加粗). */
private static Font coverFont = null;
/**
* getBaseFont : (获取可以解析中文的字体:使用宋体). <br>
*
* @author
* @return
* @since JDK 1.8
*/
public static BaseFont getBaseFontChinese() {
try {
// // 宋体资源文件路径,可以从C://Windows//Fonts//simsun.ttc拷贝到相应目录下 TODO 字体需要拷贝一份
// URL path = PDFUtil.class.getResource("/config/fonts/simsun.ttc");
// return BaseFont.createFont(path + ",0", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
// 本地测试:使用windows自带的宋体文件
return BaseFont.createFont("C://Windows//Fonts//simsun.ttc,0", BaseFont.IDENTITY_H,
false);
} catch (Exception e) {
logger.error("设置字体样式失败", e);
return null;
}
}
/**
* getNormalFont : (获取普通字体样式). <br>
*
* @author
* @return
* @since JDK 1.8
*/
public static Font getNormalFont() {
if (normalFont == null) {
BaseFont bfChinese = getBaseFontChinese();
normalFont = new Font(bfChinese, FONTSIZE_NORMAL, Font.NORMAL);
}
return normalFont;
}
/**
* getTitleFont : (获取标题通用字体). <br>
*
* @author
* @return
* @since JDK 1.8
*/
public static Font getTitleFont() {
if (titleFont == null) {
BaseFont bfChinese = getBaseFontChinese();
titleFont = new Font(bfChinese, FONTSIZE_TITILE, Font.BOLD);
}
return titleFont;
}
/**
* getTitleFont : (获取封面通用字体). <br>
*
* @author
* @return
* @since JDK 1.8
*/
public static Font getCoverFontFont() {
if (coverFont == null) {
BaseFont bfChinese = getBaseFontChinese();
coverFont = new Font(bfChinese, FONTSIZE_COVER, Font.BOLD);
}
return coverFont;
}
/**
* getfieldValue : (通过反射,根据方法名,执行方法,最终返回行结果的toString值). <br>
*
* @author
* @param <T> 方法执行的入参
* @param object 对象
* @param methodName 方法名
* @param args 方法执行参数
* @since JDK 1.8
*/
private static <T> String getfieldValue(T object, String methodName, Object... args) {
try {
Field declaredField = object.getClass().getDeclaredField(methodName);
declaredField.setAccessible(true);
Object value = declaredField.get(object);
return value == null ? "" : value.toString();
} catch (Exception e) {
logger.error("getfieldValue error", e);
return "";
}
}
/**
* analysisPicBase64Info : (解析base64图片信息). <br>
*
* @author
* @param picBase64Info 图片base64信息,或前台echart通过调用getDataURL()方法获取的图片信息
* @return 图片经过base64解码后的信息
* @since JDK 1.8
*/
public static Element analysisPicBase64Info(String picBase64Info) {
if (StringUtils.isEmpty(picBase64Info)) {
// 空段落
return new Paragraph();
}
// 1.获取图片base64字符串信息:若入参是通过前台echarts调用getDataURL()方法获取的,则该字符串包含逗号,且则逗号后面的内容才是图片的信息
String pictureInfoStr =
picBase64Info.indexOf(",") == -1 ? picBase64Info : picBase64Info.split(",")[1];
// 2.将图片信息进行base64解密
byte[] imgByte = Base64.decodeBase64(pictureInfoStr);
// 对异常的数据进行处理
/**
* .图片的原始表达ascii码范围是0-255, .这里面有一些不可见的编码。然后为了图片正确传输才转成编码base64的0-63,
* .当从base64转成byte时,byte的范围是[-128,127],那么此时就会可能产生负数,而负数不是在ascii的范围里,所以需要转换一下
*/
for (int i = 0; i < imgByte.length; i++) {
if (imgByte[i] < 0) {
imgByte[i] += 256;
}
}
try {
return Image.getInstance(imgByte);
} catch (Exception e) {
logger.error("analysisPicBase64Info error", e);
return new Paragraph();
}
}
/**
* analysisPicBase64Info_batch : (批量解析base64加密的图片信息,生成Image对象). <br>
*
* @author
* @param picBase64Infos 经过base64加密的图片信息
* @return
* @since JDK 1.8
*/
public static List<Element> analysisPicBase64Info_batch(List<String> picBase64Infos) {
List<Element> images = new ArrayList<Element>();
for (String li : picBase64Infos) {
Element image = analysisPicBase64Info(li);
images.add(image);
}
return images;
}
/**
* createImage : (根据图片的base64加密文件创建pdf图片). <br>
*
* @author
* @param picBase64Info base64加密后的图片信息(支持台echart通过调用getDataURL()方法获取的图片信息)
* @param title 段落标题
* @param percentX 图片缩放比例X轴
* @param percentY 图片缩放比例Y轴
* @param titleCenter 标题是否居中,true-居中、false-默认居左
* @return 返回图片段落
* @since JDK 1.8
*/
public static Paragraph createImageFromEncodeBase64(
String picBase64Info,
String title,
float percentX,
float percentY,
boolean titleCenter) {
// 1.获取图片
Element element = analysisPicBase64Info(picBase64Info);
// 2.创建段落,并添加标题
Paragraph paragraph = new Paragraph(title, getTitleFont());
// 空行
paragraph.add(Chunk.NEWLINE);
paragraph.add(Chunk.NEWLINE);
if (titleCenter) {
// 居中
paragraph.setAlignment(Element.ALIGN_CENTER);
} else {
// 居左
setDefaultIndentationLeft(paragraph);
}
if (!(element instanceof Image)) {
// 图片解析失败
return paragraph;
}
Image image = (Image) element;
// 3.设置图片缩放比例
//image.scalePercent(percentX, percentY);
// image.scaleToFit(800,3000);
image.scaleAbsolute(percentX,percentY);
// 4.图片放入该段落
paragraph.add(image);
return paragraph;
}
/**
* createImageFromEncodeBase64_batch : (批量创建). <br>
*
* @author
* @param picBase64Infos 图片base64加密后的信息(支持台echart通过调用getDataURL()方法获取的图片信息)
* @param titles 段落标题
* @param percentXs X轴缩放比例
* @param percentYs Y轴缩放比例
* @param titleCenter 标题是否居中
* @return
* @since JDK 1.8
*/
public static Paragraph createImageFromEncodeBase64_batch(
List<String> picBase64Infos,
List<String> titles,
List<Float> percentXs,
List<Float> percentYs,
boolean titleCenter) {
Paragraph paragraphs = new Paragraph();
for (int i = 0; i <= picBase64Infos.size(); i++) {
Paragraph imagePara =
createImageFromEncodeBase64(
picBase64Infos.get(i),
titles.get(i),
percentXs.get(i),
percentYs.get(i),
titleCenter);
if (!imagePara.isEmpty()) {
paragraphs.add(imagePara);
// 空行
paragraphs.add(Chunk.NEWLINE);
paragraphs.add(Chunk.NEWLINE);
}
}
return paragraphs;
}
/**
* createTable : (创建table段落). <br>
*
* @author
* @param <T>
* @param list 构建table的数据
* @param title 该段落取的名字
* @param methodNames 需要调用的方法名,用来获取单元格数据。通常是某个属性的get方法
* @return
* @since JDK 1.8
*/
public static <T> Paragraph createTable(
List<T> list, String title, String[] tableHead, List<String> methodNames) {
return createTable(
list, FONTSIZE_NORMAL, FONTSIZE_TITILE, title, tableHead, methodNames, false);
}
/**
* createTable : (创建table段落). <br>
*
* @author
* @param <T>
* @param list 构建table的数据
* @param title 该段落取的名字
* @param methodNames 需要调用的方法名,用来获取单元格数据。通常是某个属性的get方法
* @param titleCenter 标题是否居中:true-居中,false-居左
* @return
* @since JDK 1.8
*/
public static <T> Paragraph createTable(
List<T> list,
String title,
String[] tableHead,
List<String> methodNames,
boolean titleCenter) {
return createTable(
list, FONTSIZE_NORMAL, FONTSIZE_TITILE, title, tableHead, methodNames, titleCenter);
}
/**
* createTableByList : (创建table段落). <br>
*
* @author
* @param <T>
* @param listData
* @param normalFontSize 正文字体大小
* @param titleFontSize 标题字体大小
* @param title 段落名称
* @param methodNames 获取表格属性的方法名
* @param titleCenter 段落标题是否水平居中,true:需要居中;false:默认靠左
* @return
* @since JDK 1.8
*/
public static <T> Paragraph createTable(
List<T> listData,
float normalFontSize,
float titleFontSize,
String title,
String[] tableHead,
List<String> methodNames,
boolean titleCenter) {
// 1.创建一个段落
Paragraph paragraph = new Paragraph(title, getTitleFont());
// 空行
paragraph.add(Chunk.NEWLINE);
paragraph.add(Chunk.NEWLINE);
if (titleCenter) {
// 居中
paragraph.setAlignment(Element.ALIGN_CENTER);
} else {
// 居左
setDefaultIndentationLeft(paragraph);
}
// 3.创建一个表格
PdfPTable table = new PdfPTable(tableHead.length); // 列数
try {
// 修改列宽。需要和列数对应
table.setWidths(new int[] {65,50,65,50,65,50,50,50});
} catch (DocumentException e) {
e.printStackTrace();
}
paragraph.add(table);
// 4.构造表头
for (String head : tableHead) {
head = StringUtils.isEmpty(head) ? "" : head;
PdfPCell cell = new PdfPCell(new Paragraph(head, getNormalFont()));
cell.setBackgroundColor(
new BaseColor(
Integer.parseInt("124"),
Integer.parseInt("185"),
Integer.parseInt("252"))); // 背景色
cell.setMinimumHeight(Float.parseFloat("15")); // 单元格最小高度
cell.setHorizontalAlignment(Element.ALIGN_CENTER); // 水平居中
table.addCell(cell);
}
if (CollectionUtils.isEmpty(listData)) {
// 没有数据,添加一行空单元格,并返回
for (int i = 0; i < methodNames.size(); i++) {
table.addCell(new Paragraph(" ")); // 有一个空格,否则添加不了
}
return paragraph;
}
// 5.构造table数据
for (T li : listData) {
// 反射获取名字和存入的名字比较相等获取属性值存入
for (String name : methodNames) {
String valueStr = getfieldValue(li, name);
PdfPCell cell = new PdfPCell(new Paragraph(valueStr, getNormalFont()));
cell.setHorizontalAlignment(Element.ALIGN_CENTER); // 水平居中
table.addCell(cell);
}
}
// 5.返回
return paragraph;
}
/**
* addToTable : (从段落中找到第一个table,向该table中追加数据). <br>
* (). <br>
*
* @author
* @param <T>
* @param paragraph
* @param listData
* @param methodNames
* @since JDK 1.8
*/
public static <T> void addToTable(
Paragraph paragraph, List<T> listData, List<String> methodNames) {
for (Element ele : paragraph) {
if (!(ele instanceof PdfPTable)) {
// 不是table元素,直接跳过
continue;
}
// 找到第一个table元素
PdfPTable table = (PdfPTable) ele;
for (T data : listData) {
for (String name : methodNames) {
String valueStr = getfieldValue(data, name);
PdfPCell cell = new PdfPCell(new Paragraph(valueStr, getNormalFont()));
cell.setHorizontalAlignment(Element.ALIGN_CENTER); // 水平居中
table.addCell(cell);
}
}
break;
}
}
/**
* exportDocument : (生成并下载PDF文档). <br>
* (). <br>
*
* @author
* @param document 文档对象
* @param cover 封面:若不是null,则会先添加封面,并另起新页面添加段落
* @param paragraphs 需要组成PDF文件的段落
* @param response 请求的响应对象
* @param fileName 生成的文件名称,不需要加pdf后缀
* @since JDK 1.8
*/
public static void exportDocument(
Document document,
Paragraph cover,
List<Paragraph> paragraphs,
HttpServletResponse response,
String fileName) {
try (ServletOutputStream out = response.getOutputStream()) {
response.setContentType("application/pdf;charset=UTF-8");
response.setHeader(
"Content-Disposition",
"attachment;fileName=" + URLEncoder.encode(fileName + ".pdf", "UTF-8"));
PdfWriter.getInstance(document, out);
// 打开文档
document.open();
if (cover != null) {
document.add(cover);
// 起新页面
document.newPage();
}
StringBuilder errorMsg = new StringBuilder();
for (int i = 0; i < paragraphs.size(); i++) {
try {
// 将段落添加到文档
document.add(paragraphs.get(i));
// 空行
document.add(Chunk.NEWLINE);
document.add(Chunk.NEWLINE);
} catch (DocumentException e) {
errorMsg.append("PDF文件生成出错,请检查第:").append(i).append("个段落");
}
}
if (!StringUtils.isEmpty(errorMsg.toString())) {
logger.error(errorMsg);
}
// 关闭文档
document.close();
out.flush();
} catch (Exception e) {
logger.error("生成PDF文档并下载,出错:", e);
}
}
/**
* setDefaultIndentationLeft : (设置段落标题默认左边距). <br>
*
* @author
* @param paragraph
* @since JDK 1.8
*/
public static void setDefaultIndentationLeft(Paragraph paragraph) {
paragraph.setIndentationLeft(Float.parseFloat("30"));
}
/**
* addBlankLine : (添加空行). <br>
*
* @author
* @param paragraph 需要添加空行的段落
* @param lineNum 需要添加空行的个数
* @since JDK 1.8
*/
public static void addBlankLine(Paragraph paragraph, int lineNum) {
if (paragraph == null) {
return;
}
for (int i = 0; i < lineNum; i++) {
paragraph.add(Chunk.NEWLINE);
}
}
}
结果