Java-根据模板生成PDF

2023-09-20 10:50:43


前言

在有些场景下我们可能需要根据指定的模板来生成 PDF,比如说合同、收据、发票等等。因为 PDF 是不可编辑的,所以用代码直接对 PDF 文件进行修改是很不方便的,这里我是通过 itextAdobe Acrobat 来实现的,以下就是具体实现方法。


一、准备模板

Adobe Acrobat 是由 Adobe 公司开发的一款 PDF(Portable Document Format,便携式文档格式)编辑软件。借助它,你可以以 PDF 格式制作和保存文档 ,以便于浏览和打印,或使用更高级的功能。

说白一点就是 Adobe Acrobat 可以让你的 PDF 文件编程可编辑文件,PDF 文件可编辑的话,使用代码去修改就会方便很多。

adobe 中文官网:https://www.adobe.com/cn/

Adobe Acrobat 中文官网:https://www.adobe.com/cn/acrobat.html

如果你之前没有使用过这个软件,可以在上面我提供的官网里面去下载

在这里插入图片描述

PS:不过这个软件是收费的,但是可以注册一个账号申请免费试用,然后按照提示去下载该软件,大概有一周左右的使用期限

下载完,打开该软件大概是这个样子的

在这里插入图片描述

软件有了之后就是准备模板,这里我以 劳动合同模板 为例:

在这里插入图片描述

以上一个为 PDF 的劳动合同模板,使用 Adobe Acrobat 软件中的 准备表单 工具将该 .pdf 文件导入进来

进入到 工具 栏,选择 准备表单,点击 打开

在这里插入图片描述

选择模板文件

在这里插入图片描述

再点击 开始

在这里插入图片描述

进来之后就可以对 PDF 文件进行编辑,那些需要填入的值的地方,可以 添加文本域,之后通过代码设置的值就会直接填入到文本域当中

在这里插入图片描述

双击文本域,可编辑文本域的信息

在这里插入图片描述

其中最重要的就是 名称,如果想要在这个位置上赋值的话,就需要绑定该名称,类似于给 Map 赋值需要知道 key 一样,虽然说在添加文本域的时候就会生成一个名称,但是还是建议最好自己取一个见名知意的名称。

模板各文本域名称和备注如下:

{
    "companyName":"用人单位名称",
    "legalPerson":"法人",
    "companyAddress":"用人单位地址",
    "companyPhone":"用人单位联系方式",
    "term":"合同期限",
    "startYear":"起始时间-年",
    "startMonth":"起始时间-月",
    "startDay":"起始时间-日",
    "endYear":"终止时间-年",
    "endMonth":"终止时间-月",
    "endDay":"终止时间-日",
    "probationPeriodStartYear":"试用期起始时间-年",
    "probationPeriodStartMonth":"试用期起始时间-月",
    "probationPeriodStartDay":"试用期起始时间-日",
    "probationPeriodEndYear":"试用期终止时间-年",
    "probationPeriodEndMonth":"试用期终止时间-月",
    "probationPeriodEndDay":"试用期终止时间-日",
    "probationPeriodTerm":"试用期期限",
    "post":"岗位",
    "salary":"薪资",
    "probationPeriodSalary":"试用期薪资",
    "salaryGrant":"薪资发放时间"
}

在这里插入图片描述

同时还能够设置其它一些属性,比如说字体大小、字体、对齐方式等等,这里我是把字体大小都设置为 10,字体设置为宋体,居中对齐

然后保存即可,再打开该 PDF 文件时,该文件就已成为可编辑文件了,模板到此准备完成

在这里插入图片描述


二、代码实现

导入 itext 相关的依赖:

        <!--itext-->
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itextpdf</artifactId>
            <version>5.4.2</version>
        </dependency>
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itext-asian</artifactId>
            <version>5.2.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.pdfbox</groupId>
            <artifactId>pdfbox</artifactId>
            <version>2.0.13</version>
        </dependency>

接口编写:

在这里插入图片描述

PdfController.java

import com.mike.server.system.service.PdfService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.Map;

@Slf4j
@RestController
@RequestMapping("/test")
@Api(tags = "【PDF-管理】")
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
@CrossOrigin(origins = "*", methods = {RequestMethod.POST, RequestMethod.GET})
public class PdfController {

    private final PdfService pdfService;

    @PostMapping(value = "/generate-pdf")
    @ApiOperation(value = "生成PDF", produces = "application/octet-stream")
    public void generatePdf(@RequestBody Map<String, String> params) {
        pdfService.generatePdf(params);
    }
}

PdfService.java

import java.util.Map;

public interface PdfService {

    void generatePdf(Map<String, String> params);
}

PdfServiceImpl.java

import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.AcroFields;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfStamper;
import com.mike.common.core.utils.ServletUtils;
import com.mike.server.system.service.PdfService;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;

@Slf4j
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class PdfServiceImpl implements PdfService {

    private final HttpServletResponse response;

    @Override
    @SneakyThrows
    public void generatePdf(Map<String, String> params) {


        // 读取资源文件夹下的模板
        ClassPathResource resource = new ClassPathResource("pdf-template/简单劳动合同模板.pdf");
        InputStream inputStream = resource.getInputStream();

        /*
         * 或者通过 url 从网上下载 pdf 模板文件
         *
            // 获取文件地址
            String urlPath = "模板资源文件链接-url";
            // 下载文件
            URL url = new URL(urlPath);
            URLConnection connection = url.openConnection();
            // 设置请求超时时长为 5 秒
            connection.setConnectTimeout(5*1000);
            // 读取数据
            InputStream inputStream = connection.getInputStream();
         */

        PdfReader reader = null;
        ByteArrayOutputStream bos = null;
        try {
            reader = new PdfReader(inputStream);
            bos = new ByteArrayOutputStream();
            PdfStamper pdfStamper = new PdfStamper(reader, bos);
            AcroFields acroFields = pdfStamper.getAcroFields();

            // 中文字体
            BaseFont font = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);

            for (Map.Entry<String, String> param : params.entrySet()) {
                // 设置文本域的字体为中文字体
                acroFields.setFieldProperty(param.getKey(), "textfont", font,null);
                // 将 map 中的值写到 pdf 模板对应的文本域中
                acroFields.setField(param.getKey(), param.getValue());
            }

            // 如果为false那么生成的PDF文件还能编辑,所以一定要设为true
            pdfStamper.setFormFlattening(true);
            pdfStamper.close();
            // 返回文件
            ServletUtils.writeAttachment(response, "劳动合同.pdf", bos.toByteArray());
        } catch (IOException | DocumentException e) {
            e.printStackTrace();
        } finally {
            try {
                assert bos != null;
                bos.close();
                reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

ServletUtils.java 工具类部分代码:

import cn.hutool.core.io.IoUtil;
import org.springframework.http.MediaType;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;

/**
 * 客户端工具类
 *
 * @author system
 */
public class ServletUtils {

	...

	
    /**
     * 返回附件
     *
     * @param response 响应
     * @param filename 文件名
     * @param content  附件内容
     */
    public static void writeAttachment(HttpServletResponse response, String filename, byte[] content) throws IOException {
        // 设置 header 和 contentType
        response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));
        response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
        // 输出附件
        IoUtil.write(response.getOutputStream(), false, content);
    }

	...

}

请求接口:

在这里插入图片描述
参数:

{
  "companyName": "xxxx科技有限公司",
  "legalPerson": "米大傻",
  "companyAddress": "广州xxxxxxx",
  "companyPhone": "18274563214",
  "term": "3",
  "startYear": "2023",
  "startMonth": "9",
  "startDay": "15",
  "endYear": "2026",
  "endMonth": "9",
  "endDay": "15",
  "probationPeriodStartYear": "2023",
  "probationPeriodStartMonth": "9",
  "probationPeriodStartDay": "15",
  "probationPeriodEndYear": "2023",
  "probationPeriodEndMonth": "11",
  "probationPeriodEndDay": "15",
  "probationPeriodTerm": "2",
  "post": "JAVA工程师",
  "salary": "23000",
  "probationPeriodSalary": "18000",
  "salaryGrant": "15"
}

效果:

在这里插入图片描述


三、源代码

------------------项目下载------------------
链接:百度网盘
提取码:ihyo
-------------------------------------------


总结

个人觉得代码实现起来不是很难,关键是要知道如何使用 Adobe Acrobat 工具设置 PDF 模板以及 itext 的一些 API 的使用,以后有时间我会出一篇关于 iText 的博客,主要介绍 iText 在日常开发中的主要应用。


itextpdf-接口文档:https://api.itextpdf.com/iText5/java/5.5.9/

itext 生成 PDF(一):https://blog.csdn.net/lcczpp/article/details/125424395

为何选择iText?java PDF开源库选择与iText发展历史:https://zhuanlan.zhihu.com/p/375700748

使用itext填充静态PDF模板,生成PDF新文件:https://www.cnblogs.com/hunter-space/p/static_pdf.html

更多推荐

华为云云服务器评测 宝塔+nginx 同时部署Springboot、Vue项目

系列文章目录华为云云服务器评测第一章[linux实战]华为云耀云服务器L实例Java、node环境配置华为云云服务器评测第二章[linux实战]Unbutnu添加SSHKey、启动Springboot项目华为云云服务器评测第三章[linux实战]使用Vue3、Element-plus菜单组件构建轮播图华为云云服务器评测

华为云云耀云服务器L实例评测|用Python的Flask框架加Nginx实现一个通用的爬虫项目

🏆作者简介,黑夜开发者,CSDN领军人物,全栈领域优质创作者✌,CSDN博客专家,阿里云社区专家博主,2023年6月CSDN上海赛道top4。🏆数年电商行业从业经验,AWS/阿里云资深使用用户,历任核心研发工程师,项目技术负责人。🎉欢迎👍点赞✍评论⭐收藏文章目录🚀一、前言-关于Flask框架🚀二、功能描述�

Java 代理模式之静态代理与动态代理

1,代理模式代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。代理模式的目的:(1)通过引入代理对象的方式来间接访问目标对象,防止直接访问目标对象给系统带来的不必要复杂性;(2)通过代理对象对访问进行控制;代理模式一般会有三个角色:抽象角色:指代理角色和真

C++内存管理

目录一.new和delete二.operatornew与operatordelete函数三.new和delete的实现原理四.在VS2022编译器下new和delete不匹配的问题五.定位new六.malloc/free和new/delete的区别七.C++内存分布八.内存泄漏C++在内存管理上引入了两个操作符,分别是

性能测试 —— Tomcat监控与调优:status页监控

Tomcat服务器是一个免费的开放源代码的Web应用服务器,Tomcat是Apache软件基金会(ApacheSoftwareFoundation)Jakarta项目中的一个核心项目,由Apache、Sun和其他一些公司及个人共同开发而成。Tomcat是一个轻量级应用服务器,在中小型系统和并发访问用户的场合下被普遍使用

STM32 EtherCAT 总线型(1 拖 4)步进电机解决方案

第1章概述技术特点支持标准100M/s带宽全双工EtherCAT总线网络接口及CoE通信协议一进一出(RJ45接口),支持多组动态PDO分组和对象字典的自动映射,支持站号ID的自动设置与保存,支持SDO的电机参数设置与自动保存。自动适配市面上EtherCAT总线主站系统或PLC运动控制器,如TwinCAT,Cod

【Unity实战】从零手戳一个库存背包系统

文章目录前言素材开始一、绘制背包UI二、背包开启关闭三、初始化背包网格四、添加物品五、拖拽交换功能物品六、物品拆分七、物品堆叠八、拖拽还原九、引入字典存储数据十、拾取物品十一、丢弃物品最终效果源码完结前言库存背包系统是大多数游戏的关键部分,几乎在每种类型的游戏都可能会用到,今天我将带你从零实现一个能够进行拖放的库存拆分

Linux学习第13天:嵌入式LinuxLED驱动开发:一字一符总见情

在正式写这篇笔记前,有一个事情必须要说一下。昨天更新的基于API函数的字符设备驱动开发按照正常的教程来说应该在本笔记后一天更新才对。但是由于我一时的疏忽,跳过了本笔记。在昨天学习基于API函数的时候造成了一定程度的困扰。今天重翻教程的时候才发现昨天漏了一节,今天补上。从这件小小的事情上我也明白了一个道理,学习不可一蹴而

React中组件通信01——props

React中组件通信01——props1.父传子——props1.1简单例子——props1.2props可以传递任何数据1.2.1传递数字、对象等1.2.2传递函数1.2.3传递模版jsx2.子传父+子传子——props2.1父传子——传递函数2.2子传父——通过父传子的函数实现2.3优化+子传子(兄弟通信)3.re

Spring工厂模式,模拟ApplicationContext实现

一、什么是SpringSpring是一个轻量级的Java企业开发解决方案。它集合了众多优秀的设计模式如:单例、代理、工厂模式等。在这其中最基础的也就是工厂模式。二、Spring为什么是轻量级在Spring问世之前JavaEE开发主流使用EnterpriseJavaBean(EJB)来进行。但是这个框架是重量级的,扩展性

正则表达式的学习笔记

[!note]其实这个正则表达式整体上不难,自从这个gpt出来之后这种正则表达式已经不需要我们去写了,我们并不需要自己能够去写特别深奥的代码,我们可以将这个正则表达式交给gpt去做,我们只需要能够看懂就行了,所以学习这个正则表达式,自己写不出来那种比较难的正则没有事情,只要你能够看懂别人给你的,你就达标了正则表达式的练

热文推荐