diff options
| author | Tomas Bzatek <tbzatek@users.sourceforge.net> | 2019-05-13 22:16:28 +0200 |
|---|---|---|
| committer | Tomas Bzatek <tbzatek@users.sourceforge.net> | 2019-05-13 22:16:28 +0200 |
| commit | 0c3b218886342e44275b087c41faf3b6a2b7f664 (patch) | |
| tree | 35191c66c8f20e6f9b245d81cd79398a435370d9 /src/jpeg-utils.cpp | |
| parent | 86438b558aaeb758d8770144c9e2fe70fdc4ee8a (diff) | |
| download | cataract-0c3b218886342e44275b087c41faf3b6a2b7f664.tar.xz | |
jpeg-utils: Port to gexiv2
The gexiv2 library is just a GObject wrapper around exiv2 library. It's
a healthy project that continually keeps up with exiv2 API changes.
Adopted by e.g. GIMP there's certain guarantee of future maintenance.
This allows us to get rid of C++ code, making it more readable and
predictable.
Diffstat (limited to 'src/jpeg-utils.cpp')
| -rw-r--r-- | src/jpeg-utils.cpp | 1105 |
1 files changed, 0 insertions, 1105 deletions
diff --git a/src/jpeg-utils.cpp b/src/jpeg-utils.cpp deleted file mode 100644 index 2e214e4..0000000 --- a/src/jpeg-utils.cpp +++ /dev/null @@ -1,1105 +0,0 @@ -/* Cataract - Static web photo gallery generator - * Copyright (C) 2008 Tomas Bzatek <tbzatek@users.sourceforge.net> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -#include "config.h" - -#include <stdio.h> -#include <string.h> -#include <stdlib.h> - -#include <exiv2/image.hpp> -#include <exiv2/exif.hpp> - -#ifdef HAVE_IMAGEMAGICK_7 -# include <MagickWand/MagickWand.h> -#else -# include <wand/MagickWand.h> -#endif - -#include <config.h> - -#include "jpeg-utils.h" -#include "gallery-utils.h" - - -struct ExifDataPrivate { - Exiv2::Image::AutoPtr image; -}; - - -/* - * Thread-safe ImageMagick and exiv2 libraries initialization and cleanup - */ -void -init_jpeg_utils (void) -{ - MagickWandGenesis(); - /* http://dev.exiv2.org/projects/exiv2/wiki/Thread_safety */ - /* https://bugs.kde.org/show_bug.cgi?id=166424 */ - Exiv2::XmpParser::initialize(); -} - -void -destroy_jpeg_utils (void) -{ - Exiv2::XmpParser::terminate(); - MagickWandTerminus(); -} - - -static void -shift_time (struct tm *tm, int offset_min) -{ - time_t 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; - - localtime_r (&t, tm); - } -} - -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; - - mktime (tm); - - if (tm->tm_isdst) - shift_time (tm, -60); - - return tm; -} - -static gchar * -format_exif_time (struct tm *tm) -{ - char conv[1024]; - - memset (&conv, 0, sizeof(conv)); - if (strftime (&conv[0], sizeof(conv), "%Y:%m:%d %H:%M:%S", tm)) - return g_strdup (&conv[0]); - - return NULL; -} - -/* - * EXIF and IPTC info retrieval, keeps the source file open until freed - */ -ExifData * -read_exif (const gchar *filename) -{ - ExifData *data; - - g_assert (filename != NULL); - data = exif_data_new_empty (); - - try { - data->priv->image = Exiv2::ImageFactory::open (filename); - g_assert (data->priv->image.get() != 0); - data->priv->image->readMetadata(); - } - catch (Exiv2::AnyError& e) - { - log_error ("read_exif: Caught Exiv2 exception: '%s'\n", e.what()); - exif_data_free (data); - return NULL; - } - - 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) { - g_free (data->override_copyright); - g_free (data->external_exif_data); - g_free (data->override_artist_name); - /* FIXME: free data->priv->image */ - g_free (data->priv); - g_free (data); - } -} - -static const gchar * -get_real_key_name (const gchar *key) -{ - struct StrKeyPair { - const gchar *from; - const gchar *to; - }; - static const struct StrKeyPair conv[] = { - { EXIF_CANON_CAMERA_TEMP, "Exif.CanonSi.0x000c" }, - }; - - guint i; - - for (i = 0; i < G_N_ELEMENTS (conv); i++) - if (g_str_equal (conv[i].from, key)) - return conv[i].to; - - return key; -} - - -/* Returns newly allocated string */ -/* FIXME: for some reason exiv2 returns const strings with unknown lifetime, can't really trust the library */ -#define exiv2_str_val(data,key) g_strdup(data[key].toString().c_str()) - -static gboolean -iptc_has_key (Exiv2::IptcData iptcData, const char *key) -{ - Exiv2::IptcData::const_iterator md; - - if (! iptcData.empty()) { - md = iptcData.findKey(Exiv2::IptcKey(std::string(key))); - if (md != iptcData.end() && iptcData[key].count() > 0) - return TRUE; - } - return FALSE; -} - - -/* - * 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) -{ - g_return_val_if_fail (exif != NULL, NULL); - g_return_val_if_fail (key != NULL, NULL); - - key = get_real_key_name (key); - - try { - if (g_strcmp0 (key, JPEG_COMMENT) == 0) { - return g_strdup (exif->priv->image->comment().c_str()); - } - - if (g_str_has_prefix (key, "Exif.")) { - Exiv2::ExifData &exifData = exif->priv->image->exifData(); - if (! exifData.empty()) { - return exiv2_str_val (exifData, key); - } - } - - if (g_str_has_prefix (key, "Iptc.")) { - Exiv2::IptcData &iptcData = exif->priv->image->iptcData(); - if (iptc_has_key (iptcData, key)) { - return exiv2_str_val (iptcData, key); - } - } - - return NULL; - } - catch (...) { - return NULL; - } -} - -gchar * -get_exif_data_fixed (ExifData *exif, const gchar *key) -{ - g_return_val_if_fail (exif != NULL, NULL); - g_return_val_if_fail (key != NULL, NULL); - - try - { - if (g_str_has_prefix (key, "Exif.")) { - Exiv2::ExifData &exifData = exif->priv->image->exifData(); - if (exifData.empty()) - return NULL; - - if (g_str_equal (key, EXIF_APERTURE)) { - double val = -1; - if (exif->override_aperture != -1) - val = exif->override_aperture; - else { - if (exifData["Exif.Photo.FNumber"].count() > 0) - val = exifData["Exif.Photo.FNumber"].toFloat(); - else - if (exifData["Exif.Photo.ApertureValue"].count() > 0) - val = exifData["Exif.Photo.ApertureValue"].toFloat(); - } - if (val >= 0) - return g_strdup_printf ("ƒ/%.1f", val); - } - - if (g_str_equal (key, EXIF_DATETIME) || - g_str_equal (key, "Exif.Photo.DateTimeOriginal") || - g_str_equal (key, "Exif.Photo.DateTimeDigitized") || - g_str_equal (key, "Exif.Image.DateTime")) { - gchar *val = NULL; - try { - val = exiv2_str_val (exifData, "Exif.Photo.DateTimeOriginal"); - } catch (...) { } - if (! val || strlen (val) == 0) - try { - val = exiv2_str_val (exifData, "Exif.Photo.DateTimeDigitized"); - } catch (...) { } - if (! val || strlen (val) == 0) - try { - /* usually a modification date */ - val = exiv2_str_val (exifData, "Exif.Image.DateTime"); - } catch (...) { } - - if (val && strlen (val) > 0) { - struct tm *tt = NULL; - char conv[1024]; - gchar *res = NULL; - - memset (&conv, 0, sizeof (conv)); - - if (exif->override_datetime != (time_t) -1) { - tt = (struct tm *) g_malloc0 (sizeof (struct tm)); - localtime_r (&exif->override_datetime, tt); - if (strftime (&conv[0], sizeof (conv), exif->datetime_format ? exif->datetime_format : "%c", tt)) - res = g_strdup (&conv[0]); - } - - if (! res) { - tt = parse_exif_date (val); - if (tt) { - shift_time (tt, exif->timezone_shift); - if (strftime (&conv[0], sizeof (conv), exif->datetime_format ? exif->datetime_format : "%c", tt)) - res = g_strdup (&conv[0]); - } - } - - g_free (tt); - if (res) - return res; - } - g_free (val); - } - - if (g_str_equal (key, EXIF_EXPOSURE)) { - 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_equal (key, EXIF_FLASH)) { - 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_equal (key, EXIF_FOCAL_LENGTH)) { - double val; - val = exif->override_focal_length != -1 ? exif->override_focal_length : exifData["Exif.Photo.FocalLength"].toFloat(); - if (val >= 0) - return g_strdup_printf ("%.0f mm", val); - } - - if (g_str_equal (key, EXIF_ISO)) { - long int val = exifData["Exif.Photo.ISOSpeedRatings"].toLong(); - if (val > 0) - return g_strdup_printf ("%ld", val); - } - - if (g_str_equal (key, EXIF_CANON_CAMERA_TEMP)) { - if (exifData["Exif.CanonSi.0x000c"].count() > 0) { - int long val = exifData["Exif.CanonSi.0x000c"].toLong(); - if (val > 0) - return g_strdup_printf ("%ld °C", val - 128); - } - } - } - - if (g_str_has_prefix (key, "Iptc.")) { - Exiv2::IptcData &iptcData = exif->priv->image->iptcData(); - if (iptcData.empty()) - return NULL; - } - } - catch (...) { - return NULL; - } - - return get_exif_data (exif, key); -} - -/* - * Returns TRUE if the image contains the key specified - */ -gboolean -exif_has_key (ExifData *exif, const gchar *key) -{ - const gchar *rkey; - - g_return_val_if_fail (exif != NULL, FALSE); - g_return_val_if_fail (key != NULL, FALSE); - - rkey = get_real_key_name (key); - - try { - if (g_strcmp0 (rkey, JPEG_COMMENT) == 0) { - return (! exif->priv->image->comment().empty()); - } - - if (g_str_has_prefix (rkey, "Exif.")) { - Exiv2::ExifData &exifData = exif->priv->image->exifData(); - if (exifData.empty() || exifData[rkey].count() <= 0) - return FALSE; - - /* Special case for some keys */ - if (g_str_equal (key, EXIF_CANON_CAMERA_TEMP)) { - int long val = exifData["Exif.CanonSi.0x000c"].toLong(); - return val > 0; - } - - return TRUE; - } - - if (g_str_has_prefix (rkey, "Iptc.")) { - Exiv2::IptcData &iptcData = exif->priv->image->iptcData(); - return iptc_has_key (iptcData, rkey); - } - - return FALSE; - } - catch (...) { - return FALSE; - } -} - - -static void -autorotate_image (MagickWand *magick_wand) -{ - MagickBooleanType b; - PixelWand *pixel_wand; - ExceptionType severity; - gchar *description; - - pixel_wand = NewPixelWand (); - b = PixelSetColor (pixel_wand, "#000000"); - if (b == MagickFalse) { - description = MagickGetException (magick_wand, &severity); - log_error ("autorotate_image: Error creating pixel wand: %s %s %ld %s\n", GetMagickModule(), description); - MagickRelinquishMemory (description); - } - - b = MagickTrue; - switch (MagickGetImageOrientation (magick_wand)) - { - case TopRightOrientation: - b = MagickFlopImage (magick_wand); - break; - case BottomRightOrientation: - b = MagickRotateImage (magick_wand, pixel_wand, 180.0); - break; - case BottomLeftOrientation: - b = MagickFlipImage (magick_wand); - break; - case LeftTopOrientation: - b = MagickTransposeImage (magick_wand); - break; - case RightTopOrientation: - b = MagickRotateImage (magick_wand, pixel_wand, 90.0); - break; - case RightBottomOrientation: - b = MagickTransverseImage (magick_wand); - break; - case LeftBottomOrientation: - b = MagickRotateImage (magick_wand, pixel_wand, 270.0); - break; - default: - break; - } - - if (b == MagickFalse) { - description = MagickGetException (magick_wand, &severity); - log_error ("autorotate_image: Error rotating image: %s %s %ld %s\n", GetMagickModule(), description); - MagickRelinquishMemory (description); - } - - b = MagickSetImageOrientation (magick_wand, TopLeftOrientation); - if (b == MagickFalse) { - description = MagickGetException (magick_wand, &severity); - log_error ("autorotate_image: Error saving orientation: %s %s %ld %s\n", GetMagickModule(), description); - MagickRelinquishMemory (description); - } - - DestroyPixelWand (pixel_wand); -} - -static gchar ** -parse_cmd_args (const gchar *resize_opts, const gchar *prepend, const gchar *append, const gchar *file_in, const gchar *file_out, unsigned long size_x, unsigned long size_y) -{ - gchar *in; - gchar **s; - gchar *f; - - in = g_strdup_printf ("%s %s %s %s %s", - prepend ? prepend : "", - file_in ? file_in : "", - resize_opts, - file_out ? file_out : "", - append ? append : ""); - while (g_strstr_len (in, -1, "${WIDTH}")) { - f = g_strdup_printf ("%lu", size_x); - str_replace (&in, "${WIDTH}", f); - g_free (f); - } - while (g_strstr_len (in, -1, "${HEIGHT}")) { - f = g_strdup_printf ("%lu", size_y); - str_replace (&in, "${HEIGHT}", f); - g_free (f); - } - /* ImageMagick doesn't like empty elements */ - str_trim_inside (&in); - s = g_strsplit (in, " ", -1); - g_free (in); - - return s; -} - -/* - * resize_image: resize image pointed by src and save result to dst - */ -gboolean -resize_image (const gchar *src, const gchar *dst, - unsigned long size_x, unsigned long size_y, - int quality, - gboolean thumbnail, - gboolean autorotate, - gboolean hidpi_strict_dimensions, - ExifData *exif, - gchar *resize_opts) -{ - MagickWand *magick_wand; - ImageInfo *image_info; - ExceptionInfo *exception_info; - ExceptionType severity; - unsigned long w, h; - unsigned long new_w, new_h; - double source_aspect, target_aspect; - gchar *description; - gchar **cmd_args; - gchar *res_id = NULL; - gchar *mpr_res_id; - - g_assert (src != NULL); - g_assert (dst != NULL); - - /* Read an image. */ - magick_wand = NewMagickWand(); - if (MagickReadImage (magick_wand, src) == MagickFalse) { - description = MagickGetException (magick_wand, &severity); - log_error ("Error reading image: %s %s %ld %s\n", GetMagickModule(), description); - MagickRelinquishMemory (description); - DestroyMagickWand (magick_wand); - return FALSE; - } - - if (autorotate) - autorotate_image (magick_wand); - - /* Don't resize if smaller than desired size */ - if (hidpi_strict_dimensions || MagickGetImageWidth (magick_wand) > size_x || MagickGetImageHeight (magick_wand) > size_y || exif->shave_amount > 0) - { - /* Shave borders if requested */ - if (exif->shave_amount > 0) - MagickShaveImage (magick_wand, exif->shave_amount, exif->shave_amount); - - /* Prepare image before resizing */ - if (thumbnail) { - if (exif->thumbnail_crop_style != CROP_STYLE_NORMAL) { - w = MagickGetImageWidth (magick_wand); - h = MagickGetImageHeight (magick_wand); - new_w = w; - new_h = h; - if (exif->thumbnail_crop_style == CROP_STYLE_SQUARED) { - new_w = MAX (w, h) * CROP_SIMPLE_SHAVE_AMOUNT / 100; - new_w = MIN (w - 2*new_w, h - 2*new_w); - new_h = new_w; - } - if (exif->thumbnail_crop_style == CROP_STYLE_FIXED) { - source_aspect = (double) w / (double) h; - target_aspect = (double) size_x / (double) size_y; - if (target_aspect >= source_aspect) { - new_w = w; - new_h = (int) ((double) w / target_aspect); - } else { - new_w = (int) ((double) h * target_aspect); - new_h = h; - } - new_w = (int) ((double) new_w * (double) (100 - CROP_SIMPLE_SHAVE_AMOUNT) / 100); - new_h = (int) ((double) new_h * (double) (100 - CROP_SIMPLE_SHAVE_AMOUNT) / 100); - } - switch (exif->thumbnail_crop_hint) { - case CROP_HINT_UNDEFINED: - case CROP_HINT_CENTER: - MagickCropImage (magick_wand, new_w, new_h, (w - new_w) / 2, (h - new_h) / 2); - break; - case CROP_HINT_LEFT: - MagickCropImage (magick_wand, new_w, new_h, 0, (h - new_h) / 2); - break; - case CROP_HINT_RIGHT: - MagickCropImage (magick_wand, new_w, new_h, w - new_w, (h - new_h) / 2); - break; - case CROP_HINT_TOP: - MagickCropImage (magick_wand, new_w, new_h, (w - new_w) / 2, 0); - break; - case CROP_HINT_BOTTOM: - MagickCropImage (magick_wand, new_w, new_h, (w - new_w) / 2, h - new_h); - break; - } - } - } - - /* Shave the source image to match exact dimensions after resize */ - if (hidpi_strict_dimensions && (! thumbnail || exif->thumbnail_crop_style == CROP_STYLE_NORMAL)) { - w = MagickGetImageWidth (magick_wand); - h = MagickGetImageHeight (magick_wand); - source_aspect = (double) w / (double) h; - target_aspect = (double) size_x / (double) size_y; - if (source_aspect != target_aspect) { - if (target_aspect >= source_aspect) { - new_w = w; - new_h = lround ((double) w / target_aspect); - } else { - new_w = lround ((double) h * target_aspect); - new_h = h; - } - MagickCropImage (magick_wand, new_w, new_h, (w - new_w) / 2, (h - new_h) / 2); - g_warn_if_fail (MagickGetImageWidth (magick_wand) == new_w); - g_warn_if_fail (MagickGetImageHeight (magick_wand) == new_h); - } - } - - if (resize_opts == NULL) { - /* Perform internal resizing */ - /* Note: MagickResizeImage() does no aspect correction, stretching the image to the required dimensions */ - if (thumbnail) { - MagickThumbnailImage (magick_wand, size_x, size_y); - } else { -#ifdef HAVE_IMAGEMAGICK_7 - MagickResizeImage (magick_wand, size_x, size_y, LanczosFilter); -#else - MagickResizeImage (magick_wand, size_x, size_y, LanczosFilter, 1.0); -#endif - } - } else { - /* Perform resizing through ImageMagick commandline parser */ - res_id = g_strdup_printf ("cgg_resize_image_%p", g_thread_self ()); - mpr_res_id = g_strdup_printf ("mpr:%s", res_id); - if (MagickWriteImage (magick_wand, mpr_res_id) == MagickFalse) { - description = MagickGetException (magick_wand, &severity); - log_error ("Error writing mpr image: %s %s %ld %s\n", GetMagickModule(), description); - MagickRelinquishMemory (description); - DestroyMagickWand (magick_wand); - g_free (res_id); - g_free (mpr_res_id); - return FALSE; - } - ClearMagickWand (magick_wand); - - cmd_args = parse_cmd_args (resize_opts, "convert", NULL, mpr_res_id, mpr_res_id, size_x, size_y); - image_info = AcquireImageInfo (); - g_assert (image_info != NULL); - exception_info = AcquireExceptionInfo (); - g_assert (exception_info != NULL); - if (MagickCommandGenesis (image_info, ConvertImageCommand, g_strv_length (cmd_args), cmd_args, NULL, exception_info) == MagickFalse) { - /* MagickCommandGenesis() should've printed verbose error message */ - DestroyImageInfo (image_info); - DestroyExceptionInfo (exception_info); - DestroyMagickWand (magick_wand); - g_free (res_id); - g_free (mpr_res_id); - return FALSE; - } - DestroyImageInfo (image_info); - DestroyExceptionInfo (exception_info); - g_strfreev (cmd_args); - - if (MagickReadImage (magick_wand, mpr_res_id) == MagickFalse) { - description = MagickGetException (magick_wand, &severity); - printf ("Error reading mpr image: %s %s %ld %s\n", GetMagickModule(), description); - MagickRelinquishMemory (description); - DestroyMagickWand (magick_wand); - g_free (res_id); - g_free (mpr_res_id); - return FALSE; - } - g_free (mpr_res_id); - } - } - - if (thumbnail) { - /* FIXME: this strips image ICC profile, should do proper conversion first */ - MagickStripImage (magick_wand); - } - - if ((int) MagickGetImageCompressionQuality (magick_wand) != quality) - MagickSetImageCompressionQuality (magick_wand, quality); - - /* Write the image and destroy it. */ - if (MagickWriteImage (magick_wand, dst) == MagickFalse) { - description = MagickGetException (magick_wand, &severity); - log_error ("Error writing image: %s %s %ld %s\n", GetMagickModule(), description); - DeleteImageRegistry (res_id); - g_free (res_id); - return FALSE; - } - - if (res_id) { - /* This is potentially dangerous operation - modifying ImageMagick's internal image registry */ - DeleteImageRegistry (res_id); - g_free (res_id); - } - magick_wand = DestroyMagickWand (magick_wand); - - return TRUE; -} - - -/* - * get_image_sizes: retrieve image dimensions - */ -void -get_image_sizes (const gchar *img, - unsigned long *width, unsigned long *height, - int *quality, - gboolean autorotate) -{ - MagickWand *magick_wand; - MagickBooleanType b; - ExceptionType severity; - gchar *description; - - *width = 0; - *height = 0; - if (quality) - *quality = -1; - - g_assert (img != NULL); - - /* Read an image. */ - magick_wand = NewMagickWand(); - if (autorotate) - b = MagickReadImage (magick_wand, img); - else - b = MagickPingImage (magick_wand, img); - if (b == MagickFalse) { - description = MagickGetException (magick_wand, &severity); - /* -- make it silent - log_error ("Error reading image info: %s %s %ld %s\n", GetMagickModule(), description); - */ - MagickRelinquishMemory(description); - return; - } - - if (autorotate) - autorotate_image (magick_wand); - - *width = MagickGetImageWidth (magick_wand); - *height = MagickGetImageHeight (magick_wand); - if (quality) - *quality = (int) MagickGetImageCompressionQuality (magick_wand); - - magick_wand = DestroyMagickWand (magick_wand); -} - - -/* - * calculate_sizes: calculate maximal image sizes within specified limits keeping aspect ratio - */ -void -calculate_sizes (const unsigned long max_width, const unsigned long max_height, - unsigned long *width, unsigned long *height) -{ - if (max_width > *width && max_height > *height) - return; - - double max_ratio = (double) max_width / (double) max_height; - double real_ratio = (double) *width / (double) *height; - - if (*width > *height && max_ratio <= real_ratio) { - *height = (unsigned long) (max_width / real_ratio); - *width = max_width; - } else { - *width = (unsigned long) (max_height * real_ratio); - *height = max_height; - } -} - -static gboolean -shift_exif_time (Exiv2::ExifData& exifData, const char *key, int amount) -{ - struct tm *tt; - gchar *s; - gchar *st; - gboolean res; - - res = FALSE; - try { - if (exifData[key].count() > 0) { - s = exiv2_str_val (exifData, key); - - if (s && strlen (s) > 0) { - tt = parse_exif_date (s); - if (tt) { - shift_time (tt, amount); - st = format_exif_time (tt); - if (st) - exifData[key] = st; - g_free (st); - g_free (tt); - res = TRUE; - } - } - g_free (s); - } - } catch (...) { } - - return res; -} - -static gboolean -shift_iptc_time (Exiv2::IptcData &iptcData, const char *date_key, const char *time_key, int amount) -{ - struct tm tt = {0}; - long int orig_time; - gboolean res; - Exiv2::DateValue dval; - Exiv2::TimeValue tval; - - res = FALSE; - if (iptc_has_key (iptcData, date_key) || iptc_has_key (iptcData, time_key)) { - orig_time = (iptc_has_key (iptcData, date_key) ? iptcData[date_key].toLong() : 0) + - (iptc_has_key (iptcData, time_key) ? iptcData[time_key].toLong() : 0); - if (orig_time > 0) { - orig_time += amount * 60; - localtime_r (&orig_time, &tt); - mktime (&tt); - if (tt.tm_isdst) - shift_time (&tt, -60); - - dval = Exiv2::DateValue(tt.tm_year + 1900, tt.tm_mon + 1, tt.tm_mday); - tval = Exiv2::TimeValue(tt.tm_hour, tt.tm_min, tt.tm_sec, 0, 0); - iptcData[date_key].setValue(&dval); - iptcData[time_key].setValue(&tval); - res = TRUE; - } - } - - return res; -} - -static gboolean -override_exif_time (Exiv2::ExifData &exifData, const char *key, time_t datetime) -{ - struct tm tt = {0}; - gchar *st; - gboolean res; - - res = FALSE; - try { - if (exifData[key].count() > 0) { - localtime_r (&datetime, &tt); - st = format_exif_time (&tt); - if (st) - exifData[key] = st; - g_free (st); - res = TRUE; - } - } catch (...) { } - - return res; -} - -static gboolean -override_iptc_time (Exiv2::IptcData &iptcData, const char *date_key, const char *time_key, time_t datetime) -{ - struct tm tt = {0}; - Exiv2::DateValue dval; - Exiv2::TimeValue tval; - - if (iptc_has_key (iptcData, date_key) || iptc_has_key (iptcData, time_key)) { - localtime_r (&datetime, &tt); - - dval = Exiv2::DateValue(tt.tm_year + 1900, tt.tm_mon + 1, tt.tm_mday); - tval = Exiv2::TimeValue(tt.tm_hour, tt.tm_min, tt.tm_sec, 0, 0); - iptcData[date_key].setValue(&dval); - iptcData[time_key].setValue(&tval); - - return TRUE; - } - - return FALSE; -} - - -/* List of tags we don't want to copy from external EXIF data since they are related to the RAW file, - * not the processed image. Note that this list is not by far complete. - */ -static const gchar * image_size_tags[] = { - "Exif.Image.ImageWidth", - "Exif.Image.ImageHeight", - "Exif.Image.ImageLength", - "Exif.Image.Orientation", - "Exif.Photo.PixelXDimension", - "Exif.Photo.PixelYDimension", - "Exif.Image.XResolution", - "Exif.Image.YResolution", - "Exif.Image.ResolutionUnit", - "Exif.Image.Compression", - "Exif.Image.BitsPerSample", - "Exif.Image.SamplesPerPixel", - "Exif.Photo.ComponentsConfiguration", - "Exif.Photo.CompressedBitsPerPixel", - "Exif.Image.JPEGTables", - "Exif.Image.JPEGProc", - "Exif.Image.JPEGInterchangeFormat", - "Exif.Image.JPEGInterchangeFormatLength", - "Exif.Image.JPEGRestartInterval", - "Exif.Image.JPEGLosslessPredictors", - "Exif.Image.JPEGPointTransforms", - "Exif.Image.JPEGQTables", - "Exif.Image.JPEGDCTables", - "Exif.Image.JPEGACTables", - "Exif.Image.YCbCrCoefficients", - "Exif.Image.YCbCrSubSampling", - "Exif.Image.YCbCrPositioning", - "Exif.Image.ReferenceBlackWhite", - "Exif.Image.PhotometricInterpretation", - "Exif.Image.PlanarConfiguration", -}; - -static gboolean -is_image_size_tag (const gchar *s) -{ - unsigned int i; - - for (i = 0; i < G_N_ELEMENTS (image_size_tags); i++) - if (g_str_equal (s, image_size_tags[i])) - return TRUE; - return FALSE; -} - -static void -copy_metadata (Exiv2::Image::AutoPtr &img, Exiv2::Image::AutoPtr &external_img) -{ - Exiv2::ExifData exifData; - Exiv2::ExifData &img_exifData = img->exifData(); - Exiv2::ExifData &ext_exifData = external_img->exifData(); - - /* First copy metadata from the external image excluding size tags */ - Exiv2::ExifData::const_iterator end = ext_exifData.end(); - for (Exiv2::ExifData::const_iterator i = ext_exifData.begin(); i != end; ++i) { - gchar *s = g_strdup (i->key().c_str()); - if (! is_image_size_tag (s)) - exifData[s] = ext_exifData[s]; - g_free (s); - } - - /* Copy selected size tags from the processed image */ - end = img_exifData.end(); - for (Exiv2::ExifData::const_iterator i = img_exifData.begin(); i != end; ++i) { - gchar *s = g_strdup (i->key().c_str()); - if (is_image_size_tag (s)) - exifData[s] = img_exifData[s]; - g_free (s); - } - - img->setMetadata (*external_img); - img->setExifData (exifData); -} - - - -/* - * modify_exif: - strip thumbnail stored in EXIF table - * - write down overriden keys - */ -void -modify_exif (const gchar *filename, ExifData *exif, gboolean strip_thumbnail, gboolean strip_xmp) -{ - gboolean modified; - gboolean res; - - g_assert (filename != NULL); - - if (! strip_thumbnail && exif == NULL) - return; - - modified = FALSE; - try { - Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open (filename); - g_assert (image.get() != 0); - - image->readMetadata(); - - /* Write down metadata from external file if supplied */ - if (exif && exif->external_exif_data) { - Exiv2::Image::AutoPtr ext_image = Exiv2::ImageFactory::open (exif->external_exif_data); - if (ext_image.get() != 0) { - ext_image->clearMetadata(); - ext_image->readMetadata(); - copy_metadata (image, ext_image); - modified = TRUE; - } - } - - Exiv2::ExifData &exifData = image->exifData(); - Exiv2::IptcData &iptcData = image->iptcData(); - - if (exif) { - if (exif->override_copyright) { - if (! exifData.empty()) { - exifData["Exif.Image.Copyright"] = exif->override_copyright; - modified = TRUE; - } - if (! iptcData.empty()) { - iptcData["Iptc.Application2.Copyright"] = exif->override_copyright; - modified = TRUE; - } - } - - if (exif->timezone_shift != 0 && exif->override_datetime == (time_t) -1) { - if (! exifData.empty()) { - res = shift_exif_time (exifData, "Exif.Photo.DateTimeOriginal", exif->timezone_shift); - res = shift_exif_time (exifData, "Exif.Photo.DateTimeDigitized", exif->timezone_shift) || res; - res = shift_exif_time (exifData, "Exif.Image.DateTime", exif->timezone_shift) || res; - modified = TRUE; - } - if (! iptcData.empty()) { - res = shift_iptc_time (iptcData, "Iptc.Application2.DateCreated", "Iptc.Application2.TimeCreated", exif->timezone_shift); - res = shift_iptc_time (iptcData, "Iptc.Application2.DigitizationDate", "Iptc.Application2.DigitizationTime", exif->timezone_shift) || res; - if (res) - modified = TRUE; - } - } - - if (exif->override_datetime != (time_t) -1 && !exifData.empty()) { - if (! exifData.empty()) { - res = override_exif_time (exifData, "Exif.Photo.DateTimeOriginal", exif->override_datetime); - res = override_exif_time (exifData, "Exif.Photo.DateTimeDigitized", exif->override_datetime) || res; - res = override_exif_time (exifData, "Exif.Image.DateTime", exif->override_datetime) || res; - modified = TRUE; - } - if (! iptcData.empty()) { - res = override_iptc_time (iptcData, "Iptc.Application2.DateCreated", "Iptc.Application2.TimeCreated", exif->override_datetime); - res = override_iptc_time (iptcData, "Iptc.Application2.DigitizationDate", "Iptc.Application2.DigitizationTime", exif->override_datetime) || res; - if (res) - modified = TRUE; - } - } - - if (exif->override_aperture != -1) { - if (! exifData.empty()) { - exifData["Exif.Photo.FNumber"] = Exiv2::floatToRationalCast (exif->override_aperture); - if (exifData["Exif.Photo.ApertureValue"].count() > 0) - exifData["Exif.Photo.ApertureValue"] = Exiv2::floatToRationalCast (exif->override_aperture); - modified = TRUE; - } - } - - if (exif->override_focal_length != -1) { - if (! exifData.empty()) { - exifData["Exif.Photo.FocalLength"] = Exiv2::floatToRationalCast (exif->override_focal_length); - modified = TRUE; - } - } - - if (exif->override_artist_name) { - if (! exifData.empty()) { - exifData["Exif.Image.Artist"] = exif->override_artist_name; - if (exifData["Exif.Photo.CameraOwnerName"].count() >= 1) - exifData["Exif.Photo.CameraOwnerName"] = exif->override_artist_name; - if (exifData["Exif.Canon.OwnerName"].count() >= 1) - exifData["Exif.Canon.OwnerName"] = exif->override_artist_name; - modified = TRUE; - } - } - } - - - if (strip_thumbnail && ! exifData.empty()) { -#ifdef HAVE_EXIFTHUMB - Exiv2::ExifThumb exifThumb(image->exifData()); - std::string thumbExt = exifThumb.extension(); -#else - std::string thumbExt = exifData.thumbnailExtension(); -#endif - - if (! thumbExt.empty()) { -#ifdef HAVE_EXIFTHUMB - exifThumb.erase(); -#else - exifData.eraseThumbnail(); -#endif - modified = TRUE; - } - } - - if (strip_xmp) { - if (! image->xmpData().empty()) { - image->clearXmpData (); - modified = TRUE; - } - if (! image->xmpPacket().empty()) { - image->clearXmpPacket (); - modified = TRUE; - } - } - - if (modified) - image->writeMetadata(); - } - catch (Exiv2::AnyError& e) - { - log_error ("modify_exif: Caught Exiv2 exception: '%s'\n", e.what()); - } -} |
