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

Apple iBooks Author 2.5 - Create and pub...
Apple iBooks Author helps you create and publish amazing Multi-Touch books for iPad. Now anyone can create stunning iBooks textbooks, cookbooks, history books, picture books, and more for iPad. All... Read more
TunnelBear 3.0.1 - Subscription-based pr...
TunnelBear is a subscription-based virtual private network (VPN) service and companion app, enabling you to browse the internet privately and securely. Features Browse privately - Secure your data... Read more
ExpanDrive 5.4.0 - Access cloud storage...
ExpanDrive builds cloud storage in every application, acts just like a USB drive plugged into your Mac. With ExpanDrive, you can securely access any remote file server directly from the Finder or... Read more
Opera 40.0.2308.62 - High-performance We...
Opera is a fast and secure browser trusted by millions of users. With the intuitive interface, Speed Dial and visual bookmarks for organizing favorite sites, news feature with fresh, relevant content... Read more
Apple iOS 10.0.2 - The latest version of...
iOS 10 is the biggest release of iOS ever. A massive update to Messages brings the power of the App Store to your conversations and makes messaging more personal than ever. Find your route with... Read more
Herald 7.0 - Notification plugin for Mai...
Note: Versions 2.1.3 (for OS X 10.7), 3.0.6 (for OS X 10.8), 4.0.8 (for OS X 10.9), 5.0.2 (for OS X 10.10), and 6.0.3 (for OS X 10.11) are no longer supported by the developer. Herald is a... Read more
Postbox 5.0.2 - Powerful and flexible em...
Postbox is a new email application that helps you organize your work life and get stuff done. It has all the elegance and simplicity of Apple Mail, but with more power and flexibility to manage even... Read more
Yummy FTP Pro 1.11.10 - $29.99
Yummy FTP Pro is an advanced Mac file transfer app which provides a full-featured professional toolkit combined with blazing speeds and impeccable reliability, so whether you want to transfer a few... Read more
Tidy Up 4.1.17 - $29.99
Tidy Up is a full-featured duplicate finder and disk-tidiness utility. Features With Tidy Up you can search for duplicate files and packages by the owner application, content, type, creator,... Read more
Logitech Control Center 3.9.5 - Keyboard...
Logitech Control Center (LCC) is designed to support OS X and allows you to take full advantage of your Logitech keyboard, mouse, or trackball. With the LCC you can: Browse the Internet using... Read more

How to build and care for your team in D...
Before you hit the trail and become a dog sledding legend, there’s actually a fair bit of prep work to be done. In Dog Sled Saga, you’re not only racing, you’re also building and caring for a team of furry friends. There’s a lot to consider—... | Read more »
How to win every race in Dog Sled Saga
If I had to guess, I’d say Dog Sled Saga is the most adorable racing game on the App Store right now. It’s a dog sled racing sim full of adorable, loyal puppies. Just look at those fluffy little tails wagging. Behind that cute, pixelated facade is... | Read more »
Let the war games commence in Gunship Ba...
Buzz Lightyear famously said, “This isn’t flying, this is falling – with style!” In the case of Gunship Battle: Second War, though, this really is flying - with style! The flight simulator app from Joycity puts you in control of 20 faithfully... | Read more »
How to get a high score in Fired Up
Fired Up is Noodlecake Games’ high score chasing, firefighting adventure. You take control of a wayward firefighter who propels himself up the side of a highrise with blasts of water. Sound silly? It is. It’s also pretty difficult. You can’t... | Read more »
NBA 2K17 (Games)
NBA 2K17 1.0 Device: iOS iPhone Category: Games Price: $7.99, Version: 1.0 (iTunes) Description: Following the record-breaking launch of NBA 2K16, the NBA 2K franchise continues to stake its claim as the most authentic sports video... | Read more »
Dog Sled Saga (Games)
Dog Sled Saga 1.0.1 Device: iOS Universal Category: Games Price: $3.99, Version: 1.0.1 (iTunes) Description: A game by Dan + Lisa As a rookie musher, foster a dogsledding team whose skills will grow if they're treated right. Week by... | Read more »
60 Seconds! Atomic Adventure (Games)
60 Seconds! Atomic Adventure 1.2 Device: iOS Universal Category: Games Price: $2.99, Version: 1.2 (iTunes) Description: 60 Seconds! is a dark comedy atomic adventure of scavenge and survival. Collect supplies and rescue your family... | Read more »
Tons of Bullets! (Games)
Tons of Bullets! 1.0 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0 (iTunes) Description: Tons of BulletsA retro 2D pixel platformer infused with tons of bullets and tons of features!Fight as Kenji the Ninja and... | Read more »
Sorcery! 4 (Games)
Sorcery! 4 1.0 Device: iOS Universal Category: Games Price: $4.99, Version: 1.0 (iTunes) Description: *** PLEASE NOTE: Sorcery! 4 requires a minimum of iPad 3 or iPhone 4 *** An epic adventure through a cursed citadel of monsters,... | Read more »
Building the perfect Animation Throwdown...
Animation Throwdown is a casual card game full of plenty of inside jokes from some of your favorite cartoons. It’s accessible as far as card games go, but that’s not to say that it hands your victories to you easily. There’s quite a bit that goes... | Read more »

Price Scanner via MacPrices.net

Duplicate Sweeper Free On Mac App Store For O...
To celebrate the launch of Apple’s latest macOS Sierra, Stafford, United Kingdom based Wide Angle Software has announced that its duplicate file finder software, Duplicate Sweeper, is now available... Read more
13-inch Retina MacBook Pros on sale for up to...
B&H Photo has 13″ Retina Apple MacBook Pros on sale for up to $150 off MSRP. Shipping is free, and B&H charges NY tax only: - 13″ 2.7GHz/128GB Retina MacBook Pro: $1174.99 $125 off MSRP - 13... Read more
Evidence Surfaces Pointing To New A10X Chip F...
Citing a job description for a Project Lead position at Apple’s Austin, Texas engineering labs, Motley Fool’s Ashraf Eassa deduces that development is progressing well on Apple’s next-generation in-... Read more
Check Print’R for macOS Allows Anyone to Easi...
Delaware-based Match Software has announced the release and immediate availability of Check Print’R 3.21, an important update to their easy-to-use check printing application for macOS. Check Print’R... Read more
Apple refurbished 11-inch MacBook Airs availa...
Apple has Certified Refurbished 11″ MacBook Airs (the latest models), available for up to $170 off the cost of new models. An Apple one-year warranty is included with each MacBook, and shipping is... Read more
Apple refurbished 15-inch Retina MacBook Pros...
Apple has Certified Refurbished 2015 15″ Retina MacBook Pros available for up to $380 off the cost of new models. An Apple one-year warranty is included with each model, and shipping is free: - 15″ 2... Read more
Major Version 5 Release Of Free myTuner Radio...
AppGeneration Software has announced the release of myTuner Radio 5.0.1, an important update to their live radio app for iOS and Android platforms. With myTuner Radio, you can listen to more than 30,... Read more
TheAppStore Searches And Filters iOS And macO...
Boston based InchWest has announced the official launch of TheAppStore, a free web based service that lets users search iOS and Mac apps on the web without iTunes or the App store. TheAppStore... Read more
2TB Time Capsule on sale for $199, save $100
Amazon has 2TB Apple Time Capsules on sale for $199 including free shipping. Their price is $100 off MSRP. Choose Amazon as the seller when making your purchase, rather than a third party seller. Read more
12-inch 1.2GHz Rose Gold Retina MacBook on sa...
Amazon.com has the 2016 12″ 1.2GHz Rose Gold Retina MacBook on sale for $1254.02 including free shipping. Their price is $345 off MSRP, and it’s the lowest price available for this model from any... Read more

Jobs Board

US- *Apple* Store Leader Program - Apple (Un...
…Summary Learn and grow as you explore the art of leadership at the Apple Store. You'll master our retail business inside and out through training, hands-on Read more
Senior *Apple* Administrator - Pratt Instit...
POSITION SUMMARY: Directs the coordination and standardization of campus-wide Apple systems, including planning, analysis and implementation of Apple -related Read more
Restaurant Manager (Neighborhood Captain) - A...
…in every aspect of daily operation. WHY YOU'LL LIKE IT: You'll be the Big Apple . You'll solve problems. You'll get to show your ability to handle the stress and Read more
*Apple* Retail - Multiple Positions- Napervi...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
US- *Apple* Store Leader Program - Apple (Un...
…Summary Learn and grow as you explore the art of leadership at the Apple Store. You'll master our retail business inside and out through training, hands-on Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.