Leaked source code of windows server 2003
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

/*++
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;
}