|
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 Service |
be61ee |
/*
|
|
Packit Service |
be61ee |
File: jp2image.cpp
|
|
Packit Service |
be61ee |
*/
|
|
Packit Service |
be61ee |
|
|
Packit |
01d647 |
// *****************************************************************************
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// included header files
|
|
Packit |
01d647 |
#include "config.h"
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
#include "jp2image.hpp"
|
|
Packit |
01d647 |
#include "tiffimage.hpp"
|
|
Packit |
01d647 |
#include "image.hpp"
|
|
Packit |
01d647 |
#include "image_int.hpp"
|
|
Packit |
01d647 |
#include "basicio.hpp"
|
|
Packit |
01d647 |
#include "error.hpp"
|
|
Packit |
01d647 |
#include "futils.hpp"
|
|
Packit |
01d647 |
#include "types.hpp"
|
|
Packit |
01d647 |
#include "safe_op.hpp"
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// + standard includes
|
|
Packit |
01d647 |
#include <string>
|
|
Packit |
01d647 |
#include <cstring>
|
|
Packit |
01d647 |
#include <iostream>
|
|
Packit |
01d647 |
#include <cassert>
|
|
Packit |
01d647 |
#include <cstdio>
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// JPEG-2000 box types
|
|
Packit |
01d647 |
const uint32_t kJp2BoxTypeJp2Header = 0x6a703268; // 'jp2h'
|
|
Packit |
01d647 |
const uint32_t kJp2BoxTypeImageHeader = 0x69686472; // 'ihdr'
|
|
Packit |
01d647 |
const uint32_t kJp2BoxTypeColorHeader = 0x636f6c72; // 'colr'
|
|
Packit |
01d647 |
const uint32_t kJp2BoxTypeUuid = 0x75756964; // 'uuid'
|
|
Packit |
01d647 |
const uint32_t kJp2BoxTypeClose = 0x6a703263; // 'jp2c'
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// from openjpeg-2.1.2/src/lib/openjp2/jp2.h
|
|
Packit |
01d647 |
/*#define JPIP_JPIP 0x6a706970*/
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
#define JP2_JP 0x6a502020 /**< JPEG 2000 signature box */
|
|
Packit |
01d647 |
#define JP2_FTYP 0x66747970 /**< File type box */
|
|
Packit |
01d647 |
#define JP2_JP2H 0x6a703268 /**< JP2 header box (super-box) */
|
|
Packit |
01d647 |
#define JP2_IHDR 0x69686472 /**< Image header box */
|
|
Packit |
01d647 |
#define JP2_COLR 0x636f6c72 /**< Colour specification box */
|
|
Packit |
01d647 |
#define JP2_JP2C 0x6a703263 /**< Contiguous codestream box */
|
|
Packit |
01d647 |
#define JP2_URL 0x75726c20 /**< Data entry URL box */
|
|
Packit |
01d647 |
#define JP2_PCLR 0x70636c72 /**< Palette box */
|
|
Packit |
01d647 |
#define JP2_CMAP 0x636d6170 /**< Component Mapping box */
|
|
Packit |
01d647 |
#define JP2_CDEF 0x63646566 /**< Channel Definition box */
|
|
Packit |
01d647 |
#define JP2_DTBL 0x6474626c /**< Data Reference box */
|
|
Packit |
01d647 |
#define JP2_BPCC 0x62706363 /**< Bits per component box */
|
|
Packit |
01d647 |
#define JP2_JP2 0x6a703220 /**< File type fields */
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
/* For the future */
|
|
Packit |
01d647 |
/* #define JP2_RES 0x72657320 */ /**< Resolution box (super-box) */
|
|
Packit |
01d647 |
/* #define JP2_JP2I 0x6a703269 */ /**< Intellectual property box */
|
|
Packit |
01d647 |
/* #define JP2_XML 0x786d6c20 */ /**< XML box */
|
|
Packit |
01d647 |
/* #define JP2_UUID 0x75756994 */ /**< UUID box */
|
|
Packit |
01d647 |
/* #define JP2_UINF 0x75696e66 */ /**< UUID info box (super-box) */
|
|
Packit |
01d647 |
/* #define JP2_ULST 0x756c7374 */ /**< UUID list box */
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// JPEG-2000 UUIDs for embedded metadata
|
|
Packit |
01d647 |
//
|
|
Packit |
01d647 |
// See http://www.jpeg.org/public/wg1n2600.doc for information about embedding IPTC-NAA data in JPEG-2000 files
|
|
Packit |
01d647 |
// See http://www.adobe.com/devnet/xmp/pdfs/xmp_specification.pdf for information about embedding XMP data in JPEG-2000 files
|
|
Packit |
01d647 |
const unsigned char kJp2UuidExif[] = "JpgTiffExif->JP2";
|
|
Packit |
01d647 |
const unsigned char kJp2UuidIptc[] = "\x33\xc7\xa4\xd2\xb8\x1d\x47\x23\xa0\xba\xf1\xa3\xe0\x97\xad\x38";
|
|
Packit |
01d647 |
const unsigned char kJp2UuidXmp[] = "\xbe\x7a\xcf\xcb\x97\xa9\x42\xe8\x9c\x71\x99\x94\x91\xe3\xaf\xac";
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// See section B.1.1 (JPEG 2000 Signature box) of JPEG-2000 specification
|
|
Packit |
01d647 |
const unsigned char Jp2Signature[12] = { 0x00, 0x00, 0x00, 0x0c, 0x6a, 0x50, 0x20, 0x20, 0x0d, 0x0a, 0x87, 0x0a };
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
const unsigned char Jp2Blank[] = { 0x00,0x00,0x00,0x0c,0x6a,0x50,0x20,0x20,0x0d,0x0a,0x87,0x0a,0x00,0x00,0x00,0x14,
|
|
Packit |
01d647 |
0x66,0x74,0x79,0x70,0x6a,0x70,0x32,0x20,0x00,0x00,0x00,0x00,0x6a,0x70,0x32,0x20,
|
|
Packit |
01d647 |
0x00,0x00,0x00,0x2d,0x6a,0x70,0x32,0x68,0x00,0x00,0x00,0x16,0x69,0x68,0x64,0x72,
|
|
Packit |
01d647 |
0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x01,0x07,0x07,0x00,0x00,0x00,0x00,
|
|
Packit |
01d647 |
0x00,0x0f,0x63,0x6f,0x6c,0x72,0x01,0x00,0x00,0x00,0x00,0x00,0x11,0x00,0x00,0x00,
|
|
Packit |
01d647 |
0x00,0x6a,0x70,0x32,0x63,0xff,0x4f,0xff,0x51,0x00,0x29,0x00,0x00,0x00,0x00,0x00,
|
|
Packit |
01d647 |
0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
Packit |
01d647 |
0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x07,
|
|
Packit |
01d647 |
0x01,0x01,0xff,0x64,0x00,0x23,0x00,0x01,0x43,0x72,0x65,0x61,0x74,0x6f,0x72,0x3a,
|
|
Packit |
01d647 |
0x20,0x4a,0x61,0x73,0x50,0x65,0x72,0x20,0x56,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,
|
|
Packit |
01d647 |
0x31,0x2e,0x39,0x30,0x30,0x2e,0x31,0xff,0x52,0x00,0x0c,0x00,0x00,0x00,0x01,0x00,
|
|
Packit |
01d647 |
0x05,0x04,0x04,0x00,0x01,0xff,0x5c,0x00,0x13,0x40,0x40,0x48,0x48,0x50,0x48,0x48,
|
|
Packit |
01d647 |
0x50,0x48,0x48,0x50,0x48,0x48,0x50,0x48,0x48,0x50,0xff,0x90,0x00,0x0a,0x00,0x00,
|
|
Packit |
01d647 |
0x00,0x00,0x00,0x2d,0x00,0x01,0xff,0x5d,0x00,0x14,0x00,0x40,0x40,0x00,0x00,0x00,
|
|
Packit |
01d647 |
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x93,0xcf,0xb4,
|
|
Packit |
01d647 |
0x04,0x00,0x80,0x80,0x80,0x80,0x80,0xff,0xd9
|
|
Packit |
01d647 |
};
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
//! @cond IGNORE
|
|
Packit |
01d647 |
struct Jp2BoxHeader
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
uint32_t length;
|
|
Packit |
01d647 |
uint32_t type;
|
|
Packit |
01d647 |
};
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
struct Jp2ImageHeaderBox
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
uint32_t imageHeight;
|
|
Packit |
01d647 |
uint32_t imageWidth;
|
|
Packit |
01d647 |
uint16_t componentCount;
|
|
Packit |
01d647 |
uint8_t bitsPerComponent;
|
|
Packit |
01d647 |
uint8_t compressionType;
|
|
Packit |
01d647 |
uint8_t colorspaceIsUnknown;
|
|
Packit |
01d647 |
uint8_t intellectualPropertyFlag;
|
|
Packit |
01d647 |
uint16_t compressionTypeProfile;
|
|
Packit |
01d647 |
};
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
struct Jp2UuidBox
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
uint8_t uuid[16];
|
|
Packit |
01d647 |
};
|
|
Packit |
01d647 |
//! @endcond
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// *****************************************************************************
|
|
Packit |
01d647 |
// class member definitions
|
|
Packit |
01d647 |
namespace Exiv2
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
Jp2Image::Jp2Image(BasicIo::AutoPtr io, bool create)
|
|
Packit |
01d647 |
: Image(ImageType::jp2, mdExif | mdIptc | mdXmp, io)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
if (create)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
if (io_->open() == 0)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
#ifdef EXIV2_DEBUG_MESSAGES
|
|
Packit |
01d647 |
std::cerr << "Exiv2::Jp2Image:: Creating JPEG2000 image to memory" << std::endl;
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
IoCloser closer(*io_);
|
|
Packit |
01d647 |
if (io_->write(Jp2Blank, sizeof(Jp2Blank)) != sizeof(Jp2Blank))
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
#ifdef EXIV2_DEBUG_MESSAGES
|
|
Packit |
01d647 |
std::cerr << "Exiv2::Jp2Image:: Failed to create JPEG2000 image on memory" << std::endl;
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
} // Jp2Image::Jp2Image
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
std::string Jp2Image::mimeType() const
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
return "image/jp2";
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
void Jp2Image::setComment(const std::string& /*comment*/)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
// Todo: implement me!
|
|
Packit |
01d647 |
throw(Error(kerInvalidSettingForImage, "Image comment", "JP2"));
|
|
Packit |
01d647 |
} // Jp2Image::setComment
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
static void lf(std::ostream& out,bool& bLF)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
if ( bLF ) {
|
|
Packit |
01d647 |
out << std::endl;
|
|
Packit |
01d647 |
out.flush();
|
|
Packit |
01d647 |
bLF = false ;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
static bool isBigEndian()
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
union {
|
|
Packit |
01d647 |
uint32_t i;
|
|
Packit |
01d647 |
char c[4];
|
|
Packit |
01d647 |
} e = { 0x01000000 };
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
return e.c[0]?true:false;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
static std::string toAscii(long n)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
const char* p = (const char*) &n;
|
|
Packit |
01d647 |
std::string result;
|
|
Packit |
01d647 |
bool bBigEndian = isBigEndian();
|
|
Packit |
01d647 |
for ( int i = 0 ; i < 4 ; i++) {
|
|
Packit |
01d647 |
result += p[ bBigEndian ? i : (3-i) ];
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
return result;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
void Jp2Image::readMetadata()
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
#ifdef EXIV2_DEBUG_MESSAGES
|
|
Packit |
01d647 |
std::cerr << "Exiv2::Jp2Image::readMetadata: Reading JPEG-2000 file " << io_->path() << std::endl;
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
if (io_->open() != 0)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
throw Error(kerDataSourceOpenFailed, io_->path(), strError());
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
IoCloser closer(*io_);
|
|
Packit |
01d647 |
// Ensure that this is the correct image type
|
|
Packit |
01d647 |
if (!isJp2Type(*io_, true))
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
if (io_->error() || io_->eof()) throw Error(kerFailedToReadImageData);
|
|
Packit |
01d647 |
throw Error(kerNotAnImage, "JPEG-2000");
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
long position = 0;
|
|
Packit |
01d647 |
Jp2BoxHeader box = {0,0};
|
|
Packit |
01d647 |
Jp2BoxHeader subBox = {0,0};
|
|
Packit |
01d647 |
Jp2ImageHeaderBox ihdr = {0,0,0,0,0,0,0,0};
|
|
Packit |
01d647 |
Jp2UuidBox uuid = {{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}};
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
while (io_->read((byte*)&box, sizeof(box)) == sizeof(box))
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
position = io_->tell();
|
|
Packit |
01d647 |
box.length = getLong((byte*)&box.length, bigEndian);
|
|
Packit |
01d647 |
box.type = getLong((byte*)&box.type, bigEndian);
|
|
Packit |
01d647 |
#ifdef EXIV2_DEBUG_MESSAGES
|
|
Packit |
01d647 |
std::cout << "Exiv2::Jp2Image::readMetadata: "
|
|
Packit |
01d647 |
<< "Position: " << position
|
|
Packit |
01d647 |
<< " box type: " << toAscii(box.type)
|
|
Packit |
01d647 |
<< " length: " << box.length
|
|
Packit |
01d647 |
<< std::endl;
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
if (box.length == 0) return ;
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
if (box.length == 1)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
// FIXME. Special case. the real box size is given in another place.
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
switch(box.type)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
case kJp2BoxTypeJp2Header:
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
#ifdef EXIV2_DEBUG_MESSAGES
|
|
Packit |
01d647 |
std::cout << "Exiv2::Jp2Image::readMetadata: JP2Header box found" << std::endl;
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
long restore = io_->tell();
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
while (io_->read((byte*)&subBox, sizeof(subBox)) == sizeof(subBox) && subBox.length )
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
subBox.length = getLong((byte*)&subBox.length, bigEndian);
|
|
Packit |
01d647 |
subBox.type = getLong((byte*)&subBox.type, bigEndian);
|
|
Packit |
01d647 |
#ifdef EXIV2_DEBUG_MESSAGES
|
|
Packit |
01d647 |
std::cout << "Exiv2::Jp2Image::readMetadata: "
|
|
Packit |
01d647 |
<< "subBox = " << toAscii(subBox.type) << " length = " << subBox.length << std::endl;
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
if(subBox.type == kJp2BoxTypeColorHeader && subBox.length != 15)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
#ifdef EXIV2_DEBUG_MESSAGES
|
|
Packit |
01d647 |
std::cout << "Exiv2::Jp2Image::readMetadata: "
|
|
Packit |
01d647 |
<< "Color data found" << std::endl;
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
const long pad = 3 ; // 3 padding bytes 2 0 0
|
|
Packit |
01d647 |
const size_t data_length = Safe::add(subBox.length, static_cast<uint32_t>(8));
|
|
Packit |
01d647 |
// data_length makes no sense if it is larger than the rest of the file
|
|
Packit |
01d647 |
if (data_length > io_->size() - io_->tell()) {
|
|
Packit |
01d647 |
throw Error(kerCorruptedMetadata);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
DataBuf data(static_cast<long>(data_length));
|
|
Packit |
01d647 |
io_->read(data.pData_,data.size_);
|
|
Packit |
01d647 |
const long iccLength = getULong(data.pData_+pad, bigEndian);
|
|
Packit |
01d647 |
// subtracting pad from data.size_ is safe:
|
|
Packit |
01d647 |
// size_ is at least 8 and pad = 3
|
|
Packit |
01d647 |
if (iccLength > data.size_ - pad) {
|
|
Packit |
01d647 |
throw Error(kerCorruptedMetadata);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
DataBuf icc(iccLength);
|
|
Packit |
01d647 |
::memcpy(icc.pData_,data.pData_+pad,icc.size_);
|
|
Packit |
01d647 |
#ifdef EXIV2_DEBUG_MESSAGES
|
|
Packit |
01d647 |
const char* iccPath = "/tmp/libexiv2_jp2.icc";
|
|
Packit |
01d647 |
FILE* f = fopen(iccPath,"wb");
|
|
Packit |
01d647 |
if ( f ) {
|
|
Packit |
01d647 |
fwrite(icc.pData_,icc.size_,1,f);
|
|
Packit |
01d647 |
fclose(f);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
std::cout << "Exiv2::Jp2Image::readMetadata: wrote iccProfile " << icc.size_<< " bytes to " << iccPath << std::endl ;
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
setIccProfile(icc);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
if( subBox.type == kJp2BoxTypeImageHeader)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
io_->read((byte*)&ihdr, sizeof(ihdr));
|
|
Packit |
01d647 |
#ifdef EXIV2_DEBUG_MESSAGES
|
|
Packit |
01d647 |
std::cout << "Exiv2::Jp2Image::readMetadata: Ihdr data found" << std::endl;
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
ihdr.imageHeight = getLong((byte*)&ihdr.imageHeight, bigEndian);
|
|
Packit |
01d647 |
ihdr.imageWidth = getLong((byte*)&ihdr.imageWidth, bigEndian);
|
|
Packit |
01d647 |
ihdr.componentCount = getShort((byte*)&ihdr.componentCount, bigEndian);
|
|
Packit |
01d647 |
ihdr.compressionTypeProfile = getShort((byte*)&ihdr.compressionTypeProfile, bigEndian);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
pixelWidth_ = ihdr.imageWidth;
|
|
Packit |
01d647 |
pixelHeight_ = ihdr.imageHeight;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
io_->seek(restore,BasicIo::beg);
|
|
Packit Service |
be61ee |
io_->seek(subBox.length, Exiv2::BasicIo::cur);
|
|
Packit |
01d647 |
restore = io_->tell();
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
break;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
case kJp2BoxTypeUuid:
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
#ifdef EXIV2_DEBUG_MESSAGES
|
|
Packit |
01d647 |
std::cout << "Exiv2::Jp2Image::readMetadata: UUID box found" << std::endl;
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
if (io_->read((byte*)&uuid, sizeof(uuid)) == sizeof(uuid))
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
DataBuf rawData;
|
|
Packit |
01d647 |
long bufRead;
|
|
Packit |
01d647 |
bool bIsExif = memcmp(uuid.uuid, kJp2UuidExif, sizeof(uuid))==0;
|
|
Packit |
01d647 |
bool bIsIPTC = memcmp(uuid.uuid, kJp2UuidIptc, sizeof(uuid))==0;
|
|
Packit |
01d647 |
bool bIsXMP = memcmp(uuid.uuid, kJp2UuidXmp , sizeof(uuid))==0;
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
if(bIsExif)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
#ifdef EXIV2_DEBUG_MESSAGES
|
|
Packit |
01d647 |
std::cout << "Exiv2::Jp2Image::readMetadata: Exif data found" << std::endl ;
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
rawData.alloc(box.length - (sizeof(box) + sizeof(uuid)));
|
|
Packit |
01d647 |
bufRead = io_->read(rawData.pData_, rawData.size_);
|
|
Packit |
01d647 |
if (io_->error()) throw Error(kerFailedToReadImageData);
|
|
Packit |
01d647 |
if (bufRead != rawData.size_) throw Error(kerInputDataReadFailed);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
if (rawData.size_ > 0)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
// Find the position of Exif header in bytes array.
|
|
Packit |
01d647 |
long pos = ( (rawData.pData_[0] == rawData.pData_[1])
|
|
Packit |
01d647 |
&& (rawData.pData_[0]=='I' || rawData.pData_[0]=='M')
|
|
Packit |
01d647 |
) ? 0 : -1;
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// #1242 Forgive having Exif\0\0 in rawData.pData_
|
|
Packit |
01d647 |
const byte exifHeader[] = { 0x45, 0x78, 0x69, 0x66, 0x00, 0x00 };
|
|
Packit |
01d647 |
for (long i=0 ; pos < 0 && i < rawData.size_-(long)sizeof(exifHeader) ; i++)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
if (memcmp(exifHeader, &rawData.pData_[i], sizeof(exifHeader)) == 0)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
pos = i+sizeof(exifHeader);
|
|
Packit |
01d647 |
#ifndef SUPPRESS_WARNINGS
|
|
Packit |
01d647 |
EXV_WARNING << "Reading non-standard UUID-EXIF_bad box in " << io_->path() << std::endl;
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// If found it, store only these data at from this place.
|
|
Packit |
01d647 |
if (pos >= 0 )
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
#ifdef EXIV2_DEBUG_MESSAGES
|
|
Packit |
01d647 |
std::cout << "Exiv2::Jp2Image::readMetadata: Exif header found at position " << pos << std::endl;
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
ByteOrder bo = TiffParser::decode(exifData(),
|
|
Packit |
01d647 |
iptcData(),
|
|
Packit |
01d647 |
xmpData(),
|
|
Packit |
01d647 |
rawData.pData_ + pos,
|
|
Packit |
01d647 |
rawData.size_ - pos);
|
|
Packit |
01d647 |
setByteOrder(bo);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
else
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
#ifndef SUPPRESS_WARNINGS
|
|
Packit |
01d647 |
EXV_WARNING << "Failed to decode Exif metadata." << std::endl;
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
exifData_.clear();
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
if(bIsIPTC)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
#ifdef EXIV2_DEBUG_MESSAGES
|
|
Packit |
01d647 |
std::cout << "Exiv2::Jp2Image::readMetadata: Iptc data found" << std::endl;
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
rawData.alloc(box.length - (sizeof(box) + sizeof(uuid)));
|
|
Packit |
01d647 |
bufRead = io_->read(rawData.pData_, rawData.size_);
|
|
Packit |
01d647 |
if (io_->error()) throw Error(kerFailedToReadImageData);
|
|
Packit |
01d647 |
if (bufRead != rawData.size_) throw Error(kerInputDataReadFailed);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
if (IptcParser::decode(iptcData_, rawData.pData_, rawData.size_))
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
#ifndef SUPPRESS_WARNINGS
|
|
Packit |
01d647 |
EXV_WARNING << "Failed to decode IPTC metadata." << std::endl;
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
iptcData_.clear();
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
if(bIsXMP)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
#ifdef EXIV2_DEBUG_MESSAGES
|
|
Packit |
01d647 |
std::cout << "Exiv2::Jp2Image::readMetadata: Xmp data found" << std::endl;
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
rawData.alloc(box.length - (uint32_t)(sizeof(box) + sizeof(uuid)));
|
|
Packit |
01d647 |
bufRead = io_->read(rawData.pData_, rawData.size_);
|
|
Packit |
01d647 |
if (io_->error()) throw Error(kerFailedToReadImageData);
|
|
Packit |
01d647 |
if (bufRead != rawData.size_) throw Error(kerInputDataReadFailed);
|
|
Packit |
01d647 |
xmpPacket_.assign(reinterpret_cast<char *>(rawData.pData_), rawData.size_);
|
|
Packit |
01d647 |
|
|
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 " << static_cast<uint32_t>(idx)
|
|
Packit |
01d647 |
<< " characters from the beginning of the XMP packet" << std::endl;
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
xmpPacket_ = xmpPacket_.substr(idx);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
if (xmpPacket_.size() > 0 && XmpParser::decode(xmpData_, xmpPacket_))
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
#ifndef SUPPRESS_WARNINGS
|
|
Packit |
01d647 |
EXV_WARNING << "Failed to decode XMP metadata." << std::endl;
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
break;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
default:
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
break;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// Move to the next box.
|
|
Packit |
01d647 |
io_->seek(static_cast<long>(position - sizeof(box) + box.length), BasicIo::beg);
|
|
Packit |
01d647 |
if (io_->error()) throw Error(kerFailedToReadImageData);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
} // Jp2Image::readMetadata
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
void Jp2Image::printStructure(std::ostream& out, PrintStructureOption option, int depth)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
if (io_->open() != 0)
|
|
Packit |
01d647 |
throw Error(kerDataSourceOpenFailed, io_->path(), strError());
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// Ensure that this is the correct image type
|
|
Packit |
01d647 |
if (!isJp2Type(*io_, false)) {
|
|
Packit |
01d647 |
if (io_->error() || io_->eof())
|
|
Packit |
01d647 |
throw Error(kerFailedToReadImageData);
|
|
Packit |
01d647 |
throw Error(kerNotAJpeg);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
bool bPrint = option == kpsBasic || option == kpsRecursive;
|
|
Packit |
01d647 |
bool bRecursive = option == kpsRecursive;
|
|
Packit |
01d647 |
bool bICC = option == kpsIccProfile;
|
|
Packit |
01d647 |
bool bXMP = option == kpsXMP;
|
|
Packit |
01d647 |
bool bIPTCErase = option == kpsIptcErase;
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
if (bPrint) {
|
|
Packit |
01d647 |
out << "STRUCTURE OF JPEG2000 FILE: " << io_->path() << std::endl;
|
|
Packit |
01d647 |
out << " address | length | box | data" << std::endl;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
if ( bPrint || bXMP || bICC || bIPTCErase ) {
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
long position = 0;
|
|
Packit |
01d647 |
Jp2BoxHeader box = {1,1};
|
|
Packit |
01d647 |
Jp2BoxHeader subBox = {1,1};
|
|
Packit |
01d647 |
Jp2UuidBox uuid = {{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}};
|
|
Packit |
01d647 |
bool bLF = false;
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
while (box.length && box.type != kJp2BoxTypeClose && io_->read((byte*)&box, sizeof(box)) == sizeof(box))
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
position = io_->tell();
|
|
Packit |
01d647 |
box.length = getLong((byte*)&box.length, bigEndian);
|
|
Packit |
01d647 |
box.type = getLong((byte*)&box.type, bigEndian);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
if (bPrint) {
|
|
Packit |
01d647 |
out << Internal::stringFormat("%8ld | %8ld | ", (size_t)(position - sizeof(box)),
|
|
Packit |
01d647 |
(size_t)box.length)
|
|
Packit |
01d647 |
<< toAscii(box.type) << " | ";
|
|
Packit |
01d647 |
bLF = true;
|
|
Packit |
01d647 |
if (box.type == kJp2BoxTypeClose)
|
|
Packit |
01d647 |
lf(out, bLF);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
if (box.type == kJp2BoxTypeClose)
|
|
Packit |
01d647 |
break;
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
switch (box.type) {
|
|
Packit |
01d647 |
case kJp2BoxTypeJp2Header: {
|
|
Packit |
01d647 |
lf(out, bLF);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
while (io_->read((byte*)&subBox, sizeof(subBox)) == sizeof(subBox) &&
|
|
Packit |
01d647 |
io_->tell() < position + (long)box.length) // don't read beyond the box!
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
int address = io_->tell() - sizeof(subBox);
|
|
Packit |
01d647 |
subBox.length = getLong((byte*)&subBox.length, bigEndian);
|
|
Packit |
01d647 |
subBox.type = getLong((byte*)&subBox.type, bigEndian);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
if (subBox.length < sizeof(box) || subBox.length > io_->size() - io_->tell()) {
|
|
Packit |
01d647 |
throw Error(kerCorruptedMetadata);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
DataBuf data(subBox.length - sizeof(box));
|
|
Packit |
01d647 |
io_->read(data.pData_, data.size_);
|
|
Packit |
01d647 |
if (bPrint) {
|
|
Packit |
01d647 |
out << Internal::stringFormat("%8ld | %8ld | sub:", (size_t)address,
|
|
Packit |
01d647 |
(size_t)subBox.length)
|
|
Packit |
01d647 |
<< toAscii(subBox.type) << " | "
|
|
Packit |
01d647 |
<< Internal::binaryToString(makeSlice(data, 0, std::min(30l, data.size_)));
|
|
Packit |
01d647 |
bLF = true;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
if (subBox.type == kJp2BoxTypeColorHeader) {
|
|
Packit |
01d647 |
long pad = 3; // don't know why there are 3 padding bytes
|
|
Packit |
01d647 |
if (bPrint) {
|
|
Packit |
01d647 |
out << " | pad:";
|
|
Packit |
01d647 |
for (int i = 0; i < 3; i++)
|
|
Packit |
01d647 |
out << " " << (int)data.pData_[i];
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
long iccLength = getULong(data.pData_ + pad, bigEndian);
|
|
Packit |
01d647 |
if (bPrint) {
|
|
Packit |
01d647 |
out << " | iccLength:" << iccLength;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
if (bICC) {
|
|
Packit |
01d647 |
out.write((const char*)data.pData_ + pad, iccLength);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
lf(out, bLF);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
} break;
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
case kJp2BoxTypeUuid: {
|
|
Packit |
01d647 |
if (io_->read((byte*)&uuid, sizeof(uuid)) == sizeof(uuid)) {
|
|
Packit |
01d647 |
bool bIsExif = memcmp(uuid.uuid, kJp2UuidExif, sizeof(uuid)) == 0;
|
|
Packit |
01d647 |
bool bIsIPTC = memcmp(uuid.uuid, kJp2UuidIptc, sizeof(uuid)) == 0;
|
|
Packit |
01d647 |
bool bIsXMP = memcmp(uuid.uuid, kJp2UuidXmp, sizeof(uuid)) == 0;
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
bool bUnknown = !(bIsExif || bIsIPTC || bIsXMP);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
if (bPrint) {
|
|
Packit |
01d647 |
if (bIsExif)
|
|
Packit |
01d647 |
out << "Exif: ";
|
|
Packit |
01d647 |
if (bIsIPTC)
|
|
Packit |
01d647 |
out << "IPTC: ";
|
|
Packit |
01d647 |
if (bIsXMP)
|
|
Packit |
01d647 |
out << "XMP : ";
|
|
Packit |
01d647 |
if (bUnknown)
|
|
Packit |
01d647 |
out << "????: ";
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
DataBuf rawData;
|
|
Packit |
01d647 |
rawData.alloc(box.length - sizeof(uuid) - sizeof(box));
|
|
Packit |
01d647 |
long bufRead = io_->read(rawData.pData_, rawData.size_);
|
|
Packit |
01d647 |
if (io_->error())
|
|
Packit |
01d647 |
throw Error(kerFailedToReadImageData);
|
|
Packit |
01d647 |
if (bufRead != rawData.size_)
|
|
Packit |
01d647 |
throw Error(kerInputDataReadFailed);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
if (bPrint) {
|
|
Packit |
01d647 |
out << Internal::binaryToString(makeSlice(rawData, 0, 40));
|
|
Packit |
01d647 |
out.flush();
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
lf(out, bLF);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
if (bIsExif && bRecursive && rawData.size_ > 0) {
|
|
Packit |
01d647 |
if ((rawData.pData_[0] == rawData.pData_[1]) &&
|
|
Packit |
01d647 |
(rawData.pData_[0] == 'I' || rawData.pData_[0] == 'M')) {
|
|
Packit |
01d647 |
BasicIo::AutoPtr p = BasicIo::AutoPtr(new MemIo(rawData.pData_, rawData.size_));
|
|
Packit |
01d647 |
printTiffStructure(*p, out, option, depth);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
if (bIsIPTC && bRecursive) {
|
|
Packit |
01d647 |
IptcData::printStructure(out, makeSlice(rawData.pData_, 0, rawData.size_), depth);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
if (bIsXMP && bXMP) {
|
|
Packit |
01d647 |
out.write((const char*)rawData.pData_, rawData.size_);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
} break;
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
default:
|
|
Packit |
01d647 |
break;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// Move to the next box.
|
|
Packit |
01d647 |
io_->seek(static_cast<long>(position - sizeof(box) + box.length), BasicIo::beg);
|
|
Packit |
01d647 |
if (io_->error())
|
|
Packit |
01d647 |
throw Error(kerFailedToReadImageData);
|
|
Packit |
01d647 |
if (bPrint)
|
|
Packit |
01d647 |
lf(out, bLF);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
} // JpegBase::printStructure
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
void Jp2Image::writeMetadata()
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
if (io_->open() != 0)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
throw Error(kerDataSourceOpenFailed, io_->path(), strError());
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
IoCloser closer(*io_);
|
|
Packit |
01d647 |
BasicIo::AutoPtr tempIo(new MemIo);
|
|
Packit |
01d647 |
assert (tempIo.get() != 0);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
doWriteMetadata(*tempIo); // may throw
|
|
Packit |
01d647 |
io_->close();
|
|
Packit |
01d647 |
io_->transfer(*tempIo); // may throw
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
} // Jp2Image::writeMetadata
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
#ifdef __clang__
|
|
Packit |
01d647 |
// ignore cast align errors. dataBuf.pData_ is allocated by malloc() and 4 (or 8 byte aligned).
|
|
Packit |
01d647 |
#pragma clang diagnostic push
|
|
Packit |
01d647 |
#pragma clang diagnostic ignored "-Wcast-align"
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
void Jp2Image::encodeJp2Header(const DataBuf& boxBuf,DataBuf& outBuf)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
DataBuf output(boxBuf.size_ + iccProfile_.size_ + 100); // allocate sufficient space
|
|
Packit |
01d647 |
int outlen = sizeof(Jp2BoxHeader) ; // now many bytes have we written to output?
|
|
Packit |
01d647 |
int inlen = sizeof(Jp2BoxHeader) ; // how many bytes have we read from boxBuf?
|
|
Packit |
01d647 |
Jp2BoxHeader* pBox = (Jp2BoxHeader*) boxBuf.pData_;
|
|
Packit |
01d647 |
int32_t length = getLong((byte*)&pBox->length, bigEndian);
|
|
Packit |
01d647 |
int32_t count = sizeof (Jp2BoxHeader);
|
|
Packit |
01d647 |
char* p = (char*) boxBuf.pData_;
|
|
Packit |
01d647 |
bool bWroteColor = false ;
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
while ( count < length || !bWroteColor ) {
|
|
Packit |
01d647 |
Jp2BoxHeader* pSubBox = (Jp2BoxHeader*) (p+count) ;
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// copy data. pointer could be into a memory mapped file which we will decode!
|
|
Packit |
01d647 |
Jp2BoxHeader subBox = *pSubBox ;
|
|
Packit |
01d647 |
Jp2BoxHeader newBox = subBox;
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
if ( count < length ) {
|
|
Packit |
01d647 |
subBox.length = getLong((byte*)&subBox.length, bigEndian);
|
|
Packit |
01d647 |
subBox.type = getLong((byte*)&subBox.type , bigEndian);
|
|
Packit |
01d647 |
#ifdef EXIV2_DEBUG_MESSAGES
|
|
Packit |
01d647 |
std::cout << "Jp2Image::encodeJp2Header subbox: "<< toAscii(subBox.type) << " length = " << subBox.length << std::endl;
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
count += subBox.length;
|
|
Packit |
01d647 |
newBox.type = subBox.type;
|
|
Packit |
01d647 |
} else {
|
|
Packit |
01d647 |
subBox.length=0;
|
|
Packit |
01d647 |
newBox.type = kJp2BoxTypeColorHeader;
|
|
Packit |
01d647 |
count = length;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
int32_t newlen = subBox.length;
|
|
Packit |
01d647 |
if ( newBox.type == kJp2BoxTypeColorHeader ) {
|
|
Packit |
01d647 |
bWroteColor = true ;
|
|
Packit |
01d647 |
if ( ! iccProfileDefined() ) {
|
|
Packit |
01d647 |
const char* pad = "\x01\x00\x00\x00\x00\x00\x10\x00\x00\x05\x1cuuid";
|
|
Packit |
01d647 |
uint32_t psize = 15;
|
|
Packit |
01d647 |
ul2Data((byte*)&newBox.length,psize ,bigEndian);
|
|
Packit |
01d647 |
ul2Data((byte*)&newBox.type ,newBox.type,bigEndian);
|
|
Packit |
01d647 |
::memcpy(output.pData_+outlen ,&newBox ,sizeof(newBox));
|
|
Packit |
01d647 |
::memcpy(output.pData_+outlen+sizeof(newBox) ,pad ,psize );
|
|
Packit |
01d647 |
newlen = psize ;
|
|
Packit |
01d647 |
} else {
|
|
Packit |
01d647 |
const char* pad = "\0x02\x00\x00";
|
|
Packit |
01d647 |
uint32_t psize = 3;
|
|
Packit |
01d647 |
ul2Data((byte*)&newBox.length,psize+iccProfile_.size_,bigEndian);
|
|
Packit |
01d647 |
ul2Data((byte*)&newBox.type,newBox.type,bigEndian);
|
|
Packit |
01d647 |
::memcpy(output.pData_+outlen ,&newBox ,sizeof(newBox) );
|
|
Packit |
01d647 |
::memcpy(output.pData_+outlen+sizeof(newBox) , pad ,psize );
|
|
Packit |
01d647 |
::memcpy(output.pData_+outlen+sizeof(newBox)+psize,iccProfile_.pData_,iccProfile_.size_);
|
|
Packit |
01d647 |
newlen = psize + iccProfile_.size_;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
} else {
|
|
Packit |
01d647 |
::memcpy(output.pData_+outlen,boxBuf.pData_+inlen,subBox.length);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
outlen += newlen;
|
|
Packit |
01d647 |
inlen += subBox.length;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// allocate the correct number of bytes, copy the data and update the box header
|
|
Packit |
01d647 |
outBuf.alloc(outlen);
|
|
Packit |
01d647 |
::memcpy(outBuf.pData_,output.pData_,outlen);
|
|
Packit |
01d647 |
pBox = (Jp2BoxHeader*) outBuf.pData_;
|
|
Packit |
01d647 |
ul2Data((byte*)&pBox->type,kJp2BoxTypeJp2Header,bigEndian);
|
|
Packit |
01d647 |
ul2Data((byte*)&pBox->length,outlen,bigEndian);
|
|
Packit |
01d647 |
} // Jp2Image::encodeJp2Header
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
#ifdef __clang__
|
|
Packit |
01d647 |
#pragma clang diagnostic pop
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
void Jp2Image::doWriteMetadata(BasicIo& outIo)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
if (!io_->isopen()) throw Error(kerInputDataReadFailed);
|
|
Packit |
01d647 |
if (!outIo.isopen()) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
#ifdef EXIV2_DEBUG_MESSAGES
|
|
Packit |
01d647 |
std::cout << "Exiv2::Jp2Image::doWriteMetadata: Writing JPEG-2000 file " << io_->path() << std::endl;
|
|
Packit |
01d647 |
std::cout << "Exiv2::Jp2Image::doWriteMetadata: tmp file created " << outIo.path() << std::endl;
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// Ensure that this is the correct image type
|
|
Packit |
01d647 |
if (!isJp2Type(*io_, true))
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
if (io_->error() || io_->eof()) throw Error(kerInputDataReadFailed);
|
|
Packit |
01d647 |
throw Error(kerNoImageInInputData);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// Write JPEG2000 Signature.
|
|
Packit |
01d647 |
if (outIo.write(Jp2Signature, 12) != 12) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
Jp2BoxHeader box = {0,0};
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
byte boxDataSize[4];
|
|
Packit |
01d647 |
byte boxUUIDtype[4];
|
|
Packit |
01d647 |
DataBuf bheaderBuf(8); // Box header : 4 bytes (data size) + 4 bytes (box type).
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// FIXME: Andreas, why the loop do not stop when EOF is taken from _io. The loop go out by an exception
|
|
Packit |
01d647 |
// generated by a zero size data read.
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
while(io_->tell() < (long) io_->size())
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
#ifdef EXIV2_DEBUG_MESSAGES
|
|
Packit |
01d647 |
std::cout << "Exiv2::Jp2Image::doWriteMetadata: Position: " << io_->tell() << " / " << io_->size() << std::endl;
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// Read chunk header.
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
std::memset(bheaderBuf.pData_, 0x00, bheaderBuf.size_);
|
|
Packit |
01d647 |
long bufRead = io_->read(bheaderBuf.pData_, bheaderBuf.size_);
|
|
Packit |
01d647 |
if (io_->error()) throw Error(kerFailedToReadImageData);
|
|
Packit |
01d647 |
if (bufRead != bheaderBuf.size_) throw Error(kerInputDataReadFailed);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// Decode box header.
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
box.length = getLong(bheaderBuf.pData_, bigEndian);
|
|
Packit |
01d647 |
box.type = getLong(bheaderBuf.pData_ + 4, bigEndian);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
#ifdef EXIV2_DEBUG_MESSAGES
|
|
Packit |
01d647 |
std::cout << "Exiv2::Jp2Image::doWriteMetadata: box type: " << toAscii(box.type)
|
|
Packit |
01d647 |
<< " length: " << box.length << std::endl;
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
if (box.length == 0)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
#ifdef EXIV2_DEBUG_MESSAGES
|
|
Packit |
01d647 |
std::cout << "Exiv2::Jp2Image::doWriteMetadata: Null Box size has been found. "
|
|
Packit |
01d647 |
"This is the last box of file." << std::endl;
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
box.length = (uint32_t) (io_->size() - io_->tell() + 8);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
if (box.length == 1)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
// FIXME. Special case. the real box size is given in another place.
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// Read whole box : Box header + Box data (not fixed size - can be null).
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
DataBuf boxBuf(box.length); // Box header (8 bytes) + box data.
|
|
Packit |
01d647 |
memcpy(boxBuf.pData_, bheaderBuf.pData_, 8); // Copy header.
|
|
Packit |
01d647 |
bufRead = io_->read(boxBuf.pData_ + 8, box.length - 8); // Extract box data.
|
|
Packit |
01d647 |
if (io_->error())
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
#ifdef EXIV2_DEBUG_MESSAGES
|
|
Packit |
01d647 |
std::cout << "Exiv2::Jp2Image::doWriteMetadata: Error reading source file" << std::endl;
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
throw Error(kerFailedToReadImageData);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
if (bufRead != (long)(box.length - 8))
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
#ifdef EXIV2_DEBUG_MESSAGES
|
|
Packit |
01d647 |
std::cout << "Exiv2::Jp2Image::doWriteMetadata: Cannot read source file data" << std::endl;
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
throw Error(kerInputDataReadFailed);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
switch(box.type)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
case kJp2BoxTypeJp2Header:
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
DataBuf newBuf;
|
|
Packit |
01d647 |
encodeJp2Header(boxBuf,newBuf);
|
|
Packit |
01d647 |
#ifdef EXIV2_DEBUG_MESSAGES
|
|
Packit |
01d647 |
std::cout << "Exiv2::Jp2Image::doWriteMetadata: Write JP2Header box (length: " << box.length << ")" << std::endl;
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
if (outIo.write(newBuf.pData_, newBuf.size_) != newBuf.size_) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// Write all updated metadata here, just after JP2Header.
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
if (exifData_.count() > 0)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
// Update Exif data to a new UUID box
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
Blob blob;
|
|
Packit |
01d647 |
ExifParser::encode(blob, littleEndian, exifData_);
|
|
Packit |
01d647 |
if (blob.size())
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
DataBuf rawExif(static_cast<long>(blob.size()));
|
|
Packit |
01d647 |
memcpy(rawExif.pData_, &blob[0], blob.size());
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
DataBuf boxData(8 + 16 + rawExif.size_);
|
|
Packit |
01d647 |
ul2Data(boxDataSize, boxData.size_, Exiv2::bigEndian);
|
|
Packit |
01d647 |
ul2Data(boxUUIDtype, kJp2BoxTypeUuid, Exiv2::bigEndian);
|
|
Packit |
01d647 |
memcpy(boxData.pData_, boxDataSize, 4);
|
|
Packit |
01d647 |
memcpy(boxData.pData_ + 4, boxUUIDtype, 4);
|
|
Packit |
01d647 |
memcpy(boxData.pData_ + 8, kJp2UuidExif, 16);
|
|
Packit |
01d647 |
memcpy(boxData.pData_ + 8 + 16, rawExif.pData_, rawExif.size_);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
#ifdef EXIV2_DEBUG_MESSAGES
|
|
Packit |
01d647 |
std::cout << "Exiv2::Jp2Image::doWriteMetadata: Write box with Exif metadata (length: "
|
|
Packit |
01d647 |
<< boxData.size_ << std::endl;
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
if (outIo.write(boxData.pData_, boxData.size_) != boxData.size_) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
if (iptcData_.count() > 0)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
// Update Iptc data to a new UUID box
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
DataBuf rawIptc = IptcParser::encode(iptcData_);
|
|
Packit |
01d647 |
if (rawIptc.size_ > 0)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
DataBuf boxData(8 + 16 + rawIptc.size_);
|
|
Packit |
01d647 |
ul2Data(boxDataSize, boxData.size_, Exiv2::bigEndian);
|
|
Packit |
01d647 |
ul2Data(boxUUIDtype, kJp2BoxTypeUuid, Exiv2::bigEndian);
|
|
Packit |
01d647 |
memcpy(boxData.pData_, boxDataSize, 4);
|
|
Packit |
01d647 |
memcpy(boxData.pData_ + 4, boxUUIDtype, 4);
|
|
Packit |
01d647 |
memcpy(boxData.pData_ + 8, kJp2UuidIptc, 16);
|
|
Packit |
01d647 |
memcpy(boxData.pData_ + 8 + 16, rawIptc.pData_, rawIptc.size_);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
#ifdef EXIV2_DEBUG_MESSAGES
|
|
Packit |
01d647 |
std::cout << "Exiv2::Jp2Image::doWriteMetadata: Write box with Iptc metadata (length: "
|
|
Packit |
01d647 |
<< boxData.size_ << std::endl;
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
if (outIo.write(boxData.pData_, boxData.size_) != boxData.size_) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
if (writeXmpFromPacket() == false)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
if (XmpParser::encode(xmpPacket_, xmpData_) > 1)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
#ifndef SUPPRESS_WARNINGS
|
|
Packit |
01d647 |
EXV_ERROR << "Failed to encode XMP metadata." << std::endl;
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
if (xmpPacket_.size() > 0)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
// Update Xmp data to a new UUID box
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
DataBuf xmp(reinterpret_cast<const byte*>(xmpPacket_.data()), static_cast<long>(xmpPacket_.size()));
|
|
Packit |
01d647 |
DataBuf boxData(8 + 16 + xmp.size_);
|
|
Packit |
01d647 |
ul2Data(boxDataSize, boxData.size_, Exiv2::bigEndian);
|
|
Packit |
01d647 |
ul2Data(boxUUIDtype, kJp2BoxTypeUuid, Exiv2::bigEndian);
|
|
Packit |
01d647 |
memcpy(boxData.pData_, boxDataSize, 4);
|
|
Packit |
01d647 |
memcpy(boxData.pData_ + 4, boxUUIDtype, 4);
|
|
Packit |
01d647 |
memcpy(boxData.pData_ + 8, kJp2UuidXmp, 16);
|
|
Packit |
01d647 |
memcpy(boxData.pData_ + 8 + 16, xmp.pData_, xmp.size_);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
#ifdef EXIV2_DEBUG_MESSAGES
|
|
Packit |
01d647 |
std::cout << "Exiv2::Jp2Image::doWriteMetadata: Write box with XMP metadata (length: "
|
|
Packit |
01d647 |
<< boxData.size_ << ")" << std::endl;
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
if (outIo.write(boxData.pData_, boxData.size_) != boxData.size_) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
break;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
case kJp2BoxTypeUuid:
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
if(memcmp(boxBuf.pData_ + 8, kJp2UuidExif, 16) == 0)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
#ifdef EXIV2_DEBUG_MESSAGES
|
|
Packit |
01d647 |
std::cout << "Exiv2::Jp2Image::doWriteMetadata: strip Exif Uuid box" << std::endl;
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
else if(memcmp(boxBuf.pData_ + 8, kJp2UuidIptc, 16) == 0)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
#ifdef EXIV2_DEBUG_MESSAGES
|
|
Packit |
01d647 |
std::cout << "Exiv2::Jp2Image::doWriteMetadata: strip Iptc Uuid box" << std::endl;
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
else if(memcmp(boxBuf.pData_ + 8, kJp2UuidXmp, 16) == 0)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
#ifdef EXIV2_DEBUG_MESSAGES
|
|
Packit |
01d647 |
std::cout << "Exiv2::Jp2Image::doWriteMetadata: strip Xmp Uuid box" << std::endl;
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
else
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
#ifdef EXIV2_DEBUG_MESSAGES
|
|
Packit |
01d647 |
std::cout << "Exiv2::Jp2Image::doWriteMetadata: write Uuid box (length: " << box.length << ")" << std::endl;
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
if (outIo.write(boxBuf.pData_, boxBuf.size_) != boxBuf.size_) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
break;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
default:
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
#ifdef EXIV2_DEBUG_MESSAGES
|
|
Packit |
01d647 |
std::cout << "Exiv2::Jp2Image::doWriteMetadata: write box (length: " << box.length << ")" << std::endl;
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
if (outIo.write(boxBuf.pData_, boxBuf.size_) != boxBuf.size_) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
break;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
#ifdef EXIV2_DEBUG_MESSAGES
|
|
Packit |
01d647 |
std::cout << "Exiv2::Jp2Image::doWriteMetadata: EOF" << std::endl;
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
} // Jp2Image::doWriteMetadata
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// *************************************************************************
|
|
Packit |
01d647 |
// free functions
|
|
Packit |
01d647 |
Image::AutoPtr newJp2Instance(BasicIo::AutoPtr io, bool create)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
Image::AutoPtr image(new Jp2Image(io, create));
|
|
Packit |
01d647 |
if (!image->good())
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
image.reset();
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
return image;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
bool isJp2Type(BasicIo& iIo, bool advance)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
const int32_t len = 12;
|
|
Packit |
01d647 |
byte buf[len];
|
|
Packit |
01d647 |
iIo.read(buf, len);
|
|
Packit |
01d647 |
if (iIo.error() || iIo.eof())
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
return false;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
bool matched = (memcmp(buf, Jp2Signature, len) == 0);
|
|
Packit |
01d647 |
if (!advance || !matched)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
iIo.seek(-len, BasicIo::cur);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
return matched;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
} // namespace Exiv2
|