/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT (C) Microsoft Corporation, 1989                                 */
/* COPYRIGHT    Copyright (C) 1995 IBM Corporation                           */
/*                                                                           */
/*    The following IBM OS/2 WARP source code is provided to you solely for  */
/*    the purpose of assisting you in your development of OS/2 WARP device   */
/*    drivers. You may use this code in accordance with the IBM License      */
/*    Agreement provided in the IBM Device Driver Source Kit for OS/2. This  */
/*    Copyright statement may not be removed.                                */
/*                                                                           */
/*****************************************************************************/
/**************************************************************************
 *
 * SOURCE FILE NAME = QUERY.C
 *
 * DESCRIPTIVE NAME = Query Utilities
 *
 *
 * VERSION = V2.0
 *
 * DATE
 *
 * DESCRIPTION : Contains query related routines.
 *
 *
 * FUNCTIONS   - QueryDeviceBitmaps
 *             - QueryDeviceCaps
 *             - QueryHardCopyCaps
 *             - DevQueryDeviceNames
 *
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/

#define  INCL_DEV
#define  INCL_ERRORS
#define  INCL_WINERRORS
#include "inc\prdinclt.h"
#include <pmdev.h>
#include "inc\prdqextf.h"
#include "inc\utl.h"
#include "inc\prdgextf.h"
#include "inc\prdmath.h"
#include "inc\prdtextf.h"
#include "inc\prdeextf.h"
#define  INCL_WINSHELLDATA
#include <pmshl.h>
#include "inc\prderre.h"
#include "inc\config.h"
#include "inc\pspagtun.h"             /* V2.174057  Page Tuning */
#include <string.h>
#define  INCL_GENPLIB_ERROR
#define  INCL_GENPLIB_MEMORY
#include <genplib.h>

extern CHAR szKeyPaperName[];
extern PFNL pfnlQueryDeviceCaps;       /* prdevect.c                        */
extern VOID FreeFontResource(PDV,PFONTDEF,PFNT);/* charstr2.c        */
extern PFONTMETRICS GetPFMetrics(PFONTDEF);/* charstr2.c         */
extern PFNT PfntFromIndex(PDDC,SHORT);
extern PBYTE StringTable[MAX_STRINGS]; /* devmode.c                         */
extern PVOID pProcessHeap;
extern SHORT szDlmCopy(PSZ,PSZ,SHORT);
extern PFORMSTRUCT GetImageableArea( SHORT, PFORMSTRUCT, PDESPPD, PBYTE );
extern INT  _Optlink CompareRealNames( PSZ, PSZ ); /*            */

/*
**  Put these in for DosCallBack                                      
*/

extern VOID QueryUserForm2(VOID);
extern PDDC EnterDriver(PDDC);
extern void ExitDriver(PDDC);
#define  N_SYSCOLORS   16              /* Number of standard colors         */
#define  COLORS_SUPPORTED 512          /* A MAGIC NUMBER FOR COLOR
                                          PRINTERS.                         */
#define  OFS_WIDTH     7
#define  OFS_HEIGHT    9
#define  DEF_FONT_SIZE 12              /* Default font size is 12 points    */
#define  FX_DEF_FONT_SIZE 0xc0000L     /* Default font size is 12 points    */
#define  FX_PNTS_PER_INCH 0x480000L    /* There are 72 points per inch      */
#define  MMSIN10INCH   252             /* mms in 10 inch                    */
#define  PELSPINV      720             /* units of 1/72 " in 10 inches      */
#define  MMS           7
/*
**  7/20 approaches closest to 254/720 and error is negligible
*/
#define  INCH          20

#define  TYPES_SUPPORTED 2             /* PM_Q_STD and PM_Q_RAW.            */
#define  DEVNAMELENGTH 32
#define  DEVDESCLENGTH 64

#ifndef  CAPS_ENHANCED_FONTMETRICS
   #define  CAPS_ENHANCED_FONTMETRICS 0x2000L
#endif
/* systemversion is changed from 120 to 200 for os2 2.0*/
#define  SYSTEMVERSION 0x00000200L

/***************************************************************************
 *
 * FUNCTION NAME = prdq_QueryDeviceBitmaps
 *
 * DESCRIPTION   = This routine returns the number of planes and bits per
 *                 pixel this device supports.
 *
 * INPUT         =  hdc    - The display context handle
 *                  pulOut - Ptr to the output buffer
 *                  nOut   - Number of 32 bit entries in the output buffer
 *                  pddc   - Ptr to the DC instance data
 *                  ifn    - The engine's function number
 *
 * OUTPUT        = ULONG
 *
 * RETURN-NORMAL = SUCCESS
 * RETURN-ERROR  = FAILURE
 *          
 *
 **************************************************************************/

ULONG prdq_QueryDeviceBitmaps (HDC hdc,ULONG *pulOut,LONG nOut,
                                                        PDDC pddc,ULONG ulFunN)

/*
**  HDC           hdc;      The display context handle
**  ULONG  *   pulOut;   Ptr to the output buffer
**  LONG          nOut;     Number of 32 bit entries in the output buffer
**  PDDC          pddc;     Ptr to the DC instance data
**  ULONG         ifn;      The engine's function number
*/

{
    ULONG ulRet ;

    EnterDriver( pddc );

  #if      DEBUG
    LogCall ("prdq_QueryDeviceBitmaps(%08lx, %lp, %ld, %lp, %08lx)", ((PB)&hdc)+
             sizeof(hdc));
  #endif  /* DEBUG                            */

  if (pddc->iType == OD_MEMORY)
  {
    ulRet = InnerGreQueryDeviceBitmaps (pddc->hdcMemory, pulOut,
                                        nOut, ulFunN);
  }
  else
  {
    if (nOut < 2)
    {
      RIP( "QueryDeviceBitmaps: invalid number of entries in output buffer");
      GplErrSetError( PMERR_INV_LENGTH_OR_COUNT);
      ExitDriver( pddc );
      return( FAILURE );
    }

    /*
    ** Return the number of bit planes and bits per pixel that this
    ** device supports.
    **
    ** Output[0] = number of bit planes.
    ** Output[1] = number of bits per pel.
    */
    *pulOut++ = pddc->pdv->canvas.nBitPlanes;
    *pulOut++ = pddc->pdv->canvas.nBitsPerPel;
    nOut -= 2;

    /*
    ** Zero out the remainder of the output array.
    */
    while (--nOut >= 0)
    {
      *pulOut++ = 0L;
    }

    ulRet = SUCCESS;
  }

  ExitDriver( pddc );

  return( ulRet );
}

/***************************************************************************
 *
 * FUNCTION NAME = szColonCopy
 *
 * DESCRIPTION   = Duplicates an null terminated or delimiter terminated
 *                 string with bounds checking to prevent overflow of the
 *                 destination buffer.
 *
 * INPUT         = register PSZ pszDst  - Ptr to the destination buffer
 *                 register PSZ pszSrc  - Ptr to the source string
 *                 register SHORT nbDst - Size of the destination buffer
 *
 * OUTPUT        = SHORT
 *
 * RETURN-NORMAL = SHORT i
 * RETURN-ERROR  =
 *
 ****************************************************************************/

SHORT szColonCopy( register PSZ pszDst,register PSZ pszSrc,register
                   SHORT nbDst)

/*
** register PSZ pszDst;        Ptr to the destination buffer
** register PSZ pszSrc;        Ptr to the source string
** register SHORT nbDst;       Size of the destination buffer
*/

{
  SHORT i;

  i = 0;

  while (*pszDst++ = *pszSrc++)
  {
    i++;

    if (nbDst-- <= 0 || *(pszSrc-1) == ';' || *(pszSrc-1) == '\0')
    {
      i--;
      *--pszDst = 0;
      break;
    }
  }
  return( i );
}

/***************************************************************************
 *
 * FUNCTION NAME = NewUserForms()
 *
 * DESCRIPTION   = Returns the number of user defined forms.
 *
 * INPUT         = PCNFDATA pcnfData
 *
 * OUTPUT        = SHORT
 *
 * RETURN-NORMAL = usCount
 * RETURN-ERROR  = 0
 *          
 *
 **************************************************************************/

SHORT NewUserForms(PCNFDATA pcnfData)
{
  SHORT iIndex,usCount;
  ULONG ulCount;
  PSZ   pszUserForms = NULL;
  CHAR  szBuffer[MAX_PSIZE];

  /*
  **  read the apps keyname setting from os2.ini file                    
  */
  if (!PrfQueryProfileSize( HINI_SYSTEMPROFILE, (PSZ)pcnfData->szKeyApp, (PSZ)
                            "USERFORMS", (PULONG)&ulCount))
  {
    return( 0 );
  }

  /*
  **  allocate memory so that info can be read from os2.ini
  */
  if (!(pszUserForms = (PSZ)GplMemoryAlloc( pProcessHeap, (LONG)ulCount )))
  {
    return( 0 );
  }
  PrfQueryProfileString( HINI_SYSTEMPROFILE, (PSZ)pcnfData->szKeyApp, (PSZ)
                         "USERFORMS", (PSZ)"", (PSZ)pszUserForms, (ULONG)ulCount );

  /*
  ** the form name mappings are in the format
  ** <form name>;<form size>;;<form name>;<form size> ...
  */
  iIndex = 0;
  usCount = 0;

  while (iIndex < (SHORT)(ulCount-1))
  {
    /*
    **  skip the form name and size name and scan for next form
    */
    /*
    ** Skip the form name.
    */
    while (*(pszUserForms+iIndex++) != ';')
      ;

    /*
    ** Skip the form size.
    */
    while (*(pszUserForms+iIndex++) != ';')
      ;

    iIndex++;                          /* skip the second semicolon         */
    usCount++;
  }
  GplMemoryFree((PB)pszUserForms );
  return( usCount );
}

/***************************************************************************
 *
 * FUNCTION NAME = NewUserFormName
 *
 * DESCRIPTION   = scans the forms name recorded in os2.ini ;returns the user
 *                 supplied form name and actual form name.
 *
 * INPUT         = pcnfData
 *                 usIndex
 *                 pszUserName
 *                 pszActualName
 *
 * OUTPUT        = void
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *          
 *
 **************************************************************************/

void NewUserFormName( PCNFDATA pcnfData, SHORT sIndex, PSZ pszUserName,
                      PSZ pszActualName)
{
  SHORT  iIndex, cb;
  ULONG  ulCount;
  PSZ    pszUserForms = NULL;
  CHAR   szBuffer[MAX_PSIZE];
  CHAR   szCustom[MAX_PSIZE];
  SHORT *pshDimension;
  PSZ    pszTemp;

  *pszUserName = '\0';
  *pszActualName = '\0';

  if (!PrfQueryProfileSize( HINI_SYSTEMPROFILE, (PSZ)pcnfData->szKeyApp,
                            (PSZ) "USERFORMS", (PULONG)&ulCount))
  {
    return;
  }

  /*
  **  allocate memory so that info can be read from os2.ini
  */
  if (!(pszUserForms = (PSZ)GplMemoryAlloc( pProcessHeap, (LONG)ulCount )))
  {
    return;
  }
  PrfQueryProfileString( HINI_SYSTEMPROFILE, (PSZ)pcnfData->szKeyApp,
                         (PSZ) "USERFORMS", (PSZ)"", (PSZ) pszUserForms,
                         (ULONG) ulCount);

  /*
  **  the form name mappings are in the format
  */
  /*
  **  <form name>;<form size>;;<form name>;<form size> ...
  */
  iIndex = 0;

  while (iIndex < (SHORT)(ulCount-1) && sIndex--)
  {
    /*
    **  skip the form name and size name and compare with next form
    */
    /*
    ** Skip the form name.
    */
    while (*(pszUserForms + iIndex++) != ';');

    /*
    ** Skip the form size.
    */
    while (*(pszUserForms + iIndex++) != ';');
    iIndex++;                          /* skip the second semicolon         */
  }

  /*
  ** now supply the user form name and actual form name .But we need to
  ** scan for custom name .If custom then enter the dimensions also
  */
  if (sIndex <= 0 && iIndex < (SHORT) (ulCount - 1))
  {
    szColonCopy( (PSZ) pszUserName, (PSZ)(pszUserForms + iIndex), MAX_PSIZE );

    /*
    ** Skip the form name.
    */
    while (*(pszUserForms + iIndex++) != ';' );
    szColonCopy( (PSZ)pszActualName, (PSZ) (pszUserForms + iIndex), MAX_PSIZE );

    /*
    ** compare first six bytes with custom and if a match then return
    ** dimensions as well .
    */
    szCopy( (PSZ) szCustom, (PSZ) pszActualName, MAX_PSIZE );
    szCustom[6] = '\0';
    pszTemp = (PSZ) GetR3String( (SHORT) IDS_Custom );

    if ( ! CompareRealNames( (PSZ)szCustom, pszTemp) )
/**** ; *****  TEST */
    {
      *(pszActualName + 6) = '\0';       /* make it null terminated */
      pshDimension = (SHORT *)(pszActualName + 7);
      iIndex = 0;
      cb = 7;

      /*
      ** the numbers are stored along with custom in the format
      ** custom(no1,no2)
      ** Make the custom null terminated and store the numbers in
      ** binary form .
      */

      while (*(szCustom+cb) >= '0' && *(szCustom+cb) <= '9')
      {
        iIndex = 10 * iIndex + (*(szCustom + cb++) - 0x30 );
      }
      cb++;                            /* skip the comma                    */
      *pshDimension++ = iIndex;
      iIndex = 0;

      while (*(szCustom + cb) >= '0' && *(szCustom + cb) <= '9')
      {
        iIndex = 10 * iIndex + (*(szCustom + cb++) - 0x30);
      }
      cb++;                            /* skip the comma                    */
      *pshDimension = iIndex;
    }
  }
  GplMemoryFree((PB)pszUserForms );
}

/***************************************************************************
 *
 * FUNCTION NAME = prdq_QueryDeviceCaps
 *
 * DESCRIPTION   = This call sets the color value for a given pixel.
 *
 * INPUT         = hdc    The DC handle
 *                 iItem  Index of the first item
 *                 pItems Ptr to place to store the item info
 *                 nItems The number of items requested
 *                 pddc   Ptr to the DC instance data
 *                 FunN   The function number
 *
 * OUTPUT        = ULONG
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *          
 *                                                                    
 *
 **************************************************************************/

ULONG prdq_QueryDeviceCaps( HDC hdc, LONG iItem, ULONG *pItems,
                            LONG nItems, PDDC pddc, ULONG FunN )

/*
**  HDC hdc;                     The DC handle
**  LONG iItem;                  Index of the first item
**  ULONG  *pItems;           Ptr to place to store the item info
**  LONG nItems;                 The number of items requested
**  PDDC pddc;                   Ptr to the DC instance data
**  ULONG FunN;                  The function number
*/

{
  ULONG        CapValue;
  FIXED        fxCanvas;
  PFONTDEF     pfdFont;
  PFONTMETRICS pfmMetrics;
  LONG         lAveCharWidth;
  FIXED        fxAveCharWidth;
  LONG         *plddcaps;                  /* display driver dev caps           */
  ULONG        ulCapsMax = CAPS_DEVICE_POLYSET_POINTS;

  EnterDriver( pddc );

  #if      DEBUG
    LogCall( "prdq_QueryDeviceCaps(%08lx, %ld, %lp, %ld, %lp, %08lx)", ((PB) &hdc)
             +sizeof(hdc) );
  #endif                                 /* DEBUG                             */

  PrintLog( (PSZ)"pddc->pdv->canvas.rcl = {%ld, %ld, %ld, %ld}\n",
  pddc->pdv->canvas.rcl.xLeft, pddc->pdv->canvas.rcl.yTop,
  pddc->pdv->canvas.rcl.xRight, pddc->pdv->canvas.rcl.yBottom );

  /*
  ** If this is a memory DC, then we need to the devcaps from the
  ** the display driver.
  */
#if 0
//  if (pddc->iType == OD_MEMORY)
//  {
//    if (!(plddcaps = (LONG *)GplMemoryAlloc( pddc->pdv->pDCHeap,
//                                             ulCapsMax * sizeof (LONG) )))
//    {
//      RIP( "prdq_QueryDeviceCaps: memory allocation failed" );
//      GplErrSetError( PMERR_BASE_ERROR );
//      return( FAILURE );
//    }
//
//    if (!((ULONG)(*apfnlDisplay[(NGreQueryDeviceCaps&0x0ff)])(hdc, 0L,
//       plddcaps, ulCapsMax, pddc->hddc, NGreQueryDeviceCaps)))
//    {
//      RIP( "prdq_QueryDeviceCaps: Display driver DevCaps failed" );
//      return( FAILURE );
//    }
//  }
#endif

  /*
  ** Check the input parameters for validity.  If they are
  ** invalid, then fail.  if iItem is an invalid number, other
  ** than < 0, we just return 0L.  otherwise, we fail if someone
  ** tries to get all our capabilities.
  */
  if ((!pItems) || (iItem < 0) || (nItems < 1))
  {
    RIP( "prdq_QueryDeviceCaps: invalid input paramter" );
    GplErrSetError( PMERR_INV_QUERY_ELEMENT_NO );
    ExitDriver( pddc );
    return( FAILURE );
  }

  if (pddc->iType == OD_MEMORY)
  {
     if (!(plddcaps = (LONG *)GplMemoryAlloc( pddc->pdv->pDCHeap,
                                              ulCapsMax * sizeof (LONG) )))
    {
       RIP( "prdq_QueryDeviceCaps: memory allocation failed" );
       GplErrSetError( PMERR_BASE_ERROR );
       ExitDriver( pddc );
       return( FAILURE );
    }

    if (!((ULONG) InnerGreQueryDeviceCaps (pddc->hdcMemory, 0L, plddcaps,
                                           ulCapsMax, FunN)))
    {
       RIP( "prdq_QueryDeviceCaps: Display driver DevCaps failed" );
       ExitDriver( pddc );
       return( FAILURE );
    }
  }

  /*
  ** We loop once for each device cap item that we need to
  ** return.    This is just a simple loop with a big switch
  ** statement that computes the current device cap value.
  ** The device cap value is stored at the end of the loop.
  */
  while (--nItems >= 0)
  {
    switch ((SHORT)iItem++)
    {
    case CAPS_FAMILY:
         /*
         ** Temporary Usage: Device Family hard-wired to 5.
         ** Device Family is documented in [WIN] as taking values
         ** as for type on OpenDC, but not clear what this is...as
         ** the DC Type changes depending on whether the DC is
         ** as Direct, Indirect etc.
         */
         CapValue = OD_DIRECT;
         break;

    case CAPS_IO_CAPS:
         /*
         ** Device Input/Output capability : 2 => Device supports
         ** output.
         */
         CapValue = 2L;
         break;

    case CAPS_TECHNOLOGY:
         /*
         ** Technology: 3 => Raster printer.
         **
         ** PostScript is actually a cross between a vector and a
         ** raster device.  The reason that RASTER was chosen is
         ** to prevent applications from doing weird things trying
         ** to utilize the device as if it were a plotter.
         */
         CapValue = CAPS_TECH_POSTSCRIPT;
         break;

    case CAPS_DRIVER_VERSION:
         /*
         ** this might seem wrong, but the engine expects to find
         ** the engine number the driver was written for here.
         */
         CapValue = SYSTEMVERSION;
         break;

    case CAPS_HEIGHT:
         /*
         ** Default Page Depth in pixels.
         */
         CapValue = pddc->pdv->canvas.rcl.yTop-pddc->pdv->canvas.rcl.yBottom;
         PrintLog( (PSZ)"CAPS_HEIGHT = %ld\n", CapValue );
         break;

    case CAPS_WIDTH:
         /*
         **  Default Page Width in pixels.
         */
         CapValue = pddc->pdv->canvas.rcl.xRight-pddc->pdv->canvas.rcl.xLeft;
         PrintLog( (PSZ)"CAPS_WIDTH = %ld\n", CapValue );
         break;

    case CAPS_HEIGHT_IN_CHARS:
         if (pddc->iType == OD_MEMORY)
         {
           CapValue = (pddc->pdv->canvas.rcl.yTop-pddc->pdv->canvas.rcl.yBottom
              )/plddcaps[CAPS_CHAR_HEIGHT];
           break;
         }

         /*
         **  Compute the useable canvas height in inches.
         */
         CapValue = (pddc->pdv->canvas.rcl.yTop-pddc->pdv->canvas.rcl.yBottom );

         /*
         ** The default font might be any device font (as chosen
         ** from the dialog box) but we always know its point size.
         ** Compute the number of row's of default text that fit
         ** on the canvas by multiplying the height in inches by
         ** 72 to get the canvas height in points, and then
         ** dividing by that default font size in points.
         ** Multiplication by 6 is done as
         ** multiplication by 72 and division by 12 where 12 is the
         ** default font height .
         */
         CapValue = CapValue *6/(ULONG)pddc->pdv->canvas.iRes;
         PrintLog( (PSZ)"CAPS_HEIGHT_IN_CHARS = %ld\n", CapValue );
         break;

    case CAPS_WIDTH_IN_CHARS:

         if (pddc->iType == OD_MEMORY)
         {
           CapValue = (pddc->pdv->canvas.rcl.xRight-pddc->pdv->canvas.rcl.xLeft)
                        / plddcaps[CAPS_CHAR_WIDTH];
           break;
         }

         /*
         **  Compute the useable canvas width in points.
         */
         CapValue = (pddc->pdv->canvas.rcl.xRight-pddc->pdv->canvas.rcl.xLeft );

         /*
         **  Determine the average width of characters (in points)
         **  for characters in the default font shown in the
         **  default size.
         */
         CapValue = CapValue *72/(ULONG)pddc->pdv->canvas.iRes;
         pfdFont = prda_LoadFontResource(pddc, prda_DefaultFontIndex(pddc) );

         if (!pfdFont)                  /* Couldn't get resource             */
         {
           RIP( "prdq_QueryDeviceCaps:  LoadFontResource failed" );
           ExitDriver( pddc );
           return( FAILURE );            /* Minor function saves error code   */
         }
         pfmMetrics = GetPFMetrics(pfdFont );
         lAveCharWidth = pfmMetrics->lAveCharWidth;
         fxAveCharWidth = frdiv(frmul(LongToFx(lAveCharWidth), FX_DEF_FONT_SIZE
            ), FX_1000 );
         FreeFontResource(pddc->pdv, pfdFont, PfntFromIndex(pddc, prda_DefaultFontIndex
            (pddc)));

         /*
         ** Compute how many average character widths will fit on a
         ** line of the canvas.
         */
         CapValue = CapValue/FxToLong(fxAveCharWidth );
         PrintLog( (PSZ)"CAPS_WIDTH_IN_CHARS = %ld\n", CapValue );
         break;

    case CAPS_VERTICAL_RESOLUTION:
    case CAPS_HORIZONTAL_RESOLUTION:
         /*
         ** We compute the vertical and horizontal resolution by
         ** converting the resolution from pixels per inch to pixels
         ** per meter.  This is done by using the fact that there
         ** are approximately 39.37 inches per meter.
         **
         ** It is assumed that all PostScript printers have a 1:1
         ** aspect ratio so that the vertical and horizontal
         ** resolutions are identical.
         */
         CapValue = (((ULONG)pddc->pdv->canvas.iRes)*3937L)/100L;
         PrintLog( (PSZ)"CAPS_RES = %ld\n", CapValue );
         break;

    case CAPS_CHAR_HEIGHT:         /* Default char height in device
                                        pels                              */
    case CAPS_GRAPHICS_CHAR_HEIGHT:
         if (pddc->iType == OD_MEMORY)
         {
           CapValue = plddcaps[CAPS_CHAR_HEIGHT];
           break;
         }
         CapValue = (ULONG)((DEF_FONT_SIZE *pddc->pdv->canvas.iRes)/72 );
         PrintLog( (PSZ)"CAPS_CHAR_HEIGHT = %ld\n", CapValue );
         break;

    case CAPS_CHAR_WIDTH:          /* Default char width in device pels */
    case CAPS_GRAPHICS_CHAR_WIDTH:
         if (pddc->iType == OD_MEMORY)
         {
           CapValue = plddcaps[CAPS_CHAR_HEIGHT];
           break;
         }
         CapValue = (ULONG)((DEF_FONT_SIZE *pddc->pdv->canvas.iRes)/72 );
         PrintLog( (PSZ)"CAPS_CHAR_WIDTH = %ld\n", CapValue );
         break;

    case CAPS_SMALL_CHAR_HEIGHT:   /* Default small char height in pels */
         if (pddc->iType == OD_MEMORY)
         {
           CapValue = plddcaps[CAPS_SMALL_CHAR_HEIGHT];
           break;
         }

    case CAPS_SMALL_CHAR_WIDTH:    /* Default small char width in pels  */
         if (pddc->iType == OD_MEMORY)
         {
           CapValue = plddcaps[CAPS_CHAR_WIDTH];
           break;
         }
         CapValue = (ULONG)((DEF_FONT_SIZE *pddc->pdv->canvas.iRes)/72 );
         PrintLog( (PSZ)"CAPS_SMALL_CHAR_DIMENSION = %ld\n", CapValue );
         break;

    case CAPS_COLORS:              /* Number of distinct colors         */
         if (pddc->iType == OD_MEMORY)
         {
           CapValue = plddcaps[CAPS_COLORS];
         }
         else
         {
           CapValue = pddc->pdv->canvas.nGrayShades;

                 /*
                 ** how many colors does a color printer support?
                 ** I don't know.  in theory, it should support
                 ** CapValue cubed.  however, this is not reality
                 ** due to pigment limitations etc.  512 sounds like
                 ** good number.
                                                                             */

           if (pddc->pdv->jobProperties.fIsColorDevice)
             CapValue = COLORS_SUPPORTED;
         }
         break;

    case CAPS_COLOR_PLANES:
         if (pddc->iType == OD_MEMORY)
         {
           CapValue = plddcaps[CAPS_COLOR_PLANES];
         }
         else
         {
           CapValue = pddc->pdv->canvas.nBitPlanes;
         }
         break;

    case CAPS_COLOR_BITCOUNT:
         if (pddc->iType == OD_MEMORY)
         {
           CapValue = plddcaps[CAPS_COLOR_BITCOUNT];
         }
         else
         {
           CapValue = pddc->pdv->canvas.nBitsPerPel;
         }
         break;

    case CAPS_COLOR_TABLE_SUPPORT:
         /*
         **   CR For COLOR_TABLE_SUPPORT, we return zero like the
         **   CR EGA driver.
         */
         if (pddc->iType == OD_MEMORY)
         {
           CapValue = plddcaps[CAPS_COLOR_TABLE_SUPPORT];
         }
         else
         {
           CapValue = 0;
         }
         break;

    case CAPS_MOUSE_BUTTONS:
         CapValue = 0;
         break;

    case CAPS_FOREGROUND_MIX_SUPPORT:
         /* ForeGround Mix support.                                      */
         if (pddc->iType == OD_MEMORY)
         {
           CapValue = plddcaps[CAPS_FOREGROUND_MIX_SUPPORT];
         }
         else
         {
           CapValue = CAPS_FM_OVERPAINT;
         }
         break;

    case CAPS_BACKGROUND_MIX_SUPPORT:/*                                 */
         /*  background overpaint support added                          */
         if (pddc->iType == OD_MEMORY)
         {
           CapValue = plddcaps[CAPS_BACKGROUND_MIX_SUPPORT];
         }
         else
         {
           CapValue = CAPS_BM_LEAVEALONE|CAPS_BM_OVERPAINT;
         }
         break;

    case CAPS_VIO_LOADABLE_FONTS:  /* Number of VIO fonts               */
         if (pddc->iType == OD_MEMORY)
         {
           CapValue = plddcaps[CAPS_VIO_LOADABLE_FONTS];
         }
         else
         {
           CapValue = 0;
         }
         break;

    case CAPS_WINDOW_BYTE_ALIGNMENT:/* VIO window byte alignment        */
         CapValue = 0;
         break;

    case CAPS_BITMAP_FORMATS:
         /*
         **  PostScript supports one bitmap format: monochrome.
         */
         if (pddc->iType == OD_MEMORY)
         {
           CapValue = plddcaps[CAPS_BITMAP_FORMATS];
         }
         else
         {
           CapValue = 1L;
         }
         break;

    case CAPS_RASTER_CAPS:
         /*
         ** PostScript supports all of the raster capabilities.
         **    Bit 0 = bitblt supported
         **    Bit 1 = this device supports banding
         **    Bit 2 = bitblt with scaling supported
         **    Bit 3 = reserved
         **    Bit 4 = SetPel supported
         */
         if (pddc->iType == OD_MEMORY)
         {
           CapValue = plddcaps[CAPS_RASTER_CAPS];
         }
         else
         {
           CapValue = (ULONG)(1|4|16 );
         }
         break;

    case CAPS_MARKER_HEIGHT:       /* Deflt markr-box wdth - pels       */
         /*
         **  default to height of 8 points = 8/72 inch
         */
         if (pddc->iType == OD_MEMORY)
         {
           CapValue = plddcaps[CAPS_MARKER_HEIGHT];
         }
         else
         {
           CapValue = (ULONG)((pddc->pdv->canvas.iRes *8)/72 );
         }
         break;

    case CAPS_MARKER_WIDTH:        /* Deflt markr-box dpth - pels       */
         /*
         **  default to width of 8 points = 8/72 inch
         */
         if (pddc->iType == OD_MEMORY)
         {
           CapValue = plddcaps[CAPS_MARKER_WIDTH];
         }
         else
         {
           CapValue = (ULONG)((pddc->pdv->canvas.iRes *8)/72 );
         }
         break;

    case CAPS_DEVICE_FONTS:
         if (pddc->iType == OD_MEMORY)
         {
           CapValue = plddcaps[CAPS_DEVICE_FONTS];
         }
         else
         {
           CapValue = (ULONG)prda_NumPrinterFonts( pddc );
         }
         break;

    case CAPS_GRAPHICS_SUBSET:
    case CAPS_GRAPHICS_VERSION:
    case CAPS_GRAPHICS_VECTOR_SUBSET:
         CapValue = 0;
         break;

    case CAPS_DEVICE_WINDOWING:
         /*
         **  This device does not support windowing
         */
         CapValue = 0;
         break;

    case CAPS_ADDITIONAL_GRAPHICS:
         /*
         ** Additional graphics support.
         **   Bit 0  device supports geometric line types.
         **   Bit 1  device supports kerning.
         **   Bit 2  device can supply default vector font.
         **   Bit 3  device can supply default raster font.
         **   Bit 4  device will manage default vector font.
         **   Bit 5  device will manage default raster font.
         **   Bit 13 device will handle new FONTMETRICS
         */
         if (pddc->iType == OD_MEMORY)
         {
           CapValue = plddcaps[CAPS_ADDITIONAL_GRAPHICS]
                      &~CAPS_FONT_OUTLINE_DEFAULT
                      &~CAPS_FONT_IMAGE_DEFAULT
                      &~CAPS_FONT_OUTLINE_MANAGE
                      &~CAPS_FONT_IMAGE_MANAGE
                      &~CAPS_ENHANCED_FONTMETRICS
                      | CAPS_CLIP_FILLS ;         /* turn on the pre-clip */
         }
         else
         {
           CapValue = BIT0|             /* BIT0                              */
              CAPS_GRAPHICS_KERNING_SUPPORT|/* BIT1                          */
              CAPS_FONT_OUTLINE_DEFAULT|/* BIT2                              */
              CAPS_FONT_IMAGE_MANAGE|   /* BIT5                              */
              CAPS_ENHANCED_FONTMETRICS|/* BIT13                             */
              CAPS_COSMETIC_WIDELINE_SUPPORT; /* defect 71494                */
         }
         PrintLog( (PSZ)"CAPS_ADDITIONAL_GRAPHICS requested: returning %ld\n",
            CapValue );
         break;

    case CAPS_PHYS_COLORS:
         if (pddc->iType == OD_MEMORY)
         {
           CapValue = plddcaps[CAPS_PHYS_COLORS];
         }
         else
         {

           /*
           ** Color printers can do cyan, magenta, yellow, black and white
           ** Monochrome printers can only do black and white.
           */
           if (pddc->pdv->jobProperties.fIsColorDevice)
           {
             CapValue = 5;
           }
           else
           {
             CapValue = 2;
           }
         }
         break;

    case CAPS_COLOR_INDEX:
         if (pddc->iType == OD_MEMORY)
         {
           CapValue = plddcaps[CAPS_COLOR_INDEX];
         }
         else
         {
           CapValue = N_MAXCOLORS-N_SYSCOLORS-1;
         }
         break;

    case CAPS_HORIZONTAL_FONT_RES:
    case CAPS_VERTICAL_FONT_RES:
         CapValue = pddc->pdv->canvas.iRes;
         break;

    case CAPS_DEVICE_FONT_SIM:
         CapValue = 0L;
         break;

    case CAPS_LINEWIDTH_THICK:
    case CAPS_DEVICE_POLYSET_POINTS:
    default:
         CapValue = 0L;
         break;
    }                                  /* end case                         */
    *pItems++ = CapValue;
  }                                    /* end while                         */

  if (pddc->iType == OD_MEMORY)
  {
    GplMemoryFree((PB *)plddcaps );
  }
  PrintLog( (PSZ)"Successfully leaving prdq_QueryDeviceCaps\n" );
  ExitDriver( pddc );
  return( SUCCESS );
}

/***************************************************************************
 *
 * FUNCTION NAME = PageAtPaper
 *
 * DESCRIPTION   = Given a pageno this routine finds out on what page is
 *                 this to be printed.
 *
 * INPUT         = pcnfData
 *                 pszBuffer
 *                 pshTrayno
 *
 * OUTPUT        = void
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *          
 *                                                                    
 *
 **************************************************************************/

void PageAtPaper( PCNFDATA pcnfData, PSZ pszBuffer, SHORT *pshTrayno )
{
  SHORT i = 0;
  PSZ   pszPaperName;
  CHAR  szFormName[MAX_PSIZE+1];        /* holds name of Form                */
  SHORT usCxVar;
  SHORT usDiVar;

  /*
  ** Call QueryUserForm to resolve any user defined forms
  **
  ** we should have the form name.  we now need to match it to
  ** a form size.  queryuserform will check the form
  ** name.  if it is a pre-define form, then the form
  ** size will be the same as the form name.  otherwise,
  ** it will search os2sys.ini for the form size.
  */

  QueryUserForm( pcnfData );

  szCopy( (PSZ) pszBuffer, (PSZ) pcnfData->jobProperties.szFormSize, MAX_PSIZE );

  if (pcnfData->jobProperties.iManualfeed == 1)
  {
    *pshTrayno = MANUAL;
    return ;
  }

  /*
  ** now we need to look through the list which associates tray to papername
  ** for a match, then set pshTrayno to the tray index so we can later get
  ** the correct paper tray select control string from the resources
  */
  *pshTrayno = 0;                      /* provide a default                 */
  szCopy((PSZ)szFormName, (PSZ)pcnfData->jobProperties.szFormName, MAX_PSIZE );

  while (i < INPBINS)
  {
    pszPaperName = &(pcnfData->u.iv.pSourcePaper->szPaperName[i][0] );

    if ( ! CompareRealNames( (PSZ)szFormName, (PSZ)pszPaperName) )
    {
      *pshTrayno = i;
      return ;
    }
    else
    {
      i++;
    }
  }
}

/***************************************************************************
 *
 * FUNCTION NAME = LoadPaperDim
 *
 * DESCRIPTION   = This routine returns pointers to currently selected
 *                 paper dimns.The currently selected paper is indicated
 *                 by pddc -the input argument.
 *
 * INPUT         = pddc
 *                 piPapersize
 *                 piImagesize
 *                 piResdpi
 *                 pabPapernames
 *                 lFirst
 *                 plnForms
 *
 * OUTPUT        = BOOL
 *
 * RETURN-NORMAL = TRUE
 * RETURN-ERROR  = FALSE
 *                                                                    
 *
 **************************************************************************/

BOOL LoadPaperDim( PDDC pddc,SHORT *piPapersize,SHORT *piImagesize,
                   SHORT *piResdpi,CHAR *pabPapernames,LONG lFirst,
                   LONG *plnForms )
{
  SHORT    i, j, k, l, m, n;
  PSZ      pszTemp;
  PCNFDATA pcnfData;
  PDESPPD  pdesPpd;
  /*
  **  scratch buffer to have def paper name
  */
  CHAR     szPaper[MAX_PSIZE];
  PB      *apResources[IMAXRES];
          /*
          ** array of pointer to following resources:-
          ** ppb file header ,
          ** directory buffer ,
          ** itemsbuffer ,
          ** configuration buffer ,
          ** ppd parameter buffer .
          */
  SHORT *piDim;
  FORMSTRUCT FormStruct;


  for (i = 0; i < IMAXRES; i++)
  {
    apResources[i] = NULL;
  }

  /*
  ** Allocate space to store the information specific to a given
  ** printer configuration parameters.
  */
  if (!(apResources[CNFRES] = (PVOID)GplMemoryAlloc( pProcessHeap,
                                                     sizeof(CNFDATA) )))
  {
    RIP( "LoadPaperDim: can't allocate CNFDATA instance" );
    return( FALSE );
  }
  pcnfData = (PCNFDATA)apResources[CNFRES];
  szCopy( (PSZ)pcnfData->szSegName, (PSZ)pddc->pdv->szSegName, sizeof
          (pcnfData->szSegName) );
  pcnfData->u.iv.pSourcePaper = &pddc->pdv->sourcePaper;

  /*
  ** need szKeyApp for QueryUserForm                                     
  */
  szCopy( (PSZ)pcnfData->szKeyApp, (PSZ)pddc->pdv->szKeyApp, sizeof
          (pcnfData->szKeyApp) );

  /*
  ** Read the Printer info segment from Resources appended.
  */
  pcnfData->lGetPtr = 0;

  if (!LoadInfoSegment((PB *)apResources))
  {
    RIP( "LoadPaperDim: LoadInfoSegment failed" );
    return( FALSE );
  }

  /*
  **  store the paper name in array which corresponds to the current page
  */
  PageAtPaper( (PCNFDATA) pcnfData, (PSZ) szPaper, (SHORT *) &m );
  pdesPpd = (PDESPPD)apResources[PPDRES];

  /*
  ** if count of forms specified is zero return the count supported
  ** which will include user defined as well as printer supported forms
  */
  /*
  **  szKeyApp set above                  
  **  REMOVED SZCOLONCOPY CALL                                          
  */

  if (*plnForms <= 0)
  {
    /*
    **  count of such pairs
    */
    *plnForms = (long)pdesPpd->desPage.iImgpgpairs+(long)NewUserForms
                 ((PCNFDATA)pcnfData );
    FreeMemory((PB *)apResources);
    return( TRUE );
  }
  *piResdpi = pdesPpd->desItems.iResDpi;

  /*
  **  offset to paper name,command pairs
  */
  j = pdesPpd->desPage.ofsImgblPgsz;

  /*
  **  count of such pairs
  */
  k = pdesPpd->desPage.iImgpgpairs+NewUserForms((PCNFDATA)pcnfData );
  i = 0;
  n = 0;

#if 0    /* Dont need this */
//while (i < (int)lFirst && i < pdesPpd->desPage.iImgpgpairs)
//{
//  /*
//  ** skip the first lFirst -1 paper names and do not supply parameters
//  ** for such names
//  */
//  l = *((CHAR *)apResources[RESBUF]+j );
//  j += l+1;                          /* skip name                        */
//  j += 8;                            /* skip image coordinates           */
//  i++;
//}
#endif
  /*
  ** if the start form index i is still less than lFirst it means
  ** all predefined forms have been skipped and some user defined forms
  ** also need to be skipped. Just put i equal to lFirst
  */
  i = (SHORT)lFirst;

  /*
  **  we can't return more than the number of forms supported
  */
  if (*plnForms > (k-lFirst))
  {
    *plnForms = k-lFirst;
  }

  /*
  **    The command buffer contains paper name followed by command to select the
  **    paper.  Scan for paper name ,copy the paper name to supplied buffer.
  */
  while (i < (int)(lFirst+*plnForms))
  {
    /*
    ** if i < ppd paper types get paper name from ppd resources
    ** else get paper name from os2.ini
    */
    if (i < pdesPpd->desPage.iImgpgpairs)
    {
      GetImageableArea( i, (PFORMSTRUCT)&FormStruct, pdesPpd,
                        (PBYTE)apResources[RESBUF] );
      strcpy( pabPapernames+n, FormStruct.FormName );
#if 0
//    l = *((CHAR *)apResources[RESBUF]+j );
//
//    for (m = 0; m < l; m++)
//    {
//      pabPapernames[n+m] = *((CHAR *)apResources[RESBUF]+j+m+1 );
//    }
//    pabPapernames[n+l] = '\0';
#endif
      /*
      **  get page dimensions and copy them to supplied buffer
      */
      piDim = PaperDimensions((PSZ)(pabPapernames+n), (PB *)apResources );

      /*
      ** if a valid pointer returned then only copy the dimensions.
      ** this check is redundant because such a case won't arise.
      */
      if (piDim)
      {
        *piPapersize++ = *piDim++;
        *piPapersize++ = *piDim;
      }

      /*
      **  get image dimensions and copy them to supplied buffer
      */
      piDim = ImageCoords((PSZ)(pabPapernames+n), (PB *)apResources );

      /*
      ** if a valid pointer returned then only copy the dimensions.
      ** this check is redundant because such a case won't arise.
      */
      if (piDim)
      {
        *piImagesize++ = *piDim++;
        *piImagesize++ = *piDim++;
        *piImagesize++ = *piDim++;
        *piImagesize++ = *piDim;
      }
      j += ( l+1 );
      j += 8;                          /* skip the coordinates pairs        */
    }
    else
    {
      /*
      ** get user form name and the name it is mapped to .The name it
      ** is mapped to is required to retrieve dimensions
      */
      NewUserFormName((PCNFDATA)pcnfData, (SHORT)(i-
         pdesPpd->desPage.iImgpgpairs), (PSZ)(pabPapernames+n), (PSZ)szPaper );

      /*
      ** if the actual form name returned is custom then the dimensions
      ** are returned in szPaper buffer itself
      */
      pszTemp = (PSZ)GetR3String((SHORT)IDS_Custom );

      if ( ! CompareRealNames( (PSZ)pszTemp, (PSZ)szPaper) )
      {
        *piPapersize++ = *(int *)(szPaper+OFS_WIDTH );
        *piPapersize++ = *(int *)(szPaper+OFS_HEIGHT );
        *piImagesize++ = 0;            /* this is left clip value           */
        *piImagesize++ = 0;            /* this is bottom clip value         */

        /*
        **  this is right clip value
        */
        *piImagesize++ = *(int *)(szPaper+OFS_WIDTH );

        /*
        **  this is top clip value
        */
        *piImagesize++ = *(int *)(szPaper+OFS_HEIGHT );
      }
      else
      {
        /*
        **  get page dimensions and copy them to supplied buffer
        */
        piDim = PaperDimensions((PSZ)szPaper, (PB *)apResources );

        /*
        ** if a valid pointer returned then only copy the dimensions.
        ** this check is redundant because such a case won't arise.
        */
        if (piDim)
        {
          *piPapersize++ = *piDim++;
          *piPapersize++ = *piDim;
        }

        /*
        **  get image dimensions and copy them to supplied buffer
        */
        piDim = ImageCoords((PSZ)szPaper, (PB *)apResources );

        /*
        ** if a valid pointer returned then only copy the dimensions.
        ** this check is redundant because such a case won't arise.
        */
        if (piDim)
        {
          *piImagesize++ = *piDim++;
          *piImagesize++ = *piDim++;
          *piImagesize++ = *piDim++;
          *piImagesize++ = *piDim;
        }
      }
    }
    n += 32;
    i++;
  }

  FreeMemory( (PB *)apResources );
  return( TRUE );
}

/***************************************************************************
 *
 * FUNCTION NAME = prdq_QueryHardcopyCaps
 *
 * DESCRIPTION   = If the application is asking for the number of forms, then
 *                 it will pass in zero as the form count.  In this case, the
 *                 driver just returns the number of forms available on the
 *                 printer.
 *
 * INPUT         = hdc    - The display context handle
 *                 iFirst - Index of the first form information to return
 *                 nForms - The number of forms requested
 *                 phci   - Pointer to the hardware caps info structure
 *                 pddc   - Ptr to the DC instance data
 *                 ifn    - The GRE function number
 *
 * OUTPUT        = LONG
 *
 * RETURN-NORMAL = nForms
 * RETURN-ERROR  = ERROR_NEG,
 *                 -1L
 *          
 *
 **************************************************************************/

LONG prdq_QueryHardcopyCaps (HDC hdc,LONG iFirst,LONG nForms,
                             PHCINFO phci,PDDC pddc,ULONG ifn)

/*
** HDC             hdc;        The display context handle
** LONG            iFirst;     Index of the first form information to return
** LONG            nForms;     The number of forms requested
** PHCINFO         phci;       Pointer to the hardware caps info structure
** PDDC            pddc;       Ptr to the DC instance data
** ULONG           ifn;        The GRE function number
*/

{
  LONG   li, lj, lScale;
  SHORT  i,j,k,iBindex;
  PSHORT paiPapersize;
  PSHORT paiImagesize;
  PCHAR  pabPapernames;
  PCHAR  pabPapernamesSave;
  SHORT  iResdpi;
  RECTL  rclImage;
  LONG   lRes;
  LONG   lTemp;
  PSZ    pszDefaultFormName;
  CHAR   szDefaultClipped[MAX_PSIZE+1];  /* temp buffer for clipped name      */
  /* buffer to read string from os2.ini */
  CHAR    szBuffer[128];
  /* buffer to hold the current paper type */
  CHAR    szPaper[128];
  PCHAR   pchB;
  PCHAR   pchP;
  PDV     pdv = pddc->pdv;

  #define  FX_ONEBY72    0x0038DL        /* Multiplier 1/72                   */

  /*
  **  Instead of division by 72 ,numerator is shifted right 3 bits and
  **  divided by 9
  */
  #define  N72BY8        9

  EnterDriver( pddc );

  #if      DEBUG
    LogCall( "prdq_QueryHardcopyCaps(%lp, %ld, %ld, %lp, %lp, %lp)", ((PB) &hdc)+
             sizeof(hdc) );
  #endif                                 /* DEBUG                             */

  lRes = pdv->canvas.iRes;
  lScale = (LONG)pdv->jobProperties.uScale;

  if (nForms > 0 && phci == NULL)
  {
    RIP( "QueryHardCopyCaps: nForms > 0 && phci == NULL" );
    GplErrSetError( DQHC_ERROR );
    ExitDriver( pddc );
    return( ERROR_NEG );
  }

  if (nForms > 0)
  {
    /*
    **  allocate memory for papersize and image dimensions
    */
    if (!(paiPapersize = (PVOID)GplMemoryAlloc( pdv->pDCHeap,
                                                (LONG)(nForms << 2) )))
    {
      RIP( "QueryHardCopyCaps: memory allocation failed" );
      GplErrSetError( DQHC_ERROR );
      ExitDriver( pddc );
      return( ERROR_NEG );
    }

    if (!(paiImagesize = (PVOID)GplMemoryAlloc( pdv->pDCHeap,
                                               (LONG)(nForms << 3) )))
    {
      RIP( "QueryHardCopyCaps: memory allocation failed" );
      GplErrSetError( DQHC_ERROR );
      ExitDriver( pddc );
      return( ERROR_NEG );
    }

    if (!(pabPapernames = (PVOID)GplMemoryAlloc( pdv->pDCHeap,
                                                 (LONG)(nForms *MAX_PSIZE))))
    {
      RIP( "QueryHardCopyCaps: memory allocation failed" );
      GplErrSetError( DQHC_ERROR );
      ExitDriver( pddc );
      return( ERROR_NEG );
    }

    pabPapernamesSave = pabPapernames;

    if (!LoadPaperDim( pddc, (SHORT *)paiPapersize, (SHORT *)
       paiImagesize, (SHORT *)&iResdpi, (CHAR *)pabPapernames, (long)
       iFirst, (LONG *)&nForms))
    {
      RIP( "QueryHardCopyCaps: LoadPaperDim failed" );
      GplErrSetError( DQHC_ERROR );
      ExitDriver( pddc );
      return( ERROR_NEG );
    }

    /*
    **  Read the list of all loaded form names from os2.ini
    */
    PrfQueryProfileString( HINI_PROFILE, pdv->szKeyApp, (PSZ)
                           szKeyPaperName, (PSZ)"", (PSZ)szBuffer,
                           (ULONG)sizeof(szBuffer) );

    /*
    ** the information about various forms is available : go on filling
    ** this information into the supplied area
    */
    i = j = k = 0;

    while (i++ < (int)nForms)
    {
      /*
      **  !!!CR paiImagesize and paiPapersize should be pointers to
      **  structures
      */
      szCopy((PSZ)phci->szFormname, (PSZ)pabPapernames, sizeof
             (phci->szFormname) );
      pabPapernames += 32;             /* skip the name transferred to phci */

      /*
      ** Compute the size of the imageable area in pels.
      ** Some further checks to ensure that imageable area doesn't
      ** exceed paper size are also included.
      */
      if (paiPapersize[j] < paiImagesize[k+2])
      {
        paiImagesize[k+2] = paiPapersize[j];
      }
      phci->xPels = (((long)paiImagesize[k+2])*lRes)/72L-(((long)paiImagesize
                      [k])*lRes) / 72L;

      if (paiPapersize[j+1] < paiImagesize[k+3])
      {
        paiImagesize[k+3] = paiPapersize[j+1];
      }
      phci->yPels = (((long)paiImagesize[k+3])*lRes)/72L-(((long)paiImagesize
         [k+1])*lRes)/72L;

      /*
      ** these changes done at the request of excel who want imageable
      ** area and pels to be scaled also
      */
      phci->xPels = phci->xPels *100L/lScale;
      phci->yPels = phci->yPels *100L/lScale;

      /*
      ** Calculate the imageable area and paper size in millimeters
      **
      ** Convert to millimeters is done by multiplying by the
      ** ratio of 254/720.  This was derived from the following
      ** facts:
      **     There are 25.4 millimeters per inch .
      **     There are 72 printers points per inch.
      */
      phci->xLeftClip = ((long)paiImagesize[k]*254L)/720L;
      phci->yBottomClip = ((long)paiImagesize[k+1]*254L)/720L;
      phci->xRightClip = ((long)paiImagesize[k+2]*254L)/720L;
      phci->yTopClip = ((long)paiImagesize[k+3]*254L)/720L;
      /*          
      ** Round numbers to match table in 2.0 programming guide table 18.1
      */
      phci->cx = (((((long)paiPapersize[j++])*2540L)/720L)+5) / 10;
      phci->cy = (((((long)paiPapersize[j++])*2540L)/720L)+5) / 10;

      /*
      ** the following change done at the request of excel who want
      ** the imageable area to be scaled as per the inverse of scaling
      ** factor specified by user.
      */
      phci->xLeftClip = phci->xLeftClip *100L/lScale;
      phci->yBottomClip = phci->yBottomClip *100L/lScale;
      phci->xRightClip = phci->xRightClip *100L/lScale;
      phci->yTopClip = phci->yTopClip *100L/lScale;
      phci->cx = phci->cx *100L/lScale;
      phci->cy = phci->cy *100L/lScale;

      /*
      ** If LANDSCAPE orientation is in effect then rotate
      ** the coordinates by 90 degrees.
      */
      if (pdv->jobProperties.iOrient != PORTRAIT)
      {
        lTemp = phci->cx;
        phci->cx = phci->cy;
        phci->cy = lTemp;
        lTemp = phci->xLeftClip;
        phci->xLeftClip = phci->yBottomClip;
        phci->yBottomClip = lTemp;
        lTemp = phci->xRightClip;
        phci->xRightClip = phci->yTopClip;
        phci->yTopClip = lTemp;
        lTemp = phci->xPels;
        phci->xPels = phci->yPels;
        phci->yPels = lTemp;
      }

      /*
      ** HCAPS_CURRENT means that this form was the one specified on
      ** the job properties in the DrivData structure that I used on
      ** the DevOpenDC from which I got the hDC for the
      ** DevQueryHardCopyCaps(hDC).
      **
      ** HCAPS_SELECTABLE means that the printer has this form loaded
      ** in one of its trays.  This flag could be set on three forms
      ** if you have three paper trays.
      **
      ** HCAPS_CURRENT is set typically on only one form.  HOWEVER,
      ** this might not be a subset the forms tagged with
      ** HCAPS_SELECTABLE.  I think that the spooler could actually
      ** be       in checking the
      ** HCAPS_CURRENT form in deciding if it should schedule a job.
      */
      /*
      ** Compare the form being returned with the forms selected.
      ** If the names match mark the form as current .
      */
      /*
      **  Modify code to pick a default Form Name if             
      **  JOBPROPS IS NULL OSDD PTR SM89402                      
      */
      /*
      **  Modify code to pick a default Form Name if             
      **  JOBPROPS IS NULL                                       
      */
      if ((PSZ)pdv->jobProperties.szFormName == '\0')
      {
        INT iCountry;

        /*
        ** Get the current country code set in the INI file.
        ** If the country is USA(1), Canada(2), or Brazil (55) set the
        ** default paper to Letter else set the default paper size to A4
        */
        iCountry = PrfQueryProfileInt( HINI_USERPROFILE, "PM_National",
                                       "iCountry", 1 );

        if (iCountry == 1 || iCountry == 2 || iCountry == 55)
        {
          pszDefaultFormName = (PSZ)StringTable[IDS_Letter-STRING_BASE];
        }
        else
        {
          pszDefaultFormName = (PSZ)StringTable[IDS_A4-STRING_BASE];
        }
      }
      else
      {
        pszDefaultFormName = (PSZ)pdv->jobProperties.szFormName;
      }

      /*
      ** copy to tmp using only first 32
      */
      szCopy( (PSZ)szDefaultClipped, (PSZ)pszDefaultFormName, sizeof
              (phci->szFormname) );

      /*
      **  This is the current form
      */
/**** if (szIsEqual((PSZ)szDefaultClipped, phci->szFormname))             */
      if ( ! CompareRealNames( (PSZ)szDefaultClipped, phci->szFormname ) )
      {
        phci->flAttributes = HCAPS_CURRENT;
      }
      else
      {
        phci->flAttributes = 0;
      }

      /*
      **  szBuffer = LETTER;LEGAL;A5;;
      */
      pchB = szBuffer;
      pchP = szPaper;

      while (*pchB != 0)
      {
        /*
        **           
        ** The previous code stated: "if *pchB == ';' break;", thinking
        ** that a lone semicolon meant the end of the string, which is not
        ** always true.  For the modified code below, if a lone semicolon
        ** is found, the pointer is incremented one character and reads the
        ** next form.  Since this form string is a NULL-terminated string,
        ** this loop will exit OK since it checks for 0 in order to exit.
        */
        if (*pchB != ';')
        {
          pchP = szPaper;

          while (*pchB != ';' && *pchB != 0)
          {
            *pchP++ = *pchB++;
          }
          *pchP = 0;

          if (*pchB == ';')
          {
            pchB++;
          }
          szPaper[31] = '\0';            /* truncate to formname              */

/******** if (szIsEqual((PSZ)phci->szFormname, (PSZ)szPaper))            */
          if ( ! CompareRealNames( (PSZ)phci->szFormname, (PSZ)szPaper ) )
          {
            /*
            ** This form is one of the trays
            */
            phci->flAttributes |= HCAPS_SELECTABLE;
          }
        }
        else
        {
          /*
          **           
          ** Lone semicolon found.  Increment the pointer one character
          ** in order to read the next form.
          */
          pchB++;
        }
      }
      k += 4;
      phci++;                          /* point to next form area           */
    }

    /*
    **  release the memory allocated for dimension arrays
    */
    GplMemoryFree((PB)pabPapernamesSave );
    GplMemoryFree((PB)paiPapersize );
    GplMemoryFree((PB)paiImagesize );
    ExitDriver( pddc );
    return( nForms );
  }

  /*
  ** Return the number of forms available on the device.
  */
  i = 0;                               /* number of forms requested         */

  if (!LoadPaperDim( pddc, (SHORT *)0L, (SHORT *)0L, (SHORT *)0L,
                     (CHAR *)0L, (LONG)iFirst, (LONG *)&nForms))
  {
    RIP( "QueryHardCopyCaps: LoadPaperDim failed" );
    GplErrSetError( DQHC_ERROR );
    ExitDriver( pddc );
    return( -1L );
  }
  else
  {
    ExitDriver( pddc );
    return( nForms );
  }
}

/***************************************************************************
 *
 * FUNCTION NAME = DevQueryDeviceNames
 *
 * DESCRIPTION   = The DevQueryDeviceNames function returns the device
 *                 names, descriptions, and data types supported by the
 *                 specified device driver.
 *
 *                 The application can call the function first with the
 *                 pdn and pdt parameters set to zero in order to find how
 *                 much storage is needed for the data buffers.  Having
 *                 allocated the storage, the application then calls the
 *                 function a second time in order to have the buffers
 *                 filled with data for the data to be filled in.
 *
 * INPUT         = hab           - anchor-block handle
 *                 pszDriverName - pointer to string for device driver name
 *                 pdn           - maximum number of device drivers
 *                 achDeviceName - pointer to array of device names
 *                 achDeviceDesc - pointer to array of device descriptions
 *                 pdt           - maximum number of data types
 *                 achDataType   - pointer to array of data types
 *
 *                 FURTHER DESCRIPTION OF INPUT:
 *                 ____________________________
 *                 hab            Identifies the anchor block.
 *
 *                 pszDriverName  Points to the null-terminated string that
 *                                contains the name of the device driver.
 *
 *
 *                 pdn            Points to the maximum number of device names
 *                                and descriptions that can be returned.  If
 *                                this parameter is zero, the number of device
 *                                names and descriptions supported is returned
 *                                and the arrays pointed to by the
 *                                achDeviceName and achDeviceDesc parameters
 *                                are not updated.  If this parameter is
 *                                nonzero, then its value is updated to the
 *                                number returned in the arrays pointed to by
 *                                achDeviceName and achDeviceDesc and the
 *                                arrays are updated.
 *
 *                 achDeviceName  Points to an array of null-terminated ASCII
 *                                strings, each element of which identifies a
 *                                particular device (for example, model
 *                                number).  Valid names are defined by device
 *                                drivers.  IBM4201 is an example of a device
 *                                name.
 *
 *                 achDeviceDesc  Points to an array of null-terminated ASCII
 *                                strings, each element of which is a
 *                                description of a particular device (for
 *                                example, model name).  Valid names are
 *                                defined by device drivers.  IBM 4201
 *                                Proprinter is an example of a device
 *                                description.
 *
 *                 pdt            Points to the maximum number of data types
 *                                that can be returned.  If this parameter is
 *                                zero, the number of data types supported is
 *                                returned and the array pointed to by the
 *                                achDataType parameter is not updated.  If
 *                                this parameter is nonzero, then its value is
 *                                updated to the number returned, and the
 *                                array is updated.
 *
 *                 achDataType    Points to an array of null-terminated ASCII
 *                                strings, each element of which identifies a
 *                                data type.  Valid data types are defined by
 *                                device drivers.
 *
 * OUTPUT        = BOOL
 *
 * RETURN-NORMAL = The return value is TRUE if the function is successful
 * RETURN-ERROR  = FALSE if an error occurs.
 *                                                                    
 *
 **************************************************************************/

BOOL OS2_PM_DRV_DEVICENAMES( PSZ pszDriver, LONG *pdn, PSZ pszDeviceName,
                             PSZ pszDeviceDesc, LONG *pdt, PSZ pszDataType,
                             PDDC pddc, ULONG FunN)

/*
** PSZ         pszDriver;      The name of the device driver
** LONG    *pdn;            ptr to number of entries in device name array
** PSZ         pszDeviceName;  ptr to device names array
** PSZ         pszDeviceDesc;  ptr to device descriptor array
** LONG    *pdt;            ptr to the number of data types
** PSZ         pszDataType;    ptr to the data type
** PDDC        pddc;           The display DC handle
** ULONG       FunN;           The function number
*/

{
  SHORT     i, j, k, l, m;
  PSZ       pszDevName;
  PSZ       pszDevDesc;
  PB        pbDir;
  PCNFDATA  pcnfData;
  PDESPPD   pdesPpd;
  PB       *apResources[5];
            /*
            ** array of pointer to following resources:-
            ** ppb file header ,
            ** directory buffer ,
            ** itemsbuffer ,
            ** configuration buffer ,
            ** ppd parameter buffer .
            */

  PrintLog( (PSZ)"DeviceNames: pszDriver = %ls\n", pszDriver );

  /*
  **  check for invalid count.
  */
  if ((*pdt < 0L) || (*pdn < 0L))
  {
    RIP( "OS2_PM_DRV_DEVICENAMES: negative array count." );
    GplErrSetError( PMERR_INVALID_ARRAY_COUNT );
    return( (ULONG)FALSE );
  }

  /*
  **  initialize the resource directory.
  */

  for (i = 0; i < 5; i++)
  {
    apResources[i] = (PB *)NULL;
  }

  /*
  **  create the driver's heap and initialize some stuff.
  */
/*InitGlobalHeap( );*/

  /*
  **  allocate space to store the printer configuration data.
  */
  if (!(apResources[CNFRES] = (PVOID)GplMemoryAlloc( pProcessHeap,
                                                     sizeof(CNFDATA) )))
  {
    RIP( "OS2_PM_DRV_DEVICENAMES: cant allocate CNFDATA memory." );
    GplErrSetError( PMERR_BASE_ERROR );
    return( FALSE );
  }

  /*
  ** get a pointer to the configuration data, open the resource, and
  ** save the pointer to the resource.
  */
  pcnfData = (PCNFDATA) apResources[CNFRES];

  /*
  **  Get count of files, read in dir only
  */
  pcnfData->lGetPtr = 1L;

  if (!LoadInfoSegment((PB *)apResources))
  {
    /*
    **  Will return false if no printers
    */
    RIP( "OS2_PM_DRV_DEVICENAMES: cant load info segment." );
    GplErrSetError( PMERR_RESOURCE_NOT_FOUND );
    return( FALSE );
  }

  /*
  ** pdn points to the max number of device names and descriptions that
  ** can be returned.  if pdn points to zero, the number of device
  ** names and descriptions supported by the driver is returned.
  */
  if (*pdn == 0L)
  {
    *pdn = (LONG)pcnfData->iCntFiles;
  }
  else
  {
    /*
    **  get a local pointer to the PPB directory structure.
    */
    pbDir = (PB) apResources[DIRRES];

    /*
    ** if *pdn is greater than the number of printers we support,
    ** set it to equal the number of printers we support.
    */
    if (*pdn > (LONG)pcnfData->iCntFiles)
    {
      *pdn = (LONG)pcnfData->iCntFiles;
    }

    /*
    ** we are now set up to copy each of the printer names and
    ** descriptions over to the buffer passed in by the caller.
    */
    /*
    **  get local pointers which we can update.
    */
    pszDevName = pszDeviceName;
    pszDevDesc = pszDeviceDesc;
    pcnfData->lGetPtr = 0L;            /* Get the printer info              */

    for (i = 0; i < (SHORT)*pdn; i++)
    {
      /*
      ** copy the printer name to the configuration data.
      ** this will tell LoadInfoSegment which printer info
      ** segment to load.
      */
      /*
      ** Since the device description is a (incomplete) subset of device name
      ** return the device name as the description too
      */
      szCopy( pszDevName, (PSZ) pbDir, DEVNAMELENGTH );
      szCopy( pszDevDesc, (PSZ) pbDir, DEVNAMELENGTH );

      /*
      ** update pointer into PPB directory.
      */
      pbDir += DRENTRYSIZE;

      /*
      ** update pointers into buffers.
      */
      pszDevName += DEVNAMELENGTH;
      pszDevDesc += DEVDESCLENGTH;
    }
  }

  /*
  **  free other memory used.
  */
  FreeMemory( (PB *)apResources );

  /*
  ** pdt points to the max number of data types that can be returned.
  ** if this parameter is zero, the number of data types supported is
  ** returned.  the postscript driver supports PM_Q_RAW and PM_Q_STD.
  */
  if (*pdt == 0L)
  {
    *pdt = (LONG)TYPES_SUPPORTED;
    return( TRUE );
  }

  /*
  ** we only support two data types.  if the caller asked for more, just
  ** return two.
  */
  if (*pdt > 2L)
  {
    *pdt = 2L;
  }

  /*
  ** copy PM_Q_STD into the first array element.  then check to see if the
  ** caller was unintelligent and just asked for 1 data type.  if so just
  ** give 'em one.
  */
  szCopy( (PSZ) pszDataType, (PSZ) "PM_Q_STD", 16 );

  if (*pdt == 2L)                      /*                                   */
  {
    szCopy((PSZ)pszDataType+16, (PSZ)"PM_Q_RAW", 16 );
  }

  /*
  **  delete the driver's heap and reset some stuff.
  */
/*KillGlobalHeap( );*/
  return( TRUE );
}
