/*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 = PRDTGCHS.C
 *
 * DESCRIPTIVE NAME = PRINTER DEVICE DRIVER SOURCE
 *
 *
 * VERSION = V2.0
 *
 * DATE
 *
 * DESCRIPTION   Printer Device Driver text functions
 *
 *               Reference      = Winthorn Functional Specification
 *                                Device Driver Interface Specification
 *                                Printer Device Driver Design Specification
 *
 *
 * FUNCTIONS     prdt_QueryCharPositions() Returns the positions (world
 *                                         coordinates) where the printer will
 *                                         place each given character
 *
 *               prdt_QueryTextBox()       Process the specified string as if it
 *                                         were to be drawn, using the current
 *                                         character attributes
 *
 * NOTES
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/

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

#include <pmdev.h>
#define  OD_MEMORY  8L
/* Direction tables */
extern PDDC EnterDriver(PDDC);
extern VOID ExitDriver(PDDC);
static signed char achHorizontal[] =
{
  1,  1,  0,  1,  0
};

static signed char achDirection[] =
{
  1,              1,              -1,             -1,             1
} ;

/***************************************************************************
 *
 * FUNCTION NAME = prdt_QueryCharPositions()
 *
 *
 * DESCRIPTION   = Returns the positions (world coordinates) where the printer
 *                 will place each given character, taking into account kern-
 *                 ing and extra vectors provided by the caller.
 *
 *                 Call: ulRsult = prdt_QueryCharPositions(hdc, pptlStartXY,
 *                                           ulOptions,lNumBytes, pchString,
 *                                           plVector,pptlDst, pddc, FunN);
 *                  Notes:
 *
 *                  plVector is OPTIONAL.  If present (CHS_VECTOR set), it
 *                  allows control over the positioning of each character
 *                  after the first.  These are distances measured in world
 *                  coords. (along the baseline for left-to-right and
 *                  right-to-left character directions, and along the
 *                  shearline for top-to-bottom and bottom-to-top).  These
 *                  increments, if specified, set the widths of each char-
 *                  acter.
 *
 * INPUT         = pptlStartXY    = Starting position (OPTIONAL)
 *                 ulOptions      = CHS_VECTOR (bit 1): increment vec. present
 *                                  CHS_START_XY (bit 5): start pos. present
 *                 lNumBytes      = number of bytes in the character string
 *                 pchString      = pointer to the string of codepoints
 *                 plVector       = vector of (lNumBytes) increment values.
 *                                  These are LONGs in world coordinates.
 *                 pptlDst        = pointer to array of (lNumBytes+1) positions
 *                                  First element is current position, last
 *                                  element is new current position.
 *
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = TRUE              ok
 *
 * RETURN-ERROR  = FALSE             error
 *
 **************************************************************************/

ULONG prdt_QueryCharPositions( HDC hdc, PPOINTL pptlStartXY, ULONG ulOptions,
                               LONG lNumBytes, PCH pchString, PLONG plVector,
                               PPOINTL pptlDst, PDDC pddc, ULONG FunN )
{
  ULONG         ulRet;
  register LONG i;
  PB            pbTemp;
  PCHARWIDTH    pcData;
  POINTL        ptlXYtmp;
  SHORT         sCodePoint, sFirst, sLast, sPreviousCodePt, sBreak;
  FIXED         fxCurrentX, fxCurrentY;
  FIXED         fxX, fxY, fxXNew, fxYNew;
  FIXED         fxXCharExtra, fxYCharExtra, fxXBreakExtra, fxYBreakExtra;

  EnterDriver( pddc );

  if (pddc->iType == OD_MEMORY)
  {
    ulRet = (*pfnlQueryCharPositions) (hdc, pptlStartXY,
                                       ulOptions, lNumBytes,
                                       pchString, plVector,
                                       pptlDst, pddc, FunN );
     ExitDriver( pddc );
     return( ulRet );
  }

  /*
  ** if the engine wants to do vector font simulations, we must call
  ** back to the engine's default function here.
  */
  if (pddc->pddcb->text.fFontSimulation)
  {
    ulRet = (*pfnlQueryCharPositions)( hdc, pptlStartXY, ulOptions,
                                       lNumBytes, pchString, plVector,
                                       pptlDst, pddc, FunN );
     ExitDriver( pddc );
     return( ulRet );
  }

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

  /*
  ** Disallow     parameters.
  */
  if (ulOptions & (~(CHS_VECTOR | CHS_START_XY)))
  {
    GplErrSetError(  PMERR_INV_CHAR_POS_OPTIONS );
    ExitDriver( pddc  );
    return( FAILURE );
  }

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

  /*
  ** Debugging output.
  */

  #if    DEBUG
    PrintLog((PSZ)"Options:\n" );

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

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

    if (pptlStartXY)
      PrintLog((PSZ)"Start {x,y} = {%ld,%ld}\n", pptlStartXY->x, pptlStartXY->y)
         ;
    PrintLog((PSZ)"Number of bytes in string = %ld\n", lNumBytes );

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

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

  /*
  ** load current font resource
  */
  if (!pddc->pddcb->cgs.fValidFont)
  {
    if (!prda_CheckFontResource(pddc)) /* couldn't get resources  */
    {
      ExitDriver( pddc  );
      return( FAILURE );               /* Minor function saves error code  */
    }

    /* utlps.c  */
    /*
    ** ps_selectfont(pddc );
    */
  }

  /*
  ** Point to start of character metrics
  */
  pbTemp = (PB)pddc->pddcb->text.pfdfFontDef;
  pbTemp += pddc->pddcb->text.pfdfFontDef->Header.usCharMetricsOffset;

  /*
  ** Calculate extra character spacing factors.
  */
  fxXCharExtra = frmul( pddc->pddcb->text.fxCos, pddc->pddcb->text.ChrBundle.fxExtra );
  fxYCharExtra = frmul( pddc->pddcb->text.fxSin, pddc->pddcb->text.ChrBundle.fxExtra );
  fxXBreakExtra = frmul( pddc->pddcb->text.fxCos,
                         pddc->pddcb->text.ChrBundle.fxBreakExtra );
  fxYBreakExtra = frmul( pddc->pddcb->text.fxSin,
                         pddc->pddcb->text.ChrBundle.fxBreakExtra );

  /*
  ** Set the start point
  */
  if (ulOptions & CHS_START_XY)
  {
    PrintLog( (PSZ) "Shifting current point from {%ld,%ld} to {%ld,%ld}\n",
              pddc->pddcb->pen.ptlCur.x, pddc->pddcb->pen.ptlCur.y, pptlStartXY->x,
              pptlStartXY->y );
    fxCurrentX = LongToFx( pptlStartXY->x ); /* User provided start  */
    fxCurrentY = LongToFx( pptlStartXY->y );
  }
  else
  {
    fxCurrentX = LongToFx( pddc->pddcb->pen.ptlCur.x ); /* Start at current p  */
    fxCurrentY = LongToFx( pddc->pddcb->pen.ptlCur.y );
  }

  /*
  ** compute each character's position, and stuff result into pptlDst
  */
  sFirst = pddc->pddcb->text.pfmFontMetrics->sFirstChar;
  sLast = pddc->pddcb->text.pfmFontMetrics->sLastChar;
  sBreak = sFirst+pddc->pddcb->text.pfmFontMetrics->sBreakChar;

  for (i = 0; i < lNumBytes; i++)
  {
    pptlDst->x = FxToLong( fxCurrentX ); /* stuff a point...  */
    pptlDst->y = FxToLong( fxCurrentY );
    ++pptlDst;                     /* ...and update pointer  */

    /*
    ** Determine the offset to the next character.  If an increment vector
    ** was provided, this is easy.  Otherwise, we must access the metrics
    ** for the character, and consider kerning as well.
    */
    if (ulOptions & CHS_VECTOR)
    {
      fxCurrentX += LongToFx( *plVector++ );
      pchString++;
    }
    else
    {
      /*
      ** Get a useable code point from the string.  This involves remapping
      ** if we are using a code page other than 850, and using the default
      ** character instead of the specified one if the code point is out of
      ** the bounds supported by this font.
      */
      sCodePoint = (SHORT) *pchString++;

      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;
      }
      pcData = (PCHARWIDTH)pbTemp;
      pcData += (sCodePoint - sFirst );   /* point to char's metrics  */

      /*
      ** Get the character's width
      */
      ptlXYtmp.x = (LONG) pcData->wx;
      ptlXYtmp.y = 0L;

      /*
      ** Consider kerning
      */
      if (i && pddc->pddcb->text.bFontHasKerning)
      {
        ptlXYtmp.x += prda_GetKernAmount( sPreviousCodePt, sCodePoint, pddc );
        sPreviousCodePt = sCodePoint;
      }

      /*
      ** We now have the width of the character, plus any kerning, in Adobe
      ** style (1000x1000) units.  We apply the transform matrix to this
      ** information in order to discover the width of the character in
      ** world coordinates.  That width is then added to the current position
      ** in order to yield the new current position.
      */
      fxX = LongToFx( ptlXYtmp.x / 1000L ) + frdiv( LongToFx( ptlXYtmp.x % 1000L ),
                      FX_1000 );
      fxY = LongToFx( ptlXYtmp.y / 1000L ) + frdiv( LongToFx( ptlXYtmp.y % 1000L ),
                      FX_1000 );

      /*
      ** !!!CR Replace this code by a call to TransFormPoints
      */
      fxXNew = frmul( pddc->pddcb->text.transformmatrix.mx_fxM11, fxX) + frmul
                      (pddc->pddcb->text.transformmatrix.mx_fxM21, fxY );
      fxYNew = frmul( pddc->pddcb->text.transformmatrix.mx_fxM12, fxX)+frmul
                      (pddc->pddcb->text.transformmatrix.mx_fxM22, fxY );

      /*
      ** Add size to our current position
      */
      fxCurrentX += fxXNew;
      fxCurrentY += fxYNew;

      /*
      ** If DEVESC_CHAR_EXTRA or DEVESC_BREAK_EXTRA has been invoked, then
      */
      /*
      ** we must alter the new current position, because those escapes alter
      ** the widths of all characters uniformly.  The values defining how
      ** much to alter character widths are already in world coordinates.
      ** This is why we factor this in after the transformation has already
      ** been performed.
      */
      if (sCodePoint == sBreak)
      {
        fxCurrentX += fxXBreakExtra + fxXCharExtra;
        fxCurrentY += fxYBreakExtra + fxYCharExtra;
      }
      else
      {
        fxCurrentX += fxXCharExtra;
        fxCurrentY += fxYCharExtra;
      }
    }
    PrintLog( (PSZ)"Character #%ld ends at (%f,%f)\n", i, fxCurrentX,
              fxCurrentY );
  }

  /*
  ** Stuff the new current position into the last entry of caller's array
  */
  pptlDst->x = FxToLong( fxCurrentX );
  pptlDst->y = FxToLong( fxCurrentY );

  ExitDriver( pddc );
  return( SUCCESS );
}

/***************************************************************************
 *
 * FUNCTION NAME = prdt_QueryTextBox()
 *
 *
 * DESCRIPTION   = We process the specified string as if it
 *                 were to be drawn, using the current
 *                 character attributes, and return an array
 *                 of up to 5 (x,y) coordinate pairs.  The
 *                 first four of these are the coordinates
 *                 of the top-left, bottom-left, top-right
 *                 and bottom-right corners of the
 *                 parallelogram which encom- pases the
 *                 string when drawn.  The fifth point is
 *                 the con- catenation point.  The points on
 *                 the borders of the parall- ogram are
 *                 deemed to be included within the
 *                 parallelogram.
 *
 *                 Call: ulResult = prdt_QueryTextBox (hdc, lNumBytes,
 *                                   pchString,lNumPairs, pptlXY, pddc, FunN );
 *
 *                 Notes: All coordinates are returned in world coordinates.
 *
 * INPUT         = lNumBytes      = number of bytes in the pchString array
 *                 pchString      = pointer to the string of codepoints
 *                 lNumPairs      = number of (x,y) pairs in the pptlXY array
 *                 pptlXY         = pointer to return array of up to 5 (x,y)
 *                                  pairs
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = TRUE              ok
 *
 * RETURN-ERROR  = FALSE             error
 *
 * @96713 - 12-13-94 - QueryTextBox returning       for string with
 *          CHDIR_TOPBOTTOM and CHDIR_BOTTOMTOP
 **************************************************************************/

ULONG prdt_QueryTextBox( HDC hdc, LONG lNumBytes, PSZ pchString,
                         LONG lNumPairs, PPOINTL pptlXY,
                         PDDC pddc, ULONG FunN )
{
  PB            pbTemp;
  PFONTGLOBAL   pfgGlobal;
  PCHARWIDTH    pcChar;
  LONG          lTotWidth = 0L, lMaxWidth = 0L;
  LONG          lMaxUpperBound, lMaxLowerBound, lDirection;
  BOOL          bHorizontal, bDoKerning;
  register LONG ipptlXY = 0L;
  RECTL         Rect;
  POINTL        ptlConcatenate;
  FIXED         fxExtraX;       /* Caused by DEVESC_CHAR_EXTRA  */
  FIXED         fxExtraY;       /* Caused by DEVESC_CHAR_EXTRA  */
  POINTL        ptlExtra;       /* Same as above, rounded off  */
  SHORT         sCodePoint, sFirst, sLast, sPreviousCodePt, i;
  SHORT         sBreak;         /* Code point of break char  */
  ULONG         ulBreakChars = 0L;     /* How many break chars  */
  ULONG         ulNonBreakChars = 0L;  /* How many non-break chars  */
  ULONG         ulRet;
  LONG          lStartOffset, lKernAmount, lActualWidth = 0L;

  EnterDriver( pddc );

  if (pddc->iType == OD_MEMORY)
  {
    ulRet = (*pfnlQueryTextBox) (hdc, lNumBytes, pchString,
                                 lNumPairs, pptlXY, pddc, FunN );
    ExitDriver( pddc );
    return( ulRet );
  }

  /*
  ** if the engine wants to do vector font simulaitons, we must call
  ** back to the engine's default function here.
  */
  if (pddc->pddcb->text.fFontSimulation)
  {
    ulRet = (ULONG)(*pfnlQueryTextBox)( hdc, lNumBytes, pchString, lNumPairs,
                                        pptlXY, pddc, FunN );
    ExitDriver( pddc );
    return( ulRet );
  }

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

  /*
  ** Disallow     parameters.
  */
  if (lNumBytes < 0)
  {
    GplErrSetError(  PMERR_INV_LENGTH_OR_COUNT );
    ExitDriver( pddc );
    return( FAILURE );
  }

  if ((lNumPairs < 0L) || (lNumPairs > 5L))
  {
    PrintLog( (PSZ)"Invalid number of pairs!\n" );
    GplErrSetError(  PMERR_INV_LENGTH_OR_COUNT );
    ExitDriver( pddc );
    return( FAILURE );                 /* Invalid number of pairs  */
  }

  /*
  ** Debugging output.
  */
  #if    DEBUG
    PrintLog( (PSZ)"# bytes = %ld, # pairs = %ld\n", lNumBytes, lNumPairs );

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

      for (sFirst = 0; sFirst < (SHORT)lNumBytes; sFirst++)
           PrintLog( (PSZ)"%c", pchString[sFirst] );
      PrintLog( (PSZ)"\n" );
    }
  #endif                                       /* ifdef DEBUG   */

  /*
  ** load current font directory and definition resources if necessary
  */
  if (!pddc->pddcb->cgs.fValidFont)
  {
    if (!prda_CheckFontResource(pddc)) /* couldn't get resources  */
    {
      ExitDriver( pddc );
      return( FAILURE );               /* Minor function saves error code  */
    }

    /*
    ** ps_selectfont(pddc );
    *//* utlps.c  */
  }

  /*
  ** Set internal flags to keep track of the direction and kerning
  */
  if (pddc->pddcb->text.ChrBundle.usDirection > CHDIRN_BOTTOMTOP)
  {
    PrintLog( (PSZ)"ChrBundle.usDirection has invalid value %d\n",
              pddc->pddcb->text.ChrBundle.usDirection );
    GplErrSetError(  PMERR_INV_CHAR_MODE_ATTR );
    ExitDriver( pddc );
    return( FAILURE );
  }
  else
  {
    bHorizontal = (BOOL)achHorizontal[pddc->pddcb->text.ChrBundle.usDirection];
    lDirection = (LONG)achDirection[pddc->pddcb->text.ChrBundle.usDirection];
  }

  bDoKerning = (pddc->pddcb->text.bFontHasKerning &&
                (pddc->pddcb->text.ChrBundle.usDirection == CHDIRN_LEFTRIGHT) );

  /*
  ** get the upper and lower limits of the font's bounding box
  */
  pbTemp = (PB)pddc->pddcb->text.pfdfFontDef;
  pbTemp += pddc->pddcb->text.pfdfFontDef->Header.usGlobalOffset;
  pfgGlobal = (PFONTGLOBAL)pbTemp;
  lMaxLowerBound = pfgGlobal->sFontBBox[1];
  lMaxUpperBound = pfgGlobal->sFontBBox[3];

  /*
  ** point to the character metrics data
  */
  pbTemp = (PB)pddc->pddcb->text.pfdfFontDef;
  pbTemp += pddc->pddcb->text.pfdfFontDef->Header.usCharMetricsOffset;

  /*
  ** Now we process the string.  If printing horizontally, lTotWidth
  ** will accumulate the total width of the string. If printing vert-
  ** ically, lMaxWidth will track the width of the widest character.
  */
  sFirst = pddc->pddcb->text.pfmFontMetrics->sFirstChar;
  sLast = pddc->pddcb->text.pfmFontMetrics->sLastChar;
  sBreak = sFirst+pddc->pddcb->text.pfmFontMetrics->sBreakChar;
  PrintLog( (PSZ)"Current point = {%ld,%ld}\n", pddc->pddcb->pen.ptlCur.x,
            pddc->pddcb->pen.ptlCur.y );

  for (i = 0; i < (SHORT)lNumBytes; i++)
  {
    /*
    ** Get a useable code point from the string.  This involves remapping
    ** if we are using a code page other than 850, and using the default
    ** character instead of the specified one if the code point is out of
    ** the bounds supported by this font.
    */
    sCodePoint = (SHORT)*pchString++;

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

    /*
    ** Keep a running total on number of chars/breaks in text
    ** CR!!! do we want to really add break extra on for characters that
    ** CR!!! are not in our codepage
    ** CR!!! PageMaker if fill justify mode will create a large break extra
    ** CR!!! and if this character is missing from, say, a foreign language
    ** CR!!! word we really don't want the middle of this word justified
    */
    if (sCodePoint == sBreak)
    {
      ulBreakChars++;
    }
    else
    {
      ulNonBreakChars++;
    }

    if ((sCodePoint < sFirst) || (sCodePoint > (sFirst+sLast)))
    {
      sCodePoint = sFirst+pddc->pddcb->text.pfmFontMetrics->sDefaultChar;
    }
    pcChar = (PCHARWIDTH) pbTemp;
    pcChar += (sCodePoint - sFirst );     /* point to character's metrics  */


    /*
    ** Do something with this character's width
    */
    if (bHorizontal)
    {

      if ( i == 0 )
      { /* for first char */
        if ( pcChar->llx < 0 )
          lStartOffset = (LONG)pcChar->llx;
        else
          lStartOffset = 0;
      }

      if ( i == ((SHORT)lNumBytes - 1) )
      { /* last char  note: 1 char string has above condition too */
        if ( pcChar->urx  > pcChar->wx )
          lActualWidth += (LONG)pcChar->urx;
        else
          lActualWidth += (LONG)pcChar->wx;
      }
      else
      {
        lActualWidth += (lDirection * (LONG)pcChar->wx );
      }

      /*
      ** Sum all the char widths for concat point
      */
      lTotWidth += (lDirection * (LONG)pcChar->wx );
    }
    else
    {

      if ( i == 0 )                      // @96713
      { /* for first char */
        if ( pcChar->llx < 0 )
          lStartOffset = (LONG)pcChar->llx;
        else
          lStartOffset = 0;
      }

      lMaxWidth = max(lMaxWidth, (LONG)(pcChar->urx - pcChar->llx) );
    }

    /*
    ** Account for kerning
    */
    if (i && bDoKerning)
    {
      lKernAmount = prda_GetKernAmount( sPreviousCodePt, sCodePoint, pddc );
      lTotWidth += lKernAmount;
      lActualWidth += lKernAmount;
      sPreviousCodePt = sCodePoint;
    }
  }

  PrintLog( (PSZ)"lMaxUpper = %ld, lMaxLower = %ld, TotWidth = %ld\n",
     lMaxUpperBound, lMaxLowerBound, lTotWidth );

  /*
  ** Now determine how much to adjust the rectangle due to extra
  ** spacing requested by the caller. This is tricky because the extra
  ** spacing quantities are already in world coordinates.
  */
  if ((pddc->pddcb->text.ChrBundle.usDirection == CHDIRN_LEFTRIGHT) &&
     (!pddc->pddcb->path.fPathIsOpen))
  {
    fxExtraX = frmul( LongToFx(ulBreakChars), pddc->pddcb->text.ChrBundle.fxBreakExtra)+
       frmul( LongToFx(ulNonBreakChars+ulBreakChars),
       pddc->pddcb->text.ChrBundle.fxExtra );
    fxExtraY = frmul( fxExtraX, pddc->pddcb->text.fxSin)+
       pddc->pddcb->text.fxErrorCurrentY;
    fxExtraX = frmul( fxExtraX, pddc->pddcb->text.fxCos)+
       pddc->pddcb->text.fxErrorCurrentX;
    ptlExtra.x = FxToLong(fxExtraX );
    ptlExtra.y = FxToLong(fxExtraY );
    pddc->pddcb->text.fxErrorCurrentX = 0L;
    pddc->pddcb->text.fxErrorCurrentY = 0L;
    pddc->pddcb->text.fxErrorNewX = fxExtraX-LongToFx(ptlExtra.x );
    pddc->pddcb->text.fxErrorNewY = fxExtraY-LongToFx(ptlExtra.y );
  }
  else
  {
    ptlExtra.x = 0L;
    ptlExtra.y = 0L;
  }

  /*
  ** We now know some aspects of the text box. Our next step is to
  ** determine the xLeft, xRight, yTop, yBottom, and ptlConcatenate
  ** values for the bounding box. At this point, all values are in
  ** Adobe style (1000x1000) units. Note that text direction plays a
  ** critical role here, and the empty rectangle is a special case.
  */
//Rect.xLeft = 0L;
  Rect.xLeft = lStartOffset;

  if (lNumBytes)
  {
    if (bHorizontal)
    {
      /*
      ** Horizontal text
      */
      Rect.xRight = lActualWidth;
      Rect.yTop = lMaxUpperBound;
      Rect.yBottom = lMaxLowerBound;
      ptlConcatenate.x = lTotWidth;
      ptlConcatenate.y = 0L;
    }
    else
    {
      /*
      ** Vertical text.
      */
      Rect.xRight = lMaxWidth;
      ptlConcatenate.x = 0L;

      if (lDirection == 1L)
      {
        /*
        ** Specifically, bottom to top
        */
        Rect.yBottom = lMaxLowerBound;
        Rect.yTop = (lMaxUpperBound-lMaxLowerBound) * lNumBytes+lMaxLowerBound;
        ptlConcatenate.y = Rect.yTop - lMaxLowerBound;
      }
      else
      {
        /*
        ** Specifically, top to bottom.
        */
        Rect.yTop = lMaxUpperBound;
        Rect.yBottom = lMaxUpperBound - ((lMaxUpperBound - lMaxLowerBound)
                                          *lNumBytes );
        ptlConcatenate.y = Rect.yBottom - lMaxUpperBound;
      }
    }
  }
  else
  {
    Rect.xRight = 0L;       /* An empty box     */
    Rect.yTop = 0L;
    Rect.yBottom = 0L;
    ptlConcatenate.x = 0L;
    ptlConcatenate.y = 0L;
  }
  PrintLog( (PSZ)"Untransformed coordinates:\n\txLeft = %ld, xRight = %ld\n",
            Rect.xLeft, Rect.xRight );
  PrintLog( (PSZ)
            "\tyTop = %ld, yBottom = %ld\n\tConcatenation point = (%ld,%ld)\n",
            Rect.yTop, Rect.yBottom, ptlConcatenate.x, ptlConcatenate.y );
  PrintLog( (PSZ)"Extra spacing vector (in world coords) is (%ld,%ld)\n",
            ptlExtra.x, ptlExtra.y );

  /*
  ** We now have the xLeft, xRight, yTop, yBottom, and ptlConcatenate
  ** points for the rectangle in Adobe style units. The final step is
  ** to convert this data into five coordinate pairs (lower left,
  ** upper right, etc), transform the pairs into world coordinates,
  ** and return the desired number of pairs to the caller.
  */
  if (lNumPairs > 0)
  {
    pptlXY->x = Rect.xLeft;            /* upper left  */
    pptlXY->y = Rect.yTop;
    prda_FontTransform( pptlXY, pddc );  /* transform it        */
    PrintLog( (PSZ)"\ttransformed upper left is {%ld,%ld}\n", pptlXY->x,
              pptlXY->y );
    pptlXY++;
  }

  if (lNumPairs > 1)
  {
    pptlXY->x = Rect.xLeft;            /* lower left   */
    pptlXY->y = Rect.yBottom;
    prda_FontTransform(pptlXY, pddc );  /* transform it */
    PrintLog( (PSZ)"\ttransformed lower left is {%ld,%ld}\n", pptlXY->x,
              pptlXY->y );
    pptlXY++;
  }

  if (lNumPairs > 2)
  {
    pptlXY->x = Rect.xRight;           /* upper right */
    pptlXY->y = Rect.yTop;
    prda_FontTransform( pptlXY, pddc );  /* transform it        */
    pptlXY->x += ptlExtra.x;
    pptlXY->y += ptlExtra.y;
    PrintLog( (PSZ)"\ttransformed upper right is {%ld,%ld}\n", pptlXY->x,
              pptlXY->y );
    pptlXY++;
  }

  if (lNumPairs > 3)
  {
    pptlXY->x = Rect.xRight;           /* lower right */
    pptlXY->y = Rect.yBottom;
    prda_FontTransform(pptlXY, pddc );  /* transform it */
    pptlXY->x += ptlExtra.x;
    pptlXY->y += ptlExtra.y;
    PrintLog( (PSZ)"\ttransformed lower right is {%ld,%ld}\n", pptlXY->x,
              pptlXY->y );
    pptlXY++;
  }

  if (lNumPairs > 4)
  {
    pptlXY->x = ptlConcatenate.x;      /* concat point */
    pptlXY->y = ptlConcatenate.y;
    prda_FontTransform(pptlXY, pddc );  /* transform it */
    pptlXY->x += ptlExtra.x;
    pptlXY->y += ptlExtra.y;
    PrintLog( (PSZ)"\ttransformed concat point is {%ld,%ld}\n", pptlXY->x,
              pptlXY->y );
  }
  ExitDriver( pddc );
  return( SUCCESS );
}
