如何通过apache poi在单词表中添加评论

时间:2019-12-30 11:34:44

标签: apache-poi

我想使用apache poi以编程方式对现有Word文档写评论。我已经成功地通过method mentioned here在段落中添加了注释,但是在向表中添加注释时,我不知道如何将其与真实注释相关联

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);

 run = paragraph.createRun();
 run.setText("Paragraph with the first comment.");

 paragraph.getCTP().addNewCommentRangeEnd().setId(cId);

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

在段落中,我们可以使用paragraph.getCTP().addNewR().addNewCommentReference().setId()来链接注释,但是表中似乎没有类似的方法。我该怎么办?我是apoi poi的初学者,任何建议都将不胜感激。

1 个答案:

答案 0 :(得分:0)

关于如何注释整个表的问题的简短答案是:在该表的第一个单元格中获得第一段,将CommentRangeStart设置为该段的第一元素。然后,您将获得该表最后一个单元格中的最后一个段落,以将CommentRangeEnd设置为该段落中的最后一个元素。

但是,如果您想更改已经存在的Word文档,则需要考虑更多内容。如果已经有评论该怎么办?您要链接的示例是从头开始创建 Word文档。因此,以前不能有评论。我还提供了How to manipulate content of a comment with Apache POI。但这依赖于已经存在的评论。现在我们必须结合使用两者。这需要考虑已经存在的注释以及尚未存在的注释文档,然后再重新创建。

此外,很难将CommentRangeStart设置为已经存在的段落中的第一个元素。没有为此的方法。因此,我们需要使用非常低级的XML操纵来实现这一目标。

以下示例需要一个WordDocument.docx至少有一个表。然后整个表都会被评论。

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 WordCommentWholeTable {

 //a method to get or create the CommentsDocument /word/comments.xml in the *.docx ZIP archive  
 private static MyXWPFCommentsDocument createCommentsDocument(XWPFDocument document) throws Exception {
  MyXWPFCommentsDocument myXWPFCommentsDocument = null;

  //trying to get the CommentsDocument
  for (POIXMLDocumentPart.RelationPart rpart : document.getRelationParts()) {
   String relation = rpart.getRelationship().getRelationshipType();
   if (relation.equals(XWPFRelation.COMMENT.getRelation())) {
    POIXMLDocumentPart part = rpart.getDocumentPart();
    myXWPFCommentsDocument = new MyXWPFCommentsDocument(part.getPackagePart());
    String rId = document.getRelationId(part);
    document.addRelation(rId, XWPFRelation.COMMENT, myXWPFCommentsDocument);
   }
  }

  //create a new CommentsDocument if there is not one already
  if (myXWPFCommentsDocument == null) {
   OPCPackage oPCPackage = document.getPackage();
   PackagePartName partName = PackagingURIHelper.createPartName("/word/comments.xml");
   PackagePart part = oPCPackage.createPart(
    partName, 
    "application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml");
   myXWPFCommentsDocument = new MyXWPFCommentsDocument(part);
   document.addRelation(null, XWPFRelation.COMMENT, myXWPFCommentsDocument);
  }

  return myXWPFCommentsDocument;
 }

 //a method to get the next comment Id from CTComments
 private static BigInteger getCommentId(CTComments comments) {
  BigInteger cId = BigInteger.ZERO;
  for (CTComment ctComment : comments.getCommentList()) {
   if (ctComment.getId().compareTo(cId) == 1) {
    cId = ctComment.getId();
   }
  }
  cId = cId.add(BigInteger.ONE);
  return cId;
 }

 //method to set CommentRangeStart as first element in paragraph
 private static CTMarkupRange insertCommentRangeStartAsFirstElement(XWPFParagraph paragraph) {
  String uri = CTMarkupRange.type.getName().getNamespaceURI();
  String localPart = "commentRangeStart";
  XmlCursor cursor = paragraph.getCTP().newCursor();
  cursor.toFirstChild();  
  cursor.beginElement(localPart, uri);
  cursor.toParent();
  CTMarkupRange commentRangeStart = (CTMarkupRange)cursor.getObject();
  cursor.dispose();
  return commentRangeStart;  
 }


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

  XWPFDocument document = new XWPFDocument(new FileInputStream("WordDocument.docx"));

  MyXWPFCommentsDocument myXWPFCommentsDocument = createCommentsDocument(document);
  CTComments comments = myXWPFCommentsDocument.getComments();

  CTComment ctComment;
  XWPFParagraph paragraph;
  XWPFRun run;

  //comment for the table
  BigInteger cId = getCommentId(comments);
  ctComment = comments.addNewComment();
  ctComment.setAuthor("Axel Ríchter");
  ctComment.setInitials("AR");
  ctComment.setDate(new GregorianCalendar(Locale.US));
  ctComment.addNewP().addNewR().addNewT().setStringValue("This is a comment for whole table.");
  ctComment.setId(cId);

  //get first paragraph in first table cell to set CommentRangeStart
  XWPFTable table = document.getTables().get(0);
  XWPFTableRow row = table.getRow(0);
  XWPFTableCell cell = row.getCell(0);
  paragraph = cell.getParagraphArray(0);
  CTMarkupRange commentRangeStart = insertCommentRangeStartAsFirstElement(paragraph);
  commentRangeStart.setId(cId);

  //get last paragraph in last table cell to set CommentRangeEnd and CommentReference
  row = table.getRows().get(table.getRows().size()-1);
  cell = row.getTableCells().get(row.getTableCells().size()-1);
  paragraph = cell.getParagraphs().get(cell.getParagraphs().size()-1);
  paragraph.getCTP().addNewCommentRangeEnd().setId(cId);
  paragraph.getCTP().addNewR().addNewCommentReference().setId(cId);


  FileOutputStream out = new FileOutputStream("WordDocumentWithComments.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);
   try {
    comments = CommentsDocument.Factory.parse(part.getInputStream(), DEFAULT_XML_OPTIONS).getComments();
   } catch (Exception ex) {
    // there was no comments yet
   }
   if (comments == null) 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();
  }

 }

}