前言
在有些場(chǎng)景下我們可能需要根據(jù)指定的模板來生成 PDF,比如說合同、收據(jù)、發(fā)票等等。因?yàn)?PDF 是不可編輯的,所以用代碼直接對(duì) PDF 文件進(jìn)行修改是很不方便的,這里我是通過 itext
和 Adobe Acrobat
來實(shí)現(xiàn)的,以下就是具體實(shí)現(xiàn)方法。
一、準(zhǔn)備模板
Adobe Acrobat
是由 Adobe
公司開發(fā)的一款 PDF
(Portable Document Format,便攜式文檔格式)編輯軟件。借助它,你可以以 PDF
格式制作和保存文檔 ,以便于瀏覽和打印,或使用更高級(jí)的功能。
說白一點(diǎn)就是 Adobe Acrobat
可以讓你的 PDF
文件編程可編輯文件,PDF 文件可編輯的話,使用代碼去修改就會(huì)方便很多。
adobe 中文官網(wǎng):https://www.adobe.com/cn/
Adobe Acrobat 中文官網(wǎng):https://www.adobe.com/cn/acrobat.html
如果你之前沒有使用過這個(gè)軟件,可以在上面我提供的官網(wǎng)里面去下載
PS:不過這個(gè)軟件是收費(fèi)的,但是可以注冊(cè)一個(gè)賬號(hào)申請(qǐng)免費(fèi)試用,然后按照提示去下載該軟件,大概有一周左右的使用期限
下載完,打開該軟件大概是這個(gè)樣子的
軟件有了之后就是準(zhǔn)備模板,這里我以 勞動(dòng)合同模板
為例:
以上一個(gè)為 PDF 的勞動(dòng)合同模板,使用 Adobe Acrobat
軟件中的 準(zhǔn)備表單
工具將該 .pdf
文件導(dǎo)入進(jìn)來
進(jìn)入到 工具
欄,選擇 準(zhǔn)備表單
,點(diǎn)擊 打開
選擇模板文件
再點(diǎn)擊 開始
進(jìn)來之后就可以對(duì) PDF
文件進(jìn)行編輯,那些需要填入的值的地方,可以 添加文本域
,之后通過代碼設(shè)置的值就會(huì)直接填入到文本域當(dāng)中
雙擊文本域,可編輯文本域的信息
其中最重要的就是 名稱
,如果想要在這個(gè)位置上賦值的話,就需要綁定該名稱,類似于給 Map
賦值需要知道 key
一樣,雖然說在添加文本域的時(shí)候就會(huì)生成一個(gè)名稱,但是還是建議最好自己取一個(gè)見名知意的名稱。
模板各文本域名稱和備注如下:
{
"companyName":"用人單位名稱",
"legalPerson":"法人",
"companyAddress":"用人單位地址",
"companyPhone":"用人單位聯(lián)系方式",
"term":"合同期限",
"startYear":"起始時(shí)間-年",
"startMonth":"起始時(shí)間-月",
"startDay":"起始時(shí)間-日",
"endYear":"終止時(shí)間-年",
"endMonth":"終止時(shí)間-月",
"endDay":"終止時(shí)間-日",
"probationPeriodStartYear":"試用期起始時(shí)間-年",
"probationPeriodStartMonth":"試用期起始時(shí)間-月",
"probationPeriodStartDay":"試用期起始時(shí)間-日",
"probationPeriodEndYear":"試用期終止時(shí)間-年",
"probationPeriodEndMonth":"試用期終止時(shí)間-月",
"probationPeriodEndDay":"試用期終止時(shí)間-日",
"probationPeriodTerm":"試用期期限",
"post":"崗位",
"salary":"薪資",
"probationPeriodSalary":"試用期薪資",
"salaryGrant":"薪資發(fā)放時(shí)間"
}
同時(shí)還能夠設(shè)置其它一些屬性,比如說字體大小、字體、對(duì)齊方式等等,這里我是把字體大小都設(shè)置為 10,字體設(shè)置為宋體,居中對(duì)齊
然后保存即可,再打開該 PDF 文件時(shí),該文件就已成為可編輯文件了,模板到此準(zhǔn)備完成
二、代碼實(shí)現(xiàn)
導(dǎo)入 itext
相關(guān)的依賴:
<!--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/簡(jiǎn)單勞動(dòng)合同模板.pdf");
InputStream inputStream = resource.getInputStream();
/*
* 或者通過 url 從網(wǎng)上下載 pdf 模板文件
*
// 獲取文件地址
String urlPath = "模板資源文件鏈接-url";
// 下載文件
URL url = new URL(urlPath);
URLConnection connection = url.openConnection();
// 設(shè)置請(qǐng)求超時(shí)時(shí)長為 5 秒
connection.setConnectTimeout(5*1000);
// 讀取數(shù)據(jù)
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()) {
// 設(shè)置文本域的字體為中文字體
acroFields.setFieldProperty(param.getKey(), "textfont", font,null);
// 將 map 中的值寫到 pdf 模板對(duì)應(yīng)的文本域中
acroFields.setField(param.getKey(), param.getValue());
}
// 如果為false那么生成的PDF文件還能編輯,所以一定要設(shè)為true
pdfStamper.setFormFlattening(true);
pdfStamper.close();
// 返回文件
ServletUtils.writeAttachment(response, "勞動(dòng)合同.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 響應(yīng)
* @param filename 文件名
* @param content 附件內(nèi)容
*/
public static void writeAttachment(HttpServletResponse response, String filename, byte[] content) throws IOException {
// 設(shè)置 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);
}
...
}
請(qǐng)求接口:
參數(shù):
{
"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"
}
效果:
三、源代碼
------------------項(xiàng)目下載------------------
鏈接:百度網(wǎng)盤
提取碼:ihyo-------------------------------------------
總結(jié)
個(gè)人覺得代碼實(shí)現(xiàn)起來不是很難,關(guān)鍵是要知道如何使用 Adobe Acrobat
工具設(shè)置 PDF 模板以及 itext
的一些 API 的使用,以后有時(shí)間我會(huì)出一篇關(guān)于 iText
的博客,主要介紹 iText
在日常開發(fā)中的主要應(yīng)用。
itextpdf-接口文檔:https://api.itextpdf.com/iText5/java/5.5.9/
itext 生成 PDF(一):https://blog.csdn.net/lcczpp/article/details/125424395
為何選擇iText?java PDF開源庫選擇與iText發(fā)展歷史:https://zhuanlan.zhihu.com/p/375700748文章來源:http://www.zghlxwxcb.cn/news/detail-766019.html
使用itext填充靜態(tài)PDF模板,生成PDF新文件:https://www.cnblogs.com/hunter-space/p/static_pdf.html文章來源地址http://www.zghlxwxcb.cn/news/detail-766019.html
到了這里,關(guān)于Java-根據(jù)模板生成PDF的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!