/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT (C) Microsoft Corporation, 1989                                 */
/* COPYRIGHT    Copyright (C) 1995 IBM Corporation                           */
/*                                                                           */
/*    The following IBM OS/2 WARP source code is provided to you solely for  */
/*    the purpose of assisting you in your development of OS/2 WARP device   */
/*    drivers. You may use this code in accordance with the IBM License      */
/*    Agreement provided in the IBM Device Driver Source Kit for OS/2. This  */
/*    Copyright statement may not be removed.                                */
/*                                                                           */
/*****************************************************************************/
#pragma  pagesize(55)
/**************************************************************************
 *
 * SOURCE FILE NAME = TEXTOUT.C
 *
 * DESCRIPTIVE NAME = OS/2 Plotter Presentation Driver Text Output
 *                    Functions
 *
 *
 * VERSION = V2.0
 *
 * DATE      12/12/88
 *
 * DESCRIPTION Text related output functions ... internal and external
 *
 *
 * FUNCTIONS
 *             set_cell_size
 *             set_cell_shear
 *             set_char_angle
 *             string_clip
 *             clip_char
 *             calc_QTB
 *             adjust_char_attrs
 *             CharString
 *             CharStringPos
 *             GetCodePage
 *             GetPairKerningTable
 *             SetCodePage
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*//*
**  LISTOFF
**     code file post-processor directive
*/

#include "plotters.h"
#include "clip.h"
#include "color.h"
#include "dispatch.h"
#include "dosio.h"
#include "error.h"
#include "font.h"
#include "output.h"
#include "textout.h"
#include "utils.h"
#include "xforms.h"
#include "pmfont.h"
#include "prdmath.h"
#include "chargen.h"
#include "bounds.h"
#include "lockddc.h"

/*
**  LISTON
**       code file post-processor directive
*/

/*
** Local data strings
*/
CHAR vszBackSpace[] = { '\b', '\0' };

/*
** Local functions
*/
LOCAL VOID  set_char_italic_bold( PDDC, USHORT );
LOCAL VOID  set_cell_size ( HDC, ULONG, PSIZEF, PDDC );
LOCAL VOID  set_cell_shear ( PDDC, PPOINTL );
LOCAL VOID  set_char_angle ( PDDC, PPOINTL );
LOCAL VOID  string_clip ( PRECTL, PPOINTL, USHORT, PSHORT,
                          PSHORT );
LOCAL VOID  clip_char ( HDC, UCHAR, USHORT, PRECTL,
                        PPOINTL, BOOL, PDDC, PQCINFO );
LOCAL VOID  calc_QTB ( HDC, PRECTL, UCHAR, PDDC, ULONG );
LOCAL VOID  adjust_char_attrs ( HDC, PDDC, PPOINTS, PSIZEF, PPOINTL,
                                ULONG );

/***************************************************************************
 *
 * FUNCTION NAME = set_char_italic_bold
 *
 * DESCRIPTION   = set character attribute -- Italic and or Bold
 *
 * INPUT         = pDDC      - pointer to device driver context
 *                 USHORT fFlags_cdef - Cdef flags from char attributes
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

LOCAL VOID  set_char_italic_bold( PDDC pDDC, USHORT fFlags_cdef )
{
  PPDEVICE pPDevice;

  pPDevice = pDDC->pPDevice;

  if (pPDevice->usHPGLType & HPGL2)
  {
    /*
    ** if the character attribute structure had the Italic bit set
    ** and the device is not in Italic mode
    ** set the device to italic.
    */
    if (fFlags_cdef & CDEF_ITALIC &&
        !(pPDevice->fFlags_cdef & CDEF_ITALIC))
    {
      output_bytes(pDDC, "SD5,1");
      pPDevice->fFlags_cdef |= CDEF_ITALIC;
    }
    /*
    ** else if the character attribute structure did not have the
    ** Italic bit set
    ** and the device is in Italic mode
    ** set the device to non italic.
    */
    else if (!(fFlags_cdef & CDEF_ITALIC) &&
          pPDevice->fFlags_cdef & CDEF_ITALIC)
    {
      output_bytes(pDDC, "SD5,0");
      pPDevice->fFlags_cdef &= (~CDEF_ITALIC);
    }

    /*
    ** if the character attribute structure had the BOLD bit set
    ** and the device is not in bold mode
    ** set the device to bold.
    */
    if (fFlags_cdef & CDEF_BOLD &&
        !(pPDevice->fFlags_cdef & CDEF_BOLD))
    {
      output_bytes(pDDC, "SD6,3");
      pPDevice->fFlags_cdef |= CDEF_BOLD;
    }
    /*
    ** else if the character attribute  structure
    ** did not have the BOLD bit set
    ** and the device is in BOLD mode
    ** set the device to non bold.
    */
    else if (!(fFlags_cdef & CDEF_BOLD) &&
          pPDevice->fFlags_cdef & CDEF_BOLD)
    {
      output_bytes(pDDC, "SD6,0");
      pPDevice->fFlags_cdef &= (~CDEF_BOLD);
    }
  }

  return ;
}

/***************************************************************************
 *
 * FUNCTION NAME = set_cell_size
 *
 * DESCRIPTION   = set character attribute -- cell size
 *
 * INPUT         = hdc - DC handle
 *                 FunN - COM_FLAGS
 *                 pCharCell - pointer to character cell
 *                 pDDC      - pointer to device driver context
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 *************************************************************************  */

LOCAL VOID  set_cell_size ( HDC hDC, ULONG FunN,
                            PSIZEF pCharCell, PDDC pDDC)
{
  PPDEVICE pPDevice;
  LONG     lHeight;
  LONG     ulWidth;
  //LONG     ulScale;
  BOOL     bNegative = FALSE;

  pPDevice = pDDC->pPDevice;

  if (pPDevice->CharCell.cx != pCharCell->cx || pPDevice->CharCell.cy !=
     pCharCell->cy)
  {


    LONG  lScale = pPDevice->lIWScale;
    LONG  lMultiplier = pPDevice->lIWScaleMultiplier;
    BOOL  bHpgl2 = (pPDevice->usHPGLType & HPGL2);
    RECTL rectl;

    calc_QTB(hDC, &rectl, (UCHAR)'W', pDDC, FunN);

    /*
    **     Convert plotter units into cm.  NOTE:  A
    **  scaling operation is done here - the width and
    **  height are set to 2/3 of the character cell size,
    **  since the proper spacing between characters
    **  requires the spacing to be 1.5 times the width
    **  of the character.
    */
    output_bytes(pDDC, "SI");

    /*
    ** NOTE:SI uses Floating point numbers in CM.
    **      Also SI Height does not Include the decenders
    ** Character width =
    ** 1.Make char Width and height 7/8 real size to account
    **   for internal leading.
    ** 2.Make char height and Width 3/4 real size to account for decenders.
    ** 3.Convert to CM
    ** 4.Make char width 2/3 real size.
    **   NOTE: 3/4 must match the decender size 1/4 in enable.c
    **         7/8 must match the internal leading size 1/8 in enable.c
    */
    /*
    ** -1 because  the QTB clip rectangle is 1 larger
    */
    ulWidth = (rectl.xRight - rectl.xLeft -1);
    if (pCharCell->cx < 0)
    {
      bNegative = TRUE;
    }

    /*
    ** apply scale (pels to plotter units)
    */
    ulWidth *= lScale;

    /*
    ** There are 400 plotter units per CM
    ** Divide by 4 to get 2 decimal places
    */
    ulWidth = (ulWidth / 4L);

    /*
    ** Width is set to 2/3 the box size.
    ** the actual character increment will be the
    ** full size.
    */
    ulWidth = (ulWidth * 2L) / 3L;
    construct_float(pDDC, bNegative ? -ulWidth : ulWidth, lMultiplier * 100L);

    output_comma(pDDC);


    if (pCharCell->cy < 0)
    {
      bNegative = TRUE;
    }
    lHeight = (LONG)(rectl.yTop - rectl.yBottom - 1);
    /*
    ** .65625 = 7/8 * 3/4
    */
    lHeight = (lHeight * 65625) / 100000L;        /* adj for Leading+decender */
    lHeight *= lScale;
    lHeight -= 1; /* temp fix for 7375 clipping bottom of a "g" */
    lHeight = (lHeight / 400L);
    construct_float(pDDC, bNegative ? -lHeight : lHeight, lMultiplier);

    pPDevice->CharCell = *pCharCell;
  }

  return ;
}

/***************************************************************************
 *
 * FUNCTION NAME = set_cell_shear
 *
 * DESCRIPTION   = set character attribute -- shear angle
 *
 * INPUT         = pDDC      - pointer to device driver context
 *                 pptlShear - pointer to shear angle
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 *************************************************************************  */

LOCAL VOID  set_cell_shear ( PDDC pDDC, PPOINTL pptlShear )
{
  LONG Tangent;
  PPDEVICE pPDevice;

  pPDevice = pDDC->pPDevice;

  if (pPDevice->CharShear.x != (SHORT)pptlShear->x ||
      pPDevice->CharShear.y != (SHORT)pptlShear->y)
  {
    Tangent = (pDDC->DCState.dcbnd.cbnd.ptlShear.x * 1000L) /
                  pDDC->DCState.dcbnd.cbnd.ptlShear.y;
    output_bytes(pDDC, "SL");

    if (Tangent)
      construct_float(pDDC, Tangent, 1000L);

    pPDevice->CharShear.x = (SHORT)pDDC->DCState.dcbnd.cbnd.ptlShear.x;
    pPDevice->CharShear.y = (SHORT)pDDC->DCState.dcbnd.cbnd.ptlShear.y;
  }

  return ;
}

/***************************************************************************
 *
 * FUNCTION NAME = set_cell_angle
 *
 * DESCRIPTION   = set character attribute -- angle
 *
 * INPUT         = pDDC      - pointer to device driver context
 *                 pptlAngle - pointer to angle
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 *************************************************************************  */

LOCAL VOID  set_char_angle ( PDDC pDDC, PPOINTL pptlAngle )
{
  PPDEVICE pPDevice;

  pPDevice = pDDC->pPDevice;

  if (pPDevice->CharAngle.x != (SHORT)pptlAngle->x ||
      pPDevice->CharAngle.y != (SHORT)pptlAngle->y)
  {
    pPDevice->CharAngle.x = (SHORT)pptlAngle->x;
    pPDevice->CharAngle.y = (SHORT)pptlAngle->y;
    output_bytes(pDDC, "DI");

    if (pPDevice->CharAngle.x == 0 && pPDevice->CharAngle.y == 0)
    {
      if (pPDevice->fsTransform & DXF_PORTRAIT)
        output_bytes(pDDC, "0,-1");
    }
    else
    {
      POINTS Pts;

      Pts = pPDevice->CharAngle;

      if (pPDevice->fsTransform & DXF_PORTRAIT)
      {
        SHORT Temp = Pts.x;

        Pts.x = Pts.y;
        Pts.y = -Temp;
      }
      output_number(pDDC, (LONG)Pts.x);
      output_comma(pDDC);
      output_number(pDDC, (LONG)Pts.y);
    }
  }

  return ;
}

/***************************************************************************
 *
 * FUNCTION NAME = adjust_char_attrs
 *
 * DESCRIPTION   = adjust the current character size and angles using
 *                 the currently defined transforms.
 *
 * INPUT         = hDC           - device context handle
 *                 pDDC          - pointer to device driver context
 *                 pptsCharSize  - pointer to character size
 *                 psfCharCell   - pointer to character cell
 *                 pptlCharAngle - pointer to character angle
 *                 FunN          - function number
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *                                                               
 *                                                               
 *
 **************************************************************************/

LOCAL VOID  adjust_char_attrs ( HDC hDC, PDDC pDDC,
                                PPOINTS pptsCharSize,
                                PSIZEF psfCharCell,
                                PPOINTL pptlCharAngle,
                                ULONG FunN )
/*
** NOTE:
** -----
** The values calculated here MUST match those used by
** QueryCharPositions.  The value here will be converted to cm
** and output to the plotter from set_cell_size.  QueryCharPositions
** needs that same value in world coordinate space in order that
** the driver and plotter stay in sync.
**
*/

#define  MAX_CONVERT_VALUE (ULONG)((1 << 28) - 1)
#define  OVERFLOW_SCALE 16
{
  //psfCharCell->cx = (pDDC->DCState.dcbnd.cbnd.sizfxCell.cx/ASPECT_D)*ASPECT_M;
  psfCharCell->cx  = pDDC->DCState.dcbnd.cbnd.sizfxCell.cx;
  psfCharCell->cy  = pDDC->DCState.dcbnd.cbnd.sizfxCell.cy;
  pptlCharAngle->x = pDDC->DCState.dcbnd.cbnd.ptlAngle.x;
  pptlCharAngle->y = pDDC->DCState.dcbnd.cbnd.ptlAngle.y;

  if (pptlCharAngle->x == 0 && pptlCharAngle->y == 0)
  {
    pptlCharAngle->x = 50;
  }

  if (FunN & COM_TRANSFORM)
  {
    SIZEF sfBox[3];
    FIXED fxSqrX,fxSqrY;
    BOOL fTooBig;
    USHORT i;

    sfBox[0].cx = 0;
    sfBox[0].cy = 0;
    sfBox[1].cx = psfCharCell->cx;
    sfBox[1].cy = 0;
    sfBox[2].cx = psfCharCell->cx;
    sfBox[2].cy = psfCharCell->cy;

    if (fTooBig = (ABS(sfBox[1].cx) > MAX_CONVERT_VALUE ||
                   ABS(sfBox[2].cx) > MAX_CONVERT_VALUE ||
                   ABS(sfBox[2].cy) > MAX_CONVERT_VALUE))
    {
      sfBox[1].cx /= OVERFLOW_SCALE;
      sfBox[2].cx /= OVERFLOW_SCALE;
      sfBox[2].cy /= OVERFLOW_SCALE;
    }

    /*
    ** Following modified for PTR B730630 by SteveU.  Fixed point
    ** values were being passed to GreConvert, which handles long
    ** values.  In some cases, this caused the plotters to take on
    ** incorrect char sizes.
    */
    for (i = 0; i < 3; i++)
    {                                  /* round to long value               */
      sfBox[i].cx = (sfBox[i].cx+32768L)/65536L;
      sfBox[i].cy = (sfBox[i].cy+32768L)/65536L;
    }                                  /* endfor                            */
    convert_world_to_device(hDC, (PPOINTL)sfBox, 3L, pDDC);

    for (i = 0; i < 3; i++)
    {                                  /* convert back to fixed value       */
      sfBox[i].cx *= 65536L;
      sfBox[i].cy *= 65536L;
    }                                  /* endfor                            */

    sfBox[1].cx -= sfBox[0].cx;
    sfBox[1].cy -= sfBox[0].cy;
    sfBox[2].cx -= sfBox[0].cx;
    sfBox[2].cy -= sfBox[0].cy;

    if (fTooBig)
    {
      sfBox[1].cx *= OVERFLOW_SCALE;
      sfBox[1].cy *= OVERFLOW_SCALE;
      sfBox[2].cx *= OVERFLOW_SCALE;
      sfBox[2].cy *= OVERFLOW_SCALE;
    }

    if (pDDC->DCState.usXfData & XF_ROTATION)
    {
      XFORM xfRot;
      INT iScl;

      fxSqrX = sfBox[1].cx;
      fxSqrY = sfBox[1].cy;
      iScl = 0;

      while (ABS(fxSqrX) > MAKEFIXED(181, 0) ||
             ABS(fxSqrY) > MAKEFIXED(181, 0))
      {
        iScl++;
        fxSqrX /= 2;
        fxSqrY /= 2;
      }

      fxSqrX = frmul(fxSqrX, fxSqrX);
      fxSqrY = frmul(fxSqrY, fxSqrY);
      psfCharCell->cx = (frsqrt(fxSqrX+fxSqrY) << iScl) *
                         ((psfCharCell->cx < 0) ? -1 : 1);
      fxSqrX = sfBox[2].cx;
      fxSqrY = sfBox[2].cy;
      iScl = 0;

      while (ABS(fxSqrX) > MAKEFIXED(181, 0) ||
             ABS(fxSqrY) > MAKEFIXED(181, 0))
      {
        iScl++;
        fxSqrX /= 2;
        fxSqrY /= 2;
      }
      fxSqrX = frmul(fxSqrX, fxSqrX);
      fxSqrY = frmul(fxSqrY, fxSqrY);
      psfCharCell->cy = (frsqrt(fxSqrX+fxSqrY) << iScl) *
                        ((psfCharCell->cy < 0) ? -1 : 1);
      get_angle_xform(&xfRot, &pDDC->DCState.ptlRotation);

      /*
      **  If for instance, the angle is (1,0), the transformed angle can
      **    contain coords both less than 1.  Truncation yields (0,0).
      **    Following shift insures that all significant digits are
      **    preserved.
      **                                                                
      */
      while (ABS(pptlCharAngle->x) < 1000 && ABS(pptlCharAngle->y) < 1000)
      {
        pptlCharAngle->x *= 2;
        pptlCharAngle->y *= 2;
      }

      (*daConvertWithMatrix)(hDC, (PPOINTL)pptlCharAngle, 1L,
                            (PXFORM)&xfRot, pDDC,
                            NGreConvertWithMatrix);
    }
    else
    {
      *psfCharCell = sfBox[2];
    }
  }
  pptsCharSize->x = (SHORT)((psfCharCell->cx+32768L)/65536L);
  pptsCharSize->y = (SHORT)((psfCharCell->cy+32768L)/65536L);

  /*
  **  Reduce angle to terms acceptable to HPGL
  */
  while (ABS(pptlCharAngle->x) >= 128 || ABS(pptlCharAngle->y) >= 128)
  {
    pptlCharAngle->x /= 2;
    pptlCharAngle->y /= 2;
  }
}

/***************************************************************************
 *
 * FUNCTION NAME = string_clip
 *
 * DESCRIPTION   = determine the clip limits of the supplied string
 *                 information
 *
 * INPUT         = pClipRect - clipping rectangle
 *                 pPts      - points of interest
 *                 usDir     - direction string is written
 *
 * OUTPUT        = psStart   - start of clipped string
 *                 psEnd     - end of clipped string
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 *************************************************************************  */

LOCAL VOID  string_clip ( PRECTL pClipRect, PPOINTL pPts,
                          USHORT usDir, PSHORT psStart,
                          PSHORT psEnd )
{
  SHORT sStart,
        sEnd  ;
  SHORT sCount;

  sCount = *psEnd;

  switch (usDir)
  {
    case  CHDIRN_LEFTRIGHT :
      for (sStart = 1; sStart < sCount; ++sStart)
      {
        if (pPts[sStart].x > pClipRect->xLeft)
          break;
      }

      sStart--;

      for (sEnd = sCount-1; sEnd >= sStart; --sEnd)
      {
        if (pPts[sEnd].x < pClipRect->xRight)
          break;
      }

      sEnd++;
      break;
    case  CHDIRN_RIGHTLEFT :
      for (sStart = 0; sStart < sCount; ++sStart)
      {
        if (pPts[sStart].x < pClipRect->xRight)
          break;
      }

      for (sEnd = sCount-1; sEnd >= sStart; --sEnd)
      {
        if (pPts[sEnd].x > pClipRect->xLeft)
          break;
      }
      sEnd++;
      break;
    case  CHDIRN_BOTTOMTOP :
      for (sStart = 1; sStart < sCount; ++sStart)
      {
        if (pPts[sStart].y > pClipRect->yBottom)
          break;
      }

      sStart--;

      for (sEnd = sCount-1; sEnd >= sStart; --sEnd)
      {
        if (pPts[sEnd].y < pClipRect->yTop)
          break;
      }
      sEnd++;
      break;
    case  CHDIRN_TOPBOTTOM :
      for (sStart = 0; sStart < sCount; ++sStart)
      {
        if (pPts[sStart].y < pClipRect->yTop)
          break;
      }

      for (sEnd = sCount-1; sEnd >= sStart; --sEnd)
      {
        if (pPts[sEnd].y > pClipRect->yBottom)
          break;
      }
      sEnd++;
      break;
  }
  *psStart = sStart;
  *psEnd = sEnd;

  return ;
}

/***************************************************************************
 *
 * FUNCTION NAME = clip_char
 *
 * DESCRIPTION   = function to handle complex clipping of a single
 *                 character
 *
 * INPUT         = hDC       - device context handle
 *                 uChar     - character to be clipped
 *                 usMode    - controls construct_char
 *                 prClip    - clipping rectangle
 *                 pptlStart - Character starting position
 *                 bPartChar - clipping rectangle
 *                 pDDC      - pointer to device driver context
 *                 pQC       - quad clip information
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *         
 *                                                               
 *
 **************************************************************************/

LOCAL VOID  clip_char (
                        HDC     hDC,
                        UCHAR   uChar,     /* The character to be mangled       */
                        USHORT  usMode,    /* Controls construct_char           */
                        PRECTL  prClip,    /* Clipping rectangle, if not zero   */
                        PPOINTL pptlStart, /* Character starting position */
                        BOOL    bPartChar, /*
                                           ** If TRUE, MUST apply clipping
                                           ** rectangle
                                           */
                        PDDC    pDDC,
                        PQCINFO pQC )      /* Quad Clip Information             */
{

  /*
  **    Apply all clipping rectangles to this character.   Basically keep
  **  drawing it until all clipping rectangles have been applied.  This
  **  may not produce very nice output on the plotter, but there is no
  **  alternative that I have been able to think of!
  */
  PPDEVICE pPDevice = pDDC->pPDevice;
  USHORT usIndex;                      /* Big rectangle counts              */
  RECTL rBigClip;                      /* What we get from
                                          merge_clip_rect()                 */
  BOOL bOutput;                        /* Set if data is output             */
  USHORT usRet;                        /* Value returned from
                                          merge_clip_rect()                 */
  BOOL bStart = TRUE;                  /* quad clip init scan control       */
  RECTL rctlQCBox;                     /* current y scan rect               */
  RECTL rctlQCBoxInt;                  /* intersection with user clip box   */

  usIndex = 1;                         /* Starting value for GetClipRects   */
  bOutput = FALSE;

  if (pQC->bEnabled)                   /* quad clip active ?                */
  {

    /*
    ** We are being asked to clip to an arbitrary quadrilateral (ie:
    ** a viewer transformed rectangle).  First we will reduce the area to
    ** be examined by intersecting with the clipping region, and if we
    ** are lucky the character cell will fit entirely within the region.
    ** If not,  we will break the cell down into a series of rectangular
    ** fragments and draw the character repeatedly using the fragments as
    ** the plotters clip box.
    */

    while ((usRet = merge_clip_rect(hDC, &rBigClip, prClip,
                                    &usIndex, pDDC)) != MCR_NONE)
    {
      if ((!bOutput) && (usRet == MCR_WHOLE) && (!bPartChar))
      /*
      **  The clip region encompasses the entire character so we draw it
      **  all in one shot...  WHAT LUCK!!!
      */
      {
        ///*
        //** We must backspace before terminating the label on
        //** HPGL2 devices.  This will also work on older devices. */
        //** ??Does not work???
        //*/
        //if (usMode & CC_BACKSPACE && /* Back up for remainder of char     */
        //    pPDevice->bStringInConstruction)
        //{
        //  output_bytes(pDDC, vszBackSpace);
        //  usMode = usMode & ~CC_BACKSPACE;/* Turn off-we just backed up   */
        //}

        output_clip_rect(pDDC, -1L, &rBigClip);/* Disable clip             */
        bOutput = TRUE;
        /*
        ** We must backspace by using The CP command for fixed space fonts
        ** or by setting the current position for proportional fonts on
        ** HPGL2 devices.
        ** CP-1,0 = backup 1 character vector.
        */
        if (usMode & CC_BACKSPACE &&
            pPDevice->usHPGLType & HPGL2)
        {
          check_string_construction(pDDC);
          output_bytes(pDDC, "CP-1,0");
          usMode = usMode & ~CC_BACKSPACE;/* Turn off-we just backed up   */
        }

        if (!construct_char(pDDC, uChar, usMode))
          break;                       /*       clipping a blank!           */
        usMode |= CC_BACKSPACE;        /* Back up for remainder of char     */
      }
      else
      {
        /*
        ** Since we don't have a perfect fit, part of the character must
        ** be clipped away.  get_quad_clip will supply us with a series of
        ** rectangles to be used as the plotters clip limits.
        */
        while (get_quad_clip(pQC, &rctlQCBox, bStart))
        {

          /*
          ** Since the rectangles returned by get_quad_clip may extend
          ** outside the clipping region, we now intersect with that
          ** region then send the result and the character to the plotter.
          */
          rectl_intersect(&rctlQCBoxInt, &rctlQCBox, &rBigClip);
          bStart = FALSE;

          ///*
          //** We must backspace before terminating the label on
          //** HPGL2 devices.  This will also work on older devices.
          //*/
          //if (usMode & CC_BACKSPACE && /* Back up for remainder of char     */
          //    pPDevice->bStringInConstruction)
          //{
          //  output_bytes(pDDC, vszBackSpace);
          //  usMode = usMode & ~CC_BACKSPACE;/* Turn off-we just backed up   */
          //}

          output_clip_rect(pDDC, 1L, &rctlQCBoxInt);
          bOutput = TRUE;
          /*
          ** We must backspace by using The CP command for fixed space fonts
          ** or by setting the current position for proportional fonts on
          ** HPGL2 devices.
          ** CP-1,0 = backup 1 character vector.
          */
          if (usMode & CC_BACKSPACE &&
              pPDevice->usHPGLType & HPGL2)
          {
            check_string_construction(pDDC);
            output_bytes(pDDC, "CP-1,0");
            usMode = usMode & ~CC_BACKSPACE;/* Turn off-we just backed up   */
          }

          if (!construct_char(pDDC, uChar, usMode))
            break;                     /*       clipping a blank!           */
          usMode |= CC_BACKSPACE;      /* Back up for remainder of char     */
        }
      }
    }

    /*
    ** NOTE: outputting a space for characters not printed
    ** will not work for proportional spaced fonts. When
    ** we support them.
    */
    if (!bOutput)
      construct_char(pDDC, (UCHAR)' ', usMode);

    return ;
  }

   /*
   ** Under these circumstances (not pQC->bEnabled) we have a relatively
   ** simple task to accomplish; merely clipping to the existing clip
   ** region.  To do so we call merge_clip_rect repeatedly sending the
   ** required clip limits and the character to clip, to the plotter.
   */

  while ((usRet = merge_clip_rect(hDC, &rBigClip,
                                  prClip, &usIndex,
                                  pDDC)) != MCR_NONE)
  {
    ///*
    //** We must backspace before terminating the label on
    //** HPGL2 devices.  This will also work on older devices.
    //*/
    //if (usMode & CC_BACKSPACE && /* Back up for remainder of char     */
    //    pPDevice->bStringInConstruction)
    //{
    //  output_bytes(pDDC, vszBackSpace);
    //  usMode = usMode & ~CC_BACKSPACE;/* Turn off-we just backed up   */
    //}

    if ((!bOutput) && (usRet == MCR_WHOLE) && (!bPartChar))
    {
      /*
      **  if the clip region encompasses the entire character we draw it
      **  all in one shot...
      */
      output_clip_rect(pDDC, -1L, &rBigClip);
    }
    else
    {
      /*
      ** Since we don't have a perfect fit, part of the character must
      ** be clipped away.  Each rectangle returned supplies us with
      ** clip limits which we now send to the plotter.
      */
      output_clip_rect(pDDC, 1L, &rBigClip);
    }
    bOutput = TRUE;

    /*
    ** We must backspace by using The CP command for fixed space fonts
    ** or by setting the current position for proportional fonts on
    ** HPGL2 devices.
    ** CP-1,0 = backup 1 character vector.
    */
    if (usMode & CC_BACKSPACE &&
        pPDevice->usHPGLType & HPGL2)
    {
      output_bytes(pDDC, "CP-1,0");
      usMode = usMode & ~CC_BACKSPACE;/* Turn off-we just backed up   */
    }

    if (!construct_char(pDDC, uChar, usMode))
      break;
    usMode |= CC_BACKSPACE;
  }

  if (!bOutput)
    construct_char(pDDC, (UCHAR)' ', usMode);

  return ;
}

/***************************************************************************
 *
 * FUNCTION NAME = calc_QTB
 *
 * DESCRIPTION   = compute the character cell size for clipping purposes
 *
 * INPUT         = hDC       - device context handle
 *                 prQTB     - query text box
 *                 uchChar   - Character
 *                 pDDC      - pointer to device driver context
 *                 usFunN    - function number
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

LOCAL VOID  calc_QTB( HDC    hDC,
                      PRECTL prQTB,
                      UCHAR  uscChar,
                      PDDC   pDDC,
                      ULONG  ulFunN)
{
#define  P0            4

  USHORT usDirection;
  POINTL ptlQTB[5];
  CHAR   szString[2];

  szString[0] = uscChar;
  szString[1] = '\0';

  usDirection = pDDC->DCState.dcbnd.cbnd.usDirection;
  pDDC->DCState.dcbnd.cbnd.usDirection = CHDIRN_LEFTRIGHT;

  if (QueryTextBox(hDC, 1L, szString, 4L, ptlQTB, pDDC,
                   MAKEULONG(NGreQueryTextBox, HIUSHORT(ulFunN))) != GPI_OK)
  {
    pDDC->DCState.dcbnd.cbnd.usDirection = usDirection;
    prQTB->xLeft                 = 0;
    prQTB->xRight                = 1;
    prQTB->yBottom               = 0;
    prQTB->yTop                  = 1;

    return ;
  }

  pDDC->DCState.dcbnd.cbnd.usDirection = usDirection;
  ptlQTB[P0].x                 = 0;
  ptlQTB[P0].y                 = 0;

  if (ulFunN & COM_TRANSFORM)
    convert_world_to_device(hDC, ptlQTB, 5L, pDDC);

  /*
  ** Since we are going to return a clip rectangle
  ** we must return a rectangle which is inclusive on the
  ** bottom left and exclusive on the top right.
  ** Add 1 to the top right.
  */
  prQTB->xLeft   = MIN(ptlQTB[TXTBOX_TOPLEFT].x,
                       ptlQTB[TXTBOX_BOTTOMLEFT].x)  - ptlQTB[P0].x;
  prQTB->xRight  = MAX(ptlQTB[TXTBOX_TOPRIGHT].x,
                       ptlQTB[TXTBOX_BOTTOMRIGHT].x) + 1 - ptlQTB[P0].x;
  prQTB->yBottom = MIN(ptlQTB[TXTBOX_BOTTOMLEFT].y,
                       ptlQTB[TXTBOX_BOTTOMRIGHT].y) - ptlQTB[P0].y;
  prQTB->yTop    = MAX(ptlQTB[TXTBOX_TOPLEFT].y,
                       ptlQTB[TXTBOX_TOPRIGHT].y) + 1  - ptlQTB[P0].y;
  return ;
}

/*
** þ ENGINE FUNCTIONS þþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ
*/
/***************************************************************************
 *
 * FUNCTION NAME = CharString
 *
 * DESCRIPTION   = Output a character string, starting at the current
 *                 position.
 *
 * INPUT         = hDC       - device context handle
 *                 cChars    - number of characters
 *                 pString   - string to write
 *                 pDDC      - pointer to device driver context
 *                 ulFunN    - function number
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 *************************************************************************  */

LONG  CharString ( HDC hDC, LONG cChars, PCH pString, PDDC pDDC,
                   ULONG ulFunN )
{
  LONG lResult;
  BOOL bTempDoFills;
  BOOL bTempDrawingText;


  if(!(EnterDriver(pDDC)))
    return(GPI_ERROR);
  if (pDDC->DCState.dcbnd.cdef.fFlags & CDEF_GENERIC)
  /*
  ** engine font, let the engine do the work
  */
  {
    bTempDoFills               = pDDC->DCState.bDoFills;
    pDDC->DCState.bDoFills     = FALSE;
    bTempDrawingText           = pDDC->DCState.bDrawingText;
    pDDC->DCState.bDrawingText = TRUE;
    lResult = (*daCharString)(hDC, cChars, pString,
                              pDDC, ulFunN);
    pDDC->DCState.bDrawingText = bTempDrawingText;
    pDDC->DCState.bDoFills     = bTempDoFills;
  }
  else
  {
    /*
    ** we gotta do the right thing.
    */
    lResult = CharStringPos(hDC, (PPOINTL)0, (PRECTL)0,
                            0L, cChars, pString,
                            (PLONG)0, (PCSP_INFO)0, pDDC,
                            MAKEULONG(NGreCharStringPos,
                            HIUSHORT(ulFunN)));
  }

  LeaveDriver(pDDC);
  return  lResult;
}

/***************************************************************************
 *
 * FUNCTION NAME = CharStringPos
 *
 * DESCRIPTION   = Output a character string at the specified point
 *
 * INPUT         = hDC       - device context handle
 *                 pStart    - starting point in rectangle
 *                 pRect     - rectangle
 *                 Options   - drawing options
 *                 nChars    - number of characters
 *                 pChars    - string to write
 *                 pVector   - drawing vector
 *                 pAttrs    - attributes
 *                 pDDC      - pointer to device driver context
 *                 ulFunN    - function number
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 *************************************************************************  */

LONG  CharStringPos ( HDC hDC, PPOINTL pStart, PRECTL pRect,
                      ULONG Options, LONG nChars, PCH pChars,
                      PLONG pVector, PCSP_INFO pAttrs, PDDC pDDC,
                      ULONG FunN )
{
  LONG lResult = 1L;


  if(!(EnterDriver(pDDC)))
    return(GPI_ERROR);

  /*
  **          In case of MEMORYDC Dispatch to Graphics Engine
  */
  if (pDDC->usDCType == OD_MEMORY)
  {
    if (FunN & COM_DEVICE)
    {
      lResult = InnerGreCharStringPos( pDDC->hdcMemory, pStart,
                                     pRect, Options,
                                     nChars, pChars, pVector,
                                     pAttrs, FunN );
    }
    else
    {
      lResult = (*daCharStringPos)( hDC, pStart,
                                    pRect, Options,
                                    nChars, pChars, pVector,
                                    pAttrs, pDDC, FunN );
    }
    LeaveDriver( pDDC );
    return( lResult );
  }

  /*
  ** plotter can't do it
  */
  if ((Options&(CHS_OPAQUE|CHS_CLIP)) && (FunN & COM_PATH))
  {
    LeaveDriver(pDDC);
    return  GPI_ERROR;
  }
  if (pDDC->DCState.dcbnd.cdef.fFlags & CDEF_GENERIC)
  /*
  ** let the engine do it
  */
  {
    BOOL bTempDoFills          = pDDC->DCState.bDoFills;
    BOOL bTempDrawingText      = pDDC->DCState.bDrawingText;

    pDDC->DCState.bDoFills     = FALSE;
    pDDC->DCState.bDrawingText = TRUE;
    //LeaveDriver(pDDC); moved below ?
    lResult = (*daCharStringPos)(hDC, pStart, pRect, Options,
                                 nChars, pChars, pVector,
                                 pAttrs, pDDC, FunN);
    pDDC->DCState.bDrawingText = bTempDrawingText;
    pDDC->DCState.bDoFills     = bTempDoFills;

    LeaveDriver(pDDC);
    return  lResult;
  }

  if (nChars > 0)
  /*
  ** we have to do the work
  */
  {
    PPDEVICE pPDevice = pDDC->pPDevice;
    PPOINTL pPts = (PPOINTL)GplMemoryAlloc(pPDevice->hmcbHeap,
                                            (nChars+1)*sizeof(POINTL));

    if (pPts)
    {
      ULONG  NewOptions;
      POINTL ptlEndPoint;
      POINTL ptlCorner[5] = {{0,0},{0,0},{0,0},{0,0},{0,0}};
      RECTL  rBox1;
      POINTL ptlCurPosn;
      LONG   i;
      RECTL  rcTemp;
      LONG   lTextColor = (Options & CHS_ATTR_INFO) ?
                      pAttrs->lColor :
                      pDDC->DCState.dcbnd.cbnd.lColor;

      LONG   lBackColor = (Options & CHS_ATTR_INFO) ?
                      pAttrs->lBackColor :
                      pDDC->DCState.dcbnd.cbnd.lBackColor;
      USHORT usBackMixMode = pDDC->DCState.dcbnd.cbnd.usBackMixMode;

      NewOptions = Options & (CHS_START_XY|CHS_VECTOR);

      /*
      ** get the start positions of each character in the string
      */
      QueryCharPositions(hDC, pStart, NewOptions, nChars,
                            pChars, pVector, pPts, pDDC,
            MAKEULONG(NGreQueryCharPositions, HIUSHORT(FunN)));

      /*
      ** handle directional anomolies
      */
      if (pDDC->DCState.dcbnd.cbnd.usDirection == CHDIRN_RIGHTLEFT)
        ptlEndPoint = pPts[nChars-1];
      else
        ptlEndPoint = pPts[nChars];

      /*
      ** convert the points, if necessary
      */
      if (FunN & COM_TRANSFORM)
        convert_world_to_device(hDC, pPts, nChars+1L, pDDC);

      QueryTextBox(hDC, nChars, pChars,
                   5L, ptlCorner, pDDC,
                   MAKEULONG(NGreQueryTextBox, HIUSHORT(FunN)));

      /*
      ** Determine starting point of the string
      */
      if (NewOptions & CHS_START_XY)
      {
        /*
        ** Start position supplied - use it
        */
        ptlCurPosn = *pStart;
      }
      else
      {
        /*
        ** Get current position (values in WORLD coordinates)
        */
        GreGetCurrentPosition(hDC, &ptlCurPosn);
      }

      /*
      ** adjust the corners, as needed
      */
      for (i = 0; i < 5; i++)
      {
        ptlCorner[i].x += ptlCurPosn.x;
        ptlCorner[i].y += ptlCurPosn.y;
      }

      get_bounds(4, ptlCorner, &rBox1);

      /*
      ** save the bounding box
      */
      rcTemp = rBox1;

      /*
      ** convert coords if necessary (CCIN)
      */
      if (FunN & COM_TRANSFORM)
        convert_world_to_device(hDC, (PPOINTL)&rcTemp, 2L, pDDC);

      AccumulateBounds(hDC, &rcTemp, pDDC, FunN);

      /*
      ** Draw the text background if needed
      */
      if ( check_char_background_color(pDDC, lBackColor, usBackMixMode) &&
          (FunN & COM_DRAW) )
      {
        AREABUNDLE abnd;
        POLYGON polygon;
        POINTL  aptlPolyPoints[4];
        /*
        ** save our current area bundle
        */
        abnd = pDDC->DCState.abnd;

        /*
        ** BM_ERROR        (-1L)  FM_ERROR        (-1L)
        ** BM_DEFAULT        0L   FM_DEFAULT        0L
        **                        FM_OR             1L
        ** BM_OVERPAINT      2L   FM_OVERPAINT      2L
        ** BM_LEAVEALONE     5L   FM_LEAVEALONE     5L
        **
        ** Set the area attributes to paint the text background color
        ** with the appropriate mix and call polygon to draw the background.
        */
        pDDC->DCState.abnd.lColor     = lBackColor;
        pDDC->DCState.abnd.usMixMode  = usBackMixMode;
        pDDC->DCState.abnd.lBackColor = lBackColor;
        pDDC->DCState.abnd.usBackMixMode = BM_LEAVEALONE;
        pDDC->DCState.abnd.usSymbol = PATSYM_SOLID;
        pDDC->DCState.abnd.usSet = 0;

        /*
        ** set up a polygon with the background text points
        ** The polygon is a closed polygon starting and ending
        ** at the current position
        */

        GreSetCurrentPosition(hDC, &ptlCorner[TXTBOX_BOTTOMLEFT]);
        aptlPolyPoints[0] = ptlCorner[TXTBOX_TOPLEFT];
        aptlPolyPoints[1] = ptlCorner[TXTBOX_TOPRIGHT];
        aptlPolyPoints[2] = ptlCorner[TXTBOX_BOTTOMRIGHT];
        aptlPolyPoints[3] = ptlCorner[TXTBOX_BOTTOMLEFT];

        polygon.ulPoints = 4L;
        polygon.aPointl = aptlPolyPoints;
        GrePolygonSet( hDC, POLYGON_INCL,
                       POLYGON_ALTERNATE | POLYGON_NOBOUNDARY,
                       &polygon, 1L);
        /*
        ** restore the current position and area attributes
        */
        GreSetCurrentPosition(hDC, &ptlCurPosn);
        pDDC->DCState.abnd = abnd;
      }

      if (check_char_color(pDDC, lTextColor) && (FunN & COM_DRAW))
      /*
      ** Printer interfaces with DRIVER, we have output.
      */
      {
        SIZEF  CharCell;
        POINTS CharSize;
        POINTL CharAngle;
        POINTL ptlPosition;
        USHORT usASpace;
        BOOL   bSetPosition;
        SHORT  sClipMode;
        RECTL  rectlClip;
        PRECTL prClip;
        USHORT usCharAttr;
        SHORT  sIndex;
        SHORT  sStart,sEnd;
        RECTL  rQTB;
        RECTL  rQTBRel;
        RECTL  rBox2;
        QCINFO QC;

        QC.bEnabled  = FALSE;
        usCharAttr   = 0;
        bSetPosition = ((Options&CHS_VECTOR) ||
           (pDDC->DCState.dcbnd.cbnd.usDirection != CHDIRN_LEFTRIGHT) ||
           (pDDC->DCState.dcbnd.cbnd.fxExtra != 0) ||
           (pDDC->DCState.dcbnd.cbnd.fxBreakExtra != 0) ||
           (pDDC->DCState.dcbnd.cbnd.usTextAlign != (TA_NORMAL_HORIZ |
                                              TA_NORMAL_VERT)));
        /*
        ** the underscore if needed
        */
        if (pDDC->DCState.dcbnd.cdef.fFlags & CDEF_UNDERSCORE ||
           (pDDC->DCState.dcbnd.cdef.fFlags & CDEF_STRIKEOUT))
        {
          AREABUNDLE  abnd;              /* a place to save the current attr  */
          POLYGON     polygon;
          POINTL      aptlPolyPoints[5];
          LONG        lXDist,lYDist;     /* X and Y distance  */
          LONG        lDelta;            /* Delta over lMaxBaselineExt=%change*/
          FONTMETRICS fmDefault;         /* copy of the original default FM   */
          FONTMETRICS fm;                /* font metrics in World coordinates*/
          /*
          ** save our current area bundle
          */
          abnd = pDDC->DCState.abnd;

          /*
          ** BM_ERROR        (-1L)  FM_ERROR        (-1L)
          ** BM_DEFAULT        0L   FM_DEFAULT        0L
          **                        FM_OR             1L
          ** BM_OVERPAINT      2L   FM_OVERPAINT      2L
          ** BM_LEAVEALONE     5L   FM_LEAVEALONE     5L
          **
          ** Set the area attributes to paint the text background color
          ** with the appropriate mix and call polygon to draw the background.
          */
          pDDC->DCState.abnd.lColor     = lTextColor;
          pDDC->DCState.abnd.usMixMode  = pDDC->DCState.dcbnd.cbnd.usMixMode;
          pDDC->DCState.abnd.lBackColor = lBackColor;
          pDDC->DCState.abnd.usBackMixMode = BM_LEAVEALONE;
          pDDC->DCState.abnd.usSymbol   = PATSYM_SOLID;
          pDDC->DCState.abnd.usSet      = 0;

          /*
          ** Get the Fontmetrics info for the current font
          ** in World coordinates.
          */
          fmDefault = pDDC->pPDevice->fmDefaultFont;     /* Default font      */
          cpxfm_metrics(hDC, &fm, &fmDefault,
                        (USHORT)sizeof(FONTMETRICS),TRUE, pDDC,
                        HIUSHORT(FunN), CPXFM_RET_WORLD);

          /*
          ** Get the difference in the x and y height.
          ** Note: we must consider the the X for rotated text.
          ** We can assume the right will be the same.
          */
          lXDist = ptlCorner[TXTBOX_BOTTOMLEFT].x - ptlCorner[TXTBOX_TOPLEFT].x;
          lYDist = ptlCorner[TXTBOX_BOTTOMLEFT].y - ptlCorner[TXTBOX_TOPLEFT].y;

          /*
          ** Draw the underscore if necessary
          */
          if  (pDDC->DCState.dcbnd.cdef.fFlags & CDEF_UNDERSCORE)
          {
            /*
            ** Set up a box with the the foreground text mix mode
            ** and textcolor for the underscore.
            ** Note: lUnderscorePosition is negative
            */

            /*
            ** distance from the top of the char box to the bottom of
            ** the underscore
            */
            lDelta = fm.lMaxAscender -
                      fm.lUnderscorePosition +
                      fm.lUnderscoreSize;

            /********** fire wall - check for     - devide by 0 ****/
            if (fm.lMaxBaselineExt == 0L)
            {
              DBPRINTF(("fm.lMaxBaselineExt == 0"));
              ASSERT(FALSE);
              fm.lMaxBaselineExt = 1L;
            }

            /* bottom left */
            aptlPolyPoints[0].x = ptlCorner[TXTBOX_TOPLEFT].x +
                                  ((lXDist * lDelta) / fm.lMaxBaselineExt);
            aptlPolyPoints[0].y = ptlCorner[TXTBOX_TOPLEFT].y +
                                  ((lYDist * lDelta) / fm.lMaxBaselineExt);

            /* top left */
            aptlPolyPoints[1].x = ptlCorner[TXTBOX_TOPLEFT].x +
                                  ((lXDist * (lDelta - fm.lUnderscoreSize)) /
                                    fm.lMaxBaselineExt);
            aptlPolyPoints[1].y = ptlCorner[TXTBOX_TOPLEFT].y +
                                  ((lYDist * (lDelta - fm.lUnderscoreSize)) /
                                    fm.lMaxBaselineExt);

            /* top right */
            aptlPolyPoints[2].x = ptlCorner[TXTBOX_TOPRIGHT].x +
                                  ((lXDist * (lDelta - fm.lUnderscoreSize)) /
                                    fm.lMaxBaselineExt);
            aptlPolyPoints[2].y = ptlCorner[TXTBOX_TOPRIGHT].y +
                                  ((lYDist * (lDelta - fm.lUnderscoreSize)) /
                                    fm.lMaxBaselineExt);

            /* bottom right */
            aptlPolyPoints[3].x = ptlCorner[TXTBOX_TOPRIGHT].x +
                                  ((lXDist * lDelta) / fm.lMaxBaselineExt);
            aptlPolyPoints[3].y = ptlCorner[TXTBOX_TOPRIGHT].y +
                                  ((lYDist * lDelta) / fm.lMaxBaselineExt);

            /* polygon must end at first point - Bottom Left */
            aptlPolyPoints[4]   = aptlPolyPoints[0];

            GreSetCurrentPosition(hDC, &aptlPolyPoints[0]);

            polygon.ulPoints = 4L;
            polygon.aPointl = &aptlPolyPoints[1];
            GrePolygonSet( hDC, POLYGON_INCL,
                           POLYGON_ALTERNATE | POLYGON_BOUNDARY,
                           &polygon, 1L);
          }

          /*
          ** Draw the Strikeout if necessary
          */
          if  (pDDC->DCState.dcbnd.cdef.fFlags & CDEF_STRIKEOUT)
          {
            /*
            ** Set up a box with the the foreground text mix mode
            ** and textcolor for the strikeout.
            */
            /*
            ** Distance from the top of the char box to the bottom of
            ** the Strikeout
            */
            lDelta = fm.lMaxAscender -
                      fm.lStrikeoutPosition +
                      fm.lStrikeoutSize;
            /* bottom left */
            aptlPolyPoints[0].x = ptlCorner[TXTBOX_TOPLEFT].x +
                                  ((lXDist * lDelta) / fm.lMaxBaselineExt);
            aptlPolyPoints[0].y = ptlCorner[TXTBOX_TOPLEFT].y +
                                  ((lYDist * lDelta) / fm.lMaxBaselineExt);

            /* top left */
            aptlPolyPoints[1].x = ptlCorner[TXTBOX_TOPLEFT].x +
                                  ((lXDist * (lDelta - fm.lStrikeoutSize)) /
                                    fm.lMaxBaselineExt);
            aptlPolyPoints[1].y = ptlCorner[TXTBOX_TOPLEFT].y +
                                  ((lYDist * (lDelta - fm.lStrikeoutSize)) /
                                    fm.lMaxBaselineExt);

            /* top right */
            aptlPolyPoints[2].x = ptlCorner[TXTBOX_TOPRIGHT].x +
                                  ((lXDist * (lDelta - fm.lStrikeoutSize)) /
                                    fm.lMaxBaselineExt);
            aptlPolyPoints[2].y = ptlCorner[TXTBOX_TOPRIGHT].y +
                                  ((lYDist * (lDelta - fm.lStrikeoutSize)) /
                                    fm.lMaxBaselineExt);

            /* bottom right */
            aptlPolyPoints[3].x = ptlCorner[TXTBOX_TOPRIGHT].x +
                                  ((lXDist * lDelta) / fm.lMaxBaselineExt);
            aptlPolyPoints[3].y = ptlCorner[TXTBOX_TOPRIGHT].y +
                                  ((lYDist * lDelta) / fm.lMaxBaselineExt);

            /* polygon must end at first point - Bottom Left*/
            aptlPolyPoints[4]   = aptlPolyPoints[0];

            GreSetCurrentPosition(hDC, &aptlPolyPoints[0]);

            polygon.ulPoints = 4L;
            polygon.aPointl = &aptlPolyPoints[1];
            GrePolygonSet( hDC, POLYGON_INCL,
                           POLYGON_ALTERNATE | POLYGON_BOUNDARY,
                           &polygon, 1L);

          }
          /*
          ** restore the current position and area attributes
          */
          GreSetCurrentPosition(hDC, &ptlCurPosn);
          pDDC->DCState.abnd = abnd;
        }

        /*
        ** C Set/2 conversion - Added the checks for fxExtra and fxBreakExtra.
        */

        if (Options & CHS_CLIP)
        /*
        ** need to do some clipping
        */
        {
          rcTemp = *pRect;

          /*
          ** look out, there is some rotation present to.  this
          ** could be difficult
          */
          if (pDDC->DCState.usXfData & XF_ROTATION)
          {
            rectl_intersect(&rBox2, &rcTemp, &rBox1);
            ptlCorner[0].x = rBox2.xLeft;
            ptlCorner[0].y = rBox2.yBottom;
            ptlCorner[1].x = rBox2.xRight;
            ptlCorner[1].y = rBox2.yBottom;
            ptlCorner[2].x = rBox2.xRight;
            ptlCorner[2].y = rBox2.yTop;
            ptlCorner[3].x = rBox2.xLeft;
            ptlCorner[3].y = rBox2.yTop;

            /*
            ** CCIN
            */
            if (FunN & COM_TRANSFORM)
              convert_world_to_device(hDC, ptlCorner,
                                      4L, pDDC);

            /*
            ** Adjust clip rect to be like other clip rects
            ** exclusive on the top right
            */
            ptlCorner[1].x++;
            ptlCorner[2].x++;
            ptlCorner[2].y++;
            ptlCorner[3].y++;

            get_bounds(4, ptlCorner, &rBox1);

            /*
            ** get any necessary sloping information
            */
            init_quad_clip(&QC, ptlCorner);
            sClipMode = AC_RECT2;
            rcTemp = rBox1;
          }
          else
          {
            /*
            ** CCIN
            */
            if (FunN & COM_TRANSFORM)
              convert_world_to_device(hDC, (PPOINTL)&rcTemp, 2L, pDDC);

            /*
            ** Adjust clip rect to be like other clip rects
            ** exclusive on the top right
            */
            rcTemp.xRight++;
            rcTemp.yTop++;

            /*
            ** apply it
            */
            sClipMode = apply_clipping(hDC, &rectlClip, &rcTemp, pDDC);
          }

          if (sClipMode == AC_RECT2)
          {
            rectlClip = rcTemp;
          }
          prClip = &rectlClip;
        }
        else
        /*
        ** its a little easier
        */
        {
          sClipMode = apply_clipping(hDC, &rectlClip, (PRECTL)0, pDDC);
          prClip = (PRECTL)0;
        }

        /*
        ** select the appropriate pen
        */
        select_text_pen(pDDC, lTextColor);

        /*
        ** adjust size and angle
        */
        adjust_char_attrs(hDC, pDDC, &CharSize, &CharCell, &CharAngle, FunN);

        if (CharSize.x == 0 || CharSize.y == 0)
        {
          if ((CharSize.x == 0 && CharSize.y == 0) ||
              (CharSize.x == 0 &&
               (pDDC->DCState.dcbnd.cbnd.usDirection == CHDIRN_LEFTRIGHT ||
                pDDC->DCState.dcbnd.cbnd.usDirection == CHDIRN_RIGHTLEFT)) ||
              (CharSize.y == 0 &&
               (pDDC->DCState.dcbnd.cbnd.usDirection == CHDIRN_TOPBOTTOM ||
                pDDC->DCState.dcbnd.cbnd.usDirection == CHDIRN_BOTTOMTOP)))
            nChars = 1;
        }
        set_char_italic_bold(pDDC, pDDC->DCState.dcbnd.cdef.fFlags);
        set_cell_size(hDC, FunN, &CharCell, pDDC);
        set_cell_shear(pDDC, &pDDC->DCState.dcbnd.cbnd.ptlShear);
        set_char_angle(pDDC, (PPOINTL)&CharAngle);
        usASpace = CharSize.x / 10;
        sStart = 0;
        sEnd = (SHORT)nChars-1;

        /*
        ** clip the string, based on clip mode
        */
        if (sClipMode == AC_RECT1)
          string_clip(&rectlClip, pPts,
                      pDDC->DCState.dcbnd.cbnd.usDirection,
                      &sStart, &sEnd);
        else
          if (sClipMode == AC_RECT2)
          {
            LONG lAdjustment;
            /*
            ** Start by clipping to the bounding box of the
            ** complex area. mv
            */
            string_clip(&pDDC->DCState.ClipRect, pPts,
                      pDDC->DCState.dcbnd.cbnd.usDirection,
                      &sStart, &sEnd);
            /*
            ** Get the clip box for the character
            */
            calc_QTB(hDC, &rQTBRel, (UCHAR)pChars[sStart],
                     pDDC, FunN);
            /*
            ** quick fix- Look at this again
            ** Clipping is not accurate on all plotters
            ** Make the QTB clip rectangle a little larger
            ** for a 15% margin of error
            */
            lAdjustment = (rQTBRel.xRight - rQTBRel.xLeft) * 15L / 100L;
            rQTBRel.xLeft  -= lAdjustment;
            rQTBRel.xRight += lAdjustment;
            lAdjustment = (rQTBRel.yTop - rQTBRel.yBottom) * 15L / 100L;
            rQTBRel.yBottom-= lAdjustment;
            rQTBRel.yTop   += lAdjustment;

          }

        /*
        ** add an "A" space to the start position since
        ** Stick fonts start with the center of the stick line
        ** at the current pos, which causes half the line to be
        ** clipped when clipping.
        */
        ptlPosition.y = pPts[sStart].y;
        ptlPosition.x = pPts[sStart].x + usASpace;

        move_pen(pDDC, &ptlPosition, FALSE);

        for (sIndex = sStart; sIndex <= sEnd; sIndex++)
        {
          RECTL rClipCh;
          BOOL bPartChar;


          if (sClipMode == AC_RECT2)
          {
            rQTB.xLeft   = rQTBRel.xLeft + pPts[sIndex].x;
            rQTB.xRight  = rQTBRel.xRight + pPts[sIndex].x;
            rQTB.yBottom = rQTBRel.yBottom + pPts[sIndex].y;
            rQTB.yTop    = rQTBRel.yTop + pPts[sIndex].y;

            if (prClip)
            {
              rectl_intersect(&rClipCh, prClip, &rQTB);
              bPartChar = rQTB.xLeft != rClipCh.xLeft ||
                          rQTB.yBottom != rClipCh.yBottom ||
                          rQTB.xRight != rClipCh.xRight ||
                          rQTB.yTop != rClipCh.yTop;
            }
            else
            {
              rClipCh = rQTB;
              bPartChar = FALSE;
            }
            clip_char(hDC, (UCHAR)pChars[sIndex], usCharAttr,
                      &rClipCh, &pPts[sIndex], bPartChar, pDDC, &QC);
          }
          else
            construct_char(pDDC, (UCHAR)pChars[sIndex], usCharAttr);

          /*
          ** add an "A" space to the start position since
          ** Stick fonts start with the center of the stick line
          ** at the current pos, which causes half the line to be
          ** clipped when clipping.
          */
          ptlPosition.y = pPts[sIndex+1].y;
          ptlPosition.x = pPts[sIndex+1].x + usASpace;

          if (bSetPosition)
          {
            move_pen(pDDC, &ptlPosition, FALSE);
          }
          else /* update our accounting of the physical device position */
          {
            pPDevice->PhysPosition = ptlPosition;
          }
        }
        /*
        ** Turn of string construction by terminating the string.
        */
        check_string_construction(pDDC);

        pPDevice->PhysPosition = pPts[nChars];

        if (sClipMode != AC_RECT0)
          set_clip_rectangle(pDDC);
      }

      if (!(Options & CHS_LEAVEPOS))
        GreSetCurrentPosition(hDC, &ptlEndPoint);

      GplMemoryFree ((PVOID)pPts);
    }
    else
      lResult = 0L;
  }

  LeaveDriver(pDDC);
  return  lResult;
}

/***************************************************************************
 *
 * FUNCTION NAME = GetCodePage
 *
 * DESCRIPTION   = return the current code page
 *
 * INPUT         = hDC       - device context handle
 *                 pDDC      - pointer to device driver context
 *                 FunN      - function number
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 *************************************************************************  */

LONG  GetCodePage ( HDC hDC, PDDC pDDC, ULONG FunN )
{
  ULONG ulRet;

  if (!EnterDriver(pDDC))
      return(GPI_ERROR);

  /*
  **          In case of MEMORYDC Dispatch to Graphics Engine
  */
  if (pDDC->usDCType == OD_MEMORY)
  {
    ulRet = InnerGreGetCodePage( pDDC->hdcMemory, FunN );
  }
  else
    ulRet = pDDC->ulCodePage;
  LeaveDriver(pDDC);
  return(ulRet);
}

/***************************************************************************
 *
 * FUNCTION NAME = SetCodePage
 *
 * DESCRIPTION   = set the current code page
 *
 * INPUT         = hDC       - device context handle
 *                 ulPage    - code page to set
 *                 pDDC      - pointer to device driver context
 *                 ulFunN    - function number
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = GPI_OK
 * RETURN-ERROR  = GPI_ERROR
 *
 ****************************************************************************/

LONG  SetCodePage ( HDC hDC, ULONG ulPage, PDDC pDDC, ULONG ulFunN )
{
  PUSHORT pCPVec;
  ULONG ulRet;

  if (!EnterDriver(pDDC))
      return(GPI_ERROR);

  /*
  **          In case of MEMORYDC Dispatch to Graphics Engine
  */
  if (pDDC->usDCType == OD_MEMORY)
  {
    ulRet = InnerGreSetCodePage (pDDC->hdcMemory, ulPage, ulFunN );
    LeaveDriver( pDDC );
    return( ulRet );
  }

  if (pCPVec = (PUSHORT)GreQueryCodePageVector(ulPage))
  {
    pDDC->ulCodePage = ulPage;
    pDDC->pCPVector  = pCPVec;
    LeaveDriver( pDDC );
    return  GPI_OK;
  }

  LeaveDriver( pDDC );
  return  GPI_ERROR;
}

/***************************************************************************
 *
 * FUNCTION NAME = GetPairKerningTable
 *
 * DESCRIPTION   = required to hook ... but not supported
 *
 * INPUT         = hDC       - device context handle
 *                 nPairs    - number of pairs
 *                 pPairs    - pointer to number of pairs
 *                 pDDC      - pointer to device driver context
 *                 FunN      - function number
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = GPI_OK
 * RETURN-ERROR  = GPI_ERROR
 *
 *************************************************************************  */

LONG  GetPairKerningTable ( HDC hDC, ULONG nPairs, PULONG pPairs,
                            PDDC pDDC, ULONG FunN )
{

  if (!EnterDriver(pDDC))
      return(GPI_ERROR);

  if (pDDC->usDCType == OD_MEMORY)
  {
    LeaveDriver( pDDC );
    return( 0L );
  }
  GplErrSetError(PMERR_DEV_FUNC_NOT_INSTALLED);
  LeaveDriver(pDDC);
  return 0L;
}

