Blame src/pngchunk_int.cpp

Packit 01d647
// ***************************************************************** -*- C++ -*-
Packit 01d647
/*
Packit 01d647
 * Copyright (C) 2004-2018 Exiv2 authors
Packit 01d647
 * This program is part of the Exiv2 distribution.
Packit 01d647
 *
Packit 01d647
 * This program is free software; you can redistribute it and/or
Packit 01d647
 * modify it under the terms of the GNU General Public License
Packit 01d647
 * as published by the Free Software Foundation; either version 2
Packit 01d647
 * of the License, or (at your option) any later version.
Packit 01d647
 *
Packit 01d647
 * This program is distributed in the hope that it will be useful,
Packit 01d647
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 01d647
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 01d647
 * GNU General Public License for more details.
Packit 01d647
 *
Packit 01d647
 * You should have received a copy of the GNU General Public License
Packit 01d647
 * along with this program; if not, write to the Free Software
Packit 01d647
 * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA.
Packit 01d647
 */
Packit 01d647
/*
Packit 01d647
  File:    pngchunk.cpp
Packit 01d647
 */
Packit 01d647
// *****************************************************************************
Packit 01d647
// included header files
Packit 01d647
#include "config.h"
Packit 01d647
Packit 01d647
#ifdef EXV_HAVE_LIBZ
Packit 01d647
#include "pngchunk_int.hpp"
Packit 01d647
#include "tiffimage.hpp"
Packit 01d647
#include "jpgimage.hpp"
Packit 01d647
#include "exif.hpp"
Packit 01d647
#include "iptc.hpp"
Packit 01d647
#include "image.hpp"
Packit 01d647
#include "error.hpp"
Packit 01d647
#include "enforce.hpp"
Packit 01d647
#include "helper_functions.hpp"
Packit 01d647
#include "safe_op.hpp"
Packit 01d647
Packit 01d647
// + standard includes
Packit 01d647
#include <sstream>
Packit 01d647
#include <iomanip>
Packit 01d647
#include <string>
Packit 01d647
#include <cstring>
Packit 01d647
#include <iostream>
Packit 01d647
#include <cassert>
Packit 01d647
#include <cstdio>
Packit 01d647
#include <algorithm>
Packit 01d647
Packit 01d647
#include <zlib.h>     // To uncompress or compress text chunk
Packit 01d647
Packit 01d647
/*
Packit 01d647
Packit 01d647
URLs to find informations about PNG chunks :
Packit 01d647
Packit 01d647
tEXt and zTXt chunks : http://www.vias.org/pngguide/chapter11_04.html
Packit 01d647
iTXt chunk           : http://www.vias.org/pngguide/chapter11_05.html
Packit 01d647
PNG tags             : http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/PNG.html#TextualData
Packit 01d647
Packit 01d647
*/
Packit 01d647
Packit 01d647
// *****************************************************************************
Packit 01d647
// class member definitions
Packit 01d647
namespace Exiv2 {
Packit 01d647
    namespace Internal {
Packit 01d647
Packit 01d647
    void PngChunk::decodeIHDRChunk(const DataBuf& data,
Packit 01d647
                                   int*           outWidth,
Packit 01d647
                                   int*           outHeight)
Packit 01d647
    {
Packit 01d647
        assert(data.size_ >= 8);
Packit 01d647
Packit 01d647
        // Extract image width and height from IHDR chunk.
Packit 01d647
Packit 01d647
        *outWidth  = getLong((const byte*)data.pData_,     bigEndian);
Packit 01d647
        *outHeight = getLong((const byte*)data.pData_ + 4, bigEndian);
Packit 01d647
Packit 01d647
    } // PngChunk::decodeIHDRChunk
Packit 01d647
Packit 01d647
    void PngChunk::decodeTXTChunk(Image*         pImage,
Packit 01d647
                                  const DataBuf& data,
Packit 01d647
                                  TxtChunkType   type)
Packit 01d647
    {
Packit 01d647
        DataBuf key = keyTXTChunk(data);
Packit 01d647
        DataBuf arr = parseTXTChunk(data, key.size_, type);
Packit 01d647
Packit 01d647
#ifdef EXIV2_DEBUG_MESSAGES
Packit 01d647
        std::cout << "Exiv2::PngChunk::decodeTXTChunk: TXT chunk data: "
Packit 01d647
                  << std::string((const char*)arr.pData_, arr.size_) << std::endl;
Packit 01d647
#endif
Packit 01d647
        parseChunkContent(pImage, key.pData_, key.size_, arr);
Packit 01d647
Packit 01d647
    } // PngChunk::decodeTXTChunk
Packit 01d647
Packit 01d647
    DataBuf PngChunk::decodeTXTChunk(const DataBuf& data,
Packit 01d647
                                     TxtChunkType   type)
Packit 01d647
    {
Packit 01d647
        DataBuf key = keyTXTChunk(data);
Packit 01d647
Packit 01d647
#ifdef EXIV2_DEBUG_MESSAGES
Packit 01d647
        std::cout << "Exiv2::PngChunk::decodeTXTChunk: TXT chunk key: "
Packit 01d647
                  << std::string((const char*)key.pData_, key.size_) << std::endl;
Packit 01d647
#endif
Packit 01d647
        return parseTXTChunk(data, key.size_, type);
Packit 01d647
Packit 01d647
    } // PngChunk::decodeTXTChunk
Packit 01d647
Packit 01d647
    DataBuf PngChunk::keyTXTChunk(const DataBuf& data, bool stripHeader)
Packit 01d647
    {
Packit 01d647
        // From a tEXt, zTXt, or iTXt chunk,
Packit 01d647
        // we get the key, it's a null terminated string at the chunk start
Packit 01d647
        const int offset = stripHeader ? 8 : 0;
Packit 01d647
        if (data.size_ <= offset) throw Error(kerFailedToReadImageData);
Packit 01d647
        const byte *key = data.pData_ + offset;
Packit 01d647
Packit 01d647
        // Find null string at end of key.
Packit 01d647
        int keysize=0;
Packit 01d647
        while (key[keysize] != 0)
Packit 01d647
        {
Packit 01d647
            keysize++;
Packit 01d647
            // look if keysize is valid.
Packit 01d647
            if (keysize+offset >= data.size_)
Packit 01d647
                throw Error(kerFailedToReadImageData);
Packit 01d647
        }
Packit 01d647
Packit 01d647
        return DataBuf(key, keysize);
Packit 01d647
Packit 01d647
    } // PngChunk::keyTXTChunk
Packit 01d647
Packit 01d647
    DataBuf PngChunk::parseTXTChunk(const DataBuf& data,
Packit 01d647
                                    int            keysize,
Packit 01d647
                                    TxtChunkType   type)
Packit 01d647
    {
Packit 01d647
        DataBuf arr;
Packit 01d647
Packit 01d647
        if(type == zTXt_Chunk)
Packit 01d647
        {
Packit 01d647
            enforce(data.size_ >= Safe::add(keysize, 2), Exiv2::kerCorruptedMetadata);
Packit 01d647
Packit 01d647
            // Extract a deflate compressed Latin-1 text chunk
Packit 01d647
Packit 01d647
            // we get the compression method after the key
Packit 01d647
            const byte* compressionMethod = data.pData_ + keysize + 1;
Packit 01d647
            if ( *compressionMethod != 0x00 )
Packit 01d647
            {
Packit 01d647
                // then it isn't zlib compressed and we are sunk
Packit 01d647
#ifdef EXIV2_DEBUG_MESSAGES
Packit 01d647
                std::cerr << "Exiv2::PngChunk::parseTXTChunk: Non-standard zTXt compression method.\n";
Packit 01d647
#endif
Packit 01d647
                throw Error(kerFailedToReadImageData);
Packit 01d647
            }
Packit 01d647
Packit 01d647
            // compressed string after the compression technique spec
Packit 01d647
            const byte* compressedText      = data.pData_ + keysize + 2;
Packit 01d647
            long compressedTextSize = data.size_  - keysize - 2;
Packit 01d647
            enforce(compressedTextSize < data.size_, kerCorruptedMetadata);
Packit 01d647
Packit 01d647
            zlibUncompress(compressedText, compressedTextSize, arr);
Packit 01d647
        }
Packit 01d647
        else if(type == tEXt_Chunk)
Packit 01d647
        {
Packit 01d647
            enforce(data.size_ >= Safe::add(keysize, 1), Exiv2::kerCorruptedMetadata);
Packit 01d647
            // Extract a non-compressed Latin-1 text chunk
Packit 01d647
Packit 01d647
            // the text comes after the key, but isn't null terminated
Packit 01d647
            const byte* text = data.pData_ + keysize + 1;
Packit 01d647
            long textsize    = data.size_  - keysize - 1;
Packit 01d647
Packit 01d647
            arr = DataBuf(text, textsize);
Packit 01d647
        }
Packit 01d647
        else if(type == iTXt_Chunk)
Packit 01d647
        {
Packit 01d647
            enforce(data.size_ >= Safe::add(keysize, 3), Exiv2::kerCorruptedMetadata);
Packit 01d647
            const size_t nullSeparators = std::count(&data.pData_[keysize+3], &data.pData_[data.size_], '\0');
Packit 01d647
            enforce(nullSeparators >= 2, Exiv2::kerCorruptedMetadata);
Packit 01d647
Packit 01d647
            // Extract a deflate compressed or uncompressed UTF-8 text chunk
Packit 01d647
Packit 01d647
            // we get the compression flag after the key
Packit 01d647
            const byte compressionFlag   = data.pData_[keysize + 1];
Packit 01d647
            // we get the compression method after the compression flag
Packit 01d647
            const byte compressionMethod = data.pData_[keysize + 2];
Packit 01d647
Packit 01d647
            enforce(compressionFlag == 0x00 || compressionFlag == 0x01, Exiv2::kerCorruptedMetadata);
Packit 01d647
            enforce(compressionMethod == 0x00, Exiv2::kerCorruptedMetadata);
Packit 01d647
Packit 01d647
            // language description string after the compression technique spec
Packit 01d647
            const size_t languageTextMaxSize = data.size_ - keysize - 3;
Packit 01d647
            std::string languageText =
Packit 01d647
                string_from_unterminated((const char*)(data.pData_ + Safe::add(keysize, 3)), languageTextMaxSize);
Packit 01d647
            const size_t languageTextSize = languageText.size();
Packit 01d647
Packit 01d647
            enforce(static_cast<unsigned long>(data.size_) >=
Packit 01d647
                    Safe::add(static_cast<size_t>(Safe::add(keysize, 4)), languageTextSize),
Packit 01d647
                    Exiv2::kerCorruptedMetadata);
Packit 01d647
            // translated keyword string after the language description
Packit 01d647
            std::string translatedKeyText =
Packit 01d647
                string_from_unterminated((const char*)(data.pData_ + keysize + 3 + languageTextSize + 1),
Packit 01d647
                                         data.size_ - (keysize + 3 + languageTextSize + 1));
Packit 01d647
            const unsigned int translatedKeyTextSize = static_cast<unsigned int>(translatedKeyText.size());
Packit 01d647
Packit 01d647
            if ((compressionFlag == 0x00) || (compressionFlag == 0x01 && compressionMethod == 0x00)) {
Packit 01d647
                enforce(Safe::add(static_cast<unsigned int>(keysize + 3 + languageTextSize + 1),
Packit 01d647
                                  Safe::add(translatedKeyTextSize, 1u)) <= static_cast<unsigned int>(data.size_),
Packit 01d647
                        Exiv2::kerCorruptedMetadata);
Packit 01d647
Packit 01d647
                const byte* text = data.pData_ + keysize + 3 + languageTextSize + 1 + translatedKeyTextSize + 1;
Packit 01d647
                const long textsize = static_cast<long>(data.size_ - (keysize + 3 + languageTextSize + 1 + translatedKeyTextSize + 1));
Packit 01d647
Packit 01d647
                if (compressionFlag == 0x00) {
Packit 01d647
                    // then it's an uncompressed iTXt chunk
Packit 01d647
#ifdef EXIV2_DEBUG_MESSAGES
Packit 01d647
                    std::cout << "Exiv2::PngChunk::parseTXTChunk: We found an uncompressed iTXt field\n";
Packit 01d647
#endif
Packit 01d647
Packit 01d647
                    arr.alloc(textsize);
Packit 01d647
                    arr = DataBuf(text, textsize);
Packit 01d647
                } else if (compressionFlag == 0x01 && compressionMethod == 0x00) {
Packit 01d647
                    // then it's a zlib compressed iTXt chunk
Packit 01d647
#ifdef EXIV2_DEBUG_MESSAGES
Packit 01d647
                    std::cout << "Exiv2::PngChunk::parseTXTChunk: We found a zlib compressed iTXt field\n";
Packit 01d647
#endif
Packit 01d647
Packit 01d647
                    // the compressed text comes after the translated keyword, but isn't null terminated
Packit 01d647
                    zlibUncompress(text, textsize, arr);
Packit 01d647
                }
Packit 01d647
            } else {
Packit 01d647
                // then it isn't zlib compressed and we are sunk
Packit 01d647
#ifdef EXIV2_DEBUG_MESSAGES
Packit 01d647
                std::cerr << "Exiv2::PngChunk::parseTXTChunk: Non-standard iTXt compression method.\n";
Packit 01d647
#endif
Packit 01d647
                throw Error(kerFailedToReadImageData);
Packit 01d647
            }
Packit 01d647
        }
Packit 01d647
        else
Packit 01d647
        {
Packit 01d647
#ifdef DEBUG
Packit 01d647
            std::cerr << "Exiv2::PngChunk::parseTXTChunk: We found a field, not expected though\n";
Packit 01d647
#endif
Packit 01d647
            throw Error(kerFailedToReadImageData);
Packit 01d647
        }
Packit 01d647
Packit 01d647
        return arr;
Packit 01d647
Packit 01d647
    } // PngChunk::parsePngChunk
Packit 01d647
Packit 01d647
    void PngChunk::parseChunkContent(      Image*  pImage,
Packit 01d647
                                     const byte*   key,
Packit 01d647
                                           long    keySize,
Packit 01d647
                                     const DataBuf arr)
Packit 01d647
    {
Packit 01d647
        // We look if an ImageMagick EXIF raw profile exist.
Packit 01d647
Packit 01d647
        if (   keySize >= 21
Packit 01d647
            && (   memcmp("Raw profile type exif", key, 21) == 0
Packit 01d647
                || memcmp("Raw profile type APP1", key, 21) == 0)
Packit 01d647
            && pImage->exifData().empty())
Packit 01d647
        {
Packit 01d647
            DataBuf exifData = readRawProfile(arr,false);
Packit 01d647
            long length      = exifData.size_;
Packit 01d647
Packit 01d647
            if (length > 0)
Packit 01d647
            {
Packit 01d647
                // Find the position of Exif header in bytes array.
Packit 01d647
Packit 01d647
                const byte exifHeader[] = { 0x45, 0x78, 0x69, 0x66, 0x00, 0x00 };
Packit 01d647
                long pos = -1;
Packit 01d647
Packit 01d647
                for (long i=0 ; i < length-(long)sizeof(exifHeader) ; i++)
Packit 01d647
                {
Packit 01d647
                    if (memcmp(exifHeader, &exifData.pData_[i], sizeof(exifHeader)) == 0)
Packit 01d647
                    {
Packit 01d647
                        pos = i;
Packit 01d647
                        break;
Packit 01d647
                    }
Packit 01d647
                }
Packit 01d647
Packit 01d647
                // If found it, store only these data at from this place.
Packit 01d647
Packit 01d647
                if (pos !=-1)
Packit 01d647
                {
Packit 01d647
#ifdef EXIV2_DEBUG_MESSAGES
Packit 01d647
                    std::cout << "Exiv2::PngChunk::parseChunkContent: Exif header found at position " << pos << "\n";
Packit 01d647
#endif
Packit 01d647
                    pos = pos + sizeof(exifHeader);
Packit 01d647
                    ByteOrder bo = TiffParser::decode(pImage->exifData(),
Packit 01d647
                                                      pImage->iptcData(),
Packit 01d647
                                                      pImage->xmpData(),
Packit 01d647
                                                      exifData.pData_ + pos,
Packit 01d647
                                                      length - pos);
Packit 01d647
                    pImage->setByteOrder(bo);
Packit 01d647
                }
Packit 01d647
                else
Packit 01d647
                {
Packit 01d647
#ifndef SUPPRESS_WARNINGS
Packit 01d647
                    EXV_WARNING << "Failed to decode Exif metadata.\n";
Packit 01d647
#endif
Packit 01d647
                    pImage->exifData().clear();
Packit 01d647
                }
Packit 01d647
            }
Packit 01d647
        }
Packit 01d647
Packit 01d647
        // We look if an ImageMagick IPTC raw profile exist.
Packit 01d647
Packit 01d647
        if (   keySize >= 21
Packit 01d647
            && memcmp("Raw profile type iptc", key, 21) == 0
Packit 01d647
            && pImage->iptcData().empty()) {
Packit 01d647
            DataBuf psData = readRawProfile(arr,false);
Packit 01d647
            if (psData.size_ > 0) {
Packit 01d647
                Blob iptcBlob;
Packit 01d647
                const byte *record = 0;
Packit 01d647
                uint32_t sizeIptc = 0;
Packit 01d647
                uint32_t sizeHdr = 0;
Packit 01d647
Packit 01d647
                const byte* pEnd = psData.pData_ + psData.size_;
Packit 01d647
                const byte* pCur = psData.pData_;
Packit 01d647
                while (   pCur < pEnd
Packit 01d647
                       && 0 == Photoshop::locateIptcIrb(pCur,
Packit 01d647
                                                        static_cast<long>(pEnd - pCur),
Packit 01d647
                                                        &record,
Packit 01d647
                                                        &sizeHdr,
Packit 01d647
                                                        &sizeIptc)) {
Packit 01d647
                    if (sizeIptc) {
Packit 01d647
#ifdef EXIV2_DEBUG_MESSAGES
Packit 01d647
                        std::cerr << "Found IPTC IRB, size = " << sizeIptc << "\n";
Packit 01d647
#endif
Packit 01d647
                        append(iptcBlob, record + sizeHdr, sizeIptc);
Packit 01d647
                    }
Packit 01d647
                    pCur = record + sizeHdr + sizeIptc;
Packit 01d647
                    pCur += (sizeIptc & 1);
Packit 01d647
                }
Packit 01d647
                if (   iptcBlob.size() > 0
Packit 01d647
                    && IptcParser::decode(pImage->iptcData(),
Packit 01d647
                                          &iptcBlob[0],
Packit 01d647
                                          static_cast<uint32_t>(iptcBlob.size()))) {
Packit 01d647
#ifndef SUPPRESS_WARNINGS
Packit 01d647
                    EXV_WARNING << "Failed to decode IPTC metadata.\n";
Packit 01d647
#endif
Packit 01d647
                    pImage->clearIptcData();
Packit 01d647
                }
Packit 01d647
                // If there is no IRB, try to decode the complete chunk data
Packit 01d647
                if (   iptcBlob.empty()
Packit 01d647
                    && IptcParser::decode(pImage->iptcData(),
Packit 01d647
                                          psData.pData_,
Packit 01d647
                                          psData.size_)) {
Packit 01d647
#ifndef SUPPRESS_WARNINGS
Packit 01d647
                    EXV_WARNING << "Failed to decode IPTC metadata.\n";
Packit 01d647
#endif
Packit 01d647
                    pImage->clearIptcData();
Packit 01d647
                }
Packit 01d647
            } // if (psData.size_ > 0)
Packit 01d647
        }
Packit 01d647
Packit 01d647
        // We look if an ImageMagick XMP raw profile exist.
Packit 01d647
Packit 01d647
        if (   keySize >= 20
Packit 01d647
            && memcmp("Raw profile type xmp", key, 20) == 0
Packit 01d647
            && pImage->xmpData().empty())
Packit 01d647
        {
Packit 01d647
            DataBuf xmpBuf = readRawProfile(arr,false);
Packit 01d647
            long length    = xmpBuf.size_;
Packit 01d647
Packit 01d647
            if (length > 0)
Packit 01d647
            {
Packit 01d647
                std::string& xmpPacket = pImage->xmpPacket();
Packit 01d647
                xmpPacket.assign(reinterpret_cast<char*>(xmpBuf.pData_), length);
Packit 01d647
                std::string::size_type idx = xmpPacket.find_first_of('<');
Packit 01d647
                if (idx != std::string::npos && idx > 0)
Packit 01d647
                {
Packit 01d647
#ifndef SUPPRESS_WARNINGS
Packit 01d647
                    EXV_WARNING << "Removing " << idx
Packit 01d647
                                << " characters from the beginning of the XMP packet\n";
Packit 01d647
#endif
Packit 01d647
                    xmpPacket = xmpPacket.substr(idx);
Packit 01d647
                }
Packit 01d647
                if (XmpParser::decode(pImage->xmpData(), xmpPacket))
Packit 01d647
                {
Packit 01d647
#ifndef SUPPRESS_WARNINGS
Packit 01d647
                    EXV_WARNING << "Failed to decode XMP metadata.\n";
Packit 01d647
#endif
Packit 01d647
                }
Packit 01d647
            }
Packit 01d647
        }
Packit 01d647
Packit 01d647
        // We look if an Adobe XMP string exist.
Packit 01d647
Packit 01d647
        if (   keySize >= 17
Packit 01d647
            && memcmp("XML:com.adobe.xmp", key, 17) == 0
Packit 01d647
            && pImage->xmpData().empty())
Packit 01d647
        {
Packit 01d647
            if (arr.size_ > 0)
Packit 01d647
            {
Packit 01d647
                std::string& xmpPacket = pImage->xmpPacket();
Packit 01d647
                xmpPacket.assign(reinterpret_cast<char*>(arr.pData_), arr.size_);
Packit 01d647
                std::string::size_type idx = xmpPacket.find_first_of('<');
Packit 01d647
                if (idx != std::string::npos && idx > 0)
Packit 01d647
                {
Packit 01d647
#ifndef SUPPRESS_WARNINGS
Packit 01d647
                    EXV_WARNING << "Removing " << idx << " characters "
Packit 01d647
                                << "from the beginning of the XMP packet\n";
Packit 01d647
#endif
Packit 01d647
                    xmpPacket = xmpPacket.substr(idx);
Packit 01d647
                }
Packit 01d647
                if (XmpParser::decode(pImage->xmpData(), xmpPacket))
Packit 01d647
                {
Packit 01d647
#ifndef SUPPRESS_WARNINGS
Packit 01d647
                    EXV_WARNING << "Failed to decode XMP metadata.\n";
Packit 01d647
#endif
Packit 01d647
                }
Packit 01d647
            }
Packit 01d647
        }
Packit 01d647
Packit 01d647
        // We look if a comments string exist. Note than we use only 'Description' keyword which
Packit 01d647
        // is dedicaced to store long comments. 'Comment' keyword is ignored.
Packit 01d647
Packit 01d647
        if (   keySize >= 11
Packit 01d647
            && memcmp("Description", key, 11) == 0
Packit 01d647
            && pImage->comment().empty())
Packit 01d647
        {
Packit 01d647
            pImage->setComment(std::string(reinterpret_cast<char*>(arr.pData_), arr.size_));
Packit 01d647
        }
Packit 01d647
Packit 01d647
    } // PngChunk::parseChunkContent
Packit 01d647
Packit 01d647
    std::string PngChunk::makeMetadataChunk(const std::string& metadata,
Packit 01d647
                                                  MetadataId   type)
Packit 01d647
    {
Packit 01d647
        std::string chunk;
Packit 01d647
        std::string rawProfile;
Packit 01d647
Packit 01d647
        switch (type) {
Packit 01d647
        case mdComment:
Packit 01d647
            chunk = makeUtf8TxtChunk("Description", metadata, true);
Packit 01d647
            break;
Packit 01d647
        case mdExif:
Packit 01d647
            rawProfile = writeRawProfile(metadata, "exif");
Packit 01d647
            chunk = makeAsciiTxtChunk("Raw profile type exif", rawProfile, true);
Packit 01d647
            break;
Packit 01d647
        case mdIptc:
Packit 01d647
            rawProfile = writeRawProfile(metadata, "iptc");
Packit 01d647
            chunk = makeAsciiTxtChunk("Raw profile type iptc", rawProfile, true);
Packit 01d647
            break;
Packit 01d647
        case mdXmp:
Packit 01d647
            chunk = makeUtf8TxtChunk("XML:com.adobe.xmp", metadata, false);
Packit 01d647
            break;
Packit 01d647
        case mdIccProfile:
Packit 01d647
            break;
Packit 01d647
        case mdNone:
Packit 01d647
            assert(false);
Packit 01d647
    }
Packit 01d647
Packit 01d647
        return chunk;
Packit 01d647
Packit 01d647
    } // PngChunk::makeMetadataChunk
Packit 01d647
Packit 01d647
    void PngChunk::zlibUncompress(const byte*  compressedText,
Packit 01d647
                                  unsigned int compressedTextSize,
Packit 01d647
                                  DataBuf&     arr)
Packit 01d647
    {
Packit 01d647
        uLongf uncompressedLen = compressedTextSize * 2; // just a starting point
Packit 01d647
        int zlibResult;
Packit 01d647
        int dos = 0;
Packit 01d647
Packit 01d647
        do {
Packit 01d647
            arr.alloc(uncompressedLen);
Packit 01d647
            zlibResult = uncompress((Bytef*)arr.pData_,
Packit 01d647
                                    &uncompressedLen,
Packit 01d647
                                    compressedText,
Packit 01d647
                                    compressedTextSize);
Packit 01d647
            if (zlibResult == Z_OK) {
Packit 01d647
                assert((uLongf)arr.size_ >= uncompressedLen);
Packit 01d647
                arr.size_ = uncompressedLen;
Packit 01d647
            }
Packit 01d647
            else if (zlibResult == Z_BUF_ERROR) {
Packit 01d647
                // the uncompressedArray needs to be larger
Packit 01d647
                uncompressedLen *= 2;
Packit 01d647
                // DoS protection. can't be bigger than 64k
Packit 01d647
                if (uncompressedLen > 131072) {
Packit 01d647
                    if (++dos > 1) break;
Packit 01d647
                    uncompressedLen = 131072;
Packit 01d647
                }
Packit 01d647
            }
Packit 01d647
            else {
Packit 01d647
                // something bad happened
Packit 01d647
                throw Error(kerFailedToReadImageData);
Packit 01d647
            }
Packit 01d647
        }
Packit 01d647
        while (zlibResult == Z_BUF_ERROR);
Packit 01d647
Packit 01d647
        if (zlibResult != Z_OK) {
Packit 01d647
            throw Error(kerFailedToReadImageData);
Packit 01d647
        }
Packit 01d647
    } // PngChunk::zlibUncompress
Packit 01d647
Packit 01d647
    std::string PngChunk::zlibCompress(const std::string& text)
Packit 01d647
    {
Packit 01d647
        uLongf compressedLen = static_cast<uLongf>(text.size() * 2); // just a starting point
Packit 01d647
        int zlibResult;
Packit 01d647
Packit 01d647
        DataBuf arr;
Packit 01d647
        do {
Packit 01d647
            arr.alloc(compressedLen);
Packit 01d647
            zlibResult = compress2((Bytef*)arr.pData_, &compressedLen,
Packit 01d647
                                   (const Bytef*)text.data(), static_cast<uLong>(text.size()),
Packit 01d647
                                   Z_BEST_COMPRESSION);
Packit 01d647
Packit 01d647
            switch (zlibResult) {
Packit 01d647
            case Z_OK:
Packit 01d647
                assert((uLongf)arr.size_ >= compressedLen);
Packit 01d647
                arr.size_ = compressedLen;
Packit 01d647
                break;
Packit 01d647
            case Z_BUF_ERROR:
Packit 01d647
                // The compressed array needs to be larger
Packit 01d647
#ifdef EXIV2_DEBUG_MESSAGES
Packit 01d647
                std::cout << "Exiv2::PngChunk::parsePngChunk: doubling size for compression.\n";
Packit 01d647
#endif
Packit 01d647
                compressedLen *= 2;
Packit 01d647
                // DoS protection. Cap max compressed size
Packit 01d647
                if ( compressedLen > 131072 ) throw Error(kerFailedToReadImageData);
Packit 01d647
                break;
Packit 01d647
            default:
Packit 01d647
                // Something bad happened
Packit 01d647
                throw Error(kerFailedToReadImageData);
Packit 01d647
            }
Packit 01d647
        } while (zlibResult == Z_BUF_ERROR);
Packit 01d647
Packit 01d647
        return std::string((const char*)arr.pData_, arr.size_);
Packit 01d647
Packit 01d647
    } // PngChunk::zlibCompress
Packit 01d647
Packit 01d647
    std::string PngChunk::makeAsciiTxtChunk(const std::string& keyword,
Packit 01d647
                                            const std::string& text,
Packit 01d647
                                            bool               compress)
Packit 01d647
    {
Packit 01d647
        // Chunk structure: length (4 bytes) + chunk type + chunk data + CRC (4 bytes)
Packit 01d647
        // Length is the size of the chunk data
Packit 01d647
        // CRC is calculated on chunk type + chunk data
Packit 01d647
Packit 01d647
        // Compressed text chunk using zlib.
Packit 01d647
        // Chunk data format : keyword + 0x00 + compression method (0x00) + compressed text
Packit 01d647
Packit 01d647
        // Not Compressed text chunk.
Packit 01d647
        // Chunk data format : keyword + 0x00 + text
Packit 01d647
Packit 01d647
        // Build chunk data, determine chunk type
Packit 01d647
        std::string chunkData = keyword + '\0';
Packit 01d647
        std::string chunkType;
Packit 01d647
        if (compress) {
Packit 01d647
            chunkData += '\0' + zlibCompress(text);
Packit 01d647
            chunkType = "zTXt";
Packit 01d647
        }
Packit 01d647
        else {
Packit 01d647
            chunkData += text;
Packit 01d647
            chunkType = "tEXt";
Packit 01d647
        }
Packit 01d647
        // Determine length of the chunk data
Packit 01d647
        byte length[4];
Packit 01d647
        ul2Data(length, static_cast<uint32_t>(chunkData.size()), bigEndian);
Packit 01d647
        // Calculate CRC on chunk type and chunk data
Packit 01d647
        std::string crcData = chunkType + chunkData;
Packit 01d647
        uLong tmp = crc32(0L, Z_NULL, 0);
Packit 01d647
        tmp       = crc32(tmp, (const Bytef*)crcData.data(), static_cast<uInt>(crcData.size()));
Packit 01d647
        byte crc[4];
Packit 01d647
        ul2Data(crc, tmp, bigEndian);
Packit 01d647
        // Assemble the chunk
Packit 01d647
        return std::string((const char*)length, 4) + chunkType + chunkData + std::string((const char*)crc, 4);
Packit 01d647
Packit 01d647
    } // PngChunk::makeAsciiTxtChunk
Packit 01d647
Packit 01d647
    std::string PngChunk::makeUtf8TxtChunk(const std::string& keyword,
Packit 01d647
                                           const std::string& text,
Packit 01d647
                                           bool               compress)
Packit 01d647
    {
Packit 01d647
        // Chunk structure: length (4 bytes) + chunk type + chunk data + CRC (4 bytes)
Packit 01d647
        // Length is the size of the chunk data
Packit 01d647
        // CRC is calculated on chunk type + chunk data
Packit 01d647
Packit 01d647
        // Chunk data format : keyword + 0x00 + compression flag (0x00: uncompressed - 0x01: compressed)
Packit 01d647
        //                     + compression method (0x00: zlib format) + language tag (null) + 0x00
Packit 01d647
        //                     + translated keyword (null) + 0x00 + text (compressed or not)
Packit 01d647
Packit 01d647
        // Build chunk data, determine chunk type
Packit 01d647
        std::string chunkData = keyword;
Packit 01d647
        if (compress) {
Packit 01d647
            static const char flags[] = { 0x00, 0x01, 0x00, 0x00, 0x00 };
Packit 01d647
            chunkData += std::string(flags, 5) + zlibCompress(text);
Packit 01d647
        }
Packit 01d647
        else {
Packit 01d647
            static const char flags[] = { 0x00, 0x00, 0x00, 0x00, 0x00 };
Packit 01d647
            chunkData += std::string(flags, 5) + text;
Packit 01d647
        }
Packit 01d647
        // Determine length of the chunk data
Packit 01d647
        byte length[4];
Packit 01d647
        ul2Data(length, static_cast<uint32_t>(chunkData.size()), bigEndian);
Packit 01d647
        // Calculate CRC on chunk type and chunk data
Packit 01d647
        std::string chunkType = "iTXt";
Packit 01d647
        std::string crcData = chunkType + chunkData;
Packit 01d647
        uLong tmp = crc32(0L, Z_NULL, 0);
Packit 01d647
        tmp       = crc32(tmp, (const Bytef*)crcData.data(), static_cast<uInt>(crcData.size()));
Packit 01d647
        byte crc[4];
Packit 01d647
        ul2Data(crc, tmp, bigEndian);
Packit 01d647
        // Assemble the chunk
Packit 01d647
        return std::string((const char*)length, 4) + chunkType + chunkData + std::string((const char*)crc, 4);
Packit 01d647
Packit 01d647
    } // PngChunk::makeUtf8TxtChunk
Packit 01d647
Packit 01d647
    DataBuf PngChunk::readRawProfile(const DataBuf& text,bool iTXt)
Packit 01d647
    {
Packit 01d647
        DataBuf                 info;
Packit 01d647
        unsigned char           unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
Packit 01d647
            0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
Packit 01d647
            0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
Packit 01d647
            0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
Packit 01d647
            0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
Packit 01d647
            13,14,15};
Packit 01d647
        if (text.size_ == 0) {
Packit 01d647
            return DataBuf();
Packit 01d647
        }
Packit 01d647
Packit 01d647
        if ( iTXt ) {
Packit 01d647
            info.alloc(text.size_);
Packit 01d647
            ::memcpy(info.pData_,text.pData_,text.size_);
Packit 01d647
            return  info;
Packit 01d647
        }
Packit 01d647
Packit 01d647
        const char *sp  = (char*) text.pData_+1;          // current byte (space pointer)
Packit 01d647
        const char *eot = (char*) text.pData_+text.size_; // end of text
Packit 01d647
Packit 01d647
        if (sp >= eot) {
Packit 01d647
            return DataBuf();
Packit 01d647
        }
Packit 01d647
Packit 01d647
        // Look for newline
Packit 01d647
        while (*sp != '\n')
Packit 01d647
        {
Packit 01d647
            sp++;
Packit 01d647
            if ( sp == eot )
Packit 01d647
            {
Packit 01d647
                return DataBuf();
Packit 01d647
            }
Packit 01d647
        }
Packit 01d647
        sp++ ; // step over '\n'
Packit 01d647
        if (sp == eot) {
Packit 01d647
            return DataBuf();
Packit 01d647
        }
Packit 01d647
Packit 01d647
        // Look for length
Packit 01d647
        while (*sp == '\0' || *sp == ' ' || *sp == '\n')
Packit 01d647
        {
Packit 01d647
            sp++;
Packit 01d647
            if (sp == eot )
Packit 01d647
            {
Packit 01d647
                return DataBuf();
Packit 01d647
            }
Packit 01d647
        }
Packit 01d647
Packit 01d647
        // Parse the length.
Packit 01d647
        long length = 0;
Packit 01d647
        while ('0' <= *sp && *sp <= '9')
Packit 01d647
        {
Packit 01d647
            // Compute the new length using unsigned long, so that we can
Packit 01d647
            // check for overflow.
Packit 01d647
            const unsigned long newlength = (10 * static_cast<unsigned long>(length)) + (*sp - '0');
Packit 01d647
            if (newlength > static_cast<unsigned long>(std::numeric_limits<long>::max())) {
Packit 01d647
                return DataBuf(); // Integer overflow.
Packit 01d647
            }
Packit 01d647
            length = static_cast<long>(newlength);
Packit 01d647
            sp++;
Packit 01d647
            if (sp == eot )
Packit 01d647
            {
Packit 01d647
                return DataBuf();
Packit 01d647
            }
Packit 01d647
        }
Packit 01d647
        sp++ ; // step over '\n'
Packit 01d647
        if (sp == eot) {
Packit 01d647
            return DataBuf();
Packit 01d647
        }
Packit 01d647
Packit 01d647
        enforce(length <= (eot - sp)/2, Exiv2::kerCorruptedMetadata);
Packit 01d647
Packit 01d647
        // Allocate space
Packit 01d647
        if (length == 0)
Packit 01d647
        {
Packit 01d647
#ifdef EXIV2_DEBUG_MESSAGES
Packit 01d647
            std::cerr << "Exiv2::PngChunk::readRawProfile: Unable To Copy Raw Profile: invalid profile length\n";
Packit 01d647
#endif
Packit 01d647
        }
Packit 01d647
        info.alloc(length);
Packit 01d647
        if (info.size_ != length)
Packit 01d647
        {
Packit 01d647
#ifdef EXIV2_DEBUG_MESSAGES
Packit 01d647
            std::cerr << "Exiv2::PngChunk::readRawProfile: Unable To Copy Raw Profile: cannot allocate memory\n";
Packit 01d647
#endif
Packit 01d647
            return DataBuf();
Packit 01d647
        }
Packit 01d647
Packit 01d647
        // Copy profile, skipping white space and column 1 "=" signs
Packit 01d647
Packit 01d647
        unsigned char *dp = (unsigned char*)info.pData_; // decode pointer
Packit 01d647
        unsigned int nibbles = length * 2;
Packit 01d647
Packit 01d647
        for (long i = 0; i < (long) nibbles; i++)
Packit 01d647
        {
Packit 01d647
            enforce(sp < eot, Exiv2::kerCorruptedMetadata);
Packit 01d647
            while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
Packit 01d647
            {
Packit 01d647
                if (*sp == '\0')
Packit 01d647
                {
Packit 01d647
#ifdef EXIV2_DEBUG_MESSAGES
Packit 01d647
                    std::cerr << "Exiv2::PngChunk::readRawProfile: Unable To Copy Raw Profile: ran out of data\n";
Packit 01d647
#endif
Packit 01d647
                    return DataBuf();
Packit 01d647
                }
Packit 01d647
Packit 01d647
                sp++;
Packit 01d647
                enforce(sp < eot, Exiv2::kerCorruptedMetadata);
Packit 01d647
            }
Packit 01d647
Packit 01d647
            if (i%2 == 0)
Packit 01d647
                *dp = (unsigned char) (16*unhex[(int) *sp++]);
Packit 01d647
            else
Packit 01d647
                (*dp++) += unhex[(int) *sp++];
Packit 01d647
        }
Packit 01d647
Packit 01d647
        return info;
Packit 01d647
Packit 01d647
    } // PngChunk::readRawProfile
Packit 01d647
Packit 01d647
    std::string PngChunk::writeRawProfile(const std::string& profileData,
Packit 01d647
                                          const char*        profileType)
Packit 01d647
    {
Packit 01d647
        static byte hex[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
Packit 01d647
Packit 01d647
        std::ostringstream oss;
Packit 01d647
        oss << '\n' << profileType << '\n' << std::setw(8) << profileData.size();
Packit 01d647
        const char* sp = profileData.data();
Packit 01d647
        for (std::string::size_type i = 0; i < profileData.size(); ++i) {
Packit 01d647
            if (i % 36 == 0) oss << '\n';
Packit 01d647
            oss << hex[((*sp >> 4) & 0x0f)];
Packit 01d647
            oss << hex[((*sp++) & 0x0f)];
Packit 01d647
        }
Packit 01d647
        oss << '\n';
Packit 01d647
        return oss.str();
Packit 01d647
Packit 01d647
    } // PngChunk::writeRawProfile
Packit 01d647
Packit 01d647
}}                                      // namespace Internal, Exiv2
Packit 01d647
#endif // ifdef EXV_HAVE_LIBZ
Packit 01d647