| /* im_header_int, im_header_double, im_header_string: output various fields |
| * from the VIPS header |
| * |
| * 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 |
| */ |
| |
| /* |
| |
| 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| |
| */ |
| |
| /* |
| |
| These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk |
| |
| */ |
| |
| #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> |
| |
| #ifdef WITH_DMALLOC |
| #include <dmalloc.h> |
| #endif /*WITH_DMALLOC*/ |
| |
| /** |
| * SECTION: header |
| * @short_description: get, set and walk image headers |
| * @stability: Stable |
| * @see_also: <link linkend="libvips-meta">meta</link>, |
| * <link linkend="libvips-check">check</link> |
| * @include: vips/vips.h |
| * |
| * These functions let you get at image header data (including metadata) in a |
| * uniform way. They are handy for language bindings but less useful for C |
| * users. |
| * |
| * They first search the |
| * VIPS header |
| * fields (see <link linkend="libvips-image">image</link>), then search for |
| * a metadata field of that name (see |
| * <link linkend="libvips-meta">meta</link>). |
| * Use im_header_get_typeof() to test for the |
| * existance and #GType |
| * of a header field. |
| * |
| * See <link linkend="libvips-meta">meta</link> |
| * for a set of functions for adding new metadata to an image. |
| */ |
| |
| |
| /* Name, offset pair. |
| */ |
| typedef struct _HeaderField { |
| const char *field; |
| glong offset; |
| } HeaderField; |
| |
| /* Built in fields and struct offsets. |
| */ |
| static HeaderField int_field[] = { |
| { "Xsize", G_STRUCT_OFFSET( IMAGE, Xsize ) }, |
| { "Ysize", G_STRUCT_OFFSET( IMAGE, Ysize ) }, |
| { "Bands", G_STRUCT_OFFSET( IMAGE, Bands ) }, |
| { "Bbits", G_STRUCT_OFFSET( IMAGE, Bbits ) }, |
| { "BandFmt", G_STRUCT_OFFSET( IMAGE, BandFmt ) }, |
| { "Coding", G_STRUCT_OFFSET( IMAGE, Coding ) }, |
| { "Type", G_STRUCT_OFFSET( IMAGE, Type ) }, |
| { "Xoffset", G_STRUCT_OFFSET( IMAGE, Xoffset ) }, |
| { "Yoffset", G_STRUCT_OFFSET( IMAGE, Yoffset ) } |
| }; |
| |
| /* These are actually floats :-( how annoying. We report them as doubles for |
| * consistency with the im_meta_*() functions. |
| */ |
| static HeaderField double_field[] = { |
| { "Xres", G_STRUCT_OFFSET( IMAGE, Xres ) }, |
| { "Yres", G_STRUCT_OFFSET( IMAGE, Yres ) } |
| }; |
| |
| static HeaderField string_field[] = { |
| { "filename", G_STRUCT_OFFSET( IMAGE, filename ) } |
| }; |
| |
| /** |
| * im_header_int: |
| * @im: image to get the header field from |
| * @field: field name |
| * @out: return field value |
| * |
| * Gets @out from @im under the name @field. This function searches for |
| * int-valued fields. |
| * |
| * See also: im_header_get(), im_header_get_typeof() |
| * |
| * Returns: 0 on success, -1 otherwise. |
| */ |
| int |
| im_header_int( IMAGE *im, const char *field, int *out ) |
| { |
| int i; |
| |
| for( i = 0; i < IM_NUMBER( int_field ); i++ ) |
| if( strcmp( field, int_field[i].field ) == 0 ) { |
| *out = G_STRUCT_MEMBER( int, im, |
| int_field[i].offset ); |
| break; |
| } |
| |
| if( i == IM_NUMBER( int_field ) && |
| im_meta_get_int( im, field, out ) ) { |
| im_error( "im_header_int", |
| _( "no such int field \"%s\"" ), field ); |
| return( -1 ); |
| } |
| |
| return( 0 ); |
| } |
| |
| /** |
| * im_header_double: |
| * @im: image to get the header field from |
| * @field: field name |
| * @out: return field value |
| * |
| * Gets @out from @im under the name @field. |
| * This function searches for |
| * double-valued fields. |
| * |
| * See also: im_header_get(), im_header_get_typeof() |
| * |
| * Returns: 0 on success, -1 otherwise. |
| */ |
| int |
| im_header_double( IMAGE *im, const char *field, double *out ) |
| { |
| int i; |
| |
| for( i = 0; i < IM_NUMBER( double_field ); i++ ) |
| if( strcmp( field, double_field[i].field ) == 0 ) { |
| *out = G_STRUCT_MEMBER( float, im, |
| double_field[i].offset ); |
| break; |
| } |
| |
| if( i == IM_NUMBER( double_field ) && |
| im_meta_get_double( im, field, out ) ) { |
| im_error( "im_header_double", |
| _( "no such double field \"%s\"" ), field ); |
| return( -1 ); |
| } |
| |
| return( 0 ); |
| } |
| |
| /** |
| * im_header_string: |
| * @im: image to get the header field from |
| * @field: field name |
| * @out: return field value |
| * |
| * Gets @out from @im under the name @field. |
| * This function searches for |
| * string-valued fields. |
| * |
| * See also: im_header_get(), im_header_get_typeof() |
| * |
| * Returns: 0 on success, -1 otherwise. |
| */ |
| int |
| im_header_string( IMAGE *im, const char *field, char **out ) |
| { |
| int i; |
| |
| for( i = 0; i < IM_NUMBER( string_field ); i++ ) |
| if( strcmp( field, string_field[i].field ) == 0 ) { |
| *out = G_STRUCT_MEMBER( char *, im, |
| string_field[i].offset ); |
| break; |
| } |
| |
| if( i == IM_NUMBER( string_field ) && |
| im_meta_get_string( im, field, out ) ) { |
| im_error( "im_header_string", |
| _( "no such string field \"%s\"" ), field ); |
| return( -1 ); |
| } |
| |
| return( 0 ); |
| } |
| |
| /** |
| * im_header_as_string: |
| * @im: image to get the header field from |
| * @field: field name |
| * @out: return field value as string |
| * |
| * Gets @out from @im under the name @field. |
| * 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. |
| * |
| * See also: im_header_get(), im_header_get_typeof(). |
| * |
| * Returns: 0 on success, -1 otherwise. |
| */ |
| int |
| im_header_as_string( IMAGE *im, const char *field, char **out ) |
| { |
| GValue value = { 0 }; |
| GType type; |
| |
| if( im_header_get( im, field, &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, IM_TYPE_SAVE_STRING ) ) { |
| GValue save_value = { 0 }; |
| |
| g_value_init( &save_value, IM_TYPE_SAVE_STRING ); |
| if( !g_value_transform( &value, &save_value ) ) |
| return( -1 ); |
| *out = g_strdup( im_save_string_get( &save_value ) ); |
| g_value_unset( &save_value ); |
| } |
| else |
| *out = g_strdup_value_contents( &value ); |
| |
| g_value_unset( &value ); |
| |
| return( 0 ); |
| } |
| |
| /** |
| * im_header_get_typeof: |
| * @im: image to test |
| * @field: the name to search for |
| * |
| * Read the GType for a header field. Returns zero if there is no |
| * field of that name. |
| * |
| * See also: im_header_get(). |
| * |
| * Returns: the GType of the field, or zero if there is no |
| * field of that name. |
| */ |
| GType |
| im_header_get_typeof( IMAGE *im, const char *field ) |
| { |
| int i; |
| GType type; |
| |
| for( i = 0; i < IM_NUMBER( int_field ); i++ ) |
| if( strcmp( field, int_field[i].field ) == 0 ) |
| return( G_TYPE_INT ); |
| for( i = 0; i < IM_NUMBER( double_field ); i++ ) |
| if( strcmp( field, double_field[i].field ) == 0 ) |
| return( G_TYPE_DOUBLE ); |
| for( i = 0; i < IM_NUMBER( string_field ); i++ ) |
| if( strcmp( field, string_field[i].field ) == 0 ) |
| return( G_TYPE_STRING ); |
| if( (type = im_meta_get_typeof( im, field )) ) |
| return( type ); |
| |
| return( 0 ); |
| } |
| |
| /* Fill value_copy with a copy of the value, -1 on error. value_copy must be |
| * zeroed but uninitialised. User must g_value_unset( value ). |
| */ |
| |
| /** |
| * im_header_get: |
| * @im: image to get the field from from |
| * @field: the name to give the metadata |
| * @value_copy: 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 im_header_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 |
| * im_header_double() in practice): |
| * |
| * |[ |
| * GValue value = { 0 }; |
| * double d; |
| * |
| * if( im_header_get( im, field, &value ) ) |
| * return( -1 ); |
| * |
| * if( G_VALUE_TYPE( &value ) != G_TYPE_DOUBLE ) { |
| * im_error( "mydomain", _( "field \"%s\" is of type %s, not double" ), |
| * field, g_type_name( G_VALUE_TYPE( &value ) ) ); |
| * g_value_unset( &value ); |
| * return( -1 ); |
| * } |
| * |
| * d = g_value_get_double( &value ); |
| * g_value_unset( &value ); |
| * |
| * return( 0 ); |
| * ]| |
| * |
| * See also: im_header_get_typeof(), im_header_double(). |
| * |
| * Returns: 0 on success, -1 otherwise. |
| */ |
| int |
| im_header_get( IMAGE *im, const char *field, GValue *value_copy ) |
| { |
| int i; |
| |
| for( i = 0; i < IM_NUMBER( int_field ); i++ ) |
| if( strcmp( field, int_field[i].field ) == 0 ) { |
| g_value_init( value_copy, G_TYPE_INT ); |
| g_value_set_int( value_copy, |
| G_STRUCT_MEMBER( int, im, |
| int_field[i].offset ) ); |
| return( 0 ); |
| } |
| |
| for( i = 0; i < IM_NUMBER( double_field ); i++ ) |
| if( strcmp( field, double_field[i].field ) == 0 ) { |
| g_value_init( value_copy, G_TYPE_DOUBLE ); |
| g_value_set_double( value_copy, |
| G_STRUCT_MEMBER( float, im, |
| double_field[i].offset ) ); |
| return( 0 ); |
| } |
| |
| for( i = 0; i < IM_NUMBER( string_field ); i++ ) |
| if( strcmp( field, string_field[i].field ) == 0 ) { |
| g_value_init( value_copy, G_TYPE_STRING ); |
| g_value_set_static_string( value_copy, |
| G_STRUCT_MEMBER( char *, im, |
| string_field[i].offset ) ); |
| return( 0 ); |
| } |
| |
| if( !im_meta_get( im, field, value_copy ) ) |
| return( 0 ); |
| |
| return( -1 ); |
| } |
| |
| static void * |
| header_map_fn( Meta *meta, im_header_map_fn fn, void *a ) |
| { |
| return( fn( meta->im, meta->field, &meta->value, a ) ); |
| } |
| |
| /** |
| * im_header_map: |
| * @im: image to map over |
| * @fn: function to call for each header field |
| * @a: 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: im_header_get_typeof(), im_header_get(). |
| * |
| * Returns: %NULL on success, the failing pointer otherwise. |
| */ |
| void * |
| im_header_map( IMAGE *im, im_header_map_fn fn, void *a ) |
| { |
| int i; |
| GValue value = { 0 }; |
| void *result; |
| |
| for( i = 0; i < IM_NUMBER( int_field ); i++ ) { |
| im_header_get( im, int_field[i].field, &value ); |
| result = fn( im, int_field[i].field, &value, a ); |
| g_value_unset( &value ); |
| |
| if( result ) |
| return( result ); |
| } |
| |
| for( i = 0; i < IM_NUMBER( double_field ); i++ ) { |
| im_header_get( im, double_field[i].field, &value ); |
| result = fn( im, double_field[i].field, &value, a ); |
| g_value_unset( &value ); |
| |
| if( result ) |
| return( result ); |
| } |
| |
| for( i = 0; i < IM_NUMBER( string_field ); i++ ) { |
| im_header_get( im, string_field[i].field, &value ); |
| result = fn( im, string_field[i].field, &value, a ); |
| g_value_unset( &value ); |
| |
| if( result ) |
| return( result ); |
| } |
| |
| if( im->Meta_traverse && |
| (result = im_slist_map2( im->Meta_traverse, |
| (VSListMap2Fn) header_map_fn, fn, a )) ) |
| return( result ); |
| |
| return( NULL ); |
| } |