diff options
author | Lucas Rocha <lucasr@gnome.org> | 2007-07-18 23:24:59 +0000 |
---|---|---|
committer | Lucas Almeida Rocha <lucasr@src.gnome.org> | 2007-07-18 23:24:59 +0000 |
commit | e754da90e337345c5a17377bf8a646bcb05dca61 (patch) | |
tree | 11fdf158b0842722d501ed008d854b3a81cd842d /src/eog-metadata-reader.c | |
parent | a653d4daa7e596f707fdfdf0dc551d4d1fdf04a2 (diff) | |
download | eog-e754da90e337345c5a17377bf8a646bcb05dca61.tar.gz |
Optional XMP metadata support. Fixes bug #451101 (Hubert Figuiere).
2007-07-19 Lucas Rocha <lucasr@gnome.org>
* src/eog-exif-details.[c,h], src/main.c,
src/eog-properties-dialog.[c.h], src/eog-metadata-reader.[c,h],
src/eog-image.[c,h], src/eog-image-private.h, src/Makefile.am,
data/eog.glade, configure.ac: Optional XMP metadata support.
Fixes bug #451101 (Hubert Figuiere).
svn path=/trunk/; revision=3890
Diffstat (limited to 'src/eog-metadata-reader.c')
-rw-r--r-- | src/eog-metadata-reader.c | 183 |
1 files changed, 150 insertions, 33 deletions
diff --git a/src/eog-metadata-reader.c b/src/eog-metadata-reader.c index 29cdd6b1..e407fdb9 100644 --- a/src/eog-metadata-reader.c +++ b/src/eog-metadata-reader.c @@ -13,12 +13,21 @@ typedef enum { EMR_READ_SIZE_LOW_BYTE, EMR_READ_MARKER, EMR_SKIP_BYTES, + EMR_READ_APP1, EMR_READ_EXIF, + EMR_READ_XMP, EMR_READ_ICC, EMR_READ_IPTC, EMR_FINISHED } EogMetadataReaderState; +typedef enum { + EJA_EXIF = 0, + EJA_XMP, + EJA_OTHER +} EogJpegApp1Type; + + #define EOG_JPEG_MARKER_START 0xFF #define EOG_JPEG_MARKER_SOI 0xD8 #define EOG_JPEG_MARKER_APP1 0xE1 @@ -27,6 +36,11 @@ typedef enum { #define NO_DEBUG +#define IS_FINISHED(priv) (priv->exif_chunk != NULL && \ + priv->icc_chunk != NULL && \ + priv->iptc_chunk != NULL && \ + priv->xmp_chunk != NULL) + struct _EogMetadataReaderPrivate { EogMetadataReaderState state; @@ -39,6 +53,9 @@ struct _EogMetadataReaderPrivate { gpointer icc_chunk; guint icc_len; + + gpointer xmp_chunk; + guint xmp_len; /* management fields */ int size; @@ -74,6 +91,11 @@ eog_metadata_reader_dispose (GObject *object) emr->priv->iptc_chunk = NULL; } + if (emr->priv->xmp_chunk != NULL) { + g_free (emr->priv->xmp_chunk); + emr->priv->xmp_chunk = NULL; + } + if (emr->priv->icc_chunk != NULL) { g_free (emr->priv->icc_chunk); emr->priv->icc_chunk = NULL; @@ -126,10 +148,33 @@ eog_metadata_reader_finished (EogMetadataReader *emr) return (emr->priv->state == EMR_FINISHED); } + +static EogJpegApp1Type +eog_metadata_identify_app1 (gchar *buf, guint len) +{ + if (len < 5) { + return EJA_OTHER; + } + + if (len < 29) { + return (strncmp ("Exif", buf, 5) == 0 ? EJA_EXIF : EJA_OTHER); + } + + if (strncmp ("Exif", buf, 5) == 0) { + return EJA_EXIF; + } else if (strncmp ("http://ns.adobe.com/xap/1.0/", buf, 29) == 0) { + return EJA_XMP; + } + + return EJA_OTHER; +} + + void eog_metadata_reader_consume (EogMetadataReader *emr, guchar *buf, guint len) { EogMetadataReaderPrivate *priv; + EogJpegApp1Type app1_type; int i; g_return_if_fail (EOG_IS_METADATA_READER (emr)); @@ -178,23 +223,19 @@ eog_metadata_reader_consume (EogMetadataReader *emr, guchar *buf, guint len) if (priv->size == 0) { priv->state = EMR_READ; - } - else if (priv->last_marker == EOG_JPEG_MARKER_APP1 && - priv->exif_chunk == NULL) + } else if (priv->last_marker == EOG_JPEG_MARKER_APP1 && + ((priv->exif_chunk == NULL) || (priv->xmp_chunk == NULL))) { - priv->state = EMR_READ_EXIF; - } - else if (priv->last_marker == EOG_JPEG_MARKER_APP2 && + priv->state = EMR_READ_APP1; + } else if (priv->last_marker == EOG_JPEG_MARKER_APP2 && priv->icc_chunk == NULL) { priv->state = EMR_READ_ICC; - } - else if (priv->last_marker == EOG_JPEG_MARKER_APP14 && + } else if (priv->last_marker == EOG_JPEG_MARKER_APP14 && priv->iptc_chunk == NULL) { priv->state = EMR_READ_IPTC; - } - else { + } else { priv->state = EMR_SKIP_BYTES; } @@ -217,22 +258,66 @@ eog_metadata_reader_consume (EogMetadataReader *emr, guchar *buf, guint len) } break; - case EMR_READ_EXIF: - eog_debug_message (DEBUG_IMAGE_DATA, "Read EXIF data, Length: %i", priv->size); + case EMR_READ_APP1: + eog_debug_message (DEBUG_IMAGE_DATA, "Read APP1 data, Length: %i", priv->size); - if (priv->exif_chunk == NULL) { - priv->exif_chunk = g_new0 (guchar, priv->size); - priv->exif_len = priv->size; - priv->bytes_read = 0; + app1_type = eog_metadata_identify_app1 ((gchar*) &buf[i], priv->size); + + if (app1_type != EJA_OTHER) { + EogMetadataReaderState next_state; + guchar *chunk = NULL; + int offset = 0; + + switch (app1_type) { + case EJA_EXIF: + if (priv->exif_chunk == NULL) { + priv->exif_chunk = g_new0 (guchar, priv->size); + priv->exif_len = priv->size; + priv->bytes_read = 0; + chunk = priv->exif_chunk; + next_state = EMR_READ_EXIF; + } + break; + case EJA_XMP: + if (priv->xmp_chunk == NULL) { + offset = 29 + 54; /* skip the ID + packet */ + + if (priv->size > offset) { /* ensure that we have enough bytes */ + priv->xmp_chunk = g_new0 (guchar, priv->size); + priv->xmp_len = priv->size - offset; + priv->bytes_read = 0; + chunk = priv->xmp_chunk; + next_state = EMR_READ_XMP; + } + } + default: + break; + } + + if (chunk) { + if (i + priv->size < len) { + /* read data in one block */ + memcpy ((guchar*) (chunk) + priv->bytes_read, &buf[i + offset], priv->size); + priv->state = EMR_READ; + i = i + priv->size - 1; /* the for-loop consumes the other byte */ + } else { + int chunk_len = len - i; + memcpy ((guchar*) (priv->exif_chunk) + priv->bytes_read, &buf[i], chunk_len); + priv->bytes_read += chunk_len; /* bytes already read */ + priv->size = (i + priv->size) - len; /* remaining data to read */ + i = len - 1; + priv->state = next_state; + } + } } - if (i + priv->size < len) { - /* read data in one block */ - memcpy ((guchar*) (priv->exif_chunk) + priv->bytes_read, &buf[i], priv->size); - priv->state = EMR_READ; - i = i + priv->size - 1; /* the for-loop consumes the other byte */ - } - else { + if (IS_FINISHED(priv)) + priv->state = EMR_FINISHED; + break; + + case EMR_READ_EXIF: + eog_debug_message (DEBUG_IMAGE_DATA, "Read continuation of EXIF data, length: %i", priv->size); + { int chunk_len = len - i; memcpy ((guchar*) (priv->exif_chunk) + priv->bytes_read, &buf[i], chunk_len); priv->bytes_read += chunk_len; /* bytes already read */ @@ -240,11 +325,24 @@ eog_metadata_reader_consume (EogMetadataReader *emr, guchar *buf, guint len) i = len - 1; priv->state = EMR_READ_EXIF; } + if (IS_FINISHED(priv)) + priv->state = EMR_FINISHED; + break; - if (priv->exif_chunk != NULL && priv->icc_chunk != NULL && priv->iptc_chunk != NULL) + case EMR_READ_XMP: + eog_debug_message (DEBUG_IMAGE_DATA, "Read continuation of XMP data, length: %i", priv->size); + { + int chunk_len = len - i; + memcpy ((guchar*) (priv->xmp_chunk) + priv->bytes_read, &buf[i], chunk_len); + priv->bytes_read += chunk_len; /* bytes already read */ + priv->size = (i + priv->size) - len; /* remaining data to read */ + i = len - 1; + priv->state = EMR_READ_XMP; + } + if (IS_FINISHED (priv)) priv->state = EMR_FINISHED; break; - + case EMR_READ_ICC: eog_debug_message (DEBUG_IMAGE_DATA, "Read ICC data, Length: %i\n", priv->size); @@ -259,8 +357,7 @@ eog_metadata_reader_consume (EogMetadataReader *emr, guchar *buf, guint len) memcpy ((guchar*) (priv->icc_chunk) + priv->bytes_read, &buf[i], priv->size); priv->state = EMR_READ; i = i + priv->size - 1; /* the for-loop consumes the other byte */ - } - else { + } else { int chunk_len = len - i; memcpy ((guchar*) (priv->icc_chunk) + priv->bytes_read, &buf[i], chunk_len); priv->bytes_read += chunk_len; /* bytes already read */ @@ -269,7 +366,7 @@ eog_metadata_reader_consume (EogMetadataReader *emr, guchar *buf, guint len) priv->state = EMR_READ_ICC; } - if (priv->exif_chunk != NULL && priv->icc_chunk != NULL && priv->iptc_chunk != NULL) + if (IS_FINISHED(priv)) priv->state = EMR_FINISHED; break; @@ -284,8 +381,7 @@ eog_metadata_reader_consume (EogMetadataReader *emr, guchar *buf, guint len) /* read data in one block */ memcpy ((guchar*) (priv->iptc_chunk) + priv->bytes_read, &buf[i], priv->size); priv->state = EMR_READ; - } - else { + } else { int chunk_len = len - i; memcpy ((guchar*) (priv->iptc_chunk) + priv->bytes_read, &buf[i], chunk_len); priv->bytes_read += chunk_len; /* bytes already read */ @@ -294,7 +390,7 @@ eog_metadata_reader_consume (EogMetadataReader *emr, guchar *buf, guint len) priv->state = EMR_READ_IPTC; } - if (priv->exif_chunk != NULL && priv->icc_chunk != NULL && priv->iptc_chunk != NULL) + if (IS_FINISHED(priv)) priv->state = EMR_FINISHED; break; @@ -322,7 +418,7 @@ eog_metadata_reader_get_exif_chunk (EogMetadataReader *emr, guchar **data, guint priv->exif_len = 0; } -#if HAVE_EXIF +#ifdef HAVE_EXIF ExifData* eog_metadata_reader_get_exif_data (EogMetadataReader *emr) { @@ -340,8 +436,28 @@ eog_metadata_reader_get_exif_data (EogMetadataReader *emr) } #endif + +#ifdef HAVE_EXEMPI +XmpPtr +eog_metadata_reader_get_xmp_data (EogMetadataReader *emr ) +{ + EogMetadataReaderPrivate *priv; + XmpPtr xmp = NULL; + + g_return_val_if_fail (EOG_IS_METADATA_READER (emr), NULL); + + priv = emr->priv; + + if (priv->xmp_chunk != NULL) { + xmp = xmp_new (priv->xmp_chunk, priv->xmp_len); + } + + return xmp; +} +#endif + /* - * TODO: very broken, assumes the profile fits in a single chunk. Change to + * FIXME: very broken, assumes the profile fits in a single chunk. Change to * parse the sections and construct a single memory chunk, or maybe even parse * the profile. */ @@ -351,6 +467,7 @@ eog_metadata_reader_get_icc_chunk (EogMetadataReader *emr, guchar **data, guint EogMetadataReaderPrivate *priv; g_return_if_fail (EOG_IS_METADATA_READER (emr)); + priv = emr->priv; if (priv->icc_chunk) { |