diff --git a/ChangeLog b/ChangeLog index 9da7707c..39ebc554 100644 --- a/ChangeLog +++ b/ChangeLog @@ -20,10 +20,10 @@ - improve vips_sink_screen() stability under heavy load - added vips_arrayjoin() - Python x.bandjoin(y) is now x.ibandjoin(y), sorry -- oop, removed a DEBUG from buffer.c, vips is 30% faster - faster and lower-mem TIFF read - faster bilinear interpolator - TIFF loads and saves IMAGEDESCRIPTION +- add --properties flag to tiffsave 7/5/15 started 8.1.1 - oop, vips-8.0 wrapper script should be vips-8.1, thanks Danilo diff --git a/libvips/foreign/dzsave.c b/libvips/foreign/dzsave.c index 39f2a1ca..60794451 100644 --- a/libvips/foreign/dzsave.c +++ b/libvips/foreign/dzsave.c @@ -747,12 +747,18 @@ write_blank( VipsForeignSaveDz *dz ) return( 0 ); } +/* Track this during property save. + */ +typedef struct _WriteInfo { + const char *domain; + VipsImage *image; + xmlNode *node; +} WriteInfo; + static int -set_prop( VipsForeignSaveDz *dz, +set_prop( WriteInfo *info, xmlNode *node, const char *name, const char *fmt, ... ) { - VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( dz ); - va_list ap; char value[1024]; @@ -761,7 +767,7 @@ set_prop( VipsForeignSaveDz *dz, va_end( ap ); if( !xmlSetProp( node, (xmlChar *) name, (xmlChar *) value ) ) { - vips_error( class->nickname, + vips_error( info->domain, _( "unable to set property \"%s\" to value \"%s\"." ), name, value ); return( -1 ); @@ -771,36 +777,24 @@ set_prop( VipsForeignSaveDz *dz, } static xmlNode * -new_child( VipsForeignSaveDz *dz, xmlNode *parent, const char *name ) +new_child( WriteInfo *info, xmlNode *parent, const char *name ) { - VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( dz ); - xmlNode *child; - if( !(child = xmlNewChild( parent, NULL, - (xmlChar *) name, NULL )) ) { - vips_error( class->nickname, - _( "unable to set create node \"%s\"" ), - name ); + if( !(child = xmlNewChild( parent, NULL, (xmlChar *) name, NULL )) ) { + vips_error( info->domain, + _( "unable to set create node \"%s\"" ), name ); return( NULL ); } return( child ); } -/* Track this during a property save. - */ -typedef struct _WriteInfo { - VipsForeignSaveDz *dz; - xmlNode *node; -} WriteInfo; - static void * write_vips_property( VipsImage *image, const char *field, GValue *value, void *a ) { WriteInfo *info = (WriteInfo *) a; - VipsForeignSaveDz *dz = info->dz; GType type = G_VALUE_TYPE( value ); if( g_value_type_transformable( type, VIPS_TYPE_SAVE_STRING ) ) { @@ -811,15 +805,15 @@ write_vips_property( VipsImage *image, g_value_init( &save_value, VIPS_TYPE_SAVE_STRING ); g_value_transform( value, &save_value ); - if( !(property = new_child( dz, info->node, "property" )) ) + if( !(property = new_child( info, info->node, "property" )) ) return( image ); - if( !(child = new_child( dz, property, "name" )) ) + if( !(child = new_child( info, property, "name" )) ) return( image ); xmlNodeSetContent( child, (xmlChar *) field ); - if( !(child = new_child( dz, property, "value" )) || - set_prop( dz, child, "type", g_type_name( type ) ) ) + if( !(child = new_child( info, property, "value" )) || + set_prop( info, child, "type", g_type_name( type ) ) ) return( image ); xmlNodeSetContent( child, (xmlChar *) vips_value_get_save_string( &save_value ) ); @@ -828,71 +822,78 @@ write_vips_property( VipsImage *image, return( NULL ); } -static int -write_vips_properties( VipsForeignSaveDz *dz, xmlNode *node ) +/* Pack up all the metadata from an image as XML. This called from vips2tiff + * as well. + * + * Free the result with xmlFree(). + */ +char * +vips__make_xml_metadata( const char *domain, VipsImage *image ) { - VipsForeignSave *save = (VipsForeignSave *) dz; - - xmlNode *this; + xmlDoc *doc; + GTimeVal now; + char *date; WriteInfo info; + char *dump; + int dump_size; - if( !(this = new_child( dz, node, "properties" )) ) - return( -1 ); - info.dz = dz; - info.node = this; - if( vips_image_map( save->ready, write_vips_property, &info ) ) - return( -1 ); + if( !(doc = xmlNewDoc( (xmlChar *) "1.0" )) ) { + vips_error( domain, "%s", _( "xml save error" ) ); + return( NULL ); + } + if( !(doc->children = xmlNewDocNode( doc, NULL, + (xmlChar *) "image", NULL )) ) { + vips_error( domain, "%s", _( "xml save error" ) ); + xmlFreeDoc( doc ); + return( NULL ); + } - return( 0 ); + info.domain = domain; + info.image = image; + g_get_current_time( &now ); + date = g_time_val_to_iso8601( &now ); + if( set_prop( &info, doc->children, "xmlns", + "http://www.vips.ecs.soton.ac.uk/dzsave" ) || + set_prop( &info, doc->children, "date", date ) || + set_prop( &info, doc->children, "version", VIPS_VERSION ) ) { + g_free( date ); + xmlFreeDoc( doc ); + return( NULL ); + } + g_free( date ); + + if( !(info.node = new_child( &info, doc->children, "properties" )) || + vips_image_map( image, write_vips_property, &info ) ) { + xmlFreeDoc( doc ); + return( NULL ); + } + + xmlDocDumpFormatMemory( doc, (xmlChar **) &dump, &dump_size, 1 ); + if( !dump ) { + vips_error( domain, "%s", _( "xml save error" ) ); + xmlFreeDoc( doc ); + return( NULL ); + } + xmlFreeDoc( doc ); + + return( dump ); } static int write_vips_meta( VipsForeignSaveDz *dz ) { + VipsForeignSave *save = (VipsForeignSave *) dz; VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( dz ); - xmlDoc *doc; - GTimeVal now; - char *date; char *dump; - int dump_size; GsfOutput *out; - if( !(doc = xmlNewDoc( (xmlChar *) "1.0" )) ) { - vips_error( class->nickname, "%s", _( "xml save error" ) ); - return( -1 ); - } - if( !(doc->children = xmlNewDocNode( doc, NULL, - (xmlChar *) "image", NULL )) ) { - vips_error( class->nickname, "%s", _( "xml save error" ) ); - xmlFreeDoc( doc ); - return( -1 ); - } - - g_get_current_time( &now ); - date = g_time_val_to_iso8601( &now ); - if( set_prop( dz, doc->children, "xmlns", - "http://www.vips.ecs.soton.ac.uk/dzsave" ) || - set_prop( dz, doc->children, "date", date ) || - set_prop( dz, doc->children, "version", VIPS_VERSION ) || - write_vips_properties( dz, doc->children ) ) { - g_free( date ); - xmlFreeDoc( doc ); + if( !(dump = vips__make_xml_metadata( class->nickname, save->ready )) ) return( -1 ); - } - g_free( date ); - - xmlDocDumpFormatMemory( doc, (xmlChar **) &dump, &dump_size, 1 ); - if( !dump ) { - vips_error( class->nickname, "%s", _( "xml save error" ) ); - xmlFreeDoc( doc ); - return( -1 ); - } - xmlFreeDoc( doc ); out = vips_gsf_path( dz->tree, "vips-properties.xml", dz->root_name, NULL ); - gsf_output_write( out, dump_size, (guchar *) dump ); + gsf_output_write( out, strlen( dump ), (guchar *) dump ); (void) gsf_output_close( out ); g_object_unref( out ); diff --git a/libvips/foreign/tiff.h b/libvips/foreign/tiff.h index 06ae580e..aae3f3a3 100644 --- a/libvips/foreign/tiff.h +++ b/libvips/foreign/tiff.h @@ -49,7 +49,8 @@ int vips__tiff_write( VipsImage *in, const char *filename, gboolean miniswhite, VipsForeignTiffResunit resunit, double xres, double yres, gboolean bigtiff, - gboolean rgbjpeg ); + gboolean rgbjpeg, + gboolean properties ); int vips__tiff_read_header( const char *filename, VipsImage *out, int page ); int vips__tiff_read( const char *filename, VipsImage *out, diff --git a/libvips/foreign/tiffsave.c b/libvips/foreign/tiffsave.c index af53a436..50e8444d 100644 --- a/libvips/foreign/tiffsave.c +++ b/libvips/foreign/tiffsave.c @@ -6,6 +6,8 @@ * - argh xres/yres macro was wrong * 26/1/14 * - add rgbjpeg flag + * 21/12/15 + * - add properties flag */ /* @@ -79,6 +81,7 @@ typedef struct _VipsForeignSaveTiff { double yres; gboolean bigtiff; gboolean rgbjpeg; + gboolean properties; } VipsForeignSaveTiff; typedef VipsForeignSaveClass VipsForeignSaveTiffClass; @@ -129,7 +132,8 @@ vips_foreign_save_tiff_build( VipsObject *object ) tiff->miniswhite, tiff->resunit, tiff->xres, tiff->yres, tiff->bigtiff, - tiff->rgbjpeg ) ) + tiff->rgbjpeg, + tiff->properties ) ) return( -1 ); return( 0 ); @@ -268,6 +272,14 @@ vips_foreign_save_tiff_class_init( VipsForeignSaveTiffClass *class ) VIPS_ARGUMENT_OPTIONAL_INPUT | VIPS_ARGUMENT_DEPRECATED, G_STRUCT_OFFSET( VipsForeignSaveTiff, rgbjpeg ), FALSE ); + + VIPS_ARG_BOOL( class, "properties", 21, + _( "Properties" ), + _( "Write a properties document to IMAGEDESCRIPTION" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsForeignSaveTiff, properties ), + FALSE ); + } static void diff --git a/libvips/foreign/vips2tiff.c b/libvips/foreign/vips2tiff.c index a21e74df..6e539c1c 100644 --- a/libvips/foreign/vips2tiff.c +++ b/libvips/foreign/vips2tiff.c @@ -207,6 +207,7 @@ #include #endif /*HAVE_UNISTD_H*/ #include +#include #include #include @@ -276,6 +277,7 @@ struct _Write { char *icc_profile; /* Profile to embed */ int bigtiff; /* True for bigtiff write */ int rgbjpeg; /* True for RGB not YCbCr */ + int properties; /* Set to save XML props */ }; /* Open TIFF for output. @@ -510,19 +512,31 @@ write_embed_photoshop( Write *write, TIFF *tif ) return( 0 ); } -/* Set IMAGEDESCRIPTION, if it's there. +/* Set IMAGEDESCRIPTION, if it's there. If @properties is TRUE, set from + * vips' metadata. */ static int write_embed_imagedescription( Write *write, TIFF *tif ) { - const char *imagedescription; + if( write->properties ) { + char *doc; - if( !vips_image_get_typeof( write->im, VIPS_META_IMAGEDESCRIPTION ) ) - return( 0 ); - if( vips_image_get_string( write->im, - VIPS_META_IMAGEDESCRIPTION, &imagedescription ) ) - return( -1 ); - TIFFSetField( tif, TIFFTAG_IMAGEDESCRIPTION, imagedescription ); + if( !(doc = vips__make_xml_metadata( "vips2tiff", write->im )) ) + return( -1 ); + TIFFSetField( tif, TIFFTAG_IMAGEDESCRIPTION, doc ); + xmlFree( doc ); + } + else { + const char *imagedescription; + + if( !vips_image_get_typeof( write->im, + VIPS_META_IMAGEDESCRIPTION ) ) + return( 0 ); + if( vips_image_get_string( write->im, + VIPS_META_IMAGEDESCRIPTION, &imagedescription ) ) + return( -1 ); + TIFFSetField( tif, TIFFTAG_IMAGEDESCRIPTION, imagedescription ); + } #ifdef DEBUG printf( "vips2tiff: attached imagedescription from meta\n" ); @@ -854,7 +868,8 @@ write_new( VipsImage *im, const char *filename, gboolean miniswhite, VipsForeignTiffResunit resunit, double xres, double yres, gboolean bigtiff, - gboolean rgbjpeg ) + gboolean rgbjpeg, + gboolean properties ) { Write *write; @@ -876,6 +891,7 @@ write_new( VipsImage *im, const char *filename, write->icc_profile = profile; write->bigtiff = bigtiff; write->rgbjpeg = rgbjpeg; + write->properties = properties; write->resunit = get_resunit( resunit ); write->xres = xres; @@ -1589,7 +1605,8 @@ vips__tiff_write( VipsImage *in, const char *filename, gboolean miniswhite, VipsForeignTiffResunit resunit, double xres, double yres, gboolean bigtiff, - gboolean rgbjpeg ) + gboolean rgbjpeg, + gboolean properties ) { Write *write; @@ -1607,7 +1624,8 @@ vips__tiff_write( VipsImage *in, const char *filename, if( !(write = write_new( in, filename, compression, Q, predictor, profile, tile, tile_width, tile_height, pyramid, squash, - miniswhite, resunit, xres, yres, bigtiff, rgbjpeg )) ) + miniswhite, resunit, xres, yres, bigtiff, rgbjpeg, + properties )) ) return( -1 ); if( vips_sink_disc( write->im, write_strip, write ) ) { diff --git a/libvips/include/vips/internal.h b/libvips/include/vips/internal.h index aaae7e2d..31bc6123 100644 --- a/libvips/include/vips/internal.h +++ b/libvips/include/vips/internal.h @@ -229,6 +229,8 @@ int vips_check_bands_3ormore( const char *domain, VipsImage *im ); int vips__byteswap_bool( VipsImage *in, VipsImage **out, gboolean swap ); +char *vips__make_xml_metadata( const char *domain, VipsImage *image ); + #ifdef __cplusplus } #endif /*__cplusplus*/