From 713c2d86d9335a9c681357254d4bc4f817626259 Mon Sep 17 00:00:00 2001 From: Tomas Bzatek Date: Sun, 15 Apr 2012 21:44:31 +0200 Subject: Change EXIF metadata handling into a registered function This allows much greater flexibility from templates regarding EXIF metadata handling, no more hardcoded symbols. It's possible to display essentially any attribute known to Exiv2. Please see http://exiv2.org/metadata.html This brings two new functions that can be called from templates: * get_exif_value (exiv2_attribute) * get_exif_value_fixed (exiv2_attribute) Both functions take a string argument of metadata attribute name from Exiv2 namespace. The difference is that get_exif_value_fixed() does some extra formatting for several basic attributes (e.g. datetime format). --- src/generators.c | 71 +++++++----- src/jpeg-utils.cpp | 320 ++++++++++++++++++++++++----------------------------- src/jpeg-utils.h | 58 +++++----- 3 files changed, 216 insertions(+), 233 deletions(-) (limited to 'src') diff --git a/src/generators.c b/src/generators.c index 48a377a..1d05eea 100644 --- a/src/generators.c +++ b/src/generators.c @@ -555,6 +555,27 @@ write_html_album (TGallerySetup *setup, return res; } +static char * +get_exif_value_cb (gchar **args, gpointer user_data) +{ + ExifData *exif = user_data; + + g_return_val_if_fail (exif != NULL, NULL); + g_return_val_if_fail (g_strv_length (args) != 2, NULL); /* incl. trailing NULL */ + + return get_exif_data (exif, *args); +} + +static char * +get_exif_value_fixed_cb (gchar **args, gpointer user_data) +{ + ExifData *exif = user_data; + + g_return_val_if_fail (exif != NULL, NULL); + g_return_val_if_fail (g_strv_length (args) != 2, NULL); /* incl. trailing NULL */ + + return get_exif_data_fixed (exif, *args); +} /* * write_html_image: process single image template file @@ -581,7 +602,7 @@ write_html_image (TGallerySetup *setup, gchar *img_orig_src; gchar *img_orig_dst; gchar *img_orig_dst_page; - TExifData *exif; + ExifData *exif; unsigned long img_w, img_h; unsigned long img_orig_w, img_orig_h; unsigned int item_index, next_item_index, real_item_index, real_total_items; @@ -676,13 +697,13 @@ write_html_image (TGallerySetup *setup, /* Get EXIF data from the original image */ exif = NULL; if (img_orig_src) { - exif = get_exif (img_orig_src); + exif = read_exif (img_orig_src); if (exif == NULL) log_error ("write_html_image: error getting exif data from file \"%s\"\n", img_orig_src); } /* Try destination image instead, though it might have the metadata stripped */ if (exif == NULL) { - exif = get_exif (img_dst); + exif = read_exif (img_dst); if (exif == NULL) log_error ("write_html_image: error getting exif data from file \"%s\"\n", img_dst); } @@ -699,25 +720,30 @@ write_html_image (TGallerySetup *setup, title = g_strdup (item->title); title_desc = g_strdup (item->title_description); if (setup->use_iptc_exif && title == NULL && title_desc == NULL && exif != NULL) { - if (exif->iptc_caption) - title = g_strdup (exif->iptc_caption); - if (exif->jpeg_comment) { + title = get_exif_data_fixed (exif, IPTC_CAPTION); + s1 = get_exif_data_fixed (exif, JPEG_COMMENT); + if (s1) { if (! title) - title = g_strdup (exif->jpeg_comment); + title = g_strdup (s1); else - title_desc = g_strdup (exif->jpeg_comment); + title_desc = g_strdup (s1); + g_free (s1); } - if (exif->exif_imgdesc) { + s1 = get_exif_data_fixed (exif, EXIF_IMAGE_DESCRIPTION); + if (s1) { if (! title) - title = g_strdup (exif->exif_imgdesc); + title = g_strdup (s1); /* if (! title_desc) -- disabled - title_desc = g_strdup (exif->exif_imgdesc); */ + title_desc = g_strdup (s1); */ + g_free (s1); } - if (exif->exif_usercomment) { + s1 = get_exif_data_fixed (exif, EXIF_COMMENT); + if (s1) { if (! title) - title = g_strdup (exif->exif_usercomment); + title = g_strdup (s1); if (! title_desc) - title_desc = g_strdup (exif->exif_usercomment); + title_desc = g_strdup (s1); + g_free (s1); } /* Convert line breaks to be visible in the HTML code */ if (title) { @@ -794,19 +820,8 @@ write_html_image (TGallerySetup *setup, replace_table_add_key (replace_table, "TEMPLATES_PATH", setup->supplemental_files_use_common_root ? s1 : ""); g_free (s1); - /* EXIF values */ - if (exif) { - replace_table_add_key (replace_table, "EXIF_ISO", exif->iso ? exif->iso : "??"); - replace_table_add_key (replace_table, "EXIF_TIME", exif->exposure ? exif->exposure : "??"); - replace_table_add_key (replace_table, "EXIF_APERTURE", exif->aperture ? exif->aperture : "??"); - replace_table_add_key (replace_table, "EXIF_FOCAL_LENGTH", exif->focal_length ? exif->focal_length : "??"); - replace_table_add_key (replace_table, "EXIF_FLASH", exif->flash ? exif->flash : "??"); - replace_table_add_key (replace_table, "EXIF_DATE", exif->datetime ? exif->datetime : "??"); - replace_table_add_key (replace_table, "EXIF_CAMERA_MODEL", exif->camera_model ? exif->camera_model : "??"); - s1 = g_strdup_printf ("(%s)", exif->focal_length_35mm); - replace_table_add_key (replace_table, "EXIF_FOCAL_35", exif->focal_length_35mm ? s1 : ""); - g_free (s1); - } + replace_table_register_function (replace_table, "get_exif_value", get_exif_value_cb, exif); + replace_table_register_function (replace_table, "get_exif_value_fixed", get_exif_value_fixed_cb, exif); /* Border style */ s1 = item->border_style; @@ -912,7 +927,7 @@ write_html_image (TGallerySetup *setup, g_free (imgname); g_free (preload_imgname); g_hash_table_destroy (defines); - free_exif_data (exif); + exif_data_free (exif); replace_table_free (replace_table); block_parser_free (block_parser); return res; diff --git a/src/jpeg-utils.cpp b/src/jpeg-utils.cpp index 34d3997..031ecc0 100644 --- a/src/jpeg-utils.cpp +++ b/src/jpeg-utils.cpp @@ -30,204 +30,174 @@ #include "gallery-utils.h" +struct ExifData { + Exiv2::Image::AutoPtr image; +}; + /* - * get_exif: retrieve EXIF informations from a JPEG image + * EXIF and IPTC info retrieval, keeps the source file open until freed */ -TExifData * -get_exif (const gchar *filename) +ExifData * +read_exif (const gchar *filename) { - TExifData *data; + ExifData *data; - data = (TExifData*) g_malloc0 (sizeof (TExifData)); + data = (ExifData*) g_malloc0 (sizeof (ExifData)); - try - { - Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open (filename); - g_assert (image.get() != 0); - image->readMetadata(); - - Exiv2::ExifData &exifData = image->exifData(); - if (! exifData.empty()) { - /* EXIF::Aperture */ - try { - float val = exifData["Exif.Photo.FNumber"].toFloat(); - if (val >= 0) - data->aperture = g_strdup_printf ("f/%.1f", val); - } catch (...) { } - - /* EXIF::Camera model */ - try { - data->camera_model = g_strdup (exifData["Exif.Image.Model"].toString().c_str()); - } catch (...) { } - - /* EXIF::DateTime */ - try { - const char *val = NULL; - try { - val = exifData["Exif.Photo.DateTimeOriginal"].toString().c_str(); - } catch (...) { } - if (! val || strlen (val) == 0) - try { - val = exifData["Exif.Image.DateTime"].toString().c_str(); - } catch (...) { } - - if (val && strlen (val) > 0) { - 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)) - data->datetime = g_strdup (&conv[0]); - } - } - } catch (...) { } - - /* EXIF::Shutter speed */ - try { - float val = exifData["Exif.Photo.ExposureTime"].toFloat(); - if (val > 0) { - if (val < 0.5) - data->exposure = g_strdup_printf ("1/%.0f s", 1/val); - else - data->exposure = g_strdup_printf ("%.1f s", val); - } - } catch (...) { } - - /* EXIF::Flash */ - try { - long int val = exifData["Exif.Photo.Flash"].toLong(); - if (val > 0 && (val & 1) == 1) - data->flash = g_strdup ("Flash fired"); - else - data->flash = g_strdup ("--"); - } catch (...) { } - - /* EXIF::Focal length */ - try { - float val = exifData["Exif.Photo.FocalLength"].toFloat(); - if (val >= 0) - data->focal_length = g_strdup_printf ("%.0f mm", val); - } catch (...) { } - - /* EXIF::ISO */ - try { - long int val = exifData["Exif.Photo.ISOSpeedRatings"].toLong(); - if (val > 0) - data->iso = g_strdup_printf ("%ld", val); - } catch (...) { } - - /* EXIF::Software */ - try { - data->exif_software = g_strdup (exifData["Exif.Image.Software"].toString().c_str()); - } catch (...) { } - - /* EXIF::Image description */ - try { - data->exif_imgdesc = g_strdup (exifData["Exif.Image.ImageDescription"].toString().c_str()); - } catch (...) { } - - /* EXIF::Artist */ - try { - data->exif_artist = g_strdup (exifData["Exif.Image.Artist"].toString().c_str()); - } catch (...) { } - - /* EXIF::Copyright */ - try { - data->exif_copyright = g_strdup (exifData["Exif.Image.Copyright"].toString().c_str()); - } catch (...) { } - - /* EXIF::User comment */ - try { - data->exif_usercomment = g_strdup (exifData["Exif.Photo.UserComment"].toString().c_str()); - } catch (...) { } - } - - Exiv2::IptcData &iptcData = image->iptcData(); - if (! iptcData.empty()) { - /* IPTC::Object name */ - try { - data->iptc_objectname = g_strdup (iptcData["Iptc.Application2.ObjectName"].toString().c_str()); - } catch (...) { } - - /* IPTC::Copyright */ - try { - data->iptc_copyright = g_strdup (iptcData["Iptc.Application2.Copyright"].toString().c_str()); - } catch (...) { } - - /* IPTC::Credit */ - try { - data->iptc_credit = g_strdup (iptcData["Iptc.Application2.Credit"].toString().c_str()); - } catch (...) { } - - /* IPTC::Caption */ - try { - data->iptc_caption = g_strdup (iptcData["Iptc.Application2.Caption"].toString().c_str()); - } catch (...) { } - - /* IPTC::Author */ - try { - data->iptc_author = g_strdup (iptcData["Iptc.Application2.Byline"].toString().c_str()); - } catch (...) { } - } - - /* JPEG::Comment */ - try { - data->jpeg_comment = g_strdup (image->comment().c_str()); - } catch (...) { } + try { + data->image = Exiv2::ImageFactory::open (filename); + g_assert (data->image.get() != 0); + data->image->readMetadata(); } catch (Exiv2::AnyError& e) { - log_error ("get_exif: Caught Exiv2 exception: '%s'\n", e.what()); - free_exif_data (data); + log_error ("read_exif: Caught Exiv2 exception: '%s'\n", e.what()); + exif_data_free (data); return NULL; } return data; } -/* - * free_exif_struct: free allocated structure - */ void -free_exif_data (TExifData *data) +exif_data_free (ExifData *data) { if (data) { - g_free (data->aperture); - g_free (data->camera_model); - g_free (data->datetime); - g_free (data->exposure); - g_free (data->flash); - g_free (data->focal_length); - g_free (data->focal_length_35mm); - g_free (data->iso); - - g_free (data->exif_software); - g_free (data->exif_imgdesc); - g_free (data->exif_artist); - g_free (data->exif_copyright); - g_free (data->exif_usercomment); - - g_free (data->iptc_objectname); - g_free (data->iptc_copyright); - g_free (data->iptc_credit); - g_free (data->iptc_caption); - g_free (data->iptc_author); - - g_free (data->jpeg_comment); - + /* FIXME: free data->image */ g_free (data); } } +/* + * Retrieves value of the specified key or NULL if the key does not exist. + * The key argument belongs to Exiv2 namespace - see http://exiv2.org/tags.html + */ +gchar * +get_exif_data (ExifData *exif, const gchar *key) +{ + try { + if (g_strcmp0 (key, JPEG_COMMENT) == 0) { + return g_strdup (exif->image->comment().c_str()); + } + + if (g_str_has_prefix (key, "Exif.")) { + Exiv2::ExifData &exifData = exif->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(); + if (! iptcData.empty()) { + return g_strdup (iptcData[key].toString().c_str()); + } + } + + return NULL; + } + catch (...) { + return NULL; + } +} + +gchar * +get_exif_data_fixed (ExifData *exif, const gchar *key) +{ + try + { + if (g_str_has_prefix (key, EXIF_APERTURE)) { + Exiv2::ExifData &exifData = exif->image->exifData(); + if (! exifData.empty()) { + float val = exifData["Exif.Photo.FNumber"].toFloat(); + if (val >= 0) + return g_strdup_printf ("f/%.1f", val); + } + } + + if (g_str_has_prefix (key, EXIF_DATETIME)) { + Exiv2::ExifData &exifData = exif->image->exifData(); + if (! exifData.empty()) { + const char *val = NULL; + try { + val = exifData["Exif.Photo.DateTimeOriginal"].toString().c_str(); + } catch (...) { } + if (! val || strlen (val) == 0) + try { + val = exifData["Exif.Image.DateTime"].toString().c_str(); + } catch (...) { } + + if (val && strlen (val) > 0) { + 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]); + } + } + } + } + + if (g_str_has_prefix (key, EXIF_EXPOSURE)) { + Exiv2::ExifData &exifData = exif->image->exifData(); + if (! exifData.empty()) { + float val = exifData["Exif.Photo.ExposureTime"].toFloat(); + if (val > 0) { + if (val < 0.5) + return g_strdup_printf ("1/%.0f s", 1/val); + else + return g_strdup_printf ("%.1f s", val); + } + } + } + + if (g_str_has_prefix (key, EXIF_FLASH)) { + Exiv2::ExifData &exifData = exif->image->exifData(); + if (! exifData.empty()) { + long int val = exifData["Exif.Photo.Flash"].toLong(); + if (val > 0 && (val & 1) == 1) + return g_strdup ("Flash fired"); + else + return g_strdup ("--"); + } + } + + if (g_str_has_prefix (key, EXIF_FOCAL_LENGTH)) { + Exiv2::ExifData &exifData = exif->image->exifData(); + if (! exifData.empty()) { + float val = exifData["Exif.Photo.FocalLength"].toFloat(); + if (val >= 0) + return g_strdup_printf ("%.0f mm", val); + } + } + + if (g_str_has_prefix (key, EXIF_ISO)) { + Exiv2::ExifData &exifData = exif->image->exifData(); + if (! exifData.empty()) { + long int val = exifData["Exif.Photo.ISOSpeedRatings"].toLong(); + if (val > 0) + return g_strdup_printf ("%ld", val); + } + } + } + catch (...) { + return NULL; + } + + return get_exif_data (exif, key); +} + + static void autorotate_image (MagickWand *magick_wand) { diff --git a/src/jpeg-utils.h b/src/jpeg-utils.h index a682fd1..05a6a49 100644 --- a/src/jpeg-utils.h +++ b/src/jpeg-utils.h @@ -26,43 +26,41 @@ G_BEGIN_DECLS #define SQUARED_SIMPLE_SHAVE_AMOUNT 5 /* percent */ - -/* TODO: we want to have numerical values here at some point in the future */ -typedef struct { - gchar *datetime; - gchar *camera_model; - gchar *iso; - gchar *focal_length; - gchar *focal_length_35mm; - gchar *aperture; - gchar *exposure; - gchar *flash; - - gchar *exif_software; - gchar *exif_imgdesc; - gchar *exif_artist; - gchar *exif_copyright; - gchar *exif_usercomment; - - gchar *iptc_objectname; - gchar *iptc_copyright; - gchar *iptc_credit; - gchar *iptc_caption; - gchar *iptc_author; - - gchar *jpeg_comment; -} TExifData; +/* EXIF data known keys */ +#define EXIF_APERTURE "Exif.Photo.FNumber" +#define EXIF_CAMERA_MODEL "Exif.Image.Model" +#define EXIF_DATETIME "Exif.Photo.DateTimeOriginal" +#define EXIF_EXPOSURE "Exif.Photo.ExposureTime" +#define EXIF_FLASH "Exif.Photo.Flash" +#define EXIF_FOCAL_LENGTH "Exif.Photo.FocalLength" +#define EXIF_ISO "Exif.Photo.ISOSpeedRatings" +#define EXIF_IMAGE_DESCRIPTION "Exif.Image.ImageDescription" +#define EXIF_ARTIST "Exif.Image.Artist" +#define EXIF_COPYRIGHT "Exif.Image.Copyright" +#define EXIF_COMMENT "Exif.Photo.UserComment" +#define IPTC_COPYRIGHT "Iptc.Application2.Copyright" +#define IPTC_CAPTION "Iptc.Application2.Caption" +#define IPTC_AUTHOR "Iptc.Application2.Byline" +#define JPEG_COMMENT "Jpeg.Comment" + + +typedef struct ExifData ExifData; /* - * get_exif: retrieve EXIF info from a JPEG image + * EXIF and IPTC info retrieval, keeps the source file open until freed */ -TExifData * get_exif (const gchar *filename); +ExifData * read_exif (const gchar *filename); +void exif_data_free (ExifData *data); /* - * free_exif_struct: free allocated structure + * Retrieves the value of the specified key or NULL if the key does not exist. + * The key argument belongs to Exiv2 namespace - see http://exiv2.org/tags.html + * + * The get_exif_data_fixed() function does some pretty printing for selected keys. */ -void free_exif_data (TExifData *data); +gchar * get_exif_data (ExifData *exif, const gchar *key); +gchar * get_exif_data_fixed (ExifData *exif, const gchar *key); /* -- cgit v1.2.3