Java使用IText7动态生成带审批文本框的PDF文档

Java使用IText7动态生成带审批文本框的PDF文档

文章目录

测试结果示例

实现思路

使用Canvas进行相对定位和绝对定位来确定文本框内文字位置,用Rectangle通过坐标转换来确定文本框的位置

xml 复制代码
   <itext.version>7.1.2</itext.version>
xml 复制代码
 <!--itext-->
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>kernel</artifactId>
            <version>${itext.version}</version>
        </dependency>
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>io</artifactId>
            <version>${itext.version}</version>
        </dependency>
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>layout</artifactId>
            <version>${itext.version}</version>
        </dependency>
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>forms</artifactId>
            <version>${itext.version}</version>
        </dependency>
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>pdfa</artifactId>
            <version>${itext.version}</version>
        </dependency>
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>pdftest</artifactId>
            <version>${itext.version}</version>
        </dependency>
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>font-asian</artifactId>
            <version>${itext.version}</version>
        </dependency>

1.构建第一个框的起始坐标

注意:由于Rectangle构造的坐标为以文档的左下角为xy轴,为了支持计算分页顺序添加需要转换到左上角

java 复制代码
        //第一个文本框(非审批框,高度比较大有特殊自动换行需求)
        // 定义文本框坐标和矩形尺寸(左上点坐标)
        float xLeftTop = 80;    // 左上角x坐标
        float yLeftTop = 100;   // 左上角y坐标
        float width = 450;      // 矩形宽度
        float height = 300;      // 矩形高度

        //坐标转换:默认是左下角坐标轴
        PdfPage firstPage = pdfDoc.getPage(1);
        float yBottom = firstPage.getPageSize().getHeight() - yLeftTop - height;
        Rectangle rect = new Rectangle(xLeftTop, yBottom, width, height);

2.渲染第一个框

使用Canvas进行相对定位和绝对定位,来确保文本框内的文字内容位置

为了防止自动换行超出框内,建议Canvas布局的宽度小于文本框的布局

java 复制代码
    private void createIndexFieldText(Document doc, PdfFont font, String text, Rectangle rect) {

        // 创建Canvas进行绝对定位
        PdfCanvas pdfCanvas = new PdfCanvas(doc.getPdfDocument().getLastPage());
        //防止字体出框
        Rectangle textArea = new Rectangle(rect.getX(), rect.getY(), rect.getWidth()-20, rect.getHeight());
        Canvas canvas = new Canvas(pdfCanvas, doc.getPdfDocument(), textArea);
        // 绘制文本框边框
        pdfCanvas.setStrokeColor(ColorConstants.BLACK).rectangle(rect).stroke();
        //相对定位
        Paragraph approve = new Paragraph()
                .setFont(font)
                .setFontSize(12)
                .setRelativePosition(10, 10,0,0)
                .add("致______________:");
        canvas.add(approve);
        Paragraph approve1 = new Paragraph()
                .setFont(font)
                .setFontSize(12).setFirstLineIndent(25)
                .setRelativePosition(10,  10,0,0)
                .add("________________机构:");
        canvas.add(approve1);

        Paragraph approve2 = new Paragraph("我方承担的工程," +
                "已完成相关准备工作,申请开工,请予以审核,批准。")
                .setTextAlignment(TextAlignment.JUSTIFIED)
                .setFont(font).setFirstLineIndent(25)
                .setRelativePosition(10, 10, 0,0)
                .setFontSize(12);
        canvas.add(approve2);

        // 审批相关绝对定位
        Paragraph fieldText = new Paragraph()
                .setFont(font)
                .setFontSize(12).setTextAlignment(TextAlignment.RIGHT)
                .setFixedPosition(rect.getX() + rect.getWidth()-400, rect.getY() + 40, 300)
                .add(text);
        canvas.add(fieldText);
        // 在文本框内添加
        Paragraph dateText = new Paragraph()
                .setFont(font)
                .setFontSize(12).setTextAlignment(TextAlignment.RIGHT)
                .setFixedPosition(rect.getX() + rect.getWidth()-400, rect.getY() + 20, 300)
                .add("日期:                        ");
        canvas.add(dateText);
        canvas.close();
    }

3.渲染其他的审批框

原理同大框,需计算是否需要分页,如分页需要重置新页面的起始坐标,且由于大框高度大于其他框,需要重置高度,多个框添加时需计算y轴坐标。

java 复制代码
   /**
     * 添加审批文本框
     */
    private static void addTextField(Document doc, PdfFont font, String text, Rectangle rect,boolean page) {
        // 创建Canvas进行绝对定位
        PdfCanvas pdfCanvas = new PdfCanvas(doc.getPdfDocument().getLastPage());
        //防止字体出框
        Rectangle textArea = new Rectangle(rect.getX(), rect.getY(), rect.getWidth()-20, rect.getHeight());
        if(page){
            pdfCanvas = new PdfCanvas(doc.getPdfDocument().addNewPage());
        }
        Canvas canvas = new Canvas(pdfCanvas, doc.getPdfDocument(), textArea);
        // 绘制文本框边框
        pdfCanvas.setStrokeColor(ColorConstants.BLACK).rectangle(rect).stroke();
        //审核意见定位
        Paragraph approve = new Paragraph()
                .setFont(font)
                .setFontSize(12)
                .setFixedPosition(rect.getX() + 10,  rect.getY()+rect.getHeight()-25,200)
                .add("审核意见:");
        canvas.add(approve);
        // 在文本框内添加
        Paragraph fieldText = new Paragraph()
                .setFont(font)
                .setFontSize(12).setTextAlignment(TextAlignment.RIGHT)
                .setFixedPosition(rect.getX() + rect.getWidth()-400, rect.getY() + 40, 300)
                .add(text);
        canvas.add(fieldText);
        // 在文本框内添加
        Paragraph dateText = new Paragraph()
                .setFont(font)
                .setFontSize(12).setTextAlignment(TextAlignment.RIGHT)
                .setFixedPosition(rect.getX() + rect.getWidth()-400, rect.getY() + 20, 300)
                .add("日期:                        ");
        canvas.add(dateText);
        canvas.close();

    }

4.完整代码

java 复制代码
package com.moshangshang.pdf.controller;

import com.itextpdf.kernel.colors.ColorConstants;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfPage;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.itextpdf.layout.Canvas;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.property.TextAlignment;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.List;


@RestController
@RequestMapping("")
public class PDFController {


    /**
     * 带文本框的审批表生成
     */
    @GetMapping(value = "/pdf")
    public void test( HttpServletResponse response) throws Exception {
        response.setContentType("application/pdf");
        OutputStream os = response.getOutputStream();
        // 创建 PDF 文档
        PdfDocument pdfDoc = new PdfDocument(new PdfWriter(os));
        Document document = new Document(pdfDoc,PageSize.A4);
        PdfFont font = PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H", true);
        // 设置页面尺寸和边距
        document.setMargins(50, 50, 50, 50);

        Paragraph title = new Paragraph("审批测试表")
                .setTextAlignment(TextAlignment.CENTER)
                .setFont(font).setBold().setFontSize(22);
        document.add(title);

        List<String> unitList = Arrays.asList("测试1","测试2","测试3","测试4","测试5","测试6","测试7","测试8","测试9");

        //文本框绘制索引
        int fieldTextIndex = 1;

        //第一个文本框(非审批框,高度比较大有特殊自动换行需求)
        // 定义文本框坐标和矩形尺寸(左上点坐标)
        float xLeftTop = 80;    // 左上角x坐标
        float yLeftTop = 100;   // 左上角y坐标
        float width = 450;      // 矩形宽度
        float height = 300;      // 矩形高度

        //坐标转换:默认是左下角坐标轴
        PdfPage firstPage = pdfDoc.getPage(1);
        float yBottom = firstPage.getPageSize().getHeight() - yLeftTop - height;
        Rectangle rect = new Rectangle(xLeftTop, yBottom, width, height);

        for (String name : unitList) {
            //定位
            if(fieldTextIndex == 1){
                //开工报告第一个大框添加
                createIndexFieldText(document, font, name+"(签字、印章):", rect,false);
            }else {
                //其他全为同等审批框,修改高度
                height = 150;
                //默认第一页三个框,其他页默认四个
                if(fieldTextIndex%4==0){
                    //如果大于单页数量,创建新页,并重置坐标
                    float yBottom1 = firstPage.getPageSize().getHeight() - yLeftTop - height;
                    rect = new Rectangle(xLeftTop, yBottom1, width, height);
                    addTextField(document, font, name+" (签字、印章):", rect,true);
                    fieldTextIndex++;
                    continue;
                }
                rect = new Rectangle(xLeftTop, rect.getY()-height, width, height);
                // 绘制文本框边框
                addTextField(document, font, name+"(签字、印章):", rect,false);
            }
            fieldTextIndex++;
        }

        document.close();
    }

    /**
     *
     */
    private void createIndexFieldText(Document doc, PdfFont font, String text, Rectangle rect,boolean page) {

        // 创建Canvas进行绝对定位
        PdfCanvas pdfCanvas = new PdfCanvas(doc.getPdfDocument().getLastPage());
        //防止字体出框
        Rectangle textArea = new Rectangle(rect.getX(), rect.getY(), rect.getWidth()-20, rect.getHeight());
        if(page){
             pdfCanvas = new PdfCanvas(doc.getPdfDocument().addNewPage());
        }
        Canvas canvas = new Canvas(pdfCanvas, doc.getPdfDocument(), textArea);
        // 绘制文本框边框
        pdfCanvas.setStrokeColor(ColorConstants.BLACK).rectangle(rect).stroke();
        //相对定位
        Paragraph approve = new Paragraph()
                .setFont(font)
                .setFontSize(12)
                .setRelativePosition(10,  10,0,0)
                .add("致______________(单位):");
        canvas.add(approve);
        Paragraph approve1 = new Paragraph()
                .setFont(font)
                .setFontSize(12).setFirstLineIndent(25)
                .setRelativePosition(10,  10,0,0)
                .add("________________机构:");
        canvas.add(approve1);

        Paragraph approve2 = new Paragraph("我方," +
                "已完成相关准备工作,具备开工条件,申请于2025年开工,请予以审核,批准。")
                .setTextAlignment(TextAlignment.JUSTIFIED)
                .setFont(font).setFirstLineIndent(25)
                .setRelativePosition(10, 10, 0,0)
                .setFontSize(12);
        canvas.add(approve2);

        // 审批相关绝对定位
        Paragraph fieldText = new Paragraph()
                .setFont(font)
                .setFontSize(12).setTextAlignment(TextAlignment.RIGHT)
                .setFixedPosition(rect.getX() + rect.getWidth()-400, rect.getY() + 40, 300)
                .add(text);
        canvas.add(fieldText);
        // 在文本框内添加
        Paragraph dateText = new Paragraph()
                .setFont(font)
                .setFontSize(12).setTextAlignment(TextAlignment.RIGHT)
                .setFixedPosition(rect.getX() + rect.getWidth()-400, rect.getY() + 20, 300)
                .add("日期:                        ");
        canvas.add(dateText);
        canvas.close();
    }



    /**
     * 添加审批文本框
     */
    private static void addTextField(Document doc, PdfFont font, String text, Rectangle rect,boolean page) {
        // 创建Canvas进行绝对定位
        PdfCanvas pdfCanvas = new PdfCanvas(doc.getPdfDocument().getLastPage());
        //防止字体出框
        Rectangle textArea = new Rectangle(rect.getX(), rect.getY(), rect.getWidth()-20, rect.getHeight());
        if(page){
            pdfCanvas = new PdfCanvas(doc.getPdfDocument().addNewPage());
        }
        Canvas canvas = new Canvas(pdfCanvas, doc.getPdfDocument(), textArea);
        // 绘制文本框边框
        pdfCanvas.setStrokeColor(ColorConstants.BLACK).rectangle(rect).stroke();
        //审核意见定位
        Paragraph approve = new Paragraph()
                .setFont(font)
                .setFontSize(12)
                .setFixedPosition(rect.getX() + 10,  rect.getY()+rect.getHeight()-25,200)
                .add("审核意见:");
        canvas.add(approve);
        // 在文本框内添加
        Paragraph fieldText = new Paragraph()
                .setFont(font)
                .setFontSize(12).setTextAlignment(TextAlignment.RIGHT)
                .setFixedPosition(rect.getX() + rect.getWidth()-400, rect.getY() + 40, 300)
                .add(text);
        canvas.add(fieldText);
        // 在文本框内添加
        Paragraph dateText = new Paragraph()
                .setFont(font)
                .setFontSize(12).setTextAlignment(TextAlignment.RIGHT)
                .setFixedPosition(rect.getX() + rect.getWidth()-400, rect.getY() + 20, 300)
                .add("日期:                        ");
        canvas.add(dateText);
        canvas.close();

    }

}
相关推荐
羊锦磊34 分钟前
[ Mybatis 多表关联查询 ] resultMap
java·开发语言·数据库·mysql·mybatis
ZeroToOneDev3 小时前
Java(泛型和JUnit)
java·开发语言·笔记
迪尔~5 小时前
Apache POI中通过WorkBook写入图片后出现导出PDF文件时在不同页重复写入该图片问题,如何在通过sheet获取绘图对象清除该图片
java·pdf·excel
现在,此刻5 小时前
leetcode 11. 盛最多水的容器 -java
java·算法·leetcode
DKPT6 小时前
Java设计模式之开闭原则介绍与说明
java·设计模式·开闭原则
hyy27952276846 小时前
企业级WEB应用服务器TOMCAT
java·前端·tomcat
布朗克1686 小时前
Spring Boot项目通过Feign调用三方接口的详细教程
java·spring boot·feign
Arva .6 小时前
Spring基于XML的自动装配
xml·java·spring
帅得不敢出门8 小时前
Android Framework定制长按电源键关机的窗口
android·java·framework
fatfishccc8 小时前
循序渐进学 Spring (上):从 IoC/DI 核心原理到 XML 配置实战
xml·java·数据库·spring·intellij-idea·ioc·di