summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
l---------sample/src/CIAF_1/img_1453c.jpg1
-rw-r--r--sample/src/CIAF_1/index.xml18
-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
7 files changed, 234 insertions, 36 deletions
diff --git a/sample/src/CIAF_1/img_1453c.jpg b/sample/src/CIAF_1/img_1453c.jpg
new file mode 120000
index 0000000..4b0b3c1
--- /dev/null
+++ b/sample/src/CIAF_1/img_1453c.jpg
@@ -0,0 +1 @@
+img_1453.jpg \ No newline at end of file
diff --git a/sample/src/CIAF_1/index.xml b/sample/src/CIAF_1/index.xml
index 9a00ad7..24433fd 100644
--- a/sample/src/CIAF_1/index.xml
+++ b/sample/src/CIAF_1/index.xml
@@ -100,6 +100,8 @@
<!-- Captions should be retrieved from IPTC/EXIF -->
</item>
+ <separator>Metadata</separator>
+
<item src="img_1453b.jpg">
<title>External EXIF metadata</title>
<title_description><![CDATA[This item uses external EXIF metadata, in this case supplied from a completely different picture.<br/><br/>
@@ -113,5 +115,21 @@
</metadata>
</item>
+ <item src="img_1453c.jpg">
+ <title>Time zone shift</title>
+ <title_description><![CDATA[This item uses external EXIF metadata from last example, combined with manual time zone correction. This is useful if you forgot to correct time in the camera, e.g. when travelling and crossing multiple timezones or by different DST settings.<br/><br/>
+
+ Values can be specified either in preferred <code>&#177;hh:mm</code> format specifying exact hour and minutes, or, alternatively, by simplified plain float format (e.g. <code>1</code>, <code>-2</code>, <code>+0.5</code>) where the number represents hours. Beware that in the latter case there may be precision issues converting float format to minutes.<br/><br/>
+
+ Changes can be made locally for specific item or globally for whole album (with the former taking priority).<br/><br/>
+
+ All such changes are written in generated image files in all present metadata fields. This also applies for user supplied images which are normally not supposed to be changed or resized.
+ ]]></title_description>
+ <metadata>
+ <external_exif src="exif/5l9a4978.exv" />
+ <timezone shift="+02:15" />
+ </metadata>
+ </item>
+
</items>
</gallery>
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