文章目录

  • 前言
  • 一、准备模板
  • 二、代码实现
  • 三、源代码
  • 总结

前言

在有些场景下我们可能需要根据指定的模板来生成 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 相关的依赖:

<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@SneakyThrowspublic 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文件还能编辑,所以一定要设为truepdfStamper.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 和 contentTyperesponse.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