Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1580 lines
45 KiB

/*************************** Module Header **********************************
* udphyscl.c
* Functions associated with sending data to the printer, and physical
* head movements.
*
* HISTORY:
* 09:58 on Wed 16 Jan 1991 -by- Lindsay Harris [lindsayh]
* From the windows 16 unidriv file physical.c; adpated for NT
*
* Copyright (C) 1991 - 1993 Microsoft Corporation
*
*****************************************************************************/
#include <stddef.h>
#include <windows.h>
#include <winddi.h>
#include "libproto.h"
#include "win30def.h"
#include "udmindrv.h"
#include "udpfm.h"
#include "uddevice.h"
#include "udresrc.h"
#include "pdev.h"
#include "udresid.h"
#include "udrender.h"
#include <memory.h>
#include <stdarg.h>
#include <winspool.h>
#include "rasdd.h"
#include "stretch.h"
#include "udfnprot.h"
#include "oem.h"
#include "params.h"
/*
* Local function prototypes.
*/
static int _itoa( LPSTR, int );
void SendCmd( UD_PDEV *, LPCD, int * );
/**************************** Function Header ********************************
* XMoveTo
* Performs X movement operations. This may involve sending a move
* command to the printer. Precise operation depends upon the flag
* bits set in fFlag.
* NOTE: The permissible values for fFlag are defined in uddevice.h.
* They are: MV_GRAPHICS, MV_FINE, MV_UPDATE, MV_RELATIVE, MV_PHYSICAL.
*
* RETURNS:
* Difference between requested position and that which was possible.
*
* HISTORY:
* 10:18 on Mon 07 Jun 1993 -by- Lindsay Harris [lindsayh]
* Use relative movement commands IF requested AND available.
*
* 11:34 on Fri 15 Nov 1991 -by- Lindsay Harris [lindsayh]
* Renamed flags, cleaned it up etc.
*
* Created LinS
* 8/22/90 Updated comment block
*****************************************************************************/
int
XMoveto( pUDPDev, iXIn, fFlag )
UD_PDEV *pUDPDev; /* Access to all */
int iXIn; /* In master units unless MV_GRAPHICS is set. */
int fFlag; /* Specify the operations to be done */
{
int iX; /* The absolute position */
int diff = 0;
int iDiff2; /* Second version of above */
if( fFlag & MV_GRAPHICS )
{
/* Convert to master units */
iXIn = (iXIn << pUDPDev->Resolution.ptScaleFac.x) *
pUDPDev->Resolution.ptTextScale.x;
}
/*
* Since our print origin may not correspond with the printer's,
* there are times when we need to adjust the value passed in to
* match the desired location on the page. This should not happen unless
* there is a relative move, or the move is actually relative to
* the printer's origin.
*/
iX = iXIn; /* Keep the input value for relative moves */
if( !(fFlag & (MV_RELATIVE | MV_PHYSICAL)) )
iX += pUDPDev->pfPaper.ptPrintOrig.x;
/* Relative moves just add to our current position. */
if( fFlag & MV_RELATIVE )
iX += pUDPDev->ctl.ptCursor.x; /* Relative to where we are */
/*
* X is now in master units, and is the value to use relative to
* the printer's origin.
*/
if( fFlag & MV_UPDATE )
{
pUDPDev->ctl.ptCursor.x = iX;
return 0;
}
if( pUDPDev->ctl.ptCursor.x == iX )
return 0;
if( (fFlag & (MV_RELATIVE | MV_USEREL)) == MV_USEREL )
{
/* Have an absolute position, but caller wants relative move */
fFlag |= MV_RELATIVE; /* Catch next piece of code */
iXIn = iX - pUDPDev->ctl.ptCursor.x;
}
if( (fFlag & MV_RELATIVE) && !(pUDPDev->fMode & PF_NO_RELX_MOVE) )
{
/* Use the relative move command, which is available. */
/*
* Note that the silliness involved with laserjet font rotations
* means that we have to fiddle with what we send. Basically,
* the LJ rotates the coordinate axes to allow fonts to be
* rotated by multiples of 90 degrees. SO, although we are
* fiddling with the Windows X coordinate, we may end up sending
* a relative Y move command to the printer!
*/
switch( pUDPDev->ctl.iRotate )
{
case 2: /* Upside down, right to left */
iXIn = -iXIn;
/* FALL THROUGH */
case 0: /* Normal */
if( iXIn > 0 )
diff = WriteChannel( pUDPDev, CMD_CM_XM_REL, iXIn );
else
diff = WriteChannel( pUDPDev, CMD_CM_XM_RELLEFT, -iXIn );
break;
case 1: /* Text runs towards Windows origin */
iXIn = -iXIn;
/* FALL THROUGH */
case 3: /* Text runs AWAY from Windows origin */
if( iXIn > 0 )
diff = WriteChannel( pUDPDev, CMD_CM_YM_REL, iXIn );
else
diff = WriteChannel( pUDPDev, CMD_CM_YM_RELUP, iXIn );
break;
}
/*
* diff must be the positioning error, since we only come
* here if both of the above commands exist. SO, calculate
* the new position and be gone!
*/
pUDPDev->ctl.ptCursor.x = iX - diff;
if( fFlag & MV_GRAPHICS )
{
/*
* Return units the caller is working with.
*/
diff = (diff / pUDPDev->Resolution.ptTextScale.x) >>
pUDPDev->Resolution.ptScaleFac.x;
}
return diff;
}
/*
* If a CR has to be sent before each X-move escape, the mini-driver
* writer should embed CR inside the X-move command!
*/
if( iX == 0 )
{
WriteChannel( pUDPDev, CMD_CM_CR );
pUDPDev->ctl.ptCursor.x = 0;
return 0;
}
if( pUDPDev->fMode & PF_NO_X_MOVE_CMD )
{
/*
* We assume that when XMoveto is called, the current font is always
* the default font IF the printer has no X movement command.
*/
int relx = iX - pUDPDev->ctl.ptCursor.x ;
int iDefWidth;
/* convert into master units */
iDefWidth = pUDPDev->ptDefaultFont.x * pUDPDev->Resolution.ptTextScale.x;
if( relx < 0 )
{
WriteChannel( pUDPDev, CMD_CM_CR );
relx = iX;
}
// If it's a textonly minidriver, move the cursor using blanks.
if(pUDPDev->pdh->fTechnology == GPC_TECH_TTY)
{
while( relx >= iDefWidth )
{
WriteSpoolBuf( pUDPDev, (LPSTR)" ", 1 );
relx -= iDefWidth;
}
fFlag &= ~MV_FINE; /* Clear the MV_FINE Flag */
diff = 0;
}
else
{
/* the remaining partial space is done via FineXMoveTo. */
diff = relx;
fFlag |= MV_FINE; /* Use graphics mode to reach point */
}
}
else
{
/*
* X movement commmands are available, so use them.
*/
/* use absolute movements whenever it is available */
if( (diff = WriteChannel( pUDPDev, CMD_CM_XM_ABS, iX )) == NOOCD )
{
if( iX < pUDPDev->ctl.ptCursor.x )
{
/* Move left - if possible */
iDiff2 = WriteChannel( pUDPDev, CMD_CM_XM_RELLEFT,
pUDPDev->ctl.ptCursor.x - iX );
if( iDiff2 == NOOCD )
{
/* No relative left movement, use <CR> to reach start */
WriteChannel( pUDPDev, CMD_CM_CR );
diff = iX;
}
else
diff = iDiff2; /* What's left over */
}
else /* Relative right move */
// -1 is an invalid position - set to zero
diff = iX - ( (pUDPDev->ctl.ptCursor.x == -1) ?
0: pUDPDev->ctl.ptCursor.x) ;
if( diff )
{
iDiff2 = WriteChannel( pUDPDev, CMD_CM_XM_REL, diff );
if( iDiff2 != NOOCD )
diff = iDiff2; /* Genuine result! */
}
}
}
/*
* If fine movement is required, now is the time to do it!.
*/
if( (fFlag & MV_FINE) && diff > 0 )
diff = FineXMoveto( pUDPDev, diff );
pUDPDev->ctl.ptCursor.x = iX - diff;
if( fFlag & MV_GRAPHICS )
{
diff = (diff / pUDPDev->Resolution.ptTextScale.x) >>
pUDPDev->Resolution.ptScaleFac.x;
}
/* Return the error in the position setting */
return diff;
}
/*************************** Function Header ********************************
* FineXMoveTo
* This function is called to make microspace justification.
* It is only called when the normal x movement commands cannot
* move the cursor to the asking position. For example,
* resolution is 180 DPI, x move command is 1/120". To move
* by 4 pixels in 180 DPI, CM_XM_RIGHT is sent with parameter = 2
* (1/120") then one graphics pixel is sent (1/180).
* 4/180 = 2/120 + 1/180.
* 'x' is always in MasterUnits.
*
* Return: the difference between current position and the asking x
* position
*
* History: Created LinS
* 8/22/90 Updated comment block
*
* 10/12/93 Normanh
* Return immediately if GDI style graphics is being used
*****************************************************************************/
int
FineXMoveto( pUDPDev, x )
UD_PDEV * pUDPDev;
int x; /* Master units, amount to move */
{
BYTE rgch[ CCHMAXBUF ];
int tmp;
WORD fFunc;
int rem;
/*
* !!!LindsayH - this function should only be used for dot matrix printers.
* It will cause no end of chaos on a LaserJet, and it will print black
* lines on a PaintJet! Sending a string of zeroes is VERY dubious.
*/
//normanh Agreed, it is. We happen on this with CaPSL so let's
//get out of here
if (pUDPDev->Resolution.fDump & RES_DM_GDI)
return x;
if( x < 1 )
{
/* This should never happen, BUT is disastrous if not caught */
#if DBG
if( x < 0 )
RIP( "rasdd!FineXMoveto with -ve move" );
#endif
return 0;
}
/*
* Use graphics to make fine x movements
* if( pUDPDev->rgfn[FN_XMOVETO] & FNF_XM_USE_GRAPHICS )
* don't know any other way.
*/
/* back to device units */
rem = x % pUDPDev->Resolution.ptTextScale.x;
x /= pUDPDev->Resolution.ptTextScale.x;
/* Send fine movement command */
WriteChannel( pUDPDev, CMD_RES_SENDBLOCK, x );
/* One additional byte for RES_BO_3BYTESIN4 set */
fFunc = pUDPDev->Resolution.fBlockOut;
x *= pUDPDev->ctl.sBytesPerPinPass + (fFunc & RES_BO_3BYTESIN4 ? 1: 0);
ZeroMemory( rgch, x > CCHMAXBUF ? CCHMAXBUF : x );
/* write out so many zeros */
// If it's a textonly minidriver, don't send anything
if(pUDPDev->pdh->fTechnology != GPC_TECH_TTY)
{
for( ; x > 0; x -= tmp )
{
tmp = x > CCHMAXBUF ? CCHMAXBUF : x;
WriteSpoolBuf( pUDPDev, (LPSTR)rgch, tmp );
}
}
return rem;
}
/************************* Function Header **********************************
* YMoveTo
* Change the Y position. This may result in sending commands to
* the printer, and in some printers may result in changing the
* X position too. For dot matrix printers, Y move is defined
* as being the direction the paper moves. The exact operation
* performed here depends upon the value of fFlag. The permissible
* values are defined in uddevice.h, and consist of these values:
* MV_UPDATE, MV_RELATIVE, MV_GRAPHICS, MV_PHYSICAL
*
* RETURNS:
* The difference between desired and achievable position.
*
*
* HISTORY:
* Tuesday November 30 1993 -by- Norman Hendley [normanh]
* Provided support for resolution dependent y-movement
*
* 15:21 on Mon 07 Jun 1993 -by- Lindsay Harris [lindsayh]
* Properly respect the MV_RELATIVE flag by using relative moves.
*
* 11:57 on Fri 15 Nov 1991 -by- Lindsay Harris [lindsayh]
* Cleaned it up, changed flag values etc.
*
* History: Created LinS
* 8/22/90 Updated comment block
*****************************************************************************/
int
YMoveto( pUDPDev, iYIn, fFlag )
UD_PDEV *pUDPDev;
int iYIn; /* In text units unless MV_GRAPHICS is set. */
int fFlag; /* Modify behaviour */
{
int fYMove;
int diff;
int iY;
if( fFlag & MV_GRAPHICS )
{
/* convert to text units */
iYIn = (iYIn << pUDPDev->Resolution.ptScaleFac.y) *
pUDPDev->Resolution.ptTextScale.y;
}
iY = iYIn; /* For starters, anyway! */
/* add Y print origin to get target cursor position, if necessary. */
if( !(fFlag & (MV_RELATIVE | MV_PHYSICAL)) )
iY += pUDPDev->pfPaper.ptPrintOrig.y;
if( fFlag & MV_RELATIVE )
iY += pUDPDev->ctl.ptCursor.y;
/* only update y postion */
if( fFlag & MV_UPDATE )
{
pUDPDev->ctl.ptCursor.y = iY;
return 0;
}
if( iY == pUDPDev->ctl.ptCursor.y )
return 0;
fYMove = pUDPDev->fYMove;
//New for GPC3. Convert Y & our CAP to graphics units
//We convert back before returning
if (fYMove & CM_YM_RES_DEPENDENT)
{
if (pUDPDev->Resolution.ptTextScale.y != 0)
{
iY /= pUDPDev->Resolution.ptTextScale.y;
iYIn /= pUDPDev->Resolution.ptTextScale.y;
pUDPDev->ctl.ptCursor.y /= pUDPDev->Resolution.ptTextScale.y;
}
iY >>= pUDPDev->Resolution.ptScaleFac.y;
iYIn >>= pUDPDev->Resolution.ptScaleFac.y;
pUDPDev->ctl.ptCursor.y >>= pUDPDev->Resolution.ptScaleFac.y;
}
if( (fFlag & (MV_USEREL | MV_RELATIVE)) == MV_USEREL )
{
/* Have absolute position coming in, but relative move is wanted */
fFlag |= MV_RELATIVE; /* Force relative move command */
iYIn = iY - pUDPDev->ctl.ptCursor.y;
}
if( (fFlag & MV_RELATIVE) && !(pUDPDev->fMode & PF_NO_RELY_MOVE) )
{
/*
* Caller wants relative move, and we can do it, so use the
* printer's relative move commands.
*/
/*
* NOTE: As this part of the code is used almost exclusively
* from DrvTextOut, we consider the font rotation operations.
* Thus, although we are adjusting OUR Y position, we may
* end up adjusting the PRINTER's X position.
*/
switch( pUDPDev->ctl.iRotate )
{
case 2:
iYIn = -iYIn; /* Text is upside down */
/* FALL THROUGH */
case 0:
if( iYIn > 0 )
diff = WriteChannel( pUDPDev, CMD_CM_YM_REL, iYIn );
else
diff = WriteChannel( pUDPDev, CMD_CM_YM_RELUP, -iYIn );
break;
case 1: /* Text runs towards y == 0 */
iYIn = -iYIn;
/* FALL THROUGH */
case 3:
if( iYIn > 0 )
diff = WriteChannel( pUDPDev, CMD_CM_XM_REL, iYIn );
else
diff = WriteChannel( pUDPDev, CMD_CM_XM_RELLEFT, -iYIn );
break;
}
/*
* diff must be the positioning error, since we only come
* here if both of the above commands exist. SO, calculate
* the new position and be gone!
*/
pUDPDev->ctl.ptCursor.y = iY - diff;
if( fFlag & MV_GRAPHICS )
{
/*
* Return units the caller is working with.
*/
diff = (diff / pUDPDev->Resolution.ptTextScale.y) >>
pUDPDev->Resolution.ptScaleFac.y;
}
if (fYMove & CM_YM_RES_DEPENDENT) //restore to text units
{
pUDPDev->ctl.ptCursor.y <<= pUDPDev->Resolution.ptScaleFac.y;
diff <<= pUDPDev->Resolution.ptScaleFac.y;
if (pUDPDev->Resolution.ptTextScale.y != 0)
{
diff *= pUDPDev->Resolution.ptTextScale.y;
pUDPDev->ctl.ptCursor.y *= pUDPDev->Resolution.ptTextScale.y;
}
}
return diff;
}
if( fYMove & CM_YM_FAV_ABS )
diff = WriteChannel( pUDPDev, CMD_CM_YM_ABS, iY );
else
{
/* use relative Y-move commands. */
/*
* Send CR if CM_YM_CR bit is set which means either X position has
* been lost after a Y-move escape or it is required to send a CR
* before each line-spacing command.
*/
if( fYMove & CM_YM_CR )
XMoveto( pUDPDev, 0, MV_PHYSICAL );
/* use line spacing */
if( (fYMove & CM_YM_LINESPACING) && iY > 0 )
{
DEVICECMD dcmd;
register short yParam;
/* deal in master units */
dcmd.param = (short)(iY - pUDPDev->ctl.ptCursor.y);
MasterToDevice( pUDPDev, CMD_CM_YM_LINESPACING, &dcmd );
if( dcmd.max == 0 )
{
/* 0 means no limit, so set it to a big value! */
dcmd.max = 0x7fff;
}
while( dcmd.param )
{
yParam = dcmd.param > dcmd.max ? dcmd.max : dcmd.param;
if( yParam != pUDPDev->ctl.sLineSpacing )
{
WriteChannel( pUDPDev, CMD_CM_YM_LINESPACING, yParam );
/* in device unit */
pUDPDev->ctl.sLineSpacing = yParam;
}
WriteChannel( pUDPDev, CMD_CM_LF );
dcmd.param -= yParam;
}
diff = dcmd.rem;
}
else
{
if( iY <= pUDPDev->ctl.ptCursor.y )
{
if( (diff = WriteChannel( pUDPDev, CMD_CM_YM_RELUP,
pUDPDev->ctl.ptCursor.y - iY)) == NOOCD)
return 0;
diff = -diff;
}
else
diff = WriteChannel( pUDPDev, CMD_CM_YM_REL, iY -
( (pUDPDev->ctl.ptCursor.y == -1) ? 0 : pUDPDev->ctl.ptCursor.y ));
}
}
pUDPDev->ctl.ptCursor.y = iY - diff;
if (fYMove & CM_YM_RES_DEPENDENT) //restore to text units
{
pUDPDev->ctl.ptCursor.y <<= pUDPDev->Resolution.ptScaleFac.y;
diff <<= pUDPDev->Resolution.ptScaleFac.y;
if (pUDPDev->Resolution.ptTextScale.y != 0)
{
diff *= pUDPDev->Resolution.ptTextScale.y;
pUDPDev->ctl.ptCursor.y *= pUDPDev->Resolution.ptTextScale.y;
}
}
if( fFlag & MV_GRAPHICS )
return diff >> pUDPDev->Resolution.ptScaleFac.x;
else
return diff;
}
/************************** Function Header **********************************
* WriteSpoolBuf
* This is an intermediate routine for Unidrv to send characters to
* the printer via the spooler. All characters must be sent through
* the WriteSpool( ) call. WriteSpoolBuf is internal so that Unidrv
* can buffer up short command streams before calling WriteSpool.
* This routine also checks for error flags returned from WriteSpool.
*
* RETURNS:
* Number of bytes written; 0 on error. Errors may be late.
*
* HISTORY:
* 18:56 on Thu 27 Aug 1992 -by- Lindsay Harris [lindsayh]
* ABORT processing.
*
* 16:52 on Fri 06 Mar 1992 -by- Lindsay Harris [lindsayh]
* Updated to return error status after WritePrinter() failure.
*
* Created LinS
* 8/22/90 Updated comment block
*****************************************************************************/
#ifdef RASDDPERF
BOOL gbSpool = 1;
#endif
int
WriteSpoolBuf( pUDPDev, lpBuf, cch )
UD_PDEV * pUDPDev;
BYTE *lpBuf; /* pointer to buffer containing data to be sent */
int cch; /* number of bytes to send */
{
DWORD dw;
#ifdef RASDDPERF
if (!gbSpool)
return(cch);
#endif
/*
* Check for aborted output.
*/
if( pUDPDev->fMode & PF_ABORTED )
return 0;
/*
* If the output buffer will fill up this time, flush what is
* currently in the buffer. Then, if the output is larger
* than the buffer, skip the buffering operation and call the
* output function directly.
*/
if( pUDPDev->cbSpool + cch > CCHSPOOL )
{
/* flush the buffer and write directly to the spooler */
if( !FlushSpoolBuf( pUDPDev ) )
return 0;
}
if( cch >= CCHSPOOL )
{
/*
* Write is for more bytes than the buffer can hold - bypass
* the buffering, write the output directly.
*/
if( !WritePrinter( pUDPDev->hPrinter, lpBuf, cch, &dw ) )
{
/* Write failed ... */
pUDPDev->cbSpool = 0;
pUDPDev->fMode |= PF_ABORTED;
cch = 0; /* Return error */
}
}
else
{
/* buffer up the output */
CopyMemory( pUDPDev->pbOBuf + pUDPDev->cbSpool, lpBuf, cch );
pUDPDev->cbSpool += cch;
}
return cch;
}
/************************* Function Header **********************************
* FlushSpoolBuf
* Flush our internal buffer. Checks for whether the job has been
* aborted, or an I/O error. In either case, the output is discarded,
* and we set a flag to stop further output.
*
* RETURNS:
* TRUE/FALSE, FALSE on error/abort.
*
* HISTORY:
* 17:11 on Thu 27 Aug 1992 -by- Lindsay Harris [lindsayh]
* Was here, changed to macro, reinstated as function.
*
****************************************************************************/
BOOL
FlushSpoolBuf( pUDPDev )
UD_PDEV *pUDPDev;
{
// This whole abort thing needs to be looked at
// We should not simply stop sending data,
// but we need to make sure we clean up the printer as well.
// Besides, I removed setting the pUDPDev->pso in all the code.
// This needs to be fixed - DaveSn
#ifdef LATER
/*
* First check for previous or new abort operation.
*/
if( (pUDPDev->fMode & PF_ABORTED) || EngCheckAbort( (SURFOBJ *)pUDPDev->pso) )
{
/* No more! */
pUDPDev->fMode |= PF_ABORTED; /* Could be the first time */
return FALSE;
}
#endif
/*
* Not aborted, so OK to write this data out.
*/
if( pUDPDev->cbSpool )
{
DWORD dw;
/* Have data, will write */
if( !WritePrinter( pUDPDev->hPrinter, pUDPDev->pbOBuf, pUDPDev->cbSpool, &dw ) )
{
/* I/O error, so set flag to stop further output */
pUDPDev->fMode |= PF_ABORTED;
return FALSE;
}
pUDPDev->cbSpool = 0; /* This data has gone */
}
return TRUE;
}
/************************* Function Header **********************************
* nGetNumParams
* Given a command, get the number of parameters associated with it.
*
* RETURNS:
* (int) Number of commands
*
****************************************************************************/
int
nGetNumParams(
int cmd)
{
int i;
int num = 0;
for (i = 0; i < MAXCMDINDEX; i++) {
if (gbNumParams[i].nID == cmd) {
num = gbNumParams[i].nParamCount;
break;
}
}
return(num);
}
/************************* Function Header **********************************
* WriteChannel
* Send one command.
* Access the CD from the resource heap and perform arithmetic
* operations on the parameters if necessary. Checks for max
* limit of the command. If the resulting parameter is greater than
* the max limit, the command will be sent multiple times.
*
* RETURNS:
* A value that is calculated internally, and which is probably something
* like the difference between the value sent to the printer and the
* value passed in.
*
* HISTORY:
* Wed 24 Nov 1993 -by- Norman Hendley [normanh]
* Enabled support of multiple parameters & multiple EXTCDs
* We currently do no check on the the number of EXTCDs a minidriver has
* so potentially we could run out of parameters to provide for the command
* But Unitool for GPC3 minidrivers is strict about when it allows multi
* parms & multi extcds so their counts should always match and be valid for
* that particular command.
* GPC 2 minidrivers have a max of 1 EXTCD so we don't have a problem here
*
*
* 12:32 on Tue 08 Sep 1992 -by- Lindsay Harris [lindsayh]
* Used varargs stuff.
*
* 15:11 on Mon 14 Oct 1991 -by- Lindsay Harris [lindsayh]
* Sorted it out some more.
*
*****************************************************************************/
int CDECL
WriteChannel(
UD_PDEV * pUDPDev,
int cmd, /* The command index in UDPDEV */
...
)
{
CD *pcd; /* The command descriptor to use */
int rem; /* Remainder in master units */
int *pOutParam; /* Parameter array to pass, order may be different*/
int OutParamBuf[MAX_NUM_PARAMS];
BOOL fGpcVersion2;
va_list vaArgs;
pcd = pUDPDev->apcdCmd[ cmd ];
/* No command exists for this function */
if( pcd == NULL )
return NOOCD;
fGpcVersion2 = pUDPDev->pdh->wVersion < GPC_VERSION3;
pOutParam = OutParamBuf;
rem = 0;
/* Check and process command callback.
*/
if (pcd->bCmdCbId > 0 && cmd < MAXCMDINDEX) {
int i;
/* Build the parameter list.
*/
va_start(vaArgs,cmd);
for ( i = 0; i < (int) nGetNumParams(cmd); i++ ) {
*pOutParam++ = va_arg(vaArgs,int);
}
va_end(vaArgs);
OEMCOMMANDCALLBACK(pUDPDev->pdev, pcd->bCmdCbId, &OutParamBuf);
return(0);
}
/* No command callback. Regular processing.
*
* numeric parameter, translate and check maximum value
*/
va_start(vaArgs,cmd);
if (pcd->sCount) //At least one EXTCD exists
{
register LPEXTCD lpextcd;
int param; /* Value to work with */
int iDiv; /* Divisor */
int iMult; /* Multiplier */
int i;
int iWhichParam; //Which parameter to use for this extcd
/* !!!LindsayH - Unitool bug: hack to bypass */
EXTCD extcd;
// GPC 3.0 WORD aligns the CMD so the following will be invalid on
// commands with odd length. Use macro (defined in uddevice.h) instead.
//lpextcd = (LPEXTCD)(pcd->rgchCmd + pcd->wLength);
lpextcd = GETEXTCD(pUDPDev->pdh , pcd);
//temporary hack begin
//Will happen only for LineSpacing, MasterToDevice() has already
//done the conversion, called from YMoveTo()
if (lpextcd->fMode & CMD_FMODE_SETMODE)
{
va_start(vaArgs,cmd);
*pOutParam = va_arg(vaArgs,int);
SendCmd(pUDPDev, pcd, pOutParam);
va_end(vaArgs);
return 0;
}
// temporary hack ends
for (i=0; i < pcd->sCount; i++ ) // will only iterate once for GPC2
{
/* !!!LindsayH - begin the hack */
//Normanh GPC3 *DOES* WORD align so this will never happen for
// GPC 3.0 minidrivers. Hence lpextcd++ later on is valid.
if( (int)lpextcd & 0x1 ) //
{
/*
* It appears that Unitool does NOT align the EXTCD data.
* So fix that now.
*/
memcpy( &extcd, (BYTE *)lpextcd, sizeof( EXTCD ) );
lpextcd = &extcd;
}
// GPC 2 minidrivers will always want the next parameter
// GPC 3 can specify in the EXTCD which parameter it wants to use
va_start(vaArgs,cmd);
iWhichParam = (fGpcVersion2) ? 0 : lpextcd->wParam;
while (iWhichParam-- >= 0)
param = va_arg(vaArgs,int) ;
/* !!!LindsayH - end of the hack */
/* extended command descriptor, check parameter */
iDiv = lpextcd->sUnit != 0 ? lpextcd->sUnit : 1;
/*
* If I knew for sure that sUnitMult was always 1 when not
* required, I would simplify the following statement.
*/
iMult = lpextcd->sUnitMult != 0 ? lpextcd->sUnitMult : 1;
param = (param + lpextcd->sPreAdd) * iMult;
*pOutParam = param / iDiv + lpextcd->sUnitAdd;
rem = (param % iDiv) / iMult;
if( (!(lpextcd->fMode & XCD_GEN_NO_MIN)) && (*pOutParam < lpextcd->sMin) )
*pOutParam = lpextcd->sMin;
/*
* If there is a limit to the value we can send, then
* make sure we do not exceed it.
*/
// normanh this has the potential to brake on multiple params
// if one EXTCD has a max value which we exceed, we can't know
// how to handle the other params.
// However in the current case (SEND_BLOCK)
// where multiple params are allowed, Max is not applicable.
if( lpextcd->sMax )
{
int iTemp;
iTemp = (int)lpextcd->sMax;
for( ; *pOutParam > iTemp; *pOutParam -= iTemp )
SendCmd( pUDPDev, pcd, &iTemp );
/* Remainder is sent in the call following */
}
if (!fGpcVersion2)
{
lpextcd++;
pOutParam++;
}
va_end(vaArgs);
}// end for
}
else
{
//No EXTCD's does NOT necessarily mean no parameters.Unitool will
//not save the EXTCD if settings are default.
//Problem here is we don't know if any parameters exist
//On Alphas we can't use simple pointer arithmetic to send down
//a array of parameters.
//We need to scan the command for %'s & build param buffer
LPSTR lpcmd; /* The string to process */
int i;
lpcmd = pcd->rgchCmd;
for( i = 0; i < (int)pcd->wLength; i++ )
if( lpcmd[i] == CMD_MARKER )
{
i++;
if( lpcmd[i ] != CMD_MARKER ) //%% doesn't count
*pOutParam++ = va_arg(vaArgs,int);
}
va_end(vaArgs);
}
SendCmd( pUDPDev, pcd, OutParamBuf );
return rem;
}
/************************ Function Header ************************************
* SendCommand
* Send one command. Called by WriteChannel to format a command with
* one parameter in it.
*
* RETURNS:
* Nothing
*
* HISTORY:
* 10:19 on Tue 08 Sep 1992 -by- Lindsay Harris [lindsayh]
* stdcall nonsense clean up; also make it a little clearer??
*
* 17:20 on Fri 08 May 1992 -by- Lindsay Harris [lindsayh]
* Based on Unidrv - updating header.
*
* 23 November 1993 -by- Normanh Hendley [normanh]
* Changed to allow passing a pointer to array or params rather than
* single parameter previously being passed.
*
*
*
*****************************************************************************/
void
SendCmd( pUDPDev, pcd, pParam )
UD_PDEV * pUDPDev; /* UNIDRV's PDEV */
CD *pcd; /* The Command Descriptor tells us what to do */
int *pParam; /* Start of parameters to convert */
{
int i; /* Stepping through the input buffer */
int ocmd; /* Ditto, output buffer */
BYTE rgcmd[ CCHMAXCMDLEN ]; /* Build command here */
LPSTR lpcmd; /* The string to process */
int param;
BOOL fGpcVersion2;
fGpcVersion2 = pUDPDev->pdh->wVersion < GPC_VERSION3;
ocmd = 0;
lpcmd = pcd->rgchCmd;
for( i = 0; i < (int)pcd->wLength; i++ )
{
rgcmd[ ocmd ] = lpcmd[ i ];
if( rgcmd[ ocmd ] == CMD_MARKER )
{
int digit;
// Provide compatibility and security for any GPC2 minidrivers which do
// use more than 1 formatting character in the command.
// ie Use same parameter again.
param = (fGpcVersion2) ? *pParam : *pParam++;
/*
* case 'd': parameter as decimal number
* case 'D': same as case 'd' with +/- sign
* case 'c': parameter as a single character
* case 'C': parameter as character plus '0'
* case 'l': parameter as word LSB first
* case 'm': parameter as word MSB first
* case '%': print a %
* case 'Q': queme method, 1/120" movements
* case 'q': queme method, 1/48" movements
*/
i++; /* Skip over the marker in input string */
/*
* If the next character in the input stream is a digit,
* it represents the number of digits required for this
* conversion. Check now, and remember whatever we may find.
*/
if( lpcmd[ i ] > '0' && lpcmd[ i ] <= '9' )
{
/* Numeric, so presume single digit field width. */
digit = lpcmd[ i ] - '0';
i++; /* Another char from input string */
}
else
digit = 0; /* No explicit width */
switch( lpcmd[ i ] )
{
case 'D': /* Decimal with explicit + sign */
if( param > 0 )
rgcmd[ ocmd++ ] = '+';
/* FALL THROUGH */
case 'd': /* Standard decimal conversion */
if( digit )
{
/*
* Field width is specified, so see how many digits
* are in the number, then zero fill any deficiency.
*/
int paramdigit;
paramdigit = _itoa( rgcmd + ocmd, param );
for( ; paramdigit < digit; paramdigit++ )
rgcmd[ ocmd++ ] = '0'; /* Zero pad */
}
ocmd += _itoa( rgcmd + ocmd, param ); /* Convert now */
break;
case 'C': /* Single character, offset by '0' */
param += '0';
/* FALL THROUGH */
case 'c': /* Single character */
rgcmd[ ocmd++ ] = (BYTE)param;
break;
case 'l': /* Binary output, LSB first */
rgcmd[ ocmd++ ] = (BYTE)param;
rgcmd[ ocmd++ ] = (BYTE)(param >> 8);
break;
case 'm': /* Binary output, MSB first */
rgcmd[ ocmd++ ] = (BYTE)(param >> 8);
rgcmd[ ocmd++ ] = (BYTE)param;
break;
case 'q': /* Qume method, 1/48th of an inch */
rgcmd[ ocmd++ ] = (BYTE)(((param >> 8) & 0xf) + '@');
rgcmd[ ocmd++ ] = (BYTE)(((param >> 4) & 0xf) + '@');
rgcmd[ ocmd++ ] = (BYTE)((param & 0xf) + '@');
break;
case CMD_MARKER:
rgcmd[ ocmd++ ] = CMD_MARKER;
// did not use a param, back up the parameter pointer
--pParam;
break;
default:
break;
} /* switch */
} /* if */
else
ocmd++; /* Data is copied across. */
/*
* Check for the buffer being full. Conversion commands
* write more than one byte, so leave a little room in the buffer.
*/
if( ocmd >= (2 * sizeof( rgcmd )) / 3 )
{
/* Full up - empty this one and continue */
WriteSpoolBuf( pUDPDev, (LPSTR)rgcmd, ocmd );
ocmd = 0;
}
} /* for */
/*
* Generation complete, output the data. But check there is still
* data to go!
*/
if( ocmd > 0 )
WriteSpoolBuf( pUDPDev, (LPSTR)rgcmd, ocmd );
return;
}
/**************************** Function Header ********************************
* itoa
* This function converts the given integer into an ASCII string.
*
* RETURNS:
* The length of the string.
*
* HISTORY:
* 17:22 on Fri 08 May 1992 -by- Lindsay Harris [lindsayh]
* Updated comment block: based on UNIDRV.
*
*****************************************************************************/
static int
_itoa( buf, n )
LPSTR buf; /* Where to put the result */
int n; /* Number to convert */
{
int fNeg;
int i, j;
if( fNeg = (n < 0) )
n = -n;
for( i = 0; n; i++ )
{
buf[i] = (char)(n % 10 + '0');
n /= 10;
}
/* n was zero */
if( i == 0 )
buf[i++] = '0';
if( fNeg )
buf[i++] = '-';
for( j = 0; j < i / 2; j++ )
{
int tmp;
tmp = buf[j];
buf[j] = buf[i - j - 1];
buf[i - j - 1] = (char)tmp;
}
buf[i] = '\0';
return i;
}
/*********************** Function Header *************************************
* MasterToDevice
* Converts from Master to Device units. This function is a duplicate
* of that in WriteChannel, and the two should be amalgamated.
*
* RETURNS:
* Nothing; values are returned in pdcmd.
*
* HISTORY:
* Wed 24 Mar 1993 -by- Norman Hendley [norman]
* Change method of locating the extcd to cover WORD aligned GPC3 types
*
* 10:37 on Fri 26 Mar 1993 -by- Lindsay Harris [lindsayh]
* Switch UD_PDEV to contain address of CD, not offset of it!
*
* 10:47 on Tue 15 Oct 1991 -by- Lindsay Harris [lindsayh]
* Applied fixes from WriteChannel()
*
* 5/22/90 : redid multiplication and division algorithm
* remainder is still messed up
*****************************************************************************/
void
MasterToDevice( pUDPDev, cmd, pdcmd )
UD_PDEV * pUDPDev;
int cmd;
register DEVICECMD *pdcmd;
{
CD *pcd; /* The command descriptor with data */
pdcmd->rem = 0;
pdcmd->max = 0;
pcd = pUDPDev->apcdCmd[ cmd ];
/* no command exists for this function */
if( pcd )
{
/* numeric parameter, translate and check maximum value */
// This is not a valid check for the existance of a EXTCD with GPC3.
// Use pcd->sCount instead
// if( pcd->fType & CMD_FTYPE_EXTENDED )
if (pcd->sCount)
{
register LPEXTCD lpextcd;
int iDiv; /* Divisor */
int iMult; /* Multiplier */
int iTemp; /* Intermediate result */
EXTCD extcd; /* GPC data may not be aligned */
// GPC 3.0 WORD aligns the CMD so the following will be invalid on
// commands with odd length. Use macro (defined in uddevice.h) instead.
//lpextcd = (LPEXTCD)(pcd->rgchCmd + pcd->wLength);
lpextcd= GETEXTCD(pUDPDev->pdh , pcd);
if( (int)lpextcd & 0x1 ) // won't happen for GPC3
{
/* Data is misaligned, so copy it to an aligned version */
memcpy( &extcd, (BYTE *)lpextcd, sizeof( EXTCD ) );
lpextcd = &extcd;
}
/* extended command descriptor, check parameter */
iDiv = lpextcd->sUnit != 0 ? lpextcd->sUnit : 1;
iMult = lpextcd->sUnitMult != 0 ? lpextcd->sUnitMult : 1;
iTemp = (pdcmd->param + lpextcd->sPreAdd) * iMult;
pdcmd->param = iTemp / iDiv + lpextcd->sUnitAdd;
pdcmd->rem = (iTemp % iDiv) / iMult;
pdcmd->max = lpextcd->sMax;
}
}
return;
}
/************************* Function Header ***********************************
* SelectColor
* Selects color, 0 must be the last color to be selected.
* Assumes that color info contains parameters for selecting
* black cyan magenta yellow
* Keep track of the current color selection, to reduce the amount
* of data sent to the printer
*
* ASSUME: (pUDPDev->ColorInfo.fType & DC_CT_PLANE) &&
* (pUDPDev->ColorInfo.Raster.ColorPlane.fFormat == DC_CF_EXTRACT_BLACK)
* (i.e. all other bits are NOT set --- the default CMYK model).
*
*
* RETURNS:
* Nothing.
*
* HISTORY:
* 17:24 on Fri 08 May 1992 -by- Lindsay Harris [lindsayh]
* Update comment block.
*
* 5/24/90: side effect, cursor may be reset back to 0, after selecting
* the color
*
*****************************************************************************/
void
SelectColor( pUDPDev, color )
UD_PDEV * pUDPDev; /* UNIDRV's PDEV */
int color; /* The colour to select */
{
if( color >= 0 && color != pUDPDev->ctl.sColor )
{
/* check to see if to send CR or not. */
if( pUDPDev->fColorFormat & DC_CF_SEND_CR )
XMoveto( pUDPDev, 0, MV_PHYSICAL );
if( pUDPDev->apcdCmd[ CMD_DC_GC_FIRST + color ] == NULL )
color = 0;
WriteChannel( pUDPDev, CMD_DC_GC_FIRST + color );
pUDPDev->ctl.sColor = (short)color;
}
}
/************************ Function Header ************************************
* SelectTextColor
* Select a text color. Adopted from SelectColor( ).
*
* Assume: all 8 text colors are available:
* Black, Red, Green, Yellow, Blue, Magenta, Cyan, White.
*
* RETURNS:
* Nothing
*
* HISTORY:
* 13:55 on Tue 18 Jul 1995 -by- Sandra Matts
* Added support for Color LaserJet text
*
* 13:36 on Fri 09 Apr 1993 -by- Lindsay Harris [lindsayh]
* Handle white text on LaserJet III
*
* 17:26 on Fri 08 May 1992 -by- Lindsay Harris [lindsayh]
* Updated comment block, add comments etc.
*
*****************************************************************************/
void
SelectTextColor( pPDev, color )
PDEV *pPDev; /* UNIDRV's PDEV */
int color; /* The colour to select */
{
ULONG tmpcolor;
UD_PDEV *pUDPDev = pPDev->pUDPDev;
/*
* Note that we will be called for monochrome printers that support
* white text. At the time of writing, these are PCL 5 printers.
* To handle this, the FONTSIMULATION fields are used, as these
* contain the commands to switch back and forth.
*/
/*
* NOTE: we only support 8 (at most) text colours, so we limit
* the range of input to that. The problem is that we have to
* tell GDI that we have 16 entries in our palette, and they will
* give us an index of 15 for white!
*/
if( color >= 0 && (ULONG)color != pUDPDev->ctl.ulTextColor )
{
int iCmd; /* The command to use */
if( (pUDPDev->fMDGeneral & MD_WHITE_TEXT) &&
pUDPDev->sDevPlanes == 1 && pUDPDev->sBitsPixel == 1 )
{
/* The special (LaserJet) case. Font simulations needed */
if( color )
{
/*
* For consistency, set color to 1 to stop sending
* redundant colour change commands.
*/
iCmd = CMD_FS_WHITE_TEXT_ON;
color = 1;
}
else
{
/* Black - standard text colour */
iCmd = CMD_FS_WHITE_TEXT_OFF;
}
/* Set the desired colour ! */
WriteChannel( pUDPDev, iCmd );
}
else
{
/* sandram - add support for 256 color text */
if (pUDPDev->fMode & PF_24BPP)
{
int entry = PALETTE_MAX-1;
tmpcolor = ((PAL_DATA *)(pPDev->pPalData))->ulPalCol[PALETTE_MAX-1];
WriteChannel (pUDPDev, CMD_DC_PC_ENTRY,
RED_VALUE (color),
GREEN_VALUE (color),
BLUE_VALUE (color),
(ULONG) PALETTE_MAX-1);
WriteChannel (pUDPDev, CMD_DC_PC_SELECTINDEX,
PALETTE_MAX-1);
((PAL_DATA *)(pPDev->pPalData))->ulPalCol[PALETTE_MAX-1] = tmpcolor;
}
else if (pUDPDev->fMode & PF_8BPP)
{
WriteChannel (pUDPDev, CMD_DC_PC_SELECTINDEX,
color);
}
else
{
/* If this color is not supported, use the default color: black. */
color &= (DC_OCD_TC_MAX - 1); /* 16 entry palette wrap a round */
if( pUDPDev->apcdCmd[ CMD_DC_TC_FIRST + color ] == NULL )
color = DC_OCD_TC_BLACK;
iCmd = CMD_DC_TC_FIRST + color;
if( (ULONG)color != pUDPDev->ctl.ulTextColor )
{
/* Set the desired colour - but only once! */
WriteChannel( pUDPDev, iCmd );
}
}
}
pUDPDev->ctl.ulTextColor = color;
}
return;
}