windows-nt/Source/XPSP1/NT/printscan/print/spooler/prtprocs/winprint/emf.c
2020-09-26 16:20:57 +08:00

2543 lines
88 KiB
C

/*++
Copyright (c) 1990-2000 Microsoft Corporation
All rights reserved
// @@BEGIN_DDKSPLIT
Module Name:
windows\spooler\prtprocs\winprint\emf.c
// @@END_DDKSPLIT
Abstract:
Routines to facilitate printing of EMF jobs.
// @@BEGIN_DDKSPLIT
Author:
Gerrit van Wingerden (gerritv) 5-12-1995
Revision History:
Ramanathan N. Venkatapathy (ramanv) 5-15-1997
Alvin Scholten (alvins) 3-31-00
// @@END_DDKSPLIT
--*/
#include "local.h"
#include "stddef.h"
#include <windef.h>
// @@BEGIN_DDKSPLIT
#include "wingdip.h"
/*
// @@END_DDKSPLIT
#include <winppi.h>
// @@BEGIN_DDKSPLIT
*/
// @@END_DDKSPLIT
#define EMF_DUP_NONE 0
#define EMF_DUP_VERT 1
#define EMF_DUP_HORZ 2
#define EMF_DEGREE_90 0x0001
#define EMF_DEGREE_270 0x0002
#define EMF_DEGREE_SWAP 0x8000
// PAGE_NUMBER is used to save a list of the page numbers to start new sides while
// Reverse Printing.
typedef struct _PAGE_NUMBER {
struct _PAGE_NUMBER *pNext;
DWORD dwPageNumber;
} PAGE_NUMBER, *PPAGE_NUMBER;
typedef struct _UpdateRect {
double top;
double bottom;
double left;
double right;
} UpdateRect;
// The update factors for the different nup options. These factors when multiplied
// with the horizontal and vertical resolutions give the coordinates for the rectangle
// where the EMF page is to be played.
UpdateRect URect21[] = {{0, 0.5, 0, 1},
{0.5, 1, 0, 1}};
UpdateRect URect21R[] = {{0.5, 1, 0, 1},
{0, 0.5, 0, 1}};
UpdateRect URect22[] = {{0, 1, 0, 0.5},
{0, 1, 0.5, 1}};
UpdateRect URect4[] = {{0, 0.5, 0, 0.5},
{0, 0.5, 0.5, 1},
{0.5, 1, 0, 0.5},
{0.5, 1, 0.5, 1}};
UpdateRect URect61[] = {{0, 1.0/3.0, 0, 0.5},
{0, 1.0/3.0, 0.5, 1},
{1.0/3.0, 2.0/3.0, 0, 0.5},
{1.0/3.0, 2.0/3.0, 0.5, 1},
{2.0/3.0, 1, 0, 0.5},
{2.0/3.0, 1, 0.5, 1}};
UpdateRect URect61R[] = {{2.0/3.0, 1, 0, 0.5},
{1.0/3.0, 2.0/3.0, 0, 0.5},
{0, 1.0/3.0, 0, 0.5},
{2.0/3.0, 1, 0.5, 1},
{1.0/3.0, 2.0/3.0, 0.5, 1},
{0, 1.0/3.0, 0.5, 1}};
UpdateRect URect62[] = {{0, 0.5, 0, 1.0/3.0},
{0, 0.5, 1.0/3.0, 2.0/3.0},
{0, 0.5, 2.0/3.0, 1},
{0.5, 1, 0, 1.0/3.0},
{0.5, 1, 1.0/3.0, 2.0/3.0},
{0.5, 1, 2.0/3.0, 1}};
UpdateRect URect62R[] = {{0.5, 1, 0, 1.0/3.0},
{0, 0.5, 0, 1.0/3.0},
{0.5, 1, 1.0/3.0, 2.0/3.0},
{0, 0.5, 1.0/3.0, 2.0/3.0},
{0.5, 1, 2.0/3.0, 1},
{0, 0.5, 2.0/3.0, 1}};
UpdateRect URect9[] = {{0, 1.0/3.0, 0, 1.0/3.0},
{0, 1.0/3.0, 1.0/3.0, 2.0/3.0},
{0, 1.0/3.0, 2.0/3.0, 1},
{1.0/3.0, 2.0/3.0, 0, 1.0/3.0},
{1.0/3.0, 2.0/3.0, 1.0/3.0, 2.0/3.0},
{1.0/3.0, 2.0/3.0, 2.0/3.0, 1},
{2.0/3.0, 1, 0, 1.0/3.0},
{2.0/3.0, 1, 1.0/3.0, 2.0/3.0},
{2.0/3.0, 1, 2.0/3.0, 1}};
UpdateRect URect16[] = {{0, 0.25, 0, 0.25},
{0, 0.25, 0.25, 0.5},
{0, 0.25, 0.5, 0.75},
{0, 0.25, 0.75, 1},
{0.25, 0.5, 0, 0.25},
{0.25, 0.5, 0.25, 0.5},
{0.25, 0.5, 0.5, 0.75},
{0.25, 0.5, 0.75, 1},
{0.5, 0.75, 0, 0.25},
{0.5, 0.75, 0.25, 0.5},
{0.5, 0.75, 0.5, 0.75},
{0.5, 0.75, 0.75, 1},
{0.75, 1, 0, 0.25},
{0.75, 1, 0.25, 0.5},
{0.75, 1, 0.5, 0.75},
{0.75, 1, 0.75, 1}};
BOOL
ValidNumberForNUp(
DWORD dwPages)
/*++
Function Description: Checks if the number of pages printed on a single side is Valid.
Parameters: dwPages - Number of pages printed on a single side
Return Values: TRUE if (dwPages = 1|2|4|6|9|16)
FALSE otherwise.
--*/
{
return ((dwPages == 1) || (dwPages == 2) || (dwPages == 4) ||
(dwPages == 6) || (dwPages == 9) || (dwPages == 16));
}
VOID
GetPageCoordinatesForNUp(
HDC hPrinterDC,
RECT *rectDocument,
RECT *rectBorder,
DWORD dwTotalNumberOfPages,
UINT uCurrentPageNumber,
DWORD dwNupBorderFlags,
LPBOOL pbRotate
)
/*++
Function Description: GetPageCoordinatesForNUp computes the rectangle on the Page where the
EMF file is to be played. It also determines if the picture is to
rotated.
Parameters: hPrinterDC - Printer Device Context
*rectDocument - pointer to RECT where the coordinates to play the
page will be returned.
*rectBorder - pointer to RECT where the page borders are to drawn.
dwTotalNumberOfPages - Total number of pages on 1 side.
uCurrentPageNumber - 1 based page number on the side.
dwNupBorderFlags - flags to draw border along logical pages.
pbRotate - pointer to BOOL which indicates if the picture must be
rotated.
Return Values: NONE.
--*/
{
UpdateRect *URect;
LONG lXPrintPage,lYPrintPage,lXPhyPage,lYPhyPage,lXFrame,lYFrame,ltemp,ldX,ldY;
LONG lXNewPhyPage,lYNewPhyPage,lXOffset,lYOffset,lNumRowCol,lRowIndex,lColIndex;
double dXleft,dXright,dYtop,dYbottom;
LONG xResolution = GetDeviceCaps(hPrinterDC, LOGPIXELSX);
LONG yResolution = GetDeviceCaps(hPrinterDC, LOGPIXELSY);
// Get the 0-based array index for the current page
uCurrentPageNumber = uCurrentPageNumber - 1;
if (dwTotalNumberOfPages==1 || xResolution==yResolution)
{
xResolution = yResolution = 1;
}
rectDocument->top = rectDocument->bottom = lYPrintPage = (GetDeviceCaps(hPrinterDC, DESKTOPVERTRES)-1) * xResolution;
rectDocument->left = rectDocument->right = lXPrintPage = (GetDeviceCaps(hPrinterDC, DESKTOPHORZRES)-1) * yResolution;
lXPhyPage = GetDeviceCaps(hPrinterDC, PHYSICALWIDTH) * yResolution;
lYPhyPage = GetDeviceCaps(hPrinterDC, PHYSICALHEIGHT) * xResolution;
*pbRotate = FALSE;
// Select the array containing the update factors
switch (dwTotalNumberOfPages) {
case 1: rectDocument->top = rectDocument->left = 0;
rectDocument->right += 1;
rectDocument->bottom += 1;
return;
case 2: if (lXPrintPage > lYPrintPage) { // cut vertically
URect = URect22;
lXFrame = (LONG) (lXPrintPage / 2.0);
lYFrame = lYPrintPage;
} else { // cut horizontally
URect = URect21;
lYFrame = (LONG) (lYPrintPage / 2.0);
lXFrame = lXPrintPage;
}
break;
case 4: URect = URect4;
lXFrame = (LONG) (lXPrintPage / 2.0);
lYFrame = (LONG) (lYPrintPage / 2.0);
break;
case 6: if (lXPrintPage > lYPrintPage) { // cut vertically twice
URect = URect62;
lXFrame = (LONG) (lXPrintPage / 3.0);
lYFrame = (LONG) (lYPrintPage / 2.0);
} else { // cut horizontally twice
URect = URect61;
lYFrame = (LONG) (lYPrintPage / 3.0);
lXFrame = (LONG) (lXPrintPage / 2.0);
}
break;
case 9: URect = URect9;
lXFrame = (LONG) (lXPrintPage / 3.0);
lYFrame = (LONG) (lYPrintPage / 3.0);
break;
case 16: URect = URect16;
lXFrame = (LONG) (lXPrintPage / 4.0);
lYFrame = (LONG) (lYPrintPage / 4.0);
break;
default: // Should Not Occur.
return;
}
// Set the flag if the picture has to be rotated
*pbRotate = !((lXPhyPage >= lYPhyPage) && (lXFrame >= lYFrame)) &&
!((lXPhyPage < lYPhyPage) && (lXFrame < lYFrame));
// If the picture is to be rotated, modify the rectangle selected.
if ((dwTotalNumberOfPages == 2) || (dwTotalNumberOfPages == 6)) {
if (*pbRotate) {
switch (dwTotalNumberOfPages) {
case 2: if (lXPrintPage <= lYPrintPage) {
URect = URect21R;
} // URect22 = URect22R
break;
case 6: if (lXPrintPage <= lYPrintPage) {
URect = URect61R;
} else {
URect = URect62R;
}
break;
}
}
} else {
if (*pbRotate) {
// get the number of rows/columns. switch is faster than sqrt.
switch (dwTotalNumberOfPages) {
case 4: lNumRowCol = 2;
break;
case 9: lNumRowCol = 3;
break;
case 16: lNumRowCol = 4;
break;
}
lRowIndex = (LONG) (uCurrentPageNumber / lNumRowCol);
lColIndex = (LONG) (uCurrentPageNumber % lNumRowCol);
uCurrentPageNumber = (lNumRowCol - 1 - lColIndex) * lNumRowCol + lRowIndex;
}
}
// Update the Page Coordinates.
rectDocument->top = (LONG) (rectDocument->top * URect[uCurrentPageNumber].top);
rectDocument->bottom = (LONG) (rectDocument->bottom * URect[uCurrentPageNumber].bottom);
rectDocument->left = (LONG) (rectDocument->left * URect[uCurrentPageNumber].left);
rectDocument->right = (LONG) (rectDocument->right * URect[uCurrentPageNumber].right);
// If the page border has to drawn, return the corresponding coordinates in rectBorder.
if (dwNupBorderFlags == BORDER_PRINT) {
rectBorder->top = rectDocument->top/xResolution;
rectBorder->bottom = rectDocument->bottom/xResolution - 1;
rectBorder->left = rectDocument->left/yResolution;
rectBorder->right = rectDocument->right/yResolution - 1;
}
if (*pbRotate) {
ltemp = lXFrame; lXFrame = lYFrame; lYFrame = ltemp;
}
// Get the new size of the rectangle to keep the X/Y ratio constant.
if ( ((LONG) (lYFrame*((lXPhyPage*1.0)/lYPhyPage))) >= lXFrame) {
ldX = 0;
ldY = lYFrame - ((LONG) (lXFrame*((lYPhyPage*1.0)/lXPhyPage)));
} else {
ldY = 0;
ldX = lXFrame - ((LONG) (lYFrame*((lXPhyPage*1.0)/lYPhyPage)));
}
// Adjust the position of the rectangle.
if (*pbRotate) {
if (ldX) {
rectDocument->bottom -= (LONG) (ldX / 2.0);
rectDocument->top += (LONG) (ldX / 2.0);
} else {
rectDocument->right -= (LONG) (ldY / 2.0);
rectDocument->left += (LONG) (ldY / 2.0);
}
} else {
if (ldX) {
rectDocument->left += (LONG) (ldX / 2.0);
rectDocument->right -= (LONG) (ldX / 2.0);
} else {
rectDocument->top += (LONG) (ldY / 2.0);
rectDocument->bottom -= (LONG) (ldY / 2.0);
}
}
// Adjust to get the Printable Area on the rectangle
lXOffset = GetDeviceCaps(hPrinterDC, PHYSICALOFFSETX) * yResolution;
lYOffset = GetDeviceCaps(hPrinterDC, PHYSICALOFFSETY) * xResolution;
dXleft = ( lXOffset * 1.0) / lXPhyPage;
dYtop = ( lYOffset * 1.0) / lYPhyPage;
dXright = ((lXPhyPage - (lXOffset + lXPrintPage)) * 1.0) / lXPhyPage;
dYbottom = ((lYPhyPage - (lYOffset + lYPrintPage)) * 1.0) / lYPhyPage;
lXNewPhyPage = rectDocument->right - rectDocument->left;
lYNewPhyPage = rectDocument->bottom - rectDocument->top;
if (*pbRotate) {
ltemp = lXNewPhyPage; lXNewPhyPage = lYNewPhyPage; lYNewPhyPage = ltemp;
rectDocument->left += (LONG) (dYtop * lYNewPhyPage);
rectDocument->right -= (LONG) (dYbottom * lYNewPhyPage);
rectDocument->top += (LONG) (dXright * lXNewPhyPage);
rectDocument->bottom -= (LONG) (dXleft * lXNewPhyPage);
} else {
rectDocument->left += (LONG) (dXleft * lXNewPhyPage);
rectDocument->right -= (LONG) (dXright * lXNewPhyPage);
rectDocument->top += (LONG) (dYtop * lYNewPhyPage);
rectDocument->bottom -= (LONG) (dYbottom * lYNewPhyPage);
}
if (xResolution!=yResolution)
{
rectDocument->left = rectDocument->left / yResolution;
rectDocument->right = rectDocument->right / yResolution;
rectDocument->top = rectDocument->top / xResolution;
rectDocument->bottom = rectDocument->bottom / xResolution;
}
return;
}
BOOL
PlayEMFPage(
HANDLE hSpoolHandle,
HDC hPrinterDC,
HANDLE hEMF,
DWORD dwNumberOfPagesPerSide,
DWORD dwPageNumber,
DWORD dwPageIndex,
DWORD dwNupBorderFlags,
DWORD dwAngle)
/*++
Function Description: PlayEMFPage plays the EMF in the appropriate rectangle. It performs
the required scaling, rotation and translation.
Parameters: hSpoolHandle -- handle the spool file handle
hPrinterDC -- handle to the printer device context
hEMF -- handle to the contents of the page in the spool file
dwNumberOfPagesPerSide -- number of pages to be printed per side
dwPageNumber -- page number in the document
dwPageIndex -- page number in the side. (1 based)
dwNupBorderFlags -- border printing options for nup
dwAngle -- angle for rotation (if neccesary)
Return Values: TRUE if successful
FALSE otherwise
--*/
{
BOOL bReturn = FALSE, bRotate;
RECT rectDocument, rectPrinter, rectBorder = {-1, -1, -1, -1};
RECT *prectClip = NULL;
XFORM TransXForm = {1, 0, 0, 1, 0, 0}, RotateXForm = {0, -1, 1, 0, 0, 0};
HPEN hPen;
HANDLE hFormEMF;
DWORD dwPageType,dwFormPage;
// Compute the rectangle for one page.
GetPageCoordinatesForNUp(hPrinterDC,
&rectDocument,
&rectBorder,
dwNumberOfPagesPerSide,
dwPageIndex,
dwNupBorderFlags,
&bRotate);
// If swap flag is set, reverse rotate flag
//
if (dwAngle & EMF_DEGREE_SWAP)
bRotate = !bRotate;
if (dwAngle & EMF_DEGREE_270) {
RotateXForm.eM12 = 1;
RotateXForm.eM21 = -1;
} // EMF_DEGREE_90 case is the initialization
if (bRotate) {
rectPrinter.top = 0;
rectPrinter.bottom = rectDocument.right - rectDocument.left;
rectPrinter.left = 0;
rectPrinter.right = rectDocument.bottom - rectDocument.top;
// Set the translation matrix
if (dwAngle & EMF_DEGREE_270) {
TransXForm.eDx = (float) rectDocument.right;
TransXForm.eDy = (float) rectDocument.top;
} else {
// EMF_DEGREE_90
TransXForm.eDx = (float) rectDocument.left;
TransXForm.eDy = (float) rectDocument.bottom;
}
// Set the transformation matrix
if (!SetWorldTransform(hPrinterDC, &RotateXForm) ||
!ModifyWorldTransform(hPrinterDC, &TransXForm, MWT_RIGHTMULTIPLY)) {
ODS(("Setting transformation matrix failed\n"));
goto CleanUp;
}
}
// Add clipping for Nup
if (dwNumberOfPagesPerSide != 1) {
prectClip = &rectDocument;
}
// Print the page.
if (bRotate) {
GdiPlayPageEMF(hSpoolHandle, hEMF, &rectPrinter, &rectBorder, prectClip);
} else {
GdiPlayPageEMF(hSpoolHandle, hEMF, &rectDocument, &rectBorder, prectClip);
}
bReturn = TRUE;
CleanUp:
if (!ModifyWorldTransform(hPrinterDC, NULL, MWT_IDENTITY)) {
ODS(("Setting Identity Transformation failed\n"));
bReturn = FALSE;
}
return bReturn;
}
BOOL
SetDrvCopies(
HDC hPrinterDC,
LPDEVMODEW pDevmode,
DWORD dwNumberOfCopies)
/*++
Function Description: SetDrvCopies sets the dmCopies field in pDevmode and resets
hPrinterDC with this devmode
Parameters: hPrinterDC -- handle to the printer device context
pDevmode -- pointer to devmode
dwNumberOfCopies -- value for dmCopies
Return Values: TRUE if successful
FALSE otherwise
--*/
{
BOOL bReturn;
DWORD dmFields;
if ((pDevmode->dmFields & DM_COPIES) &&
(pDevmode->dmCopies == (short) dwNumberOfCopies)) {
return TRUE;
}
// Save the old fields structure
dmFields = pDevmode->dmFields;
pDevmode->dmFields |= DM_COPIES;
pDevmode->dmCopies = (short) dwNumberOfCopies;
if (!ResetDC(hPrinterDC, pDevmode)) {
bReturn = FALSE;
} else {
bReturn = TRUE;
}
// Restore the fields structure
pDevmode->dmFields = dmFields;
if (!SetGraphicsMode(hPrinterDC,GM_ADVANCED)) {
ODS(("Setting graphics mode failed\n"));
bReturn = FALSE;
}
return bReturn;
}
BOOL
DifferentDevmodes(
LPDEVMODE pDevmode1,
LPDEVMODE pDevmode2
)
/*++
Function Description: Compares the devmodes for differences other than dmTTOption
Parameters: pDevmode1 - devmode 1
pDevmode2 - devmode 2
Return Values: TRUE if different ; FALSE otherwise
--*/
{
DWORD dwSize1, dwSize2, dwTTOffset, dwSpecOffset, dwLogOffset;
// Same pointers are the same devmode
if (pDevmode1 == pDevmode2) {
return FALSE;
}
// Check for Null devmodes
if (!pDevmode1 || !pDevmode2) {
return TRUE;
}
dwSize1 = pDevmode1->dmSize + pDevmode1->dmDriverExtra;
dwSize2 = pDevmode2->dmSize + pDevmode2->dmDriverExtra;
// Compare devmode sizes
if (dwSize1 != dwSize2) {
return TRUE;
}
dwTTOffset = FIELD_OFFSET(DEVMODE, dmTTOption);
dwSpecOffset = FIELD_OFFSET(DEVMODE, dmSpecVersion);
dwLogOffset = FIELD_OFFSET(DEVMODE, dmLogPixels);
if (wcscmp(pDevmode1->dmDeviceName,
pDevmode2->dmDeviceName)) {
// device names are different
return TRUE;
}
if (dwTTOffset < dwSpecOffset ||
dwSize1 < dwLogOffset) {
// incorrent devmode offsets
return TRUE;
}
if (memcmp((LPBYTE) pDevmode1 + dwSpecOffset,
(LPBYTE) pDevmode2 + dwSpecOffset,
dwTTOffset - dwSpecOffset)) {
// Front half is different
return TRUE;
}
// Ignore the dmTTOption setting.
if ((pDevmode1->dmCollate != pDevmode2->dmCollate) ||
wcscmp(pDevmode1->dmFormName, pDevmode2->dmFormName)) {
// form name or collate option is different
return TRUE;
}
if (memcmp((LPBYTE) pDevmode1 + dwLogOffset,
(LPBYTE) pDevmode2 + dwLogOffset,
dwSize1 - dwLogOffset)) {
// Back half is different
return TRUE;
}
return FALSE;
}
BOOL
ResetDCForNewDevmode(
HANDLE hSpoolHandle,
HDC hPrinterDC,
DWORD dwPageNumber,
BOOL bInsidePage,
DWORD dwOptimization,
LPBOOL pbNewDevmode,
LPDEVMODE pDevmode,
LPDEVMODE *pCurrentDevmode
)
/*++
Function Description: Determines if the devmode for the page is different from the
current devmode for the printer dc and resets the dc if necessary.
The parameters allow dmTTOption to be ignored in devmode comparison.
Parameters: hSpoolHandle - spool file handle
hPrinterDC - printer dc
dwPageNumber - page number before which we search for the devmode
bInsidePage - flag to ignore changes in TT options and call EndPage
before ResetDC
dwOptimization - optimization flags
pbNewDevmode - pointer to flag to indicate if ResetDC was called
pDevmode - devmode containing changed resolution settings
Return Values: TRUE if successful; FALSE otherwise
--*/
{
BOOL bReturn = FALSE;
LPDEVMODE pLastDM, pCurrDM;
// Initialize OUT parameters
*pbNewDevmode = FALSE;
// Get the devmode just before the page
if (!GdiGetDevmodeForPage(hSpoolHandle,
dwPageNumber,
&pCurrDM,
&pLastDM)) {
ODS(("GdiGetDevmodeForPage failed\n"));
return bReturn;
}
// Save pointer to current devmode
if (pCurrentDevmode)
*pCurrentDevmode = pCurrDM;
// Check if the devmodes are different
if (pLastDM != pCurrDM) {
// If the pointers are different the devmodes are always different
if (!bInsidePage ||
DifferentDevmodes(pLastDM, pCurrDM)) {
*pbNewDevmode = TRUE;
}
}
// Call ResetDC on the hPrinterDC if necessary
if (*pbNewDevmode) {
if (bInsidePage &&
!GdiEndPageEMF(hSpoolHandle, dwOptimization)) {
ODS(("EndPage failed\n"));
return bReturn;
}
if (pCurrDM) {
pCurrDM->dmPrintQuality = pDevmode->dmPrintQuality;
pCurrDM->dmYResolution = pDevmode->dmYResolution;
pCurrDM->dmCopies = pDevmode->dmCopies;
pCurrDM->dmCollate = pDevmode->dmCollate;
}
// Ignore the return values of ResetDC and SetGraphicsMode
GdiResetDCEMF(hSpoolHandle, pCurrDM);
SetGraphicsMode(hPrinterDC, GM_ADVANCED);
}
bReturn = TRUE;
return bReturn;
}
DWORD
PrintOneSideForwardEMF(
HANDLE hSpoolHandle,
HDC hPrinterDC,
DWORD dwNumberOfPagesPerSide,
DWORD dwDrvNumberOfPagesPerSide,
DWORD dwNupBorderFlags,
BOOL bDuplex,
DWORD dwOptimization,
DWORD dwPageNumber,
DWORD dwJobNumberOfCopies,
LPBOOL pbComplete,
LPDEVMODE pDevmode)
/*++
Function Description: PrintOneSideForwardEMF plays the next physical page in the same order
as the spool file.
Parameters: hSpoolHandle -- handle the spool file handle
hPrinterDC -- handle to the printer device context
dwNumberOfPagesPerSide -- number of pages to be printed per side by the print processor
dwDrvNumberOfPagesPerSide -- number of pages the driver will print per side
dwNupBorderFlags -- border printing options for nup
bDuplex -- flag to indicate duplex printing
dwOptimization -- optimization flags
dwPageNumber -- pointer to the starting page number
pbComplete -- pointer to the flag to indicate completion
pDevmode -- devmode with resolution settings
Return Values: Last Page Number if successful
0 on job completion (pbReturn set to TRUE) and
on failure (pbReturn remains FALSE)
--*/
{
DWORD dwPageIndex, dwPageType;
DWORD dwReturn = 0;
LPDEVMODEW pCurrDM;
HANDLE hEMF = NULL;
DWORD dwSides;
BOOL bNewDevmode;
DWORD cPagesToPlay;
DWORD dwAngle;
INT dmOrientation = pDevmode->dmOrientation;
// set the number of sides on this page;
dwSides = bDuplex ? 2 : 1;
*pbComplete = FALSE;
for ( ; dwSides && !*pbComplete ; --dwSides) {
// loop for a single side
for (dwPageIndex = 1;
dwPageIndex <= dwNumberOfPagesPerSide;
++dwPageIndex, ++dwPageNumber) {
if (!(hEMF = GdiGetPageHandle(hSpoolHandle,
dwPageNumber,
&dwPageType))) {
if (GetLastError() == ERROR_NO_MORE_ITEMS) {
// End of the print job
*pbComplete = TRUE;
break;
}
ODS(("GdiGetPageHandle failed\nPrinter %ws\n", pDevmode->dmDeviceName));
goto CleanUp;
}
dwAngle = EMF_DEGREE_90;
if (dwPageIndex == 1)
{
// Process new devmodes in the spool file that appear before this page
if (!ResetDCForNewDevmode(hSpoolHandle,
hPrinterDC,
dwPageNumber,
(dwPageIndex != 1),
dwOptimization,
&bNewDevmode,
pDevmode,
&pCurrDM)) {
goto CleanUp;
}
if (pCurrDM)
dmOrientation = pCurrDM->dmOrientation;
}
// in case of orientation switch we need to keep track of what
// we started with and what it is now
else if (dwNumberOfPagesPerSide > 1)
{
if (GdiGetDevmodeForPage(hSpoolHandle,
dwPageNumber,
&pCurrDM,
NULL))
{
if (pCurrDM && pCurrDM->dmOrientation != dmOrientation)
{
dwAngle = EMF_DEGREE_SWAP | EMF_DEGREE_90;
}
}
}
// Call StartPage for each new page
if ((dwPageIndex == 1) &&
!GdiStartPageEMF(hSpoolHandle)) {
ODS(("StartPage failed\nPrinter %ws\n", pDevmode->dmDeviceName));
goto CleanUp;
}
if (!PlayEMFPage(hSpoolHandle,
hPrinterDC,
hEMF,
dwNumberOfPagesPerSide,
dwPageNumber,
dwPageIndex,
dwNupBorderFlags,
dwAngle)) {
ODS(("PlayEMFPage failed\nPrinter %ws\n", pDevmode->dmDeviceName));
goto CleanUp;
}
}
//
// Explaination of the scinario set for the conditions on
// dwPageIndex1 , pbComplete and bDuplex.
// N.B. we are naming them cond.1 and cond.2
// dwPageIndex!=1 pbComplete bDuplex Condition
// 0 0 0 None
// 0 0 1 None
// 0 1 0 None
// 0 1 1 Cond2 on Second Side i.e. dwsides==1
// 1 0 0 Cond1
// 1 0 1 Cond1
// 1 1 0 Cond1
// 1 1 1 Cond1 & Cond2 on First Side i.e. dwsides==2
//
// cond.1
if (dwPageIndex != 1) {
// Call EndPage if we played any pages
if (!GdiEndPageEMF(hSpoolHandle, dwOptimization)) {
ODS(("EndPage failed\n"));
*pbComplete = FALSE;
goto CleanUp;
}
}
// cond.2
// play empty page on the back of duplex
if (*pbComplete && bDuplex && dwDrvNumberOfPagesPerSide==1) {
ODS(("PCL or PS with no N-up\n"));
//
// Checking dwsides against 2 or 1.
// depends on whether it is n-up or not.
//
if (((dwPageIndex!=1)?(dwSides==2):(dwSides==1))) {
if (!GdiStartPageEMF(hSpoolHandle) ||
!GdiEndPageEMF(hSpoolHandle, dwOptimization)) {
ODS(("EndPage failed\n"));
*pbComplete = FALSE;
goto CleanUp;
}
}
}
}
if (*pbComplete &&
dwNumberOfPagesPerSide==1 &&
dwDrvNumberOfPagesPerSide!=1 &&
dwJobNumberOfCopies!=1)
{
cPagesToPlay = dwDrvNumberOfPagesPerSide * (bDuplex ? 2 : 1);
if ((dwPageNumber-1) % cPagesToPlay)
{
//
// Number of pages played on last physical page
//
cPagesToPlay = cPagesToPlay - ((dwPageNumber-1) % cPagesToPlay);
ODS(("\nPS with N-up!\nMust fill in %u pages\n", cPagesToPlay));
for (;cPagesToPlay;cPagesToPlay--)
{
if (!GdiStartPageEMF(hSpoolHandle) || !GdiEndPageEMF(hSpoolHandle, dwOptimization))
{
ODS(("EndPage failed\n"));
goto CleanUp;
}
}
}
}
if (!(*pbComplete)) dwReturn = dwPageNumber;
CleanUp:
return dwReturn;
}
BOOL
PrintForwardEMF(
HANDLE hSpoolHandle,
HDC hPrinterDC,
DWORD dwNumberOfPagesPerSide,
DWORD dwDrvNumberOfPagesPerSide,
DWORD dwNupBorderFlags,
DWORD dwJobNumberOfCopies,
DWORD dwDrvNumberOfCopies,
BOOL bCollate,
BOOL bDuplex,
DWORD dwOptimization,
LPDEVMODEW pDevmode,
PPRINTPROCESSORDATA pData)
/*++
Function Description: PrintForwardEMF plays the EMF files in the order in which they
were spooled.
Parameters: hSpoolHandle -- handle the spool file handle
hPrinterDC -- handle to the printer device context
dwNumberOfPagesPerSide -- number of pages to be printed per side by the print processor
dwDrvNumberOfPagesPerSide -- number of pages the driver will print per side
dwNupBorderFlags -- border printing options for nup
dwJobNumberOfCopies -- number of copies of the job to be printed
dwDrvNumberOfCopies -- number of copies that the driver can print
bCollate -- flag for collating the copies
bDuplex -- flag for duplex printing
dwOptimization -- optimization flags
pDevmode -- pointer to devmode for changing the copy count
pData -- needed for status and the handle of the event: pause, resume etc.
Return Values: TRUE if successful
FALSE otherwise
--*/
{
DWORD dwLastPageNumber = 1,dwPageNumber,dwPageIndex,dwRemainingCopies;
BOOL bReturn = FALSE;
// Keep printing as long as the spool file contains EMF handles.
while (dwLastPageNumber) {
//
// If the print processor is paused, wait for it to be resumed
//
if (pData->fsStatus & PRINTPROCESSOR_PAUSED) {
WaitForSingleObject(pData->semPaused, INFINITE);
}
dwPageNumber = dwLastPageNumber;
if (bCollate) {
dwLastPageNumber = PrintOneSideForwardEMF(hSpoolHandle,
hPrinterDC,
dwNumberOfPagesPerSide,
dwDrvNumberOfPagesPerSide,
dwNupBorderFlags,
bDuplex,
dwOptimization,
dwPageNumber,
dwJobNumberOfCopies,
&bReturn,
pDevmode);
} else {
dwRemainingCopies = dwJobNumberOfCopies;
while (dwRemainingCopies) {
if (dwRemainingCopies <= dwDrvNumberOfCopies) {
SetDrvCopies(hPrinterDC, pDevmode, dwRemainingCopies);
dwRemainingCopies = 0;
} else {
SetDrvCopies(hPrinterDC, pDevmode, dwDrvNumberOfCopies);
dwRemainingCopies -= dwDrvNumberOfCopies;
}
if (!(dwLastPageNumber = PrintOneSideForwardEMF(hSpoolHandle,
hPrinterDC,
dwNumberOfPagesPerSide,
dwDrvNumberOfPagesPerSide,
dwNupBorderFlags,
bDuplex,
dwOptimization,
dwPageNumber,
dwJobNumberOfCopies,
&bReturn,
pDevmode)) &&
!bReturn) {
goto CleanUp;
}
}
}
}
CleanUp:
return bReturn;
}
BOOL
PrintOneSideReverseForDriverEMF(
HANDLE hSpoolHandle,
HDC hPrinterDC,
DWORD dwDrvNumberOfPagesPerSide,
DWORD dwTotalNumberOfPages,
DWORD dwNupBorderFlags,
BOOL bDuplex,
DWORD dwOptimization,
DWORD dwPageNumber,
LPDEVMODE pDevmode)
/*++
Function Description: PrintOneSideReverseForDriverEMF plays the EMF pages on the next
physical page, in the reverse order for the driver which does the
Nup transformations.
Parameters: hSpoolHandle -- handle the spool file handle
hPrinterDC -- handle to the printer device context
dwDrvNumberOfPagesPerSide -- number of pages the driver will print per side
dwTotalNumberOfPages -- total number of pages in the document
dwNupBorderFlags -- border printing options for nup
bDuplex -- flag to indicate duplex printing
dwOptimization -- optimization flags
dwPageNumber -- page number to start the side
pDevmode -- devmode with resolution settings
Return Values: TRUE if successful
FALSE otherwise
--*/
{
DWORD dwPageIndex, dwPageType, dwSides;
BOOL bReturn = FALSE, bNewDevmode,BeSmart;
LPDEVMODEW pCurrDM;
HANDLE hEMF = NULL;
DWORD dwLimit;
dwSides = bDuplex ? 2 : 1;
//
// If the document will fit on one phisical page, then this variable will prevent
// the printer from playing extra pages just to fill in one phisical page
// The exception is when the pages fit on a single phisical page, but they must
// be collated. Then because of design, the printer will also draw borders for the
// empty pages which are played so that the page gets ejected.
//
BeSmart = (dwTotalNumberOfPages<=dwDrvNumberOfPagesPerSide) &&
(pDevmode->dmCollate != DMCOLLATE_TRUE);
for (; dwSides; --dwSides) {
// This loop may play some empty pages in the last side, since the
// driver is doing nup and it does not keep count of the page numbers
//
dwPageIndex=BeSmart?dwPageNumber:1;
dwLimit =BeSmart?dwTotalNumberOfPages:dwDrvNumberOfPagesPerSide;
for (;dwPageIndex<=dwLimit; ++dwPageIndex,++dwPageNumber) {
if (BeSmart || dwPageNumber <= dwTotalNumberOfPages) {
if (!(hEMF = GdiGetPageHandle(hSpoolHandle,
dwPageNumber,
&dwPageType))) {
ODS(("GdiGetPageHandle failed\nPrinter %ws\n", pDevmode->dmDeviceName));
goto CleanUp;
}
// Process new devmodes in the spoolfile
if (!ResetDCForNewDevmode(hSpoolHandle,
hPrinterDC,
dwPageNumber,
FALSE,
dwOptimization,
&bNewDevmode,
pDevmode,
NULL)) {
}
}
if (!GdiStartPageEMF(hSpoolHandle)) {
ODS(("StartPage failed\nPrinter %ws\n", pDevmode->dmDeviceName));
goto CleanUp;
}
if (BeSmart || dwPageNumber <= dwTotalNumberOfPages) {
if (!PlayEMFPage(hSpoolHandle,
hPrinterDC,
hEMF,
1,
dwPageNumber,
1,
dwNupBorderFlags,
EMF_DEGREE_90)) {
ODS(("PlayEMFPage failed\n"));
goto CleanUp;
}
}
if (!GdiEndPageEMF(hSpoolHandle, dwOptimization)) {
ODS(("EndPage failed\nPrinter %ws\n", pDevmode->dmDeviceName));
goto CleanUp;
}
}
}
bReturn = TRUE;
CleanUp:
return bReturn;
}
BOOL
PrintReverseForDriverEMF(
HANDLE hSpoolHandle,
HDC hPrinterDC,
DWORD dwDrvNumberOfPagesPerSide,
DWORD dwTotalNumberOfPages,
DWORD dwNupBorderFlags,
DWORD dwJobNumberOfCopies,
DWORD dwDrvNumberOfCopies,
BOOL bCollate,
BOOL bDuplex,
BOOL bOdd,
DWORD dwOptimization,
LPDEVMODEW pDevmode,
PPAGE_NUMBER pHead,
PPRINTPROCESSORDATA pData)
/*++
Function Description: PrintReverseForDriverEMF plays the EMF pages in the reverse order
for the driver which does the Nup transformations.
Parameters: hSpoolHandle -- handle the spool file handle
hPrinterDC -- handle to the printer device context
dwDrvNumberOfPagesPerSide -- number of pages the driver will print per side
dwTotalNumberOfPages -- total number of pages in the document
dwNupBorderFlags -- border printing options for nup
dwJobNumberOfCopies -- number of copies of the job to be printed
dwDrvNumberOfCopies -- number of copies that the driver can print
bCollate -- flag for collating the copies
bDuplex -- flag to indicate duplex printing
bOdd -- flag to indicate odd number of sides to print
dwOptimization -- optimization flags
pDevmode -- pointer to devmode for changing the copy count
pHead -- pointer to a linked list containing the starting
page numbers for each of the sides
pData -- needed for status and the handle of the event: pause, resume etc.
Return Values: TRUE if successful
FALSE otherwise
--*/
{
DWORD dwPageIndex,dwPageNumber,dwRemainingCopies;
BOOL bReturn = FALSE;
// select the correct page for duplex printing
if (bDuplex && !bOdd) {
if (pHead) {
pHead = pHead->pNext;
} else {
bReturn = TRUE;
goto CleanUp;
}
}
// play the sides in reverse order
while (pHead) {
//
// If the print processor is paused, wait for it to be resumed
//
if (pData->fsStatus & PRINTPROCESSOR_PAUSED) {
WaitForSingleObject(pData->semPaused, INFINITE);
}
// set the page number
dwPageNumber = pHead->dwPageNumber;
if (bCollate) {
if (!PrintOneSideReverseForDriverEMF(hSpoolHandle,
hPrinterDC,
dwDrvNumberOfPagesPerSide,
dwTotalNumberOfPages,
dwNupBorderFlags,
bDuplex,
dwOptimization,
dwPageNumber,
pDevmode)) {
goto CleanUp;
}
} else {
dwRemainingCopies = dwJobNumberOfCopies;
while (dwRemainingCopies) {
if (dwRemainingCopies <= dwDrvNumberOfCopies) {
SetDrvCopies(hPrinterDC, pDevmode, dwRemainingCopies);
dwRemainingCopies = 0;
} else {
SetDrvCopies(hPrinterDC, pDevmode, dwDrvNumberOfCopies);
dwRemainingCopies -= dwDrvNumberOfCopies;
}
if (!PrintOneSideReverseForDriverEMF(hSpoolHandle,
hPrinterDC,
dwDrvNumberOfPagesPerSide,
dwTotalNumberOfPages,
dwNupBorderFlags,
bDuplex,
dwOptimization,
dwPageNumber,
pDevmode)) {
goto CleanUp;
}
}
}
pHead = pHead->pNext;
// go to the next page for duplex printing
if (bDuplex && pHead) {
pHead = pHead->pNext;
}
}
bReturn = TRUE;
CleanUp:
return bReturn;
}
BOOL
PrintOneSideReverseEMF(
HANDLE hSpoolHandle,
HDC hPrinterDC,
DWORD dwNumberOfPagesPerSide,
DWORD dwNupBorderFlags,
BOOL bDuplex,
DWORD dwOptimization,
DWORD dwStartPage1,
DWORD dwEndPage1,
DWORD dwStartPage2,
DWORD dwEndPage2,
LPDEVMODE pDevmode)
/*++
Function Description: PrintOneSideReverseEMF plays the EMF pages for the next physical page.
Parameters: hSpoolHandle -- handle the spool file handle
hPrinterDC -- handle to the printer device context
dwNumberOfPagesPerSide -- number of pages to be printed per side by the print
processor
dwNupBorderFlags -- border printing options for nup
bDuplex -- flag to indicate duplex printing
dwOptimization -- optimization flags
dwStartPage1 -- page number of the first EMF page on 1st side
dwEndPage1 -- page number of the last EMF page on 1st side
dwStartPage2 -- page number of the first EMF page on 2nd side
dwEndPage2 -- page number of the last EMF page on 2nd side
pDevmode -- devmode with resolution settings
Return Values: TRUE if successful
FALSE otherwise
--*/
{
DWORD dwPageNumber, dwPageIndex, dwPageType;
BOOL bReturn = FALSE, bNewDevmode;
LPDEVMODEW pCurrDM;
HANDLE hEMF = NULL;
DWORD dwEndPage, dwStartPage, dwSides, dwAngle;
INT dmOrientation = pDevmode->dmOrientation;
for (dwSides = bDuplex ? 2 : 1;
dwSides;
--dwSides) {
if (bDuplex && (dwSides == 1)) {
dwStartPage = dwStartPage2;
dwEndPage = dwEndPage2;
} else {
dwStartPage = dwStartPage1;
dwEndPage = dwEndPage1;
}
for (dwPageNumber = dwStartPage, dwPageIndex = 1;
dwPageNumber <= dwEndPage;
++dwPageNumber, ++dwPageIndex) {
if (!(hEMF = GdiGetPageHandle(hSpoolHandle,
dwPageNumber,
&dwPageType))) {
ODS(("GdiGetPageHandle failed\nPrinter %ws\n", pDevmode->dmDeviceName));
goto CleanUp;
}
dwAngle = EMF_DEGREE_90;
if (dwPageIndex == 1) {
// Process devmodes in the spool file and call StartPage
if (!ResetDCForNewDevmode(hSpoolHandle,
hPrinterDC,
dwPageNumber,
FALSE,
dwOptimization,
&bNewDevmode,
pDevmode,
&pCurrDM) ||
!GdiStartPageEMF(hSpoolHandle)) {
goto CleanUp;
}
if (pCurrDM)
dmOrientation = pCurrDM->dmOrientation;
}
// in case of orientation switch we need to keep track of what
// we started with and what it is now
else if (dwNumberOfPagesPerSide > 1)
{
if (GdiGetDevmodeForPage(hSpoolHandle,
dwPageNumber,
&pCurrDM,
NULL))
{
if (pCurrDM && pCurrDM->dmOrientation != dmOrientation)
{
dwAngle = EMF_DEGREE_SWAP | EMF_DEGREE_90;
}
}
}
if (!PlayEMFPage(hSpoolHandle,
hPrinterDC,
hEMF,
dwNumberOfPagesPerSide,
dwPageNumber,
dwPageIndex,
dwNupBorderFlags,
dwAngle)) {
ODS(("PlayEMFPage failed\nPrinter %ws\n", pDevmode->dmDeviceName));
goto CleanUp;
}
}
if ((dwPageIndex == 1) && !GdiStartPageEMF(hSpoolHandle)) {
ODS(("StartPage failed\nPrinter %ws\n", pDevmode->dmDeviceName));
goto CleanUp;
}
if (!GdiEndPageEMF(hSpoolHandle, dwOptimization)) {
ODS(("EndPage failed\nPrinter %ws\n", pDevmode->dmDeviceName));
goto CleanUp;
}
}
bReturn = TRUE;
CleanUp:
return bReturn;
}
BOOL
PrintReverseEMF(
HANDLE hSpoolHandle,
HDC hPrinterDC,
DWORD dwTotalNumberOfPages,
DWORD dwNumberOfPagesPerSide,
DWORD dwNupBorderFlags,
DWORD dwJobNumberOfCopies,
DWORD dwDrvNumberOfCopies,
BOOL bCollate,
BOOL bDuplex,
BOOL bOdd,
DWORD dwOptimization,
LPDEVMODEW pDevmode,
PPAGE_NUMBER pHead,
PPRINTPROCESSORDATA pData)
/*++
Function Description: PrintReverseEMF plays the EMF pages in the reverse order and also
performs nup transformations.
Parameters: hSpoolHandle -- handle the spool file handle
hPrinterDC -- handle to the printer device context
dwTotalNumberOfPages -- number of pages in the document
dwNumberOfPagesPerSide -- number of pages to be printed per side by the print
processor
dwNupBorderFlags -- border printing options for nup
dwJobNumberOfCopies -- number of copies of the job to be printed
dwDrvNumberOfCopies -- number of copies that the driver can print
bCollate -- flag for collating the copies
bDuplex -- flag to indicate duplex printing
bOdd -- flag to indicate odd number of sides to print
dwOptimization -- optimization flags
pDevmode -- pointer to devmode for changing the copy count
pHead -- pointer to a linked list containing the starting
page numbers for each of the sides
pData -- needed for status and the handle of the event: pause, resume etc.
Return Values: TRUE if successful
FALSE otherwise
--*/
{
DWORD dwPageNumber,dwPageIndex,dwRemainingCopies;
DWORD dwStartPage1,dwStartPage2,dwEndPage1,dwEndPage2;
BOOL bReturn = FALSE;
if (!pHead) {
bReturn = TRUE;
goto CleanUp;
}
// set the start and end page numbers for duplex and regular printing
if (bDuplex) {
if (bOdd) {
dwStartPage1 = pHead->dwPageNumber;
dwEndPage1 = dwTotalNumberOfPages;
dwStartPage2 = dwTotalNumberOfPages+1;
dwEndPage2 = 0;
} else {
dwStartPage2 = pHead->dwPageNumber;
dwEndPage2 = dwTotalNumberOfPages;
if (pHead = pHead->pNext) {
dwStartPage1 = pHead->dwPageNumber;
dwEndPage1 = dwStartPage2 - 1;
}
}
} else {
dwStartPage1 = pHead->dwPageNumber;
dwEndPage1 = dwTotalNumberOfPages;
dwStartPage2 = 0;
dwEndPage2 = 0;
}
while (pHead) {
//
// If the print processor is paused, wait for it to be resumed
//
if (pData->fsStatus & PRINTPROCESSOR_PAUSED) {
WaitForSingleObject(pData->semPaused, INFINITE);
}
if (bCollate) {
if (!PrintOneSideReverseEMF(hSpoolHandle,
hPrinterDC,
dwNumberOfPagesPerSide,
dwNupBorderFlags,
bDuplex,
dwOptimization,
dwStartPage1,
dwEndPage1,
dwStartPage2,
dwEndPage2,
pDevmode)) {
goto CleanUp;
}
} else {
dwRemainingCopies = dwJobNumberOfCopies;
while (dwRemainingCopies) {
if (dwRemainingCopies <= dwDrvNumberOfCopies) {
SetDrvCopies(hPrinterDC, pDevmode, dwRemainingCopies);
dwRemainingCopies = 0;
} else {
SetDrvCopies(hPrinterDC, pDevmode, dwDrvNumberOfCopies);
dwRemainingCopies -= dwDrvNumberOfCopies;
}
if (!PrintOneSideReverseEMF(hSpoolHandle,
hPrinterDC,
dwNumberOfPagesPerSide,
dwNupBorderFlags,
bDuplex,
dwOptimization,
dwStartPage1,
dwEndPage1,
dwStartPage2,
dwEndPage2,
pDevmode)) {
goto CleanUp;
}
}
}
if (bDuplex) {
if (pHead->pNext && pHead->pNext->pNext) {
dwEndPage2 = pHead->dwPageNumber - 1;
pHead = pHead->pNext;
dwStartPage2 = pHead->dwPageNumber;
dwEndPage1 = dwStartPage2 - 1;
pHead = pHead->pNext;
dwStartPage1 = pHead->dwPageNumber;
} else {
break;
}
} else {
pHead = pHead->pNext;
if (pHead) {
dwEndPage1 = dwStartPage1 - 1;
dwStartPage1 = pHead->dwPageNumber;
}
}
}
bReturn = TRUE;
CleanUp:
return bReturn;
}
BOOL
PrintOneSideBookletEMF(
HANDLE hSpoolHandle,
HDC hPrinterDC,
DWORD dwNumberOfPagesPerSide,
DWORD dwNupBorderFlags,
DWORD dwTotalNumberOfPages,
DWORD dwTotalPrintPages,
DWORD dwStartPage,
BOOL bReverseOrderPrinting,
DWORD dwOptimization,
DWORD dwDuplexMode,
LPDEVMODE pDevmode)
/*++
Function Description: PrintOneSideBookletEMF prints one page of the booklet job.
Parameters: hSpoolHandle -- handle the spool file handle
hPrinterDC -- handle to the printer device context
dwNumberOfPagesPerSide -- number of pages to be printed per side by the print
processor
dwNupBorderFlags -- border printing options for nup
dwTotalNumberOfPages -- number of pages in the document
dwTotalPrintPages -- number of pages to printed (multiple of 4)
dwStartPage -- number of the starting page for the side
bReverseOrderPrinting -- flag for reverse order printing
dwOptimization -- optimization flags
dwDuplexMode -- duplex printing mode (none|horz|vert)
pDevmode -- devmode with resolution settings
Return Values: TRUE if successful
FALSE otherwise
--*/
{
DWORD dwPageArray[4];
DWORD dwPagesPrinted = 0, dwPageIndex, dwAngle, dwPageType, dwLastPage;
HANDLE hEMF = NULL;
LPDEVMODEW pCurrDM;
BOOL bReturn = FALSE ,bNewDevmode;
INT dmOrientation;
// set the order of the pages
if (bReverseOrderPrinting) {
dwPageArray[0] = dwStartPage + 1;
dwPageArray[1] = dwTotalPrintPages - dwStartPage;
if (dwDuplexMode == EMF_DUP_VERT) {
dwPageArray[2] = dwStartPage;
dwPageArray[3] = dwPageArray[1] + 1;
} else { // EMF_DUP_HORZ
dwPageArray[3] = dwStartPage;
dwPageArray[2] = dwPageArray[1] + 1;
}
} else {
dwPageArray[1] = dwStartPage;
dwPageArray[0] = dwTotalPrintPages - dwStartPage + 1;
if (dwDuplexMode == EMF_DUP_VERT) {
dwPageArray[2] = dwPageArray[0] - 1;
dwPageArray[3] = dwPageArray[1] + 1;
} else { // EMF_DUP_HORZ
dwPageArray[2] = dwPageArray[1] + 1;
dwPageArray[3] = dwPageArray[0] - 1;
}
}
// Set page number for ResetDC
dwLastPage = (dwTotalNumberOfPages < dwPageArray[0]) ? dwTotalNumberOfPages
: dwPageArray[0];
// Process devmodes in the spool file
if (!ResetDCForNewDevmode(hSpoolHandle,
hPrinterDC,
dwLastPage,
FALSE,
dwOptimization,
&bNewDevmode,
pDevmode,
&pCurrDM)) {
goto CleanUp;
}
if (pCurrDM)
dmOrientation = pCurrDM->dmOrientation;
else
dmOrientation = pDevmode->dmOrientation;
while (dwPagesPrinted < 4) {
for (dwPageIndex = 1;
dwPageIndex <= dwNumberOfPagesPerSide;
++dwPageIndex, ++dwPagesPrinted) {
if (dwPageArray[dwPagesPrinted] <= dwTotalNumberOfPages) {
if (!(hEMF = GdiGetPageHandle(hSpoolHandle,
dwPageArray[dwPagesPrinted],
&dwPageType))) {
ODS(("GdiGetPageHandle failed\nPrinter %ws\n", pDevmode->dmDeviceName));
goto CleanUp;
}
}
if (dwPageIndex == 1) {
if (!GdiStartPageEMF(hSpoolHandle)) {
ODS(("StartPage failed\nPrinter %ws\n", pDevmode->dmDeviceName));
goto CleanUp;
}
}
if (dwPageArray[dwPagesPrinted] <= dwTotalNumberOfPages) {
// in case of orientation switch we need to keep track of what
// we started with and what it is now
dwAngle = 0;
if (GdiGetDevmodeForPage(hSpoolHandle,
dwPageArray[dwPagesPrinted],
&pCurrDM,
NULL))
{
if (pCurrDM && pCurrDM->dmOrientation != dmOrientation)
dwAngle |= EMF_DEGREE_SWAP;
}
if ((dwDuplexMode == EMF_DUP_VERT) &&
(dwPagesPrinted > 1)) {
dwAngle |= EMF_DEGREE_270;
} else { // EMF_DUP_HORZ or 1st side
dwAngle |= EMF_DEGREE_90;
}
if (!PlayEMFPage(hSpoolHandle,
hPrinterDC,
hEMF,
dwNumberOfPagesPerSide,
dwPageArray[dwPagesPrinted],
dwPageIndex,
dwNupBorderFlags,
dwAngle)) {
ODS(("PlayEMFPage failed\nPrinter %ws\n", pDevmode->dmDeviceName));
goto CleanUp;
}
}
if (dwPageIndex == dwNumberOfPagesPerSide) {
if (!GdiEndPageEMF(hSpoolHandle, dwOptimization)) {
ODS(("EndPage failed\nPrinter %ws\n", pDevmode->dmDeviceName));
goto CleanUp;
}
}
}
}
bReturn = TRUE;
CleanUp:
return bReturn;
}
BOOL
PrintBookletEMF(
HANDLE hSpoolHandle,
HDC hPrinterDC,
DWORD dwNumberOfPagesPerSide,
DWORD dwTotalNumberOfPages,
DWORD dwNupBorderFlags,
DWORD dwJobNumberOfCopies,
DWORD dwDrvNumberOfCopies,
BOOL bReverseOrderPrinting,
BOOL bCollate,
DWORD dwOptimization,
DWORD dwDuplexMode,
LPDEVMODEW pDevmode,
PPRINTPROCESSORDATA pData)
/*++
Function Description: PrintBookletEMF prints the job in 2-up in booklet form.
Parameters: hSpoolHandle -- handle the spool file handle
hPrinterDC -- handle to the printer device context
dwNumberOfPagesPerSide -- number of pages to be printed per side by the print
processor
dwTotalNumberOfPages -- number of pages in the document
dwNupBorderFlags -- border printing options for nup
dwJobNumberOfCopies -- number of copies of the job to be printed
dwDrvNumberOfCopies -- number of copies that the driver can print
bReverseOrderPrinting -- flag for reverse order printing
bCollate -- flag for collating the copies
dwOptimization -- optimization flags
dwDuplexMode -- duplex printing mode (none|horz|vert)
pDevmode -- pointer to devmode for changing the copy count
pData -- needed for status and the handle of the event: pause, resume etc.
Return Values: TRUE if successful
FALSE otherwise
--*/
{
BOOL bReturn = FALSE;
DWORD dwTotalPrintPages, dwNumberOfPhyPages, dwRemainingCopies, dwIndex;
// Get closest multiple of 4 greater than dwTotalNumberOfPages
dwTotalPrintPages = dwTotalNumberOfPages - (dwTotalNumberOfPages % 4);
if (dwTotalPrintPages != dwTotalNumberOfPages) {
dwTotalPrintPages += 4;
}
dwNumberOfPhyPages = (DWORD) dwTotalPrintPages / 4;
for (dwIndex = 0; dwIndex < dwNumberOfPhyPages; ++dwIndex) {
//
// If the print processor is paused, wait for it to be resumed
//
if (pData->fsStatus & PRINTPROCESSOR_PAUSED) {
WaitForSingleObject(pData->semPaused, INFINITE);
}
if (bCollate) {
if (!PrintOneSideBookletEMF(hSpoolHandle,
hPrinterDC,
dwNumberOfPagesPerSide,
dwNupBorderFlags,
dwTotalNumberOfPages,
dwTotalPrintPages,
dwIndex * 2 + 1,
bReverseOrderPrinting,
dwOptimization,
dwDuplexMode,
pDevmode)) {
goto CleanUp;
}
} else {
dwRemainingCopies = dwJobNumberOfCopies;
while (dwRemainingCopies) {
if (dwRemainingCopies <= dwDrvNumberOfCopies) {
SetDrvCopies(hPrinterDC, pDevmode, dwRemainingCopies);
dwRemainingCopies = 0;
} else {
SetDrvCopies(hPrinterDC, pDevmode, dwDrvNumberOfCopies);
dwRemainingCopies -= dwDrvNumberOfCopies;
}
if (!PrintOneSideBookletEMF(hSpoolHandle,
hPrinterDC,
dwNumberOfPagesPerSide,
dwNupBorderFlags,
dwTotalNumberOfPages,
dwTotalPrintPages,
dwIndex * 2 + 1,
bReverseOrderPrinting,
dwOptimization,
dwDuplexMode,
pDevmode)) {
goto CleanUp;
}
}
}
}
bReturn = TRUE;
CleanUp:
return bReturn;
}
BOOL
PrintEMFSingleCopy(
HANDLE hSpoolHandle,
HDC hPrinterDC,
BOOL bReverseOrderPrinting,
DWORD dwDrvNumberOfPagesPerSide,
DWORD dwNumberOfPagesPerSide,
DWORD dwTotalNumberOfPages,
DWORD dwNupBorderFlags,
DWORD dwJobNumberOfCopies,
DWORD dwDrvNumberOfCopies,
BOOL bCollate,
BOOL bOdd,
BOOL bBookletPrint,
DWORD dwOptimization,
DWORD dwDuplexMode,
LPDEVMODEW pDevmode,
PPAGE_NUMBER pHead,
PPRINTPROCESSORDATA pData)
/*++
Function Description: PrintEMFSingleCopy plays one copy of the job on hPrinterDC.
Parameters: hSpoolHandle -- handle the spool file handle
hPrinterDC -- handle to the printer device context
bReverseOrderPrinting -- flag for reverse order printing
dwDrvNumberOfPagesPerSide -- number of pages the driver will print per side
dwNumberOfPagesPerSide -- number of pages to be printed per side by the print
processor
dwTotalNumberOfPages -- number of pages in the document
dwNupBorderFlags -- border printing options for nup
dwJobNumberOfCopies -- number of copies of the job to be printed
dwDrvNumberOfCopies -- number of copies that the driver can print
bCollate -- flag for collating the copies
bOdd -- flag to indicate odd number of sides to print
bBookletPrint -- flag for booklet printing
dwOptimization -- optimization flags
dwDuplexMode -- duplex printing mode (none|horz|vert)
pDevmode -- pointer to devmode for changing the copy count
pHead -- pointer to a linked list containing the starting
page numbers for each of the sides
pData -- needed for status and the handle of the event: pause, resume etc.
Return Values: TRUE if successful
FALSE otherwise
--*/
{
BOOL bDuplex = (dwDuplexMode != EMF_DUP_NONE);
if (bBookletPrint) {
// Booklet Printing
return PrintBookletEMF(hSpoolHandle,
hPrinterDC,
dwNumberOfPagesPerSide,
dwTotalNumberOfPages,
dwNupBorderFlags,
dwJobNumberOfCopies,
dwDrvNumberOfCopies,
bReverseOrderPrinting,
bCollate,
dwOptimization,
dwDuplexMode,
pDevmode,
pData);
}
if (bReverseOrderPrinting) {
if (dwDrvNumberOfPagesPerSide != 1 || dwNumberOfPagesPerSide == 1) {
// @@BEGIN_DDKSPLIT
// Reverse printing while driver does nup / no nup required
// @@END_DDKSPLIT
return PrintReverseForDriverEMF(hSpoolHandle,
hPrinterDC,
dwDrvNumberOfPagesPerSide,
dwTotalNumberOfPages,
dwNupBorderFlags,
dwJobNumberOfCopies,
dwDrvNumberOfCopies,
bCollate,
bDuplex,
bOdd,
dwOptimization,
pDevmode,
pHead,
pData);
} else {
// Reverse printing and nup
return PrintReverseEMF(hSpoolHandle,
hPrinterDC,
dwTotalNumberOfPages,
dwNumberOfPagesPerSide,
dwNupBorderFlags,
dwJobNumberOfCopies,
dwDrvNumberOfCopies,
bCollate,
bDuplex,
bOdd,
dwOptimization,
pDevmode,
pHead,
pData);
}
} else {
// Normal printing
return PrintForwardEMF(hSpoolHandle,
hPrinterDC,
dwNumberOfPagesPerSide,
dwDrvNumberOfPagesPerSide,
dwNupBorderFlags,
dwJobNumberOfCopies,
dwDrvNumberOfCopies,
bCollate,
bDuplex,
dwOptimization,
pDevmode,
pData);
}
}
BOOL
GetStartPageList(
HANDLE hSpoolHandle,
PPAGE_NUMBER *pHead,
DWORD dwTotalNumberOfPages,
DWORD dwNumberOfPagesPerSide,
BOOL bCheckForDevmode,
LPBOOL pbOdd)
/*++
Function Description: GetStartPageList generates a list of the page numbers which
should appear on the start of each side of the job. This takes
into consideration the ResetDC calls that may appear before the
end of the page. The list generated by GetStartPageList is used
to play the job in reverse order.
Parameters: hSpoolHandle -- handle the spool file handle
pHead -- pointer to a pointer to a linked list containing the
starting page numbers for each of the sides
dwTotalNumberOfPages -- number of pages in the document
dwNumberOfPagesPerSide -- number of pages to be printed per side by the print
processor
pbOdd -- pointer to flag indicating odd number of pages to
print
Return Values: TRUE if successful
FALSE otherwise
--*/
{
DWORD dwPageIndex,dwPageNumber=1,dwPageType;
LPDEVMODEW pCurrDM, pLastDM;
PPAGE_NUMBER pTemp=NULL;
BOOL bReturn = FALSE;
BOOL bCheckDevmode;
bCheckDevmode = bCheckForDevmode && (dwNumberOfPagesPerSide != 1);
while (dwPageNumber <= dwTotalNumberOfPages) {
for (dwPageIndex = 1;
(dwPageIndex <= dwNumberOfPagesPerSide) && (dwPageNumber <= dwTotalNumberOfPages);
++dwPageIndex, ++dwPageNumber) {
if (bCheckDevmode) {
// Check if the devmode has changed requiring a new page
if (!GdiGetDevmodeForPage(hSpoolHandle, dwPageNumber,
&pCurrDM, NULL)) {
ODS(("Get devmodes failed\n"));
goto CleanUp;
}
if (dwPageIndex == 1) {
// Save the Devmode for the first page on a side
pLastDM = pCurrDM;
} else {
// If the Devmode changes in a side, start a new page
if (DifferentDevmodes(pCurrDM, pLastDM)) {
dwPageIndex = 1;
pLastDM = pCurrDM;
}
}
}
// Create a node for the start of a side
if (dwPageIndex == 1) {
if (!(pTemp = AllocSplMem(sizeof(PAGE_NUMBER)))) {
ODS(("GetStartPageList - Run out of memory"));
goto CleanUp;
}
pTemp->pNext = *pHead;
pTemp->dwPageNumber = dwPageNumber;
*pHead = pTemp;
// flip the bOdd flag
*pbOdd = !*pbOdd;
}
}
}
bReturn = TRUE;
CleanUp:
// Free up the memory in case of a failure.
if (!bReturn) {
while (pTemp = *pHead) {
*pHead = (*pHead)->pNext;
FreeSplMem(pTemp);
}
}
return bReturn;
}
BOOL
CopyDevmode(
PPRINTPROCESSORDATA pData,
LPDEVMODEW *pDevmode)
/*++
Function Description: Copies the devmode in pData or the default devmode into pDevmode.
Parameters: pData - Data structure for the print job
pDevmode - pointer to devmode
Return Value: TRUE if successful
FALSE otherwise
--*/
{
HANDLE hDrvPrinter = NULL;
BOOL bReturn = FALSE;
fnWinSpoolDrv fnList;
LONG lNeeded;
if (pData->pDevmode) {
lNeeded = pData->pDevmode->dmSize + pData->pDevmode->dmDriverExtra;
if (*pDevmode = (LPDEVMODEW) AllocSplMem(lNeeded)) {
memcpy(*pDevmode, pData->pDevmode, lNeeded);
} else {
goto CleanUp;
}
} else {
// Get the default devmode
// Get the pointer to the client side functions from the router
if (!SplInitializeWinSpoolDrv(&fnList)) {
goto CleanUp;
}
// Get a client side printer handle to pass to the driver
if (!(* (fnList.pfnOpenPrinter))(pData->pPrinterName, &hDrvPrinter, NULL)) {
ODS(("Open printer failed\nPrinter %ws\n", pData->pPrinterName));
goto CleanUp;
}
lNeeded = (* (fnList.pfnDocumentProperties))(NULL,
hDrvPrinter,
pData->pPrinterName,
NULL,
NULL,
0);
if (lNeeded <= 0 ||
!(*pDevmode = (LPDEVMODEW) AllocSplMem(lNeeded)) ||
(* (fnList.pfnDocumentProperties))(NULL,
hDrvPrinter,
pData->pPrinterName,
*pDevmode,
NULL,
DM_OUT_BUFFER) < 0) {
if (*pDevmode) {
FreeSplMem(*pDevmode);
*pDevmode = NULL;
}
ODS(("DocumentProperties failed\nPrinter %ws\n",pData->pPrinterName));
goto CleanUp;
}
}
bReturn = TRUE;
CleanUp:
if (hDrvPrinter) {
(* (fnList.pfnClosePrinter))(hDrvPrinter);
}
return bReturn;
}
BOOL
PrintEMFJob(
PPRINTPROCESSORDATA pData,
LPWSTR pDocumentName)
/*++
Function Description: Prints out a job with EMF data type.
Parameters: pData - Data structure for this job
pDocumentName - Name of this document
Return Value: TRUE if successful
FALSE if failed - GetLastError() will return reason.
--*/
{
HANDLE hSpoolHandle = NULL;
DWORD LastError;
HDC hPrinterDC = NULL;
BOOL bReverseOrderPrinting, bReturn = FALSE, bSetWorldXform = TRUE;
BOOL bCollate, bDuplex, bBookletPrint, bStartDoc = FALSE, bOdd = FALSE;
BOOL bUpdateAttributes = FALSE;
SHORT dmCollate,dmCopies;
DWORD dwNumberOfPagesPerSide, dwTotalNumberOfPages = 0, dwNupBorderFlags;
DWORD dwJobNumberOfPagesPerSide, dwDrvNumberOfPagesPerSide, dwDuplexMode;
DWORD dwJobNumberOfCopies, dwDrvNumberOfCopies,dwRemainingCopies;
DWORD dwJobOrder, dwDrvOrder, dwOptimization;
DOCINFOW DocInfo;
XFORM OldXForm;
PPAGE_NUMBER pHead = NULL,pTemp;
ATTRIBUTE_INFO_3 AttributeInfo;
LPDEVMODEW pDevmode = NULL, pFirstDM = NULL, pCopyDM;
// Copy the devmode into pDevMode
if (!CopyDevmode(pData, &pDevmode)) {
ODS(("CopyDevmode failed\nPrinter %ws\nDocument %ws\nJobID %u\n", pData->pDevmode->dmDeviceName, pData->pDocument, pData->JobId));
goto CleanUp;
}
// Update resolution before CreateDC for monochrome optimization
if (!GetJobAttributes(pData->pPrinterName,
pDevmode,
&AttributeInfo)) {
ODS(("GetJobAttributes failed\nPrinter %ws\nDocument %ws\nJobID %u\n", pData->pDevmode->dmDeviceName, pData->pDocument, pData->JobId));
goto CleanUp;
} else {
if (AttributeInfo.dwColorOptimization) {
if (pDevmode->dmPrintQuality != AttributeInfo.dmPrintQuality ||
pDevmode->dmYResolution != AttributeInfo.dmYResolution)
{
pDevmode->dmPrintQuality = AttributeInfo.dmPrintQuality;
pDevmode->dmYResolution = AttributeInfo.dmYResolution;
bUpdateAttributes = TRUE;
}
}
if (pDevmode->dmFields & DM_COLLATE)
dmCollate = pDevmode->dmCollate;
else
dmCollate = DMCOLLATE_FALSE;
if (pDevmode->dmFields & DM_COPIES)
dmCopies = pDevmode->dmCopies;
else
dmCopies = 0;
}
// Get spool file handle and printer device context from GDI
try {
hSpoolHandle = GdiGetSpoolFileHandle(pData->pPrinterName,
pDevmode,
pDocumentName);
if (hSpoolHandle) {
hPrinterDC = GdiGetDC(hSpoolHandle);
}
} except (EXCEPTION_EXECUTE_HANDLER) {
ODS(("PrintEMFJob gave an exceptionPrinter %ws\nDocument %ws\nJobID %u\n", pData->pDevmode->dmDeviceName, pData->pDocument, pData->JobId));
goto CleanUp;
}
if (!hPrinterDC || !hSpoolHandle) {
goto CleanUp;
}
// Use the first devmode in the spool file to update the copy count
// and the collate setting
if (GdiGetDevmodeForPage(hSpoolHandle, 1, &pFirstDM, NULL) &&
pFirstDM) {
if (pFirstDM->dmFields & DM_COPIES) {
pDevmode->dmFields |= DM_COPIES;
pDevmode->dmCopies = pFirstDM->dmCopies;
}
if (pFirstDM->dmFields & DM_COLLATE) {
pDevmode->dmFields |= DM_COLLATE;
pDevmode->dmCollate = pFirstDM->dmCollate;
}
}
// The number of copies of the print job is the product of the number of copies set
// from the driver UI (present in the devmode) and the number of copies in pData struct
dwJobNumberOfCopies = (pDevmode->dmFields & DM_COPIES) ? pData->Copies*pDevmode->dmCopies
: pData->Copies;
pDevmode->dmCopies = (short) dwJobNumberOfCopies;
pDevmode->dmFields |= DM_COPIES;
// If collate is true this limits the ability of the driver to do multiple copies
// and causes the driver (PS) supported n-up to print blank page borders for reverse printing.
// Therefore we disable collate for 1 page multiple copy jobs or no copies but n-up since
// collate has no meaning in those cases.
//
if ((pDevmode->dmFields & DM_COLLATE) && pDevmode->dmCollate == DMCOLLATE_TRUE)
{
if (dwJobNumberOfCopies > 1)
{
// Get the number of pages in the job. This call waits till the
// last page is spooled.
try {
dwTotalNumberOfPages = GdiGetPageCount(hSpoolHandle);
} except (EXCEPTION_EXECUTE_HANDLER) {
ODS(("PrintEMFJob gave an exceptionPrinter %ws\nDocument %ws\nJobID %u\n", pData->pDevmode->dmDeviceName, pData->pDocument, pData->JobId));
goto SkipCollateDisable;
}
if (dwTotalNumberOfPages > AttributeInfo.dwDrvNumberOfPagesPerSide)
goto SkipCollateDisable;
}
// if copies == 1 and driver n-up we will disable collate
//
else if (AttributeInfo.dwDrvNumberOfPagesPerSide <= 1 && dmCollate == DMCOLLATE_TRUE)
goto SkipCollateDisable;
pDevmode->dmCollate = DMCOLLATE_FALSE;
if (pFirstDM)
pFirstDM->dmCollate = DMCOLLATE_FALSE;
}
SkipCollateDisable:
// Update the job attributes but only if something has changed. This is an expensive
// call so we only make a second call to GetJobAttributes if something has changed.
//
if (bUpdateAttributes || pDevmode->dmCopies != dmCopies ||
((pDevmode->dmFields & DM_COLLATE) && (pDevmode->dmCollate != dmCollate)))
{
if (!GetJobAttributes(pData->pPrinterName,
pDevmode,
&AttributeInfo)) {
ODS(("GetJobAttributes failed\nPrinter %ws\nDocument %ws\nJobID %u\n", pData->pDevmode->dmDeviceName, pData->pDocument, pData->JobId));
goto CleanUp;
}
}
// Initialize bReverseOrderPrinting, dwJobNumberOfPagesPerSide,
// dwDrvNumberOfPagesPerSide, dwNupBorderFlags, dwJobNumberOfCopies,
// dwDrvNumberOfCopies and bCollate
dwJobNumberOfPagesPerSide = AttributeInfo.dwJobNumberOfPagesPerSide;
dwDrvNumberOfPagesPerSide = AttributeInfo.dwDrvNumberOfPagesPerSide;
dwNupBorderFlags = AttributeInfo.dwNupBorderFlags;
dwJobNumberOfCopies = AttributeInfo.dwJobNumberOfCopies;
dwDrvNumberOfCopies = AttributeInfo.dwDrvNumberOfCopies;
dwJobOrder = AttributeInfo.dwJobPageOrderFlags & ( NORMAL_PRINT | REVERSE_PRINT);
dwDrvOrder = AttributeInfo.dwDrvPageOrderFlags & ( NORMAL_PRINT | REVERSE_PRINT);
bReverseOrderPrinting = (dwJobOrder != dwDrvOrder);
dwJobOrder = AttributeInfo.dwJobPageOrderFlags & BOOKLET_PRINT;
dwDrvOrder = AttributeInfo.dwDrvPageOrderFlags & BOOKLET_PRINT;
bBookletPrint = (dwJobOrder != dwDrvOrder);
bCollate = (pDevmode->dmFields & DM_COLLATE) &&
(pDevmode->dmCollate == DMCOLLATE_TRUE);
bDuplex = (pDevmode->dmFields & DM_DUPLEX) &&
(pDevmode->dmDuplex != DMDUP_SIMPLEX);
if (!dwJobNumberOfCopies) {
//
// Some applications can set the copy count to 0.
// In this case we exit.
//
bReturn = TRUE;
goto CleanUp;
}
if (bDuplex) {
dwDuplexMode = (pDevmode->dmDuplex == DMDUP_HORIZONTAL) ? EMF_DUP_HORZ
: EMF_DUP_VERT;
} else {
dwDuplexMode = EMF_DUP_NONE;
}
if (bBookletPrint) {
if (!bDuplex) {
// Not supported w/o duplex printing. Use default settings.
bBookletPrint = FALSE;
dwDrvNumberOfPagesPerSide = 1;
dwJobNumberOfPagesPerSide = 1;
} else {
// Fixed settings for pages per side.
dwDrvNumberOfPagesPerSide = 1;
dwJobNumberOfPagesPerSide = 2;
}
}
// Number of pages per side that the print processor has to play
dwNumberOfPagesPerSide = (dwDrvNumberOfPagesPerSide == 1)
? dwJobNumberOfPagesPerSide
: 1;
if (dwNumberOfPagesPerSide == 1) {
// if the print processor is not doing nup, don't draw borders
dwNupBorderFlags = NO_BORDER_PRINT;
}
//
// Color optimization may cause wrong output with duplex
//
dwOptimization = (AttributeInfo.dwColorOptimization == COLOR_OPTIMIZATION &&
!bDuplex && dwJobNumberOfPagesPerSide == 1)
? EMF_PP_COLOR_OPTIMIZATION
: 0;
// Check for Valid Option for n-up printing
if (!ValidNumberForNUp(dwNumberOfPagesPerSide)) {
ODS(("Invalid N-up option\nPrinter %ws\nDocument %ws\nJobID %u\n", pData->pDevmode->dmDeviceName, pData->pDocument, pData->JobId));
goto CleanUp;
}
if (bReverseOrderPrinting || bBookletPrint) {
// Get the number of pages in the job. This call waits till the
// last page is spooled.
try {
dwTotalNumberOfPages= GdiGetPageCount(hSpoolHandle);
} except (EXCEPTION_EXECUTE_HANDLER) {
ODS(("PrintEMFJob gave an exceptionPrinter %ws\nDocument %ws\nJobID %u\n", pData->pDevmode->dmDeviceName, pData->pDocument, pData->JobId));
goto CleanUp;
}
// Get start page list for reverse printing
// Check for a change of devmode between pages only if Nup and PCL driver
if (!GetStartPageList(hSpoolHandle,
&pHead,
dwTotalNumberOfPages,
dwJobNumberOfPagesPerSide,
FALSE,
&bOdd)) {
goto CleanUp;
}
}
// Save the old transformation on hPrinterDC
if (!SetGraphicsMode(hPrinterDC,GM_ADVANCED) ||
!GetWorldTransform(hPrinterDC,&OldXForm)) {
bSetWorldXform = FALSE;
ODS(("Transformation matrix can't be set\nPrinter %ws\nDocument %ws\nJobID %u\n", pData->pDevmode->dmDeviceName, pData->pDocument, pData->JobId));
goto CleanUp;
}
// pCopyDM will be used for changing the copy count
pCopyDM = pFirstDM ? pFirstDM : pDevmode;
pCopyDM->dmPrintQuality = pDevmode->dmPrintQuality;
pCopyDM->dmYResolution = pDevmode->dmYResolution;
try {
DocInfo.cbSize = sizeof(DOCINFOW);
DocInfo.lpszDocName = pData->pDocument;
DocInfo.lpszOutput = pData->pOutputFile;
DocInfo.lpszDatatype = NULL;
if (!GdiStartDocEMF(hSpoolHandle, &DocInfo)) goto CleanUp;
bStartDoc = TRUE;
if (bCollate) {
dwRemainingCopies = dwJobNumberOfCopies & 0x0000FFFF ;
while (dwRemainingCopies) {
if (dwRemainingCopies <= dwDrvNumberOfCopies) {
SetDrvCopies(hPrinterDC, pCopyDM, dwRemainingCopies);
dwRemainingCopies = 0;
} else {
SetDrvCopies(hPrinterDC, pCopyDM, dwDrvNumberOfCopies);
dwRemainingCopies -= dwDrvNumberOfCopies;
}
if (!PrintEMFSingleCopy(hSpoolHandle,
hPrinterDC,
bReverseOrderPrinting,
dwDrvNumberOfPagesPerSide,
dwNumberOfPagesPerSide,
dwTotalNumberOfPages,
dwNupBorderFlags,
dwJobNumberOfCopies,
dwDrvNumberOfCopies,
bCollate,
bOdd,
bBookletPrint,
dwOptimization,
dwDuplexMode,
pCopyDM,
pHead,
pData)) {
goto CleanUp;
}
}
} else {
if (!PrintEMFSingleCopy(hSpoolHandle,
hPrinterDC,
bReverseOrderPrinting,
dwDrvNumberOfPagesPerSide,
dwNumberOfPagesPerSide,
dwTotalNumberOfPages,
dwNupBorderFlags,
dwJobNumberOfCopies,
dwDrvNumberOfCopies,
bCollate,
bOdd,
bBookletPrint,
dwOptimization,
dwDuplexMode,
pCopyDM,
pHead,
pData)) {
goto CleanUp;
}
}
bStartDoc = FALSE;
if (!GdiEndDocEMF(hSpoolHandle)) goto CleanUp;
} except (EXCEPTION_EXECUTE_HANDLER) {
ODS(("PrintEMFSingleCopy gave an exception\nPrinter %ws\nDocument %ws\nJobID %u\n", pData->pDevmode->dmDeviceName, pData->pDocument, pData->JobId));
goto CleanUp;
}
bReturn = TRUE;
CleanUp:
//
// Preserve the last error
//
LastError = bReturn ? ERROR_SUCCESS : GetLastError();
if (bStartDoc) {
GdiEndDocEMF(hSpoolHandle);
}
if (bSetWorldXform && hPrinterDC) {
SetWorldTransform(hPrinterDC, &OldXForm);
}
while (pTemp = pHead) {
pHead = pHead->pNext;
FreeSplMem(pTemp);
}
if (pDevmode) {
FreeSplMem(pDevmode);
}
try {
if (hSpoolHandle) {
GdiDeleteSpoolFileHandle(hSpoolHandle);
}
} except (EXCEPTION_EXECUTE_HANDLER) {
ODS(("GdiDeleteSpoolFileHandle failed\nPrinter %ws\nDocument %ws\nJobID %u\n", pData->pDevmode->dmDeviceName, pData->pDocument, pData->JobId));
}
SetLastError(LastError);
return bReturn;
}