/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
/*
* GData Client
* Copyright (C) Richard Schwarting 2009 <aquarichy@gmail.com>
*
* GData Client 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 2.1 of the License, or (at your option) any later version.
*
* GData Client 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 GData Client. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* SECTION:gdata-exif-tags
* @short_description: EXIF tags element
* @stability: Stable
* @include: gdata/exif/gdata-exif-tags.h
*
* #GDataExifTags represents a "tags" element from the
* <ulink type="http" url="http://schemas.google.com/photos/exif/2007">EXIF specification</ulink>.
*
* It is private API, since implementing classes are likely to proxy the properties and functions
* of #GDataExifTags as appropriate; most entry types which implement #GDataExifTags have no use
* for most of its properties, and it would be unnecessary and confusing to expose #GDataExifTags itself.
*
* Also note that modified EXIF values submitted back to the Google (in an update or on the original
* upload) appear to be ignored. Google's EXIF values for the uploaded image will be set to the EXIF
* metadata found in the image itself.
*
* For these reasons, properties have not been implemented on #GDataExifTags (yet).
*
* Since: 0.5.0
*/
#include <glib.h>
#include <libxml/parser.h>
#include <string.h>
#include "gdata-exif-tags.h"
#include "gdata-parsable.h"
#include "gdata-parser.h"
#include "gdata-private.h"
static void gdata_exif_tags_finalize (GObject *object);
static gboolean parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *node, gpointer user_data, GError **error);
static void get_namespaces (GDataParsable *parsable, GHashTable *namespaces);
struct _GDataExifTagsPrivate {
gdouble distance;
gdouble exposure;
gboolean flash;
gdouble focal_length;
gdouble fstop;
gchar *image_unique_id;
gint iso;
gchar *make;
gchar *model;
gint64 _time; /* in milliseconds! */
};
G_DEFINE_TYPE (GDataExifTags, gdata_exif_tags, GDATA_TYPE_PARSABLE)
static void
gdata_exif_tags_class_init (GDataExifTagsClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GDataParsableClass *parsable_class = GDATA_PARSABLE_CLASS (klass);
g_type_class_add_private (klass, sizeof (GDataExifTagsPrivate));
gobject_class->finalize = gdata_exif_tags_finalize;
parsable_class->parse_xml = parse_xml;
parsable_class->get_namespaces = get_namespaces;
parsable_class->element_name = "tags";
parsable_class->element_namespace = "exif";
}
static void
gdata_exif_tags_init (GDataExifTags *self)
{
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GDATA_TYPE_EXIF_TAGS, GDataExifTagsPrivate);
self->priv->_time = -1;
}
static void
gdata_exif_tags_finalize (GObject *object)
{
GDataExifTagsPrivate *priv = GDATA_EXIF_TAGS (object)->priv;
g_free (priv->make);
g_free (priv->model);
g_free (priv->image_unique_id);
/* Chain up to the parent class */
G_OBJECT_CLASS (gdata_exif_tags_parent_class)->finalize (object);
}
static gboolean
parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *node, gpointer user_data, GError **error)
{
gboolean success;
GDataExifTags *self = GDATA_EXIF_TAGS (parsable);
if (gdata_parser_is_namespace (node, "http://schemas.google.com/photos/exif/2007") == FALSE)
return GDATA_PARSABLE_CLASS (gdata_exif_tags_parent_class)->parse_xml (parsable, doc, node, user_data, error);
if (xmlStrcmp (node->name, (xmlChar*) "distance") == 0 ) {
/* exif:distance */
xmlChar *distance = xmlNodeListGetString (doc, node->children, TRUE);
self->priv->distance = g_ascii_strtod ((gchar*) distance, NULL);
xmlFree (distance);
} else if (xmlStrcmp (node->name, (xmlChar*) "fstop") == 0) {
/* exif:fstop */
xmlChar *fstop = xmlNodeListGetString (doc, node->children, TRUE);
self->priv->fstop = g_ascii_strtod ((gchar*) fstop, NULL);
xmlFree (fstop);
} else if (gdata_parser_string_from_element (node, "make", P_NONE, &(self->priv->make), &success, error) == TRUE ||
gdata_parser_string_from_element (node, "model", P_NONE, &(self->priv->model), &success, error) == TRUE ||
gdata_parser_string_from_element (node, "imageUniqueID", P_NONE, &(self->priv->image_unique_id), &success, error) == TRUE) {
return success;
} else if (xmlStrcmp (node->name, (xmlChar*) "exposure") == 0) {
/* exif:exposure */
xmlChar *exposure = xmlNodeListGetString (doc, node->children, TRUE);
self->priv->exposure = g_ascii_strtod ((gchar*) exposure, NULL);
xmlFree (exposure);
} else if (xmlStrcmp (node->name, (xmlChar*) "flash") == 0) {
/* exif:flash */
xmlChar *flash = xmlNodeListGetString (doc, node->children, TRUE);
if (flash == NULL)
return gdata_parser_error_required_content_missing (node, error);
self->priv->flash = (xmlStrcmp (flash, (xmlChar*) "true") == 0 ? TRUE : FALSE);
xmlFree (flash);
} else if (xmlStrcmp (node->name, (xmlChar*) "focallength") == 0) {
/* exif:focal-length */
xmlChar *focal_length = xmlNodeListGetString (doc, node->children, TRUE);
self->priv->focal_length = g_ascii_strtod ((gchar*) focal_length, NULL);
xmlFree (focal_length);
} else if (xmlStrcmp (node->name, (xmlChar*) "iso") == 0) {
/* exif:iso */
xmlChar *iso = xmlNodeListGetString (doc, node->children, TRUE);
self->priv->iso = g_ascii_strtoll ((gchar*) iso, NULL, 10);
xmlFree (iso);
} else if (xmlStrcmp (node->name, (xmlChar*) "time") == 0) {
/* exif:time */
xmlChar *time_str;
guint64 milliseconds;
time_str = xmlNodeListGetString (doc, node->children, TRUE);
milliseconds = g_ascii_strtoull ((gchar*) time_str, NULL, 10);
xmlFree (time_str);
self->priv->_time = (gint64) milliseconds;
} else {
return GDATA_PARSABLE_CLASS (gdata_exif_tags_parent_class)->parse_xml (parsable, doc, node, user_data, error);
}
return TRUE;
}
static void
get_namespaces (GDataParsable *parsable, GHashTable *namespaces)
{
g_hash_table_insert (namespaces, (gchar*) "exif", (gchar*) "http://schemas.google.com/photos/exif/2007");
}
/**
* gdata_exif_tags_get_distance:
* @self: a #GDataExifTags
*
* Gets the #GDataExifTags:distance property.
*
* Return value: the distance value, or <code class="literal">-1</code> if unknown
*
* Since: 0.5.0
*/
gdouble
gdata_exif_tags_get_distance (GDataExifTags *self)
{
g_return_val_if_fail (GDATA_IS_EXIF_TAGS (self), -1);
return self->priv->distance;
}
/**
* gdata_exif_tags_get_exposure:
* @self: a #GDataExifTags
*
* Gets the #GDataExifTags:exposure property.
*
* Return value: the exposure value, or <code class="literal">0</code> if unknown
*
* Since: 0.5.0
*/
gdouble
gdata_exif_tags_get_exposure (GDataExifTags *self)
{
g_return_val_if_fail (GDATA_IS_EXIF_TAGS (self), 0);
return self->priv->exposure;
}
/**
* gdata_exif_tags_get_flash:
* @self: a #GDataExifTags
*
* Gets the #GDataExifTags:flash property.
*
* Return value: %TRUE if flash was used, %FALSE otherwise
*
* Since: 0.5.0
*/
gboolean
gdata_exif_tags_get_flash (GDataExifTags *self)
{
g_return_val_if_fail (GDATA_IS_EXIF_TAGS (self), FALSE);
return self->priv->flash;
}
/**
* gdata_exif_tags_get_focal_length:
* @self: a #GDataExifTags
*
* Gets the #GDataExifTags:focal-length property.
*
* Return value: the focal-length value, or <code class="literal">-1</code> if unknown
*
* Since: 0.5.0
*/
gdouble
gdata_exif_tags_get_focal_length (GDataExifTags *self)
{
g_return_val_if_fail (GDATA_IS_EXIF_TAGS (self), -1);
return self->priv->focal_length;
}
/**
* gdata_exif_tags_get_fstop:
* @self: a #GDataExifTags
*
* Gets the #GDataExifTags:fstop property.
*
* Return value: the F-stop value, or <code class="literal">0</code> if unknown
*
* Since: 0.5.0
*/
gdouble
gdata_exif_tags_get_fstop (GDataExifTags *self)
{
g_return_val_if_fail (GDATA_IS_EXIF_TAGS (self), 0);
return self->priv->fstop;
}
/**
* gdata_exif_tags_get_image_unique_id:
* @self: a #GDataExifTags
*
* Gets the #GDataExifTags:image-unique-id property.
*
* Return value: the photo's unique EXIF identifier, or %NULL
*
* Since: 0.5.0
*/
const gchar *
gdata_exif_tags_get_image_unique_id (GDataExifTags *self)
{
g_return_val_if_fail (GDATA_IS_EXIF_TAGS (self), NULL);
return self->priv->image_unique_id;
}
/**
* gdata_exif_tags_get_iso:
* @self: a #GDataExifTags
*
* Gets the #GDataExifTags:iso property.
*
* Return value: the ISO speed, or <code class="literal">-1</code> if unknown
*
* Since: 0.5.0
*/
gint
gdata_exif_tags_get_iso (GDataExifTags *self)
{
g_return_val_if_fail (GDATA_IS_EXIF_TAGS (self), -1);
return self->priv->iso;
}
/**
* gdata_exif_tags_get_make:
* @self: a #GDataExifTags
*
* Gets the #GDataExifTags:make property.
*
* Return value: the name of the manufacturer of the camera, or %NULL if unknown
*
* Since: 0.5.0
*/
const gchar *
gdata_exif_tags_get_make (GDataExifTags *self)
{
g_return_val_if_fail (GDATA_IS_EXIF_TAGS (self), NULL);
return self->priv->make;
}
/**
* gdata_exif_tags_get_model:
* @self: a #GDataExifTags
*
* Gets the #GDataExifTags:model property.
*
* Return value: the model name of the camera, or %NULL if unknown
*
* Since: 0.5.0
*/
const gchar *
gdata_exif_tags_get_model (GDataExifTags *self)
{
g_return_val_if_fail (GDATA_IS_EXIF_TAGS (self), NULL);
return self->priv->model;
}
/**
* gdata_exif_tags_get_time:
* @self: a #GDataExifTags
*
* Gets the #GDataExifTags:time property as a number of milliseconds since the epoch. If the property is unset, <code class="literal">-1</code> will
* be returned.
*
* Return value: the UNIX timestamp for the time property in milliseconds, or <code class="literal">-1</code>
*
* Since: 0.5.0
*/
gint64
gdata_exif_tags_get_time (GDataExifTags *self)
{
g_return_val_if_fail (GDATA_IS_EXIF_TAGS (self), -1);
return self->priv->_time;
}