diff --git a/ChangeLog b/ChangeLog index a045cb33..01e118fe 100644 --- a/ChangeLog +++ b/ChangeLog @@ -36,7 +36,6 @@ more tightly ... this makes seq more versatile and more reliable - use expat, not libxml, for XML load ... removes a required dependency, since we get expat as part of glib -- use printf for xml write 8/12/16 started 8.4.5 - allow libgsf-1.14.26 to help centos, thanks tdiprima diff --git a/TODO b/TODO index e8841b03..156444ec 100644 --- a/TODO +++ b/TODO @@ -1,123 +1,7 @@ -- remove libxml from configure.ac - - we don't escape " inside xml attr strings, or < > inside xml content +- use VipsDbuf for tiffsave_buffer etc. - break Buffer out into a separate class that does this? VipsDbuf, for dynamic - buffer? or can it be a mode for VipsBuf? +- verify xml data against master for vips save and dzsave - buffer should expand by 30% on each fill to avoid lots of tiny realloc - - use for tiffsave_buffer etc. - - have things like write-escaped-string as extra append operations - - need one that escapes ", a separate one that escapes < > - -- dzsave vips.properties is currently - - - - - - - width - 1450 - - - height - 2048 - - - bands - 3 - - - xoffset - 0 - - - yoffset - 0 - - - xres - 0.99999999990686772 - - - yres - 0.99999999990686772 - - - vips-loader - jpegload - - - jpeg-multiscan - 0 - - - exif-data - -RXhpZgAASUkqAAgAAAAGABIBAwABAAAAAQAAABoBBQABAAAAVgAAABsBBQABAAAAXgAAACgBAwAB -AAAAAgAAABMCAwABAAAAAQAAAGmHBAABAAAAZgAAAAAAAAAR/P//KSgUChH8//8pKBQKBgAAkAcA -BAAAADAyMTABkQcABAAAAAECAwAAoAcABAAAADAxMDABoAMAAQAAAP//AAACoAQAAQAAAKoFAAAD -oAQAAQAAAAAIAAAAAAAA - - - - resolution-unit - in - - - exif-ifd0-Orientation - 1 (Top-left, Short, 1 components, 2 bytes) - - - exif-ifd0-XResolution - 4294966289/169093161 (25.399999998, Rational, 1 components, 8 bytes) - - - exif-ifd0-YResolution - 4294966289/169093161 (25.399999998, Rational, 1 components, 8 bytes) - - - exif-ifd0-ResolutionUnit - 2 (Inch, Short, 1 components, 2 bytes) - - - exif-ifd0-YCbCrPositioning - 1 (Centred, Short, 1 components, 2 bytes) - - - exif-ifd2-ExifVersion - Exif Version 2.1 (Exif Version 2.1, Undefined, 4 components, 4 bytes) - - - exif-ifd2-ComponentsConfiguration - Y Cb Cr - (Y Cb Cr -, Undefined, 4 components, 4 bytes) - - - exif-ifd2-FlashPixVersion - FlashPix Version 1.0 (FlashPix Version 1.0, Undefined, 4 components, 4 bytes) - - - exif-ifd2-ColorSpace - 65535 (Internal error (unknown value 65535), Short, 1 components, 2 bytes) - - - exif-ifd2-PixelXDimension - 1450 (1450, Long, 1 components, 4 bytes) - - - exif-ifd2-PixelYDimension - 2048 (2048, Long, 1 components, 4 bytes) - - - orientation - 1 - - - @@ -246,20 +130,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 diff --git a/libvips/include/vips/Makefile.am b/libvips/include/vips/Makefile.am index 69f51b28..a76a306e 100644 --- a/libvips/include/vips/Makefile.am +++ b/libvips/include/vips/Makefile.am @@ -6,6 +6,7 @@ pkginclude_HEADERS = \ deprecated.h \ arithmetic.h \ buf.h \ + dbuf.h \ colour.h \ conversion.h \ convolution.h \ diff --git a/libvips/include/vips/dbuf.h b/libvips/include/vips/dbuf.h new file mode 100644 index 00000000..c48908f7 --- /dev/null +++ b/libvips/include/vips/dbuf.h @@ -0,0 +1,72 @@ +/* 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 + +/* A buffer in the process of being written to ... multiple calls to + * vips_dbuf_append add to it. + */ + +typedef struct _VipsDbuf { + /* All fields are private. + */ + /*< private >*/ + + /* The current base, and the size of the allocated memory area. + */ + char *data; + size_t max_size; + + /* And the write point. + */ + size_t write_point; +} VipsDbuf; + +void vips_dbuf_destroy( VipsDbuf *buf ); +void vips_dbuf_init( VipsDbuf *buf ); +gboolean vips_dbuf_append( VipsDbuf *dbuf, const char *data, size_t size ); +gboolean vips_dbuf_appendf( VipsDbuf *dbuf, const char *fmt, ... ); +void vips_dbuf_rewind( VipsDbuf *dbuf ); +void vips_dbuf_destroy( VipsDbuf *dbuf ); +char *vips_dbuf_string( VipsDbuf *dbuf, size_t *size ); +char *vips_dbuf_steal( VipsDbuf *dbuf, size_t *size ); + +#endif /*VIPS_DBUF_H*/ + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ diff --git a/libvips/include/vips/vips.h b/libvips/include/vips/vips.h index 30be4706..75c6c3b4 100644 --- a/libvips/include/vips/vips.h +++ b/libvips/include/vips/vips.h @@ -106,6 +106,7 @@ extern "C" { #include #include +#include #include #include #include diff --git a/libvips/iofuncs/Makefile.am b/libvips/iofuncs/Makefile.am index cdf244a8..51b47f75 100644 --- a/libvips/iofuncs/Makefile.am +++ b/libvips/iofuncs/Makefile.am @@ -1,6 +1,7 @@ noinst_LTLIBRARIES = libiofuncs.la libiofuncs_la_SOURCES = \ + dbuf.c \ reorder.c \ vipsmarshal.h \ vipsmarshal.c \ diff --git a/libvips/iofuncs/dbuf.c b/libvips/iofuncs/dbuf.c new file mode 100644 index 00000000..36d22a59 --- /dev/null +++ b/libvips/iofuncs/dbuf.c @@ -0,0 +1,206 @@ +/* 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 +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +/** + * vips_dbuf_init: + * @dbuf: the buffer + * + * Initialize a buffer. + */ +void +vips_dbuf_init( VipsDbuf *dbuf ) +{ + dbuf->data = NULL; + dbuf->max_size = 0; + dbuf->write_point = 0; +} + +/** + * vips_dbuf_append: + * @dbuf: the buffer + * @data: the data to append to the buffer + * @size: the size of the len to append + * + * Append len bytes from @data to the buffer. The buffer expands if necessary. + * + * Returns: %FALSE on out of memory, %TRUE otherwise. + */ +gboolean +vips_dbuf_append( VipsDbuf *dbuf, const char *data, size_t size ) +{ + size_t new_write_point = dbuf->write_point + size; + + if( new_write_point > dbuf->max_size ) { + size_t new_max_size = 3 * (16 + new_write_point) / 2; + + char *new_data; + + if( !(new_data = g_try_realloc( dbuf->data, new_max_size )) ) { + vips_error( "VipsDbuf", "%s", _( "out of memory" ) ); + return( FALSE ); + } + + dbuf->data = new_data; + dbuf->max_size = new_max_size; + } + + memcpy( dbuf->data + dbuf->write_point, data, size ); + dbuf->write_point = new_write_point; + + return( TRUE ); +} + +/** + * vips_dbuf_appendf: + * @dbuf: the buffer + * @fmt: printf()-style format string + * @...: arguments to format string + * + * Format the string and append to @dbuf. + * + * Returns: %FALSE on out of memory, %TRUE otherwise. + */ +gboolean +vips_dbuf_appendf( 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_append( dbuf, line, strlen( line ) ) ) { + g_free( line ); + return( FALSE ); + } + g_free( line ); + + return( TRUE ); +} + +/** + * vips_dbuf_rewind: + * @dbuf: the buffer + * + * Reset the buffer to empty. No memory is freed, just the write pointer is + * reset. + */ +void +vips_dbuf_rewind( VipsDbuf *dbuf ) +{ + dbuf->write_point = 0; +} + +/** + * vips_dbuf_destroy: + * @dbuf: the buffer + * + * Destroy a buffer. This frees any allocated memory. + */ +void +vips_dbuf_destroy( VipsDbuf *dbuf ) +{ + VIPS_FREE( dbuf->data ); + dbuf->max_size = 0; + dbuf->write_point = 0; +} + +/** + * 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. + */ +char * +vips_dbuf_steal( VipsDbuf *dbuf, size_t *size ) +{ + char *data; + + vips_dbuf_append( dbuf, "", 1 ); + dbuf->write_point -= 1; + + data = dbuf->data; + + if( size ) + *size = dbuf->write_point; + + dbuf->data = NULL; + dbuf->max_size = 0; + dbuf->write_point = 0; + + return( data ); +} + +/** + * vips_dbuf_data: + * @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. + */ +char * +vips_dbuf_string( VipsDbuf *dbuf, size_t *size ) +{ + vips_dbuf_append( dbuf, "", 1 ); + dbuf->write_point -= 1; + + if( size ) + *size = dbuf->write_point; + + return( dbuf->data ); +} + + diff --git a/libvips/iofuncs/vips.c b/libvips/iofuncs/vips.c index 970be1ef..1b564e84 100644 --- a/libvips/iofuncs/vips.c +++ b/libvips/iofuncs/vips.c @@ -49,8 +49,8 @@ */ /* -#define DEBUG #define SHOW_HEADER +#define DEBUG */ #ifdef HAVE_CONFIG_H @@ -492,56 +492,6 @@ parser_read_fd( XML_Parser parser, int fd ) #define MAX_PARSE_ATTR (256) -/* A memory buffer that expands as we write to it. - */ -typedef struct _Buffer { - char *data; - size_t current_size; -} Buffer; - -static void -buffer_init( Buffer *buffer ) -{ - buffer->data = NULL; - buffer->current_size = 0; -} - -static void -buffer_append( Buffer *buffer, const char *data, int len ) -{ - size_t new_size = buffer->current_size + len; - - buffer->data = g_realloc( buffer->data, new_size ); - memcpy( buffer->data + buffer->current_size, data, len ); - buffer->current_size = new_size; -} - -static void -buffer_appendf( Buffer *buffer, const char *fmt, ... ) -{ - va_list ap; - char line[256]; - - va_start( ap, fmt ); - (void) vips_vsnprintf( line, 256, fmt, ap ); - va_end( ap ); - - buffer_append( buffer, line, strlen( line ) ); -} - -static void -buffer_rewind( Buffer *buffer ) -{ - buffer->current_size = 0; -} - -static void -buffer_destroy( Buffer *buffer ) -{ - VIPS_FREE( buffer->data ); - buffer->current_size = 0; -} - /* What we track during expat parse. */ typedef struct _VipsExpatParse { @@ -562,7 +512,7 @@ typedef struct _VipsExpatParse { /* Accumulate data here. */ - Buffer buffer; + VipsDbuf dbuf; } VipsExpatParse; static void @@ -586,7 +536,7 @@ parser_element_start_handler( void *user_data, vips_strncpy( vep->type, p[1], MAX_PARSE_ATTR ); } - buffer_rewind( &vep->buffer ); + vips_dbuf_rewind( &vep->dbuf ); } else if( strcmp( name, "header" ) == 0 ) vep->header = TRUE; @@ -666,16 +616,10 @@ parser_element_end_handler( void *user_data, const XML_Char *name ) #endif /*DEBUG*/ if( strcmp( name, "field" ) == 0 ) { - buffer_append( &vep->buffer, "", 1 ); - -#ifdef DEBUG - printf( "parser_element_end_handler: %zd bytes\n", - vep->current_size ); -#endif /*DEBUG*/ - if( vep->header ) { if( strcmp( name, "Hist" ) == 0 ) - set_history( vep->image, vep->buffer.data ); + set_history( vep->image, + vips_dbuf_string( &vep->dbuf, NULL ) ); } else { GType gtype = g_type_from_name( vep->type ); @@ -686,7 +630,8 @@ parser_element_end_handler( void *user_data, const XML_Char *name ) g_value_type_transformable( VIPS_TYPE_SAVE_STRING, gtype ) && set_meta( vep->image, - gtype, vep->name, vep->buffer.data ) ) + gtype, vep->name, + vips_dbuf_string( &vep->dbuf, NULL ) ) ) vep->error = TRUE; } } @@ -701,7 +646,7 @@ parser_data_handler( void *user_data, const XML_Char *data, int len ) printf( "parser_data_handler: %d bytes\n", len ); #endif /*DEBUG*/ - buffer_append( &vep->buffer, data, len ); + vips_dbuf_append( &vep->dbuf, data, len ); } /* Called at the end of vips open ... get any XML after the pixel data @@ -719,7 +664,7 @@ readhist( VipsImage *im ) parser = XML_ParserCreate( "UTF-8" ); vep.image = im; - buffer_init( &vep.buffer ); + vips_dbuf_init( &vep.dbuf ); vep.error = FALSE; XML_SetUserData( parser, &vep ); @@ -729,12 +674,12 @@ readhist( VipsImage *im ) if( parser_read_fd( parser, im->fd ) || vep.error ) { - buffer_destroy( &vep.buffer ); + vips_dbuf_destroy( &vep.dbuf ); XML_ParserFree( parser ); return( -1 ); } - buffer_destroy( &vep.buffer ); + vips_dbuf_destroy( &vep.dbuf ); XML_ParserFree( parser ); return( 0 ); @@ -768,8 +713,59 @@ vips__write_extension_block( VipsImage *im, void *buf, int size ) return( 0 ); } +/* Append a string to a buffer, but escape " as \". + */ +static void +dbuf_append_quotes( VipsDbuf *dbuf, const char *str ) +{ + const char *p; + size_t len; + + for( p = str; *p; p += len ) { + len = strcspn( p, "\"" ); + + vips_dbuf_append( dbuf, p, len ); + if( p[len] == '"' ) + vips_dbuf_append( dbuf, "\\", 1 ); + } +} + +/* Append a string to a buffer, but escape &<>. + */ +static void +dbuf_append_amp( VipsDbuf *dbuf, const char *str ) +{ + const char *p; + size_t len; + + for( p = str; *p; p += len ) { + len = strcspn( p, "&<>" ); + + vips_dbuf_append( dbuf, p, len ); + switch( p[len] ) { + case '&': + vips_dbuf_append( dbuf, "&", 5 ); + len += 1; + break; + + case '<': + vips_dbuf_append( dbuf, "<", 4 ); + len += 1; + break; + + case '>': + vips_dbuf_append( dbuf, ">", 4 ); + len += 1; + break; + + default: + break; + } + } +} + static void * -build_xml_meta( VipsMeta *meta, Buffer *buffer ) +build_xml_meta( VipsMeta *meta, VipsDbuf *dbuf ) { GType type = G_VALUE_TYPE( &meta->value ); @@ -786,14 +782,16 @@ build_xml_meta( VipsMeta *meta, Buffer *buffer ) if( !g_value_transform( &meta->value, &save_value ) ) { vips_error( "VipsImage", "%s", _( "error transforming to save format" ) ); - return( buffer ); + return( meta ); } str = vips_value_get_save_string( &save_value ); - buffer_appendf( buffer, " ", - g_type_name( type ), meta->name ); - buffer_append( buffer, str, strlen( str ) ); - buffer_appendf( buffer, "\n" ); + vips_dbuf_appendf( dbuf, " name ); + vips_dbuf_appendf( dbuf, "\">" ); + dbuf_append_amp( dbuf, str ); + vips_dbuf_appendf( dbuf, "\n" ); g_value_unset( &save_value ); } @@ -806,43 +804,43 @@ build_xml_meta( VipsMeta *meta, Buffer *buffer ) static char * build_xml( VipsImage *image ) { - Buffer buffer; + VipsDbuf dbuf; const char *str; - buffer_init( &buffer ); + vips_dbuf_init( &dbuf ); - buffer_appendf( &buffer, "\n" ); - buffer_appendf( &buffer, "\n", + vips_dbuf_appendf( &dbuf, "\n" ); + vips_dbuf_appendf( &dbuf, "\n", NAMESPACE_URI, VIPS_MAJOR_VERSION, VIPS_MINOR_VERSION, VIPS_MICRO_VERSION ); - buffer_appendf( &buffer, "
\n" ); + vips_dbuf_appendf( &dbuf, "
\n" ); str = vips_image_get_history( image ); - buffer_appendf( &buffer, " ", + vips_dbuf_appendf( &dbuf, " ", g_type_name( VIPS_TYPE_REF_STRING ) ); - buffer_append( &buffer, str, strlen( str ) ); - buffer_appendf( &buffer, "\n" ); + dbuf_append_amp( &dbuf, str ); + vips_dbuf_appendf( &dbuf, "\n" ); - buffer_appendf( &buffer, "
\n" ); - buffer_appendf( &buffer, " \n" ); + vips_dbuf_appendf( &dbuf, "
\n" ); + vips_dbuf_appendf( &dbuf, " \n" ); if( vips_slist_map2( image->meta_traverse, - (VipsSListMap2Fn) build_xml_meta, &buffer, NULL ) ) { - buffer_destroy( &buffer ); + (VipsSListMap2Fn) build_xml_meta, &dbuf, NULL ) ) { + vips_dbuf_destroy( &dbuf ); return( NULL ); } - buffer_appendf( &buffer, " \n" ); - buffer_appendf( &buffer, "
\n" ); + vips_dbuf_appendf( &dbuf, " \n" ); + vips_dbuf_appendf( &dbuf, "
\n" ); - return( buffer.data ); + return( vips_dbuf_steal( &dbuf, NULL ) ); } static void * vips__xml_properties_meta( VipsImage *image, const char *field, GValue *value, void *a ) { - Buffer *buffer = (Buffer *) a; + VipsDbuf *dbuf = (VipsDbuf *) a; GType type = G_VALUE_TYPE( value ); const char *str; @@ -858,18 +856,20 @@ vips__xml_properties_meta( VipsImage *image, if( !g_value_transform( value, &save_value ) ) { vips_error( "VipsImage", "%s", _( "error transforming to save format" ) ); - return( buffer ); + return( dbuf ); } str = vips_value_get_save_string( &save_value ); g_value_unset( &save_value ); - buffer_appendf( buffer, " \n" ); - buffer_appendf( buffer, " %s\n", field ); - buffer_appendf( buffer, " ", + vips_dbuf_appendf( dbuf, " \n" ); + vips_dbuf_appendf( dbuf, " " ); + dbuf_append_amp( dbuf, field ); + vips_dbuf_appendf( dbuf, "\n" ); + vips_dbuf_appendf( dbuf, " ", g_type_name( type ) ); - buffer_append( buffer, str, strlen( str ) ); - buffer_appendf( buffer, "\n" ); - buffer_appendf( buffer, " \n" ); + dbuf_append_amp( dbuf, str ); + vips_dbuf_appendf( dbuf, "\n" ); + vips_dbuf_appendf( dbuf, " \n" ); } return( NULL ); @@ -881,32 +881,32 @@ vips__xml_properties_meta( VipsImage *image, char * vips__xml_properties( VipsImage *image ) { - Buffer buffer; + VipsDbuf dbuf; GTimeVal now; char *date; - buffer_init( &buffer ); + vips_dbuf_init( &dbuf ); g_get_current_time( &now ); date = g_time_val_to_iso8601( &now ); - buffer_appendf( &buffer, "\n" ); - buffer_appendf( &buffer, "\n" ); + vips_dbuf_appendf( &dbuf, "\n", NAMESPACE_URI, date, VIPS_MAJOR_VERSION, VIPS_MINOR_VERSION, VIPS_MICRO_VERSION ); g_free( date ); - buffer_appendf( &buffer, " \n" ); + vips_dbuf_appendf( &dbuf, " \n" ); - if( vips_image_map( image, vips__xml_properties_meta, &buffer ) ) { - buffer_destroy( &buffer ); + if( vips_image_map( image, vips__xml_properties_meta, &dbuf ) ) { + vips_dbuf_destroy( &dbuf ); return( NULL ); } - buffer_appendf( &buffer, " \n" ); - buffer_appendf( &buffer, "\n" ); + vips_dbuf_appendf( &dbuf, " \n" ); + vips_dbuf_appendf( &dbuf, "\n" ); - return( buffer.data ); + return( vips_dbuf_steal( &dbuf, NULL ) ); } /* Append XML to output fd.