Java使用POI在word文档中替换书签或占位符内容

1.获取word文档对象

        写出两种不同获取word文档对象方式,通过文件服务器路径,和文件流的方式,请自行参考!

/**
     * 根据文档服务器路径获取文档对象
     * @param wordPath
     * @return
     */
    public static XWPFDocument getXWPFDocument(String wordPath) {
        FileInputStream fis = null;
        XWPFDocument document = null;
        try {
            fis = new FileInputStream(new File(wordPath));
            document = new XWPFDocument(fis);
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (Exception e) {
                    throw new RuntimeException(e.getMessage());
                }
            }
        }
        return document;
    }
    /**
     * 根据文联流获取文档对象
     * @param inputStream
     * @return
     */
    public static XWPFDocument getXWPFDocument(InputStream inputStream) {
        XWPFDocument document = null;
        try {
            document = new XWPFDocument(inputStream);
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (Exception e) {
                    throw new RuntimeException(e.getMessage());
                }
            }
        }
        return document;
    }

2. 通过poi获取word内所有书签

       注意:_GoBack书签貌似是wps自带的隐藏书签,如果不剔除的话会多出这么个书签,并且表格部分书签和文字部分书签获取方法不同!

/**
     * 获取word文档内所有书签
     *
     * @param document
     * @return
     */
    public static List<String> getBookMarksName(XWPFDocument document) {
        List<String> bookMarkList = new ArrayList<>();
        try {
            // 获取所有段落
            List<XWPFParagraph> paragraphs = document.getParagraphs();
            for (XWPFParagraph paragraph : paragraphs) {
                // 检查段落中是否有书签
                List<CTBookmark> bookmarks = paragraph.getCTP().getBookmarkStartList();
                for (CTBookmark bookmark : bookmarks) {
                    if (!bookmark.getName().equals("_GoBack")) {
                        bookMarkList.add(bookmark.getName());
                    }
                }
            }
            // 获取所有表格
            List<XWPFTable> tables = document.getTables();
            for (XWPFTable table : tables) {
                // 处理表格中的段落
                List<XWPFTableRow> rows = table.getRows();
                for (XWPFTableRow row : rows) {
                    List<XWPFTableCell> cells = row.getTableCells();
                    for (XWPFTableCell cell : cells) {
                        // 处理单元格中的段落
                        List<XWPFParagraph> cellParagraphs = cell.getParagraphs();
                        for (XWPFParagraph paragraph : cellParagraphs) {
                            // 检查段落中是否有书签
                            List<CTBookmark> bookmarks = paragraph.getCTP().getBookmarkStartList();
                            for (CTBookmark bookmark : bookmarks) {
                                if (!bookmark.getName().equals("_GoBack")) {
                                    bookMarkList.add(bookmark.getName());
                                }
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
        return bookMarkList;
    }

3.通过poi获取word内所有占位符

        只需通过文档对象加正则表达式即刻获取占位符,另外此处仅写出了对文本部分占位符获取,表格部分不适应噢;

/**
     * 获取文档内所有占位符
     *
     * @param document 文档对象
     * @param regex    占位符正则表达式
     * @return
     */
    public static List<String> getPlaceholderName(XWPFDocument document, String regex) {
        List<String> matchedTextList = new ArrayList<>();
        for (XWPFParagraph paragraph : document.getParagraphs()) {
            String text = paragraph.getText();
            Pattern pattern = Pattern.compile(regex);
            Matcher matcher = pattern.matcher(text);
            while (matcher.find()) {
                matchedTextList.add(matcher.group());
            }
        }
        return matchedTextList;
    }

4.替换占位符处文本内容

问题:指定书签位置替换,插入文本信息,会删除不属于书签所在内容
原因:获取书签位置段落,存在书签只是段落其中的一个或者多个run,如果移除段落中的run,则会删除多余内容,无法达到预期效果
 /**
     * 替换占位符位置文本
     * @param document 文档对象
     * @param placeholder 占位符
     * @param replacementText 要替换占位符的新文本
     */
    public static XWPFDocument replacePlaceholder(XWPFDocument document, String placeholder, String replacementText) {
        for (XWPFParagraph paragraph : document.getParagraphs()) {
            StringBuilder paragraphText = new StringBuilder();
            // 遍历段落中的每个文本片段
            for (XWPFRun run : paragraph.getRuns()) {
                String text = run.getText(0);
                if (text != null) {
                    paragraphText.append(text);
                }
            }
            // 获取段落中的文本
            String fullText = paragraphText.toString();
            // 寻找占位符位置
            int startIndex = fullText.indexOf(placeholder);
            boolean replaceText = false;
            while (startIndex >= 0) {
                // 定位到包含占位符的文本片段的位置
                int endIndex = startIndex + placeholder.length();
                // 替换占位符文本片段
                for (int i = 0; i < paragraph.getRuns().size(); i++) {
                    XWPFRun run = paragraph.getRuns().get(i);
                    String runText = run.getText(0);
                    if (runText != null) {
                        int runLength = runText.length();
                        if (startIndex < runLength && placeholder.contains(runText) && replaceText == false) {
                            replaceText = true;
                            int relativeStartIndex = Math.max(startIndex, 0);
                            int relativeEndIndex = Math.min(endIndex, runLength);
                            String newText = runText.substring(0, relativeStartIndex) + replacementText + runText.substring(relativeEndIndex);
                            run.setText(newText, 0);
                        } else if (startIndex < runLength && placeholder.contains(runText) && replaceText){
                            run.setText("", 0);
                        }
                        startIndex -= runLength;
                        endIndex -= runLength;
                    }
                }
            }
        }
        return document;
    }

5.替换书签部分文本内容

问题1:指定书签位置替换,插入文本信息,会删除不属于书签所在内容
原因:获取书签位置段落,存在书签只是段落其中的一个或者多个run,如果移除段落中的run,则会删除多余内容,无法达到预期效果
问题2:书签内容替换后,替换文本格式发生改变
原因:由于我是创建了一个新的运行元素 XWPFRun run = xwpfParagraph.createRun(); (run就是段落对象的下一层,段落内是一段不换行的文本,但是不同格式的会被分开存储到不同的run也就是存到不同段落片段中)来存储替换的文本,然后将其插入到段落中。然而,创建的新运行元素可能不会保留原始段落中的格式设置,这可能导致格式变化。
解决方法:复制段落原本的格式设置给要插入进去的run
/**
     * 替换书签内文本
     * @param document
     * @param bookTagMap  key: 书签名  value :需要替换书签的新文本
     */
    public static void replaceBookmarkText(XWPFDocument document, Map<String, String> bookTagMap) {
        List<XWPFParagraph> paragraphList = document.getParagraphs();
        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();
                    // 获取书签所在段落内的所有运行元素
                    List<XWPFRun> runs = xwpfParagraph.getRuns();
                    // 从书签范围内的运行元素中选择一个样式
                    XWPFRun originalRun = null;
                    for (XWPFRun xwpfRun : runs) {
                        if (xwpfRun.getCTR().getDomNode() == bookmark.getDomNode()) {
                            originalRun = xwpfRun;
                            break;
                        }
                    }
                    // 如果未找到匹配的运行元素,则使用段落内的第一个运行元素的样式
                    if (originalRun == null && !runs.isEmpty()) {
                        originalRun = runs.get(0);
                    }
                    if (originalRun != null) {
                        run.getCTR().setRPr(originalRun.getCTR().getRPr());
                    }
                    run.setText(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 {
                        ctp.getDomNode().insertBefore(run.getCTR().getDomNode(), nextNode);
                    }
                }
            }
        }
    }

遗留问题:

        在一个书签或占位符内插入多个段落并保持源格式的问题!

首先占位符本身在word内就是单独占一个段落。但是我要将占位符替换成这三个段落进去。

这个问题小编也还没找到解决方案,希望各位大佬有方案告知小编学习一下。

看到这里了都,最后希望各位老板动动小手点点赞,喜欢小编分享内容可以点个小小关注噢,再次感谢!

  • 17
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值