2004-01-19 Lutz Mueller <lutz@users.sourceforge.net>
authorLutz Mueller <lutz.s.mueller@gmail.com>
Mon, 19 Jan 2004 21:49:50 +0000 (22:49 +0100)
committerLutz Mueller <lutz.s.mueller@gmail.com>
Mon, 19 Jan 2004 21:49:50 +0000 (22:49 +0100)
* contrib/c++: Files contributed by Hans Meine <hans_meine@gmx.net>.
  Completely untested.

ChangeLog
contrib/c++/Makefile [new file with mode: 0644]
contrib/c++/exif.hxx [new file with mode: 0644]
contrib/c++/exif_module.cxx [new file with mode: 0644]

index c5897e1..2aabb1c 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2004-01-19  Lutz Mueller <lutz@users.sourceforge.net>
+
+       * contrib/c++: Files contributed by Hans Meine <hans_meine@gmx.net>.
+         Completely untested.
+
 2004-01-08  Lutz Mueller <lutz@users.sourceforge.net>
 
        * libexif/exif-entry.c (exif_entry_get_value_brief): merge into
diff --git a/contrib/c++/Makefile b/contrib/c++/Makefile
new file mode 100644 (file)
index 0000000..7315071
--- /dev/null
@@ -0,0 +1,54 @@
+BOOST_PYTHON_LIB = -L/usr/local/boost_1_29_0/lib -lboost_python
+BOOST_INCLUDES = -I/usr/local/boost_1_29_0/src
+dynmoddir = `pwd`/local
+
+CXX = g++
+CXXFLAGS := -O2 -Wall
+CPPFLAGS := -I/usr/include/python2.2
+
+LIBTOOL = $(SHELL) ./libtool
+LIBCXX = $(LIBTOOL) --tag=CXX --mode=compile $(CXX)
+LINKCXX = $(LIBTOOL) --tag=CXX --mode=link $(CXX)
+LINKCXXLIB = $(LINKCXX) -rpath $(dynmoddir)
+LINKCXXMODULE = $(LINKCXXLIB) -module -avoid-version
+LIBINSTALL = $(LIBTOOL) --mode=install /bin/install -c -p
+INSTALL = /bin/install -c -p
+
+TARGET = exif.la
+
+OBJS = \
+    exif_module.lo
+
+all: $(TARGET)
+
+install: $(TARGET)
+       $(INSTALL) -d $(dynmoddir)
+       $(LIBINSTALL) $(TARGET) $(dynmoddir)
+
+$(TARGET): $(OBJS)
+       $(LINKCXXMODULE) -o $(TARGET) $(OBJS) $(BOOST_PYTHON_LIB) -lexif
+
+clean::
+       rm -rf .libs *.o *.so *.lo *.ld $(TARGET)
+
+%.o: %.cxx
+       $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $*.cxx
+
+%.lo: %.cxx
+       $(LIBCXX) $(CXXFLAGS) $(CPPFLAGS) -c $*.cxx
+
+%.d: %.cxx
+       @echo updating header dependencies for $*.cxx
+       @rm -f $*.d
+       @$(CXX) -M $(CXXFLAGS) $(CPPFLAGS) -c $*.cxx | sed "s@$*.o@$*.d $*.o@" > $*.d \
+        || { rm -f $*.d; exit 1; }
+
+%.ld: %.cxx
+       @echo updating header dependencies for $*.cxx
+       @rm -f $*.ld
+       @$(CXX) -M $(CXXFLAGS) $(CPPFLAGS) -c $*.cxx | sed "s@$*.o@$*.ld $*.lo@" > $*.ld \
+        || { rm -f $*.ld; exit 1; }
+
+ifneq "$(MAKECMDGOALS)" "clean"
+include $(patsubst %.lo, %.ld, $(OBJS))
+endif
diff --git a/contrib/c++/exif.hxx b/contrib/c++/exif.hxx
new file mode 100644 (file)
index 0000000..75cca30
--- /dev/null
@@ -0,0 +1,536 @@
+/* exif.hxx
+ *
+ * Copyright  2002,2003 Hans Meine <hans_meine@gmx.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef EXIF_HXX
+#define EXIF_HXX
+
+#include <libexif/exif-entry.h>
+#include <libexif/exif-content.h>
+#include <libexif/exif-ifd.h>
+#include <libexif/exif-data.h>
+#include <libexif/exif-format.h>
+#include <libexif/exif-utils.h>
+#include <stdexcept>
+#include <string>
+
+namespace Exif {
+
+#ifndef EXIF_NO_EXCEPTIONS
+struct InvalidIndex : std::runtime_error
+{
+    InvalidIndex(const std::string& s)
+    : std::runtime_error(s) {}
+};
+
+struct InvalidFormat : std::runtime_error
+{
+    InvalidFormat(const std::string& s)
+    : std::runtime_error(s) {}
+};
+
+struct IOError : std::runtime_error
+{
+    IOError(const std::string& s)
+    : std::runtime_error(s) {}
+};
+#endif // EXIF_NO_EXCEPTIONS
+
+struct Entry
+{
+    ExifEntry *entry_;
+
+        // construct an empty entry, FIXME: is this needed in the public API?
+    Entry()
+    : entry_(exif_entry_new())
+    {}
+
+        // construct an entry for the given tag
+    Entry(ExifTag tag)
+    : entry_(exif_entry_new())
+    {
+        exif_entry_initialize(entry_, tag);
+    }
+
+        // copy constructor
+    Entry(Entry const &other)
+    : entry_(other.entry_)
+    {
+        exif_entry_ref(entry_);
+    }
+
+        // internal, do not use directly
+    Entry(ExifEntry *entry)
+    : entry_(entry)
+    {
+        exif_entry_ref(entry_);
+    }
+
+    ~Entry()
+    {
+        exif_entry_unref(entry_);
+    }
+
+    Entry &operator=(Entry const &other)
+    {
+        exif_entry_unref(entry_);
+        entry_ = other.entry_;
+        exif_entry_ref(entry_);
+        return *this;
+    }
+
+    ExifTag tag() const
+    {
+        return entry_->tag;
+    }
+
+/*
+    void setTag(ExifTag tag)
+    {
+        entry_->tag = tag;
+    }
+*/
+
+    ExifFormat format() const
+    {
+        return entry_->format;
+    }
+
+/*
+    void setFormat(ExifFormat format)
+    {
+        entry_->format = format;
+    }
+*/
+
+    unsigned long components() const
+    {
+        return entry_->components;
+    }
+
+/*
+    void setComponents(unsigned long components)
+    {
+        entry_->components = components;
+    }
+
+    void initialize(ExifTag tag)
+    {
+        exif_entry_initialize(entry_, tag);
+    }
+*/
+
+    ExifByte getByte(unsigned int index) const
+    {
+#ifndef EXIF_NO_EXCEPTIONS
+        if(entry_->format != EXIF_FORMAT_BYTE)
+            throw InvalidFormat(
+                "Exif::Entry::getByte(): Format is not EXIF_FORMAT_BYTE");
+        if(index >= components())
+            throw InvalidIndex(
+                "Exif::getByte: component index out of range");
+#endif
+        return *(entry_->data
+                 + index * exif_format_get_size(entry_->format));
+    }
+
+    const ExifAscii getAscii() const
+    {
+#ifndef EXIF_NO_EXCEPTIONS
+        if(entry_->format != EXIF_FORMAT_ASCII)
+            throw InvalidFormat(
+                "Exif::Entry::getAscii(): Format is not EXIF_FORMAT_ASCII");
+#endif
+        return (ExifAscii)entry_->data;
+    }
+
+    ExifShort getShort(unsigned int index) const
+    {
+#ifndef EXIF_NO_EXCEPTIONS
+        if(entry_->format != EXIF_FORMAT_SHORT)
+            throw InvalidFormat(
+                "Exif::Entry::getShort(): Format is not EXIF_FORMAT_SHORT");
+        if(index >= components())
+            throw InvalidIndex(
+                "Exif::getShort: component index out of range");
+#endif
+        return exif_get_short(entry_->data
+                              + index * exif_format_get_size(entry_->format),
+                              exif_data_get_byte_order(entry_->parent->parent));
+    }
+
+    ExifLong getLong(unsigned int index) const
+    {
+#ifndef EXIF_NO_EXCEPTIONS
+        if(entry_->format != EXIF_FORMAT_LONG)
+            throw InvalidFormat(
+                "Exif::Entry::getLong(): Format is not EXIF_FORMAT_LONG");
+        if(index >= components())
+            throw InvalidIndex(
+                "Exif::getLong: component index out of range");
+#endif
+        return exif_get_long(entry_->data
+                             + index * exif_format_get_size(entry_->format),
+                             exif_data_get_byte_order(entry_->parent->parent));
+    }
+
+    ExifSLong getSLong(unsigned int index) const
+    {
+#ifndef EXIF_NO_EXCEPTIONS
+        if(entry_->format != EXIF_FORMAT_SLONG)
+            throw InvalidFormat(
+                "Exif::Entry::getSLong(): Format is not EXIF_FORMAT_SLONG");
+        if(index >= components())
+            throw InvalidIndex(
+                "Exif::getSLong: component index out of range");
+#endif
+        return exif_get_slong(entry_->data
+                              + index * exif_format_get_size(entry_->format),
+                              exif_data_get_byte_order(entry_->parent->parent));
+    }
+
+    ExifRational getRational(unsigned int index) const
+    {
+#ifndef EXIF_NO_EXCEPTIONS
+        if(entry_->format != EXIF_FORMAT_RATIONAL)
+            throw InvalidFormat(
+                "Exif::Entry::getRational(): Format is not EXIF_FORMAT_RATIONAL");
+        if(index >= components())
+            throw InvalidIndex(
+                "Exif::getRational: component index out of range");
+#endif
+        return exif_get_rational(entry_->data
+                                 + index * exif_format_get_size(entry_->format),
+                                 exif_data_get_byte_order(entry_->parent->parent));
+    }
+
+    ExifSRational getSRational(unsigned int index) const
+    {
+#ifndef EXIF_NO_EXCEPTIONS
+        if(entry_->format != EXIF_FORMAT_SRATIONAL)
+            throw InvalidFormat(
+                "Exif::Entry::getSRational(): Format is not EXIF_FORMAT_SRATIONAL");
+        if(index >= components())
+            throw InvalidIndex(
+                "Exif::getSRational: component index out of range");
+#endif
+        return exif_get_srational(entry_->data
+                                  + index * exif_format_get_size(entry_->format),
+                                  exif_data_get_byte_order(entry_->parent->parent));
+    }
+
+    void setByte(unsigned int index, ExifByte value) const
+    {
+#ifndef EXIF_NO_EXCEPTIONS
+        if(entry_->format != EXIF_FORMAT_BYTE)
+            throw InvalidFormat(
+                "Exif::Entry::setByte(): Format is not EXIF_FORMAT_BYTE");
+        if(index >= components())
+            throw InvalidIndex(
+                "Exif::setByte: component index out of range");
+#endif
+        *(entry_->data
+          + index * exif_format_get_size(entry_->format)) = value;
+    }
+
+/*
+    const ExifAscii setAscii() const
+    {
+#ifndef EXIF_NO_EXCEPTIONS
+        if(entry_->format != EXIF_FORMAT_ASCII)
+            throw InvalidFormat(
+                "Exif::Entry::setAscii(): Format is not EXIF_FORMAT_ASCII");
+#endif
+        return (ExifAscii)entry_->data;
+    }
+*/
+
+    void setShort(unsigned int index, ExifShort value) const
+    {
+#ifndef EXIF_NO_EXCEPTIONS
+        if(entry_->format != EXIF_FORMAT_SHORT)
+            throw InvalidFormat(
+                "Exif::Entry::setShort(): Format is not EXIF_FORMAT_SHORT");
+        if(index >= components())
+            throw InvalidIndex(
+                "Exif::setShort: component index out of range");
+#endif
+        return exif_set_short(entry_->data
+                              + index * exif_format_get_size(entry_->format),
+                              exif_data_get_byte_order(entry_->parent->parent),
+                              value);
+    }
+
+    void setLong(unsigned int index, ExifLong value) const
+    {
+#ifndef EXIF_NO_EXCEPTIONS
+        if(entry_->format != EXIF_FORMAT_LONG)
+            throw InvalidFormat(
+                "Exif::Entry::setLong(): Format is not EXIF_FORMAT_LONG");
+        if(index >= components())
+            throw InvalidIndex(
+                "Exif::setLong: component index out of range");
+#endif
+        return exif_set_long(entry_->data
+                             + index * exif_format_get_size(entry_->format),
+                             exif_data_get_byte_order(entry_->parent->parent),
+                             value);
+    }
+
+    void setSLong(unsigned int index, ExifSLong value) const
+    {
+#ifndef EXIF_NO_EXCEPTIONS
+        if(entry_->format != EXIF_FORMAT_SLONG)
+            throw InvalidFormat(
+                "Exif::Entry::setSLong(): Format is not EXIF_FORMAT_SLONG");
+        if(index >= components())
+            throw InvalidIndex(
+                "Exif::setSLong: component index out of range");
+#endif
+        return exif_set_slong(entry_->data
+                              + index * exif_format_get_size(entry_->format),
+                              exif_data_get_byte_order(entry_->parent->parent),
+                              value);
+    }
+
+    void setRational(unsigned int index, ExifRational value) const
+    {
+#ifndef EXIF_NO_EXCEPTIONS
+        if(entry_->format != EXIF_FORMAT_RATIONAL)
+            throw InvalidFormat(
+                "Exif::Entry::setRational(): Format is not EXIF_FORMAT_RATIONAL");
+        if(index >= components())
+            throw InvalidIndex(
+                "Exif::setRational: component index out of range");
+#endif
+        return exif_set_rational(entry_->data
+                                 + index * exif_format_get_size(entry_->format),
+                                 exif_data_get_byte_order(entry_->parent->parent),
+                                 value);
+    }
+
+    void setSRational(unsigned int index, ExifSRational value) const
+    {
+#ifndef EXIF_NO_EXCEPTIONS
+        if(entry_->format != EXIF_FORMAT_SRATIONAL)
+            throw InvalidFormat(
+                "Exif::Entry::setSRational(): Format is not EXIF_FORMAT_SRATIONAL");
+        if(index >= components())
+            throw InvalidIndex(
+                "Exif::setSRational: component index out of range");
+#endif
+        return exif_set_srational(entry_->data
+                                  + index * exif_format_get_size(entry_->format),
+                                  exif_data_get_byte_order(entry_->parent->parent),
+                                  value);
+    }
+
+    const char *value()
+    {
+        return exif_entry_get_value(entry_);
+    }
+
+    const char *briefValue()
+    {
+        return exif_entry_get_value_brief(entry_);
+    }
+
+    void dump(unsigned int indent = 0) const
+    {
+        exif_entry_dump(entry_, indent);
+    }
+};
+
+struct Content
+{
+    ExifContent *content_;
+
+    Content()
+    : content_(exif_content_new())
+    {}
+
+    Content(Content const &other)
+    : content_(other.content_)
+    {
+        exif_content_ref(content_);
+    }
+
+        // internal, do not use directly
+    Content(ExifContent *content)
+    : content_(content)
+    {
+        exif_content_ref(content_);
+    }
+
+    ~Content()
+    {
+        exif_content_unref(content_);
+    }
+
+    Content &operator=(Content const &other)
+    {
+        exif_content_unref(content_);
+        content_ = other.content_;
+        exif_content_ref(content_);
+        return *this;
+    }
+
+    Entry operator[](ExifTag tag)
+    {
+        ExifEntry *result = exif_content_get_entry(content_, tag);
+        if(result)
+            return Entry(result);
+#ifndef EXIF_NO_EXCEPTIONS
+        throw InvalidIndex(
+            "Exif::Content: IFD does not contain given tag");
+#endif
+        return Entry();
+    }
+
+    Entry operator[](unsigned int index)
+    {
+        if(index < size())
+            return Entry(content_->entries[index]);
+#ifndef EXIF_NO_EXCEPTIONS
+        throw InvalidIndex(
+            "Exif::Content: numeric entry index out of range");
+#endif // EXIF_NO_EXCEPTIONS
+        return Entry();
+    }
+
+    unsigned int size() const
+    {
+        // FIXME: content_ should never be NULL, so this is unneeded!?
+        return content_ ? content_->count : 0;
+    }
+
+    void add(Entry &entry)
+    {
+        exif_content_add_entry(content_, entry.entry_);
+    }
+
+    void remove(Entry &entry)
+    {
+        exif_content_remove_entry(content_, entry.entry_);
+    }
+
+        // for your convenience
+    const char *value(ExifTag tag)
+    {
+        return exif_content_get_value(content_, tag);
+    }
+
+        // for your convenience
+    const char *briefValue(ExifTag tag)
+    {
+        return exif_content_get_value_brief(content_, tag);
+    }
+
+    void dump(unsigned int indent = 0) const
+    {
+        exif_content_dump(content_, indent);
+    }
+};
+
+struct Data
+{
+    ExifData *data_;
+
+    Data()
+    : data_(exif_data_new())
+    {}
+
+    Data(const char *path, bool *success = 0)
+    : data_(exif_data_new_from_file(path))
+    {
+        if(success)
+            *success = data_;
+#ifndef EXIF_NO_EXCEPTIONS
+        else
+            if(!data_)
+                throw IOError("Exif::Data: Could not load file");
+#endif // EXIF_NO_EXCEPTIONS
+        if(!data_)
+            exif_data_new();
+    }
+
+    Data(const unsigned char *data,
+         unsigned int size)
+    : data_(exif_data_new_from_data(data, size))
+    {}
+
+    Data(Data const &other)
+    : data_(other.data_)
+    {
+        exif_data_ref(data_);
+    }
+
+    ~Data()
+    {
+        exif_data_unref(data_);
+    }
+
+    Data &operator=(Data const &other)
+    {
+        exif_data_unref(data_);
+        data_ = other.data_;
+        exif_data_ref(data_);
+        return *this;
+    }
+
+    void save(unsigned char **d, unsigned int *size)
+    {
+        exif_data_save_data(data_, d, size);
+    }
+
+    unsigned int size() const
+    {
+        return EXIF_IFD_COUNT;
+    }
+
+    Content operator[](unsigned int index)
+    {
+        if(index < size())
+            return Content(data_->ifd[index]);
+#ifndef EXIF_NO_EXCEPTIONS
+        throw InvalidIndex(
+            "Exif::Data: IFD index out of range");
+#endif // EXIF_NO_EXCEPTIONS
+        return Content();
+    }
+
+    ExifByteOrder byteOrder() const
+    {
+        return exif_data_get_byte_order(data_);
+    }
+
+    void setByteOrder(ExifByteOrder bo) const
+    {
+        exif_data_set_byte_order(data_, bo);
+    }
+
+    void dump()
+    {
+        exif_data_dump(data_);
+    }
+};
+
+} // namespace Exif
+
+#endif // EXIF_HXX
diff --git a/contrib/c++/exif_module.cxx b/contrib/c++/exif_module.cxx
new file mode 100644 (file)
index 0000000..4de6a02
--- /dev/null
@@ -0,0 +1,524 @@
+/* exif-content.c
+ *
+ * Copyright  2002,2003 Hans Meine <hans_meine@gmx.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "exif.hxx"
+#include <string>
+#include <iostream>
+
+#include <Python.h>
+#include <boost/python.hpp>
+using namespace boost::python;
+
+template<class Wrapper, class Pointer>
+struct WrappedObjectIterator
+{
+       //typedef Wrapper value_type;
+       Pointer *it_, *end_;
+
+       WrappedObjectIterator(Pointer *it, Pointer *end)
+               : it_(it), end_(end)
+       {}
+
+       Wrapper next()
+       {
+               if(it_ == end_)
+               {
+                       PyErr_SetString(PyExc_StopIteration, "iterator exhausted");
+                       throw_error_already_set();
+               }
+               return Wrapper(*it_++);
+       }
+};
+
+struct PythonEntry : public Exif::Entry
+{
+       PythonEntry() {}
+       PythonEntry(Exif::Entry const &other) : Exif::Entry(other) {}
+
+       object component(long index) const
+       {
+               switch(format())
+               {
+               case EXIF_FORMAT_BYTE:
+                       return object(getByte(index));
+               case EXIF_FORMAT_SHORT:
+                       return object(getShort(index));
+               case EXIF_FORMAT_LONG:
+                       return object(getLong(index));
+               case EXIF_FORMAT_SLONG:
+                       return object(getSLong(index));
+               case EXIF_FORMAT_RATIONAL:
+                       return object(getRational(index));
+               case EXIF_FORMAT_SRATIONAL:
+                       return object(getSRational(index));
+               case EXIF_FORMAT_ASCII:
+                       //std::cerr << "returning " << entry_->size << " bytes of data..\n";
+                       //std::cerr << " (copied into " << std::string((char *)data, entry_->size).size() << "-character string)\n";
+                       return object(std::string((char *)entry_->data, entry_->size));
+               default:
+                       break;
+               }
+               return object();
+       }
+
+       object data() const
+       {
+               if((format() == EXIF_FORMAT_ASCII) || (components()==1))
+                       return component(0);
+               else
+               {
+                       list result;
+                       for(unsigned int i=0; i<components(); ++i)
+                               result.append(component(i));
+                       return result;
+               }
+       }
+
+       template<class Type>
+       Type extractComponent(unsigned int index, object value,
+                                                 const char *errorString)
+       {
+               extract<Type> extr(value);
+               if(!extr.check())
+               {
+                       PyErr_SetString(PyExc_TypeError, errorString);
+                       throw_error_already_set();
+               }
+               return extr();
+       }
+
+       void setComponent(unsigned int index, object value)
+       {
+               unsigned char *data= entry_->data
+                                                        + index * exif_format_get_size(format());
+               ExifByteOrder bo = exif_data_get_byte_order(entry_->parent->parent);
+
+               switch(format())
+               {
+               case EXIF_FORMAT_BYTE:
+                       *data= extractComponent<ExifByte>(index, value, "invalid assignment to data: could not convert value to byte format");
+                       break;
+               case EXIF_FORMAT_SHORT:
+                       exif_set_short(data, bo, extractComponent<ExifShort>(index, value, "invalid assignment to data: could not convert value to short format"));
+                       break;
+               case EXIF_FORMAT_LONG:
+                       exif_set_long(data, bo, extractComponent<ExifLong>(index, value, "invalid assignment to data: could not convert value to long format"));
+                       break;
+               case EXIF_FORMAT_SLONG:
+                       exif_set_slong(data, bo, extractComponent<ExifSLong>(index, value, "invalid assignment to data: could not convert value to signed long format"));
+                       break;
+               case EXIF_FORMAT_RATIONAL:
+                       exif_set_rational(data, bo, extractComponent<ExifRational>(index, value, "invalid assignment to data: could not convert value to rational format (2-tuple expected)"));
+                       break;
+               case EXIF_FORMAT_SRATIONAL:
+                       exif_set_srational(data, bo, extractComponent<ExifSRational>(index, value, "invalid assignment to data: could not convert value to signed rational format (2-tuple expected)"));
+                       break;
+               case EXIF_FORMAT_ASCII: // handled in setData directly
+               case EXIF_FORMAT_UNDEFINED:
+                       break;
+               }
+               return;
+       }
+
+       void setData(object data)
+       {
+               if(format() == EXIF_FORMAT_ASCII)
+               {
+                       extract<std::string> xstr(data);
+                       if(xstr.check())
+                       {
+                               std::string s= xstr();
+                               if(entry_->data)
+                                       free(entry_->data);
+                               entry_->components= s.size();
+                               //std::cerr << "assigning " << s.size() << "-character string..\n";
+                               entry_->size=
+                                       exif_format_get_size(format()) * entry_->components;
+                               entry_->data= (unsigned char *)malloc(entry_->size);
+                               memcpy(entry_->data, s.data(), entry_->size);
+                               entry_->data[entry_->size]= 0;
+                       }
+                       else
+                       {
+                               PyErr_SetString(PyExc_TypeError,
+                                                               "invalid assignment to data of ASCII format entry: string expected");
+                               throw_error_already_set();
+                       }
+               }
+               else
+               {
+                       if(components()==1)
+                               setComponent(0, data);
+                       else
+                       {
+                               extract<list> xlist(data);
+                               if(xlist.check())
+                               {
+                                       list l= xlist();
+                                       for(unsigned i=0; i<components(); ++i)
+                                               setComponent(i, l[i]);
+                               }
+                               else
+                               {
+                                       PyErr_SetString(PyExc_TypeError,
+                                                                       "invalid assignment to data of entry with more than one component: list expected");
+                                       throw_error_already_set();
+                               }
+                       }
+               }
+       }
+};
+
+struct PythonContent : public Exif::Content
+{
+       typedef WrappedObjectIterator<PythonEntry, ExifEntry *> iterator;
+
+       PythonContent() {}
+       PythonContent(Exif::Content const &other) : Exif::Content(other) {}
+
+       PythonEntry entry(object index)
+       {
+               // TODO: use Exif::Content::entry() functions
+
+               extract<ExifTag> xtag(index);
+               if(xtag.check())
+               {
+                       ExifTag index= xtag();
+                       for(unsigned int i=0; i<size(); i++)
+                       {
+                               if(content_->entries[i]->tag == index)
+                                       return Exif::Entry(content_->entries[i]);
+                       }
+                       PyErr_SetString(PyExc_KeyError,
+                                                       "tag not present in IFD content");
+                       throw_error_already_set();
+               }
+               extract<int> xint(index);
+               if(xint.check())
+               {
+                       int index= xint();
+                       if((index>=0) && (index<(long)size()))
+                               return Exif::Entry(content_->entries[index]);
+                       if((index<0) && (index>=-(long)size()))
+                               return Exif::Entry(content_->entries[size()+index]);
+                       PyErr_SetString(PyExc_IndexError,
+                                                       "invalid integer index into IFD content");
+                       throw_error_already_set();
+               }
+               PyErr_SetString(PyExc_TypeError,
+                                               "invalid index into EXIF data (integer or IFD expected)");
+               throw_error_already_set();
+               return Exif::Entry(); // never reached
+       }
+
+       iterator __iter__()
+       {
+               // FIXME: the public API is exif_content_foreach,
+               // relying on memory layout here!
+               return iterator(content_->entries,
+                                               content_->entries + content_->count);
+       }
+};
+
+struct PythonData : public Exif::Data
+{
+       typedef WrappedObjectIterator<PythonContent, ExifContent *> iterator;
+       bool success_;
+
+       PythonData() {}
+       PythonData(const char *path)
+       : Exif::Data(path, &success_)
+       {
+               if(!success_)
+               {
+                       PyErr_SetFromErrno(PyExc_IOError);
+                       //PyErr_SetString(PyExc_IOError, "");
+                       throw_error_already_set();
+               }
+       }
+       PythonData(const unsigned char *data,
+                          unsigned int size) : Exif::Data(data, size) {}
+       PythonData(Exif::Data const &other) : Exif::Data(other) {}
+
+       PythonContent ifdContent(object index)
+       {
+               extract<ExifIfd> xifd(index);
+               if(xifd.check())
+               {
+                       ExifIfd index= xifd();
+                       if(index<EXIF_IFD_COUNT)
+                               return Exif::Content(data_->ifd[index]);
+                       PyErr_SetString(PyExc_IndexError,
+                                                       "invalid IFD index into EXIF data");
+                       throw_error_already_set();
+               }
+               extract<int> xint(index);
+               if(xint.check())
+               {
+                       int index= xint();
+                       if((index>=0) && (index<(long)size()))
+                               return Exif::Content(data_->ifd[index]);
+                       if((index<0) && (index>=-(long)size()))
+                               return Exif::Content(data_->ifd[size()+index]);
+                       PyErr_SetString(PyExc_IndexError,
+                                                       "invalid integer index into EXIF data");
+                       throw_error_already_set();
+               }
+               PyErr_SetString(PyExc_TypeError,
+                                               "invalid index into EXIF data (integer or IFD expected)");
+               throw_error_already_set();
+               return Exif::Content(); // never reached
+       }
+
+       iterator __iter__()
+       {
+               return iterator(data_->ifd, data_->ifd + EXIF_IFD_COUNT);
+       }
+};
+
+template<class Rational, class Component>
+struct RationalConverter
+{
+       RationalConverter()
+       {
+               converter::registry::insert(&convertible, &construct,
+                                                                       type_id<Rational>());
+       }
+
+    static void* convertible(PyObject* obj)
+    {
+               extract<tuple> xtup(obj);
+               if(xtup.check())
+               {
+                       tuple t= xtup();
+                       if((t.attr("__len__")() == 2) &&
+                          extract<Component>(t[0]).check() &&
+                          extract<Component>(t[1]).check())
+                       {
+                               Rational *result = new Rational;
+                               result->numerator =   extract<Component>(t[0])();
+                               result->denominator = extract<Component>(t[1])();
+                               return result;
+                       }
+               }
+               return NULL;
+    }
+
+    static void construct(PyObject* obj, converter::rvalue_from_python_stage1_data* data)
+    {
+               Rational const* r =
+                       static_cast<Rational*>(data->convertible);
+        void* storage =
+                       ((converter::rvalue_from_python_storage<Rational>*)data)->storage.bytes;
+        new (storage) Rational();
+               ((Rational*)storage)->numerator = r->numerator;
+               ((Rational*)storage)->denominator = r->denominator;
+        data->convertible = storage;
+               delete r;
+       }
+
+       static PyObject *convert(Rational r)
+       {
+               tuple t= make_tuple(r.numerator, r.denominator);
+               PyObject *result= t.ptr();
+               Py_INCREF(result);
+               return result;
+       }
+};
+
+BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(entrydumps, Exif::Entry::dump, 0, 1)
+BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(contentdumps, Exif::Content::dump, 0, 1)
+
+BOOST_PYTHON_MODULE(exif)
+{
+       RationalConverter<ExifRational, ExifLong>();
+       RationalConverter<ExifSRational, ExifSLong>();
+       to_python_converter<ExifRational,
+               RationalConverter<ExifRational, ExifLong> >();
+       to_python_converter<ExifSRational,
+               RationalConverter<ExifSRational, ExifSLong> >();
+
+       enum_<ExifByteOrder>("ByteOrder")
+               .value("MOTOROLA", EXIF_BYTE_ORDER_MOTOROLA)
+               .value("INTEL", EXIF_BYTE_ORDER_INTEL);
+
+       def("name", &exif_byte_order_get_name);
+
+       enum_<ExifIfd>("IFD")
+               .value("ZERO", EXIF_IFD_0)
+               .value("ONE", EXIF_IFD_1)
+               .value("EXIF", EXIF_IFD_EXIF)
+               .value("GPS", EXIF_IFD_GPS)
+               .value("INTEROPERABILITY", EXIF_IFD_INTEROPERABILITY);
+       //.value("COUNT", EXIF_IFD_COUNT)
+
+       def("name", &exif_ifd_get_name);
+
+       enum_<ExifFormat>("Format")
+               .value("BYTE", EXIF_FORMAT_BYTE)
+               .value("ASCII", EXIF_FORMAT_ASCII)
+               .value("SHORT", EXIF_FORMAT_SHORT)
+               .value("LONG", EXIF_FORMAT_LONG)
+               .value("RATIONAL", EXIF_FORMAT_RATIONAL)
+               .value("UNDEFINED", EXIF_FORMAT_UNDEFINED)
+               .value("SLONG", EXIF_FORMAT_SLONG)
+               .value("SRATIONAL", EXIF_FORMAT_SRATIONAL);
+
+       def("name", &exif_format_get_name);
+       def("size", &exif_format_get_size);
+
+       enum_<ExifTag>("Tag")
+               .value("INTEROPERABILITY_INDEX", EXIF_TAG_INTEROPERABILITY_INDEX)
+               .value("INTEROPERABILITY_VERSION", EXIF_TAG_INTEROPERABILITY_VERSION)
+               .value("IMAGE_WIDTH", EXIF_TAG_IMAGE_WIDTH)
+               .value("IMAGE_LENGTH", EXIF_TAG_IMAGE_LENGTH)
+               .value("BITS_PER_SAMPLE", EXIF_TAG_BITS_PER_SAMPLE)
+               .value("COMPRESSION", EXIF_TAG_COMPRESSION)
+               .value("PHOTOMETRIC_INTERPRETATION", EXIF_TAG_PHOTOMETRIC_INTERPRETATION)
+               .value("FILL_ORDER", EXIF_TAG_FILL_ORDER)
+               .value("DOCUMENT_NAME", EXIF_TAG_DOCUMENT_NAME)
+               .value("IMAGE_DESCRIPTION", EXIF_TAG_IMAGE_DESCRIPTION)
+               .value("MAKE", EXIF_TAG_MAKE)
+               .value("MODEL", EXIF_TAG_MODEL)
+               .value("STRIP_OFFSETS", EXIF_TAG_STRIP_OFFSETS)
+               .value("ORIENTATION", EXIF_TAG_ORIENTATION)
+               .value("SAMPLES_PER_PIXEL", EXIF_TAG_SAMPLES_PER_PIXEL)
+               .value("ROWS_PER_STRIP", EXIF_TAG_ROWS_PER_STRIP)
+               .value("STRIP_BYTE_COUNTS", EXIF_TAG_STRIP_BYTE_COUNTS)
+               .value("X_RESOLUTION", EXIF_TAG_X_RESOLUTION)
+               .value("Y_RESOLUTION", EXIF_TAG_Y_RESOLUTION)
+               .value("PLANAR_CONFIGURATION", EXIF_TAG_PLANAR_CONFIGURATION)
+               .value("RESOLUTION_UNIT", EXIF_TAG_RESOLUTION_UNIT)
+               .value("TRANSFER_FUNCTION", EXIF_TAG_TRANSFER_FUNCTION)
+               .value("SOFTWARE", EXIF_TAG_SOFTWARE)
+               .value("DATE_TIME", EXIF_TAG_DATE_TIME)
+               .value("ARTIST", EXIF_TAG_ARTIST)
+               .value("WHITE_POINT", EXIF_TAG_WHITE_POINT)
+               .value("PRIMARY_CHROMATICITIES", EXIF_TAG_PRIMARY_CHROMATICITIES)
+               .value("TRANSFER_RANGE", EXIF_TAG_TRANSFER_RANGE)
+               .value("JPEG_PROC", EXIF_TAG_JPEG_PROC)
+               .value("JPEG_INTERCHANGE_FORMAT", EXIF_TAG_JPEG_INTERCHANGE_FORMAT)
+               .value("JPEG_INTERCHANGE_FORMAT_LENGTH", EXIF_TAG_JPEG_INTERCHANGE_FORMAT_LENGTH)
+               .value("YCBCR_COEFFICIENTS", EXIF_TAG_YCBCR_COEFFICIENTS)
+               .value("YCBCR_SUB_SAMPLING", EXIF_TAG_YCBCR_SUB_SAMPLING)
+               .value("YCBCR_POSITIONING", EXIF_TAG_YCBCR_POSITIONING)
+               .value("REFERENCE_BLACK_WHITE", EXIF_TAG_REFERENCE_BLACK_WHITE)
+               .value("RELATED_IMAGE_FILE_FORMAT", EXIF_TAG_RELATED_IMAGE_FILE_FORMAT)
+               .value("RELATED_IMAGE_WIDTH", EXIF_TAG_RELATED_IMAGE_WIDTH)
+               .value("RELATED_IMAGE_LENGTH", EXIF_TAG_RELATED_IMAGE_LENGTH)
+               .value("CFA_REPEAT_PATTERN_DIM", EXIF_TAG_CFA_REPEAT_PATTERN_DIM)
+               .value("CFA_PATTERN", EXIF_TAG_CFA_PATTERN)
+               .value("BATTERY_LEVEL", EXIF_TAG_BATTERY_LEVEL)
+               .value("COPYRIGHT", EXIF_TAG_COPYRIGHT)
+               .value("EXPOSURE_TIME", EXIF_TAG_EXPOSURE_TIME)
+               .value("FNUMBER", EXIF_TAG_FNUMBER)
+               .value("IPTC_NAA", EXIF_TAG_IPTC_NAA)
+               .value("EXIF_IFD_POINTER", EXIF_TAG_EXIF_IFD_POINTER)
+               .value("INTER_COLOR_PROFILE", EXIF_TAG_INTER_COLOR_PROFILE)
+               .value("EXPOSURE_PROGRAM", EXIF_TAG_EXPOSURE_PROGRAM)
+               .value("SPECTRAL_SENSITIVITY", EXIF_TAG_SPECTRAL_SENSITIVITY)
+               .value("GPS_INFO_IFD_POINTER", EXIF_TAG_GPS_INFO_IFD_POINTER)
+               .value("ISO_SPEED_RATINGS", EXIF_TAG_ISO_SPEED_RATINGS)
+               .value("OECF", EXIF_TAG_OECF)
+               .value("EXIF_VERSION", EXIF_TAG_EXIF_VERSION)
+               .value("DATE_TIME_ORIGINAL", EXIF_TAG_DATE_TIME_ORIGINAL)
+               .value("DATE_TIME_DIGITIZED", EXIF_TAG_DATE_TIME_DIGITIZED)
+               .value("COMPONENTS_CONFIGURATION", EXIF_TAG_COMPONENTS_CONFIGURATION)
+               .value("COMPRESSED_BITS_PER_PIXEL", EXIF_TAG_COMPRESSED_BITS_PER_PIXEL)
+               .value("SHUTTER_SPEED_VALUE", EXIF_TAG_SHUTTER_SPEED_VALUE)
+               .value("APERTURE_VALUE", EXIF_TAG_APERTURE_VALUE)
+               .value("BRIGHTNESS_VALUE", EXIF_TAG_BRIGHTNESS_VALUE)
+               .value("EXPOSURE_BIAS_VALUE", EXIF_TAG_EXPOSURE_BIAS_VALUE)
+               .value("MAX_APERTURE_VALUE", EXIF_TAG_MAX_APERTURE_VALUE)
+               .value("SUBJECT_DISTANCE", EXIF_TAG_SUBJECT_DISTANCE)
+               .value("METERING_MODE", EXIF_TAG_METERING_MODE)
+               .value("LIGHT_SOURCE", EXIF_TAG_LIGHT_SOURCE)
+               .value("FLASH", EXIF_TAG_FLASH)
+               .value("FOCAL_LENGTH", EXIF_TAG_FOCAL_LENGTH)
+               .value("SUBJECT_AREA", EXIF_TAG_SUBJECT_AREA)
+               .value("MAKER_NOTE", EXIF_TAG_MAKER_NOTE)
+               .value("USER_COMMENT", EXIF_TAG_USER_COMMENT)
+               .value("SUBSEC_TIME", EXIF_TAG_SUBSEC_TIME)
+               .value("SUB_SEC_TIME_ORIGINAL", EXIF_TAG_SUB_SEC_TIME_ORIGINAL)
+               .value("SUB_SEC_TIME_DIGITIZED", EXIF_TAG_SUB_SEC_TIME_DIGITIZED)
+               .value("FLASH_PIX_VERSION", EXIF_TAG_FLASH_PIX_VERSION)
+               .value("COLOR_SPACE", EXIF_TAG_COLOR_SPACE)
+               .value("PIXEL_X_DIMENSION", EXIF_TAG_PIXEL_X_DIMENSION)
+               .value("PIXEL_Y_DIMENSION", EXIF_TAG_PIXEL_Y_DIMENSION)
+               .value("RELATED_SOUND_FILE", EXIF_TAG_RELATED_SOUND_FILE)
+               .value("INTEROPERABILITY_IFD_POINTER", EXIF_TAG_INTEROPERABILITY_IFD_POINTER)
+               .value("FLASH_ENERGY", EXIF_TAG_FLASH_ENERGY)
+               .value("SPATIAL_FREQUENCY_RESPONSE", EXIF_TAG_SPATIAL_FREQUENCY_RESPONSE)
+               .value("FOCAL_PLANE_X_RESOLUTION", EXIF_TAG_FOCAL_PLANE_X_RESOLUTION)
+               .value("FOCAL_PLANE_Y_RESOLUTION", EXIF_TAG_FOCAL_PLANE_Y_RESOLUTION)
+               .value("FOCAL_PLANE_RESOLUTION_UNIT", EXIF_TAG_FOCAL_PLANE_RESOLUTION_UNIT)
+               .value("SUBJECT_LOCATION", EXIF_TAG_SUBJECT_LOCATION)
+               .value("EXPOSURE_INDEX", EXIF_TAG_EXPOSURE_INDEX)
+               .value("SENSING_METHOD", EXIF_TAG_SENSING_METHOD)
+               .value("FILE_SOURCE", EXIF_TAG_FILE_SOURCE)
+               .value("SCENE_TYPE", EXIF_TAG_SCENE_TYPE)
+               .value("NEW_CFA_PATTERN", EXIF_TAG_NEW_CFA_PATTERN)
+               .value("CUSTOM_RENDERED", EXIF_TAG_CUSTOM_RENDERED)
+               .value("EXPOSURE_MODE", EXIF_TAG_EXPOSURE_MODE)
+               .value("WHITE_BALANCE", EXIF_TAG_WHITE_BALANCE)
+               .value("DIGITAL_ZOOM_RATIO", EXIF_TAG_DIGITAL_ZOOM_RATIO)
+               .value("FOCAL_LENGTH_IN_35MM_FILM", EXIF_TAG_FOCAL_LENGTH_IN_35MM_FILM)
+               .value("SCENE_CAPTURE_TYPE", EXIF_TAG_SCENE_CAPTURE_TYPE)
+               .value("GAIN_CONTROL", EXIF_TAG_GAIN_CONTROL)
+               .value("CONTRAST", EXIF_TAG_CONTRAST)
+               .value("SATURATION", EXIF_TAG_SATURATION)
+               .value("SHARPNESS", EXIF_TAG_SHARPNESS)
+               .value("DEVICE_SETTING_DESCRIPTION", EXIF_TAG_DEVICE_SETTING_DESCRIPTION)
+               .value("SUBJECT_DISTANCE_RANGE", EXIF_TAG_SUBJECT_DISTANCE_RANGE)
+               .value("IMAGE_UNIQUE_ID", EXIF_TAG_IMAGE_UNIQUE_ID);
+
+       def("name", &exif_tag_get_name);
+       def("title", &exif_tag_get_title);
+       def("description", &exif_tag_get_description);
+
+       class_<PythonEntry>("Entry")
+               .add_property("tag", &Exif::Entry::tag)
+               .add_property("format", &Exif::Entry::format)
+               .add_property("components", &Exif::Entry::components)
+               .add_property("data", &PythonEntry::data,
+                                         &PythonEntry::setData)
+               .def("value", &Exif::Entry::value)
+               .def("briefValue", &Exif::Entry::briefValue)
+               .def("dump", &Exif::Entry::dump);//, entrydumps());
+
+       class_<PythonContent::iterator>("ContentIterator", no_init)
+               .def("next", &PythonContent::iterator::next);
+       class_<PythonContent>("Content")
+               .def("__len__", &Exif::Content::size)
+               .def("__getitem__", &PythonContent::entry)
+               .def("__iter__", &PythonContent::__iter__)
+               .def("dump", &Exif::Content::dump);//, contentdumps());
+
+       class_<PythonData::iterator>("DataIterator", no_init)
+               .def("next", &PythonData::iterator::next);
+       class_<PythonData>("Data")
+               .def(init<const char *>())
+               .def(init<const unsigned char *, unsigned int>())
+               .def("__len__", &Exif::Data::size)
+               .def("__getitem__", &PythonData::ifdContent)
+               .def("__iter__", &PythonData::__iter__)
+               .def("byteOrder", &Exif::Data::byteOrder)
+               .def("dump", &Exif::Data::dump);
+}