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