/*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          = EDDTQERY                                       */
/*                                                                    */
/*   Description     = Display Device Driver text query functions     */
/*                     QueryTextBox,                                  */
/*                     QueryCharPositions                             */
/*                                                                    */
/*   Function        =                                                */
/*                                                                    */
/*   Reference       = Winthorn Functional Specification              */
/*                     Device Driver Interface Specification          */
/*                     Display Device Driver Design Specification     */
/*                                                                    */
/*                                                                    */
/**********************************************************************/
/**********************************************************************/
/* This file is not used for SBCS driver now, but we need these funcs */
/* for DBCS driver, since we need to manage fonts (SBCS/DBCS) for     */
/* ourselves.  So it should be revived.                               */
/**********************************************************************/
#define INCL_DDIFONTSTRUCS
#define INCL_DDICOMFLAGS
#define INCL_DDIMISC
#define INCL_GRE_XFORMS
#define INCL_GRE_STRINGS
#define INCL_DOSMEMMGR
#include <eddinclt.h>

#include <eddetypt.h>
#include <edddtypt.h>
#include <eddttypt.h>
#include <eddgextf.h>
#include <eddtextf.h>
#include <eddacone.h>
#include <eddtcone.h>
#include <memman.h>

#include <eddjdef.h>
#include <eddjsub.h>

extern AIWORK           AIWork;
extern SHORT            Vector[];
extern PPFNL            EnginesDispatchTable;

/*====================================================================*/
/* useful macro                                                       */
/*====================================================================*/
#define NLSPARSEFLAG    (pdc->CurrentFont.NLSParseFlag)
#define NLSFONTFLAG     (pdc->CurrentFont.NLSFontFlag)
                                       /* note these 2 macros are     */
                                       /*  valid only between         */
                                       /*  EnterDriver and ExitDriver */
/*===================== Exported Routine =============================*/
/*      eddt_QueryTextBox                                             */
/*                                                                    */
/* Returns bounding rectangle of given string                         */
/*                                                                    */
/* Entry      :                                                       */
/*            - DC handle                                             */
/*            - Number of bytes in given string                       */
/*            - Ptr to string                                         */
/*            - Number of points to be returned                       */
/*            - Ptr to buffer where results should be returned        */
/*            - Ptr to driver's DC struct                             */
/*            - Function ID and Option flags                          */
/*                                                                    */
/* Returns    :                                                       */
/*            - Returns GPI_OK if completes                           */
/*            - Returns up to 5 points for bounding recntagle         */
/*                                                                    */
/* Error Returns :                                                    */
/*            - Returns GPI_ERROR if fails                            */
/*                                                                    */
/* Note       :                                                       */
/*            - Uses global work buffer, ausString and ausWidth.      */
/*                                                                    */
/*====================================================================*/
/**********************************************************************/
/* QueryTextBox processes the specified string as if it were to be    */
/* drawn, using the current character attributes and returns up to 5  */
/* coordinate pairs. The first four are the coordinates of the        */
/* rectangle which encompasses the string. The fifth point is the     */
/* concatenation point ie the position where a subsequent string      */
/* should be drawn. All coordinates are relative to the start point   */
/* of the string. Coordinates are returned in world space             */
/**********************************************************************/
DDIENTRY eddt_QueryTextBox (HDC           hdc,
                            ULONG         ArgCharnum,
                            PCHAR         ArgCodePoints,
                            ULONG         ArgCount,
                            pWcsRect      ArgTextBox,
                            PDC           pdcArg,
                            ULONG         FunN)

{
  USHORT  usBoxAdjustment;
  POINTL  lPoints[3];
  PFOCAFONT pFont;

  ULONG ulCharSpacing;                 /*                             */

  /*------------------------------------------------------------------*/
  /* entry check                                                      */
  /*------------------------------------------------------------------*/
  EnterDriver(pdcArg, FunN, EDF_STANDARD | EDF_DONT_CLEAN);

  pFont = pdc->CurrentFont.pFocaFont;

  if ((ArgCharnum & 0x80000000) ||
      ((NLSPARSEFLAG & NLSCA_DBCS) && (ArgCharnum & 1)))
    {
      LOGERR(TFUNC, "-ve ArgCharnum invalid", &ArgCharnum, 1,
                                        PMERR_INV_LENGTH_OR_COUNT);
      goto QUERYTEXTBOX_ERR_EXIT;
    }

  if (ArgCount & 0x80000000)
    {
      LOGERR(TFUNC, "-ve ArgCount invalid", &ArgCount, 1,
                                        PMERR_INV_LENGTH_OR_COUNT);
      goto QUERYTEXTBOX_ERR_EXIT;
    }

  /*------------------------------------------------------------------*/
  /* Check if simulations are required                                */
  /*------------------------------------------------------------------*/
  if (pdc->DCITextSim)
    {
      /*--------------------------------------------------------------*/
      /* if COM_DEVICE is set we must deal with the request           */
      /*--------------------------------------------------------------*/
      if (FunNTest(COM_DEVICE))
        {
          if ( pFont->fmMetrics.fsDefn & FM_DEFN_OUTLINE )
            {
              goto QUERYTEXTBOX_OK_EXIT;
            }
        }
      else
        {
          ExitDriver(pdcArg, FunN,
                     EDF_STANDARD | EDF_IGNORE_TIME | EDF_DONT_CLEAN);

          return
            EnginesDispatchTable[NGreQueryTextBox & 0xff](
                                                   hdc,
                                                   ArgCharnum,
                                                   ArgCodePoints,
                                                   ArgCount,
                                                   ArgTextBox,
                                                   pdcArg,
                                                   FunN);
        }
    }

  /*------------------------------------------------------------------*/
  /* Looks like we need to do it...                                   */
  /*------------------------------------------------------------------*/
  AIWork.Bound[0].X = AIWork.Bound[1].X = 0;

  AIWork.Bound[0].Y =
    pFont->fdDefinitions.pCellBaseOffset -
    pFont->fdDefinitions.yCellHeight;

  AIWork.Bound[1].Y = pFont->fdDefinitions.pCellBaseOffset;

  ulCharSpacing = pdc->DCICurTxtAts.cdef.charSpacing;

  /*------------------------------------------------------------------*/
  /* Provide a fast-path for all our friends using AUTOCAD.           */
  /* They seem to have a burning desire to know the text box          */
  /* surrounding fixed-pitch fonts!                                   */
  /*------------------------------------------------------------------*/
  if ( pdc->DCISpacingType == FIXED_PITCH )
    {
      /*==============================================================*/
      /* This should be OK for DBCS/MBCS, since with fixed pitch      */
      /* font, double byte char is 2 times wider than single byte     */
      /* char.  Simple, eh?                                           */
      /*==============================================================*/
      /*==============================================================*/
      /* Now we want to add charSpacing, too.  Problem is, it should  */
      /*      be added for each character, regardless it is single    */
      /*      or double byte char.                                    */
      /*==============================================================*/
      /*==============================================================*/
      /* If current CP is SBCS, total width of string is:             */
      /*      Width = (CW + CS) * N                                   */
      /*         where:  CW :  Cell Width                             */
      /*                 CS :  Character Spacing                      */
      /*                 N  :  Number of characters (= bytes)         */
      /* regardless the type of font.                                 */
      /*                                                              */
      /* If current CP is MBCS:                                       */
      /*   If used font is SBCS or DBCS:                              */
      /*      Width = (CW + CS) * N                                   */
      /*         where:  CW :  Cell Width                             */
      /*                       (SBCS cell width for SBCS font, DBCS   */
      /*                        cell width for DBCS font)             */
      /*                 CS :  Character Spacing                      */
      /*                 N  :  Number of characters (not bytes)       */
      /*                                                              */
      /*   If used font is MBCS:                                      */
      /*      Width = CW * NS + CW' * ND + CS * N                     */
      /*         where:  CW :  Cell Width (SBCS cell width)           */
      /*                 NS :  Number of single byte chars            */
      /*                 CW':  Cell Width (DBCS cell width)           */
      /*                 NS :  Number of double byte chars            */
      /*                 CS :  Character Spacing                      */
      /*                 N  :  Number of all chars  (not bytes)       */
      /*                 N' :  Number of bytes                        */
      /*                                                              */
      /*          and,   CW' = CW * 2                                 */
      /*                 NS + ND = N                                  */
      /*                 NS + ND * 2 = N'                             */
      /*          then,                                               */
      /*      Width = CW * N' + CS * N                                */
      /*                                                              */
      /* If current CP is DBCS:                                       */
      /*      Width = (CW + CS) * N                                   */
      /*         where:  CW :  Cell Width (DBCS cell width)           */
      /*                 CS :  Character Spacing                      */
      /*                 N  :  Number of characters                   */
      /*         and,    N = N' / 2                                   */
      /*                (N' :  Number of bytes)                       */
      /*                                                              */
      /*==============================================================*/
      /*--------------------------------------------------------------*/
      /* CP is SBCS                                                   */
      /*--------------------------------------------------------------*/
      if (NLSPARSEFLAG & NLSCA_SBCS)
        {                              /*                             */
          AIWork.Bound[1].X =
            (USHORT)(ArgCharnum *
            ((ULONG)pFont->fdDefinitions.xCellWidth + ulCharSpacing));
        }                              /* end of if:                  */

      /*--------------------------------------------------------------*/
      /* CP is MBCS                                                   */
      /*--------------------------------------------------------------*/
      else if (NLSPARSEFLAG & NLSCA_MBCS)
        {                              /*                             */
          if (NLSFONTFLAG & NLSCA_FONT_MBCS)
            {                          /*                             */
              AIWork.Bound[1].X =
                pFont->fdDefinitions.xCellWidth * ArgCharnum +
                ulCharSpacing *
                  eddj_CountChar( ArgCodePoints,
                                  ArgCharnum,
                                  &pdc->CurrentFont );
            }                          /* end of if:                  */
          else                         /* or if DBCS/SBCS font        */
            {                          /*                             */
              AIWork.Bound[1].X =
                (pFont->fdDefinitions.xCellWidth + ulCharSpacing) *
                eddj_CountChar( ArgCodePoints,
                                ArgCharnum,
                                &pdc->CurrentFont );
            }                          /* end of else:                */
        }                              /* end of if:                  */

      /*--------------------------------------------------------------*/
      /* CP is DBCS                                                   */
      /*--------------------------------------------------------------*/
      else                             /* or if CP is DBCS            */
        {                              /*                             */
          AIWork.Bound[1].X =
            (pFont->fdDefinitions.xCellWidth *
             (NLSFONTFLAG & NLSCA_FONT_MBCS ? 2 : 1) +
                                       /* this is width of DBCS cell  */
             ulCharSpacing) * ArgCharnum / 2;
        }                              /* end of else:                */
    }

  /*------------------------------------------------------------------*/
  /* Proportional or ABC font                                         */
  /*------------------------------------------------------------------*/
  else
    {                                  /*                             */
      ULONG ulProcessed;               /* #bytes processed            */
      ULONG ulConverted;               /* # U-DBCS chars converted    */
      ULONG ulStringLimit;             /* limit of string length we   */
                                       /*   can handle at once        */
      PFONTDETAILS pFontDtl;           /*                             */
      ULONG ulCharnum = ArgCharnum;    /*                             */
      PCHAR pchCodePoints = ArgCodePoints;

      eddj_CheckProcOptions( ArgCodePoints,
                             ArgCharnum,
                             &pdc->CurrentFont );
                                       /* first decide how to handle  */

      ulStringLimit = STRINGLIMIT( &pdc->CurrentFont );
                                       /* valid even if MUST_CHOP=OFF */
      pFontDtl = &pdc->CurrentFont;    /*                             */

      /*--------------------------------------------------------------*/
      /* repeat chop-and-call                                         */
      /*--------------------------------------------------------------*/
      if (NLSPARSEFLAG & NLSCA_UNIFIED_DBCS)
        {                              /* if U-DBCS conversion req'd  */
          while (ulCharnum)
            {                          /*                             */
              ulProcessed
                = eddj_UnifiedDBCS( pFontDtl,
                                    pchCodePoints,
                                    ulCharnum,
                                    NULL,
                                    ausString,
                                    ulStringLimit,
                                    NULL,
                                    &ulConverted );
                                       /* convert it to Unified DBCS  */
              pchCodePoints += ulProcessed;
              ulCharnum -= ulProcessed;
                                       /*                             */
              AIWork.Bound[1].X +=
                ulCharSpacing * ulProcessed +
                eddj_GetCharWidth( (PCHAR)ausString,
                                   ulConverted,
                                   pFontDtl,
                                   ausWidth );
                                       /* not need width of each char */
                                       /* so ausWidth is just dummy   */
            }                          /* end of while:               */
        }                              /* end of if:                  */
      else                             /*                             */
        {                              /* otherwise do with SBCS str  */
          AIWork.Bound[1].X = ulCharSpacing * ulCharnum;

          while (ulCharnum)            /*                             */
            {
              ulProcessed = min( ulCharnum, ulStringLimit );

              AIWork.Bound[1].X +=
                eddj_GetCharWidth( pchCodePoints,
                                   ulProcessed,
                                   pFontDtl,
                                   ausWidth );
                                       /* not need width of each char */
                                       /* so ausWidth is just dummy   */
              pchCodePoints += ulProcessed;
              ulCharnum -= ulProcessed;
            }                          /* end of while:               */
        }                              /* end of else:                */
    }

  /********************************************************************/
  /* Calculate an adjustment factor to control whether the            */
  /* returned box is inclusive or exclusive.                          */
  /* If the resulting box is going to be transformed into World       */
  /* coords then the box should be inclusive, else it will be in      */
  /* device coords and should be exclusive. The current rectangle     */
  /* in AIWork.Bound is exclusive.                                    */
  /********************************************************************/
  /*==================================================================*/
  /* Pres. Driver Ref. says it should be inclusive, but our experi-   */
  /* ence has proven it should be exclusive...                        */
  /*==================================================================*/
  usBoxAdjustment = 0;

  /********************************************************************/
  /* MJB 10/15/91: Copy the points from AIWork.Bound into an array    */
  /* of LONGs before transforming coordinates. Also transform the     */
  /* point (0,0), since we need to return the text box relative to    */
  /* the origin.                                                      */
  /********************************************************************/
  lPoints[0].x = (LONG) AIWork.Bound[0].X;
  lPoints[0].y = (LONG) AIWork.Bound[0].Y;
  lPoints[1].x = (LONG) AIWork.Bound[1].X;
  lPoints[1].y = (LONG) AIWork.Bound[1].Y;
  lPoints[2].x = lPoints[2].y = 0;

  /********************************************************************/
  /* convert to world coordinates if transform flag is set            */
  /********************************************************************/
  if ( (!pdc->DCIXFrmSimple) &&
       (FunNTest(COM_TRANSFORM)))
  {
      if ( OK != EnginesDispatchTable[NGreConvert & 0xff](
                                    hdc,
                                    CVTC_DEVICE,
                                    CVTC_WORLD,
                                    (PPOINTL)lPoints,
                                    3,
                                    0,
                                    NGreConvert) )
      {
          goto QUERYTEXTBOX_ERR_EXIT;
      }
      /****************************************************************/
      /* Subtract the point that (0,0) was transformed to from the    */
      /* other 2 points transformed to keep all coords relative to    */
      /* the origin.                                                  */
      /****************************************************************/
      lPoints[0].x -= lPoints[2].x;
      lPoints[0].y -= lPoints[2].y;
      lPoints[1].x -= lPoints[2].x;
      lPoints[1].y -= lPoints[2].y;
  }

  /********************************************************************/
  /* Return Coordinates in TextBox as follows:                        */
  /*         TopLeft, BottomLeft, TopRight, BottomRight,              */
  /*                    NextCharPos                                   */
  /********************************************************************/
  switch( min(4, ((int)ArgCount-1)) )
  {
      case 4:
          (*ArgTextBox)[4].X = lPoints[1].x;
          (*ArgTextBox)[4].Y = 0;

      case 3:
          (*ArgTextBox)[3].X = lPoints[1].x - usBoxAdjustment;
          (*ArgTextBox)[3].Y = lPoints[0].y;

      case 2:
          (*ArgTextBox)[2].X = lPoints[1].x - usBoxAdjustment;
          (*ArgTextBox)[2].Y = lPoints[1].y - usBoxAdjustment;

      case 1:
          (*ArgTextBox)[1].X = lPoints[0].x;
          (*ArgTextBox)[1].Y = lPoints[0].y;

      case 0:
          (*ArgTextBox)[0].X = lPoints[0].x;
          (*ArgTextBox)[0].Y = lPoints[1].y - usBoxAdjustment;
  }

QUERYTEXTBOX_OK_EXIT:
  /********************************************************************/
  /* Release driver semaphore                                         */
  /********************************************************************/
  ExitDriver(pdcArg, FunN, EDF_STANDARD | EDF_DONT_CLEAN);
  return GPI_OK;

QUERYTEXTBOX_ERR_EXIT:
  /********************************************************************/
  /* Release driver semaphore                                         */
  /********************************************************************/
  ExitDriver(pdcArg, FunN, EDF_STANDARD | EDF_DONT_CLEAN);
  return GPI_ERROR;
}

/*===================== Exported Routine =============================*/
/*      eddt_QueryCharPositions                                       */
/*                                                                    */
/* Returns position of each character included in given string        */
/*                                                                    */
/* Entry      :                                                       */
/*            - DC handle                                             */
/*            - Start position                                        */
/*            - Option flags                                          */
/*            - Number of bytes in given string                       */
/*            - Ptr to string                                         */
/*            - Ptr to increment vectors                              */
/*            - Ptr to buffer where results should be returned        */
/*            - Ptr to driver's DC struct                             */
/*            - Function ID and Option flags                          */
/*                                                                    */
/* Returns    :                                                       */
/*            - Returns GPI_OK if completes                           */
/*            - Returns position for each char in given buffer        */
/*                                                                    */
/* Error Returns :                                                    */
/*            - Returns GPI_ERROR if fails                            */
/*                                                                    */
/* Note       :                                                       */
/*            - Uses global work buffer, ausString and ausWidth.      */
/*                                                                    */
/*            - After investigating PMWIN source, I believe it expects*/
/*              us to put NOTHING to char position field for DBCS     */
/*              trailing bytes.  So I have added FILL_TRAILING_CHARPOS*/
/*              ifdefs...                                             */
/*                                                                    */
/*              ==> But GRE32 assumes that position for DBCS trailing */
/*              byte should be same to that for next character!       */
/*              Otherwise, GRE32 would call CharStringPos with        */
/*              incorrect increment vector values.  So we will follow */
/*              GRE32's assumption since PMWIN does not cause problems*/
/*              in this case.  FILL_TRAILING... has been removed.     */
/*                                                                    */
/*====================================================================*/
/**********************************************************************/
/* QueryCharPositions returns the positions in device coordinates of  */
/* where the currently associated device would place each given       */
/* character, taking into account kerning, etc. A vector of increments*/
/* may optionally be specified, which allows control over the         */
/* positioning of each character after the first. These distances are */
/* measured in world ccordinates. The last element of the array       */
/* contains the new current position. (Internal spec 2.70 p73)        */
/**********************************************************************/

DDIENTRY eddt_QueryCharPositions (HDC           hdc,
                                  PPOINTL       ArgStartXY,
                                  ULONG         ArgOptions,
                                  ULONG         ArgCharnum,
                                  PCHAR         ArgCodePoints,
                                  PLONG         ArgPosVector,
                                  PPOINTL       ArgXY,
                                  PDC           pdcArg,
                                  ULONG         FunN)
{
    POINTL ptlCurrentPos;
    ULONG ulCharSpacing;
    PPOINTL pptlXY = ArgXY;
    ULONG ulCharnum = ArgCharnum;
    PCHAR pchCodePoints = ArgCodePoints;
    #define pusCodePoints   ((PUSHORT)pchCodePoints)
    PLONG plPosVector = ArgPosVector;
    PBYTE pDBCSMap;


    EnterDriver(pdcArg, FunN, EDF_STANDARD | EDF_DONT_CLEAN);

    if (ArgCharnum > 32767)
    {
        LogError(PMERR_INV_LENGTH_OR_COUNT);
        goto QUERYCHARPOS_ERR_EXIT;
    }

    // if odd number of chars for /DBCS CP, return error
    if ((NLSPARSEFLAG & NLSCA_DBCS) &&
        (ArgCharnum & 1))
    {
        LogError( PMERR_INV_LENGTH_OR_COUNT );
        goto QUERYCHARPOS_ERR_EXIT;
    }

//  COMMANDBITS(FunN) &= pdc->DCICommandMask;

    if (pdc->DCITextSim)
    {
        /*------------------------------------------------------------*/
        /* If COM_DEVICE is set we must deal with the request         */
        /*------------------------------------------------------------*/
        if (FunNTest(COM_DEVICE))
        {
            /*--------------------------------------------------------*/
            /* If it's a vector font, there's no more we can do, just */
            /* say we've done it                                      */
            /*--------------------------------------------------------*/
            if (pdc->CurrentFont.
                   pFocaFont->fmMetrics.fsDefn & FONT_IS_VECTOR )
            {
                goto QUERYCHARPOS_OK_EXIT;
            }
        }
        else
        {
            /*--------------------------------------------------------*/
            /* Call the engine to provide the simulation              */
            /*--------------------------------------------------------*/

            ExitDriver(pdcArg, FunN,
                       EDF_STANDARD | EDF_IGNORE_TIME | EDF_DONT_CLEAN);
            return EnginesDispatchTable[NGreQueryCharPositions & 0xff](
                                                        hdc,
                                                        ArgStartXY,
                                                        ArgOptions,
                                                        ArgCharnum,
                                                        ArgCodePoints,
                                                        ArgPosVector,
                                                        ArgXY,
                                                        pdcArg,
                                                        FunN);
        }
    }

    /*----------------------------------------------------------------*/
    /* Get start position                                             */
    /*----------------------------------------------------------------*/
    // if start pos supplied
    if (ArgOptions & CHS_START_XY)
    {
        if (FunNTest(COM_TRANSFORM) && !(pdc->DCIXFrmSimple))
        {
            // convert world coord to device coord
            if (OK  != eddg_Convert ((PULONG)ArgStartXY,
                                     (PULONG)&ptlCurrentPos,
                                     COORD_WORLD,
                                     COORD_DEVICE,
                                     1,
                                     FunN))
            {
                goto QUERYCHARPOS_ERR_EXIT;
            }
        }
        else
        {
            ptlCurrentPos = *ArgStartXY;
        }
    }
    else
    {
        // or if should use current pos (need to conv. to dev.coord)
        ptlCurrentPos.x = pdc->DCICurrPosAI.X;
        ptlCurrentPos.y = pdc->DCIConvFactor - pdc->DCICurrPosAI.Y;
    }

    // now we have valid start pos put current pos to top buff

    *(pptlXY ++) = ptlCurrentPos;

    if (!(ArgOptions & CHS_VECTOR))
    {
      plPosVector = NULL;
    }

    eddj_CheckProcOptions (ArgCodePoints,
                           ArgCharnum,
                           &pdc->CurrentFont);

    // first decide how to handle
    // prepare charspacing value
    // note if vector supplied, we should not use this and
    // hence set it to 0 here.

    ulCharSpacing = plPosVector ? 0 : pdc->DCICurTxtAts.cdef.charSpacing;

    /*================================================================*/
    /* if fixed pitch font and no vector supplied, we can do now!     */
    /*================================================================*/
    if ((pdc->DCISpacingType == FIXED_PITCH) &&
        (plPosVector == NULL))
    {
        USHORT usWidth;

        usWidth = pdc->CurrentFont.pFocaFont->fdDefinitions.xCellWidth;

        /*============================================================*/
        /* if UNIFIED_DBCS is ON and font is SBCS, we can handle it   */
        /*   just like SBCS string, since in this case, all 2bytes    */
        /*   chars should be converted to SBCS default char.          */
        /*                                                            */
        /* if UNIFIED_DBCS is ON and CP is DBCS, we can handle        */
        /*   it just like SBCS string, since all chars are 2bytes     */
        /*   and have same width. Note when font is MBCS, cell width  */
        /*   in font definition is for 1byte char, and must be        */
        /*   doubled to get width of 2bytes char.                     */
        /*============================================================*/
        if (!(NLSPARSEFLAG & NLSCA_UNIFIED_DBCS))
        {
            while (ulCharnum--)
            {
                ptlCurrentPos.x += usWidth + ulCharSpacing;
                *pptlXY = ptlCurrentPos;
                pptlXY ++;
            }
        }
        else if (NLSPARSEFLAG & NLSCA_DBCS)
        {
            /* or if pure DBCS string...   */

            if (NLSFONTFLAG & NLSCA_FONT_MBCS)
            {
                // if font is MBCS, this is width of DBCS char.
                usWidth *= 2;
            }

            while (ulCharnum)
            {
                ptlCurrentPos.x += usWidth + ulCharSpacing;
                *pptlXY = *(pptlXY+1) = ptlCurrentPos;
                pptlXY += 2;
                ulCharnum -= 2;     // is # of bytes, actually
            }
        }
        else
        {
            /* or if MBCS codepage...      */

            pDBCSMap = pdc->CurrentFont.pDBCSMap;

            // will handle last byte later
            while (ulCharnum > 1)
            {
                if (pDBCSMap[*pchCodePoints])
                {
                    ptlCurrentPos.x += usWidth * 2 + ulCharSpacing;

                    *pptlXY = *(pptlXY+1) = ptlCurrentPos;
                    pptlXY += 2;
                    pchCodePoints += 2;
                    ulCharnum -= 2;     // is # of bytes, actually
                }
                else
                {
                    // or if single byte char
                    ptlCurrentPos.x += usWidth + ulCharSpacing;
                    *pptlXY = ptlCurrentPos;
                    pptlXY ++;
                    pchCodePoints ++;
                    ulCharnum --;
                }
            }

            // if have 1 more byte,
            if (ulCharnum)
            {
                // consider it as SBCS
                ptlCurrentPos.x += usWidth + ulCharSpacing;
                *pptlXY = ptlCurrentPos;
                pptlXY ++;
            }
        }
    }

    /*================================================================*/
    /* if proportional or vector supplied, a little more complex...   */
    /*================================================================*/
    else
    {
        ULONG ulProcessed;              // #bytes processed
        ULONG ulConverted;              // # U-DBCS chars converted
        ULONG ulStringLimit;            // limit of string length we can handle at once
        PFONTDETAILS pFontDtl;
        PLONG plVectBuff;
        PCHAR pchString;
        PUSHORT pusWidth;

        // valid even if MUST_CHOP=OFF
        ulStringLimit = STRINGLIMIT (&pdc->CurrentFont);
        pFontDtl = &pdc->CurrentFont;

        /*------------------------------------------------------------*/
        /* repeat chop-and-call                                       */
        /*------------------------------------------------------------*/
        while (ulCharnum)
        {
            /*--------------------------------------------------------*/
            /* chop and convert to U-DBCS if necessary                */
            /*--------------------------------------------------------*/
            if (NLSPARSEFLAG & NLSCA_UNIFIED_DBCS)
            {
                // if U-DBCS conversion req'd
                ulProcessed
                  = eddj_UnifiedDBCS (pFontDtl,
                                      pchCodePoints,
                                      ulCharnum,
                                      plPosVector,
                                      ausString,
                                      ulStringLimit,
                                      alVector,
                                      &ulConverted);
                // convert it to Unified DBCS using these work buffer
                plVectBuff = alVector;
                pchString = (PCHAR)ausString;
            }
            else
            {
                // otherwise do with SBCS str
                ulProcessed = ulConverted = min (ulCharnum, ulStringLimit);

                plVectBuff = plPosVector;
                pchString = ArgCodePoints;

            }

            pchCodePoints += ulProcessed;
            ulCharnum -= ulProcessed;
            // Note ulProcessed is #bytes and ulConverted is #chars

            /*--------------------------------------------------------*/
            /* if vector supplied, do with it                         */
            /*--------------------------------------------------------*/
            if (plPosVector)
            {
                // if increment vector supplied

                // advance vector ptr first
                // Note vector exists for each byte in string (not char)
                // Also note when U-DBCS is used (means MBCS/DBCS strg)
                // eddj_UnifiedDBCS converted given vector to 1 vector
                // per character format.

                plPosVector += ulProcessed;

                // convert vector to device coordinate.  We put it to
                // ausWidth so that we can handle it just like char widths later...

                if (eddt_ConvertIncVector (ausWidth,
                                           (USHORT)ulConverted,
                                           plVectBuff ) != OK)
                {
                    goto QUERYCHARPOS_ERR_EXIT;
                }
            }
            else
            /*--------------------------------------------------------*/
            /* if vector not supplied and proportional font...        */
            /*--------------------------------------------------------*/
            {
                // total width is not used this can handle any combination of CP/FONT

                eddj_GetCharWidth (pchString,
                                   ulConverted,
                                   pFontDtl,
                                   ausWidth);
            }

            /*--------------------------------------------------------*/
            /* now we calc position of each char...                   */
            /*--------------------------------------------------------*/
            pusWidth = ausWidth;

            if (!(NLSPARSEFLAG & NLSCA_UNIFIED_DBCS))
            {
                // if it is SBCS string
                while (ulConverted--)
                {
                    ptlCurrentPos.x += *(pusWidth ++) + ulCharSpacing;
                    *pptlXY = ptlCurrentPos;
                    pptlXY ++;
                }
            }
            else if (NLSPARSEFLAG & NLSCA_DBCS)
            {
                // or if pure DBCS string
                while (ulConverted--)
                {
                    ptlCurrentPos.x += *(pusWidth ++) + ulCharSpacing;
                    *pptlXY = *(pptlXY+1) = ptlCurrentPos;
                    pptlXY += 2;
                }
            }
            else
            {
                // or if MBCS string

                
                // The following code has bug.
                // The eddj_UnifiedDBCS may replace DBCS code point as
                // default char (0x20) in ausString.
                // This occurred when
                //      - SBCS font is realized on MBCS codepage
                //      - and string passed contain DBCS characters.
#ifndef   TMP
                // here is U-DBCS string
                pusCodePoints = ausString;
                while (ulConverted--)
                {
                    ptlCurrentPos.x += *(pusWidth ++) + ulCharSpacing;

                    *pptlXY = ptlCurrentPos;
                    pptlXY ++;

                    // if this is double byte char
                    if (HIBYTE(*pusCodePoints))
                    {
                        // also need to set next value
                        *pptlXY = ptlCurrentPos;
                        pptlXY ++;
                    }
                    pusCodePoints ++;
                }
#else  // TMP
                pDBCSMap = pdc->CurrentFont.pDBCSMap;
                pchCodePoints = ArgCodePoints;

                while (ulConverted--)
                {
                    ptlCurrentPos.x += *(pusWidth ++) + ulCharSpacing;

                    *pptlXY++ = ptlCurrentPos;

                    // if this is double byte char
                    if (pDBCSMap[*pchCodePoints])
                    {
                        // also need to set next value
                        *pptlXY++ = ptlCurrentPos;
                        pchCodePoints++;
                    }
                    pchCodePoints++;
                }
#endif // TMP
            }
        }
    }

    /*----------------------------------------------------------------*/
    /* now we have filled all positions                               */
    /*----------------------------------------------------------------*/

    if (FunNTest(COM_TRANSFORM) && !(pdc->DCIXFrmSimple))
    {
        if ( OK != eddg_Convert ((PULONG)ArgXY,
                                 (PULONG)ArgXY,
                                 COORD_DEVICE,
                                 COORD_WORLD,
                                 (USHORT)ArgCharnum+1,
                                 COM_TRANSFORM) )
        {
          goto QUERYCHARPOS_ERR_EXIT;
        }
    }

QUERYCHARPOS_OK_EXIT:
    /******************************************************************/
    /* Release driver semaphore                                       */
    /******************************************************************/
    ExitDriver(pdcArg, FunN, EDF_STANDARD | EDF_DONT_CLEAN);
    return(TRUE);



QUERYCHARPOS_ERR_EXIT:
    /******************************************************************/
    /* Release driver semaphore                                       */
    /******************************************************************/
    ExitDriver(pdcArg, FunN, EDF_STANDARD | EDF_DONT_CLEAN);
    return(FALSE);

}

/*===================== Exported Routine =============================*/
/*      eddt_QueryWidthTable                                          */
/*                                                                    */
/* Returns table of width for given range of characters               */
/*                                                                    */
/* Entry      :                                                       */
/*            - DC handle                                             */
/*            - First codepoint of range                              */
/*            - Number of codepoints                                  */
/*            - Ptr to buffer where results should be returned        */
/*            - Ptr to driver's DC struct                             */
/*            - Function ID and Option flags                          */
/*                                                                    */
/* Returns    :                                                       */
/*            - Returns GPI_OK if completes                           */
/*            - Returns char width array                              */
/*                                                                    */
/* Error Returns :                                                    */
/*            - Returns GPI_ERROR if fails                            */
/*                                                                    */
/* Note       :                                                       */
/*            - Mixing SBCS range and DBCS range is not allowed.      */
/*              If first codepoint is SBCS, all range is considered   */
/*              as SBCS chars.  otherwise, all range is DBCS chars.   */
/*                                                                    */
/*            - Uses global work buffer, ausString and ausWidth.      */
/*                                                                    */
/*====================================================================*/
/**********************************************************************/
/* QueryWidthTable returns the width table for the currently          */
/* selected font in the table pointed to by WidthTable                */
/**********************************************************************/

DDIENTRY eddt_QueryWidthTable(HDC       hdc,
                              ULONG     ArgFirstChar,
                              ULONG     ArgCount,
                              PULONG    ArgWidthTable,
                              PDC       pdcArg,
                              ULONG     FunN)

{

  ULONG              ulWidth;          /* width of fixed pitch chars  */
  PULONG             WidthTable;       /* pointer to width table      */
  ULONG              ulCount;          /* character count             */
  USHORT             usFirstChar;      /* Code point of first char    */
  PFONTDETAILS       pFontDtl;         /*                             */
  PFOCAFONT          pFont;            /*                             */
  USHORT             fDbcsRange;       /* flag if DBCS codepoint range*/
  ULONG              i;


  /*------------------------------------------------------------------*/
  /* entry check                                                      */
  /*------------------------------------------------------------------*/
  EnterDriver(pdcArg, FunN, EDF_STANDARD | EDF_DONT_CLEAN);

  pFontDtl = &pdc->CurrentFont;        /* for later use               */
  pFont = pFontDtl->pFocaFont;         /*                             */
                                       /*                             */
  if (HIUSHORT(ArgFirstChar))          /* check codepoint range       */
    {                                  /*                             */
      LogError(PMERR_INV_FIRST_CHAR);  /*                             */
      goto QUERYWIDTHTABLE_ERR_EXIT;   /*                             */
    }                                  /* end of if:                  */

  if ((pFontDtl->NLSParseFlag & NLSCA_SBCS)
   || ((pFontDtl->NLSParseFlag & NLSCA_MBCS)
    && !IS_DBCS_LEADING( LOBYTE(ArgFirstChar), *pFontDtl )))
    {                                  /* if current CP is SBCS       */
                                       /* or CP=MBCS & ! DBCS 1st char*/
                                       /* Note: if 2bytes char        */
                                       /*  is given, leading byte is  */
                                       /*  stored in lowest byte.     */
                                       /*  (i.e. first byte in memory)*/
      usFirstChar = LOUSHORT(ArgFirstChar);
      fDbcsRange = FALSE;              /* SBCS range is requested     */
      if (ArgFirstChar > 255)
      {
          LogError(PMERR_INV_FIRST_CHAR);
          goto QUERYWIDTHTABLE_ERR_EXIT;
      }
    }                                  /* end of if:                  */
  else                                 /* this is double byte char... */
    {                                  /*                             */
      usFirstChar
        = MAKEUSHORT( HIBYTE(ArgFirstChar), LOBYTE(ArgFirstChar) );
                                       /* handle it as USHORT         */
      fDbcsRange = TRUE;               /* DBCS range is requested     */
    }                                  /* end of else:                */

  if (HIUSHORT(ArgCount))
    {
      LogError(PMERR_INV_LENGTH_OR_COUNT);
      goto QUERYWIDTHTABLE_ERR_EXIT;
    }

  /*------------------------------------------------------------------*/
  /* simulation check                                                 */
  /*------------------------------------------------------------------*/
  if (pdc->DCITextSim)
    {
      if ( !(FunNTest(COM_DEVICE) ))
        {
          /*----------------------------------------------------------*/
          /* if COM_DEVICE is not set, call back engine               */
          /*----------------------------------------------------------*/
          ExitDriver(pdcArg, FunN,
                     EDF_STANDARD | EDF_IGNORE_TIME | EDF_DONT_CLEAN);
          return(
            EnginesDispatchTable[NGreQueryWidthTable & 0xff](
                                                        hdc,
                                                        ArgFirstChar,
                                                        ArgCount,
                                                        ArgWidthTable,
                                                        pdcArg,
                                                        FunN ));
        }
      else
        {
          /*----------------------------------------------------------*/
          /* if COM_DEVICE is set, will do our best effort            */
          /*    as far as it is not vector font                       */
          /*----------------------------------------------------------*/
          if ( pFont->fmMetrics.fsDefn & FM_DEFN_OUTLINE)
            {
              goto QUERYWIDTHTABLE_OK_EXIT;
            }
        }
    }

  /*------------------------------------------------------------------*/
  /* Do a little more checking on the number of characters that       */
  /* we are being queried about.                                      */
  /*------------------------------------------------------------------*/
  if (!fDbcsRange                      /* if SBCS range is requested  */
   && ( ArgCount + usFirstChar > 256 ))
    {                                  /* and too many count          */
      /*--------------------------------------------------------------*/
      /* We are being asked to return more characters than there      */
      /* are codepoints. Set Count so that we only return the         */
      /* ones that we have available, and fill the rest of the        */
      /* array with zeroes.                                           */
      /*--------------------------------------------------------------*/
      ulCount = (ULONG)(256 - usFirstChar);

      for ( i = ulCount; i < ArgCount; i++ )
        {
          ArgWidthTable[i] = 0;
        }
    }
  else
    {
      ulCount = ArgCount;
    }

  WidthTable = ArgWidthTable;

  /*------------------------------------------------------------------*/
  /* fixed pitch font                                                 */
  /*------------------------------------------------------------------*/
  if (pdc->DCISpacingType == FIXED_PITCH)
    {
      ulWidth = (ULONG)pFont->fdDefinitions.xCellWidth;

      if (fDbcsRange && !(pFontDtl->NLSFontFlag & NLSCA_FONT_DBCS))
        {                              /* SBCS/MBCS font has cell     */
          ulWidth *= 2;                /*  width for 1byte char       */
        }                              /* end of if:                  */

      /*--------------------------------------------------------------*/
      /* Add in the simulation spacing to the width (for bold fonts)  */
      /*--------------------------------------------------------------*/
      ulWidth += pdc->DCICurTxtAts.cdef.charSpacing;

      while( ulCount-- )
        {
          *WidthTable++ = ulWidth;
        }
    }

  /*------------------------------------------------------------------*/
  /* proportional font                                                */
  /*------------------------------------------------------------------*/
  else
    {                                  /*                             */
      ULONG ulStringLimit;             /*                             */
      ULONG ulThisTime;                /*                             */
      ULONG ulCharSpacing = pdc->DCICurTxtAts.cdef.charSpacing;

      /*--------------------------------------------------------------*/
      /* some preparation                                             */
      /*--------------------------------------------------------------*/
      if (fDbcsRange)                  /* set operation options       */
        {                              /*                             */
          pFontDtl->NLSParseFlag |= NLSCA_UNIFIED_DBCS;
          pFontDtl->NLSParseFlag &= ~NLSCA_SBC_ONLY;
        }                              /* end of if:                  */
      else                             /*                             */
        {
          pFontDtl->NLSParseFlag &= ~NLSCA_UNIFIED_DBCS;
          pFontDtl->NLSParseFlag |= NLSCA_SBC_ONLY;
        }                              /* end of else:                */

      eddj_IsCallDbcsFM( pFontDtl );   /* this depends on font type   */
                                       /* do not care for MUST_CHOP   */
                                       /* as we will chop anyway      */
      ulStringLimit = STRINGLIMIT( pFontDtl );

      /*--------------------------------------------------------------*/
      /* create string and get width                                  */
      /*--------------------------------------------------------------*/
      while (ulCount)                  /*                             */
        {                              /*                             */
          ulThisTime = min(ulCount, ulStringLimit);

          if (fDbcsRange)              /*                             */
            {                          /*                             */
              for (i = 0; i < ulThisTime; i ++)
                {                      /* create DBCS string          */
                  ausString[i] = usFirstChar ++;
                }                      /* end of for:                 */
            }                          /* end of if:                  */
          else                         /*                             */
            {                          /*                             */
              for (i = 0; i < ulThisTime; i ++)
                {                      /* create SBCS string          */
                  ((PUCHAR)ausString)[i] = (UCHAR)(usFirstChar ++);
                }                      /* end of for:                 */
            }                          /* end of else:                */

          eddj_GetCharWidth( (PCHAR)ausString,
                             ulThisTime,
                             pFontDtl,
                             ausWidth );
                                       /* we put width to work buffer */
                                       /*  as this func returns USHORT*/
          for (i = 0; i < ulThisTime; i ++)
            {                          /*                             */
              *(WidthTable ++) = ulCharSpacing + ausWidth[i];
            }                          /* end of for:                 */

          ulCount -= ulThisTime;       /*                             */
        }                              /* end of while:               */
    }                                  /* end of if: proportional     */

  /*------------------------------------------------------------------*/
  /* Now that the width table has been set up in device coordinates   */
  /* we may have to twiddle it a bit...                               */
  /*------------------------------------------------------------------*/
  if (FunNTest(COM_TRANSFORM) && !pdc->DCIXFrmSimple)
    {
#define BATCH 32
      POINTL             Points[BATCH] ;
      SHORT              Count ;
      SHORT              NPoints ;
      SHORT              NumVec ;
      PLONG              WldWidth ;
      PLONG              DevWidth ;
      POINTL             Origin;

      /*--------------------------------------------------------------*/
      /* first convert the origin                                     */
      /*--------------------------------------------------------------*/
      Origin.x = 0;
      Origin.y = 0;
      if ( ! EnginesDispatchTable[NGreConvert & 0xff](
                                               pdc->DCIhdc,
                                               CVTC_DEVICE,
                                               CVTC_WORLD,
                                               (PPOINTL)&Origin,
                                               1,
                                               NULL,
                                               NGreConvert )
         )
        {
          goto QUERYWIDTHTABLE_ERR_EXIT;
        }

      /*--------------------------------------------------------------*/
      /* now convert the given points in batches                      */
      /*--------------------------------------------------------------*/
      NumVec = (USHORT)ArgCount ;
      WldWidth = ArgWidthTable ;
      DevWidth = ArgWidthTable ;

      while (NumVec)
        {
          NPoints = min( NumVec, BATCH );
          NumVec -= NPoints;

          memset( (PVOID)Points, 0, NPoints*sizeof(POINTL) );

          for ( Count = 0 ; Count < NPoints ; Count++)
            {
              Points[Count].x = *WldWidth++ ;
            }

          if ( ! EnginesDispatchTable[NGreConvert & 0xff](
                                                   pdc->DCIhdc,
                                                   CVTC_DEVICE,
                                                   CVTC_WORLD,
                                                   (PPOINTL)Points,
                                                   (LONG)NPoints,
                                                   NULL,
                                                   NGreConvert )
             )
            {
              goto QUERYWIDTHTABLE_ERR_EXIT;
            }

          for (Count=0; Count < NPoints; Count++)
            {
              *DevWidth++ = Points[Count].x - Origin.x;
            }
        } /* while NumVec */
    }


QUERYWIDTHTABLE_OK_EXIT:

    ExitDriver(pdcArg, FunN, EDF_STANDARD | EDF_DONT_CLEAN);
    return GPI_OK;

QUERYWIDTHTABLE_ERR_EXIT:
    ExitDriver(pdcArg, FunN, EDF_STANDARD | EDF_DONT_CLEAN);
    return GPI_ERROR;
}

