/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT (C) Lexmark 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 = PRDTDFNT
 *
 * DESCRIPTIVE NAME =
 *
 *
 * VERSION
 *
 * DATE
 *
 * DESCRIPTION
 *
 * FUNCTIONS   prdt_TextOutDevFont
 *
 * ENTRY POINTS:
 *
 * DEPENDENCIES:
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/

#define INCL_32                        /* CON3201 */
#define INCL_DOSPROCESS                /* CON3201 */
#define INCL_DOSSEMAPHORES
#define INCL_GPIPRIMITIVES
#define INCL_WINHEAP
#define INCL_GPIBITMAPS
#define INCL_GPIERRORS
#include <os2.h>
#undef INCL_DOSPROCESS                 /* CON3201 */
#undef INCL_DOSSEMAPHORES
#undef INCL_GPIPRIMITIVES
#undef INCL_WINHEAP
#undef INCL_GPIBITMAPS
#undef INCL_GPIERRORS

#define INCL_DDICOMFLAGS
#define INCL_DDIBUNDLES
#define INCL_DDIFONTSTRUCS
#define INCL_DDIDEFS
#define INCL_DDIMISC
#define INCL_GRE_XFORMS
#define INCL_GRE_LINES
#include <pmddi.h>
#undef INCL_DDICOMFLAGS
#undef INCL_DDIBUNDLES
#undef INCL_DDIFONTSTRUCS
#undef INCL_DDIDEFS
#undef INCL_DDIMISC
#undef INCL_GRE_XFORMS
#undef INCL_GRE_LINES

#define INCL_WINP_SELECTIVE
#define INCL_WINP_SEI
#include <pmwinx.h>
#undef INCL_WINP_SELECTIVE
#undef INCL_WINP_SEI
#undef INCL_32                         /* CON3201 */

#include <prdconse.h>
#include <prddcone.h>
#include <prdtcone.h>

#define PRDMTYPE_INCL
#define PRDLTYPE_INCL
#define PRDFTYPE_INCL
#define NO_CONSTANT_INCL
#include <prdinclt.h>
#undef NO_CONSTANT_INCL
#undef PRDMTYPE_INCL
#undef PRDLTYPE_INCL
#undef PRDFTYPE_INCL

#include <prdgextf.h>
#include <prdiextf.h>
#include <prdlextf.h>
#include <prdtextf.h>
#include <prduextf.h>
#include <prdaextf.h>
#include <prdfextf.h>
extern long int _Optlink labs( long int );    /*****CON32O2*******/
/******************************************************************************/
/*  Set up access to engine convert functions                                 */
/******************************************************************************/
extern PFNL  da_ConvertWithMatrix;

/******************************************************************************/
/*  Access Map table to translate code points for simulated fonts.            */
/******************************************************************************/
extern CPTMappingTableEntry CPTMapTable [];


/******************************************************************************/
/*  FUNCTION: prdt_TextOutDevFont                                             */
/*                                                                            */
/*  PARAMETERS:                                                               */
/*                                                                            */
/*  HDC              DcH;            Handle to DC                             */
/*  USHORT           Command;                                                 */
/*  PPOINTL          ArgStartPos     The starting position for the string of  */
/*                                   characters                               */
/*  PRECTL           ArgOpaqueRect;  Pointer to opaque rectangle (if opaque   */
/*                                   rectangle is specified in ArgOptions)    */
/*  ULONG            ArgOptions;     Flags for the various options            */
/*  LONG             ArgCharNum;     Number of characters to output           */
/*  PCH              ArgCodePoints;  Pointer to the first character code point*/
/*                                   in the list to be printed                */
/*  PLONG            ArgPosVector;   Pointer to an array of values used to    */
/*                                   determine the spacing between characters */
/*                                   in the string                            */
/*  lpTextAttrsType  pTextAttrs;     Pointer to struct containing the colors  */
/*                                   and mixes                                */
/*  lpDCI            DCIData;        Pointer to DC Instance data              */
/*  lpFontDataType   pFontData;                                               */
/*                                                                            */
/*  DESCRIPTION:                                                              */
/*                                                                            */
/*  This function handles output in a device image font.  It handles opaque   */
/*  rectangles, bounds accumulation and clipping.  Depending on the           */
/*  CHS_LEAVEPOS bit in ArgOptions the function may or may not update the     */
/*  current position in the DC Instance data after printing the characters.   */
/*  When the string has been clipped correctly the characters to be printed   */
/*  are inserted in the graphics text band by prdt_ChainTextBand.             */
/*                                                                            */
/*  ASSUMPTIONS:                                                              */
/*                                                                            */
/*  Function not called for mode 3 text.                                      */
/******************************************************************************/
/*CON3201
USHORT prdt_TextOutDevFont(DcH, Command, ArgStart, ArgOpaqueRect,
                                  ArgOptions, ArgCharNum, ArgCodePoints,
                                  ArgPosVector, pTextAttrs, DCIData, pFontData)

HDC              DcH;
USHORT           Command;
PPOINTL          ArgStart;
PRECTL           ArgOpaqueRect;
ULONG            ArgOptions;
LONG             ArgCharNum;
PCH              ArgCodePoints;
PLONG            ArgPosVector;
lpTextAttrsType  pTextAttrs;
lpDCI            DCIData;
lpFontDataType   pFontData;
                           */

USHORT prdt_TextOutDevFont(HDC              DcH,
                           ULONG            Command,   /* CON3201 */
                           PPOINTL          ArgStart,
                           PRECTL           ArgOpaqueRect,
                           ULONG            ArgOptions,
                           LONG             ArgCharNum,
                           PCH              ArgCodePoints,
                           PLONG            ArgPosVector,
                           lpTextAttrsType  pTextAttrs,
                           lpDCI            DCIData,
                           lpFontDataType   pFontData)

{
#define TFUNC "prdt_TxtODevFnt"

    /**************************************************************************/
    /*  Local variables                                                       */
    /**************************************************************************/
    USHORT              NoOfCodePts;
    USHORT              i, j, Index;   /* Loop control variables              */
    DevRect             lcClipRect;    /* The current clip rectangle to clip  */
                                       /* the string to                       */
    DevRect             BoundsRect;    /* Bounding rectangle for bounds       */
                                       /* accumulation                        */
    SHORT               RetVal;
    SHORT               First;         /* The first character in the string to*/
                                       /* be copied to the text band          */
    SHORT               Last;          /* The last character in the string to */
                                       /* be copied to the text band          */
    POINTS              TextStart;     /* The first character cell position   */
    POINTS              TextEnd;       /* The last character cell position    */
    lpDDTType           pDDT;          /* Pointer to DDT in PDB               */
    USHORT              cCharPos;
    USHORT              cCharPosAlloc;
    BOOL                CardInUse;     /* Card already used in the DC         */
    lpCardFontListType  CardData;      /* Card data hanging off PrinterData   */
    USHORT              CardIndex;     /* Indices used to check if card       */
    USHORT              FontIndex;     /* already used                        */
    lpPDBI              PDBInst;       /* Pointer to PDB Instance data        */
    DevRect             FormClipRect;  /* Clip region for current form        */
                                       /* adjusted for the resolution         */
    DevRect             DevOpaqueRect; /* The opaque rect in device coords    */
    LONG                StringWidth;
    USHORT              SCPCommand;
    BOOL                bFixedPitch;
    lpCodePointType     pCPtInfo;
    lpFontInfoType      pFontInfo;
    USHORT              WorkCoords;
    PDCHARBUNDLE        CurTxtAts;
    PLONG               PosVector;
    POINTS              FontSP;
    POINTS              FontCP;
    POINTL              LongCoords;
    POINTL              WorldCP;
    POINTL              WorldSP;
    POINTS              DevSP;
    POINTS              DevCP;
    XFORM               xfm;
    pBMListEntry        ListEntry;
    ULONG               CharWidth;
    USHORT              RootCodePage;
    SHORT               Result;
    SHORT               MaxDescend;
    CHAR                DefaultChar;
    FOCAMETRICS         Metrics;
    PEDFH               pedfhSave;
    DevRect             TargetRect,TrgRect;
    DevRect             DevlcClipRect; /* The current clip rectangle in device*/
                                       /* coordinates                         */
    BOOL                TextPartial;
    BOOL                AddTextChain;
    USHORT              MCPIndex;      /* Index in MultiCp global tbl         */
    RECTL               BoundsRectL;   /* Bounds for clipping processing      */
    PBYTE       CharPrinted   = FNULL; /* Whether each codept printed         */
    PLONG       pCharStartPos = FNULL; /* To hold individual character        */
                                       /* changed to PLONG for PD00076        */
    PSHORT      pCharEndPos   = FNULL; /* positions if position vector        */
    lpGTBEntry  lcGTBEntry    = FNULL; /* Pointer to a GTB entry which we are */
                                       /* inserting                           */
    USHORT      StrikeBotDev;          /* PD00076                             */
    USHORT      StrikeTopDev;
    POINTL      StrikeOutPos;
    SHORT       MaxAscend;
    BYTE        EngAttrs;
    PBYTE      *pbTemp;               /* CON3201 */

    /**************************************************************************/
    /*  Cant use device fonts in Memory DC...                                 */
    /**************************************************************************/
    if (DCIData->DCIDCType == OD_MEMORY)
    {
        LOGERR(TFUNC, "Invalid for Memory DC", FNULL, 0,
               PMERR_INCORRECT_DC_TYPE);
        return (ERROR_ZERO);
    }

    /**************************************************************************/
    /*  Set up local pointers.                                                */
    /**************************************************************************/
    PDBInst     = DCIData->DCIPdbInstance;
    pDDT        = &PDBInst->DDT;
    ListEntry   = (pBMListEntry)DCIData->DCISelBitmap;
    CurTxtAts   = DCIData->DCICurTxtAts;
    pCPtInfo    = &pFontData->CPtInfo;
    pFontInfo   = &pFontData->Info;


    /**************************************************************************/
    /*  ONLY when downloading engine fonts Fix up our font pointer for engine */
    /*  font that will be downloaded                                          */
    /**************************************************************************/
    if (pFontData->Genre == ENG_DOWNLD_FONT)
    {
       pedfhSave = (PEDFH)*((PULONG)pFontInfo->pFont);

       pbTemp = (PBYTE*) ((PULONG)pFontInfo->pFont); /* CON3201 */
       *pbTemp = (PBYTE)pedfhSave->pFont;            /* CON3201 */

/*    ((PBYTE)*((PULONG)pFontInfo->pFont)) = (PBYTE)pedfhSave->pFont; CON3201 */

    }

    /**************************************************************************/
    /*  In order to get the greatest accuracy the coord system used to work   */
    /*  out the character positions will vary with the positioning method.    */
    /**************************************************************************/
    if ((ArgOptions & CHS_VECTOR) ||
        (CurTxtAts->cbnd.usPrecision == CM_MODE2) )
    {
        WorkCoords = COORD_WORLD;
        if (prdg_GetXformMatrix(DcH, WORLD_TO_FONT | KEEP_ORIGIN_SHIFT,
                                    (PXFORM)&xfm, DCIData) != OK)
            return(ERROR_ZERO);
        /**********************************************************************/
        /* PD00493 : Moved this if stmt here to eliminate extra if stmt.      */
        /* If working in world coords we also need the starting position in   */
        /* world and a World -> Font transformation matrix.                   */
        /**********************************************************************/
        if (prdt_GetStartPos(DcH, ArgStart, ArgOptions, &WorldSP,
                                 COORD_WORLD, Command, DCIData) != OK)
            return(ERROR_ZERO);
    }
    else
    {
        WorkCoords = COORD_FONT;
    }

    /**************************************************************************/
    /*  Pick up the starting position in font coords.                         */
    /**************************************************************************/
    if (prdt_GetStartPos(DcH, ArgStart, ArgOptions, &LongCoords, COORD_FONT,
                         Command, DCIData) != OK)
        return(ERROR_ZERO);

    /**************************************************************************/
    /* PD00493 : Check size of coordinates before converting to short type    */
    /**************************************************************************/
    if ( labs(LongCoords.x) > 32767 )
    {
        /**********************************************************************/
        /* Set coordinates to largest value possible for font coordinates so  */
        /* that this text string will be clipped out.                         */
        /**********************************************************************/
        FontSP.x = sgn(LongCoords.x) * 32767;
    }
    else
    {
        FontSP.x = (SHORT)LongCoords.x;
    }

    /**************************************************************************/
    /* PD00493 : Check size of coordinates before converting to short type    */
    /**************************************************************************/
    if ( labs(LongCoords.y) > 32767 )
    {
        /**********************************************************************/
        /* Set coordinates to largest value possible for font coordinates so  */
        /* that this text string will be clipped out.                         */
        /**********************************************************************/
        FontSP.y = sgn(LongCoords.y) * 32767;
    }
    else
    {
        FontSP.y = (SHORT)LongCoords.y;
    }

    RetVal = OK;

    /**************************************************************************/
    /* PD00459 : Move check for COM_DRAW after GetStartPos                    */
    /**************************************************************************/
    if (!(Command & COM_DRAW))
        goto END_DRAWING_BRANCH;

    /**************************************************************************/
    /*  ONLY when downloading engine fonts                                    */
    /**************************************************************************/
    if (pFontData->Genre == ENG_DOWNLD_FONT)
    {

      /************************************************************************/
      /*  All clipping for download engine fonts is done in device coord.     */
      /*  Pick up the starting position in device cords.  - once set the y    */
      /*  coords remain unchanged.                                            */
      /************************************************************************/
      if (prdt_GetStartPos(DcH, ArgStart, ArgOptions, &LongCoords, COORD_DEVICE,
                           Command, DCIData) != OK)
            return(ERROR_ZERO);
      DevSP.x = (SHORT)LongCoords.x;
      DevSP.y = (SHORT)LongCoords.y;
    }

    /**************************************************************************/
    /*  If we need to clip to the opaque rectangle set it up in device coords.*/
    /**************************************************************************/
    if (ArgOptions & CHS_CLIP)
    {
        if (prdg_Convert((PULONG)ArgOpaqueRect, (PULONG)DevOpaqueRect,
                         COORD_WORLD, COORD_DEVICE_WORD, 2L, (hanDC)DcH,
                         Command) != OK)
            return(ERROR_ZERO);
    }

    /**************************************************************************/
    /*  Get space for the CharPrinted array of flags and set it all to FALSE. */
    /*  From here on we have to jump to the function exit on an error so that */
    /*  we can tidy up any allocated memory.                                  */
    /**************************************************************************/
    if (prdg_AllocHeapItem(DCIData, (USHORT)ArgCharNum,
                           (PUSHORT *)&CharPrinted) != OK)
        return(ERROR_ZERO);
    for (i = 0; i < (USHORT)ArgCharNum; i++)
        CharPrinted[i] = FALSE;

    /**************************************************************************/
    /*  Set up fixed pitch flag.                                              */
    /**************************************************************************/
    if (pFontData->Genre == ENG_DOWNLD_FONT)
        bFixedPitch = 0;
    else
        bFixedPitch = pFontInfo->pFont->WidthFlag & FMF_WIDTH_MASK;

    /**************************************************************************/
    /*  Adjust the form clip rectangle from the device resolution to the font */
    /*  resolution and store a local copy.  After the clip rect is adjusted   */
    /*  for clipping to an opaque rect (if nec.) all clipping calculations are*/
    /*  done in the font resolution.                                          */
    /**************************************************************************/
    FormClipRect[0].X = prdt_XformFontValue(PDBInst->FormClipRegion[0].X,
                                            DEVICE_TO_FONT | CONV_X_VALUE,
                                            pFontData);
    FormClipRect[0].Y = prdt_XformFontValue(PDBInst->FormClipRegion[0].Y,
                                            DEVICE_TO_FONT | CONV_Y_VALUE,
                                            pFontData);
    FormClipRect[1].X = prdt_XformFontValue(PDBInst->FormClipRegion[1].X,
                                            DEVICE_TO_FONT | CONV_X_VALUE,
                                            pFontData);
    FormClipRect[1].Y = prdt_XformFontValue(PDBInst->FormClipRegion[1].Y,
                                            DEVICE_TO_FONT | CONV_Y_VALUE,
                                            pFontData);

    /**************************************************************************/
    /*  PD00267 : Pick up the bounding rectangle for the image and pass it to */
    /*  prdi_GetNextClip().                                                   */
    /**************************************************************************/
    BoundsRectL.xLeft   = (ULONG)PDBInst->FormClipRegion[0].X;
    BoundsRectL.yBottom = (ULONG)PDBInst->FormClipRegion[0].Y;
    BoundsRectL.xRight  = (ULONG)PDBInst->FormClipRegion[1].X + 2; /* PD00308 */
    BoundsRectL.yTop    = (ULONG)PDBInst->FormClipRegion[1].Y + 2; /* PD00308 */

    /**************************************************************************/
    /*  Now perform the main processing to 'print' the string.  This section  */
    /*  is only executed if COM_DRAW is set.  The string examined for every   */
    /*  valid clip rectangle.                                                 */
    /*                                                                        */
    /*  If an opaque rectangle is present and we are required to clip         */
    /*  characters to this then intersect the current clip rectangle with the */
    /*  opaque rectangle.  This is done in device coords; further character   */
    /*  clipping is done in font coords.  A character is drawn if it          */
    /*  intersects with a clip rect, lies completely within the form and      */
    /*  hasn't been drawn in a previous clip rect.                            */
    /*                                                                        */
    /*  Now we go into a loop scanning the string to determine a substring    */
    /*  that which we can print.  Each character in the string is tested until*/
    /*  we find the first one we can print and then we record the index of    */
    /*  this character in the string, then we carry on checking to determine  */
    /*  the last character we can print and record the index of this          */
    /*  character.                                                            */
    /*                                                                        */
    /*  When we have determined all the characters which are inside the clip  */
    /*  rectangle and the start and end positions of this substring we can    */
    /*  convert this information into a graphics text band entry and insert   */
    /*  into the text band with prdt_ChainTextBand.                           */
    /*                                                                        */
    /*  Use a goto here to avoid excessive indentation.                       */
    /**************************************************************************/

    /**************************************************************************/
    /*  While there is another valid clip rectangle.                          */
    /**************************************************************************/
    for (i = 1; prdi_GetNextClip(DCIData, i, (DevRect  *)lcClipRect,
                                 &BoundsRectL) == OK; i++)
    {
        /**********************************************************************/
        /*  If an opaque clip rectangle is supplied and clipping of text to   */
        /*  the rectangle is required then set the clip to the intersection of*/
        /*  the current clip region and the rectangle.                        */
        /**********************************************************************/
        if (ArgOptions & CHS_CLIP)
        {

            /******************************************************************/
            /*  If there is no intersection between the clip rectangle and the*/
            /*  opaque rectangle try the next clip rectangle.                 */
            /******************************************************************/
            if (!prdi_ClipIntSect((DevRect  *)DevOpaqueRect,
                                  (DevRect  *)lcClipRect))
                continue;
        }

        /**********************************************************************/
        /*  Save the clip rectangle in device coordinates for download engine */
        /*  fonts ONLY because the clipping checks are done in device         */
        /*  coordinates                                                       */
        /**********************************************************************/
        if (pFontData->Genre == ENG_DOWNLD_FONT)
        {
            DevlcClipRect[0] = lcClipRect[0];
            DevlcClipRect[1] = lcClipRect[1];
        }

        /**********************************************************************/
        /*  Adjust the clip region to font coords.                            */
        /**********************************************************************/
        lcClipRect[0].X = prdt_XformFontValue(lcClipRect[0].X,
                                              DEVICE_TO_FONT | CONV_X_VALUE,
                                              pFontData);
        lcClipRect[0].Y = prdt_XformFontValue(lcClipRect[0].Y,
                                              DEVICE_TO_FONT | CONV_Y_VALUE,
                                              pFontData);
        lcClipRect[1].X = prdt_XformFontValue(lcClipRect[1].X,
                                              DEVICE_TO_FONT | CONV_X_VALUE,
                                              pFontData);
        lcClipRect[1].Y = prdt_XformFontValue(lcClipRect[1].Y,
                                              DEVICE_TO_FONT | CONV_Y_VALUE,
                                              pFontData);

        /**********************************************************************/
        /*  Adjust the y coord now so that the character clipping tests are   */
        /*  easier.  The current positions need to be reset to the start      */
        /*  positions for each clip rectangle.  Once set up here the y-coords */
        /*  do not change below.                                              */
        /**********************************************************************/
        /*  PD00226 :                                                         */
        /*  Scale the max res. (Height - BaseOffset) to DEVICE resolution so  */
        /*  that it matches any approximation performed on the fontmetrics    */
        /*  values which may have been used by the application to arrive at   */
        /*  the current print position. (eg. yMaxDescender of 6 becomes 2     */
        /*  for 24-wire printer Draft_12 at Y_resolution=72)                  */
        /*  THEN,                                                             */
        /*  Scale this value back 'up' to FONT resolution so that it matches  */
        /*  scaling of the Y print position. (eg. 2 becomes 5, and a y_value  */
        /*  of 2 coming into this routine was tranformed to 5 also.)          */
        /*                                                                    */
        /*  GWD  17Jul91 for PTR PD 000226.                                   */
        /**********************************************************************/
        FontCP = FontSP;
        MaxDescend = (pCPtInfo->Height - pCPtInfo->BaseOffset);

        MaxDescend = prdt_XformFontValue(MaxDescend,
                                     FONT_TO_DEVICE | CONV_Y_VALUE,
                                     pFontData);

        MaxDescend = prdt_XformFontValue(MaxDescend,
                                     DEVICE_TO_FONT | CONV_Y_VALUE,
                                     pFontData);
        FontCP.y -= MaxDescend;

        WorldCP = WorldSP;
        if (pFontData->Genre == ENG_DOWNLD_FONT)
        {

           /*******************************************************************/
           /*  Set the current position in device cords for download engine   */
           /*  fonts ONLY.                                                    */
           /*******************************************************************/
           DevCP.x = DevSP.x;
           DevCP.y = DevSP.y - (pCPtInfo->Height - pCPtInfo->BaseOffset);
        }

        /**********************************************************************/
        /*  Check clipping in the Y direction - a character is drawn if:      */
        /*                                                                    */
        /*    - any part of the char box lies within the clip rect            */
        /*    - if the char box lies completely within the form               */
        /*                                                                    */
        /*  Note assumption that Height and BaseOffset do not vary characters.*/
        /*  NOTE - For download engine fonts :                                */
        /*                                                                    */
        /*    - the whole char is drawn if it lies completely within the clip */
        /*      region.                                                       */
        /*    - If the character is clipped, the partially clipped character  */
        /*      is drawn into the bitmap.                                     */
        /*                                                                    */
        /*  So do not check the following for engine font.                    */
        /**********************************************************************/
        if (pFontData->Genre != ENG_DOWNLD_FONT)
        {
           if ((FontCP.y >= lcClipRect[1].Y) ||
               (FontCP.y + pCPtInfo->Height <= lcClipRect[0].Y) ||
               (FontCP.y < FormClipRect[0].Y) ||
               (FontCP.y + pCPtInfo->Height > FormClipRect[1].Y))
              continue;
        }

        /**********************************************************************/
        /*  Calculate the string of characters that fall into the clip region */
        /*  and set up a text band entry for these characters.  First and Last*/
        /*  are counted from 0; initialise them to -1.                        */
        /**********************************************************************/
        First = VALUE_NOT_SET;
        Last  = VALUE_NOT_SET;
        TextPartial = FALSE;
        for (j = 0; j < (USHORT)ArgCharNum; j++)
        {

            /******************************************************************/
            /*  Set up access to current code point information.  In fact     */
            /*  since only the width is used no need to call if this is a     */
            /*  fixed pitch font.                                             */
            /******************************************************************/
            if (!bFixedPitch)
                prdt_NextCodePoint((USHORT)ArgCodePoints[j], pFontData,
                                   DCIData);

            /******************************************************************/
            /*  Check clipping in the x direction.  The character is drawn if:*/
            /*                                                                */
            /*    - it hasn't already been printed                            */
            /*    - any part of the char box lies within the clip region      */
            /*    - the char box lies completely within the form              */
            /******************************************************************/

            /******************************************************************/
            /*  Set the flag to add the chatacter to text chain to FALSE.     */
            /*  This flag is used both for device fonts as well when          */
            /*  downloading engine fonts.                                     */
            /******************************************************************/
            AddTextChain = FALSE;

            /******************************************************************/
            /*  The following IF THEN block is executed ONLY when downloading */
            /*  engine fonts.                                                 */
            /******************************************************************/
            if (pFontData->Genre == ENG_DOWNLD_FONT)
            {

               /***************************************************************/
               /*  Create a rectangle representing the character cell in      */
               /*  TargetRect                                                 */
               /***************************************************************/
               TargetRect[0].X = DevCP.x;
               TargetRect[0].Y = DevCP.y;
               TrgRect[0]      = TargetRect[0];
               TrgRect[1].X    = TargetRect[1].X = TargetRect[0].X +
                                                   pCPtInfo->Width;
               TrgRect[1].Y    = TargetRect[1].Y = TargetRect[0].Y +
                                                   pCPtInfo->Height;

               /***************************************************************/
               /*  Intersect this rectangle with the current clip rect.       */
               /***************************************************************/
               if (prdi_ClipIntSect((DevRect  *)DevlcClipRect,
                                    (DevRect  *)TrgRect) != OK)
               {
                   TextPartial = FALSE;        /* Really the end              */
               }
               else
               {

                 /*************************************************************/
                 /*  See if we should jump straight to bitmap text handling   */
                 /*  Once a partially clipped character is placed in the the  */
                 /*  bitmap, the TEXTPARTIAL flag is set to TRUE.             */
                 /*************************************************************/
                 if (!TextPartial)
                 {

                   /***********************************************************/
                   /*  Now compare the resultant rectangle contents :         */
                   /*  IF the contents of the rectangles are not the same,    */
                   /*    we have clipped a partial character                  */
                   /*  ELSE we can place the character in the text chain. So  */
                   /*    set the AddTextChain flag to TRUE.                   */
                   /***********************************************************/
                   if (prdu_memcmp((PVOID)TrgRect,(PVOID)TargetRect,
                                   sizeof(TrgRect)) == 0)
                       AddTextChain = TRUE;
                 }
                 if (TextPartial || (!AddTextChain))
                 {
                    TrgRect[0].X -= TargetRect[0].X;
                    TrgRect[0].Y -= TargetRect[0].Y;
                    TrgRect[1].X -= TargetRect[0].X;
                    TrgRect[1].Y -= TargetRect[0].Y;

                    /**********************************************************/
                    /*  Set the device text only flag to FALSE since we're    */
                    /*  about to draw into the band.                          */
                    /**********************************************************/
                    DCIData->TextOnlyDC = FALSE;

                    /**********************************************************/
                    /*  Call the bit image transfer code to place the clipped */
                    /*  character into the bitmap                             */
                    /**********************************************************/
                    (VOID)prdt_ImageChar(pCPtInfo->Defn, (DevRect  *)TrgRect,
                                         DevCP, pCPtInfo->Width,
                                         pCPtInfo->Height,ListEntry,
                                         pTextAttrs);

                    /**********************************************************/
                    /*  Set a special flag so that we process the rest of text*/
                    /*  through here for partially clipped characters         */
                    /**********************************************************/
                    TextPartial = TRUE;

                    /**********************************************************/
                    /*  If this is the first partial character after a        */
                    /*  substring has been added to text chain then set up the*/
                    /*  last character position in the substring.             */
                    /**********************************************************/
                    if (First != VALUE_NOT_SET)
                    {
                        if (Last == VALUE_NOT_SET)
                            Last = j - 1;
                    }
                 }
               }
            }

            /******************************************************************/
            /*  IF device Font                                                */
            /*    Set the AddTextChain flag to TRUE if the character can be   */
            /*    printed.                                                    */
            /******************************************************************/
            if (pFontData->Genre != ENG_DOWNLD_FONT)
            {
                   /***********************************************************/
                   /*  Image font or outline font thats not italic and shear  */
                   /*  angle is zero.                                         */
                   /***********************************************************/
                   if (!((FontCP.x >= lcClipRect[1].X) ||
                       (FontCP.x + pCPtInfo->Width <= lcClipRect[0].X) ||
                       (FontCP.x < FormClipRect[0].X) ||
                       (FontCP.x + pCPtInfo->Width > FormClipRect[1].X)))
                   {
                       AddTextChain = TRUE;
                   }

            }

            /******************************************************************/
            /*  IF the AddTextChain flag is TRUE (both for Device fonts and   */
            /*  when downloading engine font)                                 */
            /******************************************************************/
            if (AddTextChain && !CharPrinted[j])
            {

                /**************************************************************/
                /*  Printing the character so set the flag.                   */
                /**************************************************************/
                CharPrinted[j] = TRUE;
                if (First == VALUE_NOT_SET)
                {

                    /**********************************************************/
                    /*  Found the first character                             */
                    /**********************************************************/
                    First = j;
                    TextStart = FontCP;

                    /**********************************************************/
                    /*  If working in world coords we will need to allocate   */
                    /*  memory for the individual positions.                  */
                    /**********************************************************/
                    if (WorkCoords == COORD_WORLD)
                    {

                        /******************************************************/
                        /*  Allocate buffers large enough to hold the         */
                        /*  positions of this character and each subsequent   */
                        /*  character - this may well be larger than needed to*/
                        /*  store the info for just the printed characters.   */
                        /******************************************************/
                        cCharPosAlloc = (USHORT)ArgCharNum - First;
                        cCharPos = 0;

                        /* PD00076: changed alloc to 4 * cCharPosAlloc        */
                        if (prdg_AllocHeapItem(DCIData, (4 * cCharPosAlloc),
                                               (PUSHORT *)&pCharStartPos) != OK)
                        {
                            RetVal = ERROR_ZERO;
                            goto EXIT_FUNCTION;
                        }
                        if (prdg_AllocHeapItem(DCIData, (2 * cCharPosAlloc),
                                               (PUSHORT *)&pCharEndPos) != OK)
                        {
                            RetVal = ERROR_ZERO;
                            goto EXIT_FUNCTION;
                        }
                    }
                }
                if (WorkCoords == COORD_WORLD)
                {

                    /**********************************************************/
                    /*  Store the individual x coords in font resolution.     */
                    /**********************************************************/
                    /*     PD00076: Typecast to LONG                          */
                    pCharStartPos[cCharPos] = (LONG)FontCP.x;
                    pCharEndPos[cCharPos++] = FontCP.x + pCPtInfo->Width;
                }
            }
            else
            {

                /**************************************************************/
                /*  Dont print this character.  Have we found the last        */
                /*  character?  - if yes break out now so that the current    */
                /*  position is left alone.                                   */
                /**************************************************************/
                if (First != VALUE_NOT_SET)
                {
                    Last = j - 1;
                    break;
                }
            }

            /******************************************************************/
            /*  Calculate new current position.  If we get this in            */
            /*  WorldCoords, must also translate to FontCoords.               */
            /******************************************************************/
            if (ArgOptions & CHS_VECTOR)
            {
                WorldCP.x += ArgPosVector[j];
            }
            else
            {
                if (pCPtInfo->WorkCoords == COORD_FONT)
                    FontCP.x += (SHORT)pCPtInfo->WidthInWorkCoords;
                else
                    WorldCP.x += pCPtInfo->WidthInWorkCoords;
            }
            if (WorkCoords == COORD_WORLD)
            {

                /**************************************************************/
                /*  Pick up the CP in font coords.                            */
                /**************************************************************/
                LongCoords = WorldCP;
                if (!da_ConvertWithMatrix(DcH, (PPOINTL)&LongCoords, 1L,
                                          (PXFORM)&xfm, 0L,
                                          NGreConvertWithMatrix))
                {
                    RetVal = ERROR_ZERO;
                    goto EXIT_FUNCTION;
                }
                FontCP.x = (SHORT)LongCoords.x;
                if (pFontData->Genre == ENG_DOWNLD_FONT)
                {

                  /************************************************************/
                  /*  Pick up the CP in device coords.  ONLY if downloading   */
                  /*  engine fonts                                            */
                  /************************************************************/
                  if (prdg_Convert((PULONG)&WorldCP, (PULONG)&LongCoords,
                                   COORD_WORLD, COORD_DEVICE, 1L, (hanDC)DcH,
                                   COM_TRANSFORM) != OK)
                        return(ERROR_ZERO);
                  DevCP.x = (SHORT)LongCoords.x;
                }
            }
        }

        /**********************************************************************/
        /*  If no substring found to print dont set up a text chain entry -   */
        /*  try the next clip rect.                                           */
        /**********************************************************************/
        if (First == VALUE_NOT_SET)
            continue;

        /**********************************************************************/
        /*  If Last hasnt been set at this point we need to print up to the   */
        /*  end of the string.                                                */
        /**********************************************************************/
        if (Last == VALUE_NOT_SET)
            Last = (USHORT)ArgCharNum - 1;

        /**********************************************************************/
        /*  Store the number of code points and the end coords.               */
        /**********************************************************************/
        NoOfCodePts = Last - First + 1;
        TextEnd = FontCP;

        /**********************************************************************/
        /*  Set the Codepage used flag in the DC instance data to signal that */
        /*  the codepage should be downloaded to the printer if it is a       */
        /*  MultiCp font.                                                     */
        /**********************************************************************/
        if (((MCPIndex = pFontInfo->LCID.CPIndex) != 0xFF) &&
             (pFontData->Genre != ENG_DOWNLD_FONT))
        {
            DCIData->DCIDownCpFlags[MCPIndex & 0x7F].Used = TRUE;
        }

        /**********************************************************************/
        /*  We have found a sub-string which we need to print so set up a text*/
        /*  chain entry for it.  Acquire memory for band entry.  Note that    */
        /*  GTB_ENTRY_SIZE already has space for one code point so we acquire */
        /*  (n-1) more bytes if n is the number of codepoints to be printed in*/
        /*  this sub-string.                                                  */
        /**********************************************************************/
        if (prdg_AllocHeapItem(DCIData, (GTB_ENTRY_SIZE + NoOfCodePts - 1),
                               (PUSHORT *)&lcGTBEntry) != OK)
        {
            RetVal = ERROR_ZERO;
            goto EXIT_FUNCTION;
        }

        /**********************************************************************/
        /*  PD00076:                                                          */
        /**********************************************************************/
        if ( (CurTxtAts->cdef.fFlags & CDEF_STRIKEOUT) &&
             (pFontData->Genre != ENG_DOWNLD_FONT) )
        {
            /******************************************************************/
            /* Strikeout text                                                 */
            /*                                                                */
            /* Only need more one strikeout fot bitmap fonts. In order to use */
            /* the strikeout code designed for outline fonts and avoid        */
            /* cumulative errors at lower resolutions, let us allocate the    */
            /* strikeout rectangles for each character BUT we will only use   */
            /* the first rectangle to do the strikeout for the entire string  */
            /* by calculating the stringwidth.                                */
            /******************************************************************/

            if ( prdg_AllocHeapItem(DCIData, sizeof(FOURPOINTL)*NoOfCodePts,
                                  (PUSHORT *)&lcGTBEntry->GTBStrike ) != OK)
            {
                RetVal = ERROR_ZERO;
                goto EXIT_FUNCTION;
            }

            MaxAscend = pFontData->pMetrics->yMaxAscender;

            /******************************************************************/
            /* PD00076: 12/12/91.                                             */
            /******************************************************************/
            StrikeBotDev = (MaxAscend > 4) ? (MaxAscend/4) : 1;
            if ( pFontInfo->Attr & FATT_DOUBLE_HIGH )
               StrikeBotDev *= 2;

            StrikeTopDev = ( (MaxAscend > 10) ? (MaxAscend/10) : 1 );
            if ( pFontInfo->Attr & FATT_DOUBLE_HIGH )
               StrikeTopDev *= 2;
            StrikeTopDev += StrikeBotDev;

            StrikeBotDev = prdt_XformFontValue(StrikeBotDev,
                                               FONT_TO_DEVICE | CONV_Y_VALUE,
                                               pFontData);

            StrikeTopDev = prdt_XformFontValue(StrikeTopDev,
                                               FONT_TO_DEVICE | CONV_Y_VALUE,
                                               pFontData);

            /******************************************************************/
            /* PD00505 : Make sure StrkTop is larger than StrkBottom to get   */
            /* strikeout                                                      */
            /******************************************************************/
            if ( StrikeBotDev == StrikeTopDev )
                 StrikeTopDev++;


            /******************************************************************/
            /* Also clear the TextOnlyDC flag, as there is now going to be    */
            /* drawing done at PrintText time.  This assumes that             */
            /******************************************************************/
            DCIData->TextOnlyDC = FALSE;
        }
        else
        {
            lcGTBEntry->GTBStrike = FNULL;
        }

        /**********************************************************************/
        /*  Readjust the y coord - this is then tweaked according to the      */
        /*  querks of the individual printers supported.                      */
        /*                                                                    */
        /*  PD00226 :                                                         */
        /*  Use the scaled DOWN and UP  (Height - BaseOffset) value to do     */
        /*  this readjustment. (See where MaxDescend is used above. (We just  */
        /*  need to make sure we add back the same metrics value that was     */
        /*  subtracted earlier to do the Y clip calculations.)                */
        /*                                                                    */
        /*  GWD  31Jul91 for PTR PD00226.                                     */
        /**********************************************************************/
        TextEnd.y   += MaxDescend;
        TextStart.y += MaxDescend;

        /**********************************************************************/
        /* PD00076: Get the start pos for strikeout in device coordinates.    */
        /**********************************************************************/
        if ( (CurTxtAts->cdef.fFlags & CDEF_STRIKEOUT) &&
              (pFontData->Genre != ENG_DOWNLD_FONT) )
        {
           StrikeOutPos.x = (LONG)prdt_XformFontValue(
                                            TextStart.x,
                                            FONT_TO_DEVICE | CONV_X_VALUE,
                                            pFontData);
           StrikeOutPos.y = (LONG)prdt_XformFontValue(
                                            TextStart.y,
                                            FONT_TO_DEVICE | CONV_Y_VALUE,
                                            pFontData);
        }

        /**********************************************************************/
        /*  The 24-wire ProPrinters, when using a y resolution of 180         */
        /*  pels/inch, print a character with a y position based on a point   */
        /*  which is below the Baseline by an offset of the Max Descender -   */
        /*  allow for this.                                                   */
        /*                                                                    */
        /*  PD00587 : do it for 360 too.                                      */
        /*                                                                    */
        /*  PD00561 : Changed TextStart.y and TextEnd.y to yLowerCaseDescent  */
        /*  instead of yMaxDescender for the Nile/Tiber 24 wires.  Pro 24     */
        /*  wire printers will move the strikeout positiion at all            */
        /*  resolutions.  Separate fix is added for 24-wire Pros at           */
        /*  resolutions of * X 72.                                            */
        /**********************************************************************/
        if (pDDT->DDTDriverType == DDT_IBM42XX_DRV)
        {
            if ((pDDT->DDTRasterMode->ResDepth == 180) ||
                 (pDDT->DDTRasterMode->ResDepth == 360))
            {
                if (PDBInst->PrinterType > IBM_PRO_PRINTER_XL24E)
                {
                   /***********************************************************/
                   /*  PD00561 :  If the lowercase descender is NOT the value */
                   /*  for Presentor or ORATOR font then subtract off the     */
                   /*  attribute-adjusted value of the lowercase descender,   */
                   /*  if the font is Presentor or ORATOR then the lowercase  */
                   /*  descent value is zero and nothing should be subtracted */
                   /*  off.  The equation MaxDescend * 8 / 20 calculates the  */
                   /*  value of the lowercase descender.                      */
                   /***********************************************************/
                   if (pFontData->pMetrics->yLowerCaseDescent >= 8)
                   {
                      TextStart.y -= (MaxDescend * 8 / 20);
                      TextEnd.y   -= (MaxDescend * 8 / 20);
                   }
                   else
                   {
                      /******************************************************/
                      /*  Move the Strike out character up for Presentor and*/
                      /*  ORATOR fonts at a vertical resolution of 180 and  */
                      /*  360 up on the page by a value that is equal to the*/
                      /*  LowerCaseDescent value for the current font.      */
                      /******************************************************/
                      StrikeOutPos.y += (MaxDescend * 8 / 20);
                   }
                }
                else
                {
                   /***********************************************************/
                   /*  Move the Strike out character up for the 24-wire Pro-  */
                   /*  printers at a vertical resolution of 72 by half of the */
                   /*  MaxDescender value.                                    */
                   /***********************************************************/
                   StrikeOutPos.y += (MaxDescend / 2);
                }

            }
            else if ( (pDDT->DDTRasterMode->ResDepth == 72) ||
                      (pDDT->DDTRasterMode->ResDepth == 60) )
            {
                /**************************************************************/
                /*  PD00561 : Add six to the start position for 24-wire Pro-  */
                /*  printers at a resolution of * x 72 only.  This moves the  */
                /*  characters vertically up on the page by 6 (MaxDescender)  */
                /**************************************************************/
                if ((PDBInst->PrinterType <= IBM_PRO_PRINTER_XL24E) &&
                    (PDBInst->PrinterType >= IBM_PRO_PRINTER_X24))
                {
                   TextStart.y += pFontData->pMetrics->yMaxDescender;
                   TextEnd.y += pFontData->pMetrics->yMaxDescender;
                   /***********************************************************/
                   /*  Move the Strike out character up for the 24-wire Pro-  */
                   /*  printers at a vertical resolution of 72 by half of the */
                   /*  LowerCaseDescent value.                                */
                   /***********************************************************/
                   StrikeOutPos.y += (MaxDescend / 2);
                }
                else if ((((PDBInst->PrinterType > IBM_TIBER_9) &&
                           (PDBInst->PrinterType < IBM_4226)) ||
                          (PDBInst->PrinterType == IBM_2390_PS1)) &&
                         (pFontData->pMetrics->yLowerCaseDescent < 8))
                {
                   /***********************************************************/
                   /*  PD00561 : This is for the 24 wire Nile/Tibers with     */
                   /*  fonts Presentor and ORATOR only that are DOUBLE_HIGH   */
                   /*  attributed.  This will move the characters vertically  */
                   /*  up on the page by the value of the Lowercase Descender.*/
                   /***********************************************************/
                   if ( pFontInfo->Attr & FATT_DOUBLE_HIGH )
                   {
                      TextStart.y += (MaxDescend * 8 / 20);
                      TextEnd.y   += (MaxDescend * 8 / 20);
                      /********************************************************/
                      /*  Move the Strike out character up for Presentor and  */
                      /*  ORATOR fonts at a vertical resolution of 72 with    */
                      /*  the DOUBLE_HIGH attribute.  It will be moved up by  */
                      /*  a value of half the LowerCaseDescent value.        */
                      /********************************************************/
                      StrikeOutPos.y += (MaxDescend * 4 / 20);
                   }
                   else
                   {
                      /********************************************************/
                      /*  PD00561 : Moves the characters for Nile/Tiber 24    */
                      /*  wire printers vertically up on the page if the font */
                      /*  is either Presentor or ORATOR and it does not have  */
                      /*  the DOUBLE_HIGH attribute.  The position of the     */
                      /*  non-attributed text needed a larger correction than */
                      /*  the DOUBLE_HIGH attributed text.  It is moved up by */
                      /*  a value that is double that of the LowerCaseDescent.*/
                      /********************************************************/
                      TextStart.y += (MaxDescend * 16 / 20);
                      TextEnd.y += (MaxDescend * 16 / 20);
                      /********************************************************/
                      /*  Move the Strike out character up for Presentor and  */
                      /*  ORATOR fonts at a vertical resolution of 72 by a    */
                      /*  value that is half the LowerCaseDescent value.      */
                      /********************************************************/
                      StrikeOutPos.y += (MaxDescend * 4 / 20);
                   }
                }
                else if ( (((PDBInst->PrinterType > IBM_TIBER_9) &&
                            (PDBInst->PrinterType < IBM_4226)) ||
                            (PDBInst->PrinterType == IBM_2390_PS1)) &&
                          (pFontData->pMetrics->yLowerCaseDescent >= 8) &&
                          (pFontInfo->Attr & FATT_DOUBLE_HIGH) )
                {
                   /***********************************************************/
                   /*  PD00561 : Only move the characters down if the font is */
                   /*  NOT Presentor or ORATOR and it IS DOUBLE_HIGH.  The    */
                   /*  DOUBLE_HIGH attribute seemed to print these fonts just */
                   /*  a little to high while the unattributed fonts were     */
                   /*  fine, So the vertical position of the DOUBLE_HIGH      */
                   /*  attributed fonts was moved down just a little bit.     */
                   /***********************************************************/
                   TextStart.y -= 3;
                   TextEnd.y   -= 3;
                }
            }
            else if (pDDT->DDTRasterMode->ResDepth == 120)
            {
               /***************************************************************/
               /*  INKJET : We must move the characters vertically up on the  */
               /*  page for 120 x 120 resolution.  Double High fonts don't    */
               /*  move up quite as far as unattributed fonts.                */
               /***************************************************************/
               TextStart.y += (MaxDescend * 16 / 20);
               TextEnd.y += (MaxDescend * 16 / 20);

               if (pFontInfo->Attr & FATT_DOUBLE_HIGH)
               {
                  TextStart.y -= 6;
                  TextEnd.y -= 6;
               }
            }
        }

        /**********************************************************************/
        /*  Re-adjust the text start and end y coordinates back to the        */
        /*  original resolution - use half rounding (the x coordinates are    */
        /*  left in the font resolution).  For the printers with the Set      */
        /*  Cursor Position command sequence the y coordinates are also left  */
        /*  in the font resolution.                                           */
        /**********************************************************************/
        if (!(pDDT->DDTHorizMoveType & DDT_MOVE_ABS_SCP))
        {
            TextStart.y = prdt_XformFontValue(TextStart.y,
                                              FONT_TO_DEVICE | CONV_Y_VALUE,
                                              pFontData);
            TextEnd.y   = prdt_XformFontValue(TextEnd.y,
                                              FONT_TO_DEVICE | CONV_Y_VALUE,
                                              pFontData);
        }

        /**********************************************************************/
        /*  Set up a GTB entry.  The NextEntry pointer is initialised to a    */
        /*  null pointer (it will be set if required in prdt_ChainTextBand).  */
        /**********************************************************************/
        lcGTBEntry->GTBNextEntry = FNULL;
        lcGTBEntry->GTBNumCdPts  = NoOfCodePts;

        /**********************************************************************/
        /*  If the font is simulated, translate each character from the       */
        /*  simulated code page to the root code page.                        */
        /**********************************************************************/
        if ((pFontData->Simulated ) && (pFontData->Genre != ENG_DOWNLD_FONT))
        {

            /******************************************************************/
            /*  Get the default character and the root code page.  This is the*/
            /*  codepage of the metrics in font data.                         */
            /******************************************************************/
            Metrics      = pFontInfo->pFont->Metrics;
            DefaultChar  = (BYTE) Metrics.usDefaultChar;
            RootCodePage = Metrics.usCodePage;

            /******************************************************************/
            /*  Find the index for the root code page in the map table        */
            /******************************************************************/
            for (Index = 0; Index < CPT_MAP_TABLE_SIZE ; Index++)
            {
                if (CPTMapTable[Index].usRootCP == RootCodePage)
                {

                    /**********************************************************/
                    /*  Found the correct entry, so quit.                     */
                    /**********************************************************/
                    break;
                }
            }
            for (j = 0; j < NoOfCodePts; j++)
            {

                /**************************************************************/
                /*  If return code is an error, then there is no corresponding*/
                /*  character in the root code page.  Therefore we must       */
                /*  substitute the default character.                         */
                /**************************************************************/
                Result = prdt_TranslateChar(ArgCodePoints[First+j],
                                            pFontData->CodeTable, Index);
                if (Result == ERROR_NEG)
                    (lcGTBEntry->GTBCdPts)[j] = DefaultChar;
                else
                    (lcGTBEntry->GTBCdPts)[j] = (CHAR)Result;
            }
        }
        else
        {

            /******************************************************************/
            /*  No simulation required so just copy the code points without   */
            /*  any translation.                                              */
            /******************************************************************/
            for (j = 0; j < NoOfCodePts; j++)
               (lcGTBEntry->GTBCdPts)[j] = ArgCodePoints[First + j];
        }

        /**********************************************************************/
        /*  PD00076:  Handle device bitmap strikeout text.                    */
        /**********************************************************************/
        if ( (CurTxtAts->cdef.fFlags & CDEF_STRIKEOUT) &&
              (pFontData->Genre != ENG_DOWNLD_FONT) )
        {
            /******************************************************************/
            /* PD00642 : Added if stmt to handle mode 2 strikeout text.       */
            /******************************************************************/
            if (CurTxtAts->cbnd.usPrecision == CM_MODE1)
            {
                /**************************************************************/
                /* Strikeout text in character mode 1. StringWidth will be    */
                /* length of ALL the characters in the printed string. This   */
                /* is done to reduce rounding errors so that the strikeout    */
                /* line will end closer to where the string ends.             */
                /**************************************************************/
                /**************************************************************/
                /* PD00642 : Changed 3rd argument from pCharStartPos to       */
                /* &ArgPosVector[First]                                       */
                /* PD00650 : Changed 3rd argument so it will be null when     */
                /* ArgPosVector is null and First is > 0.                     */
                /**************************************************************/
                if (ArgPosVector)
                    PosVector = &ArgPosVector[First];
                else
                    PosVector = ArgPosVector;

                StringWidth = prdt_GetStringWidth((LONG)NoOfCodePts,
                                                  lcGTBEntry->GTBCdPts,
                                                  PosVector,
                                                  DCIData);

                StringWidth = (LONG)prdt_XformFontValue((USHORT)StringWidth,
                                             FONT_TO_DEVICE | CONV_X_VALUE,
                                             pFontData);

                /**************************************************************/
                /* Set up the strikeout rectangle coordinates.  These depend  */
                /* on position, and stringwidth.                              */
                /**************************************************************/
                prdt_GetStrikeoutRect( StrikeOutPos,
                                       0,
                                       0,
                                       (LONG)StrikeBotDev,
                                       (LONG)StrikeTopDev,
                                       (USHORT)StringWidth,
                                       (PFOURPOINTL)&lcGTBEntry->GTBStrike[0]);
            }
            else
            {
                /**************************************************************/
                /* PD00642 : Added code in else leg to handle mode2 strikeout.*/
                /* Strikeout text in character mode 2.                        */
                /* Get a strikeout rectangle for each character.              */
                /**************************************************************/
                for (j = 0; j < NoOfCodePts; j++)
                {
                    /**********************************************************/
                    /* Convert StringWidth from Font to Device Coordinates    */
                    /**********************************************************/
                    StringWidth = pCharEndPos[j] - pCharStartPos[j];

                    StringWidth = (LONG)prdt_XformFontValue((USHORT)StringWidth,
                                                 FONT_TO_DEVICE | CONV_X_VALUE,
                                                 pFontData);

                    StrikeOutPos.x = (LONG)prdt_XformFontValue(
                                                 (USHORT)pCharStartPos[j],
                                                 FONT_TO_DEVICE | CONV_X_VALUE,
                                                 pFontData);

                    prdt_GetStrikeoutRect( StrikeOutPos,
                                        0,
                                        0,
                                        (LONG)StrikeBotDev,
                                        (LONG)StrikeTopDev,
                                        (USHORT)StringWidth,
                                        (PFOURPOINTL)&lcGTBEntry->GTBStrike[j]);
                }
            }
        }

        lcGTBEntry->GTBTxtPos = TextStart;
        lcGTBEntry->GTBTxtEnd = TextEnd;
        if (pCharStartPos)
        {

            /******************************************************************/
            /*  GTBCharPosAlloc is set to the number of chars that space has  */
            /*  been allocated for in the position vector arrays and is stored*/
            /*  so that the driver can free the correct amount off the heap;  */
            /*  it is NOT the number of characters to be printed.             */
            /******************************************************************/
            lcGTBEntry->GTBCharStartPos = pCharStartPos;
            lcGTBEntry->GTBCharEndPos   = pCharEndPos;
            lcGTBEntry->GTBCharPosAlloc = cCharPosAlloc;
        }
        else
        {

            /******************************************************************/
            /*  Text drawn in one go - i.e.  no need to shift the printer head*/
            /*  between chars.                                                */
            /******************************************************************/
            lcGTBEntry->GTBCharStartPos = FNULL;
            lcGTBEntry->GTBCharEndPos   = FNULL;
            lcGTBEntry->GTBCharPosAlloc = 0;
        }

        /**********************************************************************/
        /*  Call prdt_ChainTextBand into the correct position in the gaphics  */
        /*  text band.                                                        */
        /**********************************************************************/
        prdt_ChainTextBand(lcGTBEntry, DCIData);

        /**********************************************************************/
        /*  Copy any other required parameters from either the FontData or the*/
        /*  TextAttrs structures into the text chain entry.  The f/g and b/g  */
        /*  colours are ignored on a mono printer, the b/g is ignored on the  */
        /*  4224-C2.                                                          */
        /**********************************************************************/
        lcGTBEntry->GTBFgClr = (BYTE)pTextAttrs->ForeColor;
        lcGTBEntry->GTBBkClr = (BYTE)pTextAttrs->BackColor;
        lcGTBEntry->GTBInfo = *pFontInfo;

        /**********************************************************************/
        /*  PD00669 : Set the underscore flag in the GTBInfo.LCID EngAttrs    */
        /*  field if CHS_UNDERSCORE is set.                                   */
        /**********************************************************************/
        if (CurTxtAts->cdef.fFlags & CDEF_UNDERSCORE)
        {
            EngAttrs  = prdm_GetLCIDEngineAttrs(lcGTBEntry->GTBInfo.LCID);
            EngAttrs |= FATTR_SEL_UNDERSCORE;
            prdm_PutLCIDEngineAttrs(lcGTBEntry->GTBInfo.LCID, EngAttrs);
        }

        /**********************************************************************/
        /*  ------------------------ IMPORTANT NOTE ------------------------  */
        /*                                                                    */
        /*  GTBInfo was set above to (*pFontInfo) which would make            */
        /*  GTBInfo->pFont to point to FMFfilestruc.  By the time we get print*/
        /*  text command, the contents of FMFfilestruc can change.  Since     */
        /*  GTBInfo->pFont is not used for printer fonts at print time, this  */
        /*  poses no problem.  But for engine fonts we need to save the       */
        /*  pointer to the engine font we want to download at print time.  So */
        /*  ONLY for the case of engine download fonts, we make GTBInfo->pFont*/
        /*  to point to the engine font header in the downloadable engine     */
        /*  fonts' link list that has been hung off the DC.                   */
        /**********************************************************************/
        if (pFontData->Genre == ENG_DOWNLD_FONT)
        {
            lcGTBEntry->GTBInfo.pFont = (lpFMFFileStruc)pedfhSave;
/*          pbTemp = (PBYTE *)&(lcGTBEntry->GTBInfo.pFont);     */ /* CON3201 */
/*          *pbTemp = (PBYTE)pedfhSave;                         */ /* CON3201 */
        }
/* CON3201 - The above two lines replaces the line below              CON3201 */
/*          (PBYTE)lcGTBEntry->GTBInfo.pFont = (PBYTE)pedfhSave;      CON3201 */
        if (pFontData->Simulated)
            lcGTBEntry->GTBCodePage = RootCodePage;
        else
            lcGTBEntry->GTBCodePage = pFontData->pMetrics->usCodePage;
        lcGTBEntry->GTBRegistryId = pFontData->pMetrics->usRegistryId;

        /**********************************************************************/
        /*  For printers with SFG support (4019s and 3816) work out           */
        /*  FontSizeType (FIXED_PITCH, PROPORTIONAL or TYPOGRAPHIC) and       */
        /*  FontSize and put on text chain.                                   */
        /*                                                                    */
        /*  FontSizeType is also used in selecting outline fonts (4019        */
        /*  Heritage only) to get the Escapement Class.                       */
        /*                                                                    */
        /*  PD00295: Nile/Tiber/Dakota need these setup as well.              */
        /**********************************************************************/
        if ((pDDT->DDTInitFlags & DDT_SFG_SUPPORT) &&
            (pFontData->Genre != ENG_DOWNLD_FONT))
        {
            if ((pFontInfo->pFont->WidthFlag & FMF_WIDTH_MASK) == 0)
            {
                /**********************************************************/
                /* PD00371: 238x, 239x, and 4226 need GTBFontSizeType to  */
                /* be PROPORTIONAL, not TYPOGRAPHIC                       */
                /**********************************************************/
                lcGTBEntry->GTBFontSizeType = PROPORTIONAL;
            }
            else
                lcGTBEntry->GTBFontSizeType = FIXED_PITCH;

            /******************************************************************/
            /*  Point Size is held in decipoints (1 decipoint = 1/720th of an */
            /*  inch).  The widths are held in the units given by xDeviceRes. */
            /*  The space character is the 0x20th character.  The font size   */
            /*  used for the SFG command is in 1/1440th of an inch.           */
            /*                                                                */
            /*  PD00371: Test for TYPOGRAPHIC to make logic clearer.          */
            /******************************************************************/
            if (lcGTBEntry->GTBFontSizeType == TYPOGRAPHIC)
            {
                lcGTBEntry->GTBFontSize = 2 * pFontData->pMetrics->
                                              usNominalPointSize;
            }
            else if (lcGTBEntry->GTBFontSizeType == PROPORTIONAL)
            {
                CharWidth = (ULONG)pFontInfo->pFont->CharWidth[0x20];
                lcGTBEntry->GTBFontSize = (USHORT)((CharWidth * 1440L) /
                                          (ULONG)pFontData->pMetrics->
                                          xDeviceRes);
            }
            else                       /* FIXED_PITCH                         */
            {
                CharWidth = (ULONG)(pFontInfo->pFont->WidthFlag &
                            FMF_WIDTH_MASK);
                lcGTBEntry->GTBFontSize = (USHORT)((CharWidth * 1440L) /
                                          (ULONG)pFontData->pMetrics->
                                          xDeviceRes);
            }
        }                              /* ... if SFG_SUPPORT ...              */

        /**********************************************************************/
        /* Set NHFS and NVFS to zero - this will act as a flag in             */
        /* prdp_SetUpFont.                                                    */
        /**********************************************************************/
        lcGTBEntry->GTBNHFS  = 0;
        lcGTBEntry->GTBNVFS  = 0;

        /**********************************************************************/
        /* PD00073 : Added following two fields for non outline fonts         */
        /**********************************************************************/
        lcGTBEntry->GTBShear = 0;
        lcGTBEntry->GTBAngle = 0;

        /**********************************************************************/
        /* PD00670 : Added char precision mode field to text chain structure  */
        /**********************************************************************/
        lcGTBEntry->GTBCharMode = CurTxtAts->cbnd.usPrecision;
    }

END_DRAWING_BRANCH:

    if (!(ArgOptions & CHS_LEAVEPOS) || (Command & COM_BOUND))
    {

        /**********************************************************************/
        /*  Get the total width for the whole character string.               */
        /**********************************************************************/
        if (ArgOptions & CHS_VECTOR)
            PosVector = ArgPosVector;
        else
            PosVector = FNULL;
        StringWidth = prdt_GetStringWidth(ArgCharNum, ArgCodePoints, PosVector,
                                          DCIData);
        if (!(ArgOptions & CHS_LEAVEPOS))
        {

            /******************************************************************/
            /*  We attempts to always retain the highest possible accuracy in */
            /*  font coords.                                                  */
            /******************************************************************/
            if (WorkCoords == COORD_FONT)
            {

                /**************************************************************/
                /*  StringWidth will be in font coords...                     */
                /**************************************************************/
                /**************************************************************/
                /* PD00493 : Check size of result before adding StringWidth   */
                /* to x coordinate to make sure it will not overflow 16 bits  */
                /**************************************************************/
                if ( (32767 - abs(FontSP.x) ) < (SHORT)StringWidth )
                    FontCP.x = sgn(FontSP.x) *  32767;
                else
                    FontCP.x = FontSP.x + (SHORT)StringWidth;

                FontCP.y = FontSP.y;
                /**************************************************************/
                /* PD00550 : Cast both calls below to SHORT before converting */
                /* to LONG to get sign extended for negative numbers.         */
                /**************************************************************/
                LongCoords.x = (LONG)(SHORT)prdt_XformFontValue(FontCP.x,
                                                 FONT_TO_DEVICE | CONV_X_VALUE,
                                                 pFontData);
                LongCoords.y = (LONG)(SHORT)prdt_XformFontValue(FontCP.y,
                                                 FONT_TO_DEVICE | CONV_Y_VALUE,
                                                 pFontData);
                SCPCommand = 0;
            }
            else
            {

                /**************************************************************/
                /*  StringWidth will be in world coords...  This calculation  */
                /*  is inaccurate if there is an angle or shear - but in this */
                /*  case the engine sends us one char at a time so it         */
                /*  shouldn't matter.                                         */
                /**************************************************************/
                WorldCP.x = WorldSP.x + StringWidth;
                WorldCP.y = WorldSP.y;
                LongCoords = WorldCP;
                if (!da_ConvertWithMatrix(DcH, (PPOINTL)&LongCoords, 1L,
                                          (PXFORM)&xfm, 0L,
                                          NGreConvertWithMatrix))
                {
                    RetVal = ERROR_ZERO;
                    goto EXIT_FUNCTION;
                }
                FontCP.x = (SHORT)LongCoords.x;
                FontCP.y = FontSP.y;
                LongCoords = WorldCP;
/* CON3201      SCPCommand = COM_TRANSFORM;             Bits are in HIUSHORT */
                SCPCommand = HIUSHORT(COM_TRANSFORM);
            }
            if (prdl_SetCurrentPosition(DcH, &LongCoords, DCIData,
                                        MAKELONG(NGreSetCurrentPosition,
                                                 SCPCommand)) != OK)
            {
                RetVal = ERROR_ZERO;
                goto EXIT_FUNCTION;
            }

            /******************************************************************/
            /*  Save the CP in font coords for accuracy.                      */
            /******************************************************************/
            DCIData->DCICurrPosFont = FontCP;
            DCIData->DCIStateFlags |= FONT_COORDS_VALID;
        }
        if (Command & COM_BOUND)
        {
            if (WorkCoords == COORD_WORLD)
            {

                /**************************************************************/
                /*  Need to convert StringWidth from a world width to a font  */
                /*  width.  Tweak the World -> Font xform matrix to remove the*/
                /*  origin shift.  This calculation is inaccurate if there is */
                /*  an angle or shear - but in this case the engine sends us  */
                /*  one char at a time so it doesn't matter.                  */
                /**************************************************************/
                LongCoords.x = StringWidth;
                LongCoords.y = 0L;
                xfm.lM41 = 0L;
                xfm.lM42 = 0L;
                if (!da_ConvertWithMatrix(DcH, (PPOINTL)&LongCoords, 1L,
                                          (PXFORM)&xfm, 0L,
                                          NGreConvertWithMatrix))
                {
                    RetVal = ERROR_ZERO;
                    goto EXIT_FUNCTION;
                }
                StringWidth = LongCoords.x;
            }

            /******************************************************************/
            /*  StringWidth is now in Font coords.  Take off the 1 (to get an */
            /*  INCLUSIVE rectangle) at this stage.                           */
            /******************************************************************/
            BoundsRect[0].X = FontSP.x;
            BoundsRect[0].Y = FontSP.y - (pCPtInfo->Height - pCPtInfo->
                                                             BaseOffset);
            BoundsRect[1].X = FontSP.x + (SHORT)StringWidth - 1;
            BoundsRect[1].Y = FontSP.y + pCPtInfo->BaseOffset - 1;

            /******************************************************************/
            /*  Adjust the bounding rect to device coords.                    */
            /******************************************************************/
            BoundsRect[0].X = prdt_XformFontValue(BoundsRect[0].X,
                                                  FONT_TO_DEVICE | CONV_X_VALUE,
                                                  pFontData);
            BoundsRect[0].Y = prdt_XformFontValue(BoundsRect[0].Y,
                                                  FONT_TO_DEVICE | CONV_Y_VALUE,
                                                  pFontData);
            BoundsRect[1].X = prdt_XformFontValue(BoundsRect[1].X,
                                                  FONT_TO_DEVICE | CONV_X_VALUE,
                                                  pFontData);
            BoundsRect[1].Y = prdt_XformFontValue(BoundsRect[1].Y,
                                                  FONT_TO_DEVICE | CONV_Y_VALUE,
                                                  pFontData);
            (VOID)prdg_AddBounds((DevRect  *)BoundsRect, DCIData);
        }
    }

EXIT_FUNCTION:

    /**************************************************************************/
    /*  Free CharPrinted storage                                              */
    /**************************************************************************/
    if (CharPrinted != FNULL)
    {
       (VOID)prdg_FreeHeapItem(DCIData, (USHORT)ArgCharNum,
                                        (PUSHORT)CharPrinted);
    }
    return(RetVal);
}
#undef TFUNC
