[gimp] New GimpMetadata as subclass of GExiv2Metadata
- From: Hartmut Kuhse <hartmutkuhse src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp] New GimpMetadata as subclass of GExiv2Metadata
- Date: Tue, 3 Jan 2017 18:30:46 +0000 (UTC)
commit 3ab08c8bfddb03ec4fc9a994084198228fb4f951
Author: Hartmut Kuhse <hk_priv gmx de>
Date: Tue Jan 3 19:19:10 2017 +0100
New GimpMetadata as subclass of GExiv2Metadata
app/core/core-enums.c | 6 +-
app/core/core-enums.h | 3 +-
app/core/core-types.h | 1 +
app/core/gimp.c | 2 +
app/core/gimpimage-merge.c | 23 +-
app/core/gimpimage-undo-push.c | 16 +
app/core/gimpimage-undo-push.h | 4 +-
app/core/gimpitem.c | 40 +
app/core/gimpitem.h | 4 +
app/core/gimpitemundo.c | 74 +-
app/core/gimpitemundo.h | 2 +
app/core/gimplayer.c | 12 +-
app/pdb/internal-procs.c | 2 +-
app/pdb/item-cmds.c | 121 +
app/xcf/xcf-load.c | 142 +-
app/xcf/xcf-save.c | 72 +-
configure.ac | 2 +-
icons/Color/16/gimp-image-metadata.png | Bin 0 -> 569 bytes
icons/Color/24/gimp-image-metadata.png | Bin 0 -> 850 bytes
icons/Color/24/gimp-image-metadata.svg | 275 ++
icons/Color/48/gimp-image-metadata.png | Bin 0 -> 1903 bytes
icons/Color/scalable/gimp-image-metadata.svg | 268 ++
icons/Legacy/16/gimp-image-metadata.png | Bin 0 -> 569 bytes
icons/Legacy/24/gimp-image-metadata.png | Bin 0 -> 850 bytes
icons/Legacy/48/gimp-image-metadata.png | Bin 0 -> 1903 bytes
icons/Symbolic-Inverted/24/gimp-image-metadata.svg | 189 ++
icons/Symbolic/16/gimp-image-metadata.png | Bin 0 -> 461 bytes
icons/Symbolic/24/gimp-image-metadata.png | Bin 0 -> 739 bytes
icons/Symbolic/24/gimp-image-metadata.svg | 189 ++
icons/Symbolic/48/gimp-image-metadata.png | Bin 0 -> 1367 bytes
icons/Symbolic/64/gimp-image-metadata.png | Bin 0 -> 1853 bytes
icons/Symbolic/scalable/gimp-image-metadata.svg | 189 ++
icons/icon-list.mk | 5 +
libgimp/Makefile.am | 3 +
libgimp/gimp.def | 2 +
libgimp/gimp.h | 1 +
libgimp/gimpimagemetadata.c | 420 ++--
libgimp/gimpimagemetadata.h | 1 +
libgimp/gimpitem.c | 86 +
libgimp/gimpitem.h | 40 +
libgimp/gimpitem_pdb.c | 62 +
libgimp/gimpitem_pdb.h | 89 +-
libgimpbase/Makefile.am | 6 +
libgimpbase/gimpattribute.c | 2002 ++++++++++++
libgimpbase/gimpattribute.h | 97 +
libgimpbase/gimpbase.def | 42 +-
libgimpbase/gimpbase.h | 1 +
libgimpbase/gimpbasetypes.h | 3 +-
libgimpbase/gimpmetadata.c | 3330 ++++++++++++++++----
libgimpbase/gimpmetadata.h | 144 +-
libgimpbase/gimprational.c | 301 ++
libgimpbase/gimprational.h | 68 +
libgimpwidgets/gimpicons.c | 1 +
libgimpwidgets/gimpicons.h | 1 +
plug-ins/common/Makefile.am | 59 +-
plug-ins/common/file-jp2-load.c | 8 +-
plug-ins/common/file-png.c | 23 +-
plug-ins/common/gimprc.common | 3 +-
plug-ins/common/plugin-defs.pl | 3 +-
plug-ins/file-jpeg/jpeg-load.c | 13 +-
plug-ins/file-jpeg/jpeg-load.h | 1 +
plug-ins/file-jpeg/jpeg-save.c | 3 +-
plug-ins/file-jpeg/jpeg.c | 8 +-
plug-ins/file-psd/psd.c | 6 +-
plug-ins/file-tiff/file-tiff-load.c | 3 +
plug-ins/file-tiff/file-tiff-load.h | 1 +
plug-ins/file-tiff/file-tiff.c | 18 +-
plug-ins/ui/Makefile.am | 4 +-
plug-ins/ui/plug-in-metadata.ui | 908 ------
po-plug-ins/POTFILES.in | 10 +-
tools/pdbgen/pdb/item.pdb | 78 +-
71 files changed, 7566 insertions(+), 1924 deletions(-)
---
diff --git a/app/core/core-enums.c b/app/core/core-enums.c
index cbdcf44..ac60a05 100644
--- a/app/core/core-enums.c
+++ b/app/core/core-enums.c
@@ -853,14 +853,15 @@ gimp_undo_type_get_type (void)
{ GIMP_UNDO_IMAGE_SIZE, "GIMP_UNDO_IMAGE_SIZE", "image-size" },
{ GIMP_UNDO_IMAGE_RESOLUTION, "GIMP_UNDO_IMAGE_RESOLUTION", "image-resolution" },
{ 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_COLOR_MANAGED, "GIMP_UNDO_IMAGE_COLOR_MANAGED", "image-color-managed" },
+ { GIMP_UNDO_IMAGE_METADATA, "GIMP_UNDO_IMAGE_METADATA", "image-metadata" },
{ GIMP_UNDO_GUIDE, "GIMP_UNDO_GUIDE", "guide" },
{ GIMP_UNDO_SAMPLE_POINT, "GIMP_UNDO_SAMPLE_POINT", "sample-point" },
{ GIMP_UNDO_DRAWABLE, "GIMP_UNDO_DRAWABLE", "drawable" },
{ GIMP_UNDO_DRAWABLE_MOD, "GIMP_UNDO_DRAWABLE_MOD", "drawable-mod" },
{ GIMP_UNDO_MASK, "GIMP_UNDO_MASK", "mask" },
+ { GIMP_UNDO_ITEM_METADATA, "GIMP_UNDO_ITEM_METADATA", "item-metadata" },
{ GIMP_UNDO_ITEM_REORDER, "GIMP_UNDO_ITEM_REORDER", "item-reorder" },
{ GIMP_UNDO_ITEM_RENAME, "GIMP_UNDO_ITEM_RENAME", "item-rename" },
{ GIMP_UNDO_ITEM_DISPLACE, "GIMP_UNDO_ITEM_DISPLACE", "item-displace" },
@@ -946,14 +947,15 @@ gimp_undo_type_get_type (void)
{ GIMP_UNDO_IMAGE_SIZE, NC_("undo-type", "Image size"), NULL },
{ GIMP_UNDO_IMAGE_RESOLUTION, NC_("undo-type", "Image resolution change"), 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_COLOR_MANAGED, NC_("undo-type", "Change color managed state"), NULL },
+ { GIMP_UNDO_IMAGE_METADATA, "GIMP_UNDO_IMAGE_METADATA", NULL },
{ GIMP_UNDO_GUIDE, NC_("undo-type", "Guide"), NULL },
{ GIMP_UNDO_SAMPLE_POINT, NC_("undo-type", "Sample Point"), NULL },
{ GIMP_UNDO_DRAWABLE, NC_("undo-type", "Layer/Channel"), NULL },
{ GIMP_UNDO_DRAWABLE_MOD, NC_("undo-type", "Layer/Channel modification"), NULL },
{ GIMP_UNDO_MASK, NC_("undo-type", "Selection mask"), NULL },
+ { GIMP_UNDO_ITEM_METADATA, "GIMP_UNDO_ITEM_METADATA", NULL },
{ GIMP_UNDO_ITEM_REORDER, NC_("undo-type", "Reorder item"), NULL },
{ GIMP_UNDO_ITEM_RENAME, NC_("undo-type", "Rename item"), NULL },
{ GIMP_UNDO_ITEM_DISPLACE, NC_("undo-type", "Move item"), NULL },
diff --git a/app/core/core-enums.h b/app/core/core-enums.h
index 95616e6..5b7e922 100644
--- a/app/core/core-enums.h
+++ b/app/core/core-enums.h
@@ -425,14 +425,15 @@ typedef enum /*< pdb-skip >*/
GIMP_UNDO_IMAGE_SIZE, /*< desc="Image size" >*/
GIMP_UNDO_IMAGE_RESOLUTION, /*< desc="Image resolution change" >*/
GIMP_UNDO_IMAGE_GRID, /*< desc="Grid" >*/
- GIMP_UNDO_IMAGE_METADATA, /*< desc="Change metadata" >*/
GIMP_UNDO_IMAGE_COLORMAP, /*< desc="Change indexed palette" >*/
GIMP_UNDO_IMAGE_COLOR_MANAGED, /*< desc="Change color managed state" >*/
+ GIMP_UNDO_IMAGE_METADATA, /*< desc="Change metadata >*/
GIMP_UNDO_GUIDE, /*< desc="Guide" >*/
GIMP_UNDO_SAMPLE_POINT, /*< desc="Sample Point" >*/
GIMP_UNDO_DRAWABLE, /*< desc="Layer/Channel" >*/
GIMP_UNDO_DRAWABLE_MOD, /*< desc="Layer/Channel modification" >*/
GIMP_UNDO_MASK, /*< desc="Selection mask" >*/
+ GIMP_UNDO_ITEM_METADATA, /*< desc="Change metadata >*/
GIMP_UNDO_ITEM_REORDER, /*< desc="Reorder item" >*/
GIMP_UNDO_ITEM_RENAME, /*< desc="Rename item" >*/
GIMP_UNDO_ITEM_DISPLACE, /*< desc="Move item" >*/
diff --git a/app/core/core-types.h b/app/core/core-types.h
index 11eaa62..57921bb 100644
--- a/app/core/core-types.h
+++ b/app/core/core-types.h
@@ -157,6 +157,7 @@ typedef struct _GimpGroupLayer GimpGroupLayer;
typedef struct _GimpUndo GimpUndo;
typedef struct _GimpUndoStack GimpUndoStack;
typedef struct _GimpUndoAccumulator GimpUndoAccumulator;
+typedef struct _GimpViewableUndo GimpViewableUndo;
/* Symmetry transformations */
diff --git a/app/core/gimp.c b/app/core/gimp.c
index 25ee206..ff02bbd 100644
--- a/app/core/gimp.c
+++ b/app/core/gimp.c
@@ -38,6 +38,8 @@
#include "plug-in/gimppluginmanager.h"
#include "plug-in/gimppluginmanager-restore.h"
+#include "gui/gimpdbusservice-generated.h"
+
#include "paint/gimp-paint.h"
#include "text/gimp-fonts.h"
diff --git a/app/core/gimpimage-merge.c b/app/core/gimpimage-merge.c
index 3d97d13..5e1e362 100644
--- a/app/core/gimpimage-merge.c
+++ b/app/core/gimpimage-merge.c
@@ -40,7 +40,9 @@
#include "gimpgrouplayer.h"
#include "gimpimage.h"
#include "gimpimage-merge.h"
+#include "gimpimage-metadata.h"
#include "gimpimage-undo.h"
+#include "gimpitem.h"
#include "gimpitemstack.h"
#include "gimplayer-floating-selection.h"
#include "gimplayer-new.h"
@@ -428,6 +430,7 @@ gimp_image_merge_layers (GimpImage *image,
GimpLayer *layer;
GimpLayer *bottom_layer;
GimpParasiteList *parasites;
+ GimpMetadata *metadata;
gint count;
gint x1, y1, x2, y2;
gint off_x, off_y;
@@ -522,8 +525,8 @@ gimp_image_merge_layers (GimpImage *image,
(gimp_drawable_is_indexed (GIMP_DRAWABLE (layer)) &&
! gimp_drawable_has_alpha (GIMP_DRAWABLE (layer))))
{
- GeglColor *color;
- GimpRGB bg;
+ GeglColor *color;
+ GimpRGB bg;
merge_layer = gimp_layer_new (image, (x2 - x1), (y2 - y1),
gimp_image_get_layer_format (image, FALSE),
@@ -585,6 +588,22 @@ gimp_image_merge_layers (GimpImage *image,
bottom_layer = layer;
+ /* Copy the metadata of the bottom layer to the new layer */
+
+ metadata = gimp_item_get_metadata (GIMP_ITEM (bottom_layer));
+
+ if (!metadata)
+ metadata = gimp_image_get_metadata (image);
+
+ if (metadata)
+ {
+ GimpMetadata *new_metadata;
+
+ new_metadata = gimp_metadata_duplicate (metadata);
+ gimp_item_set_metadata (GIMP_ITEM (merge_layer), new_metadata, TRUE);
+ g_object_unref (new_metadata);
+ }
+
/* Copy the tattoo and parasites of the bottom layer to the new layer */
gimp_item_set_tattoo (GIMP_ITEM (merge_layer),
gimp_item_get_tattoo (GIMP_ITEM (bottom_layer)));
diff --git a/app/core/gimpimage-undo-push.c b/app/core/gimpimage-undo-push.c
index 670cc42..c3b05e2 100644
--- a/app/core/gimpimage-undo-push.c
+++ b/app/core/gimpimage-undo-push.c
@@ -49,6 +49,7 @@
#include "gimpsamplepoint.h"
#include "gimpsamplepointundo.h"
#include "gimpselection.h"
+#include "gimpviewable.h"
#include "text/gimptextlayer.h"
#include "text/gimptextundo.h"
@@ -172,6 +173,21 @@ gimp_image_undo_push_image_metadata (GimpImage *image,
NULL);
}
+
+GimpUndo *
+gimp_image_undo_push_item_metadata (GimpImage *image,
+ const gchar *undo_desc,
+ GimpItem *item)
+{
+ g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
+
+ return gimp_image_undo_push (image, GIMP_TYPE_ITEM_UNDO,
+ GIMP_UNDO_ITEM_METADATA, undo_desc,
+ GIMP_DIRTY_IMAGE_META,
+ "item", item,
+ NULL);
+}
+
GimpUndo *
gimp_image_undo_push_image_parasite (GimpImage *image,
const gchar *undo_desc,
diff --git a/app/core/gimpimage-undo-push.h b/app/core/gimpimage-undo-push.h
index 57bc970..592954b 100644
--- a/app/core/gimpimage-undo-push.h
+++ b/app/core/gimpimage-undo-push.h
@@ -118,6 +118,9 @@ GimpUndo * gimp_image_undo_push_item_parasite_remove(GimpImage *image,
const gchar *undo_desc,
GimpItem *item,
const gchar *name);
+GimpUndo * gimp_image_undo_push_item_metadata (GimpImage *image,
+ const gchar *undo_desc,
+ GimpItem *item);
/* layer undos */
@@ -234,5 +237,4 @@ GimpUndo * gimp_image_undo_push_fs_to_layer (GimpImage *image,
GimpUndo * gimp_image_undo_push_cantundo (GimpImage *image,
const gchar *undo_desc);
-
#endif /* __GIMP_IMAGE_UNDO_PUSH_H__ */
diff --git a/app/core/gimpitem.c b/app/core/gimpitem.c
index ab36f52..9167319 100644
--- a/app/core/gimpitem.c
+++ b/app/core/gimpitem.c
@@ -99,6 +99,7 @@ struct _GimpItemPrivate
GimpColorTag color_tag; /* color tag */
GList *offset_nodes; /* offset nodes to manage */
+ GimpMetadata *metadata /* items metadata */
};
#define GET_PRIVATE(item) G_TYPE_INSTANCE_GET_PRIVATE (item, \
@@ -331,6 +332,7 @@ gimp_item_init (GimpItem *item)
g_object_force_floating (G_OBJECT (item));
private->parasites = gimp_parasite_list_new ();
+ private->metadata = NULL;
}
static void
@@ -2420,3 +2422,41 @@ gimp_item_is_in_set (GimpItem *item,
return FALSE;
}
+
+void
+gimp_item_set_metadata (GimpItem *item,
+ GimpMetadata *metadata,
+ gboolean push_undo)
+{
+ GimpItemPrivate *private;
+
+ g_return_val_if_fail (GIMP_IS_ITEM (item), NULL);
+
+ private = GET_PRIVATE (item);
+
+ if (metadata != private->metadata)
+ {
+ if (push_undo)
+ gimp_image_undo_push_item_metadata (gimp_item_get_image (item),
+ NULL,
+ item);
+
+ if (private->metadata)
+ g_object_unref (private->metadata);
+
+ private->metadata = metadata;
+ g_object_ref (private->metadata);
+ }
+}
+
+GimpMetadata *
+gimp_item_get_metadata (GimpItem *item)
+{
+ GimpItemPrivate *private;
+
+ g_return_val_if_fail (GIMP_IS_ITEM (item), NULL);
+
+ private = GET_PRIVATE (item);
+
+ return private->metadata;
+}
diff --git a/app/core/gimpitem.h b/app/core/gimpitem.h
index da431e1..2be3b75 100644
--- a/app/core/gimpitem.h
+++ b/app/core/gimpitem.h
@@ -364,5 +364,9 @@ gboolean gimp_item_mask_intersect (GimpItem *item,
gboolean gimp_item_is_in_set (GimpItem *item,
GimpItemSet set);
+void gimp_item_set_metadata (GimpItem *item,
+ GimpMetadata *metadata,
+ gboolean push_undo);
+GimpMetadata * gimp_item_get_metadata (GimpItem *item);
#endif /* __GIMP_ITEM_H__ */
diff --git a/app/core/gimpitemundo.c b/app/core/gimpitemundo.c
index 34c9539..529f310 100644
--- a/app/core/gimpitemundo.c
+++ b/app/core/gimpitemundo.c
@@ -22,6 +22,8 @@
#include "core-types.h"
+#include "libgimpbase/gimpbase.h"
+
#include "gimpimage.h"
#include "gimpitem.h"
#include "gimpitemundo.h"
@@ -34,18 +36,21 @@ enum
};
-static void gimp_item_undo_constructed (GObject *object);
-static void gimp_item_undo_set_property (GObject *object,
- guint property_id,
- const GValue *value,
- GParamSpec *pspec);
-static void gimp_item_undo_get_property (GObject *object,
- guint property_id,
- GValue *value,
- GParamSpec *pspec);
+static void gimp_item_undo_constructed (GObject *object);
+static void gimp_item_undo_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_item_undo_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
-static void gimp_item_undo_free (GimpUndo *undo,
- GimpUndoMode undo_mode);
+static void gimp_item_undo_free (GimpUndo *undo,
+ GimpUndoMode undo_mode);
+static void gimp_item_undo_pop (GimpUndo *undo,
+ GimpUndoMode undo_mode,
+ GimpUndoAccumulator *accum);
G_DEFINE_TYPE (GimpItemUndo, gimp_item_undo, GIMP_TYPE_UNDO)
@@ -63,6 +68,7 @@ gimp_item_undo_class_init (GimpItemUndoClass *klass)
object_class->set_property = gimp_item_undo_set_property;
object_class->get_property = gimp_item_undo_get_property;
+ undo_class->pop = gimp_item_undo_pop;
undo_class->free = gimp_item_undo_free;
g_object_class_install_property (object_class, PROP_ITEM,
@@ -84,7 +90,14 @@ gimp_item_undo_constructed (GObject *object)
G_OBJECT_CLASS (parent_class)->constructed (object);
- g_assert (GIMP_IS_ITEM (item_undo->item));
+ switch (GIMP_UNDO (object)->undo_type)
+ {
+ case GIMP_UNDO_ITEM_METADATA:
+ item_undo->metadata =
+ gimp_metadata_duplicate (gimp_item_get_metadata (item_undo->item));
+ break;
+ }
+
}
static void
@@ -139,5 +152,42 @@ gimp_item_undo_free (GimpUndo *undo,
item_undo->item = NULL;
}
+ if (item_undo->metadata)
+ {
+ g_object_unref (item_undo->metadata);
+ item_undo->metadata = NULL;
+ }
+
GIMP_UNDO_CLASS (parent_class)->free (undo, undo_mode);
}
+
+static void
+gimp_item_undo_pop (GimpUndo *undo,
+ GimpUndoMode undo_mode,
+ GimpUndoAccumulator *accum)
+{
+ GimpItemUndo *item_undo = GIMP_ITEM_UNDO (undo);
+ GimpItem *item = GIMP_ITEM_UNDO (undo)->item;
+
+ switch (undo->undo_type)
+ {
+ case GIMP_UNDO_ITEM_METADATA:
+ {
+ GimpMetadata *metadata;
+
+ metadata = gimp_metadata_duplicate (gimp_item_get_metadata (item));
+
+ gimp_item_set_metadata (item, item_undo->metadata, FALSE);
+
+ if (item_undo->metadata)
+ g_object_unref (item_undo->metadata);
+ item_undo->metadata = metadata;
+ }
+ break;
+
+ default:
+// g_assert_not_reached ();
+ break;
+
+ }
+}
diff --git a/app/core/gimpitemundo.h b/app/core/gimpitemundo.h
index 1b95592..31e504a 100644
--- a/app/core/gimpitemundo.h
+++ b/app/core/gimpitemundo.h
@@ -38,6 +38,8 @@ struct _GimpItemUndo
GimpUndo parent_instance;
GimpItem *item; /* the item this undo is for */
+ GimpMetadata *metadata;
+
};
struct _GimpItemUndoClass
diff --git a/app/core/gimplayer.c b/app/core/gimplayer.c
index d8284b8..d8a3eb5 100644
--- a/app/core/gimplayer.c
+++ b/app/core/gimplayer.c
@@ -743,7 +743,9 @@ static GimpItem *
gimp_layer_duplicate (GimpItem *item,
GType new_type)
{
- GimpItem *new_item;
+ GimpItem *new_item;
+ GimpMetadata *metadata;
+ GimpMetadata *new_metadata;
g_return_val_if_fail (g_type_is_a (new_type, GIMP_TYPE_DRAWABLE), NULL);
@@ -774,6 +776,14 @@ gimp_layer_duplicate (GimpItem *item,
new_layer->edit_mask = layer->edit_mask;
new_layer->show_mask = layer->show_mask;
}
+
+ metadata = gimp_item_get_metadata (item);
+ if (metadata)
+ {
+ new_metadata = gimp_metadata_duplicate (metadata);
+ gimp_item_set_metadata (new_item, new_metadata, FALSE);
+ g_object_unref (new_metadata);
+ }
}
return new_item;
diff --git a/app/pdb/internal-procs.c b/app/pdb/internal-procs.c
index 5379699..ff75522 100644
--- a/app/pdb/internal-procs.c
+++ b/app/pdb/internal-procs.c
@@ -28,7 +28,7 @@
#include "internal-procs.h"
-/* 802 procedures registered total */
+/* 804 procedures registered total */
void
internal_procs_init (GimpPDB *pdb)
diff --git a/app/pdb/item-cmds.c b/app/pdb/item-cmds.c
index 2a19e25..0ba96b8 100644
--- a/app/pdb/item-cmds.c
+++ b/app/pdb/item-cmds.c
@@ -822,6 +822,67 @@ item_set_tattoo_invoker (GimpProcedure *procedure,
}
static GimpValueArray *
+item_get_metadata_invoker (GimpProcedure *procedure,
+ Gimp *gimp,
+ GimpContext *context,
+ GimpProgress *progress,
+ const GimpValueArray *args,
+ GError **error)
+{
+ gboolean success = TRUE;
+ GimpValueArray *return_vals;
+ GimpItem *item;
+ gchar *metadata_string = NULL;
+
+ item = gimp_value_get_item (gimp_value_array_index (args, 0), gimp);
+
+ if (success)
+ {
+ GimpMetadata *metadata = gimp_item_get_metadata (item);
+
+ 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 *
+item_set_metadata_invoker (GimpProcedure *procedure,
+ Gimp *gimp,
+ GimpContext *context,
+ GimpProgress *progress,
+ const GimpValueArray *args,
+ GError **error)
+{
+ gboolean success = TRUE;
+ GimpItem *item;
+ const gchar *metadata_string;
+
+ item = gimp_value_get_item (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_item_set_metadata (item, metadata, TRUE);
+
+ if (metadata)
+ g_object_unref (metadata);
+ }
+
+ return gimp_procedure_get_return_values (procedure, success,
+ error ? *error : NULL);
+}
+
+static GimpValueArray *
item_attach_parasite_invoker (GimpProcedure *procedure,
Gimp *gimp,
GimpContext *context,
@@ -1731,6 +1792,66 @@ register_item_procs (GimpPDB *pdb)
g_object_unref (procedure);
/*
+ * gimp-item-get-metadata
+ */
+ procedure = gimp_procedure_new (item_get_metadata_invoker);
+ gimp_object_set_static_name (GIMP_OBJECT (procedure),
+ "gimp-item-get-metadata");
+ gimp_procedure_set_static_strings (procedure,
+ "gimp-item-get-metadata",
+ "Returns the item's metadata.",
+ "Returns metadata from the item.",
+ "Spencer Kimball & Peter Mattis",
+ "Spencer Kimball & Peter Mattis",
+ "1995-1996",
+ NULL);
+ gimp_procedure_add_argument (procedure,
+ gimp_param_spec_item_id ("item",
+ "item",
+ "The item",
+ pdb->gimp, FALSE,
+ GIMP_PARAM_READWRITE));
+ gimp_procedure_add_return_value (procedure,
+ gimp_param_spec_string ("metadata-string",
+ "metadata string",
+ "The metadata as a xml string",
+ FALSE, FALSE, FALSE,
+ NULL,
+ GIMP_PARAM_READWRITE));
+ gimp_pdb_register_procedure (pdb, procedure);
+ g_object_unref (procedure);
+
+ /*
+ * gimp-item-set-metadata
+ */
+ procedure = gimp_procedure_new (item_set_metadata_invoker);
+ gimp_object_set_static_name (GIMP_OBJECT (procedure),
+ "gimp-item-set-metadata");
+ gimp_procedure_set_static_strings (procedure,
+ "gimp-item-set-metadata",
+ "Set the item's metadata.",
+ "Sets metadata on the item.",
+ "Spencer Kimball & Peter Mattis",
+ "Spencer Kimball & Peter Mattis",
+ "1995-1996",
+ NULL);
+ gimp_procedure_add_argument (procedure,
+ gimp_param_spec_item_id ("item",
+ "item",
+ "The item",
+ pdb->gimp, FALSE,
+ GIMP_PARAM_READWRITE));
+ gimp_procedure_add_argument (procedure,
+ gimp_param_spec_string ("metadata-string",
+ "metadata string",
+ "The metadata as a xml string",
+ FALSE, FALSE, FALSE,
+ NULL,
+ GIMP_PARAM_READWRITE));
+ gimp_pdb_register_procedure (pdb, procedure);
+ g_object_unref (procedure);
+
+ /*
* gimp-item-attach-parasite
*/
procedure = gimp_procedure_new (item_attach_parasite_invoker);
diff --git a/app/xcf/xcf-load.c b/app/xcf/xcf-load.c
index 5be2a71..cf8128f 100644
--- a/app/xcf/xcf-load.c
+++ b/app/xcf/xcf-load.c
@@ -48,6 +48,7 @@
#include "core/gimpimage-undo.h"
#include "core/gimpitemstack.h"
#include "core/gimplayer-floating-selection.h"
+#include "core/gimpitem.h"
#include "core/gimplayer-new.h"
#include "core/gimplayermask.h"
#include "core/gimpparasitelist.h"
@@ -268,6 +269,31 @@ xcf_load_image (Gimp *gimp,
gimp_parasite_list_remove (private->parasites,
gimp_parasite_name (parasite));
}
+ else
+ {
+
+ /* check for an attributes parasite */
+ parasite = gimp_image_parasite_find (GIMP_IMAGE (image),
+ "gimp-image-attributes");
+ if (parasite)
+ {
+ GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image);
+ GimpMetadata *metadata;
+ const gchar *attributes_string;
+
+ attributes_string = (gchar *) gimp_parasite_data (parasite);
+ metadata = gimp_metadata_deserialize (attributes_string);
+
+ if (metadata)
+ {
+ 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),
@@ -284,13 +310,10 @@ xcf_load_image (Gimp *gimp,
}
else
{
- GimpMetadata *metadata = gimp_image_get_metadata (image);
- GError *my_error = NULL;
+ GimpMetadata *metadata = NULL;
+ GError *my_error = NULL;
- if (metadata)
- g_object_ref (metadata);
- else
- metadata = gimp_metadata_new ();
+ metadata = gimp_metadata_new ();
if (! gimp_metadata_set_from_exif (metadata,
gimp_parasite_data (parasite),
@@ -341,13 +364,10 @@ xcf_load_image (Gimp *gimp,
}
else
{
- GimpMetadata *metadata = gimp_image_get_metadata (image);
- GError *my_error = NULL;
+ GimpMetadata *metadata = NULL;
+ GError *my_error = NULL;
- if (metadata)
- g_object_ref (metadata);
- else
- metadata = gimp_metadata_new ();
+ metadata = gimp_metadata_new ();
if (! gimp_metadata_set_from_xmp (metadata,
(const guint8 *) xmp_data + 10,
@@ -1443,25 +1463,26 @@ xcf_load_layer (XcfInfo *info,
GimpImage *image,
GList **item_path)
{
- GimpLayer *layer;
- GimpLayerMask *layer_mask;
- guint32 hierarchy_offset;
- guint32 layer_mask_offset;
- gboolean apply_mask = TRUE;
- gboolean edit_mask = FALSE;
- gboolean show_mask = FALSE;
- gboolean active;
- gboolean floating;
- guint32 group_layer_flags = 0;
- guint32 text_layer_flags = 0;
- gint width;
- gint height;
- gint type;
- GimpImageBaseType base_type;
- gboolean has_alpha;
- const Babl *format;
- gboolean is_fs_drawable;
- gchar *name;
+ GimpLayer *layer;
+ GimpLayerMask *layer_mask;
+ const GimpParasite *parasite;
+ guint32 hierarchy_offset;
+ guint32 layer_mask_offset;
+ gboolean apply_mask = TRUE;
+ gboolean edit_mask = FALSE;
+ gboolean show_mask = FALSE;
+ gboolean active;
+ gboolean floating;
+ guint32 group_layer_flags = 0;
+ guint32 text_layer_flags = 0;
+ gint width;
+ gint height;
+ gint type;
+ GimpImageBaseType base_type;
+ gboolean has_alpha;
+ const Babl *format;
+ gboolean is_fs_drawable;
+ gchar *name;
/* check and see if this is the drawable the floating selection
* is attached to. if it is then we'll do the attachment in our caller.
@@ -1538,6 +1559,29 @@ xcf_load_layer (XcfInfo *info,
GIMP_LOG (XCF, "layer props loaded");
+ parasite = gimp_item_parasite_find (GIMP_ITEM (layer),
+ "gimp-item-metadata");
+ if (parasite)
+ {
+ GimpMetadata *metadata;
+ const gchar *metadata_string;
+
+ metadata_string = (gchar *) gimp_parasite_data (parasite);
+ metadata = gimp_metadata_deserialize (metadata_string);
+
+ if (metadata)
+ {
+ gimp_item_set_metadata (GIMP_ITEM (layer), metadata, FALSE);
+ g_object_unref (metadata);
+ }
+
+ gimp_item_parasite_detach (GIMP_ITEM (layer),
+ "gimp-item-metadata",
+ FALSE);
+ }
+
+ GIMP_LOG (XCF, "layer metadata loaded");
+
xcf_progress_update (info);
/* call the evil text layer hack that might change our layer pointer */
@@ -1627,13 +1671,14 @@ static GimpChannel *
xcf_load_channel (XcfInfo *info,
GimpImage *image)
{
- GimpChannel *channel;
- guint32 hierarchy_offset;
- gint width;
- gint height;
- gboolean is_fs_drawable;
- gchar *name;
- GimpRGB color = { 0.0, 0.0, 0.0, GIMP_OPACITY_OPAQUE };
+ GimpChannel *channel;
+ const GimpParasite *parasite;
+ guint32 hierarchy_offset;
+ gint width;
+ gint height;
+ gboolean is_fs_drawable;
+ gchar *name;
+ GimpRGB color = { 0.0, 0.0, 0.0, GIMP_OPACITY_OPAQUE };
/* check and see if this is the drawable the floating selection
* is attached to. if it is then we'll do the attachment in our caller.
@@ -1658,6 +1703,27 @@ xcf_load_channel (XcfInfo *info,
if (!xcf_load_channel_props (info, image, &channel))
goto error;
+ parasite = gimp_item_parasite_find (GIMP_ITEM (channel),
+ "gimp-item-metadata");
+ if (parasite)
+ {
+ GimpMetadata *metadata;
+ const gchar *metadata_string;
+
+ metadata_string = (gchar *) gimp_parasite_data (parasite);
+ metadata = gimp_metadata_deserialize (metadata_string);
+
+ if (metadata)
+ {
+ gimp_item_set_metadata (GIMP_ITEM (channel), metadata, FALSE);
+ g_object_unref (metadata);
+ }
+
+ gimp_item_parasite_detach (GIMP_ITEM (channel),
+ "gimp-item-metadata",
+ FALSE);
+ }
+
xcf_progress_update (info);
/* read the hierarchy and layer mask offsets */
diff --git a/app/xcf/xcf-save.c b/app/xcf/xcf-save.c
index 7930b0f..3d9cf4a 100644
--- a/app/xcf/xcf-save.c
+++ b/app/xcf/xcf-save.c
@@ -45,6 +45,7 @@
#include "core/gimpimage-metadata.h"
#include "core/gimpimage-private.h"
#include "core/gimpimage-sample-points.h"
+#include "core/gimpitem.h"
#include "core/gimplayer.h"
#include "core/gimplayermask.h"
#include "core/gimpparasitelist.h"
@@ -337,11 +338,11 @@ xcf_save_image_props (XcfInfo *info,
GimpImage *image,
GError **error)
{
- GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image);
- GimpParasite *grid_parasite = NULL;
- GimpParasite *meta_parasite = NULL;
- GimpParasite *compat_parasite = NULL;
- GimpUnit unit = gimp_image_get_unit (image);
+ GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image);
+ GimpParasite *grid_parasite = NULL;
+ GimpParasite *meta_parasite = NULL;
+ GimpParasite *compat_parasite = NULL;
+ GimpUnit unit = gimp_image_get_unit (image);
gdouble xres;
gdouble yres;
@@ -393,6 +394,27 @@ xcf_save_image_props (XcfInfo *info,
gimp_parasite_list_add (private->parasites, grid_parasite);
}
+#if 0
+ if (gimp_image_get_attributes (image))
+ {
+ GimpParasite *attributes_parasite = NULL;
+ GimpAttributes *attributes = gimp_image_get_attributes (image);
+ gchar *attributes_string;
+
+ attributes_string = gimp_attributes_serialize (attributes);
+
+ if (attributes_string)
+ {
+ attributes_parasite = gimp_parasite_new ("gimp-image-attributes",
+ GIMP_PARASITE_PERSISTENT,
+ strlen (attributes_string) + 1,
+ attributes_string);
+ gimp_parasite_list_add (private->parasites, attributes_parasite);
+ g_free (attributes_string);
+ }
+ }
+#endif
+
if (gimp_image_get_metadata (image))
{
GimpMetadata *metadata = gimp_image_get_metadata (image);
@@ -468,6 +490,25 @@ xcf_save_layer_props (XcfInfo *info,
GimpParasiteList *parasites;
gint offset_x;
gint offset_y;
+ GimpParasite *metadata_parasite = NULL;
+
+ if (gimp_item_get_metadata (GIMP_ITEM (layer)))
+ {
+ GimpMetadata *metadata = gimp_item_get_metadata (GIMP_ITEM (layer));
+ gchar *metadata_string;
+
+ metadata_string = gimp_metadata_serialize (metadata);
+
+ if (metadata_string)
+ {
+ metadata_parasite = gimp_parasite_new ("gimp-item-metadata",
+ GIMP_PARASITE_PERSISTENT,
+ strlen (metadata_string) + 1,
+ metadata_string);
+ gimp_item_parasite_attach (GIMP_ITEM (layer), metadata_parasite, FALSE);
+ g_free (metadata_string);
+ }
+ }
if (gimp_viewable_get_children (GIMP_VIEWABLE (layer)))
xcf_check_error (xcf_save_prop (info, image, PROP_GROUP_ITEM, error));
@@ -583,6 +624,27 @@ xcf_save_channel_props (XcfInfo *info,
{
GimpParasiteList *parasites;
guchar col[3];
+#if 0
+ GimpParasite *attributes_parasite = NULL;
+
+ if (gimp_item_get_attributes (GIMP_ITEM (channel)))
+ {
+ GimpAttributes *attributes = gimp_item_get_attributes (GIMP_ITEM (channel));
+ gchar *attributes_string;
+
+ attributes_string = gimp_attributes_serialize (attributes);
+
+ if (attributes_string)
+ {
+ attributes_parasite = gimp_parasite_new ("gimp-item-attributes",
+ GIMP_PARASITE_PERSISTENT,
+ strlen (attributes_string) + 1,
+ attributes_string);
+ gimp_item_parasite_attach (GIMP_ITEM (channel), attributes_parasite, FALSE);
+ g_free (attributes_string);
+ }
+ }
+#endif
if (channel == gimp_image_get_active_channel (image))
xcf_check_error (xcf_save_prop (info, image, PROP_ACTIVE_CHANNEL, error));
diff --git a/configure.ac b/configure.ac
index e9bf40a..918b78b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -66,7 +66,7 @@ m4_define([pycairo_required_version], [1.0.2])
m4_define([poppler_required_version], [0.44.0])
m4_define([poppler_data_required_version], [0.4.7])
m4_define([libgudev_required_version], [167])
-m4_define([gexiv2_required_version], [0.6.1])
+m4_define([gexiv2_required_version], [0.10.3])
m4_define([libmypaint_required_version], [1.3.0])
m4_define([lcms_required_version], [2.7])
m4_define([libpng_required_version], [1.6.25])
diff --git a/icons/Color/16/gimp-image-metadata.png b/icons/Color/16/gimp-image-metadata.png
new file mode 100644
index 0000000..dd0bafa
Binary files /dev/null and b/icons/Color/16/gimp-image-metadata.png differ
diff --git a/icons/Color/24/gimp-image-metadata.png b/icons/Color/24/gimp-image-metadata.png
new file mode 100644
index 0000000..597dfc4
Binary files /dev/null and b/icons/Color/24/gimp-image-metadata.png differ
diff --git a/icons/Color/24/gimp-image-metadata.svg b/icons/Color/24/gimp-image-metadata.svg
new file mode 100644
index 0000000..f0526ba
--- /dev/null
+++ b/icons/Color/24/gimp-image-metadata.svg
@@ -0,0 +1,275 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="24"
+ height="24"
+ viewBox="0 0 24 24"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.91 r13725"
+ sodipodi:docname="gimp-image-tag 24.svg">
+ <defs
+ id="defs4">
+ <linearGradient
+ id="linearGradient19282"
+ osb:paint="solid"
+ gradientTransform="matrix(1.2178615,0,0,1.043881,378.84797,1794.4958)">
+ <stop
+ offset="0"
+ id="stop19284"
+ style="stop-color:#505050;stop-opacity:1;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient19282-2"
+ osb:paint="solid"
+ gradientTransform="matrix(1.2178615,0,0,1.043881,356.69172,1794.4958)">
+ <stop
+ offset="0"
+ id="stop19284-1"
+ style="stop-color:#666666;stop-opacity:1" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2807"
+ id="linearGradient2813"
+ x1="34.197105"
+ y1="33.725819"
+ x2="17.160702"
+ y2="17.396523"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.51166,0,0,0.475019,3.220846,5.837432)" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient2807">
+ <stop
+ style="stop-color:#d3d7cf"
+ offset="0"
+ id="stop2809" />
+ <stop
+ style="stop-color:#eeeeec"
+ offset="1"
+ id="stop2811" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2065"
+ id="linearGradient2071"
+ x1="-11.986486"
+ y1="13.122552"
+ x2="-11.986486"
+ y2="29.726542"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.46355126,0,0,0.4786134,21.056353,5.7526316)" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient2065">
+ <stop
+ style="stop-color:#555753"
+ offset="0"
+ id="stop2067" />
+ <stop
+ style="stop-color:#fcaf3e"
+ offset="1"
+ id="stop2069" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4928"
+ id="radialGradient4915"
+ cx="-6.0070167"
+ cy="32.837029"
+ fx="-6.0070167"
+ fy="32.837029"
+ r="9.90625"
+ gradientTransform="matrix(0.462962,0,0,0.44272,14.86446,6.713194)"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ id="linearGradient4928">
+ <stop
+ id="stop4930"
+ offset="0"
+ style="stop-color:#fce94f;stop-opacity:1;" />
+ <stop
+ id="stop4932"
+ offset="1"
+ style="stop-color:#fce94f;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4616"
+ id="linearGradient4622"
+ x1="25.355263"
+ y1="34.006802"
+ x2="25.355263"
+ y2="32.409008"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.462962,0,0,0.461866,4.347921,6.0846753)" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient4616">
+ <stop
+ style="stop-color:#2e3436;stop-opacity:1;"
+ offset="0"
+ id="stop4618" />
+ <stop
+ style="stop-color:#2e3436;stop-opacity:0;"
+ offset="1"
+ id="stop4620" />
+ </linearGradient>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="2.4076613"
+ inkscape:cx="160"
+ inkscape:cy="995.02261"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="2340"
+ inkscape:window-height="1022"
+ inkscape:window-x="156"
+ inkscape:window-y="156"
+ inkscape:window-maximized="0" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Ebene 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <g
+ id="layer1-5"
+ inkscape:label="Ebene 1"
+ transform="matrix(2.3238903,0,0,2.3238903,-624.38128,-1263.9315)">
+ <g
+ inkscape:label="Layer 1"
+ id="layer1-6"
+ transform="translate(269.14287,-491.99997)">
+ <g
+ transform="matrix(0.69565065,0,0,0.80000484,24.148475,1040.5518)"
+ id="g4028">
+ <g
+ transform="translate(-38.71351,-12.237062)"
+ id="g2403">
+ <g
+ id="g3799"
+ transform="matrix(0.557846,0,0,0.461866,-85.36183,64.50698)"
+ inkscape:r_cx="true"
+ inkscape:r_cy="true"
+ style="opacity:0.58241763" />
+ <rect
+
style="opacity:1;fill:url(#linearGradient2813);fill-opacity:1;stroke:#8a8a8a;stroke-width:0.99999869;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect2073"
+ width="22.000015"
+ height="19.00001"
+ x="4.500001"
+ y="7.4999981"
+ rx="1.8229138"
+ ry="1.8185979" />
+ <rect
+
style="opacity:1;fill:url(#linearGradient2071);fill-opacity:1;stroke:#888a85;stroke-width:1.01321542;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect2063"
+ width="16.236822"
+ height="11.486709"
+ x="7.3816152"
+ y="11.256679"
+ rx="0.20280379"
+ ry="0.2071792" />
+ <path
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:url(#radialGradient4915);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none"
+ d="m 11.79411,16.401215 c -2.392951,0.151471 -4.296871,2.150075 -4.296871,4.575364 0,0.09366
0.00889,0.181964 0.014468,0.274234 l 9.143509,0 c 0.0056,-0.09227 0.01447,-0.180574 0.01447,-0.274234
0,-2.523879 -2.056354,-4.575364 -4.586223,-4.575364 -0.09882,0 -0.192077,-0.0062 -0.289351,0 z"
+ id="path4898"
+ inkscape:r_cx="true"
+ inkscape:r_cy="true"
+ inkscape:connector-curvature="0" />
+ <rect
+
style="opacity:1;fill:url(#linearGradient4622);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect1324"
+ width="15.740722"
+ height="0.92373294"
+ x="7.5886593"
+ y="21.326269"
+ rx="0"
+ ry="0" />
+ <rect
+
style="opacity:1;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.99999893;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect2077"
+ width="20.000008"
+ height="16.99999"
+ x="5.5000043"
+ y="8.4999981"
+ rx="1.42609"
+ ry="1.4227122" />
+ <path
+
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 18.031043,21.675958 c 0,0 -0.257213,-1.424098 -0.257213,-2.296525 0.0071,-0.39799
3.604954,-3.498458 3.614237,-3.960285 l -0.836462,0.727219 -0.585726,-1.589717 1.974613,0.204118
0.925925,-0.923733 -1.048689,0.768044 -0.360661,-0.08165 -0.4425,-1.610129 0.22506,1.569306 -1.63441,-0.08165
0.511509,1.29875 -1.416971,-1.400809 1.498813,1.949752 c 3.7e-5,0.43486 -2.954107,3.089518 -2.949079,2.631777
L 16.024282,17.241 l -0.102301,-2.216308 0.925925,-0.263924 0.811179,-1.99555 -1.069313,1.844378
-0.834796,-0.03774 -1.868696,-1.574507 -2.565841,-0.979767 2.192051,1.201894 -1.757172,0.155689 2.314813,0
1.388887,1.385599 0.163681,2.393383 -2.478493,-2.393383 1.042268,1.250322 -1.505231,1.05901
1.653439,-0.923733 2.615524,3.286696 -0.1023,2.242895 1.183137,0 z"
+ id="path2079"
+ sodipodi:nodetypes="cccccccccccccccccccccccccccccccccccc"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:r_cy="true"
+ inkscape:r_cx="true"
+ id="path4924"
+ d="m 11.925565,18.563623 c -1.305803,0.08266 -2.344749,1.17327 -2.344749,2.49672 0,0.05111
0.00485,0.0993 0.00789,0.149646 l 4.989498,0 c 0.003,-0.05035 0.0079,-0.09854 0.0079,-0.149646 0,-1.377249
-1.122129,-2.49672 -2.502647,-2.49672 -0.05393,0 -0.104814,-0.0034 -0.157896,0 z"
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#fef39e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none"
+ inkscape:connector-curvature="0" />
+ <path
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#fffbd7;fill-opacity:0.55681817;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none"
+ d="m 11.963041,19.163267 c -0.995873,0.06304 -1.788226,0.894796 -1.788226,1.904128 0,0.03898
0.0037,0.07573 0.006,0.114127 l 3.805249,0 c 0.0023,-0.0384 0.006,-0.07515 0.006,-0.114127 0,-1.050362
-0.855793,-1.904128 -1.908647,-1.904128 -0.04113,0 -0.07994,-0.0026 -0.12042,0 z"
+ id="path4926"
+ inkscape:r_cx="true"
+ inkscape:r_cy="true"
+ inkscape:connector-curvature="0" />
+ </g>
+ </g>
+ </g>
+ <g
+ transform="matrix(0.70710678,-0.70710678,0.70710678,0.70710678,81.714643,582.33533)"
+ style="stroke:#f7ffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="g4559">
+ <path
+
style="fill:#0000ff;fill-opacity:1;stroke:#f7ffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ d="m 157.03498,113.29807 8.9e-4,-3.47688 1.13932,-1.14753 1.13933,-1.14754 1.16389,1.14589
1.1639,1.14588 0,3.47853 0,3.47853 -2.30411,0 -2.30411,0 8.9e-4,-3.47688 z"
+ id="path4551"
+ inkscape:connector-curvature="0" />
+ <circle
+
style="fill:#ffffff;fill-opacity:1;stroke:#f7ffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path4557"
+ cx="159.2991"
+ cy="109.53211"
+ r="0.58017093" />
+ </g>
+ </g>
+ <rect
+
style="fill:#ffffff;fill-opacity:1;stroke:#dbddda;stroke-width:0.1;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect4212"
+ width="39.872719"
+ height="36.549992"
+ x="157.41417"
+ y="451.77939" />
+ </g>
+</svg>
diff --git a/icons/Color/48/gimp-image-metadata.png b/icons/Color/48/gimp-image-metadata.png
new file mode 100644
index 0000000..14a539c
Binary files /dev/null and b/icons/Color/48/gimp-image-metadata.png differ
diff --git a/icons/Color/scalable/gimp-image-metadata.svg b/icons/Color/scalable/gimp-image-metadata.svg
new file mode 100644
index 0000000..444d767
--- /dev/null
+++ b/icons/Color/scalable/gimp-image-metadata.svg
@@ -0,0 +1,268 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="16"
+ height="16"
+ viewBox="0 0 16 16"
+ id="svg4214"
+ version="1.1"
+ inkscape:version="0.91 r13725"
+ sodipodi:docname="gimp-image-tag.svg">
+ <defs
+ id="defs4216">
+ <linearGradient
+ id="linearGradient19282"
+ osb:paint="solid"
+ gradientTransform="matrix(1.2178615,0,0,1.043881,378.84797,1794.4958)">
+ <stop
+ offset="0"
+ id="stop19284"
+ style="stop-color:#505050;stop-opacity:1;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient19282-2"
+ osb:paint="solid"
+ gradientTransform="matrix(1.2178615,0,0,1.043881,356.69172,1794.4958)">
+ <stop
+ offset="0"
+ id="stop19284-1"
+ style="stop-color:#666666;stop-opacity:1" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2807"
+ id="linearGradient2813"
+ x1="34.197105"
+ y1="33.725819"
+ x2="17.160702"
+ y2="17.396523"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.51166,0,0,0.475019,3.220846,5.837432)" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient2807">
+ <stop
+ style="stop-color:#d3d7cf"
+ offset="0"
+ id="stop2809" />
+ <stop
+ style="stop-color:#eeeeec"
+ offset="1"
+ id="stop2811" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2065"
+ id="linearGradient2071"
+ x1="-11.986486"
+ y1="13.122552"
+ x2="-11.986486"
+ y2="29.726542"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.46355126,0,0,0.4786134,21.056353,5.7526316)" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient2065">
+ <stop
+ style="stop-color:#555753"
+ offset="0"
+ id="stop2067" />
+ <stop
+ style="stop-color:#fcaf3e"
+ offset="1"
+ id="stop2069" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4928"
+ id="radialGradient4915"
+ cx="-6.0070167"
+ cy="32.837029"
+ fx="-6.0070167"
+ fy="32.837029"
+ r="9.90625"
+ gradientTransform="matrix(0.462962,0,0,0.44272,14.86446,6.713194)"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ id="linearGradient4928">
+ <stop
+ id="stop4930"
+ offset="0"
+ style="stop-color:#fce94f;stop-opacity:1;" />
+ <stop
+ id="stop4932"
+ offset="1"
+ style="stop-color:#fce94f;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4616"
+ id="linearGradient4622"
+ x1="25.355263"
+ y1="34.006802"
+ x2="25.355263"
+ y2="32.409008"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.462962,0,0,0.461866,4.347921,6.0846753)" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient4616">
+ <stop
+ style="stop-color:#2e3436;stop-opacity:1;"
+ offset="0"
+ id="stop4618" />
+ <stop
+ style="stop-color:#2e3436;stop-opacity:0;"
+ offset="1"
+ id="stop4620" />
+ </linearGradient>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="3.8436364"
+ inkscape:cx="41.428571"
+ inkscape:cy="1011.4286"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="3440"
+ inkscape:window-height="1377"
+ inkscape:window-x="-8"
+ inkscape:window-y="-8"
+ inkscape:window-maximized="1" />
+ <metadata
+ id="metadata4219">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Ebene 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <g
+ id="layer1-9"
+ inkscape:label="Ebene 1"
+ transform="translate(-268.84576,-544.6878)">
+ <g
+ inkscape:label="Layer 1"
+ id="layer1-6"
+ transform="translate(269.14287,-491.99997)">
+ <g
+ transform="matrix(0.69565065,0,0,0.80000484,24.148475,1040.5518)"
+ id="g4028">
+ <g
+ transform="translate(-38.71351,-12.237062)"
+ id="g2403">
+ <g
+ id="g3799"
+ transform="matrix(0.557846,0,0,0.461866,-85.36183,64.50698)"
+ inkscape:r_cx="true"
+ inkscape:r_cy="true"
+ style="opacity:0.58241763" />
+ <rect
+
style="opacity:1;fill:url(#linearGradient2813);fill-opacity:1;stroke:#8a8a8a;stroke-width:0.99999869;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect2073"
+ width="22.000015"
+ height="19.00001"
+ x="4.500001"
+ y="7.4999981"
+ rx="1.8229138"
+ ry="1.8185979" />
+ <rect
+
style="opacity:1;fill:url(#linearGradient2071);fill-opacity:1;stroke:#888a85;stroke-width:1.01321542;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect2063"
+ width="16.236822"
+ height="11.486709"
+ x="7.3816152"
+ y="11.256679"
+ rx="0.20280379"
+ ry="0.2071792" />
+ <path
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:url(#radialGradient4915);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none"
+ d="m 11.79411,16.401215 c -2.392951,0.151471 -4.296871,2.150075 -4.296871,4.575364 0,0.09366
0.00889,0.181964 0.014468,0.274234 l 9.143509,0 c 0.0056,-0.09227 0.01447,-0.180574 0.01447,-0.274234
0,-2.523879 -2.056354,-4.575364 -4.586223,-4.575364 -0.09882,0 -0.192077,-0.0062 -0.289351,0 z"
+ id="path4898"
+ inkscape:r_cx="true"
+ inkscape:r_cy="true"
+ inkscape:connector-curvature="0" />
+ <rect
+
style="opacity:1;fill:url(#linearGradient4622);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect1324"
+ width="15.740722"
+ height="0.92373294"
+ x="7.5886593"
+ y="21.326269"
+ rx="0"
+ ry="0" />
+ <rect
+
style="opacity:1;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.99999893;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect2077"
+ width="20.000008"
+ height="16.99999"
+ x="5.5000043"
+ y="8.4999981"
+ rx="1.42609"
+ ry="1.4227122" />
+ <path
+
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 18.031043,21.675958 c 0,0 -0.257213,-1.424098 -0.257213,-2.296525 0.0071,-0.39799
3.604954,-3.498458 3.614237,-3.960285 l -0.836462,0.727219 -0.585726,-1.589717 1.974613,0.204118
0.925925,-0.923733 -1.048689,0.768044 -0.360661,-0.08165 -0.4425,-1.610129 0.22506,1.569306 -1.63441,-0.08165
0.511509,1.29875 -1.416971,-1.400809 1.498813,1.949752 c 3.7e-5,0.43486 -2.954107,3.089518 -2.949079,2.631777
L 16.024282,17.241 l -0.102301,-2.216308 0.925925,-0.263924 0.811179,-1.99555 -1.069313,1.844378
-0.834796,-0.03774 -1.868696,-1.574507 -2.565841,-0.979767 2.192051,1.201894 -1.757172,0.155689 2.314813,0
1.388887,1.385599 0.163681,2.393383 -2.478493,-2.393383 1.042268,1.250322 -1.505231,1.05901
1.653439,-0.923733 2.615524,3.286696 -0.1023,2.242895 1.183137,0 z"
+ id="path2079"
+ sodipodi:nodetypes="cccccccccccccccccccccccccccccccccccc"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:r_cy="true"
+ inkscape:r_cx="true"
+ id="path4924"
+ d="m 11.925565,18.563623 c -1.305803,0.08266 -2.344749,1.17327 -2.344749,2.49672 0,0.05111
0.00485,0.0993 0.00789,0.149646 l 4.989498,0 c 0.003,-0.05035 0.0079,-0.09854 0.0079,-0.149646 0,-1.377249
-1.122129,-2.49672 -2.502647,-2.49672 -0.05393,0 -0.104814,-0.0034 -0.157896,0 z"
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#fef39e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none"
+ inkscape:connector-curvature="0" />
+ <path
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#fffbd7;fill-opacity:0.55681817;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none"
+ d="m 11.963041,19.163267 c -0.995873,0.06304 -1.788226,0.894796 -1.788226,1.904128 0,0.03898
0.0037,0.07573 0.006,0.114127 l 3.805249,0 c 0.0023,-0.0384 0.006,-0.07515 0.006,-0.114127 0,-1.050362
-0.855793,-1.904128 -1.908647,-1.904128 -0.04113,0 -0.07994,-0.0026 -0.12042,0 z"
+ id="path4926"
+ inkscape:r_cx="true"
+ inkscape:r_cy="true"
+ inkscape:connector-curvature="0" />
+ </g>
+ </g>
+ </g>
+ <g
+ transform="matrix(0.70710678,-0.70710678,0.70710678,0.70710678,81.714643,582.33533)"
+ style="stroke:#f7ffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="g4559">
+ <path
+
style="fill:#0000ff;fill-opacity:1;stroke:#f7ffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ d="m 157.03498,113.29807 8.9e-4,-3.47688 1.13932,-1.14753 1.13933,-1.14754 1.16389,1.14589
1.1639,1.14588 0,3.47853 0,3.47853 -2.30411,0 -2.30411,0 8.9e-4,-3.47688 z"
+ id="path4551"
+ inkscape:connector-curvature="0" />
+ <circle
+
style="fill:#ffffff;fill-opacity:1;stroke:#f7ffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path4557"
+ cx="159.2991"
+ cy="109.53211"
+ r="0.58017093" />
+ </g>
+ </g>
+ </g>
+</svg>
diff --git a/icons/Legacy/16/gimp-image-metadata.png b/icons/Legacy/16/gimp-image-metadata.png
new file mode 100644
index 0000000..dd0bafa
Binary files /dev/null and b/icons/Legacy/16/gimp-image-metadata.png differ
diff --git a/icons/Legacy/24/gimp-image-metadata.png b/icons/Legacy/24/gimp-image-metadata.png
new file mode 100644
index 0000000..597dfc4
Binary files /dev/null and b/icons/Legacy/24/gimp-image-metadata.png differ
diff --git a/icons/Legacy/48/gimp-image-metadata.png b/icons/Legacy/48/gimp-image-metadata.png
new file mode 100644
index 0000000..14a539c
Binary files /dev/null and b/icons/Legacy/48/gimp-image-metadata.png differ
diff --git a/icons/Symbolic-Inverted/24/gimp-image-metadata.svg
b/icons/Symbolic-Inverted/24/gimp-image-metadata.svg
new file mode 100644
index 0000000..a615598
--- /dev/null
+++ b/icons/Symbolic-Inverted/24/gimp-image-metadata.svg
@@ -0,0 +1,189 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="24"
+ height="24"
+ id="svg4136"
+ version="1.1"
+ inkscape:version="0.91 r13725"
+ viewBox="0 0 24 24"
+ sodipodi:docname="gimp-image-tag-sym-24.svg"
+ inkscape:export-filename="D:\x\gimp-image-tag-sym-48.png"
+ inkscape:export-xdpi="68.080002"
+ inkscape:export-ydpi="68.080002">
+ <defs
+ id="defs4138">
+ <linearGradient
+ id="linearGradient8074"
+ osb:paint="solid">
+ <stop
+ style="stop-color:#be00be;stop-opacity:1;"
+ offset="0"
+ id="stop8072" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient7561"
+ osb:paint="solid">
+ <stop
+ style="stop-color:#a5a5a5;stop-opacity:1;"
+ offset="0"
+ id="stop7558" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient7548"
+ osb:paint="solid">
+ <stop
+ style="stop-color:#ebebeb;stop-opacity:1;"
+ offset="0"
+ id="stop7546" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient7542"
+ osb:paint="solid">
+ <stop
+ style="stop-color:#c9c9c9;stop-opacity:1;"
+ offset="0"
+ id="stop7538" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient19282"
+ osb:paint="solid"
+ gradientTransform="matrix(0,-735328.32,170712.69,0,2464326300,577972450)">
+ <stop
+ style="stop-color:#b4b4b4;stop-opacity:1;"
+ offset="0"
+ id="stop19284" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient19282-4"
+ osb:paint="solid"
+ gradientTransform="matrix(0.34682586,0,0,0.30620888,-13.35187,382.03851)">
+ <stop
+ style="stop-color:#bebebe;stop-opacity:1;"
+ offset="0"
+ id="stop19284-0" />
+ </linearGradient>
+ <linearGradient
+ xlink:href="#linearGradient19282-4"
+ id="linearGradient10174"
+ x1="-97"
+ y1="168.5"
+ x2="-87"
+ y2="168.5"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(520.93726,194.66866)" />
+ <linearGradient
+ xlink:href="#linearGradient19282-4"
+ id="linearGradient10176"
+ x1="142.0002"
+ y1="386.95001"
+ x2="156.0002"
+ y2="386.95001"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(520.93726,194.66866)" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="5.5"
+ inkscape:cx="-7.7272727"
+ inkscape:cy="32"
+ inkscape:current-layer="g4559"
+ showgrid="true"
+ inkscape:document-units="px"
+ inkscape:grid-bbox="true"
+ inkscape:window-width="3440"
+ inkscape:window-height="1377"
+ inkscape:window-x="-8"
+ inkscape:window-y="-8"
+ inkscape:window-maximized="1" />
+ <metadata
+ id="metadata4141">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ id="layer1"
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ transform="translate(0,-40)">
+ <g
+ id="stock"
+ style="display:inline"
+ transform="matrix(1.4886364,0,0,1.508596,-93.79,-1.8312358)"
+ inkscape:export-xdpi="90.773643"
+ inkscape:export-ydpi="90.773643">
+ <g
+ transform="translate(-77.874095,-351.36258)"
+ style="display:inline"
+ id="gimp-image">
+ <rect
+ transform="translate(241.0002,217)"
+ y="162"
+ x="-100"
+ height="16"
+ width="16"
+ id="rect6346"
+ style="fill:none;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect5882"
+ d="m 142.0002,379.9 0,14.1 14,0 0,-14.1 z m 1,1.1 12,0 0,9 -12,0 z"
+ style="fill:url(#linearGradient10176);fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect10683"
+ transform="translate(241.0002,217)"
+ d="m -97,165 0,7 1,0 c 0,-0.75515 0.671573,-1.375 1.5,-1.375 0.828427,0 1.5,0.61985 1.5,1.375 l
1,0 0,-1.5 c 1.4e-4,-0.98573 -0.7274,-1.47402 -2,-2.0625 0,0 0.02947,3.7e-4 0.25,0 0.40803,0.0835
1.38212,0.3501 1.75,0.59375 -0.09819,-0.35554 -0.14101,-0.89353 -0.4375,-1.34375 C -92.73401,167.23732
-94,166.34375 -94,166.34375 l 0.0625,-0.0312 c 0.05568,-0.0402 0.9453,0.7655 1.25,0.6875 -0.13515,-0.32497
-0.25949,-0.63468 -0.40625,-0.8125 l 0.1875,-0.0312 c 0.35629,0.59147 0.46684,0.72309 0.84375,1.15625
0.12472,0.0571 -0.03108,-0.0423 1.0625,-0.59375 L -91,167 c 0,0 -1.0625,0.30993 -0.75,0.90625 0.22248,0.30573
0.39823,1.01025 0.75,1.09375 0,0 0.85009,-1.22532 1.5,-1.625 0,0 -0.21923,-0.50879 -0.21875,-0.78125
3.7e-4,-0.20918 0.15625,-0.59375 0.15625,-0.59375 0.02319,0.39561 -0.0029,0.83091 0.28125,1.21875 L
-88.34375,166 -88,166 c 0,0 -2.25088,2.3886 -2,2.78125 0.39455,-0.048 2,-0.34375 2,-0.34375 l 0,0.0625 c
-1.04689,0.34884 -1.67111,0.68872 -2,1.5 l 0,2 3,0 0,-7
z"
+ style="opacity:0.3;fill:url(#linearGradient10174);fill-opacity:1;stroke:none" />
+ </g>
+ </g>
+ <g
+ transform="matrix(2.1155203,-2.1155203,2.1155203,2.1155203,-559.23734,114.18176)"
+
style="fill:#808080;stroke:#f7ffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="g4559">
+ <path
+
style="fill:#999999;fill-opacity:1;stroke:#f7ffff;stroke-width:0.03767881;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ d="m 148.95421,117.66287 0.005,-1.31006 0.43094,-0.434 0.43093,-0.43402 0.4369,0.43009
0.43691,0.43009 -0.005,1.31068 -0.005,1.31067 -0.86817,0.003 -0.86817,0.003 0.005,-1.31005 z"
+ id="path4551"
+ inkscape:connector-curvature="0"
+ inkscape:export-xdpi="90.773643"
+ inkscape:export-ydpi="90.773643" />
+ <circle
+
style="fill:#ffffff;fill-opacity:1;stroke:#f7ffff;stroke-width:0.03767936;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path4557"
+ cx="150.25793"
+ cy="116.81281"
+ r="0.21860467"
+ inkscape:export-xdpi="90.773643"
+ inkscape:export-ydpi="90.773643"
+ transform="matrix(0.99999277,-0.00380225,-0.00380225,0.99999277,0,0)" />
+ </g>
+ </g>
+ <g
+ inkscape:groupmode="layer"
+ id="layer2"
+ inkscape:label="Layer 2"
+ transform="translate(0,-40)" />
+</svg>
diff --git a/icons/Symbolic/16/gimp-image-metadata.png b/icons/Symbolic/16/gimp-image-metadata.png
new file mode 100644
index 0000000..e7fac8f
Binary files /dev/null and b/icons/Symbolic/16/gimp-image-metadata.png differ
diff --git a/icons/Symbolic/24/gimp-image-metadata.png b/icons/Symbolic/24/gimp-image-metadata.png
new file mode 100644
index 0000000..37aeaa4
Binary files /dev/null and b/icons/Symbolic/24/gimp-image-metadata.png differ
diff --git a/icons/Symbolic/24/gimp-image-metadata.svg b/icons/Symbolic/24/gimp-image-metadata.svg
new file mode 100644
index 0000000..a615598
--- /dev/null
+++ b/icons/Symbolic/24/gimp-image-metadata.svg
@@ -0,0 +1,189 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="24"
+ height="24"
+ id="svg4136"
+ version="1.1"
+ inkscape:version="0.91 r13725"
+ viewBox="0 0 24 24"
+ sodipodi:docname="gimp-image-tag-sym-24.svg"
+ inkscape:export-filename="D:\x\gimp-image-tag-sym-48.png"
+ inkscape:export-xdpi="68.080002"
+ inkscape:export-ydpi="68.080002">
+ <defs
+ id="defs4138">
+ <linearGradient
+ id="linearGradient8074"
+ osb:paint="solid">
+ <stop
+ style="stop-color:#be00be;stop-opacity:1;"
+ offset="0"
+ id="stop8072" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient7561"
+ osb:paint="solid">
+ <stop
+ style="stop-color:#a5a5a5;stop-opacity:1;"
+ offset="0"
+ id="stop7558" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient7548"
+ osb:paint="solid">
+ <stop
+ style="stop-color:#ebebeb;stop-opacity:1;"
+ offset="0"
+ id="stop7546" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient7542"
+ osb:paint="solid">
+ <stop
+ style="stop-color:#c9c9c9;stop-opacity:1;"
+ offset="0"
+ id="stop7538" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient19282"
+ osb:paint="solid"
+ gradientTransform="matrix(0,-735328.32,170712.69,0,2464326300,577972450)">
+ <stop
+ style="stop-color:#b4b4b4;stop-opacity:1;"
+ offset="0"
+ id="stop19284" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient19282-4"
+ osb:paint="solid"
+ gradientTransform="matrix(0.34682586,0,0,0.30620888,-13.35187,382.03851)">
+ <stop
+ style="stop-color:#bebebe;stop-opacity:1;"
+ offset="0"
+ id="stop19284-0" />
+ </linearGradient>
+ <linearGradient
+ xlink:href="#linearGradient19282-4"
+ id="linearGradient10174"
+ x1="-97"
+ y1="168.5"
+ x2="-87"
+ y2="168.5"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(520.93726,194.66866)" />
+ <linearGradient
+ xlink:href="#linearGradient19282-4"
+ id="linearGradient10176"
+ x1="142.0002"
+ y1="386.95001"
+ x2="156.0002"
+ y2="386.95001"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(520.93726,194.66866)" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="5.5"
+ inkscape:cx="-7.7272727"
+ inkscape:cy="32"
+ inkscape:current-layer="g4559"
+ showgrid="true"
+ inkscape:document-units="px"
+ inkscape:grid-bbox="true"
+ inkscape:window-width="3440"
+ inkscape:window-height="1377"
+ inkscape:window-x="-8"
+ inkscape:window-y="-8"
+ inkscape:window-maximized="1" />
+ <metadata
+ id="metadata4141">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ id="layer1"
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ transform="translate(0,-40)">
+ <g
+ id="stock"
+ style="display:inline"
+ transform="matrix(1.4886364,0,0,1.508596,-93.79,-1.8312358)"
+ inkscape:export-xdpi="90.773643"
+ inkscape:export-ydpi="90.773643">
+ <g
+ transform="translate(-77.874095,-351.36258)"
+ style="display:inline"
+ id="gimp-image">
+ <rect
+ transform="translate(241.0002,217)"
+ y="162"
+ x="-100"
+ height="16"
+ width="16"
+ id="rect6346"
+ style="fill:none;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect5882"
+ d="m 142.0002,379.9 0,14.1 14,0 0,-14.1 z m 1,1.1 12,0 0,9 -12,0 z"
+ style="fill:url(#linearGradient10176);fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect10683"
+ transform="translate(241.0002,217)"
+ d="m -97,165 0,7 1,0 c 0,-0.75515 0.671573,-1.375 1.5,-1.375 0.828427,0 1.5,0.61985 1.5,1.375 l
1,0 0,-1.5 c 1.4e-4,-0.98573 -0.7274,-1.47402 -2,-2.0625 0,0 0.02947,3.7e-4 0.25,0 0.40803,0.0835
1.38212,0.3501 1.75,0.59375 -0.09819,-0.35554 -0.14101,-0.89353 -0.4375,-1.34375 C -92.73401,167.23732
-94,166.34375 -94,166.34375 l 0.0625,-0.0312 c 0.05568,-0.0402 0.9453,0.7655 1.25,0.6875 -0.13515,-0.32497
-0.25949,-0.63468 -0.40625,-0.8125 l 0.1875,-0.0312 c 0.35629,0.59147 0.46684,0.72309 0.84375,1.15625
0.12472,0.0571 -0.03108,-0.0423 1.0625,-0.59375 L -91,167 c 0,0 -1.0625,0.30993 -0.75,0.90625 0.22248,0.30573
0.39823,1.01025 0.75,1.09375 0,0 0.85009,-1.22532 1.5,-1.625 0,0 -0.21923,-0.50879 -0.21875,-0.78125
3.7e-4,-0.20918 0.15625,-0.59375 0.15625,-0.59375 0.02319,0.39561 -0.0029,0.83091 0.28125,1.21875 L
-88.34375,166 -88,166 c 0,0 -2.25088,2.3886 -2,2.78125 0.39455,-0.048 2,-0.34375 2,-0.34375 l 0,0.0625 c
-1.04689,0.34884 -1.67111,0.68872 -2,1.5 l 0,2 3,0 0,-7
z"
+ style="opacity:0.3;fill:url(#linearGradient10174);fill-opacity:1;stroke:none" />
+ </g>
+ </g>
+ <g
+ transform="matrix(2.1155203,-2.1155203,2.1155203,2.1155203,-559.23734,114.18176)"
+
style="fill:#808080;stroke:#f7ffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="g4559">
+ <path
+
style="fill:#999999;fill-opacity:1;stroke:#f7ffff;stroke-width:0.03767881;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ d="m 148.95421,117.66287 0.005,-1.31006 0.43094,-0.434 0.43093,-0.43402 0.4369,0.43009
0.43691,0.43009 -0.005,1.31068 -0.005,1.31067 -0.86817,0.003 -0.86817,0.003 0.005,-1.31005 z"
+ id="path4551"
+ inkscape:connector-curvature="0"
+ inkscape:export-xdpi="90.773643"
+ inkscape:export-ydpi="90.773643" />
+ <circle
+
style="fill:#ffffff;fill-opacity:1;stroke:#f7ffff;stroke-width:0.03767936;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path4557"
+ cx="150.25793"
+ cy="116.81281"
+ r="0.21860467"
+ inkscape:export-xdpi="90.773643"
+ inkscape:export-ydpi="90.773643"
+ transform="matrix(0.99999277,-0.00380225,-0.00380225,0.99999277,0,0)" />
+ </g>
+ </g>
+ <g
+ inkscape:groupmode="layer"
+ id="layer2"
+ inkscape:label="Layer 2"
+ transform="translate(0,-40)" />
+</svg>
diff --git a/icons/Symbolic/48/gimp-image-metadata.png b/icons/Symbolic/48/gimp-image-metadata.png
new file mode 100644
index 0000000..d25b8e1
Binary files /dev/null and b/icons/Symbolic/48/gimp-image-metadata.png differ
diff --git a/icons/Symbolic/64/gimp-image-metadata.png b/icons/Symbolic/64/gimp-image-metadata.png
new file mode 100644
index 0000000..02a2b12
Binary files /dev/null and b/icons/Symbolic/64/gimp-image-metadata.png differ
diff --git a/icons/Symbolic/scalable/gimp-image-metadata.svg b/icons/Symbolic/scalable/gimp-image-metadata.svg
new file mode 100644
index 0000000..0a41fe7
--- /dev/null
+++ b/icons/Symbolic/scalable/gimp-image-metadata.svg
@@ -0,0 +1,189 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="16"
+ height="16"
+ id="svg4136"
+ version="1.1"
+ inkscape:version="0.91 r13725"
+ viewBox="0 0 16 16"
+ sodipodi:docname="gimp-image-tag-sym-16.svg"
+ inkscape:export-filename="D:\x\gimp-image-tag-sym-48.png"
+ inkscape:export-xdpi="68.080002"
+ inkscape:export-ydpi="68.080002">
+ <defs
+ id="defs4138">
+ <linearGradient
+ id="linearGradient8074"
+ osb:paint="solid">
+ <stop
+ style="stop-color:#be00be;stop-opacity:1;"
+ offset="0"
+ id="stop8072" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient7561"
+ osb:paint="solid">
+ <stop
+ style="stop-color:#a5a5a5;stop-opacity:1;"
+ offset="0"
+ id="stop7558" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient7548"
+ osb:paint="solid">
+ <stop
+ style="stop-color:#ebebeb;stop-opacity:1;"
+ offset="0"
+ id="stop7546" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient7542"
+ osb:paint="solid">
+ <stop
+ style="stop-color:#c9c9c9;stop-opacity:1;"
+ offset="0"
+ id="stop7538" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient19282"
+ osb:paint="solid"
+ gradientTransform="matrix(0,-735328.32,170712.69,0,2464326300,577972450)">
+ <stop
+ style="stop-color:#b4b4b4;stop-opacity:1;"
+ offset="0"
+ id="stop19284" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient19282-4"
+ osb:paint="solid"
+ gradientTransform="matrix(0.34682586,0,0,0.30620888,-13.35187,382.03851)">
+ <stop
+ style="stop-color:#bebebe;stop-opacity:1;"
+ offset="0"
+ id="stop19284-0" />
+ </linearGradient>
+ <linearGradient
+ xlink:href="#linearGradient19282-4"
+ id="linearGradient10174"
+ x1="-97"
+ y1="168.5"
+ x2="-87"
+ y2="168.5"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(520.93726,194.66866)" />
+ <linearGradient
+ xlink:href="#linearGradient19282-4"
+ id="linearGradient10176"
+ x1="142.0002"
+ y1="386.95001"
+ x2="156.0002"
+ y2="386.95001"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(520.93726,194.66866)" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="5.5"
+ inkscape:cx="-7.7272727"
+ inkscape:cy="32"
+ inkscape:current-layer="g4559"
+ showgrid="true"
+ inkscape:document-units="px"
+ inkscape:grid-bbox="true"
+ inkscape:window-width="3440"
+ inkscape:window-height="1377"
+ inkscape:window-x="-8"
+ inkscape:window-y="-8"
+ inkscape:window-maximized="1" />
+ <metadata
+ id="metadata4141">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ id="layer1"
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ transform="translate(0,-48)">
+ <g
+ id="stock"
+ style="display:inline"
+ transform="matrix(0.97282511,0,0,0.98586873,-61.228842,20.979234)"
+ inkscape:export-xdpi="90.773643"
+ inkscape:export-ydpi="90.773643">
+ <g
+ transform="translate(-77.874095,-351.36258)"
+ style="display:inline"
+ id="gimp-image">
+ <rect
+ transform="translate(241.0002,217)"
+ y="162"
+ x="-100"
+ height="16"
+ width="16"
+ id="rect6346"
+ style="fill:none;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect5882"
+ d="m 142.0002,379.9 0,14.1 14,0 0,-14.1 z m 1,1.1 12,0 0,9 -12,0 z"
+ style="fill:url(#linearGradient10176);fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect10683"
+ transform="translate(241.0002,217)"
+ d="m -97,165 0,7 1,0 c 0,-0.75515 0.671573,-1.375 1.5,-1.375 0.828427,0 1.5,0.61985 1.5,1.375 l
1,0 0,-1.5 c 1.4e-4,-0.98573 -0.7274,-1.47402 -2,-2.0625 0,0 0.02947,3.7e-4 0.25,0 0.40803,0.0835
1.38212,0.3501 1.75,0.59375 -0.09819,-0.35554 -0.14101,-0.89353 -0.4375,-1.34375 C -92.73401,167.23732
-94,166.34375 -94,166.34375 l 0.0625,-0.0312 c 0.05568,-0.0402 0.9453,0.7655 1.25,0.6875 -0.13515,-0.32497
-0.25949,-0.63468 -0.40625,-0.8125 l 0.1875,-0.0312 c 0.35629,0.59147 0.46684,0.72309 0.84375,1.15625
0.12472,0.0571 -0.03108,-0.0423 1.0625,-0.59375 L -91,167 c 0,0 -1.0625,0.30993 -0.75,0.90625 0.22248,0.30573
0.39823,1.01025 0.75,1.09375 0,0 0.85009,-1.22532 1.5,-1.625 0,0 -0.21923,-0.50879 -0.21875,-0.78125
3.7e-4,-0.20918 0.15625,-0.59375 0.15625,-0.59375 0.02319,0.39561 -0.0029,0.83091 0.28125,1.21875 L
-88.34375,166 -88,166 c 0,0 -2.25088,2.3886 -2,2.78125 0.39455,-0.048 2,-0.34375 2,-0.34375 l 0,0.0625 c
-1.04689,0.34884 -1.67111,0.68872 -2,1.5 l 0,2 3,0 0,-7
z"
+ style="opacity:0.3;fill:url(#linearGradient10174);fill-opacity:1;stroke:none" />
+ </g>
+ </g>
+ <g
+ transform="matrix(2.1155203,-2.1155203,2.1155203,2.1155203,-559.23734,114.18176)"
+
style="fill:#808080;stroke:#f7ffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="g4559">
+ <path
+
style="fill:#999999;fill-opacity:1;stroke:#f7ffff;stroke-width:0.02462314;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ d="m 147.26469,118.59655 0.003,-0.85612 0.28162,-0.28362 0.28161,-0.28364 0.28552,0.28107
0.28552,0.28106 -0.003,0.85653 -0.003,0.85653 -0.56735,0.002 -0.56735,0.002 0.003,-0.85612 z"
+ id="path4551"
+ inkscape:connector-curvature="0"
+ inkscape:export-xdpi="90.773643"
+ inkscape:export-ydpi="90.773643" />
+ <circle
+
style="fill:#ffffff;fill-opacity:1;stroke:#f7ffff;stroke-width:0.02462349;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path4557"
+ cx="148.27634"
+ cy="118.23177"
+ r="0.14285833"
+ inkscape:export-xdpi="90.773643"
+ inkscape:export-ydpi="90.773643"
+ transform="matrix(0.99999277,-0.00380225,-0.00380225,0.99999277,0,0)" />
+ </g>
+ </g>
+ <g
+ inkscape:groupmode="layer"
+ id="layer2"
+ inkscape:label="Layer 2"
+ transform="translate(0,-48)" />
+</svg>
diff --git a/icons/icon-list.mk b/icons/icon-list.mk
index 5ed962b..db71840 100644
--- a/icons/icon-list.mk
+++ b/icons/icon-list.mk
@@ -142,6 +142,7 @@ scalable_images = \
scalable/gimp-histogram.svg \
scalable/gimp-images.svg \
scalable/gimp-image.svg \
+ scalable/gimp-image-metadata.svg \
scalable/gimp-image-open.svg \
scalable/gimp-image-reload.svg \
scalable/gimp-info.svg \
@@ -353,6 +354,7 @@ vector24_images = \
24/gimp-grid.svg \
24/gimp-histogram.svg \
24/gimp-image.svg \
+ 24/gimp-image-metadata.svg \
24/gimp-images.svg \
24/gimp-info.svg \
24/gimp-landscape.svg \
@@ -605,6 +607,7 @@ icons16_images = \
16/gimp-histogram-logarithmic.png \
16/gimp-histogram.png \
16/gimp-image.png \
+ 16/gimp-image-metadata.png \
16/gimp-images.png \
16/gimp-image-open.png \
16/gimp-image-reload.png \
@@ -861,6 +864,7 @@ icons24_images = \
24/gimp-hchain.png \
24/gimp-hfill.png \
24/gimp-image.png \
+ 24/gimp-image-metadata.png \
24/gimp-images.png \
24/gimp-info.png \
24/gimp-layer-mask.png \
@@ -970,6 +974,7 @@ icons48_images = \
48/gimp-channel.png \
48/gimp-floating-selection.png \
48/gimp-image.png \
+ 48/gimp-image-metadata.png \
48/gimp-layer-mask.png \
48/gimp-layer.png \
48/gimp-prefs-color-management.png \
diff --git a/libgimp/Makefile.am b/libgimp/Makefile.am
index 72ce2b7..dfb0169 100644
--- a/libgimp/Makefile.am
+++ b/libgimp/Makefile.am
@@ -222,6 +222,8 @@ libgimp_sources = \
gimpimage.h \
gimpimagecolorprofile.c \
gimpimagecolorprofile.h \
+ gimpitem.c \
+ gimpitem.h \
gimplayer.c \
gimplayer.h \
gimppalette.c \
@@ -345,6 +347,7 @@ gimpinclude_HEADERS = \
gimpgradientselect.h \
gimpimage.h \
gimpimagecolorprofile.h \
+ gimpitem.h \
gimplayer.h \
gimppalette.h \
gimppalettes.h \
diff --git a/libgimp/gimp.def b/libgimp/gimp.def
index ac54622..066c234 100644
--- a/libgimp/gimp.def
+++ b/libgimp/gimp.def
@@ -557,6 +557,7 @@ EXPORTS
gimp_item_get_linked
gimp_item_get_lock_content
gimp_item_get_lock_position
+ gimp_item_get_metadata
gimp_item_get_name
gimp_item_get_parasite
gimp_item_get_parasite_list
@@ -576,6 +577,7 @@ EXPORTS
gimp_item_set_linked
gimp_item_set_lock_content
gimp_item_set_lock_position
+ gimp_item_set_metadata
gimp_item_set_name
gimp_item_set_tattoo
gimp_item_set_visible
diff --git a/libgimp/gimp.h b/libgimp/gimp.h
index 0b89b20..bf23188 100644
--- a/libgimp/gimp.h
+++ b/libgimp/gimp.h
@@ -46,6 +46,7 @@
#include <libgimp/gimpgradientselect.h>
#include <libgimp/gimpimage.h>
#include <libgimp/gimpimagecolorprofile.h>
+#include <libgimp/gimpitem.h>
#include <libgimp/gimplayer.h>
#include <libgimp/gimppalette.h>
#include <libgimp/gimppalettes.h>
diff --git a/libgimp/gimpimagemetadata.c b/libgimp/gimpimagemetadata.c
index 5591403..7115437 100644
--- a/libgimp/gimpimagemetadata.c
+++ b/libgimp/gimpimagemetadata.c
@@ -28,6 +28,7 @@
#include "gimp.h"
#include "gimpui.h"
#include "gimpimagemetadata.h"
+#include "libgimpbase/gimpbase.h"
#include "libgimp-intl.h"
@@ -44,7 +45,6 @@ static gboolean gimp_image_metadata_rotate_dialog (gint32 image_I
GExiv2Orientation orientation,
const gchar *parasite_name);
-
/* public functions */
/**
@@ -67,7 +67,7 @@ gimp_image_metadata_load_prepare (gint32 image_ID,
GFile *file,
GError **error)
{
- GimpMetadata *metadata;
+ GimpMetadata *metadata;
g_return_val_if_fail (image_ID > 0, NULL);
g_return_val_if_fail (mime_type != NULL, NULL);
@@ -81,18 +81,24 @@ gimp_image_metadata_load_prepare (gint32 image_ID,
#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");
+// FILE *f = fopen ("/tmp/gimp-test-xml1", "w");
+ FILE *f = fopen ("e:\\gimp-test-xml1", "w");
fprintf (f, "%s", xml);
fclose (f);
- f = fopen ("/tmp/gimp-test-xml2", "w");
+ GimpMetadata *new = gimp_metadata_deserialize (xml);
+
+ gimp_metadata_print (new);
+
+ gchar *xml2 = gimp_metadata_serialize (new);
+
+// f = fopen ("/tmp/gimp-test-xml2", "w");
+ f = fopen ("e:\\gimp-test-xml2", "w");
fprintf (f, "%s", xml2);
fclose (f);
- system ("diff -u /tmp/gimp-test-xml1 /tmp/gimp-test-xml2");
+// system ("diff -u /tmp/gimp-test-xml1 /tmp/gimp-test-xml2");
g_free (xml);
g_free (xml2);
@@ -100,7 +106,7 @@ gimp_image_metadata_load_prepare (gint32 image_ID,
}
#endif
- gexiv2_metadata_erase_exif_thumbnail (metadata);
+ gexiv2_metadata_erase_exif_thumbnail (GEXIV2_METADATA (metadata));
}
return metadata;
@@ -122,6 +128,7 @@ gimp_image_metadata_load_prepare (gint32 image_ID,
*/
void
gimp_image_metadata_load_finish (gint32 image_ID,
+ gint32 layer_ID,
const gchar *mime_type,
GimpMetadata *metadata,
GimpMetadataLoadFlags flags,
@@ -129,17 +136,28 @@ gimp_image_metadata_load_finish (gint32 image_ID,
{
g_return_if_fail (image_ID > 0);
g_return_if_fail (mime_type != NULL);
- g_return_if_fail (GEXIV2_IS_METADATA (metadata));
+ g_return_if_fail (IS_GIMP_METADATA(metadata));
+
+ if (flags & GIMP_METADATA_LOAD_ORIENTATION)
+ {
+ gimp_image_metadata_rotate_query (image_ID, mime_type,
+ metadata, interactive);
+ }
if (flags & GIMP_METADATA_LOAD_COMMENT)
{
- gchar *comment;
+ GimpAttribute *attribute = NULL;
+ gchar *comment = NULL;
- comment = gexiv2_metadata_get_tag_interpreted_string (metadata,
- "Exif.Photo.UserComment");
- if (! comment)
- comment = gexiv2_metadata_get_tag_interpreted_string (metadata,
- "Exif.Image.ImageDescription");
+ attribute = gimp_metadata_get_attribute (metadata, "Exif.Photo.UserComment");
+ if (attribute)
+ comment = gimp_attribute_get_string (attribute);
+ else
+ {
+ attribute = gimp_metadata_get_attribute (metadata, "Exif.Image.ImageDescription");
+ if (attribute)
+ comment = gimp_attribute_get_string (attribute);
+ }
if (comment)
{
@@ -155,7 +173,6 @@ gimp_image_metadata_load_finish (gint32 image_ID,
gimp_parasite_free (parasite);
}
}
-
if (flags & GIMP_METADATA_LOAD_RESOLUTION)
{
gdouble xres;
@@ -169,19 +186,10 @@ gimp_image_metadata_load_finish (gint32 image_ID,
}
}
- if (flags & GIMP_METADATA_LOAD_ORIENTATION)
- {
- gimp_image_metadata_rotate_query (image_ID, mime_type,
- metadata, interactive);
- }
-
if (flags & GIMP_METADATA_LOAD_COLORSPACE)
{
GimpColorProfile *profile = gimp_image_get_color_profile (image_ID);
- /* only look for colorspace information from metadata if the
- * image didn't contain an embedded color profile
- */
if (! profile)
{
GimpMetadataColorspace colorspace;
@@ -209,7 +217,11 @@ gimp_image_metadata_load_finish (gint32 image_ID,
g_object_unref (profile);
}
- gimp_image_set_metadata (image_ID, metadata);
+ gimp_image_set_metadata (image_ID, metadata);
+ if (layer_ID > 0)
+ {
+ gimp_item_set_metadata (layer_ID, metadata);
+ }
}
/**
@@ -219,7 +231,7 @@ gimp_image_metadata_load_finish (gint32 image_ID,
* @suggested_flags: Suggested default values for the @flags passed to
* gimp_image_metadata_save_finish()
*
- * Gets the image metadata for saving it using
+ * Gets the image attributes for saving using
* gimp_image_metadata_save_finish().
*
* The @suggested_flags are determined from what kind of metadata
@@ -227,7 +239,7 @@ gimp_image_metadata_load_finish (gint32 image_ID,
* value for GIMP_METADATA_SAVE_THUMBNAIL is determined by whether
* there was a thumbnail in the previously imported image.
*
- * Returns: The image's metadata, prepared for saving.
+ * Returns: The image's attributes, prepared for saving.
*
* Since: 2.10
*/
@@ -236,7 +248,7 @@ gimp_image_metadata_save_prepare (gint32 image_ID,
const gchar *mime_type,
GimpMetadataSaveFlags *suggested_flags)
{
- GimpMetadata *metadata;
+ GimpMetadata *metadata = NULL;
g_return_val_if_fail (image_ID > 0, NULL);
g_return_val_if_fail (mime_type != NULL, NULL);
@@ -244,133 +256,36 @@ gimp_image_metadata_save_prepare (gint32 image_ID,
*suggested_flags = GIMP_METADATA_SAVE_ALL;
+ /* this is a GimpMetadata XML-Packet
+ * it is deserialized to a GimpMetadata object.
+ * Deserialization fills the GExiv2Metadata object of the GimpMetadata*/
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 (! gexiv2_metadata_has_exif (metadata))
+ if (! gimp_metadata_has_tag_type (metadata, TAG_EXIF))
*suggested_flags &= ~GIMP_METADATA_SAVE_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 */
- if (! gexiv2_metadata_has_xmp (metadata))
+ if (! gimp_metadata_has_tag_type (metadata, TAG_XMP))
*suggested_flags &= ~GIMP_METADATA_SAVE_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 */
- if (! gexiv2_metadata_has_iptc (metadata))
+ if (! gimp_metadata_has_tag_type (metadata, TAG_IPTC))
*suggested_flags &= ~GIMP_METADATA_SAVE_IPTC;
- g_snprintf (buffer, sizeof (buffer),
- "%d-%d-%d",
- g_date_time_get_year (datetime),
- g_date_time_get_month (datetime),
- g_date_time_get_day_of_month (datetime));
- gexiv2_metadata_set_tag_string (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);
/* Thumbnail */
if (FALSE /* FIXME if (original image had a thumbnail) */)
*suggested_flags &= ~GIMP_METADATA_SAVE_THUMBNAIL;
+
}
return metadata;
@@ -401,17 +316,26 @@ gimp_image_metadata_save_finish (gint32 image_ID,
GFile *file,
GError **error)
{
- GExiv2Metadata *new_metadata;
+ GExiv2Metadata *gimpmetadata;
+ GExiv2Metadata *filemetadata;
+ GimpMetadata *gexivdata;
+ gboolean success = FALSE;
+ gchar buffer[32];
+ GDateTime *datetime;
+ const GimpParasite *comment_parasite;
+ const gchar *comment = NULL;
+ gint image_width;
+ gint image_height;
+ gdouble xres;
+ gdouble yres;
+ gchar *value;
+
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);
@@ -421,74 +345,199 @@ gimp_image_metadata_save_finish (gint32 image_ID,
GIMP_METADATA_SAVE_THUMBNAIL)))
return TRUE;
+ 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);
+
/* read metadata from saved file */
- new_metadata = gimp_metadata_load_from_file (file, error);
+ gexivdata = gimp_metadata_load_from_file (file, error);
- if (! new_metadata)
+ if (! gexivdata)
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);
+ filemetadata = GEXIV2_METADATA (gexivdata);
+ gimpmetadata = GEXIV2_METADATA (metadata);
+
+ support_exif = gexiv2_metadata_get_supports_exif (filemetadata);
+ support_xmp = gexiv2_metadata_get_supports_xmp (filemetadata);
+ support_iptc = gexiv2_metadata_get_supports_iptc (filemetadata);
- if ((flags & GIMP_METADATA_SAVE_EXIF) && support_exif)
+ /* Exif */
+
+ if (flags & GIMP_METADATA_SAVE_EXIF && support_exif)
{
- gchar **exif_data = gexiv2_metadata_get_exif_tags (metadata);
+ gint i;
+
+ gchar **exif_data = gexiv2_metadata_get_exif_tags (gimpmetadata);
for (i = 0; exif_data[i] != NULL; i++)
{
- if (! gexiv2_metadata_has_tag (new_metadata, exif_data[i]) &&
+ if (! gexiv2_metadata_has_tag (filemetadata, 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 = gexiv2_metadata_get_tag_string (gimpmetadata, exif_data[i]);
+ gexiv2_metadata_set_tag_string (filemetadata, exif_data[i],
value);
g_free (value);
}
}
g_strfreev (exif_data);
+
+ if (comment)
+ {
+ gimp_metadata_new_attribute (gexivdata,
+ "Exif.Photo.UserComment",
+ (gchar *) comment,
+ TYPE_UNICODE);
+ gimp_metadata_new_attribute (gexivdata,
+ "Exif.Image.ImageDescription",
+ (gchar *) comment,
+ TYPE_ASCII);
+ }
+
+ 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));
+ gimp_metadata_new_attribute (gexivdata,
+ "Exif.Image.DateTime",
+ buffer,
+ TYPE_ASCII);
+
+ gimp_metadata_new_attribute (gexivdata,
+ "Exif.Image.Software",
+ PACKAGE_STRING,
+ TYPE_ASCII);
+
+ gimp_metadata_set_pixel_size (gexivdata,
+ image_width, image_height);
+
+ gimp_image_get_resolution (image_ID, &xres, &yres);
+ gimp_metadata_set_resolution (gexivdata, xres, yres,
+ gimp_image_get_unit (image_ID));
}
- if ((flags & GIMP_METADATA_SAVE_XMP) && support_xmp)
+ /* XMP */
+
+ if (flags & GIMP_METADATA_SAVE_XMP && support_xmp)
{
- gchar **xmp_data = gexiv2_metadata_get_xmp_tags (metadata);
+ gint i;
+
+ gchar **xmp_data = gexiv2_metadata_get_xmp_tags (gimpmetadata);
for (i = 0; xmp_data[i] != NULL; i++)
{
- if (! gexiv2_metadata_has_tag (new_metadata, xmp_data[i]) &&
+ GimpAttribute *attribute;
+
+ if (! gexiv2_metadata_has_tag (filemetadata, 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);
+ /* we need the attribute-value here, because some xmp-tags cannot be read out and stored like
exif tags:
+ * xmp-tags allow structures like lists. These structures need this special handling. */
+ attribute = gimp_metadata_get_attribute (metadata, xmp_data[i]);
+ gimp_metadata_add_attribute (gexivdata, attribute);
}
}
g_strfreev (xmp_data);
+
+ gimp_metadata_new_attribute (gexivdata,
+ "Xmp.dc.Format",
+ (gchar *) mime_type,
+ TYPE_ASCII);
+
+ if (! g_strcmp0 (mime_type, "image/tiff"))
+ {
+ /* TIFF specific XMP data */
+
+ g_snprintf (buffer, sizeof (buffer), "%d", image_width);
+ gimp_metadata_new_attribute (gexivdata,
+ "Xmp.tiff.ImageWidth",
+ buffer,
+ TYPE_ASCII);
+
+ g_snprintf (buffer, sizeof (buffer), "%d", image_height);
+ gimp_metadata_new_attribute (gexivdata,
+ "Xmp.tiff.ImageLength",
+ buffer,
+ TYPE_ASCII);
+
+ 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));
+ gimp_metadata_new_attribute (gexivdata,
+ "Xmp.tiff.DateTime",
+ buffer,
+ TYPE_ASCII);
+ }
}
- if ((flags & GIMP_METADATA_SAVE_IPTC) && support_iptc)
+ /* IPTC */
+
+ if (flags & GIMP_METADATA_SAVE_IPTC && support_iptc)
{
- gchar **iptc_data = gexiv2_metadata_get_iptc_tags (metadata);
+ gint i;
+
+ gchar **iptc_data = gexiv2_metadata_get_iptc_tags (gimpmetadata);
for (i = 0; iptc_data[i] != NULL; i++)
{
- if (! gexiv2_metadata_has_tag (new_metadata, iptc_data[i]) &&
+ GimpAttribute *attribute;
+
+ if (! gexiv2_metadata_has_tag (filemetadata, 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);
+ /* we need the attribute-value here, because iptc-tags of TYPE_MULTIPLE cannot be read out and
stored like exif tags:
+ * These tags need this special handling. */
+ attribute = gimp_metadata_get_attribute (metadata, iptc_data[i]);
+ gimp_metadata_add_attribute (gexivdata, attribute);
}
}
g_strfreev (iptc_data);
+
+ g_snprintf (buffer, sizeof (buffer),
+ "%d-%d-%d",
+ g_date_time_get_year (datetime),
+ g_date_time_get_month (datetime),
+ g_date_time_get_day_of_month (datetime));
+ gimp_metadata_new_attribute (gexivdata,
+ "Iptc.Application2.DateCreated",
+ buffer,
+ TYPE_ASCII);
+
+ g_snprintf (buffer, sizeof (buffer),
+ "%02d:%02d:%02d",
+ g_date_time_get_hour (datetime),
+ g_date_time_get_minute (datetime),
+ g_date_time_get_second (datetime));
+ gimp_metadata_new_attribute (gexivdata,
+ "Iptc.Application2.TimeCreated",
+ buffer,
+ TYPE_ASCII);
+
+ g_date_time_unref (datetime);
+
}
- if (flags & GIMP_METADATA_SAVE_THUMBNAIL)
+ /* Thumbnail */
+
+ if ((flags & GIMP_METADATA_SAVE_THUMBNAIL) && g_strcmp0 (mime_type, "image/tiff"))
{
GdkPixbuf *thumb_pixbuf;
gchar *thumb_buffer;
@@ -524,27 +573,27 @@ gimp_image_metadata_save_finish (gint32 image_ID,
{
gchar buffer[32];
- gexiv2_metadata_set_exif_thumbnail_from_buffer (new_metadata,
+ gexiv2_metadata_set_exif_thumbnail_from_buffer (filemetadata,
(guchar *) thumb_buffer,
count);
g_snprintf (buffer, sizeof (buffer), "%d", thumbw);
- gexiv2_metadata_set_tag_string (new_metadata,
+ gexiv2_metadata_set_tag_string (filemetadata,
"Exif.Thumbnail.ImageWidth",
buffer);
g_snprintf (buffer, sizeof (buffer), "%d", thumbh);
- gexiv2_metadata_set_tag_string (new_metadata,
+ gexiv2_metadata_set_tag_string (filemetadata,
"Exif.Thumbnail.ImageLength",
buffer);
- gexiv2_metadata_set_tag_string (new_metadata,
+ gexiv2_metadata_set_tag_string (filemetadata,
"Exif.Thumbnail.BitsPerSample",
"8 8 8");
- gexiv2_metadata_set_tag_string (new_metadata,
+ gexiv2_metadata_set_tag_string (filemetadata,
"Exif.Thumbnail.SamplesPerPixel",
"3");
- gexiv2_metadata_set_tag_string (new_metadata,
+ gexiv2_metadata_set_tag_string (filemetadata,
"Exif.Thumbnail.PhotometricInterpretation",
"6");
@@ -554,9 +603,9 @@ gimp_image_metadata_save_finish (gint32 image_ID,
g_object_unref (thumb_pixbuf);
}
- success = gimp_metadata_save_to_file (new_metadata, file, error);
+ success = gimp_metadata_save_to_file (gexivdata, file, error);
- g_object_unref (new_metadata);
+ g_object_unref (filemetadata);
return success;
}
@@ -565,21 +614,25 @@ gint32
gimp_image_metadata_load_thumbnail (GFile *file,
GError **error)
{
- GimpMetadata *metadata;
- GInputStream *input_stream;
- GdkPixbuf *pixbuf;
- guint8 *thumbnail_buffer;
- gint thumbnail_size;
- gint32 image_ID = -1;
+ GimpMetadata *metadata;
+ GExiv2Metadata *gexiv2metadata;
+ GInputStream *input_stream;
+ GdkPixbuf *pixbuf;
+ guint8 *thumbnail_buffer;
+ gint thumbnail_size;
+ gint32 image_ID = -1;
g_return_val_if_fail (G_IS_FILE (file), -1);
g_return_val_if_fail (error == NULL || *error == NULL, -1);
metadata = gimp_metadata_load_from_file (file, error);
+
+ gexiv2metadata = GEXIV2_METADATA (metadata);
+
if (! metadata)
return -1;
- if (! gexiv2_metadata_get_exif_thumbnail (metadata,
+ if (! gexiv2_metadata_get_exif_thumbnail (gexiv2metadata,
&thumbnail_buffer,
&thumbnail_size))
{
@@ -611,7 +664,7 @@ gimp_image_metadata_load_thumbnail (GFile *file,
gimp_image_insert_layer (image_ID, layer_ID, -1, 0);
gimp_image_metadata_rotate (image_ID,
- gexiv2_metadata_get_orientation (metadata));
+ gexiv2_metadata_get_orientation (gexiv2metadata));
}
g_object_unref (metadata);
@@ -726,12 +779,15 @@ gimp_image_metadata_rotate_query (gint32 image_ID,
GimpMetadata *metadata,
gboolean interactive)
{
+ GExiv2Metadata *gexiv2metadata;
GimpParasite *parasite;
gchar *parasite_name;
GExiv2Orientation orientation;
gboolean query = interactive;
- orientation = gexiv2_metadata_get_orientation (metadata);
+ gexiv2metadata = GEXIV2_METADATA (metadata);
+
+ orientation = gexiv2_metadata_get_orientation (gexiv2metadata);
if (orientation <= GEXIV2_ORIENTATION_NORMAL ||
orientation > GEXIV2_ORIENTATION_MAX)
@@ -771,9 +827,9 @@ gimp_image_metadata_rotate_query (gint32 image_ID,
gimp_image_metadata_rotate (image_ID, orientation);
- gexiv2_metadata_set_tag_string (metadata,
+ gexiv2_metadata_set_tag_string (gexiv2metadata,
"Exif.Image.Orientation", "1");
- gexiv2_metadata_set_tag_string (metadata,
+ gexiv2_metadata_set_tag_string (gexiv2metadata,
"Xmp.tiff.Orientation", "1");
}
diff --git a/libgimp/gimpimagemetadata.h b/libgimp/gimpimagemetadata.h
index 26d0b71..1771ec2 100644
--- a/libgimp/gimpimagemetadata.h
+++ b/libgimp/gimpimagemetadata.h
@@ -35,6 +35,7 @@ GimpMetadata * gimp_image_metadata_load_prepare (gint32 image_ID
GFile *file,
GError **error);
void gimp_image_metadata_load_finish (gint32 image_ID,
+ gint32 layer_ID,
const gchar *mime_type,
GimpMetadata *metadata,
GimpMetadataLoadFlags flags,
diff --git a/libgimp/gimpitem.c b/libgimp/gimpitem.c
new file mode 100644
index 0000000..ae19d5d
--- /dev/null
+++ b/libgimp/gimpitem.c
@@ -0,0 +1,86 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2000 Peter Mattis and Spencer Kimball
+ *
+ * gimpitem.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 "gimp.h"
+#include "gimpitem.h"
+
+
+/**
+ * gimp_item_get_metadata:
+ * @item_ID: The item.
+ *
+ * Returns the item's metadata.
+ *
+ * Returns metadata from the item.
+ *
+ * Returns: The metadata, or %NULL if there is none.
+ *
+ * Since: GIMP 2.10
+ **/
+GimpMetadata *
+gimp_item_get_metadata (gint32 item_ID)
+{
+ GimpMetadata *metadata = NULL;
+ gchar *metadata_string;
+
+ metadata_string = _gimp_item_get_metadata (item_ID);
+ if (metadata_string)
+ {
+ metadata = gimp_metadata_deserialize (metadata_string);
+ g_free (metadata_string);
+ }
+
+ return metadata;
+}
+
+/**
+ * gimp_item_set_metadata:
+ * @item_ID: The item.
+ * @metadata: The GimpMetadata object.
+ *
+ * Set the item's metadata.
+ *
+ * Sets metadata on the item, or deletes it if
+ * @metadata is %NULL.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: GIMP 2.10
+ **/
+gboolean
+gimp_item_set_metadata (gint32 item_ID,
+ GimpMetadata *metadata)
+{
+ gchar *metadata_string = NULL;
+ gboolean success;
+
+ if (metadata)
+ metadata_string = gimp_metadata_serialize (metadata);
+
+ success = _gimp_item_set_metadata (item_ID, metadata_string);
+
+ if (metadata_string)
+ g_free (metadata_string);
+
+ return success;
+}
+
diff --git a/libgimp/gimpitem.h b/libgimp/gimpitem.h
new file mode 100644
index 0000000..2b7ecf7
--- /dev/null
+++ b/libgimp/gimpitem.h
@@ -0,0 +1,40 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2000 Peter Mattis and Spencer Kimball
+ *
+ * gimpitem.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_ITEM_H__
+#define __GIMP_ITEM_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gboolean gimp_item_set_metadata (gint32 item_ID,
+ GimpMetadata *metadata);
+GimpMetadata * gimp_item_get_metadata (gint32 item_ID);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_ITEM_H__ */
diff --git a/libgimp/gimpitem_pdb.c b/libgimp/gimpitem_pdb.c
index c1a004f..2f68038 100644
--- a/libgimp/gimpitem_pdb.c
+++ b/libgimp/gimpitem_pdb.c
@@ -935,6 +935,68 @@ gimp_item_set_tattoo (gint32 item_ID,
}
/**
+ * _gimp_item_get_metadata:
+ * @item_ID: The item.
+ *
+ * Returns the item's metadata.
+ *
+ * Returns metadata from the item.
+ *
+ * Returns: The metadata as a xml string.
+ **/
+gchar *
+_gimp_item_get_metadata (gint32 item_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *metadata_string = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-item-get-metadata",
+ &nreturn_vals,
+ GIMP_PDB_ITEM, item_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_item_set_metadata:
+ * @item_ID: The item.
+ * @metadata_string: The metadata as a xml string.
+ *
+ * Set the item's metadata.
+ *
+ * Sets metadata on the item.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+_gimp_item_set_metadata (gint32 item_ID,
+ const gchar *metadata_string)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-item-set-metadata",
+ &nreturn_vals,
+ GIMP_PDB_ITEM, item_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_item_attach_parasite:
* @item_ID: The item.
* @parasite: The parasite to attach to the item.
diff --git a/libgimp/gimpitem_pdb.h b/libgimp/gimpitem_pdb.h
index 6b81f04..a16d0e4 100644
--- a/libgimp/gimpitem_pdb.h
+++ b/libgimp/gimpitem_pdb.h
@@ -32,49 +32,52 @@ G_BEGIN_DECLS
/* For information look into the C source or the html documentation */
-gboolean gimp_item_is_valid (gint32 item_ID);
-gint32 gimp_item_get_image (gint32 item_ID);
-gboolean gimp_item_delete (gint32 item_ID);
-gboolean gimp_item_is_drawable (gint32 item_ID);
-gboolean gimp_item_is_layer (gint32 item_ID);
-gboolean gimp_item_is_text_layer (gint32 item_ID);
-gboolean gimp_item_is_channel (gint32 item_ID);
-gboolean gimp_item_is_layer_mask (gint32 item_ID);
-gboolean gimp_item_is_selection (gint32 item_ID);
-gboolean gimp_item_is_vectors (gint32 item_ID);
-gboolean gimp_item_is_group (gint32 item_ID);
-gint32 gimp_item_get_parent (gint32 item_ID);
-gint* gimp_item_get_children (gint32 item_ID,
- gint *num_children);
-gchar* gimp_item_get_name (gint32 item_ID);
-gboolean gimp_item_set_name (gint32 item_ID,
- const gchar *name);
-gboolean gimp_item_get_visible (gint32 item_ID);
-gboolean gimp_item_set_visible (gint32 item_ID,
- gboolean visible);
-gboolean gimp_item_get_linked (gint32 item_ID);
-gboolean gimp_item_set_linked (gint32 item_ID,
- gboolean linked);
-gboolean gimp_item_get_lock_content (gint32 item_ID);
-gboolean gimp_item_set_lock_content (gint32 item_ID,
- gboolean lock_content);
-gboolean gimp_item_get_lock_position (gint32 item_ID);
-gboolean gimp_item_set_lock_position (gint32 item_ID,
- gboolean lock_position);
-GimpColorTag gimp_item_get_color_tag (gint32 item_ID);
-gboolean gimp_item_set_color_tag (gint32 item_ID,
- GimpColorTag color_tag);
-gint gimp_item_get_tattoo (gint32 item_ID);
-gboolean gimp_item_set_tattoo (gint32 item_ID,
- gint tattoo);
-gboolean gimp_item_attach_parasite (gint32 item_ID,
- const GimpParasite *parasite);
-gboolean gimp_item_detach_parasite (gint32 item_ID,
- const gchar *name);
-GimpParasite* gimp_item_get_parasite (gint32 item_ID,
- const gchar *name);
-gchar** gimp_item_get_parasite_list (gint32 item_ID,
- gint *num_parasites);
+gboolean gimp_item_is_valid (gint32 item_ID);
+gint32 gimp_item_get_image (gint32 item_ID);
+gboolean gimp_item_delete (gint32 item_ID);
+gboolean gimp_item_is_drawable (gint32 item_ID);
+gboolean gimp_item_is_layer (gint32 item_ID);
+gboolean gimp_item_is_text_layer (gint32 item_ID);
+gboolean gimp_item_is_channel (gint32 item_ID);
+gboolean gimp_item_is_layer_mask (gint32 item_ID);
+gboolean gimp_item_is_selection (gint32 item_ID);
+gboolean gimp_item_is_vectors (gint32 item_ID);
+gboolean gimp_item_is_group (gint32 item_ID);
+gint32 gimp_item_get_parent (gint32 item_ID);
+gint* gimp_item_get_children (gint32 item_ID,
+ gint *num_children);
+gchar* gimp_item_get_name (gint32 item_ID);
+gboolean gimp_item_set_name (gint32 item_ID,
+ const gchar *name);
+gboolean gimp_item_get_visible (gint32 item_ID);
+gboolean gimp_item_set_visible (gint32 item_ID,
+ gboolean visible);
+gboolean gimp_item_get_linked (gint32 item_ID);
+gboolean gimp_item_set_linked (gint32 item_ID,
+ gboolean linked);
+gboolean gimp_item_get_lock_content (gint32 item_ID);
+gboolean gimp_item_set_lock_content (gint32 item_ID,
+ gboolean lock_content);
+gboolean gimp_item_get_lock_position (gint32 item_ID);
+gboolean gimp_item_set_lock_position (gint32 item_ID,
+ gboolean lock_position);
+GimpColorTag gimp_item_get_color_tag (gint32 item_ID);
+gboolean gimp_item_set_color_tag (gint32 item_ID,
+ GimpColorTag color_tag);
+gint gimp_item_get_tattoo (gint32 item_ID);
+gboolean gimp_item_set_tattoo (gint32 item_ID,
+ gint tattoo);
+G_GNUC_INTERNAL gchar* _gimp_item_get_metadata (gint32 item_ID);
+G_GNUC_INTERNAL gboolean _gimp_item_set_metadata (gint32 item_ID,
+ const gchar *metadata_string);
+gboolean gimp_item_attach_parasite (gint32 item_ID,
+ const GimpParasite *parasite);
+gboolean gimp_item_detach_parasite (gint32 item_ID,
+ const gchar *name);
+GimpParasite* gimp_item_get_parasite (gint32 item_ID,
+ const gchar *name);
+gchar** gimp_item_get_parasite_list (gint32 item_ID,
+ gint *num_parasites);
G_END_DECLS
diff --git a/libgimpbase/Makefile.am b/libgimpbase/Makefile.am
index c97e68f..d35c9c0 100644
--- a/libgimpbase/Makefile.am
+++ b/libgimpbase/Makefile.am
@@ -90,6 +90,8 @@ libgimpbase_sources = \
gimpparam.h \
gimpversion.h \
\
+ gimpattribute.c \
+ gimpattribute.h \
gimpbase-private.c \
gimpbase-private.h \
gimpchecks.c \
@@ -110,6 +112,8 @@ libgimpbase_sources = \
gimpparasiteio.h \
gimpprotocol.c \
gimpprotocol.h \
+ gimprational.c \
+ gimprational.h \
gimprectangle.c \
gimprectangle.h \
gimpreloc.c \
@@ -143,6 +147,7 @@ libgimpbaseinclude_HEADERS = \
gimpparam.h \
gimpversion.h \
\
+ gimpattribute.h \
gimpchecks.h \
gimpdatafiles.h \
gimpenv.h \
@@ -150,6 +155,7 @@ libgimpbaseinclude_HEADERS = \
gimpmetadata.h \
gimpparasite.h \
gimpparasiteio.h \
+ gimprational.h \
gimprectangle.h \
gimpsignal.h \
gimpunit.h \
diff --git a/libgimpbase/gimpattribute.c b/libgimpbase/gimpattribute.c
new file mode 100644
index 0000000..0450b4d
--- /dev/null
+++ b/libgimpbase/gimpattribute.c
@@ -0,0 +1,2002 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimpattribute.c
+ * Copyright (C) 2014 Hartmut Kuhse <hatti gimp org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib-object.h>
+
+#ifdef G_OS_WIN32
+#endif
+
+#include "gimpbasetypes.h"
+#include "gimpattribute.h"
+#include "gimprational.h"
+
+#define _g_free0(var) (var = (g_free (var), NULL))
+#define _g_regex_unref0(var) ((var == NULL) ? NULL : (var = (g_regex_unref (var), NULL)))
+#define _g_error_free0(var) ((var == NULL) ? NULL : (var = (g_error_free (var), NULL)))
+
+typedef struct _TagTime TagTime;
+typedef struct _TagDate TagDate;
+
+struct _TagTime {
+ int tag_time_sec;
+ int tag_time_min;
+ int tag_time_hour;
+ int tag_tz_hour;
+ int tag_tz_min;
+ };
+
+struct _TagDate {
+ int tag_year;
+ int tag_month;
+ int tag_day;
+ };
+
+
+typedef struct _GimpAttributeClass GimpAttributeClass;
+typedef struct _GimpAttributePrivate GimpAttributePrivate;
+
+struct _GimpAttribute {
+ GObject parent_instance;
+ GimpAttributePrivate *priv;
+};
+
+struct _GimpAttributeClass {
+ GObjectClass parent_class;
+};
+
+struct _GimpAttributePrivate {
+ gchar *name;
+ gchar *sorted_name;
+
+ gchar *tag_value;
+ gchar *interpreted_value;
+
+ gchar *exif_type;
+
+ GimpAttributeValueType value_type;
+ GimpAttributeTagType tag_type;
+
+ gchar *attribute_type;
+ gchar *attribute_ifd;
+ gchar *attribute_tag;
+ gboolean is_new_name_space;
+
+ gboolean has_structure;
+ GimpAttributeStructureType structure_type;
+ GSList *attribute_structure;
+};
+
+enum {
+ PROP_0,
+ PROP_NAME,
+ PROP_LONG_VALUE,
+ PROP_SLONG_VALUE,
+ PROP_SHORT_VALUE,
+ PROP_SSHORT_VALUE,
+ PROP_DATE_VALUE,
+ PROP_TIME_VALUE,
+ PROP_ASCII_VALUE,
+ PROP_BYTE_VALUE,
+ PROP_MULTIPLE_VALUE,
+ PROP_RATIONAL_VALUE,
+ PROP_SRATIONAL_VALUE
+};
+
+static const gchar *xmp_namespaces[] =
+{
+ "dc",
+ "xmp",
+ "xmpRights",
+ "xmpMM",
+ "xmpBJ",
+ "xmpTPg",
+ "xmpDM",
+ "pdf",
+ "photoshop",
+ "crs",
+ "tiff",
+ "exif",
+ "aux",
+ "plus",
+ "digiKam",
+ "iptc",
+ "iptcExt",
+ "Iptc4xmpCore",
+ "Iptc4xmpExt",
+ "MicrosoftPhoto",
+ "kipi",
+ "mediapro",
+ "expressionmedia",
+ "MP",
+ "MPRI",
+ "MPReg",
+ "mwg-rs"
+};
+
+
+static gpointer gimp_attribute_parent_class = NULL;
+static gint counter = 0;
+
+
+static GimpAttribute* gimp_attribute_construct (GType object_type,
+ const gchar *name);
+static void gimp_attribute_finalize (GObject *obj);
+static void gimp_attribute_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_attribute_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_attribute_set_name (GimpAttribute *attribute,
+ const gchar *value);
+static gboolean get_tag_time (gchar *input,
+ TagTime *tm);
+static gboolean get_tag_date (gchar *input,
+ TagDate *dt);
+static gchar * gimp_attribute_escape_value (gchar *name,
+ gchar *value,
+ gboolean *encoded);
+static gchar * gimp_attribute_get_structure_number (gchar *cptag,
+ gint start,
+ gint *struct_number);
+
+static gchar* string_replace_str (const gchar *original,
+ const gchar *old_pattern,
+ const gchar *replacement);
+static gint string_index_of (gchar *haystack,
+ gchar *needle,
+ gint start_index);
+static glong string_strnlen (gchar *str,
+ glong maxlen);
+static gchar* string_substring (gchar *attribute,
+ glong offset,
+ glong len);
+
+
+/**
+ * gimp_attribute_new_string:
+ *
+ * @name: a constant #gchar that describes the name of the attribute
+ * @value: a #gchar value, representing a time
+ * @type: a #GimpAttributeTagType
+ *
+ * Creates a new #GimpAttribute object with @name as name and a #gchar @value
+ * as value of type @type.
+ * @value is converted to the correct type.
+ *
+ * Return value: a new @GimpAttribute
+ *
+ * Since: 2.10
+ */
+GimpAttribute*
+gimp_attribute_new_string (const gchar *name,
+ gchar *value,
+ GimpAttributeValueType type)
+{
+ GimpAttribute *attribute = NULL;
+ GimpAttributePrivate *private;
+
+ attribute = gimp_attribute_construct (GIMP_TYPE_ATTRIBUTE, name);
+ if (attribute)
+ {
+ private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
+ private->tag_value = g_strdup (value);
+ private->value_type = type;
+ }
+
+ return attribute;
+}
+
+/**
+ * gimp_attribute_get_name:
+ *
+ * @attribute: a @GimpAttribute
+ *
+ * Return value: full name of the #GimpAttribute object
+ * e.g. "Exif.Image.XResolution"
+ *
+ * Since: 2.10
+ */
+const gchar*
+gimp_attribute_get_name (GimpAttribute* attribute)
+{
+ GimpAttributePrivate *private;
+ g_return_val_if_fail (attribute != NULL, NULL);
+ private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
+ return (const gchar *) private->name;
+}
+
+/**
+ * gimp_attribute_get_sortable_name:
+ *
+ * @attribute: a @GimpAttribute
+ *
+ * Return value: name of the #GimpAttribute object for sorting
+ * e.g. from tag:
+ * "xmp.xmpMM.History[2]..."
+ * it returns:
+ * "xmp.xmpMM.History[000002]..."
+ *
+ * Since: 2.10
+ */
+const gchar*
+gimp_attribute_get_sortable_name (GimpAttribute* attribute)
+{
+ GimpAttributePrivate *private;
+ g_return_val_if_fail (attribute != NULL, NULL);
+ private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
+ if (private->sorted_name)
+ return (const gchar *) private->sorted_name;
+ else
+ return (const gchar *) private->name;
+}
+
+/**
+ * gimp_attribute_get_attribute_type:
+ *
+ * @attribute: a @GimpAttribute
+ *
+ * Return value: attribute type of the #GimpAttribute object
+ * e.g. "exif", "iptc", ...
+ *
+ * Since: 2.10
+ */
+const gchar*
+gimp_attribute_get_attribute_type (GimpAttribute* attribute)
+{
+ GimpAttributePrivate *private;
+ g_return_val_if_fail (attribute != NULL, NULL);
+ private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
+ return (const gchar *) private->attribute_type;
+}
+
+/**
+ * gimp_attribute_get_attribute_ifd:
+ *
+ * @attribute: a @GimpAttribute
+ *
+ * Return value: ifd of the #GimpAttribute object
+ *
+ * Since: 2.10
+ */
+const gchar*
+gimp_attribute_get_attribute_ifd (GimpAttribute* attribute)
+{
+ GimpAttributePrivate *private;
+ g_return_val_if_fail (attribute != NULL, NULL);
+ private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
+ return (const gchar *) private->attribute_ifd;
+}
+
+/**
+ * gimp_attribute_get_attribute_tag:
+ *
+ * @attribute: a @GimpAttribute
+ *
+ * Return value: tag of the #GimpAttribute object
+ *
+ * Since: 2.10
+ */
+const gchar*
+gimp_attribute_get_attribute_tag (GimpAttribute* attribute)
+{
+ GimpAttributePrivate *private;
+ g_return_val_if_fail (attribute != NULL, NULL);
+ private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
+ return (const gchar *) private->attribute_tag;
+}
+
+/**
+ * gimp_attribute_set_value_type:
+ *
+ * @attribute: a @GimpAttribute
+ * @value_type: a @GimpAttributeValueType
+ *
+ * sets value type of the #GimpAttribute object
+ *
+ * Since: 2.10
+ */
+void
+gimp_attribute_set_value_type (GimpAttribute* attribute,
+ GimpAttributeValueType value_type)
+{
+ GimpAttributePrivate *private;
+ g_return_if_fail (attribute != NULL);
+ private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
+ private->value_type = value_type;
+}
+
+/**
+ * gimp_attribute_get_value_type:
+ *
+ * @attribute: a @GimpAttribute
+ *
+ * Return value: value type of the #GimpAttribute object
+ *
+ * Since: 2.10
+ */
+GimpAttributeValueType
+gimp_attribute_get_value_type (GimpAttribute* attribute)
+{
+ GimpAttributePrivate *private;
+ g_return_val_if_fail (attribute != NULL, TYPE_INVALID);
+ private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
+ return private->value_type;
+}
+
+/**
+ * gimp_attribute_get_attribute_structure:
+ *
+ * @attribute: a @GimpAttribute
+ *
+ * Return value: attribute_structure of the #GimpAttribute object
+ * This is the start value of a xmpBag sequence.
+ *
+ * Since: 2.10
+ */
+GSList *
+gimp_attribute_get_attribute_structure (GimpAttribute* attribute)
+{
+ GimpAttributePrivate *private;
+ g_return_val_if_fail (attribute != NULL, NULL);
+ private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
+
+ return private->attribute_structure;
+}
+/**
+ * gimp_attribute_is_new_namespace:
+ *
+ * @attribute: a @GimpAttribute
+ *
+ * Return value: if namespace must be defined or not
+ *
+ * Since: 2.10
+ */
+const gboolean
+gimp_attribute_is_new_namespace (GimpAttribute* attribute)
+{
+ GimpAttributePrivate *private;
+ g_return_val_if_fail (attribute != NULL, FALSE);
+ private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
+ return (const gboolean) private->is_new_name_space;
+}
+
+/**
+ * gimp_attribute_get_value:
+ *
+ * @attribute: a @GimpAttribute
+ *
+ * Return value: #GValue of the #GimpAttribute object
+ * The type of value is represented by glib-types
+ *
+ * Since: 2.10
+ */
+GValue
+gimp_attribute_get_value (GimpAttribute *attribute)
+{
+ GimpAttributePrivate *private;
+ GValue val = G_VALUE_INIT;
+ gchar *value_string;
+
+ g_return_val_if_fail(GIMP_IS_ATTRIBUTE (attribute), val);
+
+ private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
+ value_string = private->tag_value;
+
+ switch (private->value_type)
+ {
+ case TYPE_LONG:
+ {
+ g_value_init (&val, G_TYPE_ULONG);
+ g_value_set_ulong (&val, (gulong) atol (value_string));
+ }
+ break;
+ case TYPE_SLONG:
+ {
+ g_value_init (&val, G_TYPE_LONG);
+ g_value_set_long (&val, (glong) atol (value_string));
+ }
+ break;
+ case TYPE_SHORT:
+ {
+ g_value_init (&val, G_TYPE_UINT);
+ g_value_set_uint (&val, (guint) atoi (value_string));
+ }
+ break;
+ case TYPE_SSHORT:
+ {
+ g_value_init (&val, G_TYPE_INT);
+ g_value_set_int (&val, (gint) atoi (value_string));
+ }
+ break;
+ case TYPE_DATE:
+ {
+ g_value_init (&val, G_TYPE_STRING);
+ g_value_set_string (&val, value_string);
+ }
+ break;
+ case TYPE_TIME:
+ {
+ g_value_init (&val, G_TYPE_STRING);
+ g_value_set_string (&val, value_string);
+ }
+ break;
+ case TYPE_ASCII:
+ {
+ g_value_init (&val, G_TYPE_STRING);
+ g_value_set_string (&val, value_string);
+ }
+ break;
+ case TYPE_UNICODE:
+ {
+ g_value_init (&val, G_TYPE_STRING);
+ g_value_set_string (&val, value_string);
+ }
+ break;
+ case TYPE_BYTE:
+ {
+ g_value_init (&val, G_TYPE_UCHAR);
+ g_value_set_uchar (&val, value_string[0]);
+ }
+ break;
+ case TYPE_MULTIPLE:
+ {
+ gchar **result;
+
+ g_value_init (&val, G_TYPE_STRV);
+ result = g_strsplit (value_string, "\n", 0);
+ g_value_set_boxed (&val, result);
+ }
+ break;
+ case TYPE_RATIONAL:
+ {
+ gchar **nom;
+ gchar **rats;
+ gint count;
+ gint i;
+ Rational *rval;
+ GArray *rational_array;
+
+ rats = g_strsplit (value_string, " ", -1);
+
+ count = 0;
+ while (rats[count])
+ count++;
+
+ rational_array = g_array_new (TRUE, TRUE, sizeof (RationalValue));
+
+ for (i = 0; i < count; i++)
+ {
+ RationalValue or;
+
+ nom = g_strsplit (rats[i], "/", 2);
+
+ if (nom[0] && nom[1])
+ {
+ or.nom = (guint) atoi (nom [0]);
+ or.denom = (guint) atoi (nom [1]);
+ }
+ else
+ count--;
+
+ rational_array = g_array_append_val (rational_array, or);
+
+ g_strfreev (nom);
+ }
+
+ rval = g_slice_new (Rational);
+
+ rval->rational_array = rational_array;
+ rval->length = count;
+
+ g_value_init (&val, GIMP_TYPE_RATIONAL);
+ g_value_set_boxed (&val, rval);
+
+ g_strfreev (rats);
+ }
+ break;
+ case TYPE_SRATIONAL:
+ {
+ gchar **nom;
+ gchar **srats;
+ gint count;
+ gint i;
+ SRational *srval;
+ GArray *srational_array;
+
+ srats = g_strsplit (value_string, " ", -1);
+
+ count = 0;
+ while (srats[count])
+ count++;
+
+ srational_array = g_array_new (TRUE, TRUE, sizeof (SRationalValue));
+
+ for (i = 0; i < count; i++)
+ {
+ SRationalValue or;
+
+ nom = g_strsplit (srats[i], "/", 2);
+
+ if (nom[0] && nom[1])
+ {
+ or.nom = (gint) atoi (nom [0]);
+ or.denom = (guint) atoi (nom [1]);
+ }
+ else
+ count--;
+
+ srational_array = g_array_append_val (srational_array, or);
+
+ g_strfreev (nom);
+ }
+
+ srval = g_slice_new (SRational);
+
+ srval->srational_array = srational_array;
+ srval->length = count;
+
+ g_value_init (&val, GIMP_TYPE_SRATIONAL);
+ g_value_set_boxed (&val, srval);
+
+ g_strfreev (srats);
+ }
+ break;
+ case TYPE_UNKNOWN:
+ default:
+ break;
+ }
+
+ return val;
+}
+
+/**
+ * gimp_attribute_get_string:
+ *
+ * @attribute: a @GimpAttribute
+ *
+ * Return value: newly allocated #gchar string representation of the #GimpAttribute object.
+ * The return is always a valid value according to the #GimpAttributeValueType
+ *
+ * Since: 2.10
+ */
+gchar*
+gimp_attribute_get_string (GimpAttribute *attribute)
+{
+ GimpAttributePrivate *private;
+ gchar *val = NULL;
+ TagDate dt = {0};
+ TagTime tm = {0};
+
+ g_return_val_if_fail(GIMP_IS_ATTRIBUTE (attribute), NULL);
+
+ private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
+
+ switch (private->value_type)
+ {
+ case TYPE_INVALID:
+ break;
+ case TYPE_LONG:
+ {
+ val = g_strdup_printf ("%lu", (gulong) atol (private->tag_value));
+ }
+ break;
+ case TYPE_SLONG:
+ {
+ val = g_strdup_printf ("%ld", (glong) atol (private->tag_value));
+ }
+ break;
+ case TYPE_FLOAT:
+ {
+ val = g_strdup_printf ("%.5f", (gfloat) atof (private->tag_value));
+ }
+ break;
+ case TYPE_DOUBLE:
+ {
+ val = g_strdup_printf ("%.6f", (gdouble) atof (private->tag_value));
+ }
+ break;
+ case TYPE_SHORT:
+ {
+ val = g_strdup_printf ("%u", (gushort) atoi (private->tag_value));
+ }
+ break;
+ case TYPE_SSHORT:
+ {
+ val = g_strdup_printf ("%d", (gshort) atoi (private->tag_value));
+ }
+ break;
+ case TYPE_DATE:
+ {
+ get_tag_date (private->tag_value, &dt);
+
+ val = g_strdup_printf ("%4d-%02d-%02d", dt.tag_year,
+ dt.tag_month,
+ dt.tag_day);
+ }
+ break;
+ case TYPE_TIME:
+ {
+ gchar *tz_h = NULL;
+ get_tag_time (private->tag_value, &tm);
+
+ tz_h = tm.tag_tz_hour < 0 ?
+ g_strdup_printf ("-%02d", tm.tag_tz_hour) :
+ g_strdup_printf ("+%02d", tm.tag_tz_hour);
+
+ val = g_strdup_printf ("%02d:%02d:%02d%s:%02d", tm.tag_time_hour,
+ tm.tag_time_min,
+ tm.tag_time_sec,
+ tz_h,
+ tm.tag_tz_min);
+ g_free (tz_h);
+ }
+ break;
+ case TYPE_ASCII:
+ {
+ val = g_strdup (private->tag_value);
+ }
+ break;
+ case TYPE_UNICODE:
+ {
+ val = g_strdup (private->tag_value);
+ }
+ break;
+ case TYPE_BYTE:
+ {
+ val = g_strdup_printf ("%c", private->tag_value[0]);
+ }
+ break;
+ case TYPE_MULTIPLE:
+ {
+ val = g_strdup (private->tag_value);
+ }
+ break;
+ case TYPE_RATIONAL:
+ {
+ gint i;
+ GString *string;
+ Rational *rational;
+
+ string_to_rational (private->tag_value, &rational);
+
+ string = g_string_new (NULL);
+
+ for (i = 0; i < rational->length; i++)
+ {
+ RationalValue or;
+
+ or = g_array_index (rational->rational_array, RationalValue, i);
+
+ g_string_append_printf (string, "%u/%u ",
+ or.nom,
+ or.denom);
+ }
+ g_string_truncate (string, string->len-1);
+ val = g_string_free (string, FALSE);
+ }
+ break;
+ case TYPE_SRATIONAL:
+ {
+ gint i;
+ GString *string;
+ SRational *srational;
+
+ string_to_srational (private->tag_value, &srational);
+
+ string = g_string_new (NULL);
+
+ for (i = 0; i < srational->length; i++)
+ {
+ SRationalValue or;
+
+ or = g_array_index (srational->srational_array, SRationalValue, i);
+
+ g_string_append_printf (string, "%u/%u ",
+ or.nom,
+ or.denom);
+ }
+
+ g_string_truncate (string, string->len-1);
+ val = g_string_free (string, FALSE);
+ }
+ break;
+ case TYPE_UNKNOWN:
+ default:
+ break;
+ }
+ return val;
+}
+
+/**
+ * gimp_attribute_get_interpreted_string:
+ *
+ * @attribute: a #GimpAttribute
+ *
+ * returns the interpreted value or the pure string, if no
+ * interpreted value is found
+ *
+ * Since: 2.10
+ */
+const gchar *
+gimp_attribute_get_interpreted_string (GimpAttribute *attribute)
+{
+ GimpAttributePrivate *private;
+
+ g_return_val_if_fail(GIMP_IS_ATTRIBUTE (attribute), NULL);
+
+ private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
+
+ if (private->interpreted_value)
+ return (const gchar *) private->interpreted_value;
+ else
+ return (const gchar *) private->tag_value;
+}
+
+/**
+ * gimp_attribute_set_interpreted_string:
+ *
+ * @attribute: a #GimpAttribute
+ * @value: a constant #gchar
+ *
+ * sets the interpreted string of the #GimpAttribute object to
+ * a @value
+ *
+ * @value can be freed after
+ *
+ * Since: 2.10
+ */
+void
+gimp_attribute_set_interpreted_string (GimpAttribute* attribute, const gchar* value)
+{
+ GimpAttributePrivate *private;
+
+ g_return_if_fail(GIMP_IS_ATTRIBUTE (attribute));
+ g_return_if_fail (value != NULL);
+
+ private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
+
+ private->interpreted_value = g_strdup (value);
+}
+
+/**
+ * gimp_attribute_get_tag_type:
+ *
+ * @attribute: a @GimpAttribute
+ *
+ *
+ * Return value: #GimpAttributeTagType
+ * The tag type of the Attribute
+ *
+ * Since: 2.10
+ */
+GimpAttributeTagType
+gimp_attribute_get_tag_type (GimpAttribute *attribute)
+{
+ GimpAttributePrivate *private;
+
+ g_return_val_if_fail (attribute != NULL, TAG_INVALID);
+
+ private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
+
+ return private->tag_type;
+}
+
+void
+gimp_attribute_print (GimpAttribute *attribute)
+{
+ const gchar *tag;
+ const gchar *interpreted;
+ gchar *value;
+
+ g_return_if_fail(GIMP_IS_ATTRIBUTE (attribute));
+
+ tag = gimp_attribute_get_name (attribute);
+ value = gimp_attribute_get_string (attribute);
+ interpreted = gimp_attribute_get_interpreted_string (attribute);
+
+ g_print ("---\n%p\nTag: %s\n\tValue:%s\n\tInterpreted value:%s\n", attribute, tag, value, interpreted);
+
+}
+
+/**
+ * gimp_attribute_get_gps_degree:
+ *
+ * @attribute: a @GimpAttribute
+ *
+ * returns the degree representation of the #GimpAttribute GPS value,
+ * -999.9 otherwise.
+ *
+ * Return value: #gdouble degree representation of the #GimpAttributes GPS value
+ *
+ * Since: 2.10
+ */
+gdouble
+gimp_attribute_get_gps_degree (GimpAttribute *attribute)
+{
+ gdouble return_val;
+ GimpAttributePrivate *private;
+
+ g_return_val_if_fail (attribute != NULL, TAG_INVALID);
+
+ private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
+
+ if (private->value_type == TYPE_RATIONAL)
+ {
+ gint i;
+ gdouble r_val = 0.0;
+ Rational *rational;
+
+ string_to_rational (private->tag_value, &rational);
+
+ for (i = 0; i < rational->length; i++)
+ {
+ RationalValue or;
+
+ or = g_array_index (rational->rational_array, RationalValue, i);
+
+ r_val = (gfloat)or.nom / (gfloat)or.denom;
+
+ if (i == 0)
+ return_val = r_val;
+ else if (i == 1)
+ return_val = return_val + (r_val / 60);
+ else if (i == 2)
+ return_val = return_val + (r_val / 3600);
+ else return -999.9;
+ }
+
+ return return_val;
+ }
+ else
+ return -999.9;
+
+}
+
+/**
+ * gimp_attribute_get_xml:
+ *
+ * @attribute: a @GimpAttribute
+ *
+ * returns the xml representation of the #GimpAttribute
+ * in form:
+ *
+ * <tag name="name" type="GimpAttributeValueType">
+ * <value [encoding=base64]>value</value>
+ * [<interpreted [encoding=base64]>interpreted value</interpreted>]
+ * </tag>
+ *
+ * Return value: #gchar xml representation of the #GimpAttribute object
+ *
+ * Since: 2.10
+ */
+gchar*
+gimp_attribute_get_xml (GimpAttribute *attribute)
+{
+ gchar *v_escaped = NULL;
+ gchar *i_escaped = NULL;
+ gchar *interpreted_tag_elem = NULL;
+ gboolean is_interpreted = FALSE;
+ gboolean encoded;
+ gchar *start_tag_elem;
+ gchar *end_tag_elem;
+ gchar *value_tag_elem;
+ gchar *struct_tag_elem = NULL;
+ gboolean utf = TRUE;
+ GimpAttributePrivate *private;
+ GString *xml;
+
+ g_return_val_if_fail (GIMP_IS_ATTRIBUTE (attribute), NULL);
+
+ private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
+
+ xml = g_string_new (NULL);
+
+ if(private->interpreted_value && g_strcmp0 (private->tag_value, private->interpreted_value))
+ {
+ is_interpreted = TRUE;
+ }
+
+ v_escaped = gimp_attribute_escape_value (private->name, private->tag_value, &encoded);
+
+ start_tag_elem = g_strdup_printf (" <attribute name=\"%s\" type=\"%d\">\n", private->name,
private->value_type);
+ end_tag_elem = g_strdup_printf (" </attribute>");
+
+ g_string_append (xml, start_tag_elem);
+
+ if (encoded)
+ {
+ value_tag_elem = g_strdup_printf (" <value encoding=\"base64\">%s</value>\n", v_escaped);
+ }
+ else
+ {
+ value_tag_elem = g_strdup_printf (" <value>%s</value>\n", v_escaped);
+ }
+
+ g_string_append (xml, value_tag_elem);
+
+ if (private->has_structure)
+ {
+ struct_tag_elem = g_strdup_printf (" <structure>%d</structure>\n", private->structure_type);
+ g_string_append (xml, struct_tag_elem);
+ }
+
+ if (is_interpreted && utf)
+ {
+ i_escaped = gimp_attribute_escape_value (private->name, private->interpreted_value, &encoded);
+
+ if (encoded)
+ {
+ interpreted_tag_elem = g_strdup_printf (" <interpreted encoding=\"base64\">%s</interpreted>\n",
i_escaped);
+ }
+ else
+ {
+ interpreted_tag_elem = g_strdup_printf (" <interpreted>%s</interpreted>\n", i_escaped);
+ }
+
+ g_string_append (xml, interpreted_tag_elem);
+ }
+
+ g_string_append (xml, end_tag_elem);
+
+ g_free (v_escaped);
+ g_free (start_tag_elem);
+ g_free (end_tag_elem);
+ g_free (value_tag_elem);
+ if (i_escaped)
+ g_free (i_escaped);
+ if (interpreted_tag_elem)
+ g_free (interpreted_tag_elem);
+ if (struct_tag_elem)
+ g_free (interpreted_tag_elem);
+
+ return g_string_free (xml, FALSE);
+}
+
+/**
+ * gimp_attribute_get_value_type_from_string:
+ *
+ * @exiv_tag_type_string: a @gchar
+ *
+ * converts a string representation tag type from gexiv2/exiv2
+ * to a #GimpAttributeValueType
+ *
+ * Return value: #GimpAttributeValueType
+ *
+ * Since: 2.10
+ */
+GimpAttributeValueType
+gimp_attribute_get_value_type_from_string (const gchar* string)
+{
+ GimpAttributeValueType type;
+ gchar *lowchar;
+
+ if (! string)
+ return TYPE_INVALID;
+
+ lowchar = g_ascii_strdown (string, -1);
+
+ if (!g_strcmp0 (lowchar, "invalid"))
+ type = TYPE_INVALID;
+ else if (!g_strcmp0 (lowchar, "byte"))
+ type = TYPE_BYTE;
+ else if (!g_strcmp0 (lowchar, "ascii"))
+ type = TYPE_ASCII;
+ else if (!g_strcmp0 (lowchar, "short"))
+ type = TYPE_SHORT;
+ else if (!g_strcmp0 (lowchar, "long"))
+ type = TYPE_LONG;
+ else if (!g_strcmp0 (lowchar, "rational"))
+ type = TYPE_RATIONAL;
+ else if (!g_strcmp0 (lowchar, "sbyte"))
+ type = TYPE_BYTE;
+ else if (!g_strcmp0 (lowchar, "undefined"))
+ type = TYPE_ASCII;
+ else if (!g_strcmp0 (lowchar, "sshort"))
+ type = TYPE_SSHORT;
+ else if (!g_strcmp0 (lowchar, "slong"))
+ type = TYPE_SLONG;
+ else if (!g_strcmp0 (lowchar, "srational"))
+ type = TYPE_SRATIONAL;
+ else if (!g_strcmp0 (lowchar, "float"))
+ type = TYPE_FLOAT;
+ else if (!g_strcmp0 (lowchar, "double"))
+ type = TYPE_DOUBLE;
+ else if (!g_strcmp0 (lowchar, "ifd"))
+ type = TYPE_ASCII;
+ else if (!g_strcmp0 (lowchar, "string"))
+ type = TYPE_UNICODE;
+ else if (!g_strcmp0 (lowchar, "date"))
+ type = TYPE_DATE;
+ else if (!g_strcmp0 (lowchar, "time"))
+ type = TYPE_TIME;
+ else if (!g_strcmp0 (lowchar, "comment"))
+ type = TYPE_UNICODE;
+ else if (!g_strcmp0 (lowchar, "directory"))
+ type = TYPE_UNICODE;
+ else if (!g_strcmp0 (lowchar, "xmptext"))
+ type = TYPE_ASCII;
+ else if (!g_strcmp0 (lowchar, "xmpalt"))
+ type = TYPE_MULTIPLE;
+ else if (!g_strcmp0 (lowchar, "xmpbag"))
+ type = TYPE_MULTIPLE;
+ else if (!g_strcmp0 (lowchar, "xmpseq"))
+ type = TYPE_MULTIPLE;
+ else if (!g_strcmp0 (lowchar, "langalt"))
+ type = TYPE_MULTIPLE;
+ else
+ type = TYPE_INVALID;
+
+ g_free (lowchar);
+
+ return type;
+}
+
+/**
+ * gimp_attribute_is_valid:
+ *
+ * @attribute: a @GimpAttribute
+ *
+ * checks, if @attribute is valid.
+ * A @GimpAttribute is valid, if the @GimpAttributeValueType
+ * has a valid entry.
+ *
+ * Return value: @gboolean: TRUE if valid, FALSE otherwise
+ *
+ * Since: 2.10
+ */
+gboolean
+gimp_attribute_is_valid (GimpAttribute *attribute)
+{
+ GimpAttributePrivate *private;
+
+ g_return_val_if_fail (attribute != NULL, FALSE);
+
+ private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
+
+ if (private->value_type == TYPE_INVALID)
+ return FALSE;
+ else
+ return TRUE;
+}
+
+/*
+ * internal functions
+ */
+
+/**
+ * gimp_attribute_set_name:
+ *
+ * @attribute: a #GimpAttribute
+ * @value: a constant #gchar
+ *
+ * sets the name of the #GimpAttribute object to
+ * a @value
+ *
+ * @value can be freed after
+ *
+ * Since: 2.10
+ */
+static void
+gimp_attribute_set_name (GimpAttribute* attribute, const gchar* value)
+{
+ gchar **split_name;
+ gchar *lowchar;
+ GimpAttributePrivate *private;
+
+ g_return_if_fail (attribute != NULL);
+
+ private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
+
+ _g_free0 (private->name);
+
+ private->name = g_strdup (value);
+
+ private->name = string_replace_str (private->name, "Iptc4xmpExt", "iptcExt");
+ private->name = string_replace_str (private->name, "Iptc4xmpCore", "iptc");
+
+// if (! g_strcmp0 (private->name, "Exif.Image.ResolutionUnit"))
+// {
+// g_print ("found: %s\n", private->name);
+// }
+
+ split_name = g_strsplit (private->name, ".", 3);
+
+ if (split_name [0])
+ private->attribute_type = split_name [0];
+ if (split_name [1])
+ private->attribute_ifd = split_name [1];
+ if (split_name [2])
+ private->attribute_tag = split_name [2];
+
+ attribute->priv->is_new_name_space = FALSE;
+
+ lowchar = g_ascii_strdown (private->attribute_type, -1);
+
+ if (!g_strcmp0 (lowchar, "exif"))
+ {
+ private->tag_type = TAG_EXIF;
+ }
+ else if (!g_strcmp0 (lowchar, "xmp"))
+ {
+ gint j;
+ gint p1 = 0;
+ gint p2 = 0;
+ gboolean is_known = FALSE;
+ gchar *structure_tag_name = NULL;
+
+ structure_tag_name = g_strdup (private->name);
+
+ private->tag_type = TAG_XMP;
+
+ while (p2 != -1)
+ {
+ p2 = string_index_of (structure_tag_name, "[", p1);
+
+ if (p2 > -1)
+ {
+ gchar *struct_string = NULL;
+ gint struct_number;
+
+ private->has_structure = TRUE;
+ private->structure_type = STRUCTURE_TYPE_BAG; /* there's no way to get the real type from
gexiv2 */
+
+ struct_string = string_substring (structure_tag_name, 0, p2);
+ private->attribute_structure = g_slist_prepend (private->attribute_structure, struct_string);
+ structure_tag_name = gimp_attribute_get_structure_number (structure_tag_name, p2,
&struct_number);
+
+ p1 = p2 + 1;
+ }
+ }
+
+ if (g_strcmp0 (private->name, structure_tag_name))
+ private->sorted_name = structure_tag_name;
+ else
+ private->sorted_name = NULL;
+
+ for (j = 0; j < G_N_ELEMENTS (xmp_namespaces); j++)
+ {
+ if (! g_strcmp0 (private->attribute_ifd, xmp_namespaces[j]))
+ {
+ is_known = TRUE;
+ break;
+ }
+ }
+ if (! is_known)
+ {
+ private->is_new_name_space = TRUE;
+ }
+ }
+ else if (!g_strcmp0 (lowchar, "iptc"))
+ {
+ private->tag_type = TAG_IPTC;
+ }
+ else if (!g_strcmp0 (lowchar, "gimp"))
+ {
+ private->tag_type = TAG_GIMP;
+ }
+ else
+ {
+ private->tag_type = TAG_MISC;
+ }
+
+ g_free (lowchar);
+}
+
+/**
+ * gimp_attribute_set_structure_type:
+ *
+ * @attribute: a #GimpAttribute
+ * @value: a GimpAttributeStructureType
+ *
+ * Sets the structure type, Bag, Seq, None, etc.
+ * The structure is not checked.
+ * The structure type is only relevant for
+ * GimpAttribute, that are part of a structure.
+ *
+ * Since : 2.10
+ */
+void
+gimp_attribute_set_structure_type (GimpAttribute *attribute,
+ GimpAttributeStructureType value)
+{
+ GimpAttributePrivate *private;
+
+ g_return_if_fail (attribute != NULL);
+
+ private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
+
+ private->structure_type = value;
+}
+
+/**
+ * gimp_attribute_get_structure_type:
+ *
+ * @attribute: a #GimpAttribute
+ *
+ * The structure type, Bag, Seq, None, etc.
+ * Return value: a #GimpAttributeStructureType
+ *
+ * Since : 2.10
+ */
+GimpAttributeStructureType
+gimp_attribute_get_structure_type (GimpAttribute *attribute)
+{
+ GimpAttributePrivate *private;
+
+ g_return_val_if_fail (attribute != NULL, STRUCTURE_TYPE_NONE);
+
+ private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
+
+ return private->structure_type;
+
+}
+
+/**
+ * gimp_attribute_has_structure:
+ *
+ * @attribute: a #GimpAttribute
+ *
+ * Return value: TRUE, if @attribute is part
+ * of a structure.
+ *
+ * Since : 2.10
+ */
+gboolean
+gimp_attribute_has_structure (GimpAttribute *attribute)
+{
+ GimpAttributePrivate *private;
+
+ g_return_val_if_fail (attribute != NULL, FALSE);
+
+ private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
+
+ return private->has_structure;
+}
+
+/**
+ * gimp_attribute_escape_value:
+ *
+ * @name: a gchar *
+ * @value: a gchar*
+ * @encoded: a pointer to a gboolean
+ *
+ * converts @value into an escaped string for GMarkup.
+ * If @value is not valid UTF-8, it is converted
+ * into a base64 coded string and @encoded is set to TRUE,
+ * otherwise FALSE
+ *
+ * Return value: a new allocated @gchar *
+ *
+ * Since : 2.10
+ */
+static gchar *
+gimp_attribute_escape_value (gchar *name,
+ gchar *value,
+ gboolean *encoded)
+{
+
+ if (!g_utf8_validate (value, -1, NULL))
+ {
+ gchar *enc_val = NULL;
+
+ *encoded = TRUE;
+ enc_val = g_base64_encode ((const guchar *) value,
+ strlen (value) + 1);
+
+ return enc_val;
+ }
+ *encoded = FALSE;
+
+ return g_markup_escape_text (value, -1);
+}
+
+/**
+ * gimp_attribute_copy:
+ *
+ * @attribute: a #GimpAttribute
+ *
+ * duplicates a #GimpAttribute object
+ *
+ * Return value: a new @GimpAttribute or %NULL
+ *
+ * Since : 2.10
+ */
+GimpAttribute*
+gimp_attribute_copy (GimpAttribute *attribute)
+{
+ GimpAttribute *new_attribute = NULL;
+ GimpAttributePrivate *private;
+ GimpAttributePrivate *new_private;
+
+ g_return_val_if_fail (GIMP_IS_ATTRIBUTE (attribute), NULL);
+
+ new_attribute = (GimpAttribute*) g_object_new (GIMP_TYPE_ATTRIBUTE, NULL);
+
+ if (new_attribute)
+ {
+ private = GIMP_ATTRIBUTE_GET_PRIVATE(attribute);
+ new_private = GIMP_ATTRIBUTE_GET_PRIVATE(new_attribute);
+ if (private->name)
+ new_private->name = g_strdup (private->name);
+
+ if (private->tag_value)
+ new_private->tag_value = g_strdup (private->tag_value);
+
+ if (private->interpreted_value)
+ new_private->interpreted_value = g_strdup (private->interpreted_value);
+
+ if (private->exif_type)
+ new_private->exif_type = g_strdup (private->exif_type);
+
+ if (private->attribute_type)
+ new_private->attribute_type = g_strdup (private->attribute_type);
+
+ if (private->attribute_ifd)
+ new_private->attribute_ifd = g_strdup (private->attribute_ifd);
+
+ if (private->attribute_tag)
+ new_private->attribute_tag = g_strdup (private->attribute_tag);
+
+ new_private->is_new_name_space = private->is_new_name_space;
+ new_private->value_type = private->value_type;
+ new_private->tag_type = private->tag_type;
+
+ return new_attribute;
+ }
+
+ return NULL;
+}
+
+/**
+ * gimp_attribute_construct:
+ *
+ * @object_type: a #GType
+ * @name: a constant #gchar that describes the name of the attribute
+ *
+ * constructs a new #GimpAttribute object
+ *
+ * Return value: a new @GimpAttribute or %NULL if no @name is set
+ *
+ * Since : 2.10
+ */
+static GimpAttribute*
+gimp_attribute_construct (GType object_type,
+ const gchar *name)
+{
+ GimpAttribute * attribute = NULL;
+
+ g_return_val_if_fail (name != NULL, NULL);
+
+ attribute = (GimpAttribute*) g_object_new (object_type, NULL);
+ if (attribute)
+ {
+ gimp_attribute_set_name (attribute, name);
+ }
+ return attribute;
+}
+
+/**
+ * gimp_attribute_class_init:
+ *
+ * class initializer
+ */
+static void
+gimp_attribute_class_init (GimpAttributeClass * klass)
+{
+ gimp_attribute_parent_class = g_type_class_peek_parent (klass);
+ g_type_class_add_private (klass, sizeof(GimpAttributePrivate));
+
+ G_OBJECT_CLASS (klass)->get_property = gimp_attribute_get_property;
+ G_OBJECT_CLASS (klass)->set_property = gimp_attribute_set_property;
+ G_OBJECT_CLASS (klass)->finalize = gimp_attribute_finalize;
+}
+
+/**
+ * gimp_attribute_instance_init:
+ *
+ * instance initializer
+ */
+static void
+gimp_attribute_instance_init (GimpAttribute * attribute)
+{
+ GimpAttributePrivate *private;
+ attribute->priv = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
+ private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
+
+ private->name = NULL;
+ private->sorted_name = NULL;
+ private->tag_value = NULL;
+ private->interpreted_value = NULL;
+ private->exif_type = NULL;
+ private->value_type = TYPE_INVALID;
+ private->attribute_type = NULL;
+ private->attribute_ifd = NULL;
+ private->attribute_tag = NULL;
+ private->attribute_structure = NULL;
+ private->is_new_name_space = FALSE;
+ private->has_structure = FALSE;
+ private->structure_type = STRUCTURE_TYPE_NONE;
+}
+
+/**
+ * gimp_attribute_finalize:
+ *
+ * instance finalizer
+ */
+static void
+gimp_attribute_finalize (GObject* obj)
+{
+ GimpAttribute *attribute;
+ GimpAttributePrivate *private;
+
+ g_return_if_fail (GIMP_IS_ATTRIBUTE (obj));
+
+ attribute = GIMP_ATTRIBUTE (obj);
+ private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
+
+ counter++;
+
+ if(private->name)
+ _g_free0 (private->name);
+
+ if(private->sorted_name)
+ _g_free0 (private->sorted_name);
+
+ if(private->tag_value)
+ _g_free0 (private->tag_value);
+
+ if(private->interpreted_value)
+ _g_free0 (private->interpreted_value);
+
+ if(private->exif_type)
+ _g_free0 (private->exif_type);
+
+ if(private->attribute_type)
+ _g_free0 (private->attribute_type);
+
+ if(private->attribute_ifd)
+ _g_free0 (private->attribute_ifd);
+
+ if(private->attribute_tag)
+ _g_free0 (private->attribute_tag);
+
+ if(private->attribute_structure)
+ g_slist_free_full (private->attribute_structure, g_free);
+
+ G_OBJECT_CLASS (gimp_attribute_parent_class)->finalize (obj);
+ obj = NULL;
+}
+
+/**
+ * gimp_attribute_get_property
+ *
+ * instance get property
+ */
+static void
+gimp_attribute_get_property (GObject * object,
+ guint property_id,
+ GValue * value,
+ GParamSpec * pspec)
+{
+}
+
+/**
+ * gimp_attribute_set_property
+ *
+ * instance set property
+ */
+static void
+gimp_attribute_set_property (GObject * object,
+ guint property_id,
+ const GValue * value,
+ GParamSpec * pspec)
+{
+}
+
+static gchar *
+gimp_attribute_get_structure_number (gchar *cptag, gint start, gint *number)
+{
+ gint p1;
+ gchar *tag;
+ gchar *new_tag;
+ gchar *oldnr;
+ gchar *newnr;
+
+ start++;
+
+ tag = g_strdup (cptag);
+
+ p1 = string_index_of (tag, "]", start);
+
+ if (p1 > -1)
+ {
+ gchar *number_string = NULL;
+ gint len;
+
+ len = p1-start;
+
+ number_string = string_substring (tag, start, len);
+
+ *number = atoi (number_string);
+
+ oldnr = g_strdup_printf ("[%d]", *number);
+ newnr = g_strdup_printf ("[%06d]", *number);
+ new_tag = string_replace_str (tag, oldnr, newnr);
+
+ g_free (oldnr);
+ g_free (newnr);
+ g_free (tag);
+ g_free (number_string);
+
+ return new_tag;
+ }
+ else
+ {
+ *number = -1;
+ return NULL;
+ }
+
+}
+
+/**
+ * gimp_attribute_get_type
+ *
+ * Return value: #GimpAttribute type
+ */
+GType
+gimp_attribute_get_type (void)
+{
+ static volatile gsize gimp_attribute_type_id__volatile = 0;
+ if (g_once_init_enter(&gimp_attribute_type_id__volatile))
+ {
+ static const GTypeInfo g_define_type_info =
+ { sizeof(GimpAttributeClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gimp_attribute_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL,
+ sizeof(GimpAttribute),
+ 0,
+ (GInstanceInitFunc) gimp_attribute_instance_init,
+ NULL
+ };
+
+ GType gimp_attribute_type_id;
+ gimp_attribute_type_id = g_type_register_static (G_TYPE_OBJECT,
+ "GimpAttribute",
+ &g_define_type_info,
+ 0);
+
+ g_once_init_leave(&gimp_attribute_type_id__volatile,
+ gimp_attribute_type_id);
+ }
+ return gimp_attribute_type_id__volatile;
+}
+
+
+/*
+ * Helper functions
+ */
+
+/**
+ * get_tag_time:
+ *
+ * @input: a #gchar array
+ * @tm: a pointer to a #TagTime struct
+ *
+ * converts a ISO 8601 IPTC Time to a TagTime structure
+ * Input is:
+ * HHMMSS
+ * HHMMSS:HHMM
+ *
+ * Return value: #gboolean for success
+ *
+ * Since: 2.10
+ */
+static gboolean
+get_tag_time (gchar *input, TagTime *tm)
+{
+ long val;
+ GString *string = NULL;
+ gchar *tmpdate;
+ gboolean flong;
+
+ g_return_val_if_fail (input != NULL, FALSE);
+
+ input = g_strstrip (input);
+
+ if (*input == '\0' || !g_ascii_isdigit (*input))
+ return FALSE;
+
+ string = g_string_new (NULL);
+
+ val = strtoul (input, (char **)&input, 10);
+
+ if (val < 25) /* HH:MM:SS+/-HH:MM */
+ {
+ flong = FALSE;
+ g_string_append_printf (string, "%02ld", val);
+ }
+ else
+ {
+ flong = TRUE;
+ g_string_append_printf (string, "%06ld", val);
+ }
+
+
+ if (! flong) /* exactly 2 times */
+ {
+ input++;
+ val = strtoul (input, (char **)&input, 10);
+ g_string_append_printf (string, "%02ld", val);
+ input++;
+ val = strtoul (input, (char **)&input, 10);
+ g_string_append_printf (string, "%02ld", val);
+ }
+
+ tmpdate = g_string_free (string, FALSE);
+
+ val = strtoul (tmpdate, (char **)&tmpdate, 10);
+
+ /* hhmmss */
+
+ tm->tag_time_sec = val % 100;
+ tm->tag_time_min = (val % 10000) / 100;
+ tm->tag_time_hour = val / 10000;
+
+ string = g_string_new (NULL);
+
+ if (flong) /* :HHSS for time zone */
+ {
+ val = 0L;
+
+ if (*input == ':')
+ {
+ input++;
+ val = strtoul (input, (char **)&input, 10);
+ }
+ g_string_append_printf (string, "%05ld", val);
+ }
+ else
+ {
+ val = 0L;
+
+ if (*input == '+' || *input == '-') /* +/- HH:MM for time zone */
+ {
+ val = strtoul (input, (char **)&input, 10);
+ g_string_append_printf (string, "%02ld", val);
+ input++;
+ val = strtoul (input, (char **)&input, 10);
+ g_string_append_printf (string, "%02ld", val);
+ }
+ else
+ {
+ g_string_append_printf (string, "%04ld", val);
+ }
+ }
+
+ tmpdate = g_string_free (string, FALSE);
+
+ val = strtoul (tmpdate, (char **)&tmpdate, 10);
+
+ tm->tag_tz_min = (val % 100) / 100;
+ tm->tag_tz_hour = val / 100;
+
+ return *input == '\0';
+}
+
+/**
+ * get_tag_date:
+ *
+ * @input: a #gchar array
+ * @dt: a pointer to a #TagDate struct
+ *
+ * converts a ISO 8601 IPTC Date to a TagDate structure
+ * Input is:
+ * CCYYMMDD
+ *
+ * Return value: #gboolean for success
+ *
+ * Since: 2.10
+ */
+static gboolean
+get_tag_date (gchar *input, TagDate *dt)
+{
+ long val;
+ GString *string = NULL;
+ gchar *tmpdate;
+ gboolean eod;
+
+ g_return_val_if_fail (input != NULL, FALSE);
+
+ input = g_strstrip (input);
+
+ if (*input == '\0' || !g_ascii_isdigit (*input))
+ return FALSE;
+
+ string = g_string_new (NULL);
+ eod = FALSE;
+
+ while (! eod)
+ {
+ val = strtoul (input, (char **)&input, 10);
+
+ if (*input == '-' ||
+ *input == ':')
+ {
+ input++;
+ }
+ else
+ {
+ eod = TRUE;
+ }
+
+ if (val < 10)
+ g_string_append_printf (string, "%02ld", val);
+ else
+ g_string_append_printf (string, "%ld", val);
+ }
+ tmpdate = g_string_free (string, FALSE);
+
+ val = strtoul (tmpdate, (char **)&tmpdate, 10);
+
+ /* YYYYMMDD */
+ dt->tag_day = val % 100;
+ dt->tag_month = (val % 10000) / 100;
+ dt->tag_year = val / 10000;
+
+ return *input == '\0';
+}
+
+/**
+ * string_replace_str:
+ *
+ * @original: the original string
+ * @old_pattern: the pattern to replace
+ * @replacement: the replacement
+ *
+ * replaces @old_pattern by @replacement in @original
+ * This routine is copied from VALA.
+ *
+ * Return value: the new string.
+ *
+ * Since: 2.10
+ */
+static gchar*
+string_replace_str (const gchar* original, const gchar* old_pattern, const gchar* replacement) {
+ gchar *result = NULL;
+ GError *_inner_error_ = NULL;
+
+ g_return_val_if_fail (original != NULL, NULL);
+ g_return_val_if_fail (old_pattern != NULL, NULL);
+ g_return_val_if_fail (replacement != NULL, NULL);
+
+ {
+ GRegex *regex = NULL;
+ const gchar *_tmp0_ = NULL;
+ gchar *_tmp1_ = NULL;
+ gchar *_tmp2_ = NULL;
+ GRegex *_tmp3_ = NULL;
+ GRegex *_tmp4_ = NULL;
+ gchar *_tmp5_ = NULL;
+ GRegex *_tmp6_ = NULL;
+ const gchar *_tmp7_ = NULL;
+ gchar *_tmp8_ = NULL;
+ gchar *_tmp9_ = NULL;
+
+ _tmp0_ = old_pattern;
+ _tmp1_ = g_regex_escape_string (_tmp0_, -1);
+ _tmp2_ = _tmp1_;
+ _tmp3_ = g_regex_new (_tmp2_, 0, 0, &_inner_error_);
+ _tmp4_ = _tmp3_;
+ _g_free0 (_tmp2_);
+ regex = _tmp4_;
+
+ if (_inner_error_ != NULL)
+ {
+ if (_inner_error_->domain == G_REGEX_ERROR)
+ {
+ goto __catch0_g_regex_error;
+ }
+ g_critical ("file %s: line %d: unexpected error: %s (%s, %d)", __FILE__, __LINE__,
_inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code);
+ g_clear_error (&_inner_error_);
+ return NULL;
+ }
+
+ _tmp6_ = regex;
+ _tmp7_ = replacement;
+ _tmp8_ = g_regex_replace_literal (_tmp6_, original, (gssize) (-1), 0, _tmp7_, 0, &_inner_error_);
+ _tmp5_ = _tmp8_;
+
+ if (_inner_error_ != NULL)
+ {
+ _g_regex_unref0 (regex);
+ if (_inner_error_->domain == G_REGEX_ERROR)
+ {
+ goto __catch0_g_regex_error;
+ }
+ _g_regex_unref0 (regex);
+ g_critical ("file %s: line %d: unexpected error: %s (%s, %d)", __FILE__, __LINE__,
_inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code);
+ g_clear_error (&_inner_error_);
+ return NULL;
+ }
+
+ _tmp9_ = _tmp5_;
+ _tmp5_ = NULL;
+ result = _tmp9_;
+ _g_free0 (_tmp5_);
+ _g_regex_unref0 (regex);
+ return result;
+ }
+
+ goto __finally0;
+ __catch0_g_regex_error:
+ {
+ GError* e = NULL;
+ e = _inner_error_;
+ _inner_error_ = NULL;
+ g_assert_not_reached ();
+ _g_error_free0 (e);
+ }
+
+ __finally0:
+ if (_inner_error_ != NULL)
+ {
+ g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__,
_inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code);
+ g_clear_error (&_inner_error_);
+ return NULL;
+ }
+}
+
+/**
+ * string_index_of:
+ *
+ * @haystack: a #gchar
+ * @needle: a #gchar
+ * @start_index: a #gint
+ *
+ *
+ * Return value: #gint that points to the position
+ * of @needle in @haystack, starting at @start_index,
+ * or -1, if @needle is not found
+ *
+ * Since: 2.10
+ */
+static gint
+string_index_of (gchar* haystack, gchar* needle, gint start_index)
+{
+ gint result = 0;
+ gchar* temp1_ = NULL;
+ g_return_val_if_fail (haystack != NULL, -1);
+ g_return_val_if_fail (needle != NULL, -1);
+ temp1_ = strstr (((gchar*) haystack) + start_index, needle);
+ if (temp1_ != NULL)
+ {
+ result = (gint) (temp1_ - ((gchar*) haystack));
+ return result;
+ }
+ else
+ {
+ result = -1;
+ return result;
+ }
+}
+
+/**
+ * string_strnlen:
+ *
+ * @str: a #gchar
+ * @maxlen: a #glong
+ *
+ * Returns the length of @str or @maxlen, if @str
+ * is longer that @maxlen
+ *
+ * Return value: #glong
+ *
+ * Since: 2.10
+ */
+static glong
+string_strnlen (gchar* str, glong maxlen)
+{
+ glong result = 0L;
+ gchar* temp1_ = NULL;
+ temp1_ = memchr (str, 0, (gsize) maxlen);
+ if (temp1_ == NULL)
+ {
+ return maxlen;
+ }
+ else
+ {
+ result = (glong) (temp1_ - str);
+ return result;
+ }
+}
+
+/**
+ * string_substring:
+ *
+ * @string: a #gchar
+ * @offset: a #glong
+ * @len: a #glong
+ *
+ * Returns a substring of @string, starting at @offset
+ * with a legth of @len
+ *
+ * Return value: #gchar to be freed if no longer use
+ *
+ * Since: 2.10
+ */
+static gchar*
+string_substring (gchar* string, glong offset, glong len)
+{
+ gchar* result = NULL;
+ glong string_length = 0L;
+ gboolean _tmp0_ = FALSE;
+ g_return_val_if_fail(string != NULL, NULL);
+ if (offset >= ((glong) 0))
+ {
+ _tmp0_ = len >= ((glong) 0);
+ }
+ else
+ {
+ _tmp0_ = FALSE;
+ }
+
+ if (_tmp0_)
+ {
+ string_length = string_strnlen ((gchar*) string, offset + len);
+ }
+ else
+ {
+ string_length = (glong) strlen (string);
+ }
+
+ if (offset < ((glong) 0))
+ {
+ offset = string_length + offset;
+ g_return_val_if_fail (offset >= ((glong ) 0), NULL);
+ }
+ else
+ {
+ g_return_val_if_fail (offset <= string_length, NULL);
+ }
+ if (len < ((glong) 0))
+ {
+ len = string_length - offset;
+ }
+
+ g_return_val_if_fail ((offset + len) <= string_length, NULL);
+ result = g_strndup (((gchar*) string) + offset, (gsize) len);
+ return result;
+}
diff --git a/libgimpbase/gimpattribute.h b/libgimpbase/gimpattribute.h
new file mode 100644
index 0000000..c4b3a15
--- /dev/null
+++ b/libgimpbase/gimpattribute.h
@@ -0,0 +1,97 @@
+/* gimpattribute.h generated by valac 0.24.0, the Vala compiler, do not modify */
+
+
+#ifndef __GIMPATTRIBUTE_H__
+#define __GIMPATTRIBUTE_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+#define GIMP_TYPE_ATTRIBUTE (gimp_attribute_get_type ())
+#define GIMP_ATTRIBUTE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_ATTRIBUTE,
GimpAttribute))
+#define GIMP_ATTRIBUTE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_ATTRIBUTE,
GimpAttributeClass))
+#define GIMP_IS_ATTRIBUTE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_ATTRIBUTE))
+#define GIMP_IS_ATTRIBUTE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_ATTRIBUTE))
+#define GIMP_ATTRIBUTE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_ATTRIBUTE,
GimpAttributeClass))
+#define GIMP_ATTRIBUTE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GIMP_TYPE_ATTRIBUTE,
GimpAttributePrivate))
+
+#define GIMP_TYPE_ATTRIBUTE_TAG_TYPE (gimp_attribute_tag_type_get_type ())
+
+GType gimp_attribute_tag_type_get_type (void) G_GNUC_CONST;
+
+typedef enum {
+ TYPE_INVALID,
+ TYPE_LONG,
+ TYPE_SLONG,
+ TYPE_FLOAT,
+ TYPE_DOUBLE,
+ TYPE_SHORT,
+ TYPE_SSHORT,
+ TYPE_DATE,
+ TYPE_TIME,
+ TYPE_ASCII,
+ TYPE_UNICODE,
+ TYPE_BYTE,
+ TYPE_MULTIPLE,
+ TYPE_RATIONAL,
+ TYPE_SRATIONAL,
+ TYPE_UNKNOWN,
+ NUM_VALUE_TYPES
+} GimpAttributeValueType;
+
+typedef enum {
+ STRUCTURE_TYPE_BAG,
+ STRUCTURE_TYPE_SEQ,
+ STRUCTURE_TYPE_ALT,
+ STRUCTURE_TYPE_LANG,
+ STRUCTURE_TYPE_NONE,
+ STRUCTURE_TYPE_STRUCT,
+ NUM_STRUCTURE_TYPES
+} GimpAttributeStructureType;
+
+typedef enum {
+ TAG_INVALID,
+ TAG_EXIF,
+ TAG_XMP,
+ TAG_IPTC,
+ TAG_GIMP,
+ TAG_MISC,
+ NUM_TAG_TYPES
+} GimpAttributeTagType;
+
+GType gimp_attribute_get_type (void) G_GNUC_CONST;
+
+GimpAttribute* gimp_attribute_new_string (const gchar *name,
+ gchar *value,
+ GimpAttributeValueType type);
+const gchar* gimp_attribute_get_interpreted_string (GimpAttribute
*attribute);
+void gimp_attribute_set_interpreted_string (GimpAttribute
*attribute,
+ const gchar
*value);
+GimpAttribute* gimp_attribute_copy (GimpAttribute
*attribute);
+gboolean gimp_attribute_is_valid (GimpAttribute
*attribute);
+const gchar* gimp_attribute_get_attribute_type (GimpAttribute
*attribute);
+const gchar* gimp_attribute_get_attribute_ifd (GimpAttribute
*attribute);
+const gchar* gimp_attribute_get_attribute_tag (GimpAttribute
*attribute);
+const gboolean gimp_attribute_is_new_namespace (GimpAttribute
*attribute);
+const gchar* gimp_attribute_get_name (GimpAttribute
*attribute);
+const gchar* gimp_attribute_get_sortable_name (GimpAttribute
*attribute);
+GValue gimp_attribute_get_value (GimpAttribute
*attribute);
+gchar* gimp_attribute_get_string (GimpAttribute
*attribute);
+GimpAttributeTagType gimp_attribute_get_tag_type (GimpAttribute
*attribute);
+GimpAttributeValueType gimp_attribute_get_value_type_from_string (const gchar
*string);
+GimpAttributeValueType gimp_attribute_get_value_type (GimpAttribute
*attribute);
+void gimp_attribute_set_value_type (GimpAttribute
*attribute,
+ GimpAttributeValueType
value_type);
+gboolean gimp_attribute_has_structure (GimpAttribute
*attribute);
+GSList* gimp_attribute_get_attribute_structure (GimpAttribute
*attribute);
+void gimp_attribute_set_structure_type (GimpAttribute
*attribute,
+ GimpAttributeStructureType
value);
+GimpAttributeStructureType gimp_attribute_get_structure_type (GimpAttribute
*attribute);
+gdouble gimp_attribute_get_gps_degree (GimpAttribute
*attribute);
+gchar* gimp_attribute_get_xml (GimpAttribute
*attribute);
+void gimp_attribute_print (GimpAttribute
*attribute);
+
+G_END_DECLS
+
+#endif
diff --git a/libgimpbase/gimpbase.def b/libgimpbase/gimpbase.def
index 5897ed8..f3395f9 100644
--- a/libgimpbase/gimpbase.def
+++ b/libgimpbase/gimpbase.def
@@ -1,6 +1,29 @@
EXPORTS
gimp_add_mask_type_get_type
gimp_any_to_utf8
+ gimp_attribute_copy
+ gimp_attribute_get_attribute_type
+ gimp_attribute_get_attribute_ifd
+ gimp_attribute_get_attribute_structure
+ gimp_attribute_get_attribute_tag
+ gimp_attribute_get_interpreted_string
+ gimp_attribute_get_name
+ gimp_attribute_get_sortable_name
+ gimp_attribute_get_string
+ gimp_attribute_get_structure_type
+ gimp_attribute_get_tag_type
+ gimp_attribute_get_value
+ gimp_attribute_get_value_type
+ gimp_attribute_get_value_type_from_string
+ gimp_attribute_get_gps_degree
+ gimp_attribute_get_xml
+ gimp_attribute_has_structure
+ gimp_attribute_is_valid
+ gimp_attribute_new_string
+ gimp_attribute_print
+ gimp_attribute_set_interpreted_string
+ gimp_attribute_set_structure_type
+ gimp_attribute_set_value_type
gimp_base_init
gimp_blend_mode_get_type
gimp_brush_generated_shape_get_type
@@ -70,21 +93,36 @@ EXPORTS
gimp_memsize_to_string
gimp_merge_type_get_type
gimp_message_handler_type_get_type
+ gimp_metadata_add_attribute
gimp_metadata_deserialize
gimp_metadata_duplicate
+ gimp_metadata_get_attribute
gimp_metadata_get_colorspace
gimp_metadata_get_resolution
+ gimp_metadata_get_table
+ gimp_metadata_get_type
+ gimp_metadata_has_attribute
+ gimp_metadata_has_tag_type
gimp_metadata_is_tag_supported
+ gimp_metadata_iter_init
+ gimp_metadata_iter_next
gimp_metadata_load_from_file
gimp_metadata_new
- gimp_metadata_save_to_file
+ gimp_metadata_new_attribute
+ gimp_metadata_new_gexiv2metadata
+ gimp_metadata_print
+ gimp_metadata_remove_attribute
+ gimp_metadata_has_attribute
gimp_metadata_serialize
gimp_metadata_set_bits_per_sample
gimp_metadata_set_colorspace
+ gimp_metadata_save_to_file
gimp_metadata_set_from_exif
gimp_metadata_set_from_xmp
gimp_metadata_set_pixel_size
gimp_metadata_set_resolution
+ gimp_metadata_size
+ gimp_metadata_to_xmp_packet
gimp_micro_version
gimp_minor_version
gimp_offset_type_get_type
@@ -210,3 +248,5 @@ EXPORTS
gp_tile_ack_write
gp_tile_data_write
gp_tile_req_write
+ rational_free
+ srational_free
diff --git a/libgimpbase/gimpbase.h b/libgimpbase/gimpbase.h
index a5682e7..f60bef7 100644
--- a/libgimpbase/gimpbase.h
+++ b/libgimpbase/gimpbase.h
@@ -23,6 +23,7 @@
#include <libgimpbase/gimpbasetypes.h>
+#include <libgimpbase/gimpattribute.h>
#include <libgimpbase/gimpchecks.h>
#include <libgimpbase/gimpcpuaccel.h>
#include <libgimpbase/gimpdatafiles.h>
diff --git a/libgimpbase/gimpbasetypes.h b/libgimpbase/gimpbasetypes.h
index bef9904..d05d80c 100644
--- a/libgimpbase/gimpbasetypes.h
+++ b/libgimpbase/gimpbasetypes.h
@@ -45,6 +45,8 @@ G_BEGIN_DECLS
#endif
+typedef struct _GimpAttribute GimpAttribute;
+typedef struct _GimpMetadata GimpMetadata;
typedef struct _GimpParasite GimpParasite;
typedef struct _GimpDatafileData GimpDatafileData;
typedef struct _GimpEnumDesc GimpEnumDesc;
@@ -55,7 +57,6 @@ typedef struct _GimpValueArray GimpValueArray;
typedef void (* GimpDatafileLoaderFunc) (const GimpDatafileData *file_data,
gpointer user_data);
-typedef struct _GExiv2Metadata GimpMetadata;
/**
diff --git a/libgimpbase/gimpmetadata.c b/libgimpbase/gimpmetadata.c
index 8da75a7..d1f360f 100644
--- a/libgimpbase/gimpmetadata.c
+++ b/libgimpbase/gimpmetadata.c
@@ -22,6 +22,10 @@
#include "config.h"
+#include <stdio.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
#include <stdlib.h>
#include <string.h>
@@ -30,6 +34,8 @@
#include "libgimpmath/gimpmath.h"
+#include "gimprational.h"
+#include "gimpattribute.h"
#include "gimpbasetypes.h"
#include "gimplimits.h"
@@ -38,24 +44,78 @@
#include "libgimp/libgimp-intl.h"
+typedef struct _GimpMetadataClass GimpMetadataClass;
+typedef struct _GimpMetadataPrivate GimpMetadataPrivate;
-/**
- * SECTION: gimpmetadata
- * @title: gimpmetadata
- * @short_description: Basic functions for handling #GimpMetadata objects.
- * @see_also: gimp_image_metadata_load_prepare(),
- * gimp_image_metadata_load_finish(),
- * gimp_image_metadata_load_prepare(),
- * gimp_image_metadata_load_finish().
- *
- * Basic functions for handling #GimpMetadata objects.
- **/
+struct _GimpMetadataPrivate {
+ GHashTable *attribute_table;
+ GHashTable *sorted_to_attribute;
+ GList *sorted_key_list;
+ GSList *xmp_structure_list;
+
+};
+struct _GimpMetadata {
+ GExiv2Metadata parent_instance;
+ GimpMetadataPrivate *priv;
+};
-static GQuark gimp_metadata_error_quark (void);
-static void gimp_metadata_add (GimpMetadata *src,
- GimpMetadata *dest);
+struct _GimpMetadataClass {
+ GExiv2MetadataClass parent_class;
+};
+static gpointer gimpmetadata_parent_class = NULL;
+static GimpAttribute *current_attribute = NULL;
+static gboolean iter_initialized = FALSE;
+
+static void gimp_metadata_finalize (GObject *obj);
+static void gimp_metadata_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_metadata_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static GimpAttribute * gimp_metadata_get_attribute_sorted (GimpMetadata *metadata,
+ const gchar *sorted_name);
+static void gimp_metadata_deserialize_error (GMarkupParseContext *context,
+ GError *error,
+ gpointer user_data);
+static GQuark gimp_metadata_error_quark (void);
+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);
+static void gimp_metadata_deserialize_text (GMarkupParseContext *context,
+ const gchar *text,
+ gsize text_len,
+ gpointer user_data,
+ GError **error);
+static void gimp_metadata_deserialize_end_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ gpointer user_data,
+ GError **error);
+static const gchar* gimp_metadata_name_to_value (const gchar
**attribute_names,
+ const gchar
**attribute_values,
+ const gchar *name);
+static gboolean has_xmp_structure (GSList *xmp_list,
+ const gchar *entry);
+GExiv2Metadata * gimp_metadata_new_gexiv2metadata (void);
+GimpAttribute * gimp_metadata_from_parent (GimpMetadata *metadata,
+ const gchar *name);
+void gimp_metadata_to_parent (GimpMetadata *metadata,
+ GimpAttribute *attribute);
+void gimp_metadata_add_attribute_to_list (GimpMetadata *metadata,
+ GimpAttribute *attribute);
+void gimp_metadata_from_gexiv2 (GimpMetadata *metadata);
+void gimp_metadata_to_gexiv2 (GimpMetadata *metadata);
+
+G_DEFINE_TYPE (GimpMetadata, gimp_metadata, GEXIV2_TYPE_METADATA)
+
+#define parent_class gimp_metadata_parent_class
static const gchar *tiff_tags[] =
{
@@ -92,7 +152,6 @@ static const gchar *unsupported_tags[] =
"Exif.Image.ClipPath",
"Exif.Image.XClipPathUnits",
"Exif.Image.YClipPathUnits",
- "Xmp.xmpMM.History",
"Exif.Image.XPTitle",
"Exif.Image.XPComment",
"Exif.Image.XPAuthor",
@@ -100,8 +159,7 @@ static const gchar *unsupported_tags[] =
"Exif.Image.XPSubject",
"Exif.Image.DNGVersion",
"Exif.Image.DNGBackwardVersion",
- "Exif.Iop"
-};
+ "Exif.Iop"};
static const guint8 minimal_exif[] =
{
@@ -142,53 +200,170 @@ static const guint8 wilber_jpg[] =
static const guint wilber_jpg_len = G_N_ELEMENTS (wilber_jpg);
+typedef struct
+{
+ gchar name[1024];
+ gboolean base64;
+ GimpAttributeValueType type;
+ GimpMetadata *metadata;
+} GimpMetadataParseData;
+
+struct Namespaces{
+ const gchar *namespace_name;
+ const gchar *namespace_URI;
+};
+
+struct Namespaces namespaces_table[] = {
+ {"gimp", "http://www.gimp.org/ns/2.10/" },
+ {"dwc", "http://rs.tdwg.org/dwc/terms/" },
+ {"lr", "http://ns.adobe.com/lr/1.0/" },
+ {"gpano", "http://ns.google.com/photos/1.0/panorama/" },
+ {"panorama", "http://ns.adobe.com/photoshop/1.0/panorama-profile/" }
+
+};
+
+
+static void gimp_metadata_class_init (GimpMetadataClass * klass)
+{
+ gimpmetadata_parent_class = g_type_class_peek_parent (klass);
+ g_type_class_add_private (klass, sizeof(GimpMetadataPrivate));
+
+ G_OBJECT_CLASS (klass)->get_property = gimp_metadata_get_property;
+ G_OBJECT_CLASS (klass)->set_property = gimp_metadata_set_property;
+ G_OBJECT_CLASS (klass)->finalize = gimp_metadata_finalize;
+}
+
+static void gimp_metadata_init (GimpMetadata * self)
+{
+ self->priv = GIMP_METADATA_GET_PRIVATE (self);
+ self->priv->attribute_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+ self->priv->sorted_to_attribute = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+ self->priv->sorted_key_list = NULL;
+ self->priv->xmp_structure_list = NULL;
+}
+
+/**
+ * gimp_metadata_finalize:
+ *
+ * instance finalizer
+ */
+static void
+gimp_metadata_finalize (GObject* obj)
+{
+ GimpMetadata * metadata;
+
+ metadata = G_TYPE_CHECK_INSTANCE_CAST (obj, TYPE_GIMP_METADATA, GimpMetadata);
+
+ g_hash_table_unref (metadata->priv->attribute_table);
+ g_hash_table_unref (metadata->priv->sorted_to_attribute);
+ g_list_free_full (metadata->priv->sorted_key_list, (GDestroyNotify) g_free);
+ g_slist_free (metadata->priv->xmp_structure_list);
+
+ G_OBJECT_CLASS (gimpmetadata_parent_class)->finalize (obj);
+}
+
+/**
+ * gimp_metadata_get_property
+ *
+ * instance get property
+ */
+static void
+gimp_metadata_get_property (GObject * object,
+ guint property_id,
+ GValue * value,
+ GParamSpec * pspec)
+{
+}
+
+/**
+ * gimp_metadata_set_property
+ *
+ * instance set property
+ */
+static void
+gimp_metadata_set_property (GObject * object,
+ guint property_id,
+ const GValue * value,
+ GParamSpec * pspec)
+{
+}
/**
* gimp_metadata_new:
*
- * Creates a new #GimpMetadata instance.
+ * returns a new #GimpMetadata object
*
- * Return value: The new #GimpMetadata.
+ * Return value: a new @GimpMetadata object
*
- * Since: 2.10
+ * Since : 2.10
*/
GimpMetadata *
gimp_metadata_new (void)
{
- GExiv2Metadata *metadata = NULL;
+ GimpMetadata *new_metadata = NULL;
+ gint i;
if (gexiv2_initialize ())
{
- metadata = gexiv2_metadata_new ();
+ new_metadata = g_object_new (TYPE_GIMP_METADATA, NULL);
- if (! gexiv2_metadata_open_buf (metadata, wilber_jpg, wilber_jpg_len,
- NULL))
+ if (gexiv2_initialize ())
{
- g_object_unref (metadata);
+ if (gexiv2_metadata_open_buf (GEXIV2_METADATA (new_metadata), wilber_jpg, wilber_jpg_len,
+ NULL))
+ {
+ for (i = 0; i < G_N_ELEMENTS (namespaces_table); i++)
+ {
+ struct Namespaces n_space = namespaces_table[i];
+ gexiv2_metadata_register_xmp_namespace (n_space.namespace_URI, n_space.namespace_name);
+ }
+ }
- return NULL;
}
+
+ return new_metadata;
+
}
+ else
+ {
+ return NULL;
+ }
+}
- return metadata;
+/**
+ * gimp_metadata_size: ToDo handle size
+ *
+ * @metadata: a #GimpMetadata
+ *
+ * Return value: a #gint: amount of #GimpAttribute objects in
+ * the #GimpMetadata container
+ *
+ * Since : 2.10
+ */
+gint
+gimp_metadata_size (GimpMetadata *metadata)
+{
+ GimpMetadataPrivate *private = GIMP_METADATA_GET_PRIVATE (metadata);
+
+ return g_hash_table_size (private->attribute_table);
}
/**
* gimp_metadata_duplicate:
- * @metadata: The object to duplicate, or %NULL.
*
- * Duplicates a #GimpMetadata instance.
+ * @metadata: a #GimpMetadata
*
- * Return value: The new #GimpMetadata, or %NULL if @metadata is %NULL.
+ * Duplicates the #GimpMetadata object with all the #GimpAttribute objects
+ * Return value: a copy of the @metadata object
*
- * Since: 2.10
+ * Since : 2.10
*/
GimpMetadata *
gimp_metadata_duplicate (GimpMetadata *metadata)
{
- GimpMetadata *new_metadata = NULL;
+ GimpMetadata * new_metadata = NULL;
- g_return_val_if_fail (metadata == NULL || GEXIV2_IS_METADATA (metadata), NULL);
+ g_return_val_if_fail (IS_GIMP_METADATA (metadata), NULL);
if (metadata)
{
@@ -196,361 +371,2298 @@ gimp_metadata_duplicate (GimpMetadata *metadata)
xml = gimp_metadata_serialize (metadata);
new_metadata = gimp_metadata_deserialize (xml);
- g_free (xml);
}
return new_metadata;
}
-typedef struct
+/**
+ * gimp_metadata_get_table:
+ *
+ * @metadata: a #GimpMetadata
+ *
+ * Return value: the #GHashTable
+ *
+ * Since : 2.10
+ */
+GHashTable *
+gimp_metadata_get_table (GimpMetadata *metadata)
{
- gchar name[1024];
- gboolean base64;
- GimpMetadata *metadata;
-} GimpMetadataParseData;
+ return metadata->priv->attribute_table;
+}
-static const gchar*
-gimp_metadata_attribute_name_to_value (const gchar **attribute_names,
- const gchar **attribute_values,
- const gchar *name)
+/**
+ * gimp_metadata_add_attribute:
+ *
+ * @metadata : a #GimpMetadata
+ * @attribute : a #GimpAttribute
+ *
+ * stores the @attribute in the @metadata container
+ *
+ * Since : 2.10
+ */
+void
+gimp_metadata_add_attribute (GimpMetadata *metadata,
+ GimpAttribute *attribute)
{
- while (*attribute_names)
+ const gchar *name;
+ gchar *lowchar;
+
+ g_return_if_fail (IS_GIMP_METADATA (metadata));
+ g_return_if_fail (GIMP_IS_ATTRIBUTE (attribute));
+
+ name = gimp_attribute_get_name (attribute);
+
+ if (name)
{
- if (! strcmp (*attribute_names, name))
+ lowchar = g_ascii_strdown (name, -1);
+
+ /* FIXME: really simply add? That means, that an older value is overwritten */
+
+ if (g_hash_table_insert (metadata->priv->attribute_table, (gpointer) g_strdup (lowchar), (gpointer)
attribute))
{
- return *attribute_values;
- }
+ gchar *sortable_tag;
- attribute_names++;
- attribute_values++;
+ sortable_tag = g_ascii_strdown (gimp_attribute_get_sortable_name (attribute), -1);
+
+ if (g_hash_table_insert (metadata->priv->sorted_to_attribute, (gpointer) g_strdup (sortable_tag),
(gpointer) g_strdup (lowchar)))
+ {
+ metadata->priv->sorted_key_list = g_list_insert_sorted (metadata->priv->sorted_key_list,
+ (gpointer) g_strdup (sortable_tag),
+ (GCompareFunc) g_strcmp0);
+ gimp_metadata_to_parent (metadata, attribute);
+ }
+ else
+ {
+ g_hash_table_remove (metadata->priv->attribute_table, (gpointer) g_strdup (lowchar));
+ }
+
+ g_free (sortable_tag);
+ }
+ g_free (lowchar);
}
+}
- return NULL;
+/**
+ * gimp_metadata_get_attribute:
+ *
+ * @metadata : a #GimpMetadata
+ * @name : a #gchar array
+ *
+ * gets the #GimpAttribute object with @name
+ *
+ * Return value: the #GimpAttribute object if found, NULL otherwise.
+ *
+ * Since : 2.10
+ */
+GimpAttribute *
+gimp_metadata_get_attribute (GimpMetadata *metadata,
+ const gchar *name)
+{
+ GimpAttribute *attribute_data = NULL;
+
+ g_return_val_if_fail (IS_GIMP_METADATA (metadata), NULL);
+
+ attribute_data = gimp_metadata_from_parent (metadata, name);
+
+ return attribute_data;
}
-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)
+/**
+ * gimp_metadata_get_attribute_sorted:
+ *
+ * @metadata: a #GimpMetadata
+ * @name : a #gchar array
+ *
+ * gets the #GimpAttribute object with @sorted_name as
+ * sortable_name
+ * see GimpAttribute::get_sortable_name
+ *
+ * Return value: the #GimpAttribute object if found, NULL otherwise.
+ *
+ * Since : 2.10
+ */
+static GimpAttribute *
+gimp_metadata_get_attribute_sorted (GimpMetadata *metadata,
+ const gchar *sorted_name)
{
- GimpMetadataParseData *parse_data = user_data;
+ gchar *lowchar;
+ GimpAttribute *attribute_data = NULL;
+ gpointer *data;
+ gchar *name_of_tag;
- if (! strcmp (element_name, "tag"))
- {
- const gchar *name;
- const gchar *encoding;
+ g_return_val_if_fail (IS_GIMP_METADATA (metadata), NULL);
+ g_return_val_if_fail (sorted_name != NULL, NULL);
- name = gimp_metadata_attribute_name_to_value (attribute_names,
- attribute_values,
- "name");
- encoding = gimp_metadata_attribute_name_to_value (attribute_names,
- attribute_values,
- "encoding");
+ lowchar = g_ascii_strdown (sorted_name, -1);
- if (! name)
- {
- g_set_error (error, gimp_metadata_error_quark (), 1001,
- "Element 'tag' does not contain required attribute 'name'.");
- return;
- }
+ data = g_hash_table_lookup (metadata->priv->sorted_to_attribute, (gpointer) lowchar);
- strncpy (parse_data->name, name, sizeof (parse_data->name));
- parse_data->name[sizeof (parse_data->name) - 1] = 0;
+ if (data)
+ {
+ name_of_tag = (gchar *) data;
- parse_data->base64 = (encoding && ! strcmp (encoding, "base64"));
+ data = g_hash_table_lookup (metadata->priv->attribute_table, (gpointer) name_of_tag);
+ if (data)
+ {
+ attribute_data = (GimpAttribute *) data;
+ }
}
-}
-static void
-gimp_metadata_deserialize_end_element (GMarkupParseContext *context,
- const gchar *element_name,
- gpointer user_data,
- GError **error)
-{
+ g_free (lowchar);
+
+ return attribute_data;
}
-static void
-gimp_metadata_deserialize_text (GMarkupParseContext *context,
- const gchar *text,
- gsize text_len,
- gpointer user_data,
- GError **error)
+/**
+ * gimp_metadata_remove_attribute: ToDo remove from gexiv2 ToDo rewrite
+ *
+ * @metadata : a #GimpMetadata
+ * @name : a #gchar array
+ *
+ * removes the #GimpAttribute object with @name from the @metadata container
+ *
+ * Return value: TRUE, if removing was successful, FALSE otherwise.
+ *
+ * Since : 2.10
+ */
+gboolean
+gimp_metadata_remove_attribute (GimpMetadata *metadata,
+ const gchar *name)
{
- GimpMetadataParseData *parse_data = user_data;
- const gchar *current_element;
+ gchar *lowchar;
+ gboolean success = FALSE;
+ GHashTableIter iter_remove;
+ gpointer key, value;
+ gchar *tag_to_remove = NULL;
+ gchar *name_of_tag = NULL;
- current_element = g_markup_parse_context_get_element (context);
+ lowchar = g_ascii_strdown (name, -1);
- if (! g_strcmp0 (current_element, "tag"))
+ if (g_hash_table_remove (metadata->priv->attribute_table, (gpointer) lowchar))
{
- gchar *value = g_strndup (text, text_len);
+ gchar *tag_list_remove = NULL;
- if (parse_data->base64)
+ g_hash_table_iter_init (&iter_remove, metadata->priv->sorted_to_attribute);
+ while (g_hash_table_iter_next (&iter_remove, &key, &value))
{
- guchar *decoded;
- gsize len;
+ tag_to_remove = (gchar *) key;
+ name_of_tag = (gchar *) value;
- decoded = g_base64_decode (value, &len);
+ if (! g_strcmp0 (lowchar, name_of_tag))
+ break;
+ }
- if (decoded[len - 1] == '\0')
- gexiv2_metadata_set_tag_string (parse_data->metadata,
- parse_data->name,
- (const gchar *) decoded);
+ tag_list_remove = g_strdup (tag_to_remove); /* because removing from hashtable frees tag_to_remove */
- g_free (decoded);
+ if (g_hash_table_remove (metadata->priv->sorted_to_attribute, (gpointer) tag_to_remove))
+ {
+ GList *list = NULL;
+
+ for (list = metadata->priv->sorted_key_list; list; list = list->next)
+ {
+ gchar *s_tag = (gchar *) list->data;
+
+ if (! g_strcmp0 (s_tag, tag_list_remove))
+ {
+ metadata->priv->sorted_key_list = g_list_remove (metadata->priv->sorted_key_list,
+ (gconstpointer) s_tag);
+ g_free (s_tag);
+ break;
+ }
+ }
+ g_free (tag_list_remove);
+ success = TRUE;
}
else
{
- gexiv2_metadata_set_tag_string (parse_data->metadata,
- parse_data->name,
- value);
+ if (name_of_tag)
+ g_free (name_of_tag);
+ if (tag_to_remove)
+ g_free (tag_to_remove);
+ success = FALSE;
}
+ }
+ g_free (lowchar);
- g_free (value);
+ gexiv2_metadata_clear_tag (GEXIV2_METADATA (metadata), name);
+
+ return success;
+}
+
+/**
+ * gimp_metadata_has_attribute:
+ *
+ * @metadata : a #GimpMetadata
+ * @name : a #gchar array
+ *
+ * tests, if a #GimpAttribute object with @name is in the @metadata container
+ *
+ * Return value: TRUE if yes, FALSE otherwise.
+ *
+ * Since : 2.10
+ */
+gboolean
+gimp_metadata_has_attribute (GimpMetadata *metadata,
+ const gchar *name)
+{
+ gchar *lowchar;
+ gboolean success;
+
+ lowchar = g_ascii_strdown (name, -1);
+
+ success = gexiv2_metadata_has_tag (GEXIV2_METADATA(metadata), name);
+
+ g_free (lowchar);
+
+ return success;
+}
+
+/**
+ * gimp_metadata_new_attribute:
+ *
+ * @metadata: a #GimpMetadata
+ * @name : a #gchar array
+ * @value : a #gchar array
+ * @type : a #GimpAttributeValueType
+ *
+ * adds a #GimpAttribute object to @metadata container.
+ * The #GimpAttribute object is created from the
+ * @name,
+ * @value and
+ * @type parameters.
+ *
+ * Return value: TRUE if successful, FALSE otherwise.
+ *
+ * Since : 2.10
+ */
+gboolean
+gimp_metadata_new_attribute (GimpMetadata *metadata,
+ const gchar *name,
+ gchar *value,
+ GimpAttributeValueType type)
+{
+ GimpAttribute *attribute;
+
+ attribute = gimp_attribute_new_string (name, value, type);
+ if (attribute)
+ {
+ gimp_metadata_add_attribute (metadata, attribute);
+ return TRUE;
}
+
+ return FALSE;
}
-static void
-gimp_metadata_deserialize_error (GMarkupParseContext *context,
- GError *error,
- gpointer user_data)
+/**
+ * gimp_metadata_serialize: ToDo handle old metadata and get data from sorted list -> attributes.c
+ *
+ * @metadata: a #GimpMetadata
+ *
+ * creates a xml representation of all #GimpAttribute objects in the #GimpMetadata container.
+ * see #GimpAttribute:gimp_attribute_get_xml
+ *
+ * Return value: a new #gchar array, the xml representation of the #GimpMetadata object.
+ *
+ * Since : 2.10
+ */
+gchar *
+gimp_metadata_serialize (GimpMetadata *metadata)
{
- g_printerr ("Metadata parse error: %s\n", error->message);
+ GString *string;
+ GList *key_list;
+
+ g_return_val_if_fail (IS_GIMP_METADATA (metadata), NULL);
+
+ gimp_metadata_from_gexiv2 (metadata);
+
+ string = g_string_new (NULL);
+
+ g_string_append (string, "<?xml version='1.0' encoding='UTF-8'?>\n");
+ g_string_append (string, "<metadata>\n");
+
+ for (key_list = metadata->priv->sorted_key_list; key_list; key_list = key_list->next)
+ {
+ gchar *xml = NULL;
+ GimpAttribute *attribute = NULL;
+ gchar *p_key = (gchar *) key_list->data;
+
+ attribute = gimp_metadata_get_attribute_sorted (metadata, p_key);
+
+ xml = gimp_attribute_get_xml (attribute);
+
+ g_string_append_printf (string, "%s\n", xml);
+
+ g_free (xml);
+ }
+
+ g_string_append (string, "</metadata>\n");
+
+ return g_string_free (string, FALSE);
}
/**
* gimp_metadata_deserialize:
- * @metadata_xml: A string of serialized metadata XML.
*
- * Deserializes a string of XML that has been created by
- * gimp_metadata_serialize().
+ * @xml: a #gchar array
*
- * Return value: The new #GimpMetadata.
+ * parses a xml representation of a #GimpMetadata container.
+ * see
+ * #GimpMetadata:gimp_metadata_deserialize_start_element
+ * #GimpMetadata:gimp_metadata_deserialize_end_element
+ * #GimpMetadata:gimp_metadata_deserialize_text
+ * #GimpMetadata:gimp_metadata_deserialize_error
*
- * Since: 2.10
+ * Return value: a new #GimpMetadata object.
+ *
+ * Since : 2.10
*/
GimpMetadata *
-gimp_metadata_deserialize (const gchar *metadata_xml)
+gimp_metadata_deserialize (const gchar *xml)
{
- GimpMetadata *metadata;
- GMarkupParser markup_parser;
- GimpMetadataParseData parse_data;
- GMarkupParseContext *context;
+ GMarkupParser *markup_parser = g_slice_new (GMarkupParser);
+ GimpMetadataParseData *parse_data = g_slice_new (GimpMetadataParseData);
+ GMarkupParseContext *context;
+ GimpMetadata *metadata;
- g_return_val_if_fail (metadata_xml != NULL, NULL);
+ g_return_val_if_fail (xml != NULL, NULL);
metadata = gimp_metadata_new ();
- parse_data.metadata = metadata;
+ 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;
+ 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);
+ context = g_markup_parse_context_new (markup_parser, 0, parse_data, NULL);
g_markup_parse_context_parse (context,
- metadata_xml, strlen (metadata_xml),
+ xml, strlen (xml),
NULL);
g_markup_parse_context_unref (context);
- return metadata;
+ g_slice_free (GMarkupParser, markup_parser);
+ g_slice_free (GimpMetadataParseData, parse_data);
+
+ return metadata;
}
-static gchar *
-gimp_metadata_escape (const gchar *name,
- const gchar *value,
- gboolean *base64)
+/**
+ * gimp_metadata_from_gexiv2metadata:
+ * @metadata: The metadata the gexiv2metadata will be added to, may be %NULL
+ * @gexiv2metadata: The metadata in gexiv2 format
+ *
+ * Converts the @gexiv2metadata retrieved from a file into
+ * a #GimpMetadata object
+ *
+ * Return value: The #GimpMetadata object
+ *
+ * Since: GIMP 2.10
+ */
+//GimpMetadata *
+//gimp_metadata_from_gexiv2metadata (GimpMetadata *metadata,
+// GimpMetadata *gexivdata)
+//{
+// const gchar *tag_type;
+// GimpAttribute *attribute;
+// GimpAttributeValueType attrib_type;
+// gint i;
+// GimpMetadata *new_metadata = NULL;
+// GExiv2Metadata *gexiv2metadata = NULL;
+//
+// gexiv2metadata = GEXIV2_METADATA(gexivdata);
+//
+// if (!metadata)
+// {
+// new_metadata = gimp_metadata_new ();
+// }
+// else
+// {
+// new_metadata = gimp_metadata_duplicate (metadata);
+// g_object_unref (metadata);
+// }
+//
+// if (new_metadata)
+// {
+// gchar **exif_data;
+// gchar **xmp_data;
+// gchar **iptc_data;
+// gboolean no_interpreted = TRUE; /*FIXME: No interpreted String possible */
+//
+// exif_data = gexiv2_metadata_get_exif_tags (gexiv2metadata);
+//
+// for (i = 0; exif_data[i] != NULL; i++)
+// {
+// gchar *interpreted_value = NULL;
+// gchar *value = NULL;
+// gboolean interpreted = FALSE;
+//
+// value = gexiv2_metadata_get_tag_string (gexiv2metadata, exif_data[i]);
+// interpreted_value = gexiv2_metadata_get_tag_interpreted_string (gexiv2metadata, exif_data[i]);
+// tag_type = gexiv2_metadata_get_tag_type (exif_data[i]);
+// attrib_type = gimp_attribute_get_value_type_from_string (tag_type);
+//
+// interpreted = g_strcmp0 (value, interpreted_value);
+//
+// if (!interpreted)
+// {
+// gint length;
+//
+// length = strlen (interpreted_value);
+// if (length > 2048)
+// {
+// g_free (interpreted_value);
+// interpreted_value = g_strdup_printf ("(Size of value: %d)", length);
+// interpreted = TRUE;
+// }
+// }
+//
+// attribute = gimp_attribute_new_string (exif_data[i], value, attrib_type);
+// if (gimp_attribute_is_valid (attribute))
+// {
+// if (no_interpreted)
+// {
+// if (interpreted)
+// {
+// gimp_attribute_set_interpreted_string (attribute, interpreted_value);
+// }
+// }
+// gimp_metadata_add_attribute (new_metadata, attribute);
+// }
+// else
+// {
+// g_object_unref (attribute);
+// }
+//
+// g_free (interpreted_value);
+// g_free (value);
+// }
+//
+// g_strfreev (exif_data);
+//
+// xmp_data = gexiv2_metadata_get_xmp_tags (gexiv2metadata);
+//
+// for (i = 0; xmp_data[i] != NULL; i++)
+// {
+// gchar *interpreted_value = NULL;
+// gchar *value = NULL;
+// gboolean interpreted = FALSE;
+//
+// value = gexiv2_metadata_get_tag_string (gexiv2metadata, xmp_data[i]);
+// interpreted_value = gexiv2_metadata_get_tag_interpreted_string (gexiv2metadata, xmp_data[i]);
+// tag_type = gexiv2_metadata_get_tag_type (xmp_data[i]);
+// attrib_type = gimp_attribute_get_value_type_from_string (tag_type);
+//
+// interpreted = g_strcmp0 (value, interpreted_value);
+//
+// attribute = gimp_attribute_new_string (xmp_data[i], value, attrib_type);
+//
+// if (gimp_attribute_is_valid (attribute))
+// {
+// if (no_interpreted)
+// {
+// if (interpreted)
+// gimp_attribute_set_interpreted_string (attribute, interpreted_value);
+// }
+// gimp_metadata_add_attribute (new_metadata, attribute);
+// }
+// else
+// {
+// g_object_unref (attribute);
+// }
+//
+// g_free (value);
+// }
+//
+// g_strfreev (xmp_data);
+//
+// iptc_data = gexiv2_metadata_get_iptc_tags (gexiv2metadata);
+//
+// for (i = 0; iptc_data[i] != NULL; i++)
+// {
+// gchar *interpreted_value = NULL;
+// gchar *value = NULL;
+// gboolean interpreted = FALSE;
+//
+// value = gexiv2_metadata_get_tag_string (gexiv2metadata, iptc_data[i]);
+// interpreted_value = gexiv2_metadata_get_tag_interpreted_string (gexiv2metadata, iptc_data[i]);
+// tag_type = gexiv2_metadata_get_tag_type (iptc_data[i]);
+// attrib_type = gimp_attribute_get_value_type_from_string (tag_type);
+//
+// interpreted = g_strcmp0 (value, interpreted_value);
+//
+// attribute = gimp_attribute_new_string (iptc_data[i], value, attrib_type);
+// if (gimp_attribute_is_valid (attribute))
+// {
+// if (no_interpreted)
+// {
+// if (interpreted)
+// gimp_attribute_set_interpreted_string (attribute, interpreted_value);
+// }
+// gimp_metadata_add_attribute (new_metadata, attribute);
+// }
+// else
+// {
+// g_object_unref (attribute);
+// }
+//
+// g_free (value);
+// }
+//
+// g_strfreev (iptc_data);
+// }
+// return new_metadata;
+//}
+
+/**
+ * gimp_metadata_from_gexiv2:
+ * @metadata: The metadata
+ *
+ * Constructs the @metadata retrieved from the gexiv2 package.
+ *
+ *
+ * Since: GIMP 2.10
+ */
+void
+gimp_metadata_from_gexiv2 (GimpMetadata *metadata)
{
- if (! g_utf8_validate (value, -1, NULL))
+ const gchar *tag_type;
+ GimpAttribute *attribute;
+ GimpAttributeValueType attrib_type;
+ gint i;
+ GExiv2Metadata *gexiv2metadata = NULL;
+ GimpMetadataPrivate *priv;
+
+ g_return_if_fail (IS_GIMP_METADATA (metadata));
+
+ priv = GIMP_METADATA_GET_PRIVATE (metadata);
+
+ if (priv->attribute_table)
+ g_hash_table_unref (priv->attribute_table);
+ if (priv->sorted_to_attribute)
+ g_hash_table_unref (priv->sorted_to_attribute);
+ if (priv->sorted_key_list)
+ g_list_free_full (priv->sorted_key_list, (GDestroyNotify) g_free);
+ if (priv->xmp_structure_list)
+ g_slist_free (priv->xmp_structure_list);
+
+
+ priv->attribute_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+ priv->sorted_to_attribute = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+ priv->sorted_key_list = NULL;
+ priv->xmp_structure_list = NULL;
+
+ gexiv2metadata = GEXIV2_METADATA(metadata);
+
{
- gchar *encoded;
+ gchar **exif_data;
+ gchar **xmp_data;
+ gchar **iptc_data;
+ gboolean no_interpreted = TRUE; /*FIXME: No interpreted String possible */
+
+ exif_data = gexiv2_metadata_get_exif_tags (gexiv2metadata);
+
+ for (i = 0; exif_data[i] != NULL; i++)
+ {
+ gchar *interpreted_value = NULL;
+ gchar *value = NULL;
+ gboolean interpreted = FALSE;
- encoded = g_base64_encode ((const guchar *) value, strlen (value) + 1);
+ value = gexiv2_metadata_get_tag_string (gexiv2metadata, exif_data[i]);
+ interpreted_value = gexiv2_metadata_get_tag_interpreted_string (gexiv2metadata, exif_data[i]);
+ tag_type = gexiv2_metadata_get_tag_type (exif_data[i]);
+ attrib_type = gimp_attribute_get_value_type_from_string (tag_type);
+
+ interpreted = g_strcmp0 (value, interpreted_value);
+
+ if (!interpreted)
+ {
+ gint length;
+
+ length = strlen (interpreted_value);
+ if (length > 2048)
+ {
+ g_free (interpreted_value);
+ interpreted_value = g_strdup_printf ("(Size of value: %d)", length);
+ interpreted = TRUE;
+ }
+ }
+
+ attribute = gimp_attribute_new_string (exif_data[i], value, attrib_type);
+ if (gimp_attribute_is_valid (attribute))
+ {
+ if (no_interpreted)
+ {
+ if (interpreted)
+ {
+ gimp_attribute_set_interpreted_string (attribute, interpreted_value);
+ }
+ }
+ gimp_metadata_add_attribute_to_list (metadata, attribute);
+ }
+ else
+ {
+ g_object_unref (attribute);
+ }
+
+ g_free (interpreted_value);
+ g_free (value);
+ }
+
+ g_strfreev (exif_data);
+
+ xmp_data = gexiv2_metadata_get_xmp_tags (gexiv2metadata);
+
+ for (i = 0; xmp_data[i] != NULL; i++)
+ {
+ gchar *interpreted_value = NULL;
+ gchar *value = NULL;
+ gboolean interpreted = FALSE;
- g_printerr ("Invalid UTF-8 in metadata value %s, encoding as base64: %s\n",
- name, encoded);
+ value = gexiv2_metadata_get_tag_string (gexiv2metadata, xmp_data[i]);
+ interpreted_value = gexiv2_metadata_get_tag_interpreted_string (gexiv2metadata, xmp_data[i]);
+ tag_type = gexiv2_metadata_get_tag_type (xmp_data[i]);
+ attrib_type = gimp_attribute_get_value_type_from_string (tag_type);
- *base64 = TRUE;
+ interpreted = g_strcmp0 (value, interpreted_value);
- return encoded;
+ attribute = gimp_attribute_new_string (xmp_data[i], value, attrib_type);
+
+ if (gimp_attribute_is_valid (attribute))
+ {
+ if (no_interpreted)
+ {
+ if (interpreted)
+ gimp_attribute_set_interpreted_string (attribute, interpreted_value);
+ }
+ gimp_metadata_add_attribute_to_list (metadata, attribute);
+ }
+ else
+ {
+ g_object_unref (attribute);
+ }
+
+ g_free (value);
+ }
+
+ g_strfreev (xmp_data);
+
+ iptc_data = gexiv2_metadata_get_iptc_tags (gexiv2metadata);
+
+ for (i = 0; iptc_data[i] != NULL; i++)
+ {
+ gchar *interpreted_value = NULL;
+ gchar *value = NULL;
+ gboolean interpreted = FALSE;
+
+ value = gexiv2_metadata_get_tag_string (gexiv2metadata, iptc_data[i]);
+ interpreted_value = gexiv2_metadata_get_tag_interpreted_string (gexiv2metadata, iptc_data[i]);
+ tag_type = gexiv2_metadata_get_tag_type (iptc_data[i]);
+ attrib_type = gimp_attribute_get_value_type_from_string (tag_type);
+
+ interpreted = g_strcmp0 (value, interpreted_value);
+
+ attribute = gimp_attribute_new_string (iptc_data[i], value, attrib_type);
+ if (gimp_attribute_is_valid (attribute))
+ {
+ if (no_interpreted)
+ {
+ if (interpreted)
+ gimp_attribute_set_interpreted_string (attribute, interpreted_value);
+ }
+ gimp_metadata_add_attribute (metadata, attribute);
+ }
+ else
+ {
+ g_object_unref (attribute);
+ }
+
+ g_free (value);
+ }
+
+ g_strfreev (iptc_data);
}
+}
+
+/**
+ * gimp_metadata_add_list:
+ *
+ * @metadata : a #GimpMetadata
+ * @attribute : a #GimpAttribute
+ *
+ * stores the @attribute in the @metadata container
+ *
+ * Since : 2.10
+ */
+void
+gimp_metadata_add_attribute_to_list (GimpMetadata *metadata,
+ GimpAttribute *attribute)
+{
+ const gchar *name;
+ gchar *lowchar;
+ GimpMetadataPrivate *priv;
+
+ g_return_if_fail (IS_GIMP_METADATA (metadata));
+ g_return_if_fail (GIMP_IS_ATTRIBUTE (attribute));
+
+ priv = GIMP_METADATA_GET_PRIVATE (metadata);
- *base64 = FALSE;
+ name = gimp_attribute_get_name (attribute);
- return g_markup_escape_text (value, -1);
+ if (name)
+ {
+ lowchar = g_ascii_strdown (name, -1);
+
+ /* FIXME: really simply add? That means, that an older value is overwritten */
+
+ if (g_hash_table_insert (priv->attribute_table, (gpointer) g_strdup (lowchar), (gpointer) attribute))
+ {
+ gchar *sortable_tag;
+
+ sortable_tag = g_ascii_strdown (gimp_attribute_get_sortable_name (attribute), -1);
+
+ if (g_hash_table_insert (priv->sorted_to_attribute, (gpointer) g_strdup (sortable_tag), (gpointer)
g_strdup (lowchar)))
+ {
+ priv->sorted_key_list = g_list_insert_sorted (priv->sorted_key_list,
+ (gpointer) g_strdup (sortable_tag),
+ (GCompareFunc) g_strcmp0);
+ }
+ else
+ {
+ g_hash_table_remove (priv->attribute_table, (gpointer) g_strdup (lowchar));
+ }
+
+ g_free (sortable_tag);
+ }
+ g_free (lowchar);
+ }
}
-static void
-gimp_metadata_append_tag (GString *string,
- const gchar *name,
- gchar *value,
- gboolean base64)
+/**
+ * gimp_metadata_from_parent:
+ * @metadata: The metadata the gexiv2metadata will be added to, may be %NULL
+ * @gexiv2metadata: The metadata in gexiv2 format
+ *
+ * Converts the @gexiv2metadata retrieved from a file into
+ * a #GimpMetadata object
+ *
+ * Return value: The #GimpMetadata object
+ *
+ * Since: GIMP 2.10
+ */
+GimpAttribute *
+gimp_metadata_from_parent (GimpMetadata *metadata,
+ const gchar *name)
{
- if (value)
+ const gchar *tag_type;
+ gboolean no_interpreted = TRUE; /*FIXME: No interpreted String possible */
+ gchar *interpreted_value = NULL;
+ gchar *value = NULL;
+ gboolean interpreted = FALSE;
+ GimpAttribute *attribute = NULL;
+ GimpAttributeValueType attrib_type;
+ GExiv2Metadata *gexiv2metadata;
+
+ g_return_val_if_fail (IS_GIMP_METADATA (metadata), NULL);
+
+ gexiv2metadata = GEXIV2_METADATA (metadata);
+
+ if (gexiv2_metadata_has_tag (gexiv2metadata, name))
{
- if (base64)
+ value = gexiv2_metadata_get_tag_string (gexiv2metadata, name);
+ interpreted_value = gexiv2_metadata_get_tag_interpreted_string (gexiv2metadata, name);
+ tag_type = gexiv2_metadata_get_tag_type (name);
+ attrib_type = gimp_attribute_get_value_type_from_string (tag_type);
+
+ interpreted = g_strcmp0 (value, interpreted_value);
+
+ if (!interpreted)
+ {
+ gint length;
+
+ length = strlen (interpreted_value);
+ if (length > 2048)
+ {
+ g_free (interpreted_value);
+ interpreted_value = g_strdup_printf ("(Size of value: %d)", length);
+ interpreted = TRUE;
+ }
+ }
+
+ attribute = gimp_attribute_new_string (name, value, attrib_type);
+ if (gimp_attribute_is_valid (attribute))
{
- g_string_append_printf (string, " <tag name=\"%s\" encoding=\"base64\">%s</tag>\n",
- name, value);
+ if (no_interpreted)
+ {
+ if (interpreted)
+ {
+ gimp_attribute_set_interpreted_string (attribute, interpreted_value);
+ }
+ }
}
else
{
- g_string_append_printf (string, " <tag name=\"%s\">%s</tag>\n",
- name, value);
+ g_object_unref (attribute);
}
+ g_free (interpreted_value);
g_free (value);
}
+ return attribute;
}
/**
- * gimp_metadata_serialize:
- * @metadata: A #GimpMetadata instance.
+ * gimp_metadata_print:
+ * @metadata: The #GimpMetadata
+ *
+ * prints out information of metadata
+ *
+ * Since: GIMP 2.10
+ */
+void
+gimp_metadata_print (GimpMetadata *metadata)
+{
+ gint i;
+ GList *key_list = NULL;
+
+ g_return_if_fail (IS_GIMP_METADATA (metadata));
+
+ gimp_metadata_from_gexiv2 (metadata);
+
+ i = 0;
+
+ for (key_list = metadata->priv->sorted_key_list; key_list; key_list = key_list->next)
+ {
+ const gchar *tag;
+ const gchar *interpreted;
+ gchar *value;
+ GimpAttribute *attribute;
+ gchar *p_key = (gchar *) key_list->data;
+
+ attribute = gimp_metadata_get_attribute_sorted (metadata, p_key);
+
+ if (! attribute)
+ continue;
+
+ i++;
+
+ tag = gimp_attribute_get_name (attribute);
+ value = gimp_attribute_get_string (attribute);
+ interpreted = gimp_attribute_get_interpreted_string (attribute);
+
+ g_print ("%p: %s\n%04d. Tag: %s\n\tValue:%s\n\tInterpreted value:%s\n", attribute, p_key, i, tag,
value, interpreted);
+
+ if (value)
+ g_free (value);
+ }
+}
+
+/**
+ * gimp_metadata_to_gexiv2metadata:
+ * @metadata: The #GimpMetadata
+ * @gexiv2metadata: The #GimpMetadata
+ * @mime_type: the mime type of the image
*
- * Serializes @metadata into an XML string that can later be deserialized
- * using gimp_metadata_deserialize().
+ * Converts @metadata to @gexiv2metadata in gexiv2 format
*
- * Return value: The serialized XML string.
+ * Since: GIMP 2.10
+ */
+//void
+//gimp_metadata_to_gexiv2metadata (GimpMetadata *metadata,
+// GimpMetadata *gexivdata,
+// const gchar *mime_type)
+//{
+// gchar *o_packet = NULL;
+// gboolean write_tag = FALSE;
+// gboolean namespace = FALSE;
+// gboolean check_mime = TRUE;
+// gboolean support_exif;
+// gboolean support_xmp;
+// gboolean support_iptc;
+// GSList *xmp_structure_list = NULL;
+// GList *key_list = NULL;
+// gint i;
+// GExiv2Metadata *gexiv2metadata;
+//
+// g_return_if_fail (IS_GIMP_METADATA (metadata));
+//
+// gexiv2metadata = GEXIV2_METADATA(gexivdata);
+//
+// for (i = 0; i < G_N_ELEMENTS (namespaces_table); i++)
+// {
+// struct Namespaces n_space = namespaces_table[i];
+// gexiv2_metadata_register_xmp_namespace (n_space.namespace_URI, n_space.namespace_name);
+// }
+//
+// support_exif = gexiv2_metadata_get_supports_exif (gexiv2metadata);
+// support_xmp = gexiv2_metadata_get_supports_xmp (gexiv2metadata);
+// support_iptc = gexiv2_metadata_get_supports_iptc (gexiv2metadata);
+//
+// for (key_list = metadata->priv->sorted_key_list; key_list; key_list = key_list->next)
+// {
+// const gchar *tag;
+// const gchar *ns_name;
+// gchar *p_key = (gchar *) key_list->data;
+// gchar *value = NULL;
+// gchar *category = NULL;
+// gboolean is_xmp = FALSE;
+// gboolean has_structure = FALSE;
+// GimpAttribute *attribute;
+// GimpAttributeValueType tag_value_type;
+//
+// write_tag = FALSE;
+// namespace = FALSE;
+//
+// attribute = gimp_metadata_get_attribute_sorted (metadata, p_key);
+//
+// if (! attribute)
+// continue;
+//
+// tag = gimp_attribute_get_name (attribute);
+// has_structure = gimp_attribute_has_structure (attribute);
+//
+// if (mime_type)
+// check_mime = gimp_metadata_is_tag_supported (tag, mime_type);
+//
+// if (check_mime)
+// {
+// gchar *t_packet = NULL;
+//
+// tag_value_type = gimp_attribute_get_value_type (attribute);
+// value = gimp_attribute_get_string (attribute);
+// category = g_ascii_strdown (gimp_attribute_get_attribute_type (attribute), -1);
+//
+// if (tag && value && category)
+// {
+// if(! g_strcmp0 (category, "exif") && support_exif)
+// {
+// write_tag = TRUE;
+// }
+// else if(! g_strcmp0 (category, "xmp") && support_xmp)
+// {
+// write_tag = TRUE;
+// is_xmp = TRUE;
+//
+// namespace = gimp_attribute_is_new_namespace (attribute);
+//
+// if (namespace)
+// {
+// write_tag = FALSE;
+//
+// ns_name = gimp_attribute_get_attribute_ifd (attribute);
+//
+// for (i = 0; i < G_N_ELEMENTS (namespaces_table); i++)
+// {
+// struct Namespaces n_space = namespaces_table[i];
+// if(! g_strcmp0 (ns_name, n_space.namespace_name))
+// {
+// write_tag = TRUE;
+// break;
+// }
+// }
+// }
+//
+// if (write_tag && has_structure)
+// {
+// gboolean success = TRUE;
+// GSList *structure;
+// GSList *list;
+// GimpAttributeStructureType structure_type;
+//
+// structure = gimp_attribute_get_attribute_structure (attribute);
+// structure_type = gimp_attribute_get_structure_type (attribute);
+//
+// for (list = structure; list; list = list->next)
+// {
+// const gchar *structure_element = (const gchar*) list->data;
+// gboolean has_tag = gexiv2_metadata_has_tag (gexiv2metadata,
structure_element);
+//
+// if (!has_tag && ! has_xmp_structure (xmp_structure_list, structure_element))
+// {
+// switch (structure_type)
+// {
+// case STRUCTURE_TYPE_ALT:
+// success = gexiv2_metadata_set_xmp_tag_struct (gexiv2metadata,
structure_element, GEXIV2_STRUCTURE_XA_ALT); /*start block*/
+// break;
+// case STRUCTURE_TYPE_BAG:
+// success = gexiv2_metadata_set_xmp_tag_struct (gexiv2metadata,
structure_element, GEXIV2_STRUCTURE_XA_BAG); /*start block*/
+// break;
+// case STRUCTURE_TYPE_SEQ:
+// success = gexiv2_metadata_set_xmp_tag_struct (gexiv2metadata,
structure_element, GEXIV2_STRUCTURE_XA_SEQ); /*start block*/
+// break;
+// default:
+// success = FALSE;
+// break;
+// }
+//
+// if (success)
+// xmp_structure_list = g_slist_prepend (xmp_structure_list,
(gpointer)structure_element);
+// }
+// }
+// }
+// }
+// else if(! g_strcmp0 (category, "iptc") && support_iptc)
+// {
+// write_tag = TRUE;
+// }
+// else
+// {
+// write_tag = FALSE;
+// }
+//
+// if (write_tag)
+// {
+// switch (tag_value_type)
+// {
+// case TYPE_INVALID:
+// break;
+// case TYPE_LONG:
+// case TYPE_SLONG:
+// case TYPE_FLOAT:
+// case TYPE_DOUBLE:
+// case TYPE_SHORT:
+// case TYPE_SSHORT:
+// case TYPE_DATE:
+// case TYPE_TIME:
+// case TYPE_ASCII:
+// case TYPE_UNICODE:
+// case TYPE_BYTE:
+// case TYPE_RATIONAL:
+// case TYPE_SRATIONAL:
+// {
+// gexiv2_metadata_set_tag_string (gexiv2metadata, tag, value);
+// }
+// break;
+// case TYPE_MULTIPLE:
+// {
+// GValue h;
+// gchar **values;
+//
+// h = gimp_attribute_get_value (attribute);
+// values = (gchar **) g_value_get_boxed (&h);
+// gexiv2_metadata_set_tag_multiple (gexiv2metadata, tag, (const gchar **) values);
+// g_strfreev (values);
+// }
+// break;
+// case TYPE_UNKNOWN:
+// default:
+// break;
+//
+// }
+//
+// if (is_xmp)
+// {
+// t_packet = gexiv2_metadata_generate_xmp_packet (gexiv2metadata,
GEXIV2_USE_COMPACT_FORMAT | GEXIV2_OMIT_ALL_FORMATTING, 0);
+//
+// if (! g_strcmp0 (t_packet, o_packet))
+// {
+// gexiv2_metadata_clear_tag (gexiv2metadata, tag);
+// g_print ("cleared to gexiv2metadata:\n%s, %s\n", tag, value);
+// }
+// else
+// {
+// o_packet = g_strdup (t_packet);
+// }
+// }
+// }
+// }
+//
+// if (t_packet)
+// g_free (t_packet);
+// if (value)
+// g_free (value);
+// if (category)
+// g_free (category);
+// }
+// }
+//
+// if (o_packet)
+// g_free (o_packet);
+// if (xmp_structure_list)
+// g_slist_free (xmp_structure_list);
+//}
+
+/**
+ * gimp_metadata_to_gexiv2:
+ * @metadata: The #GimpMetadata
*
- * Since: 2.10
+ * Converts @metadata to @gexiv2metadata packet
+ *
+ * Since: GIMP 2.10
*/
-gchar *
-gimp_metadata_serialize (GimpMetadata *metadata)
+void
+gimp_metadata_to_gexiv2 (GimpMetadata *metadata)
{
- GString *string;
- gchar **exif_data = NULL;
- gchar **iptc_data = NULL;
- gchar **xmp_data = NULL;
- gchar *value;
- gchar *escaped;
- gboolean base64;
- gint i;
+ gchar *o_packet = NULL;
+ gboolean write_tag = FALSE;
+ gboolean namespace = FALSE;
+ gboolean support_exif;
+ gboolean support_xmp;
+ gboolean support_iptc;
+ GSList *xmp_structure_list = NULL;
+ GList *key_list = NULL;
+ gint i;
+ GExiv2Metadata *gexiv2metadata;
+
+ g_return_if_fail (IS_GIMP_METADATA (metadata));
+
+ gexiv2metadata = GEXIV2_METADATA(metadata);
+
+ for (i = 0; i < G_N_ELEMENTS (namespaces_table); i++)
+ {
+ struct Namespaces n_space = namespaces_table[i];
+ gexiv2_metadata_register_xmp_namespace (n_space.namespace_URI, n_space.namespace_name);
+ }
- g_return_val_if_fail (GEXIV2_IS_METADATA (metadata), NULL);
+ support_exif = gexiv2_metadata_get_supports_exif (gexiv2metadata);
+ support_xmp = gexiv2_metadata_get_supports_xmp (gexiv2metadata);
+ support_iptc = gexiv2_metadata_get_supports_iptc (gexiv2metadata);
- string = g_string_new (NULL);
+ for (key_list = metadata->priv->sorted_key_list; key_list; key_list = key_list->next)
+ {
+ const gchar *tag;
+ const gchar *ns_name;
+ gchar *p_key = (gchar *) key_list->data;
+ gchar *value = NULL;
+ gchar *category = NULL;
+ gboolean is_xmp = FALSE;
+ gboolean has_structure = FALSE;
+ gchar *t_packet = NULL;
+ GimpAttribute *attribute;
+ GimpAttributeValueType tag_value_type;
- g_string_append (string, "<?xml version='1.0' encoding='UTF-8'?>\n");
- g_string_append (string, "<metadata>\n");
+ write_tag = FALSE;
+ namespace = FALSE;
+
+ attribute = gimp_metadata_get_attribute_sorted (metadata, p_key);
- exif_data = gexiv2_metadata_get_exif_tags (metadata);
+ if (! attribute)
+ continue;
- if (exif_data)
+ tag = gimp_attribute_get_name (attribute);
+ has_structure = gimp_attribute_has_structure (attribute);
+
+ tag_value_type = gimp_attribute_get_value_type (attribute);
+ value = gimp_attribute_get_string (attribute);
+ category = g_ascii_strdown (gimp_attribute_get_attribute_type (attribute), -1);
+
+ if (tag && value && category)
+ {
+ if(! g_strcmp0 (category, "exif") && support_exif)
+ {
+ write_tag = TRUE;
+ }
+ else if(! g_strcmp0 (category, "xmp") && support_xmp)
+ {
+ write_tag = TRUE;
+ is_xmp = TRUE;
+
+ namespace = gimp_attribute_is_new_namespace (attribute);
+
+ if (namespace)
+ {
+ write_tag = FALSE;
+
+ ns_name = gimp_attribute_get_attribute_ifd (attribute);
+
+ for (i = 0; i < G_N_ELEMENTS (namespaces_table); i++)
+ {
+ struct Namespaces n_space = namespaces_table[i];
+ if(! g_strcmp0 (ns_name, n_space.namespace_name))
+ {
+ write_tag = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (write_tag && has_structure)
+ {
+ gboolean success = TRUE;
+ GSList *structure;
+ GSList *list;
+ GimpAttributeStructureType structure_type;
+
+ structure = gimp_attribute_get_attribute_structure (attribute);
+ structure_type = gimp_attribute_get_structure_type (attribute);
+
+ for (list = structure; list; list = list->next)
+ {
+ const gchar *structure_element = (const gchar*) list->data;
+ gboolean has_tag = gexiv2_metadata_has_tag (gexiv2metadata, structure_element);
+
+ if (!has_tag && ! has_xmp_structure (xmp_structure_list, structure_element))
+ {
+ switch (structure_type)
+ {
+ case STRUCTURE_TYPE_ALT:
+ success = gexiv2_metadata_set_xmp_tag_struct (gexiv2metadata,
structure_element, GEXIV2_STRUCTURE_XA_ALT); /*start block*/
+ break;
+ case STRUCTURE_TYPE_BAG:
+ success = gexiv2_metadata_set_xmp_tag_struct (gexiv2metadata,
structure_element, GEXIV2_STRUCTURE_XA_BAG); /*start block*/
+ break;
+ case STRUCTURE_TYPE_SEQ:
+ success = gexiv2_metadata_set_xmp_tag_struct (gexiv2metadata,
structure_element, GEXIV2_STRUCTURE_XA_SEQ); /*start block*/
+ break;
+ default:
+ success = FALSE;
+ break;
+ }
+
+ if (success)
+ xmp_structure_list = g_slist_prepend (xmp_structure_list,
(gpointer)structure_element);
+ }
+ }
+ }
+ }
+ else if(! g_strcmp0 (category, "iptc") && support_iptc)
+ {
+ write_tag = TRUE;
+ }
+ else
+ {
+ write_tag = FALSE;
+ }
+
+ if (write_tag)
+ {
+ switch (tag_value_type)
+ {
+ case TYPE_INVALID:
+ break;
+ case TYPE_LONG:
+ case TYPE_SLONG:
+ case TYPE_FLOAT:
+ case TYPE_DOUBLE:
+ case TYPE_SHORT:
+ case TYPE_SSHORT:
+ case TYPE_DATE:
+ case TYPE_TIME:
+ case TYPE_ASCII:
+ case TYPE_UNICODE:
+ case TYPE_BYTE:
+ case TYPE_RATIONAL:
+ case TYPE_SRATIONAL:
+ {
+ gexiv2_metadata_set_tag_string (gexiv2metadata, tag, value);
+ }
+ break;
+ case TYPE_MULTIPLE:
+ {
+ GValue h;
+ gchar **values;
+
+ h = gimp_attribute_get_value (attribute);
+ values = (gchar **) g_value_get_boxed (&h);
+ gexiv2_metadata_set_tag_multiple (gexiv2metadata, tag, (const gchar **) values);
+ g_strfreev (values);
+ }
+ break;
+ case TYPE_UNKNOWN:
+ default:
+ break;
+
+ }
+
+ if (is_xmp)
+ {
+ /* XMP packet for testing
+ * There is no way to check, if storing of tags were successful, because gexiv2 has no
error handling
+ * The only way is to compare a xpm packet before and after the storing process. If they
are equal, the storing was not successful
+ * so the stored tag must be deleted, otherwise the whole xmp data is corrupt. */
+ t_packet = gexiv2_metadata_generate_xmp_packet (gexiv2metadata, GEXIV2_USE_COMPACT_FORMAT
| GEXIV2_OMIT_ALL_FORMATTING, 0);
+
+ if (! g_strcmp0 (t_packet, o_packet))
+ {
+ gexiv2_metadata_clear_tag (gexiv2metadata, tag);
+ g_print ("cleared to gexiv2metadata:\n%s, %s\n", tag, value);
+ }
+ else
+ {
+ o_packet = g_strdup (t_packet);
+ }
+ }
+ }
+ }
+
+ if (t_packet)
+ g_free (t_packet);
+ if (value)
+ g_free (value);
+ if (category)
+ g_free (category);
+ }
+
+ if (o_packet)
+ g_free (o_packet);
+ if (xmp_structure_list)
+ g_slist_free (xmp_structure_list);
+}
+
+/**
+ * gimp_metadata_to_parent:
+ * @metadata: The #GimpMetadata
+ * @gexiv2metadata: The #GimpMetadata
+ * @mime_type: the mime type of the image
+ *
+ * Converts @metadata to @gexiv2metadata in gexiv2 format
+ *
+ * Since: GIMP 2.10
+ */
+void
+gimp_metadata_to_parent (GimpMetadata *metadata,
+ GimpAttribute *attribute)
+{
+ gchar *o_packet = NULL;
+ gboolean write_tag = FALSE;
+ gboolean namespace = FALSE;
+ gint i;
+ GExiv2Metadata *gexiv2metadata;
+ const gchar *tag;
+ const gchar *ns_name;
+ gchar *value = NULL;
+ gchar *category = NULL;
+ gboolean is_xmp = FALSE;
+ gboolean has_structure = FALSE;
+ GimpAttributeValueType tag_value_type;
+ gchar *t_packet = NULL;
+ GimpMetadataPrivate *priv;
+
+ g_return_if_fail (IS_GIMP_METADATA (metadata));
+ g_return_if_fail (GIMP_IS_ATTRIBUTE (attribute));
+
+ gexiv2metadata = GEXIV2_METADATA (metadata);
+ priv = GIMP_METADATA_GET_PRIVATE (metadata);
+ write_tag = FALSE;
+ namespace = FALSE;
+
+ tag = gimp_attribute_get_name (attribute);
+ has_structure = gimp_attribute_has_structure (attribute);
+
+
+ tag_value_type = gimp_attribute_get_value_type (attribute);
+ value = gimp_attribute_get_string (attribute);
+ category = g_ascii_strdown (gimp_attribute_get_attribute_type (attribute), -1);
+
+// if (g_str_has_prefix (tag, "Xmp"))
+// {
+// g_print ("found xmp: %s\n", tag);
+// }
+//
+// if (g_str_has_prefix (tag, "Xmp.xmpMM.History"))
+// {
+// g_print ("found struct: %s\n", tag);
+// }
+
+
+ if (tag && value && category)
{
- for (i = 0; exif_data[i] != NULL; i++)
+ if(! g_strcmp0 (category, "exif"))
{
- value = gexiv2_metadata_get_tag_string (metadata, exif_data[i]);
- escaped = gimp_metadata_escape (exif_data[i], value, &base64);
- g_free (value);
+ write_tag = TRUE;
+ }
+ else if(! g_strcmp0 (category, "xmp"))
+ {
+ write_tag = TRUE;
+ is_xmp = TRUE;
+
+ /* XMP packet for testing before adding tag
+ * There is no way to check, if storing of tags were successful, because gexiv2 has no error
handling
+ * The only way is to compare a xpm packet before and after the storing process. If they are
equal, the storing was not successful
+ * so the stored tag must be deleted, otherwise the whole xmp data is corrupt. */
+ o_packet = gexiv2_metadata_generate_xmp_packet (gexiv2metadata, GEXIV2_USE_COMPACT_FORMAT |
GEXIV2_OMIT_ALL_FORMATTING, 0);
- gimp_metadata_append_tag (string, exif_data[i], escaped, base64);
+ namespace = gimp_attribute_is_new_namespace (attribute);
+
+ if (namespace)
+ {
+ write_tag = FALSE;
+
+ ns_name = gimp_attribute_get_attribute_ifd (attribute);
+
+ for (i = 0; i < G_N_ELEMENTS (namespaces_table); i++)
+ {
+ struct Namespaces n_space = namespaces_table[i];
+ if(! g_strcmp0 (ns_name, n_space.namespace_name))
+ {
+ write_tag = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (write_tag && has_structure)
+ {
+ gboolean success = TRUE;
+ GSList *structure;
+ GSList *list;
+ GimpAttributeStructureType structure_type;
+
+ structure = gimp_attribute_get_attribute_structure (attribute);
+ structure_type = gimp_attribute_get_structure_type (attribute);
+
+ for (list = structure; list; list = list->next)
+ {
+ const gchar *structure_element = (const gchar*) list->data;
+ gboolean has_tag = gexiv2_metadata_has_tag (gexiv2metadata, structure_element);
+
+ if (!has_tag && ! has_xmp_structure (priv->xmp_structure_list, structure_element))
+ {
+ switch (structure_type)
+ {
+ case STRUCTURE_TYPE_ALT:
+ success = gexiv2_metadata_set_xmp_tag_struct (gexiv2metadata, structure_element,
GEXIV2_STRUCTURE_XA_ALT); /*start block*/
+ break;
+ case STRUCTURE_TYPE_BAG:
+ success = gexiv2_metadata_set_xmp_tag_struct (gexiv2metadata, structure_element,
GEXIV2_STRUCTURE_XA_BAG); /*start block*/
+ break;
+ case STRUCTURE_TYPE_SEQ:
+ success = gexiv2_metadata_set_xmp_tag_struct (gexiv2metadata, structure_element,
GEXIV2_STRUCTURE_XA_SEQ); /*start block*/
+ break;
+ default:
+ success = FALSE;
+ break;
+ }
+
+ if (success)
+ priv->xmp_structure_list = g_slist_prepend (priv->xmp_structure_list,
(gpointer)structure_element);
+ }
+ }
+ }
+ }
+ else if(! g_strcmp0 (category, "iptc"))
+ {
+ write_tag = TRUE;
+ }
+ else
+ {
+ write_tag = FALSE;
}
- g_strfreev (exif_data);
+ if (write_tag)
+ {
+ switch (tag_value_type)
+ {
+ case TYPE_INVALID:
+ break;
+ case TYPE_LONG:
+ case TYPE_SLONG:
+ case TYPE_FLOAT:
+ case TYPE_DOUBLE:
+ case TYPE_SHORT:
+ case TYPE_SSHORT:
+ case TYPE_DATE:
+ case TYPE_TIME:
+ case TYPE_ASCII:
+ case TYPE_UNICODE:
+ case TYPE_BYTE:
+ case TYPE_RATIONAL:
+ case TYPE_SRATIONAL:
+ {
+ gexiv2_metadata_set_tag_string (gexiv2metadata, tag, value);
+ }
+ break;
+ case TYPE_MULTIPLE:
+ {
+ GValue h;
+ gchar **values;
+
+ h = gimp_attribute_get_value (attribute);
+ values = (gchar **) g_value_get_boxed (&h);
+ gexiv2_metadata_set_tag_multiple (gexiv2metadata, tag, (const gchar **) values);
+ g_strfreev (values);
+ }
+ break;
+ case TYPE_UNKNOWN:
+ default:
+ break;
+
+ }
+
+ if (is_xmp)
+ {
+ /* XMP packet for testing after adding tag */
+ t_packet = gexiv2_metadata_generate_xmp_packet (gexiv2metadata, GEXIV2_USE_COMPACT_FORMAT |
GEXIV2_OMIT_ALL_FORMATTING, 0);
+
+ if (! g_strcmp0 (t_packet, o_packet))
+ {
+ gexiv2_metadata_clear_tag (gexiv2metadata, tag);
+ g_print ("cleared to gexiv2metadata:\n%s, %s\n", tag, value);
+ }
+ }
+ }
}
- xmp_data = gexiv2_metadata_get_xmp_tags (metadata);
+ if (t_packet)
+ g_free (t_packet);
+ if (value)
+ g_free (value);
+ if (category)
+ g_free (category);
+ if (o_packet)
+ g_free (o_packet);
+}
- if (xmp_data)
+/**
+ * gimp_metadata_to_xmp_packet: ToDo handle xmp-packet
+ * @metadata: The #GimpMetadata
+ * @mime_type : a mime_type
+ *
+ * Converts @metadata to a xmp packet
+ * It looks like an ugly hack, but let
+ * gexiv2/exiv2 do all the hard work.
+ *
+ * Return value: a #gchar*, representing a xml packet.
+ *
+ * Since: GIMP 2.10
+ */
+const gchar *
+gimp_metadata_to_xmp_packet (GimpMetadata *metadata,
+ const gchar *mime_type)
+
+{
+ gint i;
+ const gchar *packet_string;
+ gchar *o_packet = NULL;
+ gboolean check_mime = TRUE;
+ gboolean write_tag = FALSE;
+ gboolean namespace = FALSE;
+ gboolean support_exif;
+ gboolean support_xmp;
+ gboolean support_iptc;
+ GExiv2Metadata *gexiv2metadata;
+ GSList *xmp_structure_list = NULL;
+ GList *key_list = NULL;
+
+ g_return_val_if_fail (IS_GIMP_METADATA (metadata), NULL);
+
+ gimp_metadata_from_gexiv2 (metadata);
+
+ gexiv2metadata = gimp_metadata_new_gexiv2metadata ();
+
+ for (i = 0; i < G_N_ELEMENTS (namespaces_table); i++)
{
- for (i = 0; xmp_data[i] != NULL; i++)
+ struct Namespaces n_space = namespaces_table[i];
+ gexiv2_metadata_register_xmp_namespace (n_space.namespace_URI, n_space.namespace_name);
+ }
+
+ support_exif = gexiv2_metadata_get_supports_exif (gexiv2metadata);
+ support_xmp = gexiv2_metadata_get_supports_xmp (gexiv2metadata);
+ support_iptc = gexiv2_metadata_get_supports_iptc (gexiv2metadata);
+
+ for (key_list = metadata->priv->sorted_key_list; key_list; key_list = key_list->next)
+ {
+ gchar *p_key = (gchar *) key_list->data;
+
+ const gchar *tag;
+ const gchar *attribute_tag;
+ const gchar *ns_name;
+ gchar *new_tag = NULL;
+ gchar *value = NULL;
+ gchar *category = NULL;
+ gboolean has_structure;
+ gboolean temp_attribute = FALSE;
+ GimpAttribute *attribute;
+ GimpAttributeValueType tag_value_type;
+
+ write_tag = FALSE;
+ namespace = FALSE;
+
+ attribute = gimp_metadata_get_attribute_sorted (metadata, p_key);
+
+ if (! attribute)
+ continue;
+
+ tag = gimp_attribute_get_name (attribute);
+ attribute_tag = gimp_attribute_get_attribute_tag (attribute);
+ has_structure = gimp_attribute_has_structure (attribute);
+
+ if (mime_type)
+ check_mime = gimp_metadata_is_tag_supported (tag, mime_type);
+
+ if (check_mime)
{
- value = gexiv2_metadata_get_tag_string (metadata, xmp_data[i]);
- escaped = gimp_metadata_escape (xmp_data[i], value, &base64);
- g_free (value);
+ gchar *sec_tag = NULL;
+ gchar *t_packet = NULL;
+
+ tag_value_type = gimp_attribute_get_value_type (attribute);
+ value = gimp_attribute_get_string (attribute);
+ category = g_ascii_strdown (gimp_attribute_get_attribute_type (attribute), -1);
+
+ if (tag && value && category)
+ {
+ if(! g_strcmp0 (category, "exif") && support_exif)
+ {
+ new_tag = g_strdup_printf ("Xmp.exif.%s", attribute_tag);
+
+ write_tag = TRUE;
+
+// /* Now for some specialities */
+// if (! g_strcmp0 (new_tag, "Xmp.exif.ISOSpeedRatings"))
+// {
+// g_print ("ungültig\n");
+// attribute = gimp_attribute_new_string ("Xmp.exif.ISOSpeedRatings", value,
TYPE_ASCII);
+// if (attribute)
+// {
+// temp_attribute = TRUE;
+// tag_value_type = TYPE_ASCII;
+// }
+// else
+// {
+// write_tag = FALSE;
+// }
+// }
+ }
+ else if(! g_strcmp0 (category, "xmp") && support_xmp)
+ {
+ new_tag = g_strdup_printf ("%s", tag);
+
+ write_tag = TRUE;
+
+ namespace = gimp_attribute_is_new_namespace (attribute);
+
+ if (namespace)
+ {
+ write_tag = FALSE;
+
+ ns_name = gimp_attribute_get_attribute_ifd (attribute);
+
+ for (i = 0; i < G_N_ELEMENTS (namespaces_table); i++)
+ {
+ struct Namespaces n_space = namespaces_table[i];
+ if(! g_strcmp0 (ns_name, n_space.namespace_name))
+ {
+ write_tag = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (write_tag && has_structure)
+ {
+ gboolean success = TRUE;
+ GSList *structure;
+ GSList *list;
+ GimpAttributeStructureType structure_type;
+
+ structure = gimp_attribute_get_attribute_structure (attribute);
+ structure_type = gimp_attribute_get_structure_type (attribute);
+
+ for (list = structure; list; list = list->next)
+ {
+ const gchar *structure_element = (const gchar*) list->data;
+ gboolean has_tag = gexiv2_metadata_has_tag (gexiv2metadata,
structure_element);
+
+ if (!has_tag && ! has_xmp_structure (xmp_structure_list, structure_element))
+ {
+ switch (structure_type)
+ {
+ case STRUCTURE_TYPE_ALT:
+ success = gexiv2_metadata_set_xmp_tag_struct (gexiv2metadata,
structure_element, GEXIV2_STRUCTURE_XA_ALT); /*start block*/
+ break;
+ case STRUCTURE_TYPE_BAG:
+ success = gexiv2_metadata_set_xmp_tag_struct (gexiv2metadata,
structure_element, GEXIV2_STRUCTURE_XA_BAG); /*start block*/
+ break;
+ case STRUCTURE_TYPE_SEQ:
+ success = gexiv2_metadata_set_xmp_tag_struct (gexiv2metadata,
structure_element, GEXIV2_STRUCTURE_XA_SEQ); /*start block*/
+ break;
+ default:
+ success = FALSE;
+ break;
+ }
+
+ if (success)
+ xmp_structure_list = g_slist_prepend (xmp_structure_list,
(gpointer)structure_element);
+ }
+ }
+ }
+ }
+ else if(! g_strcmp0 (category, "iptc") && support_iptc)
+ {
+ new_tag = g_strdup_printf ("Xmp.iptc.%s", attribute_tag);
+ sec_tag = g_strdup_printf ("Xmp.iptcExt.%s", attribute_tag);
+ write_tag = TRUE;
+ }
+ else
+ {
+ write_tag = FALSE;
+ }
+
+ if (write_tag)
+ {
+ switch (tag_value_type)
+ {
+ case TYPE_INVALID:
+ break;
+ case TYPE_LONG:
+ case TYPE_SLONG:
+ case TYPE_FLOAT:
+ case TYPE_DOUBLE:
+ case TYPE_SHORT:
+ case TYPE_SSHORT:
+ case TYPE_DATE:
+ case TYPE_TIME:
+ case TYPE_ASCII:
+ case TYPE_UNICODE:
+ case TYPE_BYTE:
+ case TYPE_RATIONAL:
+ case TYPE_SRATIONAL:
+ {
+ gexiv2_metadata_set_tag_string (gexiv2metadata, new_tag, value);
+ if (sec_tag)
+ {
+ gexiv2_metadata_set_tag_string (gexiv2metadata, sec_tag, value);
+ g_free (sec_tag);
+ }
+
+ }
+ break;
+ case TYPE_MULTIPLE:
+ {
+ GValue h;
+ gchar **values;
+
+ h = gimp_attribute_get_value (attribute);
+ values = (gchar **) g_value_get_boxed (&h);
+ gexiv2_metadata_set_tag_multiple (gexiv2metadata, new_tag, (const gchar **) values);
+ if (sec_tag)
+ {
+ gexiv2_metadata_set_tag_multiple (gexiv2metadata, sec_tag, (const gchar **)
values);
+ g_free (sec_tag);
+ }
+ g_strfreev (values);
+ }
+ break;
+ case TYPE_UNKNOWN:
+ default:
+ break;
+
+ }
+
+ t_packet = gexiv2_metadata_generate_xmp_packet (gexiv2metadata, GEXIV2_USE_COMPACT_FORMAT
| GEXIV2_OMIT_ALL_FORMATTING, 0);
+
+ if (! t_packet || ! g_strcmp0 (t_packet, o_packet))
+ {
+ gexiv2_metadata_clear_tag (gexiv2metadata, new_tag);
+ }
+ else
+ {
+ o_packet = g_strdup (t_packet);
+ }
+ }
+ }
- gimp_metadata_append_tag (string, xmp_data[i], escaped, base64);
+ if (t_packet)
+ g_free (t_packet);
+ if (value)
+ g_free (value);
+ if (category)
+ g_free (category);
+ if (new_tag)
+ g_free (new_tag);
+
+ if (temp_attribute)
+ g_object_unref (attribute);
+ }
+ }
+
+ if (o_packet)
+ g_free (o_packet);
+ if (xmp_structure_list)
+ g_slist_free (xmp_structure_list);
+
+ packet_string = gexiv2_metadata_generate_xmp_packet (gexiv2metadata, GEXIV2_USE_COMPACT_FORMAT |
GEXIV2_WRITE_ALIAS_COMMENTS, 0);
+ return packet_string;
+}
+
+/**
+ * gimp_metadata_has_tag_type:
+ * @metadata: The metadata
+ * @tag_type: The #GimpAttributeTagType to test
+ *
+ * tests, if @metadata contains at least one tag of @tag_type
+ *
+ * Return value: TRUE if found, FALSE otherwise
+ *
+ * Since: GIMP 2.10
+ */
+gboolean
+gimp_metadata_has_tag_type (GimpMetadata *metadata,
+ GimpAttributeTagType tag_type)
+{
+ GHashTableIter iter;
+ gpointer p_key, p_value;
+
+ g_return_val_if_fail (IS_GIMP_METADATA (metadata), FALSE);
+
+ gimp_metadata_from_gexiv2 (metadata);
+
+ g_hash_table_iter_init (&iter, metadata->priv->attribute_table);
+
+ while (g_hash_table_iter_next (&iter, &p_key, &p_value))
+ {
+ GimpAttribute *attribute;
+
+ attribute = (GimpAttribute *) p_value;
+
+ if (gimp_attribute_get_tag_type (attribute) == tag_type)
+ return TRUE;
+
+ }
+ return FALSE;
+}
+
+GList *
+gimp_metadata_iter_init (GimpMetadata *metadata, GList **iter)
+{
+ g_return_val_if_fail (IS_GIMP_METADATA (metadata), NULL);
+
+ gimp_metadata_from_gexiv2 (metadata);
+
+ *iter = metadata->priv->sorted_key_list;
+ iter_initialized = TRUE;
+
+ return metadata->priv->sorted_key_list;
+}
+
+gboolean
+gimp_metadata_iter_next (GimpMetadata *metadata, GimpAttribute **attribute, GList **prev)
+{
+ gchar *sorted;
+ GList *tmp;
+
+ g_return_val_if_fail (IS_GIMP_METADATA (metadata), FALSE);
+
+ *attribute = NULL;
+
+ if (iter_initialized)
+ {
+ tmp = g_list_first (*prev);
+ }
+ else
+ {
+ tmp = g_list_next (*prev);
+ }
+
+ if (tmp)
+ {
+ *prev = tmp;
+ sorted = (gchar *) tmp->data;
+
+ *attribute = gimp_metadata_get_attribute_sorted (metadata, sorted);
+ }
+
+ iter_initialized = FALSE;
+
+ if (*attribute)
+ return TRUE;
+ else
+ return FALSE;
+
+}
+
+/**
+ * gimp_metadata_error_quark:
+ *
+ * Return value: #GQuark
+ *
+ * Since: GIMP 2.10
+ */
+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;
+}
+
+/**
+ * gimp_metadata_deserialize_error:
+ *
+ * Error while parsing
+ *
+ * Since: GIMP 2.10
+ */
+static void
+gimp_metadata_deserialize_error (GMarkupParseContext *context,
+ GError *error,
+ gpointer user_data)
+{
+ g_printerr ("XML parse error: %s\n", error->message);
+}
+
+/**
+ * gimp_metadata_name_to_value:
+ *
+ * @attribute_names : #gchar **
+ * @attribute_values : #gchar **
+ * @name : #gchar *
+ *
+ * searches for values in @attribute_values by a given @name (parsing xml)
+ *
+ * Since: GIMP 2.10
+ */
+static const gchar*
+gimp_metadata_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;
}
- g_strfreev (xmp_data);
+ attribute_names++;
+ attribute_values++;
}
- iptc_data = gexiv2_metadata_get_iptc_tags (metadata);
+ return NULL;
+}
+
+/**
+ * gimp_metadata_deserialize_start_element:
+ *
+ * @context : #GMarkupParseContext
+ * @element_name : #gchar *
+ * @attribute_names : #gchar **
+ * @attribute_values : #gchar **
+ * @user_data : #gpointer to #GimpMetadataParseData struct
+ * @error : #GError **
+ *
+ * start of a tag (parsing xml)
+ *
+ * Since: GIMP 2.10
+ */
+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 (iptc_data)
+ if (! strcmp (element_name, "tag"))
{
- for (i = 0; iptc_data[i] != NULL; i++)
+ const gchar *name;
+ const gchar *encoding;
+
+ name = gimp_metadata_name_to_value (attribute_names,
+ attribute_values,
+ "name");
+ encoding = gimp_metadata_name_to_value (attribute_names,
+ attribute_values,
+ "encoding");
+
+ if (! name)
{
- value = gexiv2_metadata_get_tag_string (metadata, iptc_data[i]);
- escaped = gimp_metadata_escape (iptc_data[i], value, &base64);
- g_free (value);
+ g_set_error (error, gimp_metadata_error_quark (), 1001,
+ "Element 'tag' does not contain required attribute 'name'.");
+ return;
+ }
- gimp_metadata_append_tag (string, iptc_data[i], escaped, base64);
+ strncpy (parse_data->name, name, sizeof (parse_data->name));
+ parse_data->name[sizeof (parse_data->name) - 1] = 0;
+
+ parse_data->base64 = (encoding && ! strcmp (encoding, "base64"));
+ }
+ else if (! strcmp (element_name, "attribute"))
+ {
+ const gchar *name = NULL;
+ const gchar *type = NULL;
+
+ name = gimp_metadata_name_to_value (attribute_names,
+ attribute_values,
+ "name");
+ type = gimp_metadata_name_to_value (attribute_names,
+ attribute_values,
+ "type");
+
+ if (! name)
+ {
+ g_set_error (error, gimp_metadata_error_quark (), 1001,
+ "Element 'tag' does not contain required attribute 'name'.");
+ return;
}
- g_strfreev (iptc_data);
+ if (! type)
+ {
+ g_set_error (error, gimp_metadata_error_quark (), 1001,
+ "Element 'tag' does not contain required attribute 'type'.");
+ return;
+ }
+
+ strncpy (parse_data->name, name, sizeof (parse_data->name));
+ parse_data->name[sizeof (parse_data->name) - 1] = 0;
+
+ parse_data->type = (GimpAttributeValueType) atoi (type);
+ }
+ else if (! strcmp (element_name, "value"))
+ {
+ const gchar *encoding = NULL;
+
+ encoding = gimp_metadata_name_to_value (attribute_names,
+ attribute_values,
+ "encoding");
+
+ parse_data->base64 = (encoding && ! strcmp (encoding, "base64"));
}
+ else if (! strcmp (element_name, "interpreted"))
+ {
+ const gchar *encoding = NULL;
- g_string_append (string, "</metadata>\n");
+ encoding = gimp_metadata_name_to_value (attribute_names,
+ attribute_values,
+ "encoding");
+
+ parse_data->base64 = (encoding && ! strcmp (encoding, "base64"));
+ }
- return g_string_free (string, FALSE);
}
/**
- * gimp_metadata_load_from_file:
- * @file: The #GFile to load the metadata from
- * @error: Return location for error message
+ * gimp_metadata_deserialize_text:
*
- * Loads #GimpMetadata from @file.
+ * @context : #GMarkupParseContext
+ * @text : const #gchar *
+ * @text_len : #gsize
+ * @user_data : #gpointer to #GimpMetadataParseData struct
+ * @error : #GError **
*
- * Return value: The loaded #GimpMetadata.
+ * text of a tag (parsing xml)
*
- * Since: 2.10
+ * Since: GIMP 2.10
*/
-GimpMetadata *
-gimp_metadata_load_from_file (GFile *file,
- GError **error)
+static void
+gimp_metadata_deserialize_text (GMarkupParseContext *context,
+ const gchar *text,
+ gsize text_len,
+ gpointer user_data,
+ GError **error)
{
- GExiv2Metadata *meta = NULL;
- gchar *path;
- gchar *filename;
+ GimpMetadataParseData *parse_data = user_data;
+ GimpAttribute *attribute = NULL;
+ const gchar *current_element;
- g_return_val_if_fail (G_IS_FILE (file), NULL);
- g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+ current_element = g_markup_parse_context_get_element (context);
- path = g_file_get_path (file);
+ if (! g_strcmp0 (current_element, "tag")) /*old metadata*/
+ {
+ gchar *value = g_strndup (text, text_len);
- if (! path)
+ if (parse_data->base64)
+ {
+ guchar *decoded;
+ gsize len;
+
+ decoded = g_base64_decode (value, &len);
+
+ if (decoded[len - 1] == '\0')
+ gexiv2_metadata_set_tag_string (GEXIV2_METADATA (parse_data->metadata),
+ parse_data->name,
+ (const gchar *) decoded);
+
+ g_free (decoded);
+ }
+ else
+ {
+ gexiv2_metadata_set_tag_string (GEXIV2_METADATA (parse_data->metadata),
+ parse_data->name,
+ value);
+ }
+
+ g_free (value);
+ }
+ else if (! g_strcmp0 (current_element, "value"))
{
- g_set_error (error, gimp_metadata_error_quark (), 0,
- _("Can load metadata only from local files"));
- return NULL;
+ gchar *value = g_strndup (text, text_len);
+
+ if (parse_data->base64)
+ {
+ guchar *decoded;
+ gsize len;
+
+ decoded = g_base64_decode (value, &len);
+
+ if (decoded[len - 1] == '\0')
+ attribute = gimp_attribute_new_string (parse_data->name,
+ (gchar *)decoded,
+ parse_data->type);
+
+ g_free (decoded);
+ }
+ else
+ {
+ attribute = gimp_attribute_new_string (parse_data->name,
+ value,
+ parse_data->type);
+ }
+
+ g_free (value);
+
+ if (attribute && gimp_attribute_is_valid (attribute))
+ {
+ gimp_metadata_add_attribute (parse_data->metadata, attribute);
+ current_attribute = attribute;
+ }
+ else
+ g_object_unref (attribute);
+
}
+ else if (! g_strcmp0 (current_element, "structure"))
+ {
+ GimpAttribute *attribute = NULL;
+ gchar *value = NULL;
-#ifdef G_OS_WIN32
- filename = g_win32_locale_filename_from_utf8 (path);
-#else
- filename = g_strdup (path);
-#endif
+ attribute = current_attribute;
- g_free (path);
+ if (attribute)
+ {
+ gint v;
+ GimpAttributeStructureType struct_type = STRUCTURE_TYPE_BAG;
- if (gexiv2_initialize ())
+ value = g_strndup (text, text_len);
+
+ v = atoi (value);
+
+ if (v > -1)
+ struct_type = (GimpAttributeStructureType) v;
+
+ gimp_attribute_set_structure_type (attribute, struct_type);
+
+ g_free (value);
+ }
+ }
+ else if (! g_strcmp0 (current_element, "interpreted"))
{
- meta = gexiv2_metadata_new ();
+ GimpAttribute *attribute = NULL;
+ gchar *value = NULL;
+
+ attribute = current_attribute;
- if (! gexiv2_metadata_open_path (meta, filename, error))
+ if (attribute)
{
- g_object_unref (meta);
- g_free (filename);
+ value = g_strndup (text, text_len);
- return NULL;
+ if (parse_data->base64)
+ {
+ guchar *decoded = NULL;
+ gsize len;
+
+ decoded = g_base64_decode (value, &len);
+
+ if (decoded[len - 1] == '\0')
+ gimp_attribute_set_interpreted_string (attribute, (const gchar *)decoded);
+
+ g_free (decoded);
+ }
+ else
+ {
+ gimp_attribute_set_interpreted_string (attribute, (const gchar *)value);
+ }
+ g_free (value);
}
}
+}
- g_free (filename);
+/**
+ * gimp_metadata_deserialize_end_element:
+ *
+ * @context : #GMarkupParseContext
+ * @element_name : #gchar *
+ * @user_data : #gpointer to #GimpMetadataParseData struct
+ * @error : #GError **
+ *
+ * end of a tag (parsing xml)
+ *
+ * Since: GIMP 2.10
+ */
+static void
+gimp_metadata_deserialize_end_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ gpointer user_data,
+ GError **error)
+{
+ GimpMetadataParseData *parse_data = user_data;
- return meta;
+ if (! strcmp (element_name, "attribute"))
+ {
+ current_attribute = NULL;
+ }
+ else if (! strcmp (element_name, "value"))
+ {
+ parse_data->base64 = FALSE;
+ }
+ else if (! strcmp (element_name, "interpreted"))
+ {
+ parse_data->base64 = FALSE;
+ }
+}
+
+static gboolean
+has_xmp_structure (GSList *xmp_list, const gchar *entry)
+{
+ GSList *list;
+
+ for (list = xmp_list; list; list = list->next)
+ {
+ const gchar *to_test = (const gchar*) list->data;
+
+ if (! g_strcmp0 (to_test, entry))
+ return TRUE;
+ }
+
+ return FALSE;
}
/**
+ * gimp_metadata_new_gexiv2metadata:
+ *
+ * Creates a new #GExiv2Metadata instance.
+ *
+ * Return value: The new #GExiv2Metadata.
+ *
+ * Since: 2.10
+ */
+GExiv2Metadata *
+gimp_metadata_new_gexiv2metadata (void)
+{
+ GExiv2Metadata *gexiv2metadata = NULL;
+ gint i;
+
+ if (gexiv2_initialize ())
+ {
+ gexiv2metadata = gexiv2_metadata_new ();
+
+ if (! gexiv2_metadata_open_buf (gexiv2metadata, wilber_jpg, wilber_jpg_len,
+ NULL))
+ {
+ g_object_unref (gexiv2metadata);
+
+ return NULL;
+ }
+ for (i = 0; i < G_N_ELEMENTS (namespaces_table); i++)
+ {
+ struct Namespaces n_space = namespaces_table[i];
+ gexiv2_metadata_register_xmp_namespace (n_space.namespace_URI, n_space.namespace_name);
+ }
+
+ }
+ return gexiv2metadata;
+}
+
+
+/**
* gimp_metadata_save_to_file:
* @metadata: A #GimpMetadata instance.
* @file: The file to save the metadata to
@@ -567,11 +2679,11 @@ gimp_metadata_save_to_file (GimpMetadata *metadata,
GFile *file,
GError **error)
{
- gchar *path;
- gchar *filename;
- gboolean success;
+ gchar *path;
+ gchar *filename;
+ gboolean success;
- g_return_val_if_fail (GEXIV2_IS_METADATA (metadata), FALSE);
+ g_return_val_if_fail (IS_GIMP_METADATA (metadata), FALSE);
g_return_val_if_fail (G_IS_FILE (file), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
@@ -592,7 +2704,7 @@ gimp_metadata_save_to_file (GimpMetadata *metadata,
g_free (path);
- success = gexiv2_metadata_save_file (metadata, filename, error);
+ success = gexiv2_metadata_save_file (GEXIV2_METADATA (metadata), filename, error);
g_free (filename);
@@ -600,6 +2712,63 @@ gimp_metadata_save_to_file (GimpMetadata *metadata,
}
/**
+ * gimp_metadata_load_from_file:
+ * @file: The #GFile to load the metadata from
+ * @error: Return location for error message
+ *
+ * Loads #GimpMetadata from @file.
+ *
+ * Return value: The loaded #GimpMetadata.
+ *
+ * Since: 2.10
+ */
+GimpMetadata *
+gimp_metadata_load_from_file (GFile *file,
+ GError **error)
+{
+ GimpMetadata *metadata = 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 ())
+ {
+ metadata = gimp_metadata_new ();
+
+ if (! gexiv2_metadata_open_path (GEXIV2_METADATA (metadata), filename, error))
+ {
+ g_free (filename);
+ g_object_unref (metadata);
+
+ return NULL;
+ }
+ }
+
+ g_free (filename);
+
+ return metadata;
+}
+
+/**
* gimp_metadata_set_from_exif:
* @metadata: A #GimpMetadata instance.
* @exif_data: The blob of Exif data to set
@@ -619,17 +2788,20 @@ gimp_metadata_set_from_exif (GimpMetadata *metadata,
GError **error)
{
- GByteArray *exif_bytes;
- GimpMetadata *exif_metadata;
- guint8 data_size[2] = { 0, };
- const guint8 eoi[2] = { 0xff, 0xd9 };
+ GByteArray *exif_bytes;
+ GExiv2Metadata *exif_metadata;
+ guint8 data_size[2] = { 0, };
+ const guint8 eoi[2] = { 0xff, 0xd9 };
- g_return_val_if_fail (GEXIV2_IS_METADATA (metadata), FALSE);
+ g_return_val_if_fail (IS_GIMP_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 + 2 < 65536, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+ exif_metadata = GEXIV2_METADATA (metadata);
+ g_object_ref (exif_metadata);
+
data_size[0] = ((exif_data_length + 2) & 0xFF00) >> 8;
data_size[1] = ((exif_data_length + 2) & 0x00FF);
@@ -642,8 +2814,6 @@ gimp_metadata_set_from_exif (GimpMetadata *metadata,
(guint8 *) exif_data, exif_data_length);
exif_bytes = g_byte_array_append (exif_bytes, eoi, 2);
- exif_metadata = gimp_metadata_new ();
-
if (! gexiv2_metadata_open_buf (exif_metadata,
exif_bytes->data, exif_bytes->len, error))
{
@@ -661,7 +2831,6 @@ gimp_metadata_set_from_exif (GimpMetadata *metadata,
return FALSE;
}
- gimp_metadata_add (exif_metadata, metadata);
g_object_unref (exif_metadata);
g_byte_array_free (exif_bytes, TRUE);
@@ -687,14 +2856,15 @@ gimp_metadata_set_from_xmp (GimpMetadata *metadata,
gint xmp_data_length,
GError **error)
{
- GimpMetadata *xmp_metadata;
+ GExiv2Metadata *xmp_metadata;
- g_return_val_if_fail (GEXIV2_IS_METADATA (metadata), FALSE);
+ g_return_val_if_fail (IS_GIMP_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_metadata = gimp_metadata_new ();
+ xmp_metadata = GEXIV2_METADATA (metadata);
+ g_object_ref (xmp_metadata);
if (! gexiv2_metadata_open_buf (xmp_metadata,
xmp_data, xmp_data_length, error))
@@ -711,200 +2881,65 @@ gimp_metadata_set_from_xmp (GimpMetadata *metadata,
return FALSE;
}
- gimp_metadata_add (xmp_metadata, metadata);
g_object_unref (xmp_metadata);
-
return TRUE;
}
/**
- * gimp_metadata_set_pixel_size:
- * @metadata: A #GimpMetadata instance.
- * @width: Width in pixels
- * @height: Height in pixels
- *
- * Sets Exif.Image.ImageWidth and Exif.Image.ImageLength on @metadata.
- *
- * Since: 2.10
- */
-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);
-}
-
-/**
- * gimp_metadata_set_bits_per_sample:
- * @metadata: A #GimpMetadata instance.
- * @bits_per_sample: Bits per pixel, per component
- *
- * Sets Exif.Image.BitsPerSample on @metadata.
- *
- * Since: 2.10
- */
-void
-gimp_metadata_set_bits_per_sample (GimpMetadata *metadata,
- gint bits_per_sample)
-{
- gchar buffer[32];
-
- g_return_if_fail (GEXIV2_IS_METADATA (metadata));
-
- g_snprintf (buffer, sizeof (buffer), "%d %d %d",
- bits_per_sample, bits_per_sample, bits_per_sample);
- gexiv2_metadata_set_tag_string (metadata, "Exif.Image.BitsPerSample", buffer);
-}
-
-/**
- * gimp_metadata_get_resolution:
- * @metadata: A #GimpMetadata instance.
- * @xres: Return location for the X Resolution, in ppi
- * @yres: Return location for the Y Resolution, in ppi
- * @unit: Return location for the unit unit
+ * gimp_metadata_is_tag_supported:
+ * @tag: A metadata tag name
+ * @mime_type: A mime type
*
- * Returns values based on Exif.Image.XResolution,
- * Exif.Image.YResolution and Exif.Image.ResolutionUnit of @metadata.
+ * Returns whether @tag is supported in a file of type @mime_type.
*
- * Return value: %TRUE on success, %FALSE otherwise.
+ * Return value: %TRUE if the @tag supported with @mime_type, %FALSE otherwise.
*
* Since: 2.10
*/
gboolean
-gimp_metadata_get_resolution (GimpMetadata *metadata,
- gdouble *xres,
- gdouble *yres,
- GimpUnit *unit)
+gimp_metadata_is_tag_supported (const gchar *tag,
+ const gchar *mime_type)
{
- gint xnom, xdenom;
- gint ynom, ydenom;
+ gint j;
- g_return_val_if_fail (GEXIV2_IS_METADATA (metadata), FALSE);
+ g_return_val_if_fail (tag != NULL, FALSE);
+ g_return_val_if_fail (mime_type != NULL, FALSE);
- if (gexiv2_metadata_get_exif_tag_rational (metadata,
- "Exif.Image.XResolution",
- &xnom, &xdenom) &&
- gexiv2_metadata_get_exif_tag_rational (metadata,
- "Exif.Image.YResolution",
- &ynom, &ydenom))
+ for (j = 0; j < G_N_ELEMENTS (unsupported_tags); j++)
{
- gchar *un;
- gint exif_unit = 2;
-
- un = gexiv2_metadata_get_tag_string (metadata,
- "Exif.Image.ResolutionUnit");
- if (un)
+ if (g_str_has_prefix (tag, unsupported_tags[j]))
{
- exif_unit = atoi (un);
- g_free (un);
+ return FALSE;
}
+ }
- if (xnom != 0 && xdenom != 0 &&
- ynom != 0 && ydenom != 0)
+ if (! strcmp (mime_type, "image/jpeg"))
+ {
+ for (j = 0; j < G_N_ELEMENTS (tiff_tags); j++)
{
- gdouble xresolution = (gdouble) xnom / (gdouble) xdenom;
- gdouble yresolution = (gdouble) ynom / (gdouble) ydenom;
-
- if (exif_unit == 3)
+ if (g_str_has_prefix (tag, tiff_tags[j]))
{
- xresolution *= 2.54;
- yresolution *= 2.54;
+ return FALSE;
}
-
- if (xresolution >= GIMP_MIN_RESOLUTION &&
- xresolution <= GIMP_MAX_RESOLUTION &&
- yresolution >= GIMP_MIN_RESOLUTION &&
- yresolution <= GIMP_MAX_RESOLUTION)
- {
- if (xres)
- *xres = xresolution;
-
- if (yres)
- *yres = yresolution;
-
- if (unit)
- {
- if (exif_unit == 3)
- *unit = GIMP_UNIT_MM;
- else
- *unit = GIMP_UNIT_INCH;
- }
-
- return TRUE;
- }
}
}
-
- return FALSE;
-}
-
-/**
- * gimp_metadata_set_resolution:
- * @metadata: A #GimpMetadata instance.
- * @xres: The image's X Resolution, in ppi
- * @yres: The image's Y Resolution, in ppi
- * @unit: The image's unit
- *
- * Sets Exif.Image.XResolution, Exif.Image.YResolution and
- * Exif.Image.ResolutionUnit of @metadata.
- *
- * Since: 2.10
- */
-void
-gimp_metadata_set_resolution (GimpMetadata *metadata,
- gdouble xres,
- gdouble yres,
- GimpUnit unit)
-{
- gchar buffer[32];
- gint exif_unit;
- gint factor;
-
- 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;
- }
-
- for (factor = 1; factor <= 100 /* arbitrary */; factor++)
+ else if (! strcmp (mime_type, "image/tiff"))
{
- if (fabs (xres * factor - ROUND (xres * factor)) < 0.01 &&
- fabs (yres * factor - ROUND (yres * factor)) < 0.01)
- break;
+ for (j = 0; j < G_N_ELEMENTS (jpeg_tags); j++)
+ {
+ if (g_str_has_prefix (tag, jpeg_tags[j]))
+ {
+ return FALSE;
+ }
+ }
}
- gexiv2_metadata_set_exif_tag_rational (metadata,
- "Exif.Image.XResolution",
- ROUND (xres * factor), factor);
-
- gexiv2_metadata_set_exif_tag_rational (metadata,
- "Exif.Image.YResolution",
- ROUND (yres * factor), factor);
-
- g_snprintf (buffer, sizeof (buffer), "%d", exif_unit);
- gexiv2_metadata_set_tag_string (metadata, "Exif.Image.ResolutionUnit", buffer);
+ return TRUE;
}
/**
* gimp_metadata_get_colorspace:
- * @metadata: A #GimpMetadata instance.
+ * @metadata: Ab #GimpMetadata instance.
*
* Returns values based on Exif.Photo.ColorSpace, Xmp.exif.ColorSpace,
* Exif.Iop.InteroperabilityIndex, Exif.Nikon3.ColorSpace,
@@ -917,22 +2952,28 @@ gimp_metadata_set_resolution (GimpMetadata *metadata,
GimpMetadataColorspace
gimp_metadata_get_colorspace (GimpMetadata *metadata)
{
- glong exif_cs = -1;
+ glong exif_cs = -1;
+ GimpAttribute *attribute = NULL;
+ GValue val;
- g_return_val_if_fail (GEXIV2_IS_METADATA (metadata),
+ g_return_val_if_fail (IS_GIMP_METADATA (metadata),
GIMP_METADATA_COLORSPACE_UNSPECIFIED);
/* the logic here was mostly taken from darktable and libkexiv2 */
- if (gexiv2_metadata_has_tag (metadata, "Exif.Photo.ColorSpace"))
+ attribute = gimp_metadata_get_attribute (metadata, "Exif.Photo.ColorSpace");
+ if ( ! attribute)
{
- exif_cs = gexiv2_metadata_get_tag_long (metadata,
- "Exif.Photo.ColorSpace");
+ attribute = gimp_metadata_get_attribute (metadata, "Xmp.exif.ColorSpace");
}
- else if (gexiv2_metadata_has_tag (metadata, "Xmp.exif.ColorSpace"))
+
+ if (attribute)
{
- exif_cs = gexiv2_metadata_get_tag_long (metadata,
- "Xmp.exif.ColorSpace");
+ if (gimp_attribute_get_value_type (attribute) == TYPE_LONG || gimp_attribute_get_value_type
(attribute) == TYPE_SLONG)
+ {
+ val = gimp_attribute_get_value (attribute);
+ exif_cs = g_value_get_long (&val);
+ }
}
if (exif_cs == 0x01)
@@ -949,8 +2990,9 @@ gimp_metadata_get_colorspace (GimpMetadata *metadata)
{
gchar *iop_index;
- iop_index = gexiv2_metadata_get_tag_string (metadata,
- "Exif.Iop.InteroperabilityIndex");
+ attribute = gimp_metadata_get_attribute (metadata, "Exif.Iop.InteroperabilityIndex");
+ if (attribute)
+ iop_index = gimp_attribute_get_string (attribute);
if (! g_strcmp0 (iop_index, "R03"))
{
@@ -968,37 +3010,45 @@ gimp_metadata_get_colorspace (GimpMetadata *metadata)
g_free (iop_index);
}
- if (gexiv2_metadata_has_tag (metadata, "Exif.Nikon3.ColorSpace"))
+ attribute = gimp_metadata_get_attribute (metadata, "Exif.Nikon3.ColorSpace");
+ if (attribute)
{
- glong nikon_cs;
-
- nikon_cs = gexiv2_metadata_get_tag_long (metadata,
- "Exif.Nikon3.ColorSpace");
-
- if (nikon_cs == 0x01)
+ if (gimp_attribute_get_value_type (attribute) == TYPE_LONG || gimp_attribute_get_value_type
(attribute) == TYPE_SLONG)
{
- return GIMP_METADATA_COLORSPACE_SRGB;
- }
- else if (nikon_cs == 0x02)
- {
- return GIMP_METADATA_COLORSPACE_ADOBERGB;
+ glong nikon_cs;
+
+ val = gimp_attribute_get_value (attribute);
+ nikon_cs = g_value_get_long (&val);
+
+ if (nikon_cs == 0x01)
+ {
+ return GIMP_METADATA_COLORSPACE_SRGB;
+ }
+ else if (nikon_cs == 0x02)
+ {
+ return GIMP_METADATA_COLORSPACE_ADOBERGB;
+ }
}
}
- if (gexiv2_metadata_has_tag (metadata, "Exif.Canon.ColorSpace"))
+ attribute = gimp_metadata_get_attribute (metadata, "Exif.Canon.ColorSpace");
+ if (attribute)
{
- glong canon_cs;
-
- canon_cs = gexiv2_metadata_get_tag_long (metadata,
- "Exif.Canon.ColorSpace");
-
- if (canon_cs == 0x01)
+ if (gimp_attribute_get_value_type (attribute) == TYPE_LONG || gimp_attribute_get_value_type
(attribute) == TYPE_SLONG)
{
- return GIMP_METADATA_COLORSPACE_SRGB;
- }
- else if (canon_cs == 0x02)
- {
- return GIMP_METADATA_COLORSPACE_ADOBERGB;
+ glong canon_cs;
+
+ val = gimp_attribute_get_value (attribute);
+ canon_cs = g_value_get_long (&val);
+
+ if (canon_cs == 0x01)
+ {
+ return GIMP_METADATA_COLORSPACE_SRGB;
+ }
+ else if (canon_cs == 0x02)
+ {
+ return GIMP_METADATA_COLORSPACE_ADOBERGB;
+ }
}
}
@@ -1010,201 +3060,295 @@ gimp_metadata_get_colorspace (GimpMetadata *metadata)
}
/**
- * gimp_metadata_set_colorspace:
- * @metadata: A #GimpMetadata instance.
- * @colorspace: The color space.
+ * gimp_metadata_get_resolution:
+ * @metadata: A #GimpMetadata instance.
+ * @xres: Return location for the X Resolution, in ppi
+ * @yres: Return location for the Y Resolution, in ppi
+ * @unit: Return location for the unit unit
*
- * Sets Exif.Photo.ColorSpace, Xmp.exif.ColorSpace,
- * Exif.Iop.InteroperabilityIndex, Exif.Nikon3.ColorSpace,
- * Exif.Canon.ColorSpace of @metadata.
+ * Returns values based on Exif.Image.XResolution,
+ * Exif.Image.YResolution and Exif.Image.ResolutionUnit of @metadata.
*
- * Since: 2.10
+ * Return value: %TRUE on success, %FALSE otherwise.
+ *
+ * Since: GIMP 2.10
*/
-void
-gimp_metadata_set_colorspace (GimpMetadata *metadata,
- GimpMetadataColorspace colorspace)
+gboolean
+gimp_metadata_get_resolution (GimpMetadata *metadata,
+ gdouble *xres,
+ gdouble *yres,
+ GimpUnit *unit)
{
- g_return_if_fail (GEXIV2_IS_METADATA (metadata));
+ GimpAttribute *x_attribute;
+ GimpAttribute *y_attribute;
+ GimpAttribute *res_attribute;
- switch (colorspace)
- {
- case GIMP_METADATA_COLORSPACE_UNSPECIFIED:
- gexiv2_metadata_clear_tag (metadata, "Exif.Photo.ColorSpace");
- gexiv2_metadata_clear_tag (metadata, "Xmp.exif.ColorSpace");
- gexiv2_metadata_clear_tag (metadata, "Exif.Iop.InteroperabilityIndex");
- gexiv2_metadata_clear_tag (metadata, "Exif.Nikon3.ColorSpace");
- gexiv2_metadata_clear_tag (metadata, "Exif.Canon.ColorSpace");
- break;
+ gint xnom, xdenom;
+ gint ynom, ydenom;
+ gint exif_unit = 2;
- case GIMP_METADATA_COLORSPACE_UNCALIBRATED:
- gexiv2_metadata_set_tag_long (metadata, "Exif.Photo.ColorSpace", 0xffff);
- if (gexiv2_metadata_has_tag (metadata, "Xmp.exif.ColorSpace"))
- gexiv2_metadata_set_tag_long (metadata, "Xmp.exif.ColorSpace", 0xffff);
- gexiv2_metadata_clear_tag (metadata, "Exif.Iop.InteroperabilityIndex");
- gexiv2_metadata_clear_tag (metadata, "Exif.Nikon3.ColorSpace");
- gexiv2_metadata_clear_tag (metadata, "Exif.Canon.ColorSpace");
- break;
+ g_return_val_if_fail (IS_GIMP_METADATA (metadata), FALSE);
- case GIMP_METADATA_COLORSPACE_SRGB:
- gexiv2_metadata_set_tag_long (metadata, "Exif.Photo.ColorSpace", 0x01);
+ x_attribute = gimp_metadata_get_attribute (metadata, "Exif.Image.XResolution");
+ y_attribute = gimp_metadata_get_attribute (metadata, "Exif.Image.YResolution");
+ res_attribute = gimp_metadata_get_attribute (metadata, "Exif.Image.ResolutionUnit");
- if (gexiv2_metadata_has_tag (metadata, "Xmp.exif.ColorSpace"))
- gexiv2_metadata_set_tag_long (metadata, "Xmp.exif.ColorSpace", 0x01);
+ if (x_attribute)
+ {
+ GValue value;
+ Rational *rats;
+ gint *_nom;
+ gint *_denom;
+ gint l;
- if (gexiv2_metadata_has_tag (metadata, "Exif.Iop.InteroperabilityIndex"))
- gexiv2_metadata_set_tag_string (metadata,
- "Exif.Iop.InteroperabilityIndex", "R98");
+ value = gimp_attribute_get_value (x_attribute);
- if (gexiv2_metadata_has_tag (metadata, "Exif.Nikon3.ColorSpace"))
- gexiv2_metadata_set_tag_long (metadata, "Exif.Nikon3.ColorSpace", 0x01);
+ rats = g_value_get_boxed (&value);
- if (gexiv2_metadata_has_tag (metadata, "Exif.Canon.ColorSpace"))
- gexiv2_metadata_set_tag_long (metadata, "Exif.Canon.ColorSpace", 0x01);
- break;
+ rational_to_int (rats, &_nom, &_denom, &l);
- case GIMP_METADATA_COLORSPACE_ADOBERGB:
- gexiv2_metadata_set_tag_long (metadata, "Exif.Photo.ColorSpace", 0x02);
+ if (l > 0)
+ {
+ xnom = _nom[0];
+ xdenom = _denom[0];
+ }
+ rational_free (rats);
+ }
+ else
+ return FALSE;
- if (gexiv2_metadata_has_tag (metadata, "Xmp.exif.ColorSpace"))
- gexiv2_metadata_set_tag_long (metadata, "Xmp.exif.ColorSpace", 0x02);
+ if (y_attribute)
+ {
+ GValue value;
+ Rational *rats;
+ gint *_nom;
+ gint *_denom;
+ gint l;
- if (gexiv2_metadata_has_tag (metadata, "Exif.Iop.InteroperabilityIndex"))
- gexiv2_metadata_set_tag_string (metadata,
- "Exif.Iop.InteroperabilityIndex", "R03");
+ value = gimp_attribute_get_value (y_attribute);
- if (gexiv2_metadata_has_tag (metadata, "Exif.Nikon3.ColorSpace"))
- gexiv2_metadata_set_tag_long (metadata, "Exif.Nikon3.ColorSpace", 0x02);
+ rats = g_value_get_boxed (&value);
- if (gexiv2_metadata_has_tag (metadata, "Exif.Canon.ColorSpace"))
- gexiv2_metadata_set_tag_long (metadata, "Exif.Canon.ColorSpace", 0x02);
- break;
- }
-}
+ rational_to_int (rats, &_nom, &_denom, &l);
-/**
- * gimp_metadata_is_tag_supported:
- * @tag: A metadata tag name
- * @mime_type: A mime type
- *
- * Returns whether @tag is supported in a file of type @mime_type.
- *
- * Return value: %TRUE if the @tag supported with @mime_type, %FALSE otherwise.
- *
- * Since: 2.10
- */
-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]))
+ if (l > 0)
{
- return FALSE;
+ ynom = _nom[0];
+ ydenom = _denom[0];
}
+ rational_free (rats);
}
+ else
+ return FALSE;
- if (! strcmp (mime_type, "image/jpeg"))
+ if (res_attribute)
{
- for (j = 0; j < G_N_ELEMENTS (tiff_tags); j++)
- {
- if (g_str_has_prefix (tag, tiff_tags[j]))
- {
- return FALSE;
- }
- }
+ GValue value = gimp_attribute_get_value (res_attribute);
+
+ exif_unit = (gint) g_value_get_uint (&value);
}
- else if (! strcmp (mime_type, "image/tiff"))
+
+ if (xnom != 0 && xdenom != 0 &&
+ ynom != 0 && ydenom != 0)
{
- for (j = 0; j < G_N_ELEMENTS (jpeg_tags); j++)
+ gdouble xresolution = (gdouble) xnom / (gdouble) xdenom;
+ gdouble yresolution = (gdouble) ynom / (gdouble) ydenom;
+
+ if (exif_unit == 3)
{
- if (g_str_has_prefix (tag, jpeg_tags[j]))
+ xresolution *= 2.54;
+ yresolution *= 2.54;
+ }
+
+ if (xresolution >= GIMP_MIN_RESOLUTION &&
+ xresolution <= GIMP_MAX_RESOLUTION &&
+ yresolution >= GIMP_MIN_RESOLUTION &&
+ yresolution <= GIMP_MAX_RESOLUTION)
+ {
+ if (xres)
+ *xres = xresolution;
+
+ if (yres)
+ *yres = yresolution;
+
+ if (unit)
{
- return FALSE;
+ if (exif_unit == 3)
+ *unit = GIMP_UNIT_MM;
+ else
+ *unit = GIMP_UNIT_INCH;
}
+
+ return TRUE;
}
}
-
- return TRUE;
+ return FALSE;
}
+/**
+ * gimp_metadata_set_bits_per_sample:
+ * @metadata: A #GimpMetadata instance.
+ * @bps: Bytes per pixel, per component
+ *
+ * Sets Exif.Image.BitsPerSample on @metadata.
+ *
+ * Since: GIMP 2.10
+ */
+void
+gimp_metadata_set_bits_per_sample (GimpMetadata *metadata,
+ gint bps)
+{
+ gchar buffer[32];
-/* private functions */
+ g_return_if_fail ( (metadata));
-static GQuark
-gimp_metadata_error_quark (void)
+ g_snprintf (buffer, sizeof (buffer), "%d", bps);
+ gimp_metadata_new_attribute (metadata, "Exif.Image.BitsPerSample", buffer, TYPE_SHORT);
+}
+
+/**
+ * gimp_metadata_set_pixel_size:
+ * @metadata: A #GimpMetadata instance.
+ * @width: Width in pixels
+ * @height: Height in pixels
+ *
+ * Sets Exif.Image.ImageWidth and Exif.Image.ImageLength on @metadata.
+ *
+ * Since: GIMP 2.10
+ */
+void
+gimp_metadata_set_pixel_size (GimpMetadata *metadata,
+ gint width,
+ gint height)
{
- static GQuark quark = 0;
+ gchar buffer[32];
- if (G_UNLIKELY (quark == 0))
- quark = g_quark_from_static_string ("gimp-metadata-error-quark");
+ g_return_if_fail (IS_GIMP_METADATA (metadata));
- return quark;
+ g_snprintf (buffer, sizeof (buffer), "%d", width);
+ gimp_metadata_new_attribute (metadata, "Exif.Image.ImageWidth", buffer, TYPE_LONG);
+
+ g_snprintf (buffer, sizeof (buffer), "%d", height);
+ gimp_metadata_new_attribute (metadata, "Exif.Image.ImageLength", buffer, TYPE_LONG);
}
-static void
-gimp_metadata_add (GimpMetadata *src,
- GimpMetadata *dest)
+/**
+ * gimp_metadata_set_colorspace:
+ * @metadata: A #GimpMetadata instance.
+ * @colorspace: The color space.
+ *
+ * Sets Exif.Photo.ColorSpace, Xmp.exif.ColorSpace,
+ * Exif.Iop.InteroperabilityIndex, Exif.Nikon3.ColorSpace,
+ * Exif.Canon.ColorSpace of @metadata.
+ *
+ * Since: 2.10
+ */
+void
+gimp_metadata_set_colorspace (GimpMetadata *metadata,
+ GimpMetadataColorspace colorspace)
{
- gchar *value;
- gint i;
+ g_return_if_fail (IS_GIMP_METADATA (metadata));
- if (gexiv2_metadata_get_supports_exif (src) &&
- gexiv2_metadata_get_supports_exif (dest))
+ gimp_metadata_remove_attribute (metadata, "Exif.Photo.ColorSpace");
+ gimp_metadata_remove_attribute (metadata, "Xmp.exif.ColorSpace");
+ gimp_metadata_remove_attribute (metadata, "Exif.Iop.InteroperabilityIndex");
+
+ switch (colorspace)
{
- gchar **exif_data = gexiv2_metadata_get_exif_tags (src);
+ case GIMP_METADATA_COLORSPACE_UNSPECIFIED:
+ gimp_metadata_remove_attribute (metadata, "Exif.Nikon3.ColorSpace");
+ gimp_metadata_remove_attribute (metadata, "Exif.Canon.ColorSpace");
+ break;
- if (exif_data)
+ case GIMP_METADATA_COLORSPACE_UNCALIBRATED:
+ gimp_metadata_new_attribute (metadata, "Exif.Photo.ColorSpace", "0xffff", TYPE_LONG);
+ gimp_metadata_new_attribute (metadata, "Xmp.exif.ColorSpace", "0xffff", TYPE_LONG);
+ break;
+
+ case GIMP_METADATA_COLORSPACE_SRGB:
+ gimp_metadata_new_attribute (metadata, "Exif.Photo.ColorSpace", "0x01", TYPE_LONG);
+ gimp_metadata_new_attribute (metadata, "Xmp.exif.ColorSpace", "0x01", TYPE_LONG);
+ gimp_metadata_new_attribute (metadata, "Exif.Iop.InteroperabilityIndex", "R98", TYPE_ASCII);
+ if (gimp_metadata_has_attribute (metadata, "Exif.Nikon3.ColorSpace"))
{
- 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);
- }
+ gimp_metadata_remove_attribute (metadata, "Exif.Nikon3.ColorSpace");
+ gimp_metadata_new_attribute (metadata, "Exif.Nikon3.ColorSpace", "0x01", TYPE_LONG);
+ }
+ if (gimp_metadata_has_attribute (metadata, "Exif.Canon.ColorSpace"))
+ {
+ gimp_metadata_remove_attribute (metadata, "Exif.Canon.ColorSpace");
+ gimp_metadata_new_attribute (metadata, "Exif.Canon.ColorSpace", "0x01", TYPE_LONG);
+ }
+ break;
- g_strfreev (exif_data);
+ case GIMP_METADATA_COLORSPACE_ADOBERGB:
+ gimp_metadata_new_attribute (metadata, "Exif.Photo.ColorSpace", "0x02", TYPE_LONG);
+ gimp_metadata_new_attribute (metadata, "Xmp.exif.ColorSpace", "0x02", TYPE_LONG);
+ gimp_metadata_new_attribute (metadata, "Exif.Iop.InteroperabilityIndex", "R03", TYPE_ASCII);
+ if (gimp_metadata_has_attribute (metadata, "Exif.Nikon3.ColorSpace"))
+ {
+ gimp_metadata_remove_attribute (metadata, "Exif.Nikon3.ColorSpace");
+ gimp_metadata_new_attribute (metadata, "Exif.Nikon3.ColorSpace", "0x02", TYPE_LONG);
+ }
+ if (gimp_metadata_has_attribute (metadata, "Exif.Canon.ColorSpace"))
+ {
+ gimp_metadata_remove_attribute (metadata, "Exif.Canon.ColorSpace");
+ gimp_metadata_new_attribute (metadata, "Exif.Canon.ColorSpace", "0x02", TYPE_LONG);
}
+ break;
}
+}
- if (gexiv2_metadata_get_supports_xmp (src) &&
- gexiv2_metadata_get_supports_xmp (dest))
- {
- gchar **xmp_data = gexiv2_metadata_get_xmp_tags (src);
+/**
+ * gimp_metadata_set_resolution:
+ * @metadata: A #GimpMetadata instance.
+ * @xres: The image's X Resolution, in ppi
+ * @yres: The image's Y Resolution, in ppi
+ * @unit: The image's unit
+ *
+ * Sets Exif.Image.XResolution, Exif.Image.YResolution and
+ * Exif.Image.ResolutionUnit @metadata.
+ *
+ * Since: GIMP 2.10
+ */
+void
+gimp_metadata_set_resolution (GimpMetadata *metadata,
+ gdouble xres,
+ gdouble yres,
+ GimpUnit unit)
+{
+ gchar buffer[64];
+ gint exif_unit;
+ gint factor;
- 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_return_if_fail (IS_GIMP_METADATA (metadata));
- g_strfreev (xmp_data);
- }
+ if (gimp_unit_is_metric (unit))
+ {
+ xres /= 2.54;
+ yres /= 2.54;
+
+ exif_unit = 3;
+ }
+ else
+ {
+ exif_unit = 2;
}
- if (gexiv2_metadata_get_supports_iptc (src) &&
- gexiv2_metadata_get_supports_iptc (dest))
+ for (factor = 1; factor <= 100 /* arbitrary */; factor++)
{
- gchar **iptc_data = gexiv2_metadata_get_iptc_tags (src);
+ if (fabs (xres * factor - ROUND (xres * factor)) < 0.01 &&
+ fabs (yres * factor - ROUND (yres * factor)) < 0.01)
+ break;
+ }
- 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_snprintf (buffer, sizeof (buffer), "%d/%d", ROUND (xres * factor), factor);
+ gimp_metadata_new_attribute (metadata, "Exif.Image.XResolution", buffer, TYPE_RATIONAL);
- g_strfreev (iptc_data);
- }
- }
+ g_snprintf (buffer, sizeof (buffer), "%d/%d", ROUND (yres * factor), factor);
+ gimp_metadata_new_attribute (metadata, "Exif.Image.YResolution", buffer, TYPE_RATIONAL);
+
+ g_snprintf (buffer, sizeof (buffer), "%d", exif_unit);
+ gimp_metadata_new_attribute (metadata, "Exif.Image.ResolutionUnit", buffer, TYPE_SHORT);
}
+
diff --git a/libgimpbase/gimpmetadata.h b/libgimpbase/gimpmetadata.h
index 3a6b27c..cac1d8e 100644
--- a/libgimpbase/gimpmetadata.h
+++ b/libgimpbase/gimpmetadata.h
@@ -1,29 +1,19 @@
-/* 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__
+#ifndef __GIMPMETADATA_H__
+#define __GIMPMETADATA_H__
G_BEGIN_DECLS
+#include <gexiv2/gexiv2.h>
+
+#define TYPE_GIMP_METADATA (gimp_metadata_get_type ())
+#define GIMP_METADATA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_GIMP_METADATA, GimpMetadata))
+#define GIMP_METADATA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_GIMP_METADATA, GimpMetadataClass))
+#define IS_GIMP_METADATA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_GIMP_METADATA))
+#define IS_GIMP_METADATA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_GIMP_METADATA))
+#define GIMP_METADATA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_GIMP_METADATA,
GimpMetadataClass))
+#define GIMP_METADATA_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), TYPE_GIMP_METADATA,
GimpMetadataPrivate))
+
typedef enum
{
GIMP_METADATA_LOAD_COMMENT = 1 << 0,
@@ -52,51 +42,73 @@ typedef enum
GIMP_METADATA_COLORSPACE_ADOBERGB
} GimpMetadataColorspace;
+GType gimp_metadata_get_type (void)
G_GNUC_CONST;
+GimpMetadata * gimp_metadata_new (void);
+gint gimp_metadata_size
(GimpMetadata *metadata);
+GimpMetadata * gimp_metadata_duplicate
(GimpMetadata *metadata);
+GHashTable * gimp_metadata_get_table
(GimpMetadata *metadata);
+void gimp_metadata_add_attribute
(GimpMetadata *metadata,
+
GimpAttribute *attribute);
+GimpAttribute * gimp_metadata_get_attribute
(GimpMetadata *metadata,
+ const
gchar *name);
+gboolean gimp_metadata_remove_attribute
(GimpMetadata *metadata,
+ const
gchar *name);
+gboolean gimp_metadata_has_attribute
(GimpMetadata *metadata,
+ const
gchar *name);
+gboolean gimp_metadata_new_attribute
(GimpMetadata *metadata,
+ const
gchar *name,
+ gchar
*value,
+
GimpAttributeValueType type);
+gchar * gimp_metadata_serialize
(GimpMetadata *metadata);
+GimpMetadata * gimp_metadata_deserialize (const
gchar *xml);
+void gimp_metadata_print
(GimpMetadata *metadata);
+const gchar * gimp_metadata_to_xmp_packet
(GimpMetadata *metadata,
+ const
gchar *mime_type);
+gboolean gimp_metadata_has_tag_type
(GimpMetadata *metadata,
+
GimpAttributeTagType tag_type);
+GList * gimp_metadata_iter_init
(GimpMetadata *metadata,
+ GList
**iter);
+gboolean gimp_metadata_iter_next
(GimpMetadata *metadata,
+
GimpAttribute **attribute,
+ GList
**prev);
+gboolean gimp_metadata_save_to_file
(GimpMetadata *metadata,
+ GFile
*file,
+ GError
**error);
+GimpMetadata * gimp_metadata_load_from_file (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);
+gboolean gimp_metadata_is_tag_supported (const
gchar *tag,
+ const
gchar *mime_type);
+GimpMetadataColorspace gimp_metadata_get_colorspace
(GimpMetadata *metadata);
+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);
+void gimp_metadata_set_bits_per_sample
(GimpMetadata *metadata,
+ gint
bits_per_sample);
+void gimp_metadata_set_pixel_size
(GimpMetadata *metadata,
+ gint
width,
+ gint
height);
+void gimp_metadata_set_colorspace
(GimpMetadata *metadata,
+
GimpMetadataColorspace colorspace);
+//GimpMetadata * gimp_metadata_from_gexiv2metadata
(GimpMetadata *metadata,
+//
GimpMetadata *gexivdata);
+//void gimp_metadata_to_gexiv2metadata
(GimpMetadata *metadata,
+//
GimpMetadata *gexivdata,
+// const
gchar *mime_type);
-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);
-
-GimpMetadataColorspace
- gimp_metadata_get_colorspace (GimpMetadata *metadata);
-void gimp_metadata_set_colorspace (GimpMetadata *metadata,
- GimpMetadataColorspace colorspace);
-
-gboolean gimp_metadata_is_tag_supported (const gchar *tag,
- const gchar *mime_type);
G_END_DECLS
-#endif /* __GIMP_METADATA_H__ */
+#endif
diff --git a/libgimpbase/gimprational.c b/libgimpbase/gimprational.c
new file mode 100644
index 0000000..93d6754
--- /dev/null
+++ b/libgimpbase/gimprational.c
@@ -0,0 +1,301 @@
+/*
+ * gimprational.c
+ *
+ * Created on: 19.09.2014
+ * Author: kuhse
+ */
+
+#include <stdlib.h>
+
+#include "gimprational.h"
+
+
+static gpointer rational_copy (gpointer data);
+static gpointer srational_copy (gpointer data);
+
+
+G_DEFINE_BOXED_TYPE (Rational, rational,
+ rational_copy,
+ rational_free)
+G_DEFINE_BOXED_TYPE (SRational, srational,
+ srational_copy,
+ srational_free)
+
+
+/**
+ * string_to_rational:
+ *
+ * @string: a #gchar array
+ * @rational: a pointer to a #Rational struct
+ *
+ * converts a string, representing one/more rational values into
+ * a #Rational struct.
+ *
+ * Since: 2.10
+ */
+void
+string_to_rational (gchar *string, Rational **rational)
+{
+ gchar **nom;
+ gchar **rats;
+ gint count;
+ gint i;
+ Rational *rval;
+ GArray *rational_array;
+
+ rats = g_strsplit (string, " ", -1);
+
+ count = 0;
+ while (rats[count])
+ count++;
+
+ rational_array = g_array_new (TRUE, TRUE, sizeof (RationalValue));
+
+ for (i = 0; i < count; i++)
+ {
+ RationalValue or;
+
+ nom = g_strsplit (rats[i], "/", 2);
+
+ if (nom[0] && nom[1])
+ {
+ or.nom = (guint) atoi (nom [0]);
+ or.denom = (guint) atoi (nom [1]);
+ }
+ else
+ count--;
+
+ rational_array = g_array_append_val (rational_array, or);
+
+ g_strfreev (nom);
+ }
+
+ g_strfreev (rats);
+
+ rval = g_slice_new (Rational);
+
+ rval->rational_array = rational_array;
+ rval->length = count;
+
+ *rational = rval;
+}
+
+/**
+ * rational_to_string:
+ *
+ * @rational : a pointer to a @Rational struct
+ * @nom : a pointer to a #gint array
+ * @denom : a pointer to a #gint array
+ * @length : a pointer to a #gint
+ *
+ * converts a @rational to nom/denum gchar arrays
+ *
+ * Since: 2.10
+ */
+void
+rational_to_int (Rational *rational, gint **nom, gint **denom, gint *length)
+{
+ gint i;
+ gint *_nom;
+ gint *_denom;
+
+ g_return_if_fail (rational != NULL);
+
+ _nom = g_new (gint, rational->length);
+ _denom = g_new (gint, rational->length);
+
+ for (i = 0; i < rational->length; i++)
+ {
+ RationalValue one;
+ one = g_array_index(rational->rational_array, RationalValue, i);
+ _nom[i] = one.nom;
+ _denom[i] = one.denom;
+ }
+
+ *nom = _nom;
+ *denom = _denom;
+ *length = rational->length;
+}
+
+/**
+ * rational_copy:
+ *
+ * @data: a #gpointer to a #Rational structure
+ *
+ * copy part of the #Rational type
+ *
+ * Since: 2.10
+ */
+static gpointer
+rational_copy (gpointer data)
+{
+ struct _Rational *rational = (struct _Rational *) data;
+ struct _Rational *copy = g_slice_new (Rational);
+ gint i;
+ GArray *rlacp;
+
+ if (!data)
+ return NULL;
+
+ rlacp = g_array_new (TRUE, TRUE, sizeof (RationalValue));
+
+ for (i = 0; i < rational->length; i++)
+ {
+ RationalValue or;
+ RationalValue cor;
+
+ or = g_array_index (rational->rational_array, RationalValue, i);
+
+ cor.nom = or.nom;
+ cor.denom = or.denom;
+
+ rlacp = g_array_append_val (rlacp, cor);
+ }
+
+ copy->rational_array = rlacp;
+ copy->length = rational->length;
+
+ return (gpointer) copy;
+}
+
+/**
+ * rational_free:
+ *
+ * @data: a #gpointer to a #Rational structure
+ *
+ * free part of the #Rational type
+ *
+ * Since: 2.10
+ */
+void
+rational_free (gpointer data)
+{
+ struct _Rational *rational = (struct _Rational *) data;
+
+ if (rational)
+ {
+ if (rational->rational_array)
+ g_array_free (rational->rational_array, TRUE);
+ rational->length = 0;
+ }
+}
+
+/**
+ * string_to_srational:
+ *
+ * @string: a #gchar array
+ * @srational: a pointer to a #Rational struct
+ *
+ * converts a string, representing one/more srational values into
+ * a #SRational struct.
+ *
+ * Since: 2.10
+ */
+void
+string_to_srational (gchar *string, SRational **srational)
+{
+ gchar **nom;
+ gchar **srats;
+ gint count;
+ gint i;
+ SRational *srval;
+ GArray *srational_array;
+
+ srats = g_strsplit (string, " ", -1);
+
+ count = 0;
+ while (srats[count])
+ count++;
+
+ srational_array = g_array_new (TRUE, TRUE, sizeof (SRationalValue));
+
+ for (i = 0; i < count; i++)
+ {
+ SRationalValue or;
+
+ nom = g_strsplit (srats[i], "/", 2);
+
+ if (nom[0] && nom[1])
+ {
+ or.nom = (gint) atoi (nom [0]);
+ or.denom = (guint) atoi (nom [1]);
+ }
+ else
+ count--;
+
+ srational_array = g_array_append_val (srational_array, or);
+
+ g_strfreev (nom);
+ }
+
+ g_strfreev (srats);
+
+ srval = g_slice_new (SRational);
+
+ srval->srational_array = srational_array;
+ srval->length = count;
+
+ *srational = srval;
+}
+/**
+ * srational_copy:
+ *
+ * @data: a #gpointer to a #SRational structure
+ *
+ * copy part of the #SRational type
+ *
+ * Since: 2.10
+ */
+static gpointer
+srational_copy (gpointer data)
+{
+ struct _SRational *srational = (struct _SRational *) data;
+ struct _SRational *copy = g_slice_new (SRational);
+ gint i;
+ GArray *rlacp;
+
+ if (!data)
+ return NULL;
+
+ rlacp = g_array_new (TRUE, TRUE, sizeof (SRationalValue));
+
+ for (i = 0; i < srational->length; i++)
+ {
+ SRationalValue or;
+ SRationalValue cor;
+
+ or = g_array_index (srational->srational_array, SRationalValue, i);
+
+ cor.nom = or.nom;
+ cor.denom = or.denom;
+
+ rlacp = g_array_append_val (rlacp, cor);
+ }
+
+ copy->srational_array = rlacp;
+ copy->length = srational->length;
+
+ return (gpointer) copy;
+}
+
+/**
+ * srational_free:
+ *
+ * @data: a #gpointer to a #SRational structure
+ *
+ * free part of the #SRational type
+ *
+ * Since: 2.10
+ */
+void
+srational_free (gpointer data)
+{
+ struct _SRational *srational = (struct _SRational *) data;
+
+ if (srational)
+ {
+ if (srational->srational_array)
+ g_array_free (srational->srational_array, TRUE);
+ srational->length = 0;
+ }
+}
diff --git a/libgimpbase/gimprational.h b/libgimpbase/gimprational.h
new file mode 100644
index 0000000..ab020c8
--- /dev/null
+++ b/libgimpbase/gimprational.h
@@ -0,0 +1,68 @@
+/*
+ * gimprational.h
+ *
+ * Created on: 19.09.2014
+ * Author: kuhse
+ */
+
+#ifndef __GIMPRATIONAL_H__
+#define __GIMPRATIONAL_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GIMP_TYPE_RATIONAL (rational_get_type ())
+#define GIMP_TYPE_SRATIONAL (srational_get_type ())
+
+typedef struct _RationalValue RationalValue;
+
+struct _RationalValue
+{
+ guint nom;
+ guint denom;
+};
+
+typedef struct _SRationalValue SRationalValue;
+
+struct _SRationalValue
+{
+ gint nom;
+ guint denom;
+};
+
+typedef struct _Rational Rational;
+
+struct _Rational
+{
+ GArray *rational_array;
+ gint length;
+};
+
+typedef struct _SRational SRational;
+
+struct _SRational
+{
+ GArray *srational_array;
+ gint length;
+};
+
+GType rational_get_type (void) G_GNUC_CONST;
+GType srational_get_type (void) G_GNUC_CONST;
+
+void rational_free (gpointer data);
+void srational_free (gpointer data);
+
+void string_to_rational (gchar *string,
+ Rational **rational);
+void rational_to_int (Rational *rational,
+ gint **nom,
+ gint **denom,
+ gint *length);
+void string_to_srational (gchar *string,
+ SRational **srational);
+
+
+G_END_DECLS
+
+#endif /* __GIMPRATIONAL_H__ */
diff --git a/libgimpwidgets/gimpicons.c b/libgimpwidgets/gimpicons.c
index 0bec46e..2ddd6ed 100644
--- a/libgimpwidgets/gimpicons.c
+++ b/libgimpwidgets/gimpicons.c
@@ -166,6 +166,7 @@ static const GtkStockItem gimp_stock_items[] =
{ GIMP_STOCK_TOOL_PRESET, NULL, 0, 0, LIBGIMP_DOMAIN },
{ GIMP_STOCK_IMAGE, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_IMAGE_METADATA, NULL, 0, 0, LIBGIMP_DOMAIN },
{ GIMP_STOCK_LAYER, NULL, 0, 0, LIBGIMP_DOMAIN },
{ GIMP_STOCK_TEXT_LAYER, NULL, 0, 0, LIBGIMP_DOMAIN },
{ GIMP_STOCK_FLOATING_SELECTION, NULL, 0, 0, LIBGIMP_DOMAIN },
diff --git a/libgimpwidgets/gimpicons.h b/libgimpwidgets/gimpicons.h
index 4d82e5c..66f423a 100644
--- a/libgimpwidgets/gimpicons.h
+++ b/libgimpwidgets/gimpicons.h
@@ -182,6 +182,7 @@ G_BEGIN_DECLS
#define GIMP_STOCK_FLIP_VERTICAL "gimp-flip-vertical"
#define GIMP_STOCK_IMAGE "gimp-image"
+#define GIMP_STOCK_IMAGE_METADATA "gimp-image-metadata"
#define GIMP_STOCK_LAYER "gimp-layer"
#define GIMP_STOCK_TEXT_LAYER "gimp-text-layer"
#define GIMP_STOCK_FLOATING_SELECTION "gimp-floating-selection"
diff --git a/plug-ins/common/Makefile.am b/plug-ins/common/Makefile.am
index 90b048b..57f1f3f 100644
--- a/plug-ins/common/Makefile.am
+++ b/plug-ins/common/Makefile.am
@@ -52,6 +52,7 @@ libexec_PROGRAMS = \
align-layers \
animation-optimize \
animation-play \
+ attributes \
blinds \
blur \
border-average \
@@ -116,10 +117,10 @@ libexec_PROGRAMS = \
grid \
guillotine \
hot \
+ iptc \
jigsaw \
$(MAIL) \
max-rgb \
- metadata \
newsprint \
nl-filter \
oilify \
@@ -218,6 +219,24 @@ animation_play_LDADD = \
$(INTLLIBS) \
$(animation_play_RC)
+attributes_SOURCES = \
+ attributes.c
+
+attributes_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(attributes_RC)
+
blinds_SOURCES = \
blinds.c
@@ -1354,10 +1373,12 @@ hot_LDADD = \
$(INTLLIBS) \
$(hot_RC)
-jigsaw_SOURCES = \
- jigsaw.c
+iptc_CFLAGS = $(GEXIV2_CFLAGS)
-jigsaw_LDADD = \
+iptc_SOURCES = \
+ iptc.c
+
+iptc_LDADD = \
$(libgimpui) \
$(libgimpwidgets) \
$(libgimpmodule) \
@@ -1367,14 +1388,15 @@ jigsaw_LDADD = \
$(libgimpcolor) \
$(libgimpbase) \
$(GTK_LIBS) \
+ $(GEXIV2_LIBS) \
$(RT_LIBS) \
$(INTLLIBS) \
- $(jigsaw_RC)
+ $(iptc_RC)
-mail_SOURCES = \
- mail.c
+jigsaw_SOURCES = \
+ jigsaw.c
-mail_LDADD = \
+jigsaw_LDADD = \
$(libgimpui) \
$(libgimpwidgets) \
$(libgimpmodule) \
@@ -1386,12 +1408,12 @@ mail_LDADD = \
$(GTK_LIBS) \
$(RT_LIBS) \
$(INTLLIBS) \
- $(mail_RC)
+ $(jigsaw_RC)
-max_rgb_SOURCES = \
- max-rgb.c
+mail_SOURCES = \
+ mail.c
-max_rgb_LDADD = \
+mail_LDADD = \
$(libgimpui) \
$(libgimpwidgets) \
$(libgimpmodule) \
@@ -1403,14 +1425,12 @@ max_rgb_LDADD = \
$(GTK_LIBS) \
$(RT_LIBS) \
$(INTLLIBS) \
- $(max_rgb_RC)
-
-metadata_CFLAGS = $(GEXIV2_CFLAGS)
+ $(mail_RC)
-metadata_SOURCES = \
- metadata.c
+max_rgb_SOURCES = \
+ max-rgb.c
-metadata_LDADD = \
+max_rgb_LDADD = \
$(libgimpui) \
$(libgimpwidgets) \
$(libgimpmodule) \
@@ -1420,10 +1440,9 @@ metadata_LDADD = \
$(libgimpcolor) \
$(libgimpbase) \
$(GTK_LIBS) \
- $(GEXIV2_LIBS) \
$(RT_LIBS) \
$(INTLLIBS) \
- $(metadata_RC)
+ $(max_rgb_RC)
newsprint_SOURCES = \
newsprint.c
diff --git a/plug-ins/common/file-jp2-load.c b/plug-ins/common/file-jp2-load.c
index 0facb3c..2803617 100644
--- a/plug-ins/common/file-jp2-load.c
+++ b/plug-ins/common/file-jp2-load.c
@@ -57,6 +57,7 @@ static void run (const gchar *name,
gint *nreturn_vals,
GimpParam **return_vals);
static gint32 load_image (const gchar *filename,
+ gint32 *layer_ID,
GError **error);
static gboolean load_icc_profile (jas_image_t *jas_image,
gint image_ID,
@@ -126,6 +127,7 @@ run (const gchar *name,
GimpPDBStatusType status = GIMP_PDB_SUCCESS;
gint image_ID;
GError *error = NULL;
+ gint32 layer_ID;
run_mode = param[0].data.d_int32;
@@ -154,7 +156,7 @@ run (const gchar *name,
break;
}
- image_ID = load_image (param[1].data.d_string, &error);
+ image_ID = load_image (param[1].data.d_string, &layer_ID, &error);
if (image_ID != -1)
{
@@ -168,7 +170,7 @@ run (const gchar *name,
{
GimpMetadataLoadFlags flags = GIMP_METADATA_LOAD_ALL;
- gimp_image_metadata_load_finish (image_ID, "image/jp2",
+ gimp_image_metadata_load_finish (image_ID, layer_ID, "image/jp2",
metadata, flags,
interactive);
@@ -203,13 +205,13 @@ run (const gchar *name,
static gint32
load_image (const gchar *filename,
+ gint32 *layer_ID,
GError **error)
{
gint fd;
jas_stream_t *stream;
gint32 image_ID = -1;
jas_image_t *image;
- gint32 layer_ID;
GimpImageType image_type;
GimpImageBaseType base_type;
gint width;
diff --git a/plug-ins/common/file-png.c b/plug-ins/common/file-png.c
index 6efb7f1..12c9030 100644
--- a/plug-ins/common/file-png.c
+++ b/plug-ins/common/file-png.c
@@ -140,6 +140,7 @@ static void run (const gchar *name,
GimpParam **return_vals);
static gint32 load_image (const gchar *filename,
+ gint32 *layer_ID,
gboolean interactive,
gboolean *resolution_loaded,
GError **error);
@@ -147,6 +148,7 @@ static gboolean save_image (const gchar *filename,
gint32 image_ID,
gint32 drawable_ID,
gint32 orig_image_ID,
+ gint *bits_depth,
GError **error);
static int respin_cmap (png_structp pp,
@@ -416,6 +418,7 @@ run (const gchar *name,
GimpRunMode run_mode;
GimpPDBStatusType status = GIMP_PDB_SUCCESS;
gint32 image_ID;
+ gint32 layer_ID;
gint32 drawable_ID;
GError *error = NULL;
@@ -448,6 +451,7 @@ run (const gchar *name,
}
image_ID = load_image (param[1].data.d_string,
+ &layer_ID,
interactive,
&resolution_loaded,
&error);
@@ -467,7 +471,7 @@ run (const gchar *name,
if (resolution_loaded)
flags &= ~GIMP_METADATA_LOAD_RESOLUTION;
- gimp_image_metadata_load_finish (image_ID, "image/png",
+ gimp_image_metadata_load_finish (image_ID, layer_ID, "image/png",
metadata, flags,
interactive);
@@ -492,6 +496,7 @@ run (const gchar *name,
GimpMetadata *metadata;
GimpMetadataSaveFlags metadata_flags;
gint32 orig_image_ID;
+ gint bits_depth;
GimpExportReturn export = GIMP_EXPORT_CANCEL;
gboolean alpha;
@@ -525,8 +530,8 @@ run (const gchar *name,
}
metadata = gimp_image_metadata_save_prepare (orig_image_ID,
- "image/png",
- &metadata_flags);
+ "image/png",
+ &metadata_flags);
pngvals.save_exif = (metadata_flags & GIMP_METADATA_SAVE_EXIF) != 0;
pngvals.save_xmp = (metadata_flags & GIMP_METADATA_SAVE_XMP) != 0;
@@ -608,7 +613,7 @@ run (const gchar *name,
if (status == GIMP_PDB_SUCCESS)
{
if (save_image (param[3].data.d_string,
- image_ID, drawable_ID, orig_image_ID, &error))
+ image_ID, drawable_ID, orig_image_ID, &bits_depth, &error))
{
if (metadata)
{
@@ -637,10 +642,12 @@ run (const gchar *name,
metadata_flags &= ~GIMP_METADATA_SAVE_THUMBNAIL;
file = g_file_new_for_path (param[3].data.d_string);
+
gimp_image_metadata_save_finish (orig_image_ID,
"image/png",
metadata, metadata_flags,
file, NULL);
+ g_object_unref (metadata);
g_object_unref (file);
}
@@ -655,8 +662,6 @@ run (const gchar *name,
if (export == GIMP_EXPORT_EXPORT)
gimp_image_delete (image_ID);
- if (metadata)
- g_object_unref (metadata);
}
else if (strcmp (name, GET_DEFAULTS_PROC) == 0)
{
@@ -817,6 +822,7 @@ load_color_profile (png_structp pp,
*/
static gint32
load_image (const gchar *filename,
+ gint32 *layer_ID,
gboolean interactive,
gboolean *resolution_loaded,
GError **error)
@@ -1350,6 +1356,8 @@ load_image (const gchar *filename,
g_object_unref (buffer);
}
+ *layer_ID = layer;
+
return image;
}
@@ -1427,6 +1435,7 @@ save_image (const gchar *filename,
gint32 image_ID,
gint32 drawable_ID,
gint32 orig_image_ID,
+ gint *bits_depth,
GError **error)
{
gint i, k; /* Looping vars */
@@ -1500,6 +1509,8 @@ save_image (const gchar *filename,
break;
}
+ *bits_depth = bit_depth;
+
pp = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!pp)
{
diff --git a/plug-ins/common/gimprc.common b/plug-ins/common/gimprc.common
index 0ff328d..a8e6406 100644
--- a/plug-ins/common/gimprc.common
+++ b/plug-ins/common/gimprc.common
@@ -1,6 +1,7 @@
align_layers_RC = align-layers.rc.o
animation_optimize_RC = animation-optimize.rc.o
animation_play_RC = animation-play.rc.o
+attributes_RC = attributes.rc.o
blinds_RC = blinds.rc.o
blur_RC = blur.rc.o
border_average_RC = border-average.rc.o
@@ -65,10 +66,10 @@ gradient_map_RC = gradient-map.rc.o
grid_RC = grid.rc.o
guillotine_RC = guillotine.rc.o
hot_RC = hot.rc.o
+iptc_RC = iptc.rc.o
jigsaw_RC = jigsaw.rc.o
mail_RC = mail.rc.o
max_rgb_RC = max-rgb.rc.o
-metadata_RC = metadata.rc.o
newsprint_RC = newsprint.rc.o
nl_filter_RC = nl-filter.rc.o
oilify_RC = oilify.rc.o
diff --git a/plug-ins/common/plugin-defs.pl b/plug-ins/common/plugin-defs.pl
index 324e7da..4c79517 100644
--- a/plug-ins/common/plugin-defs.pl
+++ b/plug-ins/common/plugin-defs.pl
@@ -2,6 +2,7 @@
'align-layers' => { ui => 1 },
'animation-optimize' => {},
'animation-play' => { ui => 1, gegl => 1 },
+ 'attributes' => { ui => 1, gegl => 1 },
'blinds' => { ui => 1 },
'blur' => {},
'border-average' => { ui => 1, gegl => 1 },
@@ -66,10 +67,10 @@
'grid' => { ui => 1 },
'guillotine' => {},
'hot' => { ui => 1 },
+ 'iptc' => { ui => 1, libs => 'GEXIV2_LIBS', cflags => 'GEXIV2_CFLAGS' },
'jigsaw' => { ui => 1 },
'mail' => { ui => 1, optional => 1 },
'max-rgb' => { ui => 1 },
- 'metadata' => { ui => 1, libs => 'GEXIV2_LIBS', cflags => 'GEXIV2_CFLAGS' },
'newsprint' => { ui => 1 },
'nl-filter' => { ui => 1 },
'oilify' => { ui => 1 },
diff --git a/plug-ins/file-jpeg/jpeg-load.c b/plug-ins/file-jpeg/jpeg-load.c
index 6cc945f..ed1ac6b 100644
--- a/plug-ins/file-jpeg/jpeg-load.c
+++ b/plug-ins/file-jpeg/jpeg-load.c
@@ -58,12 +58,13 @@ gint32 preview_layer_ID;
gint32
load_image (const gchar *filename,
GimpRunMode runmode,
+ gint32 *layer_ID,
gboolean preview,
gboolean *resolution_loaded,
GError **error)
{
gint32 volatile image_ID;
- gint32 layer_ID;
+ gint32 _layer_ID;
struct jpeg_decompress_struct cinfo;
struct my_error_mgr jerr;
jpeg_saved_marker_ptr marker;
@@ -232,11 +233,11 @@ load_image (const gchar *filename,
cinfo.output_width,
cinfo.output_height,
layer_type, 100, GIMP_NORMAL_MODE);
- layer_ID = preview_layer_ID;
+ _layer_ID = preview_layer_ID;
}
else
{
- layer_ID = gimp_layer_new (image_ID, _("Background"),
+ _layer_ID = gimp_layer_new (image_ID, _("Background"),
cinfo.output_width,
cinfo.output_height,
layer_type, 100, GIMP_NORMAL_MODE);
@@ -343,7 +344,7 @@ load_image (const gchar *filename,
* loop counter, so that we don't have to keep track ourselves.
*/
- buffer = gimp_drawable_get_buffer (layer_ID);
+ buffer = gimp_drawable_get_buffer (_layer_ID);
format = babl_format (image_type == GIMP_RGB ? "R'G'B' u8" : "Y' u8");
while (cinfo.output_scanline < cinfo.output_height)
@@ -412,7 +413,9 @@ load_image (const gchar *filename,
gimp_progress_update (1.0);
}
- gimp_image_insert_layer (image_ID, layer_ID, -1, 0);
+ gimp_image_insert_layer (image_ID, _layer_ID, -1, 0);
+
+ *layer_ID = _layer_ID;
return image_ID;
}
diff --git a/plug-ins/file-jpeg/jpeg-load.h b/plug-ins/file-jpeg/jpeg-load.h
index d4e61b0..281e03d 100644
--- a/plug-ins/file-jpeg/jpeg-load.h
+++ b/plug-ins/file-jpeg/jpeg-load.h
@@ -20,6 +20,7 @@
gint32 load_image (const gchar *filename,
GimpRunMode runmode,
+ gint32 *layer_ID,
gboolean preview,
gboolean *resolution_loaded,
GError **error);
diff --git a/plug-ins/file-jpeg/jpeg-save.c b/plug-ins/file-jpeg/jpeg-save.c
index c38ff5c..e01cef6 100644
--- a/plug-ins/file-jpeg/jpeg-save.c
+++ b/plug-ins/file-jpeg/jpeg-save.c
@@ -188,6 +188,7 @@ background_jpeg_save (PreviewPersistent *pp)
GFile *file = g_file_new_for_path (pp->file_name);
GFileInfo *info;
gchar *text;
+ gint32 layer_ID;
GError *error = NULL;
info = g_file_query_info (file,
@@ -218,7 +219,7 @@ background_jpeg_save (PreviewPersistent *pp)
g_object_unref (file);
/* and load the preview */
- load_image (pp->file_name, GIMP_RUN_NONINTERACTIVE, TRUE, NULL, NULL);
+ load_image (pp->file_name, GIMP_RUN_NONINTERACTIVE, &layer_ID, TRUE, NULL, NULL);
}
/* we cleanup here (load_image doesn't run in the background) */
diff --git a/plug-ins/file-jpeg/jpeg.c b/plug-ins/file-jpeg/jpeg.c
index 5808348..298d1e3 100644
--- a/plug-ins/file-jpeg/jpeg.c
+++ b/plug-ins/file-jpeg/jpeg.c
@@ -173,6 +173,7 @@ run (const gchar *name,
GimpRunMode run_mode;
GimpPDBStatusType status = GIMP_PDB_SUCCESS;
gint32 image_ID;
+ gint32 layer_ID;
gint32 drawable_ID;
GimpParasite *parasite;
GError *error = NULL;
@@ -210,7 +211,7 @@ run (const gchar *name,
break;
}
- image_ID = load_image (param[1].data.d_string, run_mode, FALSE,
+ image_ID = load_image (param[1].data.d_string, run_mode, &layer_ID, FALSE,
&resolution_loaded, &error);
if (image_ID != -1)
@@ -223,12 +224,12 @@ run (const gchar *name,
if (metadata)
{
- GimpMetadataLoadFlags flags = GIMP_METADATA_LOAD_ALL;
+ GimpMetadataLoadFlags flags = GIMP_METADATA_LOAD_ALL;
if (resolution_loaded)
flags &= ~GIMP_METADATA_LOAD_RESOLUTION;
- gimp_image_metadata_load_finish (image_ID, "image/jpeg",
+ gimp_image_metadata_load_finish (image_ID, layer_ID, "image/jpeg",
metadata, flags,
load_interactive);
@@ -574,7 +575,6 @@ run (const gchar *name,
g_object_unref (file);
}
}
-
if (metadata)
g_object_unref (metadata);
}
diff --git a/plug-ins/file-psd/psd.c b/plug-ins/file-psd/psd.c
index 0a52c00..f8cb84f 100644
--- a/plug-ins/file-psd/psd.c
+++ b/plug-ins/file-psd/psd.c
@@ -211,7 +211,7 @@ run (const gchar *name,
if (resolution_loaded)
flags &= ~GIMP_METADATA_LOAD_RESOLUTION;
- gimp_image_metadata_load_finish (image_ID, "image/x-psd",
+ gimp_image_metadata_load_finish (image_ID, -1, "image/x-psd",
metadata, flags,
interactive);
@@ -315,6 +315,8 @@ run (const gchar *name,
metadata, metadata_flags,
file, NULL);
g_object_unref (file);
+
+ g_object_unref (metadata);
}
values[0].data.d_status = GIMP_PDB_SUCCESS;
@@ -334,8 +336,6 @@ run (const gchar *name,
if (export == GIMP_EXPORT_EXPORT)
gimp_image_delete (image_ID);
- if (metadata)
- g_object_unref (metadata);
}
/* Unknown procedure */
diff --git a/plug-ins/file-tiff/file-tiff-load.c b/plug-ins/file-tiff/file-tiff-load.c
index fe83b94..34a2b59 100644
--- a/plug-ins/file-tiff/file-tiff-load.c
+++ b/plug-ins/file-tiff/file-tiff-load.c
@@ -219,6 +219,7 @@ load_dialog (TIFF *tif,
gint32
load_image (GFile *file,
+ gint32 *layer_ID,
TIFF *tif,
TiffSelectedPages *pages,
gboolean *resolution_loaded,
@@ -929,6 +930,8 @@ load_image (GFile *file,
g_free (name);
}
+ *layer_ID = layer;
+
if (! base_format && image_type == GIMP_INDEXED)
{
/* can't create the palette format here, need to get it from
diff --git a/plug-ins/file-tiff/file-tiff-load.h b/plug-ins/file-tiff/file-tiff-load.h
index 372af71..5695039 100644
--- a/plug-ins/file-tiff/file-tiff-load.h
+++ b/plug-ins/file-tiff/file-tiff-load.h
@@ -37,6 +37,7 @@ gboolean load_dialog (TIFF *tif,
TiffSelectedPages *pages);
gint32 load_image (GFile *file,
+ gint32 *layer_ID,
TIFF *tif,
TiffSelectedPages *pages,
gboolean *resolution_loaded,
diff --git a/plug-ins/file-tiff/file-tiff.c b/plug-ins/file-tiff/file-tiff.c
index aa57870..d5dd476 100644
--- a/plug-ins/file-tiff/file-tiff.c
+++ b/plug-ins/file-tiff/file-tiff.c
@@ -264,13 +264,14 @@ run (const gchar *name,
if (run_it)
{
- gint32 image;
- gboolean resolution_loaded = FALSE;
+ gint32 image;
+ gint32 layer_ID;
+ gboolean resolution_loaded = FALSE;
gimp_set_data (LOAD_PROC,
&pages.target, sizeof (pages.target));
- image = load_image (file, tif, &pages,
+ image = load_image (file, &layer_ID, tif, &pages,
&resolution_loaded,
&error);
@@ -291,7 +292,7 @@ run (const gchar *name,
if (resolution_loaded)
flags &= ~GIMP_METADATA_LOAD_RESOLUTION;
- gimp_image_metadata_load_finish (image, "image/tiff",
+ gimp_image_metadata_load_finish (image, layer_ID, "image/tiff",
metadata, flags,
run_mode == GIMP_RUN_INTERACTIVE);
@@ -471,9 +472,9 @@ run (const gchar *name,
* exiv2 saves them with wrong type and the original values
* could be invalid, see also bug 761823
*/
- gexiv2_metadata_clear_tag (metadata, "Exif.Image.0x0118");
- gexiv2_metadata_clear_tag (metadata, "Exif.Image.0x0119");
- gexiv2_metadata_clear_tag (metadata, "Exif.Image.PageNumber");
+ gimp_metadata_remove_attribute (metadata, "Exif.Image.0x0118");
+ gimp_metadata_remove_attribute (metadata, "Exif.Image.0x0119");
+ gimp_metadata_remove_attribute (metadata, "Exif.Image.PageNumber");
gimp_metadata_set_bits_per_sample (metadata, saved_bpp);
@@ -499,6 +500,7 @@ run (const gchar *name,
"image/tiff",
metadata, metadata_flags,
file, NULL);
+ g_object_unref (metadata);
}
/* Store mvals data */
@@ -515,8 +517,6 @@ run (const gchar *name,
if (export == GIMP_EXPORT_EXPORT)
gimp_image_delete (image);
- if (metadata)
- g_object_unref (metadata);
}
else
{
diff --git a/plug-ins/ui/Makefile.am b/plug-ins/ui/Makefile.am
index 1b569f8..16de1db 100644
--- a/plug-ins/ui/Makefile.am
+++ b/plug-ins/ui/Makefile.am
@@ -5,6 +5,8 @@ uidata_DATA = \
plug-in-file-png.ui \
plug-in-file-raw.ui \
plug-in-file-tiff.ui \
- plug-in-metadata.ui
+ plug-in-attributes.ui \
+ plug-in-iptc.ui
+
EXTRA_DIST = $(uidata_DATA)
diff --git a/po-plug-ins/POTFILES.in b/po-plug-ins/POTFILES.in
index 2d3c511..10c4437 100644
--- a/po-plug-ins/POTFILES.in
+++ b/po-plug-ins/POTFILES.in
@@ -6,6 +6,7 @@
plug-ins/common/align-layers.c
plug-ins/common/animation-optimize.c
plug-ins/common/animation-play.c
+plug-ins/common/attributes.c
plug-ins/common/blinds.c
plug-ins/common/blur.c
plug-ins/common/border-average.c
@@ -73,7 +74,6 @@ plug-ins/common/hot.c
plug-ins/common/jigsaw.c
plug-ins/common/mail.c
plug-ins/common/max-rgb.c
-plug-ins/common/metadata.c
plug-ins/common/newsprint.c
plug-ins/common/nl-filter.c
plug-ins/common/oilify.c
@@ -159,7 +159,8 @@ plug-ins/gimpressionist/utils.c
[type: gettext/glade]plug-ins/ui/plug-in-file-png.ui
[type: gettext/glade]plug-ins/ui/plug-in-file-raw.ui
[type: gettext/glade]plug-ins/ui/plug-in-file-tiff.ui
-[type: gettext/glade]plug-ins/ui/plug-in-metadata.ui
+[type: gettext/glade]plug-ins/ui/plug-in-metainfo.ui
+[type: gettext/glade]plug-ins/ui/plug-in-attributes.ui
plug-ins/gradient-flare/gradient-flare.c
plug-ins/help-browser/dialog.c
plug-ins/help-browser/help-browser.c
@@ -217,6 +218,11 @@ plug-ins/lighting/lighting-ui.c
plug-ins/map-object/map-object-apply.c
plug-ins/map-object/map-object-main.c
plug-ins/map-object/map-object-ui.c
+plug-ins/metainfo/interface.c
+plug-ins/metainfo/page-administration.c
+plug-ins/metainfo/page-rights.c
+plug-ins/metainfo/page-description.c
+plug-ins/metainfo/page-artwork.c
plug-ins/pagecurl/pagecurl.c
plug-ins/print/print-draw-page.c
plug-ins/print/print-page-layout.c
diff --git a/tools/pdbgen/pdb/item.pdb b/tools/pdbgen/pdb/item.pdb
index b6f5ff9..2aff3cf 100644
--- a/tools/pdbgen/pdb/item.pdb
+++ b/tools/pdbgen/pdb/item.pdb
@@ -783,6 +783,61 @@ CODE
);
}
+sub item_get_metadata {
+ $blurb = "Returns the item's metadata.";
+ $help = 'Returns metadata from the item.';
+
+ &std_pdb_misc('2016', '2.10');
+
+ @inargs = (
+ { name => 'item', type => 'item',
+ desc => 'The item' }
+ );
+
+ @outargs = (
+ { name => 'metadata_string', type => 'string', wrap => 1,
+ desc => 'The metadata as a xml string'}
+ );
+
+ %invoke = (
+ code => <<'CODE'
+{
+ GimpMetadata *metadata = gimp_item_get_metadata (item);
+
+ if (metadata)
+ metadata_string = gimp_metadata_serialize (metadata);
+}
+CODE
+ );
+}
+
+sub item_set_metadata {
+ $blurb = "Set the item's metadata.";
+ $help = 'Sets metadata on the item.';
+
+ &std_pdb_misc('2016', '2.10');
+
+ @inargs = (
+ { name => 'item', type => 'item',
+ desc => 'The item' },
+ { name => 'metadata_string', type => 'string', wrap => 1,
+ desc => 'The metadata as a xml string' }
+ );
+
+ %invoke = (
+ code => <<'CODE'
+{
+ GimpMetadata *metadata = gimp_metadata_deserialize (metadata_string);
+
+ gimp_item_set_metadata (item, metadata, TRUE);
+
+ if (metadata)
+ g_object_unref (metadata);
+}
+CODE
+ );
+}
+
sub item_attach_parasite {
$blurb = 'Add a parasite to an item.';
@@ -896,10 +951,12 @@ CODE
}
@headers = qw("core/gimplayermask.h"
+ "libgimpbase/gimpbase.h"
"core/gimplist.h"
"core/gimpselection.h"
+ "core/gimpitem.h"
"text/gimptextlayer.h"
- "vectors/gimpvectors.h"
+ "vectors/gimpvectors.h"
"gimppdb-utils.h"
"gimppdbcontext.h"
"gimp-intl.h");
@@ -910,13 +967,13 @@ CODE
item_is_drawable
item_is_layer
item_is_text_layer
- item_is_channel
- item_is_layer_mask
- item_is_selection
- item_is_vectors
- item_is_group
+ item_is_channel
+ item_is_layer_mask
+ item_is_selection
+ item_is_vectors
+ item_is_group
item_get_parent
- item_get_children
+ item_get_children
item_get_name item_set_name
item_get_visible item_set_visible
item_get_linked item_set_linked
@@ -924,9 +981,10 @@ CODE
item_get_lock_position item_set_lock_position
item_get_color_tag item_set_color_tag
item_get_tattoo item_set_tattoo
- item_attach_parasite item_detach_parasite
- item_get_parasite
- item_get_parasite_list);
+ item_get_metadata item_set_metadata
+ item_attach_parasite item_detach_parasite
+ item_get_parasite
+ item_get_parasite_list);
%exports = (app => [@procs], lib => [@procs]);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]