You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3150 lines
85 KiB
3150 lines
85 KiB
/*++
|
|
|
|
Copyright (c) 1996-1997 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
ppd.c
|
|
|
|
Abstract:
|
|
|
|
Public interface to the PPD parser
|
|
|
|
Environment:
|
|
|
|
Windows NT PostScript driver
|
|
|
|
Revision History:
|
|
|
|
10/14/96 -davidx-
|
|
Add new interface function MapToDeviceOptIndex.
|
|
|
|
09/30/96 -davidx-
|
|
Cleaner handling of ManualFeed and AutoSelect feature.
|
|
|
|
09/24/96 -davidx-
|
|
Implement ResolveUIConflicts.
|
|
|
|
09/23/96 -davidx-
|
|
Implement ChangeOptionsViaID.
|
|
|
|
08/30/96 -davidx-
|
|
Changes after the 1st code review.
|
|
|
|
08/19/96 -davidx-
|
|
Implemented most of the interface functions except:
|
|
ChangeOptionsViaID
|
|
ResolveUIConflicts
|
|
|
|
08/16/96 -davidx-
|
|
Created it.
|
|
|
|
--*/
|
|
|
|
#include "lib.h"
|
|
#include "ppd.h"
|
|
|
|
#ifndef KERNEL_MODE
|
|
|
|
#ifndef WINNT_40
|
|
|
|
#include "appcompat.h"
|
|
|
|
#else // WINNT_40
|
|
|
|
#endif // !WINNT_40
|
|
|
|
#endif // !KERNEL_MODE
|
|
|
|
//
|
|
// Forward declaration of local static functions
|
|
//
|
|
|
|
BOOL BCheckFeatureConflict(PUIINFO, POPTSELECT, DWORD, PDWORD, DWORD, DWORD);
|
|
BOOL BCheckFeatureOptionConflict(PUIINFO, DWORD, DWORD, DWORD, DWORD);
|
|
BOOL BSearchConstraintList(PUIINFO, DWORD, DWORD, DWORD);
|
|
DWORD DwReplaceFeatureOption(PUIINFO, POPTSELECT, DWORD, DWORD, DWORD);
|
|
DWORD DwInternalMapToOptIndex(PUIINFO, PFEATURE, LONG, LONG, PDWORD);
|
|
|
|
|
|
//
|
|
// DeleteRawBinaryData only called from driverui
|
|
//
|
|
#ifndef KERNEL_MODE
|
|
|
|
void
|
|
DeleteRawBinaryData(
|
|
IN PTSTR ptstrPpdFilename
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Delete raw binary printer description data.
|
|
|
|
Arguments:
|
|
|
|
ptstrDataFilename - Specifies the name of the original printer description file
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
PTSTR ptstrBpdFilename;
|
|
|
|
// only for test purposes. Upgrades are hard to debug...
|
|
ERR(("Deleting .bpd file\n"));
|
|
|
|
//
|
|
// Sanity check
|
|
//
|
|
|
|
if (ptstrPpdFilename == NULL)
|
|
{
|
|
RIP(("PPD filename is NULL.\n"));
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Generate BPD filename from the specified PPD filename
|
|
//
|
|
|
|
if (! (ptstrBpdFilename = GenerateBpdFilename(ptstrPpdFilename)))
|
|
return;
|
|
|
|
if (!DeleteFile(ptstrBpdFilename))
|
|
ERR(("DeleteRawBinaryData failed: %d\n", GetLastError()));
|
|
|
|
MemFree(ptstrBpdFilename);
|
|
}
|
|
#endif
|
|
|
|
|
|
PRAWBINARYDATA
|
|
LoadRawBinaryData(
|
|
IN PTSTR ptstrDataFilename
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Load raw binary printer description data.
|
|
|
|
Arguments:
|
|
|
|
ptstrDataFilename - Specifies the name of the original printer description file
|
|
|
|
Return Value:
|
|
|
|
Pointer to raw binary printer description data
|
|
NULL if there is an error
|
|
|
|
--*/
|
|
|
|
{
|
|
PRAWBINARYDATA pRawData;
|
|
|
|
//
|
|
// Sanity check
|
|
//
|
|
|
|
if (ptstrDataFilename == NULL)
|
|
{
|
|
RIP(("PPD filename is NULL.\n"));
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Attempt to load cached binary printer description data first
|
|
//
|
|
|
|
if ((pRawData = PpdLoadCachedBinaryData(ptstrDataFilename)) == NULL)
|
|
{
|
|
#if !defined(KERNEL_MODE) || defined(USERMODE_DRIVER)
|
|
|
|
//
|
|
// If there is no cached binary data or it's out-of-date, we'll parse
|
|
// the ASCII text file and cache the resulting binary data.
|
|
//
|
|
|
|
pRawData = PpdParseTextFile(ptstrDataFilename);
|
|
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// Initialize various pointer fields inside the printer description data
|
|
//
|
|
|
|
if (pRawData)
|
|
{
|
|
PINFOHEADER pInfoHdr;
|
|
PUIINFO pUIInfo;
|
|
PPPDDATA pPpdData;
|
|
|
|
pInfoHdr = (PINFOHEADER) pRawData;
|
|
pUIInfo = GET_UIINFO_FROM_INFOHEADER(pInfoHdr);
|
|
pPpdData = GET_DRIVER_INFO_FROM_INFOHEADER(pInfoHdr);
|
|
|
|
ASSERT(pUIInfo != NULL && pUIInfo->dwSize == sizeof(UIINFO));
|
|
ASSERT(pPpdData != NULL && pPpdData->dwSizeOfStruct == sizeof(PPDDATA));
|
|
|
|
pRawData->pvReserved = NULL;
|
|
pRawData->pvPrivateData = pRawData;
|
|
|
|
pUIInfo->pubResourceData = (PBYTE) pInfoHdr;
|
|
pUIInfo->pInfoHeader = pInfoHdr;
|
|
|
|
#ifndef KERNEL_MODE
|
|
|
|
#ifndef WINNT_40 // Win2K user mode driver
|
|
|
|
if (GetAppCompatFlags2(VER40) & GACF2_NOCUSTOMPAPERSIZES)
|
|
{
|
|
pUIInfo->dwFlags &= ~FLAG_CUSTOMSIZE_SUPPORT;
|
|
}
|
|
|
|
#else // WINNT_40
|
|
|
|
/* NT4 solution here */
|
|
|
|
#endif // !WINNT_40
|
|
|
|
#endif // !KERNEL_MODE
|
|
}
|
|
|
|
if (pRawData == NULL)
|
|
ERR(("LoadRawBinaryData failed: %d\n", GetLastError()));
|
|
|
|
return pRawData;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
UnloadRawBinaryData(
|
|
IN PRAWBINARYDATA pRawData
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Unload raw binary printer description data previously loaded using LoadRawBinaryData
|
|
|
|
Arguments:
|
|
|
|
pRawData - Points to raw binary printer description data
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
--*/
|
|
|
|
{
|
|
ASSERT(pRawData != NULL);
|
|
MemFree(pRawData);
|
|
}
|
|
|
|
|
|
|
|
PINFOHEADER
|
|
InitBinaryData(
|
|
IN PRAWBINARYDATA pRawData,
|
|
IN PINFOHEADER pInfoHdr,
|
|
IN POPTSELECT pOptions
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize and return an instance of binary printer description data
|
|
|
|
Arguments:
|
|
|
|
pRawData - Points to raw binary printer description data
|
|
pInfoHdr - Points to an existing of binary data instance
|
|
pOptions - Specifies the options used to initialize the binary data instance
|
|
|
|
Return Value:
|
|
|
|
Pointer to an initialized binary data instance
|
|
|
|
Note:
|
|
|
|
If pInfoHdr parameter is NULL, the parser returns a new binary data instance
|
|
which should be freed by calling FreeBinaryData. If pInfoHdr parameter is not
|
|
NULL, the existing binary data instance is reinitialized.
|
|
|
|
If pOption parameter is NULL, the parser should use the default option values
|
|
for generating the binary data instance. The parser may have special case
|
|
optimization to handle this case.
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// For PPD parser, all instances of the binary printer description
|
|
// are the same as the original raw binary data.
|
|
//
|
|
|
|
ASSERT(pRawData != NULL && pRawData == pRawData->pvPrivateData);
|
|
ASSERT(pInfoHdr == NULL || pInfoHdr == (PINFOHEADER) pRawData);
|
|
|
|
return (PINFOHEADER) pRawData;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
FreeBinaryData(
|
|
IN PINFOHEADER pInfoHdr
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Free an instance of the binary printer description data
|
|
|
|
Arguments:
|
|
|
|
pInfoHdr - Points to a binary data instance previously returned from an
|
|
InitBinaryData(pRawData, NULL, pOptions) call
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// For PPD parser, there is nothing to be done here.
|
|
//
|
|
|
|
ASSERT(pInfoHdr != NULL);
|
|
}
|
|
|
|
|
|
|
|
PINFOHEADER
|
|
UpdateBinaryData(
|
|
IN PRAWBINARYDATA pRawData,
|
|
IN PINFOHEADER pInfoHdr,
|
|
IN POPTSELECT pOptions
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Update an instance of binary printer description data
|
|
|
|
Arguments:
|
|
|
|
pRawData - Points to raw binary printer description data
|
|
pInfoHdr - Points to an existing of binary data instance
|
|
pOptions - Specifies the options used to update the binary data instance
|
|
|
|
Return Value:
|
|
|
|
Pointer to the updated binary data instance
|
|
NULL if there is an error
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// For PPD parser, there is nothing to be done here.
|
|
//
|
|
|
|
ASSERT(pRawData != NULL && pRawData == pRawData->pvPrivateData);
|
|
ASSERT(pInfoHdr == NULL || pInfoHdr == (PINFOHEADER) pRawData);
|
|
|
|
return pInfoHdr;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
InitDefaultOptions(
|
|
IN PRAWBINARYDATA pRawData,
|
|
OUT POPTSELECT pOptions,
|
|
IN INT iMaxOptions,
|
|
IN INT iMode
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize the option array with default settings from the printer description file
|
|
|
|
Arguments:
|
|
|
|
pRawData - Points to raw binary printer description data
|
|
pOptions - Points to an array of OPTSELECT structures for storing the default settings
|
|
iMaxOptions - Max number of entries in pOptions array
|
|
iMode - Specifies what the caller is interested in:
|
|
MODE_DOCUMENT_STICKY
|
|
MODE_PRINTER_STICKY
|
|
MODE_DOCANDPRINTER_STICKY
|
|
|
|
Return Value:
|
|
|
|
FALSE if the input option array is not large enough to hold
|
|
all default option values, TRUE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
INT iStart, iOptions, iIndex;
|
|
PINFOHEADER pInfoHdr;
|
|
PUIINFO pUIInfo;
|
|
PFEATURE pFeature;
|
|
POPTSELECT pTempOptions;
|
|
BOOL bResult = TRUE;
|
|
|
|
ASSERT(pOptions != NULL);
|
|
|
|
//
|
|
// Get pointers to various data structures
|
|
//
|
|
|
|
PPD_GET_UIINFO_FROM_RAWDATA(pRawData, pInfoHdr, pUIInfo);
|
|
|
|
ASSERT(pUIInfo != NULL);
|
|
|
|
iOptions = pRawData->dwDocumentFeatures + pRawData->dwPrinterFeatures;
|
|
ASSERT(iOptions <= MAX_PRINTER_OPTIONS);
|
|
|
|
if ((pTempOptions = MemAllocZ(MAX_PRINTER_OPTIONS*sizeof(OPTSELECT))) == NULL)
|
|
{
|
|
ERR(("Memory allocation failed\n"));
|
|
|
|
ZeroMemory(pOptions, iMaxOptions*sizeof(OPTSELECT));
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Construct the default option array
|
|
//
|
|
|
|
ASSERT(NULL_OPTSELECT == 0);
|
|
|
|
for (iIndex = 0; iIndex < iOptions; iIndex++)
|
|
{
|
|
pFeature = PGetIndexedFeature(pUIInfo, iIndex);
|
|
|
|
ASSERT(pFeature != NULL);
|
|
|
|
pTempOptions[iIndex].ubCurOptIndex = (BYTE)
|
|
((pFeature->dwFlags & FEATURE_FLAG_NOUI) ?
|
|
OPTION_INDEX_ANY :
|
|
pFeature->dwDefaultOptIndex);
|
|
}
|
|
|
|
//
|
|
// Resolve any conflicts between default option selections
|
|
//
|
|
|
|
ResolveUIConflicts(pRawData, pTempOptions, MAX_PRINTER_OPTIONS, iMode);
|
|
|
|
//
|
|
// Determine if the caller is interested in doc- and/or printer-sticky options
|
|
//
|
|
|
|
switch (iMode)
|
|
{
|
|
case MODE_DOCUMENT_STICKY:
|
|
|
|
iStart = 0;
|
|
iOptions = pRawData->dwDocumentFeatures;
|
|
break;
|
|
|
|
case MODE_PRINTER_STICKY:
|
|
|
|
iStart = pRawData->dwDocumentFeatures;
|
|
iOptions = pRawData->dwPrinterFeatures;
|
|
break;
|
|
|
|
default:
|
|
|
|
ASSERT(iMode == MODE_DOCANDPRINTER_STICKY);
|
|
iStart = 0;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Make sure the input option array is large enough
|
|
//
|
|
|
|
if (iOptions > iMaxOptions)
|
|
{
|
|
RIP(("Option array too small: %d < %d\n", iMaxOptions, iOptions));
|
|
iOptions = iMaxOptions;
|
|
bResult = FALSE;
|
|
}
|
|
|
|
//
|
|
// Copy the default option array
|
|
//
|
|
|
|
CopyMemory(pOptions, pTempOptions+iStart, iOptions*sizeof(OPTSELECT));
|
|
|
|
MemFree(pTempOptions);
|
|
return bResult;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
ValidateDocOptions(
|
|
IN PRAWBINARYDATA pRawData,
|
|
IN OUT POPTSELECT pOptions,
|
|
IN INT iMaxOptions
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Validate the devmode option array and correct any invalid option selections
|
|
|
|
Arguments:
|
|
|
|
pRawData - Points to raw binary printer description data
|
|
pOptions - Points to an array of OPTSELECT structures that need validation
|
|
iMaxOptions - Max number of entries in pOptions array
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
INT cFeatures, iIndex;
|
|
PINFOHEADER pInfoHdr;
|
|
PUIINFO pUIInfo;
|
|
|
|
ASSERT(pOptions != NULL);
|
|
|
|
//
|
|
// Get pointers to various data structures
|
|
//
|
|
|
|
PPD_GET_UIINFO_FROM_RAWDATA(pRawData, pInfoHdr, pUIInfo);
|
|
ASSERT(pUIInfo != NULL);
|
|
|
|
cFeatures = pRawData->dwDocumentFeatures;
|
|
ASSERT(cFeatures <= MAX_PRINTER_OPTIONS);
|
|
|
|
if (cFeatures > iMaxOptions)
|
|
{
|
|
RIP(("Option array too small: %d < %d\n", iMaxOptions, cFeatures));
|
|
cFeatures = iMaxOptions;
|
|
}
|
|
|
|
//
|
|
// loop through doc-sticky features to validate each option selection(s)
|
|
//
|
|
|
|
for (iIndex = 0; iIndex < cFeatures; iIndex++)
|
|
{
|
|
PFEATURE pFeature;
|
|
INT cAllOptions, cSelectedOptions, iNext;
|
|
BOOL bValid;
|
|
|
|
if ((pOptions[iIndex].ubCurOptIndex == OPTION_INDEX_ANY) &&
|
|
(pOptions[iIndex].ubNext == NULL_OPTSELECT))
|
|
{
|
|
//
|
|
// We use OPTION_INDEX_ANY intentionally, so don't change it.
|
|
//
|
|
|
|
continue;
|
|
}
|
|
|
|
pFeature = PGetIndexedFeature(pUIInfo, iIndex);
|
|
ASSERT(pFeature != NULL);
|
|
|
|
//
|
|
// number of available options
|
|
//
|
|
|
|
cAllOptions = pFeature->Options.dwCount;
|
|
|
|
//
|
|
// number of selected options
|
|
//
|
|
|
|
cSelectedOptions = 0;
|
|
|
|
iNext = iIndex;
|
|
|
|
bValid = TRUE;
|
|
|
|
do
|
|
{
|
|
cSelectedOptions++;
|
|
|
|
if ((iNext >= iMaxOptions) ||
|
|
(pOptions[iNext].ubCurOptIndex >= cAllOptions) ||
|
|
(cSelectedOptions > cAllOptions))
|
|
{
|
|
//
|
|
// either the option index is out of range,
|
|
// or the current option selection is invalid,
|
|
// or the number of selected options (for PICKMANY)
|
|
// exceeds available options
|
|
//
|
|
|
|
bValid = FALSE;
|
|
break;
|
|
}
|
|
|
|
iNext = pOptions[iNext].ubNext;
|
|
|
|
} while (iNext != NULL_OPTSELECT);
|
|
|
|
if (!bValid)
|
|
{
|
|
ERR(("Corrected invalid option array value for feature %d\n", iIndex));
|
|
|
|
pOptions[iIndex].ubCurOptIndex = (BYTE)
|
|
((pFeature->dwFlags & FEATURE_FLAG_NOUI) ?
|
|
OPTION_INDEX_ANY :
|
|
pFeature->dwDefaultOptIndex);
|
|
|
|
pOptions[iIndex].ubNext = NULL_OPTSELECT;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
CheckFeatureOptionConflict(
|
|
IN PRAWBINARYDATA pRawData,
|
|
IN DWORD dwFeature1,
|
|
IN DWORD dwOption1,
|
|
IN DWORD dwFeature2,
|
|
IN DWORD dwOption2
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Check if (dwFeature1, dwOption1) constrains (dwFeature2, dwOption2)
|
|
|
|
Arguments:
|
|
|
|
pRawData - Points to raw binary printer description data
|
|
dwFeature1, dwOption1 - Feature and option indices of the first feature/option pair
|
|
dwFeature2, dwOption2 - Feature and option indices of the second feature/option pair
|
|
|
|
Return Value:
|
|
|
|
TRUE if (dwFeature1, dwOption1) constrains (dwFeature2, dwOption2)
|
|
FALSE otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
PINFOHEADER pInfoHdr;
|
|
PUIINFO pUIInfo;
|
|
|
|
PPD_GET_UIINFO_FROM_RAWDATA(pRawData, pInfoHdr, pUIInfo);
|
|
|
|
ASSERT(pUIInfo != NULL);
|
|
|
|
return BCheckFeatureOptionConflict(pUIInfo, dwFeature1, dwOption1, dwFeature2, dwOption2);
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
ResolveUIConflicts(
|
|
IN PRAWBINARYDATA pRawData,
|
|
IN OUT POPTSELECT pOptions,
|
|
IN INT iMaxOptions,
|
|
IN INT iMode
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Resolve any conflicts between printer feature option selections
|
|
|
|
Arguments:
|
|
|
|
pRawData - Points to raw binary printer description data
|
|
pOptions - Points to an array of OPTSELECT structures for storing the modified options
|
|
iMaxOptions - Max number of entries in pOptions array
|
|
iMode - Specifies how the conflicts should be resolved:
|
|
MODE_DOCUMENT_STICKY - only resolve conflicts between doc-sticky features
|
|
MODE_PRINTER_STICKY - only resolve conflicts between printer-sticky features
|
|
MODE_DOCANDPRINTER_STICKY - resolve conflicts all features
|
|
|
|
Return Value:
|
|
|
|
TRUE if there is no conflicts between printer feature option selection
|
|
FALSE otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
PINFOHEADER pInfoHdr;
|
|
PUIINFO pUIInfo;
|
|
PFEATURE pFeatures;
|
|
PDWORD pdwFlags;
|
|
POPTSELECT pTempOptions;
|
|
DWORD dwStart, dwOptions, dwIndex, dwTotalFeatureCount;
|
|
BOOL bReturnValue = TRUE;
|
|
BOOL bCheckConflictOnly;
|
|
|
|
struct _PRIORITY_INFO {
|
|
|
|
DWORD dwFeatureIndex;
|
|
DWORD dwPriority;
|
|
|
|
} *pPriorityInfo;
|
|
|
|
ASSERT(pOptions);
|
|
|
|
//
|
|
// Initialize pointers to various data structures
|
|
//
|
|
|
|
PPD_GET_UIINFO_FROM_RAWDATA(pRawData, pInfoHdr, pUIInfo);
|
|
|
|
ASSERT(pUIInfo != NULL);
|
|
|
|
pFeatures = OFFSET_TO_POINTER(pInfoHdr, pUIInfo->loFeatureList);
|
|
dwTotalFeatureCount = pRawData->dwDocumentFeatures + pRawData->dwPrinterFeatures;
|
|
|
|
if (iMaxOptions < (INT) dwTotalFeatureCount)
|
|
{
|
|
ERR(("Option array for ResolveUIConflicts is too small.\n"));
|
|
return bReturnValue;
|
|
}
|
|
|
|
//
|
|
// Determine if the caller is interested in doc- and/or printer-sticky options
|
|
//
|
|
|
|
bCheckConflictOnly = ((iMode & DONT_RESOLVE_CONFLICT) != 0);
|
|
iMode &= ~DONT_RESOLVE_CONFLICT;
|
|
|
|
switch (iMode)
|
|
{
|
|
case MODE_DOCUMENT_STICKY:
|
|
|
|
dwStart = 0;
|
|
dwOptions = pRawData->dwDocumentFeatures;
|
|
break;
|
|
|
|
case MODE_PRINTER_STICKY:
|
|
|
|
dwStart = pRawData->dwDocumentFeatures;
|
|
dwOptions = pRawData->dwPrinterFeatures;
|
|
break;
|
|
|
|
default:
|
|
|
|
ASSERT(iMode == MODE_DOCANDPRINTER_STICKY);
|
|
dwStart = 0;
|
|
dwOptions = dwTotalFeatureCount;
|
|
break;
|
|
}
|
|
|
|
if (dwOptions == 0)
|
|
return TRUE;
|
|
|
|
//
|
|
// This problem is not completely solvable in the worst case.
|
|
// But the approach below should work if the PPD is well-formed.
|
|
//
|
|
// for each feature starting from the highest priority one
|
|
// and down to the lowest priority one do:
|
|
// for each selected option of the feature do:
|
|
// if the option is not constrained, continue
|
|
// else do one of the following:
|
|
// if the conflict feature has lower priority, continue
|
|
// else resolve the current feature/option pair:
|
|
// if UIType is PickMany, deselect the current option
|
|
// else try to change the option to:
|
|
// Default option
|
|
// each option of the feature in sequence
|
|
// OPTION_INDEX_ANY as the last resort
|
|
//
|
|
|
|
pPriorityInfo = MemAlloc(dwOptions * sizeof(struct _PRIORITY_INFO));
|
|
pTempOptions = MemAlloc(iMaxOptions * sizeof(OPTSELECT));
|
|
pdwFlags = MemAllocZ(dwOptions * sizeof(DWORD));
|
|
|
|
if (pPriorityInfo && pTempOptions && pdwFlags)
|
|
{
|
|
//
|
|
// Copy the options array into a temporary working buffer
|
|
//
|
|
|
|
CopyMemory(pTempOptions, pOptions, sizeof(OPTSELECT) * iMaxOptions);
|
|
|
|
//
|
|
// Sort the feature indices according to their priority
|
|
//
|
|
|
|
for (dwIndex = 0; dwIndex < dwOptions; dwIndex++)
|
|
{
|
|
pPriorityInfo[dwIndex].dwFeatureIndex = dwIndex + dwStart;
|
|
pPriorityInfo[dwIndex].dwPriority = pFeatures[dwIndex + dwStart].dwPriority;
|
|
}
|
|
|
|
for (dwIndex = 0; dwIndex < dwOptions; dwIndex++)
|
|
{
|
|
struct _PRIORITY_INFO tempPriorityInfo;
|
|
DWORD dwLoop, dwMax = dwIndex;
|
|
|
|
for (dwLoop = dwIndex + 1; dwLoop < dwOptions; dwLoop++)
|
|
{
|
|
if (pPriorityInfo[dwLoop].dwPriority > pPriorityInfo[dwMax].dwPriority)
|
|
dwMax = dwLoop;
|
|
}
|
|
|
|
if (dwMax != dwIndex)
|
|
{
|
|
tempPriorityInfo = pPriorityInfo[dwMax];
|
|
pPriorityInfo[dwMax] = pPriorityInfo[dwIndex];
|
|
pPriorityInfo[dwIndex] = tempPriorityInfo;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Loop through every feature, starting from the highest
|
|
// priority one down to the lowest priority one.
|
|
//
|
|
|
|
for (dwIndex = 0; dwIndex < dwOptions; )
|
|
{
|
|
DWORD dwCurFeature, dwCurOption, dwCurNext;
|
|
BOOL bConflict = FALSE;
|
|
|
|
//
|
|
// Loop through every selected option of the current feature
|
|
//
|
|
|
|
dwCurNext = dwCurFeature = pPriorityInfo[dwIndex].dwFeatureIndex;
|
|
|
|
do
|
|
{
|
|
DWORD dwFeature, dwOption, dwNext;
|
|
|
|
dwCurOption = pTempOptions[dwCurNext].ubCurOptIndex;
|
|
dwCurNext = pTempOptions[dwCurNext].ubNext;
|
|
|
|
//
|
|
// Check if the current feature/option pair is constrained
|
|
//
|
|
|
|
for (dwFeature = dwStart; dwFeature < dwStart + dwOptions; dwFeature++)
|
|
{
|
|
dwNext = dwFeature;
|
|
|
|
do
|
|
{
|
|
dwOption = pTempOptions[dwNext].ubCurOptIndex;
|
|
dwNext = pTempOptions[dwNext].ubNext;
|
|
|
|
if (BCheckFeatureOptionConflict(pUIInfo,
|
|
dwFeature,
|
|
dwOption,
|
|
dwCurFeature,
|
|
dwCurOption))
|
|
{
|
|
bConflict = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
while (dwNext != NULL_OPTSELECT);
|
|
|
|
//
|
|
// Check if a conflict was detected
|
|
//
|
|
|
|
if (bConflict)
|
|
{
|
|
VERBOSE(("Conflicting option selections: (%d, %d) - (%d, %d)\n",
|
|
dwFeature, dwOption,
|
|
dwCurFeature, dwCurOption));
|
|
|
|
if (pdwFlags[dwFeature - dwStart] & 0x10000)
|
|
{
|
|
//
|
|
// The conflicting feature has higher priority than
|
|
// the current feature. Change the selected option
|
|
// of the current feature.
|
|
//
|
|
|
|
pdwFlags[dwCurFeature - dwStart] =
|
|
DwReplaceFeatureOption(pUIInfo,
|
|
pTempOptions,
|
|
dwCurFeature,
|
|
dwCurOption,
|
|
pdwFlags[dwCurFeature - dwStart]);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// The conflicting feature has lower priority than
|
|
// the current feature. Change the selected option
|
|
// of the conflicting feature.
|
|
//
|
|
|
|
pdwFlags[dwFeature - dwStart] =
|
|
DwReplaceFeatureOption(pUIInfo,
|
|
pTempOptions,
|
|
dwFeature,
|
|
dwOption,
|
|
pdwFlags[dwFeature - dwStart]);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
while ((dwCurNext != NULL_OPTSELECT) && !bConflict);
|
|
|
|
//
|
|
// If no conflict is found for the selected options of
|
|
// the current feature, then move on to the next feature.
|
|
// Otherwise, repeat the loop on the current feature.
|
|
//
|
|
|
|
if (! bConflict)
|
|
{
|
|
//
|
|
// Make the current feature as visited
|
|
//
|
|
|
|
pdwFlags[dwCurFeature - dwStart] |= 0x10000;
|
|
|
|
dwIndex++;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// If a conflict is found, set the return value to false
|
|
//
|
|
|
|
bReturnValue = FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Copy the resolved options array from the temporary working
|
|
// buffer back to the input options array. This results in
|
|
// all option selections being compacted at the beginning
|
|
// of the array.
|
|
//
|
|
|
|
if (! bCheckConflictOnly)
|
|
{
|
|
INT iNext = (INT) dwTotalFeatureCount;
|
|
|
|
for (dwIndex = 0; dwIndex < dwTotalFeatureCount; dwIndex ++)
|
|
{
|
|
VCopyOptionSelections(pOptions,
|
|
dwIndex,
|
|
pTempOptions,
|
|
dwIndex,
|
|
&iNext,
|
|
iMaxOptions);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// If we couldn't allocate temporary working buffer,
|
|
// then return to the caller without doing anything.
|
|
//
|
|
|
|
ERR(("Memory allocation failed.\n"));
|
|
}
|
|
|
|
MemFree(pTempOptions);
|
|
MemFree(pdwFlags);
|
|
MemFree(pPriorityInfo);
|
|
|
|
return bReturnValue;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
EnumEnabledOptions(
|
|
IN PRAWBINARYDATA pRawData,
|
|
IN POPTSELECT pOptions,
|
|
IN DWORD dwFeatureIndex,
|
|
OUT PBOOL pbEnabledOptions,
|
|
IN INT iMode
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determine which options of the specified feature should be enabled
|
|
based on the current option selections of printer features
|
|
|
|
Arguments:
|
|
|
|
pRawData - Points to raw binary printer description data
|
|
pOptions - Points to the current feature option selections
|
|
dwFeatureIndex - Specifies the index of the feature in question
|
|
pbEnabledOptions - An array of BOOLs, each entry corresponds to an option
|
|
of the specified feature. On exit, if the entry is TRUE, the corresponding
|
|
option is enabled. Otherwise, the corresponding option should be disabled.
|
|
iMode - Specifies what the caller is interested in:
|
|
MODE_DOCUMENT_STICKY
|
|
MODE_PRINTER_STICKY
|
|
MODE_DOCANDPRINTER_STICKY
|
|
|
|
Return Value:
|
|
|
|
TRUE if any option for the specified feature is enabled,
|
|
FALSE if all options of the specified feature are disabled
|
|
(i.e. the feature itself is disabled)
|
|
|
|
--*/
|
|
|
|
{
|
|
PINFOHEADER pInfoHdr;
|
|
PUIINFO pUIInfo;
|
|
PFEATURE pFeature;
|
|
DWORD dwIndex, dwCount;
|
|
BOOL bFeatureEnabled = FALSE;
|
|
|
|
ASSERT(pOptions && pbEnabledOptions);
|
|
|
|
//
|
|
// Get pointers to various data structures
|
|
//
|
|
|
|
PPD_GET_UIINFO_FROM_RAWDATA(pRawData, pInfoHdr, pUIInfo);
|
|
|
|
ASSERT(pUIInfo != NULL);
|
|
|
|
if (! (pFeature = PGetIndexedFeature(pUIInfo, dwFeatureIndex)))
|
|
{
|
|
ASSERT(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
dwCount = pFeature->Options.dwCount;
|
|
|
|
//
|
|
// Go through each option of the specified feature and
|
|
// determine whether it should be enabled or disabled.
|
|
//
|
|
|
|
for (dwIndex = 0; dwIndex < dwCount; dwIndex++)
|
|
{
|
|
DWORD dwFeature, dwOption;
|
|
BOOL bEnabled = TRUE;
|
|
|
|
for (dwFeature = 0;
|
|
dwFeature < pRawData->dwDocumentFeatures + pRawData->dwPrinterFeatures;
|
|
dwFeature ++)
|
|
{
|
|
if (BCheckFeatureConflict(pUIInfo,
|
|
pOptions,
|
|
dwFeature,
|
|
&dwOption,
|
|
dwFeatureIndex,
|
|
dwIndex))
|
|
{
|
|
bEnabled = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
pbEnabledOptions[dwIndex] = bEnabled;
|
|
bFeatureEnabled = bFeatureEnabled || bEnabled;
|
|
}
|
|
|
|
return bFeatureEnabled;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
EnumNewUIConflict(
|
|
IN PRAWBINARYDATA pRawData,
|
|
IN POPTSELECT pOptions,
|
|
IN DWORD dwFeatureIndex,
|
|
IN PBOOL pbSelectedOptions,
|
|
OUT PCONFLICTPAIR pConflictPair
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Check if there are any conflicts between the currently selected options
|
|
for the specified feature an other feature/option selections.
|
|
|
|
Arguments:
|
|
|
|
pRawData - Points to raw binary printer description data
|
|
pOptions - Points to the current feature/option selections
|
|
dwFeatureIndex - Specifies the index of the interested printer feature
|
|
pbSelectedOptions - Specifies which options for the specified feature are selected
|
|
pConflictPair - Return the conflicting pair of feature/option selections
|
|
|
|
Return Value:
|
|
|
|
TRUE if there is a conflict between the selected options for the specified feature
|
|
and other feature option selections.
|
|
|
|
FALSE if the selected options for the specified feature is consistent with other
|
|
feature option selections.
|
|
|
|
--*/
|
|
|
|
{
|
|
PINFOHEADER pInfoHdr;
|
|
PUIINFO pUIInfo;
|
|
PFEATURE pSpecifiedFeature;
|
|
DWORD dwIndex, dwCount, dwPriority;
|
|
BOOL bConflict = FALSE;
|
|
|
|
ASSERT(pOptions && pbSelectedOptions && pConflictPair);
|
|
|
|
//
|
|
// Get pointers to various data structures
|
|
//
|
|
|
|
PPD_GET_UIINFO_FROM_RAWDATA(pRawData, pInfoHdr, pUIInfo);
|
|
|
|
ASSERT(pUIInfo != NULL);
|
|
|
|
if (! (pSpecifiedFeature = PGetIndexedFeature(pUIInfo, dwFeatureIndex)))
|
|
{
|
|
ASSERT(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
dwCount = pSpecifiedFeature->Options.dwCount;
|
|
|
|
//
|
|
// Go through the selected options of the specified feature
|
|
// and check if they are constrained.
|
|
//
|
|
|
|
for (dwIndex = 0; dwIndex < dwCount; dwIndex++)
|
|
{
|
|
DWORD dwFeature, dwOption;
|
|
PFEATURE pFeature;
|
|
|
|
//
|
|
// Skip options which are not selected
|
|
//
|
|
|
|
if (! pbSelectedOptions[dwIndex])
|
|
continue;
|
|
|
|
for (dwFeature = 0;
|
|
dwFeature < pRawData->dwDocumentFeatures + pRawData->dwPrinterFeatures;
|
|
dwFeature ++)
|
|
{
|
|
if (dwFeature == dwFeatureIndex)
|
|
continue;
|
|
|
|
if (BCheckFeatureConflict(pUIInfo,
|
|
pOptions,
|
|
dwFeature,
|
|
&dwOption,
|
|
dwFeatureIndex,
|
|
dwIndex))
|
|
{
|
|
pFeature = PGetIndexedFeature(pUIInfo, dwFeature);
|
|
ASSERT(pFeature != NULL);
|
|
|
|
//
|
|
// Remember the highest priority conflict-pair
|
|
//
|
|
|
|
if (!bConflict || pFeature->dwPriority > dwPriority)
|
|
{
|
|
dwPriority = pFeature->dwPriority;
|
|
|
|
if (dwPriority >= pSpecifiedFeature->dwPriority)
|
|
{
|
|
pConflictPair->dwFeatureIndex1 = dwFeature;
|
|
pConflictPair->dwOptionIndex1 = dwOption;
|
|
pConflictPair->dwFeatureIndex2 = dwFeatureIndex;
|
|
pConflictPair->dwOptionIndex2 = dwIndex;
|
|
}
|
|
else
|
|
{
|
|
pConflictPair->dwFeatureIndex1 = dwFeatureIndex;
|
|
pConflictPair->dwOptionIndex1 = dwIndex;
|
|
pConflictPair->dwFeatureIndex2 = dwFeature;
|
|
pConflictPair->dwOptionIndex2 = dwOption;
|
|
}
|
|
}
|
|
|
|
bConflict = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// For PickMany UI types, the current selections for the specified
|
|
// feature could potentially conflict with each other.
|
|
//
|
|
|
|
if (pSpecifiedFeature->dwUIType == UITYPE_PICKMANY)
|
|
{
|
|
for (dwOption = 0; dwOption < dwCount; dwOption++)
|
|
{
|
|
if (BCheckFeatureOptionConflict(pUIInfo,
|
|
dwFeatureIndex,
|
|
dwOption,
|
|
dwFeatureIndex,
|
|
dwIndex))
|
|
{
|
|
if (!bConflict || pSpecifiedFeature->dwPriority > dwPriority)
|
|
{
|
|
dwPriority = pSpecifiedFeature->dwPriority;
|
|
pConflictPair->dwFeatureIndex1 = dwFeatureIndex;
|
|
pConflictPair->dwOptionIndex1 = dwOption;
|
|
pConflictPair->dwFeatureIndex2 = dwFeatureIndex;
|
|
pConflictPair->dwOptionIndex2 = dwIndex;
|
|
}
|
|
|
|
bConflict = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return bConflict;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
EnumNewPickOneUIConflict(
|
|
IN PRAWBINARYDATA pRawData,
|
|
IN POPTSELECT pOptions,
|
|
IN DWORD dwFeatureIndex,
|
|
IN DWORD dwOptionIndex,
|
|
OUT PCONFLICTPAIR pConflictPair
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Check if there are any conflicts between the currently selected option
|
|
for the specified feature an other feature/option selections.
|
|
|
|
This is similar to EnumNewUIConflict above except that only one selected
|
|
option is allowed for the specified feature.
|
|
|
|
Arguments:
|
|
|
|
pRawData - Points to raw binary printer description data
|
|
pOptions - Points to the current feature/option selections
|
|
dwFeatureIndex - Specifies the index of the interested printer feature
|
|
dwOptionIndex - Specifies the selected option of the specified feature
|
|
pConflictPair - Return the conflicting pair of feature/option selections
|
|
|
|
Return Value:
|
|
|
|
TRUE if there is a conflict between the selected option for the specified feature
|
|
and other feature/option selections.
|
|
|
|
FALSE if the selected option for the specified feature is consistent with other
|
|
feature/option selections.
|
|
|
|
--*/
|
|
|
|
{
|
|
PINFOHEADER pInfoHdr;
|
|
PUIINFO pUIInfo;
|
|
PFEATURE pSpecifiedFeature, pFeature;
|
|
DWORD dwPriority, dwFeature, dwOption;
|
|
BOOL bConflict = FALSE;
|
|
|
|
ASSERT(pOptions && pConflictPair);
|
|
|
|
//
|
|
// Get pointers to various data structures
|
|
//
|
|
|
|
PPD_GET_UIINFO_FROM_RAWDATA(pRawData, pInfoHdr, pUIInfo);
|
|
|
|
ASSERT(pUIInfo != NULL);
|
|
|
|
if ((pSpecifiedFeature = PGetIndexedFeature(pUIInfo, dwFeatureIndex)) == NULL ||
|
|
(dwOptionIndex >= pSpecifiedFeature->Options.dwCount))
|
|
{
|
|
ASSERT(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Check if the specified feature/option is constrained
|
|
//
|
|
|
|
for (dwFeature = 0;
|
|
dwFeature < pRawData->dwDocumentFeatures + pRawData->dwPrinterFeatures;
|
|
dwFeature ++)
|
|
{
|
|
if (dwFeature == dwFeatureIndex)
|
|
continue;
|
|
|
|
if (BCheckFeatureConflict(pUIInfo,
|
|
pOptions,
|
|
dwFeature,
|
|
&dwOption,
|
|
dwFeatureIndex,
|
|
dwOptionIndex))
|
|
{
|
|
pFeature = PGetIndexedFeature(pUIInfo, dwFeature);
|
|
|
|
ASSERT(pFeature != NULL);
|
|
|
|
//
|
|
// Remember the highest priority conflict-pair
|
|
//
|
|
|
|
if (!bConflict || pFeature->dwPriority > dwPriority)
|
|
{
|
|
dwPriority = pFeature->dwPriority;
|
|
|
|
if (dwPriority >= pSpecifiedFeature->dwPriority)
|
|
{
|
|
pConflictPair->dwFeatureIndex1 = dwFeature;
|
|
pConflictPair->dwOptionIndex1 = dwOption;
|
|
pConflictPair->dwFeatureIndex2 = dwFeatureIndex;
|
|
pConflictPair->dwOptionIndex2 = dwOptionIndex;
|
|
}
|
|
else
|
|
{
|
|
pConflictPair->dwFeatureIndex1 = dwFeatureIndex;
|
|
pConflictPair->dwOptionIndex1 = dwOptionIndex;
|
|
pConflictPair->dwFeatureIndex2 = dwFeature;
|
|
pConflictPair->dwOptionIndex2 = dwOption;
|
|
}
|
|
}
|
|
|
|
bConflict = TRUE;
|
|
}
|
|
}
|
|
|
|
return bConflict;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
ChangeOptionsViaID(
|
|
IN PINFOHEADER pInfoHdr,
|
|
IN OUT POPTSELECT pOptions,
|
|
IN DWORD dwFeatureID,
|
|
IN PDEVMODE pDevmode
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Modifies an option array using the information in public devmode fields
|
|
|
|
Arguments:
|
|
|
|
pInfoHdr - Points to an instance of binary printer description data
|
|
pOptions - Points to the option array to be modified
|
|
dwFeatureID - Specifies which field(s) of the input devmode should be used
|
|
pDevmode - Specifies the input devmode
|
|
|
|
Return Value:
|
|
TRUE if successful, FALSE if the specified feature ID is not supported
|
|
or there is an error
|
|
|
|
Note:
|
|
|
|
We assume the input devmode fields have been validated by the caller.
|
|
|
|
--*/
|
|
|
|
{
|
|
PRAWBINARYDATA pRawData;
|
|
PUIINFO pUIInfo;
|
|
PFEATURE pFeature;
|
|
DWORD dwFeatureIndex;
|
|
LONG lParam1, lParam2;
|
|
BOOL abEnabledOptions[MAX_PRINTER_OPTIONS];
|
|
PDWORD pdwPaperIndex = (PDWORD)abEnabledOptions;
|
|
DWORD dwCount, dwOptionIndex, i;
|
|
|
|
ASSERT(pOptions && pDevmode);
|
|
|
|
//
|
|
// Get a pointer to the FEATURE structure corresponding to
|
|
// the specified feature ID.
|
|
//
|
|
|
|
pRawData = (PRAWBINARYDATA) pInfoHdr;
|
|
PPD_GET_UIINFO_FROM_RAWDATA(pRawData, pInfoHdr, pUIInfo);
|
|
|
|
ASSERT(pUIInfo != NULL);
|
|
|
|
if ((dwFeatureID >= MAX_GID) ||
|
|
(pFeature = GET_PREDEFINED_FEATURE(pUIInfo, dwFeatureID)) == NULL)
|
|
{
|
|
VERBOSE(("ChangeOptionsViaID failed: feature ID = %d\n", dwFeatureID));
|
|
SetLastError(ERROR_NOT_SUPPORTED);
|
|
return FALSE;
|
|
}
|
|
|
|
dwFeatureIndex = GET_INDEX_FROM_FEATURE(pUIInfo, pFeature);
|
|
|
|
//
|
|
// Handle it according to what dwFeatureID is specified
|
|
//
|
|
|
|
lParam1 = lParam2 = 0;
|
|
|
|
switch (dwFeatureID)
|
|
{
|
|
case GID_PAGESIZE:
|
|
|
|
//
|
|
// Don't select any PageRegion option by default
|
|
//
|
|
|
|
{
|
|
PFEATURE pPageRgnFeature;
|
|
DWORD dwPageRgnIndex;
|
|
|
|
if (pPageRgnFeature = GET_PREDEFINED_FEATURE(pUIInfo, GID_PAGEREGION))
|
|
{
|
|
dwPageRgnIndex = GET_INDEX_FROM_FEATURE(pUIInfo, pPageRgnFeature);
|
|
|
|
pOptions[dwPageRgnIndex].ubCurOptIndex =
|
|
(BYTE) pPageRgnFeature->dwNoneFalseOptIndex;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the devmode specifies PostScript custom page size,
|
|
// we assume the parameters have already been validated
|
|
// during devmode merge proces. So here we simply return
|
|
// the custom page size option index.
|
|
//
|
|
|
|
if ((pDevmode->dmFields & DM_PAPERSIZE) &&
|
|
(pDevmode->dmPaperSize == DMPAPER_CUSTOMSIZE))
|
|
{
|
|
ASSERT(SUPPORT_CUSTOMSIZE(pUIInfo));
|
|
pOptions[dwFeatureIndex].ubCurOptIndex = (BYTE) pUIInfo->dwCustomSizeOptIndex;
|
|
return TRUE;
|
|
}
|
|
|
|
lParam1 = pDevmode->dmPaperWidth * DEVMODE_PAPER_UNIT;
|
|
lParam2 = pDevmode->dmPaperLength * DEVMODE_PAPER_UNIT;
|
|
break;
|
|
|
|
case GID_INPUTSLOT:
|
|
|
|
lParam1 = pDevmode->dmDefaultSource;
|
|
break;
|
|
|
|
case GID_RESOLUTION:
|
|
|
|
//
|
|
// If none is set, this function is not called with par. GID_RESOLUTION
|
|
//
|
|
|
|
ASSERT(pDevmode->dmFields & (DM_PRINTQUALITY | DM_YRESOLUTION));
|
|
|
|
switch (pDevmode->dmFields & (DM_PRINTQUALITY | DM_YRESOLUTION))
|
|
{
|
|
|
|
case DM_PRINTQUALITY: // set both if only one is set
|
|
|
|
lParam1 = lParam2 = pDevmode->dmPrintQuality;
|
|
break;
|
|
|
|
case DM_YRESOLUTION: // set both if only one is set
|
|
|
|
lParam1 = lParam2 = pDevmode->dmYResolution;
|
|
break;
|
|
|
|
default:
|
|
lParam1 = pDevmode->dmPrintQuality;
|
|
lParam2 = pDevmode->dmYResolution;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case GID_DUPLEX:
|
|
|
|
lParam1 = pDevmode->dmDuplex;
|
|
break;
|
|
|
|
case GID_MEDIATYPE:
|
|
|
|
lParam1 = pDevmode->dmMediaType;
|
|
break;
|
|
|
|
case GID_COLLATE:
|
|
|
|
lParam1 = pDevmode->dmCollate;
|
|
break;
|
|
|
|
default:
|
|
|
|
VERBOSE(("ChangeOptionsViaID failed: feature ID = %d\n", dwFeatureID));
|
|
SetLastError(ERROR_NOT_SUPPORTED);
|
|
return FALSE;
|
|
}
|
|
|
|
ASSERT(pFeature->dwUIType != UITYPE_PICKMANY);
|
|
|
|
if (dwFeatureID == GID_PAGESIZE)
|
|
{
|
|
dwCount = DwInternalMapToOptIndex(pUIInfo, pFeature, lParam1, lParam2, pdwPaperIndex);
|
|
|
|
if (dwCount == 0 )
|
|
return TRUE;
|
|
|
|
if (dwCount > 1 )
|
|
{
|
|
POPTION pOption;
|
|
LPCTSTR pDisplayName;
|
|
|
|
for (i = 0; i < dwCount; i++)
|
|
{
|
|
if (pOption = PGetIndexedOption(pUIInfo, pFeature, pdwPaperIndex[i]))
|
|
{
|
|
if ((pDisplayName = OFFSET_TO_POINTER(pRawData, pOption->loDisplayName)) &&
|
|
(_tcsicmp(pDevmode->dmFormName, pDisplayName) == EQUAL_STRING) )
|
|
{
|
|
dwOptionIndex = pdwPaperIndex[i];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (i >= dwCount)
|
|
dwOptionIndex = pdwPaperIndex[0];
|
|
}
|
|
else
|
|
dwOptionIndex = pdwPaperIndex[0];
|
|
|
|
pOptions[dwFeatureIndex].ubCurOptIndex = (BYTE)dwOptionIndex;
|
|
}
|
|
else
|
|
{
|
|
pOptions[dwFeatureIndex].ubCurOptIndex =
|
|
(BYTE) DwInternalMapToOptIndex(pUIInfo, pFeature, lParam1, lParam2, NULL);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
MapToDeviceOptIndex(
|
|
IN PINFOHEADER pInfoHdr,
|
|
IN DWORD dwFeatureID,
|
|
IN LONG lParam1,
|
|
IN LONG lParam2,
|
|
OUT PDWORD pdwOptionIndexes
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Map logical values to device feature option index
|
|
|
|
Arguments:
|
|
|
|
pInfoHdr - Points to an instance of binary printer description data
|
|
dwFeatureID - Indicate which feature the logical values are related to
|
|
lParam1, lParam2 - Parameters depending on dwFeatureID
|
|
pdwOptionIndexes - if Not NULL, means fill this array with all indicies
|
|
which match the search criteria. In this case the return value
|
|
is the number of elements in the array initialized. Currently
|
|
we assume the array is large enough (256 elements).
|
|
(It should be non-NULL only for GID_PAGESIZE.)
|
|
|
|
dwFeatureID = GID_PAGESIZE:
|
|
map logical paper specification to physical page size option
|
|
|
|
lParam1 = paper width in microns
|
|
lParam2 = paper height in microns
|
|
|
|
dwFeatureID = GID_RESOLUTION:
|
|
map logical resolution to physical resolution option
|
|
|
|
lParam1 = x-resolution in dpi
|
|
lParam2 = y-resolution in dpi
|
|
|
|
Return Value:
|
|
|
|
If pdwOptionIndexes is NULL, returns index of the feature option corresponding
|
|
to the specified logical values; OPTION_INDEX_ANY if the specified logical
|
|
values cannot be mapped to any feature option.
|
|
|
|
If pdwOptionIndexes is not NULL (for GID_PAGESIZE), returns the number of elements
|
|
filled in the output buffer. Zero means the specified logical values cannot be mapped
|
|
to any feature option.
|
|
|
|
--*/
|
|
|
|
{
|
|
PRAWBINARYDATA pRawData;
|
|
PUIINFO pUIInfo;
|
|
PFEATURE pFeature;
|
|
|
|
//
|
|
// Get a pointer to the FEATURE structure corresponding to
|
|
// the specified feature ID.
|
|
//
|
|
|
|
pRawData = (PRAWBINARYDATA) pInfoHdr;
|
|
PPD_GET_UIINFO_FROM_RAWDATA(pRawData, pInfoHdr, pUIInfo);
|
|
|
|
ASSERT(pUIInfo != NULL);
|
|
|
|
if ((dwFeatureID >= MAX_GID) ||
|
|
(pFeature = GET_PREDEFINED_FEATURE(pUIInfo, dwFeatureID)) == NULL)
|
|
{
|
|
VERBOSE(("MapToDeviceOptIndex failed: feature ID = %d\n", dwFeatureID));
|
|
|
|
if (!pdwOptionIndexes)
|
|
return OPTION_INDEX_ANY;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// pdwOptionIndexes can be non-NULL only if dwFeatureID is GID_PAGESIZE.
|
|
//
|
|
|
|
ASSERT(dwFeatureID == GID_PAGESIZE || pdwOptionIndexes == NULL);
|
|
|
|
return DwInternalMapToOptIndex(pUIInfo, pFeature, lParam1, lParam2, pdwOptionIndexes);
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
CombineOptionArray(
|
|
IN PRAWBINARYDATA pRawData,
|
|
OUT POPTSELECT pCombinedOptions,
|
|
IN INT iMaxOptions,
|
|
IN POPTSELECT pDocOptions,
|
|
IN POPTSELECT pPrinterOptions
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Combine doc-sticky with printer-sticky option selections to form a single option array
|
|
|
|
Arguments:
|
|
|
|
pRawData - Points to raw binary printer description data
|
|
pCombinedOptions - Points to an array of OPTSELECTs for holding the combined options
|
|
iMaxOptions - Max number of entries in pCombinedOptions array
|
|
pDocOptions - Specifies the array of doc-sticky options
|
|
pPrinterOptions - Specifies the array of printer-sticky options
|
|
|
|
Return Value:
|
|
|
|
FALSE if the combined option array is not large enough to store
|
|
all the option values, TRUE otherwise.
|
|
|
|
Note:
|
|
|
|
Either pDocOptions or pPrinterOptions could be NULL but not both. If pDocOptions
|
|
is NULL, then in the combined option array, the options for document-sticky
|
|
features will be OPTION_INDEX_ANY. Same is true when pPrinterOptions is NULL.
|
|
|
|
--*/
|
|
|
|
{
|
|
PINFOHEADER pInfoHdr;
|
|
PUIINFO pUIInfo;
|
|
PFEATURE pFeatures;
|
|
INT iCount, iDocOptions, iPrinterOptions, iNext;
|
|
|
|
//
|
|
// Calculate the number of features: both doc-sticky and printer-sticky
|
|
//
|
|
|
|
PPD_GET_UIINFO_FROM_RAWDATA(pRawData, pInfoHdr, pUIInfo);
|
|
|
|
ASSERT(pUIInfo != NULL);
|
|
|
|
pFeatures = OFFSET_TO_POINTER(pInfoHdr, pUIInfo->loFeatureList);
|
|
|
|
iDocOptions = (INT) pRawData->dwDocumentFeatures;
|
|
iPrinterOptions = (INT) pRawData->dwPrinterFeatures;
|
|
iNext = iDocOptions + iPrinterOptions;
|
|
ASSERT(iNext <= iMaxOptions);
|
|
|
|
//
|
|
// Copy doc-sticky options into the combined array.
|
|
// Take care of the special case where pDocOptions is NULL.
|
|
//
|
|
|
|
if (pDocOptions == NULL)
|
|
{
|
|
for (iCount = 0; iCount < iDocOptions; iCount++)
|
|
{
|
|
pCombinedOptions[iCount].ubCurOptIndex = OPTION_INDEX_ANY;
|
|
pCombinedOptions[iCount].ubNext = NULL_OPTSELECT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (iCount = 0; iCount < iDocOptions; iCount++)
|
|
{
|
|
VCopyOptionSelections(pCombinedOptions,
|
|
iCount,
|
|
pDocOptions,
|
|
iCount,
|
|
&iNext,
|
|
iMaxOptions);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Copy printer-sticky options into the combined option array.
|
|
//
|
|
|
|
if (pPrinterOptions == NULL)
|
|
{
|
|
for (iCount = 0; iCount < iPrinterOptions; iCount++)
|
|
{
|
|
pCombinedOptions[iCount + iDocOptions].ubCurOptIndex = OPTION_INDEX_ANY;
|
|
pCombinedOptions[iCount + iDocOptions].ubNext = NULL_OPTSELECT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (iCount = 0; iCount < iPrinterOptions; iCount++)
|
|
{
|
|
VCopyOptionSelections(pCombinedOptions,
|
|
iCount + iDocOptions,
|
|
pPrinterOptions,
|
|
iCount,
|
|
&iNext,
|
|
iMaxOptions);
|
|
}
|
|
}
|
|
|
|
if (iNext > iMaxOptions)
|
|
WARNING(("Option array too small: size = %d, needed = %d\n", iMaxOptions, iNext));
|
|
|
|
return (iNext <= iMaxOptions);
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
SeparateOptionArray(
|
|
IN PRAWBINARYDATA pRawData,
|
|
IN POPTSELECT pCombinedOptions,
|
|
OUT POPTSELECT pOptions,
|
|
IN INT iMaxOptions,
|
|
IN INT iMode
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Separate an option array into doc-sticky and for printer-sticky options
|
|
|
|
Arguments:
|
|
|
|
pRawData - Points to raw binary printer description data
|
|
pCombinedOptions - Points to the combined option array to be separated
|
|
pOptions - Points to an array of OPTSELECT structures
|
|
for storing the separated option array
|
|
iMaxOptions - Max number of entries in pOptions array
|
|
iMode - Whether the caller is interested in doc- or printer-sticky options:
|
|
MODE_DOCUMENT_STICKY
|
|
MODE_PRINTER_STICKY
|
|
|
|
Return Value:
|
|
|
|
FALSE if the destination option array is not large enough to hold
|
|
the separated option values, TRUE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
INT iStart, iCount, iOptions, iNext;
|
|
|
|
//
|
|
// Determine if the caller is interested in doc-sticky or printer-sticky options
|
|
//
|
|
|
|
if (iMode == MODE_DOCUMENT_STICKY)
|
|
{
|
|
iStart = 0;
|
|
iOptions = (INT) pRawData->dwDocumentFeatures;
|
|
}
|
|
else
|
|
{
|
|
ASSERT (iMode == MODE_PRINTER_STICKY);
|
|
iStart = (INT) pRawData->dwDocumentFeatures;
|
|
iOptions = (INT) pRawData->dwPrinterFeatures;
|
|
}
|
|
|
|
iNext = iOptions;
|
|
ASSERT(iNext <= iMaxOptions);
|
|
|
|
//
|
|
// Separate the requested options out of the combined option array
|
|
//
|
|
|
|
for (iCount = 0; iCount < iOptions; iCount++)
|
|
{
|
|
VCopyOptionSelections(pOptions,
|
|
iCount,
|
|
pCombinedOptions,
|
|
iStart + iCount,
|
|
&iNext,
|
|
iMaxOptions);
|
|
}
|
|
|
|
if (iNext > iMaxOptions)
|
|
WARNING(("Option array too small: size = %d, needed = %d\n", iMaxOptions, iNext));
|
|
|
|
return (iNext <= iMaxOptions);
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
ReconstructOptionArray(
|
|
IN PRAWBINARYDATA pRawData,
|
|
IN OUT POPTSELECT pOptions,
|
|
IN INT iMaxOptions,
|
|
IN DWORD dwFeatureIndex,
|
|
IN PBOOL pbSelectedOptions
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Modify an option array to change the selected options for the specified feature
|
|
|
|
Arguments:
|
|
|
|
pRawData - Points to raw binary printer description data
|
|
pOptions - Points to an array of OPTSELECT structures to be modified
|
|
iMaxOptions - Max number of entries in pOptions array
|
|
dwFeatureIndex - Specifies the index of printer feature in question
|
|
pbSelectedOptions - Which options of the specified feature is selected
|
|
|
|
Return Value:
|
|
|
|
FALSE if the input option array is not large enough to hold
|
|
all modified option values. TRUE otherwise.
|
|
|
|
Note:
|
|
|
|
Number of BOOLs in pSelectedOptions must match the number of options
|
|
for the specified feature.
|
|
|
|
This function always leaves the option array in a compact format (i.e.
|
|
all unused entries are left at the end of the array).
|
|
|
|
--*/
|
|
|
|
{
|
|
INT iNext, iCount, iDest;
|
|
DWORD dwIndex;
|
|
PINFOHEADER pInfoHdr;
|
|
PUIINFO pUIInfo;
|
|
PFEATURE pFeature;
|
|
POPTSELECT pTempOptions;
|
|
|
|
ASSERT(pOptions && pbSelectedOptions);
|
|
|
|
//
|
|
// Get pointers to various data structures
|
|
//
|
|
|
|
PPD_GET_UIINFO_FROM_RAWDATA(pRawData, pInfoHdr, pUIInfo);
|
|
|
|
ASSERT(pUIInfo != NULL);
|
|
|
|
if (! (pFeature = PGetIndexedFeature(pUIInfo, dwFeatureIndex)))
|
|
{
|
|
ASSERT(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Assume the entire input option array is used by default. This is
|
|
// not exactly true but it shouldn't have any adverse effects either.
|
|
//
|
|
|
|
iNext = iMaxOptions;
|
|
|
|
//
|
|
// Special case (faster) for non-PickMany UI types
|
|
//
|
|
|
|
if (pFeature->dwUIType != UITYPE_PICKMANY)
|
|
{
|
|
for (dwIndex = 0, iCount = 0;
|
|
dwIndex < pFeature->Options.dwCount;
|
|
dwIndex ++)
|
|
{
|
|
if (pbSelectedOptions[dwIndex])
|
|
{
|
|
pOptions[dwFeatureIndex].ubCurOptIndex = (BYTE) dwIndex;
|
|
ASSERT(pOptions[dwFeatureIndex].ubNext == NULL_OPTSELECT);
|
|
iCount++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Exactly one option is allowed to be selected
|
|
//
|
|
|
|
ASSERT(iCount == 1);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Handle PickMany UI type:
|
|
// allocate a temporary option array and copy the input option values
|
|
// except the option values for the specified feature.
|
|
//
|
|
|
|
if (pTempOptions = MemAllocZ(iMaxOptions * sizeof(OPTSELECT)))
|
|
{
|
|
DWORD dwOptions;
|
|
|
|
dwOptions = pRawData->dwDocumentFeatures + pRawData->dwPrinterFeatures;
|
|
iNext = dwOptions;
|
|
|
|
if (iNext > iMaxOptions)
|
|
{
|
|
ASSERT(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
for (dwIndex = 0; dwIndex < dwOptions; dwIndex++)
|
|
{
|
|
if (dwIndex != dwFeatureIndex)
|
|
{
|
|
VCopyOptionSelections(pTempOptions,
|
|
dwIndex,
|
|
pOptions,
|
|
dwIndex,
|
|
&iNext,
|
|
iMaxOptions);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Reconstruct the option values for the specified feature
|
|
//
|
|
|
|
pTempOptions[dwFeatureIndex].ubCurOptIndex = OPTION_INDEX_ANY;
|
|
pTempOptions[dwFeatureIndex].ubNext = NULL_OPTSELECT;
|
|
|
|
iDest = dwFeatureIndex;
|
|
iCount = 0;
|
|
|
|
for (dwIndex = 0; dwIndex < pFeature->Options.dwCount; dwIndex ++)
|
|
{
|
|
if (pbSelectedOptions[dwIndex])
|
|
{
|
|
if (iCount++ == 0)
|
|
{
|
|
//
|
|
// The first selected option
|
|
//
|
|
|
|
pTempOptions[iDest].ubCurOptIndex = (BYTE) dwIndex;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Subsequent selected options
|
|
//
|
|
|
|
if (iNext < iMaxOptions)
|
|
{
|
|
pTempOptions[iDest].ubNext = (BYTE) iNext;
|
|
pTempOptions[iNext].ubCurOptIndex = (BYTE) dwIndex;
|
|
iDest = iNext;
|
|
}
|
|
|
|
iNext++;
|
|
}
|
|
}
|
|
}
|
|
|
|
pTempOptions[iDest].ubNext = NULL_OPTSELECT;
|
|
|
|
//
|
|
// Copy the reconstructed option array from the temporary buffer
|
|
// back to the input option array provided by the caller.
|
|
//
|
|
|
|
CopyMemory(pOptions, pTempOptions, iMaxOptions * sizeof(OPTSELECT));
|
|
MemFree(pTempOptions);
|
|
}
|
|
else
|
|
{
|
|
ERR(("Cannot allocate memory for temporary option array\n"));
|
|
}
|
|
}
|
|
|
|
return (iNext <= iMaxOptions);
|
|
}
|
|
|
|
|
|
|
|
PTSTR
|
|
GenerateBpdFilename(
|
|
PTSTR ptstrPpdFilename
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Generate a filename for the cached binary PPD data given a PPD filename
|
|
|
|
Arguments:
|
|
|
|
ptstrPpdFilename - Specifies the PPD filename
|
|
|
|
Return Value:
|
|
|
|
Pointer to BPD filename string, NULL if there is an error
|
|
|
|
--*/
|
|
|
|
{
|
|
PTSTR ptstrBpdFilename, ptstrExtension;
|
|
INT iLength;
|
|
|
|
//
|
|
// If the PPD filename has .PPD extension, replace it with .BPD extension.
|
|
// Otherwise, append .BPD extension at the end.
|
|
//
|
|
|
|
iLength = _tcslen(ptstrPpdFilename);
|
|
|
|
if ((ptstrExtension = _tcsrchr(ptstrPpdFilename, TEXT('.'))) == NULL ||
|
|
_tcsicmp(ptstrExtension, PPD_FILENAME_EXT) != EQUAL_STRING)
|
|
{
|
|
WARNING(("Bad PPD filename extension: %ws\n", ptstrPpdFilename));
|
|
|
|
ptstrExtension = ptstrPpdFilename + iLength;
|
|
iLength += _tcslen(BPD_FILENAME_EXT);
|
|
}
|
|
|
|
//
|
|
// Allocate memory and compose the BPD filename
|
|
//
|
|
|
|
if (ptstrBpdFilename = MemAlloc((iLength + 1) * sizeof(TCHAR)))
|
|
{
|
|
StringCchCopyW(ptstrBpdFilename, iLength + 1, ptstrPpdFilename);
|
|
|
|
//
|
|
// The first if-block ensures that (ptstrExtension - ptstrPpdFileName) is
|
|
// non-negative, and (iLength + 1) is greater than (ptstrExtension - ptstrPpdFileName).
|
|
//
|
|
StringCchCopyW(ptstrBpdFilename + (ptstrExtension - ptstrPpdFilename),
|
|
(iLength + 1) - (ptstrExtension - ptstrPpdFilename),
|
|
BPD_FILENAME_EXT);
|
|
|
|
VERBOSE(("BPD filename: %ws\n", ptstrBpdFilename));
|
|
}
|
|
else
|
|
{
|
|
ERR(("Memory allocation failed: %d\n", GetLastError()));
|
|
}
|
|
|
|
return ptstrBpdFilename;
|
|
}
|
|
|
|
|
|
|
|
PRAWBINARYDATA
|
|
PpdLoadCachedBinaryData(
|
|
PTSTR ptstrPpdFilename
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Load cached binary PPD data file into memory
|
|
|
|
Arguments:
|
|
|
|
ptstrPpdFilename - Specifies the PPD filename
|
|
|
|
Return Value:
|
|
|
|
Pointer to PPD data if successful, NULL if there is an error
|
|
|
|
--*/
|
|
|
|
{
|
|
HFILEMAP hFileMap;
|
|
DWORD dwSize;
|
|
PVOID pvData;
|
|
PTSTR ptstrBpdFilename;
|
|
PRAWBINARYDATA pRawData, pCopiedData;
|
|
BOOL bValidCache = FALSE;
|
|
|
|
//
|
|
// Generate BPD filename from the specified PPD filename
|
|
//
|
|
|
|
if (! (ptstrBpdFilename = GenerateBpdFilename(ptstrPpdFilename)))
|
|
return NULL;
|
|
|
|
//
|
|
// First map the data file into memory
|
|
//
|
|
|
|
if (! (hFileMap = MapFileIntoMemory(ptstrBpdFilename, &pvData, &dwSize)))
|
|
{
|
|
TERSE(("Couldn't map file '%ws' into memory: %d\n", ptstrBpdFilename, GetLastError()));
|
|
MemFree(ptstrBpdFilename);
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Verify size, parser version number, and signature.
|
|
// Allocate a memory buffer and copy data into it.
|
|
//
|
|
|
|
pRawData = pvData;
|
|
pCopiedData = NULL;
|
|
|
|
if ((dwSize > sizeof(INFOHEADER) + sizeof(UIINFO) + sizeof(PPDDATA)) &&
|
|
(dwSize >= pRawData->dwFileSize) &&
|
|
(pRawData->dwParserVersion == PPD_PARSER_VERSION) &&
|
|
(pRawData->dwParserSignature == PPD_PARSER_SIGNATURE) &&
|
|
(BIsRawBinaryDataUpToDate(pRawData)))
|
|
{
|
|
#ifndef WINNT_40
|
|
|
|
PPPDDATA pPpdData;
|
|
|
|
//
|
|
// For Win2K+ systems, we support MUI where user can switch UI language
|
|
// and MUI knows to redirect resource loading calls to the correct resource
|
|
// DLL (built by MUI). However, PPD parser caches some display names into
|
|
// the .bpd file, where the display names are obtained based on the UI
|
|
// language when the parsing occurs. To support MUI, we store the UI language
|
|
// ID into the .bpd file and now if we see the current user's UI language ID
|
|
// doesn't match to the one stored in the .bpd file, we need to throw away
|
|
// the old .bpd file and reparse the .ppd, so we can get correct display names
|
|
// under current UI language.
|
|
//
|
|
|
|
pPpdData = GET_DRIVER_INFO_FROM_INFOHEADER((PINFOHEADER)pRawData);
|
|
|
|
if (pPpdData && pPpdData->dwUserDefUILangID == (DWORD)GetUserDefaultUILanguage())
|
|
{
|
|
bValidCache = TRUE;
|
|
}
|
|
|
|
#else
|
|
|
|
bValidCache = TRUE;
|
|
|
|
#endif // !WINNT_40
|
|
}
|
|
|
|
if (bValidCache &&
|
|
(pCopiedData = MemAlloc(dwSize)))
|
|
{
|
|
CopyMemory(pCopiedData, pRawData, dwSize);
|
|
}
|
|
else
|
|
{
|
|
ERR(("Invalid binary PPD data\n"));
|
|
SetLastError(ERROR_INVALID_DATA);
|
|
}
|
|
|
|
MemFree(ptstrBpdFilename);
|
|
UnmapFileFromMemory(hFileMap);
|
|
|
|
return pCopiedData;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
BSearchConstraintList(
|
|
PUIINFO pUIInfo,
|
|
DWORD dwConstraintIndex,
|
|
DWORD dwFeature,
|
|
DWORD dwOption
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Check if the specified feature/option appears in a constraint list
|
|
|
|
Arguments:
|
|
|
|
pUIInfo - Points to a UIINFO structure
|
|
dwConstraintIndex - Specifies the constraint list to be searched
|
|
dwFeature, dwOption - Specifies the feature/option we're interested in
|
|
|
|
Return Value:
|
|
|
|
TRUE if dwFeature/dwOption appears on the specified constraint list
|
|
FALSE otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
PUICONSTRAINT pConstraint;
|
|
BOOL bMatch = FALSE;
|
|
|
|
pConstraint = OFFSET_TO_POINTER(pUIInfo->pInfoHeader, pUIInfo->UIConstraints.loOffset);
|
|
|
|
//
|
|
// Go through each item on the constraint list
|
|
//
|
|
|
|
while (!bMatch && (dwConstraintIndex != NULL_CONSTRAINT))
|
|
{
|
|
ASSERT(dwConstraintIndex < pUIInfo->UIConstraints.dwCount);
|
|
|
|
if (pConstraint[dwConstraintIndex].dwFeatureIndex == dwFeature)
|
|
{
|
|
//
|
|
// If the option index is OPTION_INDEX_ANY, it matches
|
|
// any option other than None/False.
|
|
//
|
|
|
|
if (pConstraint[dwConstraintIndex].dwOptionIndex == OPTION_INDEX_ANY)
|
|
{
|
|
PFEATURE pFeature;
|
|
|
|
pFeature = PGetIndexedFeature(pUIInfo, dwFeature);
|
|
ASSERT(pFeature != NULL);
|
|
|
|
bMatch = (pFeature->dwNoneFalseOptIndex != dwOption);
|
|
}
|
|
else
|
|
{
|
|
bMatch = (pConstraint[dwConstraintIndex].dwOptionIndex == dwOption);
|
|
}
|
|
}
|
|
|
|
dwConstraintIndex = pConstraint[dwConstraintIndex].dwNextConstraint;
|
|
}
|
|
|
|
return bMatch;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
BCheckFeatureOptionConflict(
|
|
PUIINFO pUIInfo,
|
|
DWORD dwFeature1,
|
|
DWORD dwOption1,
|
|
DWORD dwFeature2,
|
|
DWORD dwOption2
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Check if there is a conflict between a pair of feature/options
|
|
|
|
Arguments:
|
|
|
|
pUIInfo - Points to a UIINFO structure
|
|
dwFeature1, dwOption1 - Specifies the first feature/option
|
|
dwFeature2, dwOption2 - Specifies the second feature/option
|
|
|
|
Return Value:
|
|
|
|
TRUE if dwFeature1/dwOption1 constrains dwFeature2/dwOption2,
|
|
FALSE otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
PFEATURE pFeature;
|
|
POPTION pOption;
|
|
|
|
//
|
|
// Check for special case:
|
|
// either dwOption1 or dwOption2 is OPTION_INDEX_ANY
|
|
//
|
|
|
|
if ((dwOption1 == OPTION_INDEX_ANY) ||
|
|
(dwOption2 == OPTION_INDEX_ANY) ||
|
|
(dwFeature1 == dwFeature2 && dwOption1 == dwOption2))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Go through the constraint list associated with dwFeature1
|
|
//
|
|
|
|
if (! (pFeature = PGetIndexedFeature(pUIInfo, dwFeature1)))
|
|
return FALSE;
|
|
|
|
if ((dwOption1 != pFeature->dwNoneFalseOptIndex) &&
|
|
BSearchConstraintList(pUIInfo,
|
|
pFeature->dwUIConstraintList,
|
|
dwFeature2,
|
|
dwOption2))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Go through the constraint list associated with dwFeature1/dwOption1
|
|
//
|
|
|
|
if ((pOption = PGetIndexedOption(pUIInfo, pFeature, dwOption1)) &&
|
|
BSearchConstraintList(pUIInfo,
|
|
pOption->dwUIConstraintList,
|
|
dwFeature2,
|
|
dwOption2))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Automatically check the reciprocal constraint for:
|
|
// (dwFeature2, dwOption2) => (dwFeature1, dwOption1)
|
|
//
|
|
|
|
if (! (pFeature = PGetIndexedFeature(pUIInfo, dwFeature2)))
|
|
return FALSE;
|
|
|
|
if ((dwOption2 != pFeature->dwNoneFalseOptIndex) &&
|
|
BSearchConstraintList(pUIInfo,
|
|
pFeature->dwUIConstraintList,
|
|
dwFeature1,
|
|
dwOption1))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
if ((pOption = PGetIndexedOption(pUIInfo, pFeature, dwOption2)) &&
|
|
BSearchConstraintList(pUIInfo,
|
|
pOption->dwUIConstraintList,
|
|
dwFeature1,
|
|
dwOption1))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
BCheckFeatureConflict(
|
|
PUIINFO pUIInfo,
|
|
POPTSELECT pOptions,
|
|
DWORD dwFeature1,
|
|
PDWORD pdwOption1,
|
|
DWORD dwFeature2,
|
|
DWORD dwOption2
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Check if there is a conflict between the current option selections
|
|
of a feature and the specified feature/option
|
|
|
|
Arguments:
|
|
|
|
pUIInfo - Points to a UIINFO structure
|
|
pOptions - Points to the current feature option selections
|
|
dwFeature1 - Specifies the feature whose current option selections we're interested in
|
|
pdwOption1 - In case of a conflict, returns the option of dwFeature1
|
|
which caused the conflict
|
|
dwFeature2, dwOption2 - Specifies the feature/option to be checked
|
|
|
|
Return Value:
|
|
|
|
TRUE if there is a conflict between the current selections of dwFeature1
|
|
and dwFeature2/dwOption2, FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD dwIndex = dwFeature1;
|
|
|
|
do
|
|
{
|
|
if (BCheckFeatureOptionConflict(pUIInfo,
|
|
dwFeature1,
|
|
pOptions[dwIndex].ubCurOptIndex,
|
|
dwFeature2,
|
|
dwOption2))
|
|
{
|
|
*pdwOption1 = pOptions[dwIndex].ubCurOptIndex;
|
|
return TRUE;
|
|
}
|
|
|
|
dwIndex = pOptions[dwIndex].ubNext;
|
|
|
|
} while (dwIndex != NULL_OPTSELECT);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
DwReplaceFeatureOption(
|
|
PUIINFO pUIInfo,
|
|
POPTSELECT pOptions,
|
|
DWORD dwFeatureIndex,
|
|
DWORD dwOptionIndex,
|
|
DWORD dwHint
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
description-of-function
|
|
|
|
Arguments:
|
|
|
|
pUIInfo - Points to UIINFO structure
|
|
pOptions - Points to the options array to be modified
|
|
dwFeatureIndex, dwOptionIndex - Specifies the feature/option to be replaced
|
|
dwHint - Hint on how to replace the specified feature option.
|
|
|
|
Return Value:
|
|
|
|
New hint value to be used next time this function is called on the same feature.
|
|
|
|
Note:
|
|
|
|
HIWORD of dwHint should be returned untouched. LOWORD of dwHint is
|
|
used by this function to determine how to replace the specified feature/option.
|
|
|
|
--*/
|
|
|
|
{
|
|
PFEATURE pFeature;
|
|
DWORD dwNext;
|
|
|
|
pFeature = PGetIndexedFeature(pUIInfo, dwFeatureIndex);
|
|
ASSERT(pFeature != NULL);
|
|
|
|
if (pFeature->dwUIType == UITYPE_PICKMANY)
|
|
{
|
|
//
|
|
// For PickMany feature, simply unselect the specified feature option.
|
|
//
|
|
|
|
dwNext = dwFeatureIndex;
|
|
|
|
while ((pOptions[dwNext].ubCurOptIndex != dwOptionIndex) &&
|
|
(dwNext = pOptions[dwNext].ubNext) != NULL_OPTSELECT)
|
|
{
|
|
}
|
|
|
|
if (dwNext != NULL_OPTSELECT)
|
|
{
|
|
DWORD dwLast;
|
|
|
|
pOptions[dwNext].ubCurOptIndex = OPTION_INDEX_ANY;
|
|
|
|
//
|
|
// Compact the list of selected options for the specified
|
|
// feature to filter out any redundant OPTION_INDEX_ANY entries.
|
|
//
|
|
|
|
dwLast = dwNext = dwFeatureIndex;
|
|
|
|
do
|
|
{
|
|
if (pOptions[dwNext].ubCurOptIndex != OPTION_INDEX_ANY)
|
|
{
|
|
pOptions[dwLast].ubCurOptIndex = pOptions[dwNext].ubCurOptIndex;
|
|
dwLast = pOptions[dwLast].ubNext;
|
|
}
|
|
|
|
dwNext = pOptions[dwNext].ubNext;
|
|
}
|
|
while (dwNext != NULL_OPTSELECT);
|
|
|
|
pOptions[dwLast].ubNext = NULL_OPTSELECT;
|
|
}
|
|
else
|
|
{
|
|
ERR(("Trying to replace non-existent feature/option.\n"));
|
|
}
|
|
|
|
return dwHint;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// For non-PickMany feature, use the hint paramater to determine
|
|
// how to replace the specified feature option:
|
|
//
|
|
// If this is the first time we're trying to replace the
|
|
// selected option of the specified feature, then we'll
|
|
// replace it with the default option for that feature.
|
|
//
|
|
// Otherwise, we'll try each option of the specified feature in turn.
|
|
//
|
|
// If we've exhausted all of the options for the specified
|
|
// (which should happen if the PPD file is well-formed),
|
|
// then we'll use OPTION_INDEX_ANY as the last resort.
|
|
//
|
|
|
|
dwNext = dwHint & 0xffff;
|
|
|
|
if (dwNext == 0)
|
|
dwOptionIndex = pFeature->dwDefaultOptIndex;
|
|
else if (dwNext > pFeature->Options.dwCount)
|
|
dwOptionIndex = OPTION_INDEX_ANY;
|
|
else
|
|
dwOptionIndex = dwNext - 1;
|
|
|
|
pOptions[dwFeatureIndex].ubCurOptIndex = (BYTE) dwOptionIndex;
|
|
|
|
return (dwHint & 0xffff0000) | (dwNext + 1);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
DwInternalMapToOptIndex(
|
|
PUIINFO pUIInfo,
|
|
PFEATURE pFeature,
|
|
LONG lParam1,
|
|
LONG lParam2,
|
|
OUT PDWORD pdwOptionIndexes
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Map logical values to device feature option index
|
|
|
|
Arguments:
|
|
|
|
pUIInfo - Points to UIINFO structure
|
|
pFeature - Specifies the interested feature
|
|
lParam1, lParam2 - Parameters depending on pFeature->dwFeatureID
|
|
pdwOptionIndexes - if Not NULL, means fill this array with all indicies
|
|
which match the search criteria. In this case the return value
|
|
is the number of elements in the array initialized. Currently
|
|
we assume the array is large enough (256 elements).
|
|
(It should be non-NULL only for GID_PAGESIZE.)
|
|
|
|
GID_PAGESIZE:
|
|
map logical paper specification to physical PageSize option
|
|
|
|
lParam1 = paper width in microns
|
|
lParam2 = paper height in microns
|
|
|
|
GID_RESOLUTION:
|
|
map logical resolution to physical Resolution option
|
|
|
|
lParam1 = x-resolution in dpi
|
|
lParam2 = y-resolution in dpi
|
|
|
|
GID_INPUTSLOT:
|
|
map logical paper source to physical InputSlot option
|
|
|
|
lParam1 = DEVMODE.dmDefaultSource
|
|
|
|
GID_DUPLEX:
|
|
map logical duplex selection to physical Duplex option
|
|
|
|
lParam1 = DEVMODE.dmDuplex
|
|
|
|
GID_COLLATE:
|
|
map logical collate selection to physical Collate option
|
|
|
|
lParam1 = DEVMODE.dmCollate
|
|
|
|
GID_MEDIATYPE:
|
|
map logical media type to physical MediaType option
|
|
|
|
lParam1 = DEVMODE.dmMediaType
|
|
|
|
Return Value:
|
|
|
|
If pdwOptionIndexes is NULL, returns index of the feature option corresponding
|
|
to the specified logical values; OPTION_INDEX_ANY if the specified logical
|
|
values cannot be mapped to any feature option.
|
|
|
|
If pdwOptionIndexes is not NULL (for GID_PAGESIZE), returns the number of elements
|
|
filled in the output buffer. Zero means the specified logical values cannot be mapped
|
|
to any feature option.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD dwIndex, dwOptionIndex;
|
|
|
|
//
|
|
// Handle it according to what dwFeatureID is specified
|
|
//
|
|
|
|
dwOptionIndex = pFeature->dwNoneFalseOptIndex;
|
|
|
|
switch (pFeature->dwFeatureID)
|
|
{
|
|
case GID_PAGESIZE:
|
|
{
|
|
PPAGESIZE pPaper;
|
|
LONG lXDelta, lYDelta;
|
|
DWORD dwExactMatch;
|
|
|
|
//
|
|
// lParam1 = paper width
|
|
// lParam1 = paper height
|
|
//
|
|
|
|
//
|
|
// Go through the list of paper sizes supported by the printer
|
|
// and see if we can find an exact match to the requested size.
|
|
// (The tolerance is 1mm). If not, remember the closest match found.
|
|
//
|
|
|
|
dwExactMatch = 0;
|
|
|
|
for (dwIndex = 0; dwIndex < pFeature->Options.dwCount; dwIndex++)
|
|
{
|
|
pPaper = PGetIndexedOption(pUIInfo, pFeature, dwIndex);
|
|
ASSERT(pPaper != NULL);
|
|
|
|
//
|
|
// Custom page size is handled differently - skip it here.
|
|
//
|
|
|
|
if (pPaper->dwPaperSizeID == DMPAPER_CUSTOMSIZE)
|
|
continue;
|
|
|
|
lXDelta = abs(pPaper->szPaperSize.cx - lParam1);
|
|
lYDelta = abs(pPaper->szPaperSize.cy - lParam2);
|
|
|
|
if (lXDelta <= 1000 && lYDelta <= 1000)
|
|
{
|
|
//
|
|
// Exact match is found
|
|
//
|
|
|
|
if (pdwOptionIndexes)
|
|
{
|
|
pdwOptionIndexes[dwExactMatch++] = dwIndex;
|
|
}
|
|
else
|
|
{
|
|
dwOptionIndex = dwIndex;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (dwExactMatch > 0)
|
|
{
|
|
//
|
|
// Exact match(es) found
|
|
//
|
|
|
|
dwOptionIndex = dwExactMatch;
|
|
}
|
|
else if (dwIndex >= pFeature->Options.dwCount)
|
|
{
|
|
//
|
|
// No exact match found
|
|
//
|
|
|
|
if (SUPPORT_CUSTOMSIZE(pUIInfo) &&
|
|
BFormSupportedThruCustomSize((PRAWBINARYDATA) pUIInfo->pInfoHeader, lParam1, lParam2, NULL))
|
|
{
|
|
dwOptionIndex = pUIInfo->dwCustomSizeOptIndex;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We used to use dwClosestIndex as dwOptionIndex here, but see bug #124203, we now
|
|
// choose to behave the same as Unidrv that if there is no exact match, we return no
|
|
// match instead of the cloest match.
|
|
//
|
|
|
|
dwOptionIndex = OPTION_INDEX_ANY;
|
|
}
|
|
|
|
if (pdwOptionIndexes)
|
|
{
|
|
if (dwOptionIndex == OPTION_INDEX_ANY)
|
|
dwOptionIndex = 0;
|
|
else
|
|
{
|
|
pdwOptionIndexes[0] = dwOptionIndex;
|
|
dwOptionIndex = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case GID_INPUTSLOT:
|
|
|
|
//
|
|
// lParam1 = DEVMODE.dmDefaultSource
|
|
//
|
|
|
|
dwOptionIndex = OPTION_INDEX_ANY;
|
|
|
|
if (lParam1 >= DMBIN_USER)
|
|
{
|
|
//
|
|
// An input slot is specifically requested.
|
|
//
|
|
|
|
dwIndex = lParam1 - DMBIN_USER;
|
|
|
|
if (dwIndex < pFeature->Options.dwCount)
|
|
dwOptionIndex = dwIndex;
|
|
}
|
|
else if (lParam1 == DMBIN_MANUAL || lParam1 == DMBIN_ENVMANUAL)
|
|
{
|
|
//
|
|
// Manual feed is requested
|
|
//
|
|
|
|
for (dwIndex = 0; dwIndex < pFeature->Options.dwCount; dwIndex ++)
|
|
{
|
|
PINPUTSLOT pInputSlot;
|
|
|
|
if ((pInputSlot = PGetIndexedOption(pUIInfo, pFeature, dwIndex)) &&
|
|
(pInputSlot->dwPaperSourceID == DMBIN_MANUAL))
|
|
{
|
|
dwOptionIndex = dwIndex;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (dwOptionIndex == OPTION_INDEX_ANY)
|
|
{
|
|
//
|
|
// Treat all other cases as if no input slot is explicitly requested.
|
|
// At print time, the driver will choose an input slot based on
|
|
// the form-to-tray assignment table.
|
|
//
|
|
|
|
dwOptionIndex = 0;
|
|
}
|
|
break;
|
|
|
|
case GID_RESOLUTION:
|
|
|
|
//
|
|
// lParam1 = x-resolution
|
|
// lParam2 = y-resolution
|
|
//
|
|
|
|
{
|
|
PRESOLUTION pRes;
|
|
|
|
//
|
|
// check whether it's one of the predefined DMRES_-values
|
|
//
|
|
|
|
if ((lParam1 < 0) && (lParam2 < 0))
|
|
{
|
|
DWORD dwHiResId=0, dwLoResId, dwMedResId, dwDraftResId=0;
|
|
DWORD dwHiResProd=0, dwMedResProd=0, dwLoResProd= 0xffffffff, dwDraftResProd= 0xffffffff;
|
|
BOOL bValid = FALSE; // if there is at least one valid entry
|
|
DWORD dwResProd;
|
|
|
|
// no need to sort all the available options, just pick out the interesting ones
|
|
for (dwIndex = 0; dwIndex < pFeature->Options.dwCount; dwIndex++)
|
|
{
|
|
if ((pRes = PGetIndexedOption(pUIInfo, pFeature, dwIndex)) != NULL)
|
|
{
|
|
bValid = TRUE;
|
|
|
|
dwResProd = pRes->iXdpi * pRes->iYdpi; // use product as sort criteria
|
|
|
|
if (dwResProd > dwHiResProd) // take highest as high resolution
|
|
{
|
|
// previous max. is now second highest
|
|
dwMedResProd= dwHiResProd;
|
|
dwMedResId = dwHiResId;
|
|
|
|
dwHiResProd = dwResProd;
|
|
dwHiResId = dwIndex;
|
|
}
|
|
else if (dwResProd == dwHiResProd)
|
|
{
|
|
// duplicates possible, if e.g. 300x600 as well as 600x300 supported
|
|
// skip that
|
|
}
|
|
else if (dwResProd > dwMedResProd) // take second highest as medium,
|
|
{ // can only be hit if not max.
|
|
dwMedResProd= dwResProd;
|
|
dwMedResId = dwIndex;
|
|
}
|
|
|
|
if (dwResProd < dwDraftResProd) // take lowest as draft
|
|
{
|
|
// previous min. is now second lowest
|
|
dwLoResProd = dwDraftResProd;
|
|
dwLoResId = dwDraftResId;
|
|
|
|
dwDraftResProd = dwResProd;
|
|
dwDraftResId = dwIndex;
|
|
}
|
|
else if (dwResProd == dwDraftResProd)
|
|
{
|
|
// duplicates possible, if e.g. 300x600 as well as 600x300 supported
|
|
// skip that
|
|
}
|
|
else if (dwResProd < dwLoResProd) // take second lowest as low
|
|
{// can only be hit if not min.
|
|
dwLoResProd = dwResProd;
|
|
dwLoResId = dwIndex;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!bValid) // no valid entry ?
|
|
{
|
|
return OPTION_INDEX_ANY;
|
|
}
|
|
|
|
//
|
|
// Correct medium, might not be touched if less than 3 resolution options
|
|
//
|
|
|
|
if (dwMedResProd == 0)
|
|
{
|
|
dwMedResProd = dwHiResProd;
|
|
dwMedResId = dwHiResId;
|
|
}
|
|
|
|
//
|
|
// Correct low, might not be touched if less than 3 resolution options
|
|
//
|
|
|
|
if (dwLoResProd == 0xffffffff)
|
|
{
|
|
dwLoResProd = dwDraftResProd;
|
|
dwLoResId = dwDraftResId;
|
|
}
|
|
|
|
//
|
|
// if different, take the higher of the requested resolutions
|
|
//
|
|
|
|
switch(min(lParam1, lParam2))
|
|
{
|
|
case DMRES_DRAFT:
|
|
return dwDraftResId;
|
|
|
|
case DMRES_LOW:
|
|
return dwLoResId;
|
|
|
|
case DMRES_MEDIUM:
|
|
return dwMedResId;
|
|
|
|
case DMRES_HIGH:
|
|
return dwHiResId;
|
|
}
|
|
|
|
//
|
|
// requested is not one of the known predefined values
|
|
//
|
|
|
|
return OPTION_INDEX_ANY;
|
|
}
|
|
|
|
//
|
|
// First try to match both x- and y-resolution exactly
|
|
//
|
|
|
|
dwOptionIndex = OPTION_INDEX_ANY;
|
|
|
|
for (dwIndex = 0; dwIndex < pFeature->Options.dwCount; dwIndex++)
|
|
{
|
|
if ((pRes = PGetIndexedOption(pUIInfo, pFeature, dwIndex)) &&
|
|
(pRes->iXdpi == lParam1) &&
|
|
(pRes->iYdpi == lParam2))
|
|
{
|
|
dwOptionIndex = dwIndex;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (dwOptionIndex != OPTION_INDEX_ANY)
|
|
break;
|
|
|
|
//
|
|
// If no exact match is found, then relax the criteria a bit and
|
|
// compare the max of x- and y-resolution.
|
|
//
|
|
|
|
lParam1 = max(lParam1, lParam2);
|
|
|
|
for (dwIndex = 0; dwIndex < pFeature->Options.dwCount; dwIndex++)
|
|
{
|
|
if ((pRes = PGetIndexedOption(pUIInfo, pFeature, dwIndex)) &&
|
|
(max(pRes->iXdpi, pRes->iYdpi) == lParam1))
|
|
{
|
|
dwOptionIndex = dwIndex;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case GID_DUPLEX:
|
|
|
|
//
|
|
// lParam1 = DEVMODE.dmDuplex
|
|
//
|
|
|
|
for (dwIndex = 0; dwIndex < pFeature->Options.dwCount; dwIndex++)
|
|
{
|
|
PDUPLEX pDuplex;
|
|
|
|
if ((pDuplex = PGetIndexedOption(pUIInfo, pFeature, dwIndex)) &&
|
|
((LONG) pDuplex->dwDuplexID == lParam1))
|
|
{
|
|
dwOptionIndex = dwIndex;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case GID_COLLATE:
|
|
|
|
//
|
|
// lParam1 = DEVMODE.dmCollate
|
|
//
|
|
|
|
for (dwIndex = 0; dwIndex < pFeature->Options.dwCount; dwIndex++)
|
|
{
|
|
PCOLLATE pCollate;
|
|
|
|
if ((pCollate = PGetIndexedOption(pUIInfo, pFeature, dwIndex)) &&
|
|
((LONG) pCollate->dwCollateID == lParam1))
|
|
{
|
|
dwOptionIndex = dwIndex;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case GID_MEDIATYPE:
|
|
|
|
//
|
|
// lParam1 = DEVMODE.dmMediaType
|
|
//
|
|
|
|
if (lParam1 >= DMMEDIA_USER)
|
|
{
|
|
dwIndex = lParam1 - DMMEDIA_USER;
|
|
|
|
if (dwIndex < pFeature->Options.dwCount)
|
|
dwOptionIndex = dwIndex;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
|
|
VERBOSE(("DwInternalMapToOptIndex failed: feature ID = %d\n", pFeature->dwFeatureID));
|
|
break;
|
|
}
|
|
|
|
return dwOptionIndex;
|
|
}
|
|
|
|
|
|
|
|
PTSTR
|
|
PtstrGetDefaultTTSubstTable(
|
|
PUIINFO pUIInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Return a copy of the default font substitution table
|
|
|
|
Arguments:
|
|
|
|
pUIInfo - Pointer to UIINFO structure
|
|
|
|
Return Value:
|
|
|
|
Pointer to a copy of the default font substitution table
|
|
NULL if there is an error
|
|
|
|
--*/
|
|
|
|
{
|
|
PTSTR ptstrDefault, ptstrTable = NULL;
|
|
DWORD dwSize;
|
|
|
|
//
|
|
// Make a copy of the default font substitution table
|
|
//
|
|
|
|
if ((ptstrDefault = OFFSET_TO_POINTER(pUIInfo->pubResourceData, pUIInfo->loFontSubstTable)) &&
|
|
(dwSize = pUIInfo->dwFontSubCount) &&
|
|
(ptstrTable = MemAlloc(dwSize)))
|
|
{
|
|
ASSERT(BVerifyMultiSZPair(ptstrDefault, dwSize));
|
|
CopyMemory(ptstrTable, ptstrDefault, dwSize);
|
|
}
|
|
|
|
return ptstrTable;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
VConvertOptSelectArray(
|
|
PRAWBINARYDATA pRawData,
|
|
POPTSELECT pNt5Options,
|
|
DWORD dwNt5MaxCount,
|
|
PBYTE pubNt4Options,
|
|
DWORD dwNt4MaxCount,
|
|
INT iMode
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Convert NT4 feature/option selections to NT5 format
|
|
|
|
Arguments:
|
|
|
|
pRawData - Points to raw binary printer description data
|
|
pNt5Options - Points to NT5 feature/option selection array
|
|
pNt4Options - Points to NT4 feature/option selection array
|
|
iMode - Convert doc- or printer-sticky options?
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
--*/
|
|
|
|
{
|
|
PPPDDATA pPpdData;
|
|
PBYTE pubNt4Mapping;
|
|
DWORD dwNt5Index, dwNt5Offset, dwCount;
|
|
DWORD dwNt4Index, dwNt4Offset;
|
|
|
|
pPpdData = GET_DRIVER_INFO_FROM_INFOHEADER((PINFOHEADER) pRawData);
|
|
|
|
ASSERT(pPpdData != NULL);
|
|
|
|
//
|
|
// Determine whether we're converting doc-sticky or
|
|
// printer-sticky feature selections.
|
|
//
|
|
|
|
if (iMode == MODE_DOCUMENT_STICKY)
|
|
{
|
|
dwCount = pRawData->dwDocumentFeatures;
|
|
dwNt5Offset = dwNt4Offset = 0;
|
|
}
|
|
else
|
|
{
|
|
dwCount = pRawData->dwPrinterFeatures;
|
|
dwNt5Offset = pRawData->dwDocumentFeatures;
|
|
dwNt4Offset = pPpdData->dwNt4DocFeatures;
|
|
}
|
|
|
|
//
|
|
// Get a pointer to the NT4-NT5 feature index mapping table
|
|
//
|
|
|
|
pubNt4Mapping = OFFSET_TO_POINTER(pRawData, pPpdData->Nt4Mapping.loOffset);
|
|
|
|
ASSERT(pubNt4Mapping != NULL);
|
|
|
|
ASSERT(pPpdData->Nt4Mapping.dwCount ==
|
|
pRawData->dwDocumentFeatures + pRawData->dwPrinterFeatures);
|
|
|
|
//
|
|
// Convert the feature option selection array
|
|
//
|
|
|
|
for (dwNt5Index=0; dwNt5Index < dwCount; dwNt5Index++)
|
|
{
|
|
dwNt4Index = pubNt4Mapping[dwNt5Index + dwNt5Offset] - dwNt4Offset;
|
|
|
|
if (dwNt4Index < dwNt4MaxCount && pubNt4Options[dwNt4Index] != OPTION_INDEX_ANY)
|
|
pNt5Options[dwNt5Index].ubCurOptIndex = pubNt4Options[dwNt4Index];
|
|
}
|
|
}
|
|
|