/*++

Copyright (c) 1996  Microsoft Corporation

Module Name:

    custsize.c

Abstract:

    Parser functions for handling custom page size feature

Environment:

    Windows NT PostScript driver

Revision History:

    03/20/97 -davidx-
        Created it.

--*/

#include "lib.h"
#include "ppd.h"


BOOL
BFixupCustomSizeDataFeedDirection(
    PUIINFO          pUIInfo,
    PPPDDATA         pPpdData,
    PCUSTOMSIZEDATA  pCSData
    )

/*++

Routine Description:

    Validate the requested paper feed direction parameter
    Fix up any inconsistency if necessary

Arguments:

    pPpdData - Points to PPDDATA structure
    pCSData - Specifies custom page size parameters

Return Value:

    TRUE if we can find a feeding direction to fit the custom page size.
    FALSE otherwise.

Note:

    See Figure 3 on page 109 of PPD 4.3 spec for more details.

--*/

#define ORIENTATION_AVAILABLE(iOrient) (dwFlags & (1 << (iOrient)))
#define FIXUP_FEEDDIRECTION(iMainOrient, iAltOrient, wAltFeedDirection) \
        { \
            if (! ORIENTATION_AVAILABLE(iMainOrient)) \
            { \
                pCSData->wFeedDirection = \
                    ORIENTATION_AVAILABLE(iAltOrient) ? \
                        (wAltFeedDirection) : \
                        MAX_FEEDDIRECTION; \
            } \
        }

{
    static const DWORD  adwMasks[4] = { 0x1, 0x3, 0x7, 0xf };
    DWORD   dwFlags;
    BOOL    bXGreaterThanY;
    BOOL    bShortEdgeFirst;
    LONG    lVal;
    WORD    wFeedDirectionSave;

    //
    // Figure out which custom page size orientations are supported
    // dwFlags is DWORD value whose lower-order 4 bits are interpreted as flags:
    //     If orientation N is supported, then bit 1 << N will be set
    //

    dwFlags = 0xf;

    if ((lVal = MINCUSTOMPARAM_ORIENTATION(pPpdData)) > 3)
        dwFlags = 0;
    else if (lVal > 0)
        dwFlags &= ~adwMasks[lVal - 1];

    if ((lVal = MAXCUSTOMPARAM_ORIENTATION(pPpdData)) < 3)
        dwFlags &= adwMasks[lVal];

    wFeedDirectionSave = pCSData->wFeedDirection;
    bXGreaterThanY = (pCSData->dwX > pCSData->dwY);

    //
    // First try to fit within the current feeding direction.
    //

    switch (pCSData->wFeedDirection)
    {
    case SHORTEDGEFIRST:

        if (bXGreaterThanY)
        {
            // orientation 0 (or 2)
            FIXUP_FEEDDIRECTION(0, 2, SHORTEDGEFIRST_FLIPPED);
        }
        else
        {
            // orientation 1 (or 3)
            FIXUP_FEEDDIRECTION(1, 3, SHORTEDGEFIRST_FLIPPED);
        }
        break;

    case SHORTEDGEFIRST_FLIPPED:

        if (bXGreaterThanY)
        {
            // orientation 2 (or 0)
            FIXUP_FEEDDIRECTION(2, 0, SHORTEDGEFIRST);
        }
        else
        {
            // orientation 3 (or 1)
            FIXUP_FEEDDIRECTION(3, 1, SHORTEDGEFIRST);
        }
        break;

    case LONGEDGEFIRST:

        if (bXGreaterThanY)
        {
            // orientation 1 (or 3)
            FIXUP_FEEDDIRECTION(1, 3, LONGEDGEFIRST_FLIPPED);
        }
        else
        {
            // orientation 0 (or 2)
            FIXUP_FEEDDIRECTION(0, 2, LONGEDGEFIRST_FLIPPED);
        }
        break;

    case LONGEDGEFIRST_FLIPPED:

        if (bXGreaterThanY)
        {
            // orientation 3 (or 1)
            FIXUP_FEEDDIRECTION(3, 1, LONGEDGEFIRST);
        }
        else
        {
            // orientation 2 (or 0)
            FIXUP_FEEDDIRECTION(2, 0, LONGEDGEFIRST);
        }
        break;
    }

    //
    // If the paper feed direction is not valid, we'll automatically
    // pick one here (default to long-edge-first if possible). This
    // should always change Long to Short, or Short to Long.
    //

    if (pCSData->wFeedDirection >= MAX_FEEDDIRECTION)
    {
        if (bXGreaterThanY)
        {
            if (ORIENTATION_AVAILABLE(1))
                pCSData->wFeedDirection = LONGEDGEFIRST;
            else if (ORIENTATION_AVAILABLE(3))
                pCSData->wFeedDirection = LONGEDGEFIRST_FLIPPED;
            else if (ORIENTATION_AVAILABLE(0))
                pCSData->wFeedDirection = SHORTEDGEFIRST;
            else // (ORIENTATION_AVAILABLE(2))
                pCSData->wFeedDirection = SHORTEDGEFIRST_FLIPPED;
        }
        else
        {
            if (ORIENTATION_AVAILABLE(0))
                pCSData->wFeedDirection = LONGEDGEFIRST;
            else if (ORIENTATION_AVAILABLE(2))
                pCSData->wFeedDirection = LONGEDGEFIRST_FLIPPED;
            else if (ORIENTATION_AVAILABLE(1))
                pCSData->wFeedDirection = SHORTEDGEFIRST;
            else // (ORIENTATION_AVAILABLE(3))
                pCSData->wFeedDirection = SHORTEDGEFIRST_FLIPPED;
        }
    }

    bShortEdgeFirst =
        (pCSData->wFeedDirection == SHORTEDGEFIRST ||
         pCSData->wFeedDirection == SHORTEDGEFIRST_FLIPPED);

    if ( (!bShortEdgeFirst && !LONGEDGEFIRST_SUPPORTED(pUIInfo, pPpdData)) ||
         (bShortEdgeFirst && !SHORTEDGEFIRST_SUPPORTED(pUIInfo, pPpdData)))
    {
        //
        // The other feeding direction we picked doesn't fit either, so we
        // have to stick with the original feeding direction.
        //

        pCSData->wFeedDirection = wFeedDirectionSave;

        //
        // Check the availability of orientations and flip the feed direction if necessary
        //

        if ((pCSData->wFeedDirection == LONGEDGEFIRST || pCSData->wFeedDirection == SHORTEDGEFIRST) &&
            !(ORIENTATION_AVAILABLE(0) || ORIENTATION_AVAILABLE(1)))
        {
            pCSData->wFeedDirection = (pCSData->wFeedDirection == LONGEDGEFIRST) ?
                LONGEDGEFIRST_FLIPPED : SHORTEDGEFIRST_FLIPPED;
        }
        else if ((pCSData->wFeedDirection == LONGEDGEFIRST_FLIPPED || pCSData->wFeedDirection == SHORTEDGEFIRST_FLIPPED) &&
            !(ORIENTATION_AVAILABLE(2) || ORIENTATION_AVAILABLE(3)))
        {
            pCSData->wFeedDirection = (pCSData->wFeedDirection == LONGEDGEFIRST_FLIPPED) ?
                LONGEDGEFIRST : SHORTEDGEFIRST;
        }

        return FALSE;
    }
    else
        return TRUE;
}



BOOL
BValidateCustomPageSizeData(
    IN PRAWBINARYDATA       pRawData,
    IN OUT PCUSTOMSIZEDATA  pCSData
    )

/*++

Routine Description:

    Validate the specified custom page size parameters, and
    Fix up any inconsistencies found.

Arguments:

    pRawData - Points to raw binary printer description data
    pCSData - Specifies the custom page size parameters to be validate

Return Value:

    TRUE if the custom page size parameters are valid, FALSE otherwise.
    If FALSE is returned, custom page size parameters have been
    fixed up to a consistent state.

--*/

{
    PUIINFO             pUIInfo;
    PPPDDATA            pPpdData;
    PPAGESIZE           pPageSize;
    CUSTOMSIZEDATA      csdata;
    PDWORD              pdwWidth, pdwHeight;
    DWORD               dwTemp;
    BOOL                bShortEdgeFirst, bXGreaterThanY;
    BOOL                bFit;

    pUIInfo = GET_UIINFO_FROM_INFOHEADER((PINFOHEADER) pRawData);
    pPpdData = GET_DRIVER_INFO_FROM_INFOHEADER((PINFOHEADER) pRawData);

    ASSERT(pUIInfo != NULL && pPpdData != NULL);

    if ((pPageSize = PGetCustomPageSizeOption(pUIInfo)) == NULL)
    {
        RIP(("Custom page size not supported\n"));
        return TRUE;
    }

    csdata = *pCSData;

    //
    // Width and height offset parameters are straightforward to verify
    //

    if ((LONG) csdata.dwWidthOffset < MINCUSTOMPARAM_WIDTHOFFSET(pPpdData))
        csdata.dwWidthOffset = MINCUSTOMPARAM_WIDTHOFFSET(pPpdData);
    else if ((LONG) csdata.dwWidthOffset > MAXCUSTOMPARAM_WIDTHOFFSET(pPpdData))
        csdata.dwWidthOffset = MAXCUSTOMPARAM_WIDTHOFFSET(pPpdData);

    if ((LONG) csdata.dwHeightOffset < MINCUSTOMPARAM_HEIGHTOFFSET(pPpdData))
        csdata.dwHeightOffset = MINCUSTOMPARAM_HEIGHTOFFSET(pPpdData);
    else if ((LONG) csdata.dwHeightOffset > MAXCUSTOMPARAM_HEIGHTOFFSET(pPpdData))
        csdata.dwHeightOffset = MAXCUSTOMPARAM_HEIGHTOFFSET(pPpdData);

    //
    // Validate cut-sheet vs. roll-fed selection
    //

    if (csdata.wCutSheet && !(pPpdData->dwCustomSizeFlags & CUSTOMSIZE_CUTSHEET))
        csdata.wCutSheet = FALSE;
    else if (!csdata.wCutSheet && !(pPpdData->dwCustomSizeFlags & CUSTOMSIZE_ROLLFED))
        csdata.wCutSheet = TRUE;

    //
    // Check if the specified paper feed direction can be satisfied
    //

    bFit = BFixupCustomSizeDataFeedDirection(pUIInfo, pPpdData, &csdata);

    //
    // If we haven't been able to fit the custom paper size in
    // correct feeding direction and orientation, then we have
    // to swap width and height here, because they will be opposite
    // of what PPD spec 4.3 page 109 Figure 3 specifies.
    //

    if (!bFit)
    {
        dwTemp = csdata.dwX;
        csdata.dwX = csdata.dwY;
        csdata.dwY = dwTemp;
    }

    //
    // Verify width and height parameters
    //

    bShortEdgeFirst =
        (csdata.wFeedDirection == SHORTEDGEFIRST ||
         csdata.wFeedDirection == SHORTEDGEFIRST_FLIPPED);

    bXGreaterThanY = (csdata.dwX > csdata.dwY);

    if ((bShortEdgeFirst && bXGreaterThanY) ||
        (!bShortEdgeFirst && !bXGreaterThanY))
    {
        // In this case: x <=> height, y <=> width

        pdwHeight = &csdata.dwX;
        pdwWidth = &csdata.dwY;
    }
    else
    {
        // In this case: x <=> width, y <=> height

        pdwWidth = &csdata.dwX;
        pdwHeight = &csdata.dwY;
    }

    if ((LONG) (*pdwWidth + csdata.dwWidthOffset) > pPageSize->szPaperSize.cx)
    {
        *pdwWidth = pPageSize->szPaperSize.cx - csdata.dwWidthOffset;

        if ((LONG) *pdwWidth < MINCUSTOMPARAM_WIDTH(pPpdData))
        {
            *pdwWidth = MINCUSTOMPARAM_WIDTH(pPpdData);
            csdata.dwWidthOffset = pPageSize->szPaperSize.cx - *pdwWidth;
        }
    }
    else if ((LONG) *pdwWidth < MINCUSTOMPARAM_WIDTH(pPpdData))
    {
        *pdwWidth = MINCUSTOMPARAM_WIDTH(pPpdData);
    }

    if ((LONG) (*pdwHeight + csdata.dwHeightOffset) > pPageSize->szPaperSize.cy)
    {
        *pdwHeight = pPageSize->szPaperSize.cy - csdata.dwHeightOffset;

        if ((LONG) *pdwHeight < MINCUSTOMPARAM_HEIGHT(pPpdData))
        {
            *pdwHeight = MINCUSTOMPARAM_HEIGHT(pPpdData);
            csdata.dwHeightOffset = pPageSize->szPaperSize.cy - *pdwHeight;
        }
    }
    else if ((LONG) *pdwHeight < MINCUSTOMPARAM_HEIGHT(pPpdData))
    {
        *pdwHeight = MINCUSTOMPARAM_HEIGHT(pPpdData);
    }

    //
    // Check if anything has changed and
    // return appropriate result value
    //

    if (memcmp(pCSData, &csdata, sizeof(csdata)) == 0)
        return TRUE;

    *pCSData = csdata;
    return FALSE;
}



VOID
VFillDefaultCustomPageSizeData(
    IN PRAWBINARYDATA   pRawData,
    OUT PCUSTOMSIZEDATA pCSData,
    IN BOOL             bMetric
    )

/*++

Routine Description:

    Initialize the custom page size parameters to their default values

Arguments:

    pRawData - Points to raw printer description data
    pCSData - Buffer for storing default custom page size parameters
    bMetric - Whether we're on a metric system

Return Value:

    NONE

--*/

{
    PPPDDATA    pPpdData;

    pPpdData = GET_DRIVER_INFO_FROM_INFOHEADER((PINFOHEADER) pRawData);

    ASSERT(pPpdData != NULL);

    //
    // Default to Letter or A4 depending on whether
    // we're on metric system or not
    //

    if (bMetric)
    {
        pCSData->dwX = 210000;  // 210mm
        pCSData->dwY = 297000; // 297mm
    }
    else
    {
        pCSData->dwX = 215900;  // 8.5"
        pCSData->dwY = 279400; // 11"
    }

    //
    // Get default offsets and feed direction
    //

    pCSData->dwWidthOffset = MINCUSTOMPARAM_WIDTHOFFSET(pPpdData);
    pCSData->dwHeightOffset = MINCUSTOMPARAM_HEIGHTOFFSET(pPpdData);

    pCSData->wFeedDirection =
        (pPpdData->dwCustomSizeFlags & CUSTOMSIZE_SHORTEDGEFEED) ?
            SHORTEDGEFIRST : LONGEDGEFIRST;

    //
    // Make sure the default custom page size parameters are consistent
    //

    (VOID) BValidateCustomPageSizeData(pRawData, pCSData);
}



VOID
VGetCustomSizeParamRange(
    IN PRAWBINARYDATA    pRawData,
    IN PCUSTOMSIZEDATA   pCSData,
    OUT PCUSTOMSIZERANGE pCSRange
    )

/*++

Routine Description:

    Return the valid ranges for custom page size width, height,
    and offset parameters based on their current values

Arguments:

    pRawData - Points to raw printer description data
    pCSData - Specifies the current custom page size parameter values
    pCSRange - Output buffer for returning custom page size parameter ranges
        It should point to an array of 4 CUSTOMSIZERANGE structures:
            0 (CUSTOMPARAM_WIDTH)
            1 (CUSTOMPARAM_HEIGHT)
            2 (CUSTOMPARAM_WIDTHOFFSET)
            3 (CUSTOMPARAM_HEIGHTOFFSET)

Return Value:

    NONE

--*/

{
    PUIINFO             pUIInfo;
    PPPDDATA            pPpdData;
    BOOL                bShortEdgeFirst, bXGreaterThanY;
    PPAGESIZE           pPageSize;
    CUSTOMSIZEDATA      csdata;
    PCUSTOMSIZERANGE    pWidthRange, pHeightRange, pTempRange;
    BOOL                bFit;

    pUIInfo = GET_UIINFO_FROM_INFOHEADER((PINFOHEADER) pRawData);
    pPpdData = GET_DRIVER_INFO_FROM_INFOHEADER((PINFOHEADER) pRawData);

    ASSERT(pUIInfo != NULL && pPpdData != NULL);

    pPageSize = PGetCustomPageSizeOption(pUIInfo);

    ASSERT(pPageSize != NULL);

    //
    // The range for width and height offsets are predictable
    //

    pCSRange[CUSTOMPARAM_WIDTHOFFSET].dwMin = MINCUSTOMPARAM_WIDTHOFFSET(pPpdData);
    pCSRange[CUSTOMPARAM_WIDTHOFFSET].dwMax = MAXCUSTOMPARAM_WIDTHOFFSET(pPpdData);
    pCSRange[CUSTOMPARAM_HEIGHTOFFSET].dwMin = MINCUSTOMPARAM_HEIGHTOFFSET(pPpdData);
    pCSRange[CUSTOMPARAM_HEIGHTOFFSET].dwMax = MAXCUSTOMPARAM_HEIGHTOFFSET(pPpdData);

    //
    // The range for width and height are affected by the selected paper feed direction
    //

    csdata = *pCSData;
    bFit = BFixupCustomSizeDataFeedDirection(pUIInfo, pPpdData, &csdata);

    bShortEdgeFirst =
        (csdata.wFeedDirection == SHORTEDGEFIRST ||
         csdata.wFeedDirection == SHORTEDGEFIRST_FLIPPED);

    bXGreaterThanY = (csdata.dwX > csdata.dwY);

    if ((bShortEdgeFirst && bXGreaterThanY) ||
        (!bShortEdgeFirst && !bXGreaterThanY))
    {
        //
        // Here user's logical x/y and custom page
        // size width/height are swapped
        //

        pWidthRange = pCSRange + CUSTOMPARAM_HEIGHT;
        pHeightRange = pCSRange + CUSTOMPARAM_WIDTH;
    }
    else
    {
        //
        // Here user's logical x/y correspond to
        // custom page size width/height
        //

        pWidthRange = pCSRange + CUSTOMPARAM_WIDTH;
        pHeightRange = pCSRange + CUSTOMPARAM_HEIGHT;
    }

    //
    // If we haven't been able to fit the custom paper size in
    // correct feeding direction and orientation, then we have
    // to swap width and height here, because they will be opposite
    // of what PPD spec 4.3 page 109 Figure 3 specifies.
    //

    if (!bFit)
    {
        pTempRange = pWidthRange;
        pWidthRange = pHeightRange;
        pHeightRange = pTempRange;
    }

    pWidthRange->dwMin = MINCUSTOMPARAM_WIDTH(pPpdData);
    pWidthRange->dwMax = MAXCUSTOMPARAM_WIDTH(pPpdData);
    pHeightRange->dwMin = MINCUSTOMPARAM_HEIGHT(pPpdData);
    pHeightRange->dwMax = MAXCUSTOMPARAM_HEIGHT(pPpdData);

    if (pWidthRange->dwMax > pPageSize->szPaperSize.cx - csdata.dwWidthOffset)
        pWidthRange->dwMax = pPageSize->szPaperSize.cx - csdata.dwWidthOffset;

    if (pHeightRange->dwMax > pPageSize->szPaperSize.cy - csdata.dwHeightOffset)
        pHeightRange->dwMax = pPageSize->szPaperSize.cy - csdata.dwHeightOffset;
}



BOOL
BFormSupportedThruCustomSize(
    PRAWBINARYDATA  pRawData,
    DWORD           dwX,
    DWORD           dwY,
    PWORD           pwFeedDirection
    )

/*++

Routine Description:

    Determine whether a form can be supported through custom page size

Arguments:

    pRawData - Points to raw printer description data
    dwX, dwY - Form width and height (in microns)
    pwFeedDirection - if not NULL, will be set to the selected feed direction

Return Value:

    TRUE if the form can be supported through custom page size
    FALSE if not. In that case, pwFeedDirection will be LONGEDGEFIRST.

--*/
{
    PPPDDATA        pPpdData = GET_DRIVER_INFO_FROM_INFOHEADER((PINFOHEADER) pRawData);
    static WORD     awPrefFeedDir[] = {
                                        LONGEDGEFIRST,
                                        SHORTEDGEFIRST
                                      };
    CUSTOMSIZEDATA  csdata;
    DWORD           i;

    for (i = 0; i < (sizeof(awPrefFeedDir)/sizeof(WORD)); i++)
    {
        csdata.dwX = dwX;
        csdata.dwY = dwY;
        csdata.dwWidthOffset =
        csdata.dwHeightOffset = 0;
        csdata.wCutSheet = TRUE;
        csdata.wFeedDirection = awPrefFeedDir[i];

        (VOID) BValidateCustomPageSizeData(pRawData, &csdata);

        if (dwX == csdata.dwX && dwY == csdata.dwY && csdata.wFeedDirection != MAX_FEEDDIRECTION)
        {
            if (pwFeedDirection != NULL)
                *pwFeedDirection = csdata.wFeedDirection; // might be flipped

            return TRUE;
        }
    }

    if (pwFeedDirection != NULL)
        *pwFeedDirection = LONGEDGEFIRST; // just set a safe default, the return value should be checked !

    return FALSE;
}