mirror of https://github.com/GNOME/gimp.git
2003 lines
49 KiB
C
2003 lines
49 KiB
C
/* LIBGIMP - The GIMP Library
|
|
* Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
|
|
*
|
|
* gimpattribute.c
|
|
* Copyright (C) 2014 Hartmut Kuhse <hatti@gimp.org>
|
|
*
|
|
* This library is free software: you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 3 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library. If not, see
|
|
* <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <stdio.h>
|
|
#ifdef HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <glib-object.h>
|
|
|
|
#ifdef G_OS_WIN32
|
|
#endif
|
|
|
|
#include "gimpbasetypes.h"
|
|
#include "gimpattribute.h"
|
|
#include "gimprational.h"
|
|
|
|
#define _g_free0(var) (var = (g_free (var), NULL))
|
|
#define _g_regex_unref0(var) ((var == NULL) ? NULL : (var = (g_regex_unref (var), NULL)))
|
|
#define _g_error_free0(var) ((var == NULL) ? NULL : (var = (g_error_free (var), NULL)))
|
|
|
|
typedef struct _TagTime TagTime;
|
|
typedef struct _TagDate TagDate;
|
|
|
|
struct _TagTime {
|
|
int tag_time_sec;
|
|
int tag_time_min;
|
|
int tag_time_hour;
|
|
int tag_tz_hour;
|
|
int tag_tz_min;
|
|
};
|
|
|
|
struct _TagDate {
|
|
int tag_year;
|
|
int tag_month;
|
|
int tag_day;
|
|
};
|
|
|
|
|
|
typedef struct _GimpAttributeClass GimpAttributeClass;
|
|
typedef struct _GimpAttributePrivate GimpAttributePrivate;
|
|
|
|
struct _GimpAttribute {
|
|
GObject parent_instance;
|
|
GimpAttributePrivate *priv;
|
|
};
|
|
|
|
struct _GimpAttributeClass {
|
|
GObjectClass parent_class;
|
|
};
|
|
|
|
struct _GimpAttributePrivate {
|
|
gchar *name;
|
|
gchar *sorted_name;
|
|
|
|
gchar *tag_value;
|
|
gchar *interpreted_value;
|
|
|
|
gchar *exif_type;
|
|
|
|
GimpAttributeValueType value_type;
|
|
GimpAttributeTagType tag_type;
|
|
|
|
gchar *attribute_type;
|
|
gchar *attribute_ifd;
|
|
gchar *attribute_tag;
|
|
gboolean is_new_name_space;
|
|
|
|
gboolean has_structure;
|
|
GimpAttributeStructureType structure_type;
|
|
GSList *attribute_structure;
|
|
};
|
|
|
|
enum {
|
|
PROP_0,
|
|
PROP_NAME,
|
|
PROP_LONG_VALUE,
|
|
PROP_SLONG_VALUE,
|
|
PROP_SHORT_VALUE,
|
|
PROP_SSHORT_VALUE,
|
|
PROP_DATE_VALUE,
|
|
PROP_TIME_VALUE,
|
|
PROP_ASCII_VALUE,
|
|
PROP_BYTE_VALUE,
|
|
PROP_MULTIPLE_VALUE,
|
|
PROP_RATIONAL_VALUE,
|
|
PROP_SRATIONAL_VALUE
|
|
};
|
|
|
|
static const gchar *xmp_namespaces[] =
|
|
{
|
|
"dc",
|
|
"xmp",
|
|
"xmpRights",
|
|
"xmpMM",
|
|
"xmpBJ",
|
|
"xmpTPg",
|
|
"xmpDM",
|
|
"pdf",
|
|
"photoshop",
|
|
"crs",
|
|
"tiff",
|
|
"exif",
|
|
"aux",
|
|
"plus",
|
|
"digiKam",
|
|
"iptc",
|
|
"iptcExt",
|
|
"Iptc4xmpCore",
|
|
"Iptc4xmpExt",
|
|
"MicrosoftPhoto",
|
|
"kipi",
|
|
"mediapro",
|
|
"expressionmedia",
|
|
"MP",
|
|
"MPRI",
|
|
"MPReg",
|
|
"mwg-rs"
|
|
};
|
|
|
|
|
|
static gpointer gimp_attribute_parent_class = NULL;
|
|
static gint counter = 0;
|
|
|
|
|
|
static GimpAttribute* gimp_attribute_construct (GType object_type,
|
|
const gchar *name);
|
|
static void gimp_attribute_finalize (GObject *obj);
|
|
static void gimp_attribute_get_property (GObject *object,
|
|
guint property_id,
|
|
GValue *value,
|
|
GParamSpec *pspec);
|
|
static void gimp_attribute_set_property (GObject *object,
|
|
guint property_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec);
|
|
static void gimp_attribute_set_name (GimpAttribute *attribute,
|
|
const gchar *value);
|
|
static gboolean get_tag_time (gchar *input,
|
|
TagTime *tm);
|
|
static gboolean get_tag_date (gchar *input,
|
|
TagDate *dt);
|
|
static gchar * gimp_attribute_escape_value (gchar *name,
|
|
gchar *value,
|
|
gboolean *encoded);
|
|
static gchar * gimp_attribute_get_structure_number (gchar *cptag,
|
|
gint start,
|
|
gint *struct_number);
|
|
|
|
static gchar* string_replace_str (const gchar *original,
|
|
const gchar *old_pattern,
|
|
const gchar *replacement);
|
|
static gint string_index_of (gchar *haystack,
|
|
gchar *needle,
|
|
gint start_index);
|
|
static glong string_strnlen (gchar *str,
|
|
glong maxlen);
|
|
static gchar* string_substring (gchar *attribute,
|
|
glong offset,
|
|
glong len);
|
|
|
|
|
|
/**
|
|
* gimp_attribute_new_string:
|
|
*
|
|
* @name: a constant #gchar that describes the name of the attribute
|
|
* @value: a #gchar value, representing a time
|
|
* @type: a #GimpAttributeTagType
|
|
*
|
|
* Creates a new #GimpAttribute object with @name as name and a #gchar @value
|
|
* as value of type @type.
|
|
* @value is converted to the correct type.
|
|
*
|
|
* Return value: a new @GimpAttribute
|
|
*
|
|
* Since: 2.10
|
|
*/
|
|
GimpAttribute*
|
|
gimp_attribute_new_string (const gchar *name,
|
|
gchar *value,
|
|
GimpAttributeValueType type)
|
|
{
|
|
GimpAttribute *attribute = NULL;
|
|
GimpAttributePrivate *private;
|
|
|
|
attribute = gimp_attribute_construct (GIMP_TYPE_ATTRIBUTE, name);
|
|
if (attribute)
|
|
{
|
|
private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
|
|
private->tag_value = g_strdup (value);
|
|
private->value_type = type;
|
|
}
|
|
|
|
return attribute;
|
|
}
|
|
|
|
/**
|
|
* gimp_attribute_get_name:
|
|
*
|
|
* @attribute: a @GimpAttribute
|
|
*
|
|
* Return value: full name of the #GimpAttribute object
|
|
* e.g. "Exif.Image.XResolution"
|
|
*
|
|
* Since: 2.10
|
|
*/
|
|
const gchar*
|
|
gimp_attribute_get_name (GimpAttribute* attribute)
|
|
{
|
|
GimpAttributePrivate *private;
|
|
g_return_val_if_fail (attribute != NULL, NULL);
|
|
private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
|
|
return (const gchar *) private->name;
|
|
}
|
|
|
|
/**
|
|
* gimp_attribute_get_sortable_name:
|
|
*
|
|
* @attribute: a @GimpAttribute
|
|
*
|
|
* Return value: name of the #GimpAttribute object for sorting
|
|
* e.g. from tag:
|
|
* "xmp.xmpMM.History[2]..."
|
|
* it returns:
|
|
* "xmp.xmpMM.History[000002]..."
|
|
*
|
|
* Since: 2.10
|
|
*/
|
|
const gchar*
|
|
gimp_attribute_get_sortable_name (GimpAttribute* attribute)
|
|
{
|
|
GimpAttributePrivate *private;
|
|
g_return_val_if_fail (attribute != NULL, NULL);
|
|
private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
|
|
if (private->sorted_name)
|
|
return (const gchar *) private->sorted_name;
|
|
else
|
|
return (const gchar *) private->name;
|
|
}
|
|
|
|
/**
|
|
* gimp_attribute_get_attribute_type:
|
|
*
|
|
* @attribute: a @GimpAttribute
|
|
*
|
|
* Return value: attribute type of the #GimpAttribute object
|
|
* e.g. "exif", "iptc", ...
|
|
*
|
|
* Since: 2.10
|
|
*/
|
|
const gchar*
|
|
gimp_attribute_get_attribute_type (GimpAttribute* attribute)
|
|
{
|
|
GimpAttributePrivate *private;
|
|
g_return_val_if_fail (attribute != NULL, NULL);
|
|
private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
|
|
return (const gchar *) private->attribute_type;
|
|
}
|
|
|
|
/**
|
|
* gimp_attribute_get_attribute_ifd:
|
|
*
|
|
* @attribute: a @GimpAttribute
|
|
*
|
|
* Return value: ifd of the #GimpAttribute object
|
|
*
|
|
* Since: 2.10
|
|
*/
|
|
const gchar*
|
|
gimp_attribute_get_attribute_ifd (GimpAttribute* attribute)
|
|
{
|
|
GimpAttributePrivate *private;
|
|
g_return_val_if_fail (attribute != NULL, NULL);
|
|
private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
|
|
return (const gchar *) private->attribute_ifd;
|
|
}
|
|
|
|
/**
|
|
* gimp_attribute_get_attribute_tag:
|
|
*
|
|
* @attribute: a @GimpAttribute
|
|
*
|
|
* Return value: tag of the #GimpAttribute object
|
|
*
|
|
* Since: 2.10
|
|
*/
|
|
const gchar*
|
|
gimp_attribute_get_attribute_tag (GimpAttribute* attribute)
|
|
{
|
|
GimpAttributePrivate *private;
|
|
g_return_val_if_fail (attribute != NULL, NULL);
|
|
private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
|
|
return (const gchar *) private->attribute_tag;
|
|
}
|
|
|
|
/**
|
|
* gimp_attribute_set_value_type:
|
|
*
|
|
* @attribute: a @GimpAttribute
|
|
* @value_type: a @GimpAttributeValueType
|
|
*
|
|
* sets value type of the #GimpAttribute object
|
|
*
|
|
* Since: 2.10
|
|
*/
|
|
void
|
|
gimp_attribute_set_value_type (GimpAttribute* attribute,
|
|
GimpAttributeValueType value_type)
|
|
{
|
|
GimpAttributePrivate *private;
|
|
g_return_if_fail (attribute != NULL);
|
|
private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
|
|
private->value_type = value_type;
|
|
}
|
|
|
|
/**
|
|
* gimp_attribute_get_value_type:
|
|
*
|
|
* @attribute: a @GimpAttribute
|
|
*
|
|
* Return value: value type of the #GimpAttribute object
|
|
*
|
|
* Since: 2.10
|
|
*/
|
|
GimpAttributeValueType
|
|
gimp_attribute_get_value_type (GimpAttribute* attribute)
|
|
{
|
|
GimpAttributePrivate *private;
|
|
g_return_val_if_fail (attribute != NULL, TYPE_INVALID);
|
|
private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
|
|
return private->value_type;
|
|
}
|
|
|
|
/**
|
|
* gimp_attribute_get_attribute_structure:
|
|
*
|
|
* @attribute: a @GimpAttribute
|
|
*
|
|
* Return value: attribute_structure of the #GimpAttribute object
|
|
* This is the start value of a xmpBag sequence.
|
|
*
|
|
* Since: 2.10
|
|
*/
|
|
GSList *
|
|
gimp_attribute_get_attribute_structure (GimpAttribute* attribute)
|
|
{
|
|
GimpAttributePrivate *private;
|
|
g_return_val_if_fail (attribute != NULL, NULL);
|
|
private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
|
|
|
|
return private->attribute_structure;
|
|
}
|
|
/**
|
|
* gimp_attribute_is_new_namespace:
|
|
*
|
|
* @attribute: a @GimpAttribute
|
|
*
|
|
* Return value: if namespace must be defined or not
|
|
*
|
|
* Since: 2.10
|
|
*/
|
|
const gboolean
|
|
gimp_attribute_is_new_namespace (GimpAttribute* attribute)
|
|
{
|
|
GimpAttributePrivate *private;
|
|
g_return_val_if_fail (attribute != NULL, FALSE);
|
|
private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
|
|
return (const gboolean) private->is_new_name_space;
|
|
}
|
|
|
|
/**
|
|
* gimp_attribute_get_value:
|
|
*
|
|
* @attribute: a @GimpAttribute
|
|
*
|
|
* Return value: #GValue of the #GimpAttribute object
|
|
* The type of value is represented by glib-types
|
|
*
|
|
* Since: 2.10
|
|
*/
|
|
GValue
|
|
gimp_attribute_get_value (GimpAttribute *attribute)
|
|
{
|
|
GimpAttributePrivate *private;
|
|
GValue val = G_VALUE_INIT;
|
|
gchar *value_string;
|
|
|
|
g_return_val_if_fail(GIMP_IS_ATTRIBUTE (attribute), val);
|
|
|
|
private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
|
|
value_string = private->tag_value;
|
|
|
|
switch (private->value_type)
|
|
{
|
|
case TYPE_LONG:
|
|
{
|
|
g_value_init (&val, G_TYPE_ULONG);
|
|
g_value_set_ulong (&val, (gulong) atol (value_string));
|
|
}
|
|
break;
|
|
case TYPE_SLONG:
|
|
{
|
|
g_value_init (&val, G_TYPE_LONG);
|
|
g_value_set_long (&val, (glong) atol (value_string));
|
|
}
|
|
break;
|
|
case TYPE_SHORT:
|
|
{
|
|
g_value_init (&val, G_TYPE_UINT);
|
|
g_value_set_uint (&val, (guint) atoi (value_string));
|
|
}
|
|
break;
|
|
case TYPE_SSHORT:
|
|
{
|
|
g_value_init (&val, G_TYPE_INT);
|
|
g_value_set_int (&val, (gint) atoi (value_string));
|
|
}
|
|
break;
|
|
case TYPE_DATE:
|
|
{
|
|
g_value_init (&val, G_TYPE_STRING);
|
|
g_value_set_string (&val, value_string);
|
|
}
|
|
break;
|
|
case TYPE_TIME:
|
|
{
|
|
g_value_init (&val, G_TYPE_STRING);
|
|
g_value_set_string (&val, value_string);
|
|
}
|
|
break;
|
|
case TYPE_ASCII:
|
|
{
|
|
g_value_init (&val, G_TYPE_STRING);
|
|
g_value_set_string (&val, value_string);
|
|
}
|
|
break;
|
|
case TYPE_UNICODE:
|
|
{
|
|
g_value_init (&val, G_TYPE_STRING);
|
|
g_value_set_string (&val, value_string);
|
|
}
|
|
break;
|
|
case TYPE_BYTE:
|
|
{
|
|
g_value_init (&val, G_TYPE_UCHAR);
|
|
g_value_set_uchar (&val, value_string[0]);
|
|
}
|
|
break;
|
|
case TYPE_MULTIPLE:
|
|
{
|
|
gchar **result;
|
|
|
|
g_value_init (&val, G_TYPE_STRV);
|
|
result = g_strsplit (value_string, "\n", 0);
|
|
g_value_set_boxed (&val, result);
|
|
}
|
|
break;
|
|
case TYPE_RATIONAL:
|
|
{
|
|
gchar **nom;
|
|
gchar **rats;
|
|
gint count;
|
|
gint i;
|
|
Rational *rval;
|
|
GArray *rational_array;
|
|
|
|
rats = g_strsplit (value_string, " ", -1);
|
|
|
|
count = 0;
|
|
while (rats[count])
|
|
count++;
|
|
|
|
rational_array = g_array_new (TRUE, TRUE, sizeof (RationalValue));
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
RationalValue or;
|
|
|
|
nom = g_strsplit (rats[i], "/", 2);
|
|
|
|
if (nom[0] && nom[1])
|
|
{
|
|
or.nom = (guint) atoi (nom [0]);
|
|
or.denom = (guint) atoi (nom [1]);
|
|
}
|
|
else
|
|
count--;
|
|
|
|
rational_array = g_array_append_val (rational_array, or);
|
|
|
|
g_strfreev (nom);
|
|
}
|
|
|
|
rval = g_slice_new (Rational);
|
|
|
|
rval->rational_array = rational_array;
|
|
rval->length = count;
|
|
|
|
g_value_init (&val, GIMP_TYPE_RATIONAL);
|
|
g_value_set_boxed (&val, rval);
|
|
|
|
g_strfreev (rats);
|
|
}
|
|
break;
|
|
case TYPE_SRATIONAL:
|
|
{
|
|
gchar **nom;
|
|
gchar **srats;
|
|
gint count;
|
|
gint i;
|
|
SRational *srval;
|
|
GArray *srational_array;
|
|
|
|
srats = g_strsplit (value_string, " ", -1);
|
|
|
|
count = 0;
|
|
while (srats[count])
|
|
count++;
|
|
|
|
srational_array = g_array_new (TRUE, TRUE, sizeof (SRationalValue));
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
SRationalValue or;
|
|
|
|
nom = g_strsplit (srats[i], "/", 2);
|
|
|
|
if (nom[0] && nom[1])
|
|
{
|
|
or.nom = (gint) atoi (nom [0]);
|
|
or.denom = (guint) atoi (nom [1]);
|
|
}
|
|
else
|
|
count--;
|
|
|
|
srational_array = g_array_append_val (srational_array, or);
|
|
|
|
g_strfreev (nom);
|
|
}
|
|
|
|
srval = g_slice_new (SRational);
|
|
|
|
srval->srational_array = srational_array;
|
|
srval->length = count;
|
|
|
|
g_value_init (&val, GIMP_TYPE_SRATIONAL);
|
|
g_value_set_boxed (&val, srval);
|
|
|
|
g_strfreev (srats);
|
|
}
|
|
break;
|
|
case TYPE_UNKNOWN:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
/**
|
|
* gimp_attribute_get_string:
|
|
*
|
|
* @attribute: a @GimpAttribute
|
|
*
|
|
* Return value: newly allocated #gchar string representation of the #GimpAttribute object.
|
|
* The return is always a valid value according to the #GimpAttributeValueType
|
|
*
|
|
* Since: 2.10
|
|
*/
|
|
gchar*
|
|
gimp_attribute_get_string (GimpAttribute *attribute)
|
|
{
|
|
GimpAttributePrivate *private;
|
|
gchar *val = NULL;
|
|
TagDate dt = {0};
|
|
TagTime tm = {0};
|
|
|
|
g_return_val_if_fail(GIMP_IS_ATTRIBUTE (attribute), NULL);
|
|
|
|
private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
|
|
|
|
switch (private->value_type)
|
|
{
|
|
case TYPE_INVALID:
|
|
break;
|
|
case TYPE_LONG:
|
|
{
|
|
val = g_strdup_printf ("%lu", (gulong) atol (private->tag_value));
|
|
}
|
|
break;
|
|
case TYPE_SLONG:
|
|
{
|
|
val = g_strdup_printf ("%ld", (glong) atol (private->tag_value));
|
|
}
|
|
break;
|
|
case TYPE_FLOAT:
|
|
{
|
|
val = g_strdup_printf ("%.5f", (gfloat) atof (private->tag_value));
|
|
}
|
|
break;
|
|
case TYPE_DOUBLE:
|
|
{
|
|
val = g_strdup_printf ("%.6f", (gdouble) atof (private->tag_value));
|
|
}
|
|
break;
|
|
case TYPE_SHORT:
|
|
{
|
|
val = g_strdup_printf ("%u", (gushort) atoi (private->tag_value));
|
|
}
|
|
break;
|
|
case TYPE_SSHORT:
|
|
{
|
|
val = g_strdup_printf ("%d", (gshort) atoi (private->tag_value));
|
|
}
|
|
break;
|
|
case TYPE_DATE:
|
|
{
|
|
get_tag_date (private->tag_value, &dt);
|
|
|
|
val = g_strdup_printf ("%4d-%02d-%02d", dt.tag_year,
|
|
dt.tag_month,
|
|
dt.tag_day);
|
|
}
|
|
break;
|
|
case TYPE_TIME:
|
|
{
|
|
gchar *tz_h = NULL;
|
|
get_tag_time (private->tag_value, &tm);
|
|
|
|
tz_h = tm.tag_tz_hour < 0 ?
|
|
g_strdup_printf ("-%02d", tm.tag_tz_hour) :
|
|
g_strdup_printf ("+%02d", tm.tag_tz_hour);
|
|
|
|
val = g_strdup_printf ("%02d:%02d:%02d%s:%02d", tm.tag_time_hour,
|
|
tm.tag_time_min,
|
|
tm.tag_time_sec,
|
|
tz_h,
|
|
tm.tag_tz_min);
|
|
g_free (tz_h);
|
|
}
|
|
break;
|
|
case TYPE_ASCII:
|
|
{
|
|
val = g_strdup (private->tag_value);
|
|
}
|
|
break;
|
|
case TYPE_UNICODE:
|
|
{
|
|
val = g_strdup (private->tag_value);
|
|
}
|
|
break;
|
|
case TYPE_BYTE:
|
|
{
|
|
val = g_strdup_printf ("%c", private->tag_value[0]);
|
|
}
|
|
break;
|
|
case TYPE_MULTIPLE:
|
|
{
|
|
val = g_strdup (private->tag_value);
|
|
}
|
|
break;
|
|
case TYPE_RATIONAL:
|
|
{
|
|
gint i;
|
|
GString *string;
|
|
Rational *rational;
|
|
|
|
string_to_rational (private->tag_value, &rational);
|
|
|
|
string = g_string_new (NULL);
|
|
|
|
for (i = 0; i < rational->length; i++)
|
|
{
|
|
RationalValue or;
|
|
|
|
or = g_array_index (rational->rational_array, RationalValue, i);
|
|
|
|
g_string_append_printf (string, "%u/%u ",
|
|
or.nom,
|
|
or.denom);
|
|
}
|
|
g_string_truncate (string, string->len-1);
|
|
val = g_string_free (string, FALSE);
|
|
}
|
|
break;
|
|
case TYPE_SRATIONAL:
|
|
{
|
|
gint i;
|
|
GString *string;
|
|
SRational *srational;
|
|
|
|
string_to_srational (private->tag_value, &srational);
|
|
|
|
string = g_string_new (NULL);
|
|
|
|
for (i = 0; i < srational->length; i++)
|
|
{
|
|
SRationalValue or;
|
|
|
|
or = g_array_index (srational->srational_array, SRationalValue, i);
|
|
|
|
g_string_append_printf (string, "%u/%u ",
|
|
or.nom,
|
|
or.denom);
|
|
}
|
|
|
|
g_string_truncate (string, string->len-1);
|
|
val = g_string_free (string, FALSE);
|
|
}
|
|
break;
|
|
case TYPE_UNKNOWN:
|
|
default:
|
|
break;
|
|
}
|
|
return val;
|
|
}
|
|
|
|
/**
|
|
* gimp_attribute_get_interpreted_string:
|
|
*
|
|
* @attribute: a #GimpAttribute
|
|
*
|
|
* returns the interpreted value or the pure string, if no
|
|
* interpreted value is found
|
|
*
|
|
* Since: 2.10
|
|
*/
|
|
const gchar *
|
|
gimp_attribute_get_interpreted_string (GimpAttribute *attribute)
|
|
{
|
|
GimpAttributePrivate *private;
|
|
|
|
g_return_val_if_fail(GIMP_IS_ATTRIBUTE (attribute), NULL);
|
|
|
|
private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
|
|
|
|
if (private->interpreted_value)
|
|
return (const gchar *) private->interpreted_value;
|
|
else
|
|
return (const gchar *) private->tag_value;
|
|
}
|
|
|
|
/**
|
|
* gimp_attribute_set_interpreted_string:
|
|
*
|
|
* @attribute: a #GimpAttribute
|
|
* @value: a constant #gchar
|
|
*
|
|
* sets the interpreted string of the #GimpAttribute object to
|
|
* a @value
|
|
*
|
|
* @value can be freed after
|
|
*
|
|
* Since: 2.10
|
|
*/
|
|
void
|
|
gimp_attribute_set_interpreted_string (GimpAttribute* attribute, const gchar* value)
|
|
{
|
|
GimpAttributePrivate *private;
|
|
|
|
g_return_if_fail(GIMP_IS_ATTRIBUTE (attribute));
|
|
g_return_if_fail (value != NULL);
|
|
|
|
private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
|
|
|
|
private->interpreted_value = g_strdup (value);
|
|
}
|
|
|
|
/**
|
|
* gimp_attribute_get_tag_type:
|
|
*
|
|
* @attribute: a @GimpAttribute
|
|
*
|
|
*
|
|
* Return value: #GimpAttributeTagType
|
|
* The tag type of the Attribute
|
|
*
|
|
* Since: 2.10
|
|
*/
|
|
GimpAttributeTagType
|
|
gimp_attribute_get_tag_type (GimpAttribute *attribute)
|
|
{
|
|
GimpAttributePrivate *private;
|
|
|
|
g_return_val_if_fail (attribute != NULL, TAG_INVALID);
|
|
|
|
private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
|
|
|
|
return private->tag_type;
|
|
}
|
|
|
|
void
|
|
gimp_attribute_print (GimpAttribute *attribute)
|
|
{
|
|
const gchar *tag;
|
|
const gchar *interpreted;
|
|
gchar *value;
|
|
|
|
g_return_if_fail(GIMP_IS_ATTRIBUTE (attribute));
|
|
|
|
tag = gimp_attribute_get_name (attribute);
|
|
value = gimp_attribute_get_string (attribute);
|
|
interpreted = gimp_attribute_get_interpreted_string (attribute);
|
|
|
|
g_print ("---\n%p\nTag: %s\n\tValue:%s\n\tInterpreted value:%s\n", attribute, tag, value, interpreted);
|
|
|
|
}
|
|
|
|
/**
|
|
* gimp_attribute_get_gps_degree:
|
|
*
|
|
* @attribute: a @GimpAttribute
|
|
*
|
|
* returns the degree representation of the #GimpAttribute GPS value,
|
|
* -999.9 otherwise.
|
|
*
|
|
* Return value: #gdouble degree representation of the #GimpAttributes GPS value
|
|
*
|
|
* Since: 2.10
|
|
*/
|
|
gdouble
|
|
gimp_attribute_get_gps_degree (GimpAttribute *attribute)
|
|
{
|
|
gdouble return_val;
|
|
GimpAttributePrivate *private;
|
|
|
|
g_return_val_if_fail (attribute != NULL, TAG_INVALID);
|
|
|
|
private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
|
|
|
|
if (private->value_type == TYPE_RATIONAL)
|
|
{
|
|
gint i;
|
|
gdouble r_val = 0.0;
|
|
Rational *rational;
|
|
|
|
string_to_rational (private->tag_value, &rational);
|
|
|
|
for (i = 0; i < rational->length; i++)
|
|
{
|
|
RationalValue or;
|
|
|
|
or = g_array_index (rational->rational_array, RationalValue, i);
|
|
|
|
r_val = (gfloat)or.nom / (gfloat)or.denom;
|
|
|
|
if (i == 0)
|
|
return_val = r_val;
|
|
else if (i == 1)
|
|
return_val = return_val + (r_val / 60);
|
|
else if (i == 2)
|
|
return_val = return_val + (r_val / 3600);
|
|
else return -999.9;
|
|
}
|
|
|
|
return return_val;
|
|
}
|
|
else
|
|
return -999.9;
|
|
|
|
}
|
|
|
|
/**
|
|
* gimp_attribute_get_xml:
|
|
*
|
|
* @attribute: a @GimpAttribute
|
|
*
|
|
* returns the xml representation of the #GimpAttribute
|
|
* in form:
|
|
*
|
|
* <tag name="name" type="GimpAttributeValueType">
|
|
* <value [encoding=base64]>value</value>
|
|
* [<interpreted [encoding=base64]>interpreted value</interpreted>]
|
|
* </tag>
|
|
*
|
|
* Return value: #gchar xml representation of the #GimpAttribute object
|
|
*
|
|
* Since: 2.10
|
|
*/
|
|
gchar*
|
|
gimp_attribute_get_xml (GimpAttribute *attribute)
|
|
{
|
|
gchar *v_escaped = NULL;
|
|
gchar *i_escaped = NULL;
|
|
gchar *interpreted_tag_elem = NULL;
|
|
gboolean is_interpreted = FALSE;
|
|
gboolean encoded;
|
|
gchar *start_tag_elem;
|
|
gchar *end_tag_elem;
|
|
gchar *value_tag_elem;
|
|
gchar *struct_tag_elem = NULL;
|
|
gboolean utf = TRUE;
|
|
GimpAttributePrivate *private;
|
|
GString *xml;
|
|
|
|
g_return_val_if_fail (GIMP_IS_ATTRIBUTE (attribute), NULL);
|
|
|
|
private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
|
|
|
|
xml = g_string_new (NULL);
|
|
|
|
if(private->interpreted_value && g_strcmp0 (private->tag_value, private->interpreted_value))
|
|
{
|
|
is_interpreted = TRUE;
|
|
}
|
|
|
|
v_escaped = gimp_attribute_escape_value (private->name, private->tag_value, &encoded);
|
|
|
|
start_tag_elem = g_strdup_printf (" <attribute name=\"%s\" type=\"%d\">\n", private->name, private->value_type);
|
|
end_tag_elem = g_strdup_printf (" </attribute>");
|
|
|
|
g_string_append (xml, start_tag_elem);
|
|
|
|
if (encoded)
|
|
{
|
|
value_tag_elem = g_strdup_printf (" <value encoding=\"base64\">%s</value>\n", v_escaped);
|
|
}
|
|
else
|
|
{
|
|
value_tag_elem = g_strdup_printf (" <value>%s</value>\n", v_escaped);
|
|
}
|
|
|
|
g_string_append (xml, value_tag_elem);
|
|
|
|
if (private->has_structure)
|
|
{
|
|
struct_tag_elem = g_strdup_printf (" <structure>%d</structure>\n", private->structure_type);
|
|
g_string_append (xml, struct_tag_elem);
|
|
}
|
|
|
|
if (is_interpreted && utf)
|
|
{
|
|
i_escaped = gimp_attribute_escape_value (private->name, private->interpreted_value, &encoded);
|
|
|
|
if (encoded)
|
|
{
|
|
interpreted_tag_elem = g_strdup_printf (" <interpreted encoding=\"base64\">%s</interpreted>\n", i_escaped);
|
|
}
|
|
else
|
|
{
|
|
interpreted_tag_elem = g_strdup_printf (" <interpreted>%s</interpreted>\n", i_escaped);
|
|
}
|
|
|
|
g_string_append (xml, interpreted_tag_elem);
|
|
}
|
|
|
|
g_string_append (xml, end_tag_elem);
|
|
|
|
g_free (v_escaped);
|
|
g_free (start_tag_elem);
|
|
g_free (end_tag_elem);
|
|
g_free (value_tag_elem);
|
|
if (i_escaped)
|
|
g_free (i_escaped);
|
|
if (interpreted_tag_elem)
|
|
g_free (interpreted_tag_elem);
|
|
if (struct_tag_elem)
|
|
g_free (interpreted_tag_elem);
|
|
|
|
return g_string_free (xml, FALSE);
|
|
}
|
|
|
|
/**
|
|
* gimp_attribute_get_value_type_from_string:
|
|
*
|
|
* @exiv_tag_type_string: a @gchar
|
|
*
|
|
* converts a string representation tag type from gexiv2/exiv2
|
|
* to a #GimpAttributeValueType
|
|
*
|
|
* Return value: #GimpAttributeValueType
|
|
*
|
|
* Since: 2.10
|
|
*/
|
|
GimpAttributeValueType
|
|
gimp_attribute_get_value_type_from_string (const gchar* string)
|
|
{
|
|
GimpAttributeValueType type;
|
|
gchar *lowchar;
|
|
|
|
if (! string)
|
|
return TYPE_INVALID;
|
|
|
|
lowchar = g_ascii_strdown (string, -1);
|
|
|
|
if (!g_strcmp0 (lowchar, "invalid"))
|
|
type = TYPE_INVALID;
|
|
else if (!g_strcmp0 (lowchar, "byte"))
|
|
type = TYPE_BYTE;
|
|
else if (!g_strcmp0 (lowchar, "ascii"))
|
|
type = TYPE_ASCII;
|
|
else if (!g_strcmp0 (lowchar, "short"))
|
|
type = TYPE_SHORT;
|
|
else if (!g_strcmp0 (lowchar, "long"))
|
|
type = TYPE_LONG;
|
|
else if (!g_strcmp0 (lowchar, "rational"))
|
|
type = TYPE_RATIONAL;
|
|
else if (!g_strcmp0 (lowchar, "sbyte"))
|
|
type = TYPE_BYTE;
|
|
else if (!g_strcmp0 (lowchar, "undefined"))
|
|
type = TYPE_ASCII;
|
|
else if (!g_strcmp0 (lowchar, "sshort"))
|
|
type = TYPE_SSHORT;
|
|
else if (!g_strcmp0 (lowchar, "slong"))
|
|
type = TYPE_SLONG;
|
|
else if (!g_strcmp0 (lowchar, "srational"))
|
|
type = TYPE_SRATIONAL;
|
|
else if (!g_strcmp0 (lowchar, "float"))
|
|
type = TYPE_FLOAT;
|
|
else if (!g_strcmp0 (lowchar, "double"))
|
|
type = TYPE_DOUBLE;
|
|
else if (!g_strcmp0 (lowchar, "ifd"))
|
|
type = TYPE_ASCII;
|
|
else if (!g_strcmp0 (lowchar, "string"))
|
|
type = TYPE_UNICODE;
|
|
else if (!g_strcmp0 (lowchar, "date"))
|
|
type = TYPE_DATE;
|
|
else if (!g_strcmp0 (lowchar, "time"))
|
|
type = TYPE_TIME;
|
|
else if (!g_strcmp0 (lowchar, "comment"))
|
|
type = TYPE_UNICODE;
|
|
else if (!g_strcmp0 (lowchar, "directory"))
|
|
type = TYPE_UNICODE;
|
|
else if (!g_strcmp0 (lowchar, "xmptext"))
|
|
type = TYPE_ASCII;
|
|
else if (!g_strcmp0 (lowchar, "xmpalt"))
|
|
type = TYPE_MULTIPLE;
|
|
else if (!g_strcmp0 (lowchar, "xmpbag"))
|
|
type = TYPE_MULTIPLE;
|
|
else if (!g_strcmp0 (lowchar, "xmpseq"))
|
|
type = TYPE_MULTIPLE;
|
|
else if (!g_strcmp0 (lowchar, "langalt"))
|
|
type = TYPE_MULTIPLE;
|
|
else
|
|
type = TYPE_INVALID;
|
|
|
|
g_free (lowchar);
|
|
|
|
return type;
|
|
}
|
|
|
|
/**
|
|
* gimp_attribute_is_valid:
|
|
*
|
|
* @attribute: a @GimpAttribute
|
|
*
|
|
* checks, if @attribute is valid.
|
|
* A @GimpAttribute is valid, if the @GimpAttributeValueType
|
|
* has a valid entry.
|
|
*
|
|
* Return value: @gboolean: TRUE if valid, FALSE otherwise
|
|
*
|
|
* Since: 2.10
|
|
*/
|
|
gboolean
|
|
gimp_attribute_is_valid (GimpAttribute *attribute)
|
|
{
|
|
GimpAttributePrivate *private;
|
|
|
|
g_return_val_if_fail (attribute != NULL, FALSE);
|
|
|
|
private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
|
|
|
|
if (private->value_type == TYPE_INVALID)
|
|
return FALSE;
|
|
else
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* internal functions
|
|
*/
|
|
|
|
/**
|
|
* gimp_attribute_set_name:
|
|
*
|
|
* @attribute: a #GimpAttribute
|
|
* @value: a constant #gchar
|
|
*
|
|
* sets the name of the #GimpAttribute object to
|
|
* a @value
|
|
*
|
|
* @value can be freed after
|
|
*
|
|
* Since: 2.10
|
|
*/
|
|
static void
|
|
gimp_attribute_set_name (GimpAttribute* attribute, const gchar* value)
|
|
{
|
|
gchar **split_name;
|
|
gchar *lowchar;
|
|
GimpAttributePrivate *private;
|
|
|
|
g_return_if_fail (attribute != NULL);
|
|
|
|
private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
|
|
|
|
_g_free0 (private->name);
|
|
|
|
private->name = g_strdup (value);
|
|
|
|
private->name = string_replace_str (private->name, "Iptc4xmpExt", "iptcExt");
|
|
private->name = string_replace_str (private->name, "Iptc4xmpCore", "iptc");
|
|
|
|
// if (! g_strcmp0 (private->name, "Exif.Image.ResolutionUnit"))
|
|
// {
|
|
// g_print ("found: %s\n", private->name);
|
|
// }
|
|
|
|
split_name = g_strsplit (private->name, ".", 3);
|
|
|
|
if (split_name [0])
|
|
private->attribute_type = split_name [0];
|
|
if (split_name [1])
|
|
private->attribute_ifd = split_name [1];
|
|
if (split_name [2])
|
|
private->attribute_tag = split_name [2];
|
|
|
|
attribute->priv->is_new_name_space = FALSE;
|
|
|
|
lowchar = g_ascii_strdown (private->attribute_type, -1);
|
|
|
|
if (!g_strcmp0 (lowchar, "exif"))
|
|
{
|
|
private->tag_type = TAG_EXIF;
|
|
}
|
|
else if (!g_strcmp0 (lowchar, "xmp"))
|
|
{
|
|
gint j;
|
|
gint p1 = 0;
|
|
gint p2 = 0;
|
|
gboolean is_known = FALSE;
|
|
gchar *structure_tag_name = NULL;
|
|
|
|
structure_tag_name = g_strdup (private->name);
|
|
|
|
private->tag_type = TAG_XMP;
|
|
|
|
while (p2 != -1)
|
|
{
|
|
p2 = string_index_of (structure_tag_name, "[", p1);
|
|
|
|
if (p2 > -1)
|
|
{
|
|
gchar *struct_string = NULL;
|
|
gint struct_number;
|
|
|
|
private->has_structure = TRUE;
|
|
private->structure_type = STRUCTURE_TYPE_BAG; /* there's no way to get the real type from gexiv2 */
|
|
|
|
struct_string = string_substring (structure_tag_name, 0, p2);
|
|
private->attribute_structure = g_slist_prepend (private->attribute_structure, struct_string);
|
|
structure_tag_name = gimp_attribute_get_structure_number (structure_tag_name, p2, &struct_number);
|
|
|
|
p1 = p2 + 1;
|
|
}
|
|
}
|
|
|
|
if (g_strcmp0 (private->name, structure_tag_name))
|
|
private->sorted_name = structure_tag_name;
|
|
else
|
|
private->sorted_name = NULL;
|
|
|
|
for (j = 0; j < G_N_ELEMENTS (xmp_namespaces); j++)
|
|
{
|
|
if (! g_strcmp0 (private->attribute_ifd, xmp_namespaces[j]))
|
|
{
|
|
is_known = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if (! is_known)
|
|
{
|
|
private->is_new_name_space = TRUE;
|
|
}
|
|
}
|
|
else if (!g_strcmp0 (lowchar, "iptc"))
|
|
{
|
|
private->tag_type = TAG_IPTC;
|
|
}
|
|
else if (!g_strcmp0 (lowchar, "gimp"))
|
|
{
|
|
private->tag_type = TAG_GIMP;
|
|
}
|
|
else
|
|
{
|
|
private->tag_type = TAG_MISC;
|
|
}
|
|
|
|
g_free (lowchar);
|
|
}
|
|
|
|
/**
|
|
* gimp_attribute_set_structure_type:
|
|
*
|
|
* @attribute: a #GimpAttribute
|
|
* @value: a GimpAttributeStructureType
|
|
*
|
|
* Sets the structure type, Bag, Seq, None, etc.
|
|
* The structure is not checked.
|
|
* The structure type is only relevant for
|
|
* GimpAttribute, that are part of a structure.
|
|
*
|
|
* Since : 2.10
|
|
*/
|
|
void
|
|
gimp_attribute_set_structure_type (GimpAttribute *attribute,
|
|
GimpAttributeStructureType value)
|
|
{
|
|
GimpAttributePrivate *private;
|
|
|
|
g_return_if_fail (attribute != NULL);
|
|
|
|
private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
|
|
|
|
private->structure_type = value;
|
|
}
|
|
|
|
/**
|
|
* gimp_attribute_get_structure_type:
|
|
*
|
|
* @attribute: a #GimpAttribute
|
|
*
|
|
* The structure type, Bag, Seq, None, etc.
|
|
* Return value: a #GimpAttributeStructureType
|
|
*
|
|
* Since : 2.10
|
|
*/
|
|
GimpAttributeStructureType
|
|
gimp_attribute_get_structure_type (GimpAttribute *attribute)
|
|
{
|
|
GimpAttributePrivate *private;
|
|
|
|
g_return_val_if_fail (attribute != NULL, STRUCTURE_TYPE_NONE);
|
|
|
|
private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
|
|
|
|
return private->structure_type;
|
|
|
|
}
|
|
|
|
/**
|
|
* gimp_attribute_has_structure:
|
|
*
|
|
* @attribute: a #GimpAttribute
|
|
*
|
|
* Return value: TRUE, if @attribute is part
|
|
* of a structure.
|
|
*
|
|
* Since : 2.10
|
|
*/
|
|
gboolean
|
|
gimp_attribute_has_structure (GimpAttribute *attribute)
|
|
{
|
|
GimpAttributePrivate *private;
|
|
|
|
g_return_val_if_fail (attribute != NULL, FALSE);
|
|
|
|
private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
|
|
|
|
return private->has_structure;
|
|
}
|
|
|
|
/**
|
|
* gimp_attribute_escape_value:
|
|
*
|
|
* @name: a gchar *
|
|
* @value: a gchar*
|
|
* @encoded: a pointer to a gboolean
|
|
*
|
|
* converts @value into an escaped string for GMarkup.
|
|
* If @value is not valid UTF-8, it is converted
|
|
* into a base64 coded string and @encoded is set to TRUE,
|
|
* otherwise FALSE
|
|
*
|
|
* Return value: a new allocated @gchar *
|
|
*
|
|
* Since : 2.10
|
|
*/
|
|
static gchar *
|
|
gimp_attribute_escape_value (gchar *name,
|
|
gchar *value,
|
|
gboolean *encoded)
|
|
{
|
|
|
|
if (!g_utf8_validate (value, -1, NULL))
|
|
{
|
|
gchar *enc_val = NULL;
|
|
|
|
*encoded = TRUE;
|
|
enc_val = g_base64_encode ((const guchar *) value,
|
|
strlen (value) + 1);
|
|
|
|
return enc_val;
|
|
}
|
|
*encoded = FALSE;
|
|
|
|
return g_markup_escape_text (value, -1);
|
|
}
|
|
|
|
/**
|
|
* gimp_attribute_copy:
|
|
*
|
|
* @attribute: a #GimpAttribute
|
|
*
|
|
* duplicates a #GimpAttribute object
|
|
*
|
|
* Return value: a new @GimpAttribute or %NULL
|
|
*
|
|
* Since : 2.10
|
|
*/
|
|
GimpAttribute*
|
|
gimp_attribute_copy (GimpAttribute *attribute)
|
|
{
|
|
GimpAttribute *new_attribute = NULL;
|
|
GimpAttributePrivate *private;
|
|
GimpAttributePrivate *new_private;
|
|
|
|
g_return_val_if_fail (GIMP_IS_ATTRIBUTE (attribute), NULL);
|
|
|
|
new_attribute = (GimpAttribute*) g_object_new (GIMP_TYPE_ATTRIBUTE, NULL);
|
|
|
|
if (new_attribute)
|
|
{
|
|
private = GIMP_ATTRIBUTE_GET_PRIVATE(attribute);
|
|
new_private = GIMP_ATTRIBUTE_GET_PRIVATE(new_attribute);
|
|
if (private->name)
|
|
new_private->name = g_strdup (private->name);
|
|
|
|
if (private->tag_value)
|
|
new_private->tag_value = g_strdup (private->tag_value);
|
|
|
|
if (private->interpreted_value)
|
|
new_private->interpreted_value = g_strdup (private->interpreted_value);
|
|
|
|
if (private->exif_type)
|
|
new_private->exif_type = g_strdup (private->exif_type);
|
|
|
|
if (private->attribute_type)
|
|
new_private->attribute_type = g_strdup (private->attribute_type);
|
|
|
|
if (private->attribute_ifd)
|
|
new_private->attribute_ifd = g_strdup (private->attribute_ifd);
|
|
|
|
if (private->attribute_tag)
|
|
new_private->attribute_tag = g_strdup (private->attribute_tag);
|
|
|
|
new_private->is_new_name_space = private->is_new_name_space;
|
|
new_private->value_type = private->value_type;
|
|
new_private->tag_type = private->tag_type;
|
|
|
|
return new_attribute;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* gimp_attribute_construct:
|
|
*
|
|
* @object_type: a #GType
|
|
* @name: a constant #gchar that describes the name of the attribute
|
|
*
|
|
* constructs a new #GimpAttribute object
|
|
*
|
|
* Return value: a new @GimpAttribute or %NULL if no @name is set
|
|
*
|
|
* Since : 2.10
|
|
*/
|
|
static GimpAttribute*
|
|
gimp_attribute_construct (GType object_type,
|
|
const gchar *name)
|
|
{
|
|
GimpAttribute * attribute = NULL;
|
|
|
|
g_return_val_if_fail (name != NULL, NULL);
|
|
|
|
attribute = (GimpAttribute*) g_object_new (object_type, NULL);
|
|
if (attribute)
|
|
{
|
|
gimp_attribute_set_name (attribute, name);
|
|
}
|
|
return attribute;
|
|
}
|
|
|
|
/**
|
|
* gimp_attribute_class_init:
|
|
*
|
|
* class initializer
|
|
*/
|
|
static void
|
|
gimp_attribute_class_init (GimpAttributeClass * klass)
|
|
{
|
|
gimp_attribute_parent_class = g_type_class_peek_parent (klass);
|
|
g_type_class_add_private (klass, sizeof(GimpAttributePrivate));
|
|
|
|
G_OBJECT_CLASS (klass)->get_property = gimp_attribute_get_property;
|
|
G_OBJECT_CLASS (klass)->set_property = gimp_attribute_set_property;
|
|
G_OBJECT_CLASS (klass)->finalize = gimp_attribute_finalize;
|
|
}
|
|
|
|
/**
|
|
* gimp_attribute_instance_init:
|
|
*
|
|
* instance initializer
|
|
*/
|
|
static void
|
|
gimp_attribute_instance_init (GimpAttribute * attribute)
|
|
{
|
|
GimpAttributePrivate *private;
|
|
attribute->priv = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
|
|
private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
|
|
|
|
private->name = NULL;
|
|
private->sorted_name = NULL;
|
|
private->tag_value = NULL;
|
|
private->interpreted_value = NULL;
|
|
private->exif_type = NULL;
|
|
private->value_type = TYPE_INVALID;
|
|
private->attribute_type = NULL;
|
|
private->attribute_ifd = NULL;
|
|
private->attribute_tag = NULL;
|
|
private->attribute_structure = NULL;
|
|
private->is_new_name_space = FALSE;
|
|
private->has_structure = FALSE;
|
|
private->structure_type = STRUCTURE_TYPE_NONE;
|
|
}
|
|
|
|
/**
|
|
* gimp_attribute_finalize:
|
|
*
|
|
* instance finalizer
|
|
*/
|
|
static void
|
|
gimp_attribute_finalize (GObject* obj)
|
|
{
|
|
GimpAttribute *attribute;
|
|
GimpAttributePrivate *private;
|
|
|
|
g_return_if_fail (GIMP_IS_ATTRIBUTE (obj));
|
|
|
|
attribute = GIMP_ATTRIBUTE (obj);
|
|
private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
|
|
|
|
counter++;
|
|
|
|
if(private->name)
|
|
_g_free0 (private->name);
|
|
|
|
if(private->sorted_name)
|
|
_g_free0 (private->sorted_name);
|
|
|
|
if(private->tag_value)
|
|
_g_free0 (private->tag_value);
|
|
|
|
if(private->interpreted_value)
|
|
_g_free0 (private->interpreted_value);
|
|
|
|
if(private->exif_type)
|
|
_g_free0 (private->exif_type);
|
|
|
|
if(private->attribute_type)
|
|
_g_free0 (private->attribute_type);
|
|
|
|
if(private->attribute_ifd)
|
|
_g_free0 (private->attribute_ifd);
|
|
|
|
if(private->attribute_tag)
|
|
_g_free0 (private->attribute_tag);
|
|
|
|
if(private->attribute_structure)
|
|
g_slist_free_full (private->attribute_structure, g_free);
|
|
|
|
G_OBJECT_CLASS (gimp_attribute_parent_class)->finalize (obj);
|
|
obj = NULL;
|
|
}
|
|
|
|
/**
|
|
* gimp_attribute_get_property
|
|
*
|
|
* instance get property
|
|
*/
|
|
static void
|
|
gimp_attribute_get_property (GObject * object,
|
|
guint property_id,
|
|
GValue * value,
|
|
GParamSpec * pspec)
|
|
{
|
|
}
|
|
|
|
/**
|
|
* gimp_attribute_set_property
|
|
*
|
|
* instance set property
|
|
*/
|
|
static void
|
|
gimp_attribute_set_property (GObject * object,
|
|
guint property_id,
|
|
const GValue * value,
|
|
GParamSpec * pspec)
|
|
{
|
|
}
|
|
|
|
static gchar *
|
|
gimp_attribute_get_structure_number (gchar *cptag, gint start, gint *number)
|
|
{
|
|
gint p1;
|
|
gchar *tag;
|
|
gchar *new_tag;
|
|
gchar *oldnr;
|
|
gchar *newnr;
|
|
|
|
start++;
|
|
|
|
tag = g_strdup (cptag);
|
|
|
|
p1 = string_index_of (tag, "]", start);
|
|
|
|
if (p1 > -1)
|
|
{
|
|
gchar *number_string = NULL;
|
|
gint len;
|
|
|
|
len = p1-start;
|
|
|
|
number_string = string_substring (tag, start, len);
|
|
|
|
*number = atoi (number_string);
|
|
|
|
oldnr = g_strdup_printf ("[%d]", *number);
|
|
newnr = g_strdup_printf ("[%06d]", *number);
|
|
new_tag = string_replace_str (tag, oldnr, newnr);
|
|
|
|
g_free (oldnr);
|
|
g_free (newnr);
|
|
g_free (tag);
|
|
g_free (number_string);
|
|
|
|
return new_tag;
|
|
}
|
|
else
|
|
{
|
|
*number = -1;
|
|
return NULL;
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* gimp_attribute_get_type
|
|
*
|
|
* Return value: #GimpAttribute type
|
|
*/
|
|
GType
|
|
gimp_attribute_get_type (void)
|
|
{
|
|
static volatile gsize gimp_attribute_type_id__volatile = 0;
|
|
if (g_once_init_enter(&gimp_attribute_type_id__volatile))
|
|
{
|
|
static const GTypeInfo g_define_type_info =
|
|
{ sizeof(GimpAttributeClass),
|
|
(GBaseInitFunc) NULL,
|
|
(GBaseFinalizeFunc) NULL,
|
|
(GClassInitFunc) gimp_attribute_class_init,
|
|
(GClassFinalizeFunc) NULL,
|
|
NULL,
|
|
sizeof(GimpAttribute),
|
|
0,
|
|
(GInstanceInitFunc) gimp_attribute_instance_init,
|
|
NULL
|
|
};
|
|
|
|
GType gimp_attribute_type_id;
|
|
gimp_attribute_type_id = g_type_register_static (G_TYPE_OBJECT,
|
|
"GimpAttribute",
|
|
&g_define_type_info,
|
|
0);
|
|
|
|
g_once_init_leave(&gimp_attribute_type_id__volatile,
|
|
gimp_attribute_type_id);
|
|
}
|
|
return gimp_attribute_type_id__volatile;
|
|
}
|
|
|
|
|
|
/*
|
|
* Helper functions
|
|
*/
|
|
|
|
/**
|
|
* get_tag_time:
|
|
*
|
|
* @input: a #gchar array
|
|
* @tm: a pointer to a #TagTime struct
|
|
*
|
|
* converts a ISO 8601 IPTC Time to a TagTime structure
|
|
* Input is:
|
|
* HHMMSS
|
|
* HHMMSS:HHMM
|
|
*
|
|
* Return value: #gboolean for success
|
|
*
|
|
* Since: 2.10
|
|
*/
|
|
static gboolean
|
|
get_tag_time (gchar *input, TagTime *tm)
|
|
{
|
|
long val;
|
|
GString *string = NULL;
|
|
gchar *tmpdate;
|
|
gboolean flong;
|
|
|
|
g_return_val_if_fail (input != NULL, FALSE);
|
|
|
|
input = g_strstrip (input);
|
|
|
|
if (*input == '\0' || !g_ascii_isdigit (*input))
|
|
return FALSE;
|
|
|
|
string = g_string_new (NULL);
|
|
|
|
val = strtoul (input, (char **)&input, 10);
|
|
|
|
if (val < 25) /* HH:MM:SS+/-HH:MM */
|
|
{
|
|
flong = FALSE;
|
|
g_string_append_printf (string, "%02ld", val);
|
|
}
|
|
else
|
|
{
|
|
flong = TRUE;
|
|
g_string_append_printf (string, "%06ld", val);
|
|
}
|
|
|
|
|
|
if (! flong) /* exactly 2 times */
|
|
{
|
|
input++;
|
|
val = strtoul (input, (char **)&input, 10);
|
|
g_string_append_printf (string, "%02ld", val);
|
|
input++;
|
|
val = strtoul (input, (char **)&input, 10);
|
|
g_string_append_printf (string, "%02ld", val);
|
|
}
|
|
|
|
tmpdate = g_string_free (string, FALSE);
|
|
|
|
val = strtoul (tmpdate, (char **)&tmpdate, 10);
|
|
|
|
/* hhmmss */
|
|
|
|
tm->tag_time_sec = val % 100;
|
|
tm->tag_time_min = (val % 10000) / 100;
|
|
tm->tag_time_hour = val / 10000;
|
|
|
|
string = g_string_new (NULL);
|
|
|
|
if (flong) /* :HHSS for time zone */
|
|
{
|
|
val = 0L;
|
|
|
|
if (*input == ':')
|
|
{
|
|
input++;
|
|
val = strtoul (input, (char **)&input, 10);
|
|
}
|
|
g_string_append_printf (string, "%05ld", val);
|
|
}
|
|
else
|
|
{
|
|
val = 0L;
|
|
|
|
if (*input == '+' || *input == '-') /* +/- HH:MM for time zone */
|
|
{
|
|
val = strtoul (input, (char **)&input, 10);
|
|
g_string_append_printf (string, "%02ld", val);
|
|
input++;
|
|
val = strtoul (input, (char **)&input, 10);
|
|
g_string_append_printf (string, "%02ld", val);
|
|
}
|
|
else
|
|
{
|
|
g_string_append_printf (string, "%04ld", val);
|
|
}
|
|
}
|
|
|
|
tmpdate = g_string_free (string, FALSE);
|
|
|
|
val = strtoul (tmpdate, (char **)&tmpdate, 10);
|
|
|
|
tm->tag_tz_min = (val % 100) / 100;
|
|
tm->tag_tz_hour = val / 100;
|
|
|
|
return *input == '\0';
|
|
}
|
|
|
|
/**
|
|
* get_tag_date:
|
|
*
|
|
* @input: a #gchar array
|
|
* @dt: a pointer to a #TagDate struct
|
|
*
|
|
* converts a ISO 8601 IPTC Date to a TagDate structure
|
|
* Input is:
|
|
* CCYYMMDD
|
|
*
|
|
* Return value: #gboolean for success
|
|
*
|
|
* Since: 2.10
|
|
*/
|
|
static gboolean
|
|
get_tag_date (gchar *input, TagDate *dt)
|
|
{
|
|
long val;
|
|
GString *string = NULL;
|
|
gchar *tmpdate;
|
|
gboolean eod;
|
|
|
|
g_return_val_if_fail (input != NULL, FALSE);
|
|
|
|
input = g_strstrip (input);
|
|
|
|
if (*input == '\0' || !g_ascii_isdigit (*input))
|
|
return FALSE;
|
|
|
|
string = g_string_new (NULL);
|
|
eod = FALSE;
|
|
|
|
while (! eod)
|
|
{
|
|
val = strtoul (input, (char **)&input, 10);
|
|
|
|
if (*input == '-' ||
|
|
*input == ':')
|
|
{
|
|
input++;
|
|
}
|
|
else
|
|
{
|
|
eod = TRUE;
|
|
}
|
|
|
|
if (val < 10)
|
|
g_string_append_printf (string, "%02ld", val);
|
|
else
|
|
g_string_append_printf (string, "%ld", val);
|
|
}
|
|
tmpdate = g_string_free (string, FALSE);
|
|
|
|
val = strtoul (tmpdate, (char **)&tmpdate, 10);
|
|
|
|
/* YYYYMMDD */
|
|
dt->tag_day = val % 100;
|
|
dt->tag_month = (val % 10000) / 100;
|
|
dt->tag_year = val / 10000;
|
|
|
|
return *input == '\0';
|
|
}
|
|
|
|
/**
|
|
* string_replace_str:
|
|
*
|
|
* @original: the original string
|
|
* @old_pattern: the pattern to replace
|
|
* @replacement: the replacement
|
|
*
|
|
* replaces @old_pattern by @replacement in @original
|
|
* This routine is copied from VALA.
|
|
*
|
|
* Return value: the new string.
|
|
*
|
|
* Since: 2.10
|
|
*/
|
|
static gchar*
|
|
string_replace_str (const gchar* original, const gchar* old_pattern, const gchar* replacement) {
|
|
gchar *result = NULL;
|
|
GError *_inner_error_ = NULL;
|
|
|
|
g_return_val_if_fail (original != NULL, NULL);
|
|
g_return_val_if_fail (old_pattern != NULL, NULL);
|
|
g_return_val_if_fail (replacement != NULL, NULL);
|
|
|
|
{
|
|
GRegex *regex = NULL;
|
|
const gchar *_tmp0_ = NULL;
|
|
gchar *_tmp1_ = NULL;
|
|
gchar *_tmp2_ = NULL;
|
|
GRegex *_tmp3_ = NULL;
|
|
GRegex *_tmp4_ = NULL;
|
|
gchar *_tmp5_ = NULL;
|
|
GRegex *_tmp6_ = NULL;
|
|
const gchar *_tmp7_ = NULL;
|
|
gchar *_tmp8_ = NULL;
|
|
gchar *_tmp9_ = NULL;
|
|
|
|
_tmp0_ = old_pattern;
|
|
_tmp1_ = g_regex_escape_string (_tmp0_, -1);
|
|
_tmp2_ = _tmp1_;
|
|
_tmp3_ = g_regex_new (_tmp2_, 0, 0, &_inner_error_);
|
|
_tmp4_ = _tmp3_;
|
|
_g_free0 (_tmp2_);
|
|
regex = _tmp4_;
|
|
|
|
if (_inner_error_ != NULL)
|
|
{
|
|
if (_inner_error_->domain == G_REGEX_ERROR)
|
|
{
|
|
goto __catch0_g_regex_error;
|
|
}
|
|
g_critical ("file %s: line %d: unexpected error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code);
|
|
g_clear_error (&_inner_error_);
|
|
return NULL;
|
|
}
|
|
|
|
_tmp6_ = regex;
|
|
_tmp7_ = replacement;
|
|
_tmp8_ = g_regex_replace_literal (_tmp6_, original, (gssize) (-1), 0, _tmp7_, 0, &_inner_error_);
|
|
_tmp5_ = _tmp8_;
|
|
|
|
if (_inner_error_ != NULL)
|
|
{
|
|
_g_regex_unref0 (regex);
|
|
if (_inner_error_->domain == G_REGEX_ERROR)
|
|
{
|
|
goto __catch0_g_regex_error;
|
|
}
|
|
_g_regex_unref0 (regex);
|
|
g_critical ("file %s: line %d: unexpected error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code);
|
|
g_clear_error (&_inner_error_);
|
|
return NULL;
|
|
}
|
|
|
|
_tmp9_ = _tmp5_;
|
|
_tmp5_ = NULL;
|
|
result = _tmp9_;
|
|
_g_free0 (_tmp5_);
|
|
_g_regex_unref0 (regex);
|
|
return result;
|
|
}
|
|
|
|
goto __finally0;
|
|
__catch0_g_regex_error:
|
|
{
|
|
GError* e = NULL;
|
|
e = _inner_error_;
|
|
_inner_error_ = NULL;
|
|
g_assert_not_reached ();
|
|
_g_error_free0 (e);
|
|
}
|
|
|
|
__finally0:
|
|
if (_inner_error_ != NULL)
|
|
{
|
|
g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code);
|
|
g_clear_error (&_inner_error_);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* string_index_of:
|
|
*
|
|
* @haystack: a #gchar
|
|
* @needle: a #gchar
|
|
* @start_index: a #gint
|
|
*
|
|
*
|
|
* Return value: #gint that points to the position
|
|
* of @needle in @haystack, starting at @start_index,
|
|
* or -1, if @needle is not found
|
|
*
|
|
* Since: 2.10
|
|
*/
|
|
static gint
|
|
string_index_of (gchar* haystack, gchar* needle, gint start_index)
|
|
{
|
|
gint result = 0;
|
|
gchar* temp1_ = NULL;
|
|
g_return_val_if_fail (haystack != NULL, -1);
|
|
g_return_val_if_fail (needle != NULL, -1);
|
|
temp1_ = strstr (((gchar*) haystack) + start_index, needle);
|
|
if (temp1_ != NULL)
|
|
{
|
|
result = (gint) (temp1_ - ((gchar*) haystack));
|
|
return result;
|
|
}
|
|
else
|
|
{
|
|
result = -1;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* string_strnlen:
|
|
*
|
|
* @str: a #gchar
|
|
* @maxlen: a #glong
|
|
*
|
|
* Returns the length of @str or @maxlen, if @str
|
|
* is longer that @maxlen
|
|
*
|
|
* Return value: #glong
|
|
*
|
|
* Since: 2.10
|
|
*/
|
|
static glong
|
|
string_strnlen (gchar* str, glong maxlen)
|
|
{
|
|
glong result = 0L;
|
|
gchar* temp1_ = NULL;
|
|
temp1_ = memchr (str, 0, (gsize) maxlen);
|
|
if (temp1_ == NULL)
|
|
{
|
|
return maxlen;
|
|
}
|
|
else
|
|
{
|
|
result = (glong) (temp1_ - str);
|
|
return result;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* string_substring:
|
|
*
|
|
* @string: a #gchar
|
|
* @offset: a #glong
|
|
* @len: a #glong
|
|
*
|
|
* Returns a substring of @string, starting at @offset
|
|
* with a legth of @len
|
|
*
|
|
* Return value: #gchar to be freed if no longer use
|
|
*
|
|
* Since: 2.10
|
|
*/
|
|
static gchar*
|
|
string_substring (gchar* string, glong offset, glong len)
|
|
{
|
|
gchar* result = NULL;
|
|
glong string_length = 0L;
|
|
gboolean _tmp0_ = FALSE;
|
|
g_return_val_if_fail(string != NULL, NULL);
|
|
if (offset >= ((glong) 0))
|
|
{
|
|
_tmp0_ = len >= ((glong) 0);
|
|
}
|
|
else
|
|
{
|
|
_tmp0_ = FALSE;
|
|
}
|
|
|
|
if (_tmp0_)
|
|
{
|
|
string_length = string_strnlen ((gchar*) string, offset + len);
|
|
}
|
|
else
|
|
{
|
|
string_length = (glong) strlen (string);
|
|
}
|
|
|
|
if (offset < ((glong) 0))
|
|
{
|
|
offset = string_length + offset;
|
|
g_return_val_if_fail (offset >= ((glong ) 0), NULL);
|
|
}
|
|
else
|
|
{
|
|
g_return_val_if_fail (offset <= string_length, NULL);
|
|
}
|
|
if (len < ((glong) 0))
|
|
{
|
|
len = string_length - offset;
|
|
}
|
|
|
|
g_return_val_if_fail ((offset + len) <= string_length, NULL);
|
|
result = g_strndup (((gchar*) string) + offset, (gsize) len);
|
|
return result;
|
|
}
|