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.formats.png.chunks;
018
019import java.io.ByteArrayInputStream;
020import java.io.IOException;
021import java.nio.charset.StandardCharsets;
022import java.util.Arrays;
023import java.util.logging.Level;
024import java.util.logging.Logger;
025import java.util.zip.InflaterInputStream;
026
027import org.apache.commons.imaging.ImagingException;
028import org.apache.commons.imaging.common.Allocator;
029import org.apache.commons.imaging.common.BinaryFunctions;
030import org.apache.commons.io.IOUtils;
031
032/**
033 * The PNG iCCP chunk. If "present, the image samples conform to the color space represented by the embedded ICC profile as defined by the International Color
034 * Consortium".
035 *
036 * @see <a href="http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html">PNG Specification</a>
037 */
038public final class PngChunkIccp extends PngChunk {
039
040    /*
041     * Logger.
042     */
043    private static final Logger LOGGER = Logger.getLogger(PngChunkIccp.class.getName());
044
045    /**
046     * ICC profile name.
047     */
048    private final String profileName;
049
050    /**
051     * Compression method.
052     */
053    private final int compressionMethod;
054
055    /**
056     * Compressed profile data.
057     */
058    private final byte[] compressedProfile;
059
060    /**
061     * Uncompressed profile data.
062     */
063    private final byte[] uncompressedProfile;
064
065    /**
066     * Constructs a new instance.
067     *
068     * @param length    chunk length
069     * @param chunkType chunk type
070     * @param crc       CRC computed over the chunk type and chunk data (but not the length)
071     * @param bytes     chunk data bytes
072     * @throws ImagingException when no profile name is present
073     * @throws IOException      when an error happens while reading the profile data
074     */
075    public PngChunkIccp(final int length, final int chunkType, final int crc, final byte[] bytes) throws ImagingException, IOException {
076        super(length, chunkType, crc, bytes);
077
078        final int index = BinaryFunctions.indexOf0(bytes, "PngChunkIccp: No Profile Name");
079        final byte[] nameBytes = Arrays.copyOf(bytes, index);
080        profileName = new String(nameBytes, StandardCharsets.ISO_8859_1);
081
082        compressionMethod = bytes[index + 1];
083
084        final int compressedProfileLength = bytes.length - (index + 1 + 1);
085        compressedProfile = Allocator.byteArray(compressedProfileLength);
086        System.arraycopy(bytes, index + 1 + 1, compressedProfile, 0, compressedProfileLength);
087
088        if (LOGGER.isLoggable(Level.FINEST)) {
089            LOGGER.finest("ProfileName: " + profileName);
090            LOGGER.finest("ProfileName.length(): " + profileName.length());
091            LOGGER.finest("CompressionMethod: " + compressionMethod);
092            LOGGER.finest("CompressedProfileLength: " + compressedProfileLength);
093            LOGGER.finest("bytes.length: " + bytes.length);
094        }
095
096        uncompressedProfile = IOUtils.toByteArray(new InflaterInputStream(new ByteArrayInputStream(compressedProfile)));
097
098        if (LOGGER.isLoggable(Level.FINEST)) {
099            LOGGER.finest("UncompressedProfile: " + bytes.length);
100        }
101    }
102
103    public byte[] getCompressedProfile() {
104        return compressedProfile.clone();
105    }
106
107    public int getCompressionMethod() {
108        return compressionMethod;
109    }
110
111    public String getProfileName() {
112        return profileName;
113    }
114
115    /**
116     * Gets a copy of the uncompressed profile data.
117     *
118     * @return the uncompressed profile data
119     */
120    public byte[] getUncompressedProfile() {
121        return uncompressedProfile.clone();
122    }
123
124}