/*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 = UTLPS.C
 *
 * DESCRIPTIVE NAME =
 *
 *
 * VERSION = V2.0
 *
 * DATE
 *
 * DESCRIPTION : This module is the low-level interface to the PostScript
 *               machine. The functions in this module mirror the
 *               functionality present in PostScript.  As calls are made to
 *               this module, the state of the PostScript machine is
 *               maintained so that redundant output can be eliminated.
 *
 * FUNCTIONS - szCopy
 *           - MatrixToChannel
 *           - init_cgs
 *           - ps_init
 *           - GetMappedFontName
 *           - DoSelectFont
 *           - ps_selectfont
 *           - ps_enddoc
 *           - InvertTransfer
 *           - ps_effects
 *           - EatSeper
 *           - FindString
 *           - ScanInt
 *           - ps_startdoc
 *           - ps_scalefont
 *           - ps_rotatefont
 *           - ps_shearfont
 *           - ps_setlinewidth
 *           - ps_setlinejoin
 *           - ps_setlinecap
 *           - ps_movetoCP
 *           - ps_moveto
 *           - ps_sync_cp
 *           - ps_lineto
 *           - ps_curveto
 *           - ps_strokecurrentpath
 *           - ps_stroke
 *           - ps_clearpath
 *           - ps_newpath
 *           - ps_closepath
 *           - ps_strokepath
 *           - ps_fill
 *           - ps_eofill
 *           - ps_setfont
 *           - ps_SetDownloadedFont
 *           - ps_show
 *           - ps_showpage
 *           - ps_status
 *           - ps_gsave
 *           - ps_restore
 *           - ps_savematrix
 *           - ps_restorematrix
 *           - ps_saveclipmatrix
 *           - ps_restoreclipmatrix
 *           - ps_concat
 *           - ps_setmatrix
 *           - ps_translate
 *           - ps_clip
 *           - ps_eoclip
 *           - ps_imagedata
 *           - ps_setrgbcolor
 *           - ps_setdashnow
 *           - ps_setdash
 *           - ps_arc
 *           - ps_arcn
 *           - DeselectManual
 *           - GetDuplexCommand
 *           - LoadTrayCommand
 *
 * NOTES
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/


#pragma pack(1)
#include <math.h>

#include "inc\prdinclt.h"
#include <pmdev.h>
#include "inc\prdmath.h"
#include "inc\utl.h"
#include "inc\prdgextf.h"
#include "inc\fremap.h"
#include "inc\prdeextf.h"
#include "inc\prdlextf.h"
#include "inc\pspagtun.h"             /* V2.174057  Page Tuning */
#include <pmspl.h>
#include "inc\prdtextf.h"
#include <string.h>                   /*            - string header */
#define  INCL_GENPLIB_ERROR
#define  INCL_GENPLIB_MEMORY
#include <genplib.h>

#define FIXED_255   0x00ff0000

extern BOOL utl_MemIsEqual(PB, PB, int);
extern LONG prdb_GetScanLine (PDDC,PB);     /* prdbm.c */
extern VOID FontDownload ( PDDC, PSZ );
extern PSZ  FullToFontName( PSZ, PDDC );
extern PSZ pszStrChr( PSZ , CHAR );
extern HWND hwndMain;
extern PVOID pProcessHeap;
VOID _Optlink ps_CosmeticLine( PDDC );
/*
**           
** Function parameters have changed for dynamic string handling.
*/
VOID GetDuplexCommand( PSZ *, PB *, PDDC );

/*
** This is some header PostScript code sent down to the printer at the
** start of each document.
*/

/*
**  The ip and id macros should be downloaded only if an imagedata
**  call is executed.
*/


/*
**  Short Name List:
**
**    c   curveto
**    cf  currentfont
**    cm  currentmatix
**    cp  closepath
**    cw  currentlinewidth
**    d   setdash
**    e   eofill
**    f   fill
**    g   setgray
**    gr  grestore
**    gs  gsave
**    j   setlinejoin
**    l   lineto
**    m   moveto
**    mx  matrix
**    n   newpath
**    r   setrgbcolor
**    s   stroke
**    sm  setmatrix
**    t   show
**    w   setlinewidth
**    wc  set cosmetic line width
*/

static PSZ apszHeader[] =
    {
    "200 dict begin",

/*
**  optimizations...
*/
    "/B {bind def} bind def ",
    "/w {setlinewidth} B /wc {dw dw idtransform pop w} B /wcx {dup dw mul exch dw mul idtransform pop w} B",
    "/_snap { transform .25 sub round .25 add exch .25 sub round .25 add exch itransform}B",
    "/c {_snap curveto} B /d {setdash} B /e {eofill} B /f {fill} B",
    "/g {setgray} B /gr {grestore} B /gs {gsave} B /j {setlinejoin} B",
    "/l {_snap lineto} B /m {_snap moveto} B /mx {matrix} B /n {newpath} B",
    "/r {setrgbcolor} B /s {stroke} B /sm {setmatrix} B /t {show} B",
    "/cm {currentmatrix} B /cp {closepath} B",
    "/cw {currentlinewidth} B /cf {currentfont} B",
    "/sd {1 1 idtransform pop abs} B /ic {initclip} B",

    "/SF {findfont exch scalefont setfont} B /MF {findfont exch makefont setfont} B",
    "/ms mx B /SM {setmatrix} B /scm {/msclp mx cm def} B",
    "/md mx cm def /setmx {md sm concat} B /st {exch def} B /AS {awidthshow} B",
    "/box {/by2 st /bx2 st /by1 st /bx1 st bx1 by1 m bx2 by1 l bx2 by2 l bx1 by2 l cp} B",
    "/CP {currentpoint} B /K1 {{pop pop 3 1 roll add 2 1 roll m CP} exch kshow} B",
    "/K {K1 3 1 roll add 2 1 roll m} B",

    /*
    **  The line below was changed to set line width to 1 (the default)
    **  instead of 0 when when resetting attributes at the start of a new page.
    **   "/eject {ms cm showpage 1 w 0 g sm} B",                      
    */

    /*
    **  The line above was changed to set line width to the current
    **  before the show page.                                         
    */
    "/eject {ms cm currentlinewidth showpage w 0 g sm} B",

    "/ishow {/is ( ) def {is 0 3 -1 roll put is stringwidth pop dup",
    "is show neg 0 rmoveto round cvi 0 rmoveto} forall} B",
    "/u {ishow} B",
    "/ip {/ip_inv st neg /ip_y st neg /ip_x st /ip_w st} B",
    "/id {ip_w 1 ip_inv [1 0 0 1 ip_x ip_y] 5 -1 roll imagemask /ip_y ip_y 1 add def} B",
    "%%EndProcSet",
    "%%EndProlog",
    "%%BeginSetup",
    "wc 0 g 2 setlinejoin",
    "0 0 0 true ip",
/*  "/SavedState0 save def",  ** issued later */

    /*
    ** The line below was commented, so that the default line width
    ** remains as set by the wc (cosmetic line width).
    ** Whenever the geometric line width is requested, that width will be
    ** set at the appropriately place and the width will be reset to wc.
    ** "1 w", // This line added to force line width to the default of one.
    **                                                                
    */

    NULL
    };


static PSZ apszMakeOutlineFont [] =
{
    "/modict 7 dict def /MOF { modict begin /uid exch def /sw exch def",
    "/nfn exch def /bfn exch def /bfd bfn findfont def /numf bfd maxlength 1 add def",
    "bfd /UniqueID known not {/numf numf 1 add def} if /ofd numf dict def",
    "bfd {exch dup /FID ne {exch ofd 3 1 roll put} {pop pop} ifelse} forall",
    "ofd /FontName nfn put ofd /PaintType 2 put ofd /StrokeWidth sw put",
    "ofd /UniqueID uid put nfn ofd definefont pop end } def",
    NULL
};

/*
**  The dash values below are sent to PostScript as the integer array
**  in the "setdash" command
*/

static short int aiDash0[] = {0};                    /* Default             */
static short int aiDash1[] = {4,12, 0};              /* Dotted              */
static short int aiDash2[] = {24,24,0};              /* Short dashed        */
static short int aiDash3[] = {24,12,4,12, 0};        /* Dash dot            */
static short int aiDash4[] = {4, 4, 4,16, 0};        /* double dotted       */
static short int aiDash5[] = {48,24, 0};             /* Long dash           */
static short int aiDash6[] = {24,12,4, 8, 4,12, 0};  /* Dash, double dot    */
static short int aiDash7[] = {0};                    /* Solid               */
static short int aiDash8[] = {0};                    /* Invisible           */
static short int aiDash9[] = {4, 4, 0};              /* Alternate pixels on */

static short int *apiDash[] = {aiDash0, aiDash1, aiDash2, aiDash3,
                             aiDash4, aiDash5, aiDash6, aiDash7,
                             aiDash8, aiDash9
                            } ;

void szCopy(PSZ, PSZ, int);

#define FATTR_SEL_OUTLINE       0x0008
#define OUTLINE_FONT_DEFINED    0x0040

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

void szCopy( register PSZ pszDst, register PSZ pszSrc,
             register int nbDst )
  /*  register PSZ pszDst;        Ptr to the destination buffer
  **  register PSZ pszSrc;        Ptr to the source string
  **  register int nbDst;         Size of the destination buffer
  */
{
  while (*pszDst++ = *pszSrc++)
  {
    if (--nbDst <= 0)
    {
      *--pszDst = 0;
      break;
    }
  }

  return;
}

/***************************************************************************
 *
 * FUNCTION NAME = MatrixToChannel
 *
 * DESCRIPTION   = Outputs a transform matrix to the print channel.  The
 *                 first four elements of the matrix are in fixed point
 *                 (16.16) format and the last two elements (translation
 *                 components) are long integers.
 *
 * INPUT         = PDDC       pddc
 *                 XFORM FAR *pfxMatrix
 *
 * OUTPUT        = void
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

void MatrixToChannel( PDDC pddc, XFORM *pfxMatrix )
{
  FIXED fxM11, fxM22;
  PrintLog( (PSZ) "pfxMatrix[0] = %f (%08lx)\n", pfxMatrix[0], pfxMatrix[0] );

  /*
  ** PostScript transform matrix can't have 0 in first or fourth place
  */
  if ( ! (fxM11 = pfxMatrix->fxM11 ))
    fxM11 = (FIXED)0x00000001;

  if ( ! (fxM22 = pfxMatrix->fxM22 ))
    fxM22 = (FIXED)0x00000001;


  PrintChannel( pddc, (PSZ) "[%f %f %f %f %ld %ld]",
                fxM11, pfxMatrix->fxM12, pfxMatrix->fxM21, fxM22,
                (LONG) pfxMatrix->lM41, (LONG) pfxMatrix->lM42 );
}

/***************************************************************************
 *
 * FUNCTION NAME = init_cgs
 *
 * DESCRIPTION   =
 *
 * INPUT         =
 *
 * OUTPUT        = void
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/


void init_cgs( PDDC pddc )
{
  PCGS pcgs;
  int i;

  pcgs = &pddc->pddcb->cgs;

  pcgs->fPathExists = FALSE;
  pcgs->fUpdateCP = TRUE;
  pcgs->fValidCP = TRUE;
  pcgs->usLineJoin = 2;
  pcgs->usLineCap = 0;
  pcgs->usLineType = LINETYPE_SOLID;
  pcgs->fxLineWidth = FX_ONE;
  pcgs->lColor = 0L;
  pcgs->fxFontWidth = FX_ZERO;
  pcgs->fxFontHeight = FX_ZERO;
  pcgs->fxFontShear = FX_ZERO;
  pcgs->fxFontAngleX = FX_ZERO;
  pcgs->fxFontAngleY = FX_ZERO;
  pcgs->fValidFont = FALSE;
  pcgs->usSelection = 0;
  pcgs->fOutlineDefined = FALSE;
  pcgs->fCurvesInPath = FALSE;
  pcgs->fFullArcInPath = FALSE;
  pcgs->fFRedefDownld = FALSE;
  pcgs->fIsGeometric = FALSE;

  pcgs->xformCTM.fxM11 = FX_ONE;
  pcgs->xformCTM.fxM12 = FX_ZERO;
  pcgs->xformCTM.fxM21 = FX_ZERO;
  pcgs->xformCTM.fxM22 = FX_ONE;
  pcgs->xformCTM.lM41 = 0L;
  pcgs->xformCTM.lM42 = 0L;

  /*
  **  clear all the entries in the font remapping table.
  */
  for (i=0 ; i < 32 ; pcgs->bFontRemap[i++] = 0);
}

/***************************************************************************
 *
 * FUNCTION NAME = ps_init
 *
 * DESCRIPTION   = This routine prepares the utlps module for operation.
 *
 * INPUT         = PDDC
 *
 * OUTPUT        = void
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

void ps_init( PDDC pddc )
{
  pddc->usgsStack = IGSMAX;
  init_cgs(pddc);
}

/***************************************************************************
 *
 * FUNCTION NAME = GetMappedFontName
 *
 * DESCRIPTION   = When a font is remapped to support various code pages
 *                 its necessary to create a pseudoname for the font.
 *                 This routine creates the pseudo-font-name based on the
 *                 font number stored in the DDC.
 *
 * INPUT         = PDDC    pddc;
 *                 PB      pbBuf;
 *                 int     cbBuf;
 *                 SHORT  usFont;
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

void GetMappedFontName( PDDC pddc, PB pbBuf, int cbBuf,
                        SHORT  usFont )
{
    BYTE bNum;

    bNum = (BYTE)usFont;

    if (cbBuf < 8)
    {
      return;
    }

    *pbBuf++ = 'F';
    *pbBuf++ = 'o';
    *pbBuf++ = 'n';
    *pbBuf++ = 't';
    *pbBuf++ = (bNum & 7) + '0';
    bNum >>= 3;
    *pbBuf++ = (bNum & 7) + '0';
    bNum >>= 3;
    *pbBuf++ = (bNum & 7) + '0';
    *pbBuf = 0;
}

/***************************************************************************
 *
 * FUNCTION NAME = DoSelectFont
 *
 * DESCRIPTION   = This routine issues commands to the printer that cause
 *                 the current font stored in the graphics state to be
 *                 selected.
 *
 * INPUT         = PDDC
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *                                                                    
 *
 **************************************************************************/


void DoSelectFont( PDDC pddc )
{
  PCGS    pcgs;        /* Ptr to the PostScript state structure */
  SHORT   i;
  FNT     fFnt;
  char    szFont[64];
  PSZ     pszFont;
  BYTE    bEntry;
  PSZ    *ppsz;
  MATRIX *pMatrix;

  /*
  ** if DEVESC_STARTDOC has not been called yet
  ** we don't want to do anything.  this is necessary for the following
  ** reasons.  if this is called before a startdoc has been done, nothing
  ** will be output to the output channel, but flags will have been set
  ** which will cause unruly behavior when trying to print characters.
  */
  if (pddc->pdv->fDocStarted == FALSE)
  {
    return;
  }

  #ifdef POLITICS
    if ((pddc->pdv->fDocStarted == FALSE) &&
         pddc->iType == OD_QUEUED)
    {
      return;
    }
  #endif

  /*
  ** Get a direct pointer to the PS structure to avoid all the
  ** long references.
  */
  pcgs = &pddc->pddcb->cgs;

  bEntry = pddc->pddcb->text.bLogFontEntry;

  /*
  ** Request PostScript to find the font and leave its descriptor on
  ** the stack.
  */
  /*
  **  First map the full name to font name(accepted by ps printer
  */

  if(QueryFontRemap(bEntry))
  {
    szFont[0] = 1;
  }
  else
  {
    szFont[0] = 2;
  }

  /*
  **  get the font name
  */
  if (QueryFontRemap(bEntry))                 /* is it remapped? */
  {
     GetMappedFontName( pddc, (PSZ) szFont, sizeof(szFont),
                        (SHORT)pddc->pddcb->text.bLogFontEntry );
     pszFont = (PSZ) szFont;
  }
  /*
  ** Is it device, hard or soft?
  */
  else if (pszFont = FullToFontName( (PSZ) pddc->pddcb->text.szFont, pddc ))
  {
    FontDownload( pddc, pddc->pddcb->text.szFont ); /* done only if necessary*/
  }
  else                                        /* something else */
  {
    pszFont = (PSZ) pcgs->szFont;
  }

  /*
  ** if an outline (hollow characters) font is selected, we will
  ** need to redefine the font as an outline font.  otherwise,
  ** things are easier.  if the font is an outline font, and we have
  ** not yet defined in in the printer, define it now.
  */
  if ((pddc->pddcb->text.lfiEntry[bEntry].bFlags & LFONT_IS_OUTLINE))
  {
    /*
    **    if (pcgs->usSelection & FATTR_SEL_OUTLINE)
    */
    if (!pcgs->fOutlineDefined)
    {
      ppsz = apszMakeOutlineFont;
      while (*ppsz)
      {
        PrintChannel( pddc, (PSZ) "%ls\n",  (PSZ) *ppsz++ );
      }
      pcgs->fOutlineDefined = TRUE;  /* indicate the font is loaded */
    }

    if (pddc->pddcb->text.lfiEntry[bEntry].bFlags & LFONT_OUTLINE_NOTDONE)
    {
      /*
      **  this is set so the font outline will be one point when the font
      **  is printed at 36 points in size.
      */
      PrintChannel( pddc, (PSZ) "/%ls /%ls1 1000 36 div\n",
                   pszFont, pszFont );
      PrintChannel( pddc, (PSZ) "/%ls findfont dup /UniqueID known\n",
                   pszFont );
      PrintChannel( pddc, (PSZ) "{/UniqueID get 1 add} {pop 1} ifelse MOF\n" );

      /*
      ** indicate that we have now defined the outline font in the
      ** printer for this logical font.
      */
      pddc->pddcb->text.lfiEntry[bEntry].bFlags &= LFONT_OUTLINE_DONE;
    }

    /*
    ** now save the fact the the current font is a hollow font.
    ** we do this by attaching a 1 to the end of the font name.
    */
    i = 0;
    while (*pszFont)
    {
      szFont[i++] = *pszFont++;
    }

    szFont[i++] = '1';
    szFont[i] = '\0';
    pszFont = (PSZ)szFont;
  }

  /*
  ** Check for asymetric font scaling and scale the font descriptor
  ** accordingly.
  ** this assumes that a square PM cell has width=height -
  ** to check that it isn't width = average char. width scaled to
  ** height
  */
  /*
  ** we can do a SCALEFONT only if we've got a square char. cell with
  ** no shear, angle, or inversion.  The pddc has the desired scale
  ** as the first member of its font transform matrix.
  */
  /*
  **  Set local pointer
  */
  pMatrix = (MATRIX *) &pddc->pddcb->text.transformmatrix;

  /*
  **  PTR OSDD B734375 added the following tests because...
  */
  if ( ( pMatrix->mx_fxM11 == pMatrix->mx_fxM22 ) &&
       ( pMatrix->mx_fxM12 == 0L ) &&
       ( pMatrix->mx_fxM21 == 0L ) &&
       /*
       **  PTR OSDD B734375
       **  The following tests are invalid after a EJECT since init_cgs resets them.
       **  They are valid after the app changes them though (until next EJECT
       **     (pcgs->fxFontHeight == pcgs->fxFontWidth) &&
       */
       (pcgs->fxFontShear == FX_ZERO) &&           /* not sheared  */
       (pcgs->fxFontAngleY == FX_ZERO) &&          /* not angled   */
       (pcgs->fxFontAngleX >= FX_ZERO) )           /* not inverted */
  {
    PrintChannel( pddc, (PSZ) "%f /%ls SF\n", pMatrix->mx_fxM11, pszFont );
  }
  /*
  ** otherwise, we must do a MAKEFONT call.  The pddc has the de-
  ** sired transformation matrix already.
  */
  else
  {
    PrintChannel( pddc, (PSZ) "[%f %f %f %f 0 0] /%ls MF\n",
                 pMatrix->mx_fxM11, pMatrix->mx_fxM12,
                 pMatrix->mx_fxM21, pMatrix->mx_fxM22,
                 pszFont) ;
  }

  /*
  **  mark the font as our current font.
  */
  szCopy( pddc->pddcb->cgs.szFont, pszFont, sizeof(pddc->pddcb->cgs.szFont) );

  /*
  ** Ask PostScript to select the font descriptor that we created.
  */
  pddc->pddcb->cgs.fValidFont = TRUE;

  PrintLog( (PSZ) "DoSelectFont command:width=%ld height=%ld shear=%ld angle = {%ld, %ld}\n",
            pcgs->fxFontWidth,pcgs->fxFontHeight,
            pcgs->fxFontShear, pcgs->fxFontAngleX, pcgs->fxFontAngleY );
}

/***************************************************************************
 *
 * FUNCTION NAME = ps_selectfont
 *
 * DESCRIPTION   = extern entry point for the local DoSelectFont routine
 *
 * INPUT         = PDDC
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

void ps_selectfont( PDDC pddc )
{
  DoSelectFont( pddc );
  return;
}

/***************************************************************************
 *
 * FUNCTION NAME = ps_enddoc
 *
 * DESCRIPTION   = The driver calls this routine when it is through with
 *                 the current document. The routine ejects the last page
 *                 (if not already done) and closes the channel.
 *
 * INPUT         = HDC  hdc;
 *                 PDDC pddc;
 *
 * OUTPUT        = SHORT
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/


SHORT ps_enddoc( HDC  hdc, PDDC pddc)
{
  BOOL   fIsDirty;
  SHORT  usReturn;
  /*
  ** The string "patfix" is used to flag changes
  ** Since 1) the fix is a change that should be removed later
  **       2) the fix code is scattered around                      
  */
  HRGN   Region;                                         /*            */
  PDV    pdv = pddc->pdv;   /* Short hand */
  CHAR   cEOF = 4;

  PrintLog( (PSZ) "ps_enddoc routine called\n") ;
  PrintLog( (PSZ) "pdv->fDocStarted = %d\n", pdv->fDocStarted );
  PrintLog( (PSZ) "pdv->cn.fUsed = %d\n", pdv->cn.fUsed );

  /*
  **  initialize usReturn.
  */
  usReturn = 0;

  if (pddc->patfix_hrgn)                               /*            */
  {                                                    /*            */
    GreSelectClipRegion( pddc->patfix_hdc, pddc->patfix_hrgn, &Region  );
    GreDestroyRegion( pddc->patfix_hdc, Region  );     /*            */
  }                                                    /*            */


  /*
  ** If the output channel was used, then do a page eject
  ** and then terminate the print job with an end of file
  ** mark.
  */
  if (pddc->fHeaderSent)
  {
    fIsDirty = pdv->cn.fUsed;

    PrintChannel( pddc, (PSZ)"%%%%Trailer\n" );

    /*
    **  Terminate the dictionary that was started in ps_startdoc
    */
    PrintChannel( pddc, (PSZ)"end\n" );

    /*
    ** If the output is an EPS file, then restore the
    ** saved state, otherwise terminate the document.
    */
    if (pdv->iDestnType==ENCAPS)
    {
      PrintChannel( pddc, (PSZ) "epsSave restore\n" );
    }
    else
    {
      /*
      ** If nothing has been sent to the channel
      ** then it isn't necessary to do a page eject.
      ** the %c 4 means end of transmition
      */
      if (fIsDirty)
      {
        PrintChannel( pddc, (PSZ) "showpage\n" );
      }
/////           
///// PrintChannel( pddc, (PSZ) "%c", 4 );  /*  send end of transmit */
    } /* end else */
  } /* end if */

  /*
  **  OSDD.733610 Send term strings if you got-em
  */
  /*
  **  Send term strings if you got-em
  */
  /*           
  ** Send ctl_d and mode switching strings to printers not to files
  */
  if ( CHECKFLAG( pdv->ulGenFlags, IS_DIRECT_PORT) )
  {
    WriteChannel( pddc, (PSZ)&cEOF, 1 );  /*  send end of transmit */

  }

  /* Send mode switch if needed */          //Must write out strings due to
  if ( ( pddc->iType == OD_DIRECT )     &&  //Novel servers
       ( pdv->usTermLength  > 0 )       &&
       ( pdv->fInitStringSent ==  TRUE ) )
  {
    WriteModeString( pddc, pdv->szTermString,
                     pdv->usTermLength );
    pdv->fInitStringSent = FALSE;
  }

  /*
  **  flush the output buffer.
  */
  FlushChannel( pddc );

  /*
  ** if we have a queued dc, we want to send an enddoc command to the
  ** spooler.  the spooler will return a job id to us.  we will return
  ** this value to the caller.
  */
  if ((pddc->iType == OD_QUEUED) && (pdv->fDocStarted == TRUE))
  {
    usReturn = SplQmEndDoc( (HSPL) pdv->cn.fh );
  }

  /*
  **           
  */
#if 0
//if (pddc->pdv->hThread)
//{
//  GplThreadFlushBuffer( pddc->pdv->hThread, TRUE );
//}
#endif

  pddc->fEOJSent = TRUE;
  pddc->fHeaderSent = FALSE;

  /*
  **  initialize things for the next document.
  */
  ps_init(pddc );
  pdv->szDocName[0] = 0 ;
  pdv->fDocStarted = FALSE;
  pdv->fQMStartDocIssued = FALSE;

  #ifdef POLITICS
  #else
    /*
    ** restore the DC level to where it was before the STARTDOC call.
    ** the fENDDOC flag is set to tell RestoreDC that we are calling
    ** it.  this is the only place where fENDDOC can be set to TRUE.
    ** if pddc->SavedState == 0, someone has restored us to oblivion,
    ** so we have nothing valid to restore to.
    */
    if (pddc->SavedState != 0)
    {
      pddc->fENDDOC = TRUE;
      if (!GreRestoreDC(hdc, (LONG)pddc->SavedState))
      {
          RIP("ps_enddoc: GreRestoreDC failed" );
      }
      pddc->fENDDOC = FALSE;
    }
  #endif

  return( usReturn );
}

/***************************************************************************
 *
 * FUNCTION NAME = InvertTransfer
 *
 * DESCRIPTION   = Sets the transfer function .For first page sets the
 *                 boundary to black .
 *
 * INPUT         = PDDC pddc;
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/


void InvertTransfer( PDDC pddc )
{
  LONG lxR,lyT ;

  unsigned i;
  PCH      pch;
  CHAR     szBuffer[80];
  PCNFDATA pcnfData;  /*pointer to printer configuration data */
  PDESPPD  pdesPpd;   /*pointer to printer descriptor segment */
  PB       apResources[IMAXRES+1];  /*array of pointers to printer resources */
           /*
           **  array of pointer to following resources:-
           **  ppb file header ,
           **  directory buffer ,
           **  itemsbuffer ,
           **  configuration buffer ,
           **  ppd parameter buffer.
           */
  PDV pdv = pddc->pdv;    /* Shorthand */

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

  /*
  ** Allocate space to store the information specific to a given
  ** printer configuration parameters.
  */
  if(!(apResources[CNFRES] = (PCHAR) GplMemoryAlloc( pProcessHeap,
                                                     sizeof(CNFDATA) )))
  {
    PrintLog( (PSZ) "InvertTransfer: can't allocate SIGNATURE instance\n" );
    return;
  }
  pcnfData = (PCNFDATA)apResources[CNFRES];

  /*
  ** initialize the information in pcnfData from pddc
  */
  szCopy( (PSZ)pcnfData->szSegName ,(PSZ)pdv->szSegName,sizeof(pcnfData->szSegName));

  /*
  **  Read the Printer info segment from Resources appended.
  */
  pcnfData->lGetPtr = 0 ;     /* Read in all the stuff */
  if (!LoadInfoSegment((PB *)apResources))
  {
    return;
  }
  pdesPpd = (PDESPPD)apResources[PPDRES];

  /*
  **  If inverted mode specified issue the following command
  */
  if (pdv->effOutput.fIsDrawInverted)
  {
    if (pdesPpd->desItems.ofsTransferInv !=-1)
    {
      pch = (PCH)(apResources[RESBUF]+pdesPpd->desItems.ofsTransferInv );
    }
    else
    {
      pch = NULL;
    }
  }
  else if (pdesPpd->desItems.ofsTransferNor !=-1)
  {
    pch = (PCH)(apResources[RESBUF]+pdesPpd->desItems.ofsTransferNor );
  }
  else
  {
    pch = NULL;
  }

  if (pch != NULL)
  {
    pch = pch+2;     /* advance by 2 to skip the length part */
  }

  lxR=pdv->canvas.rcl.xRight - pdv->canvas.rcl.xLeft;
  lyT=pdv->canvas.rcl.yTop - pdv->canvas.rcl.yBottom;

  if (pch != NULL)
  {
    while (*pch !='\0')
    {
      i=0;
      while (*pch != '\n' && *pch !='\0')
      {
        szBuffer[i++]=*pch++;
      }
      if (*pch == '\n')
      {
        pch++;
      }
      szBuffer[i++] = '\n';
      szBuffer[i] = '\0';
      PrintChannel( pddc, (PSZ) szBuffer );
    }
    PrintChannel( pddc,(PSZ) "settransfer\n" );
  }
  if (pdv->effOutput.fIsDrawInverted)
  {
    if (pch == NULL)
    {
      PrintChannel( pddc,
                    (PSZ) "{1 exch sub} settransfer\n" );
    }
    PrintChannel( pddc,
                  (PSZ) "1 setgray 0 setlinewidth 0 0 moveto\n" );
    PrintChannel( pddc,
                 (PSZ) "%ld 0 lineto %ld %ld lineto\n",
                 lxR,lxR,lyT);
    PrintChannel( pddc,
                  (PSZ) "0 %ld lineto 0 0 lineto closepath fill\n",
                  lyT);
  }

  /*
  **  free the resources allocated
  */
  FreeMemory( (PB *)apResources );
}

/***************************************************************************
 *
 * FUNCTION NAME = ps_effects
 *
 * DESCRIPTION   = Sets up printer for user selected effects
 *
 * INPUT         = PDDC pddc;
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *                                                                    
 *                                                                    
 *
 **************************************************************************/

void   ps_effects( PDDC pddc)
{
    LONG lx,ly;
    SHORT shXscale ,shYscale;
    SHORT sOrient ;         /* Orientation : Portrait or Landscape */

    shXscale = shYscale = 1;
    sOrient = pddc->pdv->jobProperties.iOrient;     /* Make local copy */

    if (sOrient == PORTRAIT)
    {
        lx = pddc->pdv->canvas.rclpt.xLeft;
        ly = pddc->pdv->canvas.rclpt.yBottom;

        if (pddc->pdv->effOutput.fIsFliptb )
        {
            ly = pddc->pdv->canvas.rclpt.yTop;
            shYscale = -1 ;
        }
        if (pddc->pdv->effOutput.fIsFliplr )
        {
            lx = pddc->pdv->canvas.rclpt.xRight;
            shXscale = -1 ;
        }

    }
    /*
    **  If landscape translate the coordinate system to right boundary and
    **  rotate the printer coordinate system by 90
    */
    else
    {
        lx = pddc->pdv->canvas.rclpt.yTop;
        ly = pddc->pdv->canvas.rclpt.xLeft;

        if (pddc->pdv->effOutput.fIsFliptb )
        {
            lx = pddc->pdv->canvas.rclpt.yBottom;
            shYscale = -1 ;
        }
        if (pddc->pdv->effOutput.fIsFliplr )
        {
            ly = pddc->pdv->canvas.rclpt.xRight;
            shXscale = -1 ;
        }

    }

    /*
    **  We do not want to scale the fill patterns so we save transform in
    **  mdevice with out scaling.  Effects do needed to be applied to mdevice
    **  though..
    */

    /*
    **  Translate
    */
    PrintChannel( pddc, (PSZ)"%ld %ld translate ", lx,ly );

    /*
    **  Rotate
    */
    if ( sOrient == LANDSCAPE )
        PrintChannel( pddc, (PSZ) "90 rotate " );

    /*
    **  Points to pel conversion
    */
    PrintChannel( pddc, (PSZ)"72 dpi div dup scale\n" );

    /*
    **  Any added effects
    */
    if ( shYscale !=1 || shXscale !=1)
        PrintChannel( pddc, (PSZ) "%d %d scale\n", shXscale, shYscale );

    /*
    **  Take snapshot of CTM for patterns
    */
    PrintChannel( pddc, (PSZ)"/mdevice matrix currentmatrix def\n" );

    /*
    **  Add in any scaling - zoom in or zoom out
    */
    if ( pddc->pdv->jobProperties.uScale !=100 )
        PrintChannel( pddc, (PSZ) "%d 100 div dup scale\n",
                      pddc->pdv->jobProperties.uScale );

}


/*
**  The following block contains code to help parse ARE and FIT parameters.
*/
PSZ pszQProcParms;                                       /*            */

/***************************************************************************
 *
 * FUNCTION NAME = EatSeper
 *
 * DESCRIPTION   = This routine moves the buffer pointer forward to the next
 *                 non-white character; also skips commas.
 *
 * INPUT         = VOID
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/


VOID EatSeper()                                          /*            */
{                                                        /*            */
    while (*pszQProcParms==' ' || *pszQProcParms=='\t' || *pszQProcParms==',' )
        ++pszQProcParms;                                 /*            */
}                                                        /*            */


/***************************************************************************
 *
 * FUNCTION NAME = FindString
 *
 * DESCRIPTION   = This routine moves the buffer pointer forward to the
 *                 character following str and returns TRUE if found;
 *                 otherwise FALSE.
 *
 * INPUT         = PCHAR   str
 *                 SHORT   len
 *
 *
 * OUTPUT        = BOOL
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/


BOOL FindString( PCHAR str, SHORT   len )                /*            */
{                                                        /*            */
    PCHAR  scan;                                         /*            */
    SHORT  i;                                            /*            */
    CHAR   ch;                                           /*            */

    ch = *str;                                           /*            */

    while (*pszQProcParms!='\0') {                       /*            */
      i = 0;                                             /*            */
      scan = str;                                        /*            */
      while (*pszQProcParms!=ch && *pszQProcParms!='\0') /*            */
          ++pszQProcParms;                               /*            */

      while ( *pszQProcParms == *scan ) {                /*            */
        pszQProcParms++;                                 /*            */
        scan++;                                          /*            */
        i++;                                             /*            */
      }                                                  /*            */
      if ( i == len ) break;                             /*            */
    }                                                    /*            */

    if ( i == len )                                      /*            */
      return( TRUE );                                    /*            */
    else                                                 /*            */
      return( FALSE );                                   /*            */
}                                                        /*            */


/***************************************************************************
 *
 * FUNCTION NAME = ScanInt
 *
 * DESCRIPTION   = This routine parses a positive ASCII decimal number from
 *                 the input file stream and returns its value.
 *
 * INPUT         = VOID
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/


SHORT ScanInt()                                          /*            */
{                                                        /*            */
    SHORT   iVal;                                        /*            */
    CHAR    chDigit;                                     /*            */

    iVal = 0;                                            /*            */
    chDigit = *pszQProcParms;                            /*            */

    while (chDigit >= '0' && chDigit <= '9')             /*            */
    {                                                    /*            */
        iVal = iVal * 10 + (chDigit - '0');              /*            */
        chDigit = *(++pszQProcParms);                    /*            */
    }                                                    /*            */

    return(iVal);                                        /*            */
}                                                        /*            */


/***************************************************************************
 *
 * FUNCTION NAME = ps_startdoc
 *
 * DESCRIPTION   = The driver calls this routine before printing a
 *                 new document.
 *
 * INPUT         = PDDC      pddc
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

void ps_startdoc ( PDDC pddc )
{
  LONG      lTimeout;
  DATETIME  datetime;
  PSZ FAR  *ppsz;
  CHAR      sz[MAX_KEYAPPSIZE];
  PSZ       pszP;
  /*
  **           
  ** Convert arrays to pointers.  Memory for these pointer will be allocated
  ** to the size needed for their respective data.
  ** Also, by using pointers instead of fixed-length arrays, it is possible
  ** to support large PostScript command strings that were otherwise impossible
  ** if the string was larger than the array.
  */
#if 0
//  UCHAR     abResBuffer [256];                           /*            */
//  char      abInpCommand[256];                           /*            */
//  char      abPaperCommand[256];
#endif
  PSZ       abResBuffer;
  PSZ       abInpCommand;
  PSZ       abPaperCommand;
  PSZ       abDuplexCommand;

  BOOL      blManualWait;
  ULONG     ulversion;
  BOOL      fIsColorDevice;                              
  HAB       hab;

  SHORT     QPP_AREwidth, QPP_AREheight, QPP_AREleft, QPP_AREtop,  /*            */
            QPP_FITleft, QPP_FITtop;                     /*            */
  BOOL      AREfound, FITfound, Clip;                    /*            */
  RECTL     ClipRectl, PageView;                         /*            */
  LONG      DEVheight, DEVwidth;                         /*            */
  POINTL    Centre,l_t_in_pels,shift_value;              /*            */
  USHORT    uCounter;                                    /*            */
  USHORT    uResValue = 0;                               /*            */
  PDV       pdv = pddc->pdv;
  BOOL      bTrayCmdsLoaded;                             // @V3.0115171
  BOOL      bIsJCLResolution;                            // @V3.0115171
  CHAR      aJCLToPS[ 81 ];                              // @V3.0115171

  PrintLog( (PSZ) "ps_startdoc (%lp)\n", pddc );
  PrintLog( (PSZ) "pddc->fEOJSent = %d\n", pddc->fEOJSent );
  PrintLog( (PSZ) "pddc->fHeaderSent = %d\n",pddc->fHeaderSent );
  PrintLog( (PSZ) "pdv->cn.fChannelIsValid = %d\n",
            pdv->cn.fChannelIsValid );
  PrintLog( (PSZ) "pdv->fDocStarted = %d\n",
            pdv->fDocStarted );

  /*
  ** if DEVESC_STARTDOC has not been issued, do not send any output
  ** to the printer.
  */
  if (!pdv->fDocStarted)
  {
    return;
  }

  #ifdef POLITICS
    /*
    ** it is supposedly guaranteed that StartDoc and Enddoc must be
    ** used to separate multiple documents in a single DC.  with that
    ** in mind, if we are called and we have just endded a document,
    ** we better have been called by DEVESC_STARTDOC, else end.
    */
    if ((pddc->fEOJSent == TRUE) && (pddc->fDEVESC_STARTDOC == FALSE))
    {
      return;
    }

    /*
    ** it is also supposedly guaranteed that if we have an OD_QUEUED DC
    ** that a DEVESC_STARDOC must be issued to begin a document.  with
    ** that in mind, make sure that if we are queued, that we have been
    ** called by DEVESC_STARTDOC, else just return.
    */
    if ((pddc->iType == OD_QUEUED) && (pddc->fDEVESC_STARTDOC == FALSE))
    {
      return;
    }

    /*
    **  reset this to FALSE.  only DEVESC_STARTDOC can set it to TRUE.
    */
    pddc->fDEVESC_STARTDOC = FALSE;
  #endif

  /*
  ** if the channel has not yet been opened, it is time to open it now
  ** if openchannel fails, it will mark the channel as invalid.  this
  ** result will be picked up by ps_status, which is used on all graphics
  ** calls.
  */
  if (!pdv->cn.fChannelOpen)
  {
    OpenChannel( pddc );
  }

  pddc->fEOJSent = FALSE;

  /*
  ** if the header has already been sent, dont send it again
  */
  if( pddc->fHeaderSent )
  {
    return;
  }

  /*
  **            Can allow multiple issues of SplQmStartdoc
  */
  if ( pdv->fQMStartDocIssued == TRUE )
  {
    return;
  }

  pddc->fHeaderSent = TRUE;
  pdv->shPageno  = 1;

  pdv->fDocStarted = TRUE;

  /*
  ** @V3.0115171
  ** Move LoadTrayCommand up so that if the Resolution is part of the JCL
  ** structure, we can output it at the head of the job.
  */
  bTrayCmdsLoaded = LoadTrayCommand( pddc, (PSZ *) &abPaperCommand,
                                     (PSZ *) &abInpCommand,
                                     (BOOL *) &blManualWait,
                                     (PSZ *) &abDuplexCommand,
                                     (PBOOL) &fIsColorDevice,
                                     (PSZ *) &abResBuffer,
                                     &bIsJCLResolution,
                                     aJCLToPS );

  /*
  ** If the Resolution is stated as a JCL command, output it here.
  */
  if (bTrayCmdsLoaded == TRUE && bIsJCLResolution == TRUE)
  {
    WriteModeString( pddc, abResBuffer, strlen( abResBuffer ) );
  }

  /*
  ** If a JCL to PS conversion string is provided, output it here.
  */
  if (aJCLToPS[ 0 ] != 0)
  {
    WriteModeString( pddc, (PB) aJCLToPS, strlen( aJCLToPS ) );
  }

  /*
  ** If this is a queued DC, then tell the spooler to start the
  ** document.
  */
  if (pddc->iType == OD_QUEUED)
  {
    if (!SplQmStartDoc( (HSPL) pdv->cn.fh, (PSZ) pdv->szDocName) )
    {
      GplErrSetError(  PMERR_STARTDOC_NOT_ISSUED );
      return;
    }

    /*
    **  We've issued a SplQmStartDoc
    */
    pdv->fQMStartDocIssued = TRUE;
  }

  /*
  ** Initialize the graphics state.
  */
  if (pdv->iDestnType==ENCAPS)
  {
    PrintChannel( pddc, (PSZ) "%%!PS-Adobe-2.0 EPSF-2.0\n" );
  }
  else
  {
/*  PrintChannel( pddc, (PSZ) "%%!PS-Adobe-1.0\n" ); */
    PrintChannel( pddc, (PSZ) "%%!PS-Adobe\n" );  /*            */
  }

  PrintChannel( pddc, (PSZ) "%%%%Title: " );
  PrintChannel( pddc, (PSZ) "%ls\n",  (PSZ) pdv->szDocName );


  hab = WinQueryAnchorBlock( hwndMain );
  ulversion = WinQueryVersion( hab );

  PrintChannel( pddc, (PSZ) "%%%%Creator: Presentation Manager %d.%d\n",
                LOBYTE(LOUSHORT(ulversion)), HIBYTE(LOUSHORT(ulversion)));
  DosGetDateTime( &datetime );
  PrintChannel( pddc, (PSZ) "%%%%CreationDate: %d:%2d  %2d/%2d/%d\n",
                (SHORT) datetime.hours, (SHORT) datetime.minutes,
                (SHORT) datetime.month, (SHORT) datetime.day,
                (SHORT) datetime.year);

  if (pdv->iDestnType==ENCAPS)
  {
    PrintChannel(pddc, (PSZ) "%%%%BoundingBox: %ld %ld %ld %ld\n",
        pdv->canvas.rcl.xLeft * 72 /pdv->canvas.iRes,
        pdv->canvas.rcl.yBottom * 72 /pdv->canvas.iRes,
        pdv->canvas.rcl.xRight * 72 /pdv->canvas.iRes,
        pdv->canvas.rcl.yTop * 72 /pdv->canvas.iRes );
    PrintChannel( pddc, (PSZ) "/epsSave save def\n" );
  }
  else /* not ENCAPS */
  {
    /*
    ** There are certain print job specific commands that
    ** should not be generated for encapsulated PostScript
    ** output files.
    */
    PrintChannel( pddc, (PSZ) "%%%%DocumentProcSets: PM_1.2\n" );
    PrintChannel( pddc, (PSZ) "%%%%DocumentSuppliedProcSets: PM_1.2\n" );

    if (pdv->iDestnType == SYSTEM )
    {
      /*
      ** DCR1399.8
      ** Key in form of PM_DD_<printerName>,<deviceDriver>.<deviceName>
      ** so extract out printer logical name (printerName)
      */
      /*
      ** Key in form of PM_DD_<printerName>,<deviceDriver>.<deviceName>
      ** so extract out printer logical name (printerName)
      */
      szCopy( (PSZ)sz, (PSZ)pdv->szKeyApp + sizeof("PM_DD_") - 1,
              MAX_KEYAPPSIZE );
      if ( pszP = pszStrChr( (PSZ)sz, ',' ) )
      {
        *pszP = '\0';
      }
      PrintChannel( pddc, (PSZ) "%%%%PrinterName: %ls\n", (PSZ) sz );
    }
    else
    {
      PrintChannel( pddc, (PSZ) "%%%%PrinterName: %ls\n",
                    (PSZ)pdv->dop.pszLogAddress );
    }

    PrintChannel( pddc, (PSZ) "%%%%PrinterRect: %ld %ld %ld %ld\n",
                  pdv->canvas.rcl.xLeft * 72 /pdv->canvas.iRes,
                  pdv->canvas.rcl.yBottom * 72 /pdv->canvas.iRes,
                  pdv->canvas.rcl.xRight * 72 /pdv->canvas.iRes,
                  pdv->canvas.rcl.yTop * 72 /pdv->canvas.iRes );
    PrintChannel( pddc, (PSZ) "%%%%EndComments\n" );
    PrintChannel( pddc, (PSZ) "%%%%BeginProcSet: PM_1.2\n" );
    PrintChannel( pddc, (PSZ) "%% Copyright (c) 1989, 1990 IBM Corporation\n" );
    PrintChannel( pddc, (PSZ) "%% Copyright (c) 1989, 1990 Microsoft Corporation\n" );

    /*
    **  Issue the wait timeout command.
    */
    if (pdv->effOutput.iWaitTimeout < 0)
    {
      lTimeout = 0;
    }
    else
    {
      lTimeout=pdv->effOutput.iWaitTimeout;
    }
    PrintChannel( pddc,
                  (PSZ) "statusdict /waittimeout %ld put\n",
                  lTimeout );

    /*
    **  Issue the job timeout command.
    */
    if (pdv->effOutput.iJobTimeout < 0)
    {
      lTimeout = 0;
    }
    else
    {
      lTimeout=pdv->effOutput.iJobTimeout;
    }
    PrintChannel( pddc,
                  (PSZ) "%ld statusdict begin jobtimeout end\n",
                  lTimeout );

    PrintChannel( pddc,
                  (PSZ) "ne {statusdict begin %ld setjobtimeout end} if\n",
                  lTimeout );

    /*
    ** @V3.0115171
    ** Return code from LoadTrayCommand().
    */
    if (bTrayCmdsLoaded == TRUE)
    {
      /*           
      ** Send the current resolution string to the print file.
      ** (LMT) if there is one
      */
      /*
      **           
      */
      /*
      ** @V3.0115171
      ** Output the resolution only if it is not part of the JCL format.
      */
      if (abResBuffer != (PSZ) 0 && bIsJCLResolution == FALSE)
      {
        /*
        **           
        ** Remove PrintChannel because it has a limit of 255 characters.
        ** The resolution string may contain more than 255 characters.
        ** WriteChannel solves this.
        */
#if 0
//      PrintChannel( pddc, (PSZ) "%ls\n",  (PSZ) abResBuffer );
#endif
        WriteChannel( pddc, abResBuffer, strlen( abResBuffer ) );
        WriteChannel( pddc, "\n", 1 );
        GplMemoryFree( abResBuffer );
      }

      /*           
      ** Move The duplex comand before paper commands
      */
      /*
      **  DCR 1462 issue the duplex command if there is one
      */
      /*
      **  issue the duplex command if there is one
      */
      /*
      **           
      */
      if ( abDuplexCommand != NULL )
      {
        WriteChannel( pddc, abDuplexCommand, strlen( abDuplexCommand ) );
        WriteChannel( pddc, "\n", 1 );
        GplMemoryFree( abDuplexCommand );
      }

      /*
      ** the input paper tray command MUST be done before the
      ** paper select command.
      */
      /*
      **  if a valid command obtained
      */
      /*
      **           
      ** This is now a pointer, not an array.
      */
#if 0
//    if (abInpCommand[0]!= '\0')
#endif
      if (abInpCommand != (PSZ) 0)
      {
        /*
        **  if manual wait flag true output the postscript wait
        **  program segment
        */
        if (blManualWait)
        {
          PrintChannel( pddc ,(PSZ) "usertime 5000 add {dup usertime lt{pop exit}if}loop \n" );
        }

        /*
        ** output the new tray select command
        */
        /*
        **           
        ** The maximum size for PrintChannel is 255 characters.  Since
        ** abInpCommand can contain more, use WriteChannel instead.
        ** For this to work properly, the string format must be:
        **   "[input command string]\n0"
        */
        WriteChannel( pddc, abInpCommand, strlen( abInpCommand ) );
#if 0
//      PrintChannel( pddc, (PSZ) "%ls\n",  (PSZ) abInpCommand );
#endif
        PrintLog( (PSZ) "InputPaperTrayCommand\n" );

        /*
        **           
        */
        GplMemoryFree( abInpCommand );
      }

      /*
      **  if a valid command obtained
      */
      /*
      **           
      ** This is now a pointer, not an array.
      */
#if 0
//    if (abPaperCommand[0]!='\0')
#endif
      if (abPaperCommand != NULL)
      {
        if (szIsEqual((PSZ)"custom" ,(PSZ) abPaperCommand))
        {
          PrintChannel( pddc,(PSZ)"statusdict begin %d %d 0 0 setpageparams end\n",
                        pdv->sourcePaper.shCustomWidth,
                        pdv->sourcePaper.shCustomHeight);
        }
        else
        {
          /*
          **           
          */
          WriteChannel( pddc, abPaperCommand, strlen( abPaperCommand ) );
#if 0
//        PrintChannel( pddc, (PSZ) "%ls\n",  (PSZ) abPaperCommand );
#endif
        }
        PrintLog( (PSZ) "PaperSelectCommand\n" );

        /*
        **           
        ** Free allocated memory.
        */
        GplMemoryFree( abPaperCommand );
      }

    }

    /*
    ** If no of copies > 1 change #copies parameter on dictionary
    */
    if (pdv->iCntCopies > 1)
    {
      PrintChannel( pddc, (PSZ) "/#copies %d def\n",
                    pdv->iCntCopies );
    }
  }

  /*
  ** Compute the cosmetic line width in pixels.  This is done with
  ** PostScript code so that a print file can be played on
  ** different printers with different resolutions and still come
  ** out looking the same.  Warning: Its not good enough to simply
  ** output the number of pixels.
  **
  ** This code must be generated before the coordinate system is
  ** scaled.  The value of "dw" is set to be less than one-half
  ** pixel in device space.  The half-pixel value is necessary
  ** so that lines don't straddle two pixels.
  */
  /*
  ** Let us draw the cosmetic line with the thickness
  ** equal to .11 * 1 / 72 inches
  ** (.11 * 300 / 72 = 33/72 printer pels for 300 dpi printer).
  ** The Postscript default user unit is 1/72 inch ( A point).
  **                                                                
  */
  /*
  ** the default linewidth looks too thin on the QMS ColorScript printer.
  ** this is special case code to thicken it up.
  */
  /*
  ** If the printer name start with "QMS ColorScript 100" then
  ** thicken the cosmetic lines.
  **                                                                
  */
  /*
  **  szCopy(sz, (PSZ)pdv->szSegName, 20) ;
  **  if (szIsEqual(sz, (PSZ)"QMS ColorScript 100"))
  **      PrintChannel(pddc, (PSZ) "/dw .5 .5 dtransform pop def\n");
  **  else
  **      PrintChannel(pddc, (PSZ) "/dw .11 .11 dtransform pop def\n");
  **  **Defect 51584 It seems like all color printers 300 DPI or less esp.
  **  **transfer roll types have trouble with very thin lines - they tend to
  **  **break up and appear uneven.  The change now is to thicken cosmetic lines
  **  **for color printers (even if B&W mode) if DPI <= 300
  */
  if ( ( fIsColorDevice == TRUE ) &&
       ( pdv->canvas.iRes <= 300 ) )
  {
    PrintChannel( pddc, (PSZ) "/dw .5 .5 dtransform pop def\n" );
  }
  else
  {
    PrintChannel( pddc, (PSZ) "/dw .11 .11 dtransform pop def\n" );
  }

  /*
  **  Defect 51584
  */
  /*
  ** Scale the coordinate system to match the resolution.
  */
  PrintChannel( pddc, (PSZ) "/dpi %d def ", pdv->canvas.iRes );

  /*
  ** Add Support for the IBM 4XXX Printing Envelopes
  */
  /*
  **  Here the 4XXX Laser printers need to print envelopes the
  **  reverse of the way the standard rotation is handled in
  **  Postscript.  So when printing an envelope on an IMB laser
  **  printer, we force the required directional attributed.
  **  All this is handled in the effects call that follows.            
  */
  szCopy( sz, pdv->jobProperties.szFormSize, 9 );
  if ((szIsEqual(sz, (PSZ)"Envelope")) ||  (szIsEqual(sz, (PSZ)"ENVELOPE")))
  {
    szCopy( sz, pdv->szSegName, 5 );
    if (szIsEqual( sz, (PSZ)"IBM " ))
    {
      szCopy( sz, pdv->szSegName + 4, 6 );
      if ( szIsEqual( sz, (PSZ)"4079 " ) == 0 )   /*Dont flip for 4079*/
      {
        pdv->effOutput.fIsFliptb = TRUE;
        pdv->effOutput.fIsFliplr = TRUE;
      }
    }
  }

  /*
  ** Set the special effects.
  */
  ps_effects( pddc );

  InvertTransfer( pddc );

  /*
  ** Download the PostScript header code.
  */
  ppsz = apszHeader;
  while (*ppsz)
  {
      PrintChannel( pddc, (PSZ) "%ls\n",  (PSZ) *ppsz++ );
  }

  ps_movetoCP( pddc );

  /*
  **  Strictly speaking outputting this transform matrix is not necessary
  **  if transform matrix is unity. To optimize it compare this matrix with
  **  unity and if equal, skip this step.
  */
  ps_setmatrix( pddc, (FIXED *) &pddc->pddcb->xformCTM );

  /*
  ** xfx is a variable we define in the printer to keep track of
  ** changes in the transform matrix, and how they affect fill patterns.
  **
  **                                                                
  **
  ** The pattern fill for the patterns PATSYM_HORIZ, PATSYM_VERT and
  ** PATSYM_DIAG1 thru PATSYM_DIAG4 will be drawn at printer pel
  ** (device coordinates).
  ** Hence, the these pattern fill will no longer be dependent on
  ** CTM and xfx value.
  */
  /*
  **     The fix below is not needed  anymore due to PTR B724529
  **     Added for PTR @107 D. Walker 8-30-91
  **     We are using the first value in the matrix to set the fill value
  **     for the current path.  This is fine unless the user (application)
  **     sets the matrix to null or in effect no change.  In this instance this
  **     value is 0.  Since xfx is used in a divide operation this can produce
  **     disasterous results (dividing by 0 is a sin).  The solution is to filter
  **     out zeros, and since the matrix does'nt change anyway this harms nothing.
  **                                                                    
  **   if (pddc->pddcb->xformCTM.fxM11 != 0)
  **       PrintChannel(pddc, (PSZ)"/xfx %f def\n", pddc->pddcb->xformCTM.fxM11);
  */
  /*
  **     We are using the first value in the matrix to set the fill value
  **     for the current path.  This is fine unless the user (application)
  **     sets the matrix to null or in effect no change.  In this instance this
  **     value is 0.  Since xfx is used in a divide operation this can produce
  **     disasterous results (dividing by 0 is a sin).  The solution is to filter
  **     out zeros, and since the matrix does'nt change anyway this harms nothing.
  **                                                                    
  */
  PrintChannel( pddc, (PSZ) "%%%%EndSetup\n%%%%Page: 1 1\n" );

  /*
  **  begin garbage collection                                      
  */
/*PrintChannel( pddc, (PSZ) "/PageState save def\n" ); */
  PrintChannel( pddc, (PSZ) "/SavedState0 save def\n" );

  /*
  **  DEF 51530 Store the CTM in PageState for later use
  */
  /*
  **  Store the CTM in PageState for later use
  */
  pdv->xformPageState = pddc->pddcb->xformCTM;

  /*
  ** The Postscript driver ignores Queue Processor Parameters, because
  ** it only supports PM_Q_RAW.  Ptr #B715027 says a critical situation
  ** exists because the ARE and FIT parameters have no effect.  This is
  ** a fix which parses the QueueProcParams and applies them at the
  ** DD1 stage of the driver.  Basically, the print manager code found
  ** in \drv7\src\pmspl\qpstd.c SetViewMatrix was copied into here.
  */
  AREfound = FITfound = Clip = FALSE;                  /*            */
  if ( (PSZ) pdv->dop.pszQueueProcParams != NULL)   /*            */
  {
    /*
    ** scan the queue processing parameters for ARE, FIT
    */
    pszQProcParms = pdv->dop.pszQueueProcParams;   /*            */
    if ( FindString( "ARE=", 4 ) && *pszQProcParms != 'C' )   /*            */
    {
      QPP_AREwidth  = ScanInt(); EatSeper();        /*            */
      QPP_AREheight = ScanInt(); EatSeper();        /*            */
      QPP_AREleft   = ScanInt(); EatSeper();        /*            */
      QPP_AREtop    = ScanInt();                    /*            */
      AREfound = TRUE;                              /*            */
    }                                                 /*            */

    pszQProcParms = pdv->dop.pszQueueProcParams;/*            */
    if ( FindString( "FIT=", 4 ) && *pszQProcParms != 'S' )
    {
      QPP_FITleft = ScanInt(); EatSeper();          /*            */
      QPP_FITtop = ScanInt();                       /*            */
      FITfound = TRUE;                              /*            */
    }                                                 /*            */

    /*
    ** OSDD.PAR07729 - the test program was passing a queue parm of COP=1 allowing
    ** execution of this code.  DEVHeight and DEVwidth must be initaliazed for
    ** later use.  The "if" test is removed
    **     if ( AREfound || FITfound ) { *************** REMOVE the test
    */

    /*
    **  DevQueryCaps(hdc, CAPS_WIDTH, 2L, DEVheight and DEVwidth);
    */
    DEVheight = pdv->canvas.rcl.yTop - pdv->canvas.rcl.yBottom;
    DEVwidth  = pdv->canvas.rcl.xRight - pdv->canvas.rcl.xLeft;

    GreGetPageViewport( pddc->patfix_hdc, &PageView ); /*            */

    DEVheight--;                                    /*            */
    DEVwidth--;                                     /*            */

    /*
    **  for debugging, show results so far...
    */
    /*
    **        PrintChannel(pddc, (PSZ) "%%%%QueueProcParams:%ls\n",
    **                     (PSZ) pdv->dop.pszQueueProcParams);
    **
    **        PrintChannel(pddc, (PSZ) "%%%%AREProcParm=%d,%d,%d,%d\n",
    **                     QPP_AREwidth, QPP_AREheight, QPP_AREleft, QPP_AREtop);
    **
    **        PrintChannel(pddc, (PSZ) "%%%%FITProcParm=%d,%d\n",
    **                     QPP_FITleft, QPP_FITtop);
    **
    **        PrintChannel(pddc, (PSZ) "%%%%canvasTBRLI=%ld,%ld,%ld,%ld,%d\n",
    **            pdv->canvas.rcl.yTop, pdv->canvas.rcl.yBottom,
    **            pdv->canvas.rcl.xRight, pdv->canvas.rcl.xLeft,
    **            pdv->canvas.iRes);
    **
    **        PrintChannel(pddc, (PSZ) "%%%%DEVheight,width=%ld,%ld\n",
    **                     DEVheight, DEVwidth);
    **
    **        PrintChannel(pddc, (PSZ) "%%%%ARE: Org PageViewLTRB:%ld,%ld,%ld,%ld\n",
    **         PageView.xLeft, PageView.yTop, PageView.xRight, PageView.yBottom);
    */

    /*
    **     } **  end of ARE or FIT found *************** REMOVE the test
    */
    if ( !AREfound )                /* No ARE= parameters... */
    {
      ClipRectl.xLeft=0l;                            /*            */
      ClipRectl.yBottom=0l;                          /*            */
      ClipRectl.xRight=DEVwidth;                     /*            */
      ClipRectl.yTop=DEVheight;                      /*            */
      Clip=FALSE;
    }
    else
    {                        /* Found ARE= parameters... */
      if (!QPP_AREleft)                               /*            */
      {
        ClipRectl.xLeft = 0L;                           /*            */
      }
      else if (QPP_AREleft == 100)                    /*            */
      {
        ClipRectl.xLeft = DEVwidth;                     /*            */
      }
      else                                            /*            */
      {
        ClipRectl.xLeft = QPP_AREleft * DEVwidth / 100L;    /*            */
      }

      if (!QPP_AREtop)                                /*            */
      {
        ClipRectl.yTop = DEVheight;                     /*            */
      }
      else if (QPP_AREtop == 100)                     /*            */
      {
        ClipRectl.yTop = 0L;                            /*            */
      }
      else
      {
        ClipRectl.yTop = (100 - QPP_AREtop) * DEVheight / 100L; /*            */
      }

      QPP_AREwidth = min( QPP_AREwidth, (UCHAR) 100 - QPP_AREleft );
      QPP_AREheight = min( QPP_AREheight, (UCHAR)100 - QPP_AREtop );

      ClipRectl.xRight = ClipRectl.xLeft + (QPP_AREwidth * DEVwidth / 100L);
      ClipRectl.yBottom = ClipRectl.yTop - (QPP_AREheight * DEVheight / 100L );

      Clip=TRUE;                                      /*            */

      /*
      **       PrintChannel(pddc, (PSZ) "%%%%ARE: BTQD:%ld,%ld,%d,%d\n",
      **       ClipRectl.yBottom,  ClipRectl.yTop,  QPP_AREheight,DEVheight);
      */
    }

    if ( !FITfound )               /* If no FIT= parameters... */
    {
      PageView.xLeft = ClipRectl.xLeft;                 /*            */
      PageView.yBottom = ClipRectl.yBottom;             /*            */

      if (((ClipRectl.yTop - ClipRectl.yBottom) * PageView.xRight) <=
          ((ClipRectl.xRight - ClipRectl.xLeft) * PageView.yTop))
      {
        PageView.xRight = PageView.xLeft +          /*            */
                         ((PageView.xRight *        /*            */
                         (ClipRectl.yTop - ClipRectl.yBottom)) /
                          PageView.yTop);           /*            */
        PageView.yTop = ClipRectl.yTop;             /*            */
      }
      else
      {                                        /*            */
        PageView.yTop = PageView.yBottom +          /*            */
                        ((PageView.yTop *            /*            */
                        (ClipRectl.xRight - ClipRectl.xLeft)) /
                         PageView.xRight);          /*            */
        PageView.xRight = ClipRectl.xRight;         /*            */
      }
    }
    else
    {                          /* If FIT= parameters... */
      Centre.x = (ClipRectl.xRight + ClipRectl.xLeft) / 2;/*            */
      Centre.y = (ClipRectl.yTop + ClipRectl.yBottom) / 2;  /*            */

      l_t_in_pels.x = PageView.xRight * QPP_FITleft / 100;/*            */
      l_t_in_pels.y = PageView.yTop * (100 - QPP_FITtop) / 100;

      shift_value.x = Centre.x - l_t_in_pels.x;     /*            */
      shift_value.y = Centre.y - l_t_in_pels.y;     /*            */

      PageView.xLeft   += shift_value.x;            /*            */
      PageView.yTop    += shift_value.y;            /*            */
      PageView.xRight  += shift_value.x;            /*            */
      PageView.yBottom += shift_value.y;            /*            */
    }                                                 /*            */

    if ( AREfound || FITfound )                       /*            */
    {
      ClipRectl.xRight++;                             /*            */
      ClipRectl.yTop++;                               /*            */
      GreSetPageViewport( (HDC) pddc->patfix_hdc,     /*            */
                              &PageView,              /*            */
                              1L);   /* what about COMPUTE_DEVICE_XFORM_NOW? */

      /*
      **       PrintChannel(pddc, (PSZ) "%%%%New PageViewLTRB:%ld,%ld,%ld,%ld\n",
      **         PageView.xLeft, PageView.yTop, PageView.xRight, PageView.yBottom);
      **
      **       PrintChannel(pddc, (PSZ) "%%%%New ClipRectl:%ld,%ld,%ld,%ld\n",
      **        ClipRectl.xLeft, ClipRectl.yTop, ClipRectl.xRight, ClipRectl.yBottom);
      */
      pddc->patfix_hrgn=FALSE;                        /*            */
      if ( Clip )      /* If ARE= parametersv changed clipping... */
      {
        if ( pddc->patfix_hrgn =                       /*            */
            GreCreateRectRegion( pddc->patfix_hdc, &ClipRectl, 1L ))
        {                                             /*            */
          GreSelectClipRegion( pddc->patfix_hdc,      /*            */
                               pddc->patfix_hrgn,     /*            */
                               &pddc->patfix_hrgn );  /*            */
        }
      }                                               /*            */
    }                                                 /*            */
  } /*  end if Queue processing parameters found */    /*            */
}

/***************************************************************************
 *
 * FUNCTION NAME = ps_scalefont
 *
 * DESCRIPTION   = The driver calls this routine to specify the font size.
 *
 * INPUT         = PDDC pddc;
 *                 FIXED fxFontWidth;
 *                 FIXED fxFontHeight;
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

void ps_scalefont( PDDC pddc, FIXED fxFontWidth, FIXED fxFontHeight )
{
  /*
  **  the font width and height are being used loosely here since
  **  the code assumes that the character cell is square, which
  **  it probably isn't.  This should be fixed as soon as we have
  **  the average width information for the font.
  */
  if (fxFontWidth==0)
  {
    fxFontWidth = fxFontHeight;
  }

  if (pddc->pddcb->cgs.fxFontWidth != fxFontWidth ||
      pddc->pddcb->cgs.fxFontHeight != fxFontHeight)
  {
    pddc->pddcb->cgs.fxFontHeight = fxFontHeight;
    pddc->pddcb->cgs.fxFontWidth = fxFontWidth;

    PrintLog( (PSZ) "ps_scalefont routine: width= %f height= %f\n",
                                               fxFontWidth,fxFontHeight );
    pddc->pddcb->cgs.fValidFont = FALSE;
  }
}


/***************************************************************************
 *
 * FUNCTION NAME = ps_rotatefont
 *
 * DESCRIPTION   = The driver calls this routine to specify the font rotation.
 *
 *                 AngleX, AngleY are the end coordinates of a line
 *                 originating at (0,0).  We save these new values in
 *                 ps.gsNew.fxFontAngleX and ps.gsNew.fxFontAngleY, and flag
 *                 an angle change.
 *
 * INPUT         = PDDC pddc
 *                 LONG lAngleX
 *                 LONG lAngleY
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/


void ps_rotatefont( PDDC pddc, LONG lAngleX, LONG lAngleY )
{
  pddc->pddcb->cgs.fxFontAngleX = LongToFx(lAngleX );
  pddc->pddcb->cgs.fxFontAngleY = LongToFx(lAngleY );

  PrintLog( (PSZ) "ps_rotatefont routine: angle {x y} = {%f %f}\n",
            pddc->pddcb->cgs.fxFontAngleX, pddc->pddcb->cgs.fxFontAngleY );
  pddc->pddcb->cgs.fValidFont = FALSE;
}


/***************************************************************************
 *
 * FUNCTION NAME = ps_shearfont
 *
 * DESCRIPTION   = The driver calls this routine to specify the font shear.
 *
 *                 ShearX, ShearY are the end coordinates of a line
 *                 originating at (0,0).  We need to compute the angle from
 *                 the vertical, and multiply this angle by the current
 *                 point size (fxHeight).
 *
 * INPUT         = PDDC pddc
 *                 LONG lShearX
 *                 LONG lShearY
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/


void ps_shearfont( PDDC pddc, LONG lShearX, LONG lShearY)
{
  FIXED fxX, fxY;

  /*
  **  the shear needed = x/y * fxFontHeight
  */
  fxX = LongToFx( lShearX );
  fxY = LongToFx( lShearY );

  /*
  **  !!!CR need to check for division by zero --
  */
  pddc->pddcb->cgs.fxFontShear = frmul( frdiv( fxX, fxY),
                                 pddc->pddcb->cgs.fxFontHeight );

  PrintLog( (PSZ) "ps_shearfont routine: shear = %ld\n", pddc->pddcb->cgs.fxFontShear );

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

/***************************************************************************
 *
 * FUNCTION NAME = ps_setlinewidth
 *
 * DESCRIPTION   = The driver calls this routine to specify the width of
 *                 lines that are "stroked."
 *
 * INPUT         = PDDC pddc         - Ptr to the DC instance data
 *                 FIXED fxLineWidth - The line width to select
 *                 BOOL fIsGeometric - TRUE if the line type is geometric
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

void ps_setlinewidth( PDDC pddc, FIXED fxLineWidth, BOOL fIsGeometric )
  /*
  ** PDDC pddc;                  Ptr to the DC instance data
  ** FIXED fxLineWidth;          The line width to select
  ** BOOL fIsGeometric;          TRUE if the line type is geometric
  */
{
  if ((pddc->pddcb->cgs.fxLineWidth != fxLineWidth) ||
      (fIsGeometric!=pddc->pddcb->cgs.fIsGeometric))
  {
    pddc->pddcb->cgs.fxLineWidth = fxLineWidth;
    pddc->pddcb->cgs.fIsGeometric = fIsGeometric;
    if (fIsGeometric)
    {
      PrintChannel( pddc, (PSZ)"%f w\n", fxLineWidth );
    }
    else
    {
      /*
      ** Added CR after wc.
      **                                                        
      */
      ps_CosmeticLine( pddc );  /* Set cosmetic width */
    }
  }
}

/***************************************************************************
 *
 * FUNCTION NAME = ps_setlinejoin
 *
 * DESCRIPTION   = The driver calls this routine to specify the "linejoin"
 *                 type for lines that are "stroked."  The linejoin values
 *                 are the ones used in PostScript.
 *
 * INPUT         = PDDC   pddc
 *                 SHORT usLineJoin
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

void ps_setlinejoin( PDDC pddc, SHORT usLineJoin )
{
  if (pddc->pddcb->cgs.usLineJoin != usLineJoin)
  {
    pddc->pddcb->cgs.usLineJoin = usLineJoin;
    PrintChannel( pddc, (PSZ)"%d j\n", usLineJoin );
  }
}

/***************************************************************************
 *
 * FUNCTION NAME = ps_setlinecap
 *
 * DESCRIPTION   = The driver calls this routine to specify the "linecap"
 *                 type for lines that are "stroked."  The linecap values
 *                 are the ones used in PostScript.
 *
 * INPUT         = PDDC   pddc
 *                 SHORT usLineCap
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

void ps_setlinecap ( PDDC pddc, SHORT usLineCap )
{
  if (pddc->pddcb->cgs.usLineCap != usLineCap)
  {
    pddc->pddcb->cgs.usLineCap = usLineCap;
    PrintChannel( pddc, (PSZ)"%d setlinecap\n", usLineCap );
  }
}

/***************************************************************************
 *
 * FUNCTION NAME = ps_movetoCP
 *
 * DESCRIPTION   = checks to see if a path exists.  if it does, we do
 *                 nothing.  if no path exists we output a moveto command
 *                 to set the printer's current position equal to that in
 *                 the pddc.  we also set a flag stating a path now exists.
 *
 * INPUT         = PDDC    pddc
 *
 *
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *                                                                    
 *
 ****************************************************************************/

void ps_movetoCP( PDDC pddc )
{
  /*
  **  if a path exists, then we already have a current point in
  **  the printer, so we do not need to output another one.
  */
  if ( pddc->pddcb->cgs.fValidCP &&
      ((!pddc->pddcb->cgs.fPathExists) || (pddc->pddcb->cgs.fUpdateCP)))
  {
    PrintChannel( pddc, (PSZ)"%ld %ld m\n",
                  pddc->pddcb->pen.ptlCur.x, pddc->pddcb->pen.ptlCur.y );
    pddc->pddcb->cgs.fPathExists = TRUE;
    pddc->pddcb->cgs.fUpdateCP = FALSE;
  }
}

/***************************************************************************
 *
 * FUNCTION NAME = ps_moveto
 *
 * DESCRIPTION   = The driver calls this routine to change the logical pen
 *                 coordinates.
 *
 *                 If pptl is different than the current position, we issue
 *                 a move to and mark ourselves as being in a path.
 *
 * INPUT         = PDDC    pddc - Ptr to the DC instance data
 *                 PPOINTL pptl - The point to moveto
 *
 *
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

void ps_moveto( PDDC pddc, PPOINTL pptl )
{
  /*
  ** IF the cp doesn't need to be updated AND it is valid AND
  ** we are in a path AND the pen.ptlCur does match pptl,
  ** THEN leave the cp alone.
  ** ELSE
  ** Set all flags to true and set pen.ptlCur to pptl.
  */
  if ((!pddc->pddcb->cgs.fUpdateCP) && (pddc->pddcb->cgs.fValidCP) &&
      (pddc->pddcb->cgs.fPathExists) && (pddc->pddcb->pen.ptlCur.y == pptl->y) &&
          (pddc->pddcb->pen.ptlCur.x == pptl->x))
  {
    return;
  }

  pddc->pddcb->cgs.fUpdateCP   = TRUE;
  pddc->pddcb->cgs.fPathExists = TRUE;
  pddc->pddcb->cgs.fValidCP    = TRUE;
  pddc->pddcb->pen.ptlCur      = pptl[0];
}

/***************************************************************************
 *
 * FUNCTION NAME = ps_sync_cp
 *
 * DESCRIPTION   = The driver calls this routine to change the logical pen
 *                 coordinates.
 *
 * INPUT         = PDDC    pddc  - Ptr to the DC instance data
 *                 PPOINTL pptl  - The point to moveto
 *                 BOOL    fDraw - Update CP if no drawing occurred
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/


void ps_sync_cp( PDDC pddc, PPOINTL pptl, BOOL fDraw )
  /*
  ** PDDC pddc;              Ptr to the DC instance data
  ** PPOINTL pptl;           The point to moveto
  ** BOOL    fDraw;          Update CP if no drawing occurred
  */
{
  /*
  **  change from FALSE b790611
  */
  /*
  **  change from FALSE
  */
  pddc->pddcb->cgs.fUpdateCP = !fDraw;
  pddc->pddcb->cgs.fValidCP = TRUE;
  pddc->pddcb->pen.ptlCur = pptl[0];
}


/***************************************************************************
 *
 * FUNCTION NAME = ps_lineto
 *
 * DESCRIPTION   = The driver calls this routine to draw a line from the
 *                 currentpoint to the new endpoint.
 *
 * INPUT         =
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

void ps_lineto( PDDC pddc, PPOINTL pptl )
  /*
  ** PDDC pddc;              Ptr to the DC instance data
  ** PPOINTL pptl;           Ptr to the coordinate to draw the line to
  */
{
  PrintChannel( pddc, (PSZ) "%ld %ld l\n", pptl->x, pptl->y );

  /*
  ** Update the new pen position
  */
  pddc->pddcb->pen.ptlCur = pptl[0];
}

#if 0
///***************************************************************************
// *
// * FUNCTION NAME = ps_curveto
// *
// * DESCRIPTION   = The driver calls this routine to draw a bezier curve.
// *
// * INPUT         = PDDC    pddc - Ptr to the DC instance data
// *                 PPOINTL pptl - Ptr to the 2 control points and endpoint
// *
// *
// *
// * OUTPUT        = VOID
// *
// * RETURN-NORMAL = NONE
// * RETURN-ERROR  = NONE
// *
// **************************************************************************/
//
//void ps_curveto ( PDDC pddc, PPOINTL pptl )
//  /*
//  ** PDDC pddc;          Ptr to the DC instance data
//  ** PPOINTL pptl;       Ptr to the 2 control points and endpoint
//  */
//{
//  PrintChannel( pddc, (PSZ) "%ld %ld %ld %ld %ld %ld c\n",
//                pptl[0].x, pptl[0].y,
//                pptl[1].x, pptl[1].y,
//                pptl[2].x, pptl[2].y );
//
//  /*
//  **  Set the current position to the endpoint of the curve
//  */
//  pddc->pddcb->pen.ptlCur = pptl[0];
//}
#endif

/***************************************************************************
 *
 * FUNCTION NAME = ps_strokecurrentpath
 *
 * DESCRIPTION   = strokes the current path using the current pen attributes.
 *
 * INPUT         =
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/


void ps_strokecurrentpath( PDDC pddc )
{
  /*
  ** clear the path if we have an invisible line or the mix mode is
  ** LEAVEALONE.
  */
  if (pddc->pddcb->pen.usType == LINETYPE_INVISIBLE ||
      pddc->pddcb->pen.usFgMix == FM_LEAVEALONE)
  {
    ps_clearpath( pddc );
  }
  else
  {
    /*
    **  if a path does not exist, we don't want to output anything.
    */
    if (pddc->pddcb->cgs.fPathExists)
    {
      /*
      **  make sure all of the line attributes are current.
      */
      ps_setlinewidth(pddc, pddc->pddcb->pen.fxWidth, FALSE);
      ps_setlinejoin(pddc, aiLineJoin[pddc->pddcb->pen.usJoin]);
      ps_setlinecap(pddc, aiLineCap[pddc->pddcb->pen.usEnd]);
      ps_setdash(pddc, pddc->pddcb->pen.usType);
      prdl_PenGray(pddc);

      if (pddc->pddcb->lPathBufSize != pddc->pddcb->lPathBufCount)
      {
        play_path_buf( pddc,FALSE );
      }

      PrintChannel( pddc, (PSZ)"s\n" );
    }
  }

  /*
  ** Indicate path no longer exists.
  */
  pddc->pddcb->cgs.fPathExists = FALSE;
  pddc->pddcb->cgs.fCurvesInPath = FALSE;
}

/***************************************************************************
 *
 * FUNCTION NAME = ps_stroke
 *
 * DESCRIPTION   = The driver calls this routine to stroke the current path
 *                 using the current pen attributes.
 *
 * INPUT         = PDDC pddc - Ptr to the DC instance data
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

void ps_stroke( PDDC pddc )
  /*
  **  PDDC pddc - Ptr to the DC instance data
  */
{
  if (pddc->pddcb->cgs.fPathExists)
  {
    if (pddc->pddcb->lPathBufSize != pddc->pddcb->lPathBufCount)
    {
      play_path_buf(pddc,FALSE);
    }

    PrintChannel(pddc, (PSZ) "s\n");
  }

  /*
  ** Indicate path no longer exists.
  */
  pddc->pddcb->cgs.fPathExists = FALSE;
  pddc->pddcb->cgs.fCurvesInPath = FALSE;
}

/***************************************************************************
 *
 * FUNCTION NAME = ps_clearpath
 *
 * DESCRIPTION   = destroys the current path by outputting a newpath command
 *                 to the printer, and setting a flag in the ddc.
 *
 * INPUT         = PDDC
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *                                                                    
 *
 **************************************************************************/


void ps_clearpath( PDDC pddc )
{
  /*
  **  if no path exists, do nothing.  if a path does exists,
  **  output a newpath to the printer, and set the flag.
  */
  if (pddc->pddcb->cgs.fPathExists)
  {
    PrintChannel( pddc, (PSZ)"n\n" );
    pddc->pddcb->cgs.fPathExists = FALSE;
    pddc->pddcb->cgs.fCurvesInPath = FALSE;
  }
}

/***************************************************************************
 *
 * FUNCTION NAME = ps_newpath
 *
 * DESCRIPTION   = The driver calls this routine to destroy the current
 *                 path if it exists.
 *
 * INPUT         =
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

void ps_newpath( PDDC pddc )
{
  if (pddc->pddcb->cgs.fPathExists)
  {
    PrintChannel( pddc, (PSZ) "n\n" );
  }

  /*
  **  Indicate that there is an empty path
  */
  pddc->pddcb->cgs.fPathExists = FALSE;
  pddc->pddcb->cgs.fCurvesInPath = FALSE;
}

/***************************************************************************
 *
 * FUNCTION NAME = ps_closepath
 *
 * DESCRIPTION   = The driver calls this routine to close the current path.
 *
 * INPUT         = PDDC pddc
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

void ps_closepath( PDDC pddc )
{
  if (pddc->pddcb->cgs.fPathExists )
  {
    PrintChannel( pddc, (PSZ) "cp\n" );
  }
}

#if 0
///***************************************************************************
// *
// * FUNCTION NAME = ps_strokepath
// *
// * DESCRIPTION   = The driver calls this routine to convert the current
// *                 path outline into a path and then stroke the new path
// *                 that was created.
// *
// * INPUT         = PDDC pddc;
// *
// * OUTPUT        = VOID
// *
// * RETURN-NORMAL = NONE
// * RETURN-ERROR  = NONE
// *
// ****************************************************************************/
//
//void ps_strokepath( PDDC pddc )
//{
//  if (pddc->pddcb->cgs.fPathExists)
//  {
//    PrintChannel( pddc, (PSZ) "strokepath\n" );
//
//    /*
//    ** The closepath operator is invalid after a strokepath
//    */
//    /*
//    **  indicate path no longer exists.
//    */
//    pddc->pddcb->cgs.fPathExists = FALSE;
//    pddc->pddcb->cgs.fCurvesInPath = FALSE;
//  }
//}
#endif

/***************************************************************************
 *
 * FUNCTION NAME = ps_fill
 *
 * DESCRIPTION   = The driver calls this routine to fill the current path
 *                 using the winding number rule.  The current path is
 *                 automatically closed if it hasn't been done explicitly.
 *
 * INPUT         = PDDC pddc;
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

void ps_fill( PDDC pddc )
{
  if (pddc->pddcb->cgs.fPathExists)
  {
    if (pddc->pddcb->lPathBufSize != pddc->pddcb->lPathBufCount)
    {
      play_path_buf( pddc, FALSE );
    }

    /*
    **  Output the PostScript operator to do an even/odd mode fill
    */
    PrintChannel( pddc, (PSZ) "f\n" );

    /*
    **  The fill operator deletes the current path.
    */
    /*
    **  indicate path no longer exists.
    */
    pddc->pddcb->cgs.fPathExists = FALSE;
    pddc->pddcb->cgs.fCurvesInPath = FALSE;
  }
}

#if 0
///***************************************************************************
// *
// * FUNCTION NAME = ps_eofill
// *
// * DESCRIPTION   = The driver calls this routine to fill the current path
// *                 using the even-odd rule.  The current path is automatically
// *                 closed if it hasn't been done explicitly.
// *
// * INPUT         = PDDC
// *
// * OUTPUT        = VOID
// *
// * RETURN-NORMAL = NONE
// * RETURN-ERROR  = NONE
// *
// **************************************************************************/
//
//
//void ps_eofill( PDDC pddc )
//{
//  if (pddc->pddcb->cgs.fPathExists)
//  {
//    if (pddc->pddcb->lPathBufSize != pddc->pddcb->lPathBufCount)
//    {
//      play_path_buf( pddc, FALSE );
//    }
//
//    /*
//    **  Output the PostScript operator to do an even/odd mode fill
//    */
//    PrintChannel( pddc, (PSZ) "e\n" );
//
//    /*
//    **  The fill operator deletes the current path.
//    */
//    /*
//    **  indicate path no longer exists.
//    */
//    pddc->pddcb->cgs.fPathExists = FALSE;
//    pddc->pddcb->cgs.fCurvesInPath = FALSE;
//  }
//}
//
#endif

/***************************************************************************
 *
 * FUNCTION NAME = ps_setfont
 *
 * DESCRIPTION   = The driver calls this routine to select in the a new font
 *                 name.
 *
 * INPUT         = PDDC pddc
 *                 PSZ pszFont
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

void ps_setfont( PDDC pddc, PSZ pszFont )
{
  if (szIsEqual(pddc->pddcb->cgs.szFont,pszFont))
  {
    return;
  }

  utl_memcopy( pddc->pddcb->cgs.szFont, pszFont, sizeof(pddc->pddcb->cgs.szFont) );

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

/***************************************************************************
 *
 * FUNCTION NAME = ps_SetDownloadedFont
 *
 * DESCRIPTION   = The driver calls this routine to select in a new font
 *                 which we created and downloaded to the Postscript printer
 *                 previously.
 *
 * INPUT         =
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *                                                                    
 *
 **************************************************************************/


void ps_SetDownloadedFont( PDDC pddc )

{
  pddc->pddcb->cgs.fValidFont = FALSE;
  prda_CheckFontResource( pddc );  /*            make sure font is available */
  DoSelectFont( pddc );
}

/***************************************************************************
 *
 * FUNCTION NAME = ps_show
 *
 * DESCRIPTION   = The driver calls this routine to output text in the
 *                 current font.
 *
 * INPUT         = PDDC   pddc               - Ptr to the DC instance data
 *                 LONG   lNumBytes          - How many bytes to draw
 *                 SHORT usEnableawidthshow - Zero inhibits use of awidthshow
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

void ps_show( PDDC pddc, LONG lNumBytes, SHORT usEnableawidthshow )
  /*
  **  PDDC pddc -                 Ptr to the DC instance data
  **  LONG lNumBytes -            How many bytes to draw
  **  SHORT usEnableawidthshow - Zero inhibits use of awidthshow
  */
{
  LONG i;
  BOOL bUseawidthshow;

  /*
  ** Use awidthshow only if the upper layer says its okay and we are
  ** currently not in a path and the application has specified extra
  ** spacing parameters.
  */
  if ( bUseawidthshow = usEnableawidthshow && (!pddc->pddcb->path.fPathIsOpen) &&
                      (pddc->pddcb->text.ChrBundle.fxExtra || pddc->pddcb->text.ChrBundle.fxBreakExtra) )
  {
    /*
    ** If we are going to use awidthshow, then output the numeric
    ** parameters before the string.
    */

    PrintChannel( pddc, (PSZ) "%f %f %d %f %f ",
              frmul( pddc->pddcb->text.fxCos, pddc->pddcb->text.ChrBundle.fxBreakExtra ),
              frmul( pddc->pddcb->text.fxSin, pddc->pddcb->text.ChrBundle.fxBreakExtra ),
              pddc->pddcb->text.pfmFontMetrics->sFirstChar +
                pddc->pddcb->text.pfmFontMetrics->sBreakChar,
              frmul( pddc->pddcb->text.fxCos, pddc->pddcb->text.ChrBundle.fxExtra ),
              frmul( pddc->pddcb->text.fxSin, pddc->pddcb->text.ChrBundle.fxExtra ) );
  }

  /*
  ** Output the actual string to be printed.
  */
  PrintChannel( pddc, (PSZ) "(" );
  for (i=0; i<lNumBytes; i++)
  {
    PrintChannel( pddc, (PSZ) "%c", pddc->pdv->chText[i] );
  }

  /*
  **  Now tell PostScript what to do with the string. If we are using
  **  awidthshow, then perform the AS macro.  Otherwise, if we are in
  **  a path, then do a true charpath.  Otherwise, do a show.
  */
  if (bUseawidthshow)
  {
    PrintChannel( pddc, (PSZ) ") AS\n" );
  }
  else if (pddc->pddcb->path.fPathIsOpen)
  {
    PrintChannel( pddc, (PSZ) ") false charpath\n" );
  }
  /*
  ** fIntCharWidth is set if DEVESC_INTCHARWIDTH has set it.  In this
  ** case, we want to use ishow instead of show.
  */
  else if (pddc->fIntCharWidth)
  {
    PrintChannel( pddc, (PSZ) ") u\n" );
  }
  else
  {
    PrintChannel( pddc, (PSZ) ") t\n" );
  }
  pddc->pddcb->cgs.fValidCP = FALSE;
}

/***************************************************************************
 *
 * FUNCTION NAME = ps_showpage
 *
 * DESCRIPTION   = The driver calls this routine to eject a page from the
 *                 printer.
 *
 * INPUT         = PDDC pddc
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

void ps_showpage( PDDC pddc )

{
  PrintChannel( pddc, (PSZ) "eject\n" );

  /*
  ** Flush the channel and mark the output channel as unused so
  ** that if an enddoc without additional output then we can prevent
  ** a blank page from being ejected.
  */
  FlushChannel( pddc );
  pddc->pdv->cn.fUsed = FALSE;

  init_cgs( pddc );
}

/***************************************************************************
 *
 * FUNCTION NAME = ps_status
 *
 * DESCRIPTION   = Returns either SUCCESS or FAILURE depending on whether
 *                 an error has occurred on the I/O channel or not
 *
 * INPUT         = PDDC pddc
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

BOOL ps_status( PDDC pddc )
{
  ASSERT( pddc );
  ASSERT( pddc->pdv );

  if (pddc->pdv->cn.fChannelError)
  {
    PrintLog( (PSZ) "ps_status: Error on I/O channel\n" );
    GplErrSetError(  PMERR_DOSOPEN_FAILURE );
    return( FAILURE );
  }

  PrintLog( (PSZ) "ps_status: Success (no error on channel)\n" );

  return( SUCCESS );
}

/***************************************************************************
 *
 * FUNCTION NAME = ps_gsave
 *
 * DESCRIPTION   = The driver calls this routine to save the current graphics
 *                 state.
 *
 *                 Copies only up to the first null byte.
 *
 * INPUT         =
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

void ps_gsave( PDDC pddc )
{
  /*
  **  an error condition should be raised if the grestore stack
  **  is overflowed
  */
  if (pddc->usgsStack > 0)
  {
    --pddc->usgsStack;
    utl_memcopy( (PSZ) &pddc->agsStack[pddc->usgsStack],
                 (PSZ) &pddc->pddcb->cgs, sizeof(CGS) );
  }
  else
  {
    RIP( (PSZ)"ps_gsave:  too many gsaves overran the stack." );
  }

  PrintChannel( pddc, (PSZ) "gs\n" );
}

/***************************************************************************
 *
 * FUNCTION NAME = ps_restore
 *
 * DESCRIPTION   = The driver calls this routine to restore the current
 *                 graphics state from the gsave stack.
 *
 * INPUT         = PDDC pddc
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

void ps_grestore( PDDC pddc )
{
  /*
  **  to do this correctly, we should actually restore the
  **  entire state from a stack.  The state should also be
  **  saved by the ps_gsave call.
  */
  if (pddc->usgsStack < IGSMAX)
  {
    utl_memcopy( (PSZ) &pddc->pddcb->cgs,
                 (PSZ) &pddc->agsStack[pddc->usgsStack++],
                 sizeof(CGS));
  }

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

  PrintChannel( pddc, (PSZ) "gr\n" );
}

/***************************************************************************
 *
 * FUNCTION NAME = ps_savematrix
 *
 * DESCRIPTION   = Save the current transformation matrix so that it can
 *                 be restored later.
 *
 * INPUT         = PDDC pddc
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/


void ps_savematrix( PDDC pddc )
{
  PrintChannel( pddc, (PSZ) "/ms%d mx cm def\n",
                pddc->pddcb->usMatrixIndex );
  pddc->pddcb->usMatrixIndex ++;
}

/***************************************************************************
 *
 * FUNCTION NAME = ps_restorematrix
 *
 * DESCRIPTION   = Restore the previously saved transformation matrix.
 *
 * INPUT         = PDDC pddc
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

void ps_restorematrix( PDDC pddc )
{
  pddc->pddcb->usMatrixIndex--;
  PrintChannel( pddc, (PSZ) "ms%d SM\n",
                pddc->pddcb->usMatrixIndex );

  pddc->pddcb->cgs.fValidCP=FALSE;

  /*
  ** The CTM is modified, hence set the cosmetic line width.
  **                                                                
  */
  ps_CosmeticLine( pddc );  /* Set cosmetic width */
}

/***************************************************************************
 *
 * FUNCTION NAME = ps_saveclipmatrix
 *
 * DESCRIPTION   = Save the current transformation matrix so that it can be
 *                 restored later.
 *
 * INPUT         = PDDC pddc
 *
 *
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

void ps_saveclipmatrix( PDDC pddc )
{
  PrintChannel( pddc, (PSZ) "scm\n" );
}

/***************************************************************************
 *
 * FUNCTION NAME = ps_restoreclipmatrix
 *
 * DESCRIPTION   = Restore the previously saved transformation matrix.
 *
 * INPUT         = PDDC pddc
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

void ps_restoreclipmatrix( PDDC pddc )
{
  PrintChannel( pddc, (PSZ) "msclp SM\n" );
  pddc->pddcb->cgs.fValidCP = FALSE;
}

#if 0
///***************************************************************************
// *
// * FUNCTION NAME = ps_concat
// *
// * DESCRIPTION   = Concatenate a matrix with the current transformation
// *                 matrix.
// *
// * INPUT         = PDDC pddc
// *                 XFORM *pfxMatrix;
// *
// *
// *
// * OUTPUT        = VOID
// *
// * RETURN-NORMAL = NONE
// * RETURN-ERROR  = NONE
// *
// **************************************************************************/
//
//void ps_concat( PDDC pddc, XFORM *pfxMatrix )
//{
//  MatrixToChannel( pddc, pfxMatrix );
//  PrintChannel( pddc, (PSZ) " concat\n" );
//
//  pddc->pddcb->cgs.fValidCP = FALSE;
//}
#endif

/***************************************************************************
 *
 * FUNCTION NAME = ps_setmatrix
 *
 * DESCRIPTION   = Set the current transformation matrix.
 *
 * INPUT         = PDDC pddc;
 *                 FIXED *pfxMatrix;
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

void ps_setmatrix( PDDC pddc, FIXED *pfxMatrix )
{
  /*
  ** Note that only the first four components of the translation
  ** matrix are fixed point.  The translation components are
  ** ordinary integers.
  */
  PrintLog( (PSZ) "ps_SM()\n" );

  /*
  ** Send a new transformation matrix if it is different from the
  ** current one.
  */
  if (!utl_MemIsEqual( (PB) &pddc->pddcb->cgs.xformCTM, (PB) &pfxMatrix,
                           sizeof(pddc->pddcb->cgs.xformCTM) ))
  {
    utl_memcopy( (PSZ) &pddc->pddcb->cgs.xformCTM, (PSZ) pfxMatrix, sizeof(pddc->pddcb->cgs.xformCTM) );

    MatrixToChannel( pddc, (XFORM *)pfxMatrix );
    PrintChannel( pddc, (PSZ) " setmx\n" );

    pddc->pddcb->cgs.fValidCP=FALSE;

    /*
    ** The CTM is modified, hence set the cosmetic line width.
    **                                                            
    */
    ps_CosmeticLine( pddc );    /* Set cosmetic width */

    /*
    ** If there is a dashed line attribute then we need to re-issue it
    */
    switch ( pddc->pddcb->cgs.usLineType )
    {
      case 0: /* these are solid - don't need to reissue */
      case 7:
      case 8:
        break;

      default:
        ps_setdashnow( pddc );
    }
  }
}

#if 0
///***************************************************************************
// *
// * FUNCTION NAME = ps_translate
// *
// * DESCRIPTION   = Do a coordinate system translation by dx, dy
// *
// * INPUT         = PDDC pddc;
// *                 PPOINTL pptl;
// *
// * OUTPUT        = VOID
// *
// * RETURN-NORMAL = NONE
// * RETURN-ERROR  = NONE
// *
// **************************************************************************/
//void ps_translate( PDDC pddc, PPOINTL pptl )
//{
//  PrintChannel( pddc, (PSZ) "%ld %ld translate\n", pptl->x, pptl->y );
//
//  pddc->pddcb->cgs.fValidCP = FALSE;
//}
#endif

/***************************************************************************
 *
 * FUNCTION NAME = ps_clip
 *
 * DESCRIPTION   = Intersect the current path with the current clipping path
 *                 and make this the new clipping path. The winding number
 *                 rule is used to determine the area clipped.
 *
 * INPUT         =
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

void  ps_clip( PDDC pddc )
{
  PrintChannel( pddc, (PSZ) "clip n\n" );
  pddc->pddcb->cgs.fPathExists = FALSE;  /* indicate path no longer exists. */
  pddc->pddcb->cgs.fCurvesInPath = FALSE;
}

#if 0
///***************************************************************************
// *
// * FUNCTION NAME = ps_eoclip
// *
// * DESCRIPTION   = Intersect the current path with the current clipping path
// *                 and make this the new clipping path. The even/odd rule is
// *                 used to determine the area clipped.
// *
// * INPUT         =
// *
// * OUTPUT        = VOID
// *
// * RETURN-NORMAL = NONE
// * RETURN-ERROR  = NONE
// *
// **************************************************************************/
//
//void ps_eoclip( PDDC pddc )
//{
//  PrintChannel( pddc, (PSZ) "eoclip n\n" );
//  pddc->pddcb->cgs.fPathExists = FALSE;  /* indicate path no longer exists. */
//  pddc->pddcb->cgs.fCurvesInPath = FALSE;
//}
#endif

/***************************************************************************
 *
 * FUNCTION NAME = ps_imagedata
 *
 * DESCRIPTION   = Output a scanline of image data.
 *
 * INPUT         = PDDC pddc     - Ptr to the DC instance data
 *                 BOOL fInvert  - TRUE if the image should be inverted
 *                 LONG ix       - The horizontal starting position
 *                 LONG iy       - The vertical starting position
 *                 LONG nxBits   - The number of bits horizontally
 *                 PB   pbBits   - Ptr to the image bits
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

void ps_imagedata( PDDC pddc, BOOL fInvert, LONG ix, LONG iy,
                   LONG nxBits, PB pbBits )
/*
** PDDC pddc;              Ptr to the DC instance data
** BOOL fInvert;           TRUE if the image should be inverted
** LONG ix;                The horizontal starting position
** LONG iy;                The vertical starting position
** LONG nxBits;            The number of bits horizontally
** PB pbBits;              Ptr to the image bits
*/
{
  short int nbRow;
  short int i;

  /*
  **  This could be done more efficiently, but it's image data.
  */
  PrintChannel( pddc, (PSZ) "%ld %ld %ld  %b ip\n", nxBits,
                ix,iy,!fInvert );

  /*
  ** Calculate the number of bytes in a row of the image
  */
  nbRow = (((short int) nxBits) + 7) >> 3;

  PrintChannel( pddc, (PSZ) "{<" );

  for (i = 0; i < nbRow; ++i)
  {
    PrintChannel( pddc, (PSZ) "%02x", *pbBits++ & 0x0ff );
  }

  PrintChannel( pddc, (PSZ) ">} id\n" );
}


/*****************************************************************************\
** @V3.0GAMMA1
** New function to apply Gamma
**
** GammaAdjust applies the gamma correction to the color passed in
**
\*****************************************************************************/

LONG _Optlink GammaAdjust( PBYTE pbGammaTable,  LONG lColor )
{
  LONG lRed, lGreen, lBlue;

  /* If no table just return color */
  if ( ! pbGammaTable )
  {
    return lColor;
  }

  lBlue =  pbGammaTable[ ( lColor & 0x000000ff )         + BLUE_OFFSET ];
  lGreen = pbGammaTable[ (( lColor & 0x0000ff00 ) >> 8)  + GREEN_OFFSET ];
  lRed   = pbGammaTable[ (( lColor & 0x00ff0000 ) >> 16) + RED_OFFSET ];

  return (lRed << 16) | (lGreen << 8) | lBlue;

}



/***************************************************************************
 *
 * FUNCTION NAME = ps_setrgbcolor
 *
 * DESCRIPTION   = This routine is called by the driver to set the current
 *                 color used to draw lines, fill, etc.  For non-color
 *                 printers, its implemented by converting the RGB color
 *                 triplet to a gray-level between 0.0 (black) and 1.0 (white).
 *
 * INPUT         = PDDC pddc
 *                 LONG lColor
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

void ps_setrgbcolor( PDDC pddc, LONG lColor )
{
  FIXED fxRed, fxGreen, fxBlue, fxGray, fxTmp;

  /* @V3.0GAMMA1
  ** Apply Gamma
  */
  lColor = GammaAdjust( pddc->pdv->pbGammaTable, lColor );

  if (pddc->pddcb->cgs.lColor != lColor)
  {
    if (pddc->pdv->jobProperties.fIsColorDevice)
    {
      pddc->pddcb->cgs.lColor = lColor;

      fxTmp = (lColor & 0x000000ff) << 16;
      fxBlue = frdiv(fxTmp, FIXED_255);
      fxTmp = (lColor & 0x0000ff00) << 8;
      fxGreen = frdiv(fxTmp, FIXED_255);
      fxTmp = (lColor & 0x00ff0000);
      fxRed = frdiv(fxTmp, FIXED_255);

      if ((fxRed == fxGreen) && (fxRed == fxBlue))
      {
        PrintChannel( pddc, (PSZ) "%f g\n", fxRed );
      }
      else
      {
        PrintChannel( pddc, (PSZ) "%f %f %f r\n",
                      fxRed, fxGreen, fxBlue);
      }
    }
    else
    {
      pddc->pddcb->cgs.lColor = lColor;
      fxGray = prdc_RgbToGray(pddc, lColor);
      PrintChannel(pddc, (PSZ) "%f g\n", fxGray);
    }
  }
}

/***************************************************************************
 *
 * FUNCTION NAME = ps_setdashnow
 *
 * DESCRIPTION   = Sets dash to the currently defined style.
 *
 * INPUT         = PDDC
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

void ps_setdashnow( PDDC pddc )
{
  SHORT *piDash;

  piDash = apiDash[pddc->pddcb->cgs.usLineType] ;
  PrintChannel( pddc, (PSZ) "[" );
  while (*piDash)
  {
    PrintChannel( pddc, (PSZ) "sd %d mul ", *piDash++ );
  }
  PrintChannel( pddc, (PSZ) "] 0 d\n" );
}

/***************************************************************************
 *
 * FUNCTION NAME = ps_setdash
 *
 * DESCRIPTION   = This routine sets the line-type style corresponding to
 *                 a solid or one of the various dashed line styles.
 *
 * INPUT         = PDDC   pddc       - Ptr to the DC instance data
 *                 SHORT usLineType - The engine's linetype value
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

void ps_setdash ( PDDC pddc, SHORT usLineType )
  /*
  ** PDDC pddc;              Ptr to the DC instance data
  ** SHORT usLineType;      The engine's linetype value
  */
{
  if (usLineType != pddc->pddcb->cgs.usLineType)
  {
      pddc->pddcb->cgs.usLineType = usLineType;
      ps_setdashnow( pddc );
  }
}

#if 0
///***************************************************************************
// *
// * FUNCTION NAME = ps_arc
// *
// * DESCRIPTION   = This is the low level routine called by PartialArc to
// *                 tell postscript to draw the arc.  This routine transforms
// *                 the arc by the arc parameters, and restores the current
// *                 transformation when it is done.
// *
// * INPUT         = PDDC        pddc
// *                 PPOINTL     pptl
// *                 FIXED       fxRadius
// *                 FIXED       fxStart
// *                 FIXED       fxEnd
// *
// * OUTPUT        = VOID
// *
// * RETURN-NORMAL = NONE
// * RETURN-ERROR  = NONE
// *                                                                    
// *
// **************************************************************************/
//
//void ps_arc( PDDC pddc, PPOINTL pptl, FIXED fxRadius,
//             FIXED fxStart, FIXED fxEnd )
//{
//  pddc->pddcb->cgs.fPathExists = TRUE;
//  PrintChannel( pddc, (PSZ)"%ld %ld %f %f %f arc\n",
//                pptl->x, pptl->y, fxRadius, fxStart, fxEnd );
//}
//
///***************************************************************************
// *
// * FUNCTION NAME = ps_arcn
// *
// * DESCRIPTION   =
// *
// * INPUT         = PDDC    pddc
// *                 PPOINTL pptl
// *                 FIXED   fxRadius
// *                 FIXED   fxStart
// *                 FIXED   fxEnd
// *
// * OUTPUT        = VOID
// *
// * RETURN-NORMAL = NONE
// * RETURN-ERROR  = NONE
// *
// **************************************************************************/
//
//void ps_arcn( PDDC pddc, PPOINTL pptl, FIXED fxRadius,
//              FIXED fxStart, FIXED fxEnd )
//{
//  pddc->pddcb->cgs.fPathExists = TRUE;
//  PrintChannel( pddc, (PSZ)"%ld %ld %f %f %f arcn\n",
//                pptl->x, pptl->y, fxRadius, fxStart, fxEnd );
//}
#endif

/*
** ------------------ Tray functions ---------------------
*/

/***************************************************************************
 *
 * FUNCTION NAME = GetDuplexCommand
 *
 * DESCRIPTION   = Gets the proper duplex command from the ppb and copies
 *                 it to the supplied buffer.
 *
 * INPUT         = PB      pbDuplexCommand
 *                 PB *apResources
 *                 SHORT   sDuplexMode
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/
/*
**           
** Since the duplex command is now a pointer, modify the parameters to
** reflect this change.
*/
VOID GetDuplexCommand( PSZ *pbDuplexCommand, PB *apResources, PDDC pddc )
{
  SHORT    j;        /*scratch index variables */
  PDESPPD  pdesPpd;   /*pointer to printer descriptor segment*/

  pdesPpd = (PDESPPD)apResources[PPDRES];

  switch ( pddc->pdv->sDuplexMode )
  {
  case DUPLEX_FALSE:                 /* Turn off duplexing */
       j = pdesPpd->desItems.ofsDuplexFalse;
       break;

  case DUPLEX_DUPLEXNOTUMBLE:        /* Turn on just duplexing */
       j = pdesPpd->desItems.ofsDuplexNoTumble;
       break;

  case DUPLEX_DUPLEXTUMBLE:          /* Turn on duplex and tumble */
       j = pdesPpd->desItems.ofsDuplexTumble;
       break;

  case DUPLEX_NONE:                    /* If duplex not supported    */
       if (j = pdesPpd->desItems.ofsDuplexFalse )  /* if there is NO false */
       break;                                      /* fall into default case */

  default:                             /* or unknown set first       */
       *pbDuplexCommand = 0;           /* character of buffer to 0   */
       return;                         /* Just return no copy needed */
  }

  /*
  **  Copy over the command string
  */
  /*
  **           
  */
  *pbDuplexCommand = NULL;
  if ((*pbDuplexCommand = (PSZ) GplMemoryAlloc( pddc->pdv->pDCHeap,
                              strlen( apResources [ RESBUF ] + j ) + 1 )) != 0)
  {
    strcpy( *pbDuplexCommand, apResources[ RESBUF ] + j );
  }

#if 0
//  k = *((CHAR *) apResources[RESBUF] + j);
//
//  for ( l = 0; l < k; l++ )
//  {
//    *(pbDuplexCommand+l) = *((CHAR *)apResources[RESBUF] + j + l + 1);
//  }
//
//  *(pbDuplexCommand + l) = 0;   /* Term string */
#endif
}

/***************************************************************************
 *
 * FUNCTION NAME = LoadTrayCommand
 *
 * DESCRIPTION   = This routine returns pointers to currently selected
 *                 paper dimns.The currently selected paper is indicated by
 *                 pddc -the input argument.The additional parameters are
 *                 pointers to paper command buffer,Input tray command buffer
 *                 and output tray command buffer.
 *                 If successful the routine returns TRUE else FALSE.
 *
 * INPUT         = PDDC       pddc          - pointer to device context info
 *                 PB  pbPaperCommand       - pointer to paper command buffer
 *                 PB  pbInpCommand         - pointer to input tray command
 *                                             buffer
 *                 BOOL * pblManualWait - pointer to ManualWait flag
 *                 PB  pbDuplexCommand      - Pointer to Duplex command
 *                 PBOOL pfIsColorDevice
 *                 PSZ pResBuffer - Ponter to the Postscript Resolution
 *                     buffer.
 *                 pIsJCLResolution - Flag, if the resolution is part of the
 *                   job control language (JCL), then this is TRUE.  Default
 *                   is FALSE.
 *                 pJCLToPS - If pIsJCLResolution is TRUE, then this contains
 *                   the JCL-to-PS interpreter language.  Otherwise, a NULL
 *                   string is returned.
 *
 * OUTPUT        = BOOL
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/
/*
**           
** Now that most of the PostScript command buffers have been changed from
** arrays to pointers, modify the parameters to reflect this change.
*/
/*
** @V3.0115171
** Add new parameters for JCL Resolution
*/
BOOL LoadTrayCommand ( PDDC pddc, PSZ *pbPaperCommand, PSZ *pbInpCommand,
                       BOOL *pblManualWait, PSZ *pbDuplexCommand,
                       PBOOL pfIsColorDevice, PSZ *pResBuffer,
                       PBOOL pIsJCLResolution, PBYTE pJCLToPS )

  /*
  **  PDDC       pddc;               pointer to device context info
  **  PB  pbPaperCommand;            pointer to paper command buffer
  **  PB  pbInpCommand;              pointer to input tray command buffer
  **  BOOL * pblManualWait;      pointer to ManualWait flag
  **  PB  pbDuplexCommand;           Pointer to Duplex command
  **  PBOOL pfIsColorDevice;
  */
  /*
  **  PB  pbDuplexCommand;           Pointer to Duplex command DCR 1462
  */
{
    unsigned  i;
    PCNFDATA  pcnfData;   /* pointer to printer configuration data */
    PDESPPD   pdesPpd;    /* pointer to printer descriptor segment */
    CHAR      szPaper[MAX_PSIZE];   /* to contain current paper name */
/*  CHAR      szDefPaper[MAX_PSIZE];   // to contain default paper name */
    SHORT     shTrayIndex ;
    PB        apResources[IMAXRES+1];   /* array of pointers to printer resources */
              /*
              **  array of pointer to following resources:-
              **  ppb file header ,
              **  directory buffer ,
              **  itemsbuffer ,
              **  configuration buffer ,
              **  ppd parameter buffer.
              */
    PUCHAR    pTempResBuffer; /*            */
    USHORT    uCounter;       /*            */
    USHORT    uResValue = 0;  /*            */
    USHORT    k;              /*            */

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

    *pblManualWait =FALSE;

    /*
    ** Allocate space to store the information specific to a given
    ** printer configuration parameters.
    */
    if(!(apResources[CNFRES] = (PCHAR) GplMemoryAlloc( pProcessHeap,
                                                            sizeof(CNFDATA) )))
    {
      PrintLog( (PSZ) "LoadTrayCommand: can't allocate SIGNATURE instance\n");
      return( FALSE );
    }
    pcnfData = (PCNFDATA)apResources[CNFRES];

    /*
    ** initialize the information in pcnfData from pddc
    */
    szCopy((PSZ)pcnfData->szSegName ,(PSZ)pddc->pdv->szSegName,sizeof(pcnfData->szSegName)) ;
    pcnfData->u.iv.pSourcePaper = &pddc->pdv->sourcePaper ;
    pcnfData->effOutput = pddc->pdv->effOutput  ;
    pcnfData->jobProperties = pddc->pdv->jobProperties;
    pcnfData->iCntCopies = pddc->pdv->iCntCopies ;
    pcnfData->iDestnType = pddc->pdv->iDestnType ;
    szCopy((PSZ)pcnfData->szDestnFile,(PSZ)pddc->pdv->szDestnFile,sizeof(pcnfData->szDestnFile));

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

    /*
    **  Read the Printer info segment from Resources appended.
    */
    pcnfData->lGetPtr = 0 ;
    if (!LoadInfoSegment( (PB *)apResources) )
    {
      return( FALSE );
    }

    /*
    **  store the paper name in array which corresponds to the current page
    */
    PageAtPaper( (PCNFDATA)pcnfData ,(PSZ)szPaper ,(SHORT *)&shTrayIndex );

    pdesPpd = (PDESPPD) apResources[PPDRES];

    /*
    ** @V3.0115171
    ** Assign the JCL-to-PS command to the return pointer.
    */
    *pJCLToPS = 0;
    if (pdesPpd->desItems.ofsJCLToPS != -1)
    {
      strcpy( pJCLToPS, apResources[ RESBUF ] +
                        pdesPpd->desItems.ofsJCLToPS );
    }

#if 0     /*            */
//  /*
//  **  Read the default paper name into a scratch buffer,For default paper
//  **  name we don't output any command if that is the first page
//  */
//  if (pdesPpd->desPage.ofsDfpgsz != -1)
//  {
//    i= *(apResources[RESBUF] + pdesPpd->desPage.ofsDfpgsz);
//    szCopy( (PSZ)szDefPaper,(PSZ)(apResources[RESBUF] + pdesPpd->desPage.ofsDfpgsz + 1),
//            (i + 1) );
//    *(szDefPaper + i)='\0';
//  }
//
//  if ( szIsEqual((PSZ)szPaper,(PSZ)szDefPaper))
//  {
//    *pbPaperCommand ='\0';
//  }
//  /*
//  **  read the paper select command
//  */
//  else
#endif

    /*
    **           
    */
    if (!CmndPaper( pbPaperCommand, (PB *)apResources, (PSZ) szPaper, pddc ))
    {
      if (*pbPaperCommand != NULL)
      {
        GplMemoryFree( *pbPaperCommand );
      }
    }

    if (pddc->pdv->shPageno > 1)
    {
        /*
        **           
        */
        **pbInpCommand = 0;
    }

    /*
    **  read the input tray select command
    */
    /*
    **           
    */
    CmndInpTray( pbInpCommand, (PB *) apResources, shTrayIndex,
                 pddc );

    /*
    **  DCR 1462 Get the duplex command if needed
    */
    /*
    **  Get the duplex command if needed
    */
    GetDuplexCommand( pbDuplexCommand, (PB *) apResources, pddc );

    /*
    ** @V3.0115171
    ** Assign the resolution JCL flag to the output pointer.
    */
    *pIsJCLResolution = pdesPpd->desItems.ResList.bIsJCLResolution;

    /*           
    ** Set up resolution
    */
    *pResBuffer = '\0';  /* Init buffer */

    /*
    ** Retrieve the resolution buffer and number of available resolutions.
    */
    pTempResBuffer = (PUCHAR)( apResources[RESBUF] +
                               pdesPpd->desItems.ResList.uResOffset );

    /*
    ** Go through the resolution list until a matching
    ** resolution string is found.
    */
    for (uCounter = 0 ; uCounter < pdesPpd->desItems.ResList.uNumOfRes &&
                        uResValue == 0 ; uCounter++)
    {
      uResValue = *(PUSHORT) pTempResBuffer;
      pTempResBuffer += 2;

      /*
      ** If the current string is not the one we're looking
      ** for, go to the next string.  This is done by looking
      ** at the first byte of the string, which contains the
      ** string length.  Increment the pointer by that value
      ** plus 2, two for the end characters, the other for the
      ** string length byte.
      */
      if (uResValue != pddc->pdv->canvas.iRes)
      {
        /*
        ** Reset the temporary resolution value and go to the next string.
        */
        uResValue = 0;

        /*
        **           
        */
#if 0
//      pTempResBuffer += *pTempResBuffer + 1;
#endif
        pTempResBuffer += strlen( pTempResBuffer ) + 1;
      }
      else
      {
        /*
        **           
        */
#if 0
//      /*
//      ** K is the length of the of the string
//      */
//      k = *(PUCHAR)pTempResBuffer++;
//
//      /*
//      ** This is just a formality, but remove any preceeding spaces
//      ** from the current resolution buffer.  This is so that the
//      ** resolution string won't look like it's indented in the
//      ** output file.
//      */
//      while ( *pTempResBuffer == ' ' )
//      {
//        pTempResBuffer++;
//        k--;     /* for each blank we remove one less to print */
//      }
#endif

        /*
        ** Copy the needed string to that buffer.  Make the string
        ** NULL terminated.
        */
        /*
        **           
        ** The reason for the "+ 2" is that the newline character will later
        ** be added to this buffer.
        */
        *pResBuffer = (PSZ) 0;
        if ((*pResBuffer = GplMemoryAlloc( pddc->pdv->pDCHeap,
                                          strlen( pTempResBuffer ) + 2 )) != 0)
        {
          strcpy( *pResBuffer, pTempResBuffer );
        }
#if 0
//      for ( i = 0; i <  k; i++ )
//      {
//        *pResBuffer++ = *pTempResBuffer++;
//      }
//      *pResBuffer = '\0';
#endif

        /*
        ** Since this loop exits when uResValue != 0, let's be extra careful
        ** and set uResValue to a non-zero number, so we're gaurenteed
        ** that this loop will exit.
        */
        uResValue = 0xFF;
      }
    }

    /*
    **  Defect 51584 See if this is a color device - even if user picked B&W
    */
    /*
    **  See if this is a color device - even if user picked B&W
    */
    *pfIsColorDevice = pdesPpd->desItems.fIsColorDevice;

    /*
    **  free the resources allocated
    */
    FreeMemory( (PB *) apResources );

    return( TRUE );
}

/***************************************************************************
 *
 * FUNCTION NAME = ps_CosmeticLine
 *
 * DESCRIPTION   = Writes out the cosmetic line command.  If multiplier
 *                 is one or zero ues the old "wc" command.
 *                 If multiplier  greater than 1 ues the wcx which takes a parm
 *
 *
 * INPUT         = PDDC       pddc          - pointer to device context info
 *
 * OUTPUT        = prints a string
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

VOID _Optlink ps_CosmeticLine( PDDC pddc )
{
  FIXED fxWidth = pddc->pddcb->pen.fxWidth;

  /*
  ** The Cosmetic line width is stored in pddc->pddcb->pen.fxWidth
  */

  /*
  ** Just use wc for multipliers equal or less than 1.0
  */
  if ( (LONG) fxWidth <= 0x00010000 )
  {
    PrintChannel( pddc, (PSZ)"wc\n" );
  }
  else    /* use wcx which has multplier */
  {
    PrintChannel( pddc, (PSZ)"%f wcx\n", fxWidth );
  }
  return;
}
