summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTomas Bzatek <tbzatek@users.sourceforge.net>2013-03-10 12:41:13 +0100
committerTomas Bzatek <tbzatek@users.sourceforge.net>2013-03-10 12:41:13 +0100
commit6468dd6da3764498fcabbf99603f50974f26c2c6 (patch)
treef2ed6269be321bccc9ad2743009847fecacdd539 /src
parent0bdfd296ea68a2ec796be603faba3a85586c4a4e (diff)
downloadcataract-6468dd6da3764498fcabbf99603f50974f26c2c6.tar.xz
Add support for datetime/timezone shift
Simple datetime shift, including EXIF data modification.
Diffstat (limited to 'src')
-rw-r--r--src/generators.c26
-rw-r--r--src/items.c39
-rw-r--r--src/items.h2
-rw-r--r--src/jpeg-utils.cpp170
-rw-r--r--src/jpeg-utils.h14
5 files changed, 215 insertions, 36 deletions
diff --git a/src/generators.c b/src/generators.c
index 9c65d8e..fe62b39 100644
--- a/src/generators.c
+++ b/src/generators.c
@@ -146,6 +146,22 @@ get_image_paths (TGallerySetup *setup,
}
+static void
+metadata_apply_overrides (ExifData *exif_data,
+ TGallerySetup *setup,
+ TAlbum *items,
+ TIndexItem *item)
+{
+ g_return_if_fail (exif_data != NULL);
+
+ g_free (exif_data->override_copyright);
+ exif_data->override_copyright = g_strdup (setup->add_copyright);
+ exif_data->timezone_shift = item->metadata_tz_shift;
+ if (exif_data->timezone_shift == 0)
+ exif_data->timezone_shift = items->metadata_tz_shift;
+}
+
+
/*
* generate_image: generate needed image sizes
*/
@@ -167,6 +183,7 @@ generate_image (TGallerySetup *setup,
int quality;
GList *l;
TImageSize *image_size;
+ ExifData *exif_data;
res = ! query_update;
@@ -230,8 +247,12 @@ generate_image (TGallerySetup *setup,
}
}
}
- if (! is_thumbnail)
- modify_exif (img_dst, setup->erase_exif_thumbnail, setup->add_copyright);
+ if (! is_thumbnail) {
+ exif_data = exif_data_new_empty ();
+ metadata_apply_overrides (exif_data, setup, items, item);
+ modify_exif (img_dst, exif_data, setup->erase_exif_thumbnail);
+ exif_data_free (exif_data);
+ }
g_free (img_src);
g_free (img_dst);
@@ -754,6 +775,7 @@ write_html_image (TGallerySetup *setup,
if (exif == NULL)
log_error ("write_html_image: error getting exif data from file \"%s\"\n", img_dst);
}
+ metadata_apply_overrides (exif, setup, parent_items, item);
/* Test for basic EXIF keys presence */
if (exif != NULL && exif_has_key (exif, EXIF_APERTURE) &&
exif_has_key (exif, EXIF_FOCAL_LENGTH) &&
diff --git a/src/items.c b/src/items.c
index a0c2534..6ac7e56 100644
--- a/src/items.c
+++ b/src/items.c
@@ -30,6 +30,30 @@
#include "atom-writer.h"
+
+static int
+parse_timezone_string (const gchar *str)
+{
+ int hour = 0;
+ int min = 0;
+ float f;
+ char *endptr = NULL;
+
+ /* Expecting +-hh:mm format */
+ if (sscanf (str, "%d:%d", &hour, &min) != 2) {
+ /* Try simple float conversion instead */
+ f = strtof (str, &endptr);
+ if (endptr == NULL || *endptr != '\0') {
+ log_error ("Invalid timezone string \"%s\", ignoring.\n", str);
+ return 0;
+ }
+ return (int)(f * 60);
+ }
+
+ return hour * 60 + min;
+}
+
+
/*
* parse_album_xml: XML parser for gallery index.xml files
*/
@@ -92,6 +116,13 @@ parse_album_xml (const gchar *filename, TPathInfo *path_info)
index->meta_description = xml_file_get_node_value (xml, "/gallery/general/meta/description/text()");
index->meta_keywords = xml_file_get_node_value (xml, "/gallery/general/meta/keywords/text()");
+ s = xml_file_get_node_attribute (xml, "/gallery/general/metadata/timezone", "shift");
+ if (s != NULL) {
+ index->metadata_tz_shift = parse_timezone_string (s2);
+ g_free (s2);
+ }
+
+
index->nofullsize = xml_file_get_node_present (xml, "/gallery/general/nofullsize");
index->fullsize = xml_file_get_node_present (xml, "/gallery/general/fullsize");
@@ -219,6 +250,14 @@ parse_album_xml (const gchar *filename, TPathInfo *path_info)
item->metadata_external_exif = xml_file_get_node_attribute (xml, s, "src");
g_free (s);
+ s = g_strdup_printf ("/gallery/items/*[%d]/metadata/timezone", i + 1);
+ s2 = xml_file_get_node_attribute (xml, s, "shift");
+ g_free (s);
+ if (s2 != NULL) {
+ item->metadata_tz_shift = parse_timezone_string (s2);
+ g_free (s2);
+ }
+
if (item->path || item->preview) {
g_ptr_array_add (index->items, item);
} else {
diff --git a/src/items.h b/src/items.h
index 15baeae..2b36927 100644
--- a/src/items.h
+++ b/src/items.h
@@ -64,6 +64,7 @@ typedef struct {
gchar *auth_username;
gchar *auth_passwd;
TAuthType auth_type;
+ int metadata_tz_shift; /* minutes */
} TAlbum;
typedef struct {
@@ -81,6 +82,7 @@ typedef struct {
TIndexItemType type;
gboolean hidden;
gchar *metadata_external_exif;
+ int metadata_tz_shift; /* minutes */
} TIndexItem;
typedef struct {
diff --git a/src/jpeg-utils.cpp b/src/jpeg-utils.cpp
index bee9562..df3c94e 100644
--- a/src/jpeg-utils.cpp
+++ b/src/jpeg-utils.cpp
@@ -30,11 +30,55 @@
#include "gallery-utils.h"
-struct ExifData {
+struct ExifDataPrivate {
Exiv2::Image::AutoPtr image;
};
+static struct tm *
+parse_exif_date (const char *str)
+{
+ struct tm *tm;
+ char *res;
+
+ tm = (struct tm *) g_malloc0 (sizeof (struct tm));
+
+ res = strptime (str, "%Y:%m:%d %H:%M:%S", tm);
+ if (res == NULL || *res != '\0')
+ return NULL;
+
+ /* FIXME: what was this used for? */
+#if 0
+ tm->tm_year -= 1900;
+ tm->tm_mon--;
+#endif
+ mktime (tm);
+
+ return tm;
+}
+
+static void
+shift_time (struct tm *tm, int offset_min)
+{
+ time_t t;
+ struct tm *new_t;
+
+ if (offset_min != 0) {
+ /* FIXME: converting between time formats could make some data lost, better to operate over struct tm directly */
+ t = mktime (tm);
+ if (t == (time_t) -1) {
+ log_error ("Cannot shift time %p by %d minutes\n", tm, offset_min);
+ return;
+ }
+
+ t += offset_min * 60;
+
+ new_t = localtime (&t);
+ memcpy (tm, new_t, sizeof (struct tm));
+ }
+}
+
+
/*
* EXIF and IPTC info retrieval, keeps the source file open until freed
*/
@@ -43,12 +87,12 @@ read_exif (const gchar *filename)
{
ExifData *data;
- data = (ExifData*) g_malloc0 (sizeof (ExifData));
+ data = exif_data_new_empty ();
try {
- data->image = Exiv2::ImageFactory::open (filename);
- g_assert (data->image.get() != 0);
- data->image->readMetadata();
+ data->priv->image = Exiv2::ImageFactory::open (filename);
+ g_assert (data->priv->image.get() != 0);
+ data->priv->image->readMetadata();
}
catch (Exiv2::AnyError& e)
{
@@ -60,11 +104,24 @@ read_exif (const gchar *filename)
return data;
}
+ExifData *
+exif_data_new_empty ()
+{
+ ExifData *data;
+
+ data = (ExifData*) g_malloc0 (sizeof (ExifData));
+ data->priv = (ExifDataPrivate*) g_malloc0 (sizeof (ExifDataPrivate));
+
+ return data;
+}
+
void
exif_data_free (ExifData *data)
{
if (data) {
- /* FIXME: free data->image */
+ g_free (data->override_copyright);
+ /* FIXME: free data->priv->image */
+ g_free (data->priv);
g_free (data);
}
}
@@ -103,18 +160,18 @@ get_exif_data (ExifData *exif, const gchar *key)
try {
if (g_strcmp0 (key, JPEG_COMMENT) == 0) {
- return g_strdup (exif->image->comment().c_str());
+ return g_strdup (exif->priv->image->comment().c_str());
}
if (g_str_has_prefix (key, "Exif.")) {
- Exiv2::ExifData &exifData = exif->image->exifData();
+ Exiv2::ExifData &exifData = exif->priv->image->exifData();
if (! exifData.empty()) {
return g_strdup (exifData[key].toString().c_str());
}
}
if (g_str_has_prefix (key, "Iptc.")) {
- Exiv2::IptcData &iptcData = exif->image->iptcData();
+ Exiv2::IptcData &iptcData = exif->priv->image->iptcData();
if (! iptcData.empty()) {
return g_strdup (iptcData[key].toString().c_str());
}
@@ -136,7 +193,7 @@ get_exif_data_fixed (ExifData *exif, const gchar *key)
try
{
if (g_str_has_prefix (key, "Exif.")) {
- Exiv2::ExifData &exifData = exif->image->exifData();
+ Exiv2::ExifData &exifData = exif->priv->image->exifData();
if (exifData.empty())
return NULL;
@@ -153,24 +210,29 @@ get_exif_data_fixed (ExifData *exif, const gchar *key)
} catch (...) { }
if (! val || strlen (val) == 0)
try {
+ val = exifData["Exif.Photo.DateTimeDigitized"].toString().c_str();
+ } catch (...) { }
+ if (! val || strlen (val) == 0)
+ try {
+ /* usually a modification date */
val = exifData["Exif.Image.DateTime"].toString().c_str();
} catch (...) { }
if (val && strlen (val) > 0) {
- struct tm tt;
+ struct tm *tt;
char conv[1024];
memset (&conv, 0, sizeof (conv));
- memset (&tt, 0, sizeof (struct tm));
-
- if (sscanf (val, "%d:%d:%d %d:%d:%d", &tt.tm_year, &tt.tm_mon, &tt.tm_mday, &tt.tm_hour, &tt.tm_min, &tt.tm_sec) == 6)
- {
- tt.tm_year -= 1900;
- tt.tm_mon--;
- mktime (&tt);
- if (strftime (&conv[0], sizeof (conv), "%c", &tt))
- return g_strdup (&conv[0]);
+
+ tt = parse_exif_date (val);
+ if (tt) {
+ shift_time (tt, exif->timezone_shift);
+ if (strftime (&conv[0], sizeof (conv), "%c", tt)) {
+ g_free (tt);
+ return g_strdup (&conv[0]);
}
+ g_free (tt);
+ }
}
}
@@ -214,7 +276,7 @@ get_exif_data_fixed (ExifData *exif, const gchar *key)
}
if (g_str_has_prefix (key, "Iptc.")) {
- Exiv2::IptcData &iptcData = exif->image->iptcData();
+ Exiv2::IptcData &iptcData = exif->priv->image->iptcData();
if (iptcData.empty())
return NULL;
}
@@ -239,16 +301,16 @@ exif_has_key (ExifData *exif, const gchar *key)
try {
if (g_strcmp0 (key, JPEG_COMMENT) == 0) {
- return (! exif->image->comment().empty());
+ return (! exif->priv->image->comment().empty());
}
if (g_str_has_prefix (key, "Exif.")) {
- Exiv2::ExifData &exifData = exif->image->exifData();
+ Exiv2::ExifData &exifData = exif->priv->image->exifData();
return !exifData.empty() && exifData[key].count() > 0;
}
if (g_str_has_prefix (key, "Iptc.")) {
- Exiv2::IptcData &iptcData = exif->image->iptcData();
+ Exiv2::IptcData &iptcData = exif->priv->image->iptcData();
return !iptcData.empty() && iptcData[key].count() > 0;
}
@@ -450,17 +512,50 @@ calculate_sizes (const unsigned long max_width, const unsigned long max_height,
}
}
+static gboolean
+shift_exif_time (Exiv2::ExifData& exifData, const char *key, int amount)
+{
+ const char *s;
+ gboolean res;
+
+ res = FALSE;
+ try {
+ if (exifData[key].count() > 0) {
+ s = exifData[key].toString().c_str();
+
+ if (s && strlen (s) > 0) {
+ struct tm *tt;
+ char conv[1024];
+
+ memset (&conv, 0, sizeof (conv));
+
+ tt = parse_exif_date (s);
+ if (tt) {
+ shift_time (tt, amount);
+ if (strftime (&conv[0], sizeof (conv), "%Y:%m:%d %H:%M:%S", tt)) {
+ exifData[key] = (const char *) &conv[0];
+ res = TRUE;
+ }
+ g_free (tt);
+ }
+ }
+ }
+ } catch (...) { }
+
+ return res;
+}
/*
* modify_exif: - strip thumbnail stored in EXIF table
- * - add copyright to Exif::Image::Copyright and Iptc::Application2::Copyright
+ * - write down overriden keys
*/
void
-modify_exif (const gchar *filename, gboolean strip_thumbnail, const gchar *add_copyright)
+modify_exif (const gchar *filename, ExifData *exif, gboolean strip_thumbnail)
{
gboolean modified;
+ gboolean res;
- if (! strip_thumbnail && add_copyright == NULL)
+ if (! strip_thumbnail && exif == NULL)
return;
modified = FALSE;
@@ -470,10 +565,23 @@ modify_exif (const gchar *filename, gboolean strip_thumbnail, const gchar *add_c
image->readMetadata();
Exiv2::ExifData &exifData = image->exifData();
- if (add_copyright) {
- exifData["Exif.Image.Copyright"] = add_copyright;
- image->iptcData()["Iptc.Application2.Copyright"] = add_copyright;
- modified = TRUE;
+
+ if (exif) {
+ if (exif->override_copyright) {
+ exifData["Exif.Image.Copyright"] = exif->override_copyright;
+ image->iptcData()["Iptc.Application2.Copyright"] = exif->override_copyright;
+ modified = TRUE;
+ }
+
+ if (exif->timezone_shift != 0 && !exifData.empty()) {
+ /* need original data to calculate the shift from */
+ res = shift_exif_time (exifData, "Exif.Photo.DateTimeOriginal", exif->timezone_shift);
+ res = shift_exif_time (exifData, "Exif.Photo.DateTimeDigitized", exif->timezone_shift) || res;
+ if (! res)
+ /* usually a modification date, shift as a last option */
+ shift_exif_time (exifData, "Exif.Image.DateTime", exif->timezone_shift);
+ modified = TRUE;
+ }
}
if (strip_thumbnail && ! exifData.empty()) {
diff --git a/src/jpeg-utils.h b/src/jpeg-utils.h
index 86602a2..89a2cd4 100644
--- a/src/jpeg-utils.h
+++ b/src/jpeg-utils.h
@@ -45,13 +45,21 @@ G_BEGIN_DECLS
#define JPEG_COMMENT "Jpeg.Comment"
-typedef struct ExifData ExifData;
+typedef struct ExifDataPrivate ExifDataPrivate;
+
+typedef struct {
+ ExifDataPrivate *priv;
+
+ gchar *override_copyright;
+ int timezone_shift;
+} ExifData;
/*
* EXIF and IPTC info retrieval, keeps the source file open until freed
*/
ExifData * read_exif (const gchar *filename);
+ExifData * exif_data_new_empty ();
void exif_data_free (ExifData *data);
/*
@@ -95,9 +103,9 @@ void calculate_sizes (const unsigned long max_width, const unsigned long max_hei
/*
* modify_exif: - strip thumbnail stored in EXIF table
- * - add copyright to Exif::Image::Copyright and Iptc::Application2::Copyright
+ * - write down overriden keys
*/
-void modify_exif (const gchar *filename, gboolean strip_thumbnail, const gchar *add_copyright);
+void modify_exif (const gchar *filename, ExifData *exif, gboolean strip_thumbnail);
G_END_DECLS