/*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 = ENABLE.C
 *
 * DESCRIPTIVE NAME = Contains Enable function and related routines.
 *
 *
 * VERSION = V2.0
 *
 * DATE
 *
 * DESCRIPTION PRINTER DRIVER SOURCE
 *
 *
 * FUNCTIONS
 *
 *                 Enable              This call switches to the required
 *                                     Enable subfunction.
 *
 *                 prde_EnableDC       This routine implements subfunction 5
 *                                     of the "enable" entry point called
 *                                     by the engine.
 *
 *                 prde_SetDDCDefaults This routine initializes the values in
 *                                     the DC instance data structure to
 *                                     their default values.
 *
 *
 *                 prde_CloseDisplay  This routine closes the display driver
 *                                    memory DC.
 *
 *                 prde_DisableDC     This routine disables the ddc
 *                                    and releases it's memory.
 *
 *                 prde_DisableDC     This routine frees up all heap
 *                                    space used by extra DDC structures
 *                                    that were created by "Save DC" requests.
 *
 *                 prde_ResetDC       This routine is Enable subfunction 9.
 *                                    Resets the ddc state.
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
 *     extern ULONG   prdc_InstallColorTable (ULONG, LONG, LONG, LONG  *, PDDC);
 *     extern void   szCopy(PSZ, PSZ, int);
 *     extern void  CloseLogFile();
 *     extern void    ExitDriver( PDDC pddc );
 *     extern PFNT   PfntFromIndex( PDDC, SHORT );
 *     extern VOID   FreeFontResource( PFONTDEF, PFNT );
 *
*/

#pragma pack(1)
#define INCL_GPILOGCOLORTABLE
#define INCL_DEV
#include "inc\prdinclt.h"
#include <pmdev.h>
#include "inc\prdgextf.h"
#include "inc\prdeextf.h"
#include "inc\utl.h"
#include "inc\prderre.h"
#include "inc\prdatypt.h"
#include "inc\prdaextf.h"
#include "inc\prdcextf.h"
#include "inc\prdmath.h"
#include "inc\prdtextf.h"
#include "inc\pspagtun.h"              /* V2.174057   Page Tuning */
#define  INCL_GENPLIB_ERROR
#define  INCL_GENPLIB_MEMORY
#define INCL_GENPLIB_PATTERNS          /* genplib pattern create/del        */
#include <genplib.h>
/*            */
extern ULONG ULGreVersion;

#if IPMD
#include <builtin.h>
#endif

typedef struct _STYLE_RATIO
{
    UCHAR   rx;
    UCHAR   ry;
}STYLE_RATIO,  *PRATIO;

typedef struct _PBSAVE
{
  SHORT usLineJoin;
  SHORT usLineCap;
  FIXED fxLineWidth;
  LONG lColor;               /* Current RGB color                           */
} PBSAVE, *PPBSAVE;

#define CP_LIMIT    10

extern ULONG  prdc_InstallColorTable (ULONG, LONG, LONG, LONG  *, PDDC);
extern void   szCopy(PSZ, PSZ, int);                    /* utlps.c */
#if DEBUG
extern void   CloseLogFile();
#endif
extern void   ExitDriver( PDDC pddc );
extern VOID   EnterDriver( PDDC pddc );
extern PFNT   PfntFromIndex( PDDC, SHORT );             /* charstr2.c */
extern VOID   FreeFontResource( PDV, PFONTDEF, PFNT );  /* charstr2.c */
extern USHORT prdq_EndDoc(HDC, PDDC, PUSHORT, PLONG);
extern LONG APIENTRY QueryDeviceSurface( PDDC pddc, PDEVICESURFACE pDS );

/*
** !!! The EnterDriver/LeaveDriver stuff below needs to be thought out
** !!! more carefully.  Do we even want to be locking the DDC on these
** !!! enable calls?  Do we want to leave the DDC locked between the
** !!! BEGIN_CLOSE_DC and the DISABLE_DC calls?  etc.
** !!!
** !!! What happens to our lock count when a DC gets SAVED/RESTORED?
** !!! This sounds like potential disaster.
*/


HMTX tsemDriver,
     tsemLPT1,
     tsemLPT2,
     tsemLPT3,
     tsemCOM1,
     tsemCOM2,
     tsemCOM3;

void init_semaphores()
{
 DosCreateMutexSem ("\\sem32\\driver", &tsemDriver,0L, FALSE);
 DosCreateMutexSem ("\\sem32\\lpt1", &tsemLPT1,0L, FALSE);
 DosCreateMutexSem ("\\sem32\\lpt2", &tsemLPT2,0L, FALSE);
 DosCreateMutexSem ("\\sem32\\lpt3", &tsemLPT3,0L, FALSE);
 DosCreateMutexSem ("\\sem32\\com1", &tsemCOM1,0L, FALSE);
 DosCreateMutexSem ("\\sem32\\com2", &tsemCOM2,0L, FALSE);
 DosCreateMutexSem ("\\sem32\\com3", &tsemCOM3,0L, FALSE);
}

extern HMODULE pscript_module;

/*
** The OS2_PM_DRV_ENABLE entry point handles the DC management functions.
** Those functions can be placed in three categories:
**
**   1. Transactions involved in opening a DC.
**
**   2. Transactions involved in closing a DC.
**
**   3. Other DC functions.  This includes SaveDC, RestoreDC, and ResetDC.
**
**
** Opening and Closing DC's
** ------------------------
**
** The open and close transaction sequences are symmetric:
**
**    Open Transactions       Close Transactions
** |  -----------------    ^  ------------------
** |                       |
** |   FillLogDevBlk       |    ... No Equivalent ...
** |                       |
** |   FillPhyDevBlk       |    DisablePhyDevBlk
** |                       |
** |   EnableDC            |    DisableDC
** |                       |
** V   CompleteOpenDC      |    BeginCloseDC
**
** Each of the Close transaction undoes the actions taken by its corresponding
** Open transaction.  For instance the EnableDC function allocates the DDC data
** area within the driver and returns the hDDC handle to identify that area.
** The corresponding DisableDC function invalidates the hDDC handle and
** deallocates the DDC data area.
**
** The FillLogDevBlk transaction occurs when some thread of a process opens the
** first instance of a DC for this driver.  During this transaction a dispatch
** table will be created for later use by the graphics engine.
**
** Except for the OS2_PM_DRV_ENABLE, OS2_PM_DRV_DEVMODE and OS2_PM_DRV_DEVNAMES
** calls, all DDI interface calls are dispatched through the function table
** created here.  This means that most calls go directly to the correct
** function rather than indirecting through a hierarchy of function layers.
**
** The FillPhyDevBlk transaction will occur only if the result flags from
** the FillLogDevBlk transaction indicate that it is necessary.  It's used
** by device drivers which support multiple types of physical devices.  It
** will occur during the DevOpen for the first DC associated with a particular
** type of physical device.
**
** This is where any memory allocation and initializations relating to a
** physical device should be done.
**
** During the EnableDC transaction the driver will allocate and format the
** driver specific data (the DDC) associated with the DC being constructed.
**
** At CompleteOpenDC time the DC is completely constructed and initialed.
** This transaction gives the driver an opportunity to make final adjustments
** to the DC before the application gets access to it.
**
**
** Saving and Restoring DC's
** -------------------------
**
** These functions manage a stack of entries that correspond to most of the
** state of a DC.  SaveDC pushes an entry on the stack.  RestoreDC retrieves
** one of the push'd entries and uses it to revert the DC back to its earlier
** saved state.  Note that a particular RevertDC call may pop more than one
** entry from stack of SaveDC entries.
**
**
** Reset DC
** --------
**
** This call reset a DC to its original initialized state as it was just after
** the CompleteOpenDC transaction.
**
*/

/***************************************************************************
 *
 * FUNCTION NAME = Enable
 *
 * DESCRIPTION   = This call switches to the required Enable subfunction.
 *
 * INPUT         = subfunc, param1, param2
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = (ULONG)ENG_OK)
 *
 * RETURN-ERROR  = (ULONG)(ERROR_NEG)
 *
 **************************************************************************/

/* ULONG prde_Enable( ULONG subfunc, PB param1, PB param2 ) */
ULONG _System OS2_PM_DRV_ENABLE( ULONG subfunc, PB param1, PB param2 )

{
  SHORT  usResult;
  ULONG  DResult;
  PDDC   pddc;
  HDC    hdc;

  init_semaphores ();

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

  switch ((SHORT) subfunc)
  {
  case ENABLE_LDB:
       #if ipmd
       _interrupt(3);
       #endif

       /*            Set the gre version */
       ULGreVersion = ((FLParamsLst *) param1)->ulVersion;

       usResult = prde_FillLdb( (FLParamsLst *)param1,
                                (FLReturnsLst *) param2);

       if (usResult != SUCCESS)
       {
         RIP( "Enable: ENABLE_LDB - prde_FillLdb failed");
       }
       break;

  case ENABLE_PDB:
       return( prde_FillPdb( (PDEVOPENSTRUC)param1,
                             (Dword) param2));
       break;

  case DISABLE_PDB:
      usResult = prde_DisablePdb( (PDV)param1, param2 );
      if (usResult != SUCCESS)
      {
          RIP( "Enable: DISABLE_PDB - prde_DisablePpb failed" );
      }
      break;

  case ENABLE_DC:
       return( DResult = prde_EnableDC( (EDCParamsLst  *)param1,
                                        (Dword) param2));
       break;

  case DISABLE_DC:
       pddc = (PDDC) param1;
       EnterDriver( pddc );
       if ( pddc == (PDDC) INVALID_ADDRESS )
       {
         RIP( "Enable: DISABLE_DC - invalid pddc address" );
         usResult = (SHORT) ERROR_NEG;
         break;
       }

       usResult = prde_DisableDC( pddc );

       /*
       ** ExitDriver( pddc  ); pddc is now dead, so we can't unlock it.
       */
       if (usResult != SUCCESS)
       {
         RIP( "Enable: DISABLE_DC - prde_DisableDC failed" );
       }
       break;

  case SAVE_DC_STATE:
       pddc = (PDDC) param1;
       EnterDriver( pddc );
       if ( pddc == (PDDC)  INVALID_ADDRESS )
       {
         RIP( "Enable: SAVE_DC - invalid pddc address" );
         usResult = (SHORT) ERROR_NEG;
         break;
       }
       usResult = prde_SaveDC( pddc, (ULONG) param2 );
       ExitDriver( pddc );
       if (usResult != SUCCESS)
       {
         RIP( "Enable: prde_SaveDC failed" );
       }
       break;

  case RESTORE_DC_STATE:
       pddc = (PDDC) param1;
       EnterDriver( pddc );
       if ( pddc == (PDDC)  INVALID_ADDRESS )
       {
         RIP( "Enable: RESTORE_DC - invalid pddc address" );
         usResult = (SHORT) ERROR_NEG;
         break;
       }
       usResult = prde_RestoreDC( pddc, (LONG) param2 );
       ExitDriver( pddc );
       if (usResult != SUCCESS)
       {
         RIP( "Enable: prde_RestoreDC failed" );
       }
       break;

  case RESET_DC:
       pddc = (PDDC) param1;
       EnterDriver( pddc );
       if ( pddc == (PDDC)  INVALID_ADDRESS )
       {
           RIP( "Enable: RESET_DC - invalid pddc address" );
           usResult = (SHORT) ERROR_NEG;
           break;
       }
       usResult = prde_ResetDC( pddc, (unsigned long) param2 );
       ExitDriver( pddc );
       if (usResult != SUCCESS)
       {
         RIP( "Enable: RESET_DC - prde_ResetDC failed" );
       }
       break;

  case COMPLETE_OPEN_DC:
       pddc = (PDDC) param2;
       EnterDriver( pddc );
       if ( pddc == (PDDC) INVALID_ADDRESS )
       {
         RIP( "Enable: COMPLETE_OPEN_DC - invalid pddc address" );
         usResult = (SHORT) ERROR_NEG;
         break;
       }
       usResult = SUCCESS;

       /*
       **            Activate spooler for STD data
       */
       if ( pddc->iType == OD_QUEUED &&
            pddc->pdv->usDataType == PM_Q_STD )
       {
         if ( SplStdOpen( (HDC)param1 ) == FALSE )
         {
           usResult = (USHORT)ERROR_NEG;
         }
       }

       /*
       ** patfix (begin)
       ** see utlps.c for full description of "patfix".
       */
       pddc->patfix_hdc = (HDC) param1;
       pddc->patfix_hrgn = 0;

       /*
       ** patfix (end)
       */
       ExitDriver( pddc );
       break;

  case BEGIN_CLOSE_DC:
       pddc = (PDDC) param2;
       EnterDriver( pddc );
       if ( pddc == (PDDC) INVALID_ADDRESS )
       {
         RIP( "Enable: BEGIN_CLOSE_DC - invalid pddc address" );
         usResult = (SHORT) ERROR_NEG;
         break;
       }

       hdc = (HDC) param1;

       /*
       ** Make sure an enddoc has been done
       */
       if ( pddc->pdv->fDocStarted == TRUE ||
            pddc->fDocWasAborted == TRUE)
       {
         LONG   lSize = sizeof( USHORT );
         USHORT usData;

         prdq_EndDoc( hdc, pddc, (PUSHORT) &usData, (PLONG) &lSize );
       }

       if ( pddc->iType == OD_DIRECT ||
            pddc->iType == OD_QUEUED )
       {
         usResult = CloseChannel( hdc, pddc );
       }
       else
       {
         usResult = SUCCESS;
       }

       if (pddc->iType == OD_MEMORY)
       {
         if ( pddc->pdv->fAllocatedPatterns )
         {
           pddc->pdv->fAllocatedPatterns = (BOOL) !
                          GplPatternDeleteBitmaps( pddc->pdv->pDCHeap,
                          (PBMAPINFO)pddc->pDeviceSurface->abmapinfoDefPattern,
                          DEFAULT_PATTERNS_NUMBER );

         }
       }

       ExitDriver( pddc );
       if (usResult != SUCCESS)
       {
         RIP( "Enable: BEGIN_CLOSE_DC - CloseChannel failed" );
       }
       break;

  case QUERY_DEVICE_SURFACE:
       /*
       ** If param1 is null engine is asking if driver supports new 2.2
       ** engine.  By returning zero we are answering we do.
       */

       usResult = SUCCESS;

       if ( param1 )
       {
         /*
         ** If param1 not null it is a pointer to device surface, param2
         ** is a pointer to pddc.  QueryDeviceSurface returns a 0 for OK
         */
         if ( QueryDeviceSurface( (PDDC)param2, (PDEVICESURFACE)param1 ))
         {
           usResult = FAILURE;
         }
       }

       break;
  default:
       RIP( "Enable: invalid enable function number" );
       usResult = (Word)ERROR_NEG;
       break;
  }

  /*
  ** Temporary Usage : Map OK (1) to ENG_OK (0).
  ** The Engine requires return 0 (OK) for Enable processing, but 1
  ** (OK) for everything else. So translate OK to ENG_OK and not OK
  ** to minus one.
  */
  if (usResult == SUCCESS)
  {
      return( (ULONG)ENG_OK );
  }

  RIP( "Enable: Sub function returned error code" );
  return( (ULONG) (ERROR_NEG) );
}

/***************************************************************************
 *
 * FUNCTION NAME = prde_EnableDC
 *
 * DESCRIPTION   = This routine implements subfunction 5 of the "enable"
 *                 entry point called by the engine.  This subfunction is
 *                 called to create a Device DC structure called the DDC.
 *                 This DDC will be linked to the device descriptor structure
 *                 (DV) already created.  Every (engine) DC will have its own
 *                 DDC and DV in the driver.  Also, a memory DC will be opened
 *                 in the display driver for use with this DC. The types
 *                 of DC are:
 *
 *                     OD_QUEUED   - spooled output
 *                     OD_DIRECT   - sends output directly to a file
 *                     OD_INFO     - same as direct without actual output
 *                     OD_METAFILE - !!!CR don't know what to do with this.
 *                     OD_MEMORY   - uses display driver to manipulate bitmaps
 *
 *                 If we are being called upon to open a memory DC, then only
 *                 a portion of the DDC structure will be created, since the
 *                 display driver will in fact be doing most of the work for
 *                 us.
 *
 * INPUT         = param1, param2
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = pddc , -1
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

ULONG prde_EnableDC( EDCParamsLst *param1, ULONG param2 )

{
    PDDC         pddc;       /*  ptr to DC instance data */
    SHORT        nbAlloc;    /* The number of bytes to allocate */
    STYLE_RATIO  Ratio;
    SHORT        i;
    PDV          pdv;

    /*
    ** Print some debugging information
    */
    #if DEBUG
      LogCall( "prde_EnableDC(%lp, %lp)", ((PB) &param1) + sizeof(param1) );
    #endif /* DEBUG */

    PrintLog( "DcType = %ld\n", param1->DcType );
    PrintLog( "param1->pdv = %lp\n", param1->pdv );

    /*
    ** allocate memory for the DDC structure.  if we have a memory
    ** DC, we do not have to worry about the DDC Bundles.
    */
    if (!(pddc = (PDDC) GplMemoryAlloc( param1->pdv->pDCHeap, sizeof(DDC) )))
    {
      RIP( "prde_EnableDC: DDC memory allocation failed" );
      goto OPEN_DC_FAILURE;
    }

    /*
    ** Fill in the values for the DDC header. The hdcMemory and pdv for
    ** the display driver DC will be determined later.
    */
    pddc->id         = DRIVER_ID;
    pddc->iType      = param1->DcType;
    pddc->hdc        = param1->hdc ;
    pddc->hdcMemory  = (HDC) NULL;
    pddc->hrgnMemory = (HRGN) NULL ;
    pddc->cLockCount = 0; /* Not yet locked. */
    pddc->pdv        = param1->pdv ;
    pdv              = pddc->pdv;

    /*           
    ** Don't open shadow dc on 2.2 engine
    */
    if ( ULGreVersion < RASTER_ENGINE_22 )
    {
      /*
      ** Ravisankar.
      ** 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.
      **
      */
      if (!(pddc->hdcMemory = GreOpenDC( (HDC) NULL, OD_MEMORY,"*", 0L,
                                         (PDEVOPENDATA) NULL)))
      {
        RIP( "Prde_EnableDC: GreOpenDc failed" );
        goto OPEN_DC_FAILURE ;
      }

      /*
      ** Create an empty region .
      */
      if (!(pddc->hrgnMemory = GreCreateRectRegion (pddc->hdcMemory,
                                                    (PRECTL) NULL, 0 )))
      {
        RIP( "GreCreateRectRegion failed" );
        goto OPEN_DC_FAILURE;
      }

      /*
      ** Set the Style Ratio 1:1.
      */
      Ratio.rx = 0x64;
      Ratio.ry = 0x64;
      PrintLog( "Setting style ratio on display driver memory DC\n" );
      if (!(GreSetStyleRatio( pddc->hdcMemory, (PRATIO) &Ratio )))
      {
        RIP( "SetStyleRatio returned zero (error)" );
        goto OPEN_DC_FAILURE ;
      }
    }

    if ( param1->DcType == OD_MEMORY )
    {
      pddc->pddcb = (PDDCB) NULL;
    }
    else
    {
      /*
      ** Unless this is a memory DC, initialize the fields in the DDC
      ** structure and reset the PostScript interface module.
      */
      if (!(pddc->pddcb = (PDDCB) GplMemoryAlloc( pdv->pDCHeap, sizeof(DDCB) )))
      {
         RIP( "prde_EnableDC: DDCB memory allocation failed" );
         goto OPEN_DC_FAILURE ;
      }

      pdv->fDocStarted = FALSE ;
      pdv->fInitStringSent = FALSE;  /*            */
      pdv->fQMStartDocIssued = FALSE ;
      pddc->fHeaderSent = FALSE;
      pddc->fEOJSent = FALSE;
      pddc->fENDDOC = FALSE;
      pdv->cn.fChannelOpen = FALSE;
      pdv->szDocName[0] = 0 ;
      pddc->pddcb->cgs.szFont[0] = 0;    /* this gets done once, whereas ps_init */
      ps_init( pddc );                    /* is done in several places. */

      pddc->pddcb->cgs.fFRedefDownld = FALSE;
      pddc->pddcb->cgs.cCodePagesRemapped = 0;
      prde_SetDDCDefaults (pddc);
    }


    return ( (ULONG)pddc );

OPEN_DC_FAILURE:

    PrintLog( "prde_EnableDC: Failed to perform enable subfunction 5 correctly\n" );

    /*
    ** Clean up the mess we may have made.
    */
    if (pddc)
    {
      if (pddc->hdcMemory)
      {
        GreDestroyRegion (pddc->hdcMemory, pddc->hrgnMemory);
        GreCloseDC (pddc->hdcMemory);
        pddc->hdcMemory = (HDC) NULL;
        pddc->hrgnMemory = (HRGN) NULL;

      }

      GplMemoryFree( (PB)pddc );    /* Free up memory */
      pddc = NULL;

      /*
      ** Engine should take care of deleting the DV structure
      */
    }
    return ( -1 );  /* Defect 53492 */
}

/***************************************************************************
 *
 * FUNCTION NAME = prde_SetDDCDefaults
 *
 * DESCRIPTION   = This routine initializes the values in the DC instance
 *                 data structure to their default values.
 *
 * INPUT         = (pddc)
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

void prde_SetDDCDefaults( PDDC   pddc )
  /* PDDC   pddc;                    ptr to the DC instance data */
{
  LONG i;
  PDDCB pddcb = pddc->pddcb;

  PrintLog( "prde_SetDDCDefaults\n" );

  /*
  ** pddc->ptsemPort = NULL;
  */
  /*
  ** Indicate that there are no saved DCs.
  */
  pddc->usSavedDCs = 0 ;
  pddc->SavedState = 0;       /* the level we save to in DEVESC_STARTDOC. */
  pddcb->pddcbNext = (PDDCB) 0L ;

  /*
  ** set the mxsave index to zero
  */
  pddcb->usMatrixIndex = 0;

  /*
  ** initialize abortdoc flag.
  */
  /*
  ** This flag is reset here, even though it is still 'used' when
  ** passing through here.
  */
#if 0
//pddc->fDocWasAborted = FALSE;
#endif

  /*
  ** initialize Integer Character Widths as FALSE;
  */
  pddc->fIntCharWidth = FALSE;

  /*
  ** Initialize Path Buffer Seg vars
  */
  pddcb->lpPathBuf = NULL;

  pddcb->lPathBufSize  = PATH_BUF_SIZE_EMPTY;
  pddcb->lPathBufCount = PATH_BUF_SIZE_EMPTY;

  /*            */
  /*
  ** Initialize Clip Path Buffer Seg vars
  */
  pddcb->lpClipPathBuf     = NULL ;
  pddcb->lpClipPathBufSave = NULL ;
  pddcb->lClipPathBufSize  = 0L ;
  pddcb->lClipPathBufCount = 0L ;
  pddcb->ulClipPathOptions = 0 ;
  pddcb->fClipChanged      = TRUE ;


  /*
  ** Initialize the path state.
  */
  pddcb->path.fPathIsOpen = FALSE;
  pddcb->path.fAreaIsOpen = FALSE;
  pddcb->path.fPathOrArea = FALSE;
  pddcb->path.fDidClipPath = FALSE;
  pddcb->path.fDidClipRect = FALSE;
  pddcb->path.fModifiedPath = FALSE;
  pddcb->path.uscClipRects = 1;

  /*
  ** Set the default transformation matrix to the identity matrix.
  */
  pddcb->xformCTM.fxM11 = FX_ONE;
  pddcb->xformCTM.fxM12 = 0L;
  pddcb->xformCTM.fxM21 = 0L;
  pddcb->xformCTM.fxM22 = FX_ONE;
  pddcb->xformCTM.lM41 = 0L;
  pddcb->xformCTM.lM42 = 0L;

  /*
  ** Initialize the bounds state.
  */
  pddcb->bounds.fInit = TRUE;
  pddcb->bounds.fAccumulate = FALSE;
  pddcb->bounds.rcl.xLeft = FX_MIN;    /* 28 SIGNED BITS */
  pddcb->bounds.rcl.xRight = FX_MAX;
  pddcb->bounds.rcl.yTop = FX_MAX;
  pddcb->bounds.rcl.yBottom = FX_MIN;

  /*
  ** Initialize the color table.
  */
  pddcb->color.nColors = 0;
  prdc_CreateLogColorTable( (HDC)NULL, LCOL_RESET, LCOLF_DEFAULT, 0L, 0L, (LONG *) FNULL,
                            pddc, NGreCreateLogColorTable );


  /*
  ** Initialize list of logical fonts currently in use; none @ start
  */
  for (i = 0; i <= MAX_FONT_LCID; i++)
  {
    pddcb->text.lfiEntry[i].bFlags = 0;
    ClearFontRemap( i );
    pddc->usCodePages[i] = 850;
  }

  pddcb->text.bLogFontEntry = 0 ;
  pddcb->text.pfdfFontDef = (PVOID)0L;
  pddcb->text.usCurFontDef = (SHORT)  -1;      /* invalid index */

  for (i = 0; i < CP_LIMIT; i++)
  {
    pddcb->cgs.usCPTable[i] = 0;
  }

  /*
  ** Initialize extra character spacing to none
  */
  pddcb->text.ChrBundle.usTextAlign = TA_NORMAL_HORIZ;
  pddcb->text.ChrBundle.fxExtra = FX_ZERO ;
  pddcb->text.ChrBundle.fxBreakExtra = FX_ZERO ;
  pddcb->text.fxErrorCurrentX = FX_ZERO ;
  pddcb->text.fxErrorCurrentY = FX_ZERO ;

  /*
  ** Current pen position and origin
  */
  pddcb->pen.ptlCur.x = 0L;
  pddcb->pen.ptlCur.y = 0L;
  pddcb->pen.ptlOrg.x = 0L;
  pddcb->pen.ptlOrg.y = 0L;

  /*
  ** Set the initial code page to 850, the code page that the
  ** default font supports.  Applications will be able to change
  ** the current code page by using SetCodePage in the case of the
  ** default font, or by using the usCodePage field in the FATTRS
  ** structure in the case of logical fonts.
  */
  pddcb->text.pusCurCodePgVector = (PSHORT) GreQueryCodePageVector(850L);
  pddcb->text.pusDefCodePgVector = pddcb->text.pusCurCodePgVector;
  pddcb->text.usDefCodePg = 850;

  /*
  ** The prda_SetAttributes functions are used to set the default
  ** attributes.
  */
  /*
  ** Do Line Attributes
  */
  (void) prda_SetLineAttrs( 0xFFFF, 0, FNULL, pddc );

  /*
  ** Do Text Attributes
  */
  (void) prda_SetTextAttrs( 0xFFFF, 0, FNULL, pddc );

  /*
  ** Do Marker Attributes
  */
  (void) prda_SetMarkAttrs( 0xFFFF, 0, FNULL, pddc );
  pddcb->mkr.usfMFont = FALSE;

  /*
  ** Do Image Attributes
  */
  (void) prda_SetImagAttrs( 0xFFFF, 0, FNULL, pddc );

  /*
  ** Do Pattern Attributes
  */
  (void) prda_SetPtrnAttrs( 0xFFFF, 0, FNULL, pddc );
  pddcb->pat.usfFontLoaded = FALSE;

  PrintLog( (PB) "Returning from SetDDCDefaults\n" );
}

#if 0
///***************************************************************************
// *
// * FUNCTION NAME = prde_CloseDisplay
// *
// *
// * DESCRIPTION   = This routine closes the display driver memory DC.
// *
// * INPUT         = (pddc)
// *
// * OUTPUT        = NONE
// *
// * RETURN-NORMAL = NONE
// *
// * RETURN-ERROR  = NONE
// *
// **************************************************************************/
//
//void prde_CloseDisplay( PDDC pddc )
//{
//  if (pddc->hddc)
//  {
//    PrintLog( "Closing display driver memory DC\n" );
//    PrintLog( "\tEnable subfunction 11\n" );
//    if ( (*pfnlPMDispEnable) ( (ULONG) 11L, (ULONG) pddc->hdc,
//                                             (ULONG) pddc->hddc ) )
//    {
//      RIP( "prde_CloseDisplay: Subfunction 11 returned nonzero (error)" );
//      return;
//    }
//    PrintLog( "\tEnable subfunction 6\n" );
//    if ( (*pfnlPMDispEnable) ( (ULONG) 6L, (ULONG) pddc->hddc,
//                                                      (ULONG) 0L ) )
//    {
//      RIP( "prde_CloseDisplay: Subfunction 6 returned nonzero (error)" );
//      return;
//    }
//    if (pddc->pdvDisplay)
//    {
//      PrintLog( "\tEnable subfunction 4\n" );
//      if ( (*pfnlPMDispEnable) ( (ULONG) 4L, (ULONG) pddc->pdvDisplay,
//                                                       (ULONG) 0L ) )
//      {
//          RIP( "prde_CloseDisplay: Subfunction 4 returned nonzero (error)" );
//          return;
//      }
//    }
//    PrintLog( "Display driver memory DC successfully closed\n" );
//  }
//}
#endif

/*****************************************************************************\
**
** FUNCTION NAME: FreePathBufs
**
** DESCRIPTION:   Frees both Path buffer and clip buffer
**
** INPUT:         pddcb , pddc
**
** OUTPUT:        NONE
**
\*****************************************************************************/

VOID FreePathBufs( PDDCB pddcb, PDDC pddc )
{
  /*
  ** Free the Path buffer
  */
  if ( pddcb->lpPathBuf )
  {
    GplMemoryFree ( (PVOID)pddcb->lpPathBufSave );
    pddcb->lpPathBuf = NULL;
    pddcb->lpPathBufSave = NULL;
  }

  /*
  ** Free the Clip Path Buffer if it is not done.
  */
  if ( pddcb->lpClipPathBuf )
  {
    GplMemoryFree ( (PVOID)pddcb->lpClipPathBufSave );
    pddcb->lpClipPathBuf = NULL;
    pddcb->lpClipPathBufSave = NULL;
  }

  return;
}


/***************************************************************************
 *
 * FUNCTION NAME = prde_DisableDC
 *
 * DESCRIPTION   = This routine disables the ddc and releases it's memory.
 *
 * INPUT         = pddc, param2
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = (SUCCESS)
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

SHORT prde_DisableDC( PDDC pddc )

  /* PDDC            pddc;          ptr to the DC instance data */
{
  SHORT      Result;

  #if DEBUG
    LogCall( "prde_DisableDC(%lp, %lp)", ((PB) &pddc) + sizeof(PDDC) );
  #endif /* DEBUG */

    /*
    ** Call the display driver to close the memory DC we created.
    ** If ours isn't a memory DC, then issue an end_doc command to
    ** the printer so that it can eject the page if necessary and
    ** delete any DDC structures that were created by saving the DC.
    */
#if 0
//  prde_CloseDisplay( pddc );
#endif

  if (pddc->iType != OD_MEMORY)
  {
    prde_DeleteSavedDCs( pddc );
  }

  /*
  ** Free the memory allocated to the DC.
  */
  if (pddc)
  {
    if (pddc->iType != OD_MEMORY)
    {
      FreePathBufs ( pddc->pddcb, pddc );

      GplMemoryFree ((PB) pddc->pddcb ) ;
      pddc->pddcb = NULL;
    }

    if (pddc->hdcMemory)
    {
      GreDestroyRegion (pddc->hdcMemory, pddc->hrgnMemory);
      GreCloseDC (pddc->hdcMemory);
      pddc->hdcMemory = (HDC) NULL;
      pddc->hrgnMemory = (HRGN) NULL;
    }

    /* @V3.1109317
    ** Delete Scratch DC if it exists
    */
    if ( pddc->pdv->hdcScratch )
    {
      GreCloseDC ( pddc->pdv->hdcScratch );
    }

#if 0
//    DosRequestMutexSem(tsemDriver,SEM_INDEFINITE_WAIT );
//    RemoveEntry( DDC_MAP,
//                 sizeof(HDDC),
//                 (PBYTE) &pddc->hddc,
//                 sizeof(DDCENTRY) );
//    DosReleaseMutexSem(tsemDriver );
#endif

    GplMemoryFree((PB) pddc ) ;
    pddc = NULL;
  }
  return (SUCCESS);
}

/***************************************************************************
 *
 * FUNCTION NAME = prde_DeleteSavedDCs
 *
 * DESCRIPTION   = This routine frees up all heap space used by extra DDC
 *                 structures that were created by "Save DC" requests.
 *
 * INPUT         = (pddc)
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

void prde_DeleteSavedDCs( PDDC pddc )
{
  PDDCB   pddcbSaved;

  if (pddcbSaved = (PDDCB) pddc->pddcb->pddcbNext)
  {
    PrintLog(  (PSZ) "Deleting unused saved DCs\n");
    do
    {
      pddc->pddcb = (PDDCB) pddcbSaved->pddcbNext;

      FreePathBufs ( pddcbSaved, pddc );
      GplMemoryFree ( (PB) pddcbSaved ) ;
      pddcbSaved = NULL;
      pddc->usSavedDCs--;
    }
    while (pddcbSaved = pddc->pddcb);
  }
}

/***************************************************************************
 *
 * FUNCTION NAME = prde_ResetDC
 *
 * DESCRIPTION   = This routine is Enable subfunction 9.  Resets the ddc state.
 *
 * INPUT         = pddc, unused
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = (SUCCESS)
 *
 * RETURN-ERROR  = (FAILURE)
 *
 **************************************************************************/

SHORT prde_ResetDC (PDDC pddc, ULONG unused )
  /* PDDC    pddc;                       ptr to the DC instance data */
{
  #if DEBUG
    LogCall( "prde_ResetDC(%lp, %ld)",  ((PB) &pddc) + sizeof(PDDC) );
  #endif /* DEBUG */

#if 0
//  if ( pddc->iType == OD_MEMORY )
//  {
//    /*
//    ** This is a memory DC, so we simply pass the request for a
//    ** DC reset on to the display driver.
//    */
//    if ( !(*pfnlPMDispEnable) ( (ULONG) 9L, (ULONG) pddc->hddc,
//                                                             unused ) )
//    {
//      PrintLog( (PSZ) "Display driver successfully reset DC\n");
//      return (SUCCESS);
//    }
//    else
//    {
//      PrintLog( (PSZ) "Display driver returned error in resetting DC\n");
//      return (FAILURE);
//    }
//  }
#endif

  if (pddc->iType == OD_MEMORY)
  {
    if ( ULGreVersion >= RASTER_ENGINE_22 )
    {
      return( SUCCESS );
    }
    /*
    ** This is a memory DC, so we simply pass the request for a
    ** DC reset on to the Gre.
    */
    if (GreResetDC((ULONG) pddc->hdcMemory,unused))
    {
      PrintLog( (PSZ) "Gre successfully reset DC\n" );
      return( SUCCESS );
    }
    else
    {
      PrintLog( (PSZ) "Gre returned error in resetting DC\n" );
      return( FAILURE );
    }
  }
  else
  {
    /*
    ** This is not a memory DC, so we must perform the reset
    ** operation ourselves.
    */
    PrintLog( (PSZ) "PostScript driver called upon to reset DC\n" );

    /*
    ** Delete any saved DCs.
    */
    prde_DeleteSavedDCs( pddc );

    /*
    ** Free the path buffer.
    */
    FreePathBufs ( pddc->pddcb, pddc );

    /*
    ** free the old font resource
    */
    if ( pddc->pddcb->text.pfdfFontDef )
    {
      FreeFontResource( pddc->pdv, pddc->pddcb->text.pfdfFontDef, 0 );
      pddc->pddcb->text.pfdfFontDef = NULL;
    }

    /*
    ** Reset our internal state, and then output a command to
    ** the PostScript printer to reset its state as well.
    */
    ps_init( pddc );
    prde_SetDDCDefaults( pddc );

    /*
    ** only restore to SavedState0 if we are not already there.
    */
    PrintChannel( pddc, (PSZ) "SavedState0 restore\n" );
    PrintChannel( pddc, (PSZ)"/SavedState0 save def\n" );

    /*
    ** clear the font redefine flag.
    */
    pddc->pddcb->cgs.fFRedefDownld = FALSE;
    return( SUCCESS );
  }
}

/***************************************************************************
 *
 * FUNCTION NAME = prde_RestoreDC
 *
 * DESCRIPTION   = This routine is RestoreDC Enable subfunction.  Restores the
 *                  ddc to a previously saved state.
 *
 * INPUT         = pddc, lCount
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = (SUCCESS)
 *
 * RETURN-ERROR  = (FAILURE)
 *
 **************************************************************************/

SHORT prde_RestoreDC( PDDC pddc, LONG lCount )
  /* PDDC    pddc;                      ptr to the DC instance data */
  /* LONG    lCount;                   Which saved DC we are to restore */
{
  PFONTDEF    pfdTmp;
  SHORT      i;
  SHORT    usHowManyPops;       /* How many POPs must we perform? */
  XFORM     xformSaved;
  PDDCB     pddcbSaved, pddcbTemp;
  LONG      hddcCurrent;         /* The hddc that the engine thinks */
                                 /* is associated with this DC */

  #if DEBUG
    LogCall( "prde_RestoreDC (%lp, %ld)", ((PB) &pddc) + sizeof(PDDC) );
  #endif /* DEBUG */

  /*
  ** this check is in here to be consistent with other printer drivers.
  ** in particular, printer drivers which do journaling.  doing a
  ** RestoreDC to level 1 trashes journaling, so all drivers will at
  ** least RIP to warn people not to do this.  this situation will
  ** result in a FAILURE in cruiser.
  */

  /*            */

  /*
  ** !!!CR make sure we handle lcount < or > 0 properly. -
  */

  if ((labs(lCount) < (LONG)pddc->SavedState) && (pddc->fENDDOC == FALSE) &&
      (pddc->pdv->fDocStarted))
  {
      RIP( "RestoreDC: Invalid RestoreDC within a document." );
      pddc->SavedState = 0L;
  }

#if 0
//  if ( pddc->iType == OD_MEMORY )
//  {
//    /*
//    ** This is a memory DC, so we simply pass the request for a
//    ** DC restore on to the display driver.  Then we patch
//    ** things up with the engine in case the display driver
//    ** told the engine that the hddc has changed.
//    */
//
//
//    if ( !(*pfnlPMDispEnable) ( (ULONG) 8L, (ULONG) pddc->hddc,
//                                                           lCount ) )
//    {
//        PrintLog( (PSZ) "Display driver successfully restored DC\n" );
//
//        /*
//        ** Get what the engine thinks is the current hddc.  If
//        ** this is a       value, then we assume the display
//        ** driver has changed the hddc.  In that case we hold
//        ** on to the display driver's new hddc, and tell the
//        ** engine to change the hddc back to what we had before.
//        */
//        hddcCurrent = (LONG) GetDriverInfo( pddc->hdc, 0L,
//                                            pddc->hdc );
//
//#if DEBUG
//          if ( hddcCurrent == -1L )
//              PrintLog ( (PSZ) "GetDriverInfo call failed\n" );
//          if ( pddc != (PDDC) hddcCurrent )
//          {
//              PrintLog ( (PSZ) "Engine thinks hddc has changed to %lp\n",
//                                                               hddcCurrent );
//              pddc->hddc = hddcCurrent;
//              if (SetDriverInfo ( (LONG) pddc, pddc->hdc, 0L, pddc->hdc) == -1L)
//                  PrintLog ( (PSZ) "SetDriverInfo call failed\n" );
//              else
//                  PrintLog ( (PSZ) "Successfully reset hddc back to %lp\n",pddc );
//          }
//          else
//              PrintLog ( (PSZ) "Display driver did not change hddc\n" );
//#else
//          if ( pddc != (PDDC)  hddcCurrent )
//              pddc = hddcCurrent;
//#endif /* DEBUG */
//          return( SUCCESS );
//      }
//      else
//      {
//        RIP( "RestoreDC: Display driver returned error in restoring DC" );
//        return( FAILURE );
//      }
//  }
#endif   /* old memory dc */

  if (pddc->iType == OD_MEMORY)
  {
    if ( ULGreVersion >= RASTER_ENGINE_22 )
    {
      return( SUCCESS );
    }
    /*
    ** This is a memory DC, so we simply pass the request for a
    ** DC restore on to the Gre.
    */
    if (GreRestoreDC (pddc->hdcMemory,lCount))
    {
      PrintLog( (PSZ) "Gre successfully restored DC\n" );
      return( SUCCESS );
    }
    else
    {
      RIP( "RestoreDC: Display driver returned error in restoring DC" );
      return( FAILURE );
    }
  }
  else
  {
    /*
    ** This is not a memory DC, so we must perform the restore
    ** operation ourselves.  This consists of determining how
    ** many POPs are necessary, discarding any saved DCs that
    ** will be lost, copying the saved DC into the current DC,
    ** discarding the saved DC we just copied, and outputting
    ** the proper command to PostScript to restore state.
    */
    PrintLog( (PSZ) "PostScript driver called upon to restore DC\n" );
    PrintLog( (PSZ) "\tusSavedDCs = %d,  lCount = %ld\n",
              pddc->usSavedDCs, lCount );
    PrintLog( (PSZ) "\tpddc->usSavedDCs = %d\n", pddc->usSavedDCs );
    PrintLog( (PSZ) "\tpddc->pddcb->pddcbNext = %lp\n", pddc->pddcb->pddcbNext );
    PrintLog( (PSZ) "\tpre restore lpPathBuf = %lp", pddc->pddcb->lpPathBuf );

    /*            */
    PrintLog( (PSZ) "\tpre restore lpClipPathBuf = %lp", pddc->pddcb->lpClipPathBuf );

    if ( lCount>0L )
    {
      if (((SHORT) lCount) > pddc->usSavedDCs)
      {
          RIP( "RestoreDC: There is invalid saved state" );
          GplErrSetError (  PMERR_INV_LENGTH_OR_COUNT );
          return( FAILURE );
      }
      usHowManyPops = pddc->usSavedDCs - (SHORT)  lCount;
    }
    else if ( lCount<0L )
    {
      if ( ( (SHORT)  (-lCount) )>pddc->usSavedDCs )
      {
        RIP( "RestoreDC: There aren't enough saves to pop!" );
        GplErrSetError (  PMERR_INV_LENGTH_OR_COUNT );
        return( FAILURE );
      }
      usHowManyPops = (SHORT) ( -(lCount + 1L) );
    }
    else
    {
      RIP( "RestoreDC: A count of zero is illegal" );
      GplErrSetError (  PMERR_INV_LENGTH_OR_COUNT );
      return( FAILURE );
    }

    pddcbSaved = (PDDCB) pddc->pddcb->pddcbNext;
    PrintLog( (PSZ) "\t%d saved DCs will be discarded in order to reach the desired one\n", usHowManyPops );

    while (usHowManyPops)
    {
      pddcbTemp = pddcbSaved;
      pddcbSaved = (PDDCB) pddcbSaved->pddcbNext;
      FreePathBufs ( pddcbTemp, pddc );
      GplMemoryFree ( (PB) pddcbTemp ) ;
      pddcbTemp = NULL;
      pddc->usSavedDCs--;
      usHowManyPops--;
    }

    /*
    ** if a font resource loaded, first free this resource.
    */
    if ( pddc->pddcb->text.pfdfFontDef )
    {
      FreeFontResource( pddc->pdv, pddc->pddcb->text.pfdfFontDef, 0 );
      pddc->pddcb->text.pfdfFontDef = NULL;  /*            */
    }

    /*
    ** save the current transform matrix before it is restored.
    ** see large comment below for details.
    */
    xformSaved =  pddc->pddcb->xformCTM;

    /*
    ** free the path buffer.
    */
    FreePathBufs ( pddc->pddcb, pddc );

    /*
    ** restore the pddcb level in the pddc.
    */
    utl_memcopy( (PSZ) pddc->pddcb, (PSZ) pddcbSaved, sizeof (DDCB) );

    PrintLog( (PSZ) "\tRestored DC as PostScript SavedState #%d\n",
              (pddc->usSavedDCs) );
    PrintLog( (PSZ) "\tCopied %d bytes from save area %lp to ddc %lp\n",
              (SHORT)  sizeof(DDCB), pddcbSaved, pddc->pddcb );
    PrintLog( (PSZ) "\tlpPathBuf restored to %lp\n", pddc->pddcb->lpPathBuf );

    /*            */
    PrintLog( (PSZ) "\tlpClipPathBuf restored to %lp\n", pddc->pddcb->lpClipPathBuf );

    pddc->usSavedDCs--;

    /*            */
    /*
    ** RestoreDC in the engine makes the following does the following:
    ** first, it calls the driver's RestoreDC, it then calls
    ** RestoreRegions, RestoreXforms, and RestorePath.  This has the
    ** following effects:  The RestoreDC call does a restore in the
    ** postscript printer.  This has the effect of blowing away the
    ** transform matrix that was in effect before the restore.  At this
    ** point the printer and graphics engine would be out of sync.
    ** Then when the engine calls RestoreRegions, which calls
    ** NotifyClipChange in the driver.  NotifyClipChange uses the
    ** current transform it gets from the engine to set up the clip
    ** rectangles.  At this point, then engine tells us the transform
    ** is different than it is in the printer.  The solution is to
    ** restore to the specified DC level, then redefine the current
    ** matrix as saved in the pddc.  This way the engine and the
    ** printer remain in sync.  When the engine call RestoreXforms,
    ** NotifyClipChange is NOT called in the driver so everything is
    ** still in sync.  This is where the recent change comes in.
    ** We hook out RestorePath already, so at that point, we update
    ** the transform matrix.  All of the sudden, the printer and
    ** engine are back in sync.
    */
    PrintChannel (pddc, (PSZ)"SavedState%d restore\n", pddc->usSavedDCs + 1 );

    /*
    ** redefine the current transform matrix in the printer.
    ** first, save the current matrix in the printer.
    */
    ps_savematrix( pddc );

    pddc->pddcb->cgs.xformCTM.fxM11 += 1;  /* force it to change. */
    ps_setmatrix(pddc, (FIXED  *)&xformSaved );

    GplMemoryFree ( (PB) pddcbSaved ) ;
    pddcbSaved = NULL;

    /*
    ** clear the font redefine flag and font resource validity flag.
    */
    pddc->pddcb->text.pfdfFontDef = (PVOID) 0L;
    pddc->pddcb->text.usCurFontDef = (SHORT)  -1;      /* invalid index */

    pddc->pddcb->cgs.fValidFont = FALSE;
    pddc->pddcb->cgs.fFRedefDownld = FALSE;

    return( SUCCESS );
  }
}

/***************************************************************************
 *
 * FUNCTION NAME = prde_SaveDC
 *
 * DESCRIPTION   = This routine is SaveDC Enable subfunction.  Saves the
 *                 ddc state.
 *
 * INPUT         = pddc, unused
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = (SUCCESS)
 *
 * RETURN-ERROR  = (FAILURE)
 *
 **************************************************************************/

SHORT prde_SaveDC( PDDC pddc, ULONG unused )
  /* PDDC          pddc;                 ptr to the DC instance data */
{
  PFONTDEF pfdTmp;
  SHORT    i;
  PDDCB    pddcbSaved;
  ULONG    ulBufSize;

  #if DEBUG
    LogCall( "prde_SaveDC(%lp, %ld)", ((PB) &pddc) + sizeof(PDDC) );
  #endif /* DEBUG */

#if 0
//  if ( pddc->iType == OD_MEMORY )
//  {
//    /*
//    ** This is a memory DC, so we simply pass the request for a
//    ** DC save  on to the display driver.
//    */
//
//
//    if ( !(*pfnlPMDispEnable) ( (ULONG) 7L, (ULONG) pddc->hddc,
//                                                            unused ) )
//    {
//        PrintLog( (PSZ) "Display driver successfully saved DC\n" );
//        return( SUCCESS );
//    }
//    else
//    {
//        RIP( "SaveDC: Display driver returned error in saving DC" );
//        return( FAILURE );
//    }
//  }
#endif

  if ( pddc->iType == OD_MEMORY )
  {
    if ( ULGreVersion >= RASTER_ENGINE_22 )
    {
      return( SUCCESS );
    }
    /*
    ** This is a memory DC, so we simply pass the request for a
    ** DC save  on to the GRE.
    */
    if (GreSaveDC(pddc->hdcMemory))
    {
      PrintLog( (PSZ) "Gre successfully saved DC\n" );
      return( SUCCESS );
    }
    else
    {
      RIP( "SaveDC: Gre returned error in saving DC" );
      return( FAILURE );
    }
  }
  else
  {
    /*
    ** This is not a memory DC, so we must perform the save
    ** operation ourselves.  This is done by creating an exact
    ** copy of the current DDC, adding a link from the current
    ** DDC to this copy, and then incrementing the count of
    ** saved DCs in the current (original) DDC.
    */
    PrintLog( (PSZ) "PostScript driver called upon to save DC\n" );

    if (!(pddcbSaved = (PDDCB) GplMemoryAlloc ( pddc->pdv->pDCHeap,
                                                sizeof(DDCB))))
    {
      RIP( "SaveDC: Not enough memory available to allocate new DDCB" );
      GplErrSetError (PMERR_INSUFFICIENT_MEMORY );
      return( FAILURE );
    }

    utl_memcopy ((PSZ)pddcbSaved, (PSZ)pddc->pddcb, sizeof(DDCB) );
    PrintLog( (PSZ) "\tCopied %d bytes from ddc %lp to save place %lp\n",
                       (SHORT)  sizeof(DDCB), pddc->pddcb, pddcbSaved );
    pddc->pddcb->pddcbNext = (struct _DDCB  *) pddcbSaved;

    /*           
    ** bumped the count first so the first save that comes from app or
    ** system will be SavedState1
    */
    pddc->usSavedDCs++;
    PrintChannel (pddc, (PSZ) "/SavedState%d save def\n", pddc->usSavedDCs );

    /*
    ** Save the path buffer.
    */
    if (pddc->pddcb->lpPathBuf )
    {
      ulBufSize = pddc->pddcb->lPathBufSize + sizeof( PBSAVE );
      pddc->pddcb->lpPathBuf = GplMemoryAlloc( pddc->pdv->pDCHeap, ulBufSize );

      pddc->pddcb->lpPathBufSave = pddc->pddcb->lpPathBuf;
      utl_memcopy(pddc->pddcb->lpPathBufSave,
                  pddcbSaved->lpPathBufSave,
                  ulBufSize - pddc->pddcb->lPathBufCount);
    }
    else
    {
      pddcbSaved->lpPathBuf     = NULL;
      pddcbSaved->lPathBufSize  = PATH_BUF_SIZE_EMPTY;
      pddcbSaved->lPathBufCount = PATH_BUF_SIZE_EMPTY;
    }

    /*            */
    /* Save the clip path buffer.*/
    if (pddc->pddcb->lpClipPathBuf != NULL)
    {

      pddc->pddcb->lpClipPathBuf = GplMemoryAlloc( pddc->pdv->pDCHeap,
                                               pddc->pddcb->lClipPathBufSize );

      pddc->pddcb->lpClipPathBufSave = pddc->pddcb->lpClipPathBuf;

      utl_memcopy(pddc->pddcb->lpClipPathBuf, pddcbSaved->lpClipPathBuf,
                  pddc->pddcb->lClipPathBufSize -
                  pddc->pddcb->lClipPathBufCount);

    }
    else
    {
      pddcbSaved->lClipPathBufSize    = 0L;
      pddcbSaved->lClipPathBufCount   = 0L;
      pddcbSaved->lpClipPathBuf       = NULL;
      pddcbSaved->lpClipPathBufSave   = NULL;
    }

    PrintLog( (PSZ) "\tSaved DC as PostScript SavedState #%d\n", pddc->usSavedDCs );
    PrintLog( (PSZ) "\tpddc->usSavedDCs = %d\n", pddc->usSavedDCs );
    PrintLog( (PSZ) "\tpddc->pddcb->pddcbNext = %lp\n", pddc->pddcb->pddcbNext );
    PrintLog( (PSZ) "\tpddc->pddcb->lpPathBuf = %lp\n", pddc->pddcb->lpPathBuf );

    /*            */
    PrintLog( (PSZ) "\tpddc->pddcb->lpClipPathBuf = %lp\n", pddc->pddcb->lpClipPathBuf );

    return( SUCCESS );
  }
}
