/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT (C) Microsoft Corporation, 1989                                 */
/* COPYRIGHT    Copyright (C) 1995 IBM Corporation                           */
/*                                                                           */
/*    The following IBM OS/2 WARP source code is provided to you solely for  */
/*    the purpose of assisting you in your development of OS/2 WARP device   */
/*    drivers. You may use this code in accordance with the IBM License      */
/*    Agreement provided in the IBM Device Driver Source Kit for OS/2. This  */
/*    Copyright statement may not be removed.                                */
/*                                                                           */
/*****************************************************************************/
#pragma  pagesize(55)

/**************************************************************************
 *
 * SOURCE FILE NAME = FONT.C
 *
 * DESCRIPTIVE NAME = PLOTTER DRIVER
 *
 *
 * VERSION = V2.0
 *
 * DATE                06/21/89
 *
 * DESCRIPTION This file contains the code and data for font support
 *
 *
 * FUNCTIONS   cpxfm_metrics              copy fontmetrics from source
 *                                        to destination
 *
 *             DeviceQueryFontAttributes()Return font metrics
 *
 *             DeviceQueryFonts           Return information about the
 *                                        plotter's built-in fonts
 *
 *             RealizeFont()              PROCESS DEVICE FONTS
 *
 *             QueryTextBox               QUERY TEXT BOX FOR CHAR STRING
 *
 *             QueryWidthTable            Return width info for font
 *
 *             QueryCharPositions()       Determine the origin of char
 *
 *
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/
#define  INCL_DDIMISC2 1               /* Get the Realize Font commands     */
#define  INCL_GPIPRIMATIVES

#include "plotters.h"
#include "dispatch.h"
#include "error.h"
#include "outpline.h"
#include "utils.h"
#include "xforms.h"
#include "font.h"
#include "init.h"
#include "lockddc.h"
#include "prdmath.h"
#include "new.h"

#include <pmfont.h>

/*
** Define a structure used to transform the fontmetrics data values
**  that need to be transformed.  These are filled in from the source
**  values,  processed through GreConvert,  then copied into the destination.
** NOTES:  The first point is a zero reference to enable the other
**  values to be adjusted to a zero base.  The comments following each
**  entry indicate which values of the pair are used.
*/

typedef struct
{
  POINTL ptlZero;                      /* X & Y                             */
  POINTL ptlEmHeight;                  /* Y                                 */
  POINTL ptlXHeight;                   /* Y                                 */
  POINTL ptlMaxAscender;               /* Y                                 */
  POINTL ptlMaxDescender;              /* Y                                 */
  POINTL ptlLowerCaseAscent;           /* Y                                 */
  POINTL ptlLowerCaseDescent;          /* Y                                 */
  POINTL ptlInternalLeading;           /* Y                                 */
  POINTL ptlExternalLeading;           /* Y                                 */
  POINTL ptlAveCharWidth;              /* X                                 */
  POINTL ptlMaxCharInc;                /* X                                 */
  POINTL ptlEmInc;                     /* X                                 */
  POINTL ptlMaxBaselineExt;            /* Y                                 */
  POINTL ptlSubscriptSize;             /* X & Y                             */
  POINTL ptlSubscriptOffset;           /* X & Y                             */
  POINTL ptlSuperscriptSize;           /* X & Y                             */
  POINTL ptlSuperscriptOffset;         /* X & Y                             */
  POINTL ptlUnderscoreSize;            /* Y                                 */
  POINTL ptlUnderscorePosition;        /* Y                                 */
  POINTL ptlStrikeoutSize;             /* Y                                 */
  POINTL ptlStrikeoutPosition;         /* y                                 */
} FONTXFORM;

/*
**   Number of POINTLs in the above
*/

#define  FONT_XFORM_POINTS (sizeof( FONTXFORM )/ sizeof( POINTL ))
#define  FRAC_FX(fx)        MAKEFIXED(0,FIXEDFRAC(fx))
#define  ROUNDFX(x) FIXEDINT((x) + 0x8000)  // Round to nearest whole number

/*
**    Local Functions
*/

VOID foca_to_font(PFONTMETRICS,PFOCAFONT);

VOID GetExtraIncrements(PDDC, ULONG, PPOINTL, PFIXED, PFIXED, PFIXED,
                        PFIXED, PFIXED, PFIXED);

ULONG FindVectorLength(PPOINTL);

LONG  GetHorizTextAlignShift(LONG, LONG, LONG, LONG, USHORT, USHORT);

LONG  GetVertTextAlignShift(LONG, LONG, LONG, LONG, USHORT, USHORT);

USHORT GetStandardAlignment(USHORT, USHORT);

/***************************************************************************
 *
 * FUNCTION NAME =  cpxfm_metrics
 *
 *
 * DESCRIPTION   =  copy fontmetrics from source to destination
 *                   Copy fontmetrics from source to destination,
 *                   up to count bytes.  Any required
 *                   transformations are applied.
 *
 *                  WARNING:
 *                    This function overwrites the data at
 *                    pMetricsSrc.  If this data is
 *                    valuable, it should be copied before
 *                    entry to here.
 *
 * INPUT         = (hDC,pMetricsDest,pMetricsSrc,usCount,fScale,pDDC,
 *                  fusComFlags,fusRetMode)
 *
 *                  pMetricsDest- Dest Metrics
 *                  pMetricsScr - Source Metrics
 *                  usCount     - Sizeof FM
 *                  fScale      - TRUE = Scale the FM
 *                  fusComFlags - COM_FLAGS of the calling function
 *                  fusRetMode  - Mode flags for cpxfm_metrics
 *                            CPXFM_RET_WORLD   0x0000
 *                                Function should return World  FM
 *                            CPXFM_RET_DEVICE  0x0001
 *                                Function should return Device FM
 *
 * OUTPUT        = NONE
 *
 *
 * RETURN-NORMAL = Returns TRUE if copy & transforms done correctly,
 *                 else FALSE.
 *
 *
 * RETURN-ERROR  = FALSE
 *
 **************************************************************************/

BOOL cpxfm_metrics(HDC hDC, PFONTMETRICS pMetricsDest,
                         PFONTMETRICS pMetricsSrc,USHORT usCount,
                         BOOL fScale,PDDC pDDC,USHORT fusComFlags,
                         USHORT fusRetMode)

 /* PFONTMETRICS pMetricsDest;            Where output goes                 */
 /* PFONTMETRICS pMetricsSrc;             Where data comes from             */
 /* USHORT usCount;                       Number of bytes to transfer       */
 /* BOOL fScale;                          Whether to (Scale) or not         */
 /* PDDC pDDC;                            Access to heap                    */
 /* BOOL fusComFlags;                     COM_FLAGS transform or not        */

{

   /*
   ** * A basic copy function, but with two complications. First is that *
   ** the destination size is passed, so the whole structure may not * need
   ** to be copied. Second, the output needs to have its values * in world
   ** coordinates, and the input is in device coordinates, * so some
   ** transformations need to be made.
   */

  PPDEVICE pPDevice = pDDC->pPDevice;
  PBYTE pFrom,pTo;
  FONTXFORM fxData;
  XFORM xfmCell;                       /* Transform font to world space     */
  USHORT usNumBytes;
  LONG   lWorldDefCharWidth;
  LONG   lWorldDefCharHeight;


  /*
  ** The tedious part - performing the transform
  */
  fxData.ptlZero.x = 0;
  fxData.ptlZero.y = 0;
  fxData.ptlEmHeight.x = 0;
  fxData.ptlEmHeight.y = pMetricsSrc->lEmHeight;
  fxData.ptlXHeight.x = 0;
  fxData.ptlXHeight.y = pMetricsSrc->lXHeight;
  fxData.ptlMaxAscender.x = 0;
  fxData.ptlMaxAscender.y = pMetricsSrc->lMaxAscender;
  fxData.ptlMaxDescender.x = 0;
  fxData.ptlMaxDescender.y = pMetricsSrc->lMaxDescender;
  fxData.ptlLowerCaseAscent.x = 0;
  fxData.ptlLowerCaseAscent.y = pMetricsSrc->lLowerCaseAscent;
  fxData.ptlLowerCaseDescent.x = 0;
  fxData.ptlLowerCaseDescent.y = pMetricsSrc->lLowerCaseDescent;
  fxData.ptlInternalLeading.x = 0;
  fxData.ptlInternalLeading.y = pMetricsSrc->lInternalLeading;
  fxData.ptlExternalLeading.x = 0;
  fxData.ptlExternalLeading.y = pMetricsSrc->lExternalLeading;
  fxData.ptlAveCharWidth.x = pMetricsSrc->lAveCharWidth;
  fxData.ptlAveCharWidth.y = 0;
  fxData.ptlMaxCharInc.x = pMetricsSrc->lMaxCharInc;
  fxData.ptlMaxCharInc.y = 0;
  fxData.ptlEmInc.x = pMetricsSrc->lEmInc;
  fxData.ptlEmInc.y = 0;
  fxData.ptlMaxBaselineExt.x = 0;
  fxData.ptlMaxBaselineExt.y = pMetricsSrc->lMaxBaselineExt;

  /*
  ** The real points - subsrcipt/superscript information
  */
  fxData.ptlSubscriptSize.x   = pMetricsSrc->lSubscriptXSize;
  fxData.ptlSubscriptSize.y   = pMetricsSrc->lSubscriptYSize;
  fxData.ptlSubscriptOffset.x = pMetricsSrc->lSubscriptXOffset;
  fxData.ptlSubscriptOffset.y = pMetricsSrc->lSubscriptYOffset;
  fxData.ptlSuperscriptSize.x = pMetricsSrc->lSuperscriptXSize;
  fxData.ptlSuperscriptSize.y = pMetricsSrc->lSuperscriptYSize;
  fxData.ptlSuperscriptOffset.x = pMetricsSrc->lSuperscriptXOffset;
  fxData.ptlSuperscriptOffset.y = pMetricsSrc->lSuperscriptYOffset;

  /*
  ** Finally, 4 Y values
  */
  fxData.ptlUnderscoreSize.x = 0;
  fxData.ptlUnderscoreSize.y = pMetricsSrc->lUnderscoreSize;
  fxData.ptlUnderscorePosition.x = 0;
  fxData.ptlUnderscorePosition.y = pMetricsSrc->lUnderscorePosition;
  fxData.ptlStrikeoutSize.x = 0;
  fxData.ptlStrikeoutSize.y = pMetricsSrc->lStrikeoutSize;
  fxData.ptlStrikeoutPosition.x = 0;
  fxData.ptlStrikeoutPosition.y = pMetricsSrc->lStrikeoutPosition;

  /*
  ** * Need to transform - this is a vector font, so the only transform *
  ** of interest is the cell transform. This is defined as: * CellXform.M11
  ** = sizfxCell.x / xDeviceRes; * CellXform.M22 = sizfxCell.y /
  ** yDeviceRes; * This should be applied to the array of POINTLs that was
  ** just * produced.
  */

  lWorldDefCharHeight = pPDevice->lCapsDefCharHeight;
  lWorldDefCharWidth  = pPDevice->lCapsDefCharWidth;
  if (fusComFlags & HIUSHORT(COM_TRANSFORM))
  {
    /*
    ** Convert the default char box to world while accounting for
    ** rotation. We convert the device char box to world to establish  MV
    ** the ratio to the new world char box.
    */
    ConvertWithRotation(hDC, pDDC, FALSE, &lWorldDefCharWidth,
                        &lWorldDefCharHeight);
  }

  xfmCell.fxM11 = pDDC->DCState.dcbnd.cbnd.sizfxCell.cx /
                                           lWorldDefCharWidth;
                                        // pMetricsSrc->sXDeviceRes;
  xfmCell.fxM22 = pDDC->DCState.dcbnd.cbnd.sizfxCell.cy /
                                           lWorldDefCharHeight;
                                        // pMetricsSrc->sYDeviceRes;
  xfmCell.fxM12 = MAKEFIXED(0, 0);
  xfmCell.fxM21 = MAKEFIXED(0, 0);
  xfmCell.lM41  = 0;
  xfmCell.lM42  = 0;

  /*
  ** Must ignore the sign of the cell size data
  */
  if (xfmCell.fxM11 < 0)
    xfmCell.fxM11 = -xfmCell.fxM11;

  if (xfmCell.fxM22 < 0)
    xfmCell.fxM22 = -xfmCell.fxM22;

  if (fScale)
  {
    if (!(BOOL)(*daConvertWithMatrix)(hDC, (PPOINTL)&fxData,
                                      (LONG)FONT_XFORM_POINTS,
                                      (PXFORM)&xfmCell,
                                      pDDC,
                                      (LONG)NGreConvertWithMatrix))
    {
      /*
      ** Transform failed - return the     news
      */
      return  FALSE;
    }
  }

  /*
  ** Return data in world format if necessary
  ** Note the caller may want device coordinates
  ** even if COM_TRANSFORM is set. This is
  ** useful when working with fonts
  ** in device coordinates.
  */
  if (!(fusRetMode & CPXFM_RET_DEVICE) &&
      (fusComFlags & HIUSHORT(COM_TRANSFORM)))
  {
    // had the rotation to 0 bug
    //convert_device_to_world(hDC, (PPOINTL)&fxData,
    //                             (LONG)FONT_XFORM_POINTS,
    //                              pDDC);
    /*
    ** Convert the points to world while watching out for rotation
    */
    PPOINTL ppointl = (PPOINTL)&fxData;
    LONG lCount;
    for (lCount = 0;lCount < FONT_XFORM_POINTS; lCount++ )
    {
      ConvertWithRotation(hDC, pDDC, FALSE, &ppointl->x,
                          &ppointl->y);
      ppointl++;
    } /* endfor */
  }

  /*
  ** Copy back to source matrix before copying to destination
  */
  pMetricsSrc->lEmHeight = fxData.ptlEmHeight.y-fxData.ptlZero.y;
  pMetricsSrc->lXHeight  = fxData.ptlXHeight.y-fxData.ptlZero.y;
  pMetricsSrc->lMaxAscender  = fxData.ptlMaxAscender.y-fxData.ptlZero.y;
  pMetricsSrc->lMaxDescender = fxData.ptlMaxDescender.y-fxData.ptlZero.y;
  pMetricsSrc->lLowerCaseAscent  = fxData.ptlLowerCaseAscent.y-fxData.ptlZero.y;
  pMetricsSrc->lLowerCaseDescent = fxData.ptlLowerCaseDescent.y-
                                       fxData.ptlZero.y;
  pMetricsSrc->lInternalLeading = fxData.ptlInternalLeading.y-fxData.ptlZero.y;
  pMetricsSrc->lExternalLeading = fxData.ptlExternalLeading.y-fxData.ptlZero.y;
  pMetricsSrc->lAveCharWidth = fxData.ptlAveCharWidth.x-fxData.ptlZero.x;
  pMetricsSrc->lMaxCharInc = fxData.ptlMaxCharInc.x-fxData.ptlZero.x;
  pMetricsSrc->lEmInc = fxData.ptlEmInc.x-fxData.ptlZero.x;
  pMetricsSrc->lMaxBaselineExt = fxData.ptlMaxBaselineExt.y-fxData.ptlZero.y;

  /*
  ** The real points - subsrcipt/superscript information
  */
  pMetricsSrc->lSubscriptXSize = fxData.ptlSubscriptSize.x-fxData.ptlZero.x;
  pMetricsSrc->lSubscriptYSize = fxData.ptlSubscriptSize.y-fxData.ptlZero.y;
  pMetricsSrc->lSubscriptXOffset = fxData.ptlSubscriptOffset.x-
                                       fxData.ptlZero.x;
  pMetricsSrc->lSubscriptYOffset = fxData.ptlSubscriptOffset.y-
                                       fxData.ptlZero.y;
  pMetricsSrc->lSuperscriptXSize = fxData.ptlSuperscriptSize.x-
                                       fxData.ptlZero.x;
  pMetricsSrc->lSuperscriptYSize = fxData.ptlSuperscriptSize.y-
                                       fxData.ptlZero.y;
  pMetricsSrc->lSuperscriptXOffset = fxData.ptlSuperscriptOffset.x-
                                       fxData.ptlZero.x;
  pMetricsSrc->lSuperscriptYOffset = fxData.ptlSuperscriptOffset.y-
                                       fxData.ptlZero.y;

  /*
  ** Finally, 4 Y values
  */
  pMetricsSrc->lUnderscoreSize = fxData.ptlUnderscoreSize.y-fxData.ptlZero.y;
  pMetricsSrc->lUnderscorePosition = fxData.ptlUnderscorePosition.y-
                                         fxData.ptlZero.y;
  pMetricsSrc->lStrikeoutSize = fxData.ptlStrikeoutSize.y-fxData.ptlZero.y;
  pMetricsSrc->lStrikeoutPosition = fxData.ptlStrikeoutPosition.y-
                                        fxData.ptlZero.y;

  /*
  ** Finally have the data, so copy as much as is required
  */
  pFrom = (PBYTE)pMetricsSrc;
  pTo = (PBYTE)pMetricsDest;

  /*
  **            PTR B719977
  */
  usNumBytes = MIN(usCount, (USHORT)sizeof(FONTMETRICS));

  /*
  ** fill in the portion of our font metrics they asked for -- MarkV
  */
  CopyMem(pTo, pFrom, usNumBytes);

  /*
  ** fill the portion beyond our metrics with 0's (if any)-- MarkV
  */
  while (++usNumBytes <= usCount)
    pTo[usNumBytes] = 0;

  return  TRUE;
}

/***************************************************************************
 *
 * FUNCTION NAME = DeviceQueryFontAttributes()
 *
 *
 * DESCRIPTION   = Return font metrics
 *
 *                 Returns the font metrics for the currently
 *                 selected font.  If this is an engine font, call
 *                 back.  Otherwise, copy to a local buffer then
 *                 transform the values from font notional to world
 *                 space.
 *
 *
 * INPUT         = hDC,SizeMetrics,pMetrics,pDDC,FunN
 *
 *
 * OUTPUT        = NONE
 *
 *
 *
 *
 * RETURN-NORMAL = bResult
 *
 *
 *
 * RETURN-ERROR  = NONE
 *
 *
 *
 **************************************************************************/

BOOL  DeviceQueryFontAttributes(HDC hDC,LONG SizeMetrics,
                                           PFONTMETRICS pMetrics,PDDC pDDC,
                                           ULONG FunN)

 /* LONG SizeMetrics;                     Number of bytes to return (per    */
 /*                                       struct)                           */
 /* PFONTMETRICS pMetrics;                Base of area to place data        */

{
  BOOL bResult;
  FONTMETRICS fmLocal;                 /* A copy of the original data       */

  if(!(EnterDriver(pDDC)))
    return(GPI_ERROR);

  /*
  **          Raise an error in the case of MEMORYDC
  */
  if (pDDC->usDCType == OD_MEMORY)
  {
    LeaveDriver( pDDC );
    return( GPI_ERROR );
  }
  FNTDBGMSG("\n\r\n\rPLOTTERS:  enter DeviceQueryFontAttributes");

  if (pDDC->DCState.dcbnd.cdef.fFlags & CDEF_GENERIC)
  {

    /*
    ** Not us, so get the engine working again
    */
    bResult = (BOOL)(*daDeviceQueryFontAttributes)(hDC,
                                                   SizeMetrics,
                                                   pMetrics,
                                                   pDDC,
                                                   FunN);
  }
  else
  {
    /*
    ** Local - copy to a temporary structure & transform
    */
    fmLocal = pDDC->pPDevice->fmDefaultFont;     /* Default font      */
    bResult = cpxfm_metrics(hDC, pMetrics, &fmLocal,
                            (USHORT)SizeMetrics,TRUE, pDDC, HIUSHORT(FunN),
                            CPXFM_RET_WORLD);
  }
  FNTDBGMSG("\n\rPLOTTERS:  exit DeviceQueryFontAttributes\n\r");

  LeaveDriver(pDDC);
  return  bResult;
}

/***************************************************************************
 *
 * FUNCTION NAME = DeviceQueryFonts
 *
 *
 * DESCRIPTION   = Return information about the plotter's built-in fonts.
 *
 *
 *
 * INPUT         = hDC,Options,pFacename,pMetrics,SizeMetrics,
 *                 pFonts,pDDC,FunN
 *
 *
 *
 * OUTPUT        = NONE
 *
 *
 *
 *
 * RETURN-NORMAL = lRetValue
 *
 *
 *
 * RETURN-ERROR  = NONE
 *
 *
 *
 **************************************************************************/

LONG  DeviceQueryFonts(HDC hDC,ULONG Options,PSZ pFacename,
                                  PFONTMETRICS pMetrics,LONG SizeMetrics,PLONG
                                  pFonts,PDDC pDDC,ULONG FunN)

 /* ULONG Options;                        Select private/public fonts       */
 /* PSZ pFacename;                        Facename to match, or null for    */
 /*                                         all                             */
 /* PFONTMETRICS pMetrics;                Area to place the data            */
 /* LONG SizeMetrics;                     Bytes per entry in the above      */
 /* PLONG pFonts;                         Number of fonts                   */
 /*                                       requested/returned                */

{
  LONG lRetValue;                      /* Value returned to caller          */
  LONG lFontsIn;                       /* Number caller wants               */
  FONTMETRICS fmLocal;                 /* A copy of the valuable
                                          information                       */

  if (!EnterDriver(pDDC))
      return(GPI_ERROR);
  /*
  **          Device fonts can't be realized for MEMORYDC
  */
  if (pDDC->usDCType == OD_MEMORY)
  {
    LeaveDriver( pDDC );
    return( 0L );
  }

  lFontsIn = *pFonts;
  *pFonts = 0L;                        /* Default answer                    */
  lRetValue = 0L;                      /* Default return value              */
  FNTDBGMSG("\n\r\n\rPLOTTERS:  enter DeviceQueryFonts");

  if (Options & QF_PUBLIC)
  {
    /*
    ** It is for us. ALL device fonts are public fonts, so * ignore
    ** calls for private fonts. Is it our font - either nameless OR our
    ** name?
    */
    if (pFacename == (PSZ)0 ||
        lstrcmp(pFacename, pDDC->pPDevice->fmDefaultFont.szFacename))
    {
      lRetValue = 1L;                  /* Only one font on plotter          */

      if (lFontsIn > 0)
      {
        /*
        ** Caller wants the data!
        */
        fmLocal = pDDC->pPDevice->fmDefaultFont;
        fmLocal.usCodePage = 0;        /* Specification                     */

        if (cpxfm_metrics(hDC, pMetrics, &fmLocal,
                          (USHORT)SizeMetrics, FALSE, pDDC, HIUSHORT(FunN),
                          CPXFM_RET_WORLD))
        {
          lRetValue = 0L;              /* Caller gets 'em all               */
          *pFonts = 1L;                /* Just to confirm it                */
        }
      }
    }
  }
  FNTDBGMSG("\n\rPLOTTERS:  exit DeviceQueryFonts\n\r");
  LeaveDriver(pDDC);
  return  lRetValue;
}

/***************************************************************************
 *
 * FUNCTION NAME = RealizeFont()
 *
 *
 * DESCRIPTION   = PROCESS DEVICE FONTS
 *
 *                 Performs 4 different functions.  1/ Realize a device font.         e
 *                 2/ Realiz an engine font as a device font.  3/ Delete a            e
 *                 device font.  4/ Delet an engine font realized as a device         e
 *                 font.                                                              e
 *                                                                                    e
 *
 *
 * INPUT         = (hDC,Command,pLogFont,Font,pDDC,FunN)
 *
 *
 *
 *
 * OUTPUT        = NONE
 *
 *
 *
 *
 * RETURN-NORMAL = lResult
 *
 *
 *
 * RETURN-ERROR  = NONE
 *
 *
 *
 **************************************************************************/

LONG  RealizeFont(HDC hDC,ULONG Command,PFATTRS pLogFont,ULONG Font,
                             PDDC pDDC,ULONG FunN)

 /* ULONG Command;                        Which particular operation is     */
 /*                                       desired                           */
 /* PFATTRS pLogFont;                     Font attributes for matching      */
 /* ULONG Font;                           Font handle, function sensitive   */

{
  LONG lResult;

  if (!EnterDriver(pDDC))
      return(GPI_ERROR);

  /*
  **          In case of MEMORYDC Dispatch to Graphics Engine
  */
  if (pDDC->usDCType == OD_MEMORY)
  {
    LeaveDriver( pDDC );
    return (Command == RF_DELETE_FONT ? GPI_ERROR : 0);
  }

  FNTDBGMSG("\n\r\n\rPLOTTERS:  enter RealizeFont");

  if (Command == REALIZE_FONT)
  {
    /*
    ** Return our own font
    */
    lResult = 0L;                      /* Default - no can do!              */

    if (pLogFont->lMatch <= 0)
    {
      /*
      ** Check if our font matches the request * An outline font is
      ** required. We can oblige if the Match * field is our value, OR it
      ** is zero and we match on font * characteristics. The only
      ** characteristic used is face name.
      */
      if (
        //(pLogFont->fsFontUse & FATTR_FONTUSE_OUTLINE) &&
          (pLogFont->lMatch == PLOTTER_FONT_MATCH ||
          (pLogFont->lMatch == 0 &&
           lstrcmp (pLogFont->szFacename,
                  pDDC->pPDevice->fmDefaultFont.szFacename))))
      {
        FNTDBGMSG("\n\rFont is realizable\n\r.");
        lResult = (LONG)(PFONTMETRICS)&pDDC->pPDevice->fmDefaultFont;
      }
#if      FONTDEBUG
      else
      {
        FNTDBGMSG("\n\rFont is not realizable.");
      }
#endif
    }
#if      FONTDEBUG
    else
    {
      FNTDBGMSG("\n\rpLogFont->lMatch is not acceptable");
    }
#endif
  }
  else
  {
    if (Command == REALIZE_ENGINE_FONT)
    {
      /*
      ** Use an engine font as a device font - cannot do this
      */
      FNTDBGMSG("\n\rPLOTTERS:  cannot realize engine fonts");
      lResult = 0L;
    }
    else
    {
      /*
      ** Delete a font - a nop for us.
      */
      FNTDBGMSG("\n\rDeleting a font...");
      lResult = GPI_OK;
    }
  }

  FNTDBGMSG("\n\rPLOTTERS:  exit RealizeFont\n\r");
  LeaveDriver(pDDC);
  return  lResult;
}

/***************************************************************************
 *
 * FUNCTION NAME = QueryTextBox
 *
 *
 * DESCRIPTION   = QUERY TEXT BOX FOR CHAR STRING
 *
 *                 query text box for the given char string and return the
 *                 coordinate of the text box
 *
 *                   1____________________3
 *                   ³                    ³
 *                   ³                    ³
 *                   ³                    ³
 *                 --³--------------------5----  <--- baseline
 *                   ³____________________³
 *                   2                    4
 *
 *                   1: TXTBOX_TOPLEFT
 *                   2: TXTBOX_BOTTOMLEFT
 *                   3: TXTBOX_TOPRIGHT
 *                   4: TXTBOX_BOTTOMRIGHT
 *                   5: TXTBOX_CONCAT
 *
 *
 * INPUT         = (hDC,cChars,pChars,cPoints,pPoints,pDDC,ulFunN)
 *
 * OUTPUT        = NONE
 *
 *
 *
 *
 * RETURN-NORMAL = GPI_OK;
 *
 *
 *
 * RETURN-ERROR  = GPI_ERROR;
 *
 *
 *
 **************************************************************************/

LONG  QueryTextBox(HDC hDC,ULONG cChars,PCH pChars,LONG cPoints,
                              PPOINTL pPoints,PDDC pDDC,ULONG ulFunN)
{

  /*
  ** Determine the size of the enclosing box around the text string *
  ** supplied to us in pChars. The plotter's internal font is a * fixed
  ** pitch font, so there is no great difficulty calculating!
  */

  PPOINTL pptlPosn;
  POINTL ptlCharSize;                  /* Long charsize                     */
  LONG lResult;                        /* What we return to caller          */
  LONG lxLeft,lxRight;                 /* Left and Right X coordinates      */
  LONG lyTop,lyBottom,lyBaseline;      /* Y positions                       */
  LONG lLeading;
  LONG i;
  XFORM xfmRot;                        /* Character direction               */
  ULONG ulRet;

  if(!(EnterDriver(pDDC)))
    return(GPI_ERROR);

  /*
  **          In case of MEMORYDC Dispatch to Graphics Engine
  */
  if (pDDC->usDCType == OD_MEMORY)
  {
    ulRet = (*daQueryTextBox) (hDC, cChars, pChars,
                                 cPoints, pPoints, pDDC, ulFunN );
    LeaveDriver( pDDC );
    return( ulRet );
  }

  /*
  ** If the font is not ours, just call back to the engine!
  */
  FNTDBGMSG("\n\r\n\rPLOTTERS:  enter QueryTextBox");

  if (pDDC->DCState.dcbnd.cdef.fFlags & CDEF_GENERIC)
  {
    /*
    ** Where's the engine?
    */
    lResult = (*daQueryTextBox)(hDC, cChars, pChars, cPoints, pPoints,
                                pDDC, ulFunN);
  }
  else
  {
    /*
    ** * We must do the work, so here goes. First step is to * calculate
    ** the limits of the rectangle. These are determined * by the
    ** character direction. Having calculated the rectangle * limits, then
    ** insert them into the caller's array.
    */
    lResult = GPI_OK;

    /*
    ** * NOTE: The "bugger" factors here are to adjust the cell size * for
    ** the shape of the characters: the default character size * for the
    ** plotters has an aspect ratio of 187:269 (W:H), so * reduce to apply
    ** this ratio.
    */
    //ptlCharSize.x = ((pDDC->DCState.dcbnd.cbnd.sizfxCell.cx / ASPECT_D) *
    //                             ASPECT_M + 32768) / 65536L;
    ptlCharSize.x = (pDDC->DCState.dcbnd.cbnd.sizfxCell.cx + 32768) / 65536L;
    ptlCharSize.y = (pDDC->DCState.dcbnd.cbnd.sizfxCell.cy + 32768) / 65536L;
    /*
    ** .65625 is 7/8 * 3/4
    ** to match the charbox scaling in textout.c set_cel_size
    ** 1/8 in for internal leading and 1/4 is for decenders
    */
    ptlCharSize.x = ptlCharSize.x * 65625L / 100000L;
    lLeading = ptlCharSize.y / 8L;
    ptlCharSize.y = ptlCharSize.y * 7L / 8L;
    lyBaseline = 0L;                   /* Most common value                 */

    switch (pDDC->DCState.dcbnd.cbnd.usDirection)
    {
      case  CHDIRN_LEFTRIGHT :         /* Usual english text                */
        lxLeft   = 0L;
        lxRight  = cChars * ptlCharSize.x;
        lyTop    = (ptlCharSize.y * 3/4) + lLeading; /* yMaxAscender       */
        lyBottom = -ptlCharSize.y/4;
        break;
      case  CHDIRN_RIGHTLEFT :         /* Backwards english text - arabic?  */
        lxLeft   = -cChars * ptlCharSize.x;
        lxRight  = 0L;
        lyTop    = (ptlCharSize.y * 3/4) + lLeading;;
        lyBottom = -ptlCharSize.y/4;
        break;
      case  CHDIRN_TOPBOTTOM :
        lxLeft   = 0L;
        lxRight  = ptlCharSize.x;
        lyTop    = 0L;
        lyBottom = -cChars * (ptlCharSize.y + lLeading) - (ptlCharSize.y/4);
        lyBaseline = -cChars * (ptlCharSize.y + lLeading);
        break;
      case  CHDIRN_BOTTOMTOP :         /* Going up                          */
        lxLeft   = 0L;
        lxRight  = ptlCharSize.x;
        lyTop    = cChars * (ptlCharSize.y + lLeading);
        lyBottom = -ptlCharSize.y/4;
        break;
    }
    pptlPosn = pPoints;

    /*
    ** Corners calculated, so onto the array.
    */
    for (i = 0; i < cPoints; i++, pptlPosn++)
    {
      switch (i)
      {
        case  TXTBOX_TOPLEFT :
          pptlPosn->x = lxLeft;
          pptlPosn->y = lyTop;
          break;
        case  TXTBOX_BOTTOMLEFT :
          pptlPosn->x = lxLeft;
          pptlPosn->y = lyBottom;
          break;
        case  TXTBOX_TOPRIGHT :
          pptlPosn->x = lxRight;
          pptlPosn->y = lyTop;
          break;
        case  TXTBOX_BOTTOMRIGHT :
          pptlPosn->x = lxRight;
          pptlPosn->y = lyBottom;
          break;
        case  TXTBOX_CONCAT :
          if (pDDC->DCState.dcbnd.cbnd.usDirection == CHDIRN_RIGHTLEFT)
            pptlPosn->x = lxLeft;
          else
            pptlPosn->x = lxRight;
          pptlPosn->y = lyBaseline;
          break;
      }                                /* end switch                        */
    }
    pptlPosn = pPoints;

    /*
    ** Now compute the TextAlignment
    */

    if(pDDC->DCState.dcbnd.cbnd.usTextAlign != (TA_NORMAL_HORIZ
                                               | TA_NORMAL_VERT))
    {
      LONG   lShift;
      USHORT usDirection = pDDC->DCState.dcbnd.cbnd.usDirection;
      USHORT usTextAlign = GetStandardAlignment(
                           pDDC->DCState.dcbnd.cbnd.usTextAlign, usDirection);

      /*
      ** First get the Horizontal alignment shift
      */
      lShift = GetHorizTextAlignShift(lxLeft, lxRight, lyTop,
                                      lyBottom, usTextAlign, usDirection);
      /*
      ** Add the shift to the x values of the Text Box
      */
      for (i = 0; i < cPoints; i++, pptlPosn++)
      {
        switch (i)
        {
          case  TXTBOX_TOPLEFT :
            pptlPosn->x += lShift;
            break;
          case  TXTBOX_BOTTOMLEFT :
            pptlPosn->x += lShift;
            break;
          case  TXTBOX_TOPRIGHT :
            pptlPosn->x += lShift;
            break;
          case  TXTBOX_BOTTOMRIGHT :
            pptlPosn->x += lShift;
            break;
          case  TXTBOX_CONCAT :
            pptlPosn->x += lShift;
            break;
        }
      }   /* end for loop               */
      pptlPosn = pPoints;

      /*
      ** Now get the Vertical alignment shifts
      */
      lShift = GetVertTextAlignShift(lxLeft, lxRight, lyTop, lyBottom,
                                     usTextAlign, usDirection);
      /*
      ** Add the shifts to the y values of the Text Box
      */
      for (i = 0; i < cPoints; i++, pptlPosn++)
      {
        switch (i)
        {
          case  TXTBOX_TOPLEFT :
            pptlPosn->y += lShift;
            break;
          case  TXTBOX_BOTTOMLEFT :
            pptlPosn->y += lShift;
            break;
          case  TXTBOX_TOPRIGHT :
            pptlPosn->y += lShift;
            break;
          case  TXTBOX_BOTTOMRIGHT :
            pptlPosn->y += lShift;
            break;
          case  TXTBOX_CONCAT :
            pptlPosn->y += lShift;
            break;
        }
      }     /* end for loop            */
      pptlPosn = pPoints;
      /*
      ** End TextAlignment calculations
      */
    }


    /*
    ** Now compute fxExtra and fxBreakExtra
    */
    if((pDDC->DCState.dcbnd.cbnd.fxExtra != 0) ||
      (pDDC->DCState.dcbnd.cbnd.fxBreakExtra != 0))
    {
      FIXED    fxExtraX;
      FIXED    fxExtraY;
      FIXED    fxBreakExtraX;
      FIXED    fxBreakExtraY;
      FIXED    fxExtraErrorX;
      FIXED    fxExtraErrorY;
      FIXED    fxExtraSumX   = 0;
      FIXED    fxExtraSumY   = 0;
      LONG      i            = 0;
      LONG      cChars2      = 0;
      ULONG     ulChar;
      PPOINTL   pExtraPoints = pPoints;

      /*
      ** Calculate the amount to increment each character
      */
      GetExtraIncrements(pDDC,
                         ulFunN,
                         &pExtraPoints[TXTBOX_CONCAT],
                         &fxExtraX,
                         &fxExtraY,
                         &fxBreakExtraX,
                         &fxBreakExtraY,
                         &fxExtraErrorX,
                         &fxExtraErrorY);


      for(i = 0; i < (INT)cChars; pChars += (INT)cChars2, i += (INT)cChars2)
      {
         /*
         ** assume SBCS
         */
         cChars2   = 1;

         ulChar = (USHORT)*pChars;
         if(ulChar == ' ')
         {
            fxExtraSumX += fxBreakExtraX;
            fxExtraSumY += fxBreakExtraY;
         }
         /*
         ** Accumulate the extra increment
         */
         fxExtraSumX += fxExtraX;
         fxExtraSumY += fxExtraY;
      }
      /*
      ** Add acumulated Extra increment to text box
      */
      pExtraPoints[TXTBOX_CONCAT].x += (LONG)FIXEDINT(fxExtraSumX);
      pExtraPoints[TXTBOX_CONCAT].y += (LONG)FIXEDINT(fxExtraSumY);

      switch(pDDC->DCState.dcbnd.cbnd.usDirection)
      {
      case CHDIRN_LEFTRIGHT:
         pExtraPoints[TXTBOX_BOTTOMRIGHT].x += (LONG)FIXEDINT(fxExtraSumX);
         pExtraPoints[TXTBOX_BOTTOMRIGHT].y += (LONG)FIXEDINT(fxExtraSumY);
         pExtraPoints[TXTBOX_TOPRIGHT].x    += (LONG)FIXEDINT(fxExtraSumX);
         pExtraPoints[TXTBOX_TOPRIGHT].y    += (LONG)FIXEDINT(fxExtraSumY);
         break;
      case CHDIRN_RIGHTLEFT:
         pExtraPoints[TXTBOX_BOTTOMLEFT].x  += (LONG)FIXEDINT(fxExtraSumX);
         pExtraPoints[TXTBOX_BOTTOMLEFT].y  += (LONG)FIXEDINT(fxExtraSumY);
         pExtraPoints[TXTBOX_TOPLEFT].x     += (LONG)FIXEDINT(fxExtraSumX);
         pExtraPoints[TXTBOX_TOPLEFT].y     += (LONG)FIXEDINT(fxExtraSumY);
         break;
      case CHDIRN_TOPBOTTOM:
         pExtraPoints[TXTBOX_BOTTOMLEFT].x  += (LONG)FIXEDINT(fxExtraSumX);
         pExtraPoints[TXTBOX_BOTTOMLEFT].y  += (LONG)FIXEDINT(fxExtraSumY);
         pExtraPoints[TXTBOX_BOTTOMRIGHT].x += (LONG)FIXEDINT(fxExtraSumX);
         pExtraPoints[TXTBOX_BOTTOMRIGHT].y += (LONG)FIXEDINT(fxExtraSumY);
         break;
      case CHDIRN_BOTTOMTOP:
         pExtraPoints[TXTBOX_TOPLEFT].x     += (LONG)FIXEDINT(fxExtraSumX);
         pExtraPoints[TXTBOX_TOPLEFT].y     += (LONG)FIXEDINT(fxExtraSumY);
         pExtraPoints[TXTBOX_TOPRIGHT].x    += (LONG)FIXEDINT(fxExtraSumX);
         pExtraPoints[TXTBOX_TOPRIGHT].y    += (LONG)FIXEDINT(fxExtraSumY);
         break;
      }
    }
    /*
    ** We are done with Extra increments
    */
    /*
    ** At this point, all our coordinates are in world space, origin
    ** based.  Apply any rotation that may be required.
    */

    if (get_angle_xform(&xfmRot, &pDDC->DCState.dcbnd.cbnd.ptlAngle))
    {

      /*
      ** Text rotation is in force - apply the rotation now
      */
      if (!(BOOL)(*daConvertWithMatrix)(hDC, (PPOINTL)pPoints,
                                        cPoints, (PXFORM)&xfmRot,
                                        pDDC, (LONG)NGreConvertWithMatrix))
      {
        LeaveDriver(pDDC);
        return  GPI_ERROR;
      }
    }
  }

  /*
  **  7/31/91   Engine fonts now return here
  */
  FNTDBGMSG("\n\rPLOTTERS:  exit QueryTextBox\n\r");

  LeaveDriver(pDDC);
  return  lResult;
}

/***************************************************************************
 *
 * FUNCTION NAME = QueryWidthTable
 *
 *
 * DESCRIPTION   = Return width info for font
 *
 *                 Returns information about the width
 *                 of characters in the currently
 *                 selected font.  If the current font
 *                 is the plotters hardware font,
 *                 information is returned from here,
 *                 otherwise the call is passed onto
 *                 the engine, which knows much more
 *                 about fonts than here.
 *
 * INPUT         = (hDC,lFirstChar,cCount,pWidthTable,pDDC,
 *                  ulFunN)
 *
 *
 *
 * OUTPUT        = NONE
 *
 *
 *
 *
 * RETURN-NORMAL = GPI_OK    - all is well
 *
 *
 *
 * RETURN-ERROR  = GPI_ERROR - could not do i
 *
 *
 *
 **************************************************************************/

BOOL  QueryWidthTable(HDC hDC,LONG lFirstChar,LONG cCount,PLONG
                                 pWidthTable,PDDC pDDC,ULONG ulFunN)

 /* LONG lFirstChar;                      First character for which to      */
 /*                                       return data                       */
 /* LONG cCount;                          Number of characters to return    */
 /* PLONG pWidthTable;                    Area where the output is placed   */

{

  /*
  ** * Function is easy to implement, as the plotter's hardware font * is
  ** fixed pitch, so basically the width is constant. Only two *
  ** complications - one is to decide whether to call the engine, the *
  ** other being to validate the range of characters requested.
  */

  LONG lxIncr;                         /* X increment for all characters!   */


  if(!(EnterDriver(pDDC)))
    return(GPI_ERROR);

  /*
  **          In case of MEMORYDC Return Error
  */
  if (pDDC->usDCType == OD_MEMORY)
  {
    LeaveDriver( pDDC );
    return( 0L );
  }

  if (pDDC->DCState.dcbnd.cdef.fFlags&CDEF_GENERIC)
  {
    /*
    ** Somebody else - aim it at the engine
    */
    LeaveDriver(pDDC);
    return (BOOL)(*daQueryWidthTable)(hDC, lFirstChar, cCount,
                                      pWidthTable, pDDC, ulFunN);
  }

  /*
  ** Are the parameters OK?
  */
  if (lFirstChar < 0 || lFirstChar > 255 || cCount < 0 || cCount > 256)
  {

    /*
    ** No good - too early or too late
    */
    GplErrSetWarning(PMERR_INV_LENGTH_OR_COUNT);
    LeaveDriver(pDDC);
    return  GPI_ERROR;
  }


  if ((lxIncr = pDDC->DCState.dcbnd.cbnd.sizfxCell.cx) < 0)
    lxIncr = -lxIncr;                  /* Only positive values!             */

  /*
  ** Apply rounding and scaling
  */
  //lxIncr = ((lxIncr/ASPECT_D)*ASPECT_M+32768)/65536L;
  lxIncr = (lxIncr+32768)/65536L;
  /*
  ** .65625 is 7/8 * 3/4
  ** to match the charbox scaling in textout.c set_cel_size
  */
  lxIncr = lxIncr * 65625L/ 100000L;

  while (cCount-- > 0)
    *pWidthTable++ = lxIncr;           /* They are all the same             */

  LeaveDriver(pDDC);
  return  GPI_OK;
}

/***************************************************************************
 *
 * FUNCTION NAME = QueryCharPositions()
 *
 *
 * DESCRIPTION   = Determine the origin of char
 *
 *                 Determine the origin of each character
 *                 in the given string Adjusts for
 *                 character direction etc.
 *
 *
 * INPUT         = (hDC,pStartXY,ulOptions,cChars,pChars,pDX,
 *                  pPosn,pDDC,ulFunN)
 *
 *
 *
 * OUTPUT        = NONE
 *
 *
 *
 *
 * RETURN-NORMAL =  GPI_OK
 *
 *
 *
 * RETURN-ERROR  = NONE
 *
 *
 *
 **************************************************************************/

LONG  QueryCharPositions(HDC hDC,PPOINTL pStartXY,ULONG ulOptions,
                                    LONG cChars,PCH pChars,PLONG pDX,PPOINTL
                                    pPosn,PDDC pDDC,ULONG ulFunN)

 /* PPOINTL pStartXY;                     Optional starting position        */
 /* ULONG ulOptions;                      Control flags - what we do        */
 /* LONG cChars;                          Number of chars in following      */
 /* PCH pChars;                           The charactes of interest         */
 /*                                        (ignored here)                   */
 /* PLONG pDX;                            Increment vector - distance to    */
 /*                                       next char                         */
 /* PPOINTL pPosn;                        Vector (cChars + 1) where output  */
 /*                                       goes                              */

{
  POINTL ptlStart;                     /* Effective starting address        */
  POINTL ptlInc;                       /* Increment from one position to
                                          next                              */
  PPOINTL pPosnIn;
  LONG Index;
  USHORT usDirection;                  /* Used inside loop - copy of
                                          charbundle                        */
  XFORM xfmRot;                        /* For text rotation transform       */
  ULONG ulRet;

  if(!(EnterDriver(pDDC)))
    return(GPI_ERROR);

  /*
  **          In case of MEMORYDC Dispatch to Graphics Engine
  */
  if (pDDC->usDCType == OD_MEMORY)
  {
    ulRet = (*daQueryCharPositions) (hDC, pStartXY,
                                       ulOptions, cChars,
                                       pChars, pDX,
                                       pPosn, pDDC, ulFunN );
    LeaveDriver( pDDC );
    return( ulRet );
  }

  if (pDDC->DCState.dcbnd.cdef.fFlags&CDEF_GENERIC)
  {

    /*
    ** Not our font, so call back to the engine
    */
    LeaveDriver(pDDC);
    return (*daQueryCharPositions)(hDC, pStartXY, ulOptions,
                                   cChars, pChars, pDX, pPosn,
                                   pDDC, ulFunN);
  }

  pPosnIn = pPosn;                     /* Keep it for later                 */
  usDirection = pDDC->DCState.dcbnd.cbnd.usDirection;

  if (ulOptions & CHS_VECTOR)
  {
    /*
    ** * A vector has been supplied in world coordinates. This makes *
    ** life very easy - we use the vector rather than the font data. * But
    ** character direction must be considered here.
    */
    ptlStart.x = pDDC->DCState.dcbnd.cbnd.usDirection ==
                    CHDIRN_RIGHTLEFT ? -*pDX : 0;
    ptlStart.y = pDDC->DCState.dcbnd.cbnd.usDirection ==
                    CHDIRN_TOPBOTTOM ? -*pDX : 0;

    for (Index = 0; Index <= (INT)cChars; ++Index, ++pPosn)
    {
      pPosn->x = ptlStart.x;
      pPosn->y = ptlStart.y;

      /*
      ** The hard part - figure out next position
      */
      switch (usDirection)
      {
        case  CHDIRN_LEFTRIGHT :       /* Normal operation                  */
          ptlStart.x += *pDX;
          break;
        case  CHDIRN_RIGHTLEFT :       /* Reverse of normal direction       */
          ptlStart.x -= *pDX;
          break;
        case  CHDIRN_TOPBOTTOM :       /* Going down                        */
          ptlStart.y -= *pDX;
          break;
        case  CHDIRN_BOTTOMTOP :       /* Going up                          */
          ptlStart.y += *pDX;
          break;
      }
      ++pDX;
    }
  }
  else
  {
    /*
    ** Use the font information to generate the desired data. There *
    ** will need to be a transformation to world coordinates after we *
    ** finish this operation, as the font data is in device coordinates.
    */
    ptlStart.x = 0;                    /* Start at origin - adjusted later  */
    ptlStart.y = 0;

    /*
    ** Determine char width/height - charcell dependent
    */

    ptlInc.x = (pDDC->DCState.dcbnd.cbnd.sizfxCell.cx + 32768L) / 65536L;

    /*
    ** .65625 is 7/8 * 3/4
    ** adj internal leading
    ** ptlInc.x = ptlInc.x * 7L / 8L;
    ** adj for decenders
    ** ptlInc.x = ptlInc.x * 3L / 4L;
    */
    ptlInc.x = ptlInc.x * 65625 / 100000L;

    ptlInc.y = (pDDC->DCState.dcbnd.cbnd.sizfxCell.cy + 32768L) / 65536L;

    switch (usDirection)
    {
      case  CHDIRN_LEFTRIGHT :         /* Normal, English text              */
        ptlInc.y = 0;                  /* Don't want this one               */
        break;
      case  CHDIRN_RIGHTLEFT :         /* Arabic                            */
        ptlInc.x = -ptlInc.x;
        ptlInc.y = 0;
        ptlStart.x = ptlInc.x;
        break;
      case  CHDIRN_TOPBOTTOM :
        ptlInc.x = 0;
        ptlInc.y = -ptlInc.y;
        ptlStart.y = 3 * ptlInc.y / 4;
        break;
      case  CHDIRN_BOTTOMTOP :
        ptlInc.x = 0;
        ptlStart.y = ptlInc.y / 4;
        break;
    }

      /*
      ** Generate the new values
      */

    for (Index = 0; Index <= (INT)cChars; ++Index, ++pPosn)
    {
      pPosn->x    = ptlStart.x;
      pPosn->y    = ptlStart.y;
      ptlStart.x += ptlInc.x;
      ptlStart.y += ptlInc.y;
    }
  }

  /*
  ** Now compute the TextAlignment
  */
  if(pDDC->DCState.dcbnd.cbnd.usTextAlign != (TA_NORMAL_HORIZ
                                             | TA_NORMAL_VERT))
  {
    PPOINTL  pptlAlignShift = pPosnIn;
    POINTL   ptlCharSize;
    LONG     lLeading,
             lyBaseline,
             lxLeft,
             lxRight,
             lyTop,
             lyBottom,
             lShift;
    USHORT   usTextAlign = GetStandardAlignment(
                           pDDC->DCState.dcbnd.cbnd.usTextAlign, usDirection);
    /*
    ** This code comes from QueryTextBox.  The text should be repositioned the
    ** same amount of as the Text Box, so I will use the calculations from the
    ** Text Box as to get the amount of shift to apply to the characters.
    */
    ptlCharSize.x = (pDDC->DCState.dcbnd.cbnd.sizfxCell.cx + 32768) / 65536L;
    ptlCharSize.y = (pDDC->DCState.dcbnd.cbnd.sizfxCell.cy + 32768) / 65536L;
    /*
    ** .65625 is 7/8 * 3/4
    ** to match the charbox scaling in textout.c set_cel_size
    ** 1/8 in for internal leading and 1/4 is for decenders
    */
    ptlCharSize.x = ptlCharSize.x * 65625L / 100000L;
    lLeading = ptlCharSize.y / 8L;
    ptlCharSize.y = ptlCharSize.y * 7L / 8L;
    lyBaseline = 0L;                   /* Most common value                 */
    switch (pDDC->DCState.dcbnd.cbnd.usDirection)
    {
      case  CHDIRN_LEFTRIGHT :         /* Usual english text                */
        lxLeft   = 0L;
        lxRight  = cChars * ptlCharSize.x;
        lyTop    = (ptlCharSize.y * 3/4) + lLeading; /* yMaxAscender       */
        lyBottom = -ptlCharSize.y/4;
        break;
      case  CHDIRN_RIGHTLEFT :         /* Backwards english text - arabic?  */
        lxLeft   = -cChars * ptlCharSize.x;
        lxRight  = 0L;
        lyTop    = (ptlCharSize.y * 3/4) + lLeading;;
        lyBottom = -ptlCharSize.y/4;
        break;
      case  CHDIRN_TOPBOTTOM :
        lxLeft   = 0L;
        lxRight  = ptlCharSize.x;
        lyTop    = 0L;
        lyBottom = -cChars * (ptlCharSize.y + lLeading) - (ptlCharSize.y/4);
        lyBaseline = -cChars * (ptlCharSize.y + lLeading);
        break;
      case  CHDIRN_BOTTOMTOP :         /* Going up                          */
        lxLeft   = 0L;
        lxRight  = ptlCharSize.x;
        lyTop    = cChars * (ptlCharSize.y + lLeading);
        lyBottom = -ptlCharSize.y/4;
        break;
    }
    /*
    ** Get the Horizontal alignment shift
    */
    lShift = GetHorizTextAlignShift(lxLeft, lxRight, lyTop,
                                    lyBottom, usTextAlign, usDirection);
    /*
    ** Add the shift to the x values in the position vector
    */
    for (Index = 0; Index <= (INT)cChars; ++Index, ++pptlAlignShift)
    {
      pptlAlignShift->x = pptlAlignShift->x + lShift;
    }
    pptlAlignShift = pPosnIn;

    /*
    ** Get the Vertical alignment shift
    */
    lShift = GetVertTextAlignShift(lxLeft, lxRight, lyTop,
                                   lyBottom, usTextAlign, usDirection);
    /*
    ** Add the shift to the y values in the position vector
    */
    for (Index = 0; Index <= (INT)cChars; Index++, pptlAlignShift++)
    {
      pptlAlignShift->y = pptlAlignShift->y + lShift;
    }
    pptlAlignShift = pPosnIn;
  }

  /*
  ** Now compute fxExtra and fxBreakExtra
  */
  if((pDDC->DCState.dcbnd.cbnd.fxExtra != 0) ||
    (pDDC->DCState.dcbnd.cbnd.fxBreakExtra != 0))
  {
    POINTL   ptlDirectionVector;
    FIXED    fxExtraX;
    FIXED    fxExtraY;
    FIXED    fxBreakExtraX;
    FIXED    fxBreakExtraY;
    FIXED    fxExtraErrorX;
    FIXED    fxExtraErrorY;
    FIXED    fxExtraSumX  = 0;
    FIXED    fxExtraSumY  = 0;
    LONG      i           = 0;
    LONG      cChars2     = 0;
    ULONG    ulChar;
    PPOINTL  pExtraPosn = pPosnIn;

    ptlDirectionVector.x = pExtraPosn[cChars].x - pExtraPosn[0].x;
    ptlDirectionVector.y = pExtraPosn[cChars].y - pExtraPosn[0].y;
    /*
    ** Calculate the amount of Extra increment
    */
    GetExtraIncrements(pDDC,
                       ulFunN,
                       &ptlDirectionVector,
                       &fxExtraX,
                       &fxExtraY,
                       &fxBreakExtraX,
                       &fxBreakExtraY,
                       &fxExtraErrorX,
                       &fxExtraErrorY);


    for(i = 0; i < (INT)cChars; pChars += (INT)cChars2, i += (INT)cChars2)
    {
       /*
       ** assume SBCS
       */
       cChars2   = 1;

       ulChar = (USHORT)*pChars;
       /*
       ** Is this the break char
       */
       if(ulChar == ' ')
       {
          fxExtraSumX += fxBreakExtraX;
          fxExtraSumY += fxBreakExtraY;
       }
       /*
       ** Accumulate the Extra increments
       */
       fxExtraSumX += fxExtraX;
       fxExtraSumY += fxExtraY;

       fxExtraSumX = (ptlDirectionVector.x > 0L)
            ? MAX(fxExtraSumX,
                  MAKEFIXED((SHORT)(pExtraPosn[i].x-pExtraPosn[i+1].x),0))
            : MIN(fxExtraSumX,
                  MAKEFIXED((SHORT)(pExtraPosn[i].x-pExtraPosn[i+1].x),0));
       fxExtraSumY = (ptlDirectionVector.y > 0L)
            ? MAX(fxExtraSumY,
                  MAKEFIXED((SHORT)(pExtraPosn[i].y-pExtraPosn[i+1].y),0))
            : MIN(fxExtraSumY,
                  MAKEFIXED((SHORT)(pExtraPosn[i].y-pExtraPosn[i+1].y),0));
       /*
       ** Add the accumulated extra increment to the character positions
       */
       pExtraPosn[i + 1].x += (LONG)FIXEDINT(fxExtraSumX);
       pExtraPosn[i + 1].y += (LONG)FIXEDINT(fxExtraSumY);

    /*
    ** Compute new error term for text spacing.
    ** Put the error term in DC temporarily so CharStringPos can use it to
    ** set the text spacing error.  We could be in trouble if more than one
    ** thread access it.
    */

     }
     {
        FXQUAD fxqSqRoot;

        FXToFXQuad(&fxqSqRoot, FX_FXMulFX(FRAC_FX(fxExtraSumX),
                                                   FRAC_FX(fxExtraSumX))
                            + FX_FXMulFX(FRAC_FX(fxExtraSumY),
                                                   FRAC_FX(fxExtraSumY)));
        pDDC->fxTempExtraError = (FIXED)quad_square_root((QUAD *)&fxqSqRoot);
     }
  }
  /*
  ** We are now done with Extra increments
  */
  /*
  ** At this point, all our coordinates are in world space, origin based.
  ** Apply any rotation that may be required.
  */
  if (get_angle_xform(&xfmRot, &pDDC->DCState.dcbnd.cbnd.ptlAngle))
  {
    /*
    ** Text rotation is in force - apply the rotation now
    */
    if (!(BOOL)(*daConvertWithMatrix)(hDC, (PPOINTL)pPosnIn,
                                      cChars+1L, (PXFORM)&xfmRot,
                                      pDDC, (LONG)NGreConvertWithMatrix))
    {
      LeaveDriver(pDDC);
      return  GPI_ERROR;
    }
  }


  if (ulOptions&CHS_START_XY)
  {
    /*
    ** Start position supplied - use it
    */
    ptlStart = *pStartXY;
  }
  else
  {
    /*
    ** Get the values in WORLD coordinates
    */
    GetCurrentPosition(hDC, &ptlStart, pDDC,
                       MAKEULONG(NGreGetCurrentPosition, HIUSHORT(ulFunN)));
  }

  /*
  ** Add the starting position to the values we have.
  */
  for (Index = 0; Index <= (INT)cChars; ++Index, ++pPosnIn)
  {
    pPosnIn->x += ptlStart.x;
    pPosnIn->y += ptlStart.y;
  }

  LeaveDriver(pDDC);
  return  GPI_OK;                      /* AOK                               */
}

/***************************************************************************
 *
 * FUNCTION NAME = GetExtraIncrements
 *
 *
 * DESCRIPTION   = Calculate the amount to increment space between
 *                 each character in a string.  The app must use
 *                 GpiSetCharExtra or GpiSetCharBreakExtra to enable
 *                 the extra spacing.
 *
 *
 * INPUT         = PDDC    pDDC
 *                 ULONG   ulFunN
 *                 PPOINTL pptlDirectionVector
 *                 PFIXED  pfxExtraX
 *                 PFIXED  pfxExtraY
 *                 PFIXED  pfxBreakExtraX
 *                 PFIXED  pfxBreakExtraY
 *                 PFIXED  pfxExtraErrorX
 *                 PFIXED  pfxExtraErrorY
 *
 *
 *
 * OUTPUT        = Returns amount of increment in
 *                 pfxExtraX, pfxExtraY, pfxBreakExtraX and pfxBreakExtraY
 *
 *
 *
 *
 * RETURN-NORMAL = NONE
 *
 *
 *
 * RETURN-ERROR  = NONE
 *
 *
 *
 **************************************************************************/

VOID GetExtraIncrements(PDDC    pDDC,
                        ULONG   ulFunN,
                        PPOINTL pptlDirectionVector,
                        PFIXED  pfxExtraX,
                        PFIXED  pfxExtraY,
                        PFIXED  pfxBreakExtraX,
                        PFIXED  pfxBreakExtraY,
                        PFIXED  pfxExtraErrorX,
                        PFIXED  pfxExtraErrorY)
{
   LONG  lVectorLength;
   FIXED fxExtraError,
         fxScaleX,
         fxScaleY;

   lVectorLength = (LONG)FindVectorLength(pptlDirectionVector);

   if(lVectorLength == 0)
   {
      *pfxExtraX      = 0;
      *pfxExtraY      = 0;
      *pfxBreakExtraX = 0;
      *pfxBreakExtraY = 0;
      *pfxExtraErrorX = 0;
      *pfxExtraErrorY = 0;
   }
   else
   {

      fxExtraError    = GreGetExtraError((HDC)pDDC);
      fxScaleX        = L_LDivFx(pptlDirectionVector->x, lVectorLength);
      fxScaleY        = L_LDivFx(pptlDirectionVector->y, lVectorLength);

      *pfxExtraX      = FX_FXMulFX(pDDC->DCState.dcbnd.cbnd.fxExtra,
                                   fxScaleX);
      *pfxExtraY      = FX_FXMulFX(pDDC->DCState.dcbnd.cbnd.fxExtra,
                                   fxScaleY);
      *pfxBreakExtraX = FX_FXMulFX(pDDC->DCState.dcbnd.cbnd.fxBreakExtra,
                                   fxScaleX);
      *pfxBreakExtraY = FX_FXMulFX(pDDC->DCState.dcbnd.cbnd.fxBreakExtra,
                                   fxScaleY);
      *pfxExtraErrorX = FX_FXMulFX(fxExtraError, fxScaleX);
      *pfxExtraErrorY = FX_FXMulFX(fxExtraError, fxScaleY);
   }
}

/***************************************************************************
 *
 * FUNCTION NAME = FindVectorLength
 *
 *
 * DESCRIPTION   = Finds the length of a given vector
 *
 *
 * INPUT         = PPOINTL
 *
 *
 *
 *
 * OUTPUT        = Length of Vector
 *
 *
 *
 *
 * RETURN-NORMAL =
 *
 *
 *
 * RETURN-ERROR  = NONE
 *
 *
 *
 **************************************************************************/
ULONG FindVectorLength(PPOINTL pPtl)
{
   LONG lOFlag;

   if(!pPtl->x)
      return( ABS( pPtl->y ));

   if(!pPtl->y)
      return( ABS( pPtl->x ));

   {
      QUAD   q1, q2, q3;
      FIXED  fxSqRoot;
      ULONG  ulLength;
    /*===============================================================
    ** The following two calls to fxmultiply are used here to produce
    ** a 64-bit result.  If the PS is using LONG coordinates, it is
    ** mathematically possible to create a 64-bit result when
    ** squaring either x or y.  When viewing this call with the
    ** debugger, ignore the value returned in EAX.  This value will
    ** be the MIDDLE 32-bits and may confuse the real intent here.
    *===============================================================*/

    fxmultiply(&q1, pPtl->x, pPtl->x, &lOFlag);
    fxmultiply(&q2, pPtl->y, pPtl->y, &lOFlag);

    AddFXQuad((FXQUAD *)&q1, (FXQUAD *)&q2, (FXQUAD *)&q3);

    /*===============================================================
    ** OK.  Now we have a very large, possible 64-bit, value in the
    ** QUAD structure, q3.  We can also use the property that:
    **
    **      sqrt( x ) * sqrt( y )          = sqrt( x * y )
    **      sqrt( x ) * sqrt( 0x100000000) = sqrt( x * 0x100000000 )
    **      sqrt( x ) * 0x10000            = sqrt( x * 0x100000000 )
    **
    ** If the value in q3 fits within 32-bits, we simply align it
    ** based on the real QUAD structure by moving the low DWORD into
    ** the high DWORD and then setting the low DWORD to zero.  The
    ** result from quad_square_root() is then rounded to the nearest
    ** integer and converted into a long.
    **
    ** If, however, the value in q3 exceeds 32-bits, we can assume
    ** it is a 32:32 fixed value of the form:
    **
    **                x / 0x100000000
    **
    ** This allows us to pass it directly to quad_square_root.  When
    ** we return, we must multiply the result by 0x10000.  However,
    ** we can also accomplish this by using the fact that
    ** quad_square_root() returns a 16:16 fixed value that can be
    ** cast to remove the decimal point and produce the 32-bit value
    ** we are looking for.
    **
    ** 4/16/92   Richard Wooley   PTR SM10175
    **===============================================================
    **
    ** Since quad_square_root cannot handle super large values, make
    ** sure we don't shift numbers that are just under 1 (as 32.32)
    **
    ** 6/2/92    Marc L. Cohen    PTR SM12179
    *===============================================================*/

    if ( q3.qhi == 0L && !(q3.qlo & 0x80000000 )) {
        q3.qhi = q3.qlo;
        q3.qlo = 0L;
        fxSqRoot = quad_square_root(&q3);
        ulLength = (ULONG) ROUNDFX( fxSqRoot );
    }
    else {
        ulLength = (ULONG) quad_square_root(&q3);
    } /* endif */

    return (ulLength);

   }
}


/***************************************************************************
 *
 * FUNCTION NAME = GetHorizTextAlignShift
 *
 *
 * DESCRIPTION   = Finds text alignment shift in the horizontal direction
 *                 based on the usTextAlignment
 *
 *
 * INPUT         = LONG lxLeft,
 *                 LONG lxRight,
 *                 LONG lyTop,
 *                 LONG lyBottom,
 *                 USHORT usTextAlign,
 *                 USHORT usDirection
 *
 *
 * OUTPUT        = Amount of Horizontal shift
 *
 *
 *
 *
 * RETURN-NORMAL =
 *
 *
 *
 * RETURN-ERROR  = NONE
 *
 *
 *
 **************************************************************************/

LONG  GetHorizTextAlignShift(LONG lxLeft,
                             LONG lxRight,
                             LONG lyTop,
                             LONG lyBottom,
                             USHORT usTextAlign,
                             USHORT usDirection)
{
  LONG lShift = 0;

  switch(usTextAlign & 0x00FF)
  {
    case TA_LEFT:
      switch (usDirection)
      {
        case  CHDIRN_RIGHTLEFT :         /* Backwards english text - arabic?  */
          lShift = lxLeft;
          break;
        case  CHDIRN_LEFTRIGHT :         /* Usual english text                */
        /*
        ** lAveCharWidth == lMaxCharInc == lEmInc so there is nothing to
        ** do here
        */
        case  CHDIRN_TOPBOTTOM :
        case  CHDIRN_BOTTOMTOP :         /* Going up                          */
          break;
      }
      break;

    case TA_CENTER :
      switch (usDirection)
      {
        case  CHDIRN_LEFTRIGHT :         /* Usual english text                */
          lShift = lxRight / 2;
          break;
        case  CHDIRN_RIGHTLEFT :         /* Backwards english text - arabic?  */
          lShift = lxLeft / 2;
          break;
        case  CHDIRN_TOPBOTTOM :
        case  CHDIRN_BOTTOMTOP :         /* Going up                          */
          break;
      }
      break;

    case TA_RIGHT :
      switch (usDirection)
      {
        case  CHDIRN_LEFTRIGHT :         /* Usual english text                */
          lShift = lxRight;
          break;
        case  CHDIRN_RIGHTLEFT :         /* Backwards english text - arabic?  */
        /*
        ** lAveCharWidth == lMaxCharInc == lEmInc so there is nothing to
        ** do here
        */
        case  CHDIRN_TOPBOTTOM :
        case  CHDIRN_BOTTOMTOP :         /* Going up                          */
          break;
      }
      break;

  } /* END switch(usTextAlign & 0x00FF) */
  return(lShift);
}



/***************************************************************************
 *
 * FUNCTION NAME = GetVertTextAlignShift
 *
 *
 * DESCRIPTION   = Finds text alignment shift in the vertical direction
 *                 based on the usTextAlignment
 *
 *
 * INPUT         = LONG lxLeft,
 *                 LONG lxRight,
 *                 LONG lyTop,
 *                 LONG lyBottom,
 *                 USHORT usTextAlign,
 *                 USHORT usDirection
 *
 *
 * OUTPUT        = Amount of Vertical shift
 *
 *
 *
 *
 * RETURN-NORMAL =
 *
 *
 *
 * RETURN-ERROR  = NONE
 *
 *
 *
 **************************************************************************/

LONG  GetVertTextAlignShift(LONG lxLeft,
                            LONG lxRight,
                            LONG lyTop,
                            LONG lyBottom,
                            USHORT usTextAlign,
                            USHORT usDirection)
{
  LONG lShift = 0;

  switch(usTextAlign & 0xFF00)
  {
    case TA_TOP :
      switch (usDirection)
      {
        case  CHDIRN_LEFTRIGHT :         /* Usual english text                */
        case  CHDIRN_RIGHTLEFT :         /* Backwards english text - arabic?  */
        case  CHDIRN_BOTTOMTOP :         /* Going up                          */
          lShift = lyTop;
          break;
        case  CHDIRN_TOPBOTTOM :
          break;
      }
      break;

    case TA_HALF :
      switch (usDirection)
      {
        case  CHDIRN_LEFTRIGHT :         /* Usual english text                */
        case  CHDIRN_RIGHTLEFT :         /* Backwards english text - arabic?  */
        case  CHDIRN_BOTTOMTOP :         /* Going up                          */
          lShift = ((lyTop - lyBottom) / 2) + lyBottom;
          break;
        case  CHDIRN_TOPBOTTOM :
          lShift =  lyBottom / 2;
          break;
      }
      break;

    case TA_BASE :
      switch (usDirection)
      {
        case  CHDIRN_TOPBOTTOM :
        case  CHDIRN_LEFTRIGHT :         /* Usual english text                */
        case  CHDIRN_RIGHTLEFT :         /* Backwards english text - arabic?  */
        case  CHDIRN_BOTTOMTOP :         /* Going up                          */
          break;
      }
      break;

    case TA_BOTTOM :
      switch (usDirection)
      {
        case  CHDIRN_LEFTRIGHT :         /* Usual english text                */
        case  CHDIRN_RIGHTLEFT :         /* Backwards english text - arabic?  */
        case  CHDIRN_TOPBOTTOM :
          lShift = lyBottom;
          break;
        case  CHDIRN_BOTTOMTOP :         /* Going up                          */
          break;
      }
      break;

  } /* END switch(usTextAlign & 0xFF00) */
  return(lShift);
}

/***************************************************************************
 *
 * FUNCTION NAME = GetStandardAlignment
 *
 *
 * DESCRIPTION   = Finds standard text alignment based on the direction
 *                 of the text
 *
 * INPUT         = USHORT usTextAlign
 *                 USHROT usDirection
 *
 *
 *
 * OUTPUT        = The standard text alignment
 *
 *
 *
 *
 * RETURN-NORMAL =
 *
 *
 *
 * RETURN-ERROR  = NONE
 *
 *
 *
 **************************************************************************/

USHORT GetStandardAlignment(USHORT usTextAlign, USHORT usTextDirection)
{
  USHORT usVertAlign = usTextAlign & 0xFF00;
  USHORT usHorzAlign = usTextAlign & 0x00FF;

  if (usVertAlign == TA_STANDARD_VERT)
  {
    switch(usTextDirection)
    {
      case CHDIRN_LEFTRIGHT:
      case CHDIRN_RIGHTLEFT:
      case CHDIRN_BOTTOMTOP:
        usVertAlign = TA_BOTTOM;
        break;
      case CHDIRN_TOPBOTTOM:
        usVertAlign = TA_TOP;
        break;
    }
  }

  if (usHorzAlign == TA_STANDARD_HORIZ)
  {
    switch(usTextDirection)
    {
      case CHDIRN_LEFTRIGHT:
      case CHDIRN_BOTTOMTOP:
      case CHDIRN_TOPBOTTOM:
        usHorzAlign = TA_LEFT;
        break;
      case CHDIRN_RIGHTLEFT:
        usHorzAlign = TA_RIGHT;
        break;
    }
  }
  return(usVertAlign | usHorzAlign);
}
