/**
 **	$Header: /import/dev-vis/image/imtools/v2.0/libim/src/RCS/imps.c,v 1.12 92/12/03 01:51:31 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/imps.c,v 1.12 92/12/03 01:51:31 nadeau Exp $"

/**
 **  FILE
 **	imps.c		-  PostScript file output
 **
 **  PROJECT
 **	libim	-  SDSC image manipulation library
 **
 **  DESCRIPTION
 **	imps.c contains a write routine to write 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
 **
 **	ImPsWrite1	f  write a 1-bit monochrome image to a PostScript file
 **	ImPsWriteGray	f  write a grayscale image to a PostScript file
 **	ImPsWrite8	f  write an 8-bit index image to a PostScript file
 **	ImPsWriteRGB	f  write an RGB image to a PostScript file
 **
 **  PRIVATE CONTENTS
 **	imPsHex		v  Integer->Hex character conversion table
 **	imPsHeader	v  PostScript header section
 **	imPsProlog	v  PostScript prolog section
 **	imPsImageColor	v  Color printing PostScript procedure
 **	imPsImageGray	v  Grayscale printing PostScript procedure
 **	imPsImageMono	v  Monochrome printing PostScript procedure
 **	imPsScript	v  PostScript page script section
 **	imPsData	v  PostScript data section
 **	imPsTrailer	v  PostScript trailer section
 **
 **  HISTORY
 **	$Log:	imps.c,v $
 **	Revision 1.12  92/12/03  01:51:31  nadeau
 **	Corrected info messages.
 **	
 **	Revision 1.11  92/11/04  12:05:47  groening
 **	put ImFIleFormat info and magic number info
 **	from imfmt.c into this file.
 **	
 **	Revision 1.10  92/10/19  14:13:28  groening
 **	added ImInfo statements
 **	
 **	Revision 1.9  92/08/31  17:33:06  vle
 **	Updated copyright notice.
 **	
 **	Revision 1.8  91/10/03  09:16:19  nadeau
 **	Extensive rewrite to update to match Adobe 3.0 DSC.
 **	
 **	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"

/*
 *  PS - Adobe PostScript
 *      For information on these structures, how to use them, etc. please
 *      see imfmt.c.
 */
extern int ImPsWrite1( ), ImPsWriteGray( ), ImPsWrite8( ), ImPsWriteRGB( );
private char *imPsNames[ ]  = { "ps", "postscript", NULL };
private uchar imPsMagicNumber[ ] = { '!', '%' };
private ImFileFormatWriteMap imPsWriteMap[ ] =
{
        /* in                   out                                     */
        /* VFB type,    attr.,  type,ch,dep,    attr.,  func            */
        { IMVFBMONO,    0,      IN,1,1,         0,      ImPsWrite1 },
        { IMVFBINDEX8,  0,      IN,1,8,         0,      ImPsWriteGray },
        { IMVFBINDEX8,  C,      IN,1,8,         C,      ImPsWrite8 },
        { IMVFBRGB,     0,      RGB,3,8,        0,      ImPsWriteRGB },
        { -1,           0,      -1,             0,      NULL },
};

private ImFileMagic imFilePsMagic1=
{
	0, 2, imPsMagicNumber
};

private ImFileMagic *imFilePsMagic []=
{
	&imFilePsMagic1,
	NULL
};

public ImFileFormat ImFilePsFormat =
{
	imPsNames, "Adobe PostScript file",
	"Adobe",
	"None.  PostScript images cannot be read in!",
	"1-bit monochrome, 8-bit grayscale, 8-bit color index, and 24-bit\n\
RGB color images.",
	imFilePsMagic,
	FALSE, FALSE,
	FALSE, TRUE,
	NULL, NULL, imPsWriteMap
};


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





/*
 *  GLOBAL
 *	imPsHeader	-  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 *imPsHeader = "\
%%!PS-Adobe-3.0\n\
%%%%Creator:       %s\n\
%%%%For:           %s\n\
%%%%CreationDate:  %s\
%%%%Title:         %s\n\
%%%%BoundingBox:   0 0 612 792\n\
%%%%Orientation:   Portrait\n\
%%%%PageOrder:     Ascend\n\
%%%%Pages:         1\n\
%%%%EndComments\n\
";





/*
 *  GLOBAL
 *	imPsProlog	-  PostScript prolog section
 *
 *  DESCRIPTION
 *	The PostScript file prolog defines the various functions (procs)
 *	to be used in the PostScript program.  In our case we define some
 *	functions to convert inches to dots, we define some flags, and we
 *	define the generic, super-flexible ImPsPrepare routine.
 */

private char *imPsProlog = "\
%%BeginProlog\n\
\n\
\n\
\n\
%  WANT TO CHANGE THE IMAGE SIZE AND POSITION ?\n\
%	This is easy!  Search forward for 'CHANGE THIS' for instructions.\n\
\n\
\n\
\n\
%  PROC\n\
%	value inch\n\
%	value inches\n\
%	value inchs\n\
%\n\
%  DESCRIPTION\n\
%	Map inches to dots.\n\
\n\
/inch   { 72 mul } bind def\n\
/inches { 72 mul } bind def\n\
/inchs  { 72 mul } bind def\n\
\n\
\n\
%  CONSTANTS\n\
%	autosize\n\
%	autosizeInches\n\
%	autoorient\n\
%	landscape\n\
%	portrait\n\
\n\
/autosize   -1 def\n\
/autosizeInches autosize inches def\n\
/autoorient -1 def\n\
/landscape   1 def\n\
/portrait    2 def\n\
\n\
\n\
%  PROC\n\
%	ImPsPrepare\n\
%\n\
%  DESCRIPTION\n\
%	The orientation, position, and size of the image are determined and\n\
%	transforms made.\n\
%\n\
%	If no explicit image size has been given, the image is autosized to\n\
%	make maximum use of available space.  The aspect ratio of the page is\n\
%	compared with that of the image in order to decide if rotating the\n\
%	image will allow us to scale it up bigger.  The image is then\n\
%	positioned and scaled as large as possible, while maintaining its\n\
%	original aspect ratio.\n\
\n\
/ImPsPrepare\n\
{\n\
	/ImPsImageAspect ImPsImageH ImPsImageW div def\n\
\n\
	% Determine the orientation to use for the image.\n\
	ImPsOrient autoorient eq\n\
	{\n\
		% Defaulted.  Set it to the direction that will let us scale\n\
		% the image up the most.\n\
		ImPsImageAspect 1 lt\n\
		{\n\
			/ImPsOrient landscape def\n\
		}\n\
		{\n\
			/ImPsOrient portrait def\n\
		}\n\
	} if\n\
\n\
\n\
	% Position and size the image.\n\
	ImPsOrient portrait eq\n\
	{\n\
		% Upright orientation.  Determine size of printed image.\n\
\n\
		ImPsPageLLCornerX ImPsPositionX add\n\
		ImPsPageLLCornerY ImPsPositionY add translate\n\
\n\
		ImPsSizeW autosize eq ImPsSizeW autosizeInches eq or\n\
		ImPsSizeH autosize eq ImPsSizeH autosizeInches eq or and\n\
		{\n\
			% Both sizes defaulted.\n\
			/ImPsSizeH ImPsPageH ImPsPositionX sub def\n\
			/ImPsSizeW ImPsPageW ImPsPositionY sub def\n\
			/ImPsPageAspect ImPsSizeH ImPsSizeW div def\n\
\n\
			ImPsImageAspect ImPsPageAspect lt\n\
			{\n\
				/ImPsSizeH ImPsSizeW ImPsImageAspect mul def\n\
			}\n\
			{\n\
				/ImPsSizeW ImPsSizeH ImPsImageAsect div def\n\
			} ifelse\n\
		}\n\
		{\n\
			% One or the other size specified.  Set the other one.\n\
			ImPsSizeW autosize eq ImPsSizeW autosizeInches eq or\n\
			{\n\
				% Height given.  Set width.\n\
				/ImPsSizeW ImPsSizeH ImPsImageAspect div def\n\
			} if\n\
			ImPsSizeH autosize eq ImPsSizeH autosizeInches eq or\n\
			{\n\
				% Width given.  Set height.\n\
				/ImPsSizeH ImPsSizeW ImPsImageAspect mul def\n\
			} if\n\
		} ifelse\n\
		ImPsSizeW ImPsSizeH scale\n\
	}\n\
	{\n\
		% Sideways orientation.  Determine size of printed image.\n\
\n\
		ImPsSizeW autosize eq ImPsSizeW autosizeInches eq or\n\
		ImPsSizeH autosize eq ImPsSizeH autosizeInches eq or and\n\
		{\n\
			% Both sizes defaulted.\n\
			/ImPsSizeH ImPsPageW ImPsPositionX sub def\n\
			/ImPsSizeW ImPsPageH ImPsPositionY sub def\n\
			/ImPsPageAspect ImPsSizeH ImPsSizeW div def\n\
\n\
			ImPsImageAspect 1 ImPsPageAspect div lt\n\
			{\n\
				/ImPsSizeH ImPsSizeW ImPsImageAspect mul def\n\
			}\n\
			{\n\
				/ImPsSizeW ImPsSizeH ImPsImageAspect div def\n\
			} ifelse\n\
		}\n\
		{\n\
			ImPsSizeW ImPsSizeH\n\
			/ImPsSizeW exch def\n\
			/ImPsSizeH exch def\n\
\n\
			% One or the other size specified.  Set the other one.\n\
			ImPsSizeW autosize eq ImPsSizeW autosizeInches eq or\n\
			{\n\
				% Height given.  Set width.\n\
				/ImPsSizeW ImPsSizeH ImPsImageAspect div def\n\
			} if\n\
			ImPsSizeH autosize eq ImPsSizeH autosizeInches eq or\n\
			{\n\
				% Width given.  Set height.\n\
				/ImPsSizeH ImPsSizeW ImPsImageAspect mul def\n\
			} if\n\
		} ifelse\n\
		ImPsPageLLCornerX ImPsPositionX add ImPsSizeH add\n\
		ImPsPageLLCornerY ImPsPositionY add translate\n\
		90 rotate\n\
\n\
		ImPsSizeW ImPsSizeH scale\n\
	} ifelse\n\
} bind def\n\
\n\
\n\
";





/*
 *  GLOBALS
 *	imPsImageColor	-  Color printing PostScript procedure
 *	imPsImageGray	-  Grayscale printing PostScript procedure
 *	imPsImageMono	-  Monochrome printing PostScript procedure
 *
 *  DESCRIPTION
 *	Each of these three globals defines the same function (proc) for the
 *	PostScript file header.  In each case, the ImPsImage function takes
 *	hex image data and renders it onto the page.  The difference is only
 *	in the type of data being fed ImPsImage.
 *
 *	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 *imPsImageColor = "\
%  PROC\n\
%	ImPsImage\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\
/ImPsImage\n\
{\n\
	/buffer     ImPsImageW 3 mul string def\n\
	/graybuffer ImPsImageW string       def\n\
\n\
	ImPsPrepare\n\
\n\
	ImPsImageW ImPsImageH 8\n\
	[ ImPsImageW   0\n\
	  0            ImPsImageH neg\n\
	  0            ImPsImageH ]\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 ImPsImageW 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 *imPsImageGray = "\
%  PROC\n\
%	ImPsImage\n\
%\n\
%  DESCRIPTION\n\
%	Grayscale hex image data is read from the current file and imaged.\n\
\n\
/ImPsImage\n\
{\n\
	/buffer ImPsImageW string def\n\
\n\
	ImPsPrepare\n\
\n\
	ImPsImageW ImPsImageH 8\n\
	[ ImPsImageW   0\n\
	  0            ImPsImageH neg\n\
	  0            ImPsImageH ]\n\
	{\n\
		currentfile buffer readhexstring pop\n\
	}\n\
	image\n\
	showpage\n\
} bind def\n\
\n\
%%EndProlog\n\
";


private char *imPsImageMono = "\
%  PROC\n\
%	ImPsImage\n\
%\n\
%  DESCRIPTION\n\
%	Grayscale hex image data is read from the current file and imaged.\n\
\n\
/ImPsImage\n\
{\n\
	/buffer ImPsImageW 7 add 8 idiv string def\n\
\n\
	ImPsPrepare\n\
\n\
	ImPsImageW ImPsImageH 1\n\
	[ ImPsImageW   0\n\
	  0            ImPsImageH neg\n\
	  0            ImPsImageH ]\n\
	{\n\
		currentfile buffer readhexstring pop\n\
	}\n\
	image\n\
	showpage\n\
} bind def\n\
\n\
%%EndProlog\n\
";





/*
 *  GLOBAL
 *	imPsScript	-  PostScript page script section
 *
 *  DESCRIPTION
 *	The PostScript file script is executed for each page (in our case
 *	only one).  The generic portion of the script sets some variables
 *	and gives comments to a potential editor of the PostScript file.
 */

private char *imPsScript = "\
%%Page:  1 1\n\
%%BeginPageSetup\n\
\n\
% Determine the available print area.\n\
gsave clippath pathbbox grestore\n\
/ImPsPageH 		exch def\n\
/ImPsPageW  		exch def\n\
/ImPsPageLLCornerY      exch def\n\
/ImPsPageLLCornerX      exch def\n\
/ImPsPageH 		ImPsPageH ImPsPageLLCornerY sub def\n\
/ImPsPageW  		ImPsPageW ImPsPageLLCornerX sub def\n\
\n\
%%EndPageSetup\n\
\n\
\n\
\n\
% CHANGE THIS !\n\
%	If you want to override the automatic sizing and positioning\n\
%	of the image when printing, change the following lines to your\n\
%	desired values.\n\
%\n\
%	If you leave one or the other size set to 'autosize', it will be\n\
%	filled in automatically, maintaining the image's aspect ratio.\n\
\n\
/ImPsOrient	autoorient def		% landscape or portrait or autoorient\n\
\n\
/ImPsSizeH	autosize inches def	% Height in inches\n\
/ImPsSizeW	autosize inches def	% Width in inches\n\
\n\
/ImPsPositionX	0 inches def		% Lower-left corner\n\
/ImPsPositionY	0 inches def		% Lower-left corner\n\
\n\
\n\
\n\
% Do not change this.\n\
";





/*
 *  GLOBAL
 *	imPsData	-  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 *imPsData = "\
/ImPsImageW	%d def			%% Width in pixels\n\
/ImPsImageH	%d def			%% Height in pixels\n\
%%%%BeginData:  %d Hex Lines\n\
ImPsImage\n\
";





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

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





/*
 *  FUNCTION
 *	ImPsWrite1	-  write a 1-bit monochrome image to a PostScript file
 *	ImPsWriteGray	-  write a grayscale image to a PostScript file
 *	ImPsWrite8	-  write an 8-bit index image to a PostScript file
 *	ImPsWriteRGB	-  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	*/
ImPsWrite1( 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;		/* Column location		*/
	int       xdim;			/* # columns			*/
	int       ydim;			/* # rows			*/
	ImVfb    *srcVfb;		/* VFB to convert		*/
	long      clock;		/* Clock time			*/
	char	   message[512];	/* char string for ImInfo to printout */


	/*  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, imPsHeader,
		ImErrorProgramName,		/* Creator:		*/
		getlogin( ),			/* For:			*/
		ctime( &clock ),		/* CreationDate:	*/
		ImErrorFileName );		/* Title:		*/

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

	fprintf( fp, "%s", imPsProlog );
	fprintf( fp, "%s", imPsImageMono );
	fprintf( fp, "%s", imPsScript );
	fprintf( fp, imPsData,
		xdim,				/* ImPsImageWidth	*/
		ydim,				/* ImPsImageHeight	*/
		1 + (((xdim + 7) / 8) * 2 * ydim + 71) / 72 );


	/* 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( imPsHex[ ((pixel >> 4) & 0xF) ], fp );
				putc( imPsHex[ (pixel & 0xF) ], fp );
				pixel = 0;
				if( ++ipix >= 36 )
				{
					putc( '\n', fp );
					ipix = 0;
				}
			}
		}
		if ( (xdim & 0x7) != 0 )
		{
			pixel <<= 8 - (xdim & 0x7);
			putc( imPsHex[ ((pixel >> 4) & 0xF) ], fp );
			putc( imPsHex[ (pixel & 0xF) ], fp );
			if( ++ipix >= 36 )
			{
				putc( '\n', fp );
				ipix = 0;
			}
		}
	}

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

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

public int				/* Returns # of entries used	*/
ImPsWriteGray( 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[512];	/* char string for ImInfo to printout */


	/*  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, imPsHeader,
		ImErrorProgramName,		/* Creator:		*/
		getlogin( ),			/* For:			*/
		ctime( &clock ),		/* CreationDate:	*/
		ImErrorFileName );		/* Title:		*/

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

	fprintf( fp, "%s", imPsProlog );
	fprintf( fp, "%s", imPsImageGray );
	fprintf( fp, "%s", imPsScript );
	fprintf( fp, imPsData,
		xdim,				/* ImPsImageWidth	*/
		ydim,				/* ImPsImageHeight	*/
		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( imPsHex[ (pixel>>4) & 0xf ], fp );
		putc( imPsHex[ pixel & 0xf ], fp );

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

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

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

public int				/* Returns # of entries used	*/
ImPsWrite8( 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[512];	/* char string for ImInfo to printout */


	/*  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, imPsHeader,
		ImErrorProgramName,		/* Creator:		*/
		getlogin( ),			/* For:			*/
		ctime( &clock ),		/* CreationDate:	*/
		ImErrorFileName );		/* Title:		*/

        sprintf (message, "%d x %d",xdim, ydim);
        ImInfo ("Resolution",message);
        ImInfo ("Type","8-bit Color Indexed");

	fprintf( fp, "%s", imPsProlog );
	fprintf( fp, "%s", imPsImageColor );
	fprintf( fp, "%s", imPsScript );
	fprintf( fp, imPsData,
		xdim,				/* ImPsImageWidth	*/
		ydim,				/* ImPsImageHeight	*/
		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( imPsHex[ (value>>4) & 0xf ], fp );
		putc( imPsHex[ value & 0xf ], fp );

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

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

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

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

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

public int				/* Returns # of entries used	*/
ImPsWriteRGB( 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[512];	/* char string for ImInfo to printout */


	/* 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, imPsHeader,
		ImErrorProgramName,		/* Creator:		*/
		getlogin( ),			/* For:			*/
		ctime( &clock ),		/* CreationDate:	*/
		ImErrorFileName );		/* Title:		*/

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


	fprintf( fp, "%s", imPsProlog );
	fprintf( fp, "%s", imPsImageColor );
	fprintf( fp, "%s", imPsScript );
	fprintf( fp, imPsData,
		xdim,				/* ImPsImageWidth	*/
		ydim,				/* ImPsImageHeight	*/
		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( imPsHex[ (pixel>>4) & 0xf ], fp );
		putc( imPsHex[ pixel & 0xf ], fp );

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

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

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

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

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