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