You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

XWPFComments.java 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. /* ====================================================================
  2. Licensed to the Apache Software Foundation (ASF) under one or more
  3. contributor license agreements. See the NOTICE file distributed with
  4. this work for additional information regarding copyright ownership.
  5. The ASF licenses this file to You under the Apache License, Version 2.0
  6. (the "License"); you may not use this file except in compliance with
  7. the License. You may obtain a copy of the License at
  8. http://www.apache.org/licenses/LICENSE-2.0
  9. Unless required by applicable law or agreed to in writing, software
  10. distributed under the License is distributed on an "AS IS" BASIS,
  11. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. See the License for the specific language governing permissions and
  13. limitations under the License.
  14. ==================================================================== */
  15. package org.apache.poi.xwpf.usermodel;
  16. import org.apache.poi.common.usermodel.PictureType;
  17. import org.apache.poi.ooxml.POIXMLDocumentPart;
  18. import org.apache.poi.ooxml.POIXMLException;
  19. import org.apache.poi.ooxml.POIXMLRelation;
  20. import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
  21. import org.apache.poi.openxml4j.opc.PackagePart;
  22. import org.apache.poi.util.IOUtils;
  23. import org.apache.poi.util.Internal;
  24. import org.apache.xmlbeans.XmlException;
  25. import org.apache.xmlbeans.XmlOptions;
  26. import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTComment;
  27. import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTComments;
  28. import org.openxmlformats.schemas.wordprocessingml.x2006.main.CommentsDocument;
  29. import javax.xml.namespace.QName;
  30. import java.io.IOException;
  31. import java.io.InputStream;
  32. import java.io.OutputStream;
  33. import java.math.BigInteger;
  34. import java.util.ArrayList;
  35. import java.util.Collections;
  36. import java.util.List;
  37. import static org.apache.poi.ooxml.POIXMLTypeLoader.DEFAULT_XML_OPTIONS;
  38. /**
  39. * specifies all of the comments defined in the current document
  40. */
  41. public class XWPFComments extends POIXMLDocumentPart {
  42. XWPFDocument document;
  43. private final List<XWPFComment> comments = new ArrayList<>();
  44. private final List<XWPFPictureData> pictures = new ArrayList<>();
  45. private CTComments ctComments;
  46. /**
  47. * Construct XWPFComments from a package part
  48. *
  49. * @param part the package part holding the data of the footnotes,
  50. */
  51. public XWPFComments(POIXMLDocumentPart parent, PackagePart part) {
  52. super(parent, part);
  53. if (!(getParent() instanceof XWPFDocument)) {
  54. throw new IllegalStateException("Parent is not a XWPFDocuemnt: " + getParent());
  55. }
  56. this.document = (XWPFDocument) getParent();
  57. if (this.document == null) {
  58. throw new NullPointerException();
  59. }
  60. }
  61. /**
  62. * Construct XWPFComments from scratch for a new document.
  63. */
  64. public XWPFComments() {
  65. ctComments = CTComments.Factory.newInstance();
  66. }
  67. /**
  68. * read comments form an existing package
  69. */
  70. @Override
  71. public void onDocumentRead() throws IOException {
  72. try (InputStream is = getPackagePart().getInputStream()) {
  73. CommentsDocument doc = CommentsDocument.Factory.parse(is, DEFAULT_XML_OPTIONS);
  74. ctComments = doc.getComments();
  75. for (CTComment ctComment : ctComments.getCommentList()) {
  76. comments.add(new XWPFComment(ctComment, this));
  77. }
  78. } catch (XmlException e) {
  79. throw new POIXMLException("Unable to read comments", e);
  80. }
  81. for (POIXMLDocumentPart poixmlDocumentPart : getRelations()) {
  82. if (poixmlDocumentPart instanceof XWPFPictureData) {
  83. XWPFPictureData xwpfPicData = (XWPFPictureData) poixmlDocumentPart;
  84. pictures.add(xwpfPicData);
  85. document.registerPackagePictureData(xwpfPicData);
  86. }
  87. }
  88. }
  89. /**
  90. * Adds a picture to the comments.
  91. *
  92. * @param is The stream to read image from
  93. * @param format The format of the picture, see {@link Document}
  94. * @return the index to this picture (0 based), the added picture can be
  95. * obtained from {@link #getAllPictures()} .
  96. * @throws InvalidFormatException If the format of the picture is not known.
  97. * @throws IOException If reading the picture-data from the stream fails.
  98. * @see #addPictureData(InputStream, PictureType)
  99. */
  100. public String addPictureData(InputStream is, int format) throws InvalidFormatException, IOException {
  101. byte[] data = IOUtils.toByteArrayWithMaxLength(is, XWPFPictureData.getMaxImageSize());
  102. return addPictureData(data, format);
  103. }
  104. /**
  105. * Adds a picture to the comments.
  106. *
  107. * @param is The stream to read image from
  108. * @param pictureType The {@link PictureType} of the picture
  109. * @return the index to this picture (0 based), the added picture can be
  110. * obtained from {@link #getAllPictures()} .
  111. * @throws InvalidFormatException If the pictureType of the picture is not known.
  112. * @throws IOException If reading the picture-data from the stream fails.
  113. * @since POI 5.2.3
  114. */
  115. public String addPictureData(InputStream is, PictureType pictureType) throws InvalidFormatException, IOException {
  116. byte[] data = IOUtils.toByteArrayWithMaxLength(is, XWPFPictureData.getMaxImageSize());
  117. return addPictureData(data, pictureType);
  118. }
  119. /**
  120. * Adds a picture to the comments.
  121. *
  122. * @param pictureData The picture data
  123. * @param format The format of the picture, see {@link Document}
  124. * @return the index to this picture (0 based), the added picture can be
  125. * obtained from {@link #getAllPictures()} .
  126. * @throws InvalidFormatException If the format of the picture is not known.
  127. */
  128. public String addPictureData(byte[] pictureData, int format) throws InvalidFormatException {
  129. return addPictureData(pictureData, PictureType.findByOoxmlId(format));
  130. }
  131. /**
  132. * Adds a picture to the comments.
  133. *
  134. * @param pictureData The picture data
  135. * @param pictureType The {@link PictureType} of the picture.
  136. * @return the index to this picture (0 based), the added picture can be
  137. * obtained from {@link #getAllPictures()} .
  138. * @throws InvalidFormatException If the pictureType of the picture is not known.
  139. * @since POI 5.2.3
  140. */
  141. public String addPictureData(byte[] pictureData, PictureType pictureType) throws InvalidFormatException {
  142. if (pictureType == null) {
  143. throw new InvalidFormatException("pictureType is not supported");
  144. }
  145. XWPFPictureData xwpfPicData = document.findPackagePictureData(pictureData);
  146. POIXMLRelation relDesc = XWPFPictureData.RELATIONS[pictureType.ooxmlId];
  147. if (xwpfPicData == null) {
  148. /* Part doesn't exist, create a new one */
  149. int idx = getXWPFDocument().getNextPicNameNumber(pictureType);
  150. xwpfPicData = (XWPFPictureData) createRelationship(relDesc, XWPFFactory.getInstance(), idx);
  151. /* write bytes to new part */
  152. PackagePart picDataPart = xwpfPicData.getPackagePart();
  153. try (OutputStream out = picDataPart.getOutputStream()) {
  154. out.write(pictureData);
  155. } catch (IOException e) {
  156. throw new POIXMLException(e);
  157. }
  158. document.registerPackagePictureData(xwpfPicData);
  159. pictures.add(xwpfPicData);
  160. return getRelationId(xwpfPicData);
  161. } else if (!getRelations().contains(xwpfPicData)) {
  162. /*
  163. * Part already existed, but was not related so far. Create
  164. * relationship to the already existing part and update
  165. * POIXMLDocumentPart data.
  166. */
  167. // TODO add support for TargetMode.EXTERNAL relations.
  168. RelationPart rp = addRelation(null, XWPFRelation.IMAGES, xwpfPicData);
  169. pictures.add(xwpfPicData);
  170. return rp.getRelationship().getId();
  171. } else {
  172. /* Part already existed, get relation id and return it */
  173. return getRelationId(xwpfPicData);
  174. }
  175. }
  176. /**
  177. * save and commit comments
  178. */
  179. @Override
  180. protected void commit() throws IOException {
  181. XmlOptions xmlOptions = new XmlOptions(DEFAULT_XML_OPTIONS);
  182. xmlOptions.setSaveSyntheticDocumentElement(new QName(
  183. CTComments.type.getName().getNamespaceURI(), "comments"));
  184. PackagePart part = getPackagePart();
  185. try (OutputStream out = part.getOutputStream()) {
  186. ctComments.save(out, xmlOptions);
  187. }
  188. }
  189. public List<XWPFPictureData> getAllPictures() {
  190. return Collections.unmodifiableList(pictures);
  191. }
  192. /**
  193. * Gets the underlying CTComments object for the comments.
  194. *
  195. * @return CTComments object
  196. */
  197. public CTComments getCtComments() {
  198. return ctComments;
  199. }
  200. /**
  201. * set a new comments
  202. */
  203. @Internal
  204. public void setCtComments(CTComments ctComments) {
  205. this.ctComments = ctComments;
  206. }
  207. /**
  208. * Get the list of {@link XWPFComment} in the Comments part.
  209. */
  210. public List<XWPFComment> getComments() {
  211. return comments;
  212. }
  213. /**
  214. * Get the specified comment by position
  215. *
  216. * @param pos Array position of the comment
  217. */
  218. public XWPFComment getComment(int pos) {
  219. if (pos >= 0 && pos < ctComments.sizeOfCommentArray()) {
  220. return getComments().get(pos);
  221. }
  222. return null;
  223. }
  224. /**
  225. * Get the specified comment by comment id
  226. *
  227. * @param id comment id
  228. * @return the specified comment
  229. */
  230. public XWPFComment getCommentByID(String id) {
  231. for (XWPFComment comment : comments) {
  232. if (comment.getId().equals(id)) {
  233. return comment;
  234. }
  235. }
  236. return null;
  237. }
  238. /**
  239. * Get the specified comment by ctComment
  240. */
  241. public XWPFComment getComment(CTComment ctComment) {
  242. for (XWPFComment comment : comments) {
  243. if (comment.getCtComment() == ctComment) {
  244. return comment;
  245. }
  246. }
  247. return null;
  248. }
  249. /**
  250. * Create a new comment and add it to the document.
  251. *
  252. * @param cid comment Id
  253. */
  254. public XWPFComment createComment(BigInteger cid) {
  255. CTComment ctComment = ctComments.addNewComment();
  256. ctComment.setId(cid);
  257. XWPFComment comment = new XWPFComment(ctComment, this);
  258. comments.add(comment);
  259. return comment;
  260. }
  261. /**
  262. * Remove the specified comment if present.
  263. *
  264. * @param pos Array position of the comment to be removed
  265. * @return True if the comment was removed.
  266. */
  267. public boolean removeComment(int pos) {
  268. if (pos >= 0 && pos < ctComments.sizeOfCommentArray()) {
  269. comments.remove(pos);
  270. ctComments.removeComment(pos);
  271. return true;
  272. }
  273. return false;
  274. }
  275. public XWPFDocument getXWPFDocument() {
  276. if (null != document) {
  277. return document;
  278. }
  279. return (XWPFDocument) getParent();
  280. }
  281. public void setXWPFDocument(XWPFDocument document) {
  282. this.document = document;
  283. }
  284. }