前言
证书颁发 本来是第三方机构颁发的,由于需求有所更改,现在由 “我们” 颁发证书这个功能。由于每个人的证书都不一样,但是格式都一样,所以我们需要一个模板来动态生成证书。
制作表单&&效果
制作表单的工具:UPDF (收费)
红色区域是需要填写的数据。
对应的表单如图下:
表单分别为:Name、Date、log(图片)、Year、Moor、Day
注意:设置表单的名称最好是唯一
引入 itext7-core
iText7-core 是一个强大的开源 PDF 开发库,适用于 Java 和 .NET 平台。它提供了一套丰富的 API,用于创建、修改和处理 PDF 文档。
依赖
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext7-core</artifactId>
<version>8.0.5</version>
<type>pom</type>
</dependency>
Demo
现在模板有了,模板中的表单也设置了,整体流程大致如下:
- 加载模板文件
- 初始化 PDF 文档
- 设置字体
- 填充表单字段
- 插入图片
- 固化表单字段
- 保存并关闭文档
准备
模板和图片,放在 resource 下的static路径下:
根据表单设置Map,格式为,“表单的名称” -> "需要添加的数据"。
user实体只有两个字段,name、beginTime。
Map<String, String> fieldValues(User user) {
return Map.of(
"Name", user.getName(),
"Date", this.timestampToFormattedDate(user.getBeginTime()),
"Year", String.valueOf(LocalDate.now().getYear()),
"Moon", String.valueOf(LocalDate.now().getMonthValue()),
"Day", String.valueOf(LocalDate.now().getDayOfMonth())
);
}
/**
* * 将 timestamp 类的时间转换成 yyyy年MM月dd日
*/
public String timestampToFormattedDate(Timestamp timestamp) {
LocalDate localDate = timestamp.toLocalDateTime().toLocalDate();
return localDate.format(DateTimeFormatter.ofPattern("yyyy年MM月dd日"));
}
生成PDF
一定要设置字体,不设置字体,中文填写不进去,默认是Helvetica,不支持中的。
void createPDF(User user) {
// 定义模板文件路径和输出文件路径
String templatePath = "static/module.pdf";
String outputPath = "xxxxx证书.pdf";
try {
// 加载 PDF 模板
ClassPathResource resource = new ClassPathResource(templatePath);
PdfDocument pdfDoc = new PdfDocument(
new PdfReader(resource.getFile().getAbsolutePath()),
new PdfWriter(outputPath)
);
// 设置字体,支持中文简体字体
PdfFont font = PdfFontFactory.createFont("STSongStd-Light", "UniGB-UCS2-H");
// 填充表单字段
this.fillFormFields(pdfDoc, font, user);
// 插入图片
this.insertImage(pdfDoc, "static/luffy.png", "log");
// 固化表单字段
PdfAcroForm form = PdfAcroForm.getAcroForm(pdfDoc, true);
form.flattenFields();
pdfDoc.close();
logger.info("PDF 创建成功,保存路径: " + outputPath);
} catch (Exception e) {
logger.error("创建 PDF 时发生错误", e);
throw new RuntimeException("创建 PDF 失败", e);
}
}
填充表单字段
// 填充表单字段
private void fillFormFields(PdfDocument pdfDoc, PdfFont font, User user) {
PdfAcroForm form = PdfAcroForm.getAcroForm(pdfDoc, true);
Map<String, String> fieldValues = this.fieldValues(user);
fieldValues.forEach((fieldName, fieldValue) -> {
PdfFormField field = form.getField(fieldName);
if (field != null) {
field.setFont(font);
field.setFontSize(16f);
field.setValue(fieldValue);
} else {
logger.warn("未找到名为 '" + fieldName + "' 的字段");
}
});
}
插入图片
private void insertImage(PdfDocument pdfDoc, String imagePath, String fieldName) {
try {
ClassPathResource imageResource = new ClassPathResource(imagePath);
if (!imageResource.exists()) {
throw new FileNotFoundException("图片文件未找到: " + imagePath);
}
ImageData imageData = ImageDataFactory.create(imageResource.getFile().getAbsolutePath());
PdfAcroForm form = PdfAcroForm.getAcroForm(pdfDoc, true);
PdfFormField imageField = form.getField(fieldName);
if (imageField == null || imageField.getWidgets().isEmpty()) {
throw new RuntimeException("未找到图片字段或字段未绑定页面: " + fieldName);
}
// 获取当前页面插入图片的区域
Rectangle rect = imageField.getWidgets().get(0).getRectangle().toRectangle();
PdfPage page = imageField.getWidgets().get(0).getPage();
int pageNumber = pdfDoc.getPageNumber(page);
// 在指定位置绘制图片
PdfCanvas canvas = new PdfCanvas(pdfDoc.getPage(pageNumber));
canvas.addImageFittedIntoRectangle(imageData, rect, false);
logger.info("图片插入成功: " + imagePath + " -> 字段: " + fieldName);
} catch (Exception e) {
logger.error("插入图片时发生错误", e);
throw new RuntimeException("插入图片失败", e);
}
}
测试&&效果
@Test
void createPDF() {
User user = new User();
user.setName("张三");
user.setBeginTime(new Timestamp(System.currentTimeMillis()));
pdfService.createPDF(user);
}
总结
代码实现比较简单,主要是模板花了点时间,找了好些个比较流行的pdf工具,不是要收费,就要购买,要么有水印。。。。。。。 改想法,把填写的地方挖空,把数据插进入,类似于编辑pdf的时候,插入文本框,在里面填写数据。编写发现,需要知道一张pdf的高和宽,以及插入文本框的位置、文本框的大小,折腾了大半天,算了,还是使用回表单填充吧,只能用外挂了(dddd)。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。