/*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.                                */
/*                                                                           */
/*****************************************************************************/
/*
** NOTE:  This file has been removed from the 32-bit driver, but has been
** included as an example for font usage in a driver.
*/

#if 0
/**************************************************************************
 *
 * SOURCE FILE NAME =   AFMTOPFM.C
 *
 * DESCRIPTIVE NAME =   AFM (Adobe Font Metrics) file to PFM file conversion utility.
 *
 *
 * VERSION = V2.0
 *
 * DATE      02/25/88
 *
 * DESCRIPTION  POSTSCRIPT SOURCE
 *
 *
 * FUNCTIONS
 *
 *   szMove()             Copies a string.
 *   UnGetLine()          This routine pushes the most recent line back into the input buffer.
 *   EatWhite()           This routine moves the input buffer pointer forward
 *   GetLine()            This routine gets the next non-empty line of text
 *   GetWord()            Get the next word from input buffer
 *   MapToken()           This routine maps an ascii key word into an enumerated token.
 *   GetNumber()          This routine parses an ASCII decimal number
 *   GetFloat()           This routine parses an ASCII floating point decimal number
 *   GetToken()           Get the next token from the input stream.
 *   GetCharCode()        Determines code point of a Postscript character
 *   GetNextCharCode()    Determines code point of a Postscript character
 *   Swap                 Swap values
 *   QSort                Sort
 *   ParseKernPairs()     Parse the pairwise kerning data.
 *   ParseKernData()      Start processing the pairwise kerning data.
 *   ParseString()        Move the next string from the line buffer
 *   ParseWeight()        Parse the fonts weight and set the corresponding entry
 *   ParseCharWidth()     Parse a character's width and return its numeric value.
 *   ParseCharCode()      Parse the ascii form of a character's code point
 *   ParseCharBox()       Parse the character's bounding box
 *   ParseCharName()      Parse a character's name
 *   ParseCharMetrics()   Parse the character metrics entry in the input file
 *   ParseBoundingBox()   Parse a character's bounding box
 *   ParsePitchType()     Parse the pitch type and set the variable pitch
 *   OpenAfm()            Initialize the afm structure, and open the files.
 *   FixCharWidths()
 *   SetAfm()             Finish setting the character and font metrics in the afm
 *   EvenUp()             Word-align the output buffer.
 *   PutByte()            This function writes a byte to the output buffer.
 *   PutWord()            This function writes a word to the output buffer.
 *   PutString()          This function writes a null terminated string to the output buffer.
 *   WriteOutput()        This function writes the device font info structure
 *   GetNextWord          Copy next word from source to destination buffer
 *   StrCopy              Copy finite or NULL terminated string to destination
 *   ParseItalic          Parse the input string for the 'italic' key-word
 *   ParseFamilyClass     Parse the input string for the various family
 *
 *
 *
 *
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
 *
 *              BOOL   szIsEqual( PSZ, PSZ );
 *              PVOID   prdg_AllocMem( SHORT );
 *              VOID   prdg_FreeMem( PVOID );
 *
 *
 *
 *
 *
 *
*/
#define INCL_32
#define  INCL_DOSFILEMGR
#include <stdlib.h>
#include <os2.h>
#include "inc\afmtopfm.h"
#include "inc\mapping.h"

/*
** Constants
*/

#define  CR            0x0D
#define  LF            0x0A
#define  ARBITRARY_CODEPAGE 65400      /* IBM CODEPAGE number for arbitrary */
                                       /*   glyphs                          */
#define  OPEN_AFM_FLAGS OPEN_ACCESS_READONLY  | OPEN_SHARE_DENYNONE  | \
OPEN_FLAGS_FAIL_ON_ERROR  | OPEN_FLAGS_RANDOMSEQUENTIAL
#define  OPEN_PFM_FLAGS OPEN_ACCESS_WRITEONLY  | OPEN_SHARE_DENYNONE  | \
OPEN_FLAGS_FAIL_ON_ERROR  | OPEN_FLAGS_SEQUENTIAL

/*
** Globals in DATAR3
*/
static BOOL fEOF;
static BOOL fUnGetLine;
static PCD  charmap;                   /* the conversion table from Adobe   */
                                       /* character  names into application code points */
static CHAR rgbLine[MAX_LINESIZE];     /* current line of text being        */
                                       /* processed                         */
static PSZ pszLine;                    /* ptr to the current location in    */
                                       /* the line                          */
static PSZ pbBuffin;                   /* ptr to current location in input  */
                                       /* buffer                            */
static SHORT cbBuffout;                /* number of bytes in output buffer  */
static PSZ pbBuffout;                  /* ptr to current location in output */
                                       /* buffer                            */
AfmData afmDefault =
{
  0,                                   /* Word version;*/
  0,                                   /* int iMaxWidth;*/
  0,                                   /* int iCapHeight; */
  FALSE,                               /* BOOL fVariablePitch;*/
  {
    0
  } ,                                  /* Rect rcBBox; */
  {
    0
  } ,                                  /* char szFontName[MAX_STR]; */
  {
    0
  } ,                                /* char szEncodingScheme[MAX_STR];*/
  {
    0
  } ,                                  /* char szComment[MAX_STR]; */
  {
    0
  } ,                                  /* char szWeight[MAX_STR]; */
  {
    0
  } ,                                  /* char szVersion[MAX_STR]; */
  {
    0
  } ,                                  /* char szNotice[MAX_STR]; */
  {
    -1
  } ,          /* CMData rgcm[MAX_CHARS];*//* The character metrics */
  0,     /* int cKPairs; *//* the number of kerning pairs */
  {
    0
  }                                    /* KPair rgKPairs[MAX_KPAIRS]; */
} ;

static AfmData afm;
FONTMETRICS fmDefault =
{
  {
    0
  } ,                                  /* CHAR szFamilyname[FACESIZE]; */
  {
    0
  } ,                                  /* CHAR szFacename[FACESIZE]; */
  0,                                   /* SHORT idRegistry; */
  CODE_PAGE,                           /* SHORT usCodePage; */
  1000L,                               /* LONG lEmHeight; */
  0L,                                  /* LONG lXHeight; */
  0L,                                  /* LONG lMaxAscender; */
  0L,                                  /* LONG lMaxDescender; */
  0L,                                  /* LONG lLowerCaseAscent; */
  0L,                                  /* LONG lLowerCaseDescent; */
  0L,                                  /* LONG lInternalLeading; */
  0L,                                  /* LONG lExternalLeading; */
  0L,                                  /* LONG lAveCharWidth; */
  0L,                                  /* LONG lMaxCharInc; */
  1000L,                               /* LONG lEmInc; */
  0L,                                  /* LONG lMaxBaselineExt; */
  0,                                   /* SHORT sCharSlope; */
  0,                                   /* SHORT sInlineDir; */
  0,                                   /* SHORT sCharRot; */
  FW_NORMAL,                           /* SHORT usWeightClass; */
  5,                                   /* SHORT usWidthClass; */
  1000,                                /* SHORT sXDeviceRes; */
  1000,                                /* SHORT sYDeviceRes; */
  FIRST_CHAR,                          /* SHORT sFirstChar; */
  LAST_CHAR,                           /* SHORT sLastChar; */
  31,                                  /* SHORT sDefaultChar; */
  31,                                  /* SHORT sBreakChar; */
  120,                                 /* SHORT sNominalPointSize; */
  10,                                  /* SHORT sMinimumPointSize; */
  7200,                                /* SHORT sMaximumPointSize; */
  0,                                   /* SHORT fsType; */
  FM_DEFN_OUTLINE,                     /* SHORT fsDefn; */
  0,                                   /* SHORT fsSelection; */
  0L,                                  /* SHORT fsCapabilities; */
  0L,                                  /* LONG lSubscriptXSize; */
  0L,                                  /* LONG lSubscriptYSize; */
  0L,                                  /* LONG lSubscriptXOffset; */
  0L,                                  /* LONG lSubscriptYOffset; */
  0L,                                  /* LONG lSuperscriptXSize; */
  0L,                                  /* LONG lSuperscriptYSize; */
  0L,                                  /* LONG lSuperscriptXOffset; */
  0L,                                  /* LONG lSuperscriptYOffset; */
  0L,                                  /* LONG lUnderscoreSize; */
  0L,                                  /* LONG lUnderscorePosition; */
  0L,                                  /* LONG lStrikeoutSize; */
  0L,                                  /* LONG lStrikeoutPosition; */
  0,                                   /* SHORT sKerningPairs; */
  0,                                   /* SHORT sFamilyClass; */
  0L                                   /* LONG lMatch; */
} ;

static FONTMETRICS fmFont;



/*
** Function Prototypes
*/
extern BOOL  _System szIsEqual(PSZ,PSZ);
extern PVOID   prdg_AllocMem(SHORT);
extern VOID   prdg_FreeMem(PVOID);


/*
**  Internal Subroutines
*/
/***************************************************************************
 *
 * FUNCTION NAME =  szMove()
 *
 * DESCRIPTION   =  Copies a string.  This function will copy at
 *                  most the number of bytes in the destination area - 1.
 *
 * INPUT         = szDst,szSrc,cbDst
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

VOID   szMove( PSZ szDst, PSZ szSrc, SHORT cbDst )

/*  PSZ szDst;                            Ptr to the destination area       */
/*  PSZ szSrc;                            Ptr to the source area            */
/*  SHORT cbDst;                          The size of the destination area  */

{

  while (*szDst++ = *szSrc++)
  {
    if (--cbDst <= 0)
    {
      *(szDst-1) = 0;
      break;
    }
  }
}

/***************************************************************************
 *
 * FUNCTION NAME = UnGetLine()
 *
 * DESCRIPTION   = This routine pushes the most recent line back into the input buffer.
 *
 * INPUT         = NONE
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

VOID UnGetLine ()
{
  fUnGetLine = TRUE;
  pszLine    = rgbLine;
}

/***************************************************************************
 *
 * FUNCTION NAME = EatWhite()
 *
 * DESCRIPTION   = This routine moves the input buffer pointer forward to the
 *                 next non-white character.
 *
 * INPUT         = NONE
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

VOID EatWhite ()
{
  while (*pszLine == ' ' || *pszLine == '\t')
  {
    ++pszLine;
  }
}

/***************************************************************************
 *
 * FUNCTION NAME = GetLine()
 *
 *
 * DESCRIPTION   = This routine gets the next non-empty line of text
 *                 out of the input buffer and returns a flag
 *                 indicating "Done?"; TRUE iff the last line has
 *                 been moved.
 *
 * INPUT         = NONE
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = TRUE
 *
 * RETURN-ERROR  = FALSE
 *
 **************************************************************************/

BOOL GetLine ()
{
  PSZ pTmp;

  pszLine = pTmp = rgbLine;

  if (fUnGetLine)
  {
    return (fUnGetLine = FALSE);
  }

  do
  {
    if (*pbBuffin == '\0')
    {
      return (fEOF = TRUE);
    }

    /*
    **  move chars until end of line. Since .AFM files can be
    **  created by ANY ascii editor, we don't know how EOL might
    **  be encoded; so we check CR,LF,CRLF,LFCR.  We may skip a
    **  blank line here, but that's ok.  Fixes OSDD PTR HM01021
    */
    /*
    **  move chars until end of line. Since .AFM files can be
    **  created by ANY ascii editor, we don't know how EOL might
    **  be encoded; so we check CR,LF,CRLF,LFCR.  We may skip a
    **  blank line here, but that's ok.
    */

    while ((*pbBuffin != CR) && (*pbBuffin != LF))
    {
      *pTmp++ = *pbBuffin++;
    }

    if ((*pbBuffin == LF) || (*pbBuffin == CR))
    {
      pbBuffin++;
    }
    *pTmp = '\0';                      /* null-terminater */
    EatWhite ();                       /* discard leading spaces */
  }

  while (*pszLine == '\0');            /* empty line? Try again. */
  return  fEOF;
}

/***************************************************************************
 *
 * FUNCTION NAME = GetWord()
 *
 * DESCRIPTION   = Get the next word from input buffer
 *
 *                 This routine gets the next word delimited
 *                 by white space from the input buffer.
 *
 * INPUT         = pszWord,cbWord
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

VOID GetWord( PSZ pszWord, SHORT cbWord )
 /* PSZ pszWord;                          ptr to the destination area       */
 /* SHORT cbWord;                         the size of the destination area  */
{
  CHAR bCh;

  EatWhite ();

  while (--cbWord > 0)
  {
    switch (bCh = *pszLine++)
    {
    case 0:
         --pszLine;
         goto DONE;

    case ' ':
    case '\t':
         --pszLine;
         goto DONE;

    case ';':
         *pszWord++ = bCh;
         goto DONE;
    default:
         *pszWord++ = bCh;
         break;
    }
  }

DONE:

  *pszWord = 0;
}

/***************************************************************************
 *
 * FUNCTION NAME = MapToken()
 *
 *
 * DESCRIPTION   = This routine maps an ascii key word into an enumerated token.
 *
 *
 * INPUT         = pszWord
 *
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = The token value.
 *
 *
 *
 * RETURN-ERROR  = NONE
 *
 *
 *
 **************************************************************************/

TokType MapToken( PSZ pszWord )
 /* PSZ pszWord;                          ptr to the ascii keyword string   */
{
  TokType iToken = 0;

  while (iToken < tok_max)
  {
    if (szIsEqual((PSZ)pszWord,
                  (PSZ)tokens[iToken]))
    {
      return (iToken);
    }
    ++iToken;
  }
  return (tok_unknown);
}

/***************************************************************************
 *
 * FUNCTION NAME = GetNumber()
 *
 *
 * DESCRIPTION   = This routine parses an ASCII decimal number from the
 *                 input file stream and returns its value.
 *
 * INPUT         = NONE
 *
 *
 *
 * OUTPUT        = NONE
 *
 *
 * RETURN-NORMAL = iVal
 *
 *
 *
 * RETURN-ERROR  = NONE
 *
 *
 *
 **************************************************************************/

SHORT GetNumber ()
{
  SHORT iVal;
  BOOL  fNegative;
  CHAR  chDigit;

  iVal = 0;
  EatWhite ();

  if (fNegative = ((chDigit = *pszLine) == '-'))
  {
    chDigit = *(++pszLine);
  }

  while (chDigit >= '0' && chDigit <= '9')
  {
    iVal = iVal *10 + (chDigit - '0');
    chDigit = *(++pszLine);
  }

  if (fNegative)
  {
    iVal = -iVal;
  }

  return (iVal);
}

/***************************************************************************
 *
 * FUNCTION NAME = GetFloat()
 *
 * DESCRIPTION   = This routine parses an ASCII floating point decimal number
 *                 from the input file stream and returns its value scaled by 10.
 *
 * INPUT         = NONE
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = lVal
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

SHORT GetFloat( VOID )
{
  SHORT lVal, lDivisor;
  BOOL  fNegative;
  CHAR  chDigit;

  EatWhite ();
  lVal = 0L;

  if (fNegative = ((chDigit = *pszLine) == '-'))
  {
    chDigit = *(++pszLine);
  }

  while (chDigit >= '0' && chDigit <= '9')
  {
    lVal = (lVal + (chDigit - '0')) * 10;
    chDigit = *(++pszLine);
  }

  if (chDigit == '.')
  {
    chDigit = *(++pszLine);

    if (chDigit >= '0' && chDigit <= '9')
    {
      lVal += (chDigit - '0');
      chDigit = *(++pszLine);

      if (chDigit >= '5' && chDigit <= '9')
      {
        ++lVal;
      }
    }
  }

  if (fNegative)
  {
    lVal = -lVal;
  }

  return (lVal);
}

/***************************************************************************
 *
 * FUNCTION NAME = GetToken()
 *
 * DESCRIPTION   = Get the next token from the input stream.
 *
 * INPUT         = NONE
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = MapToken((PSZ)szWord)
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

SHORT GetToken ()
{
  CHAR szWord[80];

  if (*pszLine == 0)
  {
    return (tok_max);
  }
  GetWord( (PSZ) szWord, sizeof (szWord) );
  return (MapToken( (PSZ) szWord ) );
}

/***************************************************************************
 *
 * FUNCTION NAME = GetCharCode()
 *
 * DESCRIPTION   = Determines code point of a Postscript character
 *                 Given a name of a Postscript character, this
 *                 function determines what its code point shall
 *                 be (in the sense of winthorn multi- code page
 *                 support) and what character code must be
 *                 output in a Postscript "show" command in
 *                 order to display this character.  If the
 *                 latter is zero, then a complete Postscript
 *                 font remapping is necessary in order to
 *                 display this character.
 *
 * INPUT         = pcd
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

VOID GetCharCode( PCD pcd )
{
  PCD p;

  p = charmap;

  while (p->pszName)
  {
    if (szIsEqual((PSZ)pcd->pszName,
                  (PSZ)p->pszName))
    {
      pcd->iCP850Code   = p->iCP850Code;
      pcd->ipscriptCode = p->ipscriptCode;
      return;
    }
    ++p;
  }
  pcd->iCP850Code = 0;
  return ;
}

/***************************************************************************
 *
 * FUNCTION NAME = GetNextCharCode()
 *
 * DESCRIPTION   = Determines code point of a Postscript character
 *
 *                 This is similar to GetCharCode() above.  But
 *                 one Adobe character name may map to several
 *                 code page 850 code points.  This function
 *                 continues scanning the list and returns matches
 *                 until the end of the list is reached, at which
 *                 time a null pointer is returned.
 *
 * INPUT         = pcd,pcdRemainingList
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = 0
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

PCD  GetNextCharCode( PCD pcd, PCD pcdRemainingList )
{
  PCD p;

  p = pcdRemainingList;

  while (p->pszName)
  {
    if (szIsEqual( (PSZ) pcd->pszName,
                  (PSZ) p->pszName ) )
    {
      pcd->iCP850Code   = p->iCP850Code;
      pcd->ipscriptCode = p->ipscriptCode;
      return (p + 1);
    }
    ++p;
  }
  pcd->iCP850Code = 0;
  return 0;
}

/***************************************************************************
 *
 * FUNCTION NAME = Swap
 *
 * DESCRIPTION   = Swap values
 *
 * INPUT         = (pK1,pK2)
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

typedef KPair  *PKPair;
VOID   Swap( PKPair pK1, PKPair pK2 )
{
  KPair kTmp;

  kTmp = *pK1;
  *pK1 = *pK2;
  *pK2 = kTmp;
  return;
}

/***************************************************************************
 *
 * FUNCTION NAME = QSort
 *
 * DESCRIPTION   = Sort
 *
 * INPUT         = (pBase,cNum)
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

VOID QSort( PKPair pBase, SHORT cNum )
{
  PKPair pFirst, pLast, pEnd;
  SHORT  sPivKey;


  if (cNum <= 2)
  {
    if (cNum == 2 && (pBase->iKey1 > (pBase+1)->iKey1))
    {
      Swap (pBase, pBase + 1 );
    }
    return ;
  }

  pEnd = pBase +(cNum / 2);            /* pivot pointer */
  sPivKey = pEnd->iKey1;               /* pivot value */
  Swap (pBase, pEnd);                  /* save pivot as a[0] */
  pFirst = pBase + 1;
  pLast = pEnd = pBase + cNum - 1;
                                        /* pFirst = &a[1], pLast = &a[end] */

  while (TRUE)
  {
    /*
    ** *pFirst = first element > sPivKey
    ** or pBase
    */
    while ((pFirst->iKey1 <= sPivKey) && (pFirst < pEnd))
    {
      pFirst++;
    }

    /*
    ** *pLast = last element < sPivKey
    ** or pBase
    */
    while ((sPivKey <= pLast->iKey1) && (pLast > pBase))
    {
      pLast--;
    }

    if (pFirst >= pLast)
    {
      break;
    }
    Swap( pFirst, pLast );
  }

  /*
  ** exit conditions:
  ** sPivKey = min(array) ==> pBase = pLast = pFirst < pEnd
  **
  **   normally             ==> pBase <= pLast < pLast+1 <= pFirst <= pEnd
  **
  **   sPivKey = max(array) ==> pBase < pLast = pFirst = pEnd
  */
  if (pBase < pLast)
  {
    Swap( pBase, pLast );
  }

  /*
  ** Sort bottom to pivot - 1
  */
  QSort( pBase, (pLast - pBase) );

  /*
  ** sort (pivot+n+1) to top, where n
  ** elements == sPivKey aren't sorted
  */
  QSort( pFirst, (pEnd - pFirst + 1) );
}

/***************************************************************************
 *
 * FUNCTION NAME = ParseKernPairs()
 *
 * DESCRIPTION   = Parse the pairwise kerning data.
 *
 * INPUT         = NONE
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

VOID ParseKernPairs ()
{
  SHORT    i;
  SHORT    iCh1;
  SHORT    iCh2;
  TokType  iToken;
  SHORT    cPairs;
  CHAR     szWord[80];
  CharData cd1, cd2;

  cPairs = GetNumber ();
  i = 0;

  while (i < cPairs)
  {
    if (GetLine ())
    {
      break;
    }

    if (GetToken () != tok_kpx)
    {
      UnGetLine ();
      break;
    }
    GetWord( (PSZ) szWord, sizeof (szWord) );
    cd1.pszName = (PSZ) szWord;
    GetCharCode( (PCD) &cd1 );
    iCh1 = cd1.ipscriptCode;
    GetWord( (PSZ) szWord, sizeof (szWord) );
    cd2.pszName = (PSZ) szWord;
    GetCharCode( (PCD) &cd2 );
    iCh2 = cd2.ipscriptCode;

    /*
    ** If either character is not in codepage 850 then throw away this
    ** kerning pair.
    */
    if (iCh1 == 0 || iCh2 == 0)
    {
      --cPairs;
      continue;
    }
    afm.rgKPairs[i].sAmount = GetNumber ();
    afm.rgKPairs[i].iKey1 = iCh1;
    afm.rgKPairs[i].iKey2 = iCh2;
    ++i;
  }
  fmFont.sKerningPairs = afm.cKPairs = cPairs;
  GetLine ();
  iToken = GetToken ();

  if (fEOF)
  {
    return;
  }
  else if (iToken != tok_endkernpairs)
  {
    return;
  }

  QSort( (PVOID) &afm.rgKPairs[0], cPairs );
}

/***************************************************************************
 *
 * FUNCTION NAME = ParseKernData()
 *
 * DESCRIPTION   = Start processing the pairwise kerning data.
 *
 * INPUT         = NONE
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

VOID ParseKernData ()
{
  if (!GetLine () && (GetToken () == tok_startkernpairs))
  {
    ParseKernPairs ();
  }
}

/***************************************************************************
 *
 * FUNCTION NAME = ParseString()
 *
 * DESCRIPTION   = Move the next string from the line buffer
 *                 into the afm structure.
 *
 * INPUT         = psz,maxsize
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

VOID ParseString( PSZ psz, SHORT maxsize )
{
  EatWhite ();
  szMove( (PSZ) psz, (PSZ) pszLine, maxsize );
}

/***************************************************************************
 *
 * FUNCTION NAME = ParseWeight()
 *
 * DESCRIPTION   = Parse the fonts weight and set the corresponding entry
 *                 in the afm structure.
 *
 * INPUT         = NONE
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

VOID ParseWeight ()
{
  CHAR szWord[80];

  GetWord( (PSZ) szWord, sizeof (szWord) );

  if (szIsEqual( (PSZ)szWord, (PSZ) "Medium" ) ||
      szIsEqual( (PSZ) szWord, (PSZ) "Roman" ) || szIsEqual( (PSZ) szWord,
                                                            (PSZ) "Regular" ) ||
      szIsEqual( (PSZ) szWord, (PSZ) "MediumCondensed" ) )
  {
    fmFont.usWeightClass = FW_NORMAL;
  }
  else if (szIsEqual ((PSZ) szWord, (PSZ) "Bold") ||
           szIsEqual ((PSZ) szWord, (PSZ) "BoldCondensed") )
  {
    fmFont.usWeightClass = FW_BOLD;
  }
  else if (szIsEqual( (PSZ) szWord, (PSZ) "Demi") )
  {
    fmFont.usWeightClass = FW_SEMIBOLD;
  }
  else if (szIsEqual( (PSZ) szWord, (PSZ)"Light") )
  {
    fmFont.usWeightClass = FW_LIGHT;
  }
  else if (szIsEqual( (PSZ) szWord, (PSZ) "Book") )
  {
    fmFont.usWeightClass = FW_SEMILIGHT;
  }
  else if (szIsEqual( (PSZ) szWord, (PSZ)"Black") )
  {
    fmFont.usWeightClass = FW_ULTRABOLD;
  }

  szMove( (PSZ) afm.szWeight, (PSZ) szWord, sizeof (szWord) );
}

/***************************************************************************
 *
 * FUNCTION NAME = ParseCharWidth()
 *
 * DESCRIPTION   = Parse a character's width and return its numeric value.
 *
 * INPUT         = NONE
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = iWdth
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

SHORT ParseCharWidth ()
{
  CHAR           szWord[16];
  register SHORT iWdth;

  GetWord( (PSZ) szWord, sizeof (szWord) );

  if (szIsEqual( (PSZ) tokens[tok_wx], (PSZ) szWord ))
  {
    iWdth = GetNumber ();

    if (iWdth == 0)
    {
      return (iWdth);
    }
    EatWhite ();

    if (*pszLine++ != ';')
    {
      return (iWdth);
    }
  }

  return (iWdth);
}

/***************************************************************************
 *
 * FUNCTION NAME = ParseCharCode()
 *
 * DESCRIPTION   = Parse the ascii form of a character's code point and
 *                 return its numeric value.
 *
 * INPUT         = NONE
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = iChar
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/


SHORT ParseCharCode ()
{
  SHORT iChar;
  CHAR  szWord[16];

  iChar = 0;
  GetWord( (PSZ) szWord, sizeof (szWord) );

  if (szIsEqual( (PSZ) "C", (PSZ) szWord) )
  {
    iChar = GetNumber ();

    if (iChar == 0)
    {
      return (iChar);
    }

    EatWhite ();

    if (*pszLine++ != ';')
    {
      return (iChar);
    }
  }
  return (iChar);
}

/***************************************************************************
 *
 * FUNCTION NAME = ParseCharBox()
 *
 * DESCRIPTION   = Parse the character's bounding box and return its
 *                 dimensions in the destination rectangle.
 *
 * INPUT         = prc
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

VOID ParseCharBox( PRECT prc )

 /* PRECT prc;                            pointer to the destination  */
 /*                                         rectangle                */
{
  CHAR szWord[16];

  GetWord( (PSZ) szWord, sizeof (szWord) );

  if (szIsEqual( (PSZ) "B", (PSZ) szWord) )
  {
    prc->left = GetNumber ();
    prc->bottom = GetNumber ();
    prc->right = GetNumber ();
    prc->top = GetNumber ();
  }
  else
  {
    return;
  }

  EatWhite ();

  if (*pszLine++ != ';')
  {
    return;
  }
}

/***************************************************************************
 *
 * FUNCTION NAME = ParseCharName()
 *
 * DESCRIPTION   = Parse a character's name and return its name
 *                 and numeric values.  Also return a pointer
 *                 into the character list to show how much of
 *                 the list has already been scanned.
 *
 * INPUT         = pcd
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = (0),(p+1)
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

PCD ParseCharName( PCD pcd )
{
  SHORT iChar;
  PCD   p;

  EatWhite ();
  GetWord( (PSZ) pcd->pszName, 16 );

  if (szIsEqual( (PSZ) "N", (PSZ) pcd->pszName) )
  {
    GetWord( (PSZ) pcd->pszName, 16 );
    GetCharCode( pcd );
  }
  EatWhite ();

  if (*pszLine++ != ';')
  {
    return (0);
  }
  p = charmap;

  while (p->pszName)
  {
    if (szIsEqual( (PSZ) pcd->pszName, (PSZ) p->pszName) )
    {
      return (p + 1);
    }
    ++p;
  }
  return (0);
}

/***************************************************************************
 *
 * FUNCTION NAME = ParseCharMetrics()
 *
 * DESCRIPTION   = Parse the character metrics entry in the input file
 *                 and set the width and bounding box in the afm structure.
 *
 * INPUT         = NONE
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

VOID ParseCharMetrics ()
{
  SHORT    cChars;
  SHORT    i;
  SHORT    iWdth;
  SHORT    iChar;
  long     MWidth, MHeight;
  long     MRatio;
  Rect     rcChar;
  CharData cd;
  CHAR     szName[20];
  PCD      pcdNext;

  cChars = GetNumber ();

  for (i = 0 ; i < cChars ; ++i)
  {
    if (GetLine ())
    {
      return;
    }
    iChar = ParseCharCode ();
    iWdth = ParseCharWidth ();

    if (iWdth > (SHORT) fmFont.lMaxCharInc)
    {
      fmFont.lMaxCharInc = (LONG) iWdth;
    }
    cd.pszName = (PSZ) szName;
    pcdNext = ParseCharName( (PCD) &cd );
    ParseCharBox( (PRECT) &rcChar );

    /*
    ** get the size of M square for the font.  from this we will
    ** get the relative width of the font and fill in the
    ** font metrics usWidthClass field.
    */
    if (szIsEqual( cd.pszName, "M") )
    {
      MWidth  = (long) (rcChar.right - rcChar.left);
      MHeight = (long) (rcChar.top - rcChar.bottom);

      /*
      ** now get the width/height ratio.
      */
      MRatio = (MWidth * 100L) / MHeight;

      if (MRatio <= 50L)
      {
        fmFont.usWidthClass = ULTRACONDENSED;
      }
      else if (MRatio <= 63L)
      {
        fmFont.usWidthClass = EXTRACONDENSED;
      }
      else if (MRatio <= 75L)
      {
        fmFont.usWidthClass = CONDENSED;
      }
      else if (MRatio <= 88L)
      {
        fmFont.usWidthClass = SEMICONDENSED;
      }
      else if (MRatio <= 112l)
      {
        fmFont.usWidthClass = MEDIUM;
      }
      else if (MRatio <= 124L)
      {
        fmFont.usWidthClass = SEMIEXPANDED;
      }
      else if (MRatio <= 149L)
      {
        fmFont.usWidthClass = EXPANDED;
      }
      else if (MRatio <= 199L)
      {
        fmFont.usWidthClass = EXTRAEXPANDED;
      }
      else
      {
        fmFont.usWidthClass = ULTRAEXPANDED;
      }
    }

    /*
    ** If this character is the hyphen, extract some
    ** info for the font metrics "strikeout" fields.
    */
    if (szIsEqual( (PSZ) cd.pszName, (PSZ) "hyphen") )
    {
      fmFont.lStrikeoutSize     = rcChar.top - rcChar.bottom;
      fmFont.lStrikeoutPosition = (rcChar.top + rcChar.bottom) / 2;
    }

    while (pcdNext)
    {
      afm.rgcm[cd.iCP850Code].ipscriptCode = cd.ipscriptCode;
      afm.rgcm[cd.iCP850Code].cvec.x = iWdth;
      afm.rgcm[cd.iCP850Code].cvec.y = 0;
      afm.rgcm[cd.iCP850Code].rc.top = rcChar.top;
      afm.rgcm[cd.iCP850Code].rc.left = rcChar.left;
      afm.rgcm[cd.iCP850Code].rc.right = rcChar.right;
      afm.rgcm[cd.iCP850Code].rc.bottom = rcChar.bottom;
      pcdNext = GetNextCharCode( (PCD) &cd, pcdNext );
    }
  }
  GetLine ();
}

/***************************************************************************
 *
 * FUNCTION NAME = ParseBoundingBox()
 *
 * DESCRIPTION   = Parse a character's bounding box and return its size in
 *                 the afm structure.
 *
 * INPUT         = NONE
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

VOID ParseBoundingBox ()
{
  afm.rcBBox.left         = GetNumber ();
  afm.rcBBox.bottom       = GetNumber ();
  afm.rcBBox.right        = GetNumber ();
  afm.rcBBox.top          = GetNumber ();
  fmFont.lMaxAscender     = (LONG) afm.rcBBox.top;
  fmFont.lMaxDescender    = (LONG) abs( afm.rcBBox.bottom );
  fmFont.lInternalLeading = (fmFont.lMaxAscender + fmFont.lMaxDescender) - 1000;
}

/***************************************************************************
 *
 * FUNCTION NAME = ParsePitchType()
 *
 * DESCRIPTION   = Parse the pitch type and set the variable pitch
 *                 flag in the afm structure.
 *
 * INPUT         = NONE
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

VOID ParsePitchType( VOID )
{
  SHORT iChar;
  CHAR  szWord[16];

  EatWhite ();
  GetWord( (PSZ) szWord, sizeof(szWord) );

  if (szIsEqual( (PSZ) "true", (PSZ) szWord) )
  {
    afm.fVariablePitch = FALSE;
    fmFont.fsType = 1;
  }
  else
  {
    afm.fVariablePitch = TRUE;
    fmFont.fsType = 0;
  }
}

/***************************************************************************
 *
 * FUNCTION NAME = OpenAfm()
 *
 * DESCRIPTION   = Initialize the afm structure, and open the files.
 *
 * INPUT         = pszAFMNONE
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = pbBuffin
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

PVOID OpenAfm( PSZ pszAFM )
{
  HFILE      fhIn;                          /* file handle     */
  FILESTATUS fsIn;                          /* file info struc */
  ULONG      cbRead;                        /* of bytes read   */
  SHORT      i;
  ULONG      usAct;
  PSZ        pTmp;

  afm    = afmDefault;                    /* afm & fm strucs */
  fmFont = fmDefault;

  /*
  ** init character metrics strucs to -1
  */
  for (i = 0 ; i < MAX_CHARS ; afm.rgcm[i++].ipscriptCode = -1);

  if (!DosOpen( pszAFM, (PHFILE) &fhIn, &usAct, 0L, FILE_READONLY,
                FILE_OPEN, OPEN_AFM_FLAGS, 0L) )
  {
    DosQFileInfo( fhIn, (SHORT) 1, (PVOID) &fsIn, sizeof( fsIn ) );
    fsIn.cbFile += 3;                  /* for ending CR,LF,null */
    pbBuffin = (PVOID) prdg_AllocMem( (SHORT) fsIn.cbFile);
    DosRead( fhIn, (PVOID) pbBuffin, (SHORT) fsIn.cbFile, &cbRead);

    /*
    ** If it doesn't end in a CR,LF pair, add one
    */
    pTmp = pbBuffin+cbRead;            /* past end  */

    if (*(pTmp-2) != CR || *(pTmp-1) != LF)
    {
      *pTmp++ = CR;
      *pTmp++ = LF;
    }
    *pTmp = '\0';                      /* null-terminate */
    DosClose( fhIn );
  }
  return (pbBuffin);
}

/***************************************************************************
 *
 * FUNCTION NAME = FixCharWidths()
 *
 * DESCRIPTION   = Any necessary character that this font does not
 *                 support will currently have ipscriptCode == -1.
 *                 Force all of these characters to map to (and
 *                 have character metrics of) the default
 *                 character, typically a blank.
 *
 * INPUT         = NONE
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

VOID FixCharWidths ()
{
  CMData cmSpace;
  SHORT  i;

  cmSpace = afm.rgcm[32];

  for (i = 0 ; i < MAX_CHARS ; ++i)
  {
    if (afm.rgcm[i].ipscriptCode == -1)
    {
      afm.rgcm[i] = cmSpace;
    }
  }
}

/***************************************************************************
 *
 * FUNCTION NAME = SetAfm()
 *
 * DESCRIPTION   = Finish setting the character and font metrics in the afm to
 *                 their proper values.
 *
 * INPUT         = NONE
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

VOID SetAfm ()
{
  LONG     lSumx = 0L;
  register SHORT i, x;

  if (!afm.fVariablePitch)
  {
    x = afm.rgcm[fmFont.sFirstChar].cvec.x;

    for (i = fmFont.sFirstChar ; i <= (fmFont.sFirstChar + fmFont.sLastChar) ;
         ++i)
    {
      afm.rgcm[i].cvec.x = x;
    }
  }

  /*
  ** set average width, lowercase ascent, and lowercase descent
  */
  for (i = 'a' ; i <= 'z' ; i++)
  {
    lSumx += (LONG)(afm.rgcm[i].cvec.x * iWeightFactor[i - 'a']);

    if ((LONG) afm.rgcm[i].rc.top > fmFont.lLowerCaseAscent)
    {
      fmFont.lLowerCaseAscent = (LONG) afm.rgcm[i].rc.top;
    }

    if ((LONG) abs(afm.rgcm[i].rc.bottom) > fmFont.lLowerCaseDescent)
    {
      fmFont.lLowerCaseDescent = (LONG) abs( afm.rgcm[i].rc.bottom );
    }
  }
  lSumx += (LONG) (afm.rgcm[' '].cvec.x * iWeightFactor[i - 'a']);
  fmFont.lAveCharWidth = lSumx / 1000;

  /*
  ** set maximum width
  */
  for (i = fmFont.sFirstChar ; i <= fmFont.sLastChar ; ++i)
  {
    if (afm.rgcm[i].cvec.x > afm.iMaxWidth)
    {
      afm.iMaxWidth = afm.rgcm[i].cvec.x;
    }
  }

  /*
  ** this font cannot provide winthorn multi-codepage support,
  ** we better change some of the fields in the font metrics.
  */
  if (szIsEqual( (PSZ) afm.szEncodingScheme, (PSZ) "FontSpecific") )
  {
    fmFont.sFirstChar = 1;
    fmFont.sLastChar  = 255;
    fmFont.usCodePage = ARBITRARY_CODEPAGE;
  }

  /*
  ** Make sure the strikeout size and position are set reasonably
  */
  if ((!fmFont.lStrikeoutSize) || (!fmFont.lStrikeoutPosition))
  {
    fmFont.lStrikeoutPosition = 300L;

    if (fmFont.lUnderscoreSize)
    {
      fmFont.lStrikeoutSize = fmFont.lUnderscoreSize;
    }
    else
    {
      fmFont.lStrikeoutSize = 66L;
    }
  }

  /*
  ** Set maxBaselineExtent and internal leading
  */
  fmFont.lMaxBaselineExt = fmFont.lMaxAscender + fmFont.lMaxDescender;

  if (fmFont.lEmHeight)
  {
    fmFont.lInternalLeading = fmFont.lMaxBaselineExt - fmFont.lEmHeight;
  }
}

/***************************************************************************
 *
 * FUNCTION NAME = EvenUp()
 *
 * DESCRIPTION   = Word-align the output buffer.
 *
 * INPUT         = NONE
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

SHORT EvenUp( VOID )
{

  if (cbBuffout & 1)
  {
    cbBuffout++;
    pbBuffout++;
  }
  return (cbBuffout);
}

/***************************************************************************
 *
 * FUNCTION NAME = PutByte()
 *
 * DESCRIPTION   = This function writes a byte to the output buffer.
 *
 * INPUT         = iByte
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

VOID   PutByte( BYTE iByte )
{
  *pbBuffout++ = iByte;
  cbBuffout++;
}

/***************************************************************************
 *
 * FUNCTION NAME = PutWord()
 *
 * DESCRIPTION   = This function writes a word to the output buffer.
 *
 * INPUT         = iWord
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

VOID   PutWord( SHORT iWord )
{
  PSHORT psh;

  psh = (PSHORT) pbBuffout;
  *psh++ = iWord;
  pbBuffout = (PSZ) psh;
  cbBuffout += 2;
}

/***************************************************************************
 *
 * FUNCTION NAME = PutString()
 *
 * DESCRIPTION   = This function writes a null terminated string to the output buffer.
 *
 * INPUT         = psz
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

VOID PutString( PSZ psz )
{

  do
  {
    cbBuffout++;
  }

  while ('\0' != (*pbBuffout++ = *psz++));
}

/***************************************************************************
 *
 * FUNCTION NAME = WriteOutput()
 *
 * DESCRIPTION   = This function writes the device font info structure
 *                 to the output file.
 *
 * INPUT         = pszPFM
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

typedef struct _tagHDR                 /* file header                       */
{
  SHORT usVersion;
  SHORT offGlobal;
  SHORT offCharMetrics;
  SHORT offPairKerning;
  SHORT offStrings;
  SHORT offMetrics;
  SHORT sizeAll;
} HDR;

typedef HDR  *PHDR;
VOID WriteOutput( PSZ pszPFM )
{
  HFILE        fhOut;                  /* file handle                       */
  PVOID        pOutBuff;
  ULONG        i;
  PKPair       pktemp;
  PHDR         phdrtmp;
  PFONTMETRICS pfontm;

  pbBuffout = pOutBuff = prdg_AllocMem( MAX_OBUFFSIZE ); /* create buffer */
  ((PHDR) pOutBuff)->usVersion = afm.version;

  /*
  ** save room for the header, as defined above
  */
  pbBuffout += sizeof( HDR );
  ((PHDR)pOutBuff)->offGlobal = cbBuffout = sizeof( HDR );

  /*
  ** ** write the global font info **
  */
  PutWord( afm.fVariablePitch );
  PutWord( afm.rcBBox.left );
  PutWord( afm.rcBBox.bottom );
  PutWord( afm.rcBBox.right );
  PutWord( afm.rcBBox.top );
  PutWord( afm.iCapHeight );

  /*
  ** character metrics
  */
  ((PHDR) pOutBuff)->offCharMetrics = EvenUp ();

  for (i = fmFont.sFirstChar ; i <= (fmFont.sFirstChar + fmFont.sLastChar) ; i++)
  {
    PutByte( (BYTE)afm.rgcm[i].ipscriptCode );
    PutWord( afm.rgcm[i].cvec.x );
  }

  /*
  ** pair kerning data
  */
  ((PHDR) pOutBuff)->offPairKerning = EvenUp ();

  for (i = 0 ; i < afm.cKPairs ; i++)
  {
    pktemp = (PKPair) pbBuffout;
    *(pktemp)++ = afm.rgKPairs[i];
    pbBuffout = (PSZ) pktemp;
    cbBuffout += sizeof( KPair );
  }

  /*
  ** strings
  */
  ((PHDR) pOutBuff)->offStrings = EvenUp ();
  PutString( afm.szFontName );
  PutString( afm.szComment );
  PutString( afm.szVersion );
  PutString( afm.szNotice );
  PutString( afm.szEncodingScheme );
  PutString( afm.szWeight );

  /*
  ** font metrics
  */
  ((PHDR) pOutBuff)->offMetrics = EvenUp ();
  pfontm = (PFONTMETRICS) pbBuffout;         /* this sloppy code because of */
  *(pfontm)++ = fmFont;                      /* CSET/2                      */
  pbBuffout = (PSZ) pfontm;
  cbBuffout += sizeof( FONTMETRICS );

  /*
  ** total size
  */
  ((PHDR) pOutBuff)->sizeAll = EvenUp () - ((PHDR) pOutBuff)->offGlobal;

  if (!DosOpen( pszPFM, (PHFILE)&fhOut, &i, 0L, FILE_NORMAL,
                FILE_CREATE | FILE_TRUNCATE, OPEN_PFM_FLAGS, 0L ))
  {
    DosWrite( fhOut, (PVOID) pOutBuff, cbBuffout, &i );
    DosClose( fhOut );
  }
  prdg_FreeMem( pOutBuff );
  return;
}

/***************************************************************************
 *
 * FUNCTION NAME = GetNextWord
 *
 * DESCRIPTION   = Copy next word from source to destination buffer
 *
 *                 This routine copies the next word delimited by white
 *                 space from the source buffer to the destination
 *                 buffer and NULL terminates it.  It returns a pointer
 *                 to the end position of the word in the source
 *                 buffer.
 *
 * INPUT         = pszSrcStr,pszDestStr
 *
 * OUTPUT        = TRUE
 *
 * RETURN-NORMAL = pszSrcStr
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

PSZ GetNextWord( PSZ, PSZ );
PSZ GetNextWord( PSZ pszSrcStr, PSZ pszDestStr )

 /* PSZ pszSrcStr;                        ptr to the source area            */
 /* PSZ pszDestStr;                       ptr to the destination area       */
{
  /*
  ** Remove any leading white spaces.
  */
  while (*pszSrcStr && (*pszSrcStr == ' ' || *pszSrcStr == '\t'))
  {
    ++pszSrcStr;
  }

  while (*pszSrcStr && (*pszSrcStr != ' ' && *pszSrcStr != '\t'))
  {
    *pszDestStr++ = *pszSrcStr++;
  }
  *pszDestStr = 0;                     /* drop NULL at end of destination   */
  return (pszSrcStr);
}

/***************************************************************************
 *
 * FUNCTION NAME = StrCopy
 *
 * DESCRIPTION   = Copy finite or NULL terminated string to destination
 *                 buffer and lower case it during copy.
 *
 * INPUT         = psStr,pdStr,iCount
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

VOID   StrCopy( PSZ psStr, PSZ pdStr, SHORT iCount )

/*  PSZ psStr;                            pointer to source string          */
/*  PSZ pdStr;                            pointer to destination string     */
/*  SHORT iCount;                         source string length              */
{
  CHAR  c;
  SHORT i;

  for (i = 0; i < iCount; i++)         /* stay within finite bounds         */
  {
    if (!(c = *psStr))                 /* pick up copy and abort if NULL    */
    {
      break;
    }

    if (c >= 'A' && c <= 'Z')
    {
      c += 'a'-'A';
    }
    *pdStr = c;
    psStr++;
    pdStr++;
  }
  *pdStr = 0;                          /* drop NULL at end of destination   */
}

/***************************************************************************
 *
 * FUNCTION NAME = ParseItalic
 *
 * DESCRIPTION   = Parse the input string for the 'italic' key-word
 *                 to be able to set the Italic bit in the font
 *                 metrics
 *
 * INPUT         = NONE
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

VOID ParseItalic ()
{
  CHAR szStrBuff[MAX_STR];
  CHAR szWrdBuff[MAX_STR];
  PSZ  pszSrcStr;

  /*
  ** pick up private copy of name
  */
  StrCopy( (PSZ) afm.szFontName, (PSZ) szStrBuff, sizeof( afm.szFontName ) );
  pszSrcStr = (PSZ) szStrBuff;

  /*
  ** while not at end of name string, grab the
  ** next word and see if it is 'italic'
  */
  while (*pszSrcStr)
  {
    pszSrcStr = GetNextWord( (PSZ) pszSrcStr, (PSZ) szWrdBuff );

    if (szIsEqual( (PSZ) szWrdBuff, (PSZ)"italic") )
    {
      fmFont.fsSelection |= FATTR_SEL_ITALIC;
      break;
    }
  }
}

/***************************************************************************
 *
 * FUNCTION NAME = ParseFamilyClass
 *
 * DESCRIPTION   = Parse the input string for the various family
 *                 names to be able to set the Family Name ID in
 *                 the font metrics
 *
 * INPUT         = NONE
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

VOID ParseFamilyClass ()
{
  CHAR szStrBuff[MAX_STR];
  CHAR szWrdBuff[MAX_STR];
  PSZ  pszSrcStr;

  /*
  ** pick up private copy of name
  */
  StrCopy( (PSZ) fmFont.szFamilyname, (PSZ) szStrBuff,
           sizeof( fmFont.szFamilyname ) );

  /*
  ** While not at end of name string, grab the next
  ** word and run through the various possibilities
  */
  pszSrcStr = (PSZ)szStrBuff;

  while (*pszSrcStr)
  {
    pszSrcStr = GetNextWord( pszSrcStr, szWrdBuff );

    if (( szIsEqual( (PSZ) szWrdBuff, (PSZ) "avant") ) ||
       (szIsEqual( (PSZ) szWrdBuff, (PSZ) "helvetica") ))
    {
      fmFont.sFamilyClass |= FATTR_FAM_SANS_SERIF;
      break;
    }

    if ((szIsEqual((PSZ)szWrdBuff, (PSZ) "bookman")) ||
       (szIsEqual( (PSZ) szWrdBuff, (PSZ) "times")) )
    {
      fmFont.sFamilyClass |= FATTR_FAM_TRANSITIONAL_SERIF;
      break;
    }

    if ((szIsEqual( (PSZ) szWrdBuff, (PSZ) "courier")) ||
       (szIsEqual( (PSZ) szWrdBuff, (PSZ) "lubalin")) )
    {
      fmFont.sFamilyClass |= FATTR_FAM_SLAB_SERIF;
      break;
    }

    if ((szIsEqual( (PSZ) szWrdBuff, (PSZ) "garamond")) ||
       (szIsEqual( (PSZ) szWrdBuff, (PSZ) "palatino")) )
    {
      fmFont.sFamilyClass |= FATTR_FAM_OLD_STYLE_SERIF;
      break;
    }

    if ((szIsEqual( (PSZ) szWrdBuff, (PSZ) "century")) ||
       (szIsEqual( (PSZ) szWrdBuff, (PSZ) "korinna")) )
    {
      fmFont.sFamilyClass |= FATTR_FAM_CLARENDON_SERIF;
      break;
    }

    if ((szIsEqual( (PSZ) szWrdBuff, (PSZ) "souvenir")) )
    {
      fmFont.sFamilyClass |= FATTR_FAM_FREEFORM_SERIF;
      break;
    }

    if ((szIsEqual( (PSZ) szWrdBuff, (PSZ) "chancery")) )
    {
      fmFont.sFamilyClass |= FATTR_FAM_SCRIPT;
      break;
    }

    if ((szIsEqual( (PSZ) szWrdBuff, (PSZ) "symbol")) ||
       (szIsEqual( (PSZ) szWrdBuff, (PSZ) "dingbats") ))
    {
      fmFont.sFamilyClass |= FATTR_FAM_SYMBOLIC;
      break;
    }
  }
}

/***************************************************************************
 *
 * FUNCTION NAME = AfmToPfm
 *
 * DESCRIPTION   =
 *
 * INPUT         = (pszAFM,pszPFM)
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = 0
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

SHORT AfmToPfm( PSZ pszAFM, PSZ   pszPFM )

 /* PSZ pszAFM,                           input file name, with path */

 /*    pszPFM;                            output file name, with path */
{
  PVOID pInBuff;

  fEOF    = fUnGetLine = FALSE;           /* init stuff */
  pInBuff = OpenAfm( (PSZ) pszAFM);       /* save start of in-buffer */

  while (!fEOF)
  {
    GetLine ();

    switch (GetToken ())
    {
    case tok_fullname
         ParseString((PSZ)afm.szFontName,
                     sizeof(afm.szFontName));
         szMove((PSZ)fmFont.szFacename,
                (PSZ)afm.szFontName,
                sizeof(fmFont.szFacename));
         ParseItalic();
         break;

    case tok_familyname:
         ParseString( (PSZ) fmFont.szFamilyname,
                     sizeof( fmFont.szFamilyname ) );
         ParseFamilyClass ();
         break;

    case tok_encodingscheme:
         ParseString((PSZ)afm.szEncodingScheme,
                     sizeof(afm.szEncodingScheme));

         if (szIsEqual((PSZ)afm.szEncodingScheme,
                       (PSZ)"FontSpecific"))
           charmap = charmapFontSpecific;

         else
           charmap = charmapAdobeStandard;
         break;

    case tok_comment:

        if (!afm.szComment[0])
          ParseString((PSZ)afm.szComment,
                      sizeof(afm.szComment));
        break;
    case tok_weight:
        ParseWeight();
        break;

    case tok_italicangle:
         fmFont.sCharSlope = GetFloat ();
         break;

    case tok_isfixedpitch:
         ParsePitchType ();
         break;

    case tok_fontbox:
         ParseBoundingBox ();
         break;

    case tok_underlineposition:
         fmFont.lUnderscorePosition = (LONG) abs( GetNumber () );
         break;

    case tok_underlinethickness:
         fmFont.lUnderscoreSize = (LONG) GetNumber ();
         break;

    case tok_version:
         ParseString( (PSZ) afm.szVersion, sizeof( afm.szVersion ) );
         break;

    case tok_notice:
         ParseString( (PSZ) afm.szNotice, sizeof( afm.szNotice ) );
         break;

    case tok_capheight:
         afm.iCapHeight = GetNumber ();
         break;

    case tok_xheight:
         fmFont.lXHeight = (LONG) abs( GetNumber () );
         break;

    case tok_ascender:
    case tok_descender:
         (VOID) GetNumber ();
         break;

    case tok_startcharmetrics:
         ParseCharMetrics ();
         break;

    case tok_startkerndata:
         ParseKernData ();
         break;

    case tok_startfontmetrics :
         afm.version = GetFloat ();
         afm.version = (afm.version % 10) + ((afm.version / 10) << 8);
         break;

    case tok_endfontmetrics:
         fEOF = TRUE;

    default:
        break;
    }
  }

  FixCharWidths ();
  SetAfm ();
  WriteOutput( (PSZ) pszPFM );
  prdg_FreeMem( pInBuff );
  return 0;
}
#endif
