ftp.nice.ch/peanuts/GeneralData/Documents/adobe/DPS.Purple.ImportAdv.tar.gz#/NX_ImportAdv/GraphicImport.m

This is GraphicImport.m in view mode; [Download] [Up]

/*
 * (a)  (C) 1990 by Adobe Systems Incorporated. All rights reserved.
 *
 * (b)  If this Sample Code is distributed as part of the Display PostScript
 *	System Software Development Kit from Adobe Systems Incorporated,
 *	then this copy is designated as Development Software and its use is
 *	subject to the terms of the License Agreement attached to such Kit.
 *
 * (c)  If this Sample Code is distributed independently, then the following
 *	terms apply:
 *
 * (d)  This file may be freely copied and redistributed as long as:
 *	1) Parts (a), (d), (e) and (f) continue to be included in the file,
 *	2) If the file has been modified in any way, a notice of such
 *      modification is conspicuously indicated.
 *
 * (e)  PostScript, Display PostScript, and Adobe are registered trademarks of
 *	Adobe Systems Incorporated.
 * 
 * (f) THE INFORMATION BELOW IS FURNISHED AS IS, IS SUBJECT TO
 *	CHANGE WITHOUT NOTICE, AND SHOULD NOT BE CONSTRUED
 *	AS A COMMITMENT BY ADOBE SYSTEMS INCORPORATED.
 *	ADOBE SYSTEMS INCORPORATED ASSUMES NO RESPONSIBILITY
 *	OR LIABILITY FOR ANY ERRORS OR INACCURACIES, MAKES NO
 *	WARRANTY OF ANY KIND (EXPRESS, IMPLIED OR STATUTORY)
 *	WITH RESPECT TO THIS INFORMATION, AND EXPRESSLY
 *	DISCLAIMS ANY AND ALL WARRANTIES OF MERCHANTABILITY, 
 *	FITNESS FOR PARTICULAR PURPOSES AND NONINFRINGEMENT
 *	OF THIRD PARTY RIGHTS.
 */

/*
*	GraphicImport.m
*
*	This subclass of handles much of the overhead for imported
*	files such as TIFF files and EPS files. Subclasses of this object
*	provide the specific methods that differentiate the two.
*
*	Version:	2.0
*	Author:	Ken Fromm
*	History:
*			03-20-91		Created the file.
*/

#import "GraphicImport.h"
#import "DocView.h"
#import "DrawingView.h"
#import "DrawingViewWraps.h"
#import "NXBitmapImageRepSub.h"
#import "NXEPSImageRepSub.h"
#import "ResourcePanel.h"
#import "rotateprocs.h"

#import <appkit/Font.h>
#import <appkit/NXImage.h>
#import <appkit/Panel.h>
#import <appkit/nextstd.h>

#import <objc/List.h>
#import <dpsclient/wraps.h>
#import <streams/streamsimpl.h>

#import <mach.h>

static const char	*ImageErrorString = "PostScript errors have been encountered in this file. The text of the errors can be found in the console output.";

@implementation GraphicImport

/*
 *	Initializes a new GraphicImport object associated with the file.
 *	If an EPS file, then check the resources. Three routes are possible.
 *	The first makes everything as ok. The second cancels the import.
 *	The third makes the file as unimageable.
 */
- initFromFile:(const char *) file
{
	char			*end;

	ResourceList		resourceList;

	self = [super  init];

	gflags.new = YES;
	gflags.dirty = YES;
	[self  setFilename:file];
	image = [[NXImage  alloc]  init];
	if (image)
	{
		end = strrchr(file, '.');
		if (end)
		{
			if (strncmp(end, ".ps", 3) == 0 || strncmp(end, ".eps", 4) == 0)
			{
				imagerep = [[NXEPSImageRepSub  alloc]  initFromFile:file];
				if (imagerep)
				{
					/*  Check the epsf file and list any that are unavailable. */
					if (![imagerep  checkResources:&resourceList] ||
						![self  listUnavailableResources:&resourceList])
					{
						[imagerep  free];
						imagerep = NULL;
					}
					else
						[imagerep  getBoundingBox:&bounds];
				}
				else
					Notify("Import Error", "Unable to open file.");
			}
			else if (strncmp(end, ".tiff", 5) == 0)
			{
				imagerep = [[NXBitmapImageRepSub  alloc]  initFromFile:file];
				if (imagerep)
				{
					bounds.origin.x = bounds.origin.y = 0.0;
					[imagerep  getSize:&bounds.size];
				}
				else
					Notify("Import Error", "Unable to open file.");
			}
		}
	}
	else
		Notify("Import Error", "Unable to open file.");

	if (!imagerep)
	{
		[self  free];
		self = nil;
	}

	return self;
}

/*
*	Create a new object from a stream.
*/
- initFromStream:(NXStream *) stream
{
	int			len, maxlen;

	char			*data;

	ResourceList		resourceList;

	self = [super  init];

	gflags.new = YES;
	gflags.dirty = YES;
	image = [[NXImage  alloc]  init];
	if (image)
	{
		NXGetMemoryBuffer(stream, &data, &len, &maxlen);
		if (strncmp(data, "%!PS-Adobe-", 11) == 0)
		{
			imagerep = [[NXEPSImageRepSub  alloc]  initFromStream:stream];
			if (imagerep)
			{
				/* Check the epsf file and list any that are unavailable. */
				if (![imagerep  checkResources:&resourceList] ||
					![self  listUnavailableResources:&resourceList])
				{
					[imagerep  free];
					imagerep = NULL;
				}
				else
					[imagerep  getBoundingBox:&bounds];
			}
			else
				Notify("Import Error", "Unable to open file.");
		}
		else
		{
			imagerep = [[NXBitmapImageRepSub  alloc]  initFromStream:stream];
			if (imagerep)
			{
				bounds.origin.x = bounds.origin.y = 0.0;
				[imagerep  getSize:&bounds.size];
			}
			else
				Notify("Import Error", "Unable to open file.");
		}
	}
	else
		Notify("Import Error", "Unable to open file.");

	if (!imagerep)
	{
		[self  free];
		self = nil;
	}

	return self;
}

- (BOOL) listUnavailableResources:(ResourceList *) resourceList;
{
	BOOL		unavailable;

	int			i, tag;

	tag = NX_OKTAG;
	unavailable = NO;
	for (i = 0; i < RES_NUMTYPES; i++)
		unavailable = unavailable | (resourceList->types[i] != NULL);
	
	if (unavailable)
	{
		tag = [[NXApp  resourcePanel]  runModalWithList:resourceList  andName:filename];
		if (tag == NX_CANCELTAG)
		{
			[imagerep  free];
			imagerep = NULL;
		}
		else if (tag == NX_ALERTOTHER)
			gflags.unimageable = YES;
	}

	for (i = 0; i < RES_NUMTYPES; i++)
		[resourceList->types[i]  free];

	return (tag != NX_CANCELTAG);
}

/*
*	Used when printing. Add the resources used in this file
*	to the list passed in if the file is in the rectangle.
*/
- addResources:(Resource *) resourceDoc  for:(NXRect *) r
{
	if (!r || IntersectsRotatedRect(r, &bounds, rotation))
		[imagerep  addResources:resourceDoc  forFile:filename];
	
	return self;
}

- free
{
	[image  free];
	[imagerep  free];

	return [super  free];
}

- freeTemp
{
	return [super  free];
}

- copyTemp
{
	id	new;

	new = [super copy];
	[new  setFilename:filename];
	[new  setImage:image];
	[new  setImageRep:imagerep];

	return new;
}

- setFilename:(const char *) file
{
	if (file)
		filename = NXUniqueString(file);

	return self;
}

/* Used when producing a copy of this object. */
- setImage:anImage
{
	image = anImage;

	return self;
}

/* Used when producing a copy of this object. */
- setImageRep:anImageRep
{
	imagerep = anImageRep;

	return self;
}

- setSelected:(BOOL) flag
{
	gflags.selected = flag;

	return self;
}

- (BOOL) selected
{
	return (BOOL) gflags.selected;
}

- setImageable:(BOOL) flag
{
	gflags.unimageable = !flag;

	return self;
}

- (BOOL) imageable
{
	return (BOOL) !gflags.unimageable;
}

- (BOOL) error
{
	return gflags.error;
}

/*
*	pt_num is the changing control point. pt holds the relative change in each coordinate. 
*	The relative is needed and not the absolute because the closest inside control point
*	changes when one of the outside points change.
*/
- setPoint:(int) pt_num  :(const NXPoint *) pt
{	
	int			row, col;

	NXPoint		aPt, cPt, oldOrigin;

	if (pt->x || pt->y)
	{
		gflags.dirty = YES;
		aPt = *pt;
		cPt.x = cPt.y = 0.0;
		RotatePoint(&aPt, &cPt, -rotation);

		oldOrigin = bounds.origin;

		col = pt_num % 3;
		switch (col)
		{
			case LEFT:
				bounds.origin.x += aPt.x;	
				bounds.size.width -= aPt.x;
				break;
			case RIGHT:
				bounds.size.width += aPt.x;	
				break;
		}

		row = pt_num/3;
		switch (row)
		{
			case TOP:
				bounds.size.height += aPt.y;	
				break;
			case BOTTOM:
				bounds.origin.y += aPt.y;
				bounds.size.height -= aPt.y;
				break;
		}
		RotatePoint(&bounds.origin, &oldOrigin, rotation);
	}

	return self;
}

- setSize:(const NXSize *) aSize
{
	if (bounds.size.width != aSize->width && bounds.size.height != aSize->height)
	{
		gflags.dirty = YES;
		bounds.size = *aSize;
	}

	return self;
}

- setOrigin:(const NXPoint *) pt
{	
	bounds.origin = *pt;

	return self;
}

- setBounds:(const NXRect *) aRect
{
	[self  setSize:&aRect->size];
	[self  setOrigin:&aRect->origin];

	return self;
}

/*
*	Return the dimensions to the files original size.
*	Keep the same upper left corner.
*/	
- setOriginalSize
{
	NXSize		original;

	NXRect		boundsNew;

	[imagerep  getSize:&original];
	if (bounds.size.width != original.width || bounds.size.height != original.height)
	{
		gflags.dirty = YES;
		boundsNew.origin.x = bounds.origin.x;
		boundsNew.origin.y = bounds.origin.y + bounds.size.height - original.height;	
		boundsNew.size = original;
		RotatePoint(&boundsNew.origin, &bounds.origin, rotation);
		bounds = boundsNew;
	}

	return self;
}

/*
*	Return the dimensions to the files original ratio.
*	Use its mimimum dimension as the guide to figure
*	the other dimension keeping the same upper left corner.
*/	
- setOriginalRatio
{
	float			aspect, aspectOrig;

	NXSize		original;

	NXRect		boundsNew;

	[imagerep  getSize:&original];
	aspect = bounds.size.width/bounds.size.height;
	aspectOrig = original.width/original.height;
	if (aspect != aspectOrig)
	{
		gflags.dirty = YES;
		boundsNew.size = bounds.size;
		if (aspect > aspectOrig)
			boundsNew.size.width = original.width * (bounds.size.height/original.height);
		else
			boundsNew.size.height = original.height * (bounds.size.width/original.width);
	
		if (boundsNew.size.width < SIZE_MIN || boundsNew.size.height < SIZE_MIN)
		{
			if (aspectOrig > 1.0)
			{
				boundsNew.size.width = SIZE_MIN * aspectOrig;
				boundsNew.size.height = SIZE_MIN;
			}
			else
			{
				boundsNew.size.width = SIZE_MIN;
				boundsNew.size.height = SIZE_MIN/aspectOrig;
			}
		}

		boundsNew.origin.x = bounds.origin.x;
		boundsNew.origin.y = bounds.origin.y + bounds.size.height - boundsNew.size.height;	
		RotatePoint(&boundsNew.origin, &bounds.origin, rotation);

		bounds = boundsNew;

		return self;
	}

	return nil;
}

/*	Rotate the object about the point. */
- rotateAboutPoint:(NXPoint *) aPoint  withAngle:(float) angle
{
	if (angle != 0.0)
	{
		gflags.dirty = YES;
		rotation += angle;
		RotatePoint(&bounds.origin, aPoint, angle);
	}
	return self;
}

/* The pt argument holds the relative point change. */
- moveAll:(const NXPoint *) pt
{
	bounds.origin.x += pt->x;
	bounds.origin.y += pt->y;

	return self;
}

/* Given the point number, return the point. */
- getPoint:(int) pt_num  :(NXPoint *) pt
{	
	pt->x = bounds.origin.x + (pt_num % 3) * (bounds.size.width/2.0);
	pt->y = bounds.origin.y + bounds.size.height - (pt_num/3) * (bounds.size.height/2.0);

	RotatePoint(pt, &bounds.origin, rotation);
	
	return self;
}

/*
*	Returns the bounding box of the file. Factors in the rotation.
*/
- getBounds:(NXRect *)aRect
{
	RotateRectBounds(aRect, &bounds, &bounds.origin, rotation);

	return self;
}

/*
*	Return the rectangle that should be used for scrolling purposes.
*	When the rectangle passes out of the visible rectangle then
*	the screen should scroll.
*/
- getScrollRect:(NXRect *)aRect  forPtNum:(int) pt_num
{
	if (pt_num == -1)
	{
		[self  getBounds:aRect];
	}
	else
	{
		[self  getPoint:pt_num  :&aRect->origin];
		aRect->size.width = aRect->size.height = 0;
	}

	return self;
}

/* 	Constrain to ANGLES if shift key is held down. */
- constrainAngle:(float *)angle  withFlags:(int) flags
{
	if ((flags & NX_SHIFTMASK) == NX_SHIFTMASK)
		*angle = rint((rotation + *angle) / M_PI * ANGLES) * M_PI / ANGLES - rotation;

	return self;
}

/* 
*	This method constains the point to the bounds of the view. The
*	constaining is dependent on the control point that has been selected.
*/
- constrainPoint:(NXPoint *)aPt  forPtNum:(int *) pt_num
		inRect:(const NXRect *) viewRect  withFlags:(int) flags
{
	int			row, col,
				directionx, directiony;

	float			aspect_ratio;

	NXSize		proposed, original;

	NXPoint		oldOrigin;

	aPt->x = MAX(viewRect->origin.x, aPt->x);
	aPt->x = MIN(viewRect->origin.x + viewRect->size.width, aPt->x);
	aPt->y = MAX(viewRect->origin.y, aPt->y);		
	aPt->y = MIN(viewRect->origin.y + viewRect->size.height, aPt->y);

	RotatePoint(aPt, &bounds.origin, -rotation);
	oldOrigin = bounds.origin;
	[imagerep  getSize:&original];

	col = *pt_num % 3;
	row = *pt_num/3;
	directionx = directiony = 0;
	switch (col)
	{
		case LEFT:
			proposed.width = bounds.origin.x + bounds.size.width - aPt->x;
			directionx =1;	
			break;		
		case MIDDLE:
			proposed.width = bounds.size.width;
			aPt->x = bounds.origin.x + bounds.size.width/2.0;
			break;
		case RIGHT:
			proposed.width = aPt->x - bounds.origin.x;
			directionx = -1;				
			break;
	}

	switch (row)
	{
		case TOP:
			proposed.height = aPt->y - bounds.origin.y;
			directiony = -1;
			break;			
		case MIDDLE:
			proposed.height = bounds.size.height;
			aPt->y = bounds.origin.y + bounds.size.height/2.0;
			break;
		case BOTTOM:
			proposed.height = bounds.origin.y + bounds.size.height - aPt->y;
			directiony = 1;	
			break;
	}

	/* Constrain to the ratio of width vs. height. If new use the original ratio. */	
	if ((flags & NX_SHIFTMASK) == NX_SHIFTMASK || gflags.new)
	{
		if (col != MIDDLE && row != MIDDLE)
		{
			if (gflags.new)
				aspect_ratio = original.width/original.height;
			else
				aspect_ratio = bounds.size.width/bounds.size.height;
			if (proposed.width < SIZE_MIN || proposed.height < SIZE_MIN)
			{
				if (aspect_ratio > 1.0)
				{
					aPt->x = aPt->x + directionx * (proposed.width - SIZE_MIN * aspect_ratio);
					aPt->y = aPt->y + directiony * (proposed.height - SIZE_MIN);
				}
				else
				{
					aPt->x = aPt->x + directionx * (proposed.width - SIZE_MIN);
					aPt->y = aPt->y + directiony * (proposed.height - SIZE_MIN / aspect_ratio);
				}
			}
			else
			{
				if (proposed.width/proposed.height > aspect_ratio)
					aPt->x = aPt->x + directionx *
						(proposed.width - proposed.height * aspect_ratio);
				else
					aPt->y = aPt->y + directiony *
						(proposed.height - proposed.width / aspect_ratio);
			}
		}
		else
		{
			[self  getPoint:*pt_num :aPt];
			RotatePoint(aPt, &bounds.origin, -rotation);
		}
	}
	else
	{
		if (proposed.width < SIZE_MIN)
			aPt->x = aPt->x + (proposed.width - SIZE_MIN) * directionx;
		if (proposed.height < SIZE_MIN)
			aPt->y = aPt->y + (proposed.height - SIZE_MIN) * directiony;
	}

	RotatePoint(aPt, &bounds.origin, rotation);

	return self;
}

/*
*	Check for a control point hit. No need to perform the hit detection in
*	the server since its a simple rectangle intersection check. Return the
*	point number hit in the pt_num argument. The numbering starts in
*	the lower left and goes left to right and bottom to top.
*/
- hitControl:(const NXRect *) hitRect  :(int *) pt_num  forSize:(float) size
{
	int		i, j;

	float		halfc, halfw, halfh, halfhitw, halfhith;

	NXRect	knobRect, mouseRect;

	mouseRect = *hitRect;
	if (rotation != 0.0)
	{
		halfhitw= mouseRect.size.width/2;
		halfhith = mouseRect.size.width/2;
		NXInsetRect(&mouseRect, halfhitw, halfhith);
		RotatePoint(&mouseRect.origin, &bounds.origin, -rotation);
		NXInsetRect(&mouseRect, -halfhitw, -halfhith);
	};

	knobRect.size.width = knobRect.size.height = size;
	halfc = knobRect.size.width/2;
	halfw = bounds.size.width/2;
	halfh = bounds.size.height/2;
	for (i=0; i < 3; i ++)
	{
		for (j = 0; j < 3; j++)
		{
			if (!(i == MIDDLE && j == MIDDLE))
			{
				knobRect.origin.x = bounds.origin.x +  j * halfw - halfc;
				knobRect.origin.y = bounds.origin.y + bounds.size.height - i * halfh - halfc;
				if (NXIntersectsRect(&mouseRect, &knobRect))
				{
					*pt_num = i * 3 + j;
					return self;
				}
			}
		}
	}

	return nil;
}

/*
*	Check for hit dectection on the object. Since the hitpoint and the
*	object is a rectangle just perform a rectangle intersection check.
*	(The hit point is adjusted for rotation so that the intersection check
*	can be used even on a rotated epsf file.) 
*/
- hitObject:(UPath *) hitUpath  ifNotSelected:(BOOL) flag
{
	float		halfhitx, halfhity;

	NXRect	hitRect;

	if (!flag || !gflags.selected)
	{	
		NXSetRect(&hitRect, hitUpath->pts[0], hitUpath->pts[1],
			hitUpath->pts[2] - hitUpath->pts[0],
		 	hitUpath->pts[3] - hitUpath->pts[1]);

		if (rotation != 0.0)
		{
			halfhitx= hitRect.size.width/2;
			halfhity = hitRect.size.width/2;
			NXInsetRect(&hitRect, halfhitx, halfhity);
			RotatePoint(&hitRect.origin, &bounds.origin, -rotation);
			NXInsetRect(&hitRect, -halfhitx, -halfhity);
		}

		if (NXIntersectsRect(&hitRect, &bounds))
			return self;
	}

	return nil;
}

/*
*	Place the point locations and the chararacters for the control
*	points into the user path description passed in. In this case, the
*	xyshow operator is used instead of a user path. But because the
*	xyshow operator takes the same data format as the user path,
*	the buffers for the user paths are used.  The position of the point
*	is calculated relative to the position of the previous point. If
*	this is the first set of points in the description then put the
*	absolute values in place, otherwise use the lastPoint 
*	argument to calculate the displacement.
*/
- putControlPoints:(UPath *)buffer  forRect:(NXRect *)r  :(NXPoint *) lastPoint
{
	int			i, j;

	NXPoint		pt;

	NXRect		bRect;
	
	[self  getBounds:&bRect]; 
	if (!r || NXIntersectsRect(r, &bRect))
	{
		i = buffer->num_ops;
		for (j = 0; j < PTS_GRAPHIC; j++)
			buffer->ops[i++] = 'a';
		buffer->num_ops += PTS_GRAPHIC;

		i = buffer->num_pts;
		if (i == 0)
		{		
			[self  getPoint:0  :&pt];
			buffer->pts[i++] = pt.x;
			buffer->pts[i++] = pt.y;
			*lastPoint = pt;
			j = 1;
		}
		else
			j = 0;

 		for ( ; j < PTS_GRAPHIC + 1; j ++)
		{
			if (j != 4)
			{
				[self  getPoint:j  :&pt];
				buffer->pts[i++] = pt.x - lastPoint->x;
				buffer->pts[i++] = pt.y - lastPoint->y;
				*lastPoint = pt;
			}
		}

		buffer->num_pts += PTS_GRAPHIC*2;
	}
	
	return self;
}

/*
*	Draw a box around the file. If imaging write "imaging file..." if not
*	then draw cross hatches. Ideally we would like to draw the file
*	name in the box but obtaining the font metrics for the font is a
*	non-trivial exercise that is incorporated into this application.
*/
- drawBoxforRect:(NXRect *) r  imaging:(BOOL) imageFlag
{
	char		*string;

	NXSize	strSize;

	PSgsave();
	NXRectClip(r);
	PSWTranslateRotate(bounds.origin.x, bounds.origin.y, rotation * ANGLE);
	PSsetgray(NX_LTGRAY);
	PSrectfill(0.0, 0.0, bounds.size.width, bounds.size.height);

	if (imageFlag)
	{
		strSize.width = 70;
		strSize.height = 12;
		string = "Imaging file...";
	}
	else
	{
		PSWSetLine(2.0, NX_DKGRAY);
		PSWStrokeX(0.0, 0.0, bounds.size.width, bounds.size.height);
		PSrectstroke(0.0, 0.0, bounds.size.width, bounds.size.height);
		/* Insert code to show the file name. */
		strSize.width = bounds.size.width;
		strSize.height = bounds.size.height;
		string = (char *) filename;
	}

	if (string && bounds.size.width > strSize.width && bounds.size.height > strSize.height)
	{
		PSsetgray(NX_BLACK);
		PSmoveto((bounds.size.width - strSize.width)/2, (bounds.size.height - strSize.height)/2);
		PSselectfont("Helvetica",  12);
		PSshow(string);
	}
	PSgrestore();

	return self;
}

/*
 *	Draws the TIFF or EPS file.  Sets up for drawing in a window if drawing
 *	onscreen and the image is smaller than the maximum window size
 *	permitted.  Makes the window transparent and scales it to the right
 *	size to match the scale of the document.
 */
- drawObject:(NXRect *) r  withFlags:(int) flags  inView:view
{
	BOOL		useImage;

	float			scale;

	NXRect		rect, frame;

	if (!r || IntersectsRotatedRect(r, &bounds, rotation))
	{
		if (flags & REDRAWFLAG)
		{
			scale = [[view  superview]  scale];
			PSgsave();
			PSWTranslateRotate(bounds.origin.x, bounds.origin.y, rotation * ANGLE);
			PSWSetLine(1.0/scale, NX_LTGRAY);
			PSrectstroke(0.0, 0.0, bounds.size.width, bounds.size.height);
			PSgrestore();
		}
		else if (gflags.unimageable || ![NXApp  imagingFlag])
		{
			[self  drawBoxforRect:r  imaging:NO];
			gflags.new = NO;
		}
		else if (NXDrawingStatus == NX_PRINTING ||
				NXDrawingStatus == NX_COPYING ||
					[view  image])
		{
			[imagerep  drawIn:&bounds  with:(rotation*180/M_PI)];
		}
		else
		{
			useImage = YES;

			/* Get the frame of the epsf file. */
			RotateRectBounds(&frame, &bounds, &bounds.origin, rotation);
			scale = [[view  superview]  scale];
	
			/*
			*	Size the cache. Use the NXImage if the size is less than
			*	a predetermined size.
			*/
			[image  getSize:&rect.size];
			if (frame.size.width*scale != rect.size.width ||
				frame.size.height*scale != rect.size.height)
			{
				gflags.dirty = YES;
				rect.size.width = frame.size.width * scale;
				rect.size.height = frame.size.height * scale;
				if (rect.size.width < IMAGE_MAX && rect.size.height < IMAGE_MAX)
				{
					[image  setSize:&rect.size];
					[image  getSize:&rect.size];
				}
				else
					useImage = NO;
			}
				
			if (gflags.dirty)
			{
				/*
				*	Draw the gray box that tells the user
				*	the file is being imaged.
				*/
				[view  lockFocus];
				[self  drawBoxforRect:r  imaging:YES];
				[view  unlockFocus];
				[[view  window]  flushWindow];
				NXPing();

				/*
				*	Setup the drawing so that it goes into an NXImage.
				*	Some prep work needs to be done to set the
				*	position.
				*/
				if (useImage)
				{
					rect.origin.x = rect.origin.y = 0;
					[image  lockFocus];
					PSsetalpha(0.0);
					PSsetgray(NX_WHITE);
					NXRectFill(&rect);
					PSsetalpha(1.0);

					PSscale(scale, scale);
					PStranslate(-frame.origin.x, -frame.origin.y);
				}

				if ([NXApp  tracingFlag])
					DPSTraceContext(DPSGetCurrentContext(), YES);

				/*
				*	Draw the file into a subclass of NXEPSImageRep.
				*	Take the rotation into account.
				*/
				gflags.error = ![imagerep  drawIn:&bounds  with:(rotation*180/M_PI)];

				if ([NXApp  tracingFlag])
					DPSTraceContext(DPSGetCurrentContext(), NO);

				if (useImage)
					[image  unlockFocus];

				/*
				*	Display an error panel if one occurs. If it is
				*	a newly imported file, it will not be
				*	imported. If it is an existing file,
				*	will be marked as unimageable.
				*/
				if (gflags.error)
				{
					gflags.unimageable = YES;
					[self  displayError];
					if (!gflags.new)
						[self  drawBoxforRect:r  imaging:NO];
				}
			}

			/*
			*	Composite the buffer to the currently focused view.
			*/
			if (useImage && !gflags.error)
			{
				rect.origin.x = rect.origin.y = 0;
				rect.size.width = frame.size.width * scale;
				rect.size.height = frame.size.height * scale;
				[image  composite:NX_SOVER  fromRect:&rect  toPoint:&frame.origin];
			}

			gflags.new = NO;
			gflags.dirty = NO;
		}
	}

	return self;
}

/*
*	Write out the bounds, rotation, filename and imagerep.
*/
- write:(NXTypedStream *)stream
{
	[super write:stream];
 
	NXWriteRect(stream, &bounds);
	NXWriteType(stream, "f", &rotation);
	NXWriteType(stream, "%", &filename);
	NXWriteObject(stream, imagerep);
		
	return self;
}

/*
*	Read the bounds, rotation, filename and imagerep.
*/
- read:(NXTypedStream *)stream
{
	[super read:stream];

	NXReadRect(stream, &bounds);
	NXReadType(stream, "f", &rotation);
	NXReadType(stream, "%", &filename);
	imagerep = NXReadObject(stream);

	return self;
}

- awake
{
	gflags.new = YES;
	gflags.dirty = YES;
	image = [[NXImage  alloc]  init];

	return self;
}

- displayError
{
	NXRunAlertPanel("Imaging Error", ImageErrorString, "OK", NULL, NULL);

	return self;
}

@end

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.