/* get, set and copy image header fields
 *
 * 9/7/02 JC
 *	- first version
 * 7/6/05
 * 	- now reads meta fields too
 * 	- cleaned up
 *	- added im_header_exists(), im_header_map()
 * 1/8/05
 * 	- now im_header_get_type() and im_header_get() rather than
 * 	  im_header_exists()
 * 4/1/07
 * 	- removed Hist from standard fields ... now a separate function
 * 29/8/09
 * 	- im_header_get_type() renamed as im_header_get_typeof() to prevent
 * 	  confusion with GObject-style type definers
 * 1/10/09
 * 	- rename as header.c
 * 	- gtkdoc comments
 * 22/3/11
 * 	- rename fields for vips8
 * 	- move to vips_ prefix
 * 16/7/15
 * 	- auto wrap GString as RefString
 * 20/10/16
 * 	- return header enums as enums, not ints
 * 	- vips_image_get_*() all convert everything to target type if they can
 * 	- rename "field" as "name" in docs
 * 21/11/18
 * 	- get_string will allow G_STRING and REF_STRING
 * 28/12/18
 * 	- hide deprecated header fields from _map
 * 17/2/19
 * 	- add vips_image_get_page_height()
 * 19/6/19
 * 	- add vips_image_get_n_pages()
 * 20/6/19
 * 	- add vips_image_get/set_array_int()
 * 31/1/19
 * 	- lock for metadata changes
 */

/*

    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

 */

/*
#define VIPS_DEBUG
#define DEBUG
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /*HAVE_CONFIG_H*/
#include <vips/intl.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <vips/vips.h>
#include <vips/internal.h>
#include <vips/debug.h>

/**
 * SECTION: header
 * @short_description: get, set and walk image headers
 * @stability: Stable
 * @see_also: <link linkend="libvips-type">type</link>
 * @include: vips/vips.h
 *
 * These functions let you get at image header data (including metadata) in a
 * uniform way. 
 *
 * Use vips_image_get_typeof() to test for the 
 * existence and #GType of a header field.
 *
 * You can attach arbitrary metadata to images. Metadata is copied as images
 * are processed, so all images which used this image as input, directly or
 * indirectly, will have this same bit of metadata attached to them. Copying
 * is implemented with reference-counted pointers, so it is efficient, even for
 * large items of data. This does however mean that metadata items need to be
 * immutable. Metadata is handy for things like ICC profiles or EXIF data.
 *
 * Various convenience functions (eg. vips_image_set_int()) let you easily 
 * attach 
 * simple types like
 * numbers, strings and memory blocks to images. Use vips_image_map() to loop
 * over an image's fields, including all metadata.
 *
 * Items of metadata are identified by strings. Some strings are reserved, for
 * example the ICC profile for an image is known by convention as
 * "icc-profile-data".
 *
 * If you save an image in VIPS format, all metadata (with a restriction, see
 * below) is automatically saved for you in a block of XML at the end of the
 * file. When you load a VIPS image, the metadata is restored. You can use the
 * `vipsedit` command-line tool to extract or replace this block of XML.
 *
 * VIPS metadata is based on %GValue. See the docs for that system if you want
 * to do fancy stuff such as defining a new metadata type.
 * VIPS defines a new %GValue called `vips_save_string`, a variety of string,
 * see vips_value_set_save_string(). 
 * If your %GValue can be transformed to `vips_save_string`, it will be 
 * saved and loaded to and from VIPS files for you.
 *
 * VIPS provides a couple of base classes which implement
 * reference-counted areas of memory. If you base your metadata on one of
 * these types, it can be copied between images efficiently.
 */

/* Use in various small places where we need a mutex and it's not worth 
 * making a private one.
 */
static GMutex *vips__meta_lock = NULL;

/* We have to keep the gtype as a string, since we statically init this.
 */
typedef struct _HeaderField {
	const char *name;
	const char *type;
	glong offset;
} HeaderField;

/* Built in fields and struct offsets.
 */
static HeaderField vips_header_fields[] = {
	{ "width", "gint", G_STRUCT_OFFSET( VipsImage, Xsize ) },
	{ "height", "gint", G_STRUCT_OFFSET( VipsImage, Ysize ) },
	{ "bands", "gint", G_STRUCT_OFFSET( VipsImage, Bands ) },
	{ "format", "VipsBandFormat", G_STRUCT_OFFSET( VipsImage, BandFmt ) },
	{ "coding", "VipsCoding", G_STRUCT_OFFSET( VipsImage, Coding ) },
	{ "interpretation", "VipsInterpretation", 
		G_STRUCT_OFFSET( VipsImage, Type ) },
	{ "xoffset", "gint", G_STRUCT_OFFSET( VipsImage, Xoffset ) },
	{ "yoffset", "gint", G_STRUCT_OFFSET( VipsImage, Yoffset ) },
	{ "xres", "gdouble", G_STRUCT_OFFSET( VipsImage, Xres ) },
	{ "yres", "gdouble", G_STRUCT_OFFSET( VipsImage, Yres ) },
	{ "filename", "gchararray", G_STRUCT_OFFSET( VipsImage, filename ) }
};

/* Old names we keep around for back-compat. We never loop over these with
 * map, but we do check them when we look up fields by name.
 */
static HeaderField vips_header_fields_old[] = {
	{ "Xsize", "gint", G_STRUCT_OFFSET( VipsImage, Xsize ) },
	{ "Ysize", "gint", G_STRUCT_OFFSET( VipsImage, Ysize ) },
	{ "Bands", "gint", G_STRUCT_OFFSET( VipsImage, Bands ) },
	{ "Bbits", "gint", G_STRUCT_OFFSET( VipsImage, Bbits ) },
	{ "BandFmt", "gint", G_STRUCT_OFFSET( VipsImage, BandFmt ) },
	{ "Coding", "gint", G_STRUCT_OFFSET( VipsImage, Coding ) },
	{ "Type", "gint", G_STRUCT_OFFSET( VipsImage, Type ) },
	{ "Xoffset", "gint", G_STRUCT_OFFSET( VipsImage, Xoffset ) },
	{ "Yoffset", "gint", G_STRUCT_OFFSET( VipsImage, Yoffset ) },
	{ "Xres", "gdouble", G_STRUCT_OFFSET( VipsImage, Xres ) },
	{ "Yres", "gdouble", G_STRUCT_OFFSET( VipsImage, Yres ) }
};

/* This is used by (eg.) VIPS_IMAGE_SIZEOF_ELEMENT() to calculate object
 * size via vips_format_sizeof(). 
 *
 * It needs to be guint64 and not size_t since we use this as the basis for 
 * image address calcs and they have to be 64-bit, even on 32-bit machines. 
 *
 * Can't be static, we need this to be visible for vips7 compat.
 */
const guint64 vips__image_sizeof_bandformat[] = {
	sizeof( unsigned char ), 	/* VIPS_FORMAT_UCHAR */
	sizeof( signed char ), 		/* VIPS_FORMAT_CHAR */
	sizeof( unsigned short ), 	/* VIPS_FORMAT_USHORT */
	sizeof( unsigned short ), 	/* VIPS_FORMAT_SHORT */
	sizeof( unsigned int ), 	/* VIPS_FORMAT_UINT */
	sizeof( unsigned int ), 	/* VIPS_FORMAT_INT */
	sizeof( float ), 		/* VIPS_FORMAT_FLOAT */
	2 * sizeof( float ), 		/* VIPS_FORMAT_COMPLEX */
	sizeof( double ), 		/* VIPS_FORMAT_DOUBLE */
	2 * sizeof( double ) 		/* VIPS_FORMAT_DPCOMPLEX */
};

/**
 * vips_format_sizeof:
 * @format: format type
 *
 * Returns: number of bytes for a band format.
 */
guint64 
vips_format_sizeof( VipsBandFormat format )
{
	format = VIPS_CLIP( 0, format, VIPS_FORMAT_DPCOMPLEX ); 

	return( vips__image_sizeof_bandformat[format] );
}

/**
 * vips_format_sizeof_unsafe: (skip)
 * @format: format type
 *
 * A fast but dangerous version of vips_format_sizeof(). You must have
 * previously range-checked @format or you'll crash.
 *
 * Returns: number of bytes for a band format.
 */
guint64 
vips_format_sizeof_unsafe( VipsBandFormat format )
{
	g_assert( 0 <= format && format <= VIPS_FORMAT_DPCOMPLEX ); 

	return( vips__image_sizeof_bandformat[format] );
}

#ifdef DEBUG
/* Check that this meta is on the hash table.
 */
static void *
meta_sanity_on_hash( VipsMeta *meta, VipsImage *im, void *b )
{
	VipsMeta *found;

	if( meta->im != im )
		printf( "*** field \"%s\" has incorrect im\n", 
			meta->name );

	if( !(found = g_hash_table_lookup( im->meta, meta->name )) )
		printf( "*** field \"%s\" is on traverse but not in hash\n", 
			meta->name );

	if( found != meta )
		printf(  "*** meta \"%s\" on traverse and hash do not match\n", 
			meta->name );

	return( NULL );
}

static void
meta_sanity_on_traverse( const char *name, VipsMeta *meta, VipsImage *im )
{
	if( meta->name != name )
		printf( "*** field \"%s\" has incorrect name\n", 
			meta->name );

	if( meta->im != im )
		printf( "*** field \"%s\" has incorrect im\n", 
			meta->name );

	if( !g_slist_find( im->meta_traverse, meta ) )
		printf( "*** field \"%s\" is in hash but not on traverse\n", 
			meta->name );
}

static void
meta_sanity( const VipsImage *im )
{
	if( im->meta )
		g_hash_table_foreach( im->meta, 
			(GHFunc) meta_sanity_on_traverse, (void *) im );
	vips_slist_map2( im->meta_traverse, 
		(VipsSListMap2Fn) meta_sanity_on_hash, (void *) im, NULL );
}
#endif /*DEBUG*/

static void
meta_free( VipsMeta *meta )
{
#ifdef DEBUG
{
	char *str_value;

	str_value = g_strdup_value_contents( &meta->value );
	printf( "meta_free: name %s, value = %s\n", 
		meta->name, str_value );
	g_free( str_value );
}
#endif /*DEBUG*/

	if( meta->im )
		meta->im->meta_traverse = 
			g_slist_remove( meta->im->meta_traverse, meta );

	g_value_unset( &meta->value );
	g_free( meta->name );
	g_free( meta );
}

static VipsMeta *
meta_new( VipsImage *image, const char *name, GValue *value )
{
	VipsMeta *meta;

	meta = g_new( VipsMeta, 1 );
	meta->im = image;
	meta->name = NULL;
	memset( &meta->value, 0, sizeof( GValue ) );
	meta->name = g_strdup( name );

	/* Special case: we don't want to have G_STRING on meta. They will be
	 * copied down pipelines, plus some of our API (like
	 * vips_image_get_string()) assumes that the GValue is a refstring and
	 * that read-only pointers can be handed out.
	 *
	 * Turn G_TYPE_STRING into VIPS_TYPE_REF_STRING.
	 */
	if( G_VALUE_TYPE( value ) == G_TYPE_STRING )
		g_value_init( &meta->value, VIPS_TYPE_REF_STRING );
	else
		g_value_init( &meta->value, G_VALUE_TYPE( value ) );

	/* We don't do any conversions that can fail.
	 */
	(void) g_value_transform( value, &meta->value );

	image->meta_traverse = g_slist_append( image->meta_traverse, meta );
	g_hash_table_replace( image->meta, meta->name, meta ); 

#ifdef DEBUG
{
	char *str_value;

	str_value = g_strdup_value_contents( value );
	printf( "meta_new: name %s, value = %s\n", name, str_value );
	g_free( str_value );
}
#endif /*DEBUG*/

	return( meta );
}

/* Destroy all the meta on an image.
 */
void
vips__meta_destroy( VipsImage *image )
{
	VIPS_FREEF( g_hash_table_destroy, image->meta );
	g_assert( !image->meta_traverse );
}

static void
meta_init( VipsImage *im )
{
	if( !im->meta ) {
		g_assert( !im->meta_traverse );
		im->meta = g_hash_table_new_full( g_str_hash, g_str_equal,
			NULL, (GDestroyNotify) meta_free );
	}
}

/**
 * vips_image_get_width: (method)
 * @image: image to get from
 *
 * Returns: the number of pixels across the image.
 */
int
vips_image_get_width( const VipsImage *image )
{
	return( image->Xsize );
}

/**
 * vips_image_get_height: (method)
 * @image: image to get from
 *
 * Returns: the number of pixels down the image.
 */
int
vips_image_get_height( const VipsImage *image )
{
	return( image->Ysize );
}

/**
 * vips_image_get_bands: (method)
 * @image: image to get from
 *
 * Returns: the number of bands (channels) in the image.
 */
int
vips_image_get_bands( const VipsImage *image )
{
	return( image->Bands );
}

/**
 * vips_image_get_format: (method)
 * @image: image to get from
 *
 * Returns: the format of each band element.
 */
VipsBandFormat
vips_image_get_format( const VipsImage *image )
{
	return( image->BandFmt );
}

/**
 * vips_image_get_format_max: (method)
 * @image: image to get from
 *
 * Returns: the maximum numeric value possible for this format.
 */
double
vips_image_get_format_max( VipsBandFormat format )
{
	switch( format ) {
	case VIPS_FORMAT_UCHAR:
		return( UCHAR_MAX );

	case VIPS_FORMAT_CHAR:
		return( SCHAR_MAX );

	case VIPS_FORMAT_USHORT:
		return( USHRT_MAX );

	case VIPS_FORMAT_SHORT:
		return( SHRT_MAX );

	case VIPS_FORMAT_UINT:
		return( UINT_MAX );

	case VIPS_FORMAT_INT:
		return( INT_MAX );

	case VIPS_FORMAT_FLOAT:
	case VIPS_FORMAT_COMPLEX:
		return( FLT_MAX );

	case VIPS_FORMAT_DOUBLE:
	case VIPS_FORMAT_DPCOMPLEX:
		return( DBL_MAX );

	default:
		return( -1 );
	}
}

/**
 * vips_image_guess_format: (method)
 * @image: image to guess for
 *
 * Return the #VipsBandFormat for an image, guessing a sane value if
 * the set value looks crazy.
 *
 * For example, for a float image tagged as rgb16, we'd return ushort. 
 *
 * Returns: a sensible #VipsBandFormat for the image.
 */
VipsBandFormat
vips_image_guess_format( const VipsImage *image )
{
	VipsBandFormat format;

	/* Stop a compiler warning.
	 */
	format = VIPS_FORMAT_UCHAR; 

	switch( image->Type ) {
	case VIPS_INTERPRETATION_B_W: 
	case VIPS_INTERPRETATION_HISTOGRAM: 
	case VIPS_INTERPRETATION_MULTIBAND: 
		format = image->BandFmt;
		break;

	case VIPS_INTERPRETATION_FOURIER: 
		if( image->BandFmt == VIPS_FORMAT_DOUBLE ||
			image->BandFmt == VIPS_FORMAT_DPCOMPLEX )
			format = VIPS_FORMAT_DPCOMPLEX;
		else
			format = VIPS_FORMAT_COMPLEX;
		break;

	case VIPS_INTERPRETATION_sRGB: 
	case VIPS_INTERPRETATION_RGB: 
		format = VIPS_FORMAT_UCHAR;
		break;

	case VIPS_INTERPRETATION_XYZ: 
	case VIPS_INTERPRETATION_LAB: 
	case VIPS_INTERPRETATION_CMC: 
	case VIPS_INTERPRETATION_LCH: 
	case VIPS_INTERPRETATION_HSV: 
	case VIPS_INTERPRETATION_scRGB: 
	case VIPS_INTERPRETATION_YXY: 
		format = VIPS_FORMAT_FLOAT;
		break;

	case VIPS_INTERPRETATION_CMYK: 
		if( image->BandFmt == VIPS_FORMAT_USHORT )
			format = VIPS_FORMAT_USHORT;
		else
			format = VIPS_FORMAT_UCHAR;
		break;

	case  VIPS_INTERPRETATION_LABQ:
		format = VIPS_FORMAT_UCHAR;
		break;

	case  VIPS_INTERPRETATION_LABS:
		format = VIPS_FORMAT_SHORT;
		break;

	case  VIPS_INTERPRETATION_GREY16:
	case  VIPS_INTERPRETATION_RGB16:
		format = VIPS_FORMAT_USHORT;
		break;

	case  VIPS_INTERPRETATION_MATRIX:
		if( image->BandFmt == VIPS_FORMAT_DOUBLE )
			format = VIPS_FORMAT_DOUBLE;
		else
			format = VIPS_FORMAT_FLOAT;
		break;

	default:
		format = VIPS_FORMAT_NOTSET;
		break; 
	}

	return( format );
}

/**
 * vips_image_get_coding: (method)
 * @image: image to get from
 *
 * Returns: the image coding
 */
VipsCoding
vips_image_get_coding( const VipsImage *image )
{
	return( image->Coding );
}

/**
 * vips_image_get_interpretation: (method)
 * @image: image to get from
 *
 * Return the #VipsInterpretation set in the image header.
 * Use vips_image_guess_interpretation() if you want a sanity-checked value.
 *
 * Returns: the #VipsInterpretation from the image header.
 */
VipsInterpretation
vips_image_get_interpretation( const VipsImage *image )
{
	return( image->Type );
}

/* Try to guess a sane value for interpretation.
 */
static VipsInterpretation
vips_image_default_interpretation( const VipsImage *image )
{
	switch( image->Coding ) {
	case VIPS_CODING_LABQ:
		return( VIPS_INTERPRETATION_LABQ );
	case VIPS_CODING_RAD:
		return( VIPS_INTERPRETATION_sRGB );
	default:
		break;
	}

	/* 1 and 2 bands -> greyscale. The extra band could be alpha.
	 */
	if( image->Bands < 3 ) {
		if( image->BandFmt == VIPS_FORMAT_USHORT )
			return( VIPS_INTERPRETATION_GREY16 );
		else
			return( VIPS_INTERPRETATION_B_W );
	} 
	else {
		/* 3 or more -> some sort of RGB. You'll need to set CMYK 
		 * explicitly if you want that.
		 */
		if( image->BandFmt == VIPS_FORMAT_USHORT )
			return( VIPS_INTERPRETATION_RGB16 );
		else
			return( VIPS_INTERPRETATION_sRGB );
	}
}

/**
 * vips_image_guess_interpretation: (method)
 * @image: image to guess for
 *
 * Return the #VipsInterpretation for an image, guessing a sane value if
 * the set value looks crazy.
 *
 * Returns: a sensible #VipsInterpretation for the image.
 */
VipsInterpretation
vips_image_guess_interpretation( const VipsImage *image )
{
	gboolean sane;

	sane = TRUE;

	/* Coding overrides interpretation.
	 */
	switch( image->Coding ) {
	case VIPS_CODING_ERROR:
		sane = FALSE;
		break;

	case VIPS_CODING_LABQ:
		if( image->Type != VIPS_INTERPRETATION_LABQ )
			sane = FALSE;
		break;

	case VIPS_CODING_RAD:
		if( image->Type != VIPS_INTERPRETATION_sRGB )
			sane = FALSE;
		break;

	default:
		break;
	}

	switch( image->Type ) {
	case VIPS_INTERPRETATION_ERROR:
		sane = FALSE;
		break;

	case VIPS_INTERPRETATION_MULTIBAND: 
		/* This is a pretty useless generic tag. Always reset it.
		 */
		sane = FALSE;
		break;

	case VIPS_INTERPRETATION_B_W: 
		/* Don't test bands, we allow bands after the first to be
		 * unused extras, like alpha.
		 */
		break;

	case VIPS_INTERPRETATION_HISTOGRAM: 
		if( image->Xsize > 1 && image->Ysize > 1 )
			sane = FALSE;
		break;

	case VIPS_INTERPRETATION_FOURIER: 
		if( !vips_band_format_iscomplex( image->BandFmt ) )
			sane = FALSE;
		break;

	case VIPS_INTERPRETATION_XYZ: 
	case VIPS_INTERPRETATION_LAB: 
	case VIPS_INTERPRETATION_RGB: 
	case VIPS_INTERPRETATION_CMC: 
	case VIPS_INTERPRETATION_LCH: 
	case VIPS_INTERPRETATION_sRGB: 
	case VIPS_INTERPRETATION_HSV: 
	case VIPS_INTERPRETATION_scRGB: 
	case VIPS_INTERPRETATION_YXY: 
		if( image->Bands < 3 )
			sane = FALSE;
		break;

	case VIPS_INTERPRETATION_CMYK: 
		if( image->Bands < 4 )
			sane = FALSE;
		break;

	case  VIPS_INTERPRETATION_LABQ:
		if( image->Coding != VIPS_CODING_LABQ )
			sane = FALSE;
		break;

	case  VIPS_INTERPRETATION_LABS:
		if( image->BandFmt != VIPS_FORMAT_SHORT )
			sane = FALSE;
		break;

	case  VIPS_INTERPRETATION_RGB16:
		if( image->BandFmt == VIPS_FORMAT_CHAR ||
			image->BandFmt == VIPS_FORMAT_UCHAR ||
			image->Bands < 3 )
			sane = FALSE;
		break;

	case  VIPS_INTERPRETATION_GREY16:
		if( image->BandFmt == VIPS_FORMAT_CHAR ||
			image->BandFmt == VIPS_FORMAT_UCHAR )
			sane = FALSE;
		break;

	case  VIPS_INTERPRETATION_MATRIX:
		if( image->Bands != 1 )
			sane = FALSE;
		break;

	default:
		g_assert_not_reached();
	}

	if( sane )
		return( vips_image_get_interpretation( image ) );
	else
		return( vips_image_default_interpretation( image ) );
}

/**
 * vips_image_get_xres: (method)
 * @image: image to get from
 *
 * Returns: the horizontal image resolution in pixels per millimeter. 
 */
double
vips_image_get_xres( const VipsImage *image )
{
	return( image->Xres );
}

/**
 * vips_image_get_yres: (method)
 * @image: image to get from
 *
 * Returns: the vertical image resolution in pixels per millimeter. 
 */
double
vips_image_get_yres( const VipsImage *image )
{
	return( image->Yres );
}

/**
 * vips_image_get_xoffset: (method)
 * @image: image to get from
 *
 * Returns: the horizontal position of the image origin, in pixels.
 */
int
vips_image_get_xoffset( const VipsImage *image )
{
	return( image->Xoffset );
}

/**
 * vips_image_get_yoffset: (method)
 * @image: image to get from
 *
 * Returns: the vertical position of the image origin, in pixels.
 */
int
vips_image_get_yoffset( const VipsImage *image )
{
	return( image->Yoffset );
}

/**
 * vips_image_get_filename: (method)
 * @image: image to get from
 *
 * Returns: the name of the file the image was loaded from, or NULL if there
 * is no filename.
 */
const char *
vips_image_get_filename( const VipsImage *image )
{
	return( image->filename );
}

/**
 * vips_image_get_mode: (method)
 * @image: image to get from
 *
 * Image modes are things like `"t"`, meaning a memory buffer, and `"p"`
 * meaning a delayed computation. 
 *
 * Returns: the image mode.
 */
const char *
vips_image_get_mode( const VipsImage *image )
{
	return( image->mode );
}

/**
 * vips_image_get_scale: (method)
 * @image: image to get from
 *
 * Matrix images can have an optional `scale` field for use by integer 
 * convolution. 
 *
 * Returns: the scale.
 */
double
vips_image_get_scale( const VipsImage *image )
{
	double scale;

	scale = 1.0;
	if( vips_image_get_typeof( image, "scale" ) ) 
		vips_image_get_double( image, "scale", &scale );

	return( scale );
}

/**
 * vips_image_get_offset: (method)
 * @image: image to get from
 *
 * Matrix images can have an optional `offset` field for use by integer 
 * convolution. 
 *
 * Returns: the offset.
 */
double
vips_image_get_offset( const VipsImage *image )
{
	double offset;

	offset = 0.0;
	if( vips_image_get_typeof( image, "offset" ) ) 
		vips_image_get_double( image, "offset", &offset );

	return( offset );
}

/**
 * vips_image_get_page_height: (method)
 * @image: image to get from
 *
 * Multi-page images can have a page height. Fetch it, and sanity check it. If
 * page-height is not set, it defaults to the image height.
 *
 * Returns: the page height.
 */
int
vips_image_get_page_height( VipsImage *image )
{
	int page_height;

	if( vips_image_get_typeof( image, VIPS_META_PAGE_HEIGHT ) &&
		!vips_image_get_int( image, VIPS_META_PAGE_HEIGHT, 
			&page_height ) &&
		page_height > 0 &&
		page_height < image->Ysize &&
		image->Ysize % page_height == 0 ) 
		return( page_height );

	return( image->Ysize );
}

/**
 * vips_image_get_n_pages: (method)
 * @image: image to get from
 *
 * Fetch and sanity-check #VIPS_META_N_PAGES. Default to 1 if not present or
 * crazy.
 *
 * This is the number of pages in the image file, not the number of pages that
 * have been loaded into @image.
 *
 * Returns: the number of pages in the image file
 */
int
vips_image_get_n_pages( VipsImage *image )
{
	int n_pages;

	if( vips_image_get_typeof( image, VIPS_META_N_PAGES ) &&
		!vips_image_get_int( image, VIPS_META_N_PAGES, &n_pages ) &&
		n_pages > 1 &&
		n_pages < 10000 )
		return( n_pages );

	return(	1 );
}

/**
 * vips_image_get_n_subifds: (method)
 * @image: image to get from
 *
 * Fetch and sanity-check #VIPS_META_N_SUBIFDS. Default to 0 if not present or
 * crazy.
 *
 * Returns: the number of subifds in the image file
 */
int
vips_image_get_n_subifds( VipsImage *image )
{
	int n_subifds;

	if( vips_image_get_typeof( image, VIPS_META_N_SUBIFDS ) &&
		!vips_image_get_int( image, VIPS_META_N_SUBIFDS, &n_subifds ) &&
		n_subifds > 1 &&
		n_subifds < 1000 )
		return( n_subifds );

	return(	0 );
}

/**
 * vips_image_get_orientation: (method)
 * @image: image to get from
 *
 * Fetch and sanity-check #VIPS_META_ORIENTATION. Default to 1 (no rotate, 
 * no flip) if not present or crazy.
 *
 * Returns: the image orientation.
 */
int
vips_image_get_orientation( VipsImage *image )
{
	int orientation;

	if( vips_image_get_typeof( image, VIPS_META_ORIENTATION ) &&
		!vips_image_get_int( image, VIPS_META_ORIENTATION, 
			&orientation ) &&
		orientation > 0 &&
		orientation < 9 )
		return( orientation );

	return( 1 );
}

/**
 * vips_image_get_orientation_swap: (method)
 * @image: image to get from
 *
 * Return %TRUE if applying the orientation would swap width and height.
 *
 * Returns: if width/height will swap
 */
gboolean
vips_image_get_orientation_swap( VipsImage *image )
{
	int orientation = vips_image_get_orientation( image );

	return( orientation >= 5 &&
		orientation <= 8 );
}

/**
 * vips_image_get_data: (method)
 * @image: image to get data for
 *
 * Return a pointer to the image's pixel data, if possible. This can involve
 * allocating large amounts of memory and performing a long computation. Image
 * pixels are laid out in band-packed rows.
 *
 * Since this function modifies @image, it is not threadsafe. Only call it on
 * images which you are sure have not been shared with another thread. 
 *
 * See also: vips_image_wio_input(), vips_image_copy_memory().
 *
 * Returns: (transfer none): a pointer to pixel data, if possible.
 */
const void *
vips_image_get_data( VipsImage *image )
{
	if( vips_image_wio_input( image ) )
		return( NULL );

	return( image->data ); 
}

/**
 * vips_image_init_fields: (method)
 * @image: image to init
 * @xsize: image width
 * @ysize: image height
 * @bands: image bands
 * @format: band format
 * @coding: image coding
 * @interpretation: image type
 * @xres: horizontal resolution, pixels per millimetre
 * @yres: vertical resolution, pixels per millimetre
 *
 * A convenience function to set the header fields after creating an image.
 * Normally you copy the fields from your input images with
 * vips_image_pipelinev() and then make
 * any adjustments you need, but if you are creating an image from scratch,
 * for example vips_black() or vips_jpegload(), you do need to set all the
 * fields yourself.
 *
 * See also: vips_image_pipelinev().
 */
void 
vips_image_init_fields( VipsImage *image, 
	int xsize, int ysize, int bands, 
	VipsBandFormat format, VipsCoding coding, 
	VipsInterpretation interpretation, 
	double xres, double yres )
{
	g_object_set( image,
		"width", xsize,
		"height", ysize,
		"bands", bands,
		"format", format,
		NULL );

	image->Coding = coding;
	image->Type = interpretation;
	image->Xres = xres;
	image->Yres = yres;
}

static void *
meta_cp_field( VipsMeta *meta, VipsImage *dst, void *b )
{
#ifdef DEBUG
{
	char *str_value;

	str_value = g_strdup_value_contents( &meta->value );
	printf( "vips__meta_cp: copying name %s, value = %s\n", 
		meta->name, str_value );
	g_free( str_value );
}
#endif /*DEBUG*/

	(void) meta_new( dst, meta->name, &meta->value );

#ifdef DEBUG
	meta_sanity( dst );
#endif /*DEBUG*/

	return( NULL );
}

/* Copy meta on to dst. 
 */
int
vips__image_meta_copy( VipsImage *dst, const VipsImage *src )
{
	if( src->meta ) {
		/* We lock with vips_image_set() to stop races in highly-
		 * threaded applications.
		 */
		g_mutex_lock( vips__meta_lock );
		meta_init( dst );
		vips_slist_map2( src->meta_traverse,
			(VipsSListMap2Fn) meta_cp_field, dst, NULL );
		g_mutex_unlock( vips__meta_lock );
	}

	return( 0 );
}

/* We have to have this as a separate entry point so we can support the old
 * vips7 API.
 */
int 
vips__image_copy_fields_array( VipsImage *out, VipsImage *in[] )
{
	int i;
	int ni;

	g_assert( in[0] );

	/* Copy magic too, handy for knowing the original image's byte order.
	 */
	out->magic = in[0]->magic;

	out->Xsize = in[0]->Xsize;
	out->Ysize = in[0]->Ysize;
	out->Bands = in[0]->Bands;
	out->Bbits = in[0]->Bbits;
	out->BandFmt = in[0]->BandFmt;
	out->Type = in[0]->Type;
	out->Coding = in[0]->Coding;
	out->Xres = in[0]->Xres;
	out->Yres = in[0]->Yres;
	out->Xoffset = in[0]->Xoffset;
	out->Yoffset = in[0]->Yoffset;

	/* Count number of images.
	 */
	for( ni = 0; in[ni]; ni++ ) 
		;

	/* Need to copy last-to-first so that in0 meta will override any
	 * earlier meta.
	 *
	 * Don't destroy the meta on out. Things like foreign.c like setting
	 * image properties before calling a subclass loader, and those
	 * subclass loaders will sometimes write to an image. 
	 */
	for( i = ni - 1; i >= 0; i-- ) 
		if( vips__image_meta_copy( out, in[i] ) )
			return( -1 );

	/* Merge hists first to last.
	 */
	for( i = 0; in[i]; i++ )
		out->history_list = vips__gslist_gvalue_merge( 
			out->history_list, in[i]->history_list );

	return( 0 );
}

/** 
 * vips_image_set: (method)
 * @image: image to set the metadata on
 * @name: the name to give the metadata
 * @value: the %GValue to copy into the image
 *
 * Set a piece of metadata on @image. Any old metadata with that name is
 * destroyed. The %GValue is copied into the image, so you need to unset the
 * value when you're done with it.
 *
 * For example, to set an integer on an image (though you would use the
 * convenience function vips_image_set_int() in practice), you would do:
 *
 * |[
 * GValue value = { 0 };
 *
 * g_value_init (&value, G_TYPE_INT);
 * g_value_set_int (&value, 42);
 * vips_image_set (image, name, &value);
 * g_value_unset (&value);
 * ]|
 *
 * See also: vips_image_get().
 */
void
vips_image_set( VipsImage *image, const char *name, GValue *value )
{
	g_assert( name );
	g_assert( value );

	/* We lock between modifying metadata and copying metadata between
	 * images, see vips__image_meta_copy().
	 *
	 * This prevents modification of metadata by one thread racing with
	 * metadata copy on another -- this can lead to crashes in
	 * highly-threaded applications.
	 */
	g_mutex_lock( vips__meta_lock );
	meta_init( image );
	(void) meta_new( image, name, value );
	g_mutex_unlock( vips__meta_lock );

	/* If we're setting an EXIF data block, we need to automatically expand 
	 * out all the tags. This will set things like xres/yres too.
	 *
	 * We do this here rather than in meta_new() since we don't want to
	 * trigger on copy_fields.
	 */
	if( strcmp( name, VIPS_META_EXIF_NAME ) == 0 ) 
		if( vips__exif_parse( image ) ) 
			g_warning( "image_set: bad exif data" );

#ifdef DEBUG
	meta_sanity( image );
#endif /*DEBUG*/

}

/* Unforunately gvalue seems to have no way of doing this. Just handle the vips
 * built-in types.
 */
static void
vips_set_value_from_pointer( GValue *value, void *data )
{
	GType type = G_VALUE_TYPE( value );

	/* The fundamental type ... eg. G_TYPE_ENUM for a VIPS_TYPE_KERNEL,
	 * or G_TYPE_OBJECT for VIPS_TYPE_IMAGE().
	 */
	GType fundamental = G_TYPE_FUNDAMENTAL( type );

	if( fundamental == G_TYPE_INT )
		g_value_set_int( value, *((int *) data) ); 
	else if( fundamental == G_TYPE_DOUBLE )
		g_value_set_double( value, *((double *) data) ); 
	else if( fundamental == G_TYPE_ENUM )
		g_value_set_enum( value, *((int *) data) ); 
	else if( fundamental == G_TYPE_STRING ) 
		g_value_set_string( value, *((char **) data) ); 
	else
		g_warning( "%s: unimplemented vips_set_value_from_pointer() "
			"type %s",
			G_STRLOC,
			g_type_name( type ) );
}

/**
 * vips_image_get: (method)
 * @image: image to get the field from from
 * @name: the name to fetch
 * @value_copy: (transfer full) (out caller-allocates): the %GValue is copied into this
 *
 * Fill @value_copy with a copy of the header field. @value_copy must be zeroed 
 * but uninitialised.
 *
 * This will return -1 and add a message to the error buffer if the field
 * does not exist. Use vips_image_get_typeof() to test for the 
 * existence of a field first if you are not certain it will be there.
 *
 * For example, to read a double from an image (though of course you would use
 * vips_image_get_double() in practice):
 *
 * |[
 * GValue value = { 0 };
 * double d;
 *
 * if (vips_image_get (image, name, &value))
 *   return -1;
 *
 * if (G_VALUE_TYPE (&value) != G_TYPE_DOUBLE) {
 *   vips_error( "mydomain", 
 *     _("field \"%s\" is of type %s, not double"),
 *     name, 
 *     g_type_name (G_VALUE_TYPE (&value)));
 *   g_value_unset (&value);
 *   return -1;
 * }
 *
 * d = g_value_get_double (&value);
 * g_value_unset (&value);
 * ]|
 *
 * See also: vips_image_get_typeof(), vips_image_get_double().
 *
 * Returns: (skip): 0 on success, -1 otherwise.
 */
int
vips_image_get( const VipsImage *image, const char *name, GValue *value_copy )
{
	int i;
	VipsMeta *meta;

	g_assert( name );
	g_assert( value_copy );

	for( i = 0; i < VIPS_NUMBER( vips_header_fields ); i++ ) {
		HeaderField *field = &vips_header_fields[i];

		if( strcmp( field->name, name ) == 0 ) {
			GType gtype = g_type_from_name( field->type );

			g_value_init( value_copy, gtype );
			vips_set_value_from_pointer( value_copy, 
				G_STRUCT_MEMBER_P( image, field->offset ) );
			return( 0 );
		}
	}

	for( i = 0; i < VIPS_NUMBER( vips_header_fields_old ); i++ ) {
		HeaderField *field = &vips_header_fields_old[i];

		if( strcmp( field->name, name ) == 0 ) {
			GType gtype = g_type_from_name( field->type );

			g_value_init( value_copy, gtype );
			vips_set_value_from_pointer( value_copy, 
				G_STRUCT_MEMBER_P( image, field->offset ) );
			return( 0 );
		}
	}

	if( image->meta && 
		(meta = g_hash_table_lookup( image->meta, name )) ) {
		g_value_init( value_copy, G_VALUE_TYPE( &meta->value ) );
		g_value_copy( &meta->value, value_copy );

		return( 0 );
	}

	vips_error( "vips_image_get", _( "field \"%s\" not found" ), name );

	return( -1 );
}

/**
 * vips_image_get_typeof: (method)
 * @image: image to test
 * @name: the name to search for
 *
 * Read the %GType for a header field. Returns zero if there is no
 * field of that name. 
 *
 * See also: vips_image_get().
 *
 * Returns: the %GType of the field, or zero if there is no
 * field of that name.
 */
GType 
vips_image_get_typeof( const VipsImage *image, const char *name )
{
	int i;
	VipsMeta *meta;

	g_assert( name );

	for( i = 0; i < VIPS_NUMBER( vips_header_fields ); i++ ) {
		HeaderField *field = &vips_header_fields[i];

		if( strcmp( field->name, name ) == 0 ) 
			return( g_type_from_name( field->type ) ); 
	}

	for( i = 0; i < VIPS_NUMBER( vips_header_fields_old ); i++ ) {
		HeaderField *field = &vips_header_fields_old[i];

		if( strcmp( field->name, name ) == 0 ) 
			return( g_type_from_name( field->type ) ); 
	}

	if( image->meta && 
		(meta = g_hash_table_lookup( image->meta, name )) ) 
		return( G_VALUE_TYPE( &meta->value ) );

	VIPS_DEBUG_MSG( "vips_image_get_typeof: unknown field %s\n", name );

	return( 0 );
}

/**
 * vips_image_remove: (method)
 * @image: image to test
 * @name: the name to search for
 *
 * Find and remove an item of metadata. Return %FALSE if no metadata of that
 * name was found.
 *
 * See also: vips_image_set(), vips_image_get_typeof().
 *
 * Returns: %TRUE if an item of metadata of that name was found and removed
 */
gboolean
vips_image_remove( VipsImage *image, const char *name )
{
	gboolean result;

	result = FALSE;

	if( image->meta ) {
		/* We lock between modifying metadata and copying metadata 
		 * between images, see vips__image_meta_copy().
		 *
		 * This prevents modification of metadata by one thread 
		 * racing with metadata copy on another -- this can lead to 
		 * crashes in highly-threaded applications.
		 */
		g_mutex_lock( vips__meta_lock );
		result = g_hash_table_remove( image->meta, name );
		g_mutex_unlock( vips__meta_lock );
	}

	return( result );
}

/* Deprecated header fields we hide from _map.
 */
static const char *vips_image_header_deprecated[] = {
	"ipct-data",
	"gif-delay",
	"gif-loop"
};

static void *
vips_image_map_fn( VipsMeta *meta, VipsImageMapFn fn, void *a )
{
	int i;

	/* Hide deprecated fields.
	 */
	for( i = 0; i < VIPS_NUMBER( vips_image_header_deprecated ); i++ )
		if( strcmp( meta->name, vips_image_header_deprecated[i] ) == 0 )
			return( NULL );

	return( fn( meta->im, meta->name, &meta->value, a ) );
}

/**
 * vips_image_map: (method)
 * @image: image to map over
 * @fn: (scope call): function to call for each header field
 * @a: (closure fn): user data for function
 *
 * This function calls @fn for every header field, including every item of 
 * metadata. 
 *
 * Like all _map functions, the user function should return %NULL to continue
 * iteration, or a non-%NULL pointer to indicate early termination.
 *
 * See also: vips_image_get_typeof(), vips_image_get().
 *
 * Returns: (transfer none): %NULL on success, the failing pointer otherwise.
 */
void *
vips_image_map( VipsImage *image, VipsImageMapFn fn, void *a )
{
	int i;
	GValue value = { 0 };
	void *result;

	for( i = 0; i < VIPS_NUMBER( vips_header_fields ); i++ ) {
		HeaderField *field = &vips_header_fields[i];

		(void) vips_image_get( image, field->name, &value );
		result = fn( image, field->name, &value, a );
		g_value_unset( &value );

		if( result )
			return( result );
	}

	if( image->meta_traverse && 
		(result = vips_slist_map2( image->meta_traverse, 
			(VipsSListMap2Fn) vips_image_map_fn, fn, a )) )
		return( result );

	return( NULL );
}

static void *
count_fields( VipsImage *image, const char *field, GValue *value, void *a )
{
	int *n_fields = (int *) a;

	*n_fields += 1;

	return( NULL ); 
}

static void *
add_fields( VipsImage *image, const char *field, GValue *value, void *a )
{
	gchar ***p = (gchar ***) a;

	**p = g_strdup( field ); 
	*p += 1;

	return( NULL ); 
}

/**
 * vips_image_get_fields: (method)
 * @image: image to get fields from
 *
 * Get a %NULL-terminated array listing all the metadata field names on @image. 
 * Free the return result with g_strfreev().
 *
 * This is handy for language bindings. From C, it's usually more convenient to
 * use vips_image_map().
 *
 * Returns: (transfer full): metadata fields in image, as a %NULL-terminated
 * array. 
 */
gchar ** 
vips_image_get_fields( VipsImage *image )
{
	int n_fields;
	gchar **fields;
	gchar **p;

	n_fields = 0;
	(void) vips_image_map( image, count_fields, &n_fields );
	fields = g_new0( gchar *, n_fields + 1 ); 
	p = fields;
	(void) vips_image_map( image, add_fields, &p );

	return( fields ); 
}

/**
 * vips_image_set_area: (method)
 * @image: image to attach the metadata to
 * @name: metadata name
 * @free_fn: (scope async): free function for @data
 * @data: pointer to area of memory
 *
 * Attaches @data as a metadata item on @image under the name @name. When
 * VIPS no longer needs the metadata, it will be freed with @free_fn.
 *
 * See also: vips_image_get_double(), vips_image_set()
 */
void
vips_image_set_area( VipsImage *image, const char *name,
	VipsCallbackFn free_fn, void *data )
{
	GValue value = { 0 };

	vips_value_set_area( &value, free_fn, data );
	vips_image_set( image, name, &value );
	g_value_unset( &value );
}

static int
meta_get_value( const VipsImage *image,
	const char *name, GType type, GValue *value_copy )
{
	GValue value = { 0 }; 

	if( vips_image_get( image, name, &value ) )
		return( -1 );
	g_value_init( value_copy, type );
	if( !g_value_transform( &value, value_copy ) ) { 
		vips_error( "VipsImage",
			_( "field \"%s\" is of type %s, not %s" ),
			name,
			g_type_name( G_VALUE_TYPE( &value ) ),
			g_type_name( type ) );
		g_value_unset( &value );

		return( -1 );
	}
	g_value_unset( &value );

	return( 0 );
}

/**
 * vips_image_get_area: (method)
 * @image: image to get the metadata from
 * @name: metadata name
 * @data: (out): return metadata value
 *
 * Gets @data from @image under the name @name. A convenience
 * function over vips_image_get(). Use vips_image_get_typeof() to test for
 * the existence of a piece of metadata.
 *
 * See also: vips_image_set_area(), vips_image_get(),
 * vips_image_get_typeof()
 *
 * Returns: 0 on success, -1 otherwise.
 */
int
vips_image_get_area( const VipsImage *image, 
	const char *name, const void **data )
{
	GValue value_copy = { 0 };

	if( !meta_get_value( image, name, VIPS_TYPE_AREA, &value_copy ) ) {
		*data = vips_value_get_area( &value_copy, NULL );
		g_value_unset( &value_copy );
		return( 0 );
	}

	return( -1 );
}

/** 
 * vips_image_set_blob: (method)
 * @image: image to attach the metadata to
 * @name: metadata name
 * @free_fn: (scope async): free function for @data
 * @data: pointer to area of memory
 * @length: length of memory area
 *
 * Attaches @blob as a metadata item on @image under the name @name. A 
 * convenience
 * function over vips_image_set() using a vips_blob.
 *
 * See also: vips_image_get_blob(), vips_image_set().
 */
void
vips_image_set_blob( VipsImage *image, const char *name, 
	VipsCallbackFn free_fn, const void *data, size_t size )
{
	GValue value = { 0 };

	g_value_init( &value, VIPS_TYPE_BLOB );
	vips_value_set_blob( &value, free_fn, data, size );
	vips_image_set( image, name, &value );
	g_value_unset( &value );
}

/** 
 * vips_image_set_blob_copy: (method)
 * @image: image to attach the metadata to
 * @name: metadata name
 * @data: pointer to area of memory
 * @length: length of memory area
 *
 * Attaches @blob as a metadata item on @image under the name @name, taking
 * a copy of the memory area. A convenience function over 
 * vips_image_set_blob().
 *
 * See also: vips_image_get_blob(), vips_image_set().
 */
void
vips_image_set_blob_copy( VipsImage *image, 
	const char *name, const void *data, size_t length )
{
	void *data_copy;

	/* Cap at 100mb for sanity.
	 */
	if( !data ||
		length == 0 ||
		length > 100 * 1024 * 1024 )
		return;

	/* We add an extra, secret null byte at the end, just in case this blob 
	 * is read as a C string. The libtiff reader attaches
	 * XMP XML as a blob, for example.
	 */
	if( !(data_copy = vips_malloc( NULL, length + 1 )) ) 
		return;
	memcpy( data_copy, data, length );
	((unsigned char *) data_copy)[length] = '\0';

	vips_image_set_blob( image, 
		name, (VipsCallbackFn) vips_area_free_cb, data_copy, length );
}

/** 
 * vips_image_get_blob: (method)
 * @image: image to get the metadata from
 * @name: metadata name
 * @data: pointer to area of memory
 * @length: return the blob length here, optionally
 *
 * Gets @blob from @image under the name @name, optionally returns its length in
 * @length. A convenience
 * function over vips_image_get(). Use vips_image_get_typeof() to test for the 
 * existence
 * of a piece of metadata.
 *
 * See also: vips_image_get(), vips_image_get_typeof(), vips_blob_get(), 
 *
 * Returns: 0 on success, -1 otherwise.
 */
int
vips_image_get_blob( const VipsImage *image, const char *name, 
	const void **data, size_t *length )
{
	GValue value_copy = { 0 };

	if( !meta_get_value( image, name, VIPS_TYPE_BLOB, &value_copy ) ) {
		*data = vips_value_get_blob( &value_copy, length );
		g_value_unset( &value_copy );
		return( 0 );
	}

	return( -1 );
}

/** 
 * vips_image_get_int: (method)
 * @image: image to get the header field from
 * @name: field name
 * @out: (out): return field value
 *
 * Gets @out from @im under the name @name. 
 * The value will be transformed into
 * an int, if possible. 
 *
 * See also: vips_image_get(), vips_image_get_typeof()
 *
 * Returns: 0 on success, -1 otherwise.
 */
int
vips_image_get_int( const VipsImage *image, const char *name, int *out )
{
	GValue value = { 0 };

	if( meta_get_value( image, name, G_TYPE_INT, &value ) ) 
		return( -1 ); 
	*out = g_value_get_int( &value );
	g_value_unset( &value );

	return( 0 );
}

/** 
 * vips_image_set_int: (method)
 * @image: image to attach the metadata to
 * @name: metadata name
 * @i: metadata value
 *
 * Attaches @i as a metadata item on @image under the name @name. A 
 * convenience
 * function over vips_image_set().
 *
 * See also: vips_image_get_int(), vips_image_set()
 */
void
vips_image_set_int( VipsImage *image, const char *name, int i )
{
	GValue value = { 0 };

	g_value_init( &value, G_TYPE_INT );
	g_value_set_int( &value, i );
	vips_image_set( image, name, &value );
	g_value_unset( &value );
}

/** 
 * vips_image_get_double: (method)
 * @image: image to get the header field from
 * @name: field name
 * @out: return field value
 *
 * Gets @out from @im under the name @name. 
 * The value will be transformed into
 * a double, if possible. 
 *
 * See also: vips_image_get(), vips_image_get_typeof()
 *
 * Returns: 0 on success, -1 otherwise.
 */
int
vips_image_get_double( const VipsImage *image, const char *name, double *out )
{
	GValue value = { 0 };

	if( meta_get_value( image, name, G_TYPE_DOUBLE, &value ) ) 
		return( -1 ); 
	*out = g_value_get_double( &value );
	g_value_unset( &value );

	return( 0 );
}

/** 
 * vips_image_set_double: (method)
 * @image: image to attach the metadata to
 * @name: metadata name
 * @d: metadata value
 *
 * Attaches @d as a metadata item on @image as @name. A 
 * convenience
 * function over vips_image_set().
 *
 * See also: vips_image_get_double(), vips_image_set()
 */
void
vips_image_set_double( VipsImage *image, const char *name, double d )
{
	GValue value = { 0 };

	g_value_init( &value, G_TYPE_DOUBLE );
	g_value_set_double( &value, d );
	vips_image_set( image, name, &value );
	g_value_unset( &value );
}

/** 
 * vips_image_get_string: (method)
 * @image: image to get the header field from
 * @name: field name
 * @out: (transfer none): return field value
 *
 * Gets @out from @im under the name @name. 
 * The field must be of type
 * G_TYPE_STRING, VIPS_TYPE_REF_STRING.
 *
 * Do not free @out.
 *
 * Use vips_image_get_as_string() to fetch any field as a string.
 *
 * See also: vips_image_get(), vips_image_get_typeof()
 *
 * Returns: 0 on success, -1 otherwise.
 */
int
vips_image_get_string( const VipsImage *image, const char *name, 
	const char **out )
{
	GValue value = { 0 };

	if( vips_image_get( image, name, &value ) )
		return( -1 );

	if( G_VALUE_TYPE( &value ) == VIPS_TYPE_REF_STRING ) {
		VipsArea *area;

		area = g_value_get_boxed( &value );
		*out = area->data;
	}
	else if( G_VALUE_TYPE( &value ) == G_TYPE_STRING ) {
		*out = g_value_get_string( &value );
	}
	else {
		vips_error( "VipsImage",
			_( "field \"%s\" is of type %s, not VipsRefString" ),
			name,
			g_type_name( G_VALUE_TYPE( &value ) ) );
		g_value_unset( &value );

		return( -1 );
	}

	g_value_unset( &value );

	return( 0 );
}

/** 
 * vips_image_set_string: (method)
 * @image: image to attach the metadata to
 * @name: metadata name
 * @str: metadata value
 *
 * Attaches @str as a metadata item on @image as @name. 
 * A convenience
 * function over vips_image_set() using #VIPS_TYPE_REF_STRING.
 *
 * See also: vips_image_get_double(), vips_image_set().
 */
void
vips_image_set_string( VipsImage *image, const char *name, const char *str )
{
	GValue value = { 0 };

	g_value_init( &value, VIPS_TYPE_REF_STRING );
	vips_value_set_ref_string( &value, str );
	vips_image_set( image, name, &value );
	g_value_unset( &value );
}

/** 
 * vips_image_get_as_string: (method)
 * @image: image to get the header field from
 * @name: field name
 * @out: (transfer full): return field value as string
 *
 * Returns @name from @image in @out. 
 * This function will read any field, returning it as a printable string.
 * You need to free the string with g_free() when you are done with it.
 *
 * This will base64-encode BLOBs, for example. Use vips_buf_appendgv() to 
 * make a string that's for humans. 
 *
 * See also: vips_image_get(), vips_image_get_typeof(), vips_buf_appendgv().
 *
 * Returns: 0 on success, -1 otherwise.
 */
int
vips_image_get_as_string( const VipsImage *image, 
	const char *name, char **out )
{
	GValue value = { 0 };
	GType type;

	if( vips_image_get( image, name, &value ) )
		return( -1 );

	/* Display the save form, if there is one. This way we display
	 * something useful for ICC profiles, xml fields, etc.
	 */
	type = G_VALUE_TYPE( &value );
	if( g_value_type_transformable( type, VIPS_TYPE_SAVE_STRING ) ) {
		GValue save_value = { 0 };

		g_value_init( &save_value, VIPS_TYPE_SAVE_STRING );
		if( !g_value_transform( &value, &save_value ) ) 
			return( -1 );
		*out = g_strdup( vips_value_get_save_string( &save_value ) );
		g_value_unset( &save_value );
	}
	else 
		*out = g_strdup_value_contents( &value );

	g_value_unset( &value );

	return( 0 );
}

/**
 * vips_image_print_field: (method)
 * @image: image to get the header field from
 * @name: field name
 *
 * Prints field @name to stdout as ASCII. Handy for debugging. 
 */
void
vips_image_print_field( const VipsImage *image, const char *name )
{
	char *str;

	if( vips_image_get_as_string( image, name, &str ) ) {
		printf( "vips_image_print_field: unable to read field\n" );
		return;
	}

	printf( ".%s: %s\n", name, str );

	g_free( str ); 
}

/** 
 * vips_image_get_image: (method)
 * @image: image to get the metadata from
 * @name: metadata name
 * @out: (transfer full): return metadata value
 *
 * Gets @out from @im under the name @name. 
 * The field must be of type
 * #VIPS_TYPE_IMAGE. You must unref @out with g_object_unref().
 *
 * Use vips_image_get_typeof() to test for the 
 * existence of a piece of metadata.
 *
 * See also: vips_image_get(), vips_image_set_image()
 *
 * Returns: 0 on success, -1 otherwise.
 */
int 
vips_image_get_image( const VipsImage *image, 
	const char *name, VipsImage **out )
{
	GValue value = { 0 };

	if( meta_get_value( image, name, VIPS_TYPE_IMAGE, &value ) ) 
		return( -1 ); 
	*out = g_value_dup_object( &value );
	g_value_unset( &value );

	return( 0 );
}

/**
 * vips_image_set_image: (method)
 * @image: image to attach the metadata to
 * @name: metadata name
 * @im: metadata value
 *
 * Attaches @im as a metadata item on @image as @name. 
 * A convenience function over vips_image_set().
 *
 * See also: vips_image_get_image(), vips_image_set().
 */
void 
vips_image_set_image( VipsImage *image, const char *name, VipsImage *im )
{
	GValue value = { 0 };

	g_value_init( &value, VIPS_TYPE_IMAGE );
	g_value_set_object( &value, im );
	vips_image_set( image, name, &value );
	g_value_unset( &value );
}

/** 
 * vips_image_get_array_int: (method)
 * @image: image to get the metadata from
 * @name: metadata name
 * @out: (transfer none): return pointer to array
 * @n: (allow-none): return the number of elements here, optionally
 *
 * Gets @out from @im under the name @name. 
 * The field must be of type
 * #VIPS_TYPE_ARRAY_INT.
 *
 * Do not free @out. @out is valid as long as @image is valid.
 *
 * Use vips_image_get_typeof() to test for the 
 * existence of a piece of metadata.
 *
 * See also: vips_image_get(), vips_image_set_image()
 *
 * Returns: 0 on success, -1 otherwise.
 */
int 
vips_image_get_array_int( VipsImage *image, const char *name, 
	int **out, int *n )
{
	GValue value = { 0 };

	if( meta_get_value( image, name, VIPS_TYPE_ARRAY_INT, &value ) ) 
		return( -1 ); 
	*out = vips_value_get_array_int( &value, n );
	g_value_unset( &value );

	return( 0 );
}

/**
 * vips_image_set_array_int: (method)
 * @image: image to attach the metadata to
 * @name: metadata name
 * @array: (array length=n) (allow-none): array of ints
 * @n: the number of elements 
 *
 * Attaches @array as a metadata item on @image as @name. 
 * A convenience function over vips_image_set().
 *
 * See also: vips_image_get_image(), vips_image_set().
 */
void
vips_image_set_array_int( VipsImage *image, const char *name,
	const int *array, int n )
{
	GValue value = { 0 };

	g_value_init( &value, VIPS_TYPE_ARRAY_INT );
	vips_value_set_array_int( &value, array, n );
	vips_image_set( image, name, &value );
	g_value_unset( &value );
}

/** 
 * vips_image_get_array_double: (method)
 * @image: image to get the metadata from
 * @name: metadata name
 * @out: (transfer none): return pointer to array
 * @n: (allow-none): return the number of elements here, optionally
 *
 * Gets @out from @im under the name @name. 
 * The field must be of type
 * #VIPS_TYPE_ARRAY_INT.
 *
 * Do not free @out. @out is valid as long as @image is valid.
 *
 * Use vips_image_get_typeof() to test for the 
 * existence of a piece of metadata.
 *
 * See also: vips_image_get(), vips_image_set_image()
 *
 * Returns: 0 on success, -1 otherwise.
 */
int
vips_image_get_array_double( VipsImage *image, const char *name,
	double **out, int *n )
{
	GValue value = { 0 };

	if( meta_get_value( image, name, VIPS_TYPE_ARRAY_DOUBLE, &value ) ) 
		return( -1 );
	*out = vips_value_get_array_double( &value, n );
	g_value_unset( &value );

	return( 0 );
}

/**
 * vips_image_set_array_double: (method)
 * @image: image to attach the metadata to
 * @name: metadata name
 * @array: (array length=n) (allow-none): array of doubles
 * @n: the number of elements 
 *
 * Attaches @array as a metadata item on @image as @name. 
 * A convenience function over vips_image_set().
 *
 * See also: vips_image_get_image(), vips_image_set().
 */
void
vips_image_set_array_double( VipsImage *image, const char *name,
	const double *array, int n )
{
	GValue value = { 0 };

	g_value_init( &value, VIPS_TYPE_ARRAY_DOUBLE );
	vips_value_set_array_double( &value, array, n );
	vips_image_set( image, name, &value );
	g_value_unset( &value );
}

/**
 * vips_image_history_printf: (method)
 * @image: add history line to this image
 * @format: printf() format string
 * @...: arguments to format string
 *
 * Add a line to the image history. The @format and arguments are expanded, the
 * date and time is appended prefixed with a hash character, and the whole
 * string is appended to the image history and terminated with a newline.
 *
 * For example:
 *
 * |[
 * vips_image_history_printf (image, "vips invert %s %s", 
 *   in->filename, out->filename);
 * ]|
 *
 * Might add the string
 *
 * |[
 * "vips invert /home/john/fred.v /home/john/jim.v # Fri Apr 3 23:30:35 2009\n"
 * ]|
 *
 * VIPS operations don't add history lines for you because a single action at 
 * the application level might involve many VIPS operations. History must be
 * recorded by the application.
 *
 * Returns: 0 on success, -1 on error.
 */
int 
vips_image_history_printf( VipsImage *image, const char *fmt, ... )
{
	va_list args;
	char str[VIPS_PATH_MAX];
	VipsBuf buf = VIPS_BUF_STATIC( str );
	time_t timebuf;

	va_start( args, fmt );
	(void) vips_buf_vappendf( &buf, fmt, args );
	va_end( args );
	vips_buf_appends( &buf, " # " );

	/* Add the date. ctime always attaches a '\n', gah.
	 */
	time( &timebuf );
	vips_buf_appends( &buf, ctime( &timebuf ) ); 
	vips_buf_removec( &buf, '\n' ); 

#ifdef DEBUG
	printf( "vips_image_history_printf: "
		"adding:\n\t%s\nto history on image %p\n", 
		vips_buf_all( &buf ), image );
#endif /*DEBUG*/

	image->history_list = g_slist_append( image->history_list, 
		vips__gvalue_ref_string_new( vips_buf_all( &buf ) ) );

	return( 0 );
}

/**
 * vips_image_history_args: (method)
 * @image: image to attach history line to
 * @name: program name
 * @argc: number of program arguments
 * @argv: (array length=argc) (element-type char*): program arguments
 *
 * Formats the name/argv as a single string and calls
 * vips_image_history_printf(). A
 * convenience function for command-line prorams.
 *
 * See also: vips_image_get_history().
 *
 * Returns: 0 on success, -1 on error.
 */
int 
vips_image_history_args( VipsImage *image, 
	const char *name, int argc, char *argv[] )
{	
	int i;
	char txt[1024];
	VipsBuf buf = VIPS_BUF_STATIC( txt );

	vips_buf_appends( &buf, name );

	for( i = 0; i < argc; i++ ) {
		vips_buf_appends( &buf, " " );
		vips_buf_appends( &buf, argv[i] );
	}

	if( vips_image_history_printf( image, "%s", vips_buf_all( &buf ) ) ) 
		return( -1 );

	return( 0 );
}

/**
 * vips_image_get_history: (method)
 * @image: get history from here
 *
 * This function reads the image history as a C string. The string is owned
 * by VIPS and must not be freed.
 *
 * VIPS tracks the history of each image, that is, the sequence of operations
 * that generated that image. Applications built on VIPS need to call
 * vips_image_history_printf() for each action they perform, setting the 
 * command-line equivalent for the action.
 *
 * See also: vips_image_history_printf().
 *
 * Returns: (transfer none): The history of @image as a C string. Do not free!
 */
const char *
vips_image_get_history( VipsImage *image )
{
	if( !image->Hist )
		image->Hist = vips__gslist_gvalue_get( image->history_list );

	return( image->Hist ? image->Hist : "" );
}

/* Called during vips_init().
 */
void
vips__meta_init( void )
{
	if( !vips__meta_lock ) 
		vips__meta_lock = vips_g_mutex_new();
}
