Java 使用word模板替换书签/标签

package org.jeecg.modules.utils;

import com.aspose.words.Document;
import com.aspose.words.License;
import com.aspose.words.SaveFormat;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.*;
import org.jeecg.common.api.dto.FileUploadVo;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.SymbolConstant;
import org.jeecg.common.util.CommonUtils;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBookmark;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP;
import org.springframework.core.io.ClassPathResource;
import org.w3c.dom.Node;

import java.io.*;
import java.util.List;
import java.util.Map;

/**
 * Word文件处理工具类
 */
@Slf4j
public class WordUtils {

    /**
     * 处理Word文件 并 转换成pdf 保存
     *
     * @param dataMap      替换数据集
     * @param uploadPath   jeecg配置的文件路径
     * @param wordPathName word模板文件路径
     * @param uploadType   文件存储类型,local本地存储、oss等
     * @return 业务保存的路径及文件名称
     * @throws Exception
     */
    public static Result<String> createDocAndParsePdf(Map<String, Object> dataMap, String uploadPath, String wordPathName, String uploadType, String bizPath, String pdfFileName) {
        // 读取成文件流
        InputStream inputStream;
        try {
            inputStream = CommonUtils.getInputStream(uploadType, uploadPath, wordPathName);
        } catch (Exception e) {
            return Result.error("文件未找到");
        }
        XWPFDocument doc = null;
        // org.apache.poi.xwpf.usermodel.Document  document = new XWPFDocument(fileInputStream);  // TODO 后续做兼容
        try {
            //加载Word文档
            doc = new XWPFDocument(inputStream);
            // 替换word模板数据
            replaceBookTag(doc, dataMap, uploadType, uploadPath);
            //转换Word为pdf
            String relativePath = wordParseToPdf(doc, uploadType, uploadPath, bizPath, pdfFileName);
            //
            return Result.OK("转换成功", relativePath);
        } catch (Exception e) {
            log.error("转换失败:{}", e.getMessage(), e);
            return Result.error("转换失败", e.getMessage());
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            if (doc != null) {
                try {
                    doc.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
        // 保存结果文件
        // 是否能够直接使用流转换成pdf
        /* File file = null;
        try {
            file = new File(saveFilePath);
            if (file.exists()) {
                file.delete();
            } else {
                //这说明目标文件的上级目录不存在,先新建所有的上级目录
                file.getParentFile().mkdirs();
            }
            FileOutputStream fos = new FileOutputStream(file);
            doc.write(fos);
            fos.close();
        } catch (Exception e) {
            e.printStackTrace();
        } */
    }


    /**
     * doc, uploadType, uploadPath, bizPath, pdfFileName
     * word转换为pdf使用document 文档流
     */
    public static String wordParseToPdf(XWPFDocument document, String uploadType, String uploadPath, String bizPath, String pdfFileName) throws Exception {
        // 验证License 若不验证则转化出的pdf文档会有水印产生
        if (!getLicense()) {
            return null;
        }
        // String settings = FontSettings.getDefaultFontName();
        ByteArrayOutputStream baos = null;
        ByteArrayInputStream bais = null;
        FileOutputStream os = null;
        try {
            // 直接把文档流转换成二进制的outputStream
            baos = new ByteArrayOutputStream();
            //文档写入流
            document.write(baos);
            //OutputStream写入InputStream二进制流 在Document 时使用
            bais = new ByteArrayInputStream(baos.toByteArray());
            if (CommonConstant.UPLOAD_TYPE_LOCAL.equals(uploadType)) {
                String saveFilePath = uploadPath + SymbolConstant.SINGLE_SLASH + bizPath + SymbolConstant.SINGLE_SLASH + pdfFileName;
                //文件夹不存在创建文件夹
                File file = new File(saveFilePath);
                if (!file.getParentFile().exists()) {
                    //这说明目标文件的上级目录不存在,先新建所有的上级目录
                    file.getParentFile().mkdirs();
                }
                // 创建保存pdf的文档流
                os = new FileOutputStream(file);
                // Address是将要被转化的word文档
                Document doc = new Document(bais);
                doc.save(os, SaveFormat.PDF);
                return bizPath + SymbolConstant.SINGLE_SLASH + pdfFileName;
            } else {
                ByteArrayOutputStream baos2 = new ByteArrayOutputStream();
                Document doc = new Document(bais);
                doc.save(baos2, SaveFormat.PDF);
                ByteArrayInputStream bais2 = new ByteArrayInputStream(baos2.toByteArray());
                // 保存到oos 或则 mini云上去
                // CommonUtils.upload(bais, bizPath, uploadType);
                FileUploadVo fileUploadVo = CommonUtils.uploadByStream(uploadType, bais2, bizPath, pdfFileName, null, uploadPath);
                bais2.close();
                baos2.close();
                return fileUploadVo.getRelativePath();
            }
        } catch (Exception e) {
            // e.printStackTrace();
            throw new RuntimeException(e);
        } finally {
            if (baos != null) {
                baos.close();
            }
            if (bais != null) {
                bais.close();
            }
            if (os != null) {
                os.close();
            }
        }

    }
    /**
     * 破解License
     *
     * @return
     */
    public static boolean getLicense() throws Exception {
        boolean result;
        try {
            // license.xml应放在..\WebRoot\WEB-INF\classes路径下
            ClassPathResource classPathResource = new ClassPathResource("license.xml");     //模板文件
            InputStream is = classPathResource.getInputStream();
            License aposeLic = new License();
            aposeLic.setLicense(is);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception("Word转PDF失败,license获取失败");
        }
        return result;
    }

    /**
     * 处理段落(根据书签的方式替换)
     */
    public static void replaceBookTag(XWPFDocument document, Map<String, Object> bookTagMap, String uploadType, String uploadPath) throws Exception {
        List<XWPFParagraph> paragraphList = document.getParagraphs();
        List<XWPFTable> tables = document.getTables();
        if (tables != null && tables.size() > 0) {
            for (XWPFTable table : tables) {
                List<XWPFTableRow> rows = table.getRows();
                for (XWPFTableRow row : rows) {
                    List<XWPFTableCell> cells = row.getTableCells();
                    for (XWPFTableCell cell : cells) {
                        bookParagraph(cell.getParagraphs(), bookTagMap, uploadType, uploadPath);
                    }
                }
            }
        }
        bookParagraph(paragraphList, bookTagMap, uploadType, uploadPath);
    }
    /**
     * 处理书签段落
     *
     * @param paragraphList 段落集合
     * @param bookTagMap    替换数据
     *                      for (XWPFParagraph xwpfParagraph : paragraphList) {
     *                      for (XWPFParagraph paragraph : paragraphList) {
     *                      List<CTBookmark> bookmarks = paragraph.getCTP().getBookmarkStartList();
     *                      for (CTBookmark bookmark : bookmarks) {
     *                      String bookmarkName = bookmark.getName();
     *                      if (bookmarkName.equals("your_bookmark_name")) {
     *                      // 执行替换操作
     *                      // 获取书签所在段落
     *                      XWPFParagraph bookmarkParagraph = (XWPFParagraph) paragraph.getParent();
     *                      // 获取书签所在单元格
     *                      XWPFTableCell bookmarkCell = (XWPFTableCell) bookmarkParagraph.getParent();
     *                      // 在单元格中插入新的内容
     *                      bookmarkParagraph.removeRun(0);
     *                      bookmarkParagraph.createRun().setText("Replacement Text");
     *                      }
     *                      }
     *                      }
     */
    public static void bookParagraph(List<XWPFParagraph> paragraphList, Map<String, Object> bookTagMap, String uploadType, String uploadPath) {
        for (XWPFParagraph xwpfParagraph : paragraphList) {
            CTP ctp = xwpfParagraph.getCTP();
            for (int dwI = 0; dwI < ctp.sizeOfBookmarkStartArray(); dwI++) {
                CTBookmark bookmark = ctp.getBookmarkStartArray(dwI);
                if (bookTagMap.containsKey(bookmark.getName())) {
                    XWPFRun run = xwpfParagraph.createRun();
                    if (bookmark.getName().contains("img")) {
                        String fileName = bookTagMap.get(bookmark.getName())!=null?
                                bookTagMap.get(bookmark.getName()).toString() : null;
                        if (fileName!=null) {
                            // 读取文件并把文件写到word报告中
                            try (InputStream inputStream = CommonUtils.getInputStream(uploadType, uploadPath, fileName);) {
                                run.addPicture(inputStream, XWPFDocument.PICTURE_TYPE_PNG, fileName, Units.pixelToEMU(100), Units.pixelToEMU(50));
                                run.setText("");
                            } catch (IOException | InvalidFormatException e) {
                                throw new RuntimeException(e);
                            }
                        }
                    } else {
                        run.setText(bookTagMap.get(bookmark.getName()) != null ? bookTagMap.get(bookmark.getName()).toString() : "");
                    }
                    Node firstNode = bookmark.getDomNode();
                    Node nextNode = firstNode.getNextSibling();
                    while (nextNode != null) {
                        // 循环查找结束符
                        String nodeName = nextNode.getNodeName();
                        if (nodeName.equals("w:bookmarkEnd")) {
                            break;
                        }
                        // 删除中间的非结束节点,即删除原书签内容
                        Node delNode = nextNode;
                        nextNode = nextNode.getNextSibling();
                        ctp.getDomNode().removeChild(delNode);
                    }
                    if (nextNode == null) {
                        // 始终找不到结束标识的,就在书签前面添加
                        ctp.getDomNode().insertBefore(run.getCTR().getDomNode(), firstNode);
                    } else {
                        // 找到结束符,将新内容添加到结束符之前,即内容写入bookmark中间
                        ctp.getDomNode().insertBefore(run.getCTR().getDomNode(), nextNode);
                    }
                }
            }
        }
    }

    /**
     * word转pdf使用文件流 - 这方法需要先保存doc 再使用文件流转换
     *
     * @param wordFile
     * @param saveFilePath
     */
    public static void wordParseToPdf(File wordFile, String saveFilePath) throws Exception {
        if (!getLicense()) { // 验证License 若不验证则转化出的pdf文档会有水印产生
            return;
        }
        try {
            // System.out.println(wordFile.getPath());
            // long old = System.currentTimeMillis();
            File file = new File(saveFilePath); // 新建一个空白pdf文档
            if (!file.exists()) {
                file.getParentFile().mkdirs();
            }
            FileOutputStream os = new FileOutputStream(file);
            Document doc = new Document(wordFile.getPath()); // Address是将要被转化的word文档
            doc.save(os, SaveFormat.PDF);// 全面支持DOC, DOCX, OOXML, RTF HTML, OpenDocument, PDF,
            // EPUB, XPS, SWF 相互转换
            // long now = System.currentTimeMillis();
            // System.out.println("共耗时:" + ((now - old) / 1000.0) + "秒"); // 转化用时
            os.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 处理段落 Runs 标签替换方式
     *
     * @param doc
     * @param textMap
     * @throws Exception
     */
    public static void doParagraphs(XWPFDocument doc, Map<String, Object> textMap) throws Exception {
        /**----------------------------暂时先不考虑动态生成表格、目前没需求;后续有需求后添加------------------------------------**/
        List<XWPFParagraph> paragraphList = doc.getParagraphs();
        List<XWPFTable> tables = doc.getTables();
        if (tables != null && tables.size() > 0) {
            for (XWPFTable table : tables) {
                List<XWPFTableRow> rows = table.getRows();
                for (XWPFTableRow row : rows) {
                    List<XWPFTableCell> cells = row.getTableCells();
                    for (XWPFTableCell cell : cells) {
                        processParagraph(textMap, cell.getParagraphs());
                    }
                }
            }
        }
        processParagraph(textMap, paragraphList);
    }
    /**
     * 处理段落
     *
     * @param textMap
     * @param paragraphList
     * @throws InvalidFormatException
     * @throws IOException
     */
    private static void processParagraph(Map<String, Object> textMap, List<XWPFParagraph> paragraphList) throws InvalidFormatException, IOException {
        for (XWPFParagraph paragraph : paragraphList) {
            List<XWPFRun> runs = paragraph.getRuns();
            for (XWPFRun run : runs) {
                String text = run.getText(0);
                //判断文本是否需要进行替换
                if (checkText(text)) {
                    for (Map.Entry<String, Object> entry : textMap.entrySet()) {
                        // 匹配模板与替换值 格式{{key}}  可以优化只要键的形势  但是也可能出现分割的问题
                        String key = "{{" + entry.getKey() + "}}";
                        Object value = entry.getValue();
                        if (text.contains(key)) {
                            if (value instanceof String) { //文字替换
                                if (StringUtils.isNotEmpty((String) value)) {
                                    run.setText((String) value, 0);
                                    // text = text.replace(key, (String) value);
                                } else {
                                    // text = null;
                                    run.setText("", 0);
                                }
                            } else if (value instanceof InputStream) {
                                //图片替换 可能instanceof不能判断流
                                // Map picMap = (Map) value;
                                // InputStream fis = (InputStream) picMap.get(entry.getKey());
                                InputStream fis = (InputStream) value;
                                run.addPicture(fis, XWPFDocument.PICTURE_TYPE_PNG, entry.getKey(), Units.toEMU(100), Units.toEMU(80));
                                run.setText("", 0);
                            } else if (value == null) {
                                run.setText("", 0);
                            }
                            //优化,找到了就退出当前循环
                            break;
                        }
                    }
                    //替换模板原来位置
                    // run.setText(text, 0);
                }
            }
        }
    }
    /**
     * 检查是否是需要替换的数据
     *
     * @param text
     * @return
     */
    private static boolean checkText(String text) {
        if (text == null || "".equals(text)) {
            return false;
        }
        return text.contains("{{");
    }
}

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 可以使用 Java 中的 String 的 replace 方法将 / 替换成 //。示例代码如下: ```java String str = "这是一条路径:/usr/local/bin"; String newStr = str.replace("/", "//"); System.out.println(newStr); ``` 输出结果为: ``` 这是一条路径://usr//local//bin ``` 请注意,斜杠是特殊字符,需要使用转义符号 \ 进行转义。 ### 回答2: 在Java中,我们可以使用replace方法将字符串中的某个字符替换成另一个字符。如果我们想要将字符串中的 / 替换成 //,我们可以使用以下代码: ```java String originalString = "这是一个字符串/示例"; String modifiedString = originalString.replace("/", "//"); System.out.println(modifiedString); ``` 运行以上代码,输出结果将是:"这是一个字符串//示例"。在replace方法中,我们将第一个参数设置为需要替换的字符,第二个参数设置为替换后的字符。在本例中,我们将 / 替换成了 //。需要注意的是,由于 / 是特殊字符,所以在字符串中表示时需要使用转义符\,即 / 要表示为 \/。 这样,我们就可以很方便地将字符串中的 / 替换成 //。这在一些特定的应用场景中可能会很有用,比如处理文件路径时,可以将 / 替换成 // 来表示路径的分隔符,以确保路径的正确性。 ### 回答3: 在Java中,将"/"替换成"//"是为了符合Java的注释规范。在Java中,使用"//"来表示单行注释,而使用"/"来表示除法运算符。因此,为了避免将除法运算符误解为注释,需要将"/"替换为"//"。 这种替换通常发生在处理文本或源代码的过程中。例如,当我们需要读取源代码文件并进行分析时,可以使用Java的字符串替换功能将所有"/"替换为"//",以确保注释和除法运算符正确解析。 此外,在使用正则表达式进行匹配和替换时,也可以使用"//"来代替"/",因为在正则表达式中,"/"被认为是一个特殊字符,需要进行转义才能正确匹配。因此,使用"//"可以简化正则表达式的书写和处理。 总而言之,在Java中将"/"替换成"//"的目的是为了避免将除法运算符误解为注释,并确保代码正常解析和处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值