/* Copyright (c) 1992-2000 Microsoft Corporation Module Name: psexe.c Abstract: This file contains the code required to actually communicate with the PSTODIB dll and rasterize a job. Any required information is passed via some named shared memory, the name of which is passed on the command line. This name is guaranteed to be unique to the system and thus multiple postscript jobs can be imaged at the same time. --*/ #include #include #include #include #include #include #include #include "..\..\lib\psdiblib.h" #include "..\..\..\ti\psglobal\pstodib.h" #include "..\psprint\psshmem.h" #include "psexe.h" #include #include #include "debug.h" // By defining the following, each page is blited to the desktop this way // one can verify the interpreter is running without waiting for the printer // to print // //#define BLIT_TO_DESKTOP // By defining the following, you will cause all the code to get executed // except the part that REALLY writes to the printer. This is most useful in // conjuction with the BLIT_TO_DESKTOP so you can run the intrepeter and // have the output go to the desktop. I found this very useful during // development. It may see useful when porting to new windows NT platforms. //#define IGNORE_REAL_PRINTING // This is a hack, it should eventually be taken out, the reason this is here, // is becuse simply setting the windows page type in the DEVMODE structure, // is not good enough for the RASTER printer driver (of which most printer // drivers are based. With this table we borrowed from the spooler, we // can set the correct FORM name which the printer drivers respect and can // test different page sizes from the mac... // DJC HACK HACK PTSTR forms[] = { L"Letter", L"Letter Small", L"Tabloid", L"Ledger", L"Legal", L"Statement", L"Executive", L"A3", L"A4", L"A4 Small", L"A5", L"B4", L"B5", L"Folio", L"Quarto", L"10x14", L"11x17", L"Note", L"Envelope #9", L"Envelope #10", L"Envelope #11", L"Envelope #12", L"Envelope #14", L"C size sheet", L"D size sheet", L"E size sheet", L"Envelope DL", L"Envelope C5", L"Envelope C3", L"Envelope C4", L"Envelope C6", L"Envelope C65", L"Envelope B4", L"Envelope B5", L"Envelope B6", L"Envelope", L"Envelope Monarch", L"6 3/4 Envelope", L"US Std Fanfold", L"German Std Fanfold", L"German Legal Fanfold", NULL, }; // This table translates from internal PSERR_* errors, defined in pstodib.h // to errors in the event log file that the user can see in the event viewer // typedef struct { DWORD dwOutputError; DWORD dwPsError; } PS_TRANSLATE_ERRORCODES; PS_TRANSLATE_ERRORCODES adwTranslate[] = { EVENT_PSTODIB_INIT_ACCESS, PSERR_INTERPRETER_INIT_ACCESS_VIOLATION, EVENT_PSTODIB_JOB_ACCESS, PSERR_INTERPRETER_JOB_ACCESS_VIOLATION, EVENT_PSTODIB_STRING_SEQ, PSERR_LOG_ERROR_STRING_OUT_OF_SEQUENCE, EVENT_PSTODIB_FRAME_ALLOC, PSERR_FRAME_BUFFER_MEM_ALLOC_FAILED, EVENT_PSTODIB_FONTQUERYFAIL, PSERR_FONT_QUERY_PROBLEM, EVENT_PSTODIB_INTERNAL_FONT, PSERR_EXCEEDED_INTERNAL_FONT_LIMIT, EVENT_PSTODIB_MEM_FAIL, PSERR_LOG_MEMORY_ALLOCATION_FAILURE }; // Globals, this is global because the abortproc we pass in to the // graphics engine does not allow us to specify any data to pass to the // abort proc PSEXEDATA Data; /*** PsPrintCallBack * * This is the function pstodib calls whenever certain events occur * * Entry: * pPsToDib = Pointer to the current PSTODIB structure * pPsEvent = defines the event that is occuring * * Returns: * True = Event was handled, interpreter should continue execution * False = Abnormal termination, the interpreter should stop * */ BOOL CALLBACK PsPrintCallBack( IN struct _PSDIBPARMS *pPsToDib, IN OUT PPSEVENTSTRUCT pPsEvent) { BOOL bRetVal=TRUE; // Success in case we dont support // Decide on a course of action based on the event passed in // switch( pPsEvent->uiEvent ) { case PSEVENT_PAGE_READY: // The data in the pPsEvent signifies the data we need to paint.. // for know we will treat the data as one text item null // terminated simply for testing... // bRetVal = PsPrintGeneratePage( pPsToDib, pPsEvent ); break; case PSEVENT_STDIN: // The interpreter is asking for some data so simply call // the print subsystem to try to satisfy the request // bRetVal = PsHandleStdInputRequest( pPsToDib, pPsEvent ); break; case PSEVENT_SCALE: // // Here is an opportunity to modify the current transformation // matrix x and y values, this is used to image a 150 dpi job // on a 300 dpi interpreter frame. // bRetVal = PsHandleScaleEvent( pPsToDib, pPsEvent); break; case PSEVENT_ERROR_REPORT: // // End of job, there may have been errors so the psexe component // may print an error page to the target printer depending on the // extent of the errors // bRetVal = PsGenerateErrorPage( pPsToDib, pPsEvent); break; case PSEVENT_GET_CURRENT_PAGE_TYPE: // // The interpreter usually queries the current page type at // startup time, and uses this page type if the JOB did not // specifically specify a page type. // bRetVal = PsGetCurrentPageType( pPsToDib, pPsEvent); break; case PSEVENT_NON_PS_ERROR: // // Some sort of error occured, that was NOT a postscript error. // Examples might be resource allocation failure, or access to // a resource has unexpectantly ended. // bRetVal = PsLogNonPsError( pPsToDib, pPsEvent ); break; } return bRetVal; } /*** PsPrintTranslateErrorCode * * This routine simply uses the error table to translate between errors * internal to pstodib dib, and errors in the macprint.exe event file * which may be reported in the event log * * Entry: * dwPsErr = PsToDib internal error number * * * Returns: * The corresponding error number in the event error file. * * */ DWORD PsTranslateErrorCode( IN DWORD dwPsErr ) { int i; for ( i = 0 ;i < sizeof(adwTranslate)/sizeof(adwTranslate[0]) ;i++ ) { if (dwPsErr == adwTranslate[i].dwPsError) { return( adwTranslate[i].dwOutputError); } } return ( EVENT_PSTODIB_UNDEFINED_ERROR ); } /*** PsLogNonPsError * * This function logs an internal pstodib error * * Entry: * pPsToDib = Pointer to the current PSTODIB structure * pPsEvent = defines the NonPsError event structure with info * about the error that is occuring * * Returns: * True = Success, the event was logged * False = Failure could not log the error * */ BOOL PsLogNonPsError( IN PPSDIBPARMS pPsToDib, IN PPSEVENTSTRUCT pPsEvent ) { PPSEVENT_NON_PS_ERROR_STRUCT pPsError; PPSEXEDATA pData; LPTSTR aStrs[2]; DWORD dwEventError; TCHAR atchar[10]; WORD wStringCount; if (!(pData = ValidateHandle(pPsToDib->hPrivateData))) { return(FALSE); } pPsError = (PPSEVENT_NON_PS_ERROR_STRUCT) pPsEvent->lpVoid; dwEventError = PsTranslateErrorCode( pPsError->dwErrorCode); if (dwEventError == EVENT_PSTODIB_UNDEFINED_ERROR) { wsprintf( atchar,TEXT("%d"), pPsError->dwErrorCode); aStrs[1] = atchar; wStringCount = 2; }else{ wStringCount = 1; } // // Set the document name so it gets recorded in the log file // aStrs[0] = pData->pDocument; PsLogEvent( dwEventError, wStringCount, aStrs, pPsError->bError ? PSLOG_ERROR : 0 ); return(TRUE); } /*** PsGenerateErrorPage * * This function generates an error page showing the last few postscript * errors that occured. * * Entry: * pPsToDib = Pointer to the current PSTODIB structure * pPsEvent = defines the postscript errors that may have occured * * Returns: * True = Event was handled, interpreter should continue execution * False = Abnormal termination, the interpreter should stop * */ BOOL PsGenerateErrorPage( IN PPSDIBPARMS pPsToDib, IN OUT PPSEVENTSTRUCT pPsEvent) { PPSEVENT_ERROR_REPORT_STRUCT pPsErr; PPSEXEDATA pData; BOOL bDidStartPage = FALSE; HGDIOBJ hOldFont=NULL; HGDIOBJ hNewFont=NULL; HGDIOBJ hNewBoldFont=NULL; LOGFONT lfFont; HPEN hNewPen=NULL; HPEN hOldPen=NULL; BOOL bRetVal=TRUE; int iCurXPos; int iCurYPos; int i; TEXTMETRIC tm; int iYMove; PCHAR pChar; int iLen; LPJOB_INFO_1 lpJob1 = NULL; DWORD dwRequired; SIZE UserNameSize; HGDIOBJ hStockFont; if (!(pData = ValidateHandle(pPsToDib->hPrivateData))) { return(FALSE); } // // Clean up access to our error struct for easier access // pPsErr = (PPSEVENT_ERROR_REPORT_STRUCT) pPsEvent->lpVoid; // // Only report the error page if there are actual errors and ONLY // if the job had a FLUSHING mode, ie the error was critical enough // to dump the rest of the postscript job. This is done becuase // some jobs have warnings, but they are not fatal and the jobs actually // print fine, there is no need to confuse the user with an error page // if the job came out fine. // if( pPsErr->dwErrCount && (pPsErr->dwErrFlags & PSEVENT_ERROR_REPORT_FLAG_FLUSHING )) { // // Set up a do, so we can break out of any errors without using a // goto. // do { if (!GetJob( pData->hPrinter, pData->JobId, 1, (LPBYTE) NULL, 0, &dwRequired)) { if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { // Get some memory lpJob1 = (LPJOB_INFO_1) LocalAlloc( LPTR, dwRequired); if (lpJob1) { if (!GetJob(pData->hPrinter, pData->JobId, 1, (LPBYTE) lpJob1, dwRequired, &dwRequired)) { LocalFree( (HLOCAL) lpJob1 ); lpJob1 = NULL; } } } } // Verify that we have a DC created!!! if( !PsVerifyDCExistsAndCreateIfRequired( pData )) { bRetVal = FALSE; break; } if( StartPage( pData->hDC ) <= 0 ) { bRetVal = FALSE; break; } bDidStartPage = TRUE; SetMapMode( pData->hDC, MM_LOENGLISH ); hStockFont = GetStockObject( ANSI_VAR_FONT ); if (hStockFont == (HGDIOBJ) NULL) { bRetVal = FALSE; break; } if( GetObject( hStockFont, sizeof(lfFont), (LPVOID) &lfFont) == 0 ) { bRetVal = FALSE; break; } // // Create the error item font in the correct size // lfFont.lfHeight = PS_ERR_FONT_SIZE; lfFont.lfWidth = 0; hNewFont = CreateFontIndirect( &lfFont); if (hNewFont == (HFONT) NULL) { bRetVal = FALSE; break; } // Create the error Header font // lfFont.lfHeight = PS_ERR_HEADER_FONT_SIZE; lfFont.lfWeight = FW_BOLD; hNewBoldFont = CreateFontIndirect( &lfFont ); if (hNewBoldFont == (HFONT) NULL) { bRetVal = FALSE; break; } hOldFont = SelectObject( pData->hDC, hNewBoldFont); if (hOldFont == (HFONT)NULL) { bRetVal = FALSE; break; } if (!GetTextMetrics(pData->hDC, &tm)) { bRetVal = FALSE; break; } // // Set up how much to move vertically for positioning each line // iYMove = tm.tmHeight + tm.tmExternalLeading; // // Set the starting positiongs // iCurXPos = PS_INCH; iCurYPos = -PS_INCH; // // If we have info about the job then show it // if (lpJob1) { if (lpJob1->pUserName != NULL) { if (!GetTextExtentPoint( pData->hDC, lpJob1->pUserName, lstrlen(lpJob1->pUserName), &UserNameSize) ) { bRetVal = FALSE; break; } if( !TextOut( pData->hDC, iCurXPos, iCurYPos, lpJob1->pUserName, lstrlen(lpJob1->pUserName))) { bRetVal = FALSE; break; } }else{ UserNameSize.cx = 0; UserNameSize.cy = 0; } if (lpJob1->pDocument != NULL) { if( !TextOut( pData->hDC, iCurXPos + PS_QUART_INCH + UserNameSize.cx, iCurYPos, lpJob1->pDocument, lstrlen(lpJob1->pDocument))) { bRetVal = FALSE; break; } } } // // Adjust the current position // iCurYPos -= (iYMove + PS_ERR_LINE_WIDTH ); // Draw A nice line // hNewPen = CreatePen( PS_SOLID, PS_ERR_LINE_WIDTH, RGB(0,0,0)); if (hNewPen == (HPEN) NULL ) { bRetVal = FALSE; break; } // Make our new pen active // hOldPen = SelectObject( pData->hDC, hNewPen ); // Draw the line // MoveToEx( pData->hDC, iCurXPos, iCurYPos, NULL); LineTo( pData->hDC, iCurXPos + PS_ERR_LINE_LEN, iCurYPos); // Put the old pen back // SelectObject( pData->hDC, hOldPen); // Delete the pen we created // DeleteObject( hNewPen ); // Reset the line // iCurYPos -= PS_ERR_LINE_WIDTH; // Now Select the normal font // SelectObject( pData->hDC, hNewFont); // Get the updated text metrics for the new font // if (!GetTextMetrics(pData->hDC, &tm)){ bRetVal = FALSE; break; } iYMove = tm.tmHeight + tm.tmExternalLeading; // Now display each of the errors that came from PSTODIB // i = (int) pPsErr->dwErrCount; while (--i) { pChar = pPsErr->paErrs[i]; iLen = lstrlenA( pChar ); if ( !TextOutA( pData->hDC, iCurXPos, iCurYPos, pChar, iLen)) { bRetVal = FALSE; break; } iCurYPos -= (iYMove + iYMove / 3); } break; } while ( 1 ); if (!bRetVal) { PsLogEventAndIncludeLastError(EVENT_PSTODIB_ERROR_STARTPG_FAIL,TRUE); } // Clenup the DC. // if (hOldFont != (HFONT) NULL) { SelectObject( pData->hDC, hOldFont); } if (hNewFont != (HFONT) NULL) { DeleteObject( hNewFont); } if (hNewBoldFont != (HFONT)NULL) { DeleteObject( hNewBoldFont ); } if (bDidStartPage) { EndPage( pData->hDC ); } // Free the job info memory if we had any if (lpJob1) { LocalFree((HLOCAL) lpJob1); } } return(bRetVal); } /*** PsHandleScaleEvent * * This function handles scaling the current transformation matrix * (the way logical units are mapped to device units in the interpreter) in * order to simulate different page sizes for non 300 dpi devices. this * done by scaling the transformation matrix such that only the portion * of the frame buffer which can be transfered to the target printer is used. * for example if we were going to a 150 dpi device then exactly half of * 300 dpi frame buffer would be showable on the 150 dpi device, the rest of the * frame buffer would be useless as it would extend beyond the imageable area * of the printer. Thus if we scaled the current tranformation matrix by the * ration of the target device resolution over 300 (the default pstodib resolution) * then graphic objects that used to be 8 inches would now be 4 and thus take * up HALF the space they used to. * * errors that occured. * * Entry: * pPsToDib = Pointer to the current PSTODIB structure * pPsEvent = defines the current scale information for the current * postscript tranformation matrix * * Returns: * True = This can never fail * */ BOOL PsHandleScaleEvent( IN PPSDIBPARMS pPsToDib, IN OUT PPSEVENTSTRUCT pPsEvent) { PPS_SCALE pScale; pScale = (PPS_SCALE) pPsEvent->lpVoid; pScale->dbScaleX = (double) pPsToDib->uiXDestRes / (double) pScale->uiXRes; pScale->dbScaleY = (double) pPsToDib->uiYDestRes / (double) pScale->uiYRes; #ifdef BLIT_TO_DESKTOP pScale->dbScaleX *= .25; pScale->dbScaleY *= .25; #endif return(TRUE); } /*** ValidateHandle * * Validates the handle passed in to make sure its correct, if its not * it also logs an error. * * * Entry: * hQProc = Handle to data block * * Returns: * A valid ptr to a session block. * NULL - failure the handle passed in was not what was expected. */ PPSEXEDATA ValidateHandle( HANDLE hQProc ) { PPSEXEDATA pData = (PPSEXEDATA)hQProc; if (pData && pData->signature == PSEXE_SIGNATURE) { return( pData ); } else { // // Log an error so we know something is wrong // PsLogEvent( EVENT_PSTODIB_LOG_INVALID_HANDLE, 0, NULL, PSLOG_ERROR ); DBGOUT(("Validate handle failed...")); return( (PPSEXEDATA) NULL ); } } /*** PsHandleStdInputRequest * * This function handles std input requests from the interpreter by reading * more data from the Win32 spooler. * * * Entry: * pPsToDib = Pointer to the current PSTODIB structure * pPsEvent = A pointer to a structure that defines where to put the * newly acquired data * * Returns: * True = Success * FALSE = Failure the interepreter should stop processing postscript upon * return from this function. */ BOOL PsHandleStdInputRequest( IN PPSDIBPARMS pPsToDib, IN OUT PPSEVENTSTRUCT pPsEvent) { PPSEXEDATA pData; PPSEVENT_STDIN_STRUCT pStdinStruct; DWORD dwAmtToCopy; if (!(pData = ValidateHandle(pPsToDib->hPrivateData))) { return FALSE; } // // No matter what check for block or abort // if ( PsCheckForWaitAndAbort( pData )) { return(FALSE); } // // Cast the data to the correct structure // pStdinStruct = (PPSEVENT_STDIN_STRUCT) pPsEvent->lpVoid; // // Look to see if we have any data left over from our cache... // if so return that data instead of really reading from the spooler // if (pData->cbBinaryBuff != 0) { // // A little bit of math here in case the buffer passed in to // us is not big enough to hold the entire cache buffer // dwAmtToCopy = min( pData->cbBinaryBuff, pStdinStruct->dwBuffSize); // // There is data so lets copy it first... // memcpy( pStdinStruct->lpBuff, pData->lpBinaryPosToReadFrom, dwAmtToCopy ); // // Now upate the pointer and counts; // pData->cbBinaryBuff -= dwAmtToCopy; pData->lpBinaryPosToReadFrom += dwAmtToCopy; pStdinStruct->dwActualBytes = dwAmtToCopy; }else{ // // Read from the printer the amount of data the interpreter // claims he can handle // if( !ReadPrinter( pData->hPrinter, pStdinStruct->lpBuff, pStdinStruct->dwBuffSize, &(pStdinStruct->dwActualBytes ))) { // // something is wrong... so reset the bytes read to 0 // pStdinStruct->dwActualBytes = 0; // // If something unexpected happened log it // if (GetLastError() != ERROR_PRINT_CANCELLED) { // // Something happened... log it and abort // PsLogEventAndIncludeLastError(EVENT_PSTODIB_GET_DATA_FAILED,TRUE); } } } // If the number of bytes returned is 0 then were done... // if (pStdinStruct->dwActualBytes == 0) { // we read nothing from the file... declare an EOF pStdinStruct->uiFlags |= PSSTDIN_FLAG_EOF; } return(TRUE); } /*** PsCheckForWaitAndAbort * * This function checks to see if the user * more data from the Win32 spooler. * * * Entry: * pData = A pointer to our current job structure * * Returns: * True = An abort has been requested * False = Ok, normal processing */ BOOL PsCheckForWaitAndAbort( IN PPSEXEDATA pData ) { BOOL bRetVal = FALSE; // 1st verify we are not blocked... if we are, wait for the // semaphore before continuing since someone decided to PAUSE us // WaitForSingleObject(pData->semPaused, INFINITE); // Check the ABORT flag that would have been sent if the user aborted // us from printman // bRetVal = *(pData->pdwFlags) & PS_SHAREDMEM_ABORTED; #ifdef MYPSDEBUG if (bRetVal) { DBGOUT(("\nAbort requested....")); } #endif return(bRetVal); } /*** PsSetPrintingInfo * * This function brings the current devmode structure up to date, based * on number of copies and or current page type to use for the next * page. * * * Entry: * pData = Pointer to the current job data structure * ppsPage = A pointer to a structure that defines the current page * to image, based on data from the intrepreter. * * Returns: * True = A change occured, the devmode has been made up to date, and * somethign changed (signals that ResetDC should be called. * * False = Ok, no changes were observed */ BOOL PsSetPrintingInfo( IN OUT PPSEXEDATA pData, IN PPSEVENT_PAGE_READY_STRUCT ppsPage ) { BOOL bRetVal = FALSE; LPDEVMODE lpDevmode; lpDevmode = pData->printEnv.lpDevmode; if (lpDevmode != (LPDEVMODE) NULL ) { // We have a devmode so go ahead and look for changes if (lpDevmode->dmFields & DM_PAPERSIZE) { if (lpDevmode->dmPaperSize != ppsPage->iWinPageType) { lpDevmode->dmPaperSize = (short)(ppsPage->iWinPageType); bRetVal = TRUE; } } // HACKHACK DJCTEST hack hack , until page stuff gets resolved // once we can simply pass a new page type in this code will go away // if (lpDevmode->dmFields & DM_FORMNAME) { if (wcscmp( forms[ppsPage->iWinPageType-1], lpDevmode->dmFormName)) { wcscpy( lpDevmode->dmFormName, forms[ppsPage->iWinPageType-1]); bRetVal = TRUE; } } // DJC end hack.... // DJC need a decision here because if the driver does not support // multiple copies we need to simulate it ???? if (lpDevmode->dmFields & DM_COPIES) { if (lpDevmode->dmCopies != (short) ppsPage->uiCopies) { lpDevmode->dmCopies = (short)(ppsPage->uiCopies); bRetVal = TRUE; } } } return( bRetVal ); } /*** PsPrintGeneratePage * * This function is called whenever the interpreter gets a showpage * and is responsible for managing the DC of the printer we are currently * printing to. * * Entry: * pPsToDib = Pointer to the current PSTODIB structure * pPsEvent = A pointer to a structure that defines the attributes for * the frame buffer which is ready to be imaged. This function * will verify that we are not paused/aborted, verify a device * context is available to draw into, * double check and * update the current DEVMODE, reset the DC if required and * finally call the code to actualy draw the frame buffer. * * Returns: * True = Success * FALSE = Failure the interepreter should stop processing postscript upon * return from this function. */ BOOL PsPrintGeneratePage( IN PPSDIBPARMS pPsToDib, IN PPSEVENTSTRUCT pPsEvent) { PPSEXEDATA pData; PPSEVENT_PAGE_READY_STRUCT ppsPageReady; if (!(pData = ValidateHandle(pPsToDib->hPrivateData))) { // do something here,,,, we have a major problem... return(FALSE); } // Verify were not aborting.... if ( PsCheckForWaitAndAbort( pData)) { return(FALSE); } ppsPageReady = (PPSEVENT_PAGE_READY_STRUCT) pPsEvent->lpVoid; // Verify that the current set of data were imaging under is correct // If not lets set it up so it is, PAGE_SIZE, COPIES for now if (PsSetPrintingInfo( pData, ppsPageReady) && (pData->hDC != (HDC)NULL )) { DBGOUT(("\nReseting the DC")); if( ResetDC( pData->hDC, pData->printEnv.lpDevmode) == (HDC) NULL ) { PsLogEventAndIncludeLastError(EVENT_PSTODIB_RESETDC_FAILED,FALSE); } } // We may not have a dc set up yet, in this case we need to create it // with the new devmode data we just modified. If we do this then // we dont need to do a reset dc. #ifndef IGNORE_REAL_PRINTING if(!PsVerifyDCExistsAndCreateIfRequired( pData )) { return(FALSE); } #endif // Everything is ready to actuall image the frame buffer to the Target // device context, so do it... // return PsPrintStretchTheBitmap( pData, ppsPageReady ); } /*** PsPrintStretchTheBitmap * * This function actually manages the target surface and either blits or * stretchBlits (based on the resolution of the target printer) the frame * buffer. * * Entry: * pData = Pointer to the current job structure * ppsPageReady = Pointer to the page ready event structure that * the pstodib component prepared for us.. * Returns: * True = Success * FALSE = Failure the interepreter should stop processing postscript upon * return from this function. */ BOOL PsPrintStretchTheBitmap( IN PPSEXEDATA pData, IN PPSEVENT_PAGE_READY_STRUCT ppsPageReady ) { BOOL bOk = TRUE; int iXres, iYres; int iDestWide, iDestHigh; int iPageCount; int iYOffset; int iXSrc; int iYSrc; int iNumPagesToPrint; int iBlit; int iNewY; int iNewX; // Now do some calculations so we decide if we really need to // stretch the bitmap or not. If the true resolution of the target // printer is less than pstodibs (PSTDOBI_*_DPI) then we will shring // the effective area so we actually only grab a portion of the bitmap // However if the Target DPI is greater that PSTODIBS there is nothing // we can do other than actually stretch (grow) the bitmap. // #ifndef BLIT_TO_DESKTOP iXres = GetDeviceCaps(pData->hDC, LOGPIXELSX); iYres = GetDeviceCaps(pData->hDC, LOGPIXELSY); #else iXres = 300; iYres = 300; #endif // Get the DPI of the target dc anc calculate how much of the frame buffer // we must want in order to show the page correctly // iDestWide = (ppsPageReady->dwWide * iXres) / PSTODIB_X_DPI; iDestHigh = (ppsPageReady->dwHigh * iYres) / PSTODIB_Y_DPI; // If the resolution of the target printer is larger that the resolution // of the interpreter, then were forced to actually stretch the data, so // we can fill the page // if (iDestHigh > (int) ppsPageReady->dwHigh ) { iYSrc = ppsPageReady->dwHigh; iYOffset = 0; } else { iYSrc = iDestHigh; iYOffset = ppsPageReady->dwHigh - iDestHigh; } if (iDestWide > (int) ppsPageReady->dwWide) { iXSrc = ppsPageReady->dwWide; } else { iXSrc = iDestWide; } // Set up the number of pages to print, if the printer driver does not // support multiple pages on its own we need to simulate it... // if ((pData->printEnv.lpDevmode == (LPDEVMODE) NULL ) || !(pData->printEnv.lpDevmode->dmFields & DM_COPIES )) { iNumPagesToPrint = ppsPageReady->uiCopies; DBGOUT(("\nSimulating copies settting to %d", iNumPagesToPrint)); } else { DBGOUT(("\nUsing devmode copies of %d", pData->printEnv.lpDevmode->dmCopies)); iNumPagesToPrint = 1; // The driver will do it for us } // // Set the starting point of the image so we respect the fact that // postscript jobs have 0,0 at the bottom of the page and grow upwards // based on this info we need to compare the imageable area of the // device context and determine how much we need to offset the top,left // corner of our image such that the bottom of our image lines up with the // bottom of the REAL imageable area of the device. This is our best hope // of having the image appear in the correct place on the page. // iNewX = (GetDeviceCaps( pData->hDC, HORZRES) - iDestWide) / 2; iNewY = (GetDeviceCaps( pData->hDC, VERTRES) - iDestHigh) / 2; // If the printer driver does not support multiple copies then we need // to handle appropriately // for ( iPageCount = 0 ; iPageCount < iNumPagesToPrint ; iPageCount++ ) { #ifndef IGNORE_REAL_PRINTING if (StartPage( pData->hDC) <= 0 ) { PsLogEventAndIncludeLastError(EVENT_PSTODIB_FAIL_IMAGE,TRUE); bOk = FALSE; break; } #endif #ifdef BLIT_TO_DESKTOP { HDC hDC; //TEST DJC, sanity hDC = GetDC(GetDesktopWindow()); SetStretchBltMode( hDC, BLACKONWHITE); StretchDIBits ( hDC, 0, 0, iDestWide, iDestHigh, 0, iYOffset, iXSrc, iYSrc, (LPVOID) ppsPageReady->lpBuf, ppsPageReady->lpBitmapInfo, DIB_RGB_COLORS, SRCCOPY ); ReleaseDC(GetDesktopWindow(), hDC); } #endif #ifdef MYPSDEBUG printf("\nDevice True size wxh %d,%d", GetDeviceCaps(pData->hDC,HORZRES), GetDeviceCaps(pData->hDC,VERTRES)); printf("\nDevice Res %d x %d stretching from %d x %d, to %d x %d\nTo location %d %d", iXres, iYres, iXSrc, iYSrc, iDestWide, iDestHigh, iNewX, iNewY); #endif #ifndef IGNORE_REAL_PRINTING #ifdef MYPSDEBUG { TCHAR szBuff[512]; wsprintf( szBuff, TEXT("PSTODIB True device res %d x %d, Job:%ws"), GetDeviceCaps(pData->hDC,HORZRES), GetDeviceCaps(pData->hDC,VERTRES), pData->pDocument ); TextOut( pData->hDC, 0 , 0, szBuff,lstrlen(szBuff)); } #endif // Set the stretch mode in case we really stretch // SetStretchBltMode( pData->hDC, BLACKONWHITE); // // Do a check to keep the stretch from occuring unless it HAS to // if ((iDestWide == iXSrc) && (iDestHigh == iYSrc) ) { iBlit = SetDIBitsToDevice( pData->hDC, iNewX, iNewY, iDestWide, iDestHigh, 0, iYOffset, 0, ppsPageReady->dwHigh, (LPVOID) ppsPageReady->lpBuf, ppsPageReady->lpBitmapInfo, DIB_RGB_COLORS ); } else { iBlit = StretchDIBits( pData->hDC, iNewX, iNewY, iDestWide, iDestHigh, 0, iYOffset, iXSrc, iYSrc, (LPVOID) ppsPageReady->lpBuf, ppsPageReady->lpBitmapInfo, DIB_RGB_COLORS, SRCCOPY ); } if( iBlit == GDI_ERROR ){ PsLogEventAndIncludeLastError(EVENT_PSTODIB_FAIL_IMAGE,TRUE); bOk = FALSE; break; } #endif #ifndef IGNORE_REAL_PRINTING if ( EndPage( pData->hDC ) < 0 ) { PsLogEventAndIncludeLastError(EVENT_PSTODIB_FAIL_IMAGE,TRUE); bOk = FALSE; break; } #endif } return(bOk); } VOID PsLogEventAndIncludeLastError( IN DWORD dwErrorEvent, IN BOOL bError ) { TCHAR atBuff[20]; TCHAR *aStrs[2]; wsprintf( atBuff,TEXT("%d"), GetLastError()); aStrs[0] = atBuff; PsLogEvent( dwErrorEvent, 1, aStrs, PSLOG_ERROR ); } /*** PsVerifyDCExistsAndCreateIfRequired * * * This function checks to see if a DC already exists, and if it does not * then it creates one with the current devmode. * * Entry: * pData = Pointer to the current job structure * * Returns: * True = Success * FALSE = Failure the interepreter should stop processing postscript upon * return from this function. */ BOOL PsVerifyDCExistsAndCreateIfRequired( IN OUT PPSEXEDATA pData ) { BOOL bRetVal = TRUE; DOCINFO docInfo; // // We will only create a dc if it has not already done so // if (pData->hDC == (HDC) NULL ) { pData->hDC = CreateDC(TEXT(""), (LPCTSTR) pData->pPrinterName, TEXT(""), pData->printEnv.lpDevmode ); if (pData->hDC == (HDC) NULL) { PsLogEventAndIncludeLastError( EVENT_PSTODIB_CANNOT_CREATE_DC,TRUE ); return(FALSE); } // Now set the abort proc, this proc will be called occasioanly by the // system to see if we want to abort..... // SetAbortProc( pData->hDC, (ABORTPROC)PsPrintAbortProc ); docInfo.cbSize = sizeof(DOCINFO); docInfo.lpszDocName = pData->pDocument; docInfo.lpszOutput = NULL; if ( StartDoc( pData->hDC, &docInfo) == SP_ERROR ) { PsLogEventAndIncludeLastError( EVENT_PSTODIB_CANNOT_DO_STARTDOC,TRUE ); return(FALSE); } // // Set a flag saying we did the startdoc, this is so we can // return an error to the spooler if we did not, and force // deletion of the job // pData->printEnv.dwFlags |= PS_PRINT_STARTDOC_INITIATED; } return( TRUE ); } /*** PsGetDefaultDevmode * * * This function retrieves the current default DEVMODE for the printer * we are asked to image a job on. * * Entry: * pData = Pointer to the current job structure * * Returns: * True = Success * FALSE = Failure the interepreter should stop processing postscript upon * return from this function. */ BOOL PsGetDefaultDevmode( IN OUT PPSEXEDATA pData ) { DWORD dwMemRequired; PRINTER_INFO_2 *pPrinterInfo; if( !GetPrinter( pData->hPrinter, 2, (LPBYTE) NULL, 0, &dwMemRequired ) && GetLastError() != ERROR_INSUFFICIENT_BUFFER ) { PsLogEventAndIncludeLastError( EVENT_PSTODIB_GETDEFDEVMODE_FAIL,TRUE ); return(FALSE); } pPrinterInfo = (PRINTER_INFO_2 *) LocalAlloc( LPTR, dwMemRequired ); if (pPrinterInfo == (PRINTER_INFO_2 *) NULL) { PsLogEvent( EVENT_PSTODIB_MEM_ALLOC_FAILURE, 0, NULL, 0 ); return(FALSE); } if ( !GetPrinter( pData->hPrinter, 2, (LPBYTE) pPrinterInfo, dwMemRequired, &dwMemRequired ) ) { LocalFree( (HLOCAL) pPrinterInfo ); PsLogEventAndIncludeLastError( EVENT_PSTODIB_GETDEFDEVMODE_FAIL,TRUE ); return(FALSE); } dwMemRequired = DocumentProperties( (HWND) NULL, pData->hPrinter, pPrinterInfo->pPrinterName, NULL, NULL, 0 ); pData->printEnv.lpDevmode = (LPDEVMODE) LocalAlloc( LPTR, dwMemRequired ); if (pData->printEnv.lpDevmode == (LPDEVMODE) NULL) { LocalFree( (HLOCAL) pPrinterInfo ); PsLogEvent( EVENT_PSTODIB_MEM_ALLOC_FAILURE, 0, NULL, 0 ); return(FALSE); } else { DocumentProperties( (HWND) NULL, pData->hPrinter, pPrinterInfo->pPrinterName, pData->printEnv.lpDevmode, NULL, DM_COPY ); pData->printEnv.dwFlags |= PS_PRINT_FREE_DEVMODE; } LocalFree( (HLOCAL) pPrinterInfo ); return(TRUE); } /*** PsGetCurrentPageType * * * This function retrieves the current pagetype based on what was * initially in the devmode. * * Entry: * pData = Pointer to the current job structure * pPsEvent = Pointer to the structure which contains the default * page type to use... * * Returns: * True = Success * False = The interpreter should stop processing the current job * */ BOOL PsGetCurrentPageType( IN PPSDIBPARMS pPsToDib, IN OUT PPSEVENTSTRUCT pPsEvent) { PPSEXEDATA pData; PPSEVENT_CURRENT_PAGE_STRUCT ppsCurPage; LPDEVMODE lpDevmode; if (!(pData = ValidateHandle(pPsToDib->hPrivateData))) { return(FALSE); } ppsCurPage = (PPSEVENT_CURRENT_PAGE_STRUCT) pPsEvent->lpVoid; lpDevmode = pData->printEnv.lpDevmode; if (lpDevmode!= (LPDEVMODE) NULL ) { // // We have a devmode so look at it // if ( lpDevmode->dmFields & DM_PAPERSIZE) { ppsCurPage->dmPaperSize = lpDevmode->dmPaperSize; return(TRUE); } } // True is returned in either case, since not having a devmode is // not necessaraly a fatal error // return(TRUE); } /*** PsMakeDefaultDevmodeModsAndSetupResolution * * * This function sets up our initial devmode and sets up some info * so we can determine the scaling ratio based on the comparison * of true device DPI, and internal interpreter DPI * * Entry: * pData = Pointer to the current job structure * pPsEvent = Pointer to the structure which contains the * intrepreter session info. * * Returns: * VOID * */ VOID PsMakeDefaultDevmodeModsAndSetupResolution( IN PPSEXEDATA pData, IN OUT PPSDIBPARMS ppsDibParms ) { HDC hIC; LPDEVMODE lpDevmode; BOOL bVerifyNewRes = FALSE; lpDevmode = pData->printEnv.lpDevmode; ppsDibParms->uiXDestRes = PSTODIB_X_DPI; ppsDibParms->uiYDestRes = PSTODIB_Y_DPI; // 1st thing to here is create an information context so we can // determine the default resolution of the printer hIC = CreateIC(TEXT(""), (LPCTSTR) pData->pPrinterName, TEXT(""), lpDevmode ); if ( hIC != (HDC) NULL ) { ppsDibParms->uiXDestRes = GetDeviceCaps(hIC, LOGPIXELSX); ppsDibParms->uiYDestRes = GetDeviceCaps(hIC, LOGPIXELSY); if (( GetDeviceCaps(hIC, LOGPIXELSX) > PSTODIB_X_DPI ) && ( GetDeviceCaps(hIC, LOGPIXELSY) > PSTODIB_Y_DPI ) && ( lpDevmode != (LPDEVMODE) NULL )) { // Both resolutions are bigger lets go ahead and try to get the driver // to go to PSTODIB_*_DPIS // lpDevmode->dmFields |= (DM_PRINTQUALITY | DM_YRESOLUTION); lpDevmode->dmPrintQuality = PSTODIB_X_DPI; lpDevmode->dmYResolution = PSTODIB_Y_DPI; // Since we changed the resolution, it may not work so we need to // reget the IC with the new devmode to see if it works. bVerifyNewRes = TRUE; } DeleteDC( hIC ); if (bVerifyNewRes) { hIC = CreateIC(TEXT(""), (LPCTSTR) pData->pPrinterName, TEXT(""), lpDevmode); if (hIC != (HDC) NULL) { // Set the resolution for the job to the new value... // we dont expect this to change for the duration ofthe // job ppsDibParms->uiXDestRes = GetDeviceCaps(hIC, LOGPIXELSX); ppsDibParms->uiYDestRes = GetDeviceCaps(hIC, LOGPIXELSY); DeleteDC(hIC); } } } if (lpDevmode != (LPDEVMODE)NULL) { lpDevmode->dmFields |= (DM_ORIENTATION | DM_PAPERSIZE); lpDevmode->dmOrientation = DMORIENT_PORTRAIT; lpDevmode->dmCopies = 1; } } /*** PsInitPrintEnv * * * Initializes the data that tracks the DEVMODE for the current job * * Entry: * pData = Pointer to the current job structure * lpDevmode = Pointer to the current devmode to use for the job * * Returns: * VOID * */ VOID PsInitPrintEnv( PPSEXEDATA pData, LPDEVMODE lpDevmode ) { DWORD dwTotDevMode; // // Set the inital state of our flags // pData->printEnv.dwFlags = 0; pData->printEnv.lpDevmode = (LPDEVMODE) NULL; if (lpDevmode != (LPDEVMODE) NULL) { // // Since there is a devmode make a local copy cause we might // be changing it. // dwTotDevMode = lpDevmode->dmSize + lpDevmode->dmDriverExtra; pData->printEnv.lpDevmode = (LPDEVMODE) LocalAlloc( NONZEROLPTR, dwTotDevMode ); if (pData->printEnv.lpDevmode != (LPDEVMODE) NULL) { // // Set the flag so we know to free this later // pData->printEnv.dwFlags |= PS_PRINT_FREE_DEVMODE; // // Now go and copy it // memcpy( (PVOID) pData->printEnv.lpDevmode, (PVOID) lpDevmode, dwTotDevMode ); } } } /*** PsHandleBinaryFileLogicAndReturnBinaryStatus * * * This routine will look at the begining buffer of data from the ps job * and determine whether the job is BINARY. This is done by looking at the * beg of the job and looking for a string the mac Spooler inserts. If * this string exists it is converted to spaces and not passed through to the * interpreter. At this point it is a BINARY job. * * Entry: * pData = Pointer to the current job structure * * Returns: * TRUE/FALSE = True means this job should be treated as BINARY. * * */ BOOL PsHandleBinaryFileLogicAndReturnBinaryStatus( PPSEXEDATA pData ) { DWORD dwIndex; BOOL bRetVal = FALSE; pData->lpBinaryPosToReadFrom = &pData->BinaryBuff[0]; pData->cbBinaryBuff = 0; if( !ReadPrinter( pData->hPrinter, pData->lpBinaryPosToReadFrom, sizeof(pData->BinaryBuff), &(pData->cbBinaryBuff) )) { if (GetLastError() != ERROR_PRINT_CANCELLED) { // // Something happened... log it // PsLogEventAndIncludeLastError(EVENT_PSTODIB_GET_DATA_FAILED,TRUE); #ifdef MYPSDEBUG printf("\nSFMPsexe: Error from ReadPrinter when trying to get Binary buffer data "); #endif } } else { // Now do the compare if (IsJobFromMac(pData)) { bRetVal = TRUE; } // // we retain this code just in case the job from an older SFM spooler // that still prepends those strings // else if (!strncmp(pData->BinaryBuff, FILTERCONTROL, SIZE_FC) || !strncmp(pData->BinaryBuff, FILTERCONTROL_OLD, SIZE_FCOLD)) { // // turn filtering off & clear filter message // for (dwIndex = 0; dwIndex < SIZE_FC; dwIndex++) { pData->BinaryBuff[dwIndex] = '\n' ; } bRetVal = TRUE; } } return(bRetVal); } /*** PsPrintAbortProc * * * The abort procedure the system occasiaonly calls to see if we should abort * the current job * * Entry: * hdc = The current device context were drawing into * iError = A spooler error, we dont need to worry about this * * Returns: * TRUE The job should continue to be processed * FALSE The job should be aborted * * */ BOOL CALLBACK PsPrintAbortProc( HDC hdc, int iError ) { // If the print processor set the shared memory abort the job flag, // then kill the job // if( *(Data.pdwFlags) & PS_SHAREDMEM_ABORTED ) { return( FALSE ); } return(TRUE); } /*** main * * * This is the main entry point for the application and only interface to * pstodib * * * Entry: * argc = Count of arguments * argv = ptr to array of ptrs to each argument on the command * line. * * Returns: * 0 = OK, job finished via normal processing * 99 = error of some sort */ int __cdecl main( IN int argc, IN TCHAR *argv[] ) { PPSEXEDATA pData=&Data; PSDIBPARMS psDibParms; BOOL bRetVal = FALSE; LPTSTR lpCommandLine; // Get to the first item, which is the name of the shared memory that // has all the info we need. // lpCommandLine = GetCommandLine(); while (*lpCommandLine && *lpCommandLine != ' ') { lpCommandLine++; } while (*lpCommandLine && *lpCommandLine == ' ') { lpCommandLine++; } // First clear out our structure memset( (PVOID) pData, 0, sizeof(*pData)); // Set up our local structure // pData->signature = PSEXE_SIGNATURE; // First thing to do is get the name of the object we will use for // getting at the memory if (lstrlen(lpCommandLine) == 0) { // This is an error condition PsCleanUpAndExitProcess(pData, TRUE); } pData->hShared = OpenFileMapping( FILE_MAP_READ, FALSE, lpCommandLine); if (pData->hShared == (HANDLE) NULL ) { PsLogEventAndIncludeLastError(EVENT_PSTODIB_INIT_FAILED,TRUE); PsCleanUpAndExitProcess( pData, TRUE ); } else{ pData->pShared = (PPSPRINT_SHARED_MEMORY) MapViewOfFile( pData->hShared, FILE_MAP_READ, 0, 0, 0 ); if (pData->pShared == (PPSPRINT_SHARED_MEMORY) NULL) { PsLogEventAndIncludeLastError(EVENT_PSTODIB_INIT_FAILED,TRUE); PsCleanUpAndExitProcess( pData, TRUE ); } // Now set up the data from the shared memory region pData->pDocument = (LPTSTR) UTLPSRETURNPTRFROMITEM(pData->pShared, pData->pShared->dwDocumentName); pData->pPrinterName = (LPTSTR) UTLPSRETURNPTRFROMITEM(pData->pShared, pData->pShared->dwPrinterName); PsInitPrintEnv( pData, (LPDEVMODE) UTLPSRETURNPTRFROMITEM( pData->pShared, pData->pShared->dwDevmode)); pData->pDocumentPrintDocName = (LPTSTR) UTLPSRETURNPTRFROMITEM( pData->pShared, pData->pShared->dwPrintDocumentDocName); pData->semPaused = OpenEvent( EVENT_ALL_ACCESS, FALSE, (LPWSTR) UTLPSRETURNPTRFROMITEM( pData->pShared, pData->pShared->dwControlName)); if (pData->semPaused == (HANDLE) NULL) { PsLogEventAndIncludeLastError(EVENT_PSTODIB_INIT_FAILED,TRUE); PsCleanUpAndExitProcess( pData, TRUE ); } pData->pdwFlags = (LPDWORD) &pData->pShared->dwFlags; pData->JobId = pData->pShared->dwJobId; // // Now check our Abort immediately flag. If its set GET OUT! // This flag means that the print processor was not able to // correclty set the AccessToken of the primage thread of this // process to imporsonate the user which submitted the print job. // because of this we immediately exit. // if (*(pData->pdwFlags) & PS_SHAREDMEM_SECURITY_ABORT ) { #ifdef MYPSDEBUG printf("\nSFMPSEXE: Aborting due to security violation request from sfmpsprt"); #endif PsCleanUpAndExitProcess(pData,TRUE); } if (!OpenPrinter(pData->pDocumentPrintDocName, &pData->hPrinter, (LPPRINTER_DEFAULTS) NULL)) { PsLogEventAndIncludeLastError(EVENT_PSTODIB_INIT_FAILED,TRUE); PsCleanUpAndExitProcess(pData,TRUE); } // if there was no devmode get the default one... if ( pData->printEnv.lpDevmode == (LPDEVMODE) NULL) { PsGetDefaultDevmode( pData ); } PsMakeDefaultDevmodeModsAndSetupResolution( pData, &psDibParms ); // Now build up the structure for Starting PStoDIB psDibParms.uiOpFlags = 0; //Clear out to begin with.. psDibParms.fpEventProc = PsPrintCallBack; psDibParms.hPrivateData = (HANDLE) pData; // // Now before we kick off the interpreter lets read in the beg of the job // and decide if the data is to be interpreted binary. // if(PsHandleBinaryFileLogicAndReturnBinaryStatus( pData )) { // // Its a binary job so set the flag telling the interpreter such // psDibParms.uiOpFlags |= PSTODIBFLAGS_INTERPRET_BINARY; #ifdef MYPSDEBUG printf("\nSFMPSEXE:Binary requested"); #endif } bRetVal = !PStoDIB(&psDibParms); } // // This function will clean up and call ExitProcess() // thus we will NEVER get past this code // PsCleanUpAndExitProcess( pData, bRetVal); // keep the compiler happy... // return(0); } /*** PsCleanUpAndExitProcess * * This function cleans up any resources allocated, then calls ExitProcess * to terminate. * * * Entry: * pData = Pointer to current job structure * bAbort = if true we are aborting. * * Returns: * Never returns ANYTHING process actually ends HERE! */ VOID PsCleanUpAndExitProcess( IN PPSEXEDATA pData, IN BOOL bAbort ) { // First clean up the DC, if we had one... // if (pData->hDC != (HDC) NULL) { if (bAbort) { AbortDoc( pData->hDC ); } else { EndDoc( pData->hDC ); } DeleteDC( pData->hDC); } // // Now reset the error flag to error if we never did the startdoc // this will force the spooler to remove the job. // if ( !(pData->printEnv.dwFlags & PS_PRINT_STARTDOC_INITIATED ) ){ bAbort = TRUE; } // Clean up the devmode if we allocated it if (pData->printEnv.dwFlags & PS_PRINT_FREE_DEVMODE) { LocalFree( (HLOCAL) pData->printEnv.lpDevmode); } // Clean up the printer handle // if (pData->hPrinter != (HANDLE) NULL) { ClosePrinter( pData->hPrinter); } // Close the semaphore event if (pData->semPaused != (HANDLE) NULL) { CloseHandle( pData->semPaused); } if (pData->pShared != (LPVOID) NULL) { UnmapViewOfFile( (LPVOID) pData->pShared); } if (pData->hShared != (HANDLE) NULL) { CloseHandle( pData->hShared); } ExitProcess(bAbort ? PSEXE_ERROR_EXIT:PSEXE_OK_EXIT); } BOOL IsJobFromMac( IN PPSEXEDATA pData ) { PJOB_INFO_2 pji2GetJob=NULL; DWORD dwNeeded; DWORD dwRetCode; BOOL fJobCameFromMac; fJobCameFromMac = FALSE; // // get pParameters field of the jobinfo to see if this job came from a Mac // dwNeeded = 2000; while (1) { pji2GetJob = LocalAlloc( LMEM_FIXED, dwNeeded ); if (pji2GetJob == NULL) { dwRetCode = GetLastError(); break; } dwRetCode = 0; if (!GetJob( pData->hPrinter,pData->JobId, 2, (LPBYTE)pji2GetJob, dwNeeded, &dwNeeded )) { dwRetCode = GetLastError(); } if ( dwRetCode == ERROR_INSUFFICIENT_BUFFER ) { LocalFree(pji2GetJob); } else { break; } } if (dwRetCode == 0) { // // if there is pParameter field present, and if it matches with our string, // then the job came from a Mac // if (pji2GetJob->pParameters) { if ( (wcslen(pji2GetJob->pParameters) == LSIZE_FC) && (_wcsicmp(pji2GetJob->pParameters, LFILTERCONTROL) == 0) ) { fJobCameFromMac = TRUE; } } } if (pji2GetJob) { LocalFree(pji2GetJob); } return(fJobCameFromMac); }