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