刚接到了一个需求,生成一个pdf,一开始以为挺简单的,通过模板生成嘛,我也发过相应的文章,根据模板直接生成pdf,响应到前端或者根据模板生成pdf,直接指定下载位置,这两种方案都可以,不过这篇文章主要讲的生成的pdf是既有模板填充还需要自己动态生成表格,包括还需要通过java去生成Echarts图形,通过java后台生成Echarts图形我专门写了一篇文章介绍,java后台生成统计图,这个生成统计图的文章中有两种生成统计图的方式,可以自己选择。
本篇文章的重点还是在讲通过java生成pdf,其实如果是单纯的模板填充挺简单的,但是又要填充模板还要动态生成表格就比较麻烦了,因为如果在模板中画表格的框去生成的话,超过模板框的位置就会隐藏,我刚接到需求的时候也是有点难受,在网上也是找了大量的资料,研究了半天,发现好多都是你粘贴我,我粘贴你,最终我也算是搞成了,把这些整合一下,让大家用的好用一些,废话不多说,直接上代码!
这里说一下啊,如果需要生成echarts图片,先去看我的生成echarts图片文章,不然这个搞不了。
一、pom依赖
首先先引入咱们需要的pom依赖,我这里只粘贴pdf的吧,lombok和hutool经常用我就不粘贴了。
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.9</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>
二、生成pdf,模板和图片及动态生成表格
我这个没有搞页眉,只搞了页脚,设置页眉/页脚和水印的类我会在最后粘贴出来,因为这几个案例用的都是一个配置类。
实体类
package com.example.demo.domain;
import lombok.Data;
import lombok.experimental.Accessors;
import java.math.BigDecimal;
@Data
@Accessors(chain = true)
public class DuizhangDomain {
private String jg;
private Integer ydz;
private Integer wdz;
private BigDecimal dzl;
}
package com.example.demo.domain;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
@Data
@Accessors(chain = true)
public class YqTable implements Serializable {
private String jg;
private Integer yqs;
}
生成pdf代码
import cn.hutool.core.date.DateUtil;
import com.example.demo.domain.DuizhangDomain;
import com.example.demo.domain.YqTable;
import com.example.demo.pdf.phantom.App;
import com.example.demo.pdf.phantom.PageEvent;
import com.itextpdf.text.*;
import com.itextpdf.text.pdf.*;
import freemarker.template.TemplateException;
import org.springframework.core.io.ClassPathResource;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
/**
* 根据模板填充数据及图片,动态生成数据列表
*/
public class CreatePdfEchrtsAndTableMain2 {
private final static String TITLE = "这个是标题,可有可无";
public void createPdfFile(HttpServletResponse response) throws IOException, DocumentException, TemplateException {
//设置请求返回类型
response.setHeader("Content-Disposition", "attachment; filename=测试.pdf");
OutputStream outputStream = response.getOutputStream();
//模板路径,放到项目里用这个ClassPathResource
ClassPathResource classPathResource = new ClassPathResource("templates/test1.pdf");
InputStream inputStream = classPathResource.getInputStream();
PdfReader reader = new PdfReader(inputStream);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
PdfStamper ps = new PdfStamper(reader, bos);
//设置字体
final BaseFont font = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
ArrayList<BaseFont> fontList = new ArrayList<>();
fontList.add(font);
//提取表单,这个是模板画好的文本框
AcroFields s = ps.getAcroFields();
s.setSubstitutionFonts(fontList);
s.setFieldProperty("jrfk","textfont",font,null);
s.setFieldProperty("bjzs","textfont",font,null);
s.setFieldProperty("type","textfont",font,null);
s.setFieldProperty("createTime","textfont",font,null);
s.setFieldProperty("title","textfont",font,null);
s.setField("jrfk","10");
s.setField("bjzs","20");
s.setField("type","日报");
s.setField("createTime", DateUtil.now());
s.setField("title", TITLE);
//添加图片
PdfContentByte cb = ps.getOverContent(1);
//添加logo
Rectangle logo = s.getFieldPositions("logo").get(0).position;
Image logoImage = Image.getInstance("https://img1.baidu.com/it/u=3646261857,3326755268&fm=253&app=138&size=w931&n=0&f=JPG&fmt=auto?sec=1668186000&t=20050fc88fc3feb1f9d28392f4595ec6");
//根据域的大小缩放图片,我这里宽度在原有的域基础上加了100,你们可以自己调节
logoImage.scaleToFit(logo.getWidth() + 100,logo.getHeight());
logoImage.setAlignment(Image.MIDDLE);
logoImage.setAbsolutePosition(logo.getLeft(),logo.getBottom());
cb.addImage(logoImage);
//获取统计图
//获取域
Rectangle rlt = s.getFieldPositions("rlt").get(0).position;
//热力图
Image rltImage = Image.getInstance("https://img0.baidu.com/it/u=4043177345,1055141017&fm=253&app=138&size=w931&n=0&f=PNG&fmt=auto?sec=1668186000&t=8cfdc5c95cc0070eb91946d780ee8dc3");
//根据域大小设置缩放图片
rltImage.scaleToFit(rlt.getWidth() + 100,rlt.getHeight());
// 设置居中
rltImage.setAlignment(Image.MIDDLE);
//绝对定位
rltImage.setAbsolutePosition(rlt.getLeft(),rlt.getBottom());
//图片旋转,这个可以将图片进行一个旋转,看自己需求
// rltImage.setRotationDegrees(90);
cb.addImage(rltImage);
//按机构统计图
//这个是生成echarts的类,如果需要生成echarts可以去看我的另一个文章,上面前言已经提到了
App app1 = new App();
byte[] echarts1 = app1.createEcharts("ajg.ftl");
Image ajgImage = Image.getInstance(echarts1);
Rectangle ajg = s.getFieldPositions("ajg").get(0).position;
// 根据域大小设置缩放图片
ajgImage.scaleToFit(ajg.getWidth(),400);
// 设置居中
ajgImage.setAlignment(Image.MIDDLE);
// 绝对定位
ajgImage.setAbsolutePosition(ajg.getLeft(),ajg.getBottom());
cb.addImage(ajgImage);
//按机构排名,这个是在图片的基础上还要添加数据,这个模板可以画好
for (int i = 1; i <= 3; i++) {
s.setFieldProperty("ajg" + i,"textfont",font,null);
s.setField("ajg" + i,"机构" + i);
}
App app = new App();
byte[] echarts = app.createEcharts("option.ftl");
//按业务
Rectangle ayw = s.getFieldPositions("ayw").get(0).position;
Image aywImage = Image.getInstance(echarts);
// 设根据域大小设置缩放图片
aywImage.scaleToFit(ayw.getWidth(), 400);
// 设置居中
aywImage.setAlignment(Image.MIDDLE);
// 绝对定位
aywImage.setAbsolutePosition(ayw.getLeft(),ayw.getBottom());
cb.addImage(aywImage);
//按业务排名
for (int i = 1; i <= 3; i++) {
s.setFieldProperty("ayw" + i,"textfont",font,null);
s.setField("ayw" + i,"机构" + i);
}
//按场合
Rectangle acj = s.getFieldPositions("acj").get(0).position;
Image acjImage = Image.getInstance(echarts);
// 设根据域大小设置缩放图片
acjImage.scaleToFit(acj.getWidth(), 400);
// 设置居中
acjImage.setAlignment(Image.MIDDLE);
// 绝对定位
acjImage.setAbsolutePosition(acj.getLeft(),acj.getBottom());
cb.addImage(acjImage);
//按场景排名
for (int i = 1; i <= 3; i++) {
s.setFieldProperty("acj" + i,"textfont",font,null);
s.setField("acj" + i,"机构" + i);
}
//按等级
Rectangle adj = s.getFieldPositions("adj").get(0).position;
Image adjImage = Image.getInstance(echarts);
// 设根据域大小设置缩放图片
adjImage.scaleToFit(adj.getWidth(),400);
// 设置居中
adjImage.setAlignment(Image.MIDDLE);
// 绝对定位
adjImage.setAbsolutePosition(adj.getLeft(),adj.getBottom());
cb.addImage(adjImage);
//按场景排名
for (int i = 1; i <= 3; i++) {
s.setFieldProperty("adj" + i,"textfont",font,null);
s.setField("adj" + i,"机构" + i);
}
ps.setFormFlattening(true);
ps.close();
//*******************填充编辑好后的pdf**************
reader = new PdfReader(bos.toByteArray());
Rectangle pageSize = reader.getPageSize(1);
Document document = new Document(pageSize);
PdfWriter writer = PdfWriter.getInstance(document, outputStream);
writer.setPageEvent(new PageEvent());
// 打开文档
document.open();
PdfContentByte cbUnder = writer.getDirectContentUnder();
PdfImportedPage pageTemplate = writer.getImportedPage(reader, 1);
cbUnder.addTemplate(pageTemplate, 0, 0);
//重新开一页面
document.newPage();
createTable(writer,document);
// document.newPage();
createTableYq(writer,document);
document.close();
outputStream.close();
}
//为一个表格添加内容
public PdfPCell createSetCell(String value,Font font){
PdfPCell cell = new PdfPCell();
cell.setPhrase(new Phrase(value,font));
cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
cell.setHorizontalAlignment(Element.ALIGN_CENTER);
return cell;
}
//添加表格
public void createTable(PdfWriter writer,Document document) throws DocumentException, IOException {
PdfPTable table = new PdfPTable(new float[] { 30, 80, 50, 50, 50});
table.setTotalWidth(520);
table.setPaddingTop(500);
table.setLockedWidth(true);
table.setHorizontalAlignment(Element.ALIGN_CENTER);//居中
table.writeSelectedRows(0, -1,500,800,writer.getDirectContentUnder());
//每页都显示表头,输入几就是第几行的表头固定
table.setHeaderRows(2);
table.setHeaderRows(3);
//定义数据的字体
BaseFont baseFont = BaseFont.createFont("STSong-Light","UniGB-UCS2-H",BaseFont.NOT_EMBEDDED);
Font textFont = new Font(baseFont, 10, Font.NORMAL);
PdfPCell cell = new PdfPCell(new Paragraph(" ", textFont));
cell.setHorizontalAlignment( Element.ALIGN_LEFT);
cell.setVerticalAlignment(