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.