TweetFollow Us on Twitter

EPSF Files
Volume Number:9
Issue Number:4
Column Tag:C Workshop

Related Info: Quickdraw Printing Manager

Creating EPSF Files

Converting PICT files to EPSF, one of the most useful and versatile file formats on the Macintosh.

By Gary McGath, Penacook, New Hampshire

Note: Source code files accompanying article are located on MacTech CD-ROM or source code disks.

About the author

Gary McGath is an independent software consultant and developer in Penacook NH, operating under the business name Conceptual Design.

One of the most useful and versatile file formats on the Macintosh is Encapsulated PostScript Format (EPSF, or sometimes just EPS). Unlike PICT files, EPSF files are resolution-independent, and can be rotated, scaled, and otherwise modified as needed. EPSF files can describe objects (lines, arcs, Bézier curves, etc.), bitmap images, or any combination thereof. Most desktop publishing applications, regardless of processor or operating system, will accept EPSF files; a Mac application can create an EPSF application which is brought into, say, a Unix multi-user publishing system. A PostScript output device is necessary, of course, to take advantage of the format.

In spite of these benefits, comparatively few Mac applications offer the option of creating an EPSF file. The reason for this may well be that EPSF generation is tricky. Doing a full-blown job of it is a major project. This article and its accompanying code will only present the basics, and will discuss some of the more difficult issues. The code provided here does a minimal job of converting PICT files to EPSF.

The Basics

An EPSF file is simply a file of PostScript commands, of type ‘EPSF’, which follows certain conventions. These conventions are designed to allow the file to be executed by a PostScript interpreter without changing the execution environment. This means that an application can generate some PostScript, drop in an EPSF file, and then continue on its way without caring what happened in between.

The full details of creating an EPSF file are found in Adobe Systems’ Postscript® Language Reference Manual, Second Edition, Appendix H. The discussion here will be very brief.

An EPSF file has to be a good camper, cleaning up after itself. Fortunately, this is easy to do, using the save and restore operators. It must not use any operators which would create non-local changes (e.g., exitserver). It has to avoid any assumptions about how it may have been transformed by the application using it; this means that it must avoid operators such as initgraphics which defeat existing changes in the environment.

The file has to make proper use of structured comments. In PostScript, a structured comment is one which begins with two percent signs (%%) and follows certain rules. The one absolute requirement is that the file have a %%BoundingBox comment. This tells the calling application the width and height of the image created by the file.

An EPSF file has to be self-contained. It can’t rely on the definitions in the Laser Prep file; those definitions might not be present on the system which ultimately uses the file. A file which was created under System 6.x should be usable under System 7.x or DOS 5.x, and vice versa. Whatever it is going to use beyond the built-in PostScript operators, it has to define.

Because of these constraints, an EPSF file is significantly different from the PostScript file generated by “printing” to PostScript. Attractive as it might seem, a PostScript file created by the LaserWriter driver can’t easily be turned into a clean EPSF file. The application which wants to create EPSF has to do the work itself.

PostScript vs. QuickDraw

In creating PostScript, the programmer has to take into account the differences between the QuickDraw and PostScript imaging models. QuickDraw was created primarily for drawing pixels to the Macintosh screen, and secondarily for drawing to a medium-resolution output device such as the ImageWriter. PostScript was designed for high-quality typographic work.

Perhaps the most annoying difference between QuickDraw and PostScript is the way they treat the relationship between grid coordinates and pixels. The problem isn’t that QuickDraw places the origin at the top of the image and PostScript places it at the bottom; this is easily dealt with by a transformation. The tougher problem is that QuickDraw locates pixels between grid coordinates, whereas PostScript centers its objects on grid coordinates.

For instance, in QuickDraw, if you position the pen at a vertical position of 35, then draw a horizontal line one pixel thick, the line is drawn between the y-coordinates of 35 and 36. In PostScript, if you do a MoveTo the vertical position of 35, then draw a horizontal line, the line is centered on a vertical position of 35.

You may think that everything comes out in the wash; all that happens is that QuickDraw objects are uniformly shifted over and down by half a point. But it isn’t that easy. If you draw a QuickDraw box between coordinates 35 and 40, its vertical extent between outside edges is 5 points. But if you do the same by drawing horizontal lines at y-coordinates of 35 and 40 respectively (and vertical lines wherever they may go), the distance from top to bottom is 6 points.

To further annoy the PostScript programmer, QuickDraw has a basic capability which PostScript doesn’t: it can look at bits which were previously drawn and change them. For instance, it can invert all the pixels in a given area, changing black to white and white to black. PostScript is blind to the pixels which its previously drawn, so it can’t do this. This means that certain drawing modes in QuickDraw (such as invert) can’t be imitated in PostScript.

The source code provided here is directed at the conversion of PICT files to EPSF, as the simplest way to illustrate the conversion of one model to the other. This code could also serve as the basis of an EPSF generator which could, with little change, be added to any application which makes QuickDraw calls. The key is the use of the GrafPort and the “bottleneck” procedures which can be replaced to modify the actions of QuickDraw. All that’s necessary is to replace the standard bottleneck procedures with ones which generate PostScript.

Some Specific Issues

A look at the description of the PostScript operators will uncover an interesting fact: there are operators to generate circles and arcs of circles, but no operators to generate ellipses or elliptical arcs. QuickDraw, on the other hand, has elliptical arcs built into its basic capabilities. Here the trick is to use the transformation matrix; by scaling the x and y axes unequally, you can get an ellipse of any desired shape. But if you’re framing the ellipse (drawing its outline), you can’t stroke it in the scaled coordinate system, or the thickness of the outline will vary as it goes around the ellipse. You have to save the current transformation matrix, generate the path in the transformed coordinate system, restore the matrix, and then stroke the path. This is illustrated in the PostScript procedure froval (defined in the PostScript strings in DrawEPSF.c); if you understand those few lines thoroughly, you’ll have a good understanding of how to use transformations in PostScript.

Text involves special difficulties. For each font which is used in a document, it’s necessary to obtain the name of the equivalent PostScript font from the FOND resource. The code here bypasses the issue, hard-coding in the Times Roman font. Another issue which this code doesn’t address is conversion between the Macintosh and PostScript character sets. A lot of work is needed to properly put text into an EPSF file.

Patterns are even worse. Mention rendering QuickDraw patterns in EPSF files, and even the hardiest programmer may turn pale and quake. The code presented with this article simply ignores the issue, except for recognizing solid white and solid black patterns and turning them into the respective gray levels.

Many pictures consist largely or entirely of bitmapped images. These can come in several varieties, including 1-bit Bitmaps, indexed (8-bit color or gray scale) Pixmaps, and direct (16-bit or 24-bit) Pixmaps. These can be handled using the image operator. The present version is very limited, dealing only with Bitmaps. Converting color and grayscale images requires converting these values into gray-level equivalents. If you want to output images to color printers, you can use the colorimage operator; but then your images won’t print on many printers. Just to avoid getting a PostScript error on these printers, you’ll have to include PostScript code to test whether colorimage is available.

Apple has defined a number of PicComments which can be used to generate special PostScript effects or insert custom PostScript code when printing. An EPSF generator should ideally recognize all of these comments. They provide standard methods for rotating text, generating Bézier curves and dashed lines, and so on. For simplicity, the code included with this article doesn’t deal with these PicComments.

Every EPSF file created on a Mac is expected to have a PICT resource numbered 256, which represents the image for screen displays and output to non-PostScript devices. For this application, generating the PICT is really easy; it’s simply a matter of copying the original PICT.

The code which accompanies this article addresses the basic issues of creating an EPSF file, but it’s only a beginning toward a useful software product. To do the job, it needs to handle fonts rigorously, rotate and scale objects (using PicComments or some equivalent method), deal with clipping regions, handle gray levels and patterns, and process additional graphic objects such as Bézier curves. None of these problems are insurmountable, though. The routines here provide the starting point; all the rest is just working out the details.

Bibliography

Adobe Systems Incorporated, PostScript® Language Reference Manual, Second Edition. Addison-Wesley Publishing Company, 1990.

Adobe Systems Incorporated, PostScript® Language Tutorial and Cookbook. Addison-Wesley Publishing Company, 1985.

Listing: PSD.h
/* Copyright 1992, Gary D. McGath */

extern void OutputString(char *str, int theFile);
extern void OutputNum(int val, int theFile);
void OutputDouble(double val, int theFile);
void OutputHex(int ch, int theFile);
void OutputChar(int ch, int theFile);
void FlushOutBuf(int theFile);
Listing: Main.c
/* Convert PICT files to EPSF. © 1992, Gary McGath */
/* Mac Resource Defs */
#define APPLMENU 128
#define FILEMENU 180
#define WatchCursor 4
#define NIL 0L

extern void DrawEPSF(PicHandle han, int theFile);

int main (void);
void mainloop(void);
void docommand(long mensel);
void doconvert(void);
PicHandle ReadPict(int theFile);

extern int thePSResFile;

MenuHandle apmenu;
MenuHandle filemenu;
EventRecord myevent;
Handle pictHan;
WindowPtr whichwindow;
int infile;
int thePSFile;
Point gfloc = {40,40};
/* list of acceptable file types */
SFTypeList typelist = {‘PICT’};

main()
{
 InitGraf((Ptr)&thePort);
 InitFonts();
 InitWindows();
 InitDialogs(0L);
 InitMenus();
 InitCursor();
 apmenu = GetMenu(APPLMENU);
 AddResMenu(apmenu,’DRVR’);
 InsertMenu(apmenu,0);
 filemenu = GetMenu(FILEMENU);
 InsertMenu(filemenu,0);
 DrawMenuBar();
 mainloop();
}

void mainloop()
{
 int code;
 int ch;
 for (;;) {
 WaitNextEvent(everyEvent,&myevent,2L,NIL);
 switch (myevent.what) {
 case keyDown:
 ch = myevent.message & charCodeMask;
 if (myevent.modifiers & cmdKey)
 docommand(MenuKey(ch));
 break;
      case mouseDown:
 code =
  FindWindow(myevent.where,&whichwindow);
 switch (code) {
      case inMenuBar:
 docommand(MenuSelect(myevent.where));
 break;
 case inSysWindow:
 SystemClick(&myevent,whichwindow);
 break;
 }
 break;
 }
 }
}

void docommand(long mensel)
{
 register short men, item;
 char accname[64]; 
 GrafPtr saveport;
 men = mensel >> 16;
 item = (short) mensel;
 if (men == 0)
 return;
 if (men == APPLMENU) {
 if (item <= 2)  /* no “about” */
 SysBeep(1);
     else {
      GetItem(apmenu,item,(StringPtr)accname);
 GetPort(&saveport);
 OpenDeskAcc((StringPtr)accname);
 SetPort(saveport);
 }
 }
 else switch(item) {     /*  File */
 case 1:
 doconvert();
 break;
 case 3 :
 default:
 ExitToShell();
 }
 HiliteMenu(0);       /* clean up display */
}

/* Command handler to convert PICT to EPSF. */
void doconvert()
{
 SFReply myreply;
 unsigned char *pt;
 OSErr errcod;
 PicHandle picHan;
 /* specify and open input (PICT) file */
 SFGetFile(gfloc,0L,0L,1,&typelist,0L,&myreply);
 if (!myreply.good)
 return;/* cancelled out */
 errcod = FSOpen
 (&myreply.fName[0],myreply.vRefNum,&infile);
 if (errcod != noErr)
 return;
 /* specify output file */
 for (pt = &myreply.fName[1]; pt < &myreply.fName[32];)
 *pt++ = 0; /* clear out name buffer */
 SFPutFile(gfloc,
 (StringPtr)”\pName of output file:”,
 (StringPtr)”\pEPSF File”,0L,&myreply);
 if (!myreply.good) {
 FSClose(infile);
 return;/* cancelled out */
 }
 SetCursor(*GetCursor(WatchCursor));
 errcod = CreateEPSFFile(&myreply, &thePSFile);
 if (errcod == noErr) {
 picHan = ReadPict(infile);
 if (picHan == 0)
 goto done;
 AddResource(picHan,’PICT’,256,”\p”);
 DrawEPSF(picHan, thePSFile); }
done:
 if (infile)
 FSClose(infile);
 if (thePSResFile)
 CloseResFile(thePSResFile);
 if (thePSFile)
 FSClose(thePSFile);
 FlushVol(0,0);
 InitCursor();
}

/* Read the PICT into a PicHandle */
PicHandle ReadPict(int theFile)
{
 long eofPos;
 long count;
 Handle theHan;
 SetFPos(theFile, fsFromStart, 512L);/* skip PICT header */
 GetEOF(theFile, &eofPos);/* get file length */
 count = eofPos - 512;
 theHan = NewHandle(count); 
 if (MemError())
 return 0;
 HLock(theHan);
 FSRead(theFile, &count, *theHan);
 HUnlock(theHan);
 return ((PicHandle) theHan);
}
listing:  DrawEPSF.c
/* Copyright 1992, Gary D. McGath */

extern void InitOutBuf(void);
extern void OutputString(char *str, int theFile);
extern void OutputChar(int ch,int theFile);
extern void OutputNum(int n, int theFile);
extern void FlushOutBuf(int theFile);

OSErr CreateEPSFFile(SFReply *theReply, int *theFile);
void DrawEPSF(PicHandle han, int theFile);
void WriteBoundingBox
 (PicHandle han,Rect *bbRect, int theFile);
void WritePrologue(int theFile);
void FlipCoords(Rect *RectP, int theFile);
void WriteEpilogue(int theFile);

/* The strings of the prologue. */
char *prologueStrs[] = {
“/_E_save save def\r”,
“/our_dict 70 dict def\r”,
“our_dict begin /bd {bind def} def  /xd {exch def} bd\r”,
“/rdims {/bt xd /rt xd /tp xd /lf xd} bd\r”,
“/fradj {/tp tp phw add def /bt bt phw sub def\r”,
“  /lf lf phw add def /rt rt phw sub def} bd\r”,
“/arctopppp {arcto pop pop pop pop} bd\r”,
 /* fill rect proc: l t r b flrec */
“/flrec {rdims lf tp moveto rt tp lineto rt bt lineto lf bt lineto closepath 
fill} bd\r”,
 /* frame rect proc: l t r b pen.h pen.v frrec */
“/frrec {/pnh xd /pnw xd rdims\r”,
“  lf tp moveto rt tp lineto rt bt lineto lf bt lineto closepath\r”,
“  /lf lf pnw add def /tp tp pnh add def /rt rt pnw sub def /bt bt pnh 
sub def\r”,
“  lf tp moveto rt tp lineto rt bt lineto lf bt lineto closepath\r”,
“ eofill} bd\r”,
 /* fill oval proc: l t r b floval */
“/floval {rdims gsave lf rt add 2 div tp bt add 2 div translate\r”,
“ rt lf sub 2 div bt tp sub 2 div scale\r”,
“ 1 0 moveto 0 0 1 0 360 arc fill grestore} bd\r”,
 /* frame oval proc: l t r b pen.h froval */
“/froval {dup /pnw xd setlinewidth”,
“/phw pnw 0.5 mul def fradj”,
“  rdims /mt matrix currentmatrix def lf rt add 2 div tp bt add 2 div 
translate\r”,
“  rt lf sub 2 div bt tp sub 2 div scale\r”,
“  1 0 moveto 0 0 1 0 360 arc mt setmatrix stroke} bd\r”,
 /* frame arc proc: l t r b pen.h startangle arcangle frarc */
“/frarc {/arca xd /stra xd setlinewidth rdims\r”,
“  /mt matrix currentmatrix def lf rt add 2 div tp bt add 2 div translate\r”,
“  rt lf sub 2 div bt tp sub 2 div scale\r”,
“  0 0 1 stra 90 sub dup arca add arc mt setmatrix stroke} bd\r”,
“/flarc {/arca xd /stra xd rdims\r”,
“  gsave lf rt add 2 div tp bt add 2 div translate\r”,
“  rt lf sub 2 div bt tp sub 2 div scale\r”,
“  0 0 moveto 0 0 1 stra 90 sub dup arca add arc fill grestore} bd\r”,
 /* frame round rect proc: l t r b pen.h rad.h
    This version makes the simplifying assumptions of a square pen and
    circular (not elliptical) corners. */
“/frrrect {/rd xd /pnw xd rdims /phw pnw 0.5 mul def fradj\r”,
“  rt rd sub tp moveto rt tp rt tp rd add rd arctopppp\r”,
“  rt bt rt rd sub bt rd arctopppp\r”,
“  lf bt lf bt rd sub rd arctopppp\r”,
“  lf tp lf rd add tp rd arctopppp\r closepath stroke} bd\r”,
 /* fill round rect: l t r b rad.h */
“/flrrect {/rd xd rdims\r”,
“  rt rd sub tp moveto rt tp rt tp rd add rd arctopppp\r”,
“  rt bt rt rd sub bt rd arctopppp\r”,
“  lf bt lf bt rd sub rd arctopppp\r”,
“  lf tp lf rd add tp rd arctopppp\r closepath fill} bd\r”,
“”           /* last string must be null */
};

char *epilogueStrs[] = {
“end _E_save restore\r”,
“”
};

int thePSResFile;

/* Call this before calling DrawEPSF. */
OSErr CreateEPSFFile
 (SFReply *theReply, int *theFile)
 {
 OSErr err;
 FSDelete
 (&theReply->fName[0],theReply->vRefNum);
 err = Create(&theReply->fName[0], 
 theReply->vRefNum, ‘????’, ‘EPSF’);
 err = FSOpen(&theReply->fName[0], 
 theReply->vRefNum, theFile);
 SetVol((StringPtr) 0, theReply->vRefNum);
 CreateResFile(&theReply->fName[0]);
 thePSResFile = OpenResFile(&theReply->fName[0]);
 if (err != noErr)
 *theFile = 0;
 return err;
 }

/* The main routine for writing the data 
   fork of the EPSF file. */
void DrawEPSF(PicHandle han, int theFile)
 {
 GrafPtr savePort;
 GrafPtr dstPort;
 Rect dstRect;
 Rect *picRectPtr;
 InitOutBuf();
 OutputString
 (“%!PS-Adobe-3.0 EPSF-3.0\r”, theFile);
 picRectPtr = &(**han).picFrame;
 dstRect.left = 0;
 dstRect.top = 0;
 dstRect.right = picRectPtr->right;
 dstRect.bottom = picRectPtr->bottom;
 WriteBoundingBox(han, &dstRect,theFile);
 WritePrologue(theFile);
 FlipCoords(&dstRect, theFile);
 GetPort(&savePort);
 dstPort = (GrafPtr) NewPtr(sizeof(GrafPort));
 if (dstPort == 0)
 return;
 OpenPort(dstPort);
 psdInitPort(dstPort);
 psdSetupProcs(dstPort);
 SetPort((GrafPtr)dstPort);
 DrawPicture(han,picRectPtr);
 WriteEpilogue(theFile);
 FlushOutBuf(theFile);
 SetPort(savePort);
 ClosePort(dstPort);
 DisposPtr(dstPort);
 }

/* Get the dimensions of the PICT, and 
 write them as a BoundingBox comment */
void WriteBoundingBox
 (PicHandle han, Rect *RectP, int theFile)
{
 OutputString(“%%BoundingBox: 0 0 “, theFile);
 OutputNum(RectP->right, theFile);
 OutputNum(RectP->bottom, theFile);
 OutputChar(‘\r’,theFile);
}

/* Write out the prologue code of the file. */
void WritePrologue(int theFile)
{
 register int i;
 for (i=0;; i++) {
 if (prologueStrs[i][0] == 0)
 break;
 OutputString(prologueStrs[i], theFile);
 } 
}

/* Flip the coordinate system so it matches 
   the QuickDraw coordinates */
void FlipCoords(Rect *RectP, int theFile)
{
 OutputString(“1 -1 scale “, theFile);
 OutputNum(0, theFile);
 OutputNum(-RectP->bottom, theFile);
 OutputString(“translate “, theFile);
}

void WriteEpilogue(int theFile)
{
 register int i;
 for (i=0;; i++) {
 if (epilogueStrs[i][0] == 0)
 break;
 OutputString(epilogueStrs[i], theFile);
 } 
}
listing:  PSDSetup.c
/* Copyright 1992, Gary D. McGath */

extern pascal void psdRectProc
 (GrafVerb verb, Rect *theRect);
extern pascal psdTextProc
 (short byteCount, Ptr textBuf, 
 Point numer, Point denom);
extern pascal psdLineProc(Point newPt);
extern pascal psdOvalProc
 (GrafVerb verb, Rect *theRect);
extern pascal psdrRectProc
 (GrafVerb verb, Rect *theRect, 
 short ovalwidth, short ovalheight);
extern pascal psdArcProc
 (GrafVerb verb, Rect *theRect, 
 short startAngle, short arcAngle);
extern pascal psdPolyProc
 (GrafVerb verb, PolyHandle thePoly);
extern pascal psdBitsProc
 (BitMap *srcBits, Rect *srcRect, 
 Rect *dstRect, short mode, RgnHandle maskRgn);

void psdSetupProcs(GrafPtr itsPort);
pascal psdCommentProc
 (short kind, short dataSize, Handle dataHandle);
pascal psdRgnProc
 (GrafVerb verb, RgnHandle theRgn);

/* Create a CGrafPort to draw to, and set up 
   the bottleneck procedures */
void psdSetupProcs(GrafPtr itsPort)
{
 QDProcsPtr psdProcs;
 psdProcs = (QDProcsPtr) NewPtr(sizeof(QDProcs));
 itsPort->grafProcs = psdProcs;
 SetStdProcs(psdProcs);
 psdProcs->textProc = (Ptr) psdTextProc;
 psdProcs->lineProc = (Ptr) psdLineProc;
 psdProcs->rectProc = (Ptr) psdRectProc;
 psdProcs->rRectProc = (Ptr) psdrRectProc;
 psdProcs->ovalProc = (Ptr) psdOvalProc;
 psdProcs->arcProc = (Ptr) psdArcProc;
 psdProcs->polyProc = (Ptr) psdPolyProc;
 psdProcs->rgnProc = (Ptr) psdRgnProc;
 psdProcs->bitsProc = (Ptr) psdBitsProc;
 psdProcs->commentProc = (Ptr) psdCommentProc;
}

/* PicComments aren’t handled in this program */
pascal psdCommentProc
 (short kind, short dataSize, Handle dataHandle)
{
}

/* Neither are regions */
pascal psdRgnProc(GrafVerb verb, RgnHandle theRgn) 
{
}
listing:  PSDGeomProcs.c
/* Here are the Quickdraw plug-in procs which handle the 
   comparatively simple geometric stuff */
/* Copyright 1992, Gary D. McGath */

#include “psd.h”

extern int thePSFile;

pascal void psdRectProc
 (GrafVerb verb, Rect *theRect);
pascal psdOvalProc(GrafVerb verb, Rect *theRect);
pascal psdrRectProc
 (GrafVerb verb, Rect *theRect, 
 short ovalwidth, short ovalheight);
pascal psdLineProc(Point newPt);
pascal psdPolyProc
 (GrafVerb verb, PolyHandle thePoly);
pascal psdArcProc
 (GrafVerb verb, Rect *theRect, 
 short startAngle, short arcAngle);
void psdDrawLine(Point oldPt, Point newPt);
void psdWriteRect(Rect *theRect);
void psdDrawLine(Point oldPt, Point newPt);

/* psdRectProc - one of the easiest (ha!) */
pascal void psdRectProc
 (GrafVerb verb, Rect *theRect)
{
 if (thePort->pnVis < 0)
 return;/* nothing to draw */
 switch(verb) {
 case frame:
 if (thePort->pnSize.h == 0 &&
  thePort->pnSize.v == 0)
 return;
 psdSetPenPat(); 
 psdWriteRect(theRect);
 OutputNum(thePort->pnSize.h,thePSFile);     
 OutputNum(thePort->pnSize.v,thePSFile);     
 OutputString(“frrec\r”,thePSFile);
 break;
 case fill:
 case paint:
 case erase:
 if (verb == paint)
 psdSetPenPat(); 
 else if (verb == erase)
 OutputGray(0);
 else psdSetFillPat();
 psdWriteRect(theRect);
 OutputString(“flrec\r”,thePSFile);
 break;
 case invert:
 break;      /* invert isn’t supported */
 }
}

/* psdOvalProc - draw an oval.  PostScript 
   knows about circles but not
   ellipses, so we have to scale the circular path
   (but *not* the stroking
   of it) to an ellipse.  For the present, we
   assume that the pen is square (equal 
   in horizontal and vertical dimensions). */
pascal psdOvalProc(GrafVerb verb, Rect *theRect)
{
 if (thePort->pnVis < 0)
 return;      /* nothing to draw */
 switch(verb) {
 case frame:
 if (thePort->pnSize.h == 0 && 
 thePort->pnSize.v == 0)
 return;
 psdSetPenPat(); 
 psdWriteRect(theRect);
 OutputNum(thePort->pnSize.h,thePSFile);     
 OutputString(“froval\r”,thePSFile);
 break;
 case fill:
 case paint:
 case erase:
 if (verb == paint)
 psdSetPenPat(); 
 else if (verb == erase)
 OutputGray(0);
 else psdSetFillPat();    
 psdWriteRect(theRect);
 OutputString(“floval\r”,thePSFile);
 break;
 case invert:
 break;      /* invert isn’t supported */
 }
}

/* psdRRectProc - draw a round-cornered
   rectangle. In this case, we make
   two simplifying assumptions: that the pen is
   square and that the corners
   are circular (width = height). */
pascal psdrRectProc
 (GrafVerb verb, Rect *theRect, short ovalwidth, 
 short ovalheight)
{
 if (thePort->pnVis < 0)
 return;/* nothing to draw */
 switch(verb) {
 case frame:
 psdSetPenPat(); 
 psdWriteRect(theRect);
 OutputNum(thePort->pnSize.h,thePSFile);
 OutputDouble(ovalwidth/2.0,thePSFile);
 OutputString(“frrrect\r”,thePSFile);
 break;
 case fill:
 case paint:
 case erase:
 if (verb == paint)
 psdSetPenPat(); 
 else if (verb == erase)
 OutputGray(0);
 else psdSetFillPat();    
 psdWriteRect(theRect);
 OutputDouble(ovalwidth/2.0,thePSFile);
 OutputString(“flrrect\r”,thePSFile);
 break;
 case invert:
 break;      /* invert isn’t supported */
 }
}

/* psdLineProc - draw a line from the current
   point to the new point. “Lines” in Quickdraw
   are actually funny-shaped polygons. */
pascal psdLineProc(Point newPt)
{
 Point oldPt;
 oldPt = thePort->pnLoc;
 thePort->pnLoc = newPt;
 if (thePort->pnVis < 0 ||
  (thePort->pnSize.h == 0 &&
 thePort->pnSize.v == 0)) {
 return;      /* nothing to draw */
 }
 psdSetPenPat(); 
 psdDrawLine(oldPt, newPt);
}

pascal psdPolyProc
 (GrafVerb verb, PolyHandle thePoly)
{
 int npoints;
 register int i;
 PolyPtr thePolyp;
 Point prevPoint;
 HLock(thePoly);
 thePolyp = *thePoly;
 npoints = (thePolyp->polySize - 10) / 4;
 switch(verb) {
 case frame:
 if (thePort->pnSize.h == 0 && 
 thePort->pnSize.v == 0)
 return;
 psdSetPenPat(); 
 prevPoint = thePolyp->polyPoints[0];
 for (i = 1; i < npoints; i++) {
 psdDrawLine(prevPoint,
 thePolyp->polyPoints[i]);
 prevPoint = thePolyp->polyPoints[i];
 }
 break;
 case fill:
 case paint:
 case erase:
 if (verb == fill)
 psdSetFillPat();
 else if (verb == erase)
 OutputGray(0);
 else psdSetPenPat();
 prevPoint = thePolyp->polyPoints[0];
 OutputNum(prevPoint.h, thePSFile);
 OutputNum(prevPoint.v, thePSFile);
 OutputString(“moveto “, thePSFile);
 for (i = 1; i < npoints; i++) {
 prevPoint = thePolyp->polyPoints[i];
 OutputNum(prevPoint.h, thePSFile);
 OutputNum(prevPoint.v, thePSFile);
 OutputString(“lineto “, thePSFile);
 }
 OutputString(“closepath fill\r”, thePSFile);
 break;
 case invert:
 break;       /* invert isn’t supported */
 }
 HUnlock(thePoly);
}

pascal psdArcProc
 (GrafVerb verb, Rect *theRect,
 short startAngle, short arcAngle)
{
 if (thePort->pnVis < 0)
 return;         /* nothing to draw */
 if (arcAngle < 0) { /* make angle always positive */
 startAngle += arcAngle;
 arcAngle = -arcAngle;
 }
 switch(verb) {
 case frame:
 if (thePort->pnSize.h == 0 &&
 thePort->pnSize.v == 0)
 return;
 psdSetPenPat();
 psdWriteRect(theRect);
 OutputNum(thePort->pnSize.h,thePSFile);
 OutputNum(startAngle,thePSFile);
 OutputNum(arcAngle,thePSFile);
 OutputString(“frarc\r”,thePSFile);
 break;
 case fill:
 case paint:
 case erase:
 if (verb == paint)
 psdSetPenPat();
 else if (verb == erase)
 OutputGray(0);
 else psdSetFillPat();
 psdWriteRect(theRect);
 OutputDouble(startAngle,thePSFile);
 OutputDouble(arcAngle,thePSFile);
 OutputString(“flarc\r”,thePSFile);
 break;
 case invert:
 break;        /* invert isn’t supported */
 }
}

/* psdWriteRect - output the coordinates 
   of a rectangle. */
void psdWriteRect(Rect *theRect)
{
 OutputNum(theRect->left,thePSFile);
 OutputNum(theRect->top,thePSFile);
 OutputNum(theRect->right,thePSFile);
 OutputNum(theRect->bottom,thePSFile);
}

void psdDrawLine(Point oldPt, Point newPt)
{
 int hdelta, vdelta;
 Point startcorner, endcorner;
 if (newPt.h > oldPt.h) {
 hdelta = thePort->pnSize.h;
 startcorner.h = oldPt.h;
 endcorner.h = newPt.h + hdelta;
 if (newPt.v > oldPt.v) {
 vdelta = thePort->pnSize.v;
 startcorner.v = oldPt.v;
 endcorner.v = newPt.v + vdelta;
 }
 else {     /* newPt.v <= oldPt.v */
 vdelta = -thePort->pnSize.v;
 startcorner.v = oldPt.v - vdelta;
 endcorner.v = newPt.v;
 }
 }
 else {     /* newPt.h <= oldPt.h */
 hdelta = -thePort->pnSize.h;
 startcorner.h = oldPt.h - hdelta;
 endcorner.h = newPt.h;
 if (newPt.v > oldPt.v) {
 vdelta = thePort->pnSize.v;
 startcorner.v = oldPt.v;
 endcorner.v = newPt.v + vdelta;
 }
 else {     /* newPt.v <= oldPt.v */
 hdelta = -thePort->pnSize.h;
 vdelta = -thePort->pnSize.v;
 startcorner.v = oldPt.v - vdelta;
 endcorner.v = newPt.v;
 }
 }
 OutputNum(startcorner.h,thePSFile);
 OutputNum(startcorner.v,thePSFile);
 OutputString(“moveto “,thePSFile);
 OutputNum(hdelta,thePSFile);
 OutputNum(0,thePSFile);
 OutputString(“rlineto “,thePSFile);
 OutputNum(endcorner.h,thePSFile);
 OutputNum(endcorner.v - vdelta,thePSFile);
 OutputString(“lineto “,thePSFile);
 OutputNum(0,thePSFile);
 OutputNum(vdelta,thePSFile);
 OutputString(“rlineto “,thePSFile);
 OutputNum(-hdelta,thePSFile);
 OutputNum(0,thePSFile);
 OutputString(“rlineto “,thePSFile);
 OutputNum(startcorner.h,thePSFile);
 OutputNum(startcorner.v+vdelta,thePSFile);
 OutputString
 (“lineto closepath fill\r”,thePSFile);
}
listing:  PSDtext.c
/* This module contains routines for bitmap operations */
/* Copyright 1992, Gary D. McGath */

#include “psd.h”

pascal psdTextProc
 (short byteCount, Ptr textBuf,
 Point numer, Point denom);
void SelectFont(void);

extern int thePSFile;
extern int gblFont;
extern int gblSize;

pascal psdTextProc
 (short byteCount, Ptr textBuf, 
 Point numer, Point denom) 
{
 register int i;
 SelectFont();   
 OutputString(“/SV save def “,thePSFile);
 OutputNum(thePort->pnLoc.h,thePSFile);
 OutputNum(thePort->pnLoc.v,thePSFile);
 OutputString(“translate 1 -1 scale “,thePSFile);
 OutputString(“0 0 moveto\r”,thePSFile);
 /* The following code will fail in various
 cases, most notably unbalanced
 parentheses within the string. */
 OutputChar(‘(‘, thePSFile);
 for (i = 0; i < byteCount; i++)
 OutputChar(textBuf[i],thePSFile);
 OutputString(“) show SV restore\r”, thePSFile);
}
void SelectFont()
{
 if (thePort->txFont != gblFont ||
 thePort->txSize != gblSize) {
 
 /* “Real” code needs to look up the 
 PostScript font name */
 OutputString
 (“/Times-Roman findfont “,thePSFile);
 OutputNum(thePort->txSize, thePSFile);
 OutputString(“scalefont setfont\r”,thePSFile);
 gblFont = thePort->txFont;
 gblSize = thePort->txSize;
 }
}
listing:  PSDbits.c
/* This module contains routines for bitmap operations */
/* Copyright 1992, Gary D. McGath */

pascal void psdBitsProc
 (BitMap *srcBits, Rect *srcRect, 
 Rect *dstRect,
 short mode, RgnHandle maskRgn);

extern int thePSFile;

/* In this version, the only mode supported 
   is copy. Most others
   are problematical, since PostScript doesn’t
   allow logical combinations
   with an existing raster.
   We support only 1-bit bitmaps here; Pixmaps 
   are ignored (left as an exercise for 
   the reader). */
pascal void psdBitsProc
 (BitMap *srcBits, Rect *srcRect, Rect *dstRect,
 short mode, RgnHandle maskRgn)
{
 int pixelWidth, pixelDepth;
 Ptr dataPtr;
 int byteWidth;
 register int i, j;
 
 if (srcBits->rowBytes & 0X8000)
 return;     /* can’t handle pixmap */
 if (srcRect->left != srcBits->bounds.left)
 return;     /* bit shift not implemented */
 pixelWidth = srcRect->right - srcRect->left;
 pixelDepth = srcRect->bottom - srcRect->top;
 byteWidth = (pixelWidth + 7) / 8;
/* avoid accumulating garbage */
 OutputString(“/SV save def /ims “,thePSFile);
 OutputNum(byteWidth,thePSFile);
 OutputString(“string def\r”,thePSFile);           OutputNum(dstRect->left, 
thePSFile);
 OutputNum(dstRect->top, thePSFile);
 OutputString(“translate\r”,thePSFile);
 
 OutputNum
 (dstRect->right - dstRect->left, thePSFile);
 OutputNum
 (dstRect->bottom - dstRect->top, thePSFile);
 OutputString(“scale\r”,thePSFile);
 OutputNum(pixelWidth, thePSFile);
 OutputNum(pixelDepth, thePSFile);
 OutputString(“1 [“,thePSFile);
 OutputNum(pixelWidth, thePSFile);   /* width */
 OutputString(“0 0 “, thePSFile);
 OutputNum(pixelDepth, thePSFile);
 OutputString
 (“0 0 ] {currentfile ims readhexstring pop}
 image\r”,thePSFile);

 /* Now output the image as hex data. */
 dataPtr = srcBits->baseAddr;
 for (i = 0; i < pixelDepth; i++) {
 for (j = 0; j < byteWidth; j++) {
 OutputHex(~*dataPtr++,thePSFile);
 if ((j & 0X7F) == 0X7F)
 OutputChar(‘\r’, thePSFile);
 }
 if (byteWidth & 1)
 dataPtr++;
 OutputChar(‘\r’, thePSFile);
 }
 OutputString(“SV restore\r”,thePSFile);
}
listing:  PSD Port.c
/* Copyright 1992, Gary D. McGath */
/* Calls for monitoring/handling changes in the GrafPort */
 
extern void OutputString(char *str, int theFile);

void psdSetPenPat(void);
void psdSetFillPat(void);
void OutputGray(double lev);
static void psdSetPat(unsigned char *p1);

extern int thePSFile;
extern Rect gPSClipRect;

double gblGray;   /* last gray level output */
int gblFont;
int gblSize;

/* Set up defaults for the port. */
void psdInitPort(GrafPtr itsPort)
{
 itsPort->pnSize.h = 1;
 itsPort->pnSize.v = 1;
 gblGray = -1;
 gblFont = -1;
 gblSize = -1; 
}

/* Output PostScript code to match the new pattern. “Real” 
   code should set up a pattern fill; this one sets the 
   color to black, white, or 50% gray depending on the 
   pattern. */
void psdSetPenPat()
{
 psdSetPat((unsigned char *)&thePort->pnPat);
}

void psdSetFillPat()
{
 psdSetPat((unsigned char *)&thePort->fillPat);
}

static void psdSetPat(unsigned char *p1)
{
 int allWhite = 1;
 int allBlack = 1;
 register int i;
 for (i = 1; i < 8; i++) {
 if (*p1 != 0XFF)
 allBlack = 0;
 if (*p1++ != 0)
 allWhite = 0;
 }
 if (allWhite)
 OutputGray(1.0);
 else if (allBlack)
 OutputGray(0.0);
 else OutputGray(0.5);
}

/* output a gray level, only if it’s different
   from the previous one */
void OutputGray(double lev)
{
 if (lev != gblGray) {
 OutputDouble(lev,thePSFile);
 OutputString(“setgray\r”,thePSFile);
 gblGray = lev;
 }
}
listing:  WriteEPSF.c
/* Low level routines for buffering output */
/* Copyright 1992, Gary D. McGath */

#define BUFFSIZE 512

void InitOutBuf(void);
void OutputString(char *str, int theFile);
void OutputChar(int ch,int theFile);
void OutputNum(int val, int theFile);
void FlushOutBuf(int theFile);
static void OutputHexNibble(int nib, int theFile);

static char buff[BUFFSIZE]; 
static int buffoff;

/* This must be called first */
void InitOutBuf()
{
 buffoff = 0;    /* make empty buffer */
}

/* Write a C string to the file */
void OutputString(char *str, int theFile)
{
 while (*str)
 OutputChar(*str++,theFile);
}

/* write # to the file, in decimal, with a trailing space */
  void OutputNum(int val, int theFile)
{
 int dvsr = 10000;
 int qtnt;
 int leadflag = 0;
 if (val < 0) {
 val = -val;
 OutputChar(‘-’,theFile); 
 }
 while (dvsr > 0) {
 if (dvsr == 1)
 leadflag = 1;   
 qtnt = val/dvsr;
 if (qtnt == 0) {
 if (leadflag) 
 OutputChar(‘0’,theFile);
 }
 else {
 OutputChar(qtnt + ‘0’, theFile);
 leadflag = 1;
 }
 val = val - qtnt * dvsr; 
 dvsr /= 10;
 }
 OutputChar(‘ ‘, theFile);
}

/* OutputDouble - similar in concept to OutputNum. Doesn’t 
   handle huge or tiny numbers reasonably. */
void OutputDouble(double val, int theFile)
{
 long ival;
 long dvsr = 1000000;
 int qtnt;
 int leadflag = 0; 
/* special case for zero or almost */
 if (val < 0.001 && val > -0.001) {
 OutputNum(0, theFile);
 return;
 }
 ival = val * 1000;
 if (ival < 0) {
 ival = -ival;
 OutputChar(‘-’,theFile); 
 }
 while (dvsr > 0) {
 if (dvsr == 100) {
 OutputChar (‘.’, theFile); 
 leadflag = 1;   
 }
 qtnt = ival/dvsr;
 if (qtnt == 0) {
 if (leadflag)   
 OutputChar(‘0’,theFile);
 }
 else {
 OutputChar(qtnt + ‘0’, theFile);  
 leadflag = 1;
 }
 ival = ival - qtnt * dvsr;
 dvsr /= 10;
 if (ival == 0 && dvsr <= 100)
 break;
 }
 OutputChar(‘ ‘, theFile);
}

void OutputHex(int ch, int theFile)
{
 OutputHexNibble(ch >> 4, theFile);
 OutputHexNibble(ch, theFile);
}

static void OutputHexNibble(int nib, int theFile)
{
 nib &= 0XF;
 if (nib > 9)    /* A-F */
 OutputChar(nib - 10 + ‘A’, theFile);
 else OutputChar(nib + ‘0’, theFile);
}

/* Write one character to the file */
void OutputChar(int ch, int theFile)
{
 long count = BUFFSIZE;
 buff[buffoff++] = ch;
 if (buffoff >= BUFFSIZE) {
 FSWrite(theFile,&count, buff);
 buffoff = 0;
 }
}

/* Write out whatever is left in the buffer. This must be 
   called before closing. */
void FlushOutBuf(int theFile)
{
 long count;
 count = buffoff;
 FSWrite(theFile,&count, buff);
}

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Latest Forum Discussions

See All

Recruit two powerful-sounding students t...
I am a fan of anime, and I hear about a lot that comes through, but one that escaped my attention until now is A Certain Scientific Railgun T, and that name is very enticing. If it's new to you too, then players of Blue Archive can get a hands-on... | Read more »
Top Hat Studios unveils a new gameplay t...
There are a lot of big games coming that you might be excited about, but one of those I am most interested in is Athenian Rhapsody because it looks delightfully silly. The developers behind this project, the rather fancy-sounding Top Hat Studios,... | Read more »
Bound through time on the hunt for sneak...
Have you ever sat down and wondered what would happen if Dr Who and Sherlock Holmes went on an adventure? Well, besides probably being the best mash-up of English fiction, you'd get the Hidden Through Time series, and now Rogueside has announced... | Read more »
The secrets of Penacony might soon come...
Version 2.2 of Honkai: Star Rail is on the horizon and brings the culmination of the Penacony adventure after quite the escalation in the latest story quests. To help you through this new expansion is the introduction of two powerful new... | Read more »
The Legend of Heroes: Trails of Cold Ste...
I adore game series that have connecting lore and stories, which of course means the Legend of Heroes is very dear to me, Trails lore has been building for two decades. Excitedly, the next stage is upon us as Userjoy has announced the upcoming... | Read more »
Go from lowly lizard to wicked Wyvern in...
Do you like questing, and do you like dragons? If not then boy is this not the announcement for you, as Loongcheer Game has unveiled Quest Dragon: Idle Mobile Game. Yes, it is amazing Square Enix hasn’t sued them for copyright infringement, but... | Read more »
Aether Gazer unveils Chapter 16 of its m...
After a bit of maintenance, Aether Gazer has released Chapter 16 of its main storyline, titled Night Parade of the Beasts. This big update brings a new character, a special outfit, some special limited-time events, and, of course, an engaging... | Read more »
Challenge those pesky wyverns to a dance...
After recently having you do battle against your foes by wildly flailing Hello Kitty and friends at them, GungHo Online has whipped out another surprising collaboration for Puzzle & Dragons. It is now time to beat your opponents by cha-cha... | Read more »
Pack a magnifying glass and practice you...
Somehow it has already been a year since Torchlight: Infinite launched, and XD Games is celebrating by blending in what sounds like a truly fantastic new update. Fans of Cthulhu rejoice, as Whispering Mist brings some horror elements, and tests... | Read more »
Summon your guild and prepare for war in...
Netmarble is making some pretty big moves with their latest update for Seven Knights Idle Adventure, with a bunch of interesting additions. Two new heroes enter the battle, there are events and bosses abound, and perhaps most interesting, a huge... | Read more »

Price Scanner via MacPrices.net

May 2024 Apple Education discounts on MacBook...
If you’re a student, teacher, or staff member at any educational institution, you can use your .edu email address when ordering at Apple Education to take up to $300 off the purchase of a new MacBook... Read more
Clearance 16-inch M2 Pro MacBook Pros in stoc...
Apple has clearance 16″ M2 Pro MacBook Pros available in their Certified Refurbished store starting at $2049 and ranging up to $450 off original MSRP. Each model features a new outer case, shipping... Read more
Save $300 at Apple on 14-inch M3 MacBook Pros...
Apple has 14″ M3 MacBook Pros with 16GB of RAM, Certified Refurbished, available for $270-$300 off MSRP. Each model features a new outer case, shipping is free, and an Apple 1-year warranty is... Read more
Apple continues to offer 14-inch M3 MacBook P...
Apple has 14″ M3 MacBook Pros, Certified Refurbished, available starting at only $1359 and ranging up to $270 off MSRP. Each model features a new outer case, shipping is free, and an Apple 1-year... Read more
Apple AirPods Pro with USB-C return to all-ti...
Amazon has Apple’s AirPods Pro with USB-C in stock and on sale for $179.99 including free shipping. Their price is $70 (28%) off MSRP, and it’s currently the lowest price available for new AirPods... Read more
Apple Magic Keyboards for iPads are on sale f...
Amazon has Apple Magic Keyboards for iPads on sale today for up to $70 off MSRP, shipping included: – Magic Keyboard for 10th-generation Apple iPad: $199, save $50 – Magic Keyboard for 11″ iPad Pro/... Read more
Apple’s 13-inch M2 MacBook Airs return to rec...
Apple retailers have 13″ MacBook Airs with M2 CPUs in stock and on sale this weekend starting at only $849 in Space Gray, Silver, Starlight, and Midnight colors. These are the lowest prices currently... Read more
Best Buy is clearing out iPad Airs for up to...
In advance of next week’s probably release of new and updated iPad Airs, Best Buy has 10.9″ M1 WiFi iPad Airs on record-low sale prices for up to $200 off Apple’s MSRP, starting at $399. Sale prices... Read more
Every version of Apple Pencil is on sale toda...
Best Buy has all Apple Pencils on sale today for $79, ranging up to 39% off MSRP for some models. Sale prices for online orders only, in-store prices may vary. Order online and choose free shipping... Read more
Sunday Sale: Apple Studio Display with Standa...
Amazon has the standard-glass Apple Studio Display on sale for $300 off MSRP for a limited time. Shipping is free: – Studio Display (Standard glass): $1299.97 $300 off MSRP For the latest prices and... Read more

Jobs Board

Liquor Stock Clerk - S. *Apple* St. - Idaho...
Liquor Stock Clerk - S. Apple St. Boise Posting Begin Date: 2023/10/10 Posting End Date: 2024/10/14 Category: Retail Sub Category: Customer Service Work Type: Part Read more
*Apple* App Developer - Datrose (United Stat...
…year experiencein programming and have computer knowledge with SWIFT. Job Responsibilites: Apple App Developer is expected to support essential tasks for the RxASL Read more
Omnichannel Associate - *Apple* Blossom Mal...
Omnichannel Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Operations Associate - *Apple* Blossom Mall...
Operations Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Cashier - *Apple* Blossom Mall - JCPenney (...
Cashier - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Blossom Mall Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.