| | |
| | | import freemarker.template.TemplateException; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | |
| | | import org.apache.poi.xwpf.usermodel.*; |
| | | import org.springframework.mock.web.MockMultipartFile; |
| | | import org.springframework.stereotype.Component; |
| | | import org.springframework.web.multipart.MultipartFile; |
| | |
| | | import java.io.*; |
| | | import java.net.URLEncoder; |
| | | import java.nio.charset.StandardCharsets; |
| | | import java.util.Map; |
| | | import java.util.*; |
| | | import java.util.regex.Matcher; |
| | | import java.util.regex.Pattern; |
| | | |
| | | @Slf4j |
| | | @Component |
| | |
| | | } |
| | | |
| | | |
| | | public String generatePdf(String basePackagePath, String templateFileName, Object templateParam, String fileName, String saveDirectory) { |
| | | public String generatePdf(String basePackagePath, String templateFileName, Map<String,Object> templateParam, String fileName, String saveDirectory) { |
| | | try { |
| | | |
| | | fillTemplate(basePackagePath+templateFileName, saveDirectory+fileName+".docx", templateParam); |
| | | |
| | | // 创建 Freemarker 的 Configuration 对象,设置默认的不兼容改进选项 |
| | | Configuration configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS); |
| | | configuration.setDefaultEncoding("utf-8"); |
| | | // 设置模板加载器,加载模板文件 |
| | | configuration.setTemplateLoader(new ClassTemplateLoader(getClass(), basePackagePath)); |
| | | Template t = configuration.getTemplate(templateFileName, "utf-8"); |
| | | |
| | | // 使用 URLEncoder 对文件名进行编码,以防止中文文件名在不同浏览器和操作系统下出现乱码问题 |
| | | // String encodedFileName = URLEncoder.encode(fileName + "_" + System.currentTimeMillis(), "utf-8"); |
| | | String encodedFileName =fileName ; |
| | | |
| | | // 定义保存文件的路径 |
| | | File saveDir = new File(saveDirectory); |
| | | if (!saveDir.exists()) { |
| | | saveDir.mkdirs(); |
| | | } |
| | | |
| | | // 定义文件名 |
| | | String filePath = saveDir.getAbsolutePath() + File.separator + encodedFileName + ".doc"; |
| | | |
| | | // 创建 Writer 对象,用于将生成的文档写到文件中,缓存区大小设为 10KB |
| | | Writer out = new BufferedWriter(new FileWriter(filePath), 10240); |
| | | |
| | | // 将模型数据与模板结合生成 Word 文档,写入到 Writer 对象中 |
| | | t.process(templateParam, out); |
| | | out.close(); |
| | | |
| | | // Configuration configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS); |
| | | // configuration.setDefaultEncoding("utf-8"); |
| | | // // 设置模板加载器,加载模板文件 |
| | | // configuration.setTemplateLoader(new ClassTemplateLoader(getClass(), basePackagePath)); |
| | | // Template t = configuration.getTemplate(templateFileName, "utf-8"); |
| | | // |
| | | // // 使用 URLEncoder 对文件名进行编码,以防止中文文件名在不同浏览器和操作系统下出现乱码问题 |
| | | //// String encodedFileName = URLEncoder.encode(fileName + "_" + System.currentTimeMillis(), "utf-8"); |
| | | // String encodedFileName =fileName ; |
| | | // |
| | | // // 定义保存文件的路径 |
| | | // File saveDir = new File(saveDirectory); |
| | | // if (!saveDir.exists()) { |
| | | // saveDir.mkdirs(); |
| | | // } |
| | | // |
| | | // // 定义文件名 |
| | | // String filePath = saveDir.getAbsolutePath() + File.separator + encodedFileName + ".doc"; |
| | | // |
| | | // // 创建 Writer 对象,用于将生成的文档写到文件中,缓存区大小设为 10KB |
| | | // Writer out = new BufferedWriter(new FileWriter(filePath), 10240); |
| | | // |
| | | // // 将模型数据与模板结合生成 Word 文档,写入到 Writer 对象中 |
| | | // t.process(templateParam, out); |
| | | // out.close(); |
| | | String filePath = saveDirectory + File.separator + fileName + ".docx"; |
| | | File file = new File(filePath); |
| | | |
| | | // 检查文件是否存在 |
| | |
| | | fis.read(fileContent); |
| | | } |
| | | |
| | | String test = pdfUtils.test(encodedFileName + ".doc"); |
| | | String test = pdfUtils.test(fileName + ".docx"); |
| | | // MultipartFile mockMultipartFile = new MockMultipartFile(encodedFileName+".doc", fileContent); |
| | | // String s = ObsUploadUtil.obsUpload(mockMultipartFile); |
| | | return test; |
| | | } catch (IOException | TemplateException e) { |
| | | } catch (IOException e) { |
| | | log.error("生成pdf异常,异常原因:{}", e.getMessage(), e); |
| | | throw new RuntimeException("生成pdf异常,异常原因:" + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | public static void fillTemplate(String templatePath, String outputPath,Map<String, Object> dataMap) { |
| | | try (FileInputStream fis = new FileInputStream(templatePath)) { |
| | | // 设置默认编码为UTF-8 |
| | | System.setProperty("file.encoding", "UTF-8"); |
| | | |
| | | XWPFDocument document = new XWPFDocument(fis); |
| | | |
| | | // 处理段落 |
| | | for (XWPFParagraph paragraph : document.getParagraphs()) { |
| | | replaceParagraph(paragraph, dataMap); |
| | | } |
| | | |
| | | // 处理表格 |
| | | for (XWPFTable table : document.getTables()) { |
| | | for (XWPFTableRow row : table.getRows()) { |
| | | for (XWPFTableCell cell : row.getTableCells()) { |
| | | for (XWPFParagraph paragraph : cell.getParagraphs()) { |
| | | replaceParagraph(paragraph, dataMap); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 使用UTF-8编码保存文件 |
| | | try (FileOutputStream fos = new FileOutputStream(outputPath)) { |
| | | document.write(fos); |
| | | } |
| | | |
| | | System.out.println("模板填充完成!文件保存在: " + outputPath); |
| | | |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | } |
| | | } |
| | | |
| | | private static void replaceParagraph(XWPFParagraph paragraph, Map<String, Object> dataMap) { |
| | | // 获取段落中所有runs |
| | | List<XWPFRun> runs = paragraph.getRuns(); |
| | | if (runs == null || runs.isEmpty()) return; |
| | | |
| | | // 首先合并所有runs的文本,以便正确识别占位符 |
| | | StringBuilder fullText = new StringBuilder(); |
| | | for (XWPFRun run : runs) { |
| | | String text = run.getText(0); |
| | | if (text != null) { |
| | | fullText.append(text); |
| | | } |
| | | } |
| | | |
| | | String paragraphText = fullText.toString(); |
| | | |
| | | // 使用正则表达式查找所有占位符,包括括号内的 |
| | | Pattern pattern = Pattern.compile("\\$\\{[^}]+\\}|\\([^)]*\\$\\{[^}]+\\}[^)]*\\)"); |
| | | Matcher matcher = pattern.matcher(paragraphText); |
| | | |
| | | List<ReplacementInfo> replacements = new ArrayList<>(); |
| | | |
| | | // 收集所有需要替换的信息 |
| | | while (matcher.find()) { |
| | | String matched = matcher.group(); |
| | | int start = matcher.start(); |
| | | int end = matcher.end(); |
| | | |
| | | // 找出涉及到的runs |
| | | int startRun = -1; |
| | | int endRun = -1; |
| | | int currentPos = 0; |
| | | |
| | | for (int i = 0; i < runs.size(); i++) { |
| | | XWPFRun run = runs.get(i); |
| | | String runText = run.getText(0); |
| | | if (runText == null) continue; |
| | | |
| | | int runLength = runText.length(); |
| | | if (startRun == -1 && currentPos + runLength > start) { |
| | | startRun = i; |
| | | } |
| | | if (currentPos + runLength >= end) { |
| | | endRun = i; |
| | | break; |
| | | } |
| | | currentPos += runLength; |
| | | } |
| | | |
| | | if (startRun != -1 && endRun != -1) { |
| | | // 处理括号内的占位符 |
| | | String replacement = processPlaceholder(matched, dataMap); |
| | | replacements.add(new ReplacementInfo(startRun, endRun, matched, replacement)); |
| | | } |
| | | } |
| | | |
| | | // 从后向前替换,避免位置变化影响 |
| | | Collections.sort(replacements, (a, b) -> b.startRun - a.startRun); |
| | | |
| | | for (ReplacementInfo info : replacements) { |
| | | replaceRunRange(paragraph, info); |
| | | } |
| | | } |
| | | |
| | | private static String processPlaceholder(String text, Map<String, Object> dataMap) { |
| | | // 处理括号内的占位符 |
| | | Pattern placeholderPattern = Pattern.compile("\\$\\{([^}]+)\\}"); |
| | | Matcher matcher = placeholderPattern.matcher(text); |
| | | |
| | | StringBuffer result = new StringBuffer(); |
| | | while (matcher.find()) { |
| | | String placeholder = matcher.group(0); // 完整的占位符 |
| | | String key = matcher.group(1); // 占位符中的键 |
| | | String replacement = Objects.nonNull(dataMap.get("${" + key + "}"))?String.valueOf(dataMap.get("${" + key + "}")):""; |
| | | |
| | | if (replacement != null) { |
| | | // 如果在括号内,保留括号 |
| | | if (text.startsWith("(") && text.endsWith(")")) { |
| | | matcher.appendReplacement(result, replacement); |
| | | } else { |
| | | matcher.appendReplacement(result, Matcher.quoteReplacement(replacement)); |
| | | } |
| | | } |
| | | } |
| | | matcher.appendTail(result); |
| | | |
| | | return result.toString(); |
| | | } |
| | | |
| | | private static class ReplacementInfo { |
| | | int startRun; |
| | | int endRun; |
| | | String originalText; |
| | | String replacementText; |
| | | |
| | | ReplacementInfo(int startRun, int endRun, String originalText, String replacementText) { |
| | | this.startRun = startRun; |
| | | this.endRun = endRun; |
| | | this.originalText = originalText; |
| | | this.replacementText = replacementText; |
| | | } |
| | | } |
| | | |
| | | private static void replaceRunRange(XWPFParagraph paragraph, ReplacementInfo info) { |
| | | List<XWPFRun> runs = paragraph.getRuns(); |
| | | |
| | | // 保存第一个run的样式 |
| | | XWPFRun styleRun = runs.get(info.startRun); |
| | | RunStyle style = new RunStyle(styleRun); |
| | | |
| | | // 删除范围内的所有runs |
| | | for (int i = info.endRun; i >= info.startRun; i--) { |
| | | paragraph.removeRun(i); |
| | | } |
| | | |
| | | // 创建新的run并设置文本 |
| | | XWPFRun newRun = paragraph.insertNewRun(info.startRun); |
| | | newRun.setText(info.replacementText); |
| | | |
| | | // 应用样式 |
| | | style.applyStyle(newRun); |
| | | } |
| | | |
| | | // 用于保存和恢复运行样式的辅助类 |
| | | private static class RunStyle { |
| | | String fontFamily; |
| | | int fontSize; |
| | | boolean bold; |
| | | boolean italic; |
| | | String color; |
| | | UnderlinePatterns underline; |
| | | |
| | | RunStyle(XWPFRun run) { |
| | | this.fontFamily = run.getFontFamily(); |
| | | this.fontSize = run.getFontSize(); |
| | | this.bold = run.isBold(); |
| | | this.italic = run.isItalic(); |
| | | this.color = run.getColor(); |
| | | this.underline = run.getUnderline(); |
| | | } |
| | | |
| | | void applyStyle(XWPFRun run) { |
| | | if (fontFamily != null) { |
| | | run.setFontFamily(fontFamily); |
| | | run.setFontFamily(fontFamily, XWPFRun.FontCharRange.eastAsia); |
| | | } |
| | | if (fontSize != -1) { |
| | | run.setFontSize(fontSize); |
| | | } |
| | | run.setBold(bold); |
| | | run.setItalic(italic); |
| | | if (color != null) { |
| | | run.setColor(color); |
| | | } |
| | | if (underline != null) { |
| | | run.setUnderline(underline); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // public String generatePdf(String basePackagePath, String templateFileName, Object templateParam, String fileName, String saveDirectory) { |
| | | // try { |
| | | // // 创建 Freemarker 的 Configuration 对象,设置默认的不兼容改进选项 |
| | | // Configuration configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS); |
| | | // configuration.setDefaultEncoding("utf-8"); |
| | | // // 设置模板加载器,加载模板文件 |
| | | // configuration.setTemplateLoader(new ClassTemplateLoader(getClass(), basePackagePath)); |
| | | // Template t = configuration.getTemplate(templateFileName, "utf-8"); |
| | | // |
| | | // // 使用 URLEncoder 对文件名进行编码,以防止中文文件名在不同浏览器和操作系统下出现乱码问题 |
| | | //// String encodedFileName = URLEncoder.encode(fileName + "_" + System.currentTimeMillis(), "utf-8"); |
| | | // String encodedFileName =fileName ; |
| | | // |
| | | // // 定义保存文件的路径 |
| | | // File saveDir = new File(saveDirectory); |
| | | // if (!saveDir.exists()) { |
| | | // saveDir.mkdirs(); |
| | | // } |
| | | // |
| | | // // 定义文件名 |
| | | // String filePath = saveDir.getAbsolutePath() + File.separator + encodedFileName + ".doc"; |
| | | // |
| | | // // 创建 Writer 对象,用于将生成的文档写到文件中,缓存区大小设为 10KB |
| | | // Writer out = new BufferedWriter(new FileWriter(filePath), 10240); |
| | | // |
| | | // // 将模型数据与模板结合生成 Word 文档,写入到 Writer 对象中 |
| | | // t.process(templateParam, out); |
| | | // out.close(); |
| | | // |
| | | // File file = new File(filePath); |
| | | // |
| | | // // 检查文件是否存在 |
| | | // if (!file.exists()) { |
| | | // throw new FileNotFoundException("文件不存在: " + filePath); |
| | | // } |
| | | // |
| | | // // 读取文件内容 |
| | | // byte[] fileContent = new byte[(int) file.length()]; |
| | | // try (FileInputStream fis = new FileInputStream(file)) { |
| | | // fis.read(fileContent); |
| | | // } |
| | | // |
| | | // String test = pdfUtils.test(encodedFileName + ".doc"); |
| | | //// MultipartFile mockMultipartFile = new MockMultipartFile(encodedFileName+".doc", fileContent); |
| | | //// String s = ObsUploadUtil.obsUpload(mockMultipartFile); |
| | | // return test; |
| | | // } catch (IOException | TemplateException e) { |
| | | // log.error("生成pdf异常,异常原因:{}", e.getMessage(), e); |
| | | // throw new RuntimeException("生成pdf异常,异常原因:" + e.getMessage()); |
| | | // } |
| | | // } |
| | | |
| | | |
| | | |
| | | |