From 3849263b31a16a91ff08acaa42786ecfde76f33c Mon Sep 17 00:00:00 2001
From: zhangback <zhangback@163.com>
Date: 星期四, 27 十一月 2025 20:18:08 +0800
Subject: [PATCH] 提交

---
 common/src/main/java/com/ruoyi/common/utils/PdfTemplateUtil.java |  239 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 239 insertions(+), 0 deletions(-)

diff --git a/common/src/main/java/com/ruoyi/common/utils/PdfTemplateUtil.java b/common/src/main/java/com/ruoyi/common/utils/PdfTemplateUtil.java
new file mode 100644
index 0000000..a986295
--- /dev/null
+++ b/common/src/main/java/com/ruoyi/common/utils/PdfTemplateUtil.java
@@ -0,0 +1,239 @@
+package com.ruoyi.common.utils;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.batik.transcoder.TranscoderInput;
+import org.apache.batik.transcoder.TranscoderOutput;
+import org.apache.batik.transcoder.image.PNGTranscoder;
+import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.PDResources;
+import org.apache.pdfbox.pdmodel.common.PDRectangle;
+import org.apache.pdfbox.pdmodel.font.PDFont;
+import org.apache.pdfbox.pdmodel.font.PDType0Font;
+import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationWidget;
+import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceDictionary;
+import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceStream;
+import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
+import org.apache.pdfbox.pdmodel.interactive.form.PDField;
+import org.apache.pdfbox.pdmodel.PDPageContentStream;
+import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
+import org.apache.pdfbox.pdmodel.interactive.form.PDPushButton;
+import org.apache.pdfbox.pdmodel.interactive.form.PDTextField;
+
+import javax.imageio.ImageIO;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.io.*;
+import java.util.Base64;
+import java.util.Map;
+@Slf4j
+public class PdfTemplateUtil {
+    /**
+     * 濉厖 PDF 琛ㄥ崟瀛楁
+     *
+     * @param templatePdf 妯℃澘 PDF 璺緞
+     * @param outputPdf   杈撳嚭 PDF 璺緞
+     * @param params      key=琛ㄥ崟瀛楁鍚�, value=濉厖鍊�
+     * @param fontPath    涓枃瀛椾綋 ttf 璺緞
+     * @throws Exception
+     */
+
+    public static void fillPdf(String templatePdf, String outputPdf, Map<String, String> params, String fontPath) throws Exception {
+        PDDocument document = PDDocument.load(new File(templatePdf));
+        PDAcroForm form = document.getDocumentCatalog().getAcroForm();
+
+        PDType0Font font = PDType0Font.load(document, new File(fontPath));
+
+        // 娉ㄥ唽瀛椾綋璧勬簮
+        PDResources resources = form.getDefaultResources();
+        if (resources == null) {
+            resources = new PDResources();
+            form.setDefaultResources(resources);
+        }
+        COSName fontResName = resources.add(font);
+
+        for (Map.Entry<String, String> entry : params.entrySet()) {
+            PDField field = form.getField(entry.getKey());
+
+            // 鏂囧瓧瀛楁
+            if (field instanceof PDTextField) {
+                PDTextField tf = (PDTextField) field;
+                tf.setDefaultAppearance("/" + fontResName.getName() + " 10 Tf 0 0 0 rg");
+                tf.getCOSObject().setInt(COSName.Q, 1); // 姘村钩灞呬腑
+                tf.setValue(entry.getValue());
+            }
+
+            // 鍥剧墖鎸夐挳瀛楁
+            if (field instanceof PDPushButton) {
+                PDPushButton button = (PDPushButton) field;
+                String imgBase64 = entry.getValue();
+
+                if (imgBase64.contains(",")) {
+                    imgBase64 = imgBase64.split(",")[1];  // 鍘绘帀鍓嶇紑
+                }
+
+                byte[] imageBytes;
+
+                if (entry.getValue().startsWith("data:image/svg+xml;base64,")) {
+                    // SVG 杞� PNG
+                    byte[] svgBytes = Base64.getDecoder().decode(imgBase64);
+                    ByteArrayInputStream svgStream = new ByteArrayInputStream(svgBytes);
+                    PNGTranscoder transcoder = new PNGTranscoder();
+
+                    // 杈撳嚭鍒� ByteArray
+                    ByteArrayOutputStream pngOut = new ByteArrayOutputStream();
+                    TranscoderInput input = new TranscoderInput(svgStream);
+                    TranscoderOutput output = new TranscoderOutput(pngOut);
+                    transcoder.transcode(input, output);
+                    pngOut.flush();
+                    pngOut.close();
+                    imageBytes = pngOut.toByteArray();
+
+                    // BufferedImage 璇诲彇
+                    BufferedImage bufferedImage = ImageIO.read(new ByteArrayInputStream(imageBytes));
+
+                    // 鏃嬭浆 90掳
+                    int w = bufferedImage.getWidth();
+                    int h = bufferedImage.getHeight();
+                    BufferedImage rotated = new BufferedImage(h, w, bufferedImage.getType());
+                    Graphics2D g2d = rotated.createGraphics();
+                    g2d.translate(h / 2.0, w / 2.0);
+                    g2d.rotate(Math.toRadians(90));
+                    g2d.translate(-w / 2.0, -h / 2.0);
+                    g2d.drawImage(bufferedImage, 0, 0, null);
+                    g2d.dispose();
+
+                    // 杞洖瀛楄妭鏁扮粍
+                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                    ImageIO.write(rotated, "png", baos);
+                    imageBytes = baos.toByteArray();
+                } else {
+                    // PNG/JPG 鐩存帴瑙g爜
+                    imageBytes = Base64.getDecoder().decode(imgBase64);
+                }
+
+                // 鍒涘缓 PDImageXObject 骞剁粯鍒�
+                PDImageXObject pdImage = PDImageXObject.createFromByteArray(document, imageBytes, "img");
+                PDAppearanceStream appearance = button.getWidgets().get(0).getNormalAppearanceStream();
+                PDPageContentStream cs = new PDPageContentStream(document, appearance);
+                cs.drawImage(pdImage, 0, 0, appearance.getBBox().getWidth(), appearance.getBBox().getHeight());
+                cs.close();
+            }
+        }
+
+        form.refreshAppearances();
+        form.flatten();
+        document.save(outputPdf);
+        document.close();
+    }
+
+    public static void fillPdf(String templatePdf, OutputStream outputStream, Map<String, String> params, String fontPath) throws Exception {
+        PDDocument document = PDDocument.load(new File(templatePdf));
+        PDAcroForm form = document.getDocumentCatalog().getAcroForm();
+
+        PDType0Font font = PDType0Font.load(document, new File(fontPath));
+
+        // 娉ㄥ唽瀛椾綋璧勬簮
+        PDResources resources = form.getDefaultResources();
+        if (resources == null) {
+            resources = new PDResources();
+            form.setDefaultResources(resources);
+        }
+        COSName fontResName = resources.add(font);
+
+        for (Map.Entry<String, String> entry : params.entrySet()) {
+            PDField field = form.getField(entry.getKey());
+
+            // 鏂囧瓧瀛楁
+            if (field instanceof PDTextField) {
+                PDTextField tf = (PDTextField) field;
+                tf.setDefaultAppearance("/" + fontResName.getName() + " 10 Tf 0 0 0 rg");
+                tf.getCOSObject().setInt(COSName.Q, 1); // 姘村钩灞呬腑
+                tf.setValue(entry.getValue());
+            }
+
+            // 鍥剧墖鎸夐挳瀛楁
+            if (field instanceof PDPushButton) {
+                PDPushButton button = (PDPushButton) field;
+                String imgBase64 = entry.getValue();
+
+                if (imgBase64.contains(",")) {
+                    imgBase64 = imgBase64.split(",")[1];  // 鍘绘帀鍓嶇紑
+                }
+
+                byte[] imageBytes;
+
+                if (entry.getValue().startsWith("data:image/svg+xml;base64,")) {
+                    // SVG 杞� PNG
+                    byte[] svgBytes = Base64.getDecoder().decode(imgBase64);
+                    ByteArrayInputStream svgStream = new ByteArrayInputStream(svgBytes);
+                    PNGTranscoder transcoder = new PNGTranscoder();
+
+                    // 杈撳嚭鍒� ByteArray
+                    ByteArrayOutputStream pngOut = new ByteArrayOutputStream();
+                    TranscoderInput input = new TranscoderInput(svgStream);
+                    TranscoderOutput output = new TranscoderOutput(pngOut);
+                    transcoder.transcode(input, output);
+                    pngOut.flush();
+                    pngOut.close();
+                    imageBytes = pngOut.toByteArray();
+
+                    // BufferedImage 璇诲彇
+                    BufferedImage bufferedImage = ImageIO.read(new ByteArrayInputStream(imageBytes));
+
+                    // 鏃嬭浆 90掳
+                    int w = bufferedImage.getWidth();
+                    int h = bufferedImage.getHeight();
+                    BufferedImage rotated = new BufferedImage(h, w, bufferedImage.getType());
+                    Graphics2D g2d = rotated.createGraphics();
+                    g2d.translate(h / 2.0, w / 2.0);
+                    g2d.rotate(Math.toRadians(90));
+                    g2d.translate(-w / 2.0, -h / 2.0);
+                    g2d.drawImage(bufferedImage, 0, 0, null);
+                    g2d.dispose();
+
+                    // 杞洖瀛楄妭鏁扮粍
+                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                    ImageIO.write(rotated, "png", baos);
+                    imageBytes = baos.toByteArray();
+                } else {
+                    // PNG/JPG 鐩存帴瑙g爜
+                    imageBytes = Base64.getDecoder().decode(imgBase64);
+                }
+
+                // 鍒涘缓 PDImageXObject 骞剁粯鍒�
+                PDImageXObject pdImage = PDImageXObject.createFromByteArray(document, imageBytes, "img");
+                PDAppearanceStream appearance = button.getWidgets().get(0).getNormalAppearanceStream();
+                PDPageContentStream cs = new PDPageContentStream(document, appearance);
+                cs.drawImage(pdImage, 0, 0, appearance.getBBox().getWidth(), appearance.getBBox().getHeight());
+                cs.close();
+            }
+        }
+
+        form.refreshAppearances();
+        form.flatten();
+        document.save(outputStream);
+        document.close();
+    }
+
+
+
+    public static void main(String[] args) throws Exception {
+
+        String template = "D:/temp/zjsf.pdf";
+        String filled = "D:/temp/tes3.pdf";
+        String finalPdf = "D:/temp/commit_final.pdf";
+
+        // Java 8 鍐欐硶锛氱敤 HashMap 鍒濆鍖栧弬鏁�
+        Map<String, String> map = new java.util.HashMap<String, String>();
+        map.put("name", "寮犱笁");
+        map.put("idCard", "440***********1234");
+        map.put("date", "2025-11-27");
+       map.put("signImage", "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIj48c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmVyc2lvbj0iMS4xIiB3aWR0aD0iNjY0LjM5MDYyNSIgaGVpZ2h0PSIzMjgiPjxwYXRoIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIzIiBzdHJva2U9InJnYigwLDAsMCkiIGZpbGw9Im5vbmUiIGQ9Ik0gMTg1LjMgMjk2IEwgMTg1LjMgMjk2IEwgMTgzLjYgMjkwIEwgMTgwLjYgMjgyIEwgMTc3LjYgMjcyIEwgMTc0LjYgMjYwIEwgMTcxLjYgMjQxIEwgMTY3LjYgMjIzIEwgMTYzLjYgMjAyIEwgMTYwLjYgMTg0IEwgMTU4LjYgMTY2IEwgMTU1LjYgMTUwIEwgMTU0LjYgMTM3IEwgMTU0LjYgMTI5IEwgMTU0LjYgMTI0IEwgMTU0LjYgMTIyIEwgMTU1LjYgMTIxIEwgMTU3LjYgMTIxIEwgMTYwLjYgMTIyIEwgMTY5LjYgMTI5IEwgMTc3LjYgMTQwIEwgMTg5LjYgMTYxIEwgMTg5LjYgMTYxIi8+CjxwYXRoIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIzIiBzdHJva2U9InJnYigwLDAsMCkiIGZpbGw9Im5vbmUiIGQ9Ik0gMjk2LjMgMzIzIEwgMjk2LjMgMzIzIEwgMjk2LjYgMzIyIEwgMjk2LjYgMzIwIEwgMjk2LjYgMzE2IEwgMjk1LjYgMzA5IEwgMjkzLjYgMzAzIEwgMjg4LjYgMjkzIEwgMjg0LjYgMjgyIEwgMjc5LjYgMjcwIEwgMjc2LjYgMjU4IEwgMjcyLjYgMjQ1IEwgMjY3LjYgMjM0IEwgMjY0LjYgMjI3IEwgMjYxLjYgMjIxIEwgMjU5LjYgMjE4IEwgMjU1LjYgMjE1IEwgMjUyLjYgMjE1IEwgMjQ4LjYgMjE1IEwgMjQxLjYgMjE1IEwgMjQxLjYgMjE1Ii8+CjxwYXRoIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIzIiBzdHJva2U9InJnYigwLDAsMCkiIGZpbGw9Im5vbmUiIGQ9Ik0gMTcwLjMgMjQ1IEwgMTcwLjMgMjQ1IEwgMTc3LjYgMjQ3IEwgMTg1LjYgMjUxIEwgMTk1LjYgMjU1IEwgMjA5LjYgMjYwIEwgMjIxLjYgMjY2IEwgMjM3LjYgMjcwIEwgMjUxLjYgMjc1IEwgMjY0LjYgMjc4IEwgMjc1LjYgMjc4IEwgMjg0LjYgMjc4IEwgMjkxLjYgMjc4IEwgMjk2LjYgMjc2IEwgMzAwLjYgMjcyIEwgMzAyLjYgMjY5IEwgMzA0LjYgMjYzIEwgMzA1LjYgMjU2IEwgMzA1LjYgMjQ3IEwgMzA1LjYgMjM1IEwgMzAxLjYgMjE5IEwgMjk3LjYgMjA3IEwgMjkyLjYgMTkxIEwgMjg4LjYgMTc5IEwgMjgzLjYgMTcwIEwgMjgyLjYgMTY2IEwgMjgwLjYgMTY1IEwgMjgyLjYgMTY5IEwgMjg5LjYgMTc2IEwgMjk0LjYgMTg0IEwgMzAyLjYgMTk0IEwgMzA4LjYgMjAyIEwgMzE1LjYgMjExIEwgMzIyLjYgMjE5IEwgMzI3LjYgMjIzIEwgMzMzLjYgMjI3IEwgMzM4LjYgMjI5IEwgMzQxLjYgMjMxIEwgMzQzLjYgMjMxIEwgMzQ1LjYgMjMyIEwgMzQ2LjYgMjMyIEwgMzQ3LjYgMjMyIEwgMzQ4LjYgMjMxIEwgMzQ4LjYgMjMwIEwgMzQ4LjYgMjI5IEwgMzQ3LjYgMjI3IEwgMzQ2LjYgMjI2IEwgMzQzLjYgMjI1IEwgMzQxLjYgMjI1IEwgMzM4LjYgMjI2IEwgMzM1LjYgMjI4IEwgMzMyLjYgMjMwIEwgMzMwLjYgMjM1IEwgMzMwLjYgMjM1Ii8+CjxwYXRoIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIzIiBzdHJva2U9InJnYigwLDAsMCkiIGZpbGw9Im5vbmUiIGQ9Ik0gMzE2LjMgMzExIEwgMzE2LjMgMzExIEwgMzE3LjYgMzEyIEwgMzE3LjYgMzEzIEwgMzE4LjYgMzEzIEwgMzE5LjYgMzEzIEwgMzIxLjYgMzEzIEwgMzIzLjYgMzExIEwgMzI4LjYgMzAzIEwgMzM0LjYgMjkzIEwgMzQwLjYgMjc1IEwgMzQ4LjYgMjUzIEwgMzU2LjYgMjI2IEwgMzYzLjYgMTkxIEwgMzcyLjYgMTU0IEwgMzc3LjYgMTE1IEwgMzc5LjYgNzkgTCAzODAuNiA0NiBMIDM4MC42IDE4IEwgMzgwLjYgLTQgTCAzODAuNiAtMjAgTCAzODAuNiAtMzMgTCAzODAuNiAtNDQgTCAzODAuNiAtNTMgTCAzNzkuNiAtNTkgTCAzNzkuNiAtNjIgTCAzNzkuNiAtNjMgTCAzNzguNiAtNjMgTCAzNzcuNiAtNjMgTCAzNzIuNiAtNTkgTCAzNjUuNiAtNTIgTCAzNTcuNiAtNDQgTCAzNTAuNiAtMzQgTCAzNTAuNiAtMzQiLz4KPHBhdGggc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2Utd2lkdGg9IjMiIHN0cm9rZT0icmdiKDAsMCwwKSIgZmlsbD0ibm9uZSIgZD0iTSAyNDQuMyAxNDYgTCAyNDQuMyAxNDYgTCAyNDUuNiAxNDYgTCAyNDguNiAxNDQgTCAyNTIuNiAxNDEgTCAyNTguNiAxMzcgTCAyNjQuNiAxMzIgTCAyNzAuNiAxMjcgTCAyNzkuNiAxMTkgTCAyODYuNiAxMTIgTCAyOTUuNiAxMDMgTCAzMDMuNiA5NSBMIDMwOS42IDg3IEwgMzE1LjYgNzggTCAzMTguNiA3MSBMIDMyMS42IDYyIEwgMzIyLjYgNTYgTCAzMjIuNiA1MSBMIDMyMS42IDQ4IEwgMzE4LjYgNDYgTCAzMTIuNiA0MyBMIDMwNi42IDQxIEwgMjk2LjYgNDAgTCAyOTYuNiA0MCIvPgo8cGF0aCBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS13aWR0aD0iMyIgc3Ryb2tlPSJyZ2IoMCwwLDApIiBmaWxsPSJub25lIiBkPSJNIDI1Ni4zIDExMCBMIDI1Ni4zIDExMCBMIDI1OS42IDExNCBMIDI2NC42IDExOCBMIDI2OC42IDEyMSBMIDI3MS42IDEyMiBMIDI3My42IDEyNCBMIDI3NS42IDEyNSBMIDI3Ny42IDEyNSBMIDI3OC42IDEyNiBMIDI3OS42IDEyNiBMIDI3Ny42IDEyNiBMIDI3My42IDEyNiBMIDI3My42IDEyNiIvPjwvc3ZnPg==");
+
+        // 1. 濉厖鏂囧瓧瀛楁
+        fillPdf(template, filled, map,"D:\\temp\\msyh.ttf");
+
+    }
+}

--
Gitblit v1.8.0