/*++ Copyright (c) 1990-2003 Microsoft Corporation All Rights Reserved // @@BEGIN_DDKSPLIT Module Name: windows\spooler\prtprocs\winprint\winprint.c // @@END_DDKSPLIT Abstract: Win32 print processor support functions. --*/ #include "local.h" #include // @@BEGIN_DDKSPLIT /** Used for enumerating, checking supported data types !! Warning !! Must match PRINTPROCESSOR_TYPE_* defined in winprint.h If the EMF version is rev-ed, corresponding changes need to be made in spoolss\client\winspool.c (GetPrinterDataW) localspl\port.c (PortThread) localspl\schedule.c (CheckMemoryAvailable) ntgdi\client\output.c (StartDocW) !! HACK !! NT EMF 1.003 isn't really supported. Localspl is hardcoded to reject this call, but we keep it so that HP LJ 1100 monolithic driver can still install. (During install, they set the DRIVER_INFO_3 datatype to 1.003, and this fails if it isn't supported by somebody.) In localspl's LocalStartDocPrinter call, we actually reject this datatype. **/ // @@END_DDKSPLIT LPWSTR Datatypes[]={ L"RAW", // @@BEGIN_DDKSPLIT L"RAW [FF appended]", L"RAW [FF auto]", L"NT EMF 1.003", // @@END_DDKSPLIT L"NT EMF 1.006", L"NT EMF 1.007", L"NT EMF 1.008", L"TEXT", 0}; /** Misc. constants **/ #define BASE_TAB_SIZE 8 /** * For localization: **/ PWCHAR pTabsKey = L"TABS"; PWCHAR pCopiesKey = L"COPIES"; /** Prototypes **/ /** Functions found in parsparm.c **/ extern USHORT GetKeyValue( IN PWCHAR, IN PWCHAR, IN USHORT, IN OUT PUSHORT, OUT PVOID); /** Functions found in raw.c **/ extern BOOL PrintRawJob( IN PPRINTPROCESSORDATA, IN LPWSTR, IN UINT); /** Functions found in text.c **/ extern BOOL PrintTextJob( IN PPRINTPROCESSORDATA, IN LPWSTR); /** Functions found in emf.c */ extern BOOL PrintEMFJob( IN PPRINTPROCESSORDATA, IN LPWSTR); /** Functions found in support.c **/ extern PUCHAR GetPrinterInfo( IN HANDLE hPrinter, IN ULONG, OUT PULONG); BOOL BReleasePPData( IN PPRINTPROCESSORDATA * ppData ); //@@BEGIN_DDKSPLIT /* DllMain only to be compiled for DDK. //@@END_DDKSPLIT BOOL DllMain( HANDLE hModule, DWORD dwReason, LPVOID lpRes ) { return TRUE; } //@@BEGIN_DDKSPLIT */ //@@END_DDKSPLIT /*++ ******************************************************************* E n u m P r i n t P r o c e s s o r D a t a t y p e s W Routine Description: Enumerates the data types supported by the print processor. Arguments: pName => server name pPrintProcessorName => print processor name Level => level of data to return (must be 1) pDatatypes => structure array to fill in cbBuf => length of structure array in bytes pcbNeeded => buffer length copied/required pcReturned => number of structures returned Return Value: TRUE if successful FALSE if failed - caller must use GetLastError for reason ******************************************************************* --*/ BOOL EnumPrintProcessorDatatypes( LPWSTR pName, LPWSTR pPrintProcessorName, DWORD Level, LPBYTE pDatatypes, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned ) { DATATYPES_INFO_1 *pInfo1 = (DATATYPES_INFO_1 *)pDatatypes; LPWSTR *pMyDatatypes = Datatypes; DWORD cbTotal=0; ULONG cchBuf =0; LPBYTE pEnd; if ( NULL == pcbNeeded || NULL == pcReturned ) { return FALSE; SetLastError (ERROR_INVALID_PARAMETER); } /** Start assuming failure, no entries returned **/ *pcReturned = 0; /** Add up the minimum buffer required **/ while (*pMyDatatypes) { cbTotal += wcslen(*pMyDatatypes) * sizeof(WCHAR) + sizeof(WCHAR) + sizeof(DATATYPES_INFO_1); pMyDatatypes++; } /** Set the buffer length returned/required **/ *pcbNeeded = cbTotal; /** Fill in the array only if there is sufficient space **/ if (cbTotal <= cbBuf) { if ( NULL == pInfo1 ) //pInfo1 is same as pDatatypes { SetLastError (ERROR_INVALID_PARAMETER); return FALSE; } /** Pick up pointer to end of the given buffer **/ pEnd = (LPBYTE)pInfo1 + cbBuf; /** Pick up our list of supported data types **/ pMyDatatypes = Datatypes; /** Fill in the given buffer. We put the data names at the end of the buffer, working towards the front. The structures are put at the front, working towards the end. **/ while (*pMyDatatypes) { cchBuf = wcslen(*pMyDatatypes) + 1; //+1 is for \0. pEnd -= cchBuf*sizeof(WCHAR); StringCchCopy ( (LPWSTR)pEnd, cchBuf, *pMyDatatypes); pInfo1->pName = (LPWSTR)pEnd; pInfo1++; (*pcReturned)++; pMyDatatypes++; } } else { /** Caller didn't have large enough buffer, set error and return **/ SetLastError(ERROR_INSUFFICIENT_BUFFER); return FALSE; } /** Return success **/ return TRUE; } /*++ ******************************************************************* O p e n P r i n t P r o c e s s o r Routine Description: Arguments: pPrinterName => name of printer we are opening for pPrintProcessorOpenData => information used for opening the print processor Return Value: PPRINTPROCESSORDATA => processor data of opened processor if successful NULL if failed - caller uses GetLastError for reason NOTE: OpenPrinter will be called iff this returns a valid handle (and we're not journal) @@BEGIN_DDKSPLIT ClosePrintProcessor MUST be called if we succeed here, (or else things don't get cleaned up--like pIniJob->cRef for RAW jobs, which causes the queue to stick!) @@END_DDKSPLIT ******************************************************************* --*/ HANDLE OpenPrintProcessor( LPWSTR pPrinterName, PPRINTPROCESSOROPENDATA pPrintProcessorOpenData ) { PPRINTPROCESSORDATA pData; LPWSTR *pMyDatatypes=Datatypes; DWORD uDatatype=0; HANDLE hPrinter=0; HDC hDC = 0; PDEVMODEW pDevmode = NULL; /** If the caller passed a NULL for the open data, fail the call. pPrintProcessorOpenData->pDevMode can be NULL **/ if (!pPrintProcessorOpenData || !pPrintProcessorOpenData->pDatatype || !*pPrintProcessorOpenData->pDatatype) { SetLastError(ERROR_INVALID_PARAMETER); return NULL; } /** Search for the data type index we are opening for **/ while (*pMyDatatypes) { if (!_wcsicmp(*pMyDatatypes,pPrintProcessorOpenData->pDatatype)) { break; } pMyDatatypes++; uDatatype++; } /** Allocate a buffer for the print processor data to return **/ pData = (PPRINTPROCESSORDATA)AllocSplMem(sizeof(PRINTPROCESSORDATA)); if (!pData) { ODS(("Alloc failed in OpenPrintProcessor, while printing on %ws\n", pPrinterName)); return NULL; } ZeroMemory ( pData, sizeof (PRINTPROCESSORDATA) ); /** Open the processor accordingly **/ switch (uDatatype) { case PRINTPROCESSOR_TYPE_RAW: // @@BEGIN_DDKSPLIT case PRINTPROCESSOR_TYPE_RAW_FF: case PRINTPROCESSOR_TYPE_RAW_FF_AUTO: // @@END_DDKSPLIT if (!OpenPrinter(pPrinterName, &hPrinter, NULL)) goto Fail; break; case PRINTPROCESSOR_TYPE_EMF_50_1: case PRINTPROCESSOR_TYPE_EMF_50_2: case PRINTPROCESSOR_TYPE_EMF_50_3: if(pPrintProcessorOpenData->pDevMode) { if(!(pDevmode=AllocSplMem(pPrintProcessorOpenData->pDevMode->dmSize+ pPrintProcessorOpenData->pDevMode->dmDriverExtra))) { goto Fail; } memcpy(pDevmode, pPrintProcessorOpenData->pDevMode, pPrintProcessorOpenData->pDevMode->dmSize+ pPrintProcessorOpenData->pDevMode->dmDriverExtra); } break; case PRINTPROCESSOR_TYPE_TEXT: if (!(hDC = CreateDC(L"", pPrinterName, L"", pPrintProcessorOpenData->pDevMode))) goto Fail; break; default: SetLastError(ERROR_INVALID_DATATYPE); goto Fail; } /** Fill in the print processors information **/ pData->cb = sizeof(PRINTPROCESSORDATA); pData->signature = PRINTPROCESSORDATA_SIGNATURE; pData->JobId = pPrintProcessorOpenData->JobId; pData->hPrinter = hPrinter; pData->semPaused = CreateEvent(NULL, TRUE, TRUE,NULL); pData->uDatatype = uDatatype; pData->hDC = hDC; pData->Copies = 1; pData->TabSize = BASE_TAB_SIZE; /** Allocate and fill in the processors strings **/ pData->pPrinterName = AllocSplStr(pPrinterName); pData->pDatatype = AllocSplStr(pPrintProcessorOpenData->pDatatype); pData->pDocument = AllocSplStr(pPrintProcessorOpenData->pDocumentName); pData->pOutputFile = AllocSplStr(pPrintProcessorOpenData->pOutputFile); pData->pParameters = AllocSplStr(pPrintProcessorOpenData->pParameters); pData->pDevmode = pDevmode; pData->pPrinterNameFromOpenData = AllocSplStr(pPrintProcessorOpenData->pPrinterName); // // Check for validity of pData. In the AllocSplStr above, if RHS is non-null, then LHS // should be non-null. // if ( NULL == pData->semPaused || ( NULL != pPrinterName && NULL == pData->pPrinterName ) || ( NULL != pPrintProcessorOpenData->pDatatype && NULL == pData->pDatatype ) || ( NULL != pPrintProcessorOpenData->pDocumentName && NULL == pData->pDocument ) || ( NULL != pPrintProcessorOpenData->pOutputFile && NULL == pData->pOutputFile ) || ( NULL != pPrintProcessorOpenData->pParameters && NULL == pData->pParameters ) || ( NULL != pPrintProcessorOpenData->pPrinterName && NULL == pData->pPrinterNameFromOpenData) ) { goto Fail; } // @@BEGIN_DDKSPLIT /** WORKWORK : Currently, the pParameters field has the name of the printer driver. This will be fixed, and should come up here the same as the user submitted in the job's Printer Info structure. **/ // @@END_DDKSPLIT /** Parse the parameters string **/ if (pData->pParameters) { ULONG value; USHORT length = sizeof(ULONG); /** Look to see if there is a COPIES=n key/value in the Parameters field of this job. This tells us the number of times to play the data. **/ if (pData->pParameters) { GetKeyValue(pData->pParameters, pCopiesKey, VALUE_ULONG, &length, &value); if (length == sizeof(ULONG)) { pData->Copies = value; } } /** If this is a text job, see if the tab size is in there **/ if (uDatatype == PRINTPROCESSOR_TYPE_TEXT) { length = sizeof(ULONG); GetKeyValue(pData->pParameters, pTabsKey, VALUE_ULONG, &length, &value); if ((length == sizeof(ULONG)) && value) { pData->TabSize = value; } } } /* If we have a parameter string */ /** If we are doing copies, we need to check to see if this is a direct or spooled job. If it is direct, then we can't do copies because we can't rewind the data stream. **/ if (pData->Copies > 1) { ULONG Error; PPRINTER_INFO_2 pPrinterInfo2; /** If we don't already have the printer open, open it **/ if (uDatatype != PRINTPROCESSOR_TYPE_RAW // @@BEGIN_DDKSPLIT && uDatatype != PRINTPROCESSOR_TYPE_RAW_FF && uDatatype != PRINTPROCESSOR_TYPE_RAW_FF_AUTO // @@END_DDKSPLIT ) { OpenPrinter(pPrinterName, &hPrinter, NULL); } if (hPrinter && hPrinter != INVALID_HANDLE_VALUE) { /** Get the printer info - this returns an allocated buffer **/ pPrinterInfo2 = (PPRINTER_INFO_2)GetPrinterInfo(hPrinter, 2, &Error); /** If we couldn't get the info, be safe and don't do copies **/ if (!pPrinterInfo2) { ODS(("GetPrinter failed - falling back to 1 copy\n")); pData->Copies = 1; } else { if (pPrinterInfo2->Attributes & PRINTER_ATTRIBUTE_DIRECT) { pData->Copies = 1; } FreeSplMem((PUCHAR)pPrinterInfo2); } /** If we just opened the printer, close it **/ if (uDatatype != PRINTPROCESSOR_TYPE_RAW // @@BEGIN_DDKSPLIT && uDatatype != PRINTPROCESSOR_TYPE_RAW_FF && uDatatype != PRINTPROCESSOR_TYPE_RAW_FF_AUTO // @@END_DDKSPLIT ) { ClosePrinter(hPrinter); } } else { pData->Copies = 1; } } return (HANDLE)pData; Fail: BReleasePPData(&pData); return FALSE; } /*++ ******************************************************************* P r i n t D o c u m e n t O n P r i n t P r o c e s s o r Routine Description: Arguments: hPrintProcessor pDocumentName Return Value: TRUE if successful FALSE if failed - GetLastError() will return reason ******************************************************************* --*/ BOOL PrintDocumentOnPrintProcessor( HANDLE hPrintProcessor, LPWSTR pDocumentName ) { PPRINTPROCESSORDATA pData; /** Make sure the handle is valid and pick up the Print Processors data area. **/ if (!(pData = ValidateHandle(hPrintProcessor))) { return FALSE; } /** Print the job based on its data type. **/ switch (pData->uDatatype) { case PRINTPROCESSOR_TYPE_EMF_50_1: case PRINTPROCESSOR_TYPE_EMF_50_2: case PRINTPROCESSOR_TYPE_EMF_50_3: return PrintEMFJob( pData, pDocumentName ); break; case PRINTPROCESSOR_TYPE_RAW: // @@BEGIN_DDKSPLIT case PRINTPROCESSOR_TYPE_RAW_FF: case PRINTPROCESSOR_TYPE_RAW_FF_AUTO: // @@END_DDKSPLIT return PrintRawJob(pData, pDocumentName, pData->uDatatype); break; case PRINTPROCESSOR_TYPE_TEXT: return PrintTextJob(pData, pDocumentName); break; } /* Case on data type */ /** Return success **/ return TRUE; } /*++ ******************************************************************* C l o s e P r i n t P r o c e s s o r Routine Description: Frees the resources used by an open print processor. Arguments: hPrintProcessor (HANDLE) => print processor to close Return Value: TRUE if successful FALSE if failed - caller uses GetLastError for reason. ******************************************************************* --*/ BOOL ClosePrintProcessor( HANDLE hPrintProcessor ) { PPRINTPROCESSORDATA pData; /** Make sure the handle is valid and pick up the Print Processors data area. **/ if (!(pData= ValidateHandle(hPrintProcessor))) { return FALSE; } return BReleasePPData(&pData); } BOOL BReleasePPData( IN PPRINTPROCESSORDATA * ppData ) { PPRINTPROCESSORDATA pData = NULL; if ( NULL == ppData || NULL == *ppData) { return FALSE; } pData = *ppData; pData->signature = 0; /* Release any allocated resources */ if (pData->hPrinter) ClosePrinter(pData->hPrinter); if (pData->hDC) DeleteDC(pData->hDC); if (pData->pDevmode) FreeSplMem(pData->pDevmode); if (pData->pPrinterNameFromOpenData) FreeSplStr(pData->pPrinterNameFromOpenData); if (pData->semPaused) CloseHandle(pData->semPaused); if (pData->pPrinterName) FreeSplStr(pData->pPrinterName); if (pData->pDatatype) FreeSplStr(pData->pDatatype); if (pData->pDocument) FreeSplStr(pData->pDocument); if (pData->pOutputFile) FreeSplStr(pData->pOutputFile); if (pData->pParameters) FreeSplStr(pData->pParameters); ZeroMemory ( pData, sizeof (PRINTPROCESSORDATA) ); FreeSplMem(pData); *ppData = pData = NULL; return TRUE; } /*++ ******************************************************************* C o n t r o l P r i n t P r o c e s s o r Routine Description: Handles commands to pause, resume, and cancel print jobs. Arguments: hPrintProcessor = HANDLE to the PrintProcessor the command is issued for. Return Value: TRUE if command succeeded FALSE if command failed (invalid command) ******************************************************************* --*/ BOOL ControlPrintProcessor( HANDLE hPrintProcessor, DWORD Command ) { PPRINTPROCESSORDATA pData; /** Make sure the handle is valid and pick up the Print Processors data area. **/ if (pData = ValidateHandle(hPrintProcessor)) { switch (Command) { case JOB_CONTROL_PAUSE: ResetEvent(pData->semPaused); pData->fsStatus |= PRINTPROCESSOR_PAUSED; return TRUE; break; case JOB_CONTROL_CANCEL: pData->fsStatus |= PRINTPROCESSOR_ABORTED; if ((pData->uDatatype == PRINTPROCESSOR_TYPE_EMF_50_1) || (pData->uDatatype == PRINTPROCESSOR_TYPE_EMF_50_2) || (pData->uDatatype == PRINTPROCESSOR_TYPE_EMF_50_3)) CancelDC(pData->hDC); /* Fall through to release job if paused */ case JOB_CONTROL_RESUME: if (pData->fsStatus & PRINTPROCESSOR_PAUSED) { SetEvent(pData->semPaused); pData->fsStatus &= ~PRINTPROCESSOR_PAUSED; } return TRUE; break; default: return FALSE; break; } } return FALSE; } /*++ ******************************************************************* V a l i d a t e H a n d l e Routine Description: Validates the given Print Processor HANDLE (which is really a pointer to the Print Processor's data) by checking for our signature. Arguments: hQProc (HANDLE) => Print Processor data structure. This is verified as really being a pointer to the Print Processor's data. Return Value: PPRINTPROCESSORDATA if successful (valid pointer passed) NULL if failed - pointer was not valid ******************************************************************* --*/ PPRINTPROCESSORDATA ValidateHandle( HANDLE hQProc ) { /** Pick up the pointer **/ PPRINTPROCESSORDATA pData = (PPRINTPROCESSORDATA)hQProc; // // Note that spooler has to leave the critical section to call into print // proc. So the handle passed by spooler could be invalid since one // thread could call SetJob to pause/resume a job while port thread // is printing it // try { /** See if our signature exists in the suspected data region **/ if (pData && pData->signature != PRINTPROCESSORDATA_SIGNATURE) { /** Bad pointer - return failed **/ pData = NULL; } }except (1) { /** Bad pointer - return failed **/ pData = NULL; } if ( pData == NULL ) SetLastError( ERROR_INVALID_HANDLE ); return pData; } DWORD GetPrintProcessorCapabilities( LPTSTR pValueName, DWORD dwAttributes, LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded ) /*++ Function Description: GetPrintProcessorCapabilities returns information about the options supported by the print processor for the given datatype in a PRINTPROCESSOR_CAPS_1 struct. Parameters: pValueName -- datatype like RAW|NT EMF 1.006|TEXT|... dwAttributes -- printer attributes pData -- pointer to the buffer nSize -- size of the buffer pcbNeeded -- pointer to the variable to store the required buffer size Return Values: Error Codes. --*/ { LPWSTR *pDatatypes = Datatypes; DWORD dwDatatype = 0; DWORD dwReturn; PPRINTPROCESSOR_CAPS_1 ppcInfo; // Check for valid parameters. if ( !pcbNeeded || !pData || !pValueName) { dwReturn = ERROR_INVALID_PARAMETER; goto CleanUp; } *pcbNeeded = sizeof(PRINTPROCESSOR_CAPS_1); // Check for sufficient buffer. if (nSize < *pcbNeeded) { dwReturn = ERROR_MORE_DATA; goto CleanUp; } // Loop to find the index of the datatype. while (*pDatatypes) { if (!_wcsicmp(*pDatatypes,pValueName)) { break; } pDatatypes++; dwDatatype++; } ppcInfo = (PPRINTPROCESSOR_CAPS_1) pData; // Level is 1 for PRINTPROCESSOR_CAPS_1. ppcInfo->dwLevel = 1; switch (dwDatatype) { case PRINTPROCESSOR_TYPE_RAW: // @@BEGIN_DDKSPLIT case PRINTPROCESSOR_TYPE_RAW_FF: case PRINTPROCESSOR_TYPE_RAW_FF_AUTO: // @@END_DDKSPLIT case PRINTPROCESSOR_TYPE_TEXT: ppcInfo->dwNupOptions = 1; ppcInfo->dwNumberOfCopies = 0xffffffff; // maximum number of copies. ppcInfo->dwPageOrderFlags = NORMAL_PRINT; break; case PRINTPROCESSOR_TYPE_EMF_50_1: case PRINTPROCESSOR_TYPE_EMF_50_2: case PRINTPROCESSOR_TYPE_EMF_50_3: // For direct printing, masq. printers and print RAW only, // EMF is not spooled. Dont expose EMF features in the UI. if ((dwAttributes & PRINTER_ATTRIBUTE_DIRECT) || (dwAttributes & PRINTER_ATTRIBUTE_RAW_ONLY) || ((dwAttributes & PRINTER_ATTRIBUTE_LOCAL) && (dwAttributes & PRINTER_ATTRIBUTE_NETWORK))) { ppcInfo->dwNupOptions = 1; ppcInfo->dwNumberOfCopies = 1; ppcInfo->dwPageOrderFlags = NORMAL_PRINT; } else { ppcInfo->dwNupOptions = 0x0000812b; // for 1,2,4,6,9,16 up options. ppcInfo->dwNumberOfCopies = 0xffffffff; // maximum number of copies. ppcInfo->dwPageOrderFlags = REVERSE_PRINT | BOOKLET_PRINT; } break; default: // Should not happen since the spooler must check if the datatype is // supported before calling this print processor. dwReturn = ERROR_INVALID_DATATYPE; goto CleanUp; } dwReturn = ERROR_SUCCESS; CleanUp: return dwReturn; }