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 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 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 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 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}