Java generates PDF (pictures, templates, tables)

I just received a request to generate a pdf. At first I thought it was very simple. It is generated through a template. I have also posted a corresponding article, which generates a pdf directly according to the template, responds to the front end or generates a pdf according to the template, and directly specifies the download location . Both of these solutions are possible, but the generated pdf that this article mainly talks about is filled with existing templates and needs to dynamically generate tables by itself, including the need to generate Echarts graphics through java. I wrote a special one to generate Echarts graphics through the java background This article introduces that the java background generates statistical graphs . There are two ways to generate statistical graphs in this article on generating statistical graphs, and you can choose by yourself.

The focus of this article is still to generate pdf through java. In fact, it is very simple to fill the template simply, but it is more troublesome to fill the template and dynamically generate the form, because if you draw the box of the form in the template to generate If it exceeds the template box, it will be hidden. I was a little uncomfortable when I first received the request. I also found a lot of information on the Internet. After researching for a long time, I found that you pasted me, and I pasted you. In the end, I also It’s done, let’s integrate these to make it easier for everyone to use, don’t talk nonsense, just upload the code!

Let me talk about it here, if you need to generate echarts pictures, first read my article on generating echarts pictures, otherwise this will not work.

Recently, many people have asked me for a template link. I put it on the network disk. If you need it, you can download it from https://pan.baidu.com/s/1YJZtLdiySxUry4h2Gd1V7g
Extraction code: j1l5
, you can also download it from the network disk I downloaded it from my csdn resource, and I also put a copy in the resource. It is downloaded with 0 points, and everyone can take it by themselves.

1. pom dependency

First of all, introduce the pom dependencies we need. I will only paste the pdf here. I will not paste when lombok and hutool are often used.

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

2. Generate pdf, templates and pictures and dynamically generate tables

I didn't do the header, only the footer. I will paste the class for setting the header/footer and watermark at the end, because these cases use a configuration class.

Entity class

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

generate pdf code

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

Let me talk about the points that need to be paid attention to when generating pdf. Some friends may wonder why you need to call document.newPage(); to open a new page. This is because if you don’t open a new page, it will cause your dynamically generated form. It will overlap with the content of the template, pay attention to overlapping rather than covering! ! !
If I open a new page, it is equivalent to starting to dynamically generate the form on the second page. At this time, some friends may ask, what if my template does not occupy a page, so the first page will have Blank, I also thought of this question, because my requirement is to generate several different pdfs, I will release the code for how to generate this situation below, let's take a look at the results of the first generation, I There are a lot of measured data, so just stick a few results.

Example of generated results

insert image description here
insert image description here
insert image description here
This is the result! ! ! ! basically ok

3. Generate pdf, template and dynamic form

The entity classes are all the same and have not changed.

generate pdf code

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

This is the second method to generate pdf. The generated results are different. Because the first page of the template does not have too many things, there will be a lot of blank space. At this time, it would be wasteful to re-open a page when generating a dynamic form, and the customer will see It doesn't look good, so I encapsulated the method of generating the gap, and just use the gap to separate the template content from the table, and see the effect.

Example of generated results

insert image description here
It can be seen that we did not open a new page in the code, but only added a gap to ensure that the dynamically generated form and template are on the same page, so it is basically completed.

Header/Footer and Watermark Classes

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

4. Generate pdf, multi-page template method

2023-03-27 update, after using the pdf generation for a period of time, all fans suddenly have the need for multi-page template pictures and descriptions, and need multi-page drawing template fields, so the following code is available.

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

Example of generated results
insert image description here

insert image description here
insert image description here
You can see that on the second page, I also generate pictures through the picture text field of the template. We can use this method to generate multi-page templates.

5. The picture is added to the dynamic generation of pdf, no need to add the specified template domain

2023-06-30 update, today a friend asked if the picture can be dynamically rendered, without adding a template field, it is actually possible, I pasted the code below, you can use it as a reference, in fact, you can basically see the routine here That's it, so I don't paste those cumbersome codes for creating pdf documents, just paste them directly above, here I only paste the code about adding pictures without a specified field.

			//获取图片的字节码,这里我是用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);

Only this piece of code is needed to dynamically add non-module domain pictures. If you need to add a lot of pages, add document.newPage(); and renew a new page. Pay attention to the spacing, the xy axis of each page is It starts from 0.

Well, this is the code for generating pdf. I won’t stick out the controller layer and do it myself. I hope it can help everyone in need. If it helps you, please give me a thumbs up and pay attention. I will make it from time to time Update some businesses that I have solved, I hope it can help everyone! ! !

Guess you like

Origin blog.csdn.net/qq_45699784/article/details/127791747