From 3ab08c8bfddb03ec4fc9a994084198228fb4f951 Mon Sep 17 00:00:00 2001 From: Hartmut Kuhse Date: Tue, 3 Jan 2017 19:19:10 +0100 Subject: [PATCH] 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 .../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 .../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 | 422 +- 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 | 3518 +++++++++++++---- 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 | 61 +- 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, 7662 insertions(+), 2020 deletions(-) create mode 100644 icons/Color/16/gimp-image-metadata.png create mode 100644 icons/Color/24/gimp-image-metadata.png create mode 100644 icons/Color/24/gimp-image-metadata.svg create mode 100644 icons/Color/48/gimp-image-metadata.png create mode 100644 icons/Color/scalable/gimp-image-metadata.svg create mode 100644 icons/Legacy/16/gimp-image-metadata.png create mode 100644 icons/Legacy/24/gimp-image-metadata.png create mode 100644 icons/Legacy/48/gimp-image-metadata.png create mode 100644 icons/Symbolic-Inverted/24/gimp-image-metadata.svg create mode 100644 icons/Symbolic/16/gimp-image-metadata.png create mode 100644 icons/Symbolic/24/gimp-image-metadata.png create mode 100644 icons/Symbolic/24/gimp-image-metadata.svg create mode 100644 icons/Symbolic/48/gimp-image-metadata.png create mode 100644 icons/Symbolic/64/gimp-image-metadata.png create mode 100644 icons/Symbolic/scalable/gimp-image-metadata.svg create mode 100644 libgimp/gimpitem.c create mode 100644 libgimp/gimpitem.h create mode 100644 libgimpbase/gimpattribute.c create mode 100644 libgimpbase/gimpattribute.h create mode 100644 libgimpbase/gimprational.c create mode 100644 libgimpbase/gimprational.h delete mode 100644 plug-ins/ui/plug-in-metadata.ui diff --git a/app/core/core-enums.c b/app/core/core-enums.c index cbdcf44c41..ac60a0523a 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 95616e6158..5b7e92286d 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 11eaa626fc..57921bb1d2 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 25ee2060c7..ff02bbd27c 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 3d97d13f54..5e1e362d2d 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 670cc42d9d..c3b05e2f3e 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 57bc97089d..592954b852 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 ab36f52adf..91673198ce 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 da431e1319..2be3b75699 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 34c95393d1..529f310be1 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 1b95592afd..31e504ac00 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 d8284b815b..d8a3eb5f9e 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 5379699f06..ff7552245e 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 2a19e252d0..0ba96b816f 100644 --- a/app/pdb/item-cmds.c +++ b/app/pdb/item-cmds.c @@ -821,6 +821,67 @@ item_set_tattoo_invoker (GimpProcedure *procedure, error ? *error : NULL); } +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, @@ -1730,6 +1791,66 @@ register_item_procs (GimpPDB *pdb) gimp_pdb_register_procedure (pdb, procedure); 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 */ diff --git a/app/xcf/xcf-load.c b/app/xcf/xcf-load.c index 5be2a71ade..cf8128f02d 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 7930b0f7bc..3d9cf4ae27 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 e9bf40aa1c..918b78bb45 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 0000000000000000000000000000000000000000..dd0bafa2fea659f035f85e80d0e78795ed7d44cb GIT binary patch literal 569 zcmV-90>=G`P)q$gGR5;6xlh11tQ4q&JyZbgxV%juOt62q)=Awt51f@suFHjU) z>e+)A4}xdGgCYnXl~VOmLGU6(Jb5sP9z;P%J*u-USi4$HlahVoVY5k^{?NG$?=kav zZ)U#Zh={U{)$6aL!LtrUMN!mgwOY3v5z!#1YypUbl*^?7fTC#PRq>6_!om|Vg9!jB zEC?!ygk-ZBVjE+v#ddvcWAga|mEhorVHZmwj~>tUe**Zv&-BF^930GG@XnA~u3x*{ zyQ?0XMAf2z%YRr9RJd{T9!0N7$*g0Xqz?dYqtRGN>;IRv)>fD(bjWyMRYAKyC>BTA z4I&cqX6_PMcY|uh;Cq??(0&QEXZN>-h=hE7I>XZPcg|1k=g?RO-&0g=QbHQ&yNXwz z-UJZ|d3pN`51ub_`gnoEMN2RiW8A)~OsX>DDtq@ixTCKSnF8QIc!jeki`3pNaipw3 zoIKWK7@++8-Q>ffWg_zycjP15v3&Zx&aY*Q@iV;tVtYwzaeEkG`4jJaI+yhaTMf{&_xxM<;4T6e2-X z&_yMEXhD=_LRp%g&7CyziwugQp_Sg~{LV#P7x}U2HupU@H>X9m&29cW?<W z{C1u_@4M%>_i*0x98q<3bu7nmpCuAWfMr>11*BFDSEJmo}?=wwU-IPFHJN_{+YZrArie&8(ko@g7C46=TEN+ z5lk91!h*FRbBivW5`+k}`ti*P%C4Ujga|Z~>g57*tVcrTNN=B56(X3>jYSH`t+&?o zZ!SGfd3Gl|6cZXsazil@s5PU}1!Pd@4yq|kK}EqaynUF1s;kL(SDp*`qa|u@rJ?1X zn8u`rYGPPe(8Xp)+*KbRi)sF5{vq7RJBW_TEc(5+@F>mI@n`vi44NY~M#p~$3%=B4 zQU2`*5)W}=NDJaA^0MU6oL&iob6NY)`6NA*BQ-|U--QLkO_>o4NmB5YOTz_m9NO+D zhVng?$!IEyUpAN4P<;~?7+*0ky=Gu-&4jI8j42+=`%IYLFz6~zz}fUzI!DPSL0)`> z#^DGGX53CXINQqLry?6ZZ83tg4G6mH`S>n=_2gi#jX~X&{kWX72aVSbU{;7>Uv5xfcUZwnDsVU#auNYhX+QJwA c#K*h;0s^O7N%8w;mjD0&07*qoM6N<$f{J5<1poj5 literal 0 HcmV?d00001 diff --git a/icons/Color/24/gimp-image-metadata.svg b/icons/Color/24/gimp-image-metadata.svg new file mode 100644 index 0000000000..f0526ba939 --- /dev/null +++ b/icons/Color/24/gimp-image-metadata.svg @@ -0,0 +1,275 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons/Color/48/gimp-image-metadata.png b/icons/Color/48/gimp-image-metadata.png new file mode 100644 index 0000000000000000000000000000000000000000..14a539c1e18a1a768685cc7e479a5981de74501e GIT binary patch literal 1903 zcmV-#2ax!QP)bP;|1K zEGJ7%@lA?al!}Htb<|AJ1%X%<3`{YU0UdK{nsFvGd9ys0$HMZ^3}g|O{ddmYJr@=h z*}H(?nfcGX_nhy${D1e}-Q}L|NF=7_@9*zIbS)qtpo(nK9cA*COtx}K(?&Ws5E;6w zaSJl|`T4D5=7$p1(t+scbEv2&M_pYVx?6kns1{mnDU`}62BGuy^&LV1B56lhSQs=K zjVN_RqZe19P%wya3Aq&-dQenU#G8^xV6|xcy?YfRRgTKaa&o83P+p-$S$QeS%1WRu z)1XwVK}l&b5%E$|h+<6viZp5z7OVOGM|F>Qh`jt<22e!rfSw-I*6Kt9*aFeie7Ru0 zq!sf-#C%~Qq{b9vF$ z(9j@!m<0gZYz24{5Y&8rL5}cQ=x_j*^TCHpI#djTn$J_KtOu}F^c+h+TGC-+0MvZ0 zI=k}$7{sR^FYXXE0%RjM{|Nywh*c}x+bsq_&FAE2JvjgdvHtT#?Ggi^=2dx_PZoed zc&=G!vlsw1ugcBn8h~Bv+-#NrK+I>i1<+ZK(VmS9Y!(Bc=Cg9r+XkRN_Z|6dbYSa( z_Q?c5&1dDLSqIP&5H$bb_WA8rBS0E5RjJkk&?H+SXx_1%^IGVr*IbMJj}rr+<}jS|LV_RZ$EXZE-t+Q+!>vuY|%?T{kGONzr=9E4crgPKpv`bRv#r314C zwFW`^FZ#Ne#VWs*;oX6+!hTFYaCQTr@NzU8KeEXlfgaP~I&~nnEgOeb3r50Y@o0Fj zm}C~md{FbLnGC>SJwRfhi-jx$uoP#QEpA25h#=_lU zki~H(0f3lK&A1~T;Krd@E!6Z~p=p1DpA_GJF+n(>6E622&^Vcys z7ET`k<<2Rs#xNh$yiEXUCpn8sKSdNuq~MTB_HQ4PnvHLEABvF!dt>+Vk*%J?!)*wn zc1&({oT}XbIbkw0j6)b@!j`kSlW07a2fUd2%|Xrg5(jpd-dC`VcRERXH=dgik0HuOC0`7 zbVY6IM%0%E!SLV|8Xkn8z9ay;)D3t*#_q>+xPL*0$`~o1o4nr+FZ6r{CpWx@oKrL5 zGO-Vo+s5)^)<`r!rD^JqC04~aqxu&q9wuvF@T1>aIAW{mfm5=(J7-pfkoM zfZ*W0&|f7cuFLojEH%eJHmv~AJHrEzSp5wOAQJ%ICf`OB;8xJ2&IO?6>2xo9@mrO}etFzf4t{X$~jF^df%O_&R8MqheRm7P0B|>guY-A|ETW z78Iz3I&3K^c{VrC09d6&Fi*tHv)MD=JW)rQr{`uDIa`%wR%Yc3-Jzih)Yo^v3VriN z%fTThPq4KUr_GicD9vy+CuYO^10m36r3rh_Mz+is81bK|sKW&RWh}N2| pSfNW;NN3Vhu-qnzTyT#0{vXNDH(NuB>v{kH002ovPDHLkV1m!$eO~|o literal 0 HcmV?d00001 diff --git a/icons/Color/scalable/gimp-image-metadata.svg b/icons/Color/scalable/gimp-image-metadata.svg new file mode 100644 index 0000000000..444d76733e --- /dev/null +++ b/icons/Color/scalable/gimp-image-metadata.svg @@ -0,0 +1,268 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons/Legacy/16/gimp-image-metadata.png b/icons/Legacy/16/gimp-image-metadata.png new file mode 100644 index 0000000000000000000000000000000000000000..dd0bafa2fea659f035f85e80d0e78795ed7d44cb GIT binary patch literal 569 zcmV-90>=G`P)q$gGR5;6xlh11tQ4q&JyZbgxV%juOt62q)=Awt51f@suFHjU) z>e+)A4}xdGgCYnXl~VOmLGU6(Jb5sP9z;P%J*u-USi4$HlahVoVY5k^{?NG$?=kav zZ)U#Zh={U{)$6aL!LtrUMN!mgwOY3v5z!#1YypUbl*^?7fTC#PRq>6_!om|Vg9!jB zEC?!ygk-ZBVjE+v#ddvcWAga|mEhorVHZmwj~>tUe**Zv&-BF^930GG@XnA~u3x*{ zyQ?0XMAf2z%YRr9RJd{T9!0N7$*g0Xqz?dYqtRGN>;IRv)>fD(bjWyMRYAKyC>BTA z4I&cqX6_PMcY|uh;Cq??(0&QEXZN>-h=hE7I>XZPcg|1k=g?RO-&0g=QbHQ&yNXwz z-UJZ|d3pN`51ub_`gnoEMN2RiW8A)~OsX>DDtq@ixTCKSnF8QIc!jeki`3pNaipw3 zoIKWK7@++8-Q>ffWg_zycjP15v3&Zx&aY*Q@iV;tVtYwzaeEkG`4jJaI+yhaTMf{&_xxM<;4T6e2-X z&_yMEXhD=_LRp%g&7CyziwugQp_Sg~{LV#P7x}U2HupU@H>X9m&29cW?<W z{C1u_@4M%>_i*0x98q<3bu7nmpCuAWfMr>11*BFDSEJmo}?=wwU-IPFHJN_{+YZrArie&8(ko@g7C46=TEN+ z5lk91!h*FRbBivW5`+k}`ti*P%C4Ujga|Z~>g57*tVcrTNN=B56(X3>jYSH`t+&?o zZ!SGfd3Gl|6cZXsazil@s5PU}1!Pd@4yq|kK}EqaynUF1s;kL(SDp*`qa|u@rJ?1X zn8u`rYGPPe(8Xp)+*KbRi)sF5{vq7RJBW_TEc(5+@F>mI@n`vi44NY~M#p~$3%=B4 zQU2`*5)W}=NDJaA^0MU6oL&iob6NY)`6NA*BQ-|U--QLkO_>o4NmB5YOTz_m9NO+D zhVng?$!IEyUpAN4P<;~?7+*0ky=Gu-&4jI8j42+=`%IYLFz6~zz}fUzI!DPSL0)`> z#^DGGX53CXINQqLry?6ZZ83tg4G6mH`S>n=_2gi#jX~X&{kWX72aVSbU{;7>Uv5xfcUZwnDsVU#auNYhX+QJwA c#K*h;0s^O7N%8w;mjD0&07*qoM6N<$f{J5<1poj5 literal 0 HcmV?d00001 diff --git a/icons/Legacy/48/gimp-image-metadata.png b/icons/Legacy/48/gimp-image-metadata.png new file mode 100644 index 0000000000000000000000000000000000000000..14a539c1e18a1a768685cc7e479a5981de74501e GIT binary patch literal 1903 zcmV-#2ax!QP)bP;|1K zEGJ7%@lA?al!}Htb<|AJ1%X%<3`{YU0UdK{nsFvGd9ys0$HMZ^3}g|O{ddmYJr@=h z*}H(?nfcGX_nhy${D1e}-Q}L|NF=7_@9*zIbS)qtpo(nK9cA*COtx}K(?&Ws5E;6w zaSJl|`T4D5=7$p1(t+scbEv2&M_pYVx?6kns1{mnDU`}62BGuy^&LV1B56lhSQs=K zjVN_RqZe19P%wya3Aq&-dQenU#G8^xV6|xcy?YfRRgTKaa&o83P+p-$S$QeS%1WRu z)1XwVK}l&b5%E$|h+<6viZp5z7OVOGM|F>Qh`jt<22e!rfSw-I*6Kt9*aFeie7Ru0 zq!sf-#C%~Qq{b9vF$ z(9j@!m<0gZYz24{5Y&8rL5}cQ=x_j*^TCHpI#djTn$J_KtOu}F^c+h+TGC-+0MvZ0 zI=k}$7{sR^FYXXE0%RjM{|Nywh*c}x+bsq_&FAE2JvjgdvHtT#?Ggi^=2dx_PZoed zc&=G!vlsw1ugcBn8h~Bv+-#NrK+I>i1<+ZK(VmS9Y!(Bc=Cg9r+XkRN_Z|6dbYSa( z_Q?c5&1dDLSqIP&5H$bb_WA8rBS0E5RjJkk&?H+SXx_1%^IGVr*IbMJj}rr+<}jS|LV_RZ$EXZE-t+Q+!>vuY|%?T{kGONzr=9E4crgPKpv`bRv#r314C zwFW`^FZ#Ne#VWs*;oX6+!hTFYaCQTr@NzU8KeEXlfgaP~I&~nnEgOeb3r50Y@o0Fj zm}C~md{FbLnGC>SJwRfhi-jx$uoP#QEpA25h#=_lU zki~H(0f3lK&A1~T;Krd@E!6Z~p=p1DpA_GJF+n(>6E622&^Vcys z7ET`k<<2Rs#xNh$yiEXUCpn8sKSdNuq~MTB_HQ4PnvHLEABvF!dt>+Vk*%J?!)*wn zc1&({oT}XbIbkw0j6)b@!j`kSlW07a2fUd2%|Xrg5(jpd-dC`VcRERXH=dgik0HuOC0`7 zbVY6IM%0%E!SLV|8Xkn8z9ay;)D3t*#_q>+xPL*0$`~o1o4nr+FZ6r{CpWx@oKrL5 zGO-Vo+s5)^)<`r!rD^JqC04~aqxu&q9wuvF@T1>aIAW{mfm5=(J7-pfkoM zfZ*W0&|f7cuFLojEH%eJHmv~AJHrEzSp5wOAQJ%ICf`OB;8xJ2&IO?6>2xo9@mrO}etFzf4t{X$~jF^df%O_&R8MqheRm7P0B|>guY-A|ETW z78Iz3I&3K^c{VrC09d6&Fi*tHv)MD=JW)rQr{`uDIa`%wR%Yc3-Jzih)Yo^v3VriN z%fTThPq4KUr_GicD9vy+CuYO^10m36r3rh_Mz+is81bK|sKW&RWh}N2| pSfNW;NN3Vhu-qnzTyT#0{vXNDH(NuB>v{kH002ovPDHLkV1m!$eO~|o literal 0 HcmV?d00001 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 0000000000..a615598f46 --- /dev/null +++ b/icons/Symbolic-Inverted/24/gimp-image-metadata.svg @@ -0,0 +1,189 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + diff --git a/icons/Symbolic/16/gimp-image-metadata.png b/icons/Symbolic/16/gimp-image-metadata.png new file mode 100644 index 0000000000000000000000000000000000000000..e7fac8f1a2cb70d9ea2d0a8dcd3dae95c76d65ee GIT binary patch literal 461 zcmV;;0W$uHP)YdZ<|KUrZ8J|R_~qn?#I2{BLJY;Z0=zLRVtNX2mq$D z*`p8wj^iMk&0S#Ff$H{ z#iMv4(f1$XhS5(N#9cA)?npE_VjNC88)ZPi@=22Y^&HL00000NkvXXu0mjf D6G0fppLcfGt)(Agw6xGJHe5V-!du}&vk8qC zZ(h85(D)yi7(Y*P@qh4!;YK(~LwfQe30@2h2D)u3Lgb@Or0h)R^-wUSv1u!E^Eu5v zGw<`8nau0}7c2sRLZR@W;rkEU%%6r3m2+kEq zQ%EEno|976h)8IyCk(^51^}+>{s4erFnFukY&xcC_P<4)QcC^4G( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + diff --git a/icons/Symbolic/48/gimp-image-metadata.png b/icons/Symbolic/48/gimp-image-metadata.png new file mode 100644 index 0000000000000000000000000000000000000000..d25b8e18b1351daffbe059869c7c0ee81ca3e186 GIT binary patch literal 1367 zcmV-d1*rOoP)7N6Gt4!zcah*bpna4MnplSDKRlltb_(bP>}%1 zvLZ#Ukl;_~0rgB@hpGo3t5#|y+FmR5P(fRXR&7fDi&i1ON-4e!m}S$62+YMOe7-iv)-Wf8V%KaIf+E_Yn+*Am-;+Fq=Dg?uJqd z0O0fc(H)N?8jC^Gw1s{l1pc~y9lFP}x+#mWXapF0JTlF}AR0Gq0sxGRj4T?JjD&3O z==fyUjveo;R?w3tPpW+FJYVC;ty>sRCUN`jUECQPd#Yaup$%TTboh^ffsd9sr)%_l zjmZZO@cr@QMRycJXjd*@K797V1-2^^d3#0Dl|WvBQV~LEgBLHpdHU?xfSpQxv0S0D zM5EE4o>jAG3USV%>+YO{5bWyU-~sQtb+5b3w=66YaOUO^YHfvK7_JVIkvM2pSHH2W z=}O?KAMw0xV{1zb48xFa4kUzN*M^2(^HfzGBBF<->?5U|%GtanrM&9qoIrt9Xx_Hb z*3t1{ITFKHuI#U_sW~L26vb*8V~(b2k20H+MD&Pr-s&*cFMq7jAF=Sl4ye z61PW34{X}Jd7+Jn<^c@l))A2bV3vsHiRd53*efi1BXU7O0RVWlv-90;k;n%gkEdjb zn%dgwEuqj`EzQj*gb+8Sl%wf%dXO>pQf?h%j7ur!Ow$}@jI}Ywie@QG0026-Z+|}& z4u5AD20g2`*5{kv(%k&nR5E$DudmPF)zx*)G|e%O#}iRX`EqNC$Wlt#mSr_D#@wwM zZUg|pv8*q{?d{(b8wsD^KhqQld~VIoo(7;RrFJ9|3GO(K=gFx-MAH)!6Q>wsFB^tY zQ*QW@1pr`fcJ`~*wzhBcN!0p$(@lZEk-1dr_iP(utZMJxy&Ge(*tk+^CMUer*w}cc zp`oEp)3nB=gm)zXJeCNyv>Y={b3tN#P0e&uAaKOCte-jOH0ByxhKODu zq8Fu*E0I*Z3qfdnY@1UI5S5;N*9vB!Xc!B!*`qneXb_4L1n*msY zKnbj$oSY06>eMtXtZCYko`!*y6#(aaW1&7NrR({g_Z&;vSt&&T?969AmNY#|p)3I+ zvK+@b-{0SVA{veU+TY)QLesSKN~yWrx^kXv+~Qgw6aKgE?(TbuM1lYykx0HWxyQSCbeNcWJ_ms4ZmGb90 z%g&uU(*T}X@8+1rSkYy4HF)j?3QnOyuk!wHJoh6|5m;*~0&7i0V6CYLtTh#Z3KgDn Z{spZx`ys^({LcUY002ovPDHLkV1f?&iu?co literal 0 HcmV?d00001 diff --git a/icons/Symbolic/64/gimp-image-metadata.png b/icons/Symbolic/64/gimp-image-metadata.png new file mode 100644 index 0000000000000000000000000000000000000000..02a2b12f2342dd451194ccc207d0cccfac4bbd44 GIT binary patch literal 1853 zcmV-D2g3M?P)diAwz+rb(@|zOK|4i!{-`+nY_5HuX)BroCjAXASsbsTWhjp)7jYorYLwYGlT5vYSm2pdwa3R zU3vPW>fQKuWni1)e>BzTN&jlPP=iTf}3rgEQ09pWll{e_pp* z3n+O43g3&jCnmrRV`B<@`)$=^@mTC2h(3QXKmWmz!-xNBrqE_lb_#4g0YV5&O-|O7 zFut(RE2aGGySX{zXkXve#w9kF9oK+*NyK8k1pI00&YjPemzKtxQKJ&NuK%r;__Aw& z8N_3;?&!mZ@4G#o)21YB5+w$#=W;lB_^>I-lZiynqxt#w-5$>=QxZ0T5(7BrkeuU~ z&u2=3cr50bk4AshwP(-IOi5@mB?bThHw;rvcoL6y&&|$$;OglKNhzPzkz%>03I)3g(cqL{XS*_8nR(ACxTLvMfoyWT)xx}HJ~r!(&# z9K4)bT>N_^66w@+-7pMeoH6#5qA2@0=O-Csu5x2lRdq%pk)+LLJ3>VEdg@nV*9HK< zLAU#<-Ddku;N;0WwTx@G+YPVZ|K)Nrd5JNm<@5Q|H*VapkBp4u0JL+?4^WhNr_FOJ^bo28X z5%F?krId@huHSaKTzgejHD`BLM9l^O8zv~gr`1ewIGu*q@4vi~N?j`MlTseIa^*^! zuIq*nVz#)Sh*G+)PhGuwRkPV_Z`Px8H`HYS0Aw-S%N z`sB&)q?9E`MMOkSr*kg=xLmGT&iVGj*N_? z0bq1=R1nd%;o;%2^XJb`B$LTmBI=S-zQq_jR_U!#3#PnJbGcmBp(vjWo;vlhqA1hL z$>g7lV;EzaVHn4+U%!4Jo6SyMxNzZnDdnTBej<_pCW^=Dx^4hKI2`WfocoB#)H4jz zlC7?;W|ds-*PQdREBY3SqPQIn$J>!euC9WXW+ko$5!v(k{A<H6+-t@pEHeNAG)>!AErv1H z(@YJP8X%=~T3H`3`BYd4!KIW-05Y3RP9oaF7^}2;HRV*g7EBSqIR|4b>UO(lLZMK; zuMNw922w$!q@@gsfBdM4E!x|bIYT72`RZ#7I zEZqnE7-$(_wE(NBx(}%K8=L=ke)3O1%K)ndSWU|Ss|8q1%K)ndSWU|Ss|8q1%K)nd rSWU|Ss|8q1%K)ndv}n + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + diff --git a/icons/icon-list.mk b/icons/icon-list.mk index 5ed962b56f..db71840ccf 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 72ce2b7093..dfb0169dee 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 ac546229f5..066c234ccf 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 0b89b20c0e..bf23188f42 100644 --- a/libgimp/gimp.h +++ b/libgimp/gimp.h @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include diff --git a/libgimp/gimpimagemetadata.c b/libgimp/gimpimagemetadata.c index 559140354e..7115437e8b 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; - /* read metadata from saved file */ - new_metadata = gimp_metadata_load_from_file (file, error); + image_width = gimp_image_width (image_ID); + image_height = gimp_image_height (image_ID); - if (! new_metadata) + 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 */ + gexivdata = gimp_metadata_load_from_file (file, error); + + 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); - if ((flags & GIMP_METADATA_SAVE_EXIF) && support_exif) + support_exif = gexiv2_metadata_get_supports_exif (filemetadata); + support_xmp = gexiv2_metadata_get_supports_xmp (filemetadata); + support_iptc = gexiv2_metadata_get_supports_iptc (filemetadata); + + /* 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 26d0b71aa1..1771ec29d5 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 0000000000..ae19d5ded6 --- /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 + * . + */ + +#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 0000000000..2b7ecf72d7 --- /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 + * . + */ + +#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION) +#error "Only 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 c1a004f551..2f6803879b 100644 --- a/libgimp/gimpitem_pdb.c +++ b/libgimp/gimpitem_pdb.c @@ -934,6 +934,68 @@ gimp_item_set_tattoo (gint32 item_ID, return success; } +/** + * _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. diff --git a/libgimp/gimpitem_pdb.h b/libgimp/gimpitem_pdb.h index 6b81f0456c..a16d0e4563 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 c97e68f08d..d35c9c031d 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 0000000000..0450b4d2ef --- /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 + * + * 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 + * . + */ + +#include "config.h" + +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include + +#include + +#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: + * + * + * value + * [interpreted value] + * + * + * 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 (" \n", private->name, private->value_type); + end_tag_elem = g_strdup_printf (" "); + + g_string_append (xml, start_tag_elem); + + if (encoded) + { + value_tag_elem = g_strdup_printf (" %s\n", v_escaped); + } + else + { + value_tag_elem = g_strdup_printf (" %s\n", v_escaped); + } + + g_string_append (xml, value_tag_elem); + + if (private->has_structure) + { + struct_tag_elem = g_strdup_printf (" %d\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 (" %s\n", i_escaped); + } + else + { + interpreted_tag_elem = g_strdup_printf (" %s\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 0000000000..c4b3a15f3e --- /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 + +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 5897ed882b..f3395f9157 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 a5682e7e98..f60bef7bb6 100644 --- a/libgimpbase/gimpbase.h +++ b/libgimpbase/gimpbase.h @@ -23,6 +23,7 @@ #include +#include #include #include #include diff --git a/libgimpbase/gimpbasetypes.h b/libgimpbase/gimpbasetypes.h index bef9904842..d05d80c2f2 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 8da75a7145..d1f360f285 100644 --- a/libgimpbase/gimpmetadata.c +++ b/libgimpbase/gimpmetadata.c @@ -22,6 +22,10 @@ #include "config.h" +#include +#ifdef HAVE_UNISTD_H +#include +#endif #include #include @@ -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; +}; -static GQuark gimp_metadata_error_quark (void); -static void gimp_metadata_add (GimpMetadata *src, - GimpMetadata *dest); +struct _GimpMetadata { + GExiv2Metadata parent_instance; + GimpMetadataPrivate *priv; +}; +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 metadata; + return new_metadata; + + } + else + { + return NULL; + } +} + +/** + * 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,23 +371,1948 @@ 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; +} +/** + * 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) +{ + 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) + { + 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)) + { + gchar *sortable_tag; + + 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); + } +} + +/** + * 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; +} + +/** + * 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) +{ + gchar *lowchar; + GimpAttribute *attribute_data = NULL; + gpointer *data; + gchar *name_of_tag; + + g_return_val_if_fail (IS_GIMP_METADATA (metadata), NULL); + g_return_val_if_fail (sorted_name != NULL, NULL); + + lowchar = g_ascii_strdown (sorted_name, -1); + + data = g_hash_table_lookup (metadata->priv->sorted_to_attribute, (gpointer) lowchar); + + if (data) + { + name_of_tag = (gchar *) data; + + data = g_hash_table_lookup (metadata->priv->attribute_table, (gpointer) name_of_tag); + if (data) + { + attribute_data = (GimpAttribute *) data; + } + } + + g_free (lowchar); + + return attribute_data; +} + +/** + * 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) +{ + gchar *lowchar; + gboolean success = FALSE; + GHashTableIter iter_remove; + gpointer key, value; + gchar *tag_to_remove = NULL; + gchar *name_of_tag = NULL; + + lowchar = g_ascii_strdown (name, -1); + + if (g_hash_table_remove (metadata->priv->attribute_table, (gpointer) lowchar)) + { + gchar *tag_list_remove = NULL; + + g_hash_table_iter_init (&iter_remove, metadata->priv->sorted_to_attribute); + while (g_hash_table_iter_next (&iter_remove, &key, &value)) + { + tag_to_remove = (gchar *) key; + name_of_tag = (gchar *) value; + + if (! g_strcmp0 (lowchar, name_of_tag)) + break; + } + + tag_list_remove = g_strdup (tag_to_remove); /* because removing from hashtable frees tag_to_remove */ + + 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 + { + if (name_of_tag) + g_free (name_of_tag); + if (tag_to_remove) + g_free (tag_to_remove); + success = FALSE; + } + } + g_free (lowchar); + + 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; +} + +/** + * 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) +{ + 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, "\n"); + g_string_append (string, "\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, "\n"); + + return g_string_free (string, FALSE); +} + +/** + * gimp_metadata_deserialize: + * + * @xml: a #gchar array + * + * 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 + * + * Return value: a new #GimpMetadata object. + * + * Since : 2.10 + */ +GimpMetadata * +gimp_metadata_deserialize (const gchar *xml) +{ + GMarkupParser *markup_parser = g_slice_new (GMarkupParser); + GimpMetadataParseData *parse_data = g_slice_new (GimpMetadataParseData); + GMarkupParseContext *context; + GimpMetadata *metadata; + + g_return_val_if_fail (xml != NULL, NULL); + + metadata = gimp_metadata_new (); + + parse_data->metadata = metadata; + + markup_parser->start_element = gimp_metadata_deserialize_start_element; + markup_parser->end_element = gimp_metadata_deserialize_end_element; + markup_parser->text = gimp_metadata_deserialize_text; + markup_parser->passthrough = NULL; + markup_parser->error = gimp_metadata_deserialize_error; + + context = g_markup_parse_context_new (markup_parser, 0, parse_data, NULL); + + g_markup_parse_context_parse (context, + xml, strlen (xml), + NULL); + + g_markup_parse_context_unref (context); + + g_slice_free (GMarkupParser, markup_parser); + g_slice_free (GimpMetadataParseData, parse_data); + + return metadata; +} + +/** + * 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) +{ + 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 **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_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; + + 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_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); + + name = gimp_attribute_get_name (attribute); + + 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); + } +} + +/** + * 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) +{ + 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)) + { + 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)) + { + if (no_interpreted) + { + if (interpreted) + { + gimp_attribute_set_interpreted_string (attribute, interpreted_value); + } + } + } + else + { + g_object_unref (attribute); + } + + g_free (interpreted_value); + g_free (value); + } + return attribute; +} + +/** + * 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 + * + * Converts @metadata to @gexiv2metadata in gexiv2 format + * + * 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 + * + * Converts @metadata to @gexiv2metadata packet + * + * Since: GIMP 2.10 + */ +void +gimp_metadata_to_gexiv2 (GimpMetadata *metadata) +{ + 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); + } + + 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; + gchar *t_packet = NULL; + 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); + + 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) + { + if(! g_strcmp0 (category, "exif")) + { + 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); + + 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; + } + + 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); + } + } + } + } + + if (t_packet) + g_free (t_packet); + if (value) + g_free (value); + if (category) + g_free (category); + if (o_packet) + g_free (o_packet); +} + +/** + * 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++) + { + 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) + { + 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); + } + } + } + + 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_attribute_name_to_value (const gchar **attribute_names, - const gchar **attribute_values, - const gchar *name) +gimp_metadata_name_to_value (const gchar **attribute_names, + const gchar **attribute_values, + const gchar *name) { while (*attribute_names) { @@ -228,13 +2328,27 @@ gimp_metadata_attribute_name_to_value (const gchar **attribute_names, 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) + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + gpointer user_data, + GError **error) { GimpMetadataParseData *parse_data = user_data; @@ -243,10 +2357,10 @@ gimp_metadata_deserialize_start_element (GMarkupParseContext *context, const gchar *name; const gchar *encoding; - name = gimp_metadata_attribute_name_to_value (attribute_names, + name = gimp_metadata_name_to_value (attribute_names, attribute_values, "name"); - encoding = gimp_metadata_attribute_name_to_value (attribute_names, + encoding = gimp_metadata_name_to_value (attribute_names, attribute_values, "encoding"); @@ -262,16 +2376,73 @@ gimp_metadata_deserialize_start_element (GMarkupParseContext *context, 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; + } + + 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; + + encoding = gimp_metadata_name_to_value (attribute_names, + attribute_values, + "encoding"); + + parse_data->base64 = (encoding && ! strcmp (encoding, "base64")); + } + } -static void -gimp_metadata_deserialize_end_element (GMarkupParseContext *context, - const gchar *element_name, - gpointer user_data, - GError **error) -{ -} - +/** + * gimp_metadata_deserialize_text: + * + * @context : #GMarkupParseContext + * @text : const #gchar * + * @text_len : #gsize + * @user_data : #gpointer to #GimpMetadataParseData struct + * @error : #GError ** + * + * text of a tag (parsing xml) + * + * Since: GIMP 2.10 + */ static void gimp_metadata_deserialize_text (GMarkupParseContext *context, const gchar *text, @@ -279,12 +2450,13 @@ gimp_metadata_deserialize_text (GMarkupParseContext *context, gpointer user_data, GError **error) { - GimpMetadataParseData *parse_data = user_data; - const gchar *current_element; + GimpMetadataParseData *parse_data = user_data; + GimpAttribute *attribute = NULL; + const gchar *current_element; current_element = g_markup_parse_context_get_element (context); - if (! g_strcmp0 (current_element, "tag")) + if (! g_strcmp0 (current_element, "tag")) /*old metadata*/ { gchar *value = g_strndup (text, text_len); @@ -296,7 +2468,7 @@ gimp_metadata_deserialize_text (GMarkupParseContext *context, decoded = g_base64_decode (value, &len); if (decoded[len - 1] == '\0') - gexiv2_metadata_set_tag_string (parse_data->metadata, + gexiv2_metadata_set_tag_string (GEXIV2_METADATA (parse_data->metadata), parse_data->name, (const gchar *) decoded); @@ -304,193 +2476,239 @@ gimp_metadata_deserialize_text (GMarkupParseContext *context, } else { - gexiv2_metadata_set_tag_string (parse_data->metadata, + gexiv2_metadata_set_tag_string (GEXIV2_METADATA (parse_data->metadata), parse_data->name, value); } g_free (value); } -} - -static void -gimp_metadata_deserialize_error (GMarkupParseContext *context, - GError *error, - gpointer user_data) -{ - g_printerr ("Metadata parse error: %s\n", error->message); -} - -/** - * gimp_metadata_deserialize: - * @metadata_xml: A string of serialized metadata XML. - * - * Deserializes a string of XML that has been created by - * gimp_metadata_serialize(). - * - * Return value: The new #GimpMetadata. - * - * Since: 2.10 - */ -GimpMetadata * -gimp_metadata_deserialize (const gchar *metadata_xml) -{ - GimpMetadata *metadata; - GMarkupParser markup_parser; - GimpMetadataParseData parse_data; - GMarkupParseContext *context; - - g_return_val_if_fail (metadata_xml != NULL, NULL); - - metadata = gimp_metadata_new (); - - parse_data.metadata = metadata; - - markup_parser.start_element = gimp_metadata_deserialize_start_element; - markup_parser.end_element = gimp_metadata_deserialize_end_element; - markup_parser.text = gimp_metadata_deserialize_text; - markup_parser.passthrough = NULL; - markup_parser.error = gimp_metadata_deserialize_error; - - context = g_markup_parse_context_new (&markup_parser, 0, &parse_data, NULL); - - g_markup_parse_context_parse (context, - metadata_xml, strlen (metadata_xml), - NULL); - - g_markup_parse_context_unref (context); - - return metadata; -} - -static gchar * -gimp_metadata_escape (const gchar *name, - const gchar *value, - gboolean *base64) -{ - if (! g_utf8_validate (value, -1, NULL)) + else if (! g_strcmp0 (current_element, "value")) { - gchar *encoded; + gchar *value = g_strndup (text, text_len); - encoded = g_base64_encode ((const guchar *) value, strlen (value) + 1); - - g_printerr ("Invalid UTF-8 in metadata value %s, encoding as base64: %s\n", - name, encoded); - - *base64 = TRUE; - - return encoded; - } - - *base64 = FALSE; - - return g_markup_escape_text (value, -1); -} - -static void -gimp_metadata_append_tag (GString *string, - const gchar *name, - gchar *value, - gboolean base64) -{ - if (value) - { - if (base64) + if (parse_data->base64) { - g_string_append_printf (string, " %s\n", - name, value); + 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 { - g_string_append_printf (string, " %s\n", - name, value); + 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; + + attribute = current_attribute; + + if (attribute) + { + gint v; + GimpAttributeStructureType struct_type = STRUCTURE_TYPE_BAG; + + 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")) + { + GimpAttribute *attribute = NULL; + gchar *value = NULL; + + attribute = current_attribute; + + if (attribute) + { + value = g_strndup (text, text_len); + + 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); + } } } /** - * gimp_metadata_serialize: - * @metadata: A #GimpMetadata instance. + * gimp_metadata_deserialize_end_element: * - * Serializes @metadata into an XML string that can later be deserialized - * using gimp_metadata_deserialize(). + * @context : #GMarkupParseContext + * @element_name : #gchar * + * @user_data : #gpointer to #GimpMetadataParseData struct + * @error : #GError ** * - * Return value: The serialized XML string. + * 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; + + 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 */ -gchar * -gimp_metadata_serialize (GimpMetadata *metadata) +GExiv2Metadata * +gimp_metadata_new_gexiv2metadata (void) { - GString *string; - gchar **exif_data = NULL; - gchar **iptc_data = NULL; - gchar **xmp_data = NULL; - gchar *value; - gchar *escaped; - gboolean base64; - gint i; + GExiv2Metadata *gexiv2metadata = NULL; + gint i; - g_return_val_if_fail (GEXIV2_IS_METADATA (metadata), NULL); - - string = g_string_new (NULL); - - g_string_append (string, "\n"); - g_string_append (string, "\n"); - - exif_data = gexiv2_metadata_get_exif_tags (metadata); - - if (exif_data) + if (gexiv2_initialize ()) { - for (i = 0; exif_data[i] != NULL; i++) - { - value = gexiv2_metadata_get_tag_string (metadata, exif_data[i]); - escaped = gimp_metadata_escape (exif_data[i], value, &base64); - g_free (value); + gexiv2metadata = gexiv2_metadata_new (); - gimp_metadata_append_tag (string, exif_data[i], escaped, base64); + 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); } - g_strfreev (exif_data); } + return gexiv2metadata; +} - xmp_data = gexiv2_metadata_get_xmp_tags (metadata); - if (xmp_data) +/** + * gimp_metadata_save_to_file: + * @metadata: A #GimpMetadata instance. + * @file: The file to save the metadata to + * @error: Return location for error message + * + * Saves @metadata to @file. + * + * Return value: %TRUE on success, %FALSE otherwise. + * + * Since: 2.10 + */ +gboolean +gimp_metadata_save_to_file (GimpMetadata *metadata, + GFile *file, + GError **error) +{ + gchar *path; + gchar *filename; + gboolean success; + + 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); + + path = g_file_get_path (file); + + if (! path) { - for (i = 0; xmp_data[i] != NULL; i++) - { - value = gexiv2_metadata_get_tag_string (metadata, xmp_data[i]); - escaped = gimp_metadata_escape (xmp_data[i], value, &base64); - g_free (value); - - gimp_metadata_append_tag (string, xmp_data[i], escaped, base64); - } - - g_strfreev (xmp_data); + g_set_error (error, gimp_metadata_error_quark (), 0, + _("Can save metadata only to local files")); + return FALSE; } - iptc_data = gexiv2_metadata_get_iptc_tags (metadata); +#ifdef G_OS_WIN32 + filename = g_win32_locale_filename_from_utf8 (path); +#else + filename = g_strdup (path); +#endif - if (iptc_data) - { - for (i = 0; iptc_data[i] != NULL; i++) - { - value = gexiv2_metadata_get_tag_string (metadata, iptc_data[i]); - escaped = gimp_metadata_escape (iptc_data[i], value, &base64); - g_free (value); + g_free (path); - gimp_metadata_append_tag (string, iptc_data[i], escaped, base64); - } + success = gexiv2_metadata_save_file (GEXIV2_METADATA (metadata), filename, error); - g_strfreev (iptc_data); - } + g_free (filename); - g_string_append (string, "\n"); - - return g_string_free (string, FALSE); + return success; } /** @@ -508,7 +2726,7 @@ GimpMetadata * gimp_metadata_load_from_file (GFile *file, GError **error) { - GExiv2Metadata *meta = NULL; + GimpMetadata *metadata = NULL; gchar *path; gchar *filename; @@ -534,12 +2752,12 @@ gimp_metadata_load_from_file (GFile *file, if (gexiv2_initialize ()) { - meta = gexiv2_metadata_new (); + metadata = gimp_metadata_new (); - if (! gexiv2_metadata_open_path (meta, filename, error)) + if (! gexiv2_metadata_open_path (GEXIV2_METADATA (metadata), filename, error)) { - g_object_unref (meta); g_free (filename); + g_object_unref (metadata); return NULL; } @@ -547,56 +2765,7 @@ gimp_metadata_load_from_file (GFile *file, g_free (filename); - return meta; -} - -/** - * gimp_metadata_save_to_file: - * @metadata: A #GimpMetadata instance. - * @file: The file to save the metadata to - * @error: Return location for error message - * - * Saves @metadata to @file. - * - * Return value: %TRUE on success, %FALSE otherwise. - * - * Since: 2.10 - */ -gboolean -gimp_metadata_save_to_file (GimpMetadata *metadata, - GFile *file, - GError **error) -{ - gchar *path; - gchar *filename; - gboolean success; - - g_return_val_if_fail (GEXIV2_IS_METADATA (metadata), FALSE); - g_return_val_if_fail (G_IS_FILE (file), FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - - path = g_file_get_path (file); - - if (! path) - { - g_set_error (error, gimp_metadata_error_quark (), 0, - _("Can save metadata only to local files")); - return FALSE; - } - -#ifdef G_OS_WIN32 - filename = g_win32_locale_filename_from_utf8 (path); -#else - filename = g_strdup (path); -#endif - - g_free (path); - - success = gexiv2_metadata_save_file (metadata, filename, error); - - g_free (filename); - - return success; + return metadata; } /** @@ -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,376 +2881,10 @@ 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 - * - * Returns values based on Exif.Image.XResolution, - * Exif.Image.YResolution and Exif.Image.ResolutionUnit of @metadata. - * - * Return value: %TRUE on success, %FALSE otherwise. - * - * Since: 2.10 - */ -gboolean -gimp_metadata_get_resolution (GimpMetadata *metadata, - gdouble *xres, - gdouble *yres, - GimpUnit *unit) -{ - gint xnom, xdenom; - gint ynom, ydenom; - - g_return_val_if_fail (GEXIV2_IS_METADATA (metadata), 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)) - { - gchar *un; - gint exif_unit = 2; - - un = gexiv2_metadata_get_tag_string (metadata, - "Exif.Image.ResolutionUnit"); - if (un) - { - exif_unit = atoi (un); - g_free (un); - } - - if (xnom != 0 && xdenom != 0 && - ynom != 0 && ydenom != 0) - { - gdouble xresolution = (gdouble) xnom / (gdouble) xdenom; - gdouble yresolution = (gdouble) ynom / (gdouble) ydenom; - - if (exif_unit == 3) - { - 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) - { - 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++) - { - if (fabs (xres * factor - ROUND (xres * factor)) < 0.01 && - fabs (yres * factor - ROUND (yres * factor)) < 0.01) - break; - } - - 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); -} - -/** - * gimp_metadata_get_colorspace: - * @metadata: A #GimpMetadata instance. - * - * Returns values based on Exif.Photo.ColorSpace, Xmp.exif.ColorSpace, - * Exif.Iop.InteroperabilityIndex, Exif.Nikon3.ColorSpace, - * Exif.Canon.ColorSpace of @metadata. - * - * Return value: The colorspace specified by above tags. - * - * Since: 2.10 - */ -GimpMetadataColorspace -gimp_metadata_get_colorspace (GimpMetadata *metadata) -{ - glong exif_cs = -1; - - g_return_val_if_fail (GEXIV2_IS_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")) - { - exif_cs = gexiv2_metadata_get_tag_long (metadata, - "Exif.Photo.ColorSpace"); - } - else if (gexiv2_metadata_has_tag (metadata, "Xmp.exif.ColorSpace")) - { - exif_cs = gexiv2_metadata_get_tag_long (metadata, - "Xmp.exif.ColorSpace"); - } - - if (exif_cs == 0x01) - { - return GIMP_METADATA_COLORSPACE_SRGB; - } - else if (exif_cs == 0x02) - { - return GIMP_METADATA_COLORSPACE_ADOBERGB; - } - else - { - if (exif_cs == 0xffff) - { - gchar *iop_index; - - iop_index = gexiv2_metadata_get_tag_string (metadata, - "Exif.Iop.InteroperabilityIndex"); - - if (! g_strcmp0 (iop_index, "R03")) - { - g_free (iop_index); - - return GIMP_METADATA_COLORSPACE_ADOBERGB; - } - else if (! g_strcmp0 (iop_index, "R98")) - { - g_free (iop_index); - - return GIMP_METADATA_COLORSPACE_SRGB; - } - - g_free (iop_index); - } - - if (gexiv2_metadata_has_tag (metadata, "Exif.Nikon3.ColorSpace")) - { - glong nikon_cs; - - nikon_cs = gexiv2_metadata_get_tag_long (metadata, - "Exif.Nikon3.ColorSpace"); - - 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")) - { - glong canon_cs; - - canon_cs = gexiv2_metadata_get_tag_long (metadata, - "Exif.Canon.ColorSpace"); - - if (canon_cs == 0x01) - { - return GIMP_METADATA_COLORSPACE_SRGB; - } - else if (canon_cs == 0x02) - { - return GIMP_METADATA_COLORSPACE_ADOBERGB; - } - } - - if (exif_cs == 0xffff) - return GIMP_METADATA_COLORSPACE_UNCALIBRATED; - } - - return GIMP_METADATA_COLORSPACE_UNSPECIFIED; -} - -/** - * 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) -{ - g_return_if_fail (GEXIV2_IS_METADATA (metadata)); - - 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; - - 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; - - case GIMP_METADATA_COLORSPACE_SRGB: - gexiv2_metadata_set_tag_long (metadata, "Exif.Photo.ColorSpace", 0x01); - - if (gexiv2_metadata_has_tag (metadata, "Xmp.exif.ColorSpace")) - gexiv2_metadata_set_tag_long (metadata, "Xmp.exif.ColorSpace", 0x01); - - if (gexiv2_metadata_has_tag (metadata, "Exif.Iop.InteroperabilityIndex")) - gexiv2_metadata_set_tag_string (metadata, - "Exif.Iop.InteroperabilityIndex", "R98"); - - if (gexiv2_metadata_has_tag (metadata, "Exif.Nikon3.ColorSpace")) - gexiv2_metadata_set_tag_long (metadata, "Exif.Nikon3.ColorSpace", 0x01); - - if (gexiv2_metadata_has_tag (metadata, "Exif.Canon.ColorSpace")) - gexiv2_metadata_set_tag_long (metadata, "Exif.Canon.ColorSpace", 0x01); - break; - - case GIMP_METADATA_COLORSPACE_ADOBERGB: - gexiv2_metadata_set_tag_long (metadata, "Exif.Photo.ColorSpace", 0x02); - - if (gexiv2_metadata_has_tag (metadata, "Xmp.exif.ColorSpace")) - gexiv2_metadata_set_tag_long (metadata, "Xmp.exif.ColorSpace", 0x02); - - if (gexiv2_metadata_has_tag (metadata, "Exif.Iop.InteroperabilityIndex")) - gexiv2_metadata_set_tag_string (metadata, - "Exif.Iop.InteroperabilityIndex", "R03"); - - if (gexiv2_metadata_has_tag (metadata, "Exif.Nikon3.ColorSpace")) - gexiv2_metadata_set_tag_long (metadata, "Exif.Nikon3.ColorSpace", 0x02); - - if (gexiv2_metadata_has_tag (metadata, "Exif.Canon.ColorSpace")) - gexiv2_metadata_set_tag_long (metadata, "Exif.Canon.ColorSpace", 0x02); - break; - } -} - /** * gimp_metadata_is_tag_supported: * @tag: A metadata tag name @@ -1133,78 +2937,418 @@ gimp_metadata_is_tag_supported (const gchar *tag, return TRUE; } - -/* private functions */ - -static GQuark -gimp_metadata_error_quark (void) +/** + * gimp_metadata_get_colorspace: + * @metadata: Ab #GimpMetadata instance. + * + * Returns values based on Exif.Photo.ColorSpace, Xmp.exif.ColorSpace, + * Exif.Iop.InteroperabilityIndex, Exif.Nikon3.ColorSpace, + * Exif.Canon.ColorSpace of @metadata. + * + * Return value: The colorspace specified by above tags. + * + * Since: 2.10 + */ +GimpMetadataColorspace +gimp_metadata_get_colorspace (GimpMetadata *metadata) { - static GQuark quark = 0; + glong exif_cs = -1; + GimpAttribute *attribute = NULL; + GValue val; - if (G_UNLIKELY (quark == 0)) - quark = g_quark_from_static_string ("gimp-metadata-error-quark"); + g_return_val_if_fail (IS_GIMP_METADATA (metadata), + GIMP_METADATA_COLORSPACE_UNSPECIFIED); - return quark; + /* the logic here was mostly taken from darktable and libkexiv2 */ + + attribute = gimp_metadata_get_attribute (metadata, "Exif.Photo.ColorSpace"); + if ( ! attribute) + { + attribute = gimp_metadata_get_attribute (metadata, "Xmp.exif.ColorSpace"); + } + + if (attribute) + { + 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) + { + return GIMP_METADATA_COLORSPACE_SRGB; + } + else if (exif_cs == 0x02) + { + return GIMP_METADATA_COLORSPACE_ADOBERGB; + } + else + { + if (exif_cs == 0xffff) + { + gchar *iop_index; + + attribute = gimp_metadata_get_attribute (metadata, "Exif.Iop.InteroperabilityIndex"); + if (attribute) + iop_index = gimp_attribute_get_string (attribute); + + if (! g_strcmp0 (iop_index, "R03")) + { + g_free (iop_index); + + return GIMP_METADATA_COLORSPACE_ADOBERGB; + } + else if (! g_strcmp0 (iop_index, "R98")) + { + g_free (iop_index); + + return GIMP_METADATA_COLORSPACE_SRGB; + } + + g_free (iop_index); + } + + attribute = gimp_metadata_get_attribute (metadata, "Exif.Nikon3.ColorSpace"); + if (attribute) + { + if (gimp_attribute_get_value_type (attribute) == TYPE_LONG || gimp_attribute_get_value_type (attribute) == TYPE_SLONG) + { + 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; + } + } + } + + attribute = gimp_metadata_get_attribute (metadata, "Exif.Canon.ColorSpace"); + if (attribute) + { + if (gimp_attribute_get_value_type (attribute) == TYPE_LONG || gimp_attribute_get_value_type (attribute) == TYPE_SLONG) + { + 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; + } + } + } + + if (exif_cs == 0xffff) + return GIMP_METADATA_COLORSPACE_UNCALIBRATED; + } + + return GIMP_METADATA_COLORSPACE_UNSPECIFIED; } -static void -gimp_metadata_add (GimpMetadata *src, - GimpMetadata *dest) +/** + * 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 + * + * Returns values based on Exif.Image.XResolution, + * Exif.Image.YResolution and Exif.Image.ResolutionUnit of @metadata. + * + * Return value: %TRUE on success, %FALSE otherwise. + * + * Since: GIMP 2.10 + */ +gboolean +gimp_metadata_get_resolution (GimpMetadata *metadata, + gdouble *xres, + gdouble *yres, + GimpUnit *unit) { - gchar *value; - gint i; + GimpAttribute *x_attribute; + GimpAttribute *y_attribute; + GimpAttribute *res_attribute; - if (gexiv2_metadata_get_supports_exif (src) && - gexiv2_metadata_get_supports_exif (dest)) + gint xnom, xdenom; + gint ynom, ydenom; + gint exif_unit = 2; + + g_return_val_if_fail (IS_GIMP_METADATA (metadata), FALSE); + + 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 (x_attribute) { - gchar **exif_data = gexiv2_metadata_get_exif_tags (src); + GValue value; + Rational *rats; + gint *_nom; + gint *_denom; + gint l; - if (exif_data) + value = gimp_attribute_get_value (x_attribute); + + rats = g_value_get_boxed (&value); + + rational_to_int (rats, &_nom, &_denom, &l); + + if (l > 0) { - for (i = 0; exif_data[i] != NULL; i++) - { - value = gexiv2_metadata_get_tag_string (src, exif_data[i]); - gexiv2_metadata_set_tag_string (dest, exif_data[i], value); - g_free (value); - } - - g_strfreev (exif_data); + xnom = _nom[0]; + xdenom = _denom[0]; } + rational_free (rats); + } + else + return FALSE; + + if (y_attribute) + { + GValue value; + Rational *rats; + gint *_nom; + gint *_denom; + gint l; + + value = gimp_attribute_get_value (y_attribute); + + rats = g_value_get_boxed (&value); + + rational_to_int (rats, &_nom, &_denom, &l); + + if (l > 0) + { + ynom = _nom[0]; + ydenom = _denom[0]; + } + rational_free (rats); + } + else + return FALSE; + + if (res_attribute) + { + GValue value = gimp_attribute_get_value (res_attribute); + + exif_unit = (gint) g_value_get_uint (&value); } - if (gexiv2_metadata_get_supports_xmp (src) && - gexiv2_metadata_get_supports_xmp (dest)) + if (xnom != 0 && xdenom != 0 && + ynom != 0 && ydenom != 0) { - gchar **xmp_data = gexiv2_metadata_get_xmp_tags (src); + gdouble xresolution = (gdouble) xnom / (gdouble) xdenom; + gdouble yresolution = (gdouble) ynom / (gdouble) ydenom; - if (xmp_data) + if (exif_unit == 3) { - for (i = 0; xmp_data[i] != NULL; i++) + 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) { - value = gexiv2_metadata_get_tag_string (src, xmp_data[i]); - gexiv2_metadata_set_tag_string (dest, xmp_data[i], value); - g_free (value); + if (exif_unit == 3) + *unit = GIMP_UNIT_MM; + else + *unit = GIMP_UNIT_INCH; } - g_strfreev (xmp_data); + return TRUE; } } + return FALSE; +} - if (gexiv2_metadata_get_supports_iptc (src) && - gexiv2_metadata_get_supports_iptc (dest)) +/** + * 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]; + + g_return_if_fail ( (metadata)); + + 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) +{ + gchar buffer[32]; + + g_return_if_fail (IS_GIMP_METADATA (metadata)); + + 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); +} + +/** + * 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) +{ + g_return_if_fail (IS_GIMP_METADATA (metadata)); + + 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 **iptc_data = gexiv2_metadata_get_iptc_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 (iptc_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; iptc_data[i] != NULL; i++) - { - value = gexiv2_metadata_get_tag_string (src, iptc_data[i]); - gexiv2_metadata_set_tag_string (dest, iptc_data[i], value); - g_free (value); - } - - g_strfreev (iptc_data); + 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; + + 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; } } + +/** + * 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; + + g_return_if_fail (IS_GIMP_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++) + { + if (fabs (xres * factor - ROUND (xres * factor)) < 0.01 && + fabs (yres * factor - ROUND (yres * factor)) < 0.01) + break; + } + + g_snprintf (buffer, sizeof (buffer), "%d/%d", ROUND (xres * factor), factor); + gimp_metadata_new_attribute (metadata, "Exif.Image.XResolution", buffer, TYPE_RATIONAL); + + 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 3a6b27c17f..cac1d8e2b2 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 - * - * 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 - * . - */ -#ifndef __GIMP_METADATA_H__ -#define __GIMP_METADATA_H__ +#ifndef __GIMPMETADATA_H__ +#define __GIMPMETADATA_H__ G_BEGIN_DECLS +#include + +#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 0000000000..93d67546bb --- /dev/null +++ b/libgimpbase/gimprational.c @@ -0,0 +1,301 @@ +/* + * gimprational.c + * + * Created on: 19.09.2014 + * Author: kuhse + */ + +#include + +#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 0000000000..ab020c89d3 --- /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 + +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 0bec46e149..2ddd6ed1c1 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 4d82e5c77e..66f423ac76 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 90b048b3b1..57f1f3f295 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,6 +1373,26 @@ hot_LDADD = \ $(INTLLIBS) \ $(hot_RC) +iptc_CFLAGS = $(GEXIV2_CFLAGS) + +iptc_SOURCES = \ + iptc.c + +iptc_LDADD = \ + $(libgimpui) \ + $(libgimpwidgets) \ + $(libgimpmodule) \ + $(libgimp) \ + $(libgimpmath) \ + $(libgimpconfig) \ + $(libgimpcolor) \ + $(libgimpbase) \ + $(GTK_LIBS) \ + $(GEXIV2_LIBS) \ + $(RT_LIBS) \ + $(INTLLIBS) \ + $(iptc_RC) + jigsaw_SOURCES = \ jigsaw.c @@ -1405,26 +1444,6 @@ max_rgb_LDADD = \ $(INTLLIBS) \ $(max_rgb_RC) -metadata_CFLAGS = $(GEXIV2_CFLAGS) - -metadata_SOURCES = \ - metadata.c - -metadata_LDADD = \ - $(libgimpui) \ - $(libgimpwidgets) \ - $(libgimpmodule) \ - $(libgimp) \ - $(libgimpmath) \ - $(libgimpconfig) \ - $(libgimpcolor) \ - $(libgimpbase) \ - $(GTK_LIBS) \ - $(GEXIV2_LIBS) \ - $(RT_LIBS) \ - $(INTLLIBS) \ - $(metadata_RC) - newsprint_SOURCES = \ newsprint.c diff --git a/plug-ins/common/file-jp2-load.c b/plug-ins/common/file-jp2-load.c index 0facb3c2e7..280361779e 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 6efb7f1450..12c9030044 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 0ff328dbe8..a8e640677d 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 324e7da565..4c7951749d 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 6cc945f14d..ed1ac6b2ca 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 d4e61b04ad..281e03dc26 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 c38ff5c128..e01cef6016 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 580834803b..298d1e303c 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 0a52c0053a..f8cb84fcb1 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 fe83b94426..34a2b59908 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 372af71f53..569503912f 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 aa57870b1e..d5dd476f50 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 1b569f8930..16de1dbba5 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/plug-ins/ui/plug-in-metadata.ui b/plug-ins/ui/plug-in-metadata.ui deleted file mode 100644 index 1e34d8d3be..0000000000 --- a/plug-ins/ui/plug-in-metadata.ui +++ /dev/null @@ -1,908 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - True - False - - - True - True - True - - - True - True - 6 - in - - - True - True - exif-liststore - False - 0 - - - - - - True - 3 - Exif Tag - - - - 0 - - - - - - - True - 3 - Value - - - - 1 - - - - - - - - - - - True - False - 4 - 4 - Exif - - - True - False - - - - - True - True - 6 - in - - - True - True - xmp-liststore - False - 0 - - - - - - True - 3 - XMP Tag - - - - 0 - - - - - - - True - 3 - Value - - - - 1 - - - - - - - - - 1 - - - - - True - False - 4 - 4 - XMP - - - 1 - False - - - - - True - False - 6 - 6 - - - True - True - - - True - False - - - True - False - 6 - 8 - 2 - 3 - 3 - - - True - False - 0 - 3 - 3 - Title - True - - - GTK_SHRINK | GTK_FILL - - - - - True - False - 0 - 3 - 3 - Author - - - 1 - 2 - GTK_SHRINK | GTK_FILL - - - - - True - False - 0 - 3 - 3 - Authortitle - - - 2 - 3 - GTK_SHRINK | GTK_FILL - - - - - True - False - 0 - 3 - 3 - Copyright - - - 3 - 4 - GTK_SHRINK | GTK_FILL - - - - - True - False - 0 - 0 - 3 - 3 - Caption - - - 4 - 5 - GTK_SHRINK | GTK_FILL - - - - - True - False - 0 - 3 - 3 - Captionwriter - - - 5 - 6 - GTK_SHRINK | GTK_FILL - - - - - True - False - 0 - 0 - 3 - 3 - Headline - - - 6 - 7 - GTK_SHRINK | GTK_FILL - - - - - True - False - 0 - 0 - 3 - 3 - Special -Instructions - - - 7 - 8 - GTK_SHRINK | GTK_FILL - - - - - True - True - - True - - - 1 - 2 - - - - - True - True - - True - - - 1 - 2 - 1 - 2 - - - - - True - True - - True - - - 1 - 2 - 2 - 3 - - - - - True - True - - True - - - 1 - 2 - 3 - 4 - - - - - True - True - - True - - - 1 - 2 - 5 - 6 - - - - - True - True - in - - - True - True - - - - - 1 - 2 - 4 - 5 - - - - - True - True - in - - - True - True - - - - - 1 - 2 - 6 - 7 - - - - - True - True - in - - - True - True - - - - - 1 - 2 - 7 - 8 - - - - - False - True - 0 - - - - - - - True - False - 2 - 2 - Description - - - False - - - - - True - False - - - True - False - 6 - 4 - 2 - 3 - 3 - - - True - False - 0 - 0 - 3 - 3 - Keywords - - - GTK_SHRINK | GTK_FILL - - - - - True - False - 0 - 3 - 3 - Category - - - 1 - 2 - GTK_SHRINK | GTK_FILL - - - - - True - False - 0 - 0 - 3 - 3 - Supplemental -Category - - - 2 - 3 - GTK_SHRINK | GTK_FILL - - - - - True - False - 0 - 3 - 3 - Urgency - - - 3 - 4 - GTK_SHRINK | GTK_FILL - - - - - True - True - in - - - True - True - - - - - 1 - 2 - - - - - True - True - in - - - True - True - - - - - 1 - 2 - 2 - 3 - - - - - True - True - - True - - - 1 - 2 - 3 - 4 - - - - - True - True - - True - - - 1 - 2 - 1 - 2 - - - - - False - True - 0 - - - - - 1 - - - - - True - False - 2 - 2 - Keywords/Categories - - - 1 - False - - - - - True - False - - - True - False - 6 - 9 - 2 - 3 - 3 - - - True - False - - - 2 - 2 - 3 - - - - - True - False - 0 - 3 - 3 - Credit - - - GTK_SHRINK | GTK_FILL - - - - - True - False - 0 - 3 - 3 - Source - - - 1 - 2 - GTK_SHRINK | GTK_FILL - - - - - True - True - - True - - - 1 - 2 - - - - - True - True - - True - - - 1 - 2 - 1 - 2 - - - - - True - False - 0 - 3 - 3 - Transmission -reference - - - 8 - 9 - GTK_SHRINK | GTK_FILL - - - - - True - True - - True - - - 1 - 2 - 8 - 9 - - - - - True - False - 0 - 3 - 3 - City - - - 3 - 4 - GTK_SHRINK | GTK_FILL - - - - - True - True - - True - - - 1 - 2 - 3 - 4 - - - - - True - False - 0 - 3 - 3 - Sublocation - - - 4 - 5 - GTK_SHRINK | GTK_FILL - - - - - True - True - - True - - - 1 - 2 - 4 - 5 - - - - - True - False - 0 - 3 - 3 - Province/State - - - 5 - 6 - GTK_SHRINK | GTK_FILL - - - - - True - True - - True - - - 1 - 2 - 5 - 6 - - - - - True - False - 0 - Country - - - 6 - 7 - GTK_SHRINK | GTK_FILL - - - - - True - True - - True - - - 1 - 2 - 6 - 7 - - - - - True - False - - - 2 - 7 - 8 - - - - - False - True - 0 - - - - - 2 - - - - - True - False - 2 - 2 - Credits/Origin - - - 2 - False - - - - - False - True - 0 - - - - - Write IPTC Data - True - True - True - - - False - True - 1 - - - - - 2 - - - - - True - False - 4 - 4 - IPTC - - - 2 - False - - - - - True - True - 0 - - - - diff --git a/po-plug-ins/POTFILES.in b/po-plug-ins/POTFILES.in index 2d3c511c05..10c4437ee9 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 b6f5ff99f8..2aff3cff7d 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]);