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.
1983 lines
59 KiB
1983 lines
59 KiB
/*++
|
|
|
|
Copyright (c) 1996-1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
physical.c
|
|
|
|
Abstract:
|
|
|
|
Support the following functions related to sending data to the printer
|
|
and print head movements, cursor control.
|
|
|
|
WriteSpoolBuf
|
|
WriteAbortBuf
|
|
FlushSpoolBuf
|
|
WriteChannel
|
|
WriteChannelEx
|
|
XMoveTo
|
|
YMoveTo
|
|
|
|
Environment:
|
|
|
|
Windows NT Unidrv driver
|
|
|
|
Revision History:
|
|
|
|
10/14/96 -amandan-
|
|
Created
|
|
|
|
--*/
|
|
#include "unidrv.h"
|
|
|
|
static int itoA( LPSTR , int );
|
|
INT IGetNumParameter( BYTE *, INT);
|
|
BOOL BUniWritePrinter(
|
|
IN PDEV* pPDev,
|
|
IN LPVOID pBuf,
|
|
IN DWORD cbBuf,
|
|
OUT LPDWORD pcbWritten);
|
|
|
|
#define DELTA_X (pPDev->dwFontWidth/2)
|
|
#define DELTA_Y (dwMaxLineSpacing /5)
|
|
|
|
|
|
WriteSpoolBuf(
|
|
PDEV *pPDev,
|
|
BYTE *pbBuf,
|
|
INT iCount
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
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.
|
|
|
|
Arguments:
|
|
|
|
pPDev - Pointer to PDEVICE struct
|
|
pbBuf - Pointer to buffer containing data to be sent
|
|
iCount - Count of number of bytes to send
|
|
|
|
Return Value:
|
|
|
|
The number of bytes sent to the printer
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD dw;
|
|
|
|
//
|
|
// Check for aborted output
|
|
//
|
|
|
|
if( pPDev->fMode & PF_ABORTED )
|
|
return 0;
|
|
|
|
//
|
|
// If the output buffer cannot accomodate the current request,
|
|
// flush the content of the buffer first.
|
|
//
|
|
|
|
if( (pPDev->iSpool) && (pPDev->iSpool + iCount > CCHSPOOL ))
|
|
{
|
|
if( !FlushSpoolBuf( pPDev ) )
|
|
{
|
|
WriteAbortBuf(pPDev, pPDev->pbOBuf, pPDev->iSpool, 0) ;
|
|
pPDev->iSpool = 0;
|
|
return 0; // at least send previously cached stuff.
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check whether request is larger than output buffer, if so, skip buffering
|
|
// and write directly to spooler.
|
|
//
|
|
|
|
if( iCount >= CCHSPOOL )
|
|
{
|
|
if( !BUniWritePrinter( pPDev, pbBuf, iCount, &dw ) )
|
|
{
|
|
pPDev->iSpool = 0;
|
|
pPDev->fMode |= PF_ABORTED;
|
|
iCount = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// buffer up the output
|
|
//
|
|
|
|
if( pPDev->pbOBuf == NULL)
|
|
return 0;
|
|
|
|
|
|
CopyMemory( pPDev->pbOBuf + pPDev->iSpool, pbBuf, iCount );
|
|
pPDev->iSpool += iCount;
|
|
}
|
|
|
|
|
|
return iCount;
|
|
}
|
|
|
|
VOID WriteAbortBuf(
|
|
PDEV *pPDev,
|
|
BYTE *pbBuf,
|
|
INT iCount,
|
|
DWORD dwWait
|
|
)
|
|
{
|
|
DWORD dwCount = 0;
|
|
HMODULE hInst ;
|
|
typedef BOOL (*PFNFLUSHPR)( HANDLE hPrinter,
|
|
LPVOID pBuf,
|
|
DWORD cbBuf,
|
|
LPDWORD pcWritten,
|
|
DWORD cSleep) ;
|
|
|
|
//
|
|
// Call FlushPrinter only if there is no plugin hooking WritePrinter .
|
|
// One of plug-ins hooks WritePrinter, the plug-in need to call FlushPrinter.
|
|
// In that case, UNIDRV should not call FlushPrinter.
|
|
//
|
|
if( pPDev->fMode & PF_ABORTED &&
|
|
!(pPDev->fMode2 & PF2_WRITE_PRINTER_HOOKED))
|
|
{
|
|
#ifdef WINNT_40
|
|
; // Pretend we flushed
|
|
#else
|
|
|
|
BOOL bRet;
|
|
do
|
|
{
|
|
bRet = FlushPrinter(pPDev->devobj.hPrinter, pbBuf, iCount, &dwCount, dwWait);
|
|
pbBuf += dwCount;
|
|
iCount -= dwCount;
|
|
} while (bRet && iCount > 0 && dwCount > 0);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
FlushSpoolBuf(
|
|
PDEV *pPDev
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function flush our internal buffer.
|
|
|
|
Arguments:
|
|
|
|
pPDev - Pointer to PDEVICE struct
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful , otherwise FALSE
|
|
|
|
--*/
|
|
{
|
|
|
|
DWORD dwCount;
|
|
//
|
|
// Check for aborted output
|
|
//
|
|
|
|
if( pPDev->fMode & PF_ABORTED )
|
|
return 0;
|
|
|
|
|
|
//
|
|
// Write the data out
|
|
//
|
|
|
|
if( pPDev->iSpool )
|
|
{
|
|
if ( !BUniWritePrinter(pPDev, pPDev->pbOBuf, pPDev->iSpool, &dwCount) )
|
|
{
|
|
pPDev->fMode |= PF_ABORTED;
|
|
return FALSE;
|
|
}
|
|
pPDev->iSpool = 0;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
INT
|
|
XMoveTo(
|
|
PDEV *pPDev,
|
|
INT iXIn,
|
|
INT fFlag
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called to change the X position.
|
|
|
|
Arguments:
|
|
|
|
pPDev - Pointer to PDEVICE struct
|
|
iXIn - Number of units to move in X direction
|
|
fFlag - Specifies the different X move operations
|
|
|
|
Return Value:
|
|
|
|
The difference between requested and actual value sent to the device
|
|
|
|
--*/
|
|
|
|
{
|
|
int iX, iFineValue, iDiff = 0;
|
|
GPDDRIVERINFO *pDrvInfo = pPDev->pDriverInfo;
|
|
int iScale;
|
|
|
|
//
|
|
// If the position is given in graphics units, convert to master units
|
|
//
|
|
// ptGrxScale has been adjusted to suit the current orientation.
|
|
//
|
|
if (pPDev->pOrientation && pPDev->pOrientation->dwRotationAngle != ROTATE_NONE &&
|
|
pPDev->pGlobals->bRotateCoordinate == FALSE)
|
|
iScale = pPDev->ptGrxScale.y;
|
|
else
|
|
iScale = pPDev->ptGrxScale.x;
|
|
|
|
if ( fFlag & MV_GRAPHICS )
|
|
{
|
|
iXIn = (iXIn * iScale);
|
|
}
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
|
|
iX = iXIn;
|
|
|
|
//
|
|
// Basically, only adjust if we are doing absolute move
|
|
//
|
|
|
|
if ( !(fFlag & (MV_RELATIVE | MV_PHYSICAL)) )
|
|
iX += pPDev->sf.ptPrintOffsetM.x;
|
|
|
|
//
|
|
// If it's a relative move, update iX (iX will be the absolute position)
|
|
// to reflect the current cursor position
|
|
//
|
|
|
|
if ( fFlag & MV_RELATIVE )
|
|
iX += pPDev->ctl.ptCursor.x;
|
|
|
|
|
|
//By definition a negative absolute move relative to Imageable origin
|
|
//is not allowed. But the MV_FORCE_CR flag bypasses this check.
|
|
if(!(fFlag & MV_FORCE_CR) && (iX - pPDev->sf.ptPrintOffsetM.x < 0))
|
|
iX = pPDev->sf.ptPrintOffsetM.x ;
|
|
|
|
|
|
//
|
|
// Update, only update our current cursor position and return
|
|
// Do nothing if the XMoveTo cmd is called to move to the current position.
|
|
//
|
|
|
|
if ( fFlag & MV_UPDATE )
|
|
{
|
|
pPDev->ctl.ptAbsolutePos.x = pPDev->ctl.ptCursor.x = iX;
|
|
return 0;
|
|
}
|
|
|
|
if( fFlag & MV_SENDXMOVECMD )
|
|
pPDev->ctl.dwMode |= MODE_CURSOR_X_UNINITIALIZED;
|
|
|
|
if (!(pPDev->ctl.dwMode & MODE_CURSOR_X_UNINITIALIZED) && pPDev->ctl.ptCursor.x == iX )
|
|
return 0;
|
|
|
|
|
|
//
|
|
// If iX is zero and pGlobals->cxaftercr does not have
|
|
// CXCR_AT_GRXDATA_ORIGIN set, then we send CR and reset our
|
|
// cursor position to 0, which is the printable x origin
|
|
//
|
|
|
|
if (iX == 0 && (pPDev->pGlobals->cxaftercr == CXCR_AT_CURSOR_X_ORIGIN ||
|
|
pPDev->sf.ptPrintOffsetM.x == 0))
|
|
{
|
|
pPDev->ctl.ptAbsolutePos.x = pPDev->ctl.ptCursor.x = 0;
|
|
WriteChannel(pPDev, COMMANDPTR(pDrvInfo, CMD_CARRIAGERETURN));
|
|
pPDev->ctl.dwMode &= ~MODE_CURSOR_X_UNINITIALIZED;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Check whether we any X move cmd, PF_NO_XMOVE_CMD is set if we did
|
|
// not see any relative or absolute x move cmds
|
|
//
|
|
|
|
if( pPDev->fMode & PF_NO_XMOVE_CMD)
|
|
{
|
|
//
|
|
// There is no X move command(abs or relative), so we'll have to simulate
|
|
// using blanks or null graphics data (0)
|
|
//
|
|
|
|
//
|
|
// We assume that when XMoveto is called, the current font is always
|
|
// the default font IF the printer has no X movement command.
|
|
//
|
|
|
|
int iRelx = iX - pPDev->ctl.ptCursor.x ;
|
|
int iDefWidth;
|
|
|
|
//
|
|
// Convert to Master Units
|
|
//
|
|
|
|
//
|
|
// BUG_BUG, Double check that we can use Default Font here when
|
|
// we have a custom TTY driver
|
|
// seems to work so far.
|
|
//
|
|
|
|
|
|
if ( iRelx < 0 && (!pPDev->bTTY || (DWORD)(-iRelx) > DELTA_X ))
|
|
{
|
|
if (pPDev->pGlobals->cxaftercr == CXCR_AT_CURSOR_X_ORIGIN)
|
|
iRelx = iX;
|
|
else if (pPDev->pGlobals->cxaftercr == CXCR_AT_PRINTABLE_X_ORIGIN)
|
|
{
|
|
// printing offset is only available in the callers
|
|
// cooridinates so if the move is being performed
|
|
// in different coordinates, the offset will be wrong.
|
|
ASSERT(!pPDev->pOrientation || pPDev->pOrientation->dwRotationAngle == ROTATE_NONE ||
|
|
pPDev->pGlobals->bRotateCoordinate == TRUE) ;
|
|
|
|
iRelx = iX - pPDev->sf.ptPrintOffsetM.x;
|
|
}
|
|
|
|
WriteChannel( pPDev, COMMANDPTR(pDrvInfo, CMD_CARRIAGERETURN ));
|
|
}
|
|
|
|
//
|
|
// Simulate X Move, algorithm is that we always send a blank space
|
|
// for every character width, and send graphics null data for
|
|
// the remainder, in the case of TTY we skip the remaining graphics that
|
|
// cannot be sent within a space character width.
|
|
//
|
|
|
|
iDefWidth = pPDev->dwFontWidth ;
|
|
if (iDefWidth)
|
|
{
|
|
|
|
while(iRelx >= iDefWidth)
|
|
{
|
|
WriteSpoolBuf( pPDev, (LPSTR)" ", 1 );
|
|
iRelx -= iDefWidth;
|
|
}
|
|
}
|
|
else
|
|
TERSE (("XMoveTo: iDefWidth = 0\n"));
|
|
|
|
|
|
//
|
|
// Send the remaining partial space via FineXMoveTo.
|
|
//
|
|
|
|
if (!pPDev->bTTY)
|
|
{
|
|
iDiff = iRelx;
|
|
fFlag |= MV_FINE; // Use graphics mode to reach point
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
DWORD dwTestValue = abs(iX - pPDev->ctl.ptCursor.x);
|
|
COMMAND *pCmd;
|
|
|
|
//
|
|
// X movement commmands are available, so use them.
|
|
// We need to decide here whether relative or absolute command
|
|
// are favored
|
|
|
|
//
|
|
// General assumption: if dwTestValue > dwXMoveThreshold,
|
|
// absolute command will be favored
|
|
//
|
|
|
|
//
|
|
// BUG_BUG, if we are stripping blanks, we need to check whether
|
|
// it's legal to move in Graphics mode. If it's not, we have
|
|
// to get out of graphics mode before moving.
|
|
// Graphics module is responsible for keeping track
|
|
// of these things, and appearently so far, things work, so
|
|
// this is neither a bug nor a feature request at this time.
|
|
//
|
|
|
|
if (((pPDev->ctl.dwMode & MODE_CURSOR_X_UNINITIALIZED) ||
|
|
((dwTestValue > pPDev->pGlobals->dwXMoveThreshold ) &&
|
|
iX >= 0) ||
|
|
!COMMANDPTR(pDrvInfo, CMD_XMOVERELRIGHT)) &&
|
|
(pCmd = COMMANDPTR(pDrvInfo, CMD_XMOVEABSOLUTE)) != NULL)
|
|
{
|
|
//
|
|
// if the move units are less than the master units then we need to
|
|
// check whether the new position will end up being the same as the
|
|
// original position. If so, no point in sending another command.
|
|
//
|
|
if (!(pPDev->ctl.dwMode & MODE_CURSOR_X_UNINITIALIZED) &&
|
|
(pPDev->ptDeviceFac.x > 1) &&
|
|
((iX - (iX % pPDev->ptDeviceFac.x)) == pPDev->ctl.ptCursor.x))
|
|
{
|
|
iDiff = iX - pPDev->ctl.ptCursor.x;
|
|
}
|
|
else
|
|
{
|
|
// check whether the no absolute move left flag is set. If set we need
|
|
// to send a CR before doing the absolute move.
|
|
//
|
|
if (iX < pPDev->ctl.ptCursor.x && pPDev->pGlobals->bAbsXMovesRightOnly)
|
|
{
|
|
WriteChannel( pPDev, COMMANDPTR(pDrvInfo, CMD_CARRIAGERETURN ));
|
|
}
|
|
pPDev->ctl.ptAbsolutePos.x = iX;
|
|
//
|
|
// 3/13/97 ZhanW
|
|
// set up DestY as well in case it's needed (ex. FE printers).
|
|
// In that case, truncation error (iDiff) is not a concern.
|
|
// This is for backward compatibility with FE Win95 and FE NT4
|
|
// minidrivers.
|
|
//
|
|
pPDev->ctl.ptAbsolutePos.y = pPDev->ctl.ptCursor.y;
|
|
iDiff = WriteChannelEx(pPDev,
|
|
pCmd,
|
|
pPDev->ctl.ptAbsolutePos.x,
|
|
pPDev->ptDeviceFac.x);
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Use relative command to send move request
|
|
//
|
|
|
|
INT iRelRightValue = 0;
|
|
|
|
if( iX < pPDev->ctl.ptCursor.x )
|
|
{
|
|
//
|
|
// Relative move left
|
|
//
|
|
|
|
if (pCmd = COMMANDPTR(pDrvInfo,CMD_XMOVERELLEFT))
|
|
{
|
|
//
|
|
// Optimize to avoid sending 0-move cmd.
|
|
//
|
|
if ((pPDev->ctl.ptRelativePos.x =
|
|
pPDev->ctl.ptCursor.x - iX) < pPDev->ptDeviceFac.x)
|
|
iDiff = pPDev->ctl.ptRelativePos.x;
|
|
else
|
|
iDiff = WriteChannelEx(pPDev,
|
|
pCmd,
|
|
pPDev->ctl.ptRelativePos.x,
|
|
pPDev->ptDeviceFac.x);
|
|
iDiff = -iDiff;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// No Relative left move cmd, use <CR> to reach start
|
|
// Will try to use relative right move cmd to send later
|
|
//
|
|
|
|
WriteChannel(pPDev, COMMANDPTR(pDrvInfo, CMD_CARRIAGERETURN));
|
|
|
|
if (pPDev->pGlobals->cxaftercr == CXCR_AT_CURSOR_X_ORIGIN)
|
|
iRelRightValue = iX;
|
|
else if (pPDev->pGlobals->cxaftercr == CXCR_AT_PRINTABLE_X_ORIGIN)
|
|
{
|
|
// moveto code cannot handle case where printer cannot
|
|
// rotate its coordinate system, and we are in landscape mode
|
|
// and we are using relative move commands.
|
|
ASSERT(!pPDev->pOrientation || pPDev->pOrientation->dwRotationAngle == ROTATE_NONE ||
|
|
pPDev->pGlobals->bRotateCoordinate == TRUE) ;
|
|
|
|
iRelRightValue = iX - pPDev->sf.ptPrintOffsetM.x;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Relative right move
|
|
// UNIITIALZIED is an invalid position, set to zero
|
|
//
|
|
|
|
iRelRightValue = iX - pPDev->ctl.ptCursor.x;
|
|
}
|
|
|
|
if( iRelRightValue > 0 )
|
|
{
|
|
if (pCmd = COMMANDPTR(pDrvInfo, CMD_XMOVERELRIGHT))
|
|
{
|
|
//
|
|
// optimize to avoid 0-move cmd
|
|
//
|
|
if ((pPDev->ctl.ptRelativePos.x = iRelRightValue) <
|
|
pPDev->ptDeviceFac.x)
|
|
iDiff = iRelRightValue;
|
|
else
|
|
iDiff = WriteChannelEx(pPDev,
|
|
pCmd,
|
|
pPDev->ctl.ptRelativePos.x,
|
|
pPDev->ptDeviceFac.x);
|
|
}
|
|
else
|
|
iDiff = iRelRightValue;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Peform fine move command
|
|
//
|
|
|
|
if ( (fFlag & MV_FINE) && iDiff > 0 )
|
|
iDiff = FineXMoveTo( pPDev, iDiff );
|
|
|
|
//
|
|
// Update our current cursor position
|
|
//
|
|
|
|
pPDev->ctl.ptAbsolutePos.x = pPDev->ctl.ptCursor.x = iX - iDiff ;
|
|
|
|
if( fFlag & MV_GRAPHICS )
|
|
{
|
|
iDiff = (iDiff / iScale);
|
|
}
|
|
|
|
if (pPDev->fMode & PF_RESELECTFONT_AFTER_XMOVE)
|
|
{
|
|
VResetFont(pPDev);
|
|
}
|
|
|
|
pPDev->ctl.dwMode &= ~MODE_CURSOR_X_UNINITIALIZED;
|
|
return( iDiff);
|
|
}
|
|
|
|
INT
|
|
YMoveTo(
|
|
PDEV *pPDev,
|
|
INT iYIn,
|
|
INT fFlag
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called to change the Y position.
|
|
|
|
Arguments:
|
|
|
|
pPDev - Pointer to PDEVICE struct
|
|
iYIn - Number of units to move in Y direction
|
|
fFlag - Specifies the different Y move operations
|
|
|
|
Return Value:
|
|
|
|
The difference between requested and actual value sent to the device
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
INT iY, iDiff = 0;
|
|
DWORD dwTestValue;
|
|
GPDDRIVERINFO *pDrvInfo = pPDev->pDriverInfo;
|
|
COMMAND *pAbsCmd;
|
|
INT iScale;
|
|
|
|
//
|
|
// Convert to Master Units if the given units is in Graphics Units
|
|
// ptGrxScale has been adjusted to suit the current orientation.
|
|
//
|
|
if (pPDev->pOrientation && pPDev->pOrientation->dwRotationAngle != ROTATE_NONE &&
|
|
pPDev->pGlobals->bRotateCoordinate == FALSE)
|
|
iScale = pPDev->ptGrxScale.x;
|
|
else
|
|
iScale = pPDev->ptGrxScale.y;
|
|
|
|
if ( fFlag & MV_GRAPHICS )
|
|
{
|
|
iYIn = (iYIn * iScale);
|
|
}
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
|
|
iY = iYIn;
|
|
|
|
//
|
|
// Basically, only adjust if we are doing absolute move
|
|
//
|
|
if ( !(fFlag & (MV_RELATIVE | MV_PHYSICAL)) )
|
|
iY += pPDev->sf.ptPrintOffsetM.y;
|
|
|
|
//
|
|
// Adjust iY to be the absolute position
|
|
//
|
|
|
|
if( fFlag & MV_RELATIVE )
|
|
iY += pPDev->ctl.ptCursor.y;
|
|
|
|
//
|
|
// Update, only update our current cursor position and return
|
|
// Do nothing if the YMoveTo cmd is called to move to the current position.
|
|
//
|
|
|
|
if( fFlag & MV_UPDATE )
|
|
{
|
|
pPDev->ctl.ptAbsolutePos.y = pPDev->ctl.ptCursor.y = iY;
|
|
return 0;
|
|
}
|
|
|
|
|
|
if( fFlag & MV_SENDYMOVECMD )
|
|
pPDev->ctl.dwMode |= MODE_CURSOR_Y_UNINITIALIZED;
|
|
|
|
if(!(pPDev->ctl.dwMode & MODE_CURSOR_Y_UNINITIALIZED) && pPDev->ctl.ptCursor.y == iY )
|
|
return 0;
|
|
|
|
//
|
|
// General assumption: if dwTestValue > dwYMoveThreshold,
|
|
// absolute Y move command will be favored. Also, for iY < 0,
|
|
// use relative command since some printers like old LaserJet have
|
|
// printable area above y=0 accessable only through relative move cmds.
|
|
//
|
|
|
|
//
|
|
// BUG_BUG, if we are stripping blanks, we need to check whether
|
|
// it's legal to move in Graphics mode. If it's not, we have
|
|
// to get out of graphics mode before moving.
|
|
// Graphics module is responsible for keeping track
|
|
// of these things, and appearently so far, things work, so
|
|
// this is neither a bug nor a feature request at this time.
|
|
//
|
|
|
|
|
|
dwTestValue = abs(iY - pPDev->ctl.ptCursor.y);
|
|
|
|
if (((pPDev->ctl.dwMode & MODE_CURSOR_Y_UNINITIALIZED) ||
|
|
(dwTestValue > pPDev->pGlobals->dwYMoveThreshold &&
|
|
iY >= 0)) &&
|
|
(pAbsCmd = COMMANDPTR(pDrvInfo, CMD_YMOVEABSOLUTE)) != NULL)
|
|
{
|
|
//
|
|
// if the move units are less than the master units then we need to
|
|
// check whether the new position will end up being the same as the
|
|
// original position. If so, no point in sending another command.
|
|
//
|
|
if (!(pPDev->ctl.dwMode & MODE_CURSOR_Y_UNINITIALIZED) &&
|
|
(pPDev->ptDeviceFac.y > 1) &&
|
|
((iY - (iY % pPDev->ptDeviceFac.y)) == pPDev->ctl.ptCursor.y))
|
|
{
|
|
iDiff = iY - pPDev->ctl.ptCursor.y;
|
|
}
|
|
else
|
|
{
|
|
pPDev->ctl.ptAbsolutePos.y = iY;
|
|
pPDev->ctl.ptAbsolutePos.x = pPDev->ctl.ptCursor.x;
|
|
iDiff = WriteChannelEx(pPDev,
|
|
pAbsCmd,
|
|
pPDev->ctl.ptAbsolutePos.y,
|
|
pPDev->ptDeviceFac.y);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DWORD dwSendCRFlags = 0;
|
|
//
|
|
// FYMOVE_SEND_CR_FIRST indicates that it's required to send a CR
|
|
// before each line-spacing command
|
|
//
|
|
if (pPDev->fYMove & FYMOVE_SEND_CR_FIRST)
|
|
{
|
|
if ((pPDev->pGlobals->cxaftercr == CXCR_AT_CURSOR_X_ORIGIN))
|
|
dwSendCRFlags = MV_PHYSICAL | MV_FORCE_CR;
|
|
else
|
|
dwSendCRFlags = MV_PHYSICAL;
|
|
// in this case CR takes you to Printable origin so
|
|
// MV_PHYSICAL flag should not appear.
|
|
// This is a bug, but we won't fix it till something
|
|
// breaks. Too risky. !!!! Bug_Bug !!!!
|
|
}
|
|
|
|
//
|
|
// Use Relative Y-move commands
|
|
//
|
|
|
|
|
|
//
|
|
// Use line spacing if that is preferred
|
|
//
|
|
|
|
|
|
if ( ((pPDev->bTTY) ||
|
|
(pPDev->fYMove & FYMOVE_FAVOR_LINEFEEDSPACING &&
|
|
pPDev->arCmdTable[CMD_SETLINESPACING] != NULL) ) &&
|
|
(iY - pPDev->ctl.ptCursor.y > 0) &&
|
|
(pPDev->arCmdTable[CMD_LINEFEED] != NULL)
|
|
)
|
|
{
|
|
INT iLineSpacing;
|
|
DWORD dwMaxLineSpacing = pPDev->pGlobals->dwMaxLineSpacing;
|
|
|
|
if (pPDev->bTTY && (INT)dwTestValue > 0)
|
|
{ // [Peterwo] here's a hack I tried that ensures that any request for a Y-move results
|
|
// in at least one CR being sent. It doesn't work, because bRealrender
|
|
// code sends Y move commands of one scanline each, resulting in
|
|
// one line feed per scanline.
|
|
// if( (INT)dwTestValue < dwMaxLineSpacing)
|
|
// dwTestValue = dwMaxLineSpacing;
|
|
//
|
|
// if you don't send anything, leave diff undisturbed
|
|
// so the error can accumulate otherwise many small
|
|
// cursor movements will not accumulate to cause one
|
|
// occasional actual movement.
|
|
|
|
//
|
|
// For TTY driver we round up the input value one fifth of
|
|
// line spacing. This is required for not sending too many
|
|
// line feeds for small Y movements.
|
|
//
|
|
DWORD dwTmpValue;
|
|
|
|
dwTmpValue = ((dwTestValue + DELTA_Y) / dwMaxLineSpacing) * dwMaxLineSpacing;
|
|
if (dwTmpValue)
|
|
{
|
|
dwTestValue = dwTmpValue ;
|
|
}
|
|
}
|
|
while ( (INT)dwTestValue > 0)
|
|
{
|
|
if (pPDev->bTTY)
|
|
{
|
|
iLineSpacing = dwMaxLineSpacing;
|
|
if (dwTestValue < (DWORD)iLineSpacing)
|
|
{
|
|
iDiff = dwTestValue;
|
|
break;
|
|
}
|
|
if ( dwSendCRFlags )
|
|
{
|
|
XMoveTo( pPDev, 0, dwSendCRFlags );
|
|
dwSendCRFlags = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
iLineSpacing =(INT)(dwTestValue > dwMaxLineSpacing ?
|
|
dwMaxLineSpacing : dwTestValue);
|
|
//
|
|
// new code to handle positioning error when linespacingmoveunit doesn't
|
|
// equal master units
|
|
if (pPDev->pGlobals->dwLineSpacingMoveUnit > 0)
|
|
{
|
|
DWORD dwScale = pPDev->pGlobals->ptMasterUnits.y / pPDev->pGlobals->dwLineSpacingMoveUnit;
|
|
|
|
// optimize to avoid 0-move cmd when move unit is less than master units
|
|
//
|
|
if (dwTestValue < dwScale)
|
|
{
|
|
iDiff = dwTestValue;
|
|
break;
|
|
}
|
|
// Modify line spacing to be multiple of moveunit
|
|
//
|
|
iLineSpacing -= (iLineSpacing % dwScale);
|
|
}
|
|
if ( dwSendCRFlags )
|
|
{
|
|
XMoveTo( pPDev, 0, dwSendCRFlags );
|
|
dwSendCRFlags = 0;
|
|
}
|
|
|
|
if (pPDev->ctl.lLineSpacing == -1 ||
|
|
iLineSpacing != pPDev->ctl.lLineSpacing )
|
|
{
|
|
pPDev->ctl.lLineSpacing = iLineSpacing;
|
|
WriteChannel(pPDev, COMMANDPTR(pDrvInfo, CMD_SETLINESPACING));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Send the LF
|
|
//
|
|
|
|
WriteChannel(pPDev, COMMANDPTR(pDrvInfo, CMD_LINEFEED));
|
|
dwTestValue -= (DWORD)iLineSpacing;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Use relative command
|
|
//
|
|
|
|
PCOMMAND pCmd;
|
|
|
|
if ( iY <= pPDev->ctl.ptCursor.y )
|
|
{
|
|
//
|
|
// If there is no RELATIVE UP cmd, do nothing and return
|
|
//
|
|
|
|
if (pCmd = COMMANDPTR(pDrvInfo, CMD_YMOVERELUP))
|
|
{
|
|
//
|
|
// optimize to avoid 0-move cmd
|
|
//
|
|
if ((pPDev->ctl.ptRelativePos.y =
|
|
pPDev->ctl.ptCursor.y - iY) < pPDev->ptDeviceFac.y)
|
|
iDiff = pPDev->ctl.ptRelativePos.y;
|
|
else
|
|
{
|
|
// FYMOVE_SEND_CR_FIRST indicates that it's required to send a CR
|
|
// before each line-spacing command
|
|
//
|
|
if ( dwSendCRFlags )
|
|
XMoveTo( pPDev, 0, dwSendCRFlags );
|
|
|
|
iDiff = WriteChannelEx(pPDev,
|
|
pCmd,
|
|
pPDev->ctl.ptRelativePos.y,
|
|
pPDev->ptDeviceFac.y);
|
|
}
|
|
iDiff = -iDiff;
|
|
}
|
|
else
|
|
// Do nothing since we can't simulate it
|
|
iDiff = (iY - pPDev->ctl.ptCursor.y );
|
|
|
|
}
|
|
else
|
|
{
|
|
if (pCmd = COMMANDPTR(pDrvInfo, CMD_YMOVERELDOWN))
|
|
{
|
|
pPDev->ctl.ptRelativePos.y = iY - pPDev->ctl.ptCursor.y;
|
|
|
|
//
|
|
// optimize to avoid 0-move cmd
|
|
//
|
|
if (pPDev->ctl.ptRelativePos.y < pPDev->ptDeviceFac.y)
|
|
iDiff = pPDev->ctl.ptRelativePos.y;
|
|
else
|
|
{
|
|
// FYMOVE_SEND_CR_FIRST indicates that it's required to send a CR
|
|
// before each line-spacing command
|
|
//
|
|
if ( dwSendCRFlags )
|
|
XMoveTo( pPDev, 0, dwSendCRFlags );
|
|
|
|
iDiff = WriteChannelEx(pPDev,
|
|
pCmd,
|
|
pPDev->ctl.ptRelativePos.y,
|
|
pPDev->ptDeviceFac.y);
|
|
}
|
|
}
|
|
else
|
|
iDiff = (iY - pPDev->ctl.ptCursor.y );
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update our current cursor position
|
|
//
|
|
|
|
pPDev->ctl.ptAbsolutePos.y = pPDev->ctl.ptCursor.y = iY - iDiff;
|
|
|
|
if( fFlag & MV_GRAPHICS )
|
|
{
|
|
iDiff = (iDiff / iScale);
|
|
}
|
|
|
|
pPDev->ctl.dwMode &= ~MODE_CURSOR_Y_UNINITIALIZED;
|
|
return (iDiff);
|
|
}
|
|
|
|
|
|
INT
|
|
FineXMoveTo(
|
|
PDEV *pPDev,
|
|
INT iX
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
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.
|
|
'iX' is always in MasterUnits.
|
|
|
|
Arguments:
|
|
|
|
pPDev - Pointer to PDEVICE struct
|
|
iX - Amount to move in Master Units
|
|
|
|
Return Value:
|
|
|
|
The difference between the requested move and actual move
|
|
|
|
--*/
|
|
|
|
{
|
|
INT iDiff;
|
|
INT iScale;
|
|
|
|
//
|
|
// Don't do micro justification in graphics mode for device that
|
|
// set x position at leftmost position on page after printing a
|
|
// block of data OR Y position auto move to next Y row after priting
|
|
// block of data.
|
|
//
|
|
|
|
if (pPDev->pGlobals->cxafterblock == CXSBD_AT_CURSOR_X_ORIGIN ||
|
|
pPDev->pGlobals->cxafterblock == CXSBD_AT_GRXDATA_ORIGIN ||
|
|
pPDev->pGlobals->cyafterblock == CYSBD_AUTO_INCREMENT)
|
|
return iX;
|
|
|
|
//
|
|
// Convert Master units to Graphic units
|
|
//
|
|
// ptGrxScale has been adjusted to suit the current orientation.
|
|
//
|
|
if (pPDev->pOrientation && pPDev->pOrientation->dwRotationAngle != ROTATE_NONE &&
|
|
pPDev->pGlobals->bRotateCoordinate == FALSE)
|
|
iScale = pPDev->ptGrxScale.y;
|
|
else
|
|
iScale = pPDev->ptGrxScale.x;
|
|
|
|
iDiff = iX % iScale;
|
|
iX /= iScale;
|
|
|
|
if (iX > 0 )
|
|
{
|
|
INT iMaxBuf, iTmp;
|
|
BYTE rgch[ CCHMAXBUF ];
|
|
|
|
//
|
|
// Send the command, to send one block of data to the printer.
|
|
// Init the state variable first.
|
|
//
|
|
|
|
//
|
|
// BUG_BUG, how does this code work??
|
|
// What should we send for CMD_SENDBLOCKDATA?
|
|
//
|
|
|
|
//
|
|
// BUG_BUG, May be we should send BEGINGRAPHICS and ENDGRAPHICS later
|
|
//
|
|
|
|
pPDev->dwNumOfDataBytes = iX;
|
|
WriteChannel( pPDev, COMMANDPTR(pPDev->pDriverInfo, CMD_SENDBLOCKDATA));
|
|
|
|
iMaxBuf = CCHMAXBUF - (CCHMAXBUF % pPDev->ctl.sBytesPerPinPass);
|
|
iX *= pPDev->ctl.sBytesPerPinPass;
|
|
|
|
//
|
|
// Send out null graphics data, zeroes.
|
|
//
|
|
|
|
ZeroMemory( rgch, iX > CCHMAXBUF ? iMaxBuf : iX );
|
|
|
|
for ( ; iX > 0; iX -= iTmp)
|
|
{
|
|
iTmp = iX > iMaxBuf ? iMaxBuf : iX;
|
|
|
|
//
|
|
// BUG_BUG, OEMCustomization code might want to hook
|
|
// out this graphics move. Make this a bug when
|
|
// someone asks for it.
|
|
//
|
|
WriteSpoolBuf(pPDev, rgch, iTmp);
|
|
|
|
}
|
|
WriteChannel( pPDev, COMMANDPTR(pPDev->pDriverInfo, CMD_ENDBLOCKDATA));
|
|
|
|
return iDiff;
|
|
}
|
|
|
|
return iDiff;
|
|
}
|
|
|
|
INT
|
|
WriteChannel(
|
|
PDEV *pPDev,
|
|
COMMAND *pCmd
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine performs the following tasks:
|
|
- Parse through the cmd invocation str and build a CMDPARAM struct
|
|
for every %dddd encountered.
|
|
- Call IProcessTokenStream to calculate the arToken value of the parameter.
|
|
- Check for lMin and lMax in PARAMETER struct and send the command
|
|
multiple times, if necessary(MaxRepeat was seen).
|
|
- Call SendCmd to send the command to the printer.
|
|
|
|
Arguments:
|
|
|
|
pPDev - Pointer to PDEVICE struct
|
|
pCmd - Pointer to command struct to send, used for sending sequence section cmds
|
|
and predefined Unidrv commands
|
|
|
|
Return Value:
|
|
|
|
The last value send to the printer
|
|
|
|
--*/
|
|
#define MAX_NUM_PARAMS 16
|
|
{
|
|
BYTE *pInvocationStr;
|
|
CMDPARAM *pCmdParam, *pCmdParamHead;
|
|
INT i, iParamCount, iStrCount, iLastValue = 0, iRet;
|
|
PARAMETER *pParameter;
|
|
BOOL bMaxRepeat = FALSE;
|
|
CMDPARAM arCmdParam[MAX_NUM_PARAMS];
|
|
|
|
if (pCmd == NULL)
|
|
{
|
|
TERSE(("WriteChannel - Command PTR is NULL.\n"))
|
|
return (NOOCD);
|
|
}
|
|
//
|
|
// first check if this command requires callback
|
|
//
|
|
if (pCmd->dwCmdCallbackID != NO_CALLBACK_ID)
|
|
{
|
|
PLISTNODE pListNode;
|
|
DWORD dwCount = 0; // count of parameters used
|
|
DWORD adwParams[MAX_NUM_PARAMS]; // max 16 params for each callback
|
|
|
|
if (!pPDev->pfnOemCmdCallback)
|
|
return (NOOCD);
|
|
//
|
|
// check if this callback uses any parameters
|
|
//
|
|
pListNode = LISTNODEPTR(pPDev->pDriverInfo, pCmd->dwStandardVarsList);
|
|
while (pListNode)
|
|
{
|
|
if (dwCount >= MAX_NUM_PARAMS)
|
|
{
|
|
ASSERTMSG(FALSE,("Command callback exceeds # of parameters limit.\n"));
|
|
return (NOOCD);
|
|
}
|
|
|
|
adwParams[dwCount++] = *(pPDev->arStdPtrs[pListNode->dwData]);
|
|
|
|
if (pListNode->dwNextItem == END_OF_LIST)
|
|
break;
|
|
else
|
|
pListNode = LISTNODEPTR(pPDev->pDriverInfo, pListNode->dwNextItem);
|
|
}
|
|
|
|
FIX_DEVOBJ(pPDev, EP_OEMCommandCallback);
|
|
|
|
iRet = 0;
|
|
|
|
if(pPDev->pOemEntry)
|
|
{
|
|
if(((POEM_PLUGIN_ENTRY)pPDev->pOemEntry)->pIntfOem ) // OEM plug in uses COM and function is implemented.
|
|
{
|
|
HRESULT hr ;
|
|
hr = HComCommandCallback((POEM_PLUGIN_ENTRY)pPDev->pOemEntry,
|
|
(PDEVOBJ)pPDev, pCmd->dwCmdCallbackID, dwCount, adwParams, &iRet);
|
|
if(SUCCEEDED(hr))
|
|
; // cool !
|
|
}
|
|
else
|
|
{
|
|
iRet = (pPDev->pfnOemCmdCallback)((PDEVOBJ)pPDev, pCmd->dwCmdCallbackID,
|
|
dwCount, adwParams);
|
|
}
|
|
}
|
|
|
|
return iRet ;
|
|
}
|
|
|
|
//
|
|
// no cmd callback. Process the string.
|
|
//
|
|
pInvocationStr = CMDOFFSET_TO_PTR(pPDev, pCmd->strInvocation.loOffset);
|
|
iStrCount = pCmd->strInvocation.dwCount;
|
|
|
|
pCmdParam = pCmdParamHead = arCmdParam;
|
|
iParamCount = 0;
|
|
//
|
|
// Process the parameter from the invocation str.
|
|
//
|
|
|
|
for (i= 0; i < iStrCount; i++)
|
|
{
|
|
if (pInvocationStr[i] == '%')
|
|
{
|
|
if (pInvocationStr[i + 1] == '%')
|
|
{
|
|
//
|
|
// Do nothing, just skip %%
|
|
//
|
|
|
|
i += 1;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Build a list of CMDPARAM, one for each %dddd encounter
|
|
// in cmd invocation string.
|
|
//
|
|
|
|
BYTE *pCurrent = pInvocationStr + i + 1;
|
|
|
|
//
|
|
// Increment the parameter count
|
|
//
|
|
|
|
iParamCount++;
|
|
|
|
if (iParamCount > MAX_NUM_PARAMS)
|
|
{
|
|
ASSERT (iParamCount <= MAX_NUM_PARAMS);
|
|
return (NOOCD);
|
|
}
|
|
|
|
//
|
|
// Copy the 4 character that represent to parameter index from cmd str
|
|
// pInvocationStr + i points to %, pInvocationStr + i + 1 points
|
|
// to first digit of param index
|
|
//
|
|
|
|
pParameter = PGetParameter(pPDev, pCurrent);
|
|
|
|
//
|
|
// Initialize CMDPARAM for SendCmd
|
|
//
|
|
|
|
//
|
|
// IProcessTokenStream calculate the integer value parameter
|
|
// from the token stream in PARAMETER, bMaxRepeat is set
|
|
// to TRUE if OP_MAX_REPEAT operator was encountered.
|
|
//
|
|
|
|
pCmdParam->iValue = IProcessTokenStream(pPDev,
|
|
&pParameter->arTokens,
|
|
&bMaxRepeat);
|
|
|
|
//
|
|
// Save the last value calculated (will only be used by XMoveTo and YMoveTo)
|
|
// which assumes there is only one paramter per move command
|
|
//
|
|
|
|
iLastValue = pCmdParam->iValue;
|
|
|
|
pCmdParam->dwFormat = pParameter->dwFormat;
|
|
pCmdParam->dwDigits = pParameter->dwDigits;
|
|
pCmdParam->dwFlags = pParameter->dwFlags;
|
|
|
|
//
|
|
// Check for dwFlags PARAM_FLAG_MIN_USED and PARAM_FLAG_MAX_USED
|
|
//
|
|
|
|
if (pCmdParam->dwFlags & PARAM_FLAG_MIN_USED &&
|
|
pCmdParam->iValue < pParameter->lMin)
|
|
{
|
|
pCmdParam->iValue = pParameter->lMin;
|
|
}
|
|
|
|
if (pCmdParam->dwFlags & PARAM_FLAG_MAX_USED &&
|
|
!bMaxRepeat &&
|
|
pCmdParam->iValue > pParameter->lMax)
|
|
{
|
|
pCmdParam->iValue = pParameter->lMax;
|
|
|
|
}
|
|
|
|
//
|
|
// Move to next paramter
|
|
//
|
|
pCmdParam++;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// We are here means have a list of CMD parameter, pointed to by
|
|
// pCmdParamHead, one for each
|
|
// %dddd encountered, in the order they were encountered in invocation string
|
|
|
|
//
|
|
// MAJOR ASSUMPTION, GPD specification specifies that
|
|
// only ONE parameter is valid for commands that use OP_MAX_REPEAT operator
|
|
// So assume only one here (pCmdParamHead and pParameter are valid).
|
|
//
|
|
|
|
if (bMaxRepeat && pCmdParamHead->dwFlags & PARAM_FLAG_MAX_USED &&
|
|
pCmdParamHead->iValue > pParameter->lMax)
|
|
{
|
|
INT iRemainder, iRepeat;
|
|
|
|
ASSERT(iParamCount == 1);
|
|
|
|
iRemainder = pCmdParamHead->iValue % pParameter->lMax;
|
|
iRepeat = pCmdParamHead->iValue / pParameter->lMax;
|
|
|
|
while (iRepeat--)
|
|
{
|
|
pCmdParamHead->iValue = pParameter->lMax;
|
|
SendCmd(pPDev, pCmd, pCmdParamHead);
|
|
}
|
|
|
|
//
|
|
// Send remainder
|
|
//
|
|
if (iRemainder > 0)
|
|
{
|
|
pCmdParamHead->iValue = iRemainder;
|
|
SendCmd(pPDev, pCmd, pCmdParamHead);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Send the command to the printer
|
|
// SendCmd will process the command and format the parameters
|
|
// in the order encounter in the invocation string
|
|
//
|
|
|
|
SendCmd(pPDev, pCmd, pCmdParamHead);
|
|
}
|
|
|
|
return (iLastValue);
|
|
}
|
|
|
|
INT
|
|
WriteChannelEx(
|
|
PDEV *pPDev,
|
|
COMMAND *pCmd,
|
|
INT iRequestedValue,
|
|
INT iDeviceScaleFac
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine performs the following tasks:
|
|
- Call WriteChannel to write the command and get the
|
|
value of the last paramter calculated for the cmd.
|
|
- Use device factor to convert device units returned from WriteChannel to
|
|
master units
|
|
- Take the difference between requested and actual value
|
|
|
|
Arguments:
|
|
|
|
pPDev - Pointer to PDEVICE struct
|
|
pCmd - Pointer to command struct to send, used for sending sequence section cmds
|
|
and predefined Unidrv commands
|
|
iRequestedValue - Value of requested move command in Master units
|
|
iDeviceScaleFac - Scale factor to convert Device units to Master units
|
|
|
|
Return Value:
|
|
|
|
The difference between actual value and requested value in Master units
|
|
|
|
Note:
|
|
|
|
This function is only called only by XMoveTo and YMoveTo and assumes
|
|
that all move commands have only one parameter.
|
|
|
|
--*/
|
|
|
|
{
|
|
INT iActualValue;
|
|
|
|
//
|
|
// Get the device unit returned from WriteChannel and convert it
|
|
// to Master units based on the scale factor passed in
|
|
// Scale = Master/Device.
|
|
//
|
|
|
|
iActualValue = WriteChannel(pPDev, pCmd);
|
|
iActualValue *= iDeviceScaleFac;
|
|
|
|
return (iRequestedValue - iActualValue);
|
|
|
|
}
|
|
|
|
PPARAMETER
|
|
PGetParameter(
|
|
PDEV *pPDev,
|
|
BYTE *pInvocationStr
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine get the parameter index from pInvocationStr passed in and
|
|
return the PARAMETER struct associated with the index
|
|
|
|
Arguments:
|
|
|
|
pPDev - Pointer to PDEVICE struct
|
|
pInvocationStr - Pointer Invocation str containing the index
|
|
|
|
Return Value:
|
|
|
|
Pointer to PARAMETER struct associated with the index specified in
|
|
the invocation string.
|
|
|
|
Note:
|
|
|
|
Parameter index is the 4 bytes pointed to by pInvocationStr
|
|
|
|
--*/
|
|
{
|
|
|
|
BYTE arTemp[5];
|
|
INT iParamIndex;
|
|
PARAMETER *pParameter;
|
|
|
|
//
|
|
// Copy the 4 character that represent to parameter index from cmd str
|
|
// pInvocationStr
|
|
//
|
|
|
|
strncpy(arTemp, pInvocationStr, 4);
|
|
arTemp[4] = '\0';
|
|
iParamIndex = atoi(arTemp);
|
|
pParameter = PARAMETERPTR(pPDev->pDriverInfo, iParamIndex);
|
|
|
|
ASSERT(pParameter != NULL);
|
|
|
|
return (pParameter);
|
|
}
|
|
|
|
VOID
|
|
SendCmd(
|
|
PDEV *pPDev,
|
|
COMMAND *pCmd,
|
|
CMDPARAM *pParam
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by WriteChannel to write one command to the
|
|
printer via. WriteSpoolBuf. WriteChannel passes a pointer to an array
|
|
of CMDPARAM. Each CMDPARAM describes the parameter for each %dddd
|
|
encounter in cmd invocation string (the CMDPARAM is sorted in the order
|
|
encountered).
|
|
|
|
Arguments:
|
|
|
|
pPDev - Pointer to PDEVICE struct
|
|
pCmd - Pointer to COMMAND struct
|
|
pParam - Pointer to and array CMDPARAM struct, containing everything needed to format
|
|
the parameter
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
INT iInput, iOutput; // Used to index through input and output buffers
|
|
BYTE arOutputCmd[CCHMAXBUF]; // Output buffer to send to printer
|
|
PBYTE pInputCmd; // Pointer to Cmd invocation str.
|
|
|
|
//
|
|
// Get the command invocation string
|
|
//
|
|
|
|
pInputCmd = CMDOFFSET_TO_PTR(pPDev, pCmd->strInvocation.loOffset);
|
|
iOutput = 0;
|
|
|
|
//
|
|
// Go through all the bytes in the invocation string and transfer them
|
|
// to the output buffer. Replace %dddd with format value calculated
|
|
// and %% with %.
|
|
//
|
|
|
|
for (iInput = 0; iInput < (INT)pCmd->strInvocation.dwCount; iInput++)
|
|
{
|
|
if (pInputCmd[iInput] == '%' )
|
|
{
|
|
|
|
if (pInputCmd[iInput + 1] == '%')
|
|
{
|
|
//
|
|
// %% equals '%', skip over marker %%
|
|
//
|
|
|
|
arOutputCmd[iOutput++] = '%';
|
|
iInput += 1;
|
|
|
|
}
|
|
else
|
|
{
|
|
INT iValue;
|
|
DWORD dwFlags, dwDigits, dwFormat;
|
|
|
|
//
|
|
// Skip over the marker % and dddd for %dddd found in invocation str.
|
|
// Skip 4 bytes (%dddd)
|
|
//
|
|
|
|
iInput += 4;
|
|
|
|
dwDigits = pParam->dwDigits;
|
|
dwFlags = pParam->dwFlags;
|
|
dwFormat = pParam->dwFormat;
|
|
iValue = pParam->iValue;
|
|
pParam++;
|
|
|
|
//
|
|
// Format the parameter according the the dwFormat specified in PARAMETER struct
|
|
//
|
|
|
|
switch (dwFormat)
|
|
{
|
|
|
|
//
|
|
// case 'd': parameter as decimal number
|
|
// case 'D': same as case 'd' with + sign if value > 0
|
|
// case 'c': parameter as a single character
|
|
// case 'C': parameter as character plus '0'
|
|
// case 'f': parameter as decinal number with decimal point inserted
|
|
// before the second digit from the right.
|
|
// case 'l': parameter as word LSB first
|
|
// case 'm': parameter as word MSB first
|
|
// case 'q': parameter as Qume method, 1/48" movements
|
|
// case 'g': parameter as 2 *abs(param) + is_negative(param)
|
|
// case 'n': Canon integer encoding
|
|
// case 'v': NEC VFU encoding
|
|
// case '%': print a %
|
|
|
|
case 'D':
|
|
if (iValue > 0)
|
|
arOutputCmd[iOutput++] = '+';
|
|
//
|
|
// Fall through
|
|
//
|
|
|
|
case 'd':
|
|
if (dwDigits > 0 && dwFlags & PARAM_FLAG_FIELDWIDTH_USED)
|
|
{
|
|
//
|
|
// Temp call to get the number of digits for the iValue
|
|
//
|
|
|
|
int iParamDigit = itoA(arOutputCmd + iOutput, iValue);
|
|
|
|
for ( ; iParamDigit < (INT)dwDigits; iParamDigit++)
|
|
{
|
|
//
|
|
// Zero pads
|
|
//
|
|
arOutputCmd[iOutput++] = '0';
|
|
}
|
|
}
|
|
iOutput += itoA( arOutputCmd + iOutput, iValue);
|
|
break;
|
|
|
|
case 'C':
|
|
iValue += '0';
|
|
|
|
//
|
|
// Fall through
|
|
//
|
|
|
|
case 'c':
|
|
arOutputCmd[iOutput++] = (BYTE)iValue;
|
|
break;
|
|
|
|
case 'f':
|
|
{
|
|
int x, y, i;
|
|
BYTE arTemp[CCHMAXBUF];
|
|
LPSTR pCurrent = arOutputCmd + iOutput;
|
|
ULONG cchpCurrentLen = 0;
|
|
if ( (LONG)CCHOF(arOutputCmd) - iOutput > 0 )
|
|
{
|
|
cchpCurrentLen = CCHOF(arOutputCmd) - iOutput;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
|
|
x = iValue /100;
|
|
y = iValue % 100;
|
|
|
|
iOutput += itoA(pCurrent, x);
|
|
|
|
StringCchCatA ( pCurrent, cchpCurrentLen, "."); //strcat(pCurrent, ".");
|
|
|
|
i = itoA(arTemp, y);
|
|
|
|
//
|
|
// Take care of the case where the mod yields 1 digit, pad a zero
|
|
//
|
|
|
|
if (i < 2 )
|
|
{
|
|
StringCchCatA ( pCurrent, cchpCurrentLen, "0"); //strcat(pCurrent, "0");
|
|
}
|
|
|
|
StringCchCatA(pCurrent, cchpCurrentLen, arTemp); //strcat(pCurrent, arTemp);
|
|
|
|
//
|
|
// Increment iOutput to include the 2 digits after the
|
|
// decimal and the "."
|
|
//
|
|
|
|
iOutput += 3;
|
|
}
|
|
break;
|
|
|
|
case 'l':
|
|
arOutputCmd[iOutput++] = (BYTE)iValue;
|
|
arOutputCmd[iOutput++] = (BYTE)(iValue >> 8);
|
|
break;
|
|
|
|
case 'm':
|
|
arOutputCmd[iOutput++] = (BYTE)(iValue >> 8);
|
|
arOutputCmd[iOutput++] = (BYTE)iValue;
|
|
break;
|
|
|
|
|
|
case 'q':
|
|
arOutputCmd[ iOutput++ ] = (BYTE)(((iValue >> 8) & 0xf) + '@');
|
|
arOutputCmd[ iOutput++ ] = (BYTE)(((iValue >> 4) & 0xf) + '@');
|
|
arOutputCmd[ iOutput++ ] = (BYTE)((iValue & 0xf) + '@');
|
|
break;
|
|
|
|
case 'g':
|
|
{
|
|
if (iValue >= 0)
|
|
iValue = iValue << 1;
|
|
else
|
|
iValue = ((-iValue) << 1) + 1;
|
|
|
|
while (iValue >= 64)
|
|
{
|
|
arOutputCmd[iOutput++] = (char)((iValue & 0x003f) + 63);
|
|
iValue >>= 6;
|
|
}
|
|
arOutputCmd[iOutput++] = (char)(iValue + 191);
|
|
|
|
}
|
|
break;
|
|
|
|
case 'n':
|
|
{
|
|
WORD absParam = (WORD)abs(iValue);
|
|
WORD absTmp;
|
|
|
|
if (absParam <= 15)
|
|
{
|
|
arOutputCmd[iOutput++] = 0x20
|
|
| ((iValue >= 0)? 0x10:0)
|
|
| (BYTE)absParam;
|
|
}
|
|
else if (absParam <= 1023)
|
|
{
|
|
arOutputCmd[iOutput++] = 0x40
|
|
| (BYTE)(absParam/16);
|
|
arOutputCmd[iOutput++] = 0x20
|
|
| ((iValue >= 0)? 0x10:0)
|
|
| (BYTE)(absParam % 16);
|
|
}
|
|
else
|
|
{
|
|
arOutputCmd[iOutput++] = 0x40
|
|
| (BYTE)(absParam / 1024);
|
|
absTmp = absParam % 1024;
|
|
arOutputCmd[iOutput++] = 0x40
|
|
| (BYTE)(absTmp / 16);
|
|
arOutputCmd[iOutput++] = 0x20
|
|
| ((iValue >= 0)? 0x10:0)
|
|
| (BYTE)(absTmp % 16);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'v':
|
|
//
|
|
// NEC VFU(Vertical Format Unit)
|
|
//
|
|
// VFU is a command to specify a paper size
|
|
// (the length of form feed for the NEC 20PL dotmatrix
|
|
// printer.
|
|
//
|
|
// On NEC dotmatrix printer, 1 line is 1/6 inch.
|
|
// If you want to specify N line paper size,
|
|
// you need to send GS, N+1 Data and RS.
|
|
//
|
|
// GS (0x1d)
|
|
// TOF Data (0x41, 0x00)
|
|
// Data (0x40, 0x00)
|
|
// Data (0x40, 0x00)
|
|
// Data (0x40, 0x00)
|
|
// ..
|
|
// ..
|
|
// Data (0x40, 0x00)
|
|
// TOF Data (0x41, 0x00)
|
|
// RS (0x1e)
|
|
//
|
|
arOutputCmd[iOutput++] = 0x1D;
|
|
arOutputCmd[iOutput++] = 0x41;
|
|
arOutputCmd[iOutput++] = 0x00;
|
|
while(--iValue > 0)
|
|
{
|
|
if( iOutput >= CCHMAXBUF - 5)
|
|
{
|
|
WriteSpoolBuf( pPDev, arOutputCmd, iOutput );
|
|
iOutput = 0;
|
|
}
|
|
|
|
arOutputCmd[iOutput++] = 0x40;
|
|
arOutputCmd[iOutput++] = 0x00;
|
|
}
|
|
arOutputCmd[iOutput++] = 0x41;
|
|
arOutputCmd[iOutput++] = 0x00;
|
|
arOutputCmd[iOutput++] = 0x1E;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Copy the input to output and increment the output count
|
|
//
|
|
|
|
arOutputCmd[iOutput++] = pInputCmd[iInput];
|
|
|
|
}
|
|
|
|
//
|
|
// Write output cmd buffer out to spool buffer in the case
|
|
// where it full or nearly full (2/3 full)
|
|
//
|
|
|
|
if( iOutput >= (2 * sizeof( arOutputCmd )) / 3 )
|
|
{
|
|
WriteSpoolBuf( pPDev, arOutputCmd, iOutput );
|
|
iOutput = 0;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Write the data to spool buffer
|
|
//
|
|
|
|
if ( iOutput > 0 )
|
|
WriteSpoolBuf( pPDev, arOutputCmd, iOutput );
|
|
|
|
|
|
return;
|
|
}
|
|
|
|
INT
|
|
IProcessTokenStream(
|
|
PDEV *pPDev,
|
|
ARRAYREF *pToken ,
|
|
PBOOL pbMaxRepeat
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function process a given token stream and calculate the value
|
|
for the command parameter.
|
|
|
|
Arguments:
|
|
|
|
pPDev - Pointer to PDEVICE
|
|
pToken - Pointer to an array of TOKENSTREAM representing the operands
|
|
and operators for RPN calc. pToken->dwCount is the number of
|
|
TOKENSTREAM in the array. pToken->loOffset is the index
|
|
to the first TOKENSTREAM in the array.
|
|
pbMaxRepeat - Indicates a max repeat operator was seen in token stream
|
|
|
|
Return Value:
|
|
|
|
The calculated value, always an INT and set pbMaxRepeat TRUE if
|
|
the OP_MAX_REPEAT operator was seen.
|
|
|
|
--*/
|
|
|
|
{
|
|
INT iRet = 0, sp = 0;
|
|
INT arStack[MAX_STACK_SIZE];
|
|
DWORD dwCount = pToken->dwCount;
|
|
TOKENSTREAM * ptstrToken = TOKENSTREAMPTR(pPDev->pDriverInfo, pToken->loOffset);
|
|
|
|
|
|
*pbMaxRepeat = FALSE;
|
|
|
|
while (dwCount--)
|
|
{
|
|
switch(ptstrToken->eType)
|
|
{
|
|
case OP_INTEGER:
|
|
if (sp >= MAX_STACK_SIZE)
|
|
goto ErrorExit;
|
|
|
|
arStack[sp++] = (INT)ptstrToken->dwValue;
|
|
break;
|
|
|
|
case OP_VARI_INDEX:
|
|
// dwValue is the index to standard variable list
|
|
if (sp >= MAX_STACK_SIZE)
|
|
goto ErrorExit;
|
|
|
|
arStack[sp++] = (INT)*(pPDev->arStdPtrs[ptstrToken->dwValue]);
|
|
break;
|
|
|
|
case OP_MIN:
|
|
if (--sp <= 0)
|
|
goto ErrorExit;
|
|
|
|
if (arStack[sp-1] > arStack[sp])
|
|
arStack[sp-1] = arStack[sp];
|
|
break;
|
|
|
|
case OP_MAX:
|
|
if (--sp <= 0)
|
|
goto ErrorExit;
|
|
|
|
if (arStack[sp-1] < arStack[sp])
|
|
arStack[sp-1] = arStack[sp];
|
|
break;
|
|
|
|
case OP_ADD:
|
|
if (--sp <= 0)
|
|
goto ErrorExit;
|
|
|
|
arStack[sp-1] += arStack[sp];
|
|
break;
|
|
|
|
case OP_SUB:
|
|
if (--sp <= 0)
|
|
goto ErrorExit;
|
|
|
|
arStack[sp-1] -= arStack[sp];
|
|
break;
|
|
|
|
case OP_MULT:
|
|
if (--sp <= 0)
|
|
goto ErrorExit;
|
|
|
|
arStack[sp-1] *= arStack[sp];
|
|
break;
|
|
|
|
case OP_DIV:
|
|
if (--sp <= 0)
|
|
goto ErrorExit;
|
|
|
|
arStack[sp-1] /= arStack[sp];
|
|
break;
|
|
|
|
case OP_MOD:
|
|
if (--sp <= 0)
|
|
goto ErrorExit;
|
|
|
|
arStack[sp-1] %= arStack[sp];
|
|
break;
|
|
|
|
case OP_MAX_REPEAT:
|
|
//
|
|
// If pbMaxRepeat is TRUE, can only send the parameters in
|
|
// increment of lMax repeat value or smaller, set in Parameter list until
|
|
//
|
|
|
|
*pbMaxRepeat = TRUE;
|
|
break;
|
|
|
|
case OP_HALT:
|
|
if (sp == 0)
|
|
goto ErrorExit;
|
|
|
|
iRet = arStack[--sp];
|
|
break;
|
|
|
|
default:
|
|
VERBOSE (("IProcessTokenStream - unknown command!"));
|
|
break;
|
|
}
|
|
ptstrToken++;
|
|
}
|
|
|
|
return (iRet);
|
|
|
|
ErrorExit:
|
|
ERR(("IProcessTokenStream, invalid stack pointer"));
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
itoA( LPSTR buf, INT n )
|
|
{
|
|
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;
|
|
}
|
|
|
|
BOOL
|
|
BUniWritePrinter(
|
|
IN PDEV* pPDev,
|
|
IN LPVOID pBuf,
|
|
IN DWORD cbBuf,
|
|
OUT LPDWORD pcbWritten)
|
|
{
|
|
DWORD dwCount;
|
|
BOOL bReturn = FALSE;
|
|
|
|
//
|
|
// Is there any plug-in that hooks WritePrinter?
|
|
// If there is, the plug-in need to take care of all output.
|
|
// Call plug-in's WritePrinter method.
|
|
//
|
|
if(pPDev->pOemEntry && pPDev->fMode2 & PF2_WRITE_PRINTER_HOOKED)
|
|
{
|
|
START_OEMENTRYPOINT_LOOP(pPDev);
|
|
|
|
//
|
|
// OEM plug in uses COM and function is implemented.
|
|
//
|
|
if(((POEM_PLUGIN_ENTRY)pPDev->pOemEntry)->pIntfOem )
|
|
{
|
|
//
|
|
// Call only the first available WritePrinter method in
|
|
// multiple plug-ins.
|
|
//
|
|
|
|
if (pOemEntry->dwFlags & OEMWRITEPRINTER_HOOKED)
|
|
{
|
|
HRESULT hr;
|
|
|
|
//
|
|
// WritePrinter is supported by this plug-in DLL.
|
|
// Plug-in's WritePrinter should not return E_NOTIMPL or
|
|
// E_NOTINTERFACE.
|
|
//
|
|
pPDev->fMode2 |= PF2_CALLING_OEM_WRITE_PRINTER;
|
|
hr = HComWritePrinter((POEM_PLUGIN_ENTRY)pPDev->pOemEntry,
|
|
(PDEVOBJ)pPDev,
|
|
pBuf,
|
|
cbBuf,
|
|
pcbWritten);
|
|
pPDev->fMode2 &= ~PF2_CALLING_OEM_WRITE_PRINTER;
|
|
|
|
//
|
|
// If Plug-in's WritePrinter succeeded, return TRUE.
|
|
//
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
//
|
|
// If the method is called and succeeded, return TRUE.
|
|
//
|
|
bReturn = TRUE;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// If WritePrinter method failed, break.
|
|
//
|
|
bReturn = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
END_OEMENTRYPOINT_LOOP;
|
|
|
|
if (pPDev->pVectorProcs != NULL)
|
|
{
|
|
pPDev->devobj.pdevOEM = pPDev->pVectorPDEV;
|
|
}
|
|
}
|
|
//
|
|
// If there is no WritePrinter hook, call spooler API WritePrinter.
|
|
//
|
|
else
|
|
{
|
|
bReturn = WritePrinter(pPDev->devobj.hPrinter,
|
|
pBuf,
|
|
cbBuf,
|
|
pcbWritten)
|
|
&& cbBuf == *pcbWritten;
|
|
}
|
|
|
|
return bReturn;
|
|
}
|
|
|