/*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 = DEVESC.C
 *
 * DESCRIPTIVE NAME = This file contains DEVESC functions and support routines.
 *
 *
 * VERSION = V2.0
 *
 * DATE
 *
 * DESCRIPTION Contains driver escape routines.
 *
 *
 * FUNCTIONS
 *
 * FUNCTION NAME = prdq_Escape  This routine dispatches off to the appropriate
 *                                escape function.
 *
 *                 prdq_QueryEscSupport This routine returns TRUE or FALSE
 *                                telling whether the given escape routine is
 *                                supported by the driver.
 *
 *                 prdq_StartDoc  This routine starts a document.
 *
 *                 prdq_EndDoc    This routine ends a document.
 *
 *                 prdq_AbortDoc  This routine aborts a document.
 *
 *                 prdq_NewFrame  Handles the DEVESC_NEWFRAME call;
 *
 *                 prdq_CharExtra Handles the DEVESC_CHAREXTRA call.  Sets extra
 *                                spacing for non-break characters.
 *
 *                 prdq_BreakExtra Handles the DEVESC_BREAKEXTRA call.  Sets
 *                                extra spacing for break characters.
 *
 *
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/

#include "inc\prdinclt.h"
#include "inc\prdmath.h"
#define  INCL_DEV
#define  INCL_SPLFSE
#include <pmdev.h>
#include "inc\prdgextf.h"
#include "inc\prdqextf.h"
#include "inc\prdtextf.h"
#include "inc\utl.h"
#include <pmspl.h>
#include "inc\pspagtun.h"              /* V2.174057   Page Tuning */
#define  INCL_GENPLIB_ERROR
#define  INCL_GENPLIB_MEMORY
#include <genplib.h>

extern VOID play_clip_rects(HDC,PDDC );

extern BOOL   RemapCodePage(PDDC,SHORT,SHORT,PSZ ); /* fonts.c */
extern VOID   ps_patfill_common(PDDC ); /* patfill.c */
extern VOID   ps_patfill_base(PDDC ); /* patfill.c */
extern VOID   SFDownload(PDDC,PSZ ); /* download.c */
extern BOOL   utl_MemIsEqual(PB,PB,int );
USHORT perform_std_spooling( HDC, PDDC );

extern PDDC EnterDriver(PDDC );
extern VOID ExitDriver(PDDC );
#define  DEVESC_INTCHARWIDTH 16997
#define  DEVESC_DEBUG  17000
#define  FX_100        6553600L
#define  CP_LIMIT      10
#define  LOCKED        0x80
#define  OD_MEMORY  8L

extern CHAR achEOF[];   /*            Defined in utlchnl.c */
#define EOFSIZE 3;      /* Do not include the trailing zero */
/***************************************************************************
 *
 * FUNCTION NAME = prdq_Escape
 *
 *
 * DESCRIPTION   = This routine dispatches off to the appropriate escape function.
 *
 * INPUT         = NONE
 *
 *
 * OUTPUT        = NONE
 *
 *
 * RETURN-NORMAL = NONE
 *
 *
 * RETURN-ERROR  = NONE
 *
 *
 *
 **************************************************************************/

ULONG prdq_Escape( HDC hdc, ULONG ulEscCode, ULONG InCount, ULONG *pInData,
                   ULONG *pOutCount, ULONG *pOutData, PDDC pddc, ULONG FunN)

  /* HDC hdc;                              The DC handle                     */
  /* PDDC pddc;                            Ptr to the DC instance data       */
{
  SHORT Result;                        /* Subfunction Result                */
  FIXED fxScale;                       /* FIXED scale factor. */
  ULONG ulRet;

  EnterDriver( pddc );

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

  if (pddc->iType == OD_MEMORY)
  {
    ulRet = InnerGreEscape (pddc->hdcMemory, ulEscCode, InCount,
                            pInData, pOutCount, pOutData, FunN );
    ExitDriver( pddc );
    return( ulRet );
  }

  /*
  ** Select function according to ArgEscape.
  */
  switch ((SHORT) ulEscCode)
  {
  case DEVESC_STARTDOC:
       Result = prdq_StartDoc( hdc, pddc, (PSZ) pInData, (LONG) InCount );
       break;

  case DEVESC_ENDDOC:
       Result = prdq_EndDoc( hdc, pddc, (PSHORT) pOutData, (PLONG) pOutCount );
       break;

  case DEVESC_NEWFRAME:
       Result = prdq_NewFrame( hdc, pddc );
       break;

  case DEVESC_ABORTDOC:
       Result = prdq_AbortDoc( hdc, pddc );
       break;

  #if 0
    case DEVESC_CHAR_EXTRA:
         Result = prdq_CharExtra(pddc, (PLONG)pInData, InCount );
         break;
    case DEVESC_BREAK_EXTRA:
         Result = prdq_BreakExtra( pddc, (PLONG)pInData, InCount );
         break;
  #endif

  case DEVESC_FLUSHOUTPUT:
       /*
       **           
       ** Flushing the channel is now handled by GENPLIB.
       */
#if 0
//     FlushChannel( pddc );
#endif
       if (pddc->pdv->hThread)
       {
         GplThreadFlushBuffer( pddc->pdv->hThread, TRUE );
       }
       Result = ps_status( pddc );
       break;

  case DEVESC_DEBUG:
#if 0
//     FlushChannel( pddc );
#endif
       if (pddc->pdv->hThread)
       {
         GplThreadFlushBuffer( pddc->pdv->hThread, TRUE );
       }
       PrintChannel( pddc, (PSZ)"\n%% %ls\n", pInData );
       PrintLog((PSZ)"\n%ls\n", pInData );
       break;

  case DEVESC_RAWDATA:
       /*
       ** DEVESC_RAWDATA is illegal if no DEVESC_STARTDOC has been done.
       */
       if (!pddc->pdv->fDocStarted)
       {
         RIP( "DEVESC_RAWDATA was called without DEVESC_STARTDOC." );
         ExitDriver( pddc );
         return( DEVESC_ERROR );
       }

       /*
       ** make sure the output channel is open.  once it is, just
       ** dump the raw data to it.
       */
       if (!pddc->pdv->cn.fChannelOpen)
       {
         OpenChannel( pddc );
       }

       /*
       ** This could be an app that is sending us postscript command
       ** via RawData, if no other output has been sent then we need
       ** to tell the spooler to start the document if it is queued.
       ** But only start the document once.
       */
       if (!pddc->pdv->fQMStartDocIssued)
       {
         /*
         ** If this is a queued DC, then tell the spooler to start the
         ** document.
         */
         if (pddc->iType == OD_QUEUED)
         {
           if (!SplQmStartDoc( (HSPL) pddc->pdv->cn.fh, (PSZ)pddc->pdv->szDocName ))
           {
             RIP( "DEVESC_RAWDATA SplQmStartDoc failed." );
             GplErrSetError( PMERR_STARTDOC_NOT_ISSUED );
             ExitDriver( pddc );
             return( DEVESC_ERROR );
           }
           pddc->pdv->fQMStartDocIssued = TRUE; /* We've issued a SplQmStartDoc */
         }
       }

       /*
       ** write channel will just write out what is in the buffer.
       ** it will NOT add a header to the output.  this is good.
       */
       /*
       ** Change type-cast for InCount from SHORT to USHORT to
       ** correct DRAG-DROP bug.
       */
       /*            */
       WriteChannel( pddc, (PB)pInData, (USHORT) InCount );
       Result = ps_status( pddc );
       break;

  case DEVESC_GETSCALINGFACTOR:
       /*
       ** check to see if we are being called with our own format.
       ** if not, pretend we aren't here.
       */
       if ((ULONG) *pOutCount != (ULONG)( sizeof(POINTL) + sizeof( POINTFX )))
       {
         ExitDriver( pddc );
         return( DEVESC_NOTIMPLEMENTED );
       }

       /*
       ** get the scaling factor in FIXED format.  ie 1.0 = 100%
       */
       fxScale = frdiv( LongToFx( (LONG)pddc->pdv->jobProperties.uScale), FX_100 );

       /*
       ** jump past the two LONGS which are worthless to us.
       */
       pOutData += 2;

       /*
       ** return the FIXED scaling factors in the PPOINTFX passed in.
       */
       *pOutData = fxScale;      /* the x and y scaling factors are */
       pOutData += 1;                   /* the same. */
       *pOutData = fxScale;
       Result = DEV_OK;
       break;

  case DEVESC_QUERYESCSUPPORT:
       /*
       ** Since there are three possible return codes for the
       ** escape function, we handle QueryEscSupport specially
       ** so that the error status for the rest of the
       ** subfunctions can be mapped convieniently.
       */

       ExitDriver( pddc );
       return( (ULONG)prdq_QueryEscSupport( (SHORT) *pInData) );

  case DEVESC_INTCHARWIDTH:
       /*
       ** this escape function simply sets a flag to tell whether
       ** integer character width is turned on or not.  the default
       ** if OFF.
       ** make sure we are being sent the correct information.
       */
       if (InCount != sizeof( BOOL ))
       {
         RIP( "DEVESC_INTCHARWIDTH: invalid parameter count." );
         ExitDriver( pddc );
         return( DEVESC_ERROR );
       }
       pddc->fIntCharWidth = (BOOL) *pInData;
       Result = DEV_OK;
       break;

  default:
    ExitDriver( pddc );
    return( (ULONG) DEVESC_NOTIMPLEMENTED );
  }

  /*
  ** On the return, success is mapped to 1 and failure is
  ** mapped to -1.
  */
  ExitDriver( pddc );
  return( (ULONG)(long) (Result ? 1 : -1) );
}

/***************************************************************************
 *
 * FUNCTION NAME = prdq_QueryEscSupport
 *
 *
 * DESCRIPTION   = This routine returns TRUE or FALSE telling whether the given
 *                 escape routine is supported by the driver.
 *
 *
 * INPUT         = (usEscCode)
 *
 *
 * OUTPUT        = NONE
 *
 *
 * RETURN-NORMAL = (1), (0)
 *
 *
 * RETURN-ERROR  = NONE
 *
 *
 *
 **************************************************************************/

SHORT prdq_QueryEscSupport( SHORT usEscCode )
{
  #if      DEBUG
    LogCall( "prdq_QueryEscSupport(%d)", ((PB)&usEscCode)+sizeof(usEscCode) );
  #endif                                 /* DEBUG */

  /*
  ** The desired escape function is indicated by ArgInData.
  ** Returns:     -1     Error
  **               0     Not Implemented
  **               1     OK
  */
  switch (usEscCode)
  {
  case DEVESC_QUERYESCSUPPORT:
  case DEVESC_STARTDOC:
  case DEVESC_ENDDOC:
  case DEVESC_NEWFRAME:
  case DEVESC_ABORTDOC:
  case DEVESC_FLUSHOUTPUT:
  case DEVESC_RAWDATA:

  #if 0
    case DEVESC_CHAR_EXTRA:
    case DEVESC_BREAK_EXTRA:
  #endif

  case DEVESC_INTCHARWIDTH:
    return( 1 );

  default:
    return( 0 );
  }
}


/***************************************************************************
 *
 * FUNCTION NAME =  prdq_StartDoc
 *
 * DESCRIPTION   =  This routine starts a document.  It is only supported if we have
 *                  an OD_QUEUED dc.  In otherwords, this routine is used to establish
 *                  the start of a new spooler print job.
 *
 * INPUT         = (hdc,pddc,pszDocName,nbName)
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = (SUCCESS) , ps_status( pddc)
 *
 * RETURN-ERROR  = (DEVESC_ERROR)
 *
 **************************************************************************/

SHORT  prdq_StartDoc( HDC hdc, PDDC pddc, PSZ pszDocName, LONG nbName )
  /*  PDDC pddc;                            Ptr to the DC instance data       */
  /*  PSZ pszDocName;                       Ptr to the document name          */
  /*  LONG nbName;                          The length of the document name   */
{
  static char szNoName[] = "";
  PSZ         psz;
  SHORT       i;
  LONG        lDCid;
  PCN         pcn;
  ULONG       nbWritten;               /* The number of bytes actually
                                          written                           */
  PSZ         pszLogAddress;
  ULONG       DosAction;
  SHORT       sParams;         /* The number of parameters in the DEVOPENSTRUC */
  PSZ         *ppsz;

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

  /*
  ** if the abort flag is set, it means that this startdoc call is
  ** coming immediately after an abortdoc call, with no output
  ** in between.  if there was no output in between the previous
  ** startdoc and the abortdoc, ie the channel was never opened,
  ** just set some flags.  If the channel had been opened, and we have
  ** a direct DC, output the end of job marker to kill the aborted job.
  */
  pddc->fDocWasAborted = FALSE;
#if 0
//if (pddc->fDocWasAborted)
//{
//  pddc->fDocWasAborted = FALSE;
//  pcn = &pddc->pdv->cn;
//
//  /*
//  ** if the channel has not been opened, then we do not have a
//  ** file hanging around to discard.
//  */
//  if (pcn->fChannelOpen)
//  {
//    /*
//    ** if we are writing direct, abort the job, close the output
//    ** channel to throw away what was there, and reopen it.
//    ** NOTE: the OD_DIRECT case will not have the abort flag set.
//    ** This case is handled in prdq_AbortDoc.
//    */
//
//    if (pddc->iType == OD_DIRECT)
//    {
//      /*
//      ** force EOJ out to printer
//      */
//      pcn->nbBuf = EOFSIZE;
//      PrtWrite( (HFILE) pcn->fh, achEOF, (ULONG)pcn->nbBuf, &nbWritten );
//      pcn->nbBuf = 0;
//
//      /*
//      ** The abort flushes buffers too fast leaving printer in a     mood
//      ** This should be fixed better
//      **           PrtAbort((SHORT)pcn->fh );             
//      */
//      PrtClose( pcn->fh );
//      pszLogAddress = pddc->pdv->dop.pszLogAddress;
//      PrtOpen( (PSZ)pszLogAddress, (PHFILE) &pcn->fh, &DosAction,
//               0L, FILE_NORMAL, (OPEN_ACTION_REPLACE_IF_EXISTS |
//               OPEN_ACTION_CREATE_IF_NEW), (OPEN_ACCESS_WRITEONLY |
//               OPEN_SHARE_DENYREADWRITE), 0L );
//    }                                /* end of (pddc->iType == OD_DIRECT) */
//  }                                  /* end of (pcn->fChannelOpen)        */
//
//  /*
//  ** set this flag to FALSE to this routine will let this case
//  ** fall through.
//  */
//  pddc->pdv->fDocStarted = FALSE;
//
//  /*
//  ** reset a bunch of stuff to start the job out right.
//  */
//  ps_init( pddc );
//}
#endif

  /*
  ** If the document has already been started then ignore any
  ** extra startdocs that occur.
  */
  if (pddc->pdv->fDocStarted)
  {
    return( SUCCESS );
  }

  /*           
  ** moved this to before ps_startdoc so it has a name
  --
  ** check for a document name.  if we are not given one, point
  ** to a null name, instead of random garbage.
  */
  if (!pszDocName)
  {
    pszDocName = (PSZ) szNoName;
  }
  szCopy((PSZ)pddc->pdv->szDocName, pszDocName, sizeof(pddc->pdv->szDocName) );

  /*
  ** Convert all non-ascii characters in the document name to spaces
  ** so that PostScript won't get upset and abort the print job.
  */
  pszDocName = (PSZ) pddc->pdv->szDocName;

  while (*pszDocName)
  {
    if ((*pszDocName < ' ') || (*pszDocName > 0x07f))
    {
      *pszDocName = ' ';
    }
    ++pszDocName;
  }

  /*
  **            Issue the spool start to record GPI/DEV calls
  */
  if ( pddc->pdv->usDataType == PM_Q_STD  &&
       pddc->iType == OD_QUEUED )
  {
    SplStdStart( hdc );
    pddc->pdv->fDocStarted = TRUE ;
    ps_startdoc( pddc );

  }

  /*
  **            This messes up the STD stuff
  */
#if 0
**/*
**** call GreSaveDC.  This will insure that we can restore the DC state
**** at the end of a document to exactly what it was at the beginning
**** of the document.  This is not really necessary for this driver, but
**** it is here for consistent behavior with drivers which do journaling.
***/
**
**lDCid = GreSaveDC(hdc );
**
**if (lDCid == GPI_ERROR)
**{
**  RIP( "StartDoc:  GreSaveDC returned GPI_ERROR." );
**  return( DEVESC_ERROR );
**}
**
**
**/*
**** save the DC id in the pddc so we can restore to it when we do an
**** enddoc.
***/
**
**pddc->SavedState = (SHORT)lDCid;
**pddc->pddcb->path.fDidClipRect = FALSE;
#endif

  /*
  ** mark that the header has not yet been sent to the printer.  it
  ** will be sent at the time the first output is ready to go out.
  */
  pddc->pdv->fDocStarted = TRUE;
  pddc->fHeaderSent = FALSE;
  pddc->fEOJSent = FALSE;

  /*
  ** we are starting a new document.  as far as the printer is concerned,
  ** it is starting over fresh, and has no codepages or remapped fonts
  ** defined.  we must, therefore, reset our driver internal tables for
  ** downloaded codepages and remapped fonts.
  */
  for (i = 0; i <= MAX_FONT_LCID; i++)
  {
    ClearFontRemap( i );
  }

  for (i = 0; i < CP_LIMIT; i++)
  {
    pddc->pddcb->cgs.usCPTable[i] = 0;
  }
  pddc->pddcb->cgs.cCodePagesRemapped = 0;
  pddc->pddcb->cgs.usNewCPs = 0;       /*                              */
  return( ps_status( pddc ) );
}

/***************************************************************************
 *
 * FUNCTION NAME = prdq_EndDoc
 *
 * DESCRIPTION   = This routine ends a document.  It is only supported if we
 *                 have  an OD_QUEUED dc.  In otherwords, this routine is used
 *                 to establish  the end of a spooler print job.
 *
 * INPUT         = (hdc,pddc,pusJobID,pnbBuf)
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = (ps_status( pddc))
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

SHORT prdq_EndDoc( HDC hdc, PDDC pddc, PSHORT pusJobID, PLONG pnbBuf )

  /* HDC hdc;                              handle to device context           */
  /* PDDC pddc;                            Ptr to the DC instance data        */
  /* PSHORT pusJobID;                     Ptr to the place to return the     */
  /*                                       spool job id                       */
  /*                                                                          */
  /*                                       defined to be a short in DevEscape!*/
  /*                                                                          */
  /* PLONG pnbBuf;                         The size of the buffer for         */
  /*                                       returning the job id               */
{
  SHORT usJobID;

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

  /*
  ** For an abort, simply reset the flag and exit.  Nothing else is needed.
  */
  if (pddc->fDocWasAborted == TRUE && pddc->pdv->hThread != (HTHREAD) 0)
  {
    pddc->fDocWasAborted = FALSE;
    return( SUCCESS );
  }

  /*
  ** if no document has been started and we are not outputting
  ** an encapsulated postscript file, then we don't want to do
  ** anything.  just return success.
  */
  if ((pddc->pdv->fDocStarted == FALSE) && (pddc->pdv->iDestnType != ENCAPS))
  {
    return( SUCCESS );
  }

  /*
  **            if std job must get metafile from buffer to spool file
  */
  if ( pddc->pdv->usDataType == PM_Q_STD )
  {
/** usJobID = perform_std_spooling( hdc, pddc ); **/
    perform_std_spooling( hdc, pddc );
  }
#if 0
* else             /* in both cases get the job id from ps_enddoc */
* {
*   /*
*   ** end the document.
*   */
*   usJobID = ps_enddoc( hdc, pddc );
* }
#endif
  usJobID = ps_enddoc( hdc, pddc );

  pddc->pdv->fDocStarted = FALSE;

  /*           
  ** Moved the check to bottom to finish the enddoc before checking as
  ** not to hang up job...
  --
  ** Check parms, Initialize the spool file handle to null
  */
  if (pusJobID)
  {
    if ( pnbBuf == NULL ||
         *pnbBuf < sizeof(SHORT))
    {
      GplErrSetError( PMERR_INV_ESCAPE_DATA );
      return( FAILURE );
    }
  }

  /*
  ** tell the spooler the document has ended if we are queued.
  */
  if (pddc->iType == OD_QUEUED)
  {
    if (pusJobID)
    {
      *pusJobID = usJobID;
      *pnbBuf = (ULONG) sizeof( usJobID );
    }
  }
  return( ps_status( pddc ) );
}

/***************************************************************************
 *
 * FUNCTION NAME = prdq_AbortDoc
 *
 *
 * DESCRIPTION   = This routine aborts a document.  This allows the user to end
 *                 a print job in a "nice" way.
 *
 *
 * INPUT         = (hdc,pddc)
 *
 *
 * OUTPUT        = NONE
 *
 *
 *
 *
 * RETURN-NORMAL = (SplQmAbortDoc( (HSPL)pddc->pdv->cn.fh))
 *
 *
 * RETURN-ERROR  = NONE
 *
 *
 *
 **************************************************************************/

SHORT prdq_AbortDoc( HDC hdc,  PDDC pddc )
  /* HDC hdc;                              handle to device context          */
{
  /* PCN         pcn;                       */
  /* The number of bytes actually written */
  /* int         nbWritten;                */

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

  /*
  ** set a flag to say we no longer have a document started.
  */
  pddc->pdv->fDocStarted = FALSE;
  pddc->fHeaderSent = FALSE;

  /*
  ** Abort can be called more than once.
  */
  if (pddc->fDocWasAborted == FALSE)
  {
    if (pddc->iType == OD_QUEUED)
    {
      if (pddc->pdv->usDataType == PM_Q_RAW)
      {
        GplThreadAbortDoc( pddc->pdv->hThread );
        pddc->fDocWasAborted = TRUE;
      }

      ps_init( pddc );
      SplQmAbortDoc( (HSPL) pddc->pdv->cn.fh );
      SplStdDelete( SplStdStop( hdc ) );
    }
    else if (pddc->iType == OD_DIRECT)
    {
      if (pddc->pdv->usDataType == PM_Q_RAW)
      {
        GplThreadAbortDoc( pddc->pdv->hThread );
      }
      pddc->fDocWasAborted = TRUE;
    }
  }

  return( SUCCESS );
}

/***************************************************************************
 *
 * FUNCTION NAME = prdq_NewFrame
 *
 * DESCRIPTION   = Handles the DEVESC_NEWFRAME call; basically a form feed.
 *
 * INPUT         = (hdc,pddc)
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = (ps_status( pddc))
 *
 * RETURN-ERROR  = (FAILURE)
 *
 **************************************************************************/

SHORT prdq_NewFrame( HDC hdc,  PDDC pddc )
  /* HDC hdc;                              handle to device context          */
  /* PDDC pddc;                            Ptr to the DC instance data       */
{
  static char    szDocDefault[] = "NF";
  SHORT          Result;
  SHORT          CPNum, CurCPs;
  register SHORT x;
  SHORT          NewCPTable[10];               /*             */
  PFNT           pFnt;                         /*             */
  XFORM          xTemp;

  #if      DEBUG
    LogCall( "prdq_NewFrame(%lp)\n", ((PB)&pddc)+sizeof(pddc) );
  #endif                                 /* DEBUG */

  if (!pddc->pdv->fDocStarted)
  {
    RIP( "DEVESC_NEWFRAME called before DEVESC_STARTDOC.  NEWFRAME returning F\
AILURE" );
    return( FAILURE );
  }
  else
  {
    /*
    ** Flush out the old data.
    */
    if (pddc->pdv->hThread)
    {
      GplThreadFlushBuffer( pddc->pdv->hThread, TRUE );
    }

    if (pddc->iType == OD_DIRECT && pddc->pdv->iDestnType == ENCAPS)
    {
      /*
      ** EPS files consist of one page only so if its
      ** an EPS file, terminate the document and close
      ** the output channel.
      */
      ps_enddoc( hdc, pddc );
      CloseChannel( hdc, pddc );
    }
  }

  /*            */
  /*
  ** To keep a good VM clean up we'll be doing a save and restore per page.
  ** The only way to reclaim VM space on level 1 is with the use of save and
  ** restore.
  */
  /*              */
#if 0
* if (pddc->usSavedDCs <= 1)         /* Don't restore in middle of saved DC */
* {
*   PrintChannel( pddc, (PSZ)"PageState restore\n" );
* }
#endif
  PrintChannel (pddc, (PSZ) "SavedState%d restore\n", pddc->usSavedDCs );

  /*
  ** Generate a PostScript page eject command
  */
  ps_showpage( pddc );

  /*
  ** A new page might be started now. Recompute input tray no ,paper
  ** name  ,output tray no and reissue the needed commands .
  */

  /*
  ** Issuing commands to select paper,input and output trays.
  */
  pddc->pdv->shPageno += 1;            /*            */

  PrintChannel( pddc, (PSZ)"%%%%Page: %d %d\n", pddc->pdv->shPageno,
                pddc->pdv->shPageno );

  /*
  ** Def 51530 - Check if the PageState CTM is different then the current
  ** matrix.  If it is reissue the CTM since the restore has reset the CTM to
  ** what is was at last pagesave - any changes made to middle of page are
  ** gone.
  */
  if (!utl_MemIsEqual((PB)&pddc->pddcb->xformCTM,
      (PB) &pddc->pdv->xformPageState, sizeof(pddc->pddcb->xformCTM)))
  {
    pddc->pdv->xformPageState = pddc->pddcb->xformCTM;
    pddc->pddcb->cgs.xformCTM.fxM11 = FX_ZERO; /* Mess up CTM */
                                                    /* Reissue the latest CTM */
    ps_setmatrix( pddc, (FIXED  *) &pddc->pdv->xformPageState );
  }

  /*            */
  /*
  ** Look for any downloaded fonts.  If any found re-download them so they
  ** are part of the pagestate
  */
  for (x = 0; x < pddc->pdv->cFonts; x++)
  {
    pFnt = (PFNT) &(*pddc->pdv->paFonts)[x];

    if ((SHORT)pFnt->usResource <= 0 &&    /* If soft font and */
       pFnt->bLoaded == TRUE)              /* downloaded but not locked */
    {
      SFDownload( pddc, (PSZ)pFnt->pszPFB ); /* Redownload the font */
      pFnt->bLoaded = TRUE | LOCKED;         /* Mark it as locked */
    }
  }

  /*            */
  /*
  ** Any remapping done in the middle of a page has being removed by the
  ** restore at the end of the page.  Now we must place this remap at the
  ** bottom of our setup to keep VM as clean as possible. Before we do our
  ** save at the begining of the next page. This will store all new character
  ** set were we can access them later without remaping.
  */
  if (pddc->pddcb->cgs.usNewCPs != 0)
  {
    CurCPs = pddc->pddcb->cgs.cCodePagesRemapped;
    CPNum = CurCPs;

    for (x = 0; x <= pddc->pddcb->cgs.usNewCPs; ++x)
    {
      NewCPTable[CurCPs-CPNum] = pddc->pddcb->cgs.usCPTable[CPNum];
      pddc->pddcb->cgs.usCPTable[CPNum] = 0;
      --CPNum;
    }
    pddc->pddcb->cgs.cCodePagesRemapped = (pddc->pddcb->cgs.cCodePagesRemapped
       -pddc->pddcb->cgs.usNewCPs );

    for (x = 0; x <= pddc->pddcb->cgs.usNewCPs; ++x)
    {
      RemapCodePage(pddc, NewCPTable[x], pddc->pddcb->text.bLogFontEntry,
         pddc->pddcb->text.szFont );
      NewCPTable[x] = 0;
    }

    /*        PrintChannel( pddc, (PSZ) "CP 0 = %D \n",pddc->pddcb->cgs.usCPTable[0] ); */
    /*        PrintChannel( pddc, (PSZ) "CP 1 = %D \n",pddc->pddcb->cgs.usCPTable[1] ); */
    /*        PrintChannel( pddc, (PSZ) "CP 2 = %D \n",pddc->pddcb->cgs.usCPTable[2] ); */

    pddc->pddcb->cgs.usNewCPs = 0;
  }

  /*
  ** Reset any patterns that may've being deleted with restore
  */
  if ((pddc->pddcb->pat.usfFontLoaded&BASEPATLOADED) &&
     !(pddc->pddcb->pat.usfFontLoaded&BASEPATSAVED))
  {
    pddc->pddcb->pat.usfFontLoaded &= ~BASEPATLOADED;
    ps_patfill_base(pddc );
    pddc->pddcb->pat.usfFontLoaded |= BASEPATSAVED;
  }

  if ((pddc->pddcb->pat.usfFontLoaded & FILLFONTLOADED) &&
     !(pddc->pddcb->pat.usfFontLoaded & FILLFONTSAVED))
  {
    pddc->pddcb->pat.usfFontLoaded &= ~FILLFONTLOADED;
    ps_patfill_common( pddc );
    pddc->pddcb->pat.usfFontLoaded |= FILLFONTSAVED;
  }

  /*            */
  if ((pddc->pddcb->pat.usfFontLoaded & LOGFONTLOADED) &&
     !(pddc->pddcb->pat.usfFontLoaded & LOGFONTSAVED))
  {
    pddc->pddcb->pat.usfFontLoaded &= ~LOGFONTLOADED;
    ps_patfill_common( pddc );
    pddc->pddcb->pat.usfFontLoaded |= LOGFONTSAVED;
  }

  /*
  ** for every page we will save at the begining and restore at the end
  ** to do our own garbage collection and keep VM as clean as possible.
  */
#if 0
* if (pddc->usSavedDCs <= 1)           /* Don't save in middle of saved DC */
* {
*   PrintChannel( pddc, (PSZ)"/PageState save def \n" );
* }
#endif
  PrintChannel (pddc, (PSZ) "/SavedState%d save def\n", pddc->usSavedDCs );

  /*
  ** restore the clipping rectangle, since it was blown away in the
  ** printer.  The showpage does an initgraphics, which returns
  ** the clipping to the full page.
  */
  play_clip_rects( hdc, pddc );

  /*
  ** Flush the channel so that all the commands get written out
  ** and then mark the channel as "unused" so that the driver
  ** can avoid emitting an extra page eject.
  */
  FlushChannel( pddc );
  pddc->pdv->cn.fUsed = FALSE;
  return( ps_status( pddc ) );
}

#if 0
/***************************************************************************
 *
 * FUNCTION NAME = prdq_CharExtra
 *
 * DESCRIPTION   = Handles the DEVESC_CHAREXTRA call.  Sets extra spacing for
 *                 non-break characters.
 *
 * INPUT         = (pddc,plParameters,ulByteCount)
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = (SUCCESS)
 *
 * RETURN-ERROR  = (FAILURE)
 *
 **************************************************************************/

SHORT prdq_CharExtra( PDDC pddc, PLONG plParameters, ULONG ulByteCount )
  /* PDDC pddc;                            Pointer to display context        */
  /*                                       instance data                     */
  /* PLONG plParameters;                   Extra spacing desired             */
  /* ULONG ulByteCount;                    The size of the parameter buffer  */
  /*                                       in bytes                          */
{
  /*
  ** If the caller gave us no input data, then we set the extra spacing
  ** for non-break characters to zero.
  */
  if (!ulByteCount)
  {
    pddc->pddcb->text.fxCharExtra = FX_ZERO;
    return( SUCCESS );
  }
  else
  {
    /*
    ** Otherwise the caller must give us one FIXED value. This is the
    ** amount of extra spacing desired, in world coordinates, to be added
    ** to each non-break character.
    */
    if (ulByteCount == sizeof(FIXED))
    {
      pddc->pddcb->text.fxCharExtra = *plParameters;
      return( SUCCESS );
    }
    else
    /*
    ** Otherwise they gave us a     byte count.
    */
    {
      GplErrSetError(  PMERR_INV_ESCAPE_DATA );
      return( FAILURE );
    }
  }
}

/***************************************************************************
 *
 * FUNCTION NAME = prdq_BreakExtra
 * DESCRIPTION   = Handles the DEVESC_BREAKEXTRA call.  Sets extra spacing for
 *                 break characters.
 * INPUT         = (pddc,plParameters,ulByteCount)
 * OUTPUT        = NONE
 * RETURN-NORMAL = (SUCCESS)
 * RETURN-ERROR  = (FAILURE)
 **************************************************************************/

SHORT prdq_BreakExtra( PDDC pddc, PLONG plParameters, ULONG ulByteCount )
  /* PDDC pddc;                            Pointer to display context        */
  /*                                       instance data                     */
  /* PLONG plParameters;                   Extra spacing desired             */
  /* ULONG ulByteCount;                    The size of the parameter buffer  */
  /*                                       in bytes                          */
{
  /*
  ** If the caller gave us no input data, then we set the extra spacing
  ** for break characters to zero.
  */
  if (!ulByteCount)
  {
    pddc->pddcb->text.fxBreakExtra = FX_ZERO;
    return( SUCCESS );
  }
  else
  {
    /*
    ** Otherwise the caller must give us one FIXED value. This is the
    ** amount of extra spacing desired, in world coordinates, to be added
    ** to each break character.
    */
    if (ulByteCount == sizeof(FIXED))
    {
      pddc->pddcb->text.fxBreakExtra = *plParameters;
      return( SUCCESS );
    }
    else
    /*
    ** Otherwise they gave us a     byte count.
    */
    {
      GplErrSetError(  PMERR_INV_ESCAPE_DATA );
      return( FAILURE );
    }
  }
}
#endif

/*
**            This is new for this feature
*/
/***************************************************************************
 *
 * FUNCTION NAME = perform_std_spooling
 *
 * DESCRIPTION   = writedata to the spooler.
 *                 End of spooling activities -
 *                 write the metafile data to the spooler.
 *
 * INPUT         = hdc pddc
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = The job ID,  as returned from the spooler
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

USHORT perform_std_spooling( HDC hdc, PDDC pddc )
{
  USHORT JobID = 0;
  USHORT usBufSize;
  HSTD   hJob;
  HSPL   hSpoolFile;
  LONG   lBytesLeft = 0;
  LONG   lBytesToRead = 0;
  LONG   lBytesRead = 0;
  SEL    sSel;
  PVOID  pBuf;
  BYTE   StackBuf[ 64 ];  /* A little buf if out of mem */

  hJob = SplStdStop( hdc );  /* Stop recording get handle */

  if ( lBytesLeft = SplStdQueryLength( hJob ) )
  {
    /*
    ** Set up buf length MAX 64K -1
    */
    if ( lBytesLeft > 0xFFFF )
    {
      usBufSize = 0xFFFF;
    }
    else
    {
      usBufSize = (USHORT) lBytesLeft;
    }

    /*
    ** Get buffer (use stack if alloc fails)
    */
    if ( !(pBuf = GplMemoryAlloc ( pddc->pdv->pDCHeap, usBufSize )))
    { /* Failure */
      pBuf = (PVOID) StackBuf;
      usBufSize = sizeof( StackBuf );
    }

    hSpoolFile = (HSPL)pddc->pdv->cn.fh;

    /*
    ** Loop geting the metafile bits from buffer to spool file
    */
    while ( lBytesLeft )
    {
      lBytesToRead = lBytesLeft;

      if ( lBytesToRead > usBufSize )
      {
        lBytesToRead = usBufSize;
      }

      SplStdGetBits( hJob, lBytesRead, lBytesToRead, (PCH)pBuf );

      /*
      ** Adjust the counts and offsets
      */
      lBytesLeft -= lBytesToRead;
      lBytesRead += lBytesToRead;

      /*
      ** Write it to the spooler - if there is an error then close the file
      */
      if ( SplQmWrite( hSpoolFile, lBytesToRead, (PCH)pBuf ) == FALSE )
      {
        SplQmClose( hSpoolFile );
        break;                          /* Break the loop */
      }
    }
  }

/*JobID = SplQmEndDoc( hSpoolFile ); */

  SplStdDelete( hJob );                 /* Delete our record of it           */

  if ( pBuf != (PVOID)StackBuf )
  {
    GplMemoryFree( pBuf );
  }

  return( JobID );
}
