/*++

Copyright (c) 1995  Microsoft Corporation

Module Name:

    forms.c

Abstract:

    Functions for manipulating forms

Environment:

	PCL-XL driver, user and kernel mode

Revision History:

	11/07/95 -davidx-
		Created it.

	mm/dd/yy -author-
		description

--*/

#include "xllib.h"



BOOL
ValidDevmodeForm(
    HANDLE       hPrinter,
    PDEVMODE     pdm,
    PFORM_INFO_1 pFormInfo,
    PWSTR        pFormName
    )

/*++

Routine Description:

    Validate the form specification in a devmode

Arguments:

    hPrinter - Handle to the printer object
    pdm - Pointer to the input devmode
    pFormInfo - Pointer to a FORM_INFO_1 structure for returning
        information about the logical form specified by the input devmode
    pFormName - Buffer to storing form name and pFormInfo->pName is set to
        this upon successful return

Return Value:

    TRUE if the input devmode specifies a valid logical form
    FALSE otherwise

--*/

{
    PFORM_INFO_1 pForm, pFormDB;
    DWORD        cForms;

    Assert(pFormInfo != NULL && pFormName != NULL);

    //
    // Get a list of forms in the system
    //

    if (! (pForm = pFormDB = GetFormsDatabase(hPrinter, &cForms))) {

        Error(("Couldn't get system forms\n"));
        return FALSE;
    }

    if (CustomDevmodeForm(pdm)) {
    
        LONG width, height;
    
        //
        // Devmode is specifying a custom form whose width
        // and height are measured in 0.1mm. Convert the unit to micron.
        //
    
        width = pdm->dmPaperWidth * 100;
        height = pdm->dmPaperLength * 100;
    
        //
        // Go through the forms database and check if
        // one of the forms has the same size as what's
        // being requested. The tolerance is 1mm.
        //

        while ((cForms != 0) &&
               (abs(width  - pForm->Size.cx) > FORMSIZE_TOLERANCE ||
                abs(height - pForm->Size.cy) > FORMSIZE_TOLERANCE))
        {
            cForms--;
            pForm++;
        }
    
        //
        // Custom size doesn't match that of any defined forms
        //
    
        if (cForms == 0) {
    
            pFormInfo->Flags = 0;
            pFormInfo->Size.cx = width;
            pFormInfo->Size.cy = height;
            pFormInfo->ImageableArea.left =
            pFormInfo->ImageableArea.top = 0;
            pFormInfo->ImageableArea.right = width;
            pFormInfo->ImageableArea.bottom = height;

            pFormInfo->pName = pFormName;
            pFormName[0] = NUL;
    
            MemFree(pFormDB);
            return TRUE;
        }
    
    } else if (pdm->dmFields & DM_PAPERSIZE && pdm->dmPaperSize >= DMPAPER_FIRST) {

        //
        // Devmode is specifying a form using paper size index.
        //

        DWORD index = pdm->dmPaperSize - DMPAPER_FIRST;

        if (index < cForms)
            pForm = pFormDB + index;

    } else if (pdm->dmFields & DM_FORMNAME) {

        //
        // Devmode is specifying a form using form name. Go through the forms database
        // and check if the requested form name matches that of a form in the database.
        //

        while (cForms && wcscmp(pForm->pName, pdm->dmFormName) != EQUAL_STRING)
            cForms--, pForm++;

        if (cForms == 0)
            pForm = NULL;
    }

    //
    // If devmode is specifying a valid logical form, then return the form information
    // in the provided buffer. Also update the devmode fields.
    //

    if (pForm != NULL) {

        *pFormInfo = *pForm;
        CopyStringW(pFormName, pForm->pName, CCHFORMNAME);
        pFormInfo->pName = pFormName;

        //
        // Convert paper size unit from microns to 0.1mm
        //

        pdm->dmPaperWidth = pForm->Size.cx / 100;
        pdm->dmPaperLength = pForm->Size.cy / 100;
        pdm->dmPaperSize = (SHORT) (pForm - pFormDB);
        pdm->dmFields |= DM_FORMNAME;
        CopyStringW(pdm->dmFormName, pForm->pName, CCHFORMNAME);
    }

    MemFree(pFormDB);
    return pForm != NULL;
}



BOOL
MapToPrinterForm(
    PMPD         pmpd,
    PFORM_INFO_1 pFormInfo,
    PPRINTERFORM pPrinterForm,
    BOOL         bStringent
    )

/*++

Routine Description:

    Map a logical form to a printer paper size

Arguments:

    pmpd - Pointer to printer description data
    pFormInfo - Pointer to logical form information
    pPrinterForm - Pointer to a buffer for returning printer form information
    bStringent - Whether to use more stringent criteria

Return Value:

    TRUE if the logical form is supported on the printer
    FALSE otherwise

--*/

{
    LONG        width, height;
    WORD        selection;
    PFEATURE    pFeature;
    PPAPERSIZE  pPaperSize, pNext;

    width = pFormInfo->Size.cx;
    height = pFormInfo->Size.cy;

    //
    // First check if the logical form name matches the name of a printer form.
    //

    Assert(pFormInfo->pName != NULL);

    pFeature = MpdPaperSizes(pmpd);
    Assert(pFeature->size == sizeof(PAPERSIZE));

    pPaperSize = FindNamedSelection(pFeature, pFormInfo->pName, &selection);

    if (pPaperSize == NULL && (!bStringent || IsUserDefinedForm(pFormInfo))) {

        LONG dx, dy, minxy;
        WORD index;

        //
        // There is no name match. Try to find out if there is a
        // printer form whose size matches that of the logical form.
        //

        minxy = MAX_LONG;

        for (index=0; index < pFeature->count; index++) {

            pNext = FindIndexedSelection(pFeature, index);

            //
            // Compare the current size with the desired size.
            //
            
            dx = pNext->size.cx - width;
            dy = pNext->size.cy - height;
            
            //
            // Check if we have an exact size match. Tolerance is 1mm.
            //
            
            if (abs(dx) <= FORMSIZE_TOLERANCE && abs(dy) <= FORMSIZE_TOLERANCE) {
            
                selection = index;
                pPaperSize = pNext;
                break;
            }
            
            //
            // Not an exact match, see if we could fit on this form.
            //
            
            if (dx >= 0 && dy >= 0) {
            
                //
                // Check to see if the current form is smaller than
                // the smallest one we've found so far.
                //
            
                if (dx+dy < minxy) {
            
                    //
                    // Tentatively remember it as the smallest size.
                    //
            
                    selection = index;
                    pPaperSize = pNext;
                    minxy = dx + dy;
                }
            }
        }

        //
        // If there is no exact size match and the printer supports
        // custom paper size and the requested size is not too big,
        // then go ahead and select custom paper size on the printer.
        //

        if (index == pFeature->count && SupportCustomSize(pmpd, width, height)) {

            if (pPrinterForm) {

                pPrinterForm->name[0] = NUL;
                pPrinterForm->size = pFormInfo->Size;
                pPrinterForm->imageableArea = pFormInfo->ImageableArea;
                pPrinterForm->selection = SELIDX_ANY;
            }

            return TRUE;
        }
    }

    //
    // If the logical form is mapped to a printer paper size, then
    // return information about the printer paper size to the caller
    //

    if (pPaperSize && pPrinterForm) {

        pPrinterForm->imageableArea = pPaperSize->imageableArea;
        RectIntersect(&pPrinterForm->imageableArea, &pFormInfo->ImageableArea);

        pPrinterForm->size = pPaperSize->size;
        pPrinterForm->selection = selection;
        CopyStringW(pPrinterForm->name, pPaperSize->pName, CCHFORMNAME);
    }

    return (pPaperSize != NULL);
}



PFORM_INFO_1
GetFormsDatabase(
    HANDLE  hPrinter,
    PDWORD  pCount
    )

/*++

Routine Description:

    Return a collection of forms in the spooler database

Arguments:

    hPrinter - Handle to a printer object
    pCount - Points to a variable for returning total number of forms

Return Value:

    Pointer to an array of FORM_INFO_1 structures if successful
    NULL otherwise

--*/

{
    PFORM_INFO_1 pFormDB = NULL;
    DWORD        cbNeeded;

    if (!EnumForms(hPrinter, 1, NULL, 0, &cbNeeded, pCount) &&
        GetLastError() == ERROR_INSUFFICIENT_BUFFER &&
        (pFormDB = MemAlloc(cbNeeded)) != NULL &&
        EnumForms(hPrinter, 1, (PBYTE) pFormDB, cbNeeded, &cbNeeded, pCount))
    {
        return pFormDB;
    }

    MemFree(pFormDB);
    return NULL;
}



VOID
FilterFormsDatabase(
    PFORM_INFO_1 pFormDB,
    DWORD        cForms,
    PMPD         pmpd
    )

/*++

Routine Description:

    Determine which system forms are supported on the given printer

Arguments:

    pFormDB - Points to a list of forms in the system
    cForms - Number of forms
    pmpd - Points to printer description data

Return Value:

    NONE

--*/

{
    PRINTERFORM printerForm;

    while (cForms--) {

        //
        // Make sure the highest order bits are not used by the spooler
        //

        Assert(!IsSupportedForm(pFormDB));

        if (MapToPrinterForm(pmpd, pFormDB, &printerForm, TRUE)) {

            SetSupportedFormIndex(pFormDB, printerForm.selection);
        }

        pFormDB++;
    }
}