[gimp] libgimp: start integrating image export with GimpProcedureConfig
- From: Michael Natterer <mitch src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp] libgimp: start integrating image export with GimpProcedureConfig
- Date: Wed, 9 Oct 2019 21:12:24 +0000 (UTC)
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]