pdf使用itext模版替换
2024/9/29...大约 4 分钟
pdf使用itext模版替换
文章作用概述
使用 itext 操作 pdf 进行模版替换。模版存放在 oss 上,需要将 oss 文件下载到服务器本地的临时文件,操作完文档将操作后的文档重新上传的 oss 上,将临时文件删除。
包含文档字符串替换和图片替换。这里图片章仅供学习替换图片使用,实际需要与 CFCA 或其他专业签章平台对接。
模版与替换效果展示
核心逻辑介绍
// 读取 PDF
OutputStream os = Files.newOutputStream(new File(outputFileUrl).toPath());
//读取pdf表单
reader = new PdfReader(pdfUrl);
//根据表单生成一个新的pdf文件
ps = new PdfStamper(reader, os);
//获取pdf表单
AcroFields form = ps.getAcroFields();
// 表单中配置了字体,替换可以放开注释
// BaseFont bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", false);
// form.addSubstitutionFont(bf);
if (null != templateValueMap && !templateValueMap.isEmpty()) {
for (String key : templateValueMap.keySet()) {
form.setField(key, String.valueOf(templateValueMap.get(key)));
}
}
GeneratePdfUtil.insertImage(form, ps, "sign_img", imageUrl);
// 设置 true 后表单不可见
ps.setFormFlattening(true);
包导入
<!-- itext pdf -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.13</version>
</dependency>
<!-- PDF字体 -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>
替换文字和图片代码
/**
* 获取得到签章的PDF的图片地址
*
* @param filePath 合同模板地址
* @param imagePath 签章地址
* @param dataMap 补充替换的数据
* @return 签章的PDF的图片地址
*/
private String getChapterPdf(String filePath, String imagePath, Map<String, String> dataMap) {
//生成的文件路径
String outputUrl = null;
File tempFile = null;
try {
outputUrl = ResourceUtils.getURL("classpath:").getPath() + "file" + System.currentTimeMillis() + ".pdf";
tempFile = new File(outputUrl);
} catch (FileNotFoundException e) {
log.error("创建临时文件失败",e);
}
//文件不存在就创建
FileUtil.touch(tempFile);
//模板路径
String pdfUrl = GeneratePdfUtil.getTempPdfUrl(filePath, true);
File imageFile = GeneratePdfUtil.getTempImageUrl(imagePath);
String result = pdfTemplateInsert(pdfUrl, outputUrl, dataMap, imageFile);
return result;
}
/**
* PDF模板插入字段插入图片
* @param pdfUrl 模本临时地址
* @param outputFileUrl 输出文件临时地址
* @param templateValueMap 替换键值对
* @param imageFile 图片文件地址
* @return
*/
public String pdfTemplateInsert(String pdfUrl, String outputFileUrl, Map<String, String> templateValueMap,
File imageFile) {
String imageUrl = imageFile.getAbsolutePath();
boolean success = true;
OutputStream os = null;
PdfStamper ps = null;
PdfReader reader = null;
try {
os = Files.newOutputStream(new File(outputFileUrl).toPath());
//读取pdf表单
reader = new PdfReader(pdfUrl);
//根据表单生成一个新的pdf文件
ps = new PdfStamper(reader, os);
//获取pdf表单
AcroFields form = ps.getAcroFields();
// 表单中配置了字体,替换可以放开注释
// BaseFont bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", false);
// form.addSubstitutionFont(bf);
if (null != templateValueMap && !templateValueMap.isEmpty()) {
for (String key : templateValueMap.keySet()) {
form.setField(key, String.valueOf(templateValueMap.get(key)));
}
}
insertImage(form, ps, "sign_img", imageUrl);
// 设置 true 后表单不可见
ps.setFormFlattening(true);
} catch (Exception e) {
log.error("生成文件异常", e);
throw new ServiceException(ErrorCodeOrderEnum.TRANSFER_FILE_GENERATE_ERROR.getMsg(),ErrorCodeOrderEnum.TRANSFER_FILE_GENERATE_ERROR.getCode());
} finally {
try {
ps.close();
reader.close();
os.close();
} catch (Exception e) {
log.error("关闭异常", e);
throw new ServiceException(ErrorCodeOrderEnum.TRANSFER_FILE_CLOSE_ERROR.getMsg(),ErrorCodeOrderEnum.TRANSFER_FILE_CLOSE_ERROR.getCode());
}
}
String ossName = "";
if (success) {
// 上传加密桶
R<Map<String, String>> ossNameResult = componentOssFeign.encryptionUpload("order",
FileUploadUtil.getMultipartFile(new File(outputFileUrl)));
// 上传文件失败,删除缓存直接抛出异常
if (ossNameResult == null || !ossNameResult.isSuccess() || ossNameResult.getData() == null
|| !ossNameResult.getData().containsKey("fileName")
|| StringUtils.isBlank(ossNameResult.getData().get("fileName"))) {
imageFile.delete();
new File(pdfUrl).delete();
new File(outputFileUrl).delete();
throw new ServiceException(ErrorCodeOrderEnum.TRANSFER_FILE_UPLOAD_ERROR);
}
ossName = ossNameResult.getData().get("fileName");
}
imageFile.delete();
new File(pdfUrl).delete();
new File(outputFileUrl).delete();
return ossName;
}
/**
* 插入图片
* @param form pdf表单
* @param stamper pdf模板对象
* @param filedName 图片字段名
* @param url 图片临时路径
*/
public static void insertImage(AcroFields form, PdfStamper stamper, String filedName, String url) {
int pageNo = form.getFieldPositions(filedName).get(0).page;
Rectangle signRect = form.getFieldPositions(filedName).get(0).position;
float x = signRect.getLeft();
float y = signRect.getBottom();
Image image = null;
try {
image = Image.getInstance(url);
} catch (BadElementException e) {
log.error("获取签章图片实例异常", e);
throw new ServiceException(ErrorCodeOrderEnum.TRANSFER_IMAGE_INSTANCE_ERROR.getMsg(),ErrorCodeOrderEnum.TRANSFER_IMAGE_INSTANCE_ERROR.getCode());
} catch (IOException e) {
log.error("获取签章图片流异常", e);
throw new ServiceException(ErrorCodeOrderEnum.TRANSFER_IMAGE_STREAM_ERROR.getMsg(),ErrorCodeOrderEnum.TRANSFER_IMAGE_STREAM_ERROR.getCode());
}
// 获取操作的页面
PdfContentByte under = stamper.getOverContent(pageNo);
// 根据域的大小缩放图片
image.scaleToFit(signRect.getWidth(), signRect.getHeight());
// 添加图片
image.setAbsolutePosition(x, y);
try {
under.addImage(image);
} catch (DocumentException e) {
log.error("添加签章异常", e);
throw new ServiceException(ErrorCodeOrderEnum.TRANSFER_INSERT_IMAGE_ERROR.getMsg(),ErrorCodeOrderEnum.TRANSFER_INSERT_IMAGE_ERROR.getCode());
}
}
/**
* 将PDF下载到本地
* @param oldPath OSS地址
* @param isUrl 是否是URL
* @return 临时文件路径
*/
public static String getTempPdfUrl(String oldPath, boolean isUrl) {
GeneratePdfUtil.checkIsPdf(oldPath, isUrl);
String suffix = oldPath.substring(oldPath.lastIndexOf("."));
String pdfPath = null;
File tempFile = null;
try {
pdfPath = ResourceUtils.getURL("classpath:").getPath() + "pdfLocal" + System.currentTimeMillis() + suffix;
tempFile = new File(pdfPath);
} catch (FileNotFoundException e) {
log.error("生成PDF文件路径异常", e);
throw new ServiceException(ErrorCodeOrderEnum.TRANSFER_CREATE_PDF_ERROR.getMsg(),ErrorCodeOrderEnum.TRANSFER_CREATE_PDF_ERROR.getCode());
}
//文件不存在就创建
FileUtil.touch(tempFile);
try {
// 构造请求头
URL url = new URL(oldPath);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setRequestProperty("Content-Type", "application/octet-stream");
connection.setRequestProperty("Content-Disposition", "attachment;filename=123456.pdf");
InputStream inputStream = connection.getInputStream();
FileOutputStream outputStream = new FileOutputStream(pdfPath);
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, length);
}
outputStream.close();
inputStream.close();
log.info("PDF文件下载完成!");
} catch (IOException e) {
log.error("下载PDF文件异常", e);
throw new ServiceException(ErrorCodeOrderEnum.TRANSFER_DOWNLOAD_PDF_ERROR.getMsg(),ErrorCodeOrderEnum.TRANSFER_DOWNLOAD_PDF_ERROR.getCode());
}
log.info("生成新的pdf文件路径:{}", pdfPath);
return pdfPath;
}
制作模版
我这里使用的 Adobe Acrobat Pro DC 作为 pdf 文件的打开软件,也可以使用其他。
双击表单,设置对应的属性,名称就是代码中 Map 替换的 Key
可以在外观中设置对应要显示的字体,设置字体后代码替换时会自动应用。
完~
#iText