diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/pdf/controller/ChangePdfController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/pdf/controller/ChangePdfController.java new file mode 100644 index 00000000..9b09181d --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/pdf/controller/ChangePdfController.java @@ -0,0 +1,60 @@ +package com.ruoyi.web.controller.pdf.controller; + +import com.ruoyi.web.controller.pdf.service.ChangePdfService; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +import java.io.ByteArrayOutputStream; +import java.util.HashMap; +import java.util.Map; +@RestController +@RequestMapping("/api/ChangePdf") +public class ChangePdfController { + private final ChangePdfService ChangePdfService; + + public ChangePdfController(ChangePdfService changePdfService) { + this.ChangePdfService = changePdfService; + } + + @PostMapping(value = "/ChangeFill", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public ResponseEntity fillPdfForm( + @RequestParam(required = false) String name, + @RequestParam(required = false) String sex, + @RequestParam(required = false) String phone, + @RequestParam(required = false) String smPost, + @RequestParam(required = false) String smGrade, + @RequestParam(required = false) MultipartFile photo) throws Exception { + + // 构建表单数据 + Map formData = new HashMap<>(); + if (name != null) formData.put("姓名", name); + if (sex != null) formData.put("性别", sex); + if (phone != null) formData.put("联系电话", phone); + if (smPost != null) formData.put("涉密岗位", smPost); + if (smPost != null) formData.put("原涉密等级", smGrade); + + // 构建图片数据 + Map imageData = new HashMap<>(); + if (photo != null && !photo.isEmpty()) { + imageData.put("照片", photo); + } + + // 生成PDF + ByteArrayOutputStream outputStream = ChangePdfService.fillForm(formData, imageData); + + // 返回PDF文件 + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_PDF); + headers.setContentDispositionFormData("attachment", "output.pdf"); + + return ResponseEntity.ok() + .headers(headers) + .body(outputStream.toByteArray()); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/pdf/service/ChangePdfService.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/pdf/service/ChangePdfService.java new file mode 100644 index 00000000..f824e095 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/pdf/service/ChangePdfService.java @@ -0,0 +1,199 @@ +package com.ruoyi.web.controller.pdf.service; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.PDPageContentStream; +import org.apache.pdfbox.pdmodel.font.PDType0Font; +import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject; +import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm; +import org.apache.pdfbox.pdmodel.interactive.form.PDField; +import org.apache.pdfbox.pdmodel.interactive.form.PDTextField; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.util.Map; +@Service +public class ChangePdfService { + // 定义最大和最小字体大小,用于动态调整 + private static final float MAX_FONT_SIZE = 14; + private static final float MIN_FONT_SIZE = 8; + // 预估的字段宽度(根据实际PDF调整) + private static final float ESTIMATED_FIELD_WIDTH = 100f; + // 需要特殊处理的字段列表 + private static final String[] AUTO_WRAP_FIELDS = { + "姓名","性别","联系电话","原涉密岗位名称" + }; + + public ByteArrayOutputStream fillForm(Map formData, + Map imageData) throws IOException { + // 禁用系统字体扫描,只使用自定义字体 + System.setProperty("org.apache.pdfbox.rendering.UseSystemFonts", "false"); + // 提高字体解析警告的日志级别 + System.setProperty("org.apache.commons.logging.Log", "org.apache.commons.logging.impl.SimpleLog"); + System.setProperty("org.apache.commons.logging.simplelog.log.org.apache.fontbox", "ERROR"); + + PDDocument document = null; + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + + try { + // 加载模板PDF + ClassLoader classLoader = getClass().getClassLoader(); + File templateFile = new File(classLoader.getResource("static/file/pdf/smrydlgwspb.pdf").getFile()); + document = PDDocument.load(templateFile); + PDAcroForm acroForm = document.getDocumentCatalog().getAcroForm(); + + if (acroForm == null) { + throw new IOException("PDF不包含表单域"); + } + + // 加载中文字体 + PDType0Font font = PDType0Font.load(document, + new File(classLoader.getResource("static/file/pdf/STSONG.TTF").getFile())); + + // 设置需要更新表单外观 + acroForm.setNeedAppearances(true); + + // 填充文本表单 + for (Map.Entry entry : formData.entrySet()) { + String fieldName = entry.getKey(); + String fieldValue = entry.getValue(); + PDField field = acroForm.getField(fieldName); + if (field != null) { + // 设置表单域为不可编辑 + field.setReadOnly(true); + // 设置表单域不高亮显示 +// for (PDAnnotationWidget widget : field.getWidgets()) { +// widget.setHighlightMode(PDAnnotationWidget.HIGHLIGHT_MODE_NONE); +// } + if (fieldValue != null && !fieldValue.isEmpty()) { + field.setValue(fieldValue); + if (field instanceof PDTextField) { + PDTextField textField = (PDTextField) field; + if (isAutoWrapField(fieldName)) { + // 计算文本在最大字体下的宽度 + float textWidth = calculateTextWidth(fieldValue, font, MAX_FONT_SIZE); + // 如果文本宽度超过预估字段宽度,启用自动换行并调整字体 + if (textWidth > ESTIMATED_FIELD_WIDTH) { + textField.setMultiline(true); + float fontSize = calculateFontSize(fieldValue, font); + textField.setDefaultAppearance("/" + font.getName() + " " + fontSize + " Tf 0 g"); + } else { + // 文本未超出,使用默认字体和不换行 + textField.setMultiline(false); + textField.setDefaultAppearance("/" + font.getName() + " " + MAX_FONT_SIZE + " Tf 0 g"); + } + } else { + textField.setDefaultAppearance("/" + font.getName() + " 12 Tf 0 g"); + } + } + } else { + // 无内容时,也设置默认外观 + if (field instanceof PDTextField) { + ((PDTextField) field).setDefaultAppearance("/" + font.getName() + " 12 Tf 0 g"); + } + } + } + } + + // 插入图片 + insertImages(document, acroForm, imageData); + + // 保存到输出流 + document.save(outputStream); + + } catch (Exception e) { + e.printStackTrace(); // 打印异常信息,方便调试 + throw new IOException("处理PDF时出错", e); + } finally { + if (document != null) { + document.close(); + } + } + + return outputStream; + } + + private void insertImages(PDDocument document, PDAcroForm acroForm, + Map imageData) throws IOException { + for (Map.Entry entry : imageData.entrySet()) { + String fieldName = entry.getKey(); + MultipartFile file = entry.getValue(); + + PDField refField = acroForm.getField(fieldName); + // 设置表单域为不可编辑 + if (refField != null) { + refField.setReadOnly(true); + } + if (refField != null && !file.isEmpty()) { + // 假设图片插入到第一页,你可以根据实际情况修改 + PDPage page = document.getPage(0); + + // 手动指定图片插入位置 + float x = 440; // 可以根据实际情况调整 + float y = 650; // 可以根据实际情况调整 + + PDImageXObject image = null; + PDPageContentStream contentStream = null; + try { + // 从上传的文件创建图片对象 + image = PDImageXObject.createFromByteArray(document, file.getBytes(), file.getOriginalFilename()); + + // 计算图片尺寸(按比例缩放) + float imageWidth = image.getWidth(); + float imageHeight = image.getHeight(); + float maxWidth = 100; + float maxHeight = 100; + + float scale = Math.min(maxWidth / imageWidth, maxHeight / imageHeight); + imageWidth *= scale; + imageHeight *= scale; + + // 在指定位置绘制图片 + contentStream = new PDPageContentStream( + document, page, PDPageContentStream.AppendMode.APPEND, true); + contentStream.drawImage(image, x, y, imageWidth, imageHeight); + } finally { + if (contentStream != null) { + contentStream.close(); + } + } + } + } + } + + // 判断是否是需要自动换行的字段 + private boolean isAutoWrapField(String fieldName) { + for (String autoWrapField : AUTO_WRAP_FIELDS) { + if (autoWrapField.equals(fieldName)) { + return true; + } + } + return false; + } + + // 计算文本在指定字体和大小下的宽度 + private float calculateTextWidth(String text, PDType0Font font, float fontSize) throws IOException { + if (text == null || text.isEmpty()) { + return 0; + } + // 计算文本宽度,考虑字符间距 + return font.getStringWidth(text) / 1000 * fontSize; + } + + // 计算适应字段的字体大小 + private float calculateFontSize(String fieldValue, PDType0Font font) throws IOException { + float fontSize = MAX_FONT_SIZE; + float textWidth = calculateTextWidth(fieldValue, font, fontSize); + + // 循环降低字体大小直到文本适合预估宽度 + while (textWidth > ESTIMATED_FIELD_WIDTH && fontSize > MIN_FONT_SIZE) { + fontSize -= 0.5f; + textWidth = calculateTextWidth(fieldValue, font, fontSize); + } + + return fontSize; + } +}