Merge branch 'try-expat'

This commit is contained in:
John Cupitt 2017-02-28 17:19:21 +00:00
commit 47a4a78a33
19 changed files with 926 additions and 837 deletions

View File

@ -32,6 +32,8 @@
- max/min sort values by y and x coordinate
- tiff read uses libtiff scanline API if rows-per-strip is large
- vips_region_shrink() knows about alpha, helps dzsave and tiffsave
- use expat, not libxml, for XML load ... removes a required dependency, since
we get expat as part of glib
8/12/16 started 8.4.5
- allow libgsf-1.14.26 to help centos, thanks tdiprima

View File

@ -46,8 +46,7 @@ Untar, then in the libvips directory you should just be able to do:
$ ./configure
Check the summary at the end of `configure` carefully.
libvips must have `build-essential`, `pkg-config`, `glib2.0-dev`, and
`libxml2-dev`.
libvips must have `build-essential`, `pkg-config`, `glib2.0-dev`.
For the vips8 Python binding, you must have
`gobject-introspection`, `python-gi-dev`, and `libgirepository1.0-dev`.
@ -145,8 +144,7 @@ Static analysis with:
# Dependencies
libvips has to have `gettext`, `glib2.0-dev` and `libxml2-dev`. Other
dependencies are optional, see below.
libvips has to have `glib2.0-dev`. Other dependencies are optional, see below.
# Optional dependencies

14
TODO
View File

@ -116,20 +116,6 @@ DEBUG:gi.overrides.Vips:assigning sequential
output imagevec
output blob
- fix up aconv
- rewrite im_conv() etc. as vips_conv(), also the mosaicing functions
finally finish --disable-deprecated option
- can we use postbuild elsewhere? look at use of "preclose" / "written", etc.
- test draw_mask on labq images
we probably need to unpack the ink back to double before blending

View File

@ -348,8 +348,8 @@ AC_CHECK_LIB(m,atan2,[AC_DEFINE(HAVE_ATAN2,1,[have atan2() in libm.])])
# have to have these
# need glib 2.6 for GOption
PKG_CHECK_MODULES(REQUIRED, glib-2.0 >= 2.6 gmodule-2.0 libxml-2.0 gobject-2.0)
PACKAGES_USED="$PACKAGES_USED glib-2.0 libxml-2.0 gmodule-2.0 gobject-2.0"
PKG_CHECK_MODULES(REQUIRED, glib-2.0 >= 2.6 gmodule-2.0 gobject-2.0 expat)
PACKAGES_USED="$PACKAGES_USED glib-2.0 gmodule-2.0 gobject-2.0 expat"
# after 2.28 we have a monotonic timer
PKG_CHECK_MODULES(MONOTONIC, glib-2.0 >= 2.28,

View File

@ -152,144 +152,9 @@
#include <stdlib.h>
#include <string.h>
#include <libxml/parser.h>
#include <vips/vips.h>
#include <vips/internal.h>
/* Track this during property save.
*/
typedef struct _WriteInfo {
const char *domain;
VipsImage *image;
xmlNode *node;
} WriteInfo;
static int
set_prop( WriteInfo *info,
xmlNode *node, const char *name, const char *fmt, ... )
{
va_list ap;
char value[1024];
va_start( ap, fmt );
(void) vips_vsnprintf( value, 1024, fmt, ap );
va_end( ap );
if( !xmlSetProp( node, (xmlChar *) name, (xmlChar *) value ) ) {
vips_error( info->domain,
_( "unable to set property \"%s\" to value \"%s\"." ),
name, value );
return( -1 );
}
return( 0 );
}
static xmlNode *
new_child( WriteInfo *info, xmlNode *parent, const char *name )
{
xmlNode *child;
if( !(child = xmlNewChild( parent, NULL, (xmlChar *) name, NULL )) ) {
vips_error( info->domain,
_( "unable to set create node \"%s\"" ), name );
return( NULL );
}
return( child );
}
static void *
write_vips_property( VipsImage *image,
const char *field, GValue *value, void *a )
{
WriteInfo *info = (WriteInfo *) a;
GType type = G_VALUE_TYPE( value );
if( g_value_type_transformable( type, VIPS_TYPE_SAVE_STRING ) ) {
GValue save_value = { 0 };
xmlNode *property;
xmlNode *child;
g_value_init( &save_value, VIPS_TYPE_SAVE_STRING );
if( !g_value_transform( value, &save_value ) )
return( image );
if( !(property = new_child( info, info->node, "property" )) )
return( image );
if( !(child = new_child( info, property, "name" )) )
return( image );
xmlNodeSetContent( child, (xmlChar *) field );
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 ) );
}
return( NULL );
}
/* 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 )
{
xmlDoc *doc;
GTimeVal now;
char *date;
WriteInfo info;
char *dump;
int dump_size;
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 );
}
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 );
}
#ifdef HAVE_GSF
#include <gsf/gsf.h>
@ -980,12 +845,11 @@ static int
write_vips_meta( VipsForeignSaveDz *dz )
{
VipsForeignSave *save = (VipsForeignSave *) dz;
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( dz );
char *dump;
GsfOutput *out;
if( !(dump = vips__make_xml_metadata( class->nickname, save->ready )) )
if( !(dump = vips__xml_properties( save->ready )) )
return( -1 );
/* For deepzom the props must go inside the ${name}_files subdir, for
@ -1001,7 +865,7 @@ write_vips_meta( VipsForeignSaveDz *dz )
(void) gsf_output_close( out );
g_object_unref( out );
xmlFree( dump );
g_free( dump );
return( 0 );
}

View File

@ -17,6 +17,8 @@
* readers
* 23/5/16
* - add buffer save functions
* 28/2/17
* - use dbuf for buffer output
*/
/*
@ -1175,9 +1177,7 @@ typedef struct {
char *filename;
FILE *fout;
char *buf;
size_t len;
size_t alloc;
VipsDbuf dbuf;
char format[256];
double expos;
@ -1192,7 +1192,7 @@ write_destroy( Write *write )
{
VIPS_FREE( write->filename );
VIPS_FREEF( fclose, write->fout );
VIPS_FREE( write->buf );
vips_dbuf_destroy( &write->dbuf );
vips_free( write );
}
@ -1211,9 +1211,7 @@ write_new( VipsImage *in )
write->filename = NULL;
write->fout = NULL;
write->buf = NULL;
write->len = 0;
write->alloc = 0;
vips_dbuf_init( &write->dbuf );
strcpy( write->format, COLRFMT );
write->expos = 1.0;
@ -1349,91 +1347,30 @@ vips__rad_save( VipsImage *in, const char *filename )
return( 0 );
}
static void
write_buf_grow( Write *write, size_t grow_len )
{
size_t new_len = write->len + grow_len;
if( new_len > write->alloc ) {
size_t proposed_alloc = (16 + write->alloc) * 3 / 2;
write->alloc = VIPS_MAX( proposed_alloc, new_len );
/* Our caller must free with g_free(), so we must use
* g_realloc().
*/
write->buf = g_realloc( write->buf, write->alloc );
VIPS_DEBUG_MSG( "write_buf_grow: grown to %zd bytes\n",
write->alloc );
}
}
static void
bprintf( Write *write, const char *fmt, ... )
{
int length;
char *write_start;
va_list ap;
/* Determine required size.
*/
va_start( ap, fmt );
length = vsnprintf( NULL, 0, fmt, ap );
va_end( ap );
write_buf_grow( write, length + 1 );
write_start = write->buf + write->len;
va_start( ap, fmt );
length = vsnprintf( write_start, length + 1, fmt, ap );
va_end( ap );
write->len += length;
g_assert( write->len <= write->alloc );
}
#define bputformat( write, s ) \
bprintf( write, "%s%s\n", FMTSTR, s )
#define bputexpos( write, ex ) \
bprintf( write, "%s%e\n", EXPOSSTR, ex )
#define bputcolcor( write, cc ) \
bprintf( write, "%s %f %f %f\n", \
COLCORSTR, (cc)[RED], (cc)[GRN], (cc)[BLU] )
#define bputaspect( write, pa ) \
bprintf( write, "%s%f\n", ASPECTSTR, pa )
#define bputprims( write, p ) \
bprintf( write, "%s %.4f %.4f %.4f %.4f %.4f %.4f %.4f %.4f\n", \
PRIMARYSTR, \
(p)[RED][CIEX], (p)[RED][CIEY], \
(p)[GRN][CIEX], (p)[GRN][CIEY], \
(p)[BLU][CIEX], (p)[BLU][CIEY], \
(p)[WHT][CIEX], (p)[WHT][CIEY] )
#define bputsresolu( write, rs ) \
bprintf( write, "%s", resolu2str( resolu_buf, rs ) )
static int
vips2rad_put_header_buf( Write *write )
{
vips2rad_make_header( write );
bprintf( write, "#?RADIANCE\n" );
bputformat( write, write->format );
bputexpos( write, write->expos );
bputcolcor( write, write->colcor );
bprintf( write, "SOFTWARE=vips %s\n", vips_version_string() );
bputaspect( write, write->aspect );
bputprims( write, write->prims );
bprintf( write, "\n" );
bputsresolu( write, &write->rs );
vips_dbuf_writef( &write->dbuf, "#?RADIANCE\n" );
vips_dbuf_writef( &write->dbuf, "%s%s\n", FMTSTR, write->format );
vips_dbuf_writef( &write->dbuf, "%s%e\n", EXPOSSTR, write->expos );
vips_dbuf_writef( &write->dbuf, "%s %f %f %f\n",
COLCORSTR,
write->colcor[RED], write->colcor[GRN], write->colcor[BLU] );
vips_dbuf_writef( &write->dbuf, "SOFTWARE=vips %s\n",
vips_version_string() );
vips_dbuf_writef( &write->dbuf, "%s%f\n", ASPECTSTR, write->aspect );
vips_dbuf_writef( &write->dbuf,
"%s %.4f %.4f %.4f %.4f %.4f %.4f %.4f %.4f\n",
PRIMARYSTR,
write->prims[RED][CIEX], write->prims[RED][CIEY],
write->prims[GRN][CIEX], write->prims[GRN][CIEY],
write->prims[BLU][CIEX], write->prims[BLU][CIEY],
write->prims[WHT][CIEX], write->prims[WHT][CIEY] );
vips_dbuf_writef( &write->dbuf, "\n" );
vips_dbuf_writef( &write->dbuf, "%s",
resolu2str( resolu_buf, &write->rs ) );
return( 0 );
}
@ -1444,25 +1381,25 @@ static int
scanline_write_buf( Write *write, COLR *scanline, int width )
{
unsigned char *buffer;
size_t size;
int length;
write_buf_grow( write, MAX_LINE );
buffer = (unsigned char *) write->buf + write->len;
vips_dbuf_allocate( &write->dbuf, MAX_LINE );
buffer = vips_dbuf_get_write( &write->dbuf, &size );
if( width < MINELEN ||
width > MAXELEN ) {
/* Write as a flat scanline.
*/
memcpy( buffer, scanline, sizeof( COLR ) * width );
write->len += sizeof( COLR ) * width;
length = sizeof( COLR ) * width;
memcpy( buffer, scanline, length );
}
else {
int length;
else
/* An RLE scanline.
*/
rle_scanline_write( scanline, width, buffer, &length );
write->len += length;
}
vips_dbuf_seek( &write->dbuf, length - size, SEEK_CUR );
return( 0 );
}
@ -1513,10 +1450,7 @@ vips__rad_save_buf( VipsImage *in, void **obuf, size_t *olen )
return( -1 );
}
*obuf = write->buf;
write->buf = NULL;
if( olen )
*olen = write->len;
*obuf = vips_dbuf_steal( &write->dbuf, olen );
write_destroy( write );

View File

@ -48,7 +48,6 @@
#include <unistd.h>
#endif /*HAVE_UNISTD_H*/
#include <string.h>
#include <libxml/parser.h>
#include <vips/vips.h>
#include <vips/internal.h>
@ -299,10 +298,7 @@ vips__tiff_openin_buffer( VipsImage *image, const void *data, size_t length )
*/
typedef struct _VipsTiffOpenoutBuffer {
size_t position;
void *data;
size_t allocated;
size_t length;
VipsDbuf dbuf;
/* On close, consolidate and write the output here.
*/
@ -322,37 +318,12 @@ static tsize_t
openout_buffer_write( thandle_t st, tdata_t data, tsize_t size )
{
VipsTiffOpenoutBuffer *buffer = (VipsTiffOpenoutBuffer *) st;
size_t new_length = VIPS_MAX( buffer->position + size, buffer->length );
#ifdef DEBUG
printf( "openout_buffer_write: %zd bytes\n", size );
#endif /*DEBUG*/
/* We could make a chain of blocks, but libtiff does a lot of seeking
* during writes, so we'd have to handle writes being
* split over blocks, which would be annoying.
*
* Instead, go for exponential realloc: the number of reallocs grows as
* log(final size), and we never over allocate by more than 30%.
*
* realloc can be very, very slow on Windows, but maybe g_realloc() is
* smarter.
*/
if( new_length > buffer->allocated ) {
buffer->allocated = VIPS_MAX( (16 + buffer->allocated) * 3 / 2,
new_length );
buffer->data = g_try_realloc( buffer->data, buffer->allocated );
if( buffer->data == NULL ) {
vips_error( "tiff memory write",
"%s", _( "Out of memory." ) );
return( 0 );
}
}
memcpy( buffer->data + buffer->position, data, size );
buffer->position += size;
buffer->length = new_length;
vips_dbuf_write( &buffer->dbuf, data, size );
return( size );
}
@ -362,8 +333,8 @@ openout_buffer_close( thandle_t st )
{
VipsTiffOpenoutBuffer *buffer = (VipsTiffOpenoutBuffer *) st;
*(buffer->out_data) = buffer->data;
*(buffer->out_length) = buffer->length;
*(buffer->out_data) = vips_dbuf_steal( &buffer->dbuf,
buffer->out_length);
return( 0 );
}
@ -378,16 +349,9 @@ openout_buffer_seek( thandle_t st, toff_t position, int whence )
position, whence );
#endif /*DEBUG*/
if( whence == SEEK_SET )
buffer->position = position;
else if( whence == SEEK_CUR )
buffer->position += position;
else if( whence == SEEK_END )
buffer->position = buffer->length + position;
else
g_assert_not_reached();
vips_dbuf_seek( &buffer->dbuf, position, whence );
return( buffer->position );
return( vips_dbuf_tell( &buffer->dbuf ) );
}
static toff_t
@ -430,10 +394,7 @@ vips__tiff_openout_buffer( VipsImage *image,
#endif /*DEBUG*/
buffer = VIPS_NEW( image, VipsTiffOpenoutBuffer );
buffer->position = 0;
buffer->data = NULL;
buffer->allocated = 0;
buffer->length = 0;
vips_dbuf_init( &buffer->dbuf );
buffer->out_data = out_data;
buffer->out_length = out_length;

View File

@ -82,6 +82,8 @@
* - turn off chroma subsample for Q > 90
* 7/11/16
* - move exif handling out to exif.c
* 27/2/17
* - use dbuf for memory output
*/
/*
@ -670,112 +672,6 @@ vips__jpeg_write_file( VipsImage *in,
return( 0 );
}
/* We can't predict how large the output buffer we need is, because we might
* need space for ICC profiles and stuff. So we write to a linked list of mem
* buffers and add a new one as they fill.
*/
#define BUFFER_SIZE (10000)
/* A buffer.
*/
typedef struct _Block {
j_compress_ptr cinfo;
struct _Block *first;
struct _Block *next;
JOCTET *data; /* Allocated area */
size_t size; /* Max size */
size_t used; /* How much has been used */
} Block;
static Block *
block_new( j_compress_ptr cinfo )
{
Block *block;
block = (Block *) (*cinfo->mem->alloc_large)
( (j_common_ptr) cinfo, JPOOL_IMAGE, sizeof( Block ) );
block->cinfo = cinfo;
block->first = block;
block->next = NULL;
block->data = (JOCTET *) (*cinfo->mem->alloc_large)
( (j_common_ptr) cinfo, JPOOL_IMAGE, BUFFER_SIZE );
block->size = BUFFER_SIZE;
block->used = 0;
return( block );
}
static Block *
block_last( Block *block )
{
while( block->next )
block = block->next;
return( block );
}
static Block *
block_append( Block *block )
{
Block *new;
g_assert( block );
new = block_new( block->cinfo );
new->first = block->first;
block_last( block )->next = new;
return( new );
}
static size_t
block_length( Block *block )
{
size_t len;
len = 0;
for( block = block->first; block; block = block->next )
len += block->used;
return( len );
}
static void
block_copy( Block *block, void *dest )
{
JOCTET *p;
p = dest;
for( block = block->first; block; block = block->next ) {
memcpy( p, block->data, block->used );
p += block->used;
}
}
#ifdef DEBUG
static void
block_print( Block *block )
{
int i;
printf( "total length = %zd\n", block_length( block ) );
printf( "set of blocks:\n" );
i = 0;
for( block = block->first; block; block = block->next ) {
printf( "%d) %p, first = %p, next = %p"
"\t data = %p, size = %zd, used = %zd\n",
i, block, block->first, block->next,
block->data, block->size, block->used );
i += 1;
}
}
#endif /*DEBUG*/
/* Just like the above, but we write to a memory buffer.
*
* A memory buffer for the compressed image.
@ -788,16 +684,36 @@ typedef struct {
/* Private stuff during write.
*/
/* Build the output area here in chunks.
/* Build the output area here.
*/
Block *block;
VipsDbuf dbuf;
/* Copy the compressed area here.
/* Write the generated area here.
*/
void **obuf; /* Allocated buffer, and size */
size_t *olen;
} OutputBuffer;
/* Buffer full method ... allocate a new output block. This is only called
* when the output area is exactly full.
*/
METHODDEF(boolean)
empty_output_buffer( j_compress_ptr cinfo )
{
OutputBuffer *buf = (OutputBuffer *) cinfo->dest;
size_t size;
vips_dbuf_allocate( &buf->dbuf, 10000 );
buf->pub.next_output_byte =
(JOCTET *) vips_dbuf_get_write( &buf->dbuf, &size );
buf->pub.free_in_buffer = size;
/* TRUE means we've made some more space.
*/
return( 1 );
}
/* Init dest method.
*/
METHODDEF(void)
@ -805,38 +721,8 @@ init_destination( j_compress_ptr cinfo )
{
OutputBuffer *buf = (OutputBuffer *) cinfo->dest;
/* Allocate relative to the image we are writing .. freed when we junk
* this output.
*/
buf->block = block_new( cinfo );
/* Set buf pointers for library.
*/
buf->pub.next_output_byte = buf->block->data;
buf->pub.free_in_buffer = buf->block->size;
}
/* Buffer full method ... allocate a new output block.
*/
METHODDEF(boolean)
empty_output_buffer( j_compress_ptr cinfo )
{
OutputBuffer *buf = (OutputBuffer *) cinfo->dest;
/* Record how many bytes we used. empty_output_buffer() is always
* called when the buffer is exactly full.
*/
buf->block->used = buf->block->size;
/* New block and reset.
*/
buf->block = block_append( buf->block );
buf->pub.next_output_byte = buf->block->data;
buf->pub.free_in_buffer = buf->block->size;
/* TRUE means we've made some more space.
*/
return( 1 );
vips_dbuf_init( &buf->dbuf );
empty_output_buffer( cinfo );
}
/* Cleanup. Copy the set of blocks out as a big lump.
@ -846,35 +732,17 @@ term_destination( j_compress_ptr cinfo )
{
OutputBuffer *buf = (OutputBuffer *) cinfo->dest;
size_t len;
void *obuf;
size_t size;
/* Record the number of bytes we wrote in the final buffer.
* pub.free_in_buffer is valid here.
/* We probably won't have filled the area that was last allocated in
* empty_output_buffer(). Chop the data size down to the length that
* was actually written.
*/
buf->block->used = buf->block->size - buf->pub.free_in_buffer;
vips_dbuf_seek( &buf->dbuf, -buf->pub.free_in_buffer, SEEK_END );
vips_dbuf_truncate( &buf->dbuf );
#ifdef DEBUG
block_print( buf->block );
#endif /*DEBUG*/
/* ... and we can count up our buffers now.
*/
len = block_length( buf->block );
/* Allocate and copy to the output area.
*/
if( !(obuf = vips_malloc( NULL, len )) )
ERREXIT( cinfo, JERR_FILE_WRITE );
else {
/* coverity doesn't know ERREXIT() does not return, so put
* this in an else.
*/
*(buf->obuf) = obuf;
*(buf->olen) = len;
block_copy( buf->block, obuf );
}
*(buf->obuf) = vips_dbuf_steal( &buf->dbuf, &size );
*(buf->olen) = size;
}
/* Set dest to one of our objects.

View File

@ -218,7 +218,6 @@
#include <unistd.h>
#endif /*HAVE_UNISTD_H*/
#include <string.h>
#include <libxml/parser.h>
#include <vips/vips.h>
#include <vips/internal.h>
@ -512,10 +511,10 @@ wtiff_embed_imagedescription( Wtiff *wtiff, TIFF *tif )
if( wtiff->properties ) {
char *doc;
if( !(doc = vips__make_xml_metadata( "vips2tiff", wtiff->im )) )
if( !(doc = vips__xml_properties( wtiff->im )) )
return( -1 );
TIFFSetField( tif, TIFFTAG_IMAGEDESCRIPTION, doc );
xmlFree( doc );
g_free( doc );
}
else {
const char *imagedescription;

View File

@ -59,6 +59,8 @@
* - support --strip option
* 17/1/17
* - invalidate operation on read error
* 27/2/17
* - use dbuf for buffer output
*/
/*
@ -741,10 +743,7 @@ typedef struct {
VipsImage *memory;
FILE *fp;
char *buf;
size_t len;
size_t alloc;
VipsDbuf dbuf;
png_structp pPng;
png_infop pInfo;
@ -756,7 +755,7 @@ write_finish( Write *write )
{
VIPS_FREEF( fclose, write->fp );
VIPS_UNREF( write->memory );
VIPS_FREE( write->buf );
vips_dbuf_destroy( &write->dbuf );
if( write->pPng )
png_destroy_write_struct( &write->pPng, &write->pInfo );
}
@ -778,9 +777,7 @@ write_new( VipsImage *in )
write->in = in;
write->memory = NULL;
write->fp = NULL;
write->buf = NULL;
write->len = 0;
write->alloc = 0;
vips_dbuf_init( &write->dbuf );
g_signal_connect( in, "close",
G_CALLBACK( write_destroy ), write );
@ -1021,41 +1018,12 @@ vips__png_write( VipsImage *in, const char *filename,
return( 0 );
}
static void
write_grow( Write *write, size_t grow_len )
{
size_t new_len = write->len + grow_len;
if( new_len > write->alloc ) {
size_t proposed_alloc = (16 + write->alloc) * 3 / 2;
write->alloc = VIPS_MAX( proposed_alloc, new_len );
/* Our result must be freedd with g_free(), so it's OK to use
* g_realloc().
*/
write->buf = g_realloc( write->buf, write->alloc );
VIPS_DEBUG_MSG( "write_buf_grow: grown to %zd bytes\n",
write->alloc );
}
}
static void
user_write_data( png_structp png_ptr, png_bytep data, png_size_t length )
{
Write *write = (Write *) png_get_io_ptr( png_ptr );
char *write_start;
write_grow( write, length );
write_start = write->buf + write->len;
memcpy( write_start, data, length );
write->len += length;
g_assert( write->len <= write->alloc );
vips_dbuf_write( &write->dbuf, data, length );
}
int
@ -1080,10 +1048,7 @@ vips__png_write_buf( VipsImage *in,
return( -1 );
}
*obuf = write->buf;
write->buf = NULL;
if( olen )
*olen = write->len;
*obuf = vips_dbuf_steal( &write->dbuf, olen );
write_finish( write );

View File

@ -6,6 +6,7 @@ pkginclude_HEADERS = \
deprecated.h \
arithmetic.h \
buf.h \
dbuf.h \
colour.h \
conversion.h \
convolution.h \

View File

@ -0,0 +1,83 @@
/* A dynamic memory buffer that expands as you write.
*/
/*
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
*/
#ifndef VIPS_DBUF_H
#define VIPS_DBUF_H
#ifdef __cplusplus
extern "C" {
#endif /*__cplusplus*/
#include <vips/vips.h>
/* A buffer in the process of being written to.
*/
typedef struct _VipsDbuf {
/* All fields are private.
*/
/*< private >*/
/* The current base, and the size of the allocated memory area.
*/
unsigned char *data;
size_t allocated_size;
/* The size of the actual data that's been written. This will usually
* be <= allocated_size, but always >= write_point.
*/
size_t data_size;
/* The write point.
*/
size_t write_point;
} VipsDbuf;
void vips_dbuf_destroy( VipsDbuf *buf );
void vips_dbuf_init( VipsDbuf *buf );
gboolean vips_dbuf_allocate( VipsDbuf *dbuf, size_t size );
unsigned char *vips_dbuf_get_write( VipsDbuf *dbuf, size_t *size );
gboolean vips_dbuf_write( VipsDbuf *dbuf,
const unsigned char *data, size_t size );
gboolean vips_dbuf_writef( VipsDbuf *dbuf, const char *fmt, ... );
void vips_dbuf_reset( VipsDbuf *dbuf );
void vips_dbuf_destroy( VipsDbuf *dbuf );
gboolean vips_dbuf_seek( VipsDbuf *dbuf, off_t offset, int whence );
void vips_dbuf_truncate( VipsDbuf *dbuf );
off_t vips_dbuf_tell( VipsDbuf *dbuf );
unsigned char *vips_dbuf_string( VipsDbuf *dbuf, size_t *size );
unsigned char *vips_dbuf_steal( VipsDbuf *dbuf, size_t *size );
#endif /*VIPS_DBUF_H*/
#ifdef __cplusplus
}
#endif /*__cplusplus*/

View File

@ -77,14 +77,6 @@ extern "C" {
*/
#define VIPS_META_ICC_NAME "icc-profile-data"
/**
* VIPS_META_XML:
*
* The original XML that was used to code the metadata after reading a VIPS
* format file.
*/
#define VIPS_META_XML "xml-header"
/**
* VIPS_META_IMAGEDESCRIPTION:
*

View File

@ -227,7 +227,7 @@ 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 );
char *vips__xml_properties( VipsImage *image );
void vips__cairo2rgba( guint32 *buf, int n );

View File

@ -106,6 +106,7 @@ extern "C" {
#include <vips/basic.h>
#include <vips/buf.h>
#include <vips/dbuf.h>
#include <vips/util.h>
#include <vips/object.h>
#include <vips/type.h>

View File

@ -1,6 +1,7 @@
noinst_LTLIBRARIES = libiofuncs.la
libiofuncs_la_SOURCES = \
dbuf.c \
reorder.c \
vipsmarshal.h \
vipsmarshal.c \

367
libvips/iofuncs/dbuf.c Normal file
View File

@ -0,0 +1,367 @@
/* A dynamic memory buffer that expands as you write.
*/
/*
Copyright (C) 1991-2003 The National Gallery
This library 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.1 of the License, or (at your option) any later version.
This library 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 library; 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
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /*HAVE_CONFIG_H*/
#include <vips/intl.h>
#include <string.h>
#include <vips/vips.h>
/**
* vips_dbuf_init:
* @dbuf: the buffer
*
* Initialize a buffer.
*/
void
vips_dbuf_init( VipsDbuf *dbuf )
{
dbuf->data = NULL;
dbuf->allocated_size = 0;
dbuf->data_size = 0;
dbuf->write_point = 0;
}
/**
* vips_dbuf_minimum_size:
* @dbuf: the buffer
* @size: the minimum size
*
* Make sure @dbuf is at least @size bytes.
*
* Returns: %FALSE on out of memory, %TRUE otherwise.
*/
gboolean
vips_dbuf_minimum_size( VipsDbuf *dbuf, size_t size )
{
if( size > dbuf->allocated_size ) {
size_t new_allocated_size = 3 * (16 + size) / 2;
unsigned char *new_data;
if( !(new_data =
g_try_realloc( dbuf->data, new_allocated_size )) ) {
vips_error( "VipsDbuf", "%s", _( "out of memory" ) );
return( FALSE );
}
dbuf->data = new_data;
dbuf->allocated_size = new_allocated_size;
}
return( TRUE );
}
/**
* vips_dbuf_allocate:
* @dbuf: the buffer
* @size: the size to allocate
*
* Make sure @dbuf has at least @size bytes available after the write point.
*
* Returns: %FALSE on out of memory, %TRUE otherwise.
*/
gboolean
vips_dbuf_allocate( VipsDbuf *dbuf, size_t size )
{
return( vips_dbuf_minimum_size( dbuf, dbuf->write_point + size ) );
}
/**
* vips_dbuf_null_terminate:
* @dbuf: the buffer
*
* Make sure the byte after the last data byte is `\0`. This extra byte is not
* included in the data size and the write point is not moved.
*
* This makes it safe to treat the dbuf contents as a C string.
*
* Returns: %FALSE on out of memory, %TRUE otherwise.
*/
static gboolean
vips_dbuf_null_terminate( VipsDbuf *dbuf )
{
if( !vips_dbuf_minimum_size( dbuf, dbuf->data_size + 1 ) )
return( FALSE );
dbuf->data[dbuf->data_size] = 0;
return( TRUE );
}
/**
* vips_dbuf_get_write:
* @dbuf: the buffer
* @size: (allow-none): optionally return length in bytes here
*
* Return a pointer to an area you can write to, return length of area in
* @size. Use vips_dbuf_allocate() before this call to set a minimum amount of
* space to have available.
*
* The write point moves to just beyond the returned block. Use
* vips_dbuf_seek() to move it back again.
*
* Returns: (transfer none): start of write area.
*/
unsigned char *
vips_dbuf_get_write( VipsDbuf *dbuf, size_t *size )
{
unsigned char *write = dbuf->data + dbuf->write_point;
size_t length = dbuf->data + dbuf->allocated_size - write;
memset( write, 0, length );
dbuf->write_point = dbuf->allocated_size;
dbuf->data_size = dbuf->allocated_size;
if( size )
*size = length;
return( write );
}
/**
* vips_dbuf_write:
* @dbuf: the buffer
* @data: the data to write to the buffer
* @size: the size of the len to write
*
* Append @size bytes from @data. @dbuf expands if necessary.
*
* Returns: %FALSE on out of memory, %TRUE otherwise.
*/
gboolean
vips_dbuf_write( VipsDbuf *dbuf, const unsigned char *data, size_t size )
{
if( !vips_dbuf_allocate( dbuf, size ) )
return( FALSE );
memcpy( dbuf->data + dbuf->write_point, data, size );
dbuf->write_point += size;
dbuf->data_size = VIPS_MAX( dbuf->data_size, dbuf->write_point );
return( TRUE );
}
/**
* vips_dbuf_writef:
* @dbuf: the buffer
* @fmt: <function>printf()</function>-style format string
* @...: arguments to format string
*
* Format the string and write to @dbuf.
*
* Returns: %FALSE on out of memory, %TRUE otherwise.
*/
gboolean
vips_dbuf_writef( VipsDbuf *dbuf, const char *fmt, ... )
{
va_list ap;
char *line;
va_start( ap, fmt );
line = g_strdup_vprintf( fmt, ap );
va_end( ap );
if( vips_dbuf_write( dbuf, (unsigned char *) line, strlen( line ) ) ) {
g_free( line );
return( FALSE );
}
g_free( line );
return( TRUE );
}
/**
* vips_dbuf_reset:
* @dbuf: the buffer
*
* Reset the buffer to empty. No memory is freed, just the data size and
* write point are reset.
*/
void
vips_dbuf_reset( VipsDbuf *dbuf )
{
dbuf->write_point = 0;
dbuf->data_size = 0;
}
/**
* vips_dbuf_destroy:
* @dbuf: the buffer
*
* Destroy a buffer. This frees any allocated memory.
*/
void
vips_dbuf_destroy( VipsDbuf *dbuf )
{
vips_dbuf_reset( dbuf );
VIPS_FREE( dbuf->data );
dbuf->allocated_size = 0;
}
/**
* vips_dbuf_seek:
* @dbuf: the buffer
* @offset: how to move the write point
* @whence: from start, from end, from current
*
* Move the write point. @whence can be %SEEK_SET, %SEEK_CUR, %SEEK_END, with
* the usual meaning.
*/
gboolean
vips_dbuf_seek( VipsDbuf *dbuf, off_t offset, int whence )
{
off_t new_write_point;
switch( whence ) {
case SEEK_SET:
new_write_point = offset;
break;
case SEEK_END:
new_write_point = dbuf->data_size + offset;
break;
case SEEK_CUR:
new_write_point = dbuf->write_point + offset;
break;
default:
g_assert( 0 );
new_write_point = dbuf->write_point;
break;
}
if( new_write_point < 0 ) {
vips_error( "VipsDbuf", "%s", "negative seek" );
return( FALSE );
}
/* Possibly need to grow the buffer
*/
if( !vips_dbuf_minimum_size( dbuf, new_write_point ) )
return( FALSE );
dbuf->write_point = new_write_point;
if( dbuf->data_size < dbuf->write_point ) {
memset( dbuf->data + dbuf->data_size, 0,
dbuf->write_point - dbuf->data_size );
dbuf->data_size = dbuf->write_point;
}
return( TRUE );
}
/**
* vips_dbuf_truncate:
* @dbuf: the buffer
*
* Truncate the data so that it ends at the write point. No memory is freed.
*/
void
vips_dbuf_truncate( VipsDbuf *dbuf )
{
dbuf->data_size = dbuf->write_point;
}
/**
* vips_dbuf_truncate:
* @dbuf: the buffer
*
* Truncate the data so that it ends at the write point. No memory is freed.
*/
off_t
vips_dbuf_tell( VipsDbuf *dbuf )
{
return( dbuf->write_point );
}
/**
* vips_dbuf_steal:
* @dbuf: the buffer
* @size: (allow-none): optionally return length in bytes here
*
* Destroy a buffer, but rather than freeing memory, a pointer is returned.
* This must be freed with g_free().
*
* A `\0` is appended, but not included in the character count. This is so the
* pointer can be safely treated as a C string.
*
* Returns: (transfer full): The pointer held by @dbuf.
*/
unsigned char *
vips_dbuf_steal( VipsDbuf *dbuf, size_t *size )
{
unsigned char *data;
vips_dbuf_null_terminate( dbuf );
data = dbuf->data;
if( size )
*size = dbuf->data_size;
dbuf->data = NULL;
vips_dbuf_destroy( dbuf );
return( data );
}
/**
* vips_dbuf_string:
* @dbuf: the buffer
* @size: (allow-none): optionally return length in bytes here
*
* Return a pointer to @dbuf's internal data.
*
* A `\0` is appended, but not included in the character count. This is so the
* pointer can be safely treated as a C string.
*
* Returns: (transfer none): The pointer held by @dbuf.
*/
unsigned char *
vips_dbuf_string( VipsDbuf *dbuf, size_t *size )
{
vips_dbuf_null_terminate( dbuf );
if( size )
*size = dbuf->data_size;
return( dbuf->data );
}

View File

@ -17,6 +17,8 @@
* image.c ... this file now just does read / write to disc
* 28/3/11
* - moved to vips_ namespace
* 25/2/17
* - use expat for xml read, printf for xml write
*/
/*
@ -47,8 +49,8 @@
*/
/*
#define DEBUG
#define SHOW_HEADER
#define DEBUG
*/
#ifdef HAVE_CONFIG_H
@ -77,7 +79,7 @@
#ifdef HAVE_IO_H
#include <io.h>
#endif /*HAVE_IO_H*/
#include <libxml/parser.h>
#include <expat.h>
#include <errno.h>
#ifdef OS_WIN32
@ -139,7 +141,7 @@
/* Our XML namespace.
*/
#define NAMESPACE "http://www.vips.ecs.soton.ac.uk/vips"
#define NAMESPACE_URI "http://www.vips.ecs.soton.ac.uk/"
/* Open for read for image files.
*/
@ -434,9 +436,9 @@ vips__read_extension_block( VipsImage *im, int *size )
psize = image_pixel_length( im );
g_assert( im->file_length > 0 );
if( im->file_length - psize > 10 * 1024 * 1024 ) {
if( im->file_length - psize > 100 * 1024 * 1024 ) {
vips_error( "VipsImage",
"%s", _( "more than a 10 megabytes of XML? "
"%s", _( "more than 100 megabytes of XML? "
"sufferin' succotash!" ) );
return( NULL );
}
@ -456,74 +458,99 @@ vips__read_extension_block( VipsImage *im, int *size )
return( buf );
}
/* Read everything after the pixels into memory.
FIXME ... why can't we use xmlParserInputBufferCreateFd and parse
directly from the fd rather than having to read the stupid thing into
memory
the libxml API docs are impossible to decipher
*/
static xmlDoc *
read_xml( VipsImage *im )
static int
parser_read_fd( XML_Parser parser, int fd )
{
void *buf;
int size;
xmlDoc *doc;
xmlNode *node;
const int chunk_size = 1024;
if( !(buf = vips__read_extension_block( im, &size )) )
return( NULL );
if( !(doc = xmlParseMemory( buf, size )) ) {
vips_free( buf );
return( NULL );
}
vips_free( buf );
if( !(node = xmlDocGetRootElement( doc )) ||
!node->nsDef ||
!vips_isprefix( NAMESPACE, (char *) node->nsDef->href ) ) {
vips_error( "VipsImage",
"%s", _( "incorrect namespace in XML" ) );
xmlFreeDoc( doc );
return( NULL );
}
ssize_t bytes_read;
do {
void *buf;
if( !(buf = XML_GetBuffer( parser, chunk_size )) ) {
vips_error( "VipsImage",
"%s", _( "unable to allocate read buffer" ) );
return( -1 );
}
bytes_read = read( fd, buf, chunk_size );
if( bytes_read == (ssize_t) -1 ) {
vips_error( "VipsImage",
"%s", _( "read error while fetching XML" ) );
return( -1 );
}
if( !XML_ParseBuffer( parser, bytes_read, bytes_read == 0 ) ) {
vips_error( "VipsImage",
"%s", _( "XML parse error" ) );
return( -1 );
}
} while( bytes_read > 0 );
return( 0 );
}
#define MAX_PARSE_ATTR (256)
/* What we track during expat parse.
*/
typedef struct _VipsExpatParse {
VipsImage *image;
/* Set on error.
*/
gboolean error;
/* TRUE for in header section.
*/
gboolean header;
/* For the current node, the type and name.
*/
XML_Char type[MAX_PARSE_ATTR];
XML_Char name[MAX_PARSE_ATTR];
/* Accumulate data here.
*/
VipsDbuf dbuf;
} VipsExpatParse;
static void
parser_element_start_handler( void *user_data,
const XML_Char *name, const XML_Char **atts )
{
VipsExpatParse *vep = (VipsExpatParse *) user_data;
const XML_Char **p;
#ifdef DEBUG
printf( "read_xml: namespace == %s\n", node->nsDef->href );
printf( "parser_element_start: %s\n", name );
for( p = atts; *p; p += 2 )
printf( "%s = %s\n", p[0], p[1] );
#endif /*DEBUG*/
return( doc );
}
if( strcmp( name, "field" ) == 0 ) {
for( p = atts; *p; p += 2 ) {
if( strcmp( p[0], "name" ) == 0 )
vips_strncpy( vep->name, p[1], MAX_PARSE_ATTR );
if( strcmp( p[0], "type" ) == 0 )
vips_strncpy( vep->type, p[1], MAX_PARSE_ATTR );
}
/* Find the first child node with a name.
*/
static xmlNode *
get_node( xmlNode *base, const char *name )
{
xmlNode *i;
for( i = base->children; i; i = i->next )
if( strcmp( (char *) i->name, name ) == 0 )
return( i );
return( NULL );
}
/* Read a string property to a buffer. TRUE for success.
*/
static int
get_sprop( xmlNode *xnode, const char *name, char *buf, int sz )
{
char *value = (char *) xmlGetProp( xnode, (xmlChar *) name );
if( !value )
return( 0 );
vips_strncpy( buf, value, sz );
VIPS_FREEF( xmlFree, value );
return( 1 );
vips_dbuf_reset( &vep->dbuf );
}
else if( strcmp( name, "header" ) == 0 )
vep->header = TRUE;
else if( strcmp( name, "meta" ) == 0 )
vep->header = FALSE;
else if( strcmp( name, "root" ) == 0 ) {
for( p = atts; *p; p += 2 )
if( strcmp( p[0], "xmlns" ) == 0 &&
!vips_isprefix( NAMESPACE_URI "vips", p[1] ) ) {
vips_error( "VipsImage", "%s",
_( "incorrect namespace in XML" ) );
vep->error = TRUE;
}
}
}
/* Chop history into lines, add each one as a refstring.
@ -555,119 +582,73 @@ set_history( VipsImage *im, char *history )
im->history_list = g_slist_reverse( history_list );
}
/* Load header fields.
*/
static int
rebuild_header_builtin( VipsImage *im, xmlNode *i )
set_meta( VipsImage *image, GType gtype, const char *name, const char *data )
{
char name[256];
GValue save_value = { 0 };
GValue value = { 0 };
if( get_sprop( i, "name", name, 256 ) ) {
if( strcmp( name, "Hist" ) == 0 ) {
char *history;
g_value_init( &save_value, VIPS_TYPE_SAVE_STRING );
vips_value_set_save_string( &save_value, data );
/* Have to take (another) copy, since we need to free
* with xmlFree().
g_value_init( &value, gtype );
if( !g_value_transform( &save_value, &value ) ) {
g_value_unset( &save_value );
vips_error( "VipsImage", "%s",
_( "error transforming from save format" ) );
return( -1 );
}
vips_image_set( image, name, &value );
g_value_unset( &save_value );
g_value_unset( &value );
return( 0 );
}
static void
parser_element_end_handler( void *user_data, const XML_Char *name )
{
VipsExpatParse *vep = (VipsExpatParse *) user_data;
#ifdef DEBUG
printf( "parser_element_end_handler: %s\n", name );
#endif /*DEBUG*/
if( strcmp( name, "field" ) == 0 ) {
if( vep->header ) {
if( strcmp( name, "Hist" ) == 0 )
set_history( vep->image,
(char *) vips_dbuf_string( &vep->dbuf,
NULL ) );
}
else {
GType gtype = g_type_from_name( vep->type );
/* Can we convert from VIPS_SAVE_STRING to type?
*/
history = (char *) xmlNodeGetContent( i );
set_history( im, history );
xmlFree( history );
if( gtype &&
g_value_type_transformable(
VIPS_TYPE_SAVE_STRING, gtype ) &&
set_meta( vep->image,
gtype, vep->name,
(char *) vips_dbuf_string( &vep->dbuf,
NULL ) ) )
vep->error = TRUE;
}
}
return( 0 );
}
/* Load meta fields.
*/
static int
rebuild_header_meta( VipsImage *im, xmlNode *i )
static void
parser_data_handler( void *user_data, const XML_Char *data, int len )
{
char name[256];
char type[256];
VipsExpatParse *vep = (VipsExpatParse *) user_data;
if( get_sprop( i, "name", name, 256 ) &&
get_sprop( i, "type", type, 256 ) ) {
GType gtype = g_type_from_name( type );
#ifdef DEBUG
printf( "parser_data_handler: %d bytes\n", len );
#endif /*DEBUG*/
/* Can we convert from VIPS_SAVE_STRING to type?
*/
if( gtype &&
g_value_type_transformable(
VIPS_TYPE_SAVE_STRING, gtype ) ) {
char *content;
GValue save_value = { 0 };
GValue value = { 0 };
content = (char *) xmlNodeGetContent( i );
g_value_init( &save_value, VIPS_TYPE_SAVE_STRING );
vips_value_set_save_string( &save_value, content );
xmlFree( content );
g_value_init( &value, gtype );
if( !g_value_transform( &save_value, &value ) ) {
g_value_unset( &save_value );
vips_error( "VipsImage",
"%s", _( "error transforming from "
"save format" ) );
return( -1 );
}
vips_image_set( im, name, &value );
g_value_unset( &save_value );
g_value_unset( &value );
}
}
return( 0 );
}
static xmlDoc *
get_xml( VipsImage *im )
{
if( vips_image_get_typeof( im, VIPS_META_XML ) ) {
xmlDoc *doc;
if( vips_image_get_area( im, VIPS_META_XML, (void *) &doc ) )
return( NULL );
return( doc );
}
return( NULL );
}
/* Rebuild header fields that depend on stuff saved in xml.
*/
static int
rebuild_header( VipsImage *im )
{
xmlDoc *doc;
if( (doc = get_xml( im )) ) {
xmlNode *root;
xmlNode *block;
if( !(root = xmlDocGetRootElement( doc )) )
return( -1 );
if( (block = get_node( root, "header" )) ) {
xmlNode *i;
for( i = block->children; i; i = i->next )
if( strcmp( (char *) i->name, "field" ) == 0 )
if( rebuild_header_builtin( im, i ) )
return( -1 );
}
if( (block = get_node( root, "meta" )) ) {
xmlNode *i;
for( i = block->children; i; i = i->next )
if( strcmp( (char *) i->name, "field" ) == 0 )
if( rebuild_header_meta( im, i ) )
return( -1 );
}
}
return( 0 );
vips_dbuf_write( &vep->dbuf, (unsigned char *) data, len );
}
/* Called at the end of vips open ... get any XML after the pixel data
@ -676,123 +657,34 @@ rebuild_header( VipsImage *im )
static int
readhist( VipsImage *im )
{
/* Junk any old xml meta.
*/
if( vips_image_get_typeof( im, VIPS_META_XML ) )
vips_image_set_area( im, VIPS_META_XML, NULL, NULL );
XML_Parser parser;
VipsExpatParse vep;
if( vips__has_extension_block( im ) ) {
xmlDoc *doc;
if( vips__seek( im->fd, image_pixel_length( im ) ) )
return( -1 );
if( !(doc = read_xml( im )) )
return( -1 );
vips_image_set_area( im, VIPS_META_XML,
(VipsCallbackFn) xmlFreeDoc, doc );
parser = XML_ParserCreate( "UTF-8" );
vep.image = im;
vips_dbuf_init( &vep.dbuf );
vep.error = FALSE;
XML_SetUserData( parser, &vep );
XML_SetElementHandler( parser,
parser_element_start_handler, parser_element_end_handler );
XML_SetCharacterDataHandler( parser, parser_data_handler );
if( parser_read_fd( parser, im->fd ) ||
vep.error ) {
vips_dbuf_destroy( &vep.dbuf );
XML_ParserFree( parser );
return( -1 );
}
if( rebuild_header( im ) )
return( -1 );
vips_dbuf_destroy( &vep.dbuf );
XML_ParserFree( parser );
return( 0 );
}
#define MAX_STRSIZE (32768) /* Max size of text for stack strings */
static int
set_prop( xmlNode *node, const char *name, const char *fmt, ... )
{
va_list ap;
char value[MAX_STRSIZE];
va_start( ap, fmt );
(void) vips_vsnprintf( value, MAX_STRSIZE, fmt, ap );
va_end( ap );
if( !xmlSetProp( node, (xmlChar *) name, (xmlChar *) value ) ) {
vips_error( "VipsImage", _( "unable to set property \"%s\" "
"to value \"%s\"." ),
name, value );
return( -1 );
}
return( 0 );
}
static int
set_sprop( xmlNode *node, const char *name, const char *value )
{
if( value && set_prop( node, name, "%s", value ) )
return( -1 );
return( 0 );
}
static int
set_field( xmlNode *node,
const char *name, const char *type, const char *content )
{
xmlNode *field;
if( !(field = xmlNewChild( node, NULL, (xmlChar *) "field", NULL )) ||
set_sprop( field, "type", type ) ||
set_sprop( field, "name", name ) )
return( -1 );
xmlNodeSetContent( field, (xmlChar *) content );
return( 0 );
}
static void *
save_fields_meta( VipsMeta *meta, xmlNode *node )
{
GType type = G_VALUE_TYPE( &meta->value );
/* If we can transform to VIPS_TYPE_SAVE_STRING and back, we can save
* and restore.
*/
if( g_value_type_transformable( type, VIPS_TYPE_SAVE_STRING ) &&
g_value_type_transformable( VIPS_TYPE_SAVE_STRING, type ) ) {
GValue save_value = { 0 };
g_value_init( &save_value, VIPS_TYPE_SAVE_STRING );
if( !g_value_transform( &meta->value, &save_value ) ) {
vips_error( "VipsImage", "%s",
_( "error transforming to save format" ) );
return( node );
}
if( set_field( node, meta->name, g_type_name( type ),
vips_value_get_save_string( &save_value ) ) ) {
g_value_unset( &save_value );
return( node );
}
g_value_unset( &save_value );
}
return( NULL );
}
static int
save_fields( VipsImage *im, xmlNode *node )
{
xmlNode *this;
/* Save header fields.
*/
if( !(this = xmlNewChild( node, NULL, (xmlChar *) "header", NULL )) )
return( -1 );
if( set_field( this, "Hist",
g_type_name( VIPS_TYPE_REF_STRING ),
vips_image_get_history( im ) ) )
return( -1 );
if( !(this = xmlNewChild( node, NULL, (xmlChar *) "meta", NULL )) )
return( -1 );
if( im->meta_traverse &&
vips_slist_map2( im->meta_traverse,
(VipsSListMap2Fn) save_fields_meta, this, NULL ) )
return( -1 );
return( 0 );
return( 0 );
}
int
@ -823,52 +715,226 @@ vips__write_extension_block( VipsImage *im, void *buf, int size )
return( 0 );
}
/* Append a string to a buffer, but escape " as \".
*/
static void
dbuf_write_quotes( VipsDbuf *dbuf, const char *str )
{
const char *p;
size_t len;
for( p = str; *p; p += len ) {
len = strcspn( p, "\"" );
vips_dbuf_write( dbuf, (unsigned char *) p, len );
if( p[len] == '"' )
vips_dbuf_writef( dbuf, "\\" );
}
}
/* Append a string to a buffer, but escape &<>.
*/
static void
dbuf_write_amp( VipsDbuf *dbuf, const char *str )
{
const char *p;
size_t len;
for( p = str; *p; p += len ) {
len = strcspn( p, "&<>" );
vips_dbuf_write( dbuf, (unsigned char *) p, len );
switch( p[len] ) {
case '&':
vips_dbuf_writef( dbuf, "&amp;" );
len += 1;
break;
case '<':
vips_dbuf_writef( dbuf, "&lt;" );
len += 1;
break;
case '>':
vips_dbuf_writef( dbuf, "&gt;" );
len += 1;
break;
default:
break;
}
}
}
static void *
build_xml_meta( VipsMeta *meta, VipsDbuf *dbuf )
{
GType type = G_VALUE_TYPE( &meta->value );
const char *str;
/* If we can transform to VIPS_TYPE_SAVE_STRING and back, we can save
* and restore.
*/
if( g_value_type_transformable( type, VIPS_TYPE_SAVE_STRING ) &&
g_value_type_transformable( VIPS_TYPE_SAVE_STRING, type ) ) {
GValue save_value = { 0 };
g_value_init( &save_value, VIPS_TYPE_SAVE_STRING );
if( !g_value_transform( &meta->value, &save_value ) ) {
vips_error( "VipsImage", "%s",
_( "error transforming to save format" ) );
return( meta );
}
str = vips_value_get_save_string( &save_value );
vips_dbuf_writef( dbuf, " <field type=\"%s\" name=\"",
g_type_name( type ) );
dbuf_write_quotes( dbuf, meta->name );
vips_dbuf_writef( dbuf, "\">" );
dbuf_write_amp( dbuf, str );
vips_dbuf_writef( dbuf, "</field>\n" );
g_value_unset( &save_value );
}
return( NULL );
}
/* Make the xml we append to vips images after the pixel data.
*/
static char *
build_xml( VipsImage *image )
{
VipsDbuf dbuf;
const char *str;
vips_dbuf_init( &dbuf );
vips_dbuf_writef( &dbuf, "<?xml version=\"1.0\"?>\n" );
vips_dbuf_writef( &dbuf, "<root xmlns=\"%svips/%d.%d.%d\">\n",
NAMESPACE_URI,
VIPS_MAJOR_VERSION, VIPS_MINOR_VERSION, VIPS_MICRO_VERSION );
vips_dbuf_writef( &dbuf, " <header>\n" );
str = vips_image_get_history( image );
vips_dbuf_writef( &dbuf, " <field type=\"%s\" name=\"Hist\">",
g_type_name( VIPS_TYPE_REF_STRING ) );
dbuf_write_amp( &dbuf, str );
vips_dbuf_writef( &dbuf, "</field>\n" );
vips_dbuf_writef( &dbuf, " </header>\n" );
vips_dbuf_writef( &dbuf, " <meta>\n" );
if( vips_slist_map2( image->meta_traverse,
(VipsSListMap2Fn) build_xml_meta, &dbuf, NULL ) ) {
vips_dbuf_destroy( &dbuf );
return( NULL );
}
vips_dbuf_writef( &dbuf, " </meta>\n" );
vips_dbuf_writef( &dbuf, "</root>\n" );
return( (char *) vips_dbuf_steal( &dbuf, NULL ) );
}
static void *
vips__xml_properties_meta( VipsImage *image,
const char *field, GValue *value, void *a )
{
VipsDbuf *dbuf = (VipsDbuf *) a;
GType type = G_VALUE_TYPE( value );
const char *str;
/* If we can transform to VIPS_TYPE_SAVE_STRING and back, we can save
* and restore.
*/
if( g_value_type_transformable( type, VIPS_TYPE_SAVE_STRING ) &&
g_value_type_transformable( VIPS_TYPE_SAVE_STRING, type ) ) {
GValue save_value = { 0 };
g_value_init( &save_value, VIPS_TYPE_SAVE_STRING );
if( !g_value_transform( value, &save_value ) ) {
vips_error( "VipsImage", "%s",
_( "error transforming to save format" ) );
return( dbuf );
}
str = vips_value_get_save_string( &save_value );
vips_dbuf_writef( dbuf, " <property>\n" );
vips_dbuf_writef( dbuf, " <name>" );
dbuf_write_amp( dbuf, field );
vips_dbuf_writef( dbuf, "</name>\n" );
vips_dbuf_writef( dbuf, " <value type=\"%s\">",
g_type_name( type ) );
dbuf_write_amp( dbuf, str );
vips_dbuf_writef( dbuf, "</value>\n" );
vips_dbuf_writef( dbuf, " </property>\n" );
g_value_unset( &save_value );
}
return( NULL );
}
/* Make the xml we write to vips-properties in dzsave, or to TIFF. A simple
* dump of all vips metadata. Free with g_free().
*/
char *
vips__xml_properties( VipsImage *image )
{
VipsDbuf dbuf;
GTimeVal now;
char *date;
vips_dbuf_init( &dbuf );
g_get_current_time( &now );
date = g_time_val_to_iso8601( &now );
vips_dbuf_writef( &dbuf, "<?xml version=\"1.0\"?>\n" );
vips_dbuf_writef( &dbuf, "<image xmlns=\"%s/dzsave\" "
"date=\"%s\" version=\"%d.%d.%d\">\n",
NAMESPACE_URI,
date,
VIPS_MAJOR_VERSION, VIPS_MINOR_VERSION, VIPS_MICRO_VERSION );
g_free( date );
vips_dbuf_writef( &dbuf, " <properties>\n" );
if( vips_image_map( image, vips__xml_properties_meta, &dbuf ) ) {
vips_dbuf_destroy( &dbuf );
return( NULL );
}
vips_dbuf_writef( &dbuf, " </properties>\n" );
vips_dbuf_writef( &dbuf, "</image>\n" );
return( (char *) vips_dbuf_steal( &dbuf, NULL ) );
}
/* Append XML to output fd.
*/
int
vips__writehist( VipsImage *im )
vips__writehist( VipsImage *image )
{
xmlDoc *doc;
char namespace[256];
char *dump;
int dump_size;
char *xml;
assert( im->dtype == VIPS_IMAGE_OPENOUT );
assert( im->fd != -1 );
assert( image->dtype == VIPS_IMAGE_OPENOUT );
assert( image->fd != -1 );
if( !(doc = xmlNewDoc( (xmlChar *) "1.0" )) )
if( !(xml = build_xml( image )) )
return( -1 );
vips_snprintf( namespace, 256, "%s/%d.%d.%d",
NAMESPACE,
VIPS_MAJOR_VERSION, VIPS_MINOR_VERSION, VIPS_MICRO_VERSION );
if( !(doc->children = xmlNewDocNode( doc,
NULL, (xmlChar *) "root", NULL )) ||
set_sprop( doc->children, "xmlns", namespace ) ||
save_fields( im, doc->children ) ) {
vips_error( "VipsImage", "%s", _( "xml save error" ) );
xmlFreeDoc( doc );
return( -1 );
}
xmlDocDumpFormatMemory( doc, (xmlChar **) &dump, &dump_size, 1 );
if( !dump ) {
vips_error( "VipsImage", "%s", _( "xml save error" ) );
xmlFreeDoc( doc );
return( -1 );
}
xmlFreeDoc( doc );
if( vips__write_extension_block( im, dump, dump_size ) ) {
xmlFree( dump );
if( vips__write_extension_block( image, xml, strlen( xml ) ) ) {
g_free( xml );
return( -1 );
}
#ifdef DEBUG
printf( "vips__writehist: saved XML is: \"%s\"", dump );
printf( "vips__writehist: saved XML is: \"%s\"", xml );
#endif /*DEBUG*/
xmlFree( dump );
g_free( xml );
return( 0 );
}

View File

@ -149,7 +149,8 @@ print_header( VipsImage *im, gboolean many )
else {
char *str;
vips_image_get_as_string( im, main_option_field, &str );
if( vips_image_get_as_string( im, main_option_field, &str ) )
return( -1 );
printf( "%s\n", str );
g_free( str );
}