Merge branch 'try-expat'
This commit is contained in:
commit
47a4a78a33
@ -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
|
||||
|
@ -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
14
TODO
@ -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
|
||||
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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 );
|
||||
}
|
||||
|
@ -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 );
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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 );
|
||||
|
||||
|
@ -6,6 +6,7 @@ pkginclude_HEADERS = \
|
||||
deprecated.h \
|
||||
arithmetic.h \
|
||||
buf.h \
|
||||
dbuf.h \
|
||||
colour.h \
|
||||
conversion.h \
|
||||
convolution.h \
|
||||
|
83
libvips/include/vips/dbuf.h
Normal file
83
libvips/include/vips/dbuf.h
Normal 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*/
|
@ -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:
|
||||
*
|
||||
|
@ -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 );
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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
367
libvips/iofuncs/dbuf.c
Normal 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 );
|
||||
}
|
||||
|
||||
|
@ -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, "&" );
|
||||
len += 1;
|
||||
break;
|
||||
|
||||
case '<':
|
||||
vips_dbuf_writef( dbuf, "<" );
|
||||
len += 1;
|
||||
break;
|
||||
|
||||
case '>':
|
||||
vips_dbuf_writef( dbuf, ">" );
|
||||
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 );
|
||||
}
|
||||
|
@ -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 );
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user