jpeg read/write copies XMP data

im_jpeg2vips() and im_vips2jpeg() now know about XMP metadata and copy
it over
This commit is contained in:
John Cupitt 2011-10-12 11:45:27 +01:00
parent 5e6251ac1d
commit 8a8bb8fcf4
4 changed files with 112 additions and 26 deletions

View File

@ -1,3 +1,6 @@
12/10/11 started 7.26.5
- jpeg read/write copies over XMP data
12/9/11 started 7.26.4 12/9/11 started 7.26.4
- fallback vips_init() - fallback vips_init()
- im_openout() compat stub was wrong, breaking ruby-vips - im_openout() compat stub was wrong, breaking ruby-vips

View File

@ -35,6 +35,8 @@
* data, some jpegs seem to have several APP1s, argh * data, some jpegs seem to have several APP1s, argh
* 20/4/2011 * 20/4/2011
* - added im_bufjpeg2vips() * - added im_bufjpeg2vips()
* 12/10/2011
* - read XMP data
*/ */
/* /*
@ -402,16 +404,19 @@ read_exif( IMAGE *im, void *data, int data_length )
{ {
char *data_copy; char *data_copy;
/* Horrifyingly, some JPEGs have several APP1 sections. We must only /* Only use the first one.
* use the first one that starts "Exif.."
*/ */
if( ((char *) data)[0] != 'E' || if( im_header_get_typeof( im, IM_META_EXIF_NAME ) ) {
((char *) data)[1] != 'x' || #ifdef DEBUG
((char *) data)[2] != 'i' || printf( "read_exif: second EXIF block, ignoring\n" );
((char *) data)[3] != 'f' ) #endif /*DEBUG*/
return( 0 );
if( im_header_get_typeof( im, IM_META_EXIF_NAME ) )
return( 0 ); return( 0 );
}
#ifdef DEBUG
printf( "read_exif: attaching %d bytes of exif\n", data_length );
#endif /*DEBUG*/
/* Always attach a copy of the unparsed exif data. /* Always attach a copy of the unparsed exif data.
*/ */
@ -464,10 +469,43 @@ read_exif( IMAGE *im, void *data, int data_length )
return( 0 ); return( 0 );
} }
/* Number of app2 sections we can capture. Each one can be 64k, so 640k should static int
read_xmp( IMAGE *im, void *data, int data_length )
{
char *data_copy;
/* XMP sections start "http". Only use the first one.
*/
if( im_header_get_typeof( im, VIPS_META_XMP_NAME ) ) {
#ifdef DEBUG
printf( "read_xmp: second XMP block, ignoring\n" );
#endif /*DEBUG*/
return( 0 );
}
#ifdef DEBUG
printf( "read_xmp: attaching %d bytes of XMP\n", data_length );
#endif /*DEBUG*/
/* Always attach a copy of the unparsed exif data.
*/
if( !(data_copy = im_malloc( NULL, data_length )) )
return( -1 );
memcpy( data_copy, data, data_length );
if( im_meta_set_blob( im, VIPS_META_XMP_NAME,
(im_callback_fn) im_free, data_copy, data_length ) ) {
im_free( data_copy );
return( -1 );
}
return( 0 );
}
/* Number of app2 sections we can capture. Each one can be 64k, so 6400k should
* be enough for anyone (haha). * be enough for anyone (haha).
*/ */
#define MAX_APP2_SECTIONS (10) #define MAX_APP2_SECTIONS (100)
/* Read a cinfo to a VIPS image. Set invert_pels if the pixel reader needs to /* Read a cinfo to a VIPS image. Set invert_pels if the pixel reader needs to
* do 255-pel. * do 255-pel.
@ -532,26 +570,38 @@ read_jpeg_header( struct jpeg_decompress_struct *cinfo,
/* Look for EXIF and ICC profile. /* Look for EXIF and ICC profile.
*/ */
for( p = cinfo->marker_list; p; p = p->next ) { for( p = cinfo->marker_list; p; p = p->next ) {
#ifdef DEBUG
{
int i;
printf( "read_jpeg_header: seen %d bytes of APP%d\n",
p->data_length,
p->marker - JPEG_APP0 );
for( i = 0; i < 10; i++ )
printf( "\t%d) '%c' (%d)\n",
i, p->data[i], p->data[i] );
}
#endif /*DEBUG*/
switch( p->marker ) { switch( p->marker ) {
case JPEG_APP0 + 1: case JPEG_APP0 + 1:
/* EXIF data. /* Possible EXIF or XMP data.
*/ */
#ifdef DEBUG if( p->data_length > 4 &&
printf( "read_jpeg_header: seen %d bytes of APP1\n", im_isprefix( "Exif", (char *) p->data ) &&
p->data_length ); read_exif( out, p->data, p->data_length ) )
#endif /*DEBUG*/
if( read_exif( out, p->data, p->data_length ) )
return( -1 ); return( -1 );
if( p->data_length > 4 &&
im_isprefix( "http", (char *) p->data ) &&
read_xmp( out, p->data, p->data_length ) )
break; break;
case JPEG_APP0 + 2: case JPEG_APP0 + 2:
/* ICC profile. /* ICC profile.
*/ */
#ifdef DEBUG
printf( "read_jpeg_header: seen %d bytes of APP2\n",
p->data_length );
#endif /*DEBUG*/
if( p->data_length > 14 && if( p->data_length > 14 &&
im_isprefix( "ICC_PROFILE", im_isprefix( "ICC_PROFILE",
(char *) p->data ) ) { (char *) p->data ) ) {
@ -570,10 +620,6 @@ read_jpeg_header( struct jpeg_decompress_struct *cinfo,
break; break;
default: default:
#ifdef DEBUG
printf( "read_jpeg_header: seen %d bytes of data\n",
p->data_length );
#endif /*DEBUG*/
break; break;
} }
} }

View File

@ -40,6 +40,8 @@
* longer overallocate or underallocate * longer overallocate or underallocate
* 8/7/11 * 8/7/11
* - oop CMYK write was not inverting, thanks Ole * - oop CMYK write was not inverting, thanks Ole
* 12/10/2011
* - write XMP data
*/ */
/* /*
@ -413,7 +415,7 @@ write_exif( Write *write )
return( -1 ); return( -1 );
#ifdef DEBUG #ifdef DEBUG
printf( "im_vips2jpeg: attaching %d bytes of EXIF\n", printf( "im_vips2jpeg: attaching %zd bytes of EXIF\n",
data_length ); data_length );
#endif /*DEBUG*/ #endif /*DEBUG*/
@ -425,6 +427,31 @@ write_exif( Write *write )
return( 0 ); return( 0 );
} }
static int
write_xmp( Write *write )
{
unsigned char *data;
size_t data_length;
/* No libexif ... just copy the embedded EXIF over.
*/
if( im_header_get_typeof( write->in, VIPS_META_XMP_NAME ) ) {
if( im_meta_get_blob( write->in, VIPS_META_XMP_NAME,
(void *) &data, &data_length ) )
return( -1 );
#ifdef DEBUG
printf( "im_vips2jpeg: attaching %zd bytes of XMP\n",
data_length );
#endif /*DEBUG*/
jpeg_write_marker( &write->cinfo, JPEG_APP0 + 1,
data, data_length );
}
return( 0 );
}
/* ICC writer from lcms, slight tweaks. /* ICC writer from lcms, slight tweaks.
*/ */
@ -629,6 +656,9 @@ write_vips( Write *write, int qfac, const char *profile )
if( write_exif( write ) ) if( write_exif( write ) )
return( -1 ); return( -1 );
if( write_xmp( write ) )
return( -1 );
/* A profile supplied as an argument overrides an embedded profile. /* A profile supplied as an argument overrides an embedded profile.
* "none" means don't attach a profile. * "none" means don't attach a profile.
*/ */

View File

@ -44,6 +44,13 @@ extern "C" {
*/ */
#define VIPS_META_EXIF_NAME "exif-data" #define VIPS_META_EXIF_NAME "exif-data"
/**
* VIPS_META_XMP_NAME:
*
* The name that JPEG read and write operations use for the image's XMP data.
*/
#define VIPS_META_XMP_NAME "xmp-data"
/** /**
* VIPS_META_ICC_NAME: * VIPS_META_ICC_NAME:
* *