/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT (C) Microsoft Corporation, 1989                                 */
/* COPYRIGHT    Copyright (C) 1995 IBM Corporation                           */
/*                                                                           */
/*    The following IBM OS/2 WARP source code is provided to you solely for  */
/*    the purpose of assisting you in your development of OS/2 WARP device   */
/*    drivers. You may use this code in accordance with the IBM License      */
/*    Agreement provided in the IBM Device Driver Source Kit for OS/2. This  */
/*    Copyright statement may not be removed.                                */
/*                                                                           */
/*****************************************************************************/
/**************************************************************************
 *
 * SOURCE FILE NAME = FONT.C
 *
 * DESCRIPTIVE NAME = POSTSCRIPT DRIVER
 *
 *
 * VERSION = V2.0
 *
 * DATE
 *
 * DESCRIPTION         Font support code & data
 *
 *
 * FUNCTIONS           FullToFontName
 *                     MatchFaceName
 *                     prda_DefaultFontIndex
 *                     prda_DeviceQueryFontAttributes
 *                     GetMappedFontName
 *                     szIsEqual
 *                     PfntFromIndex
 *                     GetPFMetrics
 *                     FreeFontResource
 *                     SFDownload
 *                     RemapBoldItalic
 *                     szLength
 *                     prda_CountKernPairs
 *                     RemapCodePage
 *                     CPDownLoaded
 *
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/

#include "inc\prdinclt.h"
#include "inc\utl.h"
#include "inc\prdgextf.h"
#include "inc\prdtcone.h"
#include "inc\prdtextf.h"
#include "inc\prdmath.h"
#include "inc\allfonts.h"
#include "inc\fremap.h"
#include "inc\ofm.h"
#include "inc\pspagtun.h"             /* V2.174057  Page Tuning */
#include "inc\mapping.h"
#include <string.h>
#include <stdlib.h>
#define  INCL_GENPLIB_ERROR
#include <genplib.h>

#define  INCL_DEV
#include <pmdev.h>
#define   OD_MEMORY   8L
/*
** #define TESTING
*/

extern SHORT   prda_DefaultFontIndex(PDDC);

extern VOID   GetMappedFontName(PDDC,PB,int,SHORT);
extern PFNT   PfntFromIndex(PDDC,SHORT);/* charstr2.c             */
extern PFONTMETRICS   GetPFMetrics(PFONTDEF);/* charstr2.c         */
extern VOID   FreeFontResource( PDV, PFONTDEF,PFNT);/* charstr2.c        */
extern VOID   SFDownload(PDDC,PSZ);/* download.c           */
extern HMODULE pscript_module;

extern PDDC EnterDriver(PDDC);
extern VOID ExitDriver(PDDC);
VOID   RemapBoldItalic(PDDC,PFATTRS);
SHORT   szLength(PSZ);
LONG   prda_CountKernPairs(PDDC);
BOOL   RemapCodePage(PDDC,SHORT,SHORT,PSZ);
BOOL CPDownLoaded(PDDC,SHORT);
#define  RF_DELETE_ENGINE_FONT 4
#define  ERROR_NEGONE  (-1L)
#define  NO_CODEPAGE   65400
#define  GRAVE         96    /* a few accent glyph ids                      */
#define  ACUTE         239
#define  CIRCUMFLEX    94
#define  TILDE         126
#define  DIERESIS      249
#define  RING          248
#define  CEDILLA       247
#define  CARON         307
#define  DOTLESSI      213
#define  CP_LIMIT      10
#define  CP_LIMIT_MINUS_ONE CP_LIMIT - 1

/*
**           !!CR FIND OUT WHAT TO DO IN CASE AN INVALID CODE PAGE
**           IS GIVEN AS INPUT
*/
/***************************************************************************
 *
 * FUNCTION NAME =  FullToFontName
 *
 *
 * DESCRIPTION   =  Maps the given Face Name to the appropriate Font Name.
 *                   Copy fontmetrics from source to destination,
 *
 *                  Effects: Scans the list of all fonts offered by
 *                           the current printer until one is found
 *                           whose Face name is the same as that requested.
 *
 * Warnings: Fonts have two different names.  The FullName is the most
 *           complete; an example is "ITC Avant Garde Gothic Book Oblique".
 *           This is the name used for matching.  However, the FontNames
 *           used in the printer font list have an abbreviated form like
 *           "AvantGarde-BookOblique".
 *
 * INPUT         = PSZ      Face Name
 *                 PDDC     pddc
 *
 *
 *
 * OUTPUT        = NONE
 *
 *
 *
 *
 * RETURN-NORMAL = Font Name PSZ.
 *                  WARNING:
 *                  This function overwrites the data at
 *                  pMetricsSrc.  If this data is
 *                  valuable, it should be copied before
 *                  entry to here.
 *
 *
 *
 * RETURN-ERROR  = FALSE
 *
 *
 *
 ****************************************************************************/

PSZ FullToFontName( PSZ pszFaceName, PDDC pddc )
{
  SHORT i;

  for (i = 0; i < pddc->pdv->cFonts; i++)
  {
    if ( (*pddc->pdv->paFonts)[i].pszFullName  &&  /*            */
         szIsEqual(pszFaceName, (PSZ)(*pddc->pdv->paFonts)[i].pszFullName))
    {
      return (PSZ) (*pddc->pdv->paFonts)[i].pszFontName;
    }
  }
  return (PSZ) 0L;            /* not found                                   */
}

/***************************************************************************
 *
 * FUNCTION NAME =  MatchFaceName
 *
 * DESCRIPTION   =  Returns the index number (0-n), in the current printer's
 *                  list of supported fonts, of the font that has the
 *                  specified face name.
 *
 *                  Effects: Scans the list of all fonts offered by
 *                           the current printer until one is found
 *                           whose Face name is the same as that requested.
 *                           The index of this matching font is then returned.
 *                           If no match is found, -1 is returned.
 *
 * Warnings: Fonts have two different names.  The FullName is the most
 *           complete; an example is "ITC Avant Garde Gothic Book Oblique".
 *           This is the name used for matching.  However, the FontNames
 *           used in the printer font list have an abbreviated form like
 *           "AvantGarde-BookOblique".
 *
 * INPUT         = PSZ      pointer to Face Name
 *                 PDDC     pointer to display context
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = Index number of desired font, or -1 if not found.
 *
 * RETURN-ERROR  = FALSE
 *
 ****************************************************************************/

SHORT MatchFaceName( PSZ pszFaceName, PDDC pddc, BOOL fOnlySoftFont )
{
  SHORT i;
  PSZ   pSz;
  PFNT  pFnt;

  /*
  ** Find the index number corresponding to this fontfullname
  */
  for (i = 0; i < pddc->pdv->cFonts; i++)
  {
    pFnt = (PFNT)&(*pddc->pdv->paFonts)[i];

    /*
    ** The tests - must have a fullname, if want softfont then usresource must
    ** be zero, and the names must match
    */
    if ( pFnt->pszFullName    &&
       ( ( fOnlySoftFont && ((SHORT)pFnt->usResource <= 0 )) || !fOnlySoftFont ) &&
         szIsEqual( pszFaceName, pFnt->pszFullName ) )
    {
      return  i;
    }
  }

  return -1;                 /* not found                                   */
}

/***************************************************************************
 *
 * FUNCTION NAME =  FontDownLoad
 *
 * DESCRIPTION   =  Download a soft font if necessary. NOP HW fonts.
 *
 * INPUT         = PDDC     pointer to display context
 *                 PSZ      pointer to Face Name
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

VOID FontDownload( PDDC pddc, PSZ pszName )
{
  SHORT i;
  PFNT  pFnt;

  if (0 <= (i = MatchFaceName( pszName, pddc, pddc->pddcb->text.fUseSoftFont )))
  {
    pFnt = (PFNT) &(*pddc->pdv->paFonts)[i];

    /*
    ** If soft font
    ** and not yet loaded.
    */
    if ((SHORT) pFnt->usResource <= 0
       && pFnt->bLoaded == FALSE)
    {
      /*
      **  Make sure channel is open before
      **  downloading fonts
      */
      if (!pddc->fHeaderSent  &&
          pddc->pdv->usDataType == PM_Q_RAW ) /*            down waste time unless raw */
      {
        ps_startdoc(pddc);
      }
      /*            changed name */
      SFDownload( pddc, (PSZ)pFnt->pszPFB);/* define soft font               */
      pFnt->bLoaded = TRUE;  /* mark it as loaded                           */
    }
  }
  return ;
}

/***************************************************************************
 *
 * FUNCTION NAME =  prda_RealizeFont
 *
 * DESCRIPTION   =  Attempts to match a device font to a requested
 *                  logical font and returns a LONG handle to the
 *                  font if successful.
 *
 *            pfatLogFont    =  Pointer to a FATTRS structure con-
 *                              taining logical font information
 *                              if this is a RF_DEVICE_FONT command.
 *            lFontHandle    =  The handle of the logical font to be
 *                              deleted if this is a RF_DELETE_FONT
 *                              command.
 *
 *
 *          RF_DELETE_FONT:
 *             1                Success
 *             0                error
 *
 * Actions:
 *          RF_DEVICE_FONT      Attempt to match font, return handle.
 *          RF_LOAD_ENGINE_FONT Return 0L, font can't be realized.
 *          RF_DELETE_FONT      Attempt to delete font with handle
 *                              specified.
 *
 * RF_DEVICE_FONT   In this case, the PostScript driver will always
 *              return a matching device font unless a code page is
 *              requested that we cannot support.  If a legitimate
 *              code page is requested but no match is provided and
 *              we can't match the facename, the  default font will
 *              be used.  The algorithm used is:
 *
 *              Check the lMatch field of the FATTRS structure.  If
 *              negative, it is presumably one of the "magic cookies"
 *              we gave the engine from DeviceQueryFonts, and we choose
 *              that font.  If positive, then the engine is telling
 *              us it will do font simulations, and we return a handle
 *              of zero.  If lMatch is zero, we look for a facename
 *              in the FATTRS structure.  If the facename is not null,
 *              we attempt to match it to one of the facenames in our
 *              list of fonts supported by the current printer.  If we
 *              match, we select the matched font.  If we don't match
 *              or the facename is null, we choose the default font.
 *
 *              Once we have chosen a font, we deal with code pages.
 *              If the usCodePage field of the FATTRS structure is
 *              zero, we simply choose the code page provided by the
 *              font.  Otherwise, we try to get a mapping vector from
 *              the requested code page to the code page supported by
 *              the font.  If we cannot get such a vector, THE FONT
 *              IS NOT REALIZABLE; we return zero to the engine.
 *
 *              Once we have chosen a font and found a suitable code
 *              page mapping vector, we look for space in our logical
 *              font info table in the pddc.  If an entry is free,
 *              we allocate it, initialize its records, and return
 *              the negative of the index as the 32 bit handle. If
 *              the table is full, we must return an error.
 *
 * RF_LOAD_ENGINE_FONT   We always return 0L.
 *
 * RF_DELETE_FONT        If the handle provided is legitimate, we
 *                       erase the corresponding font from our logical
 *                       font info table and return 1L.  Otherwise
 *                       we must return an error.  If the handle
 *                       provided corresponds to the logical font
 *                       currently in use, we first select the default
 *                       font before deleting the logical font.
 *
 *
 * INPUT         =  RF_DEVICE_FONT or
 *                  RF_LOAD_ENGINE_FONT or
 *                  RF_DELETE_FONT
 *
 * OUTPUT        = RF_DEVICE_FONT or RF_LOAD_ENGINE_FONT:
 *                 >0 32-bit handle for the new logical font
 *                  0 Font cannot be realized
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/
/*
**              !!!CR FIND OUT WHAT CODE PAGE TO USE FOR SYMBOL FONT.
*/

ULONG prda_RealizeFont( HDC hdc, ULONG ulCommand, PFATTRS pfa,
                        LONG lFontHandle, PDDC pddc, ULONG FunN)
{
  SHORT        prda_SetTextAttrs(SHORT, SHORT, PDCHARBUNDLE, PDDC);
  LONG         lFont;                /* font cookie we will return        */
  LONG         lOffset;              /* font offset from MatchFaceName    */
  USHORT       usAvailableCodePage;/* code page the font supports         */
  PUSHORT      pusRequestedVector = 0L;/* vector for requested code page  */
  LONG         i;
  PFNT         pFnt;
  PFONTDEF     pfdFont;
  PFONTMETRICS pfmMetrics;
  USHORT       usIndex, usCodePage;
  CHAR         szFont[FACESIZE];
  ULONG        tl;
  BOOL         fMustUseSoftFont;    /*            */

  EnterDriver( pddc );

  #if      DEBUG
    LogCall( "prda_RealizeFont(%08lx, %lp, %lp, %08lx)\n", ((PSZ)&hdc)+sizeof(hdc));
    PrintLog( (PSZ)"ulCommand = %ld, pfa = %lp\n", ulCommand, pfa);

    if (pfa)
    {
      PrintLog( (PSZ)"pfa->usRecordLength = %d\n", pfa->usRecordLength);
      PrintLog( (PSZ)"pfa->fsSelection = %d\n", pfa->fsSelection);
      PrintLog( (PSZ)"pfa->lMatch = %ld\n", pfa->lMatch);
      PrintLog( (PSZ)"pfa->szFacename = %ls\n", (PSZ)pfa->szFacename);
      PrintLog( (PSZ)"pfa->idRegistry = %d\n", pfa->idRegistry);
      PrintLog( (PSZ)"pfa->usCodePage = %d\n", pfa->usCodePage);
      PrintLog( (PSZ)"pfa->lMaxBaselineExt = %ld\n", pfa->lMaxBaselineExt);
      PrintLog( (PSZ)"pfa->lAveCharWidth = %ld\n", pfa->lAveCharWidth);
      PrintLog( (PSZ)"pfa->fsType = %d\n", pfa->fsType);
      PrintLog( (PSZ)"pfa->fsFontUse = %d\n", pfa->fsFontUse);
    }
    else
    {
      PrintLog( (PSZ)"pfa = NULL\n");
    }
#endif

  if (pddc->iType == OD_MEMORY)
  {
    ExitDriver( pddc );
    return (ulCommand == RF_DELETE_FONT ? GPI_ERROR : 0);
  }

  /*
  **  !!!CR GET THESE CONSTANTS DEFINED
  */
  switch ((SHORT)ulCommand)
  {
  case RF_DEVICE_FONT :   /* Realize a logical font                      */
       /*
       ** check lMatch
       */
       /*
       **            !!!CR COMMENT ON WHAT THE DIFFERENT LMATCH VALUES MEAN
       */
       /*
       ** if we have a positive lMatch just return 0, ie no match.
       */
       /*
       **           
       ** Since we are using ATM fonts lets see if we have the
       ** softfont on a positive lmatch
       */
       if (pfa->lMatch > 0L)
       {
         if ( (PSZ) pfa->szFacename == NULL )
         {
           ExitDriver(pddc);
           return (0L);
         }
         else
         {
           fMustUseSoftFont = TRUE;
         }
       }
       else
       {
         /*
         ** for 0 or neg lmatch
         */
         fMustUseSoftFont = FALSE;
       }

       /*
       ** Use the magic cookie given by the caller
       */
       lFont = pfa->lMatch;

       #if      DEBUG
         if (lFont == 0)
         {
           PrintLog( (PSZ) "lMatch is 0: Checking the facename...\n");
         }
       #endif                       /* DEBUG                                       */

       /*
       ** save the selection options for use in charstringpos
       */
       pddc->pddcb->cgs.usSelection = pfa->fsSelection;

       /*
       ** check Facename
       */
       if ((PSZ) pfa->szFacename != NULL)
       {
         PrintLog( (PSZ) "FaceName = %ls", (PSZ) pfa->szFacename);

         /*
         ** if the BOLD and/or ITALIC attributes are set,
         ** remap the font to take the attributes into
         **  account.
         */
         if (pfa->fsSelection & (FATTR_SEL_BOLD + FATTR_SEL_ITALIC))
         {
           RemapBoldItalic(pddc, pfa);
         }

         /*
         ** MatchFaceName attempts to find the facename in
         ** our current printer font list.  If successful, it
         ** returns the font's offset in the directory.  If
         ** unsuccessful, it returns -1
         **
         **
         **  !!!CR Cast MatchFacename to a long
         **  !!!CR lOffset should be lFontIndex
         */
         if ((lOffset = MatchFaceName((PSZ) pfa->szFacename, pddc,
                                      fMustUseSoftFont) ) >= 0L )
         {

           /*
           ** convert the font offset to a magic cookie
           */
           lFont = OffsetToCookie( lOffset );
           PrintLog( (PSZ) "...FaceName matched \n");
         }

         /*
         ** matches failed: choose default printer font
         */
         else
         {
           PrintLog( (PSZ) "...no match: choosing default font\n");
           lFont = OffsetToCookie((LONG)prda_DefaultFontIndex(pddc));

           /*
           **  The facename specified is not supported by driver .
           ** We should give a chance to engine to download the
           ** specified font.Hence we return 0 i.e. font is not
           ** realizable .
           */
           ExitDriver( pddc );
           return (0L);
         }
       }
       else
       {
         /*
         ** They didn't give an lMatch or a facename
         */
         PrintLog( (PSZ) "No lMatch or facename; using default font\n" );
         lFont = OffsetToCookie( (LONG) prda_DefaultFontIndex( pddc ) );
       }

       PrintLog( (PSZ) "RealizeFont--Cookie for chosen font = %ld\n", lFont );

       /*
       ** Settle the issue of code page
       */
       /*
       **  !!!CR EXPLAIN WHAT "SETTLE THE CODE PAGE ISSUE" MEANS
       */
       PrintLog( (PSZ) "Requested code page: %d\n", pfa->usCodePage );

       /*
       ** See what code page the chosen font supports
       */
       usIndex = (SHORT) CookieToOffset( lFont );
       pFnt = PfntFromIndex( pddc, usIndex );

       /*           
       ** Check to see if enough memory on printer for softfont
       */
       if ( (SHORT) pFnt->usResource <= 0 &&
             pddc->pdv->lFontCount < 999 )  /* 999 turns off check */
       {
         if ( pddc->pdv->lFontCount <= 0 )
         {
           ExitDriver(pddc);
           return ( 0L );
         }
         else
         {
           pddc->pdv->lFontCount--;
         }
       }

       pfdFont = prda_LoadFontResource(pddc, usIndex );

       if (!pfdFont)          /* Couldn't get resource                       */
       {
         /*
         ** !!CR Magic number, and ulong
         */
         ExitDriver(pddc );
         return ((LONG)-1 );   /* Minor function saves error code             */
       }
       pfmMetrics = GetPFMetrics( pfdFont );
       usAvailableCodePage = pfmMetrics->usCodePage;
       PrintLog( (PSZ) "This font supports code page %d\n", usAvailableCodePage) ;
       FreeFontResource( pddc->pdv, pfdFont, pFnt);

       /*
       ** Determine if we can map the code page that the caller wishes to
       ** use into the code page that this font supports.  If not, then
       ** we cannot realize this font.
       **
       ** !!!CR make a define for 850 instead of using a magic number
       */
       /*           
       ** Don't worry about application/caller codepage compatibility if font
       ** has 65400 (no code page)
       */
       if ( usAvailableCodePage != NO_CODEPAGE )
       {
         if (usAvailableCodePage == 850)
         {
           /*
           ** This font offers Winthorn multi-codepage support, but has the caller
           ** requested a codepage that the engine is not familiar with?
           */
           if (pfa->usCodePage)
           {
             pusRequestedVector = (PUSHORT) GreQueryCodePageVector((ULONG)
                                  pfa->usCodePage );

             if (!pusRequestedVector)
             {
               PrintLog( (PSZ)
                  "QueryCodePageVector doesn't know requested code page\n" );
               PrintLog( (PSZ) "Unknown codepage requested. Cannot realize font\n" );
               ExitDriver( pddc );
               return (0L );
             }

             #if      DEBUG
               else
               {
                 PrintLog( (PSZ) "QueryCodePageVector gave vector %lp\n",
                    pusRequestedVector );
               }
             #endif                       /* DEBUG                                       */
           }
         }
         else
         {
           /*
           ** This font only works with one specific code page.  The caller
           ** must have either requested that code page number explicitly,
           ** or requested code page zero.
           */
           if ((pfa->usCodePage != usAvailableCodePage) && (pfa->usCodePage))
           {
             PrintLog( (PSZ)
                "Font requires specific code page. Cannot realize font\n" );
             ExitDriver( pddc );
             return (0L );
           }
         }
       }

       #if      DEBUG
         if (pusRequestedVector)
         {
           PrintLog( (PSZ) "Code Point    ---> Glyph number\n" );

           /*
           **!!!CR this should be ifdef'ed out with DEBUG
           */
           for (i = 150; i < 170; i++)
           {
             PrintLog( (PSZ) "   %d                   %d\n", i,
                *(pusRequestedVector+i) );
           }
         }
       #endif                       /* DEBUG                                       */

       /*
       ** Now we must find a logical font info entry that is free
       */
       for (i = MIN_FONT_LCID; (i <= MAX_FONT_LCID) &&
       (pddc->pddcb->text.lfiEntry[i].bFlags&LFONT_IN_USE ); i++);

       if (i <= MAX_FONT_LCID)
       {
         /*
         **            Indicate if needs softfont
         */
         pddc->pddcb->text.fUseSoftFont = fMustUseSoftFont;

         /*
         ** Set up entry in logical font info table
         **
         ** !!!CR create constants for these flags instead of BIT1, BIT0, ETC
         */
         pddc->pddcb->text.lfiEntry[i].bOffset = (BYTE) CookieToOffset( lFont );
         pddc->pddcb->text.lfiEntry[i].bFlags = LFONT_IN_USE;

         if (pfa->fsType&BIT2)
         {
           pddc->pddcb->text.lfiEntry[i].bFlags |= LFONT_HAS_KERNING;
           PrintLog( (PSZ) "This font has kerning\n" );
         }

         #if      DEBUG
           else
           {
             PrintLog( (PSZ) "This font has no kerning\n" );
           }
         #endif                       /* DEBUG                                       */

         if (pfa->fsSelection&FATTR_SEL_UNDERSCORE)
         {
           pddc->pddcb->text.lfiEntry[i].bFlags |= LFONT_HAS_UNDERSCORE;
           PrintLog( (PSZ) "This font has underscore\n" );
         }

         #if      DEBUG
           else
           {
             PrintLog( (PSZ) "This font has no underscore\n" );
           }
         #endif                       /* DEBUG                                       */

         if (pfa->fsSelection&FATTR_SEL_STRIKEOUT)
         {
           pddc->pddcb->text.lfiEntry[i].bFlags |= LFONT_HAS_STRIKEOUT;
           PrintLog( (PSZ) "This font has strikeout\n" );
         }

         #if      DEBUG
           else
           {
             PrintLog( (PSZ) "This font has no strikeout\n" );
           }
         #endif                       /* DEBUG                                       */

         if (pfa->fsSelection&FATTR_SEL_OUTLINE)
         {
           pddc->pddcb->text.lfiEntry[i].bFlags |= (LFONT_IS_OUTLINE+
              LFONT_OUTLINE_NOTDONE );
           PrintLog( (PSZ) "This font is outline font\n" );
         }

         #if      DEBUG
           else
           {
             PrintLog( (PSZ) "This font is not outline font\n" );
           }
         #endif                       /* DEBUG                                       */

         /*
         ** if no codepages have been remapped, this means
         ** that we are still using codepage 850, and don't
         ** have any remapping to worry about.
         */

         #if      0
           if (pddc->pddcb->cgs.cCodePagesRemapped == 0)
           {
             ClearFontRemap( i );
           }
           else
           {
             ps_SetDownloadedFont( pddc );
           }
         #endif

         /*
         ** mark this newly defined font as having not been
         ** remapped to a codepage.
         */
         ClearFontRemap( i );

         /*
         ** make sure this newly defined font works with
         ** the current codepage.  use the codepage passed
         ** in the FATTRS structure.  if, however, it is zero,
         ** use the default codepage.
         */
         if ((PSZ) pfa->szFacename != NULL)
         {
           szCopy((PSZ) szFont, (PSZ) pfa->szFacename, FACESIZE );
         }
         else
         {
           szCopy((PSZ) szFont, (PSZ) pddc->pdv->szDefFont, FACESIZE );
         }

         /*
         ** If the font is NO_CODEPAGE keep it instead of using the
         ** process codepage
         */
         if ( usAvailableCodePage == NO_CODEPAGE )  /* APL problem */
         {
           usCodePage = NO_CODEPAGE;
         }
         else
         if (pfa->usCodePage)
         {
           usCodePage = pfa->usCodePage;
         }
         else
         {
           usCodePage = pddc->pddcb->text.usDefCodePg;
         }

         /*
         ** save the codepage for the the logical font we
         ** are realizing.
         */
         pddc->usCodePages[i] = usCodePage;

         #if      0
           /*
           ** there is no need to remap the font if no
           ** codepages have been remapped, and the codepage
           ** is 850. This is an optimization which should
           ** prevent the remapping code from being downloaded
           ** for most print jobs. There is code in QuoteChar
           ** which checks the bytes as they are output, so
           ** even if we are using codepage 850, if we use
           ** non-ascii characters, the remapping will get done
           ** when necessary.
           **
           ** NO_CODEPAGE is the codepage which means leave this
           ** font alone; don't do any remapping. also, i have
           ** put in a check to see if we have codepage 1004 or
           ** 850. both of these codepages have the identical
           ** first 128 characters. if an extended character is
           ** chosen for either of these fonts, the remapping will
           ** be done at print time by prda_QuoteChar. - kentse
           */
           if (((pddc->pddcb->cgs.cCodePagesRemapped != 0) || (usCodePage != 850)
             ) && (usCodePage != NO_CODEPAGE))
           {
             if (!RemapCodePage(pddc, usCodePage, (SHORT)i, (PSZ)szFont))
             {
               ExitDriver( pddc );
               return( 0L );
             }
             SetFontRemap(i );
           }
       #endif

         lFont = OffsetToCookie( i );
         ExitDriver( pddc );
         return( lFont );
       }
       else
       {
         /*
         ** Logical font info table is full
         */
         PrintLog( (PSZ) "Logical Font info table is full! " );
         PrintLog( (PSZ) "-- Not realized\n" );

         /*
         **  TALK TO PAULK ABOUT THE CORRECT ERROR CODE TO RETURN
         */
         GplErrSetError(  PMERR_INSUFFICIENT_MEMORY );

         /*
         ** !!CR magic number and ulong
         */
         ExitDriver( pddc );
         return( (ULONG)-1 );
       }

  case RF_LOAD_ENGINE_FONT :
  case RF_DELETE_ENGINE_FONT :
       /*
       ** PostScript driver must return 0L for this command
       */
/*
**!!! THIS CODE IS A FIX  TO GET AROUND AN ENGINE BUG.  THE ENGINE
**!!! IS CALLING US WITH THE DELETE_ENGINE_FONT COMMAND INSTEAD OF
**!!! THE DELETE_FONT COMMAND.  ONCE THE ENGINE IS FIXED, TAKE THIS
**!!! CODE OUT OF HERE!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
*/
/*
**!!! THIS CODE IS A FIX  TO GET AROUND AN ENGINE FUNCTION.  THE ENGINE
**!!! IS CALLING US WITH THE DELETE_ENGINE_FONT COMMAND INSTEAD OF
**!!! THE DELETE_FONT COMMAND.
*/
       /*
       ** See if the font handle is valid
       */
       if (lFontHandle > 0)
       {
         ExitDriver( pddc );
         return( 0 );
       }
       lOffset = CookieToOffset( lFontHandle );

       if ((lOffset < MIN_FONT_LCID) || (lOffset > MAX_FONT_LCID))
       {
         PrintLog( (PSZ) "RF_DELETE_FONT - Illegal handle %ld\n", lFontHandle );
         GplErrSetError(  PMERR_INV_FONT_ATTRS );
         ExitDriver( pddc );
         return( 0L );         /* !!CR magic number                           */
       }

       /*
       ** Handle is valid -- free up the logical font table entry
       */
       pddc->pddcb->text.lfiEntry[lOffset].bFlags = 0;
       PrintLog( (PSZ) "RF_DELETE_FONT - Deleted font entry %ld\n", lOffset );
       ExitDriver( pddc );
       return( 0L );           /* !!CR magic number                           */

/*
**!!! END OF FIX !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
*/
       /*
       ** !!!CR                    FIND OUT WHEN THIS IS CALLED
       ** !!!CR AND COMMENT  THE REASON HERE.
       */

      PrintLog( (PSZ) "RF_DELETE_ENGINE_FONT - returning error\n" );
      ExitDriver( pddc );
      return( 0L );           /* !!CR magic number                           */

  case RF_DELETE_FONT :
       /*
       ** See if the font handle is valid
       */

      lOffset = CookieToOffset( lFontHandle );

      if ((lOffset < MIN_FONT_LCID) || (lOffset > MAX_FONT_LCID))
      {
        PrintLog( (PSZ) "RF_DELETE_FONT - Illegal handle %ld\n", lFontHandle );
        GplErrSetError(  PMERR_INV_FONT_ATTRS );
        ExitDriver( pddc );
        return( 0L );         /* !!CR magic number                           */
      }

      /*
      ** Handle is valid -- free up the logical font table entry
      */
      pddc->pddcb->text.lfiEntry[lOffset].bFlags = 0;
      PrintLog( (PSZ) "RF_DELETE_FONT - Deleted font entry %ld\n", lOffset );
      ExitDriver( pddc );
      return( 1L );           /* !!CR magic number                           */

  default:
      PrintLog( (PSZ) "??? Unknown command ???\n" );
      GplErrSetError(  PMERR_INV_FONT_ATTRS );
      ExitDriver( pddc );
      return( 0L );           /* magic number !!CR                           */
  }
}

/***************************************************************************
 *
 * FUNCTION NAME =  RemapBoldItalic
 *
 * DESCRIPTION   =
 * This routine is called when we are about to realize a font, and the
 * BOLD and/or ITALIC attributes have been set.  This routine maps the
 * given font into the appropriate font, depending on which attributes
 * have been set.  For example, if the user chooses Courier as the font
 * and has the BOLD attribute set, this routine will select
 * Courier-Bold as the font.
 *
 * INPUT         =  PDDC
 *                  PFATTRS
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

VOID RemapBoldItalic( PDDC pddc, PFATTRS pfa )
{
  SHORT index;

  /*
  ** search the list of fonts.  once we find it, we will have the
  ** index into our mapping tables.  this index will be used to look
  ** into the BOLD, ITALIC, or BOLDITALIC table to find which font
  ** to map into.
  */
  for (index = 0; index < TOTAL_FONTS_SUPPORTED; index++)
  {
    if (szIsEqual((PSZ) pfa->szFacename, (PSZ) AllFonts[index]))
    {
      break;
    }
  }

  /*
  ** do nothing if we could not find the font
  */
  if (index >= TOTAL_FONTS_SUPPORTED)
  {
    return;
  }

  /*
  ** if both BOLD and ITALIC attributes are set, use BOLDITALIC table
  */
  if ((pfa->fsSelection&FATTR_SEL_BOLD) && (pfa->fsSelection&FATTR_SEL_ITALIC) )
  {
    /*
    ** if the font to map to is the same as the current font, we
    ** are done.
    */

    if (!(szIsEqual((PSZ) AllFonts[index], (PSZ) AllFonts[BoldItalicFonts[index]])))
    {
      /*
      ** copy the BOLDITALIC font into pfa->szFacename
      */
      szCopy( (PSZ) pfa->szFacename, (PSZ) AllFonts[BoldItalicFonts[index]],
              szLength((PSZ) AllFonts[BoldItalicFonts[index]]) );
    }
    return;
  }

  /*
  ** if the BOLD attribute is set, use BOLD table
  */
  if (pfa->fsSelection & FATTR_SEL_BOLD)
  {
    /*
    ** if the font to map to is the same as the current font,
    ** we are done.
    */
    if (!(szIsEqual((PSZ) AllFonts[index], (PSZ) AllFonts[BoldFonts[index]])))
    {
      /*
      ** copy the BOLD font into pfa->szFacename
      */
      szCopy( (PSZ) pfa->szFacename, (PSZ) AllFonts[BoldFonts[index]], szLength
              ((PSZ) AllFonts[BoldFonts[index]]) );
    }
    return ;
  }

  /*
  ** if the ITALIC attribute is set, use ITALIC table
  */
  if (pfa->fsSelection&FATTR_SEL_ITALIC)
  {
    /*
    ** if the font to map to is the same as the current font, we
    ** are done.
    */
    if (!(szIsEqual( (PSZ) AllFonts[index], (PSZ) AllFonts[ItalicFonts[index]])))
    {
      /*
      ** copy the ITALIC font into pfa->szFacename
      */
      szCopy( (PSZ) pfa->szFacename, (PSZ) AllFonts[ItalicFonts[index]], szLength
         ((PSZ) AllFonts[ItalicFonts[index]]) );
    }
    return ;
  }
}

/***************************************************************************
 *
 * FUNCTION NAME =  szLength
 *
 * DESCRIPTION   =  This routine calculated the length of a given
 *                  string, including the terminating NULL.
 *
 * INPUT         =  PSZ     pointer to the string.
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = Length of the given string.
 *
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

SHORT szLength( PSZ pszScan )
{
  SHORT i;

  i = 1;

  while (*pszScan++ != '\0')
  {
    i++;
  }
  return( i );
}

/***************************************************************************
 *
 * FUNCTION NAME =  prda_countKernPairs
 *
 * DESCRIPTION   = Count the number of kerning pairs that are available for
 *                 the current font and codepage.  This may be different from
 *                 the maximum number of kerning pairs available if the
 *                 some of the kerned characters are not in the current
 *                 codepage.
 *
 * INPUT         =  PSZ     pointer to the string.
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = Number of Kerning Pairs.
 *
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

LONG prda_CountKernPairs( PDDC pddc )
{
  PKERNPAIRS pkp;            /* Pointer to our kern table                   */
  SHORT      sCodePoint1;    /* Kerning pair: first character               */
  SHORT      sCodePoint2;    /* Kerning pair: second character              */
  LONG       i = 0;          /* The kerning table index                     */
  LONG       nPairs = 0;     /* The number of kern pairs                    */

  /*
  ** Make sure the font resource is loaded.
  */
  if (!pddc->pddcb->cgs.fValidFont)
  {
    if (!prda_CheckFontResource(pddc))/* couldn't get resources             */
    {
      return( ERROR_NEGONE ); /* Minor function saves error code             */
    }

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

  /*
  ** Point to start of kerning table
  */
  pkp = (PKERNPAIRS) (((PB) pddc->pddcb->text.pfdfFontDef) +
                       pddc->pddcb->text.pfdfFontDef->Header.usKerningDataOffset );

  for (i = 0; i < pddc->pddcb->text.pfmFontMetrics->sKerningPairs; ++i)
  {
    /*
    ** The code points listed in a font's kerning table are stored in
    ** the code page of the font (usually 850). Before a kerning pair
    ** may be returned to the caller, the code points must be
    ** translated into the caller's code page.  This is what the
    ** prda_BackMapChar minor function does.
    */
    sCodePoint1 = prda_BackMapChar( pkp[i].g1, pddc );
    sCodePoint2 = prda_BackMapChar( pkp[i].g2, pddc );

    if (!((sCodePoint1 == -1) || (sCodePoint2 == -1)))
    {
      ++nPairs;
    }
  }
  return( (LONG) nPairs );
}

/***************************************************************************
 *
 * FUNCTION NAME =  prda_DeviceQueryFontAttributes
 *
 * DESCRIPTION   = Copies the FONTMETRICS data for the current font to the
 *                 caller's buffer.
 *
 * Effects: Copies the entire FONTMETRICS data structure to a temporary
 *          buffer, rescales it, and then copies the requested amount to
 *          the caller's buffer.
 *
 * Warnings: A request for more bytes of information than are contained in
 *           a FONTMETRICS structure will be silently reduced to a request
 *           for exactly as many bytes as are in a FONTMETRICS structure.
 *
 * INPUT  =  hdc;             Display context handle
 *           ulNumBytes;      How many bytes of FONTMETRICS data to return
 *           pfmFontMetrics;  Pointer to buffer where data shall be placed
 *           pddc;            Pointer to display context instance data
 *           FunN;            Engine function number
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = SUCCESS
 *
 * RETURN-ERROR  = FAILURE
 *
 ****************************************************************************/

ULONG prda_DeviceQueryFontAttributes( HDC hdc, ULONG ulNumBytes,
                                      PFONTMETRICS pfmFontMetrics,
                                      PDDC pddc, ULONG FunN)
  /*
  ** HDC          hdc;               Display context handle
  ** ULONG        ulNumBytes;        How many bytes of FONTMETRICS data to return
  ** PFONTMETRICS pfmFontMetrics;    Pointer to buffer where data shall be placed
  ** PDDC         pddc;              Pointer to display context instance data
  ** ULONG        FunN;              Engine function number
  */

{
  LONG lMatch;               /* The unique negativematch value for this
                                font */
  EnterDriver( pddc );

  #if      DEBUG
    LogCall( "prda_DeviceQueryFontAttributes (%08lx, %lp, %lp, %08lx)\n", ((PB)
             &hdc)+sizeof(hdc) );
  #endif /* DEBUG                                     */

  PrintLog( (PSZ) "ulNumBytes = {%ld}\n", ulNumBytes );

  /*
  ** Raise an error in the case of memory dc.
  */
  if (pddc->iType == OD_MEMORY)
  {
    GplErrSetError( (LONG)PMERR_DEV_FUNC_NOT_INSTALLED );
    ExitDriver( pddc );
    return( GPI_ERROR );
  }

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

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

  /*
  ** Copy a maximum of sizeof(FONTMETRICS) bytes
  */
  ulNumBytes = min(ulNumBytes, (ULONG) sizeof(FONTMETRICS) );

  /*
  ** Convert the index of the current font into a "magic cookie" which
  ** we will return in the lMatch field of the font metrics, so that the
  ** caller will be able to identify this font in the future.
  */

  lMatch = OffsetToCookie( (LONG) pddc->pddcb->text.usFont );

  /*
  ** Copy metrics to the caller's buffer, converting to world coordinates
  */
  prda_CopyMetrics( pfmFontMetrics, pddc->pddcb->text.pfmFontMetrics,
                    ulNumBytes, lMatch, pddc );
  pfmFontMetrics->sKerningPairs = (SHORT) prda_CountKernPairs(pddc );
  PrintLog( (PSZ) "QueryFontAttributes: sKerningPairs = %d\n",
            pddc->pddcb->text.pfmFontMetrics->sKerningPairs );
  PrintLog( (PSZ) "Setting lMatch to %ld\n", lMatch );
  ExitDriver( pddc );
  return( SUCCESS );
}

/***************************************************************************
 *
 * FUNCTION NAME =  prda_DeviceQueryFonts
 *
 * DESCRIPTION   =
 * Copies the FONTMETRICS data for the requested font to the caller's buffer.
 * If the given facename is null, then data for all fonts will be returned,
 * buffer size permitting.
 *
 * Effects: On return, pulNumFonts will have been changed from the maximum
 *          number of fonts the caller would like returned to the actual
 *          number of fonts returned in the caller's buffer.  This will
 *          either be zero (an invalid font facename was given), one (a
 *          legitimate facename was given), or a larger number.  The latter
 *          case would occur if the caller gave a null font facename.  In
 *          that situation as many fonts would be returned as could be fit
 *          into the caller's buffer.
 *
 * Warnings: A request for more bytes of information than are contained in
 *           a FONTMETRICS structure will return an error.
 *
 *                            - Return up to size of FONTMETRICS - fill
 *           rest with zeros.
 *
 *
 *           The driver presently does not distinguish between public and
 *           private fonts, so the options parameter is ignored.
 *
 * INPUT  =  hdc;               Display context handle
 *           ulOptions;         Options flags -- presently ignored
 *           pszFaceName;       Font to return FONTMETRICS data for
 *           pfmFontMetrics;    Pointer to buffer where data shall be placed
 *           ulNumBytes;        How many bytes of FONTMETRICS data to return
 *                              for each matching font
 *           pulNumFonts;       The maximum number of fonts to be returned
 *           pddc;              Pointer to display context instance data
 *           FunN;              Engine function number                          = NONE
 *
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL =  The total number of fonts matching the requested
 *                  face name, but not returned because the buffer was
 *                  not big enough.
 *
 * RETURN-ERROR  =  -1
 *
 ****************************************************************************/

LONG prda_DeviceQueryFonts( HDC hdc, ULONG ulOptions, PSZ pszFaceName,
                            PFONTMETRICS pfmFontMetrics, ULONG ulNumBytes,
                            PULONG pulNumFonts, PDDC pddc, ULONG FunN)
{
  PFONTMETRICS pfmSrcFont;
  LONG         lMatch;
  PFNT         pFnt;
  PFONTDEF     pfdFont;
  SHORT        usStartFont, usHowManyFonts;
  SHORT        sIndex;
  ULONG        ulNumCopied;
  ULONG        ulOurFMsize;  
  register     SHORT i;      

  EnterDriver( pddc );

  #if      DEBUG
    LogCall( "prda_DeviceQueryFonts(%08lx, %lp, %lp, %08lx)\n", ((PB)&hdc)+sizeof
            (hdc) );
    (pszFaceName) ? PrintLog( (PSZ) "FaceName = %ls\n", pszFaceName) :
                    PrintLog( (PSZ) "NULL Facename\n" );
  #endif  /* DEBUG                                            */

  if (pddc->iType == OD_MEMORY)
  {
    /*
    ** Device fonts can't be realized for memory DC.
    */
    *pulNumFonts = 0 ;
    ExitDriver( pddc );
    return( 0 );
  }

  PrintLog( (PSZ) "ulNumBytes = %ld\n", ulNumBytes );
  PrintLog( (PSZ) "NumFonts = %ld\n", *pulNumFonts );

  /*
  ** The driver returns failure if its being asked for only
  ** private fonts because the driver doesn't have any private
  ** fonts.  All driver fonts are public fonts.
  */
  if ((ulOptions&QF_PRIVATE) && (!(ulOptions&QF_PUBLIC)))
  {
    ExitDriver( pddc );
    return( 0L );
  }

/*
** PTR OSDD SM10841 - the FONTMETRICS structure has increased by
** 8 bytes in 2.0 we just fill in what we know.
**
**
**     If number of bytes requested from each font's metrics is greater
**     than the size of a font metrics structure, we return an error.
**
**
**  if ( ulNumBytes > sizeof(FONTMETRICS) )
**  {
**      GplErrSetError (   PMERR_INV_LENGTH_OR_COUNT ) ;
**      return( ERROR_NEGONE) ;
**  }
*/

  ulOurFMsize = min(ulNumBytes, (ULONG)sizeof(FONTMETRICS) );

  /*
  ** If the facename is null, then every font is said to have matched.
  */
  if (pszFaceName == NULL)
  {
    usStartFont = 0;                                /* First font      */
    usHowManyFonts = prda_NumPrinterFonts(pddc );   /* Number of fonts */
    PrintLog( (PSZ) "NULL FaceName; Startfont = %d, HowMany = %d\n",
              usStartFont, usHowManyFonts );
  }

  /*
  ** Otherwise, attempt to match the facename to one of our fonts. This
  ** will result in either zero or one matches.
  */
  else
  {
    /*
    **            Just use false - don't know from just name
    */
    if (0 > (sIndex = MatchFaceName(pszFaceName, pddc, FALSE )))
    {
      PrintLog( (PSZ) "Couldn't match facename %ls\n", pszFaceName );
      usStartFont = 0;
      usHowManyFonts = 0;
    }
    else                     /* Found a matching font                       */
    {
      usStartFont = (SHORT) sIndex;
      usHowManyFonts = 1;
      PrintLog( (PSZ) "%ls: Startfont = %d, HowMany = 1\n", pszFaceName,
         usStartFont );
    }
  }

  /*
  ** Now we know how many fonts have matched, and where the matched
  ** fonts begin in the font list. The number of fonts we will return is
  ** either the number that matched or the number the caller asked for,
  ** whichever is less. Once we know how many to return, loop through
  ** that many times, and then return the "extra" number.
  */
  ulNumCopied = min( (ULONG) usHowManyFonts, *pulNumFonts );
  PrintLog( (PSZ) "Caller asked for %ld fonts, and %d fonts actually ",
            *pulNumFonts, usHowManyFonts );
  PrintLog( (PSZ) "matched.\nSo we will return %ld fonts.\n", ulNumCopied );

  while (usHowManyFonts && *pulNumFonts)
  {
    /*
    ** Load a font's resource so we can get at its metrics
    */
    pFnt = PfntFromIndex( pddc, usStartFont );

    /*           
    ** Want to avoid duplicate font names.  Dup OFM fonts will be marked with
    ** with -1s so we ignore them
    */
    if ( (SHORT) pFnt->usResource >= 0 )
    {
      pfdFont = prda_LoadFontResource(pddc, usStartFont );

      if (!pfdFont)            /* Couldn't get resource                       */
      {
        ExitDriver( pddc );
        return( ERROR_NEGONE ); /* Minor function saves error code             */
      }
      pfmSrcFont = GetPFMetrics(pfdFont );

      /*
      ** Convert the index of the font into a "magic cookie" which
      ** we will return in the lMatch field of the font metrics, so that
      ** the caller can identify this font in the future.
      */
      lMatch = OffsetToCookie((LONG)usStartFont );

      /*
      ** Copy metrics to the caller's buffer, converting to world
      ** coordinates. Then bump pointers for the next iteration.
      **
      **    PTR OSDD SM10841 zero out the unused part
      */
      /*
      ** Copy metrics to the caller's buffer, converting to world
      ** coordinates. Then bump pointers for the next iteration.
      */
      for (i = sizeof(FONTMETRICS ); i < (SHORT) ulNumBytes; i++)
      {
        *((PB)pfmFontMetrics+i) = 0;
      }
      prda_CopyMetrics( pfmFontMetrics, pfmSrcFont, ulOurFMsize, lMatch, pddc );
      FreeFontResource(pddc->pdv, pfdFont, pFnt);
      PrintLog( (PSZ) "QueryFonts: pfm->szFaceName = %ls\n", (PSZ)
                pfmFontMetrics->szFacename );
      PrintLog( (PSZ) "QueryFonts: lMatch = %ld\n", lMatch );
      usHowManyFonts--;
      (*pulNumFonts)--;
      pfmFontMetrics = (PFONTMETRICS)(((PB)pfmFontMetrics)+ulNumBytes );
    }
    usStartFont++;
  }
  *pulNumFonts = ulNumCopied;
  PrintLog( (PSZ) "Extra fonts matched but not returned = %d\n", usHowManyFonts );
  ExitDriver( pddc );
  return( (LONG)usHowManyFonts );
}

/***************************************************************************
 *
 * FUNCTION NAME =  prda_GetKerningTable
 *
 * DESCRIPTION   =
 * Copies kerning pairs from internal table to caller's buffer.
 *
 * Effects: Scans internal kerning table and copies entries one at a time
 *          to caller's buffer until we reach the end of the table or have
 *          copied the desired number of pairs.  The internal kerning table
 *          lists code points in the font's code page (usually extended
 *          code page 850).  The kerning amount is converted into world
 *          coordinates before being returned to the caller.
 *
 * Warnings: If the font supports extended code page 850 and the caller is
 *           using a code page that is a subset of this, it is possible
 *           for a kerning pair to reference a character that does not
 *           exist in the caller's code page.  Such kerning pairs will
 *           not be returned to the caller.  Therefore it is possible for
 *           the caller to get back fewer kerning pairs than were asked
 *           for, even if the caller asked for the exact number of kerning
 *           pairs offered by the font.
 *
 *           It would be logical for the kerning pairs to be returned to
 *           the caller in sorted order.  However, the specification does
 *           not mention this, so the caller cannot rely on kerning pairs
 *           being returned in any specific order.  For this reason, no
 *           attempt is made to return the kerning pairs in sorted order.
 *
 * INPUT  =  hdc;           Display context handle
 *           ulNumPairs;    Number of kerning pairs desired
 *           pkrnprDest;    Pointer to where pairs shall be stored   ;
 *           pddc;          Pointer to display context instance data ics;
 *           FunN;          Engine function number
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL =  The number of Kerning Pairs.
 *
 * RETURN-ERROR  =  -1
 *
 ****************************************************************************/

LONG  prda_GetPairKerningTable( HDC hdc, ULONG ulNumPairs,
                                PKERNINGPAIRS pkrnprDest, PDDC pddc,
                                ULONG FunN )
{

  PKERNPAIRS pkrnprSrc;      /* Pointer to our kern table                   */
  PKERNINGPAIRS pkrnprTemp;
  PKERNINGPAIRS pkrnprTemp1;
  PB pbTemp;                 /* Temp used to find our kern table            */
  LONG lNumReturned = 0L;    /* How many pairs we actually retn             */
  SHORT sKernTableIndex = 0; /* Index in our kern table                     */
  SHORT sCodePoint1;         /* Kerning pair: first character               */
  SHORT sCodePoint2;         /* Kerning pair: second character              */
  SHORT sKernAmount;         /* Amount to kern between chars                */
  SHORT sTopElement;
  SHORT sCurElement;
  SHORT us1,us2;
  ULONG tl;

  EnterDriver(pddc);

  if (pddc->iType == OD_MEMORY)
  {
    ExitDriver( pddc );
    return( 0 );
  }

  /*
  ** save it for subsequent sorting
  */
  pkrnprTemp = pkrnprDest;

  /*
  ** if the engine wants to do vector font simulaitons, we must call
  ** back to the engine's default function here.
  */
  if (pddc->pddcb->text.fFontSimulation)
  {
    tl =  (LONG)(*pfnlGetPairKerningTable)( hdc, ulNumPairs, pkrnprDest, pddc, FunN );
    ExitDriver( pddc );
    return ( tl );
  }

  #if      DEBUG
    LogCall( "prda_GetPairKerningTable (%08lx, %lp, %lp, %08lx\n)", ((PB)&hdc) +
             sizeof(hdc));
  #endif

  PrintLog( (PSZ)"Pairs requested = %ld\n", ulNumPairs );

  /*
  ** Make sure the font resource is loaded.
  */
  if (!pddc->pddcb->cgs.fValidFont)
  {
    /*
    ** Couldn't get resources
    */
    if (!prda_CheckFontResource( pddc ))
    {
      ExitDriver( pddc );
      return ( ERROR_NEGONE );    /* Minor function saves error code */
    }

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

  PrintLog( (PSZ)"Number of kerning pairs available = %d\n",
            pddc->pddcb->text.pfmFontMetrics->sKerningPairs );

  /*
  ** Point to start of kerning table
  */
  pbTemp = (PB) pddc->pddcb->text.pfdfFontDef;
  pbTemp += pddc->pddcb->text.pfdfFontDef->Header.usKerningDataOffset;
  pkrnprSrc = (PKERNPAIRS) pbTemp;

  /*
  ** Convert kerning pairs one at a time into world coordinates and
  ** the caller's code page.  Return only the ones that will be valid
  ** in the caller's code page.  Loop until we have as many kerning
  ** pairs as the caller asked for, or we run out of pairs to examine.
  */
  while ((lNumReturned < ulNumPairs) && (sKernTableIndex <
          pddc->pddcb->text.pfmFontMetrics->sKerningPairs))
  {
    /*
    ** The code points listed in a font's kerning table are stored in
    ** the code page of the font (usually 850). Before a kerning pair
    ** may be returned to the caller, the code points must be
    ** translated into the caller's code page.  This is what the
    ** prda_BackMapChar minor function does.
    */

    sCodePoint1 = prda_BackMapChar( pkrnprSrc[sKernTableIndex].g1, pddc );
    sCodePoint2 = prda_BackMapChar( pkrnprSrc[sKernTableIndex].g2, pddc );
    sKernAmount = (SHORT) FxToLong( frmul( pddc->pddcb->text.fxXScale, LongToFx
                          ((LONG)pkrnprSrc[sKernTableIndex].KernUnits)));

    if (!((sCodePoint1 == -1) || (sCodePoint2 == -1)))
    {
      pkrnprDest->sFirstChar = sCodePoint1;
      pkrnprDest->sSecondChar = sCodePoint2;
      pkrnprDest->lKerningAmount = sKernAmount;
      PrintLog((PSZ) "Kerning data[%d] = \042%c\042, \042%c\042, %d\n",
               sKernTableIndex, pkrnprDest->sFirstChar, pkrnprDest->sSecondChar,
               pkrnprDest->lKerningAmount);
      pkrnprDest++;
      lNumReturned++;
    }
    sKernTableIndex++;
  }

  /*
  **  Sort the returned kern pair table in ascending order of sFirstChar as the
  **  major key and sSecondChar as the minor key. Since sorting time is not
  **  important we are using the most inefficient algorithm on earth. Pick up
  **  the lightest and place it at its right place.
  */
  for (sTopElement = 0; sTopElement < (SHORT)lNumReturned; sTopElement++)
  {
    us1 = pkrnprTemp->sFirstChar;
    us2 = pkrnprTemp->sSecondChar;
    pkrnprDest = pkrnprTemp;
    pkrnprTemp1 = pkrnprTemp;

    for (sCurElement = sTopElement + 1; sCurElement < (SHORT) lNumReturned;
       sCurElement++)
    {
      pkrnprDest++;

      if (us1 > pkrnprDest->sFirstChar)
      {
        pkrnprTemp1 = pkrnprDest;
        us1 = pkrnprDest->sFirstChar;
        us2 = pkrnprDest->sSecondChar;
      }
      else if (us1 == pkrnprDest->sFirstChar)
      {
        if (us2 > pkrnprDest->sSecondChar)
        {
          pkrnprTemp1 = pkrnprDest;
          us1 = pkrnprDest->sFirstChar;
          us2 = pkrnprDest->sSecondChar;
        }
      }
    }

    if (pkrnprTemp1 != pkrnprTemp)/* the element at begin is not lightest
                                so replace it with the lightest             */
    {
      sCodePoint1 = pkrnprTemp1->sFirstChar;
      sCodePoint2 = pkrnprTemp1->sSecondChar;
      sKernAmount = pkrnprTemp1->lKerningAmount;
      pkrnprTemp1->sFirstChar = pkrnprTemp->sFirstChar;
      pkrnprTemp1->sSecondChar = pkrnprTemp->sSecondChar;
      pkrnprTemp1->lKerningAmount = pkrnprTemp->lKerningAmount;
      pkrnprTemp->sFirstChar = sCodePoint1;
      pkrnprTemp->sSecondChar = sCodePoint2;
      pkrnprTemp->lKerningAmount = sKernAmount;
    }
    pkrnprTemp++;
  }
  PrintLog( (PSZ)"Pairs returned = %ld\n", lNumReturned );
  ExitDriver( pddc );
  return ( lNumReturned );
}

/***************************************************************************
 *
 * FUNCTION NAME =  prda_QueryWidthTable
 *
 * DESCRIPTION   =
 * Returns the widths of characters in the currently selected font.
 *
 * Effects: Copies a portion of the currently selected font's character
 *          width table into the caller's buffer, converting into world
 *          coordinates and translating points into the caller's code page.
 *
 * Warnings: The width of the default character will be returned in place
 *           of any invalid character.  An invalid character is one with a
 *           code point less than zero, greater than 255, or outside the
 *           range supported by the font.
 *
 *
 *
 * INPUT  =  hdc                 Display context handle
 *           lFirstChar          First character whose width is desired
 *           lSize               Number of character widths desired
 *           pulWidthTable       Buffer to hold the character widths
 *           pddc                Pointer to display context instance data
 *           FunN                Engine function number
 *
 * OUTPUT        = NONE
 *
 *
 *
 *
 * RETURN-NORMAL =  SUCCESS
 *
 * RETURN-ERROR  =  FAILURE
 *
 *
 *
 ****************************************************************************/

ULONG prda_QueryWidthTable( HDC hdc, LONG lFirstChar, LONG lSize,
                            PULONG pulWidthTable, PDDC pddc,
                            ULONG FunN)
  /*
  ** HDC     hdc
  ** LONG    lFirstChar            First char whose width is desired
  ** LONG    lSize                 Number of character widths desired
  ** PULONG  pulWidthTable         Buffer to hold character widths
  ** PDDC    pddc
  ** ULONG   FunN
  */
{
  register SHORT i;
  PB             pbTemp;
  PCHARWIDTH     pcdWidths;       /* Pointer to top of width table        */
  SHORT          sFirst;          /* First char offered by this font      */
  SHORT          sLast;           /* Last char offered by this font       */
  LONG           lScaledWidth;    /* Width of char in world coordinates   */
  SHORT          sCurrentChar;    /* Current char to return width for     */
  SHORT          sRemappedChar;   /* Same, after all remapping is done    */
  PCHARWIDTH     pcdCurWidth;     /* Width of current char of interest    */
  ULONG          tl;

  EnterDriver( pddc );

  if (pddc->iType == OD_MEMORY)
  {
     ExitDriver( pddc );
     return( FAILURE );
  }

  /*
  ** if the engine wants to do vector font simulaitons, we must call
  ** back to the engine's default function here.
  */
  if (pddc->pddcb->text.fFontSimulation)
  {
    tl = (ULONG) (*pfnlQueryWidthTable)( hdc, lFirstChar, lSize,
                                         pulWidthTable, pddc, FunN );
    ExitDriver( pddc );
    return( tl );
  }

  #if      DEBUG
    LogCall( "prda_QueryWidthTable(%08lx, %lp, %lp, %lp, %08lx\n", ((PB)&hdc) +
             sizeof(hdc));
  #endif
  PrintLog( (PSZ)"Requested first codepoint (in current code page) = %ld\n",
            lFirstChar );
  PrintLog( (PSZ)"Widths requested = %ld\n", lSize );

  /*
  ** Confirm that the number of widths requested isn't less than zero
  */
  if ((lSize < 0L) || (lSize > 256L))
  {
    PrintLog( (PSZ) "??? Number of char width requested < 0\n" );
    GplErrSetError(  PMERR_INV_LENGTH_OR_COUNT );
    ExitDriver( pddc );
    return( FAILURE );
  }

  if (lFirstChar < 0L || lFirstChar > 255L)
  {
    PrintLog( (PSZ)"First char requested < 0 || > 255\n" );
    GplErrSetError(  PMERR_INV_FIRST_CHAR );
    ExitDriver( pddc );
    return( FAILURE );
  }

  /*
  ** Prepare for mapping characters between the code pages
  */
  sFirst = pddc->pddcb->text.pfmFontMetrics->sFirstChar;
  sLast = pddc->pddcb->text.pfmFontMetrics->sLastChar;
  sCurrentChar = (SHORT) lFirstChar;

  /*
  ** Make sure the font resource is loaded.
  */
  if (!pddc->pddcb->cgs.fValidFont)
  {
    /*
    ** Couldn't get resources.
    */
    if (!prda_CheckFontResource(pddc))
    {
      ExitDriver( pddc );
      return( FAILURE );      /* Minor function saves error code */
    }

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

  /*
  ** Now we want to copy the width data into the caller's buffer.
  ** The character metrics of the resource file are arranged as follows:
  **
  ** For each character:
  **
  **    0000  byte    Character code for PostScript show command
  **    0001  word    Character width vector ("WX" arguments)
  **
  **
  ** Point to start of char metrics data
  */
  pbTemp = (PB) pddc->pddcb->text.pfdfFontDef;
  pbTemp += pddc->pddcb->text.pfdfFontDef->Header.usCharMetricsOffset;
  pcdWidths = (PCHARWIDTH) pbTemp;

  for (i = 0; i < (SHORT)lSize; i++)
  {
    /*
    ** The character width table is stored in the font resource in
    ** extended code page 850 order if the font offers winthorn multi-
    ** codepage support. In this case we must reorder the width table
    ** so that we return character widths to the caller in their code
    ** page. This involves cycling through the caller's code points,
    ** remapping them one at a time. All of the caller's code points
    ** must be in the range of 0-255; any outside this range will be
    ** mapped to the default character.
    */
    if ((sCurrentChar < 0) || (sCurrentChar > 255))
    {
      sRemappedChar = sFirst+pddc->pddcb->text.pfmFontMetrics->sDefaultChar;
    }
    else
    {
      if (pddc->pddcb->text.pusCurCodePgVector)
      {
        sRemappedChar = (SHORT)*(pddc->pddcb->text.pusCurCodePgVector+
                                 sCurrentChar);
      }
      else
      {
        sRemappedChar = sCurrentChar;
      }

      if ((sRemappedChar < sFirst) || (sRemappedChar > (sFirst + sLast)))
      {
        sRemappedChar = sFirst+pddc->pddcb->text.pfmFontMetrics->sDefaultChar;
      }
    }
    pcdCurWidth = pcdWidths + (sRemappedChar - sFirst);

    /*
    ** Scale width and copy to caller's buffer
    */
    lScaledWidth = FxToLong( frmul( LongToFx( (LONG) pcdCurWidth->wx ),
                             pddc->pddcb->text.fxXScale ) );
    PrintLog( (PSZ) "Width of code point #%d is %d ", sCurrentChar,
                    pcdCurWidth->wx);
    PrintLog( (PSZ) "(scaled width = %ld)\n", lScaledWidth);
    *pulWidthTable++ = lScaledWidth;
    sCurrentChar++;
  }
  ExitDriver( pddc );
  return ( SUCCESS );
}

/***************************************************************************
 *
 * FUNCTION NAME =  prda_SetCodePage
 *
 * DESCRIPTION   =
 * Sets the code page for characters written with the default font.
 *
 * Effects: Stores the requested code page number in the DDC.  If the
 *          default font is presently selected, then the current code
 *          page number is changed as well.
 *
 * Warnings: Note that this routine is only used to change the code page
 *           of the default font; a logical font's code page is selected
 *           at CreateLogicalFont() time, and cannot be changed.
 *
 *           SERIOUS WARNING: IF AN APP REQUESTS AN INVALID CODE PAGE,
 *           GREQUERYCODEPAGEVECTOR() WILL GENERATE A RIP INSTEAD OF
 *           RETURNING A NULL POINTER.  THIS IS EXTREMELY     !!
 *           (This is true as of the 1.1 maintenance release of PM,
 *           in 1.2 this has been fixed)
 *
 * INPUT  = hdc              Display context handle
 *          ulCodePage       Desired code page number
 *          pddc             Pointer to display context instance data
 *          FunN             Engine function number
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL =  SUCCESS
 *
 * RETURN-ERROR  =  FAILURE
 *
 ****************************************************************************/

LONG prda_SetCodePage( HDC hdc, ULONG ulCodePage, PDDC pddc, ULONG FunN )
{
  PSHORT pusNewVector;      /* Pointer to remap vector for new code pg     */
  BYTE   bEntry;
  ULONG  ulOldCP;
  ULONG  ulRet;

  EnterDriver( pddc );

  #if      DEBUG
    LogCall( "prda_SetCodePage (%08lx, %lp, %08lx)\n", ((PB)&hdc)+sizeof(hdc) );
  #endif /* DEBUG                                     */

  PrintLog( (PSZ) "Requested new code page = {%ld}\n", ulCodePage );

  if(pddc->iType == OD_MEMORY)
  {
     ulRet = InnerGreSetCodePage (pddc->hdcMemory, ulCodePage, FunN );
     ExitDriver( pddc );
     return( ulRet );
  }

  /*
  ** Get mapping vector for the requested code page from engine
  */
  pusNewVector = (PSHORT) GreQueryCodePageVector(ulCodePage );

  if (pusNewVector == NULL)
  {
    RIP("SetCodePage: QueryCodePageVector returned NULL pointer" );
    GplErrSetError(  PMERR_INV_CODEPAGE );
    ExitDriver( pddc );
    return( FAILURE );
  }

  /*
  ** If we are currently using the default font, we must also change the
  ** current code page right here and now.
  */
  if (!pddc->pddcb->text.bLogFontEntry)/* 0 = default font is in use now    */
  {
    pddc->pddcb->text.pusCurCodePgVector = pusNewVector;
    PrintLog( (PSZ) "Changed default as well as current code page\n" );
  }

  /*
  ** if the default font is being given a new code page here, then
  ** download the new code page mapping.  make sure we redefine
  ** the font after the new mapping has been saved in the pddc.
  ** ps_DownloadFont relies on the information store in the pddc.
  */
  ulOldCP = (ULONG)pddc->pddcb->text.usDefCodePg;

  if (ulOldCP != ulCodePage)
  {
    /*
    ** Save the new code page in the DC instance data
    */
    pddc->pddcb->text.usDefCodePg = (SHORT) ulCodePage;
    pddc->pddcb->text.pusDefCodePgVector = pusNewVector;

    /*
    ** SetCodePage is supposed to affect only the default font.
    ** call remapcodepage to remap the default font to the new
    ** codepage.
    */
    if (!RemapCodePage(pddc, (SHORT) ulCodePage, 0, (PSZ) pddc->pdv->szDefFont) )
    {
      ExitDriver( pddc );
      return( FAILURE );
    }
    SetFontRemap( 0 );
    ps_SetDownloadedFont(pddc );
  }

  /*
  ** set the default font's CodePage
  */
  pddc->usCodePages[0] = (SHORT) ulCodePage;
  ExitDriver( pddc );
  return( SUCCESS );
}

/***************************************************************************
 *
 * FUNCTION NAME =  prda_GetCodePage
 *
 * DESCRIPTION   =
 * Returns the code page that will be used for characters written with
 * the default font.
 *
 * Warnings: Note that this routine returns the code page for the default
 *           font only; if the current font is a logical font, then the
 *           code page number returned here may not be the same as the
 *           code page number currently in use.
 *
 * INPUT  = hdc        Display context handle
 *          pddc       Pointer to display context instance data
 *          FunN       Engine function number
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL =  Code page number of the default font.
 *
 * RETURN-ERROR  =  NONE
 *
 ****************************************************************************/

LONG prda_GetCodePage( HDC hdc,PDDC pddc,ULONG FunN )
{

  ULONG ulRet;

  EnterDriver( pddc );

  #if      DEBUG
    LogCall( "prda_GetCodePage (%08lx, %lp, %08lx)\n", ((PB)&hdc)+sizeof(hdc) );
  #endif  /* DEBUG                                            */

  if (pddc->iType == OD_MEMORY)
  {
    ulRet = InnerGreGetCodePage( pddc->hdcMemory, FunN );
  }
  else
  {
    ulRet = (ULONG) pddc->pddcb->text.usDefCodePg ;
  }

  ExitDriver( pddc );

  return( ulRet );
}


/***************************************************************************
 *
 * FUNCTION NAME =  RemapCodePage
 *
 * DESCRIPTION   =
 * Action: The driver calls this routine to download a new font
 *         definition to the Postscript printer.
 *
 * INPUT  = hdc        Display context handle
 *          pddc       Pointer to display context instance data
 *          FunN       Engine function number
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL =  TRUE - Success
 *
 * RETURN-ERROR  =  False - Failure
 *                  Error in getting necessary resource.
 *
 ****************************************************************************/

BOOL RemapCodePage( PDDC pddc,SHORT usCP,SHORT usFont,PSZ szFont )
{
  CHAR           szFontName[10];
  PVOID          pResource;
  CHAR           achFailName[128];
  SHORT          uErr,cCP;
  PFONTREMAPCODE pfrcData;
  PSZ            psz;
  PSHORT         pusCodePageVector;
  SHORT          i, j, k;
  PSHORT         pusOffset;
  PCH            pChar;       /* Pointer to character metrics of font        */
  SHORT          sLineLen;
  SHORT          sFirst;
  PBYTE          pbTemp;
  PCHARWIDTH     pcData;
  SHORT          sDefaultChar;
  SHORT          sLast;
  PFNT           pFnt;
  BOOL           bIsOFM;
  SHORT          usSignedCP;

  /*
  ** if we have not yet have a startdoc,
  ** we don't want to do anything.  this is necessary for the following
  ** reasons.  if this is called before a startdoc has been done, nothing
  ** will be output to the output channel, but flags will have been set
  ** which will cause unruly behavior when trying to print characters.
  */
  if (pddc->pdv->fDocStarted == FALSE)
  {
    return( TRUE );
  }

  #ifdef   TESTING
    PrintChannel(pddc, (PSZ) "Entering RemapCodePage\n" );
  #endif                       /* TESTING                                     */

  /*           
  ** Make sure not neg ( from new frame code )
  */
  if ( usCP != NO_CODEPAGE )
    usCP = abs( usCP );

  /*
  ** Get current code page vector.  It had better not be NULL.
  */
  if (!(pusCodePageVector = (PSHORT) GreQueryCodePageVector((ULONG)usCP)))
 {
   /*
   **    if ( !(pusCodePageVector = pddc->pddcb->text.pusCurCodePgVector) )
   */
   RIP("RemapCodePage: NULL codepage vector passed in" );
   return( FALSE );
 }

  #ifdef   TESTING
    PrintChannel(pddc, (PSZ) "RemapCodePage: pusCodePageVector = %lx\n",
                  pusCodePageVector );
  #endif                       /* TESTING                                     */

  PrintLog( (PSZ) "Downloading font %ls\n", (PSZ) szFontName );
  PrintLog( (PSZ) "Loading remap data resource file fntremap.txt\n" );

  /*
  ** get our module handle so we can get the font remapping code,
  ** which is stored as a resource.
  */
  if (uErr = DosGetResource(pscript_module, MYFREMAP, REMAP_COD, &pResource))
  {
    RIP("RemapCodePage: DosGetResource failed" );
    return( FALSE );
  }

  /*
  ** we now have a pointer to our font remapping code.
  */
  pfrcData = (PFONTREMAPCODE)(pResource );

  /*
  ** if the font remapping header has not been downloaded to the
  ** printer, do it now.  this header is sent at most once per job.
  */
  if (!(pddc->pddcb->cgs.fFRedefDownld))
  {
    psz = (PSZ) pfrcData;
    psz += pfrcData->sOffsetRoutine;  /* Point to remap header text           */

    while (*psz)             /* Print text a line at a time                 */
    {
      PrintChannel(pddc, (PSZ) "%ls\n", (PSZ) psz );

      while (*psz)
      {
        psz++;
      }
      psz++;
    }
    pddc->pddcb->cgs.fFRedefDownld = TRUE;
  }

  /*
  ** Make sure the font is there.  This is trivial for a HW font,
  ** but a Soft font must be downloaded before we can redefine it.
  */
  FontDownload( pddc, (PSZ) szFont );

  /*
  ** Create unique name for the new font we will download
  */
  GetMappedFontName( pddc, (PSZ) szFontName, sizeof(szFontName), usFont );

  /*           
  ** Moved the following section up to here so we can see if the font is
  ** a device or soft (OFM) font
  **
  ** If font is device font procede in the old syle remaping
  ** If font is OFM then use the new more complete remapping
  */

  /*
  **                                                                    
  **
  **   Make sure we have a font matrix loaded for the current font.
  **   In the following FOR loop, this statement cases a trap D:
  **      j = pddc->pddcb->text.pfmFontMetrics->sFirstChar   +
  **          pddc->pddcb->text.pfmFontMetrics->sDefaultChar ;
  **   The item "pfmFontMetrics" points to a block of memory, but this
  **   process does not own that memory - thus Trap D.
  */
  prda_CheckFontResource(pddc );

  i = MatchFaceName( szFont, pddc, pddc->pddcb->text.fUseSoftFont );
  pFnt = (PFNT) &(*pddc->pdv->paFonts)[i];

  if ( pFnt->usResource <= 0 )
  {
    bIsOFM = TRUE;
    usSignedCP = -usCP; /* Indicates download font code page maping */
  }
  else
  {
    bIsOFM = FALSE;
    usSignedCP = usCP;  /* Regular old style remaping */
  }

  /*
  ** we now need to look through our table to see if this codepage
  ** has been downloaded to the printer.  if it has, we just
  ** redefine the current font, using the proper codepage.
  */

  if (CPDownLoaded(pddc, usSignedCP))
  {
    goto redefine_cur_font;
  }

  /*
  ** the codepage mapping has not been sent down to the printer.
  ** add the current codepage to our table, then download the
  ** mapping.  There is also a change     here to note.  There is
  ** a limit of 10 codepages being defined at a time.  I am
  ** imposing this to help keep postscript printers from running
  ** out of memory.  once we are asked to download the 11th codepage
  ** it simply replaces the 10th.
  */
  cCP = pddc->pddcb->cgs.cCodePagesRemapped;

  if (cCP >= CP_LIMIT)
  {
    cCP = CP_LIMIT_MINUS_ONE;
  }
  pddc->pddcb->cgs.usCPTable[cCP] = usSignedCP;
  cCP++;
  pddc->pddcb->cgs.cCodePagesRemapped = cCP;

  /*
  **                                                                
  ** count new remapped code page in the current document page.
  */
  ++pddc->pddcb->cgs.usNewCPs;


  /*
  ** End B724427
  */
  /*
  ** download the mapping for the given codepage.
  */
  PrintChannel( pddc, (PSZ) "/CP%d [ 0 ", usCP );
  sLineLen = 13;

  sFirst = pddc->pddcb->text.pfmFontMetrics->sFirstChar;
  sLast  = pddc->pddcb->text.pfmFontMetrics->sLastChar;
  sDefaultChar =  pddc->pddcb->text.pfmFontMetrics->sDefaultChar;


  if ( bIsOFM )   /*            */
  {
    pbTemp = (PBYTE)pddc->pddcb->text.pfdfFontDef;
    pbTemp += pddc->pddcb->text.pfdfFontDef->Header.usCharMetricsOffset;
  }

  for (i = 0; i < 256; i++)
  {
    /*
    ** If the line is too long, start a new one
    */
    if (sLineLen > 66)
    {
      PrintChannel( pddc, (PSZ) "\n  " );
      sLineLen = 2;
    }

    if ( ! bIsOFM ) /*            old style remaping */
    {
      if (i < 0x11 || i > 0x1B || i == 0x14 || i == 0x15)
      {
        j = *(pusCodePageVector + i );
        if ((j <= 0) || (j >= pfrcData->sNumGlyphNames))
        {
          j = sFirst + sDefaultChar;
        }
      }
      else
      {
        switch (i)
        {
        case 0x11:
             j = GRAVE;
             break;

        case 0x12:
             j = ACUTE;
             break;

        case 0x13:
             j = CIRCUMFLEX;
             break;

        case 0x16:
             j = TILDE;
             break;

        case 0x17:
             j = DIERESIS;
             break;

        case 0x18:
             j = RING;
             break;

        case 0x19:
             j = CEDILLA;
             break;

        case 0x1A:
             j = CARON;
             break;

        case 0x1B:
             j = DOTLESSI;
             break;
        }
      }

      /*
      ** Point to corresponding glyph name as known by PostScript and print
      */
      pusOffset = (PSHORT) &pfrcData->sOffsetpGlyph;
      psz = (PSZ) pfrcData;
      psz += *(pusOffset+j );

    }
    else  /* ofm case */
    {
      j = *(pusCodePageVector + i );

      if ((j <= 0) || (j > sLast))
      {
        j = sFirst + sDefaultChar;
      }
      else    /* Make sure CP is defined */
      {
        pcData = (PCHARWIDTH)pbTemp;
        pcData += ( j - sFirst );  /* point to char's metrics  */

        if ( pcData->glyph == 32 )
        {
          j = sFirst + sDefaultChar;
        }
      }

      /*
      ** Point to corresponding glyph name as known by PostScript and print
      */
      psz = (PSZ)PMGlyphName[j];
    }

    PrintChannel(pddc, (PSZ) "/%ls", (PSZ) psz );

    /*
    ** Update the line length
    */
    sLineLen +=  strlen(psz) + 1;
  }
  PrintChannel( pddc, (PSZ) "\n] def\n" );

  /*
  ** redefine the current font using the codepage mapping we
  ** just downloaded to the printer.
  */

redefine_cur_font:

  /*
  ** output the postscript commands to redefine the current font
  ** using the current codepage mapping.
  */
  PrintChannel( pddc, (PSZ) "CP%d /%ls /%ls redefine\n", usCP, (PSZ) szFontName,
                (PSZ) FullToFontName((PSZ) szFont, pddc) );
  szCopy( pddc->pddcb->cgs.szFont, (PSZ) (szFontName), sizeof
          (pddc->pddcb->cgs.szFont) );

  #ifdef   TESTING
    PrintChannel(pddc, (PSZ) "Leaving ps_RemapCodePage\n" );
  #endif                       /* TESTING                                     */

  return( TRUE );
}

/***************************************************************************
 *
 * FUNCTION NAME =  CPDownLoaded
 *
 * DESCRIPTION   =
 * This routine returns TRUE if the passed in codepage has been.
 * downloaded to the printer.  Otherwise, it returns FALSE.
 *
 * INPUT  = pddc       Pointer to display context instance data
 *          usCP       Code page
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL =  TRUE - Success
 *
 * RETURN-ERROR  =  False - Failure
 *
 ****************************************************************************/

BOOL CPDownLoaded( PDDC pddc, SHORT usCP )
{
  SHORT i, cCP;

  /*
  ** bag out if no codepages have been downloaded.
  */
  cCP = pddc->pddcb->cgs.cCodePagesRemapped;

  if (cCP == 0)
  {
    return( FALSE );
  }

  /*
  ** some codepages have been remapped.  we need to look through
  ** our table to see if the current one has been downloaded.
  */
  for (i = 0; i < cCP; i++)
  {
    if (pddc->pddcb->cgs.usCPTable[i] == usCP)
    {
      return( TRUE );
    }
  }

  /*
  ** the current code page was not found in the table.
  */

  return( FALSE );
}
