/*++

Copyright (c) 1990-1999  Microsoft Corporation


Module Name:

    devmode.c


Abstract:

    This module contains devmode conversion


Author:

    08-Jun-1995 Thu 13:47:33 created  -by-  Daniel Chou (danielc)


[Environment:]

    GDI printer drivers, user and kernel mode


[Notes:]


Revision History:

    11/09/95 -davidx-
        New conversion routines

    09-Feb-1999 Tue 11:15:55 updated  -by-  Daniel Chou (danielc)
        Move from printers\lib directory

--*/

#include "precomp.h"
#pragma hdrstop


//
// This is the devmode version 320 (DM_SPECVERSION)
//

#define DM_SPECVERSION320   0x0320
#define DM_SPECVERSION400   0x0400
#define DM_SPECVERSION401   0x0401
#define DM_SPECVER_BASE     DM_SPECVERSION320

//
// size of a device name string
//

#define CCHDEVICENAME320   32
#define CCHFORMNAME320     32

typedef struct _devicemode320A {
    BYTE    dmDeviceName[CCHDEVICENAME320];
    WORD    dmSpecVersion;
    WORD    dmDriverVersion;
    WORD    dmSize;
    WORD    dmDriverExtra;
    DWORD   dmFields;
    short   dmOrientation;
    short   dmPaperSize;
    short   dmPaperLength;
    short   dmPaperWidth;
    short   dmScale;
    short   dmCopies;
    short   dmDefaultSource;
    short   dmPrintQuality;
    short   dmColor;
    short   dmDuplex;
    short   dmYResolution;
    short   dmTTOption;
    short   dmCollate;
    BYTE    dmFormName[CCHFORMNAME320];
    WORD    dmLogPixels;
    DWORD   dmBitsPerPel;
    DWORD   dmPelsWidth;
    DWORD   dmPelsHeight;
    DWORD   dmDisplayFlags;
    DWORD   dmDisplayFrequency;
} DEVMODE320A, *PDEVMODE320A, *NPDEVMODE320A, *LPDEVMODE320A;

typedef struct _devicemode320W {
    WCHAR   dmDeviceName[CCHDEVICENAME320];
    WORD    dmSpecVersion;
    WORD    dmDriverVersion;
    WORD    dmSize;
    WORD    dmDriverExtra;
    DWORD   dmFields;
    short   dmOrientation;
    short   dmPaperSize;
    short   dmPaperLength;
    short   dmPaperWidth;
    short   dmScale;
    short   dmCopies;
    short   dmDefaultSource;
    short   dmPrintQuality;
    short   dmColor;
    short   dmDuplex;
    short   dmYResolution;
    short   dmTTOption;
    short   dmCollate;
    WCHAR   dmFormName[CCHFORMNAME320];
    WORD    dmLogPixels;
    DWORD   dmBitsPerPel;
    DWORD   dmPelsWidth;
    DWORD   dmPelsHeight;
    DWORD   dmDisplayFlags;
    DWORD   dmDisplayFrequency;
} DEVMODE320W, *PDEVMODE320W, *NPDEVMODE320W, *LPDEVMODE320W;



#ifdef UNICODE

typedef DEVMODE320W     DEVMODE320;
typedef PDEVMODE320W    PDEVMODE320;
typedef NPDEVMODE320W   NPDEVMODE320;
typedef LPDEVMODE320W   LPDEVMODE320;

#else

typedef DEVMODE320A     DEVMODE320;
typedef PDEVMODE320A    PDEVMODE320;
typedef NPDEVMODE320A   NPDEVMODE320;
typedef LPDEVMODE320A   LPDEVMODE320;

#endif // UNICODE


typedef struct _DMEXTRA400 {
    DWORD  dmICMMethod;
    DWORD  dmICMIntent;
    DWORD  dmMediaType;
    DWORD  dmDitherType;
    DWORD  dmICCManufacturer;
    DWORD  dmICCModel;
} DMEXTRA400;


typedef struct _DMEXTRA401 {
    DWORD  dmPanningWidth;
    DWORD  dmPanningHeight;
} DMEXTRA401;


#define DM_SIZE320  sizeof(DEVMODE320)
#define DM_SIZE400  (DM_SIZE320 + sizeof(DMEXTRA400))
#define DM_SIZE401  (DM_SIZE400 + sizeof(DMEXTRA401))

// Current version devmode size - public portion only

#ifdef  UNICODE
#define DM_SIZE_CURRENT sizeof(DEVMODEW)
#else
#define DM_SIZE_CURRENT sizeof(DEVMODEA)
#endif



WORD
CheckDevmodeVersion(
    PDEVMODE pdm
    )

/*++

Routine Description:

    Verify dmSpecVersion and dmSize fields of a devmode

Arguments:

    pdm - Specifies a devmode to be version-checked

Return Value:

    0 if the input devmode is unacceptable
    Otherwise, return the expected dmSpecVersion value

--*/

{
    WORD    expectedVersion;

    if (pdm == NULL)
        return 0;

    // Check against known devmode sizes

    switch (pdm->dmSize) {

    case DM_SIZE320:
        expectedVersion = DM_SPECVERSION320;
        break;

    case DM_SIZE400:
        expectedVersion = DM_SPECVERSION400;
        break;

    case DM_SIZE401:
        expectedVersion = DM_SPECVERSION401;
        break;

    default:
        expectedVersion = pdm->dmSpecVersion;
        break;
    }


    return expectedVersion;
}



LONG
ConvertDevmode(
    PDEVMODE pdmIn,
    PDEVMODE pdmOut
    )

/*++

Routine Description:

    Convert an input devmode to a different version devmode.

    Whenever driver gets an input devmode, it should call this
    routine to convert it to current version.

Arguments:

    pdmIn - Points to an input devmode
    pdmOut - Points to an initialized/valid output devmode

Return Value:

    Total number of bytes copied
    -1 if either input or output devmode is invalid

--*/

{
    WORD    dmSpecVersion, dmDriverVersion;
    WORD    dmSize, dmDriverExtra;
    LONG    cbCopied = 0;

    // Look for inconsistency between dmSpecVersion and dmSize

    if (! CheckDevmodeVersion(pdmIn) ||
        ! (dmSpecVersion = CheckDevmodeVersion(pdmOut)))
    {
        return -1;
    }

    // Copy public devmode fields

    dmDriverVersion = pdmOut->dmDriverVersion;
    dmSize = pdmOut->dmSize;
    dmDriverExtra = pdmOut->dmDriverExtra;

    cbCopied = min(dmSize, pdmIn->dmSize);
    memcpy(pdmOut, pdmIn, cbCopied);

    pdmOut->dmSpecVersion = dmSpecVersion;
    pdmOut->dmDriverVersion = dmDriverVersion;
    pdmOut->dmSize = dmSize;
    pdmOut->dmDriverExtra = dmDriverExtra;

    // Copy private devmode fields

    cbCopied += min(dmDriverExtra, pdmIn->dmDriverExtra);
    memcpy((PBYTE) pdmOut + pdmOut->dmSize,
           (PBYTE) pdmIn + pdmIn->dmSize,
           min(dmDriverExtra, pdmIn->dmDriverExtra));

    return cbCopied;
}


#if defined(UMODE) || defined(USERMODE_DRIVER)



BOOL
ConvertDevmodeOut(
    PDEVMODE pdmSrc,
    PDEVMODE pdmIn,
    PDEVMODE pdmOut
    )

/*++

Routine Description:

    Copy a source devmode to an output devmode buffer.

    Driver should call this routine before it returns to the caller
    of DrvDocumentProperties.

Arguments:

    pdmSrc - Points to a current version source devmode
    pdmIn - Points to input devmode passed to DrvDocumentProperties
    pdmOut - Output buffer pointer passed to DrvDocumentProperties

Return Value:

    TRUE if successful, FALSE otherwise

--*/

{
    if (pdmIn == NULL) {

        memcpy(pdmOut, pdmSrc, pdmSrc->dmSize + pdmSrc->dmDriverExtra);
        return TRUE;

    } else {

        // We have to deal with the public fields and private fields
        // separately. Also remember pdmIn and pdmOut may point to
        // the same buffer.

        // Public fields: take dmSpecVersion and dmSize from
        // the smaller of pdmSrc and pdmIn

        if (pdmIn->dmSize < pdmSrc->dmSize) {

            pdmOut->dmSpecVersion = pdmIn->dmSpecVersion;
            pdmOut->dmSize        = pdmIn->dmSize;

        } else {

            pdmOut->dmSpecVersion = pdmSrc->dmSpecVersion;
            pdmOut->dmSize        = pdmSrc->dmSize;
        }

        // Similarly for private fields

        if (pdmIn->dmDriverExtra < pdmSrc->dmDriverExtra) {

            pdmOut->dmDriverVersion = pdmIn->dmDriverVersion;
            pdmOut->dmDriverExtra   = pdmIn->dmDriverExtra;

        } else {

            pdmOut->dmDriverVersion = pdmSrc->dmDriverVersion;
            pdmOut->dmDriverExtra   = pdmSrc->dmDriverExtra;
        }

        return ConvertDevmode(pdmSrc, pdmOut) > 0;
    }
}



INT
CommonDrvConvertDevmode(
    PWSTR    pPrinterName,
    PDEVMODE pdmIn,
    PDEVMODE pdmOut,
    PLONG    pcbNeeded,
    DWORD    fMode,
    PDRIVER_VERSION_INFO pDriverVersions
    )

/*++

Routine Description:

    Library routine to handle common cases of DrvConvertDevMode

Arguments:

    pPrinterName, pdmIn, pdmOut, pcbNeeded, fMode
        Correspond to parameters passed to DrvConvertDevMode
    pDriverVersions - Specifies driver version numbers and private devmode sizes

Return Value:

    CDM_RESULT_TRUE
        If the case is handled by the library routine and driver
        shoud return TRUE to the caller of DrvConvertDevMode.

    CDM_RESULT_FALSE
        If the case is handled by the library routine and driver
        shoud return FALSE to the caller of DrvConvertDevMode.

    CDM_RESULT_NOT_HANDLED
        The case is NOT handled by the library routine and driver
        should continue on with whatever it needs to do.

--*/

{
    LONG    size;

    // Make sure pcbNeeded parameter is not NULL

    if (pcbNeeded == NULL) {

        SetLastError(ERROR_INVALID_PARAMETER);
        return CDM_RESULT_FALSE;
    }

    switch (fMode) {

    case CDM_CONVERT:

        // Convert any input devmode to any output devmode.
        // Both input and output must be valid.

        if (pdmOut != NULL &&
            *pcbNeeded >= (pdmOut->dmSize + pdmOut->dmDriverExtra) &&
            ConvertDevmode(pdmIn, pdmOut) > 0)
        {
            *pcbNeeded = pdmOut->dmSize + pdmOut->dmDriverExtra;
            return CDM_RESULT_TRUE;
        }
        break;

    case CDM_CONVERT351:

        // Convert any input devmode to 3.51 version devmode
        // First check if the caller provided buffer is large enough

        size = DM_SIZE320 + pDriverVersions->dmDriverExtra351;

        if (*pcbNeeded < size || pdmOut == NULL) {

            *pcbNeeded = size;
            SetLastError(ERROR_INSUFFICIENT_BUFFER);
            return CDM_RESULT_FALSE;
        }

        // Do the conversion from input devmode to 3.51 devmode

        pdmOut->dmSpecVersion = DM_SPECVERSION320;
        pdmOut->dmSize = DM_SIZE320;
        pdmOut->dmDriverVersion = pDriverVersions->dmDriverVersion351;
        pdmOut->dmDriverExtra = pDriverVersions->dmDriverExtra351;

        if (ConvertDevmode(pdmIn, pdmOut) > 0) {

            *pcbNeeded = size;
            return CDM_RESULT_TRUE;
        }

        break;

    case CDM_DRIVER_DEFAULT:

        // Convert any input devmode to current version devmode
        // First check if the caller provided buffer is large enough

        size = DM_SIZE_CURRENT + pDriverVersions->dmDriverExtra;

        if (*pcbNeeded < size || pdmOut == NULL) {

            *pcbNeeded = size;
            SetLastError(ERROR_INSUFFICIENT_BUFFER);
            return CDM_RESULT_FALSE;
        }

        // This case (getting driver-default devmode) is not handled
        // by the library routine.

        *pcbNeeded = size;

        // FALL THROUGH TO THE DEFAULT CASE!

    default:
        return CDM_RESULT_NOT_HANDLED;
    }

    SetLastError(ERROR_INVALID_PARAMETER);
    return CDM_RESULT_FALSE;
}

#endif // USER MODE

