[gimp/metadata-wip-rebased] Metadata WIP
- From: Michael Natterer <mitch src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp/metadata-wip-rebased] Metadata WIP
- Date: Sat, 19 Oct 2013 21:00:03 +0000 (UTC)
commit 9aaab61e2ae9b289999414c58a4d9cd921d19b96
Author: Michael Natterer <mitch gimp org>
Date: Sat Oct 19 18:38:01 2013 +0200
Metadata WIP
app/Makefile.am | 1 +
app/core/Makefile.am | 3 +
app/core/gimpimage-metadata.c | 69 +++
app/core/gimpimage-metadata.h | 27 +
app/core/gimpimage-private.h | 2 +
app/core/gimpimage.c | 63 +++-
app/file/Makefile.am | 4 +-
app/file/file-open.c | 160 ++++++-
app/file/file-save.c | 384 ++++++++++++++
app/pdb/image-cmds.c | 122 +++++
app/pdb/internal-procs.c | 2 +-
app/xcf/xcf-load.c | 17 +
app/xcf/xcf-save.c | 18 +-
configure.ac | 29 +-
libgimp/gimp.def | 2 +
libgimp/gimpimage.c | 60 +++
libgimp/gimpimage.h | 4 +
libgimp/gimpimage_pdb.c | 62 +++
libgimp/gimpimage_pdb.h | 3 +
libgimpbase/Makefile.am | 7 +-
libgimpbase/gimpbase.def | 11 +-
libgimpbase/gimpbase.h | 1 +
libgimpbase/gimpbasetypes.h | 2 +
libgimpbase/gimpmetadata.c | 650 +++++++++++++++++++++++
libgimpbase/gimpmetadata.h | 57 ++
plug-ins/Makefile.am | 6 -
plug-ins/common/.gitignore | 2 +
plug-ins/common/Makefile.am | 21 +
plug-ins/common/file-png.c | 149 +++++-
plug-ins/common/file-tiff-save.c | 195 ++++++--
plug-ins/common/gimprc.common | 1 +
plug-ins/common/metadata.c | 382 ++++++++++++++
plug-ins/common/plugin-defs.pl | 1 +
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 | 410 ++-------------
plug-ins/file-jpeg/jpeg-save.h | 14 +
plug-ins/file-jpeg/jpeg-settings.c | 4 -
plug-ins/file-jpeg/jpeg.c | 102 +----
plug-ins/file-psd/psd-image-res-load.c | 131 -----
plug-ins/ui/Makefile.am | 4 +-
plug-ins/ui/plug-in-file-png.ui | 106 ++++-
plug-ins/ui/plug-in-file-tiff.ui | 404 ++++++++++++++
plug-ins/ui/plug-in-metadata.ui | 908 ++++++++++++++++++++++++++++++++
po-plug-ins/POTFILES.in | 3 +
tools/pdbgen/pdb/image.pdb | 59 ++-
51 files changed, 3976 insertions(+), 1903 deletions(-)
---
diff --git a/app/Makefile.am b/app/Makefile.am
index e559433..80eaac0 100644
--- a/app/Makefile.am
+++ b/app/Makefile.am
@@ -177,6 +177,7 @@ gimpconsoleldadd = \
$(CAIRO_LIBS) \
$(GEGL_LIBS) \
$(GLIB_LIBS) \
+ $(GEXIV2_LIBS) \
$(INTLLIBS) \
$(RT_LIBS)
diff --git a/app/core/Makefile.am b/app/core/Makefile.am
index 764a798..cd94de7 100644
--- a/app/core/Makefile.am
+++ b/app/core/Makefile.am
@@ -10,6 +10,7 @@ AM_CPPFLAGS = \
$(CAIRO_CFLAGS) \
$(GEGL_CFLAGS) \
$(GDK_PIXBUF_CFLAGS) \
+ $(GEXIV2_CFLAGS) \
-I$(includedir)
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..24c6a56
--- /dev/null
+++ b/app/core/gimpimage-metadata.c
@@ -0,0 +1,69 @@
+/* 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 <gdk-pixbuf/gdk-pixbuf.h>
+#include <gegl.h>
+
+#include "libgimpbase/gimpbase.h"
+
+#include "core-types.h"
+
+#include "gimpimage.h"
+#include "gimpimage-metadata.h"
+#include "gimpimage-private.h"
+
+
+/* public functions */
+
+
+GimpMetadata *
+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,
+ GimpMetadata *metadata)
+{
+ GimpImagePrivate *private;
+
+ g_return_if_fail (GIMP_IS_IMAGE (image));
+
+ private = GIMP_IMAGE_GET_PRIVATE (image);
+
+ if (metadata != private->metadata)
+ {
+ if (private->metadata)
+ g_object_unref (private->metadata);
+
+ private->metadata = metadata;
+
+ if (private->metadata)
+ g_object_ref (private->metadata);
+
+ g_object_notify (G_OBJECT (image), "metadata");
+ }
+}
diff --git a/app/core/gimpimage-metadata.h b/app/core/gimpimage-metadata.h
new file mode 100644
index 0000000..feccd95
--- /dev/null
+++ b/app/core/gimpimage-metadata.h
@@ -0,0 +1,27 @@
+/* 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 __GIMP_IMAGE_METADATA_H__
+#define __GIMP_IMAGE_METADATA_H__
+
+
+GimpMetadata * gimp_image_get_metadata (GimpImage *image);
+void gimp_image_set_metadata (GimpImage *image,
+ GimpMetadata *metadata);
+
+
+#endif /* __GIMP_IMAGE_METADATA_H__ */
diff --git a/app/core/gimpimage-private.h b/app/core/gimpimage-private.h
index 5dee180..a4ae2f1 100644
--- a/app/core/gimpimage-private.h
+++ b/app/core/gimpimage-private.h
@@ -55,6 +55,8 @@ struct _GimpImagePrivate
const Babl *babl_palette_rgb; /* palette's RGB Babl format */
const Babl *babl_palette_rgba; /* palette's RGBA Babl format */
+ GimpMetadata *metadata; /* image's metadata */
+
gint dirty; /* dirty flag -- # of ops */
guint dirty_time; /* time when image became dirty */
gint export_dirty; /* 'dirty' but for export */
diff --git a/app/core/gimpimage.c b/app/core/gimpimage.c
index 191239c..c20552a 100644
--- a/app/core/gimpimage.c
+++ b/app/core/gimpimage.c
@@ -23,6 +23,7 @@
#include <cairo.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <gegl.h>
+#include <gexiv2/gexiv2.h>
#include "libgimpcolor/gimpcolor.h"
#include "libgimpmath/gimpmath.h"
@@ -47,6 +48,7 @@
#include "gimpimage.h"
#include "gimpimage-colormap.h"
#include "gimpimage-guides.h"
+#include "gimpimage-metadata.h"
#include "gimpimage-sample-points.h"
#include "gimpimage-preview.h"
#include "gimpimage-private.h"
@@ -130,6 +132,7 @@ enum
PROP_HEIGHT,
PROP_BASE_TYPE,
PROP_PRECISION,
+ PROP_METADATA,
PROP_BUFFER
};
@@ -610,6 +613,12 @@ gimp_image_class_init (GimpImageClass *klass)
GIMP_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
+ 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_override_property (object_class, PROP_BUFFER, "buffer");
g_type_class_add_private (klass, sizeof (GimpImagePrivate));
@@ -667,6 +676,8 @@ gimp_image_init (GimpImage *image)
private->n_colors = 0;
private->palette = NULL;
+ private->metadata = NULL;
+
private->dirty = 1;
private->dirty_time = 0;
private->undo_freeze_count = 0;
@@ -837,6 +848,9 @@ gimp_image_set_property (GObject *object,
case PROP_PRECISION:
private->precision = g_value_get_enum (value);
break;
+ case PROP_METADATA:
+ gimp_image_set_metadata (image, g_value_get_object (value));
+ break;
case PROP_BUFFER:
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -873,6 +887,9 @@ gimp_image_get_property (GObject *object,
case PROP_PRECISION:
g_value_set_enum (value, private->precision);
break;
+ case PROP_METADATA:
+ g_value_set_object (value, gimp_image_get_metadata (image));
+ break;
case PROP_BUFFER:
g_value_set_object (value, gimp_image_get_buffer (GIMP_PICKABLE (image)));
break;
@@ -948,6 +965,12 @@ gimp_image_finalize (GObject *object)
if (private->colormap)
gimp_image_colormap_free (image);
+ if (private->metadata)
+ {
+ g_object_unref (private->metadata);
+ private->metadata = NULL;
+ }
+
if (private->layers)
{
g_object_unref (private->layers);
@@ -1128,9 +1151,10 @@ gimp_image_get_size (GimpViewable *viewable,
static void
gimp_image_size_changed (GimpViewable *viewable)
{
- GimpImage *image = GIMP_IMAGE (viewable);
- GList *all_items;
- GList *list;
+ GimpImage *image = GIMP_IMAGE (viewable);
+ GimpMetadata *metadata;
+ GList *all_items;
+ GList *list;
if (GIMP_VIEWABLE_CLASS (parent_class)->size_changed)
GIMP_VIEWABLE_CLASS (parent_class)->size_changed (viewable);
@@ -1155,6 +1179,12 @@ 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_metadata_set_pixel_size (metadata,
+ gimp_image_get_width (image),
+ gimp_image_get_height (image));
+
gimp_projectable_structure_changed (GIMP_PROJECTABLE (image));
}
@@ -1181,6 +1211,33 @@ gimp_image_real_mode_changed (GimpImage *image)
static void
gimp_image_real_precision_changed (GimpImage *image)
{
+ GimpMetadata *metadata;
+
+ metadata = gimp_image_get_metadata (image);
+ if (metadata)
+ {
+ gint bps = 8;
+
+ switch (gimp_image_get_component_type (image))
+ {
+ case GIMP_COMPONENT_TYPE_U8:
+ bps = 8;
+ break;
+
+ case GIMP_COMPONENT_TYPE_U16:
+ case GIMP_COMPONENT_TYPE_HALF:
+ bps = 16;
+ break;
+
+ case GIMP_COMPONENT_TYPE_U32:
+ case GIMP_COMPONENT_TYPE_FLOAT:
+ bps = 32;
+ break;
+ }
+
+ gimp_metadata_set_bits_per_sample (metadata, bps);
+ }
+
gimp_projectable_structure_changed (GIMP_PROJECTABLE (image));
}
diff --git a/app/file/Makefile.am b/app/file/Makefile.am
index 19db33b..58dc99d 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 6517b4a..b217803 100644
--- a/app/file/file-open.c
+++ b/app/file/file-open.c
@@ -56,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"
@@ -75,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);
@@ -89,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 */
@@ -454,6 +465,9 @@ 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
@@ -839,3 +853,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)
+{
+ GFile *file;
+ GExiv2Metadata *metadata = NULL;
+ gchar *metadata_string = NULL;
+ GError *error = NULL;
+
+ file = g_file_new_for_uri (uri);
+
+ metadata = gimp_metadata_load_from_file (file, &error);
+ if (! metadata)
+ {
+ if (image->gimp->be_verbose)
+ {
+ g_printerr ("METADATA load: %s - %s: %d\n",
+ g_file_get_parse_name (file),
+ error->message, error->code);
+ }
+
+ g_clear_error (&error);
+ }
+
+ g_object_unref (file);
+
+ if (metadata)
+ {
+ metadata_string = gimp_metadata_serialize (metadata);
+ gexiv2_metadata_free (metadata);
+ }
+
+ /* get a valid metadata object, even without entries */
+ metadata = gimp_metadata_deserialize (metadata_string);
+ if (! gimp_image_get_metadata (image))
+ gimp_image_set_metadata (image, metadata);
+}
+
+/**
+ * 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;
+ GimpUnit 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);
+ g_free (comment);
+
+ gimp_image_parasite_attach (image, parasite);
+ gimp_parasite_free (parasite);
+ }
+
+ if (gimp_metadata_get_resolution (metadata, &xres, &yres, &unit))
+ {
+ gimp_image_set_resolution (image, xres, yres);
+ gimp_image_set_unit (image, unit);
+ }
+
+ 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 19d02f9..da2bfb3 100644
--- a/app/file/file-save.c
+++ b/app/file/file-save.c
@@ -66,6 +66,33 @@
#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 */
+
+static void file_save_evaluate_metadata (GimpImage *image,
+ const gchar *uri,
+ gchar *mime_type);
+
+static void file_save_get_meta_handling (GimpImage *image,
+ gchar *mime_type,
+ MetaHandling *meta_handling);
+
/* public functions */
@@ -222,6 +249,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, uri, file_proc->mime_type);
}
else if (status != GIMP_PDB_CANCEL)
{
@@ -242,3 +272,357 @@ 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
+ */
+static void
+file_save_evaluate_metadata (GimpImage *image,
+ const gchar *uri,
+ gchar *mime_type)
+{
+ GFile *file;
+ GExiv2Metadata *metadata;
+ GExiv2Metadata *new_metadata;
+ MetaHandling meta_handling;
+ gchar *value;
+ gint errorcode = 0;
+ gint i;
+ GError *error = NULL;
+
+ file_save_get_meta_handling (image, mime_type, &meta_handling);
+
+ /* getting metadata from image */
+ metadata = gimp_image_get_metadata (image);
+
+ /* reads metadata from saved file */
+ file = g_file_new_for_uri (uri);
+ new_metadata = gimp_metadata_load_from_file (file, &error);
+ if (error)
+ {
+ errorcode = error->code;
+ if (image->gimp->be_verbose)
+ {
+ g_print ("METADATA save: %s - %s: %d\n",
+ g_file_get_parse_name (file),
+ error->message, error->code);
+ }
+
+ g_clear_error (&error);
+ }
+
+ if (new_metadata && errorcode != 501) /* only done, if metadata is supported. */
+ {
+ GDateTime *datetime;
+ const GimpParasite *comment_parasite;
+ const gchar *comment = NULL;
+ gboolean support_exif;
+ gboolean support_xmp;
+ gboolean support_iptc;
+ gint image_width;
+ gint image_height;
+ gdouble xres;
+ gdouble yres;
+ gchar buffer[32];
+
+ 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 = gimp_parasite_data (comment_parasite);
+
+ /* step #1: writing metadata to new files metadata */
+ if (meta_handling.save_exif && support_exif)
+ {
+ gchar **exif_data = gexiv2_metadata_get_exif_tags (metadata);
+
+ for (i = 0; exif_data[i] != NULL; i++)
+ {
+ if (! gexiv2_metadata_has_tag (new_metadata, exif_data[i]) &&
+ gimp_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);
+ g_free (value);
+ }
+ }
+
+ g_strfreev (exif_data);
+
+ if (comment)
+ {
+ gexiv2_metadata_set_tag_string (new_metadata,
+ "Exif.Photo.UserComment",
+ comment);
+ gexiv2_metadata_set_tag_string (new_metadata,
+ "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 (new_metadata,
+ "Exif.Image.DateTime",
+ buffer);
+
+ gexiv2_metadata_set_tag_string (new_metadata,
+ "Exif.Image.Software",
+ PACKAGE_STRING);
+
+ gimp_metadata_set_pixel_size (new_metadata,
+ image_width, image_height);
+
+ gimp_image_get_resolution (image, &xres, &yres);
+ gimp_metadata_set_resolution (new_metadata, xres, yres,
+ gimp_image_get_unit (image));
+ }
+
+ if (meta_handling.save_xmp && support_xmp)
+ {
+ gchar **xmp_data = gexiv2_metadata_get_xmp_tags (metadata);
+
+ for (i = 0; xmp_data[i] != NULL; i++)
+ {
+ if (! gexiv2_metadata_has_tag (new_metadata, xmp_data[i]) &&
+ gimp_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);
+ g_free (value);
+ }
+ }
+
+ g_strfreev (xmp_data);
+
+ gexiv2_metadata_set_tag_string (new_metadata,
+ "Xmp.dc.Format",
+ mime_type);
+
+ if (! g_strcmp0 (mime_type, "image/tiff")) /* TIFF specific data */
+ {
+ gchar buffer[32];
+
+ 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 (new_metadata,
+ "Xmp.tiff.DateTime",
+ buffer);
+ }
+ }
+
+ if (meta_handling.save_iptc && support_iptc)
+ {
+ gchar **iptc_data = gexiv2_metadata_get_iptc_tags (metadata);
+
+ for (i = 0; iptc_data[i] != NULL; i++)
+ {
+ if (! gexiv2_metadata_has_tag (new_metadata, iptc_data[i]) &&
+ gimp_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);
+ g_free (value);
+ }
+ }
+
+ g_strfreev (iptc_data);
+
+ 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 (new_metadata,
+ "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 (new_metadata,
+ "Iptc.Application2.TimeCreated",
+ buffer);
+ }
+
+ /* step #6: save thumbnail */
+ if (meta_handling.save_thumbnail)
+ {
+ GimpContext *context = gimp_get_user_context (image->gimp);
+ GdkPixbuf *thumb_pixbuf;
+ gchar *thumb_buffer;
+ gsize count;
+ gint thumbw;
+ gint thumbh;
+
+ 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_pixbuf (GIMP_VIEWABLE (image),
+ context, thumbw, thumbh);
+
+ if (gdk_pixbuf_save_to_buffer (thumb_pixbuf, &thumb_buffer, &count,
+ "jpeg", &error,
+ "quality", "75",
+ NULL))
+ {
+ gexiv2_metadata_set_exif_thumbnail_from_buffer (new_metadata,
+ (guchar *) thumb_buffer,
+ count);
+
+ g_snprintf (buffer, sizeof (buffer), "%d", thumbw);
+ gexiv2_metadata_set_tag_string (new_metadata,
+ "Exif.Thumbnail.ImageWidth",
+ buffer);
+
+ g_snprintf (buffer, sizeof (buffer), "%d", thumbh);
+ gexiv2_metadata_set_tag_string (new_metadata,
+ "Exif.Thumbnail.ImageLength",
+ buffer);
+
+ gexiv2_metadata_set_tag_string (new_metadata,
+ "Exif.Thumbnail.BitsPerSample",
+ "8 8 8");
+ gexiv2_metadata_set_tag_string (new_metadata,
+ "Exif.Thumbnail.SamplesPerPixel",
+ "3");
+ gexiv2_metadata_set_tag_string (new_metadata,
+ "Exif.Thumbnail.PhotometricInterpretation",
+ "6");
+
+ 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"))
+ {
+ /* TIFF specific data, these are important */
+
+ g_snprintf (buffer, sizeof (buffer), "%d", image_width);
+ gexiv2_metadata_set_tag_string (new_metadata,
+ "Xmp.tiff.ImageWidth",
+ buffer);
+
+ g_snprintf (buffer, sizeof (buffer), "%d", image_height);
+ gexiv2_metadata_set_tag_string (new_metadata,
+ "Xmp.tiff.ImageLength",
+ buffer);
+ }
+
+ /* step #8: saving metadata to file */
+ if (! gimp_metadata_save_to_file (new_metadata, file, &error))
+ {
+ if (image->gimp->be_verbose)
+ {
+ g_printerr ("METADATA save: %s - %s: %d\n",
+ g_file_get_parse_name (file),
+ error->message, error->code);
+ }
+
+ g_clear_error (&error);
+ }
+
+ g_date_time_unref (datetime);
+ g_object_unref (new_metadata);
+ }
+
+ g_object_unref (file);
+}
+
+static void
+file_save_get_meta_handling (GimpImage *image,
+ gchar *mime_type,
+ MetaHandling *meta_handling)
+
+{
+ const GimpParasite *parasite;
+ gchar *def_str;
+ gchar *parasite_name;
+ 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_name = g_strdup_printf ("exif-handling-data(%s)", mime_type);
+
+ parasite = gimp_image_parasite_find (image, parasite_name);
+ 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);
+
+ if (num_fields != 4)
+ return;
+
+ meta_handling->save_exif = save_exif ? TRUE : FALSE;
+ meta_handling->save_xmp = save_xmp ? TRUE : FALSE;
+ meta_handling->save_iptc = save_iptc ? TRUE : FALSE;
+ meta_handling->save_thumbnail = save_thumbnail ? TRUE : FALSE;
+}
diff --git a/app/pdb/image-cmds.c b/app/pdb/image-cmds.c
index 78c9a3e..f2e822e 100644
--- a/app/pdb/image-cmds.c
+++ b/app/pdb/image-cmds.c
@@ -42,6 +42,7 @@
#include "core/gimpimage-duplicate.h"
#include "core/gimpimage-flip.h"
#include "core/gimpimage-merge.h"
+#include "core/gimpimage-metadata.h"
#include "core/gimpimage-pick-color.h"
#include "core/gimpimage-pick-layer.h"
#include "core/gimpimage-resize.h"
@@ -1707,6 +1708,67 @@ image_set_colormap_invoker (GimpProcedure *procedure,
}
static GimpValueArray *
+image_get_metadata_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)
+ {
+ GimpMetadata *metadata = gimp_image_get_metadata (image);
+
+ if (metadata)
+ metadata_string = gimp_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_set_metadata_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)
+ {
+ GimpMetadata *metadata = gimp_metadata_deserialize (metadata_string);
+
+ gimp_image_set_metadata (image, metadata);
+
+ if (metadata)
+ g_object_unref (metadata);
+ }
+
+ return gimp_procedure_get_return_values (procedure, success,
+ error ? *error : NULL);
+}
+
+static GimpValueArray *
image_clean_all_invoker (GimpProcedure *procedure,
Gimp *gimp,
GimpContext *context,
@@ -4554,6 +4616,66 @@ register_image_procs (GimpPDB *pdb)
g_object_unref (procedure);
/*
+ * gimp-image-get-metadata
+ */
+ procedure = gimp_procedure_new (image_get_metadata_invoker);
+ gimp_object_set_static_name (GIMP_OBJECT (procedure),
+ "gimp-image-get-metadata");
+ gimp_procedure_set_static_strings (procedure,
+ "gimp-image-get-metadata",
+ "Returns the image's metadata.",
+ "Returns exif/iptc/xmp metadata from the 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-set-metadata
+ */
+ procedure = gimp_procedure_new (image_set_metadata_invoker);
+ gimp_object_set_static_name (GIMP_OBJECT (procedure),
+ "gimp-image-set-metadata");
+ gimp_procedure_set_static_strings (procedure,
+ "gimp-image-set-metadata",
+ "Set the image's metadata.",
+ "Sets exif/iptc/xmp metadata on the 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);
+
+ /*
* gimp-image-clean-all
*/
procedure = gimp_procedure_new (image_clean_all_invoker);
diff --git a/app/pdb/internal-procs.c b/app/pdb/internal-procs.c
index 790cb86..5dada82 100644
--- a/app/pdb/internal-procs.c
+++ b/app/pdb/internal-procs.c
@@ -28,7 +28,7 @@
#include "internal-procs.h"
-/* 701 procedures registered total */
+/* 703 procedures registered total */
void
internal_procs_init (GimpPDB *pdb)
diff --git a/app/xcf/xcf-load.c b/app/xcf/xcf-load.c
index f3c89ab..94da1d7 100644
--- a/app/xcf/xcf-load.c
+++ b/app/xcf/xcf-load.c
@@ -41,6 +41,7 @@
#include "core/gimpimage-colormap.h"
#include "core/gimpimage-grid.h"
#include "core/gimpimage-guides.h"
+#include "core/gimpimage-metadata.h"
#include "core/gimpimage-private.h"
#include "core/gimpimage-sample-points.h"
#include "core/gimpimage-undo.h"
@@ -208,6 +209,22 @@ xcf_load_image (Gimp *gimp,
}
}
+ /* check for a metadata parasite */
+ parasite = gimp_image_parasite_find (GIMP_IMAGE (image),
+ "xcf-metadata-save");
+ if (parasite)
+ {
+ GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image);
+ gchar *meta_string;
+
+ meta_string = (gchar *) gimp_parasite_data (parasite);
+
+ gimp_image_set_metadata (image,
+ gimp_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 d931f81..983c43c 100644
--- a/app/xcf/xcf-save.c
+++ b/app/xcf/xcf-save.c
@@ -41,6 +41,7 @@
#include "core/gimpimage-colormap.h"
#include "core/gimpimage-grid.h"
#include "core/gimpimage-guides.h"
+#include "core/gimpimage-metadata.h"
#include "core/gimpimage-private.h"
#include "core/gimpimage-sample-points.h"
#include "core/gimplayer.h"
@@ -237,7 +238,22 @@ xcf_save_image (XcfInfo *info,
guint max_progress;
gint t1, t2, t3, t4;
gchar version_tag[16];
- GError *tmp_error = NULL;
+ gchar *meta_string = NULL;
+ GError *tmp_error = NULL;
+
+ /* serialize metadata */
+
+ meta_string = gimp_metadata_serialize (gimp_image_get_metadata (image));
+ if (meta_string)
+ {
+ GimpParasite *meta_parasite;
+
+ 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 a56aa9f..0409734 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
#################
@@ -2406,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/gimp.def b/libgimp/gimp.def
index fcf3fa8..8c1fd0c 100644
--- a/libgimp/gimp.def
+++ b/libgimp/gimp.def
@@ -403,6 +403,7 @@ EXPORTS
gimp_image_get_layer_by_tattoo
gimp_image_get_layer_position
gimp_image_get_layers
+ gimp_image_get_metadata
gimp_image_get_name
gimp_image_get_parasite
gimp_image_get_parasite_list
@@ -484,6 +485,7 @@ EXPORTS
gimp_image_set_component_active
gimp_image_set_component_visible
gimp_image_set_filename
+ gimp_image_set_metadata
gimp_image_set_resolution
gimp_image_set_tattoo_state
gimp_image_set_unit
diff --git a/libgimp/gimpimage.c b/libgimp/gimpimage.c
index f6ef79d..2c324ad 100644
--- a/libgimp/gimpimage.c
+++ b/libgimp/gimpimage.c
@@ -101,6 +101,66 @@ gimp_image_get_thumbnail_data (gint32 image_ID,
}
/**
+ * gimp_image_get_metadata:
+ * @image_ID: The image.
+ *
+ * Returns the image's metadata.
+ *
+ * Returns exif/iptc/xmp metadata from the image.
+ *
+ * Returns: The exif/ptc/xmp metadata, or %NULL if there is none.
+ *
+ * Since: GIMP 2.10
+ **/
+GimpMetadata *
+gimp_image_get_metadata (gint32 image_ID)
+{
+ GimpMetadata *metadata = NULL;
+ gchar *metadata_string;
+
+ metadata_string = _gimp_image_get_metadata (image_ID);
+ if (metadata_string)
+ {
+ metadata = gimp_metadata_deserialize (metadata_string);
+ g_free (metadata_string);
+ }
+
+ return metadata;
+}
+
+/**
+ * gimp_image_set_metadata:
+ * @image_ID: The image.
+ * @metadata: The exif/ptc/xmp metadata.
+ *
+ * Set the image's metadata.
+ *
+ * Sets exif/iptc/xmp metadata on the image, or deletes it if
+ * @metadata is %NULL.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: GIMP 2.10
+ **/
+gboolean
+gimp_image_set_metadata (gint32 image_ID,
+ GimpMetadata *metadata)
+{
+ gchar *metadata_string = NULL;
+ gboolean success;
+
+ if (metadata)
+ metadata_string = gimp_metadata_serialize (metadata);
+
+ success = _gimp_image_set_metadata (image_ID, metadata_string);
+
+ if (metadata_string)
+ g_free (metadata_string);
+
+ return success;
+}
+
+/**
* gimp_image_get_cmap:
* @image_ID: The image.
* @num_colors: Number of colors in the colormap array.
diff --git a/libgimp/gimpimage.h b/libgimp/gimpimage.h
index f25b9d7..376b033 100644
--- a/libgimp/gimpimage.h
+++ b/libgimp/gimpimage.h
@@ -41,6 +41,10 @@ guchar * gimp_image_get_thumbnail_data (gint32 image_ID,
gint *height,
gint *bpp);
+GimpMetadata * gimp_image_get_metadata (gint32 image_ID);
+gboolean gimp_image_set_metadata (gint32 image_ID,
+ GimpMetadata *metadata);
+
GIMP_DEPRECATED_FOR(gimp_image_get_colormap)
guchar * gimp_image_get_cmap (gint32 image_ID,
gint *num_colors);
diff --git a/libgimp/gimpimage_pdb.c b/libgimp/gimpimage_pdb.c
index ace2f69..76d9c15 100644
--- a/libgimp/gimpimage_pdb.c
+++ b/libgimp/gimpimage_pdb.c
@@ -1800,6 +1800,68 @@ _gimp_image_set_colormap (gint32 image_ID,
}
/**
+ * _gimp_image_get_metadata:
+ * @image_ID: The image.
+ *
+ * Returns the image's metadata.
+ *
+ * Returns exif/iptc/xmp metadata from the image.
+ *
+ * Returns: The exif/ptc/xmp metadata as a string.
+ **/
+gchar *
+_gimp_image_get_metadata (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *metadata_string = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-image-get-metadata",
+ &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_set_metadata:
+ * @image_ID: The image.
+ * @metadata_string: The exif/ptc/xmp metadata as a string.
+ *
+ * Set the image's metadata.
+ *
+ * Sets exif/iptc/xmp metadata on the image.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+_gimp_image_set_metadata (gint32 image_ID,
+ const gchar *metadata_string)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-set-metadata",
+ &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;
+}
+
+/**
* gimp_image_clean_all:
* @image_ID: The image.
*
diff --git a/libgimp/gimpimage_pdb.h b/libgimp/gimpimage_pdb.h
index ae5015e..e5a2466 100644
--- a/libgimp/gimpimage_pdb.h
+++ b/libgimp/gimpimage_pdb.h
@@ -148,6 +148,9 @@ G_GNUC_INTERNAL guint8* _gimp_image_get_colormap (gint32
G_GNUC_INTERNAL gboolean _gimp_image_set_colormap (gint32 image_ID,
gint num_bytes,
const guint8 *colormap);
+G_GNUC_INTERNAL gchar* _gimp_image_get_metadata (gint32 image_ID);
+G_GNUC_INTERNAL gboolean _gimp_image_set_metadata (gint32 image_ID,
+ const gchar *metadata_string);
gboolean gimp_image_clean_all (gint32 image_ID);
gboolean gimp_image_is_dirty (gint32 image_ID);
G_GNUC_INTERNAL gboolean _gimp_image_thumbnail (gint32 image_ID,
diff --git a/libgimpbase/Makefile.am b/libgimpbase/Makefile.am
index 59a258b..ffc8b3e 100644
--- a/libgimpbase/Makefile.am
+++ b/libgimpbase/Makefile.am
@@ -61,6 +61,7 @@ AM_CPPFLAGS = \
-DGIMP_BASE_COMPILATION \
-I$(top_srcdir) \
$(GIO_CFLAGS) \
+ $(GEXIV2_CFLAGS) \
$(BINRELOC_CFLAGS) \
-I$(includedir) \
$(xobjective_c)
@@ -101,6 +102,8 @@ libgimpbase_sources = \
gimpenv.h \
gimpmemsize.c \
gimpmemsize.h \
+ gimpmetadata.c \
+ gimpmetadata.h \
gimpparasite.c \
gimpparasite.h \
gimpparasiteio.c \
@@ -144,6 +147,7 @@ libgimpbaseinclude_HEADERS = \
gimpdatafiles.h \
gimpenv.h \
gimpmemsize.h \
+ gimpmetadata.h \
gimpparasite.h \
gimpparasiteio.h \
gimprectangle.h \
@@ -152,7 +156,6 @@ libgimpbaseinclude_HEADERS = \
gimputils.h \
gimpvaluearray.h
-
libgimpbase_ GIMP_API_VERSION@_la_LDFLAGS = \
-version-info $(LT_VERSION_INFO) \
$(no_undefined) \
@@ -163,9 +166,9 @@ libgimpbase_ GIMP_API_VERSION@_la_DEPENDENCIES = $(gimpbase_def)
libgimpbase_ GIMP_API_VERSION@_la_LIBADD = \
$(GIO_LIBS) \
+ $(GEXIV2_LIBS) \
$(ole32_lib)
-
install-data-local: install-ms-lib install-libtool-import-lib
uninstall-local: uninstall-ms-lib uninstall-libtool-import-lib
diff --git a/libgimpbase/gimpbase.def b/libgimpbase/gimpbase.def
index a6330e0..6d87e07 100644
--- a/libgimpbase/gimpbase.def
+++ b/libgimpbase/gimpbase.def
@@ -50,6 +50,15 @@ EXPORTS
gimp_memsize_serialize
gimp_memsize_to_string
gimp_message_handler_type_get_type
+ gimp_metadata_deserialize
+ gimp_metadata_get_resolution
+ gimp_metadata_is_tag_supported
+ gimp_metadata_load_from_file
+ gimp_metadata_save_to_file
+ gimp_metadata_serialize
+ gimp_metadata_set_bits_per_sample
+ gimp_metadata_set_pixel_size
+ gimp_metadata_set_resolution
gimp_micro_version
gimp_minor_version
gimp_paint_application_mode_get_type
@@ -100,7 +109,7 @@ EXPORTS
gimp_sysconf_directory
gimp_text_direction_get_type
gimp_text_hint_style_get_type
- gimp_text_justification_get_type
+ gimp_text_justification_get_type
gimp_transfer_mode_get_type
gimp_transform_direction_get_type
gimp_transform_resize_get_type
diff --git a/libgimpbase/gimpbase.h b/libgimpbase/gimpbase.h
index 08930dc..a5682e7 100644
--- a/libgimpbase/gimpbase.h
+++ b/libgimpbase/gimpbase.h
@@ -29,6 +29,7 @@
#include <libgimpbase/gimpenv.h>
#include <libgimpbase/gimplimits.h>
#include <libgimpbase/gimpmemsize.h>
+#include <libgimpbase/gimpmetadata.h>
#include <libgimpbase/gimpparasite.h>
#include <libgimpbase/gimprectangle.h>
#include <libgimpbase/gimpunit.h>
diff --git a/libgimpbase/gimpbasetypes.h b/libgimpbase/gimpbasetypes.h
index 481c1cd..bef9904 100644
--- a/libgimpbase/gimpbasetypes.h
+++ b/libgimpbase/gimpbasetypes.h
@@ -55,6 +55,8 @@ typedef struct _GimpValueArray GimpValueArray;
typedef void (* GimpDatafileLoaderFunc) (const GimpDatafileData *file_data,
gpointer user_data);
+typedef struct _GExiv2Metadata GimpMetadata;
+
/**
* GimpEnumDesc:
diff --git a/libgimpbase/gimpmetadata.c b/libgimpbase/gimpmetadata.c
new file mode 100644
index 0000000..c97b3cf
--- /dev/null
+++ b/libgimpbase/gimpmetadata.c
@@ -0,0 +1,650 @@
+/* LIBGIMPBASE - The GIMP Basic Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpmetadata.c
+ * Copyright (C) 2013 Hartmut Kuhse <hartmutkuhse src gnome 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 <stdlib.h>
+#include <string.h>
+
+#include <gio/gio.h>
+#include <gexiv2/gexiv2.h>
+
+#include "gimpbasetypes.h"
+
+#include "gimpmetadata.h"
+#include "gimpunit.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+#define TAG_LINE_DELIMITER "\v"
+#define TAG_TAG_DELIMITER "#"
+
+
+static gint gimp_metadata_length (const gchar *testline,
+ const gchar *delim);
+static gboolean gimp_metadata_get_rational (const gchar *value,
+ gint sections,
+ gchar ***numerator,
+ gchar ***denominator);
+
+
+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"
+};
+
+static 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
+};
+
+static const guint wilber_jpg_len = G_N_ELEMENTS (wilber_jpg);
+
+
+/**
+ * Deserializes metadata from a string
+ **/
+GExiv2Metadata *
+gimp_metadata_deserialize (const gchar *metadata_string)
+{
+ GExiv2Metadata *metadata = NULL;
+ GString *buffer;
+ gint i;
+ gint j;
+ gchar **meta_info = NULL;
+ gchar **tag_info = NULL;
+ gint num;
+ gchar *value;
+ GError **error = NULL;
+
+ g_return_val_if_fail (metadata_string != NULL, NULL);
+
+ if (gexiv2_initialize ())
+ {
+ metadata = gexiv2_metadata_new ();
+
+ if (! gexiv2_metadata_open_buf (metadata, wilber_jpg, wilber_jpg_len,
+ error))
+ {
+ return NULL;
+ }
+ }
+
+ if (metadata)
+ {
+ meta_info = g_strsplit (metadata_string, TAG_LINE_DELIMITER, -1);
+
+ if (meta_info)
+ {
+ for (i = 0; meta_info[i] != NULL; i++)
+ {
+ num = gimp_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);
+ }
+ }
+
+ g_strfreev (meta_info);
+ }
+ }
+
+ if (tag_info)
+ g_strfreev (tag_info);
+
+ return metadata;
+}
+
+/**
+ * Serializing metadata as a string
+ */
+gchar *
+gimp_metadata_serialize (GExiv2Metadata *metadata)
+{
+ GString *string;
+ gchar **exif_data = NULL;
+ gchar **iptc_data = NULL;
+ gchar **xmp_data = NULL;
+ gchar *value;
+ gint i;
+ gint n;
+
+ g_return_val_if_fail (GEXIV2_IS_METADATA (metadata), NULL);
+
+ n = 0;
+ string = g_string_new (NULL);
+
+ exif_data = gexiv2_metadata_get_exif_tags (metadata);
+
+ if (exif_data)
+ {
+ for (i = 0; exif_data[i] != NULL; i++)
+ {
+ value = gexiv2_metadata_get_tag_string (metadata, exif_data[i]);
+ g_string_append_printf (string, "%s%s%s%s", exif_data[i],
+ TAG_TAG_DELIMITER,
+ value,
+ TAG_LINE_DELIMITER);
+ g_free (value);
+ n++;
+ }
+
+ g_strfreev (exif_data);
+ }
+
+ xmp_data = gexiv2_metadata_get_xmp_tags (metadata);
+
+ if (xmp_data)
+ {
+ for (i = 0; xmp_data[i] != NULL; i++)
+ {
+ value = gexiv2_metadata_get_tag_string (metadata, xmp_data[i]);
+ g_string_append_printf (string, "%s%s%s%s", xmp_data[i],
+ TAG_TAG_DELIMITER,
+ value,
+ TAG_LINE_DELIMITER);
+ g_free (value);
+ n++;
+ }
+
+ g_strfreev (xmp_data);
+ }
+
+ iptc_data = gexiv2_metadata_get_iptc_tags (metadata);
+
+ if (iptc_data)
+ {
+ for (i = 0; iptc_data[i] != NULL; i++)
+ {
+ value = gexiv2_metadata_get_tag_string (metadata, iptc_data[i]);
+ g_string_append_printf (string, "%s%s%s%s", iptc_data[i],
+ TAG_TAG_DELIMITER,
+ value,
+ TAG_LINE_DELIMITER);
+ g_free (value);
+ n++;
+ }
+
+ g_strfreev (iptc_data);
+ }
+
+ return g_string_free (string, FALSE);
+}
+
+/*
+ * reads metadata from a physical file
+ */
+GimpMetadata *
+gimp_metadata_load_from_file (GFile *file,
+ GError **error)
+{
+ GExiv2Metadata *meta = NULL;
+ gchar *path;
+ gchar *filename;
+
+ g_return_val_if_fail (G_IS_FILE (file), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ path = g_file_get_path (file);
+
+ if (! path)
+ {
+ g_set_error (error, G_FILE_ERROR, 0,
+ _("Can load metadata only from local files"));
+ return NULL;
+ }
+
+#ifdef G_OS_WIN32
+ filename = g_win32_locale_filename_from_utf8 (path);
+#else
+ filename = g_strdup (path);
+#endif
+
+ g_free (path);
+
+ if (gexiv2_initialize ())
+ {
+ meta = gexiv2_metadata_new ();
+
+ if (! gexiv2_metadata_open_path (meta, filename, error))
+ {
+ g_object_unref (meta);
+ g_free (filename);
+
+ return NULL;
+ }
+ }
+
+ g_free (filename);
+
+ return meta;
+}
+
+/*
+ * saves metadata to a physical file
+ */
+gboolean
+gimp_metadata_save_to_file (GimpMetadata *metadata,
+ GFile *file,
+ GError **error)
+{
+ gchar *path;
+ gchar *filename;
+ gboolean success;
+
+ 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);
+
+ path = g_file_get_path (file);
+
+ if (! path)
+ {
+ g_set_error (error, G_FILE_ERROR, 0,
+ _("Can save metadata only from to files"));
+ return FALSE;
+ }
+
+#ifdef G_OS_WIN32
+ filename = g_win32_locale_filename_from_utf8 (path);
+#else
+ filename = g_strdup (path);
+#endif
+
+ g_free (path);
+
+ success = gexiv2_metadata_save_file (metadata, filename, error);
+
+ g_free (filename);
+
+ return success;
+}
+
+void
+gimp_metadata_set_pixel_size (GimpMetadata *metadata,
+ gint width,
+ gint height)
+{
+ gchar buffer[32];
+
+ g_return_if_fail (GEXIV2_IS_METADATA (metadata));
+
+ g_snprintf (buffer, sizeof (buffer), "%d", width);
+ gexiv2_metadata_set_tag_string (metadata, "Exif.Image.ImageWidth", buffer);
+
+ g_snprintf (buffer, sizeof (buffer), "%d", height);
+ gexiv2_metadata_set_tag_string (metadata, "Exif.Image.ImageLength", buffer);
+}
+
+void
+gimp_metadata_set_bits_per_sample (GimpMetadata *metadata,
+ gint bps)
+{
+ gchar buffer[32];
+
+ g_return_if_fail (GEXIV2_IS_METADATA (metadata));
+
+ g_snprintf (buffer, sizeof (buffer), "%d %d %d", bps, bps, bps);
+ gexiv2_metadata_set_tag_string (metadata, "Exif.Image.BitsPerSample", buffer);
+}
+
+/**
+ * gets exif resolution
+ */
+gboolean
+gimp_metadata_get_resolution (GimpMetadata *metadata,
+ gdouble *xres,
+ gdouble *yres,
+ GimpUnit *unit)
+{
+ gchar *xr;
+ gchar *yr;
+ gchar *un;
+ gint exif_unit;
+ gchar **xnom = NULL;
+ gchar **xdenom = NULL;
+ gchar **ynom = NULL;
+ gchar **ydenom = NULL;
+
+ g_return_val_if_fail (GEXIV2_IS_METADATA (metadata), FALSE);
+
+ 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");
+ exif_unit = atoi (un);
+ g_free (un);
+
+ if (exif_unit == 3)
+ *unit = GIMP_UNIT_MM;
+ else
+ *unit = GIMP_UNIT_INCH;
+
+ if (gimp_metadata_get_rational (xr, 1, &xnom, &xdenom))
+ {
+ gdouble x1 = g_ascii_strtod (xnom[0], NULL);
+ gdouble x2 = g_ascii_strtod (xdenom[0], NULL);
+ gdouble xrd;
+
+ if (x2 == 0.0)
+ return FALSE;
+
+ xrd = x1 / x2;
+
+ if (exif_unit == 3)
+ xrd *= 2.54;
+
+ *xres = xrd;
+ }
+
+ if (gimp_metadata_get_rational (yr, 1, &ynom, &ydenom))
+ {
+ gdouble y1 = g_ascii_strtod (ynom[0], NULL);
+ gdouble y2 = g_ascii_strtod (ydenom[0], NULL);
+ gdouble yrd;
+
+ if (y2 == 0.0)
+ return FALSE;
+
+ yrd = y1 / y2;
+
+ if (exif_unit == 3)
+ yrd *= 2.54;
+
+ *yres = yrd;
+ }
+
+ g_free (xr);
+ g_free (yr);
+
+ g_strfreev (xnom);
+ g_strfreev (xdenom);
+ g_strfreev (ynom);
+ g_strfreev (ydenom);
+
+ return TRUE;
+}
+
+/**
+ * sets exif resolution
+ */
+void
+gimp_metadata_set_resolution (GimpMetadata *metadata,
+ gdouble xres,
+ gdouble yres,
+ GimpUnit unit)
+{
+ gchar buffer[32];
+ gint exif_unit;
+
+ g_return_if_fail (GEXIV2_IS_METADATA (metadata));
+
+ if (gimp_unit_is_metric (unit))
+ {
+ xres /= 2.54;
+ yres /= 2.54;
+
+ exif_unit = 3;
+ }
+ else
+ {
+ exif_unit = 2;
+ }
+
+ g_ascii_formatd (buffer, sizeof (buffer), "%.0f/1", xres);
+ gexiv2_metadata_set_tag_string (metadata, "Exif.Image.XResolution", buffer);
+
+ g_ascii_formatd (buffer, sizeof (buffer), "%.0f/1", yres);
+ gexiv2_metadata_set_tag_string (metadata, "Exif.Image.YResolution", buffer);
+
+ g_snprintf (buffer, sizeof (buffer), "%d", exif_unit);
+ gexiv2_metadata_set_tag_string (metadata, "Exif.Image.ResolutionUnit", buffer);
+}
+
+/**
+ * checks for supported tags
+ */
+gboolean
+gimp_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 amount of delimiters in serialized
+ * metadata string
+ */
+static gint
+gimp_metadata_length (const gchar *testline,
+ const 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
+ */
+static gboolean
+gimp_metadata_get_rational (const 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 = NULL;
+ gint i;
+ gint n;
+
+ gchar **num;
+ gchar **den;
+
+ if (! value)
+ return FALSE;
+
+ if (gimp_metadata_length (value, sect) == (sections -1))
+ {
+ i = 0;
+ sects = g_strsplit (value, sect, -1);
+ while (sects[i] != NULL)
+ {
+ if(gimp_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/libgimpbase/gimpmetadata.h b/libgimpbase/gimpmetadata.h
new file mode 100644
index 0000000..7fc209f
--- /dev/null
+++ b/libgimpbase/gimpmetadata.h
@@ -0,0 +1,57 @@
+/* LIBGIMPBASE - The GIMP Basic Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpmetadata.h
+ * Copyright (C) 2013 Hartmut Kuhse <hartmutkuhse src gnome 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_METADATA_H__
+#define __GIMP_METADATA_H__
+
+G_BEGIN_DECLS
+
+GimpMetadata * gimp_metadata_deserialize (const gchar *metadata_string);
+gchar * gimp_metadata_serialize (GimpMetadata *metadata);
+
+GimpMetadata * gimp_metadata_load_from_file (GFile *file,
+ GError **error);
+gboolean gimp_metadata_save_to_file (GimpMetadata *metadata,
+ GFile *file,
+ GError **error);
+
+void gimp_metadata_set_pixel_size (GimpMetadata *metadata,
+ gint width,
+ gint height);
+void gimp_metadata_set_bits_per_sample (GimpMetadata *metadata,
+ gint bits_per_sample);
+
+gboolean gimp_metadata_get_resolution (GimpMetadata *metadata,
+ gdouble *xres,
+ gdouble *yres,
+ GimpUnit *unit);
+void gimp_metadata_set_resolution (GimpMetadata *metadata,
+ gdouble xres,
+ gdouble yres,
+ GimpUnit unit);
+
+gboolean gimp_metadata_is_tag_supported (gchar *tag,
+ gchar *mime_type);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_METADATA_H__ */
diff --git a/plug-ins/Makefile.am b/plug-ins/Makefile.am
index 50eb645..38cc20f 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) \
@@ -83,7 +78,6 @@ SUBDIRS = \
lighting \
map-object \
maze \
- $(metadata) \
pagecurl \
$(print) \
selection-to-path \
diff --git a/plug-ins/common/.gitignore b/plug-ins/common/.gitignore
index 6cb37a8..917b863 100644
--- a/plug-ins/common/.gitignore
+++ b/plug-ins/common/.gitignore
@@ -182,6 +182,8 @@
/mail.exe
/max-rgb
/max-rgb.exe
+/metadata
+/metadata.exe
/newsprint
/newsprint.exe
/nl-filter
diff --git a/plug-ins/common/Makefile.am b/plug-ins/common/Makefile.am
index 441aae7..6da3ca3 100644
--- a/plug-ins/common/Makefile.am
+++ b/plug-ins/common/Makefile.am
@@ -134,6 +134,7 @@ libexec_PROGRAMS = \
lens-flare \
$(MAIL) \
max-rgb \
+ metadata \
newsprint \
nl-filter \
noise-rgb \
@@ -1773,6 +1774,26 @@ max_rgb_LDADD = \
$(INTLLIBS) \
$(max_rgb_RC)
+metadata_CFLAGS = $(GEXIV2_CFLAGS)
+
+metadata_SOURCES = \
+ metadata.c
+
+metadata_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEXIV2_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(metadata_RC)
+
newsprint_SOURCES = \
newsprint.c
diff --git a/plug-ins/common/file-png.c b/plug-ins/common/file-png.c
index f6f8a60..d8592cf 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;
@@ -444,6 +476,7 @@ run (const gchar *name,
drawable_ID = param[2].data.d_int32;
load_defaults ();
+ save_exif_defaults (image_ID, exifvals);
/* eventually export the image */
switch (run_mode)
@@ -549,6 +582,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
@@ -563,6 +597,7 @@ run (const gchar *name,
else if (strcmp (name, GET_DEFAULTS_PROC) == 0)
{
load_defaults ();
+ save_exif_defaults (image_ID, exifvals);
*nreturn_vals = 10;
@@ -596,6 +631,10 @@ run (const gchar *name,
pngvals.time = param[6].data.d_int32;
pngvals.comment = param[7].data.d_int32;
pngvals.save_transp_pixels = param[8].data.d_int32;
+ pngvals.exif = FALSE;
+ pngvals.iptc = FALSE;
+ pngvals.xmp = FALSE;
+ pngvals.thumb = FALSE;
save_defaults ();
}
@@ -2080,6 +2119,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 +2214,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,13 +2223,21 @@ 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));
+ exifvals.save_exif = pngvals.exif;
+ exifvals.save_iptc = pngvals.iptc;
+ exifvals.save_xmp = pngvals.xmp;
+ exifvals.save_thumbnail = pngvals.thumb;
return;
}
}
@@ -2192,7 +2251,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 +2260,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,
@@ -2230,9 +2293,83 @@ load_gui_defaults (PngSaveGui *pg)
SET_ACTIVE (time);
SET_ACTIVE (comment);
SET_ACTIVE (save_transp_pixels);
+ SET_ACTIVE (exif);
+ SET_ACTIVE (xmp);
+ SET_ACTIVE (iptc);
+ SET_ACTIVE (thumb);
#undef SET_ACTIVE
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(image/png)");
+
+ 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;
+
+ exif_save_handler->save_exif = (e_save == 0) ? FALSE : TRUE;
+ exif_save_handler->save_xmp = (x_save == 0) ? FALSE : TRUE;
+ exif_save_handler->save_iptc = (i_save == 0) ? FALSE : TRUE;
+ exif_save_handler->save_thumbnail = (t_save == 0) ? FALSE : TRUE;
+
+ 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(image/png)",
+ 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 fe5ba46..b15b980 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,20 @@ save_dialog (gboolean has_alpha,
_("_Pack Bits"), COMPRESSION_PACKBITS, NULL,
_("_Deflate"), COMPRESSION_ADOBE_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 +1170,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 +1272,65 @@ 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 = FALSE;
+
+ parasite = gimp_image_get_parasite (image, "exif-handling-data(image/tiff)");
+
+ 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);
+
+ exif_save_handler->save_exif = (e_save == 0) ? FALSE : TRUE;
+ exif_save_handler->save_xmp = (x_save == 0) ? FALSE : TRUE;
+ exif_save_handler->save_iptc = (i_save == 0) ? FALSE : TRUE;
+ exif_save_handler->save_thumbnail = (t_save == 0) ? FALSE : TRUE;
+
+ 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(image/tiff)",
+ 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/gimprc.common b/plug-ins/common/gimprc.common
index 3a4bae5..59c8454 100644
--- a/plug-ins/common/gimprc.common
+++ b/plug-ins/common/gimprc.common
@@ -88,6 +88,7 @@ lens_apply_RC = lens-apply.rc.o
lens_flare_RC = lens-flare.rc.o
mail_RC = mail.rc.o
max_rgb_RC = max-rgb.rc.o
+metadata_RC = metadata.rc.o
newsprint_RC = newsprint.rc.o
nl_filter_RC = nl-filter.rc.o
noise_rgb_RC = noise-rgb.rc.o
diff --git a/plug-ins/common/metadata.c b/plug-ins/common/metadata.c
new file mode 100644
index 0000000..e94cbae
--- /dev/null
+++ b/plug-ins/common/metadata.c
@@ -0,0 +1,382 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * metadata.c
+ * Copyright (C) 2013 Hartmut Kuhse
+ *
+ * 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 <gegl.h>
+#include <gtk/gtk.h>
+#include <gexiv2/gexiv2.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define PLUG_IN_PROC "plug-in-metadata-editor"
+#define PLUG_IN_BINARY "metadata"
+#define PLUG_IN_ROLE "gimp-metadata"
+
+#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
+{
+ gchar *tag;
+ gchar *mode;
+} iptc_tag;
+
+
+/* local function prototypes */
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static gboolean metadata_dialog (gint32 image_id,
+ GExiv2Metadata *metadata);
+
+static void metadata_dialog_set_metadata (GExiv2Metadata *metadata,
+ GtkBuilder *builder);
+
+static void metadata_dialog_iptc_callback (GtkWidget *dialog,
+ GtkBuilder *builder);
+
+
+/* local variables */
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+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" }
+};
+
+
+/* functions */
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef metadata_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "Run mode { RUN-INTERACTIVE (0) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("View and edit metadata (EXIF, IPTC, XMP)"),
+ "View and edit metadata information attached to the "
+ "current image. This can include EXIF, IPTC and/or "
+ "XMP information. Some or all of this metadata "
+ "will be saved in the file, depending on the output "
+ "file format.",
+ "Hartmut Kuhse, Michael Natterer",
+ "Hartmut Kuhse, Michael Natterer",
+ "2013",
+ N_("Show Metadata"),
+ "*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (metadata_args), 0,
+ metadata_args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/File/Info");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[1];
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ INIT_I18N();
+ gimp_ui_init (PLUG_IN_BINARY, TRUE);
+
+ if (! strcmp (name, PLUG_IN_PROC))
+ {
+ GimpMetadata *metadata;
+ gint32 image_ID = param[1].data.d_image;
+
+ metadata = gimp_image_get_metadata (image_ID);
+
+ if (metadata)
+ {
+ metadata_dialog (image_ID, metadata);
+ }
+ else
+ {
+ g_message (_("This image has no metadata attached to it."));
+ }
+
+ status = GIMP_PDB_SUCCESS;
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ values[0].data.d_status = status;
+}
+
+static gboolean
+metadata_dialog (gint32 image_id,
+ GExiv2Metadata *metadata)
+{
+ GtkBuilder *builder;
+ GtkWidget *dialog;
+ GtkWidget *metadata_vbox;
+ GtkWidget *content_area;
+ GObject *write_button;
+ gchar *ui_file;
+ gchar *title;
+ gchar *fname;
+ GError *error = NULL;
+
+ builder = gtk_builder_new ();
+
+ ui_file = g_build_filename (gimp_data_directory (),
+ "ui", "plug-ins", "plug-in-metadata.ui", NULL);
+
+ if (! gtk_builder_add_from_file (builder, ui_file, &error))
+ {
+ g_printerr ("Error occured while loading UI file!\n");
+ g_printerr ("Message: %s\n", error->message);
+ g_clear_error (&error);
+ g_free (ui_file);
+ g_object_unref (builder);
+ return FALSE;
+ }
+
+ g_free (ui_file);
+ fname = g_filename_display_basename (gimp_image_get_uri (image_id));
+ title = g_strdup_printf ("Metadata: %s", fname);
+ g_free (fname);
+
+ dialog = gimp_dialog_new (title,
+ "gimp-metadata-dialog",
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
+
+ NULL);
+
+ g_free (title);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_CLOSE,
+ -1);
+
+ content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+
+ metadata_vbox = GTK_WIDGET (gtk_builder_get_object (builder,
+ "metadata-vbox"));
+ gtk_container_set_border_width (GTK_CONTAINER (metadata_vbox), 12);
+ gtk_box_pack_start (GTK_BOX (content_area), metadata_vbox, TRUE, TRUE, 0);
+
+ write_button = gtk_builder_get_object (builder, "iptc-write-button");
+
+ g_signal_connect (write_button, "clicked",
+ G_CALLBACK (metadata_dialog_iptc_callback),
+ builder);
+
+ metadata_dialog_set_metadata (metadata, builder);
+
+ gtk_dialog_run (GTK_DIALOG (dialog));
+
+ return TRUE;
+}
+
+
+/* private functions */
+
+static void
+metadata_dialog_set_metadata (GExiv2Metadata *metadata,
+ GtkBuilder *builder)
+{
+ GtkListStore *exif_store;
+ GtkListStore *xmp_store;
+ gchar **exif_data;
+ gchar **iptc_data;
+ gchar **xmp_data;
+ gchar *value;
+ gchar *value_utf;
+ GtkTreeIter iter;
+ gint i;
+
+ exif_store = GTK_LIST_STORE (gtk_builder_get_object (builder,
+ "exif-liststore"));
+ xmp_store = GTK_LIST_STORE (gtk_builder_get_object (builder,
+ "xmp-liststore"));
+
+ exif_data = gexiv2_metadata_get_exif_tags (metadata);
+
+ for (i = 0; exif_data[i] != NULL; i++)
+ {
+ gtk_list_store_append (exif_store, &iter);
+ value = gexiv2_metadata_get_tag_interpreted_string (metadata,
+ exif_data[i]);
+ value_utf = g_locale_to_utf8 (value, -1, NULL, NULL, NULL);
+
+ gtk_list_store_set (exif_store, &iter,
+ C_EXIF_TAG, exif_data[i],
+ C_EXIF_VALUE, value_utf,
+ -1);
+ }
+
+ xmp_data = gexiv2_metadata_get_xmp_tags (metadata);
+
+ for (i = 0; xmp_data[i] != NULL; i++)
+ {
+ gtk_list_store_append (xmp_store, &iter);
+ value = gexiv2_metadata_get_tag_interpreted_string (metadata,
+ xmp_data[i]);
+ value_utf = g_locale_to_utf8 (value, -1, NULL, NULL, NULL);
+
+ gtk_list_store_set (xmp_store, &iter,
+ C_XMP_TAG, xmp_data[i],
+ C_XMP_VALUE, value_utf,
+ -1);
+ }
+
+ iptc_data = gexiv2_metadata_get_iptc_tags (metadata);
+
+ for (i = 0; iptc_data[i] != NULL; i++)
+ {
+ GtkWidget *widget;
+
+ 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, -1, NULL, NULL, NULL);
+
+ if (GTK_IS_ENTRY (widget))
+ {
+ GtkEntry *entry_widget = GTK_ENTRY (widget);
+
+ gtk_entry_set_text (entry_widget, value_utf);
+ }
+ else if (GTK_IS_TEXT_VIEW (widget))
+ {
+ GtkTextView *text_view = GTK_TEXT_VIEW (widget);
+ GtkTextBuffer *buffer;
+
+ buffer = gtk_text_view_get_buffer (text_view);
+ gtk_text_buffer_set_text (buffer, value_utf, -1);
+ }
+ }
+}
+
+static void
+metadata_dialog_iptc_callback (GtkWidget *dialog,
+ GtkBuilder *builder)
+{
+#if 0
+ GExiv2Metadata *metadata;
+ gint i;
+
+ metadata = gimp_image_get_metadata (handler->image);
+
+ for (i = 0; i < G_N_ELEMENTS (iptc_tags); i++)
+ {
+ GObject *object = gtk_builder_get_object (handler->builder,
+ iptc_tags[i].tag);
+
+ if (! strcmp ("single", iptc_tags[i].mode))
+ {
+ GtkEntry *entry = GTK_ENTRY (object);
+
+ gexiv2_metadata_set_tag_string (metadata, iptc_tags[i].tag,
+ gtk_entry_get_text (entry));
+ }
+ else if (!strcmp ("multi", iptc_tags[i].mode))
+ {
+ GtkTextView *text_view = GTK_TEXT_VIEW (object);
+ GtkTextBuffer *buffer;
+ GtkTextIter start;
+ GtkTextIter end;
+ gchar *text;
+
+ buffer = gtk_text_view_get_buffer (text_view);
+ gtk_text_buffer_get_start_iter (buffer, &start);
+ gtk_text_buffer_get_end_iter (buffer, &end);
+
+ text = gtk_text_buffer_get_text (buffer, &start, &end, TRUE);
+ gexiv2_metadata_set_tag_string (metadata, iptc_tags[i].tag, text);
+ g_free (text);
+ }
+ }
+#endif
+}
diff --git a/plug-ins/common/plugin-defs.pl b/plug-ins/common/plugin-defs.pl
index 5722c4e..9456e23 100644
--- a/plug-ins/common/plugin-defs.pl
+++ b/plug-ins/common/plugin-defs.pl
@@ -89,6 +89,7 @@
'lens-flare' => { ui => 1 },
'mail' => { ui => 1, optional => 1 },
'max-rgb' => { ui => 1 },
+ 'metadata' => { ui => 1, libs => 'GEXIV2_LIBS', cflags => 'GEXIV2_CFLAGS' },
'newsprint' => { ui => 1 },
'nl-filter' => { ui => 1 },
'noise-rgb' => { ui => 1 },
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/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 9aa0c81..9fbe363 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
@@ -69,6 +61,7 @@
#define DEFAULT_EXIF TRUE
#define DEFAULT_THUMBNAIL FALSE
#define DEFAULT_XMP TRUE
+#define DEFAULT_IPTC TRUE
#define DEFAULT_USE_ORIG_QUALITY FALSE
#define JPEG_DEFAULTS_PARASITE "jpeg-save-defaults"
@@ -111,6 +104,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 +121,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;
@@ -527,82 +512,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)
{
@@ -614,36 +523,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)
{
@@ -913,7 +793,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);
@@ -1000,9 +880,9 @@ 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_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), jsvals.save_exif);
gtk_table_attach (GTK_TABLE (table), toggle, 0, 1, 2, 3, GTK_FILL, 0, 0, 0);
gtk_widget_show (toggle);
@@ -1013,13 +893,11 @@ save_dialog (void)
G_CALLBACK (make_preview),
NULL);
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
- jsvals.save_exif && exif_data);
-
- 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"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), jsvals.save_thumbnail);
gtk_table_attach (GTK_TABLE (table), toggle, 0, 1, 3, 4, GTK_FILL, 0, 0, 0);
gtk_widget_show (toggle);
@@ -1030,13 +908,10 @@ save_dialog (void)
G_CALLBACK (make_preview),
NULL);
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
- jsvals.save_thumbnail);
-#endif /* HAVE_LIBEXIF */
-
/* XMP metadata */
pg.save_xmp = toggle =
gtk_check_button_new_with_mnemonic (_("Save _XMP data"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), jsvals.save_xmp);
gtk_table_attach (GTK_TABLE (table), toggle, 0, 1, 4, 5, GTK_FILL, 0, 0, 0);
gtk_widget_show (toggle);
@@ -1047,16 +922,28 @@ save_dialog (void)
G_CALLBACK (make_preview),
NULL);
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
- jsvals.save_xmp && has_metadata);
+ gtk_widget_set_sensitive (toggle, TRUE);
- gtk_widget_set_sensitive (toggle, has_metadata);
+ /* IPTC metadata */
+ pg.save_iptc = toggle =
+ gtk_check_button_new_with_mnemonic (_("Save _IPTC data"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), jsvals.save_iptc);
+ 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_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,
@@ -1206,7 +1093,6 @@ save_dialog (void)
G_CALLBACK (save_defaults),
&pg);
gtk_widget_show (frame);
-
gtk_widget_show (table);
gtk_widget_show (dialog);
@@ -1266,13 +1152,9 @@ load_defaults (void)
jsvals.save_exif = DEFAULT_EXIF;
jsvals.save_thumbnail = DEFAULT_THUMBNAIL;
jsvals.save_xmp = DEFAULT_XMP;
+ jsvals.save_iptc = DEFAULT_IPTC;
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)
@@ -1283,7 +1165,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,
@@ -1296,12 +1178,15 @@ 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)
- memcpy (&jsvals, &tmpvals, sizeof (tmpvals));
+ if (num_fields == 14)
+ {
+ memcpy (&jsvals, &tmpvals, sizeof (tmpvals));
+ }
g_free (def_str);
}
@@ -1312,7 +1197,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,
@@ -1325,6 +1210,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,
@@ -1350,11 +1236,10 @@ 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);
+ SET_ACTIVE_BTTN (save_iptc);
#undef SET_ACTIVE_BTTN
@@ -1448,217 +1333,30 @@ use_orig_qual_changed2 (GtkWidget *toggle,
}
}
-#ifdef HAVE_LIBEXIF
-
-static guchar *tbuffer = NULL;
-static guchar *tbuffer2 = NULL;
-
-static gint tbuffer_count = 0;
-
-typedef struct
-{
- struct jpeg_destination_mgr pub; /* public fields */
- guchar *buffer;
- gint size;
-} my_destination_mgr;
-
-typedef my_destination_mgr *my_dest_ptr;
-
-static void
-init_destination (j_compress_ptr cinfo)
-{
-}
-
-static gboolean
-empty_output_buffer (j_compress_ptr cinfo)
-{
- my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
-
- tbuffer_count = tbuffer_count + 16384;
- tbuffer = g_renew (guchar, tbuffer, tbuffer_count);
- g_memmove (tbuffer + tbuffer_count - 16384, tbuffer2, 16384);
-
- dest->pub.next_output_byte = tbuffer2;
- dest->pub.free_in_buffer = 16384;
-
- return TRUE;
-}
-
-static void
-term_destination (j_compress_ptr cinfo)
-{
- my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
-
- tbuffer_count = (tbuffer_count + 16384) - (dest->pub.free_in_buffer);
-
- 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);
-}
-
-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 */
+ GimpParasite *parasite;
+ gchar *def_str;
- /* This is an important step since it will release a good deal of memory. */
- jpeg_destroy_compress (&cinfo);
+ 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;
- /* And we're done! */
+ 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 (thumbnail_data)
- {
- g_free (thumbnail_data);
- thumbnail_data = NULL;
- }
-
- if (buf)
- {
- g_free (buf);
- buf = NULL;
- }
+ gimp_image_detach_parasite (image, "exif-handling-data(image/jpeg)");
+ parasite = gimp_parasite_new ("exif-handling-data(image/jpeg)",
+ 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..663ba51 100644
--- a/plug-ins/file-jpeg/jpeg-save.h
+++ b/plug-ins/file-jpeg/jpeg-save.h
@@ -32,11 +32,22 @@ typedef struct
gboolean save_exif;
gboolean save_thumbnail;
gboolean save_xmp;
+ gboolean save_iptc;
gboolean use_orig_quality;
} JpegSaveVals;
extern JpegSaveVals jsvals;
+typedef struct
+{
+ gboolean save_exif;
+ gboolean save_xmp;
+ gboolean save_iptc;
+ gboolean save_thumbnail;
+} ExifSaveHandler;
+
+extern ExifSaveHandler exifvals;
+
extern gint32 orig_image_ID_global;
extern gint32 drawable_ID_global;
@@ -50,4 +61,7 @@ gboolean save_image (const gchar *filename,
gboolean save_dialog (void);
void load_defaults (void);
+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..cea9e61 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.
*/
@@ -58,6 +49,7 @@ gboolean load_interactive;
gchar *image_comment;
gint32 display_ID;
JpegSaveVals jsvals;
+ExifSaveHandler exifvals;
gint32 orig_image_ID_global;
gint32 drawable_ID_global;
gboolean has_metadata;
@@ -66,10 +58,6 @@ JpegSubsampling orig_subsmp;
gint num_quant_tables;
-#ifdef HAVE_LIBEXIF
-ExifData *exif_data = NULL;
-#endif
-
const GimpPlugInInfo PLUG_IN_INFO =
{
NULL, /* init_proc */
@@ -96,22 +84,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 +121,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 +200,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 +259,7 @@ 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 from gimp parasite */
load_defaults ();
switch (run_mode)
@@ -427,6 +331,7 @@ run (const gchar *name,
jsvals.save_exif = save_vals->save_exif;
jsvals.save_thumbnail = save_vals->save_thumbnail;
jsvals.save_xmp = save_vals->save_xmp;
+ jsvals.save_iptc = save_vals->save_iptc;
jsvals.use_orig_quality = save_vals->use_orig_quality;
gimp_parasite_free (parasite);
@@ -531,6 +436,7 @@ run (const gchar *name,
0, sizeof (jsvals), &jsvals);
gimp_image_attach_parasite (orig_image_ID, parasite);
gimp_parasite_free (parasite);
+ save_exif_defaults (orig_image_ID, exifvals);
}
}
else
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..aec82b9 100644
--- a/plug-ins/ui/Makefile.am
+++ b/plug-ins/ui/Makefile.am
@@ -2,6 +2,8 @@ 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 \
+ plug-in-metadata.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">●</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"><b>Compression</b></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"><b>Comment</b></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/plug-ins/ui/plug-in-metadata.ui b/plug-ins/ui/plug-in-metadata.ui
new file mode 100644
index 0000000..1d076bc
--- /dev/null
+++ b/plug-ins/ui/plug-in-metadata.ui
@@ -0,0 +1,908 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.0 -->
+ <object class="GtkListStore" id="exif-liststore">
+ <columns>
+ <!-- column-name c_exif_tag -->
+ <column type="gchararray"/>
+ <!-- column-name c_exif_value -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkListStore" id="xmp-liststore">
+ <columns>
+ <!-- column-name c_xmp_tag -->
+ <column type="gchararray"/>
+ <!-- column-name c_xmp_value -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkVBox" id="metadata-vbox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkNotebook" id="metadata-notebook">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="scrollable">True</property>
+ <child>
+ <object class="GtkScrolledWindow" id="exif-scroll">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="border_width">6</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTreeView" id="exif-treeview">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="model">exif-liststore</property>
+ <property name="headers_clickable">False</property>
+ <property name="search_column">0</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection" id="exif-treeview-selection"/>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="exif_tag_column">
+ <property name="resizable">True</property>
+ <property name="spacing">3</property>
+ <property name="title" translatable="yes">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" translatable="yes">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>
+ <child type="tab">
+ <object class="GtkLabel" id="exif">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xpad">4</property>
+ <property name="ypad">4</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>
+ <property name="border_width">6</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTreeView" id="xm-ptreeview">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="model">xmp-liststore</property>
+ <property name="headers_clickable">False</property>
+ <property name="search_column">0</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection" id="xmp-treeview-selection"/>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="xmp_tag_column">
+ <property name="resizable">True</property>
+ <property name="spacing">3</property>
+ <property name="title" translatable="yes">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" translatable="yes">Value</property>
+ <child>
+ <object class="GtkCellRendererText" id="xmp_value_cell_renderer"/>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </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="can_focus">False</property>
+ <property name="xpad">4</property>
+ <property name="ypad">4</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="box1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkNotebook" id="iptc-notebook">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkVBox" id="box2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkTable" id="desctable">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">6</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="can_focus">False</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>
+ <packing>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_author">
+ <property name="visible">True</property>
+ <property name="can_focus">False</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>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_authortitle">
+ <property name="visible">True</property>
+ <property name="can_focus">False</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>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_copyright">
+ <property name="visible">True</property>
+ <property name="can_focus">False</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>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_caption">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">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>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_captionwriter">
+ <property name="visible">True</property>
+ <property name="can_focus">False</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>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_headline">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">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>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_specialinstruct">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">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>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</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">●</property>
+ <property name="invisible_char_set">True</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">●</property>
+ <property name="invisible_char_set">True</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">●</property>
+ <property name="invisible_char_set">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="GtkEntry" id="Iptc.Application2.Copyright">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</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">●</property>
+ <property name="invisible_char_set">True</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>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTextView" id="Iptc.Application2.Caption">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </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>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTextView" id="Iptc.Application2.Headline">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </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>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTextView" id="Iptc.Application2.SpecialInstructions">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </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>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="iptcdesc">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xpad">2</property>
+ <property name="ypad">2</property>
+ <property name="label" translatable="yes">Description</property>
+ </object>
+ <packing>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="box3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkTable" id="table3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">6</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="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Keywords</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_category1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</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>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_suppcat1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">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>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_urgency1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</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>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow10">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTextView" id="Iptc.Application2.Keywords">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </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>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTextView" id="Iptc.Application2.SuppCategory">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </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">●</property>
+ <property name="invisible_char_set">True</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">●</property>
+ <property name="invisible_char_set">True</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>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </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="can_focus">False</property>
+ <property name="xpad">2</property>
+ <property name="ypad">2</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="GtkVBox" id="box4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkTable" id="table4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">6</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="hseparator4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <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="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Credit</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_source">
+ <property name="visible">True</property>
+ <property name="can_focus">False</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>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</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">●</property>
+ <property name="invisible_char_set">True</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">●</property>
+ <property name="invisible_char_set">True</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="can_focus">False</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>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</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">●</property>
+ <property name="invisible_char_set">True</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="can_focus">False</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>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</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">●</property>
+ <property name="invisible_char_set">True</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="can_focus">False</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>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</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">●</property>
+ <property name="invisible_char_set">True</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="can_focus">False</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>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</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">●</property>
+ <property name="invisible_char_set">True</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="can_focus">False</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>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</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">●</property>
+ <property name="invisible_char_set">True</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="hseparator2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="right_attach">2</property>
+ <property name="top_attach">7</property>
+ <property name="bottom_attach">8</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </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="can_focus">False</property>
+ <property name="xpad">2</property>
+ <property name="ypad">2</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="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="iptc-write-button">
+ <property name="label" translatable="yes">Write IPTC Data</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">True</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="can_focus">False</property>
+ <property name="xpad">4</property>
+ <property name="ypad">4</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="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+</interface>
diff --git a/po-plug-ins/POTFILES.in b/po-plug-ins/POTFILES.in
index d9c9502..a6518e5 100644
--- a/po-plug-ins/POTFILES.in
+++ b/po-plug-ins/POTFILES.in
@@ -96,6 +96,7 @@ plug-ins/common/lens-apply.c
plug-ins/common/lens-flare.c
plug-ins/common/mail.c
plug-ins/common/max-rgb.c
+plug-ins/common/metadata.c
plug-ins/common/newsprint.c
plug-ins/common/nl-filter.c
plug-ins/common/noise-rgb.c
@@ -187,6 +188,8 @@ plug-ins/gimpressionist/sizemap.c
plug-ins/gimpressionist/utils.c
[type: gettext/glade]plug-ins/ui/plug-in-file-gif.ui
[type: gettext/glade]plug-ins/ui/plug-in-file-png.ui
+[type: gettext/glade]plug-ins/ui/plug-in-file-tiff.ui
+[type: gettext/glade]plug-ins/ui/plug-in-metadata.ui
plug-ins/gradient-flare/gradient-flare.c
plug-ins/help-browser/dialog.c
plug-ins/help-browser/help-browser.c
diff --git a/tools/pdbgen/pdb/image.pdb b/tools/pdbgen/pdb/image.pdb
index 50a9342..94d6ebc 100644
--- a/tools/pdbgen/pdb/image.pdb
+++ b/tools/pdbgen/pdb/image.pdb
@@ -1625,6 +1625,61 @@ CODE
);
}
+sub image_get_metadata {
+ $blurb = "Returns the image's metadata.";
+ $help = 'Returns exif/iptc/xmp metadata from the image.';
+
+ &std_pdb_misc('2013', '2.10');
+
+ @inargs = (
+ { name => 'image', type => 'image',
+ desc => 'The image' }
+ );
+
+ @outargs = (
+ { name => 'metadata_string', type => 'string', wrap => 1,
+ desc => 'The exif/ptc/xmp metadata as a string'}
+ );
+
+ %invoke = (
+ code => <<'CODE'
+{
+ GimpMetadata *metadata = gimp_image_get_metadata (image);
+
+ if (metadata)
+ metadata_string = gimp_metadata_serialize (metadata);
+}
+CODE
+ );
+}
+
+sub image_set_metadata {
+ $blurb = "Set the image's metadata.";
+ $help = 'Sets exif/iptc/xmp metadata on the image.';
+
+ &std_pdb_misc('2013', '2.10');
+
+ @inargs = (
+ { name => 'image', type => 'image',
+ desc => 'The image' },
+ { name => 'metadata_string', type => 'string', wrap => 1,
+ desc => 'The exif/ptc/xmp metadata as a string' }
+ );
+
+ %invoke = (
+ code => <<'CODE'
+{
+ GimpMetadata *metadata = gimp_metadata_deserialize (metadata_string);
+
+ gimp_image_set_metadata (image, metadata);
+
+ if (metadata)
+ g_object_unref (metadata);
+}
+CODE
+ );
+}
+
sub image_clean_all {
$blurb = 'Set the image dirty count to 0.';
@@ -2988,6 +3043,7 @@ CODE
"libgimpbase/gimpbase.h"
"core/gimp.h"
"core/gimpcontainer.h"
+ "core/gimpimage-metadata.h"
"core/gimpprogress.h"
"core/gimptempbuf.h"
"core/gimpunit.h"
@@ -3029,6 +3085,7 @@ CODE
image_flatten image_merge_visible_layers image_merge_down
image_add_layer_mask image_remove_layer_mask
image_get_colormap image_set_colormap
+ image_get_metadata image_set_metadata
image_clean_all image_is_dirty
image_thumbnail
image_get_active_layer image_set_active_layer
@@ -3060,7 +3117,7 @@ CODE
# image_add_layer_mask and image_remove_layer_mask.
# If adding or removing functions, make sure the range below is
# updated correctly!
-%exports = (app => [ procs], lib => [ procs[0 44,47..85]]);
+%exports = (app => [ procs], lib => [ procs[0 44,47..87]]);
$desc = 'Image';
$doc_title = 'gimpimage';
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]