繁体   English   中英

使用 Apache POI 为特定单词添加注释或在 docx 文档中运行

[英]Adding comment to a specific word or run in docx document using Apache POI

我的目标是在 Word .docx 文档中搜索单词或短语,并为其添加注释。 关于使用 Apache POI 添加注释,我一直在参考此处此处此处的示例代码。 但是,所有三个示例都将注释添加到整个段落(甚至整个表格),而不是添加到特定单词或运行。

我曾尝试在运行级别创建一个 XML 游标,但无法将其转换为必要的 CTMarkupRange 以应用注释的开始和结束。

        // Create comment
                        BigInteger cId = getCommentId(comments);
                        ctComment = comments.addNewComment();
                        ctComment.setAuthor("John Smith");
                        ctComment.setInitials("JS");
                        ctComment.setDate(new GregorianCalendar(Locale.getDefault()));
                        ctComment.addNewP().addNewR().addNewT().setStringValue("Test Comment");
                        ctComment.setId(cId);
                        
        // Set CommentRangeStart
                        String uri = CTMarkupRange.type.getName().getNamespaceURI();
                        String localPart = "commentRangeStart";

                        // XmlCursor cursor = p.getCTP().newCursor();
                        XmlCursor cursor = r.getCTR().newCursor();  
                        cursor.toFirstChild();
                        cursor.beginElement(localPart, uri);
                        cursor.toParent();
                        CTMarkupRange commentRangeStart =  (CTMarkupRange) cursor.getObject(); // This line throws a ClassCastException error
                        cursor.dispose();

                        commentRangeStart.setId(cId);

        // Set CommentRangeEnd and CommentReference

                        p.getCTP().addNewCommentRangeEnd().setId(cId);
                        // p.getCTP().addNewR().addNewCommentReference().setId(cId);
                        r.getCTR().addNewCommentReference().setId(cId);

EDIT1:片段显示循环运行的逻辑

for(XWPFParagraph p:paragraphs){
    List<XWPFRun> runs = p.getRuns();
    if (runs.size() > 0) {
        for (XWPFRun r : runs) {
            String text = r.getText(0);
            for (Map.Entry<String, List<String>> entry : rules.entrySet()) {
                String key = entry.getKey();
                List<String> value = entry.getValue();

                for (int i = 0; i < value.size(); i++) {
                    if (text != null && regexContains(text, value.get(i))) {
                        // Create comment
                        BigInteger cId = getCommentId(comments);
                        ctComment = comments.addNewComment();
                        ctComment.setAuthor("John Smith");
                        ctComment.setInitials("JS");
                        ctComment.setDate(new GregorianCalendar(Locale.getDefault()));
                        ctComment.addNewP().addNewR().addNewT().setStringValue(key);
                        ctComment.setId(cId);

                        // New snippet from Axel Richter
                        p.getCTP().addNewCommentRangeStart().setId(cId);
                        

                        p.getCTP().addNewCommentRangeEnd().setId(cId);
                        p.getCTP().addNewR().addNewCommentReference().setId(cId);
                    }
                }
            }
        }
    }

}

这并不像您想象的那么困难。

要评论段落内的运行,需要在段落中的文本运行开始之前设置注释范围开始。 文本运行在段落结束后需要设置注释范围结束。 这正是我的代码示例已经完成的。 当然,我的代码示例中的所有段落都只运行了一个文本。

在以下完整示例中,第二条注释仅注释“第二”一词。 为此,该段落具有三个文本运行。 第一个有文本“带有”的段落,第二个有文本“第二个”和评论,第三个有文本“评论。”。

import java.io.*;

import org.apache.poi.*;
import org.apache.poi.ooxml.*;
import org.apache.poi.openxml4j.opc.*;
import org.apache.xmlbeans.*;

import org.apache.poi.xwpf.usermodel.*;

import static org.apache.poi.ooxml.POIXMLTypeLoader.DEFAULT_XML_OPTIONS;

import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;

import javax.xml.namespace.QName;

import java.math.BigInteger;
import java.util.GregorianCalendar;
import java.util.Locale;


public class CreateWordWithComments {

//a method for creating the CommentsDocument /word/comments.xml in the *.docx ZIP archive  
 private static MyXWPFCommentsDocument createCommentsDocument(XWPFDocument document) throws Exception {
  OPCPackage oPCPackage = document.getPackage();
  PackagePartName partName = PackagingURIHelper.createPartName("/word/comments.xml");
  PackagePart part = oPCPackage.createPart(partName, "application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml");
  MyXWPFCommentsDocument myXWPFCommentsDocument = new MyXWPFCommentsDocument(part);

  String rId = document.addRelation(null, XWPFRelation.COMMENT, myXWPFCommentsDocument).getRelationship().getId();

  return myXWPFCommentsDocument;
 }

 public static void main(String[] args) throws Exception {

  XWPFDocument document = new XWPFDocument();

  MyXWPFCommentsDocument myXWPFCommentsDocument = createCommentsDocument(document);

  CTComments comments = myXWPFCommentsDocument.getComments();
  CTComment ctComment;
  XWPFParagraph paragraph;
  XWPFRun run;

//first comment
  BigInteger cId = BigInteger.ZERO;

  ctComment = comments.addNewComment();
  ctComment.setAuthor("Axel Ríchter");
  ctComment.setInitials("AR");
  ctComment.setDate(new GregorianCalendar(Locale.US));
  ctComment.addNewP().addNewR().addNewT().setStringValue("The first comment.");
  ctComment.setId(cId);

  paragraph = document.createParagraph();

  paragraph.getCTP().addNewCommentRangeStart().setId(cId); //comment range start is set before text run
  run = paragraph.createRun();
  run.setText("Paragraph with the first comment.");
  paragraph.getCTP().addNewCommentRangeEnd().setId(cId); //comment range end is set after text run

  paragraph.getCTP().addNewR().addNewCommentReference().setId(cId); 

//paragraph without comment
  paragraph = document.createParagraph();
  run = paragraph.createRun();
  run.setText("Paragraph without comment.");

//second comment
  cId = cId.add(BigInteger.ONE);

  ctComment = comments.addNewComment();
  ctComment.setAuthor("Axel Ríchter");
  ctComment.setInitials("AR");
  ctComment.setDate(new GregorianCalendar(Locale.US));
  ctComment.addNewP().addNewR().addNewT().setStringValue("The second comment. Comments the word \"second\".");
  ctComment.setId(cId);

  paragraph = document.createParagraph();
  run = paragraph.createRun();
  run.setText("Paragraph with the ");

  paragraph.getCTP().addNewCommentRangeStart().setId(cId); //comment range start is set before text run
  run = paragraph.createRun();
  run.setText("second");
  paragraph.getCTP().addNewCommentRangeEnd().setId(cId); //comment range end is set after text run

  run = paragraph.createRun();
  run.setText(" comment.");

  paragraph.getCTP().addNewR().addNewCommentReference().setId(cId);

//write document
  FileOutputStream out = new FileOutputStream("CreateWordWithComments.docx");
  document.write(out);
  out.close();
  document.close();

 }

//a wrapper class for the CommentsDocument /word/comments.xml in the *.docx ZIP archive
 private static class MyXWPFCommentsDocument extends POIXMLDocumentPart {

  private CTComments comments;

  private MyXWPFCommentsDocument(PackagePart part) throws Exception {
   super(part);
   comments = CommentsDocument.Factory.newInstance().addNewComments();
  }

  private CTComments getComments() {
   return comments;
  }

  @Override
  protected void commit() throws IOException {
   XmlOptions xmlOptions = new XmlOptions(DEFAULT_XML_OPTIONS);
   xmlOptions.setSaveSyntheticDocumentElement(new QName(CTComments.type.getName().getNamespaceURI(), "comments"));
   PackagePart part = getPackagePart();
   OutputStream out = part.getOutputStream();
   comments.save(out, xmlOptions);
   out.close();
  }

 }

}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM