package com.nq.service.impl; import com.nq.common.ServerResponse; import com.nq.pojo.User; import com.nq.service.IUserAgreementService; import com.nq.utils.PropertiesUtil; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.*; import java.text.SimpleDateFormat; import java.util.Date; import org.springframework.web.multipart.MultipartFile; import com.itextpdf.text.*; import com.itextpdf.text.pdf.*; import com.itextpdf.text.pdf.BaseFont; import com.itextpdf.text.pdf.parser.PdfTextExtractor; import com.itextpdf.text.pdf.parser.SimpleTextExtractionStrategy; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.rendering.PDFRenderer; import org.apache.pdfbox.rendering.ImageType; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.List; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.Color; import java.awt.FontMetrics; import java.awt.GraphicsEnvironment; import com.nq.pojo.ContractImage; import com.nq.dao.ContractImageMapper; import com.alibaba.fastjson.JSON; @Service("iUserAgreementService") public class UserAgreementServiceImpl implements IUserAgreementService { private static final Logger log = LoggerFactory.getLogger(UserAgreementServiceImpl.class); @Autowired private com.nq.service.IUserService iUserService; @Autowired private ContractImageMapper contractImageMapper; @Override // 在generateAgreementPdf方法中修改返回逻辑 public ServerResponse generateAgreementPdf(HttpServletRequest request) { try { User user = iUserService.getCurrentUser(request); user = (User) iUserService.findByUserId(user.getId()).getData(); if (user == null) { return ServerResponse.createByErrorMsg("用户未登录"); } if (user.getIsActive()!=2 || StringUtils.isBlank(user.getRealName()) || StringUtils.isBlank(user.getIdCard()) || StringUtils.isBlank(user.getRegAddress())) { return ServerResponse.createByErrorMsg("请先完成实名认证"); } // 检查是否已生成PDF String pdfDir = PropertiesUtil.getProperty("loca.pdf.dir"); File dir = new File(pdfDir); if (!dir.exists()) { dir.mkdirs(); } String pdfFileName = "contract_" + user.getId() + ".pdf"; File pdfFile = new File(dir, pdfFileName); // 如果PDF已存在,删除后重新生成,确保数据是最新的 if (pdfFile.exists()) { log.info("PDF文件已存在,删除后重新生成: {}", pdfFile.getAbsolutePath()); boolean deleted = pdfFile.delete(); if (!deleted) { log.warn("删除旧PDF文件失败: {}", pdfFile.getAbsolutePath()); } } // 从资源中读取PDF模板文件 File pdfTemplate = getTemplateFile("中原证券股票分成协议(电子正式版).pdf"); if (pdfTemplate == null || !pdfTemplate.exists()) { return ServerResponse.createByErrorMsg("PDF模板文件不存在"); } // 从PDF模板生成新的PDF,替换占位符 generatePdfFromTemplate(pdfTemplate, pdfFile, user); // 将PDF转换为图片并返回图片路径数组 List imageUrls = convertPdfToImages(pdfFile, "contract", user.getId()); return ServerResponse.createBySuccess(imageUrls); } catch (Exception e) { log.error("生成用户协议PDF失败", e); return ServerResponse.createByErrorMsg("生成PDF失败:" + e.getMessage()); } } /** * 从PDF模板生成新的PDF,替换占位符 */ private void generatePdfFromTemplate(File templatePdf, File outputPdf, User user) throws Exception { log.info("开始生成PDF,模板路径: {}, 输出路径: {}, 用户: {}", templatePdf.getAbsolutePath(), outputPdf.getAbsolutePath(), user.getId()); PdfReader reader = null; PdfStamper stamper = null; FileOutputStream fos = null; try { reader = new PdfReader(new FileInputStream(templatePdf)); fos = new FileOutputStream(outputPdf); stamper = new PdfStamper(reader, fos); // 设置中文字体,使用EMBEDDED确保字体嵌入PDF,避免转图片时乱码 // 使用itext-asian库提供的中文字体,必须使用EMBEDDED模式 BaseFont baseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED); Font font = new Font(baseFont, 12, Font.NORMAL); log.info("中文字体已加载: {}", baseFont.getPostscriptFontName()); SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日"); String currentDate = sdf.format(new Date()); // 准备替换的数据 String realName = user.getRealName() != null ? user.getRealName() : ""; String idCard = user.getIdCard() != null ? user.getIdCard() : ""; String address = user.getRegAddress() != null ? user.getRegAddress() : ""; log.info("PDF生成数据 - 姓名: {}, 身份证: {}, 地址: {}", realName, idCard, address); // 获取PDF页数 int pageCount = reader.getNumberOfPages(); log.info("PDF模板页数: {}", pageCount); // 在每一页上查找并替换占位符 for (int pageNum = 1; pageNum <= pageCount; pageNum++) { PdfContentByte canvas = stamper.getOverContent(pageNum); Rectangle pageSize = reader.getPageSize(pageNum); // 提取PDF文本查找占位符位置 String pageText = PdfTextExtractor.getTextFromPage(reader, pageNum, new SimpleTextExtractionStrategy()); // 在PDF上查找并替换占位符 replacePlaceholdersInPdf(canvas, pageSize, pageText, realName, idCard, address, currentDate, font, pageNum); log.info("已处理PDF第{}页", pageNum); } stamper.close(); stamper = null; reader.close(); reader = null; fos.close(); fos = null; // 验证生成的文件 if (outputPdf.exists()) { long fileSize = outputPdf.length(); log.info("PDF生成完成,文件路径: {}, 文件大小: {} 字节", outputPdf.getAbsolutePath(), fileSize); if (fileSize == 0) { throw new Exception("生成的PDF文件大小为0"); } } else { throw new Exception("PDF文件生成失败,文件不存在"); } } catch (Exception e) { log.error("生成PDF失败", e); throw e; } finally { if (stamper != null) { try { stamper.close(); } catch (Exception e) { log.error("关闭stamper失败", e); } } if (reader != null) { try { reader.close(); } catch (Exception e) { log.error("关闭reader失败", e); } } if (fos != null) { try { fos.close(); } catch (Exception e) { log.error("关闭输出流失败", e); } } } } /** * 在PDF上替换占位符(使用透明背景图片方式) */ private void replacePlaceholdersInPdf(PdfContentByte canvas, Rectangle pageSize, String pageText, String realName, String idCard, String address, String currentDate, Font font,int pageNum) throws Exception { float margin = 50; float lineHeight = 14; float startY = pageSize.getHeight() - 204; if(pageNum == 1){ // 生成透明背景的姓名图片并插入 File nameImage = createTextImage(realName, font); insertImageToPdf(canvas, nameImage, margin + 75, startY); startY -= lineHeight; // 生成透明背景的地址图片并插入 File addressImage = createTextImage(address, font); insertImageToPdf(canvas, addressImage, margin + 75, startY-2); startY -= lineHeight; // 生成透明背景的身份证号图片并插入 File idCardImage = createTextImage(idCard, font); insertImageToPdf(canvas, idCardImage, margin + 95, startY-2); } if(pageNum == 3){ // 生成透明背景的日期图片并插入 File dateImage1 = createTextImage(currentDate, font); insertImageToPdf(canvas, dateImage1, margin + 60, startY+32); File dateImage2 = createTextImage(currentDate, font); insertImageToPdf(canvas, dateImage2, margin + 60, startY); } } /** * 从资源文件加载字体 */ private java.awt.Font loadFontFromResource(String fontPath, int size) { try { InputStream fontStream = this.getClass().getClassLoader().getResourceAsStream(fontPath); if (fontStream != null) { java.awt.Font font = java.awt.Font.createFont(java.awt.Font.TRUETYPE_FONT, fontStream); font = font.deriveFont(java.awt.Font.PLAIN, size); fontStream.close(); log.info("成功从资源文件加载字体: {}", fontPath); return font; } } catch (Exception e) { log.warn("从资源文件加载字体失败: {}, 错误: {}", fontPath, e.getMessage()); } return null; } /** * 从系统字体目录加载字体文件 */ private java.awt.Font loadFontFromFile(String fontPath, int size) { try { File fontFile = new File(fontPath); if (fontFile.exists()) { java.awt.Font font = java.awt.Font.createFont(java.awt.Font.TRUETYPE_FONT, fontFile); font = font.deriveFont(java.awt.Font.PLAIN, size); log.info("成功从文件加载字体: {}", fontPath); return font; } } catch (Exception e) { log.warn("从文件加载字体失败: {}, 错误: {}", fontPath, e.getMessage()); } return null; } /** * 获取可用的中文字体 */ private java.awt.Font getChineseFont(int size) { // 1. 优先从资源文件加载字体(如果项目中有字体文件) java.awt.Font font = loadFontFromResource("fonts/simsun.ttf", size); if (font != null) return font; font = loadFontFromResource("fonts/simhei.ttf", size); if (font != null) return font; // 2. 从系统字体目录加载(Linux常见字体路径) String[] systemFontPaths = { "/usr/share/fonts/chinese/simsun.ttf", // CentOS/RHEL "/usr/share/fonts/truetype/wqy/wqy-microhei.ttf", // 文泉驿 "/usr/share/fonts/truetype/noto/NotoSansCJK-Regular.ttc", // Noto字体 "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", // DejaVu "/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf", // Liberation "/usr/share/fonts/chinese/SimSun.ttf", "/usr/share/fonts/chinese/SimHei.ttf", "/usr/share/fonts/truetype/arphic/uming.ttc", // 文鼎字体 "/usr/share/fonts/truetype/arphic/ukai.ttc" }; for (String fontPath : systemFontPaths) { font = loadFontFromFile(fontPath, size); if (font != null) return font; } // 3. 按优先级尝试加载系统已安装的字体 String[] fontNames = { "SimSun", // 宋体 (Windows) "SimHei", // 黑体 (Windows) "Microsoft YaHei", // 微软雅黑 (Windows) "STSong", // 华文宋体 "STHeiti", // 华文黑体 "WenQuanYi Micro Hei", // 文泉驿微米黑 (Linux) "WenQuanYi Zen Hei", // 文泉驿正黑 (Linux) "Noto Sans CJK SC", // Noto字体 (Linux) "Droid Sans Fallback", // Android字体 (Linux) "AR PL UMing CN", // 文鼎字体 (Linux) "AR PL UKai CN" // 文鼎字体 (Linux) }; GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); String[] availableFonts = ge.getAvailableFontFamilyNames(); // 查找可用的中文字体 for (String fontName : fontNames) { for (String availableFont : availableFonts) { if (availableFont.equals(fontName)) { try { font = new java.awt.Font(fontName, java.awt.Font.PLAIN, size); log.info("成功加载系统字体: {}", fontName); return font; } catch (Exception e) { log.warn("加载字体{}失败: {}", fontName, e.getMessage()); } } } } // 如果找不到中文字体,尝试使用逻辑字体 log.warn("未找到可用的中文字体,使用SERIF逻辑字体,可能出现乱码"); return new java.awt.Font(java.awt.Font.SERIF, java.awt.Font.PLAIN, size); } /** * 创建透明背景的文本图片 */ private File createTextImage(String text, Font font) throws Exception { if (StringUtils.isBlank(text)) { text = ""; } // 创建临时文件 String tempDir = PropertiesUtil.getProperty("loca.pdf.dir"); File dir = new File(tempDir); if (!dir.exists()) { dir.mkdirs(); } String tempFileName = "text_" + System.currentTimeMillis() + "_" + Thread.currentThread().getId() + ".png"; File imageFile = new File(dir, tempFileName); // 创建透明背景的BufferedImage (TYPE_INT_ARGB 默认透明) BufferedImage image = new BufferedImage(500, 50, BufferedImage.TYPE_INT_ARGB); Graphics2D g2d = image.createGraphics(); // 启用透明背景支持 g2d.setComposite(java.awt.AlphaComposite.SrcOver); // 设置高质量渲染 g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB); g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); // 使用可用的中文字体 java.awt.Font awtFont = getChineseFont(12); g2d.setFont(awtFont); // 使用深黑色,RGB(0, 0, 0) 完全不透明 g2d.setColor(new Color(0, 0, 0, 255)); // 绘制文本 FontMetrics fm = g2d.getFontMetrics(); int textWidth = fm.stringWidth(text); int textHeight = fm.getHeight(); // 调整图片大小以适应文本 if (textWidth > 0 && textHeight > 0) { // TYPE_INT_ARGB 类型默认透明背景,不需要清除 BufferedImage resizedImage = new BufferedImage(textWidth + 20, textHeight + 10, BufferedImage.TYPE_INT_ARGB); Graphics2D g2dResized = resizedImage.createGraphics(); // 启用透明背景支持 g2dResized.setComposite(java.awt.AlphaComposite.SrcOver); // 设置高质量渲染 g2dResized.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2dResized.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB); g2dResized.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); g2dResized.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); // 使用相同的中文字体 g2dResized.setFont(awtFont); // 使用深黑色,RGB(0, 0, 0) 完全不透明 g2dResized.setColor(new Color(0, 0, 0, 255)); // 绘制文本 g2dResized.drawString(text, 10, textHeight); g2dResized.dispose(); // 保存图片 ImageIO.write(resizedImage, "PNG", imageFile); image = resizedImage; } else { // 如果文本为空,创建一个小的透明图片 BufferedImage emptyImage = new BufferedImage(50, 20, BufferedImage.TYPE_INT_ARGB); ImageIO.write(emptyImage, "PNG", imageFile); image = emptyImage; } g2d.dispose(); log.info("已创建透明背景文本图片: {}, 文本: {}", imageFile.getAbsolutePath(), text); return imageFile; } /** * 将图片插入到PDF指定位置 */ private void insertImageToPdf(PdfContentByte canvas, File imageFile, float x, float y) throws Exception { if (imageFile == null || !imageFile.exists()) { log.warn("图片文件不存在: {}", imageFile); return; } try { Image image = Image.getInstance(imageFile.getAbsolutePath()); image.setAbsolutePosition(x, y); canvas.addImage(image); log.info("图片已插入到PDF,位置: ({}, {})", x, y); // 删除临时图片文件 try { imageFile.delete(); } catch (Exception e) { log.warn("删除临时图片文件失败: {}", imageFile.getAbsolutePath(), e); } } catch (Exception e) { log.error("插入图片到PDF失败: {}", imageFile.getAbsolutePath(), e); throw e; } } @Override public ServerResponse viewAgreementPdf(HttpServletRequest request) { try { User user = iUserService.getCurrentUser(request); if (user == null) { return ServerResponse.createByErrorMsg("用户未登录"); } // 从数据库查询已签名的图片 List contractImages = contractImageMapper.selectByUserIdAndType(user.getId(), "contract"); List imageUrls = new ArrayList<>(); if (contractImages != null && !contractImages.isEmpty()) { for (ContractImage image : contractImages) { imageUrls.add(image.getAddress()); } log.info("从数据库查询到用户{}的{}张已签名合同图片", user.getId(), imageUrls.size()); } return ServerResponse.createBySuccess(imageUrls); } catch (Exception e) { log.error("查看用户协议PDF失败", e); return ServerResponse.createByErrorMsg("查看PDF失败: " + e.getMessage()); } } @Override public ServerResponse saveAgreementSignature(MultipartFile signatureFile, HttpServletRequest request) { try { User user = iUserService.getCurrentUser(request); if (user == null) { return ServerResponse.createByErrorMsg("用户未登录"); } if (signatureFile == null || signatureFile.isEmpty()) { return ServerResponse.createByErrorMsg("签名图片不能为空"); } String signatureDir = PropertiesUtil.getProperty("loca.pdf.dir"); File dir = new File(signatureDir); if (!dir.exists()) { dir.mkdirs(); } String signatureFileName = "signature_" + user.getId() + ".png"; File signatureFileLocal = new File(dir, signatureFileName); signatureFile.transferTo(signatureFileLocal); log.info("用户{}的签名图片已保存: {}", user.getId(), signatureFileLocal.getAbsolutePath()); String pdfDir = PropertiesUtil.getProperty("loca.pdf.dir"); String pdfFileName = "contract_" + user.getId() + ".pdf"; File pdfFile = new File(pdfDir, pdfFileName); //删之前的图片 deleteFilesWithPattern(pdfDir,"contract_" + user.getId()); if (pdfFile.exists()) { addSignatureToPdf(pdfFile, signatureFileLocal, user); List imageUrls = convertPdfToImages(pdfFile, "contract", user.getId()); for(String imageUrl : imageUrls) { ContractImage contractImage = new ContractImage(); contractImage.setUserId(user.getId()); contractImage.setContractType("contract"); contractImage.setAddress(imageUrl); contractImage.setAddTime(new Date()); contractImageMapper.insert(contractImage); } iUserService.updateSignedContract(user.getId(), "Y"); } return ServerResponse.createBySuccessMsg("签名保存成功"); } catch (Exception e) { log.error("保存用户协议签名失败", e); return ServerResponse.createByErrorMsg("保存签名失败:" + e.getMessage()); } } /** * 在PDF第三页插入签名图片 */ private void addSignatureToPdf(File pdfFile, File signatureImage, User user) throws Exception { PdfReader reader = new PdfReader(new FileInputStream(pdfFile)); File tempPdf = new File(pdfFile.getParent(), "temp_" + pdfFile.getName()); PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(tempPdf)); int pageCount = reader.getNumberOfPages(); // 在第三页插入签名图片 if (pageCount >= 3) { PdfContentByte canvas = stamper.getOverContent(3); Rectangle pageSize = reader.getPageSize(3); // 添加签名图片 if (signatureImage.exists()) { Image image = Image.getInstance(signatureImage.getAbsolutePath()); // 设置图片大小(根据实际需要调整) image.scaleToFit(100, 100); // 设置图片位置(使用与addHeaderImageToPdf相同的坐标) image.setAbsolutePosition(370, pageSize.getHeight() - 194); canvas.addImage(image); log.info("签名图片已插入到PDF第三页,位置: (370, {})", pageSize.getHeight() - 194); } } stamper.close(); reader.close(); // 替换原PDF文件 if (tempPdf.exists()) { pdfFile.delete(); tempPdf.renameTo(pdfFile); log.info("PDF文件已更新,签名图片已插入"); } } @Override public ServerResponse generateAgreementContractPdf(HttpServletRequest request) { try { User user = iUserService.getCurrentUser(request); user = (User) iUserService.findByUserId(user.getId()).getData(); if (user == null) { return ServerResponse.createByErrorMsg("用户未登录"); } if (user.getIsActive()!=2 || StringUtils.isBlank(user.getRealName()) || StringUtils.isBlank(user.getIdCard()) || StringUtils.isBlank(user.getRegAddress())) { return ServerResponse.createByErrorMsg("用户信息不完整,请先完成实名认证"); } String pdfDir = PropertiesUtil.getProperty("loca.pdf.dir"); File dir = new File(pdfDir); if (!dir.exists()) { dir.mkdirs(); } String pdfFileName = "agreement_" + user.getId() + ".pdf"; File pdfFile = new File(dir, pdfFileName); if (pdfFile.exists()) { boolean deleted = pdfFile.delete(); if (!deleted) { log.warn("删除旧PDF文件失败: {}", pdfFile.getAbsolutePath()); } } // 从资源中读取PDF模板文件 File pdfTemplate = getTemplateFile("中原证券商业核心信息保密协议.pdf"); if (pdfTemplate == null || !pdfTemplate.exists()) { return ServerResponse.createByErrorMsg("PDF模板文件不存在"); } generateContractPdfFromTemplate(pdfTemplate, pdfFile, user); // 将PDF转换为图片并返回图片路径数组 List imageUrls = convertPdfToImages(pdfFile, "agreement", user.getId()); return ServerResponse.createBySuccess(imageUrls); } catch (Exception e) { log.error("生成用户合同PDF失败", e); return ServerResponse.createByErrorMsg("生成PDF失败:" + e.getMessage()); } } @Override public void viewAgreementContractPdf(HttpServletRequest request, HttpServletResponse response) { try { User user = iUserService.getCurrentUser(request); if (user == null) { response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "用户未登录"); return; } // 从数据库查询已签名的图片 List contractImages = contractImageMapper.selectByUserIdAndType(user.getId(), "agreement"); List imageUrls = new ArrayList<>(); if (contractImages != null && !contractImages.isEmpty()) { for (ContractImage image : contractImages) { imageUrls.add(image.getAddress()); } log.info("从数据库查询到用户{}的{}张已签名保密协议图片", user.getId(), imageUrls.size()); } response.setContentType("application/json;charset=UTF-8"); response.getWriter().write(JSON.toJSONString(ServerResponse.createBySuccess(imageUrls))); response.getWriter().flush(); } catch (Exception e) { log.error("查看用户合同PDF失败", e); try { if (!response.isCommitted()) { response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "查看PDF失败: " + e.getMessage()); } } catch (IOException ex) { log.error("发送错误响应失败", ex); } } } @Override public ServerResponse saveAgreementContractSignature(MultipartFile signatureFile, HttpServletRequest request) { try { User user = iUserService.getCurrentUser(request); if (user == null) { return ServerResponse.createByErrorMsg("用户未登录"); } if (signatureFile == null || signatureFile.isEmpty()) { return ServerResponse.createByErrorMsg("签名图片不能为空"); } String signatureDir = PropertiesUtil.getProperty("loca.pdf.dir"); File dir = new File(signatureDir); if (!dir.exists()) { dir.mkdirs(); } String signatureFileName = "agreement_signature_" + user.getId() + ".png"; File signatureFileLocal = new File(dir, signatureFileName); signatureFile.transferTo(signatureFileLocal); log.info("用户{}的合同签名图片已保存: {}", user.getId(), signatureFileLocal.getAbsolutePath()); String pdfDir = PropertiesUtil.getProperty("loca.pdf.dir"); String pdfFileName = "agreement_" + user.getId() + ".pdf"; File pdfFile = new File(pdfDir, pdfFileName); //删之前的图片 deleteFilesWithPattern(pdfDir,"agreement_" + user.getId()); if (pdfFile.exists()) { addSignatureToContractPdf(pdfFile, signatureFileLocal, user); List imageUrls = convertPdfToImages(pdfFile, "agreement", user.getId()); for (String imageUrl : imageUrls) { ContractImage contractImage = new ContractImage(); contractImage.setUserId(user.getId()); contractImage.setContractType("agreement"); contractImage.setAddress(imageUrl); contractImage.setAddTime(new Date()); contractImageMapper.insert(contractImage); } iUserService.updateSignedAgreement(user.getId(), "Y"); } return ServerResponse.createBySuccessMsg("签名保存成功"); } catch (Exception e) { log.error("保存用户合同签名失败", e); return ServerResponse.createByErrorMsg("保存签名失败:" + e.getMessage()); } } private void generateContractPdfFromTemplate(File templatePdf, File outputPdf, User user) throws Exception { PdfReader reader = new PdfReader(new FileInputStream(templatePdf)); PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(outputPdf)); // 设置中文字体,使用EMBEDDED确保字体嵌入PDF,避免转图片时乱码 BaseFont baseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED); Font font = new Font(baseFont, 12, Font.NORMAL); SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日"); String currentDate = sdf.format(new Date()); int pageCount = reader.getNumberOfPages(); for (int pageNum = 1; pageNum <= pageCount; pageNum++) { PdfContentByte canvas = stamper.getOverContent(pageNum); Rectangle pageSize = reader.getPageSize(pageNum); replacePlaceholdersInContractPdf(canvas, pageSize, currentDate, font, pageNum); } stamper.close(); reader.close(); } private void replacePlaceholdersInContractPdf(PdfContentByte canvas, Rectangle pageSize, String currentDate, Font font, int pageNum) throws Exception { float margin = 50; float startY = pageSize.getHeight() - 374; if(pageNum == 2){ // 生成透明背景的日期图片并插入 File dateImage = createTextImage(currentDate, font); insertImageToPdf(canvas, dateImage, margin + 320, startY); } } private void addSignatureToContractPdf(File pdfFile, File signatureImage, User user) throws Exception { PdfReader reader = new PdfReader(new FileInputStream(pdfFile)); File tempPdf = new File(pdfFile.getParent(), "temp_" + pdfFile.getName()); PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(tempPdf)); int pageCount = reader.getNumberOfPages(); if (pageCount >= 2) { PdfContentByte canvas = stamper.getOverContent(2); Rectangle pageSize = reader.getPageSize(2); if (signatureImage.exists()) { Image image = Image.getInstance(signatureImage.getAbsolutePath()); image.scaleToFit(100, 100); image.setAbsolutePosition(435, pageSize.getHeight() - 288); canvas.addImage(image); log.info("签名图片已插入到合同PDF第二页"); } } stamper.close(); reader.close(); if (tempPdf.exists()) { pdfFile.delete(); tempPdf.renameTo(pdfFile); log.info("合同PDF文件已更新,签名图片已插入"); } } /** * 从固定目录获取模板文件 */ private File getTemplateFile(String templateFileName) { try { String templateDir = PropertiesUtil.getProperty("pdf.template.dir"); if (StringUtils.isBlank(templateDir)) { log.error("PDF模板目录配置不存在: pdf.template.dir"); return null; } File dir = new File(templateDir); if (!dir.exists()) { log.error("PDF模板目录不存在: {}", templateDir); return null; } File templateFile = new File(dir, templateFileName); if (!templateFile.exists()) { log.error("PDF模板文件不存在: {}", templateFile.getAbsolutePath()); return null; } log.info("使用模板文件: {}", templateFile.getAbsolutePath()); return templateFile; } catch (Exception e) { log.error("获取模板文件失败: {}", templateFileName, e); return null; } } /** * 将PDF转换为图片 */ private List convertPdfToImages(File pdfFile, String contractType, Integer userId) throws Exception { List imageUrls = new ArrayList<>(); String imagePrefix = PropertiesUtil.getProperty("pdf.server.http.prefix"); String pdfDir = PropertiesUtil.getProperty("loca.pdf.dir"); long timestamp = System.currentTimeMillis(); PDDocument document = null; try { document = PDDocument.load(pdfFile); // 创建PDFRenderer,使用高质量渲染 PDFRenderer pdfRenderer = new PDFRenderer(document); int pageCount = document.getNumberOfPages(); log.info("PDF总页数: {}", pageCount); for (int page = 0; page < pageCount; page++) { // 使用RGB模式渲染,DPI设置为300确保文字清晰 // ImageType.RGB可以更好地处理中文字体 BufferedImage image = pdfRenderer.renderImageWithDPI(page, 300, ImageType.RGB); // 对图片进行优化处理,确保文字清晰 BufferedImage optimizedImage = optimizeImage(image); String imageFileName = contractType + "_" +userId +"_" + timestamp + ".png"; if (page > 0) { imageFileName = contractType+ "_" + userId + "_" + timestamp + "_" + (page + 1) + ".png"; } File imageFile = new File(pdfDir, imageFileName); ImageIO.write(optimizedImage, "png", imageFile); String imageUrl = imagePrefix + "/" + imageFileName; imageUrls.add(imageUrl); log.info("PDF第{}页已转换为图片: {}", page + 1, imageUrl); } } finally { if (document != null) { document.close(); } } return imageUrls; } /** * 优化图片,确保文字清晰 */ private BufferedImage optimizeImage(BufferedImage originalImage) { int width = originalImage.getWidth(); int height = originalImage.getHeight(); BufferedImage optimizedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics2D g2d = optimizedImage.createGraphics(); // 设置高质量渲染 g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); g2d.drawImage(originalImage, 0, 0, null); g2d.dispose(); return optimizedImage; } @Override public ServerResponse getContractImages(HttpServletRequest request) { try { User user = iUserService.getCurrentUser(request); if (user == null) { return ServerResponse.createByErrorMsg("用户未登录"); } List images = contractImageMapper.selectByUserIdAndType(user.getId(), "contract"); List imageUrls = new ArrayList<>(); for (ContractImage image : images) { imageUrls.add(image.getAddress()); } return ServerResponse.createBySuccess(imageUrls); } catch (Exception e) { log.error("查询合同图片列表失败", e); return ServerResponse.createByErrorMsg("查询失败:" + e.getMessage()); } } @Override public ServerResponse getAgreementImages(HttpServletRequest request) { try { User user = iUserService.getCurrentUser(request); if (user == null) { return ServerResponse.createByErrorMsg("用户未登录"); } List images = contractImageMapper.selectByUserIdAndType(user.getId(), "agreement"); List imageUrls = new ArrayList<>(); for (ContractImage image : images) { imageUrls.add(image.getAddress()); } return ServerResponse.createBySuccess(imageUrls); } catch (Exception e) { log.error("查询保密协议图片列表失败", e); return ServerResponse.createByErrorMsg("查询失败:" + e.getMessage()); } } public void deleteFilesWithPattern(String dirPath, String userId) { File directory = new File(dirPath); if (!directory.exists() || !directory.isDirectory()) { System.out.println("目录不存在或路径错误: " + dirPath); return; } File[] files = directory.listFiles((dir, name) -> name.contains(userId + "_")); if (files != null) { for (File file : files) { if (file.delete()) { System.out.println("已删除文件: " + file.getName()); } else { System.out.println("删除失败: " + file.getName()); } } } } }