parent
e51ee4d9b7
commit
b9c213ddf4
@ -0,0 +1,85 @@
|
||||
package com.ruoyi.web.controller.pdf.controller;
|
||||
|
||||
|
||||
import com.ruoyi.web.controller.pdf.service.PdfService;
|
||||
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/pdf")
|
||||
public class PdfController {
|
||||
|
||||
private final PdfService pdfService;
|
||||
|
||||
public PdfController(PdfService pdfService) {
|
||||
this.pdfService = pdfService;
|
||||
}
|
||||
|
||||
@PostMapping(value = "/fill", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||
public ResponseEntity<byte[]> fillPdfForm(
|
||||
@RequestParam(required = false) String name,
|
||||
@RequestParam(required = false) String sex,
|
||||
@RequestParam(required = false) String nationa,
|
||||
@RequestParam(required = false) String formerName,
|
||||
@RequestParam(required = false) String nationality,
|
||||
@RequestParam(required = false) String maritalStatus,
|
||||
@RequestParam(required = false) String political,
|
||||
@RequestParam(required = false) String phone,
|
||||
@RequestParam(required = false) String cerno,
|
||||
@RequestParam(required = false) String address,
|
||||
@RequestParam(required = false) String registeredAuthority,
|
||||
@RequestParam(required = false) String permanentAddress,
|
||||
@RequestParam(required = false) String residentBureau,
|
||||
@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 (sex != null) formData.put("性别", sex);
|
||||
if (nationa != null) formData.put("国籍", nationa);
|
||||
if (formerName != null) formData.put("曾用名", formerName);
|
||||
if (nationality != null) formData.put("民族", nationality);
|
||||
if (maritalStatus != null) formData.put("婚姻状况", maritalStatus);
|
||||
if (political != null) formData.put("政治面貌", political);
|
||||
if (phone != null) formData.put("联系方式", phone);
|
||||
if (cerno != null) formData.put("身份证号", cerno);
|
||||
if (address != null) formData.put("户籍地址", address);
|
||||
if (registeredAuthority != null) formData.put("户籍地公安机关", registeredAuthority);
|
||||
if (permanentAddress != null) formData.put("常住地址", permanentAddress);
|
||||
if (residentBureau != null) formData.put("常住地公安机关", residentBureau);
|
||||
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 = pdfService.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());
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
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 PdfService {
|
||||
|
||||
// 定义最大和最小字体大小,用于动态调整
|
||||
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/smryscb.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 = 435; // 可以根据实际情况调整2
|
||||
float y = 620; // 可以根据实际情况调整
|
||||
|
||||
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.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 51 KiB |
Loading…
Reference in new issue