/***********************************************************************\
Converter class for Convert PICT which converts graphics from PICT to eps formats.
Copyright (C) 1993 David John Burrowes

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, 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 General Public License for more details.

You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

The author, David John Burrowes, can be reached at:
	davidjohn@kira.net.netcom.com
	David John Burrowes
	1926 Ivy #10
	San Mateo, CA 94403-1367
\***********************************************************************/

#define VERSIONSTRING "Convert PICT 1.2"

/*
====================================================================
	This is $Revision: 1.10 $ of this file
	It was last modified by $Author: death $ on $Date: 93/04/04 23:29:57 $
Note that this file was created while using the New Century Schoolbook Roman typeface.  You may find that some things line up strangely if you don't use that family.

History:
	93.07.18	djb	Added support for 	the user's having a choice of either writing out the
				piccomments as before, or having them ommitted.
	93.08.01	djb	Finished Objective-C support for PicComments defined in tech notes.

$Log:	PictConverter.m,v $
Revision 1.10  93/04/04  23:29:57  death
Sun Apr  4 23:29:57 PDT 1993

Revision 1.9  93/01/09  21:07:09  death
Sat Jan  9 21:07:09 PST 1993

Revision 1.8  93/01/01  11:51:17  death
Fri Jan  1 11:51:17 PST 1993

Revision 1.7  92/12/31  15:33:55  death
Thu Dec 31 15:33:55 PST 1992

Revision 1.6  92/12/05  23:06:28  death
Sat Dec  5 23:06:28 PST 1992

 ====================================================================
 */
 
#import "PictConverter.h" 
#import "PICTFile.h"
#import "PSFile.h"
#import <stdio.h>
#import <string.h>
#import <strings.h>
#import <stdlib.h>
#include <time.h>
#import <architecture/byte_order.h>	// for big/little endian conversins
#import "RegionConverter.h"
#import "MacToNeXTText.h"
@implementation PictConverter

/*
	Layout of the groups of routines in this file (in order)
		Administrative/ setup
		High level opcode parsers
		General opcode processing
		Processing utilities (e.g. WriteRect)
		Primary data conversion routines
		Bitmap and pattern processing (about 25-30% of the file)
	Notes:
		Warning.  In bitmap routines, we routinely write out > and 80 as terminators for the
		various PS filters.  Make any changes to these parts with caution, since PS lvl 1 code
		depends on precisely the number of bytes they are (including end of lines)
		In PS code, if you remove rapping for oval sizes that are too big, you can get neat
		results. =)
	Enhancements:
		The percent indicator on the conversion window is not updated when you'd think it
		should be (display is always one step behind what it thinks it's drawn , Ithink)
		Convert packed color bitmaps straight through, rather than unpack and then
		repack?  (Might be able to do similar for B&W, by inverting in place)
		Warning: The signs of arguments of some opcodes, esp. in Miscellaneous group, have
		not been thoroughly tested.
		Enhance overall error checking, including providing gracefull aborting (what if
		pixmap has unfamiliar values?
		My treatment of RGB color values may be bad, in that I'm assuming they are
		8 bit values in a 16 space.  This may not be accurate. 
*/




//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Administrative/setup methods
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		init
//	Parameters:	none
//	Returns: 	self
//	Stores:		none
//	Description:
//		This initializes the instance variables of the created object.
//	History:
//		93.07.18	djb	Added initializing of the new instance variable PicCommentOp...
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- init
{
	sourceFile = NullInstance;
	destFile = NullInstance;

	[super   init];
	//
	//	The following are sorted by opcode number, basically
	//
	UsedPatterns = NO;
	UsedColors = NO;
	UsedLines = NO;
	UsedText = NO;
	UsedRectangles = NO;
	UsedRoundRectangles = NO;
	UsedOvals = NO;
	UsedArcs = NO;
	UsedPolygons = NO;
	UsedRegions = NO;
	UsedBitmaps = NO;
	UsedComments = NO;
	UsedMiscellaneous = NO;

	CodeDir = NewCString(0);

	PackingSetting = YES;

	textConverter = [[MacToNeXTText  alloc] init];
	ConvertCurrentCharacters = YES;
	UsersCharConvertChoice = NO;
	PicCommentOperation = ConvertPicComments;
	return self;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		free
//	Parameters:	none
//	Returns: 	self
//	Stores:		none
//	Description:
//		This disposes of the CodeDir instance variable
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- free
{
	FreeCString(CodeDir);
	[textConverter   free];
	[super   free];
	return self;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		SetImagePacking:CLUTIncluding:AndCodeSet:
//	Parameters:	three boolean values
//	Returns: 	self
//	Stores:		none
//	Description:
//		This routine allows a caller to change two settings that affect the behavior of
//		an instance of this class.  The three values are:
//			- whether we pack bitmap when we write them out, or leave them unpacked
//			- Where we should locate the PS code to include with the code we generate.
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- SetImagePacking: (Boolean) packimages UsingPSIn: (CString) newCodeDirectory
{
	PackingSetting = packimages;
	if (newCodeDirectory != NullCString)
	{
		FreeCString(CodeDir);
		CodeDir = NewCString(strlen(newCodeDirectory));
		strcpy(CodeDir, newCodeDirectory);
	}
	return self;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		SetConvertAllChars:
//	Parameters:	boolean value
//	Returns: 	self
//	Stores:		none
//	Description:
//		Tweaks one aspect of this application's character conversion.  If YES, then
//		chars in unfamiliar families will be converted.  If NO, then they won't.  
//	History:
//		93.07.18	djb	Removed a semicolon from the end of the method name.
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- SetConvertAllChars: (Boolean) convertAll
{
	UsersCharConvertChoice = convertAll;
	return self;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		SetPicCommentConversion:
//	Parameters:	An enumerated value indicating what kind of piccomment conversion
//				should be done hereafter.
//	Returns: 	self
//	Stores:		none
//	Description:
//		This sets my instance variable PicCommentOperation to the specified
//		value.  This allows someone else to dictate how I should deal with PicComments
//		until this setting is reset.
//	History:
//		93.07.18	djb	Created
//	Bugs:
//		This doesn't assure that the enumerated parameter is a 'good' value.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- SetPicCommentConversion: (PicCommentOpType) picConversion
{
	PicCommentOperation = picConversion;
	return self;
}



//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		isThisAGoodFile:
//	Parameters:	A Pict File instance
//	Returns: 	YES if it is, NO if it isn't.
//	Stores:		none
//	Description:
//		Get the pict file's version number.  If it's 1 or 2, then return that it is a good file.
//		This should never answer 'NO', because the pict file itself will have failed to open
//		fail to open if it thinks the file is not legit.
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (Boolean) isThisAGoodFile: theFile
{
	Integer			version;
	Boolean			result	= NO;

	version = [theFile   GetVersion];
	if ((version== 2) || (version == 1))
		result = YES;
	else
	{
		[self  PutCString: "This doesn't look like a PICT file to me (could not find the `version' opcode).  Proceed with extreme caution." Into: SECOND_RESULT];
		result = NO;
	}
	return result;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		ConvertPICTfile:ToEPSfile:
//	Parameters:
//		The PICT file to convert from
//		the eps file to store into
//	Returns: 	errors
//	Stores:		none
//	Description:
//		Ths is the main conversion routine.  It converts a pict file into the specified
//		postscript file.  After initializations, this involves a couple steps: convert all
//		the PICT opcodes into a temporary file.  While doing this, keep track of what
//		types of opcodes we have used.  For instance, if we get a call to the FrameRect
//		opcode, we will set UsedRectangles to YES.  When this is done, write a header
//		onto the eps file, and then copy over all the PS groups that were actually used
//		(this allows the destination PS file to be smaller than if we copied everything).
//		After this, copy the contents of the temporary file to the PS file (the stuff was
//		written to the temporary file earlier, because we needed to get the header
//		and group definitions written before it).  Finally, write the trailing restore, etc.
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ConvertPICTfile: pictFile ToEPSfile: epsfile
{
	Boolean		finished			= NO;
	Integer		opcodeCount		= 0;
	Real			fileSize 			= [pictFile   FileSize] * 1.0;
	CString		tempString		= NewCString(100);
	PICTRect*	bounds 			= [pictFile   GetBounds];
	
	//
	//	Initalize the PS group flags, so we don't include PS code we don't want.
	//
	UsedPatterns = NO;
	UsedColors = NO;
	UsedLines = NO;
	UsedText = NO;
	UsedRectangles = NO;
	UsedRoundRectangles = NO;
	UsedOvals = NO;
	UsedArcs = NO;
	UsedPolygons = NO;
	UsedRegions = NO;
	UsedComments = NO;
	UsedMiscellaneous = NO;
	UsedBitmaps = NO;
	//
	//
	//
	ConvertCurrentCharacters = YES;
	//
	//	Set the instance variable 'sourceFile' to be the pict file,
	//	set the destFile to be a temporary file, for now.
	//
	sourceFile = pictFile;
	destFile = [[PSFile alloc] initAndUseTemporary];
	if ([destFile   GetErrorCode] == ERR_OK)
		[destFile   CreateAndOpenFor: FILE_READWRITE];
	if  ([destFile   GetErrorCode] != ERR_OK) 
	{
		[self   StoreErrorCode: ERR_CREATEFAILED
			AndText: "Could not create a necessary, temporary, file"];
		[destFile   free];
		destFile = NullInstance;
	}
	else
	{
		//
		//	Write the initiating 'startPict' which will be balanced, later, by the endPict opcode 0xFF.
		//
		[destFile   WriteInteger:  [sourceFile GetVersion]];
		[self   WritePSProcedureName: "startPict"];
		//
		//	With source and destination file established, convert all the opcodes..
		//	(Let out manager know how the conversion process is going every 10 opcodes)
		//
		while (finished == NO)
		{
			finished = [self ParseOpcode: [sourceFile   GetOpcode]];
			if ((opcodeCount % 10) == 0)
				if ( [myManager   respondsTo:@selector(SetPercentageDone:)] ) 
					[myManager   SetPercentageDone: ([sourceFile   GetCurrentPosition] * 100.0) /  fileSize];
		}
		//
		//	If no error occurred, then proceed
		//
		if ([self   GetErrorCode] >= ERR_OK)
		{
			//
			//	Write the PS Header and prolog of group ps code  Add the 'save' line.
			//
			[self   BuildHeaderInto: epsfile];
			[self   BuildPrologInto: epsfile];
			[epsfile   WritePSLine: "save"];
			[epsfile   ForceNewLine];
			//
			//	Write the matrix to compensate for the PICT and PS coordinate systems
			//
			[epsfile   WriteTextUsing: tempString
				WithFormat: "[1 0 0 -1 %d %d] concat\n", -1*bounds->left, bounds->bottom];
			//
			//	Write all the data from the conversion of the opcodes, and then the terminating 'restore'.
			//
			[epsfile   AppendFrom: destFile];
			[epsfile   ForceNewLine];
			[epsfile   WritePSLine: "restore"];
		}
		[destFile   CloseAndDelete];	// Remember that this is the temp file.  The 'destination'  of just the opcode conversion
		[destFile   free];
	}
	FreeCString(tempString);
	FreeByteString((ByteString)bounds);
	return self;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		SimpleStringConvert:
//	Parameters:	A CString structure
//	Returns: 	A converted CString
//	Stores:		none
//	Description:
//		This is a quick hack that takes a string, and turns it into another string with
//		non-printable characters escaped, and (\) all escaped as well.  The resulting string
//		is returned (truncated to 255 chars if necessary)
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (CString) SimpleStringConvert: (CString) theString
{
	CString	filteredString;
	CString	tempString;
	PositiveInteger	filterIndex;
	PositiveInteger	index;
	PositiveInteger	newLength = strlen(theString);
	//
	//	Now, filter this string, replacing certain troublesome characters with
	//	escaped equivalents.
	//
	filteredString = NewCString(newLength*4); // worst case each all will become \ddd
	filterIndex = 0;
	for (index = 0; index < newLength; index++)
	{
		if	( ( (PositiveInteger) theString[index] < 32 ) ||
			(PositiveInteger) theString[index] > 126 )
		{
			tempString = NewCString(4);
			//
			//	93.01.31	djb	OOPS!  Had been using %.3u, not octal!
			//
			sprintf(tempString, "\\%.3o", (unsigned int) theString[index]);
			strcat(filteredString, tempString);
			FreeCString(tempString);
			filterIndex += 4;
		}
		else
		{
			switch (theString[index])
			{
				case '\\' :
					strcat(filteredString, "\\\\");
					filterIndex += 2;
					break;
				case '(' :
					strcat(filteredString, "\\(");
					filterIndex += 2;
					break;
				case ')' :
					strcat(filteredString, "\\)");
					filterIndex += 2;
					break;
				default :
					filteredString[filterIndex] = theString[index];
					filterIndex ++;
					filteredString[filterIndex] = EndOfCString; // put a null back on.
					break;
			}
		}
	
	}
	//
	//	Don't let the string be longer than 255 chars (p. 639 of Big fat red ps book)
	//
	if (strlen(filteredString) > 255)
		filteredString[255] = EndOfCString;
	FreeCString(theString);
	return filteredString;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		BuildHeaderInto:
//	Parameters:	a PSfile object.
//	Returns: 	errors
//	Stores:		none
//	Description:
//		When we're ready to start building the output PS file.
//		We build a nice standard PS header here. 
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

- BuildHeaderInto: thisFile
{
	CString		tempString		= NewCString(255);
	CString		tempBasename;
	PICTRect*	bounds 			= [sourceFile   GetBounds];
	Integer		thetime;
	CString		timeString;
	//
	//	Write out inital lines.
	//	
	[thisFile   WriteComment: "!PS-Adobe-3.0 EPSF-3.0"];
	//
	//	Write as an EPS bounding box
	//
	[thisFile   WriteDSCCommentUsing: tempString
		WithFormat: "BoundingBox: %d %d %d %d",0, 0,
		(bounds->right - bounds->left), (bounds->bottom)-(bounds->top)];
	//
	//	Record the name of the file, and close off the beginning comments.
	//	
	tempBasename = [self  SimpleStringConvert: [sourceFile   GetBasename]];
	[thisFile   WriteDSCCommentUsing: tempString
		WithFormat: "Title: (%s)", tempBasename];
	[thisFile   WriteDSCCommentUsing: tempString
		WithFormat: "Creator: (%s)", VERSIONSTRING];
	thetime = time(NULL);
	timeString = (CString) ctime(&thetime);
	sprintf(tempString, "CreationDate: (%s", timeString); 
	tempString[strlen(tempString)-1] = EndOfCString; // ctime puts a \n on which we don't want
	strcat(tempString,")");
	[thisFile WriteDSCComment: tempString];
	[thisFile   WriteDSCComment: "EndComments"];
	FreeCString(tempBasename);
	
	FreeCString(tempString);
	FreeByteString((ByteString)bounds);
	return self;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		AppendFile:
//	Parameters:	the pathname of a file, and a file instance to append it to.
//	Returns: 	errors
//	Stores:		none
//	Description:
//		Given a file, and the path to a file, open the path, and append its contents
//		to the instance.  If we fail to do this, report an error.
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

- AppendFile: (CString) codeFile To: thisFile
{
	Instance	tempFile;
	
	tempFile = [[PSFile alloc] initAndUse: codeFile];
	if ([tempFile   GetErrorCode] == ERR_OK)
		[tempFile   OpenExistingFor: FILE_READ];
	if  ([tempFile   GetErrorCode] != ERR_OK) 
		[self   StoreErrorCode: ERR_OPENFAILED
			AndText: "Could not open a file of necessary PS code"];
	else
	{
		[thisFile   AppendFrom: tempFile];
		if  ([thisFile   GetErrorCode] != ERR_OK) 
			[self   StoreErrorCode: ERR_APPENDFAILED
				AndText: "Could not append a file of necessary PS code"];
		[tempFile   Close];
	}
	[tempFile   free];
	return self;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		BuildPrologInto:
//	Parameters:	A text file object.
//	Returns: 	errors
//	Stores:		none
//	Description:
//		When we're ready to start building the output PS file, we need to stick all the
//		PostScript code and procedures into the file.   This routine checks each group
//		flag, and if it is set, dumps the appropriate PS out.
//		Note: we find the path that leads us to the files of PS code that we might
//		want to append.  For each of these, if the relevant flag indicates we
//		should, we add the file name to the path, call the routine to append
//		that file, and then strip the name off the path so we can reuse it.
//		Note that we set no errors, but rely on AppendFile: to do so.
//	Bugs:
//		We should report when the PS code directory isn't even there.  however,
//		this requires more work than I'm willing to do at the moment.  (stat() is not
//		as reliable as I'd feel comfortable, since the man page says it barfs if there is
//		an 8 bit character in the path (gads)).
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

- BuildPrologInto: thisFile
{
	CString		fileName = NewCString(strlen(CodeDir)+20);
	Integer		pathlength;

	[self   ResetResults];
	
	[thisFile   WriteDSCComment: "Begin Prolog"];
	strcpy(fileName, CodeDir);
	pathlength = strlen (fileName);
	//
	//	Append all the common code
	//
	strcat(fileName, "Common");	
	[self   AppendFile: fileName To: thisFile];
	fileName[pathlength] = EndOfCString;
	//
	//	Miscellaneous
	//
	if (UsedMiscellaneous == YES)
	{
		strcat(fileName, "Miscellaneous");	
		[self   AppendFile: fileName To: thisFile];
		fileName[pathlength] = EndOfCString;
	}
	//
	//	Patterns
	//
	if (UsedPatterns == YES)
	{
		strcat(fileName, "Patterns");
		[self   AppendFile: fileName To: thisFile];
		fileName[pathlength] = EndOfCString;
	}
	//
	//	Colors
	//
	if (UsedColors == YES)
	{
		strcat(fileName, "Colors");
		[self   AppendFile: fileName To: thisFile];
		fileName[pathlength] = EndOfCString;
	}
	//
	//	lines
	//
	if (UsedLines == YES)
	{
		strcat(fileName, "Lines");	
		[self   AppendFile: fileName To: thisFile];
		fileName[pathlength] = EndOfCString;
	}
	//
	//	text
	//
	if (UsedText == YES)
	{
		strcat(fileName, "Text");	
		[self   AppendFile: fileName To: thisFile];
		fileName[pathlength] = EndOfCString;
	}
	//
	//	Rects
	//
	if (UsedRectangles == YES)
	{
		strcat(fileName, "Rectangles");
		[self   AppendFile: fileName To: thisFile];
		fileName[pathlength] = EndOfCString;
	}
	//
	//	Roundrects
	//
	if (UsedRoundRectangles == YES)
	{
		strcat(fileName, "RoundRectangles");
		[self   AppendFile: fileName To: thisFile];
		fileName[pathlength] = EndOfCString;
	}
	//
	//	ovals
	//
	if (UsedOvals == YES)
	{
		strcat(fileName, "Ovals");
		[self   AppendFile: fileName To: thisFile];
		fileName[pathlength] = EndOfCString;
	}
	//
	//	arcs
	//
	if (UsedArcs == YES)
	{
		strcat(fileName, "Arcs");
		[self   AppendFile: fileName To: thisFile];
		fileName[pathlength] = EndOfCString;
	}
	//
	//	polygons
	//
	if (UsedPolygons == YES)
	{
		strcat(fileName, "Polygons");
		[self   AppendFile: fileName To: thisFile];
		fileName[pathlength] = EndOfCString;
	}
	//
	//	regions
	//
	if (UsedRegions == YES)
	{
		strcat(fileName, "Regions");
		[self   AppendFile: fileName To: thisFile];
		fileName[pathlength] = EndOfCString;
	}
	//
	//	bitmaps
	//
	if (UsedBitmaps == YES)
	{
		strcat(fileName, "Bitmaps");
		[self   AppendFile: fileName To: thisFile];
		fileName[pathlength] = EndOfCString;
	}
	//
	//	Comments
	//
	if (UsedComments == YES)
	{
		strcat(fileName, "Comments");
		[self   AppendFile: fileName To: thisFile];
		fileName[pathlength] = EndOfCString;
	}
	FreeCString(fileName);
	[thisFile   WriteDSCComment: "End Prolog"];
	return self;
}



//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//					HIGH LEVEL OPCODE PROCESSING
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////



//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		ParseOpcode
//	Parameters:	A PICT opcode
//	Returns: 	YES if something went wrong or we reached the end of the pict file
//	Stores:		none
//	Description:
//		This method takes an opcode, and passes it on to a particular method that deal
//		with processing a particular group of opcodes.  This routine, and the ones it
//		calls, then, essentially make up a giant switch statement that has been
//		broken up for legibility's sake.
//		These methods are the heard of this object.  Given an opcode, it reads the appropriate
//		arguments, and writes appropriate postscript calls out to the output file.  Note that
//		this does not generate the actual postscript procedures that will be needed to image
//		this, but rather just the calls to some procedures.  Those PS procedures must
//		be created independantly of this object.
//		NOTE: The following code basically presumes you know something about PICT files.
//		It's organization is basically:  throw the specified opcode through a giant switch
//		statement. If, for some reason, it isn't one of the defined opcodes, a default entry
//		will try to consume its  data so no harm is done.
//		If you know nothing about PICT stuff, these are the sources that I've used to figure out 
//		what is in one (aside from mucking around with occasional PICT files by hand): Inside
//		Mac V p.84-105, The Pict.r file which comes with MPW (I think the partial path is
//		:MPW:Includes:RIncludes:Pict.r), Tech Note 21, and a bit of guessing.  =)
//	History:
//		93.07.18	djb	Changed Compiler told me that ((opCode >=0x00) && (opCode <=0x1F))
//					: comparison is always 1 due to limited range of data type.  I said 'huh?
//					opCode is 16bits!  There should be plenty of room'.  So, I poked and poked,
//					and eventualy it dawned on my thick head that since opCode is an
//					unsigned quantity, it would always be >=0.  Duh.  Kinda funny, for me,
//					though.  Needless to say, I pulled that bit out!
//					Also, moved comment processing to a separate routine call.
//	History:
//		93.08.21	djb	used 'endFound' flag to catch error flag from parsebitmap
//					definitely a hack, but for now we'll live.
//	Bugs:
//		Ignoring error flags
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

-(Boolean) ParseOpcode: (PICTOpcode) opCode
{
	Boolean	endFound = NO;
	//
	//	Now, try to parse the message.
	//
	if (opCode == 0xFF)
		endFound = YES;

	if ( (opCode <=0x1F)   || (opCode == 0xFF)  || (opCode == 0x0C00))
		[self   ParseMiscOpcode: opCode];
	else if ( (opCode >= 0x20) && (opCode <=0x27))
		[self   ParseLineOpcode: opCode];
	else if ( (opCode >= 0x28) && (opCode <=0x2F) ) 
		[self   ParseTextOpcode: opCode];
	else if ( (opCode >= 0x30) && (opCode <=0x3F) )
		[self   ParseRectangleOpcode: opCode];
	else if ( (opCode >= 0x40) && (opCode <=0x4F) )
		[self   ParseRoundRectangleOpcode: opCode];
	else if ( (opCode >= 0x50) && (opCode <=0x5F) )
		[self   ParseOvalOpcode: opCode];
	else if ( (opCode >= 0x60) && (opCode <=0x6F) )
		[self   ParseArcOpcode: opCode];
	else if ( (opCode >= 0x70) && (opCode <=0x7F) )
		[self   ParsePolygonOpcode: opCode];
	else if ( (opCode >= 0x80) && (opCode <=0x8F) )
		[self   ParseRegionOpcode: opCode];
	else if ( (opCode >= 0x90) && (opCode <=0x9F) )
		endFound = [self  ParseBitmapOpcode: opCode];
	else if ((opCode == 0xA0)  || (opCode == 0xA1))
		[self   ParseCommentOpcode: opCode];
	else if ((opCode >= 0x00A2) && (opCode != 0x0C00) && (opCode != 0x00FF))
		{
			//
			//	There are a bunch of reserved opcodes defined by apple.  On the off chance
			//	we get a picture that makes use of one of them, the following will just
			//	skip over its data and ignore it.
			//
			//
			// Opcodes 0x8000 to 0x80FF and 0x00B0 to 0x00CF have no data to
			// consume.
			//
			if (opCode >= 0x8100)
				[self Skip4PlusData];
			//
			// Opcodes 0x8000 to 0x80FF have no data to consume.
			//
			if ((opCode >= 0x7F00) && (opCode <= 0x7FFF))
				[sourceFile AdvanceBytes: 254];
			//
			// It is not clear what values follow these next opcodes.  I'm guessing 4
			//
			if ((opCode >= 0x0C01) && (opCode < 0x7F00))
				[sourceFile AdvanceBytes: 4];
			if ((opCode >= 0x0200) && (opCode < 0xBFF))
				[sourceFile AdvanceBytes: 4];
			if ((opCode >= 0x0100) && (opCode < 0x1FF))
				[sourceFile AdvanceBytes: 2];
			if ((opCode >= 0x00D0) && (opCode <= 0x00FE))
				[self Skip4PlusData];
			//
			// Opcodes 0x00B0 to 0x00CF have no data to consume.
			//
			if ((opCode >= 0x00A2) && (opCode <= 0x00AF))
				[self Skip2PlusData];
		}
		else
		{
			//
			//	Something is very weird if we get here, because all opcodes should have
			//	been consumed above.
			//
			endFound = YES;
		}
	return endFound;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//					OPCODE PROCESSING
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////



//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		ParseMiscOpcode:
//	Parameters:
//		A PICT opcode code
//	Returns: 	true if anything goes wrong
//	Stores:		none  explicitly (subcalls may)
//	Description:
//		This is one segment of the giant switch that parses pict opcodes.
//		This one deals with all the opcodes that do miscellaneous things
//		(that is, that aren't part of some well defined greater group like the text
//		opcodes, or the region ones, etc).  IT also includes opcodes (e.g. txFace)
//		which logically belong to others, but weren't worth making specialized tests for
//		above just so we could call this like that.
//	History:
//		93.07.18	djb	Moved PicComment processing into another routine.
//		93.08.15	djb	Added HeaderOp for IM v. 6 format.
//	Bugs:
//		The rectangle in a -2 headerop should be being used for the 
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (Boolean) ParseMiscOpcode: (PICTOpcode) opcode
{
	Boolean	error	= NO;
	CString	buffer;
	Integer	tempInt;
	PICTPoint* numerator;
	PICTPoint* denominator;
	Integer	version;
	Signed32Bits	temp2;
	Signed32Bits	tempsize;
	//
	//	Process the opcode
	//
	switch (opcode)
	{
		case 0x00 : // NOP opcode.  Do nothing
			break;
		case 0x01 : // Define clip region.
			// Uses common definitions.
			[self   ConvertRegion];
			[self   WritePSProcedureName: "QDclip"];
	 		break;
		case 0x02 : // bk Pat
			UsedPatterns = YES;
			[self   ConvertPict1Pattern];
			[self   WritePSProcedureName: "bkPat"];
	 		break;
		case 0x03 : // Set type family to use
			UsedText = YES;
			[self   ConvertFamily];
			[self   WritePSProcedureName: "txFont" ];
	 		break;
		case 0x04 : // Set type style to use
			UsedText = YES;
			[self  ConvertFace];
			[self  WritePSProcedureName: "txFace" ];
	 		break;
		case 0x05 : // Set type mode
			UsedText = YES;
			[self   ConvertMode];
			[self   WritePSProcedureName: "txMode" ];
	 		break;
		case 0x06 : // Set space extra
			//
			//	a fixed won't have more than 9 digits after the decimal place,
			//	(by my calculator ( 1/(2**16)))
			//
			UsedText = YES;
			buffer = NewCString(15);
			[destFile   WriteTextUsing: buffer
				WithFormat: "%.9f ", [sourceFile GetFixedNumber]];
			[self   WritePSProcedureName: "spExtra" ];
			FreeCString(buffer);
	 		break;
		case 0x07 : // Set pen size
			UsedMiscellaneous = YES;
			tempInt =  [sourceFile   GetINTEGER];  // can it be negative?
			[destFile   WriteInteger:  [sourceFile   GetINTEGER]]; // width
			[destFile   WriteInteger:  tempInt]; // height
 			[self   WritePSProcedureName: "pnSize" ];
	 		break;
		case 0x08 : // Set pen mode
			UsedMiscellaneous = YES;
			[self   ConvertMode];
			[self   WritePSProcedureName: "pnMode" ];
	 		break;
		case 0x09 : // Pen Pat
			UsedPatterns = YES;
			[self   ConvertPict1Pattern];
			[self   WritePSProcedureName: "pnPat"];
	 		break;
		case 0x0a : // fill Pat
			UsedPatterns = YES;
			[self   ConvertPict1Pattern];
			[self   WritePSProcedureName: "fillPat"];
	 		break;
		case 0x0B : // Oval size (x and y)
			UsedRoundRectangles = YES;
			[self   WritePoint: [sourceFile   GetPoint] ];
			[self   WritePSProcedureName: "ovSize" ];
	 		break;
		case 0x0C : // Origin (dh and dv)
			UsedMiscellaneous = YES;
			[destFile   WriteInteger: -1* [sourceFile GetINTEGER]]; // dh
			[destFile   WriteInteger:  -1*[sourceFile GetINTEGER]];  // dv
			[self   WritePSProcedureName: "origin" ];
	 		break;
		case 0x0D : // TextSize
			//
			//	I can't tell if parameter should be signed integers or not
			//
			UsedText = YES;
			[destFile   WriteInteger:  [sourceFile   GetINTEGER]];
			[self   WritePSProcedureName: "txSize" ];
	 		break;
		case 0x0E : // FgColor
			UsedColors = YES;
			[self   ConvertOldColor];
			[self   WritePSProcedureName: "fgColor"];
	 		break;
		case 0x0F : // BkColor
			UsedColors = YES;
			[self   ConvertOldColor];
			[self   WritePSProcedureName: "bkColor"];
	 		break;
		case 0x10: // Text ratio
			UsedText = YES;
			//
			//	presumably these are unsigned, but again, Apple calls them points.
			//
			numerator = [sourceFile   GetPoint];
			denominator = [sourceFile   GetPoint];
			[destFile   WriteReal:  numerator->x / denominator->x ]; // numerator
			[destFile   WriteReal:  numerator->y / denominator->y ]; // numerator
			FreeByteString((ByteString) numerator);
			FreeByteString((ByteString) denominator);
			[self WritePSProcedureName: "txRatio" ];
			break;			
		case 0x11 : // Version opcode
			//
			//	We should actually never end up here, I think, because the initial version
			//	opcode is read in when the pict file opens.
			//
			buffer = NewCString(31);
			[destFile WriteCommentUsing: buffer
				WithFormat: "PictVersion %d", [sourceFile   GetByte]];
			FreeCString(buffer);
			break;
		case 0x12 : // Background patten (color)
			UsedPatterns = YES;
			[self ConvertPict2Pattern];
			[self WritePSProcedureName: "bkPixPat"];
	 		break;
		case 0x13 : // Color pen pattern
			UsedPatterns = YES;
			[self ConvertPict2Pattern];
			[self WritePSProcedureName: "pnPixPat"];
	 		break;
		case 0x14 : // color fill pattern
			UsedPatterns = YES;
			[self ConvertPict2Pattern];
			[self WritePSProcedureName: "fillPixPat"];
	 		break;
		case 0x15 : // 'fractional pen position'
			UsedMiscellaneous = YES;
			[destFile   WriteInteger:  [sourceFile GetINTEGER]]; // the param... (pos?  neg??)
			[self   WritePSProcedureName: "pnLocHFrac"];
	 		break;
		case 0x16 : // extra for each non-space character
			UsedText = YES;
			//
			//	Dunno what this does. for sure.  IM suggested param is in a
			//	special format...
			//
			[destFile   WriteInteger:  [sourceFile GetINTEGER]]; // the param...
			[self WritePSProcedureName: "chExtra"];
	 		break;
		case 0x017:	// Several 'reserved for Apple use'
		case 0x018:
		case 0x019:
			break;
		case 0x1A : // RGB foreground color 
			UsedColors = YES;
			[self ConvertRGBColor];
			[self WritePSProcedureName: "RGBFgCol"];
	 		break;
		case 0x1B : // RGB background color
			UsedColors = YES;
			[self ConvertRGBColor];
			[self WritePSProcedureName: "RGBBkCol"];
	 		break;
		case 0x1C : // Use hilite mode flag
			UsedMiscellaneous = YES;
			[self WritePSProcedureName: "hiliteMode"];
	 		break;
		case 0x1D : // RGB highlighting color
			UsedColors = YES;
			[self ConvertRGBColor];
			[self WritePSProcedureName: "hiliteColor"];
	 		break;
		case 0x1E : // Use default hilite color
			UsedColors = YES;
			[self WritePSProcedureName: "defHilite"];
	 		break;
		case 0x1F : // RGB OpColor for 'arithmetic modes'
			UsedColors = YES;
			[self ConvertRGBColor];
			[self WritePSProcedureName: "hiliteColor"];
	 		break;
		case 0xFF: // End of picture
			//
			//	Inside Mac (V5 P102) says 2 bytes follow this. Pict.r for MPW doesn't agree.
			//	I'm ignoring IMV5.  Can't imagine what they are..
			//	(This routine is defined in the common code, so I don't set a Use* flag here)
			//
			error = YES;	// hacked way to get out...
			[self   WritePSProcedureName: "endPict"];
			break;
		case 0x0C00: // Header op
			//
			//	93.08.15	djb	Turns out that Inside Mac 6 redefined the HeaderOp's
			//				contents.  So, we try to distinguish between the two and
			//				write them out appropriately between []'s because of the
			//				variable number of parameters.
			//				Note that theoretically IVM volume V said all args were -1
			//
			// (uses common)
			//
			[destFile  WriteText: "[ "];
			version = [sourceFile GetINTEGER];
			if (version == -2)	// As defined by IM volume 6
			{
				[destFile   WriteInteger: version];
				[destFile   WriteInteger: [sourceFile GetINTEGER]]; // reserved
				buffer = NewCString(63);
				[destFile   WriteTextUsing: buffer
					WithFormat: "%.9f ", [sourceFile GetFixedNumber]];  // hres
				[destFile   WriteTextUsing: buffer
					WithFormat: "%.9f ", [sourceFile GetFixedNumber]];  // vres
				FreeCString(buffer);
				[destFile   WriteText: "[ "];
				[self   WriteRect: [sourceFile GetRect]]; // source rectangle
				[destFile   WriteText: "] "];
				[destFile   WriteInteger: [sourceFile GetLONGINT]]; // reserved
			}
			else
			{
				//
				//	We must reconstruct the first longint.  ick.  Is my logic good?
				//
				temp2 = [sourceFile GetINTEGER];
				tempsize = version << 16;
				tempsize |= temp2;
				[destFile   WriteInteger: tempsize];
				[destFile   WriteInteger: [sourceFile GetLONGINT]];
				[destFile   WriteInteger: [sourceFile GetLONGINT]];
				[destFile   WriteInteger: [sourceFile GetLONGINT]];
				[destFile   WriteInteger: [sourceFile GetLONGINT]];
				[destFile   WriteInteger: [sourceFile GetLONGINT]];
			}
			[destFile   WriteText: "] "];
			[self   WritePSProcedureName: "headerOp"];
			break;
		default:
			error = YES;
			break;
	}
	return error;
}



//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		ParseLineOpcode:
//	Parameters:
//		A PICT opcode code
//	Returns: 	true if anything goes wrong
//	Stores:		none  explicitly (subcalls may)
//	Description:
//		This is one segment of the giant switch that parses pict opcodes.
//		This one deals with all the opcodes that draw lines.
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (Boolean) ParseLineOpcode: (PICTOpcode) opcode
{
	Boolean	error	= NO;
	//
	//	Set the type flag
	//
	UsedLines = YES;
	
	//
	//	Process the opcode
	//
	switch (opcode)
	{
		case 0x020:
			[self   WritePoint: [sourceFile   GetPoint] ]; // From
			[self   WritePoint: [sourceFile   GetPoint] ]; // To 
			[self   WritePSProcedureName: "line"];
	 		break;
		case 0x021: // Starts from 'current' point?
			[self   WritePoint: [sourceFile   GetPoint] ]; // To
			[self   WritePSProcedureName: "lineFrom"];
	 		break;
		case 0x022:	//point to dh,dv
			[self   WritePoint: [sourceFile   GetPoint] ]; // From
			[destFile   WriteInteger:  [sourceFile  GetSignedByte] ]; // dh
			[destFile   WriteInteger:  [sourceFile  GetSignedByte] ]; // dv
			[self   WritePSProcedureName: "shortLine"];
	 		break;
		case 0x023:
			[destFile   WriteInteger:  [sourceFile  GetSignedByte] ]; // dh
			[destFile   WriteInteger:  [sourceFile  GetSignedByte] ]; // dv
			[self   WritePSProcedureName: "shortLineFrom"];
	 		break;
		case 0x024:	// Several 'reserved for Apple use'
		case 0x025:
		case 0x026:
		case 0x027:
			[self   Skip2PlusData];
			break;
		default:
			error = YES;
			break;
	}
	return error;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		ParseTextOpcode:
//	Parameters:
//		A PICT opcode code
//	Returns: 	true if anything goes wrong
//	Stores:		none  explicitly (subcalls may)
//	Description:
//		This is one segment of the giant switch that parses pict opcodes.
//		This one deals with all the opcodes that deal with text.
//	History:
//		93.08.16	djb	Added 0x2C code.
//		93.08.18	djb	Added 0x2D opcode
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (Boolean) ParseTextOpcode: (PICTOpcode) opcode
{
	Boolean	error		= NO;
	CString	fontName;
	CString	newName;
	CString	buffer;
	//
	//	Set the type flag
	//
	UsedText = YES;
	
	//
	//	Process the opcode
	//
	switch (opcode)
	{
		case 0x028:
			[self   WritePoint: [sourceFile   GetPoint] ]; // From
			[self   WriteString: [sourceFile GetPString] ];
			[self   WritePSProcedureName: "longText" ];
	 		break;
		case 0x029:
			[destFile   WriteInteger:  [sourceFile  GetByte] ]; // dh
			[self   WriteString: [sourceFile GetPString] ];
			[self   WritePSProcedureName: "DHText" ];
	 		break;
		case 0x02A:
			[destFile   WriteInteger:  [sourceFile  GetByte] ]; // dv
			[self   WriteString: [sourceFile GetPString] ];
			[self   WritePSProcedureName: "DVText"];
	 		break;
		case 0x02B:
			[destFile   WriteInteger:  [sourceFile  GetByte] ]; // dh
			[destFile   WriteInteger:  [sourceFile  GetByte] ]; // dv
			[self   WriteString: [sourceFile GetPString] ];
			[self   WritePSProcedureName: "DHDVText" ];
	 		break;
		case 0x02C:
			[sourceFile  GetINTEGER];
			[sourceFile  GetINTEGER];
			fontName = [sourceFile GetPString];
			//
			//	Convert type family names
			//
			newName = NullCString;
			if (strcmp(fontName, "Times") == 0)
				newName = "Times-Roman";
			else if (strcmp(fontName, "Avant Garde") == 0)
				newName = "AvantGarde-Book";
			else if (strcmp(fontName, "Bookman") == 0)
				newName = "Bookman-Light";
			else if (strcmp(fontName, "Helvetica Narrow") == 0)
				newName = "Helvetica-Narrow";
			else if (strcmp(fontName, "New Century Schlbk") == 0)
				newName = "NewCenturySchlbk-Roman";
			else if (strcmp(fontName, "Palatino") == 0)
				newName = "Palatino-Roman";
			else if (strcmp(fontName, "Zapf Chancery") == 0)
				newName = "ZapfChancery-MediumItalic";
			else if (strcmp(fontName, "New York") == 0)
				newName = "NewYork";
			else if (strcmp(fontName, "Los Angeles") == 0)
				newName = "LosAngeles";
			else if (strcmp(fontName, "San Francisco") == 0)
				newName = "SanFrancisco";
			if (newName != NullCString)
			{
				FreeCString(fontName);
				fontName = newName;
			}
			[destFile   WriteText:  "/"];
			[destFile   WriteText:  fontName];
			[self   WritePSProcedureName: " fontName" ];
			break;
		case 0x02D:
			[sourceFile  GetINTEGER];	// Skip the length.  We don't care.
			buffer = NewCString(63);
			[destFile   WriteTextUsing: buffer
				WithFormat: "%.9f ", [sourceFile GetFixedNumber]];  // intercharacter spacing
			[destFile   WriteTextUsing: buffer
				WithFormat: "%.9f ", [sourceFile GetFixedNumber]];  // total extra space for justification
			FreeCString(buffer);
			[self   WritePSProcedureName: "lineJustify" ];
 			break;
		case 0x02E:	// Several 'reserved for Apple use'
		case 0x02F:
			[self   Skip2PlusData];
			break;
		default:
			error = YES;
			break;
	}
	return error;
}



//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		ParseRectangleOpcode:
//	Parameters:
//		A PICT opcode code
//	Returns: 	true if anything goes wrong
//	Stores:		none  explicitly (subcalls may)
//	Description:
//		This is one segment of the giant switch that parses pict opcodes.
//		This one deals with all the opcodes that deal with Rectangles.
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (Boolean) ParseRectangleOpcode: (PICTOpcode) opcode
{
	Boolean	error	= NO;
	//
	//	Set the type flag
	//
	UsedRectangles = YES;
	
	//
	//	Process the opcode
	//
	switch (opcode)
	{
		case 0x30 : // frameRect . 
			[self   WriteRect: [sourceFile   GetRect] ];
			[self   WritePSProcedureName: "frameRect"];
	 		break;
		case 0x31 : // paintRect .
			[self   WriteRect: [sourceFile   GetRect] ];
			[self   WritePSProcedureName: "paintRect"];
	 		break;
		case 0x32 : // eraseRect . 
			[self   WriteRect: [sourceFile   GetRect] ];
			[self   WritePSProcedureName: "eraseRect"];
	 		break;
		case 0x33 : // invertRect .  
			[self   WriteRect: [sourceFile   GetRect] ];
			[self   WritePSProcedureName: "invertRect"];
	 		break;
		case 0x34 : // fillRect 
			[self   WriteRect: [sourceFile   GetRect] ];
			[self   WritePSProcedureName: "fillRect"];
	 		break;
		case 0x035:	// Several 'reserved for Apple use'  each require 8 bytes
		case 0x036:
		case 0x037:
			[sourceFile   AdvanceBytes: 8];
			break;
		case 0x38 : // frame same Rect 
			[self   WritePSProcedureName: "frameSameRect"];
	 		break;
		case 0x39 : // paint same Rect
			[self   WritePSProcedureName: "paintSameRect"];
	 		break;
		case 0x3A : // erase same Rect
			[self   WritePSProcedureName: "eraseSameRect"];
	 		break;
		case 0x3B : // invert same Rect
			[self   WritePSProcedureName: "invertSameRect"];
	 		break;
		case 0x3C : // fill same Rect
			[self   WritePSProcedureName: "fillSameRect"];
	 		break;
		case 0x03D:	// Several 'reserved for Apple use'.  Each takes no bytes of parameters.
		case 0x03E:
		case 0x03F:
			break;
		default:
			error = YES;
			break;
	}
	return error;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		ParseRoundRectangleOpcode:
//	Parameters:
//		A PICT opcode code
//	Returns: 	true if anything goes wrong
//	Stores:		none  explicitly (subcalls may)
//	Description:
//		This is one segment of the giant switch that parses pict opcodes.
//		This one deals with all the opcodes that deal with Rectangles.
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (Boolean) ParseRoundRectangleOpcode: (PICTOpcode) opcode
{
	Boolean	error	= NO;
	//
	//	Set the type flag
	//
	UsedRoundRectangles = YES;
	
	//
	//	Process the opcode
	//
	switch (opcode)
	{
		case 0x40 : // frameRRect
			[self   WriteRect: [sourceFile   GetRect] ];
			[self   WritePSProcedureName: "frameRRect"];
	 		break;
		case 0x41 : // paintRRec
			[self   WriteRect: [sourceFile   GetRect] ];
			[self   WritePSProcedureName: "paintRRect"];
	 		break;
		case 0x42 : // eraseRRect
			[self   WriteRect: [sourceFile   GetRect] ];
			[self   WritePSProcedureName: "eraseRRect"];
	 		break;
		case 0x43 : // invertRRect 
			[self   WriteRect: [sourceFile   GetRect] ];
			[self   WritePSProcedureName: "invertRRect"];
	 		break;
		case 0x44 : // fillRRect 
			[self   WriteRect: [sourceFile   GetRect] ];
			[self   WritePSProcedureName: "fillRRect"];
	 		break;
		case 0x045:	// Several 'reserved for Apple use'  each require 8 bytes
		case 0x046:
		case 0x047:
			[sourceFile   AdvanceBytes: 8];
			break;
		case 0x48 : // frame same RRect 
			[self   WritePSProcedureName: "frameSameRRect"];
	 		break;
		case 0x49 : // paint same RRect
			[self   WritePSProcedureName: "paintSameRRect"];
	 		break;
		case 0x4A : // erase same RRect
			[self   WritePSProcedureName: "eraseSameRRect"];
	 		break;
		case 0x4B : // invert same RRect
			[self   WritePSProcedureName: "invertSameRRect"];
	 		break;
		case 0x4C : // fill same RRect
			[self   WritePSProcedureName: "fillSameRRect"];
	 		break;
		case 0x04D:	// Several 'reserved for Apple use'.  Each takes no bytes of parameters.
		case 0x04E:
		case 0x04F:
			break;
		default:
			error = YES;
			break;
	}
	return error;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		ParseOvalOpcode:
//	Parameters:
//		A PICT opcode code
//	Returns: 	true if anything goes wrong
//	Stores:		none  explicitly (subcalls may)
//	Description:
//		This is one segment of the giant switch that parses pict opcodes.
//		This one deals with all the opcodes that deal with Ovals.
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (Boolean) ParseOvalOpcode: (PICTOpcode) opcode
{
	Boolean	error	= NO;
	//
	//	Set the type flag
	//
	UsedOvals = YES;
	
	//
	//	Process the opcode
	//
	switch (opcode)
	{
		case 0x50 : // frameOval 
			[self   WriteRect: [sourceFile   GetRect] ];
			[self   WritePSProcedureName: "frameOval"];
	 		break;
		case 0x51 : // paintOval
			[self   WriteRect: [sourceFile   GetRect] ];
			[self   WritePSProcedureName: "paintOval"];
	 		break;
		case 0x52 : // eraseOval 
			[self   WriteRect: [sourceFile   GetRect] ];
			[self   WritePSProcedureName: "eraseOval"];
	 		break;
		case 0x53 : // invertOval
			[self   WriteRect: [sourceFile   GetRect] ];
			[self   WritePSProcedureName: "invertOval"];
	 		break;
		case 0x54 : // fillOval
			[self   WriteRect: [sourceFile   GetRect] ];
			[self   WritePSProcedureName: "fillOval"];
	 		break;
		case 0x055:	// Several 'reserved for Apple use'  each require 8 bytes
		case 0x056:
		case 0x057:
			[sourceFile   AdvanceBytes: 8];
			break;
		case 0x58 : // frame same Oval 
			[self   WritePSProcedureName: "frameSameOval"];
	 		break;
		case 0x59 : // paint same Oval
			[self   WritePSProcedureName: "paintSameOval"];
	 		break;
		case 0x5A : // erase same Oval
			[self   WritePSProcedureName: "eraseSameOval"];
	 		break;
		case 0x5B : // invert same Oval
			[self   WritePSProcedureName: "invertSameOval"];
	 		break;
		case 0x5C : // fill same Oval
			[self   WritePSProcedureName: "fillSameOval"];
	 		break;
		case 0x05D:	// Several 'reserved for Apple use'.  Each takes no bytes of parameters.
		case 0x05E:
		case 0x05F:
			break;
		default:
			error = YES;
			break;
	}
	return error;
}



//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		ParseArcOpcode:
//	Parameters:
//		A PICT opcode code
//	Returns: 	true if anything goes wrong
//	Stores:		none  explicitly (subcalls may)
//	Description:
//		This is one segment of the giant switch that parses pict opcodes.
//		This one deals with all the opcodes that deal with Arcs.
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (Boolean) ParseArcOpcode: (PICTOpcode) opcode
{
	Boolean	error	= NO;
	//
	//	Set the type flag
	//
	UsedArcs = YES;
	
	//
	//	Process the opcode
	//
	switch (opcode)
	{
		case 0x60 : // frameArc 
			[self ConvertArc];
			[self   WritePSProcedureName: "frameArc" ];
	 		break;
		case 0x61 : // paintArc
			[self ConvertArc];
			[self   WritePSProcedureName: "paintArc" ];
	 		break;
		case 0x62 : // eraseArc 
			[self ConvertArc];
			[self   WritePSProcedureName: "eraseArc" ];
	 		break;
		case 0x63 : // invertArc .
			[self ConvertArc];
			[self   WritePSProcedureName: "invertArc" ];
	 		break;
		case 0x64 : // fillArc 
			[self ConvertArc];
			[self   WritePSProcedureName: "fillArc" ];
	 		break;
		case 0x065:	// Several 'reserved for Apple use'  each require 12 bytes
		case 0x066:
		case 0x067:
			[sourceFile AdvanceBytes: 12];
			break;
		//
		// The following 5 opcodes take angle measurements, but no rect		//
		case 0x68 : // frame same Arc 
			[self ConvertAngles];
			[self   WritePSProcedureName: "frameSameArc" ];
	 		break;
		case 0x69 : // paint same Arc
			[self ConvertAngles];
			[self   WritePSProcedureName: "paintSameArc" ];
	 		break;
		case 0x6A : // erase same Arc
			[self ConvertAngles];
			[self   WritePSProcedureName: "eraseSameArc" ];
	 		break;
		case 0x6B : // invert same Arc
			[self ConvertAngles];
			[self   WritePSProcedureName: "invertSameArc" ];
	 		break;
		case 0x6C : // fill same Arc
			[self ConvertAngles];
			[self   WritePSProcedureName: "fillSameArc" ];
	 		break;
		case 0x06D:	// Several 'reserved for Apple use'
		case 0x06E:
		case 0x06F:
			[sourceFile   AdvanceBytes: 4];
			break;
		default:
			error = YES;
			break;
	}
	return error;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		ParsePolygonOpcode:
//	Parameters:
//		A PICT opcode code
//	Returns: 	true if anything goes wrong
//	Stores:		none  explicitly (subcalls may)
//	Description:
//		This is one segment of the giant switch that parses pict opcodes.
//		This one deals with all the opcodes that deal with Polygon.
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (Boolean) ParsePolygonOpcode: (PICTOpcode) opcode
{
	Boolean	error	= NO;
	//
	//	Set the type flag
	//
	UsedPolygons = YES;
	UsedLines = YES;	// A cheat because we know that a routine in the Lines file is one we need
	
	//
	//	Process the opcode
	//
	switch (opcode)
	{
		case 0x70 : // framePoly .  
			[self ConvertPolygon];
			[self   WritePSProcedureName: "framePoly"];
	 		break;
		case 0x71 : // paintPoly .  .  
			[self ConvertPolygon];
			[self   WritePSProcedureName: "paintPoly"];
	 		break;
		case 0x72 : // erasePoly .  .  
			[self ConvertPolygon];
			[self   WritePSProcedureName: "erasePoly"];
	 		break;
		case 0x73 : // invertPoly .  .  
			[self ConvertPolygon];
			[self   WritePSProcedureName: "invertPoly"];
	 		break;
		case 0x74 : // fillPoly .  .  
			[self ConvertPolygon];
			[self   WritePSProcedureName: "fillPoly"];
	 		break;
		case 0x075:	// Several 'reserved for Apple use'  All are followed by a Polygon
		case 0x076:
		case 0x077:
			[self   SkipSizedObject];
			break;
		case 0x78 : // frame same Poly (not yet actually implemented by apple) 
			[self   WritePSProcedureName: "frameSamePoly" ];
	 		break;
		case 0x79 : // paint same Poly (not yet actually implemented by apple) 
			[self   WritePSProcedureName: "paintSamePoly" ];
	 		break;
		case 0x7A : // erase same Poly (not yet actually implemented by apple) 
			[self   WritePSProcedureName: "eraseSamePoly" ];
	 		break;
		case 0x7B : // invert same Poly (not yet actually implemented by apple) 
			[self   WritePSProcedureName: "invertSamePoly" ];
	 		break;
		case 0x7C : // fill same Poly (not yet actually implemented by apple) 
			[self   WritePSProcedureName: "fillSamePoly" ];
	 		break;
		case 0x07D:	// Several 'reserved for Apple use'  None have data bytes
		case 0x07E:
		case 0x07F:
			break;
		default:
			error = YES;
			break;
	}
	return error;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		ParseRegionOpcode:
//	Parameters:
//		A PICT opcode code
//	Returns: 	true if anything goes wrong
//	Stores:		none  explicitly (subcalls may)
//	Description:
//		This is one segment of the giant switch that parses pict opcodes.
//		This one deals with all the opcodes that deal with Regions.
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (Boolean) ParseRegionOpcode: (PICTOpcode) opcode
{
	Boolean	error	= NO;
	//
	//	Set the type flag
	//
	UsedRegions = YES;
	
	//
	//	Process the opcode
	//
	switch (opcode)
	{
		case 0x80 : // frameRgn
			[self ConvertRegion];
			[self   WritePSProcedureName: "frameRgn"];
	 		break;
		case 0x81 : // paintRgn
			[self ConvertRegion];
			[self   WritePSProcedureName: "paintRgn"];
	 		break;
		case 0x82 : // eraseRgn
			[self ConvertRegion];
			[self   WritePSProcedureName: "eraseRgn"];
	 		break;
		case 0x83 : // invertRgn
			[self ConvertRegion];
			[self   WritePSProcedureName: "invertRgn"];
	 		break;
		case 0x84 : // fillRgn
			[self ConvertRegion];
			[self   WritePSProcedureName: "fillRgn"];
	 		break;
		case 0x085:	// Several 'reserved for Apple use'  All are followed by a region 
		case 0x086:
		case 0x087:
			[self SkipSizedObject];
			break;
		case 0x88 : // frame same rgn (not yet actually implemented by apple) 
			[self   WritePSProcedureName: "frameSameRgn" ];
	 		break;
		case 0x89 : // paint same rgn (not yet actually implemented by apple) 
			[self   WritePSProcedureName: "paintSameRgn" ];
	 		break;
		case 0x8A : // erase same rgn (not yet actually implemented by apple) 
			[self   WritePSProcedureName: "eraseSameRgn" ];
	 		break;
		case 0x8B : // invert same rgn (not yet actually implemented by apple) 
			[self   WritePSProcedureName: "invertSameRgn" ];
	 		break;
		case 0x8C : // fill same rgn (not yet actually implemented by apple) 
			[self   WritePSProcedureName: "fillSameRgn" ];
	 		break;
		case 0x08D:	// Several 'reserved for Apple use'  None have data bytes
		case 0x08E:
		case 0x08F:
			break;
		default:
			error = YES;
			break;
	}
	return error;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		ParseBitmapOpcode:
//	Parameters:
//		A PICT opcode code
//	Returns: 	true if anything goes wrong
//	Stores:		none  explicitly (subcalls may)
//	Description:
//		This is one segment of the giant switch that parses pict opcodes.
//		This one deals with all the opcodes that deal with Bitmaps.
//	History:
//		93.08.21	djb	Added branches for the direct bitmaps 9a and 9b
//					Added a crude error code returning capability.
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (Boolean) ParseBitmapOpcode: (PICTOpcode) opcode
{
	Boolean	error	= NO;
	//
	//	Set the type flag
	//
	UsedBitmaps = YES;
	
	//
	//	Process the opcode
	//
	switch (opcode)
	{
		case 0x90 :
			[self   ConvertBitmapWithRegion: NO PackedData: NO];
	 		break;
		case 0x91 :
			[self   ConvertBitmapWithRegion: YES  PackedData: NO];
	 		break;
		case 0x092:	// Several 'reserved for Apple use'
		case 0x093:
		case 0x094:
		case 0x095:
		case 0x096:
		case 0x097:
			[self   Skip2PlusData];
			break;
		case 0x98 :
			[self   ConvertBitmapWithRegion: NO PackedData: YES];
	 		break;
		case 0x99 :
			[self   ConvertBitmapWithRegion: YES  PackedData: YES];
	 		break;
		case 0x09A:
			[self   ConvertDirectBitmapWithRegion: NO];
			break;
		case 0x09B:
			[self   ConvertDirectBitmapWithRegion: YES];
			break;
		case 0x09C:	// Several 'reserved for Apple use'
		case 0x09D:
		case 0x09E:
		case 0x09F:
			[self   Skip2PlusData];
			break;
		default:
			error = YES;
			break;
	}
	
	if ([self   GetErrorCode] < ERR_OK)
	{
		error = YES;
	}
	return error;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		ParseCommentOpcode:
//	Parameters:
//		A PICT opcode code
//	Returns: 	true if anything goes wrong
//	Stores:		none  explicitly (subcalls may)
//	Description:
//		This routine process the two picture comment opcodes.  One opcode's data is merely
//		a number indicating it's 'kind'.  The other opcode's data is a kind and a length of
//		data.  It happens that there are lots of opcodes out there, and many of them don't
//		seem to be documented, meaning they're basically proprietary.  There used to be
//		a publicly defined set that MacDraw used, but that info is now Claris', and isn't
//		as redily available.  So, what this routine does is it will do what little processing it
//		can for the opcodes defined in the TechNotes or Inside Mac #1, and for the others
//		write them out in a generic form.  That is, Inside Mac or TN ones will be written out
//		in the form:
//			[args] /name longComment
//		While unknown ones will be written out as
//			<hex-data> kind longComment
//		(short comments the same, without args or hex-data).
//		Note these points: the kind and args have reversed position from earlier versions
//		of the converter.  Our arguments are different types depending on what we know
//		about them. Also, the appspecific one will go out as a hex string and with a name.
//		Naturally, if the user has asked to remove all pic comments, nothing is written out.
//		²	These comments are obsolete.
//		#	These comments are not recommended.
//	History:
//		93.07.18	Started doing some Picture comment stuff (added kinds 0, 1)
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (Boolean) ParseCommentOpcode: (PICTOpcode) opcode
{
	Boolean	error	= NO;
	CString	buffer	= NewCString(1024);
	PositiveInteger	kind;
	PositiveInteger	length;
	CString			tempString;
	TPolyVerbRec	polyBitfield;
	Byte			tempByte;
	SignedByte		tempSignedByte;
	SignedByte		numIntervals;
	PositiveInteger	index;

	kind = [sourceFile GetPositiveINTEGER];

	if (PicCommentOperation == DiscardPicComments)
	{
		if (opcode == 0xA1)
			[self   Skip2PlusData];
	}
	else
	{
		UsedComments = YES;

		if (opcode == 0xA0)
			[self   ParseShortComment: kind];
		else
		{
			length = [sourceFile GetPositiveINTEGER];
			switch (kind)
			{
				case 100:
					[self   ConvertHexBytes: length];
					//
					//	Extract copies of MYAP, and 2 byte number?
					//
					[destFile   WriteText: "/AppComment "];
					break;
				case 150:	//Begin text function
					if (length != 10)
					{
						//
						//	Doc gives conflicting sizes.  if it's not the one we want do generic.
						//
						[destFile  WriteCommentUsing: buffer
							WithFormat: "%s (%d)", 
							"WARNING: Unexpected datalength for TextBegin.  doing nothing!",
							length];
						[self   ConvertHexBytes: length];
						[destFile   WritePositiveInteger: kind];
					}
					else
					{
						UsedText = YES;
						[destFile   WriteText: "[ "];
						//
						//	Read tJus
						//
						tempByte = [sourceFile   GetByte];
						switch (tempByte)
						{
							case tJusNone:
								[destFile   WriteText: "/tJusNone "];
								break;
							case tJusLeft:
								[destFile   WriteText: "/tJusLeft "];
								break;
							case tJusCenter:
								[destFile   WriteText: "/tJusCenter "];
								break;
							case tJusRight:
								[destFile   WriteText: "/tJusRight "];
								break;
							case tJusFull:
								[destFile   WriteText: "/tJusFull "];
								break;
						}
						//
						//	Read tFlip
						//
						tempByte = [sourceFile   GetByte];
						switch (tempByte)
						{
							case tFlipNone:
								[destFile   WriteText: "/tFlipNone "];
								break;
							case tFlipHorizontal:
								[destFile   WriteText: "/tFlipHorizontal "];
								break;
							case tFlipVertical:
								[destFile   WriteText: "/tFlipVertical "];
								break;
						}
						//
						//	tAngle  (clockwise rotation in degrees 0..360)
						//
						[destFile  WriteInteger: [sourceFile GetINTEGER]];
						//
						//	Skip these unused and reserved bytes (tLine and tCmnt)
						//
						[sourceFile   GetByte];
						[sourceFile   GetByte];
						//
						//	tAngleFixed (same as "tAngle" in Fixed precision)
						//
						[destFile   WriteTextUsing: buffer
							WithFormat: "%.9f ", [sourceFile GetFixedNumber]];
						[destFile   WriteText: "]"];
						[destFile   WriteText: " /TextBegin "];
					}
					break;
				case 154:	//Offset to center of rotation
					[destFile   WriteText: "[ "];
					[destFile   WriteTextUsing: buffer
						WithFormat: "%.9f ", [sourceFile GetFixedNumber]];
					[destFile   WriteTextUsing: buffer
						WithFormat: "%.9f ", [sourceFile GetFixedNumber]];
					[destFile   WriteText: "]"];
					[destFile   WriteText: " /TextCenter "];
					break;
				case 157:	//Customize line layout error distribution #
					[destFile   WriteText: "[ "];
					//
					//	Parameters are, respecitvely, 
					//		chCount	Apply for so many characters.
					//		major	percentage of line layout error to be distributed
					//				among space characters.
					//		spcChar	code of character that is to absorb
					//				the "major" line layout error
					//		minor	percentage of intercharacter distrib.
					//		ulLength	underline length.
					//
					[destFile  WriteInteger: [sourceFile GetINTEGER]]; // Apply this # chars
					[destFile   WriteTextUsing: buffer
						WithFormat: "%.9f ", [sourceFile GetFixedNumber]];
					[destFile  WriteInteger: [sourceFile GetINTEGER]];
					[destFile   WriteTextUsing: buffer
						WithFormat: "%.9f ", [sourceFile GetFixedNumber]];
					[destFile   WriteTextUsing: buffer
						WithFormat: "%.9f ", [sourceFile GetFixedNumber]];
					[destFile   WriteText: "]"];
					[destFile   WriteText: " /ClientLineLayout "];
					break;
				case 164:	//Close, Fill, Frame
					//
					//	Hey.  Since we only care about 3 of the fields, we could probably
					//	just read this in as a byte, and look for the values from 0 to 8 (or the
					//	reverse if the byte is reversed).  Just a thought.
					//
					polyBitfield = [sourceFile GetPolyVerbs];
					[destFile   WriteText: "[ "];
					if (polyBitfield.fPolyClose  == YES)
						[destFile   WriteText: "/Close "];
					if (polyBitfield.fPolyFill  == YES)
						[destFile   WriteText: "/Fill "];
					if (polyBitfield.fPolyFrame  == YES)
						[destFile   WriteText: "/Frame "];
					[destFile   WriteText: "]"];
					[destFile   WriteText: " /PolySmooth "];
					break;
				case 180:	//Draw following lines as dashed
					tempSignedByte = (SignedByte) [sourceFile   GetByte];
					[destFile   WriteText: "[ "];
					[destFile  WriteInteger: tempSignedByte];
					//
					//	Skip the centered argument
					//
					[sourceFile   GetByte];
					//
					//	Write out the dash info, unless our length is way wrong
					//
					numIntervals =  (SignedByte) [sourceFile   GetByte];
					if ((numIntervals + 3) != length)
					{
						[destFile  WriteCommentUsing: buffer
							WithFormat: "%s (%d expected %d)", 
							"WARNING: Unexpected datalength for DashedLine.  doing nothing!",
							length,
							numIntervals+3];
						[destFile  WriteInteger: numIntervals];
						[self   ConvertHexBytes: length-3];
						[destFile   WriteText: "]"];
						[destFile   WritePositiveInteger: kind];
					}
					else
					{
						//
						//	All is well, so write out the offset and dash info.
						//		(put this info in additional braces for easier processing.
						//
						[destFile   WriteText: " [ "];
						for (index = 1; index <= numIntervals; index++)
						{
							tempSignedByte = (SignedByte) [sourceFile   GetByte];
							[destFile  WriteInteger: tempSignedByte];
						}
						[destFile   WriteText: "]"];

						[destFile   WriteText: " ]"];
						[destFile   WriteText: " /DashedLine "];
					}
					break;
				case 182:	// Set fractional line widths
					//
					//	the arguments are v/h which become a fraction to scale the pen in both
					//	dimensions by.
					//	Output:   v h div
					//
					[destFile  WriteInteger: [sourceFile GetINTEGER]];
					[destFile  WriteInteger: [sourceFile GetINTEGER]];
					[destFile   WriteText: " div "];
					[destFile   WriteText: "/SetLineWidth "];
					break;
				case 192:	// PostScript data in handle
					tempString = NewCString(length);
					[sourceFile  Read: length BytesInto: tempString];
					tempString[length] = 0;
					[self   WriteString: tempString];
					[destFile   WriteText: " /PostScriptHandle "];
					FreeCString(tempString);
					break;
				case 193:	// FileName in data handle ²
					tempString = NewCString(length);
					[sourceFile  Read: length BytesInto: tempString];
					tempString[length] = 0;
					[self   WriteString: tempString];
					[destFile   WriteText: " /PostScriptFile "];
					FreeCString(tempString);
					break;
				case 195:	// PostScript data in a resource ²
					//
					//	Should be doing some length error checking?
					//
					[destFile  WriteComment: "WARNING: The ResourcePS values are quite possibly wrong."];
					[destFile   WriteText: "[ "];
					//
					//	Read in the type
					//
					tempString = NewCString(4);
					[sourceFile  Read: 4 BytesInto: tempString];
					tempString[4] = 0;	//93.11.06	Fixed.  Was setting tempString[length] to 0, but length is 8 in this case.  oops.
					[self   WriteString: tempString];
					//
					//	Convert the ID and Index
					//
					[destFile  WriteInteger: [sourceFile GetINTEGER]];
					[destFile  WriteInteger: [sourceFile GetINTEGER]];
					[destFile   WriteText: "]"];
					[destFile   WriteText: " /ResourcePS "];
					FreeCString(tempString);
					break;
				case 197:	// Call PostScript's setgray operator #
					[destFile   WriteTextUsing: buffer
						WithFormat: "%.9f ", [sourceFile GetFixedNumber]];
					[destFile   WriteText: " /SetGrayLevel "];
					break;
				case 200:	//Begin rotated port
					if (length != 8)
					{
						[destFile  WriteCommentUsing: buffer
							WithFormat: "%s (%d)", 
							"WARNING: Unexpected datalength for RotateBegin.  doing nothing!",
							length];
						[self   ConvertHexBytes: length];
						[destFile   WritePositiveInteger: kind];
					}
					else
					{
						[destFile   WriteText: "[ "];
						[destFile  WriteInteger: [sourceFile GetINTEGER]];  //flip
						[destFile  WriteInteger: [sourceFile GetINTEGER]];  //angle
						[destFile   WriteTextUsing: buffer
							WithFormat: "%.9f ", [sourceFile GetFixedNumber]];  //fixed angle
						[destFile   WriteText: "]"];
						[destFile   WriteText: " /RotateBegin "];
					}
					break;
				case 202:	//Offset to center of rotation
					[destFile   WriteText: "[ "];
					[destFile   WriteTextUsing: buffer
						WithFormat: "%.9f ", [sourceFile GetFixedNumber]];
					[destFile   WriteTextUsing: buffer
						WithFormat: "%.9f ", [sourceFile GetFixedNumber]];
					[destFile   WriteText: "]"];
					[destFile   WriteText: " /RotateCenter "];
					break;
				default:
					//	read in a length, then that # bytes and hex out.
					[self   ConvertHexBytes: length];
					[destFile   WritePositiveInteger: kind];
					break;
			}
			[self   WritePSProcedureName: "longComment"];
		}
	}
	FreeCString(buffer);
	return error;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		ParseShortComment:
//	Parameters:
//		A kind of short comment
//	Returns: 	self
//	Stores:		none  explicitly (subcalls may)
//	Description:
//		This routine process short pict comments.  At the moment, this merely means
//		writing out a name or a number for the specified kind of short comment.  Names
//		are written out when we know what about this kind of comment, numbers are
//		written out otherwise.
//		²	These comments are obsolete.
//		#	These comments are not recommended.
//	History:
//		93.08.01	Split off from the PicComment conversion code.
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ParseShortComment: (Integer) kind
{
	switch (kind)
	{
		//
		//	Defined in Inside Mac V 1
		//
		case 0:
			[destFile   WriteText: "/picLParen "];
			break;
		case 1:
			[destFile   WriteText: "/picRParen "];
			break;
		//
		//	Mentioned in Tech note as being ImageWriter specific
		//
		case 1000:
			[destFile   WriteText: "/BitMapThinningOff "];
			break;
		case 1001:
			[destFile   WriteText: "/BitMapThinningOn "];
			break;
		//
		//	Mainly LaserWriter/PS specific.  Defined in Tech Note
		//
		case 151:		// End text function
			[destFile   WriteText: "/TextEnd "];
			break;
		case 152:		// Begin string delimitation
			[destFile   WriteText: "/StringBegin "];
			break;
		case 153:		// End string delimitation
			[destFile   WriteText: "/StringEnd "];
			break;
		case 155:		// Turn LaserWriter line layout off
			[destFile   WriteText: "/LineLayoutOff "];
			break;
		case 156:		// Turn LaserWriter line layout on
			[destFile   WriteText: "/LineLayoutOn "];
			break;
		case 160:		// Begin special polygon
			[destFile   WriteText: "/PolyBegin "];
			break;
		case 161:		// End special polygon
			[destFile   WriteText: "/PolyEnd "];
			break;
		case 163:		// Ignore following polygon data
			[destFile   WriteText: "/PolyIgnore "];
			break;
		case 165:		// Close the polygon
			[destFile   WriteText: "/PolyClose "];
			break;
		case 181:		// End dashed lines
			[destFile   WriteText: "/DashedStop "];
			break;
		case 190:		//Set driver state to PostScript
			[destFile   WriteText: "/PostScriptBegin "];
			break;
		case 191:		//Restore QuickDraw state
			[destFile   WriteText: "/PostScriptEnd "];
			break;
		case 194:		//QuickDraw text is sent as PostScript ²
			UsedText = YES;
			[destFile   WriteText: "/TextIsPostScript "];
			break;
		case 196:		//Set driver state to PostScript
			[destFile   WriteText: "/PSBeginNoSave "];
			break;
		case 201:		//End rotation
			[destFile   WriteText: "/RotateEnd "];
			break;
		case 210:		//Don't clear print buffer after each page #
			[destFile   WriteText: "/FormsPrinting "];
			break;
		case 211:		// End forms printing after PrClosePage #
			[destFile   WriteText: "/EndFormsPrinting "];
			break;
		default:
			[destFile   WritePositiveInteger: kind];
			break;
	}
	[self   WritePSProcedureName: "shortComment"];
	return self;
}



//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//					PROCESSING UTILITIES
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		Skip2PlusData:
//	Parameters:	none
//	Returns: 	self
//	Stores:		none  explicitly (subcalls may)
//	Description:
//		This reads a 16 bit number in, and then moves forward as many bytes as
//		that specifies in the source file.  This is usefull because if we run into an unknown
//		opcode, it may be of the form 2 bytes of size, and that many bytes of data, and this
//		allows us to skip over it gracefully. 
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- Skip2PlusData
{
	PositiveInteger	numBytes;

	numBytes = [sourceFile   GetPositiveINTEGER];
	[sourceFile   AdvanceBytes: numBytes];
	return self;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		Skip4PlusData:
//	Parameters:	none
//	Returns: 	self
//	Stores:		none  explicitly (subcalls may)
//	Description:
//		This reads a 32 bit number in, and then moves forward as many bytes as
//		that specifies.  Useful for the same reason as Skip2PlusData:;
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- Skip4PlusData
{
	PositiveInteger	numBytes;

	numBytes = [sourceFile   GetPositiveLONGINT];
	[sourceFile   AdvanceBytes: numBytes];
	return self;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		SkipSizedObject:
//	Parameters:	none
//	Returns: 	self
//	Stores:		none  explicitly (subcalls may)
//	Description:
//		Read in a size value (2 bytes), and use it's value to skip over all that many remaining
//		bytes.  This differs from Skip2PlusData in that the byte count is part of the number
//		of bytes we are skipping. 
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- SkipSizedObject
{
	PositiveInteger	size;
	
	size = [sourceFile   GetPositiveINTEGER];
	[sourceFile   AdvanceBytes: size-2];
	return self;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		ConvertHexBytes:
//	Parameters:	The number of bytes to convert.
//	Returns: 	self
//	Stores:		none  explicitly (subcalls may)
//	Description:
//		read the specified number of bytes from the source file, and write them out as hex
//		digits in the eps file.  use a slow brute force algorithm.  =)
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ConvertHexBytes: (PositiveInteger) theBytes
{
	PositiveInteger	byteCounter;
	CString			buffer	= NewCString(2);
	
	[destFile   WriteText: "<"];
	
	for (byteCounter = 0; byteCounter < theBytes; byteCounter++)
		[destFile   WriteTextUsing: buffer WithFormat: "%.2X",  (Byte)[sourceFile GetByte]];

	[destFile   WriteText: "> "];
	FreeCString(buffer);
	return self;
}



//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		WritePoint:
//	Parameters:
//		A Point structure
//	Returns: 	self
//	Stores:		none  explicitly (subcalls may)
//	Description:
//		This simply writes out the pict's x and y coordinates, and then it assumes it
//		may free the point structure, since that's how it's always used. =)
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- WritePoint: (PICTPoint*) thePoint
{
	[destFile   WriteInteger: thePoint->x];
	[destFile   WriteInteger: thePoint->y];
	
	FreeByteString( (ByteString) thePoint);
	return self;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		WriteRect:
//	Parameters:
//		A Rect structure
//	Returns: 	self
//	Stores:		none  explicitly (subcalls may)
//	Description:
//		This simply writes out the rectangle, and then it assumes it
//		may free the rectangle structure, since that's how it's always used.  =)
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- WriteRect: (PICTRect*) theRect
{
	[destFile   WriteInteger: theRect->top];
	[destFile   WriteInteger: theRect->left];
	[destFile   WriteInteger:  theRect->bottom ];
	[destFile   WriteInteger:  theRect->right];

	FreeByteString((ByteString)theRect);
	return self;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		WritePSProcedureName:
//	Parameters:
//		A CString
//	Returns: 	self
//	Stores:		none  explicitly (subcalls may)
//	Description:
//		This merely writes a procedure name that we are passed, and follows it
//		with a newline.  Any time we are writing one of our procedure names, it'll
//		be the last thing on a line, so, we hard-code this behavior.
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- WritePSProcedureName: (CString) theName
{
	[destFile   WriteText: theName];
	[destFile   WriteText: "\n"];
	return self;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//				PRIMARY DATA CONVERSION ROUTINES
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		ConvertAngles:
//	Parameters:	none
//	Returns: 	self
//	Stores:		none  explicitly (subcalls may)
//	Description:
//		This reads the information about the angles of a pict arc in from the current position
//		in the source file, and then converts the information to a form that can be used
//		easily in the resulting postscript file
//		The angles for a PICT arc is stored as an initial angle, and the number of degrees from
//		the start angle to the end of the arc.  We write it out as a start angle and a finish
//		angle.
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ConvertAngles
{
	Integer	startAng,
			deltaAng,
			finishAng;
	//
	//	Read in angles, and compensate for the different position of 0deg in PICT and PS.
	//	then, compute the finish position, given the start and the delta.
	//
	startAng =  [sourceFile   GetINTEGER];
	deltaAng = [sourceFile GetINTEGER];
	startAng -= 90;	// compensate for the fact that PICT 90 is PS 0  (I think))
	
	if (deltaAng > 0)
		finishAng = startAng + deltaAng;
	else
	{
		finishAng = startAng;
		startAng = finishAng + deltaAng;
	}
	//
	//	To make things prettier, check if the numbers are negative, and if so, add 360.
	//	no promises that they will not still be negative afterwards.  but most of the time they
	//	will, and this will make the code easier to understand
	//	@@ Is there a chance that these will write out misleading numbers?  That is, it used
	//	to say go from x to x+y.  now we say, go from x to z, but can we end up somehow with
	//	z being behind x when it shouldn't?
	//
	if (startAng < 0)
		startAng += 360;
	if (finishAng < 0)
		finishAng += 360;
	
	[destFile   WriteInteger: startAng];
	[destFile   WriteInteger: finishAng];
	
	return self;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		ConvertArc:
//	Parameters:	none
//	Returns: 	self
//	Stores:		none  explicitly (subcalls may)
//	Description:
//		This reads the information about an arc in from the current position in the
//		source file, and then converts the information to a form that can be used
//		easily in the resulting postscript file
//		A PICT arc is stored as a bounding rectangle, which defines overall size and
//		overall circle-ness/ovalness of the circle/oval we are drawing the arc in.  After
//		this rectangle, the start and finish angles of the arc are stored.
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ConvertArc
{
	[self   WriteRect: [sourceFile   GetRect] ];
	[self   ConvertAngles];
	return self;
}



//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		ConvertPolygon:
//	Parameters:
//	Returns: 	self
//	Stores:		none  explicitly (subcalls may)
//	Description:
//		A Mac polygon consists of the following: 2 bytes of size, 8 bytes of bounding rect,
//		and a sequence of points that describe the vertices (including start and end points) of
//		a polygon.  Note that a polygon need not be a closed object.   This takes said object, and
//		writes out the points of the polygon as a series of numbers, these are followed by the
//		number of poits that we have written and finally the bounding rectangle is written
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

- ConvertPolygon
{
	PICTRect*	bounds;
	Integer		numPoints,
				pointsOnLine = 0;
	Integer		RemainingPoints;
	PICTPoint*	thePoint;
	PositiveInteger	size;
	//
	//	Get the size, and the bounding rectangle, anc compute the number of vertices
	//	using the total size of the object
	//
	size = [sourceFile   GetPositiveINTEGER];
	bounds = [sourceFile   GetRect];
	numPoints = 0;
	RemainingPoints = (size-10) / 4;
	//
	//	For each vertex, write out a little PS array of the form [x y].  Don't put more than
	//	8 on a line so things will remain readable for any browsing humans.
	//
	thePoint = [sourceFile   GetPoint];
	RemainingPoints--;
	while ( RemainingPoints >= 0 )
	{
		numPoints++;
		[destFile   WriteText: "["];
		[self   WritePoint: thePoint];
		[destFile   WriteText: "]"];
		if (pointsOnLine == 8)
		{
			pointsOnLine = 0;
			[destFile   ForceNewLine];	
		}
		else
			pointsOnLine++;
			
		if (RemainingPoints > 0)
			thePoint = [sourceFile   GetPoint];
		RemainingPoints--;	// if it was 0, this will drop it below, which will fall out of the main loop
	}
	//
	//	Well.  That was easy.  Now, just write out the number of points, so the ps routine
	//	will be able to consume all the data properly, and write out the bounding rectangle
	//	
	[destFile   WriteInteger: numPoints];
	[self   WriteRect: bounds];

	return self;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		ConvertRegion:
//	Parameters:	none
//	Returns: 	self
//	Stores:		none  explicitly (subcalls may)
//	Description:
//		A Mac region is arranged like so: 2 bytes of size of the whole region, 8 bytes of
//		bounding rectangle, then N groups of data about segments of lins.  Each group
//		of segments has a y coordinate, and then a start and stop x coordinate for
//		each segment.  Anyway, this creates a converter instance, and asks it to
//		convert the region waiting in the source file, to the destination file. 
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ConvertRegion
{
	Instance regConverter;
	regConverter= [[regionConverter  alloc] init];
	[regConverter   ConvertRegionFrom: sourceFile To: destFile];
	[regConverter  free];
	return self;
}



//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		ConvertMode
//	Parameters:	none
//	Returns: 	self
//	Stores:		none  explicitly (subcalls may)
//	Description:
//		This takes a Mac mode type value, and writes out a PS name corresponding to it
//	History:
//		93.07.18	djb	added magicMode which is a mysterious pen mode defined in a tech note.
//					It's effect is, it seems, to be to cause QuickDraw to process stuff, but not 
//					actually draw anything.
//	Bugs:
//		This gets called by a routine which wants only src, and one that wants
//		only pat.  Because we do no checking of this, there's no way to keep us from
//		writing out an illegal mode for that routin.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ConvertMode
{
	PositiveInteger	modeNum;
	CString			buffer;
	
	modeNum = [sourceFile   GetPositiveINTEGER];
	switch (modeNum)
	{
		case macSrcCopy:
			[destFile   WriteText: "/srcCopy "];
			break;
		case macSrcOr:
			[destFile   WriteText: "/srcOr "];
			break;
		case macSrcXor:
			[destFile   WriteText: "/srcXor "];
			break;
		case macSrcBic:
			[destFile   WriteText: "/srcBic "];
			break;
		case macNotSrcCopy:
			[destFile   WriteText: "/notSrcCopy "];
			break;
		case macNotSrcXor:
			[destFile   WriteText: "/notSrcXor "];
			break;
		case macNotSrcOr:
			[destFile   WriteText: "/notSrcOr "];
			break;
		case macNotSrcBic:
			[destFile   WriteText: "/notSrcBic "];
			break;
		case macPatCopy:
			[destFile   WriteText: "/patCopy "];
			break;
		case macPatOr:
			[destFile   WriteText: "/patOr "];
			break;
		case macPatXor:
			[destFile   WriteText: "/patXor "];
			break;
		case macPatBic:
			[destFile   WriteText: "/patBic "];
			break;
		case macNotPatCopy:
			[destFile   WriteText: "/notPatCopy "];
			break;
		case macNotPatOr:
			[destFile   WriteText: "/notPatOr "];
			break;
		case macNotPatXor:
			[destFile   WriteText: "/notPatXor "];
			break;
		case macNotPatBic:
			[destFile   WriteText: "/notPatBic "];
			break;
		case blend:
			[destFile   WriteText: "/blend "];
			break;
		case addPin:
			[destFile   WriteText: "/addPin "];
			break;
		case addOver:
			[destFile   WriteText: "/addOver "];
			break;
		case subPin:
			[destFile   WriteText: "/subPin "];
			break;
		case addMax:
			[destFile   WriteText: "/addMax "];
			break;
		case subOver:
			[destFile   WriteText: "/subOver "];
			break;
		case adMin:
			[destFile   WriteText: "/adMin "];
			break;
		case transparent:
			[destFile   WriteText: "/transparent "];
			break;
		case magicMode:
			[destFile   WriteText: "/magicMode "];
			break;
		default:
			buffer = NewCString(64);
			[destFile   WriteTextUsing: buffer
				WithFormat: "/badMode%d ", modeNum];
			FreeCString(buffer);
			break;
	}
	return self;
}




//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		ConvertFamily:
//	Parameters:	none
//	Returns: 	self
//	Stores:		none  explicitly (subcalls may)
//	Description:
//		This reads a number in from the pict file.  from this number, we either write out the
//		name of a standard mac type face, or we write out a number.  We also set the
//		ConvertCurrentCharacters flag, which is used when writing out strings
//		from the text opcodes later.  If this is set to NO, then we will not attempt to
//		convert the text.  Otherwise we will.
//	Bugs:
//		I dunno whether this is really a positive or a signed integer.  No tests were done,
//		and no doc seems to indicate for sure.  
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

- ConvertFamily
{
	PositiveInteger	familyNum;
	
	familyNum = [sourceFile GetPositiveINTEGER];
	switch (familyNum)
	{
		case 0:
			[destFile   WriteText: "/Chicago "];
			ConvertCurrentCharacters = YES;
			break;
		case 2:
			[destFile   WriteText: "/NewYork "];
			ConvertCurrentCharacters = YES;
			break;
		case 3:
			[destFile   WriteText: "/Geneva "];
			ConvertCurrentCharacters = YES;
			break;
		case 4:
			[destFile   WriteText: "/Monaco "];
			ConvertCurrentCharacters = YES;
			break;
		case 5:
			[destFile   WriteText: "/Venice "];
			ConvertCurrentCharacters = YES;
			break;
		case 6:
			[destFile   WriteText: "/London "];
			ConvertCurrentCharacters = YES;
			break;
		case 7:
			[destFile   WriteText: "/Athens "];
			ConvertCurrentCharacters = YES;
			break;
		case 8:
			ConvertCurrentCharacters = YES;
			[destFile   WriteText: "/SanFrancisco "];
			break;
		case 9:
			[destFile   WriteText: "/Toronto "];
			ConvertCurrentCharacters = YES;
			break;
		case 10:
			[destFile   WriteText: "/Seattle "];
			ConvertCurrentCharacters = YES;
			break;
		case 11:
			[destFile   WriteText: "/Cairo "];
			ConvertCurrentCharacters = NO;
			break;
		case 12:
			[destFile   WriteText: "/LosAngeles "];
			ConvertCurrentCharacters = YES;
			break;
		case 13:
			[destFile   WriteText: "/ZapfDingbats "];
			ConvertCurrentCharacters = NO;
			break;
		case 14:
			[destFile   WriteText: "/Bookman-Light "];
			ConvertCurrentCharacters = YES;
			break;
		case 15:
			[destFile   WriteText: "/Helvetica-Narrow "];
			ConvertCurrentCharacters = YES;
			break;
		case 16:
			[destFile   WriteText: "/Palatino-Roman "];
			ConvertCurrentCharacters = YES;
			break;
		case 18:
			[destFile   WriteText: "/ZapfChancery-MediumItalic "];
			ConvertCurrentCharacters = YES;
			break;
		case 20:
			[destFile   WriteText: "/Times-Roman "];
			ConvertCurrentCharacters = YES;
			break;
		case 21:
			[destFile   WriteText: "/Helvetica "];
			ConvertCurrentCharacters = YES;
			break;
		case 22:
			[destFile   WriteText: "/Courier "];
			ConvertCurrentCharacters = YES;
			break;
		case 23:
			[destFile   WriteText: "/Symbol "];
			ConvertCurrentCharacters = NO;
			break;
		case 24:
			[destFile   WriteText: "/Mobile "]; // Also known as Taliesin
			ConvertCurrentCharacters = NO;
			break;
		case 33:
			[destFile   WriteText: "/AvantGarde-Book "];
			ConvertCurrentCharacters = YES;
			break;
		case 34:
			[destFile   WriteText: "/NewCenturySchlbk-Roman "];
			ConvertCurrentCharacters = YES;
			break;
		default:
			[destFile   WriteInteger: familyNum];
			ConvertCurrentCharacters = UsersCharConvertChoice;
			break;
	}
	return self;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		ConvertFace:
//	Parameters:	none
//	Returns: 	self
//	Stores:		none  explicitly (subcalls may)
//	Description:
//		This converts the byte of data describing typeface attributes and writes it
//		out as a PS array describing the various attributes.
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ConvertFace
{
	bits8	theFace;
	
	theFace = [sourceFile  GetByte];
	[destFile   WriteText:"\[ "];
	if (boldBit & theFace)
		[destFile   WriteText:"/bold "];
	if (italicBit & theFace)
		[destFile   WriteText:"/italic "];
	if (underlineBit & theFace)
		[destFile   WriteText:"/underline "];
	if (outlineBit & theFace)
		[destFile   WriteText:"/outline "];
	if (shadowBit & theFace)
		[destFile   WriteText:"/shadow "];
	if (condenseBit & theFace)
		[destFile   WriteText:"/condense "];
	if (extendBit & theFace)
		[destFile   WriteText:"/extend "];
	[destFile   WriteText:"] "];
	return self;
}



//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		WriteString:
//	Parameters:	A CString structure
//	Returns: 	self
//	Stores:		none  explicitly (subcalls may)
//	Description:
//		This simply writes out specified string as a PS string, in parens, follows it with
//		a space, and then free's the string.
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- WriteString: (CString) theString
{
	CString	convertedString;
	CString	filteredString;
	CString	tempString;
	PositiveInteger	filterIndex;
	PositiveInteger	index;
	PositiveInteger	newLength = strlen(theString);
	
	if (ConvertCurrentCharacters == YES)
	{
		//
		//	Convert the Mac characters to the NeXT equivalents
		//	
		convertedString = [textConverter  ConvertString: theString
				WithLength: strlen(theString)];
		newLength	= [textConverter   GetPositiveIntegerFrom: SECOND_RESULT];
		FreeCString(theString);
		theString = convertedString;
	}
	//
	//	Now, filter this string, replacing certain troublesome characters with
	//	escaped equivalents.
	//
	filteredString = NewCString(newLength*4); // worst case each all will become \ddd
	filterIndex = 0;
	for (index = 0; index < newLength; index++)
	{
		if ( (PositiveInteger) theString[index] < (PositiveInteger) ' ')
		{
			tempString = NewCString(4);
			//
			//	93.01.31	djb	OOPS!  Had been using %.3u, not octal!
			//
			sprintf(tempString, "\\%.3o", (unsigned int) theString[index]);
			strcat(filteredString, tempString);
			FreeCString(tempString);
			filterIndex += 4;
		}
		else
		{
			switch (theString[index])
			{
				case '\\' :
					strcat(filteredString, "\\\\");
					filterIndex += 2;
					break;
				case '(' :
					strcat(filteredString, "\\(");
					filterIndex += 2;
					break;
				case ')' :
					strcat(filteredString, "\\)");
					filterIndex += 2;
					break;
				default :
					filteredString[filterIndex] = theString[index];
					filterIndex ++;
					filteredString[filterIndex] = EndOfCString; // put a null back on.
					break;
			}
		}
	
	}
	//
	//	write out the new string
	//
	[destFile   WriteText: "("];
	[destFile   WriteText: filteredString];
	[destFile   WriteText: ") "];
	
	FreeCString(filteredString);
	FreeCString(theString);
	return self;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		ConvertOldColor
//	Parameters:	none.
//	Returns: 	self
//	Stores:		none  explicitly (subcalls may)
//	Description:
//		This reads in a four byte old style Mac color, and writes it out as an array of
//		RGB values.  If the color isn't one of the pre-defined ones (in theory, this shouldn't
//		happen, I think), we instead write out an integer with the number of the color so
//		the PS code can perhaps deal with it.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ConvertOldColor
{
	PositiveInteger	colorNum;
	
	colorNum = [sourceFile GetPositiveLONGINT];
	switch (colorNum)
	{
		case oldBlackColor:
			[destFile   WriteText: "[0 0 0] "];
			break;
		case oldWhiteColor:
			[destFile   WriteText: "[1 1 1] "];
			break;
		case oldRedColor:
			[destFile   WriteText: "[1 0 0] "];
			break;
		case oldGreenColor:
			[destFile   WriteText: "[0 1 0] "];
			break;
		case oldBlueColor:
			[destFile   WriteText: "[0 0 1] "];
			break;
		case oldCyanColor:
			[destFile   WriteText: "[0 1 1] "];
			break;
		case oldMagentaColor:
			[destFile   WriteText: "[1 0 1] "];
			break;
		case oldYellowColor:
			[destFile   WriteText: "[1 1 0] "];
			break;
		default:
			[destFile   WritePositiveInteger: colorNum];
			break;
	}
	return self;
}



//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		ConvertRGBColor
//	Parameters:	none.
//	Returns: 	self
//	Stores:		none  explicitly (subcalls may)
//	Description:
//		read a mac rgb value in, write out as an array of 3 numbers from 0 to 1 for PS
//	Bugs:
//		I really can't tell whether to treat these colors as 8 or 16 bit values.  I'm assuming
//		8 for the moment.  change maxMACCOLOR to 65535.0 if
//		it should be 16.  (read in with GetPositiveINTEGER)
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#define maxMACCOLOR 255.0
- ConvertRGBColor
{
	CString	tempString = NewCString(12);
	[destFile   WriteText: "["];
	//
	//	Read the red component
	//
	[destFile   WriteTextUsing: tempString
		WithFormat: "%.9f ", ( [sourceFile   GetByte] / maxMACCOLOR)];
	[sourceFile  AdvanceBytes: 1];
	//
	//	Read the green component
	//
	[destFile   WriteTextUsing: tempString
		WithFormat: "%.9f ", ( [sourceFile   GetByte] / maxMACCOLOR)];
	[sourceFile  AdvanceBytes: 1];
	//
	//	Read the blue component
	//
	[destFile   WriteTextUsing: tempString
		WithFormat: "%.9f ", ( [sourceFile   GetByte] / maxMACCOLOR)];
	[sourceFile  AdvanceBytes: 1];
	[destFile   WriteText: "] "];

	FreeCString(tempString);
	return self;
}




//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//					BITMAP AND PATTERN PROCESSING
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////



//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		ConvertInvertedHexBytes:
//	Parameters:	The number of bytes to convert.
//	Returns: 	self
//	Stores:		none  explicitly (subcalls may)
//	Description:
//		read the specified number of bytes from the source file, and write them out as hex
//		digits in the eps file, inverting as we go.  use a slow brute force algorithm.  =)
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ConvertInvertedHexBytes: (PositiveInteger) theBytes
{
	PositiveInteger	byteCounter;
	CString			buffer	= NewCString(2);
	
	[destFile   WriteText: "<"];
	
	for (byteCounter = 0; byteCounter < theBytes; byteCounter++)
		[destFile   WriteTextUsing: buffer WithFormat: "%.2X",  (Byte) ~[sourceFile GetByte]];

	[destFile   WriteText: "> "];
	FreeCString(buffer);
	return self;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		ConvertPict1Pattern
//	Parameters:	none.
//	Returns: 	self
//	Stores:		none  explicitly (subcalls may)
//	Description:
//		This merely reads an 'old-style' 8 byte B&W pattern in from the pict file, and writes
//		it out as a binary string to the PS file.   We also write a type (0) of pattern
//		out so the parser can distinguish this from the other two types.
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ConvertPict1Pattern
{
	[self ConvertInvertedHexBytes: 8];
	[destFile   WritePositiveInteger: 0];
	return self;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		GetPixMapFrom:
//	Parameters:	the file to read a Mac pixmap record structure from
//				A pixmap structure to read into.
//	Returns: 	self
//	Stores:		none
//	Description:
//		This reads in a PixMap structure that was written out on a Macintosh.
//		Because it is from a Mac, it is made up of lots of big endian integers.  Because
//		we may be running on an Intel or other platform, we need to convert these
//		integers so they are of the right form for the native endianness.  We do this in
//		a brute force method: read in the mac form, create a second structure, and
//		effectively copy over all the fields, flipping the bytes if necessary as we go.
//	History:
//		93.08.01	djb	Created
//		93.08.15	djb	Argh!  I need to be casting the results!!!
//		93.08.16	djb	Argh! again! Wasn't casting properly in all cases.
//	Bugs:
//		I consider it icy design that we are being passed the structuer to fill in, and aren't
//		just returning on our own (the latter allows more flexibility).  However, this
//		routine was added long after it's callers were, and they were already depending on
//		their pixmaps structures being allocated on the stack.  I didn't want to risk changing
//		all the foo.bar's to foo->bar's.  So, this gets passed a map structure instead.  =(
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- GetPixMapFrom: SourceFile  Into: (modPixMap*) nativeMap
{
	[sourceFile   Read:  sizeof(modPixMap)  BytesInto: (ByteString) nativeMap];
	//
	//	Now, swap all the fields of the pixmap structure from the mac format to whatever
	//	the native one is. .
	//
	nativeMap->bounds.top = (INTEGER)NXSwapBigShortToHost(nativeMap->bounds.top);
	nativeMap->bounds.right = (INTEGER)NXSwapBigShortToHost(nativeMap->bounds.right);
	nativeMap->bounds.bottom = (INTEGER)NXSwapBigShortToHost(nativeMap->bounds.bottom);
	nativeMap->bounds.left = (INTEGER)NXSwapBigShortToHost(nativeMap->bounds.left);
	nativeMap->pmVersion = (INTEGER)NXSwapBigShortToHost(nativeMap->pmVersion);
	nativeMap->packType = (INTEGER)NXSwapBigShortToHost(nativeMap->packType);
	nativeMap->packSize = (LONGINT)NXSwapBigLongToHost(nativeMap->packSize);
	nativeMap->hRes = (FIXED)NXSwapBigLongToHost(nativeMap->hRes);
	nativeMap->vRes = (FIXED)NXSwapBigLongToHost(nativeMap->vRes);
	nativeMap->pixelType = (INTEGER)NXSwapBigShortToHost(nativeMap->pixelType);
	nativeMap->pixelSize = (INTEGER)NXSwapBigShortToHost(nativeMap->pixelSize);
	nativeMap->cmpCount = (INTEGER)NXSwapBigShortToHost(nativeMap->cmpCount);
	nativeMap->cmpSize = (INTEGER)NXSwapBigShortToHost(nativeMap->cmpSize);
	nativeMap->planeBytes = (LONGINT)NXSwapBigLongToHost(nativeMap->planeBytes);
	nativeMap->pmTable = (LONGINT)NXSwapBigLongToHost(nativeMap->pmTable);
	nativeMap->pmReserved = (LONGINT)NXSwapBigLongToHost(nativeMap->pmReserved);
	//
	//	dispose of the source one, and return the destination.
	//
//	FreeByteString((ByteString)macMap);
	return self;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		ConvertPict2Pattern
//	Parameters:	none.
//	Returns: 	self
//	Stores:		none  explicitly (subcalls may)
//	Description:
//		This reads in a PICT 2 pattern, massages the data, and writes it out for the
//		PS file.  Inside Mac V. 5 only documents two types (1&2) on p. 103.
//		Type 2: 8byte B&W pattern, and RGB color.  IM V suggests that the pattern is
//			there only for things to use if they are on a B&W machine.  Because PS
//			can deal with a color, and I can see no reason why it should ever need the
//			pattern in this case, I am discarding this data!  This may be a mistake, though.
//		Type 1: 'arbitrary' bitmap used as pattern...
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ConvertPict2Pattern
{
	PositiveInteger	patType;
	PICTRect*		bounds	= (PICTRect*)NewByteString(sizeof(PICTRect));
	modPixMap		theMap;
	PositiveInteger	dataHigh, dataWide;
	PositiveInteger	rowBytes;
	Integer			bitsDeep;
	Real				hScale, vScale;
	CString			tempString	= NewCString(30);
	Boolean			isPacked;
	Boolean			tempPacking;
	

	patType = [sourceFile   GetPositiveINTEGER];
	[self ConvertInvertedHexBytes: 8];
	switch (patType)
	{
		case 1 :
			//
			//	Clear the high bit of rowBytes, read in the pixmap data, and convert
			//	the color table that follows it. (same basic flow of events as in the bitmap
			//	converter below.
			//
			rowBytes =0x7FFF &  [sourceFile   GetPositiveINTEGER];
			[self   GetPixMapFrom: sourceFile  Into: &theMap];
			[self  ConvertColorTable];
			//
			//	move the rectangle bounding data into the bounds rect..
			//
			bounds->left = theMap.bounds.left;
			bounds->top = theMap.bounds.top;
			bounds->bottom = theMap.bounds.bottom;
			bounds->right = theMap.bounds.right;
			bitsDeep = theMap.pixelSize;
			[destFile   WriteInteger: bitsDeep];
			dataHigh = bounds->bottom - bounds->top;
			dataWide = bounds->right - bounds->left;
			if (rowBytes < 8)
				isPacked = NO;
			else
				isPacked = YES;
			//
			//	Start writing out data to the PS file.
			//
			[destFile   WriteInteger: bounds->left];
			[destFile   WriteInteger: bounds->top]; // because bottom in pict is top in ps..
			[destFile   WriteInteger: dataWide];
			[destFile   WriteInteger: dataHigh];
			[destFile  ForceNewLine];	// make sure the following data starts on a new line.
			//
			//	compute the relative scaling ratios of the bitmap (72dpi = 1)
			//	Convert first from fixedpoint data.
			//
			hScale = (theMap.hRes / 65535.0) / 72;
			vScale =  (theMap.vRes / 65535.0) / 72;
			[destFile   WriteInteger: (dataWide * hScale)];
			[destFile   WriteInteger: (dataHigh * vScale)];
			[destFile  ForceNewLine];	// make sure the following data starts on a new line.
			//
			//	Write whether the data is packed or not.  (for now, only pack because I
			//	could not figure out how to get filters to work inside a pattern proc.
			//
			[destFile WriteTextLine: "false"];
			//
			//	Now, write out the bitmap data itself converted.
			//
			[destFile WriteText: "<"];
			//
			//	A hack to wait until we can figure out how to unpack images in PS in
			//	indexed color spaces set packing so we never write out packed images
			//
			tempPacking = PackingSetting;	
			PackingSetting = NO;

			[self  ExtractImageDataUsingDataWidth: rowBytes
				Scanlines: dataHigh
				ScanlineLength:  rowBytes
				ScanlineStart: 0
				AndPixelDepth:  bitsDeep
				IsCompressed: isPacked
				UsesColor: YES];
			//
			//	Write a trailing > which an ASCIIHexEncoding  filter understands as EOF.
			//	If needed, also write an 80 which is eof for the RLE filter. 
			//
			if (PackingSetting == YES)
				[destFile WriteTextLine: "80>"];
			else
				[destFile WriteTextLine: ">"];
			[destFile   WritePositiveInteger: 1];
			PackingSetting = tempPacking;
			break;
		case 2 :
			//
			//	The 'pattern' is really halftone of the specified color.
			//	That is, you specify color XYZ, but device can't do this, so
			//	QuickDraw will build a raster using colors it can draw, that
			//	will approximate the color your want (specifed by the rgb value)
			//
			[self ConvertRGBColor];
			[destFile   WritePositiveInteger: 2];
			break;
		default :
			// Should set some kind big error here!
			break;
	}
	FreeCString(tempString);
	return self;
}





//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		ConvertBitmapWithRegion:PackedData:
//	Parameters:	Boolean flag
//	Returns: 	self
//	Stores:		none  explicitly
//	Description:
//		This reads a bitmap that may or may not have a clipping region, and may or
//		may not be packed.  It determines whether it is a B&W or color image, and then
//		extracts the appropriate data structures and writes them out.  
//	Bugs:
//		We don't examine the data nearly closely enough to assure that it is good.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

-ConvertBitmapWithRegion: (Boolean) hasRegion PackedData: (Boolean) isPacked
{
	PICTRect*		bounds;
	PICTRect*		sourceRect;
	PICTRect*		destRect;
	modPixMap		theMap;
	PositiveInteger	dataHigh, dataWide;
	PositiveInteger	rowBytes;
	Integer			bytesOut;
	Integer			diff;
	Integer			bitmapOffset;
	Integer			rasterType;
	Integer			bitsDeep;
	Boolean			colorUse;

	rowBytes = [sourceFile   GetPositiveINTEGER];  // Retrieves a Mac INTEGER (16 bits)
	//
	//	Inside Mac  V says that if the high bit of rowBytes is set, then the picture is
	//	essentially one with multiple bits per pixel.  SO.  Check the high bit (mask the
	//	rowBytes and check if zero or not), and also check for Pict version number.
	//	if the version is 1, then ignore that the high bit is set.
	//	In either case, process all the leading information up to the bitmap data.
	//
	if   (((rowBytes & 0x8000) == 0) || ([sourceFile   GetVersion] == 1))
	{
		//
		//	Write the two 'dummy' pieces of data so that the output from the two pict
		//	types of bitmaps have the same parameters, allowing one more flexibility in
		//	writing PS code.
		//
		[destFile   WritePositiveInteger:  1];	// num colors  -1
		[destFile   WriteTextLine:  "< 000000 FFFFFF >"];
		bounds = [sourceFile   GetRect];
		rasterType = 1;
		bitsDeep = 1;
	}
	else
	{
		//
		//	Clear the high bit of rowBytes, read in the pixmap data, and convert
		//	the color table that follows it.
		//
		rowBytes = 0x7FFF & rowBytes;
		[self   GetPixMapFrom: sourceFile  Into: &theMap];
		[self  ConvertColorTable];
		//
		//	move the rectangle bounding data into the bounds rect..
		//
		bounds	= (PICTRect*)NewByteString(sizeof(PICTRect));
		bounds->left = theMap.bounds.left;
		bounds->top = theMap.bounds.top;
		bounds->bottom = theMap.bounds.bottom;
		bounds->right = theMap.bounds.right;
		rasterType = 2;
		bitsDeep = theMap.pixelSize;
	}
	[destFile   WriteInteger: bitsDeep];

	//
	//	Read the source and dest rectangles in from the bitmap
	//
	sourceRect = [sourceFile   GetRect];
	destRect = [sourceFile   GetRect];
	//
	//	Start writing out data to the PS file.
	//		Convert and write out the transver mode
	//		Write the left and bottom coordinates of the destination rectangle
	//			(note that bottom in PS coords is top in PICT)
	//		Write the height and width of the destination rectangle
	//		Write the height and width of the source rectangle
	//		IF there is a region, convert it. In either case, write a flag indicating whether
	//			there is a region written out.
	//
	[self ConvertMode];
	[destFile   WriteInteger: destRect->left];
	[destFile   WriteInteger: destRect->top];	// because bottom right in pict is top in ps..
	[destFile   WriteInteger: destRect->right - destRect->left];
	[destFile   WriteInteger: destRect->bottom - destRect->top];
	[destFile   WriteInteger: sourceRect->right - sourceRect->left];
	[destFile   WriteInteger: sourceRect->bottom - sourceRect->top];
	[destFile  ForceNewLine];	// make sure the following data starts on a new line.
	//
	//	Write whether the data is packed or not.
	//
	if (PackingSetting == YES)
		[destFile WriteTextLine: "true"];
	else
		[destFile WriteTextLine: "false"];
	if (hasRegion == YES)
	{
		[self ConvertRegion];
		[destFile WriteTextLine: "true"];
	}
	else
		[destFile WriteTextLine: "false"];
	//
	//	Compute data needed for consumption of the bitmap.  Specifically:
	//	Determine how many pixels high and wide the image rectangle is.
	//	This figure very likely differs from the rowBytes width, so figure out exactly
	//	how many BYTES are in the width of the bitmap to go out.
	//	Then, figure out what the offset of the start of this bitmap in the raw data is,
	//	since strangely a bitmapped image is not always left justified in a pict bitmap...
	//
	dataHigh = bounds->bottom - bounds->top;
	dataWide = sourceRect->right - sourceRect->left;
	diff = ( (rowBytes*8) - (dataWide *bitsDeep) ) / 8;
	bytesOut = rowBytes -diff;	// remove any 'fluf' bytes.
	bitmapOffset = sourceRect->left - bounds->left; // there may be a strange offset
	//
	//	Now, write out the command to PS, and then have the bitmap data itself converted.
	//
	if (rasterType == 1)
	{
		colorUse = NO;
		[destFile WriteTextLine: "bitmap"];
	}
	else
	{
		colorUse = YES;
		[destFile WriteTextLine: "colorbitmap"];
	}

	[self  ExtractImageDataUsingDataWidth: rowBytes
		Scanlines: dataHigh
		ScanlineLength:  bytesOut
		ScanlineStart: bitmapOffset
		AndPixelDepth:  bitsDeep
		IsCompressed: isPacked
		UsesColor: colorUse];
	//
	//	Write a trailing > which an ASCIIHexEncoding  filter understands as EOF.
	//	If needed, also write an 80 which is eof for the RLE filter. 
	//
	if (PackingSetting == YES)
		[destFile WriteTextLine: "80>"];
	else
		[destFile WriteTextLine: ">"];
	FreeByteString((ByteString)bounds);
	FreeByteString((ByteString)sourceRect);
	FreeByteString((ByteString)destRect);
	return self;
}




//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:	ExtractPackedImageDataUsingDataWidth:Scanlines:ScanlineLength:
//			ScanlineStart:AndPixelDepth:IsCompressed:UsesColor:
//	Parameters:
//		A count of the number of bytes per row of raw bit data
//		A count of the number of scanlines in the file
//		The number of bytes per scanline
//		The number of bits offset into the raw scanline data that the bitmap starts
//		The number of bits that represent a pixel
//		A flag indicating whether the data is compressed
//		A flag indicating whether color was used.
//	Returns: 	self
//	Stores:		none
//	Description:
//		This consumes the packed raw data from the sourcefile, interpreting it as bitmap
//		data using the parameters given.  This raw data can be viewed as a rectangle of
//		DataWidth bytes wide and Scanlines rows tall.  The interesting part of the bitmap
//		is in the sub-rectangle that is scanlines rows tall and scanlinelength bytes wide.
//		This subrectangle may be identical to the first, or smaller (in which case, it may be
//		inset on the left by scanlinesstart bits.)  If the data is packed, then we unpack it
//		one scanline at a time, and write it out, inverting it if it is B&W, and packing it if
//		the master object has told us to.
//		The failing of this routine, perhaps, is that it attempts to do everything at once
//		(it used to be at least two that did a lot of similar, and a bit of different things), so
//		it gets a bit complex near the end...
//	Bugs:
//		It should be possible to bypass the unpacking and repacking for color images.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

- ExtractImageDataUsingDataWidth: (PositiveInteger) rawWidth
	Scanlines: (Integer) numLines
	ScanlineLength: (Integer) bytesPerScanline
	ScanlineStart: (Integer) bitOffset
	AndPixelDepth: (Integer) numBits
	IsCompressed: (Boolean)  sourcePacked
	UsesColor: (Boolean) ColorUsed
{
	PositiveInteger	index;
	Integer			byteCount;
	Integer			lastSize		= rawWidth;
	ByteString		preLine		= NewByteString(rawWidth);
	ByteString		destLine		= NewByteString (rawWidth);
	ByteString		sourceLine	= NewByteString (rawWidth);
	ByteString		packedLine	= NewByteString(rawWidth*1.5); // crude worst pack case
	Integer			numOut;
	ByteString		temp;
	Real				fileSize		= ( [sourceFile   FileSize] * 1.0);
	// divide  the above by 100 now so later division will yeild number between 0 and 100

	for (index = 0; index < numLines; index ++)
	{
		//
		//	Let the user know that we're actually doing something.
		//
		if ((index % 35) == 0)
		{
			if ( [myManager   respondsTo:@selector(SetPercentageDone:)] ) 
				[myManager   SetPercentageDone: ([sourceFile   GetCurrentPosition] * 100.0) / fileSize];
		}
		//
		//	Read in the source bitmap line, unpacking it if necessary
		//
		if (sourcePacked == NO)
			[sourceFile   Read: rawWidth BytesInto: sourceLine];
		else
		{
			//
			//	Get the length of the packed record.  (if rawWidth > 250, we use a 16 bit
			//	integer to determine the size.  This is documented on p. 105 of IM V.
			//	I do NOT know if this applies to PICT 1 data, but I'm assuming so.)
			//
			if (rawWidth > 250)
				byteCount = [sourceFile   GetINTEGER];
			else
				byteCount = [sourceFile  GetByte];
			//
			//	If the lastpreLine buffer won't hold the whole packed data, then free it and
			//	allocate a new one (this prevents needing to reallocate *every* time,
			//	without our needing to necessarily allocate a buffer of maximum size
			//	(whatever that would be)
			//
			if (byteCount > lastSize)
			{
				FreeByteString(preLine);
				preLine = NewByteString (byteCount);
				lastSize = byteCount;
			}
			//
			//	Read in the packed data and unpack it.
			//
			[sourceFile   Read: byteCount BytesInto: preLine];
			[self Unpack: rawWidth BytesFrom: preLine Into: sourceLine];
		}
		//////
		//	At this point, we should have our raw non-packed scanline data.
		//////
		//
		//	If the bitdata is offset, then shift it left appropiately, and reverse pointers from
		//	dest and source lines (this allows the following routine to use the same variables
		//	regardless of whether we did this or not.
		//
		if (bitOffset != 0)
		{
			[self   Copy: (bytesPerScanline*8) BitsAtOffset: bitOffset
				From: sourceLine Into: destLine];
			temp = destLine;
			destLine = sourceLine;
			sourceLine = temp;
		}
		//
		//	Invert the data if it is B&W, and then pack the data if the manager has asked us
		//	to (set temp to point to final buffer, and numOut to be it's length)
		//
		if ( (numBits == 1) && (ColorUsed == NO) )
			[self   Invert: bytesPerScanline In: sourceLine];
		if (PackingSetting == YES)
		{
			numOut = [self   Pack: bytesPerScanline BytesFrom: sourceLine
						Into: packedLine];
			temp = packedLine;
		}
		else
		{
			numOut = bytesPerScanline;
			temp = sourceLine;
		}
		//
		//	write out the scanline of bits as PS hex data.
		//
		[destFile   Write: numOut BytesOfHexDataFrom: temp];
	}
	FreeByteString (sourceLine);
	FreeByteString (destLine);
	FreeByteString (packedLine);
	FreeByteString (preLine);
	return self;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		ConvertColorTable
//	Parameters:	none
//	Returns: 	self
//	Stores:		none
//	Description:
//		This reads a PICT2 color table from the source file  (it assumes that the
//		source file is positioned at the start of one), and converts it into a PS Level 2
//		indexed colorspace definition.
//		Note: PICT Color tables use 16 bits per color component, in terms of storage.
//		Still, I've not seen any using more than the 8 bits (to get a total of 24bit color).
//		I'm sure it's possible there are 48 bit color values somewhere out there, but I
//		also can't find a definition of how to determine this.  It probably has to do with
//		some of the other values in the pixmap which IM V defines as being set to 0
//		Note: I did run across a pixel paint file which use only 8 bits, and then wrote a
//		number (e.g. AA01 A301 B201  for a color value of AA A3 B2, and incremented to
//		02 for next, etc), so it seems that one must assume 8 at least for now...
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ConvertColorTable
{
	PositiveInteger	numEntries;
	PositiveInteger	index;
	CString			buffer		= NewCString(40);
	Byte			Red,
					Green,
					Blue;
	//
	//	Skip the color table's seed and flag values
	//
	[sourceFile   AdvanceBytes: 6];
	//
	//	Get the number of color entries in the table (0 origin)
	//
	numEntries  = [sourceFile   GetPositiveINTEGER];
	//
	//	Start off the PS color table with the start of the indexed color space array
	//
	[destFile   WritePositiveInteger:  numEntries];
	[destFile   WriteText:  "<"];
	//
	//	Write out the rgb color data.  Put a limited number of color values on an output line
	//
	for (index = 0; index <= numEntries; index++)
	{
		[sourceFile   AdvanceBytes: 2];	// skip over the index  (assure they are sequential?)
		//
		//	Read the color components in.  Note that PICT uses 16 bits per color component,
		//	however, we can only use 8 here due to the indexed color space strategy I'm using
		//	in PS.  This actually seems to be not loose anything as far as I've seen so far.
		//
		Red = [sourceFile   GetByte];
		[sourceFile  AdvanceBytes: 1];
		Green = [sourceFile   GetByte];
		[sourceFile  AdvanceBytes: 1];
		Blue = [sourceFile   GetByte];
		[sourceFile  AdvanceBytes: 1];
		//
		//	If we've not written a nultiple of 9 colors, just write out the color in FF4502 form
		//	(FF red, 45 green, 02 blue).  If we have written 9, then put a newline afterwards
		//
		if (((index+1) % 9) != 0)
		[destFile   WriteTextUsing: buffer WithFormat: "%.2x%.2x%.2x ", Red, Green, Blue];
		else
		[destFile   WriteTextUsing: buffer WithFormat: "%.2x%.2x%.2x\n", Red, Green, Blue];
	}
	//
	//	Close the array and quit
	//
	[destFile   WriteTextLine: ">"];
	FreeCString(buffer);
	return self;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		ConvertDirectBitmapWithRegion:
//	Parameters:	Boolean flag
//	Returns: 	self
//	Stores:		none  explicitly
//	Description:
//		This routines reads in and processes one of the 'direct' bitmap structres in a
//		pict file (opcodes 9a and 9b).  These are bitmaps that have no clut's, but are direct
//		representations of color values in the pixels.
//	History:
//		93.08.21	djb	Created.
//	Bugs:
//		16bit images are completely ignored.  I'm not convinced I've got all
//		32bit cases dealt with.  Too little resources to test with.
//		Yep, properly rowbytes should be part of the pixmap.  However, it had to be
//		examind in advance in the other bitmap conversion routine, since the pixmap
//		isn't there for older bitmaps, and I didn't figure it was worth a second pixmap
//		reading routine given that.  (similar for the baseaddress field)
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

-ConvertDirectBitmapWithRegion: (Boolean) hasRegion
{
	PICTRect*		sourceRect	= NULL;
	PICTRect*		destRect		= NULL;
	modPixMap		theMap;
	PositiveInteger	dataHigh, dataWide;
	PositiveInteger	rowBytes;
	Integer			bytesOut;
	Integer			diff;
	Integer			bitmapOffset;
	Integer			bitsDeep;
	Integer			index;
	Integer			count;
	Integer			decider;
	//
	//	Skip the baseaddress field.  It should just contain 0x000000FF, anyway.
	//
	[sourceFile   GetLONGINT];
	//
	//	Read in rowbytes, but do nothing with it for now, and then read in the pixmap.
	//
	rowBytes = [sourceFile   GetPositiveINTEGER];
	rowBytes = 0x7FFF & rowBytes;
	[self   GetPixMapFrom: sourceFile  Into: &theMap];
	//
	//	Insert a real hack.  Someday we may indeed want to process 16 bit
	//	images.  But, for now the ambiguity in Inside mac, coupled with a lack
	//	of samples here, coupled with the fact that I have no personal interest in
	//	getting it done just now (someday, but not just now), really does motivate
	//	me to put this hack in.
	//
	if (theMap.pixelSize == 16)
	{
		[self   StoreErrorCode: ERR_NOSIXTEEN
			AndText: "Convert PICT found a 16bit image.  Alas, this version is not able to process those.  Your image can not be converted here."];
		goto HACKGOTO;
	}
	
	//
	//	Read the source and dest rectangles in from the bitmap
	//
	sourceRect = [sourceFile   GetRect];
	destRect = [sourceFile   GetRect];
	//
	//	Start writing out data to the PS file.
	//		Convert and write out the transfer mode
	//		Write the left and bottom coordinates of the destination rectangle
	//			(note that bottom in PS coords is top in PICT)
	//		Write the height and width of the destination rectangle
	//		Write the height and width of the source rectangle
	//		IF there is a region, convert it. In either case, write a flag indicating whether
	//			there is a region written out.
	//
	[self ConvertMode];
	[destFile   WriteInteger: destRect->left];
	[destFile   WriteInteger: destRect->top];	// because bottom right in pict is top in ps..
	[destFile   WriteInteger: destRect->right - destRect->left];
	[destFile   WriteInteger: destRect->bottom - destRect->top];
	[destFile   WriteInteger: sourceRect->right - sourceRect->left];
	[destFile   WriteInteger: sourceRect->bottom - sourceRect->top];
	[destFile  ForceNewLine];	// make sure the following data starts on a new line.
	if (PackingSetting == YES)
		[destFile WriteTextLine: "true"];
	else
		[destFile WriteTextLine: "false"];
	if (hasRegion == YES)
	{
		[self ConvertRegion];
		[destFile WriteTextLine: "true"];
	}
	else
		[destFile WriteTextLine: "false"];
	//
	//	Compute data needed for consumption of the bitmap.  Specifically:
	//	Determine how many pixels high and wide the image rectangle is.
	//	This figure very likely differs from the rowBytes width, so figure out exactly
	//	how many BYTES are in the width of the bitmap to go out.
	//	Then, figure out what the offset of the start of this bitmap in the raw data is,
	//	since strangely a bitmapped image is not always left justified in a pict bitmap...
	//
	dataHigh = theMap.bounds.bottom - theMap.bounds.top;
	dataWide = sourceRect->right - sourceRect->left;
	diff = ( (rowBytes*8) - (dataWide *bitsDeep) ) / 8;
	bytesOut = rowBytes -diff;	// remove any 'fluf' bytes.
	bitmapOffset = sourceRect->left - theMap.bounds.left; // there may be a strange offset
	//
	//	Now, write out the command to PS, and then have the bitmap data itself converted.
	//
	[destFile WriteTextLine: "24bitBitmap"];
	//
	//	Set up a value, and then switch on it to determine what packing type we are using.
	//
	decider = theMap.packType;
	if (rowBytes < 8)
		decider = 1;
	switch (decider)
	{
		case 1:
			[self   ConvertPackType1WithHeight: dataHigh
				AndWidth: rowBytes
				AndOffiset: bitmapOffset];
			break;
		case 2:
			[self   ConvertPackType2WithHeight: dataHigh
				AndWidth: rowBytes
				AndOffiset: bitmapOffset];
			break;
		case 3:
			// 16 bit image.  Skipped by thing above
			break;
		case 4:
			[self   ConvertPackType4WithHeight: dataHigh
				AndWidth: rowBytes
				AndOffiset: bitmapOffset];
			break;
		default:
			if (theMap.packType < 0)
			{
				[self   StoreErrorCode: ERR_BADPACK
					AndText: "Encountered an unknown packType when converting an image.  Your PICT file may be bad."];
			}
			else
			{
				for (index = 1; index <= dataHigh; index++)
				{
					if (rowBytes > 250)
						count = [sourceFile  GetINTEGER];
					else
						count = [sourceFile   GetByte];
					[sourceFile   AdvanceBytes: count];
				}
				[self   StoreErrorCode: ERR_UNKNOWNPACK
					AndText: "Encountered an unknown packType  when converting an image.  Unable to convert it."];
			}
			break;
	}

	HACKGOTO:

	FreeByteString((ByteString)sourceRect);
	FreeByteString((ByteString)destRect);
	return self;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		ConvertPackType1WithHeight:AndWidth:AndOffset:
//	Parameters:
//				height of bitmap (in pixels)
//				width of bitmap (pixels)
//				count of bitmap offset.
//	Returns: 	self
//	Stores:		none  explicitly
//	Description:
//		This extracts a 'direct' bitmap that was stored with packing type 1.
//		Apparently, this is used only with 16 and 32bit color images.
//		In this packing scheme, the pixel values are not altered at all.  So, we do
//		nothing with them but write them out in hex, and potentially rle
//		encode them..
//	History:
//		93.08.21	djb	Created.
//	Bugs:
//		We ignore the offset value.  It's conceivable, but I don't know if it's probable,
//		that the bitmap would not be left aligned.  If not, we would need the offset to
//		fix the problem.  This probably only occurrs with bitmaps whose pixel size is
//		less than a byte.
//		This will fail to work for 16 bit graphics, actually.  However, since our
//		caller discards 16 bit values for now, we can also ignore them.
//		Warning!  Rowbytes may need to be multiplied by .75! if this is 32 bit
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ConvertPackType1WithHeight: (Integer) dataHigh
				AndWidth: (Integer) rowBytes
				AndOffiset: (Integer) bitmapOffset
{
	Integer	index;
	Integer	bytesPerRow;
	ByteString	scanline;
	ByteString	packedline;
	Integer	packlength;
	
	bytesPerRow = rowBytes;
	scanline = NewByteString(bytesPerRow);
	packedline = NewByteString(bytesPerRow*2);
	
	for (index = 1; index <= dataHigh; index++)
	{
		[sourceFile   Read: bytesPerRow BytesInto: scanline];
		if (PackingSetting == YES)
		{
			packlength = [self  Pack: bytesPerRow BytesFrom:  scanline Into:  packedline];
			[destFile   Write: packlength BytesOfHexDataFrom: packedline    ];
		}
		else
			[destFile   Write: bytesPerRow BytesOfHexDataFrom: scanline];
	}
	
	FreeByteString(packedline);
	FreeByteString(scanline);
	return self;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		ConvertPackType2WithHeight:AndWidth:AndOffset:
//	Parameters:
//				height of bitmap (in pixels)
//				width of bitmap (pixels)
//				count of bitmap offset.
//	Returns: 	self
//	Stores:		none  explicitly
//	Description:
//		This extracts a 'direct' bitmap that was stored with packing type 2.
//		This packing type is used only for 32bit images where each pixel is
//		represented by 3 componets of 8 bytes each (RGB) and one spare 8 byte component.
//		In this packing scheme, that spare component is removed.  We simply read the
//		bitmap data in, and turn around and write it back out.
//	History:
//		93.08.21	djb	Created.
//	Bugs:
//		We ignore the offset value.  It's conceivable, but I don't know if it's probable,
//		that the bitmap would not be left aligned.  If not, we would need the offset to
//		fix the problem.  This probably only occurrs with bitmaps whose pixel size is
//		less than a byte.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ConvertPackType2WithHeight: (Integer) dataHigh
				AndWidth: (Integer) rowBytes
				AndOffiset: (Integer) bitmapOffset
{
	Integer	bytesPerRow;
	ByteString	scanline;
	ByteString	packedline;
	Integer	packlength;
	
	bytesPerRow = rowBytes * .75;	// so sayeth the color quickdraw section of inside mac VI
	scanline = NewByteString(bytesPerRow);
	packedline = NewByteString(bytesPerRow*2);
	
	[sourceFile   Read: bytesPerRow BytesInto: scanline];
	if (PackingSetting == YES)
	{
		packlength = [self  Pack: bytesPerRow BytesFrom:  scanline Into:  packedline];
		[destFile   Write: packlength BytesOfHexDataFrom: packedline];
	}
	else
		[destFile   Write: bytesPerRow BytesOfHexDataFrom: scanline];

	FreeByteString(packedline);
	return self;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		ConvertPackType4WithHeight:AndWidth:AndOffset:
//	Parameters:
//				height of bitmap (in pixels)
//				width of bitmap (pixels)
//				count of bitmap offset.
//	Returns: 	self
//	Stores:		none  explicitly
//	Description:
//		This extracts a 'direct' bitmap that was stored with packing type 4.
//		This packing type is used only for 32bit images where there are 3 components used
//		per pixel.  The scheme is simple.  Each scanline is compressed using packbits.
//	History:
//		93.08.21	djb	Created.
//	Bugs:
//		We ignore the offset value.  It's conceivable, but I don't know if it's probable,
//		that the bitmap would not be left aligned.  If not, we would need the offset to
//		fix the problem.  This probably only occurrs with bitmaps whose pixel size is
//		less than a byte.
//		If the image happens to have 4 components per pixel, we are going to die.
//		The doc says nothing about manipulating rowbytes, explicitly.  Yet, the example
//		of this type that i have had it multiplied by .75 (to take into account the lack of
//		a fourth component.  It may be simplistic to assume I should always be doing this?
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ConvertPackType4WithHeight: (Integer) dataHigh
				AndWidth: (Integer) rowBytes
				AndOffiset: (Integer) bitmapOffset
{
	Integer			index;
	Integer			bytesPerRow;
	ByteString		weirdline;
	ByteString		inputline;
	ByteString		sortedline;
	PositiveInteger	count;
	PositiveInteger	numPixels;
	PositiveInteger	pixel;
	ByteString		redAddr;
	ByteString		greenAddr;
	ByteString		blueAddr;

	bytesPerRow = .75 * rowBytes;	// See above.
	numPixels = .25 * rowBytes;	// get length of one set of components
	inputline = NewByteString(bytesPerRow*2);
	weirdline = NewByteString(bytesPerRow);
	sortedline = NewByteString(bytesPerRow);

	for (index = 1; index <= dataHigh; index++)
	{
		if (rowBytes > 250)
			count = [sourceFile  GetPositiveINTEGER];
		else
			count = [sourceFile   GetByte];

		[sourceFile   Read: count BytesInto: inputline];
		//
		//	Now, we must unpack the data, because as it stands, it is made up of
		//	the RGB values, with all the R values first, then all the G, etc...
		//	So, we unpack it, and then reorganize the bytes that make up the components
		//	of each pixel, sorting them into an RGB order for the postscript routine.
		//
		[self Unpack: bytesPerRow BytesFrom: inputline Into: weirdline];
		redAddr = weirdline;
		greenAddr = &weirdline[numPixels];
		blueAddr = &weirdline[numPixels * 2];

		for (pixel = 0; pixel < numPixels; pixel++)
		{
			sortedline[pixel*3] = redAddr[pixel];
			sortedline[(pixel*3)+1] = greenAddr[pixel];
			sortedline[(pixel*3)+2] = blueAddr[pixel];
		}
		//
		//	Whew!  Now that that is done, we turn around and pack the line (if needed)
		//	and write it out.
		//
		if (PackingSetting == YES)
		{
			//
			//	Reuse inputline and count.  What the heck. 
			//
			count = [self Pack: bytesPerRow BytesFrom: sortedline Into: inputline];
			[destFile   Write: count BytesOfHexDataFrom: inputline];
		}
		else
		{
			[destFile   Write: bytesPerRow BytesOfHexDataFrom: sortedline];
		}
	}

	FreeByteString(sortedline);
	FreeByteString(weirdline);
	FreeByteString(inputline);

	return self;
}


		
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:	Unpack:BytesFrom:Into:
//	Parameters:
//		A count of the number of bytes to unpack
//		A source string
//		A destination string.
//	Returns: 	self
//	Stores:		none
//	Description:
//		This repeatedly unpacks runs from the source line, and stores them into
//		destline.  It stops only when numBytes have been written to destLine.
//		NOTE: This, then, watches for no stop marker in the sourceLine.  If data is
//		malformed, or numBytes is bad, this may careen into space that doesn't belong to it.
//		It is also assumed that destLine is large enough for whatever gets unpacked.
//		This algorithm is, I believe, documented in, Adobe's PS level 2 reference manual
//		reference manual as their simple RLE filter and in an Apple tech note on PackBits
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- Unpack: (PositiveInteger) numBytes BytesFrom:  (ByteString) sourceLine
	Into:  (ByteString) destLine
{
	Byte		flag;
	Byte		dataByte;
	Integer	index;
	Integer	sourceIndex = 0;
	Integer	destIndex = 0;

	do
	{
		flag = sourceLine[sourceIndex];
		sourceIndex++;
		if (flag < 128)
		{
			//
			//	Copy one more than the number of bytes specified by flag litterally.
			//
			flag++;
			for (index = 0; index < flag; index++)
			{
				destLine[destIndex] =  sourceLine[sourceIndex];
				sourceIndex++;
				destIndex++;
			}
		}
		else
		{
			//
			//	Copy the next byte 257-flag times
			//
			dataByte =sourceLine[sourceIndex];
			sourceIndex++;
			for (index = 0; index < (257 -flag); index ++)
			{
				destLine[destIndex] = dataByte;
				destIndex++;
			}
		}
	}
	while (destIndex < numBytes);
	return self;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:	Pack:BytesFrom:Into:
//	Parameters:
//		A count of the number of bytes to pack
//		A source string
//		A destination string.
//	Returns: 	the size of the packed data
//	Stores:		none
//	Description:
//		This will pack the source line into the dest line, stopping only after it has written
//		lineLength bytes from source.  This assumes that the length parameter is good,
//		and that the source buffer has that many bytes, and that dest buffer has enough
//		space for the packing (I really should calculate a worst case scenario some time)
//		This data format is, I believe, documented in, Adobe's PS level 2 reference manual
//		reference manual as their simple RLE filter and in an Apple tech note on PackBits
//	Bugs:
//		This routine is not commented enough.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-(PositiveInteger) Pack: (PositiveInteger) lineLength BytesFrom:  (ByteString) source
	Into:  (ByteString) dest
{
	Boolean	runs = NO;
	Integer	numTraversedBytes;
	Integer	startByte = 0;
	Integer	index = 0;
	Integer	destIndex = 0;
	Integer	ctr;
	
		index = 0;
		do
		{
			startByte = index;
			//
			//	first Byte is always assumed to be OK.  Safe assumption.  Either
			//	we are starting (Byte 0 should always be good), or we just dumped
			//	some data, at which index points to the first Byte that did not match
			//	that pattern, and so is good to use for starting with the next
			//
			index++;
			//
			// Try to accumulate a run
			//
			while ((source[index-1] == source[index]) &&
					((index-startByte) < 128) &&
					(index < lineLength))
				{ index++; }
			if ((index-startByte) > 2)
				runs = YES;
			else
			{
				//
				//	not enough to justify a run, so carry on. as a litteral sequence of Bytes
				//
				while ((source[index-1] != source[index]) &&
						((index-startByte) < 128) &&
						(index < lineLength))
					{ index++; }
				if (index < lineLength) // We ran into a match, not the end
					{ index--; }	// Thus, back up over 1 of the 2 Bytes.
				runs = NO;
			}
			numTraversedBytes = index-startByte;
			if (runs == YES) 
			{
				dest[destIndex] = 257-numTraversedBytes;
				destIndex++;
				dest[destIndex] = source[startByte];
				destIndex++;
			}
			else
			{
				dest[destIndex] = numTraversedBytes - 1;
				destIndex++;
				for (ctr = startByte; ctr < startByte+numTraversedBytes; ctr++)
				{
					dest[destIndex] = source[ctr];
					destIndex++;
				}
			}
		}
		while (index < lineLength);

	return destIndex;	//return a number that is the num of bytes in the destination
}



//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		Invert:In
//	Parameters:	A number of bytes to convert, and the buffer to do it in.
//	Returns: 	self
//	Stores:		none
//	Description:
//		This simply inverts numBytes bytes in theBuffer.  
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- Invert: (Integer) numBytes In: (ByteString) theBuffer;
{
	Integer	index;
	for (index = 0; index < numBytes; index++)
		theBuffer[index] = ~(theBuffer[index]);
	return self;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Method:		Copy:BitsAtOffset:From:Into:
//	Parameters:	The number of bits in the source data to be copied
//				The number of bits, offset from the start of source, where source data starts
//				The source byte string to copy from
//				The destination byte string to copy into
//	Returns: 	The destination pointer (identical to the one passed)
//	Stores:		error code
//				1 The destination pointer (as a Pointer)
//				2 The count of the number of bytes stored in the destination
//	Description:
//		This routine can be used to extract arbitrary bit patterns out of a source string.
//		It was created to serve two different purposes: to allow one to get a bitmap from
//		a PICT image so it was left justified in its storage space, rather than offset some
//		number of bits in.  The other use was to extract the characters of a Mac font,
//		one scanline at a time, from the single bitmap tht makes up a Mac font.
//		Ultimately, the work is simple: take a source string, and an offset into it where
//		the data resides (and the length of the source data).  Copy the relevant bits from
//		the source to the dest.  Note that this aligns the left of the source data to the
//		beginning of a byte boundrary in dest.  Also note that it fills up an integral number
//		of bytes in dest, filling the last out with 0's if needed.
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (ByteString) Copy: (PositiveInteger) sourceSize
	BitsAtOffset: (PositiveInteger) offset
	From: (ByteString) source
	Into: (ByteString) dest
{
	PositiveInteger	firstByte, lastByte;	// First and last bytes in source string to be copied
	PositiveInteger	index, destIndex;		// indices into source and dest strings
	PositiveInteger	offsetInByte;			// Number of bits into a byte a dest byte starts
	Byte			byte1, byte2;			// Temporary bytes to hold info as copied
	
	Byte	mask;
	PositiveInteger	bitpos, bitsMoved, finalBits;
	
	[self	ResetResults];
	
	if (sourceSize == 0)
	{
		[self   StoreErrorCode: ERR_BADSOURCESIZE
			AndText: "You requested for 0 bytes.  This has been fulfilled, weird as it is."];
		dest[0] = 0;
		[self   StorePointer: (Pointer) dest];
		[self   PutPositiveInteger: 0 Into: SECOND_RESULT];
	}
	else
	{
		//
		//	Computer our start and end position in source string, in bytes, and compute what
		//	bit offset in the first byte we should start working at
		//
		firstByte = offset / 8;
		lastByte = ((offset+sourceSize-1) / 8);
		offsetInByte = offset % 8;	// Zero origin...
		//
		//	Bear in mind that it's most likely that the destination byte will straddle two bytes
		//	in the source string.  So.  Get two bytes at a time from the source, and bitshift
		//	so they each contain only the bits that are relevant to the destination byte (get its
		//	upper and lower parts), then fuse the two parts together into a single destination
		//	byte.  Increment our destination index, and store the upper of the two bytes, for
		//	it will be the lower one the next time around. 
		//	Following this, deal with any remaining bits that might belong to the final
		//	dest byte, and pad them with zero.
		//
		destIndex = 0;
		//
		//	Two major cases: our data resides in only a single byte, or it spans
		//	two or more bytes:
		//
		if (firstByte == lastByte)
		{
			//
			//	First, shift the data in the byte down so it is aligned with the start
			//	of the byte.
			//	Then Build a mask to clear out any extraneous bits that were above the
			//	source data.  We're too lazy to actully determine whether there is any
			//	junk (there would be if the offset to the data + the length < 8 bits), and
			//	so, build a mask of sourceSize length, and use it.
			//
			byte1 = source[firstByte] << offsetInByte;
			mask = 0x00;
			for (bitpos = 0; bitpos < sourceSize; bitpos ++)
			{
				mask = mask >> 1;
				mask |= 0x80;
			}
			dest[destIndex] = byte1 &  mask;
			destIndex++;
		}
		else
		{
			//
			//	Start off by pulling off contiguous groups of 8 bits from
			//	the source, and storing them as bytes in the destination.
			//
			for (index = firstByte; index < lastByte; index++)
			{
				byte1 = source[index] << offsetInByte;
				byte2 = source[index+1] >> ( 8 - offsetInByte);
				
				dest[destIndex] = byte1 | byte2;
				destIndex++;
			}
			//
			//	There are 3 possible states at this point:
			//	- We happen to have copied exactly the number of source bits to the dest.
			//	- We have copied too many bits to the dest (i.e. our final byte2 group had
			//		too many source bits in it)
			//	- We have not copied enough bits (i.e. there were bits in source[lastByte] that
			//		were not moved with those in byte2).
			//	Process each of these accordingly.
			//
			//	Calculate:
			//		Bits copied to dest in byte2:
			//		Bits source bits actually in source[lastbyte]
			//
			bitsMoved = offsetInByte;
			finalBits = (offset+sourceSize) - (lastByte*8);
			//
			//	There are a few bits which we have not yet moved.
			//
			if (finalBits > bitsMoved)
			{
				byte1 = source[index] << bitsMoved;
				mask = 0x00;
				for (bitpos = 0; bitpos < (finalBits - bitsMoved); bitpos ++)
				{
					mask = mask >> 1;
					mask |= 0x80;
				}
				dest[destIndex] = byte1 & mask;
				destIndex++;
			}
			//
			//	Check if we moved more bits than we should have
			//
			else  if (finalBits < bitsMoved)
			{
				mask = 0x00;
				for (bitpos = 0; bitpos < finalBits+( 8 - offsetInByte); bitpos ++)
				{
					mask = mask >> 1;
					mask |= 0x80;
				}
				dest[destIndex-1] = dest[destIndex-1] & mask;
			}
		}
	
		[self   StoreErrorCode: ERR_OK AndText: "No errors (big surprise)"];
		[self   StorePointer: (Pointer) dest];
		[self   PutPositiveInteger: destIndex Into: SECOND_RESULT];
	}

	return dest;
}

@end
