/*++ Copyright (c) 2000 Microsoft Corporation All rights reserved. Module Name: psoemhlp.c Abstract: PostScript helper functions for OEM UI plugins HSetOptions Author: Feng Yue (fengy) 8/24/2000 fengy Completed with support of both PPD and driver features. 7/21/2000 fengy Created it with function framework. --*/ #include "precomp.h" // // PS driver's helper functions for OEM UI plugins // /*++ Routine Name: VUpdatePSF_EMFFeatures Routine Description: change EMF features' settings to make sure they are in sync with each other Arguments: pci - pointer to driver's COMMONINFO structure dwChangedItemID - ID to indicate which item has been changed Return Value: None Last Error: None --*/ VOID VUpdatePSF_EMFFeatures( IN PCOMMONINFO pci, IN DWORD dwChangedItemID ) { PDEVMODE pdm = pci->pdm; PPSDRVEXTRA pdmPrivate = pci->pdmPrivate; // // (refer to VUpdateEmfFeatureItems and VUnpackDocumentPropertiesItems) // if (!((PUIDATA)pci)->bEMFSpooling) { ERR(("VUpdatePSF_EMFFeatures: spooler EMF disabled\n")); return; } if (dwChangedItemID != METASPOOL_ITEM) { if (!ISSET_MFSPOOL_FLAG(pdmPrivate)) { // // need to turn driver EMF on to support the EMF feature // if (dwChangedItemID == NUP_ITEM) { // // booklet // if (NUPOPTION(pdmPrivate) == BOOKLET_UP) { TERSE(("EMF turned on for BOOKLET_UP\n")); SET_MFSPOOL_FLAG(pdmPrivate); } } else if (dwChangedItemID == REVPRINT_ITEM) { BOOL bReversed = BGetPageOrderFlag(pci); // // reverse printing // if ((!REVPRINTOPTION(pdmPrivate) && bReversed) || (REVPRINTOPTION(pdmPrivate) && !bReversed)) { TERSE(("EMF turned on for reverse order\n")); SET_MFSPOOL_FLAG(pdmPrivate); } } else if (dwChangedItemID == COPIES_COLLATE_ITEM) { // // collate // if ((pdm->dmFields & DM_COLLATE) && (pdm->dmCollate == DMCOLLATE_TRUE) && !PRINTER_SUPPORTS_COLLATE(pci)) { TERSE(("EMF turned on for collate\n")); SET_MFSPOOL_FLAG(pdmPrivate); } } else { RIP(("unknown dwChangedItemID: %d\n", dwChangedItemID)); } } } else { // // driver EMF option has being changed // if (!ISSET_MFSPOOL_FLAG(pdmPrivate)) { BOOL bReversed = BGetPageOrderFlag(pci); // // drier EMF option is turned off, need to handle several EMF features // // booklet // if (NUPOPTION(pdmPrivate) == BOOKLET_UP) { TERSE(("EMF off, so BOOKLET_UP to ONE_UP\n")); NUPOPTION(pdmPrivate) = ONE_UP; } // // collate // if ((pdm->dmFields & DM_COLLATE) && (pdm->dmCollate == DMCOLLATE_TRUE) && !PRINTER_SUPPORTS_COLLATE(pci)) { TERSE(("EMF off, so collate off\n")); pdm->dmCollate = DMCOLLATE_FALSE; // // Update Collate feature option index // ChangeOptionsViaID(pci->pInfoHeader, pci->pCombinedOptions, GID_COLLATE, pdm); } // // reverse order printing // if ((!REVPRINTOPTION(pdmPrivate) && bReversed) || (REVPRINTOPTION(pdmPrivate) && !bReversed)) { TERSE(("EMF off, so reverse %d\n", bReversed)); REVPRINTOPTION(pdmPrivate) = bReversed; } } } } /*++ Routine Name: BUpdatePSF_RevPrintAndOutputOrder Routine Description: sync up settings between driver synthesized feature %PageOrder and PPD feature *OutputOrder to avoid spooler simulation Arguments: pci - pointer to driver's COMMONINFO structure dwChangedItemID - ID to indicate which item has been changed Return Value: TRUE if the sync up succeeds FALSE if there is no PPD feature "OutputOrder" or current setting for "OutputOrder" is invalid Last Error: None --*/ BOOL BUpdatePSF_RevPrintAndOutputOrder( IN PCOMMONINFO pci, IN DWORD dwChangedItemID ) { PUIINFO pUIInfo = pci->pUIInfo; PPPDDATA pPpdData; PFEATURE pFeature; pPpdData = GET_DRIVER_INFO_FROM_INFOHEADER(pci->pInfoHeader); ASSERT(pPpdData != NULL && pPpdData->dwSizeOfStruct == sizeof(PPDDATA)); // // refer to VSyncRevPrintAndOutputOrder // if (pPpdData && pPpdData->dwOutputOrderIndex != INVALID_FEATURE_INDEX && (pFeature = PGetIndexedFeature(pUIInfo, pPpdData->dwOutputOrderIndex))) { INT iSelection; POPTION pOption; PCSTR pstrOptionName; BOOL bReverse; // // "OutputOrder" feature is available. We only recognize the 2 standard options // "Normal" and "Reverse". // iSelection = pci->pCombinedOptions[pPpdData->dwOutputOrderIndex].ubCurOptIndex; if (iSelection < 2 && (pOption = PGetIndexedOption(pUIInfo, pFeature, iSelection)) && (pstrOptionName = OFFSET_TO_POINTER(pUIInfo->pubResourceData, pOption->loKeywordName))) { PPSDRVEXTRA pdmPrivate = pci->pdmPrivate; if (strcmp(pstrOptionName, "Reverse") == EQUAL_STRING) bReverse = TRUE; else bReverse = FALSE; if (dwChangedItemID == REVPRINT_ITEM) { // // reverse printing setting has just being changed. We should change // "OutputOrder" option if needed to match the requested output order. // if ((!REVPRINTOPTION(pdmPrivate) && bReverse) || (REVPRINTOPTION(pdmPrivate) && !bReverse)) { TERSE(("RevPrint change causes OutputOrder to be %d\n", 1 - iSelection)); pci->pCombinedOptions[pPpdData->dwOutputOrderIndex].ubCurOptIndex = (BYTE)(1 - iSelection); } } else { // // output order setting has just being changed. We should change reverse // printing option to match the request output order. // TERSE(("OutputOrder change causes RevPrint to be %d\n", bReverse)); REVPRINTOPTION(pdmPrivate) = bReverse; } // // sync between reverse print and output order succeeeded // return TRUE; } } // // sync between reverse print and output order failed // return FALSE; } /*++ Routine Name: VUpdatePSF_BookletAndDuplex Routine Description: sync up settings between driver synthesized feature %PagePerSheet and PPD feature *Duplex Arguments: pci - pointer to driver's COMMONINFO structure dwChangedItemID - ID to indicate which item has been changed Return Value: None Last Error: None --*/ VOID VUpdatePSF_BookletAndDuplex( IN PCOMMONINFO pci, IN DWORD dwChangedItemID ) { PUIINFO pUIInfo = pci->pUIInfo; PFEATURE pDuplexFeature; // // refer to VUpdateBookletOption // if (pDuplexFeature = GET_PREDEFINED_FEATURE(pUIInfo, GID_DUPLEX)) { PDUPLEX pDuplexOption; DWORD dwFeatureIndex, dwOptionIndex; PPSDRVEXTRA pdmPrivate = pci->pdmPrivate; dwFeatureIndex = GET_INDEX_FROM_FEATURE(pUIInfo, pDuplexFeature); dwOptionIndex = pci->pCombinedOptions[dwFeatureIndex].ubCurOptIndex; pDuplexOption = PGetIndexedOption(pUIInfo, pDuplexFeature, dwOptionIndex); if (pDuplexOption && pDuplexOption->dwDuplexID == DMDUP_SIMPLEX && NUPOPTION(pdmPrivate) == BOOKLET_UP) { ASSERT(((PUIDATA)pci)->bEMFSpooling); if (dwChangedItemID == NUP_ITEM) { DWORD cIndex; // // Booklet is enabled - turn duplex on // pDuplexOption = PGetIndexedOption(pUIInfo, pDuplexFeature, 0); for (cIndex = 0 ; cIndex < pDuplexFeature->Options.dwCount; cIndex++) { if (pDuplexOption->dwDuplexID != DMDUP_SIMPLEX) { TERSE(("Booklet change causes Duplex to be %d\n", cIndex)); pci->pCombinedOptions[dwFeatureIndex].ubCurOptIndex = (BYTE)cIndex; break; } pDuplexOption++; } } else { ASSERT(dwChangedItemID == DUPLEX_ITEM); // // Duplex is turned off, so disable booklet and set to 2 up. // TERSE(("Simplex change causes Booklet to be 2up\n")); NUPOPTION(pdmPrivate) = TWO_UP; } } } } /*++ Routine Name: HSetOptions Routine Description: set new driver settings for PPD features and driver synthesized features Arguments: poemuiobj - pointer to driver context object dwFlags - flags for the set operation pmszFeatureOptionBuf - MULTI_SZ ASCII string containing new settings' feature/option keyword pairs cbin - size in bytes of the pmszFeatureOptionBuf string pdwResult - pointer to the DWORD that will store the result of set operation Return Value: S_OK if the set operation succeeds E_INVALIDARG if input pmszFeatureOptionBuf is not in valid MULTI_SZ format, or flag for the set operation is not recognized E_FAIL if the set operation fails Last Error: None --*/ HRESULT HSetOptions( IN POEMUIOBJ poemuiobj, IN DWORD dwFlags, IN PCSTR pmszFeatureOptionBuf, IN DWORD cbIn, OUT PDWORD pdwResult ) { PCOMMONINFO pci = (PCOMMONINFO)poemuiobj; PDEVMODE pdm; PPSDRVEXTRA pdmPrivate; PUIINFO pUIInfo; PPPDDATA pPpdData; PCSTR pszFeature, pszOption; BOOL bPageSizeSet = FALSE, bPrinterSticky, bNoConflict; INT iMode; LAYOUT iOldLayout; // // do some validation on the input parameters // if (!BValidMultiSZString(pmszFeatureOptionBuf, cbIn, TRUE)) { ERR(("Set: invalid MULTI_SZ input param\n")); return E_INVALIDARG; } if (!(dwFlags & SETOPTIONS_FLAG_RESOLVE_CONFLICT) && !(dwFlags & SETOPTIONS_FLAG_KEEP_CONFLICT)) { ERR(("Set: invalid dwFlags %d\n", dwFlags)); return E_INVALIDARG; } pUIInfo = pci->pUIInfo; pPpdData = GET_DRIVER_INFO_FROM_INFOHEADER(pci->pInfoHeader); ASSERT(pPpdData != NULL && pPpdData->dwSizeOfStruct == sizeof(PPDDATA)); if (pPpdData == NULL) { return E_FAIL; } pdm = pci->pdm; bPrinterSticky = ((PUIDATA)pci)->iMode == MODE_PRINTER_STICKY ? TRUE : FALSE; if (!bPrinterSticky) { ASSERT(pdm); pdmPrivate = (PPSDRVEXTRA)GET_DRIVER_PRIVATE_DEVMODE(pdm); iOldLayout = NUPOPTION(pdmPrivate); // // First we need to propagate devmode settings (in case // plugin has changed it) into option array. // // devmode is only valid in non printer-sticky mode. Refer to comments // in HEnumConstrainedOptions(). // VFixOptionsArrayWithDevmode(pci); } // // Then set each feature specified by plugin. // pszFeature = pmszFeatureOptionBuf; while (*pszFeature) { DWORD cbFeatureKeySize, cbOptionKeySize; cbFeatureKeySize = strlen(pszFeature) + 1; pszOption = pszFeature + cbFeatureKeySize; cbOptionKeySize = strlen(pszOption) + 1; // // Feature or option setting string can't be empty. // if (cbFeatureKeySize == 1 || cbOptionKeySize == 1) { ERR(("Set: empty feature or option keyword\n")); goto next_feature; } if (*pszFeature == PSFEATURE_PREFIX) { PPSFEATURE_ENTRY pEntry, pMatchEntry; // // synthesized PS driver feature // pMatchEntry = NULL; pEntry = (PPSFEATURE_ENTRY)(&kPSFeatureTable[0]); while (pEntry->pszPSFeatureName) { if ((*pszFeature == *(pEntry->pszPSFeatureName)) && (strcmp(pszFeature, pEntry->pszPSFeatureName) == EQUAL_STRING)) { pMatchEntry = pEntry; break; } pEntry++; } // // See comments in HEnumConstrainedOptions for following stickiness mode check. // if (!pMatchEntry || (bPrinterSticky && !pMatchEntry->bPrinterSticky) || (!bPrinterSticky && pMatchEntry->bPrinterSticky)) { VERBOSE(("Set: invalid or mode-mismatched feature %s\n", pszFeature)); goto next_feature; } if (pMatchEntry->pfnPSProc) { BOOL bResult; bResult = (pMatchEntry->pfnPSProc)(pci->hPrinter, pUIInfo, pPpdData, pdm, pci->pPrinterData, pszFeature, pszOption, NULL, 0, NULL, PSFPROC_SETOPTION_MODE); if (bResult) { // // PS driver EMF features EMF, PageOrder, Nup need special postprocessing // to synchronize among EMF features (refer to cpcbDocumentPropertyCallback). // if ((*pszFeature == kstrPSFEMF[0]) && (strcmp(pszFeature, kstrPSFEMF) == EQUAL_STRING)) { ASSERT(!bPrinterSticky); VUpdatePSF_EMFFeatures(pci, METASPOOL_ITEM); } else if ((*pszFeature == kstrPSFPageOrder[0]) && (strcmp(pszFeature, kstrPSFPageOrder) == EQUAL_STRING)) { ASSERT(!bPrinterSticky); // // first try to sync between reverse print and output order feature // if (!BUpdatePSF_RevPrintAndOutputOrder(pci, REVPRINT_ITEM)) { // // if that failed, reverse print could force EMF on // VUpdatePSF_EMFFeatures(pci, REVPRINT_ITEM); } } else if ((*pszFeature == kstrPSFNup[0]) && (strcmp(pszFeature, kstrPSFNup) == EQUAL_STRING)) { ASSERT(!bPrinterSticky); if (NUPOPTION(pdmPrivate) == BOOKLET_UP) { if (!((PUIDATA)pci)->bEMFSpooling || !SUPPORTS_DUPLEX(pci)) { // // booklet is not supported if duplex is constrained by an installable // feature such as duplex unit not installed or spooler EMF is disabled // (refer to BPackItemEmfFeatures) // ERR(("Set: BOOKLET_UP ignored for %s\n", pszFeature)); NUPOPTION(pdmPrivate) = iOldLayout; } else { // // Booklet will force EMF on // VUpdatePSF_EMFFeatures(pci, NUP_ITEM); // // Booklet will also turn duplex on // VUpdatePSF_BookletAndDuplex(pci, NUP_ITEM); } } } } else { if (GetLastError() == ERROR_INVALID_PARAMETER) { ERR(("Set: %%-feature handler found invalid option %s for %s\n", pszOption, pszFeature)); } else { ERR(("Set: %%-feature handler failed on %s-%s: %d\n", pszFeature, pszOption, GetLastError())); } } } } else { PFEATURE pFeature; POPTION pOption; DWORD dwFeatureIndex, dwOptionIndex; POPTSELECT pOptionsArray = pci->pCombinedOptions; // // PPD *OpenUI feature // pFeature = PGetNamedFeature(pUIInfo, pszFeature, &dwFeatureIndex); // // See comments in HEnumConstrainedOptions for following stickiness mode check. // if (!pFeature || (bPrinterSticky && pFeature->dwFeatureType != FEATURETYPE_PRINTERPROPERTY) || (!bPrinterSticky && pFeature->dwFeatureType == FEATURETYPE_PRINTERPROPERTY)) { VERBOSE(("Set: invalid or mode-mismatched feature %s\n", pszFeature)); goto next_feature; } // // Skip GID_LEADINGEDGE, GID_USEHWMARGINS. They are not real PPD *OpenUI features. // Also skip GID_PAGEREGION, it's only set internally. We don't allow user or plugin // to set it. // if (pFeature->dwFeatureID == GID_PAGEREGION || pFeature->dwFeatureID == GID_LEADINGEDGE || pFeature->dwFeatureID == GID_USEHWMARGINS) { ERR(("Set: skip feature %s\n", pszFeature)); goto next_feature; } pOption = PGetNamedOption(pUIInfo, pFeature, pszOption, &dwOptionIndex); if (!pOption) { ERR(("Set: invalid input option %s for feature %s\n", pszOption, pszFeature)); goto next_feature; } // // update the option selection // pOptionsArray[dwFeatureIndex].ubCurOptIndex = (BYTE)dwOptionIndex; // // We don't support pick-many yet. // ASSERT(pOptionsArray[dwFeatureIndex].ubNext == NULL_OPTSELECT); // // some special postprocessing after the option setting is changed // if (pFeature->dwFeatureID == GID_PAGESIZE) { PPAGESIZE pPageSize = (PPAGESIZE)pOption; ASSERT(!bPrinterSticky); // // special handling of PS custom page size // // refer to VUnpackDocumentPropertiesItems case FORMNAME_ITEM in docprop.c // if (pPageSize->dwPaperSizeID == DMPAPER_CUSTOMSIZE) { pdm->dmFields &= ~(DM_PAPERLENGTH|DM_PAPERWIDTH|DM_FORMNAME); pdm->dmFields |= DM_PAPERSIZE; pdm->dmPaperSize = DMPAPER_CUSTOMSIZE; LOAD_STRING_PAGESIZE_NAME(pci, pPageSize, pdm->dmFormName, CCHFORMNAME); } bPageSizeSet = TRUE; } else if (pFeature->dwFeatureID == GID_OUTPUTBIN) { ASSERT(!bPrinterSticky); // // output bin change could force EMF on // VUpdatePSF_EMFFeatures(pci, REVPRINT_ITEM); } else if (pPpdData->dwOutputOrderIndex != INVALID_FEATURE_INDEX && dwFeatureIndex == pPpdData->dwOutputOrderIndex) { ASSERT(!bPrinterSticky); // // output order change causes reverse print change // if (!BUpdatePSF_RevPrintAndOutputOrder(pci, UNKNOWN_ITEM)) { ERR(("OutputOrder change syncs RevPrint failed\n")); } } } next_feature: pszFeature += cbFeatureKeySize + cbOptionKeySize; } iMode = bPrinterSticky ? MODE_PRINTER_STICKY : MODE_DOCANDPRINTER_STICKY; if (dwFlags & SETOPTIONS_FLAG_KEEP_CONFLICT) { iMode |= DONT_RESOLVE_CONFLICT; } // // If we're inside DrvDocumentPropertySheets, // we'll call the parser to resolve conflicts between // all printer features. Since all printer-sticky // features have higher priority than all doc-sticky // features, only doc-sticky option selections should // be affected. // bNoConflict = ResolveUIConflicts(pci->pRawData, pci->pCombinedOptions, MAX_COMBINED_OPTIONS, iMode); if (pdwResult) { if (dwFlags & SETOPTIONS_FLAG_RESOLVE_CONFLICT) { *pdwResult = bNoConflict ? SETOPTIONS_RESULT_NO_CONFLICT : SETOPTIONS_RESULT_CONFLICT_RESOLVED; } else { *pdwResult = bNoConflict ? SETOPTIONS_RESULT_NO_CONFLICT : SETOPTIONS_RESULT_CONFLICT_REMAINED; } } if (!bPrinterSticky) { // // Lastly we need to transfer options array settings back // to devmode so they are in sync. // VOptionsToDevmodeFields(pci, bPageSizeSet); // // A few more postprocessing here // // collate could force EMF on // VUpdatePSF_EMFFeatures(pci, COPIES_COLLATE_ITEM); // // simplex could change booklet setting // VUpdatePSF_BookletAndDuplex(pci, DUPLEX_ITEM); } return S_OK; }