static void vips_exif_exif_entry( ExifEntry *entry, VipsExif *ve ) { const char *tag_name; char vips_name_txt[256]; VipsBuf vips_name = VIPS_BUF_STATIC( vips_name_txt ); if( !(tag_name = exif_tag_get_name( entry->tag )) ) return; vips_buf_appendf( &vips_name, "exif-ifd%d-%s", exif_entry_get_ifd( entry ), tag_name ); /* Does this field exist on the image? If not, schedule it for * removal. */ if( !vips_image_get_typeof( ve->image, vips_buf_all( &vips_name ) ) ) ve->to_remove = g_slist_prepend( ve->to_remove, entry ); /* Orientation is really set from the vips * VIPS_META_ORIENTATION tag. If that's been deleted, we must delete * any matching EXIF tags too. */ if( strcmp( tag_name, "Orientation" ) == 0 && vips_image_get_typeof( ve->image, VIPS_META_ORIENTATION ) ) ve->to_remove = g_slist_prepend( ve->to_remove, entry ); }
static void vips_exif_set_int( ExifData *ed, ExifEntry *entry, unsigned long component, void *data ) { int value = *((int *) data); ExifByteOrder bo; size_t sizeof_component; size_t offset = component; if( entry->components <= component ) { VIPS_DEBUG_MSG( "vips_exif_set_int: too few components\n" ); return; } /* Wait until after the component check to make sure we cant get /0. */ bo = exif_data_get_byte_order( ed ); sizeof_component = entry->size / entry->components; offset = component * sizeof_component; VIPS_DEBUG_MSG( "vips_exif_set_int: %s = %d\n", exif_tag_get_name( entry->tag ), value ); if( entry->format == EXIF_FORMAT_SHORT ) exif_set_short( entry->data + offset, bo, value ); else if( entry->format == EXIF_FORMAT_SSHORT ) exif_set_sshort( entry->data + offset, bo, value ); else if( entry->format == EXIF_FORMAT_LONG ) exif_set_long( entry->data + offset, bo, value ); else if( entry->format == EXIF_FORMAT_SLONG ) exif_set_slong( entry->data + offset, bo, value ); }
/* Does both signed and unsigned rationals from a double*. * * Don't change the exit entry if the value currently there is a good * approximation of the double we are trying to set. */ static void vips_exif_set_double( ExifData *ed, ExifEntry *entry, unsigned long component, void *data ) { double value = *((double *) data); ExifByteOrder bo; size_t sizeof_component; size_t offset; double old_value; if( entry->components <= component ) { VIPS_DEBUG_MSG( "vips_exif_set_double: " "too few components\n" ); return; } /* Wait until after the component check to make sure we cant get /0. */ bo = exif_data_get_byte_order( ed ); sizeof_component = entry->size / entry->components; offset = component * sizeof_component; VIPS_DEBUG_MSG( "vips_exif_set_double: %s = %g\n", exif_tag_get_name( entry->tag ), value ); if( entry->format == EXIF_FORMAT_RATIONAL ) { ExifRational rv; rv = exif_get_rational( entry->data + offset, bo ); old_value = (double) rv.numerator / rv.denominator; if( VIPS_FABS( old_value - value ) > 0.0001 ) { vips_exif_double_to_rational( value, &rv ); VIPS_DEBUG_MSG( "vips_exif_set_double: %u / %u\n", rv.numerator, rv.denominator ); exif_set_rational( entry->data + offset, bo, rv ); } } else if( entry->format == EXIF_FORMAT_SRATIONAL ) { ExifSRational srv; srv = exif_get_srational( entry->data + offset, bo ); old_value = (double) srv.numerator / srv.denominator; if( VIPS_FABS( old_value - value ) > 0.0001 ) { vips_exif_double_to_srational( value, &srv ); VIPS_DEBUG_MSG( "vips_exif_set_double: %d / %d\n", srv.numerator, srv.denominator ); exif_set_srational( entry->data + offset, bo, srv ); } } }
/* Does both signed and unsigned rationals from a char*. */ static void vips_exif_set_rational( ExifData *ed, ExifEntry *entry, unsigned long component, void *data ) { char *value = (char *) data; ExifByteOrder bo; size_t sizeof_component; size_t offset; if( entry->components <= component ) { VIPS_DEBUG_MSG( "vips_exif_set_rational: " "too few components\n" ); return; } /* Wait until after the component check to make sure we cant get /0. */ bo = exif_data_get_byte_order( ed ); sizeof_component = entry->size / entry->components; offset = component * sizeof_component; VIPS_DEBUG_MSG( "vips_exif_set_rational: %s = \"%s\"\n", exif_tag_get_name( entry->tag ), value ); if( entry->format == EXIF_FORMAT_RATIONAL ) { ExifRational rv; vips_exif_parse_rational( value, &rv ); VIPS_DEBUG_MSG( "vips_exif_set_rational: %u / %u\n", rv.numerator, rv.denominator ); exif_set_rational( entry->data + offset, bo, rv ); } else if( entry->format == EXIF_FORMAT_SRATIONAL ) { ExifSRational srv; vips_exif_parse_srational( value, &srv ); VIPS_DEBUG_MSG( "vips_exif_set_rational: %d / %d\n", srv.numerator, srv.denominator ); exif_set_srational( entry->data + offset, bo, srv ); } }
static void * vips_exif_exif_remove( ExifEntry *entry, VipsExif *ve ) { #ifdef DEBUG { const char *tag_name; char vips_name_txt[256]; VipsBuf vips_name = VIPS_BUF_STATIC( vips_name_txt ); tag_name = exif_tag_get_name( entry->tag ); vips_buf_appendf( &vips_name, "exif-ifd%d-%s", exif_entry_get_ifd( entry ), tag_name ); printf( "vips_exif_exif_remove: %s\n", vips_buf_all( &vips_name ) ); } #endif /*DEBUG*/ exif_content_remove_entry( ve->content, entry ); return( NULL ); }
void exif_content_add_entry (ExifContent *c, ExifEntry *entry) { ExifEntry **entries; if (!c || !c->priv || !entry || entry->parent) return; if (exif_content_get_entry (c, entry->tag)) { exif_log (c->priv->log, EXIF_LOG_CODE_DEBUG, "ExifContent", "An attempt has been made to add " "the tag '%s' twice to an IFD. This is against " "specification.", exif_tag_get_name (entry->tag)); return; } entries = exif_mem_realloc (c->priv->mem, c->entries, sizeof (ExifEntry*) * (c->count + 1)); if (!entries) return; entry->parent = c; entries[c->count++] = entry; c->entries = entries; exif_entry_ref (entry); }
static char * exifdata_get_tag_name_utf8 (ExifTag tag) { return exif_string_to_utf8 (exif_tag_get_name (tag)); }
static void metadataparse_exif_content_foreach_entry_func (ExifEntry * entry, void *user_data) { MEUserData *meudata = (MEUserData *) user_data; GType type = G_TYPE_NONE; ExifByteOrder byte_order; const gchar *tag; /* We need the byte order */ if (!entry || !entry->parent || !entry->parent->parent) return; tag = metadataparse_exif_get_tag_from_exif (entry->tag, &type); byte_order = exif_data_get_byte_order (entry->parent->parent); if (metadataparse_handle_unit_tags (entry, meudata, byte_order)) goto done; if (!tag) goto done; if (type == GST_TYPE_FRACTION) { gint numerator = 0; gint denominator = 1; switch (entry->format) { case EXIF_FORMAT_SRATIONAL: { ExifSRational v_srat; v_srat = exif_get_srational (entry->data, byte_order); if (v_srat.denominator) { numerator = (gint) v_srat.numerator; denominator = (gint) v_srat.denominator; } } break; case EXIF_FORMAT_RATIONAL: { ExifRational v_rat; v_rat = exif_get_rational (entry->data, byte_order); if (v_rat.denominator) { numerator = (gint) v_rat.numerator; denominator = (gint) v_rat.denominator; } if (meudata->resolution_unit == 3) { /* converts from cm to inches */ if (entry->tag == EXIF_TAG_X_RESOLUTION || entry->tag == EXIF_TAG_Y_RESOLUTION) { numerator *= 2; denominator *= 5; } } } break; default: GST_ERROR ("Unexpected Tag Type"); goto done; break; } gst_tag_list_add (meudata->taglist, meudata->mode, tag, numerator, denominator, NULL); } else if (type == GST_TYPE_BUFFER) { GstBuffer *buf = gst_buffer_new_and_alloc (entry->components); memcpy (GST_BUFFER_DATA (buf), entry->data, entry->components); gst_tag_list_add (meudata->taglist, meudata->mode, tag, buf, NULL); gst_buffer_unref (buf); } else { switch (type) { case G_TYPE_STRING: { char buf[2048]; const gchar *str = exif_entry_get_value (entry, buf, sizeof (buf)); GString *value = NULL; if (entry->tag == EXIF_TAG_DATE_TIME_DIGITIZED || entry->tag == EXIF_TAG_DATE_TIME || entry->tag == EXIF_TAG_DATE_TIME_ORIGINAL) { value = g_string_new_len (str, 20); /* 20 is enough memory to hold "YYYY-MM-DDTHH:MM:SS" */ if (metadataparse_exif_convert_to_datetime (value)) { str = value->str; } else { GST_ERROR ("Unexpected date & time format for %s", tag); str = NULL; } } if (str) gst_tag_list_add (meudata->taglist, meudata->mode, tag, str, NULL); if (value) g_string_free (value, TRUE); } break; case G_TYPE_INT: /* fall through */ case G_TYPE_UINT: { gint value; switch (entry->format) { case EXIF_FORMAT_SHORT: value = exif_get_short (entry->data, byte_order); break; case EXIF_FORMAT_LONG: value = exif_get_long (entry->data, byte_order); break; default: GST_ERROR ("Unexpected Exif Tag Type (%s - %s)", tag, exif_format_get_name (entry->format)); goto done; break; } if (entry->tag == EXIF_TAG_CONTRAST || entry->tag == EXIF_TAG_SATURATION) { switch (value) { case 0: break; case 1: value = -67; /* -100-34 /2 */ break; case 2: value = 67; /* 100+34 /2 */ break; default: GST_ERROR ("Unexpected value"); break; } } gst_tag_list_add (meudata->taglist, meudata->mode, tag, value, NULL); } break; case G_TYPE_DOUBLE: { gdouble value = 0.0; if (entry->tag == EXIF_TAG_GPS_LATITUDE || entry->tag == EXIF_TAG_GPS_LONGITUDE) { ExifRational *rt = (ExifRational *) entry->data; /* DDD - degrees */ value = (gdouble) rt->numerator / (gdouble) rt->denominator; GST_DEBUG ("deg: %lu / %lu", (gulong) rt->numerator, (gulong) rt->denominator); rt++; /* MM - minutes and SS - seconds */ GST_DEBUG ("min: %lu / %lu", (gulong) rt->numerator, (gulong) rt->denominator); value += (gdouble) rt->numerator / ((gdouble) rt->denominator * 60.0); rt++; GST_DEBUG ("sec: %lu / %lu", (gulong) rt->numerator, (gulong) rt->denominator); value += (gdouble) rt->numerator / ((gdouble) rt->denominator * 3600.0); /* apply sign */ if (entry->tag == EXIF_TAG_GPS_LATITUDE) { if (((meudata->latitude_ref == 'S') && (value > 0.0)) || ((meudata->latitude_ref == 'N') && (value < 0.0))) { value = -value; } } else { if (((meudata->longitude_ref == 'W') && (value > 0.0)) || ((meudata->longitude_ref == 'E') && (value < 0.0))) { value = -value; } } GST_DEBUG ("long/lat : %lf", value); } if (entry->tag == EXIF_TAG_GPS_ALTITUDE) { ExifRational v_rat = exif_get_rational (entry->data, byte_order); value = (gdouble) v_rat.numerator / (gdouble) v_rat.denominator; if (((meudata->altitude_ref == 1) && (value > 0.0)) || ((meudata->altitude_ref == 0) && (value < 0.0))) { value = -value; } GST_DEBUG ("altitude = %lf", value); } gst_tag_list_add (meudata->taglist, meudata->mode, tag, value, NULL); } break; default: break; } } done: { #ifndef GST_DISABLE_GST_DEBUG char buf[2048]; GST_LOG ("\n Entry %p: %s (%s)\n" " Size, Comps: %d, %d\n" " Value: %s\n" " Title: %s\n" " Description: %s\n", entry, exif_tag_get_name (entry->tag), exif_format_get_name (entry->format), entry->size, (int) (entry->components), exif_entry_get_value (entry, buf, sizeof (buf)), exif_tag_get_title (entry->tag), exif_tag_get_description (entry->tag)); #endif } return; }
void FileSystemScanner::readEXIF(File* file) { static QString EXIFIFD_NAME_EXIF = exif_ifd_get_name(EXIF_IFD_EXIF); int maxDataLength = 255; char* charData = new char[maxDataLength]; ExifData* exifData = exif_data_new_from_file(file->fileInfo()->absFilePath().ascii()); // exif_data_dump(exifData); for (int i = 0; i < EXIF_IFD_COUNT; i++) { ExifContent* content = exifData->ifd[i]; QString group = QString(exif_ifd_get_name((ExifIfd) i)); if (group == EXIFIFD_NAME_EXIF) { for (unsigned int e = 0; e < content->count; e++) { ExifEntry* entry = content->entries[e]; ExifTag exifTagType = entry->tag; QString title = QString(exif_tag_get_title(entry->tag)); QString name = QString(exif_tag_get_name(entry->tag)); exif_entry_get_value(entry, charData, maxDataLength); QString data = QString(charData); QString description = QString(exif_tag_get_description(entry->tag)); // tracer->sdebug(__func__) << " - " << title << " / " << name << " = " << data << " (" << description << ")" << endl; // get the exif tagnode title and the tagnode representing this tag from the engine TagNode* exifTagNodeTitle = m_engine->exifTagNodeTitle(); TagNode* exifTagNodeEntry = exifTagNodeTitle->child(title); switch (exifTagType) { case EXIF_TAG_DATE_TIME_ORIGINAL: case EXIF_TAG_DATE_TIME_DIGITIZED: { QDateTime dateTime = readExifDateTime(data); TagNodeDateTime* exifTagNodeEntryDateTime = dynamic_cast<TagNodeDateTime*>(exifTagNodeEntry); if (exifTagNodeEntryDateTime == 0) { // tagnode for this EXIF entry does not exist --> create it exifTagNodeEntryDateTime = dynamic_cast<TagNodeDateTime*>(m_engine->createTag(exifTagNodeTitle, TagNode::TYPE_DATETIME, title, description, QString::null)); exifTagNodeEntryDateTime->setReadonly(true); } if (dateTime.isValid()) { // create new assoc new FileTagNodeAssocDateTime(file, exifTagNodeEntryDateTime, dateTime); } break; } default: { TagNodeString* exifTagNodeEntryString = dynamic_cast<TagNodeString*>(exifTagNodeEntry); if (exifTagNodeEntryString == 0) { // tagnode for this EXIF entry does not exist --> create it exifTagNodeEntryString = dynamic_cast<TagNodeString*>(m_engine->createTag(exifTagNodeTitle, TagNode::TYPE_STRING, title, description, QString::null)); exifTagNodeEntryString->setReadonly(true); } // create new assoc new FileTagNodeAssocString(file, exifTagNodeEntryString, data); } } } } } exif_data_free(exifData); delete charData; }
/*! Load data for an IFD. * * \param[in,out] data #ExifData * \param[in] ifd IFD to load * \param[in] d pointer to buffer containing raw IFD data * \param[in] ds size of raw data in buffer at \c d * \param[in] offset offset into buffer at \c d at which IFD starts * \param[in] recursion_depth number of times this function has been * recursively called without returning */ static void exif_data_load_data_content (ExifData *data, ExifIfd ifd, const unsigned char *d, unsigned int ds, unsigned int offset, unsigned int recursion_depth) { ExifLong o, thumbnail_offset = 0, thumbnail_length = 0; ExifShort n; ExifEntry *entry; unsigned int i; ExifTag tag; if (!data || !data->priv) return; /* check for valid ExifIfd enum range */ if ((((int)ifd) < 0) || ( ((int)ifd) >= EXIF_IFD_COUNT)) return; if (recursion_depth > 30) { exif_log (data->priv->log, EXIF_LOG_CODE_CORRUPT_DATA, "ExifData", "Deep recursion detected!"); return; } /* Read the number of entries */ if ((offset + 2 < offset) || (offset + 2 < 2) || (offset + 2 > ds)) { exif_log (data->priv->log, EXIF_LOG_CODE_CORRUPT_DATA, "ExifData", "Tag data past end of buffer (%u > %u)", offset+2, ds); return; } n = exif_get_short (d + offset, data->priv->order); exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData", "Loading %hu entries...", n); offset += 2; /* Check if we have enough data. */ if (offset + 12 * n > ds) { n = (ds - offset) / 12; exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData", "Short data; only loading %hu entries...", n); } for (i = 0; i < n; i++) { tag = exif_get_short (d + offset + 12 * i, data->priv->order); switch (tag) { case EXIF_TAG_EXIF_IFD_POINTER: case EXIF_TAG_GPS_INFO_IFD_POINTER: case EXIF_TAG_INTEROPERABILITY_IFD_POINTER: case EXIF_TAG_JPEG_INTERCHANGE_FORMAT_LENGTH: case EXIF_TAG_JPEG_INTERCHANGE_FORMAT: o = exif_get_long (d + offset + 12 * i + 8, data->priv->order); /* FIXME: IFD_POINTER tags aren't marked as being in a * specific IFD, so exif_tag_get_name_in_ifd won't work */ exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData", "Sub-IFD entry 0x%x ('%s') at %u.", tag, exif_tag_get_name(tag), o); switch (tag) { case EXIF_TAG_EXIF_IFD_POINTER: CHECK_REC (EXIF_IFD_EXIF); exif_data_load_data_content (data, EXIF_IFD_EXIF, d, ds, o, recursion_depth + 1); break; case EXIF_TAG_GPS_INFO_IFD_POINTER: CHECK_REC (EXIF_IFD_GPS); exif_data_load_data_content (data, EXIF_IFD_GPS, d, ds, o, recursion_depth + 1); break; case EXIF_TAG_INTEROPERABILITY_IFD_POINTER: CHECK_REC (EXIF_IFD_INTEROPERABILITY); exif_data_load_data_content (data, EXIF_IFD_INTEROPERABILITY, d, ds, o, recursion_depth + 1); break; case EXIF_TAG_JPEG_INTERCHANGE_FORMAT: thumbnail_offset = o; if (thumbnail_offset && thumbnail_length) exif_data_load_data_thumbnail (data, d, ds, thumbnail_offset, thumbnail_length); break; case EXIF_TAG_JPEG_INTERCHANGE_FORMAT_LENGTH: thumbnail_length = o; if (thumbnail_offset && thumbnail_length) exif_data_load_data_thumbnail (data, d, ds, thumbnail_offset, thumbnail_length); break; default: return; } break; default: /* * If we don't know the tag, don't fail. It could be that new * versions of the standard have defined additional tags. Note that * 0 is a valid tag in the GPS IFD. */ if (!exif_tag_get_name_in_ifd (tag, ifd)) { /* * Special case: Tag and format 0. That's against specification * (at least up to 2.2). But Photoshop writes it anyways. */ if (!memcmp (d + offset + 12 * i, "\0\0\0\0", 4)) { exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData", "Skipping empty entry at position %u in '%s'.", i, exif_ifd_get_name (ifd)); break; } exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData", "Unknown tag 0x%04x (entry %u in '%s'). Please report this tag " "to <*****@*****.**>.", tag, i, exif_ifd_get_name (ifd)); if (data->priv->options & EXIF_DATA_OPTION_IGNORE_UNKNOWN_TAGS) break; } entry = exif_entry_new_mem (data->priv->mem); if (exif_data_load_data_entry (data, entry, d, ds, offset + 12 * i)) exif_content_add_entry (data->ifd[ifd], entry); exif_entry_unref (entry); break; } } }
static int exif_data_load_data_entry (ExifData *data, ExifEntry *entry, const unsigned char *d, unsigned int size, unsigned int offset) { unsigned int s, doff; entry->tag = exif_get_short (d + offset + 0, data->priv->order); entry->format = exif_get_short (d + offset + 2, data->priv->order); entry->components = exif_get_long (d + offset + 4, data->priv->order); /* FIXME: should use exif_tag_get_name_in_ifd here but entry->parent * has not been set yet */ exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData", "Loading entry 0x%x ('%s')...", entry->tag, exif_tag_get_name (entry->tag)); /* {0,1,2,4,8} x { 0x00000000 .. 0xffffffff } * -> { 0x000000000 .. 0x7fffffff8 } */ s = exif_format_get_size(entry->format) * entry->components; if ((s < entry->components) || (s == 0)){ return 0; } /* * Size? If bigger than 4 bytes, the actual data is not * in the entry but somewhere else (offset). */ if (s > 4) doff = exif_get_long (d + offset + 8, data->priv->order); else doff = offset + 8; /* Sanity checks */ if ((doff + s < doff) || (doff + s < s) || (doff + s > size)) { exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData", "Tag data past end of buffer (%u > %u)", doff+s, size); return 0; } entry->data = exif_data_alloc (data, s); if (entry->data) { entry->size = s; memcpy (entry->data, d + doff, s); } else { /* FIXME: What do our callers do if (entry->data == NULL)? */ EXIF_LOG_NO_MEMORY(data->priv->log, "ExifData", s); } /* If this is the MakerNote, remember the offset */ if (entry->tag == EXIF_TAG_MAKER_NOTE) { if (!entry->data) { exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData", "MakerNote found with empty data"); } else if (entry->size > 6) { exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData", "MakerNote found (%02x %02x %02x %02x " "%02x %02x %02x...).", entry->data[0], entry->data[1], entry->data[2], entry->data[3], entry->data[4], entry->data[5], entry->data[6]); } data->priv->offset_mnote = doff; } return 1; }