Create, get, modify and remove comments, support operating paragraphs, pictures and tables in comments

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1887867 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Sayi 2021-03-20 16:21:41 +00:00
parent 6167f3416f
commit 0efa53456a
8 changed files with 866 additions and 39 deletions

View File

@ -26,5 +26,6 @@ public enum BodyType {
HEADER,
FOOTER,
FOOTNOTE,
TABLECELL
TABLECELL,
COMMENT
}

View File

@ -16,43 +16,411 @@
==================================================================== */
package org.apache.poi.xwpf.usermodel;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTComment;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP;
import org.apache.poi.ooxml.POIXMLDocumentPart;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlObject;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;
/**
* Sketch of XWPF comment class
*
* @author Yury Batrakov (batrakov at gmail.com)
*/
public class XWPFComment {
protected String id;
protected String author;
protected StringBuilder text;
public class XWPFComment implements IBody {
public XWPFComment(CTComment comment, XWPFDocument document) {
text = new StringBuilder(64);
id = comment.getId().toString();
author = comment.getAuthor();
protected CTComment ctComment;
protected XWPFComments comments;
protected XWPFDocument document;
private List<XWPFParagraph> paragraphs = new ArrayList<>();
private List<XWPFTable> tables = new ArrayList<>();
private List<IBodyElement> bodyElements = new ArrayList<>();
for (CTP ctp : comment.getPArray()) {
if(text.length() > 0) {
text.append("\n");
public XWPFComment(CTComment ctComment, XWPFComments comments) {
this.comments = comments;
this.ctComment = ctComment;
this.document = comments.getXWPFDocument();
init();
}
protected void init() {
XmlCursor cursor = ctComment.newCursor();
cursor.selectPath("./*");
while (cursor.toNextSelection()) {
XmlObject o = cursor.getObject();
if (o instanceof CTP) {
XWPFParagraph p = new XWPFParagraph((CTP) o, this);
bodyElements.add(p);
paragraphs.add(p);
} else if (o instanceof CTTbl) {
XWPFTable t = new XWPFTable((CTTbl) o, this);
bodyElements.add(t);
tables.add(t);
} else if (o instanceof CTSdtBlock) {
XWPFSDT c = new XWPFSDT((CTSdtBlock) o, this);
bodyElements.add(c);
}
XWPFParagraph p = new XWPFParagraph(ctp, document);
text.append(p.getText());
}
cursor.dispose();
}
public String getId() {
return id;
/**
* Get the Part to which the comment belongs, which you need for adding
* relationships to other parts
*
* @return {@link POIXMLDocumentPart} that contains the comment.
* @see org.apache.poi.xwpf.usermodel.IBody#getPart()
*/
@Override
public POIXMLDocumentPart getPart() {
return comments;
}
public String getAuthor() {
return author;
/**
* Get the part type {@link BodyType} of the comment.
*
* @return The {@link BodyType} value.
* @see org.apache.poi.xwpf.usermodel.IBody#getPartType()
*/
@Override
public BodyType getPartType() {
return BodyType.COMMENT;
}
/**
* Gets the body elements ({@link IBodyElement}) of the comment.
*
* @return List of body elements.
*/
@Override
public List<IBodyElement> getBodyElements() {
return Collections.unmodifiableList(bodyElements);
}
/**
* Returns the paragraph(s) that holds the text of the comment.
*/
@Override
public List<XWPFParagraph> getParagraphs() {
return Collections.unmodifiableList(paragraphs);
}
/**
* Get the list of {@link XWPFTable}s in the comment.
*
* @return List of tables
*/
@Override
public List<XWPFTable> getTables() {
return Collections.unmodifiableList(tables);
}
@Override
public XWPFParagraph getParagraph(CTP p) {
for (XWPFParagraph paragraph : paragraphs) {
if (paragraph.getCTP().equals(p))
return paragraph;
}
return null;
}
@Override
public XWPFTable getTable(CTTbl ctTable) {
for (XWPFTable table : tables) {
if (table == null)
return null;
if (table.getCTTbl().equals(ctTable))
return table;
}
return null;
}
@Override
public XWPFParagraph getParagraphArray(int pos) {
if (pos >= 0 && pos < paragraphs.size()) {
return paragraphs.get(pos);
}
return null;
}
@Override
public XWPFTable getTableArray(int pos) {
if (pos >= 0 && pos < tables.size()) {
return tables.get(pos);
}
return null;
}
@Override
public XWPFParagraph insertNewParagraph(XmlCursor cursor) {
if (isCursorInCmt(cursor)) {
String uri = CTP.type.getName().getNamespaceURI();
String localPart = "p";
cursor.beginElement(localPart, uri);
cursor.toParent();
CTP p = (CTP) cursor.getObject();
XWPFParagraph newP = new XWPFParagraph(p, this);
XmlObject o = null;
while (!(o instanceof CTP) && (cursor.toPrevSibling())) {
o = cursor.getObject();
}
if ((!(o instanceof CTP)) || o == p) {
paragraphs.add(0, newP);
} else {
int pos = paragraphs.indexOf(getParagraph((CTP) o)) + 1;
paragraphs.add(pos, newP);
}
int i = 0;
XmlCursor p2 = p.newCursor();
cursor.toCursor(p2);
p2.dispose();
while (cursor.toPrevSibling()) {
o = cursor.getObject();
if (o instanceof CTP || o instanceof CTTbl)
i++;
}
bodyElements.add(i, newP);
p2 = p.newCursor();
cursor.toCursor(p2);
cursor.toEndToken();
p2.dispose();
return newP;
}
return null;
}
private boolean isCursorInCmt(XmlCursor cursor) {
XmlCursor verify = cursor.newCursor();
verify.toParent();
boolean result = (verify.getObject() == this.ctComment);
verify.dispose();
return result;
}
@Override
public XWPFTable insertNewTbl(XmlCursor cursor) {
if (isCursorInCmt(cursor)) {
String uri = CTTbl.type.getName().getNamespaceURI();
String localPart = "tbl";
cursor.beginElement(localPart, uri);
cursor.toParent();
CTTbl t = (CTTbl) cursor.getObject();
XWPFTable newT = new XWPFTable(t, this);
cursor.removeXmlContents();
XmlObject o = null;
while (!(o instanceof CTTbl) && (cursor.toPrevSibling())) {
o = cursor.getObject();
}
if (!(o instanceof CTTbl)) {
tables.add(0, newT);
} else {
int pos = tables.indexOf(getTable((CTTbl) o)) + 1;
tables.add(pos, newT);
}
int i = 0;
XmlCursor cursor2 = t.newCursor();
while (cursor2.toPrevSibling()) {
o = cursor2.getObject();
if (o instanceof CTP || o instanceof CTTbl) {
i++;
}
}
cursor2.dispose();
bodyElements.add(i, newT);
cursor2 = t.newCursor();
cursor.toCursor(cursor2);
cursor.toEndToken();
cursor2.dispose();
return newT;
}
return null;
}
@Override
public void insertTable(int pos, XWPFTable table) {
bodyElements.add(pos, table);
int i = 0;
for (CTTbl tbl : ctComment.getTblList()) {
if (tbl == table.getCTTbl()) {
break;
}
i++;
}
tables.add(i, table);
}
@Override
public XWPFTableCell getTableCell(CTTc cell) {
XmlCursor cursor = cell.newCursor();
cursor.toParent();
XmlObject o = cursor.getObject();
if (!(o instanceof CTRow)) {
cursor.dispose();
return null;
}
CTRow row = (CTRow) o;
cursor.toParent();
o = cursor.getObject();
cursor.dispose();
if (!(o instanceof CTTbl)) {
return null;
}
CTTbl tbl = (CTTbl) o;
XWPFTable table = getTable(tbl);
if (table == null) {
return null;
}
XWPFTableRow tableRow = table.getRow(row);
return tableRow.getTableCell(cell);
}
/**
* Get the {@link XWPFDocument} the comment is part of.
*
* @see org.apache.poi.xwpf.usermodel.IBody#getXWPFDocument()
*/
@Override
public XWPFDocument getXWPFDocument() {
return document;
}
public String getText() {
StringBuilder text = new StringBuilder();
for (XWPFParagraph p : paragraphs) {
if (text.length() > 0) {
text.append("\n");
}
text.append(p.getText());
}
return text.toString();
}
public XWPFParagraph createParagraph() {
XWPFParagraph paragraph = new XWPFParagraph(ctComment.addNewP(), this);
paragraphs.add(paragraph);
bodyElements.add(paragraph);
return paragraph;
}
public void removeParagraph(XWPFParagraph paragraph) {
if (paragraphs.contains(paragraph)) {
CTP ctP = paragraph.getCTP();
XmlCursor c = ctP.newCursor();
c.removeXml();
c.dispose();
paragraphs.remove(paragraph);
bodyElements.remove(paragraph);
}
}
public void removeTable(XWPFTable table) {
if (tables.contains(table)) {
CTTbl ctTbl = table.getCTTbl();
XmlCursor c = ctTbl.newCursor();
c.removeXml();
c.dispose();
tables.remove(table);
bodyElements.remove(table);
}
}
public XWPFTable createTable(int rows, int cols) {
XWPFTable table = new XWPFTable(ctComment.addNewTbl(), this, rows,
cols);
tables.add(table);
bodyElements.add(table);
return table;
}
/**
* Gets the underlying CTComment object for the comment.
*
* @return CTComment object
*/
public CTComment getCtComment() {
return ctComment;
}
/**
* The owning object for this comment
*
* @return The {@link XWPFComments} object that contains this comment.
*/
public XWPFComments getComments() {
return comments;
}
/**
* Get a unique identifier for the current comment. The restrictions on the
* id attribute, if any, are defined by the parent XML element. If this
* attribute is omitted, then the document is non-conformant.
*
* @return string id
*/
public String getId() {
return ctComment.getId().toString();
}
/**
* Get the author of the current comment
*
* @return author of the current comment
*/
public String getAuthor() {
return ctComment.getAuthor();
}
/**
* Specifies the author for the current comment If this attribute is
* omitted, then no author shall be associated with the parent annotation
* type.
*
* @param author author of the current comment
*/
public void setAuthor(String author) {
ctComment.setAuthor(author);
}
/**
* Get the initials of the author of the current comment
*
* @return initials the initials of the author of the current comment
*/
public String getInitials() {
return ctComment.getInitials();
}
/**
* Specifies the initials of the author of the current comment
*
* @param initials the initials of the author of the current comment
*/
public void setInitials(String initials) {
ctComment.setInitials(initials);
}
/**
* Get the date information of the current comment
*
* @return the date information for the current comment.
*/
public Calendar getDate() {
return ctComment.getDate();
}
/**
* Specifies the date information for the current comment. If this attribute
* is omitted, then no date information shall be associated with the parent
* annotation type.
*
* @param date the date information for the current comment.
*/
public void setDate(Calendar date) {
ctComment.setDate(date);
}
}

View File

@ -0,0 +1,280 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.xwpf.usermodel;
import org.apache.poi.ooxml.POIXMLDocumentPart;
import org.apache.poi.ooxml.POIXMLException;
import org.apache.poi.ooxml.POIXMLRelation;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.opc.PackagePart;
import org.apache.poi.util.IOUtils;
import org.apache.poi.util.Internal;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlOptions;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTComment;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTComments;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CommentsDocument;
import javax.xml.namespace.QName;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static org.apache.poi.ooxml.POIXMLTypeLoader.DEFAULT_XML_OPTIONS;
/**
* specifies all of the comments defined in the current document
*/
public class XWPFComments extends POIXMLDocumentPart {
XWPFDocument document;
private List<XWPFComment> comments = new ArrayList<>();
private List<XWPFPictureData> pictures = new ArrayList<>();
private CTComments ctComments;
/**
* Construct XWPFComments from a package part
*
* @param part the package part holding the data of the footnotes,
*/
public XWPFComments(PackagePart part) {
super(part);
}
/**
* Construct XWPFComments from scratch for a new document.
*/
public XWPFComments() {
ctComments = CTComments.Factory.newInstance();
}
/**
* read comments form an existing package
*/
@Override
public void onDocumentRead() throws IOException {
try (InputStream is = getPackagePart().getInputStream()) {
CommentsDocument doc = CommentsDocument.Factory.parse(is, DEFAULT_XML_OPTIONS);
ctComments = doc.getComments();
for (CTComment ctComment : ctComments.getCommentList()) {
comments.add(new XWPFComment(ctComment, this));
}
} catch (XmlException e) {
throw new POIXMLException("Unable to read comments", e);
}
for (POIXMLDocumentPart poixmlDocumentPart : getRelations()) {
if (poixmlDocumentPart instanceof XWPFPictureData) {
XWPFPictureData xwpfPicData = (XWPFPictureData) poixmlDocumentPart;
pictures.add(xwpfPicData);
document.registerPackagePictureData(xwpfPicData);
}
}
}
/**
* Adds a picture to the comments.
*
* @param is The stream to read image from
* @param format The format of the picture.
* @return the index to this picture (0 based), the added picture can be
* obtained from {@link #getAllPictures()} .
* @throws InvalidFormatException If the format of the picture is not known.
* @throws IOException If reading the picture-data from the stream fails.
*/
public String addPictureData(InputStream is, int format) throws InvalidFormatException, IOException {
byte[] data = IOUtils.toByteArray(is);
return addPictureData(data, format);
}
/**
* Adds a picture to the comments.
*
* @param pictureData The picture data
* @param format The format of the picture.
* @return the index to this picture (0 based), the added picture can be
* obtained from {@link #getAllPictures()} .
* @throws InvalidFormatException If the format of the picture is not known.
*/
public String addPictureData(byte[] pictureData, int format) throws InvalidFormatException {
XWPFPictureData xwpfPicData = document.findPackagePictureData(pictureData, format);
POIXMLRelation relDesc = XWPFPictureData.RELATIONS[format];
if (xwpfPicData == null) {
/* Part doesn't exist, create a new one */
int idx = getXWPFDocument().getNextPicNameNumber(format);
xwpfPicData = (XWPFPictureData) createRelationship(relDesc, XWPFFactory.getInstance(), idx);
/* write bytes to new part */
PackagePart picDataPart = xwpfPicData.getPackagePart();
try (OutputStream out = picDataPart.getOutputStream()) {
out.write(pictureData);
} catch (IOException e) {
throw new POIXMLException(e);
}
document.registerPackagePictureData(xwpfPicData);
pictures.add(xwpfPicData);
return getRelationId(xwpfPicData);
} else if (!getRelations().contains(xwpfPicData)) {
/*
* Part already existed, but was not related so far. Create
* relationship to the already existing part and update
* POIXMLDocumentPart data.
*/
// TODO add support for TargetMode.EXTERNAL relations.
RelationPart rp = addRelation(null, XWPFRelation.IMAGES, xwpfPicData);
pictures.add(xwpfPicData);
return rp.getRelationship().getId();
} else {
/* Part already existed, get relation id and return it */
return getRelationId(xwpfPicData);
}
}
/**
* save and commit 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();
ctComments.save(out, xmlOptions);
out.close();
}
public List<XWPFPictureData> getAllPictures() {
return Collections.unmodifiableList(pictures);
}
/**
* Gets the underlying CTComments object for the comments.
*
* @return CTComments object
*/
public CTComments getCtComments() {
return ctComments;
}
/**
* set a new comments
*/
@Internal
public void setCtComments(CTComments ctComments) {
this.ctComments = ctComments;
}
/**
* Get the list of {@link XWPFComment} in the Comments part.
*
* @return
*/
public List<XWPFComment> getComments() {
return comments;
}
/**
* Get the specified comment by position
*
* @param pos Array position of the comment
* @return
*/
public XWPFComment getComment(int pos) {
if (pos >= 0 && pos < ctComments.sizeOfCommentArray()) {
return getComments().get(pos);
}
return null;
}
/**
* Get the specified comment by comment id
*
* @param id comment id
* @return the specified comment
*/
public XWPFComment getCommentByID(String id) {
for (XWPFComment comment : comments) {
if (comment.getId().equals(id)) {
return comment;
}
}
return null;
}
/**
* Get the specified comment by ctComment
*
* @param ctComment
* @return
*/
public XWPFComment getComment(CTComment ctComment) {
for (int i = 0; i < comments.size(); i++) {
if (comments.get(i).getCtComment() == ctComment) {
return comments.get(i);
}
}
return null;
}
/**
* Create a new comment and add it to the document.
*
* @param cid comment Id
* @return
*/
public XWPFComment createComment(BigInteger cid) {
CTComment ctComment = ctComments.addNewComment();
ctComment.setId(cid);
XWPFComment comment = new XWPFComment(ctComment, this);
comments.add(comment);
return comment;
}
/**
* Remove the specified comment if present.
*
* @param pos Array position of the comment to be removed
* @return True if the comment was removed.
*/
public boolean removeComment(int pos) {
if (pos >= 0 && pos < ctComments.sizeOfCommentArray()) {
comments.remove(pos);
ctComments.removeComment(pos);
return true;
}
return false;
}
public XWPFDocument getXWPFDocument() {
if (null != document) {
return document;
}
return (XWPFDocument) getParent();
}
public void setXWPFDocument(XWPFDocument document) {
this.document = document;
}
}

View File

@ -85,7 +85,6 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody {
protected List<XWPFFooter> footers = new ArrayList<>();
protected List<XWPFHeader> headers = new ArrayList<>();
protected List<XWPFComment> comments = new ArrayList<>();
protected List<XWPFHyperlink> hyperlinks = new ArrayList<>();
protected List<XWPFParagraph> paragraphs = new ArrayList<>();
protected List<XWPFTable> tables = new ArrayList<>();
@ -99,6 +98,7 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody {
protected XWPFFootnotes footnotes;
private CTDocument1 ctDocument;
private XWPFSettings settings;
private XWPFComments comments;
protected final List<XWPFChart> charts = new ArrayList<>();
/**
* Keeps track on all id-values used in this document and included parts, like headers, footers, etc.
@ -217,11 +217,8 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody {
headers.add(header);
header.onDocumentRead();
} else if (relation.equals(XWPFRelation.COMMENT.getRelation())) {
// TODO Create according XWPFComment class, extending POIXMLDocumentPart
CommentsDocument cmntdoc = CommentsDocument.Factory.parse(p.getPackagePart().getInputStream(), DEFAULT_XML_OPTIONS);
for (CTComment ctcomment : cmntdoc.getComments().getCommentArray()) {
comments.add(new XWPFComment(ctcomment, this));
}
this.comments = (XWPFComments) p;
this.comments.onDocumentRead();
} else if (relation.equals(XWPFRelation.SETTINGS.getRelation())) {
settings = (XWPFSettings) p;
settings.onDocumentRead();
@ -431,18 +428,27 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody {
return hyperlinks.toArray(new XWPFHyperlink[0]);
}
public XWPFComment getCommentByID(String id) {
for (XWPFComment comment : comments) {
if (comment.getId().equals(id)) {
return comment;
}
}
/**
* Get Comments
*
* @return comments
*/
public XWPFComments getDocComments() {
return comments;
}
return null;
public XWPFComment getCommentByID(String id) {
if (null == comments) {
return null;
}
return comments.getCommentByID(id);
}
public XWPFComment[] getComments() {
return comments.toArray(new XWPFComment[0]);
if (null == comments) {
return null;
}
return comments.getComments().toArray(new XWPFComment[0]);
}
/**
@ -837,6 +843,27 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody {
paragraphs.add(p);
return p;
}
/**
* Creates an empty comments for the document if one does not already exist
*
* @return comments
*/
public XWPFComments createComments() {
if (comments == null) {
CommentsDocument commentsDoc = CommentsDocument.Factory.newInstance();
XWPFRelation relation = XWPFRelation.COMMENT;
int i = getRelationIndex(relation);
XWPFComments wrapper = (XWPFComments) createRelationship(relation, XWPFFactory.getInstance(), i);
wrapper.setCtComments(commentsDoc.addNewComments());
wrapper.setXWPFDocument(getXWPFDocument());
comments = wrapper;
}
return comments;
}
/**
* Creates an empty numbering if one does not already exist and sets the numbering member

View File

@ -137,9 +137,10 @@ public final class XWPFRelation extends POIXMLRelation {
null
);
public static final XWPFRelation COMMENT = new XWPFRelation(
null,
"application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml",
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments",
null
"/word/comments.xml",
XWPFComments::new, XWPFComments::new
);
public static final XWPFRelation FOOTNOTE = new XWPFRelation(
"application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml",

View File

@ -1082,6 +1082,10 @@ public class XWPFRun implements ISDTContents, IRunElement, CharacterRun {
XWPFHeaderFooter headerFooter = (XWPFHeaderFooter) parent.getPart();
relationId = headerFooter.addPictureData(pictureData, pictureType);
picData = (XWPFPictureData) headerFooter.getRelationById(relationId);
} else if (parent.getPart() instanceof XWPFComments) {
XWPFComments comments = (XWPFComments) parent.getPart();
relationId = comments.addPictureData(pictureData, pictureType);
picData = (XWPFPictureData) comments.getRelationById(relationId);
} else {
@SuppressWarnings("resource")
XWPFDocument doc = parent.getDocument();

View File

@ -16,14 +16,19 @@
==================================================================== */
package org.apache.poi.xwpf.usermodel;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.xwpf.XWPFTestDataSamples;
import org.junit.jupiter.api.Test;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.util.Calendar;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.*;
class TestXWPFComment {
@Test
void testText() throws IOException {
try (XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("comment.docx")) {
@ -34,4 +39,83 @@ class TestXWPFComment {
assertEquals("This is the first line\n\nThis is the second line", comment.getText());
}
}
@Test
public void testAddComment() throws IOException {
BigInteger cId = BigInteger.valueOf(0);
Calendar date = Calendar.getInstance();
try (XWPFDocument docOut = new XWPFDocument()) {
assertNull(docOut.getDocComments());
XWPFComments comments = docOut.createComments();
XWPFComment comment = comments.createComment(cId);
comment.setAuthor("Author");
comment.setInitials("s");
comment.setDate(date);
XWPFDocument docIn = XWPFTestDataSamples.writeOutAndReadBack(docOut);
assertEquals(1, docIn.getComments().length);
comment = docIn.getCommentByID(cId.toString());
assertNotNull(comment);
assertEquals("Author", comment.getAuthor());
assertEquals("s", comment.getInitials());
assertEquals(date.getTimeInMillis(), comment.getDate().getTimeInMillis());
}
}
@Test
void testRemoveComment() throws IOException {
try (XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("comment.docx")) {
assertEquals(1, doc.getComments().length);
doc.getDocComments().removeComment(0);
XWPFDocument docIn = XWPFTestDataSamples.writeOutAndReadBack(doc);
assertEquals(0, docIn.getComments().length);
}
}
@Test
void testCreateParagraph() throws IOException {
try (XWPFDocument doc = new XWPFDocument()) {
XWPFComments comments = doc.createComments();
XWPFComment comment = comments.createComment(BigInteger.ONE);
XWPFParagraph paragraph = comment.createParagraph();
paragraph.createRun().setText("comment paragraph text");
XWPFDocument docIn = XWPFTestDataSamples.writeOutAndReadBack(doc);
XWPFComment xwpfComment = docIn.getCommentByID("1");
assertEquals(1, xwpfComment.getParagraphs().size());
String text = xwpfComment.getParagraphArray(0).getText();
assertEquals("comment paragraph text", text);
}
}
@Test
void testAddPicture() throws IOException, InvalidFormatException {
try (XWPFDocument doc = new XWPFDocument()) {
XWPFComments comments = doc.createComments();
XWPFComment comment = comments.createComment(BigInteger.ONE);
XWPFParagraph paragraph = comment.createParagraph();
XWPFRun r = paragraph.createRun();
r.addPicture(new ByteArrayInputStream(new byte[0]),
Document.PICTURE_TYPE_JPEG, "test.jpg", 21, 32);
assertEquals(1, comments.getAllPictures().size());
assertEquals(1, doc.getAllPackagePictures().size());
}
}
@Test
void testCreateTable() throws IOException {
try (XWPFDocument doc = new XWPFDocument()) {
XWPFComments comments = doc.createComments();
XWPFComment comment = comments.createComment(BigInteger.ONE);
comment.createTable(1, 1);
XWPFDocument docIn = XWPFTestDataSamples.writeOutAndReadBack(doc);
XWPFComment xwpfComment = docIn.getCommentByID("1");
assertEquals(1, xwpfComment.getTables().size());
}
}
}

View File

@ -0,0 +1,62 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.xwpf.usermodel;
import org.apache.poi.xwpf.XWPFTestDataSamples;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.math.BigInteger;
import static org.junit.jupiter.api.Assertions.*;
class TestXWPFComments {
@Test
void testAddCommentsToDoc() throws IOException {
BigInteger cId = BigInteger.ZERO;
try (XWPFDocument docOut = new XWPFDocument()) {
assertNull(docOut.getDocComments());
// create comments
XWPFComments comments = docOut.createComments();
assertNotNull(comments);
assertSame(comments, docOut.createComments());
// create comment
XWPFComment comment = comments.createComment(cId);
comment.setAuthor("Author");
comment.createParagraph().createRun().setText("comment paragraph");
// apply comment to run text
XWPFParagraph paragraph = docOut.createParagraph();
paragraph.getCTP().addNewCommentRangeStart().setId(cId);
paragraph.getCTP().addNewR().addNewT().setStringValue("HelloWorld");
paragraph.getCTP().addNewCommentRangeEnd().setId(cId);
paragraph.getCTP().addNewR().addNewCommentReference().setId(cId);
// check
XWPFDocument docIn = XWPFTestDataSamples.writeOutAndReadBack(docOut);
assertNotNull(docIn.getDocComments());
assertEquals(1, docIn.getComments().length);
comment = docIn.getCommentByID("0");
assertTrue(null != comment);
assertEquals("Author", comment.getAuthor());
}
}
}