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
namespace Exiv2 {
const byte JpegBase::sos_ = 0xda;
@ -56,11 +57,8 @@ namespace Exiv2 {
const byte JpegBase::app1_ = 0xe1;
const byte JpegBase::app13_ = 0xed;
const byte JpegBase::com_ = 0xfe;
const uint16_t JpegBase::iptc_ = 0x0404;
const char JpegBase::exifId_[] = "Exif\0\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,
const byte initData[], long dataSize)
@ -187,7 +185,7 @@ namespace Exiv2 {
if (exifData_.load(rawExif.pData_, sizeExifData)) throw Error(36, "Exif");
--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);
// Read the rest of the APP13 segment
// needed if bufMinSize!=16: io_->seek(16-bufRead, BasicIo::cur);
@ -233,7 +231,6 @@ namespace Exiv2 {
}
} // JpegBase::readMetadata
// Operates on raw data (rather than file streams) to simplify reuse
int JpegBase::locateIptcData(const byte *pPsData,
long sizePsData,
@ -241,41 +238,8 @@ namespace Exiv2 {
uint16_t *const sizeHdr,
uint16_t *const sizeIptc) const
{
assert(record);
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 locate8BimData(pPsData, sizePsData, Photoshop::iptc, record, sizeHdr, sizeIptc);
}
return 3;
} // JpegBase::locateIptcData
void JpegBase::writeMetadata()
{
@ -341,7 +305,7 @@ namespace Exiv2 {
++search;
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);
skipApp13Ps3 = count;
++search;
@ -439,7 +403,7 @@ namespace Exiv2 {
us2Data(tmpBuf + 2,
static_cast<uint16_t>(psData.size_-sizeOldData+sizeNewData+16),
bigEndian);
memcpy(tmpBuf + 4, ps3Id_, 14);
memcpy(tmpBuf + 4, Photoshop::ps3Id, 14);
if (outIo.write(tmpBuf, 18) != 18) throw Error(21);
if (outIo.error()) throw Error(21);
@ -450,8 +414,8 @@ namespace Exiv2 {
// write new iptc record if we have it
if (iptcData_.count() > 0) {
memcpy(tmpBuf, bimId_, 4);
us2Data(tmpBuf+4, iptc_, bigEndian);
memcpy(tmpBuf, Photoshop::bimId, 4);
us2Data(tmpBuf+4, Photoshop::iptc, bigEndian);
tmpBuf[6] = 0;
tmpBuf[7] = 0;
ul2Data(tmpBuf + 8, rawIptc.size_, bigEndian);
@ -623,4 +587,50 @@ namespace Exiv2 {
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

@ -56,6 +56,13 @@ namespace Exiv2 {
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.
*/
@ -156,9 +163,6 @@ namespace Exiv2 {
static const byte com_; //!< JPEG Comment marker
static const char exifId_[]; //!< Exif 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:
// DATA
@ -353,6 +357,29 @@ namespace Exiv2 {
Image::AutoPtr newExvInstance(BasicIo::AutoPtr io, bool create);
//! Check if the file iIo is an EXV file
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

@ -55,7 +55,7 @@ EXIV2_RCSID("@(#) $Id$");
namespace Exiv2 {
MrwImage::MrwImage(BasicIo::AutoPtr io, bool create)
: Image(mdExif), io_(io)
: Image(mdExif | mdIptc), io_(io)
{
if (create) {
IoCloser closer(*io_);
@ -92,12 +92,12 @@ namespace Exiv2 {
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()

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

@ -54,7 +54,7 @@ EXIV2_RCSID("@(#) $Id$");
namespace Exiv2 {
TiffImage::TiffImage(BasicIo::AutoPtr io, bool create)
: Image(mdExif | mdComment), io_(io)
: Image(mdExif | mdIptc | mdComment), io_(io)
{
if (create) {
IoCloser closer(*io_);
@ -93,12 +93,12 @@ namespace Exiv2 {
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()

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

@ -40,8 +40,10 @@ EXIV2_RCSID("@(#) $Id$");
#include "tiffcomposite.hpp"
#include "makernote2.hpp"
#include "exif.hpp"
#include "iptc.hpp"
#include "value.hpp"
#include "image.hpp"
#include "jpgimage.hpp"
// + standard includes
#include <string>
@ -59,7 +61,8 @@ namespace Exiv2 {
{ "OLYMPUS", 0x0100, Group::olympmn, &TiffMetadataDecoder::decodeOlympThumb },
{ "*", 0x014a, Group::ifd0, 0 }, // Todo: Controversial, causes problems with Exiftool
{ "*", 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
@ -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)
{
assert(object);
@ -396,7 +430,7 @@ namespace Exiv2 {
TiffRwState::AutoPtr state)
: pData_(pData),
size_(size),
pLast_(pData + size - 1),
pLast_(pData + size),
pRoot_(pRoot),
pState_(state.release()),
pOrigState_(pState_)
@ -724,8 +758,8 @@ namespace Exiv2 {
if (object->pData() + object->size() > pLast_) {
#ifndef SUPPRESS_WARNINGS
std::cerr << "Warning: Upper boundary of data for "
<< "directory " << object->groupName() << ", "
<< " entry 0x" << std::setw(4)
<< "directory " << object->groupName()
<< ", entry 0x" << std::setw(4)
<< std::setfill('0') << std::hex << object->tag()
<< " is out of bounds:\n"
<< "Offset = 0x" << std::setw(8)

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

Loading…
Cancel
Save