Java-实现PDF合同模板填写内容并导出PDF文件

可用于公司用户合同导出pdf文件

效果图

一、导入所需要jar包

        <!--生成PDF-->
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itextpdf</artifactId>
            <version>5.5.11</version>
        </dependency>
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itext-asian</artifactId>
            <version>5.2.0</version>
        </dependency>
        <dependency>
            <groupId>org.icepdf.os</groupId>
            <artifactId>icepdf-core</artifactId>
            <version>6.1.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.pdfbox</groupId>
            <artifactId>fontbox</artifactId>
            <version>2.0.12</version>
        </dependency>
        <dependency>
            <groupId>org.apache.pdfbox</groupId>
            <artifactId>pdfbox</artifactId>
            <version>2.0.12</version>
        </dependency>

二、工具类代码实现

package com.example.excel.pdf;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.itextpdf.text.Document;
import com.itextpdf.text.Image;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.AcroFields;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfCopy;
import com.itextpdf.text.pdf.PdfImportedPage;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfStamper;
import lombok.extern.slf4j.Slf4j;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * @author: reshui
 * description:
 * DateTime:2025/2/25-17:40
 */
@Slf4j
public class PdfTemplateFillUtil {

    /**
     * 文件暂存地址
     */
    public static final String TEMP_FILE_PATH = System.getProperty("java.io.tmpdir");

    /**
     * pdf文件暂存地址
     */
    private static final String FILE_PATH = TEMP_FILE_PATH + File.separator + "generate_pdf";

    /**
     * 时间格式
     */
    public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss";

    public static void main(String[] args) {
        Map<String, Object> paramsMap = new HashMap<String, Object>();
        Map<String, Object> textAreaMap = new HashMap<String, Object>();
        Map<String, Object> imageAreaMap = new HashMap<String, Object>();
        textAreaMap.put("partyAName", "胡图图");
        textAreaMap.put("partyBName", "胡英俊");
        textAreaMap.put("address", "翻斗大街翻斗花园二号楼1001室");
        textAreaMap.put("systemOrderNumber", "Vx2024121315555020011");
        textAreaMap.put("idCard", "44xxxxxxxxxxx132123");
        textAreaMap.put("mobile", "185700xxxxxx");
        textAreaMap.put("remark", "     * BaseFont.NOT_EMBEDDED该参数指定是否将字体嵌入到生成的 PDF 文件中。BaseFont.NOT_EMBEDDED 表示不嵌入字体,即生成的 PDF 文件不会包含字体文件本身,而是依赖于查看 PDF 的设备上是否安装了相应的字体。如果设置为 BaseFont.EMBEDDED,则会将字体文件嵌入到 PDF 中,确保在任何设备上都能正确显示字体,但会增加 PDF 文件的大小。\n");

        String formatDateTime = DateUtil.formatDateTime(new Date());
        textAreaMap.put("now", formatDateTime);
        textAreaMap.put("haha", "你好呀,我是胡图图");

        textAreaMap.put("checkNow", "yes");
        textAreaMap.put("check2", "yes");

        paramsMap.put("textAreaMap", textAreaMap);

        imageAreaMap.put("companySign", "https://profile-avatar.csdnimg.cn/128f2647a3ac408eafb94c7a6706689b_weixin_42477252.jpg!1");
        imageAreaMap.put("signatureImg", "https://profile-avatar.csdnimg.cn/128f2647a3ac408eafb94c7a6706689b_weixin_42477252.jpg!1");
        paramsMap.put("imageAreaMap", imageAreaMap);
        easyGeneratePdf(paramsMap, "C:\\Users\\86138\\Desktop\\xxx\\123.pdf");
    }

    public static void easyGeneratePdf(Map<String, Object> areaMap, String fileName, String readPdfTemplateUrl) {
        String formatDateTimeStamp = DateUtil.format(new Date(), YYYYMMDDHHMMSS);
        String pdfFilePath = FILE_PATH + File.separator + formatDateTimeStamp + StrUtil.UNDERLINE + fileName + ".pdf";
        FileUtil.touch(pdfFilePath);
        generatePdf(areaMap, pdfFilePath, readPdfTemplateUrl);
    }

    public static void easyGeneratePdf(Map<String, Object> areaMap, String readPdfTemplateUrl) {
        String formatDateTimeStamp = DateUtil.format(new Date(), YYYYMMDDHHMMSS);
        String pdfFilePath = FILE_PATH + File.separator + formatDateTimeStamp + ".pdf";
        FileUtil.touch(pdfFilePath);
        generatePdf(areaMap, pdfFilePath, readPdfTemplateUrl);
    }

    /**
     * 模板填充生成PDF
     *
     * @param areaMap                  域集合
     * @param outPutPdfFilePath        输出文件路径
     * @param readPdfTemplateUrlOrPath 读取pdf模板路径
     *                                 Linix 字体
     *                                 BaseFont bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
     *                                 Windows 字体
     *                                 BaseFont bf = BaseFont.createFont("c://windows//fonts//simsunb.ttf" , BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
     *                                 <p>
     *                                 BaseFont.IDENTITY_H这个参数指定了字符编码。BaseFont.IDENTITY_H 表示使用 Unicode 水平书写方向的编码,这对于支持中文等多语言字符非常重要。如果不使用合适的编码,可能会导致字符显示乱码。
     *                                 BaseFont.NOT_EMBEDDED该参数指定是否将字体嵌入到生成的 PDF 文件中。BaseFont.NOT_EMBEDDED 表示不嵌入字体,即生成的 PDF 文件不会包含字体文件本身,而是依赖于查看 PDF 的设备上是否安装了相应的字体。如果设置为 BaseFont.EMBEDDED,则会将字体文件嵌入到 PDF 中,确保在任何设备上都能正确显示字体,但会增加 PDF 文件的大小。
     */
    protected static synchronized File generatePdf(Map<String, Object> areaMap, String outPutPdfFilePath, String readPdfTemplateUrlOrPath) {
        PdfReader reader;
        FileOutputStream out;
        ByteArrayOutputStream bos;
        PdfStamper stamper;
        PdfTemplateFillConfig config = getSystemType();
        try {
            Document doc = new Document();
            BaseFont bf = BaseFont.createFont(config.getFontName(), config.getEncoding(), config.getEmbedded());
            out = new FileOutputStream(outPutPdfFilePath);// 输出模板
            //读取 PDF 模板
            reader = new PdfReader(readPdfTemplateUrlOrPath);
            bos = new ByteArrayOutputStream();
            // 创建一个 PdfStamper 对象,用于修改 PDF
            stamper = new PdfStamper(reader, bos);
            // 获取 PDF 中的表单域
            AcroFields form = stamper.getAcroFields();
            //文字类的内容处理
            Map<String, String> textAreaMap = (Map<String, String>) areaMap.get("textAreaMap");
            if (Objects.nonNull(textAreaMap) && !textAreaMap.isEmpty()) {
                // 遍历表单数据,将数据填充到对应的表单域中
                for (Map.Entry<String, String> entry : textAreaMap.entrySet()) {
                    String fieldName = entry.getKey();
                    String fieldValue = entry.getValue();
                    if (fieldName.startsWith("check")) {
                        form.setField(fieldName, fieldValue, true);
                    } else {
                        form.setField(fieldName, fieldValue);
                    }
                }
            }
            form.addSubstitutionFont(bf);
            //图片类的内容处理
            Map<String, String> imageAreaMap = (Map<String, String>) areaMap.get("imageAreaMap");
            if (Objects.nonNull(imageAreaMap) && !imageAreaMap.isEmpty()) {
                // 遍历表单数据,将数据填充到对应的表单域中
                for (Map.Entry<String, String> entry : imageAreaMap.entrySet()) {
                    String fieldName = entry.getKey();
                    String fieldValue = entry.getValue();
                    List<AcroFields.FieldPosition> fieldPositions = form.getFieldPositions(fieldName);
                    if (form.getFieldPositions(fieldName) != null) {
                        int pageNo = fieldPositions.get(0).page;
                        Rectangle signRect = fieldPositions.get(0).position;
                        float x = signRect.getLeft();
                        float y = signRect.getBottom();
                        //根据路径读取图片
                        Image image = Image.getInstance(fieldValue);
                        //获取图片页面
                        PdfContentByte under = stamper.getOverContent(pageNo);
                        //图片大小自适应
                        image.scaleToFit(signRect.getWidth(), signRect.getHeight());
                        //添加图片
                        image.setAbsolutePosition(x, y);
                        under.addImage(image);
                    }
                }
            }
            ///*必须要调用这个,否则文档不会生成的  如果为false那么生成的PDF文件还能编辑,一定要设为true*/
            stamper.setFormFlattening(true);
            stamper.close();
            PdfCopy copy = new PdfCopy(doc, out);
            doc.open();
            for (int i = 1; i < reader.getNumberOfPages() + 1; i++) {
                doc.newPage();
                PdfImportedPage importPage = copy.getImportedPage(new PdfReader(bos.toByteArray()), i);
                copy.addPage(importPage);
            }
            doc.close();
            File file = new File(outPutPdfFilePath);
            log.info("pdf文件生成成功,文件路径为:" + file.getAbsolutePath());
            return file;
        } catch (Exception e) {
            log.error("pdf文件生成失败:", e);
        }
        return null;
    }


    public static PdfTemplateFillConfig getSystemType() {
        String os = System.getProperty("os.name").toLowerCase();
        if (os.contains("win")) {
            return PdfTemplateFillConfig.getWindowsInstance();
        } else if (os.contains("nix") || os.contains("nux") || os.contains("aix")) {
            return PdfTemplateFillConfig.getLinuxInstance();
        } else {
            log.error("未知操作系统,无法加载配置。os-{}", os);
            return null;
        }
    }
}
package com.example.excel.pdf;

import lombok.Data;

/**
 * @author: reshui
 * description:
 * DateTime:2025/2/28-15:48
 */
@Data
public class PdfTemplateFillConfig {


    /**
     * 字体名
     */
    private String fontName;

    /**
     * 编码
     */
    private String encoding;

    /**
     * 是否嵌入字体
     * 该参数指定是否将字体嵌入到生成的 PDF 文件中。BaseFont.NOT_EMBEDDED 表示不嵌入字体,即生成的 PDF 文件不会包含字体文件本身,
     * 而是依赖于查看 PDF 的设备上是否安装了相应的字体。如果设置为 BaseFont.EMBEDDED,则会将字体文件嵌入到 PDF 中,确保在任何设备上都能正确显示字体,
     * 但会增加 PDF 文件的大小。
     */
    private Boolean embedded;

    public static PdfTemplateFillConfig getLinuxInstance() {
        PdfTemplateFillConfig config = new PdfTemplateFillConfig();
        config.setFontName("STSong-Light");
        config.setEncoding("UniGB-UCS2-H");
        config.setEmbedded(true);
        return config;
    }

    public static PdfTemplateFillConfig getWindowsInstance() {
        PdfTemplateFillConfig config = new PdfTemplateFillConfig();
        config.setFontName("c://windows//fonts//simsunb.ttf");
        config.setEncoding("Identity-H");
        config.setEmbedded(true);
        return config;
    }
}

三、pdf模板效果

四、实现效果图

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/981866.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何排查服务器内存泄漏问题

服务器内存泄漏是一种常见的问题&#xff0c;可能导致系统性能下降甚至系统崩溃。以下是一般情况下用于排查服务器内存泄漏问题的步骤&#xff1a; 排查服务器内存泄漏问题的步骤&#xff1a; 监控系统资源&#xff1a; 使用系统监控工具&#xff08;如top、htop、free&#x…

Tampermonkey篡改猴官网,油猴脚本插件电脑版入口(含教程)

Tampermonkey&#xff08;篡改猴&#xff09;是一款功能强大的浏览器扩展工具&#xff0c;自2010年发布以来&#xff0c;已成为全球超过1000万用户的首选脚本管理器。它通过运行用户自定义的JavaScript脚本&#xff0c;赋予用户深度定制网页的能力&#xff0c;涵盖广告拦截、界…

Java高频面试之集合-03

hello啊&#xff0c;各位观众姥爷们&#xff01;&#xff01;&#xff01;本baby今天来报道了&#xff01;哈哈哈哈哈嗝&#x1f436; 面试官&#xff1a;说说ArrayList和LinkedList的区别 ArrayList 与 LinkedList 的详细对比 一、底层数据结构 特性ArrayListLinkedList存…

golang学习笔记——go语言安装及系统环境变量设置

文章目录 go语言安装go envgo getgoproxy测试安装 Go 插件安装 Go 插件依赖工具参考资料用户环境变量和系统环境变量用户环境变量系统环境变量示例设置环境变量的步骤设置用户环境变量设置系统环境变量 验证环境变量总结 2024年最火的5大Go框架1. Gin&#xff1a;高并发接口的“…

Composition API

为什么会产生 Composition API? Vue2 逻辑复用方式 缺点 Mixin (命名空间冲突、逻辑不清晰、不易复用)scoped slot 作用域插槽 (配置项多、代码分裂、性能差)Vue2 对 TS 支持不充分 Composition API 优点 逻辑代码更少, 更集中, 更易扩展更加丰富的 API 集成对 TS 来说,…

DeepSeek R1助力,腾讯AI代码助手解锁音乐创作新

目录 1. DeepSeekR1模型简介2. 歌词创作流程2.1 准备工作2.2 歌词生成技巧 3. 音乐制作环节3.1 主流AI音乐生成平台 4. 歌曲欣赏5. 总结展望 1. DeepSeekR1模型简介 腾讯AI代码助手最新推出的DeepSeekR1模型不仅在代码生成方面表现出色&#xff0c;其强大的自然语言处理能力也…

微信小程序接入deepseek

先上效果 话不多说&#xff0c;直接上代码&#xff08;本人用的hbuilder Xuniapp&#xff09; <template><view class"container"><!-- 聊天内容区域 --><scroll-view class"chat-list" scroll-y :scroll-top"scrollTop":…

angular+nodejs问卷调查系统

说明&#xff1a;我计划用angularmysqlnodejs&#xff0c;做一套问卷调查系统&#xff0c; 1.先设计数据库表&#xff0c; 2.然后添加模拟数据&#xff0c; 3.然后写几个查询方法 4.然后用nodejs写service服务&#xff0c;查询mysql数据 5.然后写contrller路由&#xff0c;指向…

Ubuntu20.04双系统安装及软件安装(五):VSCode

Ubuntu20.04双系统安装及软件安装&#xff08;五&#xff09;&#xff1a;VSCode 打开VScode官网&#xff0c;点击中间左侧的deb文件下载&#xff1a; 系统会弹出下载框&#xff0c;确定即可。 在文件夹的**“下载”目录**&#xff0c;可看到下载的安装包&#xff0c;在该目录下…

EasyDSS视频推拉流系统:清理缓存文件时如何确保缓存读写不受影响?

视频推拉流EasyDSS视频直播点播平台可提供一站式的视频转码、点播、直播、视频推拉流、播放H.265视频等服务&#xff0c;搭配RTMP高清摄像头使用&#xff0c;可将无人机设备的实时流推送到平台上&#xff0c;实现无人机视频推流直播、巡检等应用。 有用户咨询&#xff0c;视频推…

VS Code C++ 开发环境配置

VS Code 是当前非常流行的开发工具. 本文讲述如何配置 VS Code 作为 C开发环境. 本文将按照如下步骤来介绍如何配置 VS Code 作为 C开发环境. 安装编译器安装插件配置工作区 第一个步骤的具体操作会因为系统不同或者方案不同而有不同的选择. 环境要求 首先需要立即 VS Code…

GPIO的简介

目录 一、GPIO简介 二、GPIO基本结构 三、GPIO位结构 1、整体结构和内部各结构 2、输入部分 1.保护二极管 2.输入模式 3.浮空/上拉/下拉配置 ​编辑 4.模拟输入 5.施密特触发器 3、输出部分 1.输出部分前段 2.输出模式 3.开漏/推挽输出 4.复用开漏/推挽输出 四…

EasyDSS视频推拉流/直播点播平台:Mysql数据库接口报错502处理方法

视频推拉流/视频直播点播EasyDSS互联网直播平台支持一站式的上传、转码、直播、回放、嵌入、分享功能&#xff0c;具有多屏播放、自由组合、接口丰富等特点。平台可以为用户提供专业、稳定的直播推流、转码、分发和播放服务&#xff0c;全面满足超低延迟、超高画质、超大并发访…

AI工具:免费-文字转语音TTsmaker

前言&#xff1a; 测试了一款好用的文字转语音工具&#xff0c;简单&#xff0c;个人用免费功能就足够了。 说明&#xff1a; TTSMaker&#xff08;马克配音&#xff09;是一款免费的文本转语音工具&#xff0c;提供语音合成服务&#xff0c;支持多种语言&#xff0c;包括中…

vue3 vite 两种监听pinia状态变化的方式比较:watch, $subscribe

首先搭建vue3 vite 项目 npm create vue选择pinia 或者自己安装pinia 自己安装需要 npm install pinia并在main.js中挂在上&#xff1a; const pinia createPinia() const app createApp(App) app.use(pinia) app.mount(#app)创建stores文件夹和counter.js文件 counter.j…

【算法学习之路】5.贪心算法

贪心算法 前言一.什么是贪心算法二.例题1.合并果子2.跳跳&#xff01;3. 老鼠和奶酪 前言 我会将一些常用的算法以及对应的题单给写完&#xff0c;形成一套完整的算法体系&#xff0c;以及大量的各个难度的题目&#xff0c;目前算法也写了几篇&#xff0c;题单正在更新&#xf…

【ThreeJS Basics 1-6】Camera

文章目录 Camera 相机PerspectiveCamera 透视相机正交相机用鼠标控制相机大幅度转动&#xff08;可以看到后面&#xff09; 控制组件FlyControls 飞行组件控制FirstPersonControls 第一人称控制PointerLockControls 指针锁定控制OrbitControls 轨道控制TrackballControls 轨迹球…

Java+SpringBoot+Vue+数据可视化的百草园化妆服务平台(程序+论文+讲解+安装+调试+售后)

感兴趣的可以先收藏起来&#xff0c;还有大家在毕设选题&#xff0c;项目以及论文编写等相关问题都可以给我留言咨询&#xff0c;我会一一回复&#xff0c;希望帮助更多的人。 系统背景 市场需求催生 在当今社会&#xff0c;化妆已经成为人们日常生活和各种重要场合中不可或…

React:Redux

Redux引入 Redux感觉像组件通信的中介 state存放被管理的状态 action是申请改哪些数据&#xff0c;传入什么参数 reducer是怎么修改数据 我的理解更像是action像一个储存方法的对象&#xff0c;reducer是具体的方法的实现&#xff0c;不同的方法实现也不一样 store是个仓库…

【AI实践】基于TensorFlow/Keras的CNN(卷积神经网络)简单实现:手写数字识别的工程实践

深度神经网络系列文章 【AI深度学习网络】卷积神经网络&#xff08;CNN&#xff09;入门指南&#xff1a;从生物启发的原理到现代架构演进【AI实践】基于TensorFlow/Keras的CNN&#xff08;卷积神经网络&#xff09;简单实现&#xff1a;手写数字识别的工程实践 引言 在深度…