/* parse EXIF metadata block out into a set of fields, and reassemble EXIF * block from original block, plus modified fields * * 7/11/16 * - from jpeg2vips * 14/10/17 * - only read orientation from ifd0 * 1/2/18 * - remove exif thumbnail if "jpeg-thumbnail-data" has been removed * 3/7/18 * - add support for writing string-valued fields * 9/7/18 [@Nan619] * - get tag name from tag plus ifd * 13/11/21 * - better handling of strings with embedded metacharacters */ /* This file is part of VIPS. VIPS is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser 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 */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG_VERBOSE #define DEBUG */ #ifdef HAVE_CONFIG_H #include #endif /*HAVE_CONFIG_H*/ #include #include #include #include #include #include #include #include #include #ifdef HAVE_EXIF #ifdef UNTAGGED_EXIF #include #include #include #include #else /*!UNTAGGED_EXIF*/ #include #include #include #include #endif /*UNTAGGED_EXIF*/ #include "pforeign.h" #ifdef DEBUG_VERBOSE /* Print exif for debugging ... hacked from exif-0.6.9/actions.c */ static void show_tags( ExifData *data ) { int i; unsigned int tag; const char *name; printf( "show EXIF tags:\n" ); for( i = 0; i < EXIF_IFD_COUNT; i++ ) printf( "%-7.7s", exif_ifd_get_name( i ) ); printf( "\n" ); for( tag = 0; tag < 0xffff; tag++ ) { name = exif_tag_get_title( tag ); if( !name ) continue; printf( " 0x%04x %-29.29s", tag, name ); for( i = 0; i < EXIF_IFD_COUNT; i++ ) if( exif_content_get_entry( data->ifd[i], tag ) ) printf( " * " ); else printf( " - " ); printf( "\n" ); } } static void show_entry( ExifEntry *entry, void *client ) { char exif_text[256]; printf( "%s", exif_tag_get_title( entry->tag ) ); printf( "|" ); printf( "%s", exif_entry_get_value( entry, exif_text, 256 ) ); printf( "|" ); printf( "%s", exif_format_get_name( entry->format ) ); printf( "|" ); printf( "%d bytes", entry->size ); printf( "\n" ); } static void show_ifd( ExifContent *content, void *client ) { int *ifd = (int *) client; printf( "- ifd %d\n", *ifd ); exif_content_foreach_entry( content, show_entry, client ); *ifd += 1; } static void show_values( ExifData *data ) { ExifByteOrder order; int ifd; order = exif_data_get_byte_order( data ); printf( "EXIF tags in '%s' byte order\n", exif_byte_order_get_name( order ) ); printf( "Title|Value|Format|Size\n" ); ifd = 0; exif_data_foreach_content( data, show_ifd, &ifd ); if( data->size ) printf( "contains thumbnail of %d bytes\n", data->size ); } #endif /*DEBUG_VERBOSE*/ /* Like exif_data_new_from_data(), but don't default missing fields. * * If we do exif_data_new_from_data(), then missing fields are set to * their default value and we won't know about it. */ static ExifData * vips_exif_load_data_without_fix( const void *data, size_t length ) { ExifData *ed; /* exif_data_load_data() only allows uint for length. Limit it to less * than that: 2**20 should be enough for anyone. */ if( length < 4 ) { vips_error( "exif", "%s", _( "exif too small" ) ); return( NULL ); } if( length > 1 << 20 ) { vips_error( "exif", "%s", _( "exif too large" ) ); return( NULL ); } if( !(ed = exif_data_new()) ) { vips_error( "exif", "%s", _( "unable to init exif" ) ); return( NULL ); } exif_data_unset_option( ed, EXIF_DATA_OPTION_FOLLOW_SPECIFICATION ); if( !vips_isprefix( "Exif", (char *) data ) ) { /* Ensure "Exif" prefix as loaders may not provide it. */ void* data_with_prefix; data_with_prefix = g_malloc0( length + 6 ); memcpy( data_with_prefix, "Exif\0\0", 6 ); memcpy( data_with_prefix + 6, data, length ); exif_data_load_data( ed, data_with_prefix, length + 6 ); g_free( data_with_prefix ); } else exif_data_load_data( ed, data, length ); return( ed ); } static int vips_exif_get_int( ExifData *ed, ExifEntry *entry, unsigned long component, int *out ) { ExifByteOrder bo = exif_data_get_byte_order( ed ); size_t sizeof_component = entry->size / entry->components; size_t offset = component * sizeof_component; if( entry->format == EXIF_FORMAT_SHORT ) *out = exif_get_short( entry->data + offset, bo ); else if( entry->format == EXIF_FORMAT_SSHORT ) *out = exif_get_sshort( entry->data + offset, bo ); else if( entry->format == EXIF_FORMAT_LONG ) /* This won't work for huge values, but who cares. */ *out = (int) exif_get_long( entry->data + offset, bo ); else if( entry->format == EXIF_FORMAT_SLONG ) *out = exif_get_slong( entry->data + offset, bo ); else return( -1 ); return( 0 ); } static int vips_exif_get_rational( ExifData *ed, ExifEntry *entry, unsigned long component, ExifRational *out ) { if( entry->format == EXIF_FORMAT_RATIONAL ) { ExifByteOrder bo = exif_data_get_byte_order( ed ); size_t sizeof_component = entry->size / entry->components; size_t offset = component * sizeof_component; *out = exif_get_rational( entry->data + offset, bo ); } else return( -1 ); return( 0 ); } static int vips_exif_get_srational( ExifData *ed, ExifEntry *entry, unsigned long component, ExifSRational *out ) { if( entry->format == EXIF_FORMAT_SRATIONAL ) { ExifByteOrder bo = exif_data_get_byte_order( ed ); size_t sizeof_component = entry->size / entry->components; size_t offset = component * sizeof_component; *out = exif_get_srational( entry->data + offset, bo ); } else return( -1 ); return( 0 ); } static int vips_exif_get_double( ExifData *ed, ExifEntry *entry, unsigned long component, double *out ) { ExifRational rv; ExifSRational srv; double value; if( !vips_exif_get_rational( ed, entry, component, &rv ) ) { if( rv.denominator == 0 ) value = 0; else value = (double) rv.numerator / rv.denominator; } else if( !vips_exif_get_srational( ed, entry, component, &srv ) ) { if( srv.denominator == 0 ) value = 0; else value = (double) srv.numerator / srv.denominator; } else return( -1 ); *out = value; return( 0 ); } /* Save an exif value to a string in a way that we can restore. We only bother * for the simple formats (that a client might try to change) though. * * Keep in sync with vips_exif_from_s() below. */ static void vips_exif_to_s( ExifData *ed, ExifEntry *entry, VipsBuf *buf ) { unsigned long i; int iv; ExifRational rv; ExifSRational srv; char txt[256]; if( entry->format == EXIF_FORMAT_ASCII ) { /* libexif does not null-terminate strings. Copy out and add * the \0 ourselves. */ int len = VIPS_MIN( 254, entry->size ); memcpy( txt, entry->data, len ); txt[len] = '\0'; vips_buf_appendf( buf, "%s ", txt ); } else if( entry->components < 10 && !vips_exif_get_int( ed, entry, 0, &iv ) ) { for( i = 0; i < entry->components; i++ ) { vips_exif_get_int( ed, entry, i, &iv ); vips_buf_appendf( buf, "%d ", iv ); } } else if( entry->components < 10 && !vips_exif_get_rational( ed, entry, 0, &rv ) ) { for( i = 0; i < entry->components; i++ ) { vips_exif_get_rational( ed, entry, i, &rv ); vips_buf_appendf( buf, "%u/%u ", rv.numerator, rv.denominator ); } } else if( entry->components < 10 && !vips_exif_get_srational( ed, entry, 0, &srv ) ) { for( i = 0; i < entry->components; i++ ) { vips_exif_get_srational( ed, entry, i, &srv ); vips_buf_appendf( buf, "%d/%d ", srv.numerator, srv.denominator ); } } else vips_buf_appendf( buf, "%s ", exif_entry_get_value( entry, txt, 256 ) ); vips_buf_appendf( buf, "(%s, %s, %lu components, %d bytes)", exif_entry_get_value( entry, txt, 256 ), exif_format_get_name( entry->format ), entry->components, entry->size ); } typedef struct _VipsExifParams { VipsImage *image; ExifData *ed; } VipsExifParams; /* tags do not uniquely set tag names: the same tag can have different * names in different ifds. * * As long as this entry has been linked to an ifd, get the tag name. */ static const char * vips_exif_entry_get_name( ExifEntry *entry ) { if( !entry->parent ) return( NULL ); return( exif_tag_get_name_in_ifd( entry->tag, exif_entry_get_ifd( entry ) ) ); } static void vips_exif_attach_entry( ExifEntry *entry, VipsExifParams *params ) { const char *tag_name; char vips_name_txt[256]; VipsBuf vips_name = VIPS_BUF_STATIC( vips_name_txt ); char value_txt[256]; VipsBuf value = VIPS_BUF_STATIC( value_txt ); if( !(tag_name = vips_exif_entry_get_name( entry )) ) return; vips_buf_appendf( &vips_name, "exif-ifd%d-%s", exif_entry_get_ifd( entry ), tag_name ); vips_exif_to_s( params->ed, entry, &value ); /* Can't do anything sensible with the error return. */ (void) vips_image_set_string( params->image, vips_buf_all( &vips_name ), vips_buf_all( &value ) ); } static void vips_exif_get_content( ExifContent *content, VipsExifParams *params ) { exif_content_foreach_entry( content, (ExifContentForeachEntryFunc) vips_exif_attach_entry, params ); } static int vips_exif_entry_get_double( ExifData *ed, int ifd, ExifTag tag, double *out ) { ExifEntry *entry; if( !(entry = exif_content_get_entry( ed->ifd[ifd], tag )) || entry->components != 1 ) return( -1 ); return( vips_exif_get_double( ed, entry, 0, out ) ); } static int vips_exif_entry_get_int( ExifData *ed, int ifd, ExifTag tag, int *out ) { ExifEntry *entry; if( !(entry = exif_content_get_entry( ed->ifd[ifd], tag )) || entry->components != 1 ) return( -1 ); return( vips_exif_get_int( ed, entry, 0, out ) ); } /* Set the image resolution from the EXIF tags. */ static int vips_image_resolution_from_exif( VipsImage *image, ExifData *ed ) { double xres, yres; int unit; /* The main image xres/yres are in ifd0. ifd1 has xres/yres of the * image thumbnail, if any. * * Don't warn about missing res fields, it's very common, especially for * things like webp. */ if( vips_exif_entry_get_double( ed, 0, EXIF_TAG_X_RESOLUTION, &xres ) || vips_exif_entry_get_double( ed, 0, EXIF_TAG_Y_RESOLUTION, &yres ) ) return( -1 ); /* resuint is optional and defaults to inch. */ unit = 2; (void) vips_exif_entry_get_int( ed, 0, EXIF_TAG_RESOLUTION_UNIT, &unit ); #ifdef DEBUG printf( "vips_image_resolution_from_exif: seen exif tags " "xres = %g, yres = %g, unit = %d\n", xres, yres, unit ); #endif /*DEBUG*/ switch( unit ) { case 1: /* No units, instead xres / yres gives the pixel aspect ratio. */ break; case 2: /* In inches. */ xres /= 25.4; yres /= 25.4; vips_image_set_string( image, VIPS_META_RESOLUTION_UNIT, "in" ); break; case 3: /* In cm. */ xres /= 10.0; yres /= 10.0; vips_image_set_string( image, VIPS_META_RESOLUTION_UNIT, "cm" ); break; default: g_warning( "%s", _( "unknown EXIF resolution unit" ) ); return( -1 ); } #ifdef DEBUG printf( "vips_image_resolution_from_exif: " "seen exif resolution %g, %g p/mm\n", xres, yres ); #endif /*DEBUG*/ /* Don't allow negative resolution. */ image->Xres = VIPS_MAX( 0, xres ); image->Yres = VIPS_MAX( 0, yres ); return( 0 ); } /* Need to fwd ref this. */ static int vips_exif_resolution_from_image( ExifData *ed, VipsImage *image ); /* Scan the exif block on the image, if any, and make a set of vips metadata * tags for what we find. */ int vips__exif_parse( VipsImage *image ) { const void *data; size_t size; ExifData *ed; VipsExifParams params; const char *str; if( !vips_image_get_typeof( image, VIPS_META_EXIF_NAME ) ) return( 0 ); if( vips_image_get_blob( image, VIPS_META_EXIF_NAME, &data, &size ) ) return( -1 ); if( !(ed = vips_exif_load_data_without_fix( data, size )) ) return( -1 ); #ifdef DEBUG_VERBOSE show_tags( ed ); show_values( ed ); #endif /*DEBUG_VERBOSE*/ /* Look for resolution fields and use them to set the VIPS xres/yres * fields. * * If the fields are missing, set them from the image, which will have * previously had them set from something like JFIF. */ if( vips_image_resolution_from_exif( image, ed ) && vips_exif_resolution_from_image( ed, image ) ) { exif_data_free( ed ); return( -1 ); } /* Make sure all required fields are there before we attach the vips * metadata. */ exif_data_fix( ed ); /* Attach informational fields for what we find. */ params.image = image; params.ed = ed; exif_data_foreach_content( ed, (ExifDataForeachContentFunc) vips_exif_get_content, ¶ms ); vips_image_set_blob_copy( image, "jpeg-thumbnail-data", ed->data, ed->size ); exif_data_free( ed ); /* Orientation handling. ifd0 has the Orientation tag for the main * image. */ if( vips_image_get_typeof( image, "exif-ifd0-Orientation" ) != 0 && !vips_image_get_string( image, "exif-ifd0-Orientation", &str ) ) { int orientation; orientation = atoi( str ); if( orientation < 1 || orientation > 8 ) orientation = 1; vips_image_set_int( image, VIPS_META_ORIENTATION, orientation ); } return( 0 ); } 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", vips_exif_entry_get_name( entry ), 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 ); } static void vips_exif_double_to_rational( double value, ExifRational *rv ) { /* We will usually set factors of 10, so use 1000 as the denominator * and it'll probably be OK. */ rv->numerator = value * 1000; rv->denominator = 1000; } static void vips_exif_double_to_srational( double value, ExifSRational *srv ) { /* We will usually set factors of 10, so use 1000 as the denominator * and it'll probably be OK. */ srv->numerator = value * 1000; srv->denominator = 1000; } /* Parse a char * into an ExifRational. We allow floats as well. */ static void vips_exif_parse_rational( const char *str, ExifRational *rv ) { if( sscanf( str, " %u / %u ", &rv->numerator, &rv->denominator ) == 2 ) return; vips_exif_double_to_rational( g_ascii_strtod( str, NULL ), rv ); } /* Parse a char * into an ExifSRational. We allow floats as well. */ static void vips_exif_parse_srational( const char *str, ExifSRational *srv ) { if( sscanf( str, " %d / %d ", &srv->numerator, &srv->denominator ) == 2 ) return; vips_exif_double_to_srational( g_ascii_strtod( str, NULL ), 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", vips_exif_entry_get_name( entry ), 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 ); } } /* 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", vips_exif_entry_get_name( entry ), value ); if( entry->format == EXIF_FORMAT_RATIONAL ) { ExifRational rv; rv = exif_get_rational( entry->data + offset, bo ); if( rv.denominator == 0 ) old_value = 0; else 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 ); if( srv.denominator == 0 ) old_value = 0; else 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 ); } } } typedef void (*write_fn)( ExifData *ed, ExifEntry *entry, unsigned long component, void *data ); /* String-valued tags need special treatment, sadly. * * Strings are written in three ways: * * 1. As ASCII, but with an 8-byte preamble giving the encoding (it's always * ASCII though) and the format undefined. * 2. As plain ASCII, with the format giving the encoding. * 3. As UTF16 in the MS tags. */ static gboolean tag_is_encoding( ExifTag tag ) { return( tag == EXIF_TAG_USER_COMMENT ); } static gboolean tag_is_ascii( ExifTag tag ) { return( tag == EXIF_TAG_MAKE || tag == EXIF_TAG_MODEL || tag == EXIF_TAG_IMAGE_DESCRIPTION || tag == EXIF_TAG_ARTIST || tag == EXIF_TAG_SOFTWARE || tag == EXIF_TAG_COPYRIGHT || tag == EXIF_TAG_DATE_TIME || tag == EXIF_TAG_DATE_TIME_ORIGINAL || tag == EXIF_TAG_DATE_TIME_DIGITIZED || tag == EXIF_TAG_SUB_SEC_TIME || tag == EXIF_TAG_SUB_SEC_TIME_ORIGINAL || tag == EXIF_TAG_SUB_SEC_TIME_DIGITIZED #ifdef HAVE_EXIF_0_6_22 || tag == EXIF_TAG_CAMERA_OWNER_NAME || tag == EXIF_TAG_BODY_SERIAL_NUMBER || tag == EXIF_TAG_LENS_MAKE || tag == EXIF_TAG_LENS_MODEL || tag == EXIF_TAG_LENS_SERIAL_NUMBER #endif #ifdef HAVE_EXIF_0_6_23 || tag == EXIF_TAG_OFFSET_TIME || tag == EXIF_TAG_OFFSET_TIME_ORIGINAL || tag == EXIF_TAG_OFFSET_TIME_DIGITIZED || tag == EXIF_TAG_GPS_LATITUDE_REF || tag == EXIF_TAG_GPS_LONGITUDE_REF || tag == EXIF_TAG_GPS_SATELLITES || tag == EXIF_TAG_GPS_STATUS || tag == EXIF_TAG_GPS_MEASURE_MODE || tag == EXIF_TAG_GPS_SPEED_REF || tag == EXIF_TAG_GPS_TRACK_REF || tag == EXIF_TAG_GPS_IMG_DIRECTION_REF || tag == EXIF_TAG_GPS_MAP_DATUM || tag == EXIF_TAG_GPS_DEST_LATITUDE_REF || tag == EXIF_TAG_GPS_DEST_LONGITUDE_REF || tag == EXIF_TAG_GPS_DEST_BEARING_REF || tag == EXIF_TAG_GPS_DEST_DISTANCE_REF || tag == EXIF_TAG_GPS_DATE_STAMP #endif ); } static gboolean tag_is_utf16( ExifTag tag ) { return( tag == EXIF_TAG_XP_TITLE || tag == EXIF_TAG_XP_COMMENT || tag == EXIF_TAG_XP_AUTHOR || tag == EXIF_TAG_XP_KEYWORDS || tag == EXIF_TAG_XP_SUBJECT ); } /* Set a libexif-formatted string entry. */ static void vips_exif_alloc_string( ExifEntry *entry, unsigned long components ) { ExifMem *mem; g_assert( !entry->data ); /* The string in the entry must be allocated with the same allocator * that was used to allocate the entry itself. We can't do this * because the allocator is private :( so we must assume the entry was * created with the default one. */ mem = exif_mem_new_default(); /* EXIF_FORMAT_UNDEFINED is correct for EXIF_TAG_USER_COMMENT, our * caller should change this if it wishes. */ entry->data = exif_mem_alloc( mem, components ); entry->size = components; entry->components = components; entry->format = EXIF_FORMAT_UNDEFINED; VIPS_FREEF( exif_mem_unref, mem ); } /* The final " (xx, yy, zz, kk)" part of the string (if present) was * added by us in _to_s(), we must remove it before setting the string * back again. * * It may not be there if the user has changed the string. */ static char * drop_tail( const char *data ) { char *str; char *p; str = g_strdup( data ); p = str + strlen( str ); if( p > str && *g_utf8_prev_char( p ) == ')' && (p = g_utf8_strrchr( str, -1, (gunichar) '(')) && p > str && *(p = g_utf8_prev_char( p )) == ' ' ) *p = '\0'; return( str ); } /* special header required for EXIF_TAG_USER_COMMENT. */ #define ASCII_COMMENT "ASCII\0\0\0" /* Write a libvips NULL-terminated utf-8 string into a entry tagged with a * encoding. UserComment is like this, for example. */ static void vips_exif_set_string_encoding( ExifData *ed, ExifEntry *entry, unsigned long component, const char *data ) { char *str; char *ascii; int len; str = drop_tail( data ); /* libexif can only really save ASCII to things like UserComment. */ ascii = g_str_to_ascii( str, NULL ); g_free( str ); str = ascii; /* libexif comment strings are not NULL-terminated, and have an * encoding tag (always ASCII) in the first 8 bytes. */ len = strlen( str ); vips_exif_alloc_string( entry, sizeof( ASCII_COMMENT ) - 1 + len ); memcpy( entry->data, ASCII_COMMENT, sizeof( ASCII_COMMENT ) - 1 ); memcpy( entry->data + sizeof( ASCII_COMMENT ) - 1, str, len ); g_free( str ); } /* Write a libvips NULL-terminated utf-8 string into an ASCII entry. Tags like * ImageDescription work like this. */ static void vips_exif_set_string_ascii( ExifData *ed, ExifEntry *entry, unsigned long component, const char *data ) { char *str; char *ascii; int len; str = drop_tail( data ); /* libexif can only really save ASCII to things like UserComment. */ ascii = g_str_to_ascii( str, NULL ); g_free( str ); str = ascii; /* ASCII strings are NULL-terminated. */ len = strlen( str ); vips_exif_alloc_string( entry, len + 1 ); memcpy( entry->data, str, len + 1 ); entry->format = EXIF_FORMAT_ASCII; g_free( str ); } /* Write a libvips NULL-terminated utf-8 string into a utf16 entry. */ static void vips_exif_set_string_utf16( ExifData *ed, ExifEntry *entry, unsigned long component, const char *data ) { char *str; gunichar2 *utf16; glong len; str = drop_tail( data ); utf16 = g_utf8_to_utf16( str, -1, NULL, &len, NULL ); /* libexif utf16 strings are NULL-terminated. */ vips_exif_alloc_string( entry, (len + 1) * 2 ); memcpy( entry->data, utf16, (len + 1) * 2 ); entry->format = EXIF_FORMAT_BYTE; g_free( utf16 ); g_free( str ); } /* Write a tag. Update what's there, or make a new one. */ static void vips_exif_set_tag( ExifData *ed, int ifd, ExifTag tag, write_fn fn, void *data ) { ExifEntry *entry; if( (entry = exif_content_get_entry( ed->ifd[ifd], tag )) ) { fn( ed, entry, 0, data ); } else { entry = exif_entry_new(); /* tag must be set before calling exif_content_add_entry. */ entry->tag = tag; exif_content_add_entry( ed->ifd[ifd], entry ); exif_entry_unref( entry ); /* libexif makes us have a special path for string-valued * fields :( */ if( tag_is_encoding( tag ) ) vips_exif_set_string_encoding( ed, entry, 0, data ); else if( tag_is_ascii( tag ) ) vips_exif_set_string_ascii( ed, entry, 0, data ); else if( tag_is_utf16( tag ) ) vips_exif_set_string_utf16( ed, entry, 0, data ); else { exif_entry_initialize( entry, tag ); fn( ed, entry, 0, data ); } } } /* Set the EXIF resolution from the vips xres/yres tags. */ static int vips_exif_resolution_from_image( ExifData *ed, VipsImage *image ) { double xres, yres; const char *p; int unit; VIPS_DEBUG_MSG( "vips_exif_resolution_from_image: vips res of %g, %g\n", image->Xres, image->Yres ); /* Default to inches, more progs support it. */ unit = 2; if( vips_image_get_typeof( image, VIPS_META_RESOLUTION_UNIT ) && !vips_image_get_string( image, VIPS_META_RESOLUTION_UNIT, &p ) ) { if( vips_isprefix( "cm", p ) ) unit = 3; else if( vips_isprefix( "none", p ) ) unit = 1; } switch( unit ) { case 1: xres = image->Xres; yres = image->Yres; break; case 2: xres = image->Xres * 25.4; yres = image->Yres * 25.4; break; case 3: xres = image->Xres * 10.0; yres = image->Yres * 10.0; break; default: g_warning( "%s", _( "unknown EXIF resolution unit" ) ); return( 0 ); } /* Main image xres/yres/unit are in ifd0. ifd1 has the thumbnail * xres/yres/unit. */ vips_exif_set_tag( ed, 0, EXIF_TAG_X_RESOLUTION, vips_exif_set_double, (void *) &xres ); vips_exif_set_tag( ed, 0, EXIF_TAG_Y_RESOLUTION, vips_exif_set_double, (void *) &yres ); vips_exif_set_tag( ed, 0, EXIF_TAG_RESOLUTION_UNIT, vips_exif_set_int, (void *) &unit ); return( 0 ); } /* Exif also tracks image dimensions. */ static int vips_exif_set_dimensions( ExifData *ed, VipsImage *im ) { VIPS_DEBUG_MSG( "vips_exif_set_dimensions: vips size of %d, %d\n", im->Xsize, im->Ysize ); vips_exif_set_tag( ed, 2, EXIF_TAG_PIXEL_X_DIMENSION, vips_exif_set_int, (void *) &im->Xsize ); vips_exif_set_tag( ed, 2, EXIF_TAG_PIXEL_Y_DIMENSION, vips_exif_set_int, (void *) &im->Ysize ); return( 0 ); } /* And orientation. */ static int vips_exif_set_orientation( ExifData *ed, VipsImage *im ) { int orientation; /* We set the tag, even if it's been deleted, since it's a required * field. */ if( !vips_image_get_typeof( im, VIPS_META_ORIENTATION ) || vips_image_get_int( im, VIPS_META_ORIENTATION, &orientation ) ) orientation = 1; VIPS_DEBUG_MSG( "set_exif_orientation: %d\n", orientation ); vips_exif_set_tag( ed, 0, EXIF_TAG_ORIENTATION, vips_exif_set_int, (void *) &orientation ); return( 0 ); } /* And thumbnail. */ static int vips_exif_set_thumbnail( ExifData *ed, VipsImage *im ) { /* Delete any old thumbnail data. We should use the exif free func, * but the memory allocator is not exposed by libexif! Hopefully they * are just using free(). * * exif.c makes this assumption too when it tries to update a * thumbnail. */ if( ed->data ) { free( ed->data ); ed->data = NULL; } ed->size = 0; /* Update EXIF thumbnail from metadata, if any. */ if( vips_image_get_typeof( im, "jpeg-thumbnail-data" ) ) { const void *data; size_t size; if( vips_image_get_blob( im, "jpeg-thumbnail-data", &data, &size ) ) return( -1 ); /* Again, we should use the exif allocator attached to this * entry, but it is not exposed! */ if( size > 0 && data ) { ed->data = malloc( size ); memcpy( ed->data, data, size ); ed->size = size; } } return( 0 ); } /* Skip any spaces. */ static const char * skip_space( const char *p ) { while( p && *p == ' ' ) p += 1; return( p ); } /* Skip to the end of this non-space sequence. */ static const char * skip_nonspace( const char *p ) { while( p && *p && *p != ' ' ) p += 1; return( p ); } /* See also vips_exif_to_s() ... keep in sync. Only the numeric types are * handled here, since they can be updated. For string types, we have to * destroy and recreate, see above. */ static void vips_exif_from_s( ExifData *ed, ExifEntry *entry, const char *value ) { unsigned long i; const char *p; int v; if( entry->format == EXIF_FORMAT_SHORT || entry->format == EXIF_FORMAT_SSHORT || entry->format == EXIF_FORMAT_LONG || entry->format == EXIF_FORMAT_SLONG ) { if( entry->components >= 10 ) return; p = value; for( i = 0; i < entry->components; i++ ) { if( !(p = skip_space( p )) ) break; v = atof( p ); vips_exif_set_int( ed, entry, i, &v ); p = skip_nonspace( p ); } } else if( entry->format == EXIF_FORMAT_RATIONAL || entry->format == EXIF_FORMAT_SRATIONAL ) { if( entry->components >= 10 ) return; p = value; for( i = 0; i < entry->components; i++ ) { if( !(p = skip_space( p )) ) break; vips_exif_set_rational( ed, entry, i, (void *) p ); p = skip_nonspace( p ); } } } static void vips_exif_set_entry( ExifData *ed, ExifEntry *entry, unsigned long component, void *data ) { const char *string = (const char *) data; vips_exif_from_s( ed, entry, string ); } static void * vips_exif_image_field( VipsImage *image, const char *field, GValue *value, void *data ) { ExifData *ed = (ExifData *) data; const char *string; int ifd; const char *p; ExifTag tag; if( !vips_isprefix( "exif-ifd", field ) ) return( NULL ); /* value must be a string. */ if( vips_image_get_string( image, field, &string ) ) { g_warning( _( "bad exif meta \"%s\"" ), field ); return( NULL ); } p = field + strlen( "exif-ifd" ); ifd = atoi( p ); for( ; isdigit( *p ); p++ ) ; if( *p != '-' ) { g_warning( _( "bad exif meta \"%s\"" ), field ); return( NULL ); } /* GPSVersionID is tag 0 (the error return) so we have to * test the name too. */ if( !(tag = exif_tag_from_name( p + 1 )) && strcmp( p + 1, "GPSVersionID" ) != 0 ) { g_warning( _( "bad exif meta \"%s\"" ), field ); return( NULL ); } vips_exif_set_tag( ed, ifd, tag, vips_exif_set_entry, (void *) string ); return( NULL ); } typedef struct _VipsExifRemove { VipsImage *image; ExifData *ed; ExifContent *content; GSList *to_remove; } VipsExifRemove; static void vips_exif_exif_entry( ExifEntry *entry, VipsExifRemove *ve ) { const char *tag_name; char vips_name_txt[256]; VipsBuf vips_name_buf = VIPS_BUF_STATIC( vips_name_txt ); const char *vips_name; const char *vips_value; if( !(tag_name = vips_exif_entry_get_name( entry )) ) return; vips_buf_appendf( &vips_name_buf, "exif-ifd%d-%s", exif_entry_get_ifd( entry ), tag_name ); vips_name = vips_buf_all( &vips_name_buf ); /* Is there a image metadata item for this tag? */ vips_value = NULL; if( vips_image_get_typeof( ve->image, vips_name ) ) { /* No easy way to return an error code from here, sadly. */ if( vips_image_get_string( ve->image, vips_name, &vips_value ) ) g_warning( _( "bad exif meta \"%s\"" ), vips_name ); } /* Does this field exist on the image? If not, schedule it for * removal. */ if( !vips_value ) 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_value ) ve->to_remove = g_slist_prepend( ve->to_remove, entry ); /* If this is a string tag with a new value, we must also remove it * ready for recreation, see the comment below. */ if( vips_value && (tag_is_encoding( entry->tag ) || tag_is_ascii( entry->tag ) || tag_is_utf16( entry->tag )) ) { char value_txt[256]; VipsBuf value = VIPS_BUF_STATIC( value_txt ); /* Render the original exif-data value to a string and see * if the user has changed it. If they have, remove it ready * for re-adding. * * Leaving it there prevents it being recreated. */ vips_exif_to_s( ve->ed, entry, &value ); if( strcmp( vips_buf_all( &value ), vips_value ) != 0 ) ve->to_remove = g_slist_prepend( ve->to_remove, entry ); } } static void * vips_exif_exif_remove( ExifEntry *entry, VipsExifRemove *ve, void *b ) { #ifdef DEBUG { const char *tag_name; char vips_name_txt[256]; VipsBuf vips_name = VIPS_BUF_STATIC( vips_name_txt ); tag_name = vips_exif_entry_get_name( entry ); 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 ); } static void vips_exif_exif_content( ExifContent *content, VipsExifRemove *ve ) { ve->content = content; ve->to_remove = NULL; exif_content_foreach_entry( content, (ExifContentForeachEntryFunc) vips_exif_exif_entry, ve ); vips_slist_map2( ve->to_remove, (VipsSListMap2Fn) vips_exif_exif_remove, ve, NULL ); VIPS_FREEF( g_slist_free, ve->to_remove ); } static void vips_exif_update( ExifData *ed, VipsImage *image ) { VipsExifRemove ve; VIPS_DEBUG_MSG( "vips_exif_update: \n" ); /* If this exif came from the image (rather than being an exif block we * have made afresh), then any fields which are in the block but not on * the image must have been deliberately removed. Remove them from the * block as well. * * Any string-valued fields (eg. comment etc.) which exist as libvips * metadata tags with changed whose values have changed must also be * removed. * * libexif does not allow you to change string lengths (you must make * new tags) so we have to remove ready to re-add. */ if( vips_image_get_typeof( image, VIPS_META_EXIF_NAME ) ) { ve.image = image; ve.ed = ed; exif_data_foreach_content( ed, (ExifDataForeachContentFunc) vips_exif_exif_content, &ve ); } /* Walk the image and add any exif- that's set in image metadata. */ vips_image_map( image, vips_exif_image_field, ed ); } /* Examine the metadata tags on the image and update the EXIF block. */ int vips__exif_update( VipsImage *image ) { unsigned char *data; size_t length; unsigned int idl; ExifData *ed; /* Either parse from the embedded EXIF, or if there's none, make * some fresh EXIF we can write the resolution to. */ if( vips_image_get_typeof( image, VIPS_META_EXIF_NAME ) ) { if( vips_image_get_blob( image, VIPS_META_EXIF_NAME, (void *) &data, &length ) ) return( -1 ); if( !(ed = vips_exif_load_data_without_fix( data, length )) ) return( -1 ); } else { ed = exif_data_new(); exif_data_set_option( ed, EXIF_DATA_OPTION_FOLLOW_SPECIFICATION ); exif_data_set_data_type( ed, EXIF_DATA_TYPE_COMPRESSED ); exif_data_set_byte_order( ed, EXIF_BYTE_ORDER_INTEL ); } /* Make sure all required fields are there before we attach the vips * metadata. */ exif_data_fix( ed ); /* Update EXIF tags from the image metadata. */ vips_exif_update( ed, image ); /* Update EXIF resolution from the vips image header. */ if( vips_exif_resolution_from_image( ed, image ) ) { exif_data_free( ed ); return( -1 ); } /* Update EXIF image dimensions from the vips image header. */ if( vips_exif_set_dimensions( ed, image ) ) { exif_data_free( ed ); return( -1 ); } /* Update EXIF orientation from the vips image header. */ if( vips_exif_set_orientation( ed, image ) ) { exif_data_free( ed ); return( -1 ); } /* Update the thumbnail. */ if( vips_exif_set_thumbnail( ed, image ) ) { exif_data_free( ed ); return( -1 ); } /* Reserialise and write. exif_data_save_data() returns an int for some * reason. */ exif_data_save_data( ed, &data, &idl ); if( !idl ) { vips_error( "exif", "%s", _( "error saving EXIF" ) ); exif_data_free( ed ); return( -1 ); } length = idl; #ifdef DEBUG printf( "vips__exif_update: generated %zd bytes of EXIF\n", length ); #endif /*DEBUG*/ vips_image_set_blob( image, VIPS_META_EXIF_NAME, (VipsCallbackFn) vips_area_free_cb, data, length ); exif_data_free( ed ); return( 0 ); } #else /*!HAVE_EXIF*/ int vips__exif_parse( VipsImage *image ) { return( 0 ); } int vips__exif_update( VipsImage *image ) { return( 0 ); } #endif /*!HAVE_EXIF*/