8c828cbde12e2273149fcbda547f1c03f1b755e6
 app/Makefile.am                                |   4 +-
 app/actions/file-actions.c                     |   7 +
 app/actions/file-commands.c                    |  17 +
 app/actions/file-commands.h                    |   2 +
 app/core/Makefile.am                           |   5 +-
 app/core/gimpimage-metadata.c                  | 544 +++++++++++++++
 app/core/gimpimage-metadata.h                  |  50 ++
 app/core/gimpimage-private.h                   |   1 +
 app/core/gimpimage.c                           |  87 ++-
 app/core/gimpimage.h                           |   5 +
 app/dialogs/Makefile.am                        |   2 +
 app/dialogs/metadata-dialog.c                  | 256 +++++++
 app/dialogs/metadata-dialog.h                  |  80 +++
 app/file/Makefile.am                           |   4 +-
 app/file/file-open.c                           | 162 ++++-
 app/file/file-save.c                           | 327 +++++++++
 app/pdb/Makefile.am                            |   1 +
 app/pdb/metadata-cmds.c                        | 163 +++++
 app/ui/Makefile.am                             |   6 +
 app/ui/metadata.ui                             | 895 +++++++++++++++++++++++++
 app/widgets/gimphelp-ids.h                     |   2 +
 app/xcf/xcf-load.c                             |  17 +
 app/xcf/xcf-save.c                             |  15 +
 configure.ac                                   |  30 +-
 libgimp/Makefile.am                            |  13 +-
 libgimp/gimp.def                               |   2 +
 libgimp/gimp.h                                 |   1 +
 libgimp/gimp_pdb_headers.h                     |   1 +
 libgimp/gimpmetadata.c                         | 223 ++++++
 libgimp/gimpmetadata.h                         |  51 ++
 libgimp/gimpmetadata_pdb.c                     |  97 +++
 libgimp/gimpmetadata_pdb.h                     |  42 ++
 libgimpbase/Makefile.am                        |   5 +-
 libgimpbase/gimpbase.def                       |   1 +
 libgimpbase/gimpbase.h                         |   1 +
 libgimpbase/gimpwilber.c                       |  67 ++
 libgimpbase/gimpwilber.h                       |  31 +
 menus/image-menu.xml.in                        |   1 +
 plug-ins/Makefile.am                           |   5 -
 plug-ins/color-rotate/color-rotate-callbacks.c | 184 ++---
 plug-ins/color-rotate/color-rotate-dialog.c    |  68 +-
 plug-ins/color-rotate/color-rotate-utils.c     |  28 +-
 plug-ins/color-rotate/color-rotate.c           |  36 +-
 plug-ins/color-rotate/color-rotate.h           |   2 +-
 plug-ins/common/file-png.c                     | 142 +++-
 plug-ins/common/file-tiff-save.c               | 200 ++++--
 plug-ins/file-jpeg/Makefile.am                 |  13 -
 plug-ins/file-jpeg/gimpexif.c                  | 160 -----
 plug-ins/file-jpeg/gimpexif.h                  |  39 --
 plug-ins/file-jpeg/jpeg-exif.c                 | 458 -------------
 plug-ins/file-jpeg/jpeg-exif.h                 |  41 --
 plug-ins/file-jpeg/jpeg-load.c                 | 495 --------------
 plug-ins/file-jpeg/jpeg-load.h                 |  11 -
 plug-ins/file-jpeg/jpeg-save.c                 | 423 +++---------
 plug-ins/file-jpeg/jpeg-save.h                 |   6 +
 plug-ins/file-jpeg/jpeg-settings.c             |   4 -
 plug-ins/file-jpeg/jpeg.c                      | 123 +---
 plug-ins/file-jpeg/jpeg.h                      |   8 +
 plug-ins/file-psd/psd-image-res-load.c         | 131 ----
 plug-ins/ui/Makefile.am                        |   3 +-
 plug-ins/ui/plug-in-file-png.ui                | 106 ++-
 plug-ins/ui/plug-in-file-tiff.ui               | 404 +++++++++++
 tools/pdbgen/Makefile.am                       |   1 +
 tools/pdbgen/pdb/metadata.pdb                  |  85 +++
 64 files changed, 4350 insertions(+), 2044 deletions(-)

diff --git a/app/Makefile.am b/app/Makefile.am
index e559433..25b9c94 100644
--- a/app/Makefile.am
+++ b/app/Makefile.am
@@ -30,6 +30,7 @@ SUBDIRS = \
 	actions		\
 	menus		\
 	gui		\
+	ui		\
 	.		\
 	tests
 
@@ -157,8 +158,8 @@ gimpconsoleldadd = \
 	pdb/libapppdb.a			\
 	plug-in/libappplug-in.a		\
 	vectors/libappvectors.a		\
-	core/libappcore.a		\
 	file/libappfile.a		\
+	core/libappcore.a		\
 	text/libapptext.a		\
 	paint/libapppaint.a		\
 	operations/libappoperations.a	\
@@ -177,6 +178,7 @@ gimpconsoleldadd = \
 	$(CAIRO_LIBS)			\
 	$(GEGL_LIBS)			\
 	$(GLIB_LIBS)			\
+  $(GEXIV2_LIBS)	\
 	$(INTLLIBS)			\
 	$(RT_LIBS)
 
diff --git a/app/actions/file-actions.c b/app/actions/file-actions.c
index 45185a2..0c1c5d3 100644
--- a/app/actions/file-actions.c
+++ b/app/actions/file-actions.c
@@ -108,6 +108,12 @@ static const GimpActionEntry file_actions[] =
     G_CALLBACK (file_close_all_cmd_callback),
     GIMP_HELP_FILE_CLOSE_ALL },
 
+  { "file-show-metadata", NULL,
+    NC_("file-action", "Show Metadata"), NULL,
+    NC_("file-action", "Show metadata of image"),
+    G_CALLBACK (file_show_metadata_cmd_callback),
+    GIMP_HELP_FILE_SHOW_METADATA },
+
   { "file-quit", GTK_STOCK_QUIT,
     NC_("file-action", "_Quit"), "<primary>Q",
     NC_("file-action", "Quit the GNU Image Manipulation Program"),
@@ -280,6 +286,7 @@ file_actions_update (GimpActionGroup *group,
   SET_SENSITIVE ("file-save-as",         drawable);
   SET_SENSITIVE ("file-save-a-copy",     drawable);
   SET_SENSITIVE ("file-revert",          image && (gimp_image_get_uri (image) || source));
+  SET_SENSITIVE ("file-show-metadata",   image);
   SET_SENSITIVE ("file-export-to",       drawable);
   SET_VISIBLE   ("file-export-to",       ! show_overwrite);
   SET_SENSITIVE ("file-overwrite",       show_overwrite);
diff --git a/app/actions/file-commands.c b/app/actions/file-commands.c
index 9ca2f62..eb46543 100644
--- a/app/actions/file-commands.c
+++ b/app/actions/file-commands.c
@@ -53,6 +53,7 @@
 #include "display/gimpdisplay-foreach.h"
 
 #include "dialogs/file-save-dialog.h"
+#include "dialogs/metadata-dialog.h"
 
 #include "actions.h"
 #include "file-commands.h"
@@ -478,6 +479,22 @@ file_close_all_cmd_callback (GtkAction *action,
 }
 
 void
+file_show_metadata_cmd_callback (GtkAction *action,
+                                 gpointer   data)
+{
+  Gimp        *gimp;
+  GtkWidget   *widget;
+  GimpImage   *image;
+  return_if_no_gimp (gimp, data);
+  return_if_no_widget (widget, data);
+
+  image = action_data_get_image (data);
+
+  metadata_dialog_show (gimp, image);
+
+}
+
+void
 file_quit_cmd_callback (GtkAction *action,
                         gpointer   data)
 {
diff --git a/app/actions/file-commands.h b/app/actions/file-commands.h
index 95ef997..b6b93de 100644
--- a/app/actions/file-commands.h
+++ b/app/actions/file-commands.h
@@ -37,6 +37,8 @@ void   file_create_template_cmd_callback (GtkAction   *action,
 
 void   file_revert_cmd_callback          (GtkAction   *action,
                                           gpointer     data);
+void   file_show_metadata_cmd_callback   (GtkAction   *action,
+                                          gpointer     data);
 void   file_close_all_cmd_callback       (GtkAction   *action,
                                           gpointer     data);
 void   file_quit_cmd_callback            (GtkAction   *action,
diff --git a/app/core/Makefile.am b/app/core/Makefile.am
index df5435b..7137427 100644
--- a/app/core/Makefile.am
+++ b/app/core/Makefile.am
@@ -10,7 +10,8 @@ AM_CPPFLAGS = \
 	$(CAIRO_CFLAGS)					\
 	$(GEGL_CFLAGS)					\
 	$(GDK_PIXBUF_CFLAGS)				\
-	-I$(includedir)
+	-I$(includedir)		\
+  $(GEXIV2_CFLAGS)
 
 noinst_LIBRARIES = libappcore.a
 
@@ -219,6 +220,8 @@ libappcore_a_sources = \
 	gimpimage-item-list.h			\
 	gimpimage-merge.c			\
 	gimpimage-merge.h			\
+  gimpimage-metadata.c	\
+  gimpimage-metadata.h	\
 	gimpimage-new.c				\
 	gimpimage-new.h				\
 	gimpimage-pick-color.c			\
diff --git a/app/core/gimpimage-metadata.c b/app/core/gimpimage-metadata.c
new file mode 100644
index 0000000..d203cb0
--- /dev/null
+++ b/app/core/gimpimage-metadata.c
@@ -0,0 +1,544 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <glib-object.h>
+#include <glib/gprintf.h>
+
+#include "libgimpbase/gimpbase.h"
+
+#include "gimpimage-metadata.h"
+
+static const gchar *tiff_tags[] =
+  {
+    "Xmp.tiff",
+    "Exif.Image.ImageWidth",
+    "Exif.Image.ImageLength",
+    "Exif.Image.BitsPerSample",
+    "Exif.Image.Compression",
+    "Exif.Image.PhotometricInterpretation",
+    "Exif.Image.FillOrder",
+    "Exif.Image.SamplesPerPixel",
+    "Exif.Image.StripOffsets",
+    "Exif.Image.RowsPerStrip",
+    "Exif.Image.StripByteCounts",
+    "Exif.Image.PlanarConfiguration"
+  };
+
+static const gchar *jpeg_tags[] =
+  {
+    "Exif.Image.JPEGProc",
+    "Exif.Image.JPEGInterchangeFormat",
+    "Exif.Image.JPEGInterchangeFormatLength",
+    "Exif.Image.JPEGRestartInterval",
+    "Exif.Image.JPEGLosslessPredictors",
+    "Exif.Image.JPEGPointTransforms",
+    "Exif.Image.JPEGQTables",
+    "Exif.Image.JPEGDCTables",
+    "Exif.Image.JPEGACTables"
+  };
+
+static const gchar *unsupported_tags[] =
+  {
+      "Exif.Image.SubIFDs",
+      "Exif.Image.ClipPath",
+      "Exif.Image.XClipPathUnits",
+      "Exif.Image.YClipPathUnits",
+      "Xmp.xmpMM.History",
+      "Exif.Image.XPTitle",
+      "Exif.Image.XPComment",
+      "Exif.Image.XPAuthor",
+      "Exif.Image.XPKeywords",
+      "Exif.Image.XPSubject",
+      "Exif.Image.DNGVersion",
+      "Exif.Image.DNGBackwardVersion",
+      "Exif.Iop"
+  };
+
+gint                   gimp_image_metadata_length           (gchar        *testline,
+                                                             gchar        *delim);
+
+gboolean               gimp_image_metadata_get_rational     (gchar        *value,
+                                                             gint          sections,
+                                                             gchar      ***numerator,
+                                                             gchar      ***denominator);
+
+/* public functions */
+
+/*
+ * reads metadata from a physical file
+ */
+GExiv2Metadata  *
+gimp_image_metadata_get_from_file (gchar *filename, GError **error)
+{
+  GExiv2Metadata  *meta;
+  gchar           *loadfile;
+
+#ifdef G_OS_WIN32
+  loadfile = g_win32_locale_filename_from_utf8 (filename);
+#else
+  loadfile = filename;
+#endif
+
+  if ( gexiv2_initialize ())
+    {
+      g_clear_error (error);
+      meta = gexiv2_metadata_new ();
+
+      if (! gexiv2_metadata_open_path (meta, loadfile, error))
+        {
+          return NULL;
+        }
+    }
+  return meta;
+}
+
+/*
+ * saves metadata to a physical file
+ */
+void
+gimp_image_metadata_save_to_file (gchar          *filename,
+                                  GExiv2Metadata *metadata,
+                                  GError        **error)
+{
+  gchar *savefile;
+
+  #ifdef G_OS_WIN32
+    savefile = g_win32_locale_filename_from_utf8 (filename);
+  #else
+    savefile = filename;
+  #endif
+
+  if(metadata && filename)
+    {
+      g_clear_error (error);
+      gexiv2_metadata_save_file (metadata, savefile, error);
+    }
+}
+
+/**
+ * Deserializes metadata from a string using a
+ * jpg-binary
+ **/
+GExiv2Metadata *
+gimp_image_metadata_deserialize (gchar *metadata_string)
+{
+  GError         **error = NULL;
+  GExiv2Metadata  *metadata;
+  GString         *buffer;
+  gint             i;
+  gint             j;
+  gchar          **meta_info = NULL;
+  gchar          **tag_info = NULL;
+  guint            size;
+  const guint8    *wilber;
+  gint             num;
+  gchar           *value;
+
+  if ( gexiv2_initialize ())
+    {
+      metadata = gexiv2_metadata_new ();
+
+      wilber = gimp_wilber_get_data (&size);
+
+      if (! gexiv2_metadata_open_buf (metadata, wilber, size, error))
+        {
+#ifdef GIMP_CONFIG_DEBUG
+          g_printerr ( "Error occured while loading metadata!\n" );
+          g_printerr ( "Message: %s\n", error->message );
+#endif
+          return NULL;
+        }
+    }
+
+  if (metadata && metadata_string)
+    {
+
+      meta_info = g_strsplit (metadata_string, TAG_LINE_DELIMITER, -1);
+
+      i=0;
+
+      while (meta_info[i] != NULL)
+        {
+          num = gimp_image_metadata_length (meta_info[i], TAG_TAG_DELIMITER);
+          tag_info = g_strsplit (meta_info[i], TAG_TAG_DELIMITER, -1);
+          if (num > 0)
+            {
+              if (num > 1)
+                {
+                  buffer = g_string_new (NULL);
+                  for (j = 1; j < (num + 1); j++)
+                    {
+                      g_string_append_printf (buffer, "%s%s", tag_info[j], TAG_TAG_DELIMITER); /* recreates value */
+                    }
+                  value = g_strndup (buffer->str, buffer->len - 1);  /* to avoid the trailing '#' */
+                  g_string_free (buffer, TRUE);
+                }
+              else
+                {
+                  value = tag_info[1];
+                }
+
+              gexiv2_metadata_set_tag_string (metadata, tag_info[0], value);  /* setting value to metadata */
+            }
+          i++;
+        }
+    }
+
+  if (meta_info)
+    g_strfreev (meta_info);
+
+  if (tag_info)
+    g_strfreev (tag_info);
+
+  return metadata;
+
+}
+
+/**
+ * Serializing metadata as a string
+ */
+gchar *
+gimp_image_metadata_serialize (GExiv2Metadata *metadata)
+{
+  gchar              **exif_data = NULL;
+  gchar              **iptc_data = NULL;
+  gchar              **xmp_data = NULL;
+  gchar               *value;
+  gint                 i;
+  gint                 n;
+  GString             *metadata_buffer;
+  const gchar         *metadata_string = NULL;
+
+  n = 0;
+
+  if (metadata)
+    {
+      metadata_buffer = g_string_new (NULL);
+
+      exif_data = gexiv2_metadata_get_exif_tags (metadata);
+
+      i = 0;
+      while (exif_data[i] != NULL)
+        {
+          value = gexiv2_metadata_get_tag_string (metadata, exif_data[i]);
+          g_string_append_printf (metadata_buffer, "%s%s%s%s", exif_data[i],
+                                  TAG_TAG_DELIMITER,
+                                  value,
+                                  TAG_LINE_DELIMITER);
+          n++;
+          i++;
+        }
+
+      if(exif_data)
+        g_strfreev (exif_data);
+
+      xmp_data = gexiv2_metadata_get_xmp_tags (metadata);
+
+      i = 0;
+      while (xmp_data[i] != NULL)
+        {
+          value = gexiv2_metadata_get_tag_string (metadata, xmp_data[i]);
+          g_string_append_printf (metadata_buffer, "%s%s%s%s", xmp_data[i],
+                                  TAG_TAG_DELIMITER,
+                                  value,
+                                  TAG_LINE_DELIMITER);
+
+          n++;
+          i++;
+        }
+
+      if(xmp_data)
+        g_strfreev (xmp_data);
+
+      iptc_data = gexiv2_metadata_get_iptc_tags (metadata);
+
+      i = 0;
+      while (iptc_data[i] != NULL)
+        {
+          value = gexiv2_metadata_get_tag_string (metadata, iptc_data[i]);
+          g_string_append_printf (metadata_buffer, "%s%s%s%s", iptc_data[i],
+                                  TAG_TAG_DELIMITER,
+                                  value,
+                                  TAG_LINE_DELIMITER);
+
+          n++;
+          i++;
+        }
+
+      if(iptc_data)
+        g_strfreev (iptc_data);
+
+      if (metadata_buffer->len > 1)
+        metadata_string = g_strndup (metadata_buffer->str, metadata_buffer->len - 1);
+      else
+        metadata_string = g_strdup (metadata_buffer->str);
+
+      g_string_free (metadata_buffer, TRUE);
+
+      return metadata_string;
+    }
+
+  return NULL;
+}
+
+/**
+ * gets exif resolution
+ */
+gboolean
+gimp_image_metadata_get_resolution (GExiv2Metadata *metadata, gdouble *xres, gdouble *yres, gint *unit)
+{
+  gchar *xr;
+  gchar *yr;
+  gchar *un;
+  gchar *r;
+  gdouble x1, x2;
+  gdouble y1, y2;
+
+  gdouble xrd;
+  gdouble yrd;
+
+
+  gint    u;
+  gchar **xnom = NULL;
+  gchar **xdenom = NULL;
+  gchar **ynom = NULL;
+  gchar **ydenom = NULL;
+
+  xr = gexiv2_metadata_get_tag_string (metadata, "Exif.Image.XResolution");
+  yr = gexiv2_metadata_get_tag_string (metadata, "Exif.Image.YResolution");
+  un = gexiv2_metadata_get_tag_string (metadata, "Exif.Image.ResolutionUnit");
+
+  if (gimp_image_metadata_get_rational (xr, 1, &xnom, &xdenom))
+    {
+      x1 = g_ascii_strtod (xnom[0], &r);
+      x2 = g_ascii_strtod (xdenom[0], &r);
+
+      if (x2 == 0.0)
+        return FALSE;
+
+      xrd = x1 / x2;
+      u = atoi (un);
+      switch (u)
+      {
+        case 2:
+          break;
+        case 3:
+          xrd *= 2.54;
+          break;
+        default:
+          break;
+      }
+
+      *xres = xrd;
+
+      *unit = u;
+    }
+
+  if (gimp_image_metadata_get_rational (yr, 1, &ynom, &ydenom))
+    {
+      y1 = g_ascii_strtod (ynom[0], &r);
+      y2 = g_ascii_strtod (ydenom[0], &r);
+
+      if (y2 == 0.0)
+        return FALSE;
+
+      yrd = y1 / y2;
+
+      switch (u)
+      {
+        case 2:
+          break;
+        case 3:
+          yrd *= 2.54;
+          break;
+        default:
+          break;
+      }
+
+      *yres = yrd;
+    }
+
+  g_strfreev (xnom);
+  g_strfreev (xdenom);
+  g_strfreev (ynom);
+  g_strfreev (ydenom);
+
+  return TRUE;
+
+}
+
+/**
+ * sets exif resolution
+ */
+void
+gimp_image_metadata_set_resolution (GExiv2Metadata *metadata, gdouble xres, gdouble yres, gint unit)
+{
+  gchar *str = NULL;
+
+  str = g_strdup_printf ("%.0f/1", xres);
+  gexiv2_metadata_set_tag_string (metadata, "Exif.Image.XResolution", str);
+  str = g_strdup_printf ("%.0f/1", yres);
+  gexiv2_metadata_set_tag_string (metadata, "Exif.Image.YResolution", str);
+  str = g_strdup_printf ("%d", unit);
+  gexiv2_metadata_set_tag_string (metadata, "Exif.Image.ResolutionUnit", str);
+
+}
+
+/**
+ * checks for supported tags
+ */
+gboolean
+gimp_image_metadata_is_tag_supported (gchar *tag, gchar *mime_type)
+{
+  gint j;
+
+  for (j = 0; j < G_N_ELEMENTS (unsupported_tags); j++)
+    {
+      if (g_str_has_prefix (tag, unsupported_tags[j]))
+        {
+          return FALSE;
+        }
+    }
+
+  if (!g_strcmp0 (mime_type, "image/jpeg"))
+    {
+      for (j = 0; j < G_N_ELEMENTS (tiff_tags); j++)
+        {
+          if (g_str_has_prefix (tag, tiff_tags[j]))
+            {
+              return FALSE;
+            }
+        }
+    }
+  else if (!g_strcmp0 (mime_type, "image/tiff"))
+    {
+      for (j = 0; j < G_N_ELEMENTS (jpeg_tags); j++)
+        {
+          if (g_str_has_prefix (tag, jpeg_tags[j]))
+            {
+              return FALSE;
+            }
+        }
+    }
+
+  return TRUE;
+
+}
+
+
+/* private functions */
+
+/**
+ * determines the amound of delimiters in serialized
+ * metadata string
+ */
+gint
+gimp_image_metadata_length (gchar *testline, gchar *delim)
+{
+  gchar     *delim_test;
+  gint       i;
+  gint       sum;
+
+  delim_test = g_strdup (testline);
+
+  sum =0;
+
+  for (i=0; i < strlen (delim_test); i++)
+    {
+      if (delim_test[i] == delim[0])
+        sum++;
+    }
+
+  g_free (delim_test);
+  return sum;
+}
+
+/**
+ * gets rational values from string
+ */
+gboolean
+gimp_image_metadata_get_rational (gchar *value, gint sections, gchar ***numerator, gchar ***denominator)
+{
+  GSList *nomlist = NULL;
+  GSList *denomlist = NULL;
+
+  GSList *nlist, *dlist;
+
+  gchar   sect[] = " ";
+  gchar   rdel[] = "/";
+  gchar **sects;
+  gchar **nom;
+  gint    i;
+  gint    n;
+
+  gchar **num;
+  gchar **den;
+
+  if (!value)
+    return FALSE;
+
+  if(gimp_image_metadata_length (value, sect) == (sections -1))
+    {
+      i = 0;
+      sects = g_strsplit (value, sect, -1);
+      while (sects[i] != NULL)
+        {
+          if(gimp_image_metadata_length (sects[i], rdel) == 1)
+            {
+              nom = g_strsplit (sects[i], rdel, -1);
+              nomlist = g_slist_prepend (nomlist, g_strdup (nom[0]));
+              denomlist = g_slist_prepend (denomlist, g_strdup (nom[1]));
+            }
+          else
+            {
+              return FALSE;
+            }
+          i++;
+        }
+    }
+  else
+    {
+      return FALSE;
+    }
+
+  n = i;
+
+  num = g_new0 (gchar*, i + 1);
+  den = g_new0 (gchar*, n + 1);
+
+  for (nlist = nomlist; nlist; nlist = nlist->next)
+    num[--i] = nlist->data;
+
+  for (dlist = denomlist; dlist; dlist = dlist->next)
+    den[--n] = dlist->data;
+
+  *numerator = num;
+  *denominator = den;
+
+  g_slist_free (nomlist);
+  g_slist_free (denomlist);
+
+  g_strfreev (sects);
+  g_strfreev (nom);
+
+  return TRUE;
+}
diff --git a/app/core/gimpimage-metadata.h b/app/core/gimpimage-metadata.h
new file mode 100644
index 0000000..0f36c98
--- /dev/null
+++ b/app/core/gimpimage-metadata.h
@@ -0,0 +1,50 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef METADATA_H_
+#define METADATA_H_
+
+#include <gexiv2/gexiv2.h>
+
+#define TAG_LINE_DELIMITER "\v"
+#define TAG_TAG_DELIMITER  "#"
+
+GExiv2Metadata  *gimp_image_metadata_get_from_file       (gchar            *filename,
+                                                          GError          **error);
+
+void             gimp_image_metadata_save_to_file        (gchar            *filename,
+                                                          GExiv2Metadata   *metadata,
+                                                          GError          **error);
+
+GExiv2Metadata  *gimp_image_metadata_deserialize         (gchar            *metadata_string);
+
+gchar           *gimp_image_metadata_serialize           (GExiv2Metadata   *metadata);
+
+gboolean         gimp_image_metadata_get_resolution      (GExiv2Metadata   *metadata,
+                                                          gdouble          *xres,
+                                                          gdouble          *yres,
+                                                          gint             *unit);
+
+void             gimp_image_metadata_set_resolution      (GExiv2Metadata   *metadata,
+                                                          gdouble           xres,
+                                                          gdouble           yres,
+                                                          gint              unit);
+
+gboolean         gimp_image_metadata_is_tag_supported    (gchar            *tag,
+                                                          gchar            *mime_type);
+
+#endif /* METADATA_H_ */
diff --git a/app/core/gimpimage-private.h b/app/core/gimpimage-private.h
index 5dee180..a1be220 100644
--- a/app/core/gimpimage-private.h
+++ b/app/core/gimpimage-private.h
@@ -48,6 +48,7 @@ struct _GimpImagePrivate
   GimpUnit           resolution_unit;       /*  resolution unit              */
   GimpImageBaseType  base_type;             /*  base gimp_image type         */
   GimpPrecision      precision;             /*  image's precision            */
+  GExiv2Metadata    *metadata;              /*  images metadata              */
 
   guchar            *colormap;              /*  colormap (for indexed)       */
   gint               n_colors;              /*  # of colors (for indexed)    */
diff --git a/app/core/gimpimage.c b/app/core/gimpimage.c
index 544cf4e..e667642 100644
--- a/app/core/gimpimage.c
+++ b/app/core/gimpimage.c
@@ -130,7 +130,8 @@ enum
   PROP_HEIGHT,
   PROP_BASE_TYPE,
   PROP_PRECISION,
-  PROP_BUFFER
+  PROP_BUFFER,
+  PROP_METADATA
 };
 
 
@@ -579,6 +580,12 @@ gimp_image_class_init (GimpImageClass *klass)
                                                         GIMP_PARAM_READWRITE |
                                                         G_PARAM_CONSTRUCT_ONLY));
 
+  g_object_class_install_property (object_class, PROP_METADATA,
+                                   g_param_spec_object ("metadata", NULL, NULL,
+                                                        GEXIV2_TYPE_METADATA,
+                                                        GIMP_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT));
+
   g_object_class_install_property (object_class, PROP_ID,
                                    g_param_spec_int ("id", NULL, NULL,
                                                      0, G_MAXINT, 0,
@@ -662,6 +669,7 @@ gimp_image_init (GimpImage *image)
   private->resolution_unit     = GIMP_UNIT_INCH;
   private->base_type           = GIMP_RGB;
   private->precision           = GIMP_PRECISION_U8_GAMMA;
+  private->metadata            = NULL;
 
   private->colormap            = NULL;
   private->n_colors            = 0;
@@ -822,6 +830,9 @@ gimp_image_set_property (GObject      *object,
     case PROP_GIMP:
       image->gimp = g_value_get_object (value);
       break;
+    case PROP_METADATA:
+      private->metadata = (GExiv2Metadata *)g_value_get_object (value);
+      break;
     case PROP_ID:
       g_assert_not_reached ();
       break;
@@ -858,6 +869,9 @@ gimp_image_get_property (GObject    *object,
     case PROP_GIMP:
       g_value_set_object (value, image->gimp);
       break;
+    case PROP_METADATA:
+      g_value_set_object (value, private->metadata);
+      break;
     case PROP_ID:
       g_value_set_int (value, private->ID);
       break;
@@ -938,6 +952,12 @@ gimp_image_finalize (GObject *object)
       private->projection = NULL;
     }
 
+  if (private->metadata)
+    {
+      gexiv2_metadata_free (private->metadata);
+      private->metadata = NULL;
+    }
+
   if (private->graph)
     {
       g_object_unref (private->graph);
@@ -1128,9 +1148,12 @@ gimp_image_get_size (GimpViewable *viewable,
 static void
 gimp_image_size_changed (GimpViewable *viewable)
 {
+  GExiv2Metadata *metadata;
   GimpImage *image = GIMP_IMAGE (viewable);
   GList     *all_items;
   GList     *list;
+  gint       width;
+  gint       height;
 
   if (GIMP_VIEWABLE_CLASS (parent_class)->size_changed)
     GIMP_VIEWABLE_CLASS (parent_class)->size_changed (viewable);
@@ -1155,6 +1178,14 @@ gimp_image_size_changed (GimpViewable *viewable)
 
   gimp_viewable_size_changed (GIMP_VIEWABLE (gimp_image_get_mask (image)));
 
+  metadata = gimp_image_get_metadata (image);
+  if (metadata)
+    {
+      gimp_image_get_size (image, &width, &height);
+
+      gexiv2_metadata_set_tag_string (metadata,  "Exif.Image.ImageWidth", g_strdup_printf ("%d", width));
+      gexiv2_metadata_set_tag_string (metadata,  "Exif.Image.ImageLength", g_strdup_printf ("%d", height));
+    }
   gimp_projectable_structure_changed (GIMP_PROJECTABLE (image));
 }
 
@@ -2525,7 +2556,36 @@ void
 gimp_image_precision_changed (GimpImage *image)
 {
   g_return_if_fail (GIMP_IS_IMAGE (image));
+  GExiv2Metadata *metadata;
+  gchar *precision_str;
 
+  metadata = gimp_image_get_metadata (image);
+  if (metadata)
+    {
+      switch (gimp_image_get_precision (image))
+        {
+        case   GIMP_PRECISION_U8_LINEAR: /*< desc="8-bit linear integer"         >*/
+        case   GIMP_PRECISION_U8_GAMMA:  /*< desc="8-bit gamma integer"          >*/
+          precision_str = "8 8 8";
+          break;
+        case   GIMP_PRECISION_U16_LINEAR:  /*< desc="16-bit linear integer"        >*/
+        case   GIMP_PRECISION_U16_GAMMA:   /*< desc="16-bit gamma integer"         >*/
+        case   GIMP_PRECISION_HALF_LINEAR: /*< desc="16-bit linear floating point" >*/
+        case   GIMP_PRECISION_HALF_GAMMA:  /*< desc="16-bit gamma floating point"  >*/
+          precision_str = "16 16 16";
+          break;
+        case   GIMP_PRECISION_U32_LINEAR: /*< desc="32-bit linear integer"        >*/
+        case   GIMP_PRECISION_U32_GAMMA:  /*< desc="32-bit gamma integer"         >*/
+        case   GIMP_PRECISION_FLOAT_LINEAR: /*< desc="32-bit linear floating point" >*/
+        case   GIMP_PRECISION_FLOAT_GAMMA:  /*< desc="32-bit gamma floating point"  >*/
+          precision_str = "32 32 32";
+          break;
+        default:
+          precision_str = "8 8 8";
+          break;
+        }
+      gexiv2_metadata_set_tag_string (metadata, "Exif.Image.BitsPerSample", precision_str);
+    }
   g_signal_emit (image, gimp_image_signals[PRECISION_CHANGED], 0);
 }
 
@@ -4190,3 +4250,28 @@ gimp_image_invalidate_previews (GimpImage *image)
   gimp_item_stack_invalidate_previews (layers);
   gimp_item_stack_invalidate_previews (channels);
 }
+
+GExiv2Metadata *
+gimp_image_get_metadata (GimpImage *image)
+{
+  GimpImagePrivate *private;
+
+  g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
+
+  private = GIMP_IMAGE_GET_PRIVATE (image);
+
+  return private->metadata;
+}
+
+void
+gimp_image_set_metadata (GimpImage * image, GExiv2Metadata *metadata)
+{
+  GimpImagePrivate *private;
+
+  g_return_if_fail (GIMP_IS_IMAGE (image));
+
+  private = GIMP_IMAGE_GET_PRIVATE (image);
+
+  private->metadata = metadata;
+
+}
diff --git a/app/core/gimpimage.h b/app/core/gimpimage.h
index 3cd6a1e..59c21fa 100644
--- a/app/core/gimpimage.h
+++ b/app/core/gimpimage.h
@@ -20,6 +20,7 @@
 
 
 #include "gimpviewable.h"
+#include <gexiv2/gexiv2.h>
 
 
 #define GIMP_IMAGE_ACTIVE_PARENT ((gpointer) 1)
@@ -420,5 +421,9 @@ void            gimp_image_invalidate_previews   (GimpImage          *image);
 
 const gchar   * gimp_image_get_string_untitled   (void);
 
+GExiv2Metadata *gimp_image_get_metadata          (GimpImage          *image);
+
+void            gimp_image_set_metadata          (GimpImage          *image,
+                                                  GExiv2Metadata     *metadata);
 
 #endif /* __GIMP_IMAGE_H__ */
diff --git a/app/dialogs/Makefile.am b/app/dialogs/Makefile.am
index 63fdb64..9bb9b72 100644
--- a/app/dialogs/Makefile.am
+++ b/app/dialogs/Makefile.am
@@ -56,6 +56,8 @@ libappdialogs_a_sources = \
 	layer-options-dialog.c		\
 	layer-options-dialog.h		\
 	lebl-dialog.c			\
+	metadata-dialog.c		\
+	metadata-dialog.h		\
 	module-dialog.c			\
 	module-dialog.h			\
 	offset-dialog.c			\
diff --git a/app/dialogs/metadata-dialog.c b/app/dialogs/metadata-dialog.c
new file mode 100644
index 0000000..54ddd76
--- /dev/null
+++ b/app/dialogs/metadata-dialog.c
@@ -0,0 +1,256 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <gtk/gtk.h>
+#include <gegl.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "dialogs-types.h"
+
+#include "gegl/gimp-babl.h"
+
+#include "core/gimp.h"
+#include "core/gimpimage.h"
+
+#include "core/gimpimage-metadata.h"
+
+#include "file/file-utils.h"
+
+#include "widgets/gimphelp-ids.h"
+
+#include "metadata-dialog.h"
+
+#include "gimp-intl.h"
+
+#include <gexiv2/gexiv2.h>
+#include "core/gimpimage-metadata.h"
+
+
+/*  local function prototypes  */
+static void   metadata_dialog_get_metadata  (GimpImage             *image,
+                                             GtkBuilder            *builder);
+
+static void   metadata_dialog_response      (GtkWidget             *dialog,
+                                             guint                  response_id,
+                                             Gimp                  *gimp);
+
+static void   metadata_dialog_iptc_callback (GtkWidget             *dialog,
+                                             BuildHandler          *handler);
+
+/*  public functions  */
+
+GtkWidget *
+metadata_dialog_show (Gimp *gimp, GimpImage *image)
+{
+  GError       *error = NULL;
+  GtkWidget    *dialog;
+  BuildHandler *handler;
+  GtkWidget    *metadata_vbox;
+  GtkWidget    *content_area;
+  GtkButton    *write_button;
+  gchar        *ui_file = NULL;
+  const gchar  *fname;
+
+  g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL);
+
+  handler = g_new0 (BuildHandler, 1);
+
+  handler->builder = gtk_builder_new();
+
+  handler->image = image;
+
+  fname = file_utils_uri_display_basename(gimp_image_get_any_uri (image));
+
+  ui_file = g_build_filename (gimp_data_directory (),"ui/metadata.ui", NULL);
+  if( ! gtk_builder_add_from_file( handler->builder, ui_file, &error ) )
+    {
+      g_print( "Error occured while loading UI file!\n" );
+      g_print( "Message: %s\n", error->message );
+      return NULL;
+    }
+
+  metadata_vbox = GTK_WIDGET( gtk_builder_get_object( handler->builder, "metadata_vbox" ) );
+
+  dialog = gimp_dialog_new (g_strdup_printf ("Metadata: %s", fname),
+                            "gimp-metadata-dialog",
+                            NULL, 0,
+                            gimp_standard_help_func,
+                            GIMP_HELP_METADATA_DIALOG,
+
+                            GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
+
+                            NULL);
+
+  gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+                                           GTK_RESPONSE_CLOSE,
+                                           -1);
+
+  g_signal_connect (dialog, "response",
+                    G_CALLBACK (metadata_dialog_response),
+                    gimp);
+
+  content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+  gtk_box_pack_start( GTK_BOX( content_area ), metadata_vbox, TRUE, TRUE, 0 );
+
+  write_button = GTK_BUTTON (gtk_builder_get_object (handler->builder, "iptc_write_button"));
+
+  g_signal_connect(G_OBJECT(write_button), "clicked",
+                   G_CALLBACK(metadata_dialog_iptc_callback),
+                   handler);
+
+  metadata_dialog_get_metadata (image, handler->builder);
+
+  gtk_window_set_default_size (GTK_WINDOW (dialog), 400, 500);
+
+  gtk_widget_show (dialog);
+  return dialog;
+}
+
+/*  private functions  */
+
+static void
+metadata_dialog_get_metadata (GimpImage *image, GtkBuilder *builder)
+{
+  GExiv2Metadata *metadata;
+  GtkWidget     *widget;
+  GtkEntry      *entry_widget;
+  GtkTextView   *textview_widget;
+  GtkTextBuffer *text_view_buffer;
+  GtkListStore  *exif_list_store;
+  GtkListStore  *xmp_list_store;
+  gchar        **exif_data;
+  gchar        **iptc_data;
+  gchar        **xmp_data;
+  gchar         *value;
+  gchar         *value_utf;
+  GtkTreeIter    iter;
+  gint           i;
+
+  exif_list_store = GTK_LIST_STORE (gtk_builder_get_object ( builder, "exifliststore" ));
+  xmp_list_store = GTK_LIST_STORE (gtk_builder_get_object ( builder, "xmpliststore" ));
+
+  metadata = gimp_image_get_metadata (image);
+
+  if (metadata)
+    {
+      exif_data = gexiv2_metadata_get_exif_tags (metadata);
+
+      i = 0;
+      while (exif_data[i] != NULL)
+        {
+          gtk_list_store_append(exif_list_store, &iter);
+          value = gexiv2_metadata_get_tag_interpreted_string (metadata, exif_data[i]);
+          value_utf = g_locale_to_utf8 (value, strlen (value), NULL, NULL, NULL);
+          gtk_list_store_set (exif_list_store, &iter,
+                              C_EXIF_TAG, exif_data[i],
+                              C_EXIF_VALUE, value_utf,
+                              -1);
+          i++;
+        }
+
+      xmp_data = gexiv2_metadata_get_xmp_tags (metadata);
+
+      i = 0;
+      while (xmp_data[i] != NULL)
+        {
+          gtk_list_store_append(xmp_list_store, &iter);
+          value = gexiv2_metadata_get_tag_interpreted_string (metadata, xmp_data[i]);
+          value_utf = g_locale_to_utf8 (value, strlen (value), NULL, NULL, NULL);
+          gtk_list_store_set (xmp_list_store, &iter,
+                              C_XMP_TAG, xmp_data[i],
+                              C_XMP_VALUE, value_utf,
+                              -1);
+          i++;
+        }
+
+      iptc_data = gexiv2_metadata_get_iptc_tags (metadata);
+
+      i = 0;
+      while (iptc_data[i] != NULL)
+        {
+          widget = GTK_WIDGET( gtk_builder_get_object(builder, iptc_data[i] ) );
+          value = gexiv2_metadata_get_tag_interpreted_string (metadata, iptc_data[i]);
+          value_utf = g_locale_to_utf8 (value, strlen (value), NULL, NULL, NULL);
+          if (GTK_IS_ENTRY (widget))
+            {
+              entry_widget = GTK_ENTRY (widget);
+              gtk_entry_set_text (entry_widget, value_utf);
+            }
+          else if (GTK_IS_TEXT_VIEW (widget))
+            {
+              textview_widget = GTK_TEXT_VIEW (widget);
+              text_view_buffer = gtk_text_view_get_buffer (textview_widget);
+              gtk_text_buffer_set_text (text_view_buffer, value_utf, strlen (value_utf));
+            }
+          i++;
+        }
+    }
+}
+
+static void
+metadata_dialog_iptc_callback (GtkWidget *dialog, BuildHandler *handler)
+{
+  GExiv2Metadata *metadata;
+  GtkEntry      *entry;
+  GtkTextView   *text_view;
+  GtkTextBuffer *text_view_buffer;
+  GtkTextIter   start;
+  GtkTextIter   end;
+  gchar         *multi_text = NULL;
+
+  gint i;
+
+  metadata = gimp_image_get_metadata (handler->image);
+
+  for (i = 0; i < G_N_ELEMENTS (iptc_tags); i++)
+    {
+      if (!strcmp ("single", iptc_tags[i].mode))
+        {
+          entry = GTK_ENTRY (gtk_builder_get_object (handler->builder, iptc_tags[i].tag));
+          gexiv2_metadata_set_tag_string (metadata, iptc_tags[i].tag, gtk_entry_get_text (entry));
+        }
+      else if (!strcmp ("multi", iptc_tags[i].mode))
+        {
+          text_view = GTK_TEXT_VIEW (gtk_builder_get_object (handler->builder, iptc_tags[i].tag));
+          text_view_buffer = gtk_text_view_get_buffer (text_view);
+          gtk_text_buffer_get_start_iter (text_view_buffer, &start);
+          gtk_text_buffer_get_end_iter (text_view_buffer, &end);
+          multi_text = gtk_text_buffer_get_text (text_view_buffer, &start, &end, TRUE);
+          gexiv2_metadata_set_tag_string (metadata, iptc_tags[i].tag, multi_text);
+        }
+    }
+  g_free (multi_text);
+}
+
+static void
+metadata_dialog_response (GtkWidget *dialog,
+                          guint      response_id,
+                          Gimp      *gimp)
+{
+  switch (response_id)
+    {
+    default:
+      gtk_widget_destroy (dialog);
+      break;
+    }
+}
diff --git a/app/dialogs/metadata-dialog.h b/app/dialogs/metadata-dialog.h
new file mode 100644
index 0000000..dd472a4
--- /dev/null
+++ b/app/dialogs/metadata-dialog.h
@@ -0,0 +1,80 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __INPUT_DEVICES_DIALOG_H__
+#define __INPUT_DEVICES_DIALOG_H__
+
+#define EXIF_PREFIX "Exif."
+#define IPTC_PREFIX "Iptc."
+#define XMP_PREFIX  "Xmp."
+
+enum
+{
+  C_XMP_TAG = 0,
+  C_XMP_VALUE,
+  NUM_XMP_COLS
+};
+
+enum
+{
+  C_EXIF_TAG = 0,
+  C_EXIF_VALUE,
+  NUM_EXIF_COLS
+};
+
+typedef struct _BuildHandler BuildHandler;
+
+struct _BuildHandler
+{
+  GimpImage  *image;
+  GtkBuilder *builder;
+  gchar      *meta;
+};
+
+typedef struct
+{
+  gchar * tag;
+  gchar * mode;
+}iptc_tag;
+
+static const iptc_tag const iptc_tags[] =
+{
+  {"Iptc.Application2.Byline",                  "single"},
+  {"Iptc.Application2.BylineTitle",             "single"},
+  {"Iptc.Application2.Caption",                 "multi"},
+  {"Iptc.Application2.Category",                "single"},
+  {"Iptc.Application2.City",                    "single"},
+  {"Iptc.Application2.Copyright",               "single"},
+  {"Iptc.Application2.CountryName",             "single"},
+  {"Iptc.Application2.Credit",                  "single"},
+  {"Iptc.Application2.Headline",                "multi"},
+  {"Iptc.Application2.Keywords",                "multi"},
+  {"Iptc.Application2.ObjectName",              "single"},
+  {"Iptc.Application2.ProvinceState",           "single"},
+  {"Iptc.Application2.Source",                  "single"},
+  {"Iptc.Application2.SpecialInstructions",     "multi"},
+  {"Iptc.Application2.SubLocation",             "single"},
+  {"Iptc.Application2.SuppCategory",            "multi"},
+  {"Iptc.Application2.TransmissionReference",   "single"},
+  {"Iptc.Application2.Urgency",                 "single"},
+  {"Iptc.Application2.Writer",                  "single"}
+};
+
+GtkWidget * metadata_dialog_show (Gimp *gimp, GimpImage *image);
+
+
+#endif /* __INPUT_DEVICES_DIALOG_H__ */
diff --git a/app/file/Makefile.am b/app/file/Makefile.am
index 19db33b..6328550 100644
--- a/app/file/Makefile.am
+++ b/app/file/Makefile.am
@@ -8,7 +8,9 @@ AM_CPPFLAGS = \
 	-I$(top_srcdir)/app		\
 	$(GEGL_CFLAGS)			\
 	$(GDK_PIXBUF_CFLAGS)		\
-	-I$(includedir)
+	-I$(includedir)		\
+	$(GTK_CFLAGS)			\
+  $(GEXIV2_CFLAGS)
 
 noinst_LIBRARIES = libappfile.a
 
diff --git a/app/file/file-open.c b/app/file/file-open.c
index 136de5a..cf4bd2e 100644
--- a/app/file/file-open.c
+++ b/app/file/file-open.c
@@ -32,6 +32,7 @@
 #include <unistd.h>
 #endif
 
+#include <gtk/gtk.h>
 #include <gegl.h>
 #include <glib/gstdio.h>
 
@@ -55,11 +56,12 @@
 #include "core/gimpimage.h"
 #include "core/gimpimage-merge.h"
 #include "core/gimpimage-undo.h"
+#include "core/gimpimage-flip.h"
+#include "core/gimpimage-rotate.h"
 #include "core/gimpimagefile.h"
 #include "core/gimplayer.h"
 #include "core/gimpparamspecs.h"
 #include "core/gimpprogress.h"
-
 #include "pdb/gimppdb.h"
 
 #include "plug-in/gimppluginmanager.h"
@@ -74,6 +76,8 @@
 
 #include "gimp-intl.h"
 
+#include <gexiv2/gexiv2.h>
+#include "core/gimpimage-metadata.h"
 
 static void     file_open_sanitize_image       (GimpImage                 *image,
                                                 gboolean                   as_new);
@@ -88,6 +92,14 @@ static GList *  file_open_get_layers           (const GimpImage           *image
                                                 gboolean                   merge_visible,
                                                 gint                      *n_visible);
 static gboolean file_open_file_proc_is_import  (const GimpPlugInProcedure *file_proc);
+void            file_set_image_metadata        (GimpImage                 *image,
+                                                const gchar               *uri);
+void            file_evaluate_image_metadata   (GimpImage                 *image,
+                                                GimpContext               *context);
+gboolean        file_open_metadata             (Gimp                      *gimp,
+                                                GimpContext               *context,
+                                                GimpImage                 *image,
+                                                const gchar               *uri);
 
 
 /*  public functions  */
@@ -453,6 +465,8 @@ file_open_with_proc_and_display (Gimp                *gimp,
 
   if (image)
     {
+      file_set_image_metadata (image, uri);
+      file_evaluate_image_metadata (image, context);
       /* If the file was imported we want to set the layer name to the
        * file name. For now, assume that multi-layered imported images
        * have named the layers already, so only rename the layer of
@@ -508,7 +522,7 @@ file_open_with_proc_and_display (Gimp                *gimp,
             }
         }
 
-      /*  announce that we opened this image  */
+/*  announce that we opened this image  */
       gimp_image_opened (image->gimp, uri);
     }
 
@@ -838,3 +852,147 @@ file_open_file_proc_is_import (const GimpPlugInProcedure *file_proc)
            file_proc->mime_type &&
            strcmp (file_proc->mime_type, "image/xcf") == 0);
 }
+
+/**
+ * Gets Exif Metadata from file and sets is as an image property
+ */
+void
+file_set_image_metadata (GimpImage *image, const gchar *uri)
+{
+  GError              *error           = NULL;
+  gchar               *filename;
+  GExiv2Metadata      *metadata        = NULL;
+  gchar               *metadata_string = NULL;
+
+
+  filename = file_utils_filename_from_uri (uri);
+
+  metadata = gimp_image_metadata_get_from_file (filename, &error);
+  if (error)
+    {
+      if (image->gimp->be_verbose)
+        {
+          g_print ( "METADATA load: %s - %s: %d\n", filename, error->message, error->code);
+        }
+      g_clear_error (&error);
+    }
+
+  if (metadata)
+    {
+      metadata_string = gimp_image_metadata_serialize (metadata);
+      gexiv2_metadata_free (metadata);
+    }
+
+  /* get a valid metadata object, even without entries */
+  metadata = gimp_image_metadata_deserialize (metadata_string);
+  if (!gimp_image_get_metadata (image))
+    gimp_image_set_metadata (image, metadata);
+
+  g_free (filename);
+}
+
+/**
+ * Evaluates metadata and sets image information accordingly
+ * User Comment -> gimp-comment parasite
+ * Resolution   -> image resolution
+ * Orientation  -> image orientation
+ * Erases exif thumbnail, it will be rewritten, when exported
+ **/
+void
+file_evaluate_image_metadata (GimpImage *image, GimpContext *context)
+{
+  GExiv2Metadata *metadata;
+
+  metadata = gimp_image_get_metadata (image);
+
+  if (metadata)
+    {
+      gchar    *comment;
+      gdouble   xres;
+      gdouble   yres;
+      gint      unit;
+      gint      orientation;
+
+      comment = gexiv2_metadata_get_tag_string (metadata, "Exif.Photo.UserComment");
+      if (!comment)
+        {
+          comment = gexiv2_metadata_get_tag_string (metadata, "Exif.Image.ImageDescription");
+        }
+
+      if (comment)
+        {
+          GimpParasite   *parasite;
+
+          parasite = gimp_parasite_new ("gimp-comment",
+                                        GIMP_PARASITE_PERSISTENT,
+                                        strlen (comment)+1,
+                                        comment);
+
+          gimp_image_parasite_attach (image, parasite);
+          gimp_parasite_free (parasite);
+        }
+
+      if (gimp_image_metadata_get_resolution (metadata, &xres, &yres, &unit))
+        {
+          gimp_image_set_resolution (image, xres, yres);
+          switch (unit)
+          {
+            case 2:
+              gimp_image_set_unit (image, GIMP_UNIT_INCH);
+              break;
+            case 3:
+              gimp_image_set_unit (image, GIMP_UNIT_MM);
+              break;
+          }
+        }
+
+      orientation = gexiv2_metadata_get_orientation (metadata);
+
+      if (orientation > 0)
+        {
+          switch (orientation)
+          {
+            case 1:  /* standard orientation, do nothing */
+              break;
+
+            case 2:  /* flipped right-left               */
+              gimp_image_flip (image, context, GIMP_ORIENTATION_HORIZONTAL, NULL);
+              break;
+
+            case 3:  /* rotated 180                      */
+              gimp_image_rotate (image, context, GIMP_ROTATE_180, NULL);
+              break;
+
+            case 4:  /* flipped top-bottom               */
+              gimp_image_flip (image, context, GIMP_ORIENTATION_VERTICAL, NULL);
+              break;
+
+            case 5:  /* flipped diagonally around '\'    */
+              gimp_image_rotate (image, context, GIMP_ROTATE_90, NULL);
+              gimp_image_flip (image, context, GIMP_ORIENTATION_HORIZONTAL, NULL);
+              break;
+
+            case 6:  /* 90 CW                            */
+              gimp_image_rotate (image, context, GIMP_ROTATE_90, NULL);
+              break;
+
+            case 7:  /* flipped diagonally around '/'    */
+              gimp_image_rotate (image, context, GIMP_ROTATE_90, NULL);
+              gimp_image_flip (image, context, GIMP_ORIENTATION_VERTICAL, NULL);
+              break;
+
+            case 8:  /* 90 CCW                           */
+              gimp_image_rotate (image, context, GIMP_ROTATE_270, NULL);
+              break;
+
+            default: /* shouldn't happen                 */
+              break;
+          }
+
+          gexiv2_metadata_set_tag_string (metadata,  "Exif.Image.Orientation", "1");
+          gexiv2_metadata_set_tag_string (metadata,  "Xmp.tiff.Orientation", "1");
+
+        }
+      gexiv2_metadata_erase_exif_thumbnail (metadata);
+    }
+}
diff --git a/app/file/file-save.c b/app/file/file-save.c
index 360f97a..9e764fc 100644
--- a/app/file/file-save.c
+++ b/app/file/file-save.c
@@ -50,6 +50,7 @@
 #include "core/gimpcontext.h"
 #include "core/gimpdocumentlist.h"
 #include "core/gimpdrawable.h"
+#include "core/gimpviewable.h"
 #include "core/gimpimage.h"
 #include "core/gimpimagefile.h"
 #include "core/gimpparamspecs.h"
@@ -65,6 +66,30 @@
 
 #include "gimp-intl.h"
 
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gexiv2/gexiv2.h>
+#include "core/gimpimage-metadata.h"
+
+#define EXIF_THUMBNAIL_SIZE 256
+
+typedef struct
+{
+  gboolean save_exif;
+  gboolean save_xmp;
+  gboolean save_iptc;
+  gboolean save_thumbnail;
+} MetaHandling;
+
+/* private functions */
+
+void       file_save_evaluate_metadata           (GimpImage          *image,
+                                                  gchar              *filename,
+                                                  gchar              *mime_type);
+
+void       file_save_load_meta_handling          (GimpImage          *image,
+                                                  gchar              *mime_type);
+
+static MetaHandling meta_handling;
 
 /*  public functions  */
 
@@ -221,6 +246,9 @@ file_save (Gimp                *gimp,
       if (GIMP_PROCEDURE (file_proc)->proc_type == GIMP_INTERNAL)
         gimp_imagefile_save_thumbnail (imagefile, file_proc->mime_type, image,
                                        NULL);
+
+      /* metadata handling */
+      file_save_evaluate_metadata (image, filename, file_proc->mime_type);
     }
   else if (status != GIMP_PDB_CANCEL)
     {
@@ -241,3 +269,302 @@ file_save (Gimp                *gimp,
 
   return status;
 }
+
+/**
+ * reads metadata from the new file
+ * saves metadata from the current image and writes them back to the new file
+ */
+void
+file_save_evaluate_metadata (GimpImage *image, gchar *filename, gchar *mime_type)
+{
+  GError              *error = NULL;
+  GExiv2Metadata      *metadata = NULL;
+  GExiv2Metadata      *new_metadata = NULL;
+  gchar              **exif_data = NULL;
+  gchar              **iptc_data = NULL;
+  gchar              **xmp_data  = NULL;
+  gint                 i;
+  gchar               *value;
+  const GimpParasite  *comment_parasite;
+  const gchar         *comment = NULL;
+  gdouble              xres;
+  gdouble              yres;
+  gint                 unit;
+  gint                 image_width;
+  gint                 image_height;
+  GdkPixbuf           *thumb_pixbuf;
+  gchar               *thumb_buffer;
+  gsize                count;
+  gint                 thumbw;
+  gint                 thumbh;
+  gint                 errorcode;
+  GDateTime           *datetime;
+  gboolean             support_exif;
+  gboolean             support_xmp;
+  gboolean             support_iptc;
+
+
+  file_save_load_meta_handling (image, mime_type);
+
+  /* getting metadata from image */
+  metadata = gimp_image_get_metadata (image);
+
+  /* reads metadata from saved file */
+  new_metadata = gimp_image_metadata_get_from_file (filename, &error);
+  if (error)
+    {
+      errorcode = error->code;
+      if (image->gimp->be_verbose)
+        {
+          g_print ( "METADATA save: %s - %s: %d\n", filename, error->message, error->code);
+        }
+      g_clear_error (&error);
+    }
+
+  if (new_metadata && errorcode != 501) /* only done, if metadata is supported. */
+    {
+      support_exif = gexiv2_metadata_get_supports_exif (new_metadata);
+      support_xmp = gexiv2_metadata_get_supports_xmp (new_metadata);
+      support_iptc = gexiv2_metadata_get_supports_iptc (new_metadata);
+
+      image_width = gimp_image_get_width (image);
+      image_height = gimp_image_get_height (image);
+
+      datetime = g_date_time_new_now_local ();
+
+      comment_parasite = gimp_image_parasite_find (image, "gimp-comment");
+      if (comment_parasite)
+        {
+          comment = (gchar *) gimp_parasite_data (comment_parasite);
+        }
+
+      /* step #1: writing metadata to new files metadata */
+      if (meta_handling.save_exif && support_exif)  /* only if wanted */
+        {
+          exif_data = gexiv2_metadata_get_exif_tags (metadata);
+
+          i = 0;
+          while (exif_data[i] != NULL)
+            {
+              if (!gexiv2_metadata_has_tag (new_metadata, exif_data[i]) &&
+                  gimp_image_metadata_is_tag_supported (exif_data[i], mime_type))
+                {
+                  value = gexiv2_metadata_get_tag_string (metadata, exif_data[i]);
+                  gexiv2_metadata_set_tag_string (new_metadata, exif_data[i], value);
+                }
+              i++;
+            }
+
+          if (comment)
+            {
+              gexiv2_metadata_set_tag_string (new_metadata,  "Exif.Photo.UserComment", comment);
+              gexiv2_metadata_set_tag_string (new_metadata,  "Exif.Image.ImageDescription", comment);
+            }
+
+          gexiv2_metadata_set_tag_string (new_metadata,  "Exif.Image.DateTime", g_strdup_printf ("%d:%02d:%02d %02d:%02d:%02d",
+                                                                                                 g_date_time_get_year (datetime),
+                                                                                                 g_date_time_get_month (datetime),
+                                                                                                 g_date_time_get_day_of_month (datetime),
+                                                                                                 g_date_time_get_hour (datetime),
+                                                                                                 g_date_time_get_minute (datetime),
+                                                                                                 g_date_time_get_second (datetime)));
+          gexiv2_metadata_set_tag_string (new_metadata,  "Exif.Image.Software", PACKAGE_STRING);
+          gexiv2_metadata_set_tag_string (new_metadata,  "Exif.Image.ImageWidth", g_strdup_printf ("%d", image_width));
+          gexiv2_metadata_set_tag_string (new_metadata,  "Exif.Image.ImageLength", g_strdup_printf ("%d", image_height));
+
+          gimp_image_get_resolution (image, &xres, &yres);
+
+          switch (gimp_image_get_unit (image))
+          {
+            case GIMP_UNIT_INCH:
+              unit = 2;
+              break;
+            case GIMP_UNIT_MM:
+              unit = 3;
+              break;
+            default:
+              unit = 2;
+              break;
+          }
+
+          gimp_image_metadata_set_resolution (new_metadata, xres, yres, unit);
+        }
+
+      if (meta_handling.save_xmp && support_xmp)  /* only if wanted */
+        {
+          xmp_data = gexiv2_metadata_get_xmp_tags (metadata);
+
+          i = 0;
+          while (xmp_data[i] != NULL)
+            {
+              if (!gexiv2_metadata_has_tag (new_metadata, xmp_data[i]) &&
+                  gimp_image_metadata_is_tag_supported (xmp_data[i], mime_type))
+                {
+                  value = gexiv2_metadata_get_tag_string (metadata, xmp_data[i]);
+                  gexiv2_metadata_set_tag_string (new_metadata, xmp_data[i], value);
+                }
+              i++;
+            }
+
+          gexiv2_metadata_set_tag_string (new_metadata,  "Xmp.dc.Format", mime_type);
+          if (!g_strcmp0 (mime_type, "image/tiff"))       /* TIFF specific data */
+            {
+              gexiv2_metadata_set_tag_string (new_metadata,  "Xmp.tiff.DateTime", g_strdup_printf ("%d:%02d:%02d %02d:%02d:%02d",
+                                                                                                   g_date_time_get_year (datetime),
+                                                                                                   g_date_time_get_month (datetime),
+                                                                                                   g_date_time_get_day_of_month (datetime),
+                                                                                                   g_date_time_get_hour (datetime),
+                                                                                                   g_date_time_get_minute (datetime),
+                                                                                                   g_date_time_get_second (datetime)));
+            }
+        }
+
+      if (meta_handling.save_iptc && support_iptc)
+        {
+          iptc_data = gexiv2_metadata_get_iptc_tags (metadata);
+
+          i = 0;
+          while (iptc_data[i] != NULL)
+            {
+              if (!gexiv2_metadata_has_tag (new_metadata, iptc_data[i]) &&
+                  gimp_image_metadata_is_tag_supported (iptc_data[i], mime_type))
+                {
+                  value = gexiv2_metadata_get_tag_string (metadata, iptc_data[i]);
+                  gexiv2_metadata_set_tag_string (new_metadata, iptc_data[i], value);
+                }
+              i++;
+            }
+          gexiv2_metadata_set_tag_string (new_metadata,  "Iptc.Application2.DateCreated", g_strdup_printf ("%d-%d-%d",
+                                                                                                           g_date_time_get_year (datetime),
+                                                                                                           g_date_time_get_month (datetime),
+                                                                                                           g_date_time_get_day_of_month (datetime)));
+          gexiv2_metadata_set_tag_string (new_metadata,  "Iptc.Application2.TimeCreated", g_strdup_printf ("%02d:%02d:%02d-%02d:%02d",
+                                                                                                           g_date_time_get_hour (datetime),
+                                                                                                           g_date_time_get_minute (datetime),
+                                                                                                           g_date_time_get_second (datetime),
+                                                                                                           g_date_time_get_hour (datetime),
+                                                                                                           g_date_time_get_minute (datetime)));
+        }
+
+      /* step #6: save thumbnail */
+      if (meta_handling.save_thumbnail)  /* only if wanted */
+        {
+          if(image_width > image_height)
+            {
+              thumbw = EXIF_THUMBNAIL_SIZE;
+              thumbh = EXIF_THUMBNAIL_SIZE * image_height / image_width;
+            }
+          else
+            {
+              thumbh = EXIF_THUMBNAIL_SIZE;
+              thumbw = EXIF_THUMBNAIL_SIZE * image_height / image_width;
+            }
+
+          thumb_pixbuf = gimp_viewable_get_new_pixbuf (GIMP_VIEWABLE (image), image->gimp->user_context, thumbw, thumbh);
+          if (gdk_pixbuf_save_to_buffer (thumb_pixbuf, &thumb_buffer, &count, "jpeg",
+                                         &error,
+                                         "quality", "90",
+                                         NULL))
+            {
+              gexiv2_metadata_set_exif_thumbnail_from_buffer (new_metadata, (const guint8 *)thumb_buffer, (gint)count);
+            }
+          g_free (thumb_buffer);
+        }
+
+      /* step #7: settings for specific mime types */
+      if (!g_strcmp0 (mime_type, "image/x-psd"))     /* PSD specific data */
+        {
+        }
+      else if (!g_strcmp0 (mime_type, "image/jpeg"))     /* JPEG specific data */
+        {
+        }
+      else if (!g_strcmp0 (mime_type, "image/tiff")  && support_xmp)       /* TIFF specific data, these are important */
+        {
+          gexiv2_metadata_set_tag_string (new_metadata,  "Xmp.tiff.ImageWidth", g_strdup_printf ("%d", image_width));
+          gexiv2_metadata_set_tag_string (new_metadata,  "Xmp.tiff.ImageLength", g_strdup_printf ("%d", image_height));
+        }
+
+      /* step #8: saving metadata to file */
+      gimp_image_metadata_save_to_file (filename, new_metadata, &error);
+      if (error)
+        {
+          errorcode = error->code;
+          if (image->gimp->be_verbose)
+            {
+              g_print ( "METADATA save: %s - %s: %d\n", filename, error->message, error->code);
+            }
+          g_clear_error (&error);
+        }
+
+      g_date_time_unref ( datetime );
+
+      if (error)
+        g_error_free (error);
+      if (exif_data)
+        g_strfreev (exif_data);
+      if (xmp_data)
+        g_strfreev (xmp_data);
+      if (iptc_data)
+        g_strfreev (iptc_data);
+    }
+}
+
+void
+file_save_load_meta_handling (GimpImage *image, gchar *mime_type)
+{
+  const GimpParasite *parasite;
+  gchar        *def_str;
+  gint          save_exif;
+  gint          save_xmp;
+  gint          save_iptc;
+  gint          save_thumbnail;
+  gint          num_fields;
+
+  if (!g_strcmp0 (mime_type, "image/x-psd"))     /* PSD specific data */
+    {
+      meta_handling.save_exif      = TRUE;
+      meta_handling.save_iptc      = TRUE;
+      meta_handling.save_xmp       = TRUE;
+      meta_handling.save_thumbnail = TRUE;
+      return;
+    }
+
+  meta_handling.save_exif      = FALSE;
+  meta_handling.save_iptc      = FALSE;
+  meta_handling.save_xmp       = FALSE;
+  meta_handling.save_thumbnail = FALSE;
+
+  parasite = gimp_image_parasite_find (image, "exif-handling-data");
+
+  if (! parasite)
+    return;
+
+  def_str = g_strndup (gimp_parasite_data (parasite),
+                       gimp_parasite_data_size (parasite));
+
+  num_fields = sscanf (def_str, "%d %d %d %d",
+                       &save_exif,
+                       &save_xmp,
+                       &save_iptc,
+                       &save_thumbnail);
+
+  g_free (def_str);
+
+  gimp_image_parasite_detach (image, "exif-handling-data");
+
+  if (num_fields != 4)
+    return;
+
+  if (save_exif)
+    meta_handling.save_exif = TRUE;
+
+  if (save_xmp)
+    meta_handling.save_xmp = TRUE;
+
+  if (save_iptc)
+    meta_handling.save_iptc = TRUE;
+
+  if (save_thumbnail)
+    meta_handling.save_thumbnail = TRUE;
+
+}
diff --git a/app/pdb/Makefile.am b/app/pdb/Makefile.am
index fffe894..5f0d68a 100644
--- a/app/pdb/Makefile.am
+++ b/app/pdb/Makefile.am
@@ -65,6 +65,7 @@ libappinternal_procs_a_SOURCES = \
 	item-transform-cmds.c		\
 	layer-cmds.c			\
 	message-cmds.c			\
+	metadata-cmds.c			\
 	paint-tools-cmds.c		\
 	palette-cmds.c			\
 	palette-select-cmds.c		\
diff --git a/app/pdb/metadata-cmds.c b/app/pdb/metadata-cmds.c
new file mode 100644
index 0000000..e6bab0f
--- /dev/null
+++ b/app/pdb/metadata-cmds.c
@@ -0,0 +1,163 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-2003 Spencer Kimball and Peter Mattis
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl. */
+
+#include "config.h"
+
+#include <gexiv2/gexiv2.h>
+
+#include <gegl.h>
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "libgimpbase/gimpbase.h"
+
+#include "pdb-types.h"
+
+#include "core/gimp.h"
+#include "core/gimpimage-metadata.h"
+#include "core/gimpimage.h"
+#include "core/gimpparamspecs.h"
+
+#include "gimppdb.h"
+#include "gimpprocedure.h"
+#include "internal-procs.h"
+
+
+static GimpValueArray *
+image_metadata_get_invoker (GimpProcedure         *procedure,
+                            Gimp                  *gimp,
+                            GimpContext           *context,
+                            GimpProgress          *progress,
+                            const GimpValueArray  *args,
+                            GError               **error)
+{
+  gboolean success = TRUE;
+  GimpValueArray *return_vals;
+  GimpImage *image;
+  gchar *metadata_string = NULL;
+
+  image = gimp_value_get_image (gimp_value_array_index (args, 0), gimp);
+
+  if (success)
+    {
+      GExiv2Metadata *metadata;
+      metadata = gimp_image_get_metadata (image);
+      metadata_string = gimp_image_metadata_serialize (metadata);
+    }
+
+  return_vals = gimp_procedure_get_return_values (procedure, success,
+                                                  error ? *error : NULL);
+
+  if (success)
+    g_value_take_string (gimp_value_array_index (return_vals, 1), metadata_string);
+
+  return return_vals;
+}
+
+static GimpValueArray *
+image_metadata_set_invoker (GimpProcedure         *procedure,
+                            Gimp                  *gimp,
+                            GimpContext           *context,
+                            GimpProgress          *progress,
+                            const GimpValueArray  *args,
+                            GError               **error)
+{
+  gboolean success = TRUE;
+  GimpImage *image;
+  const gchar *metadata_string;
+
+  image = gimp_value_get_image (gimp_value_array_index (args, 0), gimp);
+  metadata_string = g_value_get_string (gimp_value_array_index (args, 1));
+
+  if (success)
+    {
+      GExiv2Metadata *metadata;
+      metadata = gimp_image_metadata_deserialize (metadata_string);
+      gimp_image_set_metadata (image, metadata);
+    }
+
+  return gimp_procedure_get_return_values (procedure, success,
+                                           error ? *error : NULL);
+}
+
+void
+register_metadata_procs (GimpPDB *pdb)
+{
+  GimpProcedure *procedure;
+
+  /*
+   * gimp-image-metadata-get
+   */
+  procedure = gimp_procedure_new (image_metadata_get_invoker);
+  gimp_object_set_static_name (GIMP_OBJECT (procedure),
+                               "gimp-image-metadata-get");
+  gimp_procedure_set_static_strings (procedure,
+                                     "gimp-image-metadata-get",
+                                     "Returns images metadata.",
+                                     "Returns exif/iptc/xmp metadata from image.",
+                                     "Spencer Kimball & Peter Mattis",
+                                     "Spencer Kimball & Peter Mattis",
+                                     "1995-1996",
+                                     NULL);
+  gimp_procedure_add_argument (procedure,
+                               gimp_param_spec_image_id ("image",
+                                                         "image",
+                                                         "The image",
+                                                         pdb->gimp, FALSE,
+                                                         GIMP_PARAM_READWRITE));
+  gimp_procedure_add_return_value (procedure,
+                                   gimp_param_spec_string ("metadata-string",
+                                                           "metadata string",
+                                                           "The exif/ptc/xmp metadata as a string",
+                                                           FALSE, FALSE, FALSE,
+                                                           NULL,
+                                                           GIMP_PARAM_READWRITE));
+  gimp_pdb_register_procedure (pdb, procedure);
+  g_object_unref (procedure);
+
+  /*
+   * gimp-image-metadata-set
+   */
+  procedure = gimp_procedure_new (image_metadata_set_invoker);
+  gimp_object_set_static_name (GIMP_OBJECT (procedure),
+                               "gimp-image-metadata-set");
+  gimp_procedure_set_static_strings (procedure,
+                                     "gimp-image-metadata-set",
+                                     "set images metadata.",
+                                     "Sets exif/iptc/xmp metadata to image.",
+                                     "Spencer Kimball & Peter Mattis",
+                                     "Spencer Kimball & Peter Mattis",
+                                     "1995-1996",
+                                     NULL);
+  gimp_procedure_add_argument (procedure,
+                               gimp_param_spec_image_id ("image",
+                                                         "image",
+                                                         "The image",
+                                                         pdb->gimp, FALSE,
+                                                         GIMP_PARAM_READWRITE));
+  gimp_procedure_add_argument (procedure,
+                               gimp_param_spec_string ("metadata-string",
+                                                       "metadata string",
+                                                       "The exif/ptc/xmp metadata as a string",
+                                                       FALSE, FALSE, FALSE,
+                                                       NULL,
+                                                       GIMP_PARAM_READWRITE));
+  gimp_pdb_register_procedure (pdb, procedure);
+  g_object_unref (procedure);
+}
diff --git a/app/ui/Makefile.am b/app/ui/Makefile.am
new file mode 100644
index 0000000..e600051
--- /dev/null
+++ b/app/ui/Makefile.am
@@ -0,0 +1,6 @@
+ui_datadir = $(gimpdatadir)/ui
+
+ui_data_DATA = \
+	metadata.ui
+
+EXTRA_DIST = $(ui_data_DATA)
diff --git a/app/ui/metadata.ui b/app/ui/metadata.ui
new file mode 100644
index 0000000..1a07d45
--- /dev/null
+++ b/app/ui/metadata.ui
@@ -0,0 +1,895 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="gtk+" version="2.16"/>
+  <!-- interface-naming-policy project-wide -->
+  <object class="GtkListStore" id="xmpliststore">
+    <columns>
+      <!-- column-name c_xmp_tag -->
+      <column type="gchararray"/>
+      <!-- column-name c_xmp_value -->
+      <column type="gchararray"/>
+    </columns>
+  </object>
+  <object class="GtkListStore" id="exifliststore">
+    <columns>
+      <!-- column-name c_exif_tag -->
+      <column type="gchararray"/>
+      <!-- column-name c_exif_value -->
+      <column type="gchararray"/>
+    </columns>
+  </object>
+  <object class="GtkVBox" id="metadata_vbox">
+    <property name="visible">True</property>
+    <property name="orientation">vertical</property>
+    <child>
+      <object class="GtkNotebook" id="metadata_notebook">
+        <property name="visible">True</property>
+        <property name="can_focus">True</property>
+        <property name="scrollable">True</property>
+        <property name="tab_hborder">4</property>
+        <property name="homogeneous">True</property>
+        <child>
+          <object class="GtkScrolledWindow" id="exif_scroll">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <child>
+              <object class="GtkViewport" id="exif_viewport">
+                <property name="visible">True</property>
+                <property name="resize_mode">queue</property>
+                <property name="shadow_type">none</property>
+                <child>
+                  <object class="GtkTreeView" id="exiftreeview">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="model">exifliststore</property>
+                    <property name="headers_clickable">False</property>
+                    <property name="search_column">0</property>
+                    <child>
+                      <object class="GtkTreeViewColumn" id="exif_tag_column">
+                        <property name="resizable">True</property>
+                        <property name="spacing">3</property>
+                        <property name="title">EXIF Tag</property>
+                        <child>
+                          <object class="GtkCellRendererText" id="exif_tag_cell_renderer"/>
+                          <attributes>
+                            <attribute name="text">0</attribute>
+                          </attributes>
+                        </child>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkTreeViewColumn" id="exif_value_column">
+                        <property name="resizable">True</property>
+                        <property name="spacing">3</property>
+                        <property name="title">Value</property>
+                        <child>
+                          <object class="GtkCellRendererText" id="exif_value_cell_renderer"/>
+                          <attributes>
+                            <attribute name="text">1</attribute>
+                          </attributes>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child type="tab">
+          <object class="GtkLabel" id="exif">
+            <property name="visible">True</property>
+            <property name="label" translatable="yes">EXIF</property>
+          </object>
+          <packing>
+            <property name="tab_expand">True</property>
+            <property name="tab_fill">False</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkScrolledWindow" id="xmp_scroll">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <child>
+              <object class="GtkViewport" id="xmp_viewport">
+                <property name="visible">True</property>
+                <property name="resize_mode">queue</property>
+                <property name="shadow_type">none</property>
+                <child>
+                  <object class="GtkTreeView" id="xmptreeview">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="model">xmpliststore</property>
+                    <property name="headers_clickable">False</property>
+                    <property name="search_column">0</property>
+                    <child>
+                      <object class="GtkTreeViewColumn" id="xmp_tag_column">
+                        <property name="resizable">True</property>
+                        <property name="spacing">3</property>
+                        <property name="title">XMP Tag</property>
+                        <child>
+                          <object class="GtkCellRendererText" id="xmp_tag_cell_renderer"/>
+                          <attributes>
+                            <attribute name="text">0</attribute>
+                          </attributes>
+                        </child>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkTreeViewColumn" id="xmp_value_column">
+                        <property name="resizable">True</property>
+                        <property name="spacing">3</property>
+                        <property name="title">Value</property>
+                        <child>
+                          <object class="GtkCellRendererText" id="xmp_value_cell_renderer"/>
+                          <attributes>
+                            <attribute name="text">1</attribute>
+                          </attributes>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child type="tab">
+          <object class="GtkLabel" id="xmp">
+            <property name="visible">True</property>
+            <property name="label" translatable="yes">XMP</property>
+          </object>
+          <packing>
+            <property name="position">1</property>
+            <property name="tab_fill">False</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkVBox" id="iptc_vbox">
+            <property name="visible">True</property>
+            <property name="orientation">vertical</property>
+            <property name="spacing">2</property>
+            <child>
+              <object class="GtkNotebook" id="iptc_notebook">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <child>
+                  <object class="GtkScrolledWindow" id="iptc_desc_scroll">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="hscrollbar_policy">automatic</property>
+                    <property name="vscrollbar_policy">automatic</property>
+                    <child>
+                      <object class="GtkViewport" id="iptc_desc_viewport">
+                        <property name="visible">True</property>
+                        <property name="resize_mode">queue</property>
+                        <property name="shadow_type">none</property>
+                        <child>
+                          <object class="GtkTable" id="desctable">
+                            <property name="visible">True</property>
+                            <property name="n_rows">8</property>
+                            <property name="n_columns">2</property>
+                            <property name="column_spacing">3</property>
+                            <property name="row_spacing">3</property>
+                            <child>
+                              <object class="GtkLabel" id="l_title">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="xpad">3</property>
+                                <property name="ypad">3</property>
+                                <property name="label" translatable="yes">Title</property>
+                                <property name="single_line_mode">True</property>
+                              </object>
+                            </child>
+                            <child>
+                              <object class="GtkLabel" id="l_author">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="xpad">3</property>
+                                <property name="ypad">3</property>
+                                <property name="label" translatable="yes">Author</property>
+                              </object>
+                              <packing>
+                                <property name="top_attach">1</property>
+                                <property name="bottom_attach">2</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkLabel" id="l_authortitle">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="xpad">3</property>
+                                <property name="ypad">3</property>
+                                <property name="label" translatable="yes">Authortitle</property>
+                              </object>
+                              <packing>
+                                <property name="top_attach">2</property>
+                                <property name="bottom_attach">3</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkLabel" id="l_copyright">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="xpad">3</property>
+                                <property name="ypad">3</property>
+                                <property name="label" translatable="yes">Copyright</property>
+                              </object>
+                              <packing>
+                                <property name="top_attach">3</property>
+                                <property name="bottom_attach">4</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkLabel" id="l_caption">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="xpad">3</property>
+                                <property name="ypad">3</property>
+                                <property name="label" translatable="yes">Caption</property>
+                              </object>
+                              <packing>
+                                <property name="top_attach">4</property>
+                                <property name="bottom_attach">5</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkLabel" id="l_captionwriter">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="xpad">3</property>
+                                <property name="ypad">3</property>
+                                <property name="label" translatable="yes">Captionwriter</property>
+                              </object>
+                              <packing>
+                                <property name="top_attach">5</property>
+                                <property name="bottom_attach">6</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkLabel" id="l_headline">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="xpad">3</property>
+                                <property name="ypad">3</property>
+                                <property name="label" translatable="yes">Headline</property>
+                              </object>
+                              <packing>
+                                <property name="top_attach">6</property>
+                                <property name="bottom_attach">7</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkLabel" id="l_specialinstruct">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="xpad">3</property>
+                                <property name="ypad">3</property>
+                                <property name="label" translatable="yes">Special Instructions</property>
+                              </object>
+                              <packing>
+                                <property name="top_attach">7</property>
+                                <property name="bottom_attach">8</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkEntry" id="Iptc.Application2.ObjectName">
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="invisible_char">&#x25CF;</property>
+                              </object>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="right_attach">2</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkEntry" id="Iptc.Application2.Byline">
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="invisible_char">&#x25CF;</property>
+                              </object>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="right_attach">2</property>
+                                <property name="top_attach">1</property>
+                                <property name="bottom_attach">2</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkEntry" id="Iptc.Application2.BylineTitle">
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="invisible_char">&#x25CF;</property>
+                              </object>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="right_attach">2</property>
+                                <property name="top_attach">2</property>
+                                <property name="bottom_attach">3</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkEntry" id="Iptc.Application2.Copyright">
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="invisible_char">&#x25CF;</property>
+                              </object>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="right_attach">2</property>
+                                <property name="top_attach">3</property>
+                                <property name="bottom_attach">4</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkEntry" id="Iptc.Application2.Writer">
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="invisible_char">&#x25CF;</property>
+                              </object>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="right_attach">2</property>
+                                <property name="top_attach">5</property>
+                                <property name="bottom_attach">6</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkScrolledWindow" id="caption_scroll">
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <child>
+                                  <object class="GtkViewport" id="caption_viewport">
+                                    <property name="visible">True</property>
+                                    <property name="resize_mode">queue</property>
+                                    <child>
+                                      <object class="GtkTextView" id="Iptc.Application2.Caption">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">True</property>
+                                      </object>
+                                    </child>
+                                  </object>
+                                </child>
+                              </object>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="right_attach">2</property>
+                                <property name="top_attach">4</property>
+                                <property name="bottom_attach">5</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkScrolledWindow" id="headline_scroll">
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <child>
+                                  <object class="GtkViewport" id="headline_viewport">
+                                    <property name="visible">True</property>
+                                    <property name="resize_mode">queue</property>
+                                    <child>
+                                      <object class="GtkTextView" id="Iptc.Application2.Headline">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">True</property>
+                                      </object>
+                                    </child>
+                                  </object>
+                                </child>
+                              </object>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="right_attach">2</property>
+                                <property name="top_attach">6</property>
+                                <property name="bottom_attach">7</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkScrolledWindow" id="instruct_scroll">
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <child>
+                                  <object class="GtkViewport" id="instruct_viewport">
+                                    <property name="visible">True</property>
+                                    <property name="resize_mode">queue</property>
+                                    <child>
+                                      <object class="GtkTextView" id="Iptc.Application2.SpecialInstructions">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">True</property>
+                                      </object>
+                                    </child>
+                                  </object>
+                                </child>
+                              </object>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="right_attach">2</property>
+                                <property name="top_attach">7</property>
+                                <property name="bottom_attach">8</property>
+                              </packing>
+                            </child>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child type="tab">
+                  <object class="GtkLabel" id="iptcdesc">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">Description</property>
+                  </object>
+                  <packing>
+                    <property name="tab_fill">False</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkScrolledWindow" id="iptc_category_scroll1">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="hscrollbar_policy">automatic</property>
+                    <property name="vscrollbar_policy">automatic</property>
+                    <child>
+                      <object class="GtkViewport" id="iptc_category_viewport1">
+                        <property name="visible">True</property>
+                        <property name="resize_mode">queue</property>
+                        <property name="shadow_type">none</property>
+                        <child>
+                          <object class="GtkTable" id="table3">
+                            <property name="visible">True</property>
+                            <property name="n_rows">4</property>
+                            <property name="n_columns">2</property>
+                            <property name="column_spacing">3</property>
+                            <property name="row_spacing">3</property>
+                            <child>
+                              <object class="GtkLabel" id="l_keywords1">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="xpad">3</property>
+                                <property name="ypad">3</property>
+                                <property name="label" translatable="yes">Keywords</property>
+                              </object>
+                            </child>
+                            <child>
+                              <object class="GtkLabel" id="l_category1">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="xpad">3</property>
+                                <property name="ypad">3</property>
+                                <property name="label" translatable="yes">Category</property>
+                              </object>
+                              <packing>
+                                <property name="top_attach">1</property>
+                                <property name="bottom_attach">2</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkLabel" id="l_suppcat1">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="xpad">3</property>
+                                <property name="ypad">3</property>
+                                <property name="label" translatable="yes">Supplemental
+Category</property>
+                              </object>
+                              <packing>
+                                <property name="top_attach">2</property>
+                                <property name="bottom_attach">3</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkLabel" id="l_urgency1">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="xpad">3</property>
+                                <property name="ypad">3</property>
+                                <property name="label" translatable="yes">Urgency</property>
+                              </object>
+                              <packing>
+                                <property name="top_attach">3</property>
+                                <property name="bottom_attach">4</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkScrolledWindow" id="scrolledwindow10">
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <child>
+                                  <object class="GtkViewport" id="viewport10">
+                                    <property name="visible">True</property>
+                                    <property name="resize_mode">queue</property>
+                                    <child>
+                                      <object class="GtkTextView" id="Iptc.Application2.Keywords">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">True</property>
+                                      </object>
+                                    </child>
+                                  </object>
+                                </child>
+                              </object>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="right_attach">2</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkScrolledWindow" id="scrolledwindow12">
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <child>
+                                  <object class="GtkViewport" id="viewport12">
+                                    <property name="visible">True</property>
+                                    <property name="resize_mode">queue</property>
+                                    <child>
+                                      <object class="GtkTextView" id="Iptc.Application2.SuppCategory">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">True</property>
+                                      </object>
+                                    </child>
+                                  </object>
+                                </child>
+                              </object>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="right_attach">2</property>
+                                <property name="top_attach">2</property>
+                                <property name="bottom_attach">3</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkEntry" id="Iptc.Application2.Urgency">
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="invisible_char">&#x25CF;</property>
+                              </object>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="right_attach">2</property>
+                                <property name="top_attach">3</property>
+                                <property name="bottom_attach">4</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkEntry" id="Iptc.Application2.Category">
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="invisible_char">&#x25CF;</property>
+                              </object>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="right_attach">2</property>
+                                <property name="top_attach">1</property>
+                                <property name="bottom_attach">2</property>
+                              </packing>
+                            </child>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+                <child type="tab">
+                  <object class="GtkLabel" id="iptckeys">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">Keywords/Categories</property>
+                  </object>
+                  <packing>
+                    <property name="position">1</property>
+                    <property name="tab_fill">False</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkScrolledWindow" id="iptc_credit_scroll">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="hscrollbar_policy">automatic</property>
+                    <property name="vscrollbar_policy">automatic</property>
+                    <child>
+                      <object class="GtkViewport" id="iptc_credit_viewport">
+                        <property name="visible">True</property>
+                        <property name="resize_mode">queue</property>
+                        <property name="shadow_type">none</property>
+                        <child>
+                          <object class="GtkTable" id="table4">
+                            <property name="visible">True</property>
+                            <property name="n_rows">9</property>
+                            <property name="n_columns">2</property>
+                            <property name="column_spacing">3</property>
+                            <property name="row_spacing">3</property>
+                            <child>
+                              <object class="GtkHSeparator" id="hseparator3">
+                                <property name="visible">True</property>
+                              </object>
+                              <packing>
+                                <property name="top_attach">2</property>
+                                <property name="bottom_attach">3</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkHSeparator" id="hseparator4">
+                                <property name="visible">True</property>
+                              </object>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="right_attach">2</property>
+                                <property name="top_attach">2</property>
+                                <property name="bottom_attach">3</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkLabel" id="l_credit">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="xpad">3</property>
+                                <property name="ypad">3</property>
+                                <property name="label" translatable="yes">Credit</property>
+                              </object>
+                            </child>
+                            <child>
+                              <object class="GtkLabel" id="l_source">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="xpad">3</property>
+                                <property name="ypad">3</property>
+                                <property name="label" translatable="yes">Source</property>
+                              </object>
+                              <packing>
+                                <property name="top_attach">1</property>
+                                <property name="bottom_attach">2</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkEntry" id="Iptc.Application2.Credit">
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="invisible_char">&#x25CF;</property>
+                              </object>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="right_attach">2</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkEntry" id="Iptc.Application2.Source">
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="invisible_char">&#x25CF;</property>
+                              </object>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="right_attach">2</property>
+                                <property name="top_attach">1</property>
+                                <property name="bottom_attach">2</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkLabel" id="l_transmission">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="xpad">3</property>
+                                <property name="ypad">3</property>
+                                <property name="label" translatable="yes">Transmission
+reference</property>
+                              </object>
+                              <packing>
+                                <property name="top_attach">8</property>
+                                <property name="bottom_attach">9</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkEntry" id="Iptc.Application2.TransmissionReference">
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="invisible_char">&#x25CF;</property>
+                              </object>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="right_attach">2</property>
+                                <property name="top_attach">8</property>
+                                <property name="bottom_attach">9</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkLabel" id="l_city">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="xpad">3</property>
+                                <property name="ypad">3</property>
+                                <property name="label" translatable="yes">City</property>
+                              </object>
+                              <packing>
+                                <property name="top_attach">3</property>
+                                <property name="bottom_attach">4</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkEntry" id="Iptc.Application2.City">
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="invisible_char">&#x25CF;</property>
+                              </object>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="right_attach">2</property>
+                                <property name="top_attach">3</property>
+                                <property name="bottom_attach">4</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkLabel" id="l_sublocation">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="xpad">3</property>
+                                <property name="ypad">3</property>
+                                <property name="label" translatable="yes">Sublocation</property>
+                              </object>
+                              <packing>
+                                <property name="top_attach">4</property>
+                                <property name="bottom_attach">5</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkEntry" id="Iptc.Application2.SubLocation">
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="invisible_char">&#x25CF;</property>
+                              </object>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="right_attach">2</property>
+                                <property name="top_attach">4</property>
+                                <property name="bottom_attach">5</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkLabel" id="l_province">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="xpad">3</property>
+                                <property name="ypad">3</property>
+                                <property name="label" translatable="yes">Province/State</property>
+                              </object>
+                              <packing>
+                                <property name="top_attach">5</property>
+                                <property name="bottom_attach">6</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkEntry" id="Iptc.Application2.ProvinceState">
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="invisible_char">&#x25CF;</property>
+                              </object>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="right_attach">2</property>
+                                <property name="top_attach">5</property>
+                                <property name="bottom_attach">6</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkLabel" id="l_country">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="label" translatable="yes">Country</property>
+                              </object>
+                              <packing>
+                                <property name="top_attach">6</property>
+                                <property name="bottom_attach">7</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkEntry" id="Iptc.Application2.CountryName">
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="invisible_char">&#x25CF;</property>
+                              </object>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="right_attach">2</property>
+                                <property name="top_attach">6</property>
+                                <property name="bottom_attach">7</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkHSeparator" id="hseparator1">
+                                <property name="visible">True</property>
+                              </object>
+                              <packing>
+                                <property name="top_attach">7</property>
+                                <property name="bottom_attach">8</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkHSeparator" id="hseparator2">
+                                <property name="visible">True</property>
+                              </object>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="right_attach">2</property>
+                                <property name="top_attach">7</property>
+                                <property name="bottom_attach">8</property>
+                              </packing>
+                            </child>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="position">2</property>
+                  </packing>
+                </child>
+                <child type="tab">
+                  <object class="GtkLabel" id="iptccredits">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">Credits/Origin</property>
+                  </object>
+                  <packing>
+                    <property name="position">2</property>
+                    <property name="tab_fill">False</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkHBox" id="hbox4">
+                <property name="visible">True</property>
+                <property name="spacing">2</property>
+                <child>
+                  <object class="GtkButton" id="iptc_write_button">
+                    <property name="label" translatable="yes">write</property>
+                    <property name="width_request">70</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">True</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="position">2</property>
+          </packing>
+        </child>
+        <child type="tab">
+          <object class="GtkLabel" id="iptc">
+            <property name="visible">True</property>
+            <property name="label" translatable="yes">IPTC</property>
+          </object>
+          <packing>
+            <property name="position">2</property>
+            <property name="tab_fill">False</property>
+          </packing>
+        </child>
+      </object>
+      <packing>
+        <property name="position">0</property>
+      </packing>
+    </child>
+  </object>
+</interface>
diff --git a/app/widgets/gimphelp-ids.h b/app/widgets/gimphelp-ids.h
index 72097fb..9becc42 100644
--- a/app/widgets/gimphelp-ids.h
+++ b/app/widgets/gimphelp-ids.h
@@ -40,6 +40,7 @@
 #define GIMP_HELP_FILE_SAVE_AS_TEMPLATE           "gimp-file-save-as-template"
 #define GIMP_HELP_FILE_REVERT                     "gimp-file-revert"
 #define GIMP_HELP_FILE_CLOSE                      "gimp-file-close"
+#define GIMP_HELP_FILE_SHOW_METADATA              "gimp-file-show-metadata"
 #define GIMP_HELP_FILE_CLOSE_ALL                  "gimp-file-close-all"
 #define GIMP_HELP_FILE_CREATE_TEMPLATE            "gimp-file-save-as-template" /* Update string along with gimp-help-2 */
 #define GIMP_HELP_FILE_QUIT                       "gimp-file-quit"
@@ -513,6 +514,7 @@
 #define GIMP_HELP_DISPLAY_FILTER_DIALOG           "gimp-display-filter-dialog"
 #define GIMP_HELP_HISTOGRAM_DIALOG                "gimp-histogram-dialog"
 #define GIMP_HELP_INFO_DIALOG                     "gimp-info-dialog"
+#define GIMP_HELP_METADATA_DIALOG                 "gimp-metadata-dialog"
 #define GIMP_HELP_MODULE_DIALOG                   "gimp-module-dialog"
 #define GIMP_HELP_NAVIGATION_DIALOG               "gimp-navigation-dialog"
 #define GIMP_HELP_TEXT_EDITOR_DIALOG              "gimp-text-editor-dialog"
diff --git a/app/xcf/xcf-load.c b/app/xcf/xcf-load.c
index 6c7ebee..dbb3523 100644
--- a/app/xcf/xcf-load.c
+++ b/app/xcf/xcf-load.c
@@ -52,6 +52,7 @@
 #include "core/gimpprogress.h"
 #include "core/gimpselection.h"
 #include "core/gimptemplate.h"
+#include "core/gimpimage-metadata.h"
 
 #include "text/gimptextlayer.h"
 #include "text/gimptextlayer-xcf.h"
@@ -209,6 +210,22 @@ xcf_load_image (Gimp     *gimp,
         }
     }
 
+  /* check for a metadata parasite */
+  parasite = gimp_image_parasite_find (GIMP_IMAGE (image),
+                                       "xcf-metadata-save");
+  if (parasite)
+    {
+      gchar *meta_string = NULL;
+      GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image);
+
+      meta_string = (gchar *) gimp_parasite_data (parasite);
+
+      gimp_image_set_metadata (image,
+                               gimp_image_metadata_deserialize (meta_string));
+      gimp_parasite_list_remove (private->parasites,
+                                 gimp_parasite_name (parasite));
+    }
+
   xcf_progress_update (info);
 
   while (TRUE)
diff --git a/app/xcf/xcf-save.c b/app/xcf/xcf-save.c
index d577971..0764767 100644
--- a/app/xcf/xcf-save.c
+++ b/app/xcf/xcf-save.c
@@ -49,6 +49,7 @@
 #include "core/gimpparasitelist.h"
 #include "core/gimpprogress.h"
 #include "core/gimpsamplepoint.h"
+#include "core/gimpimage-metadata.h"
 
 #include "text/gimptextlayer.h"
 #include "text/gimptextlayer-xcf.h"
@@ -239,6 +240,20 @@ xcf_save_image (XcfInfo    *info,
   gint     t1, t2, t3, t4;
   gchar    version_tag[16];
   GError  *tmp_error = NULL;
+  const GimpParasite *meta_parasite;
+  gchar   *meta_string = NULL;
+
+  /* serialize metadata */
+
+  meta_string = gimp_image_metadata_serialize (gimp_image_get_metadata (image));
+  if (meta_string)
+    {
+      meta_parasite = gimp_parasite_new ("xcf-metadata-save",
+                                         GIMP_PARASITE_PERSISTENT,
+                                         strlen (meta_string)+1,
+                                         meta_string);
+      gimp_image_parasite_attach (image, meta_parasite);
+    }
 
   /* write out the tag information for the image */
   if (info->file_version > 0)
diff --git a/configure.ac b/configure.ac
index cf329d8..1eac84e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -60,6 +60,7 @@ m4_define([poppler_required_version], [0.12.4])
 m4_define([libcurl_required_version], [7.15.1])
 m4_define([libgudev_required_version], [167])
 m4_define([exif_required_version], [0.6.15])
+m4_define([gexiv2_required_version], [0.6.1])
 m4_define([lcms_required_version], [2.2])
 m4_define([libpng_required_version], [1.2.37])
 m4_define([liblzma_required_version], [5.0.0])
@@ -123,11 +124,13 @@ GDK_PIXBUF_REQUIRED_VERSION=gdk_pixbuf_required_version
 GTK_REQUIRED_VERSION=gtk_required_version
 CAIRO_REQUIRED_VERSION=cairo_required_version
 GEGL_REQUIRED_VERSION=gegl_required_version
+GEXIV2_REQUIRED_VERSION=gexiv2_required_version
 AC_SUBST(GLIB_REQUIRED_VERSION)
 AC_SUBST(GDK_PIXBUF_REQUIRED_VERSION)
 AC_SUBST(GTK_REQUIRED_VERSION)
 AC_SUBST(CAIRO_REQUIRED_VERSION)
 AC_SUBST(GEGL_REQUIRED_VERSION)
+AC_SUBST(GEXIV2_REQUIRED_VERSION)
 
 # The symbol GIMP_UNSTABLE is defined above for substitution in
 # Makefiles and conditionally defined here as a preprocessor symbol
@@ -607,6 +610,7 @@ if test "x$FREETYPE_CONFIG" != "xno" ; then
 fi
 AC_SUBST(FREETYPE_LIBS)
 
+PKG_CHECK_MODULES(GEXIV2, gexiv2 >= gexiv2_required_version)
 
 ##########################################
 # Check for some special functions we need
@@ -1327,28 +1331,6 @@ AC_SUBST(MNG_LIBS)
 AC_SUBST(MNG_CFLAGS)
 
 
-############################################################
-# libexif: Library to allow exif tags to be read from, and
-# saved to, jpeg files. Currently, this permits exif data to
-# avoid destruction, but no data modification is performed.
-############################################################
-
-AC_ARG_WITH(libexif,  [  --without-libexif       build without EXIF support])
-
-have_libexif=no
-if test "x$with_libexif" != xno && test -z "$EXIF_LIBS" && test -n "$JPEG_LIBS"; then
-  have_libexif=yes
-  PKG_CHECK_MODULES(EXIF, libexif >= exif_required_version,
-    AC_DEFINE(HAVE_LIBEXIF, 1, [Define to 1 if libexif is available]),
-    have_libexif="no (libexif not found or too old)")
-fi
-
-AC_SUBST(EXIF_CFLAGS)
-AC_SUBST(EXIF_LIBS)
-
-AM_CONDITIONAL(HAVE_LIBEXIF, test "x$have_libexif" = xyes)
-
-
 #################
 # Check for libaa
 #################
@@ -2215,6 +2197,7 @@ app/tools/Makefile
 app/vectors/Makefile
 app/widgets/Makefile
 app/xcf/Makefile
+app/ui/Makefile
 app/tests/Makefile
 app/tests/files/Makefile
 app/tests/gimpdir-empty/Makefile
@@ -2405,9 +2388,6 @@ Optional Plug-Ins:
   X11 Mouse Cursor:    $have_xmc
   XPM:                 $have_libxpm
 
-Plug-In Features:
-  EXIF support:        $have_libexif
-
 Optional Modules:
   ALSA (MIDI Input):   $have_alsa
   Linux Input:         $have_linux_input (GUdev support: $have_libgudev)
diff --git a/libgimp/Makefile.am b/libgimp/Makefile.am
index 775ed14..dee9ffc 100644
--- a/libgimp/Makefile.am
+++ b/libgimp/Makefile.am
@@ -69,8 +69,9 @@ AM_CPPFLAGS = \
 	$(GTK_CFLAGS)			\
 	$(GEGL_CFLAGS)			\
 	-I$(includedir)			\
-	$(xobjective_c)
-
+	$(xobjective_c)		\
+  $(GEXIV2_CFLAGS)
+  
 lib_LTLIBRARIES = libgimp-@GIMP_API_VERSION@.la libgimpui-@GIMP_API_VERSION@.la
 
 PDB_WRAPPERS_C = \
@@ -105,6 +106,7 @@ PDB_WRAPPERS_C = \
 	gimpitemtransform_pdb.c		\
 	gimplayer_pdb.c			\
 	gimpmessage_pdb.c		\
+	gimpmetadata_pdb.c		\
 	gimppainttools_pdb.c		\
 	gimppalette_pdb.c		\
 	gimppalettes_pdb.c		\
@@ -158,6 +160,7 @@ PDB_WRAPPERS_H = \
 	gimpitemtransform_pdb.h		\
 	gimplayer_pdb.h			\
 	gimpmessage_pdb.h		\
+	gimpmetadata_pdb.h		\
 	gimppainttools_pdb.h		\
 	gimppalette_pdb.h		\
 	gimppalettes_pdb.h		\
@@ -206,6 +209,8 @@ libgimp_sources = \
 	gimpimage.h		\
 	gimplayer.c		\
 	gimplayer.h		\
+	gimpmetadata.c	\
+	gimpmetadata.h	\
 	gimppalette.c		\
 	gimppalette.h		\
 	gimppalettes.c		\
@@ -325,6 +330,7 @@ gimpinclude_HEADERS = \
 	gimpgradientselect.h		\
 	gimpimage.h			\
 	gimplayer.h			\
+	gimpmetadata.h	\
 	gimppalette.h			\
 	gimppalettes.h			\
 	gimppaletteselect.h		\
@@ -378,7 +384,8 @@ libgimp_@GIMP_API_VERSION@_la_LIBADD = \
 	$(CAIRO_LIBS)		\
 	$(GEGL_LIBS)		\
 	$(GDK_PIXBUF_LIBS)	\
-	$(RT_LIBS)
+	$(RT_LIBS)		\
+  $(GEXIV2_LIBS)
 
 libgimp_@GIMP_API_VERSION@_la_DEPENDENCIES = \
 	$(gimp_def)		\
diff --git a/libgimp/gimp.def b/libgimp/gimp.def
index fcf3fa8..e0fcdaf 100644
--- a/libgimp/gimp.def
+++ b/libgimp/gimp.def
@@ -445,6 +445,8 @@ EXPORTS
 	gimp_image_lower_vectors_to_bottom
 	gimp_image_merge_down
 	gimp_image_merge_visible_layers
+	gimp_image_get_metadata
+	gimp_image_set_metadata
 	gimp_image_new
 	gimp_image_new_with_precision
 	gimp_image_parasite_attach
diff --git a/libgimp/gimp.h b/libgimp/gimp.h
index 5a05e4e..ef11981 100644
--- a/libgimp/gimp.h
+++ b/libgimp/gimp.h
@@ -45,6 +45,7 @@
 #include <libgimp/gimpgradientselect.h>
 #include <libgimp/gimpimage.h>
 #include <libgimp/gimplayer.h>
+#include <libgimp/gimpmetadata.h>
 #include <libgimp/gimppalette.h>
 #include <libgimp/gimppalettes.h>
 #include <libgimp/gimppaletteselect.h>
diff --git a/libgimp/gimp_pdb_headers.h b/libgimp/gimp_pdb_headers.h
index 195c729..de4a39b 100644
--- a/libgimp/gimp_pdb_headers.h
+++ b/libgimp/gimp_pdb_headers.h
@@ -58,6 +58,7 @@
 #include <libgimp/gimpitemtransform_pdb.h>
 #include <libgimp/gimplayer_pdb.h>
 #include <libgimp/gimpmessage_pdb.h>
+#include <libgimp/gimpmetadata_pdb.h>
 #include <libgimp/gimppainttools_pdb.h>
 #include <libgimp/gimppalette_pdb.h>
 #include <libgimp/gimppalettes_pdb.h>
diff --git a/libgimp/gimpmetadata.c b/libgimp/gimpmetadata.c
new file mode 100644
index 0000000..192b8d5
--- /dev/null
+++ b/libgimp/gimpmetadata.c
@@ -0,0 +1,223 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpmetadata.c
+ *
+ * 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 3 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, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <glib/gprintf.h>
+
+#include "gimp.h"
+#include "libgimpbase/gimpbase.h"
+#include "gimpmetadata.h"
+
+/* private prototypes */
+gint       gimp_image_metadata_length           (gchar *testline, gchar *delim);
+
+/* public functions */
+
+/**
+ * usable from plug-ins
+ * gets metadata from serialized metadata from image
+ */
+GExiv2Metadata *
+gimp_image_get_metadata (gint32 image_ID)
+{
+  GError          **error = NULL;
+  GExiv2Metadata   *metadata;
+  GString          *buffer;
+  gchar            *metadata_string = NULL;
+  gchar           **meta_info = NULL;
+  gchar           **tag_info = NULL;
+  gchar            *value;
+  gint              i;
+  gint              j;
+  const guint8     *wilber;
+  guint             size;
+  gint              num;
+
+  metadata_string = gimp_image_metadata_get (image_ID);
+
+  if ( gexiv2_initialize ())
+    {
+      metadata = gexiv2_metadata_new ();
+
+      wilber = gimp_wilber_get_data (&size);
+
+      if (! gexiv2_metadata_open_buf (metadata, wilber, size, error))
+        {
+#ifdef GIMP_CONFIG_DEBUG
+          g_printerr ( "Error occured while loading metadata!\n" );
+          g_printerr ( "Message: %s\n", error->message );
+#endif
+          return NULL;
+        }
+    }
+
+  if (metadata && metadata_string)
+    {
+
+      meta_info = g_strsplit (metadata_string, TAG_LINE_DELIMITER, -1);
+
+      i=0;
+
+      while (meta_info[i] != NULL)
+        {
+          num = gimp_image_metadata_length (meta_info[i], TAG_TAG_DELIMITER);
+          tag_info = g_strsplit (meta_info[i], TAG_TAG_DELIMITER, -1);
+          if (num > 0)
+            {
+              if (num > 1)
+                {
+                  buffer = g_string_new (NULL);
+                  for (j = 1; j < (num + 1); j++)
+                    {
+                      g_string_append_printf (buffer, "%s%s", tag_info[j], TAG_TAG_DELIMITER); /* recreates value */
+                    }
+                  value = g_strndup (buffer->str, buffer->len - 1);  /* to avoid the trailing '#' */
+                  g_string_free (buffer, TRUE);
+                }
+              else
+                {
+                  value = tag_info[1];
+                }
+
+              gexiv2_metadata_set_tag_string (metadata, tag_info[0], value);  /* setting value to metadata */
+            }
+          i++;
+        }
+    }
+
+  if (meta_info)
+    g_strfreev (meta_info);
+
+  if (tag_info)
+    g_strfreev (tag_info);
+
+  return metadata;
+}
+
+/**
+ * usable from plug-ins
+ * sets image metadata from serialized metadata
+ */
+void
+gimp_image_set_metadata (gint32 image_ID,
+                                  GExiv2Metadata *metadata)
+{
+  gchar              **exif_data = NULL;
+  gchar              **iptc_data = NULL;
+  gchar              **xmp_data = NULL;
+  gchar               *value;
+  gint                 i;
+  gint                 n;
+  GString             *metadata_buffer;
+  const gchar         *metadata_string = NULL;
+
+  if (metadata)
+    {
+      metadata_buffer = g_string_new (NULL);
+
+      exif_data = gexiv2_metadata_get_exif_tags (metadata);
+
+      i = 0;
+      while (exif_data[i] != NULL)
+        {
+          value = gexiv2_metadata_get_tag_string (metadata, exif_data[i]);
+          g_string_append_printf (metadata_buffer, "%s%s%s%s", exif_data[i],
+                                  TAG_TAG_DELIMITER,
+                                  value,
+                                  TAG_LINE_DELIMITER);
+          n++;
+          i++;
+        }
+
+      if(exif_data)
+        g_strfreev (exif_data);
+
+      xmp_data = gexiv2_metadata_get_xmp_tags (metadata);
+
+      i = 0;
+      while (xmp_data[i] != NULL)
+        {
+          value = gexiv2_metadata_get_tag_string (metadata, xmp_data[i]);
+          g_string_append_printf (metadata_buffer, "%s%s%s%s", xmp_data[i],
+                                  TAG_TAG_DELIMITER,
+                                  value,
+                                  TAG_LINE_DELIMITER);
+
+          n++;
+          i++;
+        }
+
+      if(xmp_data)
+        g_strfreev (xmp_data);
+
+      iptc_data = gexiv2_metadata_get_iptc_tags (metadata);
+
+      i = 0;
+      while (iptc_data[i] != NULL)
+        {
+          value = gexiv2_metadata_get_tag_string (metadata, iptc_data[i]);
+          g_string_append_printf (metadata_buffer, "%s%s%s%s", iptc_data[i],
+                                  TAG_TAG_DELIMITER,
+                                  value,
+                                  TAG_LINE_DELIMITER);
+
+          n++;
+          i++;
+        }
+
+      if(iptc_data)
+        g_strfreev (iptc_data);
+
+      if (metadata_buffer->len > 1)
+        metadata_string = g_strndup (metadata_buffer->str, metadata_buffer->len - 1);
+      else
+        metadata_string = g_strdup (metadata_buffer->str);
+
+      g_string_free (metadata_buffer, TRUE);
+
+      gimp_image_metadata_set (image_ID, metadata_string);
+    }
+}
+
+
+/* private functions */
+
+gint
+gimp_image_metadata_length (gchar *testline, gchar *delim)
+{
+  gchar     *delim_test;
+  gint       i;
+  gint       sum;
+
+  delim_test = g_strdup (testline);
+
+  sum =0;
+
+  for (i=0; i < strlen (delim_test); i++)
+    {
+      if (delim_test[i] == delim[0])
+        sum++;
+    }
+
+  g_free (delim_test);
+  return sum;
+}
diff --git a/libgimp/gimpmetadata.h b/libgimp/gimpmetadata.h
new file mode 100644
index 0000000..97bcee3
--- /dev/null
+++ b/libgimp/gimpmetadata.h
@@ -0,0 +1,51 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpmetadata.h
+ *
+ * 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 3 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, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_METADATA_H__
+#define __GIMP_METADATA_H__
+
+#include <gexiv2/gexiv2.h>
+
+G_BEGIN_DECLS
+
+#define TAG_LINE_DELIMITER "\v"
+#define TAG_TAG_DELIMITER  "#"
+
+typedef struct _GimpImageMetadata GimpImageMetadata;
+
+struct _GimpImageMetadata
+{
+  gchar *tag;
+  gchar *value;
+};
+
+/* For information look into the C source or the html documentation */
+GExiv2Metadata *gimp_image_get_metadata (gint32          image_ID);
+
+void            gimp_image_set_metadata (gint32          image_ID,
+                                         GExiv2Metadata *metadata);
+
+G_END_DECLS
+
+#endif /* __GIMP_METADATA_H__ */
diff --git a/libgimp/gimpmetadata_pdb.c b/libgimp/gimpmetadata_pdb.c
new file mode 100644
index 0000000..9bb974a
--- /dev/null
+++ b/libgimp/gimpmetadata_pdb.c
@@ -0,0 +1,97 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpmetadata_pdb.c
+ *
+ * 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 3 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, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimpimagemetadata
+ * @title: gimpimagemetadata
+ * @short_description: Metadata procedures
+ *
+ * Metadata procedures.
+ **/
+
+
+/**
+ * gimp_image_metadata_get:
+ * @image_ID: The image.
+ *
+ * Returns images metadata.
+ *
+ * Returns exif/iptc/xmp metadata from image.
+ *
+ * Returns: The exif/ptc/xmp metadata as a string.
+ **/
+gchar *
+gimp_image_metadata_get (gint32 image_ID)
+{
+  GimpParam *return_vals;
+  gint nreturn_vals;
+  gchar *metadata_string = NULL;
+
+  return_vals = gimp_run_procedure ("gimp-image-metadata-get",
+                                    &nreturn_vals,
+                                    GIMP_PDB_IMAGE, image_ID,
+                                    GIMP_PDB_END);
+
+  if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+    metadata_string = g_strdup (return_vals[1].data.d_string);
+
+  gimp_destroy_params (return_vals, nreturn_vals);
+
+  return metadata_string;
+}
+
+/**
+ * gimp_image_metadata_set:
+ * @image_ID: The image.
+ * @metadata_string: The exif/ptc/xmp metadata as a string.
+ *
+ * set images metadata.
+ *
+ * Sets exif/iptc/xmp metadata to image.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_image_metadata_set (gint32       image_ID,
+                         const gchar *metadata_string)
+{
+  GimpParam *return_vals;
+  gint nreturn_vals;
+  gboolean success = TRUE;
+
+  return_vals = gimp_run_procedure ("gimp-image-metadata-set",
+                                    &nreturn_vals,
+                                    GIMP_PDB_IMAGE, image_ID,
+                                    GIMP_PDB_STRING, metadata_string,
+                                    GIMP_PDB_END);
+
+  success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+  gimp_destroy_params (return_vals, nreturn_vals);
+
+  return success;
+}
diff --git a/libgimp/gimpmetadata_pdb.h b/libgimp/gimpmetadata_pdb.h
new file mode 100644
index 0000000..53647bd
--- /dev/null
+++ b/libgimp/gimpmetadata_pdb.h
@@ -0,0 +1,42 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpmetadata_pdb.h
+ *
+ * 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 3 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, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_METADATA_PDB_H__
+#define __GIMP_METADATA_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gchar*   gimp_image_metadata_get (gint32       image_ID);
+gboolean gimp_image_metadata_set (gint32       image_ID,
+                                  const gchar *metadata_string);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_METADATA_PDB_H__ */
diff --git a/libgimpbase/Makefile.am b/libgimpbase/Makefile.am
index 59a258b..f8828b3 100644
--- a/libgimpbase/Makefile.am
+++ b/libgimpbase/Makefile.am
@@ -119,6 +119,8 @@ libgimpbase_sources = \
 	gimputils.h            	\
 	gimpvaluearray.c	\
 	gimpvaluearray.h	\
+  gimpwilber.c     \
+  gimpwilber.h     \
 	gimpwin32-io.h		\
 	gimpwire.c		\
 	gimpwire.h
@@ -150,7 +152,8 @@ libgimpbaseinclude_HEADERS = \
 	gimpsignal.h		\
 	gimpunit.h		\
 	gimputils.h		\
-	gimpvaluearray.h
+	gimpvaluearray.h  \
+  gimpwilber.h
 
 
 libgimpbase_@GIMP_API_VERSION@_la_LDFLAGS = \
diff --git a/libgimpbase/gimpbase.def b/libgimpbase/gimpbase.def
index 9eeb82b..4177564 100644
--- a/libgimpbase/gimpbase.def
+++ b/libgimpbase/gimpbase.def
@@ -139,6 +139,7 @@ EXPORTS
 	gimp_value_array_truncate
 	gimp_value_array_unref
 	gimp_vectors_stroke_type_get_type
+  gimp_wilber_get_data
 	gimp_wire_clear_error
 	gimp_wire_destroy
 	gimp_wire_error
diff --git a/libgimpbase/gimpbase.h b/libgimpbase/gimpbase.h
index 08930dc..6585310 100644
--- a/libgimpbase/gimpbase.h
+++ b/libgimpbase/gimpbase.h
@@ -35,6 +35,7 @@
 #include <libgimpbase/gimputils.h>
 #include <libgimpbase/gimpversion.h>
 #include <libgimpbase/gimpvaluearray.h>
+#include <libgimpbase/gimpwilber.h>
 
 #ifndef G_OS_WIN32
 #include <libgimpbase/gimpsignal.h>
diff --git a/libgimpbase/gimpwilber.c b/libgimpbase/gimpwilber.c
new file mode 100644
index 0000000..16bc23e
--- /dev/null
+++ b/libgimpbase/gimpwilber.c
@@ -0,0 +1,67 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpwilber.c
+ * Copyright (C) 2007 Sven Neumann <sven@gimp.org>
+ *
+ * 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 3 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, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <glib-object.h>
+
+#include "gimpwilber.h"
+
+const guint8 wilber_jpg[] = {
+  0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, 0x00, 0x01,
+  0x01, 0x01, 0x00, 0x5a, 0x00, 0x5a, 0x00, 0x00, 0xff, 0xdb, 0x00, 0x43,
+  0x00, 0x50, 0x37, 0x3c, 0x46, 0x3c, 0x32, 0x50, 0x46, 0x41, 0x46, 0x5a,
+  0x55, 0x50, 0x5f, 0x78, 0xc8, 0x82, 0x78, 0x6e, 0x6e, 0x78, 0xf5, 0xaf,
+  0xb9, 0x91, 0xc8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdb, 0x00, 0x43, 0x01, 0x55, 0x5a,
+  0x5a, 0x78, 0x69, 0x78, 0xeb, 0x82, 0x82, 0xeb, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xc0, 0x00, 0x11, 0x08, 0x00, 0x10, 0x00, 0x10, 0x03,
+  0x01, 0x22, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xc4, 0x00,
+  0x16, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x02, 0xff, 0xc4, 0x00,
+  0x1e, 0x10, 0x00, 0x01, 0x05, 0x00, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x03, 0x11, 0x31,
+  0x04, 0x12, 0x51, 0x61, 0x71, 0xff, 0xc4, 0x00, 0x14, 0x01, 0x01, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0xff, 0xc4, 0x00, 0x14, 0x11, 0x01, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11,
+  0x00, 0x3f, 0x00, 0x18, 0xa0, 0x0e, 0x6d, 0xbc, 0xf5, 0xca, 0xf7, 0x78,
+  0xb6, 0xfe, 0x3b, 0x23, 0xb2, 0x1d, 0x64, 0x68, 0xf0, 0x8a, 0x39, 0x4b,
+  0x74, 0x9c, 0xa5, 0x5f, 0x35, 0x8a, 0xb2, 0x7e, 0xa0, 0xff, 0xd9, 0x00
+};
+
+guint wilber_jpg_len = 324;
+
+
+const guint8 *
+gimp_wilber_get_data (guint *length)
+{
+  *length = wilber_jpg_len;
+  return wilber_jpg;
+}
+
diff --git a/libgimpbase/gimpwilber.h b/libgimpbase/gimpwilber.h
new file mode 100644
index 0000000..ddd64e2
--- /dev/null
+++ b/libgimpbase/gimpwilber.h
@@ -0,0 +1,31 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpwilber.h
+ * Copyright (C) 2007 Sven Neumann <sven@gimp.org>
+ *
+ * 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 3 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, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_WILBER_H__
+#define __GIMP_WILBER_H__
+
+G_BEGIN_DECLS
+
+const guint8 *gimp_wilber_get_data   (guint *);
+
+G_END_DECLS
+
+#endif /* __GIMP_WILBER_H__ */
diff --git a/menus/image-menu.xml.in b/menus/image-menu.xml.in
index 26496ee..6214f5d 100644
--- a/menus/image-menu.xml.in
+++ b/menus/image-menu.xml.in
@@ -52,6 +52,7 @@
       <placeholder name="Send" />
       <separator />
       <placeholder name="Info" />
+        <menuitem action="file-show-metadata"/>
       <separator />
       <menuitem action="view-close" />
       <menuitem action="file-close-all" />
diff --git a/plug-ins/Makefile.am b/plug-ins/Makefile.am
index 50eb645..fd4062c 100644
--- a/plug-ins/Makefile.am
+++ b/plug-ins/Makefile.am
@@ -51,11 +51,6 @@ else
 file_uri = file-uri
 endif
 
-if HAVE_LIBEXIF
-metadata = metadata
-endif
-
-
 SUBDIRS = \
 	$(script_fu)		\
 	$(pygimp)		\
diff --git a/plug-ins/color-rotate/color-rotate-callbacks.c b/plug-ins/color-rotate/color-rotate-callbacks.c
index 5c96b75..ff948e5 100644
--- a/plug-ins/color-rotate/color-rotate-callbacks.c
+++ b/plug-ins/color-rotate/color-rotate-callbacks.c
@@ -87,7 +87,7 @@ rcm_360_degrees (GtkWidget *button,
 
   circle->angle->beta = circle->angle->alpha-circle->angle->cw_ccw * 0.001;
 
-  rcm_render_preview (Current.Bna->after);
+  rcm_render_preview (Current_c.Bna->after);
 }
 
 void
@@ -115,7 +115,7 @@ rcm_a_to_b (GtkWidget *button,
 
   SWAP (circle->angle->alpha, circle->angle->beta);
 
-  rcm_render_preview (Current.Bna->after);
+  rcm_render_preview (Current_c.Bna->after);
 }
 
 
@@ -131,14 +131,14 @@ rcm_spinbutton_to_degrees (GtkWidget *button,
   adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (button));
 
   gtk_adjustment_configure (adj,
-                            value * rcm_units_factor (Current.Units),
+                            value * rcm_units_factor (Current_c.Units),
                             gtk_adjustment_get_lower (adj), 360.0,
                             0.01, 1.0,
                             gtk_adjustment_get_page_size (adj));
 
   gtk_spin_button_set_digits (GTK_SPIN_BUTTON (button), 2);
 
-  gtk_label_set_text (GTK_LABEL (label), rcm_units_string (Current.Units));
+  gtk_label_set_text (GTK_LABEL (label), rcm_units_string (Current_c.Units));
 }
 
 void
@@ -147,27 +147,27 @@ rcm_switch_to_degrees (GtkWidget *button,
 {
   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
     {
-      Current.Units = DEGREES;
+      Current_c.Units = DEGREES;
 
-      rcm_spinbutton_to_degrees (Current.From->alpha_entry,
-                                 Current.From->angle->alpha,
-                                 Current.From->alpha_units_label);
+      rcm_spinbutton_to_degrees (Current_c.From->alpha_entry,
+                                 Current_c.From->angle->alpha,
+                                 Current_c.From->alpha_units_label);
 
-      rcm_spinbutton_to_degrees (Current.From->beta_entry,
-                                 Current.From->angle->beta,
-                                 Current.From->beta_units_label);
+      rcm_spinbutton_to_degrees (Current_c.From->beta_entry,
+                                 Current_c.From->angle->beta,
+                                 Current_c.From->beta_units_label);
 
-      rcm_spinbutton_to_degrees (Current.To->alpha_entry,
-                                 Current.To->angle->alpha,
-                                 Current.To->alpha_units_label);
+      rcm_spinbutton_to_degrees (Current_c.To->alpha_entry,
+                                 Current_c.To->angle->alpha,
+                                 Current_c.To->alpha_units_label);
 
-      rcm_spinbutton_to_degrees (Current.To->beta_entry,
-                                 Current.To->angle->beta,
-                                 Current.To->beta_units_label);
+      rcm_spinbutton_to_degrees (Current_c.To->beta_entry,
+                                 Current_c.To->angle->beta,
+                                 Current_c.To->beta_units_label);
 
-      rcm_spinbutton_to_degrees (Current.Gray->hue_entry,
-                                 Current.Gray->hue,
-                                 Current.Gray->hue_units_label);
+      rcm_spinbutton_to_degrees (Current_c.Gray->hue_entry,
+                                 Current_c.Gray->hue,
+                                 Current_c.Gray->hue_units_label);
     }
 }
 
@@ -181,14 +181,14 @@ rcm_spinbutton_to_radians (GtkWidget *button,
   adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (button));
 
   gtk_adjustment_configure (adj,
-                            value * rcm_units_factor (Current.Units),
+                            value * rcm_units_factor (Current_c.Units),
                             gtk_adjustment_get_lower (adj), TP,
                             0.0001, 0.001,
                             gtk_adjustment_get_page_size (adj));
 
   gtk_spin_button_set_digits (GTK_SPIN_BUTTON (button), 4);
 
-  gtk_label_set_text (GTK_LABEL (label), rcm_units_string (Current.Units));
+  gtk_label_set_text (GTK_LABEL (label), rcm_units_string (Current_c.Units));
 }
 
 void
@@ -197,27 +197,27 @@ rcm_switch_to_radians (GtkWidget *button,
 {
   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
     {
-      Current.Units = RADIANS;
+      Current_c.Units = RADIANS;
 
-      rcm_spinbutton_to_radians (Current.From->alpha_entry,
-                                 Current.From->angle->alpha,
-                                 Current.From->alpha_units_label);
+      rcm_spinbutton_to_radians (Current_c.From->alpha_entry,
+                                 Current_c.From->angle->alpha,
+                                 Current_c.From->alpha_units_label);
 
-      rcm_spinbutton_to_radians (Current.From->beta_entry,
-                                 Current.From->angle->beta,
-                                 Current.From->beta_units_label);
+      rcm_spinbutton_to_radians (Current_c.From->beta_entry,
+                                 Current_c.From->angle->beta,
+                                 Current_c.From->beta_units_label);
 
-      rcm_spinbutton_to_radians (Current.To->alpha_entry,
-                                 Current.To->angle->alpha,
-                                 Current.To->alpha_units_label);
+      rcm_spinbutton_to_radians (Current_c.To->alpha_entry,
+                                 Current_c.To->angle->alpha,
+                                 Current_c.To->alpha_units_label);
 
-      rcm_spinbutton_to_radians (Current.To->beta_entry,
-                                 Current.To->angle->beta,
-                                 Current.To->beta_units_label);
+      rcm_spinbutton_to_radians (Current_c.To->beta_entry,
+                                 Current_c.To->angle->beta,
+                                 Current_c.To->beta_units_label);
 
-      rcm_spinbutton_to_radians (Current.Gray->hue_entry,
-                                 Current.Gray->hue,
-                                 Current.Gray->hue_units_label);
+      rcm_spinbutton_to_radians (Current_c.Gray->hue_entry,
+                                 Current_c.Gray->hue,
+                                 Current_c.Gray->hue_units_label);
     }
 }
 
@@ -231,14 +231,14 @@ rcm_spinbutton_to_radians_over_PI (GtkWidget *button,
   adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (button));
 
   gtk_adjustment_configure (adj,
-                            value * rcm_units_factor (Current.Units),
+                            value * rcm_units_factor (Current_c.Units),
                             gtk_adjustment_get_lower (adj), 2.0,
                             0.0001, 0.001,
                             gtk_adjustment_get_page_size (adj));
 
   gtk_spin_button_set_digits (GTK_SPIN_BUTTON (button), 4);
 
-  gtk_label_set_text (GTK_LABEL (label), rcm_units_string (Current.Units));
+  gtk_label_set_text (GTK_LABEL (label), rcm_units_string (Current_c.Units));
 }
 
 void
@@ -247,27 +247,27 @@ rcm_switch_to_radians_over_PI (GtkWidget *button,
 {
   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
     {
-      Current.Units = RADIANS_OVER_PI;
+      Current_c.Units = RADIANS_OVER_PI;
 
-      rcm_spinbutton_to_radians_over_PI (Current.From->alpha_entry,
-                                         Current.From->angle->alpha,
-                                         Current.From->alpha_units_label);
+      rcm_spinbutton_to_radians_over_PI (Current_c.From->alpha_entry,
+                                         Current_c.From->angle->alpha,
+                                         Current_c.From->alpha_units_label);
 
-      rcm_spinbutton_to_radians_over_PI (Current.From->beta_entry,
-                                         Current.From->angle->beta,
-                                         Current.From->beta_units_label);
+      rcm_spinbutton_to_radians_over_PI (Current_c.From->beta_entry,
+                                         Current_c.From->angle->beta,
+                                         Current_c.From->beta_units_label);
 
-      rcm_spinbutton_to_radians_over_PI (Current.To->alpha_entry,
-                                         Current.To->angle->alpha,
-                                         Current.To->alpha_units_label);
+      rcm_spinbutton_to_radians_over_PI (Current_c.To->alpha_entry,
+                                         Current_c.To->angle->alpha,
+                                         Current_c.To->alpha_units_label);
 
-      rcm_spinbutton_to_radians_over_PI (Current.To->beta_entry,
-                                         Current.To->angle->beta,
-                                         Current.To->beta_units_label);
+      rcm_spinbutton_to_radians_over_PI (Current_c.To->beta_entry,
+                                         Current_c.To->angle->beta,
+                                         Current_c.To->beta_units_label);
 
-      rcm_spinbutton_to_radians_over_PI (Current.Gray->hue_entry,
-                                         Current.Gray->hue,
-                                         Current.Gray->hue_units_label);
+      rcm_spinbutton_to_radians_over_PI (Current_c.Gray->hue_entry,
+                                         Current_c.Gray->hue,
+                                         Current_c.Gray->hue_units_label);
     }
 }
 
@@ -280,9 +280,9 @@ rcm_switch_to_gray_to (GtkWidget *button,
 {
   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
     {
-      Current.Gray_to_from = GRAY_TO;
+      Current_c.Gray_to_from = GRAY_TO;
 
-      rcm_render_preview (Current.Bna->after);
+      rcm_render_preview (Current_c.Bna->after);
     }
 }
 
@@ -292,9 +292,9 @@ rcm_switch_to_gray_from (GtkWidget *button,
 {
   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
     {
-      Current.Gray_to_from = GRAY_FROM;
+      Current_c.Gray_to_from = GRAY_FROM;
 
-      rcm_render_preview (Current.Bna->after);
+      rcm_render_preview (Current_c.Bna->after);
     }
 }
 
@@ -305,7 +305,7 @@ void
 rcm_preview_as_you_drag (GtkWidget *button,
 			 gpointer  *value)
 {
-  Current.RealTime = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
+  Current_c.RealTime = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
 }
 
 void
@@ -316,15 +316,15 @@ rcm_combo_callback (GtkWidget *widget,
 
   gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget), &value);
 
-  Current.reduced = rcm_reduce_image (Current.drawable, Current.mask,
+  Current_c.reduced = rcm_reduce_image (Current_c.drawable, Current_c.mask,
                                       MAX_PREVIEW_SIZE, value);
 
-  gtk_widget_set_size_request (Current.Bna->before,
-                               Current.reduced->width,
-                               Current.reduced->height);
-  gtk_widget_set_size_request (Current.Bna->after,
-                               Current.reduced->width,
-                               Current.reduced->height);
+  gtk_widget_set_size_request (Current_c.Bna->before,
+                               Current_c.reduced->width,
+                               Current_c.reduced->height);
+  gtk_widget_set_size_request (Current_c.Bna->after,
+                               Current_c.reduced->width,
+                               Current_c.reduced->height);
 }
 
 
@@ -375,14 +375,14 @@ rcm_button_press_event (GtkWidget      *widget,
 
           gtk_spin_button_set_value (GTK_SPIN_BUTTON (circle->alpha_entry),
                                      circle->angle->alpha *
-                                     rcm_units_factor(Current.Units));
+                                     rcm_units_factor(Current_c.Units));
 
           gtk_spin_button_set_value (GTK_SPIN_BUTTON (circle->beta_entry),
                                      circle->angle->beta *
-                                     rcm_units_factor(Current.Units));
+                                     rcm_units_factor(Current_c.Units));
 
-          if (Current.RealTime)
-            rcm_render_preview (Current.Bna->after);
+          if (Current_c.RealTime)
+            rcm_render_preview (Current_c.Bna->after);
         }
     }
   else
@@ -396,7 +396,7 @@ rcm_release_event (GtkWidget      *widget,
 		   GdkEventButton *event,
 		   RcmCircle      *circle)
 {
-  rcm_render_preview (Current.Bna->after);
+  rcm_render_preview (Current_c.Bna->after);
 
   return TRUE;
 }
@@ -430,14 +430,14 @@ rcm_motion_notify_event (GtkWidget      *widget,
 
       gtk_spin_button_set_value (GTK_SPIN_BUTTON (circle->alpha_entry),
                                  circle->angle->alpha *
-                                 rcm_units_factor(Current.Units));
+                                 rcm_units_factor(Current_c.Units));
 
       gtk_spin_button_set_value (GTK_SPIN_BUTTON (circle->beta_entry),
                                  circle->angle->beta *
-                                 rcm_units_factor(Current.Units));
+                                 rcm_units_factor(Current_c.Units));
 
-      if (Current.RealTime)
-        rcm_render_preview (Current.Bna->after);
+      if (Current_c.RealTime)
+        rcm_render_preview (Current_c.Bna->after);
     }
 
   gdk_event_request_motions (event);
@@ -484,13 +484,13 @@ rcm_gray_button_press_event (GtkWidget      *widget,
   gtk_widget_queue_draw (circle->preview);
 
   gtk_spin_button_set_value (GTK_SPIN_BUTTON (circle->hue_entry),
-                             circle->hue * rcm_units_factor (Current.Units));
+                             circle->hue * rcm_units_factor (Current_c.Units));
 
   gtk_spin_button_set_value (GTK_SPIN_BUTTON (circle->satur_entry),
                              circle->satur);
 
-  if (Current.RealTime)
-    rcm_render_preview (Current.Bna->after);
+  if (Current_c.RealTime)
+    rcm_render_preview (Current_c.Bna->after);
 
   return TRUE;
 }
@@ -500,7 +500,7 @@ rcm_gray_release_event (GtkWidget      *widget,
 			GdkEventButton *event,
 			RcmGray        *circle)
 {
-  rcm_render_preview (Current.Bna->after);
+  rcm_render_preview (Current_c.Bna->after);
 
   return TRUE;
 }
@@ -524,13 +524,13 @@ rcm_gray_motion_notify_event (GtkWidget      *widget,
     circle->satur = 1;
 
   gtk_spin_button_set_value (GTK_SPIN_BUTTON (circle->hue_entry),
-                             circle->hue * rcm_units_factor(Current.Units));
+                             circle->hue * rcm_units_factor(Current_c.Units));
 
   gtk_spin_button_set_value (GTK_SPIN_BUTTON (circle->satur_entry),
                              circle->satur);
 
-  if (Current.RealTime)
-    rcm_render_preview (Current.Bna->after);
+  if (Current_c.RealTime)
+    rcm_render_preview (Current_c.Bna->after);
 
   gdk_event_request_motions (event);
 
@@ -545,11 +545,11 @@ rcm_set_alpha (GtkWidget *entry,
                RcmCircle *circle)
 {
   circle->angle->alpha = (gtk_spin_button_get_value (GTK_SPIN_BUTTON (entry)) /
-                          rcm_units_factor (Current.Units));
+                          rcm_units_factor (Current_c.Units));
 
   gtk_widget_queue_draw (circle->preview);
 
-  rcm_render_preview (Current.Bna->after);
+  rcm_render_preview (Current_c.Bna->after);
 }
 
 void
@@ -557,11 +557,11 @@ rcm_set_beta (GtkWidget *entry,
               RcmCircle *circle)
 {
   circle->angle->beta = (gtk_spin_button_get_value (GTK_SPIN_BUTTON (entry)) /
-                         rcm_units_factor(Current.Units));
+                         rcm_units_factor(Current_c.Units));
 
   gtk_widget_queue_draw (circle->preview);
 
-  rcm_render_preview (Current.Bna->after);
+  rcm_render_preview (Current_c.Bna->after);
 }
 
 void
@@ -569,11 +569,11 @@ rcm_set_hue (GtkWidget *entry,
              RcmGray   *circle)
 {
   circle->hue = (gtk_spin_button_get_value (GTK_SPIN_BUTTON (entry)) /
-                 rcm_units_factor(Current.Units));
+                 rcm_units_factor(Current_c.Units));
 
   gtk_widget_queue_draw (circle->preview);
 
-  rcm_render_preview (Current.Bna->after);
+  rcm_render_preview (Current_c.Bna->after);
 }
 
 void
@@ -584,7 +584,7 @@ rcm_set_satur (GtkWidget *entry,
 
   gtk_widget_queue_draw (circle->preview);
 
-  rcm_render_preview (Current.Bna->after);
+  rcm_render_preview (Current_c.Bna->after);
 }
 
 void
@@ -595,5 +595,5 @@ rcm_set_gray_sat (GtkWidget *entry,
 
   gtk_widget_queue_draw (circle->preview);
 
-  rcm_render_preview (Current.Bna->after);
+  rcm_render_preview (Current_c.Bna->after);
 }
diff --git a/plug-ins/color-rotate/color-rotate-dialog.c b/plug-ins/color-rotate/color-rotate-dialog.c
index 6ffc5a1..d072011 100644
--- a/plug-ins/color-rotate/color-rotate-dialog.c
+++ b/plug-ins/color-rotate/color-rotate-dialog.c
@@ -121,13 +121,13 @@ rcm_create_previews (void)
   gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
   gtk_widget_show (label);
 
-  frame = rcm_create_one_preview (&Current.Bna->before, ORIGINAL,
-                                  Current.reduced->width,
-                                  Current.reduced->height);
+  frame = rcm_create_one_preview (&Current_c.Bna->before, ORIGINAL,
+                                  Current_c.reduced->width,
+                                  Current_c.reduced->height);
   gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
   gtk_widget_show (frame);
 
-  g_signal_connect_after (Current.Bna->before, "size-allocate",
+  g_signal_connect_after (Current_c.Bna->before, "size-allocate",
                           G_CALLBACK (rcm_render_preview),
                           NULL);
 
@@ -139,13 +139,13 @@ rcm_create_previews (void)
   gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
   gtk_widget_show (label);
 
-  frame = rcm_create_one_preview (&Current.Bna->after, CURRENT,
-                                  Current.reduced->width,
-                                  Current.reduced->height);
+  frame = rcm_create_one_preview (&Current_c.Bna->after, CURRENT,
+                                  Current_c.reduced->width,
+                                  Current_c.reduced->height);
   gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
   gtk_widget_show (frame);
 
-  g_signal_connect_after (Current.Bna->after, "size-allocate",
+  g_signal_connect_after (Current_c.Bna->after, "size-allocate",
                           G_CALLBACK (rcm_render_preview),
                           NULL);
 
@@ -155,12 +155,12 @@ rcm_create_previews (void)
 
   button = gtk_check_button_new_with_label (_("Continuous update"));
   gtk_box_pack_start(GTK_BOX (vbox), button, FALSE, FALSE, 0);
-  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), Current.RealTime);
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), Current_c.RealTime);
   gtk_widget_show (button);
 
   g_signal_connect (button, "clicked",
                     G_CALLBACK (rcm_preview_as_you_drag),
-                    &(Current.RealTime));
+                    &(Current_c.RealTime));
 
   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
@@ -174,7 +174,7 @@ rcm_create_previews (void)
                                   _("Selection"),    SELECTION,
                                   _("Context"),      SELECTION_IN_CONTEXT,
                                   NULL);
-  gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), Current.Slctn);
+  gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), Current_c.Slctn);
 
   gtk_box_pack_start (GTK_BOX (hbox), combo, TRUE, TRUE, 0);
   gtk_widget_show (combo);
@@ -315,7 +315,7 @@ rcm_create_one_circle (gint         height,
                     st);
 
   /* label */
-  st->alpha_units_label = gtk_label_new (rcm_units_string (Current.Units));
+  st->alpha_units_label = gtk_label_new (rcm_units_string (Current_c.Units));
   gtk_widget_show (st->alpha_units_label);
 
   gtk_table_attach (GTK_TABLE (legend_table),
@@ -341,7 +341,7 @@ rcm_create_one_circle (gint         height,
                     st);
 
   /* label */
-  st->beta_units_label = gtk_label_new (rcm_units_string (Current.Units));
+  st->beta_units_label = gtk_label_new (rcm_units_string (Current_c.Units));
   gtk_widget_show (st->beta_units_label);
 
   gtk_table_attach (GTK_TABLE (legend_table), st->beta_units_label, 5,6, 0,1,
@@ -374,16 +374,16 @@ rcm_create_main (void)
 {
   GtkWidget *vbox;
 
-  Current.From = rcm_create_one_circle (SUM, C_("color-rotate", "From:"));
-  Current.To   = rcm_create_one_circle (SUM, C_("color-rotate", "To:"));
+  Current_c.From = rcm_create_one_circle (SUM, C_("color-rotate", "From:"));
+  Current_c.To   = rcm_create_one_circle (SUM, C_("color-rotate", "To:"));
 
   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
   gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
   gtk_widget_show (vbox);
 
-  gtk_box_pack_start (GTK_BOX (vbox), Current.From->frame,
+  gtk_box_pack_start (GTK_BOX (vbox), Current_c.From->frame,
 		      FALSE, FALSE, 0);
-  gtk_box_pack_start (GTK_BOX (vbox), Current.To->frame,
+  gtk_box_pack_start (GTK_BOX (vbox), Current_c.To->frame,
 		      FALSE, FALSE, 0);
 
   return vbox;
@@ -407,7 +407,7 @@ rcm_create_gray (void)
   RcmGray   *st;
   GtkAdjustment *adj;
 
-  Current.Gray = st = g_new (RcmGray, 1);
+  Current_c.Gray = st = g_new (RcmGray, 1);
   st->hue   = 0;
   st->satur = 0;
 
@@ -483,7 +483,7 @@ rcm_create_gray (void)
                     st);
 
   /* Gray: Circle: units label */
-  st->hue_units_label = gtk_label_new (rcm_units_string (Current.Units));
+  st->hue_units_label = gtk_label_new (rcm_units_string (Current_c.Units));
   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
   gtk_table_attach (GTK_TABLE (table), st->hue_units_label, 2, 3, 0, 1,
 		    GTK_FILL, GTK_FILL, 0, 0);
@@ -524,24 +524,24 @@ rcm_create_gray (void)
   gtk_box_pack_start (GTK_BOX (radio_box), button, FALSE, FALSE, 0);
   gtk_widget_show (button);
 
-  if (Current.Gray_to_from == GRAY_FROM)
+  if (Current_c.Gray_to_from == GRAY_FROM)
     gtk_button_clicked (GTK_BUTTON (button));
 
   g_signal_connect (button, "clicked",
                     G_CALLBACK (rcm_switch_to_gray_from),
-                    &(Current.Gray_to_from));
+                    &(Current_c.Gray_to_from));
 
   button = gtk_radio_button_new_with_label (group, _("Change to this"));
   group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button));
   gtk_box_pack_start (GTK_BOX (radio_box), button, FALSE, FALSE, 0);
   gtk_widget_show (button);
 
-  if (Current.Gray_to_from == GRAY_TO)
+  if (Current_c.Gray_to_from == GRAY_TO)
     gtk_button_clicked (GTK_BUTTON (button));
 
   g_signal_connect (button, "clicked",
                     G_CALLBACK (rcm_switch_to_gray_to),
-                    &(Current.Gray_to_from));
+                    &(Current_c.Gray_to_from));
 
   /** Gray: What is gray? **/
   frame = gimp_frame_new (_("Gray Threshold"));
@@ -598,7 +598,7 @@ rcm_create_units (void)
   gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
   gtk_widget_show (button);
 
-  if (Current.Units == RADIANS)
+  if (Current_c.Units == RADIANS)
     gtk_button_clicked (GTK_BUTTON (button));
 
   g_signal_connect (button, "clicked",
@@ -610,7 +610,7 @@ rcm_create_units (void)
   gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
   gtk_widget_show (button);
 
-  if (Current.Units == RADIANS_OVER_PI)
+  if (Current_c.Units == RADIANS_OVER_PI)
     gtk_button_clicked (GTK_BUTTON (button));
 
   g_signal_connect (button, "clicked",
@@ -621,7 +621,7 @@ rcm_create_units (void)
   gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
   gtk_widget_show (button);
 
-  if (Current.Units == DEGREES)
+  if (Current_c.Units == DEGREES)
     gtk_button_clicked (GTK_BUTTON (button));
 
   g_signal_connect (button, "clicked",
@@ -643,7 +643,7 @@ color_rotate_dialog (void)
   GtkWidget *previews;
   gboolean   run;
 
-  Current.Bna = g_new (RcmBna, 1);
+  Current_c.Bna = g_new (RcmBna, 1);
 
   gimp_ui_init (PLUG_IN_BINARY, TRUE);
   color_rotate_stock_init ();
@@ -664,13 +664,13 @@ color_rotate_dialog (void)
 
   gimp_window_set_transient (GTK_WINDOW (dialog));
 
-  Current.Bna->dlg = dialog;
+  Current_c.Bna->dlg = dialog;
 
   /* Create sub-dialogs */
-  Current.reduced = rcm_reduce_image (Current.drawable, Current.mask,
-                                      MAX_PREVIEW_SIZE, Current.Slctn);
+  Current_c.reduced = rcm_reduce_image (Current_c.drawable, Current_c.mask,
+                                      MAX_PREVIEW_SIZE, Current_c.Slctn);
 
-  Current.Bna->bna_frame = previews = rcm_create_previews ();
+  Current_c.Bna->bna_frame = previews = rcm_create_previews ();
 
   /* H-Box */
   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
@@ -698,9 +698,9 @@ color_rotate_dialog (void)
 
   gtk_widget_show (dialog);
 
-  rcm_render_circle (Current.From->preview, SUM, MARGIN);
-  rcm_render_circle (Current.To->preview, SUM, MARGIN);
-  rcm_render_circle (Current.Gray->preview, GRAY_SUM, GRAY_MARGIN);
+  rcm_render_circle (Current_c.From->preview, SUM, MARGIN);
+  rcm_render_circle (Current_c.To->preview, SUM, MARGIN);
+  rcm_render_circle (Current_c.Gray->preview, GRAY_SUM, GRAY_MARGIN);
 
   run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
 
diff --git a/plug-ins/color-rotate/color-rotate-utils.c b/plug-ins/color-rotate/color-rotate-utils.c
index 1fe8da7..38750d0 100644
--- a/plug-ins/color-rotate/color-rotate-utils.c
+++ b/plug-ins/color-rotate/color-rotate-utils.c
@@ -169,7 +169,7 @@ rcm_angle_inside_slice (float     angle,
 gboolean
 rcm_is_gray (float s)
 {
-  return (s <= Current.Gray->gray_sat);
+  return (s <= Current_c.Gray->gray_sat);
 }
 
 
@@ -328,10 +328,10 @@ rcm_render_preview (GtkWidget *preview)
 
   version = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (preview), "mode"));
 
-  reduced   = Current.reduced;
+  reduced   = Current_c.reduced;
   RW        = reduced->width;
   RH        = reduced->height;
-  bytes     = Current.drawable->bpp;
+  bytes     = Current_c.drawable->bpp;
   hsv_array = reduced->hsv;
   rgb_array = reduced->rgb;
 
@@ -355,14 +355,14 @@ rcm_render_preview (GtkWidget *preview)
 
               if (rcm_is_gray(S) && (reduced->mask[i*RW+j] != 0))
                 {
-                  switch (Current.Gray_to_from)
+                  switch (Current_c.Gray_to_from)
                     {
                     case GRAY_FROM:
-                      if (rcm_angle_inside_slice (Current.Gray->hue,
-                                                  Current.From->angle) <= 1)
+                      if (rcm_angle_inside_slice (Current_c.Gray->hue,
+                                                  Current_c.From->angle) <= 1)
                         {
-                          H = Current.Gray->hue/TP;
-                          S = Current.Gray->satur;
+                          H = Current_c.Gray->hue/TP;
+                          S = Current_c.Gray->satur;
                         }
                       else
                         skip = TRUE;
@@ -372,8 +372,8 @@ rcm_render_preview (GtkWidget *preview)
                       unchanged = FALSE;
                       skip = TRUE;
                       gimp_hsv_to_rgb4 (rgb,
-                                        Current.Gray->hue/TP,
-                                        Current.Gray->satur,
+                                        Current_c.Gray->hue/TP,
+                                        Current_c.Gray->satur,
                                         V);
                       break;
 
@@ -385,10 +385,10 @@ rcm_render_preview (GtkWidget *preview)
               if (!skip)
                 {
                   unchanged = FALSE;
-                  H = rcm_linear (rcm_left_end (Current.From->angle),
-                                  rcm_right_end (Current.From->angle),
-                                  rcm_left_end (Current.To->angle),
-                                  rcm_right_end (Current.To->angle),
+                  H = rcm_linear (rcm_left_end (Current_c.From->angle),
+                                  rcm_right_end (Current_c.From->angle),
+                                  rcm_left_end (Current_c.To->angle),
+                                  rcm_right_end (Current_c.To->angle),
                                   H * TP);
 
                   H = angle_mod_2PI(H) / TP;
diff --git a/plug-ins/color-rotate/color-rotate.c b/plug-ins/color-rotate/color-rotate.c
index e4056ab..b811fa4 100644
--- a/plug-ins/color-rotate/color-rotate.c
+++ b/plug-ins/color-rotate/color-rotate.c
@@ -62,7 +62,7 @@ static void  color_rotate (GimpDrawable     *drawable);
 
 /* Global variables */
 
-RcmParams Current =
+RcmParams Current_c =
 {
   SELECTION,           /* SELECTION ONLY */
   TRUE,                /* REAL TIME */
@@ -129,18 +129,18 @@ run (const gchar      *name,
   values[0].type          = GIMP_PDB_STATUS;
   values[0].data.d_status = status;
 
-  Current.drawable = gimp_drawable_get (param[2].data.d_drawable);
-  Current.mask = gimp_drawable_get (gimp_image_get_selection (param[1].data.d_image));
+  Current_c.drawable = gimp_drawable_get (param[2].data.d_drawable);
+  Current_c.mask = gimp_drawable_get (gimp_image_get_selection (param[1].data.d_image));
 
-  if (gimp_drawable_is_rgb (Current.drawable->drawable_id))
+  if (gimp_drawable_is_rgb (Current_c.drawable->drawable_id))
     {
       if (color_rotate_dialog ())
         {
           gimp_progress_init (_("Rotating the colors"));
 
-          gimp_tile_cache_ntiles (2 * (Current.drawable->width /
+          gimp_tile_cache_ntiles (2 * (Current_c.drawable->width /
                                        gimp_tile_width () + 1));
-          color_rotate (Current.drawable);
+          color_rotate (Current_c.drawable);
           gimp_displays_flush ();
         }
       else
@@ -156,7 +156,7 @@ run (const gchar      *name,
   values[0].data.d_status = status;
 
   if (status == GIMP_PDB_SUCCESS)
-    gimp_drawable_detach (Current.drawable);
+    gimp_drawable_detach (Current_c.drawable);
 }
 
 
@@ -185,13 +185,13 @@ color_rotate_row (const guchar *src_row,
 
       if (rcm_is_gray (S))
         {
-          if (Current.Gray_to_from == GRAY_FROM)
+          if (Current_c.Gray_to_from == GRAY_FROM)
             {
-              if (rcm_angle_inside_slice (Current.Gray->hue,
-                                          Current.From->angle) <= 1)
+              if (rcm_angle_inside_slice (Current_c.Gray->hue,
+                                          Current_c.From->angle) <= 1)
                 {
-                  H = Current.Gray->hue / TP;
-                  S = Current.Gray->satur;
+                  H = Current_c.Gray->hue / TP;
+                  S = Current_c.Gray->satur;
                 }
               else
                 {
@@ -201,17 +201,17 @@ color_rotate_row (const guchar *src_row,
           else
             {
               skip = TRUE;
-              gimp_hsv_to_rgb4 (rgb, Current.Gray->hue / TP,
-                                Current.Gray->satur, V);
+              gimp_hsv_to_rgb4 (rgb, Current_c.Gray->hue / TP,
+                                Current_c.Gray->satur, V);
             }
         }
 
       if (! skip)
         {
-          H = rcm_linear (rcm_left_end (Current.From->angle),
-                          rcm_right_end (Current.From->angle),
-                          rcm_left_end (Current.To->angle),
-                          rcm_right_end (Current.To->angle),
+          H = rcm_linear (rcm_left_end (Current_c.From->angle),
+                          rcm_right_end (Current_c.From->angle),
+                          rcm_left_end (Current_c.To->angle),
+                          rcm_right_end (Current_c.To->angle),
                           H * TP);
 
           H = angle_mod_2PI (H) / TP;
diff --git a/plug-ins/color-rotate/color-rotate.h b/plug-ins/color-rotate/color-rotate.h
index 883efc3..fe3698a 100644
--- a/plug-ins/color-rotate/color-rotate.h
+++ b/plug-ins/color-rotate/color-rotate.h
@@ -133,4 +133,4 @@ typedef struct
 
 /* Global variables */
 
-extern RcmParams Current;
+extern RcmParams Current_c;
diff --git a/plug-ins/common/file-png.c b/plug-ins/common/file-png.c
index f6f8a60..ddee08f 100644
--- a/plug-ins/common/file-png.c
+++ b/plug-ins/common/file-png.c
@@ -88,6 +88,10 @@ typedef struct
   gboolean  comment;
   gboolean  save_transp_pixels;
   gint      compression_level;
+  gboolean  exif;
+  gboolean  xmp;
+  gboolean  iptc;
+  gboolean  thumb;
 }
 PngSaveVals;
 
@@ -104,6 +108,10 @@ typedef struct
   GtkWidget *comment;
   GtkWidget *save_transp_pixels;
   GtkAdjustment *compression_level;
+  GtkWidget *exif;
+  GtkWidget *xmp;
+  GtkWidget *iptc;
+  GtkWidget *thumb;
 }
 PngSaveGui;
 
@@ -119,6 +127,14 @@ typedef struct
 }
 PngGlobals;
 
+typedef struct
+{
+  gboolean  save_exif;
+  gboolean  save_xmp;
+  gboolean  save_iptc;
+  gboolean  save_thumbnail;
+} ExifSaveHandler;
+
 /*
  * Local functions...
  */
@@ -163,6 +179,10 @@ static gint      find_unused_ia_color      (GeglBuffer       *buffer,
 static void      load_defaults             (void);
 static void      save_defaults             (void);
 static void      load_gui_defaults         (PngSaveGui       *pg);
+void             load_exif_defaults     (gint32           image,
+                                         ExifSaveHandler *exif_save_handler);
+void             save_exif_defaults     (gint32           image,
+                                         ExifSaveHandler  exif_save_handler);
 
 /*
  * Globals...
@@ -186,7 +206,19 @@ static const PngSaveVals defaults =
   TRUE,
   TRUE,
   TRUE,
-  9
+  9,
+  TRUE,                /* save exif       */
+  TRUE,                /* save xmp        */
+  TRUE,                /* save iptc        */
+  TRUE                 /* save thumbnail  */
+};
+
+static ExifSaveHandler exifvals =
+{
+  TRUE,                /* save exif       */
+  TRUE,                /* save xmp        */
+  TRUE,                /* save iptc        */
+  TRUE                 /* save thumbnail  */
 };
 
 static PngSaveVals pngvals;
@@ -486,6 +518,8 @@ run (const gchar      *name,
           if (! alpha)
             pngvals.bkgd = FALSE;
 
+          load_exif_defaults (orig_image_ID, &exifvals);
+
           /*
            * Then acquire information with a dialog...
            */
@@ -538,6 +572,7 @@ run (const gchar      *name,
            * Possibly retrieve data...
            */
           gimp_get_data (SAVE_PROC, &pngvals);
+          load_exif_defaults (orig_image_ID, &exifvals);
           break;
 
         default:
@@ -549,6 +584,7 @@ run (const gchar      *name,
           if (save_image (param[3].data.d_string,
                           image_ID, drawable_ID, orig_image_ID, &error))
             {
+              save_exif_defaults (image_ID, exifvals);
               gimp_set_data (SAVE_PROC, &pngvals, sizeof (pngvals));
             }
           else
@@ -2080,6 +2116,18 @@ save_dialog (gint32    image_ID,
   pg.time = toggle_button_init (builder, "save-creation-time",
                                 pngvals.time,
                                 &pngvals.time);
+  pg.exif = toggle_button_init (builder, "sv_exif",
+                                pngvals.exif,
+                                &pngvals.exif);
+  pg.xmp = toggle_button_init (builder, "sv_xmp",
+                                pngvals.xmp,
+                                &pngvals.xmp);
+  pg.iptc = toggle_button_init (builder, "sv_iptc",
+                                pngvals.iptc,
+                                &pngvals.iptc);
+  pg.thumb = toggle_button_init (builder, "sv_thumbnail",
+                                pngvals.thumb,
+                                &pngvals.thumb);
 
   /* Comment toggle */
   parasite = gimp_image_get_parasite (image_ID, "gimp-comment");
@@ -2163,7 +2211,7 @@ load_defaults (void)
 
       gimp_parasite_free (parasite);
 
-      num_fields = sscanf (def_str, "%d %d %d %d %d %d %d %d %d",
+      num_fields = sscanf (def_str, "%d %d %d %d %d %d %d %d %d %d %d %d %d",
                            &tmpvals.interlaced,
                            &tmpvals.bkgd,
                            &tmpvals.gama,
@@ -2172,11 +2220,15 @@ load_defaults (void)
                            &tmpvals.time,
                            &tmpvals.comment,
                            &tmpvals.save_transp_pixels,
-                           &tmpvals.compression_level);
+                           &tmpvals.compression_level,
+                           &tmpvals.exif,
+                           &tmpvals.xmp,
+                           &tmpvals.iptc,
+                           &tmpvals.thumb);
 
       g_free (def_str);
 
-      if (num_fields == 9)
+      if (num_fields == 13)
         {
           memcpy (&pngvals, &tmpvals, sizeof (tmpvals));
           return;
@@ -2192,7 +2244,7 @@ save_defaults (void)
   GimpParasite *parasite;
   gchar        *def_str;
 
-  def_str = g_strdup_printf ("%d %d %d %d %d %d %d %d %d",
+  def_str = g_strdup_printf ("%d %d %d %d %d %d %d %d %d %d %d %d %d",
                              pngvals.interlaced,
                              pngvals.bkgd,
                              pngvals.gama,
@@ -2201,7 +2253,11 @@ save_defaults (void)
                              pngvals.time,
                              pngvals.comment,
                              pngvals.save_transp_pixels,
-                             pngvals.compression_level);
+                             pngvals.compression_level,
+                             pngvals.exif,
+                             pngvals.xmp,
+                             pngvals.iptc,
+                             pngvals.thumb);
 
   parasite = gimp_parasite_new (PNG_DEFAULTS_PARASITE,
                                 GIMP_PARASITE_PERSISTENT,
@@ -2236,3 +2292,77 @@ load_gui_defaults (PngSaveGui *pg)
   gtk_adjustment_set_value (pg->compression_level,
                             pngvals.compression_level);
 }
+
+void
+load_exif_defaults (gint32 image, ExifSaveHandler *exif_save_handler)
+{
+  GimpParasite *parasite;
+  gchar        *def_str;
+  gint          num_fields;
+  gint          e_save;
+  gint          x_save;
+  gint          i_save;
+  gint          t_save;
+
+  exif_save_handler->save_exif      = pngvals.exif  = TRUE;
+  exif_save_handler->save_xmp       = pngvals.xmp   = TRUE;
+  exif_save_handler->save_iptc      = pngvals.iptc  = TRUE;
+  exif_save_handler->save_thumbnail = pngvals.thumb = TRUE;
+
+  parasite = gimp_image_get_parasite (image, "exif-handling-data");
+
+  if (! parasite)
+    return;
+
+  def_str = g_strndup (gimp_parasite_data (parasite),
+                       gimp_parasite_data_size (parasite));
+
+  gimp_parasite_free (parasite);
+
+  num_fields = sscanf (def_str, "%d %d %d %d",
+                       &e_save,
+                       &x_save,
+                       &i_save,
+                       &t_save);
+
+  if (num_fields != 4)
+    return;
+
+  if(e_save == 0)
+    exif_save_handler->save_exif = FALSE;
+  if(x_save == 0)
+    exif_save_handler->save_xmp = FALSE;
+  if(i_save == 0)
+    exif_save_handler->save_iptc = FALSE;
+  if(t_save == 0)
+    exif_save_handler->save_thumbnail = FALSE;
+
+  g_free (def_str);
+}
+
+void
+save_exif_defaults (gint32 image, ExifSaveHandler exif_save_handler)
+{
+  GimpParasite *parasite;
+  gchar        *def_str;
+
+  exif_save_handler.save_exif      = pngvals.exif;
+  exif_save_handler.save_xmp       = pngvals.xmp;
+  exif_save_handler.save_iptc      = pngvals.iptc;
+  exif_save_handler.save_thumbnail = pngvals.thumb;
+
+  def_str = g_strdup_printf ("%d %d %d %d",
+                             exif_save_handler.save_exif ? 1 : 0,
+                             exif_save_handler.save_xmp ? 1 : 0,
+                             exif_save_handler.save_iptc ? 1 : 0,
+                             exif_save_handler.save_thumbnail ? 1 : 0);
+
+  parasite = gimp_parasite_new ("exif-handling-data",
+                                GIMP_PARASITE_PERSISTENT,
+                                strlen (def_str), def_str);
+
+  gimp_image_attach_parasite (image, parasite);
+
+  gimp_parasite_free (parasite);
+  g_free (def_str);
+}
diff --git a/plug-ins/common/file-tiff-save.c b/plug-ins/common/file-tiff-save.c
index c5a18e9..f9b501d 100644
--- a/plug-ins/common/file-tiff-save.c
+++ b/plug-ins/common/file-tiff-save.c
@@ -93,6 +93,14 @@ typedef struct
   guchar       *pixel;
 } channel_data;
 
+typedef struct
+{
+  gboolean  save_exif;
+  gboolean  save_xmp;
+  gboolean  save_iptc;
+  gboolean  save_thumbnail;
+} ExifSaveHandler;
+
 /* Declare some local functions.
  */
 static void   query     (void);
@@ -133,6 +141,11 @@ static TIFF     *tiff_open              (const gchar *filename,
                                          const gchar *mode,
                                          GError     **error);
 
+void             load_exif_defaults     (gint32           image,
+                                         ExifSaveHandler *exif_save_handler);
+void             save_exif_defaults     (gint32           image,
+                                         ExifSaveHandler  exif_save_handler);
+
 const GimpPlugInInfo PLUG_IN_INFO =
 {
   NULL,  /* init_proc  */
@@ -147,6 +160,14 @@ static TiffSaveVals tsvals =
   TRUE,                /*  alpha handling */
 };
 
+static ExifSaveHandler exifvals =
+{
+  TRUE,                /* save exif       */
+  TRUE,                /* save xmp        */
+  TRUE,                /* save iptc        */
+  TRUE                 /* save thumbnail  */
+};
+
 static gchar       *image_comment = NULL;
 static GimpRunMode  run_mode      = GIMP_RUN_INTERACTIVE;
 
@@ -292,6 +313,8 @@ run (const gchar      *name,
             }
           gimp_parasite_free (parasite);
 
+          load_exif_defaults (orig_image, &exifvals);
+
           /*  First acquire information with a dialog  */
           if (! save_dialog (gimp_drawable_has_alpha (drawable),
                              image_is_monochrome (image)))
@@ -338,6 +361,9 @@ run (const gchar      *name,
               tsvals.save_transp_pixels = pvals->save_transp_pixels;
             }
           gimp_parasite_free (parasite);
+
+          load_exif_defaults (orig_image, &exifvals);
+
           break;
 
         default:
@@ -350,6 +376,7 @@ run (const gchar      *name,
                           &error))
             {
               /*  Store mvals data  */
+              save_exif_defaults (orig_image, exifvals);
               gimp_set_data (SAVE_PROC, &tsvals, sizeof (TiffSaveVals));
             }
           else
@@ -1075,25 +1102,44 @@ static gboolean
 save_dialog (gboolean has_alpha,
              gboolean is_monochrome)
 {
-  GtkWidget *dialog;
-  GtkWidget *vbox;
-  GtkWidget *frame;
-  GtkWidget *hbox;
-  GtkWidget *label;
-  GtkWidget *entry;
-  GtkWidget *toggle;
-  GtkWidget *g3;
-  GtkWidget *g4;
-  gboolean   run;
+  GError      *error = NULL;
+  GtkWidget   *dialog;
+  GtkWidget   *vbox;
+  GtkWidget   *frame;
+  GtkWidget   *entry;
+  GtkWidget   *toggle;
+  GtkWidget   *cmp_g3;
+  GtkWidget   *cmp_g4;
+  GtkBuilder  *builder;
+  gchar       *ui_file;
+  gboolean     run;
 
   dialog = gimp_export_dialog_new (_("TIFF"), PLUG_IN_BINARY, SAVE_PROC);
 
-  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
-  gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+  builder = gtk_builder_new ();
+  ui_file = g_build_filename (gimp_data_directory (),
+                              "ui/plug-ins/plug-in-file-tiff.ui",
+                              NULL);
+  if (! gtk_builder_add_from_file (builder, ui_file, &error))
+    {
+      gchar *display_name = g_filename_display_name (ui_file);
+
+      g_printerr (_("Error loading UI file '%s': %s"),
+                  display_name, error ? error->message : _("Unknown error"));
+
+      g_free (display_name);
+    }
+
+  g_free (ui_file);
+
+  vbox = GTK_WIDGET( gtk_builder_get_object( builder, "tiff_export_vbox" ) );
+
   gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)),
-                      vbox, FALSE, TRUE, 0);
+                      vbox, TRUE, TRUE, 0);
+  gtk_widget_show (vbox);
+
+  vbox = GTK_WIDGET( gtk_builder_get_object( builder, "radio_button_box" ) );
 
-  /*  compression  */
   frame = gimp_int_radio_group_new (TRUE, _("Compression"),
                                     G_CALLBACK (gimp_radio_button_update),
                                     &tsvals.compression, tsvals.compression,
@@ -1103,20 +1149,21 @@ save_dialog (gboolean has_alpha,
                                     _("_Pack Bits"), COMPRESSION_PACKBITS, NULL,
                                     _("_Deflate"),   COMPRESSION_DEFLATE,  NULL,
                                     _("_JPEG"),      COMPRESSION_JPEG,     NULL,
-                                    _("CCITT Group _3 fax"), COMPRESSION_CCITTFAX3, &g3,
-                                    _("CCITT Group _4 fax"), COMPRESSION_CCITTFAX4, &g4,
+                                    _("CCITT Group _3 fax"), COMPRESSION_CCITTFAX3, &cmp_g3,
+                                    _("CCITT Group _4 fax"), COMPRESSION_CCITTFAX4, &cmp_g4,
 
                                     NULL);
 
-  gtk_widget_set_sensitive (g3, is_monochrome);
-  gtk_widget_set_sensitive (g4, is_monochrome);
+
+  gtk_widget_set_sensitive (cmp_g3, is_monochrome);
+  gtk_widget_set_sensitive (cmp_g4, is_monochrome);
 
   if (! is_monochrome)
     {
       if (tsvals.compression == COMPRESSION_CCITTFAX3 ||
           tsvals.compression ==  COMPRESSION_CCITTFAX4)
         {
-          gimp_int_radio_group_set_active (GTK_RADIO_BUTTON (g3),
+          gimp_int_radio_group_set_active (GTK_RADIO_BUTTON (cmp_g3),
                                            COMPRESSION_NONE);
         }
     }
@@ -1124,40 +1171,49 @@ save_dialog (gboolean has_alpha,
   gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
   gtk_widget_show (frame);
 
-  /* Keep colors behind alpha mask */
-  toggle = gtk_check_button_new_with_mnemonic
-    ( _("Save _color values from transparent pixels"));
+  toggle = GTK_WIDGET( gtk_builder_get_object( builder, "sv_alpha" ) );
   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
                                 has_alpha && tsvals.save_transp_pixels);
   gtk_widget_set_sensitive (toggle, has_alpha);
-  gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
-  gtk_widget_show (toggle);
-
   g_signal_connect (toggle, "toggled",
                     G_CALLBACK (gimp_toggle_button_update),
                     &tsvals.save_transp_pixels);
 
-  /* comment entry */
-  hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
-  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
-  gtk_widget_show (hbox);
-
-  label = gtk_label_new ( _("Comment:"));
-  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
-  gtk_widget_show (label);
-
-  entry = gtk_entry_new ();
-  gtk_widget_show (entry);
-  gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
+  entry = GTK_WIDGET( gtk_builder_get_object( builder, "commentfield" ) );
   gtk_entry_set_text (GTK_ENTRY (entry), image_comment ? image_comment : "");
 
   g_signal_connect (entry, "changed",
                     G_CALLBACK (comment_entry_callback),
                     NULL);
 
-  gtk_widget_show (frame);
+  toggle = GTK_WIDGET( gtk_builder_get_object( builder, "sv_exif" ) );
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+                                exifvals.save_exif);
+  g_signal_connect (toggle, "toggled",
+                    G_CALLBACK (gimp_toggle_button_update),
+                    &exifvals.save_exif);
+
+  toggle = GTK_WIDGET( gtk_builder_get_object( builder, "sv_xmp" ) );
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+                                exifvals.save_xmp);
+  g_signal_connect (toggle, "toggled",
+                    G_CALLBACK (gimp_toggle_button_update),
+                    &exifvals.save_xmp);
+
+  toggle = GTK_WIDGET( gtk_builder_get_object( builder, "sv_iptc" ) );
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+                                exifvals.save_iptc);
+  g_signal_connect (toggle, "toggled",
+                    G_CALLBACK (gimp_toggle_button_update),
+                    &exifvals.save_iptc);
+
+  toggle = GTK_WIDGET( gtk_builder_get_object( builder, "sv_thumbnail" ) );
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+                                exifvals.save_thumbnail);
+  g_signal_connect (toggle, "toggled",
+                    G_CALLBACK (gimp_toggle_button_update),
+                    &exifvals.save_thumbnail);
 
-  gtk_widget_show (vbox);
   gtk_widget_show (dialog);
 
   run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
@@ -1217,3 +1273,69 @@ byte2bit (const guchar *byteline,
       *bitline = invert ? ~bitval & (0xff << (8 - width)) : bitval;
     }
 }
+
+void
+load_exif_defaults (gint32 image, ExifSaveHandler *exif_save_handler)
+{
+  GimpParasite *parasite;
+  gchar        *def_str;
+  gint          num_fields;
+  gint          e_save;
+  gint          x_save;
+  gint          i_save;
+  gint          t_save;
+
+  exif_save_handler->save_exif      = TRUE;
+  exif_save_handler->save_xmp       = TRUE;
+  exif_save_handler->save_iptc      = TRUE;
+  exif_save_handler->save_thumbnail = TRUE;
+
+  parasite = gimp_image_get_parasite (image, "exif-handling-data");
+
+  if (! parasite)
+    return;
+
+  def_str = g_strndup (gimp_parasite_data (parasite),
+                       gimp_parasite_data_size (parasite));
+
+  gimp_parasite_free (parasite);
+
+  num_fields = sscanf (def_str, "%d %d %d %d",
+                       &e_save,
+                       &x_save,
+                       &i_save,
+                       &t_save);
+
+  if(e_save == 0)
+    exif_save_handler->save_exif = FALSE;
+  if(x_save == 0)
+    exif_save_handler->save_xmp = FALSE;
+  if(i_save == 0)
+    exif_save_handler->save_iptc = FALSE;
+  if(t_save == 0)
+    exif_save_handler->save_thumbnail = FALSE;
+
+  g_free (def_str);
+}
+
+void
+save_exif_defaults (gint32 image, ExifSaveHandler exif_save_handler)
+{
+  GimpParasite *parasite;
+  gchar        *def_str;
+
+  def_str = g_strdup_printf ("%d %d %d %d",
+                             exif_save_handler.save_exif ? 1 : 0,
+                             exif_save_handler.save_xmp ? 1 : 0,
+                             exif_save_handler.save_iptc ? 1 : 0,
+                             exif_save_handler.save_thumbnail ? 1 : 0);
+
+  parasite = gimp_parasite_new ("exif-handling-data",
+                                GIMP_PARASITE_PERSISTENT,
+                                strlen (def_str), def_str);
+
+  gimp_image_attach_parasite (image, parasite);
+
+  gimp_parasite_free (parasite);
+  g_free (def_str);
+}
diff --git a/plug-ins/file-jpeg/Makefile.am b/plug-ins/file-jpeg/Makefile.am
index 5b3f2ae..a55dc9e 100644
--- a/plug-ins/file-jpeg/Makefile.am
+++ b/plug-ins/file-jpeg/Makefile.am
@@ -45,14 +45,6 @@ file_jpeg_SOURCES = \
 	jpeg-settings.c \
 	jpeg-settings.h
 
-if HAVE_LIBEXIF
-file_jpeg_SOURCES += \
-	jpeg-exif.c	\
-	jpeg-exif.h	\
-	gimpexif.c	\
-	gimpexif.h
-endif
-
 file_jpeg_LDADD = \
 	$(libgimpui)		\
 	$(libgimpwidgets)	\
@@ -69,11 +61,6 @@ file_jpeg_LDADD = \
 	$(INTLLIBS)		\
 	$(file_jpeg_RC)
 
-if HAVE_LIBEXIF
-file_jpeg_LDADD += \
-	$(EXIF_LIBS)
-endif
-
 noinst_PROGRAMS = jpegqual
 
 jpegqual_SOURCES = \
diff --git a/plug-ins/file-jpeg/gimpexif.c b/plug-ins/file-jpeg/gimpexif.c
deleted file mode 100644
index ab14a99..0000000
--- a/plug-ins/file-jpeg/gimpexif.c
+++ /dev/null
@@ -1,160 +0,0 @@
-/* GIMP - The GNU Image Manipulation Program
- * Copyright (C) 1995 Spencer Kimball and Peter Mattis
- *
- * 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 3 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, see <http://www.gnu.org/licenses/>.
- */
-
-/*
- * EXIF-handling code for the metadata library.
- */
-
-#include "config.h"
-
-#include <stdlib.h>
-
-#include <libgimp/gimp.h>
-
-#include <libexif/exif-data.h>
-#include <libexif/exif-content.h>
-#include <libexif/exif-utils.h>
-
-#include "gimpexif.h"
-
-
-#define EXIF_HEADER_SIZE 8
-
-/*
- * gimp_metadata_store_exif:
- * @image_ID:    the GIMP image to work on.
- * @exif_data:   the Exif data that is to be stored.
- *
- * This function is supposed to load the "gimp-metadata" parasite
- * (which is in XMP format), parse it, add the exif information,
- * reformat it into XMP, and store the result as the new parasite.
- * The infrastructure to do this is not yet available, so for the
- * moment it does something much simpler -- it calls upon libexif
- * to serialize the exif data, and stores the result in a parasite
- * called "exif-data".
- */
-void gimp_metadata_store_exif    (gint32    image_ID,
-                                  ExifData *exif_data)
-{
-  GimpParam    *return_vals;
-  gint          nreturn_vals;
-  GimpParasite *parasite      = NULL;
-  guchar       *exif_buf      = NULL;
-  guint         exif_buf_len  = 0;
-
-  exif_data_save_data (exif_data, &exif_buf, &exif_buf_len);
-
-  if (exif_buf_len > EXIF_HEADER_SIZE)
-    {
-      parasite = gimp_parasite_new ("exif-data",
-                                    GIMP_PARASITE_PERSISTENT,
-                                    exif_buf_len, exif_buf);
-      gimp_image_attach_parasite (image_ID, parasite);
-      gimp_parasite_free (parasite);
-    }
-  return_vals = gimp_run_procedure ("plug-in-metadata-decode-exif",
-                                    &nreturn_vals,
-                                    GIMP_PDB_IMAGE,      image_ID,
-                                    GIMP_PDB_INT32,      7,
-                                    GIMP_PDB_INT8ARRAY,  "unused",
-                                    GIMP_PDB_END);
-  if (return_vals[0].data.d_status != GIMP_PDB_SUCCESS)
-    g_warning ("JPEG Exif -> XMP Merge failed");
-
-  free (exif_buf);
-}
-
-/*
- * gimp_metadata_generate_exif:
- * @image_ID:   the ID of the GIMP image to work on.
- *
- * This function is supposed to load the "gimp-metadata" parasite
- * (which is a block of XMP info), parse it, extract the exif
- * values, and build an ExifData structure from them.
- * The infrastructure to do this is not yet available, so for the
- * moment it does something much simpler -- it loads the "exif-data"
- * parasite (which is a serialized representation of the exif data),
- * and calls upon libexif to parse it into an ExifData struct.
- *
- * returns: The reconstructed exif data, or NULL if none exists.
- */
-ExifData *
-gimp_metadata_generate_exif (gint32 image_ID)
-{
-  ExifData     *exif_data;
-  GimpParasite *parasite = gimp_image_get_parasite (image_ID, "exif-data");
-
-  if (parasite)
-    {
-      exif_data = exif_data_new_from_data (gimp_parasite_data (parasite),
-                                           gimp_parasite_data_size (parasite));
-
-      gimp_parasite_free (parasite);
-      return exif_data;
-    }
-
-  return NULL;
-}
-
-/*
- * gimp_exif_content_get_value:
- * @content:   ExifContent block from which to get value
- * @tag:       Tag whose value to get
- * @value:     Place to put the result
- * @maxlen:    Maximum size of returned string
- *
- * This function is a wrapper around the libexif function
- * exif_content_get_value(), necessary to deal with an incompatible
- * API change.  It looks up the value of the specified tag,
- * returning the result as a human-readable string.  Note that
- * @value must be pre-allocated.
- */
-const gchar *
-gimp_exif_content_get_value (ExifContent *content,
-                             ExifTag      tag,
-                             gchar       *value,
-                             gint         maxlen)
-{
-  ExifEntry *entry = exif_content_get_entry (content, tag);
-
-  if (entry)
-    return exif_entry_get_value (entry, value, maxlen);
-  else
-    return NULL;
-}
-
-/*
- * gimp_exif_data_remove_entry:
- * @data:      ExifData pointer
- * @ifd:       Index of the ifd holding the entry to be removed
- * @tag:       Tag of the entry to be removed
- *
- * This is a convenience function for removing a specified
- * entry from an exif data structure.  If no such entry
- * exists, the function returns without doing anything.
- */
-void
-gimp_exif_data_remove_entry (ExifData *exif_data,
-                             ExifIfd   ifd,
-                             ExifTag   tag)
-{
-  ExifEntry  *entry = exif_content_get_entry (exif_data->ifd[ifd],
-                                              tag);
-
-  if (entry)
-    exif_content_remove_entry (exif_data->ifd[ifd], entry);
-}
diff --git a/plug-ins/file-jpeg/gimpexif.h b/plug-ins/file-jpeg/gimpexif.h
deleted file mode 100644
index 5ceaaef..0000000
--- a/plug-ins/file-jpeg/gimpexif.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/* GIMP - The GNU Image Manipulation Program
- * Copyright (C) 1995 Spencer Kimball and Peter Mattis
- *
- * 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 3 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, see <http://www.gnu.org/licenses/>.
- */
-
-/*
- * EXIF-handling code for the metadata library.
- */
-
-#ifndef __GIMP_EXIF_H__
-#define __GIMP_EXIF_H__
-
-void          gimp_metadata_store_exif    (gint32       image_ID,
-                                           ExifData    *exif_data);
-
-ExifData *    gimp_metadata_generate_exif (gint32       image_ID);
-
-const gchar * gimp_exif_content_get_value (ExifContent *content,
-                                           ExifTag      tag,
-                                           gchar       *value,
-                                           gint         maxlen);
-
-void          gimp_exif_data_remove_entry (ExifData    *exif_data,
-                                           ExifIfd      ifd,
-                                           ExifTag      tag);
-
-#endif /* __GIMP_EXIF_H__ */
diff --git a/plug-ins/file-jpeg/jpeg-exif.c b/plug-ins/file-jpeg/jpeg-exif.c
deleted file mode 100644
index bc6787d..0000000
--- a/plug-ins/file-jpeg/jpeg-exif.c
+++ /dev/null
@@ -1,458 +0,0 @@
-/* GIMP - The GNU Image Manipulation Program
- * Copyright (C) 1995 Spencer Kimball and Peter Mattis
- *
- * 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 3 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, see <http://www.gnu.org/licenses/>.
- */
-
-/*
- * EXIF-handling code for the jpeg plugin.  May eventually be better
- * to move this stuff into libgimpbase or a new libgimpmetadata and
- * make it available for other plugins.
- */
-
-#include "config.h"
-
-#include <stdio.h>
-#include <string.h>
-#include <setjmp.h>
-
-#include <jpeglib.h>
-#include <jerror.h>
-
-#include <libexif/exif-content.h>
-#include <libexif/exif-data.h>
-#include <libexif/exif-utils.h>
-
-#include <libgimp/gimp.h>
-#include <libgimp/gimpui.h>
-
-#include "jpeg-exif.h"
-#include "gimpexif.h"
-#include "jpeg.h"
-#include "jpeg-settings.h"
-
-#include "libgimp/stdplugins-intl.h"
-
-
-#define THUMBNAIL_SIZE             128
-#define JPEG_EXIF_ROTATE_PARASITE  "exif-orientation-rotate"
-
-
-static gboolean  jpeg_exif_rotate_query_dialog (gint32 image_ID);
-
-
-/*  Replacement for exif_data_new_from_file() to work around
- *  filename encoding problems (see bug #335391).
- */
-ExifData *
-jpeg_exif_data_new_from_file (const gchar  *filename,
-                              GError      **error)
-{
-  ExifData    *data;
-  GMappedFile *file;
-
-  g_return_val_if_fail (filename != NULL, NULL);
-  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
-
-  file = g_mapped_file_new (filename, FALSE, error);
-  if (! file)
-    return NULL;
-
-  data = exif_data_new_from_data ((guchar *) g_mapped_file_get_contents (file),
-                                  g_mapped_file_get_length (file));
-
-  g_mapped_file_unref (file);
-
-  return data;
-}
-
-
-gint
-jpeg_exif_get_orientation (ExifData *exif_data)
-{
-  ExifEntry *entry;
-  gint       byte_order = exif_data_get_byte_order (exif_data);
-
-  /* get orientation and rotate image accordingly if necessary */
-  if ((entry = exif_content_get_entry (exif_data->ifd[EXIF_IFD_0],
-                                       EXIF_TAG_ORIENTATION)))
-    {
-      return exif_get_short (entry->data, byte_order);
-    }
-
-  return 0;
-}
-
-
-gboolean
-jpeg_exif_get_resolution (ExifData *exif_data,
-                          gdouble  *xresolution,
-                          gdouble  *yresolution,
-                          gint     *unit)
-{
-  gboolean       success;
-  ExifEntry     *entry;
-  gint           byte_order;
-  gdouble        xres;
-  gdouble        yres;
-  gint           ruint;
-  ExifRational   r;
-
-  success = FALSE;
-  byte_order = exif_data_get_byte_order (exif_data);
-
-  do
-    {
-      entry = exif_content_get_entry (exif_data->ifd[EXIF_IFD_0],
-                                      EXIF_TAG_X_RESOLUTION);
-      if (!entry)
-        break;
-
-      r = exif_get_rational (entry->data, byte_order);
-      if (r.denominator == 0.0)
-        break;
-      xres = r.numerator / r.denominator;
-
-      entry = exif_content_get_entry (exif_data->ifd[EXIF_IFD_0],
-                                      EXIF_TAG_Y_RESOLUTION);
-      if (!entry)
-        break;
-
-      r = exif_get_rational (entry->data, byte_order);
-      if (r.denominator == 0.0)
-        break;
-      yres = r.numerator / r.denominator;
-
-      entry = exif_content_get_entry (exif_data->ifd[EXIF_IFD_0],
-                                      EXIF_TAG_RESOLUTION_UNIT);
-      if (!entry)
-        break;
-
-      ruint = exif_get_short (entry->data, byte_order);
-      if ((ruint != 2) && /* inches */
-          (ruint != 3))   /* centimetres */
-        break;
-
-      success = TRUE;
-    }
-  while (0);
-
-  if (success)
-    {
-      *xresolution = xres;
-      *yresolution = yres;
-      *unit = ruint;
-    }
-
-  return success;
-}
-
-
-void
-jpeg_setup_exif_for_save (ExifData     *exif_data,
-                          const gint32  image_ID)
-{
-  ExifRational  r;
-  gdouble       xres, yres;
-  ExifEntry    *entry;
-  gint          byte_order = exif_data_get_byte_order (exif_data);
-
-  /* set orientation to top - left */
-  if ((entry = exif_content_get_entry (exif_data->ifd[EXIF_IFD_0],
-                                       EXIF_TAG_ORIENTATION)))
-    {
-      exif_set_short (entry->data, byte_order, (ExifShort) 1);
-    }
-
-  /* set x and y resolution */
-  gimp_image_get_resolution (image_ID, &xres, &yres);
-  r.numerator =   xres;
-  r.denominator = 1;
-  if ((entry = exif_content_get_entry (exif_data->ifd[EXIF_IFD_0],
-                                       EXIF_TAG_X_RESOLUTION)))
-    {
-      exif_set_rational (entry->data, byte_order, r);
-    }
-  r.numerator = yres;
-  if ((entry = exif_content_get_entry (exif_data->ifd[EXIF_IFD_0],
-                                       EXIF_TAG_Y_RESOLUTION)))
-    {
-      exif_set_rational (entry->data, byte_order, r);
-    }
-
-  /* set resolution unit, always inches */
-  if ((entry = exif_content_get_entry (exif_data->ifd[EXIF_IFD_0],
-                                       EXIF_TAG_RESOLUTION_UNIT)))
-    {
-      exif_set_short (entry->data, byte_order, (ExifShort) 2);
-    }
-
-  /* set software to "GIMP" and include the version number */
-  if ((entry = exif_content_get_entry (exif_data->ifd[EXIF_IFD_0],
-                                       EXIF_TAG_SOFTWARE)))
-    {
-      const gchar *name = "GIMP " GIMP_VERSION;
-
-      entry->data = (guchar *) g_strdup (name);
-      entry->size = strlen (name) + 1;
-      entry->components = entry->size;
-    }
-
-  /* set the width and height */
-  if ((entry = exif_content_get_entry (exif_data->ifd[EXIF_IFD_EXIF],
-                                       EXIF_TAG_PIXEL_X_DIMENSION)))
-    {
-      exif_set_long (entry->data, byte_order,
-                     (ExifLong) gimp_image_width (image_ID));
-    }
-  if ((entry = exif_content_get_entry (exif_data->ifd[EXIF_IFD_EXIF],
-                                       EXIF_TAG_PIXEL_Y_DIMENSION)))
-    {
-      exif_set_long (entry->data, byte_order,
-                     (ExifLong) gimp_image_height (image_ID));
-    }
-
-  /*
-   * set the date & time image was saved
-   * note, date & time of original photo is stored elsewwhere, we
-   * aren't losing it.
-   */
-  if ((entry = exif_content_get_entry (exif_data->ifd[EXIF_IFD_0],
-                                       EXIF_TAG_DATE_TIME)))
-    {
-      /* small memory leak here */
-      entry->data = NULL;
-      exif_entry_initialize (entry, EXIF_TAG_DATE_TIME);
-    }
-
-  /* should set components configuration, don't know how */
-
-  /*
-   * remove entries that don't apply to jpeg
-   * (may have come from tiff or raw)
-   */
-  gimp_exif_data_remove_entry (exif_data, EXIF_IFD_0, EXIF_TAG_COMPRESSION);
-  gimp_exif_data_remove_entry (exif_data, EXIF_IFD_0, EXIF_TAG_IMAGE_WIDTH);
-  gimp_exif_data_remove_entry (exif_data, EXIF_IFD_0, EXIF_TAG_IMAGE_LENGTH);
-  gimp_exif_data_remove_entry (exif_data, EXIF_IFD_0, EXIF_TAG_BITS_PER_SAMPLE);
-  gimp_exif_data_remove_entry (exif_data, EXIF_IFD_0, EXIF_TAG_SAMPLES_PER_PIXEL);
-  gimp_exif_data_remove_entry (exif_data, EXIF_IFD_0, EXIF_TAG_PHOTOMETRIC_INTERPRETATION);
-  gimp_exif_data_remove_entry (exif_data, EXIF_IFD_0, EXIF_TAG_STRIP_OFFSETS);
-  gimp_exif_data_remove_entry (exif_data, EXIF_IFD_0, EXIF_TAG_PLANAR_CONFIGURATION);
-  gimp_exif_data_remove_entry (exif_data, EXIF_IFD_0, EXIF_TAG_YCBCR_SUB_SAMPLING);
-
-  /* should set thumbnail attributes */
-}
-
-void
-jpeg_exif_rotate (gint32 image_ID,
-                  gint   orientation)
-{
-  switch (orientation)
-    {
-    case 1:  /* standard orientation, do nothing */
-      break;
-
-    case 2:  /* flipped right-left               */
-      gimp_image_flip (image_ID, GIMP_ORIENTATION_HORIZONTAL);
-      break;
-
-    case 3:  /* rotated 180                      */
-      gimp_image_rotate (image_ID, GIMP_ROTATE_180);
-      break;
-
-    case 4:  /* flipped top-bottom               */
-      gimp_image_flip (image_ID, GIMP_ORIENTATION_VERTICAL);
-      break;
-
-    case 5:  /* flipped diagonally around '\'    */
-      gimp_image_rotate (image_ID, GIMP_ROTATE_90);
-      jpeg_swap_original_settings (image_ID);
-      gimp_image_flip (image_ID, GIMP_ORIENTATION_HORIZONTAL);
-      break;
-
-    case 6:  /* 90 CW                            */
-      gimp_image_rotate (image_ID, GIMP_ROTATE_90);
-      jpeg_swap_original_settings (image_ID);
-      break;
-
-    case 7:  /* flipped diagonally around '/'    */
-      gimp_image_rotate (image_ID, GIMP_ROTATE_90);
-      jpeg_swap_original_settings (image_ID);
-      gimp_image_flip (image_ID, GIMP_ORIENTATION_VERTICAL);
-      break;
-
-    case 8:  /* 90 CCW                           */
-      gimp_image_rotate (image_ID, GIMP_ROTATE_270);
-      jpeg_swap_original_settings (image_ID);
-      break;
-
-    default: /* shouldn't happen                 */
-      break;
-    }
-}
-
-void
-jpeg_exif_rotate_query (gint32 image_ID,
-                        gint   orientation)
-{
-  GimpParasite *parasite;
-  gboolean      query = load_interactive;
-
-  if (orientation < 2 || orientation > 8)
-    return;
-
-  parasite = gimp_get_parasite (JPEG_EXIF_ROTATE_PARASITE);
-
-  if (parasite)
-    {
-      if (strncmp (gimp_parasite_data (parasite), "yes",
-                   gimp_parasite_data_size (parasite)) == 0)
-        {
-          query = FALSE;
-        }
-      else if (strncmp (gimp_parasite_data (parasite), "no",
-                        gimp_parasite_data_size (parasite)) == 0)
-        {
-          gimp_parasite_free (parasite);
-          return;
-        }
-
-      gimp_parasite_free (parasite);
-    }
-
-  if (query && ! jpeg_exif_rotate_query_dialog (image_ID))
-    return;
-
-  jpeg_exif_rotate (image_ID, orientation);
-}
-
-static gboolean
-jpeg_exif_rotate_query_dialog (gint32 image_ID)
-{
-  GtkWidget *dialog;
-  GtkWidget *hbox;
-  GtkWidget *vbox;
-  GtkWidget *label;
-  GtkWidget *toggle;
-  GdkPixbuf *pixbuf;
-  gint       response;
-
-  dialog = gimp_dialog_new (_("Rotate Image?"), PLUG_IN_ROLE,
-                            NULL, 0, NULL, NULL,
-
-                            _("_Keep Orientation"), GTK_RESPONSE_CANCEL,
-                            GIMP_STOCK_TOOL_ROTATE, GTK_RESPONSE_OK,
-
-                            NULL);
-
-  gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
-                                           GTK_RESPONSE_OK,
-                                           GTK_RESPONSE_CANCEL,
-                                           -1);
-
-  gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
-  gimp_window_set_transient (GTK_WINDOW (dialog));
-
-  hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
-  gtk_container_set_border_width (GTK_CONTAINER (hbox), 12);
-  gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
-                      hbox, FALSE, FALSE, 0);
-  gtk_widget_show (hbox);
-
-  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
-  gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
-  gtk_widget_show (vbox);
-
-  pixbuf = gimp_image_get_thumbnail (image_ID,
-                                     THUMBNAIL_SIZE, THUMBNAIL_SIZE,
-                                     GIMP_PIXBUF_SMALL_CHECKS);
-
-  if (pixbuf)
-    {
-      GtkWidget *image;
-      gchar     *name;
-
-      image = gtk_image_new_from_pixbuf (pixbuf);
-      g_object_unref (pixbuf);
-
-      gtk_box_pack_start (GTK_BOX (vbox), image, FALSE, FALSE, 0);
-      gtk_widget_show (image);
-
-      name = gimp_image_get_name (image_ID);
-
-      label = gtk_label_new (name);
-      gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_MIDDLE);
-      gimp_label_set_attributes (GTK_LABEL (label),
-                                 PANGO_ATTR_STYLE,  PANGO_STYLE_ITALIC,
-                                 -1);
-      gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
-      gtk_widget_show (label);
-
-      g_free (name);
-    }
-
-  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
-  gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
-  gtk_widget_show (vbox);
-
-  label = g_object_new (GTK_TYPE_LABEL,
-                        "label",   _("According to the EXIF data, "
-                                     "this image is rotated."),
-                        "wrap",    TRUE,
-                        "justify", GTK_JUSTIFY_LEFT,
-                        "xalign",  0.0,
-                        "yalign",  0.5,
-                        NULL);
-  gimp_label_set_attributes (GTK_LABEL (label),
-                             PANGO_ATTR_SCALE,  PANGO_SCALE_LARGE,
-                             PANGO_ATTR_WEIGHT, PANGO_WEIGHT_BOLD,
-                             -1);
-  gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
-  gtk_widget_show (label);
-
-  label = g_object_new (GTK_TYPE_LABEL,
-                        "label",   _("Would you like GIMP to rotate it "
-                                     "into the standard orientation?"),
-                        "wrap",    TRUE,
-                        "justify", GTK_JUSTIFY_LEFT,
-                        "xalign",  0.0,
-                        "yalign",  0.5,
-                        NULL);
-  gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
-  gtk_widget_show (label);
-
-  toggle = gtk_check_button_new_with_mnemonic (_("_Don't ask me again"));
-  gtk_box_pack_end (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
-  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), FALSE);
-  gtk_widget_show (toggle);
-
-  response = gimp_dialog_run (GIMP_DIALOG (dialog));
-
-  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (toggle)))
-    {
-      GimpParasite *parasite;
-      const gchar  *str = (response == GTK_RESPONSE_OK) ? "yes" : "no";
-
-      parasite = gimp_parasite_new (JPEG_EXIF_ROTATE_PARASITE,
-                                    GIMP_PARASITE_PERSISTENT,
-                                    strlen (str), str);
-      gimp_attach_parasite (parasite);
-      gimp_parasite_free (parasite);
-    }
-
-  gtk_widget_destroy (dialog);
-
-  return (response == GTK_RESPONSE_OK);
-}
diff --git a/plug-ins/file-jpeg/jpeg-exif.h b/plug-ins/file-jpeg/jpeg-exif.h
deleted file mode 100644
index 3e9a29f..0000000
--- a/plug-ins/file-jpeg/jpeg-exif.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* GIMP - The GNU Image Manipulation Program
- * Copyright (C) 1995 Spencer Kimball and Peter Mattis
- *
- * 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 3 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, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef __JPEG_EXIF_H__
-#define __JPEG_EXIF_H__
-
-extern ExifData *exif_data;
-
-ExifData * jpeg_exif_data_new_from_file (const gchar   *filename,
-                                         GError       **error);
-
-gint      jpeg_exif_get_orientation     (ExifData      *exif_data);
-
-gboolean  jpeg_exif_get_resolution      (ExifData       *exif_data,
-                                         gdouble        *xresolution,
-                                         gdouble        *yresolution,
-                                         gint           *unit);
-
-void      jpeg_setup_exif_for_save      (ExifData      *exif_data,
-                                         const gint32   image_ID);
-
-void      jpeg_exif_rotate              (gint32         image_ID,
-                                         gint           orientation);
-void      jpeg_exif_rotate_query        (gint32         image_ID,
-                                         gint           orientation);
-
-#endif /* __JPEG_EXIF_H__ */
diff --git a/plug-ins/file-jpeg/jpeg-load.c b/plug-ins/file-jpeg/jpeg-load.c
index 41cd2e4..ea2a13e 100644
--- a/plug-ins/file-jpeg/jpeg-load.c
+++ b/plug-ins/file-jpeg/jpeg-load.c
@@ -26,10 +26,6 @@
 #include <jpeglib.h>
 #include <jerror.h>
 
-#ifdef HAVE_LIBEXIF
-#include <libexif/exif-data.h>
-#endif /* HAVE_LIBEXIF */
-
 #ifdef HAVE_LCMS
 #include <lcms2.h>
 #endif
@@ -43,21 +39,11 @@
 #include "jpeg-icc.h"
 #include "jpeg-settings.h"
 #include "jpeg-load.h"
-#ifdef HAVE_LIBEXIF
-#include "jpeg-exif.h"
-#include "gimpexif.h"
-#endif
-
 
 static void  jpeg_load_resolution           (gint32    image_ID,
                                              struct jpeg_decompress_struct
                                                        *cinfo);
 
-#ifdef HAVE_LIBEXIF
-static gboolean  jpeg_load_exif_resolution  (gint32    image_ID,
-                                             ExifData *exif_data);
-#endif
-
 static void      jpeg_load_sanitize_comment (gchar    *comment);
 
 static gpointer  jpeg_load_cmyk_transform   (guint8   *profile_data,
@@ -90,9 +76,6 @@ load_image (const gchar  *filename,
   gint             tile_height;
   gint             scanlines;
   gint             i, start, end;
-#ifdef HAVE_LIBEXIF
-  gint             orientation = 0;
-#endif
 #ifdef HAVE_LCMS
   cmsHTRANSFORM    cmyk_transform = NULL;
 #else
@@ -265,9 +248,6 @@ load_image (const gchar  *filename,
       GString  *comment_buffer = NULL;
       guint8   *profile        = NULL;
       guint     profile_size   = 0;
-#ifdef HAVE_LIBEXIF
-      ExifData *exif_data      = NULL;
-#endif
 
       /* Step 5.0: save the original JPEG settings in a parasite */
       jpeg_detect_original_settings (&cinfo, image_ID);
@@ -304,18 +284,9 @@ load_image (const gchar  *filename,
               g_print ("jpeg-load: found EXIF block (%d bytes)\n",
                        (gint) (len - sizeof (JPEG_APP_HEADER_EXIF)));
 #endif
-#ifdef HAVE_LIBEXIF
-              if (! exif_data)
-                exif_data = exif_data_new ();
-              /* if there are multiple blocks, their data will be merged */
-              exif_data_load_data (exif_data, (unsigned char *) data, len);
-#endif
             }
         }
 
-#ifdef HAVE_LIBEXIF
-      if (!jpeg_load_exif_resolution (image_ID, exif_data))
-#endif
         jpeg_load_resolution (image_ID, &cinfo);
 
       /* if we found any comments, then make a parasite for them */
@@ -334,55 +305,6 @@ load_image (const gchar  *filename,
           g_string_free (comment_buffer, TRUE);
         }
 
-#ifdef HAVE_LIBEXIF
-      /* if we found any EXIF block, then attach the metadata to the image */
-      if (exif_data)
-        {
-          gimp_metadata_store_exif (image_ID, exif_data);
-          orientation = jpeg_exif_get_orientation (exif_data);
-          exif_data_unref (exif_data);
-          exif_data = NULL;
-        }
-#endif
-
-      /* Step 5.2: check for XMP metadata in APP1 markers (after EXIF) */
-      for (marker = cinfo.marker_list; marker; marker = marker->next)
-        {
-          const gchar *data = (const gchar *) marker->data;
-          gsize        len  = marker->data_length;
-
-          if ((marker->marker == JPEG_APP0 + 1)
-              && (len > sizeof (JPEG_APP_HEADER_XMP) + 20)
-              && ! strcmp (JPEG_APP_HEADER_XMP, data))
-            {
-              GimpParam *return_vals;
-              gint       nreturn_vals;
-              gchar     *xmp_packet;
-
-#ifdef GIMP_UNSTABLE
-              g_print ("jpeg-load: found XMP packet (%d bytes)\n",
-                       (gint) (len - sizeof (JPEG_APP_HEADER_XMP)));
-#endif
-              xmp_packet = g_strndup (data + sizeof (JPEG_APP_HEADER_XMP),
-                                      len - sizeof (JPEG_APP_HEADER_XMP));
-
-              /* FIXME: running this through the PDB is not very efficient */
-              return_vals = gimp_run_procedure ("plug-in-metadata-decode-xmp",
-                                                &nreturn_vals,
-                                                GIMP_PDB_IMAGE, image_ID,
-                                                GIMP_PDB_STRING, xmp_packet,
-                                                GIMP_PDB_END);
-
-              if (return_vals[0].data.d_status != GIMP_PDB_SUCCESS)
-                {
-                  g_warning ("JPEG - unable to decode XMP metadata packet");
-                }
-
-              gimp_destroy_params (return_vals, nreturn_vals);
-              g_free (xmp_packet);
-            }
-        }
-
       /* Step 5.3: check for an embedded ICC profile in APP2 markers */
       jpeg_icc_read_profile (&cinfo, &profile, &profile_size);
 
@@ -489,10 +411,6 @@ load_image (const gchar  *filename,
 
   gimp_image_insert_layer (image_ID, layer_ID, -1, 0);
 
-#ifdef HAVE_LIBEXIF
-  jpeg_exif_rotate_query (image_ID, orientation);
-#endif
-
   return image_ID;
 }
 
@@ -537,53 +455,6 @@ jpeg_load_resolution (gint32                         image_ID,
     }
 }
 
-#ifdef HAVE_LIBEXIF
-
-static gboolean
-jpeg_load_exif_resolution (gint32        image_ID,
-                           ExifData     *exif_data)
-{
-  gboolean success;
-  gdouble xresolution;
-  gdouble yresolution;
-  gint    unit;
-
-  if (!exif_data)
-    return FALSE;
-
-  if (!jpeg_exif_get_resolution (exif_data,
-                                 &xresolution,
-                                 &yresolution,
-                                 &unit))
-    return FALSE;
-
-  switch (unit)
-    {
-    case 2:
-      success = TRUE;
-      break;
-    case 3: /* dots per cm */
-      xresolution *= 2.54;
-      yresolution *= 2.54;
-      gimp_image_set_unit (image_ID, GIMP_UNIT_MM);
-      success = TRUE;
-      break;
-    default:
-      g_warning ("Unknown EXIF resolution unit %d; skipping EXIF resolution.",
-                 unit);
-      success = FALSE;
-    }
-
-  if (success)
-    {
-      gimp_image_set_resolution (image_ID, xresolution, yresolution);
-    }
-
-  return success;
-}
-
-#endif /* HAVE_LIBEXIF */
-
 /*
  * A number of JPEG files have comments written in a local character set
  * instead of UTF-8.  Some of these files may have been saved by older
@@ -608,372 +479,6 @@ jpeg_load_sanitize_comment (gchar *comment)
     }
 }
 
-
-#ifdef HAVE_LIBEXIF
-
-typedef struct
-{
-  struct jpeg_source_mgr pub;   /* public fields */
-
-  guchar  *buffer;
-  gint     size;
-  JOCTET   terminal[2];
-} my_source_mgr;
-
-typedef my_source_mgr * my_src_ptr;
-
-static void
-init_source (j_decompress_ptr cinfo)
-{
-}
-
-
-static boolean
-fill_input_buffer (j_decompress_ptr cinfo)
-{
-  my_src_ptr src = (my_src_ptr) cinfo->src;
-
-  /* Since we have given all we have got already
-   * we simply fake an end of file
-   */
-
-  src->pub.next_input_byte = src->terminal;
-  src->pub.bytes_in_buffer = 2;
-  src->terminal[0]         = (JOCTET) 0xFF;
-  src->terminal[1]         = (JOCTET) JPEG_EOI;
-
-  return TRUE;
-}
-
-static void
-skip_input_data (j_decompress_ptr cinfo,
-                 long             num_bytes)
-{
-  my_src_ptr src = (my_src_ptr) cinfo->src;
-
-  src->pub.next_input_byte = src->pub.next_input_byte + num_bytes;
-}
-
-static void
-term_source (j_decompress_ptr cinfo)
-{
-}
-
-gint32
-load_thumbnail_image (const gchar   *filename,
-                      gint          *width,
-                      gint          *height,
-                      GimpImageType *type,
-                      GError       **error)
-{
-  gint32 volatile  image_ID;
-  ExifData        *exif_data;
-  gint32           layer_ID;
-  struct jpeg_decompress_struct cinfo;
-  struct my_error_mgr           jerr;
-  guchar          *buf;
-  guchar         **rowbuf;
-  GimpImageBaseType image_type;
-  GimpImageType    layer_type;
-  GeglBuffer      *buffer = NULL;
-  gint             tile_height;
-  gint             scanlines;
-  gint             i, start, end;
-  gint             orientation;
-  my_src_ptr       src;
-  FILE            *infile;
-
-  image_ID = -1;
-  exif_data = jpeg_exif_data_new_from_file (filename, NULL);
-
-  if (! ((exif_data) && (exif_data->data) && (exif_data->size > 0)))
-    return -1;
-
-  orientation = jpeg_exif_get_orientation (exif_data);
-
-  cinfo.err = jpeg_std_error (&jerr.pub);
-  jerr.pub.error_exit     = my_error_exit;
-  jerr.pub.output_message = my_output_message;
-
-  gimp_progress_init_printf (_("Opening thumbnail for '%s'"),
-                             gimp_filename_to_utf8 (filename));
-
-  /* Establish the setjmp return context for my_error_exit to use. */
-  if (setjmp (jerr.setjmp_buffer))
-    {
-      /* If we get here, the JPEG code has signaled an error.  We
-       * need to clean up the JPEG object, close the input file,
-       * and return.
-       */
-      jpeg_destroy_decompress (&cinfo);
-
-      if (image_ID != -1)
-        gimp_image_delete (image_ID);
-
-      if (exif_data)
-        {
-          exif_data_unref (exif_data);
-          exif_data = NULL;
-        }
-
-      if (buffer)
-        g_object_unref (buffer);
-
-      return -1;
-    }
-
-  /* Now we can initialize the JPEG decompression object. */
-  jpeg_create_decompress (&cinfo);
-
-  /* Step 2: specify data source (eg, a file) */
-
-  if (cinfo.src == NULL)
-    cinfo.src = (struct jpeg_source_mgr *)(*cinfo.mem->alloc_small)
-      ((j_common_ptr) &cinfo, JPOOL_PERMANENT,
-       sizeof (my_source_mgr));
-
-  src = (my_src_ptr) cinfo.src;
-
-  src->pub.init_source       = init_source;
-  src->pub.fill_input_buffer = fill_input_buffer;
-  src->pub.skip_input_data   = skip_input_data;
-  src->pub.resync_to_restart = jpeg_resync_to_restart;
-  src->pub.term_source       = term_source;
-
-  src->pub.bytes_in_buffer   = exif_data->size;
-  src->pub.next_input_byte   = exif_data->data;
-
-  src->buffer = exif_data->data;
-  src->size = exif_data->size;
-
-  /* Step 3: read file parameters with jpeg_read_header() */
-
-  jpeg_read_header (&cinfo, TRUE);
-
-  /* Step 4: set parameters for decompression */
-
-  /* In this example, we don't need to change any of the defaults set by
-   * jpeg_read_header(), so we do nothing here.
-   */
-
-  /* Step 5: Start decompressor */
-
-  jpeg_start_decompress (&cinfo);
-
-  /* We may need to do some setup of our own at this point before
-   * reading the data.  After jpeg_start_decompress() we have the
-   * correct scaled output image dimensions available, as well as
-   * the output colormap if we asked for color quantization.  In
-   * this example, we need to make an output work buffer of the
-   * right size.
-   */
-
-  /* temporary buffer */
-  tile_height = gimp_tile_height ();
-  buf = g_new (guchar,
-               tile_height * cinfo.output_width * cinfo.output_components);
-
-  rowbuf = g_new (guchar *, tile_height);
-
-  for (i = 0; i < tile_height; i++)
-    rowbuf[i] = buf + cinfo.output_width * cinfo.output_components * i;
-
-  /* Create a new image of the proper size and associate the
-   * filename with it.
-   */
-  switch (cinfo.output_components)
-    {
-    case 1:
-      image_type = GIMP_GRAY;
-      layer_type = GIMP_GRAY_IMAGE;
-      break;
-
-    case 3:
-      image_type = GIMP_RGB;
-      layer_type = GIMP_RGB_IMAGE;
-      break;
-
-    case 4:
-      if (cinfo.out_color_space == JCS_CMYK)
-        {
-          image_type = GIMP_RGB;
-          layer_type = GIMP_RGB_IMAGE;
-          break;
-        }
-      /*fallthrough*/
-
-    default:
-      g_message ("Don't know how to load JPEG images "
-                 "with %d color channels, using colorspace %d (%d).",
-                 cinfo.output_components, cinfo.out_color_space,
-                 cinfo.jpeg_color_space);
-
-      if (exif_data)
-        {
-          exif_data_unref (exif_data);
-          exif_data = NULL;
-        }
-
-      return -1;
-      break;
-    }
-
-  image_ID = gimp_image_new (cinfo.output_width, cinfo.output_height,
-                             image_type);
-
-  gimp_image_undo_disable (image_ID);
-  gimp_image_set_filename (image_ID, filename);
-
-  jpeg_load_resolution (image_ID, &cinfo);
-
-  layer_ID = gimp_layer_new (image_ID, _("Background"),
-                             cinfo.output_width,
-                             cinfo.output_height,
-                             layer_type, 100, GIMP_NORMAL_MODE);
-
-  /* Step 6: while (scan lines remain to be read) */
-  /*           jpeg_read_scanlines(...); */
-
-  /* Here we use the library's state variable cinfo.output_scanline as the
-   * loop counter, so that we don't have to keep track ourselves.
-   */
-  buffer = gimp_drawable_get_buffer (layer_ID);
-
-  while (cinfo.output_scanline < cinfo.output_height)
-    {
-      start = cinfo.output_scanline;
-      end   = cinfo.output_scanline + tile_height;
-      end   = MIN (end, cinfo.output_height);
-      scanlines = end - start;
-
-      for (i = 0; i < scanlines; i++)
-        jpeg_read_scanlines (&cinfo, (JSAMPARRAY) &rowbuf[i], 1);
-
-      if (cinfo.out_color_space == JCS_CMYK)
-        jpeg_load_cmyk_to_rgb (buf, cinfo.output_width * scanlines, NULL);
-
-      gegl_buffer_set (buffer,
-                       GEGL_RECTANGLE (0, start, cinfo.output_width, scanlines),
-                       0,
-                       NULL,
-                       buf,
-                       GEGL_AUTO_ROWSTRIDE);
-
-      gimp_progress_update ((gdouble) cinfo.output_scanline /
-                            (gdouble) cinfo.output_height);
-    }
-
-  /* Step 7: Finish decompression */
-
-  jpeg_finish_decompress (&cinfo);
-  /* We can ignore the return value since suspension is not possible
-   * with the stdio data source.
-   */
-
-  /* Step 8: Release JPEG decompression object */
-
-  /* This is an important step since it will release a good deal
-   * of memory.
-   */
-  jpeg_destroy_decompress (&cinfo);
-
-  g_object_unref (buffer);
-
-  /* free up the temporary buffers */
-  g_free (rowbuf);
-  g_free (buf);
-
-  /* At this point you may want to check to see whether any
-   * corrupt-data warnings occurred (test whether
-   * jerr.num_warnings is nonzero).
-   */
-  gimp_image_insert_layer (image_ID, layer_ID, -1, 0);
-
-
-  /* NOW to get the dimensions of the actual image to return the
-   * calling app
-   */
-  cinfo.err = jpeg_std_error (&jerr.pub);
-  jerr.pub.error_exit = my_error_exit;
-  jerr.pub.output_message = my_output_message;
-
-  if ((infile = g_fopen (filename, "rb")) == NULL)
-    {
-      g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
-                   _("Could not open '%s' for reading: %s"),
-                   gimp_filename_to_utf8 (filename), g_strerror (errno));
-
-      if (exif_data)
-        {
-          exif_data_unref (exif_data);
-          exif_data = NULL;
-        }
-
-      return -1;
-    }
-
-  /* Establish the setjmp return context for my_error_exit to use. */
-  if (setjmp (jerr.setjmp_buffer))
-    {
-      /* If we get here, the JPEG code has signaled an error.  We
-       * need to clean up the JPEG object, close the input file,
-       * and return.
-       */
-      jpeg_destroy_decompress (&cinfo);
-
-      if (image_ID != -1)
-        gimp_image_delete (image_ID);
-
-      if (exif_data)
-        {
-          exif_data_unref (exif_data);
-          exif_data = NULL;
-        }
-
-      return -1;
-    }
-
-  /* Now we can initialize the JPEG decompression object. */
-  jpeg_create_decompress (&cinfo);
-
-  /* Step 2: specify data source (eg, a file) */
-
-  jpeg_stdio_src (&cinfo, infile);
-
-  /* Step 3: read file parameters with jpeg_read_header() */
-
-  jpeg_read_header (&cinfo, TRUE);
-
-  jpeg_start_decompress (&cinfo);
-
-  *width  = cinfo.output_width;
-  *height = cinfo.output_height;
-
-  /* Step 4: Release JPEG decompression object */
-
-  /* This is an important step since it will release a good deal
-   * of memory.
-   */
-  jpeg_destroy_decompress (&cinfo);
-
-  fclose (infile);
-
-  if (exif_data)
-    {
-      exif_data_unref (exif_data);
-      exif_data = NULL;
-    }
-
-  jpeg_exif_rotate (image_ID, orientation);
-
-  *type = layer_type;
-
-  return image_ID;
-}
-
-#endif /* HAVE_LIBEXIF */
-
-
 static gpointer
 jpeg_load_cmyk_transform (guint8 *profile_data,
                           gsize   profile_len)
diff --git a/plug-ins/file-jpeg/jpeg-load.h b/plug-ins/file-jpeg/jpeg-load.h
index fe38c41..1f543ef 100644
--- a/plug-ins/file-jpeg/jpeg-load.h
+++ b/plug-ins/file-jpeg/jpeg-load.h
@@ -23,15 +23,4 @@ gint32 load_image           (const gchar  *filename,
                              gboolean      preview,
                              GError      **error);
 
-
-#ifdef HAVE_LIBEXIF
-
-gint32 load_thumbnail_image (const gchar   *filename,
-                             gint          *width,
-                             gint          *height,
-                             GimpImageType *type,
-                             GError       **error);
-
-#endif /* HAVE_LIBEXIF */
-
 #endif /* __JPEG_LOAD_H__ */
diff --git a/plug-ins/file-jpeg/jpeg-save.c b/plug-ins/file-jpeg/jpeg-save.c
index 2ec4286..b717786 100644
--- a/plug-ins/file-jpeg/jpeg-save.c
+++ b/plug-ins/file-jpeg/jpeg-save.c
@@ -33,10 +33,6 @@
 #include <jpeglib.h>
 #include <jerror.h>
 
-#ifdef HAVE_LIBEXIF
-#include <libexif/exif-data.h>
-#endif /* HAVE_LIBEXIF */
-
 #include <libgimp/gimp.h>
 #include <libgimp/gimpui.h>
 
@@ -47,10 +43,6 @@
 #include "jpeg-load.h"
 #include "jpeg-save.h"
 #include "jpeg-settings.h"
-#ifdef HAVE_LIBEXIF
-#include "jpeg-exif.h"
-#endif
-
 
 #define SCALE_WIDTH         125
 
@@ -111,6 +103,7 @@ typedef struct
   GtkWidget     *save_exif;
   GtkWidget     *save_thumbnail;
   GtkWidget     *save_xmp;
+  GtkWidget     *save_iptc;
   GtkWidget     *use_orig_quality;      /*quant tables toggle*/
 } JpegSaveGui;
 
@@ -127,15 +120,6 @@ static void  use_orig_qual_changed  (GtkWidget     *toggle,
 static void  use_orig_qual_changed2 (GtkWidget     *toggle,
                                      GtkWidget     *combo);
 
-#ifdef HAVE_LIBEXIF
-
-static gint  create_thumbnail    (gint32         image_ID,
-                                  gint32         drawable_ID,
-                                  gdouble        quality,
-                                  guchar       **thumbnail_buffer);
-
-#endif /* HAVE_LIBEXIF */
-
 
 static GtkWidget *restart_markers_scale = NULL;
 static GtkWidget *restart_markers_label = NULL;
@@ -509,82 +493,6 @@ save_image (const gchar  *filename,
    */
   jpeg_start_compress (&cinfo, TRUE);
 
-#ifdef HAVE_LIBEXIF
-
-  /* Create the thumbnail JPEG in a buffer */
-  if ((jsvals.save_exif && exif_data) || jsvals.save_thumbnail)
-    {
-      ExifData *exif_data_tmp           = NULL;
-      guchar   *exif_buf                = NULL;
-      guchar   *thumbnail_buffer        = NULL;
-      gint      thumbnail_buffer_length = 0;
-      guint     exif_buf_len;
-      gdouble   quality                 = MIN (75.0, jsvals.quality);
-
-      if ( (! jsvals.save_exif) || (! exif_data))
-        exif_data_tmp = exif_data_new ();
-      else
-        exif_data_tmp = exif_data;
-
-      /* avoid saving markers longer than 65533, gradually decrease
-       * quality in steps of 5 until exif_buf_len is lower than that.
-       */
-      for (exif_buf_len = 65535;
-           exif_buf_len > 65533 && quality > 0.0;
-           quality -= 5.0)
-        {
-          if (jsvals.save_thumbnail)
-            thumbnail_buffer_length = create_thumbnail (image_ID, drawable_ID,
-                                                        quality,
-                                                        &thumbnail_buffer);
-
-          exif_data_tmp->data = thumbnail_buffer;
-          exif_data_tmp->size = thumbnail_buffer_length;
-
-          if (exif_buf)
-            free (exif_buf);
-
-          exif_data_save_data (exif_data_tmp, &exif_buf, &exif_buf_len);
-        }
-
-      if (exif_buf_len > 65533)
-        {
-          /* last attempt with quality 0.0 */
-          if (jsvals.save_thumbnail)
-            thumbnail_buffer_length = create_thumbnail (image_ID, drawable_ID,
-                                                        0.0,
-                                                        &thumbnail_buffer);
-          exif_data_tmp->data = thumbnail_buffer;
-          exif_data_tmp->size = thumbnail_buffer_length;
-
-          if (exif_buf)
-            free (exif_buf);
-
-          exif_data_save_data (exif_data_tmp, &exif_buf, &exif_buf_len);
-        }
-
-      if (exif_buf_len > 65533)
-        {
-          /* still no go? save without thumbnail */
-          exif_data_tmp->data = NULL;
-          exif_data_tmp->size = 0;
-
-          if (exif_buf)
-            free (exif_buf);
-
-          exif_data_save_data (exif_data_tmp, &exif_buf, &exif_buf_len);
-        }
-
-#ifdef GIMP_UNSTABLE
-      g_print ("jpeg-save: saving EXIF block (%d bytes)\n", exif_buf_len);
-#endif
-      jpeg_write_marker (&cinfo, JPEG_APP0 + 1, exif_buf, exif_buf_len);
-
-      if (exif_buf)
-        free (exif_buf);
-    }
-#endif /* HAVE_LIBEXIF */
-
   /* Step 4.1: Write the comment out - pw */
   if (image_comment && *image_comment)
     {
@@ -596,36 +504,7 @@ save_image (const gchar  *filename,
                          (guchar *) image_comment, strlen (image_comment));
     }
 
-  /* Step 4.2: Write the XMP packet in an APP1 marker */
-  if (jsvals.save_xmp)
-    {
-      /* FIXME: temporary hack until the right thing is done by a library */
-      parasite = gimp_image_get_parasite (orig_image_ID, "gimp-metadata");
-      if (parasite)
-        {
-          const gchar *xmp_data;
-          glong        xmp_data_size;
-          guchar      *app_block;
-
-          xmp_data = ((const gchar *) gimp_parasite_data (parasite)) + 10;
-          xmp_data_size = gimp_parasite_data_size (parasite) - 10;
-#ifdef GIMP_UNSTABLE
-          g_print ("jpeg-save: saving XMP packet (%d bytes)\n",
-                   (int) xmp_data_size);
-#endif
-          app_block = g_malloc (sizeof (JPEG_APP_HEADER_XMP) + xmp_data_size);
-          memcpy (app_block, JPEG_APP_HEADER_XMP,
-                  sizeof (JPEG_APP_HEADER_XMP));
-          memcpy (app_block + sizeof (JPEG_APP_HEADER_XMP), xmp_data,
-                  xmp_data_size);
-          jpeg_write_marker (&cinfo, JPEG_APP0 + 1, app_block,
-                             sizeof (JPEG_APP_HEADER_XMP) + xmp_data_size);
-          g_free (app_block);
-          gimp_parasite_free (parasite);
-        }
-    }
-
-  /* Step 4.3: store the color profile if there is one */
+  /* Step 4.2: store the color profile if there is one */
   parasite = gimp_image_get_parasite (orig_image_ID, "icc-profile");
   if (parasite)
     {
@@ -894,7 +773,7 @@ save_dialog (void)
   gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
   gtk_widget_show (frame);
 
-  table = gtk_table_new (4, 7, FALSE);
+  table = gtk_table_new (4, 8, FALSE);
   gtk_table_set_col_spacings (GTK_TABLE (table), 6);
   gtk_table_set_row_spacings (GTK_TABLE (table), 6);
   gtk_table_set_col_spacing (GTK_TABLE (table), 1, 12);
@@ -981,7 +860,6 @@ save_dialog (void)
   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
                                 jsvals.progressive);
 
-#ifdef HAVE_LIBEXIF
   pg.save_exif = toggle =
     gtk_check_button_new_with_mnemonic (_("Save _EXIF data"));
   gtk_table_attach (GTK_TABLE (table), toggle, 0, 1, 2, 3, GTK_FILL, 0, 0, 0);
@@ -995,9 +873,9 @@ save_dialog (void)
                     NULL);
 
   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
-                                jsvals.save_exif && exif_data);
+                                TRUE);
 
-  gtk_widget_set_sensitive (toggle, exif_data != NULL);
+  gtk_widget_set_sensitive (toggle, TRUE);
 
   pg.save_thumbnail = toggle =
     gtk_check_button_new_with_mnemonic (_("Save _thumbnail"));
@@ -1012,8 +890,7 @@ save_dialog (void)
                     NULL);
 
   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
-                                jsvals.save_thumbnail);
-#endif /* HAVE_LIBEXIF */
+                                TRUE);
 
   /* XMP metadata */
   pg.save_xmp = toggle =
@@ -1029,15 +906,33 @@ save_dialog (void)
                     NULL);
 
   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
-                                jsvals.save_xmp && has_metadata);
+                                TRUE);
+
+  gtk_widget_set_sensitive (toggle, TRUE);
+
+  /* IPTC metadata */
+  pg.save_iptc = toggle =
+    gtk_check_button_new_with_mnemonic (_("Save _IPTC data"));
+  gtk_table_attach (GTK_TABLE (table), toggle, 0, 1, 5, 6, GTK_FILL, 0, 0, 0);
+  gtk_widget_show (toggle);
+
+  g_signal_connect (toggle, "toggled",
+                    G_CALLBACK (gimp_toggle_button_update),
+                    &jsvals.save_iptc);
+  g_signal_connect (toggle, "toggled",
+                    G_CALLBACK (make_preview),
+                    NULL);
+
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+                                TRUE);
 
-  gtk_widget_set_sensitive (toggle, has_metadata);
+  gtk_widget_set_sensitive (toggle, TRUE);
 
   /* custom quantization tables - now used also for original quality */
   pg.use_orig_quality = toggle =
     gtk_check_button_new_with_mnemonic (_("_Use quality settings from original "
                                           "image"));
-  gtk_table_attach (GTK_TABLE (table), toggle, 0, 4, 5, 6, GTK_FILL, 0, 0, 0);
+  gtk_table_attach (GTK_TABLE (table), toggle, 0, 4, 6, 7, GTK_FILL, 0, 0, 0);
   gtk_widget_show (toggle);
 
   gimp_help_set_help_data (toggle,
@@ -1249,11 +1144,6 @@ load_defaults (void)
   jsvals.save_xmp         = DEFAULT_XMP;
   jsvals.use_orig_quality = DEFAULT_USE_ORIG_QUALITY;
 
-#ifdef HAVE_LIBEXIF
-  if (exif_data && (exif_data->data))
-    jsvals.save_thumbnail = TRUE;
-#endif /* HAVE_LIBEXIF */
-
   parasite = gimp_get_parasite (JPEG_DEFAULTS_PARASITE);
 
   if (! parasite)
@@ -1264,7 +1154,7 @@ load_defaults (void)
 
   gimp_parasite_free (parasite);
 
-  num_fields = sscanf (def_str, "%lf %lf %d %d %d %d %d %d %d %d %d %d %d",
+  num_fields = sscanf (def_str, "%lf %lf %d %d %d %d %d %d %d %d %d %d %d %d",
                        &tmpvals.quality,
                        &tmpvals.smoothing,
                        &tmpvals.optimize,
@@ -1277,11 +1167,12 @@ load_defaults (void)
                        &tmpvals.save_exif,
                        &tmpvals.save_thumbnail,
                        &tmpvals.save_xmp,
+                       &tmpvals.save_iptc,
                        &tmpvals.use_orig_quality);
 
   tmpvals.subsmp = subsampling;
 
-  if (num_fields == 13)
+  if (num_fields == 14)
     memcpy (&jsvals, &tmpvals, sizeof (tmpvals));
 
   g_free (def_str);
@@ -1293,7 +1184,7 @@ save_defaults (void)
   GimpParasite *parasite;
   gchar        *def_str;
 
-  def_str = g_strdup_printf ("%lf %lf %d %d %d %d %d %d %d %d %d %d %d",
+  def_str = g_strdup_printf ("%lf %lf %d %d %d %d %d %d %d %d %d %d %d %d",
                              jsvals.quality,
                              jsvals.smoothing,
                              jsvals.optimize,
@@ -1306,6 +1197,7 @@ save_defaults (void)
                              jsvals.save_exif,
                              jsvals.save_thumbnail,
                              jsvals.save_xmp,
+                             jsvals.save_iptc,
                              jsvals.use_orig_quality);
   parasite = gimp_parasite_new (JPEG_DEFAULTS_PARASITE,
                                 GIMP_PARASITE_PERSISTENT,
@@ -1331,10 +1223,8 @@ load_gui_defaults (JpegSaveGui *pg)
   SET_ACTIVE_BTTN (progressive);
   SET_ACTIVE_BTTN (use_orig_quality);
   SET_ACTIVE_BTTN (preview);
-#ifdef HAVE_LIBEXIF
   SET_ACTIVE_BTTN (save_exif);
   SET_ACTIVE_BTTN (save_thumbnail);
-#endif
   SET_ACTIVE_BTTN (save_xmp);
 
 #undef SET_ACTIVE_BTTN
@@ -1429,217 +1319,76 @@ use_orig_qual_changed2 (GtkWidget *toggle,
     }
 }
 
-#ifdef HAVE_LIBEXIF
-
-static guchar *tbuffer = NULL;
-static guchar *tbuffer2 = NULL;
-
-static gint tbuffer_count = 0;
-
-typedef struct
+void
+load_exif_defaults (gint32 image, ExifSaveHandler *exif_save_handler)
 {
-  struct jpeg_destination_mgr  pub;   /* public fields */
-  guchar                      *buffer;
-  gint                         size;
-} my_destination_mgr;
+  GimpParasite *parasite;
+  gchar        *def_str;
+  gint          num_fields;
+  gint          e_save;
+  gint          x_save;
+  gint          i_save;
+  gint          t_save;
 
-typedef my_destination_mgr *my_dest_ptr;
+  exif_save_handler->save_exif      = TRUE;
+  exif_save_handler->save_xmp       = TRUE;
+  exif_save_handler->save_iptc      = TRUE;
+  exif_save_handler->save_thumbnail = TRUE;
 
-static void
-init_destination (j_compress_ptr cinfo)
-{
-}
+  parasite = gimp_image_get_parasite (image, "exif-handling-data");
 
-static gboolean
-empty_output_buffer (j_compress_ptr cinfo)
-{
-  my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
+  if (! parasite)
+    return;
 
-  tbuffer_count = tbuffer_count + 16384;
-  tbuffer = g_renew (guchar, tbuffer, tbuffer_count);
-  g_memmove (tbuffer + tbuffer_count - 16384, tbuffer2, 16384);
+  def_str = g_strndup (gimp_parasite_data (parasite),
+                       gimp_parasite_data_size (parasite));
 
-  dest->pub.next_output_byte = tbuffer2;
-  dest->pub.free_in_buffer   = 16384;
+  gimp_parasite_free (parasite);
 
-  return TRUE;
-}
+  num_fields = sscanf (def_str, "%d %d %d %d",
+                       &e_save,
+                       &x_save,
+                       &i_save,
+                       &t_save);
 
-static void
-term_destination (j_compress_ptr cinfo)
-{
-  my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
+  if (num_fields != 4)
+    return;
 
-  tbuffer_count = (tbuffer_count + 16384) - (dest->pub.free_in_buffer);
+  if(e_save == 0)
+    exif_save_handler->save_exif = FALSE;
+  if(x_save == 0)
+    exif_save_handler->save_xmp = FALSE;
+  if(i_save == 0)
+    exif_save_handler->save_iptc = FALSE;
+  if(t_save == 0)
+    exif_save_handler->save_thumbnail = FALSE;
 
-  tbuffer = g_renew (guchar, tbuffer, tbuffer_count);
-  g_memmove (tbuffer + tbuffer_count - (16384 - dest->pub.free_in_buffer),
-             tbuffer2, 16384 - dest->pub.free_in_buffer);
+  g_free (def_str);
 }
 
-static gint
-create_thumbnail (gint32    image_ID,
-                  gint32    drawable_ID,
-                  gdouble   quality,
-                  guchar  **thumbnail_buffer)
+void
+save_exif_defaults (gint32 image, ExifSaveHandler *exif_save_handler)
 {
-  int                         width, height;
-  gint                        req_width, req_height, bpp, rbpp;
-  guchar                     *thumbnail_data = NULL;
-  struct jpeg_compress_struct cinfo;
-  struct my_error_mgr         jerr;
-  my_dest_ptr                 dest;
-  JSAMPROW                    scanline[1];
-  guchar                     *buf = NULL;
-  gint                        i;
-
-  {
-    GeglBuffer *buffer = gimp_drawable_get_buffer (drawable_ID);
-    width  = gegl_buffer_get_width (buffer);
-    height = gegl_buffer_get_height (buffer);
-    g_object_unref (buffer);
-  }
-
-  req_width  = 196;
-  req_height = 196;
-
-  if (MIN (width, height) < 196)
-    req_width = req_height = MIN(width, height);
-
-  thumbnail_data = gimp_drawable_get_thumbnail_data (drawable_ID,
-                                                     &req_width, &req_height,
-                                                     &bpp);
-
-  if (! thumbnail_data)
-    return 0;
-
-  rbpp = bpp;
-
-  if ((bpp == 2) || (bpp == 4))
-    {
-      rbpp = bpp - 1;
-    }
-
-  buf = g_new (guchar, req_width * bpp);
-  tbuffer2 = g_new (guchar, 16384);
-
-  tbuffer_count = 0;
-
-  cinfo.err = jpeg_std_error (&jerr.pub);
-  jerr.pub.error_exit = my_error_exit;
-
-  /* Establish the setjmp return context for my_error_exit to use. */
-  if (setjmp (jerr.setjmp_buffer))
-    {
-      /* If we get here, the JPEG code has signaled an error.
-       * We need to clean up the JPEG object, free memory, and return.
-       */
-      jpeg_destroy_compress (&cinfo);
-
-      if (thumbnail_data)
-        {
-          g_free (thumbnail_data);
-          thumbnail_data = NULL;
-        }
-
-      if (buf)
-        {
-          g_free (buf);
-          buf = NULL;
-        }
-
-      if (tbuffer2)
-        {
-          g_free (tbuffer2);
-          tbuffer2 = NULL;
-        }
-
-      return 0;
-    }
-
-  /* Now we can initialize the JPEG compression object. */
-  jpeg_create_compress (&cinfo);
-
-  if (cinfo.dest == NULL)
-    cinfo.dest = (struct jpeg_destination_mgr *)
-      (*cinfo.mem->alloc_small) ((j_common_ptr) &cinfo, JPOOL_PERMANENT,
-                                 sizeof(my_destination_mgr));
-
-  dest = (my_dest_ptr) cinfo.dest;
-  dest->pub.init_destination    = init_destination;
-  dest->pub.empty_output_buffer = empty_output_buffer;
-  dest->pub.term_destination    = term_destination;
-
-  dest->pub.next_output_byte = tbuffer2;
-  dest->pub.free_in_buffer   = 16384;
-
-  dest->buffer = tbuffer2;
-  dest->size   = 16384;
-
-  cinfo.input_components = rbpp;
-  cinfo.image_width      = req_width;
-  cinfo.image_height     = req_height;
-
-  /* colorspace of input image */
-  cinfo.in_color_space = (rbpp == 3) ? JCS_RGB : JCS_GRAYSCALE;
-
-  /* Now use the library's routine to set default compression parameters.
-   * (You must set at least cinfo.in_color_space before calling this,
-   * since the defaults depend on the source color space.)
-   */
-  jpeg_set_defaults (&cinfo);
-
-  jpeg_set_quality (&cinfo, (gint) (quality + 0.5), jsvals.baseline);
-
-  /* Step 4: Start compressor */
-
-  /* TRUE ensures that we will write a complete interchange-JPEG file.
-   * Pass TRUE unless you are very sure of what you're doing.
-   */
-  jpeg_start_compress (&cinfo, TRUE);
-
-  while (cinfo.next_scanline < (unsigned int) req_height)
-    {
-      for (i = 0; i < req_width; i++)
-        {
-          buf[(i * rbpp) + 0] = thumbnail_data[(cinfo.next_scanline * req_width * bpp) + (i * bpp) + 0];
-
-          if (rbpp == 3)
-            {
-              buf[(i * rbpp) + 1] = thumbnail_data[(cinfo.next_scanline * req_width * bpp) + (i * bpp) + 1];
-              buf[(i * rbpp) + 2] = thumbnail_data[(cinfo.next_scanline * req_width * bpp) + (i * bpp) + 2];
-            }
-        }
-
-      scanline[0] = buf;
-      jpeg_write_scanlines (&cinfo, scanline, 1);
-  }
-
-  /* Step 6: Finish compression */
-  jpeg_finish_compress (&cinfo);
-
-  /* Step 7: release JPEG compression object */
-
-  /* This is an important step since it will release a good deal of memory. */
-  jpeg_destroy_compress (&cinfo);
+  GimpParasite *parasite;
+  gchar        *def_str;
 
-  /* And we're done! */
+  exif_save_handler->save_exif      = jsvals.save_exif;
+  exif_save_handler->save_xmp       = jsvals.save_xmp;
+  exif_save_handler->save_iptc      = jsvals.save_iptc;
+  exif_save_handler->save_thumbnail = jsvals.save_thumbnail;
 
-  if (thumbnail_data)
-    {
-      g_free (thumbnail_data);
-      thumbnail_data = NULL;
-    }
+  def_str = g_strdup_printf ("%d %d %d %d",
+                             exif_save_handler->save_exif ? 1 : 0,
+                             exif_save_handler->save_xmp ? 1 : 0,
+                             exif_save_handler->save_iptc ? 1 : 0,
+                             exif_save_handler->save_thumbnail ? 1 : 0);
 
-  if (buf)
-    {
-      g_free (buf);
-      buf = NULL;
-    }
+  parasite = gimp_parasite_new ("exif-handling-data",
+                                GIMP_PARASITE_PERSISTENT,
+                                strlen (def_str), def_str);
 
-  *thumbnail_buffer = tbuffer;
+  gimp_image_attach_parasite (image, parasite);
 
-  return tbuffer_count;
+  gimp_parasite_free (parasite);
+  g_free (def_str);
 }
-
-#endif /* HAVE_LIBEXIF */
diff --git a/plug-ins/file-jpeg/jpeg-save.h b/plug-ins/file-jpeg/jpeg-save.h
index 4465991..a063e81 100644
--- a/plug-ins/file-jpeg/jpeg-save.h
+++ b/plug-ins/file-jpeg/jpeg-save.h
@@ -32,6 +32,7 @@ typedef struct
   gboolean         save_exif;
   gboolean         save_thumbnail;
   gboolean         save_xmp;
+  gboolean         save_iptc;
   gboolean         use_orig_quality;
 } JpegSaveVals;
 
@@ -50,4 +51,9 @@ gboolean    save_image         (const gchar  *filename,
 gboolean    save_dialog        (void);
 void        load_defaults      (void);
 
+void        load_exif_defaults (gint32 image,
+                                ExifSaveHandler *exif_save_handler);
+void        save_exif_defaults (gint32 image,
+                                ExifSaveHandler *exif_save_handler);
+
 #endif /* __JPEG_SAVE_H__ */
diff --git a/plug-ins/file-jpeg/jpeg-settings.c b/plug-ins/file-jpeg/jpeg-settings.c
index e4112b9..b86e2bc 100644
--- a/plug-ins/file-jpeg/jpeg-settings.c
+++ b/plug-ins/file-jpeg/jpeg-settings.c
@@ -50,10 +50,6 @@
 
 #include <jpeglib.h>
 
-#ifdef HAVE_LIBEXIF
-#include <libexif/exif-data.h>
-#endif /* HAVE_LIBEXIF */
-
 #include <libgimp/gimp.h>
 
 #include "libgimp/stdplugins-intl.h"
diff --git a/plug-ins/file-jpeg/jpeg.c b/plug-ins/file-jpeg/jpeg.c
index bdfb70a..b99217f 100644
--- a/plug-ins/file-jpeg/jpeg.c
+++ b/plug-ins/file-jpeg/jpeg.c
@@ -24,10 +24,6 @@
 #include <jpeglib.h>
 #include <jerror.h>
 
-#ifdef HAVE_LIBEXIF
-#include <libexif/exif-data.h>
-#endif /* HAVE_LIBEXIF */
-
 #include <libgimp/gimp.h>
 #include <libgimp/gimpui.h>
 
@@ -37,11 +33,6 @@
 #include "jpeg-settings.h"
 #include "jpeg-load.h"
 #include "jpeg-save.h"
-#ifdef HAVE_LIBEXIF
-#include "jpeg-exif.h"
-#include "gimpexif.h"
-#endif
-
 
 /* Declare local functions.
  */
@@ -66,10 +57,6 @@ JpegSubsampling  orig_subsmp;
 gint             num_quant_tables;
 
 
-#ifdef HAVE_LIBEXIF
-ExifData        *exif_data = NULL;
-#endif
-
 const GimpPlugInInfo PLUG_IN_INFO =
 {
   NULL,  /* init_proc  */
@@ -79,6 +66,14 @@ const GimpPlugInInfo PLUG_IN_INFO =
 };
 
 
+static ExifSaveHandler exifvals =
+{
+  TRUE,                /* save exif       */
+  TRUE,                /* save xmp        */
+  TRUE,                /* save iptc        */
+  TRUE                 /* save thumbnail  */
+};
+
 MAIN ()
 
 
@@ -96,22 +91,6 @@ query (void)
     { GIMP_PDB_IMAGE,   "image",         "Output image" }
   };
 
-#ifdef HAVE_LIBEXIF
-
-  static const GimpParamDef thumb_args[] =
-  {
-    { GIMP_PDB_STRING, "filename",     "The name of the file to load"  },
-    { GIMP_PDB_INT32,  "thumb-size",   "Preferred thumbnail size"      }
-  };
-  static const GimpParamDef thumb_return_vals[] =
-  {
-    { GIMP_PDB_IMAGE,  "image",        "Thumbnail image"               },
-    { GIMP_PDB_INT32,  "image-width",  "Width of full-sized image"     },
-    { GIMP_PDB_INT32,  "image-height", "Height of full-sized image"    }
-  };
-
-#endif /* HAVE_LIBEXIF */
-
   static const GimpParamDef save_args[] =
   {
     { GIMP_PDB_INT32,    "run-mode",     "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
@@ -149,25 +128,6 @@ query (void)
                                     "",
                                     "6,string,JFIF,6,string,Exif");
 
-#ifdef HAVE_LIBEXIF
-
-  gimp_install_procedure (LOAD_THUMB_PROC,
-                          "Loads a thumbnail from a JPEG image",
-                          "Loads a thumbnail from a JPEG image (only if it exists)",
-                          "Mukund Sivaraman <muks@mukund.org>, Sven Neumann <sven@gimp.org>",
-                          "Mukund Sivaraman <muks@mukund.org>, Sven Neumann <sven@gimp.org>",
-                          "November 15, 2004",
-                          NULL,
-                          NULL,
-                          GIMP_PLUGIN,
-                          G_N_ELEMENTS (thumb_args),
-                          G_N_ELEMENTS (thumb_return_vals),
-                          thumb_args, thumb_return_vals);
-
-  gimp_register_thumbnail_loader (LOAD_PROC, LOAD_THUMB_PROC);
-
-#endif /* HAVE_LIBEXIF */
-
   gimp_install_procedure (SAVE_PROC,
                           "saves files in the JPEG file format",
                           "saves files in the lossy, widely supported JPEG format",
@@ -247,48 +207,6 @@ run (const gchar      *name,
         }
 
     }
-
-#ifdef HAVE_LIBEXIF
-
-  else if (strcmp (name, LOAD_THUMB_PROC) == 0)
-    {
-      if (nparams < 2)
-        {
-          status = GIMP_PDB_CALLING_ERROR;
-        }
-      else
-        {
-          const gchar  *filename = param[0].data.d_string;
-          gint          width    = 0;
-          gint          height   = 0;
-          GimpImageType type     = -1;
-
-          image_ID = load_thumbnail_image (filename, &width, &height, &type,
-                                           &error);
-
-          if (image_ID != -1)
-            {
-              *nreturn_vals = 6;
-              values[1].type         = GIMP_PDB_IMAGE;
-              values[1].data.d_image = image_ID;
-              values[2].type         = GIMP_PDB_INT32;
-              values[2].data.d_int32 = width;
-              values[3].type         = GIMP_PDB_INT32;
-              values[3].data.d_int32 = height;
-              values[4].type         = GIMP_PDB_INT32;
-              values[4].data.d_int32 = type;
-              values[5].type         = GIMP_PDB_INT32;
-              values[5].data.d_int32 = 1; /* num_layers */
-            }
-          else
-            {
-              status = GIMP_PDB_EXECUTION_ERROR;
-            }
-        }
-    }
-
-#endif /* HAVE_LIBEXIF */
-
   else if (strcmp (name, SAVE_PROC) == 0)
     {
       image_ID = orig_image_ID = param[1].data.d_int32;
@@ -348,14 +266,6 @@ run (const gchar      *name,
           gimp_parasite_free (parasite);
         }
 
-#ifdef HAVE_LIBEXIF
-
-      exif_data = gimp_metadata_generate_exif (orig_image_ID);
-      if (exif_data)
-        jpeg_setup_exif_for_save (exif_data, orig_image_ID);
-
-#endif /* HAVE_LIBEXIF */
-
       load_defaults ();
 
       switch (run_mode)
@@ -408,6 +318,12 @@ run (const gchar      *name,
                                           &orig_subsmp,
                                           &num_quant_tables);
 
+          load_exif_defaults (orig_image_ID, &exifvals);
+          jsvals.save_exif        = exifvals.save_exif;
+          jsvals.save_thumbnail   = exifvals.save_thumbnail;
+          jsvals.save_xmp         = exifvals.save_xmp;
+          jsvals.save_iptc        = exifvals.save_iptc;
+
           /* load up the previously used values (if file was saved once) */
           parasite = gimp_image_get_parasite (orig_image_ID,
                                               "jpeg-save-options");
@@ -424,9 +340,10 @@ run (const gchar      *name,
               jsvals.restart          = save_vals->restart;
               jsvals.dct              = save_vals->dct;
               jsvals.preview          = save_vals->preview;
-              jsvals.save_exif        = save_vals->save_exif;
-              jsvals.save_thumbnail   = save_vals->save_thumbnail;
-              jsvals.save_xmp         = save_vals->save_xmp;
+              jsvals.save_exif        = exifvals.save_exif      = save_vals->save_exif;
+              jsvals.save_thumbnail   = exifvals.save_thumbnail = save_vals->save_thumbnail;
+              jsvals.save_xmp         = exifvals.save_xmp       = save_vals->save_xmp;
+              jsvals.save_iptc        = exifvals.save_iptc      = save_vals->save_iptc;
               jsvals.use_orig_quality = save_vals->use_orig_quality;
 
               gimp_parasite_free (parasite);
@@ -496,6 +413,10 @@ run (const gchar      *name,
             {
               status = GIMP_PDB_EXECUTION_ERROR;
             }
+          else
+            {
+              save_exif_defaults (image_ID, &exifvals);
+            }
         }
 
       if (export == GIMP_EXPORT_EXPORT)
diff --git a/plug-ins/file-jpeg/jpeg.h b/plug-ins/file-jpeg/jpeg.h
index 5cc0651..4d51e94 100644
--- a/plug-ins/file-jpeg/jpeg.h
+++ b/plug-ins/file-jpeg/jpeg.h
@@ -51,6 +51,14 @@ typedef enum
   JPEG_SUBSAMPLING_1x2_1x1_1x1 = 3
 } JpegSubsampling;
 
+typedef struct
+{
+  gboolean  save_exif;
+  gboolean  save_xmp;
+  gboolean  save_iptc;
+  gboolean  save_thumbnail;
+} ExifSaveHandler;
+
 extern gint32 volatile  preview_image_ID;
 extern gint32           preview_layer_ID;
 extern gboolean         undo_touched;
diff --git a/plug-ins/file-psd/psd-image-res-load.c b/plug-ins/file-psd/psd-image-res-load.c
index bf2c343..178b9dc 100644
--- a/plug-ins/file-psd/psd-image-res-load.c
+++ b/plug-ins/file-psd/psd-image-res-load.c
@@ -94,9 +94,6 @@
 #include <jpeglib.h>
 #include <jerror.h>
 
-#ifdef HAVE_LIBEXIF
-#include <libexif/exif-data.h>
-#endif /* HAVE_LIBEXIF */
 #ifdef HAVE_IPTCDATA
 #include <libiptcdata/iptc-data.h>
 #endif /* HAVE_IPTCDATA */
@@ -196,11 +193,6 @@ static gint     load_resource_1058     (const PSDimageres     *res_a,
                                         FILE                  *f,
                                         GError               **error);
 
-static gint     load_resource_1060     (const PSDimageres     *res_a,
-                                        const gint32           image_id,
-                                        FILE                  *f,
-                                        GError               **error);
-
 static gint     load_resource_2000     (const PSDimageres     *res_a,
                                         const gint32           image_id,
                                         FILE                  *f,
@@ -353,7 +345,6 @@ load_image_resource (PSDimageres   *res_a,
             break;
 
           case PSD_XMP_DATA:
-            load_resource_1060 (res_a, image_id, f, error);
             break;
 
           default:
@@ -1209,21 +1200,7 @@ load_resource_1058 (const PSDimageres  *res_a,
                     FILE               *f,
                     GError            **error)
 {
-  /* Load EXIF data block */
-
-#ifdef HAVE_LIBEXIF
-  ExifData     *exif_data;
-  ExifEntry    *exif_entry;
-  guchar       *exif_buf;
-  guchar       *tmp_data;
-  guint         exif_buf_len;
-  gint16        jpeg_len;
-  gint16        jpeg_fill = 0;
-  GimpParam    *return_vals;
-  gint          nreturn_vals;
-#else
   gchar        *name;
-#endif /* HAVE_LIBEXIF */
 
   GimpParasite *parasite;
   gchar        *res_data;
@@ -1238,79 +1215,6 @@ load_resource_1058 (const PSDimageres  *res_a,
       return -1;
     }
 
-#ifdef HAVE_LIBEXIF
-  /* Add JPEG header & trailer to the TIFF Exif data held in PSD
-     resource to allow us to use libexif to serialize the data
-     in the same manner as the JPEG load.
-  */
-  jpeg_len = res_a->data_len + 8;
-  tmp_data = g_malloc (res_a->data_len + 12);
-  /* SOI & APP1 markers */
-  memcpy (tmp_data, "\xFF\xD8\xFF\xE1", 4);
-  /* APP1 block len */
-  memcpy (tmp_data + 4, &jpeg_len, 2);
-  /* Exif marker */
-  memcpy (tmp_data + 6, "Exif", 4);
-  /* Filler */
-  memcpy (tmp_data + 10, &jpeg_fill, 2);
-  /* Exif data */
-  memcpy (tmp_data + 12, res_data, res_a->data_len);
-
-  /* Create Exif data structure */
-  exif_data = exif_data_new_from_data (tmp_data, res_a->data_len + 12);
-  g_free (tmp_data);
-  IFDBG (3) exif_data_dump (exif_data);
-
-  /* Check for XMP data block in Exif data - PS7 */
-  if ((exif_entry = exif_content_get_entry (exif_data->ifd[EXIF_IFD_0],
-                                            EXIF_TAG_XML_PACKET)))
-    {
-      IFDBG(3) g_debug ("Processing Exif XMP data block");
-      /*Create NULL terminated EXIF data block */
-      tmp_data = g_malloc (exif_entry->size + 1);
-      memcpy (tmp_data, exif_entry->data, exif_entry->size);
-      tmp_data[exif_entry->size] = 0;
-      /* Merge with existing XMP data block */
-      return_vals = gimp_run_procedure (DECODE_XMP_PROC,
-                                        &nreturn_vals,
-                                        GIMP_PDB_IMAGE,  image_id,
-                                        GIMP_PDB_STRING, tmp_data,
-                                        GIMP_PDB_END);
-      g_free (tmp_data);
-      gimp_destroy_params (return_vals, nreturn_vals);
-      IFDBG(3) g_debug ("Deleting XMP block from Exif data");
-      /* Delete XMP data from Exif block */
-      exif_content_remove_entry (exif_data->ifd[EXIF_IFD_0],
-                                 exif_entry);
-    }
-
-  /* Check for Photoshp Image Resource data block in Exif data - PS7 */
-  if ((exif_entry = exif_content_get_entry (exif_data->ifd[EXIF_IFD_0],
-                                            EXIF_TAG_IMAGE_RESOURCES)))
-    {
-      IFDBG(3) g_debug ("Deleting PS Image Resource block from Exif data");
-      /* Delete PS Image Resource data from Exif block */
-      exif_content_remove_entry (exif_data->ifd[EXIF_IFD_0],
-                                 exif_entry);
-    }
-
-  IFDBG (3) exif_data_dump (exif_data);
-  /* Store resource data as a GIMP Exif parasite */
-  IFDBG (2) g_debug ("Processing exif data as GIMP Exif parasite");
-  /* Serialize exif data */
-  exif_data_save_data (exif_data, &exif_buf, &exif_buf_len);
-  if (exif_buf_len > EXIF_HEADER_SIZE)
-    {
-      parasite = gimp_parasite_new (GIMP_PARASITE_EXIF,
-                                    GIMP_PARASITE_PERSISTENT,
-                                    exif_buf_len, exif_buf);
-      gimp_image_attach_parasite (image_id, parasite);
-      gimp_parasite_free (parasite);
-    }
-  exif_data_unref (exif_data);
-  g_free (exif_buf);
-
-#else
   /* Store resource data as a standard psd parasite */
   IFDBG (2) g_debug ("Processing exif data as psd parasite");
   name = g_strdup_printf ("psd-image-resource-%.4s-%.4x",
@@ -1322,42 +1226,7 @@ load_resource_1058 (const PSDimageres  *res_a,
   gimp_parasite_free (parasite);
   g_free (name);
 
-#endif /* HAVE_LIBEXIF */
-
-  g_free (res_data);
-  return 0;
-}
-
-static gint
-load_resource_1060 (const PSDimageres  *res_a,
-                    const gint32        image_id,
-                    FILE               *f,
-                    GError            **error)
-{
-  /* Load XMP Metadata block */
-  GimpParam    *return_vals;
-  gint          nreturn_vals;
-  gchar        *res_data;
-
-  IFDBG(2) g_debug ("Process image resource block: 1060: XMP Data");
-
-  res_data = g_malloc (res_a->data_len + 1);
-  if (fread (res_data, res_a->data_len, 1, f) < 1)
-    {
-      psd_set_error (feof (f), errno, error);
-      g_free (res_data);
-      return -1;
-    }
-  /* Null terminate metadata block for decode procedure */
-  res_data[res_a->data_len] = 0;
-
-  return_vals = gimp_run_procedure (DECODE_XMP_PROC,
-                                    &nreturn_vals,
-                                    GIMP_PDB_IMAGE,  image_id,
-                                    GIMP_PDB_STRING, res_data,
-                                    GIMP_PDB_END);
   g_free (res_data);
-  gimp_destroy_params (return_vals, nreturn_vals);
   return 0;
 }
 
diff --git a/plug-ins/ui/Makefile.am b/plug-ins/ui/Makefile.am
index 0f52918..6918cdd 100644
--- a/plug-ins/ui/Makefile.am
+++ b/plug-ins/ui/Makefile.am
@@ -2,6 +2,7 @@ uidatadir = $(gimpdatadir)/ui/plug-ins
 
 uidata_DATA = \
 	plug-in-file-gif.ui	\
-	plug-in-file-png.ui
+	plug-in-file-png.ui \
+	plug-in-file-tiff.ui
 
 EXTRA_DIST = $(uidata_DATA)
diff --git a/plug-ins/ui/plug-in-file-png.ui b/plug-ins/ui/plug-in-file-png.ui
index 0005595..db4ec36 100644
--- a/plug-ins/ui/plug-in-file-png.ui
+++ b/plug-ins/ui/plug-in-file-png.ui
@@ -1,20 +1,18 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
 <interface>
-  <!-- interface-requires gtk+ 2.12 -->
+  <requires lib="gtk+" version="2.24"/>
   <!-- interface-naming-policy project-wide -->
   <object class="GtkAdjustment" id="compression-level">
     <property name="upper">9</property>
-
-    <!-- NOTE: Set value _after_ upper so the value don't get clamped -->
     <property name="value">9</property>
-
     <property name="step_increment">1</property>
     <property name="page_increment">1</property>
   </object>
   <object class="GtkTable" id="table">
     <property name="visible">True</property>
+    <property name="can_focus">False</property>
     <property name="border_width">12</property>
-    <property name="n_rows">10</property>
+    <property name="n_rows">11</property>
     <property name="n_columns">3</property>
     <property name="column_spacing">6</property>
     <property name="row_spacing">6</property>
@@ -139,6 +137,7 @@
     <child>
       <object class="GtkLabel" id="compression-level-label">
         <property name="visible">True</property>
+        <property name="can_focus">False</property>
         <property name="xalign">0</property>
         <property name="label" translatable="yes">Co_mpression level:</property>
         <property name="use_underline">True</property>
@@ -147,7 +146,7 @@
       <packing>
         <property name="top_attach">8</property>
         <property name="bottom_attach">9</property>
-        <property name="x_options"></property>
+        <property name="x_options"/>
       </packing>
     </child>
     <child>
@@ -170,7 +169,11 @@
         <property name="can_focus">True</property>
         <property name="has_tooltip">True</property>
         <property name="tooltip_text" translatable="yes">Choose a high compression level for small file size</property>
-        <property name="invisible_char">&#x25CF;</property>
+        <property name="invisible_char">●</property>
+        <property name="primary_icon_activatable">False</property>
+        <property name="secondary_icon_activatable">False</property>
+        <property name="primary_icon_sensitive">True</property>
+        <property name="secondary_icon_sensitive">True</property>
         <property name="adjustment">compression-level</property>
         <property name="numeric">True</property>
       </object>
@@ -179,12 +182,13 @@
         <property name="right_attach">3</property>
         <property name="top_attach">8</property>
         <property name="bottom_attach">9</property>
-        <property name="x_options"></property>
+        <property name="x_options"/>
       </packing>
     </child>
     <child>
       <object class="GtkHButtonBox" id="hbuttonbox">
         <property name="visible">True</property>
+        <property name="can_focus">False</property>
         <property name="spacing">6</property>
         <property name="layout_style">start</property>
         <child>
@@ -218,6 +222,90 @@
       </object>
       <packing>
         <property name="right_attach">3</property>
+        <property name="top_attach">10</property>
+        <property name="bottom_attach">11</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkExpander" id="expander1">
+        <property name="visible">True</property>
+        <property name="can_focus">True</property>
+        <child>
+          <object class="GtkVBox" id="vbox1">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <child>
+              <object class="GtkCheckButton" id="sv_exif">
+                <property name="label" translatable="yes">save EXIF data</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">False</property>
+                <property name="active">True</property>
+                <property name="draw_indicator">True</property>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkCheckButton" id="sv_xmp">
+                <property name="label" translatable="yes">save XMP data</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">False</property>
+                <property name="active">True</property>
+                <property name="draw_indicator">True</property>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkCheckButton" id="sv_iptc">
+                <property name="label" translatable="yes">save IPTC data</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">False</property>
+                <property name="active">True</property>
+                <property name="draw_indicator">True</property>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">2</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkCheckButton" id="sv_thumbnail">
+                <property name="label" translatable="yes">save thumbnail</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">False</property>
+                <property name="active">True</property>
+                <property name="draw_indicator">True</property>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">3</property>
+              </packing>
+            </child>
+          </object>
+        </child>
+        <child type="label">
+          <object class="GtkLabel" id="label1">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="label" translatable="yes">Advanced</property>
+          </object>
+        </child>
+      </object>
+      <packing>
+        <property name="right_attach">3</property>
         <property name="top_attach">9</property>
         <property name="bottom_attach">10</property>
       </packing>
diff --git a/plug-ins/ui/plug-in-file-tiff.ui b/plug-ins/ui/plug-in-file-tiff.ui
new file mode 100644
index 0000000..ace9c2c
--- /dev/null
+++ b/plug-ins/ui/plug-in-file-tiff.ui
@@ -0,0 +1,404 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <requires lib="gtk+" version="2.24"/>
+  <!-- interface-naming-policy project-wide -->
+  <object class="GtkVBox" id="tiff_export_vbox">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <child>
+      <object class="GtkFrame" id="frame1">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="label_xalign">0</property>
+        <property name="shadow_type">none</property>
+        <child>
+          <object class="GtkAlignment" id="alignment1">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="left_padding">12</property>
+            <child>
+              <object class="GtkHBox" id="hbox1">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <child>
+                  <object class="GtkVBox" id="vbox2">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="spacing">3</property>
+                    <child>
+                      <object class="GtkRadioButton" id="cmp_none">
+                        <property name="label" translatable="yes">no compression</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">False</property>
+                        <property name="active">True</property>
+                        <property name="draw_indicator">True</property>
+                      </object>
+                      <packing>
+                        <property name="expand">True</property>
+                        <property name="fill">True</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkRadioButton" id="cmp_lzw">
+                        <property name="label" translatable="yes">LZW</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">False</property>
+                        <property name="active">True</property>
+                        <property name="draw_indicator">True</property>
+                        <property name="group">cmp_none</property>
+                      </object>
+                      <packing>
+                        <property name="expand">True</property>
+                        <property name="fill">True</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkRadioButton" id="cmp_packbits">
+                        <property name="label" translatable="yes">PackBits</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">False</property>
+                        <property name="active">True</property>
+                        <property name="draw_indicator">True</property>
+                        <property name="group">cmp_none</property>
+                      </object>
+                      <packing>
+                        <property name="expand">True</property>
+                        <property name="fill">True</property>
+                        <property name="position">2</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkRadioButton" id="cmp_deflate">
+                        <property name="label" translatable="yes">Deflate</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">False</property>
+                        <property name="active">True</property>
+                        <property name="draw_indicator">True</property>
+                        <property name="group">cmp_none</property>
+                      </object>
+                      <packing>
+                        <property name="expand">True</property>
+                        <property name="fill">True</property>
+                        <property name="position">3</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkRadioButton" id="cmp_jpeg">
+                        <property name="label" translatable="yes">JPEG</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">False</property>
+                        <property name="active">True</property>
+                        <property name="draw_indicator">True</property>
+                        <property name="group">cmp_none</property>
+                      </object>
+                      <packing>
+                        <property name="expand">True</property>
+                        <property name="fill">True</property>
+                        <property name="position">4</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">True</property>
+                    <property name="fill">True</property>
+                    <property name="padding">3</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkVBox" id="vbox4">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <child>
+                      <object class="GtkRadioButton" id="cmp_g3">
+                        <property name="label" translatable="yes">CCITT Group 3 fax</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">False</property>
+                        <property name="active">True</property>
+                        <property name="draw_indicator">True</property>
+                        <property name="group">cmp_none</property>
+                      </object>
+                      <packing>
+                        <property name="expand">True</property>
+                        <property name="fill">True</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkRadioButton" id="cmp_g4">
+                        <property name="label" translatable="yes">CCITT Group _4 fax</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">False</property>
+                        <property name="active">True</property>
+                        <property name="draw_indicator">True</property>
+                        <property name="group">cmp_none</property>
+                      </object>
+                      <packing>
+                        <property name="expand">True</property>
+                        <property name="fill">True</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">True</property>
+                    <property name="fill">True</property>
+                    <property name="padding">3</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child type="label">
+          <object class="GtkLabel" id="compresslabel">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="label" translatable="yes">&lt;b&gt;Compression&lt;/b&gt;</property>
+            <property name="use_markup">True</property>
+          </object>
+        </child>
+      </object>
+      <packing>
+        <property name="expand">True</property>
+        <property name="fill">True</property>
+        <property name="padding">3</property>
+        <property name="position">0</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkHSeparator" id="hseparator3">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="fill">True</property>
+        <property name="position">1</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkVBox" id="vbox7">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <child>
+          <object class="GtkCheckButton" id="sv_alpha">
+            <property name="label" translatable="yes">Save color values from transparent pixels</property>
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">False</property>
+            <property name="draw_indicator">True</property>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+      </object>
+      <packing>
+        <property name="expand">True</property>
+        <property name="fill">True</property>
+        <property name="padding">6</property>
+        <property name="position">2</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkHSeparator" id="hseparator2">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="fill">True</property>
+        <property name="position">3</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkFrame" id="frame2">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="label_xalign">0</property>
+        <property name="shadow_type">none</property>
+        <child>
+          <object class="GtkAlignment" id="alignment2">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="left_padding">12</property>
+            <child>
+              <object class="GtkVBox" id="vbox5">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <child>
+                  <object class="GtkEntry" id="commentfield">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="invisible_char">●</property>
+                    <property name="invisible_char_set">True</property>
+                    <property name="primary_icon_activatable">False</property>
+                    <property name="secondary_icon_activatable">False</property>
+                    <property name="primary_icon_sensitive">True</property>
+                    <property name="secondary_icon_sensitive">True</property>
+                  </object>
+                  <packing>
+                    <property name="expand">True</property>
+                    <property name="fill">True</property>
+                    <property name="padding">3</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child type="label">
+          <object class="GtkLabel" id="commentlabel">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="label" translatable="yes">&lt;b&gt;Comment&lt;/b&gt;</property>
+            <property name="use_markup">True</property>
+          </object>
+        </child>
+      </object>
+      <packing>
+        <property name="expand">True</property>
+        <property name="fill">True</property>
+        <property name="padding">3</property>
+        <property name="position">4</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkHSeparator" id="hseparator1">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="fill">True</property>
+        <property name="position">5</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkExpander" id="expander1">
+        <property name="visible">True</property>
+        <property name="can_focus">True</property>
+        <child>
+          <object class="GtkHBox" id="hbox2">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <child>
+              <object class="GtkVBox" id="vbox3">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <child>
+                  <object class="GtkCheckButton" id="sv_exif">
+                    <property name="label" translatable="yes">save EXIF data</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="draw_indicator">True</property>
+                  </object>
+                  <packing>
+                    <property name="expand">True</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkCheckButton" id="sv_xmp">
+                    <property name="label" translatable="yes">save XMP data</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="draw_indicator">True</property>
+                  </object>
+                  <packing>
+                    <property name="expand">True</property>
+                    <property name="fill">True</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkVBox" id="vbox6">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <child>
+                  <object class="GtkCheckButton" id="sv_iptc">
+                    <property name="label" translatable="yes">save IPTC data</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="draw_indicator">True</property>
+                  </object>
+                  <packing>
+                    <property name="expand">True</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkCheckButton" id="sv_thumbnail">
+                    <property name="label" translatable="yes">save thumbnail</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="draw_indicator">True</property>
+                  </object>
+                  <packing>
+                    <property name="expand">True</property>
+                    <property name="fill">True</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+        </child>
+        <child type="label">
+          <object class="GtkLabel" id="adv_label">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="label" translatable="yes">Advanced</property>
+          </object>
+        </child>
+      </object>
+      <packing>
+        <property name="expand">True</property>
+        <property name="fill">True</property>
+        <property name="padding">3</property>
+        <property name="position">6</property>
+      </packing>
+    </child>
+  </object>
+</interface>
diff --git a/tools/pdbgen/Makefile.am b/tools/pdbgen/Makefile.am
index 4c98901..fda88fb 100644
--- a/tools/pdbgen/Makefile.am
+++ b/tools/pdbgen/Makefile.am
@@ -33,6 +33,7 @@ pdb_sources = \
 	pdb/item_transform.pdb		\
 	pdb/layer.pdb			\
 	pdb/message.pdb			\
+	pdb/metadata.pdb		\
 	pdb/paint_tools.pdb		\
 	pdb/palette.pdb			\
 	pdb/palette_select.pdb		\
diff --git a/tools/pdbgen/pdb/metadata.pdb b/tools/pdbgen/pdb/metadata.pdb
new file mode 100644
index 0000000..51c79e0
--- /dev/null
+++ b/tools/pdbgen/pdb/metadata.pdb
@@ -0,0 +1,85 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# 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 3 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, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh@gimp.org>
+
+sub image_metadata_get {
+    $blurb = 'Returns images metadata.';
+    $help  = 'Returns exif/iptc/xmp metadata from image.';
+
+    &std_pdb_misc('2013', '2.9');
+
+    @inargs = (
+   { name => 'image', type => 'image',
+     desc => 'The image' }
+    );
+
+    @outargs = (
+	{ name => 'metadata_string', type => 'string',
+	  desc => 'The exif/ptc/xmp metadata as a string'}
+    );
+
+    %invoke = (
+	code => <<'CODE'
+{
+  GExiv2Metadata *metadata;
+  metadata = gimp_image_get_metadata (image);
+  metadata_string = gimp_image_metadata_serialize (metadata);
+}
+CODE
+    );
+}
+
+sub image_metadata_set {
+    $blurb = 'set images metadata.';
+    $help  = 'Sets exif/iptc/xmp metadata to image.';
+
+    &std_pdb_misc('2013', '2.9');
+
+    @inargs = (
+  { name => 'image', type => 'image',
+    desc => 'The image' },
+	{ name => 'metadata_string', type => 'string',
+	  desc => 'The exif/ptc/xmp metadata as a string' }
+    );
+
+    %invoke = (
+	code => <<'CODE'
+{
+  GExiv2Metadata *metadata;
+  metadata = gimp_image_metadata_deserialize (metadata_string);
+  gimp_image_set_metadata (image, metadata);
+}
+CODE
+    );
+}
+
+@headers = qw("core/gimp.h"
+              "core/gimpimage.h"
+              "core/gimpimage-metadata.h"
+              <gexiv2/gexiv2.h>);
+
+@procs = qw(image_metadata_get
+            image_metadata_set);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Metadata';
+$doc_title = 'gimpimagemetadata';
+$doc_short_desc = 'Metadata procedures';
+$doc_long_desc = 'Metadata procedures.';
+
+1;
