How to add comment by apache poi

2020-02-01 06:15发布

I'm trying to add a comment to a MS Word document by apache poi api.
I have done part of the work by using:

CTMarkupRange commentStart = paragraph.getCTP().addNewCommentRangeStart();
commentStart.setId(BigInteger.ZERO);
XWPFRun run = paragraph.createRun();
run.setText("text");

CTMarkupRange commentEnd = paragraph.getCTP().addNewCommentRangeEnd();
commentEnd.setId(BigInteger.ZERO);

CTR ctr = paragraph.getCTP().addNewR();
CTMarkup ctMarkup = ctr.addNewCommentReference();
ctMarkup.setId(BigInteger.ZERO);

But I don't know how to link it to a real comment and I find nothing about it in api-document.
Do anyone know how to solve it?

1条回答
狗以群分
2楼-- · 2020-02-01 06:50

In a Office OpenXML Word document (XWPF) comments are in a special CommentsDocument /word/comments.xml in the *.docx ZIP archive. So at first we would need access to this document. But until now the XWPFdocument will only read that package part while creating. There is neither write access nor a possibility to create that package part.

So we must at first provide such a possibility to create the package part /word/comments.xml in the *.docx ZIP archive and to get write access to it.

In the following example the method MyXWPFCommentsDocument createCommentsDocument(XWPFDocument document) creates the package part /word/comments.xml and the relations to it. The class MyXWPFCommentsDocument is a wrapper class for that package part having write access to it.

import java.io.*;

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

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

import static org.apache.poi.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 = "rId" + (document.getRelationParts().size()+1);
  document.addRelation(rId, XWPFRelation.COMMENT, myXWPFCommentsDocument);

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

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

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

  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.");
  ctComment.setId(cId);

  paragraph = document.createParagraph();
  paragraph.getCTP().addNewCommentRangeStart().setId(cId);

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

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

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

//write document
  document.write(new FileOutputStream("CreateWordWithComments.docx"));
  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();
  }

 }
}

This works for apache poi 3.17. Since apache poi 4.0.0 the ooxml part is separated. So there must be:

...
import org.apache.poi.ooxml.*;
...
import static org.apache.poi.ooxml.POIXMLTypeLoader.DEFAULT_XML_OPTIONS;
...
查看更多
登录 后发表回答