java生成PDF(图片,模板,表格)

2023-11-14

刚接到了一个需求,生成一个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动态生成,不需要添加指定模板域

2023-06-30更新,今天有朋友问图片能不能动态渲染位置,不需要添加模板域,其实是可以的,我把代码粘贴到下面,大家可以做个参考,其实看到这里基本上套路大家也就知道了,所以我就不粘贴那些繁琐的创建pdf的document之类的代码了,直接上面粘贴就行,这里只粘贴关于无指定域添加图片的代码。

			//获取图片的字节码,这里我是用echarts生成的统计图,如果对echarts后台生成感兴趣的话可以看我另一篇文章(文章是收费的哦,白嫖客不用点了)。
			byte[] base = bankReportPdfService.getBankHistogramDataBase64All(bankId, startTime, endTime, reportTimeType, bank);
            Image ajgAllImage = Image.getInstance(base);
            //设置图片位置的x轴和y轴
            ajgAllImage.setAbsolutePosition(80, 400);//注意是从文档的左下角往右、往上计算的
            //设置图片的宽度和高度
            ajgAllImage.scaleToFit(1000, 300);
            // 设置居中
            ajgAllImage.setAlignment(Image.MIDDLE);
            //将图片添加到pdf文件中
            document.add(ajgAllImage);

只需要这一段代码就可以实现动态添加无模块域图片了,如果需要好多页面添加的话就加上document.newPage();,重新new一页,就可以了,注意间距,每页的xy轴都是从0开始的。

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

java生成PDF(图片,模板,表格) 的相关文章

  • 如何使用Spring WebClient进行同步调用?

    Spring Framework in 休息模板 https docs spring io spring framework docs current javadoc api org springframework web client R
  • Java 7 默认语言环境

    我刚刚安装了 jre7 我很惊讶地发现我的默认区域设置现在是 en US 对于jre6 它是de CH 与jre7有什么不同 默认区域设置不再是操作系统之一吗 顺便说一句 我使用的是Windows7 谢谢你的回答 编辑 我已经看到了语言环境
  • Android 中的列表(特别是 RecyclerView 和 CardView)如何工作

    请原谅我问这个问题 但我是 Android 开发新手 尽管我正在尝试了解developer android com 网站上的基础知识 但大多数示例 即使他们说它们是为 Android Studio 构建的 尚未设置为使用 Gradle 因此
  • 如何使用 JAVA 代码以编程方式捕获线程转储?

    我想通过 java 代码生成线程转储 我尝试使用 ThreadMXBean 为此 但我没有以正确的格式获得线程转储 因为我们正在使用jstack命令 请任何人提供一些帮助 他们是否有其他方式获取线程转储 使用任何其他 API 我想要的线程转
  • Reactive Spring 不支持 HttpServletRequest 作为 REST 端点中的参数?

    我创建了一个 RestController 如下所示 RestController public class GreetingController RequestMapping value greetings method RequestM
  • 文本在指定长度后分割,但不要使用 grails 打断单词

    我有一个长字符串 需要将其解析为长度不超过 50 个字符的字符串数组 对我来说 棘手的部分是确保正则表达式找到 50 个字符之前的最后一个空格 以便在字符串之间进行彻底的分隔 因为我不希望单词被切断 public List
  • 从 MS Access 中提取 OLE 对象(Word 文档)

    我有一个 Microsoft Access 数据库 其中包含一个包含 Microsoft Word 文档的 OLE 对象字段 我试图找到代码来检索保存在 OLE 对象中的文件 以便用户可以从我的 JavaFx 应用程序中的按钮下载它 但没有
  • FileNotFoundException - Struts2 文件上传

    Strange FileNotFoundException使用Struts2上传文件时 这是 JSP 的一部分
  • 如何使用正则表达式验证 1-99 范围?

    我需要验证一些用户输入 以确保输入的数字在 1 99 范围内 含 这些必须是整数 Integer 值 允许前面加 0 但可选 有效值 1 01 10 99 09 无效值 0 007 100 10 5 010 到目前为止 我已经制定了以下正则
  • 从直方图计算平均值和百分位数?

    我编写了一个计时器 可以测量任何多线程应用程序中特定代码的性能 在下面的计时器中 它还会在地图中填充花费了 x 毫秒的调用次数 我将使用这张图作为我的直方图的一部分来进行进一步的分析 例如调用花费了这么多毫秒的百分比等等 public st
  • 如何从日期中删除毫秒、秒、分钟和小时[重复]

    这个问题在这里已经有答案了 我遇到了一个问题 我想比较两个日期 然而 我只想比较年 月 日 这就是我能想到的 private Date trim Date date Calendar calendar Calendar getInstanc
  • 如何停止执行的 Jar 文件

    这感觉像是一个愚蠢的问题 但我似乎无法弄清楚 当我在 Windows 上运行 jar 文件时 它不会出现在任务管理器进程中 我怎样才能终止它 我已经尝试过 TASKKILL 但它对我也不起作用 On Linux ps ef grep jav
  • JAVA中遍历JSON数据

    我是 JSON 新手 我使用 HTTPUrlConnections 并在 JAVA 程序中获得一些响应 响应数据将类似于 data id 1 userId 1 name ABC modified 2014 12 04 created 201
  • 避免 Java 中的重复导入:继承导入?

    有没有办法 继承 导入 Example 常见枚举 public enum Constant ONE TWO THREE 使用此枚举的基类 public class Base protected void register Constant
  • Lombok @Builder 不创建不可变对象?

    在很多网站上 我看到 lombok Builder 可以用来创建不可变的对象 https www baeldung com lombok builder singular https www baeldung com lombok buil
  • 使用Java绘制维恩图

    我正在尝试根据给定的布尔方程绘制维恩图 例如 a AND b AND c我想在 Android 手机上执行此操作 因此我需要找到一种使用 Java 来执行此操作的方法 我找到了一个完美的小部件 它可以完成我在这方面寻找的一切布尔代数计算器
  • JMS 中的 MessageListener 和 Consumer 有什么区别?

    我是新来的JMS 据我了解Consumers能够从队列 主题中挑选消息 那么为什么你需要一个MessageListener因为Consumers会知道他们什么时候收到消息吗 这样的实际用途是什么MessageListener 编辑 来自Me
  • 使用 Java https 上传到 Imgur v3 错误

    我目前正在尝试使用他们当前的 API v3 上传到 imgur 但是我不断收到错误 错误 javax net ssl SSLException 证书中的主机名不匹配 api imgur com imgur com OR imgur com
  • 何时在 hibernate 中使用 DiscriminatorValue 注解

    在 hibernate 中使用 DiscriminatorValue 注释的最佳场景是什么以及何时 这两个链接最能帮助我理解继承概念 http docs oracle com javaee 6 tutorial doc bnbqn html
  • 检查应用程序是否在 Android Market 上可用

    给定 Android 应用程序 ID 包名称 如何以编程方式检查该应用程序是否在 Android Market 上可用 例如 com rovio angrybirds 可用 而 com random app ibuilt 不可用 我计划从

随机推荐

  • mysql中的锁

    锁分类 MySQL中的锁 按照锁的粒度分 分为以下三类 1 全局锁 锁定数据库中的所有表 2 表级锁 每次操作锁住整张表 3 行级锁 每次操作锁住对应的行数据 全局锁 全局锁就是对整个数据库实例加锁 加锁后整个实例就处于只读状态 后续的DM
  • 网易笔试题

    网易笔试不难 但是给了我一个教训 所以记下来以留念 时间 11月3日8 00 后来改到10 00 地点 西安交通大学教2南315教室 赶到考场时 离考试开始时间只差2分钟了 找了个座位坐下后没有任何的等待笔试就开始了 网易的笔试题目很有趣
  • CVE-2023-21839 【vulhub weblogic 漏洞复现】

    漏洞概述 由于Weblogic IIOP T3协议存在缺陷 当IIOP T3协议开启时 允许未经身份验证的攻击者通过IIOP T3协议网络访问攻击存在安全风险的WebLogic Server 漏洞利用成功WebLogic Server可能被
  • ffmpeg命令大全

    ffmpeg命令大全 FFMPEG 目录及作用 FFMPEG基本概念 FFMPEG 命令 基本信息查询命令 主要参数 视频参数 音频参数 录制 录屏 分解与复用 滤镜 简单滤镜 复杂滤镜 直播相关 前言 FFMPEG是特别强大的专门用于处理
  • c/c++获取文夹下所有图片文件路径

    在做项目的时候 我们有时候会遇到给定一个文件夹目录 获取该目录下某种类型的文件的路径 也就是遍历一个目录下的所有文件 经过查询 发现可以通过 代码实例 获取某一目录下所有的 jpg文件路径 include
  • Java知识点汇总第二篇(红色为重点内容,黄色为应用较多的,蓝色为了解的

    一 1 标识符 定义 用来表示变量名 类名 方法名 数组名和文件名的有效字符序列 以字母 下划线 美元符号等开始 后面可以跟字母 下划线 美元符号 数字等字符 注 不能以数字开始 大小写敏感 不能与关键字相同 2 关键字 定义 Java中被
  • 计算机网络-6-应用层

    Lecture06 应用层 本节PPT包含5 7三层 The Session Layer 会话层 The Presentation Layer 展示层 The Application Layer 应用层 1 第五层 The Session
  • 性能测试常见指标有哪些

    性能测试的常见指标包括 1 响应时间 Response Time 用户发送请求到系统返回结果所花费的时间 2 吞吐量 Throughput 单位时间内系统处理的请求数量 通常以每秒请求数 SPS或TPS 表示 3 并发用户数 Concurr
  • libev学习系列之三:libev编译安装

    libev学习系列之三 libev编译安装 版本说明 版本 作者 日期 备注 0 1 ZY 2019 5 31 初稿 目录 文章目录 libev学习系列之三 libev编译安装 版本说明 目录 源码结构 正常编译 交叉编译 源码结构 4 2
  • 龙书虎书鲸书啃不动?试试豆瓣评分9.5的猴书

    相传 编译原理界有三大圣书 龙书是为Compilers Principles Techniques and Tools 虎书是为Modern Compiler Implementation in C 鲸书是为Advanced Compile
  • python自动化办公(三十一)TKinter 先登录授权窗口,授权成功后进入master主窗口

    一 主简介 先登录授权窗口 比如验证账号密码信息等等 授权成功后进入master主窗口 验证成功后 进入主页面 Tkinter实现登录成功后进入主界面 月半的博客 CSDN博客 tkinter登录成功跳转主窗体
  • 安装Pycharm工具 -- ubuntu18.04

    在Ubuntu18 04下 pycharm工具的安装及其快捷方式的创建 下载pycharm安装包 tar gz包 网址 https www jetbrains com pycharm tar gz 安装包解压缩 此处没有指定解压到哪个路径
  • 最经典的黑客技术入门知识

    最经典的黑客技术入门知识 整理 Ackarlix 第一节 什么是黑客 以我的理解 黑客 大体上应该分为 正 邪 两类 正派黑客依靠自己掌握的知识帮助系统管理员找出系统中的漏洞并加以完善 而邪派黑客则是通过各种黑客技能对系统进行攻击 入侵或者
  • js原型和原型链你只要看这一篇

    一 原型概述 任何对象都有一个原型对象 这个原型对象由对象的内置属性 proto 指向它的构造函数的prototyoe指向的对象 即任何对象都是由一个构造函数创建的 被创建的对象都可以获得构造函数的prototype属性 注意 对象是没有p
  • mysql数据库内置函数大全_MySQL数据库——内置函数

    MySQL数据库 内置函数 建表并插入数据 create table student id char 36 primary key name varchar 8 not null age int 3 default 0 mobile cha
  • win7用友u8安装教程_如何在win7系统中安装用友u8(图文)

    现在很多大企业或公司都会用到用友u8软件 相信大家对用友u8都比较熟悉了 一些新手不知道如何在win7系统中安装用友u8 所以今天给大家带来就是在win7系统中安装用友u8的方法 解决方法如下 1 打开 控制面板 程序和功能 打开或关闭wi
  • 【C++】:用sort对string类型进行排序

    前言 这个问题来自于leetcode上面的一道题 Valid Anagram Given two strings s and t write a function to determine if t is an anagram of s F
  • 第3关:文件查看器

    编程要求 实现对给定文件夹目录结构的展示 并以文件名按升序排序的形式打印至控制台 如果是文件夹则在其名字之前加上 若是文件则加上 上级目录与下级目录 下级文件用两个空格作为间隔 补充完善右侧代码区中的showDirStructure Fil
  • 【经典】synergy共享鼠标键盘/一套鼠标键盘操作多台电脑

    使用场景 用一套鼠标键盘控制两个或多个电脑屏幕 所有电脑位于同一局域网下 win10 操作系统 安装 synergy step1 下载 下载地址 synergy step2 安装 选择自己想要安装在的目录然后一直 next 最后 finis
  • java生成PDF(图片,模板,表格)

    刚接到了一个需求 生成一个pdf 一开始以为挺简单的 通过模板生成嘛 我也发过相应的文章 根据模板直接生成pdf 响应到前端或者根据模板生成pdf 直接指定下载位置 这两种方案都可以 不过这篇文章主要讲的生成的pdf是既有模板填充还需要自己