前言

证书颁发 本来是第三方机构颁发的,由于需求有所更改,现在由 “我们” 颁发证书这个功能。由于每个人的证书都不一样,但是格式都一样,所以我们需要一个模板来动态生成证书。

制作表单&&效果

制作表单的工具:UPDF (收费)

红色区域是需要填写的数据。

image.png

对应的表单如图下:
表单分别为:Name、Date、log(图片)、Year、Moor、Day
注意:设置表单的名称最好是唯一

image.png

引入 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

现在模板有了,模板中的表单也设置了,整体流程大致如下:

  1. 加载模板文件
  2. 初始化 PDF 文档
  3. 设置字体
  4. 填充表单字段
  5. 插入图片
  6. 固化表单字段
  7. 保存并关闭文档

准备

模板图片,放在 resource 下的static路径下:

image.png

根据表单设置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,不支持中的。

image.png

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

image.png

总结

代码实现比较简单,主要是模板花了点时间,找了好些个比较流行的pdf工具,不是要收费,就要购买,要么有水印。。。。。。。 改想法,把填写的地方挖空,把数据插进入,类似于编辑pdf的时候,插入文本框,在里面填写数据。编写发现,需要知道一张pdf的高和宽,以及插入文本框的位置、文本框的大小,折腾了大半天,算了,还是使用回表单填充吧,只能用外挂了(dddd)。


zZ_jie
401 声望9 粉丝

虚心接受问题,砥砺前行。