/**
 **	$Header: /import/dev-vis/image/imtools/v2.0/libim/src/RCS/imeps.c,v 1.6 92/12/03 01:47:38 nadeau Exp $
 **	Copyright (c) 1989-1992  San Diego Supercomputer Center (SDSC)
 **		San Diego, California, USA
 **
 **	Users and possessors of this source code are hereby granted a
 **	nonexclusive, royalty-free copyright and design patent license to
 **	use this code in individual software.  License is not granted for
 **	commercial resale, in whole or in part, without prior written
 **	permission from SDSC.  This source is provided "AS IS" without express
 **	or implied warranty of any kind.
 **
 **	For further information contact:
 **		E-Mail:		info@sds.sdsc.edu
 **
 **		Surface Mail:	Information Center
 **				San Diego Supercomputer Center
 **				P.O. Box 85608
 **				San Diego, CA  92138-5608
 **				(619) 534-5000
 **/

#define HEADER	"    $Header: /import/dev-vis/image/imtools/v2.0/libim/src/RCS/imeps.c,v 1.6 92/12/03 01:47:38 nadeau Exp $"

/**
 **  FILE
 **	imeps.c		-  Encapsulated PostScript file output
 **
 **  PROJECT
 **	libim	-  SDSC image manipulation library
 **
 **  DESCRIPTION
 **	imeps.c contains a write routine to write Encapsulated PostScript
 **	files for the image manipulation library.  Raster data written out is
 **	taken from a tag table.
 **
 **  PUBLIC CONTENTS
 **			d =defined constant
 **			f =function
 **			m =defined macro
 **			t =typedef/struct/union
 **			v =variable
 **			? =other
 **
 **	ImEpsWrite1	f  write a 1-bit monochrome image to a PostScript file
 **	ImEpsWriteGray	f  write a grayscale image to a PostScript file
 **	ImEpsWrite8	f  write an 8-bit index image to a PostScript file
 **	ImEpsWriteRGB	f  write an RGB image to a PostScript file
 **
 **  PRIVATE CONTENTS
 **	imEpsHex	v  Integer->Hex character conversion table
 **	imEpsHeader	v  PostScript header section
 **	imEpsProlog	v  PostScript prolog section
 **	imEpsImageColor	v  Color printing PostScript procedure
 **	imEpsImageGray	v  Grayscale printing PostScript procedure
 **	imEpsImageMono	v  Monochrome printing PostScript procedure
 **	imEpsScript	v  PostScript page script section
 **	imEpsData	v  PostScript data section
 **	imEpsTrailer	v  PostScript trailer section
 **
 **  HISTORY
 **	$Log:	imeps.c,v $
 **	Revision 1.6  92/12/03  01:47:38  nadeau
 **	Corrected info messages.
 **	
 **	Revision 1.5  92/11/04  11:59:57  groening
 **	 put ImFIleFormat info and magic number info
 **	 from imfmt.c into this file.
 **	
 **	Revision 1.4  92/10/19  14:15:21  groening
 **	added ImINfo statements
 **	
 **	Revision 1.3  92/09/25  12:38:01  groening
 **	added lines for ImInfo to printout verbose information
 **	
 **	Revision 1.2  92/08/31  17:22:40  vle
 **	Updated copyright notice.
 **	
 **	Revision 1.1  91/10/03  08:49:41  nadeau
 **	Initial revision
 **	
 **	Revision 1.7  91/09/17  20:17:10  nadeau
 **	Added color PostScript support.
 **	
 **	Revision 1.6  91/02/12  10:53:58  nadeau
 **	Removed the tag table checking and VFB conversion now
 **	handled by ImFileWrite.  Updated to use INDEX8 instead
 **	of GRAY VFB.  Changed 'Creator' comment to include the
 **	program name from the flags table, if given.
 **	
 **	Revision 1.5  90/11/27  16:40:44  mjb
 **	Removed spaces in between hex bytes (xerox says this will make it
 **	read much faster).   Made 'image' byte string size the same as vfb
 **	width (ditto above comment)
 **	
 **	Revision 1.4  90/07/02  13:21:33  nadeau
 **	Updated to the new error handling mechanism.
 **	
 **	Revision 1.3  90/06/25  14:36:16  nadeau
 **	Changed ImTag* to Tag* (new names).
 ** 
 **	Revision 1.2  90/05/16  07:47:03  todd
 **	Add #include "iminternal.h" to top of file
 ** 
 **	Revision 1.1  90/05/11  14:28:23  nadeau
 **	Initial revision
 ** 
 **/

#include <stdio.h>
#include "iminternal.h"





private char imEpsHex[] = { "0123456789abcdef" };

/*
 *  EPS - Adobe Encapsulated PostScript
 *      For information on these structures, how to use them, etc. please
 *      see imfmt.c.
 */
extern int ImEpsWrite1( ), ImEpsWriteGray( ), ImEpsWrite8( ), ImEpsWriteRGB( );
private char *imEpsNames[ ]  = { "eps", "epi", "epsf", "epsi", NULL };
private uchar imEpsMagicNumber[ ] = { '!', '%', 'P', 'S', '-', 'A', 'd', 'o',
        'b', 'e', '-', '3', '.', '0', ' ', 'E', 'P', 'S', 'F', '-', '3', '.',
        '0' };
private ImFileFormatWriteMap imEpsWriteMap[ ] =
{
        /* in                   out                                     */
        /* VFB type,    attr.,  type,ch,dep,    attr.,  func            */
        { IMVFBMONO,    0,      IN,1,1,         0,      ImEpsWrite1 },
        { IMVFBINDEX8,  0,      IN,1,8,         0,      ImEpsWriteGray },
        { IMVFBINDEX8,  C,      IN,1,8,         C,      ImEpsWrite8 },
        { IMVFBRGB,     0,      RGB,3,8,        0,      ImEpsWriteRGB },
        { -1,           0,      -1,             0,      NULL },
};

private ImFileMagic imFileEpsMagic1 =
{
	0, 23, imEpsMagicNumber
};

private ImFileMagic *imFileEpsMagic []=
{
	&imFileEpsMagic1,
	NULL
};

public ImFileFormat ImFileEpsFormat=
{
	imEpsNames, "Adobe Encapsulated PostScript file",
	"Adobe",
	"None.  Encapsulated PostScript files cannot be read!",
	"1-bit monochrome, 8-bit grayscale, 8-bit color index, and 24-bit\n\
	RGB color images.",
	imFileEpsMagic,
	FALSE, FALSE,
	FALSE, TRUE,
	NULL, NULL, imEpsWriteMap
};



/*
 *  GLOBAL
 *	imEpsHeader	-  PostScript header section
 *
 *  DESCRIPTION
 *	The PostScript file header is a set of special comments conforming
 *	to the Adobe Document Structuring Conventions (DSC) as specified
 *	in the Adobe PostScript Language Reference Manual (second edition).
 *
 *	The DSC comments give the document name, creator, and whatnot.
 *	Most importantly, the DSC also gives the bounding box for the
 *	document.
 */

private char *imEpsHeader = "\
%%!PS-Adobe-3.0 EPSF-3.0\n\
%%%%BoundingBox:   0 0 %d %d\n\
%%%%Creator:       %s\n\
%%%%For:           %s\n\
%%%%CreationDate:  %s\
%%%%Title:         %s\n\
%%%%Pages:         0\n\
%%%%EndComments\n\
";





/*
 *  GLOBALS
 *	imEpsImageColor	-  Color printing PostScript procedure
 *	imEpsImageGray	-  Grayscale printing PostScript procedure
 *	imEpsImageMono	-  Monochrome printing PostScript procedure
 *
 *  DESCRIPTION
 *	Each of these three globals defines the same function (proc) for the
 *	PostScript file header.  In each case, the ImEpsImage function takes
 *	hex image data and renders it onto the page.  The difference is only
 *	in the type of data being fed ImEpsImage.
 *
 *	Some parts of this PostScript code were inspired by similar procedures
 *	designed by Loren "Buck" Buchanan of the Naval Research Lab at
 *	Kestrel Associates Inc.  His ideas are used here with his permission
 *	and our thanks.
 */

private char *imEpsImageColor = "\
%%BeginProlog\n\
%  PROC\n\
%	ImEpsImage\n\
%\n\
%  DESCRIPTION\n\
%	RGB hex image data is read from the current file.  If the PostScript\n\
%	device can handle color, the RGB image is imaged as is.  If not, the\n\
%	RGB pixels are converted to grayscale using the NTSC Y equation and\n\
%	imaged.\n\
\n\
/ImEpsImage\n\
{\n\
	/buffer     ImEpsImageW 3 mul string def\n\
	/graybuffer ImEpsImageW string       def\n\
\n\
	ImEpsImageW ImEpsImageH 8\n\
	[ 1 0\n\
	  0 -1\n\
	  0 ImEpsImageH ]\n\
\n\
	% Determine if the PostScript device can handle color by checking if\n\
	% the colorimage operator is defined.\n\
	systemdict /colorimage known\n\
	{\n\
		{\n\
			currentfile buffer readhexstring pop\n\
		} false 3\n\
		colorimage\n\
	}\n\
	{\n\
		% The PostScript device cannot do color.  Convert to grayscale.\n\
		{\n\
			% Get the RGB data\n\
			currentfile buffer readhexstring pop pop\n\
\n\
			% For each pixel...\n\
			0 1 ImEpsImageW 1 sub\n\
			{\n\
				% Compute gray value and store in graybuffer\n\
				graybuffer exch\n\
				dup 3 mul dup 1 add dup 1 add\n\
				     buffer exch get 0.114 mul\n\
				exch buffer exch get 0.587 mul add\n\
				exch buffer exch get 0.299 mul add\n\
				cvi\n\
				put\n\
			} for\n\
			graybuffer\n\
		}\n\
		image\n\
	} ifelse\n\
	showpage\n\
} bind def\n\
\n\
%%EndProlog\n\
";


private char *imEpsImageGray = "\
%%BeginProlog\n\
%  PROC\n\
%	ImEpsImage\n\
%\n\
%  DESCRIPTION\n\
%	Grayscale hex image data is read from the current file and imaged.\n\
\n\
/ImEpsImage\n\
{\n\
	/buffer ImEpsImageW string def\n\
\n\
	ImEpsImageW ImEpsImageH 8\n\
	[ 1 0\n\
	  0 -1\n\
	  0 ImEpsImageH ]\n\
	{\n\
		currentfile buffer readhexstring pop\n\
	}\n\
	image\n\
	showpage\n\
} bind def\n\
\n\
%%EndProlog\n\
";


private char *imEpsImageMono = "\
%%BeginProlog\n\
%  PROC\n\
%	ImEpsImage\n\
%\n\
%  DESCRIPTION\n\
%	Grayscale hex image data is read from the current file and imaged.\n\
\n\
/ImEpsImage\n\
{\n\
	/buffer ImEpsImageW 7 add 8 idiv string def\n\
\n\
	ImEpsImageW ImEpsImageH 1\n\
	[ 1 0\n\
	  0 -1\n\
	  0 ImEpsImageH ]\n\
	{\n\
		currentfile buffer readhexstring pop\n\
	}\n\
	image\n\
	showpage\n\
} bind def\n\
\n\
%%EndProlog\n\
";





/*
 *  GLOBAL
 *	imEpsData	-  PostScript data section
 *
 *  DESCRIPTION
 *	The data section of a PostScript script gives the image data itself.
 *	In our case it defines the size of the image and invokes the image
 *	rendering procedure.  Following it we dump the image data.
 */

private char *imEpsData = "\
%%%%Page:  1 1\n\
userdict begin\n\
/ImEpsImageW	%d def			%% Width in pixels\n\
/ImEpsImageH	%d def			%% Height in pixels\n\
%%%%BeginData:  %d Hex Lines\n\
ImEpsImage\n\
";





/*
 *  GLOBAL
 *	imEpsTrailer	-  PostScript trailer section
 *
 *  DESCRIPTION
 *	The trailer of the page, and file, is just a bunch of DSC keywords.
 */

private char *imEpsTrailer = "\
%%EndData\n\
end\n\
%%PageTrailer\n\
%%Trailer\n\
%%EOF\n\
";





/*
 *  FUNCTION
 *	ImEpsWrite1	-  write a 1-bit monochrome image to a PostScript file
 *	ImEpsWriteGray	-  write a grayscale image to a PostScript file
 *	ImEpsWrite8	-  write an 8-bit index image to a PostScript file
 *	ImEpsWriteRGB	-  write an RGB image to a PostScript file
 *
 *  DESCRIPTION
 *	The image is retrieved from the tag table and a PostScript header
 *	giving the image's dimensions output.  The appropriate PostScript
 *	procedure is dumped (grayscale or color pixel handling), followed
 *	by standard execution code and the pixels themselves.
 */

public int				/* Returns # of entries used	*/
ImEpsWrite1( pMap, ioType, fd, fp, flagsTable, tagTable )
	ImFileFormatWriteMap *pMap;	/* Write map entry to adhear to	*/
	int      ioType;		/* Type of I/O to perform	*/
	int      fd;			/* Output file descriptor	*/
	FILE     *fp;			/* Output file pointer		*/
	TagTable *flagsTable;		/* Flags			*/
	TagTable *tagTable;		/* Table of info to write	*/
{
	register int   ipix;		/* pixel counter		*/
	register ImVfbPtr p;		/* VFB pixel pointer		*/
	register int   pixel;		/* Gray pixel color		*/
	register int   x, y;		/* Pixel location		*/
	int       xdim;			/* # columns			*/
	int       ydim;			/* # rows			*/
	ImVfb    *srcVfb;		/* VFB to convert		*/
	long      clock;		/* Clock time			*/
	char	  message[100];		/* information for ImInfo	*/


	/*  Turn fd into fp so we can use fprintf.			*/
	if ( (ioType & IMFILEIOFD) && (fp = fdopen( fd, "r" )) == NULL )
	{
		ImErrNo = IMESYS;
		return ( -1 );
	}

	/* Dump the header, prolog, script, and data sections.		*/
	TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &srcVfb );
	xdim = ImVfbQWidth( srcVfb );
	ydim = ImVfbQHeight( srcVfb );
	clock = time( NULL );
	fprintf( fp, imEpsHeader,
		xdim, ydim,			/* BoundingBox:		*/
		ImErrorProgramName,		/* Creator:		*/
		getlogin( ),			/* For:			*/
		ctime( &clock ),		/* CreationDate:	*/
		ImErrorFileName );		/* Title:		*/

	sprintf (message, ImErrorFileName);
	ImInfo ("Image Name",message);
	sprintf (message, "%d x %d",xdim, ydim);
	ImInfo ("Resolution",message);
	ImInfo ("Type","1-bit Monochrome");

	/*
	 *  Dump the preview.  Nearly identical to image data except:
	 *	-  0 = white and 1 = black instead of the usual way.
	 *	-  bottom to top instead of top to bottom.
	 */
	fprintf( fp, "%%%%BeginPreview:  %d %d %d %d\n%%", xdim, ydim, 1,
		(((xdim + 7) / 8) * 2 * ydim + 71) / 72 );
	for ( ipix = 0, y = ydim; y; y-- )
	{
		p = ImVfbQPtr( srcVfb, 0, y );
		for ( pixel = 0, x = 1; x <= xdim; x++, ImVfbSInc( srcVfb, p ) )
		{
			pixel = (pixel << 1) | ((~ImVfbQMono( srcVfb, p ))&0x1);
			if ( (x & 0x7) == 0 )
			{
				putc( imEpsHex[ ((pixel >> 4) & 0xF) ], fp );
				putc( imEpsHex[ (pixel & 0xF) ], fp );
				pixel = 0;
				if( ++ipix >= 36 )
				{
					putc( '\n', fp );
					putc( '%', fp );
					ipix = 0;
				}
			}
		}
		if ( (xdim & 0x7) != 0 )
		{
			pixel <<= 8 - (xdim & 0x7);
			putc( imEpsHex[ ((pixel >> 4) & 0xF) ], fp );
			putc( imEpsHex[ (pixel & 0xF) ], fp );
			if( ++ipix >= 36 )
			{
				putc( '\n', fp );
				putc( '%', fp );
				ipix = 0;
			}
		}
	}
	fprintf( fp, "\n%%%%EndPreview\n" );


	/* Dump the data routine.					*/
	fprintf( fp, "%s", imEpsImageMono );
	fprintf( fp, imEpsData,
		xdim,				/* ImEpsImageWidth	*/
		ydim,				/* ImEpsImageHeight	*/
		1 + (((xdim + 7) / 8) * 2 * ydim + 71) / 72 );	/* Lines*/


	/* Dump the pixels.						*/
	p = ImVfbQFirst( srcVfb );
	for ( ipix = 0; ydim; ydim-- )
	{
		for ( pixel = 0, x = 1; x <= xdim; x++, ImVfbSInc( srcVfb, p ) )
		{
			pixel = (pixel << 1) | (ImVfbQMono( srcVfb, p ) & 0x1);
			if ( (x & 0x7) == 0 )
			{
				putc( imEpsHex[ ((pixel >> 4) & 0xF) ], fp );
				putc( imEpsHex[ (pixel & 0xF) ], fp );
				pixel = 0;
				if( ++ipix >= 36 )
				{
					putc( '\n', fp );
					ipix = 0;
				}
			}
		}
		if ( (xdim & 0x7) != 0 )
		{
			pixel <<= 8 - (xdim & 0x7);
			putc( imEpsHex[ ((pixel >> 4) & 0xF) ], fp );
			putc( imEpsHex[ (pixel & 0xF) ], fp );
			if( ++ipix >= 36 )
			{
				putc( '\n', fp );
				ipix = 0;
			}
		}
	}

	/* Dump the trailer.						*/
	fprintf( fp, "\n%s", imEpsTrailer );
	fflush( fp );

	return ( 1 );		/* Used VFB from tag table		*/
}


private void				/* Returns nothing		*/
imEpsWriteGrayPreview( fp, srcVfb )
	FILE  *fp;			/* File to write to		*/
	ImVfb *srcVfb;			/* VFB to write out		*/
{
	register int   ipix;		/* pixel counter		*/
	register ImVfbPtr p;		/* VFB pixel pointer		*/
	register int   pixel;		/* Gray pixel color		*/
	ImVfb    *grayVfb;		/* Grayscale VFB		*/
	int       x, y;			/* Pixel location		*/
	int       xdim;			/* # columns			*/
	int       ydim;			/* # rows			*/

	xdim = ImVfbQWidth( srcVfb );
	ydim = ImVfbQHeight( srcVfb );

	if ( (ImVfbQFields( srcVfb ) & IMVFBINDEX8) == 0 ||
		ImVfbQClt( srcVfb ) != IMCLTNULL )
	{
		/* Not a grayscale VFB.  Make one first.		*/
		grayVfb = ImVfbToGray( srcVfb, IMVFBNEW );
	}
	else
		grayVfb = srcVfb;

	/* Write out a grayscale preview image.				*/
	fprintf( fp, "%%%%BeginPreview:  %d %d %d %d\n%%", xdim, ydim, 8,
		(xdim * 2 * ydim + 71) / 72);
	for ( ipix = 0, y = ydim; y; y-- )
	{
		p = ImVfbQPtr( srcVfb, 0, y );
		for ( x = 0; x < xdim; x++, ImVfbSInc( srcVfb, p ) )
		{
			pixel = ImVfbQGray( srcVfb, p );
			putc( imEpsHex[ (pixel>>4) & 0xf ], fp );
			putc( imEpsHex[ pixel & 0xf ], fp );

			if( ++ipix >= 36 )
			{
				putc( '\n', fp );
				putc( '%', fp );
				ipix = 0;
			}
		}
	}
	fprintf( fp, "\n%%%%EndPreview\n" );

	if ( grayVfb != srcVfb )
		ImVfbFree( grayVfb );
}

public int				/* Returns # of entries used	*/
ImEpsWriteGray( pMap, ioType, fd, fp, flagsTable, tagTable )
	ImFileFormatWriteMap *pMap;	/* Write map entry to adhear to	*/
	int      ioType;		/* Type of I/O to perform	*/
	int      fd;			/* Output file descriptor	*/
	FILE     *fp;			/* Output file pointer		*/
	TagTable *flagsTable;		/* Flags			*/
	TagTable *tagTable;		/* Table of info to write	*/
{
	register int   ipix;		/* pixel counter		*/
	register ImVfbPtr p;		/* VFB pixel pointer		*/
	register int   pixel;		/* Gray pixel color		*/
	register ImVfbPtr last;		/* Last pixel in VFB		*/
	int       xdim;			/* # columns			*/
	int       ydim;			/* # rows			*/
	ImVfb    *srcVfb;		/* VFB to convert		*/
	long      clock;		/* Clock time			*/
	char	  message[100];		/* information for ImInfo	*/


	/*  Turn fd into fp so we can use fprintf.			*/
	if ( (ioType & IMFILEIOFD) && (fp = fdopen( fd, "r" )) == NULL )
	{
		ImErrNo = IMESYS;
		return ( -1 );
	}


	/* Dump the header.						*/
	TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &srcVfb );
	xdim = ImVfbQWidth( srcVfb );
	ydim = ImVfbQHeight( srcVfb );
	clock = time( NULL );
	fprintf( fp, imEpsHeader,
		xdim, ydim,			/* BoundingBox:		*/
		ImErrorProgramName,		/* Creator:		*/
		getlogin( ),			/* For:			*/
		ctime( &clock ),		/* CreationDate:	*/
		ImErrorFileName );		/* Title:		*/

	sprintf (message, ImErrorFileName);
	ImInfo ("Image Name",message);
	sprintf (message, "%d x %d",xdim, ydim);
	ImInfo ("Resolution",message);
	ImInfo ("Type","8-bit Grayscale");


	/*
	 *  Dump the Preview data.  Identical to grayscale data except
	 *  bottom up.
	 */
	imEpsWriteGrayPreview( fp, srcVfb );


	/* Dump the data routine.					*/
	fprintf( fp, "%s", imEpsImageGray );
	fprintf( fp, imEpsData,
		xdim,				/* ImEpsImageWidth	*/
		ydim,				/* ImEpsImageHeight	*/
		1 + (xdim * 2 * ydim + 71) / 72 );


	/* Dump the pixels.						*/
	p    = ImVfbQFirst( srcVfb );
	last = ImVfbQLast( srcVfb );
	for ( ipix = 0; p <= last; ImVfbSInc( srcVfb, p ) )
	{
		pixel = ImVfbQGray( srcVfb, p );
		putc( imEpsHex[ (pixel>>4) & 0xf ], fp );
		putc( imEpsHex[ pixel & 0xf ], fp );

		if( ++ipix >= 36 )
		{
			putc( '\n', fp );
			ipix = 0;
		}
	}

	/* Dump the trailer.						*/
	fprintf( fp, "\n%s", imEpsTrailer );
	fflush( fp );

	return ( 1 );		/* Used VFB from tag table		*/
}

public int				/* Returns # of entries used	*/
ImEpsWrite8( pMap, ioType, fd, fp, flagsTable, tagTable )
	ImFileFormatWriteMap *pMap;	/* Write map entry to adhear to	*/
	int      ioType;		/* Type of I/O to perform	*/
	int      fd;			/* Output file descriptor	*/
	FILE     *fp;			/* Output file pointer		*/
	TagTable *flagsTable;		/* Flags			*/
	TagTable *tagTable;		/* Table of info to write	*/
{
	register int   ipix;		/* pixel counter		*/
	register ImVfbPtr p;		/* VFB pixel pointer		*/
	register int   value;		/* CLT value			*/
	register ImVfbPtr last;		/* Last pixel in VFB		*/
		 int   pixel;		/* Gray pixel color		*/
		 ImClt *clt;		/* VFB's color lookup table	*/
		 ImCltPtr c;		/* CLT entry pointer		*/
	int       xdim;			/* # columns			*/
	int       ydim;			/* # rows			*/
	ImVfb    *srcVfb;		/* VFB to convert		*/
	long      clock;		/* Clock time			*/
	char	  message[100];		/* information for ImInfo	*/


	/*  Turn fd into fp so we can use fprintf.			*/
	if ( (ioType & IMFILEIOFD) && (fp = fdopen( fd, "r" )) == NULL )
	{
		ImErrNo = IMESYS;
		return ( -1 );
	}


	/* Dump the header.						*/
	TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &srcVfb );
	xdim = ImVfbQWidth( srcVfb );
	ydim = ImVfbQHeight( srcVfb );
	clock = time( NULL );
	fprintf( fp, imEpsHeader,
		xdim, ydim,			/* BoundingBox:		*/
		ImErrorProgramName,		/* Creator:		*/
		getlogin( ),			/* For:			*/
		ctime( &clock ),		/* CreationDate:	*/
		ImErrorFileName );		/* Title:		*/

	sprintf (message, ImErrorFileName);
	ImInfo ("Image Name",message);
	sprintf (message, "%d x %d",xdim, ydim);
	ImInfo ("Resolution",message);
	ImInfo ("Type","8-bit Color Indexed");
	ImInfo ("Color Table","256 Entries");


	/* Dump the preview image as 8-bit grayscale, bottom to top.	*/
	imEpsWriteGrayPreview( fp, srcVfb );


	/* Dump the data routine.					*/
	fprintf( fp, "%s", imEpsImageColor );
	fprintf( fp, imEpsData,
		xdim,				/* ImEpsImageWidth	*/
		ydim,				/* ImEpsImageHeight	*/
		1 + (xdim * 6 * ydim + 71) / 72 );


	/* Dump the pixels.						*/
	p    = ImVfbQFirst( srcVfb );
	last = ImVfbQLast( srcVfb );
	clt  = ImVfbQClt( srcVfb );
	for ( ipix = 0; p <= last; ImVfbSInc( srcVfb, p ) )
	{
		pixel = ImVfbQIndex8( srcVfb, p );
		c     = ImCltQPtr( clt, pixel );

		value = ImCltQRed( c );
		putc( imEpsHex[ (value>>4) & 0xf ], fp );
		putc( imEpsHex[ value & 0xf ], fp );

		value = ImCltQGreen( c );
		putc( imEpsHex[ (value>>4) & 0xf ], fp );
		putc( imEpsHex[ value & 0xf ], fp );

		value = ImCltQBlue( c );
		putc( imEpsHex[ (value>>4) & 0xf ], fp );
		putc( imEpsHex[ value & 0xf ], fp );

		if( ++ipix >= 12 )
		{
			putc( '\n', fp );
			ipix = 0;
		}
	}

	/* Dump the trailer.						*/
	fprintf( fp, "\n%s", imEpsTrailer );
	fflush( fp );

	return ( 2 );		/* Used VFB & CLT from tag table	*/
}

public int				/* Returns # of entries used	*/
ImEpsWriteRGB( pMap, ioType, fd, fp, flagsTable, tagTable )
	ImFileFormatWriteMap *pMap;	/* Write map entry to adhear to	*/
	int      ioType;		/* Type of I/O to perform	*/
	int      fd;			/* Output file descriptor	*/
	FILE     *fp;			/* Output file pointer		*/
	TagTable *flagsTable;		/* Flags			*/
	TagTable *tagTable;		/* Table of info to write	*/
{
	register int   ipix;		/* pixel counter		*/
	register ImVfbPtr p;		/* VFB pixel pointer		*/
	register int   pixel;		/* RGB pixel color		*/
	register ImVfbPtr last;		/* Last pixel in VFB		*/
	int       xdim;			/* # columns			*/
	int       ydim;			/* # rows			*/
	ImVfb    *srcVfb;		/* VFB to convert		*/
	long      clock;		/* Clock time			*/
	char	  message[100];		/* information for ImInfo	*/


	/* Turn fd into fp so we can use fprintf.			*/
	if ( (ioType & IMFILEIOFD) && (fp = fdopen( fd, "r" )) == NULL )
	{
		ImErrNo = IMESYS;
		return ( -1 );
	}


	/* Dump the header.						*/
	TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &srcVfb );
	xdim = ImVfbQWidth( srcVfb );
	ydim = ImVfbQHeight( srcVfb );
	clock = time( NULL );
	fprintf( fp, imEpsHeader,
		xdim, ydim,			/* BoundingBox:		*/
		ImErrorProgramName,		/* Creator:		*/
		getlogin( ),			/* For:			*/
		ctime( &clock ),		/* CreationDate:	*/
		ImErrorFileName );		/* Title:		*/

	sprintf (message, ImErrorFileName);
	ImInfo ("Image Name",message);
	sprintf (message, "%d x %d",xdim, ydim);
	ImInfo ("Resolution",message);
	ImInfo ("Type","24-bit RGB");


	/* Dump the preview image as 8-bit grayscale, bottom to top.	*/
	imEpsWriteGrayPreview( fp, srcVfb );


	/* Dump the data routine.					*/
	fprintf( fp, "%s", imEpsImageColor );
	fprintf( fp, imEpsData,
		xdim,				/* ImEpsImageWidth	*/
		ydim,				/* ImEpsImageHeight	*/
		1 + (xdim * 6 * ydim + 71) / 72 );

	/* Dump the pixels.						*/
	p    = ImVfbQFirst( srcVfb );
	last = ImVfbQLast( srcVfb );
	for ( ipix = 0; p <= last; ImVfbSInc( srcVfb, p ) )
	{
		pixel = ImVfbQRed( srcVfb, p );
		putc( imEpsHex[ (pixel>>4) & 0xf ], fp );
		putc( imEpsHex[ pixel & 0xf ], fp );

		pixel = ImVfbQGreen( srcVfb, p );
		putc( imEpsHex[ (pixel>>4) & 0xf ], fp );
		putc( imEpsHex[ pixel & 0xf ], fp );

		pixel = ImVfbQBlue( srcVfb, p );
		putc( imEpsHex[ (pixel>>4) & 0xf ], fp );
		putc( imEpsHex[ pixel & 0xf ], fp );

		if( ++ipix >= 12 )
		{
			putc( '\n', fp );
			ipix = 0;
		}
	}

	/* Dump the trailer.						*/
	fprintf( fp, "\n%s", imEpsTrailer );
	fflush( fp );

	return ( 1 );		/* Used VFB from tag table		*/
}
