[英]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.