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.
390 lines
16 KiB
C++
390 lines
16 KiB
C++
22 years ago
|
// ***************************************************************** -*- C++ -*-
|
||
|
/*
|
||
|
* Copyright (C) 2004 Andreas Huggel <ahuggel@gmx.net>
|
||
|
*
|
||
|
* This program is part of the Exiv2 distribution.
|
||
|
*
|
||
|
* 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.
|
||
|
*/
|
||
|
/*!
|
||
|
@file ifd.hpp
|
||
|
@brief Encoding and decoding of IFD (Image File Directory) data
|
||
|
@version $Name: $ $Revision: 1.1 $
|
||
|
@author Andreas Huggel (ahu)
|
||
|
<a href="mailto:ahuggel@gmx.net">ahuggel@gmx.net</a>
|
||
|
@date 09-Jan-04, ahu: created
|
||
|
11-Feb-04, ahu: isolated as a component
|
||
|
*/
|
||
|
#ifndef IFD_HPP_
|
||
|
#define IFD_HPP_
|
||
|
|
||
|
// *****************************************************************************
|
||
|
// included header files
|
||
|
#include "types.hpp"
|
||
|
|
||
|
// + standard includes
|
||
|
#include <string>
|
||
|
#include <vector>
|
||
|
#include <iosfwd>
|
||
|
|
||
|
// *****************************************************************************
|
||
|
// namespace extensions
|
||
|
//! Provides classes and functions to encode and decode %Exif data.
|
||
|
namespace Exif {
|
||
|
|
||
|
// *****************************************************************************
|
||
|
// class definitions
|
||
|
|
||
|
/*!
|
||
|
@brief Simple structure for 'raw' IFD directory entries (without any data
|
||
|
greater than four bytes)
|
||
|
*/
|
||
|
struct RawEntry {
|
||
|
//! Default constructor
|
||
|
RawEntry();
|
||
|
//! The IFD id
|
||
|
IfdId ifdId_;
|
||
|
//! Position in the IFD
|
||
|
int ifdIdx_;
|
||
|
//! Tag
|
||
|
uint16 tag_;
|
||
|
//! Type
|
||
|
uint16 type_;
|
||
|
//! Number of components
|
||
|
uint32 count_;
|
||
|
//! Offset, unprocessed
|
||
|
uint32 offset_;
|
||
|
//! Data from the IFD offset field if size is less or equal to four
|
||
|
char offsetData_[4];
|
||
|
//! Size of the data in bytes
|
||
|
long size_;
|
||
|
}; // struct RawEntry
|
||
|
|
||
|
//! Container type to hold 'raw' IFD directory entries
|
||
|
typedef std::vector<RawEntry> RawEntries;
|
||
|
|
||
|
/*!
|
||
|
@brief Data structure for one IFD directory entry. See the description of
|
||
|
class Ifd for an explanation of the supported modes for memory
|
||
|
allocation.
|
||
|
*/
|
||
|
class Entry {
|
||
|
public:
|
||
|
/*!
|
||
|
@brief Default constructor. The entry allocates memory for its
|
||
|
data if alloc is true (the default), otherwise it remembers
|
||
|
just the pointers into a read and writeable data buffer which
|
||
|
it doesn't allocate or delete.
|
||
|
*/
|
||
|
explicit Entry(bool alloc =true);
|
||
|
/*!
|
||
|
@brief Constructor to create an %Entry from a raw entry structure
|
||
|
and a data buffer.
|
||
|
|
||
|
@param e 'Raw' entry structure filled with the relevant data. The
|
||
|
offset_ field will only be used if size_ is greater than four.
|
||
|
@param buf Character buffer with the data of the tag. If size_ is
|
||
|
less or equal four, the data from the original IFD offset
|
||
|
field must be available in the field offsetData_. The buf is
|
||
|
not needed in this case and can be 0.
|
||
|
@param alloc Determines if memory management is required. If alloc
|
||
|
is true, a data buffer will be allocated to store data
|
||
|
of more than four bytes, else only the pointer will be
|
||
|
stored. Data less or equal than four bytes is stored
|
||
|
locally in the %Entry.
|
||
|
*/
|
||
|
Entry(const RawEntry& e, const char* buf, bool alloc =true);
|
||
|
//! Destructor
|
||
|
~Entry();
|
||
|
//! Copy constructor
|
||
|
Entry(const Entry& rhs);
|
||
|
//! Assignment operator
|
||
|
Entry& operator=(const Entry& rhs);
|
||
|
//! @name Accessors
|
||
|
//@{
|
||
|
//! Return the IFD id
|
||
|
IfdId ifdId() const { return ifdId_; }
|
||
|
//! Return the index in the IFD
|
||
|
int ifdIdx() const { return ifdIdx_; }
|
||
|
//! Return the tag
|
||
|
uint16 tag() const { return tag_; }
|
||
|
//! Return the type id.
|
||
|
uint16 type() const { return type_; }
|
||
|
//! Return the number of components in the value
|
||
|
uint32 count() const { return count_; }
|
||
|
//! Return the offset from the start of the IFD
|
||
|
uint32 offset() const { return offset_; }
|
||
|
//! Return the size of the value in bytes
|
||
|
long size() const { return size_; }
|
||
|
/*!
|
||
|
@brief Return a pointer to the data area. Do not attempt to write
|
||
|
to this pointer.
|
||
|
*/
|
||
|
const char* data() const;
|
||
|
//! Get the memory allocation mode
|
||
|
bool alloc() const { return alloc_; }
|
||
|
//@}
|
||
|
|
||
|
//! Return the size in bytes of one element of this type
|
||
|
long typeSize() const
|
||
|
{ return TypeInfo::typeSize(TypeId(type_)); }
|
||
|
//! Return the name of the type
|
||
|
const char* typeName() const
|
||
|
{ return TypeInfo::typeName(TypeId(type_)); }
|
||
|
|
||
|
/*!
|
||
|
@brief Set the offset. If the size of the data is not greater than
|
||
|
four, the offset is written into the offset field as an
|
||
|
unsigned long using the byte order given to encode it.
|
||
|
*/
|
||
|
void setOffset(uint32 offset, ByteOrder byteOrder);
|
||
|
/*!
|
||
|
@brief Set type, count and the data of the entry.
|
||
|
@throw Error ("Size too large") if no memory allocation is allowed
|
||
|
and the size of the data of value is larger than that
|
||
|
available for the data of the entry.
|
||
|
*/
|
||
|
void setValue(uint16 type, const char* buf, long size);
|
||
|
|
||
|
private:
|
||
|
/*!
|
||
|
@brief True: Requires memory allocation and deallocation,<BR>
|
||
|
False: No memory management needed.
|
||
|
*/
|
||
|
bool alloc_;
|
||
|
IfdId ifdId_; // Redundant IFD id (it is also at the IFD)
|
||
|
int ifdIdx_; // Position in the IFD
|
||
|
uint16 tag_; // Tag
|
||
|
uint16 type_; // Type
|
||
|
uint32 count_; // Number of components
|
||
|
uint32 offset_; // Offset from the start of the IFD,
|
||
|
// 0 if size <=4, i.e., if there is no offset
|
||
|
char offsetData_[4]; // Data from the offset field if size <= 4
|
||
|
long size_; // Size of the data in bytes
|
||
|
char* data_; // Pointer to the data buffer
|
||
|
}; // class Entry
|
||
|
|
||
|
//! Container type to hold all IFD directory entries
|
||
|
typedef std::vector<Entry> Entries;
|
||
|
|
||
|
//! Unary predicate that matches an Entry with a given tag
|
||
|
class FindEntryByTag {
|
||
|
public:
|
||
|
//! Constructor, initializes the object with the tag to look for
|
||
|
FindEntryByTag(uint16 tag) : tag_(tag) {}
|
||
|
/*!
|
||
|
@brief Returns true if the tag of the argument entry is equal
|
||
|
to that of the object.
|
||
|
*/
|
||
|
bool operator()(const Entry& entry) const
|
||
|
{ return tag_ == entry.tag(); }
|
||
|
|
||
|
private:
|
||
|
uint16 tag_;
|
||
|
|
||
|
}; // class FindEntryByTag
|
||
|
|
||
|
/*!
|
||
|
@brief Models an IFD (Image File Directory)
|
||
|
|
||
|
This class operates in two modes, one that allocates and deallocates the
|
||
|
memory required to store the data, and one that doesn't perform such
|
||
|
memory management and which is suitable in the case where a global data
|
||
|
buffer is available and only pointers into this buffer need to be
|
||
|
remembered. Note that the different modes imply completely different copy
|
||
|
and assignment behaviours, with the first resulting in entirely separate
|
||
|
classes and the second mode resulting in multiple classes using one and
|
||
|
the same data buffer. Use the default mode (with memory management) if
|
||
|
possible. <BR>
|
||
|
The mode without memory management is used to make "non-intrusive write
|
||
|
support" possible. This allows writing to %Exif data of an image without
|
||
|
changing the data layout of the %Exif data, to maximize chances that tag
|
||
|
data, which the %Exif reader may not understand (e.g., the Makernote)
|
||
|
remains valid. A "non-intrusive write operation" is the modification of
|
||
|
tag data without increasing the data size.
|
||
|
*/
|
||
|
class Ifd {
|
||
|
public:
|
||
|
/*!
|
||
|
@brief Constructor. Allows to set the IFD identifier. Memory management
|
||
|
is enabled, offset is set to 0. Serves as default constructor.
|
||
|
*/
|
||
|
explicit Ifd(IfdId ifdId =ifdIdNotSet);
|
||
|
/*!
|
||
|
@brief Constructor. Allows to set the IFD identifier and the offset of
|
||
|
the IFD from the start of TIFF header. Memory management is
|
||
|
enabled.
|
||
|
*/
|
||
|
Ifd(IfdId ifdId, uint32 offset);
|
||
|
/*!
|
||
|
@brief Constructor. Allows to set the IFD identifier, offset of the
|
||
|
IFD from the start of TIFF header and choose whether or not
|
||
|
memory management is required for the Entries.
|
||
|
*/
|
||
|
Ifd(IfdId ifdId, uint32 offset, bool alloc);
|
||
|
|
||
|
//! Entries const iterator type
|
||
|
typedef Entries::const_iterator const_iterator;
|
||
|
//! Entries iterator type
|
||
|
typedef Entries::iterator iterator;
|
||
|
//! The first entry
|
||
|
const_iterator begin() const { return entries_.begin(); }
|
||
|
//! End of the entries
|
||
|
const_iterator end() const { return entries_.end(); }
|
||
|
//! The first entry
|
||
|
iterator begin() { return entries_.begin(); }
|
||
|
//! End of the entries
|
||
|
iterator end() { return entries_.end(); }
|
||
|
//! Find an IFD entry by tag, return a const iterator into the entries list
|
||
|
const_iterator findTag(uint16 tag) const;
|
||
|
//! Find an IFD entry by tag, return an iterator into the entries list
|
||
|
iterator findTag(uint16 tag);
|
||
|
//! Sort the IFD entries by tag
|
||
|
void sortByTag();
|
||
|
//! Delete the directory entry with the given tag
|
||
|
void erase(uint16 tag);
|
||
|
//! Delete the directory entry at iterator position pos
|
||
|
void erase(iterator pos);
|
||
|
/*!
|
||
|
@brief Set the offset of the entry identified by tag. If no entry with
|
||
|
this tag exists, an entry of type unsigned long with one
|
||
|
component is created. If the size of the data is greater than
|
||
|
four, the offset of the entry is set to the value provided in
|
||
|
offset, else it is written to the offset field of the entry as
|
||
|
an unsigned long, encoded according to the byte order.
|
||
|
*/
|
||
|
void setOffset(uint16 tag, uint32 offset, ByteOrder byteOrder);
|
||
|
//! Set the offset of the next IFD
|
||
|
void setNext(uint32 next) { next_ = next; }
|
||
|
/*!
|
||
|
@brief Add the Entry to the IFD. Checks for duplicates: if an entry
|
||
|
with the same tag already exists, the entry is overwritten. The
|
||
|
memory allocation mode of the entry to be added must match that
|
||
|
of the IFD and the IFD ids of the IFD and Entry must match.
|
||
|
*/
|
||
|
void add(const Entry& entry);
|
||
|
/*!
|
||
|
@brief Read a complete IFD and its data from a data buffer
|
||
|
|
||
|
@param buf Pointer to the data to decode. The buffer must start with the
|
||
|
IFD data (unlike the readSubIfd() method).
|
||
|
@param byteOrder Applicable byte order (little or big endian).
|
||
|
@param offset (Optional) offset of the IFD from the start of the TIFF
|
||
|
header, if known. If not given, the offset will be guessed
|
||
|
using the assumption that the smallest offset of all IFD
|
||
|
directory entries points to a data buffer immediately follwing
|
||
|
the IFD.
|
||
|
|
||
|
@return 0 if successful
|
||
|
*/
|
||
|
int read(const char* buf, ByteOrder byteOrder, long offset =0);
|
||
|
/*!
|
||
|
@brief Read a sub-IFD from the location pointed to by the directory entry
|
||
|
with the given tag.
|
||
|
|
||
|
@param dest References the destination IFD.
|
||
|
@param buf The data buffer to read from. The buffer must contain all Exif
|
||
|
data starting from the TIFF header (unlike the read() method).
|
||
|
@param byteOrder Applicable byte order (little or big endian).
|
||
|
@param tag Tag to look for.
|
||
|
|
||
|
@return 0 if successful
|
||
|
*/
|
||
|
int readSubIfd(
|
||
|
Ifd& dest, const char* buf, ByteOrder byteOrder, uint16 tag
|
||
|
) const;
|
||
|
/*!
|
||
|
@brief Copy the IFD to a data array, return the number of bytes
|
||
|
written.
|
||
|
|
||
|
First the number of IFD entries is written (2 bytes), followed
|
||
|
by all directory entries: tag (2), type (2), number of data
|
||
|
components (4) and offset to the data or the data, if it
|
||
|
occupies not more than four bytes (4). The directory entries
|
||
|
are followed by the offset of the next IFD (4). All these
|
||
|
fields are encoded according to the byte order argument. Data
|
||
|
that doesn't fit into the offset fields follows immediately
|
||
|
after the IFD entries. The offsets in the IFD are set to
|
||
|
correctly point to the data fields, using the offset parameter
|
||
|
or the offset of the IFD.
|
||
|
|
||
|
@param buf Pointer to the data buffer. The user must ensure that the
|
||
|
buffer has enough memory. Otherwise the call results in
|
||
|
undefined behaviour.
|
||
|
@param byteOrder Applicable byte order (little or big endian).
|
||
|
@param offset Target offset from the start of the TIFF header of the
|
||
|
data array. The IFD offsets will be adjusted as necessary. If
|
||
|
not given, then it is assumed that the IFD will remain at its
|
||
|
original position, i.e., the offset of the IFD will be used.
|
||
|
@return Returns the number of characters written.
|
||
|
*/
|
||
|
long copy(char* buf, ByteOrder byteOrder, long offset =0) const;
|
||
|
//! @name Accessors
|
||
|
//@{
|
||
|
//! Ifd id of the IFD
|
||
|
IfdId ifdId() const { return ifdId_; }
|
||
|
//! Offset of the IFD from SOI
|
||
|
long offset() const { return offset_; }
|
||
|
//! Get the offset to the next IFD from the start of the TIFF header
|
||
|
long next() const { return next_; }
|
||
|
//! Get the memory allocation mode
|
||
|
bool alloc() const { return alloc_; }
|
||
|
//@}
|
||
|
//! Get the size of this IFD in bytes (IFD only, without data)
|
||
|
long size() const;
|
||
|
/*!
|
||
|
@brief Return the total size of the data of this IFD in bytes,
|
||
|
sums the size of all directory entries where size is greater
|
||
|
than four (i.e., only data that requires memory outside the
|
||
|
IFD directory entries is counted).
|
||
|
*/
|
||
|
long dataSize() const;
|
||
|
/*!
|
||
|
@brief Print the IFD in human readable format to the given stream;
|
||
|
begin each line with prefix.
|
||
|
*/
|
||
|
void print(std::ostream& os, const std::string& prefix ="") const;
|
||
|
|
||
|
private:
|
||
|
const bool alloc_; // True: requires memory allocation and deallocation,
|
||
|
// False: no memory management needed.
|
||
|
Entries entries_; // IFD entries
|
||
|
IfdId ifdId_; // IFD Id
|
||
|
long offset_; // offset of the IFD from the start of TIFF header
|
||
|
long next_; // offset of next IFD from the start of the TIFF header
|
||
|
|
||
|
}; // class Ifd
|
||
|
|
||
|
// *****************************************************************************
|
||
|
// free functions
|
||
|
|
||
|
/*!
|
||
|
@brief Compare two 'raw' IFD entries by offset, taking care of special
|
||
|
cases where one or both of the entries don't have an offset.
|
||
|
Return true if the offset of entry lhs is less than that of rhs,
|
||
|
else false. By definition, entries without an offset are greater
|
||
|
than those with an offset.
|
||
|
*/
|
||
|
bool cmpRawEntriesByOffset(const RawEntry& lhs, const RawEntry& rhs);
|
||
|
/*!
|
||
|
@brief Compare two IFD entries by tag. Return true if the tag of entry
|
||
|
lhs is less than that of rhs.
|
||
|
*/
|
||
|
bool cmpEntriesByTag(const Entry& lhs, const Entry& rhs);
|
||
|
|
||
|
} // namespace Exif
|
||
|
|
||
|
#endif // #ifndef IFD_HPP_
|