刚接到了一个需求,生成一个pdf,一开始以为挺简单的,通过模板生成嘛,我也发过相应的文章,根据模板直接生成pdf,响应到前端或者根据模板生成pdf,直接指定下载位置,这两种方案都可以,不过这篇文章主要讲的生成的pdf是既有模板填充还需要自己动态生成表格,包括还需要通过java去生成Echarts图形,通过java后台生成Echarts图形我专门写了一篇文章介绍,java后台生成统计图,这个生成统计图的文章中有两种生成统计图的方式,可以自己选择。

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

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

最近很多人都找我要模板链接,我把他放到网盘了,需要的可以去下载https://pan.baidu.com/s/1YJZtLdiySxUry4h2Gd1V7g 提取码:j1l5,不想从网盘下的也可以从的我csdn资源里面下载,资源里面我也放了一份,0积分下载的,大家自取就好。

一、pom依赖

首先先引入咱们需要的pom依赖,我这里只粘贴pdf的吧,lombok和hutool经常用我就不粘贴了。

com.itextpdf

itextpdf

5.5.9

com.itextpdf

itext-asian

5.2.0

二、生成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 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 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 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 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 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 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,多页模板方式 2023-03-27更新,各位粉丝本人在用过一段时间pdf生成以后,突然有了多页模板图片和描述的需求,需要多页画模板域,因此呢有了下面的代码,其中还要感谢苏诺Alina兄弟。

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 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

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);

}

//第二页的图片域,如果第三页有模板写3即可

PdfContentByte cb2 = ps.getOverContent(2);

//添加图片

Rectangle test = s.getFieldPositions("test").get(0).position;

byte[] base = bankReportPdfService.getBankHistogramDataBase64All(bankId, startTime, endTime, reportTimeType, bank);

Image testImage = Image.getInstance(base);

//这里需要注意一下,如果没有获取第二页的PdfContentByte的话直接填充第二页的图片域图片会顶上去

// 设根据域大小设置缩放图片

testImage .scaleToFit(test .getWidth(),400);

// 设置居中

testImage .setAlignment(Image.MIDDLE);

// 绝对定位

testImage .setAbsolutePosition(test .getLeft(),test .getBottom());

cb2.ddImage(testImage );

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();

//多页模板,你有几页模板写几页就行,我这是两页模板

for (int i = 1; i <= 2; i++) {

PdfImportedPage pageTemplate = writer.getImportedPage(reader, i);

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 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 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的代码了,controller层我就不粘出来了自己搞一下吧,希望可以帮助各位有需要的人,如果帮助到你了就帮忙点个赞,关注一下,我会不定时更新一些自己解决过的业务,希望可以帮助大家!!!

Copyright © 2088 羽毛球世界杯_世界女排世界杯 - umiloo.com All Rights Reserved.
友情链接