/*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 = CHARSTR2.C
 *
 * DESCRIPTIVE NAME = Text minor functions.
 *
 *
 * VERSION = V2.0
 *
 * DATE        06/01/88
 *
 * DESCRIPTION
 *
 *
 * FUNCTIONS
 *
 *             PfntFromIndex                   Turn a font index into a pointer
 *                                             to the PFNT struc.
 *
 *
 *             GetPFMetrics                    returns a pointer to the Font
 *                                             Metrics.
 *
 *
 *             prda_CheckFontResource          Confirms the resource for the
 *                                             selected font
 *
 *
 *             prda_LoadFontResource           Loads the resource corresponding
 *                                             to the specified font
 *
 *
 *             FreeFontResource                Frees the memory of the Font
 *                                             Resource for this font.
 *
 *
 *             prda_NumPrinterFonts            Pointer to display context
 *                                             instance data
 *
 *
 *             prda_DefaultFontIndex           Returns the index number
 *
 *
 *             prda_CopyMetrics                Copies font metrics
 *
 *
 *             ConcatXforms                    ConcatXforms
 *
 *
 *             prda_ComputeTextTransformMatrix Computes a new text transform
 *                                             matrix
 *
 *
 *             prda_FontTransform              Transforms a point
 *
 *
 *             prda_GetKernAmount              Determines the amount of kerning
 *                                             space
 *
 *
 *             prda_BackMapChar                Determines code point
 *
 *
 *             prda_RemapString                Converts an application's text
 *                                             string
 *
 *
 *             prda_QuoteChar                  put the right information into
 *                                             the buffer
 *
 *
 *             prda_DrawLine                   Draws a line from the current
 *                                             position
 *
 *
 *             SetTextBackground               Points the box with the color
 *
 *
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/

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



extern BOOL   RemapCodePage(PDDC,SHORT,SHORT,PSZ);
extern PVOID   LoadFile(PDV, PSZ); /* download.c  */
extern HMODULE pscript_module;

#define  NO_CODEPAGE   65400

VOID   ConcatXforms(PXFORM,PXFORM);
PVOID   GetPFMetrics(PFONTDEF);
VOID   FreeFontResource(PDV, PFONTDEF, PFNT);
PFNT   PfntFromIndex(PDDC,SHORT);
PFONTDEF ConvertOFMtoPFM( PDV, PVOID ); /*            */
PFONTDEF ConvertPFM( PDV, PFONTDEF );   /*            */

/***************************************************************************
 *
 * FUNCTION NAME = PFNT PfntFromIndex (PDDC, SHORT)
 *
 * DESCRIPTION   = Turn a font index into a pointer to the PFNT struc.
 *
 * INPUT         = pddc, usIndex
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = (PFNT)&(*pddc->pdv->paFonts)[usIndex]
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

PFNT  PfntFromIndex( PDDC pddc, SHORT usIndex )
  /*SHORT usIndex;                      font index  */
{
  return (PFNT) &(*pddc->pdv->paFonts)[usIndex];
}

/***************************************************************************
 *
 * FUNCTION NAME = PFONTMETRICS GetPFMetrics (pFontResource)
 *
 * DESCRIPTION   = Given a pointer to a Font Resource, returns a pointer to the
 *                 Font Metrics.
 *
 * INPUT         = pFontResource
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = pbTmp
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

PVOID GetPFMetrics( PFONTDEF pFontResource )
{
  PB pbTmp;

  pbTmp = (PB) pFontResource;
  pbTmp += pFontResource->Header.usMetricsOffset;
  return  pbTmp;
}

/*
**           
*/
/*****************************************************************************\
**
** ToTop
** _____
**
**
** Moves a member of font cache to top of list. The function keeps exchanging
** two ajacent members till the element is "bubbled" to the top
**
\*****************************************************************************/

VOID ToTop( LONG lElement,PCACHEDFONT pCachedFont )
{
  CACHEDFONT  cfHold;
  PCACHEDFONT pcfMinus1;
  INT         i;

  if ( lElement == 0 )
  {
    return;
  }

  pcfMinus1 = pCachedFont - 1;

  for ( i = 0; i < lElement; i++ )
  {
    cfHold = *pcfMinus1;
    *pcfMinus1 = *pCachedFont;
    *pCachedFont = cfHold;

    pcfMinus1--;
    pCachedFont--;
  }

  return;
}

/***************************************************************************
 *
 * FUNCTION NAME = prda_CheckFontResource (pddc)
 *
 * DESCRIPTION   = Confirms that the resource for the currently
 *                 selected font is loaded.
 *
 *                 PDDC  pddc; Pointer to display context instance data
 *                 Effects: Calls prda_LoadFontResource to load the resource
 *                          corresponding to the currently selected font.  A
 *                          pointer to the header of this resource and a
 *                          pointer to the font metrics within the resource
 *                          are placed in the display context instance data.
 *
 *                 Warnings: If the resource does not load properly, the
 *                           two pointers in the display context
 *                           instance data will be undefined and FALSE
 *                           will be returned instead of TRUE.
 *                           PMERR_BASE_ERROR will also be saved.
 *
 * INPUT         = pddc
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = TRUE or FALSE.
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

BOOL   prda_CheckFontResource( PDDC pddc )
{
  PFONTDEF pfdTmp;
  SHORT    i;

  /*
  ** if it's already loaded, there's nothing to do
  */
  if (pddc->pddcb->text.usFont == (i = pddc->pddcb->text.usCurFontDef))
  {
    return  TRUE;
  }

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

  /*
  ** load the font resource of the current font
  */
  if (!(pfdTmp = pddc->pddcb->text.pfdfFontDef = prda_LoadFontResource( pddc,
                                                 (SHORT) pddc->pddcb->text.usFont )))
  {
    return  FALSE;                     /* error loading resource  */
  }

  /*
  ** Set a pointer to the font metrics structure within the resource
  */
  pddc->pddcb->text.pfmFontMetrics = GetPFMetrics( pfdTmp );


  /*
  ** remember which font resource we have loaded now
  */
  pddc->pddcb->text.usCurFontDef = pddc->pddcb->text.usFont;
  return  TRUE;
}

/*
**           
** Will use cache
*/
/***************************************************************************
 *
 * FUNCTION NAME = PFONTDEF prda_LoadFontResource (pddc, usIndex)
 *
 *                 PDDC    pddc;    Pointer to display context instance data
 *                 SHORT  usIndex; Which font to load. This is index (0-n) in list
 *                                  of all fonts offered by current printer.
 *
 * DESCRIPTION   = Loads the resource corresponding to the specified font
 *                  and returns a pointer to
 *                 the resource data.
 *
 *                 Effects: Consults an internal table to convert the
 *                          requested font into a resource number.
 *                          Then DosGetResource is called upon to
 *                          actually load the resource.  If this is
 *                          successful, a pointer to the loaded
 *                          resource is returned.
 *
 * INPUT         = pddc, usIndex
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = Pointer to resource
 *
 * RETURN-ERROR  = null pointer if there was an error.
 *
 **************************************************************************/

PFONTDEF prda_LoadFontResource( PDDC pddc, SHORT usIndex )
{
  SHORT    usRes;
  PVOID    pResource;
  PFNT     pFnt;
  PFONTDEF pFontDef;  /*            */
  PCACHEDFONT pCachedFont;
  LONG     lFontIndex;
  INT      i;
  BOOL     fUsedCache = FALSE;

  /*
  ** See if font in cache
  */
  pCachedFont = pddc->pdv->FontCache;
  lFontIndex = usIndex + 1;   /* Make index 1 based */

  for ( i = 0; i < NUMCACHEDFONTS; i++ )
  {
    if ( pCachedFont->lIndex == lFontIndex )
    {
      pCachedFont->lUsageCount++;
      pFontDef = pCachedFont->pFontDef;
      fUsedCache = TRUE;
      break;
    }
    pCachedFont++;
  }
  if ( ! fUsedCache )
  {

    /*
    ** Load the resource number corresponding to this index
    */
    pFnt = PfntFromIndex( pddc, usIndex );

    if ( (SHORT) (usRes = pFnt->usResource) > 0 )    /*            */
    {
      if (DosGetResource2( pscript_module, FONTGRP, usRes, &pResource ))
      {
        GplErrSetError(  PMERR_BASE_ERROR );
        return( (PFONTDEF) FNULL );
      }
      pFontDef = ConvertPFM( pddc->pdv, pResource );
      /*
      ** Free resource since it's now copied to alloc-ed buffer
      */
      DosFreeResource( pResource );
    }
    else
    {
      /* @2.195445 */
      if ( ! ( pResource = LoadFile( pddc->pdv, pFnt->pszOFM) ) )
      {
        return NULL;
      }

      /*           
      ** If using OFMs must convert to PFM format
      */
      pFontDef = ConvertOFMtoPFM( pddc->pdv, pResource );

      /*
      ** Must free the original we allocated by LoadFile
      */
      /*
      **           
      ** Remove second parameter (PDV).  Not needed here.
      */
      GplMemoryFree( pResource );
    }

    /*
    ** Try and put font in cache
    */
    pCachedFont = pddc->pdv->FontCache + NUMCACHEDFONTS - 1 ; /* Start at end */
    for ( i = NUMCACHEDFONTS - 1 ; i >= 0; i-- )
    {
      if ( pCachedFont->lUsageCount == 0 )  /* Empty spot */
      {
        if ( pCachedFont->pFontDef )
        { /** Free the old fontdef **/
          GplMemoryFree( pCachedFont->pFontDef );
        }
        pCachedFont->pFontDef = pFontDef;
        pCachedFont->lUsageCount++;
        pCachedFont->lIndex = lFontIndex;
        fUsedCache = TRUE;
        break;
      }
      pCachedFont--;
    }
  }

  if ( fUsedCache )
  {
    ToTop( i, pCachedFont );
  }

  return(  pFontDef );
}

/*
**           
** Use cache
*/
/***************************************************************************
 *
 * FUNCTION NAME = VOID FreeFontResource (PFONTDATA, PFNT)
 *
 * DESCRIPTION   = Frees the memory of the Font Resource for this font.
 *
 * INPUT         = pfResource, pFont
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

VOID   FreeFontResource( PDV pdv, PFONTDEF pfResource, PFNT pFont )

  /* PFONTDEF pfResource;                  ptr to font resource  */
  /* PFNT pFont;                           ptr to font  */
{
  PCACHEDFONT pCachedFont;
  BOOL        fInCache = FALSE;
  INT         i;

  pCachedFont = pdv->FontCache;

  if (pfResource)
  {
/// if ((SHORT)pFont->usResource > 0 )    /*            */
/// {
///   DosFreeResource( pfResource );     /* hardware font  */
/// }
/// else
/// {

    /*
    ** Look for font in cache - if found then dec usage
    */
    for ( i = 0; i < NUMCACHEDFONTS; i++ )
    {
      if ( pCachedFont->pFontDef == pfResource )
      { /* Found font in cache */
        fInCache = TRUE;
        if ( pCachedFont->lUsageCount )
        {
          pCachedFont->lUsageCount--;
        }
        break;
      }
      pCachedFont++;
    }

    /*
    ** V2.194365
    ** Remove second parameter (PDV).  This function should only have
    ** one parameter.
    */
    if ( ! fInCache )
    {
      GplMemoryFree((PB)pfResource ); /* soft font  */
    }
  }
}

/***************************************************************************
 *
 * FUNCTION NAME = SHORT prda_NumPrinterFonts (pddc)
 *
 * DESCRIPTION   = PDDC  pddc; Pointer to display context instance data
 *
 *                 Effects: Scans the array of fonts offered by this printer,
 *                          counting filled Full Name entries.
 *
 * INPUT         = pddc
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = Returns the number of fonts offered by
 *                 the currently selected printer.
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

SHORT prda_NumPrinterFonts( PDDC pddc )
{
  PFNT           pFnt;
  SHORT          sCount = 0;
  register SHORT i;
  register SHORT sFontCount;

  sFontCount = pddc->pdv->cFonts;

  /*           
  ** Do not count duplicate device fonts
  */
  /*
  ** Find the index number corresponding to this fontfullname
  */
  for (i = 0 ; i < sFontCount ; i++)
  {
    pFnt = (PFNT) &(*pddc->pdv->paFonts)[i];
    if ( (SHORT) pFnt->usResource >= 0 )
    {
      sCount++;
    }
  }
  return sCount;

/*return  pddc->pdv->cFonts; **/
}

/***************************************************************************
 *
 * FUNCTION NAME = SHORT prda_DefaultFontIndex (pddc)
 *                 PDDC  pddc; Pointer to display context instance data
 *
 * DESCRIPTION   = Returns the index number (0-n), in the current
 *                 printer's list of supported fonts, of the
 *                 default font.  Both the current printer and the
 *                 default font are selected from the Control
 *                 Panel.
 *
 *                 Effects: Scans the list of all fonts offered by the current
 *                          printer until one is found whose name is identical
 *                          to that of the default font.  The index of this
 *                          matching font is then returned.
 *
 *                 Warnings:If no match is found, then zero is returned.
 *                          This will cause the first font in the
 *                          printer's list of supported fonts to be used
 *                          as if it were the default font.  Note that
 *                          this situation should never arrise naturally.
 *
 * INPUT         = pddc
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = Index number of default font.
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

SHORT prda_DefaultFontIndex( PDDC pddc )
{
  PFNT  pFnt;
  PSZ   pszTarg;
  SHORT j;

  /*
  ** scan the list of all fonts offered by this printer until
  ** either a match is found or we run out of fonts.
  */
  pszTarg = pddc->pdv->szDefFont;
  pFnt = PfntFromIndex( pddc, 0 );

  for (j = 0 ; j < pddc->pdv->cFonts ; j++)
  {
    if (pFnt->pszFullName && szIsEqual(pFnt->pszFullName, pszTarg))
    {
      return  j;
    }
    pFnt++;
  }

  /*
  ** No match was found.  Return 0.
  */
  return 0;
}

/***************************************************************************
 *
 * FUNCTION NAME = void prda_CopyMetrics (pfmDest, pfmSrc, ulLength, lMatch, pddc)
 *
 *                 PFONTMETRICS  pfmDest;  Where to copy the scaled font metrics to
 *                 PFONTMETRICS  pfmSrc;   Where the unscaled metrics may be found
 *                 ULONG         ulLength; How many bytes to copy to destination
 *                 LONG          lMatch;   Value to put in lMatch field of destination
 *                 PDDC          pddc;     Pointer to display context instance data
 *
 * DESCRIPTION   = Copies font metrics from the font resource
 *                 to the caller's buffer, rescales the data
 *                 to world coordinates, and inserts an
 *                 lMatch value into the appropriate field of
 *                 the caller's buffer.
 *
 *      Effects: Copies the entire font metrics structure to a temporary buffer,
 *               rescales all values, inserts the lMatch field, and then copies
 *               the number of bytes requested to the destination buffer.
 *
 *      Warnings: The number of bytes requested to be copied to the destination
 *                must not exceed the size of the font metrics structure, presently
 *                208 bytes in length.
 *
 * INPUT         = pfmDest, pfmSrc, ulLength, lMatch, pddc
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = Void.
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

void prda_CopyMetrics( PFONTMETRICS pfmDest, PFONTMETRICS pfmSrc, ULONG ulLength, LONG lMatch, PDDC pddc )
{
  FONTMETRICS fmFont;
  FIXED       fxXScale, fxYScale;

  /*
  ** first copy the resource metrics to a temporary FONTMETRICS
  ** structure so we can convert to world coordinates. (Resources are
  ** read-only, and we wouldn't want to modify them, anyway).
  */
  utl_memcopy( (PSZ) &fmFont, (PSZ) pfmSrc, sizeof(FONTMETRICS) );

  /*
  ** next rescale all these 1/1000" dimensions according to current font
  ** size
  */
  fxXScale = pddc->pddcb->text.fxXScale;
  fxYScale = pddc->pddcb->text.fxYScale;

  /*
  ** the following fields must be rescaled by the char. Y height
  */
  fmFont.lXHeight = FxToLong( frmul( LongToFx(pfmSrc->lXHeight), fxYScale) );
  fmFont.lEmHeight = FxToLong( frmul( LongToFx(pfmSrc->lEmHeight), fxYScale) );
  fmFont.lMaxAscender = FxToLong( frmul( LongToFx(pfmSrc->lMaxAscender),
                                  fxYScale) );
  fmFont.lMaxDescender = FxToLong( frmul( LongToFx(pfmSrc->lMaxDescender),
                                   fxYScale) );
  fmFont.lLowerCaseAscent = FxToLong( frmul( LongToFx(pfmSrc->lLowerCaseAscent),
                                      fxYScale) );
  fmFont.lLowerCaseDescent = FxToLong( frmul( LongToFx(pfmSrc->lLowerCaseDescent),
                                       fxYScale) );
  fmFont.lInternalLeading = FxToLong( frmul( LongToFx(pfmSrc->lInternalLeading),
                                      fxYScale) );
  fmFont.lExternalLeading = FxToLong( frmul( LongToFx(pfmSrc->lExternalLeading),
                                      fxYScale) );
  fmFont.lMaxBaselineExt = FxToLong( frmul( LongToFx(pfmSrc->lMaxBaselineExt),
                                     fxYScale) );
  fmFont.lUnderscoreSize = FxToLong( frmul( LongToFx(pfmSrc->lUnderscoreSize),
                                     fxYScale) );
  fmFont.lStrikeoutSize = FxToLong( frmul( LongToFx(pfmSrc->lStrikeoutSize),
                                     fxYScale) );
  fmFont.lUnderscorePosition = FxToLong( frmul( LongToFx
                                         (pfmSrc->lUnderscorePosition), fxYScale) );
  fmFont.lStrikeoutPosition = FxToLong( frmul( LongToFx
                                        (pfmSrc->lStrikeoutPosition), fxYScale) );

  /*
  ** the following fields must be rescaled by the char. X width
  */
  fmFont.lAveCharWidth = labs(FxToLong( frmul( LongToFx(pfmSrc->lAveCharWidth),
                                        fxXScale)) );
  fmFont.lMaxCharInc = labs(FxToLong( frmul( LongToFx(pfmSrc->lMaxCharInc),
                                      fxXScale)) );
  fmFont.lEmInc = labs(FxToLong( frmul( LongToFx(pfmSrc->lEmInc), fxXScale)) );

  /*
  ** Postscript doesn't use the sub/superscript fields of font metrics
  */
  fmFont.lSubscriptXSize = 0L;
  fmFont.lSubscriptYSize = 0L;
  fmFont.lSubscriptXOffset = 0L;
  fmFont.lSubscriptYOffset = 0L;
  fmFont.lSuperscriptXSize = 0L;
  fmFont.lSuperscriptYSize = 0L;
  fmFont.lSuperscriptXOffset = 0L;
  fmFont.lSuperscriptYOffset = 0L;

  /*
  ** Set the lMatch field to whatever value the major function wants.
  */
  fmFont.lMatch = lMatch;

  /*
  ** now copy ulLength bytes to the destination buffer
  */
  utl_memcopy( (PSZ) pfmDest, (PSZ) &fmFont, (int)ulLength );
  return;
}

/***************************************************************************
 *
 * FUNCTION NAME = ConcatXforms
 *
 * DESCRIPTION   = ConcatXforms
 *
 * INPUT         = (pxfm1, pxfm2)
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR = NONE
 *
 **************************************************************************/

void ConcatXforms( PXFORM pxfm1, PXFORM pxfm2 )
{
  FIXED fxM11, fxM12, fxM21, fxM22, fxM41, fxM42;

  fxM11 = frmul( pxfm1->fxM11, pxfm2->fxM11)+frmul( pxfm1->fxM21, pxfm2->fxM12 );
  fxM12 = frmul( pxfm1->fxM12, pxfm2->fxM11)+frmul( pxfm1->fxM22, pxfm2->fxM12 );
  fxM21 = frmul( pxfm1->fxM11, pxfm2->fxM21)+frmul( pxfm1->fxM21, pxfm2->fxM22 );
  fxM22 = frmul( pxfm1->fxM12, pxfm2->fxM21)+frmul( pxfm1->fxM22, pxfm2->fxM22 );
  pxfm1->fxM11 = fxM11;
  pxfm1->fxM12 = fxM12;
  pxfm1->fxM21 = fxM21;
  pxfm1->fxM22 = fxM22;
  pxfm1->lM41 = pxfm1->lM41+pxfm2->lM41;
  pxfm1->lM42 = pxfm1->lM42+pxfm2->lM42;
}

/***************************************************************************
 *
 * FUNCTION NAME = void prda_ComputeTextTransformMatrix (hdc, pddc)
 *                 PDDC   pddc;    Pointer to display context instance data
 *
 * DESCRIPTION   = Computes a new text transform matrix from the character
 *                 attribute bundle stored in the display context
 *                 instance, and puts the new transform in the instance as
 *                 well.
 *
 *    Effects: Recalculates the transform from scratch. This is necessary when
 *             any of the cell size, angle, or shear entries in the character
 *             attribute bundle change.
 *
 *    Warnings: The transform matrix that is created will be suitable for
 *              converting points from Adobe style (1000x1000) units to world
 *              coordinate space. Since we often need just to rotate values
 *              already in world coordinate space, we also save the cosine and
 *              sine values that are calculated.
 *
 * INPUT         = pddc
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

void prda_ComputeTextTransformMatrix( PDDC pddc )
{
  FIXED fxAngleX, fxAngleY, fxHypot, fxCos, fxSin;
  XFORM xfm1, xfm2;

  fxAngleX = LongToFx(pddc->pddcb->text.ChrBundle.ptlAngle.x );
  fxAngleY = LongToFx(pddc->pddcb->text.ChrBundle.ptlAngle.y );

  if (pddc->pddcb->text.ChrBundle.sizfxCell.cx != FX_ZERO)
  {
    xfm1.fxM11 = pddc->pddcb->text.ChrBundle.sizfxCell.cx;
  }
  else
  {
    xfm1.fxM11 = FX_ONE;
  }
  if (pddc->pddcb->text.ChrBundle.sizfxCell.cy != FX_ZERO)
  {
    xfm1.fxM22 = pddc->pddcb->text.ChrBundle.sizfxCell.cy;
  }
  else
  {
    xfm1.fxM22 = FX_ONE;
  }

  /*
  ** Now compute the angles for the rotation matrix.
  ** Save the cosines and sines for future use.
  */
  fxHypot = frVectLength( fxAngleX, fxAngleY );
  pddc->pddcb->text.fxCos = fxCos = frdiv( fxAngleX, fxHypot );
  pddc->pddcb->text.fxSin = fxSin = frdiv( fxAngleY, fxHypot );

  /*
  ** next we compute the x shear factor
  */
  xfm1.fxM21 = FX_ZERO;

  if ((pddc->pddcb->text.ChrBundle.ptlShear.x != 0) &&
     (pddc->pddcb->text.ChrBundle.ptlShear.y != 0))
  {
    xfm1.fxM21 = BigMulDiv(pddc->pddcb->text.ChrBundle.sizfxCell.cy,
       pddc->pddcb->text.ChrBundle.ptlShear.x,
       pddc->pddcb->text.ChrBundle.ptlShear.y );
  }
  xfm1.fxM12 = FX_ZERO;
  xfm1.lM41 = 0L;
  xfm1.lM42 = 0L;

  /*
  ** Set the rotation matrix.
  **
  ** | cos   sin |
  ** | -sin  cos |
  */
  xfm2.fxM11 = fxCos;
  xfm2.fxM12 = fxSin;
  xfm2.fxM21 = -fxSin;
  xfm2.fxM22 = fxCos;
  xfm2.lM41 = 0L;
  xfm2.lM42 = 0L;
  ConcatXforms( &xfm2, &xfm1 );

  /*
  ** Normalize the matrix and store it in our DDC.
  */
  CopyAndNormalize( (PXFORM) &xfm2, (MATRIX  *)
                    &(pddc->pddcb->text.transformmatrix) );
  return ;
}

/***************************************************************************
 *
 * FUNCTION NAME = void prda_FontTransform (pptlDest, pddc)
 *                 PPOINTL  pptlDest;  The point to be transformed
 *                 PDDC     pddc;      Pointer to display context instance data
 *
 * DESCRIPTION   = Transforms a point given in Adobe style (1000x1000) units into world
 *                 coordinate space.
 *
 *   Effects: Applies the current text transform, held in the display context
 *            instance data, to the given point and returns the new point.
 *
 * INPUT         = pptlDest, pddc
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = The point transformed into world coordinate space.
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/
 /*
 ** !!CR Ideally we will eliminate translation by current position from this
 */
 /*
 ** !!CR routine and make all major functions responsible for it.
 */

void prda_FontTransform( PPOINTL pptlDest, PDDC pddc )
{
  FIXED fxX, fxY;

  fxX = LongToFx( pptlDest->x/1000L)+frdiv(LongToFx(pptlDest->x%1000L), FX_1000 );
  fxY = LongToFx(pptlDest->y/1000L)+frdiv(LongToFx(pptlDest->y%1000L), FX_1000 );
  pptlDest->x = FxToLong( frmul( pddc->pddcb->text.transformmatrix.mx_fxM11, fxX
                          )+frmul( pddc->pddcb->text.transformmatrix.mx_fxM21, fxY))+
                          pddc->pddcb->text.transformmatrix.mx_lM41;
  pptlDest->y = FxToLong( frmul( pddc->pddcb->text.transformmatrix.mx_fxM12, fxX
                          )+frmul( pddc->pddcb->text.transformmatrix.mx_fxM22, fxY))+
                          pddc->pddcb->text.transformmatrix.mx_lM42;
}

/***************************************************************************
 *
 * FUNCTION NAME = LONG prda_GetKernAmount (sCodePoint1, sCodePoint2, pddc)
 *                 SHORT  sCodePoint1;   The code point of the left character
 *                 SHORT  sCodePoint2;   The code point of the right character
 *                 PDDC   pddc;          Pointer to display context instance data
 *
 * DESCRIPTION   = Determines the amount of kerning space appropriate
 *                 between the two given characters.  This is in Adobe
 *                 style (1000x1000) units.  The value is signed; a
 *                 negative value means pull the characters closer
 *                 together and a positive value means push them
 *                 further apart.
 *
 *   Effects: Scans the kerning pair table of the currently selected font. If
 *            the given pair of code points in in the list, then the kerning
 *            amount shown is returned wholesale. Otherwise, zero is returned
 *            to indicate that no kerning is necessary between these two code
 *            points.
 *
 *   Warnings: The code points provided must be in the code page the font is
 *             designed for, not the code page the application is using. For
 *             fonts with Winthorn multi-code page support, this means that
 *             the code points must be in extended code page 850 form (presently
 *             a value 1-316).
 *
 * INPUT         = sCodePoint1, sCodePoint2, pddc
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = 0L Amount to kern between the two given code points.
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

LONG   prda_GetKernAmount( SHORT sCodePoint1, SHORT sCodePoint2, PDDC pddc )
{

  SHORT      i = 0;
  PB         pbTemp;
  PKERNPAIRS pkrnprKernTable;
                                    /* Point to start of kerning table  */

  pbTemp = (PB) pddc->pddcb->text.pfdfFontDef;
  pbTemp += pddc->pddcb->text.pfdfFontDef->Header.usKerningDataOffset;
  pkrnprKernTable = (PKERNPAIRS) pbTemp;

  /*
  ** Get the kerning amount
  */
  while (i < pddc->pddcb->text.pfmFontMetrics->sKerningPairs)
  {
    if ((sCodePoint1 == pkrnprKernTable->g1) && (sCodePoint2 ==
       pkrnprKernTable->g2))
    {
      return( (LONG)pkrnprKernTable->KernUnits );
    }
    pkrnprKernTable++;
    i++;
  }

  /*
  ** Apparently, there is no kerning between these 2 chars
  */
  return( 0L );
}

/***************************************************************************
 *
 * FUNCTION NAME = SHORT prda_BackMapChar (sGlyphID, pddc)
 *                 SHORT sGlyphID;  An ID number from the universal glyph list
 *                 PDDC  pddc;      Pointer to display context instance data
 *
 * DESCRIPTION   = Determines what code point in the current code page
 *                 corresponds to a given glyph (character pattern).
 *
 *     Effects: If there is a code page vector, scan it to see if any code points
 *              in the current code page map to the given glyph. If a match is
 *              found, return it. If no match is found, return -1. If there is no
 *              code page vector, then this font doesn't offer winthorn multi-code
 *              page support. In this case, the provided glyph ID is simply
 *              returned as the code point.
 *
 * INPUT         = sGlyphID, pddc
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = The code point corresponding to the given glyph, or -1 if there is
 *                 none, or the glyph itself if the currently selected font doesn't
 *                 offer winthorn multi-code page support.
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

SHORT prda_BackMapChar( SHORT sGlyphID, PDDC pddc )
{
  PSHORT pusCodePageVector;
  SHORT i;

  if (!(pusCodePageVector = pddc->pddcb->text.pusCurCodePgVector))
  {
    return( sGlyphID );               /* No Winthorn multi-codepage support  */
  }

  for (i = 0; i < 256; i++)
  {
    if (sGlyphID == (SHORT)*(pusCodePageVector+i))
    {
      return( i );                      /* Match fount  */
    }
  }
  return( -1 );                   /* This code page doesn't have that glyph  */
}

/***************************************************************************
 *
 * FUNCTION NAME = LONG prda_RemapString (pchString, lCount, pddc)
 *                 PCH   pchString;  The string of code points to be remapped
 *                 LONG  lCount;     The number of code points in the string
 *                 PDDC  pddc;       Pointer to display context instance data
 *
 * DESCRIPTION   = Converts an application's text string into a string
 *                 suitable for sending to PostScript as an argument
 *                 to a show command.  This conversion requires four
 *                 procedures:  (a) Convert code points to the right
 *                 code page, (b) Possibly download a new font
 *                 remapping to PostScript, (c) Add back- slashes
 *                 before prentheses, and (d) Use octal for non-ASCII
 *                 characters.
 *
 *  Effects: Scans the string looking for troublesome code points (characters
 *           without a numeric value in the Adobe Standard Encoding). If the
 *           string contains any such characters and a font remap has not yet
 *           been downloaded to the printer, then the remap is downloaded now.
 *           Next, the string is scanned a second time, converting each code
 *           point to the proper code page, adding backslashes and octal digits
 *           as needed, and storing the final string in a buffer in the display
 *           context instance data.
 *
 *  Warnings: The font remapping is downloaded only if absolutely necessary.
 *            The download causes a performance loss, so we avoid it if we can.
 *
 *            The text buffer in the display context instance is limited in
 *            size (presently 256 bytes long). If the provided string is too
 *            long, it will get truncated. It is therefore the responsibility
 *            of the major functions to make sure this never happens.
 *            (CharStringPos will automatically break longer strings into
 *            shorter ones to avoid truncated text.)
 *
 * INPUT         = pchString, lCount, pddc
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = The number of bytes in the text buffer in the
 *                 display context instance that must be output
 *                 in the PostScript show command in order to
 *                 display this string.  A return value of -1
 *                 indicates an error in loading a resource.
 *                 PMERR_BASE_ERROR will NOT have been saved yet,
 *                 in this case.
 *
 * RETURN-ERROR  = NONE
 *
 *
 **************************************************************************/

LONG prda_RemapString( PCH pchString, LONG lCount, PDDC pddc )
{
  BYTE           bEntry;           /* Entry number in logical font table  */
  PCH            pchOutput;        /* Points to text buffer in PDDC  */
  SHORT          sBytesRemaining;  /* How much space is left in text buffer  */
  SHORT          sFirst,sLast;     /* First/last codepoints allowed by font  */
  PCH            pChar;            /* Pointer to character metrics of font  */
  BYTE           b;                /* A codepoint from caller's string  */
  SHORT          sCodePoint;       /* Remapped codepoint to font's code page  */
  SHORT          sPrint;           /* How much buff space needed to print char  */
  LONG           i, j;
  register USHORT usCodePage;
                                                  /* Initialize things  */

  pchOutput = pddc->pdv->chText;
  sBytesRemaining = (SHORT) SHOW_BUFFER_SIZE;
  sFirst = pddc->pddcb->text.pfmFontMetrics->sFirstChar;
  sLast = pddc->pddcb->text.pfmFontMetrics->sLastChar;

  /*
  ** Since prda_QuoteChar() assumes ALL characters are the same from 0x1F to
  ** 0x7F no remapping is done unless the char is above the range.  The fails
  ** when switching to and among EBCDIC code pages.  The check is done here
  ** on a line by line vs char by char check for efficiency.
  **                                                                  
  */
  bEntry = pddc->pddcb->text.bLogFontEntry;               /*            */
  usCodePage = pddc->usCodePages[bEntry];                 /*            */
  switch ( usCodePage )                                   /*            */
  {                                                       /*            */
  case NO_CODEPAGE:                                       /*            */
  case 437:   /* ASCII */
  case 850:   /* These codepages do not normally be remapped  */
  case 852:   /* If they do it will be done in prda_QuoteChar */
  case 857:                                                /*            */
  case 860:                                                /*            */
  case 861:                                                /*            */
  case 863:                                                /*            */
  case 865:                                                /*            */
  case 1004:                                               /*            */
       break;                                               /*            */

  default:    /* Anything else needs to be remapped */
       if ( !QueryFontRemap( bEntry ) )  /* Check if remapped */
       {
         RemapCodePage( pddc, usCodePage, bEntry, pddc->pddcb->text.szFont );
         SetFontRemap( bEntry );                         /*            */
         ps_SetDownloadedFont( pddc );                   /*            */
       }                                                   /*            */
  }                                                       /*            */

  sPrint = 1;

  for (i = 0; (i < lCount) && (sPrint ); i++)
  {
    /*
    ** Since font is fully redefined in the printer, we just output the
    ** character codes directly -- code page has already been accounted for
    */
    b = *(pchString++ );
    sCodePoint = (SHORT) b;

    /*
    ** if (pddc->pddcb->text.pusCurCodePgVector)
    ** sCodePoint = (SHORT) *(pddc->pddcb->text.pusCurCodePgVector+sCodePoint) ;
    */

    if ((sCodePoint < sFirst) || (sCodePoint > (sFirst+sLast)))
    {
      sCodePoint = sFirst+pddc->pddcb->text.pfmFontMetrics->sDefaultChar;
    }
    b = (BYTE) sCodePoint;
    sPrint = prda_QuoteChar( pddc, pchOutput, b, sBytesRemaining );
    pchOutput += sPrint;
    sBytesRemaining -= sPrint;
  }
  return( (LONG)(SHOW_BUFFER_SIZE - (LONG)sBytesRemaining) );
}

/***************************************************************************
 *
 * FUNCTION NAME = SHORT prda_QuoteChar (pddc, pchString, bChar, sBytesRemaining)
 *                 PDDC  pddc;
 *                 PCH   pchString;       Buffer where quoted character shall be placed
 *                 BYTE  bChar;           The character code to be quoted
 *                 SHORT sBytesRemaining; How many bytes are available to hold quoted char
 *
 * DESCRIPTION   = Given a character code 0-255, put the right information
 *                 into the buffer so that a show command in PostScript
 *                 will display the character with this code.  Codes like
 *                 65 (A) and 33 (!)  are copied directly.  Non-printable
 *                 codes like 255 are put in the buffer in octal form, ie
 *                 \377.  Some special characters (like parentheses)
 *                 require a backslash to be placed in front of them.
 *
 *                 Effects: Put the proper information into the output
 *                          buffer.  This will be one, two, or four
 *                          bytes of printable ASCII characters.
 *
 *                 Warnings: If there isn't enough buffer space available
 *                           to hold the quoted form of the character
 *                           code, then the buffer is left unchanged and
 *                           a value of zero is returned.
 *
 * INPUT         = pddc, pchString, bChar, sBytesRemaining
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = The number of bytes that were added to the
 *                 buffer; this will be either one, two, or
 *                 four.  If there isn't enough space in the
 *                 buffer to hold the quoted character, zero
 *                 is returned.
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

SHORT prda_QuoteChar( PDDC pddc, PCH pchString, BYTE bChar, SHORT sBytesRemaining )
{
  SHORT sPrinted = 2;
  BYTE  bEntry;

  switch (bChar)
  {
  /*
  ** Precede left parenthesis with a backslash
  */
  case '(' :
       if (sBytesRemaining < 2)
       {
         return( 0 );
       }

       *pchString++ = '\\';
       *pchString++ = '(';
       break;

    /*
    ** Precede right parenthesis with a backslash
    */
    case ')' :
         if (sBytesRemaining < 2)
         {
           return( 0 );
         }
         *pchString++ = '\\';
         *pchString++ = ')';
         break;

    /*
    ** Convert a backslash to a double backslash
    */
    case '\\' :
         if (sBytesRemaining < 2)
         {
           return( 0 );
         }
         *pchString++ = '\\';
         *pchString++ = '\\';
         break;

    default :
         /*
         ** Warning.  All characters in the range 0x1F - 0x7F
         ** are the same in all codepages.  The adobe default
         ** font mapping is also consistent EXCEPT for character
         ** 0x27 the "'" character.  It is quoteright when it
         ** should be quotesingle to match the codepage standard.
         */
         if ((bChar > 0x1f) && (bChar < 0x07f) && (bChar != 0x027))
         {
           /*
           ** Just copy the printable ASCII character
           */
           if (sBytesRemaining < 1)
           {
             return( 0 );
           }
           *pchString++ = bChar;
           sPrinted = 1;
         }
         else
         {
           /*
           ** we have an extended character.  let's see if
           ** we need to remap the current font to the
           ** codepage.
           */
           bEntry = pddc->pddcb->text.bLogFontEntry;

           if ((!QueryFontRemap(bEntry)) && (pddc->usCodePages[bEntry] !=
              NO_CODEPAGE))
           {
             /*
             ** remap this here font to the codepage associated
             ** with it.
             */
             RemapCodePage( pddc, pddc->usCodePages[bEntry], bEntry,
                            pddc->pddcb->text.szFont );
             SetFontRemap( bEntry );
             ps_SetDownloadedFont( pddc );
           }

           #if         0
             /*
             ** we have an extended character.  let's see if
             ** we need to remap the current font to the codepage.
             */
             if ((pddc->pddcb->cgs.cCodePagesRemapped == 0) &&
             (pddc->pddcb->text.usDefCodePg == 850))
             {
               bEntry = pddc->pddcb->text.bLogFontEntry;
               ClearFontRemap(bEntry );
               RemapCodePage(pddc, 850, bEntry, pddc->pddcb->text.szFont );
               SetFontRemap(bEntry );
               ps_SetDownloadedFont(pddc );
             }
           #endif

           /*
           ** Convert non-printable ASCII to backslash octal
           */
           if (sBytesRemaining < 4)
           {
             return( 0 );
           }
           pchString[0] = '\\';
           pchString[3] = (bChar&7)+'0';
           bChar >>= 3;
           pchString[2] = (bChar&7)+'0';
           bChar >>= 3;
           pchString[1] = (bChar&7)+'0';
           sPrinted = 4;
         }
         break;
  }
  sBytesRemaining -= sPrinted;
  return( sPrinted );
}

/***************************************************************************
 *
 * FUNCTION NAME = void prda_DrawLine (pddc, ptlBeginText, lPosition, lSize)
 *                 PDDC    pddc;          Pointer to display context instance data
 *                 POINTL  ptlBeginText;  Text starting point in world coordinates
 *                 LONG    lPosition;     Vertical position of line in Adobe units
 *                                        Positive value for above baseline, negative below
 *                 LONG    lSize;         Thickness of line in Adobe units
 *
 * DESCRIPTION   = Draws a line from the current position (assumed
 *                 to be the end of the text) to the provided
 *                 position (start of the text) at the specified
 *                 elevation with regard to the text baseline and
 *                 of the specified thickness.  If a path is
 *                 presently open, the figure is added to the
 *                 path, otherwise it is stroked immediately.
 *
 *                 Effects: If there is no path open, then a line is drawn and
 *                          stroked from the end of the text to the beginning
 *                          of the text, adjusting both endpoints for the
 *                          provided elevation.  Current position, line type,
 *                          line thickness, and line cap are all destroyed.
 *
 *                          If there is a path open, then a subpath is created
 *                          and added to the current path.  The sub path is
 *                          actually a rectangle that out- lines the requested
 *                          line.  This is necessary for outline text and text
 *                          pattern clipping.  Again, current position and
 *                          line attributes are destroyed.
 *
 *                 Warnings: It is assumed that the current position is on the
 *                           baseline at end of the string that is being
 *                           underscored or struck out.  It is assumed that
 *                           the major function saved current position and
 *                           line attributes and will restore them later.
 *                           This function must not be called if the draw bit
 *                           is turned off.
 *
 * INPUT         = pddc, ptlBeginText, lPosition, lSize
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

void prda_DrawLine( PDDC pddc, POINTL ptlBeginText, LONG lPosition, LONG lSize )
{
  POINTL ptlBeginLine,ptlEndLine;
  POINTL ptlBox[4];
  SHORT usLineEnd;

  /*
  ** Our methods depend on whether we are in a path or not.
  */
  if (!pddc->pddcb->path.fPathIsOpen)
  {
    /*
    ** We are not currently in a path. This means the text is being
    ** displayed immediately. In keeping with this, we must draw
    ** the underscore/strikeout immediately. This is just a
    ** straight line between two points.
    */
    /*
    ** Determine the two endpoints for the line we will draw
    */
    ptlEndLine.x = 0L;
    ptlEndLine.y = lPosition;
    prda_FontTransform( (PPOINTL) &ptlEndLine, pddc );
    ptlEndLine.x += pddc->pddcb->pen.ptlCur.x;
    ptlEndLine.y += pddc->pddcb->pen.ptlCur.y;
    ptlBeginLine.x = 0L;
    ptlBeginLine.y = lPosition;
    prda_FontTransform( (PPOINTL) &ptlBeginLine, pddc );
    ptlBeginLine.x += ptlBeginText.x;
    ptlBeginLine.y += ptlBeginText.y;

    /*
    ** Set the appropriate line attributes
    */
    ps_setdash( pddc, LINETYPE_SOLID );
    usLineEnd = pddc->pddcb->pen.usEnd;
    ps_setlinecap(pddc, aiLineCap[LINEEND_SQUARE] );
    ps_setlinewidth(pddc, frmul( LongToFx( lSize ), pddc->pddcb->text.fxYScale),
                                 TRUE );

    /*
    ** Draw the line
    */
    ps_newpath( pddc );
    ps_movetoCPx( pddc, (PPOINTL)&ptlBeginLine );
    ps_lineto( pddc, (PPOINTL)&ptlEndLine );
    ps_stroke( pddc );
    ps_setlinecap( pddc, aiLineCap[usLineEnd] );
  }
  else
  {
    /*
    ** We are currently in a path. This means that the text will
    ** eventually either be used for a clip region or be drawn in
    ** outline form. In either case, the underscore/strikeout must
    ** be drawn as a box and not simply as a line.
    */
    /*
    ** Determine the four corners of the box we will draw
    */
    lSize /= 2;
    ptlBox[0].x = 0L;
    ptlBox[1].x = 0L;
    ptlBox[0].y = lPosition+lSize;
    ptlBox[1].y = lPosition-lSize;
    ptlBox[2].x = 0L;
    ptlBox[3].x = 0L;
    ptlBox[2].y = lPosition-lSize;
    ptlBox[3].y = lPosition+lSize;
    prda_FontTransform( (PPOINTL) &ptlBox[0], pddc );
    prda_FontTransform( (PPOINTL) &ptlBox[1], pddc );
    prda_FontTransform( (PPOINTL) &ptlBox[2], pddc );
    prda_FontTransform( (PPOINTL) &ptlBox[3], pddc );
    ptlBox[0].x += pddc->pddcb->pen.ptlCur.x;
    ptlBox[1].x += pddc->pddcb->pen.ptlCur.x;
    ptlBox[0].y += pddc->pddcb->pen.ptlCur.y;
    ptlBox[1].y += pddc->pddcb->pen.ptlCur.y;
    ptlBox[2].x += ptlBeginText.x;
    ptlBox[3].x += ptlBeginText.x;
    ptlBox[2].y += ptlBeginText.x;
    ptlBox[3].y += ptlBeginText.y;

    /*
    ** Add the rectangle to the path
    */
    ps_moveto( pddc, (PPOINTL) &ptlBox[0] );
    ps_lineto( pddc, (PPOINTL) &ptlBox[1] );
    ps_lineto( pddc, (PPOINTL) &ptlBox[2] );
    ps_lineto( pddc, (PPOINTL) &ptlBox[3] );
    ps_closepath( pddc );
  }

  /*
  ** !!! what about the current position?
  */
}

/***************************************************************************
 *
 * FUNCTION NAME =  VOID SetTextBackground (ulOptions, pscpAttributes, pptlBox, pddc)
 *                  ULONG      ulOptions;       Whether to use default or provided color
 *                  PCSP_INFO  pscpAttributes;  Background color to used if option permits
 *                  PPOINTL    pptlBox;         Four coordinates of bounding box
 *                  PDDC       pddc;            Pointer to display context instance data
 *
 * DESCRIPTION   = Points the provided box either the default background
 *                 color or the provided background color, depending on
 *                 the options parameter.  This function is used to
 *                 paint the area behind where text will be placed in
 *                 order to simulate a background mix mode of overpaint.
 *                 (PostScript only supports a background mix mode of
 *                 leave-alone.)
 *
 *                 Effects: Sets the color to either the current background
 *                          color or that provided in the bundle.  Then
 *                          makes a path of the provided bounding box and
 *                          fills the box.
 *
 *                 Warnings: This function should not be called if the draw
 *                           bit is turned off.
 *
 * INPUT         = (ulOptions, pscpAttributes, pptlBox,
 *                  pddc)
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

VOID SetTextBackground( ULONG ulOptions, PCSP_INFO pscpAttributes, PPOINTL pptlBox, PDDC pddc )
{
  POINTL ptlClip[4];
  POINTL ptlCur;

  /*
  ** set the background color to either the attributes (if specified)
  ** or else to the current text background color
  */
  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) );
  }

  /*
  ** shuffle the QueryTextBox results for PolyLine
  */
  ptlClip[0].x = pptlBox->x;           /* upper left  */
  ptlClip[0].y = pptlBox->y;
  ptlClip[3].x = (++pptlBox)->x;       /* lower left  */
  ptlClip[3].y = pptlBox->y;
  ptlClip[1].x = (++pptlBox)->x;       /* upper right  */
  ptlClip[1].y = pptlBox->y;
  ptlClip[2].x = (++pptlBox)->x;       /* lower right  */
  ptlClip[2].y = pptlBox->y;

  /*
  ** stroke and fill the region
  */
  ptlCur = pddc->pddcb->pen.ptlCur;
  ps_newpath( pddc );
  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 );
  ps_fill( pddc );
  ps_moveto( pddc, &ptlCur );
}
