⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 blog.csdn.net/qq_45699784/article/details/127791747 「锦鲤飞上天」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

本篇文章的重点还是在讲通过java生成pdf,其实如果是单纯的模板填充挺简单的,但是又要填充模板还要动态生成表格就比较麻烦了,因为如果在模板中画表格的框去生成的话,超过模板框的位置就会隐藏,我刚接到需求的时候也是有点难受,在网上也是找了大量的资料,研究了半天,发现好多都是你粘贴我,我粘贴你,最终我也算是搞成了,把这些整合一下,让大家用的好用一些,废话不多说,直接上代码!

这里说一下啊,如果需要生成echarts图片,先去看我的生成echarts图片文章,不然这个搞不了。

模板链接,我把他放到网盘了,需要的可以去下载:

https://pan.baidu.com/s/1YJZtLdiySxUry4h2Gd1V7g 提取码:j1l5

一、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(Element.ALIGN_BOTTOM);

cell.setBorder(Rectangle.NO_BORDER);

cell.setColspan(5);

table.addCell(cell);

//表头信息
PdfPCell heandCell = new PdfPCell();
heandCell.setRowspan(1);
heandCell.setColspan(5);
heandCell.setFixedHeight(60);
heandCell.setVerticalAlignment(Element.ALIGN_MIDDLE);
heandCell.setHorizontalAlignment(Element.ALIGN_CENTER);
heandCell.setPhrase(new Phrase(TITLE + "对账情况表",textFont));
table.addCell(heandCell);

//表字段
String title[] = {"序号","机构","已对账","未对账","对账率%"};
for (int i = 0; i < title.length; i++) {
PdfPCell heardCell = new PdfPCell();
heardCell.setVerticalAlignment(Element.ALIGN_MIDDLE);
heardCell.setHorizontalAlignment(Element.ALIGN_CENTER);
heardCell.setPhrase(new Phrase(title[i], textFont));
heardCell.setMinimumHeight(20);
table.addCell(heardCell);
}

//列表数据

List<DuizhangDomain> duizhangDomains = new ArrayList<>();
for (int i = 1; i <= 1000; i++) {
DuizhangDomain duizhangDomain = new DuizhangDomain();
duizhangDomain.setJg("机构" + i).setYdz(i).setWdz(i).setDzl(new BigDecimal(i));
duizhangDomains.add(duizhangDomain);
}

for (int i = 0; i < duizhangDomains.size(); i++) {
PdfPCell setCell1 = createSetCell((i + 1) + "", textFont);
PdfPCell setCell2 = createSetCell(duizhangDomains.get(i).getJg(), textFont);
PdfPCell setCell3 = createSetCell(duizhangDomains.get(i).getYdz().toString(), textFont);
PdfPCell setCell4 = createSetCell(duizhangDomains.get(i).getWdz().toString(), textFont);
PdfPCell setCell5 = createSetCell(duizhangDomains.get(i).getDzl() + "%", textFont);
table.addCell(setCell1);
table.addCell(setCell2);
table.addCell(setCell3);
table.addCell(setCell4);
table.addCell(setCell5);
}
document.add(table);
}


public void createTableYq(PdfWriter writer,Document document) throws DocumentException, IOException {

PdfPTable table = new PdfPTable(new float[] {80, 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(Element.ALIGN_BOTTOM);
cell.setBorder(Rectangle.NO_BORDER);
cell.setColspan(2);
table.addCell(cell);

//表头信息
PdfPCell heandCell = new PdfPCell();
heandCell.setRowspan(1);
heandCell.setColspan(2);
heandCell.setFixedHeight(60);
heandCell.setVerticalAlignment(Element.ALIGN_MIDDLE);
heandCell.setHorizontalAlignment(Element.ALIGN_CENTER);
heandCell.setPhrase(new Phrase(TITLE + "逾期表",textFont));
table.addCell(heandCell);

//表字段
String title[] = {"机构名称","逾期数"};
for (int i = 0; i < title.length; i++) {
PdfPCell heardCell = new PdfPCell();
heardCell.setVerticalAlignment(Element.ALIGN_MIDDLE);
heardCell.setHorizontalAlignment(Element.ALIGN_CENTER);
heardCell.setPhrase(new Phrase(title[i], textFont));
heardCell.setMinimumHeight(20);
table.addCell(heardCell);
}

//列表数据

List<YqTable> yqTables = new ArrayList<>();
for (int i = 1; i <= 1000; i++) {
YqTable yq = new YqTable();
yq.setJg("逾期机构" + i).setYqs(i);
yqTables.add(yq);
}

for (int i = 0; i < yqTables.size(); i++) {
PdfPCell setCell2 = createSetCell(yqTables.get(i).getJg(), textFont);
PdfPCell setCell3 = createSetCell(yqTables.get(i).getYqs().toString(), textFont);
table.addCell(setCell2);
table.addCell(setCell3);
}
document.add(table);
}
}

我说一下这个生成pdf需要注意的点,可能有的朋友很疑惑为什么要调用document.newPage();新开一页,这个是因为,如果你不新开一页的话会导致你动态生成的表格会跟模板内容重叠,注意是重叠而不是覆盖!!!

新开一页的话,就相当于我在第二页开始动态生成表格,这个时候可能有朋友就要问了,那如果我的模板没有占到一页怎么办,这样第一页岂不是会有空白,这个问题我也想到了,因为我的需求是生成好几种不同的pdf,下面我会把这种情况如何生成的代码放出来,我们先看一下第一种生成出来后的结果,我测的数据比较多,就粘几个结果好了。

生成结果示例

这就是结果!!!!基本上还可以

三、生成pdf,模板和动态表格

实体类都是一样的没有变。

生成pdf代码

package com.example.demo.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.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 CreatePdfEchrtsAndTableMain3 {

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/test3.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("type", "textfont", font, null);
s.setFieldProperty("createTime", "textfont", font, null);
s.setFieldProperty("title", "textfont", font, null);
s.setField("type", "日报");
s.setField("createTime", DateUtil.now());
s.setField("title", "这是title,模板画的位置框");
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);
//添加间隙,这里为进行了一个封装,因为这个模板第一页只有一些title啥的,
//重开一页太浪费,只需要确定表格要在什么位置生成,添加一个间隙就可以了
createBlankTable(writer, document, font, 180);

createTable(writer, document);

createBlankTable(writer, document, font, 20);
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(1);
table.setHeaderRows(2);

//定义数据的字体
BaseFont baseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
Font textFont = new Font(baseFont, 10, Font.NORMAL);

//表头信息
PdfPCell heandCell = new PdfPCell();
heandCell.setRowspan(1);
heandCell.setColspan(5);
heandCell.setFixedHeight(30);
heandCell.setVerticalAlignment(Element.ALIGN_MIDDLE);
heandCell.setHorizontalAlignment(Element.ALIGN_CENTER);
heandCell.setPhrase(new Phrase("对账情况表", textFont));
table.addCell(heandCell);

//表字段
String title[] = {"序号", "机构", "已对账", "未对账", "对账率%"};
for (int i = 0; i < title.length; i++) {
PdfPCell heardCell = new PdfPCell();
heardCell.setRowspan(1);
heardCell.setVerticalAlignment(Element.ALIGN_MIDDLE);
heardCell.setHorizontalAlignment(Element.ALIGN_CENTER);
heardCell.setPhrase(new Phrase(title[i], textFont));
heardCell.setMinimumHeight(20);
table.addCell(heardCell);

}


//列表数据

List<DuizhangDomain> duizhangDomains = new ArrayList<>();
for (int i = 1; i <= 1000; i++) {
DuizhangDomain duizhangDomain = new DuizhangDomain();
duizhangDomain.setJg("机构" + i).setYdz(i).setWdz(i).setDzl(new BigDecimal(i));
duizhangDomains.add(duizhangDomain);
}

for (int i = 0; i < duizhangDomains.size(); i++) {
PdfPCell setCell1 = createSetCell((i + 1) + "", textFont);
PdfPCell setCell2 = createSetCell(duizhangDomains.get(i).getJg(), textFont);
PdfPCell setCell3 = createSetCell(duizhangDomains.get(i).getYdz().toString(), textFont);
PdfPCell setCell4 = createSetCell(duizhangDomains.get(i).getWdz().toString(), textFont);
PdfPCell setCell5 = createSetCell(duizhangDomains.get(i).getDzl() + "%", textFont);
table.addCell(setCell1);
table.addCell(setCell2);
table.addCell(setCell3);
table.addCell(setCell4);
table.addCell(setCell5);
}
document.add(table);
}


public void createTableYq(PdfWriter writer, Document document) throws DocumentException, IOException {

PdfPTable table = new PdfPTable(new float[]{80, 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 heandCell = new PdfPCell();
heandCell.setRowspan(1);
heandCell.setColspan(2);
heandCell.setFixedHeight(30);
heandCell.setVerticalAlignment(Element.ALIGN_MIDDLE);
heandCell.setHorizontalAlignment(Element.ALIGN_CENTER);
heandCell.setPhrase(new Phrase("逾期表", textFont));
table.addCell(heandCell);

//表字段
String title[] = {"机构名称", "逾期数"};
for (int i = 0; i < title.length; i++) {
PdfPCell heardCell = new PdfPCell();
heardCell.setVerticalAlignment(Element.ALIGN_MIDDLE);
heardCell.setHorizontalAlignment(Element.ALIGN_CENTER);
heardCell.setPhrase(new Phrase(title[i], textFont));
heardCell.setMinimumHeight(20);
table.addCell(heardCell);
}

//列表数据

List<YqTable> yqTables = new ArrayList<>();
for (int i = 1; i <= 1000; i++) {
YqTable yq = new YqTable();
yq.setJg("逾期机构" + i).setYqs(i);
yqTables.add(yq);
}

for (int i = 0; i < yqTables.size(); i++) {
PdfPCell setCell2 = createSetCell(yqTables.get(i).getJg(), textFont);
PdfPCell setCell3 = createSetCell(yqTables.get(i).getYqs().toString(), textFont);
table.addCell(setCell2);
table.addCell(setCell3);
}
document.add(table);
}

/**
* 创建表格跟表格之间的空白间隔
*/
public void createBlankTable(PdfWriter writer, Document document, BaseFont font, int height) throws DocumentException {
PdfPTable table = new PdfPTable(new float[]{30});
table.setTotalWidth(520);
table.setPaddingTop(500);
table.setLockedWidth(true);
table.setHorizontalAlignment(Element.ALIGN_CENTER);//居中
table.writeSelectedRows(0, -1, 500, 800, writer.getDirectContentUnder());
Font textFont = new Font(font, 10, Font.NORMAL);
PdfPCell cell = new PdfPCell(new Paragraph(" ", textFont));

cell.setHorizontalAlignment(Element.ALIGN_LEFT);

cell.setVerticalAlignment(Element.ALIGN_BOTTOM);

cell.setBorder(Rectangle.NO_BORDER);
cell.setFixedHeight(height);

cell.setColspan(1);

table.addCell(cell);
document.add(table);
}
}

这是第二种生成pdf的方法,生成的结果不一样,因为模板第一页没有太多的东西,所以会空出来很多空白,这个时候生成动态表格如果重开一页比较浪费,而且客户看着也不好看,所以我封装了一下生成间隙的方法,直接用间隙把模板内容和表格隔开即可,看一下效果。

生成结果示例

可以看到,我们在代码中并没有新开一页,只是加了一个间隙,就可以保证动态生成的表格和模板在一个页面,就此,基本上就算完成了。

页眉/页脚和水印类

package com.example.demo.pdf.phantom;

import com.itextpdf.text.*;
import com.itextpdf.text.pdf.*;

import java.io.IOException;

public class PageEvent extends PdfPageEventHelper {
/**
* 页眉
*/
//public String header = "itext测试页眉";

/**
* 文档字体大小,页脚页眉最好和文本大小一致
*/
public int presentFontSize = 10;

/**
* 文档页面大小,最好前面传入,否则默认为A4纸张
*/
public Rectangle pageSize = PageSize.A4;

// 模板
public PdfTemplate total;

// 基础字体对象
public BaseFont bf = null;

// 利用基础字体生成的字体对象,一般用于生成中文文字
public Font fontDetail = null;


/**
*
* Creates a new instance of PdfReportM1HeaderFooter 无参构造方法.
*
*/
public PageEvent() {

}

/**
*
* Creates a new instance of PdfReportM1HeaderFooter 构造方法.
*
* @param
*
* @param presentFontSize
* 数据体字体大小
* @param pageSize
* 页面文档大小,A4,A5,A6横转翻转等Rectangle对象
*/
// public PDFBuilder(String yeMei, int presentFontSize, Rectangle pageSize) {
// this.header = yeMei;
// this.presentFontSize = presentFontSize;
// this.pageSize = pageSize;
// }
public PageEvent( int presentFontSize, Rectangle pageSize) {
this.presentFontSize = presentFontSize;
this.pageSize = pageSize;
}

// public void setHeader(String header) {
// this.header = header;
// }

public void setPresentFontSize(int presentFontSize) {
this.presentFontSize = presentFontSize;
}


/**
*
* TODO 文档打开时创建模板
*
* @see com.itextpdf.text.pdf.PdfPageEventHelper#onOpenDocument(com.itextpdf.text.pdf.PdfWriter,
* com.itextpdf.text.Document)
*/
public void onOpenDocument(PdfWriter writer, Document document) {
total = writer.getDirectContent().createTemplate(50, 50);// 共 页 的矩形的长宽高
}

/**
*
* TODO 关闭每页的时候,写入页眉,写入'第几页共'这几个字。
*
* @see com.itextpdf.text.pdf.PdfPageEventHelper#onEndPage(com.itextpdf.text.pdf.PdfWriter,
* com.itextpdf.text.Document)
*/
public void onEndPage(PdfWriter writer, Document document) {
this.addPage(writer, document);
//加水印
this.addWatermark(writer);
}

//加分页
public void addPage(PdfWriter writer, Document document){
//设置分页页眉页脚字体
try {
if (bf == null) {
bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", false);
}
if (fontDetail == null) {
fontDetail = new Font(bf, presentFontSize, Font.NORMAL);// 数据体字体
}
} catch (DocumentException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

// 1.写入页眉
// ColumnText.showTextAligned(writer.getDirectContent(),
// Element.ALIGN_LEFT, new Phrase(header, fontDetail),
// document.left(), document.top() + 20, 0);
// 页眉添加图片
// String path = ResourceUtils.getFile("classpath:").getPath();
// Image img = Image.getInstance(path + "/pdfTemplates/logo.jpg");
// img.setAlignment(Image.MIDDLE);
// img.setWidthPercentage(80);
// img.scaleToFit(50,40);
// img.setAbsolutePosition(document.left(),document.top());
// writer.getDirectContent().addImage(img);
//页眉加下划线
// PdfPTable tableHeader = new PdfPTable(1);
// tableHeader.setTotalWidth(PageSize.A4.getWidth() - 60);
// PdfPCell pCell = new PdfPCell();
// pCell.setBorderWidthBottom(0.3f);
// tableHeader.addCell(pCell);
// tableHeader.writeSelectedRows(0, -1, 30, 805, writer.getDirectContent());

// 2.写入前半部分的 第 X页/共
int pageS = writer.getPageNumber();
String foot1 = "第 " + pageS + " 页 /共";
// String foot1 = pageS +"/";
Phrase footer = new Phrase(foot1, fontDetail);

// 3.计算前半部分的foot1的长度,后面好定位最后一部分的'Y页'这俩字的x轴坐标,字体长度也要计算进去 = len
float len = bf.getWidthPoint(foot1, presentFontSize);

// 4.拿到当前的PdfContentByte
PdfContentByte cb = writer.getDirectContent();

// 5.写入页脚1,x轴就是(右margin+左margin + right() -left()- len)/2.0F
// 再给偏移20F适合人类视觉感受,否则肉眼看上去就太偏左了
// ,y轴就是底边界-20,否则就贴边重叠到数据体里了就不是页脚了;注意Y轴是从下往上累加的,最上方的Top值是大于Bottom好几百开外的。
ColumnText
.showTextAligned(
cb,
Element.ALIGN_CENTER,
footer,
(document.rightMargin() + document.right()
+ document.leftMargin() - document.left() - len) / 2.0F ,
document.bottom() - 20, 0);

// 6.写入页脚2的模板(就是页脚的Y页这俩字)添加到文档中,计算模板的和Y轴,X=(右边界-左边界 - 前半部分的len值)/2.0F +
// len , y 轴和之前的保持一致,底边界-20
cb.addTemplate(total, (document.rightMargin() + document.right()
+ document.leftMargin() - document.left()) / 2.0F ,
document.bottom() - 20); // 调节模版显示的位置

}

//加水印
public void addWatermark(PdfWriter writer) {
// 水印图片
// Image image;
// try {
// image = Image.getInstance("./web/images/001.jpg");
// PdfContentByte content = writer.getDirectContentUnder();
// content.beginText();
// // 开始写入水印
// for(int k=0;k<5;k++){
// for (int j = 0; j <4; j++) {
// image.setAbsolutePosition(150*j,170*k);
// content.addImage(image);
// }
// }
// content.endText();
// } catch (IOException | DocumentException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
BaseFont font = null;
try {
font = BaseFont.createFont("STSong-Light","UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
} catch (DocumentException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
PdfGState gs = new PdfGState();
//添加透明度
gs.setFillOpacity(0.4f);
PdfContentByte content = writer.getDirectContentUnder();
content.beginText();
//水印颜色
content.setColorFill(BaseColor.DARK_GRAY);
content.setGState(gs);
//水印字体样式和大小
content.setFontAndSize(font, 35);
//插入水印 循环每页插入的条数
for (int j = 0; j < 3; j++) {
content.showTextAligned(Element.ALIGN_CENTER, "锦鲤飞上天测试水印", 300, 200 * (j + 1), 30);
}
content.endText();
}

/**
*
* TODO 关闭文档时,替换模板,完成整个页眉页脚组件
*
* @see com.itextpdf.text.pdf.PdfPageEventHelper#onCloseDocument(com.itextpdf.text.pdf.PdfWriter,
* com.itextpdf.text.Document)
*/
public void onCloseDocument(PdfWriter writer, Document document) {
// 7.最后一步了,就是关闭文档的时候,将模板替换成实际的 Y 值,至此,page x of y 制作完毕,完美兼容各种文档size。
total.beginText();
total.setFontAndSize(bf, presentFontSize);// 生成的模版的字体、颜色
String foot2 = " " + (writer.getPageNumber()) + " 页"; //页脚内容拼接 如 第1页/共2页
// String foot2 = String.valueOf(writer.getPageNumber()); //页脚内容拼接 如 1/2
total.showText(foot2);// 模版显示的内容
total.endText();
total.closePath();
}
}

好了这就是生成pdf的代码了,controller层我就不粘出来了自己搞一下吧,希望可以帮助各位有需要的人,如果帮助到你了就帮忙点个赞,关注一下,我会不定时更新一些自己解决过的业务,希望可以帮助大家!!!

文章目录
  1. 1. 一、pom依赖
  2. 2. 二、生成pdf,模板和图片及动态生成表格
  3. 3. 三、生成pdf,模板和动态表格