首页IT科技java如何实现pdf转word(java生成PDF(图片,模板,表格))

java如何实现pdf转word(java生成PDF(图片,模板,表格))

时间2025-05-01 04:35:13分类IT科技浏览3582
导读:刚接到了一个需求,生成一个pdf,一开始以为挺简单的,通过模板生成嘛,我也发过相应的文章,根据模板直接生成pdf,响应到前端或者根据模板生成pdf,直接指定下载位置,这两种方案都可以,不过这篇文章主要讲的生成的pdf是既有模板填充还需要自己动态生成表格,包括还需要通过java去生成Echarts图形,通过java后台生成Echar...

刚接到了一个需求           ,生成一个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经常用我就不粘贴了           。

<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                 ,多页模板方式 2023-03-27更新      ,各位粉丝本人在用过一段时间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 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<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的代码了                 ,controller层我就不粘出来了自己搞一下吧                 ,希望可以帮助各位有需要的人,如果帮助到你了就帮忙点个赞           ,关注一下                 ,我会不定时更新一些自己解决过的业务      ,希望可以帮助大家!!!

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
如何做网站排名优化(怎么优化网站排名靠前) 网站seo怎么优化(网站seo快速优化)