poi word 指定书签位置插入、替换指定内容(解决了替换书签内容出现的删除不属于书签文本内容的多删问题) 12-11:解决当书签为光标定位不到书签run区域位置问题

问题:指定书签位置替换,插入文本信息,会删除不属于书签所在内容

原因:获取书签位置段落,存在书签只是段落其中的一个或者多个run,如果移除段落中的run,则会删除多余内容,无法达到预期效果

解决方案:

1.找书签位置时,找到书签开始至结束中所有的run区域的node节点集合

1.1 找到document的body以及页眉页脚中所有的段落集合

1.2 遍历段落信息,以段落的node节点开始,查找本个段落包含书签的node集合,把书签名字和段落信息以及处于书签内的node节点集合映射起来。

 
    public static Map<String, WordParagraphNodesDTO> getAllDocumentBookmarkNameCustom(XWPFDocument document) {

        // 创建返回书签映射段落node节点集合
        Map<String, WordParagraphNodesDTO> bookmarkParagraphMap = new HashMap<>();

        // 获取doc文档body内容中段落集合书签段落映射集合信息
        WordBookmarkUtils.getParagraphsBookmark(document.getParagraphs(), bookmarkParagraphMap);

        // 获取页脚集合 并且遍历页脚
        List<XWPFFooter> footerList = document.getFooterList();
        for (XWPFFooter footer : footerList) {
            // 获取页脚所有表格中书签信息
            getTablesBookmark(footer.getTables(),bookmarkParagraphMap);
            // 获取页脚中所有段落的书签信息
            getParagraphsBookmark(footer.getParagraphs(), bookmarkParagraphMap);
        }
        // 获取页眉集合 并且遍历页眉
        List<XWPFHeader> headerList = document.getHeaderList();
        for (XWPFHeader header : headerList) {
            // 获取页眉中所有表格的书签信息
            getTablesBookmark(header.getTables(),bookmarkParagraphMap);
            // 获取页眉中所有段落的书签信息
            getParagraphsBookmark(header.getParagraphs(), bookmarkParagraphMap);
        }

        // 获取docbody中所有表格集合书签段落映射集合信息
        getTablesBookmark(document.getTables(),bookmarkParagraphMap);

        // 返回映射集合
        return bookmarkParagraphMap;
    }


    //  获取段落映射信息
    private static void getParagraphsBookmark(List<XWPFParagraph> paragraphs, Map<String, WordParagraphNodesDTO> bookmarkParagraphMap) {
        if (CollectionUtils.isEmpty(paragraphs)){
            return;
        }
        // 遍历段落
        for (XWPFParagraph paragraph : paragraphs) {
        // 获取,递归获取            
getBookmarkParagraphRecursion(paragraph.getCTP().getDomNode(),paragraph,bookmarkParagraphMap);
        }
    }



    // 递归获取当前段落中所有的书签段落映射信息
    public static void getBookmarkParagraphRecursion(Node node, XWPFParagraph paragraph, Map<String, WordParagraphNodesDTO> bookmarkParagraphMap) {

        if (Objects.equals("w:bookmarkStart", node.getNodeName())) {
            String nodeNameValue = node.getAttributes().getNamedItem("w:name").getNodeValue();
            String nodeIdValue = node.getAttributes().getNamedItem("w:id").getNodeValue();


            WordParagraphNodesDTO wordParagraphNodesDTO = new WordParagraphNodesDTO();
            wordParagraphNodesDTO.setParagraph(paragraph);
            ArrayList<Node> bookmarkNodeList = new ArrayList<>();
            bookmarkNodeList.add(node);
            wordParagraphNodesDTO.setBookmarkNodeList(bookmarkNodeList);

            while (null != node.getNextSibling()){
                node = node.getNextSibling();
                bookmarkNodeList.add(node);
                if (Objects.equals("w:bookmarkEnd", node.getNodeName()) && Objects.equals(nodeIdValue, node.getAttributes().getNamedItem("w:id").getNodeValue())){
                    bookmarkNodeList.add(node);
                    break;
                }
            }

            bookmarkParagraphMap.put(nodeNameValue, wordParagraphNodesDTO);
        } else {
            NodeList childNodes = node.getChildNodes();
            for (int i = 0, j = childNodes.getLength(); i < j; i++) {
                Node childNode = childNodes.item(i);
                
                getBookmarkParagraphRecursion(childNode, paragraph, bookmarkParagraphMap);
            }
        }
    }

   // 遍历段落 获取映射信息
   private static void getTablesBookmark( List<XWPFTable> tables , Map<String, WordParagraphNodesDTO> bookmarkParagraphMap) {

        if (CollectionUtils.isEmpty(tables)){
            return;
        }

        for (XWPFTable table : tables) {
            List<XWPFTableRow> rows = table.getRows();
            for (XWPFTableRow row : rows) {
                List<XWPFTableCell> tableCells = row.getTableCells();
                for (XWPFTableCell cell : tableCells) {
                    List<XWPFParagraph> paragraphs = cell.getParagraphs();
                    for (XWPFParagraph paragraph : paragraphs) {
                        getBookmarkParagraphRecursion(paragraph.getCTP().getDomNode(),paragraph,bookmarkParagraphMap);
                    }
                }
            }
        }
    }
2.插入数据时,先将本段落处于书签的run区域集合全部替换成空字符串,再将指定内容插入到当前的书签中

2.1 根据书签名称获取到映射到的段落,以及处于书签内的node集合

2.2 将处于书签内的node节点内容全部替换成空字符串

2.3 在书签存在的第一个node节点的run区域插入想要的文本内容(文本,图片)

        WordParagraphNodesDTO wordParagraphNode = bookmarkNameParagraphMap.get(bookName);
        if (null == wordParagraphNode){
            return;
        }
        XWPFParagraph paragraph = wordParagraphNode.getParagraph();
        List<XWPFRun> runList = paragraph.getRuns();
        XWPFRun replaceRun = null;
        if (CollectionUtils.isNotEmpty(runList)){
            for (XWPFRun xwpfRun : runList) {
                if (wordParagraphNode.getBookmarkNodeList().contains(xwpfRun.getCTR().getDomNode())){
                    replaceRun = xwpfRun;
                    int sizeOfTArray = xwpfRun.getCTR().sizeOfTArray();
                    for (int i = 0; i < sizeOfTArray; i++) {
                        xwpfRun.setText("",i);
                    }
                }
            }
        }

        // 插入内容
        replaceRun.setText("内容");

2023-12-11新增:

出现出现书签仅仅为一个光标,没有区域,观察xml如下,无法根据node准确定位到当前段落的哪一个位置,需要获取当前段落中匹配的node节点的上一个节点,根据上个节点定位到书签位置,插入指定信息。


获取位置添加以下代码:

    wordParagraphNodesDTO.setPreNode(node.getPreviousSibling());

插入位置添加一下代码:定位插入书签位置的前一个run区域,插入数据
 

      if (null == replaceRun){
               for (XWPFRun xwpfRun : runList) {
                  if (Objects.equals(xwpfRun.getCTR().getDomNode(),wordParagraphNode.getPreNode())){
                      replaceRun = xwpfRun;
                      break;
                  }
               }
            }

2024-4-22 新增:书签跨段落,无法删除书签中内容

public static void getBookmarkParagraphRecursion(Node node, XWPFParagraph paragraph, Map<String, WordParagraphNodesDTO> bookmarkParagraphMap) {

        if (Objects.equals(BOOKMARK_START_TAG, node.getNodeName())) {
            String nodeNameValue = node.getAttributes().getNamedItem("w:name").getNodeValue();
            if (nodeNameValue.startsWith("_")){
                return;
            }
            String nodeIdValue = node.getAttributes().getNamedItem("w:id").getNodeValue();
            WordParagraphNodesDTO wordParagraphNodesDTO = new WordParagraphNodesDTO();
            wordParagraphNodesDTO.setParagraph(paragraph);
            ArrayList<Node> bookmarkNodeList = new ArrayList<>();
            bookmarkNodeList.add(node);
            wordParagraphNodesDTO.setPreNode(node.getPreviousSibling());
            wordParagraphNodesDTO.setBookmarkNodeList(bookmarkNodeList);

            boolean bookmarkEndFlag = false; // 书签结束标记,书签跨段落

            // 一直循环匹配书签id 的结束节点 todo 可能出现书签和段落平级问题,目前还未解决
            while (null != node.getNextSibling()){
                node = node.getNextSibling();
                bookmarkNodeList.add(node);
                if (Objects.equals(BOOKMARK_END_TAG, node.getNodeName()) && Objects.equals(nodeIdValue, node.getAttributes().getNamedItem("w:id").getNodeValue())){
                    bookmarkNodeList.add(node);
                    bookmarkEndFlag = true;
                    break;
                }
            }

            // 段落集合
            List<XWPFParagraph> xwpfParagraphList = new ArrayList<>();
            if (!bookmarkEndFlag){
                XWPFDocument document = paragraph.getDocument();
                boolean endFlag = false;
                for (int i = document.getPosOfParagraph(paragraph) - 5; i <document.getParagraphs().size(); i++) {
                    XWPFParagraph xwpfParagraph = document.getParagraphArray(i);
                    if (xwpfParagraph != null) {
                        xwpfParagraphList.add(xwpfParagraph);
                        CTMarkupRange[] bookmarkEndArray = xwpfParagraph.getCTP().getBookmarkEndArray();
                        for (CTMarkupRange ctMarkupRange : bookmarkEndArray) {
                            String nodeId = ctMarkupRange.getDomNode().getAttributes().getNamedItem("w:id").getNodeValue();
                            if (Objects.equals(nodeId, nodeIdValue)) {
                                endFlag = true;
                                break;
                            }
                        }
                    }
                    if (endFlag){
                        wordParagraphNodesDTO.setParagraphList(xwpfParagraphList);
                        break;
                    }
                }
            }

            bookmarkParagraphMap.put(nodeNameValue, wordParagraphNodesDTO);
        } else {
            NodeList childNodes = node.getChildNodes();
            for (int i = 0, j = childNodes.getLength(); i < j; i++) {
                Node childNode = childNodes.item(i);
                // 递归
                getBookmarkParagraphRecursion(childNode, paragraph, bookmarkParagraphMap);
            }
        }
    }

  • 18
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 11
    评论
你可以使用 Apache POI 库来读取 Word 文档并操作书签。以下是一个 Java 示例代码,可以读取 Word 文档中的指定书签,并将其内容替换指定文本: ```java import java.io.FileInputStream; import java.io.FileOutputStream; import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.apache.poi.xwpf.usermodel.XWPFParagraph; import org.apache.poi.xwpf.usermodel.XWPFRun; import org.apache.xmlbeans.XmlCursor; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBookmark; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTMarkupRange; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTText; public class WordBookmarkReplace { public static void main(String[] args) throws Exception { String filePath = "path/to/word/document.docx"; String bookmarkName = "bookmark1"; String replacementText = "replacement text"; // Load the Word document POIFSFileSystem fs = new POIFSFileSystem(new FileInputStream(filePath)); XWPFDocument doc = new XWPFDocument(fs); // Find the bookmark by name CTBookmark bookmark = findBookmarkByName(doc, bookmarkName); if (bookmark == null) { throw new IllegalArgumentException("Bookmark not found: " + bookmarkName); } // Replace the bookmark content with the replacement text CTMarkupRange range = bookmark.getDomNode().getFirstChild().getDomNode().getFirstChild(); XmlCursor cursor = range.newCursor(); XWPFParagraph p = doc.getParagraphArray(cursor.getBookmarkId()); XWPFRun r = p.getRuns().get(0); r.setText(replacementText, 0); // Save the modified document FileOutputStream out = new FileOutputStream(filePath); doc.write(out); out.close(); doc.close(); } private static CTBookmark findBookmarkByName(XWPFDocument doc, String name) { for (CTBookmark bookmark : doc.getDocument().getBody().getSdtArray()) { if (bookmark.getName().equals(name)) { return bookmark; } } return null; } } ``` 在这个示例中,我们首先加载 Word 文档,然后使用 `findBookmarkByName` 方法查找指定名称的书签。如果找到了书签,我们就可以使用其范围信息找到书签所在的段落和文本,并将其替换指定文本。最后,我们将修改后的文档保存回原始文件。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小花客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值