ftp.nice.ch/peanuts/GeneralData/Documents/adobe/DPS.Purple.ImportInt.tar.gz#/NX_ImportInt/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 "DrawingView.h"
#import "DrawingViewWraps.h"
#import "NXBitmapImageRepSub.h"
#import "NXEPSImageRepSub.h"
#import "ResourcePanel.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
					Notify("Import Error", "Unable to open file.");
			}
			else if (strncmp(end, ".tiff", 5) == 0)
			{
				imagerep = [[NXBitmapImageRepSub  alloc]  initFromFile:file];
				if (!imagerep)
					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
				Notify("Import Error", "Unable to open file.");
		}
		else
		{
			imagerep = [[NXBitmapImageRepSub  alloc]  initFromStream:stream];
			if (!imagerep)
				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 || NXIntersectsRect(r, &bounds))
		[imagerep  addResources:resourceDoc  forFile:filename];
	
	return self;
}

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

	return [super  free];
}

- freeTemp
{
	return [super  free];
}

- copy
{
	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;
}

- (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;

	if (pt->x || pt->y)
	{
		gflags.dirty = YES;
		col = pt_num % 3;
		switch (col)
		{
			case LEFT:
				bounds.origin.x += pt->x;	
				bounds.size.width -= pt->x;
				break;
			case RIGHT:
				bounds.size.width += pt->x;			
				break;
		}

		row = pt_num/3;
		switch (row)
		{
			case TOP:
				bounds.size.height += pt->y;
				break;
			case BOTTOM:
				bounds.origin.y += pt->y;
				bounds.size.height -= pt->y;
				break;
		}
	}

	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;
}

/*	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);
	
	return self;
}

/*
*	Returns the bounding box of the file.
*/
- getBounds:(NXRect *)aRect
{
	*aRect = bounds;

	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;

	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);

	[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];
		}
	}
	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;
	}

	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;

	NXRect	knobRect;

	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(hitRect, &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.
*/
- hitObject:(UPath *) hitUpath
{
	NXRect	hitRect;

	NXSetRect(&hitRect, hitUpath->pts[0], hitUpath->pts[1],
		hitUpath->pts[2] - hitUpath->pts[0],
			hitUpath->pts[3] - hitUpath->pts[1]);

	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
{
	NXAtom		string;

	NXSize		strSize;

	PSgsave();
	NXRectClip(r);
	PStranslate(bounds.origin.x, bounds.origin.y);
	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 = filename;
	}

	if (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((char *) 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;

	NXRect		rect;

	if (!r || NXIntersectsRect(r, &bounds))
	{
		if (flags & REDRAWFLAG)
		{
			PSgsave();
			PStranslate(bounds.origin.x, bounds.origin.y);
			PSWSetLine(1.0, NX_LTGRAY);
			PSrectstroke(0.0, 0.0, bounds.size.width, bounds.size.height);
			PSgrestore();
		}
		else if (gflags.unimageable)
		{
			[self  drawBoxforRect:r  imaging:NO];
			gflags.new = NO;
		}
		else if (NXDrawingStatus == NX_PRINTING ||
				NXDrawingStatus == NX_COPYING ||
					[view  image])
		{
			[imagerep  drawIn:&bounds  with:0];
		}
		else
		{
			useImage = YES;
	
			/*
			*	Size the cache.
			*/
			[image  getSize:&rect.size];
			if (bounds.size.width!= rect.size.width ||
				bounds.size.height!= rect.size.height)
			{
				gflags.dirty = YES;
				rect.size.width = bounds.size.width;
				rect.size.height = bounds.size.height;
				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();

				/*
				*	Draw the file into an NXImage using an NXEPSImageRep.
				*	In other words, cache the image offscreen while drawing
				*	into a separate context.
				*/
				if (useImage)
				{
					rect.origin.x = rect.origin.y = 0;
					[image  lockFocus];
					PSsetalpha(0.0);
					PSsetgray(NX_WHITE);
					NXRectFill(&rect);
					PSsetalpha(1.0);

					PStranslate(-bounds.origin.x, -bounds.origin.y);
				}

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

				gflags.error = ![imagerep  drawIn:&bounds  with:0];

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

				if (useImage)
					[image  unlockFocus];

				if (gflags.error)
				{
					gflags.unimageable = YES;
					[self  displayError];
					if (!gflags.new)
						[self  drawBoxforRect:r  imaging:NO];
				}
			}

			if (useImage && !gflags.error)
			{
				rect.origin.x = rect.origin.y = 0;
				rect.size.width = bounds.size.width;
				rect.size.height = bounds.size.height;
				[image  composite:NX_SOVER  fromRect:&rect  toPoint:&bounds.origin];
			}

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

	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.