mirror of https://github.com/apache/poi.git
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:
parent
6167f3416f
commit
0efa53456a
|
@ -26,5 +26,6 @@ public enum BodyType {
|
|||
HEADER,
|
||||
FOOTER,
|
||||
FOOTNOTE,
|
||||
TABLECELL
|
||||
TABLECELL,
|
||||
COMMENT
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -838,6 +844,27 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody {
|
|||
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
|
||||
*
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue