Completely rewrite metadata handling using gexiv2

Based on original patches from Hartmut Kuhse and modified
by Michael Natterer. Changes include:

- remove libexif dependency and add a hard dependency on gexiv2
- typedef GExiv2Metadata to GimpMetadata to avoid having to
  include gexiv2 globally
- add basic GimpMetadata handling functions to libgimpbase
- add image and image file specific metadata functions to libgimp,
  including the exif orientation image rotate dialog
- port plug-ins to use the new APIs
- port file-tiff-save's UI to GtkBuilder
- add new plug-in "metadata" to view the image's metadata
- keep metadata around as GimpImage member in the core
- update the image's metadata on image size, resolution and precision
  changes
- obsolete the old metadata parasites
- migrate the old parasites to new GimpMetadata object on XCF load
This commit is contained in:
Hartmut Kuhse 2013-10-19 18:38:01 +02:00 committed by Michael Natterer
parent c51ce66f1b
commit 21bed1e2fb
68 changed files with 4810 additions and 1572 deletions

View file

@ -177,6 +177,7 @@ gimpconsoleldadd = \
$(CAIRO_LIBS) \ $(CAIRO_LIBS) \
$(GEGL_LIBS) \ $(GEGL_LIBS) \
$(GLIB_LIBS) \ $(GLIB_LIBS) \
$(GEXIV2_LIBS) \
$(INTLLIBS) \ $(INTLLIBS) \
$(RT_LIBS) $(RT_LIBS)

View file

@ -110,7 +110,8 @@ test_config_LDADD = \
$(PANGOCAIRO_LIBS) \ $(PANGOCAIRO_LIBS) \
$(GDK_PIXBUF_LIBS) \ $(GDK_PIXBUF_LIBS) \
$(GEGL_LIBS) \ $(GEGL_LIBS) \
$(GLIB_LIBS) $(GIO_LIBS) \
$(GEXIV2_LIBS)
CLEANFILES = $(EXTRA_PROGRAMS) foorc CLEANFILES = $(EXTRA_PROGRAMS) foorc

View file

@ -23,7 +23,7 @@
#include "stdlib.h" #include "stdlib.h"
#include "string.h" #include "string.h"
#include <glib-object.h> #include <gio/gio.h>
#include "libgimpbase/gimpbase.h" #include "libgimpbase/gimpbase.h"
#include "libgimpbase/gimpbase-private.h" #include "libgimpbase/gimpbase-private.h"

View file

@ -10,6 +10,7 @@ AM_CPPFLAGS = \
$(CAIRO_CFLAGS) \ $(CAIRO_CFLAGS) \
$(GEGL_CFLAGS) \ $(GEGL_CFLAGS) \
$(GDK_PIXBUF_CFLAGS) \ $(GDK_PIXBUF_CFLAGS) \
$(GEXIV2_CFLAGS) \
-I$(includedir) -I$(includedir)
noinst_LIBRARIES = libappcore.a noinst_LIBRARIES = libappcore.a
@ -219,6 +220,8 @@ libappcore_a_sources = \
gimpimage-item-list.h \ gimpimage-item-list.h \
gimpimage-merge.c \ gimpimage-merge.c \
gimpimage-merge.h \ gimpimage-merge.h \
gimpimage-metadata.c \
gimpimage-metadata.h \
gimpimage-new.c \ gimpimage-new.c \
gimpimage-new.h \ gimpimage-new.h \
gimpimage-pick-color.c \ gimpimage-pick-color.c \

View file

@ -1115,6 +1115,7 @@ gimp_undo_type_get_type (void)
{ GIMP_UNDO_IMAGE_SIZE, "GIMP_UNDO_IMAGE_SIZE", "image-size" }, { GIMP_UNDO_IMAGE_SIZE, "GIMP_UNDO_IMAGE_SIZE", "image-size" },
{ GIMP_UNDO_IMAGE_RESOLUTION, "GIMP_UNDO_IMAGE_RESOLUTION", "image-resolution" }, { GIMP_UNDO_IMAGE_RESOLUTION, "GIMP_UNDO_IMAGE_RESOLUTION", "image-resolution" },
{ GIMP_UNDO_IMAGE_GRID, "GIMP_UNDO_IMAGE_GRID", "image-grid" }, { GIMP_UNDO_IMAGE_GRID, "GIMP_UNDO_IMAGE_GRID", "image-grid" },
{ GIMP_UNDO_IMAGE_METADATA, "GIMP_UNDO_IMAGE_METADATA", "image-metadata" },
{ GIMP_UNDO_IMAGE_COLORMAP, "GIMP_UNDO_IMAGE_COLORMAP", "image-colormap" }, { GIMP_UNDO_IMAGE_COLORMAP, "GIMP_UNDO_IMAGE_COLORMAP", "image-colormap" },
{ GIMP_UNDO_GUIDE, "GIMP_UNDO_GUIDE", "guide" }, { GIMP_UNDO_GUIDE, "GIMP_UNDO_GUIDE", "guide" },
{ GIMP_UNDO_SAMPLE_POINT, "GIMP_UNDO_SAMPLE_POINT", "sample-point" }, { GIMP_UNDO_SAMPLE_POINT, "GIMP_UNDO_SAMPLE_POINT", "sample-point" },
@ -1205,6 +1206,7 @@ gimp_undo_type_get_type (void)
{ GIMP_UNDO_IMAGE_SIZE, NC_("undo-type", "Image size"), NULL }, { GIMP_UNDO_IMAGE_SIZE, NC_("undo-type", "Image size"), NULL },
{ GIMP_UNDO_IMAGE_RESOLUTION, NC_("undo-type", "Image resolution change"), NULL }, { GIMP_UNDO_IMAGE_RESOLUTION, NC_("undo-type", "Image resolution change"), NULL },
{ GIMP_UNDO_IMAGE_GRID, NC_("undo-type", "Grid"), NULL }, { GIMP_UNDO_IMAGE_GRID, NC_("undo-type", "Grid"), NULL },
{ GIMP_UNDO_IMAGE_METADATA, NC_("undo-type", "Change metadata"), NULL },
{ GIMP_UNDO_IMAGE_COLORMAP, NC_("undo-type", "Change indexed palette"), NULL }, { GIMP_UNDO_IMAGE_COLORMAP, NC_("undo-type", "Change indexed palette"), NULL },
{ GIMP_UNDO_GUIDE, NC_("undo-type", "Guide"), NULL }, { GIMP_UNDO_GUIDE, NC_("undo-type", "Guide"), NULL },
{ GIMP_UNDO_SAMPLE_POINT, NC_("undo-type", "Sample Point"), NULL }, { GIMP_UNDO_SAMPLE_POINT, NC_("undo-type", "Sample Point"), NULL },

View file

@ -530,6 +530,7 @@ typedef enum /*< pdb-skip >*/
GIMP_UNDO_IMAGE_SIZE, /*< desc="Image size" >*/ GIMP_UNDO_IMAGE_SIZE, /*< desc="Image size" >*/
GIMP_UNDO_IMAGE_RESOLUTION, /*< desc="Image resolution change" >*/ GIMP_UNDO_IMAGE_RESOLUTION, /*< desc="Image resolution change" >*/
GIMP_UNDO_IMAGE_GRID, /*< desc="Grid" >*/ GIMP_UNDO_IMAGE_GRID, /*< desc="Grid" >*/
GIMP_UNDO_IMAGE_METADATA, /*< desc="Change metadata" >*/
GIMP_UNDO_IMAGE_COLORMAP, /*< desc="Change indexed palette" >*/ GIMP_UNDO_IMAGE_COLORMAP, /*< desc="Change indexed palette" >*/
GIMP_UNDO_GUIDE, /*< desc="Guide" >*/ GIMP_UNDO_GUIDE, /*< desc="Guide" >*/
GIMP_UNDO_SAMPLE_POINT, /*< desc="Sample Point" >*/ GIMP_UNDO_SAMPLE_POINT, /*< desc="Sample Point" >*/

View file

@ -0,0 +1,103 @@
/* 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"
#include "gimpimage-undo-push.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,
gboolean push_undo)
{
GimpImagePrivate *private;
g_return_if_fail (GIMP_IS_IMAGE (image));
private = GIMP_IMAGE_GET_PRIVATE (image);
if (metadata != private->metadata)
{
if (push_undo)
gimp_image_undo_push_image_metadata (image, NULL);
if (private->metadata)
g_object_unref (private->metadata);
private->metadata = metadata;
if (private->metadata)
{
gdouble xres, yres;
g_object_ref (private->metadata);
gimp_metadata_set_pixel_size (metadata,
gimp_image_get_width (image),
gimp_image_get_height (image));
switch (gimp_image_get_component_type (image))
{
case GIMP_COMPONENT_TYPE_U8:
gimp_metadata_set_bits_per_sample (metadata, 8);
break;
case GIMP_COMPONENT_TYPE_U16:
case GIMP_COMPONENT_TYPE_HALF:
gimp_metadata_set_bits_per_sample (metadata, 16);
break;
case GIMP_COMPONENT_TYPE_U32:
case GIMP_COMPONENT_TYPE_FLOAT:
gimp_metadata_set_bits_per_sample (metadata, 32);
break;
}
gimp_image_get_resolution (image, &xres, &yres);
gimp_metadata_set_resolution (metadata, xres, yres,
gimp_image_get_unit (image));
}
g_object_notify (G_OBJECT (image), "metadata");
}
}

View file

@ -0,0 +1,28 @@
/* 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,
gboolean push_undo);
#endif /* __GIMP_IMAGE_METADATA_H__ */

View file

@ -55,6 +55,8 @@ struct _GimpImagePrivate
const Babl *babl_palette_rgb; /* palette's RGB Babl format */ const Babl *babl_palette_rgb; /* palette's RGB Babl format */
const Babl *babl_palette_rgba; /* palette's RGBA Babl format */ const Babl *babl_palette_rgba; /* palette's RGBA Babl format */
GimpMetadata *metadata; /* image's metadata */
gint dirty; /* dirty flag -- # of ops */ gint dirty; /* dirty flag -- # of ops */
guint dirty_time; /* time when image became dirty */ guint dirty_time; /* time when image became dirty */
gint export_dirty; /* 'dirty' but for export */ gint export_dirty; /* 'dirty' but for export */

View file

@ -148,6 +148,18 @@ gimp_image_undo_push_image_colormap (GimpImage *image,
NULL); NULL);
} }
GimpUndo *
gimp_image_undo_push_image_metadata (GimpImage *image,
const gchar *undo_desc)
{
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
return gimp_image_undo_push (image, GIMP_TYPE_IMAGE_UNDO,
GIMP_UNDO_IMAGE_METADATA, undo_desc,
GIMP_DIRTY_IMAGE_META,
NULL);
}
GimpUndo * GimpUndo *
gimp_image_undo_push_image_parasite (GimpImage *image, gimp_image_undo_push_image_parasite (GimpImage *image,
const gchar *undo_desc, const gchar *undo_desc,

View file

@ -38,6 +38,8 @@ GimpUndo * gimp_image_undo_push_image_grid (GimpImage *image,
GimpGrid *grid); GimpGrid *grid);
GimpUndo * gimp_image_undo_push_image_colormap (GimpImage *image, GimpUndo * gimp_image_undo_push_image_colormap (GimpImage *image,
const gchar *undo_desc); const gchar *undo_desc);
GimpUndo * gimp_image_undo_push_image_metadata (GimpImage *image,
const gchar *undo_desc);
GimpUndo * gimp_image_undo_push_image_parasite (GimpImage *image, GimpUndo * gimp_image_undo_push_image_parasite (GimpImage *image,
const gchar *undo_desc, const gchar *undo_desc,
const GimpParasite *parasite); const GimpParasite *parasite);

View file

@ -23,6 +23,7 @@
#include <cairo.h> #include <cairo.h>
#include <gdk-pixbuf/gdk-pixbuf.h> #include <gdk-pixbuf/gdk-pixbuf.h>
#include <gegl.h> #include <gegl.h>
#include <gexiv2/gexiv2.h>
#include "libgimpcolor/gimpcolor.h" #include "libgimpcolor/gimpcolor.h"
#include "libgimpmath/gimpmath.h" #include "libgimpmath/gimpmath.h"
@ -47,6 +48,7 @@
#include "gimpimage.h" #include "gimpimage.h"
#include "gimpimage-colormap.h" #include "gimpimage-colormap.h"
#include "gimpimage-guides.h" #include "gimpimage-guides.h"
#include "gimpimage-metadata.h"
#include "gimpimage-sample-points.h" #include "gimpimage-sample-points.h"
#include "gimpimage-preview.h" #include "gimpimage-preview.h"
#include "gimpimage-private.h" #include "gimpimage-private.h"
@ -130,6 +132,7 @@ enum
PROP_HEIGHT, PROP_HEIGHT,
PROP_BASE_TYPE, PROP_BASE_TYPE,
PROP_PRECISION, PROP_PRECISION,
PROP_METADATA,
PROP_BUFFER PROP_BUFFER
}; };
@ -165,12 +168,14 @@ static gchar * gimp_image_get_description (GimpViewable *viewable,
static void gimp_image_real_mode_changed (GimpImage *image); static void gimp_image_real_mode_changed (GimpImage *image);
static void gimp_image_real_precision_changed(GimpImage *image); static void gimp_image_real_precision_changed(GimpImage *image);
static void gimp_image_real_resolution_changed(GimpImage *image);
static void gimp_image_real_size_changed_detailed static void gimp_image_real_size_changed_detailed
(GimpImage *image, (GimpImage *image,
gint previous_origin_x, gint previous_origin_x,
gint previous_origin_y, gint previous_origin_y,
gint previous_width, gint previous_width,
gint previous_height); gint previous_height);
static void gimp_image_real_unit_changed (GimpImage *image);
static void gimp_image_real_colormap_changed (GimpImage *image, static void gimp_image_real_colormap_changed (GimpImage *image,
gint color_index); gint color_index);
@ -552,9 +557,9 @@ gimp_image_class_init (GimpImageClass *klass)
klass->component_visibility_changed = NULL; klass->component_visibility_changed = NULL;
klass->component_active_changed = NULL; klass->component_active_changed = NULL;
klass->mask_changed = NULL; klass->mask_changed = NULL;
klass->resolution_changed = NULL; klass->resolution_changed = gimp_image_real_resolution_changed;
klass->size_changed_detailed = gimp_image_real_size_changed_detailed; klass->size_changed_detailed = gimp_image_real_size_changed_detailed;
klass->unit_changed = NULL; klass->unit_changed = gimp_image_real_unit_changed;
klass->quick_mask_changed = NULL; klass->quick_mask_changed = NULL;
klass->selection_invalidate = NULL; klass->selection_invalidate = NULL;
@ -610,6 +615,11 @@ gimp_image_class_init (GimpImageClass *klass)
GIMP_PARAM_READWRITE | GIMP_PARAM_READWRITE |
G_PARAM_CONSTRUCT)); G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class, PROP_METADATA,
g_param_spec_object ("metadata", NULL, NULL,
GEXIV2_TYPE_METADATA,
GIMP_PARAM_READABLE));
g_object_class_override_property (object_class, PROP_BUFFER, "buffer"); g_object_class_override_property (object_class, PROP_BUFFER, "buffer");
g_type_class_add_private (klass, sizeof (GimpImagePrivate)); g_type_class_add_private (klass, sizeof (GimpImagePrivate));
@ -667,6 +677,8 @@ gimp_image_init (GimpImage *image)
private->n_colors = 0; private->n_colors = 0;
private->palette = NULL; private->palette = NULL;
private->metadata = NULL;
private->dirty = 1; private->dirty = 1;
private->dirty_time = 0; private->dirty_time = 0;
private->undo_freeze_count = 0; private->undo_freeze_count = 0;
@ -837,6 +849,7 @@ gimp_image_set_property (GObject *object,
case PROP_PRECISION: case PROP_PRECISION:
private->precision = g_value_get_enum (value); private->precision = g_value_get_enum (value);
break; break;
case PROP_METADATA:
case PROP_BUFFER: case PROP_BUFFER:
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@ -873,6 +886,9 @@ gimp_image_get_property (GObject *object,
case PROP_PRECISION: case PROP_PRECISION:
g_value_set_enum (value, private->precision); g_value_set_enum (value, private->precision);
break; break;
case PROP_METADATA:
g_value_set_object (value, gimp_image_get_metadata (image));
break;
case PROP_BUFFER: case PROP_BUFFER:
g_value_set_object (value, gimp_image_get_buffer (GIMP_PICKABLE (image))); g_value_set_object (value, gimp_image_get_buffer (GIMP_PICKABLE (image)));
break; break;
@ -948,6 +964,12 @@ gimp_image_finalize (GObject *object)
if (private->colormap) if (private->colormap)
gimp_image_colormap_free (image); gimp_image_colormap_free (image);
if (private->metadata)
{
g_object_unref (private->metadata);
private->metadata = NULL;
}
if (private->layers) if (private->layers)
{ {
g_object_unref (private->layers); g_object_unref (private->layers);
@ -1128,9 +1150,10 @@ gimp_image_get_size (GimpViewable *viewable,
static void static void
gimp_image_size_changed (GimpViewable *viewable) gimp_image_size_changed (GimpViewable *viewable)
{ {
GimpImage *image = GIMP_IMAGE (viewable); GimpImage *image = GIMP_IMAGE (viewable);
GList *all_items; GimpMetadata *metadata;
GList *list; GList *all_items;
GList *list;
if (GIMP_VIEWABLE_CLASS (parent_class)->size_changed) if (GIMP_VIEWABLE_CLASS (parent_class)->size_changed)
GIMP_VIEWABLE_CLASS (parent_class)->size_changed (viewable); GIMP_VIEWABLE_CLASS (parent_class)->size_changed (viewable);
@ -1155,6 +1178,12 @@ gimp_image_size_changed (GimpViewable *viewable)
gimp_viewable_size_changed (GIMP_VIEWABLE (gimp_image_get_mask (image))); 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)); gimp_projectable_structure_changed (GIMP_PROJECTABLE (image));
} }
@ -1181,9 +1210,48 @@ gimp_image_real_mode_changed (GimpImage *image)
static void static void
gimp_image_real_precision_changed (GimpImage *image) gimp_image_real_precision_changed (GimpImage *image)
{ {
GimpMetadata *metadata;
metadata = gimp_image_get_metadata (image);
if (metadata)
{
switch (gimp_image_get_component_type (image))
{
case GIMP_COMPONENT_TYPE_U8:
gimp_metadata_set_bits_per_sample (metadata, 8);
break;
case GIMP_COMPONENT_TYPE_U16:
case GIMP_COMPONENT_TYPE_HALF:
gimp_metadata_set_bits_per_sample (metadata, 16);
break;
case GIMP_COMPONENT_TYPE_U32:
case GIMP_COMPONENT_TYPE_FLOAT:
gimp_metadata_set_bits_per_sample (metadata, 32);
break;
}
}
gimp_projectable_structure_changed (GIMP_PROJECTABLE (image)); gimp_projectable_structure_changed (GIMP_PROJECTABLE (image));
} }
static void
gimp_image_real_resolution_changed (GimpImage *image)
{
GimpMetadata *metadata;
metadata = gimp_image_get_metadata (image);
if (metadata)
{
gdouble xres, yres;
gimp_image_get_resolution (image, &xres, &yres);
gimp_metadata_set_resolution (metadata, xres, yres,
gimp_image_get_unit (image));
}
}
static void static void
gimp_image_real_size_changed_detailed (GimpImage *image, gimp_image_real_size_changed_detailed (GimpImage *image,
gint previous_origin_x, gint previous_origin_x,
@ -1198,6 +1266,22 @@ gimp_image_real_size_changed_detailed (GimpImage *image,
gimp_viewable_size_changed (GIMP_VIEWABLE (image)); gimp_viewable_size_changed (GIMP_VIEWABLE (image));
} }
static void
gimp_image_real_unit_changed (GimpImage *image)
{
GimpMetadata *metadata;
metadata = gimp_image_get_metadata (image);
if (metadata)
{
gdouble xres, yres;
gimp_image_get_resolution (image, &xres, &yres);
gimp_metadata_set_resolution (metadata, xres, yres,
gimp_image_get_unit (image));
}
}
static void static void
gimp_image_real_colormap_changed (GimpImage *image, gimp_image_real_colormap_changed (GimpImage *image,
gint color_index) gint color_index)

View file

@ -35,6 +35,7 @@
#include "gimpimage.h" #include "gimpimage.h"
#include "gimpimage-colormap.h" #include "gimpimage-colormap.h"
#include "gimpimage-grid.h" #include "gimpimage-grid.h"
#include "gimpimage-metadata.h"
#include "gimpimage-private.h" #include "gimpimage-private.h"
#include "gimpimageundo.h" #include "gimpimageundo.h"
#include "gimpparasitelist.h" #include "gimpparasitelist.h"
@ -186,6 +187,11 @@ gimp_image_undo_constructed (GObject *object)
GIMP_IMAGE_COLORMAP_SIZE); GIMP_IMAGE_COLORMAP_SIZE);
break; break;
case GIMP_UNDO_IMAGE_METADATA:
image_undo->metadata =
gimp_metadata_duplicate (gimp_image_get_metadata (image));
break;
case GIMP_UNDO_PARASITE_ATTACH: case GIMP_UNDO_PARASITE_ATTACH:
case GIMP_UNDO_PARASITE_REMOVE: case GIMP_UNDO_PARASITE_REMOVE:
g_assert (image_undo->parasite_name != NULL); g_assert (image_undo->parasite_name != NULL);
@ -284,6 +290,9 @@ gimp_image_undo_get_memsize (GimpObject *object,
if (image_undo->colormap) if (image_undo->colormap)
memsize += GIMP_IMAGE_COLORMAP_SIZE; memsize += GIMP_IMAGE_COLORMAP_SIZE;
if (image_undo->metadata)
memsize += gimp_g_object_get_memsize (G_OBJECT (image_undo->metadata));
memsize += gimp_object_get_memsize (GIMP_OBJECT (image_undo->grid), memsize += gimp_object_get_memsize (GIMP_OBJECT (image_undo->grid),
gui_size); gui_size);
memsize += gimp_string_get_memsize (image_undo->parasite_name); memsize += gimp_string_get_memsize (image_undo->parasite_name);
@ -447,6 +456,20 @@ gimp_image_undo_pop (GimpUndo *undo,
} }
break; break;
case GIMP_UNDO_IMAGE_METADATA:
{
GimpMetadata *metadata;
metadata = gimp_metadata_duplicate (gimp_image_get_metadata (image));
gimp_image_set_metadata (image, image_undo->metadata, FALSE);
if (image_undo->metadata)
g_object_unref (image_undo->metadata);
image_undo->metadata = metadata;
}
break;
case GIMP_UNDO_PARASITE_ATTACH: case GIMP_UNDO_PARASITE_ATTACH:
case GIMP_UNDO_PARASITE_REMOVE: case GIMP_UNDO_PARASITE_REMOVE:
{ {
@ -495,6 +518,12 @@ gimp_image_undo_free (GimpUndo *undo,
image_undo->colormap = NULL; image_undo->colormap = NULL;
} }
if (image_undo->metadata)
{
g_free (image_undo->metadata);
image_undo->metadata = NULL;
}
if (image_undo->parasite_name) if (image_undo->parasite_name)
{ {
g_free (image_undo->parasite_name); g_free (image_undo->parasite_name);

View file

@ -50,6 +50,7 @@ struct _GimpImageUndo
GimpGrid *grid; GimpGrid *grid;
gint num_colors; gint num_colors;
guchar *colormap; guchar *colormap;
GimpMetadata *metadata;
gchar *parasite_name; gchar *parasite_name;
GimpParasite *parasite; GimpParasite *parasite;
}; };

View file

@ -42,6 +42,7 @@
#include "core/gimpimage-duplicate.h" #include "core/gimpimage-duplicate.h"
#include "core/gimpimage-flip.h" #include "core/gimpimage-flip.h"
#include "core/gimpimage-merge.h" #include "core/gimpimage-merge.h"
#include "core/gimpimage-metadata.h"
#include "core/gimpimage-pick-color.h" #include "core/gimpimage-pick-color.h"
#include "core/gimpimage-pick-layer.h" #include "core/gimpimage-pick-layer.h"
#include "core/gimpimage-resize.h" #include "core/gimpimage-resize.h"
@ -1706,6 +1707,67 @@ image_set_colormap_invoker (GimpProcedure *procedure,
error ? *error : NULL); error ? *error : NULL);
} }
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, TRUE);
if (metadata)
g_object_unref (metadata);
}
return gimp_procedure_get_return_values (procedure, success,
error ? *error : NULL);
}
static GimpValueArray * static GimpValueArray *
image_clean_all_invoker (GimpProcedure *procedure, image_clean_all_invoker (GimpProcedure *procedure,
Gimp *gimp, Gimp *gimp,
@ -4553,6 +4615,66 @@ register_image_procs (GimpPDB *pdb)
gimp_pdb_register_procedure (pdb, procedure); gimp_pdb_register_procedure (pdb, procedure);
g_object_unref (procedure); 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 * gimp-image-clean-all
*/ */

View file

@ -28,7 +28,7 @@
#include "internal-procs.h" #include "internal-procs.h"
/* 701 procedures registered total */ /* 703 procedures registered total */
void void
internal_procs_init (GimpPDB *pdb) internal_procs_init (GimpPDB *pdb)

View file

@ -22,6 +22,7 @@
#include <pango/pango.h> #include <pango/pango.h>
#include <pango/pangoft2.h> #include <pango/pangoft2.h>
#include <gdk-pixbuf/gdk-pixbuf.h> #include <gdk-pixbuf/gdk-pixbuf.h>
#include <gexiv2/gexiv2.h>
#include <gegl.h> #include <gegl.h>
#include "libgimpbase/gimpbase.h" #include "libgimpbase/gimpbase.h"
@ -38,6 +39,7 @@ static gchar * sanity_check_pango (void);
static gchar * sanity_check_fontconfig (void); static gchar * sanity_check_fontconfig (void);
static gchar * sanity_check_freetype (void); static gchar * sanity_check_freetype (void);
static gchar * sanity_check_gdk_pixbuf (void); static gchar * sanity_check_gdk_pixbuf (void);
static gchar * sanity_check_gexiv2 (void);
static gchar * sanity_check_babl (void); static gchar * sanity_check_babl (void);
static gchar * sanity_check_gegl (void); static gchar * sanity_check_gegl (void);
static gchar * sanity_check_gegl_ops (void); static gchar * sanity_check_gegl_ops (void);
@ -69,6 +71,9 @@ sanity_check (void)
if (! abort_message) if (! abort_message)
abort_message = sanity_check_gdk_pixbuf (); abort_message = sanity_check_gdk_pixbuf ();
if (! abort_message)
abort_message = sanity_check_gexiv2 ();
if (! abort_message) if (! abort_message)
abort_message = sanity_check_babl (); abort_message = sanity_check_babl ();
@ -346,6 +351,46 @@ sanity_check_gdk_pixbuf (void)
return NULL; return NULL;
} }
static gchar *
sanity_check_gexiv2 (void)
{
#ifdef GEXIV2_MAJOR_VERSION
#define GEXIV2_REQUIRED_MAJOR 0
#define GEXIV2_REQUIRED_MINOR 7
#define GEXIV2_REQUIRED_MICRO 0
gint gexiv2_version = gexiv2_get_version ();
if (gexiv2_version < (GEXIV2_REQUIRED_MAJOR * 100 * 100 +
GEXIV2_REQUIRED_MINOR * 100 +
GEXIV2_REQUIRED_MICRO))
{
const gint gexiv2_major_version = gexiv2_version / 100 / 100;
const gint gexiv2_minor_version = gexiv2_version / 100 % 100;
const gint gexiv2_micro_version = gexiv2_version % 100;
return g_strdup_printf
("gexiv2 version too old!\n\n"
"GIMP requires gexiv2 version %d.%d.%d or later.\n"
"Installed gexiv2 version is %d.%d.%d.\n\n"
"Somehow you or your software packager managed\n"
"to install GIMP with an older gexiv2 version.\n\n"
"Please upgrade to gexiv2 version %d.%d.%d or later.",
GEXIV2_REQUIRED_MAJOR, GEXIV2_REQUIRED_MINOR, GEXIV2_REQUIRED_MICRO,
gexiv2_major_version, gexiv2_minor_version, gexiv2_micro_version,
GEXIV2_REQUIRED_MAJOR, GEXIV2_REQUIRED_MINOR, GEXIV2_REQUIRED_MICRO);
}
#undef GEXIV2_REQUIRED_MAJOR
#undef GEXIV2_REQUIRED_MINOR
#undef GEXIV2_REQUIRED_MICRO
#endif
return NULL;
}
static gchar * static gchar *
sanity_check_babl (void) sanity_check_babl (void)
{ {

View file

@ -124,7 +124,8 @@ LDADD = \
$(PANGOCAIRO_LIBS) \ $(PANGOCAIRO_LIBS) \
$(CAIRO_LIBS) \ $(CAIRO_LIBS) \
$(GEGL_LIBS) \ $(GEGL_LIBS) \
$(GLIB_LIBS) \ $(GIO_LIBS) \
$(GEXIV2_LIBS) \
$(INTLLIBS) \ $(INTLLIBS) \
$(RT_LIBS) $(RT_LIBS)

View file

@ -41,6 +41,7 @@
#include "core/gimpimage-colormap.h" #include "core/gimpimage-colormap.h"
#include "core/gimpimage-grid.h" #include "core/gimpimage-grid.h"
#include "core/gimpimage-guides.h" #include "core/gimpimage-guides.h"
#include "core/gimpimage-metadata.h"
#include "core/gimpimage-private.h" #include "core/gimpimage-private.h"
#include "core/gimpimage-sample-points.h" #include "core/gimpimage-sample-points.h"
#include "core/gimpimage-undo.h" #include "core/gimpimage-undo.h"
@ -140,6 +141,7 @@ xcf_load_image (Gimp *gimp,
{ {
GimpImage *image = NULL; GimpImage *image = NULL;
const GimpParasite *parasite; const GimpParasite *parasite;
gboolean has_metadata = FALSE;
guint32 saved_pos; guint32 saved_pos;
guint32 offset; guint32 offset;
gint width; gint width;
@ -208,6 +210,124 @@ xcf_load_image (Gimp *gimp,
} }
} }
/* check for a metadata parasite */
parasite = gimp_image_parasite_find (GIMP_IMAGE (image),
"gimp-image-metadata");
if (parasite)
{
GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image);
GimpMetadata *metadata;
const gchar *meta_string;
meta_string = (gchar *) gimp_parasite_data (parasite);
metadata = gimp_metadata_deserialize (meta_string);
if (metadata)
{
has_metadata = TRUE;
gimp_image_set_metadata (image, metadata, FALSE);
g_object_unref (metadata);
}
gimp_parasite_list_remove (private->parasites,
gimp_parasite_name (parasite));
}
/* migrate the old "exif-data" parasite */
parasite = gimp_image_parasite_find (GIMP_IMAGE (image),
"exif-data");
if (parasite)
{
GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image);
if (has_metadata)
{
g_printerr ("xcf-load: inconsistent metadata discovered: XCF file "
"has both 'gimp-image-metadata' and 'exif-data' "
"parasites, dropping old 'exif-data'\n");
}
else
{
GimpMetadata *metadata = gimp_image_get_metadata (image);
GError *my_error = NULL;
if (metadata)
g_object_ref (metadata);
else
metadata = gimp_metadata_new ();
if (! gimp_metadata_set_from_exif (metadata,
gimp_parasite_data (parasite),
gimp_parasite_data_size (parasite),
&my_error))
{
gimp_message (gimp, G_OBJECT (info->progress),
GIMP_MESSAGE_WARNING,
_("Corrupt 'exif-data' parasite discovered.\n"
"EXIF data could not be migrated: %s"),
my_error->message);
g_clear_error (&my_error);
}
else
{
gimp_image_set_metadata (image, metadata, FALSE);
}
g_object_unref (metadata);
}
gimp_parasite_list_remove (private->parasites,
gimp_parasite_name (parasite));
}
/* migrate the old "gimp-metadata" parasite */
parasite = gimp_image_parasite_find (GIMP_IMAGE (image),
"gimp-metadata");
if (parasite)
{
GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image);
if (has_metadata)
{
g_printerr ("xcf-load: inconsistent metadata discovered: XCF file "
"has both 'gimp-image-metadata' and 'gimp-metadata' "
"parasites, dropping old 'gimp-metadata'\n");
}
else
{
GimpMetadata *metadata = gimp_image_get_metadata (image);
GError *my_error = NULL;
if (metadata)
g_object_ref (metadata);
else
metadata = gimp_metadata_new ();
if (! gimp_metadata_set_from_xmp (metadata,
gimp_parasite_data (parasite),
gimp_parasite_data_size (parasite),
&my_error))
{
gimp_message (gimp, G_OBJECT (info->progress),
GIMP_MESSAGE_WARNING,
_("Corrupt 'gimp-metadata' parasite discovered.\n"
"XMP data could not be migrated: %s"),
my_error->message);
g_clear_error (&my_error);
}
else
{
gimp_image_set_metadata (image, metadata, FALSE);
}
g_object_unref (metadata);
}
gimp_parasite_list_remove (private->parasites,
gimp_parasite_name (parasite));
}
xcf_progress_update (info); xcf_progress_update (info);
while (TRUE) while (TRUE)

View file

@ -41,6 +41,7 @@
#include "core/gimpimage-colormap.h" #include "core/gimpimage-colormap.h"
#include "core/gimpimage-grid.h" #include "core/gimpimage-grid.h"
#include "core/gimpimage-guides.h" #include "core/gimpimage-guides.h"
#include "core/gimpimage-metadata.h"
#include "core/gimpimage-private.h" #include "core/gimpimage-private.h"
#include "core/gimpimage-sample-points.h" #include "core/gimpimage-sample-points.h"
#include "core/gimplayer.h" #include "core/gimplayer.h"
@ -390,9 +391,10 @@ xcf_save_image_props (XcfInfo *info,
GimpImage *image, GimpImage *image,
GError **error) GError **error)
{ {
GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image); GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image);
GimpParasite *parasite = NULL; GimpParasite *grid_parasite = NULL;
GimpUnit unit = gimp_image_get_unit (image); GimpParasite *meta_parasite = NULL;
GimpUnit unit = gimp_image_get_unit (image);
gdouble xres; gdouble xres;
gdouble yres; gdouble yres;
@ -440,8 +442,26 @@ xcf_save_image_props (XcfInfo *info,
{ {
GimpGrid *grid = gimp_image_get_grid (image); GimpGrid *grid = gimp_image_get_grid (image);
parasite = gimp_grid_to_parasite (grid); grid_parasite = gimp_grid_to_parasite (grid);
gimp_parasite_list_add (private->parasites, parasite); gimp_parasite_list_add (private->parasites, grid_parasite);
}
if (gimp_image_get_metadata (image))
{
GimpMetadata *metadata = gimp_image_get_metadata (image);
gchar *meta_string;
meta_string = gimp_metadata_serialize (metadata);
if (meta_string)
{
meta_parasite = gimp_parasite_new ("gimp-image-metadata",
GIMP_PARASITE_PERSISTENT,
strlen (meta_string) + 1,
meta_string);
gimp_parasite_list_add (private->parasites, meta_parasite);
g_free (meta_string);
}
} }
if (gimp_parasite_list_length (private->parasites) > 0) if (gimp_parasite_list_length (private->parasites) > 0)
@ -450,11 +470,18 @@ xcf_save_image_props (XcfInfo *info,
private->parasites)); private->parasites));
} }
if (parasite) if (grid_parasite)
{ {
gimp_parasite_list_remove (private->parasites, gimp_parasite_list_remove (private->parasites,
gimp_parasite_name (parasite)); gimp_parasite_name (grid_parasite));
gimp_parasite_free (parasite); gimp_parasite_free (grid_parasite);
}
if (meta_parasite)
{
gimp_parasite_list_remove (private->parasites,
gimp_parasite_name (meta_parasite));
gimp_parasite_free (meta_parasite);
} }
xcf_check_error (xcf_save_prop (info, image, PROP_END, error)); xcf_check_error (xcf_save_prop (info, image, PROP_END, error));

View file

@ -59,7 +59,7 @@ m4_define([pygtk_required_version], [2.10.4])
m4_define([poppler_required_version], [0.12.4]) m4_define([poppler_required_version], [0.12.4])
m4_define([libcurl_required_version], [7.15.1]) m4_define([libcurl_required_version], [7.15.1])
m4_define([libgudev_required_version], [167]) 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([lcms_required_version], [2.2])
m4_define([libpng_required_version], [1.2.37]) m4_define([libpng_required_version], [1.2.37])
m4_define([liblzma_required_version], [5.0.0]) m4_define([liblzma_required_version], [5.0.0])
@ -123,11 +123,13 @@ GDK_PIXBUF_REQUIRED_VERSION=gdk_pixbuf_required_version
GTK_REQUIRED_VERSION=gtk_required_version GTK_REQUIRED_VERSION=gtk_required_version
CAIRO_REQUIRED_VERSION=cairo_required_version CAIRO_REQUIRED_VERSION=cairo_required_version
GEGL_REQUIRED_VERSION=gegl_required_version GEGL_REQUIRED_VERSION=gegl_required_version
GEXIV2_REQUIRED_VERSION=gexiv2_required_version
AC_SUBST(GLIB_REQUIRED_VERSION) AC_SUBST(GLIB_REQUIRED_VERSION)
AC_SUBST(GDK_PIXBUF_REQUIRED_VERSION) AC_SUBST(GDK_PIXBUF_REQUIRED_VERSION)
AC_SUBST(GTK_REQUIRED_VERSION) AC_SUBST(GTK_REQUIRED_VERSION)
AC_SUBST(CAIRO_REQUIRED_VERSION) AC_SUBST(CAIRO_REQUIRED_VERSION)
AC_SUBST(GEGL_REQUIRED_VERSION) AC_SUBST(GEGL_REQUIRED_VERSION)
AC_SUBST(GEXIV2_REQUIRED_VERSION)
# The symbol GIMP_UNSTABLE is defined above for substitution in # The symbol GIMP_UNSTABLE is defined above for substitution in
# Makefiles and conditionally defined here as a preprocessor symbol # Makefiles and conditionally defined here as a preprocessor symbol
@ -607,6 +609,7 @@ if test "x$FREETYPE_CONFIG" != "xno" ; then
fi fi
AC_SUBST(FREETYPE_LIBS) AC_SUBST(FREETYPE_LIBS)
PKG_CHECK_MODULES(GEXIV2, gexiv2 >= gexiv2_required_version)
########################################## ##########################################
# Check for some special functions we need # Check for some special functions we need
@ -1327,28 +1330,6 @@ AC_SUBST(MNG_LIBS)
AC_SUBST(MNG_CFLAGS) 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 # Check for libaa
################# #################
@ -2406,9 +2387,6 @@ Optional Plug-Ins:
X11 Mouse Cursor: $have_xmc X11 Mouse Cursor: $have_xmc
XPM: $have_libxpm XPM: $have_libxpm
Plug-In Features:
EXIF support: $have_libexif
Optional Modules: Optional Modules:
ALSA (MIDI Input): $have_alsa ALSA (MIDI Input): $have_alsa
Linux Input: $have_linux_input (GUdev support: $have_libgudev) Linux Input: $have_linux_input (GUdev support: $have_libgudev)

View file

@ -68,6 +68,7 @@ AM_CPPFLAGS = \
-I$(top_srcdir) \ -I$(top_srcdir) \
$(GTK_CFLAGS) \ $(GTK_CFLAGS) \
$(GEGL_CFLAGS) \ $(GEGL_CFLAGS) \
$(GEXIV2_CFLAGS) \
-I$(includedir) \ -I$(includedir) \
$(xobjective_c) $(xobjective_c)
@ -280,6 +281,8 @@ libgimpui_sources = \
gimpitemcombobox.h \ gimpitemcombobox.h \
gimpmenu.c \ gimpmenu.c \
gimpmenu.h \ gimpmenu.h \
gimpmetadata.c \
gimpmetadats.h \
gimppalettemenu.c \ gimppalettemenu.c \
gimppalettemenu.h \ gimppalettemenu.h \
gimppaletteselectbutton.c \ gimppaletteselectbutton.c \
@ -354,6 +357,7 @@ gimpinclude_HEADERS = \
gimpimagecombobox.h \ gimpimagecombobox.h \
gimpitemcombobox.h \ gimpitemcombobox.h \
gimpmenu.h \ gimpmenu.h \
gimpmetadata.h \
gimppalettemenu.h \ gimppalettemenu.h \
gimppaletteselectbutton.h \ gimppaletteselectbutton.h \
gimppatternmenu.h \ gimppatternmenu.h \
@ -399,6 +403,7 @@ libgimpui_@GIMP_API_VERSION@_la_LIBADD = \
$(libgimpbase) \ $(libgimpbase) \
$(libgimpmodule) \ $(libgimpmodule) \
$(GTK_LIBS) \ $(GTK_LIBS) \
$(GEXIV2_LIBS) \
$(RT_LIBS) $(RT_LIBS)
libgimpui_@GIMP_API_VERSION@_la_DEPENDENCIES = \ libgimpui_@GIMP_API_VERSION@_la_DEPENDENCIES = \

View file

@ -403,6 +403,7 @@ EXPORTS
gimp_image_get_layer_by_tattoo gimp_image_get_layer_by_tattoo
gimp_image_get_layer_position gimp_image_get_layer_position
gimp_image_get_layers gimp_image_get_layers
gimp_image_get_metadata
gimp_image_get_name gimp_image_get_name
gimp_image_get_parasite gimp_image_get_parasite
gimp_image_get_parasite_list gimp_image_get_parasite_list
@ -484,6 +485,7 @@ EXPORTS
gimp_image_set_component_active gimp_image_set_component_active
gimp_image_set_component_visible gimp_image_set_component_visible
gimp_image_set_filename gimp_image_set_filename
gimp_image_set_metadata
gimp_image_set_resolution gimp_image_set_resolution
gimp_image_set_tattoo_state gimp_image_set_tattoo_state
gimp_image_set_unit gimp_image_set_unit

View file

@ -100,6 +100,66 @@ gimp_image_get_thumbnail_data (gint32 image_ID,
return image_data; return image_data;
} }
/**
* 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: * gimp_image_get_cmap:
* @image_ID: The image. * @image_ID: The image.

View file

@ -41,6 +41,10 @@ guchar * gimp_image_get_thumbnail_data (gint32 image_ID,
gint *height, gint *height,
gint *bpp); 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) GIMP_DEPRECATED_FOR(gimp_image_get_colormap)
guchar * gimp_image_get_cmap (gint32 image_ID, guchar * gimp_image_get_cmap (gint32 image_ID,
gint *num_colors); gint *num_colors);

View file

@ -1799,6 +1799,68 @@ _gimp_image_set_colormap (gint32 image_ID,
return success; return success;
} }
/**
* _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: * gimp_image_clean_all:
* @image_ID: The image. * @image_ID: The image.

View file

@ -148,6 +148,9 @@ G_GNUC_INTERNAL guint8* _gimp_image_get_colormap (gint32
G_GNUC_INTERNAL gboolean _gimp_image_set_colormap (gint32 image_ID, G_GNUC_INTERNAL gboolean _gimp_image_set_colormap (gint32 image_ID,
gint num_bytes, gint num_bytes,
const guint8 *colormap); 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_clean_all (gint32 image_ID);
gboolean gimp_image_is_dirty (gint32 image_ID); gboolean gimp_image_is_dirty (gint32 image_ID);
G_GNUC_INTERNAL gboolean _gimp_image_thumbnail (gint32 image_ID, G_GNUC_INTERNAL gboolean _gimp_image_thumbnail (gint32 image_ID,

640
libgimp/gimpmetadata.c Normal file
View file

@ -0,0 +1,640 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-2000 Peter Mattis and Spencer Kimball
*
* gimpmetadata.c
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <string.h>
#include <gtk/gtk.h>
#include <gexiv2/gexiv2.h>
#include "gimp.h"
#include "gimpui.h"
#include "gimpmetadata.h"
#include "libgimp-intl.h"
static void gimp_image_metadata_rotate (gint32 image_ID,
GExiv2Orientation orientation);
static void gimp_image_metadata_rotate_query (gint32 image_ID,
const gchar *mime_type,
GimpMetadata *metadata,
gboolean interactive);
static gboolean gimp_image_metadata_rotate_dialog (gint32 image_ID,
const gchar *parasite_name);
/* public functions */
void
gimp_image_metadata_load (gint32 image_ID,
const gchar *mime_type,
GFile *file,
gboolean interactive)
{
GimpMetadata *metadata;
g_return_if_fail (image_ID > 0);
g_return_if_fail (mime_type != NULL);
g_return_if_fail (G_IS_FILE (file));
metadata = gimp_metadata_load_from_file (file, NULL);
if (metadata)
{
gchar *comment;
gdouble xres;
gdouble yres;
GimpUnit unit;
#if 0
{
gchar *xml = gimp_metadata_serialize (metadata);
GimpMetadata *new = gimp_metadata_deserialize (xml);
gchar *xml2 = gimp_metadata_serialize (new);
FILE *f = fopen ("/tmp/gimp-test-xml1", "w");
fprintf (f, "%s", xml);
fclose (f);
f = fopen ("/tmp/gimp-test-xml2", "w");
fprintf (f, "%s", xml2);
fclose (f);
system ("diff -u /tmp/gimp-test-xml1 /tmp/gimp-test-xml2");
g_free (xml);
g_free (xml2);
g_object_unref (new);
}
#endif
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_attach_parasite (image_ID, parasite);
gimp_parasite_free (parasite);
}
if (gimp_metadata_get_resolution (metadata, &xres, &yres, &unit))
{
gimp_image_set_resolution (image_ID, xres, yres);
gimp_image_set_unit (image_ID, unit);
}
gimp_image_metadata_rotate_query (image_ID, mime_type,
metadata, interactive);
gexiv2_metadata_erase_exif_thumbnail (metadata);
gimp_image_set_metadata (image_ID, metadata);
g_object_unref (metadata);
}
}
GimpMetadata *
gimp_image_metadata_save_prepare (gint32 image_ID,
const gchar *mime_type)
{
GimpMetadata *metadata;
g_return_val_if_fail (image_ID > 0, NULL);
g_return_val_if_fail (mime_type != NULL, NULL);
metadata = gimp_image_get_metadata (image_ID);
if (metadata)
{
GDateTime *datetime;
const GimpParasite *comment_parasite;
const gchar *comment = NULL;
gint image_width;
gint image_height;
gdouble xres;
gdouble yres;
gchar buffer[32];
image_width = gimp_image_width (image_ID);
image_height = gimp_image_height (image_ID);
datetime = g_date_time_new_now_local ();
comment_parasite = gimp_image_get_parasite (image_ID, "gimp-comment");
if (comment_parasite)
comment = gimp_parasite_data (comment_parasite);
/* EXIF */
if (comment)
{
gexiv2_metadata_set_tag_string (metadata,
"Exif.Photo.UserComment",
comment);
gexiv2_metadata_set_tag_string (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 (metadata,
"Exif.Image.DateTime",
buffer);
gexiv2_metadata_set_tag_string (metadata,
"Exif.Image.Software",
PACKAGE_STRING);
gimp_metadata_set_pixel_size (metadata,
image_width, image_height);
gimp_image_get_resolution (image_ID, &xres, &yres);
gimp_metadata_set_resolution (metadata, xres, yres,
gimp_image_get_unit (image_ID));
/* XMP */
gexiv2_metadata_set_tag_string (metadata,
"Xmp.dc.Format",
mime_type);
if (! g_strcmp0 (mime_type, "image/tiff"))
{
/* TIFF specific XMP data */
g_snprintf (buffer, sizeof (buffer), "%d", image_width);
gexiv2_metadata_set_tag_string (metadata,
"Xmp.tiff.ImageWidth",
buffer);
g_snprintf (buffer, sizeof (buffer), "%d", image_height);
gexiv2_metadata_set_tag_string (metadata,
"Xmp.tiff.ImageLength",
buffer);
g_snprintf (buffer, sizeof (buffer),
"%d:%02d:%02d %02d:%02d:%02d",
g_date_time_get_year (datetime),
g_date_time_get_month (datetime),
g_date_time_get_day_of_month (datetime),
g_date_time_get_hour (datetime),
g_date_time_get_minute (datetime),
g_date_time_get_second (datetime));
gexiv2_metadata_set_tag_string (metadata,
"Xmp.tiff.DateTime",
buffer);
}
/* IPTC */
g_snprintf (buffer, sizeof (buffer),
"%d-%d-%d",
g_date_time_get_year (datetime),
g_date_time_get_month (datetime),
g_date_time_get_day_of_month (datetime));
gexiv2_metadata_set_tag_string (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 (metadata,
"Iptc.Application2.TimeCreated",
buffer);
g_date_time_unref (datetime);
}
return metadata;
}
gboolean
gimp_image_metadata_save_finish (gint32 image_ID,
const gchar *mime_type,
GimpMetadata *metadata,
GFile *file,
GimpMetadataSaveFlags flags,
GError **error)
{
GExiv2Metadata *new_metadata;
gboolean support_exif;
gboolean support_xmp;
gboolean support_iptc;
gchar *value;
gboolean success = FALSE;
gint i;
g_return_val_if_fail (image_ID > 0, FALSE);
g_return_val_if_fail (mime_type != NULL, FALSE);
g_return_val_if_fail (GEXIV2_IS_METADATA (metadata), FALSE);
g_return_val_if_fail (G_IS_FILE (file), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
/* read metadata from saved file */
new_metadata = gimp_metadata_load_from_file (file, error);
if (! new_metadata)
return FALSE;
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);
if ((flags & GIMP_METADATA_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 ((flags & GIMP_METADATA_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);
}
if ((flags & GIMP_METADATA_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);
}
if (flags & GIMP_METADATA_SAVE_THUMBNAIL)
{
GdkPixbuf *thumb_pixbuf;
gchar *thumb_buffer;
gint image_width;
gint image_height;
gsize count;
gint thumbw;
gint thumbh;
#define EXIF_THUMBNAIL_SIZE 256
image_width = gimp_image_width (image_ID);
image_height = gimp_image_height (image_ID);
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_image_get_thumbnail (image_ID, thumbw, thumbh,
GIMP_PIXBUF_KEEP_ALPHA);
if (gdk_pixbuf_save_to_buffer (thumb_pixbuf, &thumb_buffer, &count,
"jpeg", NULL,
"quality", "75",
NULL))
{
gchar buffer[32];
gexiv2_metadata_set_exif_thumbnail_from_buffer (new_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);
}
g_object_unref (thumb_pixbuf);
}
success = gimp_metadata_save_to_file (new_metadata, file, error);
g_object_unref (new_metadata);
return success;
}
/* private functions */
static void
gimp_image_metadata_rotate (gint32 image_ID,
GExiv2Orientation orientation)
{
switch (orientation)
{
case GEXIV2_ORIENTATION_UNSPECIFIED:
case GEXIV2_ORIENTATION_NORMAL: /* standard orientation, do nothing */
break;
case GEXIV2_ORIENTATION_HFLIP:
gimp_image_flip (image_ID, GIMP_ORIENTATION_HORIZONTAL);
break;
case GEXIV2_ORIENTATION_ROT_180:
gimp_image_rotate (image_ID, GIMP_ROTATE_180);
break;
case GEXIV2_ORIENTATION_VFLIP:
gimp_image_flip (image_ID, GIMP_ORIENTATION_VERTICAL);
break;
case GEXIV2_ORIENTATION_ROT_90_HFLIP: /* flipped diagonally around '\' */
gimp_image_rotate (image_ID, GIMP_ROTATE_90);
gimp_image_flip (image_ID, GIMP_ORIENTATION_HORIZONTAL);
break;
case GEXIV2_ORIENTATION_ROT_90: /* 90 CW */
gimp_image_rotate (image_ID, GIMP_ROTATE_90);
break;
case GEXIV2_ORIENTATION_ROT_90_VFLIP: /* flipped diagonally around '/' */
gimp_image_rotate (image_ID, GIMP_ROTATE_90);
gimp_image_flip (image_ID, GIMP_ORIENTATION_VERTICAL);
break;
case GEXIV2_ORIENTATION_ROT_270: /* 90 CCW */
gimp_image_rotate (image_ID, GIMP_ROTATE_270);
break;
default: /* shouldn't happen */
break;
}
}
static void
gimp_image_metadata_rotate_query (gint32 image_ID,
const gchar *mime_type,
GimpMetadata *metadata,
gboolean interactive)
{
GimpParasite *parasite;
gchar *parasite_name;
GExiv2Orientation orientation;
gboolean query = interactive;
orientation = gexiv2_metadata_get_orientation (metadata);
if (orientation <= GEXIV2_ORIENTATION_NORMAL ||
orientation > GEXIV2_ORIENTATION_MAX)
return;
parasite_name = g_strdup_printf ("gimp-metadata-exif-rotate(%s)", mime_type);
parasite = gimp_get_parasite (parasite_name);
if (parasite)
{
if (strncmp (gimp_parasite_data (parasite), "yes",
gimp_parasite_data_size (parasite)) == 0)
{
query = FALSE;
}
else if (strncmp (gimp_parasite_data (parasite), "no",
gimp_parasite_data_size (parasite)) == 0)
{
gimp_parasite_free (parasite);
g_free (parasite_name);
return;
}
gimp_parasite_free (parasite);
}
if (query && ! gimp_image_metadata_rotate_dialog (image_ID,
parasite_name))
{
g_free (parasite_name);
return;
}
g_free (parasite_name);
gimp_image_metadata_rotate (image_ID, orientation);
gexiv2_metadata_set_tag_string (metadata,
"Exif.Image.Orientation", "1");
gexiv2_metadata_set_tag_string (metadata,
"Xmp.tiff.Orientation", "1");
}
static gboolean
gimp_image_metadata_rotate_dialog (gint32 image_ID,
const gchar *parasite_name)
{
GtkWidget *dialog;
GtkWidget *hbox;
GtkWidget *vbox;
GtkWidget *label;
GtkWidget *toggle;
GdkPixbuf *pixbuf;
gint response;
dialog = gimp_dialog_new (_("Rotate Image?"), "gimp-metadata-rotate-dialog",
NULL, 0, NULL, NULL,
_("_Keep Orientation"), GTK_RESPONSE_CANCEL,
GIMP_STOCK_TOOL_ROTATE, GTK_RESPONSE_OK,
NULL);
gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
GTK_RESPONSE_OK,
GTK_RESPONSE_CANCEL,
-1);
gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
gimp_window_set_transient (GTK_WINDOW (dialog));
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
gtk_container_set_border_width (GTK_CONTAINER (hbox), 12);
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
hbox, FALSE, FALSE, 0);
gtk_widget_show (hbox);
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
gtk_widget_show (vbox);
#define THUMBNAIL_SIZE 128
pixbuf = gimp_image_get_thumbnail (image_ID,
THUMBNAIL_SIZE, THUMBNAIL_SIZE,
GIMP_PIXBUF_SMALL_CHECKS);
if (pixbuf)
{
GtkWidget *image;
gchar *name;
image = gtk_image_new_from_pixbuf (pixbuf);
g_object_unref (pixbuf);
gtk_box_pack_start (GTK_BOX (vbox), image, FALSE, FALSE, 0);
gtk_widget_show (image);
name = gimp_image_get_name (image_ID);
label = gtk_label_new (name);
gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_MIDDLE);
gimp_label_set_attributes (GTK_LABEL (label),
PANGO_ATTR_STYLE, PANGO_STYLE_ITALIC,
-1);
gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
gtk_widget_show (label);
g_free (name);
}
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
gtk_widget_show (vbox);
label = g_object_new (GTK_TYPE_LABEL,
"label", _("According to the EXIF data, "
"this image is rotated."),
"wrap", TRUE,
"justify", GTK_JUSTIFY_LEFT,
"xalign", 0.0,
"yalign", 0.5,
NULL);
gimp_label_set_attributes (GTK_LABEL (label),
PANGO_ATTR_SCALE, PANGO_SCALE_LARGE,
PANGO_ATTR_WEIGHT, PANGO_WEIGHT_BOLD,
-1);
gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
gtk_widget_show (label);
label = g_object_new (GTK_TYPE_LABEL,
"label", _("Would you like GIMP to rotate it "
"into the standard orientation?"),
"wrap", TRUE,
"justify", GTK_JUSTIFY_LEFT,
"xalign", 0.0,
"yalign", 0.5,
NULL);
gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
gtk_widget_show (label);
toggle = gtk_check_button_new_with_mnemonic (_("_Don't ask me again"));
gtk_box_pack_end (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), FALSE);
gtk_widget_show (toggle);
response = gimp_dialog_run (GIMP_DIALOG (dialog));
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (toggle)))
{
GimpParasite *parasite;
const gchar *str = (response == GTK_RESPONSE_OK) ? "yes" : "no";
parasite = gimp_parasite_new (parasite_name,
GIMP_PARASITE_PERSISTENT,
strlen (str), str);
gimp_attach_parasite (parasite);
gimp_parasite_free (parasite);
}
gtk_widget_destroy (dialog);
return (response == GTK_RESPONSE_OK);
}

48
libgimp/gimpmetadata.h Normal file
View file

@ -0,0 +1,48 @@
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-2000 Peter Mattis and Spencer Kimball
*
* gimpmetadata.h
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#if !defined (__GIMP_UI_H_INSIDE__) && !defined (GIMP_COMPILATION)
#error "Only <libgimp/gimpui.h> can be included directly."
#endif
#ifndef __LIBGIMP_GIMP_METADATA_H__
#define __LIBGIMP_GIMP_METADATA_H__
G_BEGIN_DECLS
/* For information look into the C source or the html documentation */
void gimp_image_metadata_load (gint32 image_ID,
const gchar *mime_type,
GFile *file,
gboolean interactive);
GimpMetadata * gimp_image_metadata_save_prepare (gint32 image_ID,
const gchar *mime_type);
gboolean gimp_image_metadata_save_finish (gint32 image_ID,
const gchar *mime_type,
GimpMetadata *metadata,
GFile *file,
GimpMetadataSaveFlags flags,
GError **error);
G_END_DECLS
#endif /* ___LIBGIMP_GIMP_METADATA_H__ */

View file

@ -38,6 +38,9 @@ EXPORTS
gimp_image_combo_box_get_type gimp_image_combo_box_get_type
gimp_image_combo_box_new gimp_image_combo_box_new
gimp_image_menu_new gimp_image_menu_new
gimp_image_metadata_load
gimp_image_metadata_save_prepare
gimp_image_metadata_save_finish
gimp_layer_combo_box_get_type gimp_layer_combo_box_get_type
gimp_layer_combo_box_new gimp_layer_combo_box_new
gimp_layer_menu_new gimp_layer_menu_new

View file

@ -29,6 +29,7 @@
#include <libgimp/gimpexport.h> #include <libgimp/gimpexport.h>
#include <libgimp/gimpmenu.h> #include <libgimp/gimpmenu.h>
#include <libgimp/gimpmetadata.h>
#include <libgimp/gimpaspectpreview.h> #include <libgimp/gimpaspectpreview.h>
#include <libgimp/gimpdrawablepreview.h> #include <libgimp/gimpdrawablepreview.h>
#include <libgimp/gimpbrushmenu.h> #include <libgimp/gimpbrushmenu.h>

View file

@ -61,6 +61,7 @@ AM_CPPFLAGS = \
-DGIMP_BASE_COMPILATION \ -DGIMP_BASE_COMPILATION \
-I$(top_srcdir) \ -I$(top_srcdir) \
$(GIO_CFLAGS) \ $(GIO_CFLAGS) \
$(GEXIV2_CFLAGS) \
$(BINRELOC_CFLAGS) \ $(BINRELOC_CFLAGS) \
-I$(includedir) \ -I$(includedir) \
$(xobjective_c) $(xobjective_c)
@ -101,6 +102,8 @@ libgimpbase_sources = \
gimpenv.h \ gimpenv.h \
gimpmemsize.c \ gimpmemsize.c \
gimpmemsize.h \ gimpmemsize.h \
gimpmetadata.c \
gimpmetadata.h \
gimpparasite.c \ gimpparasite.c \
gimpparasite.h \ gimpparasite.h \
gimpparasiteio.c \ gimpparasiteio.c \
@ -144,6 +147,7 @@ libgimpbaseinclude_HEADERS = \
gimpdatafiles.h \ gimpdatafiles.h \
gimpenv.h \ gimpenv.h \
gimpmemsize.h \ gimpmemsize.h \
gimpmetadata.h \
gimpparasite.h \ gimpparasite.h \
gimpparasiteio.h \ gimpparasiteio.h \
gimprectangle.h \ gimprectangle.h \
@ -152,7 +156,6 @@ libgimpbaseinclude_HEADERS = \
gimputils.h \ gimputils.h \
gimpvaluearray.h gimpvaluearray.h
libgimpbase_@GIMP_API_VERSION@_la_LDFLAGS = \ libgimpbase_@GIMP_API_VERSION@_la_LDFLAGS = \
-version-info $(LT_VERSION_INFO) \ -version-info $(LT_VERSION_INFO) \
$(no_undefined) \ $(no_undefined) \
@ -163,9 +166,9 @@ libgimpbase_@GIMP_API_VERSION@_la_DEPENDENCIES = $(gimpbase_def)
libgimpbase_@GIMP_API_VERSION@_la_LIBADD = \ libgimpbase_@GIMP_API_VERSION@_la_LIBADD = \
$(GIO_LIBS) \ $(GIO_LIBS) \
$(GEXIV2_LIBS) \
$(ole32_lib) $(ole32_lib)
install-data-local: install-ms-lib install-libtool-import-lib install-data-local: install-ms-lib install-libtool-import-lib
uninstall-local: uninstall-ms-lib uninstall-libtool-import-lib uninstall-local: uninstall-ms-lib uninstall-libtool-import-lib

View file

@ -50,6 +50,19 @@ EXPORTS
gimp_memsize_serialize gimp_memsize_serialize
gimp_memsize_to_string gimp_memsize_to_string
gimp_message_handler_type_get_type gimp_message_handler_type_get_type
gimp_metadata_deserialize
gimp_metadata_duplicate
gimp_metadata_get_resolution
gimp_metadata_is_tag_supported
gimp_metadata_load_from_file
gimp_metadata_new
gimp_metadata_save_to_file
gimp_metadata_serialize
gimp_metadata_set_bits_per_sample
gimp_metadata_set_from_exif
gimp_metadata_set_from_xmp
gimp_metadata_set_pixel_size
gimp_metadata_set_resolution
gimp_micro_version gimp_micro_version
gimp_minor_version gimp_minor_version
gimp_paint_application_mode_get_type gimp_paint_application_mode_get_type

View file

@ -29,6 +29,7 @@
#include <libgimpbase/gimpenv.h> #include <libgimpbase/gimpenv.h>
#include <libgimpbase/gimplimits.h> #include <libgimpbase/gimplimits.h>
#include <libgimpbase/gimpmemsize.h> #include <libgimpbase/gimpmemsize.h>
#include <libgimpbase/gimpmetadata.h>
#include <libgimpbase/gimpparasite.h> #include <libgimpbase/gimpparasite.h>
#include <libgimpbase/gimprectangle.h> #include <libgimpbase/gimprectangle.h>
#include <libgimpbase/gimpunit.h> #include <libgimpbase/gimpunit.h>

View file

@ -55,6 +55,8 @@ typedef struct _GimpValueArray GimpValueArray;
typedef void (* GimpDatafileLoaderFunc) (const GimpDatafileData *file_data, typedef void (* GimpDatafileLoaderFunc) (const GimpDatafileData *file_data,
gpointer user_data); gpointer user_data);
typedef struct _GExiv2Metadata GimpMetadata;
/** /**
* GimpEnumDesc: * GimpEnumDesc:

945
libgimpbase/gimpmetadata.c Normal file
View file

@ -0,0 +1,945 @@
/* 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 GQuark gimp_metadata_error_quark (void);
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 void gimp_metadata_add (GimpMetadata *src,
GimpMetadata *dest);
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 minimal_exif[] =
{
0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, 0x00, 0x01,
0x01, 0x01, 0x00, 0x5a, 0x00, 0x5a, 0x00, 0x00, 0xff, 0xe1
};
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);
GimpMetadata *
gimp_metadata_new (void)
{
GExiv2Metadata *metadata = NULL;
if (gexiv2_initialize ())
{
metadata = gexiv2_metadata_new ();
if (! gexiv2_metadata_open_buf (metadata, wilber_jpg, wilber_jpg_len,
NULL))
{
g_object_unref (metadata);
return NULL;
}
}
return metadata;
}
GimpMetadata *
gimp_metadata_duplicate (GimpMetadata *metadata)
{
GimpMetadata *new_metadata = NULL;
g_return_val_if_fail (metadata == NULL || GEXIV2_IS_METADATA (metadata), NULL);
if (metadata)
{
gchar *xml;
xml = gimp_metadata_serialize (metadata);
new_metadata = gimp_metadata_deserialize (xml);
g_free (xml);
}
return new_metadata;
}
typedef struct
{
gchar name[1024];
GimpMetadata *metadata;
} GimpMetadataParseData;
static const gchar*
gimp_metadata_attribute_name_to_value (const gchar **attribute_names,
const gchar **attribute_values,
const gchar *name)
{
while (*attribute_names)
{
if (! strcmp (*attribute_names, name))
{
return *attribute_values;
}
attribute_names++;
attribute_values++;
}
return NULL;
}
static void
gimp_metadata_deserialize_start_element (GMarkupParseContext *context,
const gchar *element_name,
const gchar **attribute_names,
const gchar **attribute_values,
gpointer user_data,
GError **error)
{
GimpMetadataParseData *parse_data = user_data;
if (! strcmp (element_name, "tag"))
{
const gchar *name;
name = gimp_metadata_attribute_name_to_value (attribute_names,
attribute_values,
"name");
if (! name)
{
g_set_error (error, gimp_metadata_error_quark (), 1001,
"Element 'tag' not contain required attribute 'name'.");
return;
}
strncpy (parse_data->name, name, sizeof (parse_data->name));
parse_data->name[sizeof (parse_data->name) - 1] = 0;
}
}
static void
gimp_metadata_deserialize_end_element (GMarkupParseContext *context,
const gchar *element_name,
gpointer user_data,
GError **error)
{
}
static void
gimp_metadata_deserialize_text (GMarkupParseContext *context,
const gchar *text,
gsize text_len,
gpointer user_data,
GError **error)
{
GimpMetadataParseData *parse_data = user_data;
const gchar *current_element;
current_element = g_markup_parse_context_get_element (context);
if (! g_strcmp0 (current_element, "tag"))
{
gchar *value = g_strndup (text, text_len);
gexiv2_metadata_set_tag_string (parse_data->metadata,
parse_data->name,
value);
g_free (value);
}
}
static void
gimp_metadata_deserialize_error (GMarkupParseContext *context,
GError *error,
gpointer user_data)
{
g_printerr ("Metadata parse error: %s\n", error->message);
}
/**
* Deserializes metadata from a string
**/
GExiv2Metadata *
gimp_metadata_deserialize (const gchar *metadata_xml)
{
GimpMetadata *metadata;
GMarkupParser markup_parser;
GimpMetadataParseData parse_data;
GMarkupParseContext *context;
g_return_val_if_fail (metadata_xml != NULL, NULL);
metadata = gimp_metadata_new ();
parse_data.metadata = metadata;
markup_parser.start_element = gimp_metadata_deserialize_start_element;
markup_parser.end_element = gimp_metadata_deserialize_end_element;
markup_parser.text = gimp_metadata_deserialize_text;
markup_parser.passthrough = NULL;
markup_parser.error = gimp_metadata_deserialize_error;
context = g_markup_parse_context_new (&markup_parser, 0, &parse_data, NULL);
g_markup_parse_context_parse (context,
metadata_xml, strlen (metadata_xml),
NULL);
g_markup_parse_context_unref (context);
return metadata;
}
/**
* Serializing metadata as a string
*/
gchar *
gimp_metadata_serialize (GimpMetadata *metadata)
{
GString *string;
gchar **exif_data = NULL;
gchar **iptc_data = NULL;
gchar **xmp_data = NULL;
gchar *value;
gchar *escaped;
gint i;
g_return_val_if_fail (GEXIV2_IS_METADATA (metadata), NULL);
string = g_string_new (NULL);
g_string_append (string, "<?xml version='1.0' encoding='UTF-8'?>\n");
g_string_append (string, "<metadata>\n");
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]);
escaped = g_markup_escape_text (value, -1);
g_string_append_printf (string, " <tag name=\"%s\">%s</tag>\n",
exif_data[i], escaped);
g_free (escaped);
g_free (value);
}
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]);
escaped = g_markup_escape_text (value, -1);
g_string_append_printf (string, " <tag name=\"%s\">%s</tag>\n",
xmp_data[i], escaped);
g_free (escaped);
g_free (value);
}
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]);
escaped = g_markup_escape_text (value, -1);
g_string_append_printf (string, " <tag name=\"%s\">%s</tag>\n",
iptc_data[i], escaped);
g_free (escaped);
g_free (value);
}
g_strfreev (iptc_data);
}
g_string_append (string, "</metadata>\n");
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, gimp_metadata_error_quark (), 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, gimp_metadata_error_quark (), 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;
}
gboolean
gimp_metadata_set_from_exif (GimpMetadata *metadata,
const guchar *exif_data,
gint exif_data_length,
GError **error)
{
GByteArray *exif_bytes;
GimpMetadata *exif_metadata;
guint8 data_size[2] = { 0, };
g_return_val_if_fail (GEXIV2_IS_METADATA (metadata), FALSE);
g_return_val_if_fail (exif_data != NULL, FALSE);
g_return_val_if_fail (exif_data_length > 0, FALSE);
g_return_val_if_fail (exif_data_length < 65536, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
data_size[0] = (exif_data_length & 0xFF00) >> 8;
data_size[1] = (exif_data_length & 0x00FF);
exif_bytes = g_byte_array_new ();
exif_bytes = g_byte_array_append (exif_bytes,
minimal_exif, G_N_ELEMENTS (minimal_exif));
exif_bytes = g_byte_array_append (exif_bytes,
data_size, 2);
exif_bytes = g_byte_array_append (exif_bytes,
(guint8 *) exif_data, exif_data_length);
exif_metadata = gimp_metadata_new ();
if (! gexiv2_metadata_open_buf (exif_metadata,
exif_bytes->data, exif_bytes->len, error))
{
g_object_unref (exif_metadata);
g_byte_array_free (exif_bytes, TRUE);
return FALSE;
}
if (! gexiv2_metadata_has_exif (exif_metadata))
{
g_set_error (error, gimp_metadata_error_quark (), 0,
_("Parsing EXIF data failed."));
g_object_unref (exif_metadata);
g_byte_array_free (exif_bytes, TRUE);
return FALSE;
}
gimp_metadata_add (exif_metadata, metadata);
g_object_unref (exif_metadata);
g_byte_array_free (exif_bytes, TRUE);
return TRUE;
}
gboolean
gimp_metadata_set_from_xmp (GimpMetadata *metadata,
const guchar *xmp_data,
gint xmp_data_length,
GError **error)
{
GimpMetadata *xmp_metadata;
g_return_val_if_fail (GEXIV2_IS_METADATA (metadata), FALSE);
g_return_val_if_fail (xmp_data != NULL, FALSE);
g_return_val_if_fail (xmp_data_length > 0, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
xmp_data += 10;
xmp_data_length -= 10;
xmp_metadata = gimp_metadata_new ();
if (! gexiv2_metadata_open_buf (xmp_metadata,
xmp_data, xmp_data_length, error))
{
g_object_unref (xmp_metadata);
return FALSE;
}
if (! gexiv2_metadata_has_xmp (xmp_metadata))
{
g_set_error (error, gimp_metadata_error_quark (), 0,
_("Parsing XMP data failed."));
g_object_unref (xmp_metadata);
return FALSE;
}
gimp_metadata_add (xmp_metadata, metadata);
g_object_unref (xmp_metadata);
return TRUE;
}
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 = 2;
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");
if (! (xr && yr))
{
g_free (xr);
g_free (yr);
return FALSE;
}
un = gexiv2_metadata_get_tag_string (metadata, "Exif.Image.ResolutionUnit");
if (un)
{
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 (const gchar *tag,
const gchar *mime_type)
{
gint j;
g_return_val_if_fail (tag != NULL, FALSE);
g_return_val_if_fail (mime_type != NULL, FALSE);
for (j = 0; j < G_N_ELEMENTS (unsupported_tags); j++)
{
if (g_str_has_prefix (tag, unsupported_tags[j]))
{
return FALSE;
}
}
if (! strcmp (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 (! strcmp (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 */
static GQuark
gimp_metadata_error_quark (void)
{
static GQuark quark = 0;
if (G_UNLIKELY (quark == 0))
quark = g_quark_from_static_string ("gimp-metadata-error-quark");
return quark;
}
/**
* 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;
}
static void
gimp_metadata_add (GimpMetadata *src,
GimpMetadata *dest)
{
gchar *value;
gint i;
if (gexiv2_metadata_get_supports_exif (src) &&
gexiv2_metadata_get_supports_exif (dest))
{
gchar **exif_data = gexiv2_metadata_get_exif_tags (src);
if (exif_data)
{
for (i = 0; exif_data[i] != NULL; i++)
{
value = gexiv2_metadata_get_tag_string (src, exif_data[i]);
gexiv2_metadata_set_tag_string (dest, exif_data[i], value);
g_free (value);
}
g_strfreev (exif_data);
}
}
if (gexiv2_metadata_get_supports_xmp (src) &&
gexiv2_metadata_get_supports_xmp (dest))
{
gchar **xmp_data = gexiv2_metadata_get_xmp_tags (src);
if (xmp_data)
{
for (i = 0; xmp_data[i] != NULL; i++)
{
value = gexiv2_metadata_get_tag_string (src, xmp_data[i]);
gexiv2_metadata_set_tag_string (dest, xmp_data[i], value);
g_free (value);
}
g_strfreev (xmp_data);
}
}
if (gexiv2_metadata_get_supports_iptc (src) &&
gexiv2_metadata_get_supports_iptc (dest))
{
gchar **iptc_data = gexiv2_metadata_get_iptc_tags (src);
if (iptc_data)
{
for (i = 0; iptc_data[i] != NULL; i++)
{
value = gexiv2_metadata_get_tag_string (src, iptc_data[i]);
gexiv2_metadata_set_tag_string (dest, iptc_data[i], value);
g_free (value);
}
g_strfreev (iptc_data);
}
}
}

View file

@ -0,0 +1,82 @@
/* 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
typedef enum
{
GIMP_METADATA_SAVE_EXIF = 1 << 0,
GIMP_METADATA_SAVE_XMP = 1 << 1,
GIMP_METADATA_SAVE_IPTC = 1 << 2,
GIMP_METADATA_SAVE_THUMBNAIL = 1 << 3,
GIMP_METADATA_SAVE_ALL = (GIMP_METADATA_SAVE_EXIF |
GIMP_METADATA_SAVE_XMP |
GIMP_METADATA_SAVE_IPTC |
GIMP_METADATA_SAVE_THUMBNAIL)
} GimpMetadataSaveFlags;
GimpMetadata * gimp_metadata_new (void);
GimpMetadata * gimp_metadata_duplicate (GimpMetadata *metadata);
GimpMetadata * gimp_metadata_deserialize (const gchar *metadata_xml);
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);
gboolean gimp_metadata_set_from_exif (GimpMetadata *metadata,
const guchar *exif_data,
gint exif_data_length,
GError **error);
gboolean gimp_metadata_set_from_xmp (GimpMetadata *metadata,
const guchar *xmp_data,
gint xmp_data_length,
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 (const gchar *tag,
const gchar *mime_type);
G_END_DECLS
#endif /* __GIMP_METADATA_H__ */

View file

@ -51,11 +51,6 @@ else
file_uri = file-uri file_uri = file-uri
endif endif
if HAVE_LIBEXIF
metadata = metadata
endif
SUBDIRS = \ SUBDIRS = \
$(script_fu) \ $(script_fu) \
$(pygimp) \ $(pygimp) \
@ -83,7 +78,6 @@ SUBDIRS = \
lighting \ lighting \
map-object \ map-object \
maze \ maze \
$(metadata) \
pagecurl \ pagecurl \
$(print) \ $(print) \
selection-to-path \ selection-to-path \

View file

@ -182,6 +182,8 @@
/mail.exe /mail.exe
/max-rgb /max-rgb
/max-rgb.exe /max-rgb.exe
/metadata
/metadata.exe
/newsprint /newsprint
/newsprint.exe /newsprint.exe
/nl-filter /nl-filter

View file

@ -134,6 +134,7 @@ libexec_PROGRAMS = \
lens-flare \ lens-flare \
$(MAIL) \ $(MAIL) \
max-rgb \ max-rgb \
metadata \
newsprint \ newsprint \
nl-filter \ nl-filter \
noise-rgb \ noise-rgb \
@ -1090,13 +1091,15 @@ file_jp2_load_SOURCES = \
file-jp2-load.c file-jp2-load.c
file_jp2_load_LDADD = \ file_jp2_load_LDADD = \
$(libgimpui) \
$(libgimpwidgets) \
$(libgimpmodule) \
$(libgimp) \ $(libgimp) \
$(libgimpmath) \ $(libgimpmath) \
$(libgimpconfig) \ $(libgimpconfig) \
$(libgimpcolor) \ $(libgimpcolor) \
$(libgimpbase) \ $(libgimpbase) \
$(CAIRO_LIBS) \ $(GTK_LIBS) \
$(GDK_PIXBUF_LIBS) \
$(GEGL_LIBS) \ $(GEGL_LIBS) \
$(JP2_LIBS) \ $(JP2_LIBS) \
$(RT_LIBS) \ $(RT_LIBS) \
@ -1773,6 +1776,26 @@ max_rgb_LDADD = \
$(INTLLIBS) \ $(INTLLIBS) \
$(max_rgb_RC) $(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_SOURCES = \
newsprint.c newsprint.c

View file

@ -46,7 +46,8 @@
#include <jasper/jasper.h> #include <jasper/jasper.h>
#define LOAD_PROC "file-jp2-load" #define LOAD_PROC "file-jp2-load"
#define PLUG_IN_BINARY "file-jp2-load"
static void query (void); static void query (void);
@ -120,10 +121,13 @@ run (const gchar *name,
GimpParam **return_vals) GimpParam **return_vals)
{ {
static GimpParam values[2]; static GimpParam values[2];
GimpRunMode run_mode;
GimpPDBStatusType status = GIMP_PDB_SUCCESS; GimpPDBStatusType status = GIMP_PDB_SUCCESS;
gint image_ID; gint image_ID;
GError *error = NULL; GError *error = NULL;
run_mode = param[0].data.d_int32;
INIT_I18N (); INIT_I18N ();
gegl_init (NULL, NULL); gegl_init (NULL, NULL);
@ -135,10 +139,31 @@ run (const gchar *name,
if (strcmp (name, LOAD_PROC) == 0) if (strcmp (name, LOAD_PROC) == 0)
{ {
gboolean interactive;
switch (run_mode)
{
case GIMP_RUN_INTERACTIVE:
case GIMP_RUN_WITH_LAST_VALS:
gimp_ui_init (PLUG_IN_BINARY, FALSE);
interactive = TRUE;
break;
default:
interactive = FALSE;
break;
}
image_ID = load_image (param[1].data.d_string, &error); image_ID = load_image (param[1].data.d_string, &error);
if (image_ID != -1) if (image_ID != -1)
{ {
GFile *file = g_file_new_for_path (param[1].data.d_string);
gimp_image_metadata_load (image_ID, "image/jp2", file,
interactive);
g_object_unref (file);
*nreturn_vals = 2; *nreturn_vals = 2;
values[1].type = GIMP_PDB_IMAGE; values[1].type = GIMP_PDB_IMAGE;
values[1].data.d_image = image_ID; values[1].data.d_image = image_ID;

View file

@ -88,6 +88,10 @@ typedef struct
gboolean comment; gboolean comment;
gboolean save_transp_pixels; gboolean save_transp_pixels;
gint compression_level; gint compression_level;
gboolean save_exif;
gboolean save_xmp;
gboolean save_iptc;
gboolean save_thumbnail;
} }
PngSaveVals; PngSaveVals;
@ -104,6 +108,10 @@ typedef struct
GtkWidget *comment; GtkWidget *comment;
GtkWidget *save_transp_pixels; GtkWidget *save_transp_pixels;
GtkAdjustment *compression_level; GtkAdjustment *compression_level;
GtkWidget *save_exif;
GtkWidget *save_xmp;
GtkWidget *save_iptc;
GtkWidget *save_thumbnail;
} }
PngSaveGui; PngSaveGui;
@ -119,6 +127,7 @@ typedef struct
} }
PngGlobals; PngGlobals;
/* /*
* Local functions... * Local functions...
*/ */
@ -164,6 +173,7 @@ static void load_defaults (void);
static void save_defaults (void); static void save_defaults (void);
static void load_gui_defaults (PngSaveGui *pg); static void load_gui_defaults (PngSaveGui *pg);
/* /*
* Globals... * Globals...
*/ */
@ -186,11 +196,15 @@ static const PngSaveVals defaults =
TRUE, TRUE,
TRUE, TRUE,
TRUE, TRUE,
9 9,
TRUE, /* save exif */
TRUE, /* save xmp */
TRUE, /* save iptc */
TRUE /* save thumbnail */
}; };
static PngSaveVals pngvals; static PngSaveVals pngvals;
static PngGlobals pngg; static PngGlobals pngg;
/* /*
@ -406,6 +420,8 @@ run (const gchar *name,
GimpExportReturn export = GIMP_EXPORT_CANCEL; GimpExportReturn export = GIMP_EXPORT_CANCEL;
GError *error = NULL; GError *error = NULL;
run_mode = param[0].data.d_int32;
INIT_I18N (); INIT_I18N ();
gegl_init (NULL, NULL); gegl_init (NULL, NULL);
@ -417,13 +433,32 @@ run (const gchar *name,
if (strcmp (name, LOAD_PROC) == 0) if (strcmp (name, LOAD_PROC) == 0)
{ {
run_mode = param[0].data.d_int32; gboolean interactive;
switch (run_mode)
{
case GIMP_RUN_INTERACTIVE:
case GIMP_RUN_WITH_LAST_VALS:
gimp_ui_init (PLUG_IN_BINARY, FALSE);
interactive = TRUE;
break;
default:
interactive = FALSE;
break;
}
image_ID = load_image (param[1].data.d_string, image_ID = load_image (param[1].data.d_string,
run_mode == GIMP_RUN_INTERACTIVE, &error); interactive, &error);
if (image_ID != -1) if (image_ID != -1)
{ {
GFile *file = g_file_new_for_path (param[1].data.d_string);
gimp_image_metadata_load (image_ID, "image/png", file,
interactive);
g_object_unref (file);
*nreturn_vals = 2; *nreturn_vals = 2;
values[1].type = GIMP_PDB_IMAGE; values[1].type = GIMP_PDB_IMAGE;
values[1].data.d_image = image_ID; values[1].data.d_image = image_ID;
@ -439,7 +474,6 @@ run (const gchar *name,
{ {
gboolean alpha; gboolean alpha;
run_mode = param[0].data.d_int32;
image_ID = orig_image_ID = param[1].data.d_int32; image_ID = orig_image_ID = param[1].data.d_int32;
drawable_ID = param[2].data.d_int32; drawable_ID = param[2].data.d_int32;
@ -549,6 +583,33 @@ run (const gchar *name,
if (save_image (param[3].data.d_string, if (save_image (param[3].data.d_string,
image_ID, drawable_ID, orig_image_ID, &error)) image_ID, drawable_ID, orig_image_ID, &error))
{ {
GimpMetadata *metadata;
metadata = gimp_image_metadata_save_prepare (image_ID,
"image/png");
if (metadata)
{
GFile *file;
GimpMetadataSaveFlags flags = 0;
gimp_metadata_set_bits_per_sample (metadata, 8);
if (pngvals.save_exif) flags |= GIMP_METADATA_SAVE_EXIF;
if (pngvals.save_xmp) flags |= GIMP_METADATA_SAVE_XMP;
if (pngvals.save_iptc) flags |= GIMP_METADATA_SAVE_IPTC;
if (pngvals.save_thumbnail) flags |= GIMP_METADATA_SAVE_THUMBNAIL;
file = g_file_new_for_path (param[3].data.d_string);
gimp_image_metadata_save_finish (image_ID,
"image/png",
metadata, file, flags,
NULL);
g_object_unref (file);
g_object_unref (metadata);
}
gimp_set_data (SAVE_PROC, &pngvals, sizeof (pngvals)); gimp_set_data (SAVE_PROC, &pngvals, sizeof (pngvals));
} }
else else
@ -587,6 +648,8 @@ run (const gchar *name,
{ {
if (nparams == 9) if (nparams == 9)
{ {
load_defaults ();
pngvals.interlaced = param[0].data.d_int32; pngvals.interlaced = param[0].data.d_int32;
pngvals.compression_level = param[1].data.d_int32; pngvals.compression_level = param[1].data.d_int32;
pngvals.bkgd = param[2].data.d_int32; pngvals.bkgd = param[2].data.d_int32;
@ -2059,7 +2122,7 @@ save_dialog (gint32 image_ID,
/* Table */ /* Table */
gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)), gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)),
GTK_WIDGET (gtk_builder_get_object (builder, "table")), GTK_WIDGET (gtk_builder_get_object (builder, "table")),
TRUE, TRUE, 0); FALSE, FALSE, 0);
/* Toggles */ /* Toggles */
pg.interlaced = toggle_button_init (builder, "interlace", pg.interlaced = toggle_button_init (builder, "interlace",
@ -2080,6 +2143,18 @@ save_dialog (gint32 image_ID,
pg.time = toggle_button_init (builder, "save-creation-time", pg.time = toggle_button_init (builder, "save-creation-time",
pngvals.time, pngvals.time,
&pngvals.time); &pngvals.time);
pg.save_exif = toggle_button_init (builder, "sv_exif",
pngvals.save_exif,
&pngvals.save_exif);
pg.save_xmp = toggle_button_init (builder, "sv_xmp",
pngvals.save_xmp,
&pngvals.save_xmp);
pg.save_iptc = toggle_button_init (builder, "sv_iptc",
pngvals.save_iptc,
&pngvals.save_iptc);
pg.save_thumbnail = toggle_button_init (builder, "sv_thumbnail",
pngvals.save_thumbnail,
&pngvals.save_thumbnail);
/* Comment toggle */ /* Comment toggle */
parasite = gimp_image_get_parasite (image_ID, "gimp-comment"); parasite = gimp_image_get_parasite (image_ID, "gimp-comment");
@ -2150,12 +2225,15 @@ load_defaults (void)
{ {
GimpParasite *parasite; GimpParasite *parasite;
/* initialize with hardcoded defaults */
pngvals = defaults;
parasite = gimp_get_parasite (PNG_DEFAULTS_PARASITE); parasite = gimp_get_parasite (PNG_DEFAULTS_PARASITE);
if (parasite) if (parasite)
{ {
gchar *def_str; gchar *def_str;
PngSaveVals tmpvals; PngSaveVals tmpvals = defaults;
gint num_fields; gint num_fields;
def_str = g_strndup (gimp_parasite_data (parasite), def_str = g_strndup (gimp_parasite_data (parasite),
@ -2163,7 +2241,7 @@ load_defaults (void)
gimp_parasite_free (parasite); 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.interlaced,
&tmpvals.bkgd, &tmpvals.bkgd,
&tmpvals.gama, &tmpvals.gama,
@ -2172,18 +2250,17 @@ load_defaults (void)
&tmpvals.time, &tmpvals.time,
&tmpvals.comment, &tmpvals.comment,
&tmpvals.save_transp_pixels, &tmpvals.save_transp_pixels,
&tmpvals.compression_level); &tmpvals.compression_level,
&tmpvals.save_exif,
&tmpvals.save_xmp,
&tmpvals.save_iptc,
&tmpvals.save_thumbnail);
g_free (def_str); g_free (def_str);
if (num_fields == 9) if (num_fields == 9 || num_fields == 13)
{ pngvals = tmpvals;
memcpy (&pngvals, &tmpvals, sizeof (tmpvals));
return;
}
} }
memcpy (&pngvals, &defaults, sizeof (defaults));
} }
static void static void
@ -2192,7 +2269,7 @@ save_defaults (void)
GimpParasite *parasite; GimpParasite *parasite;
gchar *def_str; 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.interlaced,
pngvals.bkgd, pngvals.bkgd,
pngvals.gama, pngvals.gama,
@ -2201,7 +2278,11 @@ save_defaults (void)
pngvals.time, pngvals.time,
pngvals.comment, pngvals.comment,
pngvals.save_transp_pixels, pngvals.save_transp_pixels,
pngvals.compression_level); pngvals.compression_level,
pngvals.save_exif,
pngvals.save_xmp,
pngvals.save_iptc,
pngvals.save_thumbnail);
parasite = gimp_parasite_new (PNG_DEFAULTS_PARASITE, parasite = gimp_parasite_new (PNG_DEFAULTS_PARASITE,
GIMP_PARASITE_PERSISTENT, GIMP_PARASITE_PERSISTENT,
@ -2230,6 +2311,10 @@ load_gui_defaults (PngSaveGui *pg)
SET_ACTIVE (time); SET_ACTIVE (time);
SET_ACTIVE (comment); SET_ACTIVE (comment);
SET_ACTIVE (save_transp_pixels); SET_ACTIVE (save_transp_pixels);
SET_ACTIVE (save_exif);
SET_ACTIVE (save_xmp);
SET_ACTIVE (save_iptc);
SET_ACTIVE (save_thumbnail);
#undef SET_ACTIVE #undef SET_ACTIVE

View file

@ -280,6 +280,13 @@ run (const gchar *name,
if (image != -1) if (image != -1)
{ {
GFile *file = g_file_new_for_path (param[1].data.d_string);
gimp_image_metadata_load (image, "image/tiff", file,
run_mode == GIMP_RUN_INTERACTIVE);
g_object_unref (file);
*nreturn_vals = 2; *nreturn_vals = 2;
values[1].type = GIMP_PDB_IMAGE; values[1].type = GIMP_PDB_IMAGE;
values[1].data.d_image = image; values[1].data.d_image = image;

View file

@ -82,6 +82,10 @@ typedef struct
gint compression; gint compression;
gint fillorder; gint fillorder;
gboolean save_transp_pixels; gboolean save_transp_pixels;
gboolean save_exif;
gboolean save_xmp;
gboolean save_iptc;
gboolean save_thumbnail;
} TiffSaveVals; } TiffSaveVals;
typedef struct typedef struct
@ -110,6 +114,7 @@ static gboolean save_image (const gchar *filename,
gint32 image, gint32 image,
gint32 drawable, gint32 drawable,
gint32 orig_image, gint32 orig_image,
gint *saved_bpp,
GError **error); GError **error);
static gboolean save_dialog (gboolean has_alpha, static gboolean save_dialog (gboolean has_alpha,
@ -143,8 +148,13 @@ const GimpPlugInInfo PLUG_IN_INFO =
static TiffSaveVals tsvals = static TiffSaveVals tsvals =
{ {
COMPRESSION_NONE, /* compression */ COMPRESSION_NONE, /* compression */
TRUE, /* alpha handling */ TRUE, /* alpha handling */
TRUE, /* save transp. pixels */
TRUE, /* save exif */
TRUE, /* save xmp */
TRUE, /* save iptc */
TRUE /* save thumbnail */
}; };
static gchar *image_comment = NULL; static gchar *image_comment = NULL;
@ -346,9 +356,38 @@ run (const gchar *name,
if (status == GIMP_PDB_SUCCESS) if (status == GIMP_PDB_SUCCESS)
{ {
gint saved_bpp;
if (save_image (param[3].data.d_string, image, drawable, orig_image, if (save_image (param[3].data.d_string, image, drawable, orig_image,
&error)) &saved_bpp, &error))
{ {
GimpMetadata *metadata;
metadata = gimp_image_metadata_save_prepare (image,
"image/tiff");
if (metadata)
{
GFile *file;
GimpMetadataSaveFlags flags = 0;
gimp_metadata_set_bits_per_sample (metadata, saved_bpp);
if (tsvals.save_exif) flags |= GIMP_METADATA_SAVE_EXIF;
if (tsvals.save_xmp) flags |= GIMP_METADATA_SAVE_XMP;
if (tsvals.save_iptc) flags |= GIMP_METADATA_SAVE_IPTC;
if (tsvals.save_thumbnail) flags |= GIMP_METADATA_SAVE_THUMBNAIL;
file = g_file_new_for_path (param[3].data.d_string);
gimp_image_metadata_save_finish (image,
"image/tiff",
metadata, file, flags,
NULL);
g_object_unref (file);
g_object_unref (metadata);
}
/* Store mvals data */ /* Store mvals data */
gimp_set_data (SAVE_PROC, &tsvals, sizeof (TiffSaveVals)); gimp_set_data (SAVE_PROC, &tsvals, sizeof (TiffSaveVals));
} }
@ -657,6 +696,7 @@ save_image (const gchar *filename,
gint32 image, gint32 image,
gint32 layer, gint32 layer,
gint32 orig_image, /* the export function might have */ gint32 orig_image, /* the export function might have */
gint *saved_bpp,
GError **error) /* created a duplicate */ GError **error) /* created a duplicate */
{ {
gboolean status = FALSE; gboolean status = FALSE;
@ -725,6 +765,8 @@ save_image (const gchar *filename,
else else
bitspersample = 16; bitspersample = 16;
*saved_bpp = bitspersample;
drawable_type = gimp_drawable_type (layer); drawable_type = gimp_drawable_type (layer);
buffer = gimp_drawable_get_buffer (layer); buffer = gimp_drawable_get_buffer (layer);
@ -1075,25 +1117,44 @@ static gboolean
save_dialog (gboolean has_alpha, save_dialog (gboolean has_alpha,
gboolean is_monochrome) gboolean is_monochrome)
{ {
GtkWidget *dialog; GError *error = NULL;
GtkWidget *vbox; GtkWidget *dialog;
GtkWidget *frame; GtkWidget *vbox;
GtkWidget *hbox; GtkWidget *frame;
GtkWidget *label; GtkWidget *entry;
GtkWidget *entry; GtkWidget *toggle;
GtkWidget *toggle; GtkWidget *cmp_g3;
GtkWidget *g3; GtkWidget *cmp_g4;
GtkWidget *g4; GtkBuilder *builder;
gboolean run; gchar *ui_file;
gboolean run;
dialog = gimp_export_dialog_new (_("TIFF"), PLUG_IN_BINARY, SAVE_PROC); dialog = gimp_export_dialog_new (_("TIFF"), PLUG_IN_BINARY, SAVE_PROC);
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12); builder = gtk_builder_new ();
gtk_container_set_border_width (GTK_CONTAINER (vbox), 12); ui_file = g_build_filename (gimp_data_directory (),
gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)), "ui", "plug-ins", "plug-in-file-tiff.ui",
vbox, FALSE, TRUE, 0); 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, FALSE, 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"), frame = gimp_int_radio_group_new (TRUE, _("Compression"),
G_CALLBACK (gimp_radio_button_update), G_CALLBACK (gimp_radio_button_update),
&tsvals.compression, tsvals.compression, &tsvals.compression, tsvals.compression,
@ -1103,20 +1164,20 @@ save_dialog (gboolean has_alpha,
_("_Pack Bits"), COMPRESSION_PACKBITS, NULL, _("_Pack Bits"), COMPRESSION_PACKBITS, NULL,
_("_Deflate"), COMPRESSION_ADOBE_DEFLATE, NULL, _("_Deflate"), COMPRESSION_ADOBE_DEFLATE, NULL,
_("_JPEG"), COMPRESSION_JPEG, NULL, _("_JPEG"), COMPRESSION_JPEG, NULL,
_("CCITT Group _3 fax"), COMPRESSION_CCITTFAX3, &g3, _("CCITT Group _3 fax"), COMPRESSION_CCITTFAX3, &cmp_g3,
_("CCITT Group _4 fax"), COMPRESSION_CCITTFAX4, &g4, _("CCITT Group _4 fax"), COMPRESSION_CCITTFAX4, &cmp_g4,
NULL); NULL);
gtk_widget_set_sensitive (g3, is_monochrome); gtk_widget_set_sensitive (cmp_g3, is_monochrome);
gtk_widget_set_sensitive (g4, is_monochrome); gtk_widget_set_sensitive (cmp_g4, is_monochrome);
if (! is_monochrome) if (! is_monochrome)
{ {
if (tsvals.compression == COMPRESSION_CCITTFAX3 || if (tsvals.compression == COMPRESSION_CCITTFAX3 ||
tsvals.compression == COMPRESSION_CCITTFAX4) 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); COMPRESSION_NONE);
} }
} }
@ -1124,40 +1185,49 @@ save_dialog (gboolean has_alpha,
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
gtk_widget_show (frame); gtk_widget_show (frame);
/* Keep colors behind alpha mask */ toggle = GTK_WIDGET (gtk_builder_get_object (builder, "sv_alpha"));
toggle = gtk_check_button_new_with_mnemonic
( _("Save _color values from transparent pixels"));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
has_alpha && tsvals.save_transp_pixels); has_alpha && tsvals.save_transp_pixels);
gtk_widget_set_sensitive (toggle, has_alpha); 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_signal_connect (toggle, "toggled",
G_CALLBACK (gimp_toggle_button_update), G_CALLBACK (gimp_toggle_button_update),
&tsvals.save_transp_pixels); &tsvals.save_transp_pixels);
/* comment entry */ entry = GTK_WIDGET (gtk_builder_get_object (builder, "commentfield"));
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);
gtk_entry_set_text (GTK_ENTRY (entry), image_comment ? image_comment : ""); gtk_entry_set_text (GTK_ENTRY (entry), image_comment ? image_comment : "");
g_signal_connect (entry, "changed", g_signal_connect (entry, "changed",
G_CALLBACK (comment_entry_callback), G_CALLBACK (comment_entry_callback),
NULL); NULL);
gtk_widget_show (frame); toggle = GTK_WIDGET (gtk_builder_get_object (builder, "sv_exif"));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
tsvals.save_exif);
g_signal_connect (toggle, "toggled",
G_CALLBACK (gimp_toggle_button_update),
&tsvals.save_exif);
toggle = GTK_WIDGET (gtk_builder_get_object (builder, "sv_xmp"));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
tsvals.save_xmp);
g_signal_connect (toggle, "toggled",
G_CALLBACK (gimp_toggle_button_update),
&tsvals.save_xmp);
toggle = GTK_WIDGET (gtk_builder_get_object (builder, "sv_iptc"));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
tsvals.save_iptc);
g_signal_connect (toggle, "toggled",
G_CALLBACK (gimp_toggle_button_update),
&tsvals.save_iptc);
toggle = GTK_WIDGET (gtk_builder_get_object (builder, "sv_thumbnail"));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
tsvals.save_thumbnail);
g_signal_connect (toggle, "toggled",
G_CALLBACK (gimp_toggle_button_update),
&tsvals.save_thumbnail);
gtk_widget_show (vbox);
gtk_widget_show (dialog); gtk_widget_show (dialog);
run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK); run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);

View file

@ -88,6 +88,7 @@ lens_apply_RC = lens-apply.rc.o
lens_flare_RC = lens-flare.rc.o lens_flare_RC = lens-flare.rc.o
mail_RC = mail.rc.o mail_RC = mail.rc.o
max_rgb_RC = max-rgb.rc.o max_rgb_RC = max-rgb.rc.o
metadata_RC = metadata.rc.o
newsprint_RC = newsprint.rc.o newsprint_RC = newsprint.rc.o
nl_filter_RC = nl-filter.rc.o nl_filter_RC = nl-filter.rc.o
noise_rgb_RC = noise-rgb.rc.o noise_rgb_RC = noise-rgb.rc.o

382
plug-ins/common/metadata.c Normal file
View file

@ -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
}

View file

@ -51,7 +51,7 @@
'file-glob' => {}, 'file-glob' => {},
'file-header' => { ui => 1, gegl => 1 }, 'file-header' => { ui => 1, gegl => 1 },
'file-html-table' => { ui => 1, gegl => 1 }, 'file-html-table' => { ui => 1, gegl => 1 },
'file-jp2-load' => { optional => 1, gegl => 1, libs => 'JP2_LIBS' }, 'file-jp2-load' => { ui => 1, optional => 1, gegl => 1, libs => 'JP2_LIBS' },
'file-mng' => { ui => 1, gegl => 1, optional => 1, libs => 'MNG_LIBS', cflags => 'MNG_CFLAGS' }, 'file-mng' => { ui => 1, gegl => 1, optional => 1, libs => 'MNG_LIBS', cflags => 'MNG_CFLAGS' },
'file-pat' => { ui => 1, gegl => 1 }, 'file-pat' => { ui => 1, gegl => 1 },
'file-pcx' => { ui => 1, gegl => 1 }, 'file-pcx' => { ui => 1, gegl => 1 },
@ -89,6 +89,7 @@
'lens-flare' => { ui => 1 }, 'lens-flare' => { ui => 1 },
'mail' => { ui => 1, optional => 1 }, 'mail' => { ui => 1, optional => 1 },
'max-rgb' => { ui => 1 }, 'max-rgb' => { ui => 1 },
'metadata' => { ui => 1, libs => 'GEXIV2_LIBS', cflags => 'GEXIV2_CFLAGS' },
'newsprint' => { ui => 1 }, 'newsprint' => { ui => 1 },
'nl-filter' => { ui => 1 }, 'nl-filter' => { ui => 1 },
'noise-rgb' => { ui => 1 }, 'noise-rgb' => { ui => 1 },

View file

@ -22,11 +22,12 @@ AM_LDFLAGS = $(mwindows)
libexecdir = $(gimpplugindir)/plug-ins libexecdir = $(gimpplugindir)/plug-ins
AM_CPPFLAGS = \ AM_CPPFLAGS = \
-I$(top_srcdir) \ -I$(top_srcdir) \
$(GTK_CFLAGS) \ $(GTK_CFLAGS) \
$(EXIF_CFLAGS) \ $(EXIF_CFLAGS) \
$(LCMS_CFLAGS) \ $(LCMS_CFLAGS) \
$(GEGL_CFLAGS) \ $(GEGL_CFLAGS) \
$(GEXIV2_CFLAGS) \
-I$(includedir) -I$(includedir)
libexec_PROGRAMS = file-jpeg libexec_PROGRAMS = file-jpeg
@ -45,14 +46,6 @@ file_jpeg_SOURCES = \
jpeg-settings.c \ jpeg-settings.c \
jpeg-settings.h jpeg-settings.h
if HAVE_LIBEXIF
file_jpeg_SOURCES += \
jpeg-exif.c \
jpeg-exif.h \
gimpexif.c \
gimpexif.h
endif
file_jpeg_LDADD = \ file_jpeg_LDADD = \
$(libgimpui) \ $(libgimpui) \
$(libgimpwidgets) \ $(libgimpwidgets) \
@ -65,15 +58,11 @@ file_jpeg_LDADD = \
$(LCMS_LIBS) \ $(LCMS_LIBS) \
$(GTK_LIBS) \ $(GTK_LIBS) \
$(GEGL_LIBS) \ $(GEGL_LIBS) \
$(GEXIV2_LIBS) \
$(RT_LIBS) \ $(RT_LIBS) \
$(INTLLIBS) \ $(INTLLIBS) \
$(file_jpeg_RC) $(file_jpeg_RC)
if HAVE_LIBEXIF
file_jpeg_LDADD += \
$(EXIF_LIBS)
endif
noinst_PROGRAMS = jpegqual noinst_PROGRAMS = jpegqual
jpegqual_SOURCES = \ jpegqual_SOURCES = \

View file

@ -1,160 +0,0 @@
/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* EXIF-handling code for the metadata library.
*/
#include "config.h"
#include <stdlib.h>
#include <libgimp/gimp.h>
#include <libexif/exif-data.h>
#include <libexif/exif-content.h>
#include <libexif/exif-utils.h>
#include "gimpexif.h"
#define EXIF_HEADER_SIZE 8
/*
* gimp_metadata_store_exif:
* @image_ID: the GIMP image to work on.
* @exif_data: the Exif data that is to be stored.
*
* This function is supposed to load the "gimp-metadata" parasite
* (which is in XMP format), parse it, add the exif information,
* reformat it into XMP, and store the result as the new parasite.
* The infrastructure to do this is not yet available, so for the
* moment it does something much simpler -- it calls upon libexif
* to serialize the exif data, and stores the result in a parasite
* called "exif-data".
*/
void gimp_metadata_store_exif (gint32 image_ID,
ExifData *exif_data)
{
GimpParam *return_vals;
gint nreturn_vals;
GimpParasite *parasite = NULL;
guchar *exif_buf = NULL;
guint exif_buf_len = 0;
exif_data_save_data (exif_data, &exif_buf, &exif_buf_len);
if (exif_buf_len > EXIF_HEADER_SIZE)
{
parasite = gimp_parasite_new ("exif-data",
GIMP_PARASITE_PERSISTENT,
exif_buf_len, exif_buf);
gimp_image_attach_parasite (image_ID, parasite);
gimp_parasite_free (parasite);
}
return_vals = gimp_run_procedure ("plug-in-metadata-decode-exif",
&nreturn_vals,
GIMP_PDB_IMAGE, image_ID,
GIMP_PDB_INT32, 7,
GIMP_PDB_INT8ARRAY, "unused",
GIMP_PDB_END);
if (return_vals[0].data.d_status != GIMP_PDB_SUCCESS)
g_warning ("JPEG Exif -> XMP Merge failed");
free (exif_buf);
}
/*
* gimp_metadata_generate_exif:
* @image_ID: the ID of the GIMP image to work on.
*
* This function is supposed to load the "gimp-metadata" parasite
* (which is a block of XMP info), parse it, extract the exif
* values, and build an ExifData structure from them.
* The infrastructure to do this is not yet available, so for the
* moment it does something much simpler -- it loads the "exif-data"
* parasite (which is a serialized representation of the exif data),
* and calls upon libexif to parse it into an ExifData struct.
*
* returns: The reconstructed exif data, or NULL if none exists.
*/
ExifData *
gimp_metadata_generate_exif (gint32 image_ID)
{
ExifData *exif_data;
GimpParasite *parasite = gimp_image_get_parasite (image_ID, "exif-data");
if (parasite)
{
exif_data = exif_data_new_from_data (gimp_parasite_data (parasite),
gimp_parasite_data_size (parasite));
gimp_parasite_free (parasite);
return exif_data;
}
return NULL;
}
/*
* gimp_exif_content_get_value:
* @content: ExifContent block from which to get value
* @tag: Tag whose value to get
* @value: Place to put the result
* @maxlen: Maximum size of returned string
*
* This function is a wrapper around the libexif function
* exif_content_get_value(), necessary to deal with an incompatible
* API change. It looks up the value of the specified tag,
* returning the result as a human-readable string. Note that
* @value must be pre-allocated.
*/
const gchar *
gimp_exif_content_get_value (ExifContent *content,
ExifTag tag,
gchar *value,
gint maxlen)
{
ExifEntry *entry = exif_content_get_entry (content, tag);
if (entry)
return exif_entry_get_value (entry, value, maxlen);
else
return NULL;
}
/*
* gimp_exif_data_remove_entry:
* @data: ExifData pointer
* @ifd: Index of the ifd holding the entry to be removed
* @tag: Tag of the entry to be removed
*
* This is a convenience function for removing a specified
* entry from an exif data structure. If no such entry
* exists, the function returns without doing anything.
*/
void
gimp_exif_data_remove_entry (ExifData *exif_data,
ExifIfd ifd,
ExifTag tag)
{
ExifEntry *entry = exif_content_get_entry (exif_data->ifd[ifd],
tag);
if (entry)
exif_content_remove_entry (exif_data->ifd[ifd], entry);
}

View file

@ -1,39 +0,0 @@
/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* EXIF-handling code for the metadata library.
*/
#ifndef __GIMP_EXIF_H__
#define __GIMP_EXIF_H__
void gimp_metadata_store_exif (gint32 image_ID,
ExifData *exif_data);
ExifData * gimp_metadata_generate_exif (gint32 image_ID);
const gchar * gimp_exif_content_get_value (ExifContent *content,
ExifTag tag,
gchar *value,
gint maxlen);
void gimp_exif_data_remove_entry (ExifData *exif_data,
ExifIfd ifd,
ExifTag tag);
#endif /* __GIMP_EXIF_H__ */

View file

@ -1,458 +0,0 @@
/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* EXIF-handling code for the jpeg plugin. May eventually be better
* to move this stuff into libgimpbase or a new libgimpmetadata and
* make it available for other plugins.
*/
#include "config.h"
#include <stdio.h>
#include <string.h>
#include <setjmp.h>
#include <jpeglib.h>
#include <jerror.h>
#include <libexif/exif-content.h>
#include <libexif/exif-data.h>
#include <libexif/exif-utils.h>
#include <libgimp/gimp.h>
#include <libgimp/gimpui.h>
#include "jpeg-exif.h"
#include "gimpexif.h"
#include "jpeg.h"
#include "jpeg-settings.h"
#include "libgimp/stdplugins-intl.h"
#define THUMBNAIL_SIZE 128
#define JPEG_EXIF_ROTATE_PARASITE "exif-orientation-rotate"
static gboolean jpeg_exif_rotate_query_dialog (gint32 image_ID);
/* Replacement for exif_data_new_from_file() to work around
* filename encoding problems (see bug #335391).
*/
ExifData *
jpeg_exif_data_new_from_file (const gchar *filename,
GError **error)
{
ExifData *data;
GMappedFile *file;
g_return_val_if_fail (filename != NULL, NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
file = g_mapped_file_new (filename, FALSE, error);
if (! file)
return NULL;
data = exif_data_new_from_data ((guchar *) g_mapped_file_get_contents (file),
g_mapped_file_get_length (file));
g_mapped_file_unref (file);
return data;
}
gint
jpeg_exif_get_orientation (ExifData *exif_data)
{
ExifEntry *entry;
gint byte_order = exif_data_get_byte_order (exif_data);
/* get orientation and rotate image accordingly if necessary */
if ((entry = exif_content_get_entry (exif_data->ifd[EXIF_IFD_0],
EXIF_TAG_ORIENTATION)))
{
return exif_get_short (entry->data, byte_order);
}
return 0;
}
gboolean
jpeg_exif_get_resolution (ExifData *exif_data,
gdouble *xresolution,
gdouble *yresolution,
gint *unit)
{
gboolean success;
ExifEntry *entry;
gint byte_order;
gdouble xres;
gdouble yres;
gint ruint;
ExifRational r;
success = FALSE;
byte_order = exif_data_get_byte_order (exif_data);
do
{
entry = exif_content_get_entry (exif_data->ifd[EXIF_IFD_0],
EXIF_TAG_X_RESOLUTION);
if (!entry)
break;
r = exif_get_rational (entry->data, byte_order);
if (r.denominator == 0.0)
break;
xres = r.numerator / r.denominator;
entry = exif_content_get_entry (exif_data->ifd[EXIF_IFD_0],
EXIF_TAG_Y_RESOLUTION);
if (!entry)
break;
r = exif_get_rational (entry->data, byte_order);
if (r.denominator == 0.0)
break;
yres = r.numerator / r.denominator;
entry = exif_content_get_entry (exif_data->ifd[EXIF_IFD_0],
EXIF_TAG_RESOLUTION_UNIT);
if (!entry)
break;
ruint = exif_get_short (entry->data, byte_order);
if ((ruint != 2) && /* inches */
(ruint != 3)) /* centimetres */
break;
success = TRUE;
}
while (0);
if (success)
{
*xresolution = xres;
*yresolution = yres;
*unit = ruint;
}
return success;
}
void
jpeg_setup_exif_for_save (ExifData *exif_data,
const gint32 image_ID)
{
ExifRational r;
gdouble xres, yres;
ExifEntry *entry;
gint byte_order = exif_data_get_byte_order (exif_data);
/* set orientation to top - left */
if ((entry = exif_content_get_entry (exif_data->ifd[EXIF_IFD_0],
EXIF_TAG_ORIENTATION)))
{
exif_set_short (entry->data, byte_order, (ExifShort) 1);
}
/* set x and y resolution */
gimp_image_get_resolution (image_ID, &xres, &yres);
r.numerator = xres;
r.denominator = 1;
if ((entry = exif_content_get_entry (exif_data->ifd[EXIF_IFD_0],
EXIF_TAG_X_RESOLUTION)))
{
exif_set_rational (entry->data, byte_order, r);
}
r.numerator = yres;
if ((entry = exif_content_get_entry (exif_data->ifd[EXIF_IFD_0],
EXIF_TAG_Y_RESOLUTION)))
{
exif_set_rational (entry->data, byte_order, r);
}
/* set resolution unit, always inches */
if ((entry = exif_content_get_entry (exif_data->ifd[EXIF_IFD_0],
EXIF_TAG_RESOLUTION_UNIT)))
{
exif_set_short (entry->data, byte_order, (ExifShort) 2);
}
/* set software to "GIMP" and include the version number */
if ((entry = exif_content_get_entry (exif_data->ifd[EXIF_IFD_0],
EXIF_TAG_SOFTWARE)))
{
const gchar *name = "GIMP " GIMP_VERSION;
entry->data = (guchar *) g_strdup (name);
entry->size = strlen (name) + 1;
entry->components = entry->size;
}
/* set the width and height */
if ((entry = exif_content_get_entry (exif_data->ifd[EXIF_IFD_EXIF],
EXIF_TAG_PIXEL_X_DIMENSION)))
{
exif_set_long (entry->data, byte_order,
(ExifLong) gimp_image_width (image_ID));
}
if ((entry = exif_content_get_entry (exif_data->ifd[EXIF_IFD_EXIF],
EXIF_TAG_PIXEL_Y_DIMENSION)))
{
exif_set_long (entry->data, byte_order,
(ExifLong) gimp_image_height (image_ID));
}
/*
* set the date & time image was saved
* note, date & time of original photo is stored elsewwhere, we
* aren't losing it.
*/
if ((entry = exif_content_get_entry (exif_data->ifd[EXIF_IFD_0],
EXIF_TAG_DATE_TIME)))
{
/* small memory leak here */
entry->data = NULL;
exif_entry_initialize (entry, EXIF_TAG_DATE_TIME);
}
/* should set components configuration, don't know how */
/*
* remove entries that don't apply to jpeg
* (may have come from tiff or raw)
*/
gimp_exif_data_remove_entry (exif_data, EXIF_IFD_0, EXIF_TAG_COMPRESSION);
gimp_exif_data_remove_entry (exif_data, EXIF_IFD_0, EXIF_TAG_IMAGE_WIDTH);
gimp_exif_data_remove_entry (exif_data, EXIF_IFD_0, EXIF_TAG_IMAGE_LENGTH);
gimp_exif_data_remove_entry (exif_data, EXIF_IFD_0, EXIF_TAG_BITS_PER_SAMPLE);
gimp_exif_data_remove_entry (exif_data, EXIF_IFD_0, EXIF_TAG_SAMPLES_PER_PIXEL);
gimp_exif_data_remove_entry (exif_data, EXIF_IFD_0, EXIF_TAG_PHOTOMETRIC_INTERPRETATION);
gimp_exif_data_remove_entry (exif_data, EXIF_IFD_0, EXIF_TAG_STRIP_OFFSETS);
gimp_exif_data_remove_entry (exif_data, EXIF_IFD_0, EXIF_TAG_PLANAR_CONFIGURATION);
gimp_exif_data_remove_entry (exif_data, EXIF_IFD_0, EXIF_TAG_YCBCR_SUB_SAMPLING);
/* should set thumbnail attributes */
}
void
jpeg_exif_rotate (gint32 image_ID,
gint orientation)
{
switch (orientation)
{
case 1: /* standard orientation, do nothing */
break;
case 2: /* flipped right-left */
gimp_image_flip (image_ID, GIMP_ORIENTATION_HORIZONTAL);
break;
case 3: /* rotated 180 */
gimp_image_rotate (image_ID, GIMP_ROTATE_180);
break;
case 4: /* flipped top-bottom */
gimp_image_flip (image_ID, GIMP_ORIENTATION_VERTICAL);
break;
case 5: /* flipped diagonally around '\' */
gimp_image_rotate (image_ID, GIMP_ROTATE_90);
jpeg_swap_original_settings (image_ID);
gimp_image_flip (image_ID, GIMP_ORIENTATION_HORIZONTAL);
break;
case 6: /* 90 CW */
gimp_image_rotate (image_ID, GIMP_ROTATE_90);
jpeg_swap_original_settings (image_ID);
break;
case 7: /* flipped diagonally around '/' */
gimp_image_rotate (image_ID, GIMP_ROTATE_90);
jpeg_swap_original_settings (image_ID);
gimp_image_flip (image_ID, GIMP_ORIENTATION_VERTICAL);
break;
case 8: /* 90 CCW */
gimp_image_rotate (image_ID, GIMP_ROTATE_270);
jpeg_swap_original_settings (image_ID);
break;
default: /* shouldn't happen */
break;
}
}
void
jpeg_exif_rotate_query (gint32 image_ID,
gint orientation)
{
GimpParasite *parasite;
gboolean query = load_interactive;
if (orientation < 2 || orientation > 8)
return;
parasite = gimp_get_parasite (JPEG_EXIF_ROTATE_PARASITE);
if (parasite)
{
if (strncmp (gimp_parasite_data (parasite), "yes",
gimp_parasite_data_size (parasite)) == 0)
{
query = FALSE;
}
else if (strncmp (gimp_parasite_data (parasite), "no",
gimp_parasite_data_size (parasite)) == 0)
{
gimp_parasite_free (parasite);
return;
}
gimp_parasite_free (parasite);
}
if (query && ! jpeg_exif_rotate_query_dialog (image_ID))
return;
jpeg_exif_rotate (image_ID, orientation);
}
static gboolean
jpeg_exif_rotate_query_dialog (gint32 image_ID)
{
GtkWidget *dialog;
GtkWidget *hbox;
GtkWidget *vbox;
GtkWidget *label;
GtkWidget *toggle;
GdkPixbuf *pixbuf;
gint response;
dialog = gimp_dialog_new (_("Rotate Image?"), PLUG_IN_ROLE,
NULL, 0, NULL, NULL,
_("_Keep Orientation"), GTK_RESPONSE_CANCEL,
GIMP_STOCK_TOOL_ROTATE, GTK_RESPONSE_OK,
NULL);
gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
GTK_RESPONSE_OK,
GTK_RESPONSE_CANCEL,
-1);
gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
gimp_window_set_transient (GTK_WINDOW (dialog));
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
gtk_container_set_border_width (GTK_CONTAINER (hbox), 12);
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
hbox, FALSE, FALSE, 0);
gtk_widget_show (hbox);
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
gtk_widget_show (vbox);
pixbuf = gimp_image_get_thumbnail (image_ID,
THUMBNAIL_SIZE, THUMBNAIL_SIZE,
GIMP_PIXBUF_SMALL_CHECKS);
if (pixbuf)
{
GtkWidget *image;
gchar *name;
image = gtk_image_new_from_pixbuf (pixbuf);
g_object_unref (pixbuf);
gtk_box_pack_start (GTK_BOX (vbox), image, FALSE, FALSE, 0);
gtk_widget_show (image);
name = gimp_image_get_name (image_ID);
label = gtk_label_new (name);
gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_MIDDLE);
gimp_label_set_attributes (GTK_LABEL (label),
PANGO_ATTR_STYLE, PANGO_STYLE_ITALIC,
-1);
gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
gtk_widget_show (label);
g_free (name);
}
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
gtk_widget_show (vbox);
label = g_object_new (GTK_TYPE_LABEL,
"label", _("According to the EXIF data, "
"this image is rotated."),
"wrap", TRUE,
"justify", GTK_JUSTIFY_LEFT,
"xalign", 0.0,
"yalign", 0.5,
NULL);
gimp_label_set_attributes (GTK_LABEL (label),
PANGO_ATTR_SCALE, PANGO_SCALE_LARGE,
PANGO_ATTR_WEIGHT, PANGO_WEIGHT_BOLD,
-1);
gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
gtk_widget_show (label);
label = g_object_new (GTK_TYPE_LABEL,
"label", _("Would you like GIMP to rotate it "
"into the standard orientation?"),
"wrap", TRUE,
"justify", GTK_JUSTIFY_LEFT,
"xalign", 0.0,
"yalign", 0.5,
NULL);
gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
gtk_widget_show (label);
toggle = gtk_check_button_new_with_mnemonic (_("_Don't ask me again"));
gtk_box_pack_end (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), FALSE);
gtk_widget_show (toggle);
response = gimp_dialog_run (GIMP_DIALOG (dialog));
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (toggle)))
{
GimpParasite *parasite;
const gchar *str = (response == GTK_RESPONSE_OK) ? "yes" : "no";
parasite = gimp_parasite_new (JPEG_EXIF_ROTATE_PARASITE,
GIMP_PARASITE_PERSISTENT,
strlen (str), str);
gimp_attach_parasite (parasite);
gimp_parasite_free (parasite);
}
gtk_widget_destroy (dialog);
return (response == GTK_RESPONSE_OK);
}

View file

@ -1,41 +0,0 @@
/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __JPEG_EXIF_H__
#define __JPEG_EXIF_H__
extern ExifData *exif_data;
ExifData * jpeg_exif_data_new_from_file (const gchar *filename,
GError **error);
gint jpeg_exif_get_orientation (ExifData *exif_data);
gboolean jpeg_exif_get_resolution (ExifData *exif_data,
gdouble *xresolution,
gdouble *yresolution,
gint *unit);
void jpeg_setup_exif_for_save (ExifData *exif_data,
const gint32 image_ID);
void jpeg_exif_rotate (gint32 image_ID,
gint orientation);
void jpeg_exif_rotate_query (gint32 image_ID,
gint orientation);
#endif /* __JPEG_EXIF_H__ */

View file

@ -21,15 +21,13 @@
#include <errno.h> #include <errno.h>
#include <setjmp.h> #include <setjmp.h>
#include <gio/gio.h>
#include <glib/gstdio.h> #include <glib/gstdio.h>
#include <gexiv2/gexiv2.h>
#include <jpeglib.h> #include <jpeglib.h>
#include <jerror.h> #include <jerror.h>
#ifdef HAVE_LIBEXIF
#include <libexif/exif-data.h>
#endif /* HAVE_LIBEXIF */
#ifdef HAVE_LCMS #ifdef HAVE_LCMS
#include <lcms2.h> #include <lcms2.h>
#endif #endif
@ -43,21 +41,11 @@
#include "jpeg-icc.h" #include "jpeg-icc.h"
#include "jpeg-settings.h" #include "jpeg-settings.h"
#include "jpeg-load.h" #include "jpeg-load.h"
#ifdef HAVE_LIBEXIF
#include "jpeg-exif.h"
#include "gimpexif.h"
#endif
static void jpeg_load_resolution (gint32 image_ID, static void jpeg_load_resolution (gint32 image_ID,
struct jpeg_decompress_struct struct jpeg_decompress_struct
*cinfo); *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 void jpeg_load_sanitize_comment (gchar *comment);
static gpointer jpeg_load_cmyk_transform (guint8 *profile_data, static gpointer jpeg_load_cmyk_transform (guint8 *profile_data,
@ -90,9 +78,6 @@ load_image (const gchar *filename,
gint tile_height; gint tile_height;
gint scanlines; gint scanlines;
gint i, start, end; gint i, start, end;
#ifdef HAVE_LIBEXIF
gint orientation = 0;
#endif
#ifdef HAVE_LCMS #ifdef HAVE_LCMS
cmsHTRANSFORM cmyk_transform = NULL; cmsHTRANSFORM cmyk_transform = NULL;
#else #else
@ -265,9 +250,6 @@ load_image (const gchar *filename,
GString *comment_buffer = NULL; GString *comment_buffer = NULL;
guint8 *profile = NULL; guint8 *profile = NULL;
guint profile_size = 0; guint profile_size = 0;
#ifdef HAVE_LIBEXIF
ExifData *exif_data = NULL;
#endif
/* Step 5.0: save the original JPEG settings in a parasite */ /* Step 5.0: save the original JPEG settings in a parasite */
jpeg_detect_original_settings (&cinfo, image_ID); jpeg_detect_original_settings (&cinfo, image_ID);
@ -303,19 +285,10 @@ load_image (const gchar *filename,
#ifdef GIMP_UNSTABLE #ifdef GIMP_UNSTABLE
g_print ("jpeg-load: found EXIF block (%d bytes)\n", g_print ("jpeg-load: found EXIF block (%d bytes)\n",
(gint) (len - sizeof (JPEG_APP_HEADER_EXIF))); (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 #endif
} }
} }
#ifdef HAVE_LIBEXIF
if (!jpeg_load_exif_resolution (image_ID, exif_data))
#endif
jpeg_load_resolution (image_ID, &cinfo); jpeg_load_resolution (image_ID, &cinfo);
/* if we found any comments, then make a parasite for them */ /* if we found any comments, then make a parasite for them */
@ -334,55 +307,6 @@ load_image (const gchar *filename,
g_string_free (comment_buffer, TRUE); 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 */ /* Step 5.3: check for an embedded ICC profile in APP2 markers */
jpeg_icc_read_profile (&cinfo, &profile, &profile_size); jpeg_icc_read_profile (&cinfo, &profile, &profile_size);
@ -489,10 +413,6 @@ load_image (const gchar *filename,
gimp_image_insert_layer (image_ID, layer_ID, -1, 0); gimp_image_insert_layer (image_ID, layer_ID, -1, 0);
#ifdef HAVE_LIBEXIF
jpeg_exif_rotate_query (image_ID, orientation);
#endif
return image_ID; return image_ID;
} }
@ -537,53 +457,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 * 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 * instead of UTF-8. Some of these files may have been saved by older
@ -608,9 +481,6 @@ jpeg_load_sanitize_comment (gchar *comment)
} }
} }
#ifdef HAVE_LIBEXIF
typedef struct typedef struct
{ {
struct jpeg_source_mgr pub; /* public fields */ struct jpeg_source_mgr pub; /* public fields */
@ -627,7 +497,6 @@ init_source (j_decompress_ptr cinfo)
{ {
} }
static boolean static boolean
fill_input_buffer (j_decompress_ptr cinfo) fill_input_buffer (j_decompress_ptr cinfo)
{ {
@ -660,14 +529,16 @@ term_source (j_decompress_ptr cinfo)
} }
gint32 gint32
load_thumbnail_image (const gchar *filename, load_thumbnail_image (GFile *file,
gint *width, gint *width,
gint *height, gint *height,
GimpImageType *type, GimpImageType *type,
GError **error) GError **error)
{ {
gint32 volatile image_ID; gint32 volatile image_ID = -1;
ExifData *exif_data; GimpMetadata *metadata = NULL;
guint8 *thumbnail_buffer = NULL;
gint thumbnail_size;
gint32 layer_ID; gint32 layer_ID;
struct jpeg_decompress_struct cinfo; struct jpeg_decompress_struct cinfo;
struct my_error_mgr jerr; struct my_error_mgr jerr;
@ -683,20 +554,25 @@ load_thumbnail_image (const gchar *filename,
my_src_ptr src; my_src_ptr src;
FILE *infile; FILE *infile;
image_ID = -1; metadata = gimp_metadata_load_from_file (file, error);
exif_data = jpeg_exif_data_new_from_file (filename, NULL); if (! metadata)
if (! ((exif_data) && (exif_data->data) && (exif_data->size > 0)))
return -1; return -1;
orientation = jpeg_exif_get_orientation (exif_data); orientation = gexiv2_metadata_get_orientation (metadata);
if (! gexiv2_metadata_get_exif_thumbnail (metadata,
&thumbnail_buffer, &thumbnail_size))
{
g_object_unref (metadata);
return -1;
}
cinfo.err = jpeg_std_error (&jerr.pub); cinfo.err = jpeg_std_error (&jerr.pub);
jerr.pub.error_exit = my_error_exit; jerr.pub.error_exit = my_error_exit;
jerr.pub.output_message = my_output_message; jerr.pub.output_message = my_output_message;
gimp_progress_init_printf (_("Opening thumbnail for '%s'"), gimp_progress_init_printf (_("Opening thumbnail for '%s'"),
gimp_filename_to_utf8 (filename)); g_file_get_parse_name (file));
/* Establish the setjmp return context for my_error_exit to use. */ /* Establish the setjmp return context for my_error_exit to use. */
if (setjmp (jerr.setjmp_buffer)) if (setjmp (jerr.setjmp_buffer))
@ -710,15 +586,15 @@ load_thumbnail_image (const gchar *filename,
if (image_ID != -1) if (image_ID != -1)
gimp_image_delete (image_ID); gimp_image_delete (image_ID);
if (exif_data) if (metadata)
{ g_object_unref (metadata);
exif_data_unref (exif_data);
exif_data = NULL;
}
if (buffer) if (buffer)
g_object_unref (buffer); g_object_unref (buffer);
if (thumbnail_buffer)
g_free (thumbnail_buffer);
return -1; return -1;
} }
@ -740,11 +616,11 @@ load_thumbnail_image (const gchar *filename,
src->pub.resync_to_restart = jpeg_resync_to_restart; src->pub.resync_to_restart = jpeg_resync_to_restart;
src->pub.term_source = term_source; src->pub.term_source = term_source;
src->pub.bytes_in_buffer = exif_data->size; src->pub.bytes_in_buffer = thumbnail_size;
src->pub.next_input_byte = exif_data->data; src->pub.next_input_byte = thumbnail_buffer;
src->buffer = exif_data->data; src->buffer = thumbnail_buffer;
src->size = exif_data->size; src->size = thumbnail_size;
/* Step 3: read file parameters with jpeg_read_header() */ /* Step 3: read file parameters with jpeg_read_header() */
@ -808,11 +684,11 @@ load_thumbnail_image (const gchar *filename,
cinfo.output_components, cinfo.out_color_space, cinfo.output_components, cinfo.out_color_space,
cinfo.jpeg_color_space); cinfo.jpeg_color_space);
if (exif_data) if (metadata)
{ g_object_unref (metadata);
exif_data_unref (exif_data);
exif_data = NULL; if (thumbnail_buffer)
} g_free (thumbnail_buffer);
return -1; return -1;
break; break;
@ -822,7 +698,6 @@ load_thumbnail_image (const gchar *filename,
image_type); image_type);
gimp_image_undo_disable (image_ID); gimp_image_undo_disable (image_ID);
gimp_image_set_filename (image_ID, filename);
jpeg_load_resolution (image_ID, &cinfo); jpeg_load_resolution (image_ID, &cinfo);
@ -877,7 +752,9 @@ load_thumbnail_image (const gchar *filename,
*/ */
jpeg_destroy_decompress (&cinfo); jpeg_destroy_decompress (&cinfo);
g_object_unref (metadata);
g_object_unref (buffer); g_object_unref (buffer);
g_free (thumbnail_buffer);
/* free up the temporary buffers */ /* free up the temporary buffers */
g_free (rowbuf); g_free (rowbuf);
@ -897,17 +774,14 @@ load_thumbnail_image (const gchar *filename,
jerr.pub.error_exit = my_error_exit; jerr.pub.error_exit = my_error_exit;
jerr.pub.output_message = my_output_message; jerr.pub.output_message = my_output_message;
if ((infile = g_fopen (filename, "rb")) == NULL) if ((infile = g_fopen (g_file_get_path (file), "rb")) == NULL)
{ {
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno), g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
_("Could not open '%s' for reading: %s"), _("Could not open '%s' for reading: %s"),
gimp_filename_to_utf8 (filename), g_strerror (errno)); g_file_get_parse_name (file), g_strerror (errno));
if (exif_data) if (image_ID != -1)
{ gimp_image_delete (image_ID);
exif_data_unref (exif_data);
exif_data = NULL;
}
return -1; return -1;
} }
@ -924,12 +798,6 @@ load_thumbnail_image (const gchar *filename,
if (image_ID != -1) if (image_ID != -1)
gimp_image_delete (image_ID); gimp_image_delete (image_ID);
if (exif_data)
{
exif_data_unref (exif_data);
exif_data = NULL;
}
return -1; return -1;
} }
@ -958,22 +826,15 @@ load_thumbnail_image (const gchar *filename,
fclose (infile); fclose (infile);
if (exif_data) #if 0
{
exif_data_unref (exif_data);
exif_data = NULL;
}
jpeg_exif_rotate (image_ID, orientation); jpeg_exif_rotate (image_ID, orientation);
#endif
*type = layer_type; *type = layer_type;
return image_ID; return image_ID;
} }
#endif /* HAVE_LIBEXIF */
static gpointer static gpointer
jpeg_load_cmyk_transform (guint8 *profile_data, jpeg_load_cmyk_transform (guint8 *profile_data,
gsize profile_len) gsize profile_len)

View file

@ -23,15 +23,10 @@ gint32 load_image (const gchar *filename,
gboolean preview, gboolean preview,
GError **error); GError **error);
gint32 load_thumbnail_image (GFile *file,
#ifdef HAVE_LIBEXIF
gint32 load_thumbnail_image (const gchar *filename,
gint *width, gint *width,
gint *height, gint *height,
GimpImageType *type, GimpImageType *type,
GError **error); GError **error);
#endif /* HAVE_LIBEXIF */
#endif /* __JPEG_LOAD_H__ */ #endif /* __JPEG_LOAD_H__ */

View file

@ -33,10 +33,6 @@
#include <jpeglib.h> #include <jpeglib.h>
#include <jerror.h> #include <jerror.h>
#ifdef HAVE_LIBEXIF
#include <libexif/exif-data.h>
#endif /* HAVE_LIBEXIF */
#include <libgimp/gimp.h> #include <libgimp/gimp.h>
#include <libgimp/gimpui.h> #include <libgimp/gimpui.h>
@ -47,10 +43,6 @@
#include "jpeg-load.h" #include "jpeg-load.h"
#include "jpeg-save.h" #include "jpeg-save.h"
#include "jpeg-settings.h" #include "jpeg-settings.h"
#ifdef HAVE_LIBEXIF
#include "jpeg-exif.h"
#endif
#define SCALE_WIDTH 125 #define SCALE_WIDTH 125
@ -69,6 +61,7 @@
#define DEFAULT_EXIF TRUE #define DEFAULT_EXIF TRUE
#define DEFAULT_THUMBNAIL FALSE #define DEFAULT_THUMBNAIL FALSE
#define DEFAULT_XMP TRUE #define DEFAULT_XMP TRUE
#define DEFAULT_IPTC TRUE
#define DEFAULT_USE_ORIG_QUALITY FALSE #define DEFAULT_USE_ORIG_QUALITY FALSE
#define JPEG_DEFAULTS_PARASITE "jpeg-save-defaults" #define JPEG_DEFAULTS_PARASITE "jpeg-save-defaults"
@ -111,6 +104,7 @@ typedef struct
GtkWidget *save_exif; GtkWidget *save_exif;
GtkWidget *save_thumbnail; GtkWidget *save_thumbnail;
GtkWidget *save_xmp; GtkWidget *save_xmp;
GtkWidget *save_iptc;
GtkWidget *use_orig_quality; /*quant tables toggle*/ GtkWidget *use_orig_quality; /*quant tables toggle*/
} JpegSaveGui; } JpegSaveGui;
@ -127,15 +121,6 @@ static void use_orig_qual_changed (GtkWidget *toggle,
static void use_orig_qual_changed2 (GtkWidget *toggle, static void use_orig_qual_changed2 (GtkWidget *toggle,
GtkWidget *combo); 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_scale = NULL;
static GtkWidget *restart_markers_label = NULL; static GtkWidget *restart_markers_label = NULL;
@ -527,82 +512,6 @@ save_image (const gchar *filename,
*/ */
jpeg_start_compress (&cinfo, TRUE); 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 */ /* Step 4.1: Write the comment out - pw */
if (image_comment && *image_comment) if (image_comment && *image_comment)
{ {
@ -614,36 +523,7 @@ save_image (const gchar *filename,
(guchar *) image_comment, strlen (image_comment)); (guchar *) image_comment, strlen (image_comment));
} }
/* Step 4.2: Write the XMP packet in an APP1 marker */ /* Step 4.2: store the color profile if there is one */
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 */
parasite = gimp_image_get_parasite (orig_image_ID, "icc-profile"); parasite = gimp_image_get_parasite (orig_image_ID, "icc-profile");
if (parasite) if (parasite)
{ {
@ -913,7 +793,7 @@ save_dialog (void)
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
gtk_widget_show (frame); 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_col_spacings (GTK_TABLE (table), 6);
gtk_table_set_row_spacings (GTK_TABLE (table), 6); gtk_table_set_row_spacings (GTK_TABLE (table), 6);
gtk_table_set_col_spacing (GTK_TABLE (table), 1, 12); 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), gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
jsvals.progressive); jsvals.progressive);
#ifdef HAVE_LIBEXIF
pg.save_exif = toggle = pg.save_exif = toggle =
gtk_check_button_new_with_mnemonic (_("Save _EXIF data")); 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_table_attach (GTK_TABLE (table), toggle, 0, 1, 2, 3, GTK_FILL, 0, 0, 0);
gtk_widget_show (toggle); gtk_widget_show (toggle);
@ -1013,13 +893,11 @@ save_dialog (void)
G_CALLBACK (make_preview), G_CALLBACK (make_preview),
NULL); NULL);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), gtk_widget_set_sensitive (toggle, TRUE);
jsvals.save_exif && exif_data);
gtk_widget_set_sensitive (toggle, exif_data != NULL);
pg.save_thumbnail = toggle = pg.save_thumbnail = toggle =
gtk_check_button_new_with_mnemonic (_("Save _thumbnail")); 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_table_attach (GTK_TABLE (table), toggle, 0, 1, 3, 4, GTK_FILL, 0, 0, 0);
gtk_widget_show (toggle); gtk_widget_show (toggle);
@ -1030,13 +908,10 @@ save_dialog (void)
G_CALLBACK (make_preview), G_CALLBACK (make_preview),
NULL); NULL);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
jsvals.save_thumbnail);
#endif /* HAVE_LIBEXIF */
/* XMP metadata */ /* XMP metadata */
pg.save_xmp = toggle = pg.save_xmp = toggle =
gtk_check_button_new_with_mnemonic (_("Save _XMP data")); 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_table_attach (GTK_TABLE (table), toggle, 0, 1, 4, 5, GTK_FILL, 0, 0, 0);
gtk_widget_show (toggle); gtk_widget_show (toggle);
@ -1047,16 +922,29 @@ save_dialog (void)
G_CALLBACK (make_preview), G_CALLBACK (make_preview),
NULL); NULL);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), gtk_widget_set_sensitive (toggle, TRUE);
jsvals.save_xmp && has_metadata);
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 */ /* custom quantization tables - now used also for original quality */
pg.use_orig_quality = toggle = pg.use_orig_quality = toggle =
gtk_check_button_new_with_mnemonic (_("_Use quality settings from original " gtk_check_button_new_with_mnemonic (_("_Use quality settings from original "
"image")); "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); gtk_widget_show (toggle);
gimp_help_set_help_data (toggle, gimp_help_set_help_data (toggle,
@ -1266,13 +1154,9 @@ load_defaults (void)
jsvals.save_exif = DEFAULT_EXIF; jsvals.save_exif = DEFAULT_EXIF;
jsvals.save_thumbnail = DEFAULT_THUMBNAIL; jsvals.save_thumbnail = DEFAULT_THUMBNAIL;
jsvals.save_xmp = DEFAULT_XMP; jsvals.save_xmp = DEFAULT_XMP;
jsvals.save_iptc = DEFAULT_IPTC;
jsvals.use_orig_quality = DEFAULT_USE_ORIG_QUALITY; 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); parasite = gimp_get_parasite (JPEG_DEFAULTS_PARASITE);
if (! parasite) if (! parasite)
@ -1283,7 +1167,7 @@ load_defaults (void)
gimp_parasite_free (parasite); 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.quality,
&tmpvals.smoothing, &tmpvals.smoothing,
&tmpvals.optimize, &tmpvals.optimize,
@ -1296,12 +1180,15 @@ load_defaults (void)
&tmpvals.save_exif, &tmpvals.save_exif,
&tmpvals.save_thumbnail, &tmpvals.save_thumbnail,
&tmpvals.save_xmp, &tmpvals.save_xmp,
&tmpvals.use_orig_quality); &tmpvals.use_orig_quality,
&tmpvals.save_iptc);
tmpvals.subsmp = subsampling; tmpvals.subsmp = subsampling;
if (num_fields == 13) if (num_fields == 13 || num_fields == 14)
memcpy (&jsvals, &tmpvals, sizeof (tmpvals)); {
memcpy (&jsvals, &tmpvals, sizeof (tmpvals));
}
g_free (def_str); g_free (def_str);
} }
@ -1312,7 +1199,7 @@ save_defaults (void)
GimpParasite *parasite; GimpParasite *parasite;
gchar *def_str; 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.quality,
jsvals.smoothing, jsvals.smoothing,
jsvals.optimize, jsvals.optimize,
@ -1325,7 +1212,8 @@ save_defaults (void)
jsvals.save_exif, jsvals.save_exif,
jsvals.save_thumbnail, jsvals.save_thumbnail,
jsvals.save_xmp, jsvals.save_xmp,
jsvals.use_orig_quality); jsvals.use_orig_quality,
jsvals.save_iptc);
parasite = gimp_parasite_new (JPEG_DEFAULTS_PARASITE, parasite = gimp_parasite_new (JPEG_DEFAULTS_PARASITE,
GIMP_PARASITE_PERSISTENT, GIMP_PARASITE_PERSISTENT,
strlen (def_str), def_str); strlen (def_str), def_str);
@ -1350,11 +1238,10 @@ load_gui_defaults (JpegSaveGui *pg)
SET_ACTIVE_BTTN (progressive); SET_ACTIVE_BTTN (progressive);
SET_ACTIVE_BTTN (use_orig_quality); SET_ACTIVE_BTTN (use_orig_quality);
SET_ACTIVE_BTTN (preview); SET_ACTIVE_BTTN (preview);
#ifdef HAVE_LIBEXIF
SET_ACTIVE_BTTN (save_exif); SET_ACTIVE_BTTN (save_exif);
SET_ACTIVE_BTTN (save_thumbnail); SET_ACTIVE_BTTN (save_thumbnail);
#endif
SET_ACTIVE_BTTN (save_xmp); SET_ACTIVE_BTTN (save_xmp);
SET_ACTIVE_BTTN (save_iptc);
#undef SET_ACTIVE_BTTN #undef SET_ACTIVE_BTTN
@ -1447,218 +1334,3 @@ use_orig_qual_changed2 (GtkWidget *toggle,
gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), orig_subsmp); gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), orig_subsmp);
} }
} }
#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)
{
int width, height;
gint req_width, req_height, bpp, rbpp;
guchar *thumbnail_data = NULL;
struct jpeg_compress_struct cinfo;
struct my_error_mgr jerr;
my_dest_ptr dest;
JSAMPROW scanline[1];
guchar *buf = NULL;
gint i;
{
GeglBuffer *buffer = gimp_drawable_get_buffer (drawable_ID);
width = gegl_buffer_get_width (buffer);
height = gegl_buffer_get_height (buffer);
g_object_unref (buffer);
}
req_width = 196;
req_height = 196;
if (MIN (width, height) < 196)
req_width = req_height = MIN(width, height);
thumbnail_data = gimp_drawable_get_thumbnail_data (drawable_ID,
&req_width, &req_height,
&bpp);
if (! thumbnail_data)
return 0;
rbpp = bpp;
if ((bpp == 2) || (bpp == 4))
{
rbpp = bpp - 1;
}
buf = g_new (guchar, req_width * bpp);
tbuffer2 = g_new (guchar, 16384);
tbuffer_count = 0;
cinfo.err = jpeg_std_error (&jerr.pub);
jerr.pub.error_exit = my_error_exit;
/* Establish the setjmp return context for my_error_exit to use. */
if (setjmp (jerr.setjmp_buffer))
{
/* If we get here, the JPEG code has signaled an error.
* We need to clean up the JPEG object, free memory, and return.
*/
jpeg_destroy_compress (&cinfo);
if (thumbnail_data)
{
g_free (thumbnail_data);
thumbnail_data = NULL;
}
if (buf)
{
g_free (buf);
buf = NULL;
}
if (tbuffer2)
{
g_free (tbuffer2);
tbuffer2 = NULL;
}
return 0;
}
/* Now we can initialize the JPEG compression object. */
jpeg_create_compress (&cinfo);
if (cinfo.dest == NULL)
cinfo.dest = (struct jpeg_destination_mgr *)
(*cinfo.mem->alloc_small) ((j_common_ptr) &cinfo, JPOOL_PERMANENT,
sizeof(my_destination_mgr));
dest = (my_dest_ptr) cinfo.dest;
dest->pub.init_destination = init_destination;
dest->pub.empty_output_buffer = empty_output_buffer;
dest->pub.term_destination = term_destination;
dest->pub.next_output_byte = tbuffer2;
dest->pub.free_in_buffer = 16384;
dest->buffer = tbuffer2;
dest->size = 16384;
cinfo.input_components = rbpp;
cinfo.image_width = req_width;
cinfo.image_height = req_height;
/* colorspace of input image */
cinfo.in_color_space = (rbpp == 3) ? JCS_RGB : JCS_GRAYSCALE;
/* Now use the library's routine to set default compression parameters.
* (You must set at least cinfo.in_color_space before calling this,
* since the defaults depend on the source color space.)
*/
jpeg_set_defaults (&cinfo);
jpeg_set_quality (&cinfo, (gint) (quality + 0.5), jsvals.baseline);
/* Step 4: Start compressor */
/* TRUE ensures that we will write a complete interchange-JPEG file.
* Pass TRUE unless you are very sure of what you're doing.
*/
jpeg_start_compress (&cinfo, TRUE);
while (cinfo.next_scanline < (unsigned int) req_height)
{
for (i = 0; i < req_width; i++)
{
buf[(i * rbpp) + 0] = thumbnail_data[(cinfo.next_scanline * req_width * bpp) + (i * bpp) + 0];
if (rbpp == 3)
{
buf[(i * rbpp) + 1] = thumbnail_data[(cinfo.next_scanline * req_width * bpp) + (i * bpp) + 1];
buf[(i * rbpp) + 2] = thumbnail_data[(cinfo.next_scanline * req_width * bpp) + (i * bpp) + 2];
}
}
scanline[0] = buf;
jpeg_write_scanlines (&cinfo, scanline, 1);
}
/* Step 6: Finish compression */
jpeg_finish_compress (&cinfo);
/* Step 7: release JPEG compression object */
/* This is an important step since it will release a good deal of memory. */
jpeg_destroy_compress (&cinfo);
/* And we're done! */
if (thumbnail_data)
{
g_free (thumbnail_data);
thumbnail_data = NULL;
}
if (buf)
{
g_free (buf);
buf = NULL;
}
*thumbnail_buffer = tbuffer;
return tbuffer_count;
}
#endif /* HAVE_LIBEXIF */

View file

@ -32,6 +32,7 @@ typedef struct
gboolean save_exif; gboolean save_exif;
gboolean save_thumbnail; gboolean save_thumbnail;
gboolean save_xmp; gboolean save_xmp;
gboolean save_iptc;
gboolean use_orig_quality; gboolean use_orig_quality;
} JpegSaveVals; } JpegSaveVals;

View file

@ -50,10 +50,6 @@
#include <jpeglib.h> #include <jpeglib.h>
#ifdef HAVE_LIBEXIF
#include <libexif/exif-data.h>
#endif /* HAVE_LIBEXIF */
#include <libgimp/gimp.h> #include <libgimp/gimp.h>
#include "libgimp/stdplugins-intl.h" #include "libgimp/stdplugins-intl.h"

View file

@ -24,10 +24,6 @@
#include <jpeglib.h> #include <jpeglib.h>
#include <jerror.h> #include <jerror.h>
#ifdef HAVE_LIBEXIF
#include <libexif/exif-data.h>
#endif /* HAVE_LIBEXIF */
#include <libgimp/gimp.h> #include <libgimp/gimp.h>
#include <libgimp/gimpui.h> #include <libgimp/gimpui.h>
@ -37,11 +33,6 @@
#include "jpeg-settings.h" #include "jpeg-settings.h"
#include "jpeg-load.h" #include "jpeg-load.h"
#include "jpeg-save.h" #include "jpeg-save.h"
#ifdef HAVE_LIBEXIF
#include "jpeg-exif.h"
#include "gimpexif.h"
#endif
/* Declare local functions. /* Declare local functions.
*/ */
@ -66,10 +57,6 @@ JpegSubsampling orig_subsmp;
gint num_quant_tables; gint num_quant_tables;
#ifdef HAVE_LIBEXIF
ExifData *exif_data = NULL;
#endif
const GimpPlugInInfo PLUG_IN_INFO = const GimpPlugInInfo PLUG_IN_INFO =
{ {
NULL, /* init_proc */ NULL, /* init_proc */
@ -96,8 +83,6 @@ query (void)
{ GIMP_PDB_IMAGE, "image", "Output image" } { GIMP_PDB_IMAGE, "image", "Output image" }
}; };
#ifdef HAVE_LIBEXIF
static const GimpParamDef thumb_args[] = static const GimpParamDef thumb_args[] =
{ {
{ GIMP_PDB_STRING, "filename", "The name of the file to load" }, { GIMP_PDB_STRING, "filename", "The name of the file to load" },
@ -110,8 +95,6 @@ query (void)
{ GIMP_PDB_INT32, "image-height", "Height of full-sized image" } { GIMP_PDB_INT32, "image-height", "Height of full-sized image" }
}; };
#endif /* HAVE_LIBEXIF */
static const GimpParamDef save_args[] = static const GimpParamDef save_args[] =
{ {
{ GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" }, { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
@ -149,8 +132,6 @@ query (void)
"", "",
"6,string,JFIF,6,string,Exif"); "6,string,JFIF,6,string,Exif");
#ifdef HAVE_LIBEXIF
gimp_install_procedure (LOAD_THUMB_PROC, gimp_install_procedure (LOAD_THUMB_PROC,
"Loads a thumbnail from a JPEG image", "Loads a thumbnail from a JPEG image",
"Loads a thumbnail from a JPEG image (only if it exists)", "Loads a thumbnail from a JPEG image (only if it exists)",
@ -166,8 +147,6 @@ query (void)
gimp_register_thumbnail_loader (LOAD_PROC, LOAD_THUMB_PROC); gimp_register_thumbnail_loader (LOAD_PROC, LOAD_THUMB_PROC);
#endif /* HAVE_LIBEXIF */
gimp_install_procedure (SAVE_PROC, gimp_install_procedure (SAVE_PROC,
"saves files in the JPEG file format", "saves files in the JPEG file format",
"saves files in the lossy, widely supported JPEG format", "saves files in the lossy, widely supported JPEG format",
@ -237,6 +216,13 @@ run (const gchar *name,
if (image_ID != -1) if (image_ID != -1)
{ {
GFile *file = g_file_new_for_path (param[1].data.d_string);
gimp_image_metadata_load (image_ID, "image/jpeg", file,
load_interactive);
g_object_unref (file);
*nreturn_vals = 2; *nreturn_vals = 2;
values[1].type = GIMP_PDB_IMAGE; values[1].type = GIMP_PDB_IMAGE;
values[1].data.d_image = image_ID; values[1].data.d_image = image_ID;
@ -247,9 +233,6 @@ run (const gchar *name,
} }
} }
#ifdef HAVE_LIBEXIF
else if (strcmp (name, LOAD_THUMB_PROC) == 0) else if (strcmp (name, LOAD_THUMB_PROC) == 0)
{ {
if (nparams < 2) if (nparams < 2)
@ -258,14 +241,16 @@ run (const gchar *name,
} }
else else
{ {
const gchar *filename = param[0].data.d_string; GFile *file = g_file_new_for_path (param[0].data.d_string);
gint width = 0; gint width = 0;
gint height = 0; gint height = 0;
GimpImageType type = -1; GimpImageType type = -1;
image_ID = load_thumbnail_image (filename, &width, &height, &type, image_ID = load_thumbnail_image (file, &width, &height, &type,
&error); &error);
g_object_unref (file);
if (image_ID != -1) if (image_ID != -1)
{ {
*nreturn_vals = 6; *nreturn_vals = 6;
@ -286,9 +271,6 @@ run (const gchar *name,
} }
} }
} }
#endif /* HAVE_LIBEXIF */
else if (strcmp (name, SAVE_PROC) == 0) else if (strcmp (name, SAVE_PROC) == 0)
{ {
image_ID = orig_image_ID = param[1].data.d_int32; image_ID = orig_image_ID = param[1].data.d_int32;
@ -348,14 +330,7 @@ run (const gchar *name,
gimp_parasite_free (parasite); gimp_parasite_free (parasite);
} }
#ifdef HAVE_LIBEXIF /* load defaults from gimp parasite */
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 (); load_defaults ();
switch (run_mode) switch (run_mode)
@ -427,6 +402,7 @@ run (const gchar *name,
jsvals.save_exif = save_vals->save_exif; jsvals.save_exif = save_vals->save_exif;
jsvals.save_thumbnail = save_vals->save_thumbnail; jsvals.save_thumbnail = save_vals->save_thumbnail;
jsvals.save_xmp = save_vals->save_xmp; jsvals.save_xmp = save_vals->save_xmp;
jsvals.save_iptc = save_vals->save_iptc;
jsvals.use_orig_quality = save_vals->use_orig_quality; jsvals.use_orig_quality = save_vals->use_orig_quality;
gimp_parasite_free (parasite); gimp_parasite_free (parasite);
@ -511,9 +487,12 @@ run (const gchar *name,
if (status == GIMP_PDB_SUCCESS) if (status == GIMP_PDB_SUCCESS)
{ {
GimpMetadata *metadata;
/* pw - now we need to change the defaults to be whatever /* pw - now we need to change the defaults to be whatever
* was used to save this image. Dump the old parasites * was used to save this image. Dump the old parasites
* and add new ones. */ * and add new ones.
*/
gimp_image_detach_parasite (orig_image_ID, "gimp-comment"); gimp_image_detach_parasite (orig_image_ID, "gimp-comment");
if (image_comment && strlen (image_comment)) if (image_comment && strlen (image_comment))
@ -526,11 +505,36 @@ run (const gchar *name,
gimp_parasite_free (parasite); gimp_parasite_free (parasite);
} }
gimp_image_detach_parasite (orig_image_ID, "jpeg-save-options");
parasite = gimp_parasite_new ("jpeg-save-options", parasite = gimp_parasite_new ("jpeg-save-options",
0, sizeof (jsvals), &jsvals); 0, sizeof (jsvals), &jsvals);
gimp_image_attach_parasite (orig_image_ID, parasite); gimp_image_attach_parasite (orig_image_ID, parasite);
gimp_parasite_free (parasite); gimp_parasite_free (parasite);
/* write metadata */
metadata = gimp_image_metadata_save_prepare (image_ID,
"image/jpeg");
if (metadata)
{
GFile *file;
GimpMetadataSaveFlags flags = 0;
gimp_metadata_set_bits_per_sample (metadata, 8);
if (jsvals.save_exif) flags |= GIMP_METADATA_SAVE_EXIF;
if (jsvals.save_xmp) flags |= GIMP_METADATA_SAVE_XMP;
if (jsvals.save_iptc) flags |= GIMP_METADATA_SAVE_IPTC;
if (jsvals.save_thumbnail) flags |= GIMP_METADATA_SAVE_THUMBNAIL;
file = g_file_new_for_path (param[3].data.d_string);
gimp_image_metadata_save_finish (image_ID,
"image/jpeg",
metadata, file, flags,
NULL);
g_object_unref (file);
g_object_unref (metadata);
}
} }
} }
else else

View file

@ -94,9 +94,6 @@
#include <jpeglib.h> #include <jpeglib.h>
#include <jerror.h> #include <jerror.h>
#ifdef HAVE_LIBEXIF
#include <libexif/exif-data.h>
#endif /* HAVE_LIBEXIF */
#ifdef HAVE_IPTCDATA #ifdef HAVE_IPTCDATA
#include <libiptcdata/iptc-data.h> #include <libiptcdata/iptc-data.h>
#endif /* HAVE_IPTCDATA */ #endif /* HAVE_IPTCDATA */
@ -196,11 +193,6 @@ static gint load_resource_1058 (const PSDimageres *res_a,
FILE *f, FILE *f,
GError **error); 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, static gint load_resource_2000 (const PSDimageres *res_a,
const gint32 image_id, const gint32 image_id,
FILE *f, FILE *f,
@ -353,7 +345,6 @@ load_image_resource (PSDimageres *res_a,
break; break;
case PSD_XMP_DATA: case PSD_XMP_DATA:
load_resource_1060 (res_a, image_id, f, error);
break; break;
default: default:
@ -1209,21 +1200,7 @@ load_resource_1058 (const PSDimageres *res_a,
FILE *f, FILE *f,
GError **error) 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; gchar *name;
#endif /* HAVE_LIBEXIF */
GimpParasite *parasite; GimpParasite *parasite;
gchar *res_data; gchar *res_data;
@ -1238,79 +1215,6 @@ load_resource_1058 (const PSDimageres *res_a,
return -1; 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 */ /* Store resource data as a standard psd parasite */
IFDBG (2) g_debug ("Processing exif data as psd parasite"); IFDBG (2) g_debug ("Processing exif data as psd parasite");
name = g_strdup_printf ("psd-image-resource-%.4s-%.4x", name = g_strdup_printf ("psd-image-resource-%.4s-%.4x",
@ -1322,45 +1226,10 @@ load_resource_1058 (const PSDimageres *res_a,
gimp_parasite_free (parasite); gimp_parasite_free (parasite);
g_free (name); g_free (name);
#endif /* HAVE_LIBEXIF */
g_free (res_data); g_free (res_data);
return 0; 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;
}
static gint static gint
load_resource_2000 (const PSDimageres *res_a, load_resource_2000 (const PSDimageres *res_a,
const gint32 image_id, const gint32 image_id,

View file

@ -293,6 +293,33 @@ run (const gchar *name,
if (save_image (param[3].data.d_string, image_id, &error)) if (save_image (param[3].data.d_string, image_id, &error))
{ {
GimpMetadata *metadata;
metadata = gimp_image_metadata_save_prepare (image_id,
"image/x-psd");
if (metadata)
{
GFile *file;
GimpMetadataSaveFlags flags = 0;
gimp_metadata_set_bits_per_sample (metadata, 8);
if (TRUE) flags |= GIMP_METADATA_SAVE_EXIF;
if (TRUE) flags |= GIMP_METADATA_SAVE_XMP;
if (TRUE) flags |= GIMP_METADATA_SAVE_IPTC;
if (TRUE) flags |= GIMP_METADATA_SAVE_THUMBNAIL;
file = g_file_new_for_path (param[3].data.d_string);
gimp_image_metadata_save_finish (image_id,
"image/x-psd",
metadata, file, flags,
NULL);
g_object_unref (file);
g_object_unref (metadata);
}
values[0].data.d_status = GIMP_PDB_SUCCESS; values[0].data.d_status = GIMP_PDB_SUCCESS;
} }
else else

View file

@ -23,6 +23,7 @@
#include <string.h> #include <string.h>
#include <libgimp/gimp.h> #include <libgimp/gimp.h>
#include <libgimp/gimpui.h>
#include "psd.h" #include "psd.h"
#include "psd-load.h" #include "psd-load.h"
@ -34,6 +35,7 @@
#include "libgimp/stdplugins-intl.h" #include "libgimp/stdplugins-intl.h"
/* Local function prototypes */ /* Local function prototypes */
static void query (void); static void query (void);
@ -168,6 +170,7 @@ run (const gchar *name,
GimpParam **return_vals) GimpParam **return_vals)
{ {
static GimpParam values[4]; static GimpParam values[4];
GimpRunMode run_mode;
GimpPDBStatusType status = GIMP_PDB_SUCCESS; GimpPDBStatusType status = GIMP_PDB_SUCCESS;
gint32 image_ID; gint32 image_ID;
GError *error = NULL; GError *error = NULL;
@ -176,6 +179,8 @@ run (const gchar *name,
GimpExportReturn export = GIMP_EXPORT_CANCEL; GimpExportReturn export = GIMP_EXPORT_CANCEL;
#endif /* PSD_SAVE */ #endif /* PSD_SAVE */
run_mode = param[0].data.d_int32;
INIT_I18N (); INIT_I18N ();
*nreturn_vals = 1; *nreturn_vals = 1;
@ -187,10 +192,31 @@ run (const gchar *name,
/* File load */ /* File load */
if (strcmp (name, LOAD_PROC) == 0) if (strcmp (name, LOAD_PROC) == 0)
{ {
gboolean interactive;
switch (run_mode)
{
case GIMP_RUN_INTERACTIVE:
case GIMP_RUN_WITH_LAST_VALS:
gimp_ui_init (PLUG_IN_BINARY, FALSE);
interactive = TRUE;
break;
default:
interactive = FALSE;
break;
}
image_ID = load_image (param[1].data.d_string, &error); image_ID = load_image (param[1].data.d_string, &error);
if (image_ID != -1) if (image_ID != -1)
{ {
GFile *file = g_file_new_for_path (param[1].data.d_string);
gimp_image_metadata_load (image_ID, "image/x-psd", file,
interactive);
g_object_unref (file);
*nreturn_vals = 2; *nreturn_vals = 2;
values[1].type = GIMP_PDB_IMAGE; values[1].type = GIMP_PDB_IMAGE;
values[1].data.d_image = image_ID; values[1].data.d_image = image_ID;
@ -237,9 +263,6 @@ run (const gchar *name,
/* File save */ /* File save */
else if (strcmp (name, SAVE_PROC) == 0) else if (strcmp (name, SAVE_PROC) == 0)
{ {
GimpRunMode run_mode;
run_mode = param[0].data.d_int32;
image_ID = param[1].data.d_int32; image_ID = param[1].data.d_int32;
drawable_ID = param[2].data.d_int32; drawable_ID = param[2].data.d_int32;

View file

@ -2,6 +2,8 @@ uidatadir = $(gimpdatadir)/ui/plug-ins
uidata_DATA = \ uidata_DATA = \
plug-in-file-gif.ui \ 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) EXTRA_DIST = $(uidata_DATA)

View file

@ -1,20 +1,18 @@
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<interface> <interface>
<!-- interface-requires gtk+ 2.12 --> <requires lib="gtk+" version="2.24"/>
<!-- interface-naming-policy project-wide --> <!-- interface-naming-policy project-wide -->
<object class="GtkAdjustment" id="compression-level"> <object class="GtkAdjustment" id="compression-level">
<property name="upper">9</property> <property name="upper">9</property>
<!-- NOTE: Set value _after_ upper so the value don't get clamped -->
<property name="value">9</property> <property name="value">9</property>
<property name="step_increment">1</property> <property name="step_increment">1</property>
<property name="page_increment">1</property> <property name="page_increment">1</property>
</object> </object>
<object class="GtkTable" id="table"> <object class="GtkTable" id="table">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property>
<property name="border_width">12</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="n_columns">3</property>
<property name="column_spacing">6</property> <property name="column_spacing">6</property>
<property name="row_spacing">6</property> <property name="row_spacing">6</property>
@ -25,6 +23,7 @@
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">False</property> <property name="receives_default">False</property>
<property name="use_underline">True</property> <property name="use_underline">True</property>
<property name="xalign">0</property>
<property name="draw_indicator">True</property> <property name="draw_indicator">True</property>
</object> </object>
<packing> <packing>
@ -38,6 +37,7 @@
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">False</property> <property name="receives_default">False</property>
<property name="use_underline">True</property> <property name="use_underline">True</property>
<property name="xalign">0</property>
<property name="draw_indicator">True</property> <property name="draw_indicator">True</property>
</object> </object>
<packing> <packing>
@ -52,6 +52,7 @@
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">False</property> <property name="receives_default">False</property>
<property name="xalign">0</property>
<property name="draw_indicator">True</property> <property name="draw_indicator">True</property>
</object> </object>
<packing> <packing>
@ -67,6 +68,7 @@
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">False</property> <property name="receives_default">False</property>
<property name="use_underline">True</property> <property name="use_underline">True</property>
<property name="xalign">0</property>
<property name="draw_indicator">True</property> <property name="draw_indicator">True</property>
</object> </object>
<packing> <packing>
@ -82,6 +84,7 @@
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">False</property> <property name="receives_default">False</property>
<property name="use_underline">True</property> <property name="use_underline">True</property>
<property name="xalign">0</property>
<property name="draw_indicator">True</property> <property name="draw_indicator">True</property>
</object> </object>
<packing> <packing>
@ -97,6 +100,7 @@
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">False</property> <property name="receives_default">False</property>
<property name="use_underline">True</property> <property name="use_underline">True</property>
<property name="xalign">0</property>
<property name="draw_indicator">True</property> <property name="draw_indicator">True</property>
</object> </object>
<packing> <packing>
@ -112,6 +116,7 @@
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">False</property> <property name="receives_default">False</property>
<property name="use_underline">True</property> <property name="use_underline">True</property>
<property name="xalign">0</property>
<property name="draw_indicator">True</property> <property name="draw_indicator">True</property>
</object> </object>
<packing> <packing>
@ -128,6 +133,7 @@
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">False</property> <property name="receives_default">False</property>
<property name="use_underline">True</property> <property name="use_underline">True</property>
<property name="xalign">0</property>
<property name="draw_indicator">True</property> <property name="draw_indicator">True</property>
</object> </object>
<packing> <packing>
@ -139,6 +145,7 @@
<child> <child>
<object class="GtkLabel" id="compression-level-label"> <object class="GtkLabel" id="compression-level-label">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property> <property name="xalign">0</property>
<property name="label" translatable="yes">Co_mpression level:</property> <property name="label" translatable="yes">Co_mpression level:</property>
<property name="use_underline">True</property> <property name="use_underline">True</property>
@ -147,7 +154,7 @@
<packing> <packing>
<property name="top_attach">8</property> <property name="top_attach">8</property>
<property name="bottom_attach">9</property> <property name="bottom_attach">9</property>
<property name="x_options"></property> <property name="x_options"/>
</packing> </packing>
</child> </child>
<child> <child>
@ -170,7 +177,9 @@
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="has_tooltip">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="tooltip_text" translatable="yes">Choose a high compression level for small file size</property>
<property name="invisible_char">&#x25CF;</property> <property name="invisible_char">●</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="adjustment">compression-level</property> <property name="adjustment">compression-level</property>
<property name="numeric">True</property> <property name="numeric">True</property>
</object> </object>
@ -179,12 +188,13 @@
<property name="right_attach">3</property> <property name="right_attach">3</property>
<property name="top_attach">8</property> <property name="top_attach">8</property>
<property name="bottom_attach">9</property> <property name="bottom_attach">9</property>
<property name="x_options"></property> <property name="x_options"/>
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkHButtonBox" id="hbuttonbox"> <object class="GtkHButtonBox" id="hbuttonbox">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">6</property> <property name="spacing">6</property>
<property name="layout_style">start</property> <property name="layout_style">start</property>
<child> <child>
@ -216,6 +226,94 @@
</packing> </packing>
</child> </child>
</object> </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="xalign">0</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="xalign">0</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="xalign">0</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="xalign">0</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> <packing>
<property name="right_attach">3</property> <property name="right_attach">3</property>
<property name="top_attach">9</property> <property name="top_attach">9</property>

View file

@ -0,0 +1,404 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk+" version="2.24"/>
<!-- interface-naming-policy project-wide -->
<object class="GtkVBox" id="tiff_export_vbox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkFrame" id="frame1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label_xalign">0</property>
<property name="shadow_type">none</property>
<child>
<object class="GtkAlignment" id="alignment1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="left_padding">12</property>
<child>
<object class="GtkHBox" id="hbox1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkVBox" id="vbox2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">3</property>
<child>
<object class="GtkRadioButton" id="cmp_none">
<property name="label" translatable="yes">no compression</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkRadioButton" id="cmp_lzw">
<property name="label" translatable="yes">LZW</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
<property name="group">cmp_none</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkRadioButton" id="cmp_packbits">
<property name="label" translatable="yes">PackBits</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
<property name="group">cmp_none</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkRadioButton" id="cmp_deflate">
<property name="label" translatable="yes">Deflate</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
<property name="group">cmp_none</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkRadioButton" id="cmp_jpeg">
<property name="label" translatable="yes">JPEG</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
<property name="group">cmp_none</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">4</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="padding">3</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkVBox" id="vbox4">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkRadioButton" id="cmp_g3">
<property name="label" translatable="yes">CCITT Group 3 fax</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
<property name="group">cmp_none</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkRadioButton" id="cmp_g4">
<property name="label" translatable="yes">CCITT Group _4 fax</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
<property name="group">cmp_none</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="padding">3</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
</child>
<child type="label">
<object class="GtkLabel" id="compresslabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">&lt;b&gt;Compression&lt;/b&gt;</property>
<property name="use_markup">True</property>
</object>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="padding">3</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkHSeparator" id="hseparator3">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkVBox" id="vbox7">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkCheckButton" id="sv_alpha">
<property name="label" translatable="yes">Save color values from transparent pixels</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="padding">6</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkHSeparator" id="hseparator2">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkFrame" id="frame2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label_xalign">0</property>
<property name="shadow_type">none</property>
<child>
<object class="GtkAlignment" id="alignment2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="left_padding">12</property>
<child>
<object class="GtkVBox" id="vbox5">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkEntry" id="commentfield">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">●</property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="padding">3</property>
<property name="position">0</property>
</packing>
</child>
</object>
</child>
</object>
</child>
<child type="label">
<object class="GtkLabel" id="commentlabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">&lt;b&gt;Comment&lt;/b&gt;</property>
<property name="use_markup">True</property>
</object>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="padding">3</property>
<property name="position">4</property>
</packing>
</child>
<child>
<object class="GtkHSeparator" id="hseparator1">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">5</property>
</packing>
</child>
<child>
<object class="GtkExpander" id="expander1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<child>
<object class="GtkHBox" id="hbox2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkVBox" id="vbox3">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkCheckButton" id="sv_exif">
<property name="label" translatable="yes">save EXIF data</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="sv_xmp">
<property name="label" translatable="yes">save XMP data</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkVBox" id="vbox6">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkCheckButton" id="sv_iptc">
<property name="label" translatable="yes">save IPTC data</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="sv_thumbnail">
<property name="label" translatable="yes">save thumbnail</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
<child type="label">
<object class="GtkLabel" id="adv_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Advanced</property>
</object>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="padding">3</property>
<property name="position">6</property>
</packing>
</child>
</object>
</interface>

View file

@ -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>

View file

@ -11,6 +11,7 @@ libgimp/gimpfontselectbutton.c
libgimp/gimpgradientmenu.c libgimp/gimpgradientmenu.c
libgimp/gimpgradientselectbutton.c libgimp/gimpgradientselectbutton.c
libgimp/gimpmenu.c libgimp/gimpmenu.c
libgimp/gimpmetadata.c
libgimp/gimppalettemenu.c libgimp/gimppalettemenu.c
libgimp/gimppaletteselectbutton.c libgimp/gimppaletteselectbutton.c
libgimp/gimppatternmenu.c libgimp/gimppatternmenu.c
@ -22,6 +23,7 @@ libgimp/gimpunitcache.c
libgimpbase/gimpbaseenums.c libgimpbase/gimpbaseenums.c
libgimpbase/gimpcpuaccel.c libgimpbase/gimpcpuaccel.c
libgimpbase/gimpmemsize.c libgimpbase/gimpmemsize.c
libgimpbase/gimpmetadata.c
libgimpbase/gimputils.c libgimpbase/gimputils.c
libgimpconfig/gimpcolorconfig.c libgimpconfig/gimpcolorconfig.c

View file

@ -96,6 +96,7 @@ plug-ins/common/lens-apply.c
plug-ins/common/lens-flare.c plug-ins/common/lens-flare.c
plug-ins/common/mail.c plug-ins/common/mail.c
plug-ins/common/max-rgb.c plug-ins/common/max-rgb.c
plug-ins/common/metadata.c
plug-ins/common/newsprint.c plug-ins/common/newsprint.c
plug-ins/common/nl-filter.c plug-ins/common/nl-filter.c
plug-ins/common/noise-rgb.c plug-ins/common/noise-rgb.c
@ -141,7 +142,6 @@ plug-ins/file-ico/ico-dialog.c
plug-ins/file-ico/ico-load.c plug-ins/file-ico/ico-load.c
plug-ins/file-ico/ico-save.c plug-ins/file-ico/ico-save.c
plug-ins/file-ico/ico.c plug-ins/file-ico/ico.c
plug-ins/file-jpeg/jpeg-exif.c
plug-ins/file-jpeg/jpeg-load.c plug-ins/file-jpeg/jpeg-load.c
plug-ins/file-jpeg/jpeg-save.c plug-ins/file-jpeg/jpeg-save.c
plug-ins/file-jpeg/jpeg.c plug-ins/file-jpeg/jpeg.c
@ -187,6 +187,8 @@ plug-ins/gimpressionist/sizemap.c
plug-ins/gimpressionist/utils.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-gif.ui
[type: gettext/glade]plug-ins/ui/plug-in-file-png.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/gradient-flare/gradient-flare.c
plug-ins/help-browser/dialog.c plug-ins/help-browser/dialog.c
plug-ins/help-browser/help-browser.c plug-ins/help-browser/help-browser.c

View file

@ -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, TRUE);
if (metadata)
g_object_unref (metadata);
}
CODE
);
}
sub image_clean_all { sub image_clean_all {
$blurb = 'Set the image dirty count to 0.'; $blurb = 'Set the image dirty count to 0.';
@ -2988,6 +3043,7 @@ CODE
"libgimpbase/gimpbase.h" "libgimpbase/gimpbase.h"
"core/gimp.h" "core/gimp.h"
"core/gimpcontainer.h" "core/gimpcontainer.h"
"core/gimpimage-metadata.h"
"core/gimpprogress.h" "core/gimpprogress.h"
"core/gimptempbuf.h" "core/gimptempbuf.h"
"core/gimpunit.h" "core/gimpunit.h"
@ -3029,6 +3085,7 @@ CODE
image_flatten image_merge_visible_layers image_merge_down image_flatten image_merge_visible_layers image_merge_down
image_add_layer_mask image_remove_layer_mask image_add_layer_mask image_remove_layer_mask
image_get_colormap image_set_colormap image_get_colormap image_set_colormap
image_get_metadata image_set_metadata
image_clean_all image_is_dirty image_clean_all image_is_dirty
image_thumbnail image_thumbnail
image_get_active_layer image_set_active_layer image_get_active_layer image_set_active_layer
@ -3060,7 +3117,7 @@ CODE
# image_add_layer_mask and image_remove_layer_mask. # image_add_layer_mask and image_remove_layer_mask.
# If adding or removing functions, make sure the range below is # If adding or removing functions, make sure the range below is
# updated correctly! # updated correctly!
%exports = (app => [@procs], lib => [@procs[0..44,47..85]]); %exports = (app => [@procs], lib => [@procs[0..44,47..87]]);
$desc = 'Image'; $desc = 'Image';
$doc_title = 'gimpimage'; $doc_title = 'gimpimage';