From 1e355114311d7bf44b96b11b745857a2b68e8a06 Mon Sep 17 00:00:00 2001 From: Tomas Bzatek Date: Sat, 2 Aug 2008 13:50:16 +0200 Subject: Port to exiv2 library Now we are able to read EXIF, IPTC and JPEG metadata such as caption, copyright, author etc. though we don't display them anywhere yet. Fixes weird shutter times. --- Makefile | 8 +- README | 5 +- generators.c | 3 +- jpeg-utils.c | 280 ----------------------------------------- jpeg-utils.cpp | 387 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ jpeg-utils.h | 28 ++++- 6 files changed, 419 insertions(+), 292 deletions(-) delete mode 100644 jpeg-utils.c create mode 100644 jpeg-utils.cpp diff --git a/Makefile b/Makefile index 86707ce..2daafa4 100644 --- a/Makefile +++ b/Makefile @@ -13,15 +13,15 @@ OBJECTS=gallery-utils.o jpeg-utils.o xml-parser.o setup.o items.o generators.o c .SUFFIXES: .c .cpp .c.o: - $(CC) $(CFLAGS) `pkg-config libxml-2.0 glib-2.0 libexif --cflags` `Wand-config --cflags` -c $< + $(CC) $(CFLAGS) `pkg-config libxml-2.0 glib-2.0 --cflags` `Wand-config --cflags` -c $< .cpp.o: - $(CC) $(CFLAGS) `pkg-config libxml-2.0 glib-2.0 libexif --cflags` `Wand-config --cppflags` -c $< + $(CPP) $(CFLAGS) `pkg-config libxml-2.0 glib-2.0 exiv2 --cflags` `Wand-config --cppflags` -c $< all cgg: check $(OBJECTS) - $(CC) -o cgg $(OBJECTS) -lm $(CFLAGS) `pkg-config libxml-2.0 glib-2.0 libexif --libs` `Wand-config --ldflags --libs` + $(CC) -o cgg $(OBJECTS) -lm $(CFLAGS) `pkg-config libxml-2.0 glib-2.0 exiv2 --libs` `Wand-config --ldflags --libs` cgg.o: cgg.c -jpeg-utils.o: jpeg-utils.c jpeg-utils.h +jpeg-utils.o: jpeg-utils.cpp jpeg-utils.h gallery-utils.o: gallery-utils.c gallery-utils.h setup.o: setup.c setup.h xml-parser.o: xml-parser.c xml-parser.h diff --git a/README b/README index 9ad1df0..f09f771 100644 --- a/README +++ b/README @@ -18,14 +18,13 @@ Feature highlights: * fast, easy to cache/mirror * XML-based description files * modern design, valid XHTML 1.0 and CSS 2 - * EXIF support + * EXIF & IPTC support * console application, allowing easy scripting (e.g. auto-refresh after new images are uploaded via FTP) Planned features: * prefetch of next image (JavaScript) * strip unnecessary blocks from images - EXIF, thumbnails - * port to exiv2 library to get rid of that weird shutter times, IPTC support * support adding comments and copyright notes to images * switchable flat album view * groups in the album - e.g. separate years, decades... @@ -57,7 +56,7 @@ BUILDING First make sure you have meet the following requirements: libxml-2.0 (tested with 2.6.32) glib-2.0 (2.12.0 recommented, tested with 2.16.3) - libexif (tested with 0.6.16) + exiv2 (tested with 0.17) ImageMagick (tested with 6.4.2.0) To compile cgg, type 'make' diff --git a/generators.c b/generators.c index 3639309..a478389 100644 --- a/generators.c +++ b/generators.c @@ -486,8 +486,7 @@ write_html_image (TGallerySetup *setup, /* Get EXIF data from the original image */ - exif = malloc (sizeof (TExifData)); - if (get_exif (original_img, exif)) + if (get_exif (original_img, &exif)) fprintf (stderr, "write_html_image: error getting exif data from file \"%s\"\n", orig_dst); /* Retrieve image sizes of preview and original image */ diff --git a/jpeg-utils.c b/jpeg-utils.c deleted file mode 100644 index dc1c880..0000000 --- a/jpeg-utils.c +++ /dev/null @@ -1,280 +0,0 @@ -/* Cataract - Static web photo gallery generator - * Copyright (C) 2008 Tomas Bzatek - * - * 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 -#include -#include - -#include -#include -#include - -#include - -#include "jpeg-utils.h" - - - - -/* - * get_exif: retrieve EXIF informations from a JPEG image - */ -int -get_exif (const char *filename, TExifData *data) -{ - ExifData *edata; - ExifEntry *entry; - ExifContent *content; - int i, j; - - if (data == NULL) - data = malloc (sizeof (TExifData)); - memset (data, 0, sizeof (TExifData)); - edata = exif_data_new_from_file (filename); - if (edata == NULL) - return -1; - - for (i = 0; i < EXIF_IFD_COUNT; i++) { - content = edata->ifd[i]; - if ((content == NULL) || (content->count == 0)) - continue; - - for (j = 0; j < content->count; j++) { - entry = content->entries[j]; - - if (! content->entries[j]) - continue; - - #define VALUE_LEN 1024 - char value[VALUE_LEN + 1]; - - switch (entry->tag) - { - case EXIF_TAG_DATE_TIME: - data->datetime = strdup (exif_entry_get_value (entry, value, VALUE_LEN)); - break; - - case EXIF_TAG_DATE_TIME_ORIGINAL: - case EXIF_TAG_DATE_TIME_DIGITIZED: - if (data->datetime == NULL) - data->datetime = strdup (exif_entry_get_value (entry, value, VALUE_LEN)); - break; - - case EXIF_TAG_MODEL: - if (data->camera_model == NULL) - data->camera_model = strdup (exif_entry_get_value (entry, value, VALUE_LEN)); - break; - - case EXIF_TAG_MAKE: - if (data->camera_model == NULL) - data->camera_model = strdup (exif_entry_get_value (entry, value, VALUE_LEN)); - break; - - case EXIF_TAG_ISO_SPEED_RATINGS: - data->iso = strdup (exif_entry_get_value (entry, value, VALUE_LEN)); - break; - - case EXIF_TAG_FOCAL_LENGTH: - data->focal_length = strdup (exif_entry_get_value (entry, value, VALUE_LEN)); - break; - - case EXIF_TAG_FOCAL_LENGTH_IN_35MM_FILM: - data->focal_length_35mm = strdup (exif_entry_get_value (entry, value, VALUE_LEN)); - break; - - case EXIF_TAG_FNUMBER: - data->aperture = strdup (exif_entry_get_value (entry, value, VALUE_LEN)); - break; - - case EXIF_TAG_APERTURE_VALUE: - if (data->aperture == NULL) - data->aperture = strdup (exif_entry_get_value (entry, value, VALUE_LEN)); - break; - - case EXIF_TAG_EXPOSURE_TIME: - data->exposure = strdup (exif_entry_get_value (entry, value, VALUE_LEN)); - break; - - case EXIF_TAG_FLASH: - data->flash = strdup (exif_entry_get_value (entry, value, VALUE_LEN)); - break; - - case EXIF_TAG_PIXEL_X_DIMENSION: - data->width = atol (exif_entry_get_value (entry, value, VALUE_LEN)); - break; - - case EXIF_TAG_PIXEL_Y_DIMENSION: - data->height = atol (exif_entry_get_value (entry, value, VALUE_LEN)); - break; - - default: - break; - } - } - } - exif_data_free (edata); - - #ifdef __DEBUG_ALL__ - printf("EXIF_TAG_DATE_TIME = %s\n", data->datetime); - printf("EXIF_TAG_MODEL = %s\n", data->camera_model); - printf("EXIF_TAG_ISO_SPEED_RATINGS = %s\n", data->iso); - printf("EXIF_TAG_FOCAL_LENGTH = %s\n", data->focal_length); - printf("EXIF_TAG_FOCAL_LENGTH_IN_35MM_FILM = %s\n", data->focal_length_35mm); - printf("EXIF_TAG_FNUMBER = %s\n", data->aperture); - printf("EXIF_TAG_EXPOSURE_TIME = %s\n", data->exposure); - printf("EXIF_TAG_FLASH = %s\n", data->flash); - printf("EXIF_TAG_PIXEL_X_DIMENSION = %lu\n", data->width); - printf("EXIF_TAG_PIXEL_Y_DIMENSION = %lu\n", data->height); - #endif - - return 0; -} - -/* - * free_exif_struct: free allocated structure - */ -void -free_exif_data (TExifData *data) -{ - if (data) { - if (data->aperture) - free (data->aperture); - if (data->camera_model) - free (data->camera_model); - if (data->datetime) - free (data->datetime); - if (data->exposure) - free (data->exposure); - if (data->flash) - free (data->flash); - if (data->focal_length) - free (data->focal_length); - if (data->focal_length_35mm) - free (data->focal_length_35mm); - if (data->iso) - free (data->iso); - free (data); - data = NULL; - } -} - - -/* - * resize_image: resize image pointed by src and save result to dst - */ -gboolean -resize_image (const char *src, const char *dst, - int size_x, int size_y, - int quality) -{ - #define ThrowWandException(wand) \ - { \ - char *description; \ - ExceptionType severity; \ - \ - description = MagickGetException (wand, &severity); \ - (void) fprintf (stderr, "Error converting image: %s %s %ld %s\n", GetMagickModule(), description); \ - description = (char*) MagickRelinquishMemory (description); \ - return FALSE; \ - } - - MagickBooleanType status; - MagickWand *magick_wand; - - /* Read an image. */ - MagickWandGenesis(); - magick_wand = NewMagickWand(); - status = MagickReadImage (magick_wand, src); - if (status == MagickFalse) - ThrowWandException (magick_wand); - MagickResizeImage (magick_wand, size_x, size_y, LanczosFilter, 1.0); - MagickSetImageCompressionQuality (magick_wand, quality); - - /* Write the image and destroy it. */ - status = MagickWriteImage (magick_wand, dst); - if (status == MagickFalse) - ThrowWandException (magick_wand); - magick_wand = DestroyMagickWand (magick_wand); - MagickWandTerminus(); - - return TRUE; -} - - -/* - * get_image_sizes: retrieve image dimensions - */ -void -get_image_sizes (const char *img, - unsigned long *width, unsigned long *height) -{ - #define xThrowWandException(wand) \ - { \ - char *description; \ - ExceptionType severity; \ - \ - description = MagickGetException (wand, &severity); \ - (void) fprintf (stderr, "Error reading image info: %s %s %ld %s\n", GetMagickModule(), description); \ - description = (char*) MagickRelinquishMemory(description); \ - return; \ - } - - MagickBooleanType status; - MagickWand *magick_wand; - - *width = -1; - *height = -1; - - /* Read an image. */ - MagickWandGenesis(); - magick_wand = NewMagickWand(); - status = MagickPingImage (magick_wand, img); - if (status == MagickFalse) - xThrowWandException (magick_wand); - *width = MagickGetImageWidth (magick_wand); - *height = MagickGetImageHeight (magick_wand); - - magick_wand = DestroyMagickWand (magick_wand); - MagickWandTerminus(); -} - - -/* - * 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 = max_width / real_ratio; - *width = max_width; - } - else - { - *width = max_height * real_ratio; - *height = max_height; - } -} - diff --git a/jpeg-utils.cpp b/jpeg-utils.cpp new file mode 100644 index 0000000..973b448 --- /dev/null +++ b/jpeg-utils.cpp @@ -0,0 +1,387 @@ +/* Cataract - Static web photo gallery generator + * Copyright (C) 2008 Tomas Bzatek + * + * 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 +#include +#include + +#include +#include + +#include + +#include "jpeg-utils.h" + + + +/* + * get_exif: retrieve EXIF informations from a JPEG image + */ +int +get_exif (const char *filename, TExifData **exif_data) +{ + TExifData *data; + + data = (TExifData*) malloc (sizeof (TExifData)); + memset (data, 0, sizeof (TExifData)); + *exif_data = data; + + 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 { + const char *val = exifData["Exif.Image.Model"].toString().c_str(); + if (val && strlen(val) > 0) + data->camera_model = strdup (val); + } 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) { + static struct tm tt; + static char conv[1024]; + + 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--; + if (strftime (&conv[0], sizeof(conv), "%c", &tt)) + data->datetime = 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 = strdup ((char *) "Flash fired"); + else + data->flash = strdup ((char *) "--"); + } 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 = strdup (exifData["Exif.Image.Software"].toString().c_str()); + } catch (...) { } + + /* EXIF::Image description */ + try { + data->exif_imgdesc = strdup (exifData["Exif.Image.ImageDescription"].toString().c_str()); + } catch (...) { } + + /* EXIF::Artist */ + try { + data->exif_artist = strdup (exifData["Exif.Image.Artist"].toString().c_str()); + } catch (...) { } + + /* EXIF::Copyright */ + try { + data->exif_copyright = strdup (exifData["Exif.Image.Copyright"].toString().c_str()); + } catch (...) { } + + /* EXIF::User comment */ + try { + data->exif_usercomment = strdup (exifData["Exif.Photo.UserComment"].toString().c_str()); + } catch (...) { } + } + + Exiv2::IptcData &iptcData = image->iptcData(); + if (! iptcData.empty()) { + /* IPTC::Object name */ + try { + data->iptc_objectname = strdup (iptcData["Iptc.Application2.ObjectName"].toString().c_str()); + } catch (...) { } + + /* IPTC::Copyright */ + try { + data->iptc_copyright = strdup (iptcData["Iptc.Application2.Copyright"].toString().c_str()); + } catch (...) { } + + /* IPTC::Credit */ + try { + data->iptc_credit = strdup (iptcData["Iptc.Application2.Credit"].toString().c_str()); + } catch (...) { } + + /* IPTC::Caption */ + try { + data->iptc_caption = strdup (iptcData["Iptc.Application2.Caption"].toString().c_str()); + } catch (...) { } + + /* IPTC::Author */ + try { + data->iptc_author = strdup (iptcData["Iptc.Application2.Byline"].toString().c_str()); + } catch (...) { } + } + + } + catch (Exiv2::AnyError& e) + { + fprintf (stderr, "get_exif: Caught Exiv2 exception: '%s'\n", e.what()); + return -1; + } + + /* Read the JPEG comment */ + MagickBooleanType status; + MagickWand *magick_wand; + char *comment; + + MagickWandGenesis(); + magick_wand = NewMagickWand(); + status = MagickPingImage (magick_wand, filename); + if (status == MagickTrue) { + comment = MagickGetImageProperty(magick_wand, "comment"); + if (comment) + data->jpeg_comment = strdup (comment); + MagickRelinquishMemory (comment); + } + magick_wand = DestroyMagickWand (magick_wand); + MagickWandTerminus(); + + +#ifdef __DEBUG_ALL__ + printf("EXIF_TAG_DATE_TIME = '%s'\n", data->datetime); + printf("EXIF_TAG_MODEL = '%s'\n", data->camera_model); + printf("EXIF_TAG_ISO_SPEED_RATINGS = '%s'\n", data->iso); + printf("EXIF_TAG_FOCAL_LENGTH = '%s'\n", data->focal_length); + printf("EXIF_TAG_FOCAL_LENGTH_IN_35MM_FILM = '%s'\n", data->focal_length_35mm); + printf("EXIF_TAG_FNUMBER = '%s'\n", data->aperture); + printf("EXIF_TAG_EXPOSURE_TIME = '%s'\n", data->exposure); + printf("EXIF_TAG_FLASH = '%s'\n", data->flash); + printf("Exif.Image.Software = '%s'\n", data->exif_software); + printf("Exif.Image.ImageDescription = '%s'\n", data->exif_imgdesc); + printf("Exif.Image.Artist = '%s'\n", data->exif_artist); + printf("Exif.Image.Copyright = '%s'\n", data->exif_copyright); + printf("Exif.Photo.UserComment = '%s'\n", data->exif_usercomment); + printf("Iptc.Application2.ObjectName = '%s'\n", data->iptc_objectname); + printf("Iptc.Application2.Copyright = '%s'\n", data->iptc_copyright); + printf("Iptc.Application2.Credit = '%s'\n", data->iptc_credit); + printf("Iptc.Application2.Caption = '%s'\n", data->iptc_caption); + printf("Iptc.Application2.Byline = '%s'\n", data->iptc_author); + printf("JPEG comment = '%s'\n", data->jpeg_comment); +#endif + + return 0; +} + +/* + * free_exif_struct: free allocated structure + */ +void +free_exif_data (TExifData *data) +{ + if (data) { + if (data->aperture) + free (data->aperture); + if (data->camera_model) + free (data->camera_model); + if (data->datetime) + free (data->datetime); + if (data->exposure) + free (data->exposure); + if (data->flash) + free (data->flash); + if (data->focal_length) + free (data->focal_length); + if (data->focal_length_35mm) + free (data->focal_length_35mm); + if (data->iso) + free (data->iso); + + if (data->exif_software) + free (data->exif_software); + if (data->exif_imgdesc) + free (data->exif_imgdesc); + if (data->exif_artist) + free (data->exif_artist); + if (data->exif_copyright) + free (data->exif_copyright); + if (data->exif_usercomment) + free (data->exif_usercomment); + + if (data->iptc_objectname) + free (data->iptc_objectname); + if (data->iptc_copyright) + free (data->iptc_copyright); + if (data->iptc_credit) + free (data->iptc_credit); + if (data->iptc_caption) + free (data->iptc_caption); + if (data->iptc_author) + free (data->iptc_author); + + if (data->jpeg_comment) + free (data->jpeg_comment); + + free (data); + data = NULL; + } +} + + +/* + * resize_image: resize image pointed by src and save result to dst + */ +gboolean +resize_image (const char *src, const char *dst, + int size_x, int size_y, + int quality) +{ + #define ThrowWandException(wand) \ + { \ + char *description; \ + ExceptionType severity; \ + \ + description = MagickGetException (wand, &severity); \ + (void) fprintf (stderr, "Error converting image: %s %s %ld %s\n", GetMagickModule(), description); \ + description = (char*) MagickRelinquishMemory (description); \ + return FALSE; \ + } + + MagickBooleanType status; + MagickWand *magick_wand; + + /* Read an image. */ + MagickWandGenesis(); + magick_wand = NewMagickWand(); + status = MagickReadImage (magick_wand, src); + if (status == MagickFalse) + ThrowWandException (magick_wand); + MagickResizeImage (magick_wand, size_x, size_y, LanczosFilter, 1.0); + MagickSetImageCompressionQuality (magick_wand, quality); + + /* Write the image and destroy it. */ + status = MagickWriteImage (magick_wand, dst); + if (status == MagickFalse) + ThrowWandException (magick_wand); + magick_wand = DestroyMagickWand (magick_wand); + MagickWandTerminus(); + + return TRUE; +} + + +/* + * get_image_sizes: retrieve image dimensions + */ +void +get_image_sizes (const char *img, + unsigned long *width, unsigned long *height) +{ + #define xThrowWandException(wand) \ + { \ + char *description; \ + ExceptionType severity; \ + \ + description = MagickGetException (wand, &severity); \ + (void) fprintf (stderr, "Error reading image info: %s %s %ld %s\n", GetMagickModule(), description); \ + description = (char*) MagickRelinquishMemory(description); \ + return; \ + } + + MagickBooleanType status; + MagickWand *magick_wand; + + *width = -1; + *height = -1; + + /* Read an image. */ + MagickWandGenesis(); + magick_wand = NewMagickWand(); + status = MagickPingImage (magick_wand, img); + if (status == MagickFalse) + xThrowWandException (magick_wand); + *width = MagickGetImageWidth (magick_wand); + *height = MagickGetImageHeight (magick_wand); + + magick_wand = DestroyMagickWand (magick_wand); + MagickWandTerminus(); +} + + +/* + * 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 = max_width / real_ratio; + *width = max_width; + } + else + { + *width = max_height * real_ratio; + *height = max_height; + } +} + diff --git a/jpeg-utils.h b/jpeg-utils.h index 56bb51d..4c47d3c 100644 --- a/jpeg-utils.h +++ b/jpeg-utils.h @@ -15,9 +15,15 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifdef __cplusplus + extern "C" { +#endif + #include +/* TODO: we want to have numerical values here at some point in the future */ typedef struct { char *datetime; char *camera_model; @@ -27,15 +33,27 @@ typedef struct { char *aperture; char *exposure; char *flash; - unsigned long width; - unsigned long height; + + char *exif_software; + char *exif_imgdesc; + char *exif_artist; + char *exif_copyright; + char *exif_usercomment; + + char *iptc_objectname; + char *iptc_copyright; + char *iptc_credit; + char *iptc_caption; + char *iptc_author; + + char *jpeg_comment; } TExifData; /* * get_exif: retrieve EXIF info from a JPEG image */ -int get_exif (const char *filename, TExifData *data); +int get_exif (const char *filename, TExifData **exif_data); /* * free_exif_struct: free allocated structure @@ -63,3 +81,7 @@ void get_image_sizes (const char *img, void calculate_sizes (const unsigned long max_width, const unsigned long max_height, unsigned long *width, unsigned long *height); + +#ifdef __cplusplus + } +#endif \ No newline at end of file -- cgit v1.2.3