/*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 = CHARSTR.C
 *
 * DESCRIPTIVE NAME = PRINTER DRIVER SOURCE
 *
 *
 * VERSION = V2.0
 *
 * DATE      08/10/89
 *
 * DESCRIPTION Contains CharStringPos, CharString and
 *             supporting routines.
 *
 * FUNCTIONS
 *
 *             prdt_CharStringPos()
 *             prdt_CharString()
 *             SetTextColors
 *             CharStringDebug
 *             CharStringRect
 *             CharStringKern
 *             CharStringWidthVector
 *             CharStringLeftRight
 *             CharStringRightLeft
 *             CharStringUpDown
 *             CharStringReverse
 *
 *
 *
 *
 *
 *
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/

#include "inc\prdinclt.h"
#include "inc\prdgextf.h"
#include "inc\utl.h"
#include "inc\prdmath.h"
#include "inc\prdtextf.h"
#include "inc\prdlextf.h"
#include "inc\prdcextf.h"              /* for prdc_GetColor   */
#include "inc\pspagtun.h"              /* V2.174057   Page Tuning */
#define  INCL_GENPLIB_ERROR
#include <genplib.h>

#include <pmdev.h>

#define  OD_MEMORY  8L

SHORT   szLength(PSZ);
VOID   SetTextBackground(ULONG,PCSP_INFO,PPOINTL,PDDC);
VOID   SetTextColors(PDDC,ULONG,PCSP_INFO,PPOINTL,ULONG);
VOID   CharStringDebug(PDDC,PPOINTL,PRECTL,ULONG,LONG,PCH);
BOOL   CharStringRect(PDDC,PRECTL,PCSP_INFO,ULONG,ULONG);
LONG   CharStringKern(PDDC,SHORT,SHORT,SHORT,SHORT);
ULONG   prdt_QueryTextBox(HDC,LONG,PSZ,LONG,PPOINTL,PDDC,ULONG);
BOOL   CharStringWidthVector(HDC,PDDC,PLONG,LONG,PCH,ULONG,PCSP_INFO,
                                       ULONG);

BOOL   CharStringLeftRight(HDC,PDDC,LONG,PCH,ULONG,PCSP_INFO,ULONG);
BOOL   CharStringRightLeft(HDC,PDDC,LONG,PCH,ULONG,PCSP_INFO,ULONG);
BOOL   CharStringUpDown(HDC,PDDC,LONG,PCH,ULONG,PCSP_INFO,SHORT,
                                  ULONG);

VOID   CharStringReverse(LONG,PCH);

extern PDDC EnterDriver(PDDC);
extern VOID ExitDriver(PDDC);
/*
** ********************************** Engine Entry Point ***************
*/
/***************************************************************************
 *
 * FUNCTION NAME = prdt_CharStringPos()
 *
 * DESCRIPTION   = Draws a character string starting optionally
 *                 at the speci- fied (X,Y) position or at the
 *                 current (X,Y) position.
 *
 * Call: ULONG(ulReturn)  = prdt_CharStringPos (hdc, pptlStartXY,
 *                              prclClipRect, ulOptions, lNumBytes,
 *                              pchString, plVector, pscpAttributes,
 *                              pddc, FunN);
 *
 * Input:  pptlStartXY    = Starting position (OPTIONAL)
 *         prclClipRect   = pointer to an array of four coordinates
 *                          specifying the corners of the clipping
 *                          rectangle that defines the background
 *                          of the characters (OPTIONAL)
 *         ulOptions      = CHS_OPAQUE (bit 0): opaque rectangle
 *                          CHS_VECTOR (bit 1): increment vec. present
 *                          CHS_LEAVEPOS (bit 3): leave current pos.
 *                              at start of char string (set), else
 *                              move current pos. to end of char str.
 *                          CHS_CLIP   (bit 4): clip string to rect.
 *                          CHS_START_XY (bit 5): start pos. present
 *                          CHS_ATTR_INFO (bit 6): attrs. structure
 *                              present
 *         lNumBytes      = number of bytes in the character string
 *         pchString      = pointer to the string of codepoints
 *         plVector       = vector of (lNumBytes) increment values.
 *                          These are LONGs in world coordinates.
 *         pscpAttributes = pointer to structure of attributes to be
 *                          used in the operation.
 *
 * Output:       0              error
 *               1              ok
 *               2              correlate hit
 *
 * Notes:       plVector is OPTIONAL.  If present (CHS_VECTOR set), it
 *              allows control over the positioning of each character
 *              after the first.  These are distances measured in world
 *              coords. (along the baseline for left-to-right and
 *              right-to-left character directions, and along the
 *              shearline for top-to-bottom and bottom-to-top).  The
 *              ith increment is the distance of the reference point
 *              (e.g. bottom left corner) of the (i+1)th character
 *              from the reference point of the ith.  The last incre-
 *              ment may be needed to update current position.  These
 *              increments, if specified, set the widths of each char-
 *              acter.
 *
 *              If prclClipRect is present (CHS_CLIP set), it is used
 *              as both the clip rectangle and as the background of
 *              the string, rather than using the normal method of
 *              defining the background.  The points on the borders of
 *              the rectangle are included within the rectangle.
 *
 *              If pscpAttributes is present (CHS_ATTRS_INFO set), it
 *              may be used to specify the foreground and background
 *              colors for the operation.  Otherwise, the chars are
 *              drawn using the current char. attribute values and the
 *              background is drawn using the current char background
 *              color and an overpaint mix.  Both corners of the rect-
 *              angle are specified, so that the rect. is positioned
 *              independently of the current position.
 *
 *              pscpAttributes->cSize      = size of structure (bytes)
 *              pscpAttributes->lColor     = foreground color to use
 *              pscpAttributes->lBackColor = background color to use
 *
 *              CHS_CLIP causes the string to be clipped to the rect-
 *              angle.  This is independent of whether the rectangle
 *              is actually drawn.
 *
 *              If the rectangle is not present, then CHS_OPAQUE and
 *              CHS_CLIP must be zero.
 *
 *
 * INPUT         =  (hdc, pptlStartXY, prclClipRect,
 *                   ulOptions, lNumBytes, pchString,
 *                   plVector, pscpAttributes, pddc, FunN)
 *
 * OUTPUT        = 0              error
 *                 1              ok
 *                 2              correlate hit
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

ULONG prdt_CharStringPos( HDC hdc, PPOINTL pptlStartXY, PRECTL prclClipRect,
                          ULONG ulOptions, LONG lNumBytes, PCH pchString,
                          PLONG plVector, PCSP_INFO pscpAttributes,
                          PDDC pddc, ULONG FunN )
{
  POINTL ptlXYtmp, ptlBeginText;
  POINTL ptlOldCP;
  SHORT  usLineType, usLineCap;
  FIXED  fxLineWidth;
  SHORT  usDirection;
  BYTE   bEntry;
  SHORT  i, cCP;
  ULONG  ulRet;
  /* COOK @V3.106195  ptlBox needed for QueryTextBox                         */
  POINTL ptlBox[5];
  USHORT usHoriz;
  USHORT usVert;
  LONG   lTop;
  LONG   lBottom;
  LONG   lLeft;
  LONG   lRight;

  EnterDriver( pddc );

  if (pddc->iType == OD_MEMORY)
  {
    if (FunN & COM_DEVICE)
    {
      ulRet = InnerGreCharStringPos( pddc->hdcMemory, pptlStartXY,
                                     prclClipRect, ulOptions,
                                     lNumBytes, pchString, plVector,
                                     pscpAttributes, FunN );
    }
    else
    {
      ulRet = (*pfnlCharStringPos)( hdc, pptlStartXY,
                                    prclClipRect, ulOptions,
                                    lNumBytes, pchString, plVector,
                                    pscpAttributes, pddc, FunN );
    }
    ExitDriver( pddc  );
    return( ulRet );
  }

  /*           */
  /*
  ** If GPI wants us to print using the width vector, it is necessary
  ** for us to reset the font angle to that of the baseline (resetting
  ** font angle to zero).
  */
  if ((pddc->pddcb->text.ChrBundle.ptlAngle.y != 0L) && (ulOptions & CHS_VECTOR) )
  {
    ps_rotatefont( pddc, 1L, 0L );
    pddc->pddcb->cgs.fValidFont = TRUE;
  }

  /*
  ** !!!CR Verify that its OK to draw into a path or area
  ** !!!CR Find out from PaulK or WaltM if we need to do correlation
  ** !!!CR Kent says that if the font is a raster font that you can't
  ** !!!*R draw the font into an open path.  Check with Paul on what
  ** !!!CR this means.
  ** if the engine wants to do vector font simulaitons, we must call
  ** back to the engine's default function here.  we must first make
  ** sure the engine text attributes are in ssync with the driver.
  */
  if (pddc->pddcb->text.fFontSimulation)
  {
    ulRet =  (ULONG) (*pfnlCharStringPos)( hdc, pptlStartXY, prclClipRect,
                                           ulOptions, lNumBytes, pchString,
                                           plVector, pscpAttributes,
                                           pddc, FunN );
    ExitDriver( pddc  );
    return( ulRet );
  }

  #if      DEBUG
    LogCall( "prdt_CharStringPos (%lp, %ld, %lp, %lp, %lp)\n", ((PB) &hdc) +
             sizeof (hdc) );
  #endif

  /*
  ** Disallow     parameters.
  */
  if (ulOptions & (~(CHS_VECTOR | CHS_START_XY | CHS_OPAQUE | CHS_LEAVEPOS |
                 CHS_CLIP | CHS_ATTR_INFO | CHS_UNDERSCORE | CHS_STRIKEOUT )))
  {
    GplErrSetError(  PMERR_INV_CHAR_POS_OPTIONS );
    ExitDriver( pddc );
    return( FAILURE );
  }

  if (lNumBytes < 0)
  {
    GplErrSetError(  PMERR_INV_LENGTH_OR_COUNT );
    ExitDriver( pddc );
    return( FAILURE );
  }

  #if      DEBUG
    /*
    ** CharStringPos is      enough, lets hide some code
    */
    CharStringDebug( pddc, pptlStartXY, prclClipRect, ulOptions, lNumBytes,
                     pchString );
  #endif                                 /* ifdef DEB  */

  /*
  ** Make sure our font is selected, and load it in if it is not
  */
  if (!pddc->pddcb->cgs.fValidFont)
  {
    if (!prda_CheckFontResource( pddc )) /* couldn't get resourc  */
    {
      ExitDriver( pddc );
      return( FAILURE );                 /* Minor function saves error code  */
    }
    ps_selectfont( pddc );                /* utlps.c */
  }

  /*
  ** Get the the high-order word of the function number and extract the
  ** flag bits. This is in order to accumulate bounds.
  */
  pddc->pddcb->bounds.fAccumulate = FunN & COM_BOUND;

  /*
  ** save the original current position for CHS_LEAVEPOS
  */
  ptlOldCP.x = pddc->pddcb->pen.ptlCur.x;
  ptlOldCP.y = pddc->pddcb->pen.ptlCur.y;

  /*
  ** Set up for the start of the string: Set a new current position if
  ** one was provided, and remember where our string starts printing in
  ** either case.
  */
  if (ulOptions & CHS_START_XY)
  {
    PrintLog( (PSZ)"Moving current point from {%ld,%ld} to {%ld,%ld}\n",
              pddc->pddcb->pen.ptlCur.x, pddc->pddcb->pen.ptlCur.y, pptlStartXY->x,
              pptlStartXY->y );

    /*
    ** if (!prdg_PointIsValid(pddc, pptlStartXY->x, pptlStartXY->y))
    ** {
    **   goto ResetCPonError;
    ** }
    */

    ps_movetoCPx( pddc, pptlStartXY );
  }

  ptlBeginText.x = pddc->pddcb->pen.ptlCur.x; /* Where we'll start printing */
  ptlBeginText.y = pddc->pddcb->pen.ptlCur.y;

  /*
  ** if a rectangle structure is provided, clip to it or fill it if
  ** requested.
  */
  if ((prclClipRect) && ((ulOptions&CHS_CLIP) || (ulOptions&CHS_OPAQUE)))
  {
    if (!CharStringRect( pddc, prclClipRect, pscpAttributes, ulOptions, FunN ))
    {
      goto ResetCPonError;
    }
  }

  /*
  ** Get out if not bytes to output
  */
  if (!lNumBytes)
  {
    goto csp_lets_leave;
  }

  /***************************************************************************/
  /* COOK @V3.106195                                                         */
  /* Query the text box to get the x and y coordinates;  this gives you the  */
  /* size of the string and allows for different fonts.  Divide by two to    */
  /* get to the center and subtract from the current coordinates.            */
  /***************************************************************************/

  /*
  ** get a local copy of the character direction
  */
  usDirection = pddc->pddcb->text.ChrBundle.usDirection;

  if (prdt_QueryTextBox(hdc, lNumBytes, pchString, 4L, (PPOINTL)ptlBox,
                        pddc, FunN ))
  {
    usHoriz = pddc->pddcb->text.ChrBundle.usTextAlign;
    usHoriz &= 0x00FF;
    usVert = pddc->pddcb->text.ChrBundle.usTextAlign;
    usVert &= 0xFF00;
    lTop = ptlBox[TXTBOX_TOPLEFT].y;
    lBottom = ptlBox[TXTBOX_BOTTOMLEFT].y;
    lLeft = ptlBox[TXTBOX_BOTTOMLEFT].x;
    lRight = ptlBox[TXTBOX_BOTTOMRIGHT].x;

    switch (usHoriz)
    {
      case TA_LEFT:
        /* Left alignment . . .                              */
        pddc->pddcb->pen.ptlCur.x = pddc->pddcb->pen.ptlCur.x - lLeft;
        break;
      case TA_CENTER:
        /* Center it horizontally . . . */
        pddc->pddcb->pen.ptlCur.x =
              pddc->pddcb->pen.ptlCur.x - ((lRight + lLeft)/2);
        break;
      case TA_RIGHT:
        /* Move it to the left for right alignment . . .     */
        pddc->pddcb->pen.ptlCur.x = pddc->pddcb->pen.ptlCur.x - lRight;
        break;
      default:
        break;
    }
    switch (usVert)
    {
      case TA_TOP:
        /* Move it down for top alignment . . . */
        pddc->pddcb->pen.ptlCur.y = pddc->pddcb->pen.ptlCur.y - lTop;
        break;
      case TA_HALF:
        /* Center it . . . */
        pddc->pddcb->pen.ptlCur.y =
              pddc->pddcb->pen.ptlCur.y - ((lTop + lBottom)/2);
        break;
      case TA_BASE:
        break;
      case TA_BOTTOM:
        /* Move it up for bottom alignment . . . */
        pddc->pddcb->pen.ptlCur.y = pddc->pddcb->pen.ptlCur.y - lBottom;
        break;
      default:
        break;
    }
  }

  /***************************************************************************/
  /* COOK @V3.106195   end                                                   */
  /***************************************************************************/

  if ((usDirection == CHDIRN_LEFTRIGHT) && !(ulOptions&CHS_VECTOR))
  {
    /*
    ** if we are to print the text left-to-right and without incremental
    ** vectors, we can use one or a few show commands, packing several
    ** characters into each show.  (ie: it is not necessary to do a
    ** separate show command for each individual character.)
    */
    if (!CharStringLeftRight( hdc, pddc, lNumBytes, pchString, ulOptions,
                              pscpAttributes, FunN ))
    {
      goto ResetCPonError;
    }
  }
  else if (usDirection == CHDIRN_LEFTRIGHT) /* must have width vector! */
  {
    /*
    ** Otherwise it is going to be necessary for us to print the string
    ** one character at a time, adjusting the current position manually
    ** as we go.  This is slow, and generates LOTS of PostScript code.
    */
    if (!CharStringWidthVector( hdc, pddc, plVector, lNumBytes, pchString,
                                ulOptions, pscpAttributes, FunN ))
    {
      goto ResetCPonError;
    }
  }
  else if (usDirection == CHDIRN_RIGHTLEFT)
  {
    if (!CharStringRightLeft( hdc, pddc, lNumBytes, pchString, ulOptions,
                              pscpAttributes, FunN))
    {
      goto ResetCPonError;
    }
  }
  else if (!CharStringUpDown( hdc, pddc, lNumBytes, pchString, ulOptions,
                              pscpAttributes, usDirection, FunN))
  {
    goto ResetCPonError;
  }

  /*
  ** Finalization:  Perform underlining and strikeout if called for.
  ** Terminate clipping action if there was any.
  ** Restore current position if called for.
  ** If underlining or strikeout are needed, do it now
  */
  if ((FunN&COM_DRAW) &&
/*****************************************************************************
**** ((pddc->pddcb->text.pfmFontMetrics->fsSelection & FM_SEL_UNDERSCORE) ||
****  (pddc->pddcb->text.pfmFontMetrics->fsSelection & FM_SEL_STRIKEOUT)) &&
\****************************************************************************/
     ( pddc->pddcb->text.bFontHasUnderscore ||
       pddc->pddcb->text.bFontHasStrikeout  ||
       ( ulOptions & ( CHS_UNDERSCORE | CHS_STRIKEOUT ) ) )  &&
     (pddc->pddcb->text.ChrBundle.usMixMode != FM_LEAVEALONE))
  {
    usLineType = pddc->pddcb->cgs.usLineType;
    usLineCap = pddc->pddcb->cgs.usLineCap;
    fxLineWidth = pddc->pddcb->cgs.fxLineWidth;

    /*
    ** if (pddc->pddcb->text.pfmFontMetrics->fsSelection & FM_SEL_UNDERSCORE)
    */

    if (pddc->pddcb->text.bFontHasUnderscore ||
        ( ulOptions & CHS_UNDERSCORE ) )
    {
      /*
      ** Save the current position
      */
      ptlXYtmp = pddc->pddcb->pen.ptlCur;
      prda_DrawLine( pddc, ptlBeginText, -
                     (pddc->pddcb->text.pfmFontMetrics->lUnderscorePosition),
                     pddc->pddcb->text.pfmFontMetrics->lUnderscoreSize );

      /*
      ** restore the current position
      */
      ps_moveto( pddc, &ptlXYtmp );
    }

    if (pddc->pddcb->text.bFontHasStrikeout ||
        ( ulOptions & CHS_STRIKEOUT ) )
    {
      prda_DrawLine(pddc, ptlBeginText,
                    pddc->pddcb->text.pfmFontMetrics->lStrikeoutPosition,
                    pddc->pddcb->text.pfmFontMetrics->lStrikeoutSize );
    }
    ps_setdash( pddc, usLineType );
    ps_setlinecap( pddc, usLineCap );
    ps_setlinewidth( pddc, fxLineWidth, FALSE );
  }

csp_lets_leave:

  /*
  ** If we did a gsave before, we must grestore now
  */

  if ((ulOptions & CHS_CLIP) && (FunN & COM_DRAW) &&
     (pddc->pddcb->text.ChrBundle.usMixMode != FM_LEAVEALONE))
  {
    ptlXYtmp = pddc->pddcb->pen.ptlCur;

    /*
    ** ps_grestore( pddc );
    */
    PrintChannel( pddc, (PSZ)"gr\n" );    /* dont use ps_grestore!!! */

    /*
    ** NOTE!!! this grestore just blew away any font remapping
    ** that might have been done.  this will be slow as hell,
    ** but we will set a flag which will force remapping next
    ** time.  This will only affect someone who is printing
    ** extended characters, and using a clipping rectangle.
    */
    bEntry = pddc->pddcb->text.bLogFontEntry;
    ClearFontRemap( bEntry );

    /*
    ** the grestore also blew away the text color.  so we
    ** must invalidate that.
    */
    pddc->pddcb->cgs.lColor += 1L;
    ps_setmatrix( pddc, (FIXED *)&pddc->pddcb->xformCTM );

    if (!(ulOptions&CHS_LEAVEPOS))
    {
      ps_moveto( pddc, &ptlXYtmp );
    }
  }

  if (ulOptions & CHS_LEAVEPOS)
  {
    PrintLog( (PSZ)"Moving current point from {%ld,%ld} to {%ld,%ld}\n",
              pddc->pddcb->pen.ptlCur.x, pddc->pddcb->pen.ptlCur.y, ptlOldCP.x,
              ptlOldCP.y );
    ps_moveto( pddc, &ptlOldCP );
  }

  /*
  ** LF. 1/19/89 Check for an invalid current point
  */
  /*
  ** if (!prdg_PointIsValid(pddc, pddc->pddcb->pen.ptlCur.x, pddc->pddcb->pen.ptlCur.y))
  ** {
  **   goto ResetCPonError;
  ** {
  */

  PrintLog( (PSZ)"Leaving CSP Current Position is {%ld, %ld}\n",
     pddc->pddcb->pen.ptlCur.x, pddc->pddcb->pen.ptlCur.y );
  ExitDriver( pddc );
  return( SUCCESS );

ResetCPonError:

  ps_moveto( pddc, &ptlOldCP );
  ExitDriver( pddc );
  return( FAILURE );
}

/*
** ******************************** Engine Entry Point **************
*/
/***************************************************************************
 *
 * FUNCTION NAME = prdt_CharString()
 *
 * DESCRIPTION   = Draws a character string starting at the current (X,Y)
 *
 *                 Call: ULONG(ulReturn) = prdt_CharString(hdc, ulNumBytes, pchString,
 *                                                      pddc, FunN );
 *
 *                 Notes:     calls prdt_CharStringPos() with dummies for extra
 *                            parameters
 *
 * INPUT         = ulNumBytes     =  # of bytes to print
 *                 pchString      =  pointer to a string of char. codepoints
 *
 * OUTPUT        =  0              error
 *                  1              ok
 *                  2              correlate hit
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

ULONG prdt_CharString( HDC hdc, ULONG ulNumBytes, PCH pchString,
                       PDDC pddc, ULONG ulFunN )
{
  ULONG ulRet;

  EnterDriver( pddc );

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

  /*
  ** just call CharStringPos with lots of zeros
  */
  ulRet = prdt_CharStringPos( hdc, 0L, 0L, 0L, ulNumBytes, pchString, 0L, 0L,
                              pddc, NGreCharStringPos|(0xFFFF0000 & ulFunN) );
  ExitDriver( pddc );
  return( ulRet );
}

/***************************************************************************
 *
 * FUNCTION NAME = SetTextColors
 *
 * DESCRIPTION   = This routine sets the background color and draws the background if
 *                 necessary.  It then sets the foreground color for the text.
 *
 * INPUT         = (pddc, ulOptions, pscpAttributes, ptlBox, FunN)
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

VOID SetTextColors( PDDC pddc, ULONG ulOptions, PCSP_INFO pscpAttributes, PPOINTL ptlBox, ULONG FunN )
{
  /*
  ** If the background mix mode is overpaint, then we must first
  ** paint a rectangle where the text will later be placed, since
  ** PostScript won't do this for us.  But if the draw bit is turned
  ** off, or if we have already drawn an opaque rectangle, then we
  ** don't bother with the background mix mode at all.
  */
  if (pddc->pddcb->text.ChrBundle.usBackMixMode == CAPS_BM_OVERPAINT)
  {
    if ((FunN & COM_DRAW) && (!(ulOptions & CHS_OPAQUE)))
    {
      SetTextBackground(ulOptions, pscpAttributes, (PPOINTL)ptlBox, pddc );
    }
  }

  /*
  ** set the foreground color to either the attributes (if specified)
  ** or else to the current text foreground color
  */
  if (ulOptions & CHS_ATTR_INFO)
  {
    ps_setrgbcolor( pddc, prdc_GetColor(pddc, pscpAttributes->lColor) );
  }
  else
  {
    ps_setrgbcolor( pddc, prdc_GetColor( pddc,
                    pddc->pddcb->text.ChrBundle.lColor) );
  }
}

/***************************************************************************
 *
 * FUNCTION NAME = CharStringDebug
 *
 * DESCRIPTION   = CharStringDebug
 *
 * INPUT         = (pddc, pptlStartXY, prclClipRect, ulOptions, lNumBytes,
 *                  pchString)
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

VOID CharStringDebug( PDDC pddc, PPOINTL pptlStartXY, PRECTL prclClipRect, ULONG ulOptions,
                      LONG lNumBytes, PCH pchString )
{
  register LONG i;

  #if      DEBUG
    PrintLog( (PSZ)"Entering CSP Current Position is {%ld, %ld}\n",
              pddc->pddcb->pen.ptlCur.x, pddc->pddcb->pen.ptlCur.y );

  if (pptlStartXY)
  {
    PrintLog( (PSZ) "Start {x,y} = {%ld,%ld}\n", pptlStartXY->x, pptlStartXY->y);
  }

  if (prclClipRect)
  {
    PrintLog( (PSZ) "Clip rect {xLeft,yBottom,xRight,yTop} = {%ld,%ld,%ld,%ld}\n",
              prclClipRect->xLeft, prclClipRect->yBottom, prclClipRect->xRight,
              prclClipRect->yTop );
  }
  PrintLog( (PSZ)"Options:\n" );

  if (ulOptions & CHS_VECTOR)
  {
    PrintLog( (PSZ)"\tCHS_VECTOR\n" );
  }

  if (ulOptions & CHS_OPAQUE)
  {
    PrintLog( (PSZ)"\tCHS_OPAQUE\n" );
  }

  if (ulOptions & CHS_LEAVEPOS)
  {
    PrintLog( (PSZ)"\tCHS_LEAVEPOS\n" );
  }

  if (ulOptions & CHS_CLIP)
  {
    PrintLog( (PSZ)"\tCHS_CLIP\n" );
  }

  if (ulOptions & CHS_START_XY)
  {
    PrintLog( (PSZ)"\tCHS_START_XY\n" );
  }

  if (ulOptions & CHS_ATTR_INFO)
  {
    PrintLog( (PSZ)"\tCHS_ATTR_INFO\n" );
  }
  PrintLog( (PSZ)"Number of bytes in string = %ld\n", lNumBytes );

  if (lNumBytes)
  {
    PrintLog( (PSZ)"String = " );

    for (i = 0L ; i < lNumBytes ; i++)
    {
      PrintLog( (PSZ)"%c", pchString[i] );
    }
    PrintLog( (PSZ)"\n" );
  }
  #endif                                 /* if DEBUG */
}

/***************************************************************************
 *
 * FUNCTION NAME = CharStringRect
 *
 * DESCRIPTION   = This routine clips to or fills the given rectangle,
 *                 as requested.
 *
 * INPUT         = (pddc, prclClipRect, pscpAttributes, ulOptions, FunN)
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

BOOL CharStringRect( PDDC pddc, PRECTL prclClipRect, PCSP_INFO pscpAttributes,
                     ULONG ulOptions, ULONG FunN )
{
  POINTL ptlCur, ptlClip[4];

  /*
  ** Check clip rectangle for valid points
  */
  /*
  ** if (!prdg_PointIsValid(pddc, prclClipRect->xLeft, prclClipRect->yBottom))
  **      return( FAILURE );
  **  if (!prdg_PointIsValid(pddc, prclClipRect->xRight, prclClipRect->yTop))
  **      return( FAILURE );
  */

  /*
  ** If we will do clipping, we must save state first
  */
  if (ulOptions & CHS_CLIP)
  {
    /*
    ** ps_gsave( pddc );
    */

    PrintChannel( pddc, (PSZ)"gs\n" );    /* dont use ps_gsave!!! */
  }

  /*
  ** set the background fill color if necessary.
  */
  if (ulOptions & CHS_OPAQUE)
  {
    if (ulOptions & CHS_ATTR_INFO)
    {
      ps_setrgbcolor(pddc, prdc_GetColor(pddc, pscpAttributes->lBackColor) );
    }
    else
    {
      ps_setrgbcolor( pddc, prdc_GetColor( pddc, pddc->pddcb->text.ChrBundle.lBackColor) );
    }
  }

  /*
  ** convert the rect into an array of 4 points
  */
  ptlClip[0].x = prclClipRect->xLeft;  /* upper left */
  ptlClip[0].y = prclClipRect->yTop;
  ptlClip[1].x = prclClipRect->xRight; /* upper right */
  ptlClip[1].y = prclClipRect->yTop;
  ptlClip[2].x = prclClipRect->xRight; /* lower right */
  ptlClip[2].y = prclClipRect->yBottom;
  ptlClip[3].x = prclClipRect->xLeft;  /* lower left */
  ptlClip[3].y = prclClipRect->yBottom;

  /*
  ** If the draw bit is turned on, then make the rectangle into
  ** a path and make the path a clip region if appropriate.
  */
  if ((FunN & COM_DRAW) && (pddc->pddcb->text.ChrBundle.usMixMode !=
      FM_LEAVEALONE))
  {
    ptlCur = pddc->pddcb->pen.ptlCur;

    /*
    ** Make rectangle a path
    */
    /*
    ** ps_newpath( pddc );
    */

    PrintChannel( pddc, (PSZ)"n %ld %ld %ld %ld box\n", ptlClip[0].x, ptlClip[0].y,
                  ptlClip[2].x, ptlClip[2].y );

    /*
    **    ps_movetoCPx(pddc, (PPOINTL) & ptlClip[3] );
    **    ps_lineto (pddc, (PPOINTL) & ptlClip[0] );
    **    ps_lineto (pddc, (PPOINTL) & ptlClip[1] );
    **    ps_lineto (pddc, (PPOINTL) & ptlClip[2] );
    **    ps_closepath (pddc );
    */

    if (ulOptions & CHS_CLIP)            /* Select rect as clip region */
    {
      ps_clip( pddc );
    }
    ps_moveto( pddc, &ptlCur );
  }

  /*
  ** If the rectangle is to be opaqued, then expand the bounds.
  ** If the draw bit is on, then opaque the rectangle as well.
  */
  if (ulOptions & CHS_OPAQUE)
  {
    /*
    ** Expand the bounds to reflect the display of the box
    */
    prdl_ExpandBounds( pddc, ptlClip[1].x, ptlClip[1].y );
    prdl_ExpandBounds( pddc, ptlClip[3].x, ptlClip[3].y );

    /*
    ** Fill the rectangle in
    */
    if ((FunN & COM_DRAW) && (pddc->pddcb->text.ChrBundle.usMixMode !=
       FM_LEAVEALONE))
    {
      ps_fill( pddc );
    }
  }
  return( SUCCESS );
}

/***************************************************************************
 *
 * FUNCTION NAME = CharStringKern
 *
 * DESCRIPTION   = This routine returns adjusts for kerning
 *
 * INPUT         = (pddc, sPreviousCodePt, sCodePoint, sFirst, sLast)
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

LONG CharStringKern( PDDC pddc, SHORT sPreviousCodePt, SHORT sCodePoint, SHORT sFirst, SHORT sLast )
{
  if (pddc->pddcb->text.pusCurCodePgVector)
  {
    sPreviousCodePt = (SHORT) *(pddc->pddcb->text.pusCurCodePgVector +
                                sPreviousCodePt );
    sCodePoint = (SHORT) *(pddc->pddcb->text.pusCurCodePgVector + sCodePoint );
  }

  if ((sPreviousCodePt < sFirst) || (sPreviousCodePt > (sFirst + sLast)))
  {
    sPreviousCodePt = sFirst + pddc->pddcb->text.pfmFontMetrics->sDefaultChar;
  }

  if ((sCodePoint < sFirst) || (sCodePoint > (sFirst+sLast)))
  {
    sCodePoint = sFirst + pddc->pddcb->text.pfmFontMetrics->sDefaultChar;
  }

  return( prda_GetKernAmount( sPreviousCodePt, sCodePoint, pddc) );
}

/***************************************************************************
 *
 * FUNCTION NAME = CharStringWidthVector
 *
 * DESCRIPTION   = This routine prints characters, accounting for a given width vector.
 *
 * INPUT         = (hdc, pddc, plVector, lNumBytes, pchString,
 *                  ulOptions, pscpAttributes, FunN)
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

BOOL CharStringWidthVector( HDC hdc, PDDC pddc, PLONG plVector, LONG lNumBytes,
                            PCH pchString, ULONG ulOptions,
                            PCSP_INFO pscpAttributes, ULONG FunN)
{
  LONG   lUserDefWidth;
  POINTL ptlWidthVector;
  POINTL ptlBox[5];
  LONG   lPrintBytes, i;
  POINTL NewPtl;
  BOOL   Non_Zero_Baseline;

  /*             */
  /*
  ** Are we going to print at an angle ?
  */
  if (pddc->pddcb->text.ChrBundle.ptlAngle.y == 0L)
  {
    Non_Zero_Baseline = FALSE;         /* Yes we are. */
  }
  else
  {
    Non_Zero_Baseline = TRUE;          /* No were not. */
  }

  /*
  ** Since this release of the driver only supports text printed left to
  ** right, we may assume that control only comes here when text is to
  ** be printed left to right with user defined spacing.  Our first step
  ** is to determine the bounding box of the complete string for bounds
  ** purposes, and for background overpaint mix mode.
  */
  lUserDefWidth = 0L;

  for (i = 0 ; i < lNumBytes ; i++)
  {
    lUserDefWidth += plVector[i];
  }
  ptlWidthVector.x = FxToLong( frmul(pddc->pddcb->text.fxCos,
                               LongToFx( lUserDefWidth )) );
  ptlWidthVector.y = FxToLong( frmul(pddc->pddcb->text.fxSin,
                               LongToFx( lUserDefWidth )) );

  /*
  ** We find the bounding box by calling QueryTextBox on one character
  ** order to find the ascent and descent around the baseline, giving us
  ** the upper and lower left corners of the box.  For the upper and
  ** lower right corners, we will add in our width vector from above.
  */
  prdt_QueryTextBox( hdc, 1L, pchString, 2L, (PPOINTL) ptlBox, pddc, FunN );

  /*
  ** make box coordinates absolute, not relative to start
  ** of string.
  */
  for (i = 0 ; i < 4 ; i++)
  {
    ptlBox[i].x += pddc->pddcb->pen.ptlCur.x;
    ptlBox[i].y += pddc->pddcb->pen.ptlCur.y;
  }
  ptlBox[2].x = ptlBox[0].x + ptlWidthVector.x;
  ptlBox[2].y = ptlBox[0].y + ptlWidthVector.y;
  ptlBox[3].x = ptlBox[1].x + ptlWidthVector.x;
  ptlBox[3].y = ptlBox[1].y + ptlWidthVector.y;

  /*
  ** Expand the bounds to reflect the display of the text
  */
  prdl_ExpandBounds( pddc, ptlBox[0].x, ptlBox[0].y );
  prdl_ExpandBounds( pddc, ptlBox[3].x, ptlBox[3].y );

  /*
  ** set up the text colors
  */
  SetTextColors( pddc, ulOptions, pscpAttributes, ptlBox, FunN );

  /*
  ** we will be outputting the width vector in reverse order.
  ** this is to get it onto the postscript stack in the order
  ** it is needed.  start by pointing to the last of the width
  ** vector entries.
  */
  plVector += ( lNumBytes - 1 );
  ps_movetoCP( pddc );

  /*             */
  if (Non_Zero_Baseline)
  {
    /*
    ** If were going to print at an angle,
    ** rotate CTM to the required text baseline.
    */
    PrintChannel( pddc, (PSZ)" %ld %ld atan rotate\n ",
                  pddc->pddcb->text.ChrBundle.ptlAngle.y,
                  pddc->pddcb->text.ChrBundle.ptlAngle.x );
  }

  for (i = 0 ; i < lNumBytes ; i++)
  {
    /*
    ** Update the current position after the character has been printed.
    */
    lUserDefWidth = *plVector--;
    ptlWidthVector.x = FxToLong( frmul( pddc->pddcb->text.fxCos,
                                        LongToFx( lUserDefWidth)) );
    ptlWidthVector.y = FxToLong( frmul( pddc->pddcb->text.fxSin,
                                        LongToFx( lUserDefWidth )) );
    pddc->pddcb->pen.ptlCur.x += ptlWidthVector.x;
    pddc->pddcb->pen.ptlCur.y += ptlWidthVector.y;
    PrintChannel( pddc, (PSZ)"%ld ", lUserDefWidth );
  }

  /*
  ** If the draw bit is turned on, then display the character.
  */

  if ((FunN & COM_DRAW) && (pddc->pddcb->text.ChrBundle.usMixMode !=
      FM_LEAVEALONE))
  {
    /*
    ** output a currentpoint command to save the current
    ** position in the printer.  this is needed to handle
    ** the user width vectors with the kshow command correctly.
    */
    PrintChannel( pddc, (PSZ)"CP " );

    /*
    ** Get a useable code point from the string.  This
    ** involves remapping if we are using a code page other
    ** than 850, and using the default character instead of
    ** the specified one if the code point is out of
    ** the bounds supported by this font.
    */
    lPrintBytes = prda_RemapString( pchString, lNumBytes, pddc );

    if (lPrintBytes == -1L)
    {
      RIP( "CharStringWidthVector:  RemapString failed" );
      GplErrSetError( PMERR_BASE_ERROR );
      return( FAILURE );
    }

    /*
    ** output the string to be printed.  RemapString builds the
    ** string in pddc->pdv->chText.
    */
    PrintChannel( pddc, (PSZ)"(" );

    for (i = 0 ; i < lPrintBytes ; i++)
    {
      PrintChannel( pddc, (PSZ)"%c", pddc->pdv->chText[i] );
    }

    /*
    ** output the command to handle this specialized form of
    ** kern-show.
    */
    PrintChannel( pddc, (PSZ)") K\n" );

    /*             */
    if (Non_Zero_Baseline)
    {
      /*
      ** If changed above, reset to previous
      ** default.
      */
      PrintChannel( pddc, (PSZ)" %ld %ld atan rotate\n ",
                    (0L - (pddc->pddcb->text.ChrBundle.ptlAngle.y) ),
                    pddc->pddcb->text.ChrBundle.ptlAngle.x );

    }
    ps_moveto(pddc, (PPOINTL) &pddc->pddcb->pen.ptlCur );
  }
  return( SUCCESS );
}

/***************************************************************************
 *
 * FUNCTION NAME = CharStringLeftRight
 *
 * DESCRIPTION   = if we are to print the text left-to-right and
 *                 without incremental vectors, we can use one or
 *                 a few show commands, packing several
 *                 characters into each show.  (ie:  it is not
 *                 necessary to do a separate show command for
 *                 each individual character.)
 *
 * INPUT         = (hdc, pddc, lNumBytes, pchString, ulOptions,
 *                  pscpAttibutes, FunN)
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

BOOL CharStringLeftRight( HDC hdc, PDDC pddc, LONG lNumBytes, PCH pchString,
                          ULONG ulOptions, PCSP_INFO pscpAttributes,
                          ULONG FunN)
{
  POINTL ptlBox[5];
  LONG   lBytesNow;
  LONG   lKern;
  SHORT  sFirst;
  SHORT  sLast;
  BOOL   bFirstShow = TRUE;
  BOOL   bDoKerning;
  LONG   i;
  LONG   lPrintBytes;
  POINTL ptlXYtmp;

  /*
  ** Decide whether or not to do kerning on this text
  */
  bDoKerning = ((pddc->pddcb->text.bFontHasKerning) && (!(ulOptions &
                 CHS_VECTOR )) );
  sFirst = pddc->pddcb->text.pfmFontMetrics->sFirstChar;
  sLast = pddc->pddcb->text.pfmFontMetrics->sLastChar;

  /*
  ** get the size of the bounding text box
  */
  prdt_QueryTextBox( hdc, lNumBytes, (PSZ)pchString, 4L, (PPOINTL) ptlBox, pddc,
                     FunN );

  /*
  ** make box coordinates absolute, not relative to start
  ** of string.
  */
  for (i = 0 ; i < 4 ; i++)
  {
    ptlBox[i].x += pddc->pddcb->pen.ptlCur.x;
    ptlBox[i].y += pddc->pddcb->pen.ptlCur.y;
  }

  /*
  ** Expand the bounds to reflect the display of the text
  */
  prdl_ExpandBounds( pddc, ptlBox[0].x, ptlBox[0].y );
  prdl_ExpandBounds( pddc, ptlBox[3].x, ptlBox[3].y );

  /*
  ** set up the text colors
  */
  SetTextColors( pddc, ulOptions, pscpAttributes, ptlBox, FunN );

  /*
  ** Do one show command at a time,
  ** until there is nothing left to show
  */
  while (lNumBytes)
  {
    /*
    ** Decide how many characters to display in this show command.  If
    ** the font has kerning only show up until the first kern adjust
    ** is needed, otherwise show the entire string.  In either case,
    ** limit the number of characters showed at one time to 60 so we
    ** are assured of not overflowing our text buffer of 256 chars.
    */
    if (bDoKerning)
    {
      for (lBytesNow = 1L, lKern = 0L ; (lBytesNow < lNumBytes) && !lKern ;
           lBytesNow++)
      {
        lKern = CharStringKern( pddc, (SHORT) pchString[lBytesNow - 1L],
                                (SHORT) pchString[lBytesNow], sFirst, sLast );
      }

      if (lKern)
      {
        lBytesNow--;
      }
    }
    else
    {
      lBytesNow = lNumBytes;
    }

    lBytesNow = min(lBytesNow, 256L );

    /*
    ** Get the bounding box on the part of the string we will now show.
    ** Note that if this is our first show and we will be displaying
    ** the entire string, then we already have the bounding box info.
    ** On successive QueryTextBox's, hand in the error term and get
    ** the new error term on return. This will keep round off errors
    ** from accumulating.
    */
    if ((!bFirstShow) || (lBytesNow != lNumBytes))
    {
      if (!bFirstShow)
      {
        pddc->pddcb->text.fxErrorCurrentX = pddc->pddcb->text.fxErrorNewX;
        pddc->pddcb->text.fxErrorCurrentY = pddc->pddcb->text.fxErrorNewY;
      }
      prdt_QueryTextBox( hdc, lBytesNow, (PSZ) pchString, 4L, (PPOINTL) ptlBox,
                         pddc, FunN );

      /*
      ** make box coordinates absolute, not relative to start
      ** of string.
      */
      for (i = 0 ; i < 4 ; i++)
      {
        ptlBox[i].x += pddc->pddcb->pen.ptlCur.x;
        ptlBox[i].y += pddc->pddcb->pen.ptlCur.y;
      }
    }

    /*
    ** Convert the code points to the proper code page, do any needed
    ** backslash insertion, and show the substring. Do all of this, of
    ** course, only if the draw bit is turned on.
    */
    if ((FunN & COM_DRAW) && (pddc->pddcb->text.ChrBundle.usMixMode !=
       FM_LEAVEALONE))
    {
      lPrintBytes = prda_RemapString( pchString, lBytesNow, pddc );

      if (lPrintBytes == -1L)
      {
        GplErrSetError(  PMERR_BASE_ERROR );
        return( FAILURE );               /* couldn't get resources */
      }

      /*
      ** Set the start point and show it.
      */
      ps_movetoCPx( pddc, (PPOINTL) &pddc->pddcb->pen.ptlCur );
      ps_show( pddc, lPrintBytes, 1 );
    }
    bFirstShow = FALSE;               /* No long at the first show command */

    /*
    ** Now get ready to show the next portion of the string, if there
    ** is anything left.  This involves bumping the string pointer,
    ** reducing the byte count, and positioning the pen.
    */
    pchString += lBytesNow;
    lNumBytes -= lBytesNow;
    PrintLog( (PSZ)"After show, {x,y} = {%ld, %ld}\n",
              pddc->pddcb->pen.ptlCur.x, pddc->pddcb->pen.ptlCur.y );
    PrintLog( (PSZ)"Altering by  {%ld, %ld}\n", (ptlBox[3].x-ptlBox[1].x),
              (ptlBox[3].y-ptlBox[1].y) );

    /*
    ** Update the current point based on text we just showed
    ** we will adjust pddc->pddcb->pen.ptlCur directly a couple of
    ** times in the next few lines, but only do one ps_show
    ** to minimize postscript code generated.
    */
    pddc->pddcb->pen.ptlCur.x += ptlBox[TXTBOX_BOTTOMRIGHT].x -
      ptlBox [TXTBOX_BOTTOMLEFT].x;
    pddc->pddcb->pen.ptlCur.y += ptlBox[TXTBOX_BOTTOMRIGHT].y -
      ptlBox [TXTBOX_BOTTOMLEFT].y;

    /*
    ** Adjust for kerning if appropriate
    */
    if (bDoKerning && lNumBytes)
    {
      ptlXYtmp.x = CharStringKern( pddc, (SHORT)pchString[-1L],
                                   (SHORT) pchString[0L], sFirst, sLast );
      ptlXYtmp.y = 0L;
      prda_FontTransform( (PPOINTL) &ptlXYtmp, pddc );
      pddc->pddcb->pen.ptlCur.x += ptlXYtmp.x;
      pddc->pddcb->pen.ptlCur.y += ptlXYtmp.y;
    }
    ps_moveto( pddc, (PPOINTL) &pddc->pddcb->pen.ptlCur );
  }
  return( SUCCESS );
}

/***************************************************************************
 *
 * FUNCTION NAME = CharStringRightLeft
 *
 * DESCRIPTION   = if we are to print the text right-to-left and
 *                 without incremental vectors, we can use one or
 *                 a few show commands, packing several
 *                 characters into each show.  (ie:  it is not
 *                 necessary to do a separate show command for
 *                 each individual character.)
 *
 * INPUT         = (hdc, pddc, lNumBytes, pchString, ulOptions,
 *                  pscpAttibutes, FunN)
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

BOOL   CharStringRightLeft( HDC hdc, PDDC pddc, LONG lNumBytes, PCH pchString,
                            ULONG ulOptions, PCSP_INFO pscpAttributes,
                            ULONG FunN )
{
  POINTL ptlBox[5];
  LONG   lBytesNow;
  BOOL   bFirstShow = TRUE;
  LONG   i;
  LONG   lPrintBytes;
  POINTL ptlXYtmp;
  POINTL ptlSave;
  PCH    pchSave;
  LONG   lNumSave;

  /*
  ** get the size of the bounding text box
  */
  prdt_QueryTextBox( hdc, lNumBytes, (PSZ)pchString, 5L, (PPOINTL) ptlBox, pddc,
                     FunN );

  /*
  ** make box coordinates absolute, not relative to start
  ** of string. we also need to have the box move left, not right.
  */
  for (i = 0L ; i < 5L ; i++)
  {
    ptlBox[i].x += pddc->pddcb->pen.ptlCur.x;
    ptlBox[i].y += pddc->pddcb->pen.ptlCur.y;
  }

  /*
  ** Expand the bounds to reflect the display of the text
  */
  prdl_ExpandBounds( pddc, ptlBox[0].x, ptlBox[0].y );
  prdl_ExpandBounds( pddc, ptlBox[3].x, ptlBox[3].y );

  /*
  ** move the starting point to the left, since we will be drawing
  ** the text from left to right.
  */
  ps_movetoCPx( pddc, (PPOINTL) &ptlBox[4] );

  /*
  ** ptlSave is point we will reset current position to
  */
  ptlSave = ptlBox[4];

  /*
  ** save string pointer and byte count
  */
  pchSave  = pchString;
  lNumSave = lNumBytes;

  /*
  ** reverse the string, so we can print it backwards
  */
  CharStringReverse( lNumBytes, pchString );

  /*
  ** set up the text colors
  */
  SetTextColors( pddc, ulOptions, pscpAttributes, ptlBox, FunN );

  /*
  ** Do one show command at a time,
  ** until there is nothing left to show
  */
  while (lNumBytes)
  {
    /*
    ** limit the number of characters shown at a time to 256
    */
    lBytesNow = min( lNumBytes, 256L );

    /*
    ** Get the bounding box on the part of the string we will now show.
    ** Note that if this is our first show and we will be displaying
    ** the entire string, then we already have the bounding box info.
    ** On successive QueryTextBox's, hand in the error term and get
    ** the new error term on return. This will keep round off errors
    ** from accumulating.
    */
    if ((!bFirstShow) || (lBytesNow != lNumBytes))
    {
      if (!bFirstShow)
      {
        pddc->pddcb->text.fxErrorCurrentX = pddc->pddcb->text.fxErrorNewX;
        pddc->pddcb->text.fxErrorCurrentY = pddc->pddcb->text.fxErrorNewY;
      }
      prdt_QueryTextBox( hdc, lBytesNow, (PSZ)pchString, 5L, (PPOINTL) ptlBox,
                         pddc, FunN );

      /*
      ** make box coordinates absolute, not relative to start
      ** of string. we also need to have the box move left, not right.
      */
      for (i = 0L ; i < 5L ; i++)
      {
        ptlBox[i].x += pddc->pddcb->pen.ptlCur.x;
        ptlBox[i].y += pddc->pddcb->pen.ptlCur.y;
      }

      /*
      ** ptlSave is point we will reset current position to
      */
      ptlSave = ptlBox[4];
    }

    /*
    ** Convert the code points to the proper code page, do any needed
    ** backslash insertion, and show the substring. Do all of this, of
    ** course, only if the draw bit is turned on.
    */
    if ((FunN & COM_DRAW) && (pddc->pddcb->text.ChrBundle.usMixMode !=
         FM_LEAVEALONE))
    {
      lPrintBytes = prda_RemapString( pchString, lBytesNow, pddc );

      if (lPrintBytes == -1L)
      {
        GplErrSetError( PMERR_BASE_ERROR );
        return( FAILURE );               /* couldn't get resources */
      }

      /*
      ** Set the start point and show it.
      */
      ps_movetoCPx( pddc, (PPOINTL) & ptlSave );
      ps_show( pddc, lPrintBytes, 1 );
    }
    bFirstShow = FALSE;               /* No long at the first show command */

    /*
    ** Now get ready to show the next portion of the string, if there
    ** is anything left.  This involves bumping the string pointer,
    ** reducing the byte count, and positioning the pen.
    */
    pchString += lBytesNow;
    lNumBytes -= lBytesNow;
    pddc->pddcb->cgs.fUpdateCP = TRUE; /* force the moveto */
    ps_movetoCPx( pddc, (PPOINTL) & ptlSave );
  }

  /*
  ** reverse the string back, so nobody knows we messed with it
  */
  CharStringReverse( lNumSave, pchSave );
  return( SUCCESS );
}

/***************************************************************************
 *
 * FUNCTION NAME = CharStringUpDown
 *
 * DESCRIPTION   = prints text vertically, in either direction.
 *
 * INPUT         = (hdc, pddc, lNumBytes, pchString, ulOptions,
 *                  pscpAttibutes, usDirection, FunN)
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 *
 * @96713 - 12-13-94  Was not printing in the direction of shear.  Now
 *                    it is.  mjones
 **************************************************************************/

BOOL CharStringUpDown( HDC hdc, PDDC pddc, LONG lNumBytes, PCH pchString,
                       ULONG ulOptions, PCSP_INFO pscpAttributes,
                       SHORT usDirection, ULONG FunN)
{
  POINTL ptlBox[5];
  LONG   lBytesNow;
  BOOL   bFirstShow = TRUE;
  LONG   i;
  LONG   lPrintBytes;
  POINTL ptlXYtmp, ptlXYtmp2, ptlXYtmp3;
  FIXED  fxBytes;
  FIXED  fxTmp;

  /*
  ** set up the proper character height
  */

  ptlXYtmp2.x = 0;
  ptlXYtmp2.y = 1000;

  prda_FontTransform( (PPOINTL) &ptlXYtmp2, pddc );

  /*
  ** make sure we move the correct way.
  */
  if (usDirection == CHDIRN_TOPBOTTOM)
  {
    ptlXYtmp2.x  *= -1;
    ptlXYtmp2.y  *= -1;
  }


  /*
  ** we have to adjust vertical text by the max descender for
  ** some reason.  see appendix M of the GAD or some such thing
  */

   ptlXYtmp3.x = 0;
   ptlXYtmp3.y = pddc->pddcb->text.pfmFontMetrics->lMaxDescender;
   prda_FontTransform( (PPOINTL) &ptlXYtmp3, pddc );

   ptlXYtmp.x = ptlXYtmp3.x + pddc->pddcb->pen.ptlCur.x;
   ptlXYtmp.y = ptlXYtmp3.y + pddc->pddcb->pen.ptlCur.y;


  if (usDirection == CHDIRN_TOPBOTTOM)
  {
    ptlXYtmp.x += ptlXYtmp2.x;
    ptlXYtmp.y += ptlXYtmp2.y;
  }


  ps_movetoCPx( pddc, &ptlXYtmp );

  /*
  ** set up the text colors
  */
  SetTextColors( pddc, ulOptions, pscpAttributes, ptlBox, FunN );

  /*
  ** Do one show command at a time,
  ** until there is nothing left to show
  */
  while (lNumBytes)
  {
    /*
    ** limit the number of characters shown at a time to 256
    */
    lBytesNow = min( lNumBytes, 256L );

    /*
    ** Convert the code points to the proper code page, do any needed
    ** backslash insertion, and show the substring. Do all of this, of
    ** course, only if the draw bit is turned on.
    */
    if ((FunN & COM_DRAW) && (pddc->pddcb->text.ChrBundle.usMixMode !=
        FM_LEAVEALONE))
    {
      lPrintBytes = prda_RemapString( pchString, lNumBytes, pddc );

      if (lPrintBytes == -1L)
      {
        GplErrSetError(  PMERR_BASE_ERROR );
        return( FAILURE );               /* couldn't get resources */
      }

      ps_movetoCPx( pddc, (PPOINTL) & pddc->pddcb->pen.ptlCur );

      PrintChannel( pddc, (PSZ)"/updn {/dy exch def /dx exch def /upC ( ) def { gs\n" );
      PrintChannel( pddc,
                    (PSZ) "upC 0 3 -1 roll put upC stringwidth 2 div neg exch 2 div\n" );
      PrintChannel( pddc,
                    (PSZ) "neg exch rmoveto upC show gr dx dy rmoveto } forall\n" );
      PrintChannel( pddc, (PSZ)"} bind def\n" );

      /*
      ** Output the actual string to be printed
      */
      PrintChannel( pddc, (PSZ)"(" );

      for (i = 0 ; i < lBytesNow ; i++)
      {
        PrintChannel( pddc, (PSZ)"%c", pddc->pdv->chText[i] );
      }
      PrintChannel( pddc, (PSZ)") %ld %ld updn\n",  ptlXYtmp2.x, ptlXYtmp2.y);
    }
    bFirstShow = FALSE;               /* No long at the first show command */

    /*
    ** Now get ready to show the next portion of the string, if there
    ** is anything left.  This involves bumping the string pointer,
    ** reducing the byte count, and positioning the pen.
    */
    pchString += lBytesNow;
    lNumBytes -= lBytesNow;

    /*
    ** Expand the bounds to reflect the display of the text
    */
    prdl_ExpandBounds( pddc, ptlBox[0].x, ptlBox[0].y );
    prdl_ExpandBounds( pddc, ptlBox[3].x, ptlBox[3].y );

    /*
    ** Update the current point based on text we just showed
    */
    fxBytes = LongToFx( lBytesNow );

    if (usDirection == CHDIRN_TOPBOTTOM)
    {
      fxBytes -= FX_ONE;
    }

    pddc->pddcb->pen.ptlCur.x += ptlXYtmp2.x * FxToLong(fxBytes) - ptlXYtmp3.x;
    pddc->pddcb->pen.ptlCur.y += ptlXYtmp2.y * FxToLong(fxBytes) - ptlXYtmp3.y;

    ps_moveto( pddc, (PPOINTL) &pddc->pddcb->pen.ptlCur );
  }
  return( SUCCESS );
}

/***************************************************************************
 *
 * FUNCTION NAME = CharStringReverse
 *
 * DESCRIPTION   = this routine reverses lNumBytes characters in the string
 *                 pointed to by pchString.
 *
 * INPUT         = (lNumBytes, pchString)
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

VOID CharStringReverse( LONG lNumBytes, PCH pchString )
{
  PCH  pchStart;
  PCH  pchEnd;
  LONG i, j;
  BYTE bTmp;

  pchStart = pchString;
  pchEnd = pchString+lNumBytes - 1L;
  j = lNumBytes / 2L;

  for (i = 0 ; i <= j ; i++)
  {
    bTmp = *pchStart;
    *pchStart = *pchEnd;
    *pchEnd = bTmp;
    *pchStart++;
    *pchEnd--;
  }
}
