ln_ry20250512
parent
7f2d1a703c
commit
59b14f4026
@ -0,0 +1,65 @@
|
|||||||
|
package com.ruoyi.web.controller.pdf.controller;
|
||||||
|
|
||||||
|
|
||||||
|
import com.ruoyi.web.controller.pdf.service.LeavePdfService;
|
||||||
|
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/LeavePdf")
|
||||||
|
public class LeavePdfController {
|
||||||
|
|
||||||
|
private final LeavePdfService LeavePdfService;
|
||||||
|
|
||||||
|
public LeavePdfController(LeavePdfService LeavePdfService) {
|
||||||
|
this.LeavePdfService = LeavePdfService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping(value = "/LeaveFill", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||||
|
public ResponseEntity<byte[]> fillPdfForm(
|
||||||
|
@RequestParam(required = false) String name,
|
||||||
|
@RequestParam(required = false) String political,
|
||||||
|
@RequestParam(required = false) String phone,
|
||||||
|
@RequestParam(required = false) String positionCapacity,
|
||||||
|
@RequestParam(required = false) String smPost,
|
||||||
|
@RequestParam(required = false) String smGrade,
|
||||||
|
@RequestParam(required = false) MultipartFile photo) throws Exception {
|
||||||
|
|
||||||
|
// 构建表单数据
|
||||||
|
Map<String, String> formData = new HashMap<>();
|
||||||
|
if (name != null) formData.put("姓名", name);
|
||||||
|
if (political != null) formData.put("政治面貌", political);
|
||||||
|
if (phone != null) formData.put("联系电话", phone);
|
||||||
|
if (positionCapacity != null) formData.put("职务职级", positionCapacity);
|
||||||
|
if (smPost != null) formData.put("涉密岗位", smPost);
|
||||||
|
if (smGrade != null) formData.put("涉密等级", smGrade);
|
||||||
|
|
||||||
|
// 构建图片数据
|
||||||
|
Map<String, MultipartFile> imageData = new HashMap<>();
|
||||||
|
if (photo != null && !photo.isEmpty()) {
|
||||||
|
imageData.put("照片", photo);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成PDF
|
||||||
|
ByteArrayOutputStream outputStream = LeavePdfService.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());
|
||||||
|
}
|
||||||
|
}
|
@ -1,13 +0,0 @@
|
|||||||
package com.ruoyi.web.controller.pdf.dto;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
public class PdfFormRequest {
|
|
||||||
private Map<String, String> formData = new HashMap<>();
|
|
||||||
private Map<String, MultipartFile> imageData = new HashMap<>();
|
|
||||||
}
|
|
@ -0,0 +1,177 @@
|
|||||||
|
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 LeavePdfService {
|
||||||
|
|
||||||
|
// 定义最大和最小字体大小,用于动态调整
|
||||||
|
private static final float MAX_FONT_SIZE = 14;
|
||||||
|
private static final float MIN_FONT_SIZE = 8;
|
||||||
|
// 需要特殊处理的字段列表
|
||||||
|
private static final String[] AUTO_WRAP_FIELDS = {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
public ByteArrayOutputStream fillForm(Map<String, String> formData,
|
||||||
|
Map<String, MultipartFile> 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/smrylzlgspb.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<String, String> 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)) {
|
||||||
|
textField.setMultiline(true);
|
||||||
|
float fontSize = calculateFontSize(fieldValue);
|
||||||
|
textField.setDefaultAppearance("/" + font.getName() + " " + fontSize + " 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<String, MultipartFile> imageData) throws IOException {
|
||||||
|
for (Map.Entry<String, MultipartFile> 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; // 可以根据实际情况调整2
|
||||||
|
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 calculateFontSize(String fieldValue) {
|
||||||
|
int textLength = fieldValue.length();
|
||||||
|
float fontSize = 10;
|
||||||
|
if (textLength > 30) {
|
||||||
|
fontSize = Math.max(MIN_FONT_SIZE, 10 - ((textLength - 30) / 5));
|
||||||
|
}
|
||||||
|
return fontSize;
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
@ -0,0 +1,10 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Title</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in new issue