为段落添加批注,并且有位置选区,先贴个图看添加后的效果

apache poi 5.2.5 为 word 添加批注(段落添加批注)_doc

你需要了解的事

  1. 将文件docx扩展名修改为zip,你打开后可以看到下图的结构

apache poi 5.2.5 为 word 添加批注(段落添加批注)_doc_02

添加批注后的格式如下,<w:p> 表示文档段落,可以看到该标签下面有 <w:commentRangeStart>、<w:commentRangeEnd>、<w:commentReference> 标签,分别对应的是 批注的起始位置、批注的结束位置、批注在commentsExtended.xml文件中的引用

apache poi 5.2.5 为 word 添加批注(段落添加批注)_xml文件_03

理解了以上的东西,再来看下面的代码

public void addCommentToParagraph(XWPFParagraph paragraph, String text) {
        BigInteger cursorId = nextCursorId();
        CTComment ctComment = comments.addNewComment();
        ctComment.setAuthor("test");
        ctComment.setInitials("AR");
        ctComment.setDate(new GregorianCalendar(Locale.CHINA));
        ctComment.addNewP().addNewR().addNewT().setStringValue(text);
        ctComment.setId(cursorId);

        CTMarkupRange ctMarkupRange = paragraph.getCTP().addNewCommentRangeStart();
        ctMarkupRange.setId(cursorId);

        Node beginNode = paragraph.getRuns().get(0).getCTR().getDomNode();
        paragraph.getCTP().getDomNode().insertBefore(ctMarkupRange.getDomNode(), beginNode);

        paragraph.getCTP().addNewCommentRangeEnd().setId(cursorId);
        paragraph.getCTP().addNewR().addNewCommentReference().setId(cursorId);
    }

<w:commentRangeStart>、<w:commentRangeEnd>、<w:commentReference> 标签中都有 w:id 属性,需要确保这个属性是全局唯一的

  1. nextCursorId() 用于获取下一个不重复的批注ID
  2. 设置批注信息与内容
  3. 获取段落的开始位置,把 <w:commentRangeStart> 标签插入到段落前
  4. 插入 <w:commentRangeEnd>、<w:commentReference> 标签

其中 nextCursorId() 方法实现可以参考如下代码

private final Set<BigInteger> commentIdSet;
    private BigInteger cursorId = new BigInteger("23100000");

    public DocumentComments(XWPFDocument document) {
        XWPFComments docComments = document.getDocComments();
        if (docComments == null) {
            docComments = document.createComments();
        }
        comments = docComments.getCtComments();
        commentIdSet = comments.getCommentList().stream().map(CTMarkup::getId).collect(Collectors.toSet());
    }

    private BigInteger nextCursorId() {
        while (commentIdSet.contains(cursorId)) {
            cursorId = cursorId.add(BigInteger.ONE);
        }
        commentIdSet.add(cursorId);
        return cursorId;
    }


本文结束