From 8be8b3ab626d769cc1745b6b3ec5506fd489831c Mon Sep 17 00:00:00 2001
From: liujie <1793218484@qq.com>
Date: 星期一, 26 一月 2026 11:02:31 +0800
Subject: [PATCH] update
---
admin-server/src/main/java/com/estate/expert/admin/utils/wordtopdf/PdfUtils.java | 432 ++++++++++++++++++++++++++++
admin-server/src/main/java/com/estate/expert/admin/controller/stat/ReportController.java | 55 ---
admin-server/src/main/java/com/estate/expert/admin/utils/wordtopdf/WordUtil.java | 336 ++++++++++++++++++++++
admin-server/pom.xml | 27 +
4 files changed, 805 insertions(+), 45 deletions(-)
diff --git a/admin-server/pom.xml b/admin-server/pom.xml
index e304b9c..aae340b 100644
--- a/admin-server/pom.xml
+++ b/admin-server/pom.xml
@@ -22,6 +22,29 @@
<dependencies>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-test</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.documents4j</groupId>
+ <artifactId>documents4j-local</artifactId>
+ <version>1.0.3</version>
+
+ </dependency>
+ <dependency>
+ <groupId>com.documents4j</groupId>
+ <artifactId>documents4j-transformer-msoffice-word</artifactId>
+ <version>1.0.3</version>
+ </dependency>
+ <dependency>
+ <groupId>org.freemarker</groupId>
+ <artifactId>freemarker</artifactId>
+ <version>2.3.33</version>
+ </dependency>
+
<!-- JODConverter for LibreOffice integration -->
<dependency>
<groupId>org.jodconverter</groupId>
@@ -197,6 +220,10 @@
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-test</artifactId>
+ </dependency>
</dependencies>
<build>
diff --git a/admin-server/src/main/java/com/estate/expert/admin/controller/stat/ReportController.java b/admin-server/src/main/java/com/estate/expert/admin/controller/stat/ReportController.java
index 4292b8a..a3793cd 100644
--- a/admin-server/src/main/java/com/estate/expert/admin/controller/stat/ReportController.java
+++ b/admin-server/src/main/java/com/estate/expert/admin/controller/stat/ReportController.java
@@ -6,6 +6,8 @@
import com.estate.expert.admin.service.ReportService;
import com.estate.expert.admin.service.ReportTemplateService;
import com.estate.expert.admin.utils.WordToPdfConverter;
+import com.estate.expert.admin.utils.wordtopdf.PdfUtils;
+import com.estate.expert.admin.utils.wordtopdf.WordUtil;
import com.estate.expert.common.auth.IgnoreAuth;
import com.estate.expert.common.rpc.Result;
import fr.opensagres.poi.xwpf.converter.pdf.PdfConverter;
@@ -36,7 +38,9 @@
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
@Api(tags = "管理后台/统计/报告下载")
@Slf4j
@@ -92,7 +96,7 @@
}
@Autowired
- private WordToPdfConverter wordToPdfConverter;
+ private PdfUtils pdfUtils;
@ApiOperation("模糊评价报告PDF下载(新接口)- 通过Word链接转换")
@@ -101,10 +105,10 @@
public void fuzzyReportPdfNew(@ApiParam("Word文档URL") @RequestParam(value = "wordUrl") String wordUrl,
@ApiParam("文件名(可选)") @RequestParam(value = "fileName", required = false) String fileName,
HttpServletResponse response) throws Exception {
- downloadPdfFromWordUrl(wordUrl, fileName, response);
try {
- wordToPdfConverter.convertUrlToPdf(wordUrl,"C:\\Users\\Admin\\Desktop\\test.pdf");
+ String test = pdfUtils.test("1769239688785.docx");
+
} catch (Exception e) {
throw new RuntimeException(e);
}
@@ -128,45 +132,6 @@
@ApiParam("报告模版ID") @RequestParam(value = "templateId") Integer templateId,
HttpServletResponse response) throws IOException {
downloadPdf(checkTaskId, templateId, response, "打分评价", false);
- }
-
- @ApiOperation("获取报告图片列表")
- @GetMapping("/getReportImages")
- @ResponseBody
- @IgnoreAuth
- public Result<List<String>> getReportImages(@ApiParam("文件UUID") @RequestParam(value = "uuid") String uuid) {
- try {
- List<String> imageFiles = new ArrayList<>();
- Path imageDir = Path.of(REPORT_UPLOAD_DIR, "images").toAbsolutePath().normalize();
-
- if (!Files.exists(imageDir)) {
- return Result.success(imageFiles);
- }
-
- // 查找所有匹配的图片文件
- try (var stream = Files.list(imageDir)) {
- stream.filter(path -> {
- String fileName = path.getFileName().toString();
- return fileName.startsWith(uuid + "_page_") && fileName.endsWith(".png");
- })
- .sorted((p1, p2) -> {
- // 按页码排序
- String name1 = p1.getFileName().toString();
- String name2 = p2.getFileName().toString();
- int page1 = extractPageNumber(name1);
- int page2 = extractPageNumber(name2);
- return Integer.compare(page1, page2);
- })
- .forEach(path -> imageFiles.add(path.getFileName().toString()));
- }
-
- log.info("查询报告图片列表: uuid={}, 共{}张图片", uuid, imageFiles.size());
- return Result.success(imageFiles);
-
- } catch (Exception e) {
- log.error("查询报告图片列表失败: uuid={}", uuid, e);
- return Result.error("查询图片列表失败: " + e.getMessage());
- }
}
@ApiOperation("检查报告转换状态")
@@ -235,9 +200,9 @@
out.flush();
// 保存文件到服务器
- if(uuid!=null && !uuid.isEmpty()){
- saveFileToServer(docBytes, docName + ".docx",uuid);
- }
+// if(uuid!=null && !uuid.isEmpty()){
+// saveFileToServer(docBytes, docName + ".docx",uuid);
+// }
} catch (Exception e) {
log.error("文档下载失败", e);
throw new IOException("文档下载失败: " + e.getMessage(), e);
diff --git a/admin-server/src/main/java/com/estate/expert/admin/utils/wordtopdf/PdfUtils.java b/admin-server/src/main/java/com/estate/expert/admin/utils/wordtopdf/PdfUtils.java
new file mode 100644
index 0000000..b7a1039
--- /dev/null
+++ b/admin-server/src/main/java/com/estate/expert/admin/utils/wordtopdf/PdfUtils.java
@@ -0,0 +1,432 @@
+package com.estate.expert.admin.utils.wordtopdf;
+
+import com.documents4j.api.DocumentType;
+import com.documents4j.api.IConverter;
+import com.documents4j.job.LocalConverter;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.mock.web.MockMultipartFile;
+import org.springframework.stereotype.Component;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.*;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+@Slf4j
+@Component
+public class PdfUtils {
+
+ public String wordToPdf(String url,String filePath, String fileName) {
+ try {
+ DocumentType documentType = DocumentType.DOC;
+ if(url.contains(".docx")){
+ documentType = DocumentType.DOCX;
+ }
+ if(url.contains(".doc")){
+ documentType = DocumentType.DOC;
+ }
+ if(url.contains(".xlsx")){
+ documentType = DocumentType.XLSX;
+ }else {
+ if(url.contains(".xls")){
+ documentType = DocumentType.XLS;
+ }
+ }
+ InputStream inputStream = new URL(url).openStream();
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ IConverter converter = LocalConverter.builder().build();
+ converter.convert(inputStream)
+ .as(documentType)
+ .to(stream)
+ .as(DocumentType.PDF).execute();
+
+ //上传图片
+ byte2File(stream.toByteArray(),filePath + "/pdf",fileName.substring(0,fileName.lastIndexOf(".")) + ".pdf");
+ MultipartFile multipartFile = convertToMultipartFile(stream,fileName.substring(0,fileName.lastIndexOf(".")) );
+
+
+ String s = saveFileToServer(multipartFile.getBytes(), fileName, "1123121");
+ stream.close();
+ inputStream.close();
+ return s;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+ @Value("${file.upload.dir}/reports")
+ private String REPORT_UPLOAD_DIR;
+ private String saveFileToServer(byte[] fileBytes, String fileName,String uuid) {
+ try {
+ // 确保目录存在
+ Path reportDir = Path.of(REPORT_UPLOAD_DIR).toAbsolutePath().normalize();
+ if (!Files.exists(reportDir)) {
+ Files.createDirectories(reportDir);
+ }
+ //文件名随机
+ fileName =uuid+".docx";
+ // 保存文件
+ Path filePath = reportDir.resolve(fileName);
+ Files.write(filePath, fileBytes, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
+
+ log.info("文件已保存到服务器: {}", filePath.toString());
+ return fileName;
+ } catch (Exception e) {
+ log.error("保存文件到服务器失败: {}", fileName, e);
+ // 不抛出异常,避免影响用户下载
+ }
+ return null;
+ }
+ /**
+ * word 转 pdf
+ *
+ */
+ public String wordToPdf(String filePath, String fileName) {
+ try {
+ // 确保路径正确性
+ String inputFile = new File(filePath, fileName).getAbsolutePath();
+ String outputDir = new File(filePath, "pdf").getAbsolutePath();
+
+ log.info("开始转换文件:{}", inputFile);
+ // 创建输出目录
+ new File(outputDir).mkdirs();
+
+ // 使用完整的转换参数
+ List<String> command = Arrays.asList(
+ "/usr/bin/libreoffice", // 使用完整路径
+ "--headless",
+ "--norestore",
+ "--convert-to",
+ "pdf:writer_pdf_Export:PDFExport{" +
+ "EmbedStandardFonts=1;" +
+ "EmbedFonts=1;" +
+ "EmbedOnlyUsedFonts=0;" +
+ "UseTaggedPDF=1" +
+ "}",
+ "--outdir",
+ outputDir,
+ inputFile
+ );
+
+ // 创建进程构建器
+ ProcessBuilder pb = new ProcessBuilder(command);
+ log.info("执行命令:{}", command);
+
+ // 设置环境变量
+ Map<String, String> env = pb.environment();
+ env.put("LC_ALL", "zh_CN.UTF-8");
+ env.put("LANG", "zh_CN.UTF-8");
+ env.put("LANGUAGE", "zh_CN.UTF-8");
+ log.info("环境变量:{}", env);
+ // 重定向错误流到标准输出
+ pb.redirectErrorStream(true);
+ log.info("启动进程");
+ // 启动进程
+ Process process = pb.start();
+ log.info("等待进程完成");
+ // 读取输出
+ StringBuilder output = new StringBuilder();
+ try (BufferedReader reader = new BufferedReader(
+ new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) {
+ String line;
+ while ((line = reader.readLine()) != null) {
+ output.append(line).append("\n");
+ }
+ }
+ log.info("进程完成,输出:{}", output);
+ // 等待进程完成,设置超时
+ if (!process.waitFor(120, TimeUnit.SECONDS)) {
+ process.destroyForcibly();
+ throw new RuntimeException("转换超时");
+ }
+ log.info("进程完成,退出码");
+ int exitCode = process.exitValue();
+ if (exitCode != 0) {
+ throw new RuntimeException("转换失败,退出码:" + exitCode + "\n输出:" + output);
+ }
+ log.info("进程完成,退出码:{}", exitCode);
+ // 检查生成的PDF文件
+ String pdfFileName = fileName.substring(0, fileName.lastIndexOf(".")) + ".pdf";
+ File pdfFile = new File(outputDir, pdfFileName);
+ log.info("PDF文件:{}", pdfFile);
+ if (!pdfFile.exists() || pdfFile.length() == 0) {
+ throw new RuntimeException("PDF文件未生成或为空");
+ }
+ String absolutePath = pdfFile.getAbsolutePath();
+ log.info("PDF文件绝对路径:{}", absolutePath);
+ MultipartFile file = convertFileToMultipartFile(pdfFile);
+ log.info("PDF文件转换完成");
+// String s = tencentCosUtil.upLoadFile(multipartFile,"/wordToPdf");
+// String url = ObsUploadUtil.obsUpload(file);
+ log.info("PDF文件上传完成");
+ return "";
+
+ } catch (Exception e) {
+ throw new RuntimeException("PDF转换失败: " + e.getMessage(), e);
+ }
+ }
+
+ public MultipartFile convertFileToMultipartFile(File file) throws IOException {
+ // 读取文件内容到字节数组
+ byte[] fileContent = Files.readAllBytes(file.toPath());
+
+ // 创建 MultipartFile 对象
+ MultipartFile multipartFile = new MockMultipartFile(
+ file.getName(), // 文件名
+ file.getName(), // 原始文件名
+ "application/pdf", // 内容类型,根据实际情况调整
+ fileContent // 文件内容
+ );
+
+ return multipartFile;
+ }
+
+ // 在使用前检查和配置环境
+ public static void setupEnvironment() {
+ try {
+ // 1. 检查LibreOffice安装
+ checkLibreOffice();
+
+ // 2. 检查和安装字体
+ installFonts();
+
+ // 3. 配置字体
+ configureFonts();
+
+ // 4. 验证环境变量
+ checkEnvironment();
+
+ } catch (Exception e) {
+ throw new RuntimeException("环境设置失败: " + e.getMessage(), e);
+ }
+ }
+
+ private static void checkLibreOffice() throws IOException, InterruptedException {
+ Process process = Runtime.getRuntime().exec("which libreoffice");
+ if (process.waitFor() != 0) {
+ throw new RuntimeException("LibreOffice未安装");
+ }
+ }
+
+ private static void installFonts() throws IOException, InterruptedException {
+ // 创建字体安装脚本
+ String scriptContent =
+ "#!/bin/bash\n" +
+ "apt-get update\n" +
+ "apt-get install -y fonts-wqy-zenhei fonts-wqy-microhei fonts-arphic-ukai fonts-arphic-uming\n" +
+ "fc-cache -fv\n";
+
+ File script = new File("/tmp/install_fonts.sh");
+ Files.write(script.toPath(), scriptContent.getBytes());
+ script.setExecutable(true);
+
+ // 执行脚本
+ Process process = Runtime.getRuntime().exec("sudo /tmp/install_fonts.sh");
+ process.waitFor();
+
+ // 清理脚本
+ script.delete();
+ }
+
+ private static void configureFonts() throws IOException {
+ // 创建字体配置文件
+ String fontConfig =
+ "<?xml version=\"1.0\"?>\n" +
+ "<!DOCTYPE fontconfig SYSTEM \"fonts.dtd\">\n" +
+ "<fontconfig>\n" +
+ " <match target=\"pattern\">\n" +
+ " <test name=\"family\"><string>serif</string></test>\n" +
+ " <edit name=\"family\" mode=\"prepend\">\n" +
+ " <string>WenQuanYi Zen Hei</string>\n" +
+ " </edit>\n" +
+ " </match>\n" +
+ " <match target=\"pattern\">\n" +
+ " <test name=\"family\"><string>sans-serif</string></test>\n" +
+ " <edit name=\"family\" mode=\"prepend\">\n" +
+ " <string>WenQuanYi Zen Hei</string>\n" +
+ " </edit>\n" +
+ " </match>\n" +
+ "</fontconfig>";
+
+ // 写入配置文件
+ File configFile = new File(System.getProperty("user.home") + "/.fonts.conf");
+ Files.write(configFile.toPath(), fontConfig.getBytes());
+ }
+
+ private static void checkEnvironment() {
+ // 检查环境变量
+ String[] requiredVars = {"LANG", "LC_ALL", "LANGUAGE"};
+ for (String var : requiredVars) {
+ String value = System.getenv(var);
+ if (value == null || !value.contains("zh_CN")) {
+ System.err.println("警告: " + var + " 环境变量未正确设置");
+ }
+ }
+ }
+
+ public static MultipartFile convertToMultipartFile(ByteArrayOutputStream baos, String fileName) throws IOException {
+ // 创建一个临时文件
+ File tempFile = File.createTempFile(fileName, ".pdf");
+
+ // 将ByteArrayOutputStream中的数据写入临时文件
+ try (FileOutputStream fos = new FileOutputStream(tempFile)) {
+ baos.writeTo(fos);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ // 创建一个MultipartFile对象
+ return new MockMultipartFile(
+ fileName + ".pdf", // 参数名称
+ fileName + ".pdf", // 文件名
+ "application/pdf", // 内容类型
+ Files.readAllBytes(tempFile.toPath()) // 文件内容
+ );
+ }
+
+ /**
+ * file转byte
+ */
+ public static byte[] file2byte(File file){
+ byte[] buffer = null;
+ try{
+ FileInputStream fis = new FileInputStream(file);
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ byte[] b = new byte[1024];
+ int n;
+ while ((n = fis.read(b)) != -1)
+ {
+ bos.write(b, 0, n);
+ }
+ fis.close();
+ bos.close();
+ buffer = bos.toByteArray();
+ }catch (FileNotFoundException e){
+ e.printStackTrace();
+ }
+ catch (IOException e){
+ e.printStackTrace();
+ }
+ return buffer;
+ }
+
+ /**
+ * byte 转file
+ */
+ public static File byte2File(byte[] buf, String filePath, String fileName){
+ BufferedOutputStream bos = null;
+ FileOutputStream fos = null;
+ OutputStreamWriter osw = null;
+ File file = null;
+ try{
+ File dir = new File(filePath+"/");
+ if (!dir.exists()){
+ dir.mkdirs();
+ }
+ file = new File(filePath +File.separator + fileName);
+ fos = new FileOutputStream(file);
+ bos = new BufferedOutputStream(fos);
+// osw = new OutputStreamWriter(fos, StandardCharsets.UTF_8);
+ bos.write(buf);
+ }catch (Exception e){
+ e.printStackTrace();
+ }
+ finally{
+ if (bos != null){
+ try{
+ bos.close();
+ }catch (IOException e){
+ e.printStackTrace();
+ }
+ }
+ if (fos != null){
+ try{
+ fos.close();
+ }catch (IOException e){
+ e.printStackTrace();
+ }
+ }
+ }
+ return file;
+ }
+ /**
+ * multipartFile转File
+ **/
+ public static File multipartFile2File(MultipartFile multipartFile){
+ File file = null;
+ if (multipartFile != null){
+ try {
+ file= File.createTempFile("tmp", null);
+ multipartFile.transferTo(file);
+ System.gc();
+ file.deleteOnExit();
+ }catch (Exception e){
+ e.printStackTrace();
+ log.warn("multipartFile转File发生异常:"+e);
+ }
+ }
+ return file;
+ }
+
+
+ public static void main(String[] args) {
+// String url = "file:///E:\\qiyeweixin\\WXWork\\1688855207501340\\Cache\\File\\2024-09\\专业技术工作总结.docx";
+// String filePath = "E:\\qiyeweixin\\WXWork\\1688855207501340\\Cache\\File\\2024-09";
+// String fileName = "专业技术工作总结.docx";
+// String s = wordToPdf(url, filePath, fileName);
+// System.err.println(s);
+
+// String url = "file:///F:\\测试动态列表Word.doc";
+//// String filePath = "E:\\qiyeweixin\\WXWork\\1688855207501340\\Cache\\File\\2024-09";
+//// String fileName = "专业技术工作总结.docx";4
+// String filePath = "F:\\";
+//
+// String s = wordToPdf(url, filePath, "测试动态列表Word.doc");
+// System.err.println(s);
+
+
+
+ // TODO Auto-generated method stub
+
+
+// HashMap<String, Object> paramMap = new HashMap<>();
+// paramMap.put("CorpID", "SCZT006959");
+// paramMap.put("Pwd", "123456");
+// paramMap.put("Mobile", "19522115070");
+// paramMap.put("Cell", "");
+// paramMap.put("SendTime", "");
+// paramMap.put("Content", java.net.URLEncoder.encode("你好,这是测试短信发送。【职评网】"));
+// String result3 = HttpUtil.post("http://sdk2.028lk.com/sdk2/BatchSend2.aspx", paramMap);
+// if (result3 == null) {
+// result3 = "";
+// }
+// result3 = StrUtil.trim(result3);
+// log.info("返回结果:" + result3);
+//
+// if (result3.matches("^\\d+$") && !result3.equals("0")) {
+// return;
+// }
+
+ }
+
+ public String test(String fileName){
+ // windows
+ String url = "file:///F:\\file\\estate\\reports\\"+fileName;
+ String filePath = "F:\\file\\estate\\reports";
+ String result = wordToPdf(url,filePath, fileName);
+ // linux
+// String filePath = "F:\\file\\estate\\reports\\1769239688785.docx";
+// String result = wordToPdf(filePath, fileName);
+ log.info("word转pdf返回结果:" + result);
+ return result;
+ }
+
+}
diff --git a/admin-server/src/main/java/com/estate/expert/admin/utils/wordtopdf/WordUtil.java b/admin-server/src/main/java/com/estate/expert/admin/utils/wordtopdf/WordUtil.java
new file mode 100644
index 0000000..50f3631
--- /dev/null
+++ b/admin-server/src/main/java/com/estate/expert/admin/utils/wordtopdf/WordUtil.java
@@ -0,0 +1,336 @@
+package com.estate.expert.admin.utils.wordtopdf;
+
+import freemarker.cache.ClassTemplateLoader;
+import freemarker.template.Configuration;
+import freemarker.template.Template;
+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 javax.annotation.Resource;
+import java.io.*;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+@Slf4j
+@Component
+public class WordUtil {
+
+ /**
+ * 基于模板生成 Word 文档
+ *
+ * @param basePackagePath resources 目录下模板所在包路径
+ * @param templateFileName 模板文件名
+ * @param templateParam 模板参数
+ * @param fileName 文件名
+ */
+ @Resource
+ private PdfUtils pdfUtils;
+ 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);
+ XWPFParagraph pic = document.createParagraph();
+ Base64.Decoder decoder = Base64.getDecoder();
+ // 处理段落
+ 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.err.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 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);
+ // 如果有回车符,则插入一个空行
+ if (info.replacementText.contains("\n") || info.replacementText.contains("\r") || info.replacementText.contains("\r\n")) {
+ log.info("info.originalText=================: {}", info.replacementText);
+ // 每一个\n,\r,\r\n都插入一个空行,并且最后的文本进行换行,为一整个文本
+ String[] s = info.replacementText.split("\r\n");
+ for (int i = 0; i < s.length; i++) {
+ if (i == 0) {
+ newRun.setText(s[i] + " ");
+ }else {
+// newRun.addCarriageReturn();
+ newRun.setText(s[i] + " ");
+ }
+ }
+ }else {
+ newRun.setText(info.replacementText);
+ }
+
+ // 应用样式
+ style.applyStyle(newRun);
+ }
+
+
+ /**
+ * 生成PDF文件
+ * @param basePackagePath 模板文件所在的包路径
+ * @param templateFileName 模板文件名
+ * @param templateParam 模板参数
+ * @param fileName 填充参数后的word文件名
+ * @param saveDirectory 填充参数后的word保存文件目录
+ * @return
+ */
+ public String generatePdf(String basePackagePath, String templateFileName, Map<String,Object> templateParam, String fileName, String saveDirectory) {
+ try {
+ fillTemplate(basePackagePath+templateFileName, saveDirectory+fileName+".docx", templateParam);
+ String filePath = saveDirectory + fileName + ".docx";
+ 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(fileName + ".docx");
+// MultipartFile mockMultipartFile = new MockMultipartFile(encodedFileName+".doc", fileContent);
+// String s = ObsUploadUtil.obsUpload(mockMultipartFile);
+ return test;
+ } catch (IOException e) {
+ log.error("生成pdf异常,异常原因:{}", e.getMessage(), e);
+ throw new RuntimeException("生成pdf异常,异常原因:" + e.getMessage());
+ }
+ }
+
+ 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 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());
+// }
+// }
+
+
+
+
+}
--
Gitblit v1.7.1