/*DDK*************************************************************************/
/*                                                                           */
/* 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.                                */
/*                                                                           */
/*****************************************************************************/
/**********************************************************************/
/*                                                                    */
/*   Module          = EDDNGCHS                                       */
/*                                                                    */
/*   Description     = Display Device Driver minor function handlers  */
/*                     CharString and CharStringPos                   */
/*                                                                    */
/*   Function        =                                                */
/*                                                                    */
/*   Reference       = Winthorn Functional Specification              */
/*                     Device Driver Interface Specification          */
/*                     Display Device Driver Design Specification     */
/*                                                                    */
/*                                                                    */
/**********************************************************************/
/******************************************************************************/
/* CHANGE ACTIVITY:                                                           */
/*                                                                            */
/* FLAG RLSE  DATE  ORIGIN              COMMENTS                              */
/* ---- ---- ------ ------  --------------------------------------------------*/
/* @001 0206 930309 Change  Modified eddt_CharStingPos so it would set        */
/*                  Team    the pCharWidth field of the AIxfer structure      */
/*                          to the appropriate width for increment vector     */
/*                          character widths. (Defect 63056 - BZ)             */
/* @002 0205 930401 Change  Modified eddt_CharStingPos so it would account    */
/*                  Team    for the width of the last character for a vectored*/
/*                          font string when dealing with a font which has    */
/*                          only one charactered currently cached.            */
/*                          (Defect 64392 - BZ)                               */
/******************************************************************************/

/**********************************************************************/
/* Define this for the moment because without it large fonts do not   */
/* work!!!                                                            */
/**********************************************************************/
#define BLTONE

#define INCL_GPICORRELATION
#define INCL_GRE_XFORMS
#define INCL_GRE_STRINGS
#define INCL_DDIMISC
#define INCL_DDICOMFLAGS
#define INCL_GRE_CLIP
#include <eddinclt.h>

#include <eddccone.h>
#include <eddhcone.h>
#include <eddtcone.h>

#include <edddtypt.h>
#include <eddhtype.h>

#include <eddaextf.h>
#include <eddbextf.h>
#include <eddcextf.h>
#include <eddgextf.h>
#include <eddmextf.h>
#include <eddtextf.h>
#ifdef DCAF                                                               //DCAF
#include <dcafextf.h>                                                     //DCAF
#endif                                                                    //DCAF

#include <eddhmacr.h>

#include <eddfcone.h>
#include <eddftype.h>

#include <cursor.h>
#include <hwaccess.h>
#include <memman.h>
#ifdef VRAMPTR
#include <eddncach.h>
#endif /* VRAMPTR */

#ifdef BPP24
#include <convfuns.h>
#endif

#ifdef DBCS                                                 
#include <eddjdef.h>
#include <eddjsub.h>
#include <eddjcach.h>
#include <eddjfm.h>
#include <eddjfont.h>
#endif 


#if (defined(DBCS) && defined(_8514))                       
LONG GetStringWidth (PFOCAFONT pFocaFont, ULONG nChar, PUSHORT pStr);
#endif 


#ifndef DBCS                                                
SHORT               Vector[MAX_STR_LEN];
#endif 

#ifndef   _8514
extern MMReg               ShadowXGARegs;
extern pMMReg              pRealXGARegs;
extern pMMReg              pXGARegs;
#else
#include <8514.h>
#include <cacheman.h>
extern PCACHEMAP           pCacheMap;
extern MM8514Reg           Shadow8514Regs;
extern pMM8514Reg          p8514Regs;
extern DDTType             DDT;
#endif
extern TEXTPB              AIxfer;
extern BitmapHeader        NewPatternTable[];
extern ULONG               PixelOp;
extern BYTE                WinToXway[];
extern PPFNL               EnginesDispatchTable;
extern BOOL                DitheredPattern;
extern BitmapHeader        VectCacheBMHeader;
extern PFONTCACHEINFO      pFontCacheInfo;

extern ClipRectangle       GetClipsBounds;

extern AIWORK              AIWork;
extern CURSORDATA          cursor_data;
//extern USHORT              usVersion;
extern SHORT               softDrawInUse;

extern ULONG                    LinePatternCur;
extern ULONG                    LinePatternPhys;
extern ULONG                    LinePatternSys;
extern ULONG                    MarkerCur;
extern ULONG                    MarkerSys;
extern ULONG                    MarkerPhys;
extern ULONG                    pCurCacheBasePhy;
extern ULONG                    pSysCacheStartPhy;
extern ULONG                    pPhunkPhys;
extern PVOID                    pPhunkVirt;
extern drawFunctionsTable       softDrawTable;
extern drawFunctionsTable       hardDrawTable;
extern SHORT                    foregroundSession;

extern pDrawFunctionsTable      pDrawFunctions;
extern BitmapHeader             DirectListEntry;
extern BOOL                     fXgaDead;

/**********************************************************************/
/* New bits for bitmap/opaque rect clipping fix                       */
/**********************************************************************/
ClipRectangle           SaveOpaqueRect;
ClipRectangle           SaveClipsBounds;

//#define IFNOTGREY(rgb2)                              \
//        (((BYTE)((ULONG)(rgb2)  >> 16) |             \
//         (BYTE)((ULONG)(rgb2)  >> 8 )) ^             \
//         (BYTE)((ULONG)(rgb2)       ))

#define IFNOTGREY(rgb2)                                \
        ( ( (BYTE)((ULONG)(rgb2)  >> 16) ^             \
            (BYTE)((ULONG)(rgb2)       )  ) ||         \
          ( (BYTE)((ULONG)(rgb2)  >> 8 ) ^             \
            (BYTE)((ULONG)(rgb2)       )  ) )

/**********************************************************************/
/* CharString calls CharStringPos                                     */
/**********************************************************************/
DDIENTRY eddt_CharString (HDC       hdc,
                          ULONG     ArgCharnum,
                          PCHAR     ArgCodePoints,
                          PDC       pdcArg,
                          ULONG     FunN)

{
    return(eddt_CharStringPos( hdc, 0, 0, 0, ArgCharnum,
                               ArgCodePoints, 0, 0, pdcArg, FunN));

}






/**********************************************************************/
/* CharStringPos does all the work                                    */
/**********************************************************************/
DDIENTRY eddt_CharStringPos (HDC        hdc,
                             PPOINTL    ArgStart,
                             PPOINTL    ArgOpaqRect,
                             ULONG      ArgOptions,
                             ULONG      ArgCharnum,
                             PCHAR      ArgCodePoints,
                             PULONG     ArgPosVector,
                             PCSP_INFO  ArgAttrs,
                             PDC        pdcArg,
                             ULONG      FunN)

{
    /******************************************************************/
    /* local variables                                                */
    /* Start position and opaque rectangle coords are copied to       */
    /* local array and converted once. Use #defines to access them    */
    /* after conversion to 16 bits.                                   */
    /******************************************************************/
#define StartPosition  (Points[0])
#define OpaqueRect     (&Points[1])
    POINTS             Points[6];    /* must accommodate 32-bit coor  */
    USHORT             NPoints;
    PPOINTS            pBasePoint;
    PPOINTL            pPoint;
    USHORT             Point;
    PSHORT             pVector;
    POINTS             NewCurrentPosn;
    ULONG              ulResult;
    ULONG              ErrCode;      /* error to be logged            */
    ULONG              i, j, k;
    SHORT              DCOriginX;    /* local copy of DC origin       */
    SHORT              DCConv;       /* local copy of conversion fact */
    PUSHORT            PerCharDefs;    /* font per character defs     */
    ULONG              WordsUsed;      /* count of words from perchar */
    USHORT             CharHeight;     /* character height            */
    USHORT             CharWidth;
    USHORT             ASpace;         /* leading spaces              */
    USHORT             BSpace;         /* width of character detail   */
    USHORT             CSpace;         /* trailing spaces             */
    PBYTE              pCharDefn;      /* bytes defining the character*/
#ifdef _8514
    USHORT             CharDefSize;
    POINTS             StartPosition2;
    BOOL               CharTooBig = FALSE;
#endif
#ifdef BLTONE
    BOOL               BltOneChar;     /* if we blt the character     */
#endif /* BLTONE */
    USHORT             BitmapSizeBytes; /* bytes for character bitmap */
    PBYTE              pCharBitmap;  /* bitmap conrresponding to char */
    USHORT             CharBytesPerRow;/* no. of bytes in a codept row*/
    PBYTE              pCharBuffer;  /* loop var to buffer for bitmap */
    ULONG              source_index;
    BOOL               fTextBoxDone;
    ULONG              ulFColor;
    ULONG              ulBColor;
    PFOCAFONT          pFocaFont;
    RECTS              SaveTrgRect;
    RECTS              SaveSrcRect;
    PSHORT             pVc = NULL;    /* Defect 61435 - Init to no mem */

    /******************************************************************/
    /* Get driver semaphore and perform entry checks                  */
    /******************************************************************/
    EnterDriver(pdcArg, FunN, EDF_STANDARD);

#ifdef DBCS                                                 
#define NLSPARSEFLAG    (pdc->CurrentFont.NLSParseFlag)
#define NLSFONTFLAG     (pdc->CurrentFont.NLSFontFlag)
#endif 

#ifndef DBCS                                                
    /******************************************************************/
    /* only allow a maximum of 32767 characters: the high word of the */
    /* number of character parameter should always be zero            */
    /******************************************************************/
    if ( ArgCharnum > 32767 )
#else                                                       
    /******************************************************************/
    /* Now we will allow much less characters at once.  This limit    */
    /* (512 bytes at most) is clearly documented in Programming Ref., */
    /* and ATLAS/VGA DBCS also implemented it.  Also it makes a lot   */
    /* of things much easier for us...                                */
    /*                                                                */
    /* We also check if odd character count is given with DBCS CP.    */
    /******************************************************************/
    if ( (ArgCharnum > MAX_CHARSTRING_LENGTH) ||
         ((pdc->CurrentFont.NLSParseFlag & NLSCA_DBCS) &&
          (ArgCharnum & 1)) )
#endif                                                      
    {
        ErrCode = PMERR_INV_LENGTH_OR_COUNT;
        goto CHARSTRPOS_LOGERR_EXIT;
    }

#ifdef DBCS                                                 
#define CHS_KNOWN (CHS_OPAQUE | CHS_VECTOR | CHS_LEAVEPOS |     \
                   CHS_CLIP | CHS_UNDERSCORE | CHS_STRIKEOUT |  \
                   CHS_START_XY | CHS_ATTR_INFO)
    if (ArgOptions & ~CHS_KNOWN)
    {
        ErrCode = PMERR_INV_CHAR_POS_OPTIONS;
        goto CHARSTRPOS_LOGERR_EXIT;
    }
#endif 
    /******************************************************************/
    /* Defect 62336                                                   */
    /* Check there is a currently selected bitmap                     */
    /******************************************************************/
    if ( pdc->DCIBitmapType == BITMAP_NOT_SELECTED )
    {
        ErrCode = PMERR_BITMAP_NOT_SELECTED;
        goto CHARSTRPOS_LOGERR_EXIT;
    }

    /******************************************************************/
    /* Initialise return code to success                              */
    /******************************************************************/
    ulResult = OK;

    /******************************************************************/
    /* Assume vector not supplied, flag it                            */
    /******************************************************************/
    pVector = NULL;
    AIxfer.bCharWidth = 0;                                    /* @001 */

    pFocaFont = pdc->CurrentFont.pFocaFont;

    /******************************************************************/
    /* It is illegal to draw text within an area and sometimes in a   */
    /* path                                                           */
    /******************************************************************/
    if ( FunNTest(COM_AREA | COM_PATH) )
    {
        if (FunNTest(COM_AREA) )
        {
            /**********************************************************/
            /* Log an error and exit                                  */
            /**********************************************************/
            ErrCode = PMERR_INV_IN_AREA;
            goto CHARSTRPOS_LOGERR_EXIT;
        }

        else /* in a path */
        /**************************************************************/
        /* It is illegal to draw raster text or an opaque rectangle   */
        /* within a path definition.                                  */
        /**************************************************************/
        if ( ArgOptions & (CHS_CLIP | CHS_OPAQUE) )
        {
            ErrCode = PMERR_INV_IN_PATH;
            goto CHARSTRPOS_LOGERR_EXIT;
        }

        if ( !(pFocaFont->fmMetrics.fsDefn & FONT_IS_VECTOR)  )
        {
            ErrCode = PMERR_INV_IN_PATH;
            goto CHARSTRPOS_LOGERR_EXIT;
        }
    }

    /******************************************************************/
    /* Check that simulations are not required                        */
    /******************************************************************/
#ifdef NEW_CHAR_FLAGS
    /******************************************************************/
    /* DCR 25061. If CHS_UNDERSCORE or CHS_STRIKEOUT flags are set,   */
    /* then do the same as if they were set in the text attributes    */
    /* bundle: return to simulation.                                  */
    /******************************************************************/
    if ( pdc->DCITextSim ||
         (ArgOptions & (CHS_UNDERSCORE | CHS_STRIKEOUT)) )
#else /* NEW_CHAR_FLAGS */
    if (pdc->DCITextSim)
#endif /* NEW_CHAR_FLAGS */
    {
        /**************************************************************/
        /* if COM_DEVICE is set we must deal with the request         */
        /**************************************************************/
        if (FunNTest(COM_DEVICE))
        {
            if (pFocaFont->fmMetrics.fsDefn & FONT_IS_VECTOR)
            {
                LogWarning(PMERR_INV_OR_INCOMPAT_OPTIONS);
                goto CHARSTRPOS_OK_EXIT;
            }
        }
        else
        {
            /**********************************************************/
            /* call the engine to provide the simulation              */
            /**********************************************************/

            /**********************************************************/
            /* Release driver semaphore                               */
            /**********************************************************/
            ExitDriver(pdcArg, FunN, EDF_STANDARD | EDF_IGNORE_TIME);
            return EnginesDispatchTable[NGreCharStringPos & 0xff](
                                                          hdc,
                                                          ArgStart,
                                                          ArgOpaqRect,
                                                          ArgOptions,
                                                          ArgCharnum,
                                                          ArgCodePoints,
                                                          ArgPosVector,
                                                          ArgAttrs,
                                                          pdcArg,
                                                          FunN);
        }
    }

#ifdef DBCS                                                 
    /*================================================================*/
    /* Now we have to check if we can do it at once.  Being modified  */
    /*  for DBCS/MBCS handling, this routine now can handle limited   */
    /*  length of string at once.                                     */
    /*                                                                */
    /* Note MUST_CHOP would be very rare case, so we will make little */
    /*  consideration on the performance in this case.                */
    /*================================================================*/
    eddj_CheckProcOptions( ArgCodePoints,
                           ArgCharnum,
                           &pdc->CurrentFont );

    if (NLSPARSEFLAG & NLSCA_MUST_CHOP)
    {
        // if we cannot do it now...
        ULONG ulStringLimit = STRINGLIMIT( &pdc->CurrentFont );
                                       // max we can handle at once
        ULONG ulThisTime;              // #bytes to be processed now
        DevPoint CurPosSave = pdc->DCICurrPosAI;
        POINTL WldPosSave = pdc->DCICurrPosWorld;
                                       // save current pos
        ULONG ArgOptSave = ArgOptions; // save options

        ArgOptions &= ~CHS_LEAVEPOS;   // always update current pos

        while (ArgCharnum)
        {
            // decide how many bytes will be passed to worker
            ulThisTime =
              eddj_ChopString (&pdc->CurrentFont,
                               ArgCodePoints,
                               ArgCharnum,
                               ulStringLimit);
            // call myself as a worker
            ulResult =
              eddt_CharStringPos (hdc,
                                  ArgStart,
                                  ArgOpaqRect,
                                  ArgOptions,
                                  ulThisTime,
                                  ArgCodePoints,
                                  ArgPosVector,
                                  ArgAttrs,
                                  pdcArg,
                                  FunN);

            if (ulResult != GPI_OK)
            {
                ExitDriver( pdcArg, FunN, EDF_STANDARD );
                return ulResult;
            }

            // opaque rect should be drawn only once (in first call)
            ArgOptions &= ~CHS_OPAQUE;

            // do not provide start pos for following call.
            // use updated current pos
            ArgOptions &= ~CHS_START_XY;

            ArgCodePoints += ulThisTime;
            ArgPosVector += ulThisTime;
            ArgCharnum -= ulThisTime;
        }

        // if must not update pos then restore original value
        if (ArgOptSave & CHS_LEAVEPOS)
        {
            pdc->DCICurrPosAI = CurPosSave;
            pdc->DCICurrPosWorld = WldPosSave;
        }

        ExitDriver( pdcArg, FunN, EDF_STANDARD );
        return GPI_OK;
    }
#endif 
    /******************************************************************/
    /* Apply the DC command mask to the command bits                  */
    /******************************************************************/
    COMMANDBITS(FunN) &= pdc->DCICommandMask;

    /******************************************************************/
    /* put the bitmap header pointer in the parameter block           */
    /******************************************************************/
    AIxfer.pbmhDest = pdc->DCISelListEntry;

    /******************************************************************/
    /* Defect 62104                                                   */
    /* If we are dead and the destination is the screen then we need  */
    /* to avoid drawing. We do this by turning off the COM_DRAW bit.  */
    /******************************************************************/
    if ( fXgaDead && (AIxfer.pbmhDest == &DirectListEntry) )
    {
        COMMANDBITS(FunN) &= ~((USHORT)(COM_DRAW >> 16));
    }

    if ((FunNTest(COM_CORRELATE)) &&
        (pdc->DCICorrInvalid))
    {
        /**************************************************************/
        /* Will be doing correlation so get the correlation rectangles*/
        /* up to date                                                 */
        /**************************************************************/
        if ( !eddg_ClipPickWindow() )
        {
            goto CHARSTRPOS_ERR_EXIT;
        }
    }

    /******************************************************************/
    /* if supplied, copy and convert start position and opaque rect   */
    /******************************************************************/
    NPoints = 0;

    if (ArgOptions & (CHS_OPAQUE | CHS_CLIP) )
    {
        /**************************************************************/
        /* If transform bit is on the set up values for convert loop  */
        /**************************************************************/
        if (FunNTest(COM_TRANSFORM))
        {
            NPoints = 2;
            pPoint  = (PPOINTL)&Points[2];
            pBasePoint = &Points[1];
            *((PLONG)&Points[2]) = ArgOpaqRect[0].x; /* reorder  */
            *((PLONG)&Points[3]) = ArgOpaqRect[1].y; /* for AI   */
            *((PLONG)&Points[4]) = ArgOpaqRect[1].x; /* space    */
            *((PLONG)&Points[5]) = ArgOpaqRect[0].y;
        }
        else
        {
            /**********************************************************/
            /* Supplied coordinates are screen coordinates. Convert to*/
            /* AI space. Note ArgOpaqRect      here is EXCLUSIVE.     */
            /* And we want OpaqueRect to be INCLUSIVE (some of the    */
            /* time)                                                  */
            /**********************************************************/
            OpaqueRect[0].x = (SHORT)ArgOpaqRect[0].x;
            OpaqueRect[0].y = pdc->DCIConvFactor -
                            ((SHORT)ArgOpaqRect[1].y - (SHORT)1) +
                              pdc->DCIOrigin.Y;
            OpaqueRect[1].x = ((SHORT)ArgOpaqRect[1].x - (SHORT)1);
            OpaqueRect[1].y = pdc->DCIConvFactor -
                            (SHORT)ArgOpaqRect[0].y + pdc->DCIOrigin.Y;
        }
    }

    if (ArgOptions & CHS_START_XY)
    {
        /**************************************************************/
        /* If coordinates are supplied and do not need to be          */
        /* transformed then they are in screeen coordinates and so    */
        /* already have the DC origin added in. All we have to do is  */
        /* convert to AI coordinates.                                 */
        /**************************************************************/
        if (FunNTest(COM_TRANSFORM))
        {
            ++NPoints;
            pPoint  = (PPOINTL)&Points[0];
            pBasePoint = (PPOINTS)pPoint;
            *((PLONG)&Points[0]) = ArgStart->x;
            *((PLONG)&Points[1]) = ArgStart->y;
        }
        else
        {
            StartPosition.x = (SHORT)ArgStart->x;
            StartPosition.y = pdc->DCIConvFactor - (SHORT)ArgStart->y +
                                                    pdc->DCIOrigin.Y;
        }
    }
    else
    {
        /**************************************************************/
        /* use start position stored in the DCI                       */
        /**************************************************************/
        StartPosition.x = pdc->DCICurrPosAI.X;
        StartPosition.y = pdc->DCICurrPosAI.Y;
    }

    if ( NPoints )
    {
        /**************************************************************/
        /* there are some points to convert                           */
        /**************************************************************/
        if ((FunNTest(COM_TRANSFORM)) &&
            !pdc->DCIXFrmSimple)
        {
            if ( ! EnginesDispatchTable[NGreConvert & 0xff](
                                                        hdc,
                                                        CVTC_WORLD,
                                                        CVTC_DEVICE,
                                                        (PPOINTL)pPoint,
                                                        (LONG)NPoints,
                                                        NULL,
                                                        NGreConvert
                                                      )
               )
            {
                goto CHARSTRPOS_ERR_EXIT;
            }
        }

        /**************************************************************/
        /* Convert to AI space                                        */
        /* NB. rectangle re-ordering done above                       */
        /**************************************************************/
        DCOriginX = pdc->DCIOrigin.X;
        DCConv    = pdc->DCIConvFactor;
        for (Point=0; Point < NPoints; Point++, pBasePoint++, pPoint++)
        {
            /**********************************************************/
            /* Check for overflow in the converted coordinate.        */
            /**********************************************************/
            if ( ( HIUSHORT(pBasePoint->x) !=
                   HIUSHORT((LONG)((SHORT)pBasePoint->x)) ) ||
                 ( HIUSHORT(pBasePoint->y) !=
                   HIUSHORT((LONG)((SHORT)pBasePoint->y)) ) )
            {
                /******************************************************/
                /* The device coordinate is invalid.                  */
                /******************************************************/
                LogError(PMERR_COORDINATE_OVERFLOW);
                goto CHARSTRPOS_ERR_EXIT;
            }

            pBasePoint->x = DCOriginX + (SHORT)pPoint->x;
            pBasePoint->y = DCConv - (SHORT)pPoint->y;

            /**********************************************************/
            /* Check for overflow again.                              */
            /**********************************************************/
            if ( ( HIUSHORT(pBasePoint->x) !=
                   HIUSHORT((LONG)((SHORT)pBasePoint->x)) ) ||
                 ( HIUSHORT(pBasePoint->y) !=
                   HIUSHORT((LONG)((SHORT)pBasePoint->y)) ) )
            {
                /******************************************************/
                /* The device coordinate is invalid.                  */
                /******************************************************/
                LogError(PMERR_COORDINATE_OVERFLOW);
                goto CHARSTRPOS_ERR_EXIT;
            }
        }

    } /* if NPoints */

#ifdef DBCS                                                 
    /*================================================================*/
    /* Now we can convert given string to U-DBCS if necessary, since  */
    /*  we have enough size buffer.                                   */
    /*================================================================*/
    if (pdc->CurrentFont.NLSParseFlag & NLSCA_UNIFIED_DBCS)
    {
        if (!(ArgOptions & CHS_VECTOR))
        {
            ArgPosVector = NULL;
        }

        // convert string and vector result #char -> ArgCharnum
        // (each char is 2 bytes)

        eddj_UnifiedDBCS (&pdc->CurrentFont,
                          ArgCodePoints,
                          ArgCharnum,
                          (PLONG)ArgPosVector,
                          ausString,
                          WORK_BUFFER_SIZE,
                          alVector,
                          &ArgCharnum);

        // we will use this hereafter
        ArgCodePoints = (PCHAR)ausString;
        ArgPosVector = alVector;
    }

    /*================================================================*/
    /* Now if CP is DBCS/MBCS, we have changed:                       */
    /*  ArgCodePoints --> Now points to unified DBCS string           */
    /*  ArgCharnum    --> Now the number of unified DBCS char         */
    /*  ArgPosVector  --> Now points to adjusted vector array         */
    /*                    (i.e. 1 vector for 1 unified DBCS char)     */
    /*================================================================*/
    /*================================================================*/
    /* Additional Error Check for rectangle validity...               */
    /*================================================================*/
    if ( (ArgOptions & (CHS_OPAQUE | CHS_CLIP)) &&
         ((OpaqueRect[0].x > OpaqueRect[1].x) ||
          (OpaqueRect[0].y > OpaqueRect[1].y)) )
    {
        LogError( PMERR_INV_RECT );
        goto CHARSTRPOS_ERR_EXIT;
    }

    /*================================================================*/
    /* if charnum is 0, we need not use increment vectors...          */
    /*================================================================*/
    if (ArgCharnum == 0)
    {
        ArgOptions &= ~CHS_VECTOR;
    }
#endif 

    /******************************************************************/
    /* convert the increment vector to AI space, if supplied          */
    /******************************************************************/
    if ( ArgOptions & CHS_VECTOR )
    {
        /**************************************************************/
        /* convert vector to AI coordinates                           */
        /**************************************************************/
#ifndef DBCS                                                
        if ( ArgCharnum <= MAX_STR_LEN )
        {
          pVector = Vector;
        }

        else /* more chars than fit in the Vector buffer */
        {
          pVector = AllocateMemory(ArgCharnum * 2, MT_TEMP, MO_PRIVATE);
          if ( pVector == NULL )
          {
            goto CHARSTRPOS_ERR_EXIT;
          }
          pVc = pVector;
        }
#else 
        /*------------------------------------------------------------*/
        /* now we can use global buffer here, since we have already   */
        /*  chopped given string if necessary.                        */
        /*------------------------------------------------------------*/
        pVector = asDVector;
#endif 

        if (OK != eddt_ConvertIncVector( pVector, (USHORT)ArgCharnum
                                       , (PLONG)ArgPosVector
                                       )
           )
        {
            goto CHARSTRPOS_ERR_EXIT;
        }
    }

#ifdef BLTONE
    /******************************************************************/
    /* Evaluate bounding rectangle for text.                          */
    /*                                                                */
    /* At this point we check for the special case where we have an   */
    /* engine font (ie has not been realized) which contains a single */
    /* codepoint. This is the form that is used by the engine when it */
    /* caches image representations of vector fonts. These characters */
    /* are blitted to the screen which gives a much better performance*/
    /* than if they were converted to AI format, selected at the AI   */
    /* and thus trash the AI cache.                                   */
    /*                                                                */
    /* Note: this vector font caching is most important at 1.2        */
    /*       At 1.3 the IFI is more important, and that passes us     */
    /*       characters in a slightly more sensible way - however     */
    /*       the first time that the IFI is used for a particular     */
    /*       font it could be mistaken for the 1.2 vector font        */
    /*       caching, hence the extra tests for 1.3                   */
    /*       usVersion holds 0a1e for 1.3                             */
    /*                                                                */
    /* Now we have the bltonechar stuff working (it never did work    */
    /* fully I suspect) we can dispense with checking the version     */
    /* number                                                         */
    /******************************************************************/
//  if ((usVersion < 0x0A1E) &&
    if (pFocaFont->fmMetrics.usLastChar == 0)
    {
        BltOneChar = TRUE;
        AIWork.Bound[0].X = StartPosition.x;
        /**************************************************************/
        /* Its not really an offset it seems to be inclusive          */
        /**************************************************************/
        AIWork.Bound[0].Y = StartPosition.y -
                       (pFocaFont->fdDefinitions.pCellBaseOffset - (USHORT)1);

        /**************************************************************/
        /* Set up the character width and height if they are in header*/
        /**************************************************************/
        if (pFocaFont->fdDefinitions.fsFontdef & 0x02)
        {
            CharHeight = pFocaFont->fdDefinitions.yCellHeight;
        }
        if (pFocaFont->fdDefinitions.fsFontdef & 0x01)
        {
            BSpace = pFocaFont->fdDefinitions.xCellWidth;
        }

        /**************************************************************/
        /* Set up pointer to the per character definition for the     */
        /* character                                                  */
        /**************************************************************/
        PerCharDefs = (PUSHORT)((PBYTE)&(pFocaFont->fmMetrics) +
                                        pFocaFont->fmMetrics.ulSize +
                                          sizeof(FONTDEFINITIONHEADER));

        /**************************************************************/
        /* Now pick up any defined values in the per character defs   */
        /* Whist we are looking at this we may can also pick up the   */
        /* offset to the character definition                         */
        /**************************************************************/
        WordsUsed = 0;
        if (pFocaFont->fdDefinitions.fsChardef & 0x80)
        {
            pCharDefn = (PBYTE)pFocaFont + PerCharDefs[WordsUsed];
            WordsUsed += 2;
        }

        /**************************************************************/
        /* Character Width                                            */
        /**************************************************************/
        if (pFocaFont->fdDefinitions.fsChardef & 0x01)
        {
            BSpace = PerCharDefs[WordsUsed++];
        }

        /**************************************************************/
        /* Character Height                                           */
        /**************************************************************/
        if (pFocaFont->fdDefinitions.fsChardef & 0x02)
        {
            CharHeight = PerCharDefs[WordsUsed++];
        }

        /**************************************************************/
        /* Character Increment                                        */
        /**************************************************************/
        if (pFocaFont->fdDefinitions.fsChardef & 0x04)
        {
            WordsUsed++;
        }

        /**************************************************************/
        /* Pick up A,B and C space from the per character definitions */
        /* if defined there                                           */
        /**************************************************************/
        if (pFocaFont->fdDefinitions.fsChardef == FONTDEFCHAR3)
        {
            ASpace = PerCharDefs[WordsUsed++];
            BSpace = PerCharDefs[WordsUsed++];
            CSpace = PerCharDefs[WordsUsed++];
        }
        else
        {
            ASpace = 0;
            CSpace = 0;
        }

        /**************************************************************/
        /* We can now calculate the bounds. Bounds are inclusive.     */
        /**************************************************************/
        CharWidth = ASpace + BSpace + CSpace;
        AIWork.Bound[1].Y = AIWork.Bound[0].Y + CharHeight - (USHORT)1;

        /**************************************************************/
        /* Calculate the x bound allowing for multiple instances of   */
        /* the single character                                       */
        /**************************************************************/
        /*************************************************************@002*/
        /* NOTE:  When outputting the actual characters of a vectored @002*/
        /*        font string, the widths of all characters except    @002*/
        /*        the last one are passed to us in the increment      @002*/
        /*        vector, so don't forget to add the width of the     @002*/
        /*        last character.                                     @002*/
        /*************************************************************@002*/
        if (pVector)
        {
            AIWork.Bound[1].X = AIWork.Bound[0].X;
            for (i = 0; i < (ArgCharnum - 1); i++)  /* all but last char @002 */
            {
                AIWork.Bound[1].X += pVector[i];
            }
            AIWork.Bound[1].X += CharWidth;         /* last char width   @002 */
        }
        else
        {
            AIWork.Bound[1].X = AIWork.Bound[0].X + (SHORT)(ArgCharnum *
                                   (CharWidth +
                                    pdc->DCICurTxtAts.cdef.charSpacing)) -
                                (SHORT)1;
        }
    }
#endif /* BLTONE */
    else
    {
#ifdef BLTONE
        BltOneChar = FALSE;
#endif /* BLTONE */

#ifdef _8514
        // @RCW - This Cell Height should be dynamically set depending
        //        on whether a seamless app. is running or not.
        //
        if ( pFocaFont->fdDefinitions.yCellHeight >
             (pCacheMap->font_cache_bottom - pCacheMap->font_cache_top))
            CharTooBig = TRUE;

        // dont cache fonts in this case
        if ( !DDT.fCaching )
            CharTooBig = TRUE;

        // cannot use the first 4 rops on screen to screen for S3
        // since there is a     in the silicon @DMS !!!
        // so we cannot cache!!!
        #ifdef   S3
        if ( 5 > WinToXway[pdc->DCICurTxtAts.cbnd.usMixMode] )
            CharTooBig = TRUE;
        #endif

        #ifdef   BPP24
        // if the foreground or background is not grey then use bitblt to draw it
        if ( DDT.BitCount == 24 )
        {
           if (ArgOptions & CHS_ATTR_INFO)
           {
               ulFColor = LogToPhyIndex(ArgAttrs->lColor);
               ulBColor = LogToPhyIndex(ArgAttrs->lBackColor);
           }
           else
           {
               AIxfer.usFColor = ulFColor = pdc->DCICharColatts.ForeColor;
               AIxfer.usBColor = ulBColor = pdc->DCICharColatts.BackColor;
           }

           if ( (IFNOTGREY(ulFColor)) || (IFNOTGREY(ulBColor)) )
               CharTooBig = TRUE;
        }
        #endif
#endif

        AIWork.Bound[0].X = StartPosition.x;

        AIWork.Bound[0].Y = StartPosition.y -
                     (pFocaFont->fdDefinitions.pCellBaseOffset - 1);
        /**************************************************************/
        /* caching uses the hardware so we should protect the cursor  */
        /* code around the caching operations                         */
        /**************************************************************/
        if ( cursor_data.cursor_status & CURSOR_SOFTWARE )
        {
             disable_cursor();
        }

#ifdef _8514
        if (CharTooBig == FALSE)
#endif
        {
            /**************************************************************/
            /* Check that our FontCacheIndex is valid                     */
            /**************************************************************/
            if ( pFontCacheInfo[pdc->CurrentFont.usCachedFontIndex]
                              .usFontID !=pdc->CurrentFont.usFontID )
            {
                /**********************************************************/
                /* Our entry has gone!  The cache must have overflowed    */
                /* and our entry has been invalidated.  Call              */
                /* LocateCachedFont to generate a new entry.              */
                /**********************************************************/
                eddt_LocateCachedFont( &pdc->CurrentFont );
            }

#ifndef DBCS                                                
            /**************************************************************/
            /* Store a pointer to the current FontCacheInfo entry.        */
            /**************************************************************/
            AIxfer.pFontCacheInfo =
                 &pFontCacheInfo[pdc->CurrentFont.usCachedFontIndex];

            /**************************************************************/
            /* Ensure that all of the characters to be drawn are in the   */
            /* VRAM cache.  This routine will also put the width of the   */
            /* string into the variable pointed to by the last parameter. */
            /**************************************************************/
            if (!eddh_update_cache( ArgCodePoints,
                                    ArgCharnum,
                                    AIxfer.pFontCacheInfo,
                                    &pdc->CurrentFont,
                                    (PUSHORT)&AIWork.Bound[1].X ) )
            {
                /**********************************************************/
                /* The cache has been trashed.  Relocate the font and try */
                /* to cache all of the characters again.                  */
                /**********************************************************/
                eddt_LocateCachedFont( &pdc->CurrentFont );

                /**********************************************************/
                /* Store a pointer to the current FontCacheInfo entry     */
                /**********************************************************/
                AIxfer.pFontCacheInfo =
                  &pFontCacheInfo[pdc->CurrentFont.usCachedFontIndex];

                if (!eddh_update_cache( ArgCodePoints,
                                        ArgCharnum,
                                        AIxfer.pFontCacheInfo,
                                        &pdc->CurrentFont,
                                        (PUSHORT)&AIWork.Bound[1].X ) )
                {
                    /******************************************************/
                    /* The string cannot fit into the empty cache!  This  */
                    /* will never happen because our minimum cache size   */
                    /* is 96K, but just in case...                        */
                    /******************************************************/
                    ErrCode = PMERR_INV_LENGTH_OR_COUNT;
                    goto CHARSTRPOS_LOGERR_EXIT;
                }
            }
#else 
            if (eddj_PutStringInCache (ArgCodePoints,
                                       ArgCharnum,
                                       pFontCacheInfo +
                                         pdc->CurrentFont.usCachedFontIndex,
                                       &pdc->CurrentFont,
                                       &AIWork.Bound[1].X))
            {
                // put all string in cache. flush-retry is also done
                // cache index array is stored in asCIndex[].
                AIxfer.pFontCacheInfo =
                     &pFontCacheInfo[pdc->CurrentFont.usCachedFontIndex];
                // should calc here as PutStr.. call may change fontindex

  #ifdef _8514
                AIxfer.usCacheInUse = pdc->CurrentFont.usCachedFontIndex;
  #endif
            }
  #ifdef _8514                                              
            else
            {
                // Failed eddj_PutStringInCach, could not cache string.
                CharTooBig = TRUE;
            }
  #endif 
  #ifdef XGA                                                
            else
            {
                // Try one more...

                eddj_PutStringInCache (ArgCodePoints,
                                       ArgCharnum,
                                       pFontCacheInfo +
                                         pdc->CurrentFont.usCachedFontIndex,
                                       &pdc->CurrentFont,
                                       &AIWork.Bound[1].X);

                AIxfer.pFontCacheInfo =
                     &pFontCacheInfo[pdc->CurrentFont.usCachedFontIndex];
            }
  #endif 
#endif 
        }

        /**************************************************************/
        /* Convert the right-hand bound from a width into an absolute */
        /* position.                                                  */
        /**************************************************************/
#ifdef DBCS                                                 
        // =============================================================
        // Note: this also works for DBCS/MBCS, since we have
        // changed ArgCharnum to contain #char rather than #bytes
        // and charSpacing should be added for each char (regardless
        // of being double byte char or single byte char.)
        // =============================================================
#endif                                                      
        AIWork.Bound[1].X += AIWork.Bound[0].X +
         (USHORT)ArgCharnum * pdc->DCICurTxtAts.cdef.charSpacing;

        /**************************************************************/
        /* Calculate the y bound.                                     */
        /**************************************************************/
        AIWork.Bound[1].Y = AIWork.Bound[0].Y +
                               pFocaFont->fdDefinitions.yCellHeight;
        fTextBoxDone = TRUE;

        /**************************************************************/
        /* caching has finished so we can reenable the cursor         */
        /**************************************************************/
        reenable_cursor();

        /**************************************************************/
        /* Wait for the hardware to finish caching the final          */
        /* character.  This is so that nobody else can interfere with */
        /* the hardware or the scratchpad before the blt has          */
        /* finished.                                                  */
        /**************************************************************/
        WaitForRealHW;

#ifndef DBCS                                                
        /**************************************************************/
        /* Call GetTextBox if we have not got the text box, or if     */
        /* a position vector has been supplied.                       */
        /* Note that pVector is a near pointer !                      */
        /**************************************************************/
        if ( (!fTextBoxDone) || (ArgOptions & CHS_VECTOR) )
        {
            eddt_GetTextBox( (USHORT)ArgCharnum,
                             (PUCHAR)ArgCodePoints,
                             pVector,
                             pdc);
        }
#else 

  #ifdef _8514                                              
        if (CharTooBig)
        {
            eddt_InvalidateCache ();                        //@HSTTT
            eddt_LocateCachedFont (&pdc->CurrentFont);      //@HSTTT
            // query font manager
            StringToGlyph (&pdc->CurrentFont, ArgCodePoints,
                           ausGlyph, ArgCharnum);

            if (pdc->CurrentFont.NLSParseFlag & NLSCA_CALL_DBCSFM)
                eddj_ValidateFontSeg (ArgCharnum, pFocaFont, ausGlyph);

            // each glyph index has two byte
            ArgCodePoints = (PCHAR) ausGlyph;
        }
  #endif                                                    

        /**************************************************************/
        /* we need this only if vector is supplied...                 */
        /**************************************************************/
        if (ArgOptions & CHS_VECTOR)
        {
            AIWork.Bound[1].X = AIWork.Bound[0].X;

            // Note when ArgCharnum==0, we already reset CHS_VECTOR.
            for (i = 0; i < ArgCharnum-1; i++)
                AIWork.Bound[1].X += pVector[i];

  #ifdef _8514                                              
            if (!CharTooBig)
  #endif 
            {
                FCCHARDEF *pCharDef;

                // cache index is set by prev. call to PutStringInCache in asCIndex...
                pCharDef = &AIxfer.pFontCacheInfo->
                             aCharDef[asCIndex[ArgCharnum-1]];

                // add width of last char
                AIWork.Bound[1].X +=
                    (pFocaFont->fdDefinitions.fsChardef == FONTDEFCHAR3) ?
                        pCharDef->ABC.usAspace +
                        pCharDef->ABC.usBspace +
                        pCharDef->ABC.usCspace :
                    pCharDef->nonABC.usWidth;
            }
  #ifdef _8514                                              
            else
            {
                AIWork.Bound[1].X += (SHORT)
                    GetStringWidth (pFocaFont,
                                    1,
                                    &(((PUSHORT)ArgCodePoints)[ArgCharnum-1]));
            }
  #endif 
        }
  #ifdef _8514                                              
        else
        {
            if (CharTooBig)
            {
                AIWork.Bound[1].X = AIWork.Bound[0].X;
                AIWork.Bound[1].X += (USHORT)ArgCharnum * pdc->DCICurTxtAts.cdef.charSpacing;

                AIWork.Bound[1].X += (SHORT)
                    GetStringWidth (pFocaFont,
                                    ArgCharnum,
                                    (PUSHORT)ArgCodePoints);
            }
        }
  #endif 

#endif 

        /**************************************************************/
        /* The top right coordinate returned by GetTextBox is         */
        /* exclusive. Make it inclusive.                              */
        /**************************************************************/
        AIWork.Bound[1].X--;
        AIWork.Bound[1].Y--;

    }

    /******************************************************************/
    /* If required to update current position, save it here           */
    /* NB. even tho' string is horizontal, we must save new Y coord   */
    /* for cases where CHS_START_XY was set                           */
    /******************************************************************/
    if ( (ArgCharnum != 0) &&
         (!(ArgOptions & CHS_LEAVEPOS) || (pVector != NULL)) )
    {
        /******************************************************/
        /* The text string bounds have a right boundary which */
        /* is the position of the last character plus the     */
        /* character width.                                   */
        /* For a position vector we want the position of the  */
        /* last character plus the position vector increment  */
        /* for the next character x start.                    */
        /******************************************************/
        if ( pVector != NULL )
        {
           NewCurrentPosn.x = AIWork.Bound[0].X;

           for ( i = ArgCharnum; i--; )
           {
              NewCurrentPosn.x += pVector[i];
           }

        }

        else /* no position vector */
        {
           NewCurrentPosn.x = AIWork.Bound[1].X + 1;
        }
        NewCurrentPosn.y = StartPosition.y;
    }

    /******************************************************************/
    /* Adjust the start position for the base line offset             */
    /******************************************************************/
#ifdef BLTONE
    if (BltOneChar == TRUE)
    {
        /**************************************************************/
        /* Not really an offset                                       */
        /**************************************************************/
        StartPosition.y -=
                       pFocaFont->fdDefinitions.pCellBaseOffset - 1;
    }
    else
#endif /* BLTONE */
    {
        StartPosition.y += pFocaFont->fdDefinitions.yCellHeight -
                           pFocaFont->fdDefinitions.pCellBaseOffset;
    }

    /******************************************************************/
    /* redefine the type of AIxfer to save cluttering up the code     */
    /* with casts                                                     */
    /******************************************************************/
#define AIxfer (*(PBITBLTPB)&AIxfer)

    /******************************************************************/
    /* Initialise the global pointer to the clip regions              */
    /******************************************************************/
    AIxfer.pNextDestClipRect = pdc->DCIClipRects;


    if (ArgOptions & CHS_OPAQUE)
    {
        /**************************************************************/
        /* opaque rectangle required                                  */
        /* - take union of bounds rectangles                          */
        /**************************************************************/
        AIWork.Bound[0].X = min( AIWork.Bound[0].X, OpaqueRect[0].x);
        AIWork.Bound[0].Y = min( AIWork.Bound[0].Y, OpaqueRect[0].y);
        AIWork.Bound[1].X = max( AIWork.Bound[1].X, OpaqueRect[1].x);
        AIWork.Bound[1].Y = max( AIWork.Bound[1].Y, OpaqueRect[1].y);

        /**************************************************************/
        /* set up a parameter block to blt the opaque rectangle.      */
        /* the NOSHADE pattern is used to give a background colour    */
        /* fill                                                       */
        /**************************************************************/
        AIxfer.pbmhSrc = &NewPatternTable[PATSYM_NOSHADE - 1];
        AIxfer.rcsSrc.pts1.x =
        AIxfer.rcsSrc.pts2.x =
        AIxfer.rcsSrc.pts1.y =
        AIxfer.rcsSrc.pts2.y = 0;

        /**************************************************************/
        /* We call the blt functions to output the opaque rectangle.  */
        /* These routines expect non-negative coordinates. Ensure that*/
        /* this is so.                                                */
        /**************************************************************/
        if ((OpaqueRect[0].x >
                     (SHORT)AIxfer.pbmhDest->Info.Width - (SHORT)1) ||
            (OpaqueRect[0].y >
                     (SHORT)AIxfer.pbmhDest->Info.Height - (SHORT)1))
        {
            ArgOptions &= ~CHS_OPAQUE;
        }

        AIxfer.rcsTrg.pts1.x = max((USHORT)0, OpaqueRect[0].x);
        AIxfer.rcsTrg.pts1.y = max((USHORT)0, OpaqueRect[0].y);

        if (OpaqueRect[1].x < 0 || OpaqueRect[1].y < 0)
        {
            ArgOptions &= ~CHS_OPAQUE;
        }

        AIxfer.rcsTrg.pts2.x = min ((USHORT)OpaqueRect[1].x,
                                    AIxfer.pbmhDest->Info.Width - (USHORT)1);
        AIxfer.rcsTrg.pts2.y = min((USHORT)OpaqueRect[1].y,
                                   AIxfer.pbmhDest->Info.Height - (USHORT)1);

        /**************************************************************/
        /* make sure that the opaque rectangle does not use a         */
        /* dithered colour since this can produce poor quality text   */
        /* when overwritten                                           */
        /**************************************************************/
        DitheredPattern = FALSE;

    } /* opaque rectangle */


    if (ArgOptions & CHS_CLIP)
    {
        /**************************************************************/
        /* clip rectangle supplied, clip the bounding rectangle       */
        /**************************************************************/
        AIWork.Bound[0].X = max( AIWork.Bound[0].X, OpaqueRect[0].x);
        AIWork.Bound[0].Y = max( AIWork.Bound[0].Y, OpaqueRect[0].y);
        AIWork.Bound[1].X = min( AIWork.Bound[1].X, OpaqueRect[1].x);
        AIWork.Bound[1].Y = min( AIWork.Bound[1].Y, OpaqueRect[1].y);

        /**************************************************************/
        /* If we didn't cache the string, then we don't know how long */
        /* it is.  So, we should quit just yet.  Defect 74931  EKF    */
        /**************************************************************/
#ifdef  _8514
        if ( ( CharTooBig == FALSE )  &&
             ( AIWork.Bound[1].X < AIWork.Bound[0].X ||
               AIWork.Bound[1].Y < AIWork.Bound[0].Y   )  )
#else
        if ( AIWork.Bound[1].X < AIWork.Bound[0].X ||
             AIWork.Bound[1].Y < AIWork.Bound[0].Y   )
#endif
        {
            /**********************************************************/
            /* Supplied clip rectangle excludes text, nothing to do   */
            /* except check for the current position being updated.   */
            /**********************************************************/
            goto UPDATE_POS;
        }

        /**************************************************************/
        /* we want to clip to the opaque rectangle so set the bounds  */
        /* for the clip rectangles to the opaque rectangle and mark   */
        /* the cache as invalid to ensure that we call back to the    */
        /* engine.                                                    */
        /**************************************************************/
        GetClipsBounds.X0 = max((USHORT)0,
                                OpaqueRect[0].x);
        GetClipsBounds.X1 = min(OpaqueRect[1].x,
                                pdc->DCIBoundingClip[1].X);
        GetClipsBounds.X1++;
        GetClipsBounds.Y0 = max((SHORT)0,
                                (SHORT)(AIxfer.pbmhDest->Info.HWHeight -
                                        OpaqueRect[1].y));
        GetClipsBounds.Y1 = min((SHORT)
         (AIxfer.pbmhDest->Info.HWHeight  - OpaqueRect[0].y + 1),
                         ((SHORT)(pdc->DCIBoundingClip[1].Y + 1)));

        /**********************************************************/
        /* we need to ensure that the bounding rectangle lies     */
        /* within the bitmap, otherwise we get problems when      */
        /* calling back to the engine. If there is in fact nothing*/
        /* to draw, switch drawing off to ensure an early exit.   */
        /* Note that we have constructed GetClipsBounds to be     */
        /* exclusive while DCIBoundingClip is inclusive, hence    */
        /* use > rather than >= in test below.                    */
        /**********************************************************/
        if (((SHORT)GetClipsBounds.X1 < 0)                         ||
            ((SHORT)GetClipsBounds.Y1 < 0)                         ||
            ((SHORT)GetClipsBounds.X0 > pdc->DCIBoundingClip[1].X) ||
            ((SHORT)GetClipsBounds.Y0 > pdc->DCIBoundingClip[1].Y))
        {
            COMMANDBITS(FunN) &= ~((USHORT)(COM_DRAW >> 16));
        }
        pdc->ClipChanged = TRUE;
    } /* clip rectangle supplied */
    else
    {
        /**********************************************************/
        /* If we have more than a cache's worth of clips then     */
        /* optimise clipping by setting clip bounds               */
        /**********************************************************/
        if (pdc->DCIEngineClips > CACHED_CLIPS)
        {
            GetClipsBounds.X0 = max((USHORT)0,
                                    AIWork.Bound[0].X);
            GetClipsBounds.X1 = min(AIWork.Bound[1].X + (USHORT)1,
                                    pdc->DCIBoundingClip[1].X);
            GetClipsBounds.Y0 = max((USHORT)0,
                                    pdc->DCIBoundingClip[1].Y -
                                            AIWork.Bound[1].Y);
            GetClipsBounds.Y1 = min(pdc->DCIBoundingClip[1].Y + (USHORT)1 -
                                           AIWork.Bound[0].Y,
                                    pdc->DCIBoundingClip[1].Y + (USHORT)1);

            /**********************************************************/
            /* same check as above                                    */
            /* Note that we have constructed GetClipsBounds to be     */
            /* exclusive while DCIBoundingClip is inclusive, hence    */
            /* use > rather than >= in test below.                    */
            /**********************************************************/
            if (((SHORT)GetClipsBounds.X1 < 0)                         ||
                ((SHORT)GetClipsBounds.Y1 < 0)                         ||
                ((SHORT)GetClipsBounds.X0 > pdc->DCIBoundingClip[1].X) ||
                ((SHORT)GetClipsBounds.Y0 > pdc->DCIBoundingClip[1].Y))
            {
                COMMANDBITS(FunN) &= ~((USHORT)(COM_DRAW >> 16));
            }
        }
        else
        {
            GetClipsBounds.X0 = pdc->DCIBoundingClip[0].X;
            GetClipsBounds.X1 = pdc->DCIBoundingClip[1].X + (USHORT)1;
            GetClipsBounds.Y0 = pdc->DCIBoundingClip[0].Y;
            GetClipsBounds.Y1 = pdc->DCIBoundingClip[1].Y + (USHORT)1;
        }
    }

    /******************************************************************/
    /* accumulate bounds if required                                  */
    /******************************************************************/
    if (FunNTest(COM_BOUND | COM_ALT_BOUND))
    {
        eddg_AddBounds((pDevRect)AIWork.Bound, FunN, COORD_AI);
    }

#ifdef DCAF                                                               //DCAF
    /******************************************************************/  //DCAF
    /* Accumulate DCAF screen bounds if required                      */  //DCAF
    /* For 8514 style caching, the font may be to large and thus we   */  //DCAF
    /* have not yet calculated the bounding rectangle.  We'll pick it */  //DCAF
    /* up later in our CHAR_TOO_BIG code.                             */  //DCAF
    /******************************************************************/  //DCAF
#ifndef _8514                                                             //DCAF
    if ( DCAFBoundsRequired(FunN) )                                       //DCAF
#else                                                                     //DCAF
    if (( DCAFBoundsRequired(FunN) ) &&                                   //DCAF
        ( CharTooBig == FALSE ))                                          //DCAF
#endif                                                                    //DCAF
    {                                                                     //DCAF
        AccumulateScreenBoundsThroughClips( (pDevRect)AIWork.Bound,       //DCAF
                                            COORD_AI );                   //DCAF
    }                                                                     //DCAF
#endif                                                                    //DCAF

    /******************************************************************/
    /* check correlation if required                                  */
    /******************************************************************/
    if ((FunNTest(COM_CORRELATE)) &&
        (pdc->DCICorrNum))
    {
        if ( eddg_CheckRectCorrelation( (pDevRect)AIWork.Bound)
                                                             == OK_CORR)
        {
            ulResult = GPI_HITS;
        }
    }

    /******************************************************************/
    /* check that we're supposed to draw the text                     */
    /******************************************************************/
    if (!(FunNTest(COM_DRAW)))
    {
        goto CHARSTRPOS_OK_EXIT;
    }

    /**************************************************************/
    /* Now it is time to set the drawing mode correctly..         */
    /**************************************************************/
    if (pdc->DCIBitmapType == BITMAP_IS_SCREEN)
    {
        SetDrawModeHard;
    }
    else
    {
        SetDrawModeSoft;
    }

    /******************************************************************/
    /* Before we start to use the hardware, if the cursor is          */
    /* colour then it may need to be excluded and further drawing on  */
    /* the mouse interrupt thread  must be disabled                   */
    /******************************************************************/
    if ( cursor_data.cursor_status & CURSOR_SOFTWARE )
    {
        eddm_ExcludeCursor(AIWork.Bound, COORD_AI);
    }

    /******************************************************************/
    /* set up the hardware colours: these are either supplied in the  */
    /* parameter list or are taken from the attribute bundle          */
    /******************************************************************/
#undef AIxfer
#define AIxfer AIxfer
    if (ArgOptions & CHS_ATTR_INFO)
    {
        ulFColor = LogToPhyIndex(ArgAttrs->lColor);
        ulBColor = LogToPhyIndex(ArgAttrs->lBackColor);

        if ( ulFColor == CLR_NOPHYINDEX ||
             ulBColor == CLR_NOPHYINDEX )
        {
            /**********************************************************/
            /* Foreground or background color is invalid              */
            /**********************************************************/
            ErrCode = PMERR_INV_COLOR_INDEX;
            goto CHARSTRPOS_LOGERR_EXIT;
        }

        #ifndef   BPP24
        AIxfer.usFColor = (USHORT)ulFColor;
        AIxfer.usBColor = (USHORT)ulBColor;
        #else
        AIxfer.usFColor = ulFColor;
        AIxfer.usBColor = ulBColor;
        #endif
    }
    else
    {
        /**************************************************************/
        /* Use the colors and mixes from the current text attributes. */
        /**************************************************************/
        AIxfer.usFColor = ulFColor = pdc->DCICharColatts.ForeColor;
        AIxfer.usBColor = ulBColor = pdc->DCICharColatts.BackColor;
    }

    /******************************************************************/
    /* Do this now as GetClipsBounds will be affected by any call to  */
    /* do an opaque rectangle                                         */
    /******************************************************************/
    SaveOpaqueRect = GetClipsBounds;

    /******************************************************************/
    /* if there is an opaque rectangle then output it now using the   */
    /* low-level blt function                                         */
    /******************************************************************/
    if (ArgOptions & CHS_OPAQUE)
    {
        /**************************************************************/
        /* Set up the colours                                         */
        /**************************************************************/
        #ifndef   _8514
        ShadowXGARegs.FgCol = AIxfer.usFColor;
        ShadowXGARegs.BgCol = AIxfer.usBColor;
        #else
        Shadow8514Regs.Color_1 = AIxfer.usFColor;
        Shadow8514Regs.Color_0 = AIxfer.usBColor;
        #endif

#undef AIxfer
#define AIxfer (*(PBITBLTPB)&AIxfer)
        /**************************************************************/
        /* initialise the pixel operation to be used                  */
        /* -  background source: background colour                    */
        /* -  foreground source: foreground colour                    */
        /* -  step: PxBlt                                             */
        /* -  source pixel map: don't care                            */
        /* -  destination pixel map: Map A                            */
        /* -  pattern pixel map: Map B                                */
        /* -  mask pixel map: boundary disabled                       */
        /* -  drawing mode: don't care                                */
        /* -  direction octant: left to right, top to bottom          */
        /**************************************************************/
        PixelOp = BACK_SRC_BACK_COL |
                  FORE_SRC_FORE_COL |
                  STEP_PXBLT |
                  SRC_PIX_MAP_DONTCARE |
                  DST_PIX_MAP_A |
                  PAT_PIX_MAP_B |
                  MASK_PIX_MAP_OFF |
                  DRAW_MODE_DONTCARE |
                  DIR_OCTANT_LRTB;

        /**************************************************************/
        /* the opaque rectangle mix is always overpaint               */
        /* Set foreground mix as well to make things easier for the   */
        /* MESS (no old values left lying around)                     */
        /**************************************************************/
        #ifndef   _8514
        ShadowXGARegs.BgMix = HWMIX_SOURCE;
        ShadowXGARegs.FgMix = HWMIX_SOURCE;
        ShadowXGARegs.ColCompCond = COLCOMP_ALWAYS;
        #else
        Shadow8514Regs.Function_0.Mix = FUNC_S;
        Shadow8514Regs.Function_1.Mix = FUNC_S;
        Shadow8514Regs.Function_0.SrcSelect = FUNC_2OP_COL0;
        Shadow8514Regs.Function_1.SrcSelect = FUNC_2OP_COL1;
        Shadow8514Regs.Mode.UnderPaint = MD_UP_FALSE;
        #endif

        /**************************************************************/
        /* Transfer the shadow register values we have set to the real*/
        /* hw registers.                                              */
        /**************************************************************/
        TransferShadowRegisters(TSR_COLOUR_MIX);

        eddb_DrawThroughClips((*pDrawFunctions)[index_PatDestBlt],
                               (USHORT *)&AIxfer.cDestClipRects);

    } /* opaque rectangle required */

    /******************************************************************/
    /* We can exit now if the string has zero length (which happens   */
    /* by design if we are handling simulations for underscore or     */
    /* strikeout - these merely use the opaque rectangle )            */
    /******************************************************************/
    if (!ArgCharnum)
    {
        goto CHARSTRPOS_OK_EXIT;
    }

#ifdef BLTONE
    /******************************************************************/
    /* Check if we are going to output the character with a blt, in   */
    /* other words a cached vector font.                              */
    /******************************************************************/
    if (BltOneChar == TRUE)
    {
        /**************************************************************/
        /* Calculate the size of the bitmap containing the character. */
        /* Note that each row is padded to a byte boundary            */
        /**************************************************************/
        CharBytesPerRow = (USHORT)(((ULONG)BSpace + 7) / 8);
        BitmapSizeBytes = CharBytesPerRow * CharHeight;

        /**************************************************************/
        /* Use the PHUNK as buffer for the character definition       */
        /**************************************************************/
        WaitForRealHW;
        pCharBitmap = (PBYTE)pPhunkVirt;

        pCharBuffer = pCharBitmap;

        /**************************************************************/
        /* Copy bytes from character definition, adjusting the order  */
        /* into the correct order for bitmap format                   */
        /**************************************************************/
        for (i = 0; i < CharHeight; i++)
        {
            source_index = i;
            for (j = CharBytesPerRow; j--; )
            {
                *pCharBuffer++ = pCharDefn[source_index];
                source_index += CharHeight;
            }
        }

        /**************************************************************/
        /* Set up the bitmap header for the cached character bitmap   */
        /* It is used by and the assembler routine used to output the */
        /* charcter - eddh_srcdestblt.                                */
        /**************************************************************/
        VectCacheBMHeader.Bitmap   = (lpBitmap)pCharBitmap;
        VectCacheBMHeader.BMSize   = BitmapSizeBytes;
        VectCacheBMHeader.Info.Width    = CharBytesPerRow * (USHORT)8;
        VectCacheBMHeader.Info.HWWidth  =
                                   VectCacheBMHeader.Info.Width - (USHORT)1;
        VectCacheBMHeader.Info.Height   = CharHeight;
        VectCacheBMHeader.Info.HWHeight = CharHeight - (USHORT)1;
        VectCacheBMHeader.Info.HWFormat = ONE_BIT_PER_PEL;
        VectCacheBMHeader.Info.BitCount = 1;
        VectCacheBMHeader.BMPhys = pPhunkPhys;

        /**************************************************************/
        /* initialise the pixel operation to be used                  */
        /* -  background source: background colour                    */
        /* -  foreground source: foreground colour                    */
        /* -  step: PxBlt                                             */
        /* -  source pixel map: don't care                            */
        /* -  destination pixel map: Map A                            */
        /* -  pattern pixel map: Map B (non-expanding)                */
        /*                     : Map C (expanding-see eddh_srcdestblt */
        /* -  mask pixel map: boundary disabled                       */
        /* -  drawing mode: don't care                                */
        /* -  direction octant: left to right, top to bottom          */
        /**************************************************************/
        if (pdc->DCISelListEntry->Info.BitCount == 1)
        {
            PixelOp = BACK_SRC_BACK_COL |
                      FORE_SRC_FORE_COL |
                      STEP_PXBLT |
                      SRC_PIX_MAP_DONTCARE |
                      DST_PIX_MAP_A |
                      PAT_PIX_MAP_B |
                      MASK_PIX_MAP_OFF |
                      DRAW_MODE_DONTCARE |
                      DIR_OCTANT_LRTB;
        }
        else
        {
            PixelOp = BACK_SRC_BACK_COL |
                      FORE_SRC_FORE_COL |
                      STEP_PXBLT |
                      SRC_PIX_MAP_DONTCARE |
                      DST_PIX_MAP_A |
                      PAT_PIX_MAP_C |
                      MASK_PIX_MAP_OFF |
                      DRAW_MODE_DONTCARE |
                      DIR_OCTANT_LRTB;
        }

        /**************************************************************/
        /* Set the colours and mix                                    */
        /**************************************************************/
        #ifndef   _8514
        ShadowXGARegs.FgCol = (USHORT)ulFColor;
        ShadowXGARegs.BgCol = (USHORT)ulBColor;
        ShadowXGARegs.FgMix = WinToXway[pdc->DCICurTxtAts.cbnd.usMixMode];
        ShadowXGARegs.BgMix = WinToXway[pdc->DCICurTxtAts.cbnd.usBackMixMode];
        ShadowXGARegs.ColCompCond = COLCOMP_ALWAYS;
        #else
         #ifndef   BPP24
        Shadow8514Regs.Color_1 = (USHORT)ulFColor;
        Shadow8514Regs.Color_0 = (USHORT)ulBColor;
         #else
        Shadow8514Regs.Color_1 = ulFColor;
        Shadow8514Regs.Color_0 = ulBColor;
         #endif
        Shadow8514Regs.Function_1.Mix = WinToXway[pdc->DCICurTxtAts.cbnd.usMixMode];
        Shadow8514Regs.Function_0.Mix = WinToXway[pdc->DCICurTxtAts.cbnd.usBackMixMode];
        Shadow8514Regs.Function_1.SrcSelect = FUNC_2OP_COL1;
        Shadow8514Regs.Function_0.SrcSelect = FUNC_2OP_COL0;
        Shadow8514Regs.Mode.UnderPaint = MD_UP_FALSE;
        #endif

        /**************************************************************/
        /* Transfer the shadow register values we have set to the     */
        /* real hw registers.                                         */
        /**************************************************************/
        TransferShadowRegisters(TSR_COLOUR_MIX);

        /**************************************************************/
        /* Set up the parameters used by eddh_srcdestblt. Some have   */
        /* already been set up                                        */
        /**************************************************************/
        AIxfer.pbmhSrc = &VectCacheBMHeader;
        AIxfer.rcsSrc.pts1.x = 0;
        AIxfer.rcsSrc.pts2.x = BSpace - (USHORT)1;
        AIxfer.rcsSrc.pts1.y = 0;
        AIxfer.rcsSrc.pts2.y = CharHeight - (USHORT)1;

        AIxfer.rcsTrg.pts1.x = StartPosition.x + ASpace;
        AIxfer.rcsTrg.pts1.y = StartPosition.y;
        AIxfer.rcsTrg.pts2.x = AIxfer.rcsTrg.pts1.x + BSpace - (USHORT)1;
        AIxfer.rcsTrg.pts2.y = StartPosition.y + CharHeight - (USHORT)1;

        for ( i=0; i<ArgCharnum; i++ )
        {
            /**********************************************************/
            /* Save the TrgRect and SrcRect before we fix it up.      */
            /**********************************************************/
            SaveTrgRect = AIxfer.rcsTrg;
            SaveSrcRect = AIxfer.rcsSrc;

            /**************************************************************/
            /* Make the coordinates non negative so we can pass them on   */
            /* to the blt routine                                         */
            /**************************************************************/
            if (AIxfer.rcsTrg.pts2.x < 0 || AIxfer.rcsTrg.pts2.y < 0)
            {
                /**********************************************************/
                /* Update the current position, if necessary and return   */
                /**********************************************************/
                goto NEXT_CHARACTER;
            }
            if (AIxfer.rcsTrg.pts1.x < 0)
            {
                AIxfer.rcsSrc.pts1.x -= AIxfer.rcsTrg.pts1.x;
                AIxfer.rcsTrg.pts1.x = 0;
            }
            if (AIxfer.rcsTrg.pts1.y < 0)
            {
                AIxfer.rcsSrc.pts1.y -= AIxfer.rcsTrg.pts1.y;
                AIxfer.rcsTrg.pts1.y = 0;
            }

            /***********************************************************/
            /* Clip the top right edge of the target to the bitmap     */
            /* size - the Blt routines assume that clipping has been   */
            /* done.                                                   */
            /* If the character is totally clipped then skip to the    */
            /* next one.                                              */
            /***********************************************************/
            if ( AIxfer.rcsTrg.pts2.x > (SHORT)AIxfer.pbmhDest->Info.HWWidth)
            {
                AIxfer.rcsSrc.pts2.x = AIxfer.rcsSrc.pts2.x -
                (AIxfer.rcsTrg.pts2.x - AIxfer.pbmhDest->Info.HWWidth);

                if ( AIxfer.rcsSrc.pts2.x < AIxfer.rcsSrc.pts1.x )
                {
                   goto NEXT_CHARACTER;
                }
                AIxfer.rcsTrg.pts2.x = AIxfer.pbmhDest->Info.HWWidth;
            }

            if ( AIxfer.rcsTrg.pts2.y > (SHORT)AIxfer.pbmhDest->Info.HWHeight )
            {
                AIxfer.rcsSrc.pts2.y = AIxfer.rcsSrc.pts2.y -
                (AIxfer.rcsTrg.pts2.y - AIxfer.pbmhDest->Info.HWHeight);

                if ( AIxfer.rcsSrc.pts2.y < AIxfer.rcsSrc.pts1.y )
                {
                   goto NEXT_CHARACTER;
                }
                AIxfer.rcsTrg.pts2.y = AIxfer.pbmhDest->Info.HWHeight;
            }

            /**********************************************************/
            /* Draw the text                                          */
            /**********************************************************/
            eddb_DrawThroughClips((*pDrawFunctions)[index_SrcDestBlt],
                                   (USHORT *)&AIxfer.cDestClipRects);

            /**********************************************************/
            /* Cached clips are invalid as they intersect opaque rect */
            /**********************************************************/
            if (ArgOptions & CHS_CLIP)
            {
                pdc->ClipChanged = TRUE;
            }

NEXT_CHARACTER:
            /**********************************************************/
            /* Get back the original TrgRect and SrcRect              */
            /**********************************************************/
            AIxfer.rcsTrg = SaveTrgRect;
            AIxfer.rcsSrc = SaveSrcRect;

            /**********************************************************/
            /* Update it to the next position                         */
            /**********************************************************/
            if (pVector)
            {
                AIxfer.rcsTrg.pts1.x += pVector[i];
                AIxfer.rcsTrg.pts2.x += pVector[i];
            }
            else
            {
                AIxfer.rcsTrg.pts1.x +=
                       CharWidth + pdc->DCICurTxtAts.cdef.charSpacing;
                AIxfer.rcsTrg.pts2.x +=
                       CharWidth + pdc->DCICurTxtAts.cdef.charSpacing;
            }
        }

        /**********************************************************/
        /* We must now unPhunk the buffer if we PHUNKed it earlier*/
        /**********************************************************/
        if (VectCacheBMHeader.BMPhys == pPhunkPhys)
        {
            VectCacheBMHeader.BMPhys = FNULL;
        }

        /**************************************************************/
        /* Update the current position, if necessary and return       */
        /**************************************************************/
        goto UPDATE_POS;
    }
#endif /* BLTONE */

#ifdef _8514
    /******************************************************************/
    /* Check if we are going to output the character with a blt,      */
    /* instead of caching the font.                                   */
    /******************************************************************/

/* CHAR_TOO_BIG */

    if (CharTooBig)
    {
        /**************************************************************/
        /* initialise the pixel operation to be used                  */
        /* -  background source: background colour                    */
        /* -  foreground source: foreground colour                    */
        /* -  step: PxBlt                                             */
        /* -  source pixel map: don't care                            */
        /* -  destination pixel map: Map A                            */
        /* -  pattern pixel map: Map B (non-expanding)                */
        /*                     : Map C (expanding-see eddh_srcdestblt */
        /* -  mask pixel map: boundary disabled                       */
        /* -  drawing mode: don't care                                */
        /* -  direction octant: left to right, top to bottom          */
        /**************************************************************/
        if (pdc->DCISelListEntry->Info.BitCount == 1)
        {
            PixelOp = BACK_SRC_BACK_COL |
                      FORE_SRC_FORE_COL |
                      STEP_PXBLT |
                      SRC_PIX_MAP_DONTCARE |
                      DST_PIX_MAP_A |
                      PAT_PIX_MAP_B |
                      MASK_PIX_MAP_OFF |
                      DRAW_MODE_DONTCARE |
                      DIR_OCTANT_LRTB;
        }
        else
        {
            PixelOp = BACK_SRC_BACK_COL |
                      FORE_SRC_FORE_COL |
                      STEP_PXBLT |
                      SRC_PIX_MAP_DONTCARE |
                      DST_PIX_MAP_A |
                      PAT_PIX_MAP_C |
                      MASK_PIX_MAP_OFF |
                      DRAW_MODE_DONTCARE |
                      DIR_OCTANT_LRTB;
        }

        /**************************************************************/
        /* Set the colours and mix                                    */
        /**************************************************************/
        #ifndef   BPP24
        Shadow8514Regs.Color_1 = (USHORT)ulFColor;
        Shadow8514Regs.Color_0 = (USHORT)ulBColor;
        #else
        Shadow8514Regs.Color_1 = ulFColor;
        Shadow8514Regs.Color_0 = ulBColor;
        #endif
        Shadow8514Regs.Function_1.Mix = WinToXway[pdc->DCICurTxtAts.cbnd.usMixMode];
        Shadow8514Regs.Function_0.Mix = WinToXway[pdc->DCICurTxtAts.cbnd.usBackMixMode];
        Shadow8514Regs.Function_1.SrcSelect = FUNC_2OP_COL1;
        Shadow8514Regs.Function_0.SrcSelect = FUNC_2OP_COL0;
        Shadow8514Regs.Mode.UnderPaint = MD_UP_FALSE;

        /**************************************************************/
        /* Transfer the shadow register values we have set to the     */
        /* real hw registers.                                         */
        /**************************************************************/
        TransferShadowRegisters(TSR_COLOUR_MIX);

        AIWork.Bound[0].X = StartPosition.x;
        AIWork.Bound[1].X = StartPosition.x;

        /**************************************************************/
        /* Its not really an offset it seems to be inclusive          */
        /**************************************************************/
        AIWork.Bound[0].Y = StartPosition.y -
                       (pFocaFont->fdDefinitions.pCellBaseOffset - (USHORT)1);

        /**************************************************************/
        /* Set up pointer to the per character definition for the     */
        /* character                                                  */
        /**************************************************************/
        PerCharDefs = (PUSHORT)((PBYTE)&(pFocaFont->fmMetrics) +
                                         pFocaFont->fmMetrics.ulSize +
                                         sizeof(FONTDEFINITIONHEADER));

        if (pFocaFont->fdDefinitions.fsChardef == FONTDEFCHAR3)
            CharDefSize = 5;     /* 10 bytes = 5 Words */
        else
            CharDefSize = 3;     /*  6 bytes = 3 Words */

        StartPosition2.x = StartPosition.x;
        StartPosition2.y = StartPosition.y;


        for ( i=0; i<ArgCharnum; i++ )
        {
            /**************************************************************/
            /* Set up the character width and height if they are in header*/
            /**************************************************************/
            if (pFocaFont->fdDefinitions.fsFontdef & 0x02)
            {
                CharHeight = pFocaFont->fdDefinitions.yCellHeight;
            }
            if (pFocaFont->fdDefinitions.fsFontdef & 0x01)
            {
                BSpace = pFocaFont->fdDefinitions.xCellWidth;
            }

            /**************************************************************/
            /* Now pick up any defined values in the per character defs   */
            /* Whist we are looking at this we may can also pick up the   */
            /* offset to the character definition                         */
            /**************************************************************/
#ifndef DBCS                                                
            WordsUsed = (ArgCodePoints[i] - 1) * CharDefSize;
#else 
            WordsUsed = (((PUSHORT)ArgCodePoints)[i] -
                        pFocaFont->fmMetrics.usFirstChar) * CharDefSize;
#endif 

            if (pFocaFont->fdDefinitions.fsChardef & 0x80)
            {
                pCharDefn = (PBYTE)pFocaFont + PerCharDefs[WordsUsed];
                WordsUsed += 2;
            }

            /**************************************************************/
            /* Character Width                                            */
            /**************************************************************/
            if (pFocaFont->fdDefinitions.fsChardef & 0x01)
            {
                BSpace = PerCharDefs[WordsUsed++];
            }

            /**************************************************************/
            /* Character Height                                           */
            /**************************************************************/
            if (pFocaFont->fdDefinitions.fsChardef & 0x02)
            {
                CharHeight = PerCharDefs[WordsUsed++];
            }

            /**************************************************************/
            /* Character Increment                                        */
            /**************************************************************/
            if (pFocaFont->fdDefinitions.fsChardef & 0x04)
            {
                WordsUsed++;
            }

            /**************************************************************/
            /* Pick up A,B and C space from the per character definitions */
            /* if defined there                                           */
            /**************************************************************/
            if (pFocaFont->fdDefinitions.fsChardef == FONTDEFCHAR3)
            {
                ASpace = PerCharDefs[WordsUsed++];
                BSpace = PerCharDefs[WordsUsed++];
                CSpace = PerCharDefs[WordsUsed++];
            }
            else
            {
                ASpace = 0;
                CSpace = 0;
            }

            /**************************************************************/
            /* We can now calculate the bounds. Bounds are inclusive.     */
            /**************************************************************/
            CharWidth = ASpace + BSpace + CSpace;

            /**************************************************************/
            /* Calculate the x bound allowing for multiple instances of   */
            /* the single character                                       */
            /**************************************************************/
            if (pVector)
            {
                AIWork.Bound[1].X = AIWork.Bound[0].X;
                for ( j = 0; j<ArgCharnum; j++ )
                {
                    AIWork.Bound[1].X += pVector[j];
                }
            }
            else
            {
                AIWork.Bound[1].X += (CharWidth + pdc->DCICurTxtAts.cdef.charSpacing);
            }

            /**************************************************************/
            /* Calculate the size of the bitmap containing the character. */
            /* Note that each row is padded to a byte boundary            */
            /**************************************************************/
            CharBytesPerRow = (USHORT)(((ULONG)BSpace + 7) / 8);
            BitmapSizeBytes = CharBytesPerRow * CharHeight;

            /**************************************************************/
            /* Use the PHUNK as buffer for the character definition       */
            /**************************************************************/
            pCharBitmap = (PBYTE)pPhunkVirt;
            pCharBuffer = pCharBitmap;

            /**************************************************************/
            /* Copy bytes from character definition, adjusting the order  */
            /* into the correct order for bitmap format                   */
            /**************************************************************/
            for (k = 0; k < CharHeight; k++)
            {
                source_index = k;
                for (j = CharBytesPerRow; j--; )
                {
                    *pCharBuffer++ = pCharDefn[source_index];
                    source_index += CharHeight;
                }
            }

            /**************************************************************/
            /* Set up the bitmap header for the cached character bitmap   */
            /* It is used by and the assembler routine used to output the */
            /* charcter - eddh_srcdestblt.                                */
            /**************************************************************/
            VectCacheBMHeader.Bitmap   = (lpBitmap)pCharBitmap;
            VectCacheBMHeader.BMSize   = BitmapSizeBytes;
            VectCacheBMHeader.Info.Width    = CharBytesPerRow * (USHORT)8;
            VectCacheBMHeader.Info.HWWidth  =
                                       VectCacheBMHeader.Info.Width - (USHORT)1;
            VectCacheBMHeader.Info.Height   = CharHeight;
            VectCacheBMHeader.Info.HWHeight = CharHeight - (USHORT)1;
            VectCacheBMHeader.Info.HWFormat = ONE_BIT_PER_PEL;
            VectCacheBMHeader.Info.BitCount = 1;
            VectCacheBMHeader.BMPhys = pPhunkPhys;

            /**************************************************************/
            /* Set up the parameters used by eddh_srcdestblt. Some have   */
            /* already been set up                                        */
            /**************************************************************/
            AIxfer.pbmhSrc = &VectCacheBMHeader;
            AIxfer.rcsSrc.pts1.x = 0;
            AIxfer.rcsSrc.pts2.x = BSpace - (USHORT)1;
            AIxfer.rcsSrc.pts1.y = 0;
            AIxfer.rcsSrc.pts2.y = CharHeight - (USHORT)1;

            AIxfer.rcsTrg.pts1.x = StartPosition2.x + ASpace;
            AIxfer.rcsTrg.pts1.y = StartPosition2.y - (CharHeight -(USHORT)1);
            AIxfer.rcsTrg.pts2.x = AIxfer.rcsTrg.pts1.x + BSpace - (USHORT)1;
            AIxfer.rcsTrg.pts2.y = StartPosition2.y;

            /**********************************************************/
            /* Save the TrgRect and SrcRect before we fix it up.      */
            /**********************************************************/
            SaveTrgRect = AIxfer.rcsTrg;
            SaveSrcRect = AIxfer.rcsSrc;

            /**************************************************************/
            /* Make the coordinates non negative so we can pass them on   */
            /* to the blt routine                                         */
            /**************************************************************/
            if (AIxfer.rcsTrg.pts2.x < 0 || AIxfer.rcsTrg.pts2.y < 0)
            {
                /**********************************************************/
                /* Update the current position, if necessary and return   */
                /**********************************************************/
                goto NEXT_CHARACTER2;
            }

            // add code to check that the destination values are not
            // negative, this causes SrcDestBlt to think there is no
            // intersection.  just borrow code from above to shift the
            // source and target.  also add the code to check clipping
            if (AIxfer.rcsTrg.pts1.x < 0)
            {
                AIxfer.rcsSrc.pts1.x -= AIxfer.rcsTrg.pts1.x;
                AIxfer.rcsTrg.pts1.x = 0;
            }
            if (AIxfer.rcsTrg.pts1.y < 0)
            {
                AIxfer.rcsSrc.pts1.y -= AIxfer.rcsTrg.pts1.y;
                AIxfer.rcsTrg.pts1.y = 0;
            }
            if ( AIxfer.rcsTrg.pts2.x > (SHORT)AIxfer.pbmhDest->Info.HWWidth)
            {
                AIxfer.rcsSrc.pts2.x = AIxfer.rcsSrc.pts2.x -
                (AIxfer.rcsTrg.pts2.x - AIxfer.pbmhDest->Info.HWWidth);

                if ( AIxfer.rcsSrc.pts2.x < AIxfer.rcsSrc.pts1.x )
                {
                   goto NEXT_CHARACTER2;
                }
                AIxfer.rcsTrg.pts2.x = AIxfer.pbmhDest->Info.HWWidth;
            }
            if ( AIxfer.rcsTrg.pts2.y > (SHORT)AIxfer.pbmhDest->Info.HWHeight )
            {
                AIxfer.rcsSrc.pts2.y = AIxfer.rcsSrc.pts2.y -
                (AIxfer.rcsTrg.pts2.y - AIxfer.pbmhDest->Info.HWHeight);

                if ( AIxfer.rcsSrc.pts2.y < AIxfer.rcsSrc.pts1.y )
                {
                   goto NEXT_CHARACTER2;
                }
                AIxfer.rcsTrg.pts2.y = AIxfer.pbmhDest->Info.HWHeight;
            }

            /**********************************************************/
            /* Draw the text                                          */
            /**********************************************************/
            eddb_DrawThroughClips((*pDrawFunctions)[index_SrcDestBlt],
                                   (USHORT *)&AIxfer.cDestClipRects);

            /**********************************************************/
            /* Cached clips are invalid as they intersect opaque rect */
            /**********************************************************/
            if (ArgOptions & CHS_CLIP)
            {
                pdc->ClipChanged = TRUE;
            }

NEXT_CHARACTER2:
            /**********************************************************/
            /* Get back the original TrgRect and SrcRect              */
            /**********************************************************/
            AIxfer.rcsTrg = SaveTrgRect;
            AIxfer.rcsSrc = SaveSrcRect;

            /**********************************************************/
            /* Update it to the next position                         */
            /**********************************************************/
            if (pVector)
            {
                StartPosition2.x += pVector[i];
            }
            else
            {
                StartPosition2.x +=
                       CharWidth + pdc->DCICurTxtAts.cdef.charSpacing;
            }
        }
        AIWork.Bound[1].Y = AIWork.Bound[0].Y + CharHeight - (USHORT)1;

        /**********************************************************/
        /* We must now unPhunk the buffer if we PHUNKed it earlier*/
        /**********************************************************/
        if (VectCacheBMHeader.BMPhys == pPhunkPhys)
        {
            VectCacheBMHeader.BMPhys = FNULL;
        }

        /**************************************************************/
        /* Update the current position, if necessary and return       */
        /**************************************************************/
        goto UPDATE_POS;
    }
#endif /* _8514 */

    /******************************************************************/
    /* restore the type of AIxfer for text output                     */
    /******************************************************************/
#undef AIxfer
#define AIxfer AIxfer

    /******************************************************************/
    /* Set up the pointer to the font.                                */
    /******************************************************************/
    AIxfer.pIntFont = (PINTFONT)pFocaFont;

    /******************************************************************/
    /* Store a pointer to the codepoints and the position of the      */
    /* first character in the parameter block                         */
    /******************************************************************/
    AIxfer.pCodePoints = ArgCodePoints;
    AIxfer.ptsStartCoord = StartPosition;
    AIxfer.usTheCharSpacing = (USHORT)pdc->DCICurTxtAts.cdef.charSpacing;

#ifdef DBCS                                                 
    // here we have array of cache index which gives us a little more speed!
    AIxfer.psCacheIndex = asCIndex;
#endif 
    /******************************************************************/
    /* Set up the mixes from the current text attribute bundle        */
    /******************************************************************/
    #ifndef   _8514
    ShadowXGARegs.FgMix = WinToXway[pdc->DCICurTxtAts.cbnd.usMixMode];
    ShadowXGARegs.BgMix = WinToXway[pdc->DCICurTxtAts.cbnd.usBackMixMode];
    ShadowXGARegs.ColCompCond = COLCOMP_ALWAYS;
    #else
    Shadow8514Regs.Function_1.Mix = WinToXway[pdc->DCICurTxtAts.cbnd.usMixMode];
    Shadow8514Regs.Function_0.Mix = WinToXway[pdc->DCICurTxtAts.cbnd.usBackMixMode];
    Shadow8514Regs.Function_1.SrcSelect = FUNC_2OP_COL1;
    Shadow8514Regs.Function_0.SrcSelect = FUNC_2OP_COL0;
    Shadow8514Regs.Mode.UnderPaint = MD_UP_FALSE;
    #endif


    /******************************************************************/
    /* if we didn't set them up for an opaque rectangle               */
    /******************************************************************/
    if (!(ArgOptions & CHS_OPAQUE))
    {
        #ifndef   _8514
        ShadowXGARegs.FgCol = AIxfer.usFColor;
        ShadowXGARegs.BgCol = AIxfer.usBColor;
        #else
        Shadow8514Regs.Color_1 = AIxfer.usFColor;
        Shadow8514Regs.Color_0 = AIxfer.usBColor;
        #endif
    }

    /**************************************************************/
    /* Transfer the shadow register values we have set to the real*/
    /* hw registers.                                              */
    /**************************************************************/
    TransferShadowRegisters(TSR_COLOUR_MIX);

    /******************************************************************/
    /* Can the whole string be output in one go? Not if the font      */
    /* has a position vector.                                         */
    /******************************************************************/
    if ( pVector )
    {
        /**************************************************************/
        /* set parameter block to indicate just one character to be   */
        /* output                                                     */
        /**************************************************************/
        AIxfer.cChars = 1;

        /**************************************************************/
        /* loop round outputting each character in the string one at  */
        /* a time. The hardware interface is called directly for      */
        /* small bitmaps                                              */
        /**************************************************************/
        for (i = 0; i < ArgCharnum; i++)                      /* @001 */
        {
            /*********************************************/   /* @001 */
            /* Set the character width as indicated in   */   /* @001 */
            /* the user supplied increment vector.       */   /* @001 */
            /*********************************************/   /* @001 */
            AIxfer.bCharWidth = (BYTE) ArgPosVector[i];       /* @001 */

            /******************************************************/
            /* call the hardware interface to output the          */
            /* character                                          */
            /******************************************************/
            eddb_DrawThroughClips((*pDrawFunctions)[index_drawtext],
                               (USHORT *)&AIxfer.cDestClipRects);

            /******************************************************/
            /* Cached clips invalid as they intersect opaque rect */
            /******************************************************/
            if (ArgOptions & CHS_CLIP)
            {
                pdc->ClipChanged = TRUE;
            }

            /******************************************************/
            /* now use the position vector to update the position */
            /* at which the next character will be output. The    */
            /* i'th entry in the position vector array determines */
            /* the distance of the (i+1)'th character from the    */
            /* i'th. The last entry in the array may be needed to */
            /* update the current position so the character       */
            /* position is updated even after the last character  */
            /* has been output                                    */
            /******************************************************/
            AIxfer.ptsStartCoord.x += *pVector++;

            /******************************************************/
            /* set the parameter block pointer to point to the    */
            /* next character to output                           */
            /******************************************************/
#ifdef DBCS                                                 
            /*--------------------------------------------------------*/
            /* actually we do not use this pointer, but...            */
            /*--------------------------------------------------------*/
            if (!(NLSPARSEFLAG & NLSCA_UNIFIED_DBCS))
            {
#endif 
            AIxfer.pCodePoints++;
#ifdef DBCS                                                 
            }
            else
            {
                ((PUSHORT)AIxfer.pCodePoints) ++;
            }

            // this is used to draw text
            AIxfer.psCacheIndex ++;
#endif 

        } /* for each character in the string */

#ifndef DBCS                                                
        if ( ArgCharnum > MAX_STR_LEN )
        {
          FreeMemory(pVc);
        }
#endif 
    } /* position vector */

    else /* no position vector */
    {
        /**************************************************************/
        /* output all characters with a single call to the low-level  */
        /* interface per subbitmap                                    */
        /**************************************************************/
        AIxfer.cChars = (USHORT)ArgCharnum;

        /**************************************************************/
        /* small bitmaps require just one call to the hardware i/f    */
        /**************************************************************/

        eddb_DrawThroughClips((*pDrawFunctions)[index_drawtext],
                               (USHORT *)&AIxfer.cDestClipRects);

        /**********************************************************/
        /* Cached clips are invalid as they intersect opaque rect */
        /**********************************************************/
        if (ArgOptions & CHS_CLIP)
        {
            pdc->ClipChanged = TRUE;
        }

    } /* don't process each character individually */



UPDATE_POS:
    /******************************************************************/
    /* update the current position, if required                       */
    /******************************************************************/
    if ( !(ArgOptions & CHS_LEAVEPOS) )
    {
        /**************************************************************/
        /* Store the AI current position                              */
        /**************************************************************/
        pdc->DCICurrPosAI.X = NewCurrentPosn.x;
        pdc->DCICurrPosAI.Y = NewCurrentPosn.y;

        /**************************************************************/
        /* Store the world current position                           */
        /**************************************************************/
        if (OK != eddg_Convert (
                          (PULONG)&pdc->DCICurrPosAI,
                          (PULONG)&pdc->DCICurrPosWorld,
                          COORD_AI,
                          COORD_WORLD,
                          (USHORT)1,
                          COM_TRANSFORM) )

        {
            /******************************************************/
            /* An error will have been logged, so we just have to */
            /* exit immediately.                                  */
            /******************************************************/
            goto CHARSTRPOS_ERR_EXIT;
        }

    } /* if current position required to be updated */

#ifdef DCAF                                                               //DCAF
    /******************************************************************/  //DCAF
    /* Accumulate DCAF screen bounds if required                      */  //DCAF
    /* For 8514 style caching, the font may be to large and thus we   */  //DCAF
    /* have not yet calculated the bounding rectangle.  We'll pick it */  //DCAF
    /* up later in our CHAR_TOO_BIG code.                             */  //DCAF
    /******************************************************************/  //DCAF
    if ( DCAFBoundsRequired(FunN) )                                       //DCAF
    {                                                                     //DCAF
        AccumulateScreenBoundsThroughClips( (pDevRect)AIWork.Bound,       //DCAF
                                            COORD_AI );                   //DCAF
    }                                                                     //DCAF
#endif                                                                    //DCAF

/**********************************************************************/
/* Defect - 55354                                                     */
/*                                                                    */
/* If AIxfer.pbmhDest was not previously set up then this bit of      */
/* code would cause problems, especially if AIxfer.pbmhDest pointed   */
/* to a bitmap that had already been evicted.                         */
/*                                                                    */
/* The logic below has been moved up to above the CHARSTRPOS_OK_EXIT  */
/* label because there are some spots above that jump to this label   */
/* without a need to set up AIxfer.pbmhDest.                          */
/*                                                                    */
/* Joe Celi (10/5/92)                                                 */
/**********************************************************************/

#ifdef VRAMPTR
    /******************************************************************/
    /* If the target bitmap is cached then evict it since it has      */
    /* been drawn to.                                                 */
    /******************************************************************/
    if ( BITMAP_IS_CACHED(AIxfer.pbmhDest) )
    {
      evict_cached_bitmap(AIxfer.pbmhDest->bm_cache_slot);
    }
#endif /* VRAMPTR */

CHARSTRPOS_OK_EXIT:
    /******************************************************************/
    /* If the cursor is colour then cursor drawing will have been     */
    /* disabled while we used the hardware. If this is the            */
    /* case then the cursor EXCLUDED status will be set and we should */
    /* set it up to do a redraw on the next MoveCursor (CheckCursor)  */
    /******************************************************************/
    reenable_cursor();


    ExitDriver(pdcArg, FunN, EDF_STANDARD);

    return ulResult;


CHARSTRPOS_LOGERR_EXIT:
    LogError(ErrCode);

CHARSTRPOS_ERR_EXIT:
    /******************************************************************/
    /* If the cursor is colour then cursor drawing will have been     */
    /* disabled while we used the hardware. If this is the            */
    /* case then the cursor EXCLUDED status will be set and we should */
    /* set it up to do a redraw on the next MoveCursor (CheckCursor)  */
    /******************************************************************/
    reenable_cursor();

    /**************************************************************/
    /* Defect 61435 - Don't free memory that you don't have!      */
    /**************************************************************/
    if ( (ArgCharnum > MAX_STR_LEN) && (pVc != NULL) )
    {
      FreeMemory(pVc);
    }

    ExitDriver(pdcArg, FunN, EDF_STANDARD);
    return(ERROR_ZERO);

} /* eddt_CharStringPos */


#if (defined(DBCS) && defined(_8514))                       

LONG GetStringWidth (PFOCAFONT pFocaFont, ULONG nChar, PUSHORT pStr)
{
    LONG    width;
    PUSHORT pDefs;
    ULONG   i;
    ULONG   WordsUsed;
    USHORT  CharDefSize;
    USHORT  ASpace;
    USHORT  BSpace;
    USHORT  CSpace;


    pDefs = (PUSHORT)((PBYTE)&(pFocaFont->fmMetrics) +
                                pFocaFont->fmMetrics.ulSize +
                                sizeof(FONTDEFINITIONHEADER));

    if (pFocaFont->fdDefinitions.fsChardef == FONTDEFCHAR3)
        CharDefSize = 5;         // 10 bytes = 5 Words
    else
        CharDefSize = 3;         // 6 bytes = 3 Words

    width = 0;

    for (i = 0; i < nChar; i++)
    {
        if (pFocaFont->fdDefinitions.fsFontdef & 0x01)
            BSpace = pFocaFont->fdDefinitions.xCellWidth;

        WordsUsed = (pStr[i] - pFocaFont->fmMetrics.usFirstChar) * CharDefSize;

        if (pFocaFont->fdDefinitions.fsChardef & 0x80)
            WordsUsed += 2;

        if (pFocaFont->fdDefinitions.fsChardef & 0x01)
            BSpace = pDefs[WordsUsed++];

        if (pFocaFont->fdDefinitions.fsChardef & 0x04)
            WordsUsed++;

        if (pFocaFont->fdDefinitions.fsChardef == FONTDEFCHAR3)
        {
            ASpace = pDefs[WordsUsed++];
            BSpace = pDefs[WordsUsed++];
            CSpace = pDefs[WordsUsed++];
        }
        else
        {
            ASpace = 0;
            CSpace = 0;
        }

        width += ASpace + BSpace + CSpace;
    }

    return width;
}

#endif 

