You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

842 lines
26 KiB
C++

22 years ago
// ***************************************************************** -*- C++ -*-
/*
22 years ago
* Copyright (C) 2004 Andreas Huggel <ahuggel@gmx.net>
*
* This program is part of the Exiv2 distribution.
*
22 years ago
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 years ago
*/
/*
File: exif.cpp
Version: $Name: $ $Revision: 1.20 $
22 years ago
Author(s): Andreas Huggel (ahu) <ahuggel@gmx.net>
History: 26-Jan-04, ahu: created
11-Feb-04, ahu: isolated as a component
22 years ago
*/
// *****************************************************************************
#include "rcsid.hpp"
EXIV2_RCSID("@(#) $Name: $ $Revision: 1.20 $ $RCSfile: exif.cpp,v $")
22 years ago
// *****************************************************************************
// included header files
#include "exif.hpp"
#include "types.hpp"
#include "error.hpp"
#include "value.hpp"
#include "ifd.hpp"
#include "tags.hpp"
#include "image.hpp"
22 years ago
// + standard includes
#include <iostream>
#include <iomanip>
#include <sstream>
#include <fstream>
#include <utility>
#include <algorithm>
#include <map>
22 years ago
#include <cstring>
// *****************************************************************************
// class member definitions
namespace Exif {
Metadatum::Metadatum(const Entry& e, ByteOrder byteOrder)
: tag_(e.tag()), ifdId_(e.ifdId()), ifdIdx_(e.ifdIdx()), value_(0)
22 years ago
{
value_ = Value::create(TypeId(e.type()));
value_->read(e.data(), e.size(), byteOrder);
key_ = ExifTags::makeKey(tag_, ifdId_);
22 years ago
}
22 years ago
Metadatum::Metadatum(const std::string& key, Value* value)
: ifdIdx_(-1), value_(0), key_(key)
{
if (value) value_ = value->clone();
std::pair<uint16, IfdId> p = ExifTags::decomposeKey(key);
if (p.first == 0xffff) throw Error("Invalid key");
tag_ = p.first;
if (p.second == ifdIdNotSet) throw Error("Invalid key");
ifdId_ = p.second;
22 years ago
}
22 years ago
Metadatum::~Metadatum()
{
delete value_;
22 years ago
}
Metadatum::Metadatum(const Metadatum& rhs)
: tag_(rhs.tag_), ifdId_(rhs.ifdId_),
ifdIdx_(rhs.ifdIdx_), value_(0), key_(rhs.key_)
22 years ago
{
if (rhs.value_ != 0) value_ = rhs.value_->clone(); // deep copy
22 years ago
}
Metadatum& Metadatum::operator=(const Metadatum& rhs)
{
if (this == &rhs) return *this;
22 years ago
tag_ = rhs.tag_;
ifdId_ = rhs.ifdId_;
ifdIdx_ = rhs.ifdIdx_;
delete value_;
value_ = 0;
if (rhs.value_ != 0) value_ = rhs.value_->clone(); // deep copy
22 years ago
key_ = rhs.key_;
return *this;
} // Metadatum::operator=
void Metadatum::setValue(const Value* value)
{
delete value_;
22 years ago
value_ = value->clone();
}
void Metadatum::setValue(const std::string& buf)
{
if (value_ == 0) value_ = Value::create(asciiString);
value_->read(buf);
}
Thumbnail::Thumbnail()
: type_(none), size_(0), image_(0), ifd_(ifd1, 0, false)
{
}
Thumbnail::~Thumbnail()
{
delete[] image_;
}
Thumbnail::Thumbnail(const Thumbnail& rhs)
: type_(rhs.type_), size_(rhs.size_), image_(0), ifd_(ifd1, 0, false)
{
if (rhs.image_ > 0 && rhs.size_ > 0) {
image_ = new char[rhs.size_];
memcpy(image_, rhs.image_, rhs.size_);
}
if (image_ && type_ == tiff) {
tiffHeader_.read(image_);
ifd_.read(image_ + tiffHeader_.offset(),
tiffHeader_.byteOrder(), tiffHeader_.offset());
}
}
Thumbnail& Thumbnail::operator=(const Thumbnail& rhs)
{
type_ = rhs.type_;
size_ = rhs.size_;
delete[] image_;
image_ = 0;
if (rhs.image_ > 0 && rhs.size_ > 0) {
image_ = new char[rhs.size_];
memcpy(image_, rhs.image_, rhs.size_);
}
if (image_ && type_ == tiff) {
tiffHeader_.read(image_);
ifd_.read(image_ + tiffHeader_.offset(),
tiffHeader_.byteOrder(), tiffHeader_.offset());
}
return *this;
}
int Thumbnail::read(const char* buf,
const ExifData& exifData,
ByteOrder byteOrder)
{
int rc = 0;
std::string key = "Thumbnail.ImageStructure.Compression";
ExifData::const_iterator pos = exifData.findKey(key);
if (pos == exifData.end()) return -1; // no thumbnail
long compression = pos->toLong();
if (compression == 6) {
rc = readJpegImage(buf, exifData);
}
else {
rc = readTiffImage(buf, exifData, byteOrder);
}
return rc;
} // Thumbnail::read
int Thumbnail::readJpegImage(const char* buf, const ExifData& exifData)
{
std::string key = "Thumbnail.RecordingOffset.JPEGInterchangeFormat";
ExifData::const_iterator pos = exifData.findKey(key);
if (pos == exifData.end()) return 1;
long offset = pos->toLong();
key = "Thumbnail.RecordingOffset.JPEGInterchangeFormatLength";
pos = exifData.findKey(key);
if (pos == exifData.end()) return 1;
long size = pos->toLong();
image_ = new char[size];
memcpy(image_, buf + offset, size);
size_ = size;
type_ = jpeg;
22 years ago
return 0;
} // Thumbnail::readJpegImage
22 years ago
int Thumbnail::readTiffImage(const char* buf,
const ExifData& exifData,
ByteOrder byteOrder)
{
char* data = new char[64*1024]; // temporary buffer Todo: handle larger
memset(data, 0x0, 64*1024); // images (which violate the Exif Std)
long len = 0; // number of bytes in the buffer
// Copy the TIFF header
TiffHeader tiffHeader(byteOrder);
len += tiffHeader.copy(data);
// Create IFD (without Exif and GPS tags) from metadata
Ifd ifd1(ifd1);
addToIfd(ifd1, exifData.begin(), exifData.end(), tiffHeader.byteOrder());
Ifd::iterator i = ifd1.findTag(0x8769);
if (i != ifd1.end()) ifd1.erase(i);
i = ifd1.findTag(0x8825);
if (i != ifd1.end()) ifd1.erase(i);
// Do not copy the IFD yet, remember the location and leave a gap
long ifdOffset = len;
len += ifd1.size() + ifd1.dataSize();
// Copy thumbnail image data, remember the offsets used
std::string key = "Thumbnail.RecordingOffset.StripOffsets";
ExifData::const_iterator offsets = exifData.findKey(key);
if (offsets == exifData.end()) return 2;
key = "Thumbnail.RecordingOffset.StripByteCounts";
ExifData::const_iterator sizes = exifData.findKey(key);
if (sizes == exifData.end()) return 2;
std::ostringstream os; // for the new strip offsets
for (long k = 0; k < offsets->count(); ++k) {
long offset = offsets->toLong(k);
long size = sizes->toLong(k);
memcpy(data + len, buf + offset, size);
os << len << " ";
len += size;
}
// Update the IFD with the actual strip offsets (replace existing entry)
Metadatum newOffsets(*offsets);
newOffsets.setValue(os.str());
addToIfd(ifd1, newOffsets, tiffHeader.byteOrder());
// Finally, sort and copy the IFD
ifd1.sortByTag();
ifd1.copy(data + ifdOffset, tiffHeader.byteOrder(), ifdOffset);
image_ = new char[len];
memcpy(image_, data, len);
size_ = len;
tiffHeader_.read(image_);
ifd_.read(image_ + tiffHeader_.offset(),
tiffHeader_.byteOrder(), tiffHeader_.offset());
type_ = tiff;
delete[] data;
return 0;
} // Thumbnail::readTiffImage
22 years ago
int Thumbnail::write(const std::string& path) const
{
std::string p;
switch (type_) {
case jpeg:
p = path + ".jpg";
break;
case tiff:
p = path + ".tif";
break;
case none:
return 1;
break;
}
std::ofstream file(p.c_str(), std::ios::binary);
22 years ago
if (!file) return 1;
file.write(image_, size_);
22 years ago
if (!file.good()) return 2;
return 0;
} // Thumbnail::write
void Thumbnail::update(ExifData& exifData) const
{
// Todo: properly synchronize the Exif data with the actual thumbnail,
// i.e., synch all relevant metadata
switch (type_) {
case jpeg:
updateJpegImage(exifData);
break;
case tiff:
updateTiffImage(exifData);
break;
case none:
/* do nothing */
break;
}
} // Thumbnail::update
void Thumbnail::updateJpegImage(ExifData& exifData) const
{
std::string key = "Thumbnail.RecordingOffset.JPEGInterchangeFormat";
ExifData::iterator pos = exifData.findKey(key);
if (pos == exifData.end()) {
Value *value = Value::create(unsignedLong);
exifData.add(key, value);
delete value;
pos = exifData.findKey(key);
}
pos->setValue("0");
key = "Thumbnail.RecordingOffset.JPEGInterchangeFormatLength";
pos = exifData.findKey(key);
if (pos == exifData.end()) {
Value *value = Value::create(unsignedLong);
exifData.add(key, value);
delete value;
pos = exifData.findKey(key);
}
pos->setValue(toString(size_));
} // Thumbnail::updateJpegImage
void Thumbnail::updateTiffImage(ExifData& exifData) const
{
// Create metadata from the StripOffsets and StripByteCounts entries
// and add these to the Exif data, replacing existing entries
Ifd::const_iterator pos = ifd_.findTag(0x0111);
if (pos == ifd_.end()) throw Error("Bad thumbnail (0x0111)");
exifData.add(Metadatum(*pos, tiffHeader_.byteOrder()));
pos = ifd_.findTag(0x0117);
if (pos == ifd_.end()) throw Error("Bad thumbnail (0x0117)");
exifData.add(Metadatum(*pos, tiffHeader_.byteOrder()));
} // Thumbnail::updateTiffImage
long Thumbnail::copy(char* buf) const
{
long ret = 0;
switch (type_) {
case jpeg:
ret = copyJpegImage(buf);
break;
case tiff:
ret = copyTiffImage(buf);
break;
case none:
ret = 0;
break;
}
return ret;
}
long Thumbnail::copyJpegImage(char* buf) const
{
memcpy(buf, image_, size_);
return size_;
}
long Thumbnail::copyTiffImage(char* buf) const
{
long offset = ifd_.offset() + ifd_.size() + ifd_.dataSize();
long size = size_ - offset;
memcpy(buf, image_ + offset, size);
return size;
}
long Thumbnail::size() const
{
long size = 0;
switch (type_) {
case jpeg:
size = size_;
break;
case tiff:
size = size_ - ifd_.offset() - ifd_.size() - ifd_.dataSize();
break;
case none:
size = 0;
break;
}
return size;
}
void Thumbnail::setOffsets(Ifd& ifd1, ByteOrder byteOrder) const
{
switch (type_) {
case jpeg:
setJpegImageOffsets(ifd1, byteOrder);
break;
case tiff:
setTiffImageOffsets(ifd1, byteOrder);
break;
case none:
/* do nothing */
break;
}
}
void Thumbnail::setJpegImageOffsets(Ifd& ifd1, ByteOrder byteOrder) const
{
Ifd::iterator pos = ifd1.findTag(0x0201);
if (pos == ifd1.end()) throw Error("Bad thumbnail (0x0201)");
pos->setOffset(ifd1.offset() + ifd1.size() + ifd1.dataSize(), byteOrder);
22 years ago
}
void Thumbnail::setTiffImageOffsets(Ifd& ifd1, ByteOrder byteOrder) const
{
// Adjust the StripOffsets, assuming that the existing TIFF strips
// start immediately after the thumbnail IFD
long shift = ifd1.offset() + ifd1.size() + ifd1.dataSize()
- ifd_.offset() - ifd_.size() - ifd_.dataSize();
Ifd::const_iterator pos = ifd_.findTag(0x0111);
if (pos == ifd_.end()) throw Error("Bad thumbnail (0x0111)");
Metadatum offsets(*pos, tiffHeader_.byteOrder());
std::ostringstream os;
for (long k = 0; k < offsets.count(); ++k) {
os << offsets.toLong(k) + shift << " ";
}
offsets.setValue(os.str());
// Write the offsets to IFD1, encoded in the corresponding byte order
addToIfd(ifd1, offsets, byteOrder);
} // Thumbnail::setTiffImageOffsets
ExifData::ExifData()
: ifd0_(ifd0, 0, false), exifIfd_(exifIfd, 0, false),
iopIfd_(iopIfd, 0, false), gpsIfd_(gpsIfd, 0, false),
ifd1_(ifd1, 0, false), valid_(false), size_(0), data_(0)
{
}
ExifData::~ExifData()
{
delete[] data_;
}
22 years ago
int ExifData::read(const std::string& path)
{
JpegImage img;
int rc = img.readExifData(path);
if (rc) return rc;
return read(img.exifData(), img.sizeExifData());
}
int ExifData::read(const char* buf, long len)
{
// Copy the data buffer
delete[] data_;
data_ = new char[len];
memcpy(data_, buf, len);
size_ = len;
valid_ = true;
// Read the TIFF header
int ret = 0;
int rc = tiffHeader_.read(data_);
22 years ago
if (rc) return rc;
// Read IFD0
rc = ifd0_.read(data_ + tiffHeader_.offset(),
byteOrder(),
tiffHeader_.offset());
22 years ago
if (rc) return rc;
// Find and read ExifIFD sub-IFD of IFD0
rc = ifd0_.readSubIfd(exifIfd_, data_, byteOrder(), 0x8769);
22 years ago
if (rc) return rc;
// Find and read Interoperability IFD in ExifIFD
rc = exifIfd_.readSubIfd(iopIfd_, data_, byteOrder(), 0xa005);
22 years ago
if (rc) return rc;
// Find and read GPSInfo sub-IFD in IFD0
rc = ifd0_.readSubIfd(gpsIfd_, data_, byteOrder(), 0x8825);
22 years ago
if (rc) return rc;
// Read IFD1
if (ifd0_.next()) {
rc = ifd1_.read(data_ + ifd0_.next(), byteOrder(), ifd0_.next());
22 years ago
if (rc) return rc;
}
// Find and delete ExifIFD sub-IFD of IFD1
Ifd::iterator pos = ifd1_.findTag(0x8769);
if (pos != ifd1_.end()) {
ifd1_.erase(pos);
ret = -99;
}
// Find and delete GPSInfo sub-IFD in IFD1
pos = ifd1_.findTag(0x8825);
if (pos != ifd1_.end()) {
ifd1_.erase(pos);
ret = -99;
}
22 years ago
// Copy all entries from the IFDs to the internal metadata
22 years ago
metadata_.clear();
add(ifd0_.begin(), ifd0_.end(), byteOrder());
add(exifIfd_.begin(), exifIfd_.end(), byteOrder());
add(iopIfd_.begin(), iopIfd_.end(), byteOrder());
add(gpsIfd_.begin(), gpsIfd_.end(), byteOrder());
add(ifd1_.begin(), ifd1_.end(), byteOrder());
22 years ago
22 years ago
// Read the thumbnail
thumbnail_.read(data_, *this, byteOrder());
22 years ago
return ret;
22 years ago
} // ExifData::read
int ExifData::write(const std::string& path)
{
long size = this->size();
char* buf = new char[size];
long actualSize = copy(buf);
if (actualSize > size) {
throw Error("Invariant violated in ExifData::write");
}
JpegImage img;
img.setExifData(buf, actualSize);
return img.writeExifData(path);
} // ExifData::write
long ExifData::copy(char* buf)
22 years ago
{
long size = 0;
// If we can update the internal IFDs and the underlying data buffer
// from the metadata without changing the data size, then it is enough
// to copy the data buffer.
if (updateIfds()) {
//ahu Todo: remove debugging output
std::cout << "->>>>>> using non-intrusive writing <<<<<<-\n";
memcpy(buf, data_, size_);
size = size_;
}
// Else we have to do it the hard way...
else {
//ahu Todo: remove debugging output
std::cout << "->>>>>> writing from metadata <<<<<<-\n";
size = copyFromMetadata(buf);
}
return size;
}
long ExifData::copyFromMetadata(char* buf)
{
// Copy the TIFF header
long ifd0Offset = tiffHeader_.copy(buf);
// Build IFD0
Ifd ifd0(ifd0, ifd0Offset);
addToIfd(ifd0, begin(), end(), byteOrder());
// Build Exif IFD from metadata
long exifIfdOffset = ifd0Offset + ifd0.size() + ifd0.dataSize();
Ifd exifIfd(exifIfd, exifIfdOffset);
addToIfd(exifIfd, begin(), end(), byteOrder());
// Set the offset to the Exif IFD in IFD0
ifd0.erase(0x8769);
if (exifIfd.size() > 0) {
ifd0.setOffset(0x8769, exifIfdOffset, byteOrder());
}
// Build Interoperability IFD from metadata
long iopIfdOffset = exifIfdOffset + exifIfd.size() + exifIfd.dataSize();
Ifd iopIfd(iopIfd, iopIfdOffset);
addToIfd(iopIfd, begin(), end(), byteOrder());
// Set the offset to the Interoperability IFD in Exif IFD
exifIfd.erase(0xa005);
if (iopIfd.size() > 0) {
exifIfd.setOffset(0xa005, iopIfdOffset, byteOrder());
}
// Build GPSInfo IFD from metadata
long gpsIfdOffset = iopIfdOffset + iopIfd.size() + iopIfd.dataSize();
Ifd gpsIfd(gpsIfd, gpsIfdOffset);
addToIfd(gpsIfd, begin(), end(), byteOrder());
// Set the offset to the GPSInfo IFD in IFD0
ifd0.erase(0x8825);
if (gpsIfd.size() > 0) {
ifd0.setOffset(0x8825, gpsIfdOffset, byteOrder());
}
// Update Exif data from thumbnail, build IFD1 from updated metadata
thumbnail_.update(*this);
long ifd1Offset = gpsIfdOffset + gpsIfd.size() + gpsIfd.dataSize();
Ifd ifd1(ifd1, ifd1Offset);
addToIfd(ifd1, begin(), end(), byteOrder());
thumbnail_.setOffsets(ifd1, byteOrder());
long thumbOffset = ifd1Offset + ifd1.size() + ifd1.dataSize();
// Set the offset to IFD1 in IFD0
if (ifd1.size() > 0) {
ifd0.setNext(ifd1Offset);
}
// Copy all IFDs and the thumbnail image to the data buffer
ifd0.sortByTag();
ifd0.copy(buf + ifd0Offset, byteOrder(), ifd0Offset);
exifIfd.sortByTag();
exifIfd.copy(buf + exifIfdOffset, byteOrder(), exifIfdOffset);
iopIfd.sortByTag();
iopIfd.copy(buf + iopIfdOffset, byteOrder(), iopIfdOffset);
gpsIfd.sortByTag();
gpsIfd.copy(buf + gpsIfdOffset, byteOrder(), gpsIfdOffset);
ifd1.sortByTag();
ifd1.copy(buf + ifd1Offset, byteOrder(), ifd1Offset);
long len = thumbnail_.copy(buf + thumbOffset);
return len + thumbOffset;
} // ExifData::copyFromMetadata
22 years ago
long ExifData::size() const
{
long size;
if (compatible()) {
size = size_;
}
else {
size = tiffHeader_.size();
std::map<IfdId, int> ifdEntries;
const_iterator mdEnd = this->end();
for (const_iterator md = begin(); md != mdEnd; ++md) {
size += md->size();
ifdEntries[md->ifdId()] += 1;
}
std::map<IfdId, int>::const_iterator eEnd = ifdEntries.end();
std::map<IfdId, int>::const_iterator e;
for (e = ifdEntries.begin(); e != eEnd; ++e) {
size += 2 + 12 * e->second + 4;
}
size += thumbnail_.size();
// Add 1k to account for the possibility that Thumbnail::update
// may add entries to IFD1
size += 1024;
}
return size;
22 years ago
}
void ExifData::add(Ifd::const_iterator begin,
Ifd::const_iterator end,
ByteOrder byteOrder)
22 years ago
{
Ifd::const_iterator i = begin;
for (; i != end; ++i) {
add(Metadatum(*i, byteOrder));
22 years ago
}
}
void ExifData::add(const std::string& key, Value* value)
{
add(Metadatum(key, value));
}
void ExifData::add(const Metadatum& metadatum)
{
iterator i = findKey(metadatum.key());
22 years ago
if (i != end()) {
i->setValue(&metadatum.value());
22 years ago
}
else {
metadata_.push_back(metadatum);
}
22 years ago
}
ExifData::const_iterator ExifData::findKey(const std::string& key) const
{
return std::find_if(metadata_.begin(), metadata_.end(),
FindMetadatumByKey(key));
}
22 years ago
ExifData::iterator ExifData::findKey(const std::string& key)
{
return std::find_if(metadata_.begin(), metadata_.end(),
FindMetadatumByKey(key));
}
void ExifData::sortByKey()
{
std::sort(metadata_.begin(), metadata_.end(), cmpMetadataByKey);
}
void ExifData::sortByTag()
{
std::sort(metadata_.begin(), metadata_.end(), cmpMetadataByTag);
}
22 years ago
void ExifData::erase(const std::string& key)
{
iterator pos = findKey(key);
if (pos != end()) erase(pos);
}
void ExifData::erase(ExifData::iterator pos)
{
metadata_.erase(pos);
}
bool ExifData::updateIfds()
{
if (!this->compatible()) return false;
bool compatible = true;
compatible |= updateIfd(ifd0_);
compatible |= updateIfd(exifIfd_);
compatible |= updateIfd(iopIfd_);
compatible |= updateIfd(gpsIfd_);
compatible |= updateIfd(ifd1_);
return compatible;
} // ExifData::updateIfds
bool ExifData::updateIfd(Ifd& ifd)
{
if (ifd.alloc()) throw Error("Invariant violated in ExifData::updateIfd");
bool compatible = true;
Ifd::iterator end = ifd.end();
for (Ifd::iterator entry = ifd.begin(); entry != end; ++entry) {
// find the corresponding metadatum
std::string key = ExifTags::makeKey(entry->tag(), entry->ifdId());
const_iterator md = findKey(key);
if (md == this->end()) {
// corresponding metadatum was deleted: this is not (yet) a
// supported non-intrusive write operation.
compatible = false;
continue;
}
char* buf = new char[md->size()];
md->copy(buf, byteOrder());
entry->setValue(md->typeId(), buf, md->size());
delete[] buf;
}
return compatible;
} // ExifData::updateIfd
bool ExifData::compatible() const
{
bool compatible = true;
const_iterator end = this->end();
for (const_iterator md = begin(); md != end; ++md) {
// Check if the metadatum is compatible with the
// corresponding IFD entry
const Ifd* ifd = getIfd(md->ifdId());
if (!ifd) {
compatible = false;
break;
}
Ifd::const_iterator entry = ifd->findTag(md->tag());
if (entry == ifd->end()) {
compatible = false;
break;
}
if (md->size() > entry->size()) {
compatible = false;
break;
}
}
return compatible;
}
const Ifd* ExifData::getIfd(IfdId ifdId) const
{
const Ifd* ifd = 0;
switch (ifdId) {
case ifd0:
ifd = &ifd0_;
break;
case exifIfd:
ifd = &exifIfd_;
break;
case iopIfd:
ifd = &iopIfd_;
break;
case gpsIfd:
ifd = &gpsIfd_;
break;
case ifd1:
ifd = &ifd1_;
break;
default:
ifd = 0;
break;
}
return ifd;
}
22 years ago
// *************************************************************************
// free functions
void addToIfd(Ifd& ifd,
Metadata::const_iterator begin,
Metadata::const_iterator end,
ByteOrder byteOrder)
{
for (Metadata::const_iterator i = begin; i != end; ++i) {
// add only metadata with matching IFD id
if (i->ifdId() == ifd.ifdId()) {
addToIfd(ifd, *i, byteOrder);
}
}
} // addToIfd
void addToIfd(Ifd& ifd, const Metadatum& metadatum, ByteOrder byteOrder)
22 years ago
{
// Todo: Implement Assert (Stroustup 24.3.7.2)
if (!ifd.alloc()) throw Error("Invariant violated in addToIfd");
22 years ago
RawEntry e;
e.ifdId_ = metadatum.ifdId();
e.ifdIdx_ = metadatum.ifdIdx();
e.tag_ = metadatum.tag();
e.type_ = metadatum.typeId();
e.count_ = metadatum.count();
e.size_ = metadatum.size();
e.offset_ = 0; // will be calculated when the IFD is written
char* buf = 0;
if (e.size_ > 4) {
buf = new char[e.size_];
metadatum.copy(buf, byteOrder);
22 years ago
}
else {
metadatum.copy(e.offsetData_, byteOrder);
22 years ago
}
ifd.add(Entry(e, buf, ifd.alloc()));
delete[] buf;
} // addToIfd
bool cmpMetadataByTag(const Metadatum& lhs, const Metadatum& rhs)
{
return lhs.tag() < rhs.tag();
}
bool cmpMetadataByKey(const Metadatum& lhs, const Metadatum& rhs)
{
return lhs.key() < rhs.key();
}
std::ostream& operator<<(std::ostream& os, const Metadatum& md)
{
PrintFct fct = ExifTags::printFct(md.tag(), md.ifdId());
return fct(os, md.value());
}
} // namespace Exif