/*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.                                */
/*                                                                           */
/*****************************************************************************/
#pragma  pagesize(55)

/**************************************************************************
 *
 * SOURCE FILE NAME = ENABLE.C
 *
 * DESCRIPTIVE NAME = PLOTTER DRIVER SOURCE FILE
 *
 *
 * VERSION = V2.0
 *
 * DATE               9/18/88
 *
 * DESCRIPTION        PLOTTER DRIVER SOURCE
 *
 *
 * FUNCTIONS   disable_dc_begin        START DC DISABLE PROCESS
 *
 *             disable_dc_complete     disable_dc_complete (pDDC)
 *
 *             disable_pdev            Release memory and close pathway to
 *                                     device
 *
 *             enable_dc_begin         Begin DC enable process
 *
 *             enable_dc_complete      Tells if the DC has been created
 *
 *             fill_ldev               Initialize the logical device block
 *
 *             fill_pdev               created and allocate physical
 *                                     device block
 *
 *             fill_pdev_get_setup_data set up data for the device
 *
 *             fill_pdev_save_dc_data  allocate memory from pdevice block
 *
 *             initialize_pdevice      Initialize the PDevice fields
 *
 *             reset_dc_state          reset a DC
 *
 *             restore_dc_state        POP a saved DC
 *
 *             save_dc_state           save all DC information
 *
 *             setup_colors            set up color information for plotter
 *
 *             Enable                  Initialize device driver
 *                                     Enable for further description of
 *                                     the parameters.
 *
 *
 *
 *
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/
#define  INCL_SPL                      /* Spooler support                   */
#define  INCL_WINSHELLDATA             /* Shell stuff                       */

#define INCL_GENPLIB_THREAD            /* genplib thread calls              */
#define INCL_GENPLIB_PATTERNS          /* genplib pattern create/del        */
/*LISTOFF                                                                   */

#include "plotters.h"                  /* Plotters stuff                    */
#include "attribs.h"                   /* Set bundle attribute stuff        */
#include "color.h"                     /* ColorTable stuff                  */
#include "dispatch.h"                  /* Our dispatch table stuff          */
#include "enable.h"                    /* Our Enable defs                   */
#include "error.h"
#include "init.h"
#include "output.h"                    /* Output routines                   */
#include "profile.h"                   /* List of plotters                  */
#include "textout.h"                   /* Font information                  */
#include "utils.h"                     /* Utility functions                 */
#include "dosio.h"                     /* Buffer size                       */
#include "font.h"                      /* Font defines                      */
#include "escquery.h"                  /* QueryDeviceCaps                   */
#include "bitmap.h"                    /* bitmap stuff for sdBitBlt pfn     */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <band.h>

/*LISTON                                                                    */

UINT _acrtused = 1;                    /* define to fool C                  */
LOCAL LONG  disable_dc_begin(HDC,PDDC);
LOCAL LONG  disable_dc_complete(PDDC);
LOCAL LONG  disable_pdev(PPDEVICE);
LOCAL LONG  enable_dc_begin(PDENPARAMS);
LOCAL LONG  enable_dc_complete(HDC,PDDC);
LOCAL LONG  fill_ldev(PLDEVPARAM2);
LOCAL LONG  fill_pdev(PPDEVPARAM1,ULONG);
LOCAL BOOL  fill_pdev_get_setup_data(PSZ,PSZ,PPDEVICE,PDRIVDATA);
LOCAL VOID  fill_pdev_save_dc_data(PPDEVICE,PDEVOPENSTRUC);
LOCAL VOID  initialize_pdevice(PVOID,PSZ,PPDEVICE);                  // @MJH
LOCAL LONG  reset_dc_state(PDDC,ULONG);
LOCAL LONG  restore_dc_state(PDDC,LONG);
LOCAL LONG  save_dc_state(PDDC);
LOCAL VOID  setup_colors(PPDEVICE);
LONG  round_divide(LONG, LONG);


LONG APIENTRY QueryDeviceSurface( PDEVICESURFACE, PDDC );

/* Support for MEMORYDC Kran */
typedef struct _STYLE_RATIO
{
    UCHAR   rx;
    UCHAR   ry;
}STYLE_RATIO,  *PRATIO;

/***************************************************************************
 *
 * FUNCTION NAME = disable_dc_begin
 *
 *
 * DESCRIPTION   = START DC DISABLE PROCESS
 *
 *                 This ENABLE subfunction is called before a DC starts
 *                 its disable process.  This allows us to do any final
 *                 clear up prior to the full disable.  This is the
 *                 point which we close off journaling, perform
 *                 colorsorting, close off the spooler, etc.
 *
 *
 *
 *
 *
 * INPUT         = (hDC,pDDC)
 *
 * OUTPUT        = NONE
 *
 *
 *
 *
 * RETURN-NORMAL = 0L
 *
 *
 *
 * RETURN-ERROR  = NONE
 *
 *
 *
 **********************************************************************/

LOCAL LONG  disable_dc_begin(HDC hDC,PDDC pDDC)
{
  PPDEVICE pPDevice = pDDC->pPDevice;

  DBPRINTF(("disable_dc_begin\n\r"));

  if (pDDC->usDCType == OD_QUEUED || pDDC->usDCType == OD_DIRECT)
  {

    if (pDDC->usDCType == OD_QUEUED && pPDevice->DataType == PM_Q_STD &&
       pPDevice->bSpoolStdStarted && pPDevice->bSpoolStdOpened)
      perform_std_spooling(pDDC, hDC);

    if (pPDevice->bPageStarted)
      end_page(pDDC, hDC, TRUE, 0L);

    if (pPDevice->bDocStarted)
      end_doc(pDDC);

    if (pPDevice->bJobStarted)
      end_job(pDDC);

    if (pPDevice->hThread)
    {
      GplThreadDeleteInstance(&pPDevice->hThread);
    }
    /*
    ** Delete the device surface bitmap memory
    ** Make sure we allocated the pBits
    ** Make sure the pBits is not where we stored our pDDC
    ** for sdBitBlt
    */
    if (pDDC->pPDevice->bRaster &&
        pDDC->pDeviceSurface->SurfaceBmapInfo.pBits &&
        pDDC->pDeviceSurface->SurfaceBmapInfo.pBits != (PBYTE)pDDC)
      GplMemoryFree ((PVOID)pDDC->pDeviceSurface->SurfaceBmapInfo.pBits);
  }
  /*
  ** If memory DC Clean up patterns we created in QueryDeviceSurface
  */
  if (pDDC->usDCType == OD_MEMORY )
  {
    if (pDDC->pPDevice->bAllocatedPatterns)
    {
      /*
      ** Note: we only allocate patterns is querydevicesurface if
      ** we are running on the 2.2 or greater GRE
      */
      if (GplPatternDeleteBitmaps(pDDC->pPDevice->hmcbHeap,
                   (PBMAPINFO)&pDDC->pDeviceSurface->abmapinfoDefPattern,
                   DEFAULT_PATTERNS_NUMBER))
      {
        pDDC->pPDevice->bAllocatedPatterns = FALSE;
      }
    }
  }
  return (0L);
}

/***************************************************************************
 *
 * FUNCTION NAME =  disable_dc_complete
 *
 *
 * DESCRIPTION   =   disable_dc_complete (pDDC)
 *                   This ENABLE subfunction is called when a DC has
 *                   completed the disable process.  The purpose of
 *                   this function is to release any memory we
 *                   allocated for our cookie (pDDC).
 *
 *
 * INPUT         =  (pDDC)
 *
 *
 *
 * OUTPUT        = NONE
 *
 *
 *
 *
 * RETURN-NORMAL = 0L
 *
 *
 *
 * RETURN-ERROR  = NONE
 *
 *
 *
 ***********************************************************************/

LOCAL LONG  disable_dc_complete(PDDC pDDC)
{

  /*
  **  Free any saved DCs
  */
  PDCSTATE pDCState = pDDC->DCState.pSavedDCState,/* start at First         */
     pNextDCState;

  /* In case of MEMORYDC first disable hdcMemory and hrgnMemory.    Kran */
  if (pDDC->hdcMemory)
  {
    GreDestroyRegion (pDDC->hdcMemory, pDDC->hrgnMemory);
    pDDC->hrgnMemory = (HRGN) NULL;
    GreCloseDC (pDDC->hdcMemory);
    pDDC->hdcMemory = (HDC) NULL;
  }
  //if (pDDC->hdcDirectMem)
  //{
  //  GreCloseDC (pDDC->hdcDirectMem);
  //  pDDC->hdcDirectMem = NULL;
  //}

  while (pDCState)
  {
    if (pDCState->pColorTable)
    {
      /*
      ** free color table in the saved DC
      */
      if (GplMemoryFree ((PVOID)pDCState->pColorTable))
        pDCState->pColorTable = (PCOLORTABLE)NULL;
    }
    pNextDCState = pDCState->pSavedDCState;

    /*
    ** free the saved DC info struct
    */
    GplMemoryFree ((PVOID)pDCState);
    pDCState = pNextDCState;           /* move to next                      */
  }

  /*
  **  Delete the PDDC struct
  */
  if (pDDC->usDCType != OD_MEMORY && pDDC->DCState.usColorFormat != LCOLF_RGB)

    /*
    ** delete the color table in the DDC struct
    */
    if (GplMemoryFree ((PVOID)pDDC->DCState.pColorTable))
      pDDC->DCState.pColorTable = (PCOLORTABLE)NULL;

  /*
  ** C Set/2 Conversion - JimR
  ** close the hmtxDDCLock
  */
  //DosCloseMutexSem(pDDC->fsrDDCLock);


  /*
  ** delete the DDC struct
  */
  GplMemoryFree ((PVOID)pDDC);

  return (0L);
}

/***************************************************************************
 *
 * FUNCTION NAME = disable_pdev
 *
 *
 * DESCRIPTION   = Release memory and close pathway to device
 *
 *                 This ENABLE subfunction is called when a physical
 *                 device block is to be deleted.  The major purpose
 *                 of this function is to release any memory we
 *                 allocated in fill_pdev, and to close the pathway to
 *                 the device.
 *
 *
 *
 *
 * INPUT         = (pPDevice)
 *
 *
 * OUTPUT        = NONE
 *
 *
 *
 *
 * RETURN-NORMAL = 0L
 *
 *
 *
 * RETURN-ERROR  = NONE
 *
 *
 *
 ***********************************************************************/

LOCAL LONG  disable_pdev(PPDEVICE pPDevice)
{
  HMCB hmcbHeap;
  WinTerminate(pPDevice->hAB);

  if (pPDevice->pszDocumentName)
    GplMemoryFree ((PVOID)pPDevice->pszDocumentName);

  if (pPDevice->pszLogAddress)
    GplMemoryFree ((PVOID)pPDevice->pszLogAddress);

  if (pPDevice->pszDriverName)
    GplMemoryFree ((PVOID)pPDevice->pszDriverName);

  if (pPDevice->pszDataType)
    GplMemoryFree ((PVOID)pPDevice->pszDataType);

  if (pPDevice->pszComment)
    GplMemoryFree ((PVOID)pPDevice->pszComment);

  if (pPDevice->pszQueueProcName)
    GplMemoryFree ((PVOID)pPDevice->pszQueueProcName);

  if (pPDevice->pszQueueProcParams)
    GplMemoryFree ((PVOID)pPDevice->pszQueueProcParams);

  if (pPDevice->pszSpoolerParams)
    GplMemoryFree ((PVOID)pPDevice->pszSpoolerParams);

  if (pPDevice->pszNetworkParams)
    GplMemoryFree ((PVOID)pPDevice->pszNetworkParams);

  if (pPDevice->paPaletteEntry)
    GplMemoryFree ((PVOID)pPDevice->paPaletteEntry);

  if (pPDevice->pDriverData)
    GplMemoryFree ((PVOID)pPDevice->pDriverData);

  if (pPDevice->pszPrinterName)
    GplMemoryFree ((PVOID)pPDevice->pszPrinterName);

  hmcbHeap = pPDevice->hmcbHeap;

  if (GplMemoryFree ((PVOID)pPDevice))
    pPDevice = (PPDEVICE)NULL;

  GplMemoryDeleteInstance( hmcbHeap );


  return (0L);
}

/***************************************************************************
 *
 * FUNCTION NAME = enable_dc_begin
 *
 *
 * DESCRIPTION   = Begin DC enable process
 *
 *                 This ENABLE subfunction is called each time a DC
 *                 is created.  It is also the first time we are
 *                 presented our DC handle and is the time to
 *                 allocate our DeviceContext.  We are also presented
 *                 with the correspond physical device pointer we
 *                 returned in fill_pdev function.
 *
 *
 *
 * INPUT         = (Param1)
 *
 *
 * OUTPUT        = NONE
 *
 *
 *
 *
 * RETURN-NORMAL = lResult
 *
 *
 *
 * RETURN-ERROR  = NONE
 *
 *
 *
 ***********************************************************************/

LOCAL LONG  enable_dc_begin(PDENPARAMS Param1)
{
  LONG lResult = -1L;
  STYLE_RATIO  Ratio;
  PPDEVICE pPDevice = (PPDEVICE)Param1->ulStateInfo;
  PDDC pDDC = (PDDC)GplMemoryAlloc(pPDevice->hmcbHeap,
                                    sizeof(DDC));

  if (pDDC)
  {
    pDDC->lEngineVersion = GreQueryEngineVersion();
    pPDevice->pddc = pDDC;  /* temp fix for QueryDeviceSurface */
    pDDC->pPDevice = pPDevice;
    pDDC->usDCType = (USHORT)Param1->ulType;
    /* needed for the support of MEMORY_DC.  Kran */
    pDDC->hdc        = Param1->ulHDC;
    //pDDC->hdcDirect  = (HDC) NULL;
    pDDC->hdcMemory  = (HDC) NULL;
    pDDC->hrgnMemory = (HRGN) NULL;
    /*
    ** Initialize Instance Data semaphore
    */
    InitFSRSem(&pDDC->fsrDDCLock);
    pDDC->ulLockCount = 0;
    pDDC->tidThreadID = 0;

    if (pDDC->lEngineVersion < RASTER_ENGINE_22)
    {
      /*
      ** The Memory DC and bitmaps handling.
      ** Since printer can not handle bitmaps,
      ** we have to use the GRE & DISPLAY DEVICE.
      ** Create a shadow memory DC in display for all enabled DC in the printer.
      ** If the printer DC is a memory DC then,
      ** the shadow memory DC will be used for all.
      ** otherwise shadow memory DC will be used for handling the bitmaps.
      ** Kran
      */

      if (!(pDDC->hdcMemory = GreOpenDC( (HDC) NULL, OD_MEMORY,"*", 0L,
                                         (PDEVOPENDATA) NULL)))
      {
        return (lResult);
      }
      /*
      ** Create an empty region .
      */
      if (!(pDDC->hrgnMemory = GreCreateRectRegion (pDDC->hdcMemory,
                                                    (PRECTL) NULL, 0 )))
      {
        return (lResult);
      }
      /*
      ** Set the Style Ratio 1:1.
      */
      Ratio.rx = 0x64;
      Ratio.ry = 0x64;
      if (!(GreSetStyleRatio( pDDC->hdcMemory, (PRATIO) &Ratio )))
      {
        return (lResult);
      }
    }
    if ( Param1->ulType == OD_MEMORY )
    {
    // pDDC->DCState = (DCSTATE)NULL;
      lResult = (LONG)pDDC;
    }
    else
    {
      if (initialize_ddc(pDDC))
         lResult = (LONG)pDDC;
    }
  }

  return (lResult);
}

/***************************************************************************
 *
 * FUNCTION NAME = enable_dc_complete
 *
 *
 * DESCRIPTION   = Tells if the DC has been created
 *
 *                 This call tells us that the DC has been created
 *                 by the Engine.  Also it is the first time we
 *                 receive our magic cookie which we returned in
 *                 EnableDC.  At this time, we open the spooler if
 *                 we are a queued DC.  Also any metafiles or
 *                 journaling is started here.
 *
 *
 *
 * INPUT         = (hDC, pDDC)
 *
 * OUTPUT        = NONE
 *
 *
 *
 *
 * RETURN-NORMAL = lResult
 *
 *
 *
 * RETURN-ERROR  = NONE
 *
 *
 *
 ***********************************************************************/

LOCAL LONG  enable_dc_complete(HDC hDC,PDDC pDDC)
{
  PPDEVICE pPDevice = pDDC->pPDevice;
  LONG lResult = 0L;


  if (pDDC->usDCType == OD_QUEUED)
  {
    if (pPDevice->DataType == PM_Q_STD)
    {
      SplStdOpen(hDC);
      pPDevice->bSpoolStdOpened = TRUE;
    }
  }

  /*
  ** if the user wants to do raster output for the page
  */
  pPDevice->bRaster = FALSE;
  if (pPDevice->pSetup->usFlags & FL_RASTER)
  {
    PDEVICESURFACE pDS = pDDC->pDeviceSurface;

    /*
    ** if we are creating raw printer data
    */
    if ((pDDC->usDCType == OD_QUEUED &&
         pPDevice->DataType == PM_Q_RAW) ||
        (pDDC->usDCType == OD_DIRECT &&
         pPDevice->DataType == PM_Q_STD))
    {
        /*
        ** Create a raster band bitmap and set up GplJournaling
        */
        ULONG   ulPhyMem   = 8;
        ULONG   ulBandSize = 512*1024;

        pPDevice->IJournal.bPortrait = (pPDevice->fsTransform & DXF_PORTRAIT);
        pPDevice->IJournal.ulSize           = sizeof (pPDevice->IJournal);
        pPDevice->IJournal.hdc              = hDC;
        pPDevice->IJournal.hModule          = hModule;
        pPDevice->IJournal.pfunBandCallback = (PJFUN)band; //NewFrame;
        pPDevice->IJournal.pfunArg          = (PVOID)pDDC;
        pPDevice->IJournal.usXLength        = pDS->SurfaceBmapInfo.ulWidth;
        pPDevice->IJournal.usYLength        = pDS->SurfaceBmapInfo.ulHeight;
#if 0
        /*
        ** GplJournal code expects x and y to be in portrait mode
        */
        if (!pPDevice->IJournal.bPortrait)
          SWAPLONG(pPDevice->IJournal.usXLength, pPDevice->IJournal.usYLength);
#endif
        if (GplQueryPhysicalMem (&ulPhyMem))
        {
           DBPRINTF (("GplQueryPhysicalMem returns %d\n", ulPhyMem));

           if (4 >= ulPhyMem)
              // 4MB or less                    -- 256KB
              ulBandSize = 256*1024;
           else if (8 >= ulPhyMem)
              // between 4MB and 8MB            -- 512KB
              ulBandSize = 512*1024;
           else if (12 >= ulPhyMem)
              // between 8MB and 12MB           -- 768KB
              ulBandSize = 768*1024;
           else if (16 >= ulPhyMem)
              // between 8MB and 12MB           -- 1MB
              ulBandSize = 1000*1024;
           else if (24 >= ulPhyMem)
              // between 16MB and 24MB          -- 2MB
              ulBandSize = 2000*1024;
           else
              // > 24MB                         -- 4MB
              ulBandSize = 4000*1024;
        }

        // *NOTE: usDotsPerColumn should NOT be 0 for those devices that
        //        do not have the "column" concept (i.e. dot matrix printhead)
        //        and instead should be 1 (TBD - validate here or in jnl routine)
        if (pPDevice->bBanding = GplJournalCalcBandSize (&pPDevice->IJournal,
                                         pDS->SurfaceBmapInfo.ulBpp,
                                         pDS->DevCaps[CAPS_COLOR_PLANES],
                                         1,
                                         ulBandSize))
        {
           /*
           ** Update bitmap size
           */
           if (pPDevice->IJournal.bPortrait)
              pDS->SurfaceBmapInfo.ulHeight = pPDevice->IJournal.usBandLength;
           else
              pDS->SurfaceBmapInfo.ulWidth  = pPDevice->IJournal.usBandLength;

           /*
           ** update bytes per line
           */
           pDS->SurfaceBmapInfo.ulBytesPerLine = (
                                       (( pDS->SurfaceBmapInfo.ulWidth *
                                          pDS->SurfaceBmapInfo.ulBpp) + 7)/8);
        }
        /*
        ** Allocate the surface area
        ** We should do this at startdoc so we do not alloc
        ** mem for info DC.  (we may get multiple startdocs)
        ** We should not alloc the bitmap for rawdata jobs.
        */
        pDS->SurfaceBmapInfo.pBits = GplMemoryAlloc (0,
                                     pDS->SurfaceBmapInfo.ulBytesPerLine *
                                     pDS->SurfaceBmapInfo.ulHeight);

        pPDevice->bRaster = TRUE;
    }
  }
  return (lResult);
}

/***************************************************************************
 *
 * FUNCTION NAME = fill_ldev
 *
 *
 * DESCRIPTION   = Initialize the logical device block
 *
 *                 This ENABLE subfunction is called when the driver
 *                 is loaded.  Its purpose is to initialize the
 *                 logical device block, which includes setting some
 *                 bits telling the engine whether we need a PDEV
 *                 per DC (which we do), and to hook the functions
 *                 we wish to support.
 *
 *
 *
 * INPUT         = (pLDevParam)
 *
 *
 * OUTPUT        = NONE
 *
 *
 *
 *
 * RETURN-NORMAL = 0L
 *
 *
 *
 * RETURN-ERROR  = NONE
 *
 *
 *
 ***********************************************************************/

LOCAL LONG  fill_ldev(PLDEVPARAM2 pLDevParam)
{
  *(pLDevParam->pFlags) &= 0xFFFFFFF8L;
  *(pLDevParam->pFlags) |= 0x00000081L; /* Gets multiple dispatch tables in
                                           fillpdb. Needed for MEMORYDC Kran */

  if (pLDevParam->pDispatchTable)
    exchange_dispatch(pLDevParam->pDispatchTable);

  return 0L;
}

/***************************************************************************
 *
 * FUNCTION NAME = fill_pdev
 *
 *
 * DESCRIPTION   = created and allocate physical device block
 *
 *                 This ENABLE subfunction is called each time a
 *                 DevOpenDC is requested by the application.  The
 *                 purpose of this function is to create and allocate
 *                 a physical device block, which should contain any
 *                 information we need to perform I/O to the specified
 *                 device.
 *
 *
 *
 * INPUT         = (pDevParam1)
 *
 *
 * OUTPUT        = NONE
 *
 *
 *
 *
 * RETURN-NORMAL = lResult
 *
 *
 *
 * RETURN-ERROR  = NONE
 *
 *
 *
 ****************************************************************************/

LOCAL LONG  fill_pdev(PPDEVPARAM1 pDevParam1,ULONG Param2)
{
  LONG  lResult = -1L;
  PVOID hmcbHeap;
  CHAR  achAppName[255];

  if (hmcbHeap = GplMemoryCreateInstance(HEAPSIZE,HEAPSIZE,
                                         0x6000,   //max element 24k
                                         PROCESS_MEMORY))
  {
    PPDEVICE pPDevice = (PPDEVICE)GplMemoryAlloc(hmcbHeap, sizeof(PDEVICE));

    if (pPDevice)
    {
      pPDevice->hmcbHeap = hmcbHeap;
      pPDevice->hAB      = WinInitialize(0);
      pPDevice->lDcType  = pDevParam1->DcType;
      //pPDevice->pDevOpenStruct = &pDevParam1->DevOpenStruct;

      /* It is possible, when a memory device context is opened, to be given
      ** no logical address or driver data.  Now we have a dilemma.  How can
      ** we initialize ourselves for the proper device?  Well, fortunately,
      ** the graphics engine (GE) will give us a compatible HDC.  With this,
      ** we can then ask the GE for a pddc for that compatible HDC.  We can
      ** now copy all the information that we need from that pddc.
      */
      if ((((PSZ)NULL == pDevParam1->DevOpenStruct.pszLogAddress) ||
           ((CHAR)0   == *pDevParam1->DevOpenStruct.pszLogAddress) ) &&
          (pDevParam1->HDCCompat))
      {
         PDDC     pDDCCompat;
         PPDEVICE pPDeviceCompat;

         pDDCCompat = (PDDC)GetDriverInfo (pDevParam1->HDCCompat,
                                           0,             // it is a DC handle
                                           pDevParam1->HDCCompat);
         if (GPI_ALTERROR == (ULONG)pDDCCompat)
         {
            // Error!  What should we do?
            if (GplMemoryDeleteInstance( hmcbHeap ))
               hmcbHeap = (HMCB)NULL;
            return lResult;
         }
         else
         {
            pPDeviceCompat = pDDCCompat->pPDevice;

            // Allocate a copy of the driver data
            pPDevice->pDriverData = (PDRIVDATA)GplMemoryAlloc (hmcbHeap,
                                                                DRIVDATASIZ);
            if (!pPDevice->pDriverData)
            {
               if (GplMemoryDeleteInstance( hmcbHeap ))
                  hmcbHeap = (HMCB)NULL;
               return lResult;
            }

            // Copy the data from the compatible DC
            memcpy (pPDevice->pDriverData,
                    pPDeviceCompat->pDriverData,
                    DRIVDATASIZ);

            // Allocate a copy of the printer name
            pPDevice->pszPrinterName = (PSZ)GplMemoryAlloc (hmcbHeap,
                               lstrlen (pPDeviceCompat->pszPrinterName)+1);
            if (!pPDevice->pszPrinterName)
            {
               if (GplMemoryDeleteInstance( hmcbHeap ))
                  hmcbHeap = (HMCB)NULL;
               return lResult;
            }

            // And copy that data
            lstrcpy (pPDevice->pszPrinterName,
                     pPDeviceCompat->pszPrinterName);
         }
      }
      else if (pDevParam1->DevOpenStruct.pszLogAddress &&
               *pDevParam1->DevOpenStruct.pszLogAddress )
      {
         /* Ask for a copy of our driver data.  Pass in the copy of the
         ** driver data that was given to us...
         */
         pPDevice->pDriverData = return_driver_data (hmcbHeap,
                                        pDevParam1->DevOpenStruct.pdriv,
                                        pDevParam1->DevOpenStruct.pszLogAddress,
                                        FALSE);
         if (!pPDevice->pDriverData)
         {
            if (GplMemoryDeleteInstance( hmcbHeap ))
               hmcbHeap = (HMCB)NULL;
            return lResult;
         }

         /* Get the printer name (spooler terminology).  We use this to
         ** build the keyname in the os2sys.ini for this printer.
         */
         pPDevice->pszPrinterName = find_printer_name (hmcbHeap,
                                         pPDevice->pDriverData->szDeviceName,
                                         pDevParam1->DevOpenStruct.pszLogAddress,
                                         FALSE);
      }
      else
      {
         // Hmmm.... what to do?
         if (GplMemoryDeleteInstance( hmcbHeap ))
            hmcbHeap = (HMCB)NULL;
         return lResult;
      }

      /* Build the keyname in the os2sys.ini for this printer.
      */
      if (pPDevice->pszPrinterName)
         build_app_name (APP_NAME,                               // Driver name
                         pPDevice->pDriverData->szDeviceName,    // Device name
                         pPDevice->pszPrinterName,
                         achAppName);
      else
      {
         // Hmmm.... what to do?
      }

      pPDevice->pSetup = (PPROFILEINFO)pPDevice->pDriverData->abGeneralData;

      get_device_id (pPDevice->pDriverData->szDeviceName,
                     &pPDevice->Plotter,
                     &pPDevice->StringID);

      fill_pdev_save_dc_data (pPDevice, &pDevParam1->DevOpenStruct);

      initialize_pdevice(hmcbHeap, achAppName, pPDevice);

      /*
      **  If the GRE supports dynamic unhooking Param2 will be filled
      **  in with the pointer to the dispatch table
      */
      if (Param2)
      {
        /*****************************************************************
        ** Dynamically unhook the functions for MEMORY DC and Hook all the
        ** mandatory functions.
        ******************************************************************/
        if (pDevParam1->DcType == OD_MEMORY)
           unhook_for_memDC ((PULONG)Param2);
        else
        {
          ULONG fulOddCaps = PlotterClass[pPDevice->Plotter].fsOdd;
          // if raster/banding output required
          if (pPDevice->pSetup->usFlags & FL_RASTER)
          { /*
            ** if we are creating raw printer data
            */
            if ((pDevParam1->DcType == OD_QUEUED &&
                 pPDevice->DataType == PM_Q_RAW) ||
                (pDevParam1->DcType == OD_DIRECT &&
                 pPDevice->DataType == PM_Q_STD))
            {
              unhook_for_gre_raster ((PULONG)Param2);
            }
          }
          else if ((fulOddCaps & OD_PCL) && (fulOddCaps & OD_COLOR))
          {
             hook_bitblt2((PULONG)Param2);
          }
        }
      }

      /*
      ** Create the thread instance data
      */
      if(!(GplThreadCreateInstance(pPDevice->hmcbHeap, hModule,
                                   &pPDevice->hThread)))
      {
        if (GplMemoryDeleteInstance( hmcbHeap ))
           hmcbHeap = (HMCB)NULL;
        return lResult;
      }
      lResult = (LONG)pPDevice;
    }
    else
    {
      if (GplMemoryDeleteInstance( hmcbHeap ))
         hmcbHeap = (HMCB)NULL;
    }
  }

  return  lResult;
}

#if 0
XX
XX/***************************************************************************
XX *
XX * FUNCTION NAME = fill_pdev_get_setup_data
XX *
XX *
XX * DESCRIPTION   = set up data for the device
XX *
XX *                 This function will get the setup data for the specified
XX *                 device.  Currently, an invalid devicename will result
XX *                 in an error.
XX *
XX *
XX *
XX * INPUT         =  (pszLogAddress,pszDriverName,
XX *                   pPDevice,pDrivData)
XX *
XX *
XX *
XX * OUTPUT        = NONE
XX *
XX *
XX *
XX *
XX * RETURN-NORMAL = bResult;
XX *
XX *
XX *
XX * RETURN-ERROR  = NONE
XX *
XX *
XX *
XX **********************************************************************/
XX
XXLOCAL BOOL  fill_pdev_get_setup_data(PSZ pszLogAddress,
XX                                                PSZ pszDriverName,
XX                                                PPDEVICE pPDevice,
XX                                                PDRIVDATA pDrivData)
XX
XX{
XX  BOOL bResult = FALSE;
XX  ULONG lIndex;
XX  CHAR szDetails[64];
XX  CHAR szTemp[64];
XX  PSZ pszDriver,pszPort,pszQueue,pszDevice;
XX
XX  pPDevice->pDriverData = (PDRIVDATA)GplMemoryAlloc(pPDevice->hmcbHeap,
XX                                                     DRIVDATASIZ);
XX
XX  if (pPDevice->pDriverData)
XX  {
XX    /* Check whether the information passed to us is big enough
XX    ** to be meaningful.
XX    */
XX    if (!pDrivData || pDrivData->cb < DRIVDATASIZ)
XX    {
XX      GplErrSetWarning(PMERR_INV_DRIVER_DATA);
XX    }
XX    else
XX    {
XX      PPROFILEINFO pSetupSave = (PPROFILEINFO)NULL;
XX
XX
XX      if (!(IsIta_Device_Name(pDrivData->szDeviceName) ||
XX            IsIta_COM_Port_Name (pszLogAddress)))
XX      {
XX        /*
XX        **  OK! We din't get a DeviceName and we din't get a logaddress
XX        **  Try and get a logaddress from PROFILE for the default
XX        **  printer and default queue          Kran
XX        */
XX        if (lIndex = PrfQueryProfileString(HINI_PROFILE,
XX                                          "PM_SPOOLER",
XX                                          "PRINTER",
XX                                          "",
XX                                          szTemp,
XX                                          (ULONG)sizeof(szTemp)))
XX        {
XX          /*
XX          ** szTemp now contains PRINTER;
XX          ** lIndex is the no. of characters including semicolon and the null
XX          ** so reduce lIndex by 2 to put the null at semicolon
XX          */
XX          szTemp[lIndex-2] = 0;
XX          /*
XX          **  OK!!  So a Device Name wasn't passed in and we aren't
XX          **  printing direct to a physical port, so lets try find a
XX          **  device name attached to the logical printer by querying
XX          **  OS2SYS.INI for the details (and hope that the .INI file
XX          **  isn't messed up (as it almost always is!))
XX          */
XX          if (lIndex = PrfQueryProfileString(HINI_PROFILE,
XX                                            "PM_SPOOLER_PRINTER",
XX                                    //       pszLogAddress
XX                                             szTemp,
XX                                             "",
XX                                             szDetails,
XX                                             (ULONG)sizeof(szDetails)))
XX          {
XX
XX            /*
XX            **   after the above we should have the
XX            **   following (example) string held
XX            **   by szDetails:
XX            **
XX            **   "COM1;PLOTTERS.HP7475A;PLOTTER1;;"
XX            **
XX            **    So we extract the actual port, driver, and queue
XX            */
XX            szDetails[sizeof(szDetails)-1] = 0;/* put stop to things          */
XX            pszDriver = pszPort = szDetails;/* start at same point            */
XX            lstrtoksrch(pszPort, ';');
XX
XX            while (*pszDriver)
XX              pszDriver++;
XX
XX            pszDriver++;
XX            pszDevice = pszDriver;
XX            lstrtoksrch(pszDriver, '.');
XX
XX            while (*pszDevice)
XX              pszDevice++;
XX
XX            pszDevice++;
XX            pszQueue = pszDevice;
XX            lstrtoksrch(pszDevice, ';');
XX
XX            while (*pszQueue)
XX              pszQueue++;
XX
XX            pszQueue++;
XX            lstrtoksrch(pszQueue, ';');
XX
XX            /*
XX            **   We should have the following
XX            **   (example) string being pointed at
XX            **   (with blanks being NULLS):
XX            **
XX            **   "COM1 PLOTTERS HP7475A PLOTTER1"
XX            **     pszPort   points to start of COM1
XX            **     pszDriver points to start of PLOTTERS
XX            **     pszDevice points to start of HP7475A
XX            **     pszQueue  points to start of PLOTTER1;;
XX            **
XX            **    So lets do a final check:
XX            */
XX            if (IsIta_Device_Name(pszDevice))
XX              lstrcpy(pDrivData->szDeviceName, pszDevice);
XX            if (!pszLogAddress)
XX              pszLogAddress = pszPort;
XX          }
XX        }
XX      }
XX
XX      get_device_id(pDrivData->szDeviceName,
XX                    &pPDevice->Plotter,
XX                    &pPDevice->StringID);
XX      pPDevice->pDriverData->cb = DRIVDATASIZ;
XX      pPDevice->pDriverData->lVersion = 0L;
XX      lstrcpy(pPDevice->pDriverData->szDeviceName,
XX              pDrivData->szDeviceName);
XX      pPDevice->pSetup = (PPROFILEINFO)pPDevice->pDriverData->abGeneralData;
XX
XX      /*
XX      ** Now determine if what they gave us is any good
XX      */
XX      if (pDrivData->cb == DRIVDATASIZ)
XX      {
XX        pSetupSave = (PPROFILEINFO)pDrivData->abGeneralData;
XX      }
XX
XX      if (pSetupSave && pSetupSave->Version == JOBPROP_VERSION)      // @MJH3
XX      {
XX        get_defaults(pPDevice->Plotter, pPDevice->pSetup);
XX        *pPDevice->pSetup = *pSetupSave;
XX      }
XX      else
XX      {
XX        char PrinterName[32];
XX
XX
XX        if (!get_printer_name(pPDevice->hmcbHeap, pPDevice->hAB,
XX                              pPDevice->lDcType,
XX                              pszLogAddress, PrinterName) ||
XX                              (!(get_profile(pPDevice->hmcbHeap,
XX                                                       pszDriverName,
XX                                                       pDrivData->szDeviceName,
XX                                                       PrinterName,
XX                                                       pPDevice->pSetup) )))
XX        {
XX          get_defaults(pPDevice->Plotter, pPDevice->pSetup);
XX        }
XX      }
XX      bResult = TRUE;
XX    }
XX  }
XX
XX  return  bResult;
XX}
#endif

/***************************************************************************
 *
 * FUNCTION NAME = fill_pdev_save_dc_data
 *
 *
 * DESCRIPTION   = allocate memory from pdevice block
 *
 *                 This function allocates memory from our pdevice
 *                 block and saves the spooler parameters from the
 *                 enable dc structure (if they exist).
 *
 * INPUT = (pPDevice,pDevParams)
 *
 *
 * OUTPUT = NONE
 *
 * RETURN-NORMAL = NONE
 *
 *
 *
 * RETURN-ERROR = NONE
 *
 **********************************************************************/

LOCAL VOID  fill_pdev_save_dc_data(PPDEVICE pPDevice,PDEVOPENSTRUC
                                               pDevParams)
{
  SHORT Size;


  if (pDevParams->pszLogAddress && (Size = lstrlen(pDevParams->pszLogAddress))
     && (pPDevice->pszLogAddress = (PSZ)GplMemoryAlloc(pPDevice->hmcbHeap,
                                                      (ULONG)Size+1)))
  {
    lstrcpy(pPDevice->pszLogAddress, pDevParams->pszLogAddress);
  }

  if (pDevParams->pszDriverName &&
      (Size = lstrlen(pDevParams->pszDriverName)) &&
      (pPDevice->pszDriverName = (PSZ)GplMemoryAlloc(pPDevice->hmcbHeap,
                                                      (ULONG)Size+1)))
  {
    lstrcpy(pPDevice->pszDriverName, pDevParams->pszDriverName);
  }

  if (pDevParams->pszDataType && (Size = lstrlen(pDevParams->pszDataType)) &&
     (pPDevice->pszDataType = (PSZ)GplMemoryAlloc(pPDevice->hmcbHeap,
                                                 (ULONG)Size+1)))
  {
    lstrcpy(pPDevice->pszDataType, pDevParams->pszDataType);
  }

  if (pDevParams->pszComment &&
      (Size = lstrlen(pDevParams->pszComment)) &&
      (pPDevice->pszComment = (PSZ)GplMemoryAlloc(pPDevice->hmcbHeap,
                                                      (ULONG)Size+1)))
  {
    lstrcpy(pPDevice->pszComment, pDevParams->pszComment);
  }

  if (pDevParams->pszQueueProcName &&
      (Size = lstrlen (pDevParams->pszQueueProcName)) &&
      (pPDevice->pszQueueProcName = (PSZ)GplMemoryAlloc(pPDevice->hmcbHeap,
                                                             (ULONG)Size+1)))
  {
    lstrcpy(pPDevice->pszQueueProcName, pDevParams->pszQueueProcName);
  }

  if (pDevParams->pszQueueProcParams &&
      (Size = lstrlen(pDevParams->pszQueueProcParams)) &&
      (pPDevice->pszQueueProcParams = (PSZ)GplMemoryAlloc(pPDevice->hmcbHeap,
                                                         (ULONG)Size+1)))
  {
    lstrcpy(pPDevice->pszQueueProcParams, pDevParams->pszQueueProcParams);
  }

  if (pDevParams->pszSpoolerParams &&
      (Size = lstrlen(pDevParams->pszSpoolerParams)) &&
      (pPDevice->pszSpoolerParams = (PSZ)GplMemoryAlloc(pPDevice->hmcbHeap,
                                                       (ULONG)Size+1)))
  {
    lstrcpy(pPDevice->pszSpoolerParams, pDevParams->pszSpoolerParams);
  }

  if (pDevParams->pszNetworkParams &&
      (Size = lstrlen(pDevParams->pszNetworkParams)) &&
      (pPDevice->pszNetworkParams = (PSZ)GplMemoryAlloc(pPDevice->hmcbHeap,
                                                       (ULONG)Size+1)))
  {
    lstrcpy(pPDevice->pszNetworkParams, pDevParams->pszNetworkParams);
  }
  pPDevice->DataType = PM_Q_STD;

  if (pDevParams->pszDataType)
  {
    if (lstrcmp(pDevParams->pszDataType, "PM_Q_RAW"))
    {
      pPDevice->DataType = PM_Q_RAW;
    }
  }
}

/***************************************************************************
 *
 * FUNCTION NAME = initialize_pdevice
 *
 *
 * DESCRIPTION   = Initialize the PDevice fields
 *
 *
 *
 * INPUT         = (pPDevice)
 *
 * OUTPUT        = NONE
 *
 *
 *
 *
 * RETURN-NORMAL = NONE
 *
 *
 *
 * RETURN-ERROR  = NONE
 *
 *
 *
 *
 ************************************************************************/

LOCAL VOID  initialize_pdevice(PVOID hmcbHeap, PSZ pszAppName, PPDEVICE pPDevice)
{
  PPROFILEINFO pSetup = pPDevice->pSetup;
  PFONTMETRICS pfmDefaultFont = &pPDevice->fmDefaultFont;
  SHORT Size = pSetup->Size;
  SHORT Plotter = pPDevice->Plotter;
  SHORT ResIndex = PlotterClass[Plotter].PaperResIndex;
  SHORT OffIndex = PlotterClass[Plotter].PrintOffIndex;
  LONG lClipYmm;
  LONG lClipXmm;
  LONG lPrintableYmm;
  LONG lPrintableXmm;
  LONG lSizeInPlotterUnits;
  //LONG lPelsPerCM;
  BOOL bCenterOrigin;

  pPDevice->HandShake = HNDSHK_NONE;   /* No handshake as default           */
  pPDevice->CurrentPenThickness = 1;
  pPDevice->PhysPosition.x = pPDevice->PhysPosition.y = -256L;
  pPDevice->bPenIsUp = TRUE;
  pPDevice->bEnableOutput = TRUE;   /* abort will clear          */
  pPDevice->CharShear.x = 0;
  pPDevice->CharShear.y = 1;
  pPDevice->fsTransform = 0;           /* convert_point info                */
  pPDevice->usCurrentFillPattern = (USHORT)PATSYM_SOLID;/* Device default FT*/
  pPDevice->usCurrentFillAngle = 0;    /* Dev. fill angle                   */
  pPDevice->CurrentTRMode = -1;        /* current transparency mode undefined*/
  pPDevice->iLineJoin = -1;            /* Device default LJ -1 in
                                          usLineJoin will force linejoin to
                                          be set the first time.            */
  pPDevice->iLineEnd = LINEEND_DEFAULT;/* Device default LE                 */
  pPDevice->usCurrentMCROP = 252;      /* Device default MC is 252 TSo      */

  /*
  ** Initialize Raster info.  Kran
  */
  pPDevice->bHPGL2_PalettePushed = FALSE;
  pPDevice->ulCurrentPrintMode   = HPGL2;

  /*
  ** RFindex is to keep track of user patterns.
  ** Default is 1 for solid.
  ** Ranges 1..8
  ** used in bitblt ROP_PATCOPY                      Kran.
  */
  pPDevice->RFindex = 1;

  /*
  ** Set flags used by the convert_point routine to transform
  ** engine coordinates to device coordinates.
  ** Also swap horizontal res and vertical res values here when in
  ** landscape mode (since they're calculated for portrait mode).
  ** Handle special case for plotters that rotate small paper.
  */
  bCenterOrigin = CENTERORIGIN(pPDevice->Plotter);
  if (bCenterOrigin)
  {
    pPDevice->fsTransform |= DXF_CENTERED;
  }

  if (pSetup->Orientation == OR_PORTRAIT)
  {
    pPDevice->fsTransform |= DXF_PORTRAIT;
  }

  if (!(pSetup->usFlags & FL_USERDEFINEDFORMS))                      // @MJH
     if ((IsIBM7374(Plotter) && IsSizeAPaper(pSetup->Size)) || (IsIBM7375X
        (Plotter) && (IsSizeAPaper(pSetup->Size) || IsSizeCPaper(pSetup->Size))))
     {
       pPDevice->fsTransform |= DXF_ROTATE90;
     }

  /*
  ** lCapsWidth" and "lCapsHeight"
  ** Are not resolutions at all; they are the printable area width and
  ** height, respectively, in pels.  The actual resolution, both Horizontal
  ** and Vertical, are in "lPltrRes_XPelsPerMeter and
  ** lPltrRes_YPelsPerMeter" as pels per Meter.
  ** MarkV
  ***
  **
  ** Calculate Pels Per Meter=(PelsPerInch * (Inches Per Meter * 100)) / 100
  **
  */
  //pPDevice->lPltrRes_PelsPerMeter = (PlotterClass[Plotter].lResInPelsPerInch
  //   * INCHESPERMETERX100) / 100;
  pPDevice->lRes_XPelsPerInch = aptlRes_Table[pSetup->lResIndex].x;
  pPDevice->lRes_YPelsPerInch = aptlRes_Table[pSetup->lResIndex].y;
  pPDevice->lPltrRes_XPelsPerMeter = (pPDevice->lRes_XPelsPerInch *
                                      INCHESPERMETERX100) / 100;
  pPDevice->lPltrRes_YPelsPerMeter = (pPDevice->lRes_YPelsPerInch *
                                      INCHESPERMETERX100) / 100;

  if (!(pSetup->usFlags & FL_USERDEFINEDFORMS))                      // @MJH
  {
     /*
     ** hardware clips assume paper is always in the same position and
     ** we rotate the image
     */
     lClipXmm = PrintOffTable[OffIndex][Size].x;
     lClipYmm = PrintOffTable[OffIndex][Size].y;

     /*
     ** Printable X Area In Millimeters = XPapersizeInMM - X Clip values
     */
     lPrintableXmm = PhysicalPageSize[Size].x - ( 2 * lClipXmm );
  }
  else                                                               // @MJH
  {
     // Assume no hardware clipping for user defined forms
     lClipXmm      = 0;
     lClipYmm      = 0;
     find_user_form_size (hmcbHeap,
                          pszAppName,
                          Size,
                          &lPrintableXmm,
                          &lPrintableYmm);
  }

  /*
  ** Needed for mixing of PCL and HPGL/2 for PaintJet XL 300.
  ** I have absolutely no idea if there is another way.
  ** If I subtract the 2*lClipXmm I am loosing data.
  ** The Margins I am using are from HP PCL 5C Color Printer Language
  ** Technical Reference Manual. It is 50 dots at 300 dpi resolution
  ** on sides aswell as top. So it is 120 * 2 = 240 Decipoints on both sides.
  ** Kran
  */
  if (PlotterClass[Plotter].fsOdd & OD_PCL)
  {
    LONG lx,ly;
    lx = PhysicalPageSize[Size].x; // - 2 * lClipXmm;   // these values are in mm
    ly = PhysicalPageSize[Size].y; // - 2 * lClipYmm;
    if (pPDevice->fsTransform & DXF_PORTRAIT)
    {
      lx = (lx * 720 / 25.41) - 278;
      ly = (ly * 720 / 25.41) - 430;
    }
    else
    {
      lx = (lx * 720 / 25.41) - 374; //8
      ly = (ly * 720 / 25.41) - 260; //55,50
    }
    pPDevice->lxPicFrame = lx;
    pPDevice->lyPicFrame = ly;
  }

  /*
  ** Printable area in pels =
  **           (PelsPerMeter * PrintableAreaInMillimeters) / 1000
  **           HPGL2- MarkV
  */

  pPDevice->lCapsWidth = round_divide(pPDevice->lPltrRes_XPelsPerMeter *
                                  lPrintableXmm, 1000);

  /*
  ** Default x value for p1p2 = PrintableAreaInMillimeters *
  **                            PlotterUnitsPerMilimeter
  */
  lSizeInPlotterUnits = lPrintableXmm * UNITSPERMM;
  pPDevice->DefaultP1P2.xLeft  = bCenterOrigin ? -lSizeInPlotterUnits/2 : 0L;
  pPDevice->DefaultP1P2.xRight = bCenterOrigin ?
                                   lSizeInPlotterUnits/2 : lSizeInPlotterUnits;

  /*
  ** Set up the scaling factor.  Some plotters use plotter units for
  ** scaling,  others use user units.  The variable below compensates
  ** for this,  by being applied whenever the IW instruction is issued.
  ** Stored as 1000X to keep some fractional values
  */
  pPDevice->lIWScaleMultiplier = 1000L;
  {                                    /* Values in plotter units           */
    if (PlotterClass[Plotter].usHPGLCaps & HPGL2)
    {
      /*
      ** 1016 Plotter units per inch
      ** mult by ScaleMultiplier to  get decimal places.
      */
      pPDevice->lIWScale   = (PLOTTERUNITSPERINCH *
                             pPDevice->lIWScaleMultiplier * 10L) /
                                    //PlotterClass[Plotter].lResInPelsPerInch;
                                    // fix this later
                                    // we need a x and y iwscale
                                    pPDevice->lRes_XPelsPerInch;
      pPDevice->lIWScale   = round_divide(pPDevice->lIWScale,10L);
    }
    else
    {
      /*
      ** Since HPGL1 devices are using the CAPs Width and Height
      ** as the scale we will calc the IWScale the same way
      */
      pPDevice->lIWScale = (lSizeInPlotterUnits *
                            pPDevice->lIWScaleMultiplier) /
                                    pPDevice->lCapsWidth;

    }
  }

  if (!(pSetup->usFlags & FL_USERDEFINEDFORMS))                      // @MJH
  {
     if (!IsRollPaper(Size))
     {
       lPrintableYmm = PhysicalPageSize[Size].y - ( 2 * lClipYmm );

       pPDevice->lCapsHeight = round_divide(pPDevice->lPltrRes_YPelsPerMeter *
                                        lPrintableYmm, 1000);

       lSizeInPlotterUnits = lPrintableYmm * UNITSPERMM;
       pPDevice->DefaultP1P2.yBottom = bCenterOrigin ? -lSizeInPlotterUnits/2 : 0L;
       pPDevice->DefaultP1P2.yTop    = bCenterOrigin ?
                                      lSizeInPlotterUnits/2 : lSizeInPlotterUnits;

       pPDevice->lNumBands = (pPDevice->lNumBandsCompleted = 0) + 1;
     }
     else
     {                                    /* Roll Feed - not on HP7475A, so
                                             assume PltrRes                    */

       SHORT iRollPaperLengthInInches;


       if (Size == 10)
         iRollPaperLengthInInches = pSetup->Roll24;
       else
         iRollPaperLengthInInches = pSetup->Roll36;

       pPDevice->lCapsHeight = iRollPaperLengthInInches *
                                 //PlotterClass[Plotter].lResInPelsPerInch;
                                 pPDevice->lRes_YPelsPerInch;
       lSizeInPlotterUnits = (iRollPaperLengthInInches *
                                MMPER10INCH *
                                UNITSPERMM) / 10;
       pPDevice->DefaultP1P2.yBottom = bCenterOrigin ? -lSizeInPlotterUnits/2 : 0L;
       pPDevice->DefaultP1P2.yTop    = bCenterOrigin ?
                                                       lSizeInPlotterUnits/2 :
                                                       lSizeInPlotterUnits;

       pPDevice->lNumBandsCompleted = 0;

       /*
       **
       **   roll length in plotter units =
       **   40 pels per millimeter in plotter units  *
       **   25.4   millimeters per inch * # of inches
       */
       pPDevice->lLengthRes = (((lSizeInPlotterUnits *100)/254) *
                              //PlotterClass[Plotter].lResInPelsPerInch)/10;
                              pPDevice->lRes_YPelsPerInch) / 10;
       pPDevice->lNumBands = pPDevice->lCapsHeight/pPDevice->lLengthRes;
       pPDevice->lLastBandLength = pPDevice->lCapsHeight%pPDevice->lLengthRes;
     }                                    /* endif                             */
  }
  else                                                               // @MJH
  {
     pPDevice->lCapsHeight = round_divide(pPDevice->lPltrRes_YPelsPerMeter *
                                      lPrintableYmm, 1000);

     lSizeInPlotterUnits = lPrintableYmm * UNITSPERMM;
     pPDevice->DefaultP1P2.yBottom = bCenterOrigin ? -lSizeInPlotterUnits/2 : 0L;
     pPDevice->DefaultP1P2.yTop    = bCenterOrigin ?
                                    lSizeInPlotterUnits/2 : lSizeInPlotterUnits;

     pPDevice->lNumBands = (pPDevice->lNumBandsCompleted = 0) + 1;
  }

  /*
  ** Always send the device vertical P1p2
  ** Default mode for the plotter is landscape
  */
  //if (!(PlotterClass[pPDevice->Plotter].fsOdd & OD_VERTICAL_P1P2))
  {
    SWAPLONG(pPDevice->DefaultP1P2.yBottom, pPDevice->DefaultP1P2.xLeft)
    SWAPLONG(pPDevice->DefaultP1P2.yTop   , pPDevice->DefaultP1P2.xRight)
  }

  /*
  ** If in landscape mode, swap horizontal and vertical resolutions
  */
  if (!(pPDevice->fsTransform & DXF_PORTRAIT))
  {
    SWAPLONG(pPDevice->lCapsWidth, pPDevice->lCapsHeight);
    SWAPLONG(pPDevice->lPltrRes_XPelsPerMeter,pPDevice->lPltrRes_YPelsPerMeter);
    SWAPLONG(pPDevice->lRes_XPelsPerInch, pPDevice->lRes_YPelsPerInch);

    /*
    ** If the device expects the P1P2 in Horizontal units
    */
    //if (!(PlotterClass[pPDevice->Plotter].fsOdd & OD_VERTICAL_P1P2))
    //{
    //  SWAPLONG(pPDevice->DefaultP1P2.yBottom, pPDevice->DefaultP1P2.xLeft)
    //  SWAPLONG(pPDevice->DefaultP1P2.yTop   , pPDevice->DefaultP1P2.xRight)
    //}
  }

  /*
  ** Identify the HPGL capabilities
  */
  pPDevice->usHPGLType = PlotterClass[Plotter].usHPGLCapsGEC;

  if (Plotter == CLASS_COLORPRO && !pSetup->bGECOption)
  {
    pPDevice->usHPGLType = PlotterClass[Plotter].usHPGLCaps;
  }                                    /* endif                             */

  /*
  ** allocate the HPGL2 pen palette if the device supports
  ** dynamic hpgl2 pens
  */
  if (PlotterClass[Plotter].usMaxHPGL2Pens)
  {
      pPDevice->paPaletteEntry =
          (PPALETTEENTRY)GplMemoryAlloc( pPDevice->hmcbHeap,
            PlotterClass[Plotter].usMaxHPGL2Pens * sizeof(PALETTEENTRY));
      pPDevice->usPensInPalette  = 0;
      pPDevice->usLastPenDefined = 0;
  }

  setup_colors(pPDevice);

  /*
  **   Set default character sizes as 1.87mm wide by 2.69mm high. NOTE: The
  **  factors of 3/2 and 2 are related to the way the plotter font is
  **  defined.  For a given character box size,  the plotter characters
  **  are set to 2/3 the width and half the height.  This produces correct
  **  spacing.  So,  to get the default chaaracters to the desired size,
  **  the sizes below are adjusted.  Also see CharStringPos in textout.c
  */
  /*
  ** setup for 12 point font default
  */
  //pPDevice->lCapsDefCharHeight =(12*PlotterClass[Plotter].lResInPelsPerInch)/72;
  pPDevice->lCapsDefCharHeight =(12 * pPDevice->lRes_YPelsPerInch)/72;

  pPDevice->lCapsDefCharWidth  = pPDevice->lCapsDefCharHeight;
  pPDevice->lCapsDefMarkerWidth = pPDevice->lCapsDefCharHeight;
  pPDevice->lCapsDefMarkerHeight = pPDevice->lCapsDefCharHeight;

  pPDevice->bStandardCharSetActive = TRUE;

  /*
  ** Setup the default font metrics
  */
  //lstrcpy(pfmDefaultFont->szFamilyname,"Helvetica");
  //lstrcpy(pfmDefaultFont->szFacename,"Helvetica");
  lstrcpy(pfmDefaultFont->szFamilyname,"Stick");
  lstrcpy(pfmDefaultFont->szFacename,"Stick");
  pfmDefaultFont->idRegistry = 0;
  pfmDefaultFont->usCodePage = CP_850;
  pfmDefaultFont->lEmHeight = pPDevice->lCapsDefCharHeight;
  pfmDefaultFont->lInternalLeading = pPDevice->lCapsDefCharHeight/8;
  pfmDefaultFont->lMaxDescender = pPDevice->lCapsDefCharHeight/4;
  pfmDefaultFont->lMaxAscender  = pfmDefaultFont->lEmHeight -
                                  pfmDefaultFont->lMaxDescender +
                                  pfmDefaultFont->lInternalLeading;
  pfmDefaultFont->lLowerCaseAscent = pfmDefaultFont->lMaxAscender;
  pfmDefaultFont->lLowerCaseDescent = pfmDefaultFont->lMaxDescender;
  pfmDefaultFont->lXHeight  = pfmDefaultFont->lEmHeight -
                              pfmDefaultFont->lMaxDescender;
  pfmDefaultFont->lExternalLeading = 0;
  pfmDefaultFont->lAveCharWidth = pPDevice->lCapsDefCharWidth;
  pfmDefaultFont->lMaxCharInc = pPDevice->lCapsDefCharWidth;
  pfmDefaultFont->lEmInc = pPDevice->lCapsDefCharWidth;
  pfmDefaultFont->lMaxBaselineExt = pfmDefaultFont->lEmHeight;
  pfmDefaultFont->sCharSlope    = 0;
  pfmDefaultFont->sInlineDir    = 0;
  pfmDefaultFont->sCharRot      = 0;
  pfmDefaultFont->usWeightClass = FONTWEIGHT;
  pfmDefaultFont->usWidthClass  = FONTWEIGHT;
  pfmDefaultFont->sXDeviceRes   = (USHORT)pPDevice->lRes_XPelsPerInch;
  pfmDefaultFont->sYDeviceRes   = (USHORT)pPDevice->lRes_YPelsPerInch;
  pfmDefaultFont->sFirstChar    = FIRSTCHAR;
  pfmDefaultFont->sLastChar     = LASTCHAR;
  pfmDefaultFont->sDefaultChar  = DEFAULTCHAR;
  pfmDefaultFont->sBreakChar    = BREAKCHAR;
  pfmDefaultFont->sNominalPointSize   = 120;
  pfmDefaultFont->sMinimumPointSize   = 40;
  pfmDefaultFont->sMaximumPointSize   = 10000;
  //pfmDefaultFont->sMaximumPointSize = 1440;
  pfmDefaultFont->fsType = 0x0001;
  pfmDefaultFont->fsDefn = 0x0001;
  pfmDefaultFont->fsSelection     = 0;
  pfmDefaultFont->fsCapabilities  = 0;
  pfmDefaultFont->lSubscriptXSize     = pPDevice->lCapsDefCharWidth/3;
  pfmDefaultFont->lSubscriptYSize     = pPDevice->lCapsDefCharHeight/3;
  pfmDefaultFont->lSubscriptXOffset   = pPDevice->lCapsDefCharWidth;
  pfmDefaultFont->lSubscriptYOffset   = pPDevice->lCapsDefCharHeight;
  pfmDefaultFont->lSuperscriptXSize   = pPDevice->lCapsDefCharWidth/3;
  pfmDefaultFont->lSuperscriptYSize   = pPDevice->lCapsDefCharHeight/3;
  pfmDefaultFont->lSuperscriptXOffset = pPDevice->lCapsDefCharWidth;
  pfmDefaultFont->lSuperscriptYOffset = pPDevice->lCapsDefCharHeight;
  pfmDefaultFont->lUnderscoreSize     = 1;
  //too low
  //pfmDefaultFont->lUnderscorePosition = -1;
  pfmDefaultFont->lUnderscorePosition   =  0;
  pfmDefaultFont->lStrikeoutSize        =  1;
  //too low
  //pfmDefaultFont->lStrikeoutPosition = pPDevice->lCapsDefCharHeight/3;
  pfmDefaultFont->lStrikeoutPosition = pfmDefaultFont->lMaxAscender/2;
  pfmDefaultFont->sKerningPairs = 0;
  pfmDefaultFont->sFamilyClass = 0;
  pfmDefaultFont->lMatch = PLOTTER_FONT_MATCH;
  /* end setup default font metrics */


  if ((PlotterClass[Plotter].usHPGLCaps & HPGL_CHARSET7) || pSetup->bGECOption)
    pPDevice->AltCharSet = 7;
  else
    pPDevice->AltCharSet = 2;

  return ;
}

/***************************************************************************
 *
 * FUNCTION NAME = reset_dc_state
 *
 *
 * DESCRIPTION   = reset a DC
 *
 *                 This Enable subfunction is called to reset a DC to
 *                 its original initialized state.
 *
 *
 *
 *
 * INPUT         = (pDDC,Flags)
 *
 *
 *
 * OUTPUT        = NONE
 *
 *
 *
 *
 * RETURN-NORMAL = 0L
 *
 *
 *
 * RETURN-ERROR  = NONE
 *
 *
 *
 ***********************************************************************/

LOCAL LONG  reset_dc_state(PDDC pDDC,ULONG Flags)
{
  PPDEVICE pPDevice = pDDC->pPDevice;
  USHORT usDCType = pDDC->usDCType;

  if (pDDC->usDCType == OD_MEMORY)
  {
    if (pDDC->lEngineVersion < RASTER_ENGINE_22)
    {
      /*
      ** This is a memory DC, so we simply pass the request for a
      ** DC reset on to the Gre.     Kran
      */
      if (GreResetDC((ULONG) pDDC->hdcMemory,Flags))
        return(0L);
      else
        return( -1L );
    }
    else  /* for a memory DC on the NEW GRE we have nothing to do */
      return(0L);
  }

  /* this is not a MEMORYDC, So work out as usual.          Kran */
  pDDC->pPDevice = pPDevice;
  pDDC->usDCType = usDCType;
  initialize_ddc(pDDC);

  return (0L);
}

/***************************************************************************
 *
 * FUNCTION NAME = restore_dc_state
 *
 *
 * DESCRIPTION   = POP a saved DC
 *                 This Enable subfunction is called when the engine wishes the
 *                 device driver to POP a saved DC, or POP to a specified DC.
 *                 The following shows the meaning of number:
 *
 *
 *                 If Number = 0, then error.
 *                 If Number > 0, it specifies which state to restore, and the
 *                                others are lost, (if Number is 2,
 *                                then the secondit specifies which
 *                                state to restore, and the DC is
 *                                restored, the first DC is saved,
 *                                and all others are lost, (if
 *                                Number is 2, then the second
 *                                others are lost.  DC is restored,
 *                                the first DC is saved, and all
 *
 *
 *                 If Number < 0, it specifies how many states to pop back,
 *                                (if Number is -1, then we pop back 1 state.
 *
 *
 *
 * INPUT         = (pDDC,Number)
 *
 *
 * OUTPUT        = NONE
 *
 *
 * RETURN-NORMAL = 0L
 *
 *
 *
 * RETURN-ERROR  = NONE
 *
 *
 *
 ***********************************************************************/

LOCAL LONG  restore_dc_state(PDDC pDDC,LONG lNumber)
{
  LONG lResult = -1L;

  /* First things first. if it is MEMORYDC call GRE and Getout.   Kran    */
  /* May be We should check for the validity of Number before calling Gre */
  if (pDDC->usDCType == OD_MEMORY)
  {
    if (pDDC->lEngineVersion < RASTER_ENGINE_22)
    {
      /*
      ** This is a memory DC, so we simply pass the request for a
      ** DC restore on to the Gre.
      */
      if (GreRestoreDC (pDDC->hdcMemory,lNumber))
        return( 0L );
      else
        return( -1L );
    }
    else  /* for a memory DC on the NEW GRE we have nothing to do */
      return(0L);
  }

  if (lNumber > 0 && lNumber <= pDDC->lNumSavedDCs)
  {
    /*
    ** Restore the level=lNumber
    */
    LONG lState = lNumber;
    PDCSTATE pDCState = pDDC->DCState.pSavedDCState;/* start at first       */
    PDCSTATE pNextDCState;


    while (pDCState->lStateID != lState)
    {
      pNextDCState = pDCState->pSavedDCState;/* store next                  */

      /*
      ** if a color table was saved free it
      */
      if (pDCState->pColorTable)
      {
        if (GplMemoryFree ((PVOID)pDCState->pColorTable))
          pDCState->pColorTable = (PCOLORTABLE)NULL;
      }

      /*
      ** free Saved DC State info
      */
      if (GplMemoryFree ((PVOID)pDCState))
        pDCState = (PDCSTATE)NULL;
      pDDC->lNumSavedDCs--;
      pDCState = pNextDCState;         /* move to next                      */
    }

    if (pDDC->DCState.pColorTable)
    {
      /*
      ** free color table of the DC info we are overwriting
      */
      if (GplMemoryFree ((PVOID)pDDC->DCState.pColorTable))
        pDDC->DCState.pColorTable = (PCOLORTABLE)NULL;
    }

    /*
    ** restore state info
    */
    pDDC->DCState = *pDCState;
    pDDC->lNumSavedDCs--;

    /*
    ** free saved DC State info
    */
    if (GplMemoryFree ((PVOID)pDCState))
      pDCState = (PDCSTATE)NULL;

    lResult = 0L;
  }
  else
    if (lNumber < 0 && (-lNumber <= pDDC->lNumSavedDCs))
    {
      /*
      ** Pop the number off the stack
      */
      LONG lCount = -lNumber;
      PDCSTATE pDCState = pDDC->DCState.pSavedDCState;/* Start at root      */
      PDCSTATE pNextDCState;


      while (lCount--)
      {
        pNextDCState = pDCState->pSavedDCState;/* Next saved DC             */

        if (!lCount)
        {
          /*
          ** Restore the DC State
          */
          if (pDDC->DCState.pColorTable)
          {
            /*
            ** free color table of the DC info we are overwriting
            */
            if (GplMemoryFree ((PVOID)pDDC->DCState.pColorTable))
              pDDC->DCState.pColorTable = (PCOLORTABLE)NULL;
          }

          /*
          ** Restore state
          */
          pDDC->DCState = *pDCState;
        }
        else
        {
          if (pDCState->pColorTable)
          {
            /*
            ** free Saved color table
            */
            if (GplMemoryFree ((PVOID)pDCState->pColorTable))
              pDCState->pColorTable = (PCOLORTABLE)NULL;
          }
        }

        /*
        ** free the saved DC info struct
        */
        if (GplMemoryFree ((PVOID)pDCState))
          pDCState = (PDCSTATE)NULL;

        /*
        ** dec. the number of saved DCs counter
        */
        pDDC->lNumSavedDCs--;
        pDCState = pNextDCState;       /* Move to next                      */
      }
      lResult = 0L;
    }

  return (lResult);
}

/***************************************************************************
 *
 * FUNCTION NAME = save_dc_state
 *
 *
 * DESCRIPTION   = save all DC information
 *                 This Enable subfunction is called when the engine
 *                 wishes a device driver to save all DC information.
 *                 This may be called multiple times and the driver should
 *                 push the DDC in LIFO order.
 *
 *
 * INPUT = (pDDC)
 *
 * OUTPUT = NONE
 *
 *
 * RETURN-NORMAL = lResult
 *
 *
 * RETURN-ERROR = NONE
 *
 *
 ************************************************************************/

LOCAL LONG  save_dc_state(PDDC pDDC)
{
  LONG lResult = (LONG)-1L;
  PDCSTATE pPushedDCState;

  if ( pDDC->usDCType == OD_MEMORY )
  {
    if (pDDC->lEngineVersion < RASTER_ENGINE_22)
    {
      /*
      ** This is a memory DC, so we simply pass the request for a
      ** DC save  on to the GRE.    Kran
      */
      if (GreSaveDC(pDDC->hdcMemory))
        return( 0L );
      else
        return( lResult );
    }
    else  /* for a memory DC on the NEW GRE we have nothing to do */
      return(0L);
  }

  pPushedDCState = (PDCSTATE)GplMemoryAlloc(pDDC->pPDevice->hmcbHeap,
                                                      sizeof(DCSTATE));

  if (pPushedDCState)
  {
    *pPushedDCState = pDDC->DCState;

    /*
    ** Watch out for pointers saved in the state info.
    ** We must make new copies of the mem to correctly save the
    ** state.
    */
    if (pDDC->DCState.pColorTable)
    {
      /*
      ** if we have one make a copy
      */
      pPushedDCState->pColorTable = (PCOLORTABLE)GplMemoryAlloc
         (pDDC->pPDevice->hmcbHeap,
          MAX_COLOR_INDEX *sizeof(COLORTABLE));


      if (pPushedDCState->pColorTable)
      {
        /*
        ** Mem alloc OK.  Copy the color table.
        */
        CopyMem((PBYTE)pPushedDCState->pColorTable,
                    (PBYTE)pDDC->DCState.pColorTable,
                    (USHORT)(MAX_COLOR_INDEX *sizeof(COLORTABLE)));
      }
      else
      {
        /*
        ** Alloc color table failed-free save info and fail
        */
        if (GplMemoryFree ((PVOID)pPushedDCState))
          pPushedDCState = (PDCSTATE)NULL;

        return (lResult);               /* error                             */
      }
    }

    /*
    ** Every thing is OK.
    ** Update the Saved DC chain, Count, and Saved State ID.
    */
    pDDC->DCState.pSavedDCState = pPushedDCState;
    ++pDDC->lNumSavedDCs;
    pPushedDCState->lStateID = pDDC->lNumSavedDCs;
    lResult = 0L;                       /* OK                                */
  }

  return (lResult);
}

/***************************************************************************
 *
 * FUNCTION NAME = setup_colors
 *
 *
 * DESCRIPTION   = set up color information for the plotter
 *                 This function is called from fill_pdev and sets up
 *                 the color information for the selected plotter.
 *                 This function performs the following:
 *
 *                 1) Counts up the number of carousels available,
 *                    and finds the first active carousel.  If no
 *                    carousels are available, then it forces
 *                    carousel 0 to be active.
 *
 *                 2) Determines if multi-carousel is enabled.
 *                    Multi-carousel requires that more than 1
 *                    carousel is active AND that color sorting is
 *                    enabled.  If multi carousel is not enabled,
 *                    then we insure that only the 1 active carousel
 *                    is marked.  This must be done for other
 *                    functions to function properly.
 *
 *                 3) After we know our active carousels, we determine
 *                    what colors we have to work with.
 *
 *                 4) After we know our colors, we get a total so we
 *                    don't have to go through the table again just to
 *                    get a count.
 *
 *
 *
 *
 *
 * INPUT         = (pPDevice)
 *
 *
 *
 * OUTPUT        = NONE
 *
 *
 *
 *
 * RETURN-NORMAL = NONE
 *
 *
 *
 * RETURN-ERROR  = NONE
 *
 *
 *
 **********************************************************************/

LOCAL VOID  setup_colors(PPDEVICE pPDevice)
{
  PPROFILEINFO pSetup = pPDevice->pSetup;
  SHORT Plotter = pPDevice->Plotter;

  /*
  ** if the user has selected to use the application colors
  ** Not the user defined pen colors set the number of colors available
  ** to the hpgl2 pen palette size
  */
  if (pSetup->usFlags & FL_USEAPPCOLORS)
  {
    pPDevice->NumColorsAvailable = PlotterClass[Plotter].usMaxHPGL2Pens;
  }
  /*
  ** else setup carousel info and calculate the number of available
  ** pen colors
  */
  else
  {
    BOOL  bCarouselFound = FALSE;
    SHORT Count,Carousel,Color,Pen,iPenNumberInPalette;
    /*
    ** Count number of active carousels, and find first active
    */

    for (Carousel = 0, Count = 0; Carousel < MAX_CAROUSELS; Carousel++)
      if (pSetup->bActiveCarousel[Carousel])
      {
        ++Count;

        if (!bCarouselFound)
        {
          bCarouselFound = TRUE;
          pPDevice->CurrentCarousel = Carousel;
        }
      }

    /*
    ** Now determine if multi-carousel is enabled
    */
    if (Count > 1 && (pSetup->bColorSort ||
                      PlotterClass[Plotter].usHPGLCaps & HPGL2))
    {
      pPDevice->bMultiCarousel = TRUE;
      pPDevice->CurrentCarousel = 0;
    }
    else
    {
      for (Carousel = 0; Carousel < MAX_CAROUSELS; Carousel++)
        pSetup->bActiveCarousel[Carousel] = FALSE;

      pSetup->bActiveCarousel[pPDevice->CurrentCarousel] = TRUE;
    }

    /*
    ** Now figure out which colors we have available
    */
    iPenNumberInPalette = 1;             /* pen 0 is reserved                 */

    for (Carousel = 0, Count = 0; Carousel < MAX_CAROUSELS; Carousel++)
      if (pSetup->bActiveCarousel[Carousel])
        for (Pen = 0; Pen < MAX_PENS; Pen++)
          if ((Color = (SHORT)pSetup->Carousel[Carousel].Pen[Pen].Color))
          {
            pPDevice->ColorAvailable[Color-1] = TRUE;
            pPDevice->iHPGL2PenNumber[Carousel][Pen] = iPenNumberInPalette;
            iPenNumberInPalette++;
          }
          else
          {
            pPDevice->ColorAvailable[Color-1] = FALSE;
          }

    /*
    ** Now total up the colors available
    */
    for (Color = 0; Color < MAX_COLORS; Color++)
      if (pPDevice->ColorAvailable[Color])
        ++pPDevice->NumColorsAvailable;
  }
}
/*
** Exported Routines
*/
/***************************************************************************
 *
 * FUNCTION NAME = Enable
 *
 *
 * DESCRIPTION   = Initialize device driver
 *
 *                 This ENABLE function is exported by the device
 *                 driver.  This function performs initialization of
 *                 the device driver, the physical driver, and device
 *                 contexts.  Refer to the individual subfunctions of
 *                 Enable for further description of the parameters.
 *
 *
 * INPUT         = SubFunc,Param1,Param2
 *
 *
 *
 * OUTPUT        = NONE
 *
 *
 *
 *
 * RETURN-NORMAL = lResult
 *
 *
 *
 * RETURN-ERROR  = NONE
 *
 *
 *
 ***********************************************************************/

LONG  Enable(ULONG SubFunc,ULONG Param1,ULONG Param2)
{
  LONG lResult = -1L;


  switch (LOUSHORT(SubFunc))
  {
    case 1 :
      lResult = fill_ldev((PLDEVPARAM2)Param2);
      break;
    case 2 :
      lResult = fill_pdev((PPDEVPARAM1)Param1,(ULONG) Param2);
      break;
    case 4 :
      lResult = disable_pdev((PPDEVICE)Param1);
      break;
    case 5 :
      lResult = enable_dc_begin((PDENPARAMS)Param1);
      break;
    case 6 :
      lResult = disable_dc_complete((PDDC)Param1);
      break;
    case 7 :
      lResult = save_dc_state((PDDC)Param1);
      break;
    case 8 :
      lResult = restore_dc_state((PDDC)Param1, (LONG)Param2);
      break;
    case 9 :
      lResult = reset_dc_state((PDDC)Param1, Param2);
      break;
    case 10 :
      lResult = enable_dc_complete((HDC)Param1, (PDDC)Param2);
      break;
    case 11 :
      lResult = disable_dc_begin((HDC)Param1, (PDDC)Param2);
      break;
    case 14 :
      lResult = QueryDeviceSurface( (PDEVICESURFACE)Param1, (PDDC)Param2 );
      break;
  }

  return (lResult);
}

/***************************************************************************
 *
 * FUNCTION NAME = QueryDeviceSurface
 *
 * DESCRIPTION   = This routine is used by the GRE to query the device surface
 *                 info.
 *
 * INPUT         = pddc, unused
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = (SUCCESS)
 *
 * RETURN-ERROR  = (FAILURE)
 *
 **************************************************************************/
//remove this when ddi.h has new flag
#ifndef DS_NOTIFY_LAST_SDBITBLT
#define DS_NOTIFY_LAST_SDBITBLT     0x00000800
#endif

#ifndef  DS_BITBLT_TOPBOTTOM_RIGHTLEFT
#define  DS_BITBLT_TOPBOTTOM_RIGHTLEFT 0x00400000
#endif
#ifndef  DS_BITBLT_RIGHTLEFT_TOPBOTTOM
 #define  DS_BITBLT_RIGHTLEFT_TOPBOTTOM  0x02000000
#endif
#ifndef  DS_BITBLT_LEFTRIGHT_BOTTOMTOP
 #define  DS_BITBLT_LEFTRIGHT_BOTTOMTOP 0x04000000
#endif

LONG APIENTRY QueryDeviceSurface( PDEVICESURFACE pDS,  PDDC pDDC)
{
  PPDEVICE pPDevice;
  USHORT   usDCType;
  ULONG    ulDSFlgs = 0L;
  ULONG    fulOddCaps;

  if (pDS == NULL && pDDC == NULL)
  {
    return(0);  //return of 0 is OK
  }
  pPDevice   = pDDC->pPDevice;
  fulOddCaps = PlotterClass[pPDevice->Plotter].fsOdd;
  usDCType   = pDDC->usDCType;

  /*
  ** Store the pointer in Plotters DDC.  The DS contains the surface
  ** device structure.
  */
  pDDC->pDeviceSurface = pDS;

  /*
  ** These fields are required.
  */
  pDS->ulLength  = sizeof( DEVICESURFACE );

  ulDSFlgs        =  DS_TOPBOTTOM  |
                     DS_BYTEALIGN  |
                     DS_KEEP_EXTFORMAT |
                     (pDDC->usDCType != OD_MEMORY ? DS_NOTIFY_LAST_SDBITBLT:0);


  if (!(fulOddCaps &  OD_COLOR))
    ulDSFlgs     |=  DS_MONO_INVERT;

  /*
  ** if the device is a raster device  set up the
  ** bitmap rotation
  */
  if (fulOddCaps &  OD_HPRTL)
  {
    if (usDCType == OD_DIRECT ||
       ((usDCType == OD_QUEUED) && (pDDC->pPDevice->DataType == PM_Q_RAW )))
    {
       ulDSFlgs       |=  (pDDC->pPDevice->fsTransform & DXF_PORTRAIT ?
                          DS_BITBLT_TOPBOTTOM_RIGHTLEFT :
                          (pDDC->lEngineVersion > RASTER_ENGINE_22 ?
                            DS_BITBLT_LEFTRIGHT_BOTTOMTOP :
                            DS_BITBLT_RIGHTLEFT_TOPBOTTOM));
    }
  }
  pDS->ulDSFlgs  =  ulDSFlgs;

  /*
  ** Style ratio
  */
  pDS->ulStyleRatio = MAKEUSHORT (0x64, 0x64);

  pDS->pHWPalette   = (PVOID)ulDefaultPalette256;

  /*
  ** Set the caps.  This is done in QueryDeviceCaps.  The only problem
  ** is that the function was designed to work with OD_MEMORY.  For this case,
  ** temporarily set the type to 0, call the function, then set it back to
  ** the original ID.
  */
  pDDC->usDCType = OD_DIRECT;
  pDS->ulCapsCnt = CAPS_MAX_CAPS;
  QueryDeviceCaps( (HDC)0L, 0L, pDS->DevCaps, CAPS_MAX_CAPS, pDDC, 0 );
  pDDC->usDCType = usDCType;

  /*
  ** Since we hook sdBitBlt we put our pddc in pbits
  ** and we do not hook any of the bitmap functions
  ** in dispatch.c.
  ** Note: do not hook sdBitBlt for Memory DC
  */
  if (pDDC->usDCType != OD_MEMORY)
  {
    if (pPDevice->pSetup->usFlags & FL_RASTER)
    {
      pDS->SurfaceBmapInfo.pBits          = (PBYTE)NULL;
    }
    else
    { /* hook sdBitBlt
      ** Do not allocate the surface bits
      ** we do not write to them
      */
      pDS->SurfaceBmapInfo.pBits          = (PBYTE)pDDC;
      pDS->pfnBitBlt                      = (PFN)sdBitBlt;
    }
  }

  pDS->SurfaceBmapInfo.ulLength       = sizeof( BMAPINFO );

  pDS->SurfaceBmapInfo.ulType         = BMAP_VRAM;
  //if (pPDevice->pSetup->usFlags & FL_RASTER)
  //  pDS->SurfaceBmapInfo.ulType         = BMAP_VRAM;
  //else
  //  pDS->SurfaceBmapInfo.ulType         = BMAP_MEMORY;

  pDS->SurfaceBmapInfo.ulWidth        = pDS->DevCaps[ CAPS_WIDTH ];
  pDS->SurfaceBmapInfo.ulHeight       = pDS->DevCaps[ CAPS_HEIGHT ];
  if (!(fulOddCaps &  OD_COLOR))
    pDS->SurfaceBmapInfo.ulBpp        = CAPS_COLOR_BITCOUNT_1;
  else
    pDS->SurfaceBmapInfo.ulBpp        = pDS->DevCaps[ CAPS_COLOR_BITCOUNT ];
  pDS->SurfaceBmapInfo.ulBytesPerLine = (((pDS->SurfaceBmapInfo.ulWidth *
                                           pDS->SurfaceBmapInfo.ulBpp) + 7)/8);

  /*
  ** if the device is not color set up the engine to dither
  ** color images
  */
  if (!(fulOddCaps &  OD_COLOR))
  {
    pDS->DitherMatrix.ulLength        = sizeof (DITHERMATRIX);
    pDS->DitherMatrix.fLog2PhysSup    = GDM_MATRIX_DITHER;
  }
  else
    pDS->DitherMatrix.fLog2PhysSup    = GDM_USERDEF_DITHER;


  /*
  ** create Patterns for our device
  */
  if (pDDC->usDCType == OD_MEMORY )
  {
    if (!pDDC->pPDevice->bAllocatedPatterns)
    {
      if (GplPatternCreateBitmaps(pDDC->pPDevice->hmcbHeap,
                                  pDDC->pPDevice->lRes_XPelsPerInch,
                                  pDDC->pPDevice->lRes_YPelsPerInch,
                                  64L, 64L,
                                  (PBMAPINFO)&pDS->abmapinfoDefPattern,
                                  DEFAULT_PATTERNS_NUMBER))
      {
        pDDC->pPDevice->bAllocatedPatterns = TRUE;
      }
    }
  }

  return( 0 );
}

