Ejemplo n.º 1
0
/* Read a few bytes from the start of a file. For sniffing file types.
 * Filename may contain a mode. 
 */
int
vips__get_bytes( const char *filename, unsigned char buf[], int len )
{
	char name[FILENAME_MAX];
	char mode[FILENAME_MAX];
	int fd;

	/* Split off the mode part.
	 */
	im_filename_split( filename, name, mode );

	/* File may not even exist (for tmp images for example!)
	 * so no hasty messages. And the file might be truncated, so no error
	 * on read either.
	 */
	if( (fd = open( name, MODE_READONLY )) == -1 )
		return( 0 );
	if( read( fd, buf, len ) != len ) {
		close( fd );
		return( 0 );
	}
	close( fd );

	return( 1 );
}
Ejemplo n.º 2
0
int
im_vips2png( IMAGE *in, const char *filename )
{
	int compression; 
	int interlace; 

	char *p, *q;

	char name[FILENAME_MAX];
	char mode[FILENAME_MAX];
	char buf[FILENAME_MAX];

	/* Extract write mode from filename and parse.
	 */
	im_filename_split( filename, name, mode );
	strcpy( buf, mode ); 
	p = &buf[0];
	compression = 6;
	interlace = 0;
	if( (q = im_getnextoption( &p )) ) 
		compression = atoi( q );
	if( (q = im_getnextoption( &p )) ) 
		interlace = atoi( q );

	return( vips_pngsave( in, name, 
		"compression", compression, "interlace", interlace, NULL ) );
}
Ejemplo n.º 3
0
static int
webp2vips( const char *name, IMAGE *out, gboolean header_only )
{
	char filename[FILENAME_MAX];
	char mode[FILENAME_MAX];

	im_filename_split( name, filename, mode );

#ifdef HAVE_LIBWEBP
	if( header_only ) {
		if( vips__webp_read_file_header( filename, out, 1 ) )
			return( -1 );
	}
	else {
		if( vips__webp_read_file( filename, out, 1 ) )
			return( -1 );
	}
#else
	vips_error( "im_webp2vips", 
		"%s", _( "no webp support in your libvips" ) ); 

	return( -1 );
#endif /*HAVE_LIBWEBP*/

	return( 0 );
}
Ejemplo n.º 4
0
int
im_png2vips( const char *name, IMAGE *out )
{
	char filename[FILENAME_MAX];
	char mode[FILENAME_MAX];
	char *p, *q;
	gboolean sequential;
	VipsImage *x;

	im_filename_split( name, filename, mode );

	sequential = FALSE;
	p = &mode[0];
	if( (q = im_getnextoption( &p )) ) {
		if( im_isprefix( "seq", q ) )
			sequential = TRUE;
	}

	if( vips_pngload( filename, &x, 
		"sequential", sequential,
		NULL ) )
		return( -1 );

	if( vips_image_write( x, out ) ) {
		g_object_unref( x );
		return( -1 );
	}
	g_object_unref( x );

	return( 0 );
}
Ejemplo n.º 5
0
/**
 * im_vips2csv:
 * @in: image to save 
 * @filename: file to write to 
 *
 * Save a CSV (comma-separated values) file. The image is written
 * one line of text per scanline. Complex numbers are written as 
 * "(real,imaginary)" and will need extra parsing I guess. The image must
 * have a single band.
 *
 * Write options can be embedded in the filename. The options can be given 
 * in any order and are:
 *
 * <itemizedlist>
 *   <listitem>
 *     <para>
 * <emphasis>sep:separator-string</emphasis> 
 * The string to use to separate numbers in the output. 
 * The default is "\\t" (tab).
 *     </para>
 *   </listitem>
 * </itemizedlist>
 *
 * For example:
 *
 * |[
 * im_csv2vips( in, "fred.csv:sep:\t" );
 * ]|
 *
 * Will write to fred.csv, separating numbers with tab characters.
 *
 * See also: #VipsFormat, im_csv2vips(), im_write_dmask(), im_vips2ppm().
 *
 * Returns: 0 on success, -1 on error.
 */
int 
im_vips2csv( IMAGE *in, const char *filename )
{
	char *separator = "\t";

	char name[FILENAME_MAX];
	char mode[FILENAME_MAX];
	FILE *fp;
	char *p, *q, *r;

	/* Parse mode string.
	 */
	im_filename_split( filename, name, mode );
	p = &mode[0];
	while( (q = im_getnextoption( &p )) ) {
		if( im_isprefix( "sep", q ) && (r = im_getsuboption( q )) )
			separator = r;
	}

	if( im_incheck( in ) ||
		im_check_mono( "im_vips2csv", in ) ||
		im_check_uncoded( "im_vips2csv", in ) )
		return( -1 );

	if( !(fp = im__file_open_write( name, TRUE )) ) 
		return( -1 );
	if( vips2csv( in, fp, separator ) ) {
		fclose( fp );
		return( -1 );
	}

	fclose( fp );

	return( 0 );
}
Ejemplo n.º 6
0
static int
tiff2vips( const char *name, IMAGE *out, gboolean header_only )
{
	char filename[FILENAME_MAX];
	char mode[FILENAME_MAX];
	char *p, *q;
	int page;
	int seq;

	im_filename_split( name, filename, mode );

	page = 0;
	seq = 0;
	p = &mode[0];
	if( (q = im_getnextoption( &p )) ) {
		page = atoi( q );
	}
	if( (q = im_getnextoption( &p )) ) {
		if( im_isprefix( "seq", q ) )
			seq = 1;
	}

	/* We need to be compatible with the pre-sequential mode 
	 * im_tiff2vips(). This returned a "t" if given a "p" image, since it
	 * used writeline.
	 *
	 * If we're writing the image to a "p", switch it to a "t". And only
	 * for non-tiled (strip) images which we write with writeline.
	 *
	 * Don't do this for header read, since we don't want to force a
	 * malloc if all we are doing is looking at fields.
	 */

#ifdef HAVE_TIFF
	if( !header_only &&
		!seq &&
		!vips__istifftiled( filename ) &&
		out->dtype == VIPS_IMAGE_PARTIAL ) {
		if( vips__image_wio_output( out ) ) 
			return( -1 );
	}

	if( header_only ) {
		if( vips__tiff_read_header( filename, out, page ) )
			return( -1 );
	}
	else {
		if( vips__tiff_read( filename, out, page ) )
			return( -1 );
	}
#else
	vips_error( "im_tiff2vips", _( "no TIFF support in your libvips" ) ); 

	return( -1 );
#endif /*HAVE_TIFF*/

	return( 0 );
}
Ejemplo n.º 7
0
static int
isopenslide( const char *name )
{
	char filename[FILENAME_MAX];
	char mode[FILENAME_MAX];

	im_filename_split( name, filename, mode );

	return( vips_foreign_is_a( "openslideload", filename ) );
}
Ejemplo n.º 8
0
static VipsFormatFlags
tiff_flags( const char *name )
{
	char filename[FILENAME_MAX];
	char mode[FILENAME_MAX];

	im_filename_split( name, filename, mode );

	return( vips_foreign_flags( "tiffload", filename ) );
}
Ejemplo n.º 9
0
static VipsFormatFlags
openslide_flags( const char *name )
{
	char filename[FILENAME_MAX];
	char mode[FILENAME_MAX];

	im_filename_split( name, filename, mode );

	return( (VipsFormatFlags) 
		vips_foreign_flags( "openslideload", filename ) );
}
Ejemplo n.º 10
0
int
im_jpeg2vips( const char *name, IMAGE *out )
{
	char filename[FILENAME_MAX];
	char mode[FILENAME_MAX];
	char *p, *q;
	int shrink;
	gboolean fail_on_warn;
	VipsImage *t;

	/* By default, we ignore any warnings. We want to get as much of
	 * the user's data as we can.
	 */
	fail_on_warn = FALSE;

	/* Parse the filename.
	 */
	im_filename_split( name, filename, mode );
	p = &mode[0];
	shrink = 1;
	if( (q = im_getnextoption( &p )) ) {
		shrink = atoi( q );

		if( shrink != 1 && shrink != 2 && 
			shrink != 4 && shrink != 8 ) {
			im_error( "im_jpeg2vips", 
				_( "bad shrink factor %d" ), shrink );
			return( -1 );
		}
	}
	if( (q = im_getnextoption( &p )) ) {
		if( im_isprefix( "fail", q ) ) 
			fail_on_warn = TRUE;
	}

	if( vips_jpegload( filename, &t, 
		"shrink", shrink,
		"fail", fail_on_warn,
		NULL ) )
		return( -1 );

	if( vips_image_write( t, out ) ) {
		g_object_unref( t );
		return( -1 );
	}
	g_object_unref( t );

	return( 0 );
}
Ejemplo n.º 11
0
/* Do a image call.
 */
static void
apply_image_call( Reduce *rc, 
	const char *name, HeapNode **arg, PElement *out )
{
	Heap *heap = rc->heap;

	PElement rhs;
	char buf[FILENAME_MAX];
	char filename[FILENAME_MAX];
	char mode[FILENAME_MAX];
	char *fn;
	Imageinfo *ii;

	/* Get string. 
	 */
	PEPOINTRIGHT( arg[0], &rhs );
	(void) reduce_get_string( rc, &rhs, buf, FILENAME_MAX );

	/* The buf might be something like n3862.pyr.tif:1, ie. contain some
	 * load options. Split and search just for the filename component.
	 */
	im_filename_split( buf, filename, mode );

	/* Try to load image from given string.
	 */
	if( !(fn = path_find_file( filename )) )
		reduce_throw( rc );

	/* Reattach the mode and load.
	 */
	im_snprintf( buf, FILENAME_MAX, "%s:%s", fn, mode );
	if( !(ii = imageinfo_new_input( 
		main_imageinfogroup, NULL, heap, buf )) ) {
		IM_FREE( fn );
		reduce_throw( rc );
	}
	IM_FREE( fn );

	PEPUTP( out, ELEMENT_MANAGED, ii );
	MANAGED_UNREF( ii );
}
Ejemplo n.º 12
0
static int
im_openslide2vips( const char *name, IMAGE *out )
{
	char filename[FILENAME_MAX];
	char mode[FILENAME_MAX];
	char *p, *q;
	char *associated;
	int level;
	char *endptr;
	VipsImage *t;

	im_filename_split( name, filename, mode );
	level = 0;
	associated = NULL;
	p = &mode[0];
	if( (q = im_getnextoption( &p )) ) {
		level = strtoul( q, &endptr, 10 );
		if( *endptr ) {
			vips_error( "openslide2vips", "%s",
				_( "level must be a number" ) );
			return( -1 );
		}
	}
	if( (q = im_getnextoption( &p )) ) 
		associated = q;

	if( vips_openslideload( filename, &t, 
		"level", level,
		"associated", associated,
		NULL ) )
		return( -1 );
	if( vips_image_write( t, out ) ) {
		g_object_unref( t );
		return( -1 );
	}
	g_object_unref( t );

	return( 0 );
}
Ejemplo n.º 13
0
static int
jpeg2vips( const char *name, IMAGE *out, gboolean header_only )
{
	char filename[FILENAME_MAX];
	char mode[FILENAME_MAX];
	char *p, *q;
	int shrink;
	int seq;
	gboolean fail_on_warn;

	/* By default, we ignore any warnings. We want to get as much of
	 * the user's data as we can.
	 */
	fail_on_warn = FALSE;

	/* Parse the filename.
	 */
	im_filename_split( name, filename, mode );
	p = &mode[0];
	shrink = 1;
	seq = 0;
	if( (q = im_getnextoption( &p )) ) {
		shrink = atoi( q );

		if( shrink != 1 && shrink != 2 && 
			shrink != 4 && shrink != 8 ) {
			im_error( "im_jpeg2vips", 
				_( "bad shrink factor %d" ), shrink );
			return( -1 );
		}
	}
	if( (q = im_getnextoption( &p )) ) {
		if( im_isprefix( "fail", q ) ) 
			fail_on_warn = TRUE;
	}
	if( (q = im_getnextoption( &p )) ) {
		if( im_isprefix( "seq", q ) )
			seq = 1;
	}

	/* Don't use vips_jpegload() ... we call the jpeg func directly in
	 * order to avoid the foreign.c mechanisms for load-via-disc and stuff
	 * like that.
	 */

	/* We need to be compatible with the pre-sequential mode 
	 * im_jpeg2vips(). This returned a "t" if given a "p" image, since it
	 * used writeline.
	 *
	 * If we're writing the image to a "p", switch it to a "t".
	 */

	if( !header_only &&
		!seq &&
		out->dtype == VIPS_IMAGE_PARTIAL ) {
		if( vips__image_wio_output( out ) ) 
			return( -1 );
	}

#ifdef HAVE_JPEG
	if( vips__jpeg_read_file( filename, out, 
		header_only, shrink, fail_on_warn, TRUE ) )
		return( -1 );
#else
	vips_error( "im_jpeg2vips", 
		"%s", _( "no JPEG support in your libvips" ) ); 

	return( -1 );
#endif /*HAVE_JPEG*/

	return( 0 );
}
Ejemplo n.º 14
0
/**
 * im_vips2jpeg:
 * @in: image to save 
 * @filename: file to write to 
 *
 * Write a VIPS image to a file as JPEG.
 *
 * You can embed options in the filename. They have the form:
 *
 * |[
 * filename.jpg:<emphasis>compression</emphasis>,<emphasis>profile</emphasis>
 * ]|
 *
 * <itemizedlist>
 *   <listitem>
 *     <para>
 * <emphasis>compression</emphasis> 
 * Compress with this quality factor. Default 75.
 *     </para>
 *   </listitem>
 *   <listitem>
 *     <para>
 * <emphasis>profile</emphasis> 
 * Attach this ICC profile. For example, "fred.jpg:,/home/john/srgb.icc" will 
 * embed the profile stored in the file "/home/john/srgb.icc" in the JPEG 
 * image. This does not affect the pixels which are written, just the way 
 * they are tagged. You can use the special string "none" to mean 
 * "don't attach a profile".
 *     </para>
 *   </listitem>
 * </itemizedlist>
 *
 * If no profile is specified in the save string and the VIPS header 
 * contains an ICC profile named IM_META_ICC_NAME ("icc-profile-data"), the
 * profile from the VIPS header will be attached.
 *
 * The image is automatically converted to RGB, Monochrome or CMYK before 
 * saving. Any metadata attached to the image is saved as EXIF, if possible.
 *
 * Example:
 *
 * |[
 * im_vips2jpeg( in, "fred.jpg:99,none" );
 * ]|
 *
 * Will write "fred.jpg" at high-quality with no ICC profile.
 *
 * See also: #VipsFormat, im_jpeg2vips().
 *
 * Returns: 0 on success, -1 on error.
 */
int
im_vips2jpeg( IMAGE *in, const char *filename )
{
	Write *write;
	int qfac = 75; 
	char *profile = NULL;

	char *p, *q;

	char name[FILENAME_MAX];
	char mode[FILENAME_MAX];
	char buf[FILENAME_MAX];

	/* Parse mode from filename.
	 */
	im_filename_split( filename, name, mode );
	strcpy( buf, mode ); 
	p = &buf[0];
	if( (q = im_getnextoption( &p )) ) {
		if( strcmp( q, "" ) != 0 )
			qfac = atoi( mode );
	}
	if( (q = im_getnextoption( &p )) ) {
		if( strcmp( q, "" ) != 0 ) 
			profile = q;
	}
	if( (q = im_getnextoption( &p )) ) {
		im_error( "im_vips2jpeg", 
			_( "unknown extra options \"%s\"" ), q );
		return( -1 );
	}

	if( !(write = write_new( in )) )
		return( -1 );

	if( setjmp( write->eman.jmp ) ) {
		/* Here for longjmp() from new_error_exit().
		 */
		write_destroy( write );

		return( -1 );
	}

	/* Can't do this in write_new(), has to be after we've made the
	 * setjmp().
	 */
        jpeg_create_compress( &write->cinfo );

	/* Make output.
	 */
        if( !(write->eman.fp = im__file_open_write( name, FALSE )) ) {
		write_destroy( write );
                return( -1 );
        }
        jpeg_stdio_dest( &write->cinfo, write->eman.fp );

	/* Convert!
	 */
	if( write_vips( write, qfac, profile ) ) {
		write_destroy( write );
		return( -1 );
	}
	write_destroy( write );

	return( 0 );
}
Ejemplo n.º 15
0
/* Read a JPEG file into a VIPS image.
 */
static int
jpeg2vips( const char *name, IMAGE *out, gboolean header_only )
{
	char filename[FILENAME_MAX];
	char mode[FILENAME_MAX];
	char *p, *q;
	int shrink;
	struct jpeg_decompress_struct cinfo;
        ErrorManager eman;
	FILE *fp;
	int result;
	gboolean invert_pels;
	gboolean fail_on_warn;

	/* By default, we ignore any warnings. We want to get as much of
	 * the user's data as we can.
	 */
	fail_on_warn = FALSE;

	/* Parse the filename.
	 */
	im_filename_split( name, filename, mode );
	p = &mode[0];
	shrink = 1;
	if( (q = im_getnextoption( &p )) ) {
		shrink = atoi( q );

		if( shrink != 1 && shrink != 2 && 
			shrink != 4 && shrink != 8 ) {
			im_error( "im_jpeg2vips", 
				_( "bad shrink factor %d" ), shrink );
			return( -1 );
		}
	}
	if( (q = im_getnextoption( &p )) ) {
		if( im_isprefix( "fail", q ) ) 
			fail_on_warn = TRUE;
	}

	/* Make jpeg dcompression object.
 	 */
        cinfo.err = jpeg_std_error( &eman.pub );
	eman.pub.error_exit = new_error_exit;
	eman.pub.output_message = new_output_message;
	eman.fp = NULL;
	if( setjmp( eman.jmp ) ) {
		/* Here for longjmp() from new_error_exit().
		 */
		jpeg_destroy_decompress( &cinfo );

		return( -1 );
	}
        jpeg_create_decompress( &cinfo );

	/* Make input.
	 */
        if( !(fp = im__file_open_read( filename, NULL, FALSE )) ) 
                return( -1 );
	eman.fp = fp;
        jpeg_stdio_src( &cinfo, fp );

	/* Need to read in APP1 (EXIF metadata) and APP2 (ICC profile).
	 */
	jpeg_save_markers( &cinfo, JPEG_APP0 + 1, 0xffff );
	jpeg_save_markers( &cinfo, JPEG_APP0 + 2, 0xffff );

	/* Convert!
	 */
	result = read_jpeg_header( &cinfo, out, &invert_pels, shrink );
	if( !header_only && !result )
		result = read_jpeg_image( &cinfo, out, invert_pels );

	/* Close and tidy.
	 */
	fclose( fp );
	eman.fp = NULL;
	jpeg_destroy_decompress( &cinfo );

	if( eman.pub.num_warnings != 0 ) {
		if( fail_on_warn ) {
			im_error( "im_jpeg2vips", "%s", im_error_buffer() );
			result = -1;
		}
		else {
			im_warn( "im_jpeg2vips", _( "read gave %ld warnings" ), 
				eman.pub.num_warnings );
			im_warn( "im_jpeg2vips", "%s", im_error_buffer() );
		}
	}

	return( result );
}
Ejemplo n.º 16
0
int
im_vips2tiff( IMAGE *in, const char *filename )
{
    char *p, *q, *r;
    char name[FILENAME_MAX];
    char mode[FILENAME_MAX];
    char buf[FILENAME_MAX];

    VipsForeignTiffCompression compression =
        VIPS_FOREIGN_TIFF_COMPRESSION_NONE;
    int Q = 75;
    VipsForeignTiffPredictor predictor = VIPS_FOREIGN_TIFF_PREDICTOR_NONE;
    char *profile = NULL;
    gboolean tile = FALSE;
    int tile_width = 128;
    int tile_height = 128;
    gboolean pyramid = FALSE;
    gboolean squash = FALSE;
    VipsForeignTiffResunit resunit = VIPS_FOREIGN_TIFF_RESUNIT_CM;
    double xres = in->Xres * 10.0;
    double yres = in->Yres * 10.0;
    gboolean bigtiff = FALSE;

    im_filename_split( filename, name, mode );
    strcpy( buf, mode );
    p = &buf[0];
    if( (q = im_getnextoption( &p )) ) {
        if( im_isprefix( "none", q ) )
            compression = VIPS_FOREIGN_TIFF_COMPRESSION_NONE;
        else if( im_isprefix( "packbits", q ) )
            compression = VIPS_FOREIGN_TIFF_COMPRESSION_PACKBITS;
        else if( im_isprefix( "ccittfax4", q ) )
            compression = VIPS_FOREIGN_TIFF_COMPRESSION_CCITTFAX4;
        else if( im_isprefix( "lzw", q ) ) {
            compression = VIPS_FOREIGN_TIFF_COMPRESSION_LZW;

            if( (r = im_getsuboption( q )) ) {
                int i;

                if( sscanf( r, "%d", &i ) != 1 ) {
                    im_error( "im_vips2tiff",
                              "%s", _( "bad predictor "
                                       "parameter" ) );
                    return( -1 );
                }
                predictor = i;
            }
        }
        else if( im_isprefix( "deflate", q ) ) {
            compression = VIPS_FOREIGN_TIFF_COMPRESSION_DEFLATE;

            if( (r = im_getsuboption( q )) ) {
                int i;

                if( sscanf( r, "%d", &i ) != 1 ) {
                    im_error( "im_vips2tiff",
                              "%s", _( "bad predictor "
                                       "parameter" ) );
                    return( -1 );
                }
                predictor = i;
            }
        }
        else if( im_isprefix( "jpeg", q ) ) {
            compression = VIPS_FOREIGN_TIFF_COMPRESSION_JPEG;

            if( (r = im_getsuboption( q )) )
                if( sscanf( r, "%d", &Q ) != 1 ) {
                    im_error( "im_vips2tiff",
                              "%s", _( "bad JPEG quality "
                                       "parameter" ) );
                    return( -1 );
                }
        }
        else {
            im_error( "im_vips2tiff", _( "unknown compression mode "
                                         "\"%s\"\nshould be one of \"none\", "
                                         "\"packbits\", \"ccittfax4\", \"lzw\", "
                                         "\"deflate\" or \"jpeg\"" ), q );
            return( -1 );
        }
    }

    if( (q = im_getnextoption( &p )) ) {
        if( im_isprefix( "tile", q ) ) {
            tile = TRUE;

            if( (r = im_getsuboption( q )) ) {
                if( sscanf( r, "%dx%d",
                            &tile_width, &tile_height ) != 2 ) {
                    im_error( "im_vips2tiff", "%s",
                              _( "bad tile sizes" ) );
                    return( -1 );
                }
            }
        }
        else if( im_isprefix( "strip", q ) )
            tile = FALSE;
        else {
            im_error( "im_vips2tiff", _( "unknown layout mode "
                                         "\"%s\"\nshould be one of \"tile\" or "
                                         "\"strip\"" ), q );
            return( -1 );
        }
    }

    if( (q = im_getnextoption( &p )) ) {
        if( im_isprefix( "pyramid", q ) )
            pyramid = TRUE;
        else if( im_isprefix( "flat", q ) )
            pyramid = FALSE;
        else {
            im_error( "im_vips2tiff", _( "unknown multi-res mode "
                                         "\"%s\"\nshould be one of \"flat\" or "
                                         "\"pyramid\"" ), q );
            return( -1 );
        }
    }

    if( (q = im_getnextoption( &p )) ) {
        if( im_isprefix( "onebit", q ) )
            squash = TRUE;
        else if( im_isprefix( "manybit", q ) )
            squash = FALSE;
        else {
            im_error( "im_vips2tiff", _( "unknown format "
                                         "\"%s\"\nshould be one of \"onebit\" or "
                                         "\"manybit\"" ), q );
            return( -1 );
        }
    }

    if( (q = im_getnextoption( &p )) ) {
        if( im_isprefix( "res_cm", q ) )
            resunit = VIPS_FOREIGN_TIFF_RESUNIT_CM;
        else if( im_isprefix( "res_inch", q ) )
            resunit = VIPS_FOREIGN_TIFF_RESUNIT_INCH;
        else {
            im_error( "im_vips2tiff", _( "unknown resolution unit "
                                         "\"%s\"\nshould be one of \"res_cm\" or "
                                         "\"res_inch\"" ), q );
            return( -1 );
        }

        if( (r = im_getsuboption( q )) ) {
            if( sscanf( r, "%lfx%lf", &xres, &yres ) != 2 ) {
                if( sscanf( r, "%lf", &xres ) != 1 ) {
                    im_error( "im_vips2tiff", "%s",
                              _( "bad resolution values" ) );
                    return( -1 );
                }

                yres = xres;
            }

            /* vips resolutions are always in pixels/mm. If the
             * user specifies ",res_inch:72x72" then they are
             * using pixels/inch instead and we must convert.
             */
            if( resunit == VIPS_FOREIGN_TIFF_RESUNIT_INCH ) {
                xres /= 2.54;
                yres /= 2.54;
            }
        }
    }

    if( (q = im_getnextoption( &p )) && strcmp( q, "" ) != 0 )
        profile = im_strdup( NULL, q );

    if( (q = im_getnextoption( &p )) && strcmp( q, "8" ) == 0 )
        bigtiff = TRUE;

    if( (q = im_getnextoption( &p )) ) {
        im_error( "im_vips2tiff",
                  _( "unknown extra options \"%s\"" ), q );
        return( -1 );
    }

    if( vips_tiffsave( in, name,
                       "compression", compression,
                       "Q", Q,
                       "predictor", predictor,
                       "profile", profile,
                       "tile", tile,
                       "tile_width", tile_width,
                       "tile_height", tile_height,
                       "pyramid", pyramid,
                       "squash", squash,
                       "resunit", resunit,
                       "xres", xres,
                       "yres", yres,
                       "bigtiff", bigtiff,
                       NULL ) )
        return( -1 );

    return( 0 );
}