001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.imaging.icc; 018 019import static org.apache.commons.imaging.common.BinaryFunctions.printCharQuad; 020import static org.apache.commons.imaging.common.BinaryFunctions.read4Bytes; 021import static org.apache.commons.imaging.common.BinaryFunctions.skipBytes; 022 023import java.awt.color.ICC_Profile; 024import java.io.File; 025import java.io.IOException; 026import java.io.InputStream; 027import java.nio.ByteOrder; 028import java.util.logging.Level; 029import java.util.logging.Logger; 030 031import org.apache.commons.imaging.common.BinaryFileParser; 032import org.apache.commons.imaging.common.bytesource.ByteSource; 033import org.apache.commons.imaging.common.bytesource.ByteSourceArray; 034import org.apache.commons.imaging.common.bytesource.ByteSourceFile; 035 036public class IccProfileParser extends BinaryFileParser { 037 038 private static final Logger LOGGER = Logger.getLogger(IccProfileParser.class.getName()); 039 040 public IccProfileParser() { 041 this.setByteOrder(ByteOrder.BIG_ENDIAN); 042 } 043 044 public IccProfileInfo getICCProfileInfo(final ICC_Profile iccProfile) { 045 if (iccProfile == null) { 046 return null; 047 } 048 049 return getICCProfileInfo(new ByteSourceArray(iccProfile.getData())); 050 } 051 052 public IccProfileInfo getICCProfileInfo(final byte[] bytes) { 053 if (bytes == null) { 054 return null; 055 } 056 057 return getICCProfileInfo(new ByteSourceArray(bytes)); 058 } 059 060 public IccProfileInfo getICCProfileInfo(final File file) { 061 if (file == null) { 062 return null; 063 } 064 065 return getICCProfileInfo(new ByteSourceFile(file)); 066 } 067 068 public IccProfileInfo getICCProfileInfo(final ByteSource byteSource) { 069 070 InputStream is = null; 071 072 try { 073 074 is = byteSource.getInputStream(); 075 final IccProfileInfo result = readICCProfileInfo(is); 076 077 if (result == null) { 078 return null; 079 } 080 081 is.close(); 082 is = null; 083 084 for (final IccTag tag : result.getTags()) { 085 final byte[] bytes = byteSource.getBlock(tag.offset, tag.length); 086 // Debug.debug("bytes: " + bytes.length); 087 tag.setData(bytes); 088 // tag.dump("\t" + i + ": "); 089 } 090 // result.fillInTagData(byteSource); 091 092 return result; 093 } catch (final Exception e) { 094 // Debug.debug("Error: " + file.getAbsolutePath()); 095 LOGGER.log(Level.SEVERE, e.getMessage(), e); 096 } finally { 097 try { 098 if (is != null) { 099 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 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 final byte[] profileId = null; 198 skipBytes(is, 16, "Not a Valid ICC Profile"); 199 // readByteArray("ProfileID", 16, is, 200 // "Not a Valid ICC Profile"); 201 // if (LOGGER.isLoggable(Level.FINEST)) 202 // System.out 203 // .println("ProfileID: '" + new String(ProfileID) + "'"); 204 205 skipBytes(is, 28, "Not a Valid ICC Profile"); 206 207 // this.setDebug(true); 208 209 final int tagCount = read4Bytes("TagCount", is, "Not a Valid ICC Profile", getByteOrder()); 210 211 // List tags = new ArrayList(); 212 final IccTag[] tags = new IccTag[tagCount]; 213 214 for (int i = 0; i < tagCount; i++) { 215 final int tagSignature = read4Bytes("TagSignature[" + i + "]", is, "Not a Valid ICC Profile", getByteOrder()); 216 // Debug.debug("TagSignature t " 217 // + Integer.toHexString(TagSignature)); 218 219 // this.printCharQuad("TagSignature", TagSignature); 220 final int offsetToData = read4Bytes("OffsetToData[" + i + "]", is, "Not a Valid ICC Profile", getByteOrder()); 221 final int elementSize = read4Bytes("ElementSize[" + i + "]", is, "Not a Valid ICC Profile", getByteOrder()); 222 223 final IccTagType fIccTagType = getIccTagType(tagSignature); 224 // if (fIccTagType == null) 225 // throw new Error("oops."); 226 227 // System.out 228 // .println("\t[" 229 // + i 230 // + "]: " 231 // + ((fIccTagType == null) 232 // ? "unknown" 233 // : fIccTagType.name)); 234 // Debug.debug(); 235 236 final IccTag tag = new IccTag(tagSignature, offsetToData, 237 elementSize, fIccTagType); 238 // tag.dump("\t" + i + ": "); 239 tags[i] = tag; 240 // tags .add(tag); 241 } 242 243 { 244 // read stream to end, filling cache. 245 while (is.read() >= 0) { // NOPMD we're doing nothing with the data 246 } 247 } 248 249 final byte[] data = cis.getCache(); 250 251 if (data.length < profileSize) { 252 throw new IOException("Couldn't read ICC Profile."); 253 } 254 255 final IccProfileInfo result = new IccProfileInfo(data, profileSize, 256 cmmTypeSignature, profileVersion, 257 profileDeviceClassSignature, colorSpace, 258 profileConnectionSpace, profileFileSignature, 259 primaryPlatformSignature, variousFlags, deviceManufacturer, 260 deviceModel, renderingIntent, profileCreatorSignature, 261 profileId, tags); 262 263 if (LOGGER.isLoggable(Level.FINEST)) { 264 LOGGER.finest("issRGB: " + result.issRGB()); 265 } 266 267 return result; 268 } catch (final Exception e) { 269 LOGGER.log(Level.SEVERE, e.getMessage(), e); 270 } 271 272 return null; 273 } 274 275 private IccTagType getIccTagType(final int quad) { 276 for (final IccTagType iccTagType : IccTagTypes.values()) { 277 if (iccTagType.getSignature() == quad) { 278 return iccTagType; 279 } 280 } 281 282 return null; 283 } 284 285 public boolean issRGB(final ICC_Profile iccProfile) throws IOException { 286 return issRGB(new ByteSourceArray(iccProfile.getData())); 287 } 288 289 public boolean issRGB(final byte[] bytes) throws IOException { 290 return issRGB(new ByteSourceArray(bytes)); 291 } 292 293 public boolean issRGB(final File file) throws IOException { 294 return issRGB(new ByteSourceFile(file)); 295 } 296 297 public boolean issRGB(final ByteSource byteSource) throws IOException { 298 // setDebug(true); 299 300 // long length = byteSource.getLength(); 301 // 302 // if (LOGGER.isLoggable(Level.FINEST)) 303 // Debug.debug("length: " + length); 304 305 try (InputStream is = byteSource.getInputStream()) { 306 read4Bytes("ProfileSize", is, "Not a Valid ICC Profile", getByteOrder()); 307 308 // if (length != ProfileSize) 309 // return null; 310 311 skipBytes(is, 4 * 5); 312 313 skipBytes(is, 12, "Not a Valid ICC Profile"); 314 315 skipBytes(is, 4 * 3); 316 317 final int deviceManufacturer = read4Bytes("ProfileFileSignature", is, "Not a Valid ICC Profile", getByteOrder()); 318 if (LOGGER.isLoggable(Level.FINEST)) { 319 printCharQuad("DeviceManufacturer", deviceManufacturer); 320 } 321 322 final int deviceModel = read4Bytes("DeviceModel", is, "Not a Valid ICC Profile", getByteOrder()); 323 if (LOGGER.isLoggable(Level.FINEST)) { 324 printCharQuad("DeviceModel", deviceModel); 325 } 326 327 final boolean result = deviceManufacturer == IccConstants.IEC && deviceModel == IccConstants.sRGB; 328 return result; 329 } 330 } 331 332}