Java-根据模板生成PDF

文章目录


前言

在有些场景下我们可能需要根据指定的模板来生成 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 一样,虽然说在添加文本域的时候就会生成一个名称,但是还是建议最好自己取一个见名知意的名称。

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

json 复制代码
{
    "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 相关的依赖:

xml 复制代码
        <!--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

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

java 复制代码
import java.util.Map;

public interface PdfService {

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

PdfServiceImpl.java

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 工具类部分代码:

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

	...

}

请求接口:

参数:

json 复制代码
{
  "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

相关推荐
是小崔啊1 小时前
开源轮子 - EasyExcel02(深入实践)
java·开源·excel
myNameGL1 小时前
linux安装idea
java·ide·intellij-idea
青春男大1 小时前
java栈--数据结构
java·开发语言·数据结构·学习·eclipse
问道飞鱼2 小时前
【知识科普】认识正则表达式
数据库·mysql·正则表达式
HaiFan.2 小时前
SpringBoot 事务
java·数据库·spring boot·sql·mysql
水根LP492 小时前
linux系统上SQLPLUS的重“大”发现
数据库·oracle
我要学编程(ಥ_ಥ)2 小时前
一文详解“二叉树中的深搜“在算法中的应用
java·数据结构·算法·leetcode·深度优先
music0ant2 小时前
Idea 添加tomcat 并发布到tomcat
java·tomcat·intellij-idea
途途途途3 小时前
精选9个自动化任务的Python脚本精选
数据库·python·自动化
计算机徐师兄3 小时前
Java基于SSM框架的无中介租房系统小程序【附源码、文档】
java·微信小程序·小程序·无中介租房系统小程序·java无中介租房系统小程序·无中介租房微信小程序