Added support for IPTC data found in an Exif Photoshop IRB tag of a TIFF/RAW image

v0.27.3
Andreas Huggel 19 years ago
parent 449b65c39e
commit 823a84d3d2

@ -48,6 +48,7 @@ EXIV2_RCSID("@(#) $Id$");
// ***************************************************************************** // *****************************************************************************
// class member definitions // class member definitions
namespace Exiv2 { namespace Exiv2 {
const byte JpegBase::sos_ = 0xda; const byte JpegBase::sos_ = 0xda;
@ -56,11 +57,8 @@ namespace Exiv2 {
const byte JpegBase::app1_ = 0xe1; const byte JpegBase::app1_ = 0xe1;
const byte JpegBase::app13_ = 0xed; const byte JpegBase::app13_ = 0xed;
const byte JpegBase::com_ = 0xfe; const byte JpegBase::com_ = 0xfe;
const uint16_t JpegBase::iptc_ = 0x0404;
const char JpegBase::exifId_[] = "Exif\0\0"; const char JpegBase::exifId_[] = "Exif\0\0";
const char JpegBase::jfifId_[] = "JFIF\0"; const char JpegBase::jfifId_[] = "JFIF\0";
const char JpegBase::ps3Id_[] = "Photoshop 3.0\0";
const char JpegBase::bimId_[] = "8BIM";
JpegBase::JpegBase(BasicIo::AutoPtr io, bool create, JpegBase::JpegBase(BasicIo::AutoPtr io, bool create,
const byte initData[], long dataSize) const byte initData[], long dataSize)
@ -187,7 +185,7 @@ namespace Exiv2 {
if (exifData_.load(rawExif.pData_, sizeExifData)) throw Error(36, "Exif"); if (exifData_.load(rawExif.pData_, sizeExifData)) throw Error(36, "Exif");
--search; --search;
} }
else if (marker == app13_ && memcmp(buf.pData_ + 2, ps3Id_, 14) == 0) { else if (marker == app13_ && memcmp(buf.pData_ + 2, Photoshop::ps3Id, 14) == 0) {
if (size < 16) throw Error(15); if (size < 16) throw Error(15);
// Read the rest of the APP13 segment // Read the rest of the APP13 segment
// needed if bufMinSize!=16: io_->seek(16-bufRead, BasicIo::cur); // needed if bufMinSize!=16: io_->seek(16-bufRead, BasicIo::cur);
@ -233,7 +231,6 @@ namespace Exiv2 {
} }
} // JpegBase::readMetadata } // JpegBase::readMetadata
// Operates on raw data (rather than file streams) to simplify reuse // Operates on raw data (rather than file streams) to simplify reuse
int JpegBase::locateIptcData(const byte *pPsData, int JpegBase::locateIptcData(const byte *pPsData,
long sizePsData, long sizePsData,
@ -241,41 +238,8 @@ namespace Exiv2 {
uint16_t *const sizeHdr, uint16_t *const sizeHdr,
uint16_t *const sizeIptc) const uint16_t *const sizeIptc) const
{ {
assert(record); return locate8BimData(pPsData, sizePsData, Photoshop::iptc, record, sizeHdr, sizeIptc);
assert(sizeHdr);
assert(sizeIptc);
// Used for error checking
long position = 0;
// Data should follow Photoshop format, if not exit
while (position <= (sizePsData - 14) &&
memcmp(pPsData + position, bimId_, 4)==0) {
const byte *hrd = pPsData + position;
position += 4;
uint16_t type = getUShort(pPsData+ position, bigEndian);
position += 2;
// Pascal string is padded to have an even size (including size byte)
byte psSize = pPsData[position] + 1;
psSize += (psSize & 1);
position += psSize;
if (position >= sizePsData) return -2;
// Data is also padded to be even
long dataSize = getULong(pPsData + position, bigEndian);
position += 4;
if (dataSize > sizePsData - position) return -2;
if (type == iptc_) {
*sizeIptc = static_cast<uint16_t>(dataSize);
*sizeHdr = psSize + 10;
*record = hrd;
return 0;
}
position += dataSize + (dataSize & 1);
} }
return 3;
} // JpegBase::locateIptcData
void JpegBase::writeMetadata() void JpegBase::writeMetadata()
{ {
@ -341,7 +305,7 @@ namespace Exiv2 {
++search; ++search;
if (io_->seek(size-bufRead, BasicIo::cur)) throw Error(22); if (io_->seek(size-bufRead, BasicIo::cur)) throw Error(22);
} }
else if (marker == app13_ && memcmp(buf.pData_ + 2, ps3Id_, 14) == 0) { else if (marker == app13_ && memcmp(buf.pData_ + 2, Photoshop::ps3Id, 14) == 0) {
if (size < 16) throw Error(22); if (size < 16) throw Error(22);
skipApp13Ps3 = count; skipApp13Ps3 = count;
++search; ++search;
@ -439,7 +403,7 @@ namespace Exiv2 {
us2Data(tmpBuf + 2, us2Data(tmpBuf + 2,
static_cast<uint16_t>(psData.size_-sizeOldData+sizeNewData+16), static_cast<uint16_t>(psData.size_-sizeOldData+sizeNewData+16),
bigEndian); bigEndian);
memcpy(tmpBuf + 4, ps3Id_, 14); memcpy(tmpBuf + 4, Photoshop::ps3Id, 14);
if (outIo.write(tmpBuf, 18) != 18) throw Error(21); if (outIo.write(tmpBuf, 18) != 18) throw Error(21);
if (outIo.error()) throw Error(21); if (outIo.error()) throw Error(21);
@ -450,8 +414,8 @@ namespace Exiv2 {
// write new iptc record if we have it // write new iptc record if we have it
if (iptcData_.count() > 0) { if (iptcData_.count() > 0) {
memcpy(tmpBuf, bimId_, 4); memcpy(tmpBuf, Photoshop::bimId, 4);
us2Data(tmpBuf+4, iptc_, bigEndian); us2Data(tmpBuf+4, Photoshop::iptc, bigEndian);
tmpBuf[6] = 0; tmpBuf[6] = 0;
tmpBuf[7] = 0; tmpBuf[7] = 0;
ul2Data(tmpBuf + 8, rawIptc.size_, bigEndian); ul2Data(tmpBuf + 8, rawIptc.size_, bigEndian);
@ -623,4 +587,50 @@ namespace Exiv2 {
return result; return result;
} }
// Todo: Generalised from JpegBase::locateIptcData without really understanding
// the format (in particular the header). So it remains to be confirmed
// if this also makes sense for psTag != Photoshop::iptc
int locate8BimData(const byte *pPsData,
long sizePsData,
uint16_t psTag,
const byte **record,
uint16_t *const sizeHdr,
uint16_t *const sizeData)
{
assert(record);
assert(sizeHdr);
assert(sizeData);
// Used for error checking
long position = 0;
// Data should follow Photoshop format, if not exit
while ( position <= sizePsData - 14
&& memcmp(pPsData + position, Photoshop::bimId, 4) == 0) {
const byte *hrd = pPsData + position;
position += 4;
uint16_t type = getUShort(pPsData + position, bigEndian);
position += 2;
// Pascal string is padded to have an even size (including size byte)
byte psSize = pPsData[position] + 1;
psSize += (psSize & 1);
position += psSize;
if (position >= sizePsData) return -2;
// Data is also padded to be even
long dataSize = getULong(pPsData + position, bigEndian);
position += 4;
if (dataSize > sizePsData - position) return -2;
if (type == psTag) {
*sizeData = static_cast<uint16_t>(dataSize);
*sizeHdr = psSize + 10;
*record = hrd;
return 0;
}
position += dataSize + (dataSize & 1);
}
return 3;
} // locate8BimData
} // namespace Exiv2 } // namespace Exiv2

@ -56,6 +56,13 @@ namespace Exiv2 {
const int exv = 2; //!< Exv image type (see class ExvImage) const int exv = 2; //!< Exv image type (see class ExvImage)
} }
//! %Photoshop constants
namespace Photoshop {
const char ps3Id[] = "Photoshop 3.0\0"; //!< %Photoshop marker
const char bimId[] = "8BIM"; //!< %Photoshop marker
const uint16_t iptc = 0x0404; //!< %Photoshop IPTC marker
}
/*! /*!
@brief Abstract helper base class to access JPEG images. @brief Abstract helper base class to access JPEG images.
*/ */
@ -156,9 +163,6 @@ namespace Exiv2 {
static const byte com_; //!< JPEG Comment marker static const byte com_; //!< JPEG Comment marker
static const char exifId_[]; //!< Exif identifier static const char exifId_[]; //!< Exif identifier
static const char jfifId_[]; //!< JFIF identifier static const char jfifId_[]; //!< JFIF identifier
static const char ps3Id_[]; //!< Photoshop marker
static const char bimId_[]; //!< Photoshop marker
static const uint16_t iptc_; //!< Photoshop Iptc marker
private: private:
// DATA // DATA
@ -353,6 +357,29 @@ namespace Exiv2 {
Image::AutoPtr newExvInstance(BasicIo::AutoPtr io, bool create); Image::AutoPtr newExvInstance(BasicIo::AutoPtr io, bool create);
//! Check if the file iIo is an EXV file //! Check if the file iIo is an EXV file
bool isExvType(BasicIo& iIo, bool advance); bool isExvType(BasicIo& iIo, bool advance);
/*!
@brief Locates the data for a Photoshop tag in a Photoshop formated memory
buffer. Operates on raw data to simplify reuse.
@param pPsData Pointer to buffer containing entire payload of
Photoshop formated APP13 Jpeg segment.
@param sizePsData Size in bytes of pPsData.
@param psTag Tag number of the block to look for.
@param record Output value that is set to the start of the
data block within pPsData (may not be null).
@param sizeHdr Output value that is set to the size of the header
within the data block pointed to by record (may not be null).
@param sizeData Output value that is set to the size of the actual
data within the data block pointed to by record (may not be null).
@return 0 if successful;<BR>
3 if no data for psTag was found in pPsData;<BR>
-2 if the pPsData buffer does not contain valid data.
*/
int locate8BimData(const byte *pPsData,
long sizePsData,
uint16_t psTag,
const byte **record,
uint16_t *const sizeHdr,
uint16_t *const sizeData);
} // namespace Exiv2 } // namespace Exiv2

@ -55,7 +55,7 @@ EXIV2_RCSID("@(#) $Id$");
namespace Exiv2 { namespace Exiv2 {
MrwImage::MrwImage(BasicIo::AutoPtr io, bool create) MrwImage::MrwImage(BasicIo::AutoPtr io, bool create)
: Image(mdExif), io_(io) : Image(mdExif | mdIptc), io_(io)
{ {
if (create) { if (create) {
IoCloser closer(*io_); IoCloser closer(*io_);
@ -92,12 +92,12 @@ namespace Exiv2 {
void MrwImage::clearIptcData() void MrwImage::clearIptcData()
{ {
// not supported iptcData_.clear();
} }
void MrwImage::setIptcData(const IptcData& /*iptcData*/) void MrwImage::setIptcData(const IptcData& iptcData)
{ {
// not supported iptcData_ = iptcData;
} }
void MrwImage::clearComment() void MrwImage::clearComment()

@ -52,8 +52,8 @@ namespace Exiv2 {
} }
/*! /*!
@brief Class to access raw MRW images. Only Exif metadata is currently @brief Class to access raw MRW images. Exif metadata is supported
supported. directly, IPTC is read from the Exif data, if present.
*/ */
class MrwImage : public Image { class MrwImage : public Image {
friend bool isMrwType(BasicIo& iIo, bool advance); friend bool isMrwType(BasicIo& iIo, bool advance);
@ -99,15 +99,7 @@ namespace Exiv2 {
void writeMetadata(); void writeMetadata();
void setExifData(const ExifData& exifData); void setExifData(const ExifData& exifData);
void clearExifData(); void clearExifData();
/*!
@brief Not supported. MRW format does not contain IPTC metadata.
Calling this function will do nothing.
*/
void setIptcData(const IptcData& iptcData); void setIptcData(const IptcData& iptcData);
/*!
@brief Not supported. MRW format does not contain IPTC metadata.
Calling this function will do nothing.
*/
void clearIptcData(); void clearIptcData();
/*! /*!
@brief Not supported. MRW format does not contain a comment. @brief Not supported. MRW format does not contain a comment.

@ -54,7 +54,7 @@ EXIV2_RCSID("@(#) $Id$");
namespace Exiv2 { namespace Exiv2 {
TiffImage::TiffImage(BasicIo::AutoPtr io, bool create) TiffImage::TiffImage(BasicIo::AutoPtr io, bool create)
: Image(mdExif | mdComment), io_(io) : Image(mdExif | mdIptc | mdComment), io_(io)
{ {
if (create) { if (create) {
IoCloser closer(*io_); IoCloser closer(*io_);
@ -93,12 +93,12 @@ namespace Exiv2 {
void TiffImage::clearIptcData() void TiffImage::clearIptcData()
{ {
// not supported iptcData_.clear();
} }
void TiffImage::setIptcData(const IptcData& /*iptcData*/) void TiffImage::setIptcData(const IptcData& iptcData)
{ {
// not supported iptcData_ = iptcData;
} }
void TiffImage::clearComment() void TiffImage::clearComment()

@ -51,8 +51,8 @@ namespace Exiv2 {
} }
/*! /*!
@brief Class to access raw TIFF images. Only Exif metadata and a comment @brief Class to access raw TIFF images. Exif metadata and a comment
are supported. TIFF format does not contain IPTC metadata. are supported directly, IPTC is read from the Exif data, if present.
*/ */
class TiffImage : public Image { class TiffImage : public Image {
friend bool isTiffType(BasicIo& iIo, bool advance); friend bool isTiffType(BasicIo& iIo, bool advance);
@ -98,15 +98,7 @@ namespace Exiv2 {
void writeMetadata(); void writeMetadata();
void setExifData(const ExifData& exifData); void setExifData(const ExifData& exifData);
void clearExifData(); void clearExifData();
/*!
@brief Not supported. TIFF format does not contain IPTC metadata.
Calling this function will do nothing.
*/
void setIptcData(const IptcData& iptcData); void setIptcData(const IptcData& iptcData);
/*!
@brief Not supported. TIFF format does not contain IPTC metadata.
Calling this function will do nothing.
*/
void clearIptcData(); void clearIptcData();
void setComment(const std::string& comment); void setComment(const std::string& comment);
void clearComment(); void clearComment();

@ -40,8 +40,10 @@ EXIV2_RCSID("@(#) $Id$");
#include "tiffcomposite.hpp" #include "tiffcomposite.hpp"
#include "makernote2.hpp" #include "makernote2.hpp"
#include "exif.hpp" #include "exif.hpp"
#include "iptc.hpp"
#include "value.hpp" #include "value.hpp"
#include "image.hpp" #include "image.hpp"
#include "jpgimage.hpp"
// + standard includes // + standard includes
#include <string> #include <string>
@ -59,7 +61,8 @@ namespace Exiv2 {
{ "OLYMPUS", 0x0100, Group::olympmn, &TiffMetadataDecoder::decodeOlympThumb }, { "OLYMPUS", 0x0100, Group::olympmn, &TiffMetadataDecoder::decodeOlympThumb },
{ "*", 0x014a, Group::ifd0, 0 }, // Todo: Controversial, causes problems with Exiftool { "*", 0x014a, Group::ifd0, 0 }, // Todo: Controversial, causes problems with Exiftool
{ "*", Tag::all, Group::sub0_0, &TiffMetadataDecoder::decodeSubIfd }, { "*", Tag::all, Group::sub0_0, &TiffMetadataDecoder::decodeSubIfd },
{ "*", Tag::all, Group::sub0_1, &TiffMetadataDecoder::decodeSubIfd } { "*", Tag::all, Group::sub0_1, &TiffMetadataDecoder::decodeSubIfd },
{ "*", 0x8649, Group::ifd0, &TiffMetadataDecoder::decodeIrbIptc }
}; };
bool TiffDecoderInfo::operator==(const TiffDecoderInfo::Key& key) const bool TiffDecoderInfo::operator==(const TiffDecoderInfo::Key& key) const
@ -194,6 +197,37 @@ namespace Exiv2 {
} }
} }
void TiffMetadataDecoder::decodeIrbIptc(const TiffEntryBase* object)
{
assert(object != 0);
assert(pImage_ != 0);
if (!object->pData()) return;
byte const* record = 0;
uint16_t sizeHdr = 0;
uint16_t sizeData = 0;
if (0 != locate8BimData(object->pData(),
object->size(),
Photoshop::iptc,
&record,
&sizeHdr,
&sizeData)) {
return;
}
if (0 != pImage_->iptcData().load(record + sizeHdr, sizeData)) {
#ifndef SUPPRESS_WARNINGS
std::cerr << "Warning: Failed to decode IPTC block found in "
<< "Directory " << object->groupName()
<< ", entry 0x" << std::setw(4)
<< std::setfill('0') << std::hex << object->tag()
<< "\n";
#endif
// Todo: ExifKey should have an appropriate c'tor, it should not be
// necessary to use groupName here
ExifKey key(object->tag(), object->groupName());
setExifTag(key, object->pValue());
}
} // TiffMetadataDecoder::decodeIrbIptc
void TiffMetadataDecoder::decodeSubIfd(const TiffEntryBase* object) void TiffMetadataDecoder::decodeSubIfd(const TiffEntryBase* object)
{ {
assert(object); assert(object);
@ -396,7 +430,7 @@ namespace Exiv2 {
TiffRwState::AutoPtr state) TiffRwState::AutoPtr state)
: pData_(pData), : pData_(pData),
size_(size), size_(size),
pLast_(pData + size - 1), pLast_(pData + size),
pRoot_(pRoot), pRoot_(pRoot),
pState_(state.release()), pState_(state.release()),
pOrigState_(pState_) pOrigState_(pState_)
@ -724,8 +758,8 @@ namespace Exiv2 {
if (object->pData() + object->size() > pLast_) { if (object->pData() + object->size() > pLast_) {
#ifndef SUPPRESS_WARNINGS #ifndef SUPPRESS_WARNINGS
std::cerr << "Warning: Upper boundary of data for " std::cerr << "Warning: Upper boundary of data for "
<< "directory " << object->groupName() << ", " << "directory " << object->groupName()
<< " entry 0x" << std::setw(4) << ", entry 0x" << std::setw(4)
<< std::setfill('0') << std::hex << object->tag() << std::setfill('0') << std::hex << object->tag()
<< " is out of bounds:\n" << " is out of bounds:\n"
<< "Offset = 0x" << std::setw(8) << "Offset = 0x" << std::setw(8)

@ -282,6 +282,8 @@ namespace Exiv2 {
void decodeOlympThumb(const TiffEntryBase* object); void decodeOlympThumb(const TiffEntryBase* object);
//! Decode SubIFD contents to Image group if it contains primary image data //! Decode SubIFD contents to Image group if it contains primary image data
void decodeSubIfd(const TiffEntryBase* object); void decodeSubIfd(const TiffEntryBase* object);
//! Decode IPTC data from a Photoshop IRB tag
void decodeIrbIptc(const TiffEntryBase* object);
//@} //@}
private: private:

Loading…
Cancel
Save