Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
source/NewApp.c
/* |
copyright © 1991-1994 Apple Computer Inc. All rights reserved. |
NewApp.c |
This file contains all new API implementations for the ImageWriter driver. |
Modification history |
03/19/91 TED New file today |
04/23/91 Sam Weiss Changed Inherit to Forward |
05/29/91 TED Added manual feed and faster mode support |
12/20/93 dmh Sync'd with the shipping 1.0b3 GX driver. |
03/24/94 Ken Hittleman Updated manual feed to use gxTrayFeedInfo |
03/30/94 Ken Hittleman Added call to paper matching CheckStatus when switching to auto-feed |
07/05/94 Ken Hittleman Removed set color and page size from IW I path, which blew cookies on it |
08/26/94 dmh Sync'd with the shipping 1.0.1 GX driver. |
05/21/96 Jason H-H Updated for ETO#19/MW. |
05/21/96 Don Swatman Modifications for halftoning, dithering and plane seperations. |
*/ |
/**** DEFINE DEBUG ON ****/ |
//#define DEBUGLEVEL DEBUGON |
/*************************/ |
#include <Memory.h> |
#include <Errors.h> |
#include <ToolUtils.h> |
#include <Resources.h> |
#include <Packages.h> |
#include <GXPrinterDrivers.h> |
#include <GXPrinting.h> |
#include <FixMath.h> |
#include <GXMath.h> |
#include <GXGraphics.h> |
#include <GraphicsLibraries.h> |
#include <GXLayout.h> |
#include <CollectionLibrary.h> |
#include <JobFormatModeLibrary.h> |
#include <PaperTypeLibrary.h> |
#include <PicturesAndPICTLibrary.h> |
#include "CommonDefines.h" |
#include "NewDriverProtos.h" |
/* ----------------------------------------------------------------------- |
__Startup__ contains our jump table to the overrides. |
This code must be kept in sync with the assembly jump table |
in NewApp.a and the gxOverrideType resources in NewApp.rsrc |
----------------------------------------------------------------------- */ |
#if defined(__MWERKS__) |
asm void __Startup__(void); |
asm void __Startup__(void) |
{ |
dc.L 0 // Reserved for owner count. |
// Universal messages |
jmp SD_Initialize |
jmp SD_ShutDown |
jmp SD_DefaultPrinter |
jmp SD_DefaultFormat |
jmp SD_DefaultJob |
jmp SD_JobFormatDialog |
jmp SD_JobFormatModeQuery |
jmp SD_RenderPage |
jmp SD_OpenConnection |
jmp SD_CloseConnection |
jmp SD_StartSendPage |
jmp SD_FinishSendPage |
jmp SD_DumpBuffer |
jmp SD_FreeBuffer |
jmp SD_SetupImageData |
jmp SD_JobIdle |
jmp SD_JobPrintDialog |
jmp SD_HandlePanelEvent |
// Raster messages |
jmp SD_PackageBitmap |
jmp SD_LineFeed |
rts // this is needed so __Startup__ symbol works |
} |
#endif |
/* ------------------------------------------------------------------------------------ */ |
/* INTERNAL DEFINES */ |
/* ------------------------------------------------------------------------------------ */ |
// positive error for aborting job and placing on hold |
#define kPutJobOnHoldErr 3 |
// timeout (in ticks) for the initial query |
#define kQueryTimeout (7*60); |
// things this specific driver puts into the DTP config file |
#define kImageWriterConfigType 'ifig' |
#define kImageWriterConfigID (0) |
typedef struct |
{ |
Boolean hasColorRibbon; |
Boolean hasSheetFeeder; |
Boolean isImageWriterII; // is this an ImageWriter II, or an older model? |
} ImageWriterConfigRecord, *ImageWriterConfigPtr, ** ImageWriterConfigHandle; |
/* Define special characters needed */ |
#define ESCAPE (char) 27 |
/* A record to hold a set margins command */ |
typedef struct SetMarginsRecord |
{ |
char cEscape; // ESCAPE character |
char cCommand; // Set Margins command character |
char cIndentDistance[4]; // number of dots to indent |
} SetMarginsRecord, *SetMarginsPtr; |
#define kSetMarginsCommand (char)'F' // ImageWriter II uses 'F' for tabbing |
#define kSetMarginsSize 6 |
/* Define a record that can hold one scan line's worth of data, 1280 will |
only happen at 160 dpi. and 14 inch wide paper. */ |
typedef struct ScanLineRecord |
{ |
char cColorEscape; // ESCAPE character |
char cSetColorCommand; // Set color command |
char cColor; // The color |
char cEscape; // ESCAPE character |
char cCommand; // 'enter graphics' command |
char cLineLength[4]; // number of dots to print |
char iTheData[2240]; // Bits for the data, enough for one line's worth |
} ScanLineRecord, *ScanLinePtr; |
#define kGraphicsCommand (char)'G' /* graphics printing command */ |
#define kRepeatGroup (char)'V' /* repeat group character */ |
#define kSetColorCommand (char)'K' /* Set color command */ |
#define kGroupSize 6 /* Size of one group header */ |
#define kScanLineSize 3 /* NOTE: this is just the header size! */ |
// Status flags for PAP status queries |
#define kColorRibbonBit 0 |
#define kSheetFeederBit 1 |
#define kPaperOutBit 2 |
#define kCoverOpenBit 3 |
#define kOffLineBit 4 |
#define kPaperJamBit 5 |
#define kPrinterFaultBit 6 |
#define kHeadMovingBit 7 |
#define kPrinterBusyBit 8 |
#define kOutOfPaperMask ( (0x8000 >> kPaperJamBit) | (0x8000 >> kCoverOpenBit) | (0x8000 >> kOffLineBit) ) |
#define kPrinterOfflineMask ( (0x8000 >> kOffLineBit) ) |
#define kPrinterBusyMask ( (0x8000 >> kPrinterBusyBit) ) |
#define kHeadMovingMask ( (0x8000 >> kHeadMovingBit) ) |
// Color pass |
#define kYellowPass 1 |
#define kMagentaPass 2 |
#define kCyanPass 3 |
#define kBlackPass 4 |
//------------------------------------------------------------------- |
// Structures and constants for the render options collection |
// |
// This is structured so it can be used by the gxExtendedDITLType |
// resource -'xdtl'. The types and allignment are significant. |
// |
// Booleans : unsigned char : These are byte alligned. |
// Pop-up : short : Word (2Byte) alligned |
// Float : fixed : Word (2Byte) alligned |
//------------------------------------------------------------------- |
#define kDitherIt 0 |
#define kHalfToneIt 1 |
#define kRenderOptsCyan 0 |
#define kRenderOptsMagenta 1 |
#define kRenderOptsYellow 2 |
#define kRenderOptsBlack 3 |
struct CMYKRenderCollection |
{ |
// Plane flags, set to true to print the plane |
unsigned char cyanIsOn; |
unsigned char magentaIsOn; |
unsigned char yellowIsOn; |
unsigned char blackIsOn; |
// Set to "kDitherIt" for dithered output, or "kHalfToneIt" for half tone |
unsigned char renderMode; |
unsigned char fill1; |
// used when in dither mode to select the level of dither |
short ditherLevel; |
// used when in half tone mode to select how to half tone |
short dotType; |
Fixed angles[4]; |
Fixed frequency[4]; |
}; |
typedef struct CMYKRenderCollection CMYKRenderCollection; |
//<FF> |
/* ------------------------------------------------------------------------------------ */ |
/* INTERNAL ROUTINES */ |
/* ------------------------------------------------------------------------------------ */ |
void Long2Dec(long aLong, Ptr emitHere) |
/* |
Converts a long into an ASCII string, padded with leading zeros. |
*/ |
{ |
char aString[10]; |
short i, actualWidth, strLength; |
NumToString(aLong, (unsigned char*)aString); |
// Get the width of the string, check for being too small |
strLength = aString[0]; |
actualWidth = strLength; |
if (actualWidth < 4) |
actualWidth = 4; |
// output the string, padding with the requested character |
strLength = actualWidth-strLength; |
for (i = 0; i < actualWidth; ++i) |
{ |
*emitHere++ = (i < strLength) |
? '0' : aString[(i+1)-(strLength)]; |
} |
} // Long2Dec |
//<FF> |
/* ------------------------------------------------------------------------------------ */ |
Boolean PrinterHasColorRibbon(gxPrinter thePrinter) |
/* |
Returns true if the config file says that the printer is blessed with a color ribbon, |
false if it is a black and white ribbon. |
*/ |
{ |
Boolean hasColor = true; |
Str32 deviceName; |
OSErr anErr; |
ImageWriterConfigHandle configHandle; |
// if not formatting to a particular device, assume color ribbon, for widest range of colorSpaces |
GXGetPrinterName(thePrinter, deviceName); |
if (deviceName[0] != 0) |
{ |
// if we are going to a particular device, assume no color, as that is more common |
hasColor = false; |
anErr = GXFetchDTPData(deviceName, kImageWriterConfigType, kImageWriterConfigID, (Handle*)&configHandle); |
if (anErr == noErr) |
{ |
hasColor = (**configHandle).hasColorRibbon; |
DisposHandle((Handle) configHandle); |
} |
} |
return(hasColor); |
} // PrinterHasColorRibbon |
//<FF> |
/* ------------------------------------------------------------------------------------ */ |
gxViewDevice NewDeviceResolutionViewDevice(void) |
/* |
This routine creates a viewDevice and gives it a scale factor in it's mapping |
appropriate for this device (144 dpi in the case of the IW) |
*/ |
{ |
gxViewDevice vd; |
// create the viewDevices with a fake bitmap |
{ |
gxShape tempBitmap; |
gxBitmap aBitmap; |
aBitmap.pixelSize = 1; |
aBitmap.rowBytes = 0; |
aBitmap.width = 0; |
aBitmap.height = 0; |
aBitmap.image = (char*)gxMissingImagePointer; |
aBitmap.space = gxNoSpace; |
aBitmap.set = nil; |
aBitmap.profile = nil; |
tempBitmap = GXNewBitmap(&aBitmap, nil); |
vd = GXNewViewDevice(gxScreenViewDevices, tempBitmap); |
GXDisposeShape(tempBitmap); |
} |
// setup a mapping for a 144 (2X) viewDevice |
{ |
gxMapping vdMapping; |
ResetMapping(&vdMapping); |
ScaleMapping(&vdMapping, ff(2), ff(2), ff(0), ff(0)); |
GXSetViewDeviceMapping(vd, &vdMapping); |
} |
return(vd); |
} // NewDeviceResolutionViewDevice |
//<FF> |
/* ------------------------------------------------------------------------------------ */ |
Boolean JobIsBest(long *imagewriterOptions) |
/* |
Returns true if the current job is a final quality mode job, else returns false. |
Also, returns the imagewriter rendering options. |
*/ |
{ |
Boolean isFinal; |
gxQualityInfo jobQualitySettings; |
long itemSize = sizeof(jobQualitySettings); |
OSErr status; |
Collection jobCollection; |
// cache the collection |
jobCollection = GXGetJobCollection(GXGetJob()); |
// find out the info |
isFinal = false; |
status = GetCollectionItem(jobCollection, |
gxQualityTag, gxPrintingTagID, |
&itemSize, &jobQualitySettings); |
if ( (status == noErr) && (jobQualitySettings.currentQuality == (jobQualitySettings.qualityCount-1)) ) |
isFinal = true; |
// we default to super res |
*imagewriterOptions = kSuperRes; |
itemSize = sizeof(imagewriterOptions); |
status = GetCollectionItem(jobCollection, |
DriverCreator, 0, |
&itemSize, imagewriterOptions); |
// and return the job quality mode |
return(isFinal); |
} // JobIsBest |
//<FF> |
/* ------------------------------------------------------------------------------------ */ |
OSErr DoTheQuery(unsigned short * statusReturn, Boolean papStatus) |
/* |
Returns in statusString the current status for the printer. Returns various |
errors from IO package if the printer's status could not be found. |
*/ |
{ |
OSErr anErr = noErr; |
long statusLength; // status string size |
SpecGlobalsHdl hGlobals = GetMessageHandlerInstanceContext(); |
char cmdData[4]; |
Str255 theTerminatorStr; |
theTerminatorStr[0] = 0x01; // teminator codes for query |
theTerminatorStr[1] = 0x0D; // CR == 0x0D |
cmdData[0] = 0x1B; // cmdData control codes to query ImageWriter II |
cmdData[1] = 0x3F; |
// default to a clear status |
*statusReturn = 0; |
// send the query |
if (papStatus) |
{ |
char statusString[255]; // returned string |
// According to the old IW driver, sometimes it will return all of the bits |
// set. This is an error, but we can just try again. |
do |
{ |
statusLength = 255; |
anErr = Send_GXGetDeviceStatus(nil, 0, statusString, &statusLength, nil); |
} while ( (anErr == noErr) && (statusString[0] == 0xFF) ); |
// return the printer status bits in the PAP case |
*statusReturn = *(unsigned short*)&statusString[0]; |
} |
else |
{ |
char statusString[8]; // returned string |
if ((**hGlobals).isImageWriterII) |
{ |
statusLength = 8; // max number of characters to get back |
anErr = Send_GXGetDeviceStatus((Ptr)&cmdData, 2, statusString, &statusLength, theTerminatorStr); |
if ( anErr == gxAioTimeout) |
{ |
*statusReturn = kPrinterOfflineMask; |
anErr = noErr; |
} |
else |
{ |
if (anErr == noErr) |
{ |
// generate printer status bits in the serial case |
if (statusString[4] == 'C') |
*statusReturn |= 0x8000 >> kColorRibbonBit; |
if ( (statusString[5] == 'F') || (statusString[4] == 'F') ) |
*statusReturn |= 0x8000 >> kSheetFeederBit; |
} |
} |
} |
} |
nrequire(anErr, Send_GXGetDeviceStatus); |
// FALL THROUGH EXCEPTION HANDLING |
Send_GXGetDeviceStatus: |
return(anErr); |
} // DoTheQuery |
//<FF> |
/* ------------------------------------------------------------------------------------ */ |
OSErr FetchStatusString(unsigned short * statusReturn, Boolean papStatus, Boolean doRetry) |
/* |
Returns in statusString the current status for the printer. Returns various |
errors from IO package if the printer's status could not be found. |
Handles reporting error conditions to the user. |
*/ |
{ |
OSErr anErr; |
SpecGlobalsHdl hGlobals = GetMessageHandlerInstanceContext(); |
anErr = DoTheQuery(statusReturn, papStatus); |
nrequire(anErr, Send_GXGetDeviceStatus); |
// printer offline? |
if ( |
(doRetry) && |
( ((*statusReturn) & kPrinterOfflineMask) != 0 ) |
) |
{ |
gxStatusRecord theStat; |
gxStatusRecord *pStat = &theStat; |
Boolean printerIsFixed = false; |
pStat->statusOwner = 'drvr'; |
pStat->statResId = kDriverStatus; |
pStat->statResIndex = kCheckOnline; |
pStat->bufferLen = 0; |
pStat->dialogResult = 0; // ORIGINALLY SET TO ===> nil; |
// keep sending the user the alert until either |
// a) the problem resolves itself |
// b) the user responds via the dialog |
// c) some other (fatal) error happens |
do |
{ |
// tell the user |
anErr = GXAlertTheUser(pStat); |
// based on the user's response, continue or cancel |
switch (pStat->dialogResult) |
{ |
case ok: |
// retry |
break; |
case cancel: |
anErr = gxPrUserAbortErr; |
break; |
case 3: |
anErr = kPutJobOnHoldErr; |
break; |
} |
// error to return from next idle |
(**hGlobals).idleError = anErr; |
// if printer got suddenly turned online, do an OK |
if ( (anErr == noErr) && ((papStatus) || (pStat->dialogResult == ok)) ) |
{ |
pStat->dialogResult = 0; // ORIGINALLY SET TO ===> nil; |
(void) DoTheQuery(statusReturn, papStatus); |
if ( |
( ((*statusReturn) & kPrinterOfflineMask) == 0 ) |
) |
{ |
printerIsFixed = true; |
pStat->dialogResult = ok; |
anErr = noErr; |
} |
} |
} while ((anErr == noErr) && (pStat->dialogResult == 0)); // WAS ORIGINALLY ====> (pStat->dialogResult == nil) |
// printer is okay -- no error |
if (printerIsFixed) |
anErr = noErr; |
// display "sending data to the printer" message |
if (anErr == noErr) |
anErr = GXReportStatus(kDriverStatus, kSendingData); |
} |
// FALL THROUGH EXCEPTION HANDLING |
Send_GXGetDeviceStatus: |
return(anErr); |
} // FetchStatusString |
//<FF> |
/* ------------------------------------------------------------------------------------ */ |
OSErr UpdateConfiguration(void) |
/* |
This routine queries the printer for its hardware configuration (color ribbon and |
sheet feeder options), and stores that info into the configuration file. |
*/ |
{ |
SpecGlobalsHdl hGlobals; |
Str32 deviceName; |
OSErr anErr = noErr; |
ImageWriterConfigHandle configHandle; |
ImageWriterConfigPtr configPtr; |
Boolean isImageWriterII; |
ResType commType; |
hGlobals = GetMessageHandlerInstanceContext(); |
isImageWriterII = false; |
// find out what we are printing to, and how we are connected |
GXGetPrinterName(GXGetJobOutputPrinter(GXGetJob()), deviceName); |
anErr = GXFetchDTPData(deviceName, gxDeviceCommunicationsType, gxDeviceCommunicationsID, (Handle*)&configHandle); |
nrequire(anErr, FetchCommType); |
commType = **(ResType**)configHandle; |
DisposHandle((Handle) configHandle); |
// store away the communications type for future use |
//{ |
// SpecGlobalsHdl hGlobals = GetMessageHandlerInstanceContext(); |
// |
// (**hGlobals).commType = commType; |
//} |
// find out the original configuration |
if (GXFetchDTPData(deviceName, kImageWriterConfigType, kImageWriterConfigID, (Handle*)&configHandle) == noErr) |
{ |
// remember if we thought we had an IW2 when we started |
configPtr = *configHandle; |
(**hGlobals).isImageWriterII = isImageWriterII = configPtr->isImageWriterII; |
DisposeHandle((Handle) configHandle); |
// if we aren't an ImageWriter II, bail out now - because the timeout takes two minutes! |
if (!isImageWriterII) |
{ |
return(noErr); |
} |
} |
else |
{ |
if (commType == 'PPTL') |
{ |
isImageWriterII = true; |
} |
// Assume IW 2 so we do the query for real |
(**hGlobals).isImageWriterII = true; |
} |
// make a handle to hold our configuration information for the printer |
configHandle = (ImageWriterConfigHandle) NewHandle(sizeof(ImageWriterConfigRecord) ); |
anErr = MemError(); |
nrequire(anErr, NewHandle); |
// setup the default for the device - in case the query fails |
configPtr = *configHandle; |
configPtr->hasColorRibbon = false; |
configPtr->hasSheetFeeder = false; |
configPtr->isImageWriterII = true; |
// send initial data first to make sure IO handshaking is working, |
// to load the first sheet of paper into the feeder (if any), |
// and to take up "gear lash" in the device. This is copied from |
// what the old driver did. Not doing this will cause the sheet |
// feeder not to feed the initial page of data. |
{ |
char sendBuffer[11]; |
// <CR> |
sendBuffer[0] = 0x0D; |
// linefeed size = 18/144th |
sendBuffer[1] = ESCAPE; |
sendBuffer[2] = 'T'; |
sendBuffer[3] = '1'; |
sendBuffer[4] = '8'; |
// reverse line feed |
sendBuffer[5] = ESCAPE; |
sendBuffer[6] = 'r'; |
sendBuffer[7] = 0x0A; |
// forward line feed |
sendBuffer[8] = ESCAPE; |
sendBuffer[9] = 'f'; |
sendBuffer[10] = 0x0A; |
anErr = Send_GXWriteData(sendBuffer, 11); |
nrequire(anErr, Failed_SendInitialData); |
} |
{ |
unsigned short statusReturn; |
// query the device |
if ((isImageWriterII) && (commType == 'SPTL') ) |
(**hGlobals).idleTimeout = TickCount() + kQueryTimeout; |
anErr = FetchStatusString(&statusReturn, (Boolean)(commType == 'PPTL'), isImageWriterII); |
if ( (anErr == noErr) && ( (statusReturn & kPrinterOfflineMask) != 0 ) ) |
anErr = gxAioTimeout; |
(**hGlobals).idleTimeout = 0; |
GXReportStatus(kDriverStatus, kSendingData); |
// and scan the string looking for information about printer kind and options |
configPtr = *configHandle; |
if ( anErr == gxAioTimeout ) |
{ |
// if we timeout and we don't know the printer kind - assume IW1 |
if (!isImageWriterII) |
{ |
anErr = noErr; |
isImageWriterII = configPtr->isImageWriterII = false; |
} |
} |
else |
{ |
isImageWriterII = true; |
configPtr->hasColorRibbon = (statusReturn & (0x8000 >> kColorRibbonBit)) != 0; |
configPtr->hasSheetFeeder = (statusReturn & (0x8000 >> kSheetFeederBit)) != 0; |
} |
nrequire(anErr, FetchStatusString); |
} |
// Remember if this was an ImageWriter II after the query |
(**hGlobals).isImageWriterII = isImageWriterII; |
// write out the new configuration |
anErr = GXWriteDTPData(deviceName, kImageWriterConfigType, kImageWriterConfigID, (Handle)configHandle); |
// CLEANUP EXCEPTION HANDLING |
FetchStatusString: |
Failed_SendInitialData: |
DisposHandle((Handle) configHandle); |
NewHandle: |
Send_GXWriteData: |
FetchCommType: |
return(anErr); |
} // UpdateConfiguration |
/* ------------------------------------------------------------------------------------ */ |
OSErr WriteDraftChars(long **draftTable, unsigned char *draftChar, long numChars) |
/* |
This routine writes out a single character in the native set of the printer. |
It uses a table that's part of the driver to do the right thing in order to generate this |
character. |
*/ |
{ |
OSErr anErr = noErr; |
char outputChars[20]; // a maximum of 20 characters can be generated |
short charCount; |
// For each character in the buffer, determine how to map the character to a draft character |
for (; numChars > 0; --numChars, ++draftChar) |
{ |
// No characters yet for this output character |
charCount = 0; |
// Only consider characters in the printable range |
if (*draftChar >= 0x20) |
{ |
unsigned long draftControl = (*draftTable)[*draftChar-0x20]; // Fetch native mode long word corresponding to this character |
unsigned char outChar; |
unsigned char nationalSet; |
short i; |
// For each word which composes the native mode long word |
for (i = 1; i >= 0; --i) |
{ |
// Should we send a backspace character (to overstrike)? |
if ( (draftControl & 0x80000000) != 0 ) |
outputChars[charCount++] = 0x08; |
outChar = (draftControl >> 16) & 0xFF; |
if (outChar != 0) |
{ |
// Determine the national character set to select |
nationalSet = (draftControl >> 24) & 0xF; |
// Is this character in the standard, built-in character set? |
if (nationalSet == 0) |
{ |
outputChars[charCount++] = outChar; |
} |
else // T => Must select a foreign language character set |
{ |
outputChars[charCount++] = 0x1B; |
outputChars[charCount++] = 0x44; |
outputChars[charCount++] = nationalSet; |
outputChars[charCount++] = 0x00; |
outputChars[charCount++] = outChar; |
outputChars[charCount++] = 0x1B; // We always switch back to the kAmerican character set |
outputChars[charCount++] = 0x5A; |
outputChars[charCount++] = 0x07; |
outputChars[charCount++] = 0x00; |
} |
} |
// Take the next (low) word and process it (if we're not all done) |
draftControl <<= 16; |
} |
} |
// If we generated any data, send it out now |
if (charCount > 0) |
anErr = Send_GXBufferData(outputChars, charCount, gxNoBufferOptions); |
} |
return(anErr); |
} // WriteDraftChars |
/* ------------------------------------------------------------------------------------ */ |
OSErr GetPointerThisBig(Ptr *theBuff, long numBytes) |
{ |
OSErr anErr = noErr; |
if (*theBuff != nil) |
{ |
if ( GetPtrSize(*theBuff) < numBytes ) // T => Won't be big enough; make a new one |
{ |
DisposPtr(*theBuff); |
*theBuff = nil; |
} |
} |
if (*theBuff == nil) |
{ |
*theBuff = NewPtrClear(numBytes); |
anErr = MemError(); |
} |
return(anErr); |
} // GetPointerThisBig |
/* ------------------------------------------------------------------------------------ */ |
OSErr GetTextAndPosition( gxShape theShape, |
Ptr *theChars, |
long *numChars, |
gxPoint *textPosition) |
{ |
OSErr anErr = noErr; |
long textLength; |
// Determine the size of the text data and the position of the text |
textLength = GXGetLayout(theShape, nil, nil, nil, nil, nil, nil, nil, nil, textPosition); |
// Make sure we have a buffer pointer large enough to hold all of the data |
anErr = GetPointerThisBig(theChars, textLength); |
require(anErr == noErr, CantAllocTextBuff); |
// Now we retrieve the text |
GXGetLayout(theShape, *theChars, nil, nil, nil, nil, nil, nil, nil, nil); |
// Remember the number of characters in the shape |
*numChars = textLength; |
/******* Clean-up *******/ |
CantAllocTextBuff: |
return(anErr); |
} // GetTextAndPosition |
/* ------------------------------------------------------------------------------------ */ |
OSErr PrintPageInDraftMode(gxShape thePage, gxRasterImageDataHdl imageData) |
{ |
OSErr anErr = noErr; |
long i; |
long numItems; |
Fixed currYPos = ff(0); |
Ptr theChars = nil; |
long numChars = 0; |
gxPoint textPosition; |
Fixed oldTextSize = ff(0); |
SpecGlobalsHdl hGlobals = GetMessageHandlerInstanceContext(); |
// Since the page picture we need to process is a picture shape that's embedded in |
// thePage (a shape containing one item => a picture), we need to extract the real |
// page picture from thePage. |
thePage = GetPictureItem(thePage, 1, nil, nil, nil, nil); |
numItems = GXGetPicture(thePage, nil, nil, nil, nil); |
// For each shape within the picture, check its type and process it accordingly |
for (i = 1; i <= numItems; ++i) |
{ |
gxShape theShape; |
short theType; |
theShape = GetPictureItem(thePage, i, nil, nil, nil, nil); |
theType = GXGetShapeType(theShape); |
if (theType == gxLayoutType) // T => We have a layout shape |
{ |
Fixed textSize; |
char buff[12]; |
char theFace; |
short cmndBuffSz; |
// First determine the style in which we're printing |
theFace = GetStyleCommonFace( GXGetShapeStyle(theShape) ); |
buff[0] = ESCAPE; |
if ( (theFace & bold) != 0 ) // T => Turn bold facing on |
buff[1] = '!'; |
else // T => Turn it off |
buff[1] = '"'; |
buff[2] = ESCAPE; |
if ( (theFace & underline) != 0 ) // T => Turn underline facing on |
buff[3] = 'X'; |
else // T => Turn it off |
buff[3] = 'Y'; |
cmndBuffSz = 4; |
// Next determine if we need to change the size of the font being used |
textSize = GXGetShapeTextSize(theShape); |
if (textSize != oldTextSize) // T => Must issue LQ command to change font size |
{ |
buff[4] = ESCAPE; // The first escape command selects black color |
buff[5] = kSetColorCommand; |
buff[6] = '0'; |
buff[7] = ESCAPE; // The second escape command selects a draft font |
buff[8] = 'a'; |
buff[9] = '1'; |
buff[10] = ESCAPE; // The third escape command selects the character pitch |
if ( textSize <= ff(10) ) // T => Select 10 cpi |
{ |
buff[11] = 'N'; |
} |
else // T => All other sizes get mapped to 12 cpi |
{ |
buff[11] = 'E'; |
} |
// Remember the last text size |
oldTextSize = textSize; |
// Adjust the size of the data to be sent to the printer |
cmndBuffSz += 8; |
} |
// else - no change in font size |
// Send the commands to the printer |
anErr = Send_GXBufferData(buff, cmndBuffSz, gxDontSplitBuffer); |
require(anErr == noErr, CantSendFontCmnd); |
// Get the ASCII text and the starting position of the data |
anErr = GetTextAndPosition(theShape, &theChars, &numChars, &textPosition); |
require(anErr == noErr, CantGetTextAndPos); |
if ( (currYPos != ff(0)) && (currYPos != textPosition.y) ) // T => Moving to a lower line, finish the last ;line with a CR |
{ |
char c = 0x0D; |
anErr = Send_GXBufferData(&c, 1, gxNoBufferOptions); |
require(anErr == noErr, CantSendCRCmnd); |
} |
// Position the print head to the proper location on the page |
{ |
gxMapping theMapping; |
long lineFeedSize; |
Str255 positionCmndsBuff; |
unsigned long bytesInBuff = 0; |
unsigned char *p; |
GXGetShapeMapping(theShape, &theMapping); |
MapPoints(&theMapping, (long) 1, &textPosition); // Just map the first point |
// Now position the print head vertically |
lineFeedSize = (textPosition.y - currYPos) >> 16; |
anErr = Send_GXRasterLineFeed(&lineFeedSize, (char*)positionCmndsBuff, &bytesInBuff, imageData); |
require(anErr == noErr, CantEmitLineFeeds); |
// Update the current Y position pointer on the page |
currYPos = textPosition.y; |
// Now position the print head horizontally on the page |
p = &positionCmndsBuff[bytesInBuff]; |
*p++ = ESCAPE; |
*p++ = 'F'; |
Long2Dec((*hGlobals)->leftMargin + FixedToInt(textPosition.x), (char*)p); // Convert left margin into ASCII and place it at the start of the scan line |
// Update the number of bytes in the buffer |
bytesInBuff += 6; |
// Send the positioning info to the printer |
anErr = Send_GXBufferData((char*)positionCmndsBuff, bytesInBuff, gxDontSplitBuffer); |
require(anErr == noErr, CantSendPositionCmnds); |
} |
// Now we send the text data to the printer |
anErr = WriteDraftChars((long **) (*hGlobals)->draftTable, (unsigned char*)theChars, numChars); |
require(anErr == noErr, CantWriteChars); |
} |
} // for |
// Send one last CR to wrap the last line (if there was one) |
{ |
char c = 0x0D; |
anErr = Send_GXBufferData(&c, 1, gxNoBufferOptions); |
} |
/******* Clean-up *******/ |
CantWriteChars: |
CantSendPositionCmnds: |
CantEmitLineFeeds: |
CantSendCRCmnd: |
CantGetTextAndPos: |
CantSendFontCmnd: |
if (theChars != nil) |
DisposPtr(theChars); |
return(anErr); |
} // PrintPageInDraftMode |
//<FF> |
/* ------------------------------------------------------------------------------------ */ |
/* SPECIFIC DRIVER UNIVERSAL OVERRIDES */ |
/* ------------------------------------------------------------------------------------ */ |
OSErr SD_Initialize (void) |
/* |
The SD_Initalize message is called when a new job is created. The standard |
thing to do is to allocate and fill out your globals as you see fit. |
*/ |
{ |
SpecGlobalsHdl hGlobals; |
OSErr anErr; |
// we make our globals |
hGlobals = (SpecGlobalsHdl) NewHandleClear( sizeof(SpecGlobals) ); |
anErr = MemError(); |
// and we save them away |
SetMessageHandlerInstanceContext(hGlobals); |
// is everything okay? |
nrequire(anErr, MNewHandleClear); |
// Don't need to initialize because of the NewHandleCLEAR |
//(**hGlobals).draftTable = nil; |
//(**hGlobals).lineFeeds = 0; |
//(**hGlobals).packagingOptions = kNoPackagingOptions; |
//(**hGlobals).idleError = noErr; |
//(**hGlobals).idleQuery = false; |
//(**hGlobals).idleTimeout = 0; |
//(**hGlobals).timeoutPending = false; |
return(noErr); |
/*-----EXCEPTION HANDLING------*/ |
MNewHandleClear: |
return(anErr); |
} // SD_Initialize |
//<FF> |
/* ------------------------------------------------------------------------------------ */ |
OSErr SD_ShutDown(void) |
/* |
Shutdown is called when the job is done with. A good thing to do is to get |
rid of any additional storage that is laying around. |
*/ |
{ |
// clean up our stuff |
SpecGlobalsHdl hGlobals = GetMessageHandlerInstanceContext(); |
// get rid of the draft table (if we have one) |
if (hGlobals) |
DisposHandle((**hGlobals).draftTable); |
// we get rid of our storage |
DisposHandle((Handle) hGlobals); |
// clear out our globals - to avoid double disposes |
SetMessageHandlerInstanceContext(nil); |
return(noErr); |
} // SD_ShutDown |
//<FF> |
/* ------------------------------------------------------------------------------------ */ |
OSErr SD_DefaultPrinter(gxPrinter thePrinter) |
/* |
This call is made to setup the default printer object. The job of the |
specific driver is to add in any viewDevices that it wishes applications |
to be able to format specifically for. |
*/ |
{ |
OSErr anErr; |
gxViewDevice vd; |
gxJob theJob = GXGetJob(); |
// add the standard viewDevices first |
anErr = Forward_GXDefaultPrinter(thePrinter); |
nrequire(anErr, DefaultPrinter); |
// add a 144 b/w viewDevice |
vd = NewDeviceResolutionViewDevice(); |
{ |
gxSetColor theColors[2]; |
gxSetColor *pColor; |
gxColorSet theSet; |
pColor = &theColors[0]; |
pColor->rgb.red = pColor->rgb.green = pColor->rgb.blue = 0xFFFF; |
pColor++; |
pColor->rgb.red = pColor->rgb.green = pColor->rgb.blue = 0x0000; |
theSet = GXNewColorSet(gxRGBSpace, 2, theColors); |
SetViewDeviceColorSet(vd, theSet); |
GXDisposeColorSet(theSet); |
} |
anErr = GXAddPrinterViewDevice(thePrinter, vd); |
nrequire(anErr, FailedAddBWViewDevice); |
// add a 144 color viewDevice with 8 colors in it |
// |
// Color Index R G B |
// white 0 0xFFFF 0xFFFF 0xFFFF |
// yellow 1 0xFFFF 0xFFFF 0x0000 |
// magenta 2 0xFFFF 0x0000 0xFFFF |
// red 3 0xFFFF 0x0000 0x0000 |
// cyan 4 0x0000 0xFFFF 0xFFFF |
// green 5 0x0000 0xFFFF 0x0000 |
// blue 6 0x0000 0x0000 0xFFFF |
// black 7 0x0000 0x0000 0x0000 |
if (PrinterHasColorRibbon(thePrinter)) |
{ |
gxSetColor theColors[8]; |
gxSetColor *pColor; |
gxColorSet theSet; |
short idx; |
vd = NewDeviceResolutionViewDevice(); |
pColor = &theColors[0]; |
for (idx = 0; idx < 8; ++idx) |
{ |
// default the color to black |
pColor->rgb.red = pColor->rgb.green = pColor->rgb.blue = 0x0000; |
// and give it componants to go along with this index |
if (idx & 0x04) |
pColor->rgb.red = 0xFFFF; |
if (idx & 0x02) |
pColor->rgb.green = 0xFFFF; |
if (idx & 0x01) |
pColor->rgb.blue = 0xFFFF; |
// move on to the next color |
++pColor; |
} |
theSet = GXNewColorSet(gxRGBSpace, 8, theColors); |
SetViewDeviceColorSet(vd, theSet); |
GXDisposeColorSet(theSet); |
anErr = GXAddPrinterViewDevice(thePrinter, vd); |
nrequire(anErr, FailedAddColorViewDevice); |
} |
/* Only if we are the output printer (not the formatting printer) */ |
if (GXGetJobPrinter(theJob) == GXGetJobOutputPrinter(theJob)) { |
Collection jobCollection = GXGetJobCollection(GXGetJob()); |
Handle jobQualitySettingsHdl; |
gxQualityInfo *qualitySettings; |
Ptr p; |
Str255 bestString, roughString; |
// read in our quality mode strings |
{ |
short curResFile = CurResFile(); |
UseResFile(GXGetMessageHandlerResFile()); |
GetIndString( bestString, kNewQualityID, kBestString); |
GetIndString( roughString, kNewQualityID, kRoughString); |
UseResFile(curResFile); |
} |
jobQualitySettingsHdl = NewHandle(0); |
anErr = MemError(); |
nrequire(anErr, FailedNewHandle); |
anErr = GetCollectionItemHdl ( jobCollection, |
gxQualityTag, |
gxPrintingTagID, |
jobQualitySettingsHdl ); |
if (anErr == noErr) |
{ /* Check for proper structure -- count as not found if different */ |
HLockHi(jobQualitySettingsHdl); |
qualitySettings = *((gxQualityInfo **) jobQualitySettingsHdl); |
p = qualitySettings->qualityNames; |
if (qualitySettings->disableQuality) |
anErr = collectionItemNotFoundErr; |
else if (qualitySettings->qualityCount != 2) |
anErr = collectionItemNotFoundErr; |
else if (! IUEqualString((ConstStr255Param)p, bestString)) |
anErr = collectionItemNotFoundErr; |
else if (! IUEqualString((ConstStr255Param)(p + p[0] + 1), roughString)) |
anErr = collectionItemNotFoundErr; |
HUnlock(jobQualitySettingsHdl); |
} |
if (anErr == collectionItemNotFoundErr) |
{ |
Size count; |
/* Create the proper quality item */ |
SetHandleSize(jobQualitySettingsHdl,(sizeof(gxQualityInfo) + bestString[0] + roughString[0] + 2 )); |
anErr = MemError(); |
nrequire( anErr, FailedSetHandleSize ); |
qualitySettings = *((gxQualityInfo **) jobQualitySettingsHdl); |
qualitySettings->disableQuality = false; |
qualitySettings->defaultQuality = 1; |
qualitySettings->currentQuality = 1; |
qualitySettings->qualityCount = 2; |
count = bestString[0]+1; |
p = qualitySettings->qualityNames; |
BlockMove( bestString, p, count ); |
p += count; |
BlockMove( roughString, p, roughString[0]+1 ); |
/* Add the proper quality item */ |
anErr = AddCollectionItemHdl ( jobCollection, |
gxQualityTag, |
gxPrintingTagID, |
jobQualitySettingsHdl ); |
/* Make it vilatile by driver */ |
if (anErr == noErr) |
(void) SetCollectionItemInfo(jobCollection, gxQualityTag, gxPrintingTagID, 0x0000FFFF, gxVolatileOutputDriverCategory); |
} |
FailedSetHandleSize: |
DisposHandle(jobQualitySettingsHdl); |
} |
FailedNewHandle: |
return(noErr); |
// EXCEPTION HANDLING |
FailedAddColorViewDevice: |
FailedAddBWViewDevice: |
GXDisposeViewDevice(vd); |
DefaultPrinter: |
return(anErr); |
} // SD_DefaultPrinter |
//<FF> |
/* ------------------------------------------------------------------------------------ */ |
OSErr SD_DefaultFormat(gxFormat theFormat) |
{ |
OSErr anErr; |
Handle jobQualitySettingsHdl; |
anErr = Forward_GXDefaultFormat(theFormat); |
// now, if the application has set up a special formatting mode, we need to update |
// the quality mode collection item (and any private ones we use) |
if (anErr == noErr) |
{ |
gxPoint dpiPoint; |
gxMapping vdMapping; |
gxViewDevice selectedDevice; |
dpiPoint.x = ff(72); |
dpiPoint.x = ff(72); |
selectedDevice = GXGetPrinterViewDevice(GXGetJobPrinter(GXGetFormatJob(theFormat)), 0); |
if (selectedDevice != GXGetPrinterViewDevice(GXGetJobPrinter(GXGetFormatJob(theFormat)), 1) ) |
{ |
GXGetViewDeviceMapping(selectedDevice, &vdMapping); |
MapPoints(&vdMapping, 1, &dpiPoint); |
{ |
Collection jobCollection = GXGetJobCollection(GXGetJob()); |
gxQualityInfo *qualitySettings; |
jobQualitySettingsHdl = NewHandle(0); |
anErr = MemError(); |
nrequire(anErr, FailedNewHandle); |
anErr = GetCollectionItemHdl ( jobCollection, |
gxQualityTag, |
gxPrintingTagID, |
jobQualitySettingsHdl ); |
nrequire(anErr, FailedGetCollectionItemHdl); |
qualitySettings = *((gxQualityInfo **) jobQualitySettingsHdl); |
qualitySettings->currentQuality = |
(dpiPoint.y > ff(100)) ? (qualitySettings->qualityCount-1) : 0; |
anErr = AddCollectionItemHdl ( jobCollection, |
gxQualityTag, |
gxPrintingTagID, |
jobQualitySettingsHdl ); |
DisposHandle(jobQualitySettingsHdl); |
} |
if (anErr == noErr) |
{ |
long formatOptions; |
// turn off super-res |
formatOptions = 0; |
anErr = AddCollectionItem(GXGetFormatCollection(theFormat), |
DriverCreator, 0, |
sizeof(formatOptions), |
&formatOptions); |
} |
} |
} |
FailedNewHandle: |
return(anErr); |
FailedGetCollectionItemHdl: |
DisposHandle(jobQualitySettingsHdl); |
return(anErr); |
} // SD_DefaultFormat |
//<FF> |
/* ------------------------------------------------------------------------------------ */ |
OSErr SD_DefaultJob() |
/* |
We override this message to add our default - highest res possible |
*/ |
{ |
OSErr anErr; |
anErr = Forward_GXDefaultJob(); |
if (anErr == noErr) |
{ |
long imagewriterOptions = kSuperRes; |
anErr = AddCollectionItem(GXGetJobCollection(GXGetJob()), |
DriverCreator, |
0, |
sizeof(imagewriterOptions), |
&imagewriterOptions); |
} |
return(anErr); |
} // SD_DefaultJob |
/* ------------------------------------------------------------------------------------ */ |
OSErr SD_OpenConnection(void) |
/* |
The OpenConnection message is sent in order to open the connection to the device. |
*/ |
{ |
OSErr anErr; |
SpecGlobalsHdl hGlobals = (SpecGlobalsHdl)GetMessageHandlerInstanceContext(); |
// how to process idle events |
(**hGlobals).idleError = noErr; |
(**hGlobals).idleQuery = false; |
(**hGlobals).idleTimeout = 0; |
(**hGlobals).timeoutPending = false; |
// first, open the connection the standard way |
anErr = Forward_GXOpenConnection(); |
nrequire(anErr, OpenConnection); |
// then, bring the configuration file up to date |
anErr = UpdateConfiguration(); |
nrequire(anErr, UpdateConfiguration); |
return(noErr); |
// EXCEPTION HANDLING |
UpdateConfiguration: |
GXCleanupOpenConnection(); |
OpenConnection: |
return(anErr); |
} // SD_OpenConnection |
/* ------------------------------------------------------------------------------------ */ |
OSErr SD_CloseConnection(void) |
{ |
unsigned short statusReturn; |
OSErr anErr, anErr2; |
SpecGlobalsHdl hGlobals = (SpecGlobalsHdl)GetMessageHandlerInstanceContext(); |
ResType commType = (**hGlobals).commType; |
if (commType == 'PPTL') |
{ |
// flush out all data so that we can query the printer properly one last time |
anErr = Send_GXWriteData(nil, 0); |
nrequire(anErr, Send_GXWriteData1); |
// for PAP: bring the configuration file up to date & check that the printer is online |
anErr2 = UpdateConfiguration(); |
if (anErr == noErr) anErr = anErr2; |
} |
else |
{ |
// for serial: flush out all data so that we can query the printer properly one last time |
anErr = Send_GXWriteData(nil, 0); |
nrequire(anErr, Send_GXWriteData2); |
// one last time check up on printer status |
if ((**hGlobals).isImageWriterII) |
{ |
anErr2 = FetchStatusString(&statusReturn, false, true); |
if (anErr == noErr) anErr = anErr2; |
} |
} |
Send_GXWriteData2: |
Send_GXWriteData1: |
FetchStatusString: |
// close the connection the standard way |
anErr2 = Forward_GXCloseConnection(); |
if (anErr == noErr) anErr = anErr2; |
return(anErr); |
} // SD_CloseConnection |
/* ------------------------------------------------------------------------------------ */ |
OSErr SD_JobIdle() |
{ |
OSErr anErr = noErr; |
SpecGlobalsHdl hGlobals = (SpecGlobalsHdl)GetMessageHandlerInstanceContext(); |
SpecGlobalsPtr pGlobals; |
pGlobals = *hGlobals; |
if ( (pGlobals->idleQuery) && (pGlobals->commType == 'PPTL') ) |
{ |
unsigned short statusReturn; |
pGlobals->idleQuery = false; |
anErr = FetchStatusString(&statusReturn, true, true); |
nrequire(anErr, FetchStatusString); |
anErr = Forward_GXJobIdle(); |
// EXCEPTION HANDLING |
FetchStatusString: |
pGlobals = *hGlobals; |
pGlobals->idleQuery = true; |
} |
else |
anErr = Forward_GXJobIdle(); |
// if we continue looping here too long during the initial query -- give the user |
// a chance to bail or correct the problem |
pGlobals = *hGlobals; |
if ( (!(pGlobals->timeoutPending)) && (pGlobals->idleTimeout != 0) ) |
{ |
if (TickCount() > pGlobals->idleTimeout) |
{ |
gxStatusRecord theStat; |
gxStatusRecord *pStat = &theStat; |
pStat->statusOwner = 'drvr'; |
pStat->statResId = kDriverStatus; |
pStat->statResIndex = kCheckOnline; |
pStat->bufferLen = 0; |
pStat->dialogResult = 0; // ORIGINALLY SET TO ===> nil; |
// tell the user to check the printer |
pGlobals->timeoutPending = true; |
(void) GXAlertTheUser(pStat); |
pGlobals = *hGlobals; |
pGlobals->timeoutPending = false; |
// based on the user's response cancel |
switch (pStat->dialogResult) |
{ |
case ok: |
pGlobals->idleTimeout = TickCount() + kQueryTimeout; |
break; |
case cancel: |
pGlobals->idleError = gxPrUserAbortErr; |
break; |
case 3: |
pGlobals->idleError = kPutJobOnHoldErr; |
break; |
} |
// display "sending data to the printer" message |
if (anErr == noErr) |
anErr = GXReportStatus(kDriverStatus, kSendingData); |
} |
} |
if (anErr == noErr) |
anErr = pGlobals->idleError; |
return(anErr); |
} // SD_JobIdle |
/* ------------------------------------------------------------------------------------ */ |
OSErr SD_FreeBuffer(gxPrintingBuffer * theBuffer) |
{ |
OSErr anErr = noErr; |
OSErr firstError = noErr; |
SpecGlobalsHdl hGlobals = (SpecGlobalsHdl)GetMessageHandlerInstanceContext(); |
if ((**hGlobals).commType == 'PPTL') |
{ |
unsigned short statusReturn; |
anErr = FetchStatusString(&statusReturn, true, true); |
nrequire(anErr, FetchStatusString); |
} |
do |
{ |
// we can idle query now if we need to |
(**hGlobals).idleQuery = true; |
// try to send the buffer again |
anErr = Forward_GXFreeBuffer(theBuffer); |
if (firstError == noErr) |
firstError = anErr; |
(**hGlobals).idleQuery = false; |
// timeout dialog! |
if (anErr == gxAioTimeout) |
{ |
gxStatusRecord theStat; |
gxStatusRecord *pStat = &theStat; |
pStat->statusOwner = 'drvr'; |
pStat->statResId = kDriverStatus; |
pStat->statResIndex = kCheckOnline; |
pStat->bufferLen = 0; |
pStat->dialogResult = 0; // ORIGINALLY SET TO ====> nil; |
// tell the user to check the printer |
(void) GXAlertTheUser(pStat); |
// based on the user's response cancel |
switch (pStat->dialogResult) |
{ |
case ok: |
anErr = gxAioTimeout; |
break; |
case cancel: |
anErr = gxPrUserAbortErr; |
break; |
case 3: |
anErr = kPutJobOnHoldErr; |
break; |
} |
} |
} while (anErr == gxAioTimeout); |
// put down the timeout dialog, if we ever put one up |
if (firstError != noErr) |
(void) GXReportStatus(kDriverStatus, kSendingData); |
// error to return from next idle |
(**hGlobals).idleError = anErr; |
FetchStatusString: |
return(anErr); |
} // SD_FreeBuffer |
/* ------------------------------------------------------------------------------------ */ |
OSErr SD_DumpBuffer(gxPrintingBuffer * theBuffer) |
{ |
OSErr anErr; |
SpecGlobalsHdl hGlobals = (SpecGlobalsHdl)GetMessageHandlerInstanceContext(); |
if ((**hGlobals).commType == 'PPTL') |
{ |
unsigned short statusReturn; |
anErr = FetchStatusString(&statusReturn, true, true); |
nrequire(anErr, FetchStatusString); |
} |
anErr = Forward_GXDumpBuffer(theBuffer); |
FetchStatusString: |
return(anErr); |
} // SD_DumpBuffer |
/* ------------------------------------------------------------------------------------ */ |
OSErr SD_StartSendPage(gxFormat pageFormat) |
/* |
The StartSendPage message is sent just before the page begins to be rendered. |
Note that the StartSendPage message will not be sent until imaging/communication |
time, so that user interaction alerts are considered okay here |
*/ |
{ |
OSErr anErr = noErr; |
gxJob theJob = GXGetJob(); |
Collection jobCollection; |
gxPaperType thePaperType; |
gxTrayFeedInfo trayFeedInfo; |
long itemSize = sizeof(trayFeedInfo); |
ResType commType; |
unsigned short statusReturn; |
jobCollection = GXGetJobCollection(theJob); |
// cache communications type |
commType = (**(SpecGlobalsHdl)GetMessageHandlerInstanceContext()).commType; |
if (commType == 'PPTL') |
{ |
anErr = FetchStatusString(&statusReturn, true, true); |
nrequire(anErr, FetchStatusString); |
} |
else |
statusReturn = 0; |
// check to see if this particular page is to be manually fed |
anErr = GetCollectionItem(jobCollection, gxTrayFeedTag, gxPrintingTagID, &itemSize, &trayFeedInfo); |
nrequire(anErr, FailedGetCollectionItem); |
// manual feed or out of paper? Time to ask the user what to do |
if ( trayFeedInfo.manualFeedThisPage |
|| ( ( (statusReturn & kOutOfPaperMask) != 0 ) ) |
) |
{ |
// Wait for all IO to complete, so that we can correctly tell the user what to do. |
// Since the WriteData message makes sure all data is flushed before performing the |
// IO, this call insures that pending IO is complete. |
anErr = Send_GXWriteData(nil, 0); |
nrequire(anErr, FlushAllData); |
// then, conduct the alert with the user |
{ |
gxStatusRecord *pStat; |
// make a status record containing the request to the user - note that |
// we have to make room for ManualFeedRecord OR OutOfPaperRecord, but manual is bigger |
pStat = (gxStatusRecord *)NewPtrClear(sizeof(gxStatusRecord) + sizeof(gxManualFeedRecord)); |
anErr = MemError(); |
nrequire(anErr, NewPtrClear); |
pStat->statusOwner = 'univ'; |
pStat->statResId = gxUnivAlertStatusResourceId; // we use the built-in status for this |
pStat->dialogResult = 0; // ORIGINALLY SET TO =====> nil; |
if (trayFeedInfo.manualFeedThisPage) |
{ |
gxManualFeedRecord *pFeed; |
pStat->statResIndex = gxUnivManualFeedIndex; // status meaning "manual feed alert" |
pStat->bufferLen = sizeof(gxManualFeedRecord); |
pFeed = (gxManualFeedRecord*)&pStat->statusBuffer; |
// we can switch to autofeed if we want - and tell the user what kind of paper to load in |
pFeed->canAutoFeed = true; |
GXGetPaperTypeName(thePaperType = GXGetFormatPaperType(pageFormat), pFeed->paperTypeName); |
} |
else |
{ |
gxOutOfPaperRecord *pOut; |
pStat->statResIndex = gxUnivOutOfPaperIndex; // status meaning "manual feed alert" |
pStat->bufferLen = sizeof(gxOutOfPaperRecord); |
pOut = (gxOutOfPaperRecord*)&pStat->statusBuffer; |
GXGetPaperTypeName(GXGetFormatPaperType(pageFormat), pOut->paperTypeName); |
} |
// keep sending the user the alert until either |
// a) the problem resolves itself |
// b) the user responds via the dialog |
// c) some other (fatal) error happens |
do |
{ |
// tell the user |
anErr = GXAlertTheUser(pStat); |
// if the paper got suddenly loaded, do an OK |
if (commType == 'PPTL') |
{ |
(void) FetchStatusString(&statusReturn, true, true); |
if ((statusReturn & kOutOfPaperMask) == 0) |
{ |
pStat->dialogResult = ok; |
anErr = noErr; |
} |
} |
} while ((anErr == noErr) && (pStat->dialogResult == 0)); // ORIGINALLY SET TO ==> (pStat->dialogResult == nil) |
// based on the user's response, continue, cancel, or switch to auto feed |
switch ( pStat->dialogResult ) |
{ |
case ok: |
// paper is loaded |
break; |
case cancel: |
// user wishes to stop the printing process |
anErr = gxPrUserAbortErr; |
break; |
case gxAutoFeedButtonId: |
// do rest of job with auto feed |
{ |
gxPaperFeedInfo paperFeed; |
/* Update for job */ |
paperFeed.autoFeed = true; |
(void) AddCollectionItem(jobCollection, gxPaperFeedTag, gxPrintingTagID, sizeof(paperFeed), &paperFeed); |
} |
/* Update as it may be reused */ |
trayFeedInfo.manualFeedThisPage = false; /* Other trayInfo fields are still valid */ |
(void) AddCollectionItem(jobCollection, gxTrayFeedTag, gxPrintingTagID, sizeof(trayFeedInfo), &trayFeedInfo); |
/* Can pass paper type reference as this IS device communication time */ |
anErr = Send_GXCheckStatus((Ptr) &thePaperType, sizeof(thePaperType), 0, 'univ'); |
/* No need to reset tray from gxTrayFeedInfo.feedTrayIndex as there is only one tray! */ |
break; |
} // switch |
// done with the status now |
DisposPtr((Ptr) pStat); |
} |
} // if manual feed job |
// display "sending data to the printer" message |
if (anErr == noErr) |
anErr = GXReportStatus(kDriverStatus, kSendingData); |
nrequire(anErr, FailedWaitForPaper); |
// continue with the standard starting of the page |
anErr = Forward_GXStartSendPage(pageFormat); |
// FALL THROUGH AND HANDLE EXCEPTIONS |
FailedWaitForPaper: |
NewPtrClear: |
FlushAllData: |
FailedGetCollectionItem: |
FetchStatusString: |
return(anErr); |
} // SD_StartSendPage |
/* ------------------------------------------------------------------------------------ */ |
OSErr SD_FinishSendPage() |
{ |
OSErr anErr = noErr; |
Str63 formLength; // should be more than big enough for form skipping |
char len = 0; |
// we may have issued line feeds RIGHT up to the end of the page. If |
// we do that and then issue a form feed, we'll kick out a blank page. |
// to avoid that, we back up a tad and then let the normal form feed |
// go through. Option 2 would be to track each and every motion control |
// we send to the printer -- but that's more work than this. In addition, |
// this method makes sure we are synced up exactly to the hardware |
formLength[len++] = ESCAPE; |
formLength[len++] = 'T'; |
formLength[len++] = '0'; |
formLength[len++] = '1'; |
formLength[len++] = ESCAPE; |
formLength[len++] = 'r'; |
formLength[len++] = 0x0A; |
// reset to forward motion for the form feed |
formLength[len++] = ESCAPE; |
formLength[len++] = 'f'; |
anErr = Send_GXBufferData((char*)&formLength[0], len, gxNoBufferOptions ); |
nrequire(anErr, Send_GXBufferData); |
// Default implementation provides the actual form feed |
anErr = Forward_GXFinishSendPage(); |
// FALL THROUGH EXCEPTION HANDLING |
Send_GXBufferData: |
return(anErr); |
} // SD_FinishSendPage |
/* ------------------------------------------------------------------------------------ */ |
OSErr SD_JobFormatDialog(gxDialogResult* theResult) |
/* |
This message is sent in response to the user's request to put up a formatting dialog |
*/ |
{ |
OSErr anErr; |
gxJobFormatModeTableHdl theJobFormatModeList; |
long i; |
gxJob theJob = GXGetJob(); |
// set up the JobFormatMode information |
anErr = GXGetAvailableJobFormatModes(&theJobFormatModeList); |
if ((!anErr) && (theJobFormatModeList)) |
{ |
for (i = 0; i <= (*theJobFormatModeList)->numModes - 1; ++i) |
{ |
if ((*theJobFormatModeList)->modes[i] == gxTextJobFormatMode) |
{ |
GXSetPreferredJobFormatMode(gxTextJobFormatMode, false); |
break; |
} |
} |
DisposHandle((Handle)theJobFormatModeList); |
} |
// do the normal dialogs after handling the job format mode stuff |
return(Forward_GXJobDefaultFormatDialog(theResult)); |
} // SD_JobFormatModeQuery |
/* ------------------------------------------------------------------------------------ */ |
OSErr SD_JobFormatModeQuery( gxQueryType theQuery, |
void* srcData, |
void* dstData) |
/* |
This message is sent to find out information about the current job format mode. |
*/ |
{ |
OSErr anErr = noErr; |
Handle theFonts; |
Handle theStyles; |
// What type of query is being requested? |
switch(theQuery) |
{ |
case gxSetStyleJobFormatCommonStyleQuery: |
{ |
char *pStyleName; |
// Fetch the list of supported styles |
anErr = Send_GXFetchTaggedDriverData('STR#', kFormatModeStylesID, &theStyles); |
require(anErr == noErr, FailedToLoadStyles1); |
HNoPurge(theStyles); |
HLock(theStyles); |
// Determine which style is being referenced and set the corresponding style (only 2 styles |
// are currently supported) |
if (**((short **) theStyles) == 2) // T => We have the correct number of styles |
{ |
char whichFace = 0; |
pStyleName = ((char *) *theStyles) + sizeof(short); |
if ( IUCompString((ConstStr255Param)pStyleName, (ConstStr255Param)srcData) == 0 ) // T => They want bold face |
{ |
whichFace = bold; |
} |
else |
{ |
// Point to the next name in the list |
pStyleName += *pStyleName + 1; |
if ( IUCompString((ConstStr255Param)pStyleName, (ConstStr255Param)srcData) == 0 ) // T => They want underline face |
{ |
whichFace = underline; |
} |
} |
// If the client specified a valid face, set it now |
if (whichFace != 0) |
{ |
SetStyleCommonFace((gxStyle) dstData, GetStyleCommonFace((gxStyle) dstData) | whichFace); |
} |
} |
// else - something is wrong with our resource |
// Dump the temporary handle |
DisposHandle(theStyles); |
break; |
} |
case gxGetJobFormatFontCommonStylesQuery: |
{ |
short numStyles; |
short i; |
char *pStyleName; |
// Fetch the list of supported styles |
anErr = Send_GXFetchTaggedDriverData('STR#', kFormatModeStylesID, &theStyles); |
require(anErr == noErr, FailedToLoadStyles2); |
HNoPurge(theStyles); |
HLock(theStyles); |
// Determine the number of styles in the list |
numStyles = **((short **) theStyles); |
if (*(Handle *)dstData != nil) // T => Resize the handle to hold info. on the styles |
SetHandleSize(*(Handle *)dstData, sizeof(gxStyleNameTable) + ((numStyles - 1) * sizeof(Str255))); |
else |
*(Handle *)dstData = NewHandle(sizeof(gxStyleNameTable) + ((numStyles - 1) * sizeof(Str255))); |
anErr = MemError(); |
require(anErr == noErr, StyleTableResizeFailed); |
// Now extract the name of each of the supported fonts |
for (i = 1, pStyleName = ((char *) *theStyles) + sizeof(short); i <= numStyles; ++i, pStyleName += *pStyleName + 1) |
{ |
BlockMove(pStyleName, (*((gxStyleNameTableHdl) *(Handle *)dstData))->styleNames[i - 1], *pStyleName + 1); |
} |
(*((gxStyleNameTableHdl) *(Handle *)dstData))->numStyleNames = numStyles; |
// Dump the temporary handle |
DisposHandle(theStyles); |
break; |
} |
case gxGetJobFormatLineConstraintQuery: // This type of query is not supported |
if (*(Handle *)dstData != nil) |
SetHandleSize(*(Handle *)dstData, 0); // Don't return any data |
break; |
case gxGetJobFormatFontsQuery: |
{ |
short numFonts; |
short i; |
char *pFontName; |
// Fetch the list of supported fonts |
anErr = Send_GXFetchTaggedDriverData('STR#', kFormatModeFontsID, &theFonts); |
require(anErr == noErr, FailedToLoadFonts); |
HNoPurge(theFonts); |
HLock(theFonts); |
// Determine the number of fonts in the list |
numFonts = **((short **) theFonts); |
if (*(Handle *)dstData != nil) // T => Resize the handle to hold info. on the fonts |
SetHandleSize(*(Handle *)dstData, sizeof(gxFontTable) + ((numFonts - 1) * sizeof(gxFont))); |
else |
*(Handle *)dstData = NewHandle(sizeof(gxFontTable) + ((numFonts - 1) * sizeof(gxFont))); |
anErr = MemError(); |
require(anErr == noErr, FontTableResizeFailed); |
// Now generate a reference to each of the supported fonts |
for (i = 1, pFontName = ((char *) *theFonts) + sizeof(short); i <= numFonts; ++i, pFontName += *pFontName + 1) |
{ |
gxFont thisFont; |
gxFontTable *pFontTable; |
// ORIGINAL CODE |
// thisFont = FindPNameFont(gxFullFontName, (ConstStr255Param)pFontName); |
// END OF ORIGINAL CODE |
// CODE ADDED IN FROM GX FONTLIBRARY.C AS THINK C WON'T COMPILE LIBRARY |
GXFindFonts(0, gxFullFontName, |
gxMacintoshPlatform, |
gxRomanScript, |
gxEnglishLanguage, |
pFontName[0], (ConstStr255Param)pFontName, 1, 1, &thisFont); |
// ENDCODE ADDED IN FROM LIBRARY |
pFontTable = *((gxFontTableHdl) *(Handle *)dstData); |
pFontTable->fonts[i - 1] = thisFont; |
} |
(*((gxFontTableHdl) *(Handle *)dstData))->numFonts = numFonts; |
// Dump the temporary handle |
DisposHandle(theFonts); |
break; |
} |
case gxGetJobFormatFontConstraintQuery: |
{ |
gxPositionConstraintTable *pPositionTable; |
if ( *(Handle *)dstData != nil) // T => Resize the handle to hold info. on position constraints |
SetHandleSize(*(Handle *)dstData, sizeof(gxPositionConstraintTable) + sizeof(Fixed)); |
else |
*(Handle *)dstData = NewHandle( sizeof(gxPositionConstraintTable) + sizeof(Fixed) ); |
pPositionTable = *((gxPositionConstraintTableHdl) *(Handle *)dstData); |
pPositionTable->phase.x = 0; // Start at the top left corner of the page |
pPositionTable->phase.y = 0; |
pPositionTable->offset.x = ff(12); // Indent from the top left by a six lines per inch margin |
pPositionTable->offset.y = ff(12); |
pPositionTable->numSizes = 2; // Two font sizes supported |
pPositionTable->sizes[0] = ff(10); // 10 pitch |
pPositionTable->sizes[1] = ff(12); // 12 pitch |
break; |
} |
} // switch |
return(anErr); |
/******* Clean-up *******/ |
StyleTableResizeFailed: |
DisposHandle((Handle) theStyles); |
return(anErr); |
FontTableResizeFailed: |
DisposHandle((Handle) theFonts); |
FailedToLoadStyles1: |
FailedToLoadStyles2: |
FailedToLoadFonts: |
return(anErr); |
} // SD_JobFormatModeQuery |
//<FF> |
/* ------------------------------------------------------------------------------------ */ |
OSErr SD_SetupImageData( |
gxRasterImageDataHdl hImageData) // raster image data stuff |
/* |
This message is called to setup the constant data used for imaging the entire job. |
*/ |
{ |
SpecGlobalsHdl hGlobals = GetMessageHandlerInstanceContext(); |
OSErr anErr; |
gxRasterImageDataPtr pImageData; |
Boolean isJobNotFinalQuality; |
Boolean isTextJobFormatMode; |
long imagewriterOptions; |
short count; |
Handle draftTable; |
CMYKRenderCollection planes; |
long itemSize = sizeof(CMYKRenderCollection); |
Collection jobCollection; |
gxJob theJob; |
gxSetColor theColors[9]; |
gxColorSet theSet; |
short idx; |
gxPlaneSetupRec *pOnePlane; |
// do the default setup |
anErr = Forward_GXSetupImageData(hImageData); |
nrequire(anErr, Forward_GXSetupImageData); |
// test for 'final' quality mode |
isJobNotFinalQuality = !JobIsBest(&imagewriterOptions); |
// test for textJobFormatMode |
isTextJobFormatMode = ( GXGetJobFormatMode( GXGetJob() ) == gxTextJobFormatMode); |
// if the job is not final quality or using textJobFormatMode, |
// downgrade the imaging data to our lower quality |
if (isJobNotFinalQuality || isTextJobFormatMode) |
{ |
// |
// ROUGH OR TEXT MODE |
// |
// dereference for size and speed |
pImageData = *hImageData; |
// image at 80 or 72 dpi |
if (imagewriterOptions & kSuperRes) |
pImageData->hImageRes = ff(80); |
else |
pImageData->hImageRes = ff(72); |
pImageData->vImageRes = ff(72); |
// textJobFormatMode loads up the draft table, else setup halftones |
if (isTextJobFormatMode) |
{ |
anErr = Send_GXFetchTaggedDriverData('idft', gxPrintingDriverBaseID, &draftTable); |
nrequire(anErr, FailedToLoadDraftTable); |
// store away the draft table |
(**hGlobals).draftTable = draftTable; |
// Download something? |
anErr = Send_GXFetchTaggedDriverData('idft', gxPrintingDriverBaseID+1, &draftTable); |
if (anErr == resNotFound) |
{ |
draftTable = nil; |
anErr = noErr; |
} |
nrequire(anErr, GetDownloadTable); |
if (draftTable) |
{ |
HLock(draftTable); |
anErr = Send_GXBufferData(*draftTable, GetHandleSize(draftTable), gxDontSplitBuffer); |
DisposHandle(draftTable); |
nrequire(anErr, SendDownloadTable); |
} |
} |
else |
{ |
// use dither level that will look better at 72 dpi |
// resolution than our default values (MAYBE: 4 is the default now anyway) |
pImageData->theSetup.planeSetup[0].planeHalftone.method = 4; |
// of course, turn off color matching when in non-final mode! |
pImageData->theSetup.planeSetup[0].planeProfile = nil; |
} |
if (isJobNotFinalQuality) |
{ |
if (imagewriterOptions & kSuperRes) |
{ |
// use bidirectional instead of unidirectional |
// and also <esc>N instead of <esc>p for quality mode |
pImageData->packageControls.startPageStringID = gxPrintingDriverBaseID+3; |
} |
else |
{ |
// use bidirectional instead of unidirectional |
// and also <esc>n instead of <esc>p for quality mode |
pImageData->packageControls.startPageStringID = gxPrintingDriverBaseID+2; |
} |
} |
// packaging data |
pImageData->packagingInfo.headHeight = 8; // 8 pins (instead of 16) |
pImageData->packagingInfo.numberPasses = 1; // in 1 head pass (instead of 2) |
pImageData->packagingInfo.passOffset = 0; // with no space between passes |
} |
else |
{ |
// |
// FINAL QUALITY |
// |
// dereference for size and speed |
pImageData = *hImageData; |
// image at 160 or 144 dpi |
if (imagewriterOptions & kSuperRes) |
{ |
pImageData->hImageRes = ff(160); |
pImageData->packageControls.startPageStringID = gxPrintingDriverBaseID+1; |
} |
else |
{ |
pImageData->hImageRes = ff(144); |
pImageData->packageControls.startPageStringID = gxPrintingDriverBaseID+0; |
} |
} |
// not a color ribbon? Setup for black and white - do a B/W halftone rather than a dither |
if (!PrinterHasColorRibbon(GXGetJobOutputPrinter(GXGetJob()))) |
{ |
// dereference for size and speed |
pImageData = *hImageData; |
// one plane, no color flags, move the halftone info up into correct position |
pImageData->theSetup.planes = 1; |
pImageData->theSetup.depth = 1; |
pImageData->packagingInfo.colorPasses = 1; |
pImageData->packagingInfo.packageOptions = 0; |
pImageData->theSetup.planeSetup[0].planeSpace = gxNoSpace; |
pImageData->theSetup.planeSetup[0].planeSet = nil; |
pImageData->theSetup.planeSetup[0].planeProfile = nil; |
pImageData->theSetup.planeSetup[0].planeOptions = gxDefaultOffscreen; |
pImageData->theSetup.planeSetup[0].planeHalftone.method = gxRoundDot; |
pImageData->theSetup.planeSetup[0].planeHalftone.tintSpace = gxRGBSpace; |
} |
else |
{ |
// It has a color ribon, so if it has a render options collection, then set up the options |
// approriately. |
// First, see if the job has render options |
theJob = GXGetJob(); |
jobCollection = GXGetJobCollection(theJob); |
if (!GetCollectionItem( jobCollection, |
kCMYKRenderCollectionType, kCMYKRenderCollectionID, |
&itemSize, &planes)) |
{ |
pImageData = *hImageData; |
if (planes.renderMode == kDitherIt) |
{ |
// |
// Dither setup |
// |
// Create a color set |
for (idx = 0; idx < 9; ++idx) |
{ |
// default the color to black |
theColors[idx].rgba.red |
= theColors[idx].rgba.green |
= theColors[idx].rgba.blue |
= theColors[idx].rgba.alpha = 0x0000; |
// and give it componants to go along with this index |
if ( (idx == 0) |
|| (idx == 1) |
|| (idx == 2) |
|| (idx == 3) |
|| (idx == 7)) |
theColors[idx].rgba.red = 0xFFFF; |
if ( (idx == 0) |
|| (idx == 1) |
|| (idx == 4) |
|| (idx == 5) |
|| (idx == 7)) |
theColors[idx].rgba.green = 0xFFFF; |
if ( (idx == 0) |
|| (idx == 2) |
|| (idx == 4) |
|| (idx == 6) |
|| (idx == 7)) |
theColors[idx].rgba.blue = 0xFFFF; |
} |
theSet = GXNewColorSet(gxRGBSpace, 9, theColors); |
// Step through each plane, setting up the appropriate values |
for (count = 0; count < 4; ++count) |
{ |
// dereference for size and speed |
pOnePlane = &(pImageData->theSetup.planeSetup[count]); |
// Set up the options for dithering |
pOnePlane->planeOptions = gxDontSetHalftone + gxDotTypeIsDitherLevel; |
// Angle and frequency are not used when dithering |
pOnePlane->planeHalftone.angle = 0; |
pOnePlane->planeHalftone.frequency = 0; |
// When the planeOption's gxDotTypeIsDitherLevel flag is set, then planeHalftone.method |
// is used for the dither level |
pOnePlane->planeHalftone.method = planes.ditherLevel; |
// Set default for luminance |
pOnePlane->planeHalftone.tinting = gxLuminanceTint; |
// Set up the dot color |
pOnePlane->planeHalftone.dotColor.space = gxRGBSpace; |
pOnePlane->planeHalftone.dotColor.profile = nil; |
// default each plane to 0 |
pOnePlane->planeHalftone.dotColor.element.rgba.red |
= pOnePlane->planeHalftone.dotColor.element.rgba.green |
= pOnePlane->planeHalftone.dotColor.element.rgba.blue |
= pOnePlane->planeHalftone.dotColor.element.rgba.alpha |
= 0; |
if ( (count == 0) |
|| (count == 1)) |
pOnePlane->planeHalftone.dotColor.element.rgba.red = 0xFFFF; |
if ( (count == 0) |
|| (count == 2)) |
pOnePlane->planeHalftone.dotColor.element.rgba.green = 0xFFFF; |
if ( (count == 2) |
|| (count == 3)) |
pOnePlane->planeHalftone.dotColor.element.rgba.blue = 0xFFFF; |
// Set up the background dot color |
pOnePlane->planeHalftone.backgroundColor.space = gxRGBSpace; |
pOnePlane->planeHalftone.backgroundColor.profile = nil; |
pOnePlane->planeHalftone.backgroundColor.element.rgba.red |
= pOnePlane->planeHalftone.backgroundColor.element.rgba.green |
= pOnePlane->planeHalftone.backgroundColor.element.rgba.blue |
= pOnePlane->planeHalftone.backgroundColor.element.rgba.alpha |
= 0xFFFF; |
// Set up for an RGB tint space |
pOnePlane->planeHalftone.tintSpace = gxRGBSpace; |
// And an indexed plane space |
pOnePlane->planeSpace = gxIndexedSpace; |
// Set up the plane to the same as the one we just created |
pOnePlane->planeSet = theSet; |
// no color profile so set to nil |
pOnePlane->planeProfile = nil; |
} |
} |
else |
{ |
// |
// Half-Tone setup |
// |
for (count = 0; count < 4; ++count) |
{ |
// dereference for size and speed |
pOnePlane = &(pImageData->theSetup.planeSetup[count]); |
// Set up the options for halftonning...gxDefaultOffscreen |
pOnePlane->planeOptions = gxDefaultOffscreen; |
// Set this dot type from the collection |
pOnePlane->planeHalftone.method = planes.dotType; |
// Set this planes angle and frequency from the collection |
// and the appropriate tint for this plane |
switch (count+1) { |
case (kYellowPass) : |
pOnePlane->planeHalftone.angle = planes.angles [kRenderOptsYellow]; |
pOnePlane->planeHalftone.frequency = planes.frequency[kRenderOptsYellow]; |
pOnePlane->planeHalftone.tinting = gxComponent3Tint; |
break; |
case (kMagentaPass) : |
pOnePlane->planeHalftone.angle = planes.angles [kRenderOptsMagenta]; |
pOnePlane->planeHalftone.frequency = planes.frequency[kRenderOptsMagenta]; |
pOnePlane->planeHalftone.tinting = gxComponent2Tint; |
break; |
case (kCyanPass) : |
pOnePlane->planeHalftone.angle = planes.angles [kRenderOptsCyan]; |
pOnePlane->planeHalftone.frequency = planes.frequency[kRenderOptsCyan]; |
pOnePlane->planeHalftone.tinting = gxComponent1Tint; |
break; |
case (kBlackPass) : |
pOnePlane->planeHalftone.angle = planes.angles [kRenderOptsBlack]; |
pOnePlane->planeHalftone.frequency = planes.frequency[kRenderOptsBlack]; |
pOnePlane->planeHalftone.tinting = gxComponent4Tint; |
break; |
} |
// Set up the dot color to black |
pOnePlane->planeHalftone.dotColor.space = gxRGBSpace; |
pOnePlane->planeHalftone.dotColor.profile = nil; |
pOnePlane->planeHalftone.dotColor.element.rgba.red |
= pOnePlane->planeHalftone.dotColor.element.rgba.green |
= pOnePlane->planeHalftone.dotColor.element.rgba.blue |
= pOnePlane->planeHalftone.dotColor.element.rgba.alpha |
= 0; |
// Set up the background dot color to white |
pOnePlane->planeHalftone.backgroundColor.space = gxRGBSpace; |
pOnePlane->planeHalftone.backgroundColor.profile = nil; |
pOnePlane->planeHalftone.backgroundColor.element.rgba.red |
= pOnePlane->planeHalftone.backgroundColor.element.rgba.green |
= pOnePlane->planeHalftone.backgroundColor.element.rgba.blue |
= pOnePlane->planeHalftone.backgroundColor.element.rgba.alpha |
= 0xFFFF; |
// Set up for an CMYK tint space |
pOnePlane->planeHalftone.tintSpace = gxCMYKSpace; |
// No explicit color space |
pOnePlane->planeSpace = gxNoSpace; |
// No color set |
pOnePlane->planeSet = nil; |
// No profile specified |
pOnePlane->planeProfile = nil; |
} |
} |
} |
} |
return(noErr); |
// EXCEPTION HANDLING |
SendDownloadTable: |
GetDownloadTable: |
DisposHandle((**hGlobals).draftTable); |
(**hGlobals).draftTable = nil; |
FailedToLoadDraftTable: |
Forward_GXSetupImageData: |
return(anErr); |
} // SD_SetupImageData |
/* ------------------------------------------------------------------------------------ */ |
OSErr SD_FetchDriverData( |
ResType theType, |
short theID, |
Handle* theData) |
{ |
OSErr anErr; |
anErr = Forward_GXFetchTaggedDriverData(theType, theID, theData); |
// do the translation at the proper DPI by modifying the old API |
// customization resource |
if ( (anErr == noErr) && // got the resource okay |
(theType == 'cust') && // it was a customization resource |
(theID == -8192) ) // with the old API id |
{ |
long imagewriterOptions; |
if (!JobIsBest(&imagewriterOptions)) |
{ |
**((short**)theData) = 72; |
**((short**)theData+1) = 72; |
} |
} |
return(anErr); |
} // SD_FetchDriverData |
/* ------------------------------------------------------------------------------------ */ |
OSErr SD_RenderPage( gxFormat theFormat, |
gxShape thePage, |
gxPageInfoRecord *pageInfo, |
gxRasterImageDataHdl imageInfo) |
/* |
The message sent to render an entire page. |
*/ |
{ |
OSErr theError = noErr; |
// if not text mode, do it the normal (raster) way |
if (GXGetJobFormatMode(GXGetJob()) != gxTextJobFormatMode) |
{ |
gxRectangle paperSize; |
Str63 formLength; // should be more than big enough for form skipping |
char aNumber[8]; |
char len = 0; |
short formLen; // form length (in 144 dpi) |
short i; |
// find out how big our paper is |
GXGetPaperTypeDimensions(GXGetFormatPaperType(theFormat), nil, &paperSize); |
// determine the left margin (in pixels) |
{ |
SpecGlobalsHdl hGlobals = GetMessageHandlerInstanceContext(); |
SpecGlobalsPtr pGlobals; |
gxRasterImageDataPtr pImageData; |
// dereference for size and speed |
pImageData = *imageInfo; |
pGlobals = *hGlobals; |
paperSize.left += ff(18); // ImageWriter's can't go tighter than .25 inch |
if (paperSize.left > 0) |
paperSize.left = 0; |
pGlobals->leftMargin = FixedToInt( |
FixMul(-paperSize.left, |
FixDiv(pImageData->hImageRes, ff(72)))); |
// set this to be the top of form |
formLength[len++] = ESCAPE; |
formLength[len++] = 'v'; |
// set the form length to be the size of the page iff ImageWriterII |
if (pGlobals->isImageWriterII) |
{ |
formLength[len++] = ESCAPE; |
formLength[len++] = 'H'; |
formLen = FixedToInt(FixMul(paperSize.bottom-paperSize.top, ff(2)) ); // length is set in 144 dpi |
NumToString(formLen, (unsigned char*)aNumber); |
for (i = 0; i < 4-aNumber[0]; ++i) |
formLength[len++] = '0'; |
for (i = 1; i <= aNumber[0]; ++i) |
formLength[len++] = aNumber[i]; |
} |
} |
// NOW: move over the top margin |
formLen = -FixedToInt( FixMul(paperSize.top, ff(2)) ); |
// Forward line feed |
formLength[len++] = ESCAPE; |
formLength[len++] = 'f'; |
// send multiples of 99 |
if (formLen >= 99) |
{ |
formLength[len++] = ESCAPE; |
formLength[len++] = 'T'; |
formLength[len++] = '9'; |
formLength[len++] = '9'; |
while (formLen >= 99) |
{ |
formLength[len++] = 0x0A; // line feed |
formLen -= 99; |
} |
} |
// send remaining line feeds |
if (formLen > 0) |
{ |
formLength[len++] = ESCAPE; |
formLength[len++] = 'T'; |
NumToString(formLen, (unsigned char*)aNumber); |
if (aNumber[0] == 1) |
{ |
formLength[len++] = '0'; |
formLength[len++] = aNumber[1]; |
} |
else |
{ |
formLength[len++] = aNumber[1]; |
formLength[len++] = aNumber[2]; |
} |
formLength[len++] = 0x0A; // line feed |
} |
// we've got all of this data, now send it |
theError = Send_GXBufferData((char*)formLength, len, gxNoBufferOptions ); |
nrequire(theError, SetFormLength); |
// continue with normal rendering |
theError = Forward_GXRenderPage(theFormat, thePage, pageInfo, imageInfo); |
} |
else |
{ |
theError = PrintPageInDraftMode(thePage, imageInfo); |
} |
failed_WrongShape: |
SetFormLength: |
return(theError); |
} // SD_RenderPage |
//<FF> |
/* ------------------------------------------------------------------------------------ */ |
/* SPECIFIC DRIVER RASTER OVERRIDES */ |
/* ------------------------------------------------------------------------------------ */ |
OSErr SD_LineFeed ( |
long *lineFeedSize, // amount to line feed by |
Ptr buffer, unsigned long * bufferPos, // data goes here |
gxRasterImageDataHdl hImageData) // raster image data stuff |
/* |
Message is sent to output paper advance commands to the printer |
*/ |
{ |
OSErr anErr; |
Boolean amLowRes; |
long actualLineFeed = *lineFeedSize; |
amLowRes = ((**hImageData).vImageRes == ff(72)); |
// if we are in low res mode, we double the line feed size, as all ImageWriter |
// line feed commands are expressed at 144 dpi. |
if (amLowRes) |
*lineFeedSize <<= 1; |
// optimize small motions (particularlly -1 followed by +1 with no data between) |
// into groups. This gets rid of the "paper dance" for blank colors passes. |
{ |
SpecGlobalsHdl hGlobals = GetMessageHandlerInstanceContext(); |
SpecGlobalsPtr pGlobals = *hGlobals; |
if ( (pGlobals->packagingOptions == kDoSmallLineFeeds) || |
(*lineFeedSize < -1) || |
(*lineFeedSize > 1) ) |
{ |
*lineFeedSize += pGlobals->lineFeeds; |
pGlobals->lineFeeds = 0; |
// do the line feed in the default way |
anErr = Forward_GXRasterLineFeed(lineFeedSize, buffer, bufferPos, hImageData); |
} |
else |
{ |
pGlobals->lineFeeds += *lineFeedSize; |
*lineFeedSize = 0; |
anErr = noErr; |
} |
} |
// and if in low quality mode, we divide the result to make up for the multiplication |
// that we do above |
if (amLowRes) |
*lineFeedSize >>= 1; |
return(anErr); |
} // SD_LineFeed |
//<FF> |
//------------------------------------------------------------------- |
// PrintThisBand |
// |
// Gets the render options collection, and tests the current printer |
// band against it to see if it needs to draw this band |
//------------------------------------------------------------------- |
Boolean PrintThisBand ( short printerBand ) |
{ |
Boolean displayPlane = true; |
OSErr theErr; |
CMYKRenderCollection planes; |
Collection jobCollection; |
long itemSize = sizeof(CMYKRenderCollection); |
gxJob theJob; |
theJob = GXGetJob(); |
jobCollection = GXGetJobCollection(theJob); |
theErr = GetCollectionItem( jobCollection, |
kCMYKRenderCollectionType, kCMYKRenderCollectionID, |
&itemSize, &planes); |
nrequire(theErr, FailedGetCollectionItem); |
if (!theErr) |
{ |
switch (printerBand){ |
case kYellowPass : displayPlane = planes.yellowIsOn; |
break; |
case kMagentaPass : displayPlane = planes.magentaIsOn; |
break; |
case kCyanPass : displayPlane = planes.cyanIsOn; |
break; |
case kBlackPass : displayPlane = planes.blackIsOn; |
break; |
} |
} |
FailedGetCollectionItem: |
return displayPlane; |
} |
//<FF> |
/* ------------------------------------------------------------------------------------ */ |
// This macro stores one group into the pointer: |
// P = Pointer to fill into |
// G = Character for group |
// S = Length of group run in pixels |
#define EMITGROUP(P, G, S) \ |
P->cEscape = ESCAPE; \ |
P->cCommand = G; \ |
Long2Dec(S, P->cLineLength); |
//<FF> |
/* ------------------------------------------------------------------------------------ */ |
OSErr SD_PackageBitmap ( |
gxRasterPackageBitmapRec *pPackage, |
Ptr buffer, // data goes here + bufferPos |
unsigned long *bufferPos, // how much of the buffer already is full |
gxRasterImageDataHdl hImageData) // private image data |
/* |
Packages a bitmap for the ImageWriter |
This routine is called in order to add your rotated and packaged pixel |
data to the buffer. It is called once for each head pass. This routine |
is pretty complex because it also does IW run length compression. |
It has also been modified so it can handle color seperations. It does this by |
by comparing the color of the current pass to the render options collection. |
This is done by PrintThisBand(). |
It must do the following: |
1) Start filling the buffer from buffer+bufferPos. Remember |
that this pointer may not be word aligned - so be careful |
assigning things into it. |
2) If your printer does SetMargins, put a "fake" set of commands at |
the begining of the data. Since most of the time you don't |
know the margins, you can save away the value of the bufferPos, |
and backpatch it after you have finished with the offscreen. |
SetMargins is used on printers that allow you to not send starting |
and ending whitespace. |
3) Add in the rotated data for your printer. The data to stuff starts |
at location startY in hOffscreen. Stuff the bits from here until |
you reach startY+<your band size>, which is the size of your single |
band in this resolution mode. Increment your number by |
<your pass offset> + 1, which is the number of microspaces |
you will send between head passes to form this band. Take care |
that you don't step off of the end of the offscreen in this operation, |
you may be called with startY at the end of the offscreen if the first part |
of the band is white. |
colorBand contains the color band which you should be stuffing, from |
1 to the number of color passes your printer needs (usually 4). |
Pack in the correct color band. The packager will call you once |
for each color band, and correctly handle line feeds and backward |
line feeds to do the correct thing. If your printer takes full |
RGB or CYMK data, I would define your number of colors to be |
1 and pack the full RGB or CYMK data with the one call to StuffBuffers. |
If you request kSendAllColors in your raster pack resource, then this |
message will always be called for all color passes, even those that |
do not have data on them. For some printers, this is useful. For the |
case of the ImageWriter, this option is not specified, so this message |
will only be sent for colors that actually have dirty bits within them. |
4) Backpatch SetMargins from your saved value in step 2) now that you |
know the margins. |
5) Increment bufferPos by the number of bytes you have |
added to the buffer. Be sure to take into account the Set Margins |
command if you added one. |
*/ |
{ |
// #pragma unused (isColorDirty) |
OSErr anErr; // would you beleive we could make mistakes? |
ScanLinePtr pTheScanLine; // Pointer to the start of scan line data |
unsigned short lastDirtyCol; // Last dirty part of the scan line |
unsigned short firstDirty; // First dirty pixel |
unsigned short lastDirty; // Last dirty pixel |
Boolean bandIsDirty; // Is this band dirty? |
unsigned short numberBytesAdded; // Number of bytes we have added to the row |
unsigned short repeatCount; // Number of times we have seen this bitmap |
register unsigned short whichCol; // Index into the scan line data |
register unsigned short x,y; // Index values into the offscreen |
register Ptr thePtr; // Pointer to each Y scanline |
register unsigned char tempColumn; // Placeholder for the working column |
unsigned char lastColumn; // What was in the contents of the last column? |
Ptr basePtr; // Pointer to current X byte |
unsigned char outputMask; // Mask of bit to set in rotated image |
unsigned char inputMask; // Mask of bit to look at in X |
unsigned char startingInputMask; // Mask of first bit of interest |
unsigned short yPointerOffset; // Increment pointer by this to get to next scanline |
unsigned short endY, endX, incrY; // To remove loop invariants. |
unsigned short packingColor; // number of colors packing |
unsigned long originalBufferPos; // where we were in the buffer before we started; |
long originalLineFeeds; // how many line feeds did we have before? |
SpecGlobalsHdl hGlobals; |
SpecGlobalsPtr pGlobals; |
SetMarginsPtr marginBuffer; |
// Get the globals |
hGlobals = GetMessageHandlerInstanceContext(); |
pGlobals = *hGlobals; |
// save away original position in order to do a restore should the band be clean |
originalBufferPos = *bufferPos; |
originalLineFeeds = pGlobals->lineFeeds; |
if (originalLineFeeds == 0) |
{ |
anErr = noErr; |
} |
else |
{ |
// if we have any extra line feeds saved up, do them now! |
pGlobals->lineFeeds = 0; |
pGlobals->packagingOptions = kDoSmallLineFeeds; |
anErr = Send_GXRasterLineFeed((long*)&originalLineFeeds, buffer, bufferPos, hImageData); |
pGlobals = *hGlobals; |
pGlobals->packagingOptions = kNoPackagingOptions; |
} |
nrequire(anErr, SendInitialLineFeeds); |
// See if we need to print this color band |
if ( ( (*hImageData)->packagingInfo.colorPasses == 4) |
&& (! PrintThisBand ( pPackage->colorBand )) ) |
bandIsDirty = false; |
else |
{ |
// Set color if ImageWriterII |
if ((**hGlobals).isImageWriterII) |
{ |
pTheScanLine = (ScanLinePtr) (buffer + kSetMarginsSize + (*bufferPos)); |
// Set color mode for this scan line, if needed |
pTheScanLine->cColorEscape = ESCAPE; |
pTheScanLine->cSetColorCommand = kSetColorCommand; |
packingColor = (*hImageData)->packagingInfo.colorPasses; |
if (packingColor == 4) |
{ |
switch (pPackage->colorBand) |
{ |
case kYellowPass: |
pTheScanLine->cColor = '1'; |
startingInputMask = 0x10; |
break; |
case kMagentaPass: |
pTheScanLine->cColor = '2'; |
startingInputMask = 0x20; |
break; |
case kCyanPass: |
pTheScanLine->cColor = '3'; |
startingInputMask = 0x40; |
break; |
case kBlackPass: |
pTheScanLine->cColor = '0'; |
startingInputMask = 0x80; |
break; |
} |
} |
else |
{ |
pTheScanLine->cColor = '0'; |
startingInputMask = 0x80; |
} |
} |
else // Backup to eliminate cColorEscape, cSetColorCommand and cColor |
{ |
pTheScanLine = (ScanLinePtr) (buffer + kSetMarginsSize + (*bufferPos) - 3); |
packingColor = (*hImageData)->packagingInfo.colorPasses; |
startingInputMask = 0x80; |
} |
// Start with the first bit in the offscreen |
inputMask = startingInputMask; |
// We start out with no dirty bits |
firstDirty = 0; |
lastDirty = 0; |
bandIsDirty = false; |
// Set our array index to zero |
whichCol = 0; |
lastDirtyCol = 0; |
numberBytesAdded = 0; |
// Set up RLL variables |
repeatCount = 0; |
lastColumn = 0; |
// Get the byte pointer for the start of this color band |
basePtr = pPackage->bitmapToPackage->image; |
// Get the byte pointer for the start of the first scan line |
basePtr += pPackage->startRaster * pPackage->bitmapToPackage->rowBytes; |
// Save away loop invariants |
endY = pPackage->startRaster + (*hImageData)->packagingInfo.headHeight; // Ending scan line |
incrY = (*hImageData)->packagingInfo.passOffset + 1; // Number of scanlines to increment by |
endX = pPackage->dirtyRect.right; // Ending X pos |
yPointerOffset = incrY * pPackage->bitmapToPackage->rowBytes; // amount to add to the input |
// pointer to move to the next scanline |
// If the ending position is too large for the bitmap we have been given, |
// truncate it, so that we don't print garbage |
if (endY > pPackage->bitmapToPackage->height) |
endY = pPackage->bitmapToPackage->height; |
// For the entire width of the offscreen, move a rolling mask along in the |
// X direction, rotating up 8 bits of Y data per column. In addition, compress |
// runs of columns that are > 14 length. |
for (x = 0; x < endX; x++) |
{ |
// The bits in this column are clear to begin with |
tempColumn = 0; |
// Which byte to look at in the input buffer |
thePtr = basePtr; |
// Where to place the bit in the output. The ImageWriter takes the bit |
// pattern upside down. |
outputMask = 0x01; |
// Scan through this band, setting each of the 8 bits == the bit in that scan line |
for (y = pPackage->startRaster; y < endY; y += incrY) |
{ |
// If we have a bit in the input, rotate it into the output |
if ((*thePtr) & inputMask) |
tempColumn |= outputMask; |
// move onto next position in the output data |
outputMask <<= 1; |
// move onto the next scan line in the input data |
thePtr += yPointerOffset; |
} // for y |
// Save the column info |
pTheScanLine->iTheData[whichCol] = tempColumn; |
// Get the next bit from the current pointer |
inputMask >>= packingColor; |
if (!inputMask) |
{ |
// If we run out of bits, get the next byte |
basePtr++; |
// And reset the bit mask to the first bit |
inputMask = startingInputMask; |
} |
// If we have some form of data |
if (tempColumn != 0) |
{ |
if (!bandIsDirty) |
{ |
// This is the first dirty pixels we have so far |
bandIsDirty = true; |
firstDirty = x; |
} |
// This is also the last dirty pixels so far |
lastDirty = x; |
} // SetDirty |
// If we have some dirty bits |
if (bandIsDirty) |
{ |
// Move on to the next column |
whichCol++; |
// If this is a dirty column, then it is the last one so far |
if (tempColumn != 0) |
lastDirtyCol = whichCol; |
// If we have a duplication, up the repeat count |
if (tempColumn == lastColumn) // if (false) // turn off repeat groups |
{ |
repeatCount++; |
if (repeatCount == 14) |
{ |
// Kick out the old group |
whichCol -= 14; |
EMITGROUP(pTheScanLine, kGraphicsCommand, whichCol); |
numberBytesAdded += whichCol + kGroupSize; |
pTheScanLine = (ScanLinePtr)(((Ptr) pTheScanLine) + |
whichCol + kGroupSize); |
whichCol = 1; |
lastDirtyCol = 1; |
pTheScanLine->iTheData[0] = tempColumn; |
} |
} |
else |
{ |
// If we were repeating, emit the repeat group |
if (repeatCount >= 14) |
{ |
EMITGROUP(pTheScanLine, kRepeatGroup, repeatCount); |
numberBytesAdded += 1 + kGroupSize; |
pTheScanLine = (ScanLinePtr) (((Ptr) pTheScanLine) + 1 + kGroupSize); |
whichCol = 1; |
lastDirtyCol = 1; |
pTheScanLine->iTheData[0] = tempColumn; |
} |
repeatCount = 0; |
lastColumn = tempColumn; |
} |
} // BandIsDirty |
} // end of loop for width of bitmap |
} |
// if we have a dirty band - emit the final bit of data we have packaged up |
if (bandIsDirty) |
{ |
// Set the margins to be the first and last dirty pixels in the scan line - |
// the ImageWriter only does left margin optimization. |
{ |
// Get the location for placing the set margin command |
marginBuffer = (SetMarginsPtr) (buffer + (*bufferPos)); |
// Stuff in the set margin command |
marginBuffer->cEscape = ESCAPE; |
marginBuffer->cCommand = kSetMarginsCommand; |
// convert left margin into ASCII and place it at the start of the buffer |
Long2Dec((**hGlobals).leftMargin + firstDirty, (Ptr)(marginBuffer->cIndentDistance)); |
} |
// Send the last group command |
if (repeatCount < 14) |
{ |
// Emit a normal group |
EMITGROUP(pTheScanLine, kGraphicsCommand, lastDirtyCol); |
numberBytesAdded += lastDirtyCol + kGroupSize; |
} |
else |
{ |
// Don't stuff a final repeat group if it's blank space |
if (tempColumn != 0) |
{ |
// Emit a repeat group |
EMITGROUP(pTheScanLine, kRepeatGroup, repeatCount); |
numberBytesAdded += 1 + kGroupSize; |
} |
} // end of repeatCount < 14 |
// Increment the count of the buffer by bytes added for groups, plus |
// the header, if any, plus the set margins command |
(*bufferPos) += numberBytesAdded + kScanLineSize + kSetMarginsSize; |
// and put a <cr> at the end of the line |
*(char*)(buffer + (*bufferPos)) = 0x010D; |
(*bufferPos) += 1; |
} // bandIsDirty |
else |
{ |
// Band is not dirty, so don't output data as we didn't have any! |
*bufferPos = originalBufferPos; |
// restore original number of line feeds |
pGlobals = *hGlobals; |
pGlobals->lineFeeds = originalLineFeeds; |
} |
// always return your errors! |
SendInitialLineFeeds: |
return(anErr); |
} // SD_PackageBitmap |
//=================================================================== |
// Print dialog handling stuff |
//=================================================================== |
// Dialog panel items constants |
#define kDitherRB 1 |
#define kHalfToneRB 2 |
#define k_Dth_DotPU 3 |
#define k_HT_DotShapePU 4 |
#define k_HT_AngleTE0 5 |
#define k_HT_FreqTE0 6 |
#define k_HT_AngleTE1 7 |
#define k_HT_FreqTE1 8 |
#define k_HT_AngleTE2 9 |
#define k_HT_FreqTE2 10 |
#define k_HT_AngleTE3 11 |
#define k_HT_FreqTE3 12 |
#define k_Dth_Box 20 |
#define k_HT_Box 21 |
//------------------------------------------------------------------- |
// SD_JobPrintDialog |
// |
// overides : GXJobPrintDialog |
// |
// Called by the system when the print dialog is being created. |
// This gives us a chance to add out own panels. This done by |
// SetUpPrintPanel. |
//------------------------------------------------------------------- |
OSErr SD_JobPrintDialog(gxDialogResult *dlogResult ) |
{ |
OSErr theErr = noErr; |
theErr = SetUpPrintPanel( ); |
if (!theErr) |
theErr = Forward_GXJobPrintDialog(dlogResult); |
return theErr; |
} |
//------------------------------------------------------------------- |
// SD_HandlePanelEvent |
// |
// overides : HandlePanelEvent |
// |
// Messages are passed by the system to SD_HandlePanelEvent so it can |
// do any special processing while the print panel is up. We use this |
// to set up any custom items or gray fields, etc. Because we've |
// added two panels, we need to check from which panel the message |
// has been passed. This can be found in gxPanelInfoRecord.panelResId |
//------------------------------------------------------------------- |
OSErr SD_HandlePanelEvent( gxPanelInfoRecord *panelInfo ) |
{ |
OSErr theErr = noErr; |
GrafPtr oldPort; |
DialogPtr pDlg; |
CMYKRenderCollection planes; |
Collection jobCollection; |
long itemSize = sizeof(CMYKRenderCollection); |
gxJob theJob; |
pDlg = panelInfo->pDlg; |
GetPort(&oldPort); |
SetPort(pDlg); |
switch (panelInfo->panelEvt) |
{ |
case gxPanelOpenEvt: |
// Every time a panel is opened, set up any items on it that may need setting. |
if (panelInfo->panelResId == kDitherPanelID) |
OpenDitherPanel(pDlg, panelInfo->itemCount); |
break; |
case gxPanelHitEvt: |
// If the user clicks the dither or half-tone buttons then we need to gray |
// out items appropriately. As We're using a gxExtendedDITLType resource |
// -'xdtl' to handle the collection, we don't need to worry about changing |
// the state of the dialog box then writting back the collection. We get the |
// collection so we can see if the state has changed, this saves on unneeded |
// setting of dialog items states and possible flicker. |
if (panelInfo->panelResId == kDitherPanelID) |
{ |
theJob = GXGetJob(); |
jobCollection = GXGetJobCollection(theJob); |
theErr = GetCollectionItem( jobCollection, |
kCMYKRenderCollectionType, kCMYKRenderCollectionID, |
&itemSize, &planes); |
if (!theErr) |
switch (panelInfo->itemHit - panelInfo->itemCount) |
{ |
case kDitherRB: |
case kHalfToneRB: |
GrayDitherDialog ( pDlg, panelInfo->itemCount, |
planes.renderMode == kDitherIt ); |
break; |
} |
} |
break; |
} |
SetPort(oldPort); |
return theErr; |
} |
//------------------------------------------------------------------- |
// SetUpPrintPanel |
// |
// This creates the render options collection and adds it to a print |
// job.Then it adds two extra panels to the print panel. |
//------------------------------------------------------------------- |
OSErr SetUpPrintPanel( void ) |
{ |
OSErr theErr = noErr; |
gxPanelSetupRecord panelSetupRec; |
CMYKRenderCollection planesConfig; |
Collection jobCollection; |
long itemSize = sizeof(CMYKRenderCollection); |
// Get the job collection and then try to find the |
// render collection item in there. |
jobCollection = GXGetJobCollection(GXGetJob()); |
theErr = GetCollectionItem( jobCollection, |
kCMYKRenderCollectionType, kCMYKRenderCollectionID, |
&itemSize, &planesConfig); |
// If the collection item doesnÕt yet exist, create a new |
// item, set it up with default values, and add it to the |
// job collection |
if (theErr == collectionItemNotFoundErr) |
{ |
// Turn all planes on |
planesConfig.cyanIsOn = true; |
planesConfig.magentaIsOn = true; |
planesConfig.yellowIsOn = true; |
planesConfig.blackIsOn = true; |
// Print in half tone mode |
planesConfig.renderMode = kHalfToneIt; |
// Put default values into the dither parameters |
planesConfig.ditherLevel = 4; |
// Put default values into the half tone parameters |
planesConfig.dotType = gxRoundDot; |
planesConfig.angles [kRenderOptsYellow] = 0x000F0000; |
planesConfig.frequency[kRenderOptsYellow] = 0x002D0000; |
planesConfig.angles [kRenderOptsCyan] = 0x003C0000; |
planesConfig.frequency[kRenderOptsCyan] = 0x002D0000; |
planesConfig.angles [kRenderOptsMagenta] = 0x00000000; |
planesConfig.frequency[kRenderOptsMagenta] = 0x002D0000; |
planesConfig.angles [kRenderOptsBlack] = 0x002D0000; |
planesConfig.frequency[kRenderOptsBlack] = 0x002D0000; |
// Add the new collection to the job |
theErr = AddCollectionItem ( jobCollection, |
kCMYKRenderCollectionType, |
kCMYKRenderCollectionID, |
sizeof(CMYKRenderCollection), |
&planesConfig); |
} |
nrequire(theErr, GetSettings_Failed); |
// Set up the two panels: store the ID of the panel resource to use, |
// the resource file in which it is located, and the type of panel |
// that is being stored. |
panelSetupRec.panelResId = kSeperationPanelID; |
panelSetupRec.resourceRefNum = GXGetMessageHandlerResFile(); |
panelSetupRec.refCon = 0; |
panelSetupRec.panelKind = gxDriverPanel; |
theErr = GXSetupDialogPanel(&panelSetupRec); |
panelSetupRec.panelResId = kDitherPanelID; |
panelSetupRec.resourceRefNum = GXGetMessageHandlerResFile(); |
panelSetupRec.refCon = 0; |
panelSetupRec.panelKind = gxDriverPanel; |
theErr = GXSetupDialogPanel(&panelSetupRec); |
GetSettings_Failed: |
return theErr; |
} |
//------------------------------------------------------------------- |
// OpenDitherPanel |
// |
// Called by SD_HandlePanelEvent every time it is sent a |
// gxPanelOpenEvt for the kDitherPanelID. Need to get the collection |
// and gray out any items as appropriate. Also add the frame user |
// items. |
//------------------------------------------------------------------- |
OSErr OpenDitherPanel ( DialogPtr pDlg, short itemCount ) |
{ |
CMYKRenderCollection planes; |
OSErr theErr = noErr; |
long itemSize = sizeof(CMYKRenderCollection); |
Collection jobCollection; |
gxJob theJob; |
Handle hItem; |
Rect itemRect; |
short itemType; |
// Get the job |
theJob = GXGetJob(); |
// Get the collection |
jobCollection = GXGetJobCollection(theJob); |
// Get the render options collection |
theErr = GetCollectionItem( jobCollection, |
kCMYKRenderCollectionType, kCMYKRenderCollectionID, |
&itemSize, &planes); |
nrequire(theErr, FailedGetCollectionItem); |
// Gray out the items depending on the render mode |
GrayDitherDialog ( pDlg, itemCount, |
planes.renderMode == kDitherIt ); |
// Add the frame items |
GetDItem(pDlg, itemCount + k_Dth_Box, &itemType, &hItem, &itemRect); |
SetDItem(pDlg, itemCount + k_Dth_Box, itemType, (Handle)DrawBoundBox, &itemRect); |
GetDItem(pDlg, itemCount + k_HT_Box, &itemType, &hItem, &itemRect); |
SetDItem(pDlg, itemCount + k_HT_Box, itemType, (Handle)DrawBoundBox, &itemRect); |
FailedGetCollectionItem: |
return theErr; |
} |
//------------------------------------------------------------------- |
// SetDialogCtrlGray |
// |
// Sets a control dialog item to gray. |
//------------------------------------------------------------------- |
void SetDialogCtrlGray ( DialogPtr pDlg, short theItem, Boolean isEnable) |
{ |
Handle hItem; |
Rect itemRect; |
short itemType; |
GetDItem(pDlg, theItem, &itemType, &hItem, &itemRect); |
if (isEnable) |
HiliteControl ((ControlHandle)hItem, 0); |
else |
HiliteControl ((ControlHandle)hItem, 255); |
} |
//------------------------------------------------------------------- |
// GrayDitherDialog |
// |
// Gray out unused items in the hilite control panel dependent on |
// whether the dialog is half-tonning or drawing in color. |
//------------------------------------------------------------------- |
void GrayDitherDialog ( DialogPtr pDlg, short itemCount, Boolean isDither) |
{ |
if (isDither) |
{ |
SetDialogCtrlGray ( pDlg, itemCount + k_Dth_DotPU, true); |
SetDialogCtrlGray ( pDlg, itemCount + k_HT_DotShapePU, false); |
} |
else |
{ |
SetDialogCtrlGray ( pDlg, itemCount + k_Dth_DotPU, false); |
SetDialogCtrlGray ( pDlg, itemCount + k_HT_DotShapePU, true); |
} |
} |
//------------------------------------------------------------------- |
// DrawBoundBox |
// |
// Draws a Rectangle user item in a dialog |
//------------------------------------------------------------------- |
pascal void DrawBoundBox( WindowPtr theWindow, short theItem ) |
{ |
Handle hItem; |
Rect itemRect; |
short itemType; |
GetDItem(theWindow, theItem, &itemType, &hItem, &itemRect); |
FrameRect ( &itemRect ); |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-14