add @properties flag to tiffsave

writes all vips metadata to imagedescription tag
This commit is contained in:
John Cupitt 2015-12-21 14:43:59 +00:00
parent 0deb640bc4
commit 73436ddfc2
6 changed files with 119 additions and 85 deletions

View File

@ -20,10 +20,10 @@
- improve vips_sink_screen() stability under heavy load - improve vips_sink_screen() stability under heavy load
- added vips_arrayjoin() - added vips_arrayjoin()
- Python x.bandjoin(y) is now x.ibandjoin(y), sorry - 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 and lower-mem TIFF read
- faster bilinear interpolator - faster bilinear interpolator
- TIFF loads and saves IMAGEDESCRIPTION - TIFF loads and saves IMAGEDESCRIPTION
- add --properties flag to tiffsave
7/5/15 started 8.1.1 7/5/15 started 8.1.1
- oop, vips-8.0 wrapper script should be vips-8.1, thanks Danilo - oop, vips-8.0 wrapper script should be vips-8.1, thanks Danilo

View File

@ -747,12 +747,18 @@ write_blank( VipsForeignSaveDz *dz )
return( 0 ); return( 0 );
} }
/* Track this during property save.
*/
typedef struct _WriteInfo {
const char *domain;
VipsImage *image;
xmlNode *node;
} WriteInfo;
static int static int
set_prop( VipsForeignSaveDz *dz, set_prop( WriteInfo *info,
xmlNode *node, const char *name, const char *fmt, ... ) xmlNode *node, const char *name, const char *fmt, ... )
{ {
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( dz );
va_list ap; va_list ap;
char value[1024]; char value[1024];
@ -761,7 +767,7 @@ set_prop( VipsForeignSaveDz *dz,
va_end( ap ); va_end( ap );
if( !xmlSetProp( node, (xmlChar *) name, (xmlChar *) value ) ) { if( !xmlSetProp( node, (xmlChar *) name, (xmlChar *) value ) ) {
vips_error( class->nickname, vips_error( info->domain,
_( "unable to set property \"%s\" to value \"%s\"." ), _( "unable to set property \"%s\" to value \"%s\"." ),
name, value ); name, value );
return( -1 ); return( -1 );
@ -771,36 +777,24 @@ set_prop( VipsForeignSaveDz *dz,
} }
static xmlNode * 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; xmlNode *child;
if( !(child = xmlNewChild( parent, NULL, if( !(child = xmlNewChild( parent, NULL, (xmlChar *) name, NULL )) ) {
(xmlChar *) name, NULL )) ) { vips_error( info->domain,
vips_error( class->nickname, _( "unable to set create node \"%s\"" ), name );
_( "unable to set create node \"%s\"" ),
name );
return( NULL ); return( NULL );
} }
return( child ); return( child );
} }
/* Track this during a property save.
*/
typedef struct _WriteInfo {
VipsForeignSaveDz *dz;
xmlNode *node;
} WriteInfo;
static void * static void *
write_vips_property( VipsImage *image, write_vips_property( VipsImage *image,
const char *field, GValue *value, void *a ) const char *field, GValue *value, void *a )
{ {
WriteInfo *info = (WriteInfo *) a; WriteInfo *info = (WriteInfo *) a;
VipsForeignSaveDz *dz = info->dz;
GType type = G_VALUE_TYPE( value ); GType type = G_VALUE_TYPE( value );
if( g_value_type_transformable( type, VIPS_TYPE_SAVE_STRING ) ) { 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_init( &save_value, VIPS_TYPE_SAVE_STRING );
g_value_transform( value, &save_value ); g_value_transform( value, &save_value );
if( !(property = new_child( dz, info->node, "property" )) ) if( !(property = new_child( info, info->node, "property" )) )
return( image ); return( image );
if( !(child = new_child( dz, property, "name" )) ) if( !(child = new_child( info, property, "name" )) )
return( image ); return( image );
xmlNodeSetContent( child, (xmlChar *) field ); xmlNodeSetContent( child, (xmlChar *) field );
if( !(child = new_child( dz, property, "value" )) || if( !(child = new_child( info, property, "value" )) ||
set_prop( dz, child, "type", g_type_name( type ) ) ) set_prop( info, child, "type", g_type_name( type ) ) )
return( image ); return( image );
xmlNodeSetContent( child, xmlNodeSetContent( child,
(xmlChar *) vips_value_get_save_string( &save_value ) ); (xmlChar *) vips_value_get_save_string( &save_value ) );
@ -828,71 +822,78 @@ write_vips_property( VipsImage *image,
return( NULL ); return( NULL );
} }
static int /* Pack up all the metadata from an image as XML. This called from vips2tiff
write_vips_properties( VipsForeignSaveDz *dz, xmlNode *node ) * as well.
*
* Free the result with xmlFree().
*/
char *
vips__make_xml_metadata( const char *domain, VipsImage *image )
{ {
VipsForeignSave *save = (VipsForeignSave *) dz; xmlDoc *doc;
GTimeVal now;
xmlNode *this; char *date;
WriteInfo info; WriteInfo info;
char *dump;
int dump_size;
if( !(this = new_child( dz, node, "properties" )) ) if( !(doc = xmlNewDoc( (xmlChar *) "1.0" )) ) {
return( -1 ); vips_error( domain, "%s", _( "xml save error" ) );
info.dz = dz; return( NULL );
info.node = this; }
if( vips_image_map( save->ready, write_vips_property, &info ) ) if( !(doc->children = xmlNewDocNode( doc, NULL,
return( -1 ); (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 static int
write_vips_meta( VipsForeignSaveDz *dz ) write_vips_meta( VipsForeignSaveDz *dz )
{ {
VipsForeignSave *save = (VipsForeignSave *) dz;
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( dz ); VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( dz );
xmlDoc *doc;
GTimeVal now;
char *date;
char *dump; char *dump;
int dump_size;
GsfOutput *out; GsfOutput *out;
if( !(doc = xmlNewDoc( (xmlChar *) "1.0" )) ) { if( !(dump = vips__make_xml_metadata( class->nickname, save->ready )) )
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 );
return( -1 ); 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, out = vips_gsf_path( dz->tree,
"vips-properties.xml", dz->root_name, NULL ); "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 ); (void) gsf_output_close( out );
g_object_unref( out ); g_object_unref( out );

View File

@ -49,7 +49,8 @@ int vips__tiff_write( VipsImage *in, const char *filename,
gboolean miniswhite, gboolean miniswhite,
VipsForeignTiffResunit resunit, double xres, double yres, VipsForeignTiffResunit resunit, double xres, double yres,
gboolean bigtiff, gboolean bigtiff,
gboolean rgbjpeg ); gboolean rgbjpeg,
gboolean properties );
int vips__tiff_read_header( const char *filename, VipsImage *out, int page ); int vips__tiff_read_header( const char *filename, VipsImage *out, int page );
int vips__tiff_read( const char *filename, VipsImage *out, int vips__tiff_read( const char *filename, VipsImage *out,

View File

@ -6,6 +6,8 @@
* - argh xres/yres macro was wrong * - argh xres/yres macro was wrong
* 26/1/14 * 26/1/14
* - add rgbjpeg flag * - add rgbjpeg flag
* 21/12/15
* - add properties flag
*/ */
/* /*
@ -79,6 +81,7 @@ typedef struct _VipsForeignSaveTiff {
double yres; double yres;
gboolean bigtiff; gboolean bigtiff;
gboolean rgbjpeg; gboolean rgbjpeg;
gboolean properties;
} VipsForeignSaveTiff; } VipsForeignSaveTiff;
typedef VipsForeignSaveClass VipsForeignSaveTiffClass; typedef VipsForeignSaveClass VipsForeignSaveTiffClass;
@ -129,7 +132,8 @@ vips_foreign_save_tiff_build( VipsObject *object )
tiff->miniswhite, tiff->miniswhite,
tiff->resunit, tiff->xres, tiff->yres, tiff->resunit, tiff->xres, tiff->yres,
tiff->bigtiff, tiff->bigtiff,
tiff->rgbjpeg ) ) tiff->rgbjpeg,
tiff->properties ) )
return( -1 ); return( -1 );
return( 0 ); return( 0 );
@ -268,6 +272,14 @@ vips_foreign_save_tiff_class_init( VipsForeignSaveTiffClass *class )
VIPS_ARGUMENT_OPTIONAL_INPUT | VIPS_ARGUMENT_DEPRECATED, VIPS_ARGUMENT_OPTIONAL_INPUT | VIPS_ARGUMENT_DEPRECATED,
G_STRUCT_OFFSET( VipsForeignSaveTiff, rgbjpeg ), G_STRUCT_OFFSET( VipsForeignSaveTiff, rgbjpeg ),
FALSE ); 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 static void

View File

@ -207,6 +207,7 @@
#include <unistd.h> #include <unistd.h>
#endif /*HAVE_UNISTD_H*/ #endif /*HAVE_UNISTD_H*/
#include <string.h> #include <string.h>
#include <libxml/parser.h>
#include <vips/vips.h> #include <vips/vips.h>
#include <vips/internal.h> #include <vips/internal.h>
@ -276,6 +277,7 @@ struct _Write {
char *icc_profile; /* Profile to embed */ char *icc_profile; /* Profile to embed */
int bigtiff; /* True for bigtiff write */ int bigtiff; /* True for bigtiff write */
int rgbjpeg; /* True for RGB not YCbCr */ int rgbjpeg; /* True for RGB not YCbCr */
int properties; /* Set to save XML props */
}; };
/* Open TIFF for output. /* Open TIFF for output.
@ -510,19 +512,31 @@ write_embed_photoshop( Write *write, TIFF *tif )
return( 0 ); return( 0 );
} }
/* Set IMAGEDESCRIPTION, if it's there. /* Set IMAGEDESCRIPTION, if it's there. If @properties is TRUE, set from
* vips' metadata.
*/ */
static int static int
write_embed_imagedescription( Write *write, TIFF *tif ) 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 ) ) if( !(doc = vips__make_xml_metadata( "vips2tiff", write->im )) )
return( 0 ); return( -1 );
if( vips_image_get_string( write->im, TIFFSetField( tif, TIFFTAG_IMAGEDESCRIPTION, doc );
VIPS_META_IMAGEDESCRIPTION, &imagedescription ) ) xmlFree( doc );
return( -1 ); }
TIFFSetField( tif, TIFFTAG_IMAGEDESCRIPTION, imagedescription ); 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 #ifdef DEBUG
printf( "vips2tiff: attached imagedescription from meta\n" ); printf( "vips2tiff: attached imagedescription from meta\n" );
@ -854,7 +868,8 @@ write_new( VipsImage *im, const char *filename,
gboolean miniswhite, gboolean miniswhite,
VipsForeignTiffResunit resunit, double xres, double yres, VipsForeignTiffResunit resunit, double xres, double yres,
gboolean bigtiff, gboolean bigtiff,
gboolean rgbjpeg ) gboolean rgbjpeg,
gboolean properties )
{ {
Write *write; Write *write;
@ -876,6 +891,7 @@ write_new( VipsImage *im, const char *filename,
write->icc_profile = profile; write->icc_profile = profile;
write->bigtiff = bigtiff; write->bigtiff = bigtiff;
write->rgbjpeg = rgbjpeg; write->rgbjpeg = rgbjpeg;
write->properties = properties;
write->resunit = get_resunit( resunit ); write->resunit = get_resunit( resunit );
write->xres = xres; write->xres = xres;
@ -1589,7 +1605,8 @@ vips__tiff_write( VipsImage *in, const char *filename,
gboolean miniswhite, gboolean miniswhite,
VipsForeignTiffResunit resunit, double xres, double yres, VipsForeignTiffResunit resunit, double xres, double yres,
gboolean bigtiff, gboolean bigtiff,
gboolean rgbjpeg ) gboolean rgbjpeg,
gboolean properties )
{ {
Write *write; Write *write;
@ -1607,7 +1624,8 @@ vips__tiff_write( VipsImage *in, const char *filename,
if( !(write = write_new( in, filename, if( !(write = write_new( in, filename,
compression, Q, predictor, profile, compression, Q, predictor, profile,
tile, tile_width, tile_height, pyramid, squash, tile, tile_width, tile_height, pyramid, squash,
miniswhite, resunit, xres, yres, bigtiff, rgbjpeg )) ) miniswhite, resunit, xres, yres, bigtiff, rgbjpeg,
properties )) )
return( -1 ); return( -1 );
if( vips_sink_disc( write->im, write_strip, write ) ) { if( vips_sink_disc( write->im, write_strip, write ) ) {

View File

@ -229,6 +229,8 @@ int vips_check_bands_3ormore( const char *domain, VipsImage *im );
int vips__byteswap_bool( VipsImage *in, VipsImage **out, gboolean swap ); int vips__byteswap_bool( VipsImage *in, VipsImage **out, gboolean swap );
char *vips__make_xml_metadata( const char *domain, VipsImage *image );
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif /*__cplusplus*/ #endif /*__cplusplus*/