/*
- * Geeqie
- * (C) 2006 John Ellis
- * Copyright (C) 2008 The Geeqie Team
+ * Copyright (C) 2003, 2006 John Ellis
+ * Copyright (C) 2008 - 2016 The Geeqie Team
*
- * Authors:
- * Support for Exif file format, originally written by Eric Swalens.
- * Modified by Quy Tonthat
+ * Authors: Eric Swalens, Quy Tonthat
*
- * Reimplemented with generic data storage by John Ellis (Nov 2003)
+ * 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
+ * (at your option) 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.
*
* The tags were added with information from the FREE document:
* http://www.ba.wakwak.com/~tsuruzoh/Computer/Digicams/exif-e.html
* Unsupported at this time:
* IFD1 (thumbnail)
* MakerNote
- * GPSInfo
*
* TODO:
* Convert data to useable form in the ??_as_text function for:
* ComponentsConfiguration
* UserComment (convert this to UTF-8?)
- *
-
- 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
- (at your option) 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., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
+ * Add support for marker tag 0x0000
+ */
#ifdef HAVE_CONFIG_H
# include "config.h"
#include <math.h>
#include <glib.h>
+#include <glib/gprintf.h>
#include "intl.h"
#include "main.h"
#include "exif-int.h"
+#include "jpeg_parser.h"
#include "format_raw.h"
#include "ui_fileops.h"
/* tags that are special, or need special treatment */
#define TAG_EXIFOFFSET 0x8769
#define TAG_EXIFMAKERNOTE 0x927c
+#define TAG_GPSOFFSET 0x8825
/*
EXIF_MARKER_LIST_END
};
+ExifMarker ExifKnownGPSInfoMarkersList[] = {
+ /* The following do not work at the moment as the tag value 0x0000 has a
+ * special meaning. */
+ /* { 0x0000, EXIF_FORMAT_BYTE, -1, "Exif.GPSInfo.GPSVersionID", NULL, NULL }, */
+ { 0x0001, EXIF_FORMAT_STRING, 2, "Exif.GPSInfo.GPSLatitudeRef", NULL, NULL },
+ { 0x0002, EXIF_FORMAT_RATIONAL_UNSIGNED, 3, "Exif.GPSInfo.GPSLatitude", NULL, NULL },
+ { 0x0003, EXIF_FORMAT_STRING, 2, "Exif.GPSInfo.GPSLongitudeRef", NULL, NULL },
+ { 0x0004, EXIF_FORMAT_RATIONAL_UNSIGNED, 3, "Exif.GPSInfo.GPSLongitude", NULL, NULL },
+ { 0x0005, EXIF_FORMAT_BYTE_UNSIGNED, 1, "Exif.GPSInfo.GPSAltitudeRef", NULL, NULL },
+ { 0x0006, EXIF_FORMAT_RATIONAL_UNSIGNED, 1, "Exif.GPSInfo.GPSAltitude", NULL, NULL },
+ { 0x0007, EXIF_FORMAT_RATIONAL_UNSIGNED, 3, "Exif.GPSInfo.GPSTimeStamp", NULL, NULL },
+ { 0x0008, EXIF_FORMAT_STRING, -1, "Exif.GPSInfo.GPSSatellites", NULL, NULL },
+ { 0x0009, EXIF_FORMAT_STRING, -1, "Exif.GPSInfo.GPSStatus", NULL, NULL },
+ { 0x000a, EXIF_FORMAT_STRING, -1, "Exif.GPSInfo.GPSMeasureMode", NULL, NULL },
+ { 0x000b, EXIF_FORMAT_RATIONAL_UNSIGNED, -1, "Exif.GPSInfo.GPSDOP", NULL, NULL },
+ { 0x000c, EXIF_FORMAT_STRING, -1, "Exif.GPSInfo.GPSSpeedRef", NULL, NULL },
+ { 0x000d, EXIF_FORMAT_RATIONAL_UNSIGNED, -1, "Exif.GPSInfo.GPSSpeed", NULL, NULL },
+ { 0x000e, EXIF_FORMAT_STRING, -1, "Exif.GPSInfo.GPSTrackRef", NULL, NULL },
+ { 0x000f, EXIF_FORMAT_RATIONAL_UNSIGNED, -1, "Exif.GPSInfo.GPSTrack", NULL, NULL },
+ { 0x0010, EXIF_FORMAT_STRING, -1, "Exif.GPSInfo.GPSImgDirectionRef", NULL, NULL },
+ { 0x0011, EXIF_FORMAT_RATIONAL_UNSIGNED, -1, "Exif.GPSInfo.GPSImgDirection", NULL, NULL },
+ { 0x0012, EXIF_FORMAT_STRING, -1, "Exif.GPSInfo.GPSMapDatum", NULL, NULL },
+ { 0x0013, EXIF_FORMAT_STRING, -1, "Exif.GPSInfo.GPSDestLatitudeRef", NULL, NULL },
+ { 0x0014, EXIF_FORMAT_RATIONAL_UNSIGNED, -1, "Exif.GPSInfo.GPSDestLatitude", NULL, NULL },
+ { 0x0015, EXIF_FORMAT_STRING, -1, "Exif.GPSInfo.GPSDestLongitudeRef", NULL, NULL },
+ { 0x0016, EXIF_FORMAT_RATIONAL_UNSIGNED, -1, "Exif.GPSInfo.GPSDestLongitude", NULL, NULL },
+ { 0x0017, EXIF_FORMAT_STRING, -1, "Exif.GPSInfo.GPSDestBearingRef", NULL, NULL },
+ { 0x0018, EXIF_FORMAT_RATIONAL_UNSIGNED, -1, "Exif.GPSInfo.GPSDestBearing", NULL, NULL },
+ { 0x0019, EXIF_FORMAT_STRING, -1, "Exif.GPSInfo.GPSDestDistanceRef", NULL, NULL },
+ { 0x001a, EXIF_FORMAT_RATIONAL_UNSIGNED, -1, "Exif.GPSInfo.GPSDestDistance", NULL, NULL },
+ { 0x001b, EXIF_FORMAT_UNDEFINED, -1, "Exif.GPSInfo.GPSProcessingMethod", NULL, NULL },
+ { 0x001c, EXIF_FORMAT_UNDEFINED, -1, "Exif.GPSInfo.GPSAreaInformation", NULL, NULL },
+ { 0x001d, EXIF_FORMAT_RATIONAL_UNSIGNED, 3, "Exif.GPSInfo.GPSDateStamp", NULL, NULL },
+ { 0x001e, EXIF_FORMAT_SHORT, -1, "Exif.GPSInfo.GPSDifferential", NULL, NULL },
+
+ EXIF_MARKER_LIST_END
+};
+
ExifMarker ExifUnknownMarkersList[] = {
{ 0x0000, EXIF_FORMAT_UNKNOWN, 0, "unknown", NULL, NULL },
{ 0x0000, EXIF_FORMAT_BYTE_UNSIGNED, -1, "unknown", NULL, NULL },
item->tag = tag;
item->marker = marker;
item->elements = elements;
- item->data = NULL;
- item->data_len = 0;
switch (format)
{
return g_strdup(_(item->marker->description));
}
-const gchar *exif_item_get_format_name(ExifItem *item, gint brief)
+const gchar *exif_item_get_format_name(ExifItem *item, gboolean brief)
{
if (!item || !item->marker) return NULL;
return (brief) ? ExifFormatList[item->format].short_name : ExifFormatList[item->format].description;
if (data_length > 4)
{
data_offset = data_val;
- if (size < data_offset + data_length)
+ if (size < data_offset || size < data_offset + data_length)
{
log_printf("warning: exif tag %s data will overrun end of file, ignored.\n", marker->key);
return -1;
case TAG_EXIFOFFSET:
exif_parse_IFD_table(exif, tiff, data_val, size, bo, level + 1, list);
break;
+ case TAG_GPSOFFSET:
+ exif_parse_IFD_table(exif, tiff, data_val, size, bo, level + 1, ExifKnownGPSInfoMarkersList);
+ break;
case TAG_EXIFMAKERNOTE:
format_exif_makernote_parse(exif, tiff, data_val, size, bo);
break;
return -2;
}
- if (exif_jpeg_segment_find(data, size, JPEG_MARKER_APP1,
+ if (jpeg_segment_find(data, size, JPEG_MARKER_APP1,
"Exif\x00\x00", 6,
&seg_offset, &seg_length))
{
}
+gchar* exif_get_image_comment(FileData* fd)
+{
+ log_printf("%s", _("Can't get image comment: not compiled with Exiv2.\n"));
+ return g_strdup("");
+}
+
+void exif_set_image_comment(FileData* fd, const gchar* comment)
+{
+ log_printf("%s", _("Can't set image comment: not compiled with Exiv2.\n"));
+}
+
+
/*
*-------------------------------------------------------------------
* misc
return 0;
}
+ExifData *exif_get_original(ExifData *processed)
+{
+ return processed;
+}
+
void exif_free(ExifData *exif)
{
GList *work;
g_free(exif);
}
-ExifData *exif_read(gchar *path, gchar *sidecar_path)
+ExifData *exif_read(gchar *path, gchar *sidecar_path, GHashTable *modified_xmp)
{
ExifData *exif;
gpointer f;
g_free(pathl);
exif = g_new0(ExifData, 1);
- exif->items = NULL;
- exif->current = NULL;
exif->path = g_strdup(path);
res = exif_jpeg_parse(exif, (guchar *)f, size, ExifKnownMarkersList);
if (exif) exif->items = g_list_reverse(exif->items);
-#if 0
- exif_write_data_list(exif, stdout, TRUE);
- exif_write_data_list(exif, stdout, FALSE);
-#endif
-
return exif;
}
#define EXIF_DATA_AS_TEXT_MAX_COUNT 16
-gchar *exif_item_get_string(ExifItem *item, gint idx)
-{
- return exif_item_get_data_as_text(item);
-}
-
-gchar *exif_item_get_data_as_text(ExifItem *item)
+static gchar *exif_item_get_data_as_text_full(ExifItem *item, MetadataFormat format)
{
const ExifMarker *marker;
gpointer data;
case EXIF_FORMAT_BYTE_UNSIGNED:
case EXIF_FORMAT_BYTE:
case EXIF_FORMAT_UNDEFINED:
- if (ne == 1 && marker->list)
+ if (ne == 1 && marker->list && format == METADATA_FORMATTED)
{
gchar *result;
guchar val;
}
break;
case EXIF_FORMAT_STRING:
- string = g_string_append(string, (gchar *)(item->data));
+ if (item->data) string = g_string_append(string, (gchar *)(item->data));
break;
case EXIF_FORMAT_SHORT_UNSIGNED:
- if (ne == 1 && marker->list)
+ if (ne == 1 && marker->list && format == METADATA_FORMATTED)
{
gchar *result;
return text;
}
+gchar *exif_item_get_string(ExifItem *item, gint idx)
+{
+ return exif_item_get_data_as_text_full(item, METADATA_PLAIN);
+}
+
+gchar *exif_item_get_data_as_text(ExifItem *item)
+{
+ return exif_item_get_data_as_text_full(item, METADATA_FORMATTED);
+}
+
gint exif_item_get_integer(ExifItem *item, gint *value)
{
if (!item) return FALSE;
+ if (!item->elements) return FALSE;
switch (item->format)
{
}
-ExifRational *exif_item_get_rational(ExifItem *item, gint *sign)
+ExifRational *exif_item_get_rational(ExifItem *item, gint *sign, guint n)
{
if (!item) return NULL;
+ if (n >= item->elements) return NULL;
if (item->format == EXIF_FORMAT_RATIONAL ||
item->format == EXIF_FORMAT_RATIONAL_UNSIGNED)
{
if (sign) *sign = (item->format == EXIF_FORMAT_RATIONAL);
- return &((ExifRational *)(item->data))[0];
+ return &((ExifRational *)(item->data))[n];
}
return NULL;
}
-const gchar *exif_get_tag_description_by_key(const gchar *key)
+gchar *exif_get_tag_description_by_key(const gchar *key)
{
gint i;
i = 0;
while (ExifKnownMarkersList[i].tag > 0)
{
- if (strcmp(key, ExifKnownMarkersList[i].key) == 0) return _(ExifKnownMarkersList[i].description);
+ if (strcmp(key, ExifKnownMarkersList[i].key) == 0) return g_strdup(_(ExifKnownMarkersList[i].description));
i++;
}
+ i = 0;
+ while (ExifKnownGPSInfoMarkersList[i].tag > 0)
+ {
+ if (strcmp(key, ExifKnownGPSInfoMarkersList[i].key) == 0) return g_strdup(_(ExifKnownGPSInfoMarkersList[i].description));
+ i++;
+ }
+
return NULL;
}
if (text)
{
gchar *tag = exif_item_get_tag_name(item);
- fprintf(f, "%4x %9s %30s %s\n", item->tag, ExifFormatList[item->format].short_name,
+ g_fprintf(f, "%4x %9s %30s %s\n", item->tag, ExifFormatList[item->format].short_name,
tag, text);
g_free(tag);
}
{
if (!f || !exif) return;
- fprintf(f, " tag format key value\n");
- fprintf(f, "----------------------------------------------------\n");
+ g_fprintf(f, " tag format key value\n");
+ g_fprintf(f, "----------------------------------------------------\n");
if (human_readable_list)
{
text = exif_get_formatted_by_key(exif, ExifFormattedList[i].key, NULL);
if (text)
{
- fprintf(f, " %9s %30s %s\n", "string", ExifFormattedList[i].key, text);
+ g_fprintf(f, " %9s %30s %s\n", "string", ExifFormattedList[i].key, text);
}
i++;
}
exif_write_item(f, item);
}
}
- fprintf(f, "----------------------------------------------------\n");
+ g_fprintf(f, "----------------------------------------------------\n");
}
-gint exif_write(ExifData *exif)
+gboolean exif_write(ExifData *exif)
{
- log_printf("Not compiled with EXIF write support");
- return 0;
+ log_printf("Not compiled with EXIF write support\n");
+ return FALSE;
}
-ExifItem *exif_add_item(ExifData *exif, const gchar *key)
+gboolean exif_write_sidecar(ExifData *exif, gchar *path)
{
- return NULL;
+ log_printf("Not compiled with EXIF write support\n");
+ return FALSE;
}
-gint exif_item_delete(ExifData *exif, ExifItem *item)
+
+gint exif_update_metadata(ExifData *exif, const gchar *key, const GList *values)
{
return 0;
}
-gint exif_item_set_string(ExifItem *item, const gchar *str)
+GList *exif_get_metadata(ExifData *exif, const gchar *key, MetadataFormat format)
{
- return 0;
-}
+ gchar *str;
+ ExifItem *item;
+
+ if (!key) return NULL;
+ /* convert xmp key to exif key */
+ if (strcmp(key, "Xmp.tiff.Orientation") == 0) key = "Exif.Image.Orientation";
+
+ if (format == METADATA_FORMATTED)
+ {
+ gchar *text;
+ gint key_valid;
+ text = exif_get_formatted_by_key(exif, key, &key_valid);
+ if (key_valid) return g_list_append(NULL, text);
+ }
+
+ item = exif_get_item(exif, key);
+ if (!item) return NULL;
+
+ str = exif_item_get_data_as_text_full(item, format);
+
+ if (!str) return NULL;
+
+ return g_list_append(NULL, str);
+}
typedef struct _UnmapData UnmapData;
struct _UnmapData
static GList *exif_unmap_list = 0;
-guchar *exif_get_preview(ExifData *exif, guint *data_len)
+guchar *exif_get_preview(ExifData *exif, guint *data_len, gint requested_width, gint requested_height)
{
- int success;
guint offset;
const gchar* path;
struct stat st;
guchar *map_data;
size_t map_len;
int fd;
-
+
if (!exif) return NULL;
path = exif->path;
fd = open(path, O_RDONLY);
-
-
+
+
if (fd == -1)
{
return 0;
{
UnmapData *ud;
- DEBUG_1("%s: offset %lu", path, offset);
+ DEBUG_1("%s: offset %u", path, offset);
*data_len = map_len - offset;
ud = g_new(UnmapData, 1);
ud->ptr = map_data + offset;
ud->map_data = map_data;
ud->map_len = map_len;
-
+
exif_unmap_list = g_list_prepend(exif_unmap_list, ud);
return ud->ptr;
}
void exif_free_preview(guchar *buf)
{
GList *work = exif_unmap_list;
-
+
while (work)
{
UnmapData *ud = (UnmapData *)work->data;
g_assert_not_reached();
}
+void exif_init(void)
+{
+}
#endif
/* not HAVE_EXIV2 */
+/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */