Java 使用ApachePOI向特定word或在docx文档中运行添加注释
我的目标是在word.docx文档中搜索一个单词或短语,并向其添加注释。我一直在参考找到的示例代码,以及使用ApachePOI添加注释。但是,这三个示例都是在整个段落(甚至整个表格)中添加注释,而不是在特定的单词或run中添加注释 我已尝试在运行级别创建XML游标,但无法将其强制转换为必要的CTMarkupRange以应用注释的开头和结尾Java 使用ApachePOI向特定word或在docx文档中运行添加注释,java,apache-poi,Java,Apache Poi,我的目标是在word.docx文档中搜索一个单词或短语,并向其添加注释。我一直在参考找到的示例代码,以及使用ApachePOI添加注释。但是,这三个示例都是在整个段落(甚至整个表格)中添加注释,而不是在特定的单词或run中添加注释 我已尝试在运行级别创建XML游标,但无法将其强制转换为必要的CTMarkupRange以应用注释的开头和结尾 // Create comment BigInteger cId = getCommentId
// 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);
}
}
}
}
}
}
for(XWPFParagraph p:段落){
List runs=p.getRuns();
如果(运行.size()>0){
用于(XWPFRun r:运行){
String text=r.getText(0);
for(Map.Entry:rules.entrySet()){
String key=entry.getKey();
列表值=entry.getValue();
对于(int i=0;i
这并不像你想象的那么困难
若要对段落内的运行进行注释,需要在段落中的文本运行开始之前设置注释范围开始。需要在段落中文本运行结束后设置注释范围结束。这正是我的代码示例已经做过的。当然,我的代码示例中的所有段落都只运行了一个文本
在下面的完整示例中,第二条注释仅注释“second”一词。为此,该段落有三段文字。第一个文本为“带段落”,第二个文本为“第二个”并有注释,第三个文本为“注释”
这并不像你想象的那么困难 若要对段落内的运行进行注释,需要在段落中的文本运行开始之前设置注释范围开始。需要在段落中文本运行结束后设置注释范围结束。这正是我的代码示例已经做过的。当然,我的代码示例中的所有段落都只运行了一个文本 在下面的完整示例中,第二条注释仅注释“second”一词。为此,该段落有三段文字。第一个文本为“带段落”,第二个文本为“第二个”并有注释,第三个文本为“注释”
嗨Axel,感谢您的详细回复和示例代码。我知道在文本运行之前必须设置“.addNewCommentRangeStart()”,但我仍然在为我的“搜索和注释”工具的逻辑而挣扎。目前,我循环浏览每一段和每一段,并使用模式/匹配器查找与字符串匹配的内容。当我找到一个匹配的跑步记录时,我想给这个特定的跑步记录添加一条注释。但是,我假设我需要在运行之前添加注释范围起始标记?如果您有任何需要纠正我的逻辑的地方,我将不胜感激。谢谢。为了便于参考,我在原始postMy代码中添加了一个逻辑片段,仅用于在创建新段落和文本运行时在新的
XWPFDocument
中创建注释。将特殊位置的注释添加到现有文档中是一项非常复杂的任务。这不是一个问题可以回答的问题,因为这里有多个问题:在一个位置插入?那么:如何在文档中找到起始和结束位置?如果找到,如何在这些位置插入注释范围标记?评论一个现有的单词?然后:如何将单个单词放入自己的文本运行中,并在该文本运行之前和之后插入注释范围标记?嗨,Axel,感谢您的详细回复和示例代码。我知道在文本运行之前必须设置“.addNewCommentRangeStart()”,但我仍然在为我的“搜索和注释”工具的逻辑而挣扎。目前,我循环浏览每一段和每一段,并使用模式/匹配器查找与字符串匹配的内容。当我找到一个匹配的跑步记录时,我想给这个特定的跑步记录添加一条注释。但是,我假设我需要在运行之前添加注释范围起始标记?如果您有任何需要纠正我的逻辑的地方,我将不胜感激。谢谢。为了便于参考,我在原始postMy代码中添加了一个逻辑片段,仅用于在创建新段落和文本运行时在新的XWPFDocument
中创建注释。将特殊位置的注释添加到现有文档中是一项非常复杂的任务。这不是一个问题可以回答的问题,因为这里有多个问题:在一个位置插入?那么:如何在文档中找到起始和结束位置?如果找到,如何在这些位置插入注释范围标记?评论一个现有的单词?然后:如何将单个单词放入自己的文本运行中,并在该文本运行之前和之后插入注释范围标记?
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 = "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); //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();
}
}
}