1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
|
/* ====================================================================
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 java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import org.apache.poi.ooxml.POIXMLDocumentPart;
import org.apache.poi.ooxml.POIXMLException;
import org.apache.poi.ooxml.POIXMLRelation;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.openxml4j.opc.PackagePart;
import org.apache.poi.util.IOUtils;
/**
* Raw picture data, normally attached to a WordprocessingML Drawing.
* As a rule, pictures are stored in the /word/media/ part of a WordprocessingML package.
*
* @author Philipp Epp
*/
public class XWPFPictureData extends POIXMLDocumentPart {
/**
* Relationships for each known picture type
*/
protected static final POIXMLRelation[] RELATIONS;
static {
RELATIONS = new POIXMLRelation[13];
RELATIONS[Document.PICTURE_TYPE_EMF] = XWPFRelation.IMAGE_EMF;
RELATIONS[Document.PICTURE_TYPE_WMF] = XWPFRelation.IMAGE_WMF;
RELATIONS[Document.PICTURE_TYPE_PICT] = XWPFRelation.IMAGE_PICT;
RELATIONS[Document.PICTURE_TYPE_JPEG] = XWPFRelation.IMAGE_JPEG;
RELATIONS[Document.PICTURE_TYPE_PNG] = XWPFRelation.IMAGE_PNG;
RELATIONS[Document.PICTURE_TYPE_DIB] = XWPFRelation.IMAGE_DIB;
RELATIONS[Document.PICTURE_TYPE_GIF] = XWPFRelation.IMAGE_GIF;
RELATIONS[Document.PICTURE_TYPE_TIFF] = XWPFRelation.IMAGE_TIFF;
RELATIONS[Document.PICTURE_TYPE_EPS] = XWPFRelation.IMAGE_EPS;
RELATIONS[Document.PICTURE_TYPE_BMP] = XWPFRelation.IMAGE_BMP;
RELATIONS[Document.PICTURE_TYPE_WPG] = XWPFRelation.IMAGE_WPG;
}
private Long checksum;
/**
* Create a new XWPFGraphicData node
*/
protected XWPFPictureData() {
super();
}
/**
* Construct XWPFPictureData from a package part
*
* @param part the package part holding the drawing data,
*
* @since POI 3.14-Beta1
*/
public XWPFPictureData(PackagePart part) {
super(part);
}
@Override
protected void onDocumentRead() throws IOException {
super.onDocumentRead();
}
/**
* Gets the picture data as a byte array.
* <p>
* Note, that this call might be expensive since all the picture data is copied into a temporary byte array.
* You can grab the picture data directly from the underlying package part as follows:
* <br>
* <code>
* InputStream is = getPackagePart().getInputStream();
* </code>
* </p>
*
* @return the Picture data.
*/
public byte[] getData() {
try {
return IOUtils.toByteArray(getPackagePart().getInputStream());
} catch (IOException e) {
throw new POIXMLException(e);
}
}
/**
* Returns the file name of the image, eg image7.jpg . The original filename
* isn't always available, but if it can be found it's likely to be in the
* CTDrawing
*/
public String getFileName() {
String name = getPackagePart().getPartName().getName();
return name.substring(name.lastIndexOf('/') + 1);
}
/**
* Suggests a file extension for this image.
*
* @return the file extension.
*/
public String suggestFileExtension() {
return getPackagePart().getPartName().getExtension();
}
/**
* Return an integer constant that specifies type of this picture
*
* @return an integer constant that specifies type of this picture
* @see org.apache.poi.xwpf.usermodel.Document#PICTURE_TYPE_EMF
* @see org.apache.poi.xwpf.usermodel.Document#PICTURE_TYPE_WMF
* @see org.apache.poi.xwpf.usermodel.Document#PICTURE_TYPE_PICT
* @see org.apache.poi.xwpf.usermodel.Document#PICTURE_TYPE_JPEG
* @see org.apache.poi.xwpf.usermodel.Document#PICTURE_TYPE_PNG
* @see org.apache.poi.xwpf.usermodel.Document#PICTURE_TYPE_DIB
*/
public int getPictureType() {
String contentType = getPackagePart().getContentType();
for (int i = 0; i < RELATIONS.length; i++) {
if (RELATIONS[i] == null) {
continue;
}
if (RELATIONS[i].getContentType().equals(contentType)) {
return i;
}
}
return 0;
}
public Long getChecksum() {
if (this.checksum == null) {
InputStream is = null;
byte[] data;
try {
is = getPackagePart().getInputStream();
data = IOUtils.toByteArray(is);
} catch (IOException e) {
throw new POIXMLException(e);
} finally {
IOUtils.closeQuietly(is);
}
this.checksum = IOUtils.calculateChecksum(data);
}
return this.checksum;
}
@Override
public boolean equals(Object obj) {
/*
* In case two objects ARE equal, but its not the same instance, this
* implementation will always run through the whole
* byte-array-comparison before returning true. If this will turn into a
* performance issue, two possible approaches are available:<br>
* a) Use the checksum only and take the risk that two images might have
* the same CRC32 sum, although they are not the same.<br>
* b) Use a second (or third) checksum algorithm to minimise the chance
* that two images have the same checksums but are not equal (e.g.
* CRC32, MD5 and SHA-1 checksums, additionally compare the
* data-byte-array lengths).
*/
if (obj == this) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof XWPFPictureData)) {
return false;
}
XWPFPictureData picData = (XWPFPictureData) obj;
PackagePart foreignPackagePart = picData.getPackagePart();
PackagePart ownPackagePart = this.getPackagePart();
if ((foreignPackagePart != null && ownPackagePart == null)
|| (foreignPackagePart == null && ownPackagePart != null)) {
return false;
}
if (ownPackagePart != null) {
OPCPackage foreignPackage = foreignPackagePart.getPackage();
OPCPackage ownPackage = ownPackagePart.getPackage();
if ((foreignPackage != null && ownPackage == null)
|| (foreignPackage == null && ownPackage != null)) {
return false;
}
if (ownPackage != null) {
if (!ownPackage.equals(foreignPackage)) {
return false;
}
}
}
Long foreignChecksum = picData.getChecksum();
Long localChecksum = getChecksum();
if (!(localChecksum.equals(foreignChecksum))) {
return false;
}
return Arrays.equals(this.getData(), picData.getData());
}
@Override
public int hashCode() {
return getChecksum().hashCode();
}
/**
* *PictureData objects store the actual content in the part directly without keeping a
* copy like all others therefore we need to handle them differently.
*/
@Override
protected void prepareForCommit() {
// do not clear the part here
}
}
|