1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.imaging.icc;
18
19 import static org.apache.commons.imaging.common.BinaryFunctions.logCharQuad;
20 import static org.apache.commons.imaging.common.BinaryFunctions.read4Bytes;
21 import static org.apache.commons.imaging.common.BinaryFunctions.skipBytes;
22
23 import java.awt.color.ICC_Profile;
24 import java.io.File;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.util.logging.Level;
28 import java.util.logging.Logger;
29
30 import org.apache.commons.imaging.ImagingException;
31 import org.apache.commons.imaging.bytesource.ByteSource;
32 import org.apache.commons.imaging.common.Allocator;
33 import org.apache.commons.imaging.common.BinaryFileParser;
34 import org.apache.commons.io.IOUtils;
35
36 public class IccProfileParser extends BinaryFileParser {
37
38 private static final Logger LOGGER = Logger.getLogger(IccProfileParser.class.getName());
39
40
41
42
43 public IccProfileParser() {
44
45 }
46
47 public IccProfileInfo getIccProfileInfo(final byte[] bytes) throws IOException {
48 if (bytes == null) {
49 return null;
50 }
51 return getIccProfileInfo(ByteSource.array(bytes));
52 }
53
54 public IccProfileInfo getIccProfileInfo(final ByteSource byteSource) throws IOException {
55
56 final IccProfileInfo result;
57 try (InputStream is = byteSource.getInputStream()) {
58 result = readIccProfileInfo(is);
59 }
60
61 for (final IccTag tag : result.getTags()) {
62 final byte[] bytes = byteSource.getByteArray(tag.offset, tag.length);
63
64 tag.setData(bytes);
65
66 }
67
68 return result;
69 }
70
71 public IccProfileInfo getIccProfileInfo(final File file) throws IOException {
72 if (file == null) {
73 return null;
74 }
75
76 return getIccProfileInfo(ByteSource.file(file));
77 }
78
79 public IccProfileInfo getIccProfileInfo(final ICC_Profile iccProfile) throws IOException {
80 if (iccProfile == null) {
81 return null;
82 }
83
84 return getIccProfileInfo(ByteSource.array(iccProfile.getData()));
85 }
86
87 private IccTagType getIccTagType(final int quad) {
88 for (final IccTagType iccTagType : IccTagTypes.values()) {
89 if (iccTagType.getSignature() == quad) {
90 return iccTagType;
91 }
92 }
93
94 return null;
95 }
96
97 public boolean isSrgb(final byte[] bytes) throws IOException {
98 return isSrgb(ByteSource.array(bytes));
99 }
100
101 public boolean isSrgb(final ByteSource byteSource) throws IOException {
102
103
104
105
106
107
108
109 try (InputStream is = byteSource.getInputStream()) {
110 read4Bytes("ProfileSize", is, "Not a Valid ICC Profile", getByteOrder());
111
112
113
114
115 skipBytes(is, 4 * 5);
116
117 skipBytes(is, 12, "Not a Valid ICC Profile");
118
119 skipBytes(is, 4 * 3);
120
121 final int deviceManufacturer = read4Bytes("ProfileFileSignature", is, "Not a Valid ICC Profile", getByteOrder());
122 if (LOGGER.isLoggable(Level.FINEST)) {
123 logCharQuad("DeviceManufacturer", deviceManufacturer);
124 }
125
126 final int deviceModel = read4Bytes("DeviceModel", is, "Not a Valid ICC Profile", getByteOrder());
127 if (LOGGER.isLoggable(Level.FINEST)) {
128 logCharQuad("DeviceModel", deviceModel);
129 }
130
131 return deviceManufacturer == IccConstants.IEC && deviceModel == IccConstants.sRGB;
132 }
133 }
134
135 public boolean isSrgb(final File file) throws IOException {
136 return isSrgb(ByteSource.file(file));
137 }
138
139 public boolean isSrgb(final ICC_Profile iccProfile) throws IOException {
140 return isSrgb(ByteSource.array(iccProfile.getData()));
141 }
142
143 private IccProfileInfo readIccProfileInfo(InputStream is) throws IOException {
144 final CachingInputStream cis = new CachingInputStream(is);
145 is = cis;
146
147
148
149
150
151
152 final int profileSize = read4Bytes("ProfileSize", is, "Not a Valid ICC Profile", getByteOrder());
153
154
155
156
157
158
159
160
161
162
163
164
165 final int cmmTypeSignature = read4Bytes("Signature", is, "Not a Valid ICC Profile", getByteOrder());
166 if (LOGGER.isLoggable(Level.FINEST)) {
167 logCharQuad("CMMTypeSignature", cmmTypeSignature);
168 }
169
170 final int profileVersion = read4Bytes("ProfileVersion", is, "Not a Valid ICC Profile", getByteOrder());
171
172 final int profileDeviceClassSignature = read4Bytes("ProfileDeviceClassSignature", is, "Not a Valid ICC Profile", getByteOrder());
173 if (LOGGER.isLoggable(Level.FINEST)) {
174 logCharQuad("ProfileDeviceClassSignature", profileDeviceClassSignature);
175 }
176
177 final int colorSpace = read4Bytes("ColorSpace", is, "Not a Valid ICC Profile", getByteOrder());
178 if (LOGGER.isLoggable(Level.FINEST)) {
179 logCharQuad("ColorSpace", colorSpace);
180 }
181
182 final int profileConnectionSpace = read4Bytes("ProfileConnectionSpace", is, "Not a Valid ICC Profile", getByteOrder());
183 if (LOGGER.isLoggable(Level.FINEST)) {
184 logCharQuad("ProfileConnectionSpace", profileConnectionSpace);
185 }
186
187 skipBytes(is, 12, "Not a Valid ICC Profile");
188
189 final int profileFileSignature = read4Bytes("ProfileFileSignature", is, "Not a Valid ICC Profile", getByteOrder());
190 if (LOGGER.isLoggable(Level.FINEST)) {
191 logCharQuad("ProfileFileSignature", profileFileSignature);
192 }
193
194 final int primaryPlatformSignature = read4Bytes("PrimaryPlatformSignature", is, "Not a Valid ICC Profile", getByteOrder());
195 if (LOGGER.isLoggable(Level.FINEST)) {
196 logCharQuad("PrimaryPlatformSignature", primaryPlatformSignature);
197 }
198
199 final int variousFlags = read4Bytes("VariousFlags", is, "Not a Valid ICC Profile", getByteOrder());
200 if (LOGGER.isLoggable(Level.FINEST)) {
201 logCharQuad("VariousFlags", profileFileSignature);
202 }
203
204 final int deviceManufacturer = read4Bytes("DeviceManufacturer", is, "Not a Valid ICC Profile", getByteOrder());
205 if (LOGGER.isLoggable(Level.FINEST)) {
206 logCharQuad("DeviceManufacturer", deviceManufacturer);
207 }
208
209 final int deviceModel = read4Bytes("DeviceModel", is, "Not a Valid ICC Profile", getByteOrder());
210 if (LOGGER.isLoggable(Level.FINEST)) {
211 logCharQuad("DeviceModel", deviceModel);
212 }
213
214 skipBytes(is, 8, "Not a Valid ICC Profile");
215
216 final int renderingIntent = read4Bytes("RenderingIntent", is, "Not a Valid ICC Profile", getByteOrder());
217 if (LOGGER.isLoggable(Level.FINEST)) {
218 logCharQuad("RenderingIntent", renderingIntent);
219 }
220
221 skipBytes(is, 12, "Not a Valid ICC Profile");
222
223 final int profileCreatorSignature = read4Bytes("ProfileCreatorSignature", is, "Not a Valid ICC Profile", getByteOrder());
224 if (LOGGER.isLoggable(Level.FINEST)) {
225 logCharQuad("ProfileCreatorSignature", profileCreatorSignature);
226 }
227
228 skipBytes(is, 16, "Not a Valid ICC Profile");
229
230
231
232
233
234
235 skipBytes(is, 28, "Not a Valid ICC Profile");
236
237
238
239 final int tagCount = read4Bytes("TagCount", is, "Not a Valid ICC Profile", getByteOrder());
240
241
242 final IccTag[] tags = Allocator.array(tagCount, IccTag[]::new, IccTag.SHALLOW_SIZE);
243
244 for (int i = 0; i < tagCount; i++) {
245 final int tagSignature = read4Bytes("TagSignature[" + i + "]", is, "Not a Valid ICC Profile", getByteOrder());
246
247
248
249
250 final int offsetToData = read4Bytes("OffsetToData[" + i + "]", is, "Not a Valid ICC Profile", getByteOrder());
251 final int elementSize = read4Bytes("ElementSize[" + i + "]", is, "Not a Valid ICC Profile", getByteOrder());
252
253 final IccTagType fIccTagType = getIccTagType(tagSignature);
254
255
256
257
258
259
260
261
262
263
264
265
266 final IccTag tag = new IccTag(tagSignature, offsetToData, elementSize, fIccTagType);
267
268 tags[i] = tag;
269
270 }
271
272
273 IOUtils.consume(is);
274
275 final byte[] data = cis.getCache();
276
277 if (data.length < profileSize) {
278 throw new ImagingException("Couldn't read ICC Profile.");
279 }
280
281 final IccProfileInfo result = new IccProfileInfo(data, profileSize, cmmTypeSignature, profileVersion, profileDeviceClassSignature, colorSpace,
282 profileConnectionSpace, profileFileSignature, primaryPlatformSignature, variousFlags, deviceManufacturer, deviceModel, renderingIntent,
283 profileCreatorSignature, null, tags);
284
285 if (LOGGER.isLoggable(Level.FINEST)) {
286 LOGGER.finest("issRGB: " + result.isSrgb());
287 }
288
289 return result;
290 }
291
292 }