博客 / 詳情

返回

Spring 使用 itext7-core 根據表單 動態填寫 數據

前言

證書頒發 本來是第三方機構頒發的,由於需求有所更改,現在由 “我們” 頒發證書這個功能。由於每個人的證書都不一樣,但是格式都一樣,所以我們需要一個模板來動態生成證書。

製作表單&&效果

製作表單的工具: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)。

user avatar coderleo 頭像 gaoming13 頭像 frontoldman 頭像 fehaha 頭像 warn 頭像 lihaixing 頭像 caideheirenyagao 頭像 thehumble 頭像 zuckjet 頭像 dingxi 頭像 tongouba 頭像
11 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.