Technote 1144Writing Custom Hoses For LaserWriter 8.6by Richard Blanchard and Ingrid KellyUpdated by Dave Polaschek Apple Worldwide Developer Technical Support |
CONTENTS
Identifying a DTP's Type |
LaserWriter 8, Versions 8.6 and later, supports printing to a variety of desktop printer (DTP) types, including PAP, LPR and IrDA. Each desktop printer can have its own method of communicating with its associated physical printer, RIP, or other post-printing processor. LaserWriter 8.6, through the invention of custom “hoses,” added the ability for shared libraries to implement various communication methods and for these libraries to be loaded dynamically based upon a DTP’s type. This Technote gives an overview of the custom hose specification for developers. |
Associated with each LaserWriter 8 desktop printer is a LaserWriter 8 DTP TypesThe PrintingLib that ships with LaserWriter 8, versions 8.6.5 and later, supports the following DTP types: 'PAP '
Communication with the printer is performed using AppleTalk’s Printer Access Protocol (PAP). The printer’s AppleTalk name, type, and zone are stored in the compatibility portion of the '=Hld'The hold desktop printer is unique in that there is no associated communications module for it. A hold desktop printer never converts the desktop spool file to PostScript, but instead simply queues the spool file. To print a spool file queued to a hold desktop printer, the user must move the spool file into the queue of another type of desktop printer. '=Fil'A translator desktop printer writes its PostScript, EPS, or PDF output to a file. |
Note: |
'=LPR'An LPR desktop printer uses the Unix LPD protocol to communicate over TCP/IP with a print server. For more information on this protocol, please see RFC 1179. '=Cst'The custom application desktop printer writes its PostScript to disk and then launches an application to post-process the PostScript job. See Technote 1113: Customizing the Desktop Printer Utility for additional information on custom DTPs. '=Ird'The PostScript job is transmitted using an infrared link to an IrDA-capable printer. LaserWriter 8 uses the IrDA specification as outlined at http://www.irda.org/. '=USB'The PostScript job is transmitted over the Universal Serial Bus (USB). More information about USB is available at <http://www.usb.org/>. |
Note: |
Adding Hose Plug-insWhen printing a job to a desktop printer, LaserWriter 8 obtains the four-byte DTP type and then looks for a shared library containing the matching hose. LaserWriter 8 searches for the library in the following order: 1. In the System’s "Printing Plug-ins" folder (in the Extensions folder)2. In the PrintingLib file
A desktop printer’s type is obtained using the
Plug-in files managed by the Printing Plug-ins Manager, such as custom hoses, are required to have a resource of type
The |
The 'PLGN'
resource is as follows:
type 'PLGN' { // see Printing Plug-ins Manager Spec. integer = $$Countof(LibInfo); // number of libraries wide array LibInfo { unsigned longint; // Type this library handles unsigned longint; // SubType this library handles pstring; // Library Name align word; }; }; |
PluginLibInfo
structure is as follows:
typedef struct PluginLibInfo{ SettingsDataType type; SettingsDataSubType subtype; unsigned char libraryName[]; // pascal string // word aligned }PluginLibInfo; |
A ResEdit |
Hose fragments are required to export a single entry point. This entry point, |
OSStatus hoseOpen(HoseInfo *hoseInfo, const BufCallbacks *callbacks, Collection hints, Handle papaH); |
The hoseOpen routine’s primary job is to fill out the structure pointed to by hoseInfo. |
/* The HoseInfo structure is filled out by a hoseOpen procedure. The structure describes the buffer requirements of the hose as well as the function pointers for reading, writing, and closing the hose. */ typedef struct{ HoseOutProc out; // Called to write a buffer. HoseInProc in; // Called to read a buffer. HoseIdleProc idle; // Called periodically. HoseCloseProc close; // Called to close the connection. HoseConnProc connState; // This procedure returns the state // of the current connection. HoseStatusProc status; // Return the hose's current // status string. HoseDisposeProc dispose; // The hose should free up all // of the memory it allocated. Size bufSize; // The size of each allocated data buffer. long minBufs; // The hose requires this many buffers. If // there isn't enough memory to allocate them, // the client will return memFullErr. long maxBufs; // Never allocate more buffers than this. void *refcon; // A pointer that will be passed to the hose // routines. }HoseInfo; |
First the hose needs to fill in the
Before calling the hose to transmit data, the hose client provides buffering to improve performance. Because of this buffering, the hose must deal with only one transmit buffer and one receive buffer at a time. The hose client is responsible for allocating the buffers. The hose indicates the size of the buffers its client should allocate by filling in the
The callbacks parameter to |
typedef void (*FinishedWriteProc) (MemQElemPtr memElem, OSStatus err); typedef void (*FinishedReadProc) (MemQElemPtr memElem, OSStatus err); typedef struct{ FinishedWriteProc finishedWrite; FinishedReadProc finishedRead; }BufCallbacks; |
The hose’s client places two pointers to native functions in this structure. These functions are to be called by the hose when a read or write is completed. For more information on these function pointers, see the detailed description of
As part of the |
/* When generating PostScript for the output stream, the PostScript converter will by default use, if needed, characters in the range 0x80-0xFF inclusive. Use the 'kHintEighthBitTag' with a value of 'false' to prevent the converter from emitting bytes with the high bit set. */ #define kHintEighthBitTag 'bit8' #define kHintEighthBitId 1 #define kHintEighthBitVariableType Boolean #define kHintEighthBitDefault true /* When generating PostScript for the output stream, the PostScript converter will by default use, if needed, characters in the range 0x00-0x1F inclusive. Use the 'kHintTransparentChannelTag' with a value of 'false' to prevent the converter from emitting bytes less than 0x20. */ #define kHintTransparentChannelTag 'trns' #define kHintTransparentChannelId 1 #define kHintTransparentChannelVariableType Boolean #define kHintTransparentChannelDefault true |
Typically, the hints collection passed to For hoses which communicate through a channel that has attributes more restrictive than these defaults, the hose must add the appropriate collection item(s) to the hints collection to ensure that the hose client only writes bytes in the supported range. Note that only hints which are more restrictive than the defaults need to be added. If a hose supports full 8-bit communications, it need not add these hints to the hints collection passed in.
In some cases, the hints collection passed to |
OSErr err; // e.g. this hose cannot transmit the high 8 bit kHintEighthBitVariableType eightBit = false; // unlock the hint if it is already there // this is OK if the hint is already unlocked err = SetCollectionItemInfo(hints, kHintEighthBitTag, kHintEighthBitId, collectionLockMask, 0); // if the hint isn’t already there that’s fine if(err == collectionItemNotFoundErr) err = noErr; if(!err){ err = AddCollectionItem(hints, kHintEighthBitTag, kHintEighthBitId, sizeof(eightBit), &eightBit); if(!err) err = SetCollectionItemInfo(hints, kHintEighthBitTag, kHintEighthBitId, collectionLockMask, collectionLockMask); } |
See Inside Macintosh: QuickDraw GX Environment and Utilities for more information on the Collection Manager.
The last parameter to
In its |
typedef enum{ kConnClosed = 0, // Start in this state. kConnOpening, // This is the state while we wait for the // printer to accept the connection. kConnOpen, // This is the state while we do reads and // writes to the printer. kConnClosing // This is the state while we wait for the // connection to close. }ConnState; typedef ConnState (*HoseConnProc)(void *refcon); |
The hose's connection procedure should return the constant HoseOutProc
The primary purpose of a hose is to transmit data. This is accomplished by the hose’s client through calls to
and thus the hose’s function is:
The refcon parameter passed to
The second parameter to |
typedef struct MemQElem{ QElemPtr qLink; // Used by Enqueue and Dequeue- private. short qType; // Our constant (kMemQueueType) to // identify our queues-private. struct BufIO *bufIO; // So we can recover buffer information- // private. Byte *buf; // Pointer to the allocated buffer. SInt32 maxBytes; // The size of the block pointed to // by 'buf' SInt32 nBytes; // Number of valid bytes in 'buf'. Boolean eoj; // true if the data is followed by an end // of job. Boolean inQOnly; // This buffer should be used only for // the input routines- private. }MemQElem, *MemQElemPtr; |
Note: |
The hose client uses the
Again, it is highly recommended that hoses perform their writes and reads in an asynchronous manner. In this case, |
typedef void (*FinishedWriteProc)(MemQElem *memElem, OSStatus err); |
When making the
The call to HoseInProc
If a hose is managing a unidirectional communications channel, the hose need not have a routine for reading data. In this case, the
If the hose can read data from the printer, it fills the in field of the |
typedef OSStatus (*HoseInProc)(void *refcon, MemQElem memElem); |
As with |
typedef void (*FinishedReadProc)(MemQElem memElem, OSStatus err); |
As with data writes, once the HoseIdleProc
Not all hoses are able to use asynchronous completion routines to note the end of a read or write. To help these hoses, a |
typedef OSStatus (*HoseIdleProc)(void *refcon); |
HoseStatusProc
While a hose is open, the hose’s client may periodically request that the hose query the printer for status. When the call is made, the hose should copy a Pascal string describing the printer’s last known status into the buffer pointed to by |
typedef OSStatus (*HoseStatusProc)(void *refcon, StringPtr statusStr); |
Note: |
For some communications channels, such as serial channels, the status from a printer is returned on the back channel read by the HoseCloseProc
When the client is done with the hose, it calls the hose’s
HoseDisposeProc
After
If there was an error during an asynchronous close, the Hose Type RegistrationsTo avoid conflicting hose types, we ask that you register your custom hose 4-byte type by sending an email to devprograms@apple.com. Please send the following information to register your custom hose type:
|
|
Thanks to John Blanchard, Paul Danbold, David Gelphman, and C.S. Lin.