View Javadoc
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    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.imaging.icc;
18  
19  import static org.apache.commons.imaging.common.BinaryFunctions.printCharQuad;
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  
29  import org.apache.commons.imaging.common.BinaryFileParser;
30  import org.apache.commons.imaging.common.bytesource.ByteSource;
31  import org.apache.commons.imaging.common.bytesource.ByteSourceArray;
32  import org.apache.commons.imaging.common.bytesource.ByteSourceFile;
33  import org.apache.commons.imaging.util.Debug;
34  
35  public class IccProfileParser extends BinaryFileParser {
36      public IccProfileParser() {
37          this.setByteOrder(ByteOrder.BIG_ENDIAN);
38      }
39  
40      public IccProfileInfo getICCProfileInfo(final ICC_Profile iccProfile) {
41          if (iccProfile == null) {
42              return null;
43          }
44  
45          return getICCProfileInfo(new ByteSourceArray(iccProfile.getData()));
46      }
47  
48      public IccProfileInfo getICCProfileInfo(final byte[] bytes) {
49          if (bytes == null) {
50              return null;
51          }
52  
53          return getICCProfileInfo(new ByteSourceArray(bytes));
54      }
55  
56      public IccProfileInfo getICCProfileInfo(final File file) {
57          if (file == null) {
58              return null;
59          }
60  
61          return getICCProfileInfo(new ByteSourceFile(file));
62      }
63  
64      public IccProfileInfo getICCProfileInfo(final ByteSource byteSource) {
65  
66          InputStream is = null;
67  
68          try {
69  
70              is = byteSource.getInputStream();
71              final IccProfileInfo result = readICCProfileInfo(is);
72  
73              if (result == null) {
74                  return null;
75              }
76  
77              is.close();
78              is = null;
79  
80              for (final IccTag tag : result.getTags()) {
81                  final byte[] bytes = byteSource.getBlock(tag.offset, tag.length);
82                  // Debug.debug("bytes: " + bytes.length);
83                  tag.setData(bytes);
84                  // tag.dump("\t" + i + ": ");
85              }
86              // result.fillInTagData(byteSource);
87  
88              return result;
89          } catch (final Exception e) {
90              // Debug.debug("Error: " + file.getAbsolutePath());
91              Debug.debug(e);
92          } finally {
93              try {
94                  if (is != null) {
95                      is.close();
96                  }
97              } catch (final Exception e) {
98                  Debug.debug(e);
99              }
100 
101         }
102 
103         if (getDebug()) {
104             Debug.debug();
105         }
106 
107         return null;
108     }
109 
110     private IccProfileInfo readICCProfileInfo(InputStream is) {
111         final CachingInputStream cis = new CachingInputStream(is);
112         is = cis;
113 
114         if (getDebug()) {
115             Debug.debug();
116         }
117 
118         // setDebug(true);
119 
120         // if (getDebug())
121         // Debug.debug("length: " + length);
122 
123         try {
124             final int profileSize = read4Bytes("ProfileSize", is, "Not a Valid ICC Profile", getByteOrder());
125 
126             // if (length != ProfileSize)
127             // {
128             // // Debug.debug("Unexpected Length data expected: " +
129             // Integer.toHexString((int) length)
130             // // + ", encoded: " + Integer.toHexString(ProfileSize));
131             // // Debug.debug("Unexpected Length data: " + length
132             // // + ", length: " + ProfileSize);
133             // // throw new Error("asd");
134             // return null;
135             // }
136 
137             final int cmmTypeSignature = read4Bytes("Signature", is, "Not a Valid ICC Profile", getByteOrder());
138             if (getDebug()) {
139                 printCharQuad("CMMTypeSignature", cmmTypeSignature);
140             }
141 
142             final int profileVersion = read4Bytes("ProfileVersion", is, "Not a Valid ICC Profile", getByteOrder());
143 
144             final int profileDeviceClassSignature = read4Bytes("ProfileDeviceClassSignature", is, 
145                     "Not a Valid ICC Profile", getByteOrder());
146             if (getDebug()) {
147                 printCharQuad("ProfileDeviceClassSignature", profileDeviceClassSignature);
148             }
149 
150             final int colorSpace = read4Bytes("ColorSpace", is, "Not a Valid ICC Profile", getByteOrder());
151             if (getDebug()) {
152                 printCharQuad("ColorSpace", colorSpace);
153             }
154 
155             final int profileConnectionSpace = read4Bytes("ProfileConnectionSpace", is, "Not a Valid ICC Profile", getByteOrder());
156             if (getDebug()) {
157                 printCharQuad("ProfileConnectionSpace", profileConnectionSpace);
158             }
159 
160             skipBytes(is, 12, "Not a Valid ICC Profile");
161 
162             final int profileFileSignature = read4Bytes("ProfileFileSignature", is, "Not a Valid ICC Profile", getByteOrder());
163             if (getDebug()) {
164                 printCharQuad("ProfileFileSignature", profileFileSignature);
165             }
166 
167             final int primaryPlatformSignature = read4Bytes("PrimaryPlatformSignature", is, "Not a Valid ICC Profile", getByteOrder());
168             if (getDebug()) {
169                 printCharQuad("PrimaryPlatformSignature", primaryPlatformSignature);
170             }
171 
172             final int variousFlags = read4Bytes("VariousFlags", is, "Not a Valid ICC Profile", getByteOrder());
173             if (getDebug()) {
174                 printCharQuad("VariousFlags", profileFileSignature);
175             }
176 
177             final int deviceManufacturer = read4Bytes("DeviceManufacturer", is, "Not a Valid ICC Profile", getByteOrder());
178             if (getDebug()) {
179                 printCharQuad("DeviceManufacturer", deviceManufacturer);
180             }
181 
182             final int deviceModel = read4Bytes("DeviceModel", is, "Not a Valid ICC Profile", getByteOrder());
183             if (getDebug()) {
184                 printCharQuad("DeviceModel", deviceModel);
185             }
186 
187             skipBytes(is, 8, "Not a Valid ICC Profile");
188 
189             final int renderingIntent = read4Bytes("RenderingIntent", is, "Not a Valid ICC Profile", getByteOrder());
190             if (getDebug()) {
191                 printCharQuad("RenderingIntent", renderingIntent);
192             }
193 
194             skipBytes(is, 12, "Not a Valid ICC Profile");
195 
196             final int profileCreatorSignature = read4Bytes("ProfileCreatorSignature", is, "Not a Valid ICC Profile", getByteOrder());
197             if (getDebug()) {
198                 printCharQuad("ProfileCreatorSignature", profileCreatorSignature);
199             }
200 
201             final byte[] profileId = null;
202             skipBytes(is, 16, "Not a Valid ICC Profile");
203             // readByteArray("ProfileID", 16, is,
204             // "Not a Valid ICC Profile");
205             // if (getDebug())
206             // System.out
207             // .println("ProfileID: '" + new String(ProfileID) + "'");
208 
209             skipBytes(is, 28, "Not a Valid ICC Profile");
210 
211             // this.setDebug(true);
212 
213             final int tagCount = read4Bytes("TagCount", is, "Not a Valid ICC Profile", getByteOrder());
214 
215             // List tags = new ArrayList();
216             final IccTag[] tags = new IccTag[tagCount];
217 
218             for (int i = 0; i < tagCount; i++) {
219                 final int tagSignature = read4Bytes("TagSignature[" + i + "]", is, "Not a Valid ICC Profile", getByteOrder());
220                 // Debug.debug("TagSignature t "
221                 // + Integer.toHexString(TagSignature));
222 
223                 // this.printCharQuad("TagSignature", TagSignature);
224                 final int offsetToData = read4Bytes("OffsetToData[" + i + "]", is, "Not a Valid ICC Profile", getByteOrder());
225                 final int elementSize = read4Bytes("ElementSize[" + i + "]", is, "Not a Valid ICC Profile", getByteOrder());
226 
227                 final IccTagType fIccTagType = getIccTagType(tagSignature);
228                 // if (fIccTagType == null)
229                 // throw new Error("oops.");
230 
231                 // System.out
232                 // .println("\t["
233                 // + i
234                 // + "]: "
235                 // + ((fIccTagType == null)
236                 // ? "unknown"
237                 // : fIccTagType.name));
238                 // Debug.debug();
239 
240                 final IccTag tag = new IccTag(tagSignature, offsetToData,
241                         elementSize, fIccTagType);
242                 // tag.dump("\t" + i + ": ");
243                 tags[i] = tag;
244                 // tags .add(tag);
245             }
246 
247             {
248                 // read stream to end, filling cache.
249                 while (is.read() >= 0) { // NOPMD we're doing nothing with the data
250                 }
251             }
252 
253             final byte[] data = cis.getCache();
254 
255             if (data.length < profileSize) {
256                 throw new IOException("Couldn't read ICC Profile.");
257             }
258 
259             final IccProfileInfo result = new IccProfileInfo(data, profileSize,
260                     cmmTypeSignature, profileVersion,
261                     profileDeviceClassSignature, colorSpace,
262                     profileConnectionSpace, profileFileSignature,
263                     primaryPlatformSignature, variousFlags, deviceManufacturer,
264                     deviceModel, renderingIntent, profileCreatorSignature,
265                     profileId, tags);
266 
267             if (getDebug()) {
268                 Debug.debug("issRGB: " + result.issRGB());
269             }
270 
271             return result;
272         } catch (final Exception e) {
273             Debug.debug(e);
274         }
275 
276         return null;
277     }
278 
279     private IccTagType getIccTagType(final int quad) {
280         for (final IccTagType iccTagType : IccTagTypes.values()) {
281             if (iccTagType.getSignature() == quad) {
282                 return iccTagType;
283             }
284         }
285 
286         return null;
287     }
288 
289     public boolean issRGB(final ICC_Profile iccProfile) throws IOException {
290         return issRGB(new ByteSourceArray(iccProfile.getData()));
291     }
292 
293     public boolean issRGB(final byte[] bytes) throws IOException {
294         return issRGB(new ByteSourceArray(bytes));
295     }
296 
297     public boolean issRGB(final File file) throws IOException {
298         return issRGB(new ByteSourceFile(file));
299     }
300 
301     public boolean issRGB(final ByteSource byteSource) throws IOException {
302         if (getDebug()) {
303             Debug.debug();
304         }
305 
306         // setDebug(true);
307 
308         // long length = byteSource.getLength();
309         //
310         // if (getDebug())
311         // Debug.debug("length: " + length);
312 
313         try (InputStream is = byteSource.getInputStream()) {
314             read4Bytes("ProfileSize", is, "Not a Valid ICC Profile", getByteOrder());
315 
316             // if (length != ProfileSize)
317             // return null;
318 
319             skipBytes(is, 4 * 5);
320 
321             skipBytes(is, 12, "Not a Valid ICC Profile");
322 
323             skipBytes(is, 4 * 3);
324 
325             final int deviceManufacturer = read4Bytes("ProfileFileSignature", is, "Not a Valid ICC Profile", getByteOrder());
326             if (getDebug()) {
327                 printCharQuad("DeviceManufacturer", deviceManufacturer);
328             }
329 
330             final int deviceModel = read4Bytes("DeviceModel", is, "Not a Valid ICC Profile", getByteOrder());
331             if (getDebug()) {
332                 printCharQuad("DeviceModel", deviceModel);
333             }
334 
335             final boolean result = deviceManufacturer == IccConstants.IEC && deviceModel == IccConstants.sRGB;
336             return result;
337         }
338     }
339 
340 }