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
- 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

View File

@ -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 );

View File

@ -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,

View File

@ -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

View File

@ -207,6 +207,7 @@
#include <unistd.h>
#endif /*HAVE_UNISTD_H*/
#include <string.h>
#include <libxml/parser.h>
#include <vips/vips.h>
#include <vips/internal.h>
@ -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 ) ) {

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 );
char *vips__make_xml_metadata( const char *domain, VipsImage *image );
#ifdef __cplusplus
}
#endif /*__cplusplus*/