[gimp] libgimp: start integrating image export with GimpProcedureConfig



commit 8219092a6c2ad7f6ba04352f8817e8817056a2c9
Author: Michael Natterer <mitch gimp org>
Date:   Wed Oct 9 22:32:07 2019 +0200

    libgimp: start integrating image export with GimpProcedureConfig
    
    Add gimp_procedure_config_begin_export() and end_export() which
    are wrappers around begin_run() and end_run() and additionally
    pretty much completely manage GimpMetadata handling.
    
    A GimpProcedureConfig can provide boolean properties "save-exif",
    "save-xmp" etc. in order to have them automatically managed by
    begin_export() and end_export(). This also restores the feature of
    overriding the procedure's saved default values with export
    preferences, but not the values from the last export, like it
    used to be in 2.10.
    
    Move gimp_image_metadata_save_prepare() and save_finish() (which are
    now completely handled by GimpProcedureConfig) from libgimpui to
    libgimp, but keep their declarations in the libgimpui header. Not
    perfect, but not finished either.
    
    Also fix gimp_image_metadata_save_prepare() to set the affected
    GimpMetadataSaveFlags to 0 when the image has no metadata at all.

 libgimp/Makefile.gi              |   1 +
 libgimp/gimp.def                 |   4 +
 libgimp/gimpimagemetadata-save.c | 515 +++++++++++++++++++++++++++++++++++++++
 libgimp/gimpimagemetadata.c      | 461 -----------------------------------
 libgimp/gimpprocedureconfig.c    | 202 ++++++++++++++-
 libgimp/gimpprocedureconfig.h    |  10 +
 libgimp/gimpui.def               |   2 -
 libgimp/meson.build              |   1 +
 8 files changed, 729 insertions(+), 467 deletions(-)
---
diff --git a/libgimp/Makefile.gi b/libgimp/Makefile.gi
index 6739572ea4..eb6875c516 100644
--- a/libgimp/Makefile.gi
+++ b/libgimp/Makefile.gi
@@ -152,6 +152,7 @@ libgimp_introspectable = \
        ../libgimp/gimpgradientselect.c         \
        ../libgimp/gimpimage.c                  \
        ../libgimp/gimpimagecolorprofile.c      \
+       ../libgimp/gimpimagemetadata-save.c     \
        ../libgimp/gimpimageprocedure.c         \
        ../libgimp/gimpitem.c                   \
        ../libgimp/gimplayer.c                  \
diff --git a/libgimp/gimp.def b/libgimp/gimp.def
index e146895ab2..bd013f7e1a 100644
--- a/libgimp/gimp.def
+++ b/libgimp/gimp.def
@@ -438,6 +438,8 @@ EXPORTS
        gimp_image_merge_down
        gimp_image_merge_layer_group
        gimp_image_merge_visible_layers
+       gimp_image_metadata_save_finish
+       gimp_image_metadata_save_prepare
        gimp_image_new
        gimp_image_new_with_precision
        gimp_image_pick_color
@@ -682,7 +684,9 @@ EXPORTS
        gimp_procedure_add_menu_path
        gimp_procedure_add_return_value
        gimp_procedure_add_return_value_from_property
+       gimp_procedure_config_begin_export
        gimp_procedure_config_begin_run
+       gimp_procedure_config_end_export
        gimp_procedure_config_end_run
        gimp_procedure_config_get_procedure
        gimp_procedure_config_get_type
diff --git a/libgimp/gimpimagemetadata-save.c b/libgimp/gimpimagemetadata-save.c
new file mode 100644
index 0000000000..4ab03a78e7
--- /dev/null
+++ b/libgimp/gimpimagemetadata-save.c
@@ -0,0 +1,515 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2000 Peter Mattis and Spencer Kimball
+ *
+ * gimpimagemetadata-save.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
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <sys/time.h>
+
+#include <gexiv2/gexiv2.h>
+
+#include "gimp.h"
+#include "gimpimagemetadata.h"
+
+#include "libgimp-intl.h"
+
+
+typedef struct
+{
+  gchar *tag;
+  gint  type;
+} XmpStructs;
+
+
+/*  public functions  */
+
+/**
+ * gimp_image_metadata_save_prepare:
+ * @image:           The image
+ * @mime_type:       The saved file's mime-type
+ * @suggested_flags: Suggested default values for the @flags passed to
+ *                   gimp_image_metadata_save_finish()
+ *
+ * Gets the image metadata for saving it using
+ * gimp_image_metadata_save_finish().
+ *
+ * The @suggested_flags are determined from what kind of metadata
+ * (Exif, XMP, ...) is actually present in the image and the preferences
+ * for metadata exporting.
+ * The calling application may still update @available_flags, for
+ * instance to follow the settings from a previous export in the same
+ * session, or a previous export of the same image. But it should not
+ * override the preferences without a good reason since it is a data
+ * leak.
+ *
+ * The suggested value for GIMP_METADATA_SAVE_THUMBNAIL is determined by
+ * whether there was a thumbnail in the previously imported image.
+ *
+ * Returns: (transfer full): The image's metadata, prepared for saving.
+ *
+ * Since: 2.10
+ */
+GimpMetadata *
+gimp_image_metadata_save_prepare (GimpImage             *image,
+                                  const gchar           *mime_type,
+                                  GimpMetadataSaveFlags *suggested_flags)
+{
+  GimpMetadata *metadata;
+
+  g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
+  g_return_val_if_fail (mime_type != NULL, NULL);
+  g_return_val_if_fail (suggested_flags != NULL, NULL);
+
+  *suggested_flags = GIMP_METADATA_SAVE_ALL;
+
+  metadata = gimp_image_get_metadata (image);
+
+  if (metadata)
+    {
+      GDateTime          *datetime;
+      const GimpParasite *comment_parasite;
+      const gchar        *comment = NULL;
+      gint                image_width;
+      gint                image_height;
+      gdouble             xres;
+      gdouble             yres;
+      gchar               buffer[32];
+      GExiv2Metadata     *g2metadata = GEXIV2_METADATA (metadata);
+
+      image_width  = gimp_image_width  (image);
+      image_height = gimp_image_height (image);
+
+      datetime = g_date_time_new_now_local ();
+
+      comment_parasite = gimp_image_get_parasite (image, "gimp-comment");
+      if (comment_parasite)
+        comment = gimp_parasite_data (comment_parasite);
+
+      /* Exif */
+
+      if (! gimp_export_exif () ||
+          ! gexiv2_metadata_has_exif (g2metadata))
+        *suggested_flags &= ~GIMP_METADATA_SAVE_EXIF;
+
+      if (comment)
+        {
+          gexiv2_metadata_set_tag_string (g2metadata,
+                                          "Exif.Photo.UserComment",
+                                          comment);
+          gexiv2_metadata_set_tag_string (g2metadata,
+                                          "Exif.Image.ImageDescription",
+                                          comment);
+        }
+
+      g_snprintf (buffer, sizeof (buffer),
+                  "%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 (g2metadata,
+                                      "Exif.Image.DateTime",
+                                      buffer);
+
+      gexiv2_metadata_set_tag_string (g2metadata,
+                                      "Exif.Image.Software",
+                                      PACKAGE_STRING);
+
+      gimp_metadata_set_pixel_size (metadata,
+                                    image_width, image_height);
+
+      gimp_image_get_resolution (image, &xres, &yres);
+      gimp_metadata_set_resolution (metadata, xres, yres,
+                                    gimp_image_get_unit (image));
+
+      /* XMP */
+
+      if (! gimp_export_xmp () ||
+          ! gexiv2_metadata_has_xmp (g2metadata))
+        *suggested_flags &= ~GIMP_METADATA_SAVE_XMP;
+
+      gexiv2_metadata_set_tag_string (g2metadata,
+                                      "Xmp.dc.Format",
+                                      mime_type);
+
+      if (! g_strcmp0 (mime_type, "image/tiff"))
+        {
+          /* TIFF specific XMP data */
+
+          g_snprintf (buffer, sizeof (buffer), "%d", image_width);
+          gexiv2_metadata_set_tag_string (g2metadata,
+                                          "Xmp.tiff.ImageWidth",
+                                          buffer);
+
+          g_snprintf (buffer, sizeof (buffer), "%d", image_height);
+          gexiv2_metadata_set_tag_string (g2metadata,
+                                          "Xmp.tiff.ImageLength",
+                                          buffer);
+
+          g_snprintf (buffer, sizeof (buffer),
+                      "%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 (g2metadata,
+                                          "Xmp.tiff.DateTime",
+                                          buffer);
+        }
+
+      /* IPTC */
+
+      if (! gimp_export_iptc () ||
+          ! gexiv2_metadata_has_iptc (g2metadata))
+        *suggested_flags &= ~GIMP_METADATA_SAVE_IPTC;
+
+      g_snprintf (buffer, sizeof (buffer),
+                  "%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 (g2metadata,
+                                      "Iptc.Application2.DateCreated",
+                                      buffer);
+
+      g_snprintf (buffer, sizeof (buffer),
+                  "%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));
+      gexiv2_metadata_set_tag_string (g2metadata,
+                                      "Iptc.Application2.TimeCreated",
+                                      buffer);
+
+      g_date_time_unref (datetime);
+
+    }
+  else
+    {
+      /* At least initialize the returned flags with preferences defaults */
+
+      if (! gimp_export_exif ())
+        *suggested_flags &= ~GIMP_METADATA_SAVE_EXIF;
+
+      if (! gimp_export_xmp ())
+        *suggested_flags &= ~GIMP_METADATA_SAVE_XMP;
+
+      if (! gimp_export_iptc ())
+        *suggested_flags &= ~GIMP_METADATA_SAVE_IPTC;
+    }
+
+  /* Thumbnail */
+
+  if (FALSE /* FIXME if (original image had a thumbnail) */)
+    *suggested_flags &= ~GIMP_METADATA_SAVE_THUMBNAIL;
+
+  /* Color profile */
+
+  if (! gimp_export_color_profile ())
+    *suggested_flags &= ~GIMP_METADATA_SAVE_COLOR_PROFILE;
+
+  return metadata;
+}
+
+
+static void
+gimp_image_metadata_copy_tag (GExiv2Metadata *src,
+                              GExiv2Metadata *dest,
+                              const gchar    *tag)
+{
+  gchar **values = gexiv2_metadata_get_tag_multiple (src, tag);
+
+  if (values)
+    {
+      gexiv2_metadata_set_tag_multiple (dest, tag, (const gchar **) values);
+      g_strfreev (values);
+    }
+  else
+    {
+      gchar *value = gexiv2_metadata_get_tag_string (src, tag);
+
+      if (value)
+        {
+          gexiv2_metadata_set_tag_string (dest, tag, value);
+          g_free (value);
+        }
+    }
+}
+
+/**
+ * gimp_image_metadata_save_finish:
+ * @image:     The image
+ * @mime_type: The saved file's mime-type
+ * @metadata:  The metadata to set on the image
+ * @flags:     Flags to specify what of the metadata to save
+ * @file:      The file to load the metadata from
+ * @error:     Return location for error message
+ *
+ * Saves the @metadata retrieved from the image with
+ * gimp_image_metadata_save_prepare() to @file, taking into account
+ * the passed @flags.
+ *
+ * Returns: Whether the save was successful.
+ *
+ * Since: 2.10
+ */
+gboolean
+gimp_image_metadata_save_finish (GimpImage              *image,
+                                 const gchar            *mime_type,
+                                 GimpMetadata           *metadata,
+                                 GimpMetadataSaveFlags   flags,
+                                 GFile                  *file,
+                                 GError                **error)
+{
+  GimpMetadata   *new_metadata;
+  GExiv2Metadata *new_g2metadata;
+  gboolean        support_exif;
+  gboolean        support_xmp;
+  gboolean        support_iptc;
+  gboolean        success = FALSE;
+  gint            i;
+
+  g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
+  g_return_val_if_fail (mime_type != NULL, FALSE);
+  g_return_val_if_fail (GEXIV2_IS_METADATA (metadata), FALSE);
+  g_return_val_if_fail (G_IS_FILE (file), FALSE);
+  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+  if (! (flags & (GIMP_METADATA_SAVE_EXIF |
+                  GIMP_METADATA_SAVE_XMP  |
+                  GIMP_METADATA_SAVE_IPTC |
+                  GIMP_METADATA_SAVE_THUMBNAIL)))
+    return TRUE;
+
+  /* read metadata from saved file */
+  new_metadata = gimp_metadata_load_from_file (file, error);
+  new_g2metadata = GEXIV2_METADATA (new_metadata);
+
+  if (! new_metadata)
+    return FALSE;
+
+  support_exif = gexiv2_metadata_get_supports_exif (new_g2metadata);
+  support_xmp  = gexiv2_metadata_get_supports_xmp  (new_g2metadata);
+  support_iptc = gexiv2_metadata_get_supports_iptc (new_g2metadata);
+
+  if ((flags & GIMP_METADATA_SAVE_EXIF) && support_exif)
+    {
+      gchar **exif_data = gexiv2_metadata_get_exif_tags (GEXIV2_METADATA (metadata));
+
+      for (i = 0; exif_data[i] != NULL; i++)
+        {
+          if (! gexiv2_metadata_has_tag (new_g2metadata, exif_data[i]) &&
+              gimp_metadata_is_tag_supported (exif_data[i], mime_type))
+            {
+              gimp_image_metadata_copy_tag (GEXIV2_METADATA (metadata),
+                                            new_g2metadata,
+                                            exif_data[i]);
+            }
+        }
+
+      g_strfreev (exif_data);
+    }
+
+  if ((flags & GIMP_METADATA_SAVE_XMP) && support_xmp)
+    {
+      static const XmpStructs structlist[] =
+      {
+        { "Xmp.iptcExt.LocationCreated", GEXIV2_STRUCTURE_XA_BAG },
+        { "Xmp.iptcExt.LocationShown",   GEXIV2_STRUCTURE_XA_BAG },
+        { "Xmp.iptcExt.ArtworkOrObject", GEXIV2_STRUCTURE_XA_BAG },
+        { "Xmp.iptcExt.RegistryId",      GEXIV2_STRUCTURE_XA_BAG },
+        { "Xmp.xmpMM.History",           GEXIV2_STRUCTURE_XA_SEQ },
+        { "Xmp.plus.ImageSupplier",      GEXIV2_STRUCTURE_XA_SEQ },
+        { "Xmp.plus.ImageCreator",       GEXIV2_STRUCTURE_XA_SEQ },
+        { "Xmp.plus.CopyrightOwner",     GEXIV2_STRUCTURE_XA_SEQ },
+        { "Xmp.plus.Licensor",           GEXIV2_STRUCTURE_XA_SEQ }
+      };
+
+      gchar         **xmp_data;
+      struct timeval  timer_usec;
+      gint64          timestamp_usec;
+      gchar           ts[128];
+
+      gettimeofday (&timer_usec, NULL);
+      timestamp_usec = ((gint64) timer_usec.tv_sec) * 1000000ll +
+                        (gint64) timer_usec.tv_usec;
+      g_snprintf (ts, sizeof (ts), "%" G_GINT64_FORMAT, timestamp_usec);
+
+      gimp_metadata_add_xmp_history (metadata, "");
+
+      gexiv2_metadata_set_tag_string (GEXIV2_METADATA (metadata),
+                                      "Xmp.GIMP.TimeStamp",
+                                      ts);
+
+      gexiv2_metadata_set_tag_string (GEXIV2_METADATA (metadata),
+                                      "Xmp.xmp.CreatorTool",
+                                      N_("GIMP 2.10"));
+
+      gexiv2_metadata_set_tag_string (GEXIV2_METADATA (metadata),
+                                      "Xmp.GIMP.Version",
+                                      GIMP_VERSION);
+
+      gexiv2_metadata_set_tag_string (GEXIV2_METADATA (metadata),
+                                      "Xmp.GIMP.API",
+                                      GIMP_API_VERSION);
+
+      gexiv2_metadata_set_tag_string (GEXIV2_METADATA (metadata),
+                                      "Xmp.GIMP.Platform",
+#if defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__)
+                                      "Windows"
+#elif defined(__linux__)
+                                      "Linux"
+#elif defined(__APPLE__) && defined(__MACH__)
+                                      "Mac OS"
+#elif defined(unix) || defined(__unix__) || defined(__unix)
+                                      "Unix"
+#else
+                                      "Unknown"
+#endif
+                                      );
+
+      xmp_data = gexiv2_metadata_get_xmp_tags (GEXIV2_METADATA (metadata));
+
+      /* Patch necessary structures */
+      for (i = 0; i < G_N_ELEMENTS (structlist); i++)
+        {
+          gexiv2_metadata_set_xmp_tag_struct (GEXIV2_METADATA (new_g2metadata),
+                                              structlist[i].tag,
+                                              structlist[i].type);
+        }
+
+      for (i = 0; xmp_data[i] != NULL; i++)
+        {
+          if (! gexiv2_metadata_has_tag (new_g2metadata, xmp_data[i]) &&
+              gimp_metadata_is_tag_supported (xmp_data[i], mime_type))
+            {
+              gimp_image_metadata_copy_tag (GEXIV2_METADATA (metadata),
+                                            new_g2metadata,
+                                            xmp_data[i]);
+            }
+        }
+
+      g_strfreev (xmp_data);
+    }
+
+  if ((flags & GIMP_METADATA_SAVE_IPTC) && support_iptc)
+    {
+      gchar **iptc_data = gexiv2_metadata_get_iptc_tags (GEXIV2_METADATA (metadata));
+
+      for (i = 0; iptc_data[i] != NULL; i++)
+        {
+          if (! gexiv2_metadata_has_tag (new_g2metadata, iptc_data[i]) &&
+              gimp_metadata_is_tag_supported (iptc_data[i], mime_type))
+            {
+              gimp_image_metadata_copy_tag (GEXIV2_METADATA (metadata),
+                                            new_g2metadata,
+                                            iptc_data[i]);
+            }
+        }
+
+      g_strfreev (iptc_data);
+    }
+
+  if (flags & GIMP_METADATA_SAVE_THUMBNAIL)
+    {
+      GdkPixbuf *thumb_pixbuf;
+      gchar     *thumb_buffer;
+      gint       image_width;
+      gint       image_height;
+      gsize      count;
+      gint       thumbw;
+      gint       thumbh;
+
+#define EXIF_THUMBNAIL_SIZE 256
+
+      image_width  = gimp_image_width  (image);
+      image_height = gimp_image_height (image);
+
+      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_width / image_height;
+        }
+
+      thumb_pixbuf = gimp_image_get_thumbnail (image, thumbw, thumbh,
+                                               GIMP_PIXBUF_KEEP_ALPHA);
+
+      if (gdk_pixbuf_save_to_buffer (thumb_pixbuf, &thumb_buffer, &count,
+                                     "jpeg", NULL,
+                                     "quality", "75",
+                                     NULL))
+        {
+          gchar buffer[32];
+
+          gexiv2_metadata_set_exif_thumbnail_from_buffer (new_g2metadata,
+                                                          (guchar *) thumb_buffer,
+                                                          count);
+
+          g_snprintf (buffer, sizeof (buffer), "%d", thumbw);
+          gexiv2_metadata_set_tag_string (new_g2metadata,
+                                          "Exif.Thumbnail.ImageWidth",
+                                          buffer);
+
+          g_snprintf (buffer, sizeof (buffer), "%d", thumbh);
+          gexiv2_metadata_set_tag_string (new_g2metadata,
+                                          "Exif.Thumbnail.ImageLength",
+                                          buffer);
+
+          gexiv2_metadata_set_tag_string (new_g2metadata,
+                                          "Exif.Thumbnail.BitsPerSample",
+                                          "8 8 8");
+          gexiv2_metadata_set_tag_string (new_g2metadata,
+                                          "Exif.Thumbnail.SamplesPerPixel",
+                                          "3");
+          gexiv2_metadata_set_tag_string (new_g2metadata,
+                                          "Exif.Thumbnail.PhotometricInterpretation",
+                                          "6");
+
+          g_free (thumb_buffer);
+        }
+
+      g_object_unref (thumb_pixbuf);
+    }
+
+  if (flags & GIMP_METADATA_SAVE_COLOR_PROFILE)
+    {
+      /* nothing to do, but if we ever need to modify metadata based
+       * on the exported color profile, this is probably the place to
+       * add it
+       */
+    }
+
+  success = gimp_metadata_save_to_file (new_metadata, file, error);
+
+  g_object_unref (new_metadata);
+
+  return success;
+}
diff --git a/libgimp/gimpimagemetadata.c b/libgimp/gimpimagemetadata.c
index ce770c8bde..9e999e2781 100644
--- a/libgimp/gimpimagemetadata.c
+++ b/libgimp/gimpimagemetadata.c
@@ -198,467 +198,6 @@ gimp_image_metadata_load_finish (GimpImage             *image,
   gimp_image_set_metadata (image, metadata);
 }
 
-/**
- * gimp_image_metadata_save_prepare:
- * @image:           The image
- * @mime_type:       The saved file's mime-type
- * @suggested_flags: Suggested default values for the @flags passed to
- *                   gimp_image_metadata_save_finish()
- *
- * Gets the image metadata for saving it using
- * gimp_image_metadata_save_finish().
- *
- * The @suggested_flags are determined from what kind of metadata
- * (Exif, XMP, ...) is actually present in the image and the preferences
- * for metadata exporting.
- * The calling application may still update @available_flags, for
- * instance to follow the settings from a previous export in the same
- * session, or a previous export of the same image. But it should not
- * override the preferences without a good reason since it is a data
- * leak.
- *
- * The suggested value for GIMP_METADATA_SAVE_THUMBNAIL is determined by
- * whether there was a thumbnail in the previously imported image.
- *
- * Returns: (transfer full): The image's metadata, prepared for saving.
- *
- * Since: 2.10
- */
-GimpMetadata *
-gimp_image_metadata_save_prepare (GimpImage             *image,
-                                  const gchar           *mime_type,
-                                  GimpMetadataSaveFlags *suggested_flags)
-{
-  GimpMetadata *metadata;
-
-  g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
-  g_return_val_if_fail (mime_type != NULL, NULL);
-  g_return_val_if_fail (suggested_flags != NULL, NULL);
-
-  *suggested_flags = GIMP_METADATA_SAVE_ALL;
-
-  metadata = gimp_image_get_metadata (image);
-
-  if (metadata)
-    {
-      GDateTime          *datetime;
-      const GimpParasite *comment_parasite;
-      const gchar        *comment = NULL;
-      gint                image_width;
-      gint                image_height;
-      gdouble             xres;
-      gdouble             yres;
-      gchar               buffer[32];
-      GExiv2Metadata     *g2metadata = GEXIV2_METADATA (metadata);
-
-      image_width  = gimp_image_width  (image);
-      image_height = gimp_image_height (image);
-
-      datetime = g_date_time_new_now_local ();
-
-      comment_parasite = gimp_image_get_parasite (image, "gimp-comment");
-      if (comment_parasite)
-        comment = gimp_parasite_data (comment_parasite);
-
-      /* Exif */
-
-      if (! gimp_export_exif () ||
-          ! gexiv2_metadata_has_exif (g2metadata))
-        *suggested_flags &= ~GIMP_METADATA_SAVE_EXIF;
-
-      if (comment)
-        {
-          gexiv2_metadata_set_tag_string (g2metadata,
-                                          "Exif.Photo.UserComment",
-                                          comment);
-          gexiv2_metadata_set_tag_string (g2metadata,
-                                          "Exif.Image.ImageDescription",
-                                          comment);
-        }
-
-      g_snprintf (buffer, sizeof (buffer),
-                  "%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 (g2metadata,
-                                      "Exif.Image.DateTime",
-                                      buffer);
-
-      gexiv2_metadata_set_tag_string (g2metadata,
-                                      "Exif.Image.Software",
-                                      PACKAGE_STRING);
-
-      gimp_metadata_set_pixel_size (metadata,
-                                    image_width, image_height);
-
-      gimp_image_get_resolution (image, &xres, &yres);
-      gimp_metadata_set_resolution (metadata, xres, yres,
-                                    gimp_image_get_unit (image));
-
-      /* XMP */
-
-      if (! gimp_export_xmp () ||
-          ! gexiv2_metadata_has_xmp (g2metadata))
-        *suggested_flags &= ~GIMP_METADATA_SAVE_XMP;
-
-      gexiv2_metadata_set_tag_string (g2metadata,
-                                      "Xmp.dc.Format",
-                                      mime_type);
-
-      if (! g_strcmp0 (mime_type, "image/tiff"))
-        {
-          /* TIFF specific XMP data */
-
-          g_snprintf (buffer, sizeof (buffer), "%d", image_width);
-          gexiv2_metadata_set_tag_string (g2metadata,
-                                          "Xmp.tiff.ImageWidth",
-                                          buffer);
-
-          g_snprintf (buffer, sizeof (buffer), "%d", image_height);
-          gexiv2_metadata_set_tag_string (g2metadata,
-                                          "Xmp.tiff.ImageLength",
-                                          buffer);
-
-          g_snprintf (buffer, sizeof (buffer),
-                      "%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 (g2metadata,
-                                          "Xmp.tiff.DateTime",
-                                          buffer);
-        }
-
-      /* IPTC */
-
-      if (! gimp_export_iptc () ||
-          ! gexiv2_metadata_has_iptc (g2metadata))
-        *suggested_flags &= ~GIMP_METADATA_SAVE_IPTC;
-
-      g_snprintf (buffer, sizeof (buffer),
-                  "%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 (g2metadata,
-                                      "Iptc.Application2.DateCreated",
-                                      buffer);
-
-      g_snprintf (buffer, sizeof (buffer),
-                  "%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));
-      gexiv2_metadata_set_tag_string (g2metadata,
-                                      "Iptc.Application2.TimeCreated",
-                                      buffer);
-
-      g_date_time_unref (datetime);
-
-    }
-
-  /* Thumbnail */
-
-  if (FALSE /* FIXME if (original image had a thumbnail) */)
-    *suggested_flags &= ~GIMP_METADATA_SAVE_THUMBNAIL;
-
-  /* Color profile */
-
-  if (! gimp_export_color_profile ())
-    *suggested_flags &= ~GIMP_METADATA_SAVE_COLOR_PROFILE;
-
-  return metadata;
-}
-
-
-static void
-gimp_image_metadata_copy_tag (GExiv2Metadata *src,
-                              GExiv2Metadata *dest,
-                              const gchar    *tag)
-{
-  gchar **values = gexiv2_metadata_get_tag_multiple (src, tag);
-
-  if (values)
-    {
-      gexiv2_metadata_set_tag_multiple (dest, tag, (const gchar **) values);
-      g_strfreev (values);
-    }
-  else
-    {
-      gchar *value = gexiv2_metadata_get_tag_string (src, tag);
-
-      if (value)
-        {
-          gexiv2_metadata_set_tag_string (dest, tag, value);
-          g_free (value);
-        }
-    }
-}
-
-/**
- * gimp_image_metadata_save_finish:
- * @image:     The image
- * @mime_type: The saved file's mime-type
- * @metadata:  The metadata to set on the image
- * @flags:     Flags to specify what of the metadata to save
- * @file:      The file to load the metadata from
- * @error:     Return location for error message
- *
- * Saves the @metadata retrieved from the image with
- * gimp_image_metadata_save_prepare() to @file, taking into account
- * the passed @flags.
- *
- * Returns: Whether the save was successful.
- *
- * Since: 2.10
- */
-gboolean
-gimp_image_metadata_save_finish (GimpImage              *image,
-                                 const gchar            *mime_type,
-                                 GimpMetadata           *metadata,
-                                 GimpMetadataSaveFlags   flags,
-                                 GFile                  *file,
-                                 GError                **error)
-{
-  GimpMetadata   *new_metadata;
-  GExiv2Metadata *new_g2metadata;
-  gboolean        support_exif;
-  gboolean        support_xmp;
-  gboolean        support_iptc;
-  gboolean        success = FALSE;
-  gint            i;
-
-  g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
-  g_return_val_if_fail (mime_type != NULL, FALSE);
-  g_return_val_if_fail (GEXIV2_IS_METADATA (metadata), FALSE);
-  g_return_val_if_fail (G_IS_FILE (file), FALSE);
-  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
-
-  if (! (flags & (GIMP_METADATA_SAVE_EXIF |
-                  GIMP_METADATA_SAVE_XMP  |
-                  GIMP_METADATA_SAVE_IPTC |
-                  GIMP_METADATA_SAVE_THUMBNAIL)))
-    return TRUE;
-
-  /* read metadata from saved file */
-  new_metadata = gimp_metadata_load_from_file (file, error);
-  new_g2metadata = GEXIV2_METADATA (new_metadata);
-
-  if (! new_metadata)
-    return FALSE;
-
-  support_exif = gexiv2_metadata_get_supports_exif (new_g2metadata);
-  support_xmp  = gexiv2_metadata_get_supports_xmp  (new_g2metadata);
-  support_iptc = gexiv2_metadata_get_supports_iptc (new_g2metadata);
-
-  if ((flags & GIMP_METADATA_SAVE_EXIF) && support_exif)
-    {
-      gchar **exif_data = gexiv2_metadata_get_exif_tags (GEXIV2_METADATA (metadata));
-
-      for (i = 0; exif_data[i] != NULL; i++)
-        {
-          if (! gexiv2_metadata_has_tag (new_g2metadata, exif_data[i]) &&
-              gimp_metadata_is_tag_supported (exif_data[i], mime_type))
-            {
-              gimp_image_metadata_copy_tag (GEXIV2_METADATA (metadata),
-                                            new_g2metadata,
-                                            exif_data[i]);
-            }
-        }
-
-      g_strfreev (exif_data);
-    }
-
-  if ((flags & GIMP_METADATA_SAVE_XMP) && support_xmp)
-    {
-      static const XmpStructs structlist[] =
-      {
-        { "Xmp.iptcExt.LocationCreated", GEXIV2_STRUCTURE_XA_BAG },
-        { "Xmp.iptcExt.LocationShown",   GEXIV2_STRUCTURE_XA_BAG },
-        { "Xmp.iptcExt.ArtworkOrObject", GEXIV2_STRUCTURE_XA_BAG },
-        { "Xmp.iptcExt.RegistryId",      GEXIV2_STRUCTURE_XA_BAG },
-        { "Xmp.xmpMM.History",           GEXIV2_STRUCTURE_XA_SEQ },
-        { "Xmp.plus.ImageSupplier",      GEXIV2_STRUCTURE_XA_SEQ },
-        { "Xmp.plus.ImageCreator",       GEXIV2_STRUCTURE_XA_SEQ },
-        { "Xmp.plus.CopyrightOwner",     GEXIV2_STRUCTURE_XA_SEQ },
-        { "Xmp.plus.Licensor",           GEXIV2_STRUCTURE_XA_SEQ }
-      };
-
-      gchar         **xmp_data;
-      struct timeval  timer_usec;
-      gint64          timestamp_usec;
-      gchar           ts[128];
-
-      gettimeofday (&timer_usec, NULL);
-      timestamp_usec = ((gint64) timer_usec.tv_sec) * 1000000ll +
-                        (gint64) timer_usec.tv_usec;
-      g_snprintf (ts, sizeof (ts), "%" G_GINT64_FORMAT, timestamp_usec);
-
-      gimp_metadata_add_xmp_history (metadata, "");
-
-      gexiv2_metadata_set_tag_string (GEXIV2_METADATA (metadata),
-                                      "Xmp.GIMP.TimeStamp",
-                                      ts);
-
-      gexiv2_metadata_set_tag_string (GEXIV2_METADATA (metadata),
-                                      "Xmp.xmp.CreatorTool",
-                                      N_("GIMP 2.10"));
-
-      gexiv2_metadata_set_tag_string (GEXIV2_METADATA (metadata),
-                                      "Xmp.GIMP.Version",
-                                      GIMP_VERSION);
-
-      gexiv2_metadata_set_tag_string (GEXIV2_METADATA (metadata),
-                                      "Xmp.GIMP.API",
-                                      GIMP_API_VERSION);
-
-      gexiv2_metadata_set_tag_string (GEXIV2_METADATA (metadata),
-                                      "Xmp.GIMP.Platform",
-#if defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__)
-                                      "Windows"
-#elif defined(__linux__)
-                                      "Linux"
-#elif defined(__APPLE__) && defined(__MACH__)
-                                      "Mac OS"
-#elif defined(unix) || defined(__unix__) || defined(__unix)
-                                      "Unix"
-#else
-                                      "Unknown"
-#endif
-                                      );
-
-      xmp_data = gexiv2_metadata_get_xmp_tags (GEXIV2_METADATA (metadata));
-
-      /* Patch necessary structures */
-      for (i = 0; i < G_N_ELEMENTS (structlist); i++)
-        {
-          gexiv2_metadata_set_xmp_tag_struct (GEXIV2_METADATA (new_g2metadata),
-                                              structlist[i].tag,
-                                              structlist[i].type);
-        }
-
-      for (i = 0; xmp_data[i] != NULL; i++)
-        {
-          if (! gexiv2_metadata_has_tag (new_g2metadata, xmp_data[i]) &&
-              gimp_metadata_is_tag_supported (xmp_data[i], mime_type))
-            {
-              gimp_image_metadata_copy_tag (GEXIV2_METADATA (metadata),
-                                            new_g2metadata,
-                                            xmp_data[i]);
-            }
-        }
-
-      g_strfreev (xmp_data);
-    }
-
-  if ((flags & GIMP_METADATA_SAVE_IPTC) && support_iptc)
-    {
-      gchar **iptc_data = gexiv2_metadata_get_iptc_tags (GEXIV2_METADATA (metadata));
-
-      for (i = 0; iptc_data[i] != NULL; i++)
-        {
-          if (! gexiv2_metadata_has_tag (new_g2metadata, iptc_data[i]) &&
-              gimp_metadata_is_tag_supported (iptc_data[i], mime_type))
-            {
-              gimp_image_metadata_copy_tag (GEXIV2_METADATA (metadata),
-                                            new_g2metadata,
-                                            iptc_data[i]);
-            }
-        }
-
-      g_strfreev (iptc_data);
-    }
-
-  if (flags & GIMP_METADATA_SAVE_THUMBNAIL)
-    {
-      GdkPixbuf *thumb_pixbuf;
-      gchar     *thumb_buffer;
-      gint       image_width;
-      gint       image_height;
-      gsize      count;
-      gint       thumbw;
-      gint       thumbh;
-
-#define EXIF_THUMBNAIL_SIZE 256
-
-      image_width  = gimp_image_width  (image);
-      image_height = gimp_image_height (image);
-
-      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_width / image_height;
-        }
-
-      thumb_pixbuf = gimp_image_get_thumbnail (image, thumbw, thumbh,
-                                               GIMP_PIXBUF_KEEP_ALPHA);
-
-      if (gdk_pixbuf_save_to_buffer (thumb_pixbuf, &thumb_buffer, &count,
-                                     "jpeg", NULL,
-                                     "quality", "75",
-                                     NULL))
-        {
-          gchar buffer[32];
-
-          gexiv2_metadata_set_exif_thumbnail_from_buffer (new_g2metadata,
-                                                          (guchar *) thumb_buffer,
-                                                          count);
-
-          g_snprintf (buffer, sizeof (buffer), "%d", thumbw);
-          gexiv2_metadata_set_tag_string (new_g2metadata,
-                                          "Exif.Thumbnail.ImageWidth",
-                                          buffer);
-
-          g_snprintf (buffer, sizeof (buffer), "%d", thumbh);
-          gexiv2_metadata_set_tag_string (new_g2metadata,
-                                          "Exif.Thumbnail.ImageLength",
-                                          buffer);
-
-          gexiv2_metadata_set_tag_string (new_g2metadata,
-                                          "Exif.Thumbnail.BitsPerSample",
-                                          "8 8 8");
-          gexiv2_metadata_set_tag_string (new_g2metadata,
-                                          "Exif.Thumbnail.SamplesPerPixel",
-                                          "3");
-          gexiv2_metadata_set_tag_string (new_g2metadata,
-                                          "Exif.Thumbnail.PhotometricInterpretation",
-                                          "6");
-
-          g_free (thumb_buffer);
-        }
-
-      g_object_unref (thumb_pixbuf);
-    }
-
-  if (flags & GIMP_METADATA_SAVE_COLOR_PROFILE)
-    {
-      /* nothing to do, but if we ever need to modify metadata based
-       * on the exported color profile, this is probably the place to
-       * add it
-       */
-    }
-
-  success = gimp_metadata_save_to_file (new_metadata, file, error);
-
-  g_object_unref (new_metadata);
-
-  return success;
-}
-
 /**
  * gimp_image_metadata_load_thumbnail:
  * @file:  A #GFile image
diff --git a/libgimp/gimpprocedureconfig.c b/libgimp/gimpprocedureconfig.c
index 799e00b494..12dee18a02 100644
--- a/libgimp/gimpprocedureconfig.c
+++ b/libgimp/gimpprocedureconfig.c
@@ -22,6 +22,7 @@
 #include "config.h"
 
 #include "gimp.h"
+#include "gimpimagemetadata.h"
 
 #include "gimpprocedureconfig-private.h"
 
@@ -57,10 +58,14 @@ enum
 
 struct _GimpProcedureConfigPrivate
 {
-  GimpProcedure *procedure;
+  GimpProcedure         *procedure;
 
-  GimpImage     *image;
-  GimpRunMode    run_mode;
+  GimpImage             *image;
+  GimpRunMode            run_mode;
+
+  GimpMetadata          *metadata;
+  gchar                 *mime_type;
+  GimpMetadataSaveFlags  metadata_flags;
 };
 
 
@@ -83,6 +88,20 @@ G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GimpProcedureConfig, gimp_procedure_config,
 
 static GParamSpec *props[N_PROPS] = { NULL, };
 
+static const struct
+{
+  const gchar           *name;
+  GimpMetadataSaveFlags  flag;
+}
+metadata_properties[] =
+{
+  { "save-exif",          GIMP_METADATA_SAVE_EXIF          },
+  { "save-xmp",           GIMP_METADATA_SAVE_XMP           },
+  { "save-iptc",          GIMP_METADATA_SAVE_IPTC          },
+  { "save-thumbnail",     GIMP_METADATA_SAVE_THUMBNAIL     },
+  { "save-color-profile", GIMP_METADATA_SAVE_COLOR_PROFILE }
+};
+
 
 static void
 gimp_procedure_config_class_init (GimpProcedureConfigClass *klass)
@@ -129,6 +148,8 @@ gimp_procedure_config_dispose (GObject *object)
   GimpProcedureConfig *config = GIMP_PROCEDURE_CONFIG (object);
 
   g_clear_object (&config->priv->procedure);
+  g_clear_object (&config->priv->metadata);
+  g_clear_pointer (&config->priv->mime_type, g_free);
 
   G_OBJECT_CLASS (parent_class)->dispose (object);
 }
@@ -183,7 +204,7 @@ gimp_procedure_config_get_property (GObject    *object,
  * This function returns the #GimpProcedure which created @config, see
  * gimp_procedure_create_config().
  *
- * Returns: The #GimpProcedure which created @config.
+ * Returns: (transfer none): The #GimpProcedure which created @config.
  *
  * Since: 3.0
  **/
@@ -421,6 +442,179 @@ gimp_procedure_config_end_run (GimpProcedureConfig *config,
   config->priv->run_mode = -1;
 }
 
+/**
+ * gimp_procedure_config_begin_export:
+ * @config:         a #GimpProcedureConfig
+ * @original_image: the #GimpImage passed to run()
+ * @run_mode:       the #GimpRunMode passed to a #GimpProcedure's run()
+ * @args:           the #GimpValueArray passed to a #GimpProcedure's run()
+ * @mime_type:      the exported file format's mime type
+ *
+ * This is a variant of gimp_procedure_config_begin_run() to be used
+ * by file export procedures using #GimpSaveProcedure. It must be
+ * paired with a call to gimp_procedure_config_end_export() at the end
+ * of run().
+ *
+ * It does everything gimp_procedure_config_begin_run() does but
+ * provides additional features to automate file export:
+ *
+ * Exporting metadata is handled automatically, by calling
+ * gimp_image_metadata_save_prepare() and syncing its returned
+ * #GimpMetadataSaveFlags with @config's properties. (The
+ * corresponding gimp_image_metadata_save_finish() will be called by
+ * gimp_procedure_config_end_export()).
+ *
+ * The following boolean arguments of the used #GimpSaveProcedure are
+ * synced. The procedure can but must not provide these arguments.
+ *
+ * "save-exif" for %GIMP_METADATA_SAVE_EXIF.
+ *
+ * "save-xmp" for %GIMP_METADATA_SAVE_XMP.
+ *
+ * "save-iptc" for %GIMP_METADATA_SAVE_IPTC.
+ *
+ * "save-thumbnail" for %GIMP_METADATA_SAVE_THUMBNAIL.
+ *
+ * "save-color-profile" for %GIMP_METADATA_SAVE_COLOR_PROFILE.
+ *
+ * The values from the #GimpMetadataSaveFlags will only ever be used
+ * to set these properties to %FALSE, overriding the user's saved
+ * default values for the procedure, but NOT overriding the last used
+ * values from exporting @original_image or the last used values from
+ * exporting any other image using this procedure.
+ *
+ * Returns: (transfer none): The #GimpMetadata to be used for this
+ *          export, or %NULL if @original_image doesn't have metadata.
+ *
+ * Since: 3.0
+ **/
+GimpMetadata *
+gimp_procedure_config_begin_export (GimpProcedureConfig  *config,
+                                    GimpImage            *original_image,
+                                    GimpRunMode           run_mode,
+                                    const GimpValueArray *args,
+                                    const gchar          *mime_type)
+{
+  GObjectClass          *object_class;
+  GimpMetadataSaveFlags  metadata_flags;
+  gint                   i;
+
+  g_return_val_if_fail (GIMP_IS_PROCEDURE_CONFIG (config), NULL);
+  g_return_val_if_fail (GIMP_IS_IMAGE (original_image), NULL);
+  g_return_val_if_fail (args != NULL, NULL);
+  g_return_val_if_fail (mime_type != NULL, NULL);
+
+  object_class = G_OBJECT_GET_CLASS (config);
+
+  config->priv->metadata =
+    gimp_image_metadata_save_prepare (original_image,
+                                      mime_type,
+                                      &metadata_flags);
+
+  if (config->priv->metadata)
+    {
+      config->priv->mime_type      = g_strdup (mime_type);
+      config->priv->metadata_flags = metadata_flags;
+    }
+
+  for (i = 0; i < G_N_ELEMENTS (metadata_properties); i++)
+    {
+      /*  we only disable properties based on metadata flags here and
+       *  never enable them, so we don't override the user's saved
+       *  default values that are passed to us via "args"
+       */
+      if (! (metadata_flags &  metadata_properties[i].flag))
+        {
+          const gchar *prop_name = metadata_properties[i].name;
+          GParamSpec  *pspec;
+
+          pspec = g_object_class_find_property (object_class, prop_name);
+          if (pspec)
+            g_object_set (config,
+                          prop_name, FALSE,
+                          NULL);
+        }
+    }
+
+  gimp_procedure_config_begin_run (config, original_image, run_mode, args);
+
+  return config->priv->metadata;
+}
+
+/**
+ * gimp_procedure_config_end_export:
+ * @config:         a #GimpProcedureConfig
+ * @exported_image: the #GimpImage that was actually exported
+ * @file:           the #GFile @exported_image was written to
+ * @status:         the return status of the #GimpProcedure's run()
+ *
+ * This is a variant of gimp_procedure_config_end_run() to be used by
+ * file export procedures using #GimpSaveProcedure. It must be paired
+ * with a call to gimp_procedure_config_begin_export() at the
+ * beginning of run().
+ *
+ * It does everything gimp_procedure_config_begin_run() does but
+ * provides additional features to automate file export:
+ *
+ * If @status is %GIMP_PDB_SUCCESS, and
+ * gimp_procedure_config_begin_export() returned a #GimpMetadata,
+ * @config's export properties are synced back to the metadata's
+ * #GimpMetadataSaveFlags and the metadata is written to @file using
+ * gimp_image_metadata_save_finish().
+ *
+ * Since: 3.0
+ **/
+void
+gimp_procedure_config_end_export (GimpProcedureConfig  *config,
+                                  GimpImage            *exported_image,
+                                  GFile                *file,
+                                  GimpPDBStatusType     status)
+{
+  g_return_if_fail (GIMP_IS_PROCEDURE_CONFIG (config));
+  g_return_if_fail (GIMP_IS_IMAGE (exported_image));
+  g_return_if_fail (G_IS_FILE (file));
+
+  if (status == GIMP_PDB_SUCCESS &&
+      config->priv->metadata)
+    {
+      GObjectClass *object_class = G_OBJECT_GET_CLASS (config);
+      gint          i;
+
+      for (i = 0; i < G_N_ELEMENTS (metadata_properties); i++)
+        {
+          const gchar           *prop_name = metadata_properties[i].name;
+          GimpMetadataSaveFlags  prop_flag = metadata_properties[i].flag;
+          GParamSpec            *pspec;
+          gboolean               value;
+
+          pspec = g_object_class_find_property (object_class, prop_name);
+          if (pspec)
+            {
+              g_object_get (config,
+                            prop_name, &value,
+                            NULL);
+
+              if (value)
+                config->priv->metadata_flags |= prop_flag;
+              else
+                config->priv->metadata_flags &= ~prop_flag;
+            }
+
+          gimp_image_metadata_save_finish (exported_image,
+                                           config->priv->mime_type,
+                                           config->priv->metadata,
+                                           config->priv->metadata_flags,
+                                           file, NULL);
+        }
+    }
+
+  g_clear_object (&config->priv->metadata);
+  g_clear_pointer (&config->priv->mime_type, g_free);
+  config->priv->metadata_flags = 0;
+
+  gimp_procedure_config_end_run (config, status);
+}
+
 
 /*  private functions  */
 
diff --git a/libgimp/gimpprocedureconfig.h b/libgimp/gimpprocedureconfig.h
index 7a913505d5..a79f29c5a5 100644
--- a/libgimp/gimpprocedureconfig.h
+++ b/libgimp/gimpprocedureconfig.h
@@ -82,6 +82,16 @@ void    gimp_procedure_config_begin_run     (GimpProcedureConfig  *config,
 void    gimp_procedure_config_end_run       (GimpProcedureConfig  *config,
                                              GimpPDBStatusType     status);
 
+GimpMetadata *
+        gimp_procedure_config_begin_export  (GimpProcedureConfig  *config,
+                                             GimpImage            *original_image,
+                                             GimpRunMode           run_mode,
+                                             const GimpValueArray *args,
+                                             const gchar          *mime_type);
+void    gimp_procedure_config_end_export    (GimpProcedureConfig  *config,
+                                             GimpImage            *exported_image,
+                                             GFile                *file,
+                                             GimpPDBStatusType     status);
 
 G_END_DECLS
 
diff --git a/libgimp/gimpui.def b/libgimp/gimpui.def
index 3061930900..e7997c0aa6 100644
--- a/libgimp/gimpui.def
+++ b/libgimp/gimpui.def
@@ -28,8 +28,6 @@ EXPORTS
        gimp_image_metadata_load_finish
        gimp_image_metadata_load_prepare
        gimp_image_metadata_load_thumbnail
-       gimp_image_metadata_save_finish
-       gimp_image_metadata_save_prepare
        gimp_layer_combo_box_get_type
        gimp_layer_combo_box_new
        gimp_palette_select_button_get_palette
diff --git a/libgimp/meson.build b/libgimp/meson.build
index 47678de619..3ef9085edf 100644
--- a/libgimp/meson.build
+++ b/libgimp/meson.build
@@ -145,6 +145,7 @@ libgimp_sources_introspectable = [
   'gimpgradientselect.c',
   'gimpimage.c',
   'gimpimagecolorprofile.c',
+  'gimpimagemetadata-save.c',
   'gimpimageprocedure.c',
   'gimpitem.c',
   'gimplayer.c',


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]