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